use std::io::Write;
use std::str;
use std::time::Duration;
use clap::{App, AppSettings, Arg};
use serialport::{ClearBuffer, DataBits, FlowControl, Parity, SerialPort, StopBits};
fn main() {
let matches = App::new("Serialport Example - Hardware Check")
.about("Test hardware capabilities of serial ports")
.setting(AppSettings::DisableVersion)
.arg(Arg::with_name("port")
.help("The device path to a serial port")
.use_delimiter(false)
.required(true))
.arg(Arg::with_name("loopback")
.help("Run extra tests if the port is configured for hardware loopback. Mutually exclusive with the --loopback-port option")
.use_delimiter(false)
.conflicts_with("loopback-port")
.long("loopback"))
.arg(Arg::with_name("loopback-port")
.help("The device path of a second serial port that is connected to the first serial port. Mutually exclusive with the --loopback option.")
.use_delimiter(false)
.takes_value(true)
.long("loopback-port"))
.get_matches();
let port1_name = matches.value_of("port").unwrap();
let port2_name = matches.value_of("loopback-port").unwrap_or("");
let port1_loopback = matches.is_present("loopback");
if port1_loopback && port2_name != "" {
eprintln!("ERROR: loopback mode can only be enabled when a single port is specified.");
::std::process::exit(1);
}
let mut port1 = match serialport::new(port1_name, 9600).open() {
Err(e) => {
eprintln!("Failed to open \"{}\". Error: {}", port1_name, e);
::std::process::exit(1);
}
Ok(p) => p,
};
test_single_port(&mut *port1, port1_loopback);
if port2_name != "" {
let mut port2 = match serialport::new(port2_name, 9600).open() {
Err(e) => {
eprintln!("Failed to open \"{}\". Error: {}", port2_name, e);
::std::process::exit(1);
}
Ok(p) => p,
};
test_single_port(&mut *port2, false);
test_dual_ports(&mut *port1, &mut *port2);
}
}
macro_rules! baud_rate_check {
($port:ident, $baud:expr) => {
let baud_rate = $baud;
if let Err(e) = $port.set_baud_rate(baud_rate) {
println!(" {:?}: FAILED ({})", baud_rate, e);
}
match $port.baud_rate() {
Err(_) => println!(" {:?}: FAILED (error retrieving baud rate)", baud_rate),
Ok(r) if r != baud_rate => println!(
" {:?}: FAILED (baud rate {:?} does not match set baud rate {:?})",
baud_rate, r, baud_rate
),
Ok(_) => println!(" {:?}: success", baud_rate),
}
};
}
macro_rules! data_bits_check {
($port:ident, $data_bits:path) => {
let data_bits = $data_bits;
if let Err(e) = $port.set_data_bits(data_bits) {
println!(" {:?}: FAILED ({})", data_bits, e);
} else {
match $port.data_bits() {
Err(_) => println!("FAILED to retrieve data bits"),
Ok(r) if r != data_bits => println!(
" {:?}: FAILED (data bits {:?} does not match set data bits {:?})",
data_bits, r, data_bits
),
Ok(_) => println!(" {:?}: success", data_bits),
}
}
};
}
macro_rules! flow_control_check {
($port:ident, $flow_control:path) => {
let flow_control = $flow_control;
if let Err(e) = $port.set_flow_control(flow_control) {
println!(" {:?}: FAILED ({})", flow_control, e);
} else {
match $port.flow_control() {
Err(_) => println!("FAILED to retrieve flow control"),
Ok(r) if r != flow_control => println!(
" {:?}: FAILED (flow control {:?} does not match set flow control {:?})",
flow_control, r, flow_control
),
Ok(_) => println!(" {:?}: success", flow_control),
}
}
};
}
macro_rules! parity_check {
($port:ident, $parity:path) => {
let parity = $parity;
if let Err(e) = $port.set_parity(parity) {
println!(" {:?}: FAILED ({})", parity, e);
} else {
match $port.parity() {
Err(_) => println!("FAILED to retrieve parity"),
Ok(r) if r != parity => println!(
" {:?}: FAILED (parity {:?} does not match set parity {:?})",
parity, r, parity
),
Ok(_) => println!(" {:?}: success", parity),
}
}
};
}
macro_rules! stop_bits_check {
($port:ident, $stop_bits:path) => {
let stop_bits = $stop_bits;
if let Err(e) = $port.set_stop_bits(stop_bits) {
println!(" {:?}: FAILED ({})", stop_bits, e);
} else {
match $port.stop_bits() {
Err(_) => println!("FAILED to retrieve stop bits"),
Ok(r) if r != stop_bits => println!(
"FAILED, stop bits {:?} does not match set stop bits {:?}",
r, stop_bits
),
Ok(_) => println!(" {:?}: success", stop_bits),
}
}
};
}
macro_rules! clear_check {
($port:ident, $buffer_direction:path) => {
let buffer_direction = $buffer_direction;
match $port.clear(buffer_direction) {
Ok(_) => println!(" {:?}: success", buffer_direction),
Err(ref e) => println!(" {:?}: FAILED ({})", buffer_direction, e),
}
};
}
macro_rules! call_query_method_check {
($port:ident, $func:path) => {
match $func($port) {
Ok(_) => println!(" {}: success", stringify!($func)),
Err(ref e) => println!(" {}: FAILED ({})", stringify!($func), e),
}
};
}
fn test_single_port(port: &mut dyn serialport::SerialPort, loopback: bool) {
println!("Testing '{}':", port.name().unwrap());
println!("Testing baud rates...");
baud_rate_check!(port, 9600);
baud_rate_check!(port, 38_400);
baud_rate_check!(port, 115_200);
println!("Testing non-standard baud rates...");
baud_rate_check!(port, 10_000);
baud_rate_check!(port, 600_000);
baud_rate_check!(port, 1_800_000);
println!("Testing data bits...");
data_bits_check!(port, DataBits::Five);
data_bits_check!(port, DataBits::Six);
data_bits_check!(port, DataBits::Seven);
data_bits_check!(port, DataBits::Eight);
println!("Testing flow control...");
flow_control_check!(port, FlowControl::Software);
flow_control_check!(port, FlowControl::Hardware);
flow_control_check!(port, FlowControl::None);
println!("Testing parity...");
parity_check!(port, Parity::Odd);
parity_check!(port, Parity::Even);
parity_check!(port, Parity::None);
println!("Testing stop bits...");
stop_bits_check!(port, StopBits::Two);
stop_bits_check!(port, StopBits::One);
println!("Testing bytes to read and write...");
call_query_method_check!(port, SerialPort::bytes_to_write);
call_query_method_check!(port, SerialPort::bytes_to_read);
println!("Test clearing software buffers...");
clear_check!(port, ClearBuffer::Input);
clear_check!(port, ClearBuffer::Output);
clear_check!(port, ClearBuffer::All);
print!("Testing data transmission...");
std::io::stdout().flush().unwrap();
set_defaults(port);
let msg = "Test Message";
port.write_all(msg.as_bytes())
.expect("Unable to write bytes.");
println!("success");
print!("Testing data reception...");
if loopback {
let mut buf = [0u8; 12];
if port.read_exact(&mut buf).is_err() {
println!("FAILED");
} else {
assert_eq!(
str::from_utf8(&buf).unwrap(),
msg,
"Received message does not match sent"
);
println!("success");
}
}
}
fn test_dual_ports(port1: &mut dyn serialport::SerialPort, port2: &mut dyn serialport::SerialPort) {
println!(
"Testing paired ports '{}' and '{}':",
port1.name().unwrap(),
port2.name().unwrap()
);
set_defaults(port1);
set_defaults(port2);
let msg = "Test Message";
let mut buf = [0u8; 12];
println!(
" Transmitting from {} to {}...",
port1.name().unwrap(),
port2.name().unwrap()
);
let baud_rate = 2_000_000;
print!(" At {},8,n,1,noflow...", baud_rate);
std::io::stdout().flush().unwrap();
if port1.set_baud_rate(baud_rate).is_ok() && port2.set_baud_rate(baud_rate).is_ok() {
port1
.write_all(msg.as_bytes())
.expect("Unable to write bytes.");
if port2.read_exact(&mut buf).is_err() {
println!("FAILED");
} else {
assert_eq!(
str::from_utf8(&buf).unwrap(),
msg,
"Received message does not match sent"
);
println!("success");
}
} else {
println!("FAILED (does this platform & port support arbitrary baud rates?)");
}
let baud_rate = 115_200;
print!(" At {},8,n,1,noflow...", baud_rate);
std::io::stdout().flush().unwrap();
if port1.set_baud_rate(baud_rate).is_ok() && port2.set_baud_rate(baud_rate).is_ok() {
port1
.write_all(msg.as_bytes())
.expect("Unable to write bytes.");
if port2.read_exact(&mut buf).is_err() {
println!("FAILED");
} else {
assert_eq!(
str::from_utf8(&buf).unwrap(),
msg,
"Received message does not match sent"
);
println!("success");
}
} else {
println!("FAILED");
}
let baud_rate = 57_600;
print!(" At {},8,n,1,noflow...", baud_rate);
std::io::stdout().flush().unwrap();
if port1.set_baud_rate(baud_rate).is_ok() && port2.set_baud_rate(baud_rate).is_ok() {
port1
.write_all(msg.as_bytes())
.expect("Unable to write bytes.");
if port2.read_exact(&mut buf).is_err() {
println!("FAILED");
} else {
assert_eq!(
str::from_utf8(&buf).unwrap(),
msg,
"Received message does not match sent"
);
println!("success");
}
} else {
println!("FAILED");
}
let baud_rate = 10_000;
print!(" At {},8,n,1,noflow...", baud_rate);
std::io::stdout().flush().unwrap();
if port1.set_baud_rate(baud_rate).is_ok() && port2.set_baud_rate(baud_rate).is_ok() {
port1
.write_all(msg.as_bytes())
.expect("Unable to write bytes.");
if port2.read_exact(&mut buf).is_err() {
println!("FAILED");
} else {
assert_eq!(
str::from_utf8(&buf).unwrap(),
msg,
"Received message does not match sent"
);
println!("success");
}
} else {
println!("FAILED (does this platform & port support arbitrary baud rates?)");
}
let baud_rate = 9600;
print!(" At {},8,n,1,noflow...", baud_rate);
std::io::stdout().flush().unwrap();
if port1.set_baud_rate(baud_rate).is_ok() && port2.set_baud_rate(baud_rate).is_ok() {
port1
.write_all(msg.as_bytes())
.expect("Unable to write bytes.");
if port2.read_exact(&mut buf).is_err() {
println!("FAILED");
} else {
assert_eq!(
str::from_utf8(&buf).unwrap(),
msg,
"Received message does not match sent"
);
println!("success");
}
} else {
println!("FAILED");
}
port1.set_flow_control(FlowControl::Software).unwrap();
port2.set_flow_control(FlowControl::Software).unwrap();
print!(" At 9600,8,n,1,softflow...");
std::io::stdout().flush().unwrap();
port2
.write_all(msg.as_bytes())
.expect("Unable to write bytes.");
if port1.read_exact(&mut buf).is_err() {
println!("FAILED");
} else {
assert_eq!(
str::from_utf8(&buf).unwrap(),
msg,
"Received message does not match sent"
);
println!("success");
}
port1.set_flow_control(FlowControl::Hardware).unwrap();
port2.set_flow_control(FlowControl::Hardware).unwrap();
print!(" At 9600,8,n,1,hardflow...");
std::io::stdout().flush().unwrap();
port2
.write_all(msg.as_bytes())
.expect("Unable to write bytes.");
if port1.read_exact(&mut buf).is_err() {
println!("FAILED");
} else {
assert_eq!(
str::from_utf8(&buf).unwrap(),
msg,
"Received message does not match sent"
);
println!("success");
}
}
fn set_defaults(port: &mut dyn serialport::SerialPort) {
port.set_baud_rate(9600).unwrap();
port.set_data_bits(DataBits::Eight).unwrap();
port.set_flow_control(FlowControl::Software).unwrap();
port.set_parity(Parity::None).unwrap();
port.set_stop_bits(StopBits::One).unwrap();
port.set_timeout(Duration::from_millis(0)).unwrap();
}