rp2 uart bugs?

RP2040 based microcontroller boards running MicroPython.
Target audience: MicroPython users with an RP2040 boards.
This does not include conventional Linux-based Raspberry Pi boards.
Post Reply
kevinkk525
Posts: 969
Joined: Sat Feb 03, 2018 7:02 pm

rp2 uart bugs?

Post by kevinkk525 » Thu Mar 04, 2021 8:50 pm

EDIT: see the github issue https://github.com/micropython/micropython/issues/6999


I'm trying to get the UART to work but its buffer is so small, I can't even send 40 bytes at 460k baudrate, because only 35-37 actually get read.. And that is with reading without any delays in the loop except for merging the received bytes, which does indeed take longer than a single character (takes ~40us) but not longer than filling a 32 Byte buffer.. I'm a bit puzzled about this.. In my application I actually wait 10us if no data is available and read directly into a preallocated bytearray until the expected amount of bytes is received but still I can't receive all bytes!
When using 2 esp32 I could easily send 400 Bytes with a buffer of 256 Bytes.

So either I'm missing something crucial, or there is a bug somewhere?

I should add that I'm using the uart with the changes by @RobertHH to add a timeout and I use it with a timeout of 10.

Edit below, but summary is:
So somewhere in the UART implementation there must be some serious bugs and the uart is rather useless at the moment. It needs a very long delay on the sender/the uart line (>1ms) after a packet was sent/received to even return it to uart.read() and at that point it already received too much and its buffer probably drops characters. The delay might be due to uart.read() using a timeout implementation but even without that, uart is completely unreliable when receiving anything more than 30 Bytes.

Test script on the rp2:

Code: Select all

>>> uart = machine.UART(1, rx=machine.Pin(5), tx=machine.Pin(4), baudrate=460800, timeout=10)
>>> def test(t,t2):
...     while True:
...         recv=b''
...         st=time.ticks_us()
...         while time.ticks_diff(time.ticks_us(),st)<t:
...             if uart.any():
...                 recv+=uart.read()
...             else:
...                 time.sleep_us(t2)
...         if len(recv)>0:
...             print(recv,len(recv))
...
...
...
>>> test(10000,10)
Result when sending 40 bytes from an esp32 (4* b'0123456789'):

Code: Select all

b'0123456789012345678901234567890123456' 37
b'012345678901234567890123456789012345' 36
b'01234567890123456789012345678901234' 35
b'0123456789012345678901234567890123456' 37

This test reveals something more interesting:

Code: Select all

