Digitaluhr mit Schrittzähler
Rundes 1.28-Zoll-IPS-LCD-Display 240 x 240 Pixel
- RP2040, 6-Achsen-Sensor - Waveshare 22668
- RP2350, 6-Achsen-Sensor - Waveshare 28986
Anleitung getestet mit CircuitPython 10.0.0-beta
Bildbox 1 (klick hier)
Digitaluhr mit Schrittzähler
Rundes 1.28-Zoll-IPS-LCD-Display 240 x 240 Pixel- RP2040, 6-Achsen-Sensor - Waveshare 22668
- RP2350, 6-Achsen-Sensor - Waveshare 28986
Anleitung getestet mit CircuitPython 10.0.0-beta





Bildbox 1 (klick hier)
Diese Seite hat seit dem 23.08.2023 ein Update. Nachdem ich den Treiber für die Sensoren in CircuitPython fertig hatte, wurde dieses Projekt
erweitert. Genaue Hinweise zur QMI8658-library finden Sie in der
Anleitung (hier). Das oben gezeigte
Bild in der Bildbox hatte ja so wie abgebildet noch nicht funktioniert. Jetzt ändern wir das.
Der ursprüngliche Text bleibt farblich unverändert. Der hinzugefügte Text ist jetzt durchgehend blau dargestellt.
Hardware
- Rundes 1,28-Zoll-IPS-LCD-Display mit
- RP Pico 2040 oder RP Pico 2350
- USB-A zu USB-C Kabel
Überall tauchen diese kleinen runden Displays von Waveshare jetzt zum Kaufen auf. Das was die Abbildungen zeigen, sieht sehr
vielversprechend aus. Ein rundes LCD-1.28 Display mit einer Auflösung von 240x240 Pixeln, IPS-Bildschirm und 65K RGB-Farben. Da fallen
mir sofort eine Menge Anwendungsmöglichkeiten ein.
Sucht man im Netz aber nach lauffähigen 'Examples' vergleichbar zu den Bildern von den Kaufangeboten des Displays, ist man
aufgeschmissen. Außer ein paar sehr einfachen Beispielen unter MicroPython findet man nichts für den Hobbyprogrammierer.
Selbst von Waveshare existiert z.Z. nur ein MicroPython-Beispiel, welches die
Funktion der Sensoren zeigt. Damit kann man noch nicht wirklich viel anfangen. Nur ein MicroPython Beispiel für eine Analoguhr
von Tony Goodhew ist für Anwender , die unter MicroPython arbeiten, verwertbar.
Im einem ersten Artikel hatte ich die Inbetriebnahme zur grundlegenden Funktion des runden LCD-Displays-1.28 beschreiben, um dann später ebenfalls
eine Uhrfunktion unter CircuitPython, wie in den beiden unteren Bildern dargestellt, zu realisieren. Um in das Thema erfolgreich einzusteigen,
können Sie die folgende
zip-Datei
herunterladen, entpacken und später auf den Pico übertragen, wenn es um die Beispiele geht. Enthalten sind auch die notwendigen
Bibliotheken, welche im 'lib-Ordner' vorhanden sein müssen.


