docs: OTA und Recovery dokumentieren
This commit is contained in:
@@ -71,6 +71,66 @@ Verfügbare Befehle in der Konsole: `help`.
|
|||||||
- Bruno: `bruno/ninlight.yml` importieren, Environment „EVK (mDNS)" bzw. „Onboarding-AP" wählen.
|
- Bruno: `bruno/ninlight.yml` importieren, Environment „EVK (mDNS)" bzw. „Onboarding-AP" wählen.
|
||||||
- Konsole: `net` zeigt Modus, SSID, IP, RSSI und den letzten Onboarding-Fehler.
|
- Konsole: `net` zeigt Modus, SSID, IP, RSSI und den letzten Onboarding-Fehler.
|
||||||
|
|
||||||
|
## Firmware-Update
|
||||||
|
|
||||||
|
Der Build erzeugt pro Gerät zwei Images:
|
||||||
|
|
||||||
|
- **`_gz.img`** — komprimiertes App-Image für das serielle Flashen mit `tools/flash.sh` (siehe oben).
|
||||||
|
- **`_ota.img`** — dasselbe `_gz.img`, um einen 512-Byte-Trailer ergänzt (`tools/mkimage/nl_mkimage.py`,
|
||||||
|
automatisch bei jedem Build via `cmake/run_mkimage.cmake`). Der Trailer enthält Gerätetyp, Firmware-Version
|
||||||
|
und den SHA256 über das komplette `_gz.img` — das ist genau der Hash, den die Firmware beim Schreiben ins
|
||||||
|
Flash prüft.
|
||||||
|
|
||||||
|
Netz-Update über die REST-API:
|
||||||
|
|
||||||
|
curl -X PUT http://ninlight-681f.local:3333/api/v1/system/command \
|
||||||
|
-H 'Authorization: aabbccddeeff0011223344' \
|
||||||
|
-d '{"command":{"type":"fw_update","url":"http://<host-ip>:8000/pfad/zur/ninlight_evk_ota.img"}}'
|
||||||
|
|
||||||
|
Antwort `202 {"status":"updating"}` startet den Download im Hintergrund; ein per HTTP-Server bereitgestelltes
|
||||||
|
`_ota.img` reicht (z.B. `python3 -m http.server 8000`). Läuft der Trailer- bzw. Hash-Check durch, resettet die
|
||||||
|
Lampe und bootet mit der neuen Version. Wird das Image abgelehnt, gibt es **keinen** Reset — die Lampe läuft
|
||||||
|
mit der alten Firmware weiter und trägt den Grund in `last_ota_error` (`GET system/info`) ein. Ein
|
||||||
|
Verbindungsabbruch mitten im Download löst dagegen einen Wiederaufnahmeversuch per HTTP-Range aus (begrenzte
|
||||||
|
Anzahl Runden); erst wenn auch das ausgeht, landet der Versuch bei `last_ota_error = download`.
|
||||||
|
|
||||||
|
Werte von `last_ota_error` (`null`, solange nichts fehlgeschlagen ist):
|
||||||
|
|
||||||
|
| Wert | Bedeutung |
|
||||||
|
|---|---|
|
||||||
|
| `download` | Verbindung/Transfer gescheitert (auch nach ausgeschöpftem Wiederaufnahmeversuch) |
|
||||||
|
| `crc` | Prüfsumme des SDK-eigenen Update-Mechanismus stimmt nicht |
|
||||||
|
| `trailer` | Trailer fehlt, ist zu kurz oder hat eine falsche Magic/Version |
|
||||||
|
| `device_mismatch` | Trailer nennt einen anderen Gerätetyp |
|
||||||
|
| `hash` | SHA256 im Trailer stimmt nicht mit dem übertragenen Image überein |
|
||||||
|
| `busy` | es läuft bereits ein Update, oder die übergebene URL war ungültig |
|
||||||
|
|
||||||
|
Ein erneuter `fw_update`-Aufruf mit einem gültigen Image funktioniert nach einer Ablehnung sofort, ohne
|
||||||
|
manuellen Reboot.
|
||||||
|
|
||||||
|
## Recovery
|
||||||
|
|
||||||
|
Findet die Firmware beim Booten keinen Weg, sich selbst zu aktualisieren (z.B. weil das reguläre WLAN nicht
|
||||||
|
erreichbar ist), sucht sie kurz nach einem Rettungs-Zugangspunkt: SSID/Passwort sind Build-Parameter
|
||||||
|
(`NINLIGHT_RECOVERY_SSID`/`NINLIGHT_RECOVERY_PASSWORD` in `config.local.cmake`, Default für beide
|
||||||
|
`ninlight-recovery`). Wird die Station gefunden, verbindet sich die Lampe und lädt
|
||||||
|
`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.
|
||||||
|
|
||||||
|
Bei jedem Boot scannt die Firmware dafür bis zu ~3 Sekunden nach der Station und loggt das Ergebnis
|
||||||
|
(gefunden, nichts gefunden, oder Scan fehlgeschlagen). Ist keine Station in Reichweite, verlängert sich der
|
||||||
|
Boot nur um diese Scan-Zeit; danach startet der normale WLAN-Aufbau wie gewohnt. Über dem Download selbst
|
||||||
|
liegt eine harte Zeitobergrenze — läuft sie ab, wird der Watchdog nicht mehr bedient und der Chip startet neu,
|
||||||
|
statt unbeobachtet hängen zu bleiben; der nächste Boot versucht Recovery erneut.
|
||||||
|
|
||||||
|
**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
|
||||||
|
WLAN-Passwort geschützt, das mit einem öffentlichen Default im Repo steht. Wer im selben WLAN ist und das
|
||||||
|
API-Token kennt (bzw. beim Recovery in Funkreichweite ist und das Passwort kennt), kann der Lampe beliebige
|
||||||
|
Firmware unterschieben. Das ist eine bewusste Entscheidung dieses Projekts: Ziel ist, die Signaturprüfung der
|
||||||
|
alten, abgekündigten Original-Firmware zu umgehen, damit die Lampen überhaupt weiter nutzbar bleiben — nicht,
|
||||||
|
ein neues, gehärtetes Sicherheitsmodell zu bauen.
|
||||||
|
|
||||||
## Werksreset
|
## Werksreset
|
||||||
|
|
||||||
Gerät 5× hintereinander einschalten, jeweils kürzer als 5 Sekunden.
|
Gerät 5× hintereinander einschalten, jeweils kürzer als 5 Sekunden.
|
||||||
|
|||||||
+8
-1
@@ -11,6 +11,8 @@ config:
|
|||||||
value: '3333'
|
value: '3333'
|
||||||
- name: device_token
|
- name: device_token
|
||||||
value: aabbccddeeff0011223344
|
value: aabbccddeeff0011223344
|
||||||
|
- name: ota_url
|
||||||
|
value: http://192.168.1.12:8000/ninlight_evk_ota.img
|
||||||
- name: Onboarding-AP
|
- name: Onboarding-AP
|
||||||
variables:
|
variables:
|
||||||
- name: device_address
|
- name: device_address
|
||||||
@@ -19,6 +21,8 @@ config:
|
|||||||
value: '3333'
|
value: '3333'
|
||||||
- name: device_token
|
- name: device_token
|
||||||
value: aabbccddeeff0011223344
|
value: aabbccddeeff0011223344
|
||||||
|
- name: ota_url
|
||||||
|
value: http://192.168.1.12:8000/ninlight_evk_ota.img
|
||||||
items:
|
items:
|
||||||
- info:
|
- info:
|
||||||
name: Device
|
name: Device
|
||||||
@@ -205,6 +209,9 @@ items:
|
|||||||
timeout: 0
|
timeout: 0
|
||||||
followRedirects: true
|
followRedirects: true
|
||||||
maxRedirects: 5
|
maxRedirects: 5
|
||||||
|
docs: |-
|
||||||
|
Enthält `last_ota_error`: `null`, solange kein Update fehlgeschlagen ist, sonst einer von
|
||||||
|
`download`, `crc`, `trailer`, `device_mismatch`, `hash`, `busy`. Kein `reset_reason` mehr im Feld-Satz.
|
||||||
- info:
|
- info:
|
||||||
name: Get System Settings
|
name: Get System Settings
|
||||||
type: http
|
type: http
|
||||||
@@ -372,7 +379,7 @@ items:
|
|||||||
{
|
{
|
||||||
"command": {
|
"command": {
|
||||||
"type": "fw_update",
|
"type": "fw_update",
|
||||||
"url": "http://192.168.1.12:8000/ninlight_evk_ota.img"
|
"url": "{{ota_url}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
settings:
|
settings:
|
||||||
|
|||||||
@@ -38,6 +38,17 @@ Konsole: `tools/monitor.sh /dev/cu.<port>` (115200 8N1). Flashen: `tools/flash.s
|
|||||||
- [x] C1: `curl -d '[[[[[[[[[[[[[[[[[[[[' http://<ip>:3333/api/v1/device/state` → 400, Lampe läuft unverändert weiter (kein Absturz/Reboot) — 2026-09-12
|
- [x] C1: `curl -d '[[[[[[[[[[[[[[[[[[[[' http://<ip>: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-<mac4>` darf nicht wiederkommen (Scan am Mac) — 2026-09-12 (Reboot geprüft, Router-Neustart offen)
|
- [x] C2: Onboarding ok, 60 s warten, dann Reboot und Router-Neustart → AP `ninlight-<mac4>` darf nicht wiederkommen (Scan am Mac) — 2026-09-12 (Reboot geprüft, Router-Neustart offen)
|
||||||
|
|
||||||
|
## OTA und Recovery (Plan 3)
|
||||||
|
- [ ] OTA: Update über `fw_update` läuft durch, danach meldet `system/info` die neue Version
|
||||||
|
- [ ] OTA: Image eines anderen Gerätetyps wird sofort abgelehnt (`last_ota_error` = `device_mismatch`), kein Reset, Lampe läuft mit der alten Firmware weiter
|
||||||
|
- [ ] OTA: Image mit verfälschtem Byte wird abgelehnt (`last_ota_error` = `hash` oder `crc`), kein Reset
|
||||||
|
- [ ] OTA: HTTP-Server während des Downloads abschalten → Wiederaufnahmeversuch (Range-Resume) sichtbar in der Konsole, nach Ausschöpfen der Runden `last_ota_error` = `download`, Lampe bootet normal
|
||||||
|
- [ ] 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: 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
|
||||||
|
- [ ] Einstellungen bleiben über ein OTA hinweg erhalten (WLAN, Name, Fade-Zeiten)
|
||||||
|
|
||||||
## 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)
|
||||||
- [ ] E27 Color (SM2135EH): RGB und WY getrennt, Ströme plausibel (nicht zu heiß)
|
- [ ] E27 Color (SM2135EH): RGB und WY getrennt, Ströme plausibel (nicht zu heiß)
|
||||||
|
|||||||
Reference in New Issue
Block a user