MicroPython – Umstieg von Arduino

Über den Beitrag

Dieser Beitrag wendet sich an diejenigen, die Erfahrung in der Programmierung von Arduinos haben, aber mit MicroPython mal einen Blick über den Tellerrand wagen wollen. Dabei verwende ich den ESP32 als Microcontroller und uPyCraft als MicroPython IDE.

Ich werde die folgenden Themen abdecken:

  • Installation von Python und uPyCraft
  • Einrichtung von uPyCraft für den ESP32
  • Crashkurs in MicroPython
  • Einfache GPIO Operationen mit MicroPython
  • Anhang: Installation von Thonny als Alternative zu uPyCraft

Wenn ihr diesen Beitrag durcharbeitet, werdet ihr am Ende in der Lage sein, einfache MicroPython Programme zu schreiben. Außerdem werdet ihr die GPIOs des ESP32 mit den Pendants zu digitalWrite() und digitalRead() schalten bzw. auslesen können. Im nächsten Beitrag werde ich dann zeigen, wie ihr weitere Funktionen des ESP32 (PWM, A/D-Wandler, Timer, usw.) mit MicroPython nutzt.

Es ist eine ganze Menge Wissen auf engem Raum, die ich hier vermittele. In Programmierbüchern würde es dutzende Seiten füllen. Probiert die Dinge also aus und variiert die Beispiele, obwohl vieles vielleicht erst einmal langweilig ist. Aber nur so lernt ihr die Sprache.

Hilfreich, aber nicht unbedingt notwendig, sind vorherige Erfahrungen mit dem ESP32. Dazu findet ihr hier etwas.

Python und MicroPython

Python ist eine objektorientierte Hochsprache, die vergleichsweise leicht zu erlernen ist. Programme in Python Code sind gut lesbar und sehr kompakt. Die Sprache ist recht jung – sie wurde Anfang der 90er-Jahre von Guido van Rossum entwickelt. Dieser ist anscheinend ein Monty Python Fan, denn darauf geht der Name zurück. Bei den Hobbyelektronikern hat sich Python vor allem durch seine Verwendung auf dem Raspberry Pi verbreitet.

Aber Python hat auch Nachteile: Die Programme werden interpretiert und nicht kompiliert. Das macht sich sehr in der Geschwindigkeit bemerkbar. Python ist wesentlich langsamer als C++. Hinzu kommt die schwierigere Fehlersuche. Syntaxfehler werden erst gefunden, wenn der Interpreter auf die entsprechende Zeile stößt. 

Man könnte sagen, dass MicroPython in Bezug auf Python das ist, was die „Arduino-Sprache“ für C++ ist, nämlich eine Anpassung für Microcontroller. Da die von euch erstellten Programme immer auch den MicroPython Interpreter benötigen, ist diese Sprache nur für Microcontroller mit entsprechend großem Speicher und ausreichender Rechenleistung geeignet. Unter anderem gibt es MicroPython für den ESP32 und den ESP8266.

Auf https://docs.micropython.org/en/latest/index.html findet ihr die hervorragend strukturierte Dokumentation zu MicroPython.

Vorbereitung: Python installieren

Ihr müsst zunächst Python installieren, da MicroPython darauf basiert.

  1. Geht auf https://www.python.org/ und dort zum Download Link für die neueste Version.
  2. Im Bereich Files (unten) wählt ihr die richtige Installationsdatei. Für Windows am besten „Windows installer (64-bit)“.
  3. Führt die Installationsdatei aus und wählt dabei „Add Python version to PATH“.

uPyCraft installieren und den ESP32 einrichten

Installation

Sofern Ihr Windows benutzt, ladet ihr uPyCraft hier herunter. Es gibt keine Installationsdatei, sondern ihr erhaltet das ausführbare Programm direkt als „.exe“-Datei.  Nehmt die Datei und kopiert sie in den Ordner, von dem aus ihr arbeiten wollt.

Beim ersten Aufruf werdet ihr wahrscheinlich aufgefordert, SourceCodePro zu installieren. Nachdem ihr der Aufforderung nachgekommen seid, findet ihr eine Datei namens SourceCodePro.ttf auf eurem Rechner. Diese enthält die benötigten Zeichensätze. Um zu verhindern, dass ihr bei jedem Programmstart erneut danach gefragt werdet, macht einen Rechtsklick auf die Datei und wählt „Für alle Benutzer installieren“.

