C1: nl_recovery_try() verglich nichts mit der laufenden Firmware. Vor einer
laufenden Rettungsstation - laut Spec 9 der Normalfall, solange eine Charge
migriert - lud die Lampe bei jedem Boot dasselbe Image erneut und startete neu.
nl_ota_start_recovery() setzt jetzt ein Flag, das nl_fwup_verify_tail() nur im
Recovery-Pfad auswerten lässt: Trägt der Trailer dieselbe Version wie die
laufende Firmware (nl_trailer_version(), bisher ungenutzt), wird das Image
abgelehnt und der Boot läuft normal weiter. Ein absichtliches Neuflashen
derselben Version über die REST-API bleibt möglich. Kein neuer Wert im
last_ota_error-Vokabular (Spec 8.2); unterschieden wird über die Konsole.
C1/I2: boot_ms wurde erst nach nl_recovery_try() gesetzt und der
Power-Cycle-Zähler erst 5 s danach gelöscht - bei erfolgreicher Recovery also
nie. Nach NL_FACTORY_RESET_BOOTS Runden folgte ein Werksreset. boot_ms startet
jetzt am Anfang von app_task (damit gilt wieder das in Spec 3.4/5.5 zugesagte
5-s-Fenster statt faktisch ~8 s), und nl_app_clear_reset_counter() löscht den
Zähler zusätzlich, sobald die Rettungsstation gefunden ist.
I3: tls_sys_auto_mode_run() stößt direkt nach UserMain() einen SDK-eigenen
Verbindungsversuch an; das dabei entstehende NETIF_IP_NET_UP war in
recovery_join_event() nicht vom eigenen Join zu unterscheiden, die Lampe hätte
im falschen Netz geladen. nl_wifi_disable_sdk_auto_connect() schaltet das Flag
ab und kappt eine bestehende Verbindung jetzt am Anfang von nl_recovery_try();
zusätzlich prüft nl_wifi_join_blocking() über tls_wifi_get_current_bss(), dass
die Verbindung wirklich zur erwarteten SSID gehört.
- wm_internal_fls.c: TLS_DBGPRT_ERR ist in diesem Build wegkompiliert, daher
printf() für die fehlgeschlagene s_fls2_lock-Anlage; PATCHES.md korrigiert.
- nl_wifi_w600.c: der SoftAP-Backstop (Zerstören eines unerwarteten SDK-Softap)
lief bisher innerhalb von lock()/unlock() - tls_wifi_softap_destroy() und
clear_softap_param() greifen aber auf SDK-Zustand zu, nicht auf s_sm/die
Credential-Puffer, die s_lock eigentlich schützt. Jetzt wird im Lock nur noch
ein lokales Flag gesetzt, die SDK-Aufrufe laufen danach.
- clear_softap_param() liest TLS_PARAM_ID_WPROTOCOL jetzt aus SRAM (FALSE)
statt Flash (TRUE) - das Schreiben bleibt weiterhin geflasht.
- app_main.c füttert den Watchdog erst, wenn Licht-, Netz- und HTTP-Task
jeweils seit dem letzten Feed einen Fortschritt gemeldet haben. Dafür
bekommen nl_wifi_w600.c (nl_net_beat) und nl_http_server.c (nl_http_beat)
je einen volatile Heartbeat-Zähler; der HTTP-Listen-Socket bekommt ein
SO_RCVTIMEO von 2 s auf accept(), damit er auch ohne Anfragen tickt.