Skip to main content

mcurpc

Lightweight RPC framework for microcontrollers and Python.

MCURPC provides a small RPC stack for embedded targets together with Python tooling for API creation, code generation, runtime export, probing and interactive communication. APIs are described in YAML, resolved into a versioned JSON manifest and then used to generate C integration files for the embedded side.

The root README gives only a project-level overview and the most common commands. More detailed implementation notes belong in the Sphinx documentation under docs/.

What is included

  • API model and generator (mcurpc_gen): validates YAML API descriptions, resolves IDs and options, creates manifest.json, mcurpc_api.h, mcurpc_api.c and mcurpc_config.h.
  • Embedded C runtime (mcurpc_runtime): fixed MCURPC runtime sources for request dispatch, responses, events, logs and link framing.
  • Python runtime/client (mcurpc): link/message/payload handling, transports, registry lookup, device probing and manifest-driven client calls.
  • Command line interface (mcurpc_cli): project start, generation, runtime export, registry management, probing and interactive shell.
  • Optional GUI prototype (mcurpc_gui): early project-oriented GUI components.
  • Tests and CI helpers: Python unit tests, C/C++ unit tests, CLI system tests, CMake integration checks, coverage and Sphinx build commands.

Repository layout

ci_commands/   Local CI command implementations
configs/       Project/tool configuration, when present
docs/          Sphinx documentation
src/           Python packages and packaged runtime resources
tests/         Python, C/C++ and CLI system tests

The most relevant source packages are:

src/mcurpc/          Python runtime, client, transports and registry support
src/mcurpc_cli/      Unified `mcurpc` command line interface
src/mcurpc_gen/      YAML model, resolver and C source generator
src/mcurpc_runtime/  Packaged embedded runtime files and starter examples
src/mcurpc_gui/      GUI prototype modules

Requirements

The Python package requires Python 3.11 or newer. Runtime dependencies include pydantic, pyyaml, pyserial, platformdirs, cmd2 and cikit.

For the native C tests and CMake-based example checks, a working C/C++ toolchain, CMake and Ninja are expected. The repository currently uses CMake for native runtime tests and compile checks.

Installation for development

A typical local setup with uv is:

uv sync
uv run mcurpc --help

Alternatively, use an editable install in an existing virtual environment:

python -m pip install -e .
mcurpc --help

Basic workflow

A normal embedded workflow is:

  1. Create or edit an API YAML file.
  2. Create or prepare the target project.
  3. Generate API-specific C files from the YAML description.
  4. Implement the generated application callbacks in the target firmware.
  5. Register the manifest on the host side.
  6. Probe the device or open the interactive shell.

For a new starter project, the start command combines the first generation steps.

Creating a starter project

The start command creates a project from one of the packaged examples, exports the selected runtime layout and generates the API-specific files.

mcurpc start build/my_ping --example ping --layout arduino_ide
mcurpc start build/my_ping_pio --example ping --layout arduino_pio
mcurpc start build/my_counter --example counter --layout cmake
mcurpc start build/my_api --example custom --layout flat

The available layouts are:

  • arduino_ide: flattened Arduino IDE-oriented project with the C runtime and MCURPC stream wrapper.
  • arduino_pio: PlatformIO project layout with application and generated API sources below src/, generated headers below include/ and the fixed MCURPC runtime below lib/MCURPC/.
  • cmake: runtime files below an mcurpc/ subdirectory with a CMake target.
  • flat: all runtime and generated files in one project directory.

Useful options:

mcurpc start PROJECT_DIR --example ping --layout arduino_ide
mcurpc start PROJECT_DIR --example custom --layout cmake --name my_api
mcurpc start PROJECT_DIR --example counter --layout flat --force

Generating API files

Use generate when the project already exists and only the files derived from the API YAML should be updated.

mcurpc generate api.yaml -o path/to/project

The exact output paths depend on the selected layout. For the flat and Arduino IDE layouts, the generated files are written directly into the output directory:

manifest.json
mcurpc_api.c
mcurpc_api.h
mcurpc_config.h

For PlatformIO, generate with:

mcurpc generate api.yaml -o path/to/project --layout arduino_pio

The generated files are placed according to the normal PlatformIO project structure:

manifest.json
include/mcurpc_api.h
include/mcurpc_config.h
src/mcurpc_api.c

The generated files are project-specific. The fixed runtime files are exported separately through runtime or indirectly through start.

Exporting the runtime

Use runtime to copy only the fixed MCURPC runtime files into an existing project.

mcurpc runtime path/to/project --layout arduino_ide
mcurpc runtime path/to/project --layout arduino_pio
mcurpc runtime path/to/project --layout cmake
mcurpc runtime path/to/project --layout flat --force

The runtime export does not generate mcurpc_api.c, mcurpc_api.h, mcurpc_config.h or manifest.json. Those files come from the API generator.

API YAML at a glance

A minimal API can look like this:

name: garage_api
uuid: 11111111-1111-4111-8111-111111111111
version: 1

options:
  endpoint_count: 1
  tx_queue_length: 2

requests:
  - name: open_door
    request:
      - name: duration_ms
        type: u32

events:
  - name: state_changed
    payload:
      - name: state
        type: u8

errors:
  - name: invalid_state

The resolver adds internal system requests such as API identity probing. Application request IDs are kept separate from those system entries.

Registry and probing

