Files
Seth Shi 79a482a09e 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

---------

Co-authored-by: Yixin Shi <shiyixin@qiniu.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 09:27:49 +08:00

157 lines
4.8 KiB
C++

#pragma once
#include <aes/esp_aes.h>
#include <cassert>
#include <cstring>
#include <vector>
#include "esp_blufi_api.h"
#include "esp_err.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "mbedtls/aes.h"
#include "mbedtls/dhm.h"
#include "wifi_manager.h"
class Blufi {
public:
/**
* @brief Get the singleton instance of the Blufi class.
*/
static Blufi &GetInstance();
/**
* @brief Start WiFi scan for Blufi provisioning
* 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.
*/
bool start_wifi_scan();
/**
* @brief Initializes the Bluetooth controller, host, and Blufi profile.
* This is the main entry point to start the Blufi process.
* @return ESP_OK on success, otherwise an error code.
*/
esp_err_t init();
/**
* @brief Deinitializes Blufi and the Bluetooth stack.
* @return ESP_OK on success, otherwise an error code.
*/
esp_err_t deinit();
// Delete copy constructor and assignment operator for singleton
Blufi(const Blufi &) = delete;
Blufi &operator=(const Blufi &) = delete;
private:
bool inited_ = false;
Blufi();
~Blufi();
// Initialization logic
static esp_err_t _controller_init();
static esp_err_t _controller_deinit();
static esp_err_t _host_init();
static esp_err_t _host_deinit();
static esp_err_t _gap_register_callback();
static esp_err_t _host_and_cb_init();
void _security_init();
void _security_deinit();
void _dh_negotiate_data_handler(uint8_t *data, int len, uint8_t **output_data, int *output_len,
bool *need_free);
int _aes_encrypt(uint8_t iv8, uint8_t *crypt_data, int crypt_len);
int _aes_decrypt(uint8_t iv8, uint8_t *crypt_data, int crypt_len);
static uint16_t _crc_checksum(uint8_t iv8, uint8_t *data, int len);
void _handle_event(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param);
static int _get_softap_conn_num();
// WiFi scan methods
void _send_wifi_list();
void _start_dedicated_wifi_scan();
static void _wifi_scan_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id,
void *event_data);
// These C-style functions are registered with ESP-IDF and call the corresponding instance
// methods.
static void _event_callback_trampoline(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param);
static void _negotiate_data_handler_trampoline(uint8_t *data, int len, uint8_t **output_data,
int *output_len, bool *need_free);
static int _encrypt_func_trampoline(uint8_t iv8, uint8_t *crypt_data, int crypt_len);
static int _decrypt_func_trampoline(uint8_t iv8, uint8_t *crypt_data, int crypt_len);
static uint16_t _checksum_func_trampoline(uint8_t iv8, uint8_t *data, int len);
#ifdef CONFIG_BT_NIMBLE_ENABLED
static void _nimble_on_reset(int reason);
static void _nimble_on_sync();
static void _nimble_host_task(void *param);
#endif
// Security context, formerly blufi_sec struct
struct BlufiSecurity {
#define DH_SELF_PUB_KEY_LEN 128
uint8_t self_public_key[DH_SELF_PUB_KEY_LEN];
#define SHARE_KEY_LEN 128
uint8_t share_key[SHARE_KEY_LEN];
size_t share_len;
#define PSK_LEN 16
uint8_t psk[PSK_LEN];
uint8_t *dh_param;
int dh_param_len;
uint8_t iv[16];
mbedtls_dhm_context *dhm;
esp_aes_context *aes;
};
BlufiSecurity *m_sec;
// State variables
wifi_config_t m_sta_config{};
bool m_ble_is_connected;
bool m_sta_connected;
bool m_sta_got_ip;
bool m_provisioned;
bool m_deinited;
uint8_t m_sta_bssid[6]{};
uint8_t m_sta_ssid[32]{};
int m_sta_ssid_len;
bool m_sta_is_connecting;
esp_blufi_extra_info_t m_sta_conn_info{};
// WiFi scan related
std::vector<wifi_ap_record_t> 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;
};