From 79a482a09e528da604537c45544285d092038673 Mon Sep 17 00:00:00 2001 From: Seth Shi Date: Thu, 30 Apr 2026 09:27:49 +0800 Subject: [PATCH] fix(blufi): GET_WIFI_LIST triggers real-time scan with guaranteed response (#1964) * fix(blufi): GET_WIFI_LIST triggers real-time scan with guaranteed response Previously, ESP_BLUFI_EVENT_GET_WIFI_LIST waited for any in-progress scan to finish and then returned the cached result. When the cache was empty (e.g. after a config-mode transition that stopped the Wi-Fi driver), _send_wifi_list() returned silently with no response frame, leaving the App waiting until timeout. Changes: - GET_WIFI_LIST now clears the cache and starts a fresh scan immediately. - _wifi_scan_event_handler calls _send_wifi_list() after every scan triggered by a GET_WIFI_LIST request. - start_wifi_scan() calls esp_wifi_start() before esp_wifi_scan_start() to handle the case where the driver was stopped during a mode transition (ESP_ERR_WIFI_STATE is treated as already-started). - _send_wifi_list() sends ESP_BLUFI_WIFI_SCAN_FAIL when no APs are found, so the App always receives a terminal response. - Redundant static_cast in _wifi_scan_event_handler replaced with the existing local `self` pointer. Co-Authored-By: Claude Sonnet 4.6 * fix(blufi): preserve cache fast-path, fall back to live scan only when needed Address review feedback on always-rescan latency regression. The GET_WIFI_LIST handler now distinguishes three cases: 1. Scan in flight: defer the response via m_send_list_after_scan; the scan-done handler dispatches when it fires. Removes the previous blocking `while (m_scan_in_progress) vTaskDelay(500)` which would stall the BluFi event task indefinitely if the scan never completed. 2. Cache populated: respond from cache immediately (~50 ms, no latency change vs original behavior). _send_wifi_list() still kicks off an async refresh scan as before to keep the cache fresh. 3. Cache empty and no scan running: trigger a live scan and dispatch from the scan-done handler. If start_wifi_scan() fails, send ESP_BLUFI_WIFI_SCAN_FAIL so the App exits its wait state. State variables are also disentangled: - m_scan_should_save_ssid keeps its original meaning (write scan results into m_ap_records). Cleared during connect-to-AP so the connect-time scan does not pollute the cache. - m_send_list_after_scan is new and tracks "the next scan-done event should respond to a pending GET_WIFI_LIST request". The previous PR conflated these two responsibilities onto m_scan_should_save_ssid, which would have caused init-time scans to spuriously emit a wifi list to the App. start_wifi_scan() now returns bool so the caller can distinguish "scan started or already running" from "could not start a scan". Co-Authored-By: Claude Opus 4.7 --------- Co-authored-by: Yixin Shi Co-authored-by: Claude Sonnet 4.6 --- main/boards/common/blufi.cpp | 62 +++++++++++++++++++++++++++--------- main/boards/common/blufi.h | 11 ++++++- 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/main/boards/common/blufi.cpp b/main/boards/common/blufi.cpp index c4be933..f38c9af 100644 --- a/main/boards/common/blufi.cpp +++ b/main/boards/common/blufi.cpp @@ -533,13 +533,13 @@ int Blufi::_get_softap_conn_num() { return 0; } -void Blufi::start_wifi_scan() { +bool Blufi::start_wifi_scan() { ESP_LOGI(BLUFI_TAG, "Starting dedicated WiFi scan"); - // Check if a scan is already in progress + // Already running: caller can rely on the in-flight scan and await its done event. if (m_scan_in_progress) { ESP_LOGW(BLUFI_TAG, "Scan already in progress, skipping"); - return; + return true; } m_scan_in_progress = true; @@ -555,14 +555,14 @@ void Blufi::start_wifi_scan() { if (err != ESP_OK) { ESP_LOGE(BLUFI_TAG, "Failed to set WiFi mode to STA: %s", esp_err_to_name(err)); m_scan_in_progress = false; - return; + return false; } // Need to restart WiFi for mode change to take effect err = esp_wifi_start(); if (err != ESP_OK) { ESP_LOGE(BLUFI_TAG, "Failed to start WiFi after mode switch: %s", esp_err_to_name(err)); m_scan_in_progress = false; - return; + return false; } // Register scan event handler esp_event_handler_instance_t scan_event_instance; @@ -575,28 +575,36 @@ void Blufi::start_wifi_scan() { if (err != ESP_OK) { ESP_LOGE(BLUFI_TAG, "Failed to start WiFi scan: %s", esp_err_to_name(err)); m_scan_in_progress = false; - return; + return false; + } + } else if (current_mode == WIFI_MODE_STA || current_mode == WIFI_MODE_APSTA) { + // Ensure WiFi driver is started (may have been stopped during config mode transition) + err = esp_wifi_start(); + if (err != ESP_OK && err != ESP_ERR_WIFI_STATE) { + ESP_LOGE(BLUFI_TAG, "Failed to start WiFi before scan: %s", esp_err_to_name(err)); + m_scan_in_progress = false; + return false; } - } else if (current_mode == WIFI_MODE_STA) { - // Start scan err = esp_wifi_scan_start(NULL, false); if (err != ESP_OK) { ESP_LOGE(BLUFI_TAG, "Failed to start WiFi scan: %s", esp_err_to_name(err)); m_scan_in_progress = false; - return; + return false; } } else { ESP_LOGE(BLUFI_TAG, "Unexpected WiFi mode: %d", current_mode); m_scan_in_progress = false; - return; + return false; } ESP_LOGI(BLUFI_TAG, "WiFi scan started"); + return true; } void Blufi::_send_wifi_list() { if (m_ap_records.empty()) { - ESP_LOGW(BLUFI_TAG, "No AP records available to send"); + ESP_LOGW(BLUFI_TAG, "No AP records available, sending WiFi scan fail"); + esp_blufi_send_error_info(ESP_BLUFI_WIFI_SCAN_FAIL); return; } @@ -631,7 +639,7 @@ void Blufi::_wifi_scan_event_handler(void* arg, esp_event_base_t event_base, int ESP_LOGW(BLUFI_TAG, "No APs found"); self->m_ap_records.clear(); } else { - if (static_cast(arg)->m_scan_should_save_ssid == true) { + if (self->m_scan_should_save_ssid) { self->m_ap_records.resize(ap_num); esp_wifi_scan_get_ap_records(&ap_num, self->m_ap_records.data()); @@ -643,6 +651,11 @@ void Blufi::_wifi_scan_event_handler(void* arg, esp_event_base_t event_base, int } } self->m_scan_in_progress = false; + // Dispatch a pending GET_WIFI_LIST response if one is waiting on this scan. + if (self->m_send_list_after_scan) { + self->m_send_list_after_scan = false; + self->_send_wifi_list(); + } } } @@ -865,10 +878,29 @@ void Blufi::_handle_event(esp_blufi_cb_event_t event, esp_blufi_cb_param_t* para break; case ESP_BLUFI_EVENT_GET_WIFI_LIST: { ESP_LOGI(BLUFI_TAG, "BLUFI get wifi list"); - while (m_scan_in_progress) { - vTaskDelay(pdMS_TO_TICKS(500)); + // Case 1: a scan is already in flight (init scan or refresh scan started by + // the previous _send_wifi_list()). Defer the response to its done handler + // instead of blocking the BluFi task. + if (m_scan_in_progress) { + m_send_list_after_scan = true; + break; + } + // Case 2: cache is populated. Respond immediately; _send_wifi_list() also + // kicks off an async refresh scan to keep the cache fresh. + if (!m_ap_records.empty()) { + _send_wifi_list(); + break; + } + // Case 3: no cache (e.g. driver was stopped during a config-mode transition, + // init scan never completed). Trigger a real scan and dispatch from the + // scan-done handler. If the scan cannot start, return an error frame so the + // App exits its wait state instead of timing out. + m_scan_should_save_ssid = true; + m_send_list_after_scan = true; + if (!start_wifi_scan()) { + m_send_list_after_scan = false; + esp_blufi_send_error_info(ESP_BLUFI_WIFI_SCAN_FAIL); } - _send_wifi_list(); break; } default: diff --git a/main/boards/common/blufi.h b/main/boards/common/blufi.h index 2e02f7e..3c6dc2d 100644 --- a/main/boards/common/blufi.h +++ b/main/boards/common/blufi.h @@ -25,8 +25,9 @@ public: * This method intelligently handles WiFi scanning based on current WiFi state: * - If WiFi config mode is active, it uses the existing scan results from WifiConfigurationAp * - Otherwise, it performs a dedicated scan without interfering with normal WiFi operations + * @return true if a scan was started (or was already in progress); false on failure. */ - void start_wifi_scan(); + bool start_wifi_scan(); /** * @brief Initializes the Bluetooth controller, host, and Blufi profile. @@ -143,5 +144,13 @@ private: // WiFi scan related std::vector m_ap_records; bool m_scan_in_progress = false; + // When true, scan results are stored in m_ap_records on scan completion. + // Cleared during connect-to-AP so that the connect-time scan does not + // overwrite the cache with results gathered for connection purposes. bool m_scan_should_save_ssid = true; + // When true, the next scan-done event responds to a pending GET_WIFI_LIST + // request from the App. Set by the GET_WIFI_LIST handler when no cache is + // available or a scan is already in flight; cleared by the scan-done + // handler after dispatching the response. + bool m_send_list_after_scan = false; };