← index #16079PR #18996
Related · high · value 2.829
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

esp32/machine_uart: Allow passing -1 to specify pin will be unused.

openby DvdGiessenopened 2026-03-25updated 2026-03-25
port-esp32

Summary

The UART constructor previously required you to pass a TX and RX pin, else it would use the default pin numbers.

However, sometimes you don't want to allocate a pin for one of these functions, for example when you have a read-only or write-only UART. The workaround would be to pass an unused pin for the unused function, or to let some pin first be claimed by the UART and then steal it away for some other function.

The ESP-IDF supports leaving a pin unused by passing UART_PIN_NO_CHANGE (which equal -1) as the pin number.

machine_pin_get_id doesn't consider -1 a valid pin number (rightly so). So I used a small macro, inspired by the one used in machine_lan.c, that passes through a -1 as UART_PIN_NO_CHANGE.

(The RTS and CTS pins already default to UART_PIN_NO_CHANGE, so they were only changed for consistency with the other pin assignments.)

Testing

Tested it on a few ESP32-based boards where I have a read-only UART and no other free pins. When passing tx=-1 everything works as expected: The UART functions as normal, without trying to configure a pin for the TX function.

Generative AI

I did not use generative AI tools when creating this PR.

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