Bei der Entwicklung von Embedded Software muss man sich mit Crosscompiling, herstellerspezifischen Libraries / Tools / IDEs sowie low-level Sprachen herumschlagen. Diese langsamen Iterationen fallen insbesondere beim Prototyping negativ ins Gewicht. Mit MicroPython ist das Versprechen verbunden, die komfortablen Features von Python3 – inkl. interaktiver Konsole – für die Entwicklung im Embedded Bereich zugänglich zu machen. Kompilieren und flashen ist nicht nötig und Code wird einfacher wiederverwendbar.
Low-level Programmierung schnell und leicht gemacht!
Aufwändige Embedded-Entwicklung
Embedded-Systeme haben beschränkte Ressourcen. Dies bedeutet, dass die Software stark auf die Hardware abgestimmt werden muss. Der Overhead für Hochsprachen und umfangreiche Bibliotheken gelten oftmals als inakzeptabel. Dies bedeutet, dass man sich als Entwickler mit Plattformabhängigen IDEs / Tools, Libraries und Cross-Compilern herumschlagen muss.
Die Programmiersprache C ist im Embedded-Umfeld nach wie vor weit verbreitet, da sie es erlaubt, das Geschehen in der Hardware sehr genau zu kontrollieren. Für Low-Level-Funktionen ist dies sehr wünschenswert. High-Level-Abstraktionen sind jedoch aufgrund der fehlenden Objektorientiertheit und dem aufwändigem Datenhandling umständlich. Und jede Codeänderung erfordert ein erneutes Kompilieren. Danach muss das Artefakt über eine zusätzliche Hardware („Programmer“) auf das Target geflasht werden, was den Entwicklungsflusses weiter einschränkt. Ebenso ist das Debugging im Betrieb nur über spezielle Betriebsmodi / Debug-Connector möglich.
All dies zusammen macht die Embedded-Entwicklung zwar spannend, aber auch deutlich träger als bei modernen Applikationen für PCs oder Smartphones.
Schnellere Entwicklungszyklen mit MicroPython
MicroPython ist ein Betriebssystem, welches z.B. auf dem PyBoard vorinstalliert ist.
Das PyBoard lässt sich über ein microUSB-Kabel mit dem PC verbinden. Es meldet sich als serielle Schnittstelle sowie als Massenspeicher (wie ein USB-Stick) an.
Öffnet man die serielle Schnittstelle z.B. mit PuTTY (Default Baudrate: 115’200), wird eine interaktive Konsole präsentiert, wie man sie von Python kennt – und man kann direkt Befehle eintippen:
Auf der Konsole lassen sich sogar von Linux bekannte Shortcuts benutzen – z.B. vorherige Befehle mit Pfeil-hoch/runter kopieren, oder Autocompletion mit Tabulator.
Interessant ist, dass MicroPython die Hardware-Funktionen abstrahiert und in kompakter Python-Syntax verfügbar macht:
>>> import pyb >>> pyb.LED(1).on() # LED einschalten >>> analog_input = pyb.ADC(pyb.Pin('X19')) >>> analog_input.read() # ADC auslesen 557
Diese Funktionen werden direkt auf dem Board ausgeführt – und wir haben nichts kompiliert oder über ein dediziertes Tool geflasht. Wir können direkt auf dem Board entwickeln und debuggen!
Jetzt besteht nur noch das Problem, dass diese Eingaben nach einem Power Cycle (USB trennen und neu verbinden) wieder gelöscht sind. Wie kann man nun ein Programm auf das Board „flashen“, sodass es beim nächsten Start ausgeführt wird? Hier kommt der Massenspeicher zum Einsatz.
Wir schreiben unser Programm in ein Text-File und nennnen dieses „main.py“. Ein „Blinky“ kann folgendermassen aussehen:
import pyb, time while True: pyb.LED(1).toggle() time.sleep_ms(100)
Dieses File kopieren wir einfach auf den angemeldeten „USB-Stick“, der vom PyBoard angeboten wird – fertig.
Beim nächsten Powercycle führt das Board das Programm in „main.py“ aus. Wenn wir die Ausführung unterbrechen möchten, drücken wir auf der interaktiven Konsole einfach Ctrl+C. Sämtliche Funktionen / Klassen / Objekte, welche wir zum Ausführungszeitpunkt definiert hatten, stehen uns nach dem Ctrl+C auch auf der interaktiven Konsole zur Verfügung. Die Ausführung wird lediglich unterbrochen, es wird kein automatisches Reset des RAM ausgeführt – das ist sehr praktisch zum Debuggen.
Wir können also schnell die Hardware-Funktionen des Boards auf der Konsole ausprobieren. Langwieriges Flashen entfällt, da wir einfach Dateien rüberkopieren. Und die kompakte aber mächtige Syntax von Python steht zur Verfügung.
Das alles funktioniert ohne dedizierte Tools und Hardware. Ein beliebiger Texteditor und ein beliebiges Tool für die serielle Schnittstelle reichen aus.
Ein Schritt zurück: Wieso ausgerechnet Python und woher kommt MicroPython?
Python erfreut sich einer grossen Beliebtheit, was sich in verschiedenen Ranglisten wiederspiegelt, z.B. von IEEE und Stack Overflow. Python-Code gilt als leicht lesbar, intuitiv und kompakt. Die Syntax ist leicht erlernbar und erlaubt objektorientierte sowie prozedurale Konzepte. Das dynamische Verhalten verursacht allerdings einen erhöhten Speicher- und Leistungsbedarf im Vergleich zu C und anderen Sprachen. Das macht Python zwar sehr schnell in der Entwicklung, jedoch nicht geeignet für kleine Mikrocontroller.
MicroPython ist eine schlanke Implementation von Python 3, welche optimiert wurde für Mikrocontroller. So werden die mächtigen Funktionen von Python auch auf Mikrocontrollern verfügbar. MicroPython wurde von Damien George entwickelt und ist unter MIT-Lizenz als Open-Source-Software verfügbar.
Natürlich verursacht diese Flexiblität einen Overhead ggü. nativ kompilierten Tools. Allerdings ist der Ressourcen-Verbrauch von MicroPython relativ klein. Es werden ledigllich 256 kB Speicher sowie 16kB RAM benötigt. MicroPython ist bereits auf kleinen Prozessoren wie einem Cortex M0+ verwendbar – sogar noch mit genug Luft zum zocken …
Detailliertere Messungen zur Latenz etc. finden sich z.B. im Blog-Beitrag auf elektroniknet.de.
Die Einschränkungen von MicroPython ggü. dem „echten“ Python sind dabei relativ klein. Konzeptbedingt sind sehr viel weniger Libraries out of the box nach der Installation verfügbar. Die weiteren Unterschiede fallen in einfacheren Anwendungen gar nicht auf. Eine detaillierte Auflistung findet man direkt auf GitHub oder in den Docs. Das einfache Listen- und String-Handling sowie das Exception-Handling wurden weitestgehend übernommen. Sogar das File-Handling funktioniert wie in Python:
# As known from Python... f = open('data.txt', 'w') f.write('some' + ' more ' + 'data') f.close()
Embedded-Entwickler können sich vorstellen, wie viel Aufwand (Zeit und Code-Zeilen) mit C in Character-Array-Memory-Allozierung stecken würde. Ganz zu schweigen von File-Systemen usw.
Die verfügbaren Funktionen reichen aber noch deutlich weiter. Ein WLAN-Access-Point gefällig?
import network wl_ap = network.WLAN(1) wl_ap.config(essid='PYBD') # set AP SSID wl_ap.config(password='pybd0123') # set AP password wl_ap.config(channel=6) # set AP channel wl_ap.active(1) # enable the AP wl_ap.status('stations') # get a list of connection stations wl_ap.active(0) # shut down the AP
MicroPython für eigene Hardware
MicroPython ist für viele Plattformen und Prozessoren verfügbar, insbesondere STM32, aber auch ESP32 / ESP8266, nRF (Nordic), SAMD (Microchip). Die Plattformen sind direkt im Repository sichtbar, sowie die unterstützen Boards (z.B. STM32). MicroPython setzt also keine besondere Hardware voraus, sondern ist für verschiedenste Boards verfügbar. Man kann MicroPython auch für Unix oder Windows kompilieren.
Falls man externe Sensoren über einen Peripherie-Bus wie I2C, SPI oder UART ansteuern möchte, kann man dies meist direkt in Python machen:
import machine LED_BROADCAST = 1 # broadcast I2C address LED_ADDR = 60 # default individual LED36 address i2c = machine.I2C('X') # select X bus def set_brightness(addr=LED_BROADCAST, b=100): ba = bytearray(b'\x02\x16 ') ba[-1] = b & 0xff i2c.writeto(addr, ba)
MicroPython ist in C geschrieben. Falls man besondere Hardware-Funktionen verwenden möchte oder bestimmte Treiber-Funktionen benötigt, kann man diese auch selber (in C) hinzufügen und in MicroPython hineinkompilieren. Anleitungen dazu findet man z.B. in den dev-docs.
Einige Tipps für den Einstieg
Der komfortabelste Einstieg geschieht über dedizierte „MicroPython-Boards“ wie dem PyBoard D-series. Letzteres ist direkt auf MicroPython.org bestellbar und bietet eine Vielzahl von Möglichkeiten (inkl. WLAN / Bluetooth) in kompakten Abmessungen. Ausserdem sind verschiedene Erweiterungs-Boards („Tiles„) verfügbar.
Ein weiteres interessantes Board kann auch OpenMV sein. Hier wurde sogar ein Bildsensor und eine komplette Machine-Vision-Bibliothek integriert. So kann man direkt auf dem Mikrocontroller in Python Feature-/ Eye-Detection, Blob-Tracking, Barcode Decoding, Neural Networks usw. umsetzen.
Nebst dem MicroPython-Haupt-Repository finden sich im MicroPython-Lib-Repository viele Libraries, welche ein breites Band an Funktionalitäten abdecken. Diese lassen sich (wie von Python-Files bekannt) durch einfaches Rüberkopieren auf den Massenspeicher installieren.
Falls Multithreading im MicroPython-Projekt benötigt wird, sollte man sich uasyncio LINK (angelehnt an asyncio) mal genauer anschauen.
In der Handhabung auf der interaktiven Konsole (REPL) seien noch zwei praktische Funktionen erwähnt:
- Man kann direkt in der Konsole einen Reboot machen, indem man Ctrl+D drückt (soft reset).
- Mit Ctrl+E erreicht man einen „Entry Mode“, in welchem man einfach aus der Zwischenablage auch grössere Code-Snippets (inkl. Einrückung) einfügen kann.
MicroPython: Embedded Entwicklung leicht(er) gemacht
MicroPython erlaubt wesentlich schnellere Embedded-Entwicklungszyklen, dank:
- intuitiver Syntax und mächtigen High-Level-Funktionen von Python
- wiederverwendbarer, plattformunabhängiger Applikationslogik
- Entfallen des Kompilierungs- und Flashing-Prozesses
- einfachem Debugging auf der interaktiven Konsole
Komplexe Projekte mit spezialisierter Hardware benötigen immer noch ein fundiertes Verständnis und sind nicht in wenigen Minuten umgesetzt – allerdings wird die Entwicklungszeit drastisch reduziert.
Wer mehr über MicroPython erfahren möchte, sollte an die Guild42-Veranstaltung am 18. November 2019 in Bern kommen. Tobias Badertscher und Christian Müller von der Noser Engineering AG stellen dort MicroPython vor. Sie werden ein Beispiel zeigen, wie sich auf einem PyBoard D-series ein „Tile“ ansteuern, ein WLAN-Hotspot aufbauen und ein Dritthersteller-Sensor auslesen lässt. Weitere Infos und kostenlose Anmeldung unter https://guild42.ch/?p=1002. Der (Prototypen-)Source-Code für das Projekt findet sich auf GitHub unter https://github.com/chrismue/pybd_g42_fs.