MicroPython für den ESP32 und Co. - Teil 1: Installation und erste Programme
über
Von Dr. Günter Spanner (Deutschland)
Python hat in den letzten Jahren einen enormen Aufschwung erlebt. Nicht zuletzt haben verschiedene Einplatinensysteme wie der Raspberry Pi zu dessen Bekanntheitsgrad beigetragen.
Aber auch in anderen Gebieten wie der künstlichen Intelligenz oder dem Machine Learning hat Python weite Verbreitung gefunden. Es ist daher naheliegend, Python bzw. die Variante MicroPython auch für den Einsatz auf Mikrocontrollern zu verwenden.
In diesem Artikel soll auf die Grundlagen von MicroPython eingegangen werden, unter anderem auf die wichtigsten Programmbefehle und Bibliotheken. Für ein paar kleine Demo-Anwendungen verwenden wir einen ESP32-Controller, den wir mit einer Firmware ausstatten müssen, die Python-Befehle interpretiert. Diese Befehle stellen wir mit einer Entwicklungsumgebung zusammen, die auf einem PC läuft. Die Python-Befehle können einzeln oder als ganzes Programm vom PC zum Controller geschickt werden - das funktioniert über USB, aber auch ein WLAN-Netzwerk. Doch der Reihe nach!
Programmier- und Entwicklungsumgebungen
Im Gegensatz zum Arduino-System stehen für die Arbeit mit MicroPython mehrere Entwicklungsumgebungen (IDEs: Integrated Developing Environment) zur Verfügung. Die zwei am weitesten verbreiteten Programmierumgebungen sind aktuell
1. µPyCraft
2. Thonny
Darüber hinaus kann auch der vor allem im Bereich der Künstlichen Intelligenz eingesetzte Anaconda Navigator für die Programmierung von Controllern eingesetzt werden. Jede Methode hat naturgemäß spezifische Vor- und Nachteile. So bietet die µPyCraft-IDE nur eine vergleichsweise einfache Oberfläche. Sie arbeitet mit simplen Grafikelementen und erinnert an textorientierte Betriebssysteme.
Thonny dagegen verfügt über eine vollständig grafische Oberfläche im Windows-Stil (Bild 1).
Die IDE ist bei Makern sehr beliebt, insbesondere auch deshalb, weil sie unter dem Raspbian-Betriebssystem auf dem Raspberry Pi verfügbar ist. Viele RasPi-Anwender sind daher bereits bestens mit Thonny vertraut. Thonny steht für die wichtigsten Betriebssysteme wie Windows, Mac OS X oder Linux Ubuntu kostenlos im Internet zur Verfügung.
Um mit Thonny arbeiten zu können, muss Python 3 auf dem Entwicklungsrechner installiert sein. Falls dies noch nicht der Fall ist, kann die Installation über die entsprechende Webseite nachgeholt werden.
Danach kann Thonny selbst über [1] installiert werden. Hierzu wird auf der Webseite oben rechts das passende Betriebssystem ausgewählt. Nach dem Starten der heruntergeladenen exe-Datei läuft der übliche Installationsprozess ab. Anschließend steht die IDE für erste Python-Anwendungen zur Verfügung.
Installation des Interpreters
Als Nächstes wird die aktuelle MicroPython-Firmware geladen. Diese findet sich auf der offiziellen MicroPython-Webseite [3]. Hier stehen eine Reihe von Controller-Optionen zur Verfügung. In diesem Artikel soll insbesondere auf den ESP32 eingegangen werden. Deshalb muss die aktuellste (latest) Version für diesen Controllertyp heruntergeladen werden. Dazu klickt man auf der Seite unter Espressif ESP-based boards auf das passende Bild mit der Unterschrift Generic ESP32 module. Auf der sich nun öffnenden Seite stehen mehrere Firmware-Varianten zur Verfügung. Dabei ist zu beachten, dass die jeweils aktuellste Version meist mit dem Zusatz „unstable" versehen ist. Diese Option ist eher für Entwickler der Interpreter-Firmware selbst oder experimentierfreudige Naturen geeignet. Möchte man mit einem stabilen System arbeiten, sollte man die erste Variante ohne den unstable-Zusatz wählen, z. B.
GENERIC : esp32-idf3-20200902-v1.13.bin
Mit einem Klick auf den entsprechenden Link wird die Firmware heruntergeladen.
Nun kann das Controller-Board (zum Beispiel ein ESP32-Pico-Kit, siehe Kasten Required Parts) via USB mit dem PC verbunden werden.
Anschließend wird die Thonny IDE gestartet. Hier muss als Erstes im Menü Run der Unterpunkt Select interpreter aufgerufen werden. Daraufhin öffnet sich das Fenster Thonny options (Bild 2).
Auf der Registerkarte Interpreter können mehrere Optionen u.a. auch für den ESP32 aufgerufen werden.
Anschließend ist unter „Port“ der USB-Port auszuwählen, an welchem der ESP32 aktiv ist. Alternativ kann man die Option < Try to detect port automatically > selektieren. Diese arbeitet jedoch nicht in allen Fällen zuverlässig.
Unter dem Eintrag Firmware wird nun der Installer gestartet. Der Port wird nicht automatisch übernommen und muss nochmals eingetragen werden. Danach ist die oben heruntergeladene Firmware auszuwählen. Mit Install startet der Installationsprozess. Nach dem Schließen der noch offenen Fenster steht der Programmierung des ESP32 unter MicroPython nichts mehr im Weg.
Bibliotheken
Mit Python zu arbeiten heißt, Bibliotheken zu nutzen. Alle Programme von Grund auf neu zu schreiben wäre im besten Fall höchst ineffizient. Der überragende Erfolg von Python beruht zu einem wesentlichen Teil auf den dazu verfügbaren Bibliotheken. Leider lassen sich nicht alle Libraries mit den eingeschränkten Ressourcen eines Mikrocontrollers verwenden. Daher wurde für MicroPython eine eigene Auswahl an Libraries entwickelt. Viele davon stehen als Standard-Bibliotheken bereits mit dem Download der Thonny-IDE zur Verfügung.
Die beiden wichtigsten Standard-Libs in MicroPython sind machine und time. Über die import-Anweisung werden die Libs für den Controller verfügbar. Um die Bibliotheksfunktionen zugänglich zu machen, stehen unter anderem die folgenden Optionen zur Verfügung:
- import module
- from module import name
Im ersten Fall wird das komplette Modul eingebunden. Im zweiten nur die jeweils benötigten Routinen.
Das machine-Modul enthält die Funktionen mit Bezug auf die Hardware eines bestimmten Controllers. Die Funktionen in diesem Modul ermöglichen also den direkten und uneingeschränkten Zugriff auf die Steuerung von Hardwareeinheiten wie CPU, Timer, Busse, I/O-Pins usw. Es ist zu beachten, dass bei falscher Verwendung dieses Moduls Fehlfunktionen, Abstürze und in extremen Fällen sogar Hardwareschäden auftreten können.
Die Klasse Pin ist eine der wichtigsten Funktionen im machine-Modul. Ein Pin-Objekt wird zur Steuerung von Eingabe/Ausgabe-Pins verwendet. Pin-Objekte sind üblicherweise einem physikalischen Pin des Controllers zugeordnet. Damit lassen sich also Ausgangsspannungen steuern oder Eingangspegel einlesen.
Die Pin-Klasse verfügt über Methoden zum Einstellen des Pin-Modes, wie IN und OUT, mit welchen ein Pin als Ein- oder Ausgang definiert werden kann.
Das time-Modul bietet verschiedene zeitbezogene Funktionen. Die Klasse sleep aus diesem Modul unterbricht die Ausführung des aktuellen Programms für die angegebene Anzahl von Sekunden. Das Argument kann auch eine Gleitkommazahl sein, um eine exakte Ruhezeit bis auf Sekundenbruchteile anzugeben. Die Anweisungen
from machine import Pin
from time import sleep
stellen die beiden Funktionen Pin und sleep zur Verfügung. Damit lassen sich zum einen die einzelnen Port-Pins eines Controllers ansprechen, zum anderen wird eine einfache Zeitsteuerung möglich. Über die Anweisung
led = Pin(2, Pin.OUT)
wird ein Objekt led erzeugt, das dem I/O-Pin Nr. 2 zugeordnet ist und diesen als Ausgabe-Pin definiert. Diesem Objekt können nun verschiedene Werte zugeordnet werden. So wird dem Objekt über
led.value(1)
beispielsweise der Wert 1 zugeordnet. Dies bedeutet, dass der zugehörige Pin 2 nun High-Potential führt, im Falle des ESP32 also 3,3 V.
Die REPL-Konsole
Um den Zustand eines Ports sichtbar zu machen, kann man dort eine LED mit Vorwiderstand anschließen. Bild 3 zeigt darüber hinaus auch bereits den Anschluss eines OLED-Displays, das erst weiter unten zum Einsatz kommt.
Der Port und damit die LED können nun ganz einfach über die sogenannte REPL-Konsole gesteuert werden. REPL steht für „Read Evaluate Print Loop“. Über diese interaktive MicroPython-Eingabeaufforderung kann direkt auf den ESP32 zugegriffen werden. Die Verwendung von REPL stellt somit eine sehr einfache Methode dar, um Anweisungen zu testen oder Befehle auszuführen.
In der Thonny-IDE wird die REPL-Konsole als „Shell“ bezeichnet und steht im unteren rechten Fenster zur Verfügung. Hier können die Anweisungen aus dem letzten Abschnitt direkt eingegeben werden (Bild 4).
Nach der Ausführung der letzten Anweisung sollte die LED aufleuchten. Mit led.value(0) kann die LED wieder ausgeschaltet werden.
Die REPL-Konsole verfügt über einige interessante Features, die bei der Arbeit mit MicroPython sehr nützlich sein können. So werden etwa zuvor eingegebene Textzeilen gespeichert. Mit den Aufwärts- und Abwärtspfeiltasten können bereits früher eingegebene Zeilen bei Bedarf wieder aufgerufen werden.
Ein weiteres hilfreiches Feature ist die Tab-Ergänzung. Durch Drücken der Tabulatortaste wird das aktuell eingegebene Wort automatisch vervollständigt. Dies kann auch genutzt werden, um Informationen über Funktionen und Methoden eines Moduls oder Objekts zu erhalten.
Nach der Eingabe von „ma” und Drücken der Tabulatortaste erfolgt z. B. die Ergänzung zu „machine”, vorausgesetzt das machine-Modul wurde, wie oben gezeigt, vorher importiert. Wird danach der Punkt (.) eingegeben und die Tabulatortaste erneut gedrückt, erscheint eine vollständige Liste aller im Maschinenmodul verfügbaren Funktionen (Bild 5).
Damit wird ein Nachschlagen von Anweisungen, Objekten oder Methoden in einer Dokumentation in den meisten Fällen überflüssig.
WLAN-Zugriff über WebREPL
Einer der großen Vorteile des ESP32 ist seine hervorragende WLAN-Konnektivität. Was liegt also näher, als die REPL-Konsole drahtlos über WLAN zur Verfügung zu stellen. Hierfür kann das WebREPL-Interface genutzt werden.
Der erste Schritt zur Verwendung von WebREPL besteht darin, sicherzustellen, dass es auf dem ESP32 verfügbar und aktiviert ist. WebREPL ist standardmäßig nicht aktiviert und muss mit einem einmaligen Befehl
import webrepl_setup
über die serielle Schnittstelle eingeschaltet werden. Es folgt eine Aufforderung, die Funktion zu aktivieren oder zu deaktivieren und ein Kennwort festzulegen. Anschließend muss ein Reboot durchgeführt werden.
Um WebREPL in einem WLAN-Netzwerk zu verwenden, ist der ESP32 zunächst mit dem Netzwerk zu verbinden. Dazu sind in der seriellen REPL die folgenden Befehle auszuführen:
import network
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect('ssid', 'password')
Für die Platzhalter ssid und password sind natürlich die korrekten Zugangsdaten des vorhandenen WLANs einzusetzen. Die Anweisung
wlan.ifconfig()
liefert dann die IP-Daten mit welchen der ESP im Netz angemeldet wurde (Bild 6).
Über
import webrepl
webrepl.start()
wird der WebREPL-Client aktiviert.
Danach kann in einem Browser die Adresse
http://micropython.org/webrepl/#xxx.xxx.xxx.xxx:8266
aufgerufen werden. Für xxx.xxx.xxx.xxx ist die über wlan.ifconfig() ermittelte IP einzusetzen. Danach kann man sich über den Reiter Connect mit dem oben im WebREPL-Setup festgelegten Kennwort anmelden.
Nach dem Start von WebREPL befindet sich die Konsole eventuell im „raw REPL“-Modus. Dieser dient zur direkten Eingabe von Befehlszeilen über „copy und paste“. Unter Umständen ist hier noch einmal mit CTRL-B in den Normal-Modus umzuschalten. Dann können die Anweisungen wie gewohnt eingegeben werden.
Auf diese Weise ist man nun in der Lage, den ESP vollständig drahtlos zu steuern. Über einfache Schaltfunktionen lassen sich so sogar bereits grundlegende Heimautomatisierungsfunktionen realisieren (Bild 7).
Falls ein geeignetes Dateisystem auf dem ESP32 installiert wird, können sogar Softwareupdates drahtlos, also OTA (Over The Air) vorgenommen werden.
Bei der Arbeit mit WebREPL sollte man beachten, dass es sich hierbei noch um ein „experimentelles Feature” handelt, welches noch nicht in allen Situationen absolut zuverlässig arbeitet.
Programmsteuerung
Die REPL- bzw. WebREPL-Konsole ist bestens für Testzwecke geeignet. Für klassische Programmabläufe steht dagegen das darüber liegende Editor-Fenster zur Verfügung. Dort kann beispielsweise das folgende Programm für eine automatische Nachlaufsteuerung (Nachtlicht, Treppenhaus-Beleuchtung) erstellt werden (Bild 8).
Nach der Eingabe und dem Starten des Programms mit dem Start-Icon (weißer Pfeil im grünen Kreis) erfolgt eine Aufforderung zum Speichern des Programms. Hier wird die Option MicroPython device gewählt und ein Programmname (z.B. Automatic_LED) eingegeben und das Programm abgespeichert. Anschließend leuchtet die LED für 3 Sekunden auf und erlischt dann selbständig. Durch nochmaliges Betätigen des Start-Buttons kann das Programm nun direkt gestartet werden.
Um das klassische Demo-Programm (eine blinkende LED) umzusetzen, fehlt nur noch die while-Anweisung. Diese sorgt dafür, dass ein Befehl oder ein Befehlsblock wiederholt ausgeführt wird. Der Spezialfall while True: führt zu einer endlosen Wiederholung, also einem dauerhaften Blinken der LED:
from machine import Pin
from time import sleep
led = Pin(2, Pin.OUT)
while True:
led.on()
sleep(0.5)
led.off()
sleep(0.5)
Dabei ist zu beachten, dass die while-Anweisung mit einem Doppelpunkt abgeschlossen werden muss. Der darauf folgende Funktionsblock ist gemäß der allgemeinen Python-Konvention durch eine Einrückung über Leerzeichnen zu markieren. Thonny verfügt jedoch über eine automatische Einrückung. Sobald nach dem Doppelpunkt die Return-Taste gedrückt wird, erscheint der Cursor in der nächsten Zeile bereits an einer eingerückten Position. Selbstverständlich steht die TAB-Vervollständigung auch im Programm-Editor zur Verfügung. Laufende Programme können über CTRL-C beendet werden.
MicroPython in a Nutshell
Obwohl MicroPython von seinen Bibliotheken lebt, ist ein gewisses Verständnis der Elementar-Anweisungen erforderlich, um Programme zu verstehen oder selbst entwickeln zu können. Im folgenden werden daher die wichtigsten MicroPython-Anweisungen in aller Kürze vorgestellt.
Einfache Kommentare, werden mit dem #-Zeichen eingeleitet. Sie beginnen mit # und enden mit dem Zeilenende:
>>> print("hello ESP32") # this is a comment
hello ESP32
Ein Kommentar wird im Programmablauf ignoriert, da er nur Informationen für die Programmentwickler liefern soll. Über die hier ebenfalls verwendete print()-Anweisung können Informationen auf das Terminal ausgegeben werden print() ist einerseits direkt im Terminalfenster ausführbar, zum anderen dient die Anweisung in Programmen zur Ausgabe von textbasierten Informationen.
Wie bereits beim LED-Blinkprogramm klar wurde, werden in MicroPython verschiedene Blöcke durch Einrückungen gekennzeichnet. Damit sind geschweifte Klammern („{}“) oder ähnliches nicht mehr notwendig. Der Vorteil dieser Methode ist, dass man sozusagen bis zu einem gewissen Maß zur strukturierten Programmierung gezwungen wird.
Variablen lassen sich in Python besonders einfach erstellen. Es ist nicht erforderlich, einen Datentyp anzugeben. Zudem sind Variablen auch direkt in der Konsole einsetzbar:
>>> a=17
>>> b=12
>>> print(a*b)
204
In MicroPython haben arithmetische Operatoren die aus der Mathematik bekannten Bedeutungen. Neben Addition, Subtraktion, Multiplikation und Division sind auch die Operatoren // für Ganzzahlige Division % für Modulo (also Rest der Division) und ** für Exponenten vorhanden.
Darüber hinaus stehen in MicroPython die üblichen Verzweigungs- und Schleifenanweisungen zur Verfügung. In einer Verzweigung wird im Programm eine Bedingung über die Schlüsselworte if und else definiert. Je nachdem, ob diese Bedingung wahr oder falsch ist, wird das Programm an unterschiedlichen Stellen fortgesetzt. Die else-Anweisungen werden nur ausgeführt, wenn die if-Abfrage falsch ist:
if True:
# block 01
print ("True")
else:
# block 02
print ("False")
Schleifen dienen dazu, Anweisungen zu wiederholen. Die Ausführung wird solange fortgesetzt, bis eine vorgegebene Bedingung erfüllt ist. Es sind zwei Varianten verfügbar:
• ‘while’ loops
• ‘for’ loops
Sollen z.B. die Zahlen von 1 bis 9 auf die Konsole ausgegeben werden, kann die folgende while-Schleife verwendet werden:
number=1
while number<10:
print(number)
number=number+1
Der zu wiederholende Code wird durch die Einrückung angezeigt. Die Aufgabe kann auch mit einer for-Schleife erledigt werden:
for number in range(1, 10):
print(number)
Automatische Notsignale
Mit den jetzt bekannten Programmstrukturen kann bereits eine erste praktische Anwendung umgesetzt werden. Mit dem Programm entsteht eine automatische SOS-Bake, die bei See- oder Bergnot eingesetzt werden kann:
from machine import Pin
from time import sleep
led=Pin(2,Pin.OUT)
while True:
for n in range(3):
led.value(1)
sleep(.1)
led.value(0)
sleep(.5)
sleep(1)
for n in range(3):
led.value(1)
sleep(.4)
led.value(0)
sleep(.5)
sleep(1)
for n in range(3):
led.value(1)
sleep(.1)
led.value(0)
sleep(.5)
sleep(2)
Das OLED-Display: Ein Bildschirm im Kleinformat
Auch mit einer einzigen LED kann man also bereits sinnvolle Informationen ausgeben, wenn man beispielsweise, wie in der SOS-Bake, Morsecode verwendet. Wesentlich aktueller ist aber natürlich die Datenausgabe über ein OLED-Display. Ein weit verbreiteter Typ sind Displays, die von einem SSD1306-Displaycontroller gesteuert werden. Wir verwenden eine Anzeigeeinheit, die bei einer Größe von nur 0,96-Zoll (ca. 2,5 cm) über eine Auflösung von 128 x 64 Pixel verfügt.
MicroPython bringt bereits standardmäßig die Library für SSD1306-Displays mit. Diese ermöglicht sowohl die Darstellung von Texten und numerischen Daten als auch die Anzeige von einfachen Grafiken.
Die einfachsten Versionen der SSD1306-Module verfügen über lediglich vier Pins. Dies ist ausreichend um das Display über den sogenannten I2C-Bus anzusteuern. Der Anschluss des Displays ist bereits in Bild 3 dargestellt. Im Folgenden sind die erforderlichen Verbindungen nochmals in tabellarischer Form zusammengefasst:
Das Skript in Listing 1 gibt eine Textnachricht und ein einfaches Grafikelement in Form eines Rahmens auf das Display aus (siehe Bild 3).
Die zugehörige Library steht als Standardbibliothek zur Verfügung (ssd1306.py im Download unten) und kann separat auf das Board hochgeladen werden. Die Pin-Deklaration für den I2C-Bus erfolgt über:
i2c = I2C (-1, scl = Pin (22), sda = Pin (21))
Der Parameter „-1“ gibt an, dass das verwendete Modul weder über Reset- noch über Interrupt-Pins verfügt. Die Anzahl der Pixel des angeschlossenen Moduls wird mit
oled = SSD1306_I2C(128, 64, i2c)
erfasst. Damit ist das Display betriebsbereit. Über die Funktion text() werden Informationen auf die Anzeige ausgegeben. Mit der Methode show() wird die Anzeige aktualisiert. Die text()-Funktion akzeptiert die folgenden Argumente:
• Nachricht (Typ String)
• X-Position und Y-Position des Textfeldes in Pixeleinheiten
• Optionale Textfarbe: 0 = schwarz (dunkel) und 1 = weiß (hell)
Die show() -Methode lässt die Änderungen auf dem Display sichtbar werden. Die rect() -Methode erlaubt beispielsweise die grafische Darstellungen eines Rechtecks. Sie akzeptiert die folgenden Argumente:
• X/Y-Koordinate der unteren linken Ecke des Rechtecks
• Y/Y-Koordinate der oberen rechten Ecke des Rechtecks
• Pixelfarbe: 0 = schwarz, 1 = weiß
Die Anweisung
oled.rect(5, 5, 116, 52, 1)
zaubert also einen rechteckigen Rahmen auf die Anzeige. Damit steht einer Anwendung des Displays für die Ausgabe von Informationen, vom einfachen Text bis hin zu Darstellung komplexer Sensordaten nichts mehr im Weg.
Zusammenfassung und Ausblick
Mit MicroPython steht eine moderne und leistungsfähige Programmiersprache zur Verfügung. Über Bibliotheken können auch komplexere Projekte schnell und einfach umgesetzt werden. Nachdem in diesem ersten Artikel die Installation der zugehörigen IDE und einige einfache Anwendungsbeispiele vorgestellt wurden, werden in einem zweiten Beitrag weitere Aspekte von MicroPython erläutert. Als Praxisanwendung soll dann unter anderem ein LED-Dotmatrix-Display im Großformat vorgestellt werden.
Weitere Informationen zum Thema MicroPython, zum ESP32-Controller und den hier vorgestellten Beispielen finden sich im Buch „MicroPython für Mikrocontroller“.
Mehr über MicroPython
Möchten Sie mehr über Themen wie ESP32 und MicroPython erfahren? Abonnieren Sie Elektor und verpassen Sie keinen Artikel, kein DIY-Projekt und kein Technik-Tutorial.
Diskussion (10 Kommentare)