e-bike-tracker-device/src/serial.rs

146 lines
4.2 KiB
Rust
Raw Normal View History

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,
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<T> = std::result::Result<T, SerialError>;
pub struct RxIter<UART: serial::Uart> {
inner: Rx<UART>,
timeout: Duration,
}
impl<UART: serial::Uart> RxIter<UART> {
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<UART: serial::Uart> Iterator for RxIter<UART> {
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<Self::Item> {
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<UART: serial::Uart> {
pub rx: RxIter<UART>,
pub tx: Tx<UART>,
}
impl<UART: serial::Uart> SerialIO<UART> {
pub fn new(tx: Tx<UART>, rx: Rx<UART>) -> Self {
Self {
rx: RxIter { inner: rx, timeout: Duration::from_millis(0) },
tx,
}
}
pub fn send_bytes(&mut self, payload: &[u8], eos: Option<u8>) -> Result<usize> {
let mut num_bytes = 0;
for b in payload.iter() {
nb::block!(self.tx.write(*b))
.map_err(|err| SerialError::WriteError)?;
num_bytes += 1;
}
if num_bytes == payload.len() {
eos.map(|b| nb::block!(self.tx.write(b)));
Ok(num_bytes + if eos.is_none() { 0 } else { 1 })
}
else {
Err(SerialError::WriteError)
}
}
/// Reads a whole line (that ends with \\n) within the given `timeout` passed on input.
pub fn read_line(&mut self, timeout: Duration) -> Result<String> {
let mut line: String = self.rx.reset(timeout)
.map(|b| char::from(b))
.take_while(|c| *c != '\n')
.collect();
// TODO: \r\n is true for sim800l, but might not be valud for other devices. Re-implement
// this function so that it can be used on all devices.
// \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<UART: serial::Uart> io::Read for SerialIO<UART> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
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<UART: serial::Uart> io::Write for SerialIO<UART> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.send_bytes(buf, None)
.map_err(|_| io::Error::from(io::ErrorKind::Other))
}
fn flush(&mut self) -> io::Result<()> {
self.tx.flush()
.map_err(|_| io::Error::from(io::ErrorKind::Other))
}
}