Files

29 KiB
Raw Permalink Blame History

蓝牙数据传输协议与 API 通信文档

本文档是固件与蓝牙客户端(微信小程序)之间 BLE 通信的唯一协议说明,包含 GATT 定义、二进制帧格式、命令清单、TLV 定义、结果码/状态码、主动推送、响应模型与客户端接入要求。

本文档以当前工程代码为准,对应源码: include/ble_tlv_protocol.hsrc/ble_tlv_protocol.cppsrc/radar_manager.cppsrc/tasks_manager.cppsrc/wifi_manager.cpp。 若代码与本文档不一致,以代码为准并同步更新本文档。


概述

本协议用于雷达设备与蓝牙客户端(微信小程序)之间的双向通信。设备作为 BLE GATT Server(外设),客户端作为 GATT Client(中心);所有业务数据统一封装为 TLV 二进制帧,通过 GATT 特征的写入与 Notify 收发。

┌──────────────┐   b1 写入(命令)        ┌────────────────┐
│              │ ──────────────────────▶ │                │
│  BLE 客户端  │   b2 通知(响应)        │    雷达设备    │
│  (小程序)  │ ◀────────────────────── │ (GATT Server)│
│              │   a1/a2/b3 通知(推送)  │                │
│              │ ◀────────────────────── │                │
└──────────────┘                         └────────────────┘

通信模型

方向 特征 说明
客户端 → 设备 b1Write 下发命令
设备 → 客户端 b2Notify 命令响应,按 seq 与请求匹配
设备 → 客户端 a1 / a2 / b3Notify 主动推送数据与状态,不做请求匹配

协议特点

  1. 统一帧格式 AA 55 | VER | CMD | SEQ | LEN | PAYLOAD | CRC,二进制紧凑,适合嵌入式设备。
  2. 命令与数据分离:命令走 b1/b2 一问一答,数据与状态走 a1/a2/b3 主动推送。
  3. 结果以 TLV_RESULT_CODE 判定,错误码/状态码的文案由客户端本地映射。
  4. 支持 Notify 分包与客户端重组(当前固定 20 字节一包)。
  5. 状态推送去重:仅在状态变化时经 b3 发送。
  6. 配网等耗时命令采用"先 PROCESSING、后最终结果"的异步响应模型。

当前实现参数

