Raspberry Pi Pico: Ist CircuitPython das bessere MicroPython?

Nachdem ich im ersten Artikel über den Raspberry Pi Pico die Hardware, die technischen Daten und das Pinout des Pico vorgestellt und mit der STM32 Bluepill, dem Arduino Nano und dem ESP8266 verglichen haben, blieb die Frage im Raum stehen, wie einfach sich der Pico programmieren lässt und ob er ein ernstzunehmender Konkurrent zu den etablierten Mikrocontrollern ist.

Im zweiten Artikel ging es dann um die Installation des UF2-Files für MicroPython und die erste Programmierung darin über Putty, was sich als wenig komfortabel herausgestellt hatte.

In dritten Artikel habe ich die Entwicklungsumgebung Thonny für MicroPython auf dem Raspberry Pi Pico vorgestellt und außerdem ein paar Python-Programme geschrieben: Um die interne LED zum Blinken zu bringen, den internen Temperatursensor auszulesen und die Temperatur auf einem OLED-Display anzuzeigen. Auch der Paketmanager und die Suche nach Libraries wurde kurz erklärt. Auch wenn schlussendlich alles lief, ging mir die instabile serielle Verbindung auf die Nerven.

Darum wollen wir heute eine andere Entwicklungsumgebung ausprobieren: CircuitPython. Das ist ein Ableger von MicroPython, der von Adafruit weiterentwickelt wurde und gegenüber Thonny so einige Vorteile zu bieten scheint. So soll das Hochladen komfortabler über ein eingebundenes Laufwerk und nicht mehr über serielle Schnittstelle gehen. Und außerdem soll auch eine Emulation von HID-Devices (Human Interface Devices wie Tastaturen, Mäuse etc.) über den USB-Port gehen. CircuitPython wollen wir heute ausprobieren.

CircuitPython downloaden und installieren

Zu finden ist CircuitPython unter circuitpython.org, dort findet sich neben der Dokumentation auch die UF2-Datei, die wir zur Installation von CircuitPython auf unseren Raspberry Pi Pico brauchen.

Bei mir ist zur Zeit Version 7.1.0 aktuell, die es auch in deutsch gibt und die folgende Module schon mit sich bringt: _bleio, adafruit_bus_device, adafruit_pixelbuf, aesio, alarm, analogio, atexit, audiobusio, audiocore, audiomixer, audiomp3, audiopwmio, binascii, bitbangio, bitmaptools, bitops, board, busio, countio, digitalio, displayio, errno, fontio, framebufferio, getpass, gifio, imagecapture, json, keypad, math, microcontroller, msgpack, neopixel_write, nvm, onewireio, os, paralleldisplay, pulseio, pwmio, qrio, rainbowio, random, re, rgbmatrix, rotaryio, rtc, sdcardio, sharpdisplay, storage, struct, supervisor, synthio, terminalio, time, touchio, traceback, ulab, usb_cdc, usb_hid, usb_midi, vectorio, watchdog.

Besonders die USB-Module interessieren mich, denn mit Arduino und ESP8266/ESP32 ist das nicht so einfach möglich und hier sehe ich eine Lücke, die der Pico schließen könnte. USB-HID kann ich zwar auch mit einem Digispark realisieren, wie hier geschehen, aber da ist nur ein AT Tiny 85 drauf mit ganz wenig Pins und sehr wenig Speicherplatz. Hier könnte der Pico glänzen. Das will ich mal näher untersuchen.

Die UF2-Datei mit dem sperrigen Namen "adafruit-circuitpython-raspberry_pi_pico-de_DE-7.1.0.uf2" lade ich mir auf meine Festplatte herunter und kopiere sie dann auf den Pico. Dazu muss der Pico in den UF2-Installationsmodus gebracht werden, wo er ein Laufwerk bereitstellt, auf das die UF2-Datei einfach per Drag and Drop kopiert wird. Dazu muss der BootSel-Button auf dem Pico-Board gedrückt gehalten werden, während man ihn an den PC ansteckt. Bei mir ein Windows 7 Pro 64 Bit-System.

Kleiner Abstecher: Reset-Taster für den Pico

