ESP32/ESP8266: Von 100 zu 10000 Zeilen Code

Die ersten Zeilen Code waren schnell geschrieben und liefen in der Arduino IDE. Doch bei der einzigen Methode void testLedStrip() sollte es nicht unbedingt bleiben. Und bereits diese allererste Version behrrschte das Art-Net-Protokoll. Doch schnell wurde es deutlich mehr Code…

Handbremse lösen hilft

Als eine der wichtigsten Veränderungen sehe ich rückblickend den Wechsel auf PlatformIO in VSCode von der Arduino IDE. Die Arduino IDE hat durchaus ihre Berechtigung, jedoch ist die Java Basis ein solcher Nervfaktor bei der Geschwindigkeit bestimmter Operationen, dass man schon viel Geduld aufbringen muss. VSCode + PlatformIO hat einige sehr wichtige Vorteile gebracht:

  • Code Completion
  • Macros
  • Übersichtliches File Handling
  • Konfigurierbare Build-Targets
  • Perfekte Suchen/Ersetzen-Funktionen für Refactoring
  • Pre-Compiler checks

Um nur einige zu nennen. VSCode ist im Prinzip mehr der Atom Editor und hat mit Visual Studio wenig zu tun. Das PlatformIO Plugin rüstet den Editor auf zu einer IDE. Das ist durchaus verblüffend, wie gut das funktioniert.

Sehr hilfreich ist vor allem die Peek-Funktion in den Code der hinter bestehenden Funktionsaufrufen steht. Gerade wenn diese Funktionsaufrufe z.B. im Code eines Frameworks liegen oder in Code den man länger nicht angefasst hat, ist es superhilfreich ein kleines Snippet von dem betroffenen Code mal eben sehen zu können. Ruck Zuck weiß man wieder was das Ding eigentlich tut.

Auch die breite Miniatur-Vorschau-Scrollleiste ist einfach megapraktisch. Weil man sich gerade bei langen Codefiles optisch perfekt orientieren kann.

Ab und an muss man VSCode dann zwar mal neustarten, wenn es zu langsam wird, aber das ist vertretbar, ich hatte keinen einzigen Crash der App und keine Datenverluste beim Coden damit.

C/C++

Etwas kann einem die IDE nicht abnehmen, das Wissen um C/C++ ist nicht mal eben auf Abruf erhältlich. Und hier bin ich im Learning-by-Doing, schnell in viele kleine Fallen gerannt. Von den speziellen Dingen rund um Scopes bis hin zu Pointern und Objektorientierung C-Style, hab ich alles berührt was nötig war, um stabilen Code zu schreiben.

Eine der wichtigsten Erkenntnisse für mich war, dass diese ganzen Dateien die man da so anlegt, letztlich nur Strukturierungshilfen für Menschen sind, dem Compiler ist es völlig schnurz, denn behandelt wird all das lustig geschriebene als wäre es EINE große DATEI. Und daher ergeben sich auch manche Probleme die man nicht erkennen kann, wenn man nicht auf diese Metaebene steigt und sieht, dass alle Dateien eigentlich in eine große Datei einfliessen, die für den Compiler die Wahrheit abbildet.

Von Variablen die angeblich schon deklariert wurden, bis hin zu fehlenden Deklarationen und doppelten imports sowie weitergereichten Pointern, die beim Verlassen des Scope ungültig wurden, habe ich alle Fehler gemacht, die man sich so vorstellen kann.

Aber es hat mich dennoch nicht vom Ziel abgebracht und mit einigem Google-Aufwand habe ich mich soweit fortgebildet dass mir die Arbeitsweise mit dieser Sprache immer vertrauter wurde. Am Ende habe ich sogar Bit-Shifting gemacht, um das Art-Net-Protokoll für soegenannte Art-Poll-Requests zu befähigen. Bits zählen ist so mit das Langweiligste was man tun kann, und sehr fehleranfällig noch dazu.

Mein wichtigstes Debugging Werkzeug war

Serial.println( "DEBUG: I AM HERE." )

das habe ich dann später ausgebaut zu ein paar Macros, die ich über des BUILD-Flag DEBUG_LAM dann komplett abschalten konnte.

    #ifdef DEBUG_LAM
    #define LOG( x )  Serial.print( x )
    #define LOG_ENC( x, y )  Serial.print( x, y )
    #define LOG_LN( x )  Serial.println( x )
    #define LOG_LN_ENC( x, y )  Serial.println( x, y )
    #else
    #define LOG( x )
    #define LOG_ENC( x, y )    
    #define LOG_LN(x)
    #define LOG_LN_ENC( x, y )
    #endif

