mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Add E1.31 support (#950)
This adds a `e131` component that allows to register `e131` addressable light effect. This uses an internal implementation that is thread-safe instead of using external libraries.
This commit is contained in:
		
							
								
								
									
										49
									
								
								esphome/components/e131/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								esphome/components/e131/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components.light.types import AddressableLightEffect | ||||||
|  | from esphome.components.light.effects import register_addressable_effect | ||||||
|  | from esphome.const import CONF_ID, CONF_NAME, CONF_METHOD, CONF_CHANNELS | ||||||
|  |  | ||||||
|  | e131_ns = cg.esphome_ns.namespace('e131') | ||||||
|  | E131AddressableLightEffect = e131_ns.class_('E131AddressableLightEffect', AddressableLightEffect) | ||||||
|  | E131Component = e131_ns.class_('E131Component', cg.Component) | ||||||
|  |  | ||||||
|  | METHODS = { | ||||||
|  |     'UNICAST': e131_ns.E131_UNICAST, | ||||||
|  |     'MULTICAST': e131_ns.E131_MULTICAST | ||||||
|  | } | ||||||
|  |  | ||||||
|  | CHANNELS = { | ||||||
|  |     'MONO': e131_ns.E131_MONO, | ||||||
|  |     'RGB': e131_ns.E131_RGB, | ||||||
|  |     'RGBW': e131_ns.E131_RGBW | ||||||
|  | } | ||||||
|  |  | ||||||
|  | CONF_UNIVERSE = 'universe' | ||||||
|  | CONF_E131_ID = 'e131_id' | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = cv.Schema({ | ||||||
|  |     cv.GenerateID(): cv.declare_id(E131Component), | ||||||
|  |     cv.Optional(CONF_METHOD, default='MULTICAST'): cv.one_of(*METHODS, upper=True), | ||||||
|  | }) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     yield cg.register_component(var, config) | ||||||
|  |     cg.add(var.set_method(METHODS[config[CONF_METHOD]])) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @register_addressable_effect('e131', E131AddressableLightEffect, "E1.31", { | ||||||
|  |     cv.GenerateID(CONF_E131_ID): cv.use_id(E131Component), | ||||||
|  |     cv.Required(CONF_UNIVERSE): cv.int_range(min=1, max=512), | ||||||
|  |     cv.Optional(CONF_CHANNELS, default='RGB'): cv.one_of(*CHANNELS, upper=True) | ||||||
|  | }) | ||||||
|  | def e131_light_effect_to_code(config, effect_id): | ||||||
|  |     parent = yield cg.get_variable(config[CONF_E131_ID]) | ||||||
|  |  | ||||||
|  |     effect = cg.new_Pvariable(effect_id, config[CONF_NAME]) | ||||||
|  |     cg.add(effect.set_first_universe(config[CONF_UNIVERSE])) | ||||||
|  |     cg.add(effect.set_channels(CHANNELS[config[CONF_CHANNELS]])) | ||||||
|  |     cg.add(effect.set_e131(parent)) | ||||||
|  |     yield effect | ||||||
							
								
								
									
										105
									
								
								esphome/components/e131/e131.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								esphome/components/e131/e131.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | |||||||
|  | #include "e131.h" | ||||||
|  | #include "e131_addressable_light_effect.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | #ifdef ARDUINO_ARCH_ESP32 | ||||||
|  | #include <WiFi.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef ARDUINO_ARCH_ESP8266 | ||||||
|  | #include <ESP8266WiFi.h> | ||||||
|  | #include <WiFiUdp.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace e131 { | ||||||
|  |  | ||||||
|  | static const char *TAG = "e131"; | ||||||
|  | static const int PORT = 5568; | ||||||
|  |  | ||||||
|  | E131Component::E131Component() {} | ||||||
|  |  | ||||||
|  | E131Component::~E131Component() { | ||||||
|  |   if (udp_) { | ||||||
|  |     udp_->stop(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void E131Component::setup() { | ||||||
|  |   udp_.reset(new WiFiUDP()); | ||||||
|  |  | ||||||
|  |   if (!udp_->begin(PORT)) { | ||||||
|  |     ESP_LOGE(TAG, "Cannot bind E131 to %d.", PORT); | ||||||
|  |     mark_failed(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   join_igmp_groups_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void E131Component::loop() { | ||||||
|  |   std::vector<uint8_t> payload; | ||||||
|  |   E131Packet packet; | ||||||
|  |   int universe = 0; | ||||||
|  |  | ||||||
|  |   while (uint16_t packet_size = udp_->parsePacket()) { | ||||||
|  |     payload.resize(packet_size); | ||||||
|  |  | ||||||
|  |     if (!udp_->read(&payload[0], payload.size())) { | ||||||
|  |       continue; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!packet_(payload, universe, packet)) { | ||||||
|  |       ESP_LOGV(TAG, "Invalid packet recevied of size %zu.", payload.size()); | ||||||
|  |       continue; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!process_(universe, packet)) { | ||||||
|  |       ESP_LOGV(TAG, "Ignored packet for %d universe of size %d.", universe, packet.count); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void E131Component::add_effect(E131AddressableLightEffect *light_effect) { | ||||||
|  |   if (light_effects_.count(light_effect)) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   ESP_LOGD(TAG, "Registering '%s' for universes %d-%d.", light_effect->get_name().c_str(), | ||||||
|  |            light_effect->get_first_universe(), light_effect->get_last_universe()); | ||||||
|  |  | ||||||
|  |   light_effects_.insert(light_effect); | ||||||
|  |  | ||||||
|  |   for (auto universe = light_effect->get_first_universe(); universe <= light_effect->get_last_universe(); ++universe) { | ||||||
|  |     join_(universe); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void E131Component::remove_effect(E131AddressableLightEffect *light_effect) { | ||||||
|  |   if (!light_effects_.count(light_effect)) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   ESP_LOGD(TAG, "Unregistering '%s' for universes %d-%d.", light_effect->get_name().c_str(), | ||||||
|  |            light_effect->get_first_universe(), light_effect->get_last_universe()); | ||||||
|  |  | ||||||
|  |   light_effects_.erase(light_effect); | ||||||
|  |  | ||||||
|  |   for (auto universe = light_effect->get_first_universe(); universe <= light_effect->get_last_universe(); ++universe) { | ||||||
|  |     leave_(universe); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool E131Component::process_(int universe, const E131Packet &packet) { | ||||||
|  |   bool handled = false; | ||||||
|  |  | ||||||
|  |   ESP_LOGV(TAG, "Received E1.31 packet for %d universe, with %d bytes", universe, packet.count); | ||||||
|  |  | ||||||
|  |   for (auto light_effect : light_effects_) { | ||||||
|  |     handled = light_effect->process_(universe, packet) || handled; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return handled; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace e131 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										57
									
								
								esphome/components/e131/e131.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								esphome/components/e131/e131.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  |  | ||||||
|  | #include <memory> | ||||||
|  | #include <set> | ||||||
|  | #include <map> | ||||||
|  |  | ||||||
|  | class UDP; | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace e131 { | ||||||
|  |  | ||||||
|  | class E131AddressableLightEffect; | ||||||
|  |  | ||||||
|  | enum E131ListenMethod { E131_MULTICAST, E131_UNICAST }; | ||||||
|  |  | ||||||
|  | const int E131_MAX_PROPERTY_VALUES_COUNT = 513; | ||||||
|  |  | ||||||
|  | struct E131Packet { | ||||||
|  |   uint16_t count; | ||||||
|  |   uint8_t values[E131_MAX_PROPERTY_VALUES_COUNT]; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class E131Component : public esphome::Component { | ||||||
|  |  public: | ||||||
|  |   E131Component(); | ||||||
|  |   ~E131Component(); | ||||||
|  |  | ||||||
|  |   void setup() override; | ||||||
|  |   void loop() override; | ||||||
|  |   float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } | ||||||
|  |  | ||||||
|  |  public: | ||||||
|  |   void add_effect(E131AddressableLightEffect *light_effect); | ||||||
|  |   void remove_effect(E131AddressableLightEffect *light_effect); | ||||||
|  |  | ||||||
|  |  public: | ||||||
|  |   void set_method(E131ListenMethod listen_method) { this->listen_method_ = listen_method; } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   bool packet_(const std::vector<uint8_t> &data, int &universe, E131Packet &packet); | ||||||
|  |   bool process_(int universe, const E131Packet &packet); | ||||||
|  |   bool join_igmp_groups_(); | ||||||
|  |   void join_(int universe); | ||||||
|  |   void leave_(int universe); | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   E131ListenMethod listen_method_{E131_MULTICAST}; | ||||||
|  |   std::unique_ptr<UDP> udp_; | ||||||
|  |   std::set<E131AddressableLightEffect *> light_effects_; | ||||||
|  |   std::map<int, int> universe_consumers_; | ||||||
|  |   std::map<int, E131Packet> universe_packets_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace e131 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										90
									
								
								esphome/components/e131/e131_addressable_light_effect.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								esphome/components/e131/e131_addressable_light_effect.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | |||||||
|  | #include "e131.h" | ||||||
|  | #include "e131_addressable_light_effect.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace e131 { | ||||||
|  |  | ||||||
|  | static const char *TAG = "e131_addressable_light_effect"; | ||||||
|  | static const int MAX_DATA_SIZE = (sizeof(E131Packet::values) - 1); | ||||||
|  |  | ||||||
|  | E131AddressableLightEffect::E131AddressableLightEffect(const std::string &name) : AddressableLightEffect(name) {} | ||||||
|  |  | ||||||
|  | int E131AddressableLightEffect::get_data_per_universe() const { return get_lights_per_universe() * channels_; } | ||||||
|  |  | ||||||
|  | int E131AddressableLightEffect::get_lights_per_universe() const { return MAX_DATA_SIZE / channels_; } | ||||||
|  |  | ||||||
|  | int E131AddressableLightEffect::get_first_universe() const { return first_universe_; } | ||||||
|  |  | ||||||
|  | int E131AddressableLightEffect::get_last_universe() const { return first_universe_ + get_universe_count() - 1; } | ||||||
|  |  | ||||||
|  | int E131AddressableLightEffect::get_universe_count() const { | ||||||
|  |   // Round up to lights_per_universe | ||||||
|  |   auto lights = get_lights_per_universe(); | ||||||
|  |   return (get_addressable_()->size() + lights - 1) / lights; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void E131AddressableLightEffect::start() { | ||||||
|  |   AddressableLightEffect::start(); | ||||||
|  |  | ||||||
|  |   if (this->e131_) { | ||||||
|  |     this->e131_->add_effect(this); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void E131AddressableLightEffect::stop() { | ||||||
|  |   if (this->e131_) { | ||||||
|  |     this->e131_->remove_effect(this); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   AddressableLightEffect::stop(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void E131AddressableLightEffect::apply(light::AddressableLight &it, const light::ESPColor ¤t_color) { | ||||||
|  |   // ignore, it is run by `E131Component::update()` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool E131AddressableLightEffect::process_(int universe, const E131Packet &packet) { | ||||||
|  |   auto it = get_addressable_(); | ||||||
|  |  | ||||||
|  |   // check if this is our universe and data are valid | ||||||
|  |   if (universe < first_universe_ || universe > get_last_universe()) | ||||||
|  |     return false; | ||||||
|  |  | ||||||
|  |   int output_offset = (universe - first_universe_) * get_lights_per_universe(); | ||||||
|  |   // limit amount of lights per universe and received | ||||||
|  |   int output_end = std::min(it->size(), std::min(output_offset + get_lights_per_universe(), packet.count - 1)); | ||||||
|  |   auto input_data = packet.values + 1; | ||||||
|  |  | ||||||
|  |   ESP_LOGV(TAG, "Applying data for '%s' on %d universe, for %d-%d.", get_name().c_str(), universe, output_offset, | ||||||
|  |            output_end); | ||||||
|  |  | ||||||
|  |   switch (channels_) { | ||||||
|  |     case E131_MONO: | ||||||
|  |       for (; output_offset < output_end; output_offset++, input_data++) { | ||||||
|  |         auto output = (*it)[output_offset]; | ||||||
|  |         output.set(light::ESPColor(input_data[0], input_data[0], input_data[0], input_data[0])); | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |  | ||||||
|  |     case E131_RGB: | ||||||
|  |       for (; output_offset < output_end; output_offset++, input_data += 3) { | ||||||
|  |         auto output = (*it)[output_offset]; | ||||||
|  |         output.set(light::ESPColor(input_data[0], input_data[1], input_data[2], | ||||||
|  |                                    (input_data[0] + input_data[1] + input_data[2]) / 3)); | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |  | ||||||
|  |     case E131_RGBW: | ||||||
|  |       for (; output_offset < output_end; output_offset++, input_data += 4) { | ||||||
|  |         auto output = (*it)[output_offset]; | ||||||
|  |         output.set(light::ESPColor(input_data[0], input_data[1], input_data[2], input_data[3])); | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace e131 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										48
									
								
								esphome/components/e131/e131_addressable_light_effect.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								esphome/components/e131/e131_addressable_light_effect.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/components/light/addressable_light_effect.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace e131 { | ||||||
|  |  | ||||||
|  | class E131Component; | ||||||
|  | struct E131Packet; | ||||||
|  |  | ||||||
|  | enum E131LightChannels { E131_MONO = 1, E131_RGB = 3, E131_RGBW = 4 }; | ||||||
|  |  | ||||||
|  | class E131AddressableLightEffect : public light::AddressableLightEffect { | ||||||
|  |  public: | ||||||
|  |   E131AddressableLightEffect(const std::string &name); | ||||||
|  |  | ||||||
|  |  public: | ||||||
|  |   void start() override; | ||||||
|  |   void stop() override; | ||||||
|  |   void apply(light::AddressableLight &it, const light::ESPColor ¤t_color) override; | ||||||
|  |  | ||||||
|  |  public: | ||||||
|  |   int get_data_per_universe() const; | ||||||
|  |   int get_lights_per_universe() const; | ||||||
|  |   int get_first_universe() const; | ||||||
|  |   int get_last_universe() const; | ||||||
|  |   int get_universe_count() const; | ||||||
|  |  | ||||||
|  |  public: | ||||||
|  |   void set_first_universe(int universe) { this->first_universe_ = universe; } | ||||||
|  |   void set_channels(E131LightChannels channels) { this->channels_ = channels; } | ||||||
|  |   void set_e131(E131Component *e131) { this->e131_ = e131; } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   bool process_(int universe, const E131Packet &packet); | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   int first_universe_{0}; | ||||||
|  |   int last_universe_{0}; | ||||||
|  |   E131LightChannels channels_{E131_RGB}; | ||||||
|  |   E131Component *e131_{nullptr}; | ||||||
|  |  | ||||||
|  |   friend class E131Component; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace e131 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										136
									
								
								esphome/components/e131/e131_packet.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								esphome/components/e131/e131_packet.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | |||||||
|  | #include "e131.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  | #include "esphome/core/util.h" | ||||||
|  |  | ||||||
|  | #include <lwip/ip_addr.h> | ||||||
|  | #include <lwip/igmp.h> | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace e131 { | ||||||
|  |  | ||||||
|  | static const char *TAG = "e131"; | ||||||
|  |  | ||||||
|  | static const uint8_t ACN_ID[12] = {0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00}; | ||||||
|  | static const uint32_t VECTOR_ROOT = 4; | ||||||
|  | static const uint32_t VECTOR_FRAME = 2; | ||||||
|  | static const uint8_t VECTOR_DMP = 2; | ||||||
|  |  | ||||||
|  | // E1.31 Packet Structure | ||||||
|  | union E131RawPacket { | ||||||
|  |   struct { | ||||||
|  |     // Root Layer | ||||||
|  |     uint16_t preamble_size; | ||||||
|  |     uint16_t postamble_size; | ||||||
|  |     uint8_t acn_id[12]; | ||||||
|  |     uint16_t root_flength; | ||||||
|  |     uint32_t root_vector; | ||||||
|  |     uint8_t cid[16]; | ||||||
|  |  | ||||||
|  |     // Frame Layer | ||||||
|  |     uint16_t frame_flength; | ||||||
|  |     uint32_t frame_vector; | ||||||
|  |     uint8_t source_name[64]; | ||||||
|  |     uint8_t priority; | ||||||
|  |     uint16_t reserved; | ||||||
|  |     uint8_t sequence_number; | ||||||
|  |     uint8_t options; | ||||||
|  |     uint16_t universe; | ||||||
|  |  | ||||||
|  |     // DMP Layer | ||||||
|  |     uint16_t dmp_flength; | ||||||
|  |     uint8_t dmp_vector; | ||||||
|  |     uint8_t type; | ||||||
|  |     uint16_t first_address; | ||||||
|  |     uint16_t address_increment; | ||||||
|  |     uint16_t property_value_count; | ||||||
|  |     uint8_t property_values[E131_MAX_PROPERTY_VALUES_COUNT]; | ||||||
|  |   } __attribute__((packed)); | ||||||
|  |  | ||||||
|  |   uint8_t raw[638]; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // We need to have at least one `1` value | ||||||
|  | // Get the offset of `property_values[1]` | ||||||
|  | const long E131_MIN_PACKET_SIZE = reinterpret_cast<long>(&((E131RawPacket *) nullptr)->property_values[1]); | ||||||
|  |  | ||||||
|  | bool E131Component::join_igmp_groups_() { | ||||||
|  |   if (listen_method_ != E131_MULTICAST) | ||||||
|  |     return false; | ||||||
|  |   if (!udp_) | ||||||
|  |     return false; | ||||||
|  |  | ||||||
|  |   for (auto universe : universe_consumers_) { | ||||||
|  |     if (!universe.second) | ||||||
|  |       continue; | ||||||
|  |  | ||||||
|  |     ip4_addr_t multicast_addr = { | ||||||
|  |         static_cast<uint32_t>(IPAddress(239, 255, ((universe.first >> 8) & 0xff), ((universe.first >> 0) & 0xff)))}; | ||||||
|  |  | ||||||
|  |     auto err = igmp_joingroup(IP4_ADDR_ANY4, &multicast_addr); | ||||||
|  |  | ||||||
|  |     if (err) { | ||||||
|  |       ESP_LOGW(TAG, "IGMP join for %d universe of E1.31 failed. Multicast might not work.", universe.first); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void E131Component::join_(int universe) { | ||||||
|  |   // store only latest received packet for the given universe | ||||||
|  |   auto consumers = ++universe_consumers_[universe]; | ||||||
|  |  | ||||||
|  |   if (consumers > 1) { | ||||||
|  |     return;  // we already joined before | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (join_igmp_groups_()) { | ||||||
|  |     ESP_LOGD(TAG, "Joined %d universe for E1.31.", universe); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void E131Component::leave_(int universe) { | ||||||
|  |   auto consumers = --universe_consumers_[universe]; | ||||||
|  |  | ||||||
|  |   if (consumers > 0) { | ||||||
|  |     return;  // we have other consumers of the given universe | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (listen_method_ == E131_MULTICAST) { | ||||||
|  |     ip4_addr_t multicast_addr = { | ||||||
|  |         static_cast<uint32_t>(IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff)))}; | ||||||
|  |  | ||||||
|  |     igmp_leavegroup(IP4_ADDR_ANY4, &multicast_addr); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   ESP_LOGD(TAG, "Left %d universe for E1.31.", universe); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool E131Component::packet_(const std::vector<uint8_t> &data, int &universe, E131Packet &packet) { | ||||||
|  |   if (data.size() < E131_MIN_PACKET_SIZE) | ||||||
|  |     return false; | ||||||
|  |  | ||||||
|  |   auto sbuff = reinterpret_cast<const E131RawPacket *>(&data[0]); | ||||||
|  |  | ||||||
|  |   if (memcmp(sbuff->acn_id, ACN_ID, sizeof(sbuff->acn_id)) != 0) | ||||||
|  |     return false; | ||||||
|  |   if (htonl(sbuff->root_vector) != VECTOR_ROOT) | ||||||
|  |     return false; | ||||||
|  |   if (htonl(sbuff->frame_vector) != VECTOR_FRAME) | ||||||
|  |     return false; | ||||||
|  |   if (sbuff->dmp_vector != VECTOR_DMP) | ||||||
|  |     return false; | ||||||
|  |   if (sbuff->property_values[0] != 0) | ||||||
|  |     return false; | ||||||
|  |  | ||||||
|  |   universe = htons(sbuff->universe); | ||||||
|  |   packet.count = htons(sbuff->property_value_count); | ||||||
|  |   if (packet.count > E131_MAX_PROPERTY_VALUES_COUNT) | ||||||
|  |     return false; | ||||||
|  |  | ||||||
|  |   memcpy(packet.values, sbuff->property_values, packet.count); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace e131 | ||||||
|  | }  // namespace esphome | ||||||
| @@ -1048,6 +1048,8 @@ output: | |||||||
|     pin: GPIO25 |     pin: GPIO25 | ||||||
|     id: dac_output |     id: dac_output | ||||||
|  |  | ||||||
|  | e131: | ||||||
|  |  | ||||||
| light: | light: | ||||||
|   - platform: binary |   - platform: binary | ||||||
|     name: "Desk Lamp" |     name: "Desk Lamp" | ||||||
| @@ -1189,6 +1191,8 @@ light: | |||||||
|               red: 0% |               red: 0% | ||||||
|               green: 100% |               green: 100% | ||||||
|               blue: 0% |               blue: 0% | ||||||
|  |     - e131: | ||||||
|  |         universe: 1 | ||||||
|   - platform: fastled_spi |   - platform: fastled_spi | ||||||
|     id: addr2 |     id: addr2 | ||||||
|     chipset: WS2801 |     chipset: WS2801 | ||||||
|   | |||||||
| @@ -697,6 +697,8 @@ mcp23017: | |||||||
| mcp23008: | mcp23008: | ||||||
|   id: mcp23008_hub |   id: mcp23008_hub | ||||||
|  |  | ||||||
|  | e131: | ||||||
|  |  | ||||||
| light: | light: | ||||||
|   - platform: neopixelbus |   - platform: neopixelbus | ||||||
|     name: Neopixelbus Light |     name: Neopixelbus Light | ||||||
| @@ -705,6 +707,9 @@ light: | |||||||
|     variant: SK6812 |     variant: SK6812 | ||||||
|     method: ESP8266_UART0 |     method: ESP8266_UART0 | ||||||
|     num_leds: 100 |     num_leds: 100 | ||||||
|  |     effects: | ||||||
|  |       - e131: | ||||||
|  |           universe: 1 | ||||||
|  |  | ||||||
| servo: | servo: | ||||||
|   id: my_servo |   id: my_servo | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user