Gutes Wetter mit Raspberry Pi Pico, MicroPython und Visual Studio Code

Der Pragmatische Architekt Michael Stal  –  15 Kommentare

Es geht weiter mit der Reise durch das Pico-Land. Während in der letzten Folge die Python-IDE Thonny zur Sprache kam, konzentrieren sich die jetzige und die kommende Folge auf Visual Studio Code für die Pico-Entwicklung unter Python und C beziehungsweise C++.

Die Thonny-IDE eignet sich sowohl für den Einstieg in die Programmiersprache Python beziehungsweise MicroPython als auch für Experimente mit dem Raspberry Pi Pico. Deshalb war meine Andeutung in der vergangenen Folge etwas despektierlich, Thonny könnte bei (semi-)professionellen Entwicklern keinen Blumentopf gewinnen. Immerhin stellt es eine ideale Spielwiese zur Verfügung.

Für größere Projekten mit komplexerem Code und mehreren Beteiligten skaliert eine einfache IDE allerdings nicht. Dort benötigen Entwickler beispielsweise Werkzeuge für Sourcecode-Verwaltung, ausgereiftere Testmöglichkeiten und leistungsfähiges Management von Ressourcen wie Bibliotheken.

Benötigte Software-Ingredienzen

Doch woher nehmen und nicht stehlen? Vier Dinge brauchen anspruchsvollere Python-Entwickler, um auf einem Linux-, macOS- oder Windows-Host Anwendungen für den Raspberry Pi Pico zu entwickeln, wobei ausschließlich 64-Bit-Betriebssysteme infrage kommen:

  • das SDK von Python in einer aktuellen Version,
  • Visual Studio Code (VS Code) als modulare Programmierumgebung (siehe diesen Blogartikel),
  • Node.js und
  • für VS Code eine Erweiterung (engl. Extension) namens Pico-Go. Diese ist eine Ableitung aus der PyMakr-Erweiterung von Pycom.

Übrigens: Besitzer eines Computers mit Apple Silicon dürfen sich darüber freuen, dass alle Werkzeuge auch unter einem M1-basierten System zur Verfügung stehen.

Weitere gute Nachricht: Die genannten Anwendungen sind kostenlos verfügbar.

Pico-Go

Für die Installation der Combo liefert der Entwickler von Pico-Go, Chris Wood, bereits eine ausführliche englischsprachige Dokumentation auf seiner Webseite. Deshalb an dieser Stelle nur eine grobe Zusammenfassung der entsprechenden Referenzen:

Sind alle aufgezeigten Pakete betriebsbereit, lässt sich in VS Code die Erweiterung Pico-Go von Chris Wood installieren.

Die Extension Pico-Go hilft bei der MicroPython-Entwicklung für den Raspberry Pi Pico unter Visual Studio Code

Der Spaß beginnt

Zunächst empfiehlt es sich, einen Ordner für Python-Projekte zu kreieren, diesen unter VS Code zu öffnen, mit Ctrl + Shift + p (Windows, Linux) beziehungsweise Command + Shift + p (Mac) die Kommandopalette zu laden, um das Kommando Pico-Go | Configure project via Palette auszuführen. Dadurch sind unter anderem Code-Vervollständigung und Syntaxprüfung (Lint) nutzbar.

Sobald Nutzer einen Raspberry Pi Pico an das Hostsystem anschließen, erkennt ihn Pico-Go und teilt dies über die untere Statusleiste der IDE mit.

Jetzt können Entwickler unter VS Code eine Programmdatei (Endung .py) anlegen, um dort MicroPython-Quellcode einzufügen. Das fertige Programm lässt sich über das Run-Kommando (untere VS-Code-Statusleiste!) auf dem Pico ausführen, oder mittels Upload auf das Board kopieren, worauf der Pico einen Reset durchführt und danach das Programm automatisch startet. Wichtig: Vor dem Upload einer Programmdatei sollten Entwickler sie in main.py umtaufen, weil der Pico andernfalls nur das Programm ablegt, ohne es weiter zu beachten.

Das Beispielprojekt "Heise Wetter"

Nach erfolgreicher Installation und Konfiguration von Pico-Go ist es an der Zeit, sich dem eigentlichen Thema zu widmen, einem Embedded-Projekt für den Pico. Entstehen soll mithilfe des Sensors BME280 eine kleine Wetterstation, die periodisch Temperatur, Luftfeuchtigkeit und Luftdruck misst.

Der Wettersensor BME280 bildet das Herz der Schaltung

