Datensammler
Unser Datensammelprogramm (Listing 2) nutzt den BME280 über die I2C-Schnittstelle des RasPi. SDA liegt dort auf Pin 3 und SCL auf Pin 5. Das spielt nur für die Verkabelung eine Rolle, das Programm selbst nutzt dafür das plattformspezifische Package board (Zeile 11). Das erleichtert das Portieren auf andere Plattformen. Über board.xxx greifen Sie auf eine ganze Reihe symbolischer Konstanten für diverse Hardware-Features zu. So liefert board.I2C() das Standard-I2C-Device und board.SPI() die SPI-Schnittstelle.
Listing 2
#!/usr/bin/python3
import time
import board
import busio
import digitalio
import adafruit_bme280
import hd44780
# Objekte erstellen
i2c = busio.I2C(board.SCL, board.SDA)
bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c,address=0x76)
display = hd44780.HD44780(i2c,trans_map={"°": 223})
btn = digitalio.DigitalInOut(board.D4)
btn.direction = digitalio.Direction.INPUT
btn.pull = digitalio.Pull.UP
# Umrechnung Luftdruck auf Seehöhe bei gegebener Höhe
seehoehe = 520
fac = pow(1.0-seehoehe/44330.0, 5.255)
# Hauptschleife
bl_on = True
while True:
try:
if bl_on:
line1 = "Temp: %3.1f°C %2d%%" % (bme280.temperature,bme280.humidity)
line2 = "Druck: %4d hPa" % int(bme280.pressure/fac)
display.write(line1,1)
display.write(line2,2)
# Abfrage Button
if not btn.value:
bl_on = not bl_on
display.backlight(bl_on)
time.sleep(0.5)
time.sleep(0.1)
except Exception as ex:
print("ex: %r" % ex)
break
display.clear()
display.backlight(False)
Neben den Datenleitungen benötigt der Sensor 3,3 Volt Spannung und einen Masseanschluss. Das Ansteuern des LC-Displays erfolgt dank des seriellen Adapters über dieselben Datenleitungen – allerdings benötigt das Display 5 Volt Betriebsspannung. Vorsichtige Bastler löten die Pullups des PCF8574T heraus, die den I2C-Bus auf 5 Volt ziehen [3]. Da der RasPi interne Pullups auf SDA und SCL mitbringt, sind diese nicht nötig (Abbildung 2).
Damit sich beide Bausteine gleichzeitig am normalen I2C-Anschluss des RasPi anschließen lassen, kommt ein Breadboard zum Einsatz. Die beiden Komponenten verwenden üblicherweise die Adressen 0x27 und 0x76 auf dem I2C-Bus. Je nach Baustein lauten die Adressen eventuell aber auch anders. Aufschluss darüber schafft das Programm I2cdetect aus den i2c-tools (Abbildung 3).
Die weiteren Zeilen aus Listing 2 zeigen den Ablauf. Zuerst erzeugt das Programm in den Zeilen 11 bis 17 die Python-Objekte für den Sensor, das Display und einen Button. Der Sensor liefert Temperatur, Luftfeuchtigkeit und entweder Luftdruck oder Höhe. Die letzten beiden Parameter hängen voneinander ab: Geben Sie die Höhe vor, gibt der Sensor den Luftdruck aus. Geben Sie hingegen den Luftdruck vor, berechnet das Konstrukt die Höhe. Bei einem stationären System ist die erste Variante sinnvoller.
Nach dem Initialisieren der Komponenten läuft das eigentliche Programm in einer Endlosschleife ab Zeile 25. Die Messung erfolgt automatisch durch den Zugriff auf Attribute des BME280-Objekts. Die Zeilen 30 und 31 formatieren die Ausgabe, damit sie auf das verwendete Display mit zweimal 16 Zeilen passen. An dieser Stelle könnte man die Daten noch in einer CSV-Datei ablegen, um sie später in LibreOffice Calc weiterzuverarbeiten oder in einer Datenbank zu speichern. Wer allerdings auf maximale Kompatibilität Wert legt, verzichtet auf solche Kapriolen, denn das Programm läuft dann nicht mehr ohne Änderung auf einem Mikrocontroller.
Einschränkungen
Das Programm aus Listing 2 verwendet aus didaktischen Gründen einen für den eigentlichen Zweck unnötigen Button. Darüber schalten Sie die Hintergrundbeleuchtung des Displays ein oder aus. Der mit GPIO4 verbundene Taster und das Programm greifen über board.D4 darauf zu. Die Abfrage gestaltet sich über die value-Attribute recht simpel (Zeile 34).
Der Button zeigt allerdings auch die aktuell größte Einschränkung von CircuitPython: Die Laufzeitumgebung unterstützt weder Interrupts noch eine irgendwie anders geartete asynchrone Verarbeitung von Code. Deshalb muss das Anwendungsprogramm den Button ständig abfragen. Je mehr Schalter es zu verwalten gilt, desto höher die Chance, hier einen Knopfdruck zu verpassen. Darüber hinaus bläht diese Vorgehensweise die restliche Anwendungslogik unnötig auf.
Das Beispielprogramm kümmert sich nicht darum und liest den Sensor zehnmal pro Sekunde aus, was aber nicht immer möglich oder sinnvoll ist. Möchten Sie den Sensor in festen Abständen auslesen, müssen Sie den letzten Lesezeitpunkt speichern und regelmäßig abfragen, ob schon genug Zeit verstrichen ist. Da CircuitPython auf dem RasPi kein eigener Interpreter bereitsteht, könnte man auf dieser Plattform die Einschränkung einfach ignorieren und mit Threads arbeiten. Allerdings lassen sich die Programme dann nicht mehr ohne Weiteres portieren.
Eine weitere Einschränkung betrifft die verfügbaren Bibliotheken. Für die SAMD21/SAMD51-MCUs gibt es inzwischen die Bibliothek displayio für die Ausgabe auf (kleinen) Displays. Damit lassen sich einigermaßen bequem Bilder, Texte und Grafiken ausgeben. Die Bibliothek ist aktuell noch nicht für den RasPi portiert. Hier bleibt nur der Rückgriff auf Bibliotheken aus dem normalen Python-Fundus wie etwa Pillow. Auch die UART-Schnittstelle funktioniert beim RasPi etwas anders und nutzt dort das normale Serial-Modul von Python.
Portierungen
Die Portierung von existierenden Bibliotheken auf CircuitPython gestaltet sich vergleichsweise einfach. Der Autor hat sowohl eine MicroPython-Bibliothek für den kleinen MP3-Player-Chip DFPlayer Mini als auch für die im Beispiel verwendeten LC-Displays erstellt. Im letzteren Fall fiel sogar fast die Hälfte der Codezeilen weg, denn die I2C-Abstraktion der CircuitPython-Bibliotheken ersetzte die entsprechenden handgeschriebenen Funktionen.
Anders verhält es sich mit dem Portieren von Blinka auf andere Plattformen. Von Adafruit gibt es eine Anleitung für die Installation auf dem Orange Pi R1, einem noch kompakteren SBC als der Pi Zero. Das auf dem Allwinner-H2-Chip beruhende Modul eignet sich mit vier CPU-Kernen für viele CPU-intensivere Aufgaben sogar besser als der Pi Zero. Der größte Nachteil dieser eher exotischen SBCs war bis jetzt die mangelhafte Unterstützung der diversen Hardware-Addons, die für die RasPi-Familie bereitstehen. Mit Blinka sieht das Bild viel besser aus: Zumindest alle Boards, die Standardschnittstellen wie I2C, SPI und UART nutzen, sind bei einer verfügbaren Blinka-Portierung sofort einsatzbereit.
Eine testweise Portierung auf den SBC Orange Pi One Plus, der den Allwinner-H6 verwendet, stellte sich als aufwendiger heraus als gedacht. Darüber hinaus verkompliziert die fehlende Dokumentation seitens des SBC-Herstellers die Arbeit zusätzlich. Allerdings gibt es auch von Adafruit keinen Portierungsleitfaden, was die Arbeit dort erschwert.
Fazit
Ein wirkliches plattformübergreifendes Programmieren funktioniert auch mit CircuitPython noch nicht perfekt. Das liegt zum einen an der fehlenden Portierung einzelner Bibliotheken, zum anderen daran, dass jede Plattform im Detail anders funktioniert. Der Portierungsaufwand sinkt aber auf jeden Fall. Für das Beispiel aus Listing 2 beschränkt sich der Änderungsbedarf auf die Definition des Buttons, und schon läuft das Programm auch auf dem Trinket-M0.
Der größte Vorteil von CircuitPython für den RasPi ist der einfache Zugang zu vielen Hardware-Komponenten, unabhängig davon, ob man auch das einfachere Programmiermodell übernimmt oder nicht. Auch das Prototyping für MCUs gestaltet sich auf dem RasPi oft einfacher als direkt auf der entsprechenden Hardware. Ebenfalls deutlich einfacher funktioniert das Portieren vorhandener Python-Bibliotheken, da CircuitPython eine stabile API für den Zugriff auf die klassischen Bussysteme wie I2C und SPI bereitstellt.
Entscheidend ist aber der Spaß am Programmieren – und hier sorgt CircuitPython dafür, dass die Hürde für den Einstieg auf einer wachsenden Zahl von Plattformen niedrig liegt.







