ESPHome  2023.3.1
e131_packet.cpp
Go to the documentation of this file.
1 #ifdef USE_ARDUINO
2 
3 #include "e131.h"
4 #include "esphome/core/log.h"
5 #include "esphome/core/util.h"
7 #include <cstring>
8 
9 #include <lwip/init.h>
10 #include <lwip/ip_addr.h>
11 #include <lwip/ip4_addr.h>
12 #include <lwip/igmp.h>
13 
14 namespace esphome {
15 namespace e131 {
16 
17 static const char *const TAG = "e131";
18 
19 static const uint8_t ACN_ID[12] = {0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00};
20 static const uint32_t VECTOR_ROOT = 4;
21 static const uint32_t VECTOR_FRAME = 2;
22 static const uint8_t VECTOR_DMP = 2;
23 
24 // E1.31 Packet Structure
25 union E131RawPacket {
26  struct {
27  // Root Layer
28  uint16_t preamble_size;
29  uint16_t postamble_size;
30  uint8_t acn_id[12];
31  uint16_t root_flength;
32  uint32_t root_vector;
33  uint8_t cid[16];
34 
35  // Frame Layer
36  uint16_t frame_flength;
37  uint32_t frame_vector;
38  uint8_t source_name[64];
39  uint8_t priority;
40  uint16_t reserved;
41  uint8_t sequence_number;
42  uint8_t options;
43  uint16_t universe;
44 
45  // DMP Layer
46  uint16_t dmp_flength;
47  uint8_t dmp_vector;
48  uint8_t type;
49  uint16_t first_address;
50  uint16_t address_increment;
51  uint16_t property_value_count;
53  } __attribute__((packed));
54 
55  uint8_t raw[638];
56 };
57 
58 // We need to have at least one `1` value
59 // Get the offset of `property_values[1]`
60 const size_t E131_MIN_PACKET_SIZE = reinterpret_cast<size_t>(&((E131RawPacket *) nullptr)->property_values[1]);
61 
63  if (listen_method_ != E131_MULTICAST)
64  return false;
65  if (!udp_)
66  return false;
67 
68  for (auto universe : universe_consumers_) {
69  if (!universe.second)
70  continue;
71 
72  ip4_addr_t multicast_addr = {static_cast<uint32_t>(
73  network::IPAddress(239, 255, ((universe.first >> 8) & 0xff), ((universe.first >> 0) & 0xff)))};
74 
75  auto err = igmp_joingroup(IP4_ADDR_ANY4, &multicast_addr);
76 
77  if (err) {
78  ESP_LOGW(TAG, "IGMP join for %d universe of E1.31 failed. Multicast might not work.", universe.first);
79  }
80  }
81 
82  return true;
83 }
84 
86  // store only latest received packet for the given universe
87  auto consumers = ++universe_consumers_[universe];
88 
89  if (consumers > 1) {
90  return; // we already joined before
91  }
92 
93  if (join_igmp_groups_()) {
94  ESP_LOGD(TAG, "Joined %d universe for E1.31.", universe);
95  }
96 }
97 
99  auto consumers = --universe_consumers_[universe];
100 
101  if (consumers > 0) {
102  return; // we have other consumers of the given universe
103  }
104 
105  if (listen_method_ == E131_MULTICAST) {
106  ip4_addr_t multicast_addr = {
107  static_cast<uint32_t>(network::IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff)))};
108 
109  igmp_leavegroup(IP4_ADDR_ANY4, &multicast_addr);
110  }
111 
112  ESP_LOGD(TAG, "Left %d universe for E1.31.", universe);
113 }
114 
115 bool E131Component::packet_(const std::vector<uint8_t> &data, int &universe, E131Packet &packet) {
116  if (data.size() < E131_MIN_PACKET_SIZE)
117  return false;
119  auto *sbuff = reinterpret_cast<const E131RawPacket *>(&data[0]);
121  if (memcmp(sbuff->acn_id, ACN_ID, sizeof(sbuff->acn_id)) != 0)
122  return false;
123  if (htonl(sbuff->root_vector) != VECTOR_ROOT)
124  return false;
125  if (htonl(sbuff->frame_vector) != VECTOR_FRAME)
126  return false;
127  if (sbuff->dmp_vector != VECTOR_DMP)
128  return false;
129  if (sbuff->property_values[0] != 0)
130  return false;
132  universe = htons(sbuff->universe);
133  packet.count = htons(sbuff->property_value_count);
135  return false;
137  memcpy(packet.values, sbuff->property_values, packet.count);
138  return true;
139 }
141 } // namespace e131
142 } // namespace esphome
143 
144 #endif // USE_ARDUINO
bool packet_(const std::vector< uint8_t > &data, int &universe, E131Packet &packet)
uint8_t sequence_number
uint8_t raw[35]
Definition: bl0939.h:19
uint32_t root_vector
uint16_t frame_flength
uint16_t preamble_size
uint8_t values[E131_MAX_PROPERTY_VALUES_COUNT]
Definition: e131.h:25
uint16_t universe
void join_(int universe)
Definition: e131_packet.cpp:85
uint8_t source_name[64]
uint8_t acn_id[12]
uint16_t address_increment
uint8_t type
const int E131_MAX_PROPERTY_VALUES_COUNT
Definition: e131.h:21
enum esphome::EntityCategory __attribute__
uint16_t property_value_count
uint8_t property_values[E131_MAX_PROPERTY_VALUES_COUNT]
uint8_t options
uint16_t first_address
void leave_(int universe)
Definition: e131_packet.cpp:98
uint8_t priority
uint16_t reserved
uint16_t dmp_flength
uint32_t frame_vector
Definition: a4988.cpp:4
const size_t E131_MIN_PACKET_SIZE
Definition: e131_packet.cpp:60
uint16_t root_flength
uint8_t dmp_vector
uint16_t postamble_size
uint8_t cid[16]