首页 > 解决方案 > 尝试将 Python 代码从电子纸显示器更改为驱动 mini PiTFT

问题描述

我正在研究这个ISS Tracker 项目,但我不想使用电子纸显示器,而是想使用240x135 Mini PiTFT 显示器。我一直在尝试重写 Python 代码来完成此操作,因为渲染代码是相同的,但我无法将其正确输出到新显示器。当涉及到 Python 代码时,我非常讨厌,所以您可以提供的任何帮助将不胜感激。源代码可在上面的 ISS Tracker 链接中找到,我修改后的代码如下:

#   International Space Station Tracker.
#   using Raspberry Pi B+, Waveshare ePaper Display and ProtoStax enclosure
#   --> https://www.waveshare.com/product/modules/oleds-lcds/e-paper/2.7inch-e-paper-hat-b.htm
#   --> https://www.protostax.com/products/protostax-for-raspberry-pi-b
#
#   It displays the current location of the ISS and also its tracked trajectory. The
#   current location is shown by the ISS icon, and the trajectory by small circles.
#   15 minute markers are shown as small rectangles.
#
#   ISS Current Location is obtained using Open Notify ISS Current Location API
#   http://open-notify.org/Open-Notify-API/ISS-Location-Now/
#
#   Written by Sridhar Rajagopal for ProtoStax.
#   BSD license. All text above must be included in any redistribution
# *


import sys
sys.path.append(r'lib')

if sys.version_info[0] < 3:
    raise Exception("Must be using Python 3")

from enum import Enum
import signal
import board
import digitalio
import adafruit_rgb_display.st7789 as st7789
import epd2in7b
import epdconfig

from PIL import Image,  ImageDraw,  ImageFont, ImageOps
from datetime import datetime
from time import time, sleep

import requests

# START mini pitft code

# Configuration for CS and DC pins (these are FeatherWing defaults on M0/M4):
cs_pin = digitalio.DigitalInOut(board.CE0)
dc_pin = digitalio.DigitalInOut(board.D25)
reset_pin = None
 
# Config for display baudrate (default max is 24mhz):
BAUDRATE = 64000000
 
# Setup SPI bus using hardware SPI:
spi = board.SPI()

# Create the ST7789 display:
disp = st7789.ST7789(
    spi,
    cs=cs_pin,
    dc=dc_pin,
    rst=reset_pin,
    baudrate=BAUDRATE,
    width=135,
    height=240,
    x_offset=53,
    y_offset=40,
)
 
# Create blank image for drawing.
# Make sure to create image with mode 'RGB' for full color.
height = disp.width  # we swap height/width to rotate it to landscape!
width = disp.height
image = Image.new("RGB", (width, height))
rotation = 90
 
# 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, rotation)

# Draw some shapes.
# First define some constants to allow easy resizing of shapes.
padding = -2
top = padding
bottom = height - padding

# Move left to right keeping track of the current x position for drawing shapes.
x = 0

# Turn on the backlight
backlight = digitalio.DigitalInOut(board.D22)
backlight.switch_to_output()
backlight.value = True

# END mini pitft code


# Update Interval for fetching positions
DATA_INTERVAL = 30 #seconds
# Update interval for the display
DISPLAY_REFRESH_INTERVAL = 2 # Number of DATA_INTERVAL between successive display updates (e.g. 2 => update display every second deta fetch)

# Note:
# The dimensions of the 2.7 in ePaper display are
# 264 x 176
# The dimensions of the Mini PiTFT display are
# 240 x 135

class Display(object):
    def __init__(self, imageWidth, imageHeight):
        self.imageWidth = imageWidth
        self.imageHeight = imageHeight

    # Draws the ISS current location and trajectory from array of positions
    def drawISS(self, positions):
        imageWhite = Image.new('1', (self.imageWidth, self.imageHeight), 255) # 1: clear the frame
        imageMap = Image.open('world_map_m.bmp').convert('L')
        imageWhite.paste(imageMap, (0,0))

        imageRed = Image.new('1', (self.imageWidth, self.imageHeight), 255) # 1: clear the frame
        issLogo = Image.open('iss.bmp').convert('L')
        drawred = ImageDraw.Draw(imageRed)

        for i,t in enumerate(positions):
            (lat,lon) = t

            # Map the lat, lon to our x/y coordinate system
            (x,y) = self.mapLatLongToXY(lat, lon)

            # last position in the positions array is the latest location
            # Every 15 minutes, we add a rectangular marker
            # and a small red circle to mark other locations

            if (i == len(positions) - 1):
                s = 10
                # drawred.rectangle((x-s,y-s,x+s,y+s), fill=0)
                imageRed.paste(issLogo, ((int)(x-s), (int)(y-s)))
            elif (((i+1) % (15 * 60 / DATA_INTERVAL)) == 0): # every 15 minutes (so 15 * 60s / DATA_INTERVAL = number of readings within 15 minutes)
                s = 2
                drawred.rectangle((x-s,y-s,x+s,y+s), fill=0)
            else:
                s = 1
                drawred.ellipse((x-s,y-s,x+s,y+s), outline=0)
                # drawred.point((x,y), fill=0)

        # Rotate image 180 degrees - Remove the # comments of the lines below to rotate the image and allow for alternate positioning/mounting of the Raspberry Pi 
        # imageRed = imageRed.transpose(Image.ROTATE_180)
        # imageWhite = imageWhite.transpose(Image.ROTATE_180)

        # return the rendered Red and White images
        return imageWhite, imageRed

    # Maps lat, long to x,y coordinates in 264x181 (the size of the world map)
    # (90 to -90 lat and -180 to 180 lon) map to 0-181 (y) and 0-264 (x) respectively
    # Simple algebra gives us the equations below
    # Recalculate as appropriate for map size and coordinates
    def mapLatLongToXY(self, lat, lon):
        x = (int)(0.733 * lon + 132)
        y = (int)(-1.006 * lat + 90.5)
        return x, y

