协议规范

bleRPC 在 BLE GATT 之上使用分层协议栈。命令层使用 RPC 元数据封装 Protocol Buffers 载荷,可选的加密层对序列化后的命令进行加密,容器层将它们分片为 MTU 大小的数据包进行 BLE 传输。

连接建立序列

在任何 RPC 调用之前,central 会与 peripheral 执行一个简短的建立序列。这是整体的时间线 — 每个步骤都在下面的章节中详细说明(密钥交换部分参见加密)。

命令层

命令层使用 RPC 元数据将 Protocol Buffers 编码的数据封装成 CommandPacket。命令通过名称(ASCII 字符串)标识,实现灵活、人类可读的调度。

命令格式

T
1 bit
reserved
7 bits
cmd_name_len
8 bits
cmd_name
variable
data_len
16 bits (LE)
data
variable
字段描述
type10 = 请求,1 = 响应
reserved7零填充
cmd_name_len8命令名称的字节长度
cmd_name可变ASCII 命令名称(例如 echoflash_read
data_len16protobuf 数据的长度(字节,小端序)
data可变Protocol Buffers 编码的载荷

命令发现

代码生成器通过以下两种方式之一从你的 .proto 文件派生命令:

消息对命令名称
EchoRequest + EchoResponseecho
FlashReadRequest + FlashReadResponseflash_read
DataWriteRequest + DataWriteResponsedata_write

流式命令单独注册 — 既可以通过服务块中的流式 rpc 方法,也可以通过在 proto/streaming.txt 中列出它们(例如 counter_stream p2ccounter_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 bits
sequence_number
8 bits
type
2
control_cmd
4
reserved
2
total_length
16 bits (LE)
payload_len
8 bits
payload
variable
字段描述
transaction_id8此请求/响应对的唯一 ID。每次事务重置。
sequence_number8每发送一个容器递增。每次事务重置。
type20b00 = 首个
control_cmd40x0(数据容器不使用)
reserved2零填充
total_length16所有容器的总载荷大小(字节,小端序)
payload_len8此容器中的载荷大小(字节)
payload可变命令数据片段

头部大小:6 字节(transaction_id + sequence_number + flags + total_length + payload_len)

后续容器 (type=0b01)

续传容器省略 total_length

transaction_id
8 bits
sequence_number
8 bits
type
2
control_cmd
4
reserved
2
payload_len
8 bits
payload
variable

头部大小:4 字节(无 total_length 字段)

字节 2 位域详情

每个容器的第三个字节编码类型、控制命令和保留位:

type
bits 7-6
control_cmd
bits 5-2
reserved
bits 1-0

容器分割示例

500 字节命令载荷,MTU=247(ATT 开销后可用 244 字节):

容器类型头部载荷总计
1FIRST6 字节238 字节244 字节
2SUBSEQUENT4 字节240 字节244 字节
3SUBSEQUENT4 字节22 字节26 字节

总载荷:500 字节(238 + 240 + 22)

最大载荷

有两个限制约束单个事务。sequence_number 为 8 位,将其限制为约 255 个容器;在典型 MTU 为 247(每个后续容器 240 字节)的情况下,实际最大载荷约为 60 KB。16 位的 total_length 字段还施加了 65,535 字节的绝对上限。对于更大的传输,请使用多次请求/响应循环。

BLE 传输

所有通信使用单个 GATT Characteristic

MTU 通过 BLE ATT MTU Exchange 过程自动协商。应用程序使用协商后的 MTU(减去 3 字节 ATT 开销)来确定最大容器载荷大小

默认超时为 100ms,可通过超时控制容器配置

请求/响应流程

完整的 RPC 调用涉及命令层编码、容器层分割、BLE 传输,以及接收端的反向操作:

控制容器

控制容器(type=0b11)携带协议级命令而非应用数据。

超时共享 (control_cmd=0x1)

允许 peripheral 向 central 通告其处理超时时间。

请求(Central → Peripheral)

字段
type0b11(控制)
control_cmd0x1
payload_len0x00

响应(Peripheral → Central)

字段
type0b11(控制)
control_cmd0x1
payload_len0x02
payloadtimeout_ms(16 位小端序,毫秒)

能力共享 (control_cmd=0x4)

允许 peripheral 向 central 通告其缓冲区约束和功能标志。

请求(Central → Peripheral)

字段
type0b11(控制)
control_cmd0x4
payload_len0x06
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)

字段
type0b11(控制)
control_cmd0x4
payload_len0x06
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方向描述
0x2Central → PeripheralCentral 结束上传流
0x3Peripheral → CentralPeripheral 结束下载流

两者均为 payload_len=0x00(无载荷)

错误通知 (control_cmd=0x5)

当发生错误时(例如响应超过缓冲区限制)由 peripheral 发送。

字段
type0b11(控制)
control_cmd0x5
payload_len0x01
payload[0]错误代码

错误代码:

密钥交换 (control_cmd=0x6)

携带密钥交换握手载荷,用于建立加密会话。当 peripheral 通过能力 flags 字段广播加密支持时使用。

字段
type0b11(控制)
control_cmd0x6
payload_len可变
payload密钥交换步骤数据(参见加密

交换 4 个容器(步骤 1–4)完成握手。完成后,所有后续数据容器携带加密载荷。

流式传输

bleRPC 支持两种流式传输模式,超越简单的请求/响应:

Peripheral → Central 流(服务端流式)

一个请求触发多个响应。流以 STREAM_END_P2C 控制容器结束。

Central → Peripheral 流(客户端流式)

发送多个请求,然后发送 STREAM_END_C2P 控制容器。Peripheral 返回最终消息。