feat(wifi): Standalone-AP im Treiber und im Startpfad

This commit is contained in:
Michael Sauer
2026-10-02 00:52:52 +02:00
parent e36add5030
commit 18eb7b5c01
5 changed files with 198 additions and 20 deletions
+26 -3
View File
@@ -111,6 +111,19 @@ int nl_app_set_wifi_credentials(const char *ssid, const char *pass)
return nl_app_save_settings(); return nl_app_save_settings();
} }
int nl_app_clear_standalone(void)
{
bool was_set;
nl_app_lock();
was_set = s_settings.standalone != 0;
s_settings.standalone = 0;
nl_app_unlock();
if (!was_set)
return 0; /* nichts zu schreiben - der Flash-Satz steht schon richtig */
return nl_app_save_settings();
}
void nl_app_factory_reset(void) void nl_app_factory_reset(void)
{ {
printf("factory reset\r\n"); printf("factory reset\r\n");
@@ -228,10 +241,20 @@ static void app_task(void *arg)
boot = s_settings; boot = s_settings;
nl_app_unlock(); nl_app_unlock();
nl_mdns_set_name(boot.name); nl_mdns_set_name(boot.name);
if (boot.wifi_ssid[0]) if (boot.standalone)
nl_wifi_start(boot.wifi_ssid, boot.wifi_pass, NINLIGHT_AP_PASSWORD); /* Kein Verbindungsversuch: die Lampe steht ausdruecklich allein. Die gespeicherten
* Zugangsdaten gehen trotzdem mit - der Standalone-Zweig der Zustandsmaschine
* verbindet nicht, aber sie zaehlen wieder, sobald der Modus ueber die API
* zurueckgenommen wird; ohne sie landete die Lampe dann im offenen
* Onboarding-AP, obwohl ihr WLAN bekannt ist. */
nl_wifi_start(boot.wifi_ssid, boot.wifi_pass,
boot.ap_pass[0] ? boot.ap_pass : NINLIGHT_AP_PASSWORD,
boot.ap_ssid, boot.ap_channel, true, boot.standalone_pulse != 0);
else if (boot.wifi_ssid[0])
nl_wifi_start(boot.wifi_ssid, boot.wifi_pass, NINLIGHT_AP_PASSWORD, "", 0, false, false);
else else
nl_wifi_start(NINLIGHT_WIFI_SSID, NINLIGHT_WIFI_PASSWORD, NINLIGHT_AP_PASSWORD); nl_wifi_start(NINLIGHT_WIFI_SSID, NINLIGHT_WIFI_PASSWORD, NINLIGHT_AP_PASSWORD,
"", 0, false, false);
} }
nl_http_server_start(nl_app_api_ctx()); nl_http_server_start(nl_app_api_ctx());
+13 -2
View File
@@ -19,8 +19,12 @@ typedef struct {
* sure the network task is still alive, not just the light task. */ * sure the network task is still alive, not just the light task. */
extern volatile uint32_t nl_net_beat; extern volatile uint32_t nl_net_beat;
/* Starts the network task. An empty ssid starts the onboarding AP. Strings are copied. */ /* Starts the network task. An empty ssid starts the onboarding AP. Strings are copied.
void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass); * Ein leerer ap_ssid heisst "wie der Hostname", ap_channel 0 heisst Kanal 6. Mit
* standalone == true wird gar nicht erst verbunden: die Lampe oeffnet ihren eigenen,
* passwortgeschuetzten AP, pulse laesst sie dabei kurz als Lebenszeichen pulsen. */
void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass,
const char *ap_ssid, uint8_t ap_channel, bool standalone, bool pulse);
/* Switches the SDK's own auto-connect off and drops any connection it may already have brought /* Switches the SDK's own auto-connect off and drops any connection it may already have brought
* up on its own. nl_wifi_start() does this too, but the boot recovery path runs before that and * up on its own. nl_wifi_start() does this too, but the boot recovery path runs before that and
* must not have the SDK joining its stored AP in parallel (I3, final review). Safe to call any * must not have the SDK joining its stored AP in parallel (I3, final review). Safe to call any
@@ -34,6 +38,13 @@ void nl_wifi_disable_sdk_auto_connect(void);
bool nl_wifi_join_blocking(const char *ssid, const char *pass, uint32_t timeout_ms); bool nl_wifi_join_blocking(const char *ssid, const char *pass, uint32_t timeout_ms);
/* Starts an onboarding test with new credentials (from the HTTP task). 0 = queued. */ /* Starts an onboarding test with new credentials (from the HTTP task). 0 = queued. */
int nl_wifi_request_onboard(const char *ssid, const char *pass); int nl_wifi_request_onboard(const char *ssid, const char *pass);
/* Schaltet den Standalone-Modus zur Laufzeit ein oder aus (aus dem HTTP-Task). Die Werte
* werden kopiert und erst im Netz-Task wirksam, nachdem die Antwort raus ist; ein leeres
* ssid/pass bzw. channel == 0 bedeutet jeweils die Vorgabe. 0 = uebernommen, sonst 503. */
int nl_wifi_request_standalone(bool enabled, const char *ssid, const char *pass,
uint8_t channel, bool pulse);
/* True, solange die Lampe in ihrem eigenen Dauer-AP steht. */
bool nl_wifi_is_standalone(void);
/* True while the unconfigured onboarding AP is open (API without token). */ /* True while the unconfigured onboarding AP is open (API without token). */
bool nl_wifi_open_access(void); bool nl_wifi_open_access(void);
void nl_wifi_status(nl_wifi_status_t *out); void nl_wifi_status(nl_wifi_status_t *out);
+140 -15
View File
@@ -16,8 +16,12 @@
#define NL_NET_PRIO 35 #define NL_NET_PRIO 35
#define NL_NET_QUEUE 8 #define NL_NET_QUEUE 8
#define MSG_NETIF 0x100u /* | netif status */ #define MSG_NETIF 0x100u /* | netif status */
#define MSG_ONBOARD 0x200u #define MSG_ONBOARD 0x200u
/* 0x400, nicht 0x300: die netif-Meldungen werden weiter unten mit (m & MSG_NETIF) erkannt,
* und 0x300 traegt das 0x100-Bit mit - die Standalone-Meldung waere als netif-Ereignis
* protokolliert worden. */
#define MSG_STANDALONE 0x400u
static u32 s_stack[768]; static u32 s_stack[768];
static tls_os_queue_t *s_queue; static tls_os_queue_t *s_queue;
@@ -29,6 +33,18 @@ static char s_hostname[24];
static char s_ssid[NL_SSID_LEN], s_pass[NL_PASS_LEN]; static char s_ssid[NL_SSID_LEN], s_pass[NL_PASS_LEN];
static char s_test_ssid[NL_SSID_LEN], s_test_pass[NL_PASS_LEN]; static char s_test_ssid[NL_SSID_LEN], s_test_pass[NL_PASS_LEN];
static char s_ap_pass[NL_PASS_LEN]; static char s_ap_pass[NL_PASS_LEN];
static char s_ap_ssid[NL_SSID_LEN]; /* leer = Hostname */
static uint8_t s_ap_channel = 6;
/* Die Daten, mit denen der laufende AP tatsaechlich aufgebaut wurde. Nur so ist zu sehen,
* ob ein Wunsch aus der API den AP wirklich veraendert - die Zustandsmaschine liefert kein
* start_ap, wenn der AP schon steht, und die alten Daten blieben sonst still in Kraft. */
static char s_ap_live_ssid[NL_SSID_LEN], s_ap_live_pass[NL_PASS_LEN];
static uint8_t s_ap_live_channel;
static bool s_ap_live;
/* Vom HTTP-Task gefuellt, vom Netz-Task uebernommen; beides unter s_lock. */
static char s_req_ap_ssid[NL_SSID_LEN], s_req_ap_pass[NL_PASS_LEN];
static uint8_t s_req_ap_channel;
static bool s_req_enabled, s_req_pulse, s_req_pending;
static bool s_ap_create_pending; /* M3: tls_wifi_softap_create() failed, retry it */ static bool s_ap_create_pending; /* M3: tls_wifi_softap_create() failed, retry it */
static uint32_t s_ap_create_retry_at; static uint32_t s_ap_create_retry_at;
@@ -88,18 +104,43 @@ void nl_wifi_disable_sdk_auto_connect(void)
tls_wifi_disconnect(); tls_wifi_disconnect();
} }
/* Reisst den AP ab und raeumt dabei auch die Buchfuehrung auf. Laeuft nur im Netz-Task
* (oder vor dessen Start) und nie unter s_lock - clear_softap_param() fasst SDK-Zustand an. */
static void destroy_ap(void)
{
tls_wifi_softap_destroy();
s_ap_create_pending = false;
s_ap_live = false;
clear_softap_param(); /* C2: every softap teardown clears the persisted SOFTAP bit */
}
static void start_ap(void) static void start_ap(void)
{ {
struct tls_softap_info_t ap; struct tls_softap_info_t ap;
struct tls_ip_info_t ip; struct tls_ip_info_t ip;
const char *ssid = s_ap_ssid[0] ? s_ap_ssid : s_hostname;
size_t n = strlen(s_ap_pass); size_t n = strlen(s_ap_pass);
int r; int r;
/* Steht derselbe AP schon, bleibt er stehen: die Zustandsmaschine verlangt start_ap
* auch dann, wenn sie den AP gar nicht erst abgebaut hat (fehlgeschlagener
* Onboarding-Versuch aus dem Standalone-Modus), und ein Neuaufbau wuerfe dort alle
* Clients ohne Not heraus. */
if (s_ap_live && strcmp(s_ap_live_ssid, ssid) == 0 && strcmp(s_ap_live_pass, s_ap_pass) == 0 &&
s_ap_live_channel == s_ap_channel)
return;
/* Umgekehrt: Name, Passwort oder Kanal haben sich geaendert, waehrend der AP steht.
* Ohne eigenen Abriss liefe er mit den alten Daten weiter. */
if (s_ap_live) {
printf("wifi: AP data changed, rebuilding\r\n");
destroy_ap();
}
memset(&ap, 0, sizeof ap); memset(&ap, 0, sizeof ap);
memset(&ip, 0, sizeof ip); memset(&ip, 0, sizeof ip);
copy((char *)ap.ssid, s_hostname, sizeof ap.ssid); copy((char *)ap.ssid, ssid, sizeof ap.ssid);
ap.encrypt = IEEE80211_ENCRYT_CCMP_WPA2; ap.encrypt = IEEE80211_ENCRYT_CCMP_WPA2;
ap.channel = 6; ap.channel = s_ap_channel;
ap.keyinfo.format = 1; /* ascii */ ap.keyinfo.format = 1; /* ascii */
ap.keyinfo.index = 1; ap.keyinfo.index = 1;
ap.keyinfo.key_len = (u8)n; ap.keyinfo.key_len = (u8)n;
@@ -108,10 +149,17 @@ static void start_ap(void)
ip.netmask[0] = 255; ip.netmask[1] = 255; ip.netmask[2] = 255; ip.netmask[3] = 0; ip.netmask[0] = 255; ip.netmask[1] = 255; ip.netmask[2] = 255; ip.netmask[3] = 0;
memcpy(ip.dnsname, "local", 6); memcpy(ip.dnsname, "local", 6);
r = tls_wifi_softap_create(&ap, &ip); r = tls_wifi_softap_create(&ap, &ip);
printf("wifi: AP %s %s\r\n", s_hostname, r == WM_SUCCESS ? "up" : "failed"); printf("wifi: AP %s (ch %u) %s\r\n", ssid, (unsigned)s_ap_channel,
r == WM_SUCCESS ? "up" : "failed");
/* M3: retry a failed AP create instead of leaving onboarding/fallback without an AP. */ /* M3: retry a failed AP create instead of leaving onboarding/fallback without an AP. */
s_ap_create_pending = r != WM_SUCCESS; s_ap_create_pending = r != WM_SUCCESS;
s_ap_create_retry_at = nl_app_now_ms() + 5000u; s_ap_create_retry_at = nl_app_now_ms() + 5000u;
s_ap_live = r == WM_SUCCESS;
if (s_ap_live) {
copy(s_ap_live_ssid, ssid, sizeof s_ap_live_ssid);
copy(s_ap_live_pass, s_ap_pass, sizeof s_ap_live_pass);
s_ap_live_channel = s_ap_channel;
}
} }
static void connect_station(bool test) static void connect_station(bool test)
@@ -149,6 +197,12 @@ static void execute(nl_wifi_actions_t a)
printf("wifi: onboarding ok, saving credentials for %s\r\n", ssid); printf("wifi: onboarding ok, saving credentials for %s\r\n", ssid);
nl_app_set_wifi_credentials(ssid, pass); nl_app_set_wifi_credentials(ssid, pass);
} }
if (a.clear_standalone) {
/* Sonst stuende die Lampe nach dem naechsten Neustart wieder in ihrem eigenen AP,
* obwohl sie gerade erfolgreich ins WLAN gewechselt ist. */
printf("wifi: standalone mode cleared\r\n");
nl_app_clear_standalone();
}
if (a.pulse_on) if (a.pulse_on)
pulse(true); pulse(true);
if (a.pulse_off) if (a.pulse_off)
@@ -157,9 +211,7 @@ static void execute(nl_wifi_actions_t a)
start_ap(); start_ap();
if (a.stop_ap) { if (a.stop_ap) {
printf("wifi: AP down\r\n"); printf("wifi: AP down\r\n");
tls_wifi_softap_destroy(); destroy_ap();
s_ap_create_pending = false;
clear_softap_param(); /* C2: every softap teardown clears the persisted SOFTAP bit */
} }
if (a.disconnect) { if (a.disconnect) {
printf("wifi: dropping stray/failed station attempt\r\n"); printf("wifi: dropping stray/failed station attempt\r\n");
@@ -186,6 +238,7 @@ static void net_task(void *arg)
nl_wifi_actions_t ev, tick; nl_wifi_actions_t ev, tick;
uint32_t m = 0, now; uint32_t m = 0, now;
bool got_ip_event = false, connected_now, ap_wanted, unexpected_softap; bool got_ip_event = false, connected_now, ap_wanted, unexpected_softap;
bool recheck_ap = false;
memset(&ev, 0, sizeof ev); memset(&ev, 0, sizeof ev);
if (tls_os_queue_receive(s_queue, &msg, 0, HZ / 10) == TLS_OS_SUCCESS) if (tls_os_queue_receive(s_queue, &msg, 0, HZ / 10) == TLS_OS_SUCCESS)
@@ -202,6 +255,28 @@ static void net_task(void *arg)
ev = nl_wifi_sm_event(&s_sm, NL_WIFI_EV_DISCONNECTED, NL_WIFI_ERR_NONE, now); ev = nl_wifi_sm_event(&s_sm, NL_WIFI_EV_DISCONNECTED, NL_WIFI_ERR_NONE, now);
} else if (m == MSG_ONBOARD) { } else if (m == MSG_ONBOARD) {
ev = nl_wifi_sm_onboard(&s_sm, now); ev = nl_wifi_sm_onboard(&s_sm, now);
} else if (m == MSG_STANDALONE) {
/* Die Wunschwerte erst hier uebernehmen: der HTTP-Task hat sie abgelegt, der
* Netz-Task ist der einzige, der s_ap_* schreibt und start_ap() aufruft. */
if (s_req_enabled) {
copy(s_ap_ssid, s_req_ap_ssid, sizeof s_ap_ssid);
copy(s_ap_pass, s_req_ap_pass[0] ? s_req_ap_pass : NINLIGHT_AP_PASSWORD,
sizeof s_ap_pass);
s_ap_channel = s_req_ap_channel ? s_req_ap_channel : 6;
ev = nl_wifi_sm_standalone(&s_sm, s_req_pulse, now);
/* Steht der AP schon, liefert die Zustandsmaschine kein start_ap. Ob die
* neuen Daten einen Neuaufbau verlangen, entscheidet start_ap() selbst. */
recheck_ap = true;
} else {
/* Zurueck auf die Vorgaben: ein spaeterer Rueckfall- oder Onboarding-AP ist
* wieder der gewohnte "ninlight-<mac4>" mit dem einkompilierten Passwort,
* genau wie bei einer Lampe, die nie standalone war. */
s_ap_ssid[0] = '\0';
copy(s_ap_pass, NINLIGHT_AP_PASSWORD, sizeof s_ap_pass);
s_ap_channel = 6;
ev = nl_wifi_sm_leave_standalone(&s_sm, s_ssid[0] != '\0', now);
}
s_req_pending = false;
} }
tick = nl_wifi_sm_tick(&s_sm, now); tick = nl_wifi_sm_tick(&s_sm, now);
/* I1: a late/stray IP-up must not announce mDNS unless the SM actually settled on STA_CONNECTED. */ /* I1: a late/stray IP-up must not announce mDNS unless the SM actually settled on STA_CONNECTED. */
@@ -217,13 +292,16 @@ static void net_task(void *arg)
if (unexpected_softap) { if (unexpected_softap) {
printf("wifi: unexpected SDK softap while AP should be down, destroying it\r\n"); printf("wifi: unexpected SDK softap while AP should be down, destroying it\r\n");
tls_wifi_softap_destroy(); destroy_ap();
clear_softap_param();
} }
if (m & MSG_NETIF) if (m & MSG_NETIF)
printf("wifi: netif event %u\r\n", (unsigned)(m & 0xFFu)); printf("wifi: netif event %u\r\n", (unsigned)(m & 0xFFu));
execute(ev); execute(ev);
/* Nach einem Wechsel in den Standalone-Modus: start_ap() baut den AP neu auf, wenn
* Name, Passwort oder Kanal sich geaendert haben, und tut sonst nichts. */
if (recheck_ap && ap_wanted)
start_ap();
execute(tick); execute(tick);
if (got_ip_event && connected_now) if (got_ip_event && connected_now)
nl_mdns_on_ip_up(); nl_mdns_on_ip_up();
@@ -237,7 +315,8 @@ static void net_task(void *arg)
} }
} }
void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass) void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass,
const char *ap_ssid, uint8_t ap_channel, bool standalone, bool pulse)
{ {
nl_wifi_actions_t a; nl_wifi_actions_t a;
struct netif *nif; struct netif *nif;
@@ -249,6 +328,8 @@ void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass)
copy(s_ssid, ssid, sizeof s_ssid); copy(s_ssid, ssid, sizeof s_ssid);
copy(s_pass, pass, sizeof s_pass); copy(s_pass, pass, sizeof s_pass);
copy(s_ap_pass, ap_pass, sizeof s_ap_pass); copy(s_ap_pass, ap_pass, sizeof s_ap_pass);
copy(s_ap_ssid, ap_ssid, sizeof s_ap_ssid); /* leer = Hostname */
s_ap_channel = ap_channel ? ap_channel : 6;
/* The SDK would otherwise reconnect on its own with credentials from its parameter area. */ /* The SDK would otherwise reconnect on its own with credentials from its parameter area. */
nl_wifi_disable_sdk_auto_connect(); nl_wifi_disable_sdk_auto_connect();
@@ -256,8 +337,7 @@ void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass)
/* C2: the SDK already brought up its own softap (e.g. from NETIF_IP_NET_UP before /* C2: the SDK already brought up its own softap (e.g. from NETIF_IP_NET_UP before
* we got a chance to run) - tear it down, our state machine owns AP lifecycle. */ * we got a chance to run) - tear it down, our state machine owns AP lifecycle. */
printf("wifi: destroying SDK-created softap left over from boot\r\n"); printf("wifi: destroying SDK-created softap left over from boot\r\n");
tls_wifi_softap_destroy(); destroy_ap();
clear_softap_param();
} }
nif = tls_get_netif(); nif = tls_get_netif();
if (nif) if (nif)
@@ -268,10 +348,11 @@ void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass)
tls_netif_add_status_event(netif_event); tls_netif_add_status_event(netif_event);
lock(); lock();
a = nl_wifi_sm_start(&s_sm, s_ssid[0] != '\0', false, false, nl_app_now_ms()); a = nl_wifi_sm_start(&s_sm, s_ssid[0] != '\0', standalone, pulse, nl_app_now_ms());
unlock(); unlock();
s_started = true; s_started = true;
printf("wifi: %s, hostname %s\r\n", s_ssid[0] ? "station" : "onboarding AP", s_hostname); printf("wifi: %s, hostname %s\r\n",
standalone ? "standalone AP" : (s_ssid[0] ? "station" : "onboarding AP"), s_hostname);
execute(a); execute(a);
tls_os_task_create(NULL, "nl_net", net_task, NULL, (u8 *)s_stack, sizeof(s_stack), NL_NET_PRIO, 0); tls_os_task_create(NULL, "nl_net", net_task, NULL, (u8 *)s_stack, sizeof(s_stack), NL_NET_PRIO, 0);
} }
@@ -294,6 +375,50 @@ int nl_wifi_request_onboard(const char *ssid, const char *pass)
return tls_os_queue_send(s_queue, (void *)MSG_ONBOARD, 0) == TLS_OS_SUCCESS ? 0 : -1; return tls_os_queue_send(s_queue, (void *)MSG_ONBOARD, 0) == TLS_OS_SUCCESS ? 0 : -1;
} }
int nl_wifi_request_standalone(bool enabled, const char *ssid, const char *pass,
uint8_t channel, bool pulse)
{
bool busy;
if (!s_started)
return -1;
lock();
/* Wie beim Onboarding: nicht auf einen laufenden Test draufsatteln, und keinen zweiten
* Wunsch annehmen, solange der erste noch in der Warteschlange steht - er ueberschriebe
* dessen Werte, die Antwort haette aber schon 202 gesagt. */
busy = s_sm.state == NL_WIFI_ST_ONBOARD_TEST || s_req_pending;
if (!busy) {
copy(s_req_ap_ssid, ssid ? ssid : "", sizeof s_req_ap_ssid);
copy(s_req_ap_pass, pass ? pass : "", sizeof s_req_ap_pass);
s_req_ap_channel = channel;
s_req_enabled = enabled;
s_req_pulse = pulse;
s_req_pending = true;
}
unlock();
if (busy)
return -1;
if (tls_os_queue_send(s_queue, (void *)MSG_STANDALONE, 0) != TLS_OS_SUCCESS) {
lock();
s_req_pending = false;
unlock();
return -1;
}
return 0;
}
bool nl_wifi_is_standalone(void)
{
bool r;
if (!s_started)
return false;
lock();
r = s_sm.state == NL_WIFI_ST_STANDALONE;
unlock();
return r;
}
bool nl_wifi_open_access(void) bool nl_wifi_open_access(void)
{ {
bool r; bool r;
+5
View File
@@ -28,6 +28,11 @@ void nl_app_request_factory_reset(void);
/* Stores new station credentials (from a successful onboarding test) and persists them. */ /* Stores new station credentials (from a successful onboarding test) and persists them. */
int nl_app_set_wifi_credentials(const char *ssid, const char *pass); int nl_app_set_wifi_credentials(const char *ssid, const char *pass);
/* Loescht den Standalone-Modus dauerhaft (gelungenes Onboarding aus dem eigenen AP heraus).
* Name, Passwort und Kanal des AP bleiben stehen - sie zaehlen wieder, sobald der Modus
* erneut eingeschaltet wird. */
int nl_app_clear_standalone(void);
/* Clears the power-cycle counter ahead of the regular 5 s deadline (spec 5.5), idempotent. /* Clears the power-cycle counter ahead of the regular 5 s deadline (spec 5.5), idempotent.
* Used by the boot recovery path once it knows this boot is a recovery boot: a successful * Used by the boot recovery path once it knows this boot is a recovery boot: a successful
* recovery never returns to the app task, so without this the counter would keep growing * recovery never returns to the app task, so without this the counter would keep growing
+14
View File
@@ -80,6 +80,14 @@ static int save_settings(const nl_settings_t *s)
static int wifi_configure(const char *ssid, const char *pass) { return nl_wifi_request_onboard(ssid, pass); } static int wifi_configure(const char *ssid, const char *pass) { return nl_wifi_request_onboard(ssid, pass); }
static bool open_access(void) { return nl_wifi_open_access(); } static bool open_access(void) { return nl_wifi_open_access(); }
/* Der Treiber nimmt den Wunsch nur entgegen; wirksam wird er im Netz-Task, nachdem die
* Antwort raus ist. 0 = uebernommen, sonst 503. */
static int standalone_configure(bool enabled, const char *ssid, const char *pass, uint8_t channel,
bool pulse)
{
return nl_wifi_request_standalone(enabled, ssid, pass, channel, pulse);
}
static void get_info(nl_sys_info_t *out) static void get_info(nl_sys_info_t *out)
{ {
nl_wifi_status_t w; nl_wifi_status_t w;
@@ -108,6 +116,7 @@ static void get_info(nl_sys_info_t *out)
out->uptime_s = nl_app_now_ms() / 1000u; out->uptime_s = nl_app_now_ms() / 1000u;
out->free_heap = tls_mem_get_avail_heapsize(); out->free_heap = tls_mem_get_avail_heapsize();
out->last_ota_error = nl_ota_last_error(); out->last_ota_error = nl_ota_last_error();
out->wifi_standalone = nl_wifi_is_standalone();
} }
static void factory_reset(void) { nl_app_request_factory_reset(); } static void factory_reset(void) { nl_app_request_factory_reset(); }
@@ -131,12 +140,17 @@ const nl_api_ctx_t *nl_app_api_ctx(void)
cJSON_InitHooks(&hooks); cJSON_InitHooks(&hooks);
ctx.board = nl_board_get(); ctx.board = nl_board_get();
ctx.fw_version = NINLIGHT_FW_VERSION; ctx.fw_version = NINLIGHT_FW_VERSION;
/* Zeigerziel ist ein statischer Puffer im Treiber und gilt ab nl_wifi_start(), das im
* Startpfad vor dem HTTP-Server laeuft. Ohne diese Zeile dereferenziert der
* Einstellungs-Handler NULL, wenn kein eigener AP-Name gesetzt ist. */
ctx.hostname = nl_wifi_hostname();
ctx.build_date = NINLIGHT_BUILD_DATE; ctx.build_date = NINLIGHT_BUILD_DATE;
ctx.get_state = get_state; ctx.get_state = get_state;
ctx.apply = apply; ctx.apply = apply;
ctx.get_settings = get_settings; ctx.get_settings = get_settings;
ctx.save_settings = save_settings; ctx.save_settings = save_settings;
ctx.wifi_configure = wifi_configure; ctx.wifi_configure = wifi_configure;
ctx.standalone_configure = standalone_configure;
ctx.open_access = open_access; ctx.open_access = open_access;
ctx.get_info = get_info; ctx.get_info = get_info;
ctx.factory_reset = factory_reset; ctx.factory_reset = factory_reset;