N8's Pi Zero TFT Screen Test Script


This script was made so I could learn how to display stuff on a little TFT screen. I actually edited this script after copying it from my Dad who copied and edited one of the example scripts. He did provide me a cheat sheet on what to install to get the TFT screen working. Here is what the cheat sheet says:



Do this to install i2C requirments:

apt update -y
apt-get update -y
apt-get upgrade -y

pip3 install --upgrade setuptools

sudo pip3 install --upgrade adafruit-python-shell
wget https://raw.githubusercontent.com/adafruit/Raspberry-Pi-Installer-Scripts/master/raspi-blinka.py
sudo python3 raspi-blinka.py
sudo pip install openpyxl





ls /dev/i2c* /dev/spi*

Create blinkatest.py with below:
import board
import digitalio
import busio

print("Hello blinka!")

# Try to great a Digital input
pin = digitalio.DigitalInOut(board.D4)
print("Digital IO ok!")

# Try to create an I2C device
i2c = busio.I2C(board.SCL, board.SDA)
print("I2C ok!")

# Try to create an SPI device
spi = busio.SPI(board.SCLK, board.MOSI, board.MISO)
print("SPI ok!")

print("done!")



Now Run:
python3 blinktest.py

Test i2c by running:
i2cdetect -y 1 (You should see device #)

Install this for Display:

sudo pip3 install adafruit-circuitpython-rgb-display
sudo pip3 install --upgrade --force-reinstall spidev

sudo apt-get install python3-pip

sudo apt-get install ttf-dejavu

sudo apt-get install python3-pil

sudo apt-get install python3-numpy




Test i2c by running:
i2cdetect -y 1


What's funny is that I didn't really read the cheat sheet before using it, and as I wasn't using the Temperature Sensor I ended up putting some unnecessary commands into the command line. If you are trying to use my script, all you need is to run the following commands from the cheat sheet above.


Install this for Display:

sudo pip3 install adafruit-circuitpython-rgb-display
sudo pip3 install --upgrade --force-reinstall spidev

sudo apt-get install python3-pip

sudo apt-get install ttf-dejavu

sudo apt-get install python3-pil

sudo apt-get install python3-numpy


Here is the actual code itself. It is important for me to note, that I recently lost the most recent version and that I am in the process of restoring some of the changes I made.


# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT

"""
This will show some Linux Statistics on the attached display. Be sure to adjust
to the display you have connected. Be sure to check the learn guides for more
usage information.

This example is for use on (Linux) computers that are using CPython with
Adafruit Blinka to support CircuitPython libraries. CircuitPython does
not support PIL/pillow (python imaging library)!
"""

import time
import subprocess
import digitalio
from digitalio import DigitalInOut, Direction
import board
from PIL import Image, ImageDraw, ImageFont
import adafruit_rgb_display.ili9341 as ili9341
import adafruit_rgb_display.st7789 as st7789  # pylint: disable=unused-import
import adafruit_rgb_display.hx8357 as hx8357  # pylint: disable=unused-import
import adafruit_rgb_display.st7735 as st7735  # pylint: disable=unused-import
import adafruit_rgb_display.ssd1351 as ssd1351  # pylint: disable=unused-import
import adafruit_rgb_display.ssd1331 as ssd1331  # pylint: disable=unused-import
import busio
#import adafruit_bme280
i2c = busio.I2C(board.SCL, board.SDA)
#bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c)
#bme280.sea_level_pressure = 1017.95

# Configuration for CS and DC pins (these are PiTFT defaults):
cs_pin = digitalio.DigitalInOut(board.CE0)
dc_pin = digitalio.DigitalInOut(board.D25)
reset_pin = digitalio.DigitalInOut(board.D24)

# Config for display baudrate (default max is 24mhz):
BAUDRATE = 24000000

# Setup SPI bus using hardware SPI:
spi = board.SPI()

# pylint: disable=line-too-long
# Create the display:
# disp = st7789.ST7789(spi, rotation=90,                            # 2.0" ST7789
# disp = st7789.ST7789(spi, height=240, y_offset=80, rotation=180,  # 1.3", 1.54" ST7789
# disp = st7789.ST7789(spi, rotation=90, width=135, height=240, x_offset=53, y_offset=40, # 1.14" ST7789
# disp = hx8357.HX8357(spi, rotation=180,                           # 3.5" HX8357
# disp = st7735.ST7735R(spi, rotation=90,                           # 1.8" ST7735R
# disp = st7735.ST7735R(spi, rotation=270, height=128, x_offset=2, y_offset=3,   # 1.44" ST7735R
# disp = st7735.ST7735R(spi, rotation=90, bgr=True,                 # 0.96" MiniTFT ST7735R
# disp = ssd1351.SSD1351(spi, rotation=180,                         # 1.5" SSD1351
# disp = ssd1351.SSD1351(spi, height=96, y_offset=32, rotation=180, # 1.27" SSD1351
# disp = ssd1331.SSD1331(spi, rotation=180,                         # 0.96" SSD1331
#disp = ili9341.ILI9341(
#    spi,
#    rotation=90,  # 2.2", 2.4", 2.8", 3.2" ILI9341
#    cs=cs_pin,
#    dc=dc_pin,
#    rst=reset_pin,
#    baudrate=BAUDRATE,
#)
disp = st7789.ST7789(
    spi, 
    height=240, 
    y_offset=80, 
    rotation=180,  # 1.3", 1.54" ST7789
    cs=cs_pin,
    dc=dc_pin,
)
#disp = st7735.ST7735R(
#spi, 
#rotation=180, 
#height=235, 
##x_offset=2, 
#y_offset=80,   # 1.44" ST7735R
#cs=cs_pin,
#dc=dc_pin,
#)
# pylint: enable=line-too-long

# Create blank image for drawing.
# Make sure to create image with mode 'RGB' for full color.
if disp.rotation % 180 == 90:
    height = disp.width  # we swap height/width to rotate it to landscape!
    width = disp.height
else:
    width = disp.width  # we swap height/width to rotate it to landscape!
    height = disp.height

image = Image.new("RGB", (width, height))

# Get drawing object to draw on image.
draw = ImageDraw.Draw(image)

# Draw a black filled box to clear the image.
draw.rectangle((0, 0, width, height), outline=0, fill=(0, 0, 0))
disp.image(image)

# First define some constants to allow easy positioning of text.
padding = -2
x = 0

# Load a TTF font.  Make sure the .ttf font file is in the
# same directory as the python script!
# Some other nice fonts to try: http://www.dafont.com/bitmap.php
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 25)



