feat(gateway): add support for full IP forwarding of KNX TP telegrams and enhance tunnel frame handling
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 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.
|
||||
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. Enable `GATEWAY_KNX_TP_FULL_IP_FORWARD` when the gateway must mirror all physical TP telegrams back out to KNXnet/IP tunnelling and multicast so ETS can monitor or download other TP devices through the gateway's IP endpoint. 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.
|
||||
|
||||
|
||||
@@ -852,6 +852,17 @@ config GATEWAY_KNX_TP_UART_9BIT_MODE
|
||||
mode commonly described as 19200 baud 9-bit UART. Disable only for
|
||||
hardware wired for 8N1 host UART mode.
|
||||
|
||||
config GATEWAY_KNX_TP_FULL_IP_FORWARD
|
||||
bool "Mirror all physical KNX TP telegrams to KNXnet/IP"
|
||||
depends on GATEWAY_KNX_BRIDGE_SUPPORTED && GATEWAY_KNX_TP_UART_PORT >= 0
|
||||
default n
|
||||
help
|
||||
Mirrors physical KNX TP telegrams received from the TP-UART line back
|
||||
out through KNXnet/IP tunnelling and multicast even when the gateway
|
||||
runs the single-interface ETS device runtime. Enable this when ETS must
|
||||
monitor or download other TP devices through the gateway's IP endpoint.
|
||||
Leave it disabled to preserve the narrower default forwarding behavior.
|
||||
|
||||
config GATEWAY_BRIDGE_KNX_TASK_STACK_SIZE
|
||||
int "KNX/IP bridge task stack bytes"
|
||||
depends on GATEWAY_KNX_BRIDGE_SUPPORTED
|
||||
|
||||
@@ -699,6 +699,7 @@ 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_KNX_TP_FULL_IP_FORWARD=y
|
||||
CONFIG_GATEWAY_BRIDGE_KNX_TASK_STACK_SIZE=12288
|
||||
CONFIG_GATEWAY_BRIDGE_KNX_TASK_PRIORITY=5
|
||||
CONFIG_GATEWAY_CLOUD_BRIDGE_SUPPORTED=y
|
||||
|
||||
@@ -697,7 +697,9 @@ 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_KNX_TP_FULL_IP_FORWARD is not set
|
||||
CONFIG_GATEWAY_BRIDGE_KNX_TASK_STACK_SIZE=12288
|
||||
CONFIG_GATEWAY_BRIDGE_KNX_TASK_PRIORITY=5
|
||||
CONFIG_GATEWAY_CLOUD_BRIDGE_SUPPORTED=y
|
||||
|
||||
@@ -509,9 +509,6 @@ bool EtsDeviceRuntime::shouldConsumeTunnelFrame(CemiFrame& frame) const {
|
||||
case M_FuncPropStateRead_req:
|
||||
return true;
|
||||
case L_data_req: {
|
||||
if (tpUartOnline()) {
|
||||
return true;
|
||||
}
|
||||
// In commissioning / programming mode ETS may address the device via its
|
||||
// individual address, the cEMI-client tunnel address (device+1), or the
|
||||
// unconfigured broadcast address 0xFFFF. Consume only those; let all
|
||||
|
||||
@@ -3476,16 +3476,20 @@ void GatewayKnxTpIpRouter::handleTunnellingRequest(const uint8_t* packet_data, s
|
||||
const bool consumed_by_openknx = handleOpenKnxTunnelFrame(
|
||||
cemi, cemi_len, client, kServiceTunnellingRequest);
|
||||
const bool routed_to_dali = routeOpenKnxGroupWrite(cemi, cemi_len, "KNX tunnel frame");
|
||||
if (!consumed_by_openknx && routed_to_dali) {
|
||||
const bool sent_to_tp = !consumed_by_openknx && !routed_to_dali &&
|
||||
transmitOpenKnxTpFrame(cemi, cemi_len);
|
||||
if ((!consumed_by_openknx && routed_to_dali) || sent_to_tp) {
|
||||
std::vector<uint8_t> tunnel_confirmation;
|
||||
if (BuildTunnelConfirmationFrame(cemi, cemi_len, &tunnel_confirmation)) {
|
||||
sendCemiFrameToClient(*client, kServiceTunnellingRequest, tunnel_confirmation.data(),
|
||||
tunnel_confirmation.size());
|
||||
}
|
||||
}
|
||||
if (consumed_by_openknx || routed_to_dali) {
|
||||
if (consumed_by_openknx || routed_to_dali || sent_to_tp) {
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGD(kTag, "KNX tunnel frame ignored: no OpenKNX/DALI/TP handler matched");
|
||||
}
|
||||
|
||||
void GatewayKnxTpIpRouter::handleDeviceConfigurationRequest(const uint8_t* packet_data, size_t len,
|
||||
@@ -3933,11 +3937,50 @@ void GatewayKnxTpIpRouter::sendTunnelIndication(const uint8_t* data, size_t len)
|
||||
if (data == nullptr || len == 0) {
|
||||
return;
|
||||
}
|
||||
for (auto& client : tunnel_clients_) {
|
||||
if (client.connected) {
|
||||
sendTunnelIndicationToClient(client, data, len);
|
||||
std::vector<uint8_t> frame_data(data, data + len);
|
||||
CemiFrame frame(frame_data.data(), static_cast<uint16_t>(frame_data.size()));
|
||||
if (!frame.valid()) {
|
||||
ESP_LOGW(kTag, "not sending invalid OpenKNX tunnel indication len=%u",
|
||||
static_cast<unsigned>(len));
|
||||
return;
|
||||
}
|
||||
|
||||
auto is_tunnel_recipient = [](const TunnelClient& client) {
|
||||
return client.connected && client.connection_type == kKnxConnectionTypeTunnel;
|
||||
};
|
||||
|
||||
auto send_to_client = [this, data, len](TunnelClient& client) {
|
||||
sendTunnelIndicationToClient(client, data, len);
|
||||
};
|
||||
|
||||
const bool suppress_source_echo =
|
||||
frame.addressType() == GroupAddress || frame.addressType() == IndividualAddress;
|
||||
const uint16_t source_address = suppress_source_echo ? frame.sourceAddress() : 0;
|
||||
|
||||
if (frame.addressType() == IndividualAddress) {
|
||||
for (auto& client : tunnel_clients_) {
|
||||
if (!is_tunnel_recipient(client)) {
|
||||
continue;
|
||||
}
|
||||
if (client.individual_address == source_address) {
|
||||
continue;
|
||||
}
|
||||
if (client.individual_address == frame.destinationAddress()) {
|
||||
send_to_client(client);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& client : tunnel_clients_) {
|
||||
if (!is_tunnel_recipient(client)) {
|
||||
continue;
|
||||
}
|
||||
if (suppress_source_echo && client.individual_address == source_address) {
|
||||
continue;
|
||||
}
|
||||
send_to_client(client);
|
||||
}
|
||||
}
|
||||
|
||||
void GatewayKnxTpIpRouter::sendTunnelIndicationToClient(TunnelClient& client, const uint8_t* data,
|
||||
@@ -4152,6 +4195,10 @@ bool GatewayKnxTpIpRouter::handleOpenKnxTunnelFrame(const uint8_t* data, size_t
|
||||
sendRoutingIndication(response, response_len);
|
||||
return;
|
||||
}
|
||||
if (response_client != nullptr && response_client->connected) {
|
||||
sendCemiFrameToClient(*response_client, service, response, response_len);
|
||||
return;
|
||||
}
|
||||
sendTunnelIndication(response, response_len);
|
||||
});
|
||||
if (needs_tunnel_confirmation && consumed && !sent_tunnel_confirmation) {
|
||||
|
||||
+1
-1
Submodule knx updated: 51bb6cdf6a...aaeb08f232
Reference in New Issue
Block a user