在低功耗蓝牙(BLE)技术体系中,GAP和GATT就像是一对默契的搭档,共同构成了设备通信的基础框架。作为从事物联网开发多年的工程师,我发现很多初学者容易混淆这两个概念。实际上,它们的职责划分非常清晰:GAP负责"认识新朋友",而GATT则处理"朋友间的对话"。
GAP(通用访问配置文件)定义了设备如何被发现和连接。想象你参加一个社交聚会,GAP就是决定你是否佩戴姓名标签(广播模式)、如何与他人握手(连接建立)以及谁能获取你的联系方式(安全权限)的那套规则。我曾在智能家居项目中遇到设备无法被发现的问题,最终排查发现正是GAP层的广播参数配置不当所致。
GATT(通用属性配置文件)则规定了连接建立后的数据交互方式。如果把BLE设备比作一本书,GATT就是这本书的目录结构——它用服务(章节)、特征(段落)和描述符(注释)的层级来组织数据。在开发健康监测设备时,我们必须严格遵循GATT规范定义心率服务(0x180D)和心率测量特征(0x2A37),否则主流手机APP将无法识别我们的设备。
BLE设备通过广播信道(37/38/39)宣告自己的存在。在实际项目中,广播间隔的设置尤为关键:
我曾测试过某款智能手环,当广播间隔设为500ms时,手机平均需要2.3秒发现设备;调整为100ms后,发现时间缩短到0.8秒,但电池续航下降了15%。这种权衡需要根据具体应用场景决定。
广播数据包(Advertising Data)最多可包含31字节,通常包含:
plaintext复制Flags: 0x06 (LE General Discoverable Mode)
Complete Local Name: "MyDevice"
Service UUID: 0x180A (Device Information)
Tx Power Level: 0xC4 (-60 dBm)
典型的BLE连接过程如下:
重要提示:连接参数设置不当会导致频繁断连。某工业传感器项目曾因将监控超时设为最小值(100ms),在信号干扰环境下出现大规模连接不稳定问题。
GAP定义了三种安全模式:
在智能门锁开发中,我们采用模式3的LESC(LE Secure Connections)配对方式,配合MITM(中间人防护)保护,有效防止了重放攻击。具体实现时需要注意:
GATT采用树状结构组织数据,以下是一个环境监测设备的典型结构:
code复制Device Information Service (0x180A)
├── Manufacturer Name String (0x2A29)
└── Firmware Revision String (0x2A26)
Environmental Sensing Service (0x181A)
├── Temperature (0x2A6E)
│ ├── Value
│ └── Client Characteristic Configuration
└── Humidity (0x2A6F)
├── Value
└── Client Characteristic Configuration
特征属性(Properties)决定了数据访问方式:
在智能灯泡项目中,我们实现了以下典型交互:
cpp复制// 发现主要服务
ble_discover_primary_services(conn_handle, NULL);
cpp复制// 写入CCC描述符
uint8_t ccc_value[2] = {0x01, 0x00}; // 启用通知
ble_write_char_value(conn_handle, ccc_handle, ccc_value, sizeof(ccc_value));
cpp复制void on_temp_notification(const uint8_t *data, uint16_t length) {
float temperature = (data[0] | (data[1]<<8)) / 100.0f;
printf("Current temp: %.1f°C\n", temperature);
}
ATT_MTU(最大传输单元)默认23字节,实际数据可用仅20字节(3字节头)。通过MTU协商可提升吞吐量:
sequence复制客户端->服务器: Exchange MTU Request (517字节)
服务器->客户端: Exchange MTU Response (247字节)
双方采用较小值: 247字节
实测数据显示,大文件传输时:
以健身手环连接手机为例:
手环(外围设备)广播包含:
手机(中心设备):
服务发现阶段:
数据传输阶段:
在开发BLE网关时,我们总结了以下优化技巧:
连接参数调优:
广播数据精简:
服务设计原则:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 设备无法发现 | 未启用广播 | 检查GAP角色配置 |
| 连接立即断开 | 参数不兼容 | 调整连接间隔/延迟 |
| 数据传输卡顿 | MTU太小 | 发起MTU交换请求 |
| 通知收不到 | CCC未配置 | 检查描述符写入值 |
某次调试中发现心率数据异常,最终定位是特征属性误设为Write instead of Notify。这个教训让我养成了双重检查属性设置的习惯。
不同平台的特殊要求:
在开发跨平台设备时,我们维护了不同的广播数据模板,通过编译选项切换。例如iOS专用广播包会确保前5字节包含完整设备名称。