dateFont = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 40)
timeFont = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 50)


circleX = 0
circleY= 210
circleBounceSwitch = True

# Input pins:


button_U = DigitalInOut(board.D23)
button_U.direction = Direction.INPUT

button_D = DigitalInOut(board.D24)
button_D.direction = Direction.INPUT

fnt = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 30)

class Blanker:
    def blank(abc):
        draw.rectangle((0, 0, 240, 240), outline="#000000", fill="#000000")  # blanking
        disp.image(image)
    def load(abc):
        draw.rectangle((0, 0, 240, 240), outline="#000000", fill="#000000")  # blanking
        draw.text((25, 120), "Loading...", font=fnt, fill="#00FF00")
        disp.image(image)

slide = Blanker()

class MyProgram:
    def __init__(self, name, location):
        self.name = name
        self.location = location

    def callProgram(self):
        slide.load() 
        #draw.rectangle((0, 0, 240, 240), outline="#000000", fill="#000000")  # blanking
        #draw.text((25, 120), "Loading...", font=fnt, fill="#00FF00")
        #disp.image(image)
        subprocess.run(['python3 '+ self.location +' &'], shell=True)
        quit()

selector = MyProgram("Selector", "/home/pi/Documents/PythonScripts/SelectorScript/SelectorScript.py")


