The serial-core
crate provides abstract types used to interface with and implement serial ports.
They can be used to write code that functions generically over all serial port types and to
implement new serial port types that work with generic code.
Add serial-core
as a dependency in Cargo.toml
:
toml
[dependencies]
serial-core = "0.4"
Import the serial-core
crate and everything from the serial_core::prelude
module. The traits in
the serial_core::prelude
module are useful to have in scope to resolve trait methods when
working with serial ports, and they are unlikely to conflict with other crates.
```rust extern crate serial_core as serial;
use std::io; use std::time::Duration;
use std::io::prelude::; use serial::prelude::; ```
Interfacing with a serial port is done through the SerialPort
, std::io::Read
, and
std::io::Write
traits, which provide methods for configuration, I/O, and control signals.
```rust
fn probe
// configuration
try!(port.reconfigure(&|settings| {
try!(settings.set_baud_rate(serial::Baud9600));
settings.set_char_size(serial::Bits8);
settings.set_parity(serial::ParityNone);
settings.set_stop_bits(serial::Stop1);
settings.set_flow_control(serial::FlowNone);
Ok(())
}));
// I/O
try!(port.set_timeout(Duration::from_millis(100)));
try!(port.write(&buf[..]));
try!(port.read(&mut buf[..]));
// control signals
try!(port.set_dtr(true));
try!(port.read_dsr());
Ok(())
} ```
For details on using an open serial port, refer to the documentation for the SerialPort
trait.
Often times, you'll want to implement a higher-level protocol over a serial port, in which case you'll probably want a handle object that owns the serial port and provides an interface specific to the higher-level protocol.
To implement a handle for generic serial ports without requiring a proliferation of generics, one can define the higher-level protocol in a trait:
```rust
struct Handle
impl {
fn new(port: P) -> Self {
Handle { port: port }
}
} impl {
fn get_name(&mut self) -> serial::Result } fn greet(greeter: &mut Greet) -> serial::Result<()> {
let name = try!(greeter.get_name()); }
``` Note that in the above example, the Alternatively, since ```rust
struct Handle {
port: Box impl Handle {
fn new }
``` The serial port crates are designed to be extensible, allowing third parties to define custom serial
port types. Reasons for implementing a custom serial port type may include bridging a serial port to
a TCP socket or other I/O device, providing a fake implementation for testing, or integrating with
custom hardware. To define a custom serial port type, start by importing the ```rust
extern crate serial_core as serial; use std::io;
use std::time::Duration;
``` Next, define a type that will implement the new serial port and optionally a type for its settings: ```rust
struct CustomSerialPort {
// ...
} struct CustomSerialPortSettings {
// ...
}
``` Implement
```rust
impl serial::SerialDevice for CustomSerialPort {
type Settings = CustomSerialPortSettings; } impl io::Read for CustomSerialPort {
fn read(&mut self, buf: &mut [u8]) -> io::Result impl io::Write for CustomSerialPort {
fn write(&mut self, buf: &[u8]) -> io::Result If a custom settings type is not needed, then the ```rust
impl serial::SerialPortSettings for CustomSerialPortSettings {
fn baudrate(&self) -> Option }
``` For details on implementing a new serial port type, refer to the documentation for the
Copyright © 2015 David Cuddeback Distributed under the MIT License. try!(self.port.write("What is your name? "));
try!(self.port.read_to_string(&mut name));
Ok(name)
}
fn say_hello(&mut self, name: &String) -> serial::Result<()> {
try!(writeln!(&mut self.port, "Hello, {}!", name));
Ok(())
}
greeter.say_hello(name)
greet()
function can interact with the Handle
struct via the
Greet
trait without caring what type of SerialPort
it's talking to.SerialPort
is object-safe, it can be boxed inside of the handle. However,
this approach may introduce an extra pointer indirection:fn get_name(&mut self) -> serial::Result<String> {
// ...
}
fn say_hello(&mut self, name: &String) -> serial::Result<()> {
// ...
}
Implementing a Custom Serial Port
serial_core
crate:SerialDevice
,
std::io::Read
, and std::io::Write
for the new serial port type:fn read_settings(&self) -> serial::Result<Self::Settings> { ... }
fn write_settings(&mut self, settings: &Self::Settings) -> serial::Result<()> { ... }
fn timeout(&self) -> Duration { ... }
fn set_timeout(&mut self, timeout: Duration) -> serial::Result<()> { ... }
fn set_rts(&mut self, level: bool) -> serial::Result<()> { ... }
fn set_dtr(&mut self, level: bool) -> serial::Result<()> { ... }
fn read_cts(&mut self) -> serial::Result<bool> { ... }
fn read_dsr(&mut self) -> serial::Result<bool> { ... }
fn read_ri(&mut self) -> serial::Result<bool> { ... }
fn read_cd(&mut self) -> serial::Result<bool> { ... }
PortSettings
struct can be used for
the SerialDevice::Settings
associated type. Otherwise, the Settings
type must implement
SerialPortSettings
:fn set_baud_rate(&mut self, baud_rate: BaudRate) -> serial::Result<()> { ... }
fn set_char_size(&mut self, char_size: CharSize) { ... }
fn set_parity(&mut self, parity: Parity) { ... }
fn set_stop_bits(&mut self, stop_bits: StopBits) { ... }
fn set_flow_control(&mut self, flow_control: FlowControl) { ... }
SerialDevice
trait.License