2019-11-18 20:43:27 +01:00
|
|
|
/*
|
|
|
|
* 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 "ESPAsyncE131.h"
|
2020-11-13 18:25:13 +01:00
|
|
|
#include "Network.h"
|
2019-11-18 20:43:27 +01:00
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
// E1.17 ACN Packet Identifier
|
|
|
|
const byte ESPAsyncE131::ACN_ID[12] = { 0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00 };
|
|
|
|
|
2020-04-13 00:42:27 +02:00
|
|
|
// Art-Net Packet Identifier
|
|
|
|
const byte ESPAsyncE131::ART_ID[8] = { 0x41, 0x72, 0x74, 0x2d, 0x4e, 0x65, 0x74, 0x00 };
|
|
|
|
|
2019-11-18 20:43:27 +01:00
|
|
|
// Constructor
|
|
|
|
ESPAsyncE131::ESPAsyncE131(e131_packet_callback_function callback) {
|
|
|
|
_callback = callback;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Public begin() members
|
|
|
|
//
|
|
|
|
/////////////////////////////////////////////////////////
|
|
|
|
|
2020-04-13 00:42:27 +02:00
|
|
|
bool ESPAsyncE131::begin(bool multicast, uint16_t port, uint16_t universe, uint8_t n) {
|
2020-09-28 16:29:01 +02:00
|
|
|
bool success = false;
|
2019-11-18 20:43:27 +01:00
|
|
|
|
2020-09-28 16:29:01 +02:00
|
|
|
if (multicast) {
|
2020-04-13 00:42:27 +02:00
|
|
|
success = initMulticast(port, universe, n);
|
|
|
|
} else {
|
2020-09-28 16:29:01 +02:00
|
|
|
success = initUnicast(port);
|
2020-04-13 00:42:27 +02:00
|
|
|
}
|
2019-11-18 20:43:27 +01:00
|
|
|
|
2020-09-28 16:29:01 +02:00
|
|
|
return success;
|
2019-11-18 20:43:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Private init() members
|
|
|
|
//
|
|
|
|
/////////////////////////////////////////////////////////
|
|
|
|
|
2020-04-13 00:42:27 +02:00
|
|
|
bool ESPAsyncE131::initUnicast(uint16_t port) {
|
2020-09-28 16:29:01 +02:00
|
|
|
bool success = false;
|
2019-11-18 20:43:27 +01:00
|
|
|
|
2020-09-28 16:29:01 +02:00
|
|
|
if (udp.listen(port)) {
|
|
|
|
udp.onPacket(std::bind(&ESPAsyncE131::parsePacket, this, std::placeholders::_1));
|
|
|
|
success = true;
|
|
|
|
}
|
|
|
|
return success;
|
2019-11-18 20:43:27 +01:00
|
|
|
}
|
|
|
|
|
2020-04-13 00:42:27 +02:00
|
|
|
bool ESPAsyncE131::initMulticast(uint16_t port, uint16_t universe, uint8_t n) {
|
2020-09-28 16:29:01 +02:00
|
|
|
bool success = false;
|
2019-11-18 20:43:27 +01:00
|
|
|
|
2020-09-28 16:29:01 +02:00
|
|
|
IPAddress address = IPAddress(239, 255, ((universe >> 8) & 0xff),
|
|
|
|
((universe >> 0) & 0xff));
|
2019-11-18 20:43:27 +01:00
|
|
|
|
2020-09-28 16:29:01 +02:00
|
|
|
if (udp.listenMulticast(address, port)) {
|
|
|
|
ip4_addr_t ifaddr;
|
|
|
|
ip4_addr_t multicast_addr;
|
2019-11-18 20:43:27 +01:00
|
|
|
|
2020-11-13 18:25:13 +01:00
|
|
|
ifaddr.addr = static_cast<uint32_t>(Network.localIP());
|
2020-09-28 16:29:01 +02:00
|
|
|
for (uint8_t i = 1; i < n; i++) {
|
|
|
|
multicast_addr.addr = static_cast<uint32_t>(IPAddress(239, 255,
|
|
|
|
(((universe + i) >> 8) & 0xff), (((universe + i) >> 0)
|
|
|
|
& 0xff)));
|
|
|
|
igmp_joingroup(&ifaddr, &multicast_addr);
|
|
|
|
}
|
2019-11-18 20:43:27 +01:00
|
|
|
|
2020-09-28 16:29:01 +02:00
|
|
|
udp.onPacket(std::bind(&ESPAsyncE131::parsePacket, this, std::placeholders::_1));
|
2019-11-18 20:43:27 +01:00
|
|
|
|
2020-09-28 16:29:01 +02:00
|
|
|
success = true;
|
|
|
|
}
|
|
|
|
return success;
|
2019-11-18 20:43:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Packet parsing - Private
|
|
|
|
//
|
|
|
|
/////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void ESPAsyncE131::parsePacket(AsyncUDPPacket _packet) {
|
2020-09-28 16:29:01 +02:00
|
|
|
bool error = false;
|
|
|
|
uint8_t protocol = P_E131;
|
2019-11-18 20:43:27 +01:00
|
|
|
|
2020-09-28 16:29:01 +02:00
|
|
|
sbuff = reinterpret_cast<e131_packet_t *>(_packet.data());
|
2020-04-13 00:42:27 +02:00
|
|
|
|
|
|
|
//E1.31 packet identifier ("ACS-E1.17")
|
2020-09-28 16:29:01 +02:00
|
|
|
if (memcmp(sbuff->acn_id, ESPAsyncE131::ACN_ID, sizeof(sbuff->acn_id)))
|
|
|
|
protocol = P_ARTNET;
|
2020-04-13 00:42:27 +02:00
|
|
|
|
2020-09-29 01:13:29 +02:00
|
|
|
if (protocol == P_ARTNET) {
|
2020-04-13 00:42:27 +02:00
|
|
|
if (memcmp(sbuff->art_id, ESPAsyncE131::ART_ID, sizeof(sbuff->art_id)))
|
|
|
|
error = true; //not "Art-Net"
|
|
|
|
if (sbuff->art_opcode != ARTNET_OPCODE_OPDMX)
|
|
|
|
error = true; //not a DMX packet
|
|
|
|
} else { //E1.31 error handling
|
|
|
|
if (htonl(sbuff->root_vector) != ESPAsyncE131::VECTOR_ROOT)
|
|
|
|
error = true;
|
|
|
|
if (htonl(sbuff->frame_vector) != ESPAsyncE131::VECTOR_FRAME)
|
|
|
|
error = true;
|
|
|
|
if (sbuff->dmp_vector != ESPAsyncE131::VECTOR_DMP)
|
|
|
|
error = true;
|
|
|
|
if (sbuff->property_values[0] != 0)
|
|
|
|
error = true;
|
2020-09-28 16:29:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (error && _packet.localPort() == DDP_DEFAULT_PORT) { //DDP packet
|
|
|
|
error = false;
|
|
|
|
protocol = P_DDP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!error) {
|
|
|
|
_callback(sbuff, _packet.remoteIP(), protocol);
|
|
|
|
}
|
2020-04-13 00:42:27 +02:00
|
|
|
}
|