feat(wifi): retain multiple network credentials

This commit is contained in:
chenjintao
2026-08-24 16:16:16 +08:00
parent 23f4969404
commit ae27d5bbaa
6 changed files with 67 additions and 29 deletions
@@ -22,6 +22,10 @@
#define CONFIG_TQ_BOOT_SOUND_TIMEOUT_MS 3000 #define CONFIG_TQ_BOOT_SOUND_TIMEOUT_MS 3000
#endif #endif
#ifndef CONFIG_TQ_WIFI_CONNECT_CYCLE_TIMEOUT_MS
#define CONFIG_TQ_WIFI_CONNECT_CYCLE_TIMEOUT_MS 20000
#endif
static const char *TAG = "app_comp_boot"; static const char *TAG = "app_comp_boot";
typedef enum { typedef enum {
@@ -194,7 +198,7 @@ static esp_err_t app_composition_run_plan_a_boot_sequence(void)
"control_plane_bootstrap_connectivity"); "control_plane_bootstrap_connectivity");
esp_err_t wifi_err = control_plane_bootstrap_connectivity(CONFIG_MULTIMODAL_WIFI_SSID, esp_err_t wifi_err = control_plane_bootstrap_connectivity(CONFIG_MULTIMODAL_WIFI_SSID,
CONFIG_MULTIMODAL_WIFI_PASSWORD, CONFIG_MULTIMODAL_WIFI_PASSWORD,
CONFIG_MULTIMODAL_CONNECT_TIMEOUT_MS); CONFIG_TQ_WIFI_CONNECT_CYCLE_TIMEOUT_MS);
if (wifi_err != ESP_OK) { if (wifi_err != ESP_OK) {
ESP_LOGW(TAG, "Connectivity bootstrap failed: %s", esp_err_to_name(wifi_err)); ESP_LOGW(TAG, "Connectivity bootstrap failed: %s", esp_err_to_name(wifi_err));
} }
@@ -39,7 +39,7 @@
#define CONTROL_PLANE_PTT_ACTIVE_POLL_INTERVAL_MS 80 #define CONTROL_PLANE_PTT_ACTIVE_POLL_INTERVAL_MS 80
#define CONTROL_PLANE_WIFI_DISCONNECTED_REFRESH_MS 5000 #define CONTROL_PLANE_WIFI_DISCONNECTED_REFRESH_MS 5000
#define CONTROL_PLANE_WIFI_DISCONNECT_GRACE_MS 3000 #define CONTROL_PLANE_WIFI_DISCONNECT_GRACE_MS 3000
#define CONTROL_PLANE_WIFI_RECONNECT_TIMEOUT_MS 10000 #define CONTROL_PLANE_WIFI_REASSOCIATION_TIMEOUT_MS 10000
#define CONTROL_PLANE_WIFI_RECONNECT_BASE_DELAY_MS 5000 #define CONTROL_PLANE_WIFI_RECONNECT_BASE_DELAY_MS 5000
#define CONTROL_PLANE_WIFI_RECONNECT_MAX_DELAY_MS 60000 #define CONTROL_PLANE_WIFI_RECONNECT_MAX_DELAY_MS 60000
#define CONTROL_PLANE_USER_WEB_RETRY_MS 5000 #define CONTROL_PLANE_USER_WEB_RETRY_MS 5000
@@ -128,6 +128,10 @@
#define CONFIG_TQ_MINI_PROGRAM_PROVISIONING_RECONNECT_FAILURES 2 #define CONFIG_TQ_MINI_PROGRAM_PROVISIONING_RECONNECT_FAILURES 2
#endif #endif
#ifndef CONFIG_TQ_WIFI_CONNECT_CYCLE_TIMEOUT_MS
#define CONFIG_TQ_WIFI_CONNECT_CYCLE_TIMEOUT_MS 20000
#endif
#ifndef CONFIG_TQ_MINI_PROGRAM_PROVISIONING_ENABLE #ifndef CONFIG_TQ_MINI_PROGRAM_PROVISIONING_ENABLE
#define CONFIG_TQ_MINI_PROGRAM_PROVISIONING_ENABLE 0 #define CONFIG_TQ_MINI_PROGRAM_PROVISIONING_ENABLE 0
#endif #endif
@@ -1618,7 +1622,8 @@ static esp_err_t control_plane_attempt_wifi_reconnect(void)
ESP_LOGI(TAG, "Attempting Wi-Fi STA reconnect"); ESP_LOGI(TAG, "Attempting Wi-Fi STA reconnect");
domain_runtime_behavior_set_keep_awake(true); domain_runtime_behavior_set_keep_awake(true);
esp_err_t err = domain_runtime_wifi_portal_connect_sta(CONTROL_PLANE_WIFI_RECONNECT_TIMEOUT_MS); esp_err_t err = domain_runtime_wifi_portal_connect_sta(
CONFIG_TQ_WIFI_CONNECT_CYCLE_TIMEOUT_MS);
domain_runtime_behavior_set_keep_awake(false); domain_runtime_behavior_set_keep_awake(false);
if (err == ESP_OK) { if (err == ESP_OK) {
@@ -2195,7 +2200,7 @@ static void control_plane_wifi_state_task(void *arg)
(unsigned)CONTROL_PLANE_NETWORK_FAILURE_REASSOC_THRESHOLD); (unsigned)CONTROL_PLANE_NETWORK_FAILURE_REASSOC_THRESHOLD);
domain_runtime_behavior_set_keep_awake(true); domain_runtime_behavior_set_keep_awake(true);
esp_err_t reassoc_err = domain_runtime_wifi_portal_reconnect_sta( esp_err_t reassoc_err = domain_runtime_wifi_portal_reconnect_sta(
CONTROL_PLANE_WIFI_RECONNECT_TIMEOUT_MS); CONTROL_PLANE_WIFI_REASSOCIATION_TIMEOUT_MS);
domain_runtime_behavior_set_keep_awake(false); domain_runtime_behavior_set_keep_awake(false);
connected = control_plane_is_sta_connected(); connected = control_plane_is_sta_connected();
ESP_LOGW(TAG, ESP_LOGW(TAG,
+6 -4
View File
@@ -15,8 +15,9 @@ typedef struct {
/** /**
* @brief Initialize Wi-Fi portal defaults and seed credentials. * @brief Initialize Wi-Fi portal defaults and seed credentials.
* *
* This writes the provided SSID/password into NVS if no credentials exist yet. * This saves the provided SSID/password as the highest-priority entry while
* The underlying Wi-Fi manager is initialized lazily when starting STA or AP. * retaining existing credentials. The underlying Wi-Fi manager is initialized
* lazily when starting STA or AP.
*/ */
esp_err_t wifi_portal_init_defaults(const char *seed_ssid, const char *seed_password); esp_err_t wifi_portal_init_defaults(const char *seed_ssid, const char *seed_password);
@@ -50,8 +51,9 @@ esp_err_t wifi_portal_connect_sta(int timeout_ms);
/** /**
* @brief Trial-connect with credentials without persisting them first. * @brief Trial-connect with credentials without persisting them first.
* *
* On success, the trial credentials atomically replace the saved credential * On success, the trial credentials are saved as the highest-priority entry
* list. On failure, the previous in-memory and NVS credentials remain intact. * while older credentials are retained. On failure, the previous in-memory
* and NVS credentials remain intact.
*/ */
esp_err_t wifi_portal_connect_sta_with_credentials(const char *ssid, esp_err_t wifi_portal_connect_sta_with_credentials(const char *ssid,
const char *password, const char *password,
+36 -19
View File
@@ -125,6 +125,35 @@ static esp_err_t wifi_portal_lock(void)
return ESP_OK; return ESP_OK;
} }
static void promote_saved_wifi(const std::string &ssid)
{
if (ssid.empty()) {
return;
}
if (wifi_portal_lock() != ESP_OK) {
ESP_LOGW(TAG, "Could not promote connected Wi-Fi \"%s\": portal lock timed out",
ssid.c_str());
return;
}
PortalUnlockGuard guard;
install_wifi_storage_hooks();
auto &credentials = SsidManager::GetInstance();
const auto &ssid_list = credentials.GetSsidList();
for (size_t i = 0; i < ssid_list.size(); ++i) {
if (ssid_list[i].ssid != ssid) {
continue;
}
if (i > 0U) {
credentials.SetDefaultSsid((int)i);
ESP_LOGI(TAG, "Promoted connected Wi-Fi \"%s\" to highest priority",
ssid.c_str());
}
return;
}
}
static wifi_portal_behavior_hooks_t snapshot_behavior_hooks(void) static wifi_portal_behavior_hooks_t snapshot_behavior_hooks(void)
{ {
wifi_portal_behavior_hooks_t hooks = {}; wifi_portal_behavior_hooks_t hooks = {};
@@ -153,6 +182,7 @@ static void wifi_manager_event_callback(WifiEvent event, const std::string &data
break; break;
case WifiEvent::Connected: case WifiEvent::Connected:
ESP_LOGI(TAG, "Connected to \"%s\"", data.c_str()); ESP_LOGI(TAG, "Connected to \"%s\"", data.c_str());
promote_saved_wifi(data);
(void)apply_sta_stability_policy(); (void)apply_sta_stability_policy();
break; break;
case WifiEvent::Disconnected: case WifiEvent::Disconnected:
@@ -209,24 +239,10 @@ static void seed_default_credentials(const char *ssid, const char *password)
return; return;
} }
bool configured_ssid_saved = false;
for (const auto &item : manager.GetSsidList()) {
if (item.ssid == ssid) {
configured_ssid_saved = true;
break;
}
}
if (saved_count > 0U && !configured_ssid_saved) {
ESP_LOGI(TAG,
"Configured seed SSID changed to \"%s\"; replacing %u stale credential candidate(s)",
ssid,
(unsigned)saved_count);
manager.Clear();
}
manager.AddSsid(ssid, password != NULL ? password : ""); manager.AddSsid(ssid, password != NULL ? password : "");
promote_saved_wifi(ssid);
ESP_LOGI(TAG, ESP_LOGI(TAG,
"Ensured configured Wi-Fi credentials for SSID \"%s\" with %u previous candidate(s)", "Ensured configured Wi-Fi credentials for SSID \"%s\" while preserving %u previous candidate(s)",
ssid, ssid,
(unsigned)saved_count); (unsigned)saved_count);
} }
@@ -456,10 +472,10 @@ esp_err_t wifi_portal_connect_sta_with_credentials(const char *ssid,
ESP_RETURN_ON_ERROR(wifi_portal_lock(), TAG, "Failed to lock Wi-Fi portal"); ESP_RETURN_ON_ERROR(wifi_portal_lock(), TAG, "Failed to lock Wi-Fi portal");
PortalUnlockGuard guard; PortalUnlockGuard guard;
auto &credentials = SsidManager::GetInstance(); auto &credentials = SsidManager::GetInstance();
credentials.Clear();
credentials.AddSsid(ssid, password != NULL ? password : ""); credentials.AddSsid(ssid, password != NULL ? password : "");
promote_saved_wifi(ssid);
ESP_LOGI(TAG, ESP_LOGI(TAG,
"Credential trial succeeded and replaced saved Wi-Fi for SSID \"%s\"", "Credential trial succeeded and saved Wi-Fi \"%s\" as highest priority",
ssid); ssid);
} }
@@ -558,7 +574,8 @@ esp_err_t wifi_portal_save_credentials(const char *ssid, const char *password)
"Password too long"); "Password too long");
SsidManager::GetInstance().AddSsid(ssid, password != NULL ? password : ""); SsidManager::GetInstance().AddSsid(ssid, password != NULL ? password : "");
ESP_LOGI(TAG, "Saved Wi-Fi credentials for SSID \"%s\"", ssid); promote_saved_wifi(ssid);
ESP_LOGI(TAG, "Saved Wi-Fi credentials for SSID \"%s\" as highest priority", ssid);
return ESP_OK; return ESP_OK;
} }
+11 -2
View File
@@ -1090,10 +1090,19 @@ menu "Example Configuration"
Password for the seed SSID. Password for the seed SSID.
config MULTIMODAL_CONNECT_TIMEOUT_MS config MULTIMODAL_CONNECT_TIMEOUT_MS
int "Wi-Fi connect timeout (ms)" int "Cloud transport connect timeout (ms)"
default 10000 default 10000
help help
Timeout for Wi-Fi STA connection attempts in milliseconds. Timeout for multimodal HTTP and WebSocket connection attempts.
config TQ_WIFI_CONNECT_CYCLE_TIMEOUT_MS
int "Wi-Fi candidate connection cycle timeout (ms)"
default 20000
range 5000 120000
help
Total time allowed for one scan and connection cycle across
visible saved Wi-Fi candidates. Failed cycles continue through
the runtime reconnect backoff policy.
config VOICE_PROMPT_WIFI_SUCCESS_FILE config VOICE_PROMPT_WIFI_SUCCESS_FILE
string "Wi-Fi success prompt file" string "Wi-Fi success prompt file"
+1
View File
@@ -19,6 +19,7 @@ CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL=y
CONFIG_TQ_MINI_PROGRAM_PROVISIONING_ENABLE=y CONFIG_TQ_MINI_PROGRAM_PROVISIONING_ENABLE=y
CONFIG_TQ_MINI_PROGRAM_PROVISIONING_CONNECT_TIMEOUT_MS=30000 CONFIG_TQ_MINI_PROGRAM_PROVISIONING_CONNECT_TIMEOUT_MS=30000
CONFIG_TQ_MINI_PROGRAM_PROVISIONING_RECONNECT_FAILURES=2 CONFIG_TQ_MINI_PROGRAM_PROVISIONING_RECONNECT_FAILURES=2
CONFIG_TQ_WIFI_CONNECT_CYCLE_TIMEOUT_MS=20000
# External PSRAM (ESP32-S3 octal mode) # External PSRAM (ESP32-S3 octal mode)
CONFIG_SPIRAM=y CONFIG_SPIRAM=y