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:
@@ -34,7 +34,7 @@ The native rewrite now wires a shared `gateway_core` bootstrap component, a mult
|
||||
|
||||
KNX Data Secure and KNXnet/IP Secure support are controlled by `GATEWAY_KNX_DATA_SECURE_SUPPORTED` and `GATEWAY_KNX_IP_SECURE_SUPPORTED`. The current KNXnet/IP Secure flag reserves and reports secure service capability, while runtime secure-session transport is still reported as not implemented until that path is wired. The gateway derives its KNX serial identity from the ESP base MAC, and the development factory setup key is deterministically derived from that KNX serial so the same board keeps the same FDSK across NVS erases.
|
||||
|
||||
The KNXnet/IP tunnel can start from the built-in default configuration before any ETS download. KNX TP-UART is enabled only when `GATEWAY_KNX_TP_UART_PORT` is `0`, `1`, or `2`; set that UART port to `-1` for IP-only operation. UART TX/RX GPIO values of `-1` mean use the ESP-IDF default IO routing for that UART, not disabled. Non-UART GPIO options use `-1` as disabled, including the KNX programming button, KNX programming LED, setup AP button, Wi-Fi reset button, and status LED.
|
||||
The KNXnet/IP tunnel can start from the built-in default configuration before any ETS download. KNX TP-UART is enabled only when `GATEWAY_KNX_TP_UART_PORT` is `0`, `1`, or `2`; set that UART port to `-1` for IP-only operation. UART TX/RX GPIO values of `-1` mean use the ESP-IDF target default pins for that UART, not disabled. `GATEWAY_KNX_TP_UART_9BIT_MODE` enables the NCN5120/OpenKNX-style 9-bit host frame on the wire, represented on ESP-IDF as 8 data bits plus even parity. Non-UART GPIO options use `-1` as disabled, including the KNX programming button, KNX programming LED, setup AP button, Wi-Fi reset button, and status LED.
|
||||
|
||||
When no KNX bridge config or ETS application data has been downloaded, the KNXnet/IP router starts in commissioning mode: OpenKNX receives tunnel programming traffic from ETS, while DALI group routing and REG1-Dali function-property actions stay inactive until ETS reports a configured application.
|
||||
|
||||
|
||||
@@ -772,6 +772,16 @@ config GATEWAY_KNX_TP_BAUDRATE
|
||||
range 1200 921600
|
||||
default 19200
|
||||
|
||||
config GATEWAY_KNX_TP_UART_9BIT_MODE
|
||||
bool "KNX TP UART 9-bit mode"
|
||||
depends on GATEWAY_KNX_BRIDGE_SUPPORTED
|
||||
default y
|
||||
help
|
||||
Enable the NCN5120/OpenKNX-style 9-bit UART frame on the wire. ESP-IDF
|
||||
exposes this as 8 data bits plus even parity, matching the TP-UART host
|
||||
mode commonly described as 19200 baud 9-bit UART. Disable only for
|
||||
hardware wired for 8N1 host UART mode.
|
||||
|
||||
config GATEWAY_BRIDGE_KNX_TASK_STACK_SIZE
|
||||
int "KNX/IP bridge task stack bytes"
|
||||
depends on GATEWAY_KNX_BRIDGE_SUPPORTED
|
||||
|
||||
@@ -886,6 +886,11 @@ 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<uint32_t>(CONFIG_GATEWAY_KNX_TP_BAUDRATE);
|
||||
#ifdef CONFIG_GATEWAY_KNX_TP_UART_9BIT_MODE
|
||||
default_knx.tp_uart.nine_bit_mode = true;
|
||||
#else
|
||||
default_knx.tp_uart.nine_bit_mode = false;
|
||||
#endif
|
||||
bridge_config.default_knx_config = default_knx;
|
||||
}
|
||||
bridge_config.knx_task_stack_size =
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
Submodule knx updated: 5d7d6e573b...346b704cbe
Reference in New Issue
Block a user