>>> def test2(t,t2):
...     while True:
...         gc.collect()
...         recv=b''
...         read=[]
...         st=time.ticks_us()
...         while time.ticks_diff(time.ticks_us(),st)<t:
...             if uart.any():
...                 rr=uart.read()
...                 recv+=rr
...                 read.append(len(rr))
...             else:
...                 time.sleep_us(t2)
...         if len(recv)>0:
...             print(recv,len(recv),read)
...
...
...
>>> test2(10000,1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in test2
NameError: name 'gc' isn't defined
>>> import gc
>>> test2(10000,1)
b'012345678901234567890123456789012345678' 39 [39]
b'012345678901234567890123456789012345' 36 [36]
b'0123456789012345678901234567890123456789' 40 [40]
b'01234567890123456789012345678901' 32 [32]
b'01234567890123456789012345678901' 32 [32]
b'01234567890123456789012345678901' 32 [32]
b'012345678901234567890123456789012345' 36 [36]
b'01234567890123456789012345678901234512345678901234567890123456789' 65 [65]
b'01234567890123456789012345678901234512345678901234567890123456789' 65 [65]
b'0123456789012345678901234567890156789012345678901234567890123456784567890123456789012345678901234562345678901234567890123456789012349012345678901234567890123456789017890123456789012345678901234567894567890123456789012345678901234562345678901234567890123456789012349012345678901234567890123456789012349012345678901234567890123456789017890123456789012345678901234567894567890123456789012345678901234562345678901234567890123456789012349012345678901234567890123456789016789012345678901234567890123456784567890123456789012345678901234561234567890123456789012345678901234012345678901234567890123456789012789012345678901234567890123456789456789012345678901234567890123456234567890123456789012345678901234901234567890123456789012345678901789012345678901234567890123456789456789012345678901234567890123456234567890123456789012345678901234012345678901234567890123456789012789012345678901234567890123456789' 895 [895]
The uart read is a single operation reading more than the uart buffer but always looses some bytes. I send 40Bytes, later 80 and 1280..

It gets even more interesting, I can introduce a delay on the sender between the first 20 Bytes and the 2nd 20 Bytes to send 40 Bytes in total and even with a delay of 500us between them, the rp2 still misses characters and reads from the uart in a single operation!

On the sender:

Code: Select all

a=b'0123456789'
uart.write(a*2);time.sleep_us(500);uart.write(a*2)
Result on the rp2:

Code: Select all

b'01234567890123456789012345678901' 32 [32]
b'012345678901234567890123456789012345' 36 [36]
b'01234567890123456789012345678901' 32 [32]
b'0123456789012345678901234567890123456789' 40 [40]
b'012345678901234567890123456789012345' 36 [36]
b'012345678901234567890123456789012345' 36 [36]
b'012345678901234567890123456789012345' 36 [36]
b'01234567890123456789012345678901' 32 [32]
b'01234567890123456789012345678901' 32 [32]
b'012345678901234567890123456789012345789' 39 [39]
b'012345678901234567890123456789012345689' 39 [39]
b'012345678901234567890123456789012345689' 39 [39]
b'0123456789012345678901234567890123456789' 40 [40]
b'0123456789012345678901234567890123456789' 40 [40]
b'01234567890123456789012345678901' 32 [32]
b'0123456789012345678901234567890123456789' 40 [40]

Now comparison to using the uart without a timeout:

Sending with a delay works fine (most of the time) for 40 Bytes (so maybe a problem with the timeout implementation?), even though the often get read within one operation which does not make sense with a 500us delay. When sending 2x30Bytes it sometimes works and I get 60 Bytes in a single read, sometimes only 32 Bytes:

Code: Select all

b'01234567890123456789012345678901' 32 [32]
b'012345678901234567890123456789012345678901234567890123456789' 60 [60]
b'012345678901234567890123456789012345678901234567890123456789' 60 [60]
b'01234567890123456789012345678901' 32 [32]
b'012345678901234567890123456789012345678901234567890123456789' 60 [60]
Only with a delay of 5ms I actually get 2 read operations. At that point sending 2*30 Bytes actually works:

Code: Select all

b'012345678901234567890123456789012345678901234567890123456789' 60 [30, 30]
b'012345678901234567890123456789012345678901234567890123456789' 60 [30, 30]
b'012345678901234567890123456789012345678901234567890123456789' 60 [30, 30]
b'012345678901234567890123456789012345678901234567890123456789' 60 [30, 30]
Doing the same thing (sending 2*30 Bytes with a 5ms delay in between) with timeout=10 results in a single read operation that gets all 60Bytes, so that's not quite right either but at least now it actually reads all Bytes.

But don't try to send more than the buffer in a single operation without sufficient delay between sends and it will mess up the data again..

So somewhere in the UART implementation there must be some serious bugs and the uart is rather useless at the moment. It needs a very long delay on the sender/the uart line (>1ms) after a packet was sent/received to even return it to uart.read() and at that point it already received too much and its buffer probably drops characters. The delay might be due to uart.read() using a timeout implementation but even without that, uart is completely unreliable when receiving anything more than 30 Bytes.

I have yet to compare this behaviour with my esp32s but this seems rather very wrong to me.. Didn't test it this thorougly on my esp32s because I had no problem sending 400 Bytes between them, which was fine for my case.
Last edited by kevinkk525 on Fri Mar 05, 2021 5:14 pm, edited 4 times in total.
Kevin Köck
Micropython Smarthome Firmware (with Home-Assistant integration): https://github.com/kevinkk525/pysmartnode

kevinkk525
Posts: 969
Joined: Sat Feb 03, 2018 7:02 pm

Re: rp2 uart buffer

Post by kevinkk525 » Fri Mar 05, 2021 6:36 am

I updated my first post with further findings.
Kevin Köck
Micropython Smarthome Firmware (with Home-Assistant integration): https://github.com/kevinkk525/pysmartnode

kevinkk525
Posts: 969
Joined: Sat Feb 03, 2018 7:02 pm

Re: rp2 uart bugs?

Post by kevinkk525 » Fri Mar 05, 2021 8:37 am

So same tests with 2 esp32s and what can I say.. you can hardly compare them.

I can actually send 40kB (!!) in a single send instruction and it gets read from the uart in a single operation without losing a single Byte!! I can't send more because the sening side throws a memory allocation error :D But that only works with timeout=10 as it continually reads until there is no data for 10ms. With no timeout, 800 Bytes seems to be the highest, even though I only wait 1us between reading cycles (but it needs at least 30us for merging the received bytes and adding the length to the list).

When sending 2*50 Bytes with a delay in between it only gets read as a single operation when using a timeout of 10 in the constructor which is obviously a problem when using read() without specifying an amount to read.
When using the uart without a timeout, it actually reads 2*50Bytes in 2 separate operations even if there is only a delay of 10us! That's how it should be.

I hope this helps someone to fix the bugs with the RP2 uart to make it a bit more reliable and closer to the esp32 uart.
Kevin Köck
Micropython Smarthome Firmware (with Home-Assistant integration): https://github.com/kevinkk525/pysmartnode

kevinkk525
Posts: 969
Joined: Sat Feb 03, 2018 7:02 pm

Re: rp2 uart bugs?

Post by kevinkk525 » Fri Mar 05, 2021 8:59 am

Further testing on the rp2 with the standard firmware (without @RobertHHs changes for timeout) revealed that uart.read() (without requesting a certain number of Bytes) is never returning.. so.. great for testing :D

So changed my test2 to read only 20 Bytes a time but that only works out for sending 20 Bytes.. If you send 40 Bytes, it already screws up.. So no improvement there..
Kevin Köck
Micropython Smarthome Firmware (with Home-Assistant integration): https://github.com/kevinkk525/pysmartnode

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: rp2 uart bugs?

Post by pythoncoder » Fri Mar 05, 2021 1:50 pm

I suggest you raise an issue with a minimal test case. It's a shame the UART constructor doesn't accept the rxbuf arg.
Peter Hinch
Index to my micropython libraries.

kevinkk525
Posts: 969
Joined: Sat Feb 03, 2018 7:02 pm

Re: rp2 uart bugs?

Post by kevinkk525 » Fri Mar 05, 2021 5:14 pm

yes you can ignore my rants above, I opened https://github.com/micropython/micropython/issues/6999
Kevin Köck
Micropython Smarthome Firmware (with Home-Assistant integration): https://github.com/kevinkk525/pysmartnode

Post Reply