Getting Started

This guide covers how to define your protocol, generate code, and integrate bleRPC into your project.

1. Define Your Protocol

Create a .proto file defining your RPC messages. Each command is a Request/Response pair:

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;
}

Commands are automatically discovered from matching *Request / *Response pairs. For example, EchoRequest + EchoResponse generates an echo command.

2. Code Generation

The generate-handlers tool generates handler stubs and client code for all supported languages.

Build the generator

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

Run code generation

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

This generates the following files:

OutputDescription
peripheral/src/generated_handlers.hC handler declarations + lookup table
peripheral/src/generated_handlers.cC weak handler stubs
peripheral_py/generated_handlers.pyPython handler stubs
central/blerpc/generated/generated_client.pyPython client mixin
central_android/.../GeneratedClient.ktKotlin abstract client
central_ios/.../GeneratedClient.swiftSwift client protocol extension

3. Platform Integration

iOS (Swift)

Dependencies

Add to your Xcode project via Swift Package Manager:

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

Generate Protobuf types

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

Usage

let client = BlerpcClient()
try await client.connect()

// Type-safe RPC call
let response = try await client.echo(message: "hello")
print(response.message)  // "hello"

// Read flash data
let flash = try await client.flashRead(address: 0, length: 8192)
print(flash.data.count)  // 8192

client.disconnect()

Android (Kotlin)

Dependencies

Add to your 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.1.0")
}

Usage

val client = BlerpcClient(context)
client.connect()

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

client.disconnect()

Python (macOS / Linux)

Dependencies

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

Generate Protobuf types

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

Usage

from blerpc.client import BlerpcClient

async with BlerpcClient() as client:
    await client.connect()

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

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

Zephyr Peripheral (C)

Implement handlers

The generated handlers are __attribute__((weak)). Override them in your own source file:

// 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;
}

Build

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

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

4. Testing with Python Central

The easiest way to verify your peripheral is working:

# Flash the peripheral firmware
west flash

# Run the Python integration tests
cd central
python -m pytest tests/test_integration.py -v

Or use the interactive client:

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

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

asyncio.run(main())
"