From e1857746f887c72df3174b3d6942d6dcf8046d5a Mon Sep 17 00:00:00 2001 From: BPoH_Voodoo Date: Thu, 8 Feb 2018 08:27:43 +0100 Subject: [PATCH 001/106] Changes to Support RGBW SK6812 / Status Updates for WebInterface after reload Implemented white support for SK6812 and other RGBW Stripes Implemented reload of values in the web interface, after closing or reload. --- Arduino/McLighting/McLighting.ino | 28 +- Arduino/McLighting/WS2812FX.cpp | 1355 +++++++++++++++++++++++++ Arduino/McLighting/WS2812FX.h | 433 ++++++++ Arduino/McLighting/colormodes.h | 2 +- Arduino/McLighting/definitions.h | 35 +- Arduino/McLighting/request_handlers.h | 60 +- clients/web/build/index.htm | 102 +- 7 files changed, 1932 insertions(+), 83 deletions(-) create mode 100644 Arduino/McLighting/WS2812FX.cpp create mode 100644 Arduino/McLighting/WS2812FX.h diff --git a/Arduino/McLighting/McLighting.ino b/Arduino/McLighting/McLighting.ino index 91acd0e4..f954834e 100644 --- a/Arduino/McLighting/McLighting.ino +++ b/Arduino/McLighting/McLighting.ino @@ -44,8 +44,8 @@ WebSocketsServer webSocket = WebSocketsServer(81); // Load libraries / Instanciate WS2812FX library // *************************************************************************** // https://github.com/kitesurfer1404/WS2812FX -#include -WS2812FX strip = WS2812FX(NUMLEDS, PIN, NEO_GRB + NEO_KHZ800); +#include "WS2812FX.h" +WS2812FX strip = WS2812FX(NUMLEDS, PIN, NEO_GRBW + NEO_KHZ800); // Parameter 1 = number of pixels in strip // Parameter 2 = Arduino pin number (most are valid) @@ -138,7 +138,7 @@ void configModeCallback (WiFiManager *myWiFiManager) { uint16_t i; for (i = 0; i < strip.numPixels(); i++) { - strip.setPixelColor(i, 0, 0, 255); + strip.setPixelColor(i, 0, 0, 0, 255); } strip.show(); } @@ -191,7 +191,7 @@ void setup() { strip.setBrightness(brightness); strip.setSpeed(convertSpeed(ws2812fx_speed)); //strip.setMode(FX_MODE_RAINBOW_CYCLE); - strip.setColor(main_color.red, main_color.green, main_color.blue); + strip.setColor(main_color.white, main_color.red, main_color.green, main_color.blue); strip.start(); // *************************************************************************** @@ -493,7 +493,7 @@ void setup() { }); server.on("/get_color", []() { - String rgbcolor = String(main_color.red, HEX) + String(main_color.green, HEX) + String(main_color.blue, HEX); + String rgbcolor = String(main_color.white, HEX) + String(main_color.red, HEX) + String(main_color.green, HEX) + String(main_color.blue, HEX); server.send(200, "text/plain", rgbcolor ); DBG_OUTPUT_PORT.print("/get_color: "); DBG_OUTPUT_PORT.println(rgbcolor); @@ -578,13 +578,13 @@ void setup() { #ifdef ENABLE_STATE_SAVE // Load state string from EEPROM - String saved_state_string = readEEPROM(256, 32); + String saved_state_string = readEEPROM(256, 36); String chk = getValue(saved_state_string, '|', 0); if (chk == "STA") { DBG_OUTPUT_PORT.printf("Found saved state: %s\n", saved_state_string.c_str()); setModeByStateString(saved_state_string); } - sprintf(last_state, "STA|%2d|%3d|%3d|%3d|%3d|%3d|%3d", mode, ws2812fx_mode, ws2812fx_speed, brightness, main_color.red, main_color.green, main_color.blue); + sprintf(last_state, "STA|%2d|%3d|%3d|%3d|%3d|%3d|%3d|%3d", mode, ws2812fx_mode, ws2812fx_speed, brightness, main_color.white, main_color.red, main_color.green, main_color.blue); #endif } @@ -617,17 +617,17 @@ void loop() { mode = HOLD; } if (mode == OFF) { - strip.setColor(0,0,0); + strip.setColor(0,0,0,0); strip.setMode(FX_MODE_STATIC); // mode = HOLD; } if (mode == ALL) { - strip.setColor(main_color.red, main_color.green, main_color.blue); + strip.setColor(main_color.white, main_color.red, main_color.green, main_color.blue); strip.setMode(FX_MODE_STATIC); mode = HOLD; } if (mode == WIPE) { - strip.setColor(main_color.red, main_color.green, main_color.blue); + strip.setColor(main_color.white, main_color.red, main_color.green, main_color.blue); strip.setMode(FX_MODE_COLOR_WIPE); mode = HOLD; } @@ -640,12 +640,12 @@ void loop() { mode = HOLD; } if (mode == THEATERCHASE) { - strip.setColor(main_color.red, main_color.green, main_color.blue); + strip.setColor(main_color.white, main_color.red, main_color.green, main_color.blue); strip.setMode(FX_MODE_THEATER_CHASE); mode = HOLD; } if (mode == TWINKLERANDOM) { - strip.setColor(main_color.red, main_color.green, main_color.blue); + strip.setColor(main_color.white, main_color.red, main_color.green, main_color.blue); strip.setMode(FX_MODE_TWINKLE_RANDOM); mode = HOLD; } @@ -670,7 +670,7 @@ void loop() { #ifdef ENABLE_STATE_SAVE // Check for state changes - sprintf(current_state, "STA|%2d|%3d|%3d|%3d|%3d|%3d|%3d", mode, strip.getMode(), ws2812fx_speed, brightness, main_color.red, main_color.green, main_color.blue); + sprintf(current_state, "STA|%2d|%3d|%3d|%3d|%3d|%3d|%3d|%3d", mode, strip.getMode(), ws2812fx_speed, brightness, main_color.white, main_color.red, main_color.green, main_color.blue); if (strcmp(current_state, last_state) != 0) { // DBG_OUTPUT_PORT.printf("STATE CHANGED: %s / %s\n", last_state, current_state); @@ -681,7 +681,7 @@ void loop() { if (state_save_requested && time_statechange + timeout_statechange_save <= millis()) { time_statechange = 0; state_save_requested = false; - writeEEPROM(256, 32, last_state); // 256 --> last_state (reserved 32 bytes) + writeEEPROM(256, 36, last_state); // 256 --> last_state (reserved 32 bytes) EEPROM.commit(); } #endif diff --git a/Arduino/McLighting/WS2812FX.cpp b/Arduino/McLighting/WS2812FX.cpp new file mode 100644 index 00000000..eb234f83 --- /dev/null +++ b/Arduino/McLighting/WS2812FX.cpp @@ -0,0 +1,1355 @@ +/* + WS2812FX.cpp - Library for WS2812 LED effects. + + Harm Aldick - 2016 + www.aldick.org + + + FEATURES + * A lot of blinken modes and counting + * WS2812FX can be used as drop-in replacement for Adafruit Neopixel Library + + NOTES + * Uses the Adafruit Neopixel library. Get it here: + https://github.com/adafruit/Adafruit_NeoPixel + + + + LICENSE + + The MIT License (MIT) + + Copyright (c) 2016 Harm Aldick + + 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. + + + CHANGELOG + + 2016-05-28 Initial beta release + 2016-06-03 Code cleanup, minor improvements, new modes + 2016-06-04 2 new fx, fixed setColor (now also resets _mode_color) + 2017-02-02 added external trigger functionality (e.g. for sound-to-light) + 2017-02-02 removed "blackout" on mode, speed or color-change + 2017-09-26 implemented segment and reverse features + 2017-11-16 changed speed calc, reduced memory footprint +*/ + +#include "WS2812FX.h" + +void WS2812FX::init() { + RESET_RUNTIME; + Adafruit_NeoPixel::begin(); + setBrightness(_brightness); + Adafruit_NeoPixel::show(); +} + +void WS2812FX::service() { + if(_running || _triggered) { + unsigned long now = millis(); // Be aware, millis() rolls over every 49 days + bool doShow = false; + for(uint8_t i=0; i < _num_segments; i++) { + _segment_index = i; + if(now > SEGMENT_RUNTIME.next_time || _triggered) { + doShow = true; + uint16_t delay = (this->*_mode[SEGMENT.mode])(); + SEGMENT_RUNTIME.next_time = now + max((int)delay, SPEED_MIN); + SEGMENT_RUNTIME.counter_mode_call++; + } + } + if(doShow) { + delay(1); // for ESP32 (see https://forums.adafruit.com/viewtopic.php?f=47&t=117327) + Adafruit_NeoPixel::show(); + } + _triggered = false; + } +} + +void WS2812FX::start() { + RESET_RUNTIME; + _running = true; +} + +void WS2812FX::stop() { + _running = false; + strip_off(); +} + +void WS2812FX::trigger() { + _triggered = true; +} + +void WS2812FX::setMode(uint8_t m) { + RESET_RUNTIME; + _segments[0].mode = constrain(m, 0, MODE_COUNT - 1); + setBrightness(_brightness); +} + +void WS2812FX::setSpeed(uint16_t s) { + RESET_RUNTIME; + _segments[0].speed = constrain(s, SPEED_MIN, SPEED_MAX); +} + +void WS2812FX::increaseSpeed(uint8_t s) { + uint16_t newSpeed = constrain(SEGMENT.speed + s, SPEED_MIN, SPEED_MAX); + setSpeed(newSpeed); +} + +void WS2812FX::decreaseSpeed(uint8_t s) { + uint16_t newSpeed = constrain(SEGMENT.speed - s, SPEED_MIN, SPEED_MAX); + setSpeed(newSpeed); +} + +void WS2812FX::setColor(uint8_t w, uint8_t r, uint8_t g, uint8_t b) { + setColor(((uint32_t)w << 24) | ((uint32_t)r << 16)| ((uint32_t)g << 8) | b); +} + +void WS2812FX::setColor(uint32_t c) { + RESET_RUNTIME; + _segments[0].colors[0] = c; + setBrightness(_brightness); +} + +void WS2812FX::setBrightness(uint8_t b) { + _brightness = constrain(b, BRIGHTNESS_MIN, BRIGHTNESS_MAX); + Adafruit_NeoPixel::setBrightness(_brightness); + Adafruit_NeoPixel::show(); + delay(1); +} + +void WS2812FX::increaseBrightness(uint8_t s) { + s = constrain(_brightness + s, BRIGHTNESS_MIN, BRIGHTNESS_MAX); + setBrightness(s); +} + +void WS2812FX::decreaseBrightness(uint8_t s) { + s = constrain(_brightness - s, BRIGHTNESS_MIN, BRIGHTNESS_MAX); + setBrightness(s); +} + +void WS2812FX::setLength(uint16_t b) { + RESET_RUNTIME; + if (b < 1) b = 1; + + // Decrease numLEDs to maximum available memory + do { + Adafruit_NeoPixel::updateLength(b); + b--; + } while(!Adafruit_NeoPixel::numLEDs && b > 1); + + _segments[0].start = 0; + _segments[0].stop = Adafruit_NeoPixel::numLEDs - 1; +} + +void WS2812FX::increaseLength(uint16_t s) { + s = _segments[0].stop - _segments[0].start + 1 + s; + setLength(s); +} + +void WS2812FX::decreaseLength(uint16_t s) { + if (s > _segments[0].stop - _segments[0].start + 1) s = 1; + s = _segments[0].stop - _segments[0].start + 1 - s; + + for(uint16_t i=_segments[0].start + s; i <= (_segments[0].stop - _segments[0].start + 1); i++) { + Adafruit_NeoPixel::setPixelColor(i, 0); + } + Adafruit_NeoPixel::show(); + + setLength(s); +} + +boolean WS2812FX::isRunning() { + return _running; +} + +uint8_t WS2812FX::getMode(void) { + return _segments[0].mode; +} + +uint16_t WS2812FX::getSpeed(void) { + return _segments[0].speed; +} + +uint8_t WS2812FX::getBrightness(void) { + return _brightness; +} + +uint16_t WS2812FX::getLength(void) { + return _segments[0].stop - _segments[0].start + 1; +} + +uint8_t WS2812FX::getModeCount(void) { + return MODE_COUNT; +} + +uint8_t WS2812FX::getNumSegments(void) { + return _num_segments; +} + +void WS2812FX::setNumSegments(uint8_t n) { + _num_segments = n; +} + +uint32_t WS2812FX::getColor(void) { + return _segments[0].colors[0]; +} + +WS2812FX::segment* WS2812FX::getSegments(void) { + return _segments; +} + +const __FlashStringHelper* WS2812FX::getModeName(uint8_t m) { + if(m < MODE_COUNT) { + return _name[m]; + } else { + return F(""); + } +} + +void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, bool reverse) { + if(n < MAX_NUM_SEGMENTS) { + if(n + 1 > _num_segments) _num_segments = n + 1; + _segments[n].start = start; + _segments[n].stop = stop; + _segments[n].mode = mode; + _segments[n].speed = speed; + _segments[n].reverse = reverse; + _segments[n].colors[0] = color; + } +} + +void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed, bool reverse) { + if(n < MAX_NUM_SEGMENTS) { + if(n + 1 > _num_segments) _num_segments = n + 1; + _segments[n].start = start; + _segments[n].stop = stop; + _segments[n].mode = mode; + _segments[n].speed = speed; + _segments[n].reverse = reverse; + + for(uint8_t i=0; i g -> b -> back to r + * Inspired by the Adafruit examples. + */ +uint32_t WS2812FX::color_wheel(uint8_t pos) { + pos = 255 - pos; + if(pos < 85) { + return ((uint32_t)(255 - pos * 3) << 16) | ((uint32_t)(0) << 8) | (pos * 3); + } else if(pos < 170) { + pos -= 85; + return ((uint32_t)(0) << 16) | ((uint32_t)(pos * 3) << 8) | (255 - pos * 3); + } else { + pos -= 170; + return ((uint32_t)(pos * 3) << 16) | ((uint32_t)(255 - pos * 3) << 8) | (0); + } +} + + +/* + * Returns a new, random wheel index with a minimum distance of 42 from pos. + */ +uint8_t WS2812FX::get_random_wheel_index(uint8_t pos) { + uint8_t r = 0; + uint8_t x = 0; + uint8_t y = 0; + uint8_t d = 0; + + while(d < 42) { + r = random(256); + x = abs(pos - r); + y = 255 - x; + d = min(x, y); + } + + return r; +} + + +/* + * No blinking. Just plain old static light. + */ +uint16_t WS2812FX::mode_static(void) { + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + Adafruit_NeoPixel::setPixelColor(i, SEGMENT.colors[0]); + } + return 500; +} + + +/* + * Blink/strobe function + * Alternate between color1 and color2 + * if(strobe == true) then create a strobe effect + */ +uint16_t WS2812FX::blink(uint32_t color1, uint32_t color2, bool strobe) { + uint32_t color = ((SEGMENT_RUNTIME.counter_mode_call & 1) == 0) ? color1 : color2; + if(SEGMENT.reverse) color = (color == color1) ? color2 : color1; + + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + Adafruit_NeoPixel::setPixelColor(i, color); + } + + if((SEGMENT_RUNTIME.counter_mode_call & 1) == 0) { + return strobe ? 20 : (SEGMENT.speed / 2); + } else { + return strobe ? SEGMENT.speed - 20 : (SEGMENT.speed / 2); + } +} + + +/* + * Normal blinking. 50% on/off time. + */ +uint16_t WS2812FX::mode_blink(void) { + return blink(SEGMENT.colors[0], SEGMENT.colors[1], false); +} + + +/* + * Classic Blink effect. Cycling through the rainbow. + */ +uint16_t WS2812FX::mode_blink_rainbow(void) { + return blink(color_wheel(SEGMENT_RUNTIME.counter_mode_call & 0xFF), SEGMENT.colors[1], false); +} + + +/* + * Classic Strobe effect. + */ +uint16_t WS2812FX::mode_strobe(void) { + return blink(SEGMENT.colors[0], SEGMENT.colors[1], true); +} + + +/* + * Classic Strobe effect. Cycling through the rainbow. + */ +uint16_t WS2812FX::mode_strobe_rainbow(void) { + return blink(color_wheel(SEGMENT_RUNTIME.counter_mode_call & 0xFF), SEGMENT.colors[1], true); +} + + +/* + * Color wipe function + * LEDs are turned on (color1) in sequence, then turned off (color2) in sequence. + * if (bool rev == true) then LEDs are turned off in reverse order + */ +uint16_t WS2812FX::color_wipe(uint32_t color1, uint32_t color2, bool rev) { + if(SEGMENT_RUNTIME.counter_mode_step < SEGMENT_LENGTH) { + uint32_t led_offset = SEGMENT_RUNTIME.counter_mode_step; + if(SEGMENT.reverse) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop - led_offset, color1); + } else { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + led_offset, color1); + } + } else { + uint32_t led_offset = SEGMENT_RUNTIME.counter_mode_step - SEGMENT_LENGTH; + if((SEGMENT.reverse && !rev) || (!SEGMENT.reverse && rev)) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop - led_offset, color2); + } else { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + led_offset, color2); + } + } + + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % (SEGMENT_LENGTH * 2); + return (SEGMENT.speed / (SEGMENT_LENGTH * 2)); +} + +/* + * Lights all LEDs one after another. + */ +uint16_t WS2812FX::mode_color_wipe(void) { + return color_wipe(SEGMENT.colors[0], SEGMENT.colors[1], false); +} + +uint16_t WS2812FX::mode_color_wipe_inv(void) { + return color_wipe(SEGMENT.colors[1], SEGMENT.colors[0], false); +} + +uint16_t WS2812FX::mode_color_wipe_rev(void) { + return color_wipe(SEGMENT.colors[0], SEGMENT.colors[1], true); +} + +uint16_t WS2812FX::mode_color_wipe_rev_inv(void) { + return color_wipe(SEGMENT.colors[1], SEGMENT.colors[0], true); +} + + +/* + * Turns all LEDs after each other to a random color. + * Then starts over with another color. + */ +uint16_t WS2812FX::mode_color_wipe_random(void) { + if(SEGMENT_RUNTIME.counter_mode_step % SEGMENT_LENGTH == 0) { // aux_param will store our random color wheel index + SEGMENT_RUNTIME.aux_param = get_random_wheel_index(SEGMENT_RUNTIME.aux_param); + } + uint32_t color = color_wheel(SEGMENT_RUNTIME.aux_param); + return color_wipe(color, color, false) * 2; +} + + +/* + * Random color intruduced alternating from start and end of strip. + */ +uint16_t WS2812FX::mode_color_sweep_random(void) { + if(SEGMENT_RUNTIME.counter_mode_step % SEGMENT_LENGTH == 0) { // aux_param will store our random color wheel index + SEGMENT_RUNTIME.aux_param = get_random_wheel_index(SEGMENT_RUNTIME.aux_param); + } + uint32_t color = color_wheel(SEGMENT_RUNTIME.aux_param); + return color_wipe(color, color, true) * 2; +} + + +/* + * Lights all LEDs in one random color up. Then switches them + * to the next random color. + */ +uint16_t WS2812FX::mode_random_color(void) { + SEGMENT_RUNTIME.aux_param = get_random_wheel_index(SEGMENT_RUNTIME.aux_param); // aux_param will store our random color wheel index + uint32_t color = color_wheel(SEGMENT_RUNTIME.aux_param); + + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + Adafruit_NeoPixel::setPixelColor(i, color); + } + return (SEGMENT.speed); +} + + +/* + * Lights every LED in a random color. Changes one random LED after the other + * to another random color. + */ +uint16_t WS2812FX::mode_single_dynamic(void) { + if(SEGMENT_RUNTIME.counter_mode_call == 0) { + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + Adafruit_NeoPixel::setPixelColor(i, color_wheel(random(256))); + } + } + + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + random(SEGMENT_LENGTH), color_wheel(random(256))); + return (SEGMENT.speed); +} + + +/* + * Lights every LED in a random color. Changes all LED at the same time + * to new random colors. + */ +uint16_t WS2812FX::mode_multi_dynamic(void) { + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + Adafruit_NeoPixel::setPixelColor(i, color_wheel(random(256))); + } + return (SEGMENT.speed); +} + + +/* + * Does the "standby-breathing" of well known i-Devices. Fixed Speed. + * Use mode "fade" if you like to have something similar with a different speed. + */ +uint16_t WS2812FX::mode_breath(void) { + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // step + uint16_t breath_delay_steps[] = { 7, 9, 13, 15, 16, 17, 18, 930, 19, 18, 15, 13, 9, 7, 4, 5, 10 }; // magic numbers for breathing LED + uint8_t breath_brightness_steps[] = { 150, 125, 100, 75, 50, 25, 16, 15, 16, 25, 50, 75, 100, 125, 150, 220, 255 }; // even more magic numbers! + + if(SEGMENT_RUNTIME.counter_mode_call == 0) { + SEGMENT_RUNTIME.aux_param = breath_brightness_steps[0] + 1; // we use aux_param to store the brightness + } + + uint8_t breath_brightness = SEGMENT_RUNTIME.aux_param; + + if(SEGMENT_RUNTIME.counter_mode_step < 8) { + breath_brightness--; + } else { + breath_brightness++; + } + + // update index of current delay when target brightness is reached, start over after the last step + if(breath_brightness == breath_brightness_steps[SEGMENT_RUNTIME.counter_mode_step]) { + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % (sizeof(breath_brightness_steps)/sizeof(uint8_t)); + } + + int lum = map(breath_brightness, 0, 255, 0, _brightness); // keep luminosity below brightness set by user + uint8_t w = (SEGMENT.colors[0] >> 24 & 0xFF) * lum / _brightness; // modify RGBW colors with brightness info + uint8_t r = (SEGMENT.colors[0] >> 16 & 0xFF) * lum / _brightness; + uint8_t g = (SEGMENT.colors[0] >> 8 & 0xFF) * lum / _brightness; + uint8_t b = (SEGMENT.colors[0] & 0xFF) * lum / _brightness; + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + Adafruit_NeoPixel::setPixelColor(i, r, g, b, w); + } + + SEGMENT_RUNTIME.aux_param = breath_brightness; + return breath_delay_steps[SEGMENT_RUNTIME.counter_mode_step]; +} + + +/* + * Fades the LEDs on and (almost) off again. + */ +uint16_t WS2812FX::mode_fade(void) { + int lum = SEGMENT_RUNTIME.counter_mode_step - 31; + lum = 63 - (abs(lum) * 2); + lum = map(lum, 0, 64, min(25, (int)_brightness), _brightness); + + uint8_t w = (SEGMENT.colors[0] >> 24 & 0xFF) * lum / _brightness; // modify RGBW colors with brightness info + uint8_t r = (SEGMENT.colors[0] >> 16 & 0xFF) * lum / _brightness; + uint8_t g = (SEGMENT.colors[0] >> 8 & 0xFF) * lum / _brightness; + uint8_t b = (SEGMENT.colors[0] & 0xFF) * lum / _brightness; + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + Adafruit_NeoPixel::setPixelColor(i, r, g, b, w); + } + + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % 64; + return (SEGMENT.speed / 64); +} + + +/* + * Runs a single pixel back and forth. + */ +uint16_t WS2812FX::mode_scan(void) { + if(SEGMENT_RUNTIME.counter_mode_step > (SEGMENT_LENGTH * 2) - 3) { + SEGMENT_RUNTIME.counter_mode_step = 0; + } + + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + Adafruit_NeoPixel::setPixelColor(i, BLACK); + } + + int led_offset = SEGMENT_RUNTIME.counter_mode_step - (SEGMENT_LENGTH - 1); + led_offset = abs(led_offset); + + if(SEGMENT.reverse) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop - led_offset, SEGMENT.colors[0]); + } else { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + led_offset, SEGMENT.colors[0]); + } + + SEGMENT_RUNTIME.counter_mode_step++; + return (SEGMENT.speed / (SEGMENT_LENGTH * 2)); +} + + +/* + * Runs two pixel back and forth in opposite directions. + */ +uint16_t WS2812FX::mode_dual_scan(void) { + if(SEGMENT_RUNTIME.counter_mode_step > (SEGMENT_LENGTH * 2) - 3) { + SEGMENT_RUNTIME.counter_mode_step = 0; + } + + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + Adafruit_NeoPixel::setPixelColor(i, BLACK); + } + + int led_offset = SEGMENT_RUNTIME.counter_mode_step - (SEGMENT_LENGTH - 1); + led_offset = abs(led_offset); + + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + led_offset, SEGMENT.colors[0]); + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + SEGMENT_LENGTH - led_offset - 1, SEGMENT.colors[0]); + + SEGMENT_RUNTIME.counter_mode_step++; + return (SEGMENT.speed / (SEGMENT_LENGTH * 2)); +} + + +/* + * Cycles all LEDs at once through a rainbow. + */ +uint16_t WS2812FX::mode_rainbow(void) { + uint32_t color = color_wheel(SEGMENT_RUNTIME.counter_mode_step); + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + Adafruit_NeoPixel::setPixelColor(i, color); + } + + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) & 0xFF; + return (SEGMENT.speed / 256); +} + + +/* + * Cycles a rainbow over the entire string of LEDs. + */ +uint16_t WS2812FX::mode_rainbow_cycle(void) { + for(uint16_t i=0; i < SEGMENT_LENGTH; i++) { + uint32_t color = color_wheel(((i * 256 / SEGMENT_LENGTH) + SEGMENT_RUNTIME.counter_mode_step) & 0xFF); + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + i, color); + } + + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) & 0xFF; + return (SEGMENT.speed / 256); +} + + +/* + * theater chase function + */ +uint16_t WS2812FX::theater_chase(uint32_t color1, uint32_t color2) { + SEGMENT_RUNTIME.counter_mode_call = SEGMENT_RUNTIME.counter_mode_call % 3; + for(uint16_t i=0; i < SEGMENT_LENGTH; i++) { + if((i % 3) == SEGMENT_RUNTIME.counter_mode_call) { + if(SEGMENT.reverse) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop - i, color1); + } else { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + i, color1); + } + } else { + if(SEGMENT.reverse) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop - i, color2); + } else { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + i, color2); + } + } + } + + return (SEGMENT.speed / SEGMENT_LENGTH); +} + + +/* + * Theatre-style crawling lights. + * Inspired by the Adafruit examples. + */ +uint16_t WS2812FX::mode_theater_chase(void) { + return theater_chase(SEGMENT.colors[0], BLACK); +} + + +/* + * Theatre-style crawling lights with rainbow effect. + * Inspired by the Adafruit examples. + */ +uint16_t WS2812FX::mode_theater_chase_rainbow(void) { + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) & 0xFF; + return theater_chase(color_wheel(SEGMENT_RUNTIME.counter_mode_step), BLACK); +} + + +/* + * Running lights effect with smooth sine transition. + */ +uint16_t WS2812FX::mode_running_lights(void) { + uint8_t w = ((SEGMENT.colors[0] >> 24) & 0xFF); + uint8_t r = ((SEGMENT.colors[0] >> 16) & 0xFF); + uint8_t g = ((SEGMENT.colors[0] >> 8) & 0xFF); + uint8_t b = (SEGMENT.colors[0] & 0xFF); + + float radPerLed = (2.0 * 3.14159) / SEGMENT_LENGTH; + for(uint16_t i=0; i < SEGMENT_LENGTH; i++) { + int lum = map((int)(sin((i + SEGMENT_RUNTIME.counter_mode_step) * radPerLed) * 128), -128, 128, 0, 255); + if(SEGMENT.reverse) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + i, (r * lum) / 256, (g * lum) / 256, (b * lum) / 256, (w * lum) / 256); + } else { + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop - i, (r * lum) / 256, (g * lum) / 256, (b * lum) / 256, (w * lum) / 256); + } + } + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % SEGMENT_LENGTH; + return (SEGMENT.speed / SEGMENT_LENGTH); +} + + +/* + * twinkle function + */ +uint16_t WS2812FX::twinkle(uint32_t color) { + if(SEGMENT_RUNTIME.counter_mode_step == 0) { + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + Adafruit_NeoPixel::setPixelColor(i, BLACK); + } + uint16_t min_leds = max(1, SEGMENT_LENGTH / 5); // make sure, at least one LED is on + uint16_t max_leds = max(1, SEGMENT_LENGTH / 2); // make sure, at least one LED is on + SEGMENT_RUNTIME.counter_mode_step = random(min_leds, max_leds); + } + + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + random(SEGMENT_LENGTH), color); + + SEGMENT_RUNTIME.counter_mode_step--; + return (SEGMENT.speed / SEGMENT_LENGTH); +} + +/* + * Blink several LEDs on, reset, repeat. + * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ + */ +uint16_t WS2812FX::mode_twinkle(void) { + return twinkle(SEGMENT.colors[0]); +} + +/* + * Blink several LEDs in random colors on, reset, repeat. + * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ + */ +uint16_t WS2812FX::mode_twinkle_random(void) { + return twinkle(color_wheel(random(256))); +} + + +/* + * fade out function + * fades out the current segment by dividing each pixel's intensity by 2 + */ +void WS2812FX::fade_out() { + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + uint32_t color = Adafruit_NeoPixel::getPixelColor(i); + color = (color >> 1) & 0x7F7F7F7F; + Adafruit_NeoPixel::setPixelColor(i, color); + } +} + +/* + * twinkle_fade function + */ +uint16_t WS2812FX::twinkle_fade(uint32_t color) { + fade_out(); + + if(random(3) == 0) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + random(SEGMENT_LENGTH), color); + } + return (SEGMENT.speed / 8); +} + + +/* + * Blink several LEDs on, fading out. + */ +uint16_t WS2812FX::mode_twinkle_fade(void) { + return twinkle_fade(SEGMENT.colors[0]); +} + + +/* + * Blink several LEDs in random colors on, fading out. + */ +uint16_t WS2812FX::mode_twinkle_fade_random(void) { + return twinkle_fade(color_wheel(random(256))); +} + + +/* + * Blinks one LED at a time. + * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ + */ +uint16_t WS2812FX::mode_sparkle(void) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.aux_param, BLACK); + SEGMENT_RUNTIME.aux_param = random(SEGMENT_LENGTH); // aux_param stores the random led index + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.aux_param, SEGMENT.colors[0]); + return (SEGMENT.speed / SEGMENT_LENGTH); +} + + +/* + * Lights all LEDs in the color. Flashes single white pixels randomly. + * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ + */ +uint16_t WS2812FX::mode_flash_sparkle(void) { + if(SEGMENT_RUNTIME.counter_mode_call == 0) { + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + Adafruit_NeoPixel::setPixelColor(i, SEGMENT.colors[0]); + } + } + + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.aux_param, SEGMENT.colors[0]); + + if(random(5) == 0) { + SEGMENT_RUNTIME.aux_param = random(SEGMENT_LENGTH); // aux_param stores the random led index + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.aux_param, WHITE); + return 20; + } + return SEGMENT.speed; +} + + +/* + * Like flash sparkle. With more flash. + * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ + */ +uint16_t WS2812FX::mode_hyper_sparkle(void) { + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + Adafruit_NeoPixel::setPixelColor(i, SEGMENT.colors[0]); + } + + if(random(5) < 2) { + for(uint16_t i=0; i < max(1, SEGMENT_LENGTH/3); i++) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + random(SEGMENT_LENGTH), WHITE); + } + return 20; + } + return SEGMENT.speed; +} + + +/* + * Strobe effect with different strobe count and pause, controlled by speed. + */ +uint16_t WS2812FX::mode_multi_strobe(void) { + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + Adafruit_NeoPixel::setPixelColor(i, BLACK); + } + + uint16_t delay = SEGMENT.speed / (2 * ((SEGMENT.speed / 10) + 1)); + if(SEGMENT_RUNTIME.counter_mode_step < (2 * ((SEGMENT.speed / 10) + 1))) { + if((SEGMENT_RUNTIME.counter_mode_step & 1) == 0) { + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + Adafruit_NeoPixel::setPixelColor(i, SEGMENT.colors[0]); + } + delay = 20; + } else { + delay = 50; + } + } + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % ((2 * ((SEGMENT.speed / 10) + 1)) + 1); + return delay; +} + + +/* + * color chase function. + * color1 = background color + * color2 and color3 = colors of two adjacent leds + */ + +uint16_t WS2812FX::chase(uint32_t color1, uint32_t color2, uint32_t color3) { + uint16_t a = SEGMENT_RUNTIME.counter_mode_step; + uint16_t b = (a + 1) % SEGMENT_LENGTH; + uint16_t c = (b + 1) % SEGMENT_LENGTH; + if(SEGMENT.reverse) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop - a, color1); + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop - b, color2); + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop - c, color3); + } else { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + a, color1); + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + b, color2); + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + c, color3); + } + + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % SEGMENT_LENGTH; + return (SEGMENT.speed / SEGMENT_LENGTH); +} + + +/* + * Bicolor chase mode + */ +uint16_t WS2812FX::mode_bicolor_chase(void) { + return chase(SEGMENT.colors[0], SEGMENT.colors[1], SEGMENT.colors[2]); +} + + +/* + * White running on _color. + */ +uint16_t WS2812FX::mode_chase_color(void) { + return chase(SEGMENT.colors[0], WHITE, WHITE); +} + + +/* + * Black running on _color. + */ +uint16_t WS2812FX::mode_chase_blackout(void) { + return chase(SEGMENT.colors[0], BLACK, BLACK); +} + + +/* + * _color running on white. + */ +uint16_t WS2812FX::mode_chase_white(void) { + return chase(WHITE, SEGMENT.colors[0], SEGMENT.colors[0]); +} + + +/* + * White running followed by random color. + */ +uint16_t WS2812FX::mode_chase_random(void) { + if(SEGMENT_RUNTIME.counter_mode_step == 0) { + SEGMENT_RUNTIME.aux_param = get_random_wheel_index(SEGMENT_RUNTIME.aux_param); + } + return chase(color_wheel(SEGMENT_RUNTIME.aux_param), WHITE, WHITE); +} + + +/* + * Rainbow running on white. + */ +uint16_t WS2812FX::mode_chase_rainbow_white(void) { + uint16_t n = SEGMENT_RUNTIME.counter_mode_step; + uint16_t m = (SEGMENT_RUNTIME.counter_mode_step + 1) % SEGMENT_LENGTH; + uint32_t color2 = color_wheel(((n * 256 / SEGMENT_LENGTH) + (SEGMENT_RUNTIME.counter_mode_call & 0xFF)) & 0xFF); + uint32_t color3 = color_wheel(((m * 256 / SEGMENT_LENGTH) + (SEGMENT_RUNTIME.counter_mode_call & 0xFF)) & 0xFF); + + return chase(WHITE, color2, color3); +} + + +/* + * White running on rainbow. + */ +uint16_t WS2812FX::mode_chase_rainbow(void) { + uint8_t color_sep = 256 / SEGMENT_LENGTH; + uint8_t color_index = SEGMENT_RUNTIME.counter_mode_call & 0xFF; + uint32_t color = color_wheel(((SEGMENT_RUNTIME.counter_mode_step * color_sep) + color_index) & 0xFF); + + return chase(color, WHITE, WHITE); +} + + +/* + * Black running on rainbow. + */ +uint16_t WS2812FX::mode_chase_blackout_rainbow(void) { + uint8_t color_sep = 256 / SEGMENT_LENGTH; + uint8_t color_index = SEGMENT_RUNTIME.counter_mode_call & 0xFF; + uint32_t color = color_wheel(((SEGMENT_RUNTIME.counter_mode_step * color_sep) + color_index) & 0xFF); + + return chase(color, BLACK, BLACK); +} + + +/* + * White flashes running on _color. + */ +uint16_t WS2812FX::mode_chase_flash(void) { + const static uint8_t flash_count = 4; + uint8_t flash_step = SEGMENT_RUNTIME.counter_mode_call % ((flash_count * 2) + 1); + + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + Adafruit_NeoPixel::setPixelColor(i, SEGMENT.colors[0]); + } + + uint16_t delay = (SEGMENT.speed / SEGMENT_LENGTH); + if(flash_step < (flash_count * 2)) { + if(flash_step % 2 == 0) { + uint16_t n = SEGMENT_RUNTIME.counter_mode_step; + uint16_t m = (SEGMENT_RUNTIME.counter_mode_step + 1) % SEGMENT_LENGTH; + if(SEGMENT.reverse) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop - n, WHITE); + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop - m, WHITE); + } else { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + n, WHITE); + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + m, WHITE); + } + delay = 20; + } else { + delay = 30; + } + } else { + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % SEGMENT_LENGTH; + } + return delay; +} + + +/* + * White flashes running, followed by random color. + */ +uint16_t WS2812FX::mode_chase_flash_random(void) { + const static uint8_t flash_count = 4; + uint8_t flash_step = SEGMENT_RUNTIME.counter_mode_call % ((flash_count * 2) + 1); + + for(uint16_t i=0; i < SEGMENT_RUNTIME.counter_mode_step; i++) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + i, color_wheel(SEGMENT_RUNTIME.aux_param)); + } + + uint16_t delay = (SEGMENT.speed / SEGMENT_LENGTH); + if(flash_step < (flash_count * 2)) { + uint16_t n = SEGMENT_RUNTIME.counter_mode_step; + uint16_t m = (SEGMENT_RUNTIME.counter_mode_step + 1) % SEGMENT_LENGTH; + if(flash_step % 2 == 0) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + n, WHITE); + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + m, WHITE); + delay = 20; + } else { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + n, color_wheel(SEGMENT_RUNTIME.aux_param)); + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + m, BLACK); + delay = 30; + } + } else { + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % SEGMENT_LENGTH; + + if(SEGMENT_RUNTIME.counter_mode_step == 0) { + SEGMENT_RUNTIME.aux_param = get_random_wheel_index(SEGMENT_RUNTIME.aux_param); + } + } + return delay; +} + + +/* + * Alternating pixels running function. + */ +uint16_t WS2812FX::running(uint32_t color1, uint32_t color2) { + for(uint16_t i=0; i < SEGMENT_LENGTH; i++) { + if((i + SEGMENT_RUNTIME.counter_mode_step) % 4 < 2) { + if(SEGMENT.reverse) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + i, color1); + } else { + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop - i, color1); + } + } else { + if(SEGMENT.reverse) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + i, color2); + } else { + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop - i, color2); + } + } + } + + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) & 0x3; + return (SEGMENT.speed / SEGMENT_LENGTH); +} + +/* + * Alternating color/white pixels running. + */ +uint16_t WS2812FX::mode_running_color(void) { + return running(SEGMENT.colors[0], WHITE); +} + + +/* + * Alternating red/blue pixels running. + */ +uint16_t WS2812FX::mode_running_red_blue(void) { + return running(RED, BLUE); +} + + +/* + * Alternating red/green pixels running. + */ +uint16_t WS2812FX::mode_merry_christmas(void) { + return running(RED, GREEN); +} + +/* + * Alternating orange/purple pixels running. + */ +uint16_t WS2812FX::mode_halloween(void) { + return running(PURPLE, ORANGE); +} + + +/* + * Random colored pixels running. + */ +uint16_t WS2812FX::mode_running_random(void) { + for(uint16_t i=SEGMENT_LENGTH-1; i > 0; i--) { + if(SEGMENT.reverse) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop - i, Adafruit_NeoPixel::getPixelColor(SEGMENT.stop - i + 1)); + } else { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + i, Adafruit_NeoPixel::getPixelColor(SEGMENT.start + i - 1)); + } + } + + if(SEGMENT_RUNTIME.counter_mode_step == 0) { + SEGMENT_RUNTIME.aux_param = get_random_wheel_index(SEGMENT_RUNTIME.aux_param); + if(SEGMENT.reverse) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop, color_wheel(SEGMENT_RUNTIME.aux_param)); + } else { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start, color_wheel(SEGMENT_RUNTIME.aux_param)); + } + } + + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step == 0) ? 1 : 0; + return (SEGMENT.speed / SEGMENT_LENGTH); +} + + +/* + * K.I.T.T. + */ +uint16_t WS2812FX::mode_larson_scanner(void) { + fade_out(); + + if(SEGMENT_RUNTIME.counter_mode_step < SEGMENT_LENGTH) { + if(SEGMENT.reverse) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop - SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + } else { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + } + } else { + if(SEGMENT.reverse) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop - ((SEGMENT_LENGTH * 2) - SEGMENT_RUNTIME.counter_mode_step) + 2, SEGMENT.colors[0]); + } else { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + ((SEGMENT_LENGTH * 2) - SEGMENT_RUNTIME.counter_mode_step) - 2, SEGMENT.colors[0]); + } + } + + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % ((SEGMENT_LENGTH * 2) - 2); + return (SEGMENT.speed / (SEGMENT_LENGTH * 2)); +} + + +/* + * Firing comets from one end. + */ +uint16_t WS2812FX::mode_comet(void) { + fade_out(); + + if(SEGMENT.reverse) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop - SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + } else { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + } + + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % SEGMENT_LENGTH; + return (SEGMENT.speed / SEGMENT_LENGTH); +} + + +/* + * Fireworks function. + */ +uint16_t WS2812FX::fireworks(uint32_t color) { + fade_out(); + +/* the old way + uint32_t px_rgb = 0; + byte px_r = 0; + byte px_g = 0; + byte px_b = 0; +*/ +uint32_t prevLed, thisLed, nextLed; + +/* the old way + // first LED has only one neighbour + px_r = (((Adafruit_NeoPixel::getPixelColor(SEGMENT.start+1) & 0xFF0000) >> 16) >> 1) + ((Adafruit_NeoPixel::getPixelColor(SEGMENT.start) & 0xFF0000) >> 16); + px_g = (((Adafruit_NeoPixel::getPixelColor(SEGMENT.start+1) & 0x00FF00) >> 8) >> 1) + ((Adafruit_NeoPixel::getPixelColor(SEGMENT.start) & 0x00FF00) >> 8); + px_b = (((Adafruit_NeoPixel::getPixelColor(SEGMENT.start+1) & 0x0000FF) ) >> 1) + ((Adafruit_NeoPixel::getPixelColor(SEGMENT.start) & 0x0000FF)); + Adafruit_NeoPixel::setPixelColor(SEGMENT.start, px_r, px_g, px_b); +*/ + // set brightness(i) = ((brightness(i-1)/2 + brightness(i+1)) / 2) + brightness(i) + for(uint16_t i=SEGMENT.start + 1; i > 2) & 0x3F3F3F3F; + thisLed = Adafruit_NeoPixel::getPixelColor(i); + nextLed = (Adafruit_NeoPixel::getPixelColor(i+1) >> 2) & 0x3F3F3F3F; + Adafruit_NeoPixel::setPixelColor(i, prevLed + thisLed + nextLed); + +/* the old way + px_r = (( + (((Adafruit_NeoPixel::getPixelColor(i-1) & 0xFF0000) >> 16) >> 1) + + (((Adafruit_NeoPixel::getPixelColor(i+1) & 0xFF0000) >> 16) ) ) >> 1) + + (((Adafruit_NeoPixel::getPixelColor(i ) & 0xFF0000) >> 16) ); + + px_g = (( + (((Adafruit_NeoPixel::getPixelColor(i-1) & 0x00FF00) >> 8) >> 1) + + (((Adafruit_NeoPixel::getPixelColor(i+1) & 0x00FF00) >> 8) ) ) >> 1) + + (((Adafruit_NeoPixel::getPixelColor(i ) & 0x00FF00) >> 8) ); + + px_b = (( + (((Adafruit_NeoPixel::getPixelColor(i-1) & 0x0000FF) ) >> 1) + + (((Adafruit_NeoPixel::getPixelColor(i+1) & 0x0000FF) ) ) ) >> 1) + + (((Adafruit_NeoPixel::getPixelColor(i ) & 0x0000FF) ) ); + + Adafruit_NeoPixel::setPixelColor(i, px_r, px_g, px_b); +*/ + } + +/* the old way + // last LED has only one neighbour + px_r = (((Adafruit_NeoPixel::getPixelColor(SEGMENT.stop-1) & 0xFF0000) >> 16) >> 2) + ((Adafruit_NeoPixel::getPixelColor(SEGMENT.stop) & 0xFF0000) >> 16); + px_g = (((Adafruit_NeoPixel::getPixelColor(SEGMENT.stop-1) & 0x00FF00) >> 8) >> 2) + ((Adafruit_NeoPixel::getPixelColor(SEGMENT.stop) & 0x00FF00) >> 8); + px_b = (((Adafruit_NeoPixel::getPixelColor(SEGMENT.stop-1) & 0x0000FF) ) >> 2) + ((Adafruit_NeoPixel::getPixelColor(SEGMENT.stop) & 0x0000FF)); + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop, px_r, px_g, px_b); +*/ + if(!_triggered) { + for(uint16_t i=0; i> 24) & 0xFF; + byte r = (SEGMENT.colors[0] >> 16) & 0xFF; + byte g = (SEGMENT.colors[0] >> 8) & 0xFF; + byte b = (SEGMENT.colors[0] & 0xFF); + byte lum = max(w, max(r, max(g, b))) / rev_intensity; + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + int flicker = random(0, lum); + Adafruit_NeoPixel::setPixelColor(i, max(r - flicker, 0), max(g - flicker, 0), max(b - flicker, 0), max(w - flicker, 0)); + } + return (SEGMENT.speed / SEGMENT_LENGTH); +} + +/* + * Random flickering. + */ +uint16_t WS2812FX::mode_fire_flicker(void) { + return fire_flicker(3); +} + +/* +* Random flickering, less intesity. +*/ +uint16_t WS2812FX::mode_fire_flicker_soft(void) { + return fire_flicker(6); +} + +/* +* Random flickering, more intesity. +*/ +uint16_t WS2812FX::mode_fire_flicker_intense(void) { + return fire_flicker(1.7); +} + + +/* + * Tricolor chase function + */ +uint16_t WS2812FX::tricolor_chase(uint32_t color1, uint32_t color2, uint32_t color3) { + for(uint16_t i=0; i < SEGMENT_LENGTH; i++) { + if((i + SEGMENT_RUNTIME.counter_mode_step) % 6 < 2) { + if(SEGMENT.reverse) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + i, color1); + } else { + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop - i, color1); + } + } else if((i + SEGMENT_RUNTIME.counter_mode_step) % 6 < 4) { + if(SEGMENT.reverse) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + i, color2); + } else { + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop - i, color2); + } + } else { + if(SEGMENT.reverse) { + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + i, color3); + } else { + Adafruit_NeoPixel::setPixelColor(SEGMENT.stop - i, color3); + } + } + } + + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % 6; + return (SEGMENT.speed / SEGMENT_LENGTH); +} + + +/* + * Tricolor chase mode + */ +uint16_t WS2812FX::mode_tricolor_chase(void) { + return tricolor_chase(SEGMENT.colors[0], SEGMENT.colors[1], SEGMENT.colors[2]); +} + + +/* + * Alternating white/red/black pixels running. + */ +uint16_t WS2812FX::mode_circus_combustus(void) { + return tricolor_chase(RED, WHITE, BLACK); +} + +/* + * ICU mode + */ +uint16_t WS2812FX::mode_icu() { + uint16_t dest = SEGMENT_RUNTIME.counter_mode_step & 0xFFFF; + + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + dest, SEGMENT.colors[0]); + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + dest + SEGMENT_LENGTH/2, SEGMENT.colors[0]); + + if(SEGMENT_RUNTIME.aux_param == dest) { // pause between eye movements + if(random(6) == 0) { // blink once in a while + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + dest, 0); + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + dest + SEGMENT_LENGTH/2, 0); + return 200; + } + SEGMENT_RUNTIME.aux_param = random(SEGMENT_LENGTH/2); + return 1000 + random(2000); + } + + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + dest, 0); + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + dest + SEGMENT_LENGTH/2, 0); + + if(SEGMENT_RUNTIME.aux_param > SEGMENT_RUNTIME.counter_mode_step) { + SEGMENT_RUNTIME.counter_mode_step++; + dest++; + } else if (SEGMENT_RUNTIME.aux_param < SEGMENT_RUNTIME.counter_mode_step) { + SEGMENT_RUNTIME.counter_mode_step--; + dest--; + } + + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + dest, SEGMENT.colors[0]); + Adafruit_NeoPixel::setPixelColor(SEGMENT.start + dest + SEGMENT_LENGTH/2, SEGMENT.colors[0]); + + return (SEGMENT.speed / SEGMENT_LENGTH); +} diff --git a/Arduino/McLighting/WS2812FX.h b/Arduino/McLighting/WS2812FX.h new file mode 100644 index 00000000..a3867a12 --- /dev/null +++ b/Arduino/McLighting/WS2812FX.h @@ -0,0 +1,433 @@ +/* + WS2812FX.h - Library for WS2812 LED effects. + + Harm Aldick - 2016 + www.aldick.org + FEATURES + * A lot of blinken modes and counting + * WS2812FX can be used as drop-in replacement for Adafruit Neopixel Library + NOTES + * Uses the Adafruit Neopixel library. Get it here: + https://github.com/adafruit/Adafruit_NeoPixel + LICENSE + The MIT License (MIT) + Copyright (c) 2016 Harm Aldick + 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. + CHANGELOG + 2016-05-28 Initial beta release + 2016-06-03 Code cleanup, minor improvements, new modes + 2016-06-04 2 new fx, fixed setColor (now also resets _mode_color) + 2017-02-02 added external trigger functionality (e.g. for sound-to-light) +*/ + +#ifndef WS2812FX_h +#define WS2812FX_h + +#include + +#define DEFAULT_BRIGHTNESS 50 +#define DEFAULT_MODE 0 +#define DEFAULT_SPEED 1000 +#define DEFAULT_COLOR 0xFF0000 + +#define SPEED_MIN 10 +#define SPEED_MAX 65535 + +#define BRIGHTNESS_MIN 0 +#define BRIGHTNESS_MAX 255 + +#define MAX_NUM_SEGMENTS 10 +#define NUM_COLORS 4 /* number of colors per segment */ +#define SEGMENT _segments[_segment_index] +#define SEGMENT_RUNTIME _segment_runtimes[_segment_index] +#define SEGMENT_LENGTH (SEGMENT.stop - SEGMENT.start + 1) +#define RESET_RUNTIME memset(_segment_runtimes, 0, sizeof(_segment_runtimes)) + +// some common colors +#define RED 0x00FF0000 +#define GREEN 0x0000FF00 +#define BLUE 0x000000FF +#define WHITE 0x00FFFFFF +#define BLACK 0x00000000 +#define YELLOW 0x00FFFF00 +#define CYAN 0x0000FFFF +#define MAGENTA 0x00FF00FF +#define PURPLE 0x00400080 +#define ORANGE 0x00FF3000 +#define ULTRAWHITE 0xFFFFFFFF + +#define MODE_COUNT 56 + +#define FX_MODE_STATIC 0 +#define FX_MODE_BLINK 1 +#define FX_MODE_BREATH 2 +#define FX_MODE_COLOR_WIPE 3 +#define FX_MODE_COLOR_WIPE_INV 4 +#define FX_MODE_COLOR_WIPE_REV 5 +#define FX_MODE_COLOR_WIPE_REV_INV 6 +#define FX_MODE_COLOR_WIPE_RANDOM 7 +#define FX_MODE_RANDOM_COLOR 8 +#define FX_MODE_SINGLE_DYNAMIC 9 +#define FX_MODE_MULTI_DYNAMIC 10 +#define FX_MODE_RAINBOW 11 +#define FX_MODE_RAINBOW_CYCLE 12 +#define FX_MODE_SCAN 13 +#define FX_MODE_DUAL_SCAN 14 +#define FX_MODE_FADE 15 +#define FX_MODE_THEATER_CHASE 16 +#define FX_MODE_THEATER_CHASE_RAINBOW 17 +#define FX_MODE_RUNNING_LIGHTS 18 +#define FX_MODE_TWINKLE 19 +#define FX_MODE_TWINKLE_RANDOM 20 +#define FX_MODE_TWINKLE_FADE 21 +#define FX_MODE_TWINKLE_FADE_RANDOM 22 +#define FX_MODE_SPARKLE 23 +#define FX_MODE_FLASH_SPARKLE 24 +#define FX_MODE_HYPER_SPARKLE 25 +#define FX_MODE_STROBE 26 +#define FX_MODE_STROBE_RAINBOW 27 +#define FX_MODE_MULTI_STROBE 28 +#define FX_MODE_BLINK_RAINBOW 29 +#define FX_MODE_CHASE_WHITE 30 +#define FX_MODE_CHASE_COLOR 31 +#define FX_MODE_CHASE_RANDOM 32 +#define FX_MODE_CHASE_RAINBOW 33 +#define FX_MODE_CHASE_FLASH 34 +#define FX_MODE_CHASE_FLASH_RANDOM 35 +#define FX_MODE_CHASE_RAINBOW_WHITE 36 +#define FX_MODE_CHASE_BLACKOUT 37 +#define FX_MODE_CHASE_BLACKOUT_RAINBOW 38 +#define FX_MODE_COLOR_SWEEP_RANDOM 39 +#define FX_MODE_RUNNING_COLOR 40 +#define FX_MODE_RUNNING_RED_BLUE 41 +#define FX_MODE_RUNNING_RANDOM 42 +#define FX_MODE_LARSON_SCANNER 43 +#define FX_MODE_COMET 44 +#define FX_MODE_FIREWORKS 45 +#define FX_MODE_FIREWORKS_RANDOM 46 +#define FX_MODE_MERRY_CHRISTMAS 47 +#define FX_MODE_FIRE_FLICKER 48 +#define FX_MODE_FIRE_FLICKER_SOFT 49 +#define FX_MODE_FIRE_FLICKER_INTENSE 50 +#define FX_MODE_CIRCUS_COMBUSTUS 51 +#define FX_MODE_HALLOWEEN 52 +#define FX_MODE_BICOLOR_CHASE 53 +#define FX_MODE_TRICOLOR_CHASE 54 +#define FX_MODE_ICU 55 + +class WS2812FX : public Adafruit_NeoPixel { + + typedef uint16_t (WS2812FX::*mode_ptr)(void); + + // segment parameters + public: + typedef struct segment { + uint8_t mode; + uint32_t colors[NUM_COLORS]; + uint16_t speed; + uint16_t start; + uint16_t stop; + bool reverse; + } segment; + + // segment runtime parameters + typedef struct segment_runtime { + uint32_t counter_mode_step; + uint32_t counter_mode_call; + unsigned long next_time; + uint16_t aux_param; + } segment_runtime; + + public: + + WS2812FX(uint16_t n, uint8_t p, neoPixelType t) : Adafruit_NeoPixel(n, p, t) { + _mode[FX_MODE_STATIC] = &WS2812FX::mode_static; + _mode[FX_MODE_BLINK] = &WS2812FX::mode_blink; + _mode[FX_MODE_COLOR_WIPE] = &WS2812FX::mode_color_wipe; + _mode[FX_MODE_COLOR_WIPE_INV] = &WS2812FX::mode_color_wipe_inv; + _mode[FX_MODE_COLOR_WIPE_REV] = &WS2812FX::mode_color_wipe_rev; + _mode[FX_MODE_COLOR_WIPE_REV_INV] = &WS2812FX::mode_color_wipe_rev_inv; + _mode[FX_MODE_COLOR_WIPE_RANDOM] = &WS2812FX::mode_color_wipe_random; + _mode[FX_MODE_RANDOM_COLOR] = &WS2812FX::mode_random_color; + _mode[FX_MODE_SINGLE_DYNAMIC] = &WS2812FX::mode_single_dynamic; + _mode[FX_MODE_MULTI_DYNAMIC] = &WS2812FX::mode_multi_dynamic; + _mode[FX_MODE_RAINBOW] = &WS2812FX::mode_rainbow; + _mode[FX_MODE_RAINBOW_CYCLE] = &WS2812FX::mode_rainbow_cycle; + _mode[FX_MODE_SCAN] = &WS2812FX::mode_scan; + _mode[FX_MODE_DUAL_SCAN] = &WS2812FX::mode_dual_scan; + _mode[FX_MODE_FADE] = &WS2812FX::mode_fade; + _mode[FX_MODE_THEATER_CHASE] = &WS2812FX::mode_theater_chase; + _mode[FX_MODE_THEATER_CHASE_RAINBOW] = &WS2812FX::mode_theater_chase_rainbow; + _mode[FX_MODE_TWINKLE] = &WS2812FX::mode_twinkle; + _mode[FX_MODE_TWINKLE_RANDOM] = &WS2812FX::mode_twinkle_random; + _mode[FX_MODE_TWINKLE_FADE] = &WS2812FX::mode_twinkle_fade; + _mode[FX_MODE_TWINKLE_FADE_RANDOM] = &WS2812FX::mode_twinkle_fade_random; + _mode[FX_MODE_SPARKLE] = &WS2812FX::mode_sparkle; + _mode[FX_MODE_FLASH_SPARKLE] = &WS2812FX::mode_flash_sparkle; + _mode[FX_MODE_HYPER_SPARKLE] = &WS2812FX::mode_hyper_sparkle; + _mode[FX_MODE_STROBE] = &WS2812FX::mode_strobe; + _mode[FX_MODE_STROBE_RAINBOW] = &WS2812FX::mode_strobe_rainbow; + _mode[FX_MODE_MULTI_STROBE] = &WS2812FX::mode_multi_strobe; + _mode[FX_MODE_BLINK_RAINBOW] = &WS2812FX::mode_blink_rainbow; + _mode[FX_MODE_CHASE_WHITE] = &WS2812FX::mode_chase_white; + _mode[FX_MODE_CHASE_COLOR] = &WS2812FX::mode_chase_color; + _mode[FX_MODE_CHASE_RANDOM] = &WS2812FX::mode_chase_random; + _mode[FX_MODE_CHASE_RAINBOW] = &WS2812FX::mode_chase_rainbow; + _mode[FX_MODE_CHASE_FLASH] = &WS2812FX::mode_chase_flash; + _mode[FX_MODE_CHASE_FLASH_RANDOM] = &WS2812FX::mode_chase_flash_random; + _mode[FX_MODE_CHASE_RAINBOW_WHITE] = &WS2812FX::mode_chase_rainbow_white; + _mode[FX_MODE_CHASE_BLACKOUT] = &WS2812FX::mode_chase_blackout; + _mode[FX_MODE_CHASE_BLACKOUT_RAINBOW] = &WS2812FX::mode_chase_blackout_rainbow; + _mode[FX_MODE_COLOR_SWEEP_RANDOM] = &WS2812FX::mode_color_sweep_random; + _mode[FX_MODE_RUNNING_COLOR] = &WS2812FX::mode_running_color; + _mode[FX_MODE_RUNNING_RED_BLUE] = &WS2812FX::mode_running_red_blue; + _mode[FX_MODE_RUNNING_RANDOM] = &WS2812FX::mode_running_random; + _mode[FX_MODE_LARSON_SCANNER] = &WS2812FX::mode_larson_scanner; + _mode[FX_MODE_COMET] = &WS2812FX::mode_comet; + _mode[FX_MODE_FIREWORKS] = &WS2812FX::mode_fireworks; + _mode[FX_MODE_FIREWORKS_RANDOM] = &WS2812FX::mode_fireworks_random; + _mode[FX_MODE_MERRY_CHRISTMAS] = &WS2812FX::mode_merry_christmas; + _mode[FX_MODE_HALLOWEEN] = &WS2812FX::mode_halloween; + _mode[FX_MODE_FIRE_FLICKER] = &WS2812FX::mode_fire_flicker; + _mode[FX_MODE_FIRE_FLICKER_SOFT] = &WS2812FX::mode_fire_flicker_soft; + _mode[FX_MODE_FIRE_FLICKER_INTENSE] = &WS2812FX::mode_fire_flicker_intense; + _mode[FX_MODE_CIRCUS_COMBUSTUS] = &WS2812FX::mode_circus_combustus; + _mode[FX_MODE_BICOLOR_CHASE] = &WS2812FX::mode_bicolor_chase; + _mode[FX_MODE_TRICOLOR_CHASE] = &WS2812FX::mode_tricolor_chase; +// if flash memory is constrained (i'm looking at you Adruino Nano), replace modes +// that use a lot of flash with mode_static (reduces flash footprint by about 3600 bytes) +#ifdef REDUCED_MODES + _mode[FX_MODE_BREATH] = &WS2812FX::mode_static; + _mode[FX_MODE_RUNNING_LIGHTS] = &WS2812FX::mode_static; + _mode[FX_MODE_ICU] = &WS2812FX::mode_static; +#else + _mode[FX_MODE_BREATH] = &WS2812FX::mode_breath; + _mode[FX_MODE_RUNNING_LIGHTS] = &WS2812FX::mode_running_lights; + _mode[FX_MODE_ICU] = &WS2812FX::mode_icu; +#endif + + _name[FX_MODE_STATIC] = F("Static"); + _name[FX_MODE_BLINK] = F("Blink"); + _name[FX_MODE_BREATH] = F("Breath"); + _name[FX_MODE_COLOR_WIPE] = F("Color Wipe"); + _name[FX_MODE_COLOR_WIPE_INV ] = F("Color Wipe Inverse"); + _name[FX_MODE_COLOR_WIPE_REV] = F("Color Wipe Reverse"); + _name[FX_MODE_COLOR_WIPE_REV_INV] = F("Color Wipe Reverse Inverse"); + _name[FX_MODE_COLOR_WIPE_RANDOM] = F("Color Wipe Random"); + _name[FX_MODE_RANDOM_COLOR] = F("Random Color"); + _name[FX_MODE_SINGLE_DYNAMIC] = F("Single Dynamic"); + _name[FX_MODE_MULTI_DYNAMIC] = F("Multi Dynamic"); + _name[FX_MODE_RAINBOW] = F("Rainbow"); + _name[FX_MODE_RAINBOW_CYCLE] = F("Rainbow Cycle"); + _name[FX_MODE_SCAN] = F("Scan"); + _name[FX_MODE_DUAL_SCAN] = F("Dual Scan"); + _name[FX_MODE_FADE] = F("Fade"); + _name[FX_MODE_THEATER_CHASE] = F("Theater Chase"); + _name[FX_MODE_THEATER_CHASE_RAINBOW] = F("Theater Chase Rainbow"); + _name[FX_MODE_RUNNING_LIGHTS] = F("Running Lights"); + _name[FX_MODE_TWINKLE] = F("Twinkle"); + _name[FX_MODE_TWINKLE_RANDOM] = F("Twinkle Random"); + _name[FX_MODE_TWINKLE_FADE] = F("Twinkle Fade"); + _name[FX_MODE_TWINKLE_FADE_RANDOM] = F("Twinkle Fade Random"); + _name[FX_MODE_SPARKLE] = F("Sparkle"); + _name[FX_MODE_FLASH_SPARKLE] = F("Flash Sparkle"); + _name[FX_MODE_HYPER_SPARKLE] = F("Hyper Sparkle"); + _name[FX_MODE_STROBE] = F("Strobe"); + _name[FX_MODE_STROBE_RAINBOW] = F("Strobe Rainbow"); + _name[FX_MODE_MULTI_STROBE] = F("Multi Strobe"); + _name[FX_MODE_BLINK_RAINBOW] = F("Blink Rainbow"); + _name[FX_MODE_CHASE_WHITE] = F("Chase White"); + _name[FX_MODE_CHASE_COLOR] = F("Chase Color"); + _name[FX_MODE_CHASE_RANDOM] = F("Chase Random"); + _name[FX_MODE_CHASE_RAINBOW] = F("Chase Rainbow"); + _name[FX_MODE_CHASE_FLASH] = F("Chase Flash"); + _name[FX_MODE_CHASE_FLASH_RANDOM] = F("Chase Flash Random"); + _name[FX_MODE_CHASE_RAINBOW_WHITE] = F("Chase Rainbow White"); + _name[FX_MODE_CHASE_BLACKOUT] = F("Chase Blackout"); + _name[FX_MODE_CHASE_BLACKOUT_RAINBOW] = F("Chase Blackout Rainbow"); + _name[FX_MODE_COLOR_SWEEP_RANDOM] = F("Color Sweep Random"); + _name[FX_MODE_RUNNING_COLOR] = F("Running Color"); + _name[FX_MODE_RUNNING_RED_BLUE] = F("Running Red Blue"); + _name[FX_MODE_RUNNING_RANDOM] = F("Running Random"); + _name[FX_MODE_LARSON_SCANNER] = F("Larson Scanner"); + _name[FX_MODE_COMET] = F("Comet"); + _name[FX_MODE_FIREWORKS] = F("Fireworks"); + _name[FX_MODE_FIREWORKS_RANDOM] = F("Fireworks Random"); + _name[FX_MODE_MERRY_CHRISTMAS] = F("Merry Christmas"); + _name[FX_MODE_HALLOWEEN] = F("Halloween"); + _name[FX_MODE_FIRE_FLICKER] = F("Fire Flicker"); + _name[FX_MODE_FIRE_FLICKER_SOFT] = F("Fire Flicker (soft)"); + _name[FX_MODE_FIRE_FLICKER_INTENSE] = F("Fire Flicker (intense)"); + _name[FX_MODE_CIRCUS_COMBUSTUS] = F("Circus Combustus"); + _name[FX_MODE_BICOLOR_CHASE] = F("Bicolor Chase"); + _name[FX_MODE_TRICOLOR_CHASE] = F("Tricolor Chase"); + _name[FX_MODE_ICU] = F("ICU"); + + _brightness = DEFAULT_BRIGHTNESS; + _running = false; + _num_segments = 1; + _segments[0].mode = DEFAULT_MODE; + _segments[0].colors[0] = DEFAULT_COLOR; + _segments[0].start = 0; + _segments[0].stop = n - 1; + _segments[0].speed = DEFAULT_SPEED; + RESET_RUNTIME; + } + + void + init(void), + service(void), + start(void), + stop(void), + setMode(uint8_t m), + setSpeed(uint16_t s), + increaseSpeed(uint8_t s), + decreaseSpeed(uint8_t s), + setColor(uint8_t w, uint8_t r, uint8_t g, uint8_t b), + setColor(uint32_t c), + setBrightness(uint8_t b), + increaseBrightness(uint8_t s), + decreaseBrightness(uint8_t s), + setLength(uint16_t b), + increaseLength(uint16_t s), + decreaseLength(uint16_t s), + trigger(void), + setNumSegments(uint8_t n), + setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, bool reverse), + setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed, bool reverse), + resetSegments(); + + boolean + isRunning(void); + + uint8_t + getMode(void), + getBrightness(void), + getModeCount(void), + getNumSegments(void); + + uint16_t + getSpeed(void), + getLength(void); + + uint32_t + color_wheel(uint8_t), + getColor(void); + + const __FlashStringHelper* + getModeName(uint8_t m); + + WS2812FX::segment* + getSegments(void); + + private: + void + strip_off(void), + fade_out(void); + + uint16_t + mode_static(void), + blink(uint32_t, uint32_t, bool strobe), + mode_blink(void), + mode_blink_rainbow(void), + mode_strobe(void), + mode_strobe_rainbow(void), + color_wipe(uint32_t, uint32_t, bool), + mode_color_wipe(void), + mode_color_wipe_inv(void), + mode_color_wipe_rev(void), + mode_color_wipe_rev_inv(void), + mode_color_wipe_random(void), + mode_color_sweep_random(void), + mode_random_color(void), + mode_single_dynamic(void), + mode_multi_dynamic(void), + mode_breath(void), + mode_fade(void), + mode_scan(void), + mode_dual_scan(void), + theater_chase(uint32_t, uint32_t), + mode_theater_chase(void), + mode_theater_chase_rainbow(void), + mode_rainbow(void), + mode_rainbow_cycle(void), + mode_running_lights(void), + twinkle(uint32_t), + mode_twinkle(void), + mode_twinkle_random(void), + twinkle_fade(uint32_t), + mode_twinkle_fade(void), + mode_twinkle_fade_random(void), + mode_sparkle(void), + mode_flash_sparkle(void), + mode_hyper_sparkle(void), + mode_multi_strobe(void), + chase(uint32_t, uint32_t, uint32_t), + mode_chase_white(void), + mode_chase_color(void), + mode_chase_random(void), + mode_chase_rainbow(void), + mode_chase_flash(void), + mode_chase_flash_random(void), + mode_chase_rainbow_white(void), + mode_chase_blackout(void), + mode_chase_blackout_rainbow(void), + running(uint32_t, uint32_t), + mode_running_color(void), + mode_running_red_blue(void), + mode_running_random(void), + mode_larson_scanner(void), + mode_comet(void), + fireworks(uint32_t), + mode_fireworks(void), + mode_fireworks_random(void), + mode_merry_christmas(void), + mode_halloween(void), + mode_fire_flicker(void), + mode_fire_flicker_soft(void), + mode_fire_flicker_intense(void), + fire_flicker(int), + mode_circus_combustus(void), + tricolor_chase(uint32_t, uint32_t, uint32_t), + mode_bicolor_chase(void), + mode_tricolor_chase(void), + mode_icu(void); + + boolean + _running, + _triggered; + + uint8_t + get_random_wheel_index(uint8_t), + _brightness; + + const __FlashStringHelper* + _name[MODE_COUNT]; // SRAM footprint: 2 bytes per element + + mode_ptr + _mode[MODE_COUNT]; // SRAM footprint: 4 bytes per element + + uint8_t _segment_index = 0; + uint8_t _num_segments = 1; + segment _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 20 bytes per element + // mode, color[], speed, start, stop, reverse + { FX_MODE_STATIC, {DEFAULT_COLOR}, DEFAULT_SPEED, 0, 7, false} + }; + segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 14 bytes per element +}; + +#endif diff --git a/Arduino/McLighting/colormodes.h b/Arduino/McLighting/colormodes.h index 2d73c5ea..48737010 100644 --- a/Arduino/McLighting/colormodes.h +++ b/Arduino/McLighting/colormodes.h @@ -25,7 +25,7 @@ void hsb2rgbAN1(uint16_t index, uint8_t sat, uint8_t bright, uint8_t myled) { temp[1] = temp[4] = (uint8_t)((((( (index & 255) * sat) / 255) + (sat ^ 255)) * bright) / 255); temp[2] = (uint8_t)(((((((index & 255) ^ 255) * sat) / 255) + (sat ^ 255)) * bright) / 255); - strip.setPixelColor(myled, temp[n + 2], temp[n + 1], temp[n]); + strip.setPixelColor(myled, temp[n + 2], temp[n + 1], temp[n], 0); } diff --git a/Arduino/McLighting/definitions.h b/Arduino/McLighting/definitions.h index 2309066d..9219f4ed 100644 --- a/Arduino/McLighting/definitions.h +++ b/Arduino/McLighting/definitions.h @@ -1,21 +1,21 @@ // Neopixel -#define PIN 5 // PIN (5 / D1) where neopixel / WS2811 strip is attached -#define NUMLEDS 24 // Number of leds in the strip +#define PIN 2 // PIN (5 / D1) where neopixel / WS2811 strip is attached +#define NUMLEDS 144 // Number of leds in the strip //#define BUILTIN_LED 2 // ESP-12F has the built in LED on GPIO2, see https://github.com/esp8266/Arduino/issues/2192 -#define BUTTON 4 // Input pin (4 / D2) for switching the LED strip on / off, connect this PIN to ground to trigger button. +#define BUTTON 0 // Input pin (4 / D2) for switching the LED strip on / off, connect this PIN to ground to trigger button. -const char HOSTNAME[] = "ESP8266_01"; // Friedly hostname +const char HOSTNAME[] = "ESPLightRGBW01"; // Friedly hostname #define ENABLE_OTA // If defined, enable Arduino OTA code. #define ENABLE_MQTT // If defined, enable MQTT client code, see: https://github.com/toblum/McLighting/wiki/MQTT-API -// #define ENABLE_BUTTON // If defined, enable button handling code, see: https://github.com/toblum/McLighting/wiki/Button-control +#define ENABLE_BUTTON // If defined, enable button handling code, see: https://github.com/toblum/McLighting/wiki/Button-control // parameters for automatically cycling favorite patterns uint32_t autoParams[][4] = { // color, speed, mode, duration (seconds) - {0xff0000, 200, 1, 5.0}, // blink red for 5 seconds - {0x00ff00, 200, 3, 10.0}, // wipe green for 10 seconds - {0x0000ff, 200, 11, 5.0}, // dual scan blue for 5 seconds - {0x0000ff, 200, 42, 15.0} // fireworks for 15 seconds + {0x00ff0000, 250, 1, 5.0}, // blink red for 5 seconds + {0x0000ff00, 200, 3, 10.0}, // wipe green for 10 seconds + {0x000000ff, 200, 14, 5.0}, // dual scan blue for 5 seconds + {0x000000ff, 200, 46, 15.0} // fireworks for 15 seconds }; #ifdef ENABLE_MQTT @@ -26,7 +26,7 @@ uint32_t autoParams[][4] = { // color, speed, mode, duration (seconds) char mqtt_intopic[strlen(HOSTNAME) + 4]; // Topic in will be: /in char mqtt_outtopic[strlen(HOSTNAME) + 5]; // Topic out will be: /out - const char mqtt_clientid[] = "ESP8266Client"; // MQTT ClientID + const char mqtt_clientid[] = "ESPLightMax"; // MQTT ClientID char mqtt_host[64] = ""; char mqtt_port[6] = ""; @@ -41,7 +41,7 @@ uint32_t autoParams[][4] = { // color, speed, mode, duration (seconds) #define DBG_OUTPUT_PORT Serial // Set debug output port // List of all color modes -enum MODE { SET_MODE, HOLD, OFF, ALL, WIPE, RAINBOW, RAINBOWCYCLE, THEATERCHASE, TWINKLERANDOM, THEATERCHASERAINBOW, TV, CUSTOM }; +enum MODE { SET_MODE, HOLD, OFF, ALL, WIPE, RAINBOW, RAINBOWCYCLE, THEATERCHASE, TWINKLERANDOM, THEATERCHASERAINBOW, TV, CUSTOM, AUTO }; MODE mode = RAINBOW; // Standard mode that is active when software starts @@ -59,16 +59,17 @@ struct ledstate // Data structure to store a state of a single led uint8_t red; uint8_t green; uint8_t blue; + uint8_t white; }; typedef struct ledstate LEDState; // Define the datatype LEDState LEDState ledstates[NUMLEDS]; // Get an array of led states to store the state of the whole strip -LEDState main_color = { 255, 0, 0 }; // Store the "main color" of the strip used in single color modes +LEDState main_color = { 0, 255, 0, 0}; // Store the "main color" of the strip used in single color modes #define ENABLE_STATE_SAVE // If defined, save state on reboot #ifdef ENABLE_STATE_SAVE - char current_state[32]; // Keeps the current state representation - char last_state[32]; // Save the last state as string representation + char current_state[36]; // Keeps the current state representation + char last_state[36]; // Save the last state as string representation unsigned long time_statechange = 0; // Time when the state last changed int timeout_statechange_save = 5000; // Timeout in ms to wait before state is saved bool state_save_requested = false; // State has to be saved after timeout @@ -76,9 +77,9 @@ LEDState main_color = { 255, 0, 0 }; // Store the "main color" of the strip use // Button handling #ifdef ENABLE_BUTTON - #define BTN_MODE_SHORT "STA| 1| 0|245|196|255|255|255" // Static white - #define BTN_MODE_MEDIUM "STA| 1| 48|245|196|255|102| 0" // Fire flicker - #define BTN_MODE_LONG "STA| 1| 46|253|196|255|102| 0" // Fireworks random + #define BTN_MODE_SHORT "STA| 1| 0|245|196| 0|255|255|255" // Static white + #define BTN_MODE_MEDIUM "STA| 1| 48|245|196| 0|255|102| 0" // Fire flicker + #define BTN_MODE_LONG "STA| 1| 46|253|196| 0|255|102| 0" // Fireworks random unsigned long keyPrevMillis = 0; const unsigned long keySampleIntervalMs = 25; diff --git a/Arduino/McLighting/request_handlers.h b/Arduino/McLighting/request_handlers.h index 7572a724..21fe77f4 100644 --- a/Arduino/McLighting/request_handlers.h +++ b/Arduino/McLighting/request_handlers.h @@ -4,10 +4,12 @@ void getArgs() { if (server.arg("rgb") != "") { uint32_t rgb = (uint32_t) strtol(server.arg("rgb").c_str(), NULL, 16); + main_color.white = ((rgb >> 24) & 0xFF); main_color.red = ((rgb >> 16) & 0xFF); main_color.green = ((rgb >> 8) & 0xFF); main_color.blue = ((rgb >> 0) & 0xFF); } else { + main_color.white = server.arg("w").toInt(); main_color.red = server.arg("r").toInt(); main_color.green = server.arg("g").toInt(); main_color.blue = server.arg("b").toInt(); @@ -21,6 +23,7 @@ void getArgs() { ws2812fx_mode = constrain(server.arg("m").toInt(), 0, strip.getModeCount() - 1); } + main_color.white = constrain(main_color.white, 0, 255); main_color.red = constrain(main_color.red, 0, 255); main_color.green = constrain(main_color.green, 0, 255); main_color.blue = constrain(main_color.blue, 0, 255); @@ -28,6 +31,8 @@ void getArgs() { DBG_OUTPUT_PORT.print("Mode: "); DBG_OUTPUT_PORT.print(mode); DBG_OUTPUT_PORT.print(", Color: "); + DBG_OUTPUT_PORT.print(main_color.white); + DBG_OUTPUT_PORT.print(", "); DBG_OUTPUT_PORT.print(main_color.red); DBG_OUTPUT_PORT.print(", "); DBG_OUTPUT_PORT.print(main_color.green); @@ -59,25 +64,27 @@ long convertSpeed(int mcl_speed) { void handleSetMainColor(uint8_t * mypayload) { // decode rgb data uint32_t rgb = (uint32_t) strtol((const char *) &mypayload[1], NULL, 16); + main_color.white = ((rgb >> 24) & 0xFF); main_color.red = ((rgb >> 16) & 0xFF); main_color.green = ((rgb >> 8) & 0xFF); main_color.blue = ((rgb >> 0) & 0xFF); - strip.setColor(main_color.red, main_color.green, main_color.blue); + strip.setColor(main_color.white, main_color.red, main_color.green, main_color.blue); } void handleSetAllMode(uint8_t * mypayload) { // decode rgb data uint32_t rgb = (uint32_t) strtol((const char *) &mypayload[1], NULL, 16); + main_color.white = ((rgb >> 24) & 0xFF); main_color.red = ((rgb >> 16) & 0xFF); main_color.green = ((rgb >> 8) & 0xFF); main_color.blue = ((rgb >> 0) & 0xFF); for (int i = 0; i < strip.numPixels(); i++) { - strip.setPixelColor(i, main_color.red, main_color.green, main_color.blue); + strip.setPixelColor(i, main_color.white, main_color.red, main_color.green, main_color.blue); } strip.show(); - DBG_OUTPUT_PORT.printf("WS: Set all leds to main color: [%u] [%u] [%u]\n", main_color.red, main_color.green, main_color.blue); + DBG_OUTPUT_PORT.printf("WS: Set all leds to main color: [%u] [%u] [%u]\n", main_color.white, main_color.red, main_color.green, main_color.blue); exit_func = true; mode = ALL; } @@ -90,21 +97,24 @@ void handleSetSingleLED(uint8_t * mypayload, uint8_t firstChar = 0) { DBG_OUTPUT_PORT.printf("led value: [%i]. Entry threshold: <= [%i] (=> %s)\n", led, strip.numPixels(), mypayload ); if (led <= strip.numPixels()) { + char whitehex[3]; char redhex[3]; char greenhex[3]; char bluehex[3]; - strncpy (redhex, (const char *) &mypayload[2 + firstChar], 2 ); - strncpy (greenhex, (const char *) &mypayload[4 + firstChar], 2 ); - strncpy (bluehex, (const char *) &mypayload[6 + firstChar], 2 ); + strncpy (whitehex, (const char *) &mypayload[2 + firstChar], 2 ); + strncpy (redhex, (const char *) &mypayload[4 + firstChar], 2 ); + strncpy (greenhex, (const char *) &mypayload[6 + firstChar], 2 ); + strncpy (bluehex, (const char *) &mypayload[8 + firstChar], 2 ); + ledstates[led].white = strtol(whitehex, NULL, 16); ledstates[led].red = strtol(redhex, NULL, 16); ledstates[led].green = strtol(greenhex, NULL, 16); ledstates[led].blue = strtol(bluehex, NULL, 16); - DBG_OUTPUT_PORT.printf("rgb.red: [%s] rgb.green: [%s] rgb.blue: [%s]\n", redhex, greenhex, bluehex); - DBG_OUTPUT_PORT.printf("rgb.red: [%i] rgb.green: [%i] rgb.blue: [%i]\n", strtol(redhex, NULL, 16), strtol(greenhex, NULL, 16), strtol(bluehex, NULL, 16)); - DBG_OUTPUT_PORT.printf("WS: Set single led [%i] to [%i] [%i] [%i] (%s)!\n", led, ledstates[led].red, ledstates[led].green, ledstates[led].blue, mypayload); + DBG_OUTPUT_PORT.printf(" rgb.white: [%s] rgb.red: [%s] rgb.green: [%s] rgb.blue: [%s]\n", whitehex, redhex, greenhex, bluehex); + DBG_OUTPUT_PORT.printf(" rgb.white: [%i] rgb.red: [%i] rgb.green: [%i] rgb.blue: [%i]\n", strtol(whitehex, NULL, 16), strtol(redhex, NULL, 16), strtol(greenhex, NULL, 16), strtol(bluehex, NULL, 16)); + DBG_OUTPUT_PORT.printf("WS: Set single led [%i] to [%i] [%i] [%i] [%i] (%s)!\n", led, ledstates[led].white, ledstates[led].red, ledstates[led].green, ledstates[led].blue, mypayload); - strip.setPixelColor(led, ledstates[led].red, ledstates[led].green, ledstates[led].blue); + strip.setPixelColor(led, ledstates[led].white, ledstates[led].red, ledstates[led].green, ledstates[led].blue); strip.show(); } exit_func = true; @@ -129,10 +139,10 @@ void handleRangeDifferentColors(uint8_t * mypayload) { // Loop for each LED. char startled[3] = { 0, 0, 0 }; char endled[3] = { 0, 0, 0 }; - char colorval[7] = { 0, 0, 0, 0, 0, 0, 0 }; + char colorval[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; strncpy ( startled, (const char *) &nextCommand[0], 2 ); strncpy ( endled, (const char *) &nextCommand[2], 2 ); - strncpy ( colorval, (const char *) &nextCommand[4], 6 ); + strncpy ( colorval, (const char *) &nextCommand[4], 8 ); int rangebegin = atoi(startled); int rangeend = atoi(endled); DBG_OUTPUT_PORT.printf("Setting RANGE from [%i] to [%i] as color [%s] \n", rangebegin, rangeend, colorval); @@ -166,24 +176,26 @@ void setModeByStateString(String saved_state_string) { ws2812fx_speed = str_ws2812fx_speed.toInt(); String str_brightness = getValue(saved_state_string, '|', 4); brightness = str_brightness.toInt(); - String str_red = getValue(saved_state_string, '|', 5); + String str_white = getValue(saved_state_string, '|', 5); + main_color.white = str_white.toInt(); + String str_red = getValue(saved_state_string, '|', 6); main_color.red = str_red.toInt(); - String str_green = getValue(saved_state_string, '|', 6); + String str_green = getValue(saved_state_string, '|', 7); main_color.green = str_green.toInt(); - String str_blue = getValue(saved_state_string, '|', 7); + String str_blue = getValue(saved_state_string, '|', 8); main_color.blue = str_blue.toInt(); - DBG_OUTPUT_PORT.printf("ws2812fx_mode: %d\n", ws2812fx_mode); DBG_OUTPUT_PORT.printf("ws2812fx_speed: %d\n", ws2812fx_speed); DBG_OUTPUT_PORT.printf("brightness: %d\n", brightness); + DBG_OUTPUT_PORT.printf("main_color.white: %d\n", main_color.white); DBG_OUTPUT_PORT.printf("main_color.red: %d\n", main_color.red); DBG_OUTPUT_PORT.printf("main_color.green: %d\n", main_color.green); DBG_OUTPUT_PORT.printf("main_color.blue: %d\n", main_color.blue); - + strip.setMode(ws2812fx_mode); strip.setSpeed(convertSpeed(ws2812fx_speed)); strip.setBrightness(brightness); - strip.setColor(main_color.red, main_color.green, main_color.blue); + strip.setColor(main_color.white, main_color.red, main_color.green, main_color.blue); } void handleSetNamedMode(String str_mode) { @@ -222,7 +234,7 @@ void handleSetWS2812FXMode(uint8_t * mypayload) { mode = HOLD; uint8_t ws2812fx_mode = (uint8_t) strtol((const char *) &mypayload[1], NULL, 10); ws2812fx_mode = constrain(ws2812fx_mode, 0, 255); - strip.setColor(main_color.red, main_color.green, main_color.blue); + strip.setColor(main_color.white, main_color.red, main_color.green, main_color.blue); strip.setMode(ws2812fx_mode); strip.start(); } @@ -233,8 +245,8 @@ char* listStatusJSON() { char modeName[30]; strncpy_P(modeName, (PGM_P)strip.getModeName(strip.getMode()), sizeof(modeName)); // copy from progmem - snprintf(json, sizeof(json), "{\"mode\":%d, \"ws2812fx_mode\":%d, \"ws2812fx_mode_name\":\"%s\", \"speed\":%d, \"brightness\":%d, \"color\":[%d, %d, %d]}", - mode, strip.getMode(), modeName, ws2812fx_speed, brightness, main_color.red, main_color.green, main_color.blue); + snprintf(json, sizeof(json), "{\"mode\":%d, \"ws2812fx_mode\":%d, \"ws2812fx_mode_name\":\"%s\", \"speed\":%d, \"brightness\":%d, \"color\":[%d, %d, %d, %d]}", + mode, strip.getMode(), modeName, ws2812fx_speed, brightness, main_color.white, main_color.red, main_color.green, main_color.blue); return json; } @@ -319,12 +331,14 @@ void autoTick() { } void handleAutoStart() { + mode = AUTO; autoCount = 0; autoTick(); strip.start(); } void handleAutoStop() { + mode = OFF; autoTicker.detach(); strip.stop(); } @@ -353,7 +367,7 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght // # ==> Set main color if (payload[0] == '#') { handleSetMainColor(payload); - DBG_OUTPUT_PORT.printf("Set main color to: [%u] [%u] [%u]\n", main_color.red, main_color.green, main_color.blue); + DBG_OUTPUT_PORT.printf("Set main color to: [%u] [%u] [%u]\n", main_color.red, main_color.green, main_color.blue, main_color.white); webSocket.sendTXT(num, "OK"); } @@ -468,7 +482,7 @@ void mqtt_callback(char* topic, byte* payload_in, unsigned int length) { // # ==> Set main color if (payload[0] == '#') { handleSetMainColor(payload); - DBG_OUTPUT_PORT.printf("MQTT: Set main color to [%u] [%u] [%u]\n", main_color.red, main_color.green, main_color.blue); + DBG_OUTPUT_PORT.printf("MQTT: Set main color to [%u] [%u] [%u]\n", main_color.red, main_color.green, main_color.blue, main_color.white); mqtt_client.publish(mqtt_outtopic, String(String("OK ") + String((char *)payload)).c_str()); } diff --git a/clients/web/build/index.htm b/clients/web/build/index.htm index ce8333e4..10275aff 100644 --- a/clients/web/build/index.htm +++ b/clients/web/build/index.htm @@ -9,13 +9,13 @@ - McLighting v2 + McLighting (RGBW) v2