Chapter 05

蓝牙 BLE 开发

深入理解 BLE 协议栈架构,实现 GATT 服务与手机 App 的无线通信

BLE vs 经典蓝牙

蓝牙技术联盟(Bluetooth SIG)维护两套截然不同的蓝牙标准:经典蓝牙(Classic Bluetooth / BR/EDR)低功耗蓝牙(Bluetooth Low Energy / BLE)。ESP32 同时支持两者,但 IoT 设备绝大多数场景使用 BLE。

特性经典蓝牙BLE(低功耗蓝牙)
频段2.4GHz,79 个 1MHz 信道2.4GHz,40 个 2MHz 信道
数据速率1~3 Mbps125Kbps~2Mbps(BLE 5)
功耗高(持续传输)极低(突发传输,平均 μA 级)
连接建立约 100ms约 3ms
典型应用音频(耳机/音箱)、文件传输传感器、健康设备、Beacon
Profile 标准A2DP(音频)、HFP(通话)GATT(自定义服务)、HRS/HID 等
BLE 5.0 新特性

BLE 5.0(2016年)引入了两个重要新特性:LE Audio(低延迟音频,适合助听器等)和 扩展广播(Extended Advertising)(广播包最大 255 字节,旧版仅 31 字节)。ESP32 原生支持 BLE 4.2,乐鑫新款芯片(ESP32-S3等)支持 BLE 5.0。

BLE 协议栈分层架构

BLE 协议栈层次结构: 应用层(App) │ ┌───▼───────────────────────────┐ │ GATT(通用属性协议) │ ← 定义数据格式和读写操作 └───────────────────────────────┘ │ ┌───▼───────────────────────────┐ │ ATT(属性协议) │ ← Client/Server 数据交换 └───────────────────────────────┘ │ ┌───▼───────────────────────────┐ │ SMP(安全管理协议) │ ← 配对、绑定、加密 └───────────────────────────────┘ │ ┌───▼───────────────────────────┐ │ GAP(通用访问规范) │ ← 广播、扫描、连接管理 └───────────────────────────────┘ │ ┌───▼───────────────────────────┐ │ L2CAP(逻辑链路控制) │ ← 数据分段重组 └───────────────────────────────┘ │ ┌───▼───────────────────────────┐ │ LL(链路层) │ ← 物理信道访问、CRC └───────────────────────────────┘ │ ┌───▼───────────────────────────┐ │ PHY(物理层) │ ← 1M/2M/Coded 调制 └───────────────────────────────┘

GATT 核心概念

Profile(规范)
由蓝牙联盟或自定义的一套 Service 集合,定义特定功能(如心率监测 HRS Profile、电池服务 BAS Profile)。
Service(服务)
功能单元,用 128-bit UUID 标识(标准服务有 16-bit 简短 UUID)。一个 Profile 可包含多个 Service,如 HRS Profile 包含心率服务 (0x180D) 和设备信息服务 (0x180A)。
Characteristic(特征值)
实际数据载体,每个 Service 包含一个或多个 Characteristic。每个 Characteristic 有属性(Properties):READ、WRITE、NOTIFY、INDICATE 等。
Descriptor(描述符)
Characteristic 的元数据。最重要的是 CCCD(Client Characteristic Configuration Descriptor,UUID 0x2902),客户端写入 0x0001 启用 Notify,写入 0x0002 启用 Indicate。
Notify vs Indicate
Notify:服务器主动推送数据,不等待客户端确认(可能丢失)。Indicate:服务器推送后等待客户端 ATT_HANDLE_VALUE_CONFIRM 确认,更可靠但开销更大。

NimBLE vs Bluedroid

ESP-IDF 提供两套蓝牙协议栈可供选择:

NimBLE(推荐)

Apache 开源项目,代码更小(约 65KB vs Bluedroid 的 200KB+),配置更简单,ESP-IDF v4.4+ 起稳定,API 更现代。新项目推荐使用 NimBLE。

Bluedroid

来自 Android 的完整蓝牙协议栈,同时支持 BLE 和经典蓝牙(A2DP/HFP 等)。需要音频功能时必须使用 Bluedroid,内存占用更大。

在 menuconfig 中选择协议栈

# 打开配置菜单
idf.py menuconfig

# 路径:Component config → Bluetooth → Bluetooth controller → ...
# 选择 NimBLE - BLE only(仅需 BLE)
# 或   Bluedroid - Dual-mode(需要经典蓝牙/音频)

BLE 广播与扫描

BLE 设备在未连接时可以持续广播(Advertising)数据包,最大 31 字节(BLE 5 扩展广播可达 255 字节)。其他设备扫描(Scanning)时可以接收这些广播,无需建立连接。这是 iBeacon、EddyStone 等定位技术和 Mesh 网络的基础。

/* NimBLE 广播示例(BLE Beacon,无需连接) */
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "host/ble_hs.h"
#include "host/util/util.h"
#include "services/gap/ble_svc_gap.h"

static void ble_advertise(void)
{
    struct ble_gap_adv_params adv_params = {
        .conn_mode = BLE_GAP_CONN_MODE_NON,    // 不可连接的广播
        .disc_mode = BLE_GAP_DISC_MODE_GEN,    // 通用可发现
        .itvl_min  = 160,    // 100ms(单位 0.625ms)
        .itvl_max  = 160,
    };

    struct ble_hs_adv_fields fields = {
        .flags          = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP,
        .name           = (uint8_t *)"ESP32-Sensor",
        .name_len       = strlen("ESP32-Sensor"),
        .name_is_complete = 1,
    };
    ble_gap_adv_set_fields(&fields);
    ble_gap_adv_start(BLE_OWN_ADDR_PUBLIC, NULL, BLE_HS_FOREVER,
                      &adv_params, NULL, NULL);
}

心率计 HRS Profile 实现

心率服务(Heart Rate Service,UUID 0x180D)是蓝牙联盟标准化的 GATT Profile,包含心率测量特征值(0x2A37),手机健康 App 可直接识别。

#include "services/hrs/ble_svc_hrs.h"
#include "nimble/nimble_port.h"

/* Heart Rate Measurement 数据格式(第一字节为 Flags):
   Bit 0: 0=uint8, 1=uint16 心率值
   Bit 1: 传感器接触状态
   Bit 4: RR 间隔是否存在                                */

static void heart_rate_task(void *arg)
{
    uint8_t hr = 60;
    while (1) {
        /* 模拟心率变化 */
        hr = 60 + (esp_random() % 40);
        ble_svc_hrs_heart_rate_set(hr);    // 更新心率值
        ESP_LOGI("HRS", "心率: %d bpm", hr);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

static void ble_host_task(void *param)
{
    nimble_port_run();    // 阻塞运行 BLE 主机任务
    nimble_port_freertos_deinit();
}

void app_main(void)
{
    nvs_flash_init();
    nimble_port_init();

    ble_svc_gap_init();     // 初始化 GAP 服务
    ble_svc_gatt_init();    // 初始化 GATT 服务
    ble_svc_hrs_init();     // 初始化心率服务

    ble_svc_gap_device_name_set("ESP32-HeartRate");

    nimble_port_freertos_init(ble_host_task);
    xTaskCreate(heart_rate_task, "hr_task", 2048, NULL, 5, NULL);
}
手机端测试工具推荐

nRF Connect(Nordic Semiconductor 出品)是最专业的 BLE 调试 App,可以扫描设备、查看 Service/Characteristic UUID、手动读写、启用 Notify,iOS 和 Android 均有,强烈推荐。