Skip to content

feat: implement wire framing, RPC server/client, and CLI tool#1

Merged
tarotene merged 12 commits into
mainfrom
feat/claude-session-20260315-011025
May 20, 2026
Merged

feat: implement wire framing, RPC server/client, and CLI tool#1
tarotene merged 12 commits into
mainfrom
feat/claude-session-20260315-011025

Conversation

@tarotene

@tarotene tarotene commented Mar 14, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add COBS/rzCOBS framing layer to telepath-wire (framing.rs)
  • Implement target-side RPC server with transport abstraction in telepath-firmware
  • Implement host-side RPC client with frame size enforcement in telepath-host
  • Add telepath-cli tool crate for host-side serial communication
  • Update nrf52840-dk example with RTT transport integration
  • Harden request/response validation: kind checks, payload size limits, split PayloadTooLarge variants
  • Fix .gitignore to use **/target/ covering all subdirectories

Key Changes

Crate What changed
telepath-wire New framing module (COBS encode/decode, rzCOBS encode/decode, MAX_FRAME_SIZE)
telepath-firmware RPC server (poll-based dispatch), transport module, payload/kind validation
telepath-host RPC client (call_raw), frame size guard, response kind validation
telepath-cli New crate — serial port CLI with ping command
nrf52840-dk RTT transport, updated example wiring

Usage

Prerequisites

  • probe-rs installed (cargo install probe-rs-tools)
  • nRF52840-DK connected via USB
  • thumbv7em-none-eabi target: rustup target add thumbv7em-none-eabi
  • udev rules (Linux): USB access for SEGGER J-Link requires a udev rule
sudo tee /etc/udev/rules.d/99-probe-rs.rules <<'RULE'
# SEGGER J-Link
ATTR{idVendor}=="1366", MODE="0666", GROUP="plugdev"
RULE
sudo udevadm control --reload-rules && sudo udevadm trigger

1. Flash the firmware

cd examples/nrf52840-dk
cargo run --release

cargo run flashes the binary and opens an RTT session. The firmware
starts immediately; LED 1 blinks to indicate liveness.

Note: --allow-erase-all is included in the runner command
(see .cargo/config.toml). This is required on first flash when
APPROTECT (factory readback protection) is active; it has no effect
once the chip is unlocked.

2. Run the CLI

In a second terminal (firmware must remain running):

cd tools/telepath-cli
cargo build
./target/debug/telepath-cli ping

Expected output:

ping -> 0xDEADBEEF

To target a different chip:

telepath-cli --chip STM32F411RETx ping

3. Use telepath-host as a library

use telepath_host::TelepathClient;

// transport: anything implementing std::io::Read + std::io::Write
let mut client = TelepathClient::new(transport);

// Call CmdID 0x0001 (ping) with no arguments
let payload = client.call_raw(0x0001, &[])?;
let val: u32 = postcard::from_bytes(&payload)?;
println!("ping -> 0x{:08X}", val);  // ping -> 0xDEADBEEF

Test plan

  • cargo test --workspace passes
  • cargo clippy --workspace -- -D warnings clean
  • cd examples/nrf52840-dk && cargo build --release succeeds
  • cd tools/telepath-cli && cargo build succeeds

🤖 Generated with Claude Code

- Add framing layer (COBS/rzCOBS) to telepath-wire
- Implement transport and RPC server in telepath-firmware
- Implement host-side RPC client in telepath-host
- Add RTT transport and update nRF52840-DK example
- Add telepath-cli tool crate
- Fix .gitignore to use **/target/ covering all subdirectories

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 14, 2026 17:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates ignore rules intended to prevent committed Rust build artifacts, but also introduces substantial new Telepath RTT/COBS protocol plumbing across host, firmware, examples, and a new CLI tool.

Changes:

  • Update .gitignore to ignore target/ directories recursively.
  • Add COBS framing utilities and integrate RTT-based transport + polling server on firmware, plus corresponding host framing in TelepathClient.
  • Add telepath-cli and update the nRF52840-DK example to use RTT transport and a ping command.

Reviewed changes

