/* ESPAsyncE131.cpp Project: ESPAsyncE131 - Asynchronous E.131 (sACN) library for Arduino ESP8266 and ESP32 Copyright (c) 2019 Shelby Merrick http://www.forkineye.com This program is provided free for you to use in any way that you wish, subject to the laws and regulations where you are using it. Due diligence is strongly suggested before using this code. Please give credit where due. The Author makes no warranty of any kind, express or implied, with regard to this program or the documentation contained in this document. The Author shall not be liable in any event for incidental or consequential damages in connection with, or arising out of, the furnishing, performance or use of these programs. */ #include #include #include #include "sacn.h" // E1.17 ACN Packet Identifier const uint8_t ESPAsyncE131::ACN_ID[12] = { 0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00 }; ESPAsyncE131::ESPAsyncE131() { stats.num_packets = 0; stats.packet_errors = 0; udp.onPacket(std::bind(&ESPAsyncE131::parsePacket, this, std::placeholders::_1)); } bool ESPAsyncE131::subscribe(uint16_t num) { bool success = false; if (universes_.count(num)) { // already subscribed return true; } success = udp.listenMulticast(E131MulticastAddress(num), E131_DEFAULT_PORT); Universe *u = new Universe(); universes_.emplace(num, u); return success; } void ESPAsyncE131::parsePacket(AsyncUDPPacket _packet) { e131_error_t error = ERROR_NONE; buff_ = reinterpret_cast(_packet.data()); if (memcmp(buff_->acn_id, ESPAsyncE131::ACN_ID, sizeof(buff_->acn_id))) error = ERROR_ACN_ID; else if (htonl(buff_->root_vector) != ESPAsyncE131::VECTOR_ROOT) error = ERROR_VECTOR_ROOT; else if (htonl(buff_->frame_vector) != ESPAsyncE131::VECTOR_FRAME) error = ERROR_VECTOR_FRAME; else if (buff_->dmp_vector != ESPAsyncE131::VECTOR_DMP) error = ERROR_VECTOR_DMP; else if (buff_->property_values[0] != 0) error = ERROR_IGNORE; if (error == ERROR_NONE) { stats.num_packets++; stats.last_clientIP = _packet.remoteIP(); stats.last_clientPort = _packet.remotePort(); stats.last_seen = millis(); uint16_t univ_number = buff_->universe >> 8 | buff_->universe << 8; if (universes_.count(univ_number)) { // slots beyond the recieved count should be zero'd std::array full = {0}; uint16_t slot_count = buff_->property_value_count >> 8 | buff_->property_value_count << 8; std::copy(std::begin(buff_->property_values), std::begin(buff_->property_values) + slot_count, std::begin(full)); // copy full-lenght universe e111_universe_t u; std::copy(std::begin(full), std::end(full), std::begin(u.data)); // set universe data universes_.at(univ_number)->setData(&u); } } else if (error == ERROR_IGNORE) { // Do nothing } else { if (Serial) dumpError(buff_, error); stats.packet_errors++; } } void ESPAsyncE131::dumpError(e131_packet_t *packet, e131_error_t error) { switch (error) { case ERROR_ACN_ID: Serial.print(F("INVALID PACKET ID: ")); for (uint i = 0; i < sizeof(ACN_ID); i++) Serial.print(packet->acn_id[i], HEX); Serial.println(""); break; case ERROR_PACKET_SIZE: Serial.println(F("INVALID PACKET SIZE: ")); break; case ERROR_VECTOR_ROOT: Serial.print(F("INVALID ROOT VECTOR: 0x")); Serial.println(htonl(packet->root_vector), HEX); break; case ERROR_VECTOR_FRAME: Serial.print(F("INVALID FRAME VECTOR: 0x")); Serial.println(htonl(packet->frame_vector), HEX); break; case ERROR_VECTOR_DMP: Serial.print(F("INVALID DMP VECTOR: 0x")); Serial.println(packet->dmp_vector, HEX); case ERROR_NONE: break; case ERROR_IGNORE: break; } } IPAddress ESPAsyncE131::E131MulticastAddress(uint16_t universe) { return IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff)); }