项目
协议版本 0x01
帧头 0xAA 0x55
校验 CRC16-CCITT(覆盖 VERSION ~ PAYLOAD,大端)
Notify 分包 固定 20 字节
命令接收缓冲 256 字节(超限返回 ERR_PROTO_FRAME_TOO_LARGE
默认连续推送间隔 500 ms(可通过 TLV_INTERVAL_MS 设置为 100~10000 ms

一、设计原则

  1. 客户端必须先按 GATT 特征 UUID 分流,再按 CMD 命令码解析。
  2. b1/b2 是命令请求/响应通道:客户端写 b1,设备通过 b2 Notify 响应。
  3. a1/a2/b3 是主动推送通道,不参与请求响应匹配。
  4. Notify 单包可能小于整帧,客户端必须按特征分别维护重组 buffer。
  5. 命令结果只以 TLV_RESULT_CODE 为事实来源,错误文案由客户端按错误码本地映射。
  6. 运行状态变化通过 b3 推送,与命令结果解耦。
  7. 协议层不再使用 flagsACKTLV_STATETLV_STEPTLV_MESSAGETLV_ERROR_MESSAGE
  8. READ 不作为主协议能力,客户端不依赖读特征获取业务数据(只有 a1/a2/b2/b3 为 Notifyb1 为 Write)。

二、GATT 服务与特征

2.1 Radar Data Service(雷达数据服务)

Service UUIDa8c1e5c0-3d5d-4a9d-8d5e-7c8b6a4e2f1a

别名 UUID 属性 方向 职责
a1 beb5483e-36e1-4688-b7f5-ea07361b26a1 NOTIFY 设备→客户端 连续雷达数据流推送
a2 beb5483e-36e1-4688-b7f5-ea07361b26a2 NOTIFY 设备→客户端 雷达状态主动推送

2.2 Device Config Service(设备配置服务)

Service UUIDa8c1e5c0-3d5d-4a9d-8d5e-7c8b6a4e2f1b

别名 UUID 属性 方向 职责
b1 beb5483e-36e1-4688-b7f5-ea07361b26b1 WRITE 客户端→设备 命令写入通道
b2 beb5483e-36e1-4688-b7f5-ea07361b26b2 NOTIFY 设备→客户端 命令响应通道
b3 beb5483e-36e1-4688-b7f5-ea07361b26b3 NOTIFY 设备→客户端 设备信息与状态主动推送

2.3 通道职责

通道 属性 匹配模式 说明
b1 Write 请求→响应 客户端发送命令
b2 Notify 请求→响应 设备返回命令响应,按 seq 匹配
a1 Notify 仅推送 连续雷达数据流,不做请求匹配
a2 Notify 仅推送 雷达状态推送,不做请求匹配
b3 Notify 仅推送 设备信息/状态推送,不做请求匹配

三、二进制帧格式

3.1 帧结构

┌──────┬──────┬───────┬─────┬─────┬───────┬───────┬─────────┬───────┬───────┐
│ SOF1 │ SOF2 │  VER  │ CMD │ SEQ │ LEN_H │ LEN_L │ PAYLOAD │ CRC_H │ CRC_L │
│ 1B   │ 1B   │  1B   │ 1B  │ 1B  │  1B   │  1B   │  N B    │  1B   │  1B   │
└──────┴──────┴───────┴─────┴─────┴───────┴───────┴─────────┴───────┴───────┘
字段 偏移 长度 说明
SOF1 0 1 帧头,固定 0xAA
SOF2 1 1 帧头,固定 0x55
VERSION 2 1 协议版本,当前 0x01
CMD 3 1 命令码
SEQ 4 1 命令请求/响应序列号;主动推送见下表
LEN 5-6 2 PAYLOAD 长度,大端
PAYLOAD 7 N TLV 编码数据区
CRC 7+N 2 CRC16-CCITT,大端
  • 最小帧长:9 字节(空 PAYLOAD)。
  • 所有多字节整数均为大端

SEQ 取值规则(与代码一致)

帧类型 SEQ
命令请求(b1)与响应(b2 请求的 seq,原样返回
a1 连续推送 设备侧自增(bleSequenceCounter++
a2 雷达状态推送 固定 0x00
b3 设备信息/状态推送 固定 0x00

客户端只在 b2 上按 seq 匹配请求响应;a1/a2/b3 不参与匹配,因此 a1 的自增 seq 仅作参考。

3.2 CRC16-CCITT

  • 算法:CRC16-CCITT(与代码 crc16Ccitt() 一致)
  • 多项式:0x1021
  • 初值:0xFFFF
  • 输入/输出反转:否
  • 最终异或:无
  • 计算范围:从 VERSIONPAYLOAD 末尾(不含 SOF1/SOF2CRC 本身)
  • 字节序:大端

3.3 TLV 编码

┌────────┬───────┬───────┬─────────┐
│  TYPE  │ LEN_H │ LEN_L │  VALUE  │
│  1B    │  1B   │  1B   │  N B    │
└────────┴───────┴───────┴─────────┘

LEN 为大端,VALUE 长度由 LEN 指定。

3.4 Notify 分包与重组

固件统一通过 sendFrameToBLE() 发送 Notify

  • 当前为固定 20 字节分片模式(BLE_FIXED_20_BYTE_MODE = 1),单包最大 20 字节。
  • 发送带互斥锁与流控(约 500 B/s,包间隔 ≥5ms)。
  • 仅当对应特征已被客户端订阅(CCCD 0x2902)且已连接时才发送。

客户端必须为每个 Notify 特征分别维护重组 buffer

通道 Buffer 说明
a1 a1Buffer 连续数据流重组
a2 a2Buffer 雷达状态重组
b2 b2Buffer 命令响应重组
b3 b3Buffer 设备信息/状态重组

四、命令码总览

命令 通道 说明
CMD_PING 0x01 b1/b2 Ping 请求/响应(可携带回显内容)
CMD_QUERY_STATUS 0x10 b1/b2 查询设备概览
CMD_QUERY_RADAR 0x12 b1/b2 查询雷达数据快照
CMD_START_CONTINUOUS 0x14 b1/b2 启动连续推送
CMD_STOP_CONTINUOUS 0x16 b1/b2 停止连续推送
CMD_RADAR_SLEEP_QUERY 0x17 b1/b2 雷达睡眠/综合状态查询开关
CMD_CONTINUOUS_PUSH 0x18 a1 连续雷达数据主动推送
CMD_DEVICE_INFO_PUSH 0x19 b3 设备信息/状态主动推送
CMD_RADAR_STATUS_PUSH 0x1A a2 雷达状态主动推送
CMD_WIFI_SCAN 0x20 b1/b2 WiFi 扫描
CMD_WIFI_CONFIG 0x22 b1/b2 WiFi 配网
CMD_GET_SAVED_WIFI 0x24 b1/b2 查询已保存 WiFi
CMD_DELETE_SAVED_WIFI 0x26 b1/b2 删除已保存 WiFi
CMD_LED_CONTROL 0x30 b1/b2 系统指示灯开关
CMD_ERROR_RESP 0x7E b2 协议层/入口级错误响应

五、命令详解(b1 写入 / b2 响应)

5.1 CMD_PING0x01

请求b1):

TLV 类型 必选 说明
TLV_ECHO_CONTENT string 回显内容,可携带任意字符串

响应b2):

TLV 类型 说明
TLV_RESULT_CODE uint8 SUCCESS
TLV_ECHO_CONTENT string 仅当请求携带内容时原样回显

5.2 CMD_QUERY_STATUS0x10

请求:无 PAYLOAD。

响应b2,按代码 buildStatusPayload() 的字段与顺序):

TLV 类型 必选 说明
TLV_RESULT_CODE uint8 结果码
TLV_DEVICE_SN uint64 设备序列号,仅 device_sn > 0 时发送
TLV_PROTOCOL_VERSION string 协议版本(当前 "1.0.0"
TLV_FIRMWARE_VERSION string 固件版本
TLV_DEVICE_TYPE string 设备类型(当前 "Radar"
TLV_MAC_ADDRESS string MAC 地址
TLV_WIFI_CONFIGURED uint8 是否保存过 WiFi0/1
TLV_WIFI_CONNECTED uint8 WiFi 是否已连接(0/1
TLV_IP_ADDRESS string IP 地址,仅 WiFi 已连接时发送
TLV_SSID string 当前 WiFi 名称,仅 WiFi 已连接时发送
TLV_WIFI_STATUS uint8 WiFi 状态,当前状态非 0 时发送
TLV_MQTT_STATUS uint8 MQTT 状态,当前状态非 0 时发送
TLV_RADAR_SLEEP_STATUS uint8 雷达睡眠查询状态,当前状态非 0 时发送
TLV_LED_ENABLED uint8 系统指示灯开关(0/1),始终发送

5.3 CMD_QUERY_RADAR0x12

请求:无 PAYLOAD。

响应b2,按代码 processQueryRadarData()):

TLV 类型 说明
TLV_RESULT_CODE uint8 结果码
TLV_TIMESTAMP uint32 设备运行时间戳(millis(),单位 ms
TLV_PRESENCE uint8 是否有人(0=无人,1=有人)
TLV_HEART_RATE_X10 uint16 心率×10
TLV_BREATH_RATE_X10 uint16 呼吸率×10
TLV_MOTION uint8 运动状态
TLV_DISTANCE_CM uint16 距离(cm
TLV_POS_X_MM int16 X 坐标(mm
TLV_POS_Y_MM int16 Y 坐标(mm
TLV_POS_Z_MM int16 Z 坐标(mm
TLV_BODY_MOVEMENT uint8 体动

快照即使无人也返回,字段为 0 属合法,客户端自行判断有效性。


5.4 CMD_START_CONTINUOUS0x14

请求b1):

TLV 类型 必选 说明
TLV_INTERVAL_MS uint16 推送间隔,有效范围 100~10000 ms

响应b2):

TLV 类型 说明
TLV_RESULT_CODE uint8 结果码
TLV_INTERVAL_MS uint16 成功时回传实际生效的间隔

错误:缺少参数 → ERR_PROTO_PARAM_MISSING;超出范围 → ERR_PROTO_PARAM_INVALID

效果:启动后设备通过 a1 推送 CMD_CONTINUOUS_PUSH,并通过 a2 每 200ms 推送 CMD_RADAR_STATUS_PUSH


5.5 CMD_STOP_CONTINUOUS0x16

请求:无 PAYLOAD。

响应b2):TLV_RESULT_CODE = SUCCESS

说明:幂等操作,无论当前是否在推送都返回成功;BLE 断开时设备自动停止推送,重连后需重新下发 CMD_START_CONTINUOUS


5.6 CMD_RADAR_SLEEP_QUERY0x17

请求b1):

TLV 类型 必选 说明
TLV_RADAR_SLEEP_ENABLED uint8 0=关闭,1=开启

响应b2):

TLV 类型 说明
TLV_RESULT_CODE uint8 结果码
TLV_RADAR_SLEEP_ENABLED uint8 当前开关状态

错误:缺失 → ERR_PROTO_PARAM_MISSING;非 0/1 → ERR_PROTO_PARAM_INVALID

推送:开关状态变化时经 b3 推送 RADAR_SLEEP_QUERY_ENABLED (0x31) / RADAR_SLEEP_QUERY_DISABLED (0x30)


5.7 CMD_LED_CONTROL0x30

请求b1):

TLV 类型 必选 说明
TLV_LED_ENABLED uint8 0=关闭,1=开启

响应b2):

TLV 类型 说明
TLV_RESULT_CODE uint8 结果码
TLV_LED_ENABLED uint8 当前开关状态

错误:缺失 → ERR_PROTO_PARAM_MISSING;非 0/1 → ERR_PROTO_PARAM_INVALID

持久化与推送:开关状态保存到 FlashPreferences 命名空间 radar_data,键 ledEnabled),断电保持;状态变化时经 b3 推送 LED_ENABLED (0x33) / LED_DISABLED (0x32)


