• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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