Rdzleo 8111515277 修复 Pendant 衍生项目无痛移植问题
实机验证通过后,按 Kapi 无屏底座路线补齐 Pendant RTC 吊坠项目的迁移修复。

1. BLE 配网与资源隔离
- sdkconfig.defaults 开启 BT 优先 PSRAM 分配,并将 LWIP socket 上限提升到 20
- sdkconfig.defaults.esp32s3 允许 BSS/NOINIT 放入 PSRAM,释放内部 SRAM 给 BLE/WiFi/RTC
- 配网模式 codec 使用 StartOutputOnly(),跳过麦克风 RX DMA 和 ES7210 输入链路
- ResetWifiConfiguration() 改为独立 wifi_reset task,避免在 iot_button/esp_timer 回调中阻塞延时
- WifiBoard 增加 IsWifiConfigMode(),供启动阶段判断是否走配网资源隔离路径

2. 音频底噪与 DMA 残留修复
- AudioCodec 增加 StartOutputOnly(),支持仅启动扬声器输出
- RTC 音频通道打开后灌入 200ms silence PCM,覆盖 I2S DMA 残留数据
- 软退出进入待命前重启 codec output 并再次灌静音,减少待命音/欢迎语前杂音
- box_audio_codec 在无硬件回采时使用 channel_mask=0,避免 I2S slot mask 被错误污染

3. 软件 loopback AEC
- 引入 esp_aec 底层同步 API,使用 DAC 输出复制构建 ref ring
- 上行 mic PCM 与延迟 ref 做同步消回声,适配无屏无硬件回采的 Pendant 形态
- AEC 采用 lazy init,减少启动阶段对 WiFi/BLE 内部 SRAM 的压力
- ref 静音时直接 passthrough,避免 AI 静音后误压制用户语音
- 在 player_pipeline_write 和 codec->OutputData 两条下行路径都追加 ref hook

4. RTC 连接稳定性与软退出
- VolcRtcProtocol 增加 LeaveRoom(bool notify_closed),支持 stop 房间但保留 rtc_handle
- hibernate 路径使用 LeaveRoom(false),避免关闭回调顺手关掉 codec output 导致待命音无声
- LeaveRoom/ForceRebuildEngine 重置 downlink_is_pcm_ 和首包标志,避免本地 Opus 音效被当 PCM 播成杂音
- OpenAudioChannel 连续失败 3 次后 ForceRebuildEngine,清理 RTC SDK 内部异常状态
- 加入 DIAG-RTC socket/heap/PSRAM/RSSI 日志,便于定位 ICE socket 和内存问题

5. Dialog watchdog 与 BOOT 唤醒
- Dialog watchdog 到期不再写 reboot_dlg_idle 后 esp_restart
- 新增 EnterIdleHibernate():软退房、清空残留音频队列、关闭麦克风、播放待命音后静默
- 新增 WakeFromHibernate():BOOT 唤醒后复用 RTC engine 并通过 ToggleChatState() 重连 RTC
- BOOT 单击优先判断 IsHibernating(),异步唤醒,避免走普通按键状态机
- hibernate 期间禁止 PowerSaveTimer 进入 Light Sleep,保护 I2C/codec 总线

6. 文档与衍生项目沉淀
- 更新石头光源属性检测方案文档
- 将 Pendant 实测通过的软退出、AEC、BLE 配网隔离经验同步到衍生项目移植规则
2026-05-29 13:36:36 +08:00

