-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Description
CircuitPython version and board name
Adafruit CircuitPython 10.0.0-beta.3 on 2025-08-29; Pimoroni Pico Plus 2 W with rp2350b
vs.
Adafruit CircuitPython 10.0.0-beta.3 on 2025-08-29; Adafruit Feather M4 Express with samd51j19
Code/REPL
# in all cases, u1 is wired to u2: tx to rx, and cts to rts
import time ; time.sleep(3) # wait for serial after reset
import sys
import board
import busio
def write(size, ch):
print(f'writing {size} bytes...', end=" ")
written = u1.write(f'{size * ch}')
time.sleep(1)
print(f'{written=} {} {u2.in_waiting=}')
if "Pico" in sys.implementation._machine:
u1 = busio.UART(tx=board.GP0, rx=board.GP1, cts=board.GP2, rts=board.GP3)
u2 = busio.UART(tx=board.GP4, rx=board.GP5, cts=board.GP6, rts=board.GP7)
elif "M4" in sys.implementation._machine:
u1 = busio.UART(tx=board.A4, rx=board.A5, cts=board.D9, rts=board.D6)
u2 = busio.UART(tx=board.A2, rx=board.A3, cts=board.D11, rts=board.D10)
u2.reset_input_buffer()
print(f'\nbusio initial {u2.in_waiting=}')
write(64, "A") # presumably fills busio default receiver_buffer_size=64 bytes
write(32, "B") # raspberry pi only: presumably fills raspberrypi hw uart rx buffer 32 bytes
if "Pico" in sys.implementation._machine:
size = 6 # <=5 somehow works; 6 will hang the serial console (CIRCUITPY is OK)
elif "M4" in sys.implementation._machine:
size = 16 # can write as much as memory allows (e.g., 32768)
write(size, "C")
r = u2.read(256)
print(f'read {len(r)} bytes: {r}')
Behavior
Pico output (writing 102 bytes):
busio initial u2.in_waiting=0
writing 64 bytes... written=64 () u2.in_waiting=64
writing 32 bytes... written=32 () u2.in_waiting=64
writing 6 bytes...
• The write blocks, but also the serial console hangs (no control-c). CIRCUITPY is still accessible. Reset is required to regain use of the serial console.
or
Pico output (writing 101 bytes):
busio initial u2.in_waiting=0
writing 64 bytes... written=64 () u2.in_waiting=64
writing 32 bytes... written=32 () u2.in_waiting=64
writing 5 bytes... written=5 () u2.in_waiting=64
read 101 bytes: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCC'
• Flow control limit not reached: bytes written == bytes read.
• Note that in_waiting
only counts the receiver_buffer_size
, not the pico hardware buffer or the magic extra 5 bytes.
...compare to...
M4 output:
busio initial u2.in_waiting=0
writing 64 bytes... written=64 () u2.in_waiting=64
writing 32 bytes... written=32 () u2.in_waiting=64
writing 16 bytes... written=16 () u2.in_waiting=64
read 64 bytes: b'AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCC'
• Hardware flow control doesn't seem to work. Any number of bytes can be written and the buffer will contain the last 64 bytes sent.
Description
No response
Additional information
I also tested an Adafruit ESP32-S2 Feather, same pinout as the M4, so I plugged it into the same wiring that the M4 was in. Hardware flow control seemed to work here, lending support to the wiring being correct. The ESP32-S2 will write the 64 bytes, then the 32 bytes, then up to 242 more bytes (implying some buffering behind-the-scenes of default receiver_buffer_size
of 242+32=274 bytes). If 243 are attempted, the write blocks and the serial console hangs, like raspberrypi. I looked for esp-idf definitions of receive size, but didn't find it.
Note that on the ESP32-S2, in_waiting
shows a maximum of 96, which presumably includes the 64 receiver_buffer_size
bytes, but only 32 of the behind-the-scenes bytes.
Perhaps it's intended behavior for hardware flow control to block writes indefinitely, ignoring the UART init timeout value. This creates a hazard for any code with hardware flow control in the case that the receiver has trouble.
It's even more hazardous for PIO UART with hardware flow control since only 8 received bytes can be buffered at a time before blocking.
It also rules out the use of asyncio
for UART writer + reader configurations communicating with each other, where the reader may not keep up with the receive buffer for a wide variety of reasons.
My goal is to understand the (intended) behaviors of UART across ports, including raspberrypi
PIO UART, to be able to write more universal CircuitPython UART code.