Copilot reviewed 15 out of 16 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
.gitignore Switches to **/target/ ignore pattern.
Cargo.toml Excludes tools/telepath-cli from the workspace.
tools/telepath-cli/Cargo.toml Adds a standalone host-side CLI crate with probe-rs/clap deps.
tools/telepath-cli/src/main.rs Implements RTT ping command using probe-rs and COBS framing.
crates/telepath-wire/src/lib.rs Exposes new framing module.
crates/telepath-wire/src/framing.rs Adds COBS encode/decode helpers and a FrameAccumulator with tests.
crates/telepath-wire/Cargo.toml Adds cobs dependency (no_std).
crates/telepath-firmware/src/transport.rs Introduces non-blocking byte-stream Transport trait.
crates/telepath-firmware/src/lib.rs Adds RTT/COBS frame accumulation, request dispatch, and response framing in poll().
crates/telepath-firmware/Cargo.toml Adds serde and postcard dependencies to support serialization in firmware crate.
crates/telepath-host/src/lib.rs Implements COBS framing in TelepathClient::call_raw and adds tests.
crates/telepath-host/Cargo.toml Adds cobs and a firmware dev-dependency for integration tests.
examples/nrf52840-dk/Cargo.toml Switches RTT stack to rtt-target, adds postcard, changes panic impl.
examples/nrf52840-dk/.cargo/config.toml Removes defmt linker script flag.
examples/nrf52840-dk/src/rtt_transport.rs Adds RTT-backed Transport implementation for firmware server.
examples/nrf52840-dk/src/main.rs Initializes RTT channels, registers ping command, and polls server in loop.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/telepath-host/src/lib.rs
Comment thread crates/telepath-host/src/lib.rs
Comment thread crates/telepath-firmware/src/lib.rs
Comment thread examples/nrf52840-dk/src/main.rs
Comment thread crates/telepath-host/src/lib.rs
Comment thread crates/telepath-host/src/lib.rs Outdated
Comment thread crates/telepath-firmware/src/lib.rs Outdated
Comment thread tools/telepath-cli/src/main.rs
tarotene and others added 4 commits March 15, 2026 02:18
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Import MAX_FRAME_SIZE in telepath-host; add FrameTooLarge guard in the
  receive loop so an unbounded stream cannot exhaust host memory.
- Split PayloadTooLarge into RequestPayloadTooLarge (args too large before
  sending) and ResponsePayloadTooLarge (response payload exceeds limit).
- Validate resp.kind == PacketType::Response after deserialization; a packet
  with the wrong kind field now returns HostError::FramingError.
- Mirror the same MAX_FRAME_SIZE guard in telepath-cli cmd_ping.
- Update call_raw_rejects_oversized_args test to match new variant name.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Reject frames whose deserialized kind != PacketType::Request; this
  prevents a mislabeled or replayed Response packet from being dispatched.
- Reject requests whose args slice exceeds MAX_PAYLOAD_SIZE before dispatch,
  guarding handlers from unexpectedly large inputs.
- Clamp dispatcher return values > MAX_PAYLOAD_SIZE to SystemError instead
  of silently truncating, so the host always receives a valid bounded frame.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Expand the cortex_m::asm::delay comment to explain why blocking the
  Embassy executor is acceptable in this single-task demo and point to the
  correct async alternative (embassy_time::Timer) for multi-task firmware.
- Rename call_raw_ping_roundtrip → server_ping_roundtrip and update its
  doc comment to accurately reflect that it exercises server.poll() + manual
  encode/decode, not the call_raw() code path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@tarotene tarotene changed the title fix: remove committed build artifacts and fix .gitignore feat: implement wire framing, RPC server/client, and CLI tool Mar 14, 2026
tarotene and others added 2 commits March 15, 2026 03:30
Add self-contained usage documentation to each crate introduced in PR #1:
- crates/telepath-wire: protocol overview, key types, framing API, constants
- crates/telepath-firmware: architecture diagram, Transport trait, CommandMetadata usage
- crates/telepath-host: TelepathClient API, HostError variants, call_raw example
- tools/telepath-cli: prerequisites, build, ping subcommand, RTT channel layout
- examples/nrf52840-dk: prerequisites, build/flash steps, RTT channels, verify with CLI

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Document the SEGGER J-Link udev rule required for USB access on Linux,
and add --allow-erase-all to the probe-rs runner command to handle the
factory APPROTECT lock on nRF52840.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
tarotene and others added 2 commits March 15, 2026 10:18
rtt_init! requires down channel indices to start at 0 and be contiguous.
The previous config used only index 1, causing rtt_init_channels! to
write to channels.down[1] in a 1-element array (out-of-bounds UB) and
advertising num_down_channels=1, so the host's down_channel(1) call
returned None and the CLI bailed with "RTT down channel 1 not found".