Ich habe zwar einen USB-Hub, bei dem ich jeden einzelnen Port ein- und ausschalten kann, das USB-Kabel also nicht jedesmal Ab- und wieder anstecken muss, doch selbst das Schalten ging mir mit der Zeit auf die Nerven, so dass ich mir einen Reset-Taster auf mein Pico-Breadboard gebaut habe:



Den habe ich rechts neben dem Pico platziert. Wird er gedrückt, verbindet er den RUN-Pin des Pico mit GND (siehe GPIO-Pinout), was einen Reset ausführt. Jetzt muss ich nur noch den Reset-Taster halten, während ich den BootSel-Taster drücke. Das ist bequemer und geht nicht so auf den USB-Hub-Schalter bzw. den USB-Port (wenn man ab- und anstecken muss).

Die 1.4 MB der UF2-Datei sind in ein paar Sekunden übertragen. Danach startet der Pico neu und diverse Gerätetreiber, die CircuitPython zur Verfügung stellt, werden von Windows gesucht und gemächlich installiert. Während das passiert und ich geduldig warte, überlege ich mir, dass dann ja nur noch 600 KB für das Programm selbst übrig bleiben, wo der Pico doch "nur" 2 MB Flash-Speicher hat. Aber für die meisten Anwendungen wird das wohl ausreichen. Ansonsten muss dann irgendwann doch mal C ran.

Mal wieder Treiberprobleme

Leider funktioniert die automatische Treiberinstallation unter Windows 7 nicht so einwandfrei und es werden nicht alle Treiber installiert:



Schauen wir uns doch einmal die Treiberliste an, und überlegen, wozu die Treiber gut sein werden:

Treiber / GerätTreiberinst.Bemerkungen
USB-Verbundgerätokay__
CircuitPython CDC controlFehlerDies wird die serielle Schnittstelle sein, um Dinge auf die Konsole auszugeben. Leider schlug die Installation fehl.
USB-MassenspeichergerätokayEs wurde ein neues Laufwerk in Windows gemountet names "CIRCUITPY" mit 1 MB. Hier werden wir später unsere Python-Programme draufziehen, um sie hochzuladen
USB-EingabegerätokayDas erwähnte HID-Device, um Tastatur, Maus und so weiter zu emulieren. Ich bin mal gespannt, ob auch Joysticks gehen
CircuitPython AudioFehlerEin Audio Device? Könnte ich mit dem Töne auf dem PC ausgeben, vielleicht sogar den Pico als MP3-Player benutzen? Leider schlug auch hier die automatische Installation fehl.
CircuitPython MIDIokayIch kenn die MIDI-Schnittstelle eher vom Atari. Die werde ich wohl nicht brauchen, sie dient normalerweise zur Steuerung von Musikinstrumenten
Raspberry Pico USB DeviceokayWas das ist, weiß ich nicht genau. Ich tippe mal auf ein "frei" programmierbare USB-Device


Leider konnten zwei Geräte nicht automatisch unter Windows 7 installiert werden. Diese tauchen mit Warnschild im Windows Geräte Manager auf:



Ich schätze mal, den CDC-Treiberfehler bekommen wir weg, wenn wir wieder mal Zadig benutzen, so wie wir es schon in diesem Artikel erklärt habe. Und ja, nachdem Zadig am Werk war, bekomme ich einen neuen COM-Port für das CDC-Device.

Das Audio-Device brauche ich erst einmal nicht und schere mich deswegen momentan noch nicht drum.

Damit ist der Pico mit CircuitPython startklar.

Ohne Entwicklungsumgebung auf den Pico mit CircuitPython

Wie mit MicroPython kann man auch mit CircuitPython ohne Entwicklungsumgebung arbeiten, nur noch ein bisschen komfortabler. Und das geht so: Wir verbinden uns zuerst auf unser CircuitPython-Laufwerk namens CircuitPi. Dort sehen wir folgendes: Datenträger in Laufwerk J: ist CIRCUITPY Volumeseriennummer: 5021-0000 Verzeichnis von J:\ 01.01.2020 00:00 <DIR> .fseventsd 01.01.2020 00:00 0 .metadata_never_index 01.01.2020 00:00 0 .Trashes 01.01.2020 00:00 <DIR> lib 01.01.2020 00:00 103 boot_out.txt 04.01.2022 16:54 117 code.py 4 Datei(en), 220 Bytes 2 Verzeichnis(se), 1.026.048 Bytes frei Interessant ist für uns die Datei code.py. Denn das ist das Programm, was ausgeführt wird. So ähnlich wie die main.py bei Thonny. Und diese können wir ganz einfach in einem Editor unserer Wahl öffnen und editieren.

