Profile 实战

BLE Profile 实战:Heart Rate Service(0x180D)心率上报

发布于 2026-07-22 · 来自「嵌入式江湖」技术专栏

Heart Rate Service(HRS,0x180D)是 BLE 里最经典、结构最简单的"传感器持续上报一个值"的 Profile,健身手环、心率带、运动手表都在用。它非常适合作为理解 GATT Profile 组织方式的入门模板——一个 Notify 特性 + 几个辅助特性就搞定。

一、角色

  • Heart Rate Sensor(心率传感器):GATT Server、Peripheral。
  • Collector(手机/码表):GATT Client、Central。

二、HRS(0x180D)特性

特性 UUID 属性 作用
Heart Rate Measurement 0x2A37 Notify 心率值(核心)
Body Sensor Location 0x2A38 Read 佩戴位置(胸/腕/手指…)
Heart Rate Control Point 0x2A39 Write 目前只支持"清零累计能量"

三、Heart Rate Measurement(0x2A37,Notify)

一帧结构由首字节 Flags 决定:

  • Flags(1 字节)
  • bit0:心率值格式。0 = UINT8(1 字节),1 = UINT16(2 字节,小端)。
  • bit1:是否含 Energy Expended 字段(0 = 否,1 = 是)。
  • bit2~3:RR-Interval 个数(00 = 0 个,01 = 1 个,10 = 2 个,11 = 3 个…)。
  • bit4~7:保留。
  • Heart Rate Value:1 或 2 字节,取决于 bit0。单位 bpm。
  • Energy Expended(可选,UINT16,单位 kJ):若 bit1 = 1 才存在,从会话开始累计。
  • RR-Interval(可选,每个 UINT16):若 bit2~3 > 0 才跟在后面。单位是 1/1024 秒,表示两次 R 波(心跳)的时间间隔,用于心率变异性(HRV)分析。

解析时永远先看 Flags:心率值是 1 字节还是 2 字节、后面有没有能量和 RR,全由 Flags 决定,不能硬按固定长度读。

四、Body Sensor Location(0x2A38,Read)

告诉主机传感器戴在哪,枚举值:0 = Other,1 = Chest(胸口),2 = Wrist(手腕),3 = Finger,4 = Hand,5 = Ear Lobe,6 = Foot。App 据此显示"腕式心率"之类提示。可选,但建议实现。

五、Heart Rate Control Point(0x2A39,Write)

目前规范只定义一个操作:写入 0x01 = Reset Energy Expended,设备把累计能量清零。写其他值应被拒绝。没必要支持 Notify/Indicate。

六、典型流程

  1. 连接。
  2. (可选)读 Body Sensor Location(0x2A38)显示佩戴位置。
  3. 订阅 Heart Rate Measurement(0x2A37)的 Notify(开 CCCD 0x2902)。
  4. 持续收到心率帧,按 Flags 解析 bpm / 能量 / RR。
  5. 需要重置累计能量时,写 0x01 到 Control Point(0x2A39)。

七、常搭配的其他服务

  • Battery Service(0x180F):电量显示。
  • Device Information Service(0x180A):厂商/型号/版本。
  • 运动场景常和 Running Speed and Cadence(0x1814)Cycling Speed and Cadence(0x1816) 一起出现,组成完整运动档案。

八、实战坑提醒

  1. 心率值字节数看 Flags bit0:写死按 1 字节读会丢掉 >255 bpm 的高字节(虽少见但规范允许 UINT16)。
  2. RR-Interval 个数看 bit2~3,不要硬读固定数量,否则会越界或错位。
  3. RR 单位是 1/1024 秒,不是毫秒,换算要乘 1000/1024。
  4. Energy Expended 只在 bit1=1 时存在,且一旦支持就从连接起持续累加,只发一次带值、后续帧沿用。
  5. Control Point 只认 0x01,其余 opcode 应返回错误,别当通用命令通道。
  6. Notify 要开 CCCD(0x2902),否则收不到心率。
  7. UINT16 字段小端(Heart Rate 的 UINT16 形式、Energy、RR 都是),别读反。
  8. 首字节是 Flags 不是心率值,解析从第二字节才开始,新手常把 Flags 当成了 bpm。

一句话总结:HRS 是"一个 Notify 特性搞定主业"的典范——0x2A37 按 Flags 承载心率/能量/RR,0x2A38 报位置,0x2A39 只负责清零能量;吃透它的 Flags 驱动结构,就掌握了绝大多数"传感器类"Profile 的套路。

更多 BLE / 鸿蒙星闪 / 芯片选型 / MCU 实战,持续更新