Grundsätzliche Idee: Über den Pico erfolgt der Zugriff auf den BME280-Sensor, der für die Erfassung der Messwerte für Temperatur, Luftfeuchtigkeit und Luftdruck zuständig ist. Die entsprechenden Werte stellt die Wetterstation auf dem SSD1306-Display dar. Zugleich gibt es eine kodierte Regel über das augenblickliche "Klima". Liegen die Werte im angenehmen Bereich, aktiviert das Programm die grüne LED. Sind Feuchtigkeit und Temperatur jenseits der eigenen Komfortzone, leuchtet die rote LED. Liegen die Werte irgendwo dazwischen, teilt die Anwendung dies über die gelbe LED mit. Diese Regel ist als eigene Funktion kodiert und lässt sich dementsprechend anpassen. Auch die Zeitdauer zwischen den Abfragen (SLEEPTIME ist konfigurierbar).

Das günstige OLE-Display mit 128 x 64 Pixeln Auflösung reicht für den vorgesehenen Zweck

Übrigens können durch Auflegen einer Fingerspitze auf den BME280 Temperatur und Feuchtigkeit stark nach oben steigen. Insofern lässt sich die Klimaregel manuell testen.

Hardware BoM (Bill of Material)

Für das Projekt benötigen wir folgende Stückliste:

  • Raspberry Pi Pico: ca. 5 Euro
  • Zwei Reihen 20er-Header-Pins: ca. 0,50 Euro
  • Breadboard für 3,50 Euro
  • Kabel zum Verbinden des Pico Micro-USB mit der USB-Schnittstelle des Hosts: ca. 3 Euro
  • Je eine rote, gelbe, grüne LED: ca. 0,30 Euro
  • 3 Widerstände (z. B. 120 Ohm, 150 Ohm oder 220 Ohm): ca. 0,10 Euro
  • Bosch Sensortec BME280 Breakout-Board mit I2C-Bus: ca. 6 Euro
  • SSD1306-OLED-Display mit 128x64 Pixeln und I2C-Bus: ca. 6 Euro
  • 14 Verbindungen Jumper-Wire männlich auf männlich: ca. 5 Euro im 120er-Pack

Gesamt: ca. 29 Euro

Wer bei den üblichen Verdächtigen in China zuschlägt, kann die Anschaffungssumme noch weiter reduzieren.

Die Schaltung

Auf dem mit Fritzing gezeichneten Schaltungsdiagramm sind rechts unten das OLED-Display SSD1306 mit 128x68 Punkten Auflösung abgebildet und rechts oben der Umweltsensor BME280. Beide sind mit dem I2C-Bus des Pico verbunden, SDA liegt dort auf Pin 6 (= GPIO 4) und SCL auf Pin 7 (= GPIO 5). GPIO steht für General Purpose IO.

Die Schaltung der Wetterstation als Fritzing-Diagramm

Achtung: Die physikalischen Pins haben keinen direkten Bezug zu ihrer logischen Bezeichnung. Der physikalische Pin 6 entspricht zum Beispiel dem logischen Pin GPIO 4, der physikalische Pin 25 dem logischen Pin GPIO 19. In Anwendungen für den Pico oder einem anderen Raspberry Pi Board sind in der Regel die logischen Namen gemeint.

  • Die Spannungsversorgung der Komponenten durch den Pico erfolgt mit 3,3 V über dessen Vcc-Ausgang (Pin 36) und einem der verschiedenen GND-Ports (im Diagramm: Pin 38).
  • Für den Anschluss der LEDs über jeweils einen 150-Ohm-Widerstand sind Pin 25 (rot), Pin 26 (gelb) und Pin 27 (grün) vorgesehen. Das entspricht den GPIO-Ports 19, 20 und 21.
Das Pin-Layout des Pico. Achtung: Logische Pins sind ungleich physikalischen Pins

Damit ist schon alles Wesentliche über die Schaltung gesagt.

Die Programmierung

Kommen wir zur Software. Im ersten Schritt ist ein Treiber für das OLED-Display SSD1306 notwendig. Den hat bereits Stefan Lehmann vom Kunststoff-Zentrum Leipzig implementiert. Der Treiber lässt sich über seine Github-Seite importieren.

Für die Ansteuerung des BME280 von Bosch habe ich einige Beispielimplementierungen unter die Lupe genommen und als Vorlage für einen eigenen Treiber kodiert. Bosch Sensortec stellt zu diesem Zweck ein hilfreiches Dokument über den Sensor auf seiner Webseite zur Verfügung.

