Check if UART write is complete?

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
Jibun no kage
Posts: 144
Joined: Mon Jul 25, 2022 9:45 pm

Check if UART write is complete?

Post by Jibun no kage » Wed Aug 17, 2022 6:48 pm

I have a UART class that is working well with on exception, is there a way to check if a given write to UART is complete? I have a potential scenario where deinit() of the UART may be called before the last, most recent, write may not be complete, completely transmitted. I set a 1 second wait to address the issue, and that is ok, in that it works, but is a kludge. The deinit is only called when ESP/pico is going to reset, so I can leave as is. But... if there is a better way?

The classic python method would be to call flush... [UART write] "This method writes bytes to the UART via a transmit (output) FIFO. It will block until all the supplied data has been transferred to the FIFO, but does not wait for the bytes to be transmitted. To wait until the FIFO has drained, ie. until all the data has been sent out on the wire, use uart.flush()."

MP UART write work different, I suspect, since no flush() method is implemented.

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Check if UART write is complete?

Post by Roberthh » Wed Aug 17, 2022 8:20 pm

no uart.flush(). There have been requests to provide an API to the esp32 wait_tx_done() function, which is the same. The implementation is basically straight forward, and will be heavily hardware dependent. It may still be, that is is easy to tell for code when the FIFO is empty, but not, whether the last byte in the transmit buffer has been transmitted. So an error of one byte (symbol) is possible.

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Check if UART write is complete?

Post by Roberthh » Wed Aug 17, 2022 8:28 pm

But maybe instaed of a blocking flush() a companion to uart.any() would be better, which returns immediately with the number of bytes still to be sent. Using that, a blocking flush can be made, if needed.

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Check if UART write is complete?

Post by Roberthh » Thu Aug 18, 2022 7:02 am

Looking into various ports, the flush() method may be doable. API

rc = uart.flush([timeout])

where rc is True, is the flush succeeded, and False, if the timeout happens. The default timeout is t.b.d. (either 0 or infinite).

For ESP32 this does not seem to be a problem. There is an appropriate sdk call. uart_wait_tx_done().
For RP2 there is no appropriate SKD call. There is the check for the writable hardware FIFO, but the FIFO size is 32 bytes. At least, there is an additional bit in the UARTFR register, telling whether the TX FIFO is empty or not. But no way to tell the size of the FiFo.
For MIMXRT. Possible to implement, since it has an internal callback & status flag which is set when the transfer is finished.
STM32: There is uart_tx_wait() which may do the job.
ESP8266: There is an internal blocking(!) uart_flush() function.
nrf: There is a nrfx_uart_tx_in_progress() function which sounds promising.
.....
SAMD: w.i.p.: should be doable. There is no HW FIFO, so it's sufficient to check the tx buffer size.

In any case, for implementations just returning the state of the FIFO there may be a 1-2 byte transmit timing error for the last data being in the transmit shift register(s).

Besides that, the uart.ioctl has an option to check, whether the stream is writeable. But that's not the same as asking, whether a transmit is finished, and not consistently implemented.

Jibun no kage
Posts: 144
Joined: Mon Jul 25, 2022 9:45 pm

Re: Check if UART write is complete?

Post by Jibun no kage » Thu Aug 18, 2022 3:43 pm

I would be looking for the same logic to be on Pico port as well. But that aside. This will not be added any time soon right? So for now, I think my only realistic option is to use my own queue for writes pending, and ensuring the that for each item in the queue a reasonable time is waited, a reverse timeout... wait at least X time.

Since... MQTT Broker->ESP MQTT subscribe->ESP UART write->Pico UART read->Do somthing->Pico UART write->ESP UART Read->ESP MQTT Publish->MQTT Broker

Interesting, that using check_msg() which is non-blocking, lets me also use UART.any() in the same main loop so I can catch any inbound MQTT on ESP, then any inbound from Pico. It is a basic FIFO model. It is only on Pico stop/pending reset, I need to ensure that any in-progress UART outbound from Pico to ESP is completed if possible.

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Check if UART write is complete?

Post by Roberthh » Thu Aug 18, 2022 4:42 pm

I can implement that and check, whether it works well. After having it done for one ports, the other ports are easy. But it will take a while, if ever, before that goes into mainline. Until then you could use your own firmware versions.
The feature is requested regularly e.g. by people dealing with RS485.

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Check if UART write is complete?

Post by Roberthh » Thu Aug 18, 2022 7:58 pm

So I have a first implementation for RP2040. Some pictures below. The test script is:

Code: Select all

from machine import UART, Pin

u=UART(0, 9600)
p2=Pin(2, Pin.OUT, value=0)

def run(n=1, t=9999):
    p2(1)
    u.write("abcde"*n)
    print(u.flush(t))
    p2(0)
The code is at: https://github.com/robert-hh/micropytho ... uart_flush

Timing Diagrams:
5 byte message
uart_flush_5.png
uart_flush_5.png (40.26 KiB) Viewed 7392 times
10 byte message
uart_flush_10.png
uart_flush_10.png (38.43 KiB) Viewed 7392 times
Note that for the short message flush() returns while the last byte is still sent. short means: length <= 5, for whatever reason.

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Check if UART write is complete?

Post by Roberthh » Fri Aug 19, 2022 7:13 am

I added uart.flush() for esp32 and mimxrt. These two behave consistent, in that uart.flush() returns after the last bit has been sent.
Edit:
Added for ESP8266 and STM32.
For STM32 it is a dummy function, since STM32's uart,write() is unbufferd and returns only after the last byte has been sent.
Edit 2:
Added to the 2nd level commit queue of the SAMD port.
Not made for the nrf port, because it is anyhow incomplete (e.g. no uart.any()). A dummy uart.flush() could be added here.
Not made for the Zephyr port, although it uses a synchronous write. So a dummy function should be sufficient.
Edit 3:
Added as dummy fct. for the nrf port,
Added for the CC3200 port, even if that is hardly still used.
Added a short extension for the documentations-

Post Reply