Repository navigation
Pixelia 0.2.2 – Fernwartung
Pixelia 0.2.2 – Fernwartung
Log, Befehle, Leistung, Neustarts und Absturzberichte per WLAN, Diagnosepaket zum Verschicken vom Handy · 2026-10-19
Neu
- System: Log-Ringpuffer: Jede Log-Zeile geht weiter unverändert auf die serielle Konsole und zusätzlich in einen 256-KB-Ringpuffer im PSRAM (No-Init-Bereich, den der Speichertest beim Start ausspart). Jede Zeile trägt eine fortlaufende Nummer, die Laufzeit in ms, die Uhrzeit und den Startzähler. Der Puffer übersteht Software-Neustarts, Updates, Abstürze und Watchdog-Resets, erst ein Stromausfall löscht ihn. Jede Zeile wird sofort aus dem Write-Back-Cache in den PSRAM zurückgeschrieben, sonst fehlen nach einem Neustart genau die letzten Zeilen. Zwei Köpfe mit Prüfsumme in einer Cache-Zeile; beim Start werden alle Datensätze nachgezählt, Unstimmiges wird abgeschnitten
- System: RTC-Sicherung der letzten 6 KB (feste Adresse 0x50000000) für den Fall, dass der nächste Start eine andere Version ist, bei der der PSRAM-Puffer an anderer Stelle liegt (die Lage verschiebt sich in 64-KB-Schritten mit der Größe von Code und Konstanten): Im Probelauf nach einem Update wird jede Zeile mitgeschrieben (Absturz → Rückfall auf die alte Version), sonst kopiert ein Shutdown-Handler die neuesten Zeilen einmal vor jedem Software-Neustart. Beim Start meldet Pixelia, woher der Puffer kommt („übernommen“, „aus RTC-Sicherung“, „neu“), mit Befund beider Köpfe
- Web: GET /api/log liefert Zeilen aus dem Puffer, inkrementell mit since=, außerdem tail=, boot= (nur ein bestimmter Start), rid (erkennt einen neu angelegten Puffer), max= und format=text. WebSocket /ws/log für das Live-Log: zuerst die letzten 50 Zeilen (tail bzw. since), dann jede neue Zeile, bis zu fünfmal pro Sekunde gebündelt. Der Hook selbst sendet nie
- Web: POST /api/cmd führt dieselben Diagnose-Befehle aus wie die serielle Konsole (in derselben Task, nacheinander) und liefert ihre Log-Zeilen als Antwort (bis 16 KB, bei mehr bleibt das Ende erhalten). Gefährliche Befehle (reboot, wifi forget/setup/connect, flash, pclk, timing, pol, bounce, clk, buf, touchrst, log clear, coredump erase) lehnt das Gerät ohne confirm=1 mit Grund ab (409). WLAN-Passwörter lassen sich weiterhin nicht über Befehle eintragen. Für den optionalen PIN aus 0.3.x vorbereitet (Kopf X-Pixelia-Pin bzw. Feld pin; ohne PIN ist alles erlaubt)
- Web: GET /api/perf mit allen Werten des Leistungsmonitors (Panel, Pixeltakt, Interrupt mit Höchstwert, Aussetzer, Engine mit Zeichen- und Logikzeit, CPU pro Kern, interner RAM, DMA und PSRAM mit Tiefstwerten und größtem Block, WLAN) und den Kosten des Log-Hooks; mit tasks=1 zusätzlich alle Tasks mit Kern, Priorität, Stack-Reserve und CPU-Last
- System: Reset-Historie der letzten 16 Starts im NVS (Namensraum diag): Startnummer, Uhrzeit (nach einem Einschalten nach dem NTP-Abgleich nachgetragen), Grund, Version, Partition, Probelauf, Rückfall mit Grund, Absturz-Task und PC, dazu die Laufzeit des vorigen Starts und der Zeitpunkt seines letzten Lebenszeichens (Herzschlag alle 2 s im RTC-Speicher). Abruf über GET /api/resets und den Befehl resets
- System: Absturzbericht per WLAN: GET /api/coredump liefert die Rohdaten aus der Partition, GET /api/coredump/summary Panic-Grund (bei CPU-Ausnahmen deren Name, z. B. StoreProhibited), Task, PC, Backtrace-Adressen, Ausnahme und die passende Version über den ELF-SHA256; POST /api/coredump/erase löscht ihn. Konsolenbefehle coredump und coredump erase
- Web: Diagnosepaket GET /api/diag: eine JSON-Datei mit Status, System (Chip, Flash, PSRAM, Partitionen mit Version und Zustand, NVS), Einstellungen samt Namen der gespeicherten WLANs (ohne Passwörter), Leistung mit Task-Liste, Reset-Historie, Absturzbericht (Zusammenfassung und Rohdaten als Base64) und dem kompletten Log-Puffer. Auf der Einrichtungsseite neuer Bereich „Diagnose“ mit Startnummer, Resetgrund, Fehlern und Warnungen seit dem Start, Zeilen im Log, Absturzbericht und dem Knopf „Diagnosepaket herunterladen“ (auf dem Handy landet die Datei unter Downloads und lässt sich von dort verschicken)
- Engine: Passwort-Filter (engine/redact.h): ersetzt jedes Vorkommen der gespeicherten WLAN-Passwörter und des Setup-WLAN-Passworts durch Sternchen gleicher Länge, auch in JSON-Schreibweise und über Blockgrenzen hinweg, längstes Geheimnis zuerst. Läuft über alle Ausgaben mit freiem Text: /api/log, /ws/log, /api/cmd und /api/diag. Das Diagnosepaket meldet am Ende secretCheck (geprüft, ersetzt)
- System: Werkzeug tools/netlog.ps1 für Claude: Live-Log per WebSocket mit Filter wie monitor.ps1 (-Full, -Filter, -Until, -MaxLines), Befehle per -Send (mit -Confirm für gefährliche), frühere Starts mit -Boot prev bzw. -Boot , ganzer Puffer mit -All; Befehlsausgaben erscheinen nicht doppelt. Volles Log in .logs\netlog-last.log
- System: Werkzeug tools/crash.ps1: Absturzbericht per WLAN (oder mit -File aus einem Diagnosepaket vom Handy) holen, das passende ELF über den SHA256 finden (lokaler Build, Testvarianten, Zwischenspeicher oder pixelia-vX.Y.Z.elf aus dem GitHub-Release), PC und Backtrace mit Funktion, Datei und Zeile dekodieren, den Eintrag der Reset-Historie und die letzten Log-Zeilen des abgestürzten Starts zeigen. -Full ruft zusätzlich esp-coredump mit GDB auf (alle Tasks), -Erase löscht den Bericht
- System: Neue Konsolenbefehle log (Puffer und Kosten des Hooks), log bench [n] (Messung), log clear, tasks, resets, coredump und coredump erase. Die Leistungszeile alle 2 s geht weiter auf die serielle Konsole, in den Puffer nur einmal pro Minute (sonst bestünde er nach einer halben Stunde nur aus Leistungszeilen)
- System: Testvariante -DPIXELIA_TEST=crash-shift: stürzt wie crash im Probelauf ab und verschiebt mit 64 KB zusätzlichen Konstanten den Log-Puffer um eine MMU-Seite (prüft den Weg über die RTC-Sicherung, wie nach einem echten Update mit mehr Code)
- Simulator: Selbsttest --test redact mit 9 Fällen (Blockgrenzen Zeichen für Zeichen und in 7er-Stücken, JSON-Schreibweise, ineinander enthaltene Geheimnisse in beiden Reihenfolgen, zu kurze Geheimnisse, 100 KB ohne Geheimnis, Abbruch des Ziels), läuft in der CI
Verbessert
- Web: /api/status enthält jetzt auch Startnummer, letzten Resetgrund, Fehler und Warnungen seit dem Start, Zeilen im Log-Puffer und einen vorhandenen Absturzbericht (Feld diag)
- System: Der Leistungsmonitor hält mehr Werte bereit (Pixeltakt, längster Interrupt, Zeichen-Höchstwert, Logikzeit, Logik-Schritte, Sprites, Weltzeit, DMA-Speicher, größter PSRAM-Block)
- Web: JSON-Schreiber als eigene Datei (json.cpp) statt inline im Header: Der Compiler (-O2) kopierte die Zeichenkettenarbeit sonst in jede Aufrufstelle, das kostete in der Entwicklung über 100 KB Code. Firmware jetzt 1170 KB (19 % der App-Partition)
- Web: Große Zwischenpuffer (WebSocket-Rahmen, gestreamte Antworten, Befehlsausgaben) werden bewusst ab 16 KB angelegt und landen so im PSRAM statt im internen RAM
- Doku: HARDWARE.md (Diagnose per WLAN mit allen Schnittstellen, Werkzeugen und Messwerten, Lage des Log-Puffers, Kernel-Sperre), Roadmap mit 0.2.2, Entscheidungsprotokoll, CLAUDE.md (netlog.ps1 und crash.ps1)
- Doku: Neue Code-Landkarte docs/CODEMAP.md: Ordner, Engine-Bausteine, Firmware-Module mit Startablauf, Tasks mit Kern und Priorität, Speicher (Partitionen, NVS-Namensräume, No-Init-Bereiche), Schnittstellen, Simulator, Werkzeuge und CI. Neue Sitzungen müssen damit nicht mehr den halben Code lesen
- Doku: CLAUDE.md mit Abschnitt „Sitzungen und Nutzungslimit“ (Vorschlag des Users): eine Sitzung pro Phase, neue Sitzung nur bei Phasenwechsel oder ab ca. 300 K Kontext, schlanke Einarbeitung über CLAUDE.md, Roadmap und CODEMAP, Denkstufe normal „high“; CODEMAP.md unter „Zuerst lesen“
Behoben
- Web: Der Status-WebSocket /ws hat unter ESP-IDF 6.1 nie gesendet: Beim Handschlag ruft der Webserver den URI-Handler nicht mehr auf, die Anmeldung stand aber genau dort. Die Einrichtungsseite lief deshalb unbemerkt über ihre Ersatz-Abfrage alle 2 s. Jetzt mit Post-Handshake-Callback (CONFIG_HTTPD_WS_POST_HANDSHAKE_CB_SUPPORT); gemessen 5 Nachrichten in 3,5 s
- System: In der Entwicklung gefunden: Die Datensatzköpfe des Log-Puffers wurden über einen uint32_t-Zeiger in eine Struktur mit uint16_t-Feldern gelesen (Verstoß gegen die Aliasing-Regeln von C++). Der Compiler nahm Länge, Größe und Startnummer als 0 an: alle Zeilen kamen leer zurück, nach einem Neustart galten 0 Zeilen als gültig. Jetzt per memcpy; die Compiler-Warnung „may be used uninitialized“ war das Symptom
- System: In der Entwicklung gefunden: Die Task-Liste (uxTaskGetSystemState) hält die Kernel-Sperre 1,1 ms, während sie die Stacks aller Tasks durchsucht. Tick- und Display-Interrupt auf Kern 1 warten so lange, der Bounce-Buffer kommt zu spät: etwa ein Display-Aussetzer pro Abfrage (im Lasttest mit 39 Abfragen pro Sekunde 368 Aussetzer und 27 fps). Die Task-Liste gibt es deshalb nur auf Wunsch (tasks, /api/perf?tasks=1, einmal im Diagnosepaket), /api/perf ohne Parameter ist aussetzerfrei
- System: In der Entwicklung gefunden: Der Puffer-Teil des Hooks kostete zuerst 34 µs pro Zeile, je etwa zur Hälfte PSRAM (mit esp_cache_msync) und RTC-Speicher (~0,3 µs pro 32-Bit-Zugriff). Jetzt RTC-Sicherung nur im Probelauf zeilenweise, Cache-Rückschreiben direkt über die HAL unter dem Spinlock von ESP-IDF, beide Köpfe in einer Cache-Zeile: 16–19 µs
- System: In der Entwicklung gefunden: Die Ausgabe eines Befehls brach bei 16 KB ab, ausgerechnet vor der Zusammenfassung. Jetzt fällt bei Überlauf der Anfang weg und das Ende bleibt erhalten
Geprüft
- System: Alle Nachweise am Gerät ausschließlich per WLAN (tools/ota.ps1, netlog.ps1, crash.ps1, kein serieller Port): Software-Neustart über /api/cmd reboot und Updates auf neue Stände: Puffer „übernommen“ (z. B. 158 bzw. 711 Zeilen früherer Starts), die Zeilen vor dem Neustart stehen im Verlauf
- System: Absturz-Testvariante crash (gleiche Puffer-Lage 0x3C120000) per Update aufgespielt: Absturz nach 20 s im Probelauf, Rückfall auf die alte Version. crash.ps1 zeigt Task selbsttest, StoreProhibited bei Adresse 0, dekodiert auf ota.cpp:125, findet das ELF über den SHA256 automatisch, die Reset-Historie meldet „Start 12 lief 20 s“, und die letzte Log-Zeile des abgestürzten Starts ist „Testversion: absichtlicher Absturz im Probelauf“ (100 ms vor dem Absturz geschrieben)
- System: Testvariante crash-shift (Puffer bei 0x3C130000): Nach dem Rückfall meldet die alte Version „PSRAM: kein gültiger Kopf“ (von der Testversion überschrieben) und „RTC: 95 Zeilen gültig“, der Puffer kommt „aus RTC-Sicherung“ mit der letzten Zeile vor dem Absturz. crash.ps1 -Full mit esp-coredump: Frame #0 test_misbehave (elapsed_s=20) at ota.cpp:125. Auch aus dem Diagnosepaket (-File) dekodiert
- System: Kosten des Log-Hooks (log bench): Puffer 15,8–18,6 µs pro Zeile im Mittel (PSRAM 12,6–15,6 µs inklusive Cache-Rückschreiben), höchstens 24–60 µs; im Probelauf mit RTC-Sicherung 30 µs. Zum Vergleich wie bisher: Formatieren 49–64 µs und serielle Ausgabe 3,6–8,7 ms pro Zeile. 100 Zeilen am Stück: 30,6 fps, 0 Aussetzer
- Web: Lasttest 40 s mit 162 Abrufen (Log 256 KB, Befehle, Absturzbericht, Leistung) und Live-Log parallel: 29,7–30,3 fps, 0 Aussetzer, interner RAM 119–121 KB frei (Tiefstwert 108 KB, Budget ≥ 60 KB), PSRAM 6,4 MB frei. Diagnosepaket 91 KB in 0,2 s, gültiges JSON
- Web: Passwort-Filter Ende zu Ende: Test-WLAN mit dem bekannten Passwort Pruef"Geheim\4711 eingetragen und einen Befehl damit abgesetzt: /api/cmd, /api/log (JSON und Text), /ws/log und /api/diag enthalten es weder roh noch in JSON-Schreibweise (Diagnosepaket: 3 Geheimnisse geprüft, 2 ersetzt). Test-WLAN danach wieder entfernt
- System: Stack-Reserven mit dem neuen Hook: IDLE 736 B, sys_evt 1180 B, webstatus 1272 B, wlan 1348 B, console 1484 B, logpush 1680 B, httpd 4312 B
- Simulator: Gestentest 11/11, Passwort-Filter 9/9
- Update: 0.2.1 am Gerät (Test des Users): läuft. Erstes tools/ota.ps1 am echten Gerät: 1168 KB in 6,9–10,8 s (108–183 KB/s), Selbsttest jeweils nach 60 s bestanden
Hinweise
- System: Verschiebt ein Update die Lage des PSRAM-Puffers (Code und Konstanten wachsen über eine 64-KB-Grenze), bleiben nur die letzten 6 KB aus der RTC-Sicherung erhalten. Nach einem Stromausfall ist der Puffer leer
- Web: Der Passwort-Filter kennt nur die gerade gespeicherten Passwörter. Pixelia schreibt Passwörter nie ins Log; tippt jemand selbst ein Passwort in einen Befehl und löscht das WLAN später, stünde es in alten Log-Zeilen im Klartext
- System: Die Task-Liste kostet etwa einen Display-Aussetzer (1,1 ms Kernel-Sperre), das Diagnosepaket enthält sie einmal. Für die Leistungsanzeige in 0.4.x gilt: die Task-Liste nie regelmäßig abfragen
- Web: Alles ist nur im Heimnetz erreichbar und ohne PIN; der optionale PIN aus 0.3.x schützt dann /api/cmd und das Löschen des Absturzberichts
- System: Testvarianten tragen -test-crash bzw. -test-crash-shift in der Version (auf 31 Zeichen gekürzt) und werden nie veröffentlicht
Pull Requests
Installation
- Update (ab 0.2.0): auf der Einrichtungsseite des Geräts unter „Update“ die Datei
pixelia-v0.2.2.binhochladen. - Erstinstallation: Web-Flasher https://baldesarra.github.io/pixelia/ öffnen (Chrome oder Edge am PC), Board per USB-Datenkabel anschließen, auf „Installieren“ klicken.
- Von Hand:
pixelia-v0.2.2-factory.binmit esptool ab Adresse0x0schreiben.