Ich kopiere mal unser altes Python-Blink-Programm hinein und speichere es: from machine import Pin from time import sleep led = Pin(25, Pin.OUT) while True: led.toggle() sleep(0.5) Dann schaue ich im Windows-Gerätemanager, welchen COM-Port mir Zadig für den CircuitPi als serielle Schnittstelle zugewiesen hat Denn die brauchen wir, um die Print-Ausgaben lesen zu können. Wohlgemerkt nicht zum Upload, der geht ja über das Laufwerk. Bei mir ist das Port COM36. Das trage ich in Putty ein, so wie ich das auch im ersten Teil erklärt habe.

Allerdings sollten wir hier, falls wir uns das deutsche UF2-CircuitPython-Image downgeloadet haben, noch UTF-8 einstellen, damit die Umlaute in Putty korrekt dargestellt werden:



Am besten, wie speichern uns die Einstellungen gleich fürs nächste Mal ab. Dann Drücken wir auf Open zum Verbinden und erhalten dann folgende Ausgabe (Evtl. STRG+D drücken, wenn nicht gleich etwas erscheint): Automatisches Neuladen ist aktiv. Speichere Dateien über USB um sie auszuführen oder verbinde dich mit der REPL zum Deaktivieren. Drücke eine beliebige Taste um REPL zu betreten. Drücke STRG-D zum neuladen. Nachdem wir STRG+D gedrückt haben, erhalten wir: weicher reboot Automatisches Neuladen ist aktiv. Speichere Dateien über USB um sie auszuführen oder verbinde dich mit der REPL zum Deaktivieren. code.py Ausgabe: Zurückverfolgung (jüngste Aufforderung zuletzt): Datei "code.py", Zeile 1, in ImportError: Kein Modul mit dem Namen 'machine' Programm wird ausgeführt. Oha! Spätestens jetzt merken wir, dass es doch einige Unterschiede zwischen dem originalen MicroPython und dessen Fork (Ableger) CircuitPython gibt, die Adafruit übrigens hier erläutert.

Unter anderem steht dort auch, das Interrupts bei CircuitPython nicht gehen. Schade eigentlich, kann man damit doch schöner Button-Handler schreiben, ohne ewig in einer Endlosschleife abfragen zu müssen.

Und da steht auch der Grund für unsere Fehlermeldung: "No machine API". Die muss also irgendwie anders lauten. Wie was genau heißt, das ist natürlich in der CircuitPython-Dokumentation.

Also ändern wir die code.py ab. Das putty-Fenster lassen wir dabei auf. Und jedes mal, wenn wir unsere Code-Datei im Editor speichern, merkt das CircuitPython und startet das Programm neu. Das finde ich überaus praktisch. Man kann im Editor bleiben, speichert da nur und im Terminalfenster ploppen dann die Fehlermeldungen hoch. Blick drauf, Fehler suchen, korrigieren, einfach speichern und der nächste Test rennt.

Wie gesagt: "machine" gibt es hier nicht. Wir müssen stattdessen "digitalio" benutzen. Passen wir unser Programm an und speichern es.

import board import digitalio from time import sleep led = digitalio.DigitalInOut(board.GP25) led.direction = digitalio.Direction.OUTPUT while True: led.toggle() sleep(0.5) In Putty erscheint automatisch Automatisches Neuladen ist aktiv. Speichere Dateien über USB um sie auszuführen oder verbinde dich mit der REPL zum Deaktivieren. code.py Ausgabe: Zurückverfolgung (jüngste Aufforderung zuletzt): Datei "code.py", Zeile 9, in AttributeError: 'DigitalInOut' Objekt hat kein Attribut 'toggle' Programm wird ausgeführt. Okay, toggle() kennt CircuitPython auch nicht. Dann halt auf die altmodische Weise: import board import digitalio from time import sleep led = digitalio.DigitalInOut(board.GP25) led.direction = digitalio.Direction.OUTPUT while True: led.value = 1 sleep(0.5) led.value = 0 sleep(0.5) Jetzt gibt es keinen Fehler mehr und die LED blinkt in Endlosschleife. Endlosschleife heißt aber auch, dass es jetzt keinen automatischen Reload mehr gibt, wenn wir das Programm ändern und speichern. Ändern wir den sleep-Wert mal auf 0.25 und speichern. Es passiert erst einmal nichts, weil das alte Programm noch munter weiter läuft und die Kontrolle hat.

