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
461 stars 171 forks source link

Async Uart Write hangs forever #362

Closed t-moe closed 9 months ago

t-moe commented 9 months ago

The following code blocks forever on the write_all line, if I write more than 129 bytes.
130 bytes are output to uart, then the core hangs up....

use esp_idf_svc::hal::uart::{AsyncUartDriver};
use esp_idf_svc::hal::{gpio::*, prelude::*, uart::config::Config};
use esp_idf_svc::hal::task::block_on;

fn main() {
    esp_idf_svc::sys::link_patches();
    esp_idf_svc::log::EspLogger::initialize_default();

    block_on(async {
        let peripherals = Peripherals::take().unwrap();
        let uart_pc_tx_pin = peripherals.pins.gpio16;
        let uart_pc_rx_pin = peripherals.pins.gpio17;

        let config = Config::new().baudrate(Hertz(115_200));
        let mut uart_pc = AsyncUartDriver::new(peripherals.uart1, uart_pc_tx_pin, uart_pc_rx_pin, AnyInputPin::none(), AnyOutputPin::none(), &config).unwrap();

        let length = 130;
        let string = "a".repeat(length);
        embedded_io_async::Write::write_all(&mut uart_pc, string.as_bytes()).await.unwrap();

        // this  line is never reached if length > 129
        log::info!("done");
    });

Also, consider this modification which inlines write_all:

        let length = 170;
        let string = "a".repeat(length);
        let mut buf = string.as_bytes();
        while !buf.is_empty() {
            match embedded_io_async::Write::write(&mut uart_pc,buf).await {
                Ok(0) => panic!("write() returned Ok(0)"),
                Ok(n) => {
                    log::info!("wrote {} bytes", n);
                    buf = &buf[n..]
                },
                Err(e) => panic!("write() returned Err({:?})", e),
            }
        }

It works and outputs:

I (581) esp_idf_embassy_time: wrote 128 bytes
I (581) esp_idf_embassy_time: wrote 42 bytes
I (591) esp_idf_embassy_time: done

But when I remove the log::info line it will just hang forever as before.

This seems to be a timing thing.

I'm using esp-idf master. and esp-idf-hal v0.42.5

ivmarkov commented 9 months ago

It does not work because - apparently - the ESP IDF UART driver does not send any notification on UART FIFO queue being empty / being consumed: https://github.com/espressif/arduino-esp32/issues/6385

Options to fix this:

ivmarkov commented 9 months ago

@t-moe I've in the meantime come with a simple solution which uses the esp_idf_hal::task::yield_now primitive. While not efficient in terms of CPU utilization, it should work.

Maybe you can try it out.

t-moe commented 9 months ago

Hei @ivmarkov , thanks for working on this and yes, I'll take a look and report back. (It might take a while though....)