332 lines
10 KiB
C++
332 lines
10 KiB
C++
/**
|
||
* @file bluetooth_provisioning_example.cc
|
||
* @brief 蓝牙配网使用示例
|
||
*
|
||
* 本文件展示了如何在ESP32项目中集成和使用蓝牙配网功能
|
||
* 包括初始化、启动配网、处理回调事件等完整流程
|
||
*/
|
||
|
||
#include "bluetooth_provisioning.h"
|
||
#include "esp_log.h"
|
||
#include "esp_wifi.h"
|
||
#include "esp_netif.h"
|
||
#include "esp_event.h"
|
||
#include "nvs_flash.h"
|
||
#include "freertos/FreeRTOS.h"
|
||
#include "freertos/task.h"
|
||
|
||
#define TAG "BluetoothProvisioningExample"
|
||
|
||
// 全局蓝牙配网对象
|
||
static BluetoothProvisioning* g_bt_provisioning = nullptr;
|
||
|
||
/**
|
||
* @brief 蓝牙配网事件回调函数
|
||
*
|
||
* 处理蓝牙配网过程中的各种事件,包括状态变化、WiFi连接等
|
||
*
|
||
* @param event 事件类型
|
||
* @param data 事件数据指针
|
||
*/
|
||
void bluetooth_provisioning_callback(BluetoothProvisioningEvent event, void* data) {
|
||
switch (event) {
|
||
case BluetoothProvisioningEvent::STATE_CHANGED: {
|
||
BluetoothProvisioningState state = g_bt_provisioning->GetState();
|
||
const char* state_names[] = {
|
||
"空闲", "初始化中", "广播中", "已连接",
|
||
"配网中", "成功", "失败", "已停止"
|
||
};
|
||
ESP_LOGI(TAG, "配网状态变更: %s", state_names[static_cast<int>(state)]);
|
||
break;
|
||
}
|
||
|
||
case BluetoothProvisioningEvent::CLIENT_CONNECTED:
|
||
ESP_LOGI(TAG, "蓝牙客户端已连接,可以开始配网");
|
||
break;
|
||
|
||
case BluetoothProvisioningEvent::CLIENT_DISCONNECTED:
|
||
ESP_LOGI(TAG, "蓝牙客户端已断开连接");
|
||
break;
|
||
|
||
case BluetoothProvisioningEvent::WIFI_CREDENTIALS: {
|
||
WiFiCredentials* credentials = static_cast<WiFiCredentials*>(data);
|
||
ESP_LOGI(TAG, "收到WiFi凭据:");
|
||
ESP_LOGI(TAG, " SSID: %s", credentials->ssid.c_str());
|
||
ESP_LOGI(TAG, " 密码长度: %d", credentials->password.length());
|
||
if (credentials->bssid_set) {
|
||
ESP_LOGI(TAG, " BSSID: %02x:%02x:%02x:%02x:%02x:%02x",
|
||
credentials->bssid[0], credentials->bssid[1], credentials->bssid[2],
|
||
credentials->bssid[3], credentials->bssid[4], credentials->bssid[5]);
|
||
}
|
||
break;
|
||
}
|
||
|
||
case BluetoothProvisioningEvent::WIFI_CONNECTED: {
|
||
esp_ip4_addr_t* ip = static_cast<esp_ip4_addr_t*>(data);
|
||
ESP_LOGI(TAG, "WiFi连接成功!IP地址: " IPSTR, IP2STR(ip));
|
||
|
||
// WiFi连接成功后,可以选择停止蓝牙配网以节省资源
|
||
// 延迟5秒后停止配网,给客户端足够时间接收状态
|
||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||
if (g_bt_provisioning) {
|
||
g_bt_provisioning->StopProvisioning();
|
||
ESP_LOGI(TAG, "配网成功,已停止蓝牙配网服务");
|
||
}
|
||
break;
|
||
}
|
||
|
||
case BluetoothProvisioningEvent::WIFI_FAILED: {
|
||
uint8_t* reason = static_cast<uint8_t*>(data);
|
||
ESP_LOGE(TAG, "WiFi连接失败,错误代码: %d", *reason);
|
||
|
||
// WiFi连接失败,可以选择重新开始配网或进行其他处理
|
||
ESP_LOGI(TAG, "WiFi连接失败,配网服务继续运行等待重新配置");
|
||
break;
|
||
}
|
||
|
||
default:
|
||
ESP_LOGW(TAG, "未处理的配网事件: %d", static_cast<int>(event));
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 初始化WiFi
|
||
*
|
||
* 配置WiFi为STA模式,为蓝牙配网做准备
|
||
*/
|
||
esp_err_t init_wifi() {
|
||
ESP_LOGI(TAG, "初始化WiFi...");
|
||
|
||
// 创建默认WiFi STA网络接口
|
||
esp_netif_create_default_wifi_sta();
|
||
|
||
// 初始化WiFi配置
|
||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||
esp_err_t ret = esp_wifi_init(&cfg);
|
||
if (ret != ESP_OK) {
|
||
ESP_LOGE(TAG, "WiFi初始化失败: %s", esp_err_to_name(ret));
|
||
return ret;
|
||
}
|
||
|
||
// 设置WiFi模式为STA
|
||
ret = esp_wifi_set_mode(WIFI_MODE_STA);
|
||
if (ret != ESP_OK) {
|
||
ESP_LOGE(TAG, "WiFi模式设置失败: %s", esp_err_to_name(ret));
|
||
return ret;
|
||
}
|
||
|
||
// 启动WiFi
|
||
ret = esp_wifi_start();
|
||
if (ret != ESP_OK) {
|
||
ESP_LOGE(TAG, "WiFi启动失败: %s", esp_err_to_name(ret));
|
||
return ret;
|
||
}
|
||
|
||
ESP_LOGI(TAG, "WiFi初始化完成");
|
||
return ESP_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 蓝牙配网任务
|
||
*
|
||
* 独立任务处理蓝牙配网的整个生命周期
|
||
*
|
||
* @param pvParameters 任务参数(未使用)
|
||
*/
|
||
void bluetooth_provisioning_task(void* pvParameters) {
|
||
ESP_LOGI(TAG, "启动蓝牙配网任务");
|
||
|
||
// 1. 创建蓝牙配网对象
|
||
g_bt_provisioning = new BluetoothProvisioning();
|
||
if (g_bt_provisioning == nullptr) {
|
||
ESP_LOGE(TAG, "蓝牙配网对象创建失败");
|
||
vTaskDelete(nullptr);
|
||
return;
|
||
}
|
||
|
||
// 2. 设置事件回调
|
||
g_bt_provisioning->SetCallback(bluetooth_provisioning_callback);
|
||
|
||
// 3. 初始化蓝牙配网
|
||
if (!g_bt_provisioning->Initialize()) {
|
||
ESP_LOGE(TAG, "蓝牙配网初始化失败");
|
||
delete g_bt_provisioning;
|
||
g_bt_provisioning = nullptr;
|
||
vTaskDelete(nullptr);
|
||
return;
|
||
}
|
||
|
||
// 4. 启动配网服务
|
||
const char* device_name = BLU_NAME;
|
||
if (!g_bt_provisioning->StartProvisioning(device_name)) {
|
||
ESP_LOGE(TAG, "蓝牙配网启动失败");
|
||
g_bt_provisioning->Deinitialize();
|
||
delete g_bt_provisioning;
|
||
g_bt_provisioning = nullptr;
|
||
vTaskDelete(nullptr);
|
||
return;
|
||
}
|
||
|
||
ESP_LOGI(TAG, "蓝牙配网服务已启动,设备名称: %s", device_name);
|
||
ESP_LOGI(TAG, "请使用ESP-IDF官方配网APP或自定义APP进行配网");
|
||
|
||
// 5. 任务主循环 - 监控配网状态
|
||
while (true) {
|
||
BluetoothProvisioningState state = g_bt_provisioning->GetState();
|
||
|
||
// 检查是否需要处理特殊状态
|
||
switch (state) {
|
||
case BluetoothProvisioningState::SUCCESS:
|
||
ESP_LOGI(TAG, "配网成功完成");
|
||
// 可以在这里添加配网成功后的处理逻辑
|
||
break;
|
||
|
||
case BluetoothProvisioningState::FAILED:
|
||
ESP_LOGE(TAG, "配网失败,尝试重新启动");
|
||
// 配网失败,尝试重新启动
|
||
g_bt_provisioning->StopProvisioning();
|
||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||
g_bt_provisioning->StartProvisioning(device_name);
|
||
break;
|
||
|
||
case BluetoothProvisioningState::STOPPED:
|
||
ESP_LOGI(TAG, "配网服务已停止");
|
||
// 配网服务已停止,可以选择退出任务或重新启动
|
||
break;
|
||
|
||
default:
|
||
// 其他状态正常运行
|
||
break;
|
||
}
|
||
|
||
// 每5秒检查一次状态
|
||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||
}
|
||
|
||
// 清理资源(通常不会执行到这里)
|
||
if (g_bt_provisioning) {
|
||
g_bt_provisioning->Deinitialize();
|
||
delete g_bt_provisioning;
|
||
g_bt_provisioning = nullptr;
|
||
}
|
||
|
||
vTaskDelete(nullptr);
|
||
}
|
||
|
||
/**
|
||
* @brief 应用程序主函数
|
||
*
|
||
* 演示如何集成蓝牙配网到现有的ESP32应用程序中
|
||
*/
|
||
extern "C" void app_main() {
|
||
ESP_LOGI(TAG, "=== 蓝牙配网示例程序启动 ===");
|
||
|
||
// 1. 初始化NVS(用于存储WiFi配置)
|
||
esp_err_t ret = nvs_flash_init();
|
||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||
ret = nvs_flash_init();
|
||
}
|
||
ESP_ERROR_CHECK(ret);
|
||
|
||
// 2. 初始化网络接口
|
||
ESP_ERROR_CHECK(esp_netif_init());
|
||
|
||
// 3. 创建默认事件循环
|
||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||
|
||
// 4. 初始化WiFi
|
||
ESP_ERROR_CHECK(init_wifi());
|
||
|
||
// 5. 创建蓝牙配网任务
|
||
BaseType_t task_ret = xTaskCreate(
|
||
bluetooth_provisioning_task, // 任务函数
|
||
"bt_provisioning", // 任务名称
|
||
8192, // 栈大小(字节)
|
||
nullptr, // 任务参数
|
||
5, // 任务优先级
|
||
nullptr // 任务句柄
|
||
);
|
||
|
||
if (task_ret != pdPASS) {
|
||
ESP_LOGE(TAG, "蓝牙配网任务创建失败");
|
||
return;
|
||
}
|
||
|
||
ESP_LOGI(TAG, "应用程序初始化完成");
|
||
|
||
// 6. 主程序循环(可以在这里添加其他应用逻辑)
|
||
while (true) {
|
||
// 这里可以添加应用程序的主要逻辑
|
||
// 例如:传感器读取、数据处理、用户交互等
|
||
|
||
ESP_LOGI(TAG, "主程序运行中...");
|
||
vTaskDelay(pdMS_TO_TICKS(30000)); // 每30秒打印一次状态
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 获取当前配网状态(供其他模块调用)
|
||
*
|
||
* @return BluetoothProvisioningState 当前配网状态
|
||
*/
|
||
BluetoothProvisioningState get_provisioning_state() {
|
||
if (g_bt_provisioning) {
|
||
return g_bt_provisioning->GetState();
|
||
}
|
||
return BluetoothProvisioningState::STOPPED;
|
||
}
|
||
|
||
/**
|
||
* @brief 检查WiFi是否已连接(供其他模块调用)
|
||
*
|
||
* @return true WiFi已连接
|
||
* @return false WiFi未连接
|
||
*/
|
||
bool is_wifi_connected() {
|
||
BluetoothProvisioningState state = get_provisioning_state();
|
||
return (state == BluetoothProvisioningState::SUCCESS);
|
||
}
|
||
|
||
/**
|
||
* @brief 手动启动配网(供其他模块调用)
|
||
*
|
||
* 可以在用户按下配网按钮或其他触发条件时调用
|
||
*
|
||
* @return true 启动成功
|
||
* @return false 启动失败
|
||
*/
|
||
bool start_provisioning_manually() {
|
||
if (g_bt_provisioning == nullptr) {
|
||
ESP_LOGE(TAG, "蓝牙配网对象未初始化");
|
||
return false;
|
||
}
|
||
|
||
BluetoothProvisioningState state = g_bt_provisioning->GetState();
|
||
if (state == BluetoothProvisioningState::ADVERTISING ||
|
||
state == BluetoothProvisioningState::CONNECTED ||
|
||
state == BluetoothProvisioningState::PROVISIONING) {
|
||
ESP_LOGW(TAG, "配网已在运行中");
|
||
return true;
|
||
}
|
||
|
||
ESP_LOGI(TAG, "手动启动蓝牙配网");
|
||
return g_bt_provisioning->StartProvisioning("小智AI-手动配网");
|
||
}
|
||
|
||
/**
|
||
* @brief 手动停止配网(供其他模块调用)
|
||
*
|
||
* @return true 停止成功
|
||
* @return false 停止失败
|
||
*/
|
||
bool stop_provisioning_manually() {
|
||
if (g_bt_provisioning == nullptr) {
|
||
ESP_LOGE(TAG, "蓝牙配网对象未初始化");
|
||
return false;
|
||
}
|
||
|
||
ESP_LOGI(TAG, "手动停止蓝牙配网");
|
||
return g_bt_provisioning->StopProvisioning();
|
||
} |