PicoBoy - elektronische Wasserwaage

technische Daten wie beim RPi Pico
MC RP2040 mit OLED-Display 128x64 Pixel

Anleitung für die Firmware CircuitPython 9.x.x



Hardware

- Pico-Boy (z.B. von hier)
- Gehäuse vom 3D-Drucker
- USB-A zu USB-C Kabel

komplette Firm- und Software als uf2-Datei:

- picoboy_spiritlevel.uf2

Software zum schrittweisen Ausprobieren

- CircuitPython 9.x.x 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   from fourwire 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  #gruene 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  # built-in a display
28  displayio.release_displays()
29  # Make the displayio SPI bus and the sh1106 display
30  spi = busio.SPI(board.GP18, board.GP19)
31  display_bus = FourWire(spi, command=board.GP8, chip_select=board.GP10, reset=board.GP9, baudrate=1000000)
32  display =adafruit_displayio_sh1106.SH1106(display_bus, width=132, height=64, rotation=0)
33
34  # Make the display context
35  splash = displayio.Group()
36  sleep = displayio.Group()
37
38  # ACC-Sensor initialisieren
39  sensor=my_stk8ba58.STK8BA58()
40
  

In den beiden Programmzeilen 35 und 36 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:

  
  

41 # Make the background bitmap when it sleeps
42 color_bitmap = displayio.Bitmap(128, 64, 1)
43 color_palette = displayio.Palette(1)
44 color_palette[0] = 0x000000
45 bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0)
46 sleep.append(bg_sprite)
47 # create the label, when it sleeps
48 text_label = label.Label(font=terminalio.FONT, text="PicoBoy", scale=2, color=0xffffff, line_spacing=1)
49 text_label.anchor_point = (0, 0)
50 text_label.anchored_position = (30, 0)
51 sleep.append(text_label)
52 text_label = label.Label(font=terminalio.FONT, text="press Joystick\n    to start", scale=1, color=0xffffff, line_spacing=1)
53 text_label.anchor_point = (0, 0)
54 text_label.anchored_position = (30, 40)
55 sleep.append(text_label)
56
57 rect1 = RoundRect(1,1,130,63,10,fill=0xffffff, outline=None)
58 splash.append(rect1)
59 rect2 = RoundRect(3,3,126,59,8,fill=0x000000, outline=None)
60 splash.append(rect2)
61
62 # Accelerometer Properties. Normally accelerometer well calibrated
63 # Will give a maximum output of 10 mts / s**2
64 # We create our bar displays
65 left_horizontal_bar = HorizontalProgressBar(
66     (20, 20),(50, 25), min_value=0, max_value=2,
67     direction=HorizontalFillDirection.RIGHT_TO_LEFT,
68     bar_color=0x00cc00, outline_color=0xffffff, fill_color=0x606060)
69 splash.append(left_horizontal_bar)
70
71 right_horizontal_bar = HorizontalProgressBar(
72     (70, 20), (50, 25), min_value=0, max_value=2,
73     direction=HorizontalFillDirection.LEFT_TO_RIGHT,
74     bar_color=0x00cc00, outline_color=0xffffff, fill_color=0x606060)
75 splash.append(right_horizontal_bar)
76
77
78 # create the label
79 text_label = label.Label(font=terminalio.FONT, text="  PicoBoy\n\n\n\nspirit-level", scale=1, color=0xffffff, line_spacing=0.95)
80 text_label.anchor_point = (0, 0)
81 text_label.anchored_position = (35, 4)
82 splash.append(text_label)
83
84 on = 0
85 display.root_group = sleep
86
  

Die 'ProgressBars' werden unterschieden nach

- HorizontalProgressBar und
- VerticalProgressBar

In unserem Fall verwende ich eine left_horizontal_bar (Zeile 65) und eine right_horizontal_bar (Zeile 71), 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 84 wird die Variable 'on=0' gesetzt und der 'Ruhemodus' angezeigt. Nun folgt die 'while-Schleife'.

  
  

87  while True:
88      if center.value == False and on == 1:
89          on = 0
90          display.root_group = sleep
91          led_red.value = False
92          led_green.value = False
93          time.sleep(1)
94      if center.value == False and on == 0:
95          on = 1
96          display.root_group = splash
97          time.sleep(1)
98      if on == 1:
99          #read STK8BA58
100         wert_y = sensor.yAcc() * 10
101         #wert_y etwas kleiner kalibrieren laut Versuchen
102         wert_y = wert_y - 0.16
103         if wert_y >= 0 and wert_y < 0.2 or wert_y < 0 and wert_y >= -0.2 :
104             led_green.value=True
105             led_red.value=False
106         # Ausschlag nach links und rechts
107         if wert_y > 0 and wert_y <= 2:
108             if wert_y >= 0.2:
109                 led_red.value = True
110                 led_green.value = False
111             left_horizontal_bar.value = wert_y
112             right_horizontal_bar.value = 0
113             left_horizontal_bar.bar_color=0x00cc00
114         if wert_y < 0 and wert_y >= -2:
115             if wert_y <= -0.2:
116                 led_red.value = True
117                 led_green.value = False
118             right_horizontal_bar.value = -wert_y
119             left_horizontal_bar.value = 0
120             right_horizontal_bar.bar_color=0x00cc00
121         time.sleep(0.2)
  

Erläuterungen:
Zeilen 88 bis 93: 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 94 bis 97: Es wird aus dem Ruhemodus in den Messmodus umgeschaltet.

Die Zeilen 98 bis 121 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 103), 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 107 bis 120). 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.