diff --git a/libsrc/leddevice/CMakeLists.txt b/libsrc/leddevice/CMakeLists.txt index d6d4682c..3f4fb983 100755 --- a/libsrc/leddevice/CMakeLists.txt +++ b/libsrc/leddevice/CMakeLists.txt @@ -20,6 +20,8 @@ SET(Leddevice_QT_HEADERS ${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.h ${CURRENT_SOURCE_DIR}/LedHIDDevice.h ${CURRENT_SOURCE_DIR}/LedDeviceRawHID.h + ${CURRENT_SOURCE_DIR}/LedDeviceTest.h + ${CURRENT_SOURCE_DIR}/LedDeviceFadeCandy.h ) SET(Leddevice_HEADERS @@ -32,6 +34,7 @@ SET(Leddevice_HEADERS ${CURRENT_SOURCE_DIR}/LedDevicePiBlaster.h ${CURRENT_SOURCE_DIR}/LedDeviceSedu.h ${CURRENT_SOURCE_DIR}/LedDeviceTest.h + ${CURRENT_SOURCE_DIR}/LedDeviceFadeCandy.h ${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.h ${CURRENT_SOURCE_DIR}/LedDeviceTpm2.h ${CURRENT_SOURCE_DIR}/LedDeviceAtmo.h @@ -53,6 +56,7 @@ SET(Leddevice_SOURCES ${CURRENT_SOURCE_DIR}/LedDevicePiBlaster.cpp ${CURRENT_SOURCE_DIR}/LedDeviceSedu.cpp ${CURRENT_SOURCE_DIR}/LedDeviceTest.cpp + ${CURRENT_SOURCE_DIR}/LedDeviceFadeCandy.cpp ${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.cpp ${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.cpp ${CURRENT_SOURCE_DIR}/LedDeviceTpm2.cpp @@ -114,7 +118,7 @@ add_library(leddevice target_link_libraries(leddevice hyperion-utils - serialport + serialport ${LIBUSB_1_LIBRARIES} #apt-get install libusb-1.0-0-dev ${CMAKE_THREAD_LIBS_INIT} ${QT_LIBRARIES} diff --git a/libsrc/leddevice/LedDeviceFactory.cpp b/libsrc/leddevice/LedDeviceFactory.cpp index 85f665a5..b0fd064f 100755 --- a/libsrc/leddevice/LedDeviceFactory.cpp +++ b/libsrc/leddevice/LedDeviceFactory.cpp @@ -30,6 +30,7 @@ #include "LedDevicePiBlaster.h" #include "LedDeviceSedu.h" #include "LedDeviceTest.h" +#include "LedDeviceFadeCandy.h" #include "LedDeviceHyperionUsbasp.h" #include "LedDevicePhilipsHue.h" #include "LedDeviceTpm2.h" @@ -243,6 +244,12 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) const std::string output = deviceConfig["output"].asString(); device = new LedDeviceTest(output); } + else if (type == "fadecandy") + { + const std::string host = deviceConfig.get("output", "127.0.0.1").asString(); + const uint16_t port = deviceConfig.get("port", 7890).asInt(); + device = new LedDeviceFadeCandy(host,port); + } else if (type == "tpm2") { const std::string output = deviceConfig["output"].asString(); diff --git a/libsrc/leddevice/LedDeviceFadeCandy.cpp b/libsrc/leddevice/LedDeviceFadeCandy.cpp new file mode 100755 index 00000000..e8c178d1 --- /dev/null +++ b/libsrc/leddevice/LedDeviceFadeCandy.cpp @@ -0,0 +1,93 @@ +#include "LedDeviceFadeCandy.h" + +static const unsigned MAX_NUM_LEDS = 512; +static const unsigned OPC_BROADCAST = 0; // OPC broadcast channel +static const unsigned OPC_SET_PIXELS = 0; // OPC command codes +static const unsigned OPC_HEADER_SIZE = 4; // OPC header size + + +LedDeviceFadeCandy::LedDeviceFadeCandy(const std::string& host, const uint16_t port) : + _host(host), _port(port) +{ + _opc_data.resize( OPC_HEADER_SIZE ); + _opc_data[0] = OPC_BROADCAST; + _opc_data[1] = OPC_SET_PIXELS; + _opc_data[2] = 0; + _opc_data[3] = 0; +} + + +LedDeviceFadeCandy::~LedDeviceFadeCandy() +{ + _client.close(); +} + + +bool LedDeviceFadeCandy::isConnected() +{ + return _client.state() == QAbstractSocket::ConnectedState; +} + + +bool LedDeviceFadeCandy::tryConnect() +{ + if ( _client.state() == QAbstractSocket::UnconnectedState ) { + qDebug("connecting to %s %i",_host.c_str(),_port); + + _client.connectToHost( _host.c_str(), _port); + if ( _client.waitForConnected(1000) ) + qDebug("connected"); + } + + return isConnected(); +} + + +int LedDeviceFadeCandy::write( const std::vector & ledValues ) +{ + ssize_t nrLedValues = ledValues.size(); + ssize_t led_data_size = nrLedValues * 3; // 3 color bytes + ssize_t opc_data_size = led_data_size + OPC_HEADER_SIZE; + + if (nrLedValues > MAX_NUM_LEDS) + { + std::cerr << "Invalid attempt to write led values. Not more than " << MAX_NUM_LEDS << " leds are allowed." << std::endl; + return -1; + } + + if ( opc_data_size != _opc_data.size() ) + _opc_data.resize( opc_data_size ); + + _opc_data[2] = led_data_size >> 8; + _opc_data[3] = led_data_size & 0xff; + + uint idx = OPC_HEADER_SIZE; + for (const ColorRgb& color : ledValues) + { + _opc_data[idx ] = unsigned( color.red ); + _opc_data[idx+1] = unsigned( color.green ); + _opc_data[idx+2] = unsigned( color.blue ); + idx += 3; + } + + return ( transferData()<0 ? -1 : 0 ); +} + + +int LedDeviceFadeCandy::transferData() +{ + if ( isConnected() || tryConnect() ) + return _client.write( _opc_data, _opc_data.size() ); + + return -2; +} + + +int LedDeviceFadeCandy::switchOff() +{ + for ( int idx=OPC_HEADER_SIZE; idx < _opc_data.size(); idx++ ) + _opc_data[idx] = 0; + + return ( transferData()<0 ? -1 : 0 ); +} + diff --git a/libsrc/leddevice/LedDeviceFadeCandy.h b/libsrc/leddevice/LedDeviceFadeCandy.h new file mode 100755 index 00000000..0b894e88 --- /dev/null +++ b/libsrc/leddevice/LedDeviceFadeCandy.h @@ -0,0 +1,70 @@ +#pragma once + +// STL/Qt includes +#include +#include +#include + +// Leddevice includes +#include + +/// +/// Implementation of the LedDevice interface for sending to +/// fadecandy/opc-server via network by using the 'open pixel control' protocol. +/// +class LedDeviceFadeCandy : public QObject, public LedDevice +{ + Q_OBJECT + +public: + /// + /// Constructs the LedDevice for fadecandy/opc server + /// + /// @param host The ip address/host name of fadecandy/opc server + /// @param port The port to use (fadecandy default is 7890) + /// + LedDeviceFadeCandy(const std::string& host, const uint16_t port); + + /// + /// Destructor of the LedDevice; closes the tcp client + /// + virtual ~LedDeviceFadeCandy(); + + /// + /// 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: + QTcpSocket _client; + const std::string _host; + const uint16_t _port; + QByteArray _opc_data; + + /// try to establish connection to opc server, if not connected yet + /// + /// @return true if connection is established + /// + bool tryConnect(); + + /// return the conenction state + /// + /// @return True if connection established + /// + bool isConnected(); + + + /// transfer current opc_data buffer to opc server + /// + /// @return amount of transfered bytes. -1 error while transfering, -2 error while connecting + /// + int transferData(); + +};