Kategorie: Embedded und Physical Computing

  • Taubenkriege – Eine einjährige Reise in die Welt des Outdoor-Edge-Computing

    Taubenkriege – Eine einjährige Reise in die Welt des Outdoor-Edge-Computing

    Wir haben einen Balkon. Dort stehen ein Feigenbaum, ein paar Fliesen und Tauben. Jedes Mal, wenn ich eine entdeckte, eilte ich hinaus und wedelte mit den Armen. Zehn Minuten später waren sie wieder da. Als Tüftler dachte ich mir natürlich: Das muss automatisiert werden.

    Die Idee war einfach – eine kleine Kamera, ein ESP32, Bewegung erkennen, ein Foto senden. Ein paar Abende Arbeit. Das war vor einem Jahr.


    Das Batterieproblem hat alles zunichte gemacht

    Die Bewegungserkennung funktionierte einwandfrei. Dann kam die Nacht. Am Morgen war die Batterie leer.

    Die meisten ESP32-Entwicklungsboards sehen auf dem Papier toll aus – Deep Sleep, LiPo-Ladegerät, Energiesparmodi. Was die Spezifikationen nicht verraten: Das Kameramodul zieht auch im Deep Sleep weiterhin Strom. Ich habe es mit Transistoren versucht, um den Strom abzuschalten. Mit MOSFETs. Mit einem zweiten Mikrocontroller, um das Hauptboard auszuschalten. Jede Lösung zog entweder zu viel Standby-Strom oder fügte Komplexität hinzu, die etwas anderes kaputt machte.

    Die Lösung war radikal: die Stromversorgung zwischen den Ereignissen komplett unterbrechen. Kein Deep Sleep. Die Schlüsselkomponente ist der TPS22918 – ein winziger Lastschalter-IC. Der PIR-Sensor löst ihn aus, wodurch der ESP32-S3 von Null aus neu gestartet wird. Der erste Firmware-Befehl sperrt die Stromschiene selbst. Fertig → das Board gibt die Sperre frei → völlige Dunkelheit. Standby-Strom: ~140 µA, hauptsächlich der PIR-Sensor selbst. Der ESP32 zieht nichts, da er keine Stromversorgung hat. Wenn ich Zeit habe, werde ich einen ausführlicheren Artikel zu diesem Thema schreiben – es war eine kleine Herausforderung, das richtige Bauteil zu finden und das winzige SMD-Bauteil zu löten, aber nach all meinen anderen gescheiterten Hardware-Versuchen läuft es einfach perfekt. 🙂

    Ja, das ist ein Steckbrett in einem Karton. Es ist ein Prototyp.


    WLAN löst den PIR aus. Im wahrsten Sinne des Wortes.

    Gerade als das Problem mit der Stromversorgung gelöst schien: WiFi-Funkstörungen lösten den PIR-Sensor während des Uploads aus. Das System spielte verrückt – es blinkte, löste sich immer wieder aus, geriet in eine Spirale. Es hat lange gedauert, das zu finden. Die Lösung: den PIR-Interrupt während der WiFi-Übertragung deaktivieren, Wiederauslösungen elegant handhaben.


    KI vs. Algorithmus – Der überraschende Teil

    Auf dem Balkon gibt es Pflanzen, ein Geländer und den Himmel. Mittags löst die Sonne den PIR ständig aus – Wolken ziehen vorbei, Schatten huschen hin und her. Die meisten aufgenommenen Fotos waren leer.

    Die naheliegende Antwort im Jahr 2025: ein neuronales Netzwerk darauf loslassen. Ich ging den anderen Weg – ein klassischer Signalverarbeitungsfilter, der auf dem ESP32 in weniger als 10 ms läuft, noch bevor das WLAN überhaupt aufwacht. Er unterteilt das Bild in ein Raster, vergleicht jede Kachel mit einem selbstgelernten, tageszeitabhängigen Hintergrund und entscheidet: echtes Ereignis oder nur Wolken?

    Getestet an 147 Außenaufnahmen: 100 % Erkennung bei echten Ereignissen, 61 % der Fehlalarme unterdrückt. Keine Trainingsdaten nötig. Keine Modelldatei. Kalibriert sich selbst anhand von Live-Aufnahmen. Passt in 2 KB.

    Manchmal sind die alten Werkzeuge die richtigen Werkzeuge.


    Der aktuelle Stand

    Die Plattform ist solide – zuverlässiges Aufwachen, Filterung von Fehlauslösungen, Foto-Upload. Was noch fehlt: Der Cloud-Check-Filter muss von Python auf C auf dem ESP32 portiert werden, und der ESP32-S3 hat genug Spielraum für ein kleines TFLite-Modell – die Hardware ist also bereits bereit für die Vogelerkennung auf dem Gerät, auch wenn das Taubenhirn noch nicht geschrieben ist.

    Die Tauben sind vielleicht schon an Altersschwäche gestorben, bevor ich fertig bin. Aber ich habe bei diesem Projekt mehr über Embedded-Hardware gelernt als bei allem zuvor. Mein Respekt für Leute, die Systeme bauen, die monatelang unbeaufsichtigt im Freien laufen – dieser Respekt ist um ein Vielfaches gestiegen.

    Code: github.com/happychriss/birdwatch-edge-platform

  • ESP32-Soundmaschine in einem IKEA-Bilderrahmen

    ESP32-Soundmaschine in einem IKEA-Bilderrahmen

    Ein Freund von mir hat einen ganz besonderen Musikgeschmack. Als unsere Gruppe also beschloss, Geschenke zu machen, die alle – im wahrsten Sinne des Wortes – in einen gemeinsamen Rahmen passen, kam mir eine Idee. Warum nicht eine echte Soundmaschine in einen IKEA-Bilderrahmen bauen? Gesamtbudget: unter 30 €, ein Wochenende und ein KI-Programmierassistent. Los geht’s.

     

    Die Hardware

    Ich hatte nicht viel Zeit, also habe ich einfach alles auf einmal bei AliExpress bestellt:

    • Ai-Thinker ESP32-Audio-Kit (ESP32-A1S V2.2) – ESP32 mit integriertem ES8388-Audio-Codec, zwei NS4150-Class-D-Verstärkern, SD-Kartensteckplatz, Bluetooth, sechs Hardware-Tasten, alles auf einer Platine. Etwa 17 €.
    • WS2812B-LED-Streifen, 30 LEDs – einzeln adressierbares RGB, angeschlossen an GPIO22. Etwa 4 €.
    • Zwei kleine Lautsprecher aus meinem Bastelvorrat.
    • Ein 5×7"-Bilderrahmen von IKEA – 5 €.
    • Ein USB-Stromkabel für 5-V-Versorgung.
    • Das war’s. Keine selbstbestückte Leiterplatte, kein Wirrwarr auf dem Steckbrett. Die Platine hat zwei USB-Anschlüsse – einen zum Flashen, einen für die Stromversorgung –, was die Sache noch übersichtlicher macht.

      Die Lautsprecher sitzen im Rahmen und sind auf die Kunststoffrückwand geklebt. Wenn ich das noch einmal bauen würde, würde ich sie direkt am Holzrahmen befestigen, damit dieser als Resonanzkörper fungiert – der 6-W-Verstärker ist da, die 4-Ω-Treiber sind da, aber im Moment entweicht der Klang durch den Kunststoff. Für 5-Euro-Lautsprecher klingt es immer noch überraschend gut. Mit dem Rahmen als Resonanzkörper wäre es wirklich hervorragend.

      Der Bau

      Alles wird mit doppelseitigem Klebeband und einer Heißklebepistole zusammengefügt. Das einzige, was gelötet werden muss, ist:

      1. Lautsprecherkabel an die JST-Anschlusspads der Platine
      2. Strom- und Signalkabel des LED-Streifens an die Platine
      3. Die Platine ist leicht in die Rückseite des Rahmens eingelassen, sodass nichts über die Rahmentiefe hinausragt. Du kannst das direkt an die Wand hängen.

        Eine Kleinigkeit, die ich beim nächsten Mal hinzufügen würde: einen externen Netzschalter. Momentan zieht man einfach den Stecker. Funktioniert zwar gut, aber trotzdem.

        Was es kann

        Der Funktionsumfang ist letztendlich ziemlich komplett geworden:

        • Spielt alle .mp3 Dateien aus dem Stammverzeichnis der SD-Karte in alphabetischer Reihenfolge der Dateinamen ab, in einer Endlosschleife
        • _welcome.mp3 wird beim Hochfahren immer als Erstes abgespielt (Unterstriche werden vor Buchstaben sortiert) – das Gerät begrüßt dich mit einem violetten Lichtrahmen
        • KEY1 kurz drücken: Pause/Weiter
        • KEY1 lang drücken (2 s): Bluetooth-A2DP-Empfangsmodus umschalten – LEDs blinken während des Pairings blau, dann streamt dein Handy Audio über denselben Codec und die gleiche LED-Anzeige
        • Taste 3/Taste 4: Vorheriger/Nächster Titel
        • Taste 5/Taste 6: Lautstärke runter/rauf
        • Bluetooth-Gerätename: Dietmars-Soundbox (natürlich ein personalisiertes Geschenk)
        • Die LED-Show

          Hier wird es technisch interessant. Die LED-Task führt auf Core 0 eine aus 5 Ebenen zusammengesetzte Show aus:

          1. Ambient – langsames Farbwechseln basierend auf der Gesamtlautstärke
          2. Beat-Burst – Blinken bei Kick-Erkennung
          3. Bass-Blob – auf die Helligkeit abgebildete tieffrequente Energie
          4. Sparkles – zufällige hochfrequente Glitzereffekte
          5. Picture Frame – die vier Seiten des Rahmens erhalten jeweils einen eigenen Farbton, gesteuert durch die Lautstärkestufe
          6. Die Audio-Pipeline funktioniert so: PCM-Samples werden aus dem I2S-Write-Callback abgegriffen (sowohl im SD- als auch im Bluetooth-Modus), von Stereo int16 in Mono float konvertiert und in einen Stream-Puffer geschoben. Die LED-Task leert diesen Puffer, führt eine 512-Punkt-FFT mit einem Hann-Fenster durch, teilt das Ergebnis in 8 logarithmische Bänder von 60 Hz bis 20 kHz auf, wendet bandweise AGC mit einer Zeitkonstante von ~10 s an und führt die Beat-Erkennung über ein Dual-EMA-Subbass-Verhältnis durch: kick_fast / kick_slow > 1.3, mit einem Mindestabstand von 200 ms zwischen den Schlägen.

            Bei leisen Passagen atmet der Rahmen in ruhigen Farben bei geringer Sättigung. Laute und schnelle Musik löst Disco-Snaps mit voller Sättigung aus, bei denen jede Seite des Rahmens einen zufällig zugewiesenen Farbton mit einer erzwungenen Trennung von ~80–120° erhält – so kommt es nie vor, dass zwei Seiten versehentlich dieselbe Farbe haben. Lautstärkestufen (leise / mittel / laut) steuern, wie viele Rahmenseiten gleichzeitig beleuchtet werden.

            Das sorgt wirklich für die richtige Stimmung im Raum. Es war toll zu sehen, wie dieser Teil funktioniert.

            Entwicklung mit Claude Code

            Ich hatte einen Docker-Container aus einem früheren Projekt – Ubuntu-Basis, Node.js, @anthropic-ai/claude-code installiert, USB-Seriell-Passthrough über docker-compose.yml. Ich habe alle AliExpress-Datenblätter und Platinen-Schaltpläne, die ich finden konnte, in einen external-docs/ Ordner, schrieb ein grobes requirements.mdund ließ Claude loslegen.

            Die Konfiguration ist es wert, beschrieben zu werden: Das Repo enthält eine CLAUDE.md , das Claude anweist, requirements.md, alle skills/ Dateien (Arbeitskonventionen pro Thema) und alle knowledge/ Dateien (validierte Konfigurationsnotizen pro Komponente) zu Beginn jeder Sitzung zu lesen. Auf diese Weise verfügt Claude über eine kontextübergreifende Persistenz über Sitzungen hinweg, ohne auf den Konversationsverlauf angewiesen zu sein. Jedes Mal, wenn Claude eine funktionierende Konfiguration bestätigt, schreibt er eine Notiz in knowledge/ — so beginnt die nächste Sitzung mit dem, was sich bereits bewährt hat.

            Die erste Herausforderung bestand darin, überhaupt Audioausgabe zu erhalten. Der ES8388-Codec reagiert empfindlich auf die Initialisierungsreihenfolge: Die SD-Karte muss vor der I2S-Initialisierung gemountet werden (GPIO25/26-Konflikt), MCLK muss vor der Codec-Initialisierung 100 ms lang stabil sein, und du musst I2S_CLK_SRC_DEFAULT — APLL verursacht breitbandiges weißes Rauschen auf dem ESP32. Claude hat all das gemeistert, indem er es versuchte, scheiterte, den Fehler auslas und Anpassungen vornahm. Der richtige Weg war, espressif/esp_codec_dev und niemals direkt in die ES8388-Register zu schreiben.

            Es gab auch einen fiesen Bug, bei dem jeder Song mit eingebettetem Cover-Art am Anfang knisterte. Die Ursache: Der ID3v2-Tag kann ein mehrere hundert KB großes JPEG enthalten, und der Helix-MP3-Decoder durchsuchte es Byte für Byte auf der Suche nach einem Sync-Frame, wodurch der I2S-DMA-Puffer leergefegt wurde. Die Lösung bestand darin, die Syncsafe-Größe aus dem ID3v2-Header zu parsen und fseek() das gesamte Tag zu überspringen, bevor die Datei an den Decoder übergeben wurde. Claude hat das selbstständig herausgefunden, nachdem ich ihm das Knackgeräusch beschrieben hatte.

            Bluetooth benötigte eine benutzerdefinierte 3-MB-Partitionstabelle, da der BT-Stack die Binärdatei auf ~1,1 MB aufbläht. Der A2DP-Daten-Callback muss streng nicht-blockierend sein – übertrage ihn StreamBufferHandle_t, Drain in einer separaten Task auf Core 1 – sonst bricht die BT-Verbindung bei jeder Belastung ab.

            Ich bin ein passabler Programmierer. Ich hätte zwei oder drei Wochenenden gebraucht, um das alles alleine zum Laufen zu bringen. Mit Claude Code war es eins.

            Stückliste

            TeilKosten
            ESP32-Audio-Kit (A1S V2.2, ES8388)17
            WS2812B LED-Streifen (30 LEDs)4
            IKEA-Bilderrahmen5
            Sonstiges (USB-Kabel, Klebeband, Kleber)~3
            Gesamt~29

            Lautsprecher aus dem Bastelvorrat, SD-Karte aus der Schublade. Rechne noch 5 € dazu, wenn du diese Teile kaufst.

            Was kommt als Nächstes

            Die naheliegende Erweiterung ist ein kleiner 5-V-LiPo-Akku unter dem Rahmen für die kabellose Wandmontage. Der LED-Streifen benötigt ohnehin 5 V, daher ist kein Aufwärtswandler erforderlich – nur ein winziger LiPo-Akku mit einer TP4056-Ladekarte unter dem Rahmen. Und ein ordentlicher externer Netzschalter.

            Der vollständige Quellcode und das Devcontainer-Setup sind auf GitHub.


            Ein paar Erkenntnisse, die ich daraus gewonnen habe: Günstige AliExpress-Hardware ist wirklich leistungsfähig, wenn du weißt, was du kaufst. Der ES8388-Codec ist ein echter Audio-Chip – er klingt gut. Und Claude Code ist ein wirklich nützliches Tool für Embedded-Projekte, nicht nur für Web-Sachen, solange du ihm einen strukturierten Kontext gibst. Der knowledge/ Ordneransatz – bei dem sich bestätigte Hardware-Fakten über mehrere Sitzungen hinweg ansammeln – hat einen echten Unterschied gemacht. Es lohnt sich, das richtig einzurichten.


            GitHub: happychriss/ESP32-A1S-sound-machine

        • ESP32-Boot-Optimierung: Menuconfig mit PlatformIO

          ESP32-Boot-Optimierung: Menuconfig mit PlatformIO

          Beschleunige die Boot-Zeit mit Menuconfig (ESP32)

          Wenn du das hier liest, bist du vielleicht schon weit gekommen … und ich bin noch auf dem Weg … daher ist das hier nur mein aktueller Stand. Korrigiere mich gerne und hilf mir, das Ganze weiter auszuarbeiten – und sei dir bewusst, dass nicht alles unten wirklich korrekt ist … aber zumindest … habe ich es so zum Laufen gebracht … 🙂

          Warum dauert das Booten eine Sekunde?

          Die Bootzeit wird für jedes eingebettete Gerät kritisch, wenn es aus dem Tiefschlaf geweckt werden und sofort reagieren muss. Ich brauchte eine kurze Bootzeit für das Goodwatch-Projekt, um sicherzustellen, dass die Uhr superschnell aufwacht, wenn ich möchte, dass sie die Zeit in Sekunden bei einer „Zeigerbewegung“ anzeigt (schau dir das Video hier an).

          Auf dem Wemos D32 Pro dauerte der Bootvorgang ca. 1 Sekunde – das ist für viele Aufgaben zu lang.

          Während dieser 1 Sekunde führt der Prozessor z. B. einen Selbsttest für den RAM (PSRAM) und einige andere Dinge durch.

          Also – ich habe mir überlegt, wie ich das überspringen kann.

          Wenn du dir schon mal das BIOS deines PCs angesehen hast, kennst du sicher einige Optionen dort; etwas Ähnliches gibt es auch für den ESP32: Menuconfig ist ein von Espressif bereitgestelltes Konfigurationsprogramm, um die Einstellungen für die ESP32-Prozessorfamilie fein abzustimmen – es sieht ein bisschen wie BIOS-Einstellungen aus:

          Aber mit Menuconfig kannst du viel mehr machen, also – was ich für Goodwatch gemacht habe

          • Boot-Vorgang beschleunigen, wie beschrieben unter: https://esp32.com/viewtopic.php?t=9448
          • Unterstützung für externen RAM: Y
          • Speichertest bei SPI-Initialisierung ausführen: N
          • Schnelleres SPI: „Serial Flasher Config“ QIO
          • Doppelte Flash-Lesegeschwindigkeit: Setze CONFIG_ESPTOOLPY_FLASHFREQ auf 80 MHz über Serial

          Das Ergebnis: Die Bootzeit hat sich drastisch verkürzt – statt Sekunden wird jetzt fast sofort angezeigt 🙂

          Aber hier kommt mein Problem – ich habe es nicht geschafft, menuconfig zu installieren und mit meinem Arduino-Build-System und den Bibliotheken zum Laufen zu bringen….

          Ich verwende ein E-Ink-Display und ziemlich viele Sensoren – daher ist es toll, Zugriff auf alle Arduino-Bibliotheken zu haben – allein schon ESP-Audio von Schreibfaul, das den ESP in ein Radio oder einen Sprachlautsprecher verwandelt, ist den Einsatz von Arduino wert, ebenso wie die großartige E-Ink-Bibliothek von ZinggJM.

          Das Build-System und „Frameworks“ verstehen

          Das Einrichten dieses Build-Systems ist eine komplizierte Aufgabe. Um die Arduino-Projekte zu erstellen, verwende ich Platformio.io – eine Build- und Entwicklungsplattform für eingebettete Geräte, die Arduino, aber auch viele andere Frameworks unterstützt.

          Es hilft, diese Begriffe zu verstehen:

          • Framework: Arduino oder espidf: Welche Art von Bibliotheken wird für die Kommunikation mit der Hardware verwendet? Das ist die grundlegendste Entscheidung.
          • Plattform: Das ist der Kern von Platformio – eine Reihe von Skripten und Dateien, die die ganze Arbeit übernehmen und das Build-System für dich einrichten. Die Plattform bestimmt, welche Version des
          • Komponenten: Code-Blöcke, die dem Framework hinzugefügt werden können, um die Funktionalität zu erweitern (Bibliotheken). Arduino kann als Komponente verwendet werden. Arduino-spezifische Komponenten (Bibliotheken, die ESP-Aufrufe in Arduino einbinden) werden hier von ESP-IDF bereitgestellt und können hier hinzugefügt werden

          Die Standardkonfiguration von Platformio ist in der Datei platformio.ini gespeichert; für ein einfaches „Arduino“-Projekt ist sie wie folgt eingerichtet:

          platform = espressif32
          board = lolin_d32_pro
          framework = arduino

          Das arduinoespressif32-Framework übernimmt die ganze Arbeit – es ermöglicht den geräteunabhängigen Arduino-Bibliotheken den Zugriff auf die gerätespezifischen ESP-IDF- und ESP32-Hardwarefunktionen.

          Wenn du den Build mit Platformio ausführst, siehst du Folgendes:

          PLATFORM: Espressif 32 (5.1.1+sha.4901957) > WEMOS LOLIN D32 PRO
          HARDWARE: ESP32 240MHz, 320KB RAM, 16MB Flash
          DEBUG: Current (cmsis-dap) External (cmsis-dap, esp-bridge, esp-prog, iot-bus-jtag, jlink, minimodule, olimex-arm-usb-ocd, olimex-arm-usb-ocd-h, olimex-arm-usb-tiny-h, olimex-jtag-tiny, tumpa)
          PACKAGES: -framework-arduinoespressif32 @ 3.20004.220825 (2.0.4) 

          Es verwendet eine Version 2.04 von arduinoespressif32 . Zur Erleichterung für alle Entwickler handelt es sich hierbei um eine „vorkompilierte/gebündelte“ Version, für die nicht die gesamte ESP32-Build-Kette installiert sein muss.

          Das erklärt, warum menuconfig nicht funktioniert – es ändert die Einstellungen im Framework. Das ist super, um mit der Entwicklung zu starten (keine Probleme mit der Versionskompatibilität) – aber es gibt keine Möglichkeit, irgendwelche Einstellungen zu ändern 🙁

          Raus aus der Höhle – ESP Framework mit Komponenten nutzen

          Die einzige Möglichkeit, menuconfig zum Laufen zu bringen und vollen Zugriff auf alle ESP32-Funktionen zu erhalten, ist der Wechsel zum Espressif IoT Development Framework. Dieses stammt direkt vom Hersteller und ermöglicht die Konfiguration der Build-Chain mit menuconfig.

          Um das Beste aus beiden Welten zu behalten, müssen wir weiterhin die Arduino-Bibliotheken verwenden, indem wir sie als Komponenten hinzufügen. Wie das funktioniert, wird hier beschrieben. Kurz gesagt: Das Projekt wird nicht mit dem Arduino-Build-System und -Ansatz eingerichtet, sondern als vollständige Espressif-Kette.

          Ein Ordner „components“ enthält die Bibliotheken, die Arduino in die „Sprache“ des ESP32 übersetzen. Alle Arduino-Bibliotheken funktionieren weiterhin und rufen die ESP32-Funktionen über den Komponenten-Wrapper auf – der wiederum die Kernfunktionen des ESP aufruft.

          Die Herausforderung – das Abgleichspiel & die Magie von Platformio

          Aber jetzt – am Ende muss alles zusammenpassen: Arduino-Version -> Komponente -> Espressif-Framework… hier beginnt der Albtraum…

          Espressif liefert bereits wichtige Informationen und stellt die Komponenten auf dieser Seite bereit.

          Die Komponenten können hier installiert werden: https://github.com/espressif/arduino-esp32.git

          Dort steht auch, dass sie mit ESP-IDF v4.4 zusammenarbeiten.

          Jetzt müssen wir das passende Framework von espidf finden, das ESP-IDF v4.4 unterstützt

          Erste Option – Probier einfach das Standard-Framework aus

          Also – lass uns die platformio.ini mit dem espidf-Framework aktualisieren. Sollte funktionieren, wir haben die Komponenten bereit

          framework = espidf
          platform = espressif32
          board = lolin_d32_pro

          Ergebnis:

          PLATFORM: Espressif 32 (5.1.1+sha.4901957) > WEMOS LOLIN D32 PRO
          HARDWARE: ESP32 240MHz, 320KB RAM, 16MB Flash
          DEBUG: Current (cmsis-dap) External .....
          PACKAGES:  - framework-espidf @ 3.40401.0 (4.4.1) 

          Hoppla… das hat nicht funktioniert – es wird für ESP-IDF 4.4.1 kompiliert, und das passt nicht zu unseren Komponenten. Am Ende des Builds erhalten wir eine Fehlermeldung: „undefined reference to `uart_get_tx_buffer_free_size'“

          Zweite Option – Die richtige Plattform für IDF v4.4 auswählen
          framework = espidf
          platform = https://github.com/platformio/platform-espressif32/archive/refs/tags/v4.4.0.zip
          board = lolin_d32_pro

          Das sieht besser aus, wir sind jetzt bei ESP-IDF 4.4.0

          PLATFORM: Espressif 32 (4.4.0) > WEMOS LOLIN D32 PRO
          HARDWARE: ESP32 240MHz, 320KB RAM, 16MB Flash
          PACKAGES:  - framework-espidf @ 3.40302.0 (4.3.2) 

          Argh… aber wieder eine Fehlermeldung:

          Arduino-esp32 kann mit ESP-IDF-Versionen zwischen 4.4.0 und 4.4.99 verwendet werden,
          aber es wird eine ältere Version erkannt: 4.3.2

          Das ist seltsam, auch die Plattformversion deutet auf 4.4 hin – es scheint, dass das ausgewählte Framework immer noch auf dem Stand von 4.3.2 ist – das passt nicht zu unseren Komponenten

          Dritte Option – Verwendung der Tasmota-Plattform

          Ich weiß nicht mehr, wo ich diesen Tipp gefunden habe – aber er hat mir das Leben gerettet. Tasmota stellt freundlicherweise eine weitere Plattformdatei für Platformio zur Verfügung, die funktioniert. Vielen Dank an Jason2866 (der dies auf Github pflegt) für all seine Arbeit – er macht das wirklich möglich!!!!!

          framework = espidf
          platform =https://github.com/tasmota/platform-espressif32
          board = lolin_d32_pro

          Das Ergebnis sieht besser aus:

          PLATFORM: Espressif 32 (2.0.4) > WEMOS LOLIN D32 PRO
          HARDWARE: ESP32 240MHz, 320KB RAM, 16MB Flash
          PACKAGES:  - framework-espidf @ 3.40403.0 <strong>(4.4.3) </strong>

          Die „2.0.4“ ist die Tasmota-Version der Plattform-Datei (nicht die ESP-IDF-Version). Aber wir sehen erfreulicherweise ein Framework mit der Auswahl 4.4.3. Hurra!!!!!!

          Ehrlich gesagt verstehe ich die ganze Magie der Tasmota-Plattformkonfiguration nicht ganz… mal sehen, was wir haben

          Tief tauchen… was unterscheidet die Tasmota- von der Platformio-Plattformdatei?

          Jede Plattformdatei enthält eine JSON-Datei, die die für die Build-Kette zu verwendenden Elemente definiert.

          Schauen wir uns diese Datei an:

          "version": "2.0.4+1",
            "frameworks": {
              "arduino": {
                "package": "framework-arduinoespressif32",
                "script": "builder/frameworks/arduino.py"
              },
              "espidf": {
                "package": "framework-espidf",
                "script": "builder/frameworks/espidf.py",
                "description": "ESP-IDF is the official development framework for the ESP32 and ESP32-S Series SoCs.",
                "homepage": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32/",
                "title": "Espressif IoT Development Framework"
              }
            },
            "packages": {
              "framework-arduinoespressif32": {
                "type": "framework",
                "optional": true,
                "owner": "tasmota",
                "version": "https://github.com/tasmota/arduino-esp32/releases/download/2.0.4.1/framework-arduinoespressif32.tar.gz"
              },
              "framework-espidf": {
                "type": "framework",
                "optional": true,
                "owner": "tasmota",
                "version": "https://github.com/tasmota/esp-idf/releases/download/v4.4.3/esp-idf-v4.4.3.zip"
              },

          Was sehen wir hier?

          • Die „Version“ 2.0.4 scheint Tasmota-spezifisch zu sein und gibt nicht die ESP-IDF-Version an
          • Das Tasmota-Paket unterstützt zwei Frameworks: „Arduino“ und „espidf“ – wir suchen nach espidf
          • Der  framework-espidf Framework verweist tatsächlich auf ein spezifisches Framework von Tasmota mit der Version 4.4.3 -> und das ist der Haken: Tasmota baut sein eigenes Framework.
          • Wenn wir uns das Framework von Tasmota hier ansehen: https://github.com/tasmota/esp-idf, sehen wir, dass es sich um einen Fork von espressif/esp-idf Version 4.4.3 handelt.
          • Wenn ich beide Forks vergleiche, sehe ich, dass sie gleich aussehen. Ich schätze also, dass die Unterschiede zwischen dem Tasmota-Paket und dem Platformio-Paket minimal sind, vielleicht nur die Versionseinstellungen in der JSON-Datei

          Das reicht mir – jetzt habe ich eine einfache Konfiguration, die funktioniert 🙂

          So geht’s – Tipps & Tricks

          Dies ist kein Tutorial zur Einrichtung der kompletten Build-Umgebung. Um dorthin zu gelangen:

          1. Richte ein normales Entwicklungsprojekt mit Platformio und folgender platformio.ini ein:
            framework = espidf
            platform =https://github.com/tasmota/platform-espressif32
            board = lolin_d32_pro
          2. Füge die passenden Komponenten von espidf zur Ordnerstruktur hinzu, wie hier beschrieben:https://espressif-docs.readthedocs-hosted.com/projects/arduino-esp32/en/latest/esp-idf_component.html
          3. Ich verwende Platformio in CLION und erhalte nach dem Befehl „pio init“ eine nicht funktionierende CMake-Datei. Ich verwende die folgende „CMakeLists.txt“:
            cmake_minimum_required(VERSION 3.16.0)
            include($ENV{IDF_PATH}/tools/cmake/project.cmake)
            list(APPEND EXTRA_COMPONENT_DIRS arduino)
            project(GoodWatch_espif)
          4. Führe „platformio build“ aus

          Lange Geschichte … kurz gesagt … aber es funktioniert 🙂

        • TinyML auf NDP101: PlatformIO & CLion mit EdgeImpulse

          TinyML auf NDP101: PlatformIO & CLion mit EdgeImpulse

          In diesem kurzen Beitrag fasse ich meine Entwicklungsumgebung mit PlatformIO & CLion zusammen. Ich habe die Datei platformio.ini angepasst, um die benötigten Bibliotheken und Dateien einzubinden – damit du loslegen kannst. EdgeImpulse bietet eine ähnliche Konfiguration, die sich eignet, wenn du mit der Arduino-IDE arbeitest.

          Hintergrund

          Ich nutze PlatformIO als Entwicklungsplattform für Embedded-Projekte und CLION als meine IDE. Beide sind integriert und bieten zusammen eine einheitliche Vorgehensweise für Programmierung, Kompilierung und Bereitstellung – unabhängig von der verwendeten MCU/CPU. Ich versuche, die Arduino-Bibliotheken zu nutzen, da diese bis zu einem gewissen Grad Plattformunabhängigkeit bieten. Außerdem sind für Arduino viele Bibliotheken verfügbar. Um das maschinelle Lernen zu erleichtern, setze ich auf EdgeImpulse – eine Cloud-Plattform, die den End-to-End-Zyklus des maschinellen Lernens unterstützt – kurz gesagt:

          • Platformio – Plattform für die Embedded-Entwicklung
          • Clion – Integrierte Entwicklungsumgebung mit Platformio-Integration
          • EdgeImpulse – Entwicklungsplattform für maschinelles Lernen auf Edge-Geräten

          Das i-Tüpfelchen: EdgeImpulse

          EdgeImpulse nimmt dir die beiden kompliziertesten Teile ab: das Modelldesign/-training und die Merkmalsextraktion. Es ist eine Wissenschaft für sich, einen kontinuierlichen Inversionsprozess auf einer kleinen MCU (z. B. ESP32) auszuführen, der die Sprachaufzeichnung, die Durchführung einer Fourier-Transformation, die Anwendung einer MEL-Transformation und schließlich die Einspeisung der Merkmale in das Modell umfasst. Da die Merkmalsextraktion während des Trainings (in der Cloud oder auf einem Desktop-PC) und später während der Inversion (auf dem ESP) sowohl logisch als auch zeitlich genau denselben Regeln folgen muss, erhöht dies die Komplexität von Embedded-ML-Projekten zusätzlich. Syntiant ist eine Partnerschaft mit EdgeImpulse eingegangen – daher wurden der Merkmalsextraktions- und der Inversionsprozess speziell auf den NDP 101 zugeschnitten – du musst dich also nicht tief in dessen Architektur einarbeiten.

          Also… was bekomme ich und wie baue ich tatsächlich etwas?

          Sobald das Training abgeschlossen ist (ausführliche Beschreibung von EdgeImpulse), bietet EdgeImpulse dir zwei Möglichkeiten, die Software auf dein Gerät herunterzuladen: Du kannst direkt ein Binärimage herunterladen und dein Modell testen oder – und hier wird es richtig interessant – den Quellcode herunterladen, der Folgendes enthält:

          • Software zur Merkmalsextraktion (DSP…)
          • Trainiertes Modell
          • Modellinversion (Merkmale in das Modell einspeisen und die Inversion ausführen)
          • Bibliothek zur Integration der „Modellentscheidung = Inversionsergebnis“ in den C-Code
          • Beispielprogramm, das all das auf Basis des Arduino-Frameworks nutzt – und tatsächlich zeigt, wie es funktioniert
          • Für NDP101 enthält es außerdem die Bibliotheken zur direkten Kommunikation mit dem NDP101 über SPI-Befehle (Modell laden, Modell ausführen…..). Ich bin noch dabei, die verschiedenen Befehle und Optionen zu erkunden

          Quellcode – Einsatzbereites Paket auf GitHub

          Meine erste Vorlagenversion für diese Konfiguration ist auf GitHub verfügbar: https://github.com/happychriss/Goodwatch_NDP101_SpeeechRecognition

          Um dies zu erstellen, habe ich das Syntiant/EdgeImpulse-Repository heruntergeladen und die Dateistruktur an das Platformio-Build-System angepasst; außerdem habe ich die Datei platformio.ini angepasst, um die benötigten Bibliotheken einzubinden.

          Bitte beachte, dass du eine der Bibliotheken patchen musst; die Basis für meine Arbeit sowie Anweisungen zum Patchen der lokalen Bibliotheken findest du hier.

          Ein nützliches Extra ist das Skript download_model.sh – es verbindet sich mit EdgeImpulse, lädt ein *Build*-Modell herunter und legt die Dateien in den richtigen Ordnern ab (./src/edgeimpulse) – so kannst du deine Binärdatei direkt aus dem Modell neu erstellen und auf das Board hochladen. Du musst in CLION eine Laufkonfiguration hinzufügen, damit du das Skript direkt starten kannst. Der API-Code für die Autorisierung auf EdgeImpulse muss zu den Parametern hinzugefügt werden.

          So sieht es aus, wenn alles eingerichtet ist. Viel Spaß beim Entwickeln.

        • Syntiant NDP 101 – Ständig aktive Spracherkennung mit geringem Stromverbrauch

          Syntiant NDP 101 – Ständig aktive Spracherkennung mit geringem Stromverbrauch

          Mal sehen … bin gespannt, ob es funktioniert … frisch aus Kalifornien…

          Always-On-Spracherkennung mit geringem Stromverbrauch mit Syntiant NDP 101

          NDP 101 – frisch von Digi-Key – was will ich jetzt damit machen?

          Ich werde das Syntiant TinyML NDP101-Board in mein neuestes Projekt integrieren: GoodWatch – eine superintelligente sprachgesteuerte Uhr.

          Derzeit verwende ich für GoodWatch einen ESP32 als MCU für die Spracherkennung. Er funktioniert mit 10 Zahlen und zwei Schlüsselwörtern (Ja, Nein). Aber – um bei so vielen Schlüsselwörtern gute Ergebnisse und eine schnelle Reaktion zu erzielen – musste ich ein bisschen tricksen: Er hört nur auf meine Worte. Wenn du mehr über ML für Edge-Geräte erfahren möchtest, schau dir das hier an: Überblick über ML auf Edge-Geräten. Wie weit ich mit einem ESP32 gekommen bin, siehst du hier: Goodwatch: eInk-Wecker mit Spracherkennung

          Mit diesem Projekt verfolge ich zwei Ziele:

          • Den Wecker mit einem Schlüsselwort aktivieren, um den Alarm einzustellen, und ihn zu 100 % sprachgesteuert betreiben
          • Die Spracherkennung nicht nur für meine Stimme zu aktivieren und eine äußerst zuverlässige Spracherkennung zu erreichen (sonst wäre das Einstellen des alten Weckers schneller 🙂

          Einige Fakten zum TinyML-Entwicklungsboard

          Board-Übersicht

          Ich habe das Entwicklungsboard „Syntiant TinyML“ bestellt. Es ist eine Kombination aus dem NDP101-Prozessor, einem Cortex-M0+ 32-Bit-ARM-MCU, einem SPH0641LM4H-Mikrofon, einem LIPO-Stromanschluss und einem USB-Stecker – also sofort einsatzbereit. Im Detail:

          • Neural Decision Processor: NDP101
          • Host processor: SAMD21 Cortex-M0+ 32bit low power ARM MCU, including:
              ◦ 256KB flash memory
              ◦ 32KB host processor SRAM
          • Board power supply: 5V micro-USB or 3.7V LiPo battery
          • 5 Digital I/Os compatible with Arduino MKR series boards
          • 1 UART interface (included in the digital I/O Pins)
          • 1 I2C interface (included in the digital I/O Pins)
          • 2MB on-board serial flash
          • 48MHz system clock
          • One user defined RGB LED
          • uSD card slot (uSD card not included)
          • BMI160 6 axis motion sensor
          • SPH0641LM4H microphone

          Das Board besteht aus zwei Komponenten – einer normalen ARM-MCU mit geringem Stromverbrauch und USB-Anschluss (für Kommunikation, Steuerung, Laden der Modelle…) sowie einer SPI-Verbindung zum Herzstück des Systems – der NDP101-MCU.

          Zur NDP1010-MCU: Ihre Hauptkomponente ist ein neuronales Netzwerk mit fester Struktur: 3 dichte Schichten mit je 256 Neuronen und 3 Dropout-Schichten; die Eingabeschicht muss immer 1600 Merkmale haben.

          Die vom Mikrofon aufgezeichneten Daten werden direkt vom Audio-Frontend (16 Bit) verarbeitet, der „Holding Tank“ fungiert als rollender Puffer für bis zu 3 Sekunden Sprache. Der Merkmalsextraktor führt eine ziemlich standardmäßige MEL-Transformation (Log-Mel LMFB) durch, bevor das neuronale Netzwerk zum Einsatz kommt. Mehr dazu kannst du hier nachlesen.

          Ich vermute, die kleine MCU wird verwendet, um die Kommunikation über SPI mit der Außenwelt zu verwalten.

          Software

          Die Geschichte hat gerade erst begonnen – sie ist verfügbar unter: https://github.com/happychriss/Goodwatch_NDP101_SpeeechRecognition

          Dies ist ein laufendes Projekt, ich habe keine Ahnung, ob es klappen wird oder wie lange es dauern wird. Ich werde weiterhin über den Fortschritt berichten – beginnend in den nächsten Tagen mit dem Entwicklungs-Setup und den ersten Schritten.

          [display-posts category=“ndp101 – Embedded ML“ include_content=“true“ image_size=“medium“ include_date=“true“ date_format=“(d.m.Y)“]
        • Goodwatch: eInk-Wecker mit Spracherkennung

          Goodwatch: eInk-Wecker mit Spracherkennung

          Goodwatch – Der eInk-Wecker, der auf meine Stimme hört – cloudfrei und läuft monatelang mit Batterie!

          Ich habe bereits den MCW gebaut – einen eInk-Wecker, der auf einem Arduino Pro Mini läuft – und war fasziniert vom tollen Aussehen und der Energieeffizienz von eInk-Displays. Ein Problem, das ich nicht wirklich effizient lösen konnte, war die Programmierung des Weckers. Immer so viele Tasten… also möchte ich, dass mein Wecker mich versteht – meine Worte auf Deutsch: die Zahlen null bis neun und die Wörter „ja“ und „nein“.

          Eine einfache Möglichkeit wäre, ein Mikrofon einzubauen und die Uhr mit Googles SpeechToText-API verbinden zu lassen – den Text zurückzubekommen – und den Wecker einzustellen. Aber mal ehrlich, wer will schon eine direkte Verbindung von seinem Schlafzimmer in die Cloud. Ich könnte auch einfach Alexa nutzen – nicht mein Stil.

          Also habe ich drei Projekte gestartet:

           

          https://youtu.be/W6pIDDZe-aQ
          Watch it – 30 sec video showing speech recognition working on an ESP32

          Hardware… Hardware… Hardware……

          Der Schlüssel zum Erfolg – ist die Wahl der richtigen Hardware – das war eine wichtige Lektion, die ich aus meinem letzten Projekt gelernt habe. Hier ist meine Wahl:

          eInk-Display

          • Gutes Display – E-Ink-EPD-Display 4,2 Zoll E-Ink Raspberry GDEW042T2
            Das E-Ink-Display unterstützt Teilaktualisierung und Kommunikation über den I2C-Bus – wichtig, nicht zu viele IO-Pins vom ESP32 zu belegen.
          • DESPI-C02 – eine kleine Platine, um das E-Paper mit dem ESP32 zu verbinden (kein Anschluss für die Stromversorgung des FrontLight-Panels) und DESPI-C03 (mit Anschluss). Achtung: Das DESPI-C03 ermöglicht es auch, beide FFC-Kabel für das Display und das FrontLight anzuschließen. Wenn du denkst, du kannst beide gleichzeitig mit Strom versorgen, zerstörst du dein Display

          eInk FrontLight

          Wenn du nachts die Uhrzeit ablesen willst, brauchst du Licht. Bei eInk ist das etwas kompliziert, du brauchst eine spezielle FrontLight. Das ist im Grunde ein Stück Glas mit LEDs am Rand. Ich hatte überlegt, es selbst zu bauen. Aber ich glaube, es zu kaufen, war eine viel bessere Idee. Trotzdem war es etwas komplizierter, das gewünschte Ergebnis zu erzielen:

          • Good Display – FrontLight-Panel <- das ist der Clou, und ich finde, es sieht nachts einfach cool aus. Das FrontLight muss genau auf das Display passen. Ich habe ziemlich viele E-Mails mit Good Display ausgetauscht, um diese Kombination auszuwählen, und war sehr überrascht von ihrem tollen Kundenservice. Am Ende kam es direkt aus China 🙂
          • FFC-Adapter RM 2,54 zum Anschluss des FrontLight-Panels an die Stromversorgung (siehe oben)
          • HW-045 DC-DC-Boost-Spannungsregler: Das FrontLight-Panel hat 7 LEDs à 200 Ohm in Reihe; wenn sie 15 mA zum Leuchten brauchen, brauchst du 21 V. Ups – noch ein Bauteil nötig, ein Spannungsverstärker.
          • IRF3708 Transistor N-MOSFET 30V 62A 87W TO220AB – zum Dimmen des Frontlichts, das muss sich natürlich an die Außenlichtverhältnisse anpassen. Eine erste Idee, PWM als Eingang für den Spannungsregler zu nutzen, war etwas naiv, daher nutzt der ESP32 den MOSFET, um den Ausgang des Spannungsreglers per PWM zu steuern, bevor das Display mit Strom versorgt wird. Damit das Ganze lange im Akkubetrieb läuft, muss jede Hardwarekomponente im Energiesparmodus sein, wenn der ESP32 im Ruhezustand ist. Für den Verstärker habe ich folgende Lösung gefunden – achte auf das winzige blaue Kabel – das versetzt den Verstärker ebenfalls in den Tiefschlafmodus:
          • MAX98357A
          Power Consumption

          Zusätzliche Hardware und Sensoren

          Hier kommt der Rest. Ich musste mehrere Runden durchlaufen, bis ich die beste Wahl gefunden hatte – aber ich bin wirklich zufrieden mit der folgenden Auswahl:

          • ESP32 als „https://www.wemos.cc/en/latest/d32/d32_pro.html“ (320 KB RAM, 4 MB PSRAM, 16 MB Flash).  Zwei sehr wichtige Dinge: Mein Exemplar hatte 16 MB Flash (Programmcode, SPIFF), was super ist, wenn du mit Machine Learning arbeiten und etwas ausgefallenere Sachen machen willst, sowie ein integriertes LIPO-Ladegerät und 4 MB PSRAM (das wird auch von der Sound-Bibliothek und für ML benötigt)
          • Adafruit DS3231 – Präzisionsuhr: Ein Wecker braucht Zeit und Alarme, beides kann der DS3231 leisten – unterstützt von einer kleinen Batterie, sodass Zeit und Alarme einen kurzen Stromausfall überstehen. Ich habe mich schließlich für die teurere Version von Adafruit entschieden, da zwei viel günstigere Versionen aus dem Internet einfach zu schnell liefen – ca. 10 Sekunden pro Tag – keine Option 🙂 Der DS3231 löst alle 5 Minuten einen Interrupt aus, um die Uhrzeit anzuzeigen und natürlich, wenn ein Alarm anliegt.
          • CS43434 I2S Digitalmikrofon – es gibt verschiedene Mikrofone auf dem Markt, ich habe sie alle ausprobiert. Zumindest von diesem kann ich sagen, dass es mit dem ESP32-I2S-Bus kompatibel ist und wirklich in guter Qualität aufnimmt. Meines habe ich über Tindie bekommen.
          • PIR HC-SR602 Infrarotsensor: Im Video siehst du, dass ich den Alarm durch eine Handbewegung über der Uhr deaktiviere – das ist ein PIR-Sensor. Erstaunlich – super, super geringer Stromverbrauch und so empfindlich. Der PIR-Sensor löst einen Interrupt auf dem ESP32 aus, um das Gerät zu wecken.
          • Adafruit I2S 3W Class-D-Verstärker-Breakout – MAX98357A: Beim Aufwachen soll ein schöner Ton erklingen, und der ESP32 hat genug Rechenleistung, um sogar MP3-Dateien zu decodieren oder von einem Radiosender zu streamen. Auch dieser Abschnitt befasst sich mit der Hardware – https://github.com/schreibfaul1/ESP32-audioI2S, um mit 5 Zeilen Code Ton in den Wecker zu bringen.
          • APDS-9960 – Digitaler Näherungs-, Umgebungslicht-, RGB- und Gestensensor: Ich wollte Gesten nutzen, um den Wecker zu aktivieren und zwischen verschiedenen Betriebsmodi zu wählen. Der APDS-9960 ist wirklich clever, er erkennt verschiedene Handbewegungen und misst Entfernungen. Außerdem kann er die Lichtstärke und -farbe erfassen. Ich habe viel Zeit investiert, aber er verbraucht auch im Energiesparmodus immer noch viel Strom. Die einzige Möglichkeit, ihn effizient zu nutzen: Den PIR-Sensor (siehe oben) immer laufen lassen – und dann die Messung starten. Leider verursacht das eine Verzögerung bei der Verarbeitung – und damit kommen wir zu meinem nächsten Projekt: Aufwachen mit meiner Stimme.

           

          Software

          Anfangs habe ich die Arduino-Plattform genutzt, um dieses Projekt zu entwickeln. Diese Plattform enthält ein vorkompiliertes ESP-IDF-Paket. Das ESP-IDF ist Espressifs offizielles IoT-Entwicklungsframework für den ESP32. Das hat zur Folge, dass alle Einstellungen, die zum Kompilieren des ESP-IDF verwendet wurden, feststehen und nicht geändert werden können.

          Boot-Geschwindigkeit ist entscheidend

          Beim Aufwachen der Goodwatch möchte ich eine schnelle Reaktion sehen – habe aber eine Pause von 1–2 Sekunden bemerkt, bevor der ESP32 aktiv wurde. Der ESP nutzt diese Zeit, um den Flash-Speicher und den RAM zu validieren. Da der Wemos D1 Pro über 4 MB PSRAM und 16 MB Flash verfügt, dauert das eine Weile.

          Durch eine Änderung der Compiler-Optionen konnte diese Überprüfung deaktiviert werden.

          Arduino als Komponente

          Der Trick besteht darin, Arduino als Komponente zu installieren. Das für die Kompilierung verwendete Haupt-Framework ist das ESP IDF, das die Arduino-Bibliotheken als Unterordner enthält. Dies ermöglicht den Zugriff auf ein Dienstprogramm namens „menueconfig“, das eine Konfigurationsdatei generiert, z. B. sdkconfig.lolin_d32_pro.

          Das Problem ist, dass Arduino und ESP-IDF unterschiedliche Build-Tools verwenden. Nach langem Suchen habe ich es geschafft, das zum Laufen zu bringen. Geschwindigkeit ist wieder KING, ich würde sagen, weniger als 500 ms Reaktionszeit auf eInk ist gut.

          Den Quellcode findest du hier. Ich habe in der platformio.ini einige Kommentare zum Build eingefügt.

          Alles ist auf GitHub: https://github.com/happychriss/GoodWatch_espidf

          In Kreisen denken…

          Ich hatte Spaß daran, die Kreisbögen zu zeichnen – das weckte Erinnerungen an die Schulzeit mit meinen Freunden „Sinus“ und „Cosinus“. Ich bin mir sicher, dass es eine viel elegantere Lösung gibt – aber zu einem bestimmten Zeitpunkt war ich einfach nur glücklich, die Kreise zu sehen. Insgesamt glaube ich, dass ich einen recht effizienten Weg gefunden habe. Schau dir diesen „frechen“ Code-Schnipsel zum Zeichnen eines Bogens an – und das Ergebnis:

          void DrawArcCircle_Q3(GxEPD2_GFX &my_display, st_pwin *pwin, int x0, int y0, double start_angle, double end_angle, int ri, int ro) {
          
              start_angle = (270 - start_angle) * (M_PI / 180);
              end_angle = (270 - end_angle) * (M_PI / 180);
              double tan_start_angel = tan(start_angle);
              double tan_end_angel = tan(end_angle);
          
              int ri2 = ri * ri;
              int ro2 = ro * ro;
              for (int cy = 0; cy <= ro; cy++) {
                  int cx_i;
                  if (abs(ri) < abs(cy)) {
                      cx_i = -1;
                  } else {
                      cx_i = (int) (sqrt(ri2 - cy * cy) + 0.5);
                  }
                  int cx_o = (int) (sqrt(ro2 - cy * cy) + 0.5);
                  int cyy = cy + y0;
          
                  int dx_1 = cy / tan_end_angel;
                  int dx_2 = cy / tan_start_angel;
          
                  if (dx_1 < cx_o) { cx_o = dx_1; };
                  if (dx_2 > cx_i) { cx_i = dx_2; };
          
                  if (cx_i <= cx_o) {
                      gfx_line(my_display, pwin, x0 - cx_i, cyy, x0 - cx_o, cyy);
                  }
          
              }
          }

          Zeit braucht Zeit – und das Rechnen mit Tagen und Monaten – noch mehr

          Ein weiteres einfaches Thema – das mich einige Zeit gekostet hat, bis ich es hinbekommen habe: Das Rechnen mit Zeit. Mein Wecker hat 5 verschiedene Weckalarme, der DS3231 kann 2 Alarme verwalten und ein Interrupt-Signal erzeugen, um den ESP32 zu wecken. Ein Alarm wird alle 5 Minuten zum Wecken verwendet, um einen neuen Bogen zu zeichnen. Der andere muss pünktlich aktualisiert werden – also musst du die Wecker sortieren, um den nächstmöglichen zu finden. Jetzt hast du Wecker an Wochentagen und am Wochenende, wiederkehrende Wecker und einmalige Wecker – du verstehst schon… Ich bin nicht stolz auf meinen Code hier – aber das Sortieren war am Ende einfach.

          struct strct_alarm {
              uint8_t i =0;
              bool active = {false};
              bool valid = {false};
              DateTime time;
          };
          
          int SortTimes(void const *a, void const *b) {
              const struct strct_alarm *ad = (struct strct_alarm *) a;
              const struct strct_alarm *bd = (struct strct_alarm *) b;
              return (int) (ad->time.secondstime() - bd->time.secondstime());
          }
          
          // Calling the qsort
          qsort(rtcData.d.alarms, ALARM_NUMBERS_DISPLAY, sizeof(strct_alarm), SortTimes);
          

          Flash – Speicher sichern

          Die Alarmzeiten müssen auf dem ESP32 gespeichert werden und sollten einen Stromausfall überstehen (ähnlich wie die Uhrzeit). Der ESP32 verfügt über einen FLASH-Speicher, perfekt – genau das, was ich brauchte. Nachdem ich ein bisschen mit CRC-Werten herumprobiert hatte, um zu prüfen, ob die Daten im Flash gültig sind, und mit Adressen… erfuhr ich in der ESP-Dokumentation von einer ESP32-Funktion namens „NVS“. – Da wurde es dann wirklich einfach. Es prüft sogar, ob der Flash konsistent ist.

             esp_err_t err = nvs_flash_init();
              if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
                  // NVS partition was truncated and needs to be erased Retry nvs_flash_init
                  ESP_ERROR_CHECK(nvs_flash_erase());
                  err = nvs_flash_init();
              }
              ESP_ERROR_CHECK( err );
          
          // Write:
                 err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle);
                  size_t required_size = sizeof(d);
                  if (err != ESP_OK) DPL("NVS_OPEN ERROR");
                  err = nvs_set_blob(my_handle, STORAGE_NAMESPACE, (uint8_t *) &d, required_size);
                  if (err != ESP_OK) DPL("NVS_WRITE ERROR");
                  // Commit
                  err = nvs_commit(my_handle);
                  if (err != ESP_OK) DPL("NVS Commit Error");
          
          // Read: 
                 err = nvs_get_blob(my_handle, STORAGE_NAMESPACE, (uint8_t *) this, &required_size);
                  if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) {
                      DPL("NVS READ ERROR");
                  }

          Programmierung des eInk-Displays

          Das wäre eigentlich der komplizierteste Teil gewesen, aber dank: https://github.com/ZinggJM/GxEPD2 war es wirklich einfach. Es war ein bisschen Arbeit, diese Bibliothek zu finden und das Konzept zu verstehen. Es gibt auch ein Support-Forum und der Autor ist wirklich hilfsbereit. DANKE.

          Es ist erwähnenswert, dass das Display über einen einfachen Adapter DESPI-C02 oder DESPI-C023 mit einem FCC-Kabel an den ESP32 angeschlossen ist, von dort aus ist es mit dem ESP verbunden. Die meisten Fehlerursachen liegen in der Verkabelung und der korrekten Zuordnung der PINS – und ich habe MISO und MOSI vertauscht — das hat mich so lange gekostet 🙂

          // From my hardware
          // EPD Pins - GxEPD2_display_selection_new_style.h using SPI
          // EPD Pins****
          // CS=5, DC=0, RST=2,BUSY=15;
          // SPI: SCK=18, SDI=MISO=23
          // BUSY -> 15, RST -> 2, DC -> 0, CS -> 5, CLK -> SCK(18), DIN -> MOSI(23), GND -> GND, 3.3V -> 3.3V
          
          #define EPD_CS 5 // Chip Select -
          #define EPD_DC 0
          #define EPD_RST 2
          #define EPD_BUSY 15
          
          GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> 
          display(GxEPD2_DRIVER_CLASS(/*CS=5*/ EPD_CS, /*DC=*/ EPD_DC, /*RST=*/ EPD_RST, /*BUSY=*/ EPD_BUSY));
          

          Aktuelles

          [display-posts category="goodwatch" include_content="true" image_size="thumbnail" include_date="true" date_format="(d.m.Y)"]

        • DIY-Smart-Button: Energiesparend & flexibles Design

          DIY-Smart-Button: Energiesparend & flexibles Design

          In 15 Sekunden das Konzept verstehen!

          https://youtu.be/STZjFQVLcN0

          Was ich mir wünsche, ist:

          • Ein winziger Touch-Button
          • Den ich einfach an die Wand oder auf den Schreibtisch kleben kann, ohne zusätzliche Verkabelung, also batteriebetrieben
          • Er lässt sich durch eine einfache Berührung aktivieren, muss also aus dem Ruhemodus aufwachen
          • Er verbindet sich mit WLAN und ist sehr flexibel, also muss er einen ESP8266 haben
          • !!!!! Die endgültige Lösung wird eine kleine Batterie haben, die genauso groß ist wie der Knopf – natürlich 🙂 !!!!

          Warum war das eine kleine Herausforderung?

          Nun ja… Ich habe das Display bereits zum Laufen gebracht, schau dir diesen Beitrag an. Aber es verbraucht im Betrieb viel Strom, also muss es in den RASTERMODUS wechseln, wenn niemand das Display nutzt. Jetzt kann es an der Wand kleben und einfach warten. Sobald du es brauchst, berührst du das Display, es wacht auf und ist bereit – nach einiger Zeit fällt es wieder in den Tiefschlaf. Dort sollte es einige Monate durchhalten.

          Es ist einfach, den ESP8266 in den Tiefschlaf zu versetzen – aber wie weckt man ihn wieder auf?

          Leider habe ich im Display keine Logik gefunden, die ein „Weck“-Signal für den ESP8266 unterstützen würde. Der Ansatz ist also, den ESP8266 immer im Ruhezustand zu lassen und einen PIC-Low-Power-Mikrocontroller zu verwenden, der dem Display regelmäßig kurzzeitig Strom zuführt, um dessen Touch-Controller zu überprüfen. Wird keine Berührung gemeldet, schaltet der PIC den Strom ab und geht wieder in den Ruhezustand. Wurde eine Berührung erkannt, weckt der PIC den ESP8266 auf, versorgt das Display mit Strom und schaltet die Hintergrundbeleuchtung ein. Alles läuft wieder.

          Energie sparen!

          Meine ursprüngliche Idee war, den ESP8266 zu nutzen, um regelmäßig aufzuwachen und das Display zu überprüfen. Leider kann der ESP8266 den Status seiner E/A-Pins (digitale Ein-/Ausgänge) im Tiefschlaf nicht beibehalten (ich habe ziemlich lange gebraucht, um das herauszufinden), sodass im Tiefschlaf Strom zum Display fließen würde (tatsächlich war es nicht einmal möglich, die Hintergrundbeleuchtung zu deaktivieren) … und *keine Energieeinsparung*.

           

          PIC 16LF1824 Low-Power-Mikrocontroller

          Hier kommt der PIC ins Spiel:

          • Der PIC schläft
          • Um das Display zu aktivieren, ist es an einen Transistor angeschlossen, der das Display „erdet“, um den Stromkreis zu schließen.
          • Sobald dies geschehen ist, zieht er die Hintergrundbeleuchtung auf Masse (damit sie deaktiviert bleibt) und überprüft den „Touch-IRQ“-Pin des Displays.
          • Erst wenn eine Berührung erkannt wird, zieht er den RST-Pin auf Masse, sodass der ESP8266 aufwacht. Das Schöne an einem PIC ist, dass er den IO-Status im Ruhezustand beibehält, keine weitere Hardware zum Betrieb benötigt und einfach programmiert werden kann.

          Ich poste diesen Auszug, um zu zeigen, wie einfach der PIC für solche Aufgaben eingesetzt werden kann:

           while (1)
              {
                  WDTCONbits.SWDTEN = 0b1;
                  SLEEP(); // sleep for watchdog sleep time 1.3 sec
                  WDTCONbits.SWDTEN = 0b0;
                 
                  pen_pressed=false;
                
                  //Enable the display (without backlight) to measure if touched 
                  IO_RC4_TFT_BKL_OUT_SetLow();
                  IO_RC3_TFT_PWR_OUT_SetHigh();  
                  
                  __delay_ms(40); //time needs the TFT to boot
                          
                  PEN_value=IO_RC5_TFT_PEN_IN_GetValue()  ;
          
                  // Pen is pressed *** Wake up display
                  if (PEN_value==0) {
                   
                      pen_pressed=true;
          
                      IO_RC0_ESP_RST_OUT_SetLow(); 
                      __delay_ms(10);
                      IO_RC0_ESP_RST_OUT_SetHigh(); 
                      
                      // enable TFT backlight
                      __delay_ms(600); //give the ESP time to wake up
                      IO_RC4_TFT_BKL_OUT_SetHigh();
                  }
          
                  // check, if the ESP allows me to sleep again
                  if (pen_pressed) {
                      do {
                          __delay_ms(500);
                          SLEEP_value=IO_RC1_ESP_SLEEP_IN_GetValue() ;
                      }
                      while (SLEEP_value!=0);
                  }
          
                  // Go back to sleep
                  IO_RC2_TST_OUT_SetLow();
                  IO_RC3_TFT_PWR_OUT_SetLow();
                  IO_RC4_TFT_BKL_OUT_SetLow() ;
                  
              }
          }

          ESP8266-Programmierung

          Nach dem Aufwachen durch den PIC übernimmt der ESP die Steuerung des Displays und verbindet sich über WLAN mit dem SmartHome-Server / DocBox-Server – um sich zu registrieren. Das ist nichts Besonderes. Aber ich wollte ein System einrichten können, das Menüs und Untermenüs anzeigt, also habe ich den folgenden Ansatz mit der AdafruitGFX-Button-Bibliothek und einem „button-press-handler“ entwickelt, wie unten gezeigt – nach der Definition der Tasten wird jede Taste einem „Handler“ (https://github.com/happychriss/TouchSwitch/blob/master/src/tft_helper.cpp), der aufgerufen wird, wenn die Taste gedrückt wird. Wenn jedes „Untermenü“ in einer eigenen C-Datei untergebracht ist, ergibt sich ein sehr modulares und strukturiertes Ergebnis.

          uint main_menu() {
          
              //****** Paint the Display ************************************************
          
              DPL("**** Paint Main-Menu Display");
          
              tft.fillScreen(ST7735_WHITE);
          
              enum M1 {
                  B_SCAN = 0, B_COPY = 1, B_HOME = 2, B_STATUS = 3
              };
          
              Adafruit_GFX_Button buttons[4];
              buttons[B_SCAN].initButtonUL(&tft, B_X_SPACE, B_Y_SPACE, B_X_WIDTH, B_Y_HEIGHT, C_BUTTON, C_BACK, C_TEXT, "SCAN", 1);
              buttons[B_COPY].initButtonUL(&tft, B_X_SPACE + (X_MAX / 2), B_Y_SPACE, B_X_WIDTH, B_Y_HEIGHT, C_BUTTON, C_BACK, C_TEXT, "COPY", 1);
              buttons[B_HOME].initButtonUL(&tft, B_X_SPACE, (Y_MAX / 2) + B_Y_SPACE, B_X_WIDTH, B_Y_HEIGHT, C_BUTTON, C_BACK, C_TEXT, "STATUS", 1);
              buttons[B_STATUS].initButtonUL(&tft, (X_MAX / 2) + B_X_SPACE, (Y_MAX / 2) + B_Y_SPACE, B_X_WIDTH, B_Y_HEIGHT, C_BUTTON, C_BACK, C_TEXT, "MORE", 1);
          
              for (int i = B_SCAN; i <= B_STATUS; i++) {
                  buttons[i].drawButton();
                  buttons[i].press(false);
              }
          
              global_status = GLOBAL_ACTION_MAIN_MENU;
              struct _button_handler buttonHandler[4];
              buttonHandler[0].button_action = menu_start_scanner;
              buttonHandler[1].button_action = menu_start_copy;
              buttonHandler[2].button_action = menu_start_test;
              buttonHandler[3].button_action = menu_start_more;
          
              return HandleButtons(buttons, 4, buttonHandler, TOUCH_SECONDS_WAIT);
          }
          

           

           

          Eine Leiterplatte bauen

          Schließlich wurde mir klar, dass das ein bisschen Verdrahtungsarbeit bedeuten würde, also habe ich meine zweite Leiterplatte mit Fritzing entworfen und direkt über Aisler bestellt, ein deutsches Unternehmen für Rapid Prototyping. Ich war sehr positiv überrascht von dem günstigen Preis und der schnellen Lieferung. Es ist so viel einfacher, die Leiterplatte zu entwerfen, als die ganze Verdrahtung selbst zu machen.

           

           

           

           

           

          Alles zusammengebaut – immer noch winzig

          Insgesamt besteht das System aus folgenden Komponenten: TFT-Display, ESP8266, PIC und Akku – und sollte trotzdem winzig sein. Ich habe viel mit verschiedenen Layouts herumprobiert – das war das Ergebnis. Schließlich muss (natürlich) noch der Akku hinzugefügt werden. Bei der Wahl des ESP habe ich mich für den Wemos Mini entschieden, der mit einem integrierten LiPo-Akku und LiPo-Ladegerät geliefert wird.

          Wie geht’s weiter?

          Ja … es braucht ein Gehäuse … 🙂