diff --git a/README.md b/README.md index ade7681..bac3164 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,6 @@ SOFTWARE. | Library | Copyright | License | | :- | :- | :-: | [WiFlash](https//git.company235.com/kevin/WiFlasher) | Copyright © 2020 Kevin Matz | [MIT](https//git.company235.com/kevin/WiFlasher/src/branch/master/LICENSE) | -[ESPAsyncE131](https//github.com/forkineye/ESPAsyncE131) | Copyright © 2019 Shelby Merrick | [NeoPixelBus](https//github.com/Makuna/NeoPixelBus) | Michael Miller| [LGPL 3.0](https//github.com/Makuna/NeoPixelBus/blob/master/COPYING) | [ESPAsyncWebServer](https//github.com/me-no-dev/ESPAsyncWebServer) | Copyright © 2016 Hristo Gochkov | [LGPL 2.1](http//www.gnu.org/licenses/lgpl-2.1.html) | [arduino-esp32](https//github.com/espressif/arduino-esp32) | Copyright © 2015 Ivan Grokhotkov | [LGPL 2.1](https//github.com/espressif/arduino-esp32/blob/master/LICENSE.md) | diff --git a/wiflash_esp32/data/www/index.html b/wiflash_esp32/data/www/index.html index 935f59d..29e0f3d 100644 --- a/wiflash_esp32/data/www/index.html +++ b/wiflash_esp32/data/www/index.html @@ -363,11 +363,6 @@ Copyright © 2020 Kevin Matz MIT - - ESPAsyncE131 - Copyright © 2019 Shelby Merrick - - NeoPixelBus Michael Miller diff --git a/wiflash_esp32/sacn.cpp b/wiflash_esp32/sacn.cpp index 2c5d7b0..cbfbeac 100644 --- a/wiflash_esp32/sacn.cpp +++ b/wiflash_esp32/sacn.cpp @@ -1,19 +1,25 @@ /* - ESPAsyncE131.cpp + sacn.cpp - Project: ESPAsyncE131 - Asynchronous E.131 (sACN) library for Arduino ESP8266 and ESP32 - Copyright (c) 2019 Shelby Merrick - http://www.forkineye.com + Copyright (c) 2020 Kevin Matz (kevin.matz@gmail.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. + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: - 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. + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. */ #include @@ -21,27 +27,23 @@ #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, +ESPsACN::ESPsACN() { + udp.onPacket(std::bind(&ESPsACN::parsePacket, this, std::placeholders::_1)); } -bool ESPAsyncE131::subscribe(uint16_t num) { - bool success = false; +bool ESPsACN::subscribe(uint16_t num) { + bool success; if (universes_.count(num)) { // already subscribed return true; } + // listen multicast; works for unicast too success = udp.listenMulticast(E131MulticastAddress(num), - E131_DEFAULT_PORT); + ACN_SDT_MULTICAST_PORT); Universe *u = new Universe(); universes_.emplace(num, u); @@ -49,80 +51,54 @@ bool ESPAsyncE131::subscribe(uint16_t num) { return success; } -void ESPAsyncE131::parsePacket(AsyncUDPPacket _packet) { - e131_error_t error = ERROR_NONE; - +void ESPsACN::parsePacket(AsyncUDPPacket _packet) { 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++; - } + // E1.31 - 5.3 ACN Packet Identifier + // The ACN Packet Identifier shall contain the following sequence of characters + if (memcmp(buff_->rlp.acn_id, ACN_PACKET_IDENTIFIER, sizeof(buff_->rlp.acn_id))) + return; + + // E1.31 - 5.5 Vector + // Sources shall set the Root Layer's Vector to VECTOR_ROOT_E131_DATA + // if the packet contains E1.31 Data + if (htonl(buff_->rlp.vector) != VECTOR_ROOT_E131_DATA) + return; + + // E1.31 - 6.2.1 E1.31 Data Packet: Vector + // Sources sending an E1.31 Data Packet shall set the E1.31 Layer's + // Vector to VECTOR_E131_DATA_PACKET. + if (htonl(buff_->frame.vector) != VECTOR_E131_DATA_PACKET) + return; + + // E1.31 - 7.2 DMP Layer: Vector + // The DMP Layer's Vector shall be set to VECTOR_DMP_SET_PROPERTY + if (buff_->dmp.vector != VECTOR_DMP_SET_PROPERTY) + return; + + // ignore unverses recieved without active subscriptions + if (!universes_.count(htons(buff_->frame.universe))) + return; + + // E1.11 - 8.5.2 Dimmer class data + // Dimmer level data should be sent in NULL START Code packets. + if (buff_->dmp.property_values[0] != E111_NULL_START) + return; + + // slots beyond the recieved count should be zero'd + std::array full = {0}; + std::copy(std::begin(buff_->dmp.property_values), + std::begin(buff_->dmp.property_values) + htons(buff_->dmp.property_value_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(htons(buff_->frame.universe))->setData(&u); } -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) { +IPAddress ESPsACN::E131MulticastAddress(uint16_t universe) { return IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff)); } diff --git a/wiflash_esp32/sacn.h b/wiflash_esp32/sacn.h index 0f86866..794344e 100644 --- a/wiflash_esp32/sacn.h +++ b/wiflash_esp32/sacn.h @@ -1,135 +1,125 @@ /* - ESPAsyncE131.h + sacn.h - Project: ESPAsyncE131 - Asynchronous E.131 (sACN) library for Arduino ESP8266 and ESP32 - Copyright (c) 2019 Shelby Merrick - http://www.forkineye.com + Copyright (c) 2020 Kevin Matz (kevin.matz@gmail.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. + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: - 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. + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. */ -#ifndef ESPASYNCE131_H_ -#define ESPASYNCE131_H_ +#ifndef SACN_H_ +#define SACN_H_ #include #include #include "dmx_universe.h" -// Defaults -#define E131_DEFAULT_PORT 5568 +// Appendix A: Defined Parameters (Normative) +#define VECTOR_ROOT_E131_DATA 0x00000004 +#define VECTOR_ROOT_E131_EXTENDED 0x00000008 -// E1.31 Packet Offsets -#define E131_ROOT_PREAMBLE_SIZE 0 -#define E131_ROOT_POSTAMBLE_SIZE 2 -#define E131_ROOT_ID 4 -#define E131_ROOT_FLENGTH 16 -#define E131_ROOT_VECTOR 18 -#define E131_ROOT_CID 22 +#define VECTOR_DMP_SET_PROPERTY 0x02 // informative -#define E131_FRAME_FLENGTH 38 -#define E131_FRAME_VECTOR 40 -#define E131_FRAME_SOURCE 44 -#define E131_FRAME_PRIORITY 108 -#define E131_FRAME_RESERVED 109 -#define E131_FRAME_SEQ 111 -#define E131_FRAME_OPT 112 -#define E131_FRAME_UNIVERSE 113 +#define VECTOR_E131_DATA_PACKET 0x00000002 -#define E131_DMP_FLENGTH 115 -#define E131_DMP_VECTOR 117 -#define E131_DMP_TYPE 118 -#define E131_DMP_ADDR_FIRST 119 -#define E131_DMP_ADDR_INC 121 -#define E131_DMP_COUNT 123 -#define E131_DMP_DATA 125 +#define VECTOR_E131_EXTENDED_SYNCHRONIZATION 0x00000001 +#define VECTOR_E131_EXTENDED_DISCOVERY 0x00000002 -// E1.31 Packet Structure +#define E131_E131_UNIVERSE_DISCOVER_INTERVAL 10000 // ms +#define E131_NETWORK_DATA_LOSS_TIMEOUT 2500 // ms +#define E131_DISCOVERY_UNIVERSE 64214 + +#define ACN_SDT_MULTICAST_PORT 5568 + +// A S C - E 1 . 1 7 +static constexpr uint8_t ACN_PACKET_IDENTIFIER[] = { 0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00 }; + + +// Table 5-1: ACN Root Layer typedef union { struct { - // Root Layer uint16_t preamble_size; uint16_t postamble_size; uint8_t acn_id[12]; - uint16_t root_flength; - uint32_t root_vector; + uint16_t flength; + uint32_t vector; uint8_t cid[16]; + } __attribute__((packed)); + uint8_t raw[37]; +} e131_rlp_t; - // Frame Layer - uint16_t frame_flength; - uint32_t frame_vector; + +// Table 6-1: E1.31 Data Packet Framing Layer +typedef union { + struct { + uint16_t flength; + uint32_t vector; uint8_t source_name[64]; uint8_t priority; uint16_t reserved; uint8_t sequence_number; uint8_t options; uint16_t universe; + } __attribute__((packed)); + uint8_t raw[77]; +} e131_frame_t; - // DMP Layer - uint16_t dmp_flength; - uint8_t dmp_vector; + +// Table 7-1: E131 Data Packet DMP Layer +typedef union { + struct { + uint16_t flength; + uint8_t vector; uint8_t type; uint16_t first_address; uint16_t address_increment; uint16_t property_value_count; uint8_t property_values[513]; } __attribute__((packed)); + uint8_t raw[523]; +} e131_dmp_t; + +// Table 4-1: E1.31 Data Packet +typedef union { + struct { + e131_rlp_t rlp; + e131_frame_t frame; + e131_dmp_t dmp; + } __attribute__((packed)); uint8_t raw[638]; } e131_packet_t; -// Error Types -typedef enum { - ERROR_NONE, - ERROR_IGNORE, - ERROR_ACN_ID, - ERROR_PACKET_SIZE, - ERROR_VECTOR_ROOT, - ERROR_VECTOR_FRAME, - ERROR_VECTOR_DMP -} e131_error_t; - -// Status structure -typedef struct { - uint32_t num_packets; - uint32_t packet_errors; - IPAddress last_clientIP; - uint16_t last_clientPort; - unsigned long last_seen; -} e131_stats_t; - typedef std::function E131PacketHandlerFunction; -class ESPAsyncE131 { +class ESPsACN { public: - ESPAsyncE131(); + ESPsACN(); bool subscribe(uint16_t universe = 1); - static void dumpError(e131_packet_t *packet, e131_error_t error); static IPAddress E131MulticastAddress(uint16_t universe); Universe * universe(uint16_t num) { return universes_.at(num); } - e131_stats_t stats; // Statistics tracker - private: - // Constants for packet validation - static const uint8_t ACN_ID[]; - static const uint32_t VECTOR_ROOT = 4; - static const uint32_t VECTOR_FRAME = 2; - static const uint8_t VECTOR_DMP = 2; - e131_packet_t *buff_; // Pointer to scratch packet buffer AsyncUDP udp; // AsyncUDP @@ -139,4 +129,4 @@ class ESPAsyncE131 { std::unordered_map universes_; }; -#endif // ESPASYNCE131_H_ +#endif // SACN_H_ diff --git a/wiflash_esp32/wiflash_esp32.ino b/wiflash_esp32/wiflash_esp32.ino index 607864c..074fa11 100644 --- a/wiflash_esp32/wiflash_esp32.ino +++ b/wiflash_esp32/wiflash_esp32.ino @@ -43,7 +43,7 @@ //// device objects Strobe *strobe; NeoPixelBus *strip; -ESPAsyncE131 *e131 = new ESPAsyncE131(); +ESPsACN *e131 = new ESPsACN(); AsyncWebServer *httpd = new AsyncWebServer(80);