《ESP32 物联网全栈实战-07》BLE 蓝牙

第 7 篇:BLE 蓝牙------近场通信与智能配网

WiFi 让 ESP32 连上了互联网,但有个鸡生蛋的问题:设备第一次怎么知道 WiFi 密码? BLE 蓝牙解决了这个问题------用手机通过蓝牙把 WiFi 凭据传给设备。同时,BLE 本身也是一个近场通信方案。


1. BLE 核心概念速览

在写代码之前,先搞清楚 BLE 的几个关键概念:

复制代码
BLE 角色:
├── Central(中心设备)→ 手机(主动扫描、发起连接)
└── Peripheral(外设) → ESP32(广播、等待连接)

GATT 数据层次:
Profile(配置文件)
  └── Service(服务)          ← UUID 标识,如 0x1809 = 健康温度计
        └── Characteristic(特征值)← 实际数据载体
              ├── Value(值)
              └── Descriptor(描述符)

Characteristics 的操作类型:

属性 含义 方向
Read 手机主动读 外设→手机
Write 手机主动写 手机→外设
Notify 外设主动推 外设→手机(无需手机轮询)
Indicate 带确认的推送 外设→手机(手机必须确认)

2. ESP32 作为 BLE 外设------完整代码

c 复制代码
// ble_sensor.c ------ BLE GATT 服务器,暴露温湿度服务

#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_bt_main.h"
#include "esp_bt.h"

#define GATTS_SERVICE_UUID       0x00FF
#define GATTS_CHAR_TEMP_UUID     0xFF01
#define GATTS_CHAR_HUMI_UUID     0xFF02
#define GATTS_NUM_HANDLE         8

static uint8_t adv_config_done = 0;

// 广播数据:设备名 + 16-bit Service UUID
static esp_ble_adv_data_t adv_data = {
    .set_scan_rsp = false,
    .include_name = true,
    .include_txpower = true,
    .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};

static esp_ble_adv_params_t adv_params = {
    .adv_int_min = 0x20,   // 32 × 0.625ms = 20ms
    .adv_int_max = 0x40,   // 64 × 0.625ms = 40ms
    .adv_type = ADV_TYPE_IND,
    .own_addr_type = BLE_ADDR_TYPE_PUBLIC,
    .channel_map = ADV_CHNL_ALL,
    .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};

// GATT 事件回调
static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
                                 esp_ble_gatts_cb_param_t *param) {
    switch (event) {
    case ESP_GATTS_REG_EVT:
        // 注册成功 → 设置广播数据
        esp_ble_gap_set_device_name("ESP32-Sensor");
        esp_ble_gap_config_adv_data(&adv_data);
        break;

    case ESP_GATTS_READ_EVT:
        // 手机读取温湿度
        if (param->read.handle == temp_handle) {
            float temp = 25.6f;  // 实际从传感器读
            esp_ble_gatts_send_response(gatts_if, param->read.conn_id,
                param->read.trans_id, ESP_GATT_OK, NULL);
        }
        break;

    case ESP_GATTS_CONNECT_EVT:
        // 手机连上了,可以降低广播间隔省电
        esp_ble_gap_stop_advertising();
        break;

    case ESP_GATTS_DISCONNECT_EVT:
        // 手机断开了,重新开始广播
        esp_ble_gap_start_advertising(&adv_params);
        break;

    default:
        break;
    }
}

// Gap 事件回调
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
    switch (event) {
    case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
        adv_config_done = 1;
        esp_ble_gap_start_advertising(&adv_params);
        break;
    default:
        break;
    }
}

// 初始化 BLE
void ble_sensor_init(void) {
    esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);  // 只开 BLE,释放经典蓝牙内存

    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
    esp_bt_controller_init(&bt_cfg);
    esp_bt_controller_enable(ESP_BT_MODE_BLE);

    esp_bluedroid_init();
    esp_bluedroid_enable();

    esp_ble_gatts_register_callback(gatts_event_handler);
    esp_ble_gap_register_callback(gap_event_handler);
    esp_ble_gatts_app_register(GATTS_SERVICE_UUID);
}

3. BLE 配网------解决"第一次联网"问题

市面上几乎所有智能家居设备都用 BLE 配网。流程如下:

