use std::error::Error; use std::io; use std::thread; use std::time::{Duration, Instant}; use embedded_hal::serial::{Read, Write}; use esp_idf_hal::serial::{self, Rx, Tx}; #[derive(Debug)] pub enum SerialError { ReadError, WriteError(String), TimeoutError, } impl Error for SerialError {} impl std::fmt::Display for SerialError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{:?}", self) } } pub type Result = std::result::Result; pub struct RxIter { inner: Rx, timeout: Duration, } impl RxIter { pub fn reset(&mut self, timeout: Duration) -> &mut Self { self.timeout = timeout; self } fn clear(&mut self) -> () { println!("clearing serial rx"); self.reset(Duration::from_millis(500)).for_each(drop); } } impl Iterator for RxIter { type Item = u8; /// `nb` returns Ok(byte), or one of Err(WouldBlock) and Err(Other) which isn't of anyone's /// interest, so the retry mechanism is triggered on _any_ error every 200ms until a byte is /// received, or the timeout is reached. fn next(&mut self) -> Option { let start = Instant::now(); loop { match self.inner.read() { Ok(b) => { self.timeout = self.timeout.saturating_sub(start.elapsed()); break Some(b) }, Err(_) => { if start.elapsed() > self.timeout { self.timeout = Duration::ZERO; break None } thread::sleep(Duration::from_millis(200)); } } } } } pub struct SerialIO { pub rx: RxIter, pub tx: Tx, } impl SerialIO { pub fn new(tx: Tx, rx: Rx) -> Self { Self { rx: RxIter { inner: rx, timeout: Duration::from_millis(0) }, tx, } } pub fn send_bytes(&mut self, payload: &[u8], eos: Option) -> Result { //self.rx.clear(); for b in payload.iter() { nb::block!(self.tx.write(*b)) .map_err(|err| SerialError::WriteError(format!("Error writing '{}' to serial, Original error {}", b, err)))?; } eos.map(|b| nb::block!(self.tx.write(b))); Ok(payload.len() + if eos.is_none() { 0 } else { 1 }) } /// Reads a whole line (that ends with \\n) within the given `timeout` passed on input. pub fn read_line(&mut self, timeout: Duration) -> Result { let mut line: String = self.rx.reset(timeout) .map(|b| char::from(b)) .take_while(|c| *c != '\n') .collect(); // \r must come right before \n on read; take_while excludes the matched element. if line.ends_with('\r') { line.push('\n'); Ok(line) } else if self.rx.timeout.as_millis() == 0 { Err(SerialError::TimeoutError) } else { Err(SerialError::ReadError) } } } impl io::Read for SerialIO { fn read(&mut self, buf: &mut [u8]) -> io::Result { let buf_size = buf.len(); let count = self.rx.reset(Duration::from_millis(1000)) .enumerate() .map(|(i, b)| { buf[i] = b; i }) .take_while(|i| i < &buf_size) .count(); Ok(count) } } impl io::Write for SerialIO { fn write(&mut self, buf: &[u8]) -> io::Result { self.send_bytes(buf, None) .map_err(|_| io::Error::from(io::ErrorKind::Other)) } fn flush(&mut self) -> io::Result<()> { self.tx.flush() .map_err(|err| io::Error::from(io::ErrorKind::Other)) } }