1. BLE GATT协议基础解析
在蓝牙低功耗(BLE)技术体系中,GATT(Generic Attribute Profile)协议扮演着数据交互框架的核心角色。作为BLE设备间通信的实际承载层,GATT定义了数据传输的结构化方式。理解GATT的层级模型,对于开发BLE应用或进行协议分析至关重要。
GATT采用分层架构设计,其核心元素包括:
- Profile:最高抽象层,代表完整的功能集合
- Service:实现特定功能的服务单元
- Characteristic:服务中的具体数据点
- Descriptor:特性的元数据描述
这些元素都以Attribute(属性)的形式存储在设备的Attribute表中。每个Attribute包含四个关键字段:
- Handle:16位唯一标识符(0x0001-0xFFFF)
- Type:标识属性类型的UUID
- Value:属性实际数据
- Permissions:访问控制规则(读/写/通知等)
关键提示:Handle相当于内存地址,Type指示数据含义,Permissions决定操作权限,这三个字段共同构成了GATT数据访问的基础。
2. 服务(Service)深度剖析
2.1 服务类型与定义
服务是GATT架构中的功能模块单元,分为两类核心类型:
-
Primary Service(主服务)
- 具备完整功能的服务实体
- 可被其他服务通过Include方式引用
- 通过Primary Service Discovery过程发现
- 示例:电池服务(0x180F)、设备信息服务(0x180A)
-
Secondary Service(辅助服务)
- 仅作为功能组件存在
- 必须被Primary Service或其他Secondary Service引用
- 典型应用场景:将通用功能模块化
服务定义(Service Definition)包含三个可能部分:
- 服务声明(Service Declaration):必须存在
- 包含定义(Include Definitions):0个或多个
- 特性定义(Characteristic Definitions):0个或多个
plaintext复制Service Definition结构示例:
[Service Declaration]
[Include Definition 1]
...
[Include Definition N]
[Characteristic Definition 1]
...
[Characteristic Definition M]
2.2 服务声明规范
服务声明作为服务的起点,其Attribute结构有严格规定:
| 字段 | 值要求 |
|---|---|
| Attribute Type | 0x2800(Primary)或0x2801(Secondary) |
| Attribute Value | 16位或128位服务UUID |
| Permissions | 只读 |
UUID分配策略:
- 16位UUID:由蓝牙技术联盟(SIG)标准化的服务
- 128位UUID:厂商自定义服务
- 设备上16位和128位UUID的服务会分组存放
开发注意:同一设备上允许存在多个相同UUID的服务实例,通过不同的Handle区分。服务定义的物理存储顺序与逻辑发现顺序无关。
3. 服务包含(Include)机制详解
3.1 Include定义解析
Include是一种服务组合技术,允许一个服务将另一个完整服务纳入自己的定义范围。其作用类似于C语言中的#include指令,实现服务功能的复用。
Include定义的Attribute结构:
| 字段 | 说明 |
|---|---|
| Type | 固定0x2802 |
| Value | 包含三部分: • 起始Handle • 结束Handle • 服务UUID(可选) |
| Permissions | 只读 |

