feat(gateway): add support for KNX TP UART 9-bit mode and enhance UART pin configuration

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-05-14 22:39:14 +08:00
parent 39ef630608
commit 4553ed32e7
10 changed files with 144 additions and 23 deletions
+76 -12
View File
@@ -9,6 +9,7 @@
#include "lwip/inet.h"
#include "lwip/sockets.h"
#include "openknx_idf/ets_device_runtime.h"
#include "soc/uart_periph.h"
#include <algorithm>
#include <array>
@@ -173,6 +174,38 @@ std::string ErrnoDetail(const std::string& message, int err) {
return std::string(message) + ": errno=" + std::to_string(err) + " (" + std::strerror(err) + ")";
}
bool ResolveUartIoPin(uart_port_t uart_port, int configured_pin, uint32_t pin_index,
int* resolved_pin) {
if (resolved_pin == nullptr) {
return false;
}
if (configured_pin >= 0) {
*resolved_pin = configured_pin;
return true;
}
if (uart_port < 0 || uart_port >= SOC_UART_NUM || pin_index >= SOC_UART_PINS_COUNT) {
*resolved_pin = UART_PIN_NO_CHANGE;
return false;
}
const int default_pin = uart_periph_signal[uart_port].pins[pin_index].default_gpio;
if (default_pin < 0) {
*resolved_pin = UART_PIN_NO_CHANGE;
return false;
}
*resolved_pin = default_pin;
return true;
}
std::string UartPinDescription(int configured_pin, int resolved_pin) {
if (configured_pin >= 0) {
return std::to_string(configured_pin);
}
if (resolved_pin >= 0) {
return std::to_string(resolved_pin) + " (default from -1)";
}
return "unrouted (-1 with no target default)";
}
std::string Ipv4String(uint32_t network_address) {
const uint32_t address = ntohl(network_address);
char buffer[16]{};
@@ -909,6 +942,9 @@ std::optional<GatewayKnxConfig> GatewayKnxConfigFromValue(const DaliValue* value
config.tp_uart.read_timeout_ms = static_cast<uint32_t>(std::max(
1, ObjectIntAny(serial, {"readTimeoutMs", "read_timeout_ms"})
.value_or(static_cast<int>(config.tp_uart.read_timeout_ms))));
config.tp_uart.nine_bit_mode = ObjectBoolAny(
serial, {"nineBitMode", "nine_bit_mode", "use9BitMode", "use_9_bit_mode"})
.value_or(config.tp_uart.nine_bit_mode);
}
return config;
}
@@ -939,6 +975,7 @@ DaliValue GatewayKnxConfigToValue(const GatewayKnxConfig& config) {
serial["rxBufferSize"] = static_cast<int>(config.tp_uart.rx_buffer_size);
serial["txBufferSize"] = static_cast<int>(config.tp_uart.tx_buffer_size);
serial["readTimeoutMs"] = static_cast<int>(config.tp_uart.read_timeout_ms);
serial["nineBitMode"] = config.tp_uart.nine_bit_mode;
out["tpUart"] = std::move(serial);
DaliValue::Array ets_associations;
ets_associations.reserve(config.ets_associations.size());
@@ -1911,13 +1948,24 @@ esp_err_t GatewayKnxTpIpRouter::start(uint32_t task_stack_size, UBaseType_t task
}
stop_requested_ = false;
last_error_.clear();
int log_tp_uart_tx_pin = -1;
int log_tp_uart_rx_pin = -1;
if (config_.tp_uart.uart_port >= 0 && config_.tp_uart.uart_port < SOC_UART_NUM) {
const uart_port_t log_uart_port = static_cast<uart_port_t>(config_.tp_uart.uart_port);
ResolveUartIoPin(log_uart_port, config_.tp_uart.tx_pin, SOC_UART_TX_PIN_IDX,
&log_tp_uart_tx_pin);
ResolveUartIoPin(log_uart_port, config_.tp_uart.rx_pin, SOC_UART_RX_PIN_IDX,
&log_tp_uart_rx_pin);
}
ESP_LOGI(kTag,
"starting KNXnet/IP router namespace=%s udp=%u tunnel=%d multicast=%d group=%s "
"tpUart=%d tx=%d rx=%d commissioningOnly=%d",
"tpUart=%d tx=%s rx=%s nineBit=%d commissioningOnly=%d",
openknx_namespace_.c_str(), static_cast<unsigned>(config_.udp_port),
config_.tunnel_enabled, config_.multicast_enabled,
config_.multicast_address.c_str(), config_.tp_uart.uart_port,
config_.tp_uart.tx_pin, config_.tp_uart.rx_pin, commissioning_only_);
UartPinDescription(config_.tp_uart.tx_pin, log_tp_uart_tx_pin).c_str(),
UartPinDescription(config_.tp_uart.rx_pin, log_tp_uart_rx_pin).c_str(),
config_.tp_uart.nine_bit_mode, commissioning_only_);
if (!configureSocket()) {
return ESP_FAIL;
}
@@ -2447,11 +2495,24 @@ bool GatewayKnxTpIpRouter::configureTpUart() {
uart_config_t uart_config{};
uart_config.baud_rate = static_cast<int>(serial.baudrate);
uart_config.data_bits = UART_DATA_8_BITS;
uart_config.parity = UART_PARITY_EVEN;
uart_config.parity = serial.nine_bit_mode ? UART_PARITY_EVEN : UART_PARITY_DISABLE;
uart_config.stop_bits = UART_STOP_BITS_1;
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
uart_config.source_clk = UART_SCLK_DEFAULT;
const uart_port_t uart_port = static_cast<uart_port_t>(serial.uart_port);
int tx_pin = UART_PIN_NO_CHANGE;
int rx_pin = UART_PIN_NO_CHANGE;
const bool tx_pin_ok = ResolveUartIoPin(uart_port, serial.tx_pin, SOC_UART_TX_PIN_IDX, &tx_pin);
const bool rx_pin_ok = ResolveUartIoPin(uart_port, serial.rx_pin, SOC_UART_RX_PIN_IDX, &rx_pin);
if (!tx_pin_ok || !rx_pin_ok) {
last_error_ = "KNX TP-UART UART" + std::to_string(serial.uart_port) +
" has no ESP-IDF default " + (!tx_pin_ok ? std::string("TX") : std::string("")) +
(!tx_pin_ok && !rx_pin_ok ? "/" : "") +
(!rx_pin_ok ? std::string("RX") : std::string("")) +
" pin; configure explicit txPin/rxPin values";
ESP_LOGE(kTag, "%s", last_error_.c_str());
return false;
}
esp_err_t err = uart_param_config(uart_port, &uart_config);
if (err != ESP_OK) {
last_error_ = EspErrDetail("failed to configure KNX TP-UART parameters on UART" +
@@ -2460,14 +2521,12 @@ bool GatewayKnxTpIpRouter::configureTpUart() {
ESP_LOGE(kTag, "%s", last_error_.c_str());
return false;
}
err = uart_set_pin(uart_port, serial.tx_pin < 0 ? UART_PIN_NO_CHANGE : serial.tx_pin,
serial.rx_pin < 0 ? UART_PIN_NO_CHANGE : serial.rx_pin,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
err = uart_set_pin(uart_port, tx_pin, rx_pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
if (err != ESP_OK) {
last_error_ = EspErrDetail("failed to configure KNX TP-UART pins uart=" +
std::to_string(serial.uart_port) + " tx=" +
std::to_string(serial.tx_pin) + " rx=" +
std::to_string(serial.rx_pin),
UartPinDescription(serial.tx_pin, tx_pin) + " rx=" +
UartPinDescription(serial.rx_pin, rx_pin),
err);
ESP_LOGE(kTag, "%s", last_error_.c_str());
return false;
@@ -2482,6 +2541,8 @@ bool GatewayKnxTpIpRouter::configureTpUart() {
return false;
}
tp_uart_port_ = serial.uart_port;
tp_uart_tx_pin_ = tx_pin;
tp_uart_rx_pin_ = rx_pin;
if (!initializeTpUart()) {
if (ets_device_ != nullptr && !ets_device_->configured()) {
ESP_LOGW(kTag,
@@ -2496,8 +2557,11 @@ bool GatewayKnxTpIpRouter::configureTpUart() {
ESP_LOGE(kTag, "%s", last_error_.c_str());
return false;
}
ESP_LOGI(kTag, "KNX TP-UART online uart=%d tx=%d rx=%d baud=%u", serial.uart_port,
serial.tx_pin, serial.rx_pin, static_cast<unsigned>(serial.baudrate));
ESP_LOGI(kTag, "KNX TP-UART online uart=%d tx=%s rx=%s baud=%u nineBit=%d",
serial.uart_port,
UartPinDescription(serial.tx_pin, tp_uart_tx_pin_).c_str(),
UartPinDescription(serial.rx_pin, tp_uart_rx_pin_).c_str(),
static_cast<unsigned>(serial.baudrate), serial.nine_bit_mode);
return true;
}
@@ -2600,8 +2664,8 @@ bool GatewayKnxTpIpRouter::initializeTpUart() {
last_error_ = (saw_reset ? "timed out waiting for KNX TP-UART state indication"
: "timed out waiting for KNX TP-UART reset indication") +
std::string(" uart=") + std::to_string(config_.tp_uart.uart_port) +
" tx=" + std::to_string(config_.tp_uart.tx_pin) +
" rx=" + std::to_string(config_.tp_uart.rx_pin) +
" tx=" + UartPinDescription(config_.tp_uart.tx_pin, tp_uart_tx_pin_) +
" rx=" + UartPinDescription(config_.tp_uart.rx_pin, tp_uart_rx_pin_) +
" timeoutMs=1500";
return false;
}