Um das zu ändern, können wir STRG-D in Putty drücken und dann nochmal speichern, was die Änderungen übernimmt. Oder wir führen einen Reset des Picos mit dem neu eingebauten Taster aus. Das trennt aber unsere Putty-Verbindung und sollte nur in Härtefällen benutzt werden, wenn STRG+D in Putty nicht funktioniert.

Das schöne ist jetzt, dass, wenn wir den Pico von USB abziehen, das Programm schon drauf ist und automatisch gestartet wird, wenn der Pico wieder Strom bekommt.

Ich finde, damit kann man schon ganz gut leben, wenn man einen halbwegs guten Editor hat, der auch ein bisschen Syntax-Highlighting kann.

Mu als Entwicklungsumgebung für CircuitPython

Adafruit, der Hersteller von CircuitPython empfiehlt auf seinen Seiten den Mu-Editor als Entwicklungsumgebung, den wir hier downloaden können. Es gibt auch eine Version für Windows 7 / 64bit.

Leider fragt die Installationsroutine nicht danach, wo das Programm installiert werden soll, sondern klatscht es einfach in den User-Appdata-Ordner auf dem C-Laufwerk. In Appdata gehören eigentlich nur Daten rein, wie der Ordnername schon vorgibt. Keine Programme. Doof, wenn C: nur eine kleine SSD ist, auf der eigentlich nur Windows laufen soll. Aber diese Installationsmarotte wird immer häufiger. Schafft es schon einmal eine größere C-SSD an. Aber das ist ein anderes Thema. Die Installation dauert eine Weile, was wohl mehr daran liegt, dass der Editor in Python geschrieben ist (da ist alles ein wenig gemächlicher).

Wenn der Mu dann endlich gestartet ist, bekommen wir eine Auswahlliste:



Hier wählen wir den Eintrag CircuitPython aus, denn wir wollen Programme dafür schreiben. Bevor wir auf OK klicken, sollten wir sicherstellen, dass unserer CircuitPi angeschlossen ist, sondern meckert Mu.

Danach finden wir uns im Mu-Editor wieder. Geben wir dort doch einmal unseren Blink-Script ein und klicken außerdem oben auf "Seriell". Danach klicken wir auf Speichern und wählen als Ziel unser CircuitPi-Laufwerk und darauf die Datei "code.py":



Auch wenn es nirgends steht: STRG+S zum speichern funktioniert auch. Dann müssen wir nicht jedes mal den Button drücken. Durch das Speichern wird wie vorhin das geänderte Programm neu gestartet. Ändern wir einmal die Zeiten für das Blinken, dann sehen wir den Unterschied.

Unsere Konsolen-Ausgaben haben wir jetzt direkt unter dem Editorfenster. Auf Putty können wir jetzt verzichten. Den COM-Port hat Mu von allein erkannt. Schön, besonders, wenn man den Pico mal wechseln und dann einen anderen Port haben sollte.

Mit "Hineinzoomen" und "Herauszoomen" kann man die Schriftgröße anpassen und ein Klick auf "Prüfen" führt eine lokale Syntax-Prüfung durch, also auf dem PC. Die ist äußerst praktisch, den zum einen ist sie schneller als das Speichern und zum zweiten sehr viel übersichtlicher:



Ich habe als Fehler mal eingebaut, "true" klein zu schreiben. Richtig muss es "True" lauten. Das sagt mir zwar auf die Konsole nach dem Speichern mit Datei "code.py", Zeile 12, in ; NameError: Name 'true' ist nirgends definiert worden (Schreibweise kontrollieren), aber durch "Prüfen" habe ich die Fehlerstelle gleich im Code markiert. Das erspart mir das Fehlermeldung suchen unten und das anschließende Zeilennummer suchen unten. Ich kann das gleich an Ort und Stelle korrigieren.

