vexide_devices/smart/
serial.rs

1//! Generic Serial Communication
2//!
3//! This module provides an interface for using V5 Smart Ports as serial communication
4//! ports over RS-485. It allows bidirectional communication with any device that speaks
5//! serial over the V5's RS-485 interface.
6//!
7//! # Hardware Description
8//!
9//! V5 Smart Ports provide half-duplex RS-485 serial communication at up to an allowed
10//! 921600 baud for user programs.
11//!
12//! The ports supply 12.8V VCC nominally (VCC is wired directly to the V5's battery lines,
13//! providing voltage somewhere in the range of 12-14V). Writes to the serial port are buffered,
14//! but are automatically flushed by VEXos as fast as possible (down to ~10µs or so).
15
16use core::{
17    mem::ManuallyDrop,
18    pin::Pin,
19    task::{Context, Poll},
20};
21
22use no_std_io::io;
23use snafu::Snafu;
24use vex_sdk::{
25    vexDeviceGenericSerialBaudrate, vexDeviceGenericSerialEnable, vexDeviceGenericSerialFlush,
26    vexDeviceGenericSerialPeekChar, vexDeviceGenericSerialReadChar, vexDeviceGenericSerialReceive,
27    vexDeviceGenericSerialReceiveAvail, vexDeviceGenericSerialTransmit,
28    vexDeviceGenericSerialWriteChar, vexDeviceGenericSerialWriteFree, V5_DeviceT,
29};
30
31use super::{SmartDevice, SmartDeviceType, SmartPort};
32
33/// A Smart Port configured as a generic RS-485 serial port.
34///
35/// This struct implements the [`Read`] and [`Write`] traits from vexide's `io` module
36/// for reading/writing to the serial port.
37///
38/// [`Read`]: vexide_core::io::Read
39/// [`Write`]: vexide_core::io::Write
40#[derive(Debug, Eq, PartialEq)]
41pub struct SerialPort {
42    port: SmartPort,
43    device: V5_DeviceT,
44}
45
46// SAFETY: Required because we store a raw pointer to the device handle to avoid it getting from the
47// SDK each device function. Simply sharing a raw pointer across threads is not inherently unsafe.
48unsafe impl Send for SerialPort {}
49unsafe impl Sync for SerialPort {}
50
51impl SerialPort {
52    /// The maximum user-configurable baud rate for generic serial under normal conditions.
53    pub const MAX_BAUD_RATE: u32 = 921_600;
54
55    /// The length of the serial FIFO input and output buffers.
56    pub const INTERNAL_BUFFER_SIZE: usize = 1024;
57
58    /// Open and configure a generic serial port on a [`SmartPort`].
59    ///
60    /// This configures a [`SmartPort`] to act as a generic serial controller capable of sending/receiving
61    /// data. Providing a baud rate, or the transmission rate of bits is required. The maximum allowed
62    /// baud rate is 921600.
63    ///
64    /// # Examples
65    ///
66    /// ```
67    /// use vexide::prelude::*;
68    ///
69    /// #[vexide::main]
70    /// async fn main(peripherals: Peripherals) {
71    ///     let serial = SerialPort::open(peripherals.port_1, 115200).await;
72    /// }
73    /// ```
74    pub fn open(port: SmartPort, baud_rate: u32) -> SerialPortOpenFuture {
75        SerialPortOpenFuture {
76            state: SerialPortOpenState::Configure { baud_rate },
77            serial: ManuallyDrop::new(Self {
78                device: unsafe { port.device_handle() },
79                port,
80            }),
81        }
82    }
83
84    /// Configures the baud rate of the serial port.
85    ///
86    /// Baud rate determines the speed of communication over the data channel. Under normal conditions, user code is limited
87    /// to a maximum baudrate of 921600.
88    ///
89    /// # Examples
90    ///
91    /// ```
92    /// use vexide::prelude::*;
93    ///
94    /// #[vexide::main]
95    /// async fn main(peripherals: Peripherals) {
96    ///     let mut serial = SerialPort::open(peripherals.port_1, 115200).await;
97    ///
98    ///     // Change to 9600 baud
99    ///     serial.set_baud_rate(9600);
100    /// }
101    /// ```
102    pub fn set_baud_rate(&mut self, baud_rate: u32) {
103        unsafe {
104            vexDeviceGenericSerialBaudrate(self.device, baud_rate as i32);
105        }
106    }
107
108    /// Clears the internal input and output FIFO buffers.
109    ///
110    /// This can be useful to reset state and remove old, potentially unneeded data
111    /// from the input FIFO buffer or to cancel sending any data in the output FIFO
112    /// buffer.
113    ///
114    /// # This is not the same thing as "flushing".
115    ///
116    /// This function does not cause the data in the output buffer to be
117    /// written. It simply clears the internal buffers. Unlike stdout, generic
118    /// serial does not use buffered IO (the FIFO buffers are written as soon
119    /// as possible).
120    ///
121    /// # Examples
122    ///
123    /// ```
124    /// use vexide::prelude::*;
125    ///
126    /// #[vexide::main]
127    /// async fn main(peripherals: Peripherals) {
128    ///     let mut serial = SerialPort::open(peripherals.port_1, 115200).await;
129    ///
130    ///     _ = serial.clear_buffers();
131    ///     _ = serial.write(b"Buffers are clear!");
132    /// }
133    /// ```
134    pub fn clear_buffers(&mut self) {
135        unsafe {
136            vexDeviceGenericSerialFlush(self.device);
137        }
138    }
139
140    /// Read the next byte available in the serial port's input buffer, or `None` if the input buffer is empty.
141    ///
142    /// # Examples
143    ///
144    /// ```
145    /// use vexide::prelude::*;
146    ///
147    /// #[vexide::main]
148    /// async fn main(peripherals: Peripherals) {
149    ///     let serial = SerialPort::open(peripherals.port_1, 115200).await;
150    ///
151    ///     loop {
152    ///         if let Some(byte) = serial.read_byte() {
153    ///             println!("Got byte: {}", byte);
154    ///         }
155    ///
156    ///         sleep(core::time::Duration::from_millis(10)).await;
157    ///     }
158    /// }
159    /// ```
160    pub fn read_byte(&mut self) -> Option<u8> {
161        let byte = unsafe { vexDeviceGenericSerialReadChar(self.device) };
162
163        match byte {
164            -1 => None,
165            _ => Some(byte as u8),
166        }
167    }
168
169    /// Read the next byte available in the port's input buffer without removing it. Returns
170    /// `None` if the input buffer is empty.
171    ///
172    /// # Examples
173    ///
174    /// ```
175    /// use vexide::prelude::*;
176    ///
177    /// #[vexide::main]
178    /// async fn main(peripherals: Peripherals) {
179    ///     let serial = SerialPort::open(peripherals.port_1, 115200).await;
180    ///
181    ///     if let Some(next_byte) = serial.peek_byte() {
182    ///         println!("Next byte: {}", next_byte);
183    ///     }
184    /// }
185    /// ```
186    #[must_use]
187    pub fn peek_byte(&self) -> Option<u8> {
188        match unsafe { vexDeviceGenericSerialPeekChar(self.device) } {
189            -1 => None,
190            byte => Some(byte as u8),
191        }
192    }
193
194    /// Write a single byte to the port's output buffer.
195    ///
196    /// # Errors
197    ///
198    /// - A [`SerialError::WriteFailed`] error is returned if the byte could not be written.
199    ///
200    /// # Examples
201    ///
202    /// ```
203    /// use vexide::prelude::*;
204    ///
205    /// #[vexide::main]
206    /// async fn main(peripherals: Peripherals) {
207    ///     let mut serial = SerialPort::open(peripherals.port_1, 115200).await;
208    ///
209    ///     // Attempt to write 0x80 (128u8) to the output buffer
210    ///     _ = serial.write_byte(0x80);
211    /// }
212    /// ```
213    pub fn write_byte(&mut self, byte: u8) -> Result<(), SerialError> {
214        match unsafe { vexDeviceGenericSerialWriteChar(self.device, byte) } {
215            -1 => WriteFailedSnafu.fail(),
216            _ => Ok(()),
217        }
218    }
219
220    /// Returns the number of bytes available to be read in the port's FIFO input buffer.
221    ///
222    /// # Errors
223    ///
224    /// - A [`SerialError::ReadFailed`] error is returned if the serial device's status could not be read.
225    ///
226    /// # Examples
227    ///
228    /// ```
229    /// use vexide::prelude::*;
230    ///
231    /// #[vexide::main]
232    /// async fn main(peripherals: Peripherals) {
233    ///     let mut serial = SerialPort::open(peripherals.port_1, 115200).await;
234    ///
235    ///     if serial.unread_bytes().is_ok_and(|bytes| bytes > 0) {
236    ///         if let Ok(byte) = serial.read_byte() {
237    ///             // Okay to unwrap here, since we've established that there was at least one byte to read.
238    ///             println!("{}", byte.unwrap());
239    ///         }
240    ///     }
241    /// }
242    /// ```
243    pub fn unread_bytes(&self) -> Result<usize, SerialError> {
244        match unsafe { vexDeviceGenericSerialReceiveAvail(self.device) } {
245            -1 => ReadFailedSnafu.fail(),
246            available => Ok(available as usize),
247        }
248    }
249
250    /// Returns the number of bytes free in the port's FIFO output buffer.
251    ///
252    /// # Errors
253    ///
254    /// - A [`SerialError::ReadFailed`] error is returned if the serial device's status could not be read.
255    ///
256    /// # Examples
257    ///
258    /// ```
259    /// use vexide::prelude::*;
260    ///
261    /// #[vexide::main]
262    /// async fn main(peripherals: Peripherals) {
263    ///     let mut serial = SerialPort::open(peripherals.port_1, 115200).await;
264    ///
265    ///     // Write a byte if there's free space in the buffer.
266    ///     if serial.available_write_bytes().is_ok_and(|available| available > 0) {
267    ///         _ = serial.write_byte(0x80);
268    ///     }
269    /// }
270    /// ```
271    pub fn available_write_bytes(&self) -> Result<usize, SerialError> {
272        match unsafe { vexDeviceGenericSerialWriteFree(self.device) } {
273            -1 => ReadFailedSnafu.fail(),
274            available => Ok(available as usize),
275        }
276    }
277}
278
279impl io::Read for SerialPort {
280    /// Read some bytes from this serial port into the specified buffer, returning
281    /// how many bytes were read.
282    ///
283    /// # Errors
284    ///
285    /// - An error with the kind [`io::ErrorKind::Other`] is returned if an unexpected internal read error occurred.
286    ///
287    /// # Examples
288    ///
289    /// ```
290    /// use vexide::prelude::*;
291    ///
292    /// #[vexide::main]
293    /// async fn main(peripherals: Peripherals) {
294    ///     let mut serial = SerialPort::open(peripherals.port_1, 115200).await;
295    ///
296    ///     let mut buffer = vec![0; 2048];
297    ///
298    ///     loop {
299    ///         _ = serial.read(&mut buffer);
300    ///         sleep(core::time::Duration::from_millis(10)).await;
301    ///     }
302    /// }
303    /// ```
304    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
305        match unsafe {
306            vexDeviceGenericSerialReceive(self.device, buf.as_mut_ptr(), buf.len() as i32)
307        } {
308            -1 => Err(io::Error::new(
309                io::ErrorKind::Other,
310                "Internal read error occurred.",
311            )),
312            received => Ok(received as usize),
313        }
314    }
315}
316
317impl io::Write for SerialPort {
318    /// Write a buffer into the serial port's output buffer, returning how many bytes
319    /// were written.
320    ///
321    /// # Errors
322    ///
323    /// - An error with the kind [`io::ErrorKind::Other`] is returned if an unexpected internal write error occurred.
324    ///
325    /// # Examples
326    ///
327    /// ```
328    /// use vexide::prelude::*;
329    ///
330    /// #[vexide::main]
331    /// async fn main(peripherals: Peripherals) {
332    ///     let mut serial = SerialPort::open(peripherals.port_1, 115200).await;
333    ///
334    ///     _ = serial.write(b"yo");
335    /// }
336    /// ```
337    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
338        match unsafe { vexDeviceGenericSerialTransmit(self.device, buf.as_ptr(), buf.len() as i32) }
339        {
340            -1 => Err(io::Error::new(
341                io::ErrorKind::Other,
342                "Internal write error occurred.",
343            )),
344            written => Ok(written as usize),
345        }
346    }
347
348    /// This function does nothing.
349    ///
350    /// Generic serial does not use traditional buffers, so data in the output buffer is immediately sent.
351    ///
352    /// If you wish to *clear* both the read and write buffers, you can use [`Self::clear_buffers`].
353    fn flush(&mut self) -> io::Result<()> {
354        Ok(())
355    }
356}
357
358impl SmartDevice for SerialPort {
359    fn port_number(&self) -> u8 {
360        self.port.number()
361    }
362
363    fn device_type(&self) -> SmartDeviceType {
364        SmartDeviceType::GenericSerial
365    }
366}
367impl From<SerialPort> for SmartPort {
368    fn from(device: SerialPort) -> Self {
369        device.port
370    }
371}
372
373/// Errors that can occur when interacting with a [`SerialPort`].
374#[derive(Debug, Snafu)]
375pub enum SerialError {
376    /// Internal write error occurred.
377    WriteFailed,
378
379    /// Internal read error occurred.
380    ReadFailed,
381}
382
383#[derive(Debug, Clone, Copy)]
384enum SerialPortOpenState {
385    Configure { baud_rate: u32 },
386    Waiting,
387}
388
389/// Future that opens and configures a [`SerialPort`].
390///
391/// If the port was not previous configured as a generic serial port, this may
392/// take a few milliseconds to complete.
393#[must_use = "futures do nothing unless you `.await` or poll them"]
394#[derive(Debug)]
395pub struct SerialPortOpenFuture {
396    state: SerialPortOpenState,
397    serial: ManuallyDrop<SerialPort>,
398}
399
400impl core::future::Future for SerialPortOpenFuture {
401    type Output = SerialPort;
402
403    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
404        let this = self.get_mut();
405
406        if let SerialPortOpenState::Configure { baud_rate } = this.state {
407            unsafe {
408                vexDeviceGenericSerialEnable(this.serial.device, 0);
409                vexDeviceGenericSerialBaudrate(this.serial.device, baud_rate as i32);
410            }
411
412            this.state = SerialPortOpenState::Waiting;
413        }
414
415        if this.serial.validate_port().is_ok() {
416            // SAFETY: Device is not accessed from self.serial after `Poll::Ready` return.
417            Poll::Ready(unsafe { ManuallyDrop::take(&mut this.serial) })
418        } else {
419            cx.waker().wake_by_ref();
420            Poll::Pending
421        }
422    }
423}