はじめに

このガイドでは、プロトコルの定義、コード生成、およびbleRPCのプロジェクトへの統合方法を説明します。

1. プロトコルの定義

RPCメッセージを定義する.protoファイルを作成します。各コマンドはRequest/Responseのペアです:

syntax = "proto3";
package blerpc;

message EchoRequest {
  string message = 1;
}

message EchoResponse {
  string message = 1;
}

message FlashReadRequest {
  uint32 address = 1;
  uint32 length = 2;
}

message FlashReadResponse {
  uint32 address = 1;
  bytes data = 2;
}

コマンドは*Request / *Responseのペアから自動的に検出されます。例えば、EchoRequest + EchoResponseからechoコマンドが生成されます。

2. コード生成

generate-handlersツールは、対応するすべての言語のハンドラースタブとクライアントコードを生成します。

ジェネレーターのビルド

cd tools/generate-handlers
go build -o generate-handlers .

コード生成の実行

./generate-handlers -root /path/to/blerpc

以下のファイルが生成されます:

出力説明
peripheral_fw/src/generated_handlers.hCハンドラー宣言 + ルックアップテーブル
peripheral_fw/src/generated_handlers.cC weakハンドラースタブ
peripheral_py/generated_handlers.pyPythonハンドラースタブ
central_py/blerpc/generated/generated_client.pyPythonクライアントmixin
central_android/.../GeneratedClient.ktKotlin抽象クライアント
central_ios/.../GeneratedClient.swiftSwiftクライアントプロトコル拡張
central_flutter/.../generated_client.dartDartクライアントmixin
central_rn/.../GeneratedClient.tsTypeScript抽象クライアント(React Native)
central_fw/src/generated_client.hC centralクライアント宣言
central_fw/src/generated_client.cC centralクライアント実装

3. プラットフォーム統合

iOS (Swift)

依存関係

Swift Package Managerを使ってXcodeプロジェクトに追加します:

// Package.swift or Xcode > File > Add Package Dependencies
https://github.com/tdaira/blerpc-protocol-swift  (from: 0.5.0)
https://github.com/apple/swift-protobuf.git      (from: 1.28.0)

Protobuf型の生成

brew install swift-protobuf
protoc --swift_out=YourApp/Proto/ \
  --swift_opt=Visibility=Public \
  -I proto/ proto/blerpc.proto

使い方

let client = BlerpcClient()

// ペリフェラルをスキャン
let devices = try await client.scan()
try await client.connect(device: devices[0])

// 型安全なRPC呼び出し
let response = try await client.echo(message: "hello")
print(response.message)  // "hello"

// フラッシュデータの読み取り
let flash = try await client.flashRead(address: 0, length: 8192)
print(flash.data.count)  // 8192

client.disconnect()

Android (Kotlin)

依存関係

build.gradle.ktsに追加します:

repositories {
    maven {
        url = uri("https://maven.pkg.github.com/tdaira/blerpc-protocol-kt")
        credentials { /* GitHub token */ }
    }
}

dependencies {
    implementation("com.blerpc:blerpc-protocol-kt:0.5.0")
}

使い方

val client = BlerpcClient(context)

// ペリフェラルをスキャン
val devices = client.scan()
client.connect(devices[0])

val response = client.echo(message = "hello")
println(response.message)  // "hello"

client.disconnect()

Python (macOS / Linux)

依存関係

pip install bleak protobuf
pip install git+https://github.com/tdaira/blerpc-protocol.git

Protobuf型の生成

protoc --python_out=central_py/blerpc/generated/ \
  -I proto/ proto/blerpc.proto

使い方

from blerpc.client import BlerpcClient

client = BlerpcClient()

# ペリフェラルをスキャン
devices = await client.scan()
await client.connect(devices[0])

resp = await client.echo(message="hello")
print(resp.message)  # "hello"

flash = await client.flash_read(address=0, length=8192)
print(len(flash.data))  # 8192

await client.disconnect()

Flutter (Dart)

依存関係

pubspec.yamlに追加します:

dependencies:
  blerpc_protocol: ^0.6.0
  flutter_blue_plus: ^1.35.0
  protobuf: ^6.0.0

Protobuf型の生成

dart pub global activate protoc_plugin
protoc --dart_out=lib/proto/ \
  -I proto/ proto/blerpc.proto

使い方

final client = BlerpcClient();

// ペリフェラルをスキャン
final devices = await client.scan();
await client.connect(devices[0]);

