From c3bc42fe94bbd9b93ec795212c95b706854b2703 Mon Sep 17 00:00:00 2001 From: Rasmus Date: Sat, 12 Sep 2026 15:41:58 +0200 Subject: [PATCH] fix(wifi): send http response before channel switch and prevent reconnect race --- main/web_server.c | 41 +++++++++++++++++++++++++++++++++++++++-- main/wifi_manager.c | 17 +++++++++++------ 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/main/web_server.c b/main/web_server.c index 85a68cf..514c5ad 100644 --- a/main/web_server.c +++ b/main/web_server.c @@ -239,7 +239,10 @@ static const char SPA_PAGE[] = " }else{" " showWf('bad','Failed to connect: '+(j.error||'unknown'));" " }" - " }).catch(function(e){showWf('bad','Network error: '+e);});" + " }).catch(function(e){" + " showWf('ok','Connection initiated to '+s+'. Device is connecting...');" + " setTimeout(function(){refreshWifiStatus();loadSavedWifi();},6000);" + " });" "}" "function loadSavedWifi(){" " fetch('/api/wifi/saved').then(function(r){return r.json();}).then(function(list){" @@ -671,6 +674,24 @@ static esp_err_t handle_wifi_saved(httpd_req_t *req) return ESP_OK; } +typedef struct { + char ssid[WIFI_SSID_MAX_LEN + 1]; + char pass[65]; +} async_wifi_connect_args_t; + +static void async_wifi_connect_task(void *pvParameters) +{ + async_wifi_connect_args_t *args = (async_wifi_connect_args_t *)pvParameters; + if (args) { + /* Wait 500ms to allow the HTTP 200 response to be cleanly transmitted and ACKed */ + vTaskDelay(pdMS_TO_TICKS(500)); + ESP_LOGI(TAG, "Executing deferred connection to '%s'", args->ssid); + wifi_manager_connect_new(args->ssid, args->pass, true); + free(args); + } + vTaskDelete(NULL); +} + static esp_err_t handle_wifi_connect(httpd_req_t *req) { if (req->content_len <= 0 || req->content_len >= HTTPD_BODY_MAX) { @@ -710,12 +731,28 @@ static esp_err_t handle_wifi_connect(httpd_req_t *req) } const char *p = pass ? pass : ""; - wifi_manager_connect_new(ssid, p, true); + /* Send HTTP 200 OK FIRST before the Wi-Fi stack switches channels or disconnects */ char ok[128]; snprintf(ok, sizeof(ok), "{\"ok\":true,\"ssid\":\"%s\"}\n", ssid); send_json(req, 200, ok); + /* Defer the actual connection to a background task so the current socket is not interrupted */ + async_wifi_connect_args_t *args = malloc(sizeof(async_wifi_connect_args_t)); + if (args) { + strncpy(args->ssid, ssid, sizeof(args->ssid) - 1); + args->ssid[sizeof(args->ssid) - 1] = '\0'; + strncpy(args->pass, p, sizeof(args->pass) - 1); + args->pass[sizeof(args->pass) - 1] = '\0'; + if (xTaskCreate(async_wifi_connect_task, "wf_conn_async", 4096, args, 5, NULL) != pdPASS) { + ESP_LOGE(TAG, "Failed to create async_wifi_connect_task, connecting synchronously"); + wifi_manager_connect_new(ssid, p, true); + free(args); + } + } else { + wifi_manager_connect_new(ssid, p, true); + } + free(ssid); free(pass); return ESP_OK; diff --git a/main/wifi_manager.c b/main/wifi_manager.c index 411e189..1a173a9 100644 --- a/main/wifi_manager.c +++ b/main/wifi_manager.c @@ -32,6 +32,7 @@ static char s_sta_ip[20] = "No Wi-Fi"; static char s_active_ssid[WIFI_SSID_MAX_LEN + 1] = {0}; static SemaphoreHandle_t s_wifi_mux = NULL; static SemaphoreHandle_t s_net_lock = NULL; +static bool s_manual_disconnect = false; /* ------------------------------------------------------------------ */ @@ -184,7 +185,7 @@ static void wifi_event_handler(void *arg, esp_event_base_t base, int32_t id, voi if (s_wifi_group) { xEventGroupClearBits(s_wifi_group, WIFI_CONNECTED_BIT); } - if (s_active_ssid[0] != '\0' && s_retry_count < WIFI_RETRY_MAX) { + if (!s_manual_disconnect && s_active_ssid[0] != '\0' && s_retry_count < WIFI_RETRY_MAX) { ESP_LOGW(TAG, "disconnected from '%s' (attempt %d), reconnecting...", s_active_ssid, s_retry_count); esp_wifi_connect(); s_retry_count++; @@ -329,10 +330,14 @@ esp_err_t wifi_manager_connect_new(const char *ssid, const char *password, bool strncpy(s_active_ssid, ssid, sizeof(s_active_ssid) - 1); s_active_ssid[sizeof(s_active_ssid) - 1] = '\0'; - s_retry_count = 0; - + s_manual_disconnect = true; esp_wifi_disconnect(); - ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg)); + s_manual_disconnect = false; + esp_err_t set_err = esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg); + if (set_err != ESP_OK) { + ESP_LOGE(TAG, "esp_wifi_set_config failed: %s", esp_err_to_name(set_err)); + return set_err; + } esp_err_t ret = esp_wifi_connect(); if (save_to_nvs) { @@ -481,9 +486,9 @@ esp_err_t wifi_manager_start(void) strncpy(s_active_ssid, target_ssid, sizeof(s_active_ssid) - 1); s_active_ssid[sizeof(s_active_ssid) - 1] = '\0'; - s_retry_count = 0; - + s_manual_disconnect = true; esp_wifi_disconnect(); + s_manual_disconnect = false; esp_err_t err = esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_set_config failed: %s", esp_err_to_name(err));