toy-Kapi_Rtc/main/boards/common/wifi_board.cc
2026-01-20 16:55:17 +08:00

575 lines
22 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连接管理、
* 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 <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板级模块的日志输出
/**
* @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<WifiBoard*>(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<WifiBoard*>(user_data);
if (board) {
board->OnBluFiProvisioningEvent(event, data);
}
}