// 型安全なRPC呼び出し
final response = await client.echo(message: 'hello');
print(response.message);  // "hello"

// フラッシュデータの読み取り
final flash = await client.flashRead(address: 0, length: 8192);
print(flash.data.length);  // 8192

client.disconnect();

React Native (TypeScript)

依存関係

package.jsonに追加します:

{
  "dependencies": {
    "@blerpc/protocol-rn": "file:../blerpc-protocol-rn",
    "protobufjs": "^7.4.0",
    "react-native-ble-plx": "^3.2.1",
    "react-native-get-random-values": "^1.11.0",
    "buffer": "^6.0.3",
    "fast-text-encoding": "^1.0.6",
    "@noble/ciphers": "^1.3.0",
    "@noble/curves": "^1.9.7",
    "@noble/hashes": "^1.8.0"
  }
}

Protobuf型の生成

npx pbjs -t static-module -w commonjs \
  -o src/proto/blerpc.js ../proto/blerpc.proto
npx pbts -o src/proto/blerpc.d.ts src/proto/blerpc.js

使い方

import { BlerpcClient } from './client/BlerpcClient';
import { BleTransport } from './ble/BleTransport';

const transport = new BleTransport();
const devices = await transport.scan();
const client = new BlerpcClient(transport);
await client.connect(devices[0]);

// 型安全なRPC呼び出し
const response = await client.echo({ message: 'hello' });
console.log(response.message);  // "hello"

// フラッシュデータの読み取り
const flash = await client.flashRead({ address: 0, length: 8192 });
console.log(flash.data.length);  // 8192

client.disconnect();

Zephyr Central (C)

生成されたクライアント

コードジェネレーターは、すべてのコマンドに対する型付きラッパー関数を持つgenerated_client.hgenerated_client.cを生成します。3つのトランスポート関数(blerpc_rpc_callblerpc_stream_receiveblerpc_stream_send)を実装し、生成されたAPIを呼び出します:

#include "generated_client.h"

// 型安全なRPC呼び出し — protobufのエンコード/デコードは自動的に処理されます
blerpc_EchoResponse resp;
int ret = blerpc_echo("Hello from central!", &resp);
if (ret == 0) {
    LOG_INF("Echo: %s", resp.message);
}

// フラッシュデータの読み取り(FT_CALLBACKフィールドは自動的に処理されます)
blerpc_FlashReadResponse flash;
uint8_t data_buf[8192];
size_t data_len;
ret = blerpc_flash_read(0, 8192, &flash, data_buf, sizeof(data_buf), &data_len);

// P2Cストリーミング
blerpc_CounterStreamResponse results[20];
size_t count;
ret = blerpc_counter_stream(20, results, 20, &count);

ビルド

west build -b nrf54l15dk/nrf54l15/cpuapp central_fw

Zephyr Peripheral (C)

ハンドラーの実装

生成されたハンドラーは__attribute__((weak))です。独自のソースファイルでオーバーライドします:

// handlers.c
#include "generated_handlers.h"
#include "blerpc.pb.h"

int handle_echo(const uint8_t *req_data, size_t req_len,
                pb_ostream_t *ostream) {
    blerpc_EchoRequest req = blerpc_EchoRequest_init_zero;
    pb_istream_t stream = pb_istream_from_buffer(req_data, req_len);
    if (!pb_decode(&stream, blerpc_EchoRequest_fields, &req))
        return -1;

    blerpc_EchoResponse resp = blerpc_EchoResponse_init_zero;
    strncpy(resp.message, req.message, sizeof(resp.message) - 1);
    if (!pb_encode(ostream, blerpc_EchoResponse_fields, &resp))
        return -1;
    return 0;
}

ビルド

# nRF54L15
west build -b nrf54l15dk/nrf54l15/cpuapp peripheral_fw

# EFR32xG22E
west build -b xg22_ek2710a peripheral_fw -- -DBOARD_ROOT=/path/to/blerpc

4. Python Centralでのテスト

ペリフェラルの動作確認に最も簡単な方法:

# ペリフェラルファームウェアを書き込み
west flash

# Python統合テストを実行
cd central_py
python -m pytest tests/test_integration.py -v

または、インタラクティブクライアントを使用します:

python -c "
import asyncio
from blerpc.client import BlerpcClient

async def main():
    client = BlerpcClient()
    devices = await client.scan()
    await client.connect(devices[0])
    resp = await client.echo(message='hello')
    print(f'Echo: {resp.message}')
    await client.disconnect()

asyncio.run(main())
"