Außer dem "true" mäkelt Mu noch meine Einrückungen mit 2 Spaces an. Python hätte gerne 4 Spaces. Ich finde das ja eher unübersichtlich, besonders, wenn es um mehrfach verschachtelte Schleifen geht. Ich finde die ganze Idee mit dem Einrücken fragwürdig. Was war an den geschweiften Klammern in C falsch? Oder einem "If" und "Endif" in Basic und Co.? Da können sich so schnell Fehler einschleichen, die man dann ganz schnell übersieht. Aber sei's drum. Irgendwer hat man seine eigene Vorliebe für einen Abstand von 4 Spaces zementiert und daran müssen wir uns jetzt halten, auch wenn's gegen die eigene Überzeugung und Gewohnheit geht.

Um die Einrückungen zu korrigieren, können wir jetzt händisch Leerzeichen einfügen oder wir klicken einfach oben auf "Tidy". Dann werden die Einrückungen von Mu hoffentlich korrekt vollzogen. Der muss dann nämlich nach der logischen Syntax gehen und da die ja auch "nur" in Einrückungen liegt, kann da schon mal was durcheinander gehen, sich alle Einrückungen verschieben, ein heilloses Durcheinander entstehen, Schleifen früher oder später als gewollt beendet werden und man schaut am Ende nicht mehr durch den Code. Das noch so als kleinen Nachschlag zur "genialen" Idee mit den Einrückungen. Aber wie gesagt: anderes Thema. Man kann ja mit Kommentaren zusätzlich noch #Endif und #Endfor schreiben, um das für sich selbst sicherer zu gestalten. Einfacher macht es die Sache nicht.

Der Plotter in Mu

Wenn das Programm übrigens nach dem Speichern nicht automatisch neustartet, dann hilft nach wie vor ein STRG+D im seriellen Fenster für einen soft reset.

Den Plotter kennen wir ja schon von der Arduino IDE: Hier werden die Werte, die wir mit "print()" ausgeben in einer Skala angezeigt. Damit könnten wir doch schön unseren Temperaturverlauf anzeigen. Das wollen wir doch gleich mal mit folgendem Code ausprobieren: import board import digitalio import time from time import sleep import microcontroller while True: temp=microcontroller.cpu.temperature print( "(" + "{:.2f}".format(temp) + ")" ) sleep(2) CircuitPython enthält eine Funktion, die uns die Temperatur abnimmt: microcontroller.cpu.temperature. Das nimmt uns nur das Rechnen ab, die Genauigkeit der Temperaturmessung bleibt trotzdem unterirdisch.

Klicken wir noch auf dem "Plotter"-Button und erhalten eine grafische Kurve:



Bei dem Ausschlag nach oben in etwa der Mitte der Kurve habe ich den RP2040 warm-"gehaucht", um zu testen, ob der Sensor auch reagiert.

Um die Kurve zu erhalten, muss der per print() ausgegebene Wert zwingend in Klammern stehen. Das reicht aber auch schon, die Skalierung der Kurve macht Mu automatisch. Wollen wir mehrere Kurven, so geben wir sie in den Klammern durch Kommata getrennt an, zum Beispiel "(10, 20 30)", was dann drei Kurven ergibt.

Der Plotter ist gut, um sich verändernde Werte über eine gewisse Zeit zu beobachten. Natürlich kann man auch schnellere Ausgaben machen, dann läuft auch die Kurve schneller durch.

Dann können wir auch gleich mal testen, wie sich Mu mit dem Einbinden von Libraries / Paketen anstellt. Denn für unser OLED-Display brauchen wir doch eines, oder?

Pakete (Libraries) installieren in Mu / CircuitPython

Nur wo ist im Menü der Eintrag für den Paketmanager? Bei der Thonny-Entwicklungsumgebung konnten wir darin nach Bibliotheken suchen und haben so unsere OLED-Bibliothek gefunden. Hier unter Mu scheint es gar keinen Paketmanager zu geben? Wie kann das sein?