Der Sensor misst neben Temperatur (in Grad Celsius) und Feuchtigkeit (in Prozent) auch den Luftdruck (in mbar beziehungsweise HectoPascal). Wer auf die Messung der Luftfeuchtigkeit verzichten kann, sollte auf die billigere Variante BMP280 zugreifen. Dieser Sensor lässt sich teilweise bereits für unter einem Euro im Internet erwerben.

Damit niemand mühsam das Beispielprogramm abtippen oder mit Copy&Paste zusammenschneiden muss, habe ich den Code auf einer Github-Seite bereitgestellt. Hier der Link für das GitHub-Repository.

Im Folgenden kommen hauptsächlich einige wichtige Fragmente zur Sprache, die dem Verständnis dienen.

Für die Implementierung des Programmes sind einige Bibliotheken notwendig, die meisten davon aus den Pico- beziehungsweise Micropython-SDKs. Die Bibliothek für die Ansteuerung des OLED-Displays ist, wie bereits erwähnt, auf GitHub zu finden.

from machine import Pin, I2C    # Wir brauchen Pin und I2C des Pico
from micropython import const
from ustruct import unpack as unp
from ssd1306 import SSD1306_I2C # Modul für SSD1306
import utime # Zwei Zeit-Bibliotheken
import time

Für die Entscheidung, ob das Wetter gut, schlecht, mittel ist, fungiert die Funktion condition(). Diese enthält eine eigene Regel, die jeder für sich selbst ändern kann:

#--Condition ----------------------
COND_RED = 1 # Schlechtes Klima
COND_GREEN = 2 # Angenehmes Klima
COND_YELLOW = 3 # Mittleres Klima
#----------------------------------

ComfortZoneTemp = (15,25) # Meine Komfortzone für Temperatur liegt zwischen 15 und 25 Grad
ComfortZoneHumi = (10,40) # Meine Komfortzone für Feuchtigkeit liegt zwischen 10 und 40%
#
def condition(temperature, humidity, pressure):
niceTemperature = temperature >= ComfortZoneTemp[0] and temperature <= ComfortZoneTemp[1]
niceHumidity = humidity >= ComfortZoneHumi[0] and humidity <= ComfortZoneHumi[1]
if niceHumidity and niceTemperature:
return COND_GREEN
elif (niceHumidity != niceTemperature): # XOR
return COND_YELLOW
else:
return COND_RED
Die Anwendung initialisiert den I2C-Bus zum Zugriff auf BME280 und SSD1306:#

Die Anwendung initialisiert den I2C-Bus zum Zugriff auf BME280 und SSD1306:

sda = Pin(4) # BME280 und SSD1306 sind an GPIO 4 und 5 angeschlossen
scl = Pin(5)
i2c = I2C(0,sda=sda,scl=scl,freq=400000) # I2C-Bus 0
i2c_addr_bme = 0x76 # Ich gehe davon aus, der BME280 liegt an 0x76

Die LEDs befinden sich an den GPIO-Ports 19, 20, 21:

GreenLED  = Pin(21, Pin.OUT) # Grüne LED an GPIO 21
YellowLED = Pin(20, Pin.OUT) # Gelbe LED an GPIO 20
RedLED = Pin(19, Pin.OUT) # Rote LED an GPIO 19

Die Variable SLEEPTIME legt die Zeit zwischen zwei Messungen fest:

SLEEPTIME = 5

Hinter der Klasse BMX280 verbirgt sich der Treiber für den Umweltsensor. Deren Konstruktor nimmt etliche Initialisierungen vor. Interessant für Anwender ist hauptsächlich die Methode measure(), weil sie Druck, Feuchtigkeit und Temperatur zurückliefert.

class BMX280:
# Im Konstruktor werden primäre Datenmember und Konstanten belegt
def __init__(self, i2c, i2c_addr_bme)
def measure(self)
# ... diverse Hilfsmethoden ...

Das Hauptprogramm der Software sucht zunächst nach Sensoren und Aktoren am I2C-Bus – optional, weil abhängig von der Belegung der boole’schen Variable debug.

if debug:
print('Ich habe an folgenden Adressen Komponenten am I2C-Bus entdeckt:')
devices = i2c.scan()
if devices:
for i in devices:
print(hex(i))
utime.sleep_ms(2000)

Gilt debug == True, erfolgt an weiteren Stellen die Bildschirmausgabe am Terminal der IDE.

Anschließend initialisiert die Anwendung die Treiber für SSD1306 (Klasse SSD1306_I2C) und BME280 beziehungsweise BMP280 (Klasse BMX280).

