协议规范
bleRPC 在 BLE GATT 之上使用分层协议栈。命令层使用 RPC 元数据封装 Protocol Buffers 载荷,可选的加密层对序列化后的命令进行加密,容器层将它们分片为 MTU 大小的数据包进行 BLE 传输。
连接建立序列
在任何 RPC 调用之前,central 会与 peripheral 执行一个简短的建立序列。这是整体的时间线 — 每个步骤都在下面的章节中详细说明(密钥交换部分参见加密)。
命令层
命令层使用 RPC 元数据将 Protocol Buffers 编码的数据封装成 CommandPacket。命令通过名称(ASCII 字符串)标识,实现灵活、人类可读的调度。
命令格式
| 字段 | 位 | 描述 |
|---|---|---|
type | 1 | 0 = 请求,1 = 响应 |
reserved | 7 | 零填充 |
cmd_name_len | 8 | 命令名称的字节长度 |
cmd_name | 可变 | ASCII 命令名称(例如 echo、flash_read) |
data_len | 16 | protobuf 数据的长度(字节,小端序) |
data | 可变 | Protocol Buffers 编码的载荷 |
命令发现
代码生成器通过以下两种方式之一从你的 .proto 文件派生命令:
- 服务定义(首选) — 如果文件声明了
service { ... }块,则每个rpc方法都会成为一个命令。命令名称是方法名的snake_case形式,其请求/响应类型取自方法签名 - 命名约定(回退) — 当未定义服务时,匹配的
*Request/*Response消息对会生成一个命令,其名称是前缀转换为snake_case的形式:
| 消息对 | 命令名称 |
|---|---|
EchoRequest + EchoResponse | echo |
FlashReadRequest + FlashReadResponse | flash_read |
DataWriteRequest + DataWriteResponse | data_write |
流式命令单独注册 — 既可以通过服务块中的流式 rpc 方法,也可以通过在 proto/streaming.txt 中列出它们(例如 counter_stream p2c、counter_upload c2p)。这些会生成专用的流式 API(参见流式传输),而非普通的请求/响应方法。
加密层
加密是可选的。当会话处于活动状态时(参见加密),序列化后的命令在交给容器层之前会先使用 AES-128-GCM 进行加密。加密后的载荷为 [counter:4][ciphertext:N][tag:16] — 共 20 字节开销(4 字节计数器 + 16 字节 GCM 标签)— 随后像明文命令一样被分割到容器中。
计数器同时充当 AES-GCM nonce 和重放防护。有关握手、密钥派生、nonce 构造和重放规则,请参见加密。
容器层
容器层将大载荷分割为 MTU 大小的数据包,并在接收端重新组装。每个容器携带一个事务 ID 和序列号,用于跟踪和排序。
容器格式
所有多字节字段为小端序。
首个容器 (type=0b00)
事务中的第一个容器包含总载荷长度:
| 字段 | 位 | 描述 |
|---|---|---|
transaction_id | 8 | 此请求/响应对的唯一 ID。每次事务重置。 |
sequence_number | 8 | 每发送一个容器递增。每次事务重置。 |
type | 2 | 0b00 = 首个 |
control_cmd | 4 | 0x0(数据容器不使用) |
reserved | 2 | 零填充 |
total_length | 16 | 所有容器的总载荷大小(字节,小端序) |
payload_len | 8 | 此容器中的载荷大小(字节) |
payload | 可变 | 命令数据片段 |
头部大小:6 字节(transaction_id + sequence_number + flags + total_length + payload_len)
后续容器 (type=0b01)
续传容器省略 total_length:
头部大小:4 字节(无 total_length 字段)
字节 2 位域详情
每个容器的第三个字节编码类型、控制命令和保留位:
type:00= 首个,01= 后续,11= 控制control_cmd:0x0–0x6(仅在 type=11时有效)reserved:零填充
容器分割示例
500 字节命令载荷,MTU=247(ATT 开销后可用 244 字节):
| 容器 | 类型 | 头部 | 载荷 | 总计 |
|---|---|---|---|---|
| 1 | FIRST | 6 字节 | 238 字节 | 244 字节 |
| 2 | SUBSEQUENT | 4 字节 | 240 字节 | 244 字节 |
| 3 | SUBSEQUENT | 4 字节 | 22 字节 | 26 字节 |
总载荷:500 字节(238 + 240 + 22)
最大载荷
有两个限制约束单个事务。sequence_number 为 8 位,将其限制为约 255 个容器;在典型 MTU 为 247(每个后续容器 240 字节)的情况下,实际最大载荷约为 60 KB。16 位的 total_length 字段还施加了 65,535 字节的绝对上限。对于更大的传输,请使用多次请求/响应循环。
BLE 传输
所有通信使用单个 GATT Characteristic:
- 请求:中心设备(Central)通过 Write Without Response 发送写入请求
- 响应:外围设备(Peripheral)通过 Notify 发送
MTU 通过 BLE ATT MTU Exchange 过程自动协商。应用程序使用协商后的 MTU(减去 3 字节 ATT 开销)来确定最大容器载荷大小
默认超时为 100ms,可通过超时控制容器配置
请求/响应流程
完整的 RPC 调用涉及命令层编码、容器层分割、BLE 传输,以及接收端的反向操作:
控制容器
控制容器(type=0b11)携带协议级命令而非应用数据。
超时共享 (control_cmd=0x1)
允许 peripheral 向 central 通告其处理超时时间。
请求(Central → Peripheral)
| 字段 | 值 |
|---|---|
| type | 0b11(控制) |
| control_cmd | 0x1 |
| payload_len | 0x00 |
响应(Peripheral → Central)
| 字段 | 值 |
|---|---|
| type | 0b11(控制) |
| control_cmd | 0x1 |
| payload_len | 0x02 |
| payload | timeout_ms(16 位小端序,毫秒) |
能力共享 (control_cmd=0x4)
允许 peripheral 向 central 通告其缓冲区约束和功能标志。
请求(Central → Peripheral)
| 字段 | 值 |
|---|---|
| type | 0b11(控制) |
| control_cmd | 0x4 |
| payload_len | 0x06 |
| payload[0:2] | max_request_payload_size(16 位小端序,通常为 0) |
| payload[2:4] | max_response_payload_size(16 位小端序,通常为 0) |
| payload[4:6] | flags(16 位小端序,通常为 0) |
响应(Peripheral → Central)
| 字段 | 值 |
|---|---|
| type | 0b11(控制) |
| control_cmd | 0x4 |
| payload_len | 0x06 |
| payload[0:2] | max_request_payload_size(16 位小端序) |
| payload[2:4] | max_response_payload_size(16 位小端序) |
| payload[4:6] | flags(16 位小端序)— 位 0:支持加密 |
能力载荷始终为 6 字节。没有可选功能的 peripheral 只需报告 flags = 0x0000。
流结束 (control_cmd=0x2, 0x3)
表示流式序列结束。任一端都可以终止流。
| control_cmd | 方向 | 描述 |
|---|---|---|
0x2 | Central → Peripheral | Central 结束上传流 |
0x3 | Peripheral → Central | Peripheral 结束下载流 |
两者均为 payload_len=0x00(无载荷)
错误通知 (control_cmd=0x5)
当发生错误时(例如响应超过缓冲区限制)由 peripheral 发送。
| 字段 | 值 |
|---|---|
| type | 0b11(控制) |
| control_cmd | 0x5 |
| payload_len | 0x01 |
| payload[0] | 错误代码 |
错误代码:
0x01— RESPONSE_TOO_LARGE:响应载荷超过max_response_payload_size0x02— BUSY:peripheral 没有空闲的请求槽位(它仍在处理另一个事务);central 应重试
密钥交换 (control_cmd=0x6)
携带密钥交换握手载荷,用于建立加密会话。当 peripheral 通过能力 flags 字段广播加密支持时使用。
| 字段 | 值 |
|---|---|
| type | 0b11(控制) |
| control_cmd | 0x6 |
| payload_len | 可变 |
| payload | 密钥交换步骤数据(参见加密) |
交换 4 个容器(步骤 1–4)完成握手。完成后,所有后续数据容器携带加密载荷。
流式传输
bleRPC 支持两种流式传输模式,超越简单的请求/响应:
Peripheral → Central 流(服务端流式)
一个请求触发多个响应。流以 STREAM_END_P2C 控制容器结束。
Central → Peripheral 流(客户端流式)
发送多个请求,然后发送 STREAM_END_C2P 控制容器。Peripheral 返回最终消息。