PicoBoy - Digitaluhr mit Weckfunktion
technische Daten wie beim RPi Pico
MC RP2040 mit OLED-Display 128x64 Pixel
Anleitung für die Firmware CircuitPython
Hardware
- PicoBoy
- USB-A zu USB-C Kabel
PicoBoy - Digitaluhr mit Weckfunktion
technische Daten wie beim RPi PicoMC RP2040 mit OLED-Display 128x64 Pixel
Anleitung für die Firmware CircuitPython
- USB-A zu USB-C Kabel
Nachdem im vorigen Teil die Benutzung des Joysticks und die Ansteuerung der LED's erklärt wurde, soll hier zunächst die Ansteuerung
des Lautsprechers gezeigt werden.
Los gehts
Angesteuert wird der kleine Lautsprecher auf der PicoBoy Platine mit dem GPIO 15. Über die GPIO Ports können keine analogen Signale ausgegeben werden, sondern nur digitale Zustände 0 oder 1. Man kann aber mit Hilfe der s.g. Puls-Weiten-Modulation (PWM) z.B. gedimmte LEDs und variable Motordrehzahlen realisieren. Oder wie in unserem Fall den Lautsprecher zur Abgabe von Tönen bringen. Per Software erzeugt man ein pulsweitenmoduliertes Signal mithilfe einer Schleife, in der der Ausgang den Zustand ständig wechselt und zwischendurch etwas pausiert. Das Beispiel im unteren Kasten zeigt, wie der Lautsprecher (Leisesprecher) und die rote LED angesprochen werden. Dabei ertönt der Lautsprecher zehn mal und die LED wird mit ansteigender Frequenz heller, bis sie ausgeschaltet wird. Wenn die Variable den Wert 10 erreicht hat, wird die while-Schleife mit 'break' abgebrochen. Zuvor wird aber der Lautsprecher mit 'deinit' abgeschaltet, damit der GPIO strommäßig nicht überlastet wird, worauf der Hersteller hinweist.
1 import time 2 import board 3 import busio 4 import displayio 5 import fourwire 6 import digitalio 7 import adafruit_displayio_sh1106 8 import pwmio 9 10 # GP15: Boardeigener Speaker, duty_cycle: zwischen 0 und 65535, frequency zwischen 1 Hz und 48 MHz 11 speaker = pwmio.PWMOut(board.GP15, frequency=1000, variable_frequency=True) 12 led_red = pwmio.PWMOut(board.GP5) 13 14 i = 0 15 16 while True: 17 for cycle in range(0, 65535, +2): # Cycles through the full PWM range from 0 to 65535 18 led_red.duty_cycle = cycle 19 speaker.duty_cycle = 32768 20 time.sleep(0.2) 21 speaker.duty_cycle = 0 22 time.sleep(0.2) 23 i += 1 24 if i == 10: 25 led_red.duty_cycle = 0 26 speaker.deinit() 27 break
Damit sind nun alle 'Zutaten' beisammen, um das Programm in der versprochenen Art zu erweitern. Es soll eine Uhrfunktion
mit:
digitale Anzeige der Uhrzeit
Anzeige des Datums
Anzeige des Wochentages
und einer
Weckfunktion,
bei welcher der Lautsprecher einen Ton gibt und die rote LED blinkt.
Hier kommt der erste Teil des Programms, welcher überwiegend in den vorangegangenen Beispielen vorkam. Ergänzt wurde er um
DisplayGroups (Zeilen 41 bis 49)und die entsprechenden Label für das 'Stellmenü' und die 'Untermenüs' (Zeilen 103 bis 121). Die
Definition des Kreises in Zeile 108 und 109 stellt im 'Stellmenü' den 'Optionbutton' dar.
1 import time 2 import board 3 import busio 4 import rtc 5 import displayio 6 import fourwire 7 import digitalio 8 import terminalio 9 from adafruit_display_text import label 10 from adafruit_display_shapes.rect import Rect 11 from adafruit_display_shapes.circle import Circle 12 import adafruit_displayio_sh1106 13 import pwmio 14 15 # Compatibility with both CircuitPython 8.x.x and 9.x.x. 16 # Remove after 8.x.x is no longer a supported release. 17 try: 18 from fourwire import FourWire 19 except ImportError: 20 from displayio import FourWire 21 22 #DC = board.GP8 23 #Reset = board.GP9 24 #CS = board.GP10 25 #SCK = board.GP18 26 #MOSI = board.GP19 27 28 # built-in a display 29 displayio.release_displays() 30 # Make the displayio SPI bus and the sh1106 display 31 spi = busio.SPI(board.GP18, board.GP19) 32 # 33 # Circuit 8.x.x 34 #display_bus = displayio.FourWire(spi, command=board.GP8, chip_select=board.GP10, reset=board.GP9, baudrate=1000000) 35 #display =adafruit_displayio_sh1106.SH1106(display_bus, width=132, height=64, rotation=0, brightness = 1) 36 # 37 # Circuit 9.x.x 38 display_bus = FourWire(spi, command=board.GP8, chip_select=board.GP10, reset=board.GP9, baudrate=1000000) 39 display =adafruit_displayio_sh1106.SH1106(display_bus, width=132, height=64, rotation=0, brightness = 1) 40 41 # Make the display contexts 42 splash = displayio.Group() 43 display.root_group = splash 44 setup = displayio.Group() 45 display.root_group = setup 46 set_hour_min = displayio.Group() 47 display.root_group = set_hour_min 48 set_datum = displayio.Group() 49 display.root_group = set_datum 50 51 #rote LED 52 #led_red = digitalio.DigitalInOut(board.GP5) 53 #led_red.direction = digitalio.Direction.OUTPUT 54 led_red = pwmio.PWMOut(board.GP5) 55 speaker = pwmio.PWMOut(board.GP15) 56 #gelbe LED 57 led_yellow = digitalio.DigitalInOut(board.GP6) 58 led_yellow.direction = digitalio.Direction.OUTPUT 59 #grüne LED 60 led_green = digitalio.DigitalInOut(board.GP7) 61 led_green.direction = digitalio.Direction.OUTPUT 62 63 ## Joystick 64 #JOY_UP = GP1 65 up = digitalio.DigitalInOut(board.GP1) 66 up.direction = digitalio.Direction.INPUT 67 up.pull = digitalio.Pull.UP 68 #JOY_DOWN = GP3 69 down = digitalio.DigitalInOut(board.GP3) 70 down.direction = digitalio.Direction.INPUT 71 down.pull = digitalio.Pull.UP 72 #JOY_LEFT = GP4 73 left = digitalio.DigitalInOut(board.GP4) 74 left.direction = digitalio.Direction.INPUT 75 left.pull = digitalio.Pull.UP 76 #JOY_RIGHT = GP2 77 right = digitalio.DigitalInOut(board.GP2) 78 right.direction = digitalio.Direction.INPUT 79 right.pull = digitalio.Pull.UP 80 #JOY_CENTER = GP0 81 center = digitalio.DigitalInOut(board.GP0) 82 center.direction = digitalio.Direction.INPUT 83 center.pull = digitalio.Pull.UP 84 85 zeit = "00:00:00" 86 wotage = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"] 87 alarm = [6,0] 88 option = 1 89 90 # create the label 91 updating_zeit = label.Label(font=terminalio.FONT, text="", scale=2, color=0xffffff, line_spacing=1) 92 # set label position on the display 93 updating_zeit.anchor_point = (0, 0) 94 updating_zeit.anchored_position = (15, 20) 95 # add label to group that is showing on display 96 splash.append(updating_zeit) 97 98 updating_label = label.Label(font=terminalio.FONT, text="", scale=1, color=0xffffff, line_spacing=1) 99 updating_label.anchor_point = (0, 0) 100 updating_label.anchored_position = (45, 3) 101 splash.append(updating_label) 102 103 updating_setup = label.Label(font=terminalio.FONT, text="Uhr stellen\n Uhrzeit\n Datum\n Alarm\n Exit", scale=1, color=0xffffff, line_spacing=1) 104 updating_setup.anchor_point = (0, 0) 105 updating_setup.anchored_position = (30, 0) 106 setup.append(updating_setup) 107 108 circle_auswahl = Circle(50, 17, 3, fill=0xffff00, outline=0x000000 ) 109 setup.append(circle_auswahl) 110 111 # Stunde und Minute stellen 112 updating_stunde_minute = label.Label(font=terminalio.FONT, text="00:00:00", scale=2, color=0xffffff, line_spacing=1) 113 updating_stunde_minute.anchor_point = (0, 0) 114 updating_stunde_minute.anchored_position = (15, 20) 115 set_hour_min.append(updating_stunde_minute) 116 117 # Datum stellen 118 updating_datum = label.Label(font=terminalio.FONT, text="01:01:2024", scale=1, color=0xffffff, line_spacing=1) 119 updating_datum.anchor_point = (0, 0) 120 updating_datum.anchored_position = (45, 25) 121 set_datum.append(updating_datum) 122
Die einzelnen 'Menüs' sind in den Funktionen
def Uhrzeit_stellen(year, month, day, hour, minute, tag),
def Datum_stellen(year, month, day, hour, minute, tag) und
def Alarmzeit_stellen(a_hour, a_minute)
enthalten, denen die Variable in den Klammern übergeben werden. Mit Hilfe des Joystick
lassen sich durch
hoch - runter die Stunden und mit
rechts - links die Minuten stellen.
Bei Joystick gedrückt wird der Vorgang aufgerufen bzw. beendet. Interessant sind dabei die Zeilen 156, 157 und 193, 194. Hier wird der
Timer verändert, indem die neuen Werte für time.localtime() als time.struct_time() geschrieben werden. Im unteren Kasten sind die
Funktionen zum Kopieren enthalten:
123 def Datum_stellen(year, month, day, hour, minute, tag): 124 display.root_group = set_datum 125 while True: 126 if up.value == False: 127 day += 1 128 if day == 32: 129 day = 1 130 if down.value == False: 131 day -= 1 132 if day == -1: 133 day = 31 134 if right.value == False: 135 month += 1 136 if month == 13: 137 month = 1 138 year +=1 139 if left.value == False: 140 month -= 1 141 if month == -1: 142 month = 12 143 year -= 1 144 time.sleep(0.2) 145 # Anzeige von Datum 146 if day < 10: 147 datum = "0" + str(day) + "." 148 else: 149 datum = str(day) + "." 150 if month < 10: 151 datum = datum + "0" + str(month) + "." + str(year) 152 else: 153 datum = datum + str(month) + "." + str(year) 154 updating_datum.text = datum 155 if center.value == False: 156 r = rtc.RTC() 157 r.datetime = time.struct_time((year, month, day, hour, minute, 0, tag, 1, -1)) 158 time.sleep(0.2) 159 display.root_groop = splash 160 break 161 162 def Uhrzeit_stellen(year, month, day, hour, minute, tag): 163 display.root_group = set_hour_min 164 while True: 165 if up.value == False: 166 hour += 1 167 if hour == 24: 168 hour = 0 169 if down.value == False: 170 hour -= 1 171 if hour == 0: 172 hour = 23 173 if right.value == False: 174 minute += 1 175 if minute == 60: 176 minute = 0 177 if left.value == False: 178 minute -= 1 179 if minute == -1: 180 minute = 59 181 time.sleep(0.2) 182 ## Anzeige beim Stellen der Uhr 183 if hour < 10: 184 zeit = "0" + str(hour) 185 else: 186 zeit = str(hour) 187 if minute < 10: 188 zeit = zeit + ":0" + str(minute) + ":00" 189 else: 190 zeit = zeit + ":" + str(minute) + ":00" 191 updating_stunde_minute.text = zeit 192 if center.value == False: 193 r = rtc.RTC() 194 r.datetime = time.struct_time((year, month, day, hour, minute, 0, tag, 1, -1)) 195 time.sleep(0.2) 196 display.root_groop = splash 197 break 198 199 def Alarmzeit_stellen(a_hour, a_minute): 200 display.root_group = set_hour_min 201 while True: 202 if up.value == False: 203 a_hour += 1 204 if a_hour == 24: 205 a_hour = 0 206 if down.value == False: 207 a_hour -= 1 208 if a_hour == 0: 209 a_hour = 23 210 if right.value == False: 211 a_minute += 1 212 if a_minute == 60: 213 a_minute = 0 214 if left.value == False: 215 a_minute -= 1 216 if a_minute == -1: 217 a_minute = 59 218 time.sleep(0.2) 219 ## Anzeige beim Stellen der Uhr 220 if a_hour < 10: 221 zeit = "0" + str(a_hour) 222 else: 223 zeit = str(a_hour) 224 if a_minute < 10: 225 zeit = zeit + ":0" + str(a_minute) 226 else: 227 zeit = zeit + ":" + str(a_minute) 228 updating_stunde_minute.text = zeit 229 if center.value == False: 230 alarm[0] = a_hour 231 alarm[1] = a_minute 232 display.root_groop = splash 233 led_green.value = True 234 break 235
Die Funktionen werden später aus der while-Schleife heraus aufgerufen. In den folgenden Programmzeilen 236 bis 250 werden die Variable 'hour', 'minute', 'second', 'year', 'month', 'day' sowie 'tag' für Wochentag beim ersten Programmstart mit Hilfe von 'time.localtime()' mit Werten belegt. Wenn 'year == 2020' ist, wird davon ausgegangen, dass der Start nicht am Rechner erfolgte und die Variable 'stellen = 1' für das Stellmenü gesetzt.
236 current_time = time.localtime() 237 hour = current_time.tm_hour 238 minute = current_time.tm_min 239 second = current_time.tm_sec 240 year = current_time.tm_year 241 month = current_time.tm_mon 242 day = current_time.tm_mday 243 tag = current_time.tm_wday 244 datum = str(day) + "." + str(month) + "." + str(year) 245 if current_time.tm_year > 2020: 246 stellen = 0 247 if current_time.tm_year == 2020: 248 year = 2024 249 stellen = 1 250
Der Wert für das Jahr 2024 in Zeile 248 wurde gewählt, damit man beim Stellen nicht immer durch die ohnehin
zurückliegenden Jahre 'scrollen' muss. Wird das Programm länger genutzt, sollte der Wert angepasst werden.
Es folgt die while-Schleife mit der Programmabarbeitung.
252 while True: 252 if stellen == 0: 253 display.root_group = splash 254 current_time = time.localtime() 255 hour = current_time.tm_hour 256 minute = current_time.tm_min 257 second = current_time.tm_sec 258 year = current_time.tm_year 259 month = current_time.tm_mon 260 day = current_time.tm_mday 261 tag = current_time.tm_wday 262 datum = str(day) + "." + str(month) + "." + str(year) 263 if stellen == 1: 264 display.root_group = setup 265 if (down.value) == False: 266 circle_auswahl.fill = 0x000000 267 circle_auswahl.y = circle_auswahl.y + 12 268 time.sleep(0.4) 269 circle_auswahl.fill = 0xffff00 270 if option < 5: 271 option +=1 272 if option == 5: 273 circle_auswahl.y = 15 274 option = 1 275 if up.value == False: 276 circle_auswahl.fill = 0x000000 277 circle_auswahl.y = circle_auswahl.y - 12 278 time.sleep(0.4) 279 circle_auswahl.fill = 0xffff00 280 if option > 0: 281 option -=1 282 if option == 0: 283 circle_auswahl.y = 51 284 option = 4 285 if center.value == False: 286 time.sleep(0.4) 287 if option == 1: 288 Uhrzeit_stellen(year, month, day, hour, minute, tag) 289 stellen = 0 290 if option == 2: 291 Datum_stellen(year, month, day, hour, minute, tag) 292 stellen = 0 293 if option == 3: 294 a_hour = alarm[0] 295 a_minute = alarm[1] 296 Alarmzeit_stellen(a_hour, a_minute) 297 stellen = 0 298 display.root_groop = splash 299 if option == 4: 300 stellen = 0 301 option = 1 302 display.root_groop = splash 303 if center.value == False and stellen == 0: 304 stellen = 1 305 time.sleep(0.6) 306 if hour < 10: 307 zeit = "0" + str(hour) 308 else: 309 zeit = str(hour) 310 if minute < 10: 311 zeit = zeit + ":0" + str(minute) 312 else: 313 zeit = zeit + ":" + str(minute) 314 if second < 10: 315 zeit = zeit + ":0" + str(second) 316 else: 317 zeit = zeit + ":" + str(second) 318 ## Wochentag, Datum und Zeit anzeigen 319 updating_zeit.text = zeit 320 updating_label.text = wotage[tag] + "\n\n\n\n" + datum 321 if alarm[0] == hour and alarm[1] == minute: 322 led_green.value = False 323 for cycle in range(0, 65535, +2): # Cycles through the full PWM range from 0 to 65535 324 led_red.duty_cycle = cycle # Cycles the LED pin duty cycle through the range of values 325 speaker.duty_cycle = 32678 326 for cycle in range(65534, 0, -2): # Cycles through the PWM range backwards from 65534 to 0 327 led_red.duty_cycle = cycle # Cycles the LED pin duty cycle through the range of values 328 speaker.duty_cycle = 0 329
Auch hierzu noch ein paar Erläuterungen:
Die Zeilen 252 bis 262 aktualisieren bei jedem Schleifendurchlauf die Variable für die Anzeige von Zeit und Datum, während ab
Zeile 263 das 'Setup-Menü' zum Stellen aufgerufen wird, falls die Bedingung erfüllt ist (siehe unteres Bild). Die leuchtende
grüne LED zeigt an, dass bereits eine Alarmzeit eingestellt wurde.
Mit dem Joystick navigiert man zur gewünschten Option und bestätigt diese durch Drücken mittig. Es öffnet sich das
entsprechende 'Untermenü' im dem Uhrzeit, Datum oder Alarmzeit eingestellt werden können. Mit einem Druck auf den Joystick mittig,
wird jede Aktion abgeschlossen. Auch über 'Exit' kann das Menü verlassen werden.
Wenn die eingestellte Alarmzeit erreicht ist, meldet sich die blinkende rote LED und das etwas mickrig klingende Signal des Minilautsprechers
für eine Minute. Eine Abstellmöglichkeit ist z.Z. nicht vorgesehen.
Damit ist der PicoBoy in Verbindung mit dem Gehäuse aus dem 3-D Drucker eine richtige kleine Digitaluhr z.B. für den Schreibtisch. Obwohl
ich nur an wenigen Stellen Kommentarzeilen eingefügt habe, hoffe ich dennoch, dass die Anleitung verständlich war. Wenn Sie meinen, man
kann das Programm an vielen Stellen noch weiter verkürzen und optimieren, sind Sie gerne zu weiteren eigenen Veränderungen aufgerufen. Sie
können den Quellcode für private Zwecke vollständig oder teilweise nutzen und beliebig verändern. Falls es doch Probleme
gibt, nutzen Sie den kompletten Quellcode in Form der zip-Datei
von hier, und erst dann wenden Sie sich bitte per e-Mail an mich. Konkrete Fragen zum Thema PicoBoy, CircuitPython oder RPI-Pico werde ich
gerne beantworten.
Viel Spass und Erfolg beim Ausprobieren.
Viel Spass und Erfolg beim Ausprobieren.