5.8 CMD_WIFI_SCAN0x20

请求:无 PAYLOAD。

异步响应b2):

第一阶段(立即):

TLV 类型 说明
TLV_RESULT_CODE uint8 PROCESSING (0x01)

第二阶段(扫描完成):

TLV 类型 必选 说明
TLV_RESULT_CODE uint8 结果码
TLV_WIFI_COUNT uint16 WiFi 数量,成功且非空时发送
TLV_WIFI_ITEM block WiFi 条目,可重复多个

每个 TLV_WIFI_ITEM 内为独立 TLV

TLV 类型 说明
TLV_SSID string WiFi 名称
TLV_RSSI int8 信号强度(dBm
TLV_SECURITY uint8 加密类型,见第十节

错误:启动扫描失败 → ERR_WIFI_BUSY;超时 → ERR_WIFI_SCAN_TIMEOUT


5.9 CMD_WIFI_CONFIG0x22

请求b1):

TLV 类型 必选 说明
TLV_SSID string WiFi 名称
TLV_PASSWORD string WiFi 密码

异步响应b2):

第一阶段(立即):

TLV 类型 说明
TLV_RESULT_CODE uint8 PROCESSING (0x01)
TLV_SSID string 正在配置的 SSID(回显)

第二阶段(完成):

TLV 类型 必选 说明
TLV_RESULT_CODE uint8 结果码
TLV_SSID string 成功时发送
TLV_IP_ADDRESS string 成功时发送