while True:
    # Draw a black filled box to clear the image.
    draw.rectangle((0, 0, width, height), outline=0, fill=0)

    # Shell scripts for system monitoring from here:
    # https://unix.stackexchange.com/questions/119126/command-to-display-memory-usage-disk-usage-and-cpu-load
    cmd = "hostname -I | cut -d' ' -f1"
    IP = "IP: " + subprocess.check_output(cmd, shell=True).decode("utf-8")
   # cmd = "top -bn1 | grep load | awk '{printf \"CPU Load: %.2f\", $(NF-2)}'"
   # CPU = subprocess.check_output(cmd, shell=True).decode("utf-8")
  #  cmd = "free -m | awk 'NR==2{printf \"Mem: %s/%s MB   %.2f%%\", $3,$2,$3*100/$2 }'"
  #       print("%0.1f" % bme280.temperature, file=f)
  #  cmd = "cat /tmp/outsidetemp.txt| awk '{print \"Outside Temp:\"$1\"C\"}'"
  #  Outside = subprocess.check_output(cmd, shell=True).decode("utf-8")
    cmd = "date +%m/%d/%Y"
    Date = subprocess.check_output(cmd, shell=True).decode("utf-8")

    cmd = "date +%H:%M:%S"
    Time = subprocess.check_output(cmd, shell=True).decode("utf-8")

    cmd = "pidof python3"
    PID = "PID: " + subprocess.check_output(cmd, shell=True).decode("utf-8")
    

 

    # Write four lines of text.
    y = padding
    draw.text((x, y), IP, font=font, fill="#00FF00")
    y += font.getsize(IP)[1]
    draw.text((x, y), "N8's Pi Zero", font=dateFont, fill="#00FF00")
    y += font.getsize(Date)[1]
   # draw.text((x, y), CPU, font=font, fill="#FFFF00")
   # y += font.getsize(CPU)[1]
   # draw.text((x, y), MemUsage, font=font, fill="#00FF00")
   # y += font.getsize(MemUsage)[1]
   # draw.text((x, y), Disk, font=font, fill="#0000FF")
   # y += font.getsize(Disk)[1]
    draw.text((x, y+7), Date, font=dateFont, fill="#00FF00")
    y += font.getsize(Date)[1]
   # draw.text((x, y), Temp, font=font, fill="#FFFFFF")
   # y += font.getsize(Temp)[1]
   # draw.text((x, y), Outside, font=font, fill="#FFFF00")

    draw.text((x, y+9), Time, font=timeFont, fill="#00FF00")
    y += font.getsize(Time)[1]
    
    draw.text((x, y+25), PID, font=font, fill="#00FF00")
    y+= font.getsize(PID)[1]   


 

   #draw.rounded_rectangle(xy=(circleX, circleY, 10, 10), radius=5, fill="#00FF00")

    draw.rectangle((circleX, circleY-10, circleX+20, circleY+10), outline=0, fill="#00FF00")

   
 
    if circleX == 0 :
        circleBounceSwitch = True
        circleX+=5
    elif circleX == 220 :
        circleBounceSwitch = False
        circleX-=5
    elif circleBounceSwitch :
        circleX+=5
    else:
        circleX-=5

    if not button_D.value:  # down pressed
        draw.text((5, 120), "Clearing", font=font, fill="#00FF00")
        selector.callProgram()
    

    if not button_D.value:
        selector.callProgram()  

# Display image.
    disp.image(image)
    time.sleep(0.1)


As you can see I have a couple of classes in my script here, they have had cameos in all of my scripts as of late simply due to their utility. I plan to replace the current loading screen with something I made in Photoshop the other day. But here is the Blanker Class and an Object:


class Blanker:
     def blank(abc):
         draw.rectangle((0, 0, 240, 240), outline="#000000", fill="#000000") # blanking
         disp.image(image)
     def load(abc):
         draw.rectangle((0, 0, 240, 240), outline="#000000", fill="#000000") # blanking
         draw.text((25, 120), "Loading...", font=fnt, fill="#00FF00")
         disp.image(image)

slide = Blanker()


The Blanker class was originally just made to display a blank black square, but I realized I wanted something to show that I was switching between scripts (something which I mastered in another script). No arguments are necessary for the Blanker Objects. As seen below MyProgram Class relies on the Blanker.load() function:


class MyProgram:
     def __init__(self, name, location):
         self.name = name
         self.location = location

     def callProgram(self):
         slide.load()
         #draw.rectangle((0, 0, 240, 240), outline="#000000", fill="#000000") # blanking
         #draw.text((25, 120), "Loading...", font=fnt, fill="#00FF00")
         #disp.image(image)
         subprocess.run(['python3 '+ self.location +' &'], shell=True)
         quit()

selector = MyProgram("Selector", "File path, put in later.")


As you can plainly see there is still a bit of pseudocode in the object there ("File path, put this in later." Ha ha!):


selector = MyProgram("Selector", "File path, put in later.")


This is thanks to a broken SD card. While I didn't lose very much of this program I decided to renovate the whole Pi Zero TFT project. On this new iteration, I plan to use Raspbian Lite as I've gotten used to using vi to edit my scripts. Besides Raspbian Full seems to be slow on the Pi Zero for some reason, but I might try using Raspbian Full as an alternative should I be able to obtain certain peripherals. You can also see me relearing my Object Oriented Programming in realtime in the MyProgram.callProgram() function:


     def callProgram(self):
         slide.load()
         #draw.rectangle((0, 0, 240, 240), outline="#000000", fill="#000000") # blanking
         #draw.text((25, 120), "Loading...", font=fnt, fill="#00FF00")
         #disp.image(image)
         subprocess.run(['python3 '+ self.location +' &'], shell=True)
         quit()


I had been just copy/pasting the three commented out loading lines that later became Blanker.load() when I rememebered that OOP was a thing and realized it would be infinitely more effecient to just put that in. Then putting together the MyProgram.callProgram() function to further streamline my scripting. I'm not sure I ever ended up calling Blanker.load() anywhere outside of the MyProgram.callProgram() function which I believe is considered good practice. When I finish the overarching Project I have going with these scripts, I think I'd like to provide a .IMG for people to use on their own Raspberry Pis.