[uart] Always call pin setup for UART0 default pins on ESP-IDF

Fix UART0 communication failure on ESP-IDF 5.4.2+ that affected LD2410
and other UART sensors on default UART0 pins across all ESP32 variants.

The gpio_reset_pin() workaround from PR #12519 sets GPIO_MODE_DISABLE
which disables the input buffer. Without a subsequent pin->setup() call,
uart_set_pin() on IOMUX-connected UART0 default pins does not re-enable
the input buffer, so the RX pin cannot receive data.

PR #11914 made pin->setup() conditional on pull/open-drain flags, which
meant most users' UART0 RX pins never got their input buffer re-enabled.

The fix: always call pin->setup() for pins matching U0TXD_GPIO_NUM or
U0RXD_GPIO_NUM to restore proper GPIO direction after gpio_reset_pin().
For non-UART0 pins, the conditional behavior is preserved to avoid
breaking external components (issue #11823).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
J. Nick Koston
2026-02-19 17:08:07 -06:00
parent 17a810b939
commit e81eff22c3

View File

@@ -149,21 +149,31 @@ void IDFUARTComponent::load_settings(bool dump_config) {
// Workaround for ESP-IDF issue: https://github.com/espressif/esp-idf/issues/17459
// Commit 9ed617fb17 removed gpio_func_sel() calls from uart_set_pin(), which breaks
// UART on default UART0 pins that may have residual state from boot console.
// Reset these pins before configuring UART to ensure they're in a clean state.
if (tx == U0TXD_GPIO_NUM || tx == U0RXD_GPIO_NUM) {
auto is_default_uart0_pin = [](int8_t pin_num) -> bool {
return pin_num == U0TXD_GPIO_NUM || pin_num == U0RXD_GPIO_NUM;
};
// Reset UART0 default pins before configuring UART to ensure they're in a clean state.
if (is_default_uart0_pin(tx)) {
gpio_reset_pin(static_cast<gpio_num_t>(tx));
}
if (rx == U0TXD_GPIO_NUM || rx == U0RXD_GPIO_NUM) {
if (is_default_uart0_pin(rx)) {
gpio_reset_pin(static_cast<gpio_num_t>(rx));
}
// Setup pins after reset to preserve open drain/pullup/pulldown flags
auto setup_pin_if_needed = [](InternalGPIOPin *pin) {
// Setup pins after reset to configure GPIO direction and pull resistors.
// For UART0 default pins, setup() must always be called because gpio_reset_pin()
// above sets GPIO_MODE_DISABLE which disables the input buffer. Without setup(),
// uart_set_pin() on ESP-IDF 5.4.2+ does not re-enable the input buffer for
// IOMUX-connected pins, so the RX pin cannot receive data (see issue #10132).
// For other pins, only call setup() if pull or open-drain flags are set to avoid
// disturbing the default pin state which breaks some external components (#11823).
auto setup_pin_if_needed = [&is_default_uart0_pin](InternalGPIOPin *pin) {
if (!pin) {
return;
}
const auto mask = gpio::Flags::FLAG_OPEN_DRAIN | gpio::Flags::FLAG_PULLUP | gpio::Flags::FLAG_PULLDOWN;
if ((pin->get_flags() & mask) != gpio::Flags::FLAG_NONE) {
if (is_default_uart0_pin(pin->get_pin()) || (pin->get_flags() & mask) != gpio::Flags::FLAG_NONE) {
pin->setup();
}
};