In der Hauptschleife liest der Code über measure() die Wetterwerte ein, aktiviert abhängig von den Messwerten entweder die rote, grüne oder gelbe LED, und stellt die Werte am OLED-Display dar. Nach einer Wartezeit folgt der Übergang zur nächsten Messrunde:

oled = SSD1306_I2C(128,64,i2c)
bme = BMX280(i2c = i2c, i2c_addr_bme = i2c_addr_bme)

oled.fill(0)
oled.show()

# HAUPTSCHLEIFE #
while True:
temperature, humidity, pressure = bme.measure()
#......................
currentState = condition(temperature, humidity, pressure)
if currentState == COND_GREEN:
GreenLED.value(1)
YellowLED.value(0)
RedLED.value(0)
if debug:
print("Angenehmes Klima")
elif currentState == COND_YELLOW:
GreenLED.value(0)
YellowLED.value(1)
RedLED.value(0)
if debug:
print("Geht so")
elif currentState == COND_RED:
GreenLED.value(0)
YellowLED.value(0)
RedLED.value(1)
if debug:
print("Unangenehmes Klima")
# ......................
oled.fill(0)
oled.text("Heise Wetter", 5, 10)
# Formatierte Ausgabe mit 7 Ziffern bei 2 Nachkommastellen
oled.text(str('% 7.2f' % temperature) + " Grad", 5,20)
oled.text(str('% 7.2f' % humidity) + " %",5,30)
oled.text(str('% 7.2f' % pressure) + " HPa",5,40)
# Und jetzt enthuellen
oled.show()
utime.sleep(SLEEPTIME) # Schlafen bis zur nächsten Messung

Der fertige und funktionierende Versuchsaufbau sieht folgendermaßen aus:

Das fertige Projekt in einem prototypischen Aufbau

Fazit

Das Beispielprojekt hat erste Einblicke in die MicroPython-Programmierung des Pico unter Visual Studio Code gegeben. Ein Problem, das sich momentan noch zeigt, sind fehlende Module für die Ansteuerung einiger Bauteile, insbesondere von Breakout-Boards und komplexeren Sensoren. Da der Pico noch sehr neu ist, dürfte sich dies in den nächsten Monaten ändern und dadurch ein ähnlich breites Ökosystem entstehen, wie es bei Arduino, Espressif (ESP32, ESP8266), Raspberry Pi Single-Board-Computer, Adafruit, Sparkfun und ST Microelectronics bereits der Fall ist.

Natürlich gibt es für das gezeigte Beispiel diverse Optimierungsmöglichkeiten, etwa:

  • Das Programm kümmert sich nicht um Energieeffizienz. Wer eine Wetterstation unabhängig vom Stromnetz betreiben möchte, könnte zum Beispiel die verschiedenen Schlafmöglichkeiten des Pico und der verwendeten Bauteile nutzen.
  • Die Schaltung ist weiter ausbaubar, etwa durch Hinzunahme von Sensoren für Gewittererkennung, Regen, Bodenfeuchtigkeit, Luftqualität, Feinstaub und CO2-Belastung.
  • Für Wettermessungen gibt es neben den BME280 viele Alternativen, die sich einsetzen lassen, zum Beispiel der DHT22.
  • Manche Breakout-Boards für BME280 oder SSD1306 benutzen statt des I2C-Bus den SPI-Bus, der eine andere Verdrahtung erfordert.
  • Es wären auch andere Anzeigen als der monochrome SSD1306 denkbar, etwa Displays mit höherer Auflösung und/oder mehreren Farben, das Vorhandensein oder die Eigenentwicklung entsprechender Treibersoftware vorausgesetzt.
  • Anstelle des BME280 ließe sich auch der neuere Sensor BME680 nutzen, was sich aber preislich auswirkt. Die Kosten belaufen sich in diesem Fall auf um die 20 Euro. Außerdem habe ich dafür noch keinen Treiber entdeckt. Vorteil des BME680 wäre die Berechnung der Luftqualität.

Interessierte Leser haben also noch genug Möglichkeiten, sich weiter auszutoben.

Der Fokus dieses Artikels lag auf MicroPython. Übrigens existiert auch CircuitPython (entwickelt von Adafruit) als Alternative, aber davon soll nicht weiter die Rede sein, da die konzeptionellen Unterschiede eher marginal sind. In der nächsten Folge geht es um die Programmierung mit C beziehungsweise C++.

Bis dahin viel Spaß beim Picomentieren.