第一章 GATT协议基础:从BLE协议栈到属性读写

做蓝牙开发这么多年,我见过太多工程师一上来就撸GATT代码,结果连Attribute是什么都没搞明白。嗯,今天咱们就把地基打牢。

1.1 BLE协议栈架构:GATT站在哪一层?

先看整体。BLE协议栈分三层:Controller、Host、Application。GATT在Host层的最上面,紧挨着应用。

核心分层关系:

  • Controller:物理层+链路层,管收发、广播、连接
  • Host:L2CAP、ATT、GATT、GAP、SM、Generic Attribute Profile
  • Application:你的业务代码,调用GATT API

说白了,GATT是Host层给应用层开的“后门”。你不需要关心射频怎么调、连接怎么建,GATT帮你把数据包装成“属性”,应用只管读写就行。

BLE协议栈架构与GATT位置 Application(你的业务代码) GATT(Generic Attribute Profile) ATT(Attribute Protocol) L2CAP HCI(Host Controller Interface) Controller(LL + PHY) GATT 位于 Host 层最顶部,直接为应用层提供属性读写接口 下层 ATT 负责属性传输,L2CAP 负责数据分片重组,Controller 负责射频收发

个人经验:我刚开始做BLE时,总以为GATT就是全部。后来调试一个心率设备,发现数据老是丢包,查了两天才发现是L2CAP的MTU没配对。记住:GATT只是“翻译官”,底层传输靠L2CAP和Controller。

1.2 GATT与GAP的关系:一个管连接,一个管数据

很多人把GAP和GATT搞混。我简单说:

  • GAP(Generic Access Profile):管设备怎么被发现、怎么建立连接。广播、扫描、连接参数,都是GAP的事。
  • GATT(Generic Attribute Profile):管连接建立之后,数据怎么读写、怎么通知。服务、特征、属性,都是GATT的事。

你想想看:GAP是“门卫”,负责开门关门;GATT是“管家”,负责屋里东西怎么摆放、怎么取用。

维度 GAP GATT
职责 设备发现、连接建立、广播 属性读写、通知、服务发现
角色 Peripheral / Central Server / Client
数据模型 广播包、扫描响应 Service、Characteristic、Descriptor
典型场景 手机扫描手环、发起连接 手机读取手环心率值、订阅通知

避坑指南:我曾经在一个项目中,把GAP的广播间隔设得太短,结果GATT通信时频繁断连。后来才意识到:GAP的连接参数(比如连接间隔、延迟)直接影响GATT的吞吐量。调GATT之前,先确认GAP参数是否合理。

1.3 Attribute Protocol基础:GATT的“快递员”

GATT自己不干活,它依赖底层的ATT(Attribute Protocol)。ATT负责把数据打包成“属性”,然后通过L2CAP发出去。

一个属性包含四个要素:

  • Attribute Handle:属性的唯一ID,16位整数,0x0001~0xFFFF
  • Attribute Type:UUID,标识属性是什么(比如心率测量是0x2A37)
  • Attribute Permissions:读写权限(可读、可写、需要加密等)
  • Attribute Value:实际数据,最长512字节(受MTU限制)

ATT定义了6种PDU操作:

操作 方向 说明
Read Request / Response Client → Server → Client 客户端请求读取属性值
Write Request / Response Client → Server → Client 客户端写入属性值,需要确认
Write Command Client → Server 客户端写入属性值,不需要确认
Notify Server → Client 服务器主动通知,不需要确认
Indicate Server → Client 服务器主动指示,需要确认
Find Information Request / Response Client → Server → Client 客户端发现属性列表

关键点:Notify和Indicate的区别在于可靠性。Notify像发——发了就不管了;Indicate像发挂号信——对方必须回“收到了”。我建议:对关键数据(比如血糖报警)用Indicate,对高频数据(比如实时心率)用Notify。

1.4 GATT角色定义:Server与Client

GATT定义了两种角色:

  • GATT Server:持有属性数据的一方。通常是传感器设备(如手环、体温计)。
  • GATT Client:读写属性数据的一方。通常是手机、平板、网关。

一个设备可以同时是Server和Client。比如手机连接手环时,手机是Client,手环是Server;但手机同时连接云服务器时,手机又变成了Client。

Server内部的数据组织方式:

GATT Server
├── Service 1 (UUID: 0x180D)  // 心率服务
│   ├── Characteristic 1 (UUID: 0x2A37)  // 心率测量
│   │   ├── Properties: Notify
│   │   ├── Value: [当前心率值]
│   │   └── Descriptor: Client Characteristic Configuration (0x2902)
│   └── Characteristic 2 (UUID: 0x2A38)  // 体感位置
│       ├── Properties: Read
│       └── Value: [手腕/胸部]
└── Service 2 (UUID: 0x180F)  // 电池服务
    └── Characteristic 1 (UUID: 0x2A19)  // 电池电量
        ├── Properties: Read, Notify
        └── Value: [80%]

个人习惯:我设计GATT Server时,会先画一张“属性树”,把Service、Characteristic、Descriptor的层级关系画清楚。这样写代码时不容易漏掉权限配置。你想想看,如果Characteristic的权限配错了,Client读不到数据,排查起来很痛苦。

1.5 本章小结

这一章我们理清了:

  • GATT在BLE协议栈中的位置——Host层最上面
  • GAP管连接,GATT管数据——分工明确
  • ATT是GATT的底层协议,负责属性传输
  • GATT Server持有数据,GATT Client读写数据