Experimente
Neopixeluhr am PicoBoy-Color
Projekte und Anleitung in CircuitPython 9.x.x
Bildbox 1 (klick hier)
Experimente
Neopixeluhr am PicoBoy-Color
Projekte und Anleitung in CircuitPython 9.x.x
Bildbox 1 (klick hier)
Es folgt die angekündigte Neopixeluhr. Sie baut auf dem Beispiel
'temp_uhr.py' von hier auf.
Hardware / Software
- PicoBoy - Color
- Neopixelring 12 LED
- USB-C Kabel
- Firmware CircuitPython 9.x.x
- neopixel.mpy (aus dem CircuitPython Bundle)
Los gehts
Die ursprünglichen Zeilen 1 bis 41 sahen so aus:
1 import time 2 import gc 3 import rtc 4 import board 5 import busio 6 from adafruit_display_text import label 7 from adafruit_display_shapes.roundrect import RoundRect 8 import terminalio 9 import displayio 10 import keypad 11 from adafruit_st7789 import ST7789 12 from adafruit_bme280 import basic as adafruit_bme280 13 14 #I2C Pin of the PicoBoy - Color 15 SDA = board.GP20 16 SCL = board.GP21 17 # Create sensor object, using the board's default I2C bus. 18 i2c = busio.I2C(SCL, SDA) 19 bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c, address=0x77) 20 21 # change this to match the location's pressure (hPa) at sea level 22 bme280.sea_level_pressure = 1030 23 24 # 1,69 Zoll 240x280 Pixel ST7789 Display 25 # SPI-Bus, in Landscape format 26 dc=board.GP8 27 reset=board.GP9 28 cs=board.GP10 29 sck=board.GP18 30 mosi=board.GP19 31 bl=board.GP26 32 # Release any resources currently in use for the displays 33 displayio.release_displays() 34 spi = busio.SPI(sck, mosi) 35 display_bus = displayio.FourWire(spi, command=dc, chip_select=cs, reset=reset) 36 display = ST7789(display_bus, rotation=90, width=280, height=240, backlight_pin=bl, rowstart=20, colstart=0) 37 display.brightness = 1 38 # Make the display context 39 splash = displayio.Group() 40 timeset= displayio.Group() 41 display.root_group = splash
Da wir jetzt keinen Temperatursensor angeschlossen haben, löschen Sie die Zeilen 12 bis 22. Zur Nutzung der Button werden folgende Zeilen ergänzt:
32 K_UP = 0x04 33 K_DOWN = 0x02 34 K_RIGHT = 0x08 35 K_LEFT = 0x01 36 K_A = 0x10 37 K_B = 0x20 38 K_Z = 0x40 39 40 class _Buttons: 41 def __init__(self): 42 self.keys = keypad.Keys((board.GP4, board.GP3, board.GP1, board.GP2, board.GP27, board.GP28, board.GP0), 43 value_when_pressed=False, interval=0.05) 44 self.last_state = 0 45 self.event = keypad.Event(0, False) 46 self.last_z_press = None 47 48 def get_pressed(self): 49 buttons = self.last_state 50 events = self.keys.events 51 while events: 52 if events.get_into(self.event): 53 bit = 1 << self.event.key_number 54 if self.event.pressed: 55 buttons |= bit 56 self.last_state |= bit 57 else: 58 self.last_state &= ~bit 59 return buttons 60 buttons = _Buttons() 61
Für die Anzeige der Uhrzeit auf dem Display und die Stellfunktion werden jetzt die Zeichen- und Textelemente ergänzt:
62 # Make a background color fill 63 color_bitmap = displayio.Bitmap(display.width, display.height, 3) 64 color_palette = displayio.Palette(3) 65 color_palette[0] = 0x660088 66 color_palette[1] = 0x000000 67 color_palette[2] = 0xffffff 68 bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0) 69 splash.append(bg_sprite) 70 # Draw a label 71 text_area = label.Label(font=terminalio.FONT, text="00:00:00", color=0xFFFF00, scale=5, line_spacing=1 ) 72 text_group = displayio.Group(x=25, y=110) 73 text_group.append(text_area) 74 splash.append(text_group) 75 76 # roundrect in timeset 77 roundrect1 = RoundRect(25, 40, 230, 70, 10, fill=0x0, outline=0xffffff, stroke=3) 78 timeset.append(roundrect1) 79 # create the label for time in timeset 80 updating_label_zeit = label.Label(font=terminalio.FONT, text="00:00:00", scale=4, color=0xffffff, line_spacing=1) 81 updating_label_zeit.anchor_point = (0, 0) 82 updating_label_zeit.anchored_position = (45, 50) 83 timeset.append(updating_label_zeit) 84 #create the label for timeset function 85 updating_label = label.Label(font=terminalio.FONT, scale=2, color=0x00ff00, line_spacing=1) 86 updating_label.text = "up : hour up\ndown : hour down\nright: min up\nleft : min down\nexit : ok" 87 updating_label.anchor_point = (0, 0) 88 updating_label.anchored_position = (40, 120) 89 timeset.append(updating_label) 90
Es folgen der Import der Neopixel-Bibliotheken und die Definition der Funktionen für die Effekte:
91 import neopixel 92 from rainbowio import colorwheel 93 94 num_pixels = 12 95 pixel_pin = board.GP20 96 pixels = neopixel.NeoPixel(pixel_pin, num_pixels, brightness=0.05, auto_write=False) 97 #strip1 = neopixel.NeoPixel(pixel_pin, num_pixels, brightness=0.15) 98 99 sine1 = [ # These are the pixels in order of animation - 12 pixels in total: 100 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] 101 102 COLORS = ( 103 (155, 0, 0), 104 ( 0, 155, 0), 105 ( 0, 0, 155), 106 (155, 155, 0), 107 (155, 0, 155), 108 ( 0, 155, 155), 109 ) 110 111 def rainbow(speed): 112 for k in range (2): 113 for j in range(255): 114 for i in range(12): 115 pixel_index = (i * 256 // 12) + j 116 pixels[i] = colorwheel(pixel_index & 255) 117 pixels.show() 118 time.sleep(speed) 119 120 def colorwheel(pos): 121 if pos < 0 or pos > 255: 122 return (0, 0, 0, 0) 123 if pos < 85: 124 return (255 - pos * 3, pos * 3, 0, 0) 125 if pos < 170: 126 pos -= 85 127 return (0, 255 - pos * 3, pos * 3, 0) 128 pos -= 170 129 return (pos * 3, 0, 255 - pos * 3, 0) 130 131 def rainbow_cycle(wait): 132 for j in range(255): 133 for i in range(num_pixels): 134 rc_index = (i * 256 // num_pixels) + j 135 pixels[i] = colorwheel(rc_index & 255) 136 pixels.show() 137
Wir kommen nun zur eigentlichen Uhr. Vor der 'While'-Schleife werden noch zwei Variable definiert (debounce und stellen) und auf den logischen
Wert 'False' gesetzt. Außerdem werden alle Neopixel-LEDs ausgeschaltet.
Da der Timer des Microcontrollers ohne Synchronisation am PC mit 1.1.2020 um 0:00 Uhr startet brauchen wir eine Stellmöglichkeit. Die
übernehmen wir aus dem o.g. Beispiel 'temp_uhr.py'. Im unteren Kasten sind das die Zeilen 155 bis 226. Kopieren Sie alle Zeilen zu den
schon vorhandenen hinzu. Zeilen, die wir erst später nutzen sind zunächst auskommentiert. Aufgerufen wird der
Stellmodus mit dem Taster A. Zum Stellen werden die Joystick-Funktionen genutzt. Verlassen wird der Stellmodus mit dem Joystick 'ok'.
Ein Tipp zum Vorgehen: Stellen Sie zuerst die aktuelle Stunde ein. Dann die Minute um eins höher als die aktuelle Minute. Drücken Sie
dann im Abgleich mit einer Vergleichsuhr (Handy od. PC) auf 'ok', wenn die Sekunde von 59 auf 00 umschaltet. So kann der Start recht gut eingestellt
werden. Zur Genauigkeit des Timers über einen längeren Zeitraum habe ich z.Z. noch keine Erfahrung.
138 pixels.fill((0 , 0, 0)) 139 pixels.show() 140 stellen = False 141 debounce = False 142 143 while True: 144 ##### 145 # Zeit stellen 146 ##### 147 cur_btn_vals = buttons.get_pressed() 148 cur_up = cur_btn_vals & K_UP 149 cur_left = cur_btn_vals & K_LEFT 150 cur_right = cur_btn_vals & K_RIGHT 151 cur_down = cur_btn_vals & K_DOWN 152 cur_a = cur_btn_vals & K_A 153 cur_b = cur_btn_vals & K_B 154 cur_exit = cur_btn_vals & K_Z 155 # Stellen aufrufen 156 if cur_a and not debounce: 157 stellen = True 158 debounce = True 159 r = rtc.RTC() 160 r.datetime = time.struct_time((2024, 11, 3, 12, minute, 0, 6, 1, -1)) 161 display.root_group = timeset 162 163 #if cur_b and not debounce: 164 # debounce = True 165 # text_area.scale=3 166 # text_area.text = " fasten your\n\n seat belt" 167 # for wait in range(10, 1, -2): 168 # for color in COLORS: 169 # for i in range(len(sine1)): 170 # # Set 'head' pixel to color: 171 # pixels[sine1[i]] = color 172 # # Erase 'tail,' 8 pixels back: 173 # pixels[sine1[(i + len(sine1) - 1) % len(sine1)]] = [0, 0, 0] 174 # pixels.write() # Refresh LED states 175 # time.sleep(wait/300) # xx millisecond delay 176 # # rainbow cycle 177 # rainbow_cycle(0) 178 # pixels.fill(( 0, 0, 0)) 179 # text_area.scale=5 180 # Stellen verlassen 181 if cur_exit and not debounce: 182 r = rtc.RTC() 183 r.datetime = time.struct_time((2024, 11, 3, hour, minute, 0, 6, 1, -1)) 184 stellen = False 185 debounce = True 186 display.root_group = splash 187 #rainbow(0) 188 #pixels.fill(( 0, 0, 0)) 189 if stellen == True: 190 updating_label_zeit.text = "{:02}:{:02}:00".format(current_time.tm_hour,current_time.tm_min) 191 # Stunden erhoehen 192 if cur_up and not debounce: 193 debounce = True 194 if hour < 23: 195 hour += 1 196 else: 197 hour = 0 198 r = rtc.RTC() 199 r.datetime = time.struct_time((2024, 11, 3, hour, minute, 0, 6, 1, -1)) 200 # Stunden verringern 201 if cur_down and not debounce: 202 debounce = True 203 if hour > 0: 204 hour -= 1 205 else: 206 hour = 23 207 r = rtc.RTC() 208 r.datetime = time.struct_time((2024, 11, 3, hour, minute, 0, 6, 1, -1)) 209 # Minuten erhoehen 210 if cur_right and not debounce: 211 debounce = True 212 if minute < 59: 213 minute += 1 214 else: 215 minute = 0 216 r = rtc.RTC() 217 r.datetime = time.struct_time((2024, 11, 3, hour, minute, 0, 6, 1, -1)) 218 # Minuten verringern 219 if cur_left and not debounce: 220 debounce = True 221 if minute > 0: 222 minute -= 1 223 else: 224 minute = 59 225 r = rtc.RTC() 226 r.datetime = time.struct_time((2024, 11, 3, hour, minute, 0, 6, 1, -1)) 227 if cur_btn_vals == 0: 228 debounce = False 229 ##### 230 # Zeit anzeigen 231 ##### 232 current_time = time.localtime() 233 hour = current_time.tm_hour 234 if hour > 12: 235 neohour = hour -12 236 minute = current_time.tm_min 237 second = current_time.tm_sec 238 text_area.text = "{:02}:{:02}:{:02}".format(current_time.tm_hour,current_time.tm_min,current_time.tm_sec) 239
Zum Abschluss werden die Neopixeleffekte hinzugefügt. Und zwar:
- Anzeige der aktuellen Stunde rot
- Anzeige der Minuten (nach jeweils 60/12 = 5 Minuten) in grün
- Anzeige der Sekunden blau (vergangene Sekunde wird schwächer, folgende Sekunde stärker)
- zu jeder vollen Minute erfolgt ein Lichteffekt
- mit der Taste B wird ein besonderer Lichteffekt aufgerufen.
Eine Besonderheit wird Ihnen auffallen. Wenn sich zwei 'Zeiger' überdecken, wird nur die grüne Minute-LED angezeigt.
Diese überdeckt dann für fünf Minuten die rote Stunden-LED.
Fügen Sie den restlichen Quellcode zum Programm hinzu und speichern
ihn als 'code.py'. In den Zeilen 163 bis 179 entfernen Sie die Auskommentierung, um den Effekt für die
Taste B zu aktivieren. Beim Start am Rechner braucht keine Zeit eingestellt werden. Ohne Rechner ist die
Einstellung anzuraten, wenn Sie nicht in der Stunde 'Null' leben wollen.
240 # Lichteffekt zur vollen Minute 241 if second == 0 : 242 rainbow(0) 243 pixels.fill(( 0, 0, 0)) 244 second += 1 245 # Position Stunde, Minute, Sekunde 246 hour_pixel = neohour - 6 247 minute_pixel = int(minute / 5) - 6 248 second_pixel = int(second / 5) - 6 249 pixels[second_pixel] = (0, 0, 255) 250 # Sekunden-LED wird heller 251 if second % 5 == 1: 252 pixels[second_pixel + 1] = (0, 0, 25) 253 if second % 5 == 2: 254 pixels[second_pixel + 1] = (0, 0, 50) 255 if second % 5 == 3: 256 pixels[second_pixel + 1] = (0, 0, 75) 257 if second % 5 == 4: 258 pixels[second_pixel + 1] = (0, 0, 120) 259 if second % 5 == 5: 260 pixels[second_pixel + 1] = (0, 0, 255) 261 # Sekunden-LED wird schwaecher 262 if second % 5 == 1: 263 pixels[second_pixel] = (0, 0, 120) 264 if second % 5 == 2: 265 pixels[second_pixel] = (0, 0, 75) 266 if second % 5 == 3: 267 pixels[second_pixel] = (0, 0, 50) 268 if second % 5 == 4: 269 pixels[second_pixel] = (0, 0, 25) 270 if second % 5 == 5: 271 pixels[second_pixel] = (0, 0, 0) 272 pixels[second_pixel -1] = (0, 0, 0) 273 # Sekunde-LED wird von Minute oder Stunde ueberdeckt 274 if pixels[second_pixel] == pixels[minute_pixel]: 275 pixels[second_pixel] = (100, 100, 100) 276 if pixels[second_pixel] == pixels[hour_pixel]: 277 pixels[second_pixel] = (100, 0, 100) 278 if pixels[minute_pixel] == pixels[hour_pixel]: 279 pixels[minute_pixel] = (255, 100, 0) 280 pixels[hour_pixel] = (255, 0, 0) 281 pixels[minute_pixel] = (0, 255, 0) 282 pixels.show()
Hier noch ein kurzer Ausschnitt vom ersten Test.
Viel Spass und Erfolg beim Ausprobieren.
Hier noch ein kurzer Ausschnitt vom ersten Test.
Viel Spass und Erfolg beim Ausprobieren.