Ihr werdet auch gefragt, ob Ihr eine neuere Version von uPyCraft installieren wollt. Klickt auf OK. Nun findet ihr eine weitere Datei namens uPyCraft_Vx.y.exe. Die ursprüngliche uPyCraft.exe Datei könnt ihr löschen.

Damit MicroPython auf dem ESP32 läuft, müsst ihr nun die Firmware für den ESP32 herunterladen. Dazu geht ihr auf https://micropython.org/download/esp32/ und wählt die neueste, stabile „GENERIC“ Version:

MicroPython Firmware für den ESP32
MicroPython Firmware für den ESP32

Legt die Firmware Datei am besten in eurem uPyCraft Ordner ab.

Die uPyCraft Benutzeroberfläche

Die Benutzeroberfläche von uPyCraft besteht aus fünf Bereichen:

uPyCraft für MicroPython - Benutzeroberfläche
uPyCraft Benutzeroberfläche
  1. Dateiverwaltung
  2. Programmeditor
  3. Shell (Ein-/Ausgabe)
  4. Symbolleiste
  5. Menüleiste

Erklärungen folgen noch – ich möchte an dieser Stelle nur sicherstellen, dass ihr wisst, worüber ich rede, wenn ich auf die Bereiche Bezug nehme.

Einrichten des ESP32

Verbindet den ESP32 mit eurem Rechner. In der Menüleiste wählt ihr unter Tools → Serial den Port, an dem euer ESP32 hängt. Klickt darauf. Wenn der Port nicht auftaucht, fehlt euch vielleicht ein Treiber für den USB-zu-UART Adapter auf dem ESP32. In dem Fall findet ihr den Treiber hier

Firmware brennen

Nun müsst ihr noch die Firmware auf euren ESP32 brennen. Geht dazu auf Tools → BurnFirmware. Wählt die Firmware Datei, die ihr zuvor heruntergeladen habt. Die richtigen Einstellungen sehen so aus (euer Port ist natürlich anzupassen):

Micropython Firmare brennen
MicroPython Firmware für den ESP32 brennen

Sollte der Brennvorgang nicht funktionieren, müsst ihr eventuell zuerst die Boot-Taste drücken. Das ist abhängig von eurem Board.

Wenn alles gut gegangen ist, taucht im Ein-/Ausgabefenster der Prompt „>>>“ auf. Evtl. müsst ihr den ESP32 dazu resetten oder einmal vom Strom trennen. Wenn ihr ihn wieder einsteckt, müsst ihr den Serial Port erneut wählen. uPyCraft ist in dieser Hinsicht nicht besonders smart.

Damit habt ihr es geschafft und könnt endlich loslegen.

Erste Schritte

Python erlaubt, dass ihr Code direkt in der Shell (Ein-/Ausgabefenster) eingeben und testen könnt. Das nennt sich REPL für „Read-Evaluate-Print Loop“. REPL ist eine großartige Sache und gehört zu den Dingen, die mit einer compilerbasierten Sprache in dieser Form nicht möglich wären.

Probiert es aus, indem ihr zunächst a = 42 (oder eure Lieblingszahl) eingebt und mit Enter bestätigt. Die nächste Anweisung lautet print("a =", a). Als Ausgabe erhaltet ihr a = 42. Mit type(a) fragt ihr den Variablentyp von a ab. Dann gebt ihr a *= 1.0 ein, danach einfach a und schließlich wieder type(a):

Erste Schritte mit MicroPython in der uPyCraft Shell.
Erste MicroPython Schritte

Damit habt ihr schon einiges über Python gelernt:

  • Ihr müsst keine Variablentypen deklarieren. Python interpretiert den Typ aus der Art der Eingabe:
    • a = 42.0 ergäbe ein Float.
    • Mit a = "42" wäre a ein String. Gleichbedeutend ist a = '42'.
  • Anweisungen werden nicht durch ein Semikolon, sondern durch einen Zeilenumbruch abgeschlossen.
  • Variablentypen sind als Klasse definiert.
  • Die print() Funktion sorgt automatisch für einen Zeilenumbruch.
  • In der Shell reicht der Variablenname + Enter, um den Wert der Variablen zu erhalten.
  • Variablentypen werden in größerem Maße impliziert umgewandelt als bei C++. a *= 1.0 hätte in C++ keine Umwandlung ausgelöst.
    • Explizite Umwandlungen, z. B. float(variable), int(variable) oder str(variable) sind auch möglich.
  • Ihr könnt mehrere Ausgaben in einer print() Anweisung zusammenfassen, jeweils durch ein Komma getrennt.
    • Python fügt automatisch Leerzeichen zwischen den Teilen der Ausgabe ein.

