3. GATT 协议详解:ATT、服务、特征值、描述符、UUID

讲 BLE 开发,GATT 是绕不开的核心。我个人习惯把它理解为“蓝牙设备之间的数据契约”——双方按这个契约来读写数据。今天我们就把它拆开,看看里面到底有哪些零件。

3.1 属性协议(ATT)——数据搬运工

ATT 是 GATT 的底层传输机制。说白了,它定义了两个角色:Client 和 Server。Client 发起请求,Server 响应数据。每一笔数据交换都叫“属性操作”——读、写、通知、指示等等。

我在项目中遇到过一个问题:ATT 的 MTU(最大传输单元)默认只有 23 字节。如果你要传个大包,就得拆成多帧。有一次我傻等一个设备回复,发现它根本不响应,后来才意识到是 MTU 没协商。气不气人?所以拿到连接句柄后,第一件事就是发起 MTU 交换。

💡 经验:MTU 协商是异步的,别在连接建立后立刻读长特征值。等收到 MTU 更新事件再动手。

3.2 服务(Service)——功能分区

一个 GATT Server 里可以包含多个 Service。每个 Service 相当于一个功能模块。比如“电池服务”、“设备信息服务”、“心率服务”。

Service 分为两种:

  • Primary Service:独立的、主要功能服务。
  • Secondary Service:被其他 Service 引用的辅助服务(用得少)。

每个 Service 都有一个 UUID。蓝牙 SIG(标准组织)定义的 UUID 是 16 位,比如 0x180F 代表电池服务。如果厂家自定义,就用 128 位 UUID。

📌 关键点:一个 Service 下可以包含多个 Characteristic,也可能包含其他 Service(通过 Include 定义),但平时我们主要关注 Characteristic。

3.3 特征值(Characteristic)——真正的数据载体

Characteristic 是 GATT 里最常用的东西。它才是你收发数据的地方。每个 Characteristic 包含三个要素:

  1. 声明(Declaration):固定属性句柄,告诉你这个特征值的类型、权限。
  2. 值(Value):实际数据存放处。
  3. 描述符(Descriptor):附加信息(比如 CCCD 用于使能通知)。

嗯,这里要注意:特征值的“值”可以很长,但受 MTU 限制。如果你要传几百字节,就得考虑分片或者使用长读/长写。

⚠️ 坑:我曾经在写自定义特征值时,忘记设置正确的属性权限(比如需要写响应但设成了无响应),导致手机端一直不回调成功。一定要检查特征值的 Properties 标志位。

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长度典型例子说明
Service16 bit / 128 bit0x180F (电池)功能模块
Characteristic16 bit / 128 bit0x2A19 (电池电量)数据容器
Descriptor16 bit / 128 bit0x2902 (CCCD)元数据/配置
🎯 核心关系:一个 Service 包含若干 Characteristic,每个 Characteristic 可能附带若干 Descriptor,所有这些实体都靠唯一 UUID 来标识。
GATT协议层次结构 GATT 协议层次结构 属性协议 (ATT) 服务 (Service) 服务 (Service) 服务 (Service) 特征值 (Characteristic) 特征值 (Characteristic) 特征值 (Characteristic) 描述符 (Descriptor) 描述符 (Descriptor) 描述符 (Descriptor) 每一层实体都由唯一的 UUID 标识

画了个层次图,你要记清楚:ATT 是快递员,Service 是包裹分类,Characteristic 是具体物品,Descriptor 是使用说明书,而 UUID 就是每个物品上的二维码。理解了这套结构,你就能看懂任何 GATT Profile 了。

下回咱们用代码把这些概念串起来,写一个简单的 BLE 服务。别急,先把图存脑子里。