pw_rpc#

Efficient, low-code-size RPC system for embedded devices

Stable C++ Java JavaScript Python TypeScript

pw_rpc provides an embedded-friendly remote procedure call (RPC) system for defining and invoking structured methods over arbitrary serial, bus, or packet transports (UART, SPI, USB, BLE, Sockets). Services and messages are defined in shared Protocol Buffer (.proto) files.

pw_rpc supports C++ (with Nanopb, pw_protobuf, or Raw RPC codegen), Python, TypeScript, and Java.

syntax = "proto3";

package blinky;

import "pw_protobuf_protos/common.proto";

service Blinky {
  // Toggles the LED on or off.
  rpc ToggleLed(pw.protobuf.Empty) returns (pw.protobuf.Empty);
  // Continuously blinks the board LED a specified number of times.
  rpc Blink(BlinkRequest) returns (pw.protobuf.Empty);
}

message BlinkRequest {
  // The interval at which to blink the LED, in milliseconds.
  uint32 interval_ms = 1;
  // The number of times to blink the LED.
  optional uint32 blink_count = 2;
}
#include "pw_rpc/examples/blinky.rpc.pb.h"
#include "pw_system/rpc_server.h"

namespace blinky {

class BlinkyService final
    : public blinky::pw_rpc::nanopb::Blinky::Service<BlinkyService> {
 public:
  pw::Status ToggleLed(const pw_protobuf_Empty&, pw_protobuf_Empty&) {
    // Turn the LED off if it's currently on and vice versa
    return pw::OkStatus();
  }

  pw::Status Blink(const blinky_BlinkRequest& request, pw_protobuf_Empty&) {
    if (request.blink_count == 0) {
      // Stop blinking
    }
    if (request.interval_ms > 0) {
      // Change the blink interval
    }
    if (request.has_blink_count) {
      // Blink request.blink_count times
    }
    return pw::OkStatus();
  }
};

BlinkyService blinky_service;

}  // namespace blinky

namespace pw::system {

void UserAppInit() {
  pw::system::GetRpcServer().RegisterService(blinky::blinky_service);
}

}  // namespace pw::system
proto_library(
    name = "blinky_proto",
    srcs = ["blinky.proto"],
    deps = [
        "//pw_protobuf:common_proto",
    ],
)

nanopb_proto_library(
    name = "blinky_nanopb",
    deps = [":blinky_proto"],
)

nanopb_rpc_proto_library(
    name = "blinky_nanopb_rpc",
    nanopb_proto_library_deps = [":blinky_nanopb"],
    deps = [":blinky_proto"],
)

cc_library(
    name = "blinky_service",
    srcs = ["blinky_service.cc"],
    deps = [
        ":blinky_nanopb_rpc",
        "//pw_system:rpc_server",
    ],
)

Where to go next#

Integration & setup

For Platform Engineers & System Architects. Step-by-step checklist to bring up pw_rpc on target hardware: transports, channels, RX/TX plumbing, and dispatch loops.

Integration & setup
Creating services

For Application Developers. How to define .proto services, generate C++ code, implement unary and streaming methods, and write unit tests.

Creating services
C++ client & server

Core C++ runtime mechanics: channels, call objects, synchronous call wrappers, concurrency rules, and test fixtures.

C++ client & server
C++ with Nanopb

Lightweight C struct message generator (recommended for embedded C++).

C++ with Nanopb
C++ with pw_protobuf

Type-safe pure C++ message generator.

C++ with pw_protobuf
C++ with raw RPC

Zero-copy byte buffer RPCs and method fallback mechanics.

C++ with raw RPC
Python client

Python client library, custom channels, and pw_console tools.

Python client
TypeScript client

WebSerial, WebUSB, and browser/Node.js client library.

TypeScript client
Java client

Android and JVM client library in dev.pigweed.pw_rpc.

Java client
Benchmarking

Tools for measuring throughput, latency, and fuzzer testing.

Benchmarking
Wire protocol

Packet wire format and envelope protocol specification.

Wire protocol