← index #16079PR #17038
Related · high · value 2.665
QUERY · ISSUE

UART with the same pin for both RX and TX doesn't work on ESP32 variants

openby arduino12opened 2024-10-25updated 2026-03-24
enhancementport-esp32

Port, board and/or hardware

esp32 port

MicroPython version

MicroPython v1.23.0 on 2024-06-02; LOLIN_C3_MINI with ESP32-C3FH4
MicroPython v1.23.0 on 2024-06-02; LOLIN_S2_MINI with ESP32-S2FN4R2
MicroPython v1.23.0-602.g403401490d on 2024-10-25; LOLIN S3 MINI with ESP32S3
MicroPython v1.23.0 on 2024-06-02; Generic ESP32S3 module with ESP32S3

Reproduction

from micropython import const
from machine import Pin, UART, mem32

PIN_NUM = const(2)
PIN_FUNC_REG = const(0x60004554 + 4 * PIN_NUM) # GPIO_FUNCx_OUT_SEL_CFG_REG 
UART_NUM = const(1)
UART_BAUDRATE = const(1000000)

pin = Pin(PIN_NUM)

# pass the same `pin` to `tx` and `rx` doesn't work!
uart = UART(UART_NUM, UART_BAUDRATE, tx=pin, rx=pin)
uart.write(b'test') # UART signal doesn't routed to `PIN_NUM `.
print(uart.read()) # nothing gets printed..

# pass only `tx` (to get the `UART_SIG` number)
uart = UART(UART_NUM, UART_BAUDRATE, tx=pin)
UART_SIG = mem32[PIN_FUNC_REG] # see "Peripheral Signals via GPIO Matrix" in datasheet

# now try again, this time overwrite `PIN_FUNC_REG` with previous value
uart = UART(UART_NUM, UART_BAUDRATE, tx=pin, rx=pin)
mem32[PIN_FUNC_REG] = UART_SIG # ESP32-C3 is 6, ESP32-S3 is 12...
uart.write(b'test')
print(uart.read()) # b'test' is printed!

Expected behaviour

I expected:

uart = UART(UART_NUM, UART_BAUDRATE, tx=pin, rx=pin)

to work- but is seems to overwrite the TX output configuration and only the RX pin is configured correctly.

My workaround is to re-write the TX output configuration after the UART initialization:

uart = UART(UART_NUM, UART_BAUDRATE, tx=pin, rx=pin)
mem32[PIN_FUNC_REG] = UART_SIG

I think the UART class needs to support this case of a single pin for both TX and RX.
I use it to control some single line smart servo motors with open-drain UART bus...

The ESP32 support this common UART configuration (like RS485),
it also support echo-cancellation that may also be a nice option for the UART class..

Observed behaviour

Tested the provided sample code on ESP32-S2, ESP32-S3, ESP32-C3 -
All have the same bug and the same suggested workaround fix.

Additional Information

No, I've provided everything above.

Code of Conduct

Yes, I agree

CANDIDATE · PULL REQUEST

docs/esp32/quickref.rst Document that a Pin can be passed to UART constructor on the ESP32 ports.

openby bixb922opened 2025-03-29updated 2025-04-21
docs

Summary

The UART() constructor has the tx= and rx= parameters to specify a pin number. On the ESP32 port, an initialized machine.Pin() object can also be passed. This is useful to specify parameters such as mode=Pin.OPEN_DRAIN or drive=Pin.DRIVE_3

I could not find the documentation for this neither on the ESP32 specific page (https://docs.micropython.org/en/latest/esp32/quickref.html) nor on the generic UART() page ((https://docs.micropython.org/en/latest/library/machine.UART.html)

Testing

This is a documentation change only. However, I tested on a ESP32-S3 that mode=Pin.OPEN_DRAIN and drive=Pin.DRIVE_3 alter the voltages at the UART pins accordingly:

txpin = Pin(tx_number, mode=Pin.OUT, drive=Pin.DRIVE_3)
rxpin = Pin(rx_number, mode=Pin.IN)
while True:
      UART( 1, baudrate=baudrate, tx=txpin, rx=rxpin)
      time.sleep(5)
      UART( 1, baudrate=baudrate, tx=txpin, rx=rxpin, invert=UART.INV_TX)
      time.sleep(5)

See also discussion #16959

Keyboard

j / / n
next pair
k / / p
previous pair
1 / / h
show query pane
2 / / l
show candidate pane
c
copy suggested comment
r
toggle reasoning
g i
go to index
?
show this help
esc
close overlays

press ? or esc to close

copied