0xfefe: Danke für Nichts (Post mortem)

Der eine oder andere hat vielleicht schonmal von Fefe’s Blog gehört. Das ist eine Blog-Installation, die durch die Person Felix von Leitner mit regelmäßigen Inhalten gefüllt wird. Ich weiß nicht wann ich zum ersten Mal mit dem Blog in Berührung gekommen bin, aber das muss so um den 27c3 herum gewesen sein. Ich bin zum ersten Mal mit Inhalten von Fefe konfrontiert worden im Rahmen der Fnord News Show bzw. der Fnord Jahresrückblick 2009 auf dem Chaos Communication Congress 26c3 (mein erster Congress).

Als ich zum Zeitpunkt des 27c3 ein Jahr später Langeweile hatte, begann ich während wir mit einigen CCC’lern dem 27c3 per Videostream beiwohnten, eine kleine iOS App zu basteln, die Inhalte von Fefe’s Blog per RSS-Feed abholen und anzeigen sollte. Die erste primitive Version funktionierte bereits gegen Ende des 27c3 auf meinem Development iDevice, damals noch ein iPhone 3GS.

Nachdem die App einigermaßen stabil lief, packte ich die einfach kostenlos in den AppStore. Zu dem Zeitpunkt war ich ein eher schlechter iOS Developer (weil noch Anfänger in Sachen iOS) und ich habe iOS an einigen Stellen absolut nicht verstanden. Die App lief zwar, aber sie hatte einige extrem CPU-ineffiziente Stellen, da ich ein paar fancy Animationen haben wollte und die Renderingmechanismen bis zur Unkenntlichkeit falsch benutzt habe.

Die Fefe App (2010)

Hier ein Rückblick auf die erste Version der App die so um 2012 existierte und durchaus eine recht lustige User Experience bot durch die fancy Animationen:

Launch Screen Postingübersicht Posting mit Kommentaren Farbschema grün Farbschema Cyan

