Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async Uart Write hangs forever #362

Closed
t-moe opened this issue Jan 12, 2024 · 3 comments
Closed

Async Uart Write hangs forever #362

t-moe opened this issue Jan 12, 2024 · 3 comments

Comments

@t-moe
Copy link

t-moe commented Jan 12, 2024

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
Copy link
Collaborator

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

Options to fix this:

  • Patch the ESP IDF with a new UART event type and possibly upstream this patch in future
  • Offload the async "send" work to another hidden thread that does the sending stuff in a blocking way and notifies when done. Ugly

@github-project-automation github-project-automation bot moved this from Todo to Done in esp-rs Jan 26, 2024
@ivmarkov
Copy link
Collaborator

@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.

ivmarkov added a commit that referenced this issue Jan 26, 2024
@t-moe
Copy link
Author

t-moe commented Jan 26, 2024

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

ivmarkov added a commit that referenced this issue Jan 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

No branches or pull requests

2 participants