toy-hardware/main/bluetooth_provisioning_example.cc

332 lines
10 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_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();
}