2020-12-01 19:40:58 -05:00
|
|
|
/*
|
|
|
|
ESPAsyncE131.cpp
|
2020-12-04 14:37:47 -05:00
|
|
|
|
2020-12-01 19:40:58 -05:00
|
|
|
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.
|
2020-12-04 14:37:47 -05:00
|
|
|
|
2020-12-01 19:40:58 -05:00
|
|
|
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 <Arduino.h>
|
2020-12-07 08:45:40 -05:00
|
|
|
#include <algorithm>
|
|
|
|
#include <iterator>
|
2020-12-01 19:40:58 -05:00
|
|
|
#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 };
|
|
|
|
|
2020-12-07 08:45:40 -05:00
|
|
|
ESPAsyncE131::ESPAsyncE131() {
|
2020-12-01 19:40:58 -05:00
|
|
|
stats.num_packets = 0;
|
|
|
|
stats.packet_errors = 0;
|
2020-12-04 14:42:41 -05:00
|
|
|
|
|
|
|
udp.onPacket(std::bind(&ESPAsyncE131::parsePacket, this,
|
2020-12-07 08:45:40 -05:00
|
|
|
std::placeholders::_1));
|
2020-12-01 19:40:58 -05:00
|
|
|
}
|
|
|
|
|
2020-12-07 08:45:40 -05:00
|
|
|
bool ESPAsyncE131::subscribe(uint16_t num, e131_listen_t type) {
|
2020-12-01 19:40:58 -05:00
|
|
|
bool success = false;
|
|
|
|
|
2020-12-08 17:23:26 -05:00
|
|
|
if (universes_.count(num)) {
|
|
|
|
// already subscribed
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-12-04 14:37:47 -05:00
|
|
|
switch (type) {
|
|
|
|
case E131_UNICAST:
|
|
|
|
Serial.println("Unicasting");
|
|
|
|
success = udp.listen(E131_DEFAULT_PORT);
|
|
|
|
break;
|
|
|
|
case E131_MULTICAST:
|
2020-12-07 08:45:40 -05:00
|
|
|
success = udp.listenMulticast(E131MulticastAddress(num),
|
2020-12-04 14:37:47 -05:00
|
|
|
E131_DEFAULT_PORT);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
2020-12-01 19:40:58 -05:00
|
|
|
}
|
2020-12-07 08:45:40 -05:00
|
|
|
|
|
|
|
Universe *u = new Universe();
|
|
|
|
universes_.emplace(num, u);
|
|
|
|
|
2020-12-01 19:40:58 -05:00
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ESPAsyncE131::parsePacket(AsyncUDPPacket _packet) {
|
|
|
|
e131_error_t error = ERROR_NONE;
|
|
|
|
|
2020-12-07 08:45:40 -05:00
|
|
|
buff_ = reinterpret_cast<e131_packet_t *>(_packet.data());
|
|
|
|
if (memcmp(buff_->acn_id, ESPAsyncE131::ACN_ID, sizeof(buff_->acn_id)))
|
2020-12-01 19:40:58 -05:00
|
|
|
error = ERROR_ACN_ID;
|
2020-12-07 08:45:40 -05:00
|
|
|
else if (htonl(buff_->root_vector) != ESPAsyncE131::VECTOR_ROOT)
|
2020-12-01 19:40:58 -05:00
|
|
|
error = ERROR_VECTOR_ROOT;
|
2020-12-07 08:45:40 -05:00
|
|
|
else if (htonl(buff_->frame_vector) != ESPAsyncE131::VECTOR_FRAME)
|
2020-12-01 19:40:58 -05:00
|
|
|
error = ERROR_VECTOR_FRAME;
|
2020-12-07 08:45:40 -05:00
|
|
|
else if (buff_->dmp_vector != ESPAsyncE131::VECTOR_DMP)
|
2020-12-01 19:40:58 -05:00
|
|
|
error = ERROR_VECTOR_DMP;
|
2020-12-07 08:45:40 -05:00
|
|
|
else if (buff_->property_values[0] != 0)
|
2020-12-01 19:40:58 -05:00
|
|
|
error = ERROR_IGNORE;
|
|
|
|
|
|
|
|
if (error == ERROR_NONE) {
|
|
|
|
stats.num_packets++;
|
|
|
|
stats.last_clientIP = _packet.remoteIP();
|
|
|
|
stats.last_clientPort = _packet.remotePort();
|
|
|
|
stats.last_seen = millis();
|
2020-12-07 08:45:40 -05:00
|
|
|
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);
|
2020-12-01 19:40:58 -05:00
|
|
|
}
|
|
|
|
} else if (error == ERROR_IGNORE) {
|
|
|
|
// Do nothing
|
|
|
|
} else {
|
|
|
|
if (Serial)
|
2020-12-07 08:45:40 -05:00
|
|
|
dumpError(buff_, error);
|
2020-12-01 19:40:58 -05:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-04 14:37:47 -05:00
|
|
|
IPAddress ESPAsyncE131::E131MulticastAddress(uint16_t universe) {
|
|
|
|
return IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff));
|
|
|
|
}
|