diff --git a/apps/gateway/sdkconfig.old b/apps/gateway/sdkconfig.old index 6b7be51..1d97f53 100644 --- a/apps/gateway/sdkconfig.old +++ b/apps/gateway/sdkconfig.old @@ -699,7 +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 is not set +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 diff --git a/components/gateway_knx/src/gateway_knx.cpp b/components/gateway_knx/src/gateway_knx.cpp index 26a9bdf..6771673 100644 --- a/components/gateway_knx/src/gateway_knx.cpp +++ b/components/gateway_knx/src/gateway_knx.cpp @@ -550,6 +550,10 @@ std::optional DecodeOpenKnxGroupWrite(const uint8_t* data, si return out; } +bool IsOpenKnxGroupValueWrite(const uint8_t* data, size_t len) { + return DecodeOpenKnxGroupWrite(data, len).has_value(); +} + uint8_t Reg1PercentToArc(uint8_t value) { if (value == 0 || value == 0xff) { return value; @@ -3430,9 +3434,10 @@ void GatewayKnxTpIpRouter::handleTunnellingRequest(const uint8_t* packet_data, s const uint8_t* cemi = frame.data(); const size_t cemi_len = frame.dataLength(); const std::vector current_cemi(cemi, cemi + cemi_len); + const bool is_group_value_write = IsOpenKnxGroupValueWrite(cemi, cemi_len); const bool duplicate_sequence = sequence == client->received_sequence; const bool duplicate_payload = duplicate_sequence && client->last_received_cemi == current_cemi; - if (duplicate_payload) { + if (duplicate_payload && !is_group_value_write) { ESP_LOGD(kTag, "duplicate KNXnet/IP tunnelling request channel=%u seq=%u", static_cast(channel_id), static_cast(sequence)); sendTunnellingAck(channel_id, sequence, kKnxNoError, client->data_remote); @@ -3453,7 +3458,11 @@ void GatewayKnxTpIpRouter::handleTunnellingRequest(const uint8_t* packet_data, s } return; } - if (duplicate_sequence) { + if (duplicate_payload) { + ESP_LOGI(kTag, + "reprocessing duplicate KNXnet/IP GroupValueWrite channel=%u seq=%u without cached confirmation replay", + static_cast(channel_id), static_cast(sequence)); + } else if (duplicate_sequence) { ESP_LOGW(kTag, "accept KNXnet/IP tunnelling request channel=%u with repeated seq=%u because cEMI payload changed", static_cast(channel_id), static_cast(sequence)); @@ -4019,8 +4028,13 @@ bool GatewayKnxTpIpRouter::sendCemiFrameToClient(TunnelClient& client, uint16_t } if (service == kServiceTunnellingRequest && message_code.has_value() && message_code.value() == L_data_con) { - client.last_tunnel_confirmation_sequence = send_sequence; - client.last_tunnel_confirmation_packet = packet; + if (IsOpenKnxGroupValueWrite(data, len)) { + client.last_tunnel_confirmation_sequence = 0; + client.last_tunnel_confirmation_packet.clear(); + } else { + client.last_tunnel_confirmation_sequence = send_sequence; + client.last_tunnel_confirmation_packet = packet; + } } ESP_LOGI(kTag, "sent KNXnet/IP cEMI service=0x%04x channel=%u seq=%u cemi=0x%02x len=%u to %s", static_cast(service), static_cast(client.channel_id),