1 //! This example performs a test using real hardware ports.
2 //!
3 //! This tool serves as a debugging aid when encountering errors using `serialport-rs`. It should
4 //! expose any kernel or driver bugs that your system may have by running physical ports through
5 //! many configurations.
6 //!
7 //! There are 3 ways to run this example:
8 //!
9 //! 1) With a single port not connected to an external device:
10 //! `cargo run --example hardware_check /dev/ttyUSB0`
11 //!
12 //! 2) With a single port physically connected in loopback mode (RX<->TX)
13 //! `cargo run --example hardware_check /dev/ttyUSB0 --loopback`
14 //!
15 //! 3) With two ports physically connected to each other
16 //! `cargo run --example hardware_check /dev/ttyUSB0 /dev/ttyUSB1`
17
18 use std::io::Write;
19 use std::str;
20 use std::time::Duration;
21
22 use assert_hex::assert_eq_hex;
23 use clap::{Arg, Command};
24
25 use serialport::{ClearBuffer, DataBits, FlowControl, Parity, SerialPort, StopBits};
26
27 const TEST_MESSAGE: &[u8] = "Test Message".as_bytes();
28
main()29 fn main() {
30 let matches = Command::new("Serialport Example - Hardware Check")
31 .about("Test hardware capabilities of serial ports")
32 .disable_version_flag(true)
33 .arg(Arg::new("port")
34 .help("The device path to a serial port")
35 .use_value_delimiter(false)
36 .required(true))
37 .arg(Arg::new("loopback")
38 .help("Run extra tests if the port is configured for hardware loopback. Mutually exclusive with the --loopback-port option")
39 .use_value_delimiter(false)
40 .conflicts_with("loopback-port")
41 .long("loopback"))
42 .arg(Arg::new("loopback-port")
43 .help("The device path of a second serial port that is connected to the first serial port. Mutually exclusive with the --loopback option.")
44 .use_value_delimiter(false)
45 .takes_value(true)
46 .long("loopback-port"))
47 .get_matches();
48
49 let port1_name = matches.value_of("port").unwrap();
50 let port2_name = matches.value_of("loopback-port").unwrap_or("");
51 let port1_loopback = matches.is_present("loopback");
52
53 // Loopback mode is only available when a single port is specified
54 if port1_loopback && !port2_name.is_empty() {
55 eprintln!("ERROR: loopback mode can only be enabled when a single port is specified.");
56 ::std::process::exit(1);
57 }
58
59 // Run single-port tests on port1
60 let mut port1 = match serialport::new(port1_name, 9600).open() {
61 Err(e) => {
62 eprintln!("Failed to open \"{}\". Error: {}", port1_name, e);
63 ::std::process::exit(1);
64 }
65 Ok(p) => p,
66 };
67 test_single_port(&mut *port1, port1_loopback);
68
69 if !port2_name.is_empty() {
70 // Run single-port tests on port2
71 let mut port2 = match serialport::new(port2_name, 9600).open() {
72 Err(e) => {
73 eprintln!("Failed to open \"{}\". Error: {}", port2_name, e);
74 ::std::process::exit(1);
75 }
76 Ok(p) => p,
77 };
78 test_single_port(&mut *port2, false);
79
80 // Test loopback pair
81 test_dual_ports(&mut *port1, &mut *port2);
82 }
83 }
84
85 macro_rules! baud_rate_check {
86 ($port:ident, $baud:expr) => {
87 let baud_rate = $baud;
88 if let Err(e) = $port.set_baud_rate(baud_rate) {
89 println!(" {:?}: FAILED ({})", baud_rate, e);
90 }
91 match $port.baud_rate() {
92 Err(_) => println!(" {:?}: FAILED (error retrieving baud rate)", baud_rate),
93 Ok(r) if r != baud_rate => println!(
94 " {:?}: FAILED (baud rate {:?} does not match set baud rate {:?})",
95 baud_rate, r, baud_rate
96 ),
97 Ok(_) => println!(" {:?}: success", baud_rate),
98 }
99 };
100 }
101
102 macro_rules! data_bits_check {
103 ($port:ident, $data_bits:path) => {
104 let data_bits = $data_bits;
105 if let Err(e) = $port.set_data_bits(data_bits) {
106 println!(" {:?}: FAILED ({})", data_bits, e);
107 } else {
108 match $port.data_bits() {
109 Err(_) => println!("FAILED to retrieve data bits"),
110 Ok(r) if r != data_bits => println!(
111 " {:?}: FAILED (data bits {:?} does not match set data bits {:?})",
112 data_bits, r, data_bits
113 ),
114 Ok(_) => println!(" {:?}: success", data_bits),
115 }
116 }
117 };
118 }
119
120 macro_rules! flow_control_check {
121 ($port:ident, $flow_control:path) => {
122 let flow_control = $flow_control;
123 if let Err(e) = $port.set_flow_control(flow_control) {
124 println!(" {:?}: FAILED ({})", flow_control, e);
125 } else {
126 match $port.flow_control() {
127 Err(_) => println!("FAILED to retrieve flow control"),
128 Ok(r) if r != flow_control => println!(
129 " {:?}: FAILED (flow control {:?} does not match set flow control {:?})",
130 flow_control, r, flow_control
131 ),
132 Ok(_) => println!(" {:?}: success", flow_control),
133 }
134 }
135 };
136 }
137
138 macro_rules! parity_check {
139 ($port:ident, $parity:path) => {
140 let parity = $parity;
141 if let Err(e) = $port.set_parity(parity) {
142 println!(" {:?}: FAILED ({})", parity, e);
143 } else {
144 match $port.parity() {
145 Err(_) => println!("FAILED to retrieve parity"),
146 Ok(r) if r != parity => println!(
147 " {:?}: FAILED (parity {:?} does not match set parity {:?})",
148 parity, r, parity
149 ),
150 Ok(_) => println!(" {:?}: success", parity),
151 }
152 }
153 };
154 }
155
156 macro_rules! stop_bits_check {
157 ($port:ident, $stop_bits:path) => {
158 let stop_bits = $stop_bits;
159 if let Err(e) = $port.set_stop_bits(stop_bits) {
160 println!(" {:?}: FAILED ({})", stop_bits, e);
161 } else {
162 match $port.stop_bits() {
163 Err(_) => println!("FAILED to retrieve stop bits"),
164 Ok(r) if r != stop_bits => println!(
165 "FAILED, stop bits {:?} does not match set stop bits {:?}",
166 r, stop_bits
167 ),
168 Ok(_) => println!(" {:?}: success", stop_bits),
169 }
170 }
171 };
172 }
173
174 macro_rules! clear_check {
175 ($port:ident, $buffer_direction:path) => {
176 let buffer_direction = $buffer_direction;
177 match $port.clear(buffer_direction) {
178 Ok(_) => println!(" {:?}: success", buffer_direction),
179 Err(ref e) => println!(" {:?}: FAILED ({})", buffer_direction, e),
180 }
181 };
182 }
183
184 macro_rules! call_query_method_check {
185 ($port:ident, $func:path) => {
186 match $func($port) {
187 Ok(_) => println!(" {}: success", stringify!($func)),
188 Err(ref e) => println!(" {}: FAILED ({})", stringify!($func), e),
189 }
190 };
191 }
192
test_single_port(port: &mut dyn serialport::SerialPort, loopback: bool)193 fn test_single_port(port: &mut dyn serialport::SerialPort, loopback: bool) {
194 println!("Testing '{}':", port.name().unwrap());
195
196 // Test setting standard baud rates
197 println!("Testing baud rates...");
198 baud_rate_check!(port, 9600);
199 baud_rate_check!(port, 38_400);
200 baud_rate_check!(port, 115_200);
201
202 // Test setting non-standard baud rates
203 println!("Testing non-standard baud rates...");
204 baud_rate_check!(port, 10_000);
205 baud_rate_check!(port, 600_000);
206 baud_rate_check!(port, 1_800_000);
207
208 // Test setting the data bits
209 println!("Testing data bits...");
210 data_bits_check!(port, DataBits::Five);
211 data_bits_check!(port, DataBits::Six);
212 data_bits_check!(port, DataBits::Seven);
213 data_bits_check!(port, DataBits::Eight);
214
215 // Test setting flow control
216 println!("Testing flow control...");
217 flow_control_check!(port, FlowControl::Software);
218 flow_control_check!(port, FlowControl::Hardware);
219 flow_control_check!(port, FlowControl::None);
220
221 // Test setting parity
222 println!("Testing parity...");
223 parity_check!(port, Parity::Odd);
224 parity_check!(port, Parity::Even);
225 parity_check!(port, Parity::None);
226
227 // Test setting stop bits
228 println!("Testing stop bits...");
229 stop_bits_check!(port, StopBits::Two);
230 stop_bits_check!(port, StopBits::One);
231
232 // Test bytes to read and write
233 println!("Testing bytes to read and write...");
234 call_query_method_check!(port, SerialPort::bytes_to_write);
235 call_query_method_check!(port, SerialPort::bytes_to_read);
236
237 // Test clearing a buffer
238 println!("Test clearing software buffers...");
239 clear_check!(port, ClearBuffer::Input);
240 clear_check!(port, ClearBuffer::Output);
241 clear_check!(port, ClearBuffer::All);
242
243 // Test transmitting data
244 print!("Testing data transmission...");
245 std::io::stdout().flush().unwrap();
246 // Make sure the port has sane defaults
247 set_defaults(port);
248 let msg = "Test Message";
249 port.write_all(msg.as_bytes())
250 .expect("Unable to write bytes.");
251 println!("success");
252
253 if loopback {
254 print!("Testing data reception...");
255 port.set_timeout(Duration::from_millis(250)).ok();
256
257 let mut buf = [0u8; 12];
258 if let Err(e) = port.read_exact(&mut buf) {
259 println!("FAILED ({})", e);
260 } else {
261 assert_eq!(
262 str::from_utf8(&buf).unwrap(),
263 msg,
264 "Received message does not match sent"
265 );
266 println!("success");
267 }
268 }
269 }
270
check_test_message(sender: &mut dyn SerialPort, receiver: &mut dyn SerialPort)271 fn check_test_message(sender: &mut dyn SerialPort, receiver: &mut dyn SerialPort) {
272 // Ignore any "residue" from previous tests.
273 sender.clear(ClearBuffer::All).unwrap();
274 receiver.clear(ClearBuffer::All).unwrap();
275
276 let send_buf = TEST_MESSAGE;
277 let mut recv_buf = [0u8; TEST_MESSAGE.len()];
278
279 sender.write_all(send_buf).unwrap();
280 sender.flush().unwrap();
281
282 match receiver.read_exact(&mut recv_buf) {
283 Ok(()) => {
284 assert_eq_hex!(recv_buf, send_buf, "Received message does not match sent",);
285 println!(" success");
286 }
287 Err(e) => println!("FAILED: {:?}", e),
288 }
289 }
290
test_dual_ports(port1: &mut dyn serialport::SerialPort, port2: &mut dyn serialport::SerialPort)291 fn test_dual_ports(port1: &mut dyn serialport::SerialPort, port2: &mut dyn serialport::SerialPort) {
292 println!(
293 "Testing paired ports '{}' and '{}':",
294 port1.name().unwrap(),
295 port2.name().unwrap()
296 );
297
298 // Make sure both ports are set to sane defaults
299 set_defaults(port1);
300 set_defaults(port2);
301
302 // Test sending strings from port1 to port2
303 println!(
304 " Transmitting from {} to {}...",
305 port1.name().unwrap(),
306 port2.name().unwrap()
307 );
308
309 let baud_rate = 2_000_000;
310 println!(" At {},8,n,1,noflow...", baud_rate);
311 std::io::stdout().flush().unwrap();
312 if port1.set_baud_rate(baud_rate).is_ok() && port2.set_baud_rate(baud_rate).is_ok() {
313 check_test_message(port1, port2);
314 check_test_message(port2, port1);
315 } else {
316 println!("FAILED (does this platform & port support arbitrary baud rates?)");
317 }
318
319 let baud_rate = 115_200;
320 println!(" At {},8,n,1,noflow...", baud_rate);
321 std::io::stdout().flush().unwrap();
322 if port1.set_baud_rate(baud_rate).is_ok() && port2.set_baud_rate(baud_rate).is_ok() {
323 check_test_message(port1, port2);
324 check_test_message(port2, port1);
325 } else {
326 println!("FAILED");
327 }
328
329 let baud_rate = 57_600;
330 println!(" At {},8,n,1,noflow...", baud_rate);
331 std::io::stdout().flush().unwrap();
332 if port1.set_baud_rate(baud_rate).is_ok() && port2.set_baud_rate(baud_rate).is_ok() {
333 check_test_message(port1, port2);
334 check_test_message(port2, port1);
335 } else {
336 println!("FAILED");
337 }
338
339 let baud_rate = 10_000;
340 println!(" At {},8,n,1,noflow...", baud_rate);
341 std::io::stdout().flush().unwrap();
342 if port1.set_baud_rate(baud_rate).is_ok() && port2.set_baud_rate(baud_rate).is_ok() {
343 check_test_message(port1, port2);
344 check_test_message(port2, port1);
345 } else {
346 println!("FAILED (does this platform & port support arbitrary baud rates?)");
347 }
348 let baud_rate = 9600;
349 println!(" At {},8,n,1,noflow...", baud_rate);
350 std::io::stdout().flush().unwrap();
351 if port1.set_baud_rate(baud_rate).is_ok() && port2.set_baud_rate(baud_rate).is_ok() {
352 check_test_message(port1, port2);
353 check_test_message(port2, port1);
354 } else {
355 println!("FAILED");
356 }
357
358 // Test flow control
359 port1.set_flow_control(FlowControl::Software).unwrap();
360 port2.set_flow_control(FlowControl::Software).unwrap();
361 println!(" At 9600,8,n,1,softflow...");
362 std::io::stdout().flush().unwrap();
363 check_test_message(port1, port2);
364 check_test_message(port2, port1);
365
366 port1.set_flow_control(FlowControl::Hardware).unwrap();
367 port2.set_flow_control(FlowControl::Hardware).unwrap();
368 println!(" At 9600,8,n,1,hardflow...");
369 std::io::stdout().flush().unwrap();
370 check_test_message(port1, port2);
371 check_test_message(port2, port1);
372 }
373
set_defaults(port: &mut dyn serialport::SerialPort)374 fn set_defaults(port: &mut dyn serialport::SerialPort) {
375 port.set_baud_rate(9600).unwrap();
376 port.set_data_bits(DataBits::Eight).unwrap();
377 port.set_flow_control(FlowControl::Software).unwrap();
378 port.set_parity(Parity::None).unwrap();
379 port.set_stop_bits(StopBits::One).unwrap();
380 // TODO: Clean up timeouts and use a less-arbitrary value here. The previous timeout of 0 made
381 // test_dual_ports fail due to a timeout where having at least some some made them pass.
382 port.set_timeout(Duration::from_millis(1000)).unwrap();
383 }
384