3. GATT 协议详解:ATT、服务、特征值、描述符、UUID
讲 BLE 开发,GATT 是绕不开的核心。我个人习惯把它理解为“蓝牙设备之间的数据契约”——双方按这个契约来读写数据。今天我们就把它拆开,看看里面到底有哪些零件。
3.1 属性协议(ATT)——数据搬运工
ATT 是 GATT 的底层传输机制。说白了,它定义了两个角色:Client 和 Server。Client 发起请求,Server 响应数据。每一笔数据交换都叫“属性操作”——读、写、通知、指示等等。
我在项目中遇到过一个问题:ATT 的 MTU(最大传输单元)默认只有 23 字节。如果你要传个大包,就得拆成多帧。有一次我傻等一个设备回复,发现它根本不响应,后来才意识到是 MTU 没协商。气不气人?所以拿到连接句柄后,第一件事就是发起 MTU 交换。
3.2 服务(Service)——功能分区
一个 GATT Server 里可以包含多个 Service。每个 Service 相当于一个功能模块。比如“电池服务”、“设备信息服务”、“心率服务”。
Service 分为两种:
- Primary Service:独立的、主要功能服务。
- Secondary Service:被其他 Service 引用的辅助服务(用得少)。
每个 Service 都有一个 UUID。蓝牙 SIG(标准组织)定义的 UUID 是 16 位,比如 0x180F 代表电池服务。如果厂家自定义,就用 128 位 UUID。
3.3 特征值(Characteristic)——真正的数据载体
Characteristic 是 GATT 里最常用的东西。它才是你收发数据的地方。每个 Characteristic 包含三个要素:
- 声明(Declaration):固定属性句柄,告诉你这个特征值的类型、权限。
- 值(Value):实际数据存放处。
- 描述符(Descriptor):附加信息(比如 CCCD 用于使能通知)。
嗯,这里要注意:特征值的“值”可以很长,但受 MTU 限制。如果你要传几百字节,就得考虑分片或者使用长读/长写。
3.4 描述符(Descriptor)——特征值的说明书
Descriptor 就是描述符。它给特征值提供元数据。最常用的是 Client Characteristic Configuration Descriptor (CCCD),也就是通知/指示开关。你把 0x0001 写进去,Server 就会主动推送数据;写 0x0002 就变成带确认的指示。
其他描述符也很多:
- Characteristic User Description:字符串描述,比如“心率值”。
- Characteristic Presentation Format:数据格式说明(单位、小数点等)。
- Characteristic Aggregate Format:聚合多个特征值的格式。
每个描述符也拥有自己的 UUID 和句柄。注意:一个特征值可以有多个描述符,但通常只有一个 CCCD。
3.5 UUID——全球唯一的身份证
UUID 就是 128 位的唯一标识符,用于区分 Service、Characteristic、Descriptor。标准蓝牙协议规定:
- 16 位 UUID(如 0x180D)是 SIG 定义的,底层实际存为 128 位格式:
0000180D-0000-1000-8000-00805F9B34FB。 - 自定义 128 位 UUID 格式:
12345678-1234-5678-1234-56789abcdef0,避免冲突。
我记得有一次产品联调,两边自定义 UUID 不一致,一直连不上。查了半天发现少写了一个字符。UUID 这东西,复制粘贴都要小心,最好用结构体定义并做校验。
| 层级 | UUID长度 | 典型例子 | 说明 |
|---|---|---|---|
| Service | 16 bit / 128 bit | 0x180F (电池) | 功能模块 |
| Characteristic | 16 bit / 128 bit | 0x2A19 (电池电量) | 数据容器 |
| Descriptor | 16 bit / 128 bit | 0x2902 (CCCD) | 元数据/配置 |
画了个层次图,你要记清楚:ATT 是快递员,Service 是包裹分类,Characteristic 是具体物品,Descriptor 是使用说明书,而 UUID 就是每个物品上的二维码。理解了这套结构,你就能看懂任何 GATT Profile 了。
下回咱们用代码把这些概念串起来,写一个简单的 BLE 服务。别急,先把图存脑子里。