Später habe ich dann begonnen auch Stackdumps zu dekodieren. Aber da war die Software eigentlich schon fertig.

Features: OTA Updates, WebServer, Art-Net, Drucktaster, Konfiguration

Nachdem die ersten LED’s am leuchten waren, wollte ich natürlich Effekte. Also wurde ein paar mal die LED-Routine die den Streifen befeuert umgeändert und erweitert. Doch das reichte irgendwann nicht mehr, denn die Anzahl der Effekte nahm zu. Und ich wollte gerne wechseln können zwischen den Effekten. Auch ein einfacher Ein-Aus-Schalter wäre nützlich.

Also begann ich zunächst zwei PINS mit Drucktastern zu verbinden und diese Taster mit einen PULL-UP-Widerstand zu verdrahten, der beim Drücken der Taste volles Pegelsignal liefert. Hier war tatsächlich die Vorarbeit im Arduinokurs zu Ampelschaltungen mit Grünanforderung extrem nützlich. Denn ich wusste dass ich einen PULL-UP oder PULL-DOWN Widerstand in der Schaltung brauchte für ein gutes Signalhandling.

WebServer zur Bedienung

Relativ schnell kam dann der Wunsch auf, nicht nur einen Effekt zu haben, sondern eine Art Benutzeroberfläche / UI für die Bedienung der „Lampe“. Das An-/Ausschalten und auch z.B. die Helligkeit zu regeln. Durch Delphino hab ich dann gelernt, dass es einen kleinen WebServer für Microcontroller gibt, den ESPAsyncWebServer.

Nach ein wenig Rumprobieren, hatte ich den Server am Laufen und konnte zumindest rudimentär meine Lampe steuern. Das war schon ein beeindruckender erster Moment, das sowas auch mit einem Microcontroller geht. Es war nicht sonderlich schnell aber es funktionierte mit einem simplen Webbrowser und ich konnte damit die die Lampe selbst An- und Ausschalten, Art-Net Support An- und Ausschalten, die Helligkeit regeln, und den Controller Neustarten. Das waren so die wichtigsten Sachen die mir erstmal einfielen, die ich haben wollte.

Die aktuelle Ausbaustufe der Webseite sieht jedoch mittlerweile deutlich umfangreicher aus. Die gesamte Web UI ist mittlerweile optimiert darauf, nicht nur auf dem Desktop, sondern vor allem auf mobile Devices perfekt als Progressive Web Application (PWA) zu funktionieren, also möglichst den Eindruck einer eigenständigen App zu erzeugen.

Durch einige ergänzte <meta>-Tags und ein paar Icon-Dateien konnte ich für iOS und Android ein App-Erscheinungsbild realisieren, das auf beiden Plattformen eine schöne Bedienung zulässt und Home-Screen-Shortcuts erlaubt. Nachstehend ein Foto von der PWA auf einem alten iPod.

Webseiten in Code bauen?

Die erste Webseite die man da weiter oben im Screenshot sieht, hatte ich komplett in Code zusammengesetzt, inkl. des CSS. Das ist eine ziemlich krass Speicherfressende Methode wie ich schnell feststellte. Den gerade das Verketten von Strings sorgt schnell für Speicherfragmentierung und Stackoverflows. Ein Ausweg: Das Flash Filesystem (FS).

Mit Hilfe des SPIFFS Framework, aktivierte ich ein Filesystem auf dem Microcontroller, das es erlaubt Dateien auf dem Controller zu speichern und abzurufen.

SPIFFS steht für (S)erial (P)eripheral (I)nterface (F)lash (F)ile (S)ystem und gemeint ist dabei, dass unser ESP im SPI Programmspeicher, der auch unseren Programmcode enthält, ein einfaches Dateisystem halten kann. — Quelle

Ab jetzt musste der WebServer „nur noch“ Dateien ausliefern. Und ich konnte deutlich schneller meine Webseiten bauen, das CSS anpassen. Es dauerte nicht sehr lange und ich stellte fest, dass jeder TcpRequest der den WebServer traf ganz schön die CPU belastet. Trotz dass der WebServer auf dem zweiten Kern läuft, wird das Rendering der Lampen beeinflusst und andere Tasks scheinbar solange angehalten.

Daher war das Neuladen einer Webseite nach jedem Klick überhaupt keine gute Idee. Es musste eine leichtgewichtige Kommunikation musste her. Als Mittel der Wahl entschied ich mich für Asynchrone Requests per JavaScript. Das war dann auch der Startschuss, ein API zu definieren, denn ich wollte ja keine ganze Webseite mehr ausliefern, sondern am liebsten nur noch kleine JSON-Datenpakete.

