diff --git a/libsrc/leddevice/CMakeLists.txt b/libsrc/leddevice/CMakeLists.txt index c6eade16..2efcca56 100755 --- a/libsrc/leddevice/CMakeLists.txt +++ b/libsrc/leddevice/CMakeLists.txt @@ -37,6 +37,7 @@ SET(Leddevice_HEADERS ${CURRENT_SOURCE_DIR}/LedDeviceFadeCandy.h ${CURRENT_SOURCE_DIR}/LedDeviceUdp.h ${CURRENT_SOURCE_DIR}/LedDeviceUdpRaw.h + ${CURRENT_SOURCE_DIR}/LedDeviceUdpE131.h ${CURRENT_SOURCE_DIR}/LedUdpDevice.h ${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.h ${CURRENT_SOURCE_DIR}/LedDeviceTpm2.h @@ -63,6 +64,7 @@ SET(Leddevice_SOURCES ${CURRENT_SOURCE_DIR}/LedDeviceFadeCandy.cpp ${CURRENT_SOURCE_DIR}/LedDeviceUdp.cpp ${CURRENT_SOURCE_DIR}/LedDeviceUdpRaw.cpp + ${CURRENT_SOURCE_DIR}/LedDeviceUdpE131.cpp ${CURRENT_SOURCE_DIR}/LedUdpDevice.cpp ${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.cpp ${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.cpp diff --git a/libsrc/leddevice/LedDeviceFactory.cpp b/libsrc/leddevice/LedDeviceFactory.cpp index e7550884..cb733a44 100755 --- a/libsrc/leddevice/LedDeviceFactory.cpp +++ b/libsrc/leddevice/LedDeviceFactory.cpp @@ -37,6 +37,7 @@ #include "LedDeviceFadeCandy.h" #include "LedDeviceUdp.h" #include "LedDeviceUdpRaw.h" +#include "LedDeviceUdpE131.h" #include "LedDeviceHyperionUsbasp.h" #include "LedDevicePhilipsHue.h" #include "LedDeviceTpm2.h" @@ -279,6 +280,14 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) deviceConfig.get("latchtime",500000).asInt() ); } + else if (type == "e131") + { + device = new LedDeviceUdpE131( + deviceConfig["output"].asString(), + deviceConfig.get("latchtime",500000).asInt(), + deviceConfig.get("universe",1).asInt() + ); + } else if (type == "tpm2") { device = new LedDeviceTpm2( @@ -320,6 +329,7 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) } else { + Error(log, "Dummy device used, because configured device '%s' is unknown", type.c_str() ); throw std::runtime_error("unknown device"); } } diff --git a/libsrc/leddevice/LedDeviceUdpE131.cpp b/libsrc/leddevice/LedDeviceUdpE131.cpp new file mode 100644 index 00000000..dbd6744b --- /dev/null +++ b/libsrc/leddevice/LedDeviceUdpE131.cpp @@ -0,0 +1,77 @@ + +// STL includes +#include +#include +#include + +// Linux includes +#include +#include +#include + +// hyperion local includes +#include "LedDeviceUdpE131.h" + +LedDeviceUdpE131::LedDeviceUdpE131(const std::string& outputDevice, const unsigned latchTime, const unsigned universe) + : LedUdpDevice(outputDevice, latchTime) + , _e131_universe(universe) + +{ + // empty +} + +#define CID "hyperion!\0" +#define SOURCE_NAME "hyperion on hostname\0" + +int LedDeviceUdpE131::write(const std::vector &ledValues) +{ + _ledCount = ledValues.size(); + int _dmxChannelCount = 3 * std::min(_ledCount,170); // DMX512 has max 512 channels == 170 RGB leds + + memset(e131_packet.raw, 0, sizeof(e131_packet.raw)); + + /* Root Layer */ + e131_packet.preamble_size = htons(16); + e131_packet.postamble_size = 0; + memcpy (e131_packet.acn_id, _acn_id, 12); + e131_packet.root_flength = htons(0x7000 | (110+_dmxChannelCount) ); + e131_packet.root_vector = htonl(VECTOR_ROOT_E131_DATA); + memcpy (e131_packet.cid, CID, sizeof(CID) ); + + /* Frame Layer */ + e131_packet.frame_flength = htons(0x7000 | (88+_dmxChannelCount)); + e131_packet.frame_vector = htonl(VECTOR_E131_DATA_PACKET); + memcpy (e131_packet.source_name, SOURCE_NAME, sizeof(SOURCE_NAME)); + e131_packet.priority = 100; + e131_packet.reserved = htons(0); + e131_packet.sequence_number = _e131_seq++; + e131_packet.options = 0; // Bit 7 = Preview_Data + // Bit 6 = Stream_Terminated + // Bit 5 = Force_Synchronization + e131_packet.universe = htons(_e131_universe); + + + /* DMX Layer */ + e131_packet.dmp_flength = htons(0x7000 | (11+_dmxChannelCount)); + e131_packet.dmp_vector = VECTOR_DMP_SET_PROPERTY; + e131_packet.type = 0xa1; + e131_packet.first_address = htons(0); + e131_packet.address_increment = htons(1); + e131_packet.property_value_count = htons(1+_dmxChannelCount); + + int led_idx=0; + e131_packet.property_values[0] = 0; // start code + for (int _dmxIdx=1; _dmxIdx <= _dmxChannelCount; ) + { + e131_packet.property_values[_dmxIdx++] = ledValues[led_idx].red; + e131_packet.property_values[_dmxIdx++] = ledValues[led_idx].green; + e131_packet.property_values[_dmxIdx++] = ledValues[led_idx].blue; + led_idx ++; + } + return writeBytes(E131_DMP_DATA + 1 + _dmxChannelCount, e131_packet.raw); +} + +int LedDeviceUdpE131::switchOff() +{ + return write(std::vector(_ledCount, ColorRgb{0,0,0})); +} diff --git a/libsrc/leddevice/LedDeviceUdpE131.h b/libsrc/leddevice/LedDeviceUdpE131.h new file mode 100644 index 00000000..783cb76d --- /dev/null +++ b/libsrc/leddevice/LedDeviceUdpE131.h @@ -0,0 +1,128 @@ +#pragma once + +// STL includes +#include + +// hyperion includes +#include "LedUdpDevice.h" + +/* +* +* https://raw.githubusercontent.com/forkineye/ESPixelStick/master/_E131.h +* Project: E131 - E.131 (sACN) library for Arduino +* Copyright (c) 2015 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. +* +*/ + +#define E131_DEFAULT_PORT 5568 + +/* E1.31 Packet Offsets */ +#define E131_ROOT_PREAMBLE_SIZE 0 +#define E131_ROOT_POSTAMBLE_SIZE 2 +#define E131_ROOT_ID 4 +#define E131_ROOT_FLENGTH 16 +#define E131_ROOT_VECTOR 18 +#define E131_ROOT_CID 22 + +#define E131_FRAME_FLENGTH 38 +#define E131_FRAME_VECTOR 40 +#define E131_FRAME_SOURCE 44 +#define E131_FRAME_PRIORITY 108 +#define E131_FRAME_RESERVED 109 +#define E131_FRAME_SEQ 111 +#define E131_FRAME_OPT 112 +#define E131_FRAME_UNIVERSE 113 + +#define E131_DMP_FLENGTH 115 +#define E131_DMP_VECTOR 117 +#define E131_DMP_TYPE 118 +#define E131_DMP_ADDR_FIRST 119 +#define E131_DMP_ADDR_INC 121 +#define E131_DMP_COUNT 123 +#define E131_DMP_DATA 125 + +/* E1.31 Packet Structure */ +typedef union { + struct { + /* Root Layer */ + uint16_t preamble_size; + uint16_t postamble_size; + uint8_t acn_id[12]; + uint16_t root_flength; + uint32_t root_vector; + uint8_t cid[16]; + + /* Frame Layer */ + uint16_t frame_flength; + uint32_t frame_vector; + uint8_t source_name[64]; + uint8_t priority; + uint16_t reserved; + uint8_t sequence_number; + uint8_t options; + uint16_t universe; + + /* DMP Layer */ + uint16_t dmp_flength; + uint8_t dmp_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[638]; +} e131_packet_t; + +/* defined parameters from http://tsp.esta.org/tsp/documents/docs/BSR_E1-31-20xx_CP-2014-1009r2.pdf */ +#define VECTOR_ROOT_E131_DATA 0x00000004 +#define VECTOR_ROOT_E131_EXTENDED 0x00000008 +#define VECTOR_DMP_SET_PROPERTY 0x02 +#define VECTOR_E131_DATA_PACKET 0x00000002 +#define VECTOR_E131_EXTENDED_SYNCHRONIZATION 0x00000001 +#define VECTOR_E131_EXTENDED_DISCOVERY 0x00000002 +#define VECTOR_UNIVERSE_DISCOVERY_UNIVERSE_LIST 0x00000001 +#define E131_E131_UNIVERSE_DISCOVERY_INTERVAL 10 // seconds +#define E131_NETWORK_DATA_LOSS_TIMEOUT 2500 // milli econds +#define E131_DISCOVERY_UNIVERSE 64214 + + +/// +/// Implementation of the LedDevice interface for sending led colors via udp/E1.31 packets +/// +class LedDeviceUdpE131 : public LedUdpDevice +{ +public: + /// + /// Constructs the LedDevice for sending led colors via udp + /// + /// @param outputDevice hostname:port + /// @param latchTime + /// + + LedDeviceUdpE131(const std::string& outputDevice, const unsigned latchTime, const unsigned universe); + + + /// + /// 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 &ledValues); + + /// Switch the leds off + virtual int switchOff(); + +private: + e131_packet_t e131_packet; + uint8_t _e131_seq = 0; + uint8_t _e131_universe = 1; + uint8_t _acn_id[12] = {0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00 }; +};