《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 远程升级------设备装到天花板上了,怎么更新固件?

相关推荐
万亿少女的梦1682 小时前
基于STM32、RFID与OneNET的开放性实验室电源节点设计
stm32·物联网·onenet·rfid·esp8266
论迹复利10 小时前
第 2 章 PSA Certified 四级认证体系
物联网·安全·security·cra·psa
K成长日志11 小时前
BLE链路层空口包--数据物理信道PDU
网络·物联网·网络协议·嵌入式·蓝牙·iot·ble
星恒讯工业路由器18 小时前
AGV集群通信架构:从单点到百台级调度的挑战与对策
网络·物联网·信息与通信·工业物联网·通信架构·agv集群·实时调度
技术硬汉20 小时前
4G/5G蜂窝天线增益越大越好吗?怎么选适合自己的天线
物联网·5g·信息与通信·iot
mengpp_1234561 天前
城市排水泵站物联网监控系统:智能值守,革新市政运维模式
物联网·智慧城市·iot
芯岭技术1 天前
XL2417D无线透传模组,让用户快速接入多种物联网与智能控制应用
物联网
longxingiot1 天前
轻量化单片机数传终端降低物联网开发门槛
单片机·嵌入式硬件·物联网
java_logo1 天前
Docker 部署 EMQX:轻松搭建物联网 MQTT 消息平台
物联网·mqtt·docker·容器·emqx·轩辕镜像·emqx部署教程