Firmware: WiFi fast-connect cache (skip scan + DHCP on the next wake)
After a successful home-WiFi connection, caches BSSID/channel and IP/netmask/gateway/DNS in NVS. The next wake's first connect attempt uses the cached BSSID/channel (skips the all-channel scan) and applies the cached IP directly once the link comes up (skips DHCP) -- a couple fewer seconds of radio-on time per wake, free every wake since nothing about the network actually needs renegotiating most of the time. Falls back to a normal scan+DHCP attempt, and clears the cache, if: the fast attempt itself fails, or it "succeeds" at the WiFi layer but the full fetch cycle then fails anyway (a stale cached IP/DNS/gateway that associates but can't actually reach the server). Also cleared on (re)provisioning and factory reset, since a new network shouldn't try to reuse the old one's cache. The static-IP path needed care to get right without touching untested territory: esp_netif_set_ip_info() only posts IP_EVENT_STA_GOT_IP (what the existing connect-wait logic blocks on) once the netif is already up, which the internal netif-glue's own WIFI_EVENT_STA_CONNECTED handler guarantees by running first (registered earlier, in esp_netif_create_default_wifi_sta()) -- confirmed against ESP-IDF's own static_ip example and esp_netif_handlers.c source rather than assumed. Falling back after a failed fast attempt also needed an explicit esp_netif_dhcpc_start() first: esp_netif_dhcpc_stop() leaves the netif's DHCP status STOPPED rather than resetting to INIT, and left alone the glue would silently re-post the stale cached IP on the next connect instead of actually running DHCP (esp_netif_action_connected). Version bumped to 1.1.0 (real feature, not just a fix); build-verified clean on both board configs (devkit 8MB, XIAO 4MB), no new warnings.
This commit is contained in:
@@ -55,8 +55,12 @@ sequenceDiagram
|
|||||||
The captive portal form saves SSID/password/toolsserver to NVS and
|
The captive portal form saves SSID/password/toolsserver to NVS and
|
||||||
reboots.
|
reboots.
|
||||||
2. **Stored config exists**: connect to the saved WiFi network (a few
|
2. **Stored config exists**: connect to the saved WiFi network (a few
|
||||||
retries before falling back to provisioning if it fails), then run the
|
retries before falling back to provisioning if it fails). The first
|
||||||
fetch cycle in `frame_client.c`:
|
attempt tries a cached BSSID/channel + static IP from the last
|
||||||
|
successful connection, skipping the scan and DHCP; a bad cache falls
|
||||||
|
back to a normal attempt and gets cleared (see `firmware/README.md`'s
|
||||||
|
"WiFi fast-connect" section). Then run the fetch cycle in
|
||||||
|
`frame_client.c`:
|
||||||
- Check the next-photo and back-photo buttons (`next_button_check()`,
|
- Check the next-photo and back-photo buttons (`next_button_check()`,
|
||||||
`back_button_check()`) -- if either was what woke the device
|
`back_button_check()`) -- if either was what woke the device
|
||||||
(checked via the latched `esp_sleep_get_gpio_wakeup_status()`, not
|
(checked via the latched `esp_sleep_get_gpio_wakeup_status()`, not
|
||||||
|
|||||||
@@ -84,6 +84,26 @@ reflashing. The Kconfig value only applies before the device has ever
|
|||||||
successfully reached a configured server, or if the response doesn't
|
successfully reached a configured server, or if the response doesn't
|
||||||
include a valid interval.
|
include a valid interval.
|
||||||
|
|
||||||
|
### WiFi fast-connect
|
||||||
|
|
||||||
|
After a successful home-WiFi connection, the device caches the AP's
|
||||||
|
BSSID/channel and its own IP/netmask/gateway/DNS in NVS. The *next*
|
||||||
|
wake's first connect attempt uses that cache to skip the all-channel
|
||||||
|
scan (`wifi_config.sta.bssid_set` + a channel hint) and DHCP (a static
|
||||||
|
IP set directly) -- typically a couple of seconds less radio-on time
|
||||||
|
per wake, free every wake since it's already-known information, not a
|
||||||
|
fresh negotiation.
|
||||||
|
|
||||||
|
If that cached attempt fails outright, or it "succeeds" at the WiFi
|
||||||
|
layer but the server turns out to be unreachable (a stale cached IP,
|
||||||
|
DNS entry, or gateway), the cache is cleared and that wake falls back to
|
||||||
|
a normal scan + DHCP -- and the *next* wake tries the fast path again
|
||||||
|
from a fresh cache. A (re)provisioning event or factory reset also
|
||||||
|
clears it, since a new network shouldn't try to reuse the old one's
|
||||||
|
cached BSSID. No user-facing config for this -- it's purely an
|
||||||
|
internal optimization, invisible unless you're watching the serial log
|
||||||
|
(`"fast path: cached BSSID/channel + static IP"` vs `"attempt N/M"`).
|
||||||
|
|
||||||
## First boot
|
## First boot
|
||||||
|
|
||||||
With no stored WiFi config (a fresh device, or after erasing NVS -- see
|
With no stored WiFi config (a fresh device, or after erasing NVS -- see
|
||||||
|
|||||||
@@ -30,6 +30,70 @@ static const char *TAG = "frame_client";
|
|||||||
|
|
||||||
static EventGroupHandle_t s_sta_event_group;
|
static EventGroupHandle_t s_sta_event_group;
|
||||||
|
|
||||||
|
/* Passed as the event-handler arg during frame_wifi_connect_sta so it can
|
||||||
|
* apply the cached static IP (fast_cache != NULL) exactly once, on the
|
||||||
|
* fast-connect attempt's own WIFI_EVENT_STA_CONNECTED -- fallback attempts
|
||||||
|
* leave fast_cache NULL and get normal DHCP. */
|
||||||
|
typedef struct {
|
||||||
|
esp_netif_t *netif;
|
||||||
|
const frame_wifi_cache_t *fast_cache;
|
||||||
|
} sta_connect_ctx_t;
|
||||||
|
|
||||||
|
/* Applies a cached static IP right after L2 link-up, skipping DHCP.
|
||||||
|
* esp_netif_set_ip_info() only posts IP_EVENT_STA_GOT_IP (which is what
|
||||||
|
* unblocks the connect-attempt wait below) once the netif is already
|
||||||
|
* "up" -- true by this point, since the internal netif-glue's own
|
||||||
|
* WIFI_EVENT_STA_CONNECTED subscriber (registered earlier, in
|
||||||
|
* esp_netif_create_default_wifi_sta()) runs before this one and brings
|
||||||
|
* the netif up first. Confirmed against ESP-IDF's own
|
||||||
|
* examples/protocols/static_ip. */
|
||||||
|
static void apply_fast_ip(esp_netif_t *netif, const frame_wifi_cache_t *cache)
|
||||||
|
{
|
||||||
|
if (esp_netif_dhcpc_stop(netif) != ESP_OK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
esp_netif_ip_info_t ip_info = {
|
||||||
|
.ip.addr = cache->ip,
|
||||||
|
.netmask.addr = cache->netmask,
|
||||||
|
.gw.addr = cache->gateway,
|
||||||
|
};
|
||||||
|
if (esp_netif_set_ip_info(netif, &ip_info) != ESP_OK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cache->dns != 0) {
|
||||||
|
esp_netif_dns_info_t dns_info = { .ip.type = ESP_IPADDR_TYPE_V4 };
|
||||||
|
dns_info.ip.u_addr.ip4.addr = cache->dns;
|
||||||
|
esp_netif_set_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Records BSSID/channel/IP/netmask/gateway/DNS from a connection that just
|
||||||
|
* succeeded (fast path or normal), for the next wake's fast-connect
|
||||||
|
* attempt. Best-effort: any lookup failing here just means next wake
|
||||||
|
* falls back to a normal scan+DHCP, not a hard error. */
|
||||||
|
static void save_wifi_cache(esp_netif_t *netif)
|
||||||
|
{
|
||||||
|
wifi_ap_record_t ap_info;
|
||||||
|
if (esp_wifi_sta_get_ap_info(&ap_info) != ESP_OK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
esp_netif_ip_info_t ip_info;
|
||||||
|
if (esp_netif_get_ip_info(netif, &ip_info) != ESP_OK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
esp_netif_dns_info_t dns_info = {0};
|
||||||
|
esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns_info);
|
||||||
|
|
||||||
|
frame_wifi_cache_t cache = {0};
|
||||||
|
memcpy(cache.bssid, ap_info.bssid, sizeof(cache.bssid));
|
||||||
|
cache.channel = ap_info.primary;
|
||||||
|
cache.ip = ip_info.ip.addr;
|
||||||
|
cache.netmask = ip_info.netmask.addr;
|
||||||
|
cache.gateway = ip_info.gw.addr;
|
||||||
|
cache.dns = dns_info.ip.u_addr.ip4.addr;
|
||||||
|
frame_wifi_cache_save(&cache);
|
||||||
|
}
|
||||||
|
|
||||||
/* Builds a full URL from cfg->toolsserver + a path (no leading slash),
|
/* Builds a full URL from cfg->toolsserver + a path (no leading slash),
|
||||||
* appending cfg->access_token as ?token= if one's set. toolsserver is
|
* appending cfg->access_token as ?token= if one's set. toolsserver is
|
||||||
* normally a bare "host:port", defaulting to plain http; it may instead
|
* normally a bare "host:port", defaulting to plain http; it may instead
|
||||||
@@ -70,8 +134,13 @@ static void copy_wifi_field(uint8_t *dst, size_t dst_size, const char *src)
|
|||||||
static void sta_event_handler(void *arg, esp_event_base_t event_base,
|
static void sta_event_handler(void *arg, esp_event_base_t event_base,
|
||||||
int32_t event_id, void *event_data)
|
int32_t event_id, void *event_data)
|
||||||
{
|
{
|
||||||
|
sta_connect_ctx_t *ctx = (sta_connect_ctx_t *)arg;
|
||||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||||
esp_wifi_connect();
|
esp_wifi_connect();
|
||||||
|
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) {
|
||||||
|
if (ctx->fast_cache != NULL) {
|
||||||
|
apply_fast_ip(ctx->netif, ctx->fast_cache);
|
||||||
|
}
|
||||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||||
ESP_LOGW(TAG, "Disconnected from home WiFi");
|
ESP_LOGW(TAG, "Disconnected from home WiFi");
|
||||||
xEventGroupSetBits(s_sta_event_group, STA_FAILED_BIT);
|
xEventGroupSetBits(s_sta_event_group, STA_FAILED_BIT);
|
||||||
@@ -93,21 +162,68 @@ esp_err_t frame_wifi_connect_sta(const frame_config_t *cfg)
|
|||||||
wifi_init_config_t init_cfg = WIFI_INIT_CONFIG_DEFAULT();
|
wifi_init_config_t init_cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||||
ESP_ERROR_CHECK(esp_wifi_init(&init_cfg));
|
ESP_ERROR_CHECK(esp_wifi_init(&init_cfg));
|
||||||
|
|
||||||
|
sta_connect_ctx_t ctx = { .netif = sta_netif, .fast_cache = NULL };
|
||||||
esp_event_handler_instance_t wifi_handler;
|
esp_event_handler_instance_t wifi_handler;
|
||||||
esp_event_handler_instance_t ip_handler;
|
esp_event_handler_instance_t ip_handler;
|
||||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &sta_event_handler, NULL, &wifi_handler));
|
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &sta_event_handler, &ctx, &wifi_handler));
|
||||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &sta_event_handler, NULL, &ip_handler));
|
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &sta_event_handler, &ctx, &ip_handler));
|
||||||
|
|
||||||
wifi_config_t wifi_config = {0};
|
wifi_config_t wifi_config = {0};
|
||||||
copy_wifi_field(wifi_config.sta.ssid, sizeof(wifi_config.sta.ssid), cfg->sta_ssid);
|
copy_wifi_field(wifi_config.sta.ssid, sizeof(wifi_config.sta.ssid), cfg->sta_ssid);
|
||||||
copy_wifi_field(wifi_config.sta.password, sizeof(wifi_config.sta.password), cfg->sta_password);
|
copy_wifi_field(wifi_config.sta.password, sizeof(wifi_config.sta.password), cfg->sta_password);
|
||||||
|
|
||||||
|
frame_wifi_cache_t cache;
|
||||||
|
bool have_cache = frame_wifi_cache_load(&cache);
|
||||||
|
|
||||||
|
wifi_config_t start_config = wifi_config;
|
||||||
|
if (have_cache) {
|
||||||
|
/* Known BSSID/channel -- skips the all-channel scan. The IP side
|
||||||
|
* of the fast path (skipping DHCP) happens in apply_fast_ip once
|
||||||
|
* WIFI_EVENT_STA_CONNECTED confirms this specific AP answered. */
|
||||||
|
start_config.sta.bssid_set = true;
|
||||||
|
memcpy(start_config.sta.bssid, cache.bssid, sizeof(cache.bssid));
|
||||||
|
start_config.sta.channel = cache.channel;
|
||||||
|
start_config.sta.scan_method = WIFI_FAST_SCAN;
|
||||||
|
}
|
||||||
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &start_config));
|
||||||
ESP_ERROR_CHECK(esp_wifi_start());
|
ESP_ERROR_CHECK(esp_wifi_start());
|
||||||
|
|
||||||
esp_err_t result = ESP_FAIL;
|
esp_err_t result = ESP_FAIL;
|
||||||
for (int attempt = 1; attempt <= CONFIG_FRAME_STA_CONNECT_MAX_RETRIES; attempt++) {
|
|
||||||
|
if (have_cache) {
|
||||||
|
ESP_LOGI(TAG, "Connecting to '%s' (fast path: cached BSSID/channel + static IP)", cfg->sta_ssid);
|
||||||
|
ctx.fast_cache = &cache;
|
||||||
|
|
||||||
|
xEventGroupClearBits(s_sta_event_group, STA_CONNECTED_BIT | STA_FAILED_BIT);
|
||||||
|
esp_wifi_connect();
|
||||||
|
|
||||||
|
EventBits_t bits = xEventGroupWaitBits(s_sta_event_group, STA_CONNECTED_BIT | STA_FAILED_BIT,
|
||||||
|
pdTRUE, pdFALSE,
|
||||||
|
pdMS_TO_TICKS(CONFIG_FRAME_STA_CONNECT_TIMEOUT_MS));
|
||||||
|
ctx.fast_cache = NULL;
|
||||||
|
|
||||||
|
if (bits & STA_CONNECTED_BIT) {
|
||||||
|
result = ESP_OK;
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "Fast-connect attempt failed, falling back to a full scan");
|
||||||
|
frame_wifi_cache_clear();
|
||||||
|
/* apply_fast_ip's esp_netif_dhcpc_stop() leaves the netif's
|
||||||
|
* internal DHCP state STOPPED rather than the default INIT --
|
||||||
|
* left alone, the next WIFI_EVENT_STA_CONNECTED would make
|
||||||
|
* esp-netif's own glue silently re-post the stale cached IP
|
||||||
|
* instead of actually running DHCP (see
|
||||||
|
* esp_netif_action_connected in esp_netif_handlers.c). Calling
|
||||||
|
* this now (netif is down, mid-retry) just resets that state
|
||||||
|
* back to INIT -- confirmed against esp_netif_dhcpc_start's
|
||||||
|
* source, doesn't yet touch the network. */
|
||||||
|
esp_netif_dhcpc_start(sta_netif);
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int attempt = 1; result != ESP_OK && attempt <= CONFIG_FRAME_STA_CONNECT_MAX_RETRIES; attempt++) {
|
||||||
ESP_LOGI(TAG, "Connecting to '%s' (attempt %d/%d)", cfg->sta_ssid, attempt,
|
ESP_LOGI(TAG, "Connecting to '%s' (attempt %d/%d)", cfg->sta_ssid, attempt,
|
||||||
CONFIG_FRAME_STA_CONNECT_MAX_RETRIES);
|
CONFIG_FRAME_STA_CONNECT_MAX_RETRIES);
|
||||||
|
|
||||||
@@ -130,7 +246,9 @@ esp_err_t frame_wifi_connect_sta(const frame_config_t *cfg)
|
|||||||
vEventGroupDelete(s_sta_event_group);
|
vEventGroupDelete(s_sta_event_group);
|
||||||
s_sta_event_group = NULL;
|
s_sta_event_group = NULL;
|
||||||
|
|
||||||
if (result != ESP_OK) {
|
if (result == ESP_OK) {
|
||||||
|
save_wifi_cache(sta_netif);
|
||||||
|
} else {
|
||||||
/* Fully tear the WiFi driver back down on failure -- the caller
|
/* Fully tear the WiFi driver back down on failure -- the caller
|
||||||
* falls back to provisioning, which calls esp_wifi_init() again
|
* falls back to provisioning, which calls esp_wifi_init() again
|
||||||
* for AP mode. Leaving the driver merely stopped (rather than
|
* for AP mode. Leaving the driver merely stopped (rather than
|
||||||
@@ -799,6 +917,14 @@ void frame_client_run(const frame_config_t *cfg, fetch_action_t action, bool sho
|
|||||||
* to show what went wrong instead of leaving stale content
|
* to show what went wrong instead of leaving stale content
|
||||||
* with no indication anything failed. */
|
* with no indication anything failed. */
|
||||||
ESP_LOGW(TAG, "Fetch/display failed (%s), retrying sooner", esp_err_to_name(fetch_err));
|
ESP_LOGW(TAG, "Fetch/display failed (%s), retrying sooner", esp_err_to_name(fetch_err));
|
||||||
|
/* Covers the fast-connect cache's blind spot: WiFi can report
|
||||||
|
* a successful connection (cached static IP "worked" at the
|
||||||
|
* link layer) while the cached IP is actually stale/dead at
|
||||||
|
* the network layer -- this is the first real evidence of
|
||||||
|
* that, since it's the first thing that actually talks to the
|
||||||
|
* server. Clearing here means next wake gets a clean scan +
|
||||||
|
* DHCP instead of repeating the same silent failure. */
|
||||||
|
frame_wifi_cache_clear();
|
||||||
status_screen_show(cfg->sta_ssid, STATUS_OK, cfg->toolsserver, STATUS_FAILED);
|
status_screen_show(cfg->sta_ssid, STATUS_OK, cfg->toolsserver, STATUS_FAILED);
|
||||||
} else if (first_connection) {
|
} else if (first_connection) {
|
||||||
status_screen_show(cfg->sta_ssid, STATUS_OK, cfg->toolsserver, STATUS_OK);
|
status_screen_show(cfg->sta_ssid, STATUS_OK, cfg->toolsserver, STATUS_OK);
|
||||||
|
|||||||
@@ -115,6 +115,11 @@ esp_err_t frame_config_save(const frame_config_t *cfg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
nvs_close(handle);
|
nvs_close(handle);
|
||||||
|
|
||||||
|
/* A (re)provisioning event -- the fast-connect cache (if any) may
|
||||||
|
* belong to a different network than whatever was just saved. */
|
||||||
|
frame_wifi_cache_clear();
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,6 +162,8 @@ void frame_config_clear(void)
|
|||||||
nvs_erase_key(handle, "connected_once");
|
nvs_erase_key(handle, "connected_once");
|
||||||
nvs_commit(handle);
|
nvs_commit(handle);
|
||||||
nvs_close(handle);
|
nvs_close(handle);
|
||||||
|
|
||||||
|
frame_wifi_cache_clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t frame_config_get_last_display_crc32(uint32_t *out)
|
esp_err_t frame_config_get_last_display_crc32(uint32_t *out)
|
||||||
@@ -193,6 +200,68 @@ void frame_config_invalidate_last_display_crc32(void)
|
|||||||
nvs_close(handle);
|
nvs_close(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
* WiFi fast-connect cache
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
bool frame_wifi_cache_load(frame_wifi_cache_t *out)
|
||||||
|
{
|
||||||
|
memset(out, 0, sizeof(*out));
|
||||||
|
|
||||||
|
nvs_handle_t handle;
|
||||||
|
if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &handle) != ESP_OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bssid_len = sizeof(out->bssid);
|
||||||
|
bool ok = nvs_get_blob(handle, "wc_bssid", out->bssid, &bssid_len) == ESP_OK
|
||||||
|
&& bssid_len == sizeof(out->bssid);
|
||||||
|
ok = ok && nvs_get_u8(handle, "wc_channel", &out->channel) == ESP_OK;
|
||||||
|
ok = ok && nvs_get_u32(handle, "wc_ip", &out->ip) == ESP_OK;
|
||||||
|
ok = ok && nvs_get_u32(handle, "wc_netmask", &out->netmask) == ESP_OK;
|
||||||
|
ok = ok && nvs_get_u32(handle, "wc_gateway", &out->gateway) == ESP_OK;
|
||||||
|
/* DNS is best-effort -- missing/zero just means the fast path's static
|
||||||
|
* IP won't have a DNS server set, which only matters if toolsserver is
|
||||||
|
* a hostname rather than a bare IP; a failed cycle clears the whole
|
||||||
|
* cache regardless (see frame_client_run), so this can't wedge. */
|
||||||
|
nvs_get_u32(handle, "wc_dns", &out->dns);
|
||||||
|
|
||||||
|
nvs_close(handle);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
void frame_wifi_cache_save(const frame_wifi_cache_t *cache)
|
||||||
|
{
|
||||||
|
nvs_handle_t handle;
|
||||||
|
if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle) != ESP_OK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nvs_set_blob(handle, "wc_bssid", cache->bssid, sizeof(cache->bssid));
|
||||||
|
nvs_set_u8(handle, "wc_channel", cache->channel);
|
||||||
|
nvs_set_u32(handle, "wc_ip", cache->ip);
|
||||||
|
nvs_set_u32(handle, "wc_netmask", cache->netmask);
|
||||||
|
nvs_set_u32(handle, "wc_gateway", cache->gateway);
|
||||||
|
nvs_set_u32(handle, "wc_dns", cache->dns);
|
||||||
|
nvs_commit(handle);
|
||||||
|
nvs_close(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void frame_wifi_cache_clear(void)
|
||||||
|
{
|
||||||
|
nvs_handle_t handle;
|
||||||
|
if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle) != ESP_OK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nvs_erase_key(handle, "wc_bssid");
|
||||||
|
nvs_erase_key(handle, "wc_channel");
|
||||||
|
nvs_erase_key(handle, "wc_ip");
|
||||||
|
nvs_erase_key(handle, "wc_netmask");
|
||||||
|
nvs_erase_key(handle, "wc_gateway");
|
||||||
|
nvs_erase_key(handle, "wc_dns");
|
||||||
|
nvs_commit(handle);
|
||||||
|
nvs_close(handle);
|
||||||
|
}
|
||||||
|
|
||||||
static void generate_ap_password(char *out, size_t out_size)
|
static void generate_ap_password(char *out, size_t out_size)
|
||||||
{
|
{
|
||||||
size_t len = MIN(FRAME_AP_PASSWORD_LEN, out_size - 1);
|
size_t len = MIN(FRAME_AP_PASSWORD_LEN, out_size - 1);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
|
|
||||||
#define FRAME_CFG_SSID_MAX_LEN 32
|
#define FRAME_CFG_SSID_MAX_LEN 32
|
||||||
@@ -83,3 +84,38 @@ void ap_identity_get(char *ssid_out, size_t ssid_len, char *pass_out, size_t pas
|
|||||||
* can provision the device. Does not return.
|
* can provision the device. Does not return.
|
||||||
*/
|
*/
|
||||||
void wifi_provisioning_start(void);
|
void wifi_provisioning_start(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cached parameters from the most recent successful home-WiFi connection,
|
||||||
|
* letting the next wake's first connect attempt skip the all-channel scan
|
||||||
|
* (known BSSID/channel) and DHCP (known static IP/netmask/gateway/DNS).
|
||||||
|
* Fields are stored exactly as esp-wifi/esp-netif already use them
|
||||||
|
* internally, so they can be fed straight back in with no conversion.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint8_t bssid[6];
|
||||||
|
uint8_t channel;
|
||||||
|
uint32_t ip;
|
||||||
|
uint32_t netmask;
|
||||||
|
uint32_t gateway;
|
||||||
|
uint32_t dns; /* 0 = none cached (best-effort; a real DHCP fallback still repopulates this) */
|
||||||
|
} frame_wifi_cache_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the cached fast-connect parameters. Returns false if there's
|
||||||
|
* nothing cached yet, or it was invalidated (see frame_wifi_cache_clear).
|
||||||
|
*/
|
||||||
|
bool frame_wifi_cache_load(frame_wifi_cache_t *out);
|
||||||
|
|
||||||
|
/** Saves fast-connect parameters after a successful home-WiFi connection. */
|
||||||
|
void frame_wifi_cache_save(const frame_wifi_cache_t *cache);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the fast-connect cache. Called after a cached fast-connect
|
||||||
|
* attempt itself fails (BSSID/channel went stale), after a full fetch
|
||||||
|
* cycle fails despite a successful connection (the cached static IP may
|
||||||
|
* be unreachable even though the link came up), and by
|
||||||
|
* frame_config_save()/frame_config_clear() -- a (re)provisioning event
|
||||||
|
* means whatever was cached may belong to a different network entirely.
|
||||||
|
*/
|
||||||
|
void frame_wifi_cache_clear(void);
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
1.0.1
|
1.1.0
|
||||||
|
|||||||
Reference in New Issue
Block a user