错误SSID 为空 → ERR_PROTO_PARAM_MISSING;其他常见错误 ERR_WIFI_BUSY (0x26)ERR_WIFI_SSID_NOT_FOUND (0x21)ERR_WIFI_SIGNAL_WEAK (0x25)ERR_WIFI_WRONG_PASSWORD (0x22)ERR_WIFI_SCAN_TIMEOUT (0x20)ERR_DEV_STORAGE_FAIL (0x41)


5.10 CMD_GET_SAVED_WIFI0x24

请求:无 PAYLOAD。

响应b2,即时命令):

TLV 类型 必选 说明
TLV_RESULT_CODE uint8 SUCCESSERR_DEV_STORAGE_FAIL
TLV_WIFI_COUNT uint16 成功时发送
TLV_WIFI_ITEM block 成功时发送,可重复多个

每个 TLV_WIFI_ITEM 内为独立 TLV

TLV 类型 说明
TLV_SSID string WiFi 名称
TLV_PASSWORD string WiFi 密码

注意:已保存网络回包中包含密码,仅用于本地配网管理。


5.11 CMD_DELETE_SAVED_WIFI0x26

请求b1):

TLV 类型 必选 说明
TLV_SSID string 要删除的 WiFi 名称

响应b2,成功时):

TLV 类型 说明
TLV_RESULT_CODE uint8 结果码
TLV_SSID string 被删除的 SSID
TLV_WIFI_COUNT uint16 删除后剩余数量