Wenn ihr den Zeilenumbruch verhindern wollt, gebt als letztes Argument end = "" ein, also z.B. print("a =", a, end="").

Sehr angenehm ist, dass ihr mit den „Hoch-/Runter“-Pfeiltasten zwischen vorherigen Eingaben herum scrollen könnt.

Das erste Programm

Die direkte Eingabe von Code ist sehr praktisch, aber normalerweise werdet ihr eure Programme in Form von Dateien schreiben. Dazu kommen wir jetzt. Außerdem wollt ihr sicherlich sehen, wie ihr mit MicroPython die GPIOs steuert.

Hängt eine LED mit Vorwiderstand (denkt an die 3.3 Volt) an einen GPIO des ESP32 (ich habe GPIO18 gewählt). Dann geht in der Menüleiste auf File → New. Alternativ klickt ihr auf das oberste Symbol in der Symbolleiste. Daraufhin erscheint im Programmeditor die Überschrift „untitled“ und die Zeilennummerierung. Gebt dort das folgende Programm „blink.py“ ein:

from machine import Pin
from time import sleep
led = Pin(18, Pin.OUT)
while True:
    led.value(1) 
    sleep(1)
    led.value(0)
    sleep(1)

Speichert das Programm über die Menüleiste (File → Save) oder über das Diskettensymbol. Im aufgehenden Fenster gebt ihr einen Dateinamen ein (Input File). Danach werdet Ihr einmalig gefragt, wo der Workspace von uPyCraft angelegt werden soll. In dem gewählten Verzeichnis (am besten euer uPyCraft Ordner) legt uPyCraft das Verzeichnis „workSpace“ und das Unterverzeichnis „user_lib“ an. Im Workspace werden eure Pythonprogramme gespeichert.

Nun geht ihr auf Tools → DownloadAndRun oder ihr drückt F5 oder ihr klickt auf das Dreieck in der Symbolleiste. Das Programm wird daraufhin auf den ESP32 geladen und ausgeführt. Die LED sollte nun im Sekundentakt blinken.

Das File Handling

Jetzt klickt auf Files → Reflush Directory. Dadurch wird das Dateiverzeichnis aktualisiert. Dann klickt auf die Dreiecke vor device und vor workSpace. Wie ihr sehen werdet, befindet sich die Datei blink.py nun sowohl im Workspace wie auch auf dem ESP32 (= device). Beide Dateien lassen sich unabhängig voneinander bearbeiten. Bevor ihr die Version auf dem ESP32 öffnen könnt, müsst ihr allerdings das laufende Programm mit Stop (über Symbol- oder Menüleiste) beenden.

Ihr erkennt am Symbol neben dem Dateinamen im Editorfenster, welche Version ihr offen habt. Ich würde grundsätzlich mit der Version aus dem Workspace arbeiten.

blink.py im Workspace und auf dem ESP32
blink.py im Workspace und auf dem ESP32

Wenn ihr weitere Programme erstellt und auf den ESP32 hochladet (herunterladet in der uPyCraft Sprachweise), werden diese zusätzlich auf dem ESP32 gespeichert. Wenn ihr eine Datei auf dem ESP32 löschen wollt, dann macht einen Rechtsklick auf die Datei im Device-Verzeichnis und wählt Delete.

Automatischer Programmstart

Wenn ihr uPyCraft verlasst, stoppt auch die Ausführung von blink.py. Um blink.py wieder zum Laufen zu bekommen, müsst ihr uPyCraft erneut aufrufen, den Serial Port aktivieren, blink.py aufrufen und DownloadAndRun starten. Genauso wenig könnt ihr blink.py automatisch starten, indem ihr den ESP32 resettet oder zwischenzeitlich vom Strom trennt. Das macht auch Sinn, da ihr ja mehrere Programme auf dem ESP32 haben könnt.

Damit ein Pythonprogramm auf dem ESP32 automatisch startet, müsst ihr es „main.py“ nennen. Leider gibt es dabei ein Problem. Denn wenn main.py läuft, könnt ihr euch nicht über den Serial Port verbinden. Und damit könnt ihr main.py auch nicht stoppen. Ich habe mit uPyCraft keine andere Möglichkeit gefunden, als die Firmware neu zu brennen. Wartet deshalb mit der Umbenennung in main.py bis ihr euer Projekt fertiggestellt habt. Alternativ benutzt ihr Thonny (siehe Anhang). Da könnt ihr das laufende main.py mit Steuerung / C stoppen.

