协议规范

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

graph TD
    A["Application"] --> B["Command Layer<br/>Protobuf encode/decode + command metadata"]
    B --> E["Encryption Layer (optional)<br/>AES-128-GCM encrypt/decrypt"]
    E --> C["Container Layer<br/>MTU-based split/reassemble + sequencing"]
    C --> D["BLE GATT<br/>(single characteristic)"]
      

BLE 传输

所有通信使用单个 GATT Characteristic

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

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

容器层

容器层将大载荷分割为 MTU 大小的数据包,并在接收端重新组装。每个容器携带一个事务 ID 和序列号,用于跟踪和排序。

容器格式

所有多字节字段为小端序

首个容器 (type=0b00)

事务中的第一个容器包含总载荷长度:

transaction_id
8 bits
sequence_number
8 bits
type
2
ctrl_cmd
4
rsv
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
ctrl_cmd
4
rsv
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。对于更大的传输,请使用多次请求/响应循环。

控制容器

控制容器(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:支持加密

flags 不存在时(4 字节载荷,用于向后兼容),没有可选功能可用

流结束 (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)完成握手。完成后,所有后续数据容器携带加密载荷

加密数据容器

当加密启用时,数据容器(FIRST / SUBSEQUENT)的载荷替换为序列化命令的加密形式:

counter
32 bits (LE)
ciphertext
variable
tag
128 bits
字段大小描述
counter4 字节单调递增计数器(小端序)。用作 AES-GCM nonce 和重放检测。
ciphertextN 字节AES-128-GCM 加密的序列化命令
tag16 字节AES-GCM 认证标签

总开销:20 字节(4 计数器 + 16 标签)。密文随后按常规方式分割到容器中。

命令层

命令层使用 RPC 元数据包装 Protocol Buffers 编码的数据。命令通过名称(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 文件定义中自动生成。匹配的 *Request / *Response 消息对会生成一个命令,其名称是前缀转换为 snake_case 的形式:

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

请求/响应流程

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

sequenceDiagram
    participant C as Central
    participant P as Peripheral
    Note over C: 1. Protobuf encode request
    Note over C: 2. Wrap in command header
    Note over C: 3. Split into containers
    C->>P: Write Without Response (FIRST)
    C->>P: Write Without Response (SUBSEQUENT...)
    Note over P: 4. Reassemble containers
    Note over P: 5. Decode command + Protobuf
    Note over P: 6. Execute handler
    Note over P: 7. Encode response + split
    P->>C: Notify (FIRST)
    P->>C: Notify (SUBSEQUENT...)
    Note over C: 8. Reassemble + decode response
      

流式传输

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

Peripheral → Central 流(服务端流式)

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

sequenceDiagram
    participant C as Central
    participant P as Peripheral
    C->>P: Request
    P->>C: Response 1 (Notify)
    P->>C: Response 2 (Notify)
    P->>C: Response 3 (Notify)
    P-->>C: STREAM_END_P2C (control)
      

Central → Peripheral 流(客户端流式)

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

sequenceDiagram
    participant C as Central
    participant P as Peripheral
    C->>P: Request 1
    C->>P: Request 2
    C->>P: Request 3
    C-->>P: STREAM_END_C2P (control)
    P->>C: Final Response (Notify)
      

连接建立序列

当 central 连接到 peripheral 时,会执行以下初始化步骤:

sequenceDiagram
    participant C as Central
    participant P as Peripheral
    C->>P: BLE Connect
    C->>P: MTU Exchange
    C->>P: Discover Services
    C->>P: Enable Notifications
    C->>P: Timeout Request (ctrl)
    P->>C: Timeout Response (e.g. 100ms)
    C->>P: Capability Request (ctrl)
    P->>C: Capability Response (max_req, max_resp, flags)
    opt Encryption (flags & 0x01)
        C->>P: Key Exchange Step 1 (ctrl 0x6)
        P->>C: Key Exchange Step 2 (ctrl 0x6)
        C->>P: Key Exchange Step 3 (ctrl 0x6)
        P->>C: Key Exchange Step 4 (ctrl 0x6)
        Note over C,P: AES-128-GCM session established
    end
    Note over C,P: Ready for RPC calls