Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,20 @@ cargo run --release -- --list
sermonizer [OPTIONS]

Options:
-p, --port <PORT> Serial port path
-b, --baud <BAUD> Baud rate (default: 115200)
--line-ending <E> Line ending: none|nl|cr|crlf (default: nl)
--hex Display data as hex
--log <FILE> Log received data
--tx-log <FILE> Log transmitted data
--log-ts Add timestamps to logs
--list List available ports
-p, --port <PORT> Serial port path
-b, --baud <BAUD> Baud rate (default: 115200)
--line-ending <E> Line ending: none|nl|cr|crlf (default: nl)
--data-bits <N> Data bits: 5|6|7|8 (default: 8)
--parity <P> Parity: none|odd|even (default: none)
--stop-bits <N> Stop bits: 1|2 (default: 1)
--flow-control <F> Flow control: none|software|hardware (default: none)
--dtr <on|off> Set the DTR line after opening
--rts <on|off> Set the RTS line after opening
--hex Display data as hex
--log <FILE> Log received data
--tx-log <FILE> Log transmitted data
--log-ts Add timestamps to logs and display
--list List available ports
```

## Controls
Expand Down
154 changes: 152 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use clap::ValueEnum;
use serialport::{ClearBuffer, SerialPort};
use serialport::{ClearBuffer, DataBits, FlowControl, Parity, SerialPort, StopBits};
use std::sync::{Arc, atomic::AtomicBool};
use std::time::Duration;

Expand All @@ -10,20 +10,170 @@ use crate::serial_io::WriterMsg;
pub struct PortSettings {
pub name: String,
pub baud: u32,
pub data_bits: DataBits,
pub parity: Parity,
pub stop_bits: StopBits,
pub flow_control: FlowControl,
pub dtr: Option<bool>,
pub rts: Option<bool>,
}

impl PortSettings {
pub fn open(&self) -> serialport::Result<Box<dyn SerialPort>> {
let port = serialport::new(&self.name, self.baud)
let mut port = serialport::new(&self.name, self.baud)
.data_bits(self.data_bits)
.parity(self.parity)
.stop_bits(self.stop_bits)
.flow_control(self.flow_control)
.timeout(Duration::from_millis(100))
.open()?;

// Control lines matter for boards that wire DTR/RTS to reset or boot
// pins (e.g. ESP32, Arduino); leave them untouched unless requested
if let Some(dtr) = self.dtr {
port.write_data_terminal_ready(dtr)?;
}
if let Some(rts) = self.rts {
port.write_request_to_send(rts)?;
}

// Drop any stale data buffered by the OS
port.clear(ClearBuffer::All)?;
Ok(port)
}
}

/// Explicit level for a control line
#[derive(Copy, Clone, Debug, ValueEnum)]
pub enum Toggle {
On,
Off,
}

impl Toggle {
pub fn as_bool(self) -> bool {
matches!(self, Toggle::On)
}
}

/// Number of data bits per character
#[derive(Copy, Clone, Debug, ValueEnum)]
pub enum DataBitsArg {
#[value(name = "5")]
Five,
#[value(name = "6")]
Six,
#[value(name = "7")]
Seven,
#[value(name = "8")]
Eight,
}

impl DataBitsArg {
pub fn label(self) -> &'static str {
match self {
DataBitsArg::Five => "5",
DataBitsArg::Six => "6",
DataBitsArg::Seven => "7",
DataBitsArg::Eight => "8",
}
}
}

impl From<DataBitsArg> for DataBits {
fn from(value: DataBitsArg) -> Self {
match value {
DataBitsArg::Five => DataBits::Five,
DataBitsArg::Six => DataBits::Six,
DataBitsArg::Seven => DataBits::Seven,
DataBitsArg::Eight => DataBits::Eight,
}
}
}

/// Parity checking mode
#[derive(Copy, Clone, Debug, ValueEnum)]
pub enum ParityArg {
None,
Odd,
Even,
}

impl ParityArg {
pub fn label(self) -> &'static str {
match self {
ParityArg::None => "N",
ParityArg::Odd => "O",
ParityArg::Even => "E",
}
}
}

impl From<ParityArg> for Parity {
fn from(value: ParityArg) -> Self {
match value {
ParityArg::None => Parity::None,
ParityArg::Odd => Parity::Odd,
ParityArg::Even => Parity::Even,
}
}
}

/// Number of stop bits
#[derive(Copy, Clone, Debug, ValueEnum)]
pub enum StopBitsArg {
#[value(name = "1")]
One,
#[value(name = "2")]
Two,
}

impl StopBitsArg {
pub fn label(self) -> &'static str {
match self {
StopBitsArg::One => "1",
StopBitsArg::Two => "2",
}
}
}

impl From<StopBitsArg> for StopBits {
fn from(value: StopBitsArg) -> Self {
match value {
StopBitsArg::One => StopBits::One,
StopBitsArg::Two => StopBits::Two,
}
}
}

/// Flow control mode
#[derive(Copy, Clone, Debug, ValueEnum)]
pub enum FlowControlArg {
None,
Software,
Hardware,
}

impl FlowControlArg {
pub fn label(self) -> &'static str {
match self {
FlowControlArg::None => "none",
FlowControlArg::Software => "software",
FlowControlArg::Hardware => "hardware",
}
}
}

impl From<FlowControlArg> for FlowControl {
fn from(value: FlowControlArg) -> Self {
match value {
FlowControlArg::None => FlowControl::None,
FlowControlArg::Software => FlowControl::Software,
FlowControlArg::Hardware => FlowControl::Hardware,
}
}
}

/// Which line ending to send when you press Enter
#[derive(Copy, Clone, Debug, ValueEnum)]
pub enum LineEnding {
Expand Down
60 changes: 45 additions & 15 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ mod ui;

use anyhow::{Context, Result};
use clap::Parser;
use config::{LineEnding, PortSettings, UiConfig};
use config::{
DataBitsArg, FlowControlArg, LineEnding, ParityArg, PortSettings, StopBitsArg, Toggle, UiConfig,
};
use crossterm::terminal;
use logging::LogSink;
use port_discovery::{choose_port_interactive, get_available_ports, print_ports};
Expand All @@ -28,14 +30,38 @@ struct Args {
#[arg(short, long)]
port: Option<String>,

/// Baud rate (default 115200)
#[arg(short = 'b', long)]
baud: Option<u32>,
/// Baud rate
#[arg(short = 'b', long, default_value_t = 115_200)]
baud: u32,

/// Line ending when you press Enter (none|nl|cr|crlf). Default: nl
#[arg(long, value_enum)]
line_ending: Option<LineEnding>,

/// Data bits per character
#[arg(long, value_enum, default_value = "8")]
data_bits: DataBitsArg,

/// Parity checking mode
#[arg(long, value_enum, default_value = "none")]
parity: ParityArg,

/// Stop bits
#[arg(long, value_enum, default_value = "1")]
stop_bits: StopBitsArg,

/// Flow control mode
#[arg(long, value_enum, default_value = "none")]
flow_control: FlowControlArg,

/// Set the DTR line after opening (left untouched if omitted)
#[arg(long, value_enum)]
dtr: Option<Toggle>,

/// Set the RTS line after opening (left untouched if omitted)
#[arg(long, value_enum)]
rts: Option<Toggle>,

/// Log received bytes to this file (appends)
#[arg(long)]
log: Option<PathBuf>,
Expand Down Expand Up @@ -79,17 +105,15 @@ async fn main() -> Result<()> {
};

// Decide on baud
let baud = match args.baud {
Some(b) => {
println!("Baud: {b}");
b
}
None => {
let b = 115_200u32;
println!("Baud: {b} (default)");
b
}
};
let baud = args.baud;
println!("Baud: {baud}");
println!(
"Framing: {}{}{}, flow control: {}",
args.data_bits.label(),
args.parity.label(),
args.stop_bits.label(),
args.flow_control.label()
);

// Line ending
let line_ending = args.line_ending.unwrap_or(LineEnding::Nl);
Expand All @@ -110,6 +134,12 @@ async fn main() -> Result<()> {
let settings = PortSettings {
name: port_name.clone(),
baud,
data_bits: args.data_bits.into(),
parity: args.parity.into(),
stop_bits: args.stop_bits.into(),
flow_control: args.flow_control.into(),
dtr: args.dtr.map(Toggle::as_bool),
rts: args.rts.map(Toggle::as_bool),
};
let port = settings
.open()
Expand Down
Loading
Loading