* Started implementing artnet/DMX support

It compiles, but certainly wont work just yet

* Fix up packet data and length
correct default udp port

The data looks ok in wireshark

* Code cleanup
Sequence runs from 1..255 not 0
fix universe > 255

* code cleanups and force even number of channels

* Fix potential endianness issue

* added support for 'x' channels per fixture with zero padding
its very basic support for now - it needs better multi universe support
This commit is contained in:
penfold42 2017-05-27 06:17:12 +10:00 committed by redPanther
parent 564d4578d3
commit 64fc6a9003
7 changed files with 220 additions and 1 deletions

View File

@ -474,7 +474,7 @@ $(document).ready(function() {
devRPiSPI = ['apa102', 'ws2801', 'lpd6803', 'lpd8806', 'p9813', 'sk6812spi', 'sk6822spi', 'ws2812spi'];
devRPiPWM = ['ws281x'];
devRPiGPIO = ['piblaster'];
devNET = ['atmoorb', 'fadecandy', 'philipshue', 'tinkerforge', 'tpm2net', 'udpe131', 'udph801', 'udpraw'];
devNET = ['atmoorb', 'fadecandy', 'philipshue', 'tinkerforge', 'tpm2net', 'udpe131', 'udpartnet', 'udph801', 'udpraw'];
devUSB = ['adalight', 'dmx', 'atmo', 'hyperionusbasp', 'lightpack', 'multilightpack', 'paintpack', 'rawhid', 'sedu', 'tpm2'];
var optArr = [[]];

View File

@ -37,6 +37,7 @@ SET(Leddevice_HEADERS
${CURRENT_SOURCE_DIR}/LedDeviceUdpRaw.h
${CURRENT_SOURCE_DIR}/LedDeviceUdpH801.h
${CURRENT_SOURCE_DIR}/LedDeviceUdpE131.h
${CURRENT_SOURCE_DIR}/LedDeviceUdpArtNet.h
${CURRENT_SOURCE_DIR}/ProviderUdp.h
${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.h
${CURRENT_SOURCE_DIR}/LedDeviceTpm2.h
@ -65,6 +66,7 @@ SET(Leddevice_SOURCES
${CURRENT_SOURCE_DIR}/LedDeviceUdpRaw.cpp
${CURRENT_SOURCE_DIR}/LedDeviceUdpH801.cpp
${CURRENT_SOURCE_DIR}/LedDeviceUdpE131.cpp
${CURRENT_SOURCE_DIR}/LedDeviceUdpArtNet.cpp
${CURRENT_SOURCE_DIR}/ProviderUdp.cpp
${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.cpp
${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.cpp

View File

@ -39,6 +39,7 @@
#include "LedDeviceTpm2net.h"
#include "LedDeviceUdpRaw.h"
#include "LedDeviceUdpE131.h"
#include "LedDeviceUdpArtNet.h"
#include "LedDeviceHyperionUsbasp.h"
#include "LedDevicePhilipsHue.h"
#include "LedDeviceTpm2.h"
@ -91,6 +92,7 @@ LedDevice * LedDeviceFactory::construct(const QJsonObject & deviceConfig, const
REGISTER(Tpm2net);
REGISTER(UdpRaw);
REGISTER(UdpE131);
REGISTER(UdpArtNet);
REGISTER(UdpH801);
REGISTER(PhilipsHue);
REGISTER(AtmoOrb);

View File

@ -24,6 +24,7 @@
<file alias="schema-tpm2net">schemas/schema-tpm2net.json</file>
<file alias="schema-tpm2">schemas/schema-tpm2.json</file>
<file alias="schema-udpe131">schemas/schema-e131.json</file>
<file alias="schema-udpartnet">schemas/schema-artnet.json</file>
<file alias="schema-udph801">schemas/schema-h801.json</file>
<file alias="schema-udpraw">schemas/schema-udpraw.json</file>
<file alias="schema-ws2801">schemas/schema-ws2801.json</file>

View File

@ -0,0 +1,93 @@
#include <arpa/inet.h>
#include <QHostInfo>
// hyperion local includes
#include "LedDeviceUdpArtNet.h"
LedDeviceUdpArtNet::LedDeviceUdpArtNet(const QJsonObject &deviceConfig)
: ProviderUdp()
{
_deviceReady = init(deviceConfig);
}
bool LedDeviceUdpArtNet::init(const QJsonObject &deviceConfig)
{
_port = 6454;
ProviderUdp::init(deviceConfig);
_artnet_universe = deviceConfig["universe"].toInt(1);
_artnet_channelsPerFixture = deviceConfig["channelsPerFixture"].toInt(3);
return true;
}
LedDevice* LedDeviceUdpArtNet::construct(const QJsonObject &deviceConfig)
{
return new LedDeviceUdpArtNet(deviceConfig);
}
// populates the headers
void LedDeviceUdpArtNet::prepare(const unsigned this_universe, const unsigned this_sequence, unsigned this_dmxChannelCount)
{
// WTF? why do the specs say:
// "This value should be an even number in the range 2 512. "
if (this_dmxChannelCount & 0x1)
{
this_dmxChannelCount++;
}
memcpy (artnet_packet.ID, "Art-Net\0", 8);
artnet_packet.OpCode = htons(0x0050); // OpOutput / OpDmx
artnet_packet.ProtVer = htons(0x000e);
artnet_packet.Sequence = this_sequence;
artnet_packet.Physical = 0;
artnet_packet.SubUni = this_universe & 0xff ;
artnet_packet.Net = (this_universe >> 8) & 0x7f;
artnet_packet.Length = htons(this_dmxChannelCount);
}
int LedDeviceUdpArtNet::write(const std::vector<ColorRgb> &ledValues)
{
int retVal = 0;
int thisUniverse = _artnet_universe;
const uint8_t * rawdata = reinterpret_cast<const uint8_t *>(ledValues.data());
/*
This field is incremented in the range 0x01 to 0xff to allow the receiving node to resequence packets.
The Sequence field is set to 0x00 to disable this feature.
*/
if (_artnet_seq++ == 0)
{
_artnet_seq = 1;
}
int dmxIdx = 0; // offset into the current dmx packet
memset(artnet_packet.raw, 0, sizeof(artnet_packet.raw));
for (int ledIdx = 0; ledIdx < _ledRGBCount; ledIdx++)
{
artnet_packet.Data[dmxIdx++] = rawdata[ledIdx];
if ( (ledIdx % 3 == 2) && (ledIdx > 0) )
{
dmxIdx += (_artnet_channelsPerFixture-3);
}
// is this the last byte of last packet || last byte of other packets
if ( (ledIdx == _ledRGBCount-1) || (dmxIdx >= DMX_MAX) )
{
prepare(thisUniverse, _artnet_seq, dmxIdx);
retVal &= writeBytes(18 + std::min(dmxIdx, DMX_MAX), artnet_packet.raw);
memset(artnet_packet.raw, 0, sizeof(artnet_packet.raw));
thisUniverse ++;
dmxIdx = 0;
}
}
return retVal;
}

View File

@ -0,0 +1,79 @@
#pragma once
// hyperion includes
#include "ProviderUdp.h"
#include <QUuid>
/**
*
* 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.
*
**/
#define ArtNet_DEFAULT_PORT 5568
#define DMX_MAX 512 // 512 usable slots
// http://stackoverflow.com/questions/16396013/artnet-packet-structure
typedef union
{
struct {
char ID[8]; // "Art-Net"
uint16_t OpCode; // See Doc. Table 1 - OpCodes eg. 0x5000 OpOutput / OpDmx
uint16_t ProtVer; // 0x0e00 (aka 14)
uint8_t Sequence; // monotonic counter
uint8_t Physical; // 0x00
uint8_t SubUni; // low universe (0-255)
uint8_t Net; // high universe (not used)
uint16_t Length; // data length (2 - 512)
uint8_t Data[ DMX_MAX ]; // universe data
} __attribute__((packed));
uint8_t raw[ 18 + DMX_MAX ];
} artnet_packet_t;
///
/// Implementation of the LedDevice interface for sending led colors via udp/E1.31 packets
///
class LedDeviceUdpArtNet : public ProviderUdp
{
public:
///
/// Constructs specific LedDevice
///
/// @param deviceConfig json device config
///
LedDeviceUdpArtNet(const QJsonObject &deviceConfig);
///
/// Sets configuration
///
/// @param deviceConfig the json device config
/// @return true if success
bool init(const QJsonObject &deviceConfig);
/// constructs leddevice
static LedDevice* construct(const QJsonObject &deviceConfig);
private:
///
/// Writes the led color values to the led-device
///
/// @param ledValues The color-value per led
/// @return Zero on succes else negative
///
virtual int write(const std::vector<ColorRgb> &ledValues);
void prepare(const unsigned this_universe, const unsigned this_sequence, const unsigned this_dmxChannelCount);
artnet_packet_t artnet_packet;
uint8_t _artnet_seq = 1;
uint8_t _artnet_channelsPerFixture = 3;
unsigned _artnet_universe = 1;
};

View File

@ -0,0 +1,42 @@
{
"type":"object",
"required":true,
"properties":{
"host" : {
"type": "string",
"title":"edt_dev_spec_targetIp_title",
"propertyOrder" : 1
},
"port" : {
"type": "integer",
"title":"edt_dev_spec_port_title",
"default": 6454,
"minimum" : 0,
"maximum" : 65535,
"propertyOrder" : 2
},
"universe": {
"type": "integer",
"title":"edt_dev_spec_universe_title",
"default": 1,
"propertyOrder" : 3
},
"channelsPerFixture": {
"type": "integer",
"title":"edt_dev_spec_chanperfixture_title",
"default": 3,
"propertyOrder" : 4
},
"latchTime": {
"type": "integer",
"title":"edt_dev_spec_latchtime_title",
"default": 1,
"append" : "edt_append_ms",
"minimum": 1,
"maximum": 1000,
"access" : "expert",
"propertyOrder" : 5
}
},
"additionalProperties": true
}