diff --git a/apps/gateway/main/Kconfig.projbuild b/apps/gateway/main/Kconfig.projbuild index 4877909..526f5da 100644 --- a/apps/gateway/main/Kconfig.projbuild +++ b/apps/gateway/main/Kconfig.projbuild @@ -1577,6 +1577,15 @@ config GATEWAY_DALI_BAUDRATE help Runtime baudrate used when initializing the local DALI bus. +config GATEWAY_CONTROLLER_TASK_STACK_SIZE + int "Gateway controller task stack bytes" + range 6144 24576 + default 12288 + help + Stack used by the gateway command controller. BLE bridge transport + requests are decoded in this task and may execute JSON-heavy bridge + management actions such as KNX programming-mode changes. + endmenu menu "Connectivity Startup" diff --git a/apps/gateway/main/app_main.cpp b/apps/gateway/main/app_main.cpp index 9280151..c58e05d 100644 --- a/apps/gateway/main/app_main.cpp +++ b/apps/gateway/main/app_main.cpp @@ -164,6 +164,10 @@ #define CONFIG_GATEWAY_ETHERNET_RX_TASK_STACK_SIZE 3072 #endif +#ifndef CONFIG_GATEWAY_CONTROLLER_TASK_STACK_SIZE +#define CONFIG_GATEWAY_CONTROLLER_TASK_STACK_SIZE 12288 +#endif + #ifndef CONFIG_GATEWAY_BRIDGE_MODBUS_TASK_STACK_SIZE #define CONFIG_GATEWAY_BRIDGE_MODBUS_TASK_STACK_SIZE 6144 #endif @@ -978,6 +982,8 @@ extern "C" void app_main(void) { ESP_ERROR_CHECK(s_cache->start()); gateway::GatewayControllerConfig controller_config; + controller_config.task_stack_size = + static_cast(CONFIG_GATEWAY_CONTROLLER_TASK_STACK_SIZE); const bool network_transport_supported = profile.enable_wifi || profile.enable_eth; controller_config.setup_supported = true; controller_config.ble_supported = profile.enable_ble; diff --git a/apps/gateway/sdkconfig b/apps/gateway/sdkconfig index e45be5b..03c9e62 100644 --- a/apps/gateway/sdkconfig +++ b/apps/gateway/sdkconfig @@ -600,7 +600,7 @@ CONFIG_PARTITION_TABLE_MD5=y # # DALI Settings # -CONFIG_GATEWAY_CHANNEL_COUNT=2 +CONFIG_GATEWAY_CHANNEL_COUNT=1 # # Gateway Channel 1 @@ -619,17 +619,78 @@ CONFIG_GATEWAY_CHANNEL1_NATIVE_BAUDRATE=1200 # # Gateway Channel 2 # -CONFIG_GATEWAY_CHANNEL2_GW_ID=4 -# CONFIG_GATEWAY_CHANNEL2_PHY_DISABLED is not set -CONFIG_GATEWAY_CHANNEL2_PHY_NATIVE=y -# CONFIG_GATEWAY_CHANNEL2_PHY_UART1 is not set -# CONFIG_GATEWAY_CHANNEL2_PHY_UART2 is not set -CONFIG_GATEWAY_CHANNEL2_NATIVE_BUS_ID=1 -CONFIG_GATEWAY_CHANNEL2_NATIVE_TX_PIN=4 -CONFIG_GATEWAY_CHANNEL2_NATIVE_RX_PIN=3 -CONFIG_GATEWAY_CHANNEL2_NATIVE_BAUDRATE=1200 # end of Gateway Channel 2 +# +# Gateway Channel 3 +# +# end of Gateway Channel 3 + +# +# Gateway Channel 4 +# +# end of Gateway Channel 4 + +# +# Gateway Channel 5 +# +# end of Gateway Channel 5 + +# +# Gateway Channel 6 +# +# end of Gateway Channel 6 + +# +# Gateway Channel 7 +# +# end of Gateway Channel 7 + +# +# Gateway Channel 8 +# +# end of Gateway Channel 8 + +# +# Gateway Channel 9 +# +# end of Gateway Channel 9 + +# +# Gateway Channel 10 +# +# end of Gateway Channel 10 + +# +# Gateway Channel 11 +# +# end of Gateway Channel 11 + +# +# Gateway Channel 12 +# +# end of Gateway Channel 12 + +# +# Gateway Channel 13 +# +# end of Gateway Channel 13 + +# +# Gateway Channel 14 +# +# end of Gateway Channel 14 + +# +# Gateway Channel 15 +# +# end of Gateway Channel 15 + +# +# Gateway Channel 16 +# +# end of Gateway Channel 16 + # # Gateway Cache # @@ -644,10 +705,11 @@ CONFIG_GATEWAY_CACHE_OUTSIDE_BUS_FIRST=y # end of Gateway Cache # CONFIG_GATEWAY_ENABLE_DALI_BUS is not set +CONFIG_GATEWAY_CONTROLLER_TASK_STACK_SIZE=12288 # end of DALI Settings # -# Gateway Startup Services +# Connectivity Startup # CONFIG_GATEWAY_BLE_SUPPORTED=y CONFIG_GATEWAY_START_BLE_ENABLED=y @@ -661,6 +723,7 @@ CONFIG_GATEWAY_SMARTCONFIG_TIMEOUT_SEC=60 CONFIG_GATEWAY_ETHERNET_SUPPORTED=y CONFIG_GATEWAY_START_ETHERNET_ENABLED=y CONFIG_GATEWAY_ETHERNET_IGNORE_INIT_FAILURE=y +# end of Connectivity Startup # # Gateway Wired Ethernet @@ -678,7 +741,15 @@ CONFIG_GATEWAY_ETHERNET_PHY_ADDR=1 CONFIG_GATEWAY_ETHERNET_RX_TASK_STACK_SIZE=4096 # end of Gateway Wired Ethernet +# +# Bridge Runtime +# CONFIG_GATEWAY_BRIDGE_SUPPORTED=y +# end of Bridge Runtime + +# +# Modbus Settings +# CONFIG_GATEWAY_MODBUS_BRIDGE_SUPPORTED=y # CONFIG_GATEWAY_START_MODBUS_BRIDGE_ENABLED is not set CONFIG_GATEWAY_MODBUS_DEFAULT_TRANSPORT_TCP=y @@ -686,8 +757,14 @@ CONFIG_GATEWAY_MODBUS_DEFAULT_TRANSPORT_TCP=y # CONFIG_GATEWAY_MODBUS_DEFAULT_TRANSPORT_ASCII is not set CONFIG_GATEWAY_MODBUS_TCP_PORT=1502 CONFIG_GATEWAY_MODBUS_UNIT_ID=1 +# end of Modbus Settings + +# +# BACnet Settings +# CONFIG_GATEWAY_BACNET_BRIDGE_SUPPORTED=y # CONFIG_GATEWAY_START_BACNET_BRIDGE_ENABLED is not set +# end of BACnet Settings # # KNX Settings @@ -737,6 +814,9 @@ CONFIG_GATEWAY_BRIDGE_KNX_TASK_STACK_SIZE=12288 CONFIG_GATEWAY_BRIDGE_KNX_TASK_PRIORITY=5 # end of KNX Settings +# +# Cloud Settings +# CONFIG_GATEWAY_CLOUD_BRIDGE_SUPPORTED=y # CONFIG_GATEWAY_START_CLOUD_BRIDGE_ENABLED is not set CONFIG_GATEWAY_CLOUD_TOPIC_PREFIX="devices" @@ -747,14 +827,29 @@ CONFIG_GATEWAY_CLOUD_LTE_UART_PORT=-1 CONFIG_GATEWAY_CLOUD_LTE_UART_TX_PIN=-1 CONFIG_GATEWAY_CLOUD_LTE_UART_RX_PIN=-1 CONFIG_GATEWAY_CLOUD_LTE_UART_BAUDRATE=115200 +# end of Cloud Settings + +# +# Bridge Task Settings +# CONFIG_GATEWAY_BRIDGE_MODBUS_TASK_STACK_SIZE=6144 CONFIG_GATEWAY_BRIDGE_MODBUS_TASK_PRIORITY=4 CONFIG_GATEWAY_BRIDGE_BACNET_TASK_STACK_SIZE=8192 CONFIG_GATEWAY_BRIDGE_BACNET_TASK_PRIORITY=5 +# end of Bridge Task Settings + +# +# USB Setup +# CONFIG_GATEWAY_USB_STARTUP_DEBUG_JTAG=y # CONFIG_GATEWAY_USB_STARTUP_SETUP_SERIAL is not set +# end of USB Setup + +# +# UART0 Control +# # CONFIG_GATEWAY_485_CONTROL_ENABLED is not set -# end of Gateway Startup Services +# end of UART0 Control # # Gateway Network Services diff --git a/components/gateway_controller/include/gateway_controller.hpp b/components/gateway_controller/include/gateway_controller.hpp index e248ed6..3ded687 100644 --- a/components/gateway_controller/include/gateway_controller.hpp +++ b/components/gateway_controller/include/gateway_controller.hpp @@ -24,7 +24,7 @@ class GatewayBridgeService; class GatewayRuntime; struct GatewayControllerConfig { - uint32_t task_stack_size{6144}; + uint32_t task_stack_size{12288}; UBaseType_t task_priority{5}; int color_temperature_min{2000}; int color_temperature_max{6500}; diff --git a/components/gateway_knx/include/ets_device_runtime.h b/components/gateway_knx/include/ets_device_runtime.h index 85dbb57..3f6e1ad 100644 --- a/components/gateway_knx/include/ets_device_runtime.h +++ b/components/gateway_knx/include/ets_device_runtime.h @@ -69,6 +69,8 @@ class EtsDeviceRuntime { bool transmitTpFrame(const uint8_t* data, size_t len); bool handleTunnelFrame(const uint8_t* data, size_t len, CemiFrameSender sender); + bool handleLocalBroadcastManagementFrame(const uint8_t* data, size_t len, + CemiFrameSender sender); bool handleBusFrame(const uint8_t* data, size_t len); bool emitGroupValue(uint16_t group_object_number, const uint8_t* data, size_t len, CemiFrameSender sender); diff --git a/components/gateway_knx/include/oam_router_runtime.h b/components/gateway_knx/include/oam_router_runtime.h index f3a71cd..a6fc0aa 100644 --- a/components/gateway_knx/include/oam_router_runtime.h +++ b/components/gateway_knx/include/oam_router_runtime.h @@ -44,6 +44,8 @@ class OamRouterRuntime { Platform* platform(); void setNetworkInterface(esp_netif_t* netif); bool handleTunnelFrame(const uint8_t* data, size_t len, CemiFrameSender sender); + bool handleLocalBroadcastManagementFrame(const uint8_t* data, size_t len, + CemiFrameSender sender); void loop(); private: diff --git a/components/gateway_knx/src/ets_device_runtime.cpp b/components/gateway_knx/src/ets_device_runtime.cpp index d86c16b..36c983a 100644 --- a/components/gateway_knx/src/ets_device_runtime.cpp +++ b/components/gateway_knx/src/ets_device_runtime.cpp @@ -42,6 +42,21 @@ bool IsUsableIndividualAddress(uint16_t address) { return address != 0 && address != kInvalidIndividualAddress; } +bool IsBroadcastManagementRequest(CemiFrame& frame) { + if (frame.addressType() != GroupAddress || frame.destinationAddress() != 0x0000) { + return false; + } + switch (frame.apdu().type()) { + case IndividualAddressWrite: + case IndividualAddressRead: + case IndividualAddressSerialNumberRead: + case IndividualAddressSerialNumberWrite: + return true; + default: + return false; + } +} + std::string HexBytesString(const uint8_t* data, size_t length) { if (data == nullptr || length == 0) { return {}; @@ -336,6 +351,28 @@ bool EtsDeviceRuntime::handleTunnelFrame(const uint8_t* data, size_t len, return consumed; } +bool EtsDeviceRuntime::handleLocalBroadcastManagementFrame(const uint8_t* data, size_t len, + CemiFrameSender sender) { + if (data == nullptr || len < 2) { + return false; + } + std::vector frame_data(data, data + len); + CemiFrame frame(frame_data.data(), static_cast(frame_data.size())); + if (!frame.valid() || !IsBroadcastManagementRequest(frame)) { + return false; + } + const HopCountType hop_type = + frame.npdu().hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter; + sender_ = std::move(sender); + ActiveFunctionPropertyRuntimeScope callback_scope(this); + device_.injectDataBroadcastIndication(hop_type, frame.priority(), frame.sourceAddress(), + frame.apdu()); + loop(); + sender_ = nullptr; + installGroupObjectCallbacks(); + return true; +} + bool EtsDeviceRuntime::handleBusFrame(const uint8_t* data, size_t len) { auto* data_link_layer = device_.getDataLinkLayer(); if (data_link_layer == nullptr || data == nullptr || len < 2) { @@ -592,6 +629,10 @@ bool EtsDeviceRuntime::shouldConsumeTunnelFrame(CemiFrame& frame) const { return true; } + if (IsBroadcastManagementRequest(frame)) { + return true; + } + if (frame.addressType() == IndividualAddress) { if (dest == own_address || dest == client_address || (commissioning && dest == kKnxUnconfiguredBroadcastAddress)) { diff --git a/components/gateway_knx/src/gateway_knx_private.hpp b/components/gateway_knx/src/gateway_knx_private.hpp index 35dfdaa..5d6c17e 100644 --- a/components/gateway_knx/src/gateway_knx_private.hpp +++ b/components/gateway_knx/src/gateway_knx_private.hpp @@ -823,6 +823,22 @@ std::optional CemiMessageCode(const uint8_t* data, size_t len) { return static_cast(data[0]); } +bool IsKnxBroadcastManagementRequest(CemiFrame& frame) { + if (frame.messageCode() != L_data_req || frame.addressType() != GroupAddress || + frame.destinationAddress() != 0x0000) { + return false; + } + switch (frame.apdu().type()) { + case IndividualAddressWrite: + case IndividualAddressRead: + case IndividualAddressSerialNumberRead: + case IndividualAddressSerialNumberWrite: + return true; + default: + return false; + } +} + uint16_t KnxIpServiceForCemi(const uint8_t* data, size_t len, uint16_t fallback_service) { const auto message_code = CemiMessageCode(data, len); if (!message_code.has_value()) { diff --git a/components/gateway_knx/src/gateway_knx_router_openknx.cpp b/components/gateway_knx/src/gateway_knx_router_openknx.cpp index 4da9016..db6bfa7 100644 --- a/components/gateway_knx/src/gateway_knx_router_openknx.cpp +++ b/components/gateway_knx/src/gateway_knx_router_openknx.cpp @@ -19,14 +19,98 @@ bool GatewayKnxTpIpRouter::handleOpenKnxTunnelFrame(const uint8_t* data, size_t const uint8_t* suppress_routing_echo, size_t suppress_routing_echo_len) { bool route_to_oam = response_client != nullptr && response_client->oam_router_persona; - if (!route_to_oam && data != nullptr && len >= 2) { + bool route_to_all_internal_instances = false; + if (data != nullptr && len >= 2) { std::vector frame_data(data, data + len); CemiFrame frame(frame_data.data(), static_cast(frame_data.size())); - if (frame.valid() && oam_router_ != nullptr && - MatchesOamRouterLocalIndividualAddress(frame, *oam_router_)) { - route_to_oam = true; + if (frame.valid()) { + route_to_all_internal_instances = IsKnxBroadcastManagementRequest(frame); + if (!route_to_oam && oam_router_ != nullptr && + MatchesOamRouterLocalIndividualAddress(frame, *oam_router_)) { + route_to_oam = true; + } } } + if (route_to_all_internal_instances) { + SemaphoreGuard guard(openknx_lock_); + if (ets_device_ == nullptr && oam_router_ == nullptr) { + return false; + } + std::vector tunnel_confirmation; + const bool needs_tunnel_confirmation = + response_client != nullptr && response_client->connected && + response_service == kServiceTunnellingRequest && + BuildTunnelConfirmationFrame(data, len, &tunnel_confirmation); + bool sent_tunnel_confirmation = false; + const auto send_response = + [this, response_client, response_service, needs_tunnel_confirmation, + &tunnel_confirmation, &sent_tunnel_confirmation, suppress_routing_echo, + suppress_routing_echo_len](const uint8_t* response, size_t response_len) { + if (response == nullptr || response_len == 0) { + return; + } + publishCloudCemiFrame(response, response_len); + const bool routing_context = + response_client == nullptr && response_service == kServiceRoutingIndication; + const auto message_code = CemiMessageCode(response, response_len); + if (routing_context && suppress_routing_echo != nullptr && + IsLocalRoutingEchoIndication(response, response_len, suppress_routing_echo, + suppress_routing_echo_len)) { + return; + } + if (needs_tunnel_confirmation && !sent_tunnel_confirmation && + message_code.has_value() && message_code.value() != L_data_con) { + sent_tunnel_confirmation = sendCemiFrameToClient( + *response_client, kServiceTunnellingRequest, + tunnel_confirmation.data(), tunnel_confirmation.size()); + } + + const uint16_t service = KnxIpServiceForCemi(response, response_len, response_service); + if (service == kServiceDeviceConfigurationRequest) { + if (response_client != nullptr && response_client->connected) { + sendCemiFrameToClient(*response_client, service, response, response_len); + } else if (routing_context) { + sendRoutingIndication(response, response_len); + } + return; + } + if (message_code.has_value() && message_code.value() == L_data_con) { + if (routing_context) { + return; + } + if (response_client != nullptr && response_client->connected) { + sent_tunnel_confirmation = + sendCemiFrameToClient(*response_client, service, response, response_len) || + sent_tunnel_confirmation; + } + return; + } + if (routing_context) { + sendRoutingIndication(response, response_len); + return; + } + if (response_client != nullptr && response_client->connected) { + sendCemiFrameToClient(*response_client, service, response, response_len); + return; + } + sendTunnelIndication(response, response_len); + }; + bool consumed = false; + if (ets_device_ != nullptr) { + consumed = ets_device_->handleLocalBroadcastManagementFrame(data, len, send_response) || + consumed; + } + if (oam_router_ != nullptr) { + consumed = oam_router_->handleLocalBroadcastManagementFrame(data, len, send_response) || + consumed; + } + if (needs_tunnel_confirmation && consumed && !sent_tunnel_confirmation) { + sendCemiFrameToClient(*response_client, kServiceTunnellingRequest, + tunnel_confirmation.data(), tunnel_confirmation.size()); + } + syncOpenKnxConfigFromDevice(); + return consumed; + } if (route_to_oam) { return handleOamRouterTunnelFrame(data, len, response_client, response_service, suppress_routing_echo, suppress_routing_echo_len); diff --git a/components/gateway_knx/src/gateway_knx_router_services.cpp b/components/gateway_knx/src/gateway_knx_router_services.cpp index b330792..6ab2b7c 100644 --- a/components/gateway_knx/src/gateway_knx_router_services.cpp +++ b/components/gateway_knx/src/gateway_knx_router_services.cpp @@ -184,21 +184,31 @@ void GatewayKnxTpIpRouter::handleRoutingIndication(const uint8_t* packet_data, s const uint8_t* cemi = frame.data(); const size_t cemi_len = frame.dataLength(); bool consumed_by_local_application = false; - const bool addressed_to_oam = + const bool addressed_to_oam = oam_router_ != nullptr && MatchesOamRouterLocalIndividualAddress(frame, *oam_router_); - const bool addressed_to_reg1 = + const bool addressed_to_reg1 = ets_device_ != nullptr && MatchesOpenKnxLocalIndividualAddress(frame, *ets_device_); - if (addressed_to_oam || addressed_to_reg1) { - std::vector local_tunnel_frame; - if (BuildLocalRoutingTunnelFrame(cemi, cemi_len, &local_tunnel_frame)) { - consumed_by_local_application = handleOpenKnxTunnelFrame( + std::vector local_tunnel_frame; + const bool has_local_tunnel_frame = + BuildLocalRoutingTunnelFrame(cemi, cemi_len, &local_tunnel_frame); + bool broadcast_management = false; + if (has_local_tunnel_frame) { + CemiFrame local_frame(local_tunnel_frame.data(), + static_cast(local_tunnel_frame.size())); + broadcast_management = local_frame.valid() && IsKnxBroadcastManagementRequest(local_frame); + } + if ((addressed_to_oam || addressed_to_reg1 || broadcast_management) && + has_local_tunnel_frame) { + consumed_by_local_application = handleOpenKnxTunnelFrame( local_tunnel_frame.data(), local_tunnel_frame.size(), nullptr, kServiceRoutingIndication, frame.messageCode() == L_data_ind ? cemi : nullptr, frame.messageCode() == L_data_ind ? cemi_len : 0); - } } if (consumed_by_local_application) { + if (broadcast_management) { + transmitOpenKnxTpFrame(cemi, cemi_len); + } return; } const bool consumed_by_openknx = handleOpenKnxBusFrame(cemi, cemi_len); @@ -387,6 +397,7 @@ void GatewayKnxTpIpRouter::handleTunnellingRequest(const uint8_t* packet_data, s if (frame.messageCode() == L_data_req && frame.sourceAddress() == 0) { frame.sourceAddress(client->individual_address); } + const bool broadcast_management = IsKnxBroadcastManagementRequest(frame); const uint8_t* cemi = frame.data(); const size_t cemi_len = frame.dataLength(); const std::vector current_cemi(cemi, cemi + cemi_len); @@ -441,9 +452,9 @@ 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"); - const bool sent_to_tp = !consumed_by_openknx && !routed_to_dali && + const bool sent_to_tp = (!consumed_by_openknx || broadcast_management) && !routed_to_dali && transmitOpenKnxTpFrame(cemi, cemi_len); - if ((!consumed_by_openknx && routed_to_dali) || sent_to_tp) { + if (!consumed_by_openknx && (routed_to_dali || sent_to_tp)) { std::vector tunnel_confirmation; if (BuildTunnelConfirmationFrame(cemi, cemi_len, &tunnel_confirmation)) { sendCemiFrameToClient(*client, kServiceTunnellingRequest, tunnel_confirmation.data(), diff --git a/components/gateway_knx/src/oam_router_runtime.cpp b/components/gateway_knx/src/oam_router_runtime.cpp index 83ab2aa..a27f1fa 100644 --- a/components/gateway_knx/src/oam_router_runtime.cpp +++ b/components/gateway_knx/src/oam_router_runtime.cpp @@ -22,6 +22,21 @@ bool IsUsableIndividualAddress(uint16_t address) { return address != 0 && address != kInvalidIndividualAddress; } +bool IsBroadcastManagementRequest(CemiFrame& frame) { + if (frame.addressType() != GroupAddress || frame.destinationAddress() != 0x0000) { + return false; + } + switch (frame.apdu().type()) { + case IndividualAddressWrite: + case IndividualAddressRead: + case IndividualAddressSerialNumberRead: + case IndividualAddressSerialNumberWrite: + return true; + default: + return false; + } +} + uint32_t OamBauNumberFromBaseMac() { uint8_t mac[6]{}; if (esp_efuse_mac_get_default(mac) != ESP_OK && esp_read_mac(mac, ESP_MAC_WIFI_STA) != ESP_OK) { @@ -218,6 +233,33 @@ bool OamRouterRuntime::handleTunnelFrame(const uint8_t* data, size_t len, #endif } +bool OamRouterRuntime::handleLocalBroadcastManagementFrame(const uint8_t* data, size_t len, + CemiFrameSender sender) { +#if defined(ENABLE_BAU091A_PERSONA) + if (data == nullptr || len < 2) { + return false; + } + std::vector frame_data(data, data + len); + CemiFrame frame(frame_data.data(), static_cast(frame_data.size())); + if (!frame.valid() || !IsBroadcastManagementRequest(frame)) { + return false; + } + const HopCountType hop_type = + frame.npdu().hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter; + sender_ = std::move(sender); + device_.injectDataBroadcastIndication(hop_type, frame.priority(), frame.sourceAddress(), + frame.apdu()); + loop(); + sender_ = nullptr; + return true; +#else + (void)data; + (void)len; + (void)sender; + return false; +#endif +} + void OamRouterRuntime::loop() { #if defined(ENABLE_BAU091A_PERSONA) device_.loop(); @@ -268,6 +310,9 @@ bool OamRouterRuntime::shouldConsumeTunnelFrame(CemiFrame& frame) const { return dest == individualAddress() || dest == tunnelClientAddress() || (commissioning && dest == kKnxUnconfiguredBroadcastAddress); } + if (IsBroadcastManagementRequest(frame)) { + return true; + } return false; } default: diff --git a/knx b/knx index ae36452..d19859a 160000 --- a/knx +++ b/knx @@ -1 +1 @@ -Subproject commit ae3645239f917e5b8fe036b3aeb2df30417f5f6d +Subproject commit d19859af47d2c67a8a9a371cc3154e0f66c25491