serial_rs/
lib.rs

1//! serial-rs is a cross-platform serial library
2//! A lot of the code here is based on the [Pyserial project](https://github.com/pyserial/pyserial)
3
4#![deny(
5    missing_docs,
6    missing_debug_implementations,
7    missing_copy_implementations,
8    dead_code,
9    while_true
10)]
11use std::io::ErrorKind;
12
13#[allow(unused)]
14const XON: i8 = 17;
15#[allow(unused)]
16const XOFF: i8 = 19;
17#[allow(unused)]
18const CR: i8 = 13;
19#[allow(unused)]
20const LF: i8 = 10;
21
22#[cfg(unix)]
23pub mod posix;
24
25#[cfg(windows)]
26pub mod windows;
27
28/// Serial port result type
29pub type SerialResult<T> = std::result::Result<T, SerialError>;
30
31/// Serial port error type
32pub enum SerialError {
33    /// IO Error
34    IoError(std::io::Error),
35    /// OS Specific error
36    OsError {
37        /// OS Error code
38        code: u32,
39        /// OS Error description
40        desc: String,
41    },
42    /// Internal library error
43    LibraryError(String)
44}
45
46impl std::fmt::Debug for SerialError {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        match self {
49            Self::IoError(arg0) => f.debug_tuple("IoError").field(arg0).finish(),
50            Self::OsError { code, desc } => f
51                .debug_struct("OsError")
52                .field("code", code)
53                .field("desc", desc)
54                .finish(),
55            SerialError::LibraryError(e) => f.debug_tuple("LibraryError").field(e).finish(),
56        }
57    }
58}
59
60impl std::fmt::Display for SerialError {
61    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62        match self {
63            SerialError::IoError(e) => {
64                write!(f, "IoError {}", e)
65            }
66            SerialError::OsError { code, desc } => write!(f, "OsError {code} ({desc})"),
67            SerialError::LibraryError(e) => write!(f, "Serial-RS Lib error '{e}'"),
68        }
69    }
70}
71
72impl std::error::Error for SerialError {
73    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
74        if let Self::IoError(e) = self {
75            Some(e)
76        } else {
77            None
78        }
79    }
80}
81
82/// Serial port settings
83#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
84pub struct SerialPortSettings {
85    baud_rate: u32,
86    byte_size: ByteSize,
87    parity: Parity,
88    stop_bits: StopBits,
89    read_timeout: Option<u128>,
90    flow_control: FlowControl,
91    write_timeout: Option<u128>,
92    inter_byte_timeout: Option<u128>,
93    blocking: bool
94}
95
96impl Default for SerialPortSettings {
97    fn default() -> Self {
98        Self {
99            baud_rate: 9600,
100            byte_size: ByteSize::Eight,
101            parity: Parity::None,
102            stop_bits: StopBits::One,
103            read_timeout: None,
104            write_timeout: None,
105            flow_control: FlowControl::None,
106            inter_byte_timeout: None,
107            blocking: true
108        }
109    }
110}
111
112#[allow(missing_docs)]
113impl SerialPortSettings {
114    /// Set baud rate
115    pub fn baud(mut self, baud: u32) -> Self {
116        self.baud_rate = baud;
117        self
118    }
119
120    pub fn read_timeout(mut self, timeout: Option<u128>) -> Self {
121        self.read_timeout = timeout;
122        self
123    }
124
125    pub fn byte_size(mut self, byte_size: ByteSize) -> Self {
126        self.byte_size = byte_size;
127        self
128    }
129
130    pub fn write_timeout(mut self, timeout: Option<u128>) -> Self {
131        self.write_timeout = timeout;
132        self
133    }
134
135    pub fn parity(mut self, parity: Parity) -> Self {
136        self.parity = parity;
137        self
138    }
139
140    pub fn stop_bits(mut self, stop_bits: StopBits) -> Self {
141        self.stop_bits = stop_bits;
142        self
143    }
144
145    pub fn set_flow_control(mut self, method: FlowControl) -> Self {
146        self.flow_control = method;
147        self
148    }
149
150    pub fn set_blocking(mut self, blocking: bool) -> Self {
151        self.blocking = blocking;
152        self
153    }
154}
155
156#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
157/// Flow control method
158pub enum FlowControl {
159    /// No flow control
160    None,
161    /// DSR DTR flow control (Software)
162    DsrDtr,
163    /// XON XOFF flow control (Software)
164    XonXoff,
165    /// CTS RTS flow control (Hardware)
166    RtsCts
167}
168
169#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
170/// Bytesize for serial port
171pub enum ByteSize {
172    /// 5 bits
173    Five,
174    /// 6 bits
175    Six,
176    /// 7 bits
177    Seven,
178    /// 8 bits
179    Eight,
180}
181
182#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
183/// Parity definitions
184pub enum Parity {
185    /// No parity
186    None,
187    /// Even parity
188    Even,
189    /// Odd parity
190    Odd,
191}
192
193#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
194/// Stop bits for serial port
195pub enum StopBits {
196    /// 1 stop bit
197    One,
198    /// 1.5 stop bits
199    OnePointFive,
200    /// 2 stop bits
201    Two,
202}
203
204/// Information on a listed serial port
205#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
206pub struct PortInfo {
207    /// Name of the device
208    port: String,
209    /// Hardware-ID of the device
210    hwid: String,
211    /// Vendor ID
212    vid: u16,
213    /// Product ID
214    pid: u16,
215    /// Manufacturer
216    manufacturer: String,
217    /// Description of the device
218    description: String,
219}
220
221impl PortInfo {
222    /// Gets port name
223    pub fn get_port(&self) -> &str { &self.port }
224    /// Gets port system hardware-ID
225    pub fn get_hwid(&self) -> &str { &self.hwid }
226    /// Gets port devices' ProductID
227    pub fn get_pid(&self) -> u16 { self.pid }
228    /// Gets port devices' VendorID
229    pub fn get_vid(&self) -> u16 { self.vid }
230    /// Gets port devices' manufacturer
231    pub fn get_manufacturer(&self) -> &str { &self.manufacturer }
232    /// Gets port devices' description
233    pub fn get_desc(&self) -> &str { &self.description }
234}
235
236/// Serial port trait
237pub trait SerialPort: Send + std::io::Write + std::io::Read {
238    /// Reconfigures an open port with the current settings
239    fn reconfigure_port(&mut self) -> SerialResult<()>;
240    /// Closes the port
241    fn close(self) -> SerialResult<()>;
242    /// Sets Tx and Rx buffer size. A sensible value for these is 4096 bytes
243    fn set_buffer_size(&mut self, rx_size: usize, tx_size: usize) -> SerialResult<()>;
244    /// Sets flow control state manually
245    fn set_output_flow_control(&self, enable: bool) -> SerialResult<()>;
246    /// Sets data terminal flag
247    fn set_data_terminal_ready(&mut self, enable: bool) -> SerialResult<()>;
248    /// Sets request to send flag
249    fn set_request_to_send(&mut self, enable: bool) -> SerialResult<()>;
250    /// Sets break state flag
251    fn set_break_state(&mut self, enable: bool) -> SerialResult<()>;
252    /// Reads clear to send flag
253    fn read_clear_to_send(&self) -> SerialResult<bool>;
254    /// Reads data set ready flag
255    fn read_data_set_ready(&self) -> SerialResult<bool>;
256    /// Reads ring indicator flag
257    fn read_ring_indicator(&self) -> SerialResult<bool>;
258    /// Reads carrier detect flag
259    fn read_carrier_detect(&self) -> SerialResult<bool>;
260    /// Returns number of bytes left to read in serial buffer
261    fn bytes_to_read(&self) -> SerialResult<usize>;
262    /// Returns number of bytes left to write in serial buffer
263    fn bytes_to_write(&self) -> SerialResult<usize>;
264    /// Gets the path of the port
265    fn get_path(&self) -> String;
266    /// Tries to clone the port.
267    /// 
268    /// # Note about cloning
269    /// You must be careful when cloning a port as this can have interesting
270    /// effects. For example, if one thread tries to close the port but another
271    /// thread wants the port open
272    fn try_clone(&mut self) -> SerialResult<Box<dyn SerialPort>>;
273    /// Clears serial input buffer
274    fn clear_input_buffer(&mut self) -> SerialResult<()>;
275    /// Clears serial output buffer
276    fn clear_output_buffer(&mut self) -> SerialResult<()>;
277}
278
279/// Scanner to list avaliable serial ports on a system
280pub trait PortScanner {
281    /// Lists avaliable serial ports on a system
282    fn list_devices(&mut self) -> SerialResult<Vec<PortInfo>>;
283}
284
285impl From<SerialError> for std::io::Error {
286    fn from(e: SerialError) -> Self {
287        match e {
288            SerialError::IoError(i) => i,
289            SerialError::OsError { code: _ , desc } => std::io::Error::new(ErrorKind::Other, desc),
290            SerialError::LibraryError(e) => std::io::Error::new(ErrorKind::Other, e),
291        }
292    }
293}
294
295/// Creates a new serial port from port info
296pub fn new(info: PortInfo, settings: Option<SerialPortSettings>) -> SerialResult<Box<dyn SerialPort>> {
297    #[cfg(unix)]
298    {
299        use posix::*;
300        Ok(Box::new(TTYPort::new(info.port, settings)?))
301    }
302    #[cfg(windows)]
303    {
304        use windows::*;
305        Ok(Box::new(COMPort::new(info.port, settings)?))
306    }
307}
308
309/// Creates a new serial port from port path
310pub fn new_from_path(path: &str, settings: Option<SerialPortSettings>) -> SerialResult<Box<dyn SerialPort>> {
311    #[cfg(unix)]
312    {
313        use posix::*;
314        Ok(Box::new(TTYPort::new(path.to_string(), settings)?))
315    }
316    #[cfg(windows)]
317    {
318        use windows::*;
319        Ok(Box::new(COMPort::new(path.to_string(), settings)?))
320    }
321}
322
323/// Lists all ports on the system
324pub fn list_ports() -> SerialResult<Vec<PortInfo>> {
325    #[cfg(unix)]
326    {
327        use posix::port_lister::TTYPortScanner;
328        TTYPortScanner{}.list_devices()
329    }
330    #[cfg(windows)]
331    {
332        use windows::port_lister::COMPortLister;
333        COMPortLister{}.list_devices()
334    }
335}