Baji_Rtc_Toy/main/bluetooth_provisioning.cc

1418 lines
52 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 bluetooth_provisioning.cc
* @brief 蓝牙配网模块实现(基于自定义 GATT Server
*
* 使用自定义 BLE GATT Server + 原始广播数据raw advertising实现配网。
* 采用 dzbj 项目验证的 esp_ble_gap_config_adv_data_raw() 方式,
* 确保手机系统蓝牙可搜索到设备名称。
*
* 保留完整的 WiFi 配网业务逻辑:
* - WiFi 凭据接收、验证和连接管理
* - 配网状态机和事件回调
* - WiFi 连接状态监控和报告
* - MAC 地址发送
* - 配网成功后自动保存和重启
*/
#include "bluetooth_provisioning.h"
#include "esp_log.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_gatt_common_api.h"
#include "esp_wifi.h"
#include "esp_netif.h"
#include "freertos/event_groups.h"
#include "freertos/timers.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "application.h"
#include "assets/lang_config.h"
#include <ssid_manager.h>
#include "nvs_flash.h"
#include "nvs.h"
#include <cstring>
/// 日志标签
#define TAG "BluetoothProvisioning"
/// WiFi连接成功事件位
#define WIFI_CONNECTED_BIT BIT0
/// WiFi连接失败事件位
#define WIFI_FAIL_BIT BIT1
/// 静态单例实例指针
BluetoothProvisioning* BluetoothProvisioning::instance_ = nullptr;
/// WiFi事件组句柄
static EventGroupHandle_t s_wifi_event_group = nullptr;
/// WiFi连接重试计数器
static int s_retry_num = 0;
/// 最大重试次数
static const int MAX_RETRY = 2;
/// WiFi连接超时时间毫秒
static const int WIFI_CONNECT_TIMEOUT_MS = 30000;
/// WiFi连接超时定时器句柄
static TimerHandle_t wifi_connect_timer = nullptr;
// ============================================================
// GATT 静态数据定义
// ============================================================
// UUID 定义
static esp_bt_uuid_t prov_service_uuid = {
.len = ESP_UUID_LEN_16,
.uuid = {.uuid16 = PROV_SERVICE_UUID},
};
static esp_bt_uuid_t prov_write_char_uuid = {
.len = ESP_UUID_LEN_16,
.uuid = {.uuid16 = PROV_CHAR_WRITE_UUID},
};
static esp_bt_uuid_t prov_notify_char_uuid = {
.len = ESP_UUID_LEN_16,
.uuid = {.uuid16 = PROV_CHAR_NOTIFY_UUID},
};
static esp_bt_uuid_t cccd_uuid = {
.len = ESP_UUID_LEN_16,
.uuid = {.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG},
};
// 特征值缓冲区
static uint8_t prov_write_val[PROV_LOCAL_MTU] = {0};
static uint8_t prov_notify_val[PROV_LOCAL_MTU] = {0};
static uint8_t cccd_val[2] = {0x00, 0x00};
static esp_attr_value_t prov_write_attr = {
.attr_max_len = PROV_LOCAL_MTU,
.attr_len = 1,
.attr_value = prov_write_val,
};
static esp_attr_value_t prov_notify_attr = {
.attr_max_len = PROV_LOCAL_MTU,
.attr_len = 1,
.attr_value = prov_notify_val,
};
static esp_attr_value_t cccd_attr = {
.attr_max_len = 2,
.attr_len = 2,
.attr_value = cccd_val,
};
// 广播参数 (参考 dzbj: 快速广播间隔,确保手机快速发现)
static esp_ble_adv_params_t prov_adv_params = {
.adv_int_min = 0x20, // 20ms (dzbj 相同)
.adv_int_max = 0x40, // 40ms
.adv_type = ADV_TYPE_IND,
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
.peer_addr = {0},
.peer_addr_type = BLE_ADDR_TYPE_PUBLIC,
.channel_map = ADV_CHNL_ALL,
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};
// 原始广播数据 (运行时动态构建)
static uint8_t prov_adv_raw_data[31];
static uint8_t prov_adv_raw_len = 0;
// 扫描响应数据 (携带 TX Power + BLE MAC 地址)
static uint8_t prov_scan_rsp_data[31];
static uint8_t prov_scan_rsp_len = 0;
// ============================================================
// 构造 / 析构
// ============================================================
BluetoothProvisioning::BluetoothProvisioning()
: state_(BluetoothProvisioningState::IDLE)
, callback_(nullptr)
, client_connected_(false)
, initialized_(false)
, delayed_disconnect_(false)
, wifi_connecting_(false)
, mac_address_sent_(false) {
wifi_credentials_.ssid.clear();
wifi_credentials_.password.clear();
memset(wifi_credentials_.bssid, 0, sizeof(wifi_credentials_.bssid));
wifi_credentials_.bssid_set = false;
instance_ = this;
ESP_LOGI(TAG, "蓝牙配网对象创建完成");
}
BluetoothProvisioning::~BluetoothProvisioning() {
if (initialized_) {
Deinitialize();
}
instance_ = nullptr;
ESP_LOGI(TAG, "蓝牙配网对象销毁完成");
}
// ============================================================
// Initialize — 初始化 BLE 栈 + GATT 服务 + WiFi 事件
// ============================================================
bool BluetoothProvisioning::Initialize() {
if (initialized_) {
ESP_LOGW(TAG, "蓝牙配网已经初始化");
return true;
}
SetState(BluetoothProvisioningState::INITIALIZING);
esp_err_t ret;
// 步骤1: 初始化WiFi模块
ESP_LOGI(TAG, "初始化WiFi...");
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ret = esp_wifi_init(&cfg);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "WiFi初始化失败: %s", esp_err_to_name(ret));
SetState(BluetoothProvisioningState::FAILED);
return false;
}
ret = esp_wifi_set_mode(WIFI_MODE_STA);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "WiFi模式设置失败: %s", esp_err_to_name(ret));
SetState(BluetoothProvisioningState::FAILED);
return false;
}
ret = esp_wifi_start();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "WiFi启动失败: %s", esp_err_to_name(ret));
SetState(BluetoothProvisioningState::FAILED);
return false;
}
ESP_LOGI(TAG, "WiFi初始化完成");
// 步骤2: 初始化蓝牙控制器
ESP_LOGI(TAG, "初始化蓝牙控制器...");
esp_bt_controller_status_t ctl_status = esp_bt_controller_get_status();
#if CONFIG_IDF_TARGET_ESP32
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));
#else
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
#endif
if (ctl_status == ESP_BT_CONTROLLER_STATUS_ENABLED) {
esp_bt_controller_disable();
ctl_status = esp_bt_controller_get_status();
}
if (ctl_status == ESP_BT_CONTROLLER_STATUS_INITED) {
esp_bt_controller_deinit();
}
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
ret = esp_bt_controller_init(&bt_cfg);
if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
ESP_LOGE(TAG, "蓝牙控制器初始化失败: %s", esp_err_to_name(ret));
SetState(BluetoothProvisioningState::FAILED);
return false;
}
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
ESP_LOGE(TAG, "蓝牙控制器启用失败: %s", esp_err_to_name(ret));
SetState(BluetoothProvisioningState::FAILED);
return false;
}
// 步骤3: 初始化 Bluedroid 协议栈
ESP_LOGI(TAG, "初始化Bluedroid协议栈...");
auto bd_status = esp_bluedroid_get_status();
if (bd_status == ESP_BLUEDROID_STATUS_ENABLED) {
esp_bluedroid_disable();
bd_status = esp_bluedroid_get_status();
}
if (bd_status == ESP_BLUEDROID_STATUS_INITIALIZED) {
esp_bluedroid_deinit();
}
ret = esp_bluedroid_init();
if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
ESP_LOGE(TAG, "Bluedroid初始化失败: %s", esp_err_to_name(ret));
SetState(BluetoothProvisioningState::FAILED);
return false;
}
ret = esp_bluedroid_enable();
if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
ESP_LOGE(TAG, "Bluedroid启用失败: %s", esp_err_to_name(ret));
SetState(BluetoothProvisioningState::FAILED);
return false;
}
// 步骤4: 注册 GAP 和 GATTS 回调
ESP_LOGI(TAG, "注册 BLE GAP/GATTS 回调...");
ret = esp_ble_gap_register_callback(GapEventHandler);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "GAP回调注册失败: %s", esp_err_to_name(ret));
SetState(BluetoothProvisioningState::FAILED);
return false;
}
ret = esp_ble_gatts_register_callback(GattsEventHandler);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "GATTS回调注册失败: %s", esp_err_to_name(ret));
SetState(BluetoothProvisioningState::FAILED);
return false;
}
ret = esp_ble_gatts_app_register(PROV_APP_ID);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "GATTS App注册失败: %s", esp_err_to_name(ret));
SetState(BluetoothProvisioningState::FAILED);
return false;
}
ret = esp_ble_gatt_set_local_mtu(PROV_LOCAL_MTU);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "设置MTU失败: %s (可能已设置)", esp_err_to_name(ret));
}
// 步骤5: 创建WiFi事件同步机制
if (s_wifi_event_group == nullptr) {
s_wifi_event_group = xEventGroupCreate();
if (s_wifi_event_group == nullptr) {
ESP_LOGE(TAG, "WiFi事件组创建失败");
SetState(BluetoothProvisioningState::FAILED);
return false;
}
}
// 步骤6: 注册WiFi和IP事件处理器
ESP_LOGI(TAG, "注册WiFi事件处理器...");
ret = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &WiFiEventHandler, this);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "WiFi事件处理器注册失败: %s", esp_err_to_name(ret));
SetState(BluetoothProvisioningState::FAILED);
return false;
}
ret = esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &IPEventHandler, this);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "IP事件处理器注册失败: %s", esp_err_to_name(ret));
SetState(BluetoothProvisioningState::FAILED);
return false;
}
initialized_ = true;
SetState(BluetoothProvisioningState::IDLE);
ESP_LOGI(TAG, "蓝牙配网初始化完成 (GATT Server 模式)");
ESP_LOGI(TAG, "蓝牙MAC地址: " ESP_BD_ADDR_STR, ESP_BD_ADDR_HEX(esp_bt_dev_get_address()));
return true;
}
// ============================================================
// Deinitialize
// ============================================================
bool BluetoothProvisioning::Deinitialize() {
if (!initialized_) {
ESP_LOGW(TAG, "蓝牙配网未初始化");
return true;
}
ESP_LOGI(TAG, "开始反初始化蓝牙配网...");
if (state_ != BluetoothProvisioningState::IDLE &&
state_ != BluetoothProvisioningState::STOPPED) {
StopProvisioning();
}
esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &WiFiEventHandler);
esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &IPEventHandler);
if (s_wifi_event_group != nullptr) {
vEventGroupDelete(s_wifi_event_group);
s_wifi_event_group = nullptr;
}
esp_bluedroid_disable();
esp_bluedroid_deinit();
esp_bt_controller_disable();
esp_bt_controller_deinit();
initialized_ = false;
SetState(BluetoothProvisioningState::STOPPED);
ESP_LOGI(TAG, "蓝牙配网反初始化完成");
return true;
}
// ============================================================
// StartProvisioning — 构建原始广播数据并启动广播
// ============================================================
bool BluetoothProvisioning::StartProvisioning() {
ESP_LOGI(TAG, "🔵 开始启动蓝牙配网服务 (GATT Server)...");
ESP_LOGI(TAG, "🔍 检查初始化状态: initialized_ = %s", initialized_ ? "true" : "false");
if (!initialized_) {
ESP_LOGE(TAG, "❌ 蓝牙配网未初始化,无法启动");
return false;
}
if (state_ == BluetoothProvisioningState::ADVERTISING ||
state_ == BluetoothProvisioningState::CONNECTED ||
state_ == BluetoothProvisioningState::PROVISIONING) {
ESP_LOGW(TAG, "⚠️ 蓝牙配网已在运行中");
return true;
}
// 重置状态
client_connected_ = false;
s_retry_num = 0;
ResetMacSendingState();
ESP_LOGI(TAG, "🔄 MAC地址发送状态已重置");
// 清空之前的WiFi凭据
ESP_LOGI(TAG, "🧹 清除之前的WiFi凭据...");
if (!wifi_credentials_.ssid.empty()) {
ESP_LOGI(TAG, "🗑️ 删除已保存的SSID: %s", wifi_credentials_.ssid.c_str());
}
if (!wifi_credentials_.password.empty()) {
ESP_LOGI(TAG, "🗑️ 删除已保存的WiFi密码 (长度: %d)", (int)wifi_credentials_.password.length());
}
wifi_credentials_.ssid.clear();
wifi_credentials_.password.clear();
wifi_credentials_.bssid_set = false;
ESP_LOGI(TAG, "✅ WiFi凭据清除完成准备接收新的配网信息");
// 构建设备名称: "Airhub_" + BLE MAC 明文
char ble_device_name[32];
const uint8_t* ble_addr = esp_bt_dev_get_address();
if (ble_addr) {
snprintf(ble_device_name, sizeof(ble_device_name),
"Airhub_%02x:%02x:%02x:%02x:%02x:%02x",
ble_addr[0], ble_addr[1], ble_addr[2],
ble_addr[3], ble_addr[4], ble_addr[5]);
} else {
strcpy(ble_device_name, "Airhub_Ble");
ESP_LOGW(TAG, "获取BLE MAC失败使用默认名称: %s", ble_device_name);
}
// 设置设备名称
esp_err_t ret = esp_ble_gap_set_device_name(ble_device_name);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "❌ 设置蓝牙设备名称失败: %s", esp_err_to_name(ret));
return false;
}
ESP_LOGI(TAG, "📡 蓝牙设备名称: %s", ble_device_name);
// 构建广播数据 (Flags + 设备名)
uint8_t name_len = strlen(ble_device_name);
int offset = 0;
// Flags: LE General Discoverable + BR/EDR Not Supported
prov_adv_raw_data[offset++] = 0x02;
prov_adv_raw_data[offset++] = ESP_BLE_AD_TYPE_FLAG;
prov_adv_raw_data[offset++] = 0x06;
// Complete Local Name
prov_adv_raw_data[offset++] = name_len + 1;
prov_adv_raw_data[offset++] = ESP_BLE_AD_TYPE_NAME_CMPL;
memcpy(&prov_adv_raw_data[offset], ble_device_name, name_len);
offset += name_len;
prov_adv_raw_len = offset;
ESP_LOGI(TAG, "📡 广播数据构建完成,长度: %d 字节", prov_adv_raw_len);
// 构建扫描响应数据 (TX Power + Service UUID)
int rsp_offset = 0;
// TX Power Level
prov_scan_rsp_data[rsp_offset++] = 0x02;
prov_scan_rsp_data[rsp_offset++] = ESP_BLE_AD_TYPE_TX_PWR;
prov_scan_rsp_data[rsp_offset++] = 0x09;
// 16-bit Service UUID Complete
prov_scan_rsp_data[rsp_offset++] = 0x03;
prov_scan_rsp_data[rsp_offset++] = ESP_BLE_AD_TYPE_16SRV_CMPL;
prov_scan_rsp_data[rsp_offset++] = PROV_SERVICE_UUID & 0xFF;
prov_scan_rsp_data[rsp_offset++] = (PROV_SERVICE_UUID >> 8) & 0xFF;
prov_scan_rsp_len = rsp_offset;
ESP_LOGI(TAG, "📡 扫描响应数据构建完成,长度: %d 字节", prov_scan_rsp_len);
// 配置广播数据 (GAP回调中会依次设置扫描响应数据并启动广播)
ret = esp_ble_gap_config_adv_data_raw(prov_adv_raw_data, prov_adv_raw_len);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "❌ 配置广播数据失败: %s", esp_err_to_name(ret));
return false;
}
SetState(BluetoothProvisioningState::ADVERTISING);
ESP_LOGI(TAG, "蓝牙配网广播已启动,等待客户端连接...");
return true;
}
// ============================================================
// StopProvisioning
// ============================================================
bool BluetoothProvisioning::StopProvisioning() {
if (state_ == BluetoothProvisioningState::IDLE ||
state_ == BluetoothProvisioningState::STOPPED) {
ESP_LOGW(TAG, "蓝牙配网未在运行");
return true;
}
ESP_LOGI(TAG, "停止蓝牙配网...");
esp_ble_gap_stop_advertising();
if (client_connected_ && gatts_if_ != ESP_GATT_IF_NONE) {
esp_ble_gatts_close(gatts_if_, conn_id_);
}
client_connected_ = false;
notify_enabled_ = false;
SetState(BluetoothProvisioningState::IDLE);
ESP_LOGI(TAG, "蓝牙配网已停止");
return true;
}
// ============================================================
// StartAdvertising — 启动 BLE 广播
// ============================================================
void BluetoothProvisioning::StartAdvertising() {
// 重新配置原始广播数据GAP回调中启动广播
esp_ble_gap_config_adv_data_raw(prov_adv_raw_data, prov_adv_raw_len);
}
// ============================================================
// GAP 事件回调 (static)
// ============================================================
void BluetoothProvisioning::GapEventHandler(esp_gap_ble_cb_event_t event,
esp_ble_gap_cb_param_t* param) {
switch (event) {
case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
// 广播数据设置完成,接着配置扫描响应数据
ESP_LOGI(TAG, "📡 广播数据设置完成,配置扫描响应数据");
if (prov_scan_rsp_len > 0) {
esp_ble_gap_config_scan_rsp_data_raw(prov_scan_rsp_data, prov_scan_rsp_len);
} else {
esp_ble_gap_start_advertising(&prov_adv_params);
}
break;
case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT:
// 扫描响应数据设置完成,启动广播
ESP_LOGI(TAG, "📡 扫描响应数据设置完成,启动广播");
esp_ble_gap_start_advertising(&prov_adv_params);
break;
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
ESP_LOGE(TAG, "❌ 广播启动失败: %d", param->adv_start_cmpl.status);
} else {
ESP_LOGI(TAG, "✅ 广播启动成功");
}
break;
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
ESP_LOGI(TAG, "广播已停止");
break;
case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
ESP_LOGI(TAG, "连接参数更新: status=%d, conn_int=%d, latency=%d, timeout=%d",
param->update_conn_params.status,
param->update_conn_params.conn_int,
param->update_conn_params.latency,
param->update_conn_params.timeout);
break;
default:
break;
}
}
// ============================================================
// GATTS 事件回调 (static 分发)
// ============================================================
void BluetoothProvisioning::GattsEventHandler(esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param) {
if (event == ESP_GATTS_REG_EVT) {
if (param->reg.app_id != PROV_APP_ID) {
return;
}
} else {
if (!instance_ || gatts_if != instance_->gatts_if_) {
return;
}
}
if (instance_) {
instance_->HandleGattsEvent(event, gatts_if, param);
}
}
// ============================================================
// HandleGattsEvent — 实例内处理
// ============================================================
void BluetoothProvisioning::HandleGattsEvent(esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param) {
switch (event) {
// ---- App 注册完成,保存 gatts_if创建 Service ----
case ESP_GATTS_REG_EVT:
if (param->reg.status == ESP_GATT_OK) {
gatts_if_ = gatts_if;
ESP_LOGI(TAG, "✅ GATTS App 注册成功, gatts_if=%d", gatts_if);
CreateService(gatts_if);
} else {
ESP_LOGE(TAG, "❌ GATTS App 注册失败, status=%d", param->reg.status);
}
break;
// ---- Service 创建完成,添加 WRITE Characteristic ----
case ESP_GATTS_CREATE_EVT:
if (param->create.status == ESP_GATT_OK) {
service_handle_ = param->create.service_handle;
ESP_LOGI(TAG, "Service 创建成功, handle=%d", service_handle_);
// 添加 WRITE Characteristic (手机 → 设备)
esp_gatt_char_prop_t write_prop = ESP_GATT_CHAR_PROP_BIT_WRITE;
esp_ble_gatts_add_char(service_handle_, &prov_write_char_uuid,
ESP_GATT_PERM_WRITE,
write_prop, &prov_write_attr, nullptr);
} else {
ESP_LOGE(TAG, "Service 创建失败: %d", param->create.status);
}
break;
// ---- Characteristic 添加完成 ----
case ESP_GATTS_ADD_CHAR_EVT:
if (param->add_char.status != ESP_GATT_OK) {
ESP_LOGE(TAG, "添加特征失败: uuid=0x%04x status=%d",
param->add_char.char_uuid.uuid.uuid16, param->add_char.status);
break;
}
if (param->add_char.char_uuid.uuid.uuid16 == PROV_CHAR_WRITE_UUID) {
write_char_handle_ = param->add_char.attr_handle;
ESP_LOGI(TAG, "WRITE 特征添加成功, handle=%d", write_char_handle_);
// 添加 NOTIFY Characteristic (设备 → 手机)
esp_gatt_char_prop_t notify_prop = ESP_GATT_CHAR_PROP_BIT_NOTIFY | ESP_GATT_CHAR_PROP_BIT_READ;
esp_ble_gatts_add_char(service_handle_, &prov_notify_char_uuid,
ESP_GATT_PERM_READ,
notify_prop, &prov_notify_attr, nullptr);
} else if (param->add_char.char_uuid.uuid.uuid16 == PROV_CHAR_NOTIFY_UUID) {
notify_char_handle_ = param->add_char.attr_handle;
ESP_LOGI(TAG, "NOTIFY 特征添加成功, handle=%d", notify_char_handle_);
// 为 NOTIFY 特征添加 CCCD 描述符
esp_ble_gatts_add_char_descr(service_handle_, &cccd_uuid,
ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
&cccd_attr, nullptr);
}
break;
// ---- CCCD 描述符添加完成,启动服务 ----
case ESP_GATTS_ADD_CHAR_DESCR_EVT:
if (param->add_char_descr.status == ESP_GATT_OK) {
notify_cccd_handle_ = param->add_char_descr.attr_handle;
ESP_LOGI(TAG, "CCCD 添加成功, handle=%d", notify_cccd_handle_);
// 所有特征添加完毕,启动服务
esp_ble_gatts_start_service(service_handle_);
} else {
ESP_LOGE(TAG, "CCCD 添加失败: %d", param->add_char_descr.status);
}
break;
// ---- Service 启动完成 ----
case ESP_GATTS_START_EVT:
if (param->start.status == ESP_GATT_OK) {
ESP_LOGI(TAG, "✅ GATT Service 启动成功");
}
break;
// ---- 客户端连接 ----
case ESP_GATTS_CONNECT_EVT: {
conn_id_ = param->connect.conn_id;
client_connected_ = true;
notify_enabled_ = false;
mtu_ = 23;
ESP_LOGI(TAG, "📱 客户端已连接, conn_id=%d, addr=%02x:%02x:%02x:%02x:%02x:%02x",
conn_id_,
param->connect.remote_bda[0], param->connect.remote_bda[1],
param->connect.remote_bda[2], param->connect.remote_bda[3],
param->connect.remote_bda[4], param->connect.remote_bda[5]);
ESP_LOGI(TAG, "🔍 [DEBUG] 设置client_connected_为true");
// 重置MAC地址发送状态
ResetMacSendingState();
ESP_LOGI(TAG, "🔄 MAC地址发送状态已重置");
// 更新连接参数
esp_ble_conn_update_params_t conn_params = {};
memcpy(conn_params.bda, param->connect.remote_bda, sizeof(esp_bd_addr_t));
conn_params.latency = 0;
conn_params.max_int = 0x20; // 40ms
conn_params.min_int = 0x10; // 20ms
conn_params.timeout = 400; // 4s
esp_ble_gap_update_conn_params(&conn_params);
// 停止广播
esp_ble_gap_stop_advertising();
SetState(BluetoothProvisioningState::CONNECTED);
TriggerCallback(BluetoothProvisioningEvent::CLIENT_CONNECTED, nullptr);
ESP_LOGI(TAG, "🔍 [DEBUG] BLE连接处理完成client_connected_=%s",
client_connected_ ? "true" : "false");
break;
}
// ---- 客户端断开 ----
case ESP_GATTS_DISCONNECT_EVT: {
ESP_LOGI(TAG, "📱 客户端已断开连接, reason=0x%x, 当前状态: %s",
param->disconnect.reason, GetStateString().c_str());
ESP_LOGI(TAG, "🔍 [DEBUG] 设置client_connected_为false");
client_connected_ = false;
notify_enabled_ = false;
mtu_ = 23;
// 如果正在配网过程中,延迟处理断开事件
if (state_ == BluetoothProvisioningState::PROVISIONING) {
ESP_LOGW(TAG, "⚠️ 配网过程中BLE断开延迟5秒后处理以等待WiFi连接完成");
delayed_disconnect_ = true;
xTaskCreate([](void* param) {
vTaskDelay(pdMS_TO_TICKS(2000));
BluetoothProvisioning* self = static_cast<BluetoothProvisioning*>(param);
if (self->delayed_disconnect_) {
if (self->state_ == BluetoothProvisioningState::PROVISIONING && self->wifi_connecting_) {
ESP_LOGW(TAG, "⏰ BLE延迟断开但WiFi仍在连接中继续等待");
} else if (self->state_ == BluetoothProvisioningState::PROVISIONING) {
ESP_LOGW(TAG, "⏰ 延迟处理BLE断开WiFi连接可能已超时");
self->SetState(BluetoothProvisioningState::ADVERTISING);
self->TriggerCallback(BluetoothProvisioningEvent::CLIENT_DISCONNECTED, nullptr);
self->StartAdvertising();
}
self->delayed_disconnect_ = false;
}
vTaskDelete(nullptr);
}, "delayed_disconnect", 2048, instance_, 1, nullptr);
} else {
SetState(BluetoothProvisioningState::ADVERTISING);
TriggerCallback(BluetoothProvisioningEvent::CLIENT_DISCONNECTED, nullptr);
// 重新启动广播
StartAdvertising();
}
break;
}
// ---- MTU 协商完成 ----
case ESP_GATTS_MTU_EVT:
mtu_ = param->mtu.mtu;
ESP_LOGI(TAG, "MTU 更新: %d", mtu_);
break;
// ---- WRITE 事件 ----
case ESP_GATTS_WRITE_EVT:
if (param->write.handle == write_char_handle_) {
// 配网协议数据
ProcessWriteData(param->write.value, param->write.len);
} else if (param->write.handle == notify_cccd_handle_ && param->write.len == 2) {
// CCCD 写入: 开启/关闭 NOTIFY
uint16_t cccd_value = param->write.value[0] | (param->write.value[1] << 8);
notify_enabled_ = (cccd_value == 0x0001);
ESP_LOGI(TAG, "NOTIFY %s", notify_enabled_ ? "已启用" : "已禁用");
}
if (param->write.need_rsp) {
esp_ble_gatts_send_response(gatts_if_, param->write.conn_id,
param->write.trans_id, ESP_GATT_OK, nullptr);
}
break;
// ---- READ 事件 ----
case ESP_GATTS_READ_EVT:
ESP_LOGD(TAG, "Read event, handle=%d", param->read.handle);
break;
default:
break;
}
}
// ============================================================
// CreateService
// ============================================================
void BluetoothProvisioning::CreateService(esp_gatt_if_t gatts_if) {
esp_gatt_srvc_id_t service_id = {};
service_id.is_primary = true;
service_id.id.inst_id = 0;
service_id.id.uuid.len = ESP_UUID_LEN_16;
service_id.id.uuid.uuid.uuid16 = PROV_SERVICE_UUID;
esp_ble_gatts_create_service(gatts_if, &service_id, PROV_HANDLE_NUM);
}
// ============================================================
// ProcessWriteData — 处理手机发送的配网协议数据
// ============================================================
void BluetoothProvisioning::ProcessWriteData(const uint8_t* data, uint16_t len) {
if (!data || len < 1) return;
uint8_t cmd = data[0];
const uint8_t* payload = data + 1;
uint16_t payload_len = len - 1;
switch (cmd) {
// 接收到WiFi SSID
case PROV_CMD_SET_SSID:
ESP_LOGI(TAG, "📶 收到WiFi SSID: %.*s", payload_len, payload);
wifi_credentials_.ssid.assign(reinterpret_cast<const char*>(payload), payload_len);
{
wifi_config_t wifi_config = {};
strncpy(reinterpret_cast<char*>(wifi_config.sta.ssid),
wifi_credentials_.ssid.c_str(),
sizeof(wifi_config.sta.ssid) - 1);
esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
}
break;
// 接收到WiFi密码
case PROV_CMD_SET_PASSWORD:
ESP_LOGI(TAG, "🔐 收到WiFi密码 (长度: %d)", payload_len);
wifi_credentials_.password.assign(reinterpret_cast<const char*>(payload), payload_len);
{
wifi_config_t wifi_config = {};
strncpy(reinterpret_cast<char*>(wifi_config.sta.ssid),
wifi_credentials_.ssid.c_str(),
sizeof(wifi_config.sta.ssid) - 1);
strncpy(reinterpret_cast<char*>(wifi_config.sta.password),
wifi_credentials_.password.c_str(),
sizeof(wifi_config.sta.password) - 1);
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
}
// 重置重试计数器
s_retry_num = 0;
wifi_connecting_ = true;
// 启动WiFi连接超时定时器
if (wifi_connect_timer) {
xTimerStop(wifi_connect_timer, 0);
xTimerDelete(wifi_connect_timer, 0);
}
wifi_connect_timer = xTimerCreate("wifi_timeout",
pdMS_TO_TICKS(WIFI_CONNECT_TIMEOUT_MS),
pdFALSE, nullptr,
[](TimerHandle_t timer) {
ESP_LOGW(TAG, "⏰ WiFi连接超时强制失败处理");
if (instance_ && instance_->wifi_connecting_) {
instance_->wifi_connecting_ = false;
instance_->delayed_disconnect_ = false;
instance_->SetState(BluetoothProvisioningState::FAILED);
instance_->TriggerCallback(BluetoothProvisioningEvent::WIFI_FAILED, nullptr);
instance_->ReportWiFiStatus(false, WIFI_REASON_UNSPECIFIED);
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
});
xTimerStart(wifi_connect_timer, 0);
esp_wifi_connect();
ESP_LOGI(TAG, "📡 已发起WiFi连接请求启动超时监控");
TriggerCallback(BluetoothProvisioningEvent::WIFI_CREDENTIALS, &wifi_credentials_);
break;
// 接收到BSSID
case PROV_CMD_SET_BSSID:
if (payload_len >= 6) {
ESP_LOGI(TAG, "收到BSSID");
memcpy(wifi_credentials_.bssid, payload, 6);
wifi_credentials_.bssid_set = true;
wifi_config_t wifi_config = {};
strncpy(reinterpret_cast<char*>(wifi_config.sta.ssid),
wifi_credentials_.ssid.c_str(),
sizeof(wifi_config.sta.ssid) - 1);
strncpy(reinterpret_cast<char*>(wifi_config.sta.password),
wifi_credentials_.password.c_str(),
sizeof(wifi_config.sta.password) - 1);
memcpy(wifi_config.sta.bssid, wifi_credentials_.bssid, 6);
wifi_config.sta.bssid_set = true;
esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
}
break;
// 请求连接到AP
case PROV_CMD_CONNECT_AP:
ESP_LOGI(TAG, "📡 请求连接到APSSID: %s", wifi_credentials_.ssid.c_str());
ESP_LOGI(TAG, "🔍 [DEBUG] 当前状态: %s, client_connected_: %s",
GetStateString().c_str(), client_connected_ ? "true" : "false");
SetState(BluetoothProvisioningState::PROVISIONING);
delayed_disconnect_ = false;
s_retry_num = 0;
ESP_LOGI(TAG, "🔄 重置WiFi重试计数开始连接流程");
esp_wifi_disconnect();
vTaskDelay(pdMS_TO_TICKS(100));
esp_wifi_connect();
ESP_LOGI(TAG, "🚀 已发起WiFi连接请求");
break;
// 请求断开AP
case PROV_CMD_DISCONNECT_AP:
ESP_LOGI(TAG, "请求断开AP连接");
esp_wifi_disconnect();
break;
// 请求WiFi列表
case PROV_CMD_GET_WIFI_LIST:
ESP_LOGI(TAG, "📱 手机请求获取WiFi列表开始扫描");
{
wifi_scan_config_t scan_config = {};
scan_config.ssid = nullptr;
scan_config.bssid = nullptr;
scan_config.channel = 0;
scan_config.show_hidden = true;
scan_config.scan_type = WIFI_SCAN_TYPE_ACTIVE;
scan_config.scan_time.active.min = 100;
scan_config.scan_time.active.max = 300;
esp_err_t ret = esp_wifi_scan_start(&scan_config, false);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "🔍 WiFi扫描已启动");
} else {
ESP_LOGE(TAG, "❌ WiFi扫描启动失败: %s", esp_err_to_name(ret));
}
}
break;
// 请求断开BLE
case PROV_CMD_DISCONNECT_BLE:
ESP_LOGI(TAG, "收到断开BLE连接请求");
if (gatts_if_ != ESP_GATT_IF_NONE) {
esp_ble_gatts_close(gatts_if_, conn_id_);
}
break;
// 设置WiFi模式
case PROV_CMD_SET_WIFI_MODE:
if (payload_len >= 1) {
ESP_LOGI(TAG, "设置WiFi模式: %d", payload[0]);
esp_wifi_set_mode((wifi_mode_t)payload[0]);
}
break;
// 获取WiFi状态
case PROV_CMD_GET_WIFI_STATUS:
ESP_LOGI(TAG, "客户端请求WiFi状态");
break;
// 自定义数据
case PROV_CMD_CUSTOM_DATA:
ESP_LOGI(TAG, "收到自定义数据, 长度: %d", payload_len);
break;
default:
ESP_LOGW(TAG, "未知命令: 0x%02x", cmd);
break;
}
}
// ============================================================
// SendNotify — 通过 GATT NOTIFY 发送数据
// ============================================================
bool BluetoothProvisioning::SendNotify(const uint8_t* data, uint16_t len) {
if (gatts_if_ == ESP_GATT_IF_NONE || !client_connected_) {
return false;
}
uint16_t max_payload = mtu_ - 3;
if (len > max_payload) {
ESP_LOGW(TAG, "数据长度 %d 超过 MTU payload %d截断", len, max_payload);
len = max_payload;
}
esp_err_t ret = esp_ble_gatts_send_indicate(
gatts_if_, conn_id_, notify_char_handle_,
len, (uint8_t*)data, false);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "发送 NOTIFY 失败: %s", esp_err_to_name(ret));
return false;
}
return true;
}
// ============================================================
// ReportWiFiStatus — 通过 GATT NOTIFY 报告WiFi连接状态
// ============================================================
void BluetoothProvisioning::ReportWiFiStatus(bool success, uint8_t reason) {
ESP_LOGI(TAG, "🔍 [DEBUG] ReportWiFiStatus调用: success=%s, client_connected_=%s",
success ? "true" : "false", client_connected_ ? "true" : "false");
if (!client_connected_) {
ESP_LOGW(TAG, "客户端未连接无法发送WiFi状态");
return;
}
uint8_t buf[3];
buf[0] = PROV_RESP_WIFI_STATUS;
buf[1] = success ? 1 : 0;
buf[2] = reason;
if (notify_enabled_) {
SendNotify(buf, 3);
}
if (success) {
ESP_LOGI(TAG, "向客户端报告设备连接WiFi成功!");
} else {
ESP_LOGI(TAG, "向客户端报告连接WiFi失败原因: %d", reason);
}
}
// ============================================================
// SendWiFiList — 通过 GATT NOTIFY 发送WiFi扫描列表
// ============================================================
void BluetoothProvisioning::SendWiFiList(const wifi_ap_record_t* ap_list, uint16_t ap_count) {
if (!client_connected_) {
ESP_LOGW(TAG, "客户端未连接无法发送WiFi列表");
return;
}
if (ap_list == nullptr || ap_count == 0) {
ESP_LOGW(TAG, "WiFi列表为空");
// 发送结束标记
if (notify_enabled_) {
uint8_t end = PROV_RESP_WIFI_LIST_END;
SendNotify(&end, 1);
}
return;
}
ESP_LOGI(TAG, "向客户端发送WiFi列表共%d个AP", ap_count);
if (notify_enabled_) {
for (uint16_t i = 0; i < ap_count; i++) {
uint8_t ssid_len = strlen(reinterpret_cast<const char*>(ap_list[i].ssid));
uint8_t buf[3 + 32]; // cmd + rssi + ssid_len + ssid
buf[0] = PROV_RESP_WIFI_LIST;
buf[1] = (uint8_t)(ap_list[i].rssi & 0xFF);
buf[2] = ssid_len;
memcpy(&buf[3], ap_list[i].ssid, ssid_len);
SendNotify(buf, 3 + ssid_len);
vTaskDelay(pdMS_TO_TICKS(20)); // 每条之间稍作延迟
}
// 发送结束标记
uint8_t end = PROV_RESP_WIFI_LIST_END;
SendNotify(&end, 1);
}
ESP_LOGI(TAG, "📤 WiFi列表已发送给客户端包含 %d 个热点", ap_count);
}
// ============================================================
// SendMacAddressReliably — 通过 GATT NOTIFY 可靠发送MAC地址
// ============================================================
bool BluetoothProvisioning::SendMacAddressReliably() {
if (!client_connected_) {
ESP_LOGW(TAG, "客户端未连接无法发送MAC地址");
return false;
}
uint8_t mac[6];
esp_err_t ret = esp_wifi_get_mac(WIFI_IF_STA, mac);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "获取MAC地址失败: %s", esp_err_to_name(ret));
return false;
}
char mac_str[18];
snprintf(mac_str, sizeof(mac_str), "%02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
if (mac_address_sent_) {
ESP_LOGI(TAG, "MAC地址已发送过跳过重复发送: %s", mac_str);
return true;
}
ESP_LOGI(TAG, "开始可靠发送MAC地址: %s", mac_str);
const int MAX_SEND_ATTEMPTS = 3;
const int RETRY_DELAY_MS = 50;
for (int attempt = 1; attempt <= MAX_SEND_ATTEMPTS; attempt++) {
if (!client_connected_) {
ESP_LOGW(TAG, "发送前检查发现客户端已断开连接 (尝试 %d/%d)", attempt, MAX_SEND_ATTEMPTS);
return false;
}
ESP_LOGI(TAG, "发送MAC地址尝试 %d/%d: %s", attempt, MAX_SEND_ATTEMPTS, mac_str);
// 构建自定义数据包 (保留原有格式)
char mac_data[32];
snprintf(mac_data, sizeof(mac_data), "STA_MAC:%02x:%02x:%02x:%02x:%02x:%02x",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
uint8_t buf[1 + 32];
buf[0] = PROV_RESP_CUSTOM_DATA;
uint8_t data_len = strlen(mac_data);
memcpy(&buf[1], mac_data, data_len);
bool ok = false;
if (notify_enabled_) {
ok = SendNotify(buf, 1 + data_len);
} else {
ESP_LOGW(TAG, "NOTIFY未启用尝试直接发送");
ok = SendNotify(buf, 1 + data_len);
}
if (ok) {
ESP_LOGI(TAG, "✅ MAC地址发送成功 (尝试 %d/%d): %s", attempt, MAX_SEND_ATTEMPTS, mac_str);
mac_address_sent_ = true;
vTaskDelay(pdMS_TO_TICKS(100));
if (client_connected_) {
ESP_LOGI(TAG, "MAC地址发送完成连接状态正常");
return true;
} else {
ESP_LOGW(TAG, "MAC地址发送后检测到连接断开");
return false;
}
} else {
ESP_LOGW(TAG, "❌ MAC地址发送失败 (尝试 %d/%d): %s",
attempt, MAX_SEND_ATTEMPTS, mac_str);
if (attempt < MAX_SEND_ATTEMPTS) {
vTaskDelay(pdMS_TO_TICKS(RETRY_DELAY_MS));
}
}
}
ESP_LOGE(TAG, "MAC地址发送失败已达到最大重试次数: %s", mac_str);
return false;
}
void BluetoothProvisioning::ResetMacSendingState() {
mac_address_sent_ = false;
ESP_LOGI(TAG, "MAC地址发送状态已重置");
}
// ============================================================
// 状态管理
// ============================================================
void BluetoothProvisioning::SetState(BluetoothProvisioningState new_state) {
if (state_ != new_state) {
BluetoothProvisioningState old_state = state_;
state_ = new_state;
const char* state_names[] = {
"IDLE", "INITIALIZING", "ADVERTISING", "CONNECTED",
"PROVISIONING", "SUCCESS", "FAILED", "STOPPED"
};
ESP_LOGI(TAG, "🔄 配网状态变化: %s -> %s",
state_names[static_cast<int>(old_state)],
state_names[static_cast<int>(new_state)]);
TriggerCallback(BluetoothProvisioningEvent::STATE_CHANGED, nullptr);
}
}
void BluetoothProvisioning::TriggerCallback(BluetoothProvisioningEvent event, void* data) {
if (callback_) {
callback_(event, data);
}
}
std::string BluetoothProvisioning::GetStateString() const {
const char* state_names[] = {
"IDLE", "INITIALIZING", "ADVERTISING", "CONNECTED",
"PROVISIONING", "SUCCESS", "FAILED", "STOPPED"
};
return std::string(state_names[static_cast<int>(state_)]);
}
// ============================================================
// WiFi 事件处理 (保留原有业务逻辑)
// ============================================================
void BluetoothProvisioning::WiFiEventHandler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data) {
BluetoothProvisioning* self = static_cast<BluetoothProvisioning*>(arg);
if (event_base == WIFI_EVENT) {
switch (event_id) {
case WIFI_EVENT_STA_START:
ESP_LOGI(TAG, "WiFi STA启动");
break;
case WIFI_EVENT_STA_CONNECTED: {
wifi_event_sta_connected_t* event = static_cast<wifi_event_sta_connected_t*>(event_data);
ESP_LOGI(TAG, "✅ WiFi连接成功SSID: %.*s等待获取IP地址", event->ssid_len, event->ssid);
if (wifi_connect_timer) {
xTimerStop(wifi_connect_timer, 0);
}
self->wifi_connecting_ = false;
self->delayed_disconnect_ = false;
break;
}
case WIFI_EVENT_STA_DISCONNECTED: {
wifi_event_sta_disconnected_t* event = static_cast<wifi_event_sta_disconnected_t*>(event_data);
ESP_LOGI(TAG, "WiFi断开连接原因: %d", event->reason);
if (self->state_ != BluetoothProvisioningState::PROVISIONING) {
ESP_LOGD(TAG, "非配网状态下的WiFi断开忽略处理");
break;
}
if (s_retry_num < MAX_RETRY) {
esp_err_t ret = esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "🔄 立即重试连接WiFi (%d/%d),断开原因: %d重试结果: %s",
s_retry_num, MAX_RETRY, event->reason,
ret == ESP_OK ? "成功" : "失败");
if (wifi_connect_timer && ret == ESP_OK) {
xTimerReset(wifi_connect_timer, 0);
}
} else {
ESP_LOGE(TAG, "❌ WiFi连接失败已达到最大重试次数断开原因: %d", event->reason);
if (wifi_connect_timer) {
xTimerStop(wifi_connect_timer, 0);
}
self->wifi_connecting_ = false;
self->delayed_disconnect_ = false;
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
self->SetState(BluetoothProvisioningState::FAILED);
self->TriggerCallback(BluetoothProvisioningEvent::WIFI_FAILED, &event->reason);
self->ReportWiFiStatus(false, event->reason);
}
break;
}
case WIFI_EVENT_SCAN_DONE: {
ESP_LOGI(TAG, "📡 WiFi扫描完成准备发送WiFi列表");
uint16_t ap_count = 0;
esp_err_t ret = esp_wifi_scan_get_ap_num(&ap_count);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "❌ 获取WiFi扫描结果数量失败: %s", esp_err_to_name(ret));
break;
}
ESP_LOGI(TAG, "📊 扫描到 %d 个WiFi热点", ap_count);
if (ap_count > 0) {
wifi_ap_record_t* ap_list = (wifi_ap_record_t*)malloc(sizeof(wifi_ap_record_t) * ap_count);
if (ap_list == nullptr) {
ESP_LOGE(TAG, "❌ 分配WiFi扫描结果内存失败");
break;
}
ret = esp_wifi_scan_get_ap_records(&ap_count, ap_list);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "✅ 成功获取WiFi扫描结果");
// 过滤: 只保留2.4GHz频段(信道1-14)去重相同SSID(保留信号最强的)
// ESP-IDF返回结果已按RSSI降序排列第一个出现的同名SSID即为信号最强
uint16_t filtered_count = 0;
for (uint16_t i = 0; i < ap_count; i++) {
// 过滤5GHz频段 (信道 > 14)
if (ap_list[i].primary > 14) {
ESP_LOGD(TAG, "过滤5GHz: SSID=%s, 信道=%d, RSSI=%d",
ap_list[i].ssid, ap_list[i].primary, ap_list[i].rssi);
continue;
}
// 过滤空SSID(隐藏网络)
uint8_t ssid_len = strlen(reinterpret_cast<const char*>(ap_list[i].ssid));
if (ssid_len == 0) {
continue;
}
// SSID去重: 检查是否已存在相同SSID
bool duplicate = false;
for (uint16_t j = 0; j < filtered_count; j++) {
if (strcmp(reinterpret_cast<const char*>(ap_list[j].ssid),
reinterpret_cast<const char*>(ap_list[i].ssid)) == 0) {
duplicate = true;
break;
}
}
if (duplicate) {
continue;
}
// 将符合条件的AP移到前面
if (filtered_count != i) {
ap_list[filtered_count] = ap_list[i];
}
filtered_count++;
}
ESP_LOGI(TAG, "📊 过滤后剩余 %d 个2.4GHz热点 (原始: %d)", filtered_count, ap_count);
self->SendWiFiList(ap_list, filtered_count);
ESP_LOGI(TAG, "📤 WiFi列表已发送包含 %d 个热点", filtered_count);
} else {
ESP_LOGE(TAG, "❌ 获取WiFi扫描结果详细信息失败: %s", esp_err_to_name(ret));
}
free(ap_list);
} else {
ESP_LOGW(TAG, "⚠️ 未扫描到任何WiFi热点");
self->SendWiFiList(nullptr, 0);
}
break;
}
default:
break;
}
}
}
// ============================================================
// IP 事件处理 (保留原有业务逻辑: 保存WiFi、发送MAC、重启)
// ============================================================
void BluetoothProvisioning::IPEventHandler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data) {
BluetoothProvisioning* self = static_cast<BluetoothProvisioning*>(arg);
switch (event_id) {
case IP_EVENT_STA_GOT_IP: {
ip_event_got_ip_t* event = static_cast<ip_event_got_ip_t*>(event_data);
ESP_LOGI(TAG, "✅ WiFi获取IP地址成功: " IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
// 停止WiFi连接超时定时器
if (wifi_connect_timer) {
xTimerStop(wifi_connect_timer, 0);
xTimerDelete(wifi_connect_timer, 0);
wifi_connect_timer = nullptr;
}
self->wifi_connecting_ = false;
self->delayed_disconnect_ = false;
if (s_wifi_event_group) {
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
// 发送MAC地址
ESP_LOGI(TAG, "🔍 [DEBUG] 检查客户端连接状态: client_connected_=%s",
self->client_connected_ ? "true" : "false");
if (self && self->client_connected_) {
ESP_LOGI(TAG, "🔍 [DEBUG] 使用专用函数发送设备MAC地址...");
bool mac_sent = self->SendMacAddressReliably();
if (mac_sent) {
ESP_LOGI(TAG, "✅ 设备MAC地址发送成功");
} else {
ESP_LOGW(TAG, "⚠️ 设备MAC地址发送失败");
}
ESP_LOGI(TAG, "🔍 [DEBUG] 已跳过WiFi连接报告发送仅发送设备MAC地址");
} else {
ESP_LOGW(TAG, "🔍 [DEBUG] 无法发送: client_connected_=%s",
self->client_connected_ ? "true" : "false");
}
// 启用WiFi配置自动保存到NVS存储
ESP_LOGI(TAG, "💾 启用WiFi配置自动保存到NVS存储...");
esp_err_t storage_ret = esp_wifi_set_storage(WIFI_STORAGE_FLASH);
if (storage_ret == ESP_OK) {
ESP_LOGI(TAG, "✅ WiFi配置将自动保存到NVS存储");
} else {
ESP_LOGW(TAG, "⚠️ 设置WiFi存储模式失败: %s", esp_err_to_name(storage_ret));
}
// 手动获取当前WiFi配置并保存到NVS列表
wifi_config_t wifi_config;
esp_err_t get_config_ret = esp_wifi_get_config(WIFI_IF_STA, &wifi_config);
if (get_config_ret == ESP_OK) {
ESP_LOGI(TAG, "📋 获取当前WiFi配置成功SSID: %s", wifi_config.sta.ssid);
auto& ssid_manager = SsidManager::GetInstance();
ssid_manager.AddSsid((const char*)wifi_config.sta.ssid, (const char*)wifi_config.sta.password);
ESP_LOGI(TAG, "✅ WiFi凭据已保存到NVS列表");
} else {
ESP_LOGW(TAG, "⚠️ 获取当前WiFi配置失败: %s", esp_err_to_name(get_config_ret));
}
auto& application = Application::GetInstance();
bool skip_session = application.ShouldSkipDialogIdleSession();
ESP_LOGI(TAG, "BluetoothProvisioning WIFI_CONNECTED skip_session=%d", (int)skip_session);
if (!skip_session) {
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);
}
} else {
application.ClearDialogIdleSkipSession();
}
// 更新状态和触发回调
ESP_LOGI(TAG, "🔍 准备设置状态为SUCCESS并触发回调");
self->SetState(BluetoothProvisioningState::SUCCESS);
self->TriggerCallback(BluetoothProvisioningEvent::WIFI_CONNECTED, &event->ip_info.ip);
self->ReportWiFiStatus(true, 0);
ESP_LOGI(TAG, "📋 配网流程完成,状态: %s, client_connected_: %s",
self->GetStateString().c_str(),
self->client_connected_ ? "true" : "false");
// 延迟2000ms后强制重启设备
ESP_LOGI(TAG, "⏰ 延迟2000ms后重启设备以确保配置生效...");
vTaskDelay(pdMS_TO_TICKS(2000));
ESP_LOGI(TAG, "🔄 强制重启设备...");
esp_restart();
break;
}
default:
break;
}
}