PicoBoy - elektronische Wasserwaage
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
Software
- CircuitPython 9.0.0-beta oder neuer
- Bibliotheken im 'lib'-Ordner
PicoBoy - elektronische Wasserwaage
technische Daten wie beim RPi PicoMC RP2040 mit OLED-Display 128x64 Pixel
Anleitung für die Firmware CircuitPython
- USB-A zu USB-C Kabel
Software
- CircuitPython 9.0.0-beta oder neuer
- Bibliotheken im 'lib'-Ordner
Heute geht es um eine echt praktische Anwendung. Mit Hilfe des Pico-Boys im Gehäuse aus dem ersten Beitrag wird eine elektronische Wasserwaage
realisiert, welche jederzeit einsatzbereit ist. Die dabei genutzten Progressbalken zeigen mit ihrem Ausschlag nach links oder rechts an, wenn
der Untergrund nicht in der Waage ist. Durch die relativ kurze Zeit, die das 'Gerät' eingeschaltet sein muss und den integrierten 'Ruhemodus'
, reicht auch die Kapazität der Knopfzelle für einen längeren Zeitraum.
Los gehts
Um die Abweichung aus der Waagerechten zu erfassen, wird der Beschleunigungssensor des Pico-Boys genutzt. Wie man das macht, habe ich in diesem Beitrag auf meiner Seite beschrieben. Grundlage dafür bietet der von mir in CircuitPython angepasste Treiber 'my_stk8ba58.py'. Daraus folgt der im unteren Kasten dargestellt Programmanfang:
1 import time 2 import board 3 import busio 4 import displayio 5 import digitalio 6 import terminalio 7 import fourwire 8 from adafruit_display_text import label 9 from adafruit_display_shapes.roundrect import RoundRect 10 from adafruit_display_shapes.circle import Circle 11 import adafruit_displayio_sh1106 12 import my_stk8ba58 13 from adafruit_progressbar.horizontalprogressbar import ( 14 HorizontalProgressBar, HorizontalFillDirection,) 15 16 #rote LED 17 led_red = digitalio.DigitalInOut(board.GP5) 18 led_red.direction = digitalio.Direction.OUTPUT 19 #grüne LED 20 led_green = digitalio.DigitalInOut(board.GP7) 21 led_green.direction = digitalio.Direction.OUTPUT 22 #JOY_CENTER = GP0 23 center = digitalio.DigitalInOut(board.GP0) 24 center.direction = digitalio.Direction.INPUT 25 center.pull = digitalio.Pull.UP 26 27 # Compatibility with both CircuitPython 8.x.x and 9.x.x. 28 # Remove after 8.x.x is no longer a supported release. 29 try: 30 from fourwire import FourWire 31 except ImportError: 32 from displayio import FourWire 33 34 # built-in a display 35 displayio.release_displays() 36 # Make the displayio SPI bus and the sh1106 display 37 spi = busio.SPI(board.GP18, board.GP19) 38 # 39 # Circuit 8.x.x 40 #display_bus = displayio.FourWire(spi, command=board.GP8, chip_select=board.GP10, reset=board.GP9, baudrate=1000000) 41 #display =adafruit_displayio_sh1106.SH1106(display_bus, width=132, height=64, rotation=0) 42 # 43 # Circuit 9.x.x 44 display_bus = FourWire(spi, command=board.GP8, chip_select=board.GP10, reset=board.GP9, baudrate=1000000) 45 display =adafruit_displayio_sh1106.SH1106(display_bus, width=132, height=64, rotation=0) 46 47 # Make the display context 48 splash = displayio.Group() 49 sleep = displayio.Group() 50 51 # ACC-Sensor initialisieren 52 sensor=my_stk8ba58.STK8BA58() 53
Hinweisen möchte ich hier noch einmal besonders auf die Zeilen 27 bis 46. Wie ich bereits an früherer Stelle erwähnt habe,
gibt es bei der Version CircuitPython 9.x.x gegenüber Version 8.x.x einige Änderungen, die nicht kompatibel sind. Solange Version
9.0.0 noch als beta-Version läuft, werde ich auch die Benutzung von CircuitPython 8.x.x mit berücksichtigen, so dass möglichst
keine Programmabbrüche erfolgen.
In den beiden Programmzeilen 48 und 49 werden zwei (!) Displaybereiche definiert
- splash = displayio.Group() und
- sleep = displayio.Group()
zwischen denen beim Betätigen der Joystick-Taste umgeschaltet wird. Der Befehl
dazu lautet jetzt display.root_group = sleep bzw. display.root_group = splash und ersetzt den
früheren Befehl display.show(xxxxx), der ab Version 9.x.x nicht mehr vorkommt. Was will ich mit den beiden
Anzeigebereichen erreichen? Beim Einschalten des Pico-Boys ist der Bereich 'sleep' zu sehen.
Bei einem Druck auf den Joystick-Button wird in den 'Messmodus' gewechselt bzw. aus dem 'Messmodus' wieder in den 'Ruhemodus' zurückgeschaltet.
Dazu werden vorab Textlabel und Zeichenelemente definiert, wie sie im Kasten in den Zeilen 54 bis 99 dargestellt sind. Im Anschluss werde ich
die Benutzung der 'ProgressBar' erklären:
54 # Make the background bitmap when it sleeps 55 color_bitmap = displayio.Bitmap(128, 64, 1) 56 color_palette = displayio.Palette(1) 57 color_palette[0] = 0x000000 58 bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0) 59 sleep.append(bg_sprite) 60 # create the label, when it sleeps 61 text_label = label.Label(font=terminalio.FONT, text="PicoBoy", scale=2, color=0xffffff, line_spacing=1) 62 text_label.anchor_point = (0, 0) 63 text_label.anchored_position = (30, 0) 64 sleep.append(text_label) 65 text_label = label.Label(font=terminalio.FONT, text="press Joystick\n to start", scale=1, color=0xffffff, line_spacing=1) 66 text_label.anchor_point = (0, 0) 67 text_label.anchored_position = (30, 40) 68 sleep.append(text_label) 69 70 rect1 = RoundRect(1,1,130,63,10,fill=0xffffff, outline=None) 71 splash.append(rect1) 72 rect2 = RoundRect(3,3,126,59,8,fill=0x000000, outline=None) 73 splash.append(rect2) 74 75 # Accelerometer Properties. Normally accelerometer well calibrated 76 # Will give a maximum output of 10 mts / s**2 77 # We create our bar displays 78 left_horizontal_bar = HorizontalProgressBar( 79 (20, 20),(50, 25), min_value=0, max_value=2, 80 direction=HorizontalFillDirection.RIGHT_TO_LEFT, 81 bar_color=0x00cc00, outline_color=0xffffff, fill_color=0x606060) 82 splash.append(left_horizontal_bar) 83 84 right_horizontal_bar = HorizontalProgressBar( 85 (70, 20), (50, 25), min_value=0, max_value=2, 86 direction=HorizontalFillDirection.LEFT_TO_RIGHT, 87 bar_color=0x00cc00, outline_color=0xffffff, fill_color=0x606060) 88 splash.append(right_horizontal_bar) 89 90 91 # create the label 92 text_label = label.Label(font=terminalio.FONT, text=" PicoBoy\n\n\n\nspirit-level", scale=1, color=0xffffff, line_spacing=0.95) 93 text_label.anchor_point = (0, 0) 94 text_label.anchored_position = (35, 4) 95 splash.append(text_label) 96 97 on = 0 98 display.root_group = sleep 99
Die 'ProgressBars' werden unterschieden nach
- HorizontalProgressBar und
- VerticalProgressBar
In unserem Fall verwende ich eine left_horizontal_bar (Zeile 78) und eine
right_horizontal_bar (Zeile 84), um den Ausschlag nach links bzw. rechts anzuzeigen.
Als Parameter werden:
- die Koordinaten der oberen linken Ecke (bzw. rechten Ecke) der Fortschrittsleiste erwartet,
- die Größe (Breite, Höhe) des Fortschrittsbalkens in Pixeln,
- der niedrigste und höchste Wert min_value = xx und max_value = yy und
- weiterhin direction, bar_color, outline_color und fill_color.
Beachte: Nicht der Name left_horizontal_bar bzw. right_horizontal_bar legt die
Richtung des Ausschlags fest, sondern der Parameter direction=HorizontalFillDirection.RIGHT_TO_LEFT bzw.
direction=HorizontalFillDirection.LEFT_TO_RIGHT.
In Zeile 97 wird die Variable 'on=0' gesetzt und der 'Ruhemodus' angezeigt. Nun folgt die 'while-Schleife'.
100 while True: 101 if center.value == False and on == 1: 102 on = 0 103 display.root_group = sleep 104 led_red.value = False 105 led_green.value = False 106 time.sleep(1) 107 if center.value == False and on == 0: 108 on = 1 109 display.root_group = splash 110 time.sleep(1) 111 if on == 1: 112 #read STK8BA58 113 wert_y = sensor.yAcc() * 10 114 #wert_y etwas kleiner kalibrieren laut Versuchen 115 wert_y = wert_y - 0.16 116 if wert_y >= 0 and wert_y < 0.2 or wert_y < 0 and wert_y >= -0.2 : 117 led_green.value=True 118 led_red.value=False 119 # Ausschlag nach links und rechts 120 if wert_y > 0 and wert_y <= 2: 121 if wert_y >= 0.2: 122 led_red.value = True 123 led_green.value = False 124 left_horizontal_bar.value = wert_y 125 right_horizontal_bar.value = 0 126 left_horizontal_bar.bar_color=0x00cc00 127 if wert_y < 0 and wert_y >= -2: 128 if wert_y <= -0.2: 129 led_red.value = True 130 led_green.value = False 131 right_horizontal_bar.value = -wert_y 132 left_horizontal_bar.value = 0 133 right_horizontal_bar.bar_color=0x00cc00 134 time.sleep(0.2)
Erläuterungen:
Zeilen 101 bis 106: Der Messmodus ist an und es wurde der Joystick gedrückt. Darauf wird in den Ruhemodus umgeschaltet und
die rote und grüne LED ausgeschaltet. Die Sekunde 'time.sleep(1) dient dem Entprellen des Tasters.
Zeilen 107 bis 110: Es wird aus dem Ruhemodus in den Messmodus umgeschaltet.
Die Zeilen 110 bis 134 beschreiben die Abarbeitung im Messmodus. Als Sensorwert wird wert_y = sensor.yAcc() * 10
gesetzt. Versuche haben ergeben, dass es genauer war, diesen um 0.16 zu verringern (evtl. ist das Gehäuse nicht exakt gleich hoch
auf beiden Seiten).
Wenn sich nun die Werte zwischen -0.2 und +0.2 befinden (Zeile 116), wird fast kein Ausschlag angezeigt und die
grüne LED ist an (rote LED aus). Damit wird der Zustand 'waagerecht' wiedergegeben. Alle anderen Werte zwischen -2 und +2 zeigen
den entsprechenden Ausschlag nach links bzw. rechts und die rote LED ist an (Zeilen 120 bis 133). D.h. der Untergrund ist nach der
entsprechenden Seite 'schief'.
Fazit:
Das Ziel bei der Messung ist also, dass die rote LED möglichst nicht und die grüne LED durchgehend leuchtet. Dann liegt das
Gehäuse des Pico-Boy in der Waage bzw. der Untergrund ist waagerecht ausgerichtet. Durch einen kurzen Druck auf die Joysticktaste
wird das Gerät in den Ruhemodus geschaltet, um Strom zu sparen und die Lebensdauer der Knopfzelle zu verlängern.
Viel Spass und Erfolg beim Ausprobieren.
Viel Spass und Erfolg beim Ausprobieren.