Erklärungen zu blink.py

Zurück zum Code. Mit from module import class/method werden Klassen oder Methoden aus Modulen importiert, die sozusagen nicht zur Grundausstattung gehören. Das ist ähnlich der #include Anweisung in C++. In blink.py wird die Klasse „Pin“ aus dem Modul „machine“ und die Funktion „sleep“ aus dem Modul „time“ importiert. Eine Übersicht über die verschiedenen, ESP32-spezifischen Module, Klassen und deren Methoden findet Ihr im Quick Reference Guide.

Die Anweisung

led = Pin(18, Pin.OUT)

entspricht dem Arduino Code:

int led = 18;

pinMode(led, OUTPUT);

Allerdings mit dem Unterschied, dass „led“ in MicroPython ein Objekt der Klasse Pin ist. Entsprechend erfolgt die Schaltung des Pins auf HIGH oder LOW mit einer Methode der Klasse Pin:

led.value(1) bzw. led.value(0)

Alternativ geht auch:

led.on() bzw. led.off()

Ihr könnt Pin() weitere Argumente übergeben, z.B. den Pinzustand:

led = Pin(18, PIN.OUT, value=1) oder kurz led = Pin(18, PIN.OUT, 1)

Wenn ihr einen Pin auf Input setzen wollt und einen Pull-Up oder Pull-Down Widerstand benötigt (beispielsweise zum Auslesen eines Tasters), sieht das so aus:

buttonPin = Pin(18, Pin.IN, PIN.PULL_DOWN)   # or: PULL_UP

Hier habt ihr nebenbei auch gelernt, dass ihr Kommentare mit einem „#“ anstelle von „//“ markiert.

sleep() entspricht delay(), allerdings übergebt ihr Sekunden. Mit sleep_ms() übergebt ihr Millisekunden und sleep_us() entspricht delayMicroseconds().

loop() ersetzt ihr in MicroPython durch while True:. Alles, was ihr im Arduino Code vor und im setup() Bereich habt, kommt in MicroPython vor die while-Schleife.

Die Bedingung für die while-Schleife kommt ebenso wenig in Klammern wie die Anweisungen innerhalb der Schleife. Allein die Einrückung zeigt, was zur Schleife gehört. Bei Arduino / C++ dient die Einrückung nur der Lesbarkeit, bei MicroPython hat sie hingegen eine echte Bedeutung.

Der letzte Punkt, auf den ich hinweisen möchte, ist, dass alternativ zu from ... import ... auch einfach import ... erlaubt ist. Allerdings müsst ihr dann den Klassen und Methoden die Modulnamen voranstellen:

import machine
import time
led = machine.Pin(18, machine.Pin.OUT)
while True:
    led.on()
    time.sleep(1)
    led.off()
    time.sleep(1)

Weitere allgemeine MicroPython Elemente

Operatoren

Bei den arithmetischen Operatoren +, -, *, / müsst ihr euch nicht umgewöhnen, ebenso wenig beim Modulo %. Exponentialfunktionen realisiert ihr mit **, also z.B. 2**4 für „2 hoch 4“. Das Zeichen für die ganzzahlige Division ist //.

Als logische Operatoren stehen euch and, or und not zur Verfügung. Einfach so ausgeschrieben, also nicht wie die C++ Operatoren ||, && und !.

Die Bit- und Shiftoperatoren &, |, ^, ~, <<, >> sind wieder identisch mit der Arduino / C++ Schreibweise. Dasselbe gilt für die Vergleichsoperatoren: <, >, <=, >=, ==, !=.

Eine Übersicht der mathematischen Funktionen wie Sinus, Wurzel, usw. findet ihr hier.

Zahlensysteme

Binäre, oktale und hexadezimale Zahlen werden wie in C++ als 0b..., 0o... und 0x... dargestellt. Wenn ihr eine Zahl in einem bestimmten Format ausgeben wollt, dann benutzt bin(), oct() oder hex(). Zum Beispiel: print(bin(a)).

Darüber hinaus kann MicroPython auch mit komplexen Zahlen umgehen.

Listen und Arrays

