3. BLE 协议栈基础:GAP 与 GATT 角色、广播与扫描、连接建立过程、MTU 与 PDU 结构
做 BLE 开发,绕不开两个核心协议:GAP 和 GATT。
我刚开始接触时,也分不清它们到底管什么。后来踩了几个坑才明白——GAP 管“怎么找到对方”,GATT 管“找到之后怎么交换数据”。说白了,一个管连接前,一个管连接后。
3.1 GAP 角色:谁在说话,谁在听?
GAP(Generic Access Profile)定义了设备在无线网络中的四种角色。你想想看,一个蓝牙设备不可能既是广播者又是扫描者——同一时刻只能选一个。
| 角色 | 方向 | 典型场景 |
|---|---|---|
| Broadcaster(广播者) | 只发不收 | 信标、温湿度传感器 |
| Observer(观察者) | 只收不发 | 扫描仪、网关 |
| Peripheral(外设) | 可连接广播 | 手环、心率带 |
| Central(中心) | 主动扫描并发起连接 | 手机、平板 |
我的经验:做低功耗产品时,我习惯把传感器设成 Peripheral 角色,手机做 Central。但如果你做的是网关,Observer 角色就够了——只收不发,功耗更低。
3.2 广播与扫描:连接前的“打招呼”
广播和扫描,是 BLE 连接的第一步。设备通过广播信道(37/38/39)发送数据包,扫描设备则在三个信道上轮流监听。
广播包分两种:
- 可连接广播(Connectable Advertising):扫描设备可以发起连接请求。
- 不可连接广播(Non-connectable Advertising):只发不收,适合信标。
扫描也有两种模式:
- 被动扫描(Passive Scanning):只收广播包,不发请求。
- 主动扫描(Active Scanning):收到广播后,发送 SCAN_REQ 获取更多数据。
注意:主动扫描会增加功耗。我曾经在一个网关项目里用了主动扫描,结果电池撑不到两天。后来改成被动扫描 + 延长扫描间隔,续航直接翻倍。
3.3 连接建立过程:从广播到数据通道
连接建立,说白了就是“你发广播,我发请求,然后握手”。流程如下:
- Peripheral 发送可连接广播(包含设备地址、广播数据)
- Central 收到广播后,发送 CONNECT_REQ(包含连接参数)
- 双方切换到数据信道(37 个数据信道之一)
- 开始按连接间隔交换数据
连接参数里,最关键的三个:
- 连接间隔(Connection Interval):7.5ms ~ 4s,越小功耗越高,但延迟低。
- 从机延迟(Slave Latency):允许从机跳过几次数据交换,省电。
- 监督超时(Supervision Timeout):超过这个时间没收到包,就认为连接断开。
避坑指南:我曾经把连接间隔设成 7.5ms,结果手机端功耗爆炸。后来改成 30ms + 从机延迟 4,功耗降了 60%。记住:低功耗不是靠缩短连接间隔,而是靠拉长间隔 + 合理利用从机延迟。
3.4 GATT 角色:连接后的“数据通道”
连接建立后,GATT(Generic Attribute Profile)开始工作。它定义了两种角色:
- GATT Server:提供数据的一方(如传感器)
- GATT Client:读取/写入数据的一方(如手机 App)
GATT 的数据结构是分层的:
- Service(服务):一组相关功能的集合,比如“电池服务”
- Characteristic(特征):具体的数据点,比如“电池电量百分比”
- Descriptor(描述符):特征的附加信息,比如“电量单位是 %”
每个 Characteristic 都有属性:
- Read:客户端可以读
- Write:客户端可以写
- Notify/Indicate:服务端主动推送数据
我的习惯:Notify 比 Indicate 更省电,因为 Indicate 需要客户端确认。做数据采集时,我优先用 Notify,除非数据必须可靠传输。
3.5 MTU 与 PDU 结构:数据包长什么样?
MTU(Maximum Transmission Unit)是 BLE 连接中一次能传输的最大数据长度。默认是 23 字节(包含 3 字节头 + 20 字节有效载荷)。
但你可以通过 MTU 协商把它变大。流程很简单:
- 客户端发送
MTU Exchange Request - 服务端回复
MTU Exchange Response - 双方取较小的值作为最终 MTU
MTU 最大能到 247 字节(BLE 4.2)或 512 字节(BLE 5.0)。
注意:MTU 协商必须在连接建立后、数据交换前完成。我见过有人先发数据再协商 MTU,结果数据被截断——嗯,这个坑我踩过。
PDU(Protocol Data Unit)是 BLE 数据包的基本单位。结构如下:
| 字段 | 长度 | 说明 |
|---|---|---|
| Header | 2 字节 | 包含 PDU 类型、长度、LLID 等 |
| Payload | 0~251 字节 | 实际数据内容 |
| MIC | 4 字节 | 消息完整性校验(加密时才有) |
PDU 类型包括:
- LL_CONNECT_REQ:连接请求
- LL_DATA:数据包
- LL_EMPTY:空包(用于保持连接)
- LL_TERMINATE_IND:断开连接
关键点:MTU 是应用层能用的最大数据长度,PDU 是链路层实际传输的数据单元。MTU 协商后,应用层一次能发更多数据,但链路层还是按 PDU 分包发送。
3.6 知识结构图:GAP 与 GATT 的关系
下面这张图,帮你理清 GAP 和 GATT 在整个 BLE 协议栈中的位置:
3.7 小结:记住这几点就够了
- GAP 管连接前,GATT 管连接后
- 广播是单向的,扫描是接收端的行为
- 连接建立靠 CONNECT_REQ,参数决定功耗
- MTU 越大,单次传输数据越多,但协商必须在数据交换前完成
- PDU 是链路层的数据单元,MTU 是应用层能用的最大长度
最后一句:做 BLE 开发,别急着写代码。先把 GAP 和 GATT 的角色搞清楚,再动手——省得后面改来改去。