fully E1.17 ACN compliant receiver of E1.31 sACN data
This commit is contained in:
parent
7bc93797cb
commit
2e39a01a4c
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
acn.h
|
||||
|
||||
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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "pdu.h"
|
||||
#include "rlp.h"
|
||||
#include "rlp-udp.h"
|
||||
#include "sdt.h"
|
||||
#include "sdt-udp.h"
|
||||
#include "dmp.h"
|
||||
|
||||
// ANSI E1.17- 2015, Architecture for Control Networks
|
||||
namespace ACN {
|
||||
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
acn-dmp.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 "dmp.h"
|
||||
#include "rlp.h"
|
||||
|
||||
namespace ACN {
|
||||
namespace DMP {
|
||||
|
||||
address_type::address_type(uint8_t val) {
|
||||
z_reserved = (val >> 7) & 0b1;
|
||||
relative = (val >> 6) & 0b1;
|
||||
type = (data_type)((val >> 4) & 0b11);
|
||||
x_reserved = (val >> 2) & 0b11;
|
||||
width = (address_length)(val & 0b11);
|
||||
}
|
||||
|
||||
range::range(PDU::stream_ptr stream, data_type type, address_length length) {
|
||||
address = read(stream, length);
|
||||
if (type == SINGLE)
|
||||
return;
|
||||
incriment = read(stream, length);
|
||||
count = read(stream, length);
|
||||
}
|
||||
|
||||
uint32_t range::read(PDU::stream_ptr stream, address_length length) {
|
||||
switch (length) {
|
||||
case ONE:
|
||||
return PDU::read8(stream);
|
||||
case TWO:
|
||||
return PDU::read16(stream);
|
||||
case FOUR:
|
||||
return PDU::read32(stream);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void readHeader(PDU::pdu_ptr pdu) {
|
||||
if (pdu->flags().hasHeader) {
|
||||
address_type * header = new address_type(pdu->buffer()->get());
|
||||
pdu->setHeader(header);
|
||||
}
|
||||
}
|
||||
|
||||
void readSet(PDU::pdu_ptr pdu) {
|
||||
const address_type *header = static_cast<const address_type*>(pdu->header());
|
||||
|
||||
PDU::stream_ptr stream = pdu->buffer();
|
||||
|
||||
dmp_set_data * data = new dmp_set_data();
|
||||
while(stream->good()) {
|
||||
// Property Address
|
||||
range pr(stream, header->type, header->width);
|
||||
|
||||
// Property Data
|
||||
std::vector<uint8_t> pd;
|
||||
pd.resize(pr.address, 0);
|
||||
for (uint32_t i = 0; i < pr.count * pr.incriment; i ++)
|
||||
pd.push_back(stream->get());
|
||||
|
||||
// Property Fields
|
||||
set_property fields(pr, pd);
|
||||
data->properties.push_back(fields);
|
||||
|
||||
// set EOF if buffer is drained
|
||||
if (stream->available() == 0)
|
||||
stream->setstate(stream->rdstate() | std::ios_base::eofbit);
|
||||
}
|
||||
pdu->setData(data);
|
||||
}
|
||||
|
||||
} // DMP
|
||||
} // ACN
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
acn-dmp.h
|
||||
|
||||
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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
#include "pdu.h"
|
||||
|
||||
// Architecture for Control Networks – Device Management Protocol
|
||||
namespace ACN {
|
||||
namespace DMP {
|
||||
|
||||
// 5.1.4 Address and Data Types
|
||||
enum data_type {
|
||||
SINGLE = 0b00, // 0
|
||||
RANGE = 0b01, // 1
|
||||
ARRAY = 0b10, // 2
|
||||
SERIES = 0b11 // 3
|
||||
};
|
||||
|
||||
enum address_length {
|
||||
ONE = 0b00, // 0
|
||||
TWO = 0b01, // 1
|
||||
FOUR = 0b10, // 2
|
||||
ZERO = 0b11 // 3 (reserved)
|
||||
};
|
||||
|
||||
struct address_type : PDU::pdu_header {
|
||||
bool z_reserved : 1; // Z
|
||||
bool relative : 1; // R
|
||||
data_type type : 2; // D1, D0
|
||||
uint8_t x_reserved : 2; // X1, X0
|
||||
address_length width : 2; // A1, A0
|
||||
address_type(uint8_t);
|
||||
};
|
||||
|
||||
// 5.1.5
|
||||
struct range {
|
||||
uint32_t address;
|
||||
uint32_t incriment;
|
||||
uint32_t count;
|
||||
range() {};
|
||||
range(PDU::stream_ptr, data_type, address_length);
|
||||
private:
|
||||
uint32_t read(PDU::stream_ptr, address_length);
|
||||
};
|
||||
|
||||
typedef std::pair<range, std::vector<uint8_t>> set_property;
|
||||
struct dmp_set_data : PDU::pdu_data {
|
||||
std::vector<set_property> properties;
|
||||
};
|
||||
|
||||
// 7 Response Messages
|
||||
enum failure_reason {
|
||||
NONSPECIFIC = 1, // Non-specific or non-DMP reason.
|
||||
NOT_PROPERTY = 2, // The address does not correspond to a property.
|
||||
WRITE_ONLY = 3, // The property’s value may not be read.
|
||||
NOT_WRITABLE = 4, // The property’s value may not be written.
|
||||
DATA_ERROR = 5, // The data does not correspond to the property.
|
||||
SUBSCIRPTION_NOT_SUPPORTED = 10, // Subscriptions on the specified property are not supported by the device.
|
||||
NO_SUBSCRIPTIONS_SUPPORTED = 11, // Subscriptions not supported on any property.
|
||||
INSUFFICIENT_RESOURCES = 12, // The component cannot support more subscriptions due to resource limitations
|
||||
UNAVAILABLE = 13 // The property’s value is not available due to restrictions imposed by device specific functionality (e.g., access permission mechanisms).
|
||||
};
|
||||
|
||||
// 13.1 Protocol Codes
|
||||
static const uint32_t DMP_PROTOCOL_ID = 2; // PDU protocol value
|
||||
|
||||
// 13.2 Message Codes
|
||||
static const uint8_t GET_PROPERTY = 1;
|
||||
static const uint8_t SET_PROPERTY = 2;
|
||||
static const uint8_t GET_PROPERTY_REPLY = 3;
|
||||
static const uint8_t EVENT = 4;
|
||||
static const uint8_t SUBSCRIBE = 7;
|
||||
static const uint8_t UNSUBSCRIBE = 8;
|
||||
static const uint8_t GET_PROPERTY_FAIL = 9;
|
||||
static const uint8_t SET_PROPERTY_FAIL = 10;
|
||||
static const uint8_t SUBSCRIBE_ACCEPT = 12;
|
||||
static const uint8_t SUBSCRIBE_REJECT = 13;
|
||||
static const uint8_t SYNC_EVENT = 17;
|
||||
|
||||
void readHeader(PDU::pdu_ptr);
|
||||
void readSet(PDU::pdu_ptr);
|
||||
inline PDU::block_ptr readBlock(PDU::stream_ptr s, uint8_t vl = 1) {
|
||||
return PDU::readBlock(s, vl);
|
||||
}
|
||||
|
||||
} // DMP
|
||||
} // ACN
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
acn-pdu.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 "pdu.h"
|
||||
#include <memory>
|
||||
|
||||
namespace ACN {
|
||||
namespace PDU {
|
||||
|
||||
|
||||
pdu_flags::pdu_flags(uint8_t val) {
|
||||
hasLength = (val >> 7) & 0b1;
|
||||
hasVector = (val >> 6) & 0b1;
|
||||
hasHeader = (val >> 5) & 0b1;
|
||||
hasData = (val >> 4) & 0b1;
|
||||
};
|
||||
|
||||
Pdu::Pdu(stream_ptr stream, size_t vector_size)
|
||||
: flags_(stream->peek())
|
||||
{
|
||||
header_ = 0;
|
||||
data_ = 0;
|
||||
parent_ = 0;
|
||||
inherit_ = 0; // pointer to previous PDU in block
|
||||
|
||||
// read length and vector off of the stream
|
||||
length_ = readLength(stream, flags_.hasLength);
|
||||
if (flags_.hasVector)
|
||||
vector_ = readVector(stream, vector_size);
|
||||
|
||||
// ESP_LOGD(TAG, "length: %d vector: %d", length_, vector_);
|
||||
|
||||
// length includes the flags, length, and vector
|
||||
// calculate the remaining lenght of the PDU
|
||||
int len = length_;
|
||||
len -= flags_.hasLength ? 3 : 2;
|
||||
len -= vector_size;
|
||||
|
||||
// abort if the remaining PDU length isn't available
|
||||
if (!stream->good() || len > stream->available()) {
|
||||
stream->setstate(stream->rdstate() | std::ios_base::failbit);
|
||||
return;
|
||||
}
|
||||
|
||||
// create a stream buffer for the header and data
|
||||
uint8_t buf[len];
|
||||
stream->read(buf, len);
|
||||
buffer_ = stream_ptr(new pdu_stream(buf, len));
|
||||
}
|
||||
|
||||
Pdu::~Pdu() {
|
||||
if (header_) delete header_;
|
||||
if (data_) delete data_;
|
||||
}
|
||||
|
||||
|
||||
const uint32_t Pdu::vector() {
|
||||
if (flags_.hasVector)
|
||||
return vector_;
|
||||
if (inherit_ != nullptr)
|
||||
return inherit_->vector();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
pdu_header * Pdu::header() {
|
||||
if (flags_.hasHeader)
|
||||
return header_;
|
||||
if (inherit_ != nullptr)
|
||||
return inherit_->header();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
pdu_data * Pdu::data() {
|
||||
if (flags_.hasData)
|
||||
return data_;
|
||||
if (inherit_ != nullptr)
|
||||
return inherit_->data();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
block_ptr readBlock(stream_ptr stream, uint8_t vector_size) {
|
||||
block_ptr block(new pdu_block());
|
||||
while(stream->good()) {
|
||||
// create a PDU off of the stream
|
||||
pdu_ptr pdu(new Pdu(stream, vector_size));
|
||||
if (stream->fail()) break; // OK if eofbit is set
|
||||
|
||||
// set inheritor pointer
|
||||
if (!block->empty()) pdu->setInherit(block->back());
|
||||
|
||||
// add the PDU to the block list
|
||||
block->push_back(pdu);
|
||||
|
||||
// set EOF if buffer is drained
|
||||
if (stream->available() == 0)
|
||||
stream->setstate(stream->rdstate() | std::ios_base::eofbit);
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
uint32_t readLength(stream_ptr stream, bool hasLength) {
|
||||
uint32_t length = 0;
|
||||
length |= (stream->get() & 0x0f) << 16;
|
||||
length |= stream->get() << 8;
|
||||
if (hasLength)
|
||||
length |= stream->get();
|
||||
else
|
||||
length = length >> 8;
|
||||
return length;
|
||||
}
|
||||
|
||||
uint32_t readVector(stream_ptr stream, uint8_t vector_size) {
|
||||
uint32_t vect = 0;
|
||||
for (int o = vector_size - 1; o >= 0; o--) {
|
||||
vect |= (stream->get() << (8 * o));
|
||||
}
|
||||
return vect;
|
||||
}
|
||||
|
||||
uint8_t read8(stream_ptr stream) {
|
||||
return stream->get();
|
||||
}
|
||||
|
||||
uint16_t read16(stream_ptr stream) {
|
||||
uint16_t ret = 0;
|
||||
ret |= stream->get() << 8;
|
||||
ret |= stream->get();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t read32(stream_ptr stream) {
|
||||
uint32_t ret = 0;
|
||||
ret |= stream->get() << 24;
|
||||
ret |= stream->get() << 16;
|
||||
ret |= stream->get() << 6;
|
||||
ret |= stream->get();
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // PDU
|
||||
} // ACN
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
acn-pdu.h
|
||||
|
||||
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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
#include <istream>
|
||||
#include <memory>
|
||||
|
||||
namespace ACN {
|
||||
namespace PDU {
|
||||
|
||||
// 2.4.1. Flags
|
||||
// Flags is a 4-bit field containing flags L, V, H and D which declare how the PDU is packed.
|
||||
struct pdu_flags {
|
||||
bool hasLength : 1;
|
||||
bool hasVector : 1;
|
||||
bool hasHeader : 1;
|
||||
bool hasData : 1;
|
||||
pdu_flags(uint8_t);
|
||||
};
|
||||
|
||||
class pdu_stream;
|
||||
class Pdu;
|
||||
typedef std::shared_ptr<pdu_stream> stream_ptr;
|
||||
typedef std::shared_ptr<Pdu> pdu_ptr;
|
||||
typedef std::vector<pdu_ptr> pdu_block;
|
||||
typedef std::shared_ptr<pdu_block> block_ptr;
|
||||
|
||||
// MAYBE: remove virtuals?
|
||||
// Arduino doen't enable RTTI for run-time polymorphism.
|
||||
struct pdu_header { virtual ~pdu_header() {} };
|
||||
struct pdu_data { virtual ~pdu_data() {} };
|
||||
|
||||
/*
|
||||
pdu_buffer and pdu_stream hold the raw data of the packet
|
||||
*/
|
||||
class pdu_buffer
|
||||
: public std::basic_streambuf<uint8_t>
|
||||
{
|
||||
public:
|
||||
pdu_buffer(uint8_t * p, size_t l) { setg(p, p, p + l); }
|
||||
};
|
||||
|
||||
class pdu_stream
|
||||
: public std::basic_istream<uint8_t>
|
||||
{
|
||||
public:
|
||||
pdu_stream(uint8_t * p, size_t l)
|
||||
: std::basic_istream<uint8_t>(&_buffer)
|
||||
, _buffer(p, l) { rdbuf(&_buffer); }
|
||||
std::streamsize available() { return _buffer.in_avail(); }
|
||||
private:
|
||||
pdu_buffer _buffer;
|
||||
};
|
||||
|
||||
class Pdu {
|
||||
public:
|
||||
Pdu(stream_ptr, size_t vector_size);
|
||||
~Pdu();
|
||||
|
||||
// getters
|
||||
const pdu_flags flags() {return flags_;}
|
||||
const uint32_t length() {return length_;}
|
||||
const uint32_t vector();
|
||||
pdu_header * header();
|
||||
pdu_data * data();
|
||||
pdu_ptr parent() {return parent_;}
|
||||
stream_ptr buffer() {return buffer_;}
|
||||
|
||||
// setters
|
||||
void setHeader (pdu_header * h) {header_ = h;}
|
||||
void setData (pdu_data * d) {data_ = d;}
|
||||
void setParent (pdu_ptr pdu) {parent_ = pdu;}
|
||||
void setInherit(pdu_ptr pdu) {inherit_ = pdu;}
|
||||
|
||||
private:
|
||||
pdu_flags flags_;
|
||||
uint32_t length_;
|
||||
uint32_t vector_;
|
||||
pdu_header * header_;
|
||||
pdu_data * data_;
|
||||
pdu_ptr parent_;
|
||||
pdu_ptr inherit_;
|
||||
stream_ptr buffer_;
|
||||
};
|
||||
|
||||
|
||||
// utility functions to constuct the PDU from input stream
|
||||
block_ptr readBlock (stream_ptr, uint8_t vector_length = 4);
|
||||
uint32_t readLength(stream_ptr, bool hasLength);
|
||||
uint32_t readVector(stream_ptr, uint8_t);
|
||||
uint8_t read8 (stream_ptr);
|
||||
uint16_t read16(stream_ptr);
|
||||
uint32_t read32(stream_ptr);
|
||||
|
||||
} // PDU
|
||||
} // ACN
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
acn-rlp-udp.h
|
||||
|
||||
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 "rlp-udp.h"
|
||||
#include "sdt-udp.h"
|
||||
#include <cstring>
|
||||
|
||||
namespace ACN {
|
||||
namespace RLP {
|
||||
namespace UDP {
|
||||
|
||||
preamble_t::preamble_t(PDU::stream_ptr stream) {
|
||||
length = PDU::read16(stream);
|
||||
postamble_size = PDU::read16(stream);
|
||||
stream->read(acn_id, 12);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Validate compliance with ACN EPI 17: ACN Root Layer Protocol on UDP
|
||||
4.2. Reception
|
||||
*/
|
||||
preamble_t::operator bool () {
|
||||
// 2. Preamble Format: The preamble size includes both size fields so the
|
||||
// minimum value for preamble size is 16 (octets).
|
||||
if (length < PREAMBLE_MINIMUM_SIZE)
|
||||
return false;
|
||||
|
||||
// 2. Preamble Format: The ACN Packet Identifier shall be the text string
|
||||
// “ASC-E1.17\0\0\0” encoded in [ASCII].
|
||||
if (memcmp(acn_id, ACN_PACKET_IDENTIFIER, 12))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Process ACN packet
|
||||
*/
|
||||
PDU::block_ptr readBlock(PDU::stream_ptr packet) {
|
||||
// std::streamoff origin = packet->tellg();
|
||||
preamble_t preamble(packet);
|
||||
if (!preamble)
|
||||
packet->setstate(std::ios_base::failbit);
|
||||
|
||||
// Preamble length has a standard min, but no max. Discard any extra.
|
||||
std::streamoff extra_preamble = preamble.length - PREAMBLE_MINIMUM_SIZE;
|
||||
for (int i = 0; i < extra_preamble; i++)
|
||||
packet->get();
|
||||
|
||||
return PDU::readBlock(packet);
|
||||
}
|
||||
|
||||
} // UDP
|
||||
} // RLP
|
||||
} // ACN
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
acn-rlp-udp.h
|
||||
|
||||
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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "rlp.h"
|
||||
#include "pdu.h"
|
||||
|
||||
// ACN EPI 17. ACN Root Layer Protocol Operation on UDP
|
||||
|
||||
namespace ACN {
|
||||
namespace RLP {
|
||||
namespace UDP {
|
||||
|
||||
// The ACN Packet Identifier shall be the text string
|
||||
// “ASC-E1.17\0\0\0” encoded in [ASCII].
|
||||
static constexpr uint8_t ACN_PACKET_IDENTIFIER[] = { 0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00 };
|
||||
|
||||
// 2. Preamble Format: The preamble size includes both size fields so the
|
||||
// minimum value for preamble size is 16 (octets).
|
||||
static const uint8_t PREAMBLE_MINIMUM_SIZE = 16;
|
||||
|
||||
// 2. Preamble Format
|
||||
struct preamble_t {
|
||||
uint16_t length;
|
||||
uint16_t postamble_size;
|
||||
uint8_t acn_id[12];
|
||||
preamble_t(PDU::stream_ptr);
|
||||
operator bool();
|
||||
};
|
||||
|
||||
|
||||
PDU::block_ptr readBlock(PDU::stream_ptr);
|
||||
|
||||
} // UDP
|
||||
} // RLP
|
||||
} // ACN
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
acn-rlp.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 "rlp.h"
|
||||
|
||||
namespace ACN {
|
||||
namespace RLP {
|
||||
|
||||
rlp_header::rlp_header(PDU::stream_ptr stream)
|
||||
: pdu_header()
|
||||
{
|
||||
if (stream->available() > (long int)sizeof(cid))
|
||||
stream->read(cid, sizeof(cid));
|
||||
}
|
||||
|
||||
void readHeader(PDU::pdu_ptr pdu) {
|
||||
if (pdu->flags().hasHeader) {
|
||||
rlp_header * header = new rlp_header(pdu->buffer());
|
||||
pdu->setHeader(header);
|
||||
}
|
||||
}
|
||||
|
||||
} // RLP
|
||||
} // ACN
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
acn-rlp.h
|
||||
|
||||
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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "pdu.h"
|
||||
#include <stdint.h>
|
||||
|
||||
namespace ACN {
|
||||
namespace RLP {
|
||||
|
||||
// 2.6.1.2.2. Header Field in Root Layer PDUs
|
||||
// The Header field in Root Layer PDUs shall contain the CID of the component
|
||||
// that generated the PDU (the Source CID).
|
||||
struct rlp_header : PDU::pdu_header {
|
||||
uint8_t cid[16];
|
||||
rlp_header(PDU::stream_ptr);
|
||||
};
|
||||
|
||||
void readHeader(PDU::pdu_ptr);
|
||||
|
||||
} // RLP
|
||||
} // ACN
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
acn-sdt-udp.h
|
||||
|
||||
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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "sdt.h"
|
||||
|
||||
// ACN EPI 17. Operation of SDT on UDP Networks
|
||||
namespace ACN {
|
||||
namespace SDT {
|
||||
namespace UDP {
|
||||
|
||||
// Table 1. IPv4 Address Specification
|
||||
struct acn_sdt_udp_ipv4_addr_t {
|
||||
ip_addr_spec_t type; // SDT_ADDR_IPV4
|
||||
uint16_t udp_port;
|
||||
uint8_t ipv4[4];
|
||||
};
|
||||
|
||||
// Table 2. IPv6 Address Specification
|
||||
struct acn_sdt_udp_ipv6_addr_t {
|
||||
ip_addr_spec_t type; // SDT_ADDR_IPV6
|
||||
uint16_t udp_port;
|
||||
uint8_t ipv4[16];
|
||||
};
|
||||
|
||||
// Table 3. SDT symbolic parameters
|
||||
// static const float MAK_TIMEOUT_FACTOR = 0.1;
|
||||
// static const uint8_t MAK_MAX_RETRIES = 2; // (3 tries total)
|
||||
// static const uint8_t AD_HOC_TIMEOUT = 200; // ms
|
||||
// static const uint8_t AD_HOC_RETRIES = 2; // (3 tries total)
|
||||
// static const float RECIPROCAL_TIMEOUT_FACTOR = 0.2;
|
||||
// static const uint8_t MIN_EXPIRY_TIME = 2; // s
|
||||
// static const float NAK_TIMEOUT_FACTOR = 0.1;
|
||||
// static const uint8_t NAK_MAX_RETRIES = 2; // (3 tries total)
|
||||
// static const uint8_t NAK_HOLDOFF_INTERVAL = 2; // ms
|
||||
// static const uint8_t NAK_MAX_TIME = 10 * NAK_HOLDOFF_INTERVAL;
|
||||
// static const uint8_t NAK_BLANKTIME = 3 * NAK_HOLDOFF_INTERVAL;
|
||||
// static const uint16_t SDT_MULTICAST_PORT = 5568; // IANA registered “sdt”
|
||||
|
||||
} // UDP
|
||||
} // SDT
|
||||
} // ACN
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
acn-sdt.h
|
||||
|
||||
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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "pdu.h"
|
||||
|
||||
// ANSI E1.17- 2015, Architecture for Control Networks–
|
||||
// Session Data Transport Protocol
|
||||
namespace ACN {
|
||||
namespace SDT {
|
||||
|
||||
// 7.1 Protocol Code
|
||||
static const uint32_t SDT_PROTOCOL_ID = 1; // PDU protocol value
|
||||
|
||||
// 7.2 PDU Vector Codes
|
||||
enum sdt_vector_t {
|
||||
REL_WRAP = 1,
|
||||
UNREL_WRAP = 2,
|
||||
CHANNEL_PARAMS = 3,
|
||||
JOIN = 4,
|
||||
JOIN_REFUSE = 5,
|
||||
JOIN_ACCEPT = 6,
|
||||
LEAVE = 7,
|
||||
LEAVING = 8,
|
||||
CONNECT = 9,
|
||||
CONNECT_ACCEPT = 10,
|
||||
CONNECT_REFUSE = 11,
|
||||
DISCONNECT = 12,
|
||||
DISCONNECTING = 13,
|
||||
ACK = 14,
|
||||
NAK = 15,
|
||||
GET_SESSIONS = 16,
|
||||
SESSIONS = 17,
|
||||
};
|
||||
|
||||
// Table 7: Address Specification Types
|
||||
enum ip_addr_spec_t {
|
||||
SDT_ADDR_NULL = 0,
|
||||
SDT_ADDR_IPV4 = 1,
|
||||
SDT_ADDR_IPV6 = 2
|
||||
};
|
||||
|
||||
} // SDT
|
||||
} // ACN
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
dmx_universe.cpp
|
||||
universe.cpp
|
||||
|
||||
Copyright (c) 2020 Kevin Matz (kevin.matz@gmail.com)
|
||||
|
||||
|
@ -22,53 +22,34 @@
|
|||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "universe.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "dmx_universe.h"
|
||||
|
||||
namespace DMX {
|
||||
/*
|
||||
Constructor
|
||||
accept new data from receiver
|
||||
*/
|
||||
Universe::Universe() {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
recieve new data from nodeS
|
||||
*/
|
||||
bool Universe::setData(e111_universe_t *src) {
|
||||
switch (src->start_code) {
|
||||
void Universe::set(std::vector<uint8_t> vect) {
|
||||
// full length universe is 513, including start code
|
||||
// truncate larger data and pad shorter data with 0
|
||||
vect.resize(513, 0);
|
||||
switch (vect.front()) // start code
|
||||
{
|
||||
case E111_NULL_START:
|
||||
std::copy(std::begin(src->data), std::end(src->data), std::begin(data_.data));
|
||||
time_ = millis();
|
||||
std::copy(vect.begin(), vect.end(), null_start_data_.begin());
|
||||
for (const auto &cb : callbacks_)
|
||||
cb(this);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
for (const auto &cb : callbacks_) {
|
||||
cb(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
register a data consumer callback function
|
||||
*/
|
||||
void Universe::onData(const E111DataHandlerFunction callback)
|
||||
void Universe::onData(const DataHandlerFunction callback)
|
||||
{
|
||||
callbacks_.push_back(callback);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
milliseconds since last update
|
||||
*/
|
||||
uint32_t Universe::age() {
|
||||
uint32_t now = millis();
|
||||
if (now > time_) {
|
||||
return now - time_;
|
||||
}
|
||||
// millis values overflow after approximately 50 days.
|
||||
return (2 ^ 32 - time_) + now;
|
||||
}
|
||||
} // DMX
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
dmx_universe.h
|
||||
universe.h
|
||||
|
||||
Copyright (c) 2020 Kevin Matz (kevin.matz@gmail.com)
|
||||
|
||||
|
@ -23,37 +23,27 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <stdint.h>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
|
||||
#define E111_NULL_START 0
|
||||
namespace DMX {
|
||||
|
||||
// Table D1 - Reserved START Codes
|
||||
#define E111_ASC_TEXT_ASCII 23
|
||||
#define E111_ASC_TEST 85
|
||||
#define E111_ASC_TEXT_UTF8 144
|
||||
#define E111_ASC_MANUFACTURER 145
|
||||
#define E111_ASC_SIP 207
|
||||
static const uint8_t E111_NULL_START = 0;
|
||||
static const uint8_t E111_ASC_TEXT_ASCII = 23;
|
||||
static const uint8_t E111_ASC_TEST = 85;
|
||||
static const uint8_t E111_ASC_TEXT_UTF8 = 144;
|
||||
static const uint8_t E111_ASC_MANUFACTURER = 145;
|
||||
static const uint8_t E111_ASC_SIP = 207;
|
||||
|
||||
|
||||
// structure of DMX data
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t start_code;
|
||||
uint8_t slot[512]; // 0 index-origin!
|
||||
} __attribute__((packed));
|
||||
uint8_t data[513];
|
||||
} e111_universe_t;
|
||||
|
||||
|
||||
// forward declare the Univserse class
|
||||
class Universe;
|
||||
|
||||
// register type for data users to subscribe callbacks
|
||||
typedef std::function<void(Universe *)> E111DataHandlerFunction;
|
||||
class Universe; // forward declare the Univserse class
|
||||
typedef std::function<void(Universe *)> DataHandlerFunction;
|
||||
|
||||
// convience declare the type of null_start_data_
|
||||
typedef std::array<uint8_t, 513> null_start_data_t;
|
||||
|
||||
/*
|
||||
The Universe class
|
||||
|
@ -64,17 +54,14 @@ typedef std::function<void(Universe *)> E111DataHandlerFunction;
|
|||
*/
|
||||
class Universe {
|
||||
public:
|
||||
Universe();
|
||||
|
||||
e111_universe_t * data() {
|
||||
return &data_;
|
||||
}
|
||||
bool setData(e111_universe_t *data);
|
||||
void onData(const E111DataHandlerFunction callback);
|
||||
uint32_t age(); // millis() since setData
|
||||
null_start_data_t * data() { return &null_start_data_; }
|
||||
void onData (const DataHandlerFunction callback);
|
||||
void set (std::vector<uint8_t>);
|
||||
uint8_t slot (uint16_t address) { return null_start_data_[address]; }
|
||||
|
||||
private:
|
||||
e111_universe_t data_;
|
||||
uint32_t time_;
|
||||
std::vector<E111DataHandlerFunction> callbacks_;
|
||||
null_start_data_t null_start_data_;
|
||||
std::vector<DataHandlerFunction> callbacks_;
|
||||
};
|
||||
|
||||
} // DMX
|
|
@ -56,13 +56,10 @@ uint32_t AbstractStrobe::dmxDuration(uint8_t dmx) {
|
|||
/*
|
||||
Load data into profile from new DMX
|
||||
*/
|
||||
void AbstractStrobe::recvData(Universe * univ) {
|
||||
if ( sizeof(profile_.dmx) + address_ > sizeof(univ->data()->data)) {
|
||||
// address is higher than allowable
|
||||
return;
|
||||
}
|
||||
std::copy(univ->data()->data + address_,
|
||||
univ->data()->data + address_ + sizeof(profile_.dmx),
|
||||
void AbstractStrobe::recvData(DMX::Universe * univ) {
|
||||
// copy data from universe to profile
|
||||
std::copy(univ->data()->begin() + address_,
|
||||
univ->data()->begin() + address_ + sizeof(profile_.dmx),
|
||||
std::begin(profile_.dmx));
|
||||
|
||||
// reorder multibyte profile values (changes profile_.dmx)
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include "AbstractFixture.h"
|
||||
#include "../sacn/dmx_universe.h"
|
||||
#include "../dmx/universe.h"
|
||||
|
||||
typedef union {
|
||||
uint8_t dmx[4];
|
||||
|
@ -50,7 +50,7 @@ class AbstractStrobe
|
|||
public:
|
||||
AbstractStrobe(uint16_t address);
|
||||
|
||||
void recvData(Universe *); // data recieved callback
|
||||
void recvData(DMX::Universe *); // data recieved callback
|
||||
|
||||
protected:
|
||||
strobe_state_t state_; // state machine cycle state
|
||||
|
|
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
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 "ESPsACN.h"
|
||||
|
||||
// ESP logging module
|
||||
#include "esp_log.h"
|
||||
static const char* TAG = "ESPsACN";
|
||||
|
||||
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));
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
/*
|
||||
sacn.h
|
||||
|
||||
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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AsyncUDP.h>
|
||||
#include <unordered_map>
|
||||
#include "dmx_universe.h"
|
||||
|
||||
// Appendix A: Defined Parameters (Normative)
|
||||
#define VECTOR_ROOT_E131_DATA 0x00000004
|
||||
#define VECTOR_ROOT_E131_EXTENDED 0x00000008
|
||||
|
||||
#define VECTOR_DMP_SET_PROPERTY 0x02 // informative
|
||||
|
||||
#define VECTOR_E131_DATA_PACKET 0x00000002
|
||||
|
||||
#define VECTOR_E131_EXTENDED_SYNCHRONIZATION 0x00000001
|
||||
#define VECTOR_E131_EXTENDED_DISCOVERY 0x00000002
|
||||
|
||||
#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 {
|
||||
uint16_t preamble_size;
|
||||
uint16_t postamble_size;
|
||||
uint8_t acn_id[12];
|
||||
uint16_t flength;
|
||||
uint32_t vector;
|
||||
uint8_t cid[16];
|
||||
} __attribute__((packed));
|
||||
uint8_t raw[37];
|
||||
} e131_rlp_t;
|
||||
|
||||
|
||||
// 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;
|
||||
|
||||
|
||||
// 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;
|
||||
|
||||
|
||||
typedef std::function<void(e131_packet_t *packet)> E131PacketHandlerFunction;
|
||||
|
||||
|
||||
class ESPsACN {
|
||||
public:
|
||||
ESPsACN();
|
||||
|
||||
bool subscribe(uint16_t universe = 1);
|
||||
static IPAddress E131MulticastAddress(uint16_t universe);
|
||||
Universe * universe(uint16_t num) {
|
||||
return universes_.at(num);
|
||||
}
|
||||
|
||||
private:
|
||||
e131_packet_t *buff_; // Pointer to scratch packet buffer
|
||||
AsyncUDP udp; // AsyncUDP
|
||||
|
||||
// UDP packet parser callback
|
||||
void parsePacket(AsyncUDPPacket);
|
||||
|
||||
std::unordered_map <uint16_t, Universe*> universes_;
|
||||
};
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
data.h
|
||||
|
||||
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 "data.h"
|
||||
#include "receiver.h"
|
||||
|
||||
namespace SACN {
|
||||
namespace DATA {
|
||||
using namespace ACN;
|
||||
|
||||
frame_header::frame_header(PDU::stream_ptr stream) {
|
||||
stream->read(source_name, 64);
|
||||
priority = PDU::read8(stream);
|
||||
reserved = PDU::read16(stream);
|
||||
sequence_number = PDU::read8(stream);
|
||||
options = PDU::read8(stream);
|
||||
universe = PDU::read16(stream);
|
||||
}
|
||||
|
||||
void readHeader(PDU::pdu_ptr pdu) {
|
||||
if (pdu->flags().hasHeader) {
|
||||
frame_header * header = new frame_header(pdu->buffer());
|
||||
pdu->setHeader(header);
|
||||
}
|
||||
}
|
||||
|
||||
} // DATA
|
||||
} // SACN
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
data.h
|
||||
|
||||
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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "sacn.h"
|
||||
#include "receiver.h"
|
||||
|
||||
namespace SACN {
|
||||
namespace DATA {
|
||||
using namespace ACN;
|
||||
|
||||
// Table 6-1: E1.31 Data Packet Framing Layer
|
||||
struct frame_header : PDU::pdu_header {
|
||||
uint8_t source_name[64];
|
||||
uint8_t priority;
|
||||
uint16_t reserved;
|
||||
uint8_t sequence_number;
|
||||
uint8_t options;
|
||||
uint16_t universe;
|
||||
frame_header(PDU::stream_ptr);
|
||||
};
|
||||
|
||||
// 6.2.6 E1.31 Data Packet: Options
|
||||
enum options_t : uint8_t {
|
||||
PREVIEW_DATA = 0b10000000, // Bit 7 = Preview_Data
|
||||
STREAM_TERMINATED = 0b01000000, // Bit 6 = Stream_Terminated
|
||||
FORCE_SYNCHRONIZATION = 0b00100000, // Bit 5 = Force_Synchronization
|
||||
};
|
||||
|
||||
void readHeader(PDU::pdu_ptr);
|
||||
|
||||
} // DATA
|
||||
} // SACN
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
extended.h
|
||||
|
||||
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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "sacn.h"
|
||||
#include "receiver.h"
|
||||
|
||||
namespace SACN {
|
||||
namespace EXTENDED {
|
||||
using namespace ACN;
|
||||
|
||||
// 6.3 E1.31 Synchronization Packet Framing Layer
|
||||
struct frame_sync_header : PDU::pdu_header {
|
||||
uint8_t sequence_number;
|
||||
uint16_t sync_address;
|
||||
uint8_t reserved[2];
|
||||
};
|
||||
|
||||
|
||||
// 6.4 E1.31 Universe Discovery Packet Framing Layer
|
||||
struct frame_discovery_header : PDU::pdu_header {
|
||||
uint8_t source_name[64];
|
||||
uint8_t reserved[4];
|
||||
};
|
||||
|
||||
|
||||
// Table 8-9: E1.31 Universe Discovery Packet Universe Discovery Layer
|
||||
struct discovery_list_header : PDU::pdu_header {
|
||||
uint8_t page;
|
||||
uint8_t last_page;
|
||||
};
|
||||
|
||||
} // EXTENDED
|
||||
} // SACN
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
receiver-esp.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 "receiver-esp.h"
|
||||
|
||||
// ESP logging module
|
||||
#include "esp_log.h"
|
||||
static const char* TAG = "ESPsACN";
|
||||
|
||||
using namespace SACN;
|
||||
|
||||
EspReceiver::EspReceiver()
|
||||
: Receiver()
|
||||
{
|
||||
udp.onPacket(std::bind(&EspReceiver::udpHandler, this,
|
||||
std::placeholders::_1));
|
||||
}
|
||||
|
||||
void EspReceiver::subscribe(uint16_t num) {
|
||||
Receiver::subscribe(num);
|
||||
// listen multicast; works for unicast too
|
||||
udp.listenMulticast(IPv4MulticastAddress(num),
|
||||
ACN_SDT_MULTICAST_PORT);
|
||||
}
|
||||
|
||||
void EspReceiver::udpHandler(AsyncUDPPacket udp) {
|
||||
// Expecting IANA registered Session Data Transport traffic
|
||||
if (!udp.localPort() == ACN_SDT_MULTICAST_PORT)
|
||||
return;
|
||||
|
||||
PDU::stream_ptr packet(new PDU::pdu_stream(udp.data(), udp.available()));
|
||||
packetHandler(packet); // from base class, SACN::Receiver
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
receiver-esp.h
|
||||
|
||||
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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <IPAddress.h>
|
||||
#include <IPv6Address.h>
|
||||
#include <AsyncUDP.h>
|
||||
#include "receiver.h"
|
||||
#include "../dmx/universe.h"
|
||||
|
||||
|
||||
/*
|
||||
Arduino specification additions to the sACN library
|
||||
*/
|
||||
namespace SACN {
|
||||
// 9.3.1 Allocation of IPv4 Multicast Addresses
|
||||
// Multicast addresses are from the IPv4 Local Scope.
|
||||
inline IPAddress IPv4MulticastAddress(uint16_t universe) {
|
||||
return IPAddress(239, 255, (universe >> 8), (universe & 0xff));
|
||||
};
|
||||
|
||||
// 9.3.2 Allocation of IPv6 Multicast Addresses
|
||||
inline IPv6Address IPv6MulticastAddress(uint16_t universe) {
|
||||
IPv6Address address;
|
||||
address.fromString("ff18::83:00:" +
|
||||
String(universe >> 8, HEX) + ":" +
|
||||
String(universe & 0xff, HEX));
|
||||
return address;
|
||||
};
|
||||
} // SACN
|
||||
|
||||
|
||||
using namespace SACN;
|
||||
|
||||
class EspReceiver
|
||||
: public Receiver
|
||||
{
|
||||
public:
|
||||
EspReceiver();
|
||||
void subscribe(uint16_t universe = 1);
|
||||
|
||||
private:
|
||||
AsyncUDP udp; // AsyncUDP
|
||||
void udpHandler(AsyncUDPPacket); // UDP packet parser callback
|
||||
|
||||
};
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
receiver.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 "receiver.h"
|
||||
#include "data.h"
|
||||
|
||||
namespace SACN {
|
||||
using namespace ACN;
|
||||
|
||||
void Receiver::subscribe(const uint16_t universe) {
|
||||
if (universes_.count(universe))
|
||||
return;
|
||||
universes_.emplace(universe, new SACN::Universe());
|
||||
}
|
||||
|
||||
SACN::Universe * Receiver::universe(uint16_t universe) {
|
||||
if (universes_.count(universe))
|
||||
return universes_.at(universe);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void Receiver::packetHandler(PDU::stream_ptr packet) {
|
||||
PDU::block_ptr block = RLP::UDP::readBlock(packet);
|
||||
for(PDU::pdu_ptr pdu : *block) {
|
||||
// read RLP header
|
||||
RLP::readHeader(pdu);
|
||||
|
||||
// 5.5 Vector
|
||||
// Receivers shall discard the packet if the received value is not
|
||||
// VECTOR_ROOT_E131_DATA or VECTOR_ROOT_E131_EXTENDED.
|
||||
switch (pdu->vector()) {
|
||||
case VECTOR_ROOT_E131_DATA:
|
||||
rootDataHandler(pdu);
|
||||
break;
|
||||
case VECTOR_ROOT_E131_EXTENDED:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Receiver::rootDataHandler(PDU::pdu_ptr pdu) {
|
||||
PDU::block_ptr block = PDU::readBlock(pdu->buffer());
|
||||
// PDU data will be a block of one E1.31 frame
|
||||
for(PDU::pdu_ptr child : *block) {
|
||||
child->setParent(pdu);
|
||||
|
||||
// 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. This value indicates that the E1.31 framing
|
||||
// layer is wrapping a DMP PDU.
|
||||
switch(child->vector()) {
|
||||
case VECTOR_E131_DATA_PACKET:
|
||||
dataPacketHandler(child);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Receiver::dataPacketHandler(PDU::pdu_ptr pdu) {
|
||||
DATA::readHeader(pdu);
|
||||
DATA::frame_header * header = (DATA::frame_header*)pdu->header();
|
||||
|
||||
if (!universes_.count(header->universe))
|
||||
return;
|
||||
Universe * universe = universes_.at(header->universe);
|
||||
|
||||
// TODO: do something with merging/priorty
|
||||
// RLP::rlp_header * root_header = (RLP::rlp_header*)pdu->parent()->header();
|
||||
// uint8_t * cid = root_header->cid;
|
||||
|
||||
// PDU data will be a block of one DMP PDU
|
||||
PDU::block_ptr block = DMP::readBlock(pdu->buffer());
|
||||
for (PDU::pdu_ptr dmp : *block) {
|
||||
dmp->setParent(pdu);
|
||||
|
||||
// all DMP messages have the same header format
|
||||
DMP::readHeader(dmp);
|
||||
|
||||
// 7.2 DMP Layer: Vector
|
||||
// The DMP Layer's Vector shall be set to VECTOR_DMP_SET_PROPERTY, which
|
||||
// indicates a DMP Set Property message by sources. Receivers shall discard
|
||||
// the packet if the received value is not VECTOR_DMP_SET_PROPERTY.
|
||||
switch(dmp->vector()) {
|
||||
case DMP::SET_PROPERTY:
|
||||
DMP::readSet(dmp);
|
||||
universe->set(dmp);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}; // SACN
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
receiver.h
|
||||
|
||||
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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "sacn.h"
|
||||
#include "universe.h"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace SACN {
|
||||
using namespace ACN;
|
||||
|
||||
class Receiver
|
||||
{
|
||||
public:
|
||||
Receiver() {};
|
||||
void subscribe(const uint16_t universe);
|
||||
SACN::Universe * universe(uint16_t universe);
|
||||
|
||||
protected:
|
||||
void packetHandler(PDU::stream_ptr);
|
||||
|
||||
void rootDataHandler(PDU::pdu_ptr);
|
||||
void dataPacketHandler(PDU::pdu_ptr);
|
||||
|
||||
private:
|
||||
std::unordered_map <uint16_t, SACN::Universe *> universes_;
|
||||
};
|
||||
|
||||
};
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
sacn.h
|
||||
|
||||
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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unordered_map>
|
||||
#include "../acn/acn.h"
|
||||
|
||||
// E1.31 Lightweight streaming protocol for transport of DMX512 using ACN
|
||||
namespace SACN {
|
||||
using namespace ACN;
|
||||
|
||||
// Appendix A: Defined Parameters (Normative)
|
||||
static const uint32_t VECTOR_ROOT_E131_DATA = 0x00000004;
|
||||
static const uint32_t VECTOR_ROOT_E131_EXTENDED = 0x00000008;
|
||||
|
||||
static const uint8_t VECTOR_DMP_SET_PROPERTY = 0x02;
|
||||
|
||||
static const uint32_t VECTOR_E131_DATA_PACKET = 0x00000002;
|
||||
|
||||
static const uint32_t VECTOR_E131_EXTENDED_SYNCHRONIZATION = 0x00000001;
|
||||
static const uint32_t VECTOR_E131_EXTENDED_DISCOVERY = 0x00000002;
|
||||
|
||||
static const uint32_t VECTOR_UNIVERSE_DISCOVERY_UNIVERSE_LIST = 0x00000001;
|
||||
|
||||
static const uint16_t E131_E131_UNIVERSE_DISCOVER_INTERVAL = 10000; // ms
|
||||
static const uint16_t E131_NETWORK_DATA_LOSS_TIMEOUT = 2500; // ms
|
||||
static const uint16_t E131_DISCOVERY_UNIVERSE = 64214;
|
||||
|
||||
static const uint16_t ACN_SDT_MULTICAST_PORT = 5568;
|
||||
} // SACN
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
universe.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 "universe.h"
|
||||
|
||||
namespace SACN {
|
||||
using namespace ACN;
|
||||
|
||||
Universe::Universe()
|
||||
: DMX::Universe()
|
||||
{
|
||||
}
|
||||
|
||||
void Universe::set(PDU::pdu_ptr pdu) {
|
||||
// 7.3 Address Type and Data Type
|
||||
// Sources shall set the DMP Layer's Address Type and Data Type to 0xa1.
|
||||
// Receivers shall discard the packet if the received value is not 0xa1.
|
||||
DMP::address_type * type = (DMP::address_type*)pdu->header();
|
||||
if (!type->z_reserved) return; // needs to be true, but why?
|
||||
if (type->relative) return;
|
||||
if (type->type != DMP::ARRAY) return;
|
||||
if (type->x_reserved != 0) return;
|
||||
if (type->width != DMP::TWO) return;
|
||||
|
||||
// only accept the first property pair in the data
|
||||
DMP::dmp_set_data * data = (DMP::dmp_set_data*)pdu->data();
|
||||
DMP::set_property property = data->properties.front();
|
||||
|
||||
DMP::range pr = property.first;
|
||||
// 7.4 First Property Address
|
||||
// Sources shall set the DMP Layer's First Property Address to 0x0000.
|
||||
// Receivers shall discard the packet if the received value is not 0x0000.
|
||||
if (pr.address != 0)
|
||||
return;
|
||||
|
||||
// 7.5 Address Increment
|
||||
// Sources shall set the DMP Layer's Address Increment to 0x0001.
|
||||
// Receivers shall discard the packet if the received value is not 0x0001.
|
||||
if (pr.incriment != 1)
|
||||
return;
|
||||
|
||||
// 7.6 Property Value Count
|
||||
// The DMP Layer's Property Value Count is used to encode the number of
|
||||
// DMX512-A [DMX] Slots (including the START Code slot).
|
||||
if (pr.count < 1 || pr.count > 513)
|
||||
return;
|
||||
|
||||
// 7.7 Property Values (DMX512-A Data)
|
||||
/// The DMP Layer's Property values field is used to encode the
|
||||
// DMX512-A [DMX] START Code and data.
|
||||
DMX::Universe::set(property.second);
|
||||
}
|
||||
|
||||
};
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
universe.h
|
||||
|
||||
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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "sacn.h"
|
||||
#include "../dmx/universe.h"
|
||||
|
||||
namespace SACN {
|
||||
using namespace ACN;
|
||||
|
||||
class Universe
|
||||
: public DMX::Universe
|
||||
{
|
||||
public:
|
||||
Universe();
|
||||
void set(PDU::pdu_ptr);
|
||||
};
|
||||
|
||||
|
||||
};
|
|
@ -35,7 +35,7 @@
|
|||
#include <ESPAsyncWebServer.h>
|
||||
#include "src/wiflash_configure.h"
|
||||
#include "src/wiflash_status.h"
|
||||
#include "src/lib/sacn/ESPsACN.h"
|
||||
#include "src/lib/sacn/receiver-esp.h"
|
||||
#include "src/lib/fixture/EspStrobe.h"
|
||||
|
||||
// ESP logging module
|
||||
|
@ -46,7 +46,7 @@ static const char* TAG = "WiFlash";
|
|||
//// device objects
|
||||
EspStrobe *strobe;
|
||||
NeoPixelBus<NeoGrbwFeature, Neo800KbpsMethod> *strip;
|
||||
ESPsACN *e131 = new ESPsACN();
|
||||
EspReceiver *e131 = new EspReceiver();
|
||||
AsyncWebServer *httpd = new AsyncWebServer(80);
|
||||
|
||||
|
||||
|
@ -112,13 +112,13 @@ void startHTTPD() {
|
|||
/*
|
||||
change pixel strip colours on DMX change
|
||||
*/
|
||||
void recvPixelData(Universe *universe) {
|
||||
void recvPixelData(DMX::Universe *universe) {
|
||||
for (int i = 0; i < config::settings.strip_led_count; i++) {
|
||||
uint8_t r, g, b, w;
|
||||
r = universe->data()->data[config::settings.strip_address + (i * 4) + 0];
|
||||
g = universe->data()->data[config::settings.strip_address + (i * 4) + 1];
|
||||
b = universe->data()->data[config::settings.strip_address + (i * 4) + 2];
|
||||
w = universe->data()->data[config::settings.strip_address + (i * 4) + 3];
|
||||
r = universe->slot(config::settings.strip_address + (i * 4) + 0);
|
||||
g = universe->slot(config::settings.strip_address + (i * 4) + 1);
|
||||
b = universe->slot(config::settings.strip_address + (i * 4) + 2);
|
||||
w = universe->slot(config::settings.strip_address + (i * 4) + 3);
|
||||
strip->SetPixelColor(i, RgbwColor(r, g, b, w));
|
||||
}
|
||||
strip->Show();
|
||||
|
@ -130,6 +130,7 @@ void recvPixelData(Universe *universe) {
|
|||
*/
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.setDebugOutput(true);
|
||||
while (!Serial); // wait for serial attach
|
||||
ESP_LOGI(TAG, "Serial started.");
|
||||
|
||||
|
@ -205,12 +206,9 @@ void setup() {
|
|||
if (!strobe->begin(config::settings.strobe_led_pin, 0)) {
|
||||
ESP_LOGW(TAG, "Strobe failed to configure.");
|
||||
}
|
||||
if (e131->subscribe(config::settings.strobe_universe)) {
|
||||
e131->universe(config::settings.strobe_universe)->onData(std::bind(&EspStrobe::recvData,
|
||||
strobe, std::placeholders::_1));
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Failed to subscribe to universe: %u", config::settings.strobe_universe);
|
||||
}
|
||||
e131->subscribe(config::settings.strobe_universe);
|
||||
e131->universe(config::settings.strobe_universe)->onData(std::bind(&EspStrobe::recvData,
|
||||
strobe, std::placeholders::_1));
|
||||
}
|
||||
|
||||
// pixels
|
||||
|
@ -220,10 +218,10 @@ void setup() {
|
|||
strip = new NeoPixelBus<NeoGrbwFeature, Neo800KbpsMethod>(config::settings.strip_led_count, config::settings.strip_data_pin);
|
||||
strip->Begin();
|
||||
strip->Show();
|
||||
if (e131->subscribe(config::settings.strip_universe)) {
|
||||
e131->universe(config::settings.strip_universe)->onData(std::bind(&recvPixelData,
|
||||
std::placeholders::_1));
|
||||
}
|
||||
e131->subscribe(config::settings.strip_universe);
|
||||
e131->universe(config::settings.strip_universe)->onData(std::bind(&recvPixelData,
|
||||
std::placeholders::_1));
|
||||
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "Setup complete.");
|
||||
|
|
Loading…
Reference in New Issue