pico-watch
auf rundem 1.28-Zoll-IPS-LCD-Display 240 x 240 Pixel
- Waveshare 22668
Anleitung für die Firmware CircuitPython
Hardware
- Rundes 1,28-Zoll-IPS-LCD-Display
- RP Pico 2040 (inclusive)
- USB-A zu USB-C Kabel
pico-watch
auf rundem 1.28-Zoll-IPS-LCD-Display 240 x 240 Pixel- Waveshare 22668
Anleitung für die Firmware CircuitPython
- RP Pico 2040 (inclusive)
- USB-A zu USB-C Kabel
Mit dieser Anleitung schlagen wir ein neues Kapitel bei der Programmierung des runden Pico-LCD-Displays für alle Hobbyprogrammierer auf. Nachdem bisher überwiegend
vielsagende Bilder von Anwendungen auf dem runden Pico-LCD-Display im Netz zu sehen waren, die von zugegebenermaßen Experten in C programmiert wurden, wird jetzt
ein Zugang für alle Interessierten in CircuitPython geschaffen. Eine oft gewünschte Anwendung ist dabei z.B. ein Display mit einer Analoguhr. In der folgenden
Anleitung geht es genau darum. Dabei stehen nicht das schönste Zifferblatt oder attraktive Zeiger, sondern die Funktionen im Mittelpunkt.
Diese werden genau erklärt, so dass jeder seine eigene Uhr designen kann.
Los gehts
Als erstes wird die Firmeware 'CircuitPython' für den Pico benötigt. Die laden Sie sich direkt von hier herunter
(https://circuitpython.org/board/waveshare_rp2040_lcd_1_28/)
und installieren sie auf dem Board.
Weiter brauchen Sie im 'lib-Ordner' die Bibliotheken adafruit_imageload, adafruit_display_shapes, adafruit_display_text sowie den
Displaytreiber gc9a01.py. Die können Sie bei mir als
zip-Datei
herunterladen, entpacken und in den 'lib_Ordner' kopieren. In der zip_Datei ist auch ein Ordner 'images'. Darin sind die Bitmaps mit
dem Zifferblatt und den Zeigern. Ziehen Sie diesen Ordner ins Stammverzeichnis von 'CIRCUITPY'. Ebenso die Datei 'boot.py', welche beim
Speichern der Uhrzeit auf dem Pico eine Rolle spielt, auf die ich später noch eingehe.
Jetzt gehts richtig los.
Im unteren Kasten sehen Sie, wie das Display initialisiert wird. Kopieren Sie die Zeilen 1 bis 68 ins Thonny. Sie können auch schon mal 'start'
drücken. Es erscheint das Zifferblatt.
1 # SPDX-FileCopyrightText : 2023 Detlef Gebhardt, written for CircuitPython 2 # SPDX-FileCopyrightText : Copyright (c) 2023 Detlef Gebhardt 3 # SPDX-Filename : pico-watch 4 # SPDX-License-Identifier: GEBMEDIA 5 import time 6 import gc 7 import board 8 import busio 9 import displayio 10 import digitalio 11 import terminalio 12 import bitmaptools 13 import math 14 import adafruit_imageload 15 from adafruit_display_text import label 16 from adafruit_display_shapes.circle import Circle 17 import gc9a01 18 19 # Variable definieren 20 text = "pico-watch\n by\n gebmedia" 21 22 # Bitmap-Dateien für Hintergrund und Zeiger 23 zifferblatt = "/images/zifferblatt.bmp" 24 second_zeiger = "/images/second.bmp" 25 minute_zeiger = "/images/minute.bmp" 26 hour_zeiger = "/images/hour.bmp" 27 # 28 # Display initialisieren 29 # 30 # Ressourcen freigeben für das Display 31 displayio.release_displays() 32 33 # displayio SPI-Bus und GC9A01 Display 34 spi = busio.SPI(clock=board.LCD_CLK, MOSI=board.LCD_DIN) 35 display_bus = displayio.FourWire(spi, command=board.LCD_DC, chip_select=board.LCD_CS, reset=board.LCD_RST) 36 display = gc9a01.GC9A01(display_bus, width=240, height=240, rotation=90, backlight_pin=board.LCD_BL) 37 38 main = displayio.Group() 39 display.show(main) 40 41 # Ziffernblatt als Hintergrund 42 bg_bitmap,bg_pal = adafruit_imageload.load(zifferblatt) 43 bg_tile_grid = displayio.TileGrid(bg_bitmap, pixel_shader=bg_pal) 44 main.append(bg_tile_grid) 45 46 # Text 47 text_area = label.Label(terminalio.FONT, text=text, line_spacing=0.9, color=0x000000, anchor_point=(0.5,0.5), anchored_position=(0,0)) 48 text_group = displayio.Group(scale=1, x=120, y=80) 49 text_group.append(text_area) 50 main.append(text_group) 51 52 # Stundenzeiger 30x140 53 bitmap_pointer_hour, palette_pointer = adafruit_imageload.load(hour_zeiger, bitmap=displayio.Bitmap,palette=displayio.Palette) 54 palette_pointer.make_transparent(0) 55 # Blank bitmap vom Stundenzeiger 56 bitmap_pointer_blank_hour = displayio.Bitmap(bitmap_pointer_hour.width, bitmap_pointer_hour.height, 1) 57 58 # Minutenzeiger 30x140 59 bitmap_pointer_min, palette_pointer = adafruit_imageload.load(minute_zeiger, bitmap=displayio.Bitmap,palette=displayio.Palette) 60 palette_pointer.make_transparent(0) 61 # Blank bitmap vom Minutenzeiger 62 bitmap_pointer_blank_min = displayio.Bitmap(bitmap_pointer_min.width, bitmap_pointer_min.height, 1) 63 64 # Sekundenzeiger 30x140 pointer 65 bitmap_pointer_sec, palette_pointer = adafruit_imageload.load(second_zeiger, bitmap=displayio.Bitmap,palette=displayio.Palette) 66 palette_pointer.make_transparent(0) 67 # Blank bitmap vom Sekundenzeiger 68 bitmap_pointer_blank_sec = displayio.Bitmap(bitmap_pointer_sec.width, bitmap_pointer_sec.height, 1) 69
Im nächsten Schritt werden die transparenten Overlays für die zu drehenden Zeiger angelegt. Schließlich werden zwei kleine Kreise als zentraler Mittelpunkt definiert. Fügen Sie auch diese Zeilen (70 bis 88) im Thonny ein.
70 # Transparentes Overlay für 'rotozoom' 71 # Zeiger für die Drehung 72 bitmap_scribble_hour = displayio.Bitmap(display.width, display.height, len(palette_pointer)) 73 tile_grid = displayio.TileGrid(bitmap_scribble_hour, pixel_shader=palette_pointer) 74 main.append(tile_grid) 75 bitmap_scribble_min = displayio.Bitmap(display.width, display.height, len(palette_pointer)) 76 tile_grid = displayio.TileGrid(bitmap_scribble_min, pixel_shader=palette_pointer) 77 main.append(tile_grid) 78 bitmap_scribble_sec = displayio.Bitmap(display.width, display.height, len(palette_pointer)) 79 tile_grid = displayio.TileGrid(bitmap_scribble_sec, pixel_shader=palette_pointer) 80 main.append(tile_grid) 81 circle1 = Circle(120, 120, 10, fill=0xff0000, outline=None) 82 main.append(circle1) 83 circle2 = Circle(120, 120, 5, fill=0x000000, outline=0x0) 84 main.append(circle2) 85 86 # Zeichnen initialisieren 87 display.refresh() 88
Es folgt ein wichtiger Teil, der für die korrekte Uhrzeit wichtig ist. Wenn das Display am Rechner angeschlossen ist,
holt es sich die Zeit von 'time.localtime()' und speichert sie in den Zeilen 94 bis 98 in die Datei 'time.txt'. Ist das Display
hingegen nicht am Rechner, dann beginnt 'localtime()' am 1.1.2020 um 00:00 Uhr die Sekunden zu zählen. Für diesen Fall
lesen wir die letzte bekannte Stunde und Minute aus 'time.txt'. Zieht man z.B. das Display vom Rechner ab und schließt es
sofort an eine andere Stromversorgung (z.B. Powerbank) an, "verliert" man zwar einige Sekunden, denn der MC zählt wieder
von Null los. Ist ein Akku angeschlossen, läuft das Display sogar ohne Unterbrechung weiter.
Hier kommt auch die Rolle der Datei 'boot.py' zu Tragen. Der Pico sucht beim Bootvorgang nach dieser Datei.
Ist der Befehl storage.remount("/", False) vorhanden, dann wird der interne Speicher zum Beschreiben aktiviert.
Ansonsten ist das Pico-Laufwerk schreibgeschützt. So wird im Normalfall verhindert, dass ein anderer Rechner auf den Pico schreibt.
Jede Änderung in der Datei 'boot.py', wird erst nach einem erneuten Bootvorgang wirksam.
89 current_time = time.localtime() 90 hour = current_time.tm_hour 91 minute = current_time.tm_min 92 second = current_time.tm_sec 93 94 if current_time.tm_year > 2020: 95 with open("/time.txt", "w") as f: 96 f.write(str(hour)+"\n") 97 f.write(str(minute)+"\n") 98 f.close() 99 if current_time.tm_year == 2020: 100 with open("time.txt", "r") as f: 101 hour = int(f.readline()) 102 minute = int(f.readline()) 103 f.close() 104 second = current_time.tm_sec 105 if hour > 12: 106 hour = hour -12 107
In der 'while'-Schleife ab Zeile 108 wird jetzt bei jedem Durchlauf die aktuelle Sekunde bestimmt. Die Stunde und die Minute wird aus dem Bereich vor der Schleife verwendet. In den Zeilen 111, 113 und 115 werden die Winkel der Zeiger in Abhängigkeit von den Sekunden, Minuten und Stunden (im Bogenmaß) berechnet. Dazu finden Sie Erklärungen in der Anleitung 6 (hier). Die Zeilen 112, 114 und 116 stellen die Bitmaps der Zeiger in entsprechend gedrehter Position dar. Ab Zeile 119 werden immer bei Sekunde 59 die Werte für 'Minute' um eins erhöht, bei Minute = 60 die Stunde erhöht u.s.w. Jede Minute werden die Werte für 'Stunde' und 'Minute' in der Datei 'time.txt' aktualisiert. Dabei hat die Zeit des Speichervorgangs keinen Einfluss auf die Ganggenauigkeit, denn die Sekunden werden ja vom MC weitergezählt. Die Pause time.sleep(0.7) in Zeile 130 hat die Aufgabe zu verhindern, dass der Speichervorgang schon abgeschlossen und die Sekunde noch bei 59 ist. Dann würde die Minute auch gleich mehrfach hochgezählt. Auch diese Pause wirkt sich nicht auf die Ganggenauigkeit aus. Da die Sekunden unabhängig weiterzählen, springt der Sekundenzeiger höchstens mal zwei Positionen weiter.
108 while True: 109 current_time = time.localtime() 110 second = current_time.tm_sec 111 alpha_rad_sec = math.pi/30 * second 112 bitmaptools.rotozoom( bitmap_scribble_sec, bitmap_pointer_sec, angle = alpha_rad_sec, px=15,py=107) 113 alpha_rad_hour = math.pi/6 * hour + math.pi/180 * minute/2 114 bitmaptools.rotozoom( bitmap_scribble_hour, bitmap_pointer_hour, angle = alpha_rad_hour, px=15,py=107) 115 alpha_rad_min = math.pi/30 * minute 116 bitmaptools.rotozoom( bitmap_scribble_min, bitmap_pointer_min, angle = alpha_rad_min, px=15,py=105) 117 if second == 59: 118 minute += 1 119 if minute == 60: 120 minute = 0 121 hour += 1 122 if hour == 24: 123 hour = 0 124 with open("/time.txt", "w") as f: 125 f.write(str(hour)+"\n") 126 f.write(str(minute)+"\n") 127 f.close() 128 display.refresh() 129 #bitmaptools.rotozoom( bitmap_scribble_sec, bitmap_pointer_blank_sec, angle = alpha_rad_sec, px=15,py=105) 130 time.sleep(0.7) 131 gc.collect() 132 print(gc.mem_free())
Ihnen ist sicher der Befehl gc.collect() in Zeile 131 aufgefallen. Bei jedem Schleifendurchlauf
sorgt er dafür, nicht mehr benötigten RAM freizugeben. Wenn Sie diesen Befehl auskommentieren und den angezeigten
freien Speicher beobachten, werden Sie sehen, dass es schnell knapp werden kann. Deshalb achten Sie auch darauf, dass Sie bei
eigenen Hintergründen und Zeigern eher etwas knauserig mit der Farbtiefe der Bitmaps sind. Lieber nur 16 Farben statt 256.
Über einen Kommentar oder ein Lob
hier an dieser Stelle würde ich mich sehr freuen.
Viel Spass und Erfolg beim Ausprobieren.
Viel Spass und Erfolg beim Ausprobieren.