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}