复制代码
┌──────┐                      ┌────────┐
│ 手机  │                      │ ESP32  │
│ App  │                      │        │
└──┬───┘                      └───┬────┘
   │  1. 扫描 BLE 设备             │
   │←─────────────────────────────│ 广播 "ESP32-Sensor"
   │  2. 连接                     │
   │─────────────────────────────→│
   │  3. 写入 WiFi SSID+Password  │
   │  (Write Characteristic)      │
   │─────────────────────────────→│ 收到凭据 → 存 NVS
   │  4. 断开连接                 │
   │←─────────────────────────────│ 重启,用新凭据连 WiFi
   │                              │
   │  5. 设备 via WiFi 上报状态   │
   │←═════════════════════════════│ "配网成功,IP=192.168.1.100"

ESP32 端的关键代码:

c 复制代码
// 定义配网专用的 Characteristic
// UUID: 0xFF03, 属性: Write, 最大长度: 64 字节

// 手机写入的 JSON: {"ssid":"MyWiFi","password":"12345678"}

static void gatts_event_handler(esp_gatts_cb_event_t event, ...) {
    case ESP_GATTS_WRITE_EVT: {
        // 收到手机发来的 WiFi 凭据
        char ssid[33] = {0}, pass[65] = {0};
        // 解析 JSON → 写入 NVS
        nvs_set_str(nvs_handle, "ssid", ssid);
        nvs_set_str(nvs_handle, "pass", pass);
        nvs_commit(nvs_handle);

        // 告诉手机"收到"
        esp_ble_gatts_send_response(gatts_if, param->write.conn_id, ...);

        // 延迟 1 秒后重启
        vTaskDelay(pdMS_TO_TICKS(1000));
        esp_restart();
    }
}

重启后 app_main 里:

c 复制代码
// 从 NVS 读凭据
nvs_get_str(nvs_handle, "ssid", ssid, &len);
nvs_get_str(nvs_handle, "pass", pass, &len2);

if (strlen(ssid) > 0) {
    wifi_connect(ssid, pass);  // 正常连 WiFi
} else {
    ble_provisioning_start();  // 没有凭据 → 进入配网模式
}

4. BLE 配网 vs SmartConfig vs 网页配网

方式 优点 缺点
BLE 配网 稳定、成功率高、支持回传状态 需要蓝牙
SmartConfig 不需要蓝牙 部分路由器不兼容、成功率约 70%
网页配网(AP模式) 不需要 App 用户操作繁琐(连热点→填表单)
声波配网 酷炫 环境噪声影响大

推荐方案:BLE 为主,AP 模式为备份。


下一篇:OTA 远程升级------设备装到天花板上了,怎么更新固件?

相关推荐
华允物联-HUAIOT16 小时前
LoRa通信距离一般多远?影响因素与实际测试方法
物联网
YCOSA20251 天前
雨晨 Windows 11 IoT 企业版 Edge 轻装 VCDX 29680.1000
windows·物联网
lzqrzpt1 天前
临沂LED驱动电源工程选型标准与工艺对比解析
python·单片机·嵌入式硬件·物联网
microrain1 天前
权限改了,接口立刻生效,菜单要等重新登录:SagooIoT 权限体系的四道闸门
物联网·golang·开源·sagooiot
悟天特斯1 天前
AI驱动的楼宇节能:从“经验省电“到“算法能效“的进化之路
人工智能·物联网
wuyk5551 天前
《WiFi 嵌入式物联网开发全套实战》| 第 29 章 WiFi 长连接保活机制:无线层 + 应用层双重心跳
物联网
乐讯通物联网服务商1 天前
外勤执法记录仪接入物联网:间歇联网终端通信方案选型分析
物联网·执法记录仪·物联网卡
微三云马玮均—GEO源码系统 私有化部署2 天前
消费返物业费:消费+服务趋势的必然产物!
大数据·人工智能·物联网·区块链·生活
笨笨饿2 天前
140_AI新手村MCP与Skills是干嘛的
开发语言·人工智能·python·stm32·单片机·嵌入式硬件·物联网
小神兵2 天前
对电压源与电流源的的简单理解
单片机·嵌入式硬件·物联网