105 lines
3.5 KiB
C++
105 lines
3.5 KiB
C++
/*
|
|
sacn.cpp
|
|
|
|
Copyright (c) 2020 Kevin Matz (kevin.matz@gmail.com)
|
|
|
|
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 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>
|
|
#include <algorithm>
|
|
#include <iterator>
|
|
#include "sacn.h"
|
|
|
|
|
|
ESPsACN::ESPsACN() {
|
|
udp.onPacket(std::bind(&ESPsACN::parsePacket, this,
|
|
std::placeholders::_1));
|
|
}
|
|
|
|
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),
|
|
ACN_SDT_MULTICAST_PORT);
|
|
|
|
Universe *u = new Universe();
|
|
universes_.emplace(num, u);
|
|
|
|
return success;
|
|
}
|
|
|
|
void ESPsACN::parsePacket(AsyncUDPPacket _packet) {
|
|
buff_ = reinterpret_cast<e131_packet_t *>(_packet.data());
|
|
|
|
// 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);
|
|
}
|
|
|
|
|
|
IPAddress ESPsACN::E131MulticastAddress(uint16_t universe) {
|
|
return IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff));
|
|
}
|