The host-side registry maps API UUID and version to a resolved manifest. This lets tools probe a connected device without requiring the user to manually select a manifest.

Register an API YAML or resolved manifest:

mcurpc register api.yaml
mcurpc register manifest.json
mcurpc register api.yaml --registry ./registry --force

Show the default registry path:

mcurpc registry

Probe a device and print the matching manifest summary:

mcurpc probe serial -p /dev/ttyACM0 -b 115200
mcurpc probe --registry ./registry serial -p /dev/ttyACM0 -b 115200

The CLI also contains UDP frame transport support for host-side and test scenarios.

Interactive shell

The interactive shell probes the connected device, resolves the matching manifest and exposes manifest-driven commands.

mcurpc shell serial -p /dev/ttyACM0 -b 115200

Typical shell commands include:

info                  Show API summary
requests              List application requests
rpc <name> [options]  Call one manifest request
events                Show received events
logs                  Show received log messages
help                  Show command help
quit                  Exit the shell

The rpc command is generated from the manifest, so request parameters follow the fields declared in the API YAML.

Python client usage

The Python side can also be used directly. A manifest-driven client exposes generic calls and dynamic request methods built from the manifest.

from uuid import UUID

from mcurpc.api_info import ApiInfo
from mcurpc.client import build_client
from mcurpc.registry import ManifestRegistry
from mcurpc.transport import SerialTransport

transport = SerialTransport("/dev/ttyACM0", baudrate=115200)
transport.open()

api_info = ApiInfo(
    uuid=UUID("11111111-1111-4111-8111-111111111111"),
    version=1,
)

manifest = ManifestRegistry().find(api_info)

client = build_client(manifest, transport)
client.start()
try:
    response = client.call("open_door", {"duration_ms": 1000})
finally:
    client.stop()
    transport.close()

Check the current Python API before relying on helper names in external scripts, because the command line interface is the more stable integration surface at this stage.

Embedded integration

On the embedded side, generated request declarations are implemented by the application. The runtime calls these functions when matching request frames are received.

For PlatformIO projects, the generated configuration header is stored below the project include/ directory while the fixed MCURPC runtime is compiled from lib/MCURPC/. Add the project include directory to the build flags so the runtime can resolve mcurpc_config.h:

[env]
build_flags =
    -I${PROJECT_DIR}/include

The common [env] section applies the flag to all PlatformIO environments. It may also be placed in a specific [env:<name>] section when only one environment should use it.

For Arduino projects, the packaged MCURPC wrapper connects the runtime to an Arduino Stream:

#include "MCURPC.hpp"
#include "mcurpc_api.h"

MCURPC mcurpc(Serial);

void setup(void)
{
    Serial.begin(115200);
    mcurpc.begin();
}

void loop(void)
{
    mcurpc.process();
}

For plain C/CMake projects, call mcurpc_setup() once and mcurpc_loop() cyclically. Transport integration feeds RX bytes or frames into the runtime and consumes pending TX bytes or frames through the public transport functions.

Tests and local CI

The repository contains several test layers:

  • Python unit tests in tests/pyunittests/
  • CLI/system tests in tests/systemtests/
  • native C/C++ runtime tests in tests/cunittests/
  • generated-code compile checks in the system test cases

Common commands:

uv run pytest tests/pyunittests
uv run pytest tests/systemtests
uv run cikit ctest . --alias cunittests
uv run cikit all

cikit all runs the local workflow, including linting, Python tests, system tests, C tests, coverage steps and the Sphinx documentation build.

Documentation

The Sphinx documentation lives in docs/.

uv run cikit sphinx docs

Use the Sphinx docs for detailed runtime behavior, generator model details and build reports. The root README should stay focused on orientation and first-use workflows.

License

MCURPC is licensed under GPL-3.0-or-later.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mcurpc-0.3.2.tar.gz (120.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mcurpc-0.3.2-py3-none-any.whl (166.5 kB view details)

Uploaded Python 3

File details

Details for the file mcurpc-0.3.2.tar.gz.

File metadata

  • Download URL: mcurpc-0.3.2.tar.gz
  • Upload date:
  • Size: 120.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.7.15

File hashes

Hashes for mcurpc-0.3.2.tar.gz
Algorithm Hash digest
SHA256 0c6afec3092cf1c91ee41fe439ec1ae00704b178b97dd1ef9b5dc63719fed300
MD5 0ffeded6561f850cbcc171109e66be70
BLAKE2b-256 969a16db142d9f67d63705b730aa8b1dc9d27c37e8712fe673d672e7fd48a7fc

See more details on using hashes here.

File details

Details for the file mcurpc-0.3.2-py3-none-any.whl.

File metadata

  • Download URL: mcurpc-0.3.2-py3-none-any.whl
  • Upload date:
  • Size: 166.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.7.15

File hashes

Hashes for mcurpc-0.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 990533da9deab6a4b7d19c1778150642cf5fb7be05a3b54a5289aad4561aca26
MD5 d331543eb7bf0b17bae987a31c4a73aa
BLAKE2b-256 bce1e252412373de818aec2c150d45aa50a60022e9a87fc1fa9d465139b934cd

See more details on using hashes here.

Release history Release notifications | RSS feed

0.3.4

2 files

0.3.3

2 files

This release

0.3.2 This release

2 files

0.3.1

2 files

0.3.0

2 files

0.2.0

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page