122 aimdb serial connector cobs framed serialuart transport tokio serial embedded io async#126
Merged
lxsaah merged 5 commits intoJun 6, 2026
Conversation
- Implemented roundtrip tests for COBS framing in `cobs_framing.rs` to ensure proper encoding and decoding of frames regardless of chunk sizes. - Added tests for single frame, byte-by-byte delivery, multiple frames across chunk boundaries, and empty payloads. - Verified that encoded frames do not contain the sentinel byte except as a terminator. feat: implement Embassy client-exit smoke test - Created `embassy_smoke.rs` to test the RPC engine over the real Embassy serial transport using a loopback UART. - Validated that the engine can handle requests and responses correctly, ensuring proper COBS framing and decoding. feat: add Tokio roundtrip test for AimX over serial transport - Developed `tokio_roundtrip.rs` to test end-to-end communication using a duplex stream as a mock serial connection. - Ensured that the AimX server and client can communicate correctly over the serial transport, verifying COBS framing in both directions. chore: initialize embassy-serial-connector-demo example - Created a new example project demonstrating AimX over serial on STM32H563ZI. - Configured the project with necessary dependencies and build settings for the STM32 platform. - Added README documentation for setup and usage instructions, including hardware requirements and troubleshooting tips. chore: add build script and flash script for demo - Implemented `build.rs` to configure linker arguments for the STM32 target. - Added `flash.sh` script to facilitate flashing the firmware to the STM32H563ZI using probe-rs. chore: set up .cargo/config.toml and .gitignore for demo - Configured `.cargo/config.toml` for the STM32 target and added a `.gitignore` file to exclude build artifacts.
Closed
9 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
aimdb-serial-connector— COBS-framed serial/UART transportCloses #122 (follow-up to #39 / #120). Design: doc 041 — phase 6 embedded transports.
Overview
A new transport connector — the serial sibling of
aimdb-uds-connector— that lets aboard speak the AimX remote-access protocol over a serial line. A sensor MCU can dial
a gateway over UART, and (riding the
no_stdAimxDispatchfrom #120) an MCU can alsoserve a host over UART.
The crate contributes only the transport: the
Dialer/Listener/Connectiontriple plusthin
SerialClient/SerialServersugar over theserial://scheme. The AimX codec,dispatch, and the runtime-neutral session engines (
run_client/serve) are reusedverbatim from
aimdb-core.Wire protocol
Same compact AimX JSON as UDS, but framed with COBS (Consistent Overhead Byte Stuffing)
and a
0x00delimiter instead of a newline. COBS rewrites a payload so it never contains a0x00, then a single0x00marks the frame boundary — self-synchronizing on a lossy,unframed serial medium, so a receiver that joins mid-stream resynchronizes on the next
sentinel. AimX JSON never contains a raw
0x00, so overhead is one byte per ~254.Two runtime halves
tokio-runtimeTokioSerialConnection<S>over anyAsyncRead + AsyncWrite(realtokio_serial::SerialStream, ortokio::io::duplex()in tests)SessionClientConnector/SessionServerConnector.embassy-runtimeno_std + alloc, MCUEmbassySerialConnection<Rd, Wr>generic overembedded-io-asyncRead/Writehalves (theUart::split()shape)ConnectorBuilder, force-Sends the single-core futures viaaimdb-embassy-adapter'sSendFutureWrapper. Reconnect off by default (the UART peripheral is moved in).The Embassy half is the first raw-peripheral session connector — MQTT/KNX sidestep the
Send + Syncproblem by pulling aSend + Syncembassy_net::Stackfrom the runtimeadapter rather than owning a peripheral. The
unsafe impl Send/Synchere rest on the samesingle-core, cooperative Embassy-executor invariant
SendFutureWrapperdocuments (nopreemption / thread migration).
Key components
framingmodule — the shared COBS frame codec:encode_frameplus a chunk-tolerantFrameAccumulator. Pureno_std + alloc, so the round-trip is unit-tested independent ofany transport.
SerialClient/SerialServersugar on both halves;apply_writablemirrors the UDSconnector (marks the policy's writable records so
record.listadvertises thewritableflag).
Robustness
FrameAccumulatorcaps un-delimited buffering atDEFAULT_MAX_FRAME(8 KiB); a longer run with no sentinel is treated as a desync, dropped,and resynced on the next sentinel — so a stuck/garbage stream can't grow the buffer toward
an OOM on a small embedded heap. (
with_max_frameallows a custom cap.)recvdrops a chunk that fails to COBS-decode(line noise, or a mid-stream join) and resyncs rather than tearing down the session —
transient corruption costs one frame, not the whole connection. This matters most on
Embassy, where
reconnect: falseover a moved-in UART means a fatal read error couldn'trecover.
BufferedUart::writeisatomic-or-error (
embassy-stm32returnsBufferTooLongfor a single write larger than itsTX ring), so a frame bigger than the buffer (e.g. a
record.listreply) is split.across opens, so a half-read reply from a prior (killed) session won't desync the first
frame.
Examples
examples/serial_demo.rs(host,--features _test-tokio) — AimX client/server over adevice path (a board's ST-LINK VCP at
/dev/ttyACM0, or asocatPTY pair for ano-hardware smoke). Modes:
client,server,set(write path),raw(low-level debug).examples/embassy-serial-connector-demo/— STM32H563ZI Nucleo firmware serving thecounter(read-only) andsetting(writable) records over USART3 ↔ the ST-LINK VirtualCOM Port. The
no_stdSerialServer+AimxDispatchon real silicon, flashed viaprobe-rs, queried from the host over the wire.Testing
cobs_framing.rs— COBS round-trips across arbitrary chunk boundaries (incl. byte-by-bytedelivery), empty payloads, leading-sentinel resync, buffer-cap overflow + resync, and
large-frame pass-through.
tokio_roundtrip.rs— end-to-end AimX (record.get/record.list) over atokio::io::duplex()pipe (productionserve+AimxDispatchansweringrun_client),plus a corrupt-frame resync test.
embassy_smoke.rs— the exactrun_client<SerialDialer, _, EmbassyAdapter>monomorphization an MCU uses, driven on the host over a real loopback UART.
make testruns both halves;make test-embeddedcross-checks the Embassy halfon
thumbv7em-none-eabihf;make clippylints both (tokio + embassy + embassy/defmt).Hardware-validated end-to-end on an STM32H563ZI Nucleo over the ST-LINK VCP:
record.list/record.get/record.setand streaming subscriptions all round-tripMCU↔host.