MicroPython kennt eine ganze Reihe verschiedener Datensammlungen (Kollektionen). Dazu gehören Listen, Arrays Bytearrays, Tupel, Mengen, Dictionaries und andere. Sie unterscheiden sich unter anderem darin,

  • welche Datentypen sie aufnehmen können,
  • ob sie veränderbar sind
  • und ob die einzelnen Elemente eine feste Reihenfolge haben, also einen Index haben.

Sehr flexibel und häufig gebraucht sind Listen. Sie werden durch eckige Klammern eingefasst und ihre Elemente werden durch Kommas getrennt. Hier ein Beispiel:

l = [42, True, "abc", 66.6]

Listen lassen sich verändern, erweitern und sortieren. Ihr könnt auf einzelne Elemente zugreifen. Listen lassen sich konkatenieren (aneinanderhängen). Auch sind Listen als Elemente von Listen zugelassen. Die folgenden Ein- und Ausgaben sind – so denke ich – selbsterklärend.

Operationen mit Listen in MicroPython
Operationen mit Listen in MicroPython

Eine wichtige Funktion für Listen habe ich noch vergessen: len(l) liefert die Anzahl der Elemente in der Liste l.

Arrays sind im Prinzip Listen, die nur einen Datentyp zulassen. Ansonsten könnt ihr dieselben Methoden auf sie anwenden. Arrays arbeiten effizienter als Listen, was bei großen Datenmengen relevant werden kann. Hier ein Beispiel, wie ihr ein Array erstellt:

import array as arr
a = arr.array("i", [3,7,9,25])

Das „i“ steht für Integer. Hier muss also ausnahmsweise mal ein Datentyp spezifiziert werden. Für ein Float-Array müsstet ihr „f“ angeben. Spielt am besten ein wenig mit Listen und Arrays herum.

Veränderbare und nicht veränderbare Objekte

Die folgenden Zeilen überraschen nicht:

Diese vielleicht schon:

In (Micro-)Python ist alles ein Objekt. Und Variablen sind Namen, die auf diese Objekte verweisen. Man sagt auch: die Namen sind an die Objekte gebunden. MicroPython unterscheidet dabei veränderbare und unveränderbare Objekte. Eine „1“ ist beispielsweise ein Objekt der Klasse „int“ und nicht veränderbar. Klingt komisch, ist aber so. Listen und Arrays sind hingegen veränderbar.

Im ersten Beispiel verweisen a und b zunächst auf dasselbe Objekt, nämlich die 1. Mit b = 2 verweist b auf ein neues Objekt, nämlich die 2. a bleibt davon unberührt. Im zweiten Beispiel ändert  b[0] = 2 hingegen nicht die Bindung an das Objekt (nämlich die Liste), sondern ändert das Objekt, da dieses änderbar ist.

Im nächsten Beispiel wird wiederum die Bindung geändert:

Jedes Objekt hat in MicroPython eine eigene ID, die ihr mit id(object) abfragen könnt. Gebt hintereinander ein:

a = 1,

id(a),

a += 1 und schließlich wieder:

id(a)

Wie ihr seht, ändert sich die ID von a. Falls ihr noch Lust habt, gebt id(2) ein. Konsequenterweise ist id(a) gleich id(2)

Wenn ihr das verinnerlicht, könnt ihr einiges in MicroPython besser verstehen. Das Thema wird unter anderem wichtig, wenn ihr Listen, Arrays oder andere veränderbare Objekte an Funktionen übergebt und dort verändert. Denn eigentlich übergebt nur die Verweise und arbeitet in den Funktionen mit den Originalen. Das ist so wie das Übergeben von Referenzen in C++.

Tupel und Bytearrays

Ich hatte Tupel und Bytearrays weiter oben schon erwähnt. Ich muss näher auf diese Datentypen eingehen, da ihr mit hoher Wahrscheinlichkeit auf sie treffen werdet. Es ist anfangs etwas verwirrend Listen, Arrays, Bytearrays und Tupel auseinanderzuhalten. Und es ist so erst einmal ziemlich langweilig. Die gute Nachricht ist, dass die Methoden, die ihr auf sie anwenden könnt, sich wiederholen.

Tupel

Im Grunde genommen sind Tupel einfach nur unveränderbare Listen. Sie können beliebige Objekte aufnehmen. Im Gegensatz zu den Listen werden Tupel in runden Klammern definiert. Ihr könnt die Klammern allerdings auch weglassen. Ich persönlich finde es mit Klammern besser lesbar.

Tupel – Operationen

Wie ihr seht, funktioniert tuple_2[4] = 43 nicht. Tupel sind nicht änderbar.

