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:
@@ -2062,12 +2062,14 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
const bool commissioning_only = !knx_config.has_value();
|
||||
ESP_LOGI(kTag,
|
||||
"gateway=%u KNX/IP start config namespace=%s storedConfig=%d udp=%u tunnel=%d "
|
||||
"multicast=%d multicastGroup=%s mainGroup=%u tpUart=%d tx=%d rx=%d individual=0x%04x",
|
||||
"multicast=%d multicastGroup=%s mainGroup=%u tpUart=%d tx=%d rx=%d nineBit=%d "
|
||||
"individual=0x%04x",
|
||||
channel.gateway_id, openKnxNamespace().c_str(), !commissioning_only,
|
||||
static_cast<unsigned>(runtime_config.udp_port), runtime_config.tunnel_enabled,
|
||||
runtime_config.multicast_enabled, runtime_config.multicast_address.c_str(),
|
||||
static_cast<unsigned>(runtime_config.main_group), runtime_config.tp_uart.uart_port,
|
||||
runtime_config.tp_uart.tx_pin, runtime_config.tp_uart.rx_pin,
|
||||
runtime_config.tp_uart.nine_bit_mode,
|
||||
runtime_config.individual_address);
|
||||
knx->setConfig(runtime_config);
|
||||
knx_router->setConfig(runtime_config);
|
||||
@@ -2304,6 +2306,7 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
cJSON_AddNumberToObject(serial_json, "txPin", effective_knx->tp_uart.tx_pin);
|
||||
cJSON_AddNumberToObject(serial_json, "rxPin", effective_knx->tp_uart.rx_pin);
|
||||
cJSON_AddNumberToObject(serial_json, "baudrate", effective_knx->tp_uart.baudrate);
|
||||
cJSON_AddBoolToObject(serial_json, "nineBitMode", effective_knx->tp_uart.nine_bit_mode);
|
||||
cJSON_AddItemToObject(knx_json, "tpUart", serial_json);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ struct GatewayKnxTpUartConfig {
|
||||
size_t rx_buffer_size{1024};
|
||||
size_t tx_buffer_size{1024};
|
||||
uint32_t read_timeout_ms{20};
|
||||
bool nine_bit_mode{true};
|
||||
};
|
||||
|
||||
enum class GatewayKnxMappingMode : uint8_t {
|
||||
@@ -348,6 +349,8 @@ class GatewayKnxTpIpRouter {
|
||||
int tcp_sock_{-1};
|
||||
int active_tcp_sock_{-1};
|
||||
int tp_uart_port_{-1};
|
||||
int tp_uart_tx_pin_{-1};
|
||||
int tp_uart_rx_pin_{-1};
|
||||
std::vector<uint32_t> multicast_joined_interfaces_;
|
||||
TickType_t network_refresh_tick_{0};
|
||||
std::array<TcpClient, kMaxTcpClients> tcp_clients_{};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ namespace gateway::openknx {
|
||||
class TpuartUartInterface : public TPUart::Interface::Abstract {
|
||||
public:
|
||||
TpuartUartInterface(uart_port_t uart_port, int tx_pin, int rx_pin,
|
||||
size_t rx_buffer_size = 512, size_t tx_buffer_size = 512);
|
||||
size_t rx_buffer_size = 512, size_t tx_buffer_size = 512,
|
||||
bool nine_bit_mode = true);
|
||||
~TpuartUartInterface();
|
||||
|
||||
void begin(int baud) override;
|
||||
@@ -34,6 +35,7 @@ class TpuartUartInterface : public TPUart::Interface::Abstract {
|
||||
int rx_pin_;
|
||||
size_t rx_buffer_size_;
|
||||
size_t tx_buffer_size_;
|
||||
bool nine_bit_mode_;
|
||||
std::atomic_bool overflow_{false};
|
||||
std::function<bool()> callback_;
|
||||
};
|
||||
|
||||
@@ -80,6 +80,7 @@ EtsDeviceRuntime::EtsDeviceRuntime(std::string nvs_namespace,
|
||||
? tunnel_client_address
|
||||
: DefaultTunnelClientAddress(
|
||||
device_.deviceObject().individualAddress()));
|
||||
server->deviceAddressPropertiesTargetClient(false);
|
||||
server->tunnelFrameCallback(&EtsDeviceRuntime::EmitTunnelFrame, this);
|
||||
}
|
||||
device_.functionPropertyCallback(&EtsDeviceRuntime::HandleFunctionPropertyCommand);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "openknx_idf/tpuart_uart_interface.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "soc/uart_periph.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
@@ -9,15 +10,39 @@ namespace {
|
||||
|
||||
constexpr const char* kTag = "openknx_tpuart";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TpuartUartInterface::TpuartUartInterface(uart_port_t uart_port, int tx_pin, int rx_pin,
|
||||
size_t rx_buffer_size, size_t tx_buffer_size)
|
||||
size_t rx_buffer_size, size_t tx_buffer_size,
|
||||
bool nine_bit_mode)
|
||||
: uart_port_(uart_port),
|
||||
tx_pin_(tx_pin),
|
||||
rx_pin_(rx_pin),
|
||||
rx_buffer_size_(rx_buffer_size),
|
||||
tx_buffer_size_(tx_buffer_size) {}
|
||||
tx_buffer_size_(tx_buffer_size),
|
||||
nine_bit_mode_(nine_bit_mode) {}
|
||||
|
||||
TpuartUartInterface::~TpuartUartInterface() { end(); }
|
||||
|
||||
@@ -29,22 +54,30 @@ void TpuartUartInterface::begin(int baud) {
|
||||
uart_config_t config{};
|
||||
config.baud_rate = baud;
|
||||
config.data_bits = UART_DATA_8_BITS;
|
||||
config.parity = UART_PARITY_EVEN;
|
||||
config.parity = nine_bit_mode_ ? UART_PARITY_EVEN : UART_PARITY_DISABLE;
|
||||
config.stop_bits = UART_STOP_BITS_1;
|
||||
config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
|
||||
config.source_clk = UART_SCLK_DEFAULT;
|
||||
|
||||
int tx_pin = UART_PIN_NO_CHANGE;
|
||||
int rx_pin = UART_PIN_NO_CHANGE;
|
||||
if (!ResolveUartIoPin(uart_port_, tx_pin_, SOC_UART_TX_PIN_IDX, &tx_pin) ||
|
||||
!ResolveUartIoPin(uart_port_, rx_pin_, SOC_UART_RX_PIN_IDX, &rx_pin)) {
|
||||
ESP_LOGE(kTag, "UART%d has no ESP-IDF default TX/RX pin; configure explicit pins",
|
||||
uart_port_);
|
||||
return;
|
||||
}
|
||||
|
||||
esp_err_t err = uart_param_config(uart_port_, &config);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(kTag, "failed to configure UART%d: %s", uart_port_, esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
|
||||
err = uart_set_pin(uart_port_, tx_pin_ < 0 ? UART_PIN_NO_CHANGE : tx_pin_,
|
||||
rx_pin_ < 0 ? UART_PIN_NO_CHANGE : 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) {
|
||||
ESP_LOGE(kTag, "failed to route UART%d pins: %s", uart_port_, esp_err_to_name(err));
|
||||
ESP_LOGE(kTag, "failed to route UART%d pins tx=%d rx=%d: %s", uart_port_, tx_pin,
|
||||
rx_pin, esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user