1. GATT 协议基础与特征模型

1.1 概述

GATT(Generic Attribute Profile)是 BLE 协议栈中最高层的 Profile,定义了客户端-服务器架构下如何通过 属性协议(ATT) 发现、读取、写入和通知特征值。GATT 的核心模型包括:

  • 属性协议(ATT):提供底层属性数据的读/写/通知原语。
  • 特征(Characteristic):包含一个值以及对该值的元数据(属性、权限、描述符)。
  • 服务(Service):一组相关特征的集合。
  • UUID 规范:用于唯一标识服务、特征和描述符。
实现要点: 理解 ATT 与 GATT 的分层关系是后续实现特征读写和本地缓存更新的基础。ATT 负责数据的传输,GATT 负责数据模型的构建与交互流程。

1.2 属性协议(ATT)

ATT 是 GATT 的底层传输协议,操作对象为 属性(Attribute)。每个属性包含:

属性字段长度说明
句柄(Handle)2 字节属性的唯一索引,从 0x0001 开始
类型(Type)2 或 16 字节UUID,标识属性属于哪个服务/特征
权限(Permissions)可变读/写/加密等访问权限
值(Value)可变实际承载的数据

ATT 定义了多种 PDU,包括:Read Request/Response、Write Request/Command、Notify/Indicate 等。一个典型的 Read 流程如下(命令示例):

// GATT Client 发送 Read Request
ATT_Read_Req(handle = 0x0020)
// GATT Server 返回 Read Response
ATT_Read_Rsp(value = [0x01, 0x02, 0x03])

1.3 特征结构

一个特征由三个属性的固定组合构成:

  1. 特征声明(Characteristic Declaration):属性类型为 0x2803,包含特征属性、值句柄和特征 UUID。
  2. 特征值(Characteristic Value):属性类型为特征 UUID,存储实际数据。
  3. 特征描述符(Characteristic Descriptor):可选,例如客户端特征配置描述符(CCCD,UUID 0x2902)用于开启通知/指示。

特征声明属性的值结构如下:

typedef struct {
    uint8_t properties;          // 位掩码:读/写/通知/指示等
    uint16_t value_handle;       // 特征值属性句柄
    uint16_t uuid;               // 特征 UUID 或 128-bit UUID 的低16位
} __attribute__((packed)) characteristic_decl_t;
常见错误: 实现特征读写时,忘记定义 CCCD 导致通知无法开启。CCCD 必须为每个支持通知/指示的特征手动添加描述符,并处理 GATT 客户端的写请求。

1.4 服务与特征层次

GATT 的层次结构从顶层到底层依次为:Profile → Service → Characteristic → Descriptor。

服务分为主要服务(Primary Service)和次要服务(Secondary Service)。每个服务由一个服务声明属性(UUID 0x2800 或 0x2801)起始,其值均为服务 UUID。

以下是一个典型服务层次示例:

// 主要服务声明:UUID = 0x180D (Heart Rate Service)
//  ├── 特征声明:UUID = 0x2A37 (Heart Rate Measurement)
//  │   ├── 特征值属性(value_handle = 0x0020)
//  │   └── CCCD描述符(UUID = 0x2902, handle = 0x0022)
//  └── 特征声明:UUID = 0x2A38 (Body Sensor Location)
//      └── 特征值属性(value_handle = 0x0024)

1.5 UUID 规范

Bluetooth SIG 定义了标准化 UUID 及自定义 128-bit UUID:

UUID 类型长度示例用途
16-bit UUID (SIG 标准)2 字节0x180D已发布服务/特征
32-bit UUID4 字节0x0000180D-0000-1000-8000-00805F9B34FB 的低4字节极少使用
128-bit UUID (自定义)16 字节E20A39F4-73F5-4BC4-A12F-17D1AD07A961私有服务/特征

自定义 128-bit UUID 的 Base UUID 为 00000000-0000-1000-8000-00805F9B34FB,将前32位替换即可。例如:

// 将自定义特征 UUID 注册到 GATT 数据库
static const ble_uuid128_t custom_char_uuid =
{
    .u = { .type = BLE_UUID_TYPE_128 },
    .value = { 0x61, 0xA9, 0x07, 0xAD, 0xD1, 0x17, 0x2F, 0xA1,
               0xC4, 0xBC, 0xF5, 0x73, 0xF4, 0x39, 0x0A, 0xE2 }
};
实现流程总结: GATT 特征模型的搭建步骤:
1) 分配属性句柄(按顺序递增);
2) 声明服务(Primary Service Declaration);
3) 在每个服务内添加特征声明及对应特征值属性;
4) 若需要通知/指示,添加 CCCD 描述符;
5) 填充权限、UUID、初始值。

1.6 GATT 层次结构图

GATT 服务与特征层次结构 GATT 服务与特征层次 Profile(应用) Primary Service (UUID 0x180D) Characteristic 0x2A37 (Heart Rate Meas.) Value (handle 0x20) CCCD (handle 0x22) Secondary Service (自定义 UUID) Characteristic 自定义 128-bit UUID Value (handle 0x30) 其他描述符 底层支持:属性协议(ATT) 每个属性由(句柄、类型UUID、权限、值)构成,GATT 通过 ATT 完成属性读写 UUID 规范:16-bit 标准 UUID(SIG) / 128-bit 自定义 UUID(Base UUID + 替换前32位)

1.7 ATT 与 GATT 的关系总结

层次职责典型 PDU
ATT传输属性值,管理句柄、权限Read Req/Rsp, Write Req/Cmd, Handle Value Ntf/Ind
GATT定义服务/特征/描述符的架构和交互流程Discover All Primary Services, Read Characteristic Value
调试建议: 使用 nRF Connect 或 LightBlue 等工具扫描设备后,查看 GATT 数据库树形结构,核对服务声明、特征声明、描述符的排列顺序和句柄值是否与代码中定义的数据库一致。