Appearance
数据包格式
本文档定义蓝牙协议 V2 的数据包结构。
包结构总览
每个数据包由 Header 和 Payload 两部分组成:
+----------------+------------------+
| Header | Payload |
| (固定长度) | (变长) |
+----------------+------------------+Header 结构
Header 采用固定长度设计,便于解析和分包处理。
+----------+----------+----------+----------+----------+----------+
| magic | version | flags | cmd_id | seq_id | length |
| 2 bytes | 1 byte | 1 byte | 2 bytes | 2 bytes | 2 bytes |
+----------+----------+----------+----------+----------+----------+总长度:10 字节
字段总览
| 字段 | 长度 | 说明 |
|---|---|---|
magic | 2 字节 | 魔数,固定为 0x5A5A |
version | 1 字节 | 协议版本号,当前为 0x02 |
flags | 1 字节 | 标志位 |
cmd_id | 2 字节 | 指令码,小端序 |
seq_id | 2 字节 | 序列号,小端序 |
length | 2 字节 | Payload 长度,小端序 |
Header 字段
魔数
字段名:magic
固定值 0x5A5A,用于识别协议包边界和过滤无效数据。
接收方应检查 magic 值,不匹配时丢弃该数据。
协议版本
字段名:version
协议版本号,用于兼容性判断:
| 值 | 版本 |
|---|---|
0x01 | V1 协议(已废弃) |
0x02 | V2 协议(当前) |
标志位
字段名:flags
+-----+-----+-----+-----+-----+-----+-----+-----+
| bit7| bit6| bit5| bit4| bit3| bit2| bit1| bit0|
+-----+-----+-----+-----+-----+-----+-----+-----+
| reserved | enc | format | dir |
+-----+-----+-----+-----+-----+-----+-----+-----+| 位 | 名称 | 说明 |
|---|---|---|
| bit0 | dir | 方向:0=请求,1=响应 |
| bit1-2 | format | 序列化格式 |
| bit3 | enc | 加密标志:0=明文,1=加密 |
| bit4-7 | reserved | 保留,必须为 0 |
format 序列化格式:
| 值 | 格式 |
|---|---|
| 0 | Protobuf |
| 1 | JSON |
| 2 | 原始字节 |
| 3 | 保留 |
指令码
字段名:cmd_id
指令码,标识本次通信的操作类型。采用分块编号,高字节表示模块,低字节表示具体操作。
详见 指令表。
序列号
字段名:seq_id
用于匹配请求和响应:
- 请求方生成唯一序列号
- 响应方原样返回该序列号
- 取值范围 0-65535,循环使用
载荷长度
字段名:length
Payload 部分的字节长度,不包含 Header。
- 最大值受 MTU 限制
- length=0 表示无 Payload(如心跳包)
Payload 载荷
Payload 为业务数据,根据 flags.format 字段选择序列化方式:
| format | 内容 |
|---|---|
| Protobuf | Protobuf 二进制编码 |
| JSON | UTF-8 编码的 JSON 字符串 |
| 原始字节 | 按消息定义的字节序列 |
详细序列化规范参见 序列化规范。
加密
当 flags.enc = 1 时,Payload 部分需要加密:
+----------------+---------------------------+
| Header | Encrypted Payload |
| (明文) | (AES-CCM 加密) |
+----------------+---------------------------+- Header 始终为明文,便于解析
- 仅 Payload 部分加密
- 加密算法和密钥管理参见加密规范(待补充)
分包传输
当数据长度超过蓝牙 MTU 时,需要分包传输。
分包扩展字段
分包时在标准 Header 后追加分包信息:
+----------------+----------+----------+------------------+
| 标准 Header | total | index | Payload |
| 10 bytes | 1 byte | 1 byte | |
+----------------+----------+----------+------------------+| 字段 | 长度 | 说明 |
|---|---|---|
total | 1 字节 | 总分包数 |
index | 1 字节 | 当前分包索引(从 0 开始) |
分包规则
- 第一个分包包含完整 Header + 分包信息
- 后续分包仅包含分包信息 + Payload 片段
- 接收方按
seq_id和index组装 - 超时未收齐则丢弃
分包示例
发送 500 字节数据,MTU=200:
分包0: [Header:10B][total:1][index:0][Payload:187B] = 199B
分包1: [total:1][index:1][Payload:198B] = 199B
分包2: [total:1][index:2][Payload:115B] = 116B完整示例
请求包示例
获取设备信息请求(无 Payload):
Header:
magic: 5A 5A
version: 02
flags: 00 (请求, Protobuf, 明文)
cmd_id: 00 01 (0x0100 GetDeviceInfo, 小端序)
seq_id: 01 00 (序列号 1)
length: 00 00 (无 Payload)
完整: 5A 5A 02 00 00 01 01 00 00 00 (10 字节)响应包示例
获取设备信息响应:
Header:
magic: 5A 5A
version: 02
flags: 01 (响应, Protobuf, 明文)
cmd_id: 00 01 (0x0100 GetDeviceInfo)
seq_id: 01 00 (匹配请求序列号)
length: 20 00 (Payload 32 字节)
Payload:
[32 字节 Protobuf 编码的 DeviceInfo]
完整: 5A 5A 02 01 00 01 01 00 20 00 [Payload...] (10 + 32 = 42 字节)错误处理
无效包处理
| 情况 | 处理方式 |
|---|---|
| magic 不匹配 | 丢弃,继续扫描 |
| version 不支持 | 返回错误响应 |
| cmd_id 未知 | 返回错误响应 |
| length 超限 | 返回错误响应 |
| 解密失败 | 丢弃,不响应 |
| 反序列化失败 | 返回错误响应 |
超时处理
- 请求超时:5 秒无响应,可重试
- 分包超时:2 秒未收齐,丢弃已收分包
