Serial debugging is still central in embedded work, and Rust is excellent for building reliable terminal tooling.
I structure serial utilities as pipelines: read bytes, frame messages, parse protocol, then route structured events to output sinks. This makes it easy to swap text logs for JSON output without rewriting core logic.
Error handling is explicit at each stage. Timeouts, framing errors, and invalid payloads are separate variants, so operators can react correctly.
Using clap for arguments and serialport for transport, I can ship cross-platform tools that feel consistent and fail loudly when configuration is wrong.
Arguments with clap
I keep the CLI surface small and derive it so --help stays honest:
use clap::Parser;
#[derive(Parser)]
#[command(about = "Read framed messages from a serial port")]
struct Args {
/// Serial device, e.g. /dev/ttyUSB0 or COM3
#[arg(short, long)]
port: String,
/// Baud rate
#[arg(short, long, default_value_t = 115_200)]
baud: u32,
/// Per-read timeout in milliseconds
#[arg(long, default_value_t = 200)]
timeout_ms: u64,
}
A read loop with a timeout
The important detail is the read timeout. Serial reads that block forever make a tool feel hung and impossible to Ctrl-C cleanly; a bounded timeout turns "no data yet" into an ordinary, recoverable case instead of a stall.
use std::io::{ErrorKind, Read};
use std::time::Duration;
fn main() -> anyhow::Result<()> {
let args = Args::parse();
let mut port = serialport::new(&args.port, args.baud)
.timeout(Duration::from_millis(args.timeout_ms))
.open()?;
let mut buf = [0u8; 1024];
loop {
match port.read(&mut buf) {
Ok(0) => continue,
Ok(n) => handle_bytes(&buf[..n]),
// A timeout is normal: nothing arrived this window, loop again.
Err(e) if e.kind() == ErrorKind::TimedOut => continue,
Err(e) => return Err(e.into()),
}
}
}
handle_bytes feeds the framing/parsing pipeline described above. Because the timeout branch is explicit, the loop stays responsive and reserves real error handling for actual transport failures.
Sources
- Imported from the previous version of the site (gaborl.hu). Original publication around 2024-11-03.
Update history
- — Adopted from previous gaborl.hu (Hugo) site via legacy import.