Skip to content

feat(cli): --lower-dtr / --lower-rts (picocom-compat startup line control) #1

@TrekMax

Description

@TrekMax

Motivation

picocom -b 921600 /dev/ttyACM0 --lower-dtr --lower-rts is the canonical
"open the port without resetting the MCU" recipe for embedded developers
working with Arduino-style boards (where DTR/RTS are wired to the
reset / boot pins). Today rtcom has no equivalent, forcing users to
either:

  1. Open the port (which asserts both lines via the OS default), then
    manually press ^A t and ^A g to deassert. Two extra
    keystrokes plus a brief high-level pulse on each line — enough to
    reset some boards.
  2. Live with the reset.

Neither matches what picocom --lower-dtr --lower-rts does atomically
right after open().

Proposal

Add four boolean CLI flags mirroring picocom 1:1:

  • `--lower-dtr` — deassert DTR immediately after opening the device.
  • `--lower-rts` — deassert RTS immediately after opening the device.
  • `--raise-dtr` — assert DTR immediately after opening the device.
  • `--raise-rts` — assert RTS immediately after opening the device.

`--lower-X` and `--raise-X` for the same line are mutually exclusive
(clap can enforce via `conflicts_with`). Default behaviour is unchanged
from today (whatever the OS / driver gives us at `open()`, which is
typically both lines asserted).

Implementation sketch

`crates/rtcom-cli/src/args.rs`:

```rust
/// Deassert DTR immediately after opening the device.
#[arg(long, conflicts_with = "raise_dtr")]
pub lower_dtr: bool,

/// Assert DTR immediately after opening the device.
#[arg(long, conflicts_with = "lower_dtr")]
pub raise_dtr: bool,

// ... rts equivalents ...
```

`crates/rtcom-cli/src/main.rs::async_main` — between
`SerialPortDevice::open` and `Session::new`:

```rust
if cli.lower_dtr { device.set_dtr(false)?; }
else if cli.raise_dtr { device.set_dtr(true)?; }
if cli.lower_rts { device.set_rts(false)?; }
else if cli.raise_rts { device.set_rts(true)?; }
```

`crates/rtcom-core/src/session.rs` — accept the initial DTR/RTS state
via builders so the cached `dtr_asserted` / `rts_asserted` matches
reality and the first `^A t` toggle does the right thing:

```rust
session = session.with_initial_dtr(false).with_initial_rts(false);
```

Or simpler: have main set the lines directly and call
`session.with_initial_lines(dtr, rts)` (one builder, two args).

Acceptance

  • `rtcom /dev/ttyACM0 -b 921600 --lower-dtr --lower-rts --imap crlf`
    runs cleanly.
  • After startup, `^A t` flips DTR back to asserted (banner says
    `DTR: asserted`); next `^A t` deasserts again.
  • `--lower-dtr --raise-dtr` returns a clap error rather than
    doing the second one wins.
  • Unit tests in `args::tests` cover defaults (all four false),
    single-flag parses, mutual exclusion errors.
  • Integration test in `session_loopback.rs` sets initial lines
    via builder and asserts `session.dtr_asserted` / `rts_asserted`
    reflect the value.
  • README + man page list the four flags with picocom-equivalence
    callouts.
  • CHANGELOG `[Unreleased]` entry.

Out of scope (separate issue if anyone asks)

  • `--dtr=low|high|toggle` value-style flag. More flexible but the
    boolean pair already covers the picocom muscle-memory case; defer
    until someone needs the runtime-toggle-at-startup variant.
  • Honouring `--no-reset` semantics. `--no-reset` currently means
    "don't toggle DTR at startup"; with this issue landed it can be
    documented as equivalent to `--lower-dtr` for Arduino-reset wiring.

References

  • picocom(1) man page, `--lower-dtr` / `--lower-rts` / `--raise-*`
  • Real-hardware session that surfaced the gap: see the smoke-test
    log on local dev, late v0.1 polish.

Tracker: planned for v0.2.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions