4. GATT服务与医疗特征设计

GATT(Generic Attribute Profile)是BLE设备对外暴露数据的核心协议层。在医疗可穿戴场景中,设备通常同时承担两类角色:一类是遵循蓝牙SIG标准定义的医疗健康服务,另一类是厂商为特定算法或私有数据自定义的服务。理解这两类服务的组织方式、特征属性与数据打包规则,是设备互联互通的基础。

服务与特征的层级结构

GATT以属性表(Attribute Table)的形式组织数据,每个属性由句柄(Handle)、类型(UUID)、权限(Permissions)和值(Value)四要素构成。服务(Service)是一组相关特征的容器,特征(Characteristic)是实际承载数据的单元。一个特征至少包含两个属性:特征声明(Characteristic Declaration)和特征值(Characteristic Value);若支持通知或指示,还需附加客户端特征配置描述符(CCCD,UUID 0x2902)。

服务分为主服务(Primary Service)和次要服务(Secondary Service)。医疗设备通常将HRS、BLP等标准服务声明为主服务,供中央设备直接发现;而将一些辅助配置信息放入次要服务,由主服务引用。服务UUID可以是16位标准UUID,也可以是128位自定义UUID。标准医疗服务的16位UUID由蓝牙SIG分配,例如心率服务为0x180D,血压服务为0x1810,健康温度计服务为0x1809,脉搏血氧服务为0x1822。

GATT属性表结构:标准医疗服务与自定义服务并存 Attribute Table(按句柄递增) Handle Type (UUID) Permissions Value / 说明 0x0001 0x2800 (Primary Service) Read 0x180D — 心率服务 HRS 0x0002 0x2803 (Characteristic) Read Notify — 心率测量 0x0003 0x2A37 (Heart Rate Measurement) Notify Flags + HR值(1或2字节) 0x0004 0x2902 (CCCD) Read/Write 0x0001=Notify使能 0x0005 0x2800 (Primary Service) Read 0x1810 — 血压服务 BLP 0x0006 0x2A35 (Blood Pressure Measurement) Indicate Flags + 收缩压/舒张压/脉搏 0x0010 0x2800 (Primary Service) Read 128位自定义UUID — 运动伪影补偿服务 0x0011 128位自定义UUID Read/Write/Notify 补偿参数 + 置信度 0x0012 0x2902 (CCCD) Read/Write 0x0002=Indicate使能 注:句柄仅为示意,实际由协议栈按属性添加顺序分配;标准服务与自定义服务可交错排列。

标准医疗健康服务要点

HRS(心率服务,0x180D)的核心特征是心率测量(0x2A37)。其数据格式由第一个字节的Flags决定:bit0为0表示心率值为uint8,为1表示uint16;bit1~bit2表示传感器接触状态;bit3表示能量消耗是否存在;bit4表示RR间期是否存在。例如Flags=0x06时,表示心率值为uint8、传感器接触良好、无能量消耗和RR间期,后续1字节即为心率值。

BLP(血压服务,0x1810)的血压测量特征(0x2A35)采用Indicate方式传输,确保中央设备确认收到。Flags字节的bit0表示单位(0=mmHg,1=kPa),bit1表示是否有脉搏率,bit2表示是否有用户ID,bit3表示是否有测量状态。收缩压、舒张压、平均动脉压各占2字节(SFLOAT格式),脉搏率占2字节。

HTS(健康温度计服务,0x1809)的温度测量特征(0x2A1C)使用IEEE-11073 32位FLOAT格式,包含Flags、温度值和时间戳。温度值以0.1℃为单位,例如375表示37.5℃。

PLX(脉搏血氧服务,0x1822)的PLX连续测量特征(0x2A5F)包含SpO2和脉搏率,采用SFLOAT格式,同时可携带测量状态和设备状态标志。

特征属性与权限设计

特征的属性(Properties)决定中央设备能执行的操作,权限(Permissions)决定协议栈层面的访问控制。常见属性包括:Read、Write、Write Without Response、Notify、Indicate。医疗数据通常采用Notify或Indicate上报:Notify无需确认,适合高频心率数据;Indicate需要确认,适合血压等关键测量结果。

权限设置需与属性匹配:若特征支持Notify,则CCCD必须可读写;若特征值只读,则权限设为Read;若允许中央设备写入配置参数,则设为Read/Write。注意,权限是协议栈强制执行的安全边界,而属性是应用层的能力声明,两者不可混淆。

医疗数据字段的打包与解析

BLE特征值本质是字节数组,医疗数据的打包需遵循“小端序、紧凑排列、Flags前置”的原则。以自定义运动伪影补偿特征为例,设计如下数据格式:

字节偏移  字段          类型      说明
0        Flags         uint8     bit0: 补偿使能; bit1: 置信度有效
1-2      补偿增益      uint16    小端序,单位0.001
3-4      补偿偏移      int16     小端序,单位0.01
5        置信度        uint8     0-100,百分比
6-7      时间戳        uint16    小端序,单位10ms

解析时,中央设备先读取Flags,再根据Flags决定后续字段是否存在。例如Flags=0x03时,全部字段有效;Flags=0x01时,置信度字段无效,应跳过。这种“Flags驱动”的解析方式与标准HRS、BLP一致,可保证向前兼容。

打包时务必使用小端序(Little-Endian),这是BLE GATT的默认字节序。若使用大端序,中央设备解析出的数值将完全错误。建议在代码中显式调用小端序转换函数,而非依赖编译器默认行为。

设计检查清单

  • 标准服务UUID是否使用蓝牙SIG分配的16位UUID,避免与自定义服务冲突?
  • Notify特征是否配置了CCCD,且CCCD权限为Read/Write?
  • Indicate特征是否在应用层实现了确认超时重传机制?
  • 多字节数值是否统一采用小端序打包?
  • Flags字段是否预留了足够的扩展位,以便后续增加字段而不破坏兼容性?
  • 自定义128位UUID是否基于厂商基础UUID生成,避免随机碰撞?

在实际工程中,建议先用nRF Connect或LightBlue等工具扫描设备,确认服务与特征的UUID、属性和权限是否符合预期,再编写中央设备解析代码。若发现特征值读取失败,优先检查权限是否设置为Read,以及CCCD是否已正确写入使能值。