esp-rs / esp-idf-hal

embedded-hal implementation for Rust on ESP32 and ESP-IDF
https://docs.esp-rs.org/esp-idf-hal/
Apache License 2.0
470 stars 171 forks source link

UART read timeout? #461

Open victorbnl opened 4 months ago

victorbnl commented 4 months ago

I am trying to read a success code that is sent to my ESP by a UART sensor. To do so, I use read_exact this way

fn read_success(uart: &mut UartDriver) -> anyhow::Result<()> {
    let mut buffer = [0; 10];
    uart.read_exact(&mut buffer)?;
    assert_eq!(&buffer, b"$00023335&");
    Ok(())
}

Can I set a read timeout in case the sensor never responds?

There seems to be uart_set_rx_timeout but I couldn’t find any function that does this in the wrappers.

armandas commented 4 months ago

I had the same problem. Not sure if that's the best way to solve this, but what I did is implement my own ReadWithTimeout trait, based on the Read trait from embedded_io:

use esp_idf_hal::delay::TickType;
use esp_idf_hal::io::{EspIOError, ReadExactError};
use esp_idf_hal::uart::UartDriver;

pub trait ReadWithTimeout {
    fn read(&mut self, buf: &mut [u8], timeout_ms: u64) -> Result<usize, EspIOError>;

    fn read_exact(
        &mut self,
        mut buf: &mut [u8],
        timeout_ms: u64,
    ) -> Result<(), ReadExactError<EspIOError>> {
        while !buf.is_empty() {
            match self.read(buf, timeout_ms) {
                Ok(0) => break,
                Ok(n) => buf = &mut buf[n..],
                Err(e) => return Err(ReadExactError::Other(e)),
            }
        }
        if buf.is_empty() {
            Ok(())
        } else {
            Err(ReadExactError::UnexpectedEof)
        }
    }
}

impl<'d> ReadWithTimeout for UartDriver<'d> {
    fn read(&mut self, buf: &mut [u8], timeout_ms: u64) -> Result<usize, EspIOError> {
        UartDriver::read(self, buf, TickType::new_millis(timeout_ms).ticks()).map_err(EspIOError)
    }
}