C2: tls_wifi_softap_create() setzt das SOFTAP-Bit (IEEE80211_MODE_AP = 4) in TLS_PARAM_ID_WPROTOCOL und schreibt es in den Flash; tls_wifi_softap_destroy() löscht es nie wieder. Solange das Bit gesetzt bleibt, erstellt sys_net_status_changed() (sdk/Platform/Sys/tls_sys.c:519-525) bei jedem NETIF_IP_NET_UP über tls_auto_reconnect_softap() erneut den Onboarding-AP mit den SDK-eigenen (öffentlichen Default-)Parametern - unabhängig vom Auto-Connect-Flag. Nach jedem Onboarding/Fallback käme der AP `ninlight-<mac4>` mit dem öffentlichen Default-Passwort bei jedem Reconnect/Reboot zurück, offen fürs LAN (SDK-NAPT). Verifiziert in sdk/Platform/Sys/tls_sys.c, sdk/Include/Platform/wm_params.h und sdk/Platform/Common/Params/wm_param.c: - TLS_PARAM_ID_WPROTOCOL (10) ist ein u8, TLS_PARAM_IEEE80211_SOFTAP == 4 == IEEE80211_MODE_AP (sdk/Include/WiFi/wm_wifi.h). - tls_param_get()/tls_param_set(id, argv, to_flash) - genau die Signaturen aus dem Finding. - tls_param_to_default() (tls_param_load_factory_default() + param_to_flash()) arbeitet ausschließlich auf `struct tls_sys_param` (Wi-Fi/Netzwerk-Konfig); es liest/schreibt nirgends die MAC-Adresse oder RF-Kalibrierdaten - die liegen in einer komplett getrennten, efuse-gestützten FT_PARAM_ST-Struktur in sdk/Platform/Drivers/efuse/wm_efuse.c. tls_param_to_default() ist damit für den Werksreset sicher. Fix (nl_wifi_w600.c): 1. clear_softap_param(): liest TLS_PARAM_ID_WPROTOCOL, löscht bei gesetztem IEEE80211_MODE_AP-Bit und persistiert das (to_flash=TRUE). Aufgerufen in nl_wifi_start() (vor allem anderen) und nach jedem tls_wifi_softap_destroy(). 2. nl_wifi_start(): zerstört einen bereits vom SDK selbst erzeugten SoftAP (tls_wifi_softap_get_state() != WM_WIFI_DISCONNECTED), bevor unsere eigene State Machine startet. 3. Backstop im Netzwerk-Task: NETIF_WIFI_SOFTAP_SUCCESS, während die State Machine sagt, der AP solle unten sein (s_sm.ap_up == false) -> AP wieder zerstören und Param erneut löschen (geloggt). 4. Werksreset (nl_app_factory_reset, src/app_main.c): setzt zusätzlich tls_param_to_default() (siehe Verifikation oben) und dokumentiert das inline. Nebenbei im selben Treiber erledigt, weil sie denselben Codepfad betreffen: - M2: connect_station() loggt den Rückgabewert von tls_wifi_connect(), wenn er nicht WM_SUCCESS ist. - M3: start_ap() merkt sich einen fehlgeschlagenen tls_wifi_softap_create() (`s_ap_create_pending`) und wiederholt ihn alle 5 s, solange die State Machine den AP oben haben will. Nicht host-testbar (bindet SDK-Header ein); über den Firmware-Build verifiziert (baut sauber für evk, keine neuen Warnungen).
ninlight
Lokale Firmware für die alten Klyqa-Lampen mit WinnerMicro W600 (E27/E14/GU10 RGB+CW, E27/E14/GU10 CW, G95 Vintage) und das W600-EVK.
Voraussetzungen
- Docker Desktop (primärer Build-Pfad, erste Ausführung baut das Image
ninlight-build) - CMake ≥ 3.20, Ninja, host C compiler (für Host-Tests)
- uv (für die serielle Konsole)
- ARM GNU Toolchain mit newlib:
brew install --cask gcc-arm-embedded(nur Fallback für nativen Build)
Bauen
Docker (empfohlen – kein lokaler ARM-Compiler nötig):
tools/docker.sh cmake -S . -B build/evk -G Ninja -DCMAKE_TOOLCHAIN_FILE=cmake/arm-none-eabi.cmake -DNINLIGHT_DEVICE=evk
tools/docker.sh cmake --build build/evk
tools/docker.sh tools/build_all.sh # alle 8 Geräte
Für die tägliche Entwicklung: nur das Gerät bauen, das du verwendest (z.B. tools/docker.sh cmake --build build/evk). Der emulierte Docker-Build ist langsam — build_all.sh ist nur für Releases gedacht.
Native Builds (mit lokal installierter ARM-Toolchain):
cmake -S . -B build/evk -G Ninja -DCMAKE_TOOLCHAIN_FILE=cmake/arm-none-eabi.cmake -DNINLIGHT_DEVICE=evk
cmake --build build/evk
Geräte: evk, e27_rgb, e14_rgb, gu10_rgb, e27_cw, e14_cw, gu10_cw, g95_cw.
Lokale Einstellungen (Token, später WLAN): config.local.cmake.example → config.local.cmake.
Tests
Host-basierte Unit-Tests (laufen nativ auf dem Mac):
tools/test_host.sh
Flashen und Konsole
Verfügbare Ports anzeigen:
build/host-tools/wm_tool -l
Firmware flashen (Standard: App-Image):
tools/flash.sh <device> <port>
Das Skript druckt Bitte bei "please manually reset the device" die Reset-Taste drücken. — wenn wm_tool diese Nachricht zeigt, musst du die Reset-Taste am EVK drücken (Baud 460800). Für blank/erased Chips: tools/flash.sh <device> <port> full (flasht stattdessen das Vollbild mit secboot, auf Hardware noch ungetestet).
Serielle Konsole (115200 8N1, lokales Echo):
tools/monitor.sh /dev/cu.<port>
Verfügbare Befehle in der Konsole: help.
Netzwerk & REST
-
WLAN fest einbauen:
NINLIGHT_WIFI_SSID/NINLIGHT_WIFI_PASSWORDinconfig.local.cmake(gespeicherte Einstellungen haben Vorrang). -
Ohne WLAN-Daten (oder nach Werksreset) öffnet die Lampe den AP
ninlight-<mac4>(PasswortNINLIGHT_AP_PASSWORD, Defaultninlight) und pulsiert warmweiß. Onboarding:curl -X PUT http://192.168.4.1:3333/api/v1/system/settings \ -d '{"type":"wifi","ssid":"MeinWLAN","password":"geheim123"}' curl http://192.168.4.1:3333/api/v1/system/info # wifi.sta_ip / wifi.errorNach Erfolg schließt der AP nach 60 s. Ohne Verbindung für 5 min öffnet die Lampe den AP zusätzlich (Fallback).
-
REST auf Port 3333, Präfix
/api/v1:GET|POST device/state,GET system/info,GET|PUT system/settingsundPUT system/command(beide mit HeaderAuthorization: <token>, Default-Tokenaabbccddeeff0011223344). -
Beispiel:
curl -X POST http://ninlight-681f.local:3333/api/v1/device/state -d '{"on":true,"brightness":40,"color_temp":2700}' -
Suchen:
dns-sd -B _ninlight._tcp(macOS), Detailsdns-sd -L ninlight-681f _ninlight._tcp. -
Bruno:
bruno/ninlight.ymlimportieren, Environment „EVK (mDNS)" bzw. „Onboarding-AP" wählen. -
Konsole:
netzeigt Modus, SSID, IP, RSSI und den letzten Onboarding-Fehler.
Werksreset
Gerät 5× hintereinander einschalten, jeweils kürzer als 5 Sekunden.
Hardwaretests
Manuelle Checkliste: docs/hardware-checklist.md.