Fix: add a 1-byte placeholder at down channel 0 so indices {0,1} are
contiguous. Channel 1 remains the telepath RPC channel; update the
RttTransport::new() call to use channels.down.1.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@tarotene

Copy link
Copy Markdown
Owner Author

Verification Checklist

Before merging, please run through the following two tiers.

Tier 1 — No hardware required (mirrors CI)

# Checkout this branch first
git fetch origin feat/claude-session-20260315-011025
git checkout feat/claude-session-20260315-011025

# A. Full workspace test suite — the loopback round-trips below MUST be green:
#    - crates/telepath-firmware  : poll_ping_roundtrip
#      (full COBS frame in → server.poll() → COBS frame out, 0xDEADBEEF asserted)
#    - crates/telepath-host      : server_ping_roundtrip
#      (validates wire compatibility across the host↔firmware boundary)
cargo test --workspace

# B. Lint / formatting
cargo fmt --all -- --check
cargo clippy --workspace -- -D warnings

# C. Firmware cross-compile
cd examples/nrf52840-dk && cargo build --release && cd -

# D. CLI build (workspace-excluded crate)
cd tools/telepath-cli && cargo build && cd -

All four steps pass in CI, but running them locally catches environment issues before merge.


Tier 2 — Real nRF52840-DK (optional but recommended)

Prerequisites

  • rustup target add thumbv7em-none-eabi

  • cargo install probe-rs-tools

  • Linux udev rule (first-time setup): add a rule for the SEGGER J-Link onboard the DK:

    sudo tee /etc/udev/rules.d/99-probe-rs.rules <<'RULE'
    # SEGGER J-Link
    ATTR{idVendor}=="1366", MODE="0666", GROUP="plugdev"
    RULE
    sudo udevadm control --reload-rules && sudo udevadm trigger
    

Note on --allow-erase-all: the runner in examples/nrf52840-dk/.cargo/config.toml
already includes this flag. It is required on the first flash when factory APPROTECT is
active and has no effect once the chip is unlocked.

Steps

# 1. Flash firmware (probe-rs download — probe released after flash completes)
cd examples/nrf52840-dk && cargo run --release && cd -

#    Visual check: LED1 (P0.13) should blink at ~5 Hz, indicating the
#    server.poll() loop is alive.

# 2. One-shot ping via CLI (open a second terminal while firmware runs)
cd tools/telepath-cli && cargo run -- ping && cd -
#    Expected stdout : ping -> 0xDEADBEEF
#    Expected stderr : RTT debug log from firmware (forwarded from ch 0)

# 3. REPL smoke test
cd tools/telepath-cli && cargo run
#    Type "ping"  → ping -> 0xDEADBEEF
#    Type "help"  → lists commands
#    Type "quit"  → clean exit

Troubleshooting

Symptom Cause Fix
RTT down channel 1 not found Reserved down channel 0 missing Confirm commit a69f241 is in history
Failed to attach to RTT APPROTECT not unlocked Ensure --allow-erase-all in runner; power-cycle board; re-flash
ping times out after 5 s server.poll() loop stalled Check RTT ch 0 stderr for panic messages

Once Tier 1 passes (Tier 2 if hardware is available), this PR is ready for squash merge to preserve Conventional Commit history.

tarotene and others added 2 commits May 21, 2026 00:37
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TelepathClient and its transport were created but never called;
the test drives the roundtrip by writing into the pipe directly
and calling server.poll(). Drop both unused bindings to clear
the unused_mut / unused_variables warnings in cargo test and clippy.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@tarotene
tarotene merged commit d98edd5 into main May 20, 2026
2 checks passed
@tarotene
tarotene deleted the feat/claude-session-20260315-011025 branch May 20, 2026 16:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants