diff --git a/assets/firmware/arduino/adalight/adalight.ino b/assets/firmware/arduino/adalight/adalight.ino index 9221a3f8..6f8b0869 100644 --- a/assets/firmware/arduino/adalight/adalight.ino +++ b/assets/firmware/arduino/adalight/adalight.ino @@ -1,117 +1,239 @@ #include "FastLED.h" -// How many leds in your strip? -#define NUM_LEDS 240 +#define ANALOG_MODE_AVERAGE 0 +#define ANALOG_MODE_LAST_LED 1 -// For led chips like Neopixels, which have a data line, ground, and power, you just -// need to define DATA_PIN. For led chipsets that are SPI based (four wires - data, clock, -// ground, and power), like the LPD8806 define both DATA_PIN and CLOCK_PIN -#define DATA_PIN 6 +/************************************** + S E T U P + + set following values to your needs + **************************************/ + +// Number of leds in your strip. set to 1 and ANALOG_OUTPUT_ENABLED to true to activate analog only +#define NUM_LEDS 100 + + +#define SPI_LEDS false // connection type. Set "true" for 4 wire and "false" for 3 Wire stripes. +#define LED_TYPE WS2812B // type of your led controller, possible values, see below + +// 3 wire (pwm): NEOPIXEL BTM1829 TM1812 TM1809 TM1804 TM1803 UCS1903 UCS1903B UCS1904 UCS2903 WS2812 WS2852 +// S2812B SK6812 SK6822 APA106 PL9823 WS2811 WS2813 APA104 WS2811_40 GW6205 GW6205_40 LPD1886 LPD1886_8BIT +// 4 wire (spi): LPD8806 WS2801 WS2803 SM16716 P9813 APA102 SK9822 DOTSTAR + +// For 3 wire led stripes line Neopixel/Ws2812, which have a data line, ground, and power, you just need to define DATA_PIN. +// For led chipsets that are SPI based (four wires - data, clock, ground, and power), both defines DATA_PIN and CLOCK_PIN are needed +#define DATA_PIN 6 #define CLOCK_PIN 13 -#define COLOR_ORDER RGB +#define COLOR_ORDER GRB // colororder of the stripe, set RGB in hyperion + +#define OFF_TIMEOUT 15000 // ms to switch off after no data was received, set 0 to deactivate + +// analog rgb uni color led stripe - using of hyperion smoothing is recommended +// ATTENTION this pin config is default for atmega328 based arduinos, others might work to +// if you have flickering analog leds this might be caused by unsynced pwm signals +// try other pins is more or less the only thing that helps +#define ANALOG_OUTPUT_ENABLED true +#define ANALOG_MODE ANALOG_MODE_LAST_LED // use ANALOG_MODE_AVERAGE or ANALOG_MODE_LAST_LED +#define ANALOG_GROUND_PIN 8 // additional ground pin to make wiring a bit easier +#define ANALOG_RED_PIN 9 +#define ANALOG_GREEN_PIN 10 +#define ANALOG_BLUE_PIN 11 + +// overall color adjustments +#define ANALOG_BRIGHTNESS_RED 255 // maximum brightness for analog 0-255 +#define ANALOG_BRIGHTNESS_GREEN 255 // maximum brightness for analog 0-255 +#define ANALOG_BRIGHTNESS_BLUE 255 // maximum brightness for analog 0-255 + +#define BRIGHTNESS 255 // maximum brightness 0-255 +#define DITHER_MODE BINARY_DITHER // BINARY_DITHER or DISABLE_DITHER +#define COLOR_TEMPERATURE CRGB(255,255,255) // RGB value describing the color temperature +#define COLOR_CORRECTION CRGB(255,255,255) // RGB value describing the color correction + +// Baudrate, higher rate allows faster refresh rate and more LEDs (defined in /etc/boblight.conf) +#define serialRate 460800 // use 115200 for ftdi based boards + + +/************************************** + A D A L I G H T C O D E + + no user changes needed + **************************************/ // Adalight sends a "Magic Word" (defined in /etc/boblight.conf) before sending the pixel data uint8_t prefix[] = {'A', 'd', 'a'}, hi, lo, chk, i; -// Baudrate, higher rate allows faster refresh rate and more LEDs (defined in /etc/boblight.conf) -#define serialRate 460800 +unsigned long endTime; // Define the array of leds CRGB leds[NUM_LEDS]; -void setup() { - // Uncomment/edit one of the following lines for your leds arrangement. - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); +// set rgb to analog led stripe +void showAnalogRGB(const CRGB& led) { + if (ANALOG_OUTPUT_ENABLED) { + byte r = map(led.r, 0,255,0,ANALOG_BRIGHTNESS_RED); + byte g = map(led.g, 0,255,0,ANALOG_BRIGHTNESS_GREEN); + byte b = map(led.b, 0,255,0,ANALOG_BRIGHTNESS_BLUE); + analogWrite(ANALOG_RED_PIN , r); + analogWrite(ANALOG_GREEN_PIN, g); + analogWrite(ANALOG_BLUE_PIN , b); + } +} - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); +// set color to all leds +void showColor(const CRGB& led) { + #if NUM_LEDS > 1 || ANALOG_OUTPUT_ENABLED == false + LEDS.showColor(led); + #endif + showAnalogRGB(led); +} + +// switch of digital and analog leds +void switchOff() { + #if ANALOG_ONLY == false + memset(leds, 0, NUM_LEDS * sizeof(struct CRGB)); + FastLED.show(); + #endif + showAnalogRGB(leds[0]); +} + +// function to check if serial data is available +// if timeout occured leds switch of, if configured +bool checkIncommingData() { + boolean dataAvailable = true; + while (!Serial.available()) { + if ( OFF_TIMEOUT > 0 && endTime < millis()) { + switchOff(); + dataAvailable = false; + endTime = millis() + OFF_TIMEOUT; + } + } + + return dataAvailable; +} + +// main function that setups and runs the code +void setup() { + + // additional ground pin to make wiring a bit easier + pinMode(ANALOG_GROUND_PIN, OUTPUT); + digitalWrite(ANALOG_GROUND_PIN, LOW); + + // analog output + if (ANALOG_OUTPUT_ENABLED) { + pinMode(ANALOG_BLUE_PIN , OUTPUT); + pinMode(ANALOG_RED_PIN , OUTPUT); + pinMode(ANALOG_GREEN_PIN, OUTPUT); + } + + // Uncomment/edit one of the following lines for your leds arrangement. + int ledCount = NUM_LEDS; + if (ANALOG_MODE == ANALOG_MODE_LAST_LED) { + ledCount--; + } + + #if NUM_LEDS > 1 || ANALOG_OUTPUT_ENABLED == false + #if SPI_LEDS == true + FastLED.addLeds(leds, ledCount); + #else + FastLED.addLeds(leds, ledCount); + #endif + #endif + + // color adjustments + FastLED.setBrightness ( BRIGHTNESS ); + FastLED.setTemperature( COLOR_TEMPERATURE ); + FastLED.setCorrection ( COLOR_CORRECTION ); + FastLED.setDither ( DITHER_MODE ); // initial RGB flash - LEDS.showColor(CRGB(255, 0, 0)); - delay(250); - LEDS.showColor(CRGB(0, 255, 0)); - delay(250); - LEDS.showColor(CRGB(0, 0, 255)); - delay(250); - LEDS.showColor(CRGB(0, 0, 0)); - + showColor(CRGB(255, 0, 0)); delay(400); + showColor(CRGB(0, 255, 0)); delay(400); + showColor(CRGB(0, 0, 255)); delay(400); + showColor(CRGB(0, 0, 0)); + Serial.begin(serialRate); Serial.print("Ada\n"); // Send "Magic Word" string to host -} + + boolean transmissionSuccess; + unsigned long sum_r, sum_g, sum_b; + + // loop() is avoided as even that small bit of function overhead + // has a measurable impact on this code's overall throughput. + while (true) { + // wait for first byte of Magic Word + for (i = 0; i < sizeof prefix; ++i) { + // If next byte is not in Magic Word, the start over + if (!checkIncommingData() || prefix[i] != Serial.read()) { + i = 0; + } + } + + // Hi, Lo, Checksum + if (!checkIncommingData()) continue; + hi = Serial.read(); + if (!checkIncommingData()) continue; + lo = Serial.read(); + if (!checkIncommingData()) continue; + chk = Serial.read(); + + // if checksum does not match go back to wait + if (chk != (hi ^ lo ^ 0x55)) continue; + + memset(leds, 0, NUM_LEDS * sizeof(struct CRGB)); + transmissionSuccess = true; + sum_r = 0; + sum_g = 0; + sum_b = 0; + + // read the transmission data and set LED values + for (uint8_t idx = 0; idx < NUM_LEDS; idx++) { + byte r, g, b; + if (!checkIncommingData()) { + transmissionSuccess = false; + break; + } + r = Serial.read(); + if (!checkIncommingData()) { + transmissionSuccess = false; + break; + } + g = Serial.read(); + if (!checkIncommingData()) { + transmissionSuccess = false; + break; + } + b = Serial.read(); + leds[idx].r = r; + leds[idx].g = g; + leds[idx].b = b; + #if ANALOG_OUTPUT_ENABLED == true && ANALOG_MODE == ANALOG_MODE_AVERAGE + sum_r += r; + sum_g += g; + sum_b += b; + #endif + } + + // shows new values + if (transmissionSuccess) { + endTime = millis() + OFF_TIMEOUT; + #if NUM_LEDS > 1 || ANALOG_OUTPUT_ENABLED == false + FastLED.show(); + #endif + + #if ANALOG_OUTPUT_ENABLED == true + #if ANALOG_MODE == ANALOG_MODE_LAST_LED + showAnalogRGB(leds[NUM_LEDS-1]); + #else + showAnalogRGB(CRGB(sum_r/NUM_LEDS, sum_g/NUM_LEDS, sum_b/NUM_LEDS)); + #endif + #endif + } + } +} // end of setup + void loop() { - // wait for first byte of Magic Word - for (i = 0; i < sizeof prefix; ++i) { -waitLoop: while (!Serial.available()) ;; - // Check next byte in Magic Word - if (prefix[i] == Serial.read()) continue; - // otherwise, start over - i = 0; - goto waitLoop; - } - - // Hi, Lo, Checksum - - while (!Serial.available()) ;; - hi = Serial.read(); - while (!Serial.available()) ;; - lo = Serial.read(); - while (!Serial.available()) ;; - chk = Serial.read(); - - // if checksum does not match go back to wait - if (chk != (hi ^ lo ^ 0x55)) - { - i = 0; - goto waitLoop; - } - - memset(leds, 0, NUM_LEDS * sizeof(struct CRGB)); - // read the transmission data and set LED values - for (uint8_t i = 0; i < NUM_LEDS; i++) { - byte r, g, b; - while (!Serial.available()); - r = Serial.read(); - while (!Serial.available()); - g = Serial.read(); - while (!Serial.available()); - b = Serial.read(); - leds[i].r = r; - leds[i].g = g; - leds[i].b = b; - } - // shows new values - FastLED.show(); + // Not used. See note in setup() function. } diff --git a/include/leddevice/LedDevice.h b/include/leddevice/LedDevice.h index 85a61c23..d8ecf557 100644 --- a/include/leddevice/LedDevice.h +++ b/include/leddevice/LedDevice.h @@ -12,6 +12,8 @@ #include #include +#include + // Utility includes #include #include @@ -81,4 +83,15 @@ protected: static int _ledCount; static int _ledRGBCount; static int _ledRGBWCount; + + /// Timer object which makes sure that led data is written at a minimum rate + /// e.g. Adalight device will switch off when it does not receive data at least every 15 seconds + QTimer _refresh_timer; + +protected slots: + /// Write the last data to the leds again + int rewriteLeds(); + +private: + std::vector _ledValues; }; diff --git a/libsrc/leddevice/LedDevice.cpp b/libsrc/leddevice/LedDevice.cpp index ce6763e7..410b326e 100644 --- a/libsrc/leddevice/LedDevice.cpp +++ b/libsrc/leddevice/LedDevice.cpp @@ -17,9 +17,14 @@ LedDevice::LedDevice() , _log(Logger::getInstance("LedDevice")) , _ledBuffer(0) , _deviceReady(true) - + , _refresh_timer() { LedDevice::getLedDeviceSchemas(); + + // setup timer + _refresh_timer.setSingleShot(false); + _refresh_timer.setInterval(0); + connect(&_refresh_timer, SIGNAL(timeout()), this, SLOT(rewriteLeds())); } // dummy implemention @@ -103,7 +108,17 @@ QJsonObject LedDevice::getLedDeviceSchemas() int LedDevice::setLedValues(const std::vector& ledValues) { - return _deviceReady ? write(ledValues) : -1; + if (!_deviceReady) + return -1; + + _ledValues = ledValues; + + // restart the timer + if (_refresh_timer.interval() > 0) + { + _refresh_timer.start(); + } + return write(ledValues); } int LedDevice::switchOff() @@ -118,3 +133,8 @@ void LedDevice::setLedCount(int ledCount) _ledRGBCount = _ledCount * sizeof(ColorRgb); _ledRGBWCount = _ledCount * sizeof(ColorRgbw); } + +int LedDevice::rewriteLeds() +{ + return write(_ledValues); +} diff --git a/libsrc/leddevice/LedDeviceAdalight.cpp b/libsrc/leddevice/LedDeviceAdalight.cpp index 9d6356cb..ba80b1a2 100644 --- a/libsrc/leddevice/LedDeviceAdalight.cpp +++ b/libsrc/leddevice/LedDeviceAdalight.cpp @@ -2,7 +2,6 @@ LedDeviceAdalight::LedDeviceAdalight(const QJsonObject &deviceConfig) : ProviderRs232() - { _deviceReady = init(deviceConfig); } diff --git a/libsrc/leddevice/ProviderRs232.cpp b/libsrc/leddevice/ProviderRs232.cpp index e3e53906..3351d713 100644 --- a/libsrc/leddevice/ProviderRs232.cpp +++ b/libsrc/leddevice/ProviderRs232.cpp @@ -17,16 +17,7 @@ ProviderRs232::ProviderRs232() , _bytesWritten(0) , _frameDropCounter(0) , _lastError(QSerialPort::NoError) - , _timer() { - // setup timer - _timer.setSingleShot(false); - _timer.setInterval(1000); - connect(&_timer, SIGNAL(timeout()), this, SLOT(rewriteLeds())); - - // start the timer - _timer.start(); - connect(&_rs232Port, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(error(QSerialPort::SerialPortError))); connect(&_rs232Port, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWritten(qint64))); connect(&_rs232Port, SIGNAL(readyRead()), this, SLOT(readyRead())); @@ -38,7 +29,7 @@ bool ProviderRs232::init(const QJsonObject &deviceConfig) _deviceName = deviceConfig["output"].toString().toStdString(); _baudRate_Hz = deviceConfig["rate"].toInt(); _delayAfterConnect_ms = deviceConfig["delayAfterConnect"].toInt(250); - _timer.setInterval ( deviceConfig["rewriteTime"].toInt(5000) ); + _refresh_timer.setInterval( deviceConfig["rewriteTime"].toInt(5000) ); return true; } @@ -157,8 +148,6 @@ bool ProviderRs232::tryOpen(const int delayAfterConnect_ms) int ProviderRs232::writeBytes(const qint64 size, const uint8_t * data) { - // restart the timer - _timer.start(); if (! _blockedForDelay) { if (!_rs232Port.isOpen()) diff --git a/libsrc/leddevice/ProviderRs232.h b/libsrc/leddevice/ProviderRs232.h index fe581b0c..9a78a3a1 100644 --- a/libsrc/leddevice/ProviderRs232.h +++ b/libsrc/leddevice/ProviderRs232.h @@ -46,15 +46,12 @@ protected: * @param[in[ size The length of the data * @param[in] data The data * - * @return Zero on succes else negative + * @return Zero on success else negative */ int writeBytes(const qint64 size, const uint8_t *data); void closeDevice(); - /// The RS232 serial-device - QSerialPort _rs232Port; - private slots: /// Write the last data to the leds again int rewriteLeds(); @@ -79,7 +76,7 @@ protected: int _delayAfterConnect_ms; /// The RS232 serial-device -// QSerialPort _rs232Port; + QSerialPort _rs232Port; bool _blockedForDelay; @@ -89,9 +86,4 @@ protected: qint64 _bytesWritten; qint64 _frameDropCounter; QSerialPort::SerialPortError _lastError; - - /// Timer object which makes sure that led data is written at a minimum rate - /// e.g. Adalight device will switch off when it does not receive data at least - /// every 15 seconds - QTimer _timer; }; diff --git a/libsrc/leddevice/schemas/schema-adalight.json b/libsrc/leddevice/schemas/schema-adalight.json index 8dacc228..339c5230 100644 --- a/libsrc/leddevice/schemas/schema-adalight.json +++ b/libsrc/leddevice/schemas/schema-adalight.json @@ -19,6 +19,13 @@ "default": 250, "append" : "ms", "propertyOrder" : 3 + }, + "rewriteTime": { + "type": "integer", + "title":"refresh time", + "default": 5000, + "append" : "ms", + "propertyOrder" : 4 } }, "additionalProperties": true diff --git a/libsrc/leddevice/schemas/schema-adalightapa102.json b/libsrc/leddevice/schemas/schema-adalightapa102.json index 8dacc228..339c5230 100644 --- a/libsrc/leddevice/schemas/schema-adalightapa102.json +++ b/libsrc/leddevice/schemas/schema-adalightapa102.json @@ -19,6 +19,13 @@ "default": 250, "append" : "ms", "propertyOrder" : 3 + }, + "rewriteTime": { + "type": "integer", + "title":"refresh time", + "default": 5000, + "append" : "ms", + "propertyOrder" : 4 } }, "additionalProperties": true diff --git a/libsrc/leddevice/schemas/schema-atmo.json b/libsrc/leddevice/schemas/schema-atmo.json index 8dacc228..339c5230 100644 --- a/libsrc/leddevice/schemas/schema-atmo.json +++ b/libsrc/leddevice/schemas/schema-atmo.json @@ -19,6 +19,13 @@ "default": 250, "append" : "ms", "propertyOrder" : 3 + }, + "rewriteTime": { + "type": "integer", + "title":"refresh time", + "default": 5000, + "append" : "ms", + "propertyOrder" : 4 } }, "additionalProperties": true diff --git a/libsrc/leddevice/schemas/schema-dmx.json b/libsrc/leddevice/schemas/schema-dmx.json index 4d2ab738..6ea9e2a2 100644 --- a/libsrc/leddevice/schemas/schema-dmx.json +++ b/libsrc/leddevice/schemas/schema-dmx.json @@ -23,6 +23,13 @@ "title":"Delay after connect", "default": 250, "propertyOrder" : 3 + }, + "rewriteTime": { + "type": "integer", + "title":"refresh time", + "default": 5000, + "append" : "ms", + "propertyOrder" : 4 } }, "additionalProperties": true diff --git a/libsrc/leddevice/schemas/schema-sedu.json b/libsrc/leddevice/schemas/schema-sedu.json index 4755a475..f559ca99 100644 --- a/libsrc/leddevice/schemas/schema-sedu.json +++ b/libsrc/leddevice/schemas/schema-sedu.json @@ -18,6 +18,13 @@ "title":"Delay after connect", "default": 250, "propertyOrder" : 3 + }, + "rewriteTime": { + "type": "integer", + "title":"refresh time", + "default": 5000, + "append" : "ms", + "propertyOrder" : 4 } }, "additionalProperties": true diff --git a/libsrc/leddevice/schemas/schema-tpm2.json b/libsrc/leddevice/schemas/schema-tpm2.json index 4755a475..f559ca99 100644 --- a/libsrc/leddevice/schemas/schema-tpm2.json +++ b/libsrc/leddevice/schemas/schema-tpm2.json @@ -18,6 +18,13 @@ "title":"Delay after connect", "default": 250, "propertyOrder" : 3 + }, + "rewriteTime": { + "type": "integer", + "title":"refresh time", + "default": 5000, + "append" : "ms", + "propertyOrder" : 4 } }, "additionalProperties": true