# The main function
def main():
    # API to get ISS Current Location
    URL = 'http://api.open-notify.org/iss-now.json'

    # Initialize and clear the 2in7b (tri-color) display
    disp = st7789.ST7789(
        spi,
        cs=cs_pin,
        dc=dc_pin,
        rst=reset_pin,
        baudrate=BAUDRATE,
        width=135,
        height=240,
        x_offset=53,
        y_offset=40,
    )

    display = Display(disp.height, disp.width)

    # Store positions in list
    positions = []

    while(True):
        t0 = time()

        r = requests.get(url = URL)

        # extracting data in json format
        data = r.json()
        print(data)

        lat = float(data['iss_position']['latitude'])
        lon = float(data['iss_position']['longitude'])

        positions.append((lat, lon))
        print(positions)

        # Refresh the display on the first fetch and then on every DISPLAY_REFRESH_INTERVAL fetch
        if ((len(positions) >= 1) and ((len(positions)-1) % DISPLAY_REFRESH_INTERVAL)):
            disp.init()
            (imageWhite, imageRed) = display.drawISS(positions)
            # We're drawing the map in white and the ISS location and trajectory in red
            # Swap it around if you'd like the inverse color scheme
            disp.display(disp.getbuffer(imageWhite), disp.getbuffer(imageRed))
            sleep(2)
            disp.sleep()
       
        t1 = time()
        sleepTime = max(DATA_INTERVAL - (t1 - t0), 0)
        sleep(sleepTime) # sleep for 30 seconds minus duration of get request and display refresh



# gracefully exit without a big exception message if possible
def ctrl_c_handler(signal, frame):
    print('Goodbye!')
    # XXX : TODO
    #
    # To preserve the life of the ePaper display, it is best not to keep it powered up -
    # instead putting it to sleep when done displaying, or cutting off power to it altogether.
    #
    # dispconfig.module_exit() shuts off power to the module and calls GPIO.cleanup()
    # The latest disp library chooses to shut off power (call module_exit) even when calling disp.sleep()
    # disp.sleep() calls dispconfig.module_exit(), which in turns calls cleanup().
    # We can therefore end up in a situation calling GPIO.cleanup twice
    #
    # Need to cleanup Waveshare disp code to call GPIO.cleanup() only once
    # for now, calling dispconfig.module_init() to set up GPIO before calling module_exit to make sure
    # power to the ePaper display is cut off on exit
    # I have also modified dispconfig.py to initialize SPI handle in module_init() (vs. at the global scope)
    # because slepe/module_exit closes the SPI handle, which wasn't getting initialized in module_init
    # dispconfig.module_init()
    # dispconfig.module_exit()
    # print("Remeber to clear the display using cleardisplay.py if you plan to power down your Pi and store it, to prevent burn-in!")
    exit(0)

signal.signal(signal.SIGINT, ctrl_c_handler)


if __name__ == '__main__':
    main()

标签: pythonraspberry-pi

解决方案


你不能做

disp.display(disp.getbuffer(imageWhite), disp.getbuffer(imageRed))

因为您的 disp 对象没有名为“display”的方法

相反,您需要像这样在 disp 对象中设置图像:

disp.image(imageRed, 旋转)

在原始代码中,显示分为两部分 - 红色和黑色。它们分别由 ePaper 渲染,因此您需要两个图像。

在您的显示中,您可以构建具有所有颜色的单个图像,然后将该图像设置为您的 disp.display

disp.display(newImage, 旋转)

其中 newImage 将是 Display.drawISS 方法返回的图像。您将需要修改 drawISS 方法(或创建一个新方法,比如名为 drawISS_TFT,它返回一个 RGB 中的单个图像,其中包含地图和轨迹,以不同的颜色(而不是 drawISS 绘制一个黑色图像和其他红色图像 - 只需使用单个图像)。

这应该够了吧。

但首先,要查看您的 TFT 显示内容,只需更换

disp.display(disp.getbuffer(imageWhite), disp.getbuffer(imageRed))

disp.image(imageRed,旋转)或 disp.image(imageWhite,旋转)

如果你适当地看到国际空间站的轨迹或世界地图,那么你会在做其他事情之前知道你在正确的轨道上。

-斯里达尔


推荐阅读