From 4bf13a8143354fb3b14ae9e9ccf29c0fbe60f4b7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 19 Feb 2026 00:11:14 -0600 Subject: [PATCH 1/3] [e131] Replace std::map with std::vector for universe tracking Replace std::map with std::vector for tracking universe reference counts. This eliminates red-black tree overhead for what is typically 1-10 entries. Co-Authored-By: J. Nick Koston --- esphome/components/e131/e131.h | 9 +++++-- esphome/components/e131/e131_packet.cpp | 36 ++++++++++++++++--------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/esphome/components/e131/e131.h b/esphome/components/e131/e131.h index d4b272eae2..838e30483d 100644 --- a/esphome/components/e131/e131.h +++ b/esphome/components/e131/e131.h @@ -5,7 +5,6 @@ #include "esphome/core/component.h" #include -#include #include #include @@ -23,6 +22,11 @@ struct E131Packet { uint8_t values[E131_MAX_PROPERTY_VALUES_COUNT]; }; +struct UniverseConsumer { + uint16_t universe; + uint8_t consumers; +}; + class E131Component : public esphome::Component { public: E131Component(); @@ -41,13 +45,14 @@ class E131Component : public esphome::Component { bool packet_(const uint8_t *data, size_t len, int &universe, E131Packet &packet); bool process_(int universe, const E131Packet &packet); bool join_igmp_groups_(); + UniverseConsumer *find_universe_(int universe); void join_(int universe); void leave_(int universe); E131ListenMethod listen_method_{E131_MULTICAST}; std::unique_ptr socket_; std::vector light_effects_; - std::map universe_consumers_; + std::vector universe_consumers_; }; } // namespace e131 diff --git a/esphome/components/e131/e131_packet.cpp b/esphome/components/e131/e131_packet.cpp index ed081e5758..e1648a099b 100644 --- a/esphome/components/e131/e131_packet.cpp +++ b/esphome/components/e131/e131_packet.cpp @@ -60,17 +60,17 @@ union E131RawPacket { const size_t E131_MIN_PACKET_SIZE = reinterpret_cast(&((E131RawPacket *) nullptr)->property_values[1]); bool E131Component::join_igmp_groups_() { - if (listen_method_ != E131_MULTICAST) + if (this->listen_method_ != E131_MULTICAST) return false; if (this->socket_ == nullptr) return false; - for (auto universe : universe_consumers_) { - if (!universe.second) + for (auto &entry : this->universe_consumers_) { + if (!entry.consumers) continue; ip4_addr_t multicast_addr = - network::IPAddress(239, 255, ((universe.first >> 8) & 0xff), ((universe.first >> 0) & 0xff)); + network::IPAddress(239, 255, ((entry.universe >> 8) & 0xff), ((entry.universe >> 0) & 0xff)); err_t err; { @@ -79,34 +79,46 @@ bool E131Component::join_igmp_groups_() { } if (err) { - ESP_LOGW(TAG, "IGMP join for %d universe of E1.31 failed. Multicast might not work.", universe.first); + ESP_LOGW(TAG, "IGMP join for %d universe of E1.31 failed. Multicast might not work.", entry.universe); } } return true; } +UniverseConsumer *E131Component::find_universe_(int universe) { + for (auto &entry : this->universe_consumers_) { + if (entry.universe == universe) + return &entry; + } + return nullptr; +} + void E131Component::join_(int universe) { // store only latest received packet for the given universe - auto consumers = ++universe_consumers_[universe]; - - if (consumers > 1) { + auto *consumer = this->find_universe_(universe); + if (consumer != nullptr) { + consumer->consumers++; return; // we already joined before } - if (join_igmp_groups_()) { + this->universe_consumers_.push_back({static_cast(universe), 1}); + + if (this->join_igmp_groups_()) { ESP_LOGD(TAG, "Joined %d universe for E1.31.", universe); } } void E131Component::leave_(int universe) { - auto consumers = --universe_consumers_[universe]; + auto *consumer = this->find_universe_(universe); + if (consumer == nullptr) + return; - if (consumers > 0) { + if (--consumer->consumers > 0) { return; // we have other consumers of the given universe } - if (listen_method_ == E131_MULTICAST) { + if (this->listen_method_ == E131_MULTICAST) { ip4_addr_t multicast_addr = network::IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff)); LwIPLock lock; From 1373f6866a018523f17f4a067d2f8961f1df3e5d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 19 Feb 2026 00:33:06 -0600 Subject: [PATCH 2/3] [e131] Fix IGMP rejoin after effect stop/start cycle When leave_() decremented consumers to 0 without removing the entry, a subsequent join_() would find it, increment 0->1, and return early without rejoining the IGMP multicast group. Now join_() only skips the IGMP rejoin when consumers was already > 0. --- esphome/components/e131/e131_packet.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/esphome/components/e131/e131_packet.cpp b/esphome/components/e131/e131_packet.cpp index e1648a099b..b334100ad1 100644 --- a/esphome/components/e131/e131_packet.cpp +++ b/esphome/components/e131/e131_packet.cpp @@ -98,12 +98,13 @@ void E131Component::join_(int universe) { // store only latest received packet for the given universe auto *consumer = this->find_universe_(universe); if (consumer != nullptr) { - consumer->consumers++; - return; // we already joined before + if (consumer->consumers++ > 0) { + return; // we already joined before + } + } else { + this->universe_consumers_.push_back({static_cast(universe), 1}); } - this->universe_consumers_.push_back({static_cast(universe), 1}); - if (this->join_igmp_groups_()) { ESP_LOGD(TAG, "Joined %d universe for E1.31.", universe); } From 76fda4d4a2ffb953acc9bc678168ec5e2baa52f5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 19 Feb 2026 00:34:20 -0600 Subject: [PATCH 3/3] [e131] Use uint16_t for consumer refcount Struct pads to 4 bytes either way due to alignment, so this is free. --- esphome/components/e131/e131.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/e131/e131.h b/esphome/components/e131/e131.h index 838e30483d..776178b627 100644 --- a/esphome/components/e131/e131.h +++ b/esphome/components/e131/e131.h @@ -24,7 +24,7 @@ struct E131Packet { struct UniverseConsumer { uint16_t universe; - uint8_t consumers; + uint16_t consumers; }; class E131Component : public esphome::Component {