Add two ways back into provisioning: RST press and repeated server failure
RST/power-on trigger: checks esp_reset_reason() at the very top of boot. ESP32-C6 can't electrically distinguish the RST/EN button from a genuine power-on (both report ESP_RST_POWERON -- confirmed against ESP-IDF's own docs, ESP_RST_EXT is explicitly "not applicable"), so POWERON is treated as "user wants to reconfigure" and routes straight to provisioning. Safe because the device's only normal restart path is ESP_RST_DEEPSLEEP (its own scheduled wake), and crash-type resets (brownout/watchdog/panic) report their own distinct reasons, not POWERON -- so a flaky power supply or transient crash won't get bounced into provisioning, only an actual power cycle or RST press will (which plausibly means the frame is being moved/redeployed anyway). Auto-fallback: a new NVS-persisted consecutive-failure counter (frame_config_record_server_failure/reset_server_failures) tracks wakes where the tools server was unreachable. After CONFIG_FRAME_REPROVISION_AFTER_FAILURES in a row (default 12, ~1hr at the retry interval), the device clears its stored WiFi config and esp_restart()s rather than calling wifi_provisioning_start() directly -- doing that inline would mean initializing the display driver a second time in the same session (frame_client_run already did once), the same class of double-init bug hit earlier with WiFi. The next boot's frame_config_load() naturally reports "not provisioned" and routes through the existing, already-tested provisioning path with a single fresh epd_init(). Solves the "I moved the server to a new address" case without needing USB access. Both frame_config_save() (fresh provisioning) and any successful server contact reset the failure counter.
This commit is contained in:
@@ -83,4 +83,16 @@ menu "ESPresso Frame Configuration"
|
||||
server was unreachable or the fetch/display failed, instead of
|
||||
waiting the full FRAME_SLEEP_INTERVAL_S.
|
||||
|
||||
config FRAME_REPROVISION_AFTER_FAILURES
|
||||
int "Consecutive server-unreachable wakes before falling back to provisioning"
|
||||
default 12
|
||||
help
|
||||
If the tools server is unreachable for this many consecutive
|
||||
wakes in a row (at FRAME_RETRY_INTERVAL_S apart -- ~1 hour at
|
||||
the defaults), the device gives up retrying silently and drops
|
||||
back into provisioning mode instead, so a moved/renamed server
|
||||
can be reconfigured without a USB connection. Resets to 0 on
|
||||
any successful contact with the server, or on fresh
|
||||
provisioning.
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -286,10 +286,30 @@ void frame_client_run(const frame_config_t *cfg)
|
||||
* compounding flashing. */
|
||||
ESP_LOGW(TAG, "Tools server not reachable, retrying sooner");
|
||||
sleep_seconds = CONFIG_FRAME_RETRY_INTERVAL_S;
|
||||
bool give_up = frame_config_record_server_failure();
|
||||
if (have_display) {
|
||||
status_screen_show(cfg->sta_ssid, STATUS_OK, cfg->toolsserver, STATUS_FAILED);
|
||||
}
|
||||
if (give_up) {
|
||||
/* Unreachable for too many wakes in a row -- likely moved or
|
||||
* reconfigured. Clear the stored config and reboot rather than
|
||||
* calling wifi_provisioning_start() directly here: that would
|
||||
* mean initializing the display driver a second time in this
|
||||
* same session (frame_client_run already did once), which is
|
||||
* the same class of double-init bug hit earlier with WiFi.
|
||||
* The next boot's frame_config_load() will report "not
|
||||
* provisioned" and route through the existing, already-tested
|
||||
* provisioning path with a single, fresh epd_init(). */
|
||||
ESP_LOGW(TAG, "Server unreachable for %d consecutive wakes, falling back to provisioning",
|
||||
CONFIG_FRAME_REPROVISION_AFTER_FAILURES);
|
||||
frame_config_clear();
|
||||
if (have_display) {
|
||||
epd_sleep();
|
||||
}
|
||||
esp_restart();
|
||||
}
|
||||
} else {
|
||||
frame_config_reset_server_failures();
|
||||
if (first_connection && have_display) {
|
||||
status_screen_show(cfg->sta_ssid, STATUS_OK, cfg->toolsserver, STATUS_OK);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_netif.h"
|
||||
|
||||
@@ -26,6 +27,22 @@ void app_main(void)
|
||||
}
|
||||
ESP_ERROR_CHECK(nvs_err);
|
||||
|
||||
/* ESP32-C6 (like most ESP32 variants) can't electrically distinguish
|
||||
* the RST/EN button from a genuine power-on -- both report as
|
||||
* ESP_RST_POWERON, since there's no separate external-reset-pin signal
|
||||
* (see ESP_RST_EXT's doc comment). The device's only *normal* restart
|
||||
* path is ESP_RST_DEEPSLEEP (its own scheduled timer wake); crash-type
|
||||
* resets (brownout/watchdog/panic) report their own distinct reasons,
|
||||
* not POWERON. So treating POWERON as "user wants to reconfigure" is
|
||||
* safe: it won't fire on a scheduled wake or a transient crash, only
|
||||
* on an actual power cycle or RST press -- which for this device
|
||||
* plausibly means it's being moved/redeployed anyway. */
|
||||
if (esp_reset_reason() == ESP_RST_POWERON) {
|
||||
ESP_LOGI(TAG, "Power-on/RST reset -- entering provisioning to allow reconfiguration");
|
||||
wifi_provisioning_start();
|
||||
return;
|
||||
}
|
||||
|
||||
frame_config_t cfg;
|
||||
esp_err_t cfg_err = frame_config_load(&cfg);
|
||||
if (cfg_err == ESP_OK) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/param.h>
|
||||
@@ -93,9 +94,13 @@ esp_err_t frame_config_save(const frame_config_t *cfg)
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
/* Fresh (re)provisioning -- the next successful connection should
|
||||
* show the status screen again. */
|
||||
* show the status screen again, and any stale failure streak from
|
||||
* before this reprovisioning no longer applies. */
|
||||
err = nvs_set_u8(handle, "connected_once", 0);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
err = nvs_set_u8(handle, "fail_count", 0);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
err = nvs_commit(handle);
|
||||
}
|
||||
@@ -130,6 +135,54 @@ void frame_config_mark_connected_once(void)
|
||||
nvs_close(handle);
|
||||
}
|
||||
|
||||
bool frame_config_record_server_failure(void)
|
||||
{
|
||||
nvs_handle_t handle;
|
||||
if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle) != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t count = 0;
|
||||
nvs_get_u8(handle, "fail_count", &count); /* leaves count=0 if not yet set */
|
||||
if (count < UINT8_MAX) {
|
||||
count++;
|
||||
}
|
||||
nvs_set_u8(handle, "fail_count", count);
|
||||
nvs_commit(handle);
|
||||
nvs_close(handle);
|
||||
|
||||
return count >= CONFIG_FRAME_REPROVISION_AFTER_FAILURES;
|
||||
}
|
||||
|
||||
void frame_config_reset_server_failures(void)
|
||||
{
|
||||
nvs_handle_t handle;
|
||||
if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle) != ESP_OK) {
|
||||
return;
|
||||
}
|
||||
nvs_set_u8(handle, "fail_count", 0);
|
||||
nvs_commit(handle);
|
||||
nvs_close(handle);
|
||||
}
|
||||
|
||||
esp_err_t frame_config_clear(void)
|
||||
{
|
||||
nvs_handle_t handle;
|
||||
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Best-effort: a key simply not existing yet is fine here. */
|
||||
nvs_erase_key(handle, "sta_ssid");
|
||||
nvs_erase_key(handle, "sta_pass");
|
||||
nvs_erase_key(handle, "toolsserver");
|
||||
|
||||
err = nvs_commit(handle);
|
||||
nvs_close(handle);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void generate_ap_password(char *out, size_t out_size)
|
||||
{
|
||||
size_t len = MIN(FRAME_AP_PASSWORD_LEN, out_size - 1);
|
||||
|
||||
@@ -36,6 +36,28 @@ bool frame_config_has_connected_once(void);
|
||||
/** Marks the status screen as having been shown for the current WiFi config. */
|
||||
void frame_config_mark_connected_once(void);
|
||||
|
||||
/**
|
||||
* Increments the persisted count of consecutive wakes where the tools
|
||||
* server was unreachable. Returns true once it reaches
|
||||
* CONFIG_FRAME_REPROVISION_AFTER_FAILURES, signaling the caller should
|
||||
* fall back to provisioning instead of just retrying again.
|
||||
*/
|
||||
bool frame_config_record_server_failure(void);
|
||||
|
||||
/** Resets the consecutive-server-failure count. Call on any successful
|
||||
* contact with the tools server. */
|
||||
void frame_config_reset_server_failures(void);
|
||||
|
||||
/**
|
||||
* Erases the saved home-network config (SSID/password/toolsserver) so the
|
||||
* next boot's frame_config_load() reports "not provisioned" and falls
|
||||
* back into provisioning. Doesn't touch the AP password or other
|
||||
* device-identity state. Follow with esp_restart() -- doesn't itself
|
||||
* bring up provisioning, since that would mean initializing the display
|
||||
* driver a second time in a session that may have already done so.
|
||||
*/
|
||||
esp_err_t frame_config_clear(void);
|
||||
|
||||
/**
|
||||
* Returns this device's provisioning AP identity: a fixed SSID (from
|
||||
* Kconfig) and a password that's generated once on first use and persisted
|
||||
|
||||
Reference in New Issue
Block a user