Okay, es ist eigentlich ganz einfach: Es gibt keinen Paketmanager. CircuitPython liefert alle gebräuchlichen Module, so heißen die Pakete hier, schon mit. Darum ist das Teil auch 1.4 MB groß. Erinnert ihr euch an die lange Modul-Liste von vorhin? Die installierten Module können wir uns mit dem Befehl help("modules") in der Konsole anzeigen lassen: Adafruit CircuitPython 7.1.0 on 2021-12-28; Raspberry Pi Pico with rp2040 >>> help("modules") __main__ board micropython storage _bleio builtins msgpack struct _eve busio neopixel_write supervisor adafruit_bus_device collections onewireio synthio adafruit_pixelbuf countio os sys aesio digitalio paralleldisplay terminalio alarm displayio pulseio time analogio errno pwmio touchio array fontio qrio traceback atexit framebufferio rainbowio ulab audiobusio gc random usb_cdc audiocore getpass re usb_hid audiomixer gifio rgbmatrix usb_midi audiomp3 imagecapture rotaryio uselect audiopwmio io rp2pio vectorio binascii json rtc watchdog bitbangio keypad sdcardio bitmaptools math select bitops microcontroller sharpdisplay und alle Module im Dateisystem Da sollte eigentlich so ziemlich alles dabei sein, was man braucht. Und wenn doch was fehlt, dann muss man die Library händisch zum Beispiel von Github downloaden und nach CircuitPi:\lib kopieren, um sie zu installieren.

Die Module können wir in der Konsole auch noch näher untersuchen: >>> dir (displayio) ['__class__', '__name__', 'Bitmap', 'ColorConverter', 'Colorspace', 'Display', 'EPaperDisplay', 'FourWire', 'Group', 'I2CDisplay', 'OnDiskBitmap', 'Palette', 'ParallelBus', 'Shape', 'TileGrid', 'release_displays'] >>> dir (displayio.I2CDisplay) ['__class__', '__name__', 'send', '__bases__', '__dict__', 'reset'] Die Syntaxvervollständigung von Mu macht uns das Leben nach der richtigen Library einfacher: wir tippen "import" und dann etwa "busio." auf der Suche nach dem I2C-Bustyp und bekommen:



Mit den Cursortasten suchen wir uns den richtigen Eintrag heraus und schließen die Auswahl mit Tab ab.

Tja, aber für unser OLED haben wir nichts dabei. Man sollte annehme, es wäre unter displayio zu finden, doch da gibt es kein entsprechendes Unterobjekt.

Darum müssen wir uns auf die Suche nach einer Library für unser SSD1306-128x64px-OLED machen. Erster Anlaufpunkt sollte circuitpython.org/libraries sein, dort finden wir den Download eines "adafruit-circuitpython-bundle-7.x-mpy-20220105.zip" oder ähnlich genannten Zip-Archivs mit allen Bibliotheken in gepackten 3.7 MB. Das laden wir runter und entpacken es lokal auf der Festplatte:

D:\Raspi-Pico\adafruit-circuitpython-bundle-7.x-mpy-20220105\lib>dir Datenträger in Laufwerk D: ist X10_DATEN1_(D)_INTERN Verzeichnis von D:\Raspi-Pico\adafruit-circuitpython-bundle-7.x-mpy-20220105\lib 05.01.2022 15:01 <DIR> . 05.01.2022 15:01 <DIR> .. 05.01.2022 05:09 1.383 adafruit_74hc595.mpy 05.01.2022 15:01 <DIR> adafruit_ads1x15 05.01.2022 05:09 1.808 adafruit_adt7410.mpy 05.01.2022 05:09 2.789 adafruit_adxl34x.mpy ... 05.01.2022 05:09 4.599 adafruit_slideshow.mpy 05.01.2022 05:09 2.520 adafruit_ssd1305.mpy 05.01.2022 05:09 2.846 adafruit_ssd1306.mpy 05.01.2022 05:09 576 adafruit_ssd1322.mpy 05.01.2022 05:09 538 adafruit_ssd1325.mpy ... 05.01.2022 15:01 <DIR> adafruit_waveform 05.01.2022 15:01 <DIR> adafruit_wiznet5k 05.01.2022 05:09 2.003 adafruit_ws2801.mpy 05.01.2022 15:01 <DIR> adafruit_wsgi 05.01.2022 15:01 <DIR> asyncio 05.01.2022 05:09 811 colorsys.mpy 05.01.2022 05:09 1.401 neopixel.mpy 05.01.2022 05:09 1.187 neopixel_spi.mpy 05.01.2022 05:09 1.761 simpleio.mpy 213 Datei(en), 553.510 Bytes 78 Verzeichnis(se), 187.558.301.696 Bytes frei Von hier aus müssen wir jetzt die Datei "adafruit_displayio_ssd1306.mpy" und das Verzeichnis "adafruit_display_text" auf unser CIRCUITPY-Laufwerk in den Ordner "lib" kopieren, um diese zu installieren und im Code darauf zugreifen zu können. Alle Bibliotheken kopieren würde nicht gehen, soviel Platz ist gar nicht auf dem CIRCUITPY-Laufwerk. Wir müssen schon die notwendigen, und nur die notwendigen raussuchen.

