Expand description
§embedded-rpc
no_std request/response synchronization for async tasks (Embassy-style executors). This is not a wire protocol: it is an in-memory channel built on embassy_sync mutexes and async wakers.
Crate rename: This crate was previously published on crates.io as embassy-rpc. Depend on embedded-rpc and use the embedded_rpc crate root instead of embassy_rpc.
§What this crate does
- Single in-flight RPC: At most one request and one response are queued in the internal state machine.
- Client:
RpcService::requestsends a request and awaitsResult<Resp, RequestDroppedError>. - Server:
RpcService::servewaits for the next request and returns(Req, ServedRequest). The server must callServedRequest::respondwith a success value, or drop the handle to signal failure to the client. - Multiple clients: Several tasks may call
request(), but they are serialized: a second caller blocks until the previous RPC fully completes (response delivered and the client slot released).
§Usage
- Create a shared
RpcService<M, Req, Resp>with a mutex typeM: RawMutexappropriate for your platform (e.g.CriticalSectionRawMutexon many MCUs). - Run a server task that loops on
serve().await, inspects or mutates theReqvalue from the tuple, then callsrespond(resp)on theServedRequest. - Run client task(s) that call
request(req).awaitand handleOk(resp)vsErr(RequestDroppedError).
Req and Resp are yours: they can be integers, enums, or types that borrow data from the client for the duration of the call (see the example below).
§Example (simple types)
use embedded_rpc::RpcService;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
static SERVICE: RpcService<CriticalSectionRawMutex, u32, u32> = RpcService::new();
async fn server() {
loop {
let (req, served) = SERVICE.serve().await;
served.respond(req.saturating_add(1));
}
}
async fn client() {
match SERVICE.request(5).await {
Ok(n) => assert_eq!(n, 6),
Err(embedded_rpc::RequestDroppedError) => { /* server dropped the request */ }
}
}§Example (client lends a buffer)
Req can carry &mut [u8] (or other borrows) so the server writes into the client’s memory for the duration of one RPC. The RpcService must live no longer than the borrowed data, so keep it on the stack (or in an owning struct) together with the buffer—see the server_writes_through_client_buffer_slice integration test in this repository.
use embedded_rpc::RpcService;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
struct BufferRequest<'a> {
buffer: &'a mut [u8],
}
async fn server_task(service: &RpcService<CriticalSectionRawMutex, BufferRequest<'_>, ()>) {
let (mut req, served) = service.serve().await;
req.buffer[0] = 0xab;
served.respond(());
}
async fn client_task(
service: &RpcService<CriticalSectionRawMutex, BufferRequest<'_>, ()>,
buf: &mut [u8],
) -> Result<(), embedded_rpc::RequestDroppedError> {
service.request(BufferRequest { buffer: buf }).await
}Run server_task and client_task concurrently on the same service (spawn or join, depending on your executor).
§Considerations
RequestDroppedError: TreatErr(RequestDroppedError)as “no normal response” (server droppedServedRequestwithout callingrespond). Do not assumeOkif the server may abort handling.ServedRequest: Afterrespond, the completion handle is consumed. Dropping it without callingrespondcompletes the RPC withRequestDroppedErrorfor the client.- Lifetimes: If
Reqborrows from the client (e.g.&mut [u8]), theRpcServicevalue must not outlive those borrows. A stack-scoped service is often the right shape;Arc<RpcService<...>>with stack-borrowed payloads is usually incompatible without restructuring. - No built-in timeouts: Compose with your executor’s timeout helpers if you need deadlines on
request()orserve(). - Integration tests and
ThreadModeRawMutex: Withembassy-sync’sstdfeature,ThreadModeRawMutexmay only be used from a thread named"main". The crate’s tests spawn such a thread; on embedded firmware, prefer a mutex that matches your interrupt/preemption model.
§License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Structs§
- Request
Dropped Error - Error returned to the client when the server drops
ServedRequestwithout callingServedRequest::respond. - RpcService
- In-memory request/response synchronization for async tasks.
- Served
Request - Server-side completion handle for one request taken from
RpcService::serve.