613 lines
23 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @file wifi_board.cc
* @brief WiFi板级管理模块实现文件
*
* 本文件实现了WiFi板级管理的相关功能包括WiFi连接管理、
* BLE蓝牙配网流程控制、网络状态监控等核心功能。
* 提供完整的网络连接解决方案实现。
*/
#include "wifi_board.h"
#include "display.h"
#include "application.h"
#include "system_info.h"
#include "font_awesome_symbols.h"
#include "settings.h"
#include "assets/lang_config.h"
#include "bluetooth_provisioning.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_netif_sntp.h"
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_http.h>
#include <esp_mqtt.h>
#include <esp_udp.h>
#include <tcp_transport.h>
#include <tls_transport.h>
#include <web_socket.h>
#include <esp_log.h>
#include <wifi_station.h>
#include <wifi_configuration_ap.h>
#include <ssid_manager.h>
static const char *TAG = "WifiBoard"; ///< 日志标签用于标识WiFi板级模块的日志输出
static void wifi_reset_task(void* arg) {
auto* board = static_cast<WifiBoard*>(arg);
ESP_LOGI(TAG, "🔄 重置WiFi配置设备将重启进入配网模式");
{
Settings settings("wifi", true);
settings.SetInt("force_ap", 1);
}
auto display = board->GetDisplay();
if (display) {
display->ShowNotification(Lang::Strings::ENTERING_WIFI_CONFIG_MODE);
}
vTaskDelay(pdMS_TO_TICKS(500));
ESP_LOGI(TAG, "🔄 正在重启设备...");
esp_restart();
vTaskDelete(NULL);
}
/**
* @brief WiFi板级管理构造函数
*
* 初始化WiFi板级管理对象读取NVS存储中的配置参数。
* 检查是否设置了强制AP模式标志如果设置则重置为0。
*/
WifiBoard::WifiBoard() {
// 读取NVS存储中的强制AP模式标志
Settings settings("wifi", true);
wifi_config_mode_ = settings.GetInt("force_ap") == 1;
// 如果检测到强制AP模式重置为0并记录日志
if (wifi_config_mode_) {
ESP_LOGI(TAG, "force_ap is set to 1, reset to 0");
settings.SetInt("force_ap", 0);
}
}
/**
* @brief 获取板级类型标识
* @return std::string 返回"wifi"字符串标识当前为WiFi板级
*/
std::string WifiBoard::GetBoardType() {
return "wifi";
}
/**
* @brief 进入WiFi配置模式
*
* 启动BLE蓝牙配网流程等待用户通过手机APP配置WiFi信息。
* 如果BLE配网启动失败会持续重试直到成功。
* 不再使用传统的WiFi AP配网模式。
*/
void WifiBoard::EnterWifiConfigMode() {
ESP_LOGI(TAG, "🔵 进入配网模式 - 使用BLE蓝牙配网");
// 使用 BLE 蓝牙配网
bool success = StartBleProvisioning();
ESP_LOGI(TAG, "🔍 BLE配网启动结果: %s", success ? "成功" : "失败");
if (success) {
ESP_LOGI(TAG, "✅ BLE配网启动成功等待手机连接");
return;
}
ESP_LOGW(TAG, "⚠️ BLE配网启动失败将持续重试");
// 持续重试
while (true) {
vTaskDelay(pdMS_TO_TICKS(5000));
ESP_LOGI(TAG, "🔄 重试启动BLE配网...");
if (StartBleProvisioning()) {
ESP_LOGI(TAG, "✅ BLE配网重试成功等待手机连接");
return;
}
ESP_LOGW(TAG, "❌ BLE配网重试失败继续重试...");
}
// 以下代码保留但不会执行用于将来可能重新启用WiFi AP配网
//ESP_LOGI(TAG, "📶 启动WiFi AP配网模式播放配网提示音此代码已被禁用");
auto& application = Application::GetInstance();
application.SetDeviceState(kDeviceStateWifiConfiguring);
auto& wifi_ap = WifiConfigurationAp::GetInstance();
wifi_ap.SetLanguage(Lang::CODE);
wifi_ap.SetSsidPrefix("Airhub");
wifi_ap.Start(); // 初始化AP模式射频
// 显示 WiFi 配置 AP 的 SSID 和 Web 服务器 URL
std::string hint = Lang::Strings::CONNECT_TO_HOTSPOT;
hint += wifi_ap.GetSsid();
hint += Lang::Strings::ACCESS_VIA_BROWSER;
hint += wifi_ap.GetWebServerUrl();
hint += "\n\n";
// 播报配置 WiFi 的提示
// application.Alert(Lang::Strings::WIFI_CONFIG_MODE, hint.c_str(), "", Lang::Sounds::P3_WIFICONFIG); 原有蜡笔小新音色播报
if(strcmp(CONFIG_DEVICE_ROLE, "KAKA") == 0){
application.Alert(Lang::Strings::WIFI_CONFIG_MODE, hint.c_str(), "", Lang::Sounds::P3_KAKA_WIFICONFIG);
}
else if(strcmp(CONFIG_DEVICE_ROLE, "RTC_Test") == 0){
application.Alert(Lang::Strings::WIFI_CONFIG_MODE, hint.c_str(), "", Lang::Sounds::P3_LALA_WIFICONFIG);
}
// Wait forever until reset after configuration
while (true) {
int free_sram = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
int min_free_sram = heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL);
ESP_LOGI(TAG, "Free internal: %u minimal internal: %u", free_sram, min_free_sram);
vTaskDelay(pdMS_TO_TICKS(10000));
}
}
/**
* @brief 启动网络连接
*
* 根据配置启动WiFi连接或BLE配网流程。
* 如果设置了配网模式或没有WiFi凭据则启动BLE配网
* 否则尝试连接已保存的WiFi网络。
*/
void WifiBoard::StartNetwork() {
// 用户可以在启动时按BOOT按钮进入WiFi配置模式
// 开机按BOOT进入配网模式
if (wifi_config_mode_) {
ESP_LOGI(TAG, "🔵 进入配网模式 - BLE蓝牙配网");
EnterWifiConfigMode();
return;
}
// 如果没有配置WiFi SSID优先尝试BLE配网
auto& ssid_manager = SsidManager::GetInstance(); // 获取SSID管理器实例
auto ssid_list = ssid_manager.GetSsidList(); // 获取SSID列表
if (ssid_list.empty()) {
ESP_LOGI(TAG, "🔍 未找到WiFi凭据启动BLE配网...");
if (StartBleProvisioning()) {
ESP_LOGI(TAG, "✅ BLE配网启动成功等待手机连接...");
return;
} else {
ESP_LOGW(TAG, "❌ BLE配网启动失败将重试");
vTaskDelay(pdMS_TO_TICKS(5000));
ESP_LOGI(TAG, "🔄 重试启动BLE配网...");
StartBleProvisioning();
return;
}
}
// WiFi凭据存在尝试直接连接
auto& wifi_station = WifiStation::GetInstance();
// 设置WiFi扫描开始回调
wifi_station.OnScanBegin([this]() {
auto display = Board::GetInstance().GetDisplay();
if (display) {
display->ShowNotification(Lang::Strings::SCANNING_WIFI, 30000);
}
});
// 设置WiFi连接开始回调
wifi_station.OnConnect([this](const std::string& ssid) {
auto display = Board::GetInstance().GetDisplay();
if (display) {
std::string notification = Lang::Strings::CONNECT_TO;
notification += ssid;
notification += "...";
display->ShowNotification(notification.c_str(), 30000);
}
// 根据标志决定是否播放网络连接语音提示
auto& application = Application::GetInstance();
if (!application.ShouldSkipDialogIdleSession()) {
// application.PlaySound(Lang::Sounds::P3_LIANJIEWANGLUO); 原有蜡笔小新 音色播报
if(strcmp(CONFIG_DEVICE_ROLE, "KAKA") == 0){
application.PlaySound(Lang::Sounds::P3_KAKA_LIANJIEWANGLUO);
}
else if(strcmp(CONFIG_DEVICE_ROLE, "RTC_Test") == 0){
application.PlaySound(Lang::Sounds::P3_LALA_LIANJIEWANGLUO);
}
ESP_LOGI(TAG, "Starting WiFi connection, playing network connection sound");
} else {
ESP_LOGI(TAG, "Skipping network connection sound due to dialog idle restart flag");
// 清除跳过标志,确保后续正常使用时能播放播报
application.ClearDialogIdleSkipSession();
}
});
// 设置WiFi连接成功回调
wifi_station.OnConnected([this](const std::string& ssid) {
auto display = Board::GetInstance().GetDisplay();
if (display) {
std::string notification = Lang::Strings::CONNECTED_TO;
notification += ssid;
display->ShowNotification(notification.c_str(), 30000);
}
});
wifi_station.OnReconnectTimeout([this]() {
auto& ws = WifiStation::GetInstance();
ws.Stop();
esp_wifi_restore();
ResetWifiConfiguration();
});
// 启动WiFi站点模式
wifi_station.Start();
// 尝试连接WiFi如果失败则尝试BLE配网
// 尝试连接WiFi如果失败则尝试BLE配网
// 增加WiFi连接超时时间避免过快进入配网模式
// if (!wifi_station.WaitForConnected(90 * 1000)) {
if (!wifi_station.WaitForConnected(10 * 1000)) {
wifi_station.Stop();// 停止WiFi连接尝试
esp_wifi_restore();// 恢复WiFi默认配置
ResetWifiConfiguration();// 重置WiFi配置
return;
} else {
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG("pool.ntp.org");
esp_netif_sntp_init(&config);
int retry = 0;
while (esp_netif_sntp_sync_wait(1000 / portTICK_PERIOD_MS) == ESP_ERR_TIMEOUT && ++retry < 5) {}
setenv("TZ", "CST-8", 1);
tzset();
}
}
/**
* @brief 创建HTTP客户端对象
* @return Http* 返回ESP HTTP客户端对象指针
*/
Http* WifiBoard::CreateHttp() {
return new EspHttp();
}
/**
* @brief 创建WebSocket客户端对象
* @return WebSocket* 返回WebSocket客户端对象指针如果未配置则返回nullptr
*
* 根据配置的WebSocket URL选择使用TLS或TCP传输协议
*/
WebSocket* WifiBoard::CreateWebSocket() {
#ifdef CONFIG_CONNECTION_TYPE_WEBSOCKET
std::string url = CONFIG_WEBSOCKET_URL;
if (url.find("wss://") == 0) {
return new WebSocket(new TlsTransport()); // 使用TLS安全传输
} else {
return new WebSocket(new TcpTransport()); // 使用TCP传输
}
#endif
return nullptr;
}
/**
* @brief 创建MQTT客户端对象
* @return Mqtt* 返回ESP MQTT客户端对象指针
*/
Mqtt* WifiBoard::CreateMqtt() {
return new EspMqtt();
}
Udp* WifiBoard::CreateUdp() {
return new EspUdp();
}
// 获取网络状态图标
const char* WifiBoard::GetNetworkStateIcon() {
if (wifi_config_mode_) {// 如果是配网模式
return FONT_AWESOME_WIFI;// 返回WiFi图标
}
auto& wifi_station = WifiStation::GetInstance();// 获取WiFi配置实例
if (!wifi_station.IsConnected()) {// 如果未连接到WiFi
return FONT_AWESOME_WIFI_OFF;// 返回WiFi断开图标
}
int8_t rssi = wifi_station.GetRssi();// 获取WiFi信号强度
if (rssi >= -60) { // 信号强度大于等于-60dBm
return FONT_AWESOME_WIFI;// 返回WiFi图标
} else if (rssi >= -70) {
return FONT_AWESOME_WIFI_FAIR;// 返回WiFi信号中等图标
} else {
return FONT_AWESOME_WIFI_WEAK;// 返回WiFi信号弱图标
}
}
// 获取板级JSON配置
std::string WifiBoard::GetBoardJson() {
// Set the board type for OTA
auto& wifi_station = WifiStation::GetInstance();
std::string board_json = std::string("{\"type\":\"" BOARD_TYPE "\",");// 板级JSON配置字符串包含设备类型、名称、角色、SSID、信号强度、通道、IP地址和MAC地址
board_json += "\"name\":\"" BOARD_NAME "\",";
board_json += "\"role\":\"" CONFIG_DEVICE_ROLE "\","; // 添加设备角色字段用于OTA升级时的角色匹配
if (!wifi_config_mode_) {
board_json += "\"ssid\":\"" + wifi_station.GetSsid() + "\",";
board_json += "\"rssi\":" + std::to_string(wifi_station.GetRssi()) + ",";
board_json += "\"channel\":" + std::to_string(wifi_station.GetChannel()) + ",";
board_json += "\"ip\":\"" + wifi_station.GetIpAddress() + "\",";
}
board_json += "\"mac\":\"" + SystemInfo::GetMacAddress() + "\"}";
return board_json;
}
// 设置低功耗模式 新增配网模式下禁用省电模式
void WifiBoard::SetPowerSaveMode(bool enabled) {
// 如果正在进行 BLE 配网,强制禁用省电模式以确保 MAC 地址能正常发送到手机端
if (enabled && IsBleProvisioningActive()) {
ESP_LOGI(TAG, "🔵 配网模式下,已强制禁用省电模式!");
enabled = false;
}
ESP_LOGI(TAG, "🔋 电源管理模式切换: %s", enabled ? "启用低功耗模式" : "禁用低功耗模式(恢复正常模式)");
auto& wifi_station = WifiStation::GetInstance();
wifi_station.SetPowerSaveMode(enabled);
}
// 重置WiFi配置设备将重启进入配网模式
void WifiBoard::ResetWifiConfiguration() {
if (xTaskCreate(wifi_reset_task, "wifi_reset", 4096, this, 5, nullptr) != pdPASS) {
ESP_LOGE(TAG, "创建 wifi_reset 任务失败,直接写入配置并重启");
Settings settings("wifi", true);
settings.SetInt("force_ap", 1);
esp_restart();
}
}
// 启动BLE配网服务
bool WifiBoard::StartBleProvisioning() {
ESP_LOGI(TAG, "🔵 正在启动BLE蓝牙配网服务...");
Application::GetInstance().StopAudioProcessor();// 停止音频处理器,确保在配网过程中不处理音频数据
Application::GetInstance().ClearAudioQueue();// 清空音频队列,移除所有待处理的音频数据
// 初始化BLE配网服务
if (!bluetooth_provisioning_.Initialize()) {
ESP_LOGE(TAG, "❌ BLE蓝牙配网初始化失败");
ESP_LOGI(TAG, "🔍 BLE Initialize返回结果: false");
return false;
}
ESP_LOGI(TAG, "🔍 BLE Initialize返回结果: true");
// 为BLE事件设置回调函数
bluetooth_provisioning_.SetCallback([this](BluetoothProvisioningEvent event, void* data) {
OnBleProvisioningEvent(event, data);
});
// 启动BLE配网服务设备名称由 StartProvisioning 内部构建: Airhub_ + BLE MAC
if (!bluetooth_provisioning_.StartProvisioning()) {
ESP_LOGE(TAG, "❌ BLE蓝牙配网启动失败");
return false;
}
ESP_LOGI(TAG, "✅ BLE蓝牙配网启动成功");
ESP_LOGI(TAG, "📱 请使用支持BLE的手机APP连接设备进行配网");
ble_provisioning_active_ = true; // 标记BLE配网服务已激活
ble_provisioning_success_ = false;// 标记BLE配网服务未成功
ble_start_time_ = xTaskGetTickCount();// 记录启动时间,用于超时检测
// 显示BLE配网通知
auto display = GetDisplay();
if (display) {
display->ShowNotification("BLE配网模式", 30000);
}
// 播放配网提示音
auto& application = Application::GetInstance();
if(strcmp(CONFIG_DEVICE_ROLE, "KAKA") == 0){
application.Alert("BLE配网模式", "请使用手机APP搜索Airhub_开头的蓝牙设备", "", Lang::Sounds::P3_KAKA_WIFICONFIG);
}
else if(strcmp(CONFIG_DEVICE_ROLE, "RTC_Test") == 0){
application.Alert("BLE配网模式", "请使用手机APP搜索Airhub_开头的蓝牙设备", "", Lang::Sounds::P3_LALA_WIFICONFIG);
}
// 创建任务用于监控BLE配网状态
xTaskCreate([](void* param) {
WifiBoard* board = static_cast<WifiBoard*>(param); // 转换参数为WifiBoard指针
board->MonitorBleProvisioning();// 监控BLE配网状态
vTaskDelete(nullptr);// 删除任务,因为任务只执行一次
}, "ble_prov_monitor", 4096, this, 5, nullptr);// 创建任务优先级为5栈大小为4096字节
return true;// 启动成功返回true
}
// // BLE JSON Service 配网(暂不使用,保留代码)
// bool WifiBoard::StartBleJsonProvisioning() {
// ESP_LOGI(TAG, "🔵 正在启动BLE JSON配网服务...");
// Application::GetInstance().StopAudioProcessor();
// Application::GetInstance().ClearAudioQueue();
// if (!ble_json_service_.Initialize()) {
// ESP_LOGE(TAG, "❌ BLE JSON服务初始化失败");
// return false;
// }
// ble_json_service_.SetCommandCallback(
// [this](const std::string& cmd, int msg_id, cJSON* data) {
// Application::GetInstance().HandleBleJsonCommand(cmd, msg_id, data, ble_json_service_);
// });
// if (!ble_json_service_.Start("Airhub_Ble")) {
// ESP_LOGE(TAG, "❌ BLE JSON服务启动失败");
// return false;
// }
// ESP_LOGI(TAG, "✅ BLE JSON配网启动成功");
// ble_provisioning_active_ = true;
// ble_start_time_ = xTaskGetTickCount();
// auto display = GetDisplay();
// if (display) {
// display->ShowNotification("BLE配网模式", 30000);
// }
// auto& application = Application::GetInstance();
// if (strcmp(CONFIG_DEVICE_ROLE, "KAKA") == 0) {
// application.Alert("BLE配网模式", "请使用手机APP搜索Airhub_开头的蓝牙设备", "", Lang::Sounds::P3_KAKA_WIFICONFIG);
// } else if (strcmp(CONFIG_DEVICE_ROLE, "RTC_Test") == 0) {
// application.Alert("BLE配网模式", "请使用手机APP搜索Airhub_开头的蓝牙设备", "", Lang::Sounds::P3_LALA_WIFICONFIG);
// }
// while (true) {
// int free_sram = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
// int min_free_sram = heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL);
// ESP_LOGI(TAG, "BLE配网等待中... Free internal: %u minimal internal: %u", free_sram, min_free_sram);
// vTaskDelay(pdMS_TO_TICKS(10000));
// }
// return true;
// }
// 监控BLE配网状态
void WifiBoard::MonitorBleProvisioning() {
ESP_LOGI(TAG, "Starting BLE provisioning monitor...");
while (ble_provisioning_active_) {
TickType_t current_time = xTaskGetTickCount();
TickType_t elapsed_time = current_time - ble_start_time_;
// Check for timeout (2 minutes) - 仅记录日志不再切换到WiFi配网
if (elapsed_time >= pdMS_TO_TICKS(BLE_PROV_TIMEOUT_MS)) {
ESP_LOGW(TAG, "BLE provisioning timeout, but continuing BLE mode (no fallback to WiFi AP)");
// 增加延迟避免快速重新进入配网循环
ESP_LOGI(TAG, "🔵 BLE配网超时等待10秒后重置计时器继续等待配网");
vTaskDelay(pdMS_TO_TICKS(10000)); // 等待10秒,冷却期
// 重置计时器继续等待BLE配网
ble_start_time_ = xTaskGetTickCount();
ESP_LOGI(TAG, "🔵 计时器已重置继续等待BLE配网");
}
// Check if provisioning was successful
if (ble_provisioning_success_) {
ESP_LOGI(TAG, "BLE provisioning completed successfully");
ble_provisioning_active_ = false;
// Stop BLE provisioning
// 停止BLE配网
bluetooth_provisioning_.StopProvisioning();
// Try to connect to the configured WiFi
auto& wifi_station = WifiStation::GetInstance();
wifi_station.Start();
// 增加WiFi连接重试逻辑避免过快重新进入配网模式
int retry_count = 0; // 重试次数
const int max_retries = 3; // 最大重试次数
const int retry_timeout = 60 * 1000; // 60秒超时
// 重试连接WiFi
while (retry_count < max_retries) {
ESP_LOGI(TAG, "WiFi connection attempt %d/%d after BLE provisioning", retry_count + 1, max_retries);
// 等待WiFi连接成功
if (wifi_station.WaitForConnected(retry_timeout)) {
ESP_LOGI(TAG, "WiFi connection successful after BLE provisioning (attempt %d)", retry_count + 1);
auto display = GetDisplay();
if (display) {
display->ShowNotification("WiFi连接成功", 5000);
}
return;
}
retry_count++;// 增加重试次数
if (retry_count < max_retries) {
ESP_LOGW(TAG, "WiFi connection failed (attempt %d/%d), retrying in 10 seconds...", retry_count, max_retries);
vTaskDelay(pdMS_TO_TICKS(10000)); // 等待10秒后重试
wifi_station.Stop();
vTaskDelay(pdMS_TO_TICKS(2000)); // 等待2秒确保完全停止
wifi_station.Start(); // 重新启动WiFi连接
} else {
ESP_LOGW(TAG, "WiFi connection failed after %d attempts, entering AP mode", max_retries);
wifi_station.Stop();
wifi_config_mode_ = true;
EnterWifiConfigMode();
return;
}
}
}
// Wait before next check
vTaskDelay(pdMS_TO_TICKS(1000)); // 等待1秒后检查
}
}
// 处理BLE配网事件
void WifiBoard::OnBleProvisioningEvent(BluetoothProvisioningEvent event, void* data) {
switch (event) {
case BluetoothProvisioningEvent::CLIENT_CONNECTED:
ESP_LOGI(TAG, "BLE client connected");
{
auto display = GetDisplay();
if (display) {
display->ShowNotification("客户端已连接", 5000);
}
}
break;
// 客户端断开事件
case BluetoothProvisioningEvent::CLIENT_DISCONNECTED:
ESP_LOGI(TAG, "BLE client disconnected");
{
auto display = GetDisplay();
if (display) {
display->ShowNotification("客户端已断开", 5000);
}
}
break;
// 接收WiFi凭据事件
case BluetoothProvisioningEvent::WIFI_CREDENTIALS:
ESP_LOGI(TAG, "WiFi credentials received via BLE");
{
auto display = GetDisplay();
if (display) {
display->ShowNotification("WiFi凭据已接收", 5000);
}
}
break;
// 连接成功事件
case BluetoothProvisioningEvent::WIFI_CONNECTED:
ESP_LOGI(TAG, "设备配网成功,已连接到WiFi网络!");
ble_provisioning_success_ = true;
{
auto display = GetDisplay();
if (display) {
display->ShowNotification("WiFi连接成功", 5000);
}
auto& application = Application::GetInstance();
// application.PlaySound(Lang::Sounds::P3_LIANJIEWANGLUO); 原有蜡笔小新 音色播报
if(strcmp(CONFIG_DEVICE_ROLE, "KAKA") == 0){
application.PlaySound(Lang::Sounds::P3_KAKA_LIANJIEWANGLUO);
}
else if(strcmp(CONFIG_DEVICE_ROLE, "RTC_Test") == 0){
application.PlaySound(Lang::Sounds::P3_LALA_LIANJIEWANGLUO);
}
}
break;
// 连接失败事件
case BluetoothProvisioningEvent::WIFI_FAILED:
ESP_LOGW(TAG, "WiFi connection failed via BLE");
ble_provisioning_active_ = false;
{
auto display = GetDisplay();
if (display) {
display->ShowNotification("WiFi连接失败", 5000);
}
}
break;
default:
break;
}
}
// BLE配网回调函数
void WifiBoard::BleProvisioningCallback(BluetoothProvisioningEvent event, void* data, void* user_data) {
WifiBoard* board = static_cast<WifiBoard*>(user_data);
if (board) {
board->OnBleProvisioningEvent(event, data);
}
}