Skip to content
neuralSPOT-X
User guide
HELIA HUB

USB RPC

USB vendor-class RPC with nanopb protobuf serialization.

Declared by the example author Value
Tier Integrations
Status tested
Capabilities usb, rpc, nanopb
Boards tested apollo510_evb

Build it from a source checkout by name, because the positional app argument resolves under ./examples:

Terminal window
nsx build usb_rpc --board apollo510_evb

The rest of this page is the example’s own README, from examples/usb_rpc/.

USB CDC RPC (Remote Procedure Call) example for Apollo510 EVB.

Implements a length-prefixed nanopb (Protocol Buffers) RPC transport over USB CDC, allowing a host PC to invoke device-side functions. The device processes incoming protobuf-encoded requests, dispatches them, and returns protobuf-encoded responses.

  • USB CDC bulk transport with framed messages (4-byte LE length prefix)
  • nanopb serialization/deserialization (generated from proto/nsx_rpc.proto)
  • Extensible dispatch table in nsx_rpc_dispatch.c
  • Static allocation — no malloc
Message Direction Description
PING req → resp Round-trip latency test; response includes device uptime
STATUS req → resp Device identity: board name, firmware version
INFER req → resp Toy inference: sends raw bytes, device returns predicted class + confidence
Terminal window
nsx configure --app-dir .
nsx build --app-dir .
nsx flash --app-dir .

A Python RPC client is included in tools/:

Terminal window
# Install dependencies (from neuralspotx root)
pip install pyserial grpcio-tools
# Run the demo (auto-detects the CDC port)
python tools/rpc_host.py

Or specify a port explicitly:

Terminal window
python tools/rpc_host.py /dev/cu.usbmodem1234
Connecting to /dev/cu.usbmodem14301 ...
Connected.
=== PING ===
seq=0 uptime=1234 ms RTT=3.2 ms
seq=1 uptime=1237 ms RTT=2.8 ms
seq=2 uptime=1240 ms RTT=2.9 ms
=== STATUS ===
board : apollo510_evb
fw_version : 1.0.0
free_heap : 0 bytes
=== INFERENCE ===
trial 0: class=2 label='run' conf=0.617
trial 1: class=4 label='unknown' conf=0.539
trial 2: class=1 label='walk' conf=0.680
trial 3: class=0 label='idle' conf=0.562
trial 4: class=3 label='gesture' conf=0.711
Done.

Note: The inference handler is a toy stub — it sums input bytes and maps to one of 5 classes. Replace handle_infer() in nsx_rpc_dispatch.c with a real model for production use.

┌──────────────┬─────────────────────────────────┐
│ 4 bytes (LE) │ nanopb-encoded NsxRpcMessage │
│ payload len │ │
└──────────────┴─────────────────────────────────┘

Both host and device use the same framing. The .proto definition is in proto/nsx_rpc.proto; the Python bindings are auto-generated on first run.

usb_rpc/
├── CMakeLists.txt
├── nsx.yml
├── proto/
│ ├── nsx_rpc.proto Protocol definition
│ ├── nsx_rpc.options nanopb options (field sizes)
│ ├── nsx_rpc.pb.c Generated C bindings
│ └── nsx_rpc.pb.h
├── src/
│ ├── main.c USB init + framing state machine
│ ├── nsx_rpc_dispatch.c Handler stubs (ping, infer, status)
│ └── nsx_rpc_dispatch.h
└── tools/
├── rpc_host.py Python host-side client
└── nsx_rpc_pb2.py Generated Python bindings
  • nsx-usb — USB CDC device driver
  • nsx-nanopb — Protocol Buffers encoding
  • nsx-core — System init and harness