プロトコル仕様
bleRPCはBLE GATTの上に階層化されたプロトコルスタックを使用します。コマンド層はProtocol BuffersペイロードをRPCメタデータで包み、オプションの暗号化層はシリアライズされたコマンドを暗号化し、コンテナ層は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を使用します:
- リクエスト: CentralがWrite Without Responseで書き込み
- レスポンス: PeripheralがNotifyで送信
MTUはBLE ATT MTU Exchangeプロシージャにより自動的にネゴシエーションされます。アプリケーションはネゴシエーションされたMTU(ATTオーバーヘッドの3バイトを引いた値)を使用して、コンテナペイロードの最大サイズを決定
デフォルトタイムアウトは100msで、タイムアウト制御コンテナで設定可能
コンテナ層
コンテナ層は、大きなペイロードを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のビットフィールド詳細
各コンテナの3バイト目は、タイプ、コントロールコマンド、および予約ビットをエンコードします:
type:00= 最初、01= 後続、11= 制御control_cmd:0x0–0x6(type=11の場合のみ有効)reserved: ゼロ埋め
コンテナ分割の例
MTU=247(ATTオーバーヘッド後の利用可能サイズ244)で500バイトのコマンドペイロードの場合:
| コンテナ | タイプ | ヘッダー | ペイロード | 合計 |
|---|---|---|---|---|
| 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です。より大きな転送には、複数のリクエスト/レスポンスサイクルを使用してください。
制御コンテナ
制御コンテナ(type=0b11)は、アプリケーションデータの代わりにプロトコルレベルのコマンドを運びます。
タイムアウト共有 (control_cmd=0x1)
ペリフェラルが処理タイムアウトをセントラルに伝えることができます。
リクエスト (Central → Peripheral)
| フィールド | 値 |
|---|---|
| type | 0b11(制御) |
| control_cmd | 0x1 |
| payload_len | 0x00 |
レスポンス (Peripheral → Central)
| フィールド | 値 |
|---|---|
| type | 0b11(制御) |
| control_cmd | 0x1 |
| payload_len | 0x02 |
| payload | timeout_ms(16ビットLE、ミリ秒) |
ケイパビリティ共有 (control_cmd=0x4)
ペリフェラルがバッファ制約と機能フラグをセントラルに伝えることができます。
リクエスト (Central → Peripheral)
| フィールド | 値 |
|---|---|
| type | 0b11(制御) |
| control_cmd | 0x4 |
| payload_len | 0x06 |
| payload[0:2] | max_request_payload_size(16ビットLE、通常0) |
| payload[2:4] | max_response_payload_size(16ビットLE、通常0) |
| payload[4:6] | flags(16ビットLE、通常0) |
レスポンス (Peripheral → Central)
| フィールド | 値 |
|---|---|
| type | 0b11(制御) |
| control_cmd | 0x4 |
| payload_len | 0x06 |
| payload[0:2] | max_request_payload_size(16ビットLE) |
| payload[2:4] | max_response_payload_size(16ビットLE) |
| payload[4:6] | flags(16ビットLE) — ビット0: 暗号化サポート |
flagsが存在しない場合(後方互換性のための4バイトペイロード)、オプション機能は利用不可
ストリーム終了 (control_cmd=0x2, 0x3)
ストリーミングシーケンスの終了を通知します。どちら側からでもストリームを終了できます。
| control_cmd | 方向 | 説明 |
|---|---|---|
0x2 | Central → Peripheral | Centralがアップロードストリームを終了 |
0x3 | Peripheral → Central | Peripheralがダウンロードストリームを終了 |
どちらもpayload_len=0x00(ペイロードなし)
エラー通知 (control_cmd=0x5)
エラー発生時(例: レスポンスがバッファ制限を超えた場合)にペリフェラルから送信されます。
| フィールド | 値 |
|---|---|
| type | 0b11(制御) |
| control_cmd | 0x5 |
| payload_len | 0x01 |
| payload[0] | エラーコード |
エラーコード:
0x01— RESPONSE_TOO_LARGE: レスポンスペイロードがmax_response_payload_sizeを超過
鍵交換 (control_cmd=0x6)
暗号化セッションを確立するための鍵交換ハンドシェイクペイロードを運びます。ペリフェラルがケイパビリティのflagsフィールドで暗号化サポートをアドバタイズした場合に使用されます。
| フィールド | 値 |
|---|---|
| type | 0b11(制御) |
| control_cmd | 0x6 |
| payload_len | 可変 |
| payload | 鍵交換ステップデータ(暗号化を参照) |
ハンドシェイクを完了するために4つのコンテナ(ステップ1〜4)が交換されます。完了後、すべての後続データコンテナは暗号化されたペイロードを運ぶ
暗号化データコンテナ
暗号化が有効な場合、データコンテナ(FIRST / SUBSEQUENT)のペイロードは、シリアライズされたコマンドの暗号化形式に置き換えられます:
| フィールド | サイズ | 説明 |
|---|---|---|
counter | 4バイト | 単調増加カウンター(リトルエンディアン)。AES-GCMのnonceおよびリプレイ検出に使用。 |
ciphertext | Nバイト | AES-128-GCMで暗号化されたシリアライズ済みコマンド |
tag | 16バイト | AES-GCM認証タグ |
合計オーバーヘッド: 20バイト(カウンター4バイト + タグ16バイト)。暗号文は通常通りコンテナに分割されます。
コマンド層
コマンド層は、Protocol Buffersでエンコードされたデータをメタデータで包みます。コマンドは名前(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ファイルの定義から自動的に生成されます。*Request / *Responseメッセージのペアから、プレフィックスをsnake_caseに変換したコマンド名が生成されます:
| メッセージペア | コマンド名 |
|---|---|
EchoRequest + EchoResponse | echo |
FlashReadRequest + FlashReadResponse | flash_read |
DataWriteRequest + DataWriteResponse | data_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は、単純なリクエスト/レスポンスに加えて、2つのストリーミングパターンをサポートしています:
Peripheral → Central ストリーム(サーバーストリーミング)
1つのリクエストが複数のレスポンスをトリガーします。ストリームは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制御コンテナが続きます。ペリフェラルは最終メッセージで応答します。
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)
接続セットアップシーケンス
セントラルがペリフェラルに接続する際、以下の初期化ステップを実行します:
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