rewrite the sACN class
This commit is contained in:
parent
c4cccc97e1
commit
9248104350
|
@ -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) |
|
||||
|
|
|
@ -363,11 +363,6 @@
|
|||
<td>Copyright © 2020 Kevin Matz</td>
|
||||
<td><a href="https//git.company235.com/kevin/WiFlasher/src/branch/master/LICENSE">MIT</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https//github.com/forkineye/ESPAsyncE131">ESPAsyncE131</a></td>
|
||||
<td>Copyright © 2019 Shelby Merrick</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https//github.com/Makuna/NeoPixelBus">NeoPixelBus</a></td>
|
||||
<td>Michael Miller</td>
|
||||
|
|
|
@ -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 <Arduino.h>
|
||||
|
@ -21,27 +27,23 @@
|
|||
#include <iterator>
|
||||
#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<e131_packet_t *>(_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<uint8_t, 513> 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<uint8_t, 513> 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));
|
||||
}
|
||||
|
|
|
@ -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 <AsyncUDP.h>
|
||||
#include <unordered_map>
|
||||
#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<void(e131_packet_t *packet)> 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 <uint16_t, Universe*> universes_;
|
||||
};
|
||||
|
||||
#endif // ESPASYNCE131_H_
|
||||
#endif // SACN_H_
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
//// device objects
|
||||
Strobe *strobe;
|
||||
NeoPixelBus<NeoGrbwFeature, Neo800KbpsMethod> *strip;
|
||||
ESPAsyncE131 *e131 = new ESPAsyncE131();
|
||||
ESPsACN *e131 = new ESPsACN();
|
||||
AsyncWebServer *httpd = new AsyncWebServer(80);
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue