数据包格式
本文档定义蓝牙通信协议中的数据包格式、接口分类、加密方式等核心规范。
接口分类
蓝牙通信协议定义了三类接口,分别用于不同的通信场景:
| 接口类型 | 功能说明 | 使用场景 |
|---|---|---|
| A接口 | 开锁记录及报警信息向云平台汇报 | 设备→云平台 |
| B接口 | 固件升级、开锁账号下发指令 | 云平台→设备 |
| C接口 | 手机APP与智能门锁的交互信令 | APP/网关↔设备 |
主要使用接口
大部分开发场景使用C接口进行APP与设备的交互。
数据包结构
字段定义
通信数据包由以下字段组成:
| 字段名称 | 长度 | 说明 |
|---|---|---|
| 包头 | 4字节 | 固定标识,用于识别数据包起始位置 |
| 包类型 | 1字节 | 区分请求包和应答包 |
| 包序号 | 2字节 | 数据包序列号,用于匹配请求和应答 |
| 包标识 | 1字节 | 包版本和加密类型 |
| 数据长度 | 4字节 | 数据块长度信息 |
| 数据块 | 变长 | 实际业务数据内容 |
| 校验位 | 2字节 | 数据完整性校验 |
数据包示意图
┌──────────────────────────────────────────────────────────────┐
│ 包头 │包类型│ 包序号 │包标识│ 数据长度 │ 数据块 │校验位│
│ 4 Bytes │1 Byte│2 Bytes │1 Byte│ 4 Bytes │ 变长 │2 Bytes│
└──────────────────────────────────────────────────────────────┘字段详细说明
1. 包头 (Header)
- 长度: 4字节
- 固定值:
0xEF01EE02 - 字节序: 大端序(Big-Endian)
- 用途: 标识数据包起始位置,用于数据流同步
c
// C语言示例
#define PACKET_HEADER 0xEF01EE02
uint8_t header[4] = {0xEF, 0x01, 0xEE, 0x02};2. 包类型 (Packet Type)
- 长度: 1字节
- 取值:
0x01- 请求包(Request)0x11- 应答包(Response)
请求流程:
APP/网关 ──[0x01]──> 设备 (请求)
设备 ──[0x11]──> APP/网关 (应答)3. 包序号 (Sequence Number)
- 长度: 2字节
- 字节序: 大端序(Big-Endian)
- 取值范围:
0x0001~0xFFFF - 规则:
- 从
1开始计数 - 每次请求递增
1 - 应答包序号与请求包序号相同
- 从
c
// 包序号示例
uint16_t seq = 1; // 第一个请求包
// 请求包序号: 0x0001
send_request(seq);
// 应答包序号: 0x0001 (与请求相同)
receive_response(seq);
seq++; // 下一个请求包序号: 0x0002序号回绕
当包序号达到0xFFFF后,下一个包序号回到0x0001(不使用0x0000)。
4. 包标识 (Packet Flags)
- 长度: 1字节
- 结构:
[版本(高4位)][加密类型(低4位)]
包标识字节结构:
┌─────────────┬─────────────┐
│ 版本(4bit) │ 加密类型(4bit)│
│ Bit 7~4 │ Bit 3~0 │
└─────────────┴─────────────┘加密类型定义
| 加密类型值 | 加密方式 | 密钥来源 | 说明 |
|---|---|---|---|
0x0 | 明文 | 无 | 数据不加密,用于公开信息 |
0x1 | AES128 | 预设密钥 | 使用AES-128加密 |
0x2 | SM4 | 事先约定密钥 | 使用SM4加密,密钥预先配置 |
0x3 | SM4 | 设备指定密钥 | 使用SM4加密,密钥由设备动态分配 |
示例
c
// 版本1, SM4加密(事先约定密钥)
uint8_t flags = 0x12; // 0001 0010
// ↑ ↑
// 版本1 SM4(约定密钥)
// 版本1, 明文传输
uint8_t flags = 0x10; // 0001 0000
// ↑ ↑
// 版本1 明文5. 数据长度 (Data Length)
- 长度: 4字节
- 字节序: 大端序(Big-Endian)
- 结构:
[加密后长度(高16位)][原始长度(低16位)]
数据长度字段结构(4字节):
┌────────────────┬────────────────┐
│ 加密后长度(2B) │ 原始长度(2B) │
│ Byte 0~1 │ Byte 2~3 │
└────────────────┴────────────────┘字段说明
| 字段 | 长度 | 说明 |
|---|---|---|
| 加密后长度 | 2字节 | 实际传输的数据块长度(加密+填充后) |
| 原始长度 | 2字节 | 加密前的原始数据长度 |
计算规则
c
// 示例: 原始数据100字节, SM4加密(16字节对齐)
uint16_t original_len = 100; // 原始长度
uint16_t encrypted_len = 112; // 加密后长度(向上对齐到16倍数)
// 数据长度字段 (大端序)
uint8_t data_length[4] = {
(encrypted_len >> 8) & 0xFF, // [0] 加密后长度高字节
encrypted_len & 0xFF, // [1] 加密后长度低字节
(original_len >> 8) & 0xFF, // [2] 原始长度高字节
original_len & 0xFF // [3] 原始长度低字节
};
// 结果: {0x00, 0x70, 0x00, 0x64}明文传输
当使用明文传输时(加密类型=0),加密后长度和原始长度相同。
6. 数据块 (Data Block)
- 长度: 变长
- 内容: 具体业务数据,格式由各接口定义
- 加密: 根据包标识中的加密类型进行加密
数据块填充规则
加密时需要进行数据填充以满足块对齐要求:
| 加密算法 | 块大小 | 填充方式 |
|---|---|---|
| AES128 | 16字节 | PKCS#7填充 |
| SM4 | 16字节 | PKCS#7填充 |
PKCS#7填充示例:
原始数据(10字节): 01 02 03 04 05 06 07 08 09 0A
填充后(16字节): 01 02 03 04 05 06 07 08 09 0A 06 06 06 06 06 06
└─填充6个0x067. 校验位 (CRC)
- 长度: 2字节
- 算法: CRC16-KERMIT
- 校验范围: 包头 ~ 数据块(不包括校验位本身)
- 字节序: 大端序(Big-Endian)
CRC计算范围
┌─────────────────────────────────────────┐
│ 包头 │ 包类型 │ 包序号 │ ... │ 数据块 │ CRC │
└─────────────────────────────────────────┘
└──────── 参与CRC计算 ──────────┘ └─校验值─┘CRC16-KERMIT参数
c
// CRC16-KERMIT参数
Polynomial: 0x1021
Initial Value: 0x0000
Input Reflected: Yes
Output Reflected: Yes
Final XOR: 0x0000完整数据包示例
明文请求包示例
请求: 获取设备状态 (命令0x3040)
┌────────────────────────────────────────────────────────────────┐
│ EF 01 EE 02 │ 01 │ 00 01 │ 10 │ 00 04 00 04 │ 30 40 00 00 │ CRC │
└────────────────────────────────────────────────────────────────┘
包头 │类型│ 序号 │标识│ 数据长度 │ 数据块 │校验位│
(4字节) │请求│ 1 │明文│ 4/4 │ 命令+参数 │ │字段解析:
c
包头: 0xEF01EE02 // 固定值
包类型: 0x01 // 请求包
包序号: 0x0001 // 第1个包
包标识: 0x10 // 版本1, 明文传输
数据长度: 0x00040004 // 加密后4字节, 原始4字节
数据块: 0x30400000 // 命令0x3040 + 2字节参数
校验位: [CRC16] // 根据前面所有字节计算数据类型定义
数据块中使用的基本数据类型:
| 类型 | 长度 | 字节序 | 说明 |
|---|---|---|---|
| 整型(uN) | N字节 | 大端序 | 如u1=1字节, u2=2字节, u4=4字节 |
| 字符串(定长) | 固定 | GBK编码 | C风格字符串,以\0结尾,不足部分用\0填充 |
| 字符串(变长) | 变长 | GBK编码 | C风格字符串,以\0结尾 |
| 二进制 | 变长 | - | 原始字节数据,长度由其他字段指定 |
整型示例
c
// u1: 1字节整数
uint8_t value_u1 = 0x42;
// u2: 2字节整数 (大端序)
uint16_t value_u2 = 0x1234;
uint8_t bytes[2] = {0x12, 0x34}; // 传输时的字节顺序
// u4: 4字节整数 (大端序)
uint32_t value_u4 = 0x12345678;
uint8_t bytes[4] = {0x12, 0x34, 0x56, 0x78};字符串示例
c
// 定长字符串(16字节)
char username[16] = "admin";
// 实际数据: "admin\0\0\0\0\0\0\0\0\0\0\0"
// (5字节数据 + 1字节\0 + 10字节填充\0)
// 变长字符串
char device_name[] = "智能门锁\0"; // GBK编码
// 实际数据: {0xD6, 0xC7, 0xC4, 0xDC, 0xC3, 0xC5, 0xCB, 0xF8, 0x00}重要规则
包序号匹配
- 应答包的包序号必须与请求包相同
- 用于匹配请求和应答,防止数据错乱
- 超时未收到应答时,可使用相同序号重发请求