Bytearrays

Bytearrays sind – nicht überraschend – Arrays, die nur Bytes aufnehmen. Wie die anderen Arrays sind sie änderbar. Am besten definiert ihr Bytearrays, indem ihr der Funktion bytearray() eine Liste übergebt. Hier ein paar Beispiele:

Bytearray – Operationen

Auffällig ist die etwas kryptische Ausgabe, wenn ihr das Bytearray als Ganzes ausgebt. Sofern der Wert eines Elementes einem druckbaren ASCII-Zeichen entspricht, wird das Zeichen ausgedruckt. Falls nicht, wird das Element im Format „\xyy“ angezeigt, wobei „yy“ der Wert in hexadezimaler Schreibweise ist. 

Ich traue mich gar nicht es zu sagen: Es gibt noch mehr!

  • Bytestrings: sind unveränderbare Bytearrays und werden mit bytes() anstelle bytearray() definiert. Sonst ist alles gleich.
  • Dictionaries: ähneln enum Konstruktionen. Für die, die immer noch nicht genug haben: schaut hier.
  • Sets (Mengen): sind ungeordnete, veränderbare Kollektionen, die keine Duplikate zulassen. Sets werdet ihr selten brauchen.

Weitere Schleifen und Bedingungen

if – else – elif Konstruktionen

Zu if und else gibt es wenig zu sagen. Ein else if heißt in Micropython elif. Hier ein – zugegebenermaßen an sich ziemlich sinnloses – Beispiel:

from time import sleep
from random import randint   
while True:
    n = randint(-10, 30)   # random integer between -10 and +30
    print("Random number =", n)
    if n > 0:
        if n > 20:
            print("Number is bigger than 20")
        elif n > 10:
            print("Number is bigger than 10")
        else:
            print("Number is bigger than 0")
    elif n <= 0:
        print("Number <= 0")
    print("...........")
    sleep(1.5)

Zusätzlich lernt ihr durch dieses Beispiel:

  • Zufallszahlen in einem Bereich von x bis y (einschließlich der Grenzen) erzeugt ihr mit randint(x,y).
  • Die Funktion randint() muss importiert werden.
  • sleep() funktioniert auch mit Fließkommazahlen.

For – Schleifen

Gebt Folgendes in der Shell ein:

for i in range(1,4):
    print(i)

Ihr seht, dass MicroPython ist intelligent ist und nach dem Doppelpunkt weitere Eingaben erwartet. Ihr erhaltet:

for-Schleife in MicroPython
for-Schleife in MicroPython

for i in range(1,4) ist also äquivalent zum Arduino/C++ Ausdruck: for(int i=1; i<4; i++). Ihr könnt range() auch einen dritten Parameter übergeben und damit das Inkrement ändern. Probiert mal range(1,10,2) und range(4,1,-1) und schaut was passiert.

Oder ihr übergebt nur einen Parameter, nämlich das obere Limit. range(x) entspricht range(0,x).

Auch diese Konstruktionen funktionieren:

Weitere for-Schleifen

Ihr seht, dass man mit MicroPython ziemlich kompakten Code schreiben kann.

Funktionen

Auch bei Funktionen müsst ihr euch nicht in besonderem Maße umgewöhnen. Hier ein kleines Beispiel:

def double_it(i):
    result = i * 2
    return result
  
n = 5
print("5 * 2 =", double_it(n))
n = 5.0
print("5.0 * 2 =", double_it(n))
n = "Hello"
print("Hello * 2 =", double_it(n))

Die Ausgabe ist:

Was ihr davon lernt:

  • Funktionen werden mit def function_name(parameter): definiert.
    • Mehrere Parameter können durch Kommas getrennt eingegeben werden.
  • Ihr müsst keine Datentypen für die Parameter und Rückgabewerte angeben. Dadurch sind die Funktionen sehr flexibel, wie ihr seht.

Formatierte Ausgabe

In Python bzw. MicroPython gibt es viele Wege Zahlen und Strings formatiert auszugeben. Hier erst einmal ein unformatiertes Beispiel: 

Hier gebe ich die Zahlen nun formatiert aus:

Das ist weniger kompliziert, als es auf den ersten Blick aussieht. Ihr ersetzt die Zahlen, die ihr formatieren wollt, durch eine Formatierungsanweisung in geschweiften Klammern. Die zu formatierenden Daten hängt ihr mit .format(...) hinten dran. Der allgemeine Ausdruck für die formatierte Ausgabe ist:

