从零到一:手把手教你用ESP32和Arduino IDE配置BLE的GAP广播与GATT服务
从零到一:手把手教你用ESP32和Arduino IDE配置BLE的GAP广播与GATT服务
在物联网设备爆炸式增长的今天,低功耗蓝牙(BLE)技术因其低能耗、低成本的特点,成为智能家居、可穿戴设备和工业传感器等场景的首选通信方案。ESP32作为一款集成了Wi-Fi和蓝牙双模的微控制器,配合Arduino IDE的易用性,让开发者能够快速实现BLE功能原型。本文将带你从零开始,通过一个完整的温湿度监测项目,掌握BLE的核心概念GAP和GATT的实战应用。
1. 环境准备与基础概念
在开始编码前,我们需要准备好硬件和软件环境,并理解几个关键概念:
- ESP32开发板:推荐使用ESP32-WROOM-32系列,价格适中且性能稳定
- Arduino IDE:需安装ESP32开发板支持包(可通过 Boards Manager 添加)
- 手机调试工具:nRF Connect或LightBlue等BLE调试App
BLE通信建立在两个核心协议上:
- GAP(通用访问规范):负责设备可见性、广播和连接管理
- GATT(通用属性规范):定义数据传输的结构和服务发现
理解这两者的关系就像理解一栋建筑的入口(GAP)和内部房间功能(GATT)——GAP决定别人如何找到并进入建筑,而GATT则定义了建筑内部各个房间的用途和交互方式。
2. 配置GAP广播实现设备可见性
要让手机能够发现ESP32设备,首先需要配置GAP层的广播参数。在Arduino IDE中新建项目,引入BLE库:
#include <BLEDevice.h> #include <BLEUtils.h> #include <BLEServer.h> void setup() { Serial.begin(115200); BLEDevice::init("MyESP32_TempSensor"); // 设置设备名称 // 获取广播对象并设置参数 BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); pAdvertising->setMinInterval(0x20); // 最小广播间隔32*0.625ms=20ms pAdvertising->setMaxInterval(0x40); // 最大广播间隔64*0.625ms=40ms pAdvertising->start(); Serial.println("广播已启动,设备可被发现"); }关键参数说明:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| 设备名称 | 8-16字符 | 手机扫描时显示的名称 |
| 广播间隔 | 20-40ms | 平衡发现速度和功耗 |
| 广播类型 | 默认CONNECTABLE | 允许设备连接 |
常见问题排查:
- 如果手机无法发现设备,检查广播是否确实启动
- 广播间隔设置过大会导致设备发现缓慢
- 设备名称包含特殊字符可能导致部分手机无法识别
3. 构建GATT服务实现数据通信
设备被发现后,我们需要定义GATT服务来传输温湿度数据。BLE采用层级结构组织数据:
- 服务(Service):代表一个完整功能(如环境监测)
- 特征(Characteristic):服务中的具体数据点(如温度值)
- 描述符(Descriptor):特征的附加信息(如单位、权限)
以下是创建温湿度服务的完整代码:
// 定义服务UUID - 可自定义或使用标准UUID #define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" #define TEMPERATURE_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" #define HUMIDITY_UUID "cba1d466-344c-4be3-ab3f-189f80dd7518" BLEService *pService; BLECharacteristic *pTempCharacteristic; BLECharacteristic *pHumidCharacteristic; void setup() { // ...之前的广播配置代码... // 创建服务 pService = server->createService(SERVICE_UUID); // 创建温度特征(可读+通知) pTempCharacteristic = pService->createCharacteristic( TEMPERATURE_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY ); // 创建湿度特征(可读) pHumidCharacteristic = pService->createCharacteristic( HUMIDITY_UUID, BLECharacteristic::PROPERTY_READ ); // 启动服务 pService->start(); }特征属性决定了客户端如何与数据交互:
- READ:允许读取当前值
- WRITE:允许修改值
- NOTIFY:服务器主动推送变化(无需确认)
- INDICATE:服务器推送变化(需客户端确认)
4. 数据更新与客户端交互
有了服务框架后,我们需要定期更新传感器数据并处理客户端请求。以下是DHT22传感器数据读取和通知的示例:
#include <DHT.h> #define DHTPIN 4 // 数据引脚 #define DHTTYPE DHT22 DHT dht(DHTPIN, DHTTYPE); void updateSensorData() { float temp = dht.readTemperature(); float humid = dht.readHumidity(); if (!isnan(temp)) { pTempCharacteristic->setValue(temp); pTempCharacteristic->notify(); // 主动通知订阅的客户端 } if (!isnan(humid)) { pHumidCharacteristic->setValue(humid); } } void loop() { static unsigned long lastUpdate = 0; if (millis() - lastUpdate > 2000) { // 每2秒更新一次 updateSensorData(); lastUpdate = millis(); } }在实际项目中,你可能还需要处理以下场景:
- 连接参数协商:通过
BLEServer的updateConnParams方法优化连接间隔 - 安全配对:设置
BLESecurity对象实现加密通信 - 多服务支持:为复杂设备创建多个服务实例
5. 手机端测试与性能优化
完成固件开发后,使用nRF Connect进行端到端测试:
- 扫描并连接"MyESP32_TempSensor"设备
- 浏览服务列表,确认温湿度服务存在
- 订阅温度特征通知,观察数据变化
- 手动读取湿度特征值
性能优化建议:
广播优化:
- 合理设置广播间隔(室内20-60ms,户外可适当延长)
- 使用定向广播减少扫描响应时间
连接参数调优:
// 示例:设置连接参数(最小间隔,最大间隔,延迟,超时) pServer->updateConnParams(deviceAddress, 6, 12, 0, 400);功耗管理:
- 在无连接时进入轻度睡眠模式
- 动态调整传感器采样频率
BLE开发中常见的问题包括连接不稳定、数据吞吐量低等,这些问题通常可以通过调整以下参数解决:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 频繁断开 | 连接间隔过长 | 减小最大连接间隔 |
| 数据延迟 | 连接延迟设置过高 | 设置为0或适当值 |
| 功耗过高 | 通知频率太快 | 降低数据更新频率 |
6. 进阶功能扩展
掌握了基础GAP和GATT配置后,可以进一步扩展项目功能:
多特征服务:添加更多传感器数据
// 添加气压特征示例 #define PRESSURE_UUID "d7a4f0a2-6f8e-11eb-9439-0242ac130002" BLECharacteristic *pPressureChar = pService->createCharacteristic( PRESSURE_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY );客户端特征配置描述符(CCCD):管理通知开关
// 为温度特征添加描述符 pTempCharacteristic->addDescriptor(new BLE2902());自定义广播数据:在广播包中包含额外信息
// 添加厂商特定数据 BLEAdvertisementData advData; advData.setManufacturerData("MyCompany"); pAdvertising->setAdvertisementData(advData);在实际项目中,我发现ESP32的BLE堆栈对同时处理多个连接时性能会下降,这时可以考虑:
- 限制最大连接数
- 优化服务结构,减少特征数量
- 使用更高效的编码方式压缩数据