Unser lib-Ordner sollte dann so aussehen: Datenträger in Laufwerk J: ist CIRCUITPY Volumeseriennummer: 5021-0000 Verzeichnis von J:\lib 01.01.2020 00:00 <DIR> . 01.01.2020 00:00 <DIR> .. 05.01.2022 05:09 750 adafruit_displayio_ssd1306.mpy 05.01.2022 15:28 <DIR> adafruit_display_text 1 Datei(en), 750 Bytes 3 Verzeichnis(se), 1.010.176 Bytes frei Eine Auflistung der im Download enthaltenen Libraries gibt es übrigens auch. Dort gibt es auch Links zu den jeweiligen Dokumentationen und kleine Codebeispiele. Wobei wir natürlich auch die Code-Beispiele im "examples"-Ordner in unserem Download benutzen können. Es gibt auf der Circuit-Libraries-Seite auch noch einen Examples-Download. Den kann man sich aber schenken, weil das nochmal das selbe ohne Lib-Files wie in unserem Download ist.

Interessant ist erst wieder die API bei circuitpython.readthedocs.io. Hier steht ganz kurz, wie man die Library richtig anspricht. Und außerdem gibt es hier Links auf Beispiele und weitere Dokumentationen.

Ich persönlich finde die verteilte und meist nirgends so richtig vollständige Dokumentation nervig, auch weil hier teilweise Entwicklercontent mit Verkaufscontent gemischt ist und oft das Anpreisen von den Adafruit-gelabelten Hardware-Bauteilen ablenkt.

Besser ist es dann vielleicht, sich ein Beispiel von einem unabhängigen Hobby-Maker zu suchen und sich daraus den Code zusammenzusuchen und anzupassen. Problem dabei ist natürlich, dass man zig Beispiele für Standard-Anwendungen findet, die alle den Code aus den Examples kopiert haben.

Bei mir blieben zum Beispiel die Fragen offen, wie ich a) Helligkeit bzw. Kontrast des OLED einstelle, um dem Burn-in bzw. Burn-out Problem bei OLED Displays gleich von Anfang an ein wenig entgegenzuwirken. Und b) den Splash-Screen mit der kleinen Schlange oben links loswerde. Selbst wenn ich sofort etwas auf das OLED schreibe, sieht das Geflacker vorher unschön aus.

Aber von adafruit kenne ich das schon ein bisschen, dass die gerne überall ihren Firmennamen aufdrucken oder ausgeben. Aber ich finde, das muss zumindest auch abschaltbar sein. Außerdem sind mitgeschleppte Adafruit-Logos, die keiner anzeigen will, nur verschwendeter Speicherplatz. Und der ist bei Mikrocontrollern immer knapp.

An CircuitPython angepasste Temperaturanzeige

Ich schaue mir ein paar Beispielprogramme an und bastel mir damit den folgenden Code zusammen: # (C) 2022 by Oliver Kuhlemann # Bei Verwendung freue ich mich um Namensnennung, # Quellenangabe und Verlinkung # Quelle: http://cool-web.de/raspberry/ import board import digitalio from time import sleep import displayio import adafruit_displayio_ssd1306 import terminalio from adafruit_display_text import label import busio import microcontroller led = digitalio.DigitalInOut(board.GP25) led.direction = digitalio.Direction.OUTPUT displayio.release_displays() i2c = busio.I2C(scl=board.GP17, sda=board.GP16, frequency=400000) display_bus = displayio.I2CDisplay(i2c, device_address=0x3C) display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=128, height=64) display.brightness=0.0 # 0.0..1.0 while True: led.value = not led.value temp = microcontroller.cpu.temperature print("(" + "{:.2f}".format(temp) + ")") grp = displayio.Group(scale=2, x=0, y=10) lbl = label.Label( terminalio.FONT, text="Temperatur:\n" + "{:.2f}".format(temp) + " C", color=0xFFFFFF ) grp.append(lbl) display.show(grp) sleep(2) Jetzt wird die Temperatur alle 2 Sekunden aktualisiert auf dem OLED-Display ausgegeben und über die serielle Schnittstelle ausgegeben, so dass sie auch im Mu-Plotter anzeigbar ist. Und außerdem blinkt die interne LED des Pico im 2 Sekunden-Takt, damit man sieht, dass das Programm noch läuft, auch wenn sich die Temperatur nicht ändert.

