# Hardware-Checkliste (EVK) Konsole: `tools/monitor.sh /dev/cu.` (115200 8N1). Flashen: `tools/flash.sh evk ` — das Skript nutzt `-rs none`, die Lampe braucht also einen **manuellen** Reset in dem Zeitfenster, in dem `wm_tool` auf den Sync wartet; ein automatischer Reset über die Steuerleitungen funktioniert an diesem Board nicht. ## Licht (Plan 1, Task 11) - [x] Nach dem Boot blendet warmweiß 100 % in 0,5 s ein - [x] `rgb 255 0 0` → nur Rot, `rgb 0 255 0` → nur Grün, `rgb 0 0 255` → nur Blau - [x] `ct 2700` → nur Warmweiß, `ct 6500` → nur Kaltweiß, `ct 4000` → Mischung - [x] `bri 1` sichtbar sehr dunkel, `bri 100` hell; `bri 1 3000` → gleichmäßig wirkender Fade - [x] `off 2000` → blendet mit aktueller Farbe aus; `on 2000` → blendet ein - [x] `rgb 255 0 0` dann `ct 3000 2000` → erst dunkel, dann weiß (Moduswechsel) - [x] `blink 3` → 3× Blinken, danach alter Zustand; `pulse on` / `pulse off` - [x] Kein sichtbares Flackern bei konstanten Werten (5 kHz) ## Persistenz und Boot (Plan 1, Task 12) - [x] `pob last`: Farbe setzen, 4 s warten, Strom aus/an → gleicher Zustand, mit Fade - [x] `pob off`: Strom aus/an → bleibt aus; `on` → letzte Farbe - [x] `pob on` bei ausgeschaltetem Licht: Strom aus/an → an - [x] `fade 2000 2000` bleibt nach Reboot erhalten (`state`) - [ ] 5× Strom an/aus mit jeweils < 5 s Laufzeit → 3× Blinken, danach Defaults (`state`) (erneut zu prüfen: der vorgelagerte Recovery-Scan aus Plan 3 verschiebt jeden Boot um bis zu ~3 s, das Timing der 5-Sekunden-Grenze ist damit nicht mehr das von Plan 1) - [ ] Normaler Boot nach > 5 s Laufzeit → kein Werksreset (erneut zu prüfen, gleicher Grund: Recovery-Scan verschiebt die Boot-Zeitrechnung) - [x] Watchdog: `wdtest` → Neustart nach ca. 20–40 s (erste Ablaufzeit löst IRQ aus, Reset beim zweiten Ablauf) — getestet 2026-09-11 mit e19ee98: Neustart nach 40,4 s - [x] Getestet am 2026-09-11 mit Firmware 7502f4f am EVK (MAC 28-6D-CD-5C-68-1F) ## Netzwerk & REST (Plan 2) - [x] Mit Build-Credentials: Boot → `net` zeigt `mode=sta` und eine IP, RSSI plausibel (−30 … −90) — 2026-09-12 - [x] `dns-sd -B _ninlight._tcp` findet `ninlight-`, `dns-sd -L` zeigt TXT type/fw/mac/name — 2026-09-12 - [x] Bruno: alle Device-Requests wirken sichtbar, `Get State` spiegelt den Zustand — 2026-09-12 - [x] Ungültiger Body (`{"brightness":0}`) → 400, `system/settings` ohne Token → 401 — 2026-09-12 - [x] `bri 30` per REST (`{"brightness":30}`) oder Konsole `bri 30`, dann sofort `Reboot` per REST → nach dem Boot 30 % (Flush) — 2026-09-12 - [x] Werksreset → AP `ninlight-` sichtbar, Lampe pulsiert, Beitritt mit `ninlight` — 2026-09-12 - [x] Onboarding mit falschem Passwort → `system/info` zeigt `wifi.error = "auth"`, AP bleibt — 2026-09-12 - [x] Onboarding korrekt → `202`, Lampe verbindet, Pulsieren stoppt, AP nach 60 s weg — 2026-09-12 - [ ] Router aus > 5 min → AP erscheint (`mode=apsta`); Router an → Verbindung, AP nach 10 s weg - [x] Namensänderung per REST → TXT `name=` aktualisiert (`dns-sd -L`) — 2026-09-12 - [x] `auth_all` an → `GET device/state` ohne Token 401, mit Token 200 — 2026-09-12 - [x] Onboarding-Tests: Build ohne `NINLIGHT_WIFI_SSID` in `config.local.cmake` — 2026-09-12 - [x] C1: `curl -d '[[[[[[[[[[[[[[[[[[[[' http://:3333/api/v1/device/state` → 400, Lampe läuft unverändert weiter (kein Absturz/Reboot) — 2026-09-12 - [x] C2: Onboarding ok, 60 s warten, dann Reboot und Router-Neustart → AP `ninlight-` darf nicht wiederkommen (Scan am Mac) — 2026-09-12 (Reboot geprüft, Router-Neustart offen) ## OTA und Recovery (Plan 3) - [x] OTA: Update über `fw_update` läuft durch, danach meldet `system/info` die neue Version — 2026-09-12 (0.3.0 → 0.3.1 → 0.3.2) - [x] OTA: Image eines anderen Gerätetyps wird sofort abgelehnt (`last_ota_error` = `device_mismatch`), kein Reset, Lampe läuft mit der alten Firmware weiter — 2026-09-12 (Ablehnung nach ~4,5 s, Uptime lief durch) - [x] OTA: Image mit verfälschtem Byte wird abgelehnt (`last_ota_error` = `hash` oder `crc`), kein Reset — 2026-09-12 (`crc`, kein Reset) - [x] OTA: HTTP-Server während des Downloads abschalten → Lampe bleibt über REST erreichbar und schaltbar, weist weitere `fw_update` mit `409` ab, und beendet den Versuch mit `last_ota_error = download` — 2026-09-12 (dauerte ~7 min, kein Range-Resume; siehe README) - [ ] OTA: kurzer WLAN-Aussetzer während des Downloads (Verbindung kurz kappen und wiederherstellen) → Download wird fortgesetzt, kein sofortiger `download`-Fehler — *offen, WLAN am Teststandort nicht abschaltbar* - [x] Recovery: Lampe zieht beim Booten das Image von der Station `ninlight-recovery`, Konsole zeigt Scan-, Join- und Fetch-Meldungen — 2026-09-12 (Pi Zero 2W, NetworkManager-AP; Scan → join → fetch → flash → Reboot) - [x] Recovery: ohne Station verlängert sich der Boot um höchstens ~3 s, Konsole loggt das Scan-Ergebnis (gefunden/nichts gefunden/Scan fehlgeschlagen) — 2026-09-12 (`scan done after 1500 ms, 7 AP(s)`, danach normaler WLAN-Start) - [ ] Recovery: Gegenstelle bleibt verbunden, sendet aber keine Daten mehr (nicht: ein langsamer, aber laufender Download) → nach Ablauf der Zeitobergrenze bedient die Firmware den Watchdog nicht mehr, Chip startet neu statt hängen zu bleiben - [x] Einstellungen bleiben über ein OTA hinweg erhalten (WLAN, Name, Fade-Zeiten) — 2026-09-12 (Name, Fade 1200/800, pob, Lichtzustand) - [x] Zweiter `fw_update` mit gültigem Image direkt nach einer Ablehnung, ohne zwischenzeitlichen Reboot → läuft durch, `system/info` meldet danach die neue Version — 2026-09-12 (erst nach dem SHA256-Kontext-Fix) - [x] Lampe bleibt während und nach einer Ablehnung über REST erreichbar: `system/info` und `device/state` antworten beide weiterhin mit 200 — 2026-09-12 (system/info und device/state je 200) - [x] Lichtverhalten über ein OTA hinweg (Spec §8.2, ein Update ist kein Stromausfall): einmal mit `power_on_behaviour = off`, einmal `= on` — die Lampe ist nach dem Update jeweils in demselben Zustand (an/aus, Farbe/Helligkeit), in dem sie vor dem Update war — 2026-09-12 (pob=off + Licht an → blieb an; pob=on + Licht aus → blieb aus) - [x] `system/info.fw_version` nach einem OTA stimmt mit der Version im Trailer des geflashten Images überein (Trailer-Version z.B. mit `tools/mkimage` prüfen) — 2026-09-12 - [ ] Recovery im echten Rettungsfall: Lampe ohne gültige WLAN-Konfiguration bzw. im Onboarding-AP findet die Station, zieht das Image und startet mit der neuen Firmware - [x] Rettungsstation nach erfolgreicher Recovery angelassen lassen, Lampe neu booten lassen → kein erneutes Flashen (Version stimmt bereits überein) und kein Werksreset (Power-Cycle-Zähler wurde vor dem Recovery-Versuch gelöscht) — 2026-09-12 (zweiter Boot: `image already carries the running version, not flashing`, kein Werksreset, beide Boots melden `boot #1`) - [ ] OTA im **AP-/Fallback-Modus** (`mode=apsta`, kein Router erreichbar) → Update läuft durch wie im normalen `sta`-Modus — *offen, braucht einen abschaltbaren Access Point*. Mit `auth_all = true` am 2026-09-12 geprüft: Update läuft durch, `device/*` antwortet ohne Token korrekt mit 401 (`system/info` bleibt laut Spec §7.2 immer offen) Für den echten Flottenbau `NINLIGHT_RECOVERY_PASSWORD` und `NINLIGHT_TOKEN` in `config.local.cmake` setzen — die Defaults sind nur fürs EVK gedacht. ## Später (Lampen) - [x] SM2135EH: Standby-Bit bei allen Kanälen 0 — 2026-09-13 an migrierter GU10 RGB geprüft: **war ein echter Fehler**, die Lampe glomm im Aus weiter. Bit 5 im Kanal-Register wird jetzt gesetzt (Commit zu v0.3.8); danach beide Ausschaltwege (aus Weiß und aus Farbe) vollständig dunkel - [ ] E27 Color (SM2135EH): RGB und WY getrennt, Ströme plausibel (nicht zu heiß) — *derselbe Treiber ist am 2026-09-13 an einer GU10 RGB geprüft (rot/grün/blau sauber getrennt, warm- und kaltweiß deutlich unterschieden); für die E27 selbst steht die Prüfung noch aus* - [ ] E27 CW: Gegenphase CW sichtbar flackerfrei - [x] G95: Pinbelegung WW=PB15/CW=PB13 — 2026-09-13 an einer migrierten G95 geprüft: unteres Bereichsende deutlich warm, oberes deutlich kühler (damals noch 2500–5000 K, inzwischen 2000–5000 K), Dimmen auf 10 % flackerfrei, weiches Ausblenden - [ ] Farbtemperaturbereiche aller Varianten — *die Bereiche stammen seit 2026-09-13 von Michael aus den Produktdaten und stehen so in `nl_board.c`: G95 Vintage **2000–5000 K**, alle übrigen **2700–6500 K**. Vorher standen die vier Weiß-Varianten flach auf 2500–5000 K, was für die E27/E14/GU10 CW den oberen Bereich abgeschnitten und für die G95 den unteren verschenkt hat. Am Gerät gegengeprüft ist bisher nur gu10_rgb; die G95 wurde mit den alten Grenzen getestet und gehört an den neuen wiederholt.* ## Standalone-AP-Modus (Abnahme am EVK, MAC 28:6d:cd:5c:68:1f) - [x] `type: standalone` per `PUT system/settings` einschalten → 202 `{"status":"standalone"}`, `system/info` meldet `wifi.standalone: true` — 2026-10-02 - [x] Neustart → Lampe startet direkt im eigenen AP: Konsole `wifi: standalone AP, hostname ninlight-681f`, keine Onboarding-Zeile, kein Onboarding-Pulsen — 2026-10-02 - [x] Geänderte AP-Daten → `wifi: AP data changed, rebuilding`, danach `wifi: AP ninlight-681f (ch 6) up` — 2026-10-02 - [x] Standalone mit exakt den Vorgabedaten auf einem bereits stehenden Onboarding-AP (kein Neuaufbau) löst die mDNS-Bekanntmachung ebenfalls aus — 2026-10-02 - [x] mDNS im eigenen AP: Konsole `mdns: ninlight-681f.local _ninlight._tcp up on en1 192.168.4.1`, `dns-sd -G v4 ninlight-681f.local` löst auf `192.168.4.1` auf, `ping` 0 % Verlust — 2026-10-02 - [x] REST über den AP antwortet auf `192.168.4.1:3333` — 2026-10-02 - [ ] Startpuls als Sichtprüfung: läuft beim Start rund 5 s und gibt danach das normale Einschaltverhalten frei; mit `pulse: false` entfällt er ganz — *Logik ist hostgetestet, am Gerät hat noch niemand hingesehen* - [ ] REST über den AP verlangt das Token (`GET system/settings` ohne Token → 401) - [ ] WLAN eingeben, falsches Passwort → Gerät bleibt im eigenen AP mit eigenen Daten - [ ] WLAN eingeben, richtiges Passwort → Gerät im Heimnetz, nach einem Neustart **nicht** wieder im AP - [ ] Werksreset über 5× ein/aus → wieder offener Onboarding-AP - [ ] sACN über den Standalone-AP, **sichtbar an der Lampe** (nicht nur `active: true`), unicast an die AP-IP und multicast — *Fallen: Ein Sender ohne gesetztes `IP_MULTICAST_IF` misst bei zwei Default-Routen das eigene LAN statt den AP (am 2026-10-01 genau so schiefgegangen). Ein laufender Startpuls übermalt die sACN-Ausgabe, weil `nl_engine_pulse()` eine Überlagerung ist: für die Prüfung `pulse: false` setzen oder den Puls abwarten.* ## Offene Beobachtung aus der EVK-Session vom 2026-09-12 Beim Test mit dem verfälschten Image meldete lwIP einmalig `Assertion "pbuf_free: p->ref > 0" failed at line 747 in pbuf.c` (10:29:23). Die Lampe lief unbeeinträchtigt weiter und meldete korrekt `last_ota_error=crc`. Bei den Läufen mit fremdem Gerätetyp und bei den erfolgreichen Updates trat die Meldung nicht auf. Ursache noch nicht geklärt — in einem Build ohne Assertions wäre das ein doppeltes Freigeben eines Netzwerkpuffers. Vor dem Einsatz auf echten Lampen reproduzieren und aufklären. ## Recovery gegen die installierte Pi-Station (2026-09-13) - [x] Station installiert per `tools/station/setup_pi.sh`, idempotent (zweiter Lauf ließ die Konfiguration in Ruhe) — 2026-09-13 - [x] `ninlight-station on` schaltet Zugangspunkt und Dienst gemeinsam, `status` meldet Dienst, Adresse, Image und Version, Exit 0 — 2026-09-13 - [x] Recovery zieht das Image und flasht: `recovery image version 0.3.7 (running 0.3.6)` → `trailer ok` → Neustart mit der neuen Version — 2026-09-13 - [x] Station angelassen, erneut gebootet → `already carries the running version 0.3.7, not flashing`, `ota: skipped`, kein Werksreset (beide Boots melden `boot #1`), `last_ota_error` bleibt leer — 2026-09-13 - [x] Zustandsdatei der Station protokolliert beide Auslieferungen mit `status: ok`, Gerätetyp und Version — 2026-09-13 - [x] Fehlendes Image für den Gerätetyp → Station antwortet `404`, Eintrag `notfound`, Lampe bootet normal weiter — 2026-09-13 (Lampe meldete `update failed (download)`) - [x] Stillstand: Server hält die Verbindung, sendet nichts mehr → nach Ablauf der Zeitobergrenze startet der Chip neu — 2026-09-13 (Download 00:42:17, ~120 Wiederaufnahmeversuche im 5-s-Takt mit je null Bytes, Obergrenze exakt bei 600000 ms um 00:52:17, Reset um 00:52:56 nach dem 20-s-Watchdog, danach `boot #1` ohne Werksreset) - [x] Wiederaufnahme nach Abriss: Gegenstelle erreichbar, sendet aber nichts mehr → Lampe setzt per `Range` fort — 2026-09-13 (fünf Fortsetzungen 263700 → 198164 → 132628 → 67092 → 1556, Image vollständig; gilt **nicht** für eine tote Gegenstelle, siehe README) - [x] Echter Rettungsfall: Lampe ohne gültige WLAN-Konfiguration findet die Station und wird geflasht — 2026-09-13, zweistufig belegt: (1) Station aus, Build ohne Zugangsdaten seriell aufgespielt → `ninlight 0.3.7-4-gb169fa`, Scan ohne Treffer, Lampe geht in den Onboarding-AP, über Netz nicht erreichbar. (2) Station an, ein Stromzyklus → `station found` → `recovery image version 0.3.7 (running 0.3.7-4-gb169fa)` → `trailer ok` → Neustart → `connecting to ninIOT`. 23 Sekunden vom Einschalten bis zur geretteten Lampe. - [x] `ninlight-station off`, Pi neu starten → Pi kommt im Heim-WLAN hoch, kein Zugangspunkt, Dienst inaktiv — 2026-09-13 **Bekannte Eigenheit:** Die Lampe lädt bei jedem Boot das komplette Image (~256 KB), auch wenn sie es danach wegen gleicher Version verwirft — die Version steht im Trailer am Dateiende. Kein Fehler, aber die Station sieht pro Boot einen vollen Transfer. ## Migration echter Lampen (Plan 4b, 2026-09-13) - [x] G95 (Dev-Build, Product-ID `-klyqa.lighting.cw-ww.g95-dev`) mit dem **Entwicklungs**schlüssel signiert → angenommen, geflasht, läuft ninlight als `g95_cw` - [x] GU10 (Produktionsbuild, Product-ID `-klyqa.lighting.rgb-cw-ww.gu10`) mit dem **Produktions**schlüssel signiert → angenommen, geflasht, läuft ninlight als `gu10_rgb` - [x] Migrierte Lampe lässt sich anschließend über das normale ninlight-OTA aktualisieren (GU10 auf 0.3.8) ## Migrationsstation (Plan 4c) — Abnahme 2026-09-14 Station: Raspberry Pi Zero W Rev 1.1, Raspberry Pi OS Bookworm 12 (Python 3.11.2, Pillow 9.4.0, armv6), Waveshare 2,13" E-Paper **Revision V3** als HAT, ein Taster auf GPIO23 (noch ohne Funktion). - [x] `setup_pi.sh` richtet einen **nackten** Pi vollständig ein — Pakete, SPI, beide NetworkManager-Profile, Server, CLI, Anzeigedienst. Zweimal von Grund auf durchgelaufen. - [x] SPI wird automatisch eingeschaltet, der Neustart-Hinweis erscheint nur bei echter Änderung. - [x] `deploy.sh` liefert 8 OTA-Images und 16 signierte Migrationsimages aus. - [x] Panel-Revision am Testbild bestimmt: **V3**. - [x] Horchphase: GU10 RGB meldet sich, wird erkannt, bekommt **404** — es floss kein Byte. Protokoll: `/qcx_firmware_-klyqa.lighting.rgb-cw-ww.gu10_V1.1.1.103_ota_light.img 404 0B` Daraus abgeleitet: Ziel `gu10_rgb`, Variante **prod** (kein `-dev`), Lampenversion V1.1.1.103. - [x] Erkennung am Panel sichtbar, vom Bediener bestätigt. - [x] Zeitschalter holt die Station selbsttätig ins Heim-WLAN zurück (3 und 15 Minuten geprüft). - [x] `arm gu10_rgb` freigegeben, Lampe migriert, startet neu, läuft mit `0.3.8-24-geaaef`, per mDNS auffindbar und über REST steuerbar. `last_ota_error: null`. - [x] Migrierte Lampe meldet `color: true`, `color_temp: [2700, 6500]`, `transition_default: 300`, `wake_on_change: false`. - [x] Ausschaltfade ohne Farbrest an der frisch migrierten Lampe — 2026-09-14 bestätigt. - [ ] **Nicht geprüft und weiterhin offen: E27 RGB, E14 (RGB und CW), GU10 CW.** Von diesen Varianten lag kein Exemplar vor. Ihre Pinbelegung stammt aus der alten Firmware und war nie an echter Hardware verifiziert. **Befund während der Abnahme, behoben in `0fe9ca6`:** `ninlight-station off` stoppte den Anzeigedienst, aber kein `on`-Pfad startete ihn wieder — nach dem ersten `off` (das auch der Zeitschalter auslöst) blieb das Panel bis zum Neustart des Pi dunkel. `off` fasst den Anzeigedienst seitdem gar nicht mehr an: er läuft durchgehend und erkennt den ausgeschalteten Zustand selbst (`systemctl is-active ninlight-station`), zeichnet also von sich aus **Station aus**. Der ausdrückliche Diagnosebefehl `ninlight-station display off` bleibt erhalten. Abgesichert durch die Fälle D4 und D4b in `tools/station/tests/test_cli.sh`. **Hardware-Lehre:** Vier Kernel-Paniken an einem Tag, alle mit derselben Fehleradresse und unabhängig von Karte und Konfiguration, kamen von einem Mehrfach-Netzteil, das sein Budget dynamisch neu verhandelt. Mit einem eigenen Netzteil: `throttled=0x0` auch unter Schreiblast. Die zerschossene erste SD-Karte war die Folge, nicht die Ursache.