/** * @file wifi_board.cc * @brief WiFi板级管理模块实现文件 * * 本文件实现了WiFi板级管理的相关功能,包括WiFi连接管理、 * BluFi蓝牙配网流程控制、网络状态监控等核心功能。 * 提供完整的网络连接解决方案实现。 */ #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 #include #include #include #include #include #include #include #include #include #include #include static const char *TAG = "WifiBoard"; ///< 日志标签,用于标识WiFi板级模块的日志输出 /** * @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配置模式 * * 启动BluFi蓝牙配网流程,等待用户通过手机APP配置WiFi信息。 * 如果BluFi配网启动失败,会持续重试直到成功。 * 不再使用传统的WiFi AP配网模式。 */ void WifiBoard::EnterWifiConfigMode() { ESP_LOGI(TAG, "🔵 进入配网模式 - 使用BluFi蓝牙配网"); // 直接启动BluFi配网,不再回退到WiFi AP模式 bool blufi_success = StartBluFiProvisioning(); ESP_LOGI(TAG, "🔍 BluFi配网启动结果: %s", blufi_success ? "成功" : "失败"); if (blufi_success) { ESP_LOGI(TAG, "✅ BluFi配网启动成功,等待手机连接"); return; } ESP_LOGW(TAG, "⚠️ BluFi配网启动失败,将持续重试BluFi配网(不使用WiFi AP模式)"); ESP_LOGI(TAG, "🔄 持续重试BluFi蓝牙配网..."); // 持续重试BluFi配网 while (true) { vTaskDelay(pdMS_TO_TICKS(5000)); // 等待5秒后重试 ESP_LOGI(TAG, "🔄 重试启动BluFi蓝牙配网..."); if (StartBluFiProvisioning()) { ESP_LOGI(TAG, "✅ BluFi配网重试成功,等待手机连接"); return; } ESP_LOGW(TAG, "❌ BluFi配网重试失败,继续重试..."); } // 以下代码保留但不会执行,用于将来可能重新启用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连接或BluFi配网流程。 * 如果设置了配网模式或没有WiFi凭据,则启动BluFi配网; * 否则尝试连接已保存的WiFi网络。 */ void WifiBoard::StartNetwork() { // 用户可以在启动时按BOOT按钮进入WiFi配置模式 // 开机按BOOT进入配网模式 if (wifi_config_mode_) { ESP_LOGI(TAG, "🔵 进入配网模式 - BluFi蓝牙配网"); EnterWifiConfigMode(); return; } // 如果没有配置WiFi SSID,优先尝试BluFi配网 auto& ssid_manager = SsidManager::GetInstance(); // 获取SSID管理器实例 auto ssid_list = ssid_manager.GetSsidList(); // 获取SSID列表 if (ssid_list.empty()) { ESP_LOGI(TAG, "🔍 未找到WiFi凭据,启动BluFi蓝牙配网..."); if (StartBluFiProvisioning()) { ESP_LOGI(TAG, "✅ BluFi蓝牙配网启动成功,等待手机连接..."); // BluFi配网启动成功,等待完成或超时 return; } else { // BluFi配网启动失败,继续尝试重新启动BluFi配网 ESP_LOGW(TAG, "❌ BluFi蓝牙配网启动失败,将持续重试BluFi配网"); // 延迟后重试BluFi配网 vTaskDelay(pdMS_TO_TICKS(5000)); // 等待5秒后重试 ESP_LOGI(TAG, "🔄 重试启动BluFi蓝牙配网..."); StartBluFiProvisioning(); 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,如果失败则尝试BluFi配网 // 尝试连接WiFi,如果失败则尝试BluFi配网 // 增加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) { // 如果正在进行 BluFi 配网,强制禁用省电模式以确保 MAC 地址能正常发送到手机端 if (enabled && IsBluFiProvisioningActive()) { ESP_LOGI(TAG, "🔵 配网模式下,已强制禁用省电模式!"); enabled = false; } ESP_LOGI(TAG, "🔋 电源管理模式切换: %s", enabled ? "启用低功耗模式" : "禁用低功耗模式(恢复正常模式)"); auto& wifi_station = WifiStation::GetInstance(); wifi_station.SetPowerSaveMode(enabled); } // 重置WiFi配置,设备将重启进入配网模式 void WifiBoard::ResetWifiConfiguration() { ESP_LOGI(TAG, "🔄 重置WiFi配置,设备将重启进入配网模式"); // 设置WiFi配网标志位,确保重启后能正确进入配网模式 { Settings settings("wifi", true);// 创建WiFi配置设置对象,第二个参数true表示立即保存到NVS存储 settings.SetInt("force_ap", 1);// 设置force_ap标志为1,这个标志会在设备重启后被检查,如果为1则启动WiFi配网服务,启动时强制进入AP配网模式 } // 获取显示设备对象并显示配网提示信息 auto display = GetDisplay(); if (display) { // 在屏幕上显示"进入WiFi配置模式"的多语言提示信息 // 让用户知道设备即将重启并进入配网模式 display->ShowNotification(Lang::Strings::ENTERING_WIFI_CONFIG_MODE); } vTaskDelay(pdMS_TO_TICKS(500)); // 等待500ms,确保NVS配置保存完成,如果有屏幕显示,可以增加到1000ms让用户看清提示 ESP_LOGI(TAG, "🔄 正在重启设备..."); esp_restart(); // 重启设备,重启后会进入配网模式 } // 启动BluFi配网服务 bool WifiBoard::StartBluFiProvisioning() { ESP_LOGI(TAG, "🔵 正在启动BluFi蓝牙配网服务..."); Application::GetInstance().StopAudioProcessor();// 停止音频处理器,确保在配网过程中不处理音频数据 Application::GetInstance().ClearAudioQueue();// 清空音频队列,移除所有待处理的音频数据 // 初始化BluFi配网服务 if (!bluetooth_provisioning_.Initialize()) { ESP_LOGE(TAG, "❌ BluFi蓝牙配网初始化失败"); ESP_LOGI(TAG, "🔍 BluFi Initialize返回结果: false"); return false; } ESP_LOGI(TAG, "🔍 BluFi Initialize返回结果: true"); // 为BluFi事件设置回调函数 bluetooth_provisioning_.SetCallback([this](BluetoothProvisioningEvent event, void* data) { OnBluFiProvisioningEvent(event, data); }); // 使用设备名称启动BluFi配网服务(2.设备发现,设备名称 Airhub777) std::string device_name = BLU_NAME; // 蓝牙配网服务启动失败,StartProvisioning为蓝牙服务启动函数 if (!bluetooth_provisioning_.StartProvisioning(device_name.c_str())) { ESP_LOGE(TAG, "❌ BluFi蓝牙配网启动失败"); return false; } ESP_LOGI(TAG, "✅ BluFi蓝牙配网启动成功,设备名称: %s", device_name.c_str()); ESP_LOGI(TAG, "📱 请使用支持BluFi的手机APP连接设备进行配网"); blufi_provisioning_active_ = true; // 标记BluFi配网服务已激活 blufi_provisioning_success_ = false;// 标记BluFi配网服务未成功 blufi_start_time_ = xTaskGetTickCount();// 记录启动时间,用于超时检测 // 显示BluFi配网通知 auto display = GetDisplay(); if (display) { std::string notification = "BluFi配网模式\n设备名: " + device_name; display->ShowNotification(notification.c_str(), 30000); } // Play BluFi provisioning sound auto& application = Application::GetInstance(); // application.Alert("BluFi配网模式", ("请使用手机APP连接设备: " + device_name).c_str(), "", Lang::Sounds::P3_WIFICONFIG); 原有蜡笔小新音色 if(strcmp(CONFIG_DEVICE_ROLE, "KAKA") == 0){ application.Alert("BluFi配网模式", ("请使用手机APP连接设备: " + device_name).c_str(), "", Lang::Sounds::P3_KAKA_WIFICONFIG); } else if(strcmp(CONFIG_DEVICE_ROLE, "RTC_Test") == 0){ application.Alert("BluFi配网模式", ("请使用手机APP连接设备: " + device_name).c_str(), "", Lang::Sounds::P3_LALA_WIFICONFIG); } // 创建任务,用于监控BluFi配网状态 xTaskCreate([](void* param) { WifiBoard* board = static_cast(param); // 转换参数为WifiBoard指针 board->MonitorBluFiProvisioning();// 监控BluFi配网状态 vTaskDelete(nullptr);// 删除任务,因为任务只执行一次 }, "blufi_monitor", 4096, this, 5, nullptr);// 创建任务,优先级为5,栈大小为4096字节 return true;// 启动成功,返回true } // 监控BluFi配网状态 void WifiBoard::MonitorBluFiProvisioning() { ESP_LOGI(TAG, "Starting BluFi provisioning monitor..."); while (blufi_provisioning_active_) { TickType_t current_time = xTaskGetTickCount(); TickType_t elapsed_time = current_time - blufi_start_time_; // Check for timeout (2 minutes) - 仅记录日志,不再切换到WiFi配网 if (elapsed_time >= pdMS_TO_TICKS(BLUFI_TIMEOUT_MS)) { ESP_LOGW(TAG, "BluFi provisioning timeout, but continuing BluFi mode (no fallback to WiFi AP)"); // 增加延迟避免快速重新进入配网循环 ESP_LOGI(TAG, "🔵 BluFi配网超时,等待10秒后重置计时器继续等待配网"); vTaskDelay(pdMS_TO_TICKS(10000)); // 等待10秒,冷却期 // 重置计时器,继续等待BluFi配网 blufi_start_time_ = xTaskGetTickCount(); ESP_LOGI(TAG, "🔵 计时器已重置,继续等待BluFi配网"); } // Check if provisioning was successful if (blufi_provisioning_success_) { ESP_LOGI(TAG, "BluFi provisioning completed successfully"); blufi_provisioning_active_ = false; // Stop BluFi provisioning // 停止BluFi配网 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 BluFi provisioning", retry_count + 1, max_retries); // 等待WiFi连接成功 if (wifi_station.WaitForConnected(retry_timeout)) { ESP_LOGI(TAG, "WiFi connection successful after BluFi 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秒后检查 } } // 处理BluFi配网事件 void WifiBoard::OnBluFiProvisioningEvent(BluetoothProvisioningEvent event, void* data) { switch (event) { case BluetoothProvisioningEvent::CLIENT_CONNECTED: ESP_LOGI(TAG, "BluFi client connected"); { auto display = GetDisplay(); if (display) { display->ShowNotification("客户端已连接", 5000); } } break; // 客户端断开事件 case BluetoothProvisioningEvent::CLIENT_DISCONNECTED: ESP_LOGI(TAG, "BluFi client disconnected"); { auto display = GetDisplay(); if (display) { display->ShowNotification("客户端已断开", 5000); } } break; // 接收WiFi凭据事件 case BluetoothProvisioningEvent::WIFI_CREDENTIALS: ESP_LOGI(TAG, "WiFi credentials received via BluFi"); { auto display = GetDisplay(); if (display) { display->ShowNotification("WiFi凭据已接收", 5000); } } break; // 连接成功事件 case BluetoothProvisioningEvent::WIFI_CONNECTED: ESP_LOGI(TAG, "设备配网成功,已连接到WiFi网络!"); blufi_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 BluFi"); blufi_provisioning_active_ = false; { auto display = GetDisplay(); if (display) { display->ShowNotification("WiFi连接失败", 5000); } } break; default: break; } } // BluFi配网回调函数 void WifiBoard::BluFiProvisioningCallback(BluetoothProvisioningEvent event, void* data, void* user_data) { WifiBoard* board = static_cast(user_data); if (board) { board->OnBluFiProvisioningEvent(event, data); } }