Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

5.0.0 proposal #34

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
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
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "serialport"
version = "4.0.2-alpha.0"
version = "5.0.0-alpha.0"
authors = ["Bryant Mairs <[email protected]>"]
categories = ["hardware-support"]
edition = "2018"
Expand Down Expand Up @@ -37,7 +37,7 @@ regex = "1.5.4"
[target."cfg(windows)".dependencies.winapi]
version = "0.3.9"
features = ["cguid", "commapi", "errhandlingapi", "fileapi", "guiddef", "handleapi", "minwinbase",
"minwindef", "ntdef", "setupapi", "winbase", "winerror", "winnt"]
"minwindef", "ntdef", "setupapi", "winbase", "winerror", "winnt", "ioapiset", "synchapi"]

[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
IOKit-sys = "0.1.5"
Expand Down
87 changes: 73 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ For async I/O functionality, see the [mio-serial](https://github.com/berkowski/m

# Overview

The library exposes cross-platform serial port functionality through the `SerialPort` trait. This
library is structured to make this the simplest API to use to encourate cross-platform development
by default. Working with the resultant `Box<dyn SerialPort>` type is therefore recommended. To
expose additional platform-specific functionality use the platform-specific structs directly:
`TTYPort` for POSIX systems and `COMPort` for Windows.
The library exposes cross-platform serial port functionality through the
`SerialPort` struct. Additional platform-dependent features can be enabled by
importing platform-specific `SerialPortExt` traits. `SerialPort` implements the
standard `Read` and `Write` traits.


Serial enumeration is provided on most platforms. The implementation on Linux using `glibc` relies
on `libudev`, an external dynamic library that will need to be available on the system the final
Expand All @@ -41,36 +41,50 @@ let ports = serialport::available_ports().expect("No ports found!");
for p in ports {
println!("{}", p.port_name);
}

```

Opening and configuring a port:

```rust
let port = serialport::new("/dev/ttyUSB0", 115_200)
.timeout(Duration::from_millis(10))
.open().expect("Failed to open port");
let port = SerialPort::builder()
.baud_rate(115_200)
.read_timeout(Duration::from_millis(10))
.open("/dev/ttyUSB0")
.expect("Failed to open port");
```

Writing to a port:

```rust
use std::io::Write;

let output = "This is a test. This is only a test.".as_bytes();
port.write(output).expect("Write failed!");
```

Reading from a port (default is blocking with a 0ms timeout):
Reading from a port:

```rust
use std::io::Read;

let mut serial_buf: Vec<u8> = vec![0; 32];
port.read(serial_buf.as_mut_slice()).expect("Found no data!");
port.read(serial_buf.as_mut_slice()).expect("Read failed");
```

Some platforms expose additional functionality, which is opened using the `open_native()` method:
Some platforms expose additional functionality, which is accessed by importing the platform-specific extension trait.

```rust
let port = serialport::new("/dev/ttyUSB0", 115_200)
.open_native().expect("Failed to open port");
let port = SerialPort::builder()
.baud_rate(115_200)
.read_timeout(Duration::from_millis(10))
.open("/dev/ttyUSB0")
.expect("Failed to open port");

#[cfg(windows)]
use serialport::windows::SerialPortExt;

#[cfg(unix)]
use serialport::posix::SerialPortExt;
```

Closing a port:
Expand All @@ -79,6 +93,51 @@ Closing a port:
port is done when the `SerialPort` object is `Drop`ed either implicitly or explicitly using
`std::mem::drop` (`std::mem::drop(port)`).

# Migrating to Version 5

Prior to version 5 of this library, the `SerialPort` type was a trait, and
cross-platform functionality was provided by using `Box<dyn SerialPort>`.
Platform-specific functionality required using the platform-specific structs,
`COMPort` and `TTYPort`.

In version 5, these types have been unified, with a single `SerialPort` struct
as the only serial port type exposed by the library. Platform-specific
functionality is implemented through extension traits, which can be imported
when needed on a particular platform, to allow you to call extra functions on
the `SerialPort` struct. Using a struct instead of a trait means you no longer
need to `Box` `SerialPort` instances, and the extension traits should make it
easier to write cross-platform code that only occasionally needs access to
platform-specific features.

For example, to send a break on a TTY port, in version 4 and earlier, you would
have to use the `TTYPort` struct instead of the cross-platform `dyn SerialPort`:

```rust
use serialport::BreakDuration;

let port = serialport::new("/dev/ttyUSB0", 9600).open_native()?;
port.send_break(BreakDuration::Short)?;
```

In version 5, you can now use the common `SerialPort` type everywhere, and to
gain access to the platform-specific `send_break` method, you just have to
import the platform-specific trait.

```rust
use serialport::posix::{SerialPortExt, BreakDuration};
use serialport::SerialPort;

let port = SerialPort::builder().open("/dev/ttyUSB0")?;
port.send_break(BreakDuration::Short)?;
```

One other consequence of the switch to a having `SerialPort` as a struct rather
than a trait is that you will now need to import `std::io::Read` and
`std::io::Write` traits explicitly. Previously, the `SerialPort` trait inherited
from `Read` and `Write` so you could call read and write without importing them
whenever the `SerialPort` trait was in scope. With `SerialPort` as a struct, you
now need to explicitly import `Read` and `Write`.

# Examples

There are several included examples, which help demonstrate the functionality of this library and
Expand Down
12 changes: 7 additions & 5 deletions examples/clear_input_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ use std::time::Duration;

use clap::{App, AppSettings, Arg};

use serialport::ClearBuffer;
use serialport::{ClearBuffer, SerialPort};

fn main() {
let matches = App::new("Serialport Example - Clear Input Buffer")
Expand Down Expand Up @@ -76,9 +76,11 @@ fn run(port_name: &str, baud_rate: &str) -> Result<(), Box<dyn Error>> {
.parse::<u32>()
.map_err(|_| format!("Invalid baud rate '{}' specified", baud_rate))?;

let port = serialport::new(port_name, rate)
.timeout(Duration::from_millis(10))
.open()
let port = SerialPort::builder()
.baud_rate(rate)
.read_timeout(Some(Duration::from_millis(10)))
.write_timeout(Some(Duration::from_millis(10)))
.open(port_name)
.map_err(|ref e| format!("Port '{}' not available: {}", &port_name, e))?;

let chan_clear_buf = input_service();
Expand Down Expand Up @@ -124,7 +126,7 @@ fn input_service() -> mpsc::Receiver<()> {
break;
}
Ok(_) => tx.send(()).unwrap(), // Signal main to clear the buffer
Err(e) => panic!(e),
Err(e) => panic!("{}", e),
}
}
});
Expand Down
12 changes: 7 additions & 5 deletions examples/clear_output_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@
//

use std::error::Error;
use std::io::{self, Read};
use std::panic::panic_any;
use std::io::{self, Read, Write};
use std::sync::mpsc;
use std::thread;
use std::time::Duration;

use clap::{App, AppSettings, Arg, ArgMatches};

use serialport::ClearBuffer;
use serialport::{ClearBuffer, SerialPort};

const DEFAULT_BLOCK_SIZE: &str = "128";

Expand Down Expand Up @@ -68,9 +68,11 @@ fn run(port_name: &str, baud_rate: &str, block_size: usize) -> Result<(), Box<dy
.parse::<u32>()
.map_err(|_| format!("Invalid baud rate '{}' specified", baud_rate))?;

let mut port = serialport::new(port_name, rate)
.timeout(Duration::from_millis(10))
.open()
let mut port = SerialPort::builder()
.baud_rate(rate)
.read_timeout(Some(Duration::from_millis(10)))
.write_timeout(Some(Duration::from_millis(10)))
.open(port_name)
.map_err(|ref e| format!("Port '{}' not available: {}", &port_name, e))?;

let chan_clear_buf = input_service();
Expand Down
8 changes: 5 additions & 3 deletions examples/duplex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@
//! To test this, have a physical or virtual loopback device connected as the
//! only port in the system.

use std::io::Write;
use std::io::{Read, Write};
use std::time::Duration;
use std::{io, thread};

use serialport::SerialPort;

fn main() {
// Open the first serialport available.
let port_name = &serialport::available_ports().expect("No serial port")[0].port_name;
let mut port = serialport::new(port_name, 9600)
.open()
let mut port = SerialPort::builder()
.open(port_name)
.expect("Failed to open serial port");

// Clone the port
Expand Down
22 changes: 11 additions & 11 deletions examples/hardware_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@
//! 3) With two ports physically connected to each other
//! `cargo run --example hardware_check /dev/ttyUSB0 /dev/ttyUSB1`

use std::io::Write;
use std::io::{Read, Write};
use std::str;
use std::time::Duration;

use clap::{App, AppSettings, Arg};

Expand Down Expand Up @@ -53,28 +52,28 @@ fn main() {
}

// Run single-port tests on port1
let mut port1 = match serialport::new(port1_name, 9600).open() {
let mut port1 = match SerialPort::builder().open(port1_name) {
Err(e) => {
eprintln!("Failed to open \"{}\". Error: {}", port1_name, e);
::std::process::exit(1);
}
Ok(p) => p,
};
test_single_port(&mut *port1, port1_loopback);
test_single_port(&mut port1, port1_loopback);

if port2_name != "" {
// Run single-port tests on port2
let mut port2 = match serialport::new(port2_name, 9600).open() {
let mut port2 = match SerialPort::builder().open(port2_name) {
Err(e) => {
eprintln!("Failed to open \"{}\". Error: {}", port2_name, e);
::std::process::exit(1);
}
Ok(p) => p,
};
test_single_port(&mut *port2, false);
test_single_port(&mut port2, false);

// Test loopback pair
test_dual_ports(&mut *port1, &mut *port2);
test_dual_ports(&mut port1, &mut port2);
}
}

Expand Down Expand Up @@ -186,7 +185,7 @@ macro_rules! call_query_method_check {
};
}

fn test_single_port(port: &mut dyn serialport::SerialPort, loopback: bool) {
fn test_single_port(port: &mut SerialPort, loopback: bool) {
println!("Testing '{}':", port.name().unwrap());

// Test setting standard baud rates
Expand Down Expand Up @@ -262,7 +261,7 @@ fn test_single_port(port: &mut dyn serialport::SerialPort, loopback: bool) {
}
}

fn test_dual_ports(port1: &mut dyn serialport::SerialPort, port2: &mut dyn serialport::SerialPort) {
fn test_dual_ports(port1: &mut SerialPort, port2: &mut SerialPort) {
println!(
"Testing paired ports '{}' and '{}':",
port1.name().unwrap(),
Expand Down Expand Up @@ -420,11 +419,12 @@ fn test_dual_ports(port1: &mut dyn serialport::SerialPort, port2: &mut dyn seria
}
}

fn set_defaults(port: &mut dyn serialport::SerialPort) {
fn set_defaults(port: &mut 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();
port.set_read_timeout(None).unwrap();
port.set_write_timeout(None).unwrap();
}
6 changes: 4 additions & 2 deletions examples/pseudo_terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ fn main() {
use std::thread;
use std::time;

use serialport::{SerialPort, TTYPort};
use serialport::posix::SerialPortExt;
use serialport::SerialPort;

let (mut master, mut slave) = TTYPort::pair().expect("Unable to create pseudo-terminal pair");
let (mut master, mut slave) =
SerialPort::pair().expect("Unable to create pseudo-terminal pair");

// Master ptty has no associated path on the filesystem.
println!(
Expand Down
12 changes: 8 additions & 4 deletions examples/receive_data.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use std::io::{self, Write};
use std::io::{self, Read, Write};
use std::time::Duration;

use clap::{App, AppSettings, Arg};

use serialport::SerialPort;

fn main() {
let matches = App::new("Serialport Example - Receive Data")
.about("Reads data from a serial port and echoes it to stdout")
Expand All @@ -24,9 +26,11 @@ fn main() {
let port_name = matches.value_of("port").unwrap();
let baud_rate = matches.value_of("baud").unwrap().parse::<u32>().unwrap();

let port = serialport::new(port_name, baud_rate)
.timeout(Duration::from_millis(10))
.open();
let port = SerialPort::builder()
.baud_rate(baud_rate)
.read_timeout(Some(Duration::from_millis(10)))
.write_timeout(Some(Duration::from_millis(10)))
.open(port_name);

match port {
Ok(mut port) => {
Expand Down
7 changes: 4 additions & 3 deletions examples/transmit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::time::Duration;

use clap::{App, AppSettings, Arg};

use serialport::{DataBits, StopBits};
use serialport::{DataBits, SerialPort, StopBits};

fn main() {
let matches = App::new("Serialport Example - Heartbeat")
Expand Down Expand Up @@ -67,11 +67,12 @@ fn main() {
let rate = matches.value_of("rate").unwrap().parse::<u32>().unwrap();
let string = matches.value_of("string").unwrap();

let builder = serialport::new(port_name, baud_rate)
let builder = SerialPort::builder()
.baud_rate(baud_rate)
.stop_bits(stop_bits)
.data_bits(data_bits);
println!("{:?}", &builder);
let mut port = builder.open().unwrap_or_else(|e| {
let mut port = builder.open(port_name).unwrap_or_else(|e| {
eprintln!("Failed to open \"{}\". Error: {}", port_name, e);
::std::process::exit(1);
});
Expand Down
Loading