docs: Recovery-Zeitobergrenze, aktiven Boot-Scan und offene Pruefpunkte nachziehen

sdk/PATCHES.md, README.md und docs/hardware-checklist.md beschrieben teils
einen ueberholten Stand: die Watchdog-Einschaetzung in PATCHES.md Punkt 4
stammte noch von vor dem Drei-Heartbeat-Gate in app_main.c und vor der
Zeitobergrenze in nl_recovery.c; README behauptete eine harte Obergrenze
ueber dem gesamten Download statt nur ueber einem echten Stillstand; der
Boot-Scan ist aktiv (~3 s), nicht passiv wie an einer Stelle der Spec
beschrieben (Spec bleibt unveraendert). PATCHES.md Punkt 1 ergaenzt den
sys/nl_ota.h-Include in wm_fwup.c, Punkt 5 den fls_lock-printf.

hardware-checklist.md: Recovery-Zeile auf "Gegenstelle sendet nichts mehr"
praezisiert; die beiden Werksreset-Haken aus Plan 1 zurueckgesetzt, weil der
vorgelagerte Recovery-Scan das Boot-Timing verschiebt und sie neu geprueft
werden muessen; acht neue Pruefpunkte fuer Plan 3 ergaenzt (Zweitversuch
nach Ablehnung, REST-Erreichbarkeit waehrend/nach Ablehnung, Lichtverhalten
ueber OTA mit pob=off/on, fw_version gegen Trailer, echter Rettungsfall,
Rettungsstation nach Erfolg angelassen, AP-Modus + auth_all) sowie ein
Betriebshinweis zu NINLIGHT_RECOVERY_PASSWORD/NINLIGHT_TOKEN fuer den
Flottenbau.
This commit is contained in:
Michael Sauer
2026-09-12 03:27:33 +02:00
parent 49d6b329b0
commit 3841a3473c
3 changed files with 50 additions and 20 deletions
+9 -5
View File
@@ -120,11 +120,15 @@ erreichbar ist), sucht sie kurz nach einem Rettungs-Zugangspunkt: SSID/Passwort
`http://192.168.8.1/ninlight_<device>_ota.img` (z.B. `ninlight_evk_ota.img`) — der Dateiname enthält den `http://192.168.8.1/ninlight_<device>_ota.img` (z.B. `ninlight_evk_ota.img`) — der Dateiname enthält den
Gerätetyp, `<device>` ist der Wert von `NINLIGHT_DEVICE` aus dem Build. Gerätetyp, `<device>` ist der Wert von `NINLIGHT_DEVICE` aus dem Build.
Bei jedem Boot scannt die Firmware dafür bis zu ~3 Sekunden nach der Station und loggt das Ergebnis Bei jedem Boot scannt die Firmware dafür bis zu ~3 Sekunden aktiv (`tls_wifi_scan()`, kein passiver
(gefunden, nichts gefunden, oder Scan fehlgeschlagen). Ist keine Station in Reichweite, verlängert sich der Scan) nach der Station und loggt das Ergebnis (gefunden, nichts gefunden, oder Scan fehlgeschlagen).
Boot nur um diese Scan-Zeit; danach startet der normale WLAN-Aufbau wie gewohnt. Über dem Download selbst Ist keine Station in Reichweite, verlängert sich der Boot nur um diese Scan-Zeit; danach startet der
liegt eine harte Zeitobergrenze — läuft sie ab, wird der Watchdog nicht mehr bedient und der Chip startet neu, normale WLAN-Aufbau wie gewohnt. Über dem Download selbst liegt keine harte Zeitobergrenze — ein
statt unbeobachtet hängen zu bleiben; der nächste Boot versucht Recovery erneut. langsamer, aber weiter laufender Transfer füttert den Watchdog über den SDK-eigenen Update-Task mit
und darf beliebig lange dauern. Erst ein echter Stillstand (die Gegenstelle bleibt verbunden, sendet
aber keine Daten mehr, oder eine interne Allokations-Warteschleife hängt) lässt den Watchdog nach
10 Minuten auslaufen; der Chip startet dann neu, statt unbeobachtet zu hängen, und der nächste Boot
versucht Recovery erneut.
**Vertrauensgrenze:** Der Trailer prüft Gerätetyp und SHA256 — das ist kein kryptografisches Signaturverfahren. **Vertrauensgrenze:** Der Trailer prüft Gerätetyp und SHA256 — das ist kein kryptografisches Signaturverfahren.
Der Download läuft über `http://` ohne TLS, und der Recovery-Kanal ist ausschließlich durch das oben genannte Der Download läuft über `http://` ohne TLS, und der Recovery-Kanal ist ausschließlich durch das oben genannte
+12 -3
View File
@@ -17,8 +17,8 @@ Konsole: `tools/monitor.sh /dev/cu.<port>` (115200 8N1). Flashen: `tools/flash.s
- [x] `pob off`: Strom aus/an → bleibt aus; `on` → letzte Farbe - [x] `pob off`: Strom aus/an → bleibt aus; `on` → letzte Farbe
- [x] `pob on` bei ausgeschaltetem Licht: Strom aus/an → an - [x] `pob on` bei ausgeschaltetem Licht: Strom aus/an → an
- [x] `fade 2000 2000` bleibt nach Reboot erhalten (`state`) - [x] `fade 2000 2000` bleibt nach Reboot erhalten (`state`)
- [x] 5× Strom an/aus mit jeweils < 5 s Laufzeit → 3× Blinken, danach Defaults (`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)
- [x] Normaler Boot nach > 5 s Laufzeit → kein Werksreset - [ ] 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] 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) - [x] Getestet am 2026-09-11 mit Firmware 7502f4f am EVK (MAC 28-6D-CD-5C-68-1F)
@@ -46,8 +46,17 @@ Konsole: `tools/monitor.sh /dev/cu.<port>` (115200 8N1). Flashen: `tools/flash.s
- [ ] OTA: kurzer WLAN-Aussetzer während des Downloads (Verbindung kurz kappen und wiederherstellen) → Download wird fortgesetzt, kein sofortiger `download`-Fehler - [ ] OTA: kurzer WLAN-Aussetzer während des Downloads (Verbindung kurz kappen und wiederherstellen) → Download wird fortgesetzt, kein sofortiger `download`-Fehler
- [ ] Recovery: Lampe zieht beim Booten das Image von der Station `ninlight-recovery`, Konsole zeigt Scan-, Join- und Fetch-Meldungen - [ ] Recovery: Lampe zieht beim Booten das Image von der Station `ninlight-recovery`, Konsole zeigt Scan-, Join- und Fetch-Meldungen
- [ ] Recovery: ohne Station verlängert sich der Boot um höchstens ~3 s, Konsole loggt das Scan-Ergebnis (gefunden/nichts gefunden/Scan fehlgeschlagen) - [ ] Recovery: ohne Station verlängert sich der Boot um höchstens ~3 s, Konsole loggt das Scan-Ergebnis (gefunden/nichts gefunden/Scan fehlgeschlagen)
- [ ] Recovery: Download hängt/dauert sehr lange → nach Ablauf der Zeitobergrenze bedient die Firmware den Watchdog nicht mehr, Chip startet neu statt hängen zu bleiben - [ ] 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
- [ ] Einstellungen bleiben über ein OTA hinweg erhalten (WLAN, Name, Fade-Zeiten) - [ ] Einstellungen bleiben über ein OTA hinweg erhalten (WLAN, Name, Fade-Zeiten)
- [ ] Zweiter `fw_update` mit gültigem Image direkt nach einer Ablehnung, ohne zwischenzeitlichen Reboot → läuft durch, `system/info` meldet danach die neue Version
- [ ] Lampe bleibt während und nach einer Ablehnung über REST erreichbar: `system/info` und `device/state` antworten beide weiterhin mit 200
- [ ] 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
- [ ] `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)
- [ ] 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
- [ ] 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)
- [ ] OTA im AP-/Fallback-Modus (`mode=apsta`, kein Router erreichbar) und mit `auth_all = true` → Update läuft durch wie im normalen `sta`-Modus
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) ## Später (Lampen)
- [ ] SM2135EH: Standby-Bit bei allen Kanälen 0 prüfen (Spec §4; alte Firmware setzte es praktisch nie) - [ ] SM2135EH: Standby-Bit bei allen Kanälen 0 prüfen (Spec §4; alte Firmware setzte es praktisch nie)
+29 -12
View File
@@ -8,7 +8,8 @@ Lib/GNU, Bin/secboot.img, Tools/GNU/link_w600_2m.ld (→ ld/) und wm_tool.c (→
`Check_OTA_Image()` durch den Hook `nl_fwup_verify_tail()` (src/sys/nl_fwup_hook.h). Das Image-Format `Check_OTA_Image()` durch den Hook `nl_fwup_verify_tail()` (src/sys/nl_fwup_hook.h). Das Image-Format
(Header + Payload + 512-Byte-Endblock) bleibt unverändert. Die starke Definition des Hooks liegt seit (Header + Payload + 512-Byte-Endblock) bleibt unverändert. Die starke Definition des Hooks liegt seit
Plan 3 in `src/sys/nl_ota.c` (Trailer-Prüfung über `nl_trailer_verify()`); die bisherige `weak`-Variante Plan 3 in `src/sys/nl_ota.c` (Trailer-Prüfung über `nl_trailer_verify()`); die bisherige `weak`-Variante
`src/sys/fwup_hook_default.c` ist entfallen. `src/sys/fwup_hook_default.c` ist entfallen. Seit Plan 3 bindet `wm_fwup.c` außerdem `sys/nl_ota.h`
ein (für den `nl_fwup_verify_tail()`-Hook) — der SDK-Code linkt damit direkt gegen App-Code.
Fix-Runde 1 (Task-4-Review) hat drei weitere Stellen in diesem Bereich korrigiert: Fix-Runde 1 (Task-4-Review) hat drei weitere Stellen in diesem Bereich korrigiert:
- `wm_fwup.c`, Bedingung um `nl_fwup_verify_tail()`: Checksummen- und Trailer-Prüfung liefen in - `wm_fwup.c`, Bedingung um `nl_fwup_verify_tail()`: Checksummen- und Trailer-Prüfung liefen in
einer kurzschließenden `||`-Bedingung; bei falscher Checksumme wurde `nl_fwup_verify_tail()` nie einer kurzschließenden `||`-Bedingung; bei falscher Checksumme wurde `nl_fwup_verify_tail()` nie
@@ -82,17 +83,29 @@ Lib/GNU, Bin/secboot.img, Tools/GNU/link_w600_2m.ld (→ ld/) und wm_tool.c (→
damit nie ausgelöst, weil der Idle-Task munter weiter gefüttert hätte. Der `tls_watchdog_clr()`- damit nie ausgelöst, weil der Idle-Task munter weiter gefüttert hätte. Der `tls_watchdog_clr()`-
Aufruf wurde entfernt; `wfi` bleibt. Der einzige verbleibende `tls_watchdog_clr()`-Aufruf im SDK Aufruf wurde entfernt; `wfi` bleibt. Der einzige verbleibende `tls_watchdog_clr()`-Aufruf im SDK
ist in `Platform/Common/fwup/wm_fwup.c` (Firmware-Update-Task). ist in `Platform/Common/fwup/wm_fwup.c` (Firmware-Update-Task).
**Einschätzung aktualisiert (Task 4, Fix-Runde 1):** Als dieser Punkt geschrieben wurde, gab es noch **Einschätzung aktualisiert (Plan 3, Abschluss-Review):** Als dieser Punkt geschrieben wurde, gab es
keinen OTA-Pfad - das Fenster war rein theoretisch. Mit Task 4 ist es real und kann durch einen noch keinen OTA-Pfad - das Fenster war rein theoretisch. Inzwischen gilt:
langsamen oder hängenden Download mehrere Minuten dauern: Solange `wm_fwup.c` seinen eigenen - Im normalen Betrieb (`app_task()` in `src/app_main.c`) füttert nicht mehr jeder Durchlauf den
`tls_watchdog_clr()` aufruft, wird der Watchdog gefüttert, **egal** ob `light_task`, `nl_wifi`/`nl_mdns` Watchdog, sondern erst, wenn `light_task`, `nl_wifi`/`nl_mdns` und `nl_http_server` seit dem
oder `nl_http_server` in dieser Zeit echt hängen - der App-Task-Heartbeat-Mechanismus in letzten Füttern *alle drei* einen eigenen Beat-Zähler erhöht haben - ein einzelner hängender Task
`src/app_main.c` (der genau das eigentlich verhindern soll) greift währenddessen nicht, weil der (z.B. `nl_app_lock()` für immer gehalten) lässt den Watchdog jetzt tatsächlich auslaufen. Das war
Watchdog gar nicht bei ihm ankommt. Das ist kein Rückschritt gegenüber vorher (vor Task 4 gab es zum Zeitpunkt der ursprünglichen Einschätzung noch nicht gebaut.
dieses Fenster schlicht nicht), aber die bisherige Einstufung „unkritisch" war nur so lange richtig, - Solange `t_http_fwup()` läuft, ruft der SDK-eigene fwup-Task (`Platform/Common/fwup/wm_fwup.c:201`)
wie der Fall real nie eintrat. Kein Fix in dieser Runde - der Watchdog-Heartbeat müsste dafür wissen, bei **jeder** empfangenen Nachricht selbst `tls_watchdog_clr()` auf - unabhängig vom Drei-Heartbeat-
ob der fwup-Task selbst noch vorankommt (z. B. über einen eigenen Beat-Zähler, den `app_task` Gate oben. Das Fenster aus der ursprünglichen Einschätzung besteht also während eines laufenden
zusätzlich zu `light`/`net`/`http` prüft); das ist ein eigenständiger Task für einen späteren Plan. OTA- oder Recovery-Downloads weiterhin: ein `light_task`/`nl_wifi`/`nl_http_server`-Hänger während
dieser Zeit wird vom Watchdog nicht erkannt, weil der fwup-Task ihn unabhängig davon bedient.
- Für den Recovery-Pfad (`src/net/nl_recovery.c`, `NL_RECOVERY_DOWNLOAD_MAX_MS`) gibt es seit Plan 3
zusätzlich eine Obergrenze von 10 Minuten, nach der die Boot-Task selbst aufhört, den Watchdog zu
bedienen - das deckt aber nur einen echten Stillstand ab (Gegenstelle bleibt verbunden, sendet aber
nichts mehr, oder die `pbuf_alloc()`-Warteschleife in `wm_http_fwup.c` hängt), nicht das Fenster
oben: ein *vorankommender* Download hält den Watchdog über `wm_fwup.c` unabhängig von dieser
Obergrenze am Leben. Für den regulären OTA-Pfad (`fw_update` per REST) gibt es keine entsprechende
Obergrenze, da dort ein hängender Download die Lampe im Normalbetrieb belässt statt sie in einen
rettungslosen Boot-Zustand zu bringen.
Kein Fix in dieser Runde - der Watchdog-Heartbeat müsste dafür wissen, ob der fwup-Task selbst noch
vorankommt (z. B. über einen eigenen Beat-Zähler, den `app_task` zusätzlich zu `light`/`net`/`http`
prüft); das ist ein eigenständiger Task für einen späteren Plan.
Getestet mit dem Konsolenkommando `wdtest` Getestet mit dem Konsolenkommando `wdtest`
(src/sys/nl_console_cmd.c, src/sys/nl_console.c): es hält `nl_app_lock()` für immer, wodurch (src/sys/nl_console_cmd.c, src/sys/nl_console.c): es hält `nl_app_lock()` für immer, wodurch
`light_task` und `app_task` blockieren und der Watchdog den Chip zurücksetzen muss. `light_task` und `app_task` blockieren und der Watchdog den Chip zurücksetzen muss.
@@ -102,3 +115,7 @@ Lib/GNU, Bin/secboot.img, Tools/GNU/link_w600_2m.ld (→ ld/) und wm_tool.c (→
`tls_os_sem_create(&s_fls2_lock, 1)` stillschweigend - jetzt eine `printf()`-Meldung `tls_os_sem_create(&s_fls2_lock, 1)` stillschweigend - jetzt eine `printf()`-Meldung
(`TLS_DBGPRT_ERR` ist in diesem Build wegkompiliert), falls das Anlegen fehlschlägt (`TLS_DBGPRT_ERR` ist in diesem Build wegkompiliert), falls das Anlegen fehlschlägt
(dann bliebe der Bereich oberhalb von 1 MB unprotokolliert ungeschützt). (dann bliebe der Bereich oberhalb von 1 MB unprotokolliert ungeschützt).
I5 (Plan 3, Abschluss-Review): Aus demselben Grund (`TLS_DBGPRT_ERR` wegkompiliert) fehlte auch beim
Anlegen von `fls_lock` (dem primären Flash-Semaphor, wenige Zeilen darüber in `tls_fls_init()`) jede
sichtbare Meldung - ein Fehlschlag hätte `tls_fls_init()` mit `TLS_FLS_STATUS_ENOMEM` stumm scheitern
lassen. Jetzt ebenfalls ein `printf()`.