diff --git a/apps/gateway/main/Kconfig.projbuild b/apps/gateway/main/Kconfig.projbuild index 2e91f54..fe1723f 100644 --- a/apps/gateway/main/Kconfig.projbuild +++ b/apps/gateway/main/Kconfig.projbuild @@ -831,6 +831,17 @@ config GATEWAY_KNX_TP_BAUDRATE range 1200 921600 default 19200 +config GATEWAY_KNX_TP_STARTUP_TIMEOUT_MS + int "KNX TP UART startup timeout (ms)" + depends on GATEWAY_KNX_BRIDGE_SUPPORTED + range 0 10000 + default 2000 + help + How long the gateway should keep retrying TP-UART startup before + giving up. This covers transient UART/GPIO ownership races that can + happen immediately after an ETS-triggered reboot. Set to 0 to keep the + previous single-shot startup behavior. + config GATEWAY_KNX_TP_UART_9BIT_MODE bool "KNX TP UART 9-bit mode" depends on GATEWAY_KNX_BRIDGE_SUPPORTED diff --git a/apps/gateway/main/app_main.cpp b/apps/gateway/main/app_main.cpp index 758e838..3288c60 100644 --- a/apps/gateway/main/app_main.cpp +++ b/apps/gateway/main/app_main.cpp @@ -107,6 +107,10 @@ #define CONFIG_GATEWAY_485_CONTROL_TASK_PRIORITY 4 #endif +#ifndef CONFIG_GATEWAY_KNX_TP_STARTUP_TIMEOUT_MS +#define CONFIG_GATEWAY_KNX_TP_STARTUP_TIMEOUT_MS 2000 +#endif + #ifndef CONFIG_GATEWAY_SMARTCONFIG_TIMEOUT_SEC #define CONFIG_GATEWAY_SMARTCONFIG_TIMEOUT_SEC 60 #endif @@ -907,6 +911,8 @@ extern "C" void app_main(void) { default_knx.tp_uart.tx_pin = CONFIG_GATEWAY_KNX_TP_TX_PIN; default_knx.tp_uart.rx_pin = CONFIG_GATEWAY_KNX_TP_RX_PIN; default_knx.tp_uart.baudrate = static_cast(CONFIG_GATEWAY_KNX_TP_BAUDRATE); + default_knx.tp_uart.startup_timeout_ms = + static_cast(CONFIG_GATEWAY_KNX_TP_STARTUP_TIMEOUT_MS); #ifdef CONFIG_GATEWAY_KNX_TP_UART_9BIT_MODE default_knx.tp_uart.nine_bit_mode = true; #else diff --git a/apps/gateway/sdkconfig b/apps/gateway/sdkconfig index 73f4a0c..7dcc213 100644 --- a/apps/gateway/sdkconfig +++ b/apps/gateway/sdkconfig @@ -697,6 +697,7 @@ CONFIG_GATEWAY_KNX_TP_UART_PORT=0 CONFIG_GATEWAY_KNX_TP_TX_PIN=-1 CONFIG_GATEWAY_KNX_TP_RX_PIN=-1 CONFIG_GATEWAY_KNX_TP_BAUDRATE=19200 +CONFIG_GATEWAY_KNX_TP_STARTUP_TIMEOUT_MS=2000 CONFIG_GATEWAY_KNX_TP_UART_9BIT_MODE=y CONFIG_GATEWAY_BRIDGE_KNX_TASK_STACK_SIZE=12288 CONFIG_GATEWAY_BRIDGE_KNX_TASK_PRIORITY=5 diff --git a/components/gateway_knx/include/gateway_knx.hpp b/components/gateway_knx/include/gateway_knx.hpp index 2a89724..1464b57 100644 --- a/components/gateway_knx/include/gateway_knx.hpp +++ b/components/gateway_knx/include/gateway_knx.hpp @@ -33,6 +33,7 @@ class TpuartUartInterface; constexpr uint16_t kGatewayKnxDefaultUdpPort = 3671; constexpr const char* kGatewayKnxDefaultMulticastAddress = "224.0.23.12"; constexpr uint32_t kGatewayKnxDefaultTpBaudrate = 19200; +constexpr uint32_t kGatewayKnxDefaultTpStartupTimeoutMs = 2000; struct GatewayKnxTpUartConfig { int uart_port{1}; @@ -41,6 +42,7 @@ struct GatewayKnxTpUartConfig { uint32_t baudrate{kGatewayKnxDefaultTpBaudrate}; size_t rx_buffer_size{1024}; size_t tx_buffer_size{1024}; + uint32_t startup_timeout_ms{kGatewayKnxDefaultTpStartupTimeoutMs}; uint32_t read_timeout_ms{20}; bool nine_bit_mode{true}; }; diff --git a/components/gateway_knx/src/gateway_knx.cpp b/components/gateway_knx/src/gateway_knx.cpp index d50f66c..0909ca4 100644 --- a/components/gateway_knx/src/gateway_knx.cpp +++ b/components/gateway_knx/src/gateway_knx.cpp @@ -804,6 +804,9 @@ std::optional GatewayKnxConfigFromValue(const DaliValue* value config.tp_uart.tx_buffer_size = static_cast(std::max( 128, ObjectIntAny(serial, {"txBufferSize", "tx_buffer_size"}) .value_or(static_cast(config.tp_uart.tx_buffer_size)))); + config.tp_uart.startup_timeout_ms = static_cast(std::max( + 0, ObjectIntAny(serial, {"startupTimeoutMs", "startup_timeout_ms"}) + .value_or(static_cast(config.tp_uart.startup_timeout_ms)))); config.tp_uart.read_timeout_ms = static_cast(std::max( 1, ObjectIntAny(serial, {"readTimeoutMs", "read_timeout_ms"}) .value_or(static_cast(config.tp_uart.read_timeout_ms)))); @@ -840,6 +843,7 @@ DaliValue GatewayKnxConfigToValue(const GatewayKnxConfig& config) { serial["baudrate"] = static_cast(config.tp_uart.baudrate); serial["rxBufferSize"] = static_cast(config.tp_uart.rx_buffer_size); serial["txBufferSize"] = static_cast(config.tp_uart.tx_buffer_size); + serial["startupTimeoutMs"] = static_cast(config.tp_uart.startup_timeout_ms); serial["readTimeoutMs"] = static_cast(config.tp_uart.read_timeout_ms); serial["nineBitMode"] = config.tp_uart.nine_bit_mode; out["tpUart"] = std::move(serial); @@ -2447,7 +2451,17 @@ bool GatewayKnxTpIpRouter::configureTpUart() { ESP_LOGE(kTag, "%s", last_error_.c_str()); return false; } + const TickType_t startup_timeout_ticks = + pdMS_TO_TICKS(config_.tp_uart.startup_timeout_ms); + const TickType_t retry_poll_ticks = std::max(1, pdMS_TO_TICKS(20)); + const TickType_t startup_begin_tick = xTaskGetTickCount(); tp_uart_online_ = ets_device_->enableTpUart(true); + while (!tp_uart_online_ && startup_timeout_ticks > 0 && + (xTaskGetTickCount() - startup_begin_tick) < startup_timeout_ticks) { + vTaskDelay(retry_poll_ticks); + ets_device_->loop(); + tp_uart_online_ = ets_device_->tpUartOnline(); + } if (!tp_uart_online_) { last_error_ = "OpenKNX failed to initialize KNX TP-UART uart=" + std::to_string(config_.tp_uart.uart_port) + " tx=" + @@ -2465,6 +2479,11 @@ bool GatewayKnxTpIpRouter::configureTpUart() { ESP_LOGE(kTag, "%s", last_error_.c_str()); return false; } + const TickType_t startup_elapsed_ticks = xTaskGetTickCount() - startup_begin_tick; + if (startup_elapsed_ticks > 0) { + ESP_LOGI(kTag, "KNX TP-UART startup settled after %lu ms", + static_cast(pdTICKS_TO_MS(startup_elapsed_ticks))); + } ESP_LOGI(kTag, "KNX TP-UART online uart=%d tx=%s rx=%s baud=%u nineBit=%d", config_.tp_uart.uart_port, UartPinDescription(config_.tp_uart.tx_pin, tp_uart_tx_pin_).c_str(), diff --git a/tpuart b/tpuart index d95248f..0dd32a4 160000 --- a/tpuart +++ b/tpuart @@ -1 +1 @@ -Subproject commit d95248f994da5470242395c02fac1d4bd6887681 +Subproject commit 0dd32a4246cb6a7a254239377aac03072a6bf571