3.2 Include使用场景
-
功能扩展:主服务通过包含辅助服务来增强功能
plaintext复制
示例:健康温度计服务(0x1809)包含设备信息服务(0x180A) -
模块化设计:将通用功能封装为可复用服务
plaintext复制
示例:多个服务共用的电池级别监控模块 -
协议兼容:支持不同版本服务的嵌套引用
包含规则约束:
- Primary Service可以包含其他Primary/Secondary Service
- Secondary Service通常仅被其他Secondary Service包含
- 禁止循环包含(A包含B,B又包含A)
实践建议:设计服务包含关系时,建议绘制服务依赖图,确保无循环引用。包含深度不宜超过3层,以免增加发现复杂度。
4. 特性(Characteristic)架构解析
4.1 特性组成要素
每个Characteristic由三个部分组成完整的定义:
-
特性声明(Characteristic Declaration)
- UUID:0x2803
- 结构:特性属性(1B) + 值句柄(2B) + 类型UUID(2/16B)
- 权限:只读
-
特性值声明(Characteristic Value Declaration)
- 紧接在声明之后
- 存储实际数据值
- 权限由特性属性决定
-
特性描述符(可选)
- 提供值的元数据信息
- 位于值声明之后
- 多种类型可选
4.2 特性属性详解
特性声明中的Properties字段(1字节)定义了所有可能的操作方式:
| 位掩码 | 十六进制 | 说明 |
|---|---|---|
| 0x01 | 0x01 | Broadcast |
| 0x02 | 0x02 | Read |
| 0x04 | 0x04 | Write Without Response |
| 0x08 | 0x08 | Write |
| 0x10 | 0x10 | Notify |
| 0x20 | 0x20 | Indicate |
| 0x40 | 0x40 | Authenticated Signed Writes |
| 0x80 | 0x80 | Extended Properties |
典型组合示例:
- 只读特性:0x02
- 可写可通知:0x12 (0x02|0x10)
- 全功能特性:0x1E (多种操作组合)
5. 特性描述符全解
5.1 核心描述符类型
-
客户端特性配置描述符(CCCD,UUID=0x2902)
- 必需条件:特性支持Notify/Indicate
- 值格式:16位掩码
- 0x0001:启用通知
- 0x0002:启用指示
- 0x0000:禁用通知/指示
关键细节:这是唯一可写的描述符,其他描述符通常为只读。一个特性最多只能有一个CCCD。
-
特性用户描述描述符(UUID=0x2901)
- 提供人类可读的说明文本
- UTF-8编码字符串
- 示例:"室内温度传感器-客厅北侧"
-
特性扩展属性描述符(UUID=0x2900)
- 定义附加属性
- 仅当特性声明中Extended Properties位为1时存在
- 值格式:
- 0x01:可靠写入
- 0x02:可写辅助项
5.2 数据格式描述符
-
特性表示格式描述符(UUID=0x2904)
cpp复制struct { uint8_t format; // 数据类型 int8_t exponent; // 十进制指数 uint16_t unit; // 计量单位 uint8_t namespace; // 命名空间 uint16_t description;// 描述ID } presentation_format;常见数据类型(format):
- 0x01:布尔值
- 0x04:uint8
- 0x06:uint16
- 0x08:uint32
- 0x0C:float32
-
特性聚合格式描述符(UUID=0x2905)
- 用于组合数据表示
- 包含多个表示格式描述符的句柄
- 示例应用:地理坐标(经度+纬度+海拔)
6. GATT属性类型全集
GATT规范定义了完整的属性类型UUID:
| UUID | 名称 | 说明 |
|---|---|---|
| 0x2800 | Primary Service | 主服务声明 |
| 0x2801 | Secondary Service | 辅助服务声明 |
| 0x2802 | Include | 服务包含声明 |
| 0x2803 | Characteristic | 特性声明 |
| 0x2900 | Characteristic Extended Properties | 扩展属性 |
| 0x2901 | Characteristic User Description | 用户描述 |
| 0x2902 | Client Characteristic Configuration | 客户端配置 |
| 0x2903 | Server Characteristic Configuration | 服务器配置 |
| 0x2904 | Characteristic Presentation Format | 表示格式 |
| 0x2905 | Characteristic Aggregate Format | 聚合格式 |
实际开发中,完整的属性发现流程应遵循以下步骤:
- 发现所有Primary Service
- 解析每个Service的Include关系
- 枚举Service下的所有Characteristic
- 读取每个Characteristic的描述符
- 根据描述符配置正确的数据交互方式
7. 实战案例分析:温度监测服务
7.1 服务定义
plaintext复制Handle Type Value Permission
------ ---------- ------------------------------ ---------
0x0010 0x2800 0x1809 (Health Thermometer) Read
0x0011 0x2803 [Props:0x12, Handle:0x0012, Type:0x2A1C] Read
0x0012 0x2A1C 23.5 (温度值) Read/Notify
0x0013 0x2902 [0x0001] (启用通知) Read/Write
0x0014 0x2901 "Room Temperature Sensor" Read
0x0015 0x2904 [0x08, -2, 0x272F, 1, 0] Read
7.2 关键点解析
-
温度值编码:
- 原始值:235 (0xEB)
- 表示格式:exponent = -2
- 实际值:235 × 10^-2 = 23.5°C
- 单位:0x272F(Bluetooth SIG定义的摄氏度单位)
-
通知配置:
- CCCD值设置为0x0001表示启用通知
- 当温度变化超过0.5°C时,设备会自动发送通知
-
服务发现流程:
- 通过0x1809 UUID发现温度服务
- 读取0x0011处特性声明
- 发现温度值位于0x0012
- 配置0x0013的CCCD启用通知
- 读取0x0015了解数据格式
性能优化建议:对于频繁更新的特性(如温度),建议使用Notification而非Indication,因为前者不需要接收方确认,可以减少通信延迟和功耗。