错误SSID 为空 → ERR_PROTO_PARAM_MISSING;网络不存在 → ERR_WIFI_SSID_NOT_FOUND;删除失败 → ERR_DEV_STORAGE_FAIL


5.12 CMD_ERROR_RESP0x7E

用于协议层/入口级、无法归属到具体业务命令的错误(未知命令、帧过大、队列满、队列未初始化等),设备通过 b2 返回:

TLV 类型 说明
TLV_RESULT_CODE uint8 错误码

六、主动推送(a1 / a2 / b3)

6.1 a1 — CMD_CONTINUOUS_PUSH0x18)连续雷达数据

  • 触发CMD_START_CONTINUOUS 成功后,按 TLV_INTERVAL_MS 间隔推送(默认 500ms),且仅在数据发生变化时发送。
  • cmd = 0x18seq = 设备侧自增
  • TLV
TLV 类型 说明
TLV_PRESENCE uint8 是否有人(0/1
TLV_HEART_RATE_X10 uint16 心率×10
TLV_BREATH_RATE_X10 uint16 呼吸率×10
TLV_MOTION uint8 运动状态

无人时上述字段均发 0。TLV_SLEEP_STATETLV_HEART_WAVEFORMTLV_BREATH_WAVEFORM 当前代码未发送(已注释),客户端不应依赖。

6.2 a2 — CMD_RADAR_STATUS_PUSH0x1A)雷达状态

  • 触发CMD_START_CONTINUOUS 成功后,每 200ms 推送。
  • cmd = 0x1Aseq = 0x00
  • TLV