(Auf die Gehäuse gehe ich an anderer Stelle noch ein)
Los gehts
Das Display und der Pico sind von Hause aus verbunden. Es versteht sich, dass bisher keine Spannung angelegt ist! Als erstes wird die Firmeware für den Pico benötigt.
1. Bevor Sie den Pico erstmalig an die Stromversorgung anschließen, muss zunächst die CircuitPython-Firmware mit der Ansteuerung für das runde Pico Display übertragen werden. Ich nutze für den RP 2040 die Seite:
https://circuitpython.org/board/waveshare_rp2040_lcd_1_28/ ,
beim RP 2350 die Seite (hierfür gibt es z.Z. keine Waveshareseite zum Download):
https://circuitpython.org/board/raspberry_pi_pico2/
und lade die jeweils aktuelle uf2-Datei herunter:
2. Nach dem Download der uf2-Datei muss diese auf das Display mit dem MC übertragen werden. Im ersten Schritt nehmen Sie das Display im ausgeschalteten Zustand zur Hand und drücken die Boot-Taste (siehe Beschriftung auf der Platinenrückseite). Die wird gehalten und das Display erst dann (!!) über das USB-A zu USB-C Kabel mit dem Rechner verbunden. Nach dem Verbinden kann die Boot Taste losgelassen werden. Es öffnet sich im Filesystem ein Explorer-Fenster mit einem neuen Massenspeicher RPI-RP2 bzw. RP2350. Öffnen Sie jetzt auf dem Rechner den Download-Ordner und ziehen die uf2-Datei mit der CircuitPython Firmware auf den Massenspeicher RPI-RP2 bzw. RP2350. Sobald die Firmware-Datei auf das Display kopiert ist, wird die Firmware ausgeführt und ein neuer Massenspeicher 'CIRCUITPY' im Explorerfenster angezeigt. Damit ist die Programmiersprache CircuitPython für die Ansteuerung des runden Pico-Displays installiert und die Programmierung kann über Thonny beginnen.
3. Falls bei der ersten Benutzung von Thonny kein Board erkannt wird, muss in den Interpreter-Einstellungen die Port-Auswahl überprüft werden und ggf. auf CircuitPython (generic) eingestellt werden. Klicken Sie dazu ganz unten rechts auf den 'Button'. Das Fenster sollte jetzt so aussehen:

