feat: Enhance EtsDeviceRuntime constructor and multicast handling

- Updated EtsDeviceRuntime constructor to accept an optional tunnel_client_address parameter with a default value of 0.
- Modified EspIdfPlatform::setupMultiCast to use IPPROTO_UDP for socket creation and improved multicast interface selection based on the current IP address.
- Ensured that the multicast interface is set only if a valid local address is available.
- Adjusted the client address assignment in EtsDeviceRuntime to use the provided tunnel_client_address if valid, falling back to the default otherwise.

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-05-13 12:47:37 +08:00
parent b74367e5a0
commit 39ef630608
11 changed files with 595 additions and 118 deletions
@@ -146,7 +146,7 @@ void EspIdfPlatform::fatalError() {
void EspIdfPlatform::setupMultiCast(uint32_t addr, uint16_t port) {
closeMultiCast();
udp_sock_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
udp_sock_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (udp_sock_ < 0) {
ESP_LOGE(kTag, "failed to create UDP socket: errno=%d", errno);
return;
@@ -157,7 +157,8 @@ void EspIdfPlatform::setupMultiCast(uint32_t addr, uint16_t port) {
sockaddr_in bind_addr{};
bind_addr.sin_family = AF_INET;
bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
const uint32_t local_address = currentIpAddress();
bind_addr.sin_addr.s_addr = local_address == 0 ? htonl(INADDR_ANY) : local_address;
bind_addr.sin_port = htons(port);
if (bind(udp_sock_, reinterpret_cast<sockaddr*>(&bind_addr), sizeof(bind_addr)) < 0) {
ESP_LOGE(kTag, "failed to bind UDP socket: errno=%d", errno);
@@ -172,11 +173,20 @@ void EspIdfPlatform::setupMultiCast(uint32_t addr, uint16_t port) {
ip_mreq mreq{};
mreq.imr_multiaddr.s_addr = htonl(addr);
mreq.imr_interface.s_addr = currentIpAddress();
mreq.imr_interface.s_addr = local_address == 0 ? htonl(INADDR_ANY) : local_address;
if (setsockopt(udp_sock_, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
ESP_LOGW(kTag, "failed to join KNX multicast group: errno=%d", errno);
}
if (local_address != 0) {
in_addr multicast_interface{};
multicast_interface.s_addr = local_address;
if (setsockopt(udp_sock_, IPPROTO_IP, IP_MULTICAST_IF, &multicast_interface,
sizeof(multicast_interface)) < 0) {
ESP_LOGW(kTag, "failed to select KNX multicast interface: errno=%d", errno);
}
}
uint8_t loop = 0;
setsockopt(udp_sock_, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
@@ -56,7 +56,8 @@ void ApplyReg1DaliIdentity(Bau07B0& device, EspIdfPlatform& platform) {
} // namespace
EtsDeviceRuntime::EtsDeviceRuntime(std::string nvs_namespace,
uint16_t fallback_individual_address)
uint16_t fallback_individual_address,
uint16_t tunnel_client_address)
: nvs_namespace_(std::move(nvs_namespace)),
platform_(nullptr, nvs_namespace_.c_str()),
device_(platform_) {
@@ -75,7 +76,10 @@ EtsDeviceRuntime::EtsDeviceRuntime(std::string nvs_namespace,
device_.deviceObject().individualAddress(fallback_individual_address);
}
if (auto* server = device_.getCemiServer()) {
server->clientAddress(DefaultTunnelClientAddress(device_.deviceObject().individualAddress()));
server->clientAddress(IsUsableIndividualAddress(tunnel_client_address)
? tunnel_client_address
: DefaultTunnelClientAddress(
device_.deviceObject().individualAddress()));
server->tunnelFrameCallback(&EtsDeviceRuntime::EmitTunnelFrame, this);
}
device_.functionPropertyCallback(&EtsDeviceRuntime::HandleFunctionPropertyCommand);