TLV 类型 说明
TLV_DISTANCE_CM uint16 距离(cm
TLV_POS_X_MM int16 X 坐标(mm
TLV_POS_Y_MM int16 Y 坐标(mm
TLV_POS_Z_MM int16 Z 坐标(mm
TLV_BODY_MOVEMENT uint8 体动

6.3 b3 — CMD_DEVICE_INFO_PUSH0x19)设备信息与状态

  • 触发:BLE 连接建立时同步当前状态;WiFi/MQTT/雷达睡眠查询/指示灯状态变化时推送(去重,仅变化时发送)。
  • cmd = 0x19seq = 0x00

状态推送 TLV

TLV 类型 说明
TLV_DEVICE_STATUS uint8 设备状态码,见第九节

设备信息推送 TLV

TLV 类型 说明
TLV_RESULT_CODE uint8 结果码
TLV_PROTOCOL_VERSION string 协议版本
TLV_FIRMWARE_VERSION string 固件版本
TLV_DEVICE_TYPE string 设备类型
TLV_MAC_ADDRESS string MAC 地址
TLV_DEVICE_SN uint64 设备序列号,仅存在时发送

七、TLV 类型码

7.1 设备信息(0x01-0x0F

常量 类型 说明
TLV_RESULT_CODE 0x02 uint8 结果码
TLV_TIMESTAMP 0x04 uint32 时间戳
TLV_PROTOCOL_VERSION 0x05 string 协议版本
TLV_DEVICE_SN 0x06 uint64 设备序列号,仅存在时发送
TLV_FIRMWARE_VERSION 0x07 string 固件版本
TLV_DEVICE_TYPE 0x08 string 设备类型
TLV_MAC_ADDRESS 0x09 string MAC 地址

7.2 雷达数据(0x10-0x1F

常量 类型 说明
TLV_HEART_RATE_X10 0x10 uint16 心率×10720 = 72.0 bpm
TLV_BREATH_RATE_X10 0x11 uint16 呼吸率×10180 = 18.0 次/分)
TLV_PRESENCE 0x12 uint8 存在检测(0=无人,1=有人)
TLV_MOTION 0x13 uint8 运动状态
TLV_SLEEP_STATE 0x14 uint8 睡眠状态
TLV_DISTANCE_CM 0x15 uint16 距离(cm
TLV_POS_X_MM 0x16 int16 X 坐标(mm
TLV_POS_Y_MM 0x17 int16 Y 坐标(mm
TLV_POS_Z_MM 0x18 int16 Z 坐标(mm
TLV_BODY_MOVEMENT 0x19 uint8 体动

7.3 WiFi0x20-0x2F

常量 类型 说明
TLV_SSID 0x20 string WiFi 名称
TLV_PASSWORD 0x21 string WiFi 密码
TLV_WIFI_COUNT 0x22 uint16 WiFi 数量
TLV_WIFI_ITEM 0x23 block WiFi 条目(嵌套 TLV
TLV_RSSI 0x24 int8 信号强度(dBm
TLV_SECURITY 0x25 uint8 加密类型,见第十节

7.4 控制与状态(0x30-0x3F

常量 类型 说明
TLV_INTERVAL_MS 0x31 uint16 推送间隔(ms
TLV_RADAR_SLEEP_ENABLED 0x32 uint8 雷达睡眠查询开关(0=关,1=开)
TLV_DEVICE_STATUS 0x33 uint8 设备状态码(b3 推送)
TLV_WIFI_STATUS 0x34 uint8 WiFi 状态
TLV_MQTT_STATUS 0x35 uint8 MQTT 状态
TLV_RADAR_SLEEP_STATUS 0x36 uint8 雷达睡眠查询状态
TLV_LED_ENABLED 0x37 uint8 系统指示灯开关(0=关,1=开)

7.5 通用消息(0x40-0x4F

常量 类型 说明
TLV_IP_ADDRESS 0x41 string IP 地址
TLV_WIFI_CONFIGURED 0x42 uint8 是否保存过 WiFi0/1
TLV_WIFI_CONNECTED 0x43 uint8 WiFi 是否已连接(0/1
TLV_ECHO_CONTENT 0x44 string 回显内容

7.6 波形(0x60-0x6F

常量 类型 说明
TLV_HEART_WAVEFORM 0x60 uint8 心跳波形(原始 int8 + 128
TLV_BREATH_WAVEFORM 0x61 uint8 呼吸波形(原始 int8 + 128

八、结果码

常量 说明
SUCCESS 0x00 成功
PROCESSING 0x01 命令已接收,处理中
ERR_PROTO_CMD_UNKNOWN 0x13 未知命令
ERR_PROTO_PARAM_MISSING 0x14 参数缺失
ERR_PROTO_PARAM_INVALID 0x15 参数非法
ERR_PROTO_BUSY 0x16 设备忙
ERR_PROTO_FRAME_TOO_LARGE 0x18 请求帧过大(超过 256 字节)
ERR_WIFI_SCAN_TIMEOUT 0x20 WiFi 扫描超时
ERR_WIFI_SSID_NOT_FOUND 0x21 未找到 SSID
ERR_WIFI_WRONG_PASSWORD 0x22 WiFi 密码错误
ERR_WIFI_SIGNAL_WEAK 0x25 WiFi 信号弱
ERR_WIFI_BUSY 0x26 WiFi 忙
ERR_DEV_STATE_INVALID 0x40 当前设备状态不允许
ERR_DEV_STORAGE_FAIL 0x41 存储失败
ERR_DEV_QUEUE_FULL 0x42 命令队列已满

九、设备状态码

TLV_DEVICE_STATUS / TLV_WIFI_STATUS / TLV_MQTT_STATUS / TLV_RADAR_SLEEP_STATUS 共用同一组状态码。

9.1 WiFi 状态

常量 说明
WIFI_DISCONNECTED 0x10 WiFi 断开
WIFI_CONNECTING 0x11 WiFi 连接中
WIFI_CONNECTED 0x12 WiFi 已连接
WIFI_FAILED 0x13 WiFi 连接失败

9.2 MQTT 状态

常量 说明
DEV_MQTT_DISCONNECTED 0x20 MQTT 断开
DEV_MQTT_CONNECTING 0x21 MQTT 连接中
DEV_MQTT_CONNECTED 0x22 MQTT 已连接
DEV_MQTT_FAILED 0x23 MQTT 连接失败

9.3 雷达睡眠查询状态

常量 说明
RADAR_SLEEP_QUERY_DISABLED 0x30 雷达睡眠/综合状态查询已关闭
RADAR_SLEEP_QUERY_ENABLED 0x31 雷达睡眠/综合状态查询已开启

9.4 系统指示灯状态

常量 说明
LED_DISABLED 0x32 系统指示灯已关闭
LED_ENABLED 0x33 系统指示灯已开启

十、WiFi 安全类型

常量 说明
WIFI_SEC_OPEN 0 开放网络
WIFI_SEC_WEP 1 WEP 加密
WIFI_SEC_WPA 2 WPA 加密
WIFI_SEC_WPA2 3 WPA2 加密
WIFI_SEC_WPA3 4 WPA3 加密
WIFI_SEC_UNKNOWN 255 未知类型

十一、响应模型

11.1 同步命令

客户端写 b1 → 设备从 b2 通知响应

响应帧:

cmd = 原命令码
seq = 请求 seq
payload = TLV_RESULT_CODE + 业务数据 TLV

失败时同样使用原命令码响应,TLV_RESULT_CODE 为对应错误码;无法归属具体业务命令的入口级错误使用 CMD_ERROR_RESP (0x7E)

11.2 异步长命令

适用:CMD_WIFI_SCANCMD_WIFI_CONFIG

阶段一(立即): cmd=原命令码, seq=原seq, TLV_RESULT_CODE = PROCESSING (0x01)
阶段二(完成): cmd=原命令码, seq=原seq, TLV_RESULT_CODE = SUCCESS / ERR_XXX + 业务 TLV

客户端收到 PROCESSING不要清除 pending 请求,继续等待最终结果。


十二、客户端接入要求

12.1 基本原则

  1. 先按特征 UUID 分流,再按 cmd 解析。
  2. 每个 Notify 特征维护独立重组 buffer。
  3. 只在 b2 上按 seq 匹配请求响应。
  4. a1/a2/b3 不做请求响应匹配。
  5. 所有结果判断以 TLV_RESULT_CODE 为准,文案本地映射。

12.2 伪代码

const buffers = { a1: new Uint8Array(0), a2: new Uint8Array(0), b2: new Uint8Array(0), b3: new Uint8Array(0) }

function onNotify(characteristicId, chunk) {
  const channel = mapCharacteristicToChannel(characteristicId)
  buffers[channel] = concatBytes(buffers[channel], chunk)
  while (true) {
    const frame = tryExtractFrame(buffers[channel])
    if (!frame) break
    buffers[channel] = frame.remaining
    if (!verifyCrc(frame.bytes)) continue
    dispatchByChannel(channel, frame.decoded)
  }
}

const pending = new Map()

function sendCommand(cmd, payload) {
  const seq = nextSeq()
  pending.set(seq, { cmd })
  return writeB1(encodeFrame({ cmd, seq, payload }))
}

function handleCommandResponse(frame) {
  const req = pending.get(frame.seq)
  if (!req) return
  const resultCode = getTlvU8(frame.payload, TLV_RESULT_CODE)
  if (resultCode !== PROCESSING) pending.delete(frame.seq)
  routeCommandResult(req.cmd, resultCode, frame.payload)
}

function tryExtractFrame(buffer) {
  const sofIndex = findSOF(buffer)
  if (sofIndex < 0) return null
  const headerLen = 7
  if (buffer.length < sofIndex + headerLen) return null
  const payloadLen = (buffer[sofIndex + 5] << 8) | buffer[sofIndex + 6]
  const totalLen = headerLen + payloadLen + 2
  if (buffer.length < sofIndex + totalLen) return null
  return {
    bytes: buffer.slice(sofIndex, sofIndex + totalLen),
    remaining: buffer.slice(sofIndex + totalLen),
  }
}

12.3 典型交互流程

连接初始化

1. 连接 BLE 设备
2. 订阅 b2、b3、a1、a2 的 Notify(未订阅时设备不会发送)
3. 等待 b3 推送设备信息 / 发送 CMD_QUERY_STATUS 获取完整状态
4. 按需发送 CMD_START_CONTINUOUS

WiFi 配网

1. 发送 CMD_WIFI_SCAN → 收到 PROCESSING → 收到最终结果,展示列表
2. 用户选择 SSID 并输入密码
3. 发送 CMD_WIFI_CONFIG → 收到 PROCESSING → 收到 SUCCESS(含 IP
4. b3 推送 WIFI_CONNECTED

雷达数据监控

1. 发送 CMD_START_CONTINUOUSinterval=500ms
2. a1 持续推送 CMD_CONTINUOUS_PUSH
3. a2 每 200ms 推送 CMD_RADAR_STATUS_PUSH
4. 发送 CMD_STOP_CONTINUOUS 停止

十三、数据解析注意事项

  1. 心率/呼吸率TLV_HEART_RATE_X10TLV_BREATH_RATE_X10 为 uint16,实际值 = 值 / 10.0。
  2. 波形TLV_HEART_WAVEFORMTLV_BREATH_WAVEFORM 原始为 int8,线上为 原始值 + 128,解析时减 128。(当前 a1 未发送波形。)
  3. 坐标TLV_POS_X_MM/Y/Z 为 int16,单位 mm;米 = 值 / 1000.0。
  4. 设备序列号TLV_DEVICE_SN 固定 uint64,无 SN 时不发送,客户端不应依赖。
  5. 密码回包CMD_GET_SAVED_WIFITLV_WIFI_ITEMTLV_PASSWORD,仅用于本地配网管理。
  6. 时间戳TLV_TIMESTAMP 为设备 millis(),非绝对时间。

十四、固件实现约束

  1. 所有业务 Notify 必须通过 sendFrameToBLE() 发送,不允许直接 setValue(整帧) + notify()
  2. a1/a2/b2/b3 均为 NOTIFYb1 为 WRITE。
  3. 命令响应 seq 必须与请求一致;主动推送 a1 自增、a2/b30
  4. b1 单次写入超过 256 字节时返回 ERR_PROTO_FRAME_TOO_LARGE;命令队列(10 深度)满时返回 ERR_DEV_QUEUE_FULL
  5. 仅当客户端订阅了对应特征的 Notify 时才发送,避免无效通知。
  6. 所有多字节字段与 CRC 均为大端。
  7. 修改协议时需同步更新本文档(新增命令/字段须保持向后兼容)。

十五、版本历史

版本 日期 说明
1.0 2026-06 初始版本,纯 TLV 协议,移除 legacy JSON 兼容
2.0 2026-09 合并 ble_protocol.mdble_api_communication.md;以代码为准补齐 CMD_LED_CONTROL (0x30)TLV_LED_ENABLED (0x37)、LED 状态码、CMD_QUERY_RADARTLV_TIMESTAMPCMD_QUERY_STATUSTLV_SSID/TLV_LED_ENABLED、已保存 WiFi 含密码、删除 WiFi 回包字段,并修正 SEQ 语义