Jetzt öffnen Sie über das Thonny-Menü die Datei 'display_mit_text.py' und können sie ausführen. Wenn Sie die noch nicht heruntergeladen haben (siehe oben), sollten Sie das jetzt tun oder Sie kopieren den Code aus dem Kasten in die Zwischenablage indem Sie auf den Button 'Code kopieren' klicken und in Thonny einsetzen.
Sehen wir uns das Programm an und erklären die Details, die für unsere eigenen Anwendungen später wichtig sind.
1 # SPDX-FileCopyrightText: 2023 Detlef Gebhardt, written for CircuitPython 2 # SPDX-FileCopyrightText: Copyright (c) 2023 Detlef Gebhardt 3 # 4 # SPDX-License-Identifier: GEBMEDIA 5 import board 6 import busio 7 from busio import I2C 8 import displayio 9 import terminalio 10 import gc9a01 11 from adafruit_display_text import label 12 import fourwire 13 # Release any resources currently in use for the displays 14 displayio.release_displays() 15 16 # imu_int1 = board.IMU_INT1 # Beschleunigungssensor 17 # imu_int2 = board.IMU_INT2 # Beschleunigungssensor 18 # bat_adc = board.BAT_ADC # Baterieanzeige 19 20 # Make the displayio SPI bus and the GC9A01 display 21 spi = busio.SPI(clock=board.GP10, MOSI=board.GP11) 22 display_bus = fourwire.FourWire(spi, command=board.GP8, chip_select=board.GP9, reset=board.GP12) 23 display = gc9a01.GC9A01(display_bus, width=240, height=240, rotation=90, backlight_pin=board.GP25) 24 25 # Make the display context 26 splash = displayio.Group() 27 display.root_group = splash 28 # Make the background bitmap 29 color_bitmap = displayio.Bitmap(240, 240, 1) 30 color_palette = displayio.Palette(1) 31 color_palette[0] = 0x03C2FC 32 bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0) 33 splash.append(bg_sprite) 34 35 def text_show(): 36 text_group_t1 = displayio.Group(scale=2, x=20, y=70) 37 text1 = " rundes Display\n\n" + "1.28-Zoll-IPS-LCD\n\n" + " 240x240 Pixel" 38 text_area1 = label.Label(terminalio.FONT, text=text1, color=0x0000ff) 39 text_group_t1.append(text_area1) 40 splash.append(text_group_t1) 41 42 text_show() 43 44 while True: 45 pass
In den Zeilen 5 bis 11 werden alle benötigten Module importiert. Wichtig ist, dass sich die Module 'adafruit_display_text'
und 'gc9a01' (Treiber für das runde Display) im lib-Ordner befinden, sonst kommt später eine
Fehlermeldung. Die Zeilen 13 bis 23 initialisieren das Display und kommen in der Form in jedem späteren Programm vor. Die auskommentierten
Zeilen 16, 17, 18 nutzen später die Sensoren. Ein Hinweis sei auch zu Zeile 23 gegeben. Die Anweisung 'rotation=90'
dreht die Darstellung auf dem Display. Es sind nur 90 Grad Schritte möglich.
In Zeile 26 und 27 wird die Gruppe 'spash' definiert und für die Darstellung auf dem Display bereitgestellt. Die Zeilen 29 bis 33 erzeugen
das farbige Bitmap als Hintergrund und fügen es der Gruppe 'splash' hinzu. Jedes Objekt, welches auf dem Display dargestellt werden soll,
wird also zunächst defieniert und im Speicher abgelegt.
In gleicher Weise bin ich mit dem Text verfahren. Nur habe ich dafür eine Funktion geschrieben (Zeilen 35 bis 40) und aus Zeile 42 aufgerufen.
Da in unserem Fall noch nichts weiter passiert, steht in der 'while' Schleife die Anweisung 'pass', damit kein Fehler erzeugt wird. Später
erfolgen hier weitere Befehle.
Als nächstes aktivieren wir den ACC-Gyro-Sensor und entwickeln schrittweise eine Fitnessuhr, wie in der oberen Bildbox gezeigt.
Dazu kopieren Sie den unteren Quelltext und setzen ihn in ein neues Fenster im Thonny ein. Dazu ein paar Erläuterungen:
- In den Zeilen 8 und 9 werden die Treiber für das Display und den Sensor importiert.
- Da wir die Schritte später veranschaulichen wollen, wird eine 'Progressbar' verwendet. Dazu importieren wir den Treiber von Adafruit in Zeile 16.
- In den Zeilen 35 bis 44 werden das Display und der Sensor initialisiert.
- Der Rest ist Hintergrundgestaltung. Bei einem Klick auf 'Start' sehen Sie diesen, aber weiter noch nichts.
1 import time 2 import gc 3 import rtc 4 import board 5 import busio 6 import displayio 7 import terminalio 8 import gc9a01 9 import my_qmi8658 10 from adafruit_display_text import label 11 from adafruit_ticks import ticks_ms 12 from adafruit_display_shapes.rect import Rect 13 from adafruit_display_shapes.roundrect import RoundRect 14 from adafruit_display_shapes.circle import Circle 15 from adafruit_display_shapes.line import Line 16 from adafruit_progressbar.horizontalprogressbar import (HorizontalProgressBar, HorizontalFillDirection) 17 import fourwire 18 19 wdays = ["Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag","Sonntag"] 20 # Schritte lesen 21 step = 0 22 with open("time.txt", "r") as f: 23 step = int(f.readline()) 24 f.close() 25 with open("ziel.txt", "r") as f: 26 ziel = int(f.readline()) 27 f.close() 28 meter = 0 29 cal = 0 30 stepstop = 0 31 width = 140 32 height = 38 33 x = 50 34 y = 120 35 36 # Release any resources currently in use for the displays 37 displayio.release_displays() 38 # Make the displayio SPI bus and the GC9A01 display 39 spi = busio.SPI(clock=board.GP10, MOSI=board.GP11) 40 display_bus = fourwire.FourWire(spi, command=board.GP8, chip_select=board.GP9, reset=board.GP12) 41 display = gc9a01.GC9A01(display_bus, width=240, height=240, rotation=90, backlight_pin=board.GP25) 42 # display.brightness = 0.01 43 44 # acc initialisieren 45 sensor=my_qmi8658.QMI8658() 46 47 # Make the display context 48 main = displayio.Group() 49 display.root_group = main 50 51 # make bitmap that spans entire display, with 2 colors 52 background = displayio.Bitmap(240, 240, 1) 53 mypal = displayio.Palette(3) 54 mypal[0] = 0x921c1c 55 mypal[1] = 0xff9999 56 background.fill(0) 57 main.append(displayio.TileGrid(background, pixel_shader=mypal)) 58 background2 = Rect(0, 140, 240, 100, fill=0x00994c ) 59 60 main.append(background2) 61 line = Line(120,195,120,230,color=0x000000) 62 main.append(line) 63
Im 2. Teil werden die Progressbar, die Anzeigen der Uhrzeit, die Schritte und die entsprechenden Meter und verbrauchten KJoule im Speicher
hinzugefügt. Außerdem wird die Erfassung der Uhrzeit (Display am PC oder externe Stromversorgung) organisiert. Ich habe dazu bereits
früher erläutert, dass 'localtime()' bei externer Stromversorgung am 1.1.2000 um 00:00 Uhr beginnt, die Sekunden zu zählen. Deshalb
erfolgt bei jedem Start am PC die Erfassung der aktuellen Werte und Speicherung. Bei Bedarf wird dann auf die letzten bekannten Werte aus der Datei
'time.txt' zurückgegriffen. Die Zeilen 135 bis 144 sind dafür zuständig.
Normal ist das Dateisystem schreibgeschützt. Damit es beschrieben werden kann, braucht es die Datei 'boot.py', welche beim Systemstart
geladen wird. Achten Sie also darauf, dass Sie diese aus der entpackten zip-Datei mit auf das Displayboard kopiert haben. Falls nicht, führen Sie das jetzt
durch, entfernen dann die Stromzufuhr am Board und stellen sie für den Neustart wieder her.
64 # Create a new progress_bar object at (x, y) 65 progress_bar = HorizontalProgressBar((x, y+1), (width, height), direction=HorizontalFillDirection.LEFT_TO_RIGHT, 66 bar_color=0x3BD81C, fill_color= 0xd12929, outline_color=None, margin_size=False) 67 main.append(progress_bar) 68 circle1 = Circle(50, 140, 18, fill=0x3BD81C, outline=None) 69 main.append(circle1) 70 circle2 = Circle(190, 140, 18, fill=0xd12929, outline=None) 71 main.append(circle2) 72 73 #roundrect_f= RoundRect(30,120,160,40,20,fill=0x009900, outline=0x00ff00) 74 roundrect_f= RoundRect(31,121,178,38,19,fill=None, outline=0x00ff00) 75 main.append(roundrect_f) 76 77 #current_value = progress_bar.minimum 78 current_value = step/ziel * 100 79 80 # create the label for the time-hour and minute 81 updating_label_time = label.Label(font=terminalio.FONT, text="00:00", scale=3, color=0xffffff, line_spacing=1) 82 # set label position on the display and add label 83 updating_label_time.anchor_point = (0, 0) 84 updating_label_time.anchored_position = (60, 40) 85 main.append(updating_label_time) 86 87 # create the label for the time-second 88 updating_label_sec = label.Label(font=terminalio.FONT, text=":00", scale=2, color=0xffffff, line_spacing=1) 89 # set label position on the display and add label 90 updating_label_sec.anchor_point = (0, 0) 91 updating_label_sec.anchored_position = (155, 50) 92 main.append(updating_label_sec) 93 94 # create the label for the steps 95 updating_label_steps = label.Label(font=terminalio.FONT, text="", scale=2, color=0xffffff, line_spacing=1) 96 # set label position on the display and add label 97 updating_label_steps.anchor_point = (0, 0) 98 updating_label_steps.anchored_position = (65, 127) 99 main.append(updating_label_steps) 100 # create the label for the steps 101 text_group_step = displayio.Group(scale=1, x=90, y=170) 102 text_area1 = label.Label(terminalio.FONT, text="Schritte", color=0xffffff) 103 text_group_step.append(text_area1) 104 main.append(text_group_step) 105 106 # create the label for the meter/cal 107 updating_label_meter = label.Label(font=terminalio.FONT, text="", scale=2, color=0xffff00, line_spacing=1) 108 updating_label_meter.anchor_point = (0, 0) 109 updating_label_meter.anchored_position = (50, 180) 110 main.append(updating_label_meter) 111 updating_label_cal = label.Label(font=terminalio.FONT, text="", scale=2, color=0xffff00, line_spacing=1) 112 updating_label_cal.anchor_point = (0, 0) 113 updating_label_cal.anchored_position = (140, 180) 114 main.append(updating_label_cal) 115 116 current_time = time.localtime() 117 year = current_time.tm_year 118 hour = current_time.tm_hour 119 minute = current_time.tm_min 120 second = current_time.tm_sec 121 month = current_time.tm_mon 122 day = current_time.tm_mday 123 weekday = current_time.tm_wday 124 125 if current_time.tm_year > 2000: 126 with open("/time.txt", "w") as f: 127 f.write(str(step)+"\n") 128 f.write(str(year)+"\n") 129 f.write(str(month)+"\n") 130 f.write(str(day)+"\n") 131 f.write(str(hour)+"\n") 132 f.write(str(minute)+"\n") 133 f.write(str(weekday)+"\n") 134 f.close() 135 if current_time.tm_year == 2000: 136 with open("time.txt", "r") as f: 137 step = int(f.readline()) 138 year = int(f.readline()) 139 month = int(f.readline()) 140 day = int(f.readline()) 141 hour = int(f.readline()) 142 minute = int(f.readline()) 143 weekday = int(f.readline()) 144 f.close() 145 second = current_time.tm_sec 146 r = rtc.RTC() 147 r.datetime = time.struct_time((year, month, day, hour, minute, 0, weekday, 1, -1)) 148 149 # create the label for the date 150 text_group_day = displayio.Group(scale=2, x=25, y=95) 151 text_area1 = label.Label(terminalio.FONT, text="{:02}.{:02}. ".format(day,month) + wdays[weekday], color=0xffffff) 152 text_group_day.append(text_area1) 153 main.append(text_group_day) 154 155 start = ticks_ms() 156 hell = ticks_ms() 157 dunkel = False 158 x_alt = 0 159 y_alt = 0 160 z_alt = 0 161
Jetzt folgt die 'while'-Schleife. In den Zeilen 163 bis 170 werden die 'ACC-Werte' des Sensors gelesen. Zeile 164 holt mit
'xyz = sensor.Read_XYZ()' aus der Liste die Werte xyz[0], xyz[1], xyz[2]. Diese werden den Variablen 'wert_x',
'wert_y' und 'wert_z' zugewiesen (aber noch nicht, wie im Demobeispiel angezeigt).
In den Zeilen 171 bis 181 wird das Display hell bzw. dunkel geschaltet. Immer wenn eine Bewegung erfogt, schaltet das Display ein und nach ca.
5 Sekunden im Ruhezustand wieder ab.
Ab Zeile 182 geht es nur um die Uhrzeit. Jede Minute wird diese hochgezählt und die Progressbar läuft einmal bis zum bereits erreichten
Schrittwert (z.Z. die Zahl aus der Datei 'time.txt'). Wenn Sie das Programm starten, können Sie das sehen.
162 while True: 163 #read QMI8658 164 xyz=sensor.Read_XYZ() 165 # Display wird rel. zur x-Achse bewegt 166 wert_x = (10)*xyz[0] 167 # Display wird rel. zur y-Achse bewegt 168 wert_y = (10)*xyz[1] 169 # Display wird rel. zur z-Achse bewegt 170 wert_z = (10)*xyz[2] 171 # Display hell/dunkel 172 if abs(wert_x - x_alt) >= 1 or abs(wert_y - y_alt) >= 1: 173 display.brightness = 1 174 hell = ticks_ms() 175 dunkel = False 176 if (ticks_ms() - hell)/1000 > 5 and dunkel == False: 177 for i in range(50,1,-1): 178 display.brightness = i/50 179 time.sleep(0.05) 180 display.brightness = 0.01 181 dunkel = True 182 # Zeit holen 183 current_time = time.localtime() 184 hour = current_time.tm_hour 185 minute = current_time.tm_min 186 second = current_time.tm_sec 187 if hour == 0 and minute == 0 and second == 1: 188 month = current_time.tm_mon 189 day = current_time.tm_mday 190 weekday = current_time.tm_wday 191 if second == 59: 192 with open("/time.txt", "w") as f: 193 f.write(str(step)+"\n") 194 f.write(str(year)+"\n") 195 f.write(str(month)+"\n") 196 f.write(str(day)+"\n") 197 f.write(str(hour)+"\n") 198 f.write(str(minute)+"\n") 199 f.write(str(weekday)+"\n") 200 f.close() 201 for current_value in range(progress_bar.minimum, step/ziel * 100 + 1, 1): 202 progress_bar.value = current_value 203 time.sleep(0.01) 204 # 205 # Zeitstring zur Anzeige aufbereiten 206 # 207 updating_label_time.text = "{:02}:{:02}".format(hour,minute) 208 updating_label_sec.text = ":{:02}".format(second)
Der entscheidende Teil kommt jetzt mit dem 4. Schritt hinzu. Im unteren Kasten werden jetzt die Schritte registriert und angezeigt. Diesen Teil habe ich aus meinem Sensortest, den ich unter Micropython durchgeführt habe, übernommen. Den können Sie hier noch einmal ansehen. Mit den Werten kann man durchaus noch ein wenig experimentieren, aber grundsätzlich funktioniert alles und mein Hauptanliegen, den Sensor unter CircuitPython ans 'Laufen' zu bringen, ist erreicht. Besonders gefällt mir persönlich die 'Progressbar' von Adafruit, die sicher auch andere Projekte optisch aufwertet.
209 # 210 # Arm noch oben bewegen 211 # 212 if (abs(wert_x) < 7 and abs(wert_y) > 3 or abs(wert_x) < 7 and abs(wert_z) > 3) and stepstop == 0: 213 start = ticks_ms() 214 step += 1 215 stepstop = 1 216 updating_label_steps.text = str(step-1)+"/"+ str(ziel) 217 current_value = step/ziel * 100 218 progress_bar.value = current_value 219 meter = int((step-1)*0.762) 220 cal = int(int((step-0.38)*0.38)*4.18)/1000 221 updating_label_meter.text = str(meter) + " " + "\n m" 222 updating_label_cal.text = str(cal) + " " + "\n kJ" 223 # 224 # Arm wieder nach unten 225 # 226 if (abs(wert_x) > 7 and abs(wert_y) < 3 or abs(wert_x) > 7 and abs(wert_z) < 3) and stepstop == 1: 227 if (ticks_ms() - start)> 1000: 228 stepstop = 0 229 updating_label_steps.text = str(step)+"/"+ str(ziel) 230 progress_bar.value = current_value 231 # 232 # Schritte zuruecksetzen 233 # 234 if step == ziel: 235 step = 0 236 progress_bar.value = progress_bar.minimum 237 time.sleep(0.2) 238 239 x_alt = wert_x 240 y_alt = wert_y 241 242 gc.collect() 243 #print(gc.mem_free())
Viel Spass und Erfolg beim Ausprobieren.
Viel Spass und Erfolg beim Ausprobieren.