print(" ..... {Format_0} ...... {Format_1}.....".format(Argument_0, Argument_1,...))

Die Formatierungsanweisung Format_x ist wie folgt aufgebaut:

{N:FGesamtlänge.NachkommastellenT} mit:

  • N = Nummer der Formatierungsanweisung (optional)
  • F = weitere Formatierungsanweisungen:
    • <, >, ^ für linksbündig, rechtsbündig, zentriert
    • 0 für voranstehende Nullen
    • + für Vorzeichen
  • T = Zahlentyp:
    • d = dezimal (Integer)
    • f = float
    • b = binär
    • x = hexadezimal
  • Ohne T = String (nur mit N, F und Gesamtlänge)

Diese Schreibweise ist die moderne Variante in (Micro-)Python. Es gibt daneben noch eine veraltete Schreibweise mit Modulo (%) auf die ich hier nicht näher eingehe.

Mehr zu dem Thema Formatierung gibt es hier.

Präzision / Beschränkungen für Float und Integer

Ich komme noch mal auf die Typen Integer und Float zurück. Schaut auf die folgenden Ein-/Ausgaben:

MicroPython kennt für Ganzzahlen nur „int“ und für die Fließkommazahlen nur „float“. So etwas wie „unsigned int“, „long“ oder „double“ gibt es nicht. Trotzdem kann MicroPython mit gigantischen Ganzzahlen umgehen, wie ihr oben seht. Aber so generös MicroPython bei den Integern ist, so knauserig sieht es bei den Floats aus. Nach 7 Stellen ist  Schluss. 10/3 ist sicherlich nicht 3.3333332539.  

Anwendung / Vertiefung

Das war viel Theorie. Aber ihr wisst jetzt schon eine ganze Menge über MicroPython. Zur Entspannung gibt es noch ein bisschen Praxis. Mit der folgenden Schaltung wird ein Lauflicht (Chaser Light) aus 5 LEDs realisiert.

Schaltung für ein Lauflicht
Schaltung für ein Lauflicht

Solange einer der beiden Taster gedrückt wird, ist das Lauflicht aktiv. Je nachdem, welcher Taster gedrückt wird, läuft das Licht in die eine oder andere Richtung.

from machine import Pin
from time import sleep 

led = [19, 18, 5, 17, 16] 
btn1 = Pin(22, Pin.IN, Pin.PULL_DOWN)
btn2 = Pin(23, Pin.IN, Pin.PULL_DOWN)

for i in range(len(led)):
    led[i] = Pin(led[i], Pin.OUT)

def chaser(reverse = True):
    if reverse:
        for i in range(4,-1,-1):
            led[i].value(1)
            sleep(0.15)
            led[i].value(0)
    else:
        for i in range(0,5):
            led[i].value(1)
            sleep(0.15)
            led[i].value(0)
  
while True:
    if btn1.value():
        chaser()
    elif btn2.value():
        chaser(False)

Neu ist hier:

  • btn1.value() gibt den Zustand (HIGH/LOW) von „btn1“ zurück.
  • Mit def chaser(reverse = True)  bekommt reverse den Wert „True“ falls „chaser“ ohne Parameter aufgerufen wird.

Aber ich möchte euer Augenmerk auch auf die folgende Zeile richten:

led[i] = Pin(led[i], Pin.OUT)

„led“ ist eine Liste, deren Elemente zunächst Integerwerte sind. Die obige Anweisung ersetzt das ite Element durch ein Objekt der Klasse Pin. Das funktioniert nur deshalb, weil Listen Elemente unterschiedlicher Datentypen enthalten dürfen. Mit einem Array könnte man so etwas nicht realisieren.

Ausblick

Ihr habt in diesem Beitrag die Grundlagen von MicroPython erlernt und ihr wisst, wie ihr Programme auf den ESP32 hochladet. Zudem habt ihr erste Erfahrungen mit der Steuerung der Pins gesammelt. Im nächsten Beitrag möchte ich darauf aufbauen und zeigen, wie ihr weitere Funktionen des ESP32 mit MicroPython nutzen könnt. Dazu gehört u.a. PWM, die A/D-Wandler, Timer, Interrupts, Touch Pins und einiges mehr.

Anhang: Thonny – die komfortablere IDE

uPyCraft erfüllt die Basisanforderungen an eine MicroPython IDE. Besonders komfortabel ist das Programm allerdings nicht. Ihr habt nur wenige Einstellmöglichkeiten und selbst einfache Editiertools zum Kommentieren oder Einrücken fehlen.