Es folgte ein massiver Aufbau einer API (siehe Screenshot). Und mit jedem neuen API Endpunkt konnte die Bedienungs-UI komplexer und umfangreicher werden. Neue Features waren jetzt mehr eine Frage der Nützlichkeit, denn per API hätte ich jetzt alles einbauen können.

Ich hab mir gleich auch doe Mühe gemacht, das API auf dem Controller selbst mit einer HTML-Datei zu dokumentieren. So ist auch for andere ein schneller Lookup möglich.

Remote Fernbedienung? Remote Firmware Update!

Hat man sich erstmal dran gewöhnt, dass man die Lampe fernsteuern kann per Browser über das Wifi, kommt schnell der Wunsch auf mal eben ein Firmwareupdate auf den Controller zu spielen OHNE dass man ihn wieder per USB an den Rechner hängt.

Und das geht tatsächlich megaeinfach. Es nennt sich Over the Air (OTA) Update und wird von den Frameworks unterstützt. Mit einem PlatformIO CLI Command kann man das Update over the Air wie folgt starten:

platformio run -e BUILD_ESP8266_OTA --target upload --upload-port LAM-ESP8266.local

Wichtig ist hier die Angabe der Buildenvironment (hier BUILD_ESP8266_OTA) und des Upload Port (hier LAM-ESP8266.local der mDNS-Hostname den ich für den Microcontroller festgelegt habe; man kann aber auch die IP-Adresse direkt angeben).

Mit in folgender Zeile definiertem Handler im Code legt man z.B. fest, was passieren soll bei Beginn eines Firmwareupdates over the Air:

ArduinoOTA.onStart( []() {

// Code zur Behandlung dieses Ereignisses hier einfügen

});

Das besonders Praktische an einem Update over the Air ist, dass man kein USB-Kabel mehr zum Microcontroller braucht. Und der Controller muss sich lediglich per Wifi im selben Netz befinden, um ihn zu erreichen für ein Update. Es gibt natürlich die Möglichkeit das Update auch mit einem Passwort abzusichern und nur authentifizierte Updates zuzulassen.

Tatsächlich geht ein Update over the Air beim ESP8266 sogar schneller als per USB. Und es ist eben auch ein Weg einen Controller zu erreichen, der vielleicht sogar bereits in einem Gehäuse oder schlecht erreichbar hinter einer Wand montiert ist.

Zwischenfazit

Es ist schlichtweg beeindruckend, was man mit diesem Microcontroller alles tun kann. Allerdings profitiert man deutlich von genügend Vorkenntnissen die man mitbringt. Bei mir waren das Vorkenntnisse in folgenden Bereichen:

  • C (vor allem Macros, Debugging Strategien, Library Management)
  • HTML (wie schaut eine gültige Webseite aus)
  • CSS (wie styled man eine Seite gezielt für hübsches UI, siehe cssdiner.com) bzw. https://flukeout.github.io/
  • Javascript (für eine API-basierte Application)
  • API (wie designe ich eine RESTful API)
  • Arduino Grundlagen (wie kann ich Hardware prototypen mit dem Breadboard)
  • WebServer/TCP/UDP (Grundkenntnisse über Netzwerktraffic, Requesthandling)
  • Grafikbearbeitung (um nötige Icons in benötigter Größe selbst zu erstellen)
  • Browser Dev Tools (Umgang mit der Webconsole des Browsers, oder z.B. Postman als HTTP Client)

Why do I blog this? Der Schritt von einer Lights.ino in der Arduino IDE hin zu einer main.cpp in PlatformIO und dann zu einem Projektordner /src voller Files die sich sauber um einzelne Teile/Features kümmern, war nicht so einfach. Ich hab mindestens zwei größere Refactorings durchgeführt.

Aber es lohnt sich. Belohnt wird man mit einer sauberen Code-Struktur, einer tadellos funktionierenden RESTful API inkl. Dokumentation und einer effizienten Web-Application die auf nahezu allen Browsern Mobile und Desktop reibungslos funktioniert.

Demnächst dann eine Vorstellung des „fertigen“ Systems, sofern so ein Teil jemals fertig sein kann. Denn eines sei verraten, bei einer simplen Lampe hab ich es nicht belassen. Der LED-Strip beherrscht mittlerweile Apps genauso wie Audioausgaben und eine Art-Net-Schnittstelle über die er durch Art-Net bzw. DMX-over-IP prima durch Drittanbieter Apps gesteuert werden kann..

Schreibe einen Kommentar

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

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.