Der Code ist komplizierter als bei MicroPython und Thonny und es hat mich mehr Zeit gekostet, den richtigen Code zusammenzutragen. Okay, die adafruit-Library kann auch mehr: verschiedene Fonts, verschiedene Schriftgrößen, Grafik und so weiter, aber das macht es auch ein bisschen umständlich. Erst braucht man eine Gruppe (grp), dann Anzeige-Objekte wie Label (lbl), die man erzeugen und dann der Gruppe hinzufügen muss, um die Gruppe schließlich anzuzeigen.

Auch wenn die Library scheinbar einiges kann, finde ich sie nicht richtig dokumentiert und mir fehlen die Funktionen für Helligkeit / Kontrast und Non-Splash-Screen. Die erwarte ich schon, wenn ich mir derart in eine Lib einarbeiten muss, weil sie anscheinend so umfangreich ist. Die Helligkeit über die die Farbe zu regeln, geht auch nicht. Alles unter #808080 ist schwarz, alles ab #808080 ist weiß. Sehr sinnig. Nicht.

Und sich durch den Source-Code der Lib bei Github durchzuwühlen, um zu schauen, was sie genau kann, kann nicht der richtige Weg sein. Dann kann ich mir auch gleich das SSD1306-Data Sheet schnappen und eine eigene Lib schreiben, die dann genau so funktioniert, wie ich es will.

Schließlich habe ich doch noch eine nicht oder nur schlecht dokumentierte Möglichkeit zur Kontrastregelung gefunden: Mit display.brightness=x kann man die Helligkeit in drei Stufen regeln, wobei x=0.0 am dunkelsten und x=1.0 hellsten ist. Das zu finden war aber eher anstrengend.

Der Aufbau der Schaltung ist genau der Gleiche wie beim letzten mal:



Fazit zu CircuitPython und Mu

Das Programmieren mit CircuitPython und dem Mu-Editor ist gegenüber MicroPython und Thonny schon einfacher. Die Probleme mit der instabilen seriellen Schnittstelle bei Thonny waren schon sehr nervig, da ist das mit dem Upload über das CircuitPy-Laufwerk sehr viel besser und stabiler gelöst.

Auch gibt es eine Syntax-Vervollständigung, die zwar nicht perfekt, aber wenigstens vorhanden ist. Auch das lokale "Prüfen" kann sinnvoll sein. Allerdings ist der Warning-Level viel zu niederschwellig eingestellt und nicht änderbar. Mich interessiert wirklich nicht, ob es "schöner aussieht", wenn vor und nach einem "=" ein Leerzeichen ist und zwischen einem Funktionsnamen und der ersten Klammer keines. Hier Fehler anzukreiden macht das "Prüfen" total unübersichtlich und ziemlich unbrauchbar. Einstellungen gibt es sowieso unter Mu nur sehr sehr wenige. Der Dark-Mode war bei mir schlecht lesbar. Liegt wohl an der fehlenden Schriftkantenglättung unter Windows.

Was ich am meisten vermisst habe ist der Paketmanager und auch scheint die Dokumentation nicht so gut zu sein wie unter MicroPython. Zwar hat man hier mächtigere Libraries, aber die sind kompliziert zu installieren und eher schlecht dokumentiert.

Ich würde sagen, CircuitPython ist etwas für Fortgeschrittene, während MicroPython noch einfacher und besser für Anfänger geeignet ist.

Was mich ja heiß auf CircuitPython gemacht hat, ist, dass dieses Tastatur und Maus (und vielleicht auch Gamepad?) über USB emulieren kann, was ich so ein bisschen als Alleinstellungsmerkmal des Raspberry Pi Pico sehe.

Im nächsten Teil will ich mal herausfinden, was sich damit machen lässt.