Sehr verbreitet ist die Alternative Thonny:

Thonny Benutzeroberfläche
Thonny Benutzeroberfläche

Das sieht schon etwas freundlicher aus, nicht wahr? Und obwohl Thonny mehr kann, ist es nicht kompliziert zu bedienen. Wenn ihr mit uPyCraft zurechtkommt, werdet ihr euch in wenigen Minuten auch in Thonny zurechtfinden. 

Ein Nachteil an Thonny ist, dass ihr damit nicht die ESP32 Firmware brennen könnt. Das ist auch der entscheidende Grund, weswegen ich in meinem Beitrag primär uPyCraft vorgestellt habe. Um trotzdem mit Thonny arbeiten zu können, gibt es unter anderem diese zwei Optionen zum Brennen der Firmware:

  1. Ihr nutzt uPyCraft dafür. Das würde ich empfehlen.
  2. Ihr benutzt das recht kryptische Kommandozeilenprogramm esptool.py. Eine Anleitung gibt es z.B. hier.

Installation von Thonny

  1. Ladet die Installationsdatei von https://thonny.org/ herunter (ganz oben auf der Seite).
  2. Führt die Installationsdatei aus und folgt den Anweisungen.
  3. Verbindet euren ESP32 mit dem Computer.
  4. Öffnet Thonny und geht zu Tools → Options → Interpreter.
  5. Wählt dort „MicroPython (generic)“ und den richtigen Port.

Das war’s und ihr könnt loslegen.

Die input() Funktion

Ein weiterer Vorteil von Thonny ist, dass hier die input() Funktion implementiert ist. Bei uPyCraft funktioniert das (zumindest mit der derzeit aktuellsten Version) nicht. Mit der input() Funktion könnt ihr Daten über die Shell eingeben. Die Eingabe wird als String interpretiert und muss ggf. explizit umgewandelt werden.

while True:
    n = input("Input a number: ")
    n = int(n)
    if n > 0:
        if n > 20:
            print(n, "is bigger than 20")
        elif n > 10:
            print(n, "is bigger than 10")
        else:
            print(n, "is bigger than 0")
    elif n <= 0:
        print(n, "is <= 0")
    print("...........")

Danksagung

Die Schlange auf meinem Beitragsbild habe ich Clker-Free-Vector-Images auf Pixabay zu verdanken.

3 thoughts on “MicroPython – Umstieg von Arduino

  1. Hallo Wolfgang,

    Danke fürs schnelle Feedback! Ja, in der Tat sieht die Sprache recht simpel aus bzw. ein Umstieg sollte auch für Gelegenheitsprogrammierer gut möglich sein.
    Schaun wir mal, was draus wird… 🙂

    Grüße
    Hartmut

  2. Wie immer toller Bericht und sehr praxisnah rübergebracht!

    Siehst Du in Phyton irgendwelche tiefgreifenden Vorteile gegenüber der Programmierung über IDE im Arduino Code? Kleinigkeiten hast Du ja erwähnt, aber den „Kracher“ finde ich nicht. 😉
    Wenn ich mir die Programmiersprache so ansehe, sind sie von der Syntax nicht so weit auseinander.
    Dann der Geschwindigkeitsnachteil… was bleibt da übrig ausser der „Alternative“?

    Grüße
    Hardy

    1. Hallo Hardy,

      sagen wir mal so: wer mit C++ / Arduino glücklich ist, dem rate ich nicht, dauerhaft auf Python bzw. Micropython umzusteigen. Es wird auch nicht möglich sein, z.B. einen ATtiny mit Python zu programmieren. Das geht nur mit den „fetten“ ESP32, ESP8266, Raspberry Pi Pico, usw..

      Aber wenn ich mal eine langfristige Prognose wagen darf, dann werden die AVR MCUs verschwinden, die leistungsstarken MCUs werden der Standard und der Trend wird in Richtung Python gehen. Es ist einfacher zu lernen, z.B. kennt Python keine Zeiger (unter der Haube natürlich schon).

      Zumindest schadet es nicht, sich mal ein bisschen damit zu beschäftigen. Ein Wochenende und man kann die Basics. Ein Beitrag zu dem Thema kommt noch, dann wird noch etwas sichtbarer, dass der Python Code einfach und kompakt ist.

      Danke für den Kommentar, VG, Wolfgang

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.