1. GATT 协议基础与特征结构:服务 UUID、特征声明、描述符、属性权限
1.1 协议栈定位与层级关系
Bluetooth Low Energy(BLE)协议栈中,GATT(Generic Attribute Profile) 位于 HCI(Host Controller Interface) 之上的应用层,负责定义属性(Attribute)的发现、读取、写入和通知机制。GATT 协议并非独立存在,它依赖 ATT(Attribute Protocol) 进行数据包传输,而 GAP(Generic Access Profile) 则管理连接建立与广播。三层协作关系如图所示:
1.2 服务(Service)与特征(Characteristic)
GATT 的核心抽象是属性(Attribute),按层次组织为服务(Service)和特征(Characteristic)。每个服务由一个或多个特征组成,每个特征包含一个值以及零个或多个描述符。特征是实际数据传输的载体。
| 构件 | 16位 UUID 示例 | 作用 |
|---|---|---|
| 主服务 (Primary Service) | 0x180D (Heart Rate) | 声明一组相关功能 |
| 特征声明 (Characteristic Declaration) | 0x2803 | 特征元数据:属性句柄、UUID、权限 |
| 特征值 (Characteristic Value) | 0x2A37 (Heart Rate Measurement) | 存储应用程序数据 |
| 客户端特征配置描述符 (CCCD) | 0x2902 | 使能/关闭通知或指示 |
| 用户描述描述符 (User Description) | 0x2901 | 可读字符串,如“心率测量” |
1.3 特征声明结构详解
每个特征必须包含一个特征声明(UUID 0x2803),其数据格式固定为 5 字节(不带扩展型)或 7 字节(带扩展型)。常见 5 字节格式:
Byte 0-0: 特征属性 (Properties) — 如 0x0A (通知 + 读)
Byte 1-2: 特征值句柄 (Handle) — 指向特征值属性的句柄
Byte 3-4: 特征值 UUID (16位) 或 扩展型取前16位
1.4 属性权限(Attribute Permissions)
属性权限决定 GATT 客户端能否执行读、写、通知等操作,以及是否需要加密、认证或授权。权限由 GATT Server 端配置,在 ATT 层通过属性协议头中的 AuthSig 标志和加密链路共同实现。常用权限分为三类:
- 访问权限: 读 (read)、写 (write)、通知 (notify)、指示 (indicate)
- 加密要求: 无加密 (open)、加密连接 (encrypted)、带 MITM 保护 (authenticated)
- 授权要求: 需要应用层授权 (authorization)
在 Zephyr RTOS 中,属性权限通过 BT_GATT_PERM_READ、BT_GATT_PERM_WRITE 等宏组合定义。下面是一个典型的心率测量特征注册代码片段:
// Zephyr 示例:心率测量特征注册
BT_GATT_SERVICE_DEFINE(hrs_svc,
BT_GATT_PRIMARY_SERVICE(BT_UUID_HRS),
BT_GATT_CHARACTERISTIC(BT_UUID_HRS_MEASUREMENT,
BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_NONE,
NULL, NULL, NULL),
BT_GATT_CCC(hrs_ccc_cfg,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
);
属性权限位掩码(常见值):
| 权限宏 | 值 | 说明 |
|---|---|---|
| BT_GATT_PERM_READ | 0x001 | 允许不经加密读取 |
| BT_GATT_PERM_WRITE | 0x002 | 允许不经加密写入 |
| BT_GATT_PERM_READ_ENCRYPT | 0x004 | 需要加密链路读取 |
| BT_GATT_PERM_WRITE_ENCRYPT | 0x008 | 需要加密链路写入 |
| BT_GATT_PERM_READ_AUTHEN | 0x010 | 需要 MITM 保护读取 |
1.5 描述符(Descriptor)与 CCCD
描述符用于扩展特征的元数据。最关键的描述符是客户端特征配置描述符(CCCD,UUID 0x2902)。它允许 GATT 客户端通过写入 2 字节值来启用或禁用通知(Notification)或指示(Indication)。CCCD 值定义:
0x0000— 禁用通知/指示0x0001— 使能通知(Server 主动发送,无需确认)0x0002— 使能指示(Server 主动发送,需应用层确认)
BT_GATT_CCC() 宏并实现回调函数。1.6 实现流程概览
- 定义服务 UUID — 使用标准 16 位 UUID(如 0x180D)或自定义 128 位 UUID。
- 声明特征 — 使用
BT_GATT_CHARACTERISTIC宏,指定特征 UUID、属性、权限。 - 注册值访问回调 — 实现
read/write回调处理实际数据。 - 添加描述符 — 至少添加 CCCD,也可能添加用户描述或演示格式描述符。
- 设置属性权限 — 根据安全需求选择加密 / 认证级别。
- 调试验证 — 使用 BLE 抓包工具或 nRF Connect 读取服务特征列表,检查声明与值对齐。
1.7 调试与验证命令
在 Zephyr 环境下,可通过 Shell 命令遍历已注册的服务:
uart:~$ bt gatt-show-db
[0] Handle 0x0003: primary service (0x180D)
[1] Handle 0x0005: characteristic (0x2A37) props: 0x10 (Notify) value handle 0x0006
[2] Handle 0x0007: client characteristic config (0x2902) read write
在 Linux 上使用 gatttool 或 bluetoothctl 手动读写:
# 连接设备并列出特征
bluetoothctl
menu gatt
list-attributes // 显示所有服务与特征
select-attribute 0x0006 // 选择特征值句柄
read // 读取特征值
1.8 本章小结
掌握 GATT 属性层级是进行读写封装的基础。服务 UUID 标识一组功能,特征声明携带属性和值句柄,描述符提供配置与元数据,属性权限则决定安全边界。代码实现中,Zephyr 的宏定义提供了清晰的模型,但务必注意每个特征至少需要一个 CCCD 描述符才能支持通知/指示。下阶段将进入实际特征值的读写回调实现。