3、GAP与GATT:蓝牙通信的两大基石
好,咱们今天聊聊蓝牙协议栈里最核心的两个规范——GAP和GATT。说实话,我刚开始接触蓝牙开发时,也被这两个缩写搞得晕头转向。后来在项目里踩过几次坑,才真正理解了它们的分工。
简单来说,GAP负责“怎么找到对方、怎么连上”,GATT负责“连上之后怎么传数据”。一个是连接管理,一个是数据交互。你想想看,这不就跟两个人打电话一样吗?GAP是拨号、接听、挂断这些操作,GATT则是通话内容本身。
3.1 GAP:通用访问规范
GAP(Generic Access Profile)定义了蓝牙设备如何发现彼此、如何建立连接、以及设备在连接过程中的角色。我个人习惯把GAP理解成蓝牙设备的“社交礼仪”。
3.1.1 GAP的角色
在GAP规范里,设备分为两种角色:
- Central(中心设备):主动发起扫描和连接的设备。比如你的手机。
- Peripheral(外围设备):广播自身信息,等待被连接的设备。比如一个蓝牙手环。
嗯,这里要注意:一个设备可以同时扮演两种角色,但实际项目中很少这么干。我在做智能门锁项目时,就遇到过手机既要当Central连接门锁,又要当Peripheral广播自己的状态——结果功耗和连接稳定性都出了问题。
3.1.2 GAP的工作模式
GAP定义了四种工作模式,说白了就是设备在不同阶段的状态:
| 模式 | 说明 | 典型场景 |
|---|---|---|
| 广播模式 | 设备周期性发送广播包 | 蓝牙手环等待连接 |
| 扫描模式 | 设备监听周围的广播包 | 手机搜索附近设备 |
| 发起连接模式 | 主动向某个设备发起连接 | 手机点击“配对”按钮 |
| 连接模式 | 已建立连接,可以收发数据 | 数据传输中 |
我曾经在一个项目中,外围设备广播间隔设得太短(20ms),结果功耗飙升,电池两天就耗光了。后来我把广播间隔调到100ms,功耗降了60%,连接成功率也没受影响。所以广播间隔这个参数,一定要根据实际场景去调。
3.2 GATT:通用属性规范
GATT(Generic Attribute Profile)建立在GAP建立的连接之上,定义了数据如何组织和传输。说白了,GATT就是蓝牙设备之间的“数据字典”。
3.2.1 Client/Server模型
GATT采用经典的Client/Server模型:
- GATT Server:存储数据的一方,提供属性(Attributes)。通常是传感器设备。
- GATT Client:读取或写入数据的一方。通常是手机App。
你想想看,一个温度传感器作为GATT Server,它暴露了温度值这个属性。手机作为GATT Client,发一个读请求,Server就把温度值返回。就这么简单。
但实际项目中,角色可以互换。我记得做一个智能灯项目时,灯本身是Server,但灯也需要读取手机上的时间配置——这时候灯就临时充当了Client。嗯,GATT没有规定谁必须是Server,谁必须是Client,完全看业务需求。
3.2.2 Service与Characteristic
这是GATT里最重要的两个概念,也是初学者最容易搞混的地方。
Service(服务):一组相关功能的集合。每个Service有一个唯一的UUID标识。
Characteristic(特征):Service下的具体数据点。每个Characteristic也有自己的UUID,并且包含一个值(Value)和若干属性(Properties)。
举个例子,一个心率监测设备:
- Service:心率服务(UUID: 0x180D)
- Characteristic 1:心率测量值(UUID: 0x2A37),属性:Notify
- Characteristic 2:心率传感器位置(UUID: 0x2A38),属性:Read
你看,一个Service下可以有多个Characteristic,每个Characteristic负责一个具体的数据点。
- Read:Client可以读取这个值
- Write:Client可以写入这个值
- Notify:Server主动通知Client值发生了变化
- Indicate:类似Notify,但需要Client确认收到
3.2.3 实际开发中的Service设计
我在做蓝牙温湿度计项目时,设计了这样一个Service结构:
// 温湿度服务 UUID: 自定义 0xAAAA
// - 温度特征 UUID: 0xAAA1, 属性: Read + Notify
// - 湿度特征 UUID: 0xAAA2, 属性: Read + Notify
// - 配置特征 UUID: 0xAAA3, 属性: Write
// (用于设置上报间隔、单位切换等)
为什么温度用Notify?因为温度是实时变化的,我希望手机能第一时间收到更新。而配置特征用Write就够了,手机设置完参数后不需要频繁读取。
3.3 GAP与GATT的关系
最后总结一下两者的关系:
- GAP:负责设备发现、连接建立、连接参数更新。没有GAP,两个设备根本见不着面。
- GATT:负责连接建立后的数据交互。没有GATT,连上了也不知道传什么。
你想想看,GAP就像是你去参加一个聚会,先找到人、打招呼、握手。GATT则是握手之后你们开始聊天,聊什么内容、怎么聊,都由GATT来定义。
在实际开发中,我建议你先确定GAP的角色和参数(广播间隔、连接间隔等),再设计GATT的Service和Characteristic结构。顺序搞反了,后面调试起来会很痛苦。嗯,我就吃过这个亏。