All diese runden Gebilde drehte sich in sich selbst und es gab ein herrlich dynamisches Gesamtbild. Man beachte, dass es zu dieser Zeit Kommentare zu Fefe’s Blog gab über eine nicht zum Blog selbst gehörende URL als Service refefe (http://blog.refefe.de/rss.xml). Das heisst Leute konnten da extern kommentieren und als ich das mitbekommen hatte, hab ich die Kommentare natürlich ebenfalls in der App angezeigt.

Aufgebaut hatte den re:Fefe-Service (vermutlich auch aus blossem Spass an der Freud) Linus Neumann. In 2013 hielt Linus zusammen mit anderen dazu einen recht empfehlenswerten und kurzweiligen Vortrag mit dem Titel „Die Trolldrossel (Erkenntnisse der empirischen Trollforschung)“ (hier bei Youtube).

Später bekam Linus für diese Aktion noch eine Abmahnung von Fefe’s Anwalt (zumindest sah das für Außenstehende so aus) das Schreiben (Seite 1, Seite 2, Seite 3) schaut zunächst ernstgemeint aus, aber das Veröffentlichungsdatum 1. April sollte bereits stutzig machen.

Und so berechtigt war es dann auch die Echtheit zu bezweifeln. Die Aufklärung zu dem Schreiben wurde auf der Anwaltsseite selbst veröffentlicht mit dem Wortlaut:

[…] Nachdem die Trollkommentar-Datenbank bemerkenswerte Ausmaße annahm und damit repräsentative Auswertung erlaubte, war es an der Zeit, das re Fefe-Blog würdig zu beenden.

Ein bloßes Abschalten hätten jedoch die Trolle nicht verstanden und als Zensur bewertet. Zudem konnte niemand voraussagen, wie die Trolle auf einen kalten Entzug reagieren würden. Um sich aus der Schusslinie zu nehmen, bat mich Linus um einen anwaltlichen False Flag-Angriff in Form einer 1.April-Abmahnung, die gegenüber den Trollen das Abschalten „erklärte“. Fefe war natürlich eingeweiht und einverstanden. […]

Der ReFefe-Service und die Kommentare wurden dann im April 2014 eingestellt mit Hinweis auf das Fake-Anwaltsschreiben mit dem folgenden Hinweis:

Da mir rechtliche Konsequenzen drohen, muss ich diese Seite leider schließen.
Mein Anwalt rät mir, mich nicht zum laufenden Verfahren zu äußern.
Ich hoffe ihr habt dafür Verständnis.

Mich hat damals – als ich begann Fefe’s Inhalte zu besuchen – denke ich die reine Sensationslust zu diesem Blog getrieben. Ich fand die Art und Weise wie dort polarisiert wurde vermutlich anziehend. Heute würde ich sagen, es war viel mehr mein Ego das von diesem Blog angezogen wurde. Das Ego, das nach einfachen Lösungen nach klaren Bewertungen, nach Empörung gesucht hat, um sich sein eigenes vielfach negatives Weltbild zu verstärken. Ich war unwissend. Mein Blick war verstellt. Mein Unbewusstsein hat es geschehen lassen, dass ich für dieses Blog eine App erstellt habe, die möglicherweise sogar noch zu dessen Popularität beigetragen hat (ebenso wie übrigens Re:Fefe).

Primär hatte mich eigentlich nur interessiert, iOS Aps zu entwickeln und die Möglichkeiten des iDevice auszureizen. Ich wollte mich optisch mit dem Userinterface alternativ ausprobieren und zugleich ein wenig mit CoreGraphics und CoreAnimation – zwei wichtigen iOS Frameworks – Spass haben. Rückblickend bedauere ich es sehr, für dieses Blog eine App entwickelt zu haben.

Nunja die Zeit ging ins Land, da die App funktionierte, bekam sie lange Zeit kein Update. iOS 7 brachte dann den großen UI-Style-Wechsel in UIKit und es war notwendig alle Apps anzupassen. Da ich eine Menge eigener Apps anpassen musste, nahm ich das zum Anlass eine zweite Version der App zu entwickeln und die Fefe App damit ebenso an den neuen Stil anzupassen. Diesmal, um meine Learnings im iOS Development einzubringen und das UI deutlich effizienter und performanter zu bauen. Für mich war es wieder eher die technische Challenge die mich erneut dazu trieb die App zu erweitern.

Die 0xfefe App (2014)

Es entstand die 0xfefe App, die App im Store wurde also auch umbenannt von Fefe in 0xfefe. Ziel der neuen Version war zugleich, die App nicht mehr ausschließlich an Fefe’s Blog zu koppeln. Stattdessen sollte die App beliebige RSS-Feeds als Quelle nutzen können. Diese App war daher in der Lage, jede beliebige RSS-Feed-URL als Konfiguration zu verwenden und den Feed dahinter anzuzeigen. Das ließ sich über die iOS Settings konfigurieren und war auch meine technische Rückversicherung, falls Apple die App wegen der Inhalte ablehnen sollte. Wär die App abgelehnt worden, hätte ich einfach einen anderen RSS-Feed integriert und den Nutzern einen Hinweis eingeblendet, wie sie ihr Lieblingsblog selbst konfigurieren könnten.

In der Praxis, haben allerdings lange nicht alle RSS-Feeds so gut mit der App funktioniert, wie der Feed von Fefe’s Blog, was daran lag, dass viele der RSS-Feeds da draussen im Web keine Full-Content-Feeds sind. Ich selbst hab nur während der Entwicklung andere Feeds konfiguriert, um zu testen, ob alles damit klar geht. Im täglichen Betrieb hatte ich nie eine andere URL konfiguriert.

Die zweite Version der App war letztlich ein weitreichender Rewrite. Das gesamte UI wurde neu geschrieben und auch um eine SoundEngine mit vielen lustigen Sounds bereichert. Es gab Favoriten und Statistiken und umfangreiche Konfigurationsoptionen. Viele neue Farbschemata kamen hinzu (inklusive eines Tageslicht-Scheme; das früh die Dark-/Light-Theme Features die heute normal sind vorwegnahm), und die fancy Animationen der Post-Entry-Buttons wurden auf CoreAnimation-konforme Technik umgestellt, die nur einen winzigen Bruchteil der vorherigen Performance schluckte.

Wem einige der App Sounds bekannt vorkamen, der hat vielleicht bemerkt, dass ich einige Sounds aus einer Flash-Datei extrahiert hatte, die damals von Kim Schmitz (alias Kimble) auf seiner Webseite kimvestor abrufbar war. Ich fand die Sounds so schön futuristisch und da es mir ja lediglich um ein Spassprojekt ging, hab ich da auch beherzt reverse-engineered, um die Sounds aus der kimvestor-flash-Datei zu extrahieren.

Hier ein Überblick über die App Version 2:

Postingübersicht Posting Favoriten Statistik Einstellungen

Wie man in den Screenshots sehen kann, kamen eine Menge nützlicher Features hinzu. Unter anderem gab es jetzt einen Favoriten-Speicher. Jeder favorisierte Post wurde auf dem iPhone in der App persistent gespeichert und war Volltext-durchsuchbar und -exportierbar. Das war durchaus nützlich, wenn man mal einen Post von Fefe auf die Schnelle gesucht hat.

Ein weiteres schönes Feature, was gar nicht so trivial zu implementieren war, war die Statistik. Die App hat auf Wunsch eine Statistik über Verweilzeiten und Abrufzahlen geführt und ausschließlich lokal gespeichert (da wurde auch nie irgendwas aus der App jemals „nach Hause telefoniert“). Diese Statistik wurde dann fein in einem Tortendiagramm animiert aufbereitet. Es kann zuweilen erschrecken wenn einem die Statistik belegt, wieviel Lebenszeit man der App gewidmet hat. Ich werde meine persönliche Nutzungsstatistik weiter unten in meinem Fazit veröffentlichen.

Das Ende der App (2019)

Das Jahresende 2019 sollte dann das finale Ende der App einläuten (zumindest für alle anderen außer mir). Mitbekommen sollte ich das nur indirekt… über einen Post in Fefe’s Blog (den ich hier mal zitiere):

Einige Leser haben mich darauf hingewiesen, dass irgendeine ominöse App-Klitsche gegen Geld auf Android und Apple eine Fefe-App anbietet.

Nein, die App kommt nicht von mir. Nein, die haben vorher nicht gefragt. Nein, ich finde das nicht gut.

Sollte ich dagegen vorgehen? Gute Frage. Muss ich mal drüber nachdenken.

Der Beißreflex ist natürlich da. Auf der anderen Seite verkaufen die kein Abo, und natürlich hat die App nichts, was man nicht auch im Browser hat.

Ich sähe ehrlich gesagt akuteren Handlungsbedarf, wenn jemand eine App mit meinen Inhalten anbietet, die dann Werbung einblendet.

Auf der anderen Seite besteht natürlich die Gefahr, dass ich Nachahmer einlade, wenn ich da jetzt nicht verbrannte Erde hinterlasse.

Alles nicht so einfach. […]

Ein wenig Recherche ergab dann auch, warum es da plötzlich eine solche Interruption gab, denn auf Twitter wurde ich recht schnell fündig:

Ich möchte auf den Post den Fefe dazu schrieb gar nicht mehr groß eingehen (man soll Vergangenes ruhen lassen), aber es ist schlicht nicht wahr, dass Fefe nicht von der App wusste. Denn ich hatte ihm sogar am 2. Januar 2015 im Nachgang des Congress einen echten Brief (so richtig auf Papier) geschrieben an die Adresse die im Impressum steht:

Fefes Blog
c/o Raumfahrtagentur
Gerichtsstrasse 66
D-13347 Berlin

Hier ein Auszug aus dem Brief der an Fefe ging…

Wer sich fragt um welche Sticker es da in dem Brief geht… hier eine Übersicht:

Diese Sticker hatte ich für den Congress in 2014 erstellt und in den Stickerboxen des Congress auch unter die Teilnehmer gebracht. Ich fand das Logo so hübsch, dass ich da paar Sticker von haben wollte.

Persönliches Fazit

Es war von Beginn an ein Fehler mit einer App Fefe’s Blog noch bekannter zu machen und den Zugang dazu auf einem iOS Gerät komfortabel über eine App kostenlos zu ermöglichen. Denn die Inhalte sind meiner Ansicht nach polarisierend, vereinfachend und überwiegend negativer Natur. Sie propagieren eine negative Weltsicht und füttern die empörungssüchtigen Egos der Besucher mittels Zynismus und Schadenfreude. Das lenkt ab von den eigentlich konstruktiven Möglichkeiten sich in der Welt positiv einzubringen. Daher rate ich vom Lesen von Fefe denjenigen ab, die nicht ihre negative Weltsicht (die ihnen ihr Verstand in Form des eigenen Ego präsentiert) weiter verstärken möchten.

Die App war übrigens fast immer kostenlos, bis auf eine Ausnahme, bei der ich Spam-Kommentare durch einen Minimalpreis von 0,79€ eine Zeit lang ausschließen wollte. Aber auch das hab ich dann später gelassen. Auch ein In-App-Purchase gab es mal, aber das war mehr, um IAP auszuprobieren und wurde ebenfalls eingestellt. Leider listet der AppStore einmal angelegte IAP’s für immer und ewig. Mein Pech.

Wer denkt ich hätte damit Geld verdient… der mache sich bitte sein eigenes Bild aus den Umsatz/Gewinn-Zahlen die Apple hier für den Zeitraum 2015 bis heute anzeigt:

Muss enttäuschend sein, wenn man erkennen muss, dass lediglich ca. 1000 Downloads von der App existierten und damit genau 0$ Umsatz/Gewinn gemacht wurden, tja. Was mich eher ärgert, dass die Apps soviele Crashes hatte, LOL.

Ich finde es schade, auf welche Art und Weise die App aus dem Store letztlich verschwand und möchte hier nochmal klarstellen, dass ich die App selbst aus dem Store genommen habe, weil ich keinen Bock auf irgend einen Fefe-Mob hatte, der mir dann eventuell meine anderen (für mich wirklich wichtigen) Apps oder andere Infrastruktur kaputtmacht. Ich hoffe dass der Mensch auf Twitter (den ich im Screenshot da oben verlinkt habe) sich nach seiner Aktion besser gefühlt hat. Er hat sein Ego sicher prima mit neuem Futter versorgt. Schade, denn für alle anderen hat er ein weitgehend ehrenamtlich gepflegtes Stück Software ungeplant deorbit’ed, für 15 minutes of fame!

Ich bin ihm jedoch im nachhinein dankbar für diesen Vorgang und vergebe seinem Ego. Denn für mich persönlich hat der Vorgang einen neuen Weg aufgezeigt, der mir persönlich eine deutlich konstruktivere und positivere Weiterentwicklung ermöglicht, die mir wichtiger ist als eine App die ich als Just-for-Fun-Projekt hatte.

Noch wichtiger ist jedoch, dass ich auch Zeit gewinne. Die App hat mich nicht nur bei der Entwicklung wertvolle Lebenszeit gekostet, sondern insgesamt über 329 Stunden wertvolle Zeit, das sind fast 2 volle Wochen meiner Lebenszeit nur durch das Lesen der Inhalte. Hier ein letzter Blick auf die App, bevor ich sie auch von meinem iDevice für immer heute gelöscht habe:

Letzte Übersicht Prozentuale Nutzung Absolute Zeit der Nutzung Anzahl Favoriten Löschung der App

Ich seh‘ es positiv: Ich hatte fast 10 Jahre meinen Spaß.

Dennoch, ich sage explizit an dieser Stelle „Danke für Nichts!“, denn letztlich hat mich das Blog und seine Postings fehlgeleitet und mich wertvolle Lebenszeit gekostet. Zumindest bin ich u.a. durch diese App ein deutlich besserer App Developer geworden. Und diese App war unter anderem auch die Source Code Grundlage für mehrere Congress Fahrplan Apps die es sonst so vermutlich nie gegeben hätte:

Why do I blog this? Ich schließe jetzt Anfang 2022 u.a. mit einigen Kapiteln meines bisherigen Lebens ab. Nämlich u.a. mit Negativität in jeder Form die meine Aufmerksamkeit in der Vergangenheit bekam. Fefe’s Blog ist für mich persönlich eine Quelle an Negativität. Mein Blick war lange Zeit verstellt das zu erkennen, aber es ist nicht gesund, sich Zynismus und Schadenfreude hinzugeben, oder auch nur seine negativen Erwartungen an die Zukunft verstärken zu lassen die so niemals eintreten müssen. Die Posts in Fefe’s Blog sollen aber Empörungscharakter haben und die negative Weltsicht von Fefe selbst bestärken, um letztlich sein Ego mit einem „Told you so“ zu boosten. Das Blog ist aus meiner Sicht ein Ego-Boosting-Projekt das jeden Besucher erfolgreich trollt. Eine positive Zukunft ist denkbar und möglich und es macht mehr Sinn sein eignes Ego nicht mit Negativität zu füttern und das fremde Ego von Fefe ebenso nicht mit Klickzahlen zu füttern. Man sollte es lassen die ganzen negativen Nachrichten in sich aufzusaugen. Das führt langfristig zu keiner einzigen Verbesserung. TL;DR: „Keep calm… …and don’t read Fefe.“ Andere bewerten das noch deutlich klarer und schrieben das auch nieder.

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..

ESP32/ESP8266: Optimizing the hell out of ESPAsyncWebServer

Das schöne an dem Microcontroller ist ja, dass man einen kleinen WebServer darauf laufen lassen kann. Damit hat man eine tolle Möglichkeit ein kleines aber immer verfügbares User Interface (UI) bereitzustellen, das mit fast allen modernen Geräten die einen Webbrowser haben (Tablet, Smartphone, Laptop, PC) sofort funktioniert.

WebServer in klein

Exception Decoder MenuWas man jedoch schnell vergisst, wenn man zuvor schon WebApplications mit WebServern wie nginx oder Apache ausgeliefert hat, ist der Ressourcenbedarf von so einem Server. Der Microcontroller ist halt klein deshalb ja auch „Micro“, hat wenig Speicher und wenig CPU-Zyklen die er für sowas entbehren kann.

Die Kunst liegt also in der Beschränkung. Doch das fällt sauschwer, wenn man erstmal einen gewissen Anspruch an seine Webseiten hatte. Niemand mag gern zurück in die Steinzeit und eine Webseite bauen z.B. ohne Stylsheets. Klar, das geht, sieht aber so schlimm aus und funktioniert auf dem Smartphone z.B. so gar nicht.

Zunächst habe ich den klassischen Weg zur Gestaltung meiner Webseite gewählt, ich hab HTML geschrieben mit Bildern darin und JavaScript das dynamisch Bilder austauscht und platziert, ich habe CSS geschrieben, das Bilder zur Gestaltung einsetzt. Es sieht wunderschön aus auf dem Desktop, aber… es crasht den WebServer des Microcontrollers. Da nutzt dann das ganze tolle Design auch nichts, wenn es letztlich nicht funktioniert.

Und ich habe zig Crashes gehabt. Ich habe mir mühsam den Arduino ESP8266/ESP32 Exception Stack Trace Decoder in der Arduino IDE installiert (Anleitung). Ein dekodierter Stacktrace sieht dann z.B. so aus:

Da sieht man, dass es im konkreten Fall eher ein Speicherproblem ist bei diesem Crash, weil eine alloc Funktion involviert ist. Aber viele andere Crashes sind eben anderer Natur.

Optimizing the hell out of it

Dann beginnt man zu begreifen und optimiert. Was kann man alles tun um zu optimieren? Anfangs dachte ich die Dateigröße wäre der Bottleneck des Microcontrollers. Ich hab die JS, CSS und HTML Dateien mittels GZIP komprimiert und mit dem content-type/gzip im Header ausliefern lassen. Das reduziert auch die Menge an Daten hervorragend. Oft wird nur noch ein Zehntel der Daten übertragen, was die Auslieferung selbst deutlich schneller macht.

Doch die Dateigröße ist gar nicht das Problem beim ESPAsyncWebServer. Das eigentliche Limit ist, dass der nur maximal 3 bis 4 Requests „gleichzeitig“ bzw. parallel bearbeiten kann. Der Rest der Requests hängt dann einfach und wartet ewig auf Antwort und wird verworfen oder führt zu einer Blockade des Webservers und einem späteren Crash. Es hat auch nichts damit zu tun, dass das SPIFFS Filesystem eine Obergrenze an gleichzeitig geöffneten Files hat wie mir ein Entwickler des ESPAsyncWebServer versicherte. Und darauf nun eine passende Antwort bzw. Optimierung zu finden ist gar nicht so einfach gewesen.

Klar, man kann das CSS und das JS alles mit in das HTML-file kippen, aber dann wird es schon arg unübersichtlich. Und des weiteren bleibt das Problem mit den vielen kleinen Icons bestehen, die alle je einen Request auslösen. Das kostet Zeit und trifft auf die Limitierung des Webservers. Das Hauptziel kann also zunächst nur sein die Anzahl initialer Requests (also der Requests die benötigt werden die Seite einmal komplett zu laden) zu minimieren. Und zwar sollte das so passieren, dass man unterhalb des Limits bleibt von max. 3-4 gleichzeitig eintreffenden Requests. Das ist natürlich ein Limit, dass sobald mehr als ein Nutzer gleichzeitig auf das UI zugreift bereits zu Problemen führen kann. Es ist schade, dass die Entwickler von ESPAsyncWebServer das nicht vorhergesehen haben und entsprechend zuviele Requests schlicht und einfach mit Fehler 503 ablehnen sondern stattdessen den Server crashen lassen. Aber vielleicht gibt es auch einen Grund der mir nicht begreiflich ist derzeit.

The Magic of Data-URI’s

Hier kommt dann jetzt eine Optimierungsstrategie zum Zuge, die auch bei „normalen“ Webseiten zu krass verbesserter User Experience (UX) führen kann. Das Einbetten der Bilddaten als sogenannte Daten-URI. Was ist das eine Daten-URI?

The data URI scheme is a uniform resource identifier (URI) scheme that provides a way to include data in-line in Web pages as if they were external resources. It is a form of file literal or here document. This technique allows normally separate elements such as images and style sheets to be fetched in a single Hypertext Transfer Protocol (HTTP) request […]

Das schreibt die Wikipedia dazu. und auf dieser Webseite heißt es „You can embed the image data directly into the document with data URIs.“ und genau das ist eine sehr sinnvolle Sache. Einerseits wird das Dokument (HTML, JS, CSS) in das man diese Daten dann einbettet zwar etwas größer, aber dafür unterbleiben Requests für Bilddaten komplett.

Aus folgendem Code für ein Favicon als Meta-Link im Header in HTML…

<link rel='icon' type='image/png' sizes='32x32' href='fav-icon-32x32.png'>

…wird folgender Code als eingebettete URI:

<link rel='icon' type='image/png' sizes='32x32' href='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA9tpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcFJpZ2h0czpNYXJrZWQ9IkZhbHNlIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDowMzhmMTQ5NC1lNGJkLTExZTEtOGQ4MC1jYjVjZDBjYjc4N2QiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RDJDODA5ODk0RjU1MTFFQThBRjNFQkJENEQ2MUU4MDUiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RDJDODA5ODg0RjU1MTFFQThBRjNFQkJENEQ2MUU4MDUiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjAyODAxMTc0MDcy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'>

Der entscheidende Teil ist, dass man statt einer Hyperlink-URI eine Data-URI nutzt, die eben so ausschaut:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAA

Als Werkzeug zur Umwandlung gibt es zahlreiche Online Tools dafür, wie z.B. den Image to data-URI converter. Dort kann man seine Grafikdateien umwandeln lassen. Es gibt auch apps z.B. für macOS den Data URI Converter von Chris Yip.
Mit dem richtigen Tool ist es jedenfalls schnell gemacht.

Ergebnis: Schnell, schneller, embedded Data-URI

Die Ergebnisse sprechen absolut für sich. Nachfolgend habe ich mit zwei Screenshots im Safari Browser festgehalten, die initialen Requests und deren Laufzeit und Datenumfang, die gegen den Controller gehen im zeitlichen Verlauf. Die Ergebnisse sind absolut eindeutig: Data-URI schlägt den klassischen Weg um Längen.

Man erkennt sofort, dass nach dem Laden der initialen HTML Seite core.html weitere Requests ausgelöst werden, klar ist die core.css und core.js werden benötigt, um die Seite überhaupt korrekt anzuzeigen. Aber allein schon die erste Seite auszuliefern benötigt 1,14 Sekunden Zeit, und dann wird es problematisch, da gleich fünf weitere Requests losgehen gleichzeitig, unter anderem eben auch requests wegen Grafiken. Das schafft der Server eben manchmal nicht. Und dann sieht man auch noch, dass jedes Bild ca. 300 bis 700 Millisekunden Zeit benötigt, und das ist sehr lang!! Die gesamte Seite ist erst nach über sechs Sekunden geladen.

Die gleiche Seite optimiert mit Data-URI’s verhält sich deutlich besser. Zwar wird der initiale Download der core.html um etwa 15KB größer, interessanterweise aber kostet das kaum Zeit. Und der initiale Download führt zu lediglich zwei weiteren Requests für die core.css und core.js. Beide Dateien enthalten selbst wiederum ausschließlich Data-URI’s und führen somit zu keinen Folgerequests.

Die Auswirkung ist enorm. Die Bilddaten sind nun in 10 bis 20 Millisekunden geladen statt wie vorher in 300 bis 700. Eine Verbesserung um Faktor zehn. Insgesamt ist die ganze Seite vollständig geladen nach nur etwas über zwei Sekunden. Das erspart satte 4 Sekunden Wartezeit.

Fazit

Wer Webseiten auf dem Microcontroller ausliefert, der sollte ausschließlich inline Data-URI’s für grafische Inhalte verwenden und die Anzahl der Requests soweit wie möglich senken. Für mein Setup bestünde jetzt noch die Option auch die core.css und core.js in die core.html zu integrieren. Dann wäre nur noch eine große Datei nötig.

Aus Gründen der Code-Übersicht sehe ich davon aber erstmal ab. Sollte es aber nochmal Probleme mit dem WebServer geben, dann kann ich hier gezielt weiter optimieren.

Why do I blog this? Das Problem des instabilen und ständig crashenden ESPAsyncWebServer hat mich einige endlos erscheinende Stunden des Debuggens gekostet. Weil ich auch einfach nicht wusste was den WebServer crasht und/oder blockiert und Serial.print() statements haben nicht wesentlich geholfen. Ich hoffe es hilft anderen nicht in die gleiche Falle zu tappen. Sehr hilfreiche Hinweise zum Thema ESPAsyncWebServer habe ich überigens im Diskussionsforum/Chat zu dem Server gefunden. Das hat mir sehr geholfen.