From 70d0aae07cc3d7e371d0463769965ce20a5e9124 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Tue, 4 Sep 2018 15:51:38 +0200 Subject: [PATCH 01/22] First commit of 0.8.0 dev cycle Updated to newer WS2812FX version which supports segments (not supported in WLED yet) Added new (FastLED) effects Adjusted FX speed timings Removed Sweep transition and Custom Chase (seldomly used - hinder development) Removed solid overlay (no longer needed once segments are added) Fixed Blynk effect set --- wled00/NpbWrapper.h | 18 +- wled00/WS2812FX.cpp | 3980 ++++++++++++++++----------------- wled00/WS2812FX.h | 485 ++-- wled00/data/index.htm | 69 +- wled00/data/settings_leds.htm | Bin 8538 -> 8336 bytes wled00/data/settings_time.htm | Bin 12512 -> 12424 bytes wled00/htmls00.h | 145 +- wled00/htmls01.h | 10 +- wled00/wled00.ino | 19 +- wled00/wled01_eeprom.ino | 41 +- wled00/wled02_xml.ino | 2 - wled00/wled03_set.ino | 44 +- wled00/wled05_init.ino | 9 +- wled00/wled08_led.ino | 21 +- wled00/wled11_ol.ino | 16 +- wled00/wled16_blynk.ino | 5 +- 16 files changed, 2374 insertions(+), 2490 deletions(-) diff --git a/wled00/NpbWrapper.h b/wled00/NpbWrapper.h index b97ae8e5e..5912966a1 100644 --- a/wled00/NpbWrapper.h +++ b/wled00/NpbWrapper.h @@ -1,8 +1,12 @@ //this code is a modified version of https://github.com/Makuna/NeoPixelBus/issues/103 +#ifndef NpbWrapper_h +#define NpbWrapper_h //#define WORKAROUND_ESP32_BITBANG //see https://github.com/Aircoookie/WLED/issues/2 for flicker free ESP32 support +#define LEDPIN 2 //strip pin. Only effective for ESP32, ESP8266 must use gpio2 + //uncomment this if red and green are swapped //#define SWAPRG @@ -13,9 +17,12 @@ #else #define PIXELMETHOD NeoEsp32RmtWS2813_V3Method #endif -#else +#else //esp8266 +//you may change to DMA method on pin GPIO3 here #define PIXELMETHOD NeoEsp8266Uart800KbpsMethod +//#define PIXELMETHOD NeoEsp8266Dma800KbpsMethod #endif + //handle swapping Red and Green automatically #ifdef SWAPRG #define PIXELFEATURE3 NeoRgbFeature @@ -52,7 +59,7 @@ public: cleanup(); } - void Begin(NeoPixelType type, uint16_t countPixels, uint8_t pin) + void Begin(NeoPixelType type, uint16_t countPixels) { cleanup(); _type = type; @@ -60,12 +67,12 @@ public: switch (_type) { case NeoPixelType_Grb: - _pGrb = new NeoPixelBrightnessBus(countPixels, pin); + _pGrb = new NeoPixelBrightnessBus(countPixels, LEDPIN); _pGrb->Begin(); break; case NeoPixelType_Grbw: - _pGrbw = new NeoPixelBrightnessBus(countPixels, pin); + _pGrbw = new NeoPixelBrightnessBus(countPixels, LEDPIN); _pGrbw->Begin(); break; } @@ -110,7 +117,7 @@ public: void SetPixelColor(uint16_t indexPixel, RgbwColor color) { switch (_type) { - case NeoPixelType_Grb: _pGrbw->SetPixelColor(indexPixel, color); break; + case NeoPixelType_Grb: _pGrb->SetPixelColor(indexPixel, RgbColor(color.R,color.G,color.B)); break; case NeoPixelType_Grbw: _pGrbw->SetPixelColor(indexPixel, color); break; } } @@ -174,3 +181,4 @@ private: } } }; +#endif diff --git a/wled00/WS2812FX.cpp b/wled00/WS2812FX.cpp index 9b2e0e746..505f0ba84 100644 --- a/wled00/WS2812FX.cpp +++ b/wled00/WS2812FX.cpp @@ -4,13 +4,13 @@ www.aldick.org FEATURES * A lot of blinken modes and counting - * WS2812FX can be used as drop-in replacement for Adafruit Neopixel Library + * WS2812FX can be used as drop-in replacement for Adafruit NeoPixel Library NOTES - * Uses the Adafruit Neopixel library. Get it here: + * Uses the Adafruit NeoPixel library. Get it here: https://github.com/adafruit/Adafruit_NeoPixel LICENSE The MIT License (MIT) - Copyright (c) 2016 Harm Aldick + 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 @@ -30,1876 +30,115 @@ 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 + 2018-02-24 added hooks for user created custom effects + Modified for WLED */ -#include "Arduino.h" #include "WS2812FX.h" +#include "FastLED.h" -#define CALL_MODE(n) (this->*_mode[n])(); - -void WS2812FX::init(bool supportWhite, uint16_t countPixels, uint8_t pin,bool skipFirst) { - begin(supportWhite,countPixels,pin,skipFirst); - for (int i=0; i < _led_count; i++) _locked[i] = false; - WS2812FX::setBrightness(_brightness); +void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst) +{ + if (supportWhite == _rgbwMode && countPixels == _length && _locked != NULL) return; + RESET_RUNTIME; + _rgbwMode = supportWhite; + _skipFirstMode = skipFirst; + _length = countPixels; + if (_skipFirstMode) _length++; + uint8_t ty = 1; + if (supportWhite) ty =2; + bus->Begin((NeoPixelType)ty, _length); + if (_locked != NULL) delete _locked; + _locked = new byte[_length]; + _segments[0].start = 0; + _segments[0].stop = _length -1; + unlockAll(); + setBrightness(_brightness); show(); + _running = true; } void WS2812FX::service() { if(_running || _triggered) { - unsigned long now = millis(); - - if(now - _mode_last_call_time > _mode_delay || _triggered) { - CALL_MODE(_mode_index); - _counter_mode_call++; - _mode_last_call_time = now; - _triggered = false; - } - } -} - -void WS2812FX::trigger() { - _triggered = true; -} - -void WS2812FX::start() { - _counter_mode_call = 0; - _counter_mode_step = 0; - _mode_last_call_time = 0; - _running = true; - show(); -} - -void WS2812FX::stop() { - _running = false; - strip_off(); -} - -void WS2812FX::setMode(byte m) { - _counter_mode_call = 0; - _counter_mode_step = 0; - _mode_last_call_time = 0; - _mode_index = constrain(m, 0, MODE_COUNT-1); - _mode_color = _color; - _mode_var1 = 0; - setBrightness(_brightness); - strip_off_respectLock(); -} - -void WS2812FX::setSpeed(byte s) { - _mode_last_call_time = 0; - _speed = constrain(s, SPEED_MIN, SPEED_MAX); -} - -void WS2812FX::increaseSpeed(byte s) { - s = constrain(_speed + s, SPEED_MIN, SPEED_MAX); - setSpeed(s); -} - -void WS2812FX::decreaseSpeed(byte s) { - s = constrain(_speed - s, SPEED_MIN, SPEED_MAX); - setSpeed(s); -} - -void WS2812FX::setIntensity(byte in) { - _intensity=in; -} - -void WS2812FX::setColor(byte r, byte g, byte b) { - setColor(((uint32_t)r << 16) | ((uint32_t)g << 8) | b); -} - - -void WS2812FX::setColor(byte r, byte g, byte b, byte w) { - setColor(((uint32_t)w << 24)|((uint32_t)r << 16) | ((uint32_t)g << 8) | b); -} - -void WS2812FX::setSecondaryColor(byte r, byte g, byte b) { - setSecondaryColor(((uint32_t)r << 16) | ((uint32_t)g << 8) | b); -} - - -void WS2812FX::setSecondaryColor(byte r, byte g, byte b, byte w) { - setSecondaryColor(((uint32_t)w << 24)|((uint32_t)r << 16) | ((uint32_t)g << 8) | b); -} - -void WS2812FX::setColor(uint32_t c) { - _color = c; - _mode_color = _color; - setBrightness(_brightness); -} - -void WS2812FX::setSecondaryColor(uint32_t c) { - _color_sec = c; - if (_cronixieMode) _cronixieSecMultiplier = getSafePowerMultiplier(900, 100, c, _brightness); - setBrightness(_brightness); -} - -void WS2812FX::increaseBrightness(byte s) { - s = constrain(_brightness + s, BRIGHTNESS_MIN, BRIGHTNESS_MAX); - setBrightness(s); -} - -void WS2812FX::decreaseBrightness(byte s) { - s = constrain(_brightness - s, BRIGHTNESS_MIN, BRIGHTNESS_MAX); - setBrightness(s); -} - -bool WS2812FX::isRunning() { - return _running; -} - -byte WS2812FX::getMode(void) { - return _mode_index; -} - -byte WS2812FX::getSpeed(void) { - return _speed; -} - -byte WS2812FX::getBrightness(void) { - return _brightness; -} - -byte WS2812FX::getModeCount(void) { - return MODE_COUNT; -} - -uint32_t WS2812FX::getColor(void) { - return _color; -} - -/* ##################################################### -# -# Color and Blinken Functions -# -##################################################### */ - -/* - * Turns everything off. Doh. - */ -void WS2812FX::strip_off() { - clear(); - show(); -} - -void WS2812FX::strip_off_respectLock() { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, 0); - } - show(); -} - - -/* - * Put a value 0 to 255 in to get a color value. - * The colours are a transition r -> g -> b -> back to r - * Inspired by the Adafruit examples. - */ -uint32_t WS2812FX::color_wheel(byte 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. - */ -byte WS2812FX::get_random_wheel_index(byte pos) { - byte r = 0; - byte x = 0; - byte y = 0; - byte d = 0; - - while(d < 42) { - r = random(256); - x = abs(pos - r); - y = 255 - x; - d = minval(x, y); - } - - return r; -} - - -/* - * No blinking. Just plain old static light. - */ -void WS2812FX::mode_static(void) { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, _color); - } - show(); - _mode_delay = (_fastStandard) ? 25 : 500; -} - - -/* - * Normal blinking. on/off duty time set by FX intensity. - */ -void WS2812FX::mode_blink(void) { - if(_counter_mode_call % 2 == 1) { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, _color); - } - _mode_delay = (100 + ((1986 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX))*(float)(_intensity/128.0); - } else { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, _color_sec); - } - _mode_delay = (100 + ((1986 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX))*(float)(2.0-(_intensity/128.0)); - } - show(); -} - - -/* - * Lights all LEDs after each other up. Then turns them in - * that order off (2nd color). Repeat. - */ -void WS2812FX::mode_color_wipe(void) { - if(_counter_mode_step < _led_count) { - if (!_locked[_counter_mode_step]) - setPixelColor(_counter_mode_step, _color); - } else { - if (!_locked[_counter_mode_step - _led_count]) - setPixelColor(_counter_mode_step - _led_count, _color_sec); - } - show(); - - _counter_mode_step = (_counter_mode_step + 1) % (_led_count * 2); - - _mode_delay = 5 + ((50 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - - -/* - * Turns all LEDs after each other to a random color. - * Then starts over with another color. - */ -void WS2812FX::mode_color_wipe_random(void) { - if(_counter_mode_step == 0) { - _mode_color = get_random_wheel_index(_mode_color); - } - if (!_locked[_counter_mode_step]) - setPixelColor(_counter_mode_step, color_wheel(_mode_color)); - show(); - - _counter_mode_step = (_counter_mode_step + 1) % _led_count; - - _mode_delay = 5 + ((50 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - - -/* - * Lights all LEDs in one random color up. Then switches them - * to the next random color. - */ -void WS2812FX::mode_random_color(void) { - _mode_color = get_random_wheel_index(_mode_color); - - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, color_wheel(_mode_color)); - } - - show(); - _mode_delay = 100 + ((5000 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX); -} - - -/* - * Lights some pastel colors - */ -void WS2812FX::mode_easter(void) { - //uint32_t cols[]{0x00F7ECC5,0x00F8D5C7,0x00F9E2E7,0x00BED9D4,0x00F7ECC5,0x00F8D5C7,0x00F9E2E7}; - uint32_t cols[]{0x00FF8040,0x00E5D241,0x0077FF77,0x0077F0F0,0x00FF8040,0x00E5D241,0x0077FF77}; - mode_colorful_internal(cols); -} - - -/* - * Lights multiple random leds in a random color (higher intensity, more updates) - */ -void WS2812FX::mode_dynamic(void) { - if(_counter_mode_call == 0) { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, color_wheel(random(256))); - } - } - if (_intensity > 0) //multi dynamic - { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i] && random(256)<_intensity) - setPixelColor(i, color_wheel(random(256))); - } - } else { //single dynamic - int ran = random(_led_count); - if (!_locked[ran]) - setPixelColor(ran, color_wheel(random(256))); - } - show(); - _mode_delay = 100 + ((5000 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX); -} - - -/* - * 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. - */ -void 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 - byte 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(_counter_mode_call == 0) { - _mode_color = breath_brightness_steps[0] + 1; - } - - byte breath_brightness = _mode_color; // we use _mode_color to store the brightness - - if(_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[_counter_mode_step]) { - _counter_mode_step = (_counter_mode_step + 1) % (sizeof(breath_brightness_steps)/sizeof(byte)); - } - - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, _color); // set all LEDs to selected color - } - int b = map(breath_brightness, 0, 255, 0, _brightness); // keep brightness below brightness set by user - bus->SetBrightness(b); // set new brightness to leds - show(); - - _mode_color = breath_brightness; // we use _mode_color to store the brightness - _mode_delay = breath_delay_steps[_counter_mode_step]; -} - - -/* - * Fades the LEDs on and (almost) off again. - */ -void WS2812FX::mode_fade(void) { - - int y = _counter_mode_step - 127; - y = 256 - (abs(y) * 2); - double z = (double)y/256; - byte w = ((_color >> 24) & 0xFF), ws = ((_color_sec >> 24) & 0xFF); - byte r = ((_color >> 16) & 0xFF), rs = ((_color_sec >> 16) & 0xFF); - byte g = ((_color >> 8) & 0xFF), gs = ((_color_sec >> 8) & 0xFF); - byte b = (_color & 0xFF), bs = (_color_sec & 0xFF); - w = w+((ws - w)*z); - r = r+((rs - r)*z); - g = g+((gs - g)*z); - b = b+((bs - b)*z); - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, r, g, b, w); - } - show(); - - _counter_mode_step = (_counter_mode_step + 1) % 256; - _mode_delay = 5 + ((15 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX); -} - - -/* - * Runs a single pixel back and forth. - */ -void WS2812FX::mode_scan(void) { - if(_counter_mode_step > (_led_count*2) - 2) { - _counter_mode_step = 0; - } - _counter_mode_step++; - - int i = _counter_mode_step - (_led_count - 1); - i = abs(i); - - for(uint16_t x=0; x < _led_count; x++) { - if (!_locked[x]) - setPixelColor(x, _color_sec); - } - if (!_locked[i]) - setPixelColor(abs(i), _color); - show(); - - _mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - - -/* - * Runs two pixel back and forth in opposite directions. - */ -void WS2812FX::mode_dual_scan(void) { - if(_counter_mode_step > (_led_count*2) - 2) { - _counter_mode_step = 0; - } - _counter_mode_step++; - - int i = _counter_mode_step - (_led_count - 1); - i = abs(i); - for(uint16_t x=0; x < _led_count; x++) { - if (!_locked[x]) - setPixelColor(x, _color_sec); - } - if (!_locked[i]) - setPixelColor(i, _color); - if (!_locked[_led_count - (i+1)]) - setPixelColor(_led_count - (i+1), _color); - show(); - - _mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - - -/* - * Cycles all LEDs at once through a rainbow. - */ -void WS2812FX::mode_rainbow(void) { - uint32_t color = color_wheel(_counter_mode_step); - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, color); - } - show(); - - _counter_mode_step = (_counter_mode_step + 1) % 256; - - _mode_delay = 1 + ((50 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX); -} - - -/* - * Cycles a rainbow over the entire string of LEDs. - */ -void WS2812FX::mode_rainbow_cycle(void) { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, color_wheel(((i * 256 / ((uint16_t)(_led_count*(float)(_intensity/128.0))+1)) + _counter_mode_step) % 256)); - } - show(); - - _counter_mode_step = (_counter_mode_step + 1) % 256; - - _mode_delay = 1 + ((50 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX); -} - - -/* - * Theatre-style crawling lights. - * Inspired by the Adafruit examples. - */ -void WS2812FX::mode_theater_chase(void) { - byte j = _counter_mode_call % 6; - if(j % 2 == 0) { - for(uint16_t i=0; i < _led_count; i=i+3) { - if (!_locked[i+(j/2)]) - setPixelColor(i+(j/2), _color); - } - show(); - _mode_delay = 50 + ((500 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX); - } else { - for(uint16_t i=0; i < _led_count; i=i+3) { - if (!_locked[i+(j/2)]) - setPixelColor(i+(j/2), _color_sec); - } - _mode_delay = 1; - } -} - - -/* - * Theatre-style crawling lights with rainbow effect. - * Inspired by the Adafruit examples. - */ -void WS2812FX::mode_theater_chase_rainbow(void) { - byte j = _counter_mode_call % 6; - if(j % 2 == 0) { - for(uint16_t i=0; i < _led_count; i=i+3) { - if (!_locked[i+(j/2)]) - setPixelColor(i+(j/2), color_wheel((i+_counter_mode_step) % 256)); - } - show(); - _mode_delay = 50 + ((500 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX); - } else { - for(uint16_t i=0; i < _led_count; i=i+3) { - if (!_locked[i+(j/2)]) - setPixelColor(i+(j/2), _color_sec); - } - _mode_delay = 1; - } - _counter_mode_step = (_counter_mode_step + 1) % 256; -} - - -/* - * Running lights effect with smooth sine transition. - */ -void WS2812FX::mode_running_lights(void) { - byte w = ((_color >> 24) & 0xFF); - byte r = ((_color >> 16) & 0xFF); - byte g = ((_color >> 8) & 0xFF); - byte b = (_color & 0xFF); - - for(uint16_t i=0; i < _led_count; i++) { - int s = (sin(i+_counter_mode_call) * 127) + 128; - if (!_locked[i]) - setPixelColor(i, (((uint32_t)(r * s)) / 255), (((uint32_t)(g * s)) / 255), (((uint32_t)(b * s)) / 255), (((uint32_t)(w * s)) / 255)); - } - - show(); - - _mode_delay = 35 + ((350 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX); -} - - -/* - * Blink several LEDs on, reset, repeat. - * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ - */ -void WS2812FX::mode_twinkle(void) { - if(_counter_mode_step == 0) { - for (int i = 0; i < _led_count; i++) - { - setPixelColor(i, _color_sec); - } - uint16_t min_leds = maxval(1, _led_count/5); // make sure, at least one LED is on - uint16_t max_leds = maxval(1, _led_count/2); // make sure, at least one LED is on - _counter_mode_step = random(min_leds, max_leds); - } - int ran = random(_led_count); - if (!_locked[ran]) - setPixelColor(ran, _mode_color); - show(); - - _counter_mode_step--; - _mode_delay = 50 + ((1986 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX); -} - - -/* - * Blink several LEDs in random colors on, reset, repeat. - * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ - */ -void WS2812FX::mode_twinkle_random(void) { - _mode_color = color_wheel(random(256)); - mode_twinkle(); -} - - -/* - * Blink several LEDs on, fading out. - */ -void WS2812FX::mode_twinkle_fade(void) { - - for(uint16_t i=0; i < _led_count; i++) { - uint32_t px_rgb = getPixelColor(i); - - byte px_w = (px_rgb & 0xFF000000) >> 24; - byte px_r = (px_rgb & 0x00FF0000) >> 16; - byte px_g = (px_rgb & 0x0000FF00) >> 8; - byte px_b = (px_rgb & 0x000000FF) >> 0; - - // fade out (divide by 2) - px_w = px_w >> 1; - px_r = px_r >> 1; - px_g = px_g >> 1; - px_b = px_b >> 1; - if (!_locked[i]) - setPixelColor(i, px_r, px_g, px_b, px_w); - } - - if(random(256) < _intensity) { - int ran = random(_led_count); - if (!_locked[ran]) - setPixelColor(ran, _mode_color); - } - - show(); - - _mode_delay = 100 + ((100 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX); -} - - -/* - * Blink several LEDs in random colors on, fading out. - */ -void WS2812FX::mode_twinkle_fade_random(void) { - _mode_color = color_wheel(random(256)); - mode_twinkle_fade(); -} - - -/* - * Blinks one LED at a time. - * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ - */ -void WS2812FX::mode_sparkle(void) { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, _color_sec); - } - int ran = random(_led_count); - if (!_locked[ran]) - setPixelColor(ran ,_color); - show(); - _mode_delay = 10 + ((200 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX); -} - - -/* - * Lights all LEDs in the _color. Flashes single secondary color pixels randomly. - * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ - */ -void WS2812FX::mode_flash_sparkle(void) { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, _color); - } - - if(random(256) <= _intensity) { - int ran = random(_led_count); - if (!_locked[ran]) - setPixelColor(ran , _color_sec); - _mode_delay = 20; - } else { - _mode_delay = 20 + ((200 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX); - } - - show(); -} - - -/* - * Like flash sparkle. With more flash. - * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ - */ -void WS2812FX::mode_hyper_sparkle(void) { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, _color); - } - - if(random(256) <= _intensity) { - for(uint16_t i=0; i < maxval(1, _led_count/3); i++) { - int ran = random(_led_count); - if (!_locked[ran]) - setPixelColor(ran , _color_sec); - } - _mode_delay = 20; - } else { - _mode_delay = 15 + ((120 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX); - } - show(); -} - - -/* - * Classic Strobe effect. - */ -void WS2812FX::mode_strobe(void) { - if(_counter_mode_call % 2 == 0) { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, _color); - } - _mode_delay = 20; - } else { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, _color_sec); - } - _mode_delay = 50 + ((1986 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX); - } - show(); -} - - -/* - * Strobe effect with different strobe count and pause, controlled by _speed. - */ -void WS2812FX::mode_multi_strobe(void) { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, _color_sec); - } - - if(_counter_mode_step < (2 * ((_speed / 10) + 1))) { - if(_counter_mode_step % 2 == 0) { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, _color); - } - _mode_delay = 20; - } else { - _mode_delay = 50; - } - - } else { - _mode_delay = 100 + ((9 - (_speed % 10)) * 125); - } - - show(); - _counter_mode_step = (_counter_mode_step + 1) % ((2 * ((_speed / 10) + 1)) + 1); -} - - -/* - * Classic Strobe effect. Cycling through the rainbow. - */ -void WS2812FX::mode_strobe_rainbow(void) { - if(_counter_mode_call % 2 == 0) { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, color_wheel(_counter_mode_call % 256)); - } - _mode_delay = 20; - } else { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, _color_sec); - } - _mode_delay = 50 + ((1986 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX); - } - show(); -} - - -/* - * Classic Blink effect. Cycling through the rainbow. - */ -void WS2812FX::mode_blink_rainbow(void) { - if(_counter_mode_call % 2 == 1) { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, color_wheel(_counter_mode_call % 256)); - } - } else { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, _color_sec); - } - } - show(); - _mode_delay = 100 + ((1986 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX); -} - - -/* - * Android loading circle - */ -void WS2812FX::mode_android(void) { - if (_counter_mode_call == 0) _mode_color = 0; //we use modecolor as bool - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, _color_sec); - } - - uint16_t a = _counter_mode_step; - if (_mode_var1 > ((float)_intensity/255.0)*(float)_led_count) - { - _mode_color = 1; - } else - { - if (_mode_var1 < 2) _mode_color = 0; - } - - if (_mode_color == 0) - { - if (_counter_mode_call %3 == 1) {a++;} - else {_mode_var1++;} - } else - { - a++; - if (_counter_mode_call %3 != 1) _mode_var1--; - } - - if (a >= _led_count) a = 0; - - if (a +_mode_var1 <= _led_count) - { - for(int i = a; i < a+_mode_var1; i++) { - if (!_locked[i]) - setPixelColor(i, _color); - } - } else - { - for(int i = a; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, _color); - } - for(int i = 0; i < _mode_var1 - (_led_count - a); i++) { - if (!_locked[i]) - setPixelColor(i, _color); - } - } - _counter_mode_step = a; - - show(); - _mode_delay = 3 + ((8 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - - -/* - * _color_sec running on _color. - */ -void WS2812FX::mode_chase_color(void) { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, _color_sec); - } - - uint16_t n = _counter_mode_step; - uint16_t m = (_counter_mode_step + 1) % _led_count; - if (!_locked[n]) - setPixelColor(n, _color); - if (!_locked[m]) - setPixelColor(m, _color); - show(); - - _counter_mode_step = (_counter_mode_step + 1) % _led_count; - _mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - - -/* - * color_sec running followed by random color. - */ -void WS2812FX::mode_chase_random(void) { - if(_counter_mode_step == 0) { - if (!_locked[_led_count-1]) - setPixelColor(_led_count-1, color_wheel(_mode_color)); - _mode_color = get_random_wheel_index(_mode_color); - } - - for(uint16_t i=0; i < _counter_mode_step; i++) { - if (!_locked[i]) - setPixelColor(i, color_wheel(_mode_color)); - } - - uint16_t n = _counter_mode_step; - uint16_t m = (_counter_mode_step + 1) % _led_count; - if (!_locked[n]) - setPixelColor(n, _color_sec); - if (!_locked[m]) - setPixelColor(m, _color_sec); - - show(); - - _counter_mode_step = (_counter_mode_step + 1) % _led_count; - _mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - - -/* - * color_sec running on rainbow. - */ -void WS2812FX::mode_chase_rainbow(void) { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, color_wheel(((i * 256 / _led_count) + (_counter_mode_call % 256)) % 256)); - } - - uint16_t n = _counter_mode_step; - uint16_t m = (_counter_mode_step + 1) % _led_count; - if (!_locked[n]) - setPixelColor(n, _color_sec); - if (!_locked[m]) - setPixelColor(m, _color_sec); - show(); - - _counter_mode_step = (_counter_mode_step + 1) % _led_count; - _mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - - -/* - * _color_sec flashes running on _color. - */ -void WS2812FX::mode_chase_flash(void) { - const static byte flash_count = 4; - byte flash_step = _counter_mode_call % ((flash_count * 2) + 1); - - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, _color); - } - - if(flash_step < (flash_count * 2)) { - if(flash_step % 2 == 0) { - uint16_t n = _counter_mode_step; - uint16_t m = (_counter_mode_step + 1) % _led_count; - if (!_locked[n]) - setPixelColor(n, _color_sec); - if (!_locked[m]) - setPixelColor(m, _color_sec); - _mode_delay = 20; - } else { - _mode_delay = 30; - } - } else { - _counter_mode_step = (_counter_mode_step + 1) % _led_count; - _mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); - } - - show(); -} - - -/* - * _color_sec flashes running, followed by random color. - */ -void WS2812FX::mode_chase_flash_random(void) { - const static byte flash_count = 4; - byte flash_step = _counter_mode_call % ((flash_count * 2) + 1); - - for(uint16_t i=0; i < _counter_mode_step; i++) { - if (!_locked[i]) - setPixelColor(i, color_wheel(_mode_color)); - } - - if(flash_step < (flash_count * 2)) { - uint16_t n = _counter_mode_step; - uint16_t m = (_counter_mode_step + 1) % _led_count; - if(flash_step % 2 == 0) { - if (!_locked[n]) - setPixelColor(n, _color_sec); - if (!_locked[m]) - setPixelColor(m, _color_sec); - _mode_delay = 20; - } else { - if (!_locked[n]) - setPixelColor(n, color_wheel(_mode_color)); - if (!_locked[m]) - setPixelColor(m, 0, 0, 0); - _mode_delay = 30; - } - } else { - _counter_mode_step = (_counter_mode_step + 1) % _led_count; - _mode_delay = 1 + ((10 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); - - if(_counter_mode_step == 0) { - _mode_color = get_random_wheel_index(_mode_color); - } - } - - show(); -} - - -/* - * Rainbow running on _color_sec. - */ -void WS2812FX::mode_chase_rainbow_white(void) { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, _color_sec); - } - - uint16_t n = _counter_mode_step; - uint16_t m = (_counter_mode_step + 1) % _led_count; - if (!_locked[n]) - setPixelColor(n, color_wheel(((n * 256 / _led_count) + (_counter_mode_call % 256)) % 256)); - if (!_locked[m]) - setPixelColor(m, color_wheel(((m * 256 / _led_count) + (_counter_mode_call % 256)) % 256)); - show(); - - _counter_mode_step = (_counter_mode_step + 1) % _led_count; - _mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - -/* - * Red - Amber - Green - Blue lights running - */ -void WS2812FX::mode_colorful(void) { - uint32_t cols[]{0x00FF0000,0x00EEBB00,0x0000EE00,0x000077CC,0x00FF0000,0x00EEBB00,0x0000EE00}; - mode_colorful_internal(cols); -} - -/* - * Common function for 4-color-running (Colorful, easter) - */ -void WS2812FX::mode_colorful_internal(uint32_t cols[]) { - int i = 0; - for (i; i < _led_count ; i+=4) - { - if(!_locked[i])setPixelColor(i, cols[_counter_mode_step]); - if(!_locked[i+1])setPixelColor(i+1, cols[_counter_mode_step+1]); - if(!_locked[i+2])setPixelColor(i+2, cols[_counter_mode_step+2]); - if(!_locked[i+3])setPixelColor(i+3, cols[_counter_mode_step+3]); - } - i+=4; - if(i < _led_count && !_locked[i]) - { - setPixelColor(i, cols[_counter_mode_step]); - - if(i+1 < _led_count && !_locked[i+1]) - { - setPixelColor(i+1, cols[_counter_mode_step+1]); - - if(i+2 < _led_count && !_locked[i+2]) - { - setPixelColor(i+2, cols[_counter_mode_step+2]); + 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(delay, 5); + SEGMENT_RUNTIME.counter_mode_call++; } } + if(doShow) { + show(); + } + _triggered = false; } - - show(); - if (_speed > SPEED_MIN) _counter_mode_step++; //static if lowest speed - if (_counter_mode_step >3) _counter_mode_step = 0; - _mode_delay = 50 + (15 * (uint32_t)(SPEED_MAX - _speed)); } +void WS2812FX::clear() +{ + bus->ClearTo(RgbColor(0)); +} -/* - * Emulates a traffic light. - */ -void WS2812FX::mode_traffic_light(void) { - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, _color_sec); - } - for (int i = 0; i < _led_count-2 ; i+=3) +void WS2812FX::setPixelColor(uint16_t n, uint32_t c) { + uint8_t w = (c >> 24) & 0xFF; + uint8_t r = (c >> 16) & 0xFF; + uint8_t g = (c >> 8) & 0xFF; + uint8_t b = c & 0xFF; + setPixelColor(n, r, g, b, w); +} + +void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) +{ + if (_reverseMode) i = _length - 1 -i; + if (_locked[i] && SEGMENT.mode != FX_MODE_FIRE_2012) return; + if (IS_REVERSE) i = SEGMENT.stop - (i - SEGMENT.start); //reverse just individual segment + if (!_cronixieMode) { - switch (_counter_mode_step) - { - case 0: if(!_locked[i])setPixelColor(i, 0x00FF0000); _mode_delay = 150 + (100 * (uint32_t)(SPEED_MAX - _speed));break; - case 1: if(!_locked[i])setPixelColor(i, 0x00FF0000); _mode_delay = 150 + (20 * (uint32_t)(SPEED_MAX - _speed)); if(!_locked[i+1])setPixelColor(i+1, 0x00EECC00); break; - case 2: if(!_locked[i+2])setPixelColor(i+2, 0x0000FF00); _mode_delay = 150 + (100 * (uint32_t)(SPEED_MAX - _speed));break; - case 3: if(!_locked[i+1])setPixelColor(i+1, 0x00EECC00); _mode_delay = 150 + (20 * (uint32_t)(SPEED_MAX - _speed));break; - } - } - show(); - _counter_mode_step++; - if (_counter_mode_step >3) _counter_mode_step = 0; -} - - -/* - * Random color intruduced alternating from start and end of strip. - */ -void WS2812FX::mode_color_sweep_random(void) { - if(_counter_mode_step == 0 || _counter_mode_step == _led_count) { - _mode_color = get_random_wheel_index(_mode_color); - } - - if(_counter_mode_step < _led_count) { - if (!_locked[_counter_mode_step]) - setPixelColor(_counter_mode_step, color_wheel(_mode_color)); + if (_skipFirstMode) {i++;if(i==1)bus->SetPixelColor(i, RgbwColor(0,0,0,0));} + bus->SetPixelColor(i, RgbwColor(r,g,b,w)); } else { - if (!_locked[(_led_count * 2) - _counter_mode_step - 1]) - setPixelColor((_led_count * 2) - _counter_mode_step - 1, color_wheel(_mode_color)); - } - show(); - - _counter_mode_step = (_counter_mode_step + 1) % (_led_count * 2); - _mode_delay = 5 + ((50 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - - -/* - * Alternating color/2nd pixels running. - */ -void WS2812FX::mode_running_color(void) { - for(uint16_t i=0; i < _led_count; i++) { - if((i + _counter_mode_step) % 4 < 2) { - if (!_locked[i]) - setPixelColor(i, _mode_color); - } else { - if (!_locked[i]) - setPixelColor(i, _color_sec); - } - } - show(); - - _counter_mode_step = (_counter_mode_step + 1) % 4; - _mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - - -/* - * Alternating red/blue pixels running. (RED) - */ -void WS2812FX::mode_running_red_blue(void) { - for(uint16_t i=0; i < _led_count; i++) { - if((i + _counter_mode_step) % 4 < 2) { - if (!_locked[i]) - setPixelColor(i, 255, 0, 0); - } else { - if (!_locked[i]) - setPixelColor(i, 0, 0, 255); - } - } - show(); - - _counter_mode_step = (_counter_mode_step + 1) % 4; - _mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - - -/* - * Random colored pixels running. - */ -void WS2812FX::mode_running_random(void) { - for(uint16_t i=_led_count-1; i > 0; i--) { - if (!_locked[i]) + if(i>6)return; + byte o = 10*i; + if (_cronixieBacklightEnabled && _cronixieDigits[i] <11) { - if (!_locked[i-1]) + byte rCorr = (int)(((double)((_segments[0].colors[1]>>16) & 0xFF))*_cronixieSecMultiplier); + byte gCorr = (int)(((double)((_segments[0].colors[1]>>8) & 0xFF))*_cronixieSecMultiplier); + byte bCorr = (int)(((double)((_segments[0].colors[1]) & 0xFF))*_cronixieSecMultiplier); + byte wCorr = (int)(((double)((_segments[0].colors[1]>>24) & 0xFF))*_cronixieSecMultiplier); + for (int j=o; j< o+19; j++) { - setPixelColor(i, getPixelColor(i-1)); - } else + bus->SetPixelColor((_skipFirstMode)?j+1:j,RgbwColor(rCorr,gCorr,bCorr,wCorr)); + } + } else + { + for (int j=o; j< o+19; j++) { - setPixelColor(i, color_wheel(_mode_color)); + bus->SetPixelColor((_skipFirstMode)?j+1:j,RgbwColor(0,0,0,0)); } } - } - - if(_counter_mode_step == 0) { - _mode_color = get_random_wheel_index(_mode_color); - if (!_locked[0]) - setPixelColor(0, color_wheel(_mode_color)); - } - - show(); - - _counter_mode_step = (_counter_mode_step + 1) % 2; - - _mode_delay = 10 + ((30 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - - -/* - * K.I.T.T. - */ -void WS2812FX::mode_larson_scanner(void) { - - for(uint16_t i=0; i < _led_count; i++) { - uint32_t px_rgb = getPixelColor(i); - - byte px_r = (px_rgb & 0x00FF0000) >> 16; - byte px_g = (px_rgb & 0x0000FF00) >> 8; - byte px_b = (px_rgb & 0x000000FF) >> 0; - - // fade out (divide by 2) - px_r = px_r >> 1; - px_g = px_g >> 1; - px_b = px_b >> 1; - - if (!_locked[i]) - setPixelColor(i, px_r, px_g, px_b); - } - - uint16_t pos = 0; - - if(_counter_mode_step < _led_count) { - pos = _counter_mode_step; - } else { - pos = (_led_count * 2) - _counter_mode_step - 2; - } - - if (!_locked[pos]) - setPixelColor(pos, _color); - show(); - - _counter_mode_step = (_counter_mode_step + 1) % ((_led_count * 2) - 2); - _mode_delay = 10 + ((10 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - - -/* - * Fireing comets from one end. - */ -void WS2812FX::mode_comet(void) { - - for(uint16_t i=0; i < _led_count; i++) { - uint32_t px_rgb = getPixelColor(i); - - byte px_r = (px_rgb & 0x00FF0000) >> 16; - byte px_g = (px_rgb & 0x0000FF00) >> 8; - byte px_b = (px_rgb & 0x000000FF) >> 0; - - // fade out (divide by 2) - px_r = px_r >> 1; - px_g = px_g >> 1; - px_b = px_b >> 1; - - if (!_locked[i]) - setPixelColor(i, px_r, px_g, px_b); - } - - if (!_locked[_counter_mode_step]) - setPixelColor(_counter_mode_step, _color); - show(); - - _counter_mode_step = (_counter_mode_step + 1) % _led_count; - _mode_delay = 10 + ((10 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - - -/* - * Firework sparks. - */ -void WS2812FX::mode_fireworks(void) { - uint32_t px_rgb = 0; - byte px_r = 0; - byte px_g = 0; - byte px_b = 0; - - for(uint16_t i=0; i < _led_count; i++) { - px_rgb = getPixelColor(i); - - px_r = (px_rgb & 0x00FF0000) >> 16; - px_g = (px_rgb & 0x0000FF00) >> 8; - px_b = (px_rgb & 0x000000FF) >> 0; - - // fade out (divide by 2) - px_r = px_r >> 1; - px_g = px_g >> 1; - px_b = px_b >> 1; - - if (!_locked[i]) - setPixelColor(i, px_r, px_g, px_b); - } - - // first LED has only one neighbour - px_r = (((getPixelColor(1) & 0x00FF0000) >> 16) >> 1) + ((getPixelColor(0) & 0x00FF0000) >> 16); - px_g = (((getPixelColor(1) & 0x0000FF00) >> 8) >> 1) + ((getPixelColor(0) & 0x0000FF00) >> 8); - px_b = (((getPixelColor(1) & 0x000000FF) >> 0) >> 1) + ((getPixelColor(0) & 0x000000FF) >> 0); - if (!_locked[0]) - setPixelColor(0, px_r, px_g, px_b); - - // set brightness(i) = ((brightness(i-1)/2 + brightness(i+1)) / 2) + brightness(i) - for(uint16_t i=1; i < _led_count-1; i++) { - px_r = (( - (((getPixelColor(i-1) & 0x00FF0000) >> 16) >> 1) + - (((getPixelColor(i+1) & 0x00FF0000) >> 16) >> 0) ) >> 1) + - (((getPixelColor(i ) & 0x00FF0000) >> 16) >> 0); - - px_g = (( - (((getPixelColor(i-1) & 0x0000FF00) >> 8) >> 1) + - (((getPixelColor(i+1) & 0x0000FF00) >> 8) >> 0) ) >> 1) + - (((getPixelColor(i ) & 0x0000FF00) >> 8) >> 0); - - px_b = (( - (((getPixelColor(i-1) & 0x000000FF) >> 0) >> 1) + - (((getPixelColor(i+1) & 0x000000FF) >> 0) >> 0) ) >> 1) + - (((getPixelColor(i ) & 0x000000FF) >> 0) >> 0); - - if (!_locked[i]) - setPixelColor(i, px_r, px_g, px_b); - } - - // last LED has only one neighbour - px_r = (((getPixelColor(_led_count-2) & 0x00FF0000) >> 16) >> 2) + ((getPixelColor(_led_count-1) & 0x00FF0000) >> 16); - px_g = (((getPixelColor(_led_count-2) & 0x0000FF00) >> 8) >> 2) + ((getPixelColor(_led_count-1) & 0x0000FF00) >> 8); - px_b = (((getPixelColor(_led_count-2) & 0x000000FF) >> 0) >> 2) + ((getPixelColor(_led_count-1) & 0x000000FF) >> 0); - if (!_locked[_led_count-1]) - setPixelColor(_led_count-1, px_r, px_g, px_b); - - for(uint16_t i=0; i> 24; - byte p_r = (_color & 0x00FF0000) >> 16; - byte p_g = (_color & 0x0000FF00) >> 8; - byte p_b = (_color & 0x000000FF) >> 0; - byte flicker_val = maxval(p_r,maxval(p_g, maxval(p_b, p_w)))/(((256-_intensity)/16)+1); - for(uint16_t i=0; i < _led_count; i++) + switch(_cronixieDigits[i]) { - int flicker = random(0,flicker_val); - int r1 = p_r-flicker; - int g1 = p_g-flicker; - int b1 = p_b-flicker; - int w1 = p_w-flicker; - if(g1<0) g1=0; - if(r1<0) r1=0; - if(b1<0) b1=0; - if(w1<0) w1=0; - if (!_locked[i]) - setPixelColor(i,r1,g1,b1,w1); - } - show(); - _mode_delay = 10 + ((400 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX); -} - -/* - * Gradient run - */ -void WS2812FX::mode_gradient(void) { - byte p_w = (_color & 0xFF000000) >> 24; - byte p_r = (_color & 0x00FF0000) >> 16; - byte p_g = (_color & 0x0000FF00) >> 8; - byte p_b = (_color & 0x000000FF) >> 0; - byte p_w2 = (_color_sec & 0xFF000000) >> 24; - byte p_r2 = (_color_sec & 0x00FF0000) >> 16; - byte p_g2 = (_color_sec & 0x0000FF00) >> 8; - byte p_b2 = (_color_sec & 0x000000FF) >> 0; - byte nw,nr,ng,nb; - float per,val; //0.0 = sec 1.0 = pri - float brd = _intensity/2; if (brd <1.0) brd = 1.0; - int pp = _counter_mode_step; - int p1 = pp-_led_count; - int p2 = pp+_led_count; - - for(uint16_t i=0; i < _led_count; i++) - { - if (!_locked[i]) - { - val = minval(abs(pp-i),minval(abs(p1-i),abs(p2-i))); - per = val/brd; - if (per >1.0) per = 1.0; - nw = p_w+((p_w2 - p_w)*per); - nr = p_r+((p_r2 - p_r)*per); - ng = p_g+((p_g2 - p_g)*per); - nb = p_b+((p_b2 - p_b)*per); - setPixelColor(i,nr,ng,nb,nw); - } - } - - show(); - _counter_mode_step++; - if (_counter_mode_step >= _led_count) _counter_mode_step = 0; - if (_speed == 0) _counter_mode_step = _led_count >> 1; - _mode_delay = 7 + ((25 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - -/* - * Gradient run with hard transition - */ -void WS2812FX::mode_loading(void) { - byte p_w = (_color & 0xFF000000) >> 24; - byte p_r = (_color & 0x00FF0000) >> 16; - byte p_g = (_color & 0x0000FF00) >> 8; - byte p_b = (_color & 0x000000FF) >> 0; - byte p_w2 = (_color_sec & 0xFF000000) >> 24; - byte p_r2 = (_color_sec & 0x00FF0000) >> 16; - byte p_g2 = (_color_sec & 0x0000FF00) >> 8; - byte p_b2 = (_color_sec & 0x000000FF) >> 0; - byte nw,nr,ng,nb; - float per,val; //0.0 = sec 1.0 = pri - float brd = _intensity; if (brd <1.0) brd = 1.0; - int pp = _counter_mode_step; - int p1 = pp+_led_count; - - for(uint16_t i=0; i < _led_count; i++) - { - if (!_locked[i]) - { - pp = _counter_mode_step; - if (i > pp) pp+=_led_count; - val = abs(pp-i); - per = val/brd; - if (per >1.0) per = 1.0; - nw = p_w+((p_w2 - p_w)*per); - nr = p_r+((p_r2 - p_r)*per); - ng = p_g+((p_g2 - p_g)*per); - nb = p_b+((p_b2 - p_b)*per); - setPixelColor(i,nr,ng,nb,nw); - } - } - - show(); - _counter_mode_step++; - if (_counter_mode_step >= _led_count) _counter_mode_step = 0; - if (_speed == 0) _counter_mode_step = _led_count -1; - _mode_delay = 7 + ((25 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - -/* - * Lights all LEDs after each other up starting from the outer edges and - * finishing in the middle. Then turns them in reverse order off. Repeat. - */ -void WS2812FX::mode_dual_color_wipe_in_out(void) { - int end = _led_count - _counter_mode_step - 1; - bool odd = (_led_count % 2); - int mid = odd ? ((_led_count / 2) + 1) : (_led_count / 2); - if (_counter_mode_step < mid) { - if (!_locked[_counter_mode_step]) - setPixelColor(_counter_mode_step, _color); - if (!_locked[end]) - setPixelColor(end, _color); - } - else { - if (odd) { - // If odd, we need to 'double count' the center LED (once to turn it on, - // once to turn it off). So trail one behind after the middle LED. - if (!_locked[_counter_mode_step -1]) - setPixelColor(_counter_mode_step - 1, _color_sec); - if (!_locked[end+1]) - setPixelColor(end + 1, _color_sec); - } else { - if (!_locked[_counter_mode_step]) - setPixelColor(_counter_mode_step, _color_sec); - if (!_locked[end]) - setPixelColor(end, _color_sec); + case 0: bus->SetPixelColor((_skipFirstMode)?o+6:o+5,RgbwColor(r,g,b,w)); break; + case 1: bus->SetPixelColor((_skipFirstMode)?o+1:o+0,RgbwColor(r,g,b,w)); break; + case 2: bus->SetPixelColor((_skipFirstMode)?o+7:o+6,RgbwColor(r,g,b,w)); break; + case 3: bus->SetPixelColor((_skipFirstMode)?o+2:o+1,RgbwColor(r,g,b,w)); break; + case 4: bus->SetPixelColor((_skipFirstMode)?o+8:o+7,RgbwColor(r,g,b,w)); break; + case 5: bus->SetPixelColor((_skipFirstMode)?o+3:o+2,RgbwColor(r,g,b,w)); break; + case 6: bus->SetPixelColor((_skipFirstMode)?o+9:o+8,RgbwColor(r,g,b,w)); break; + case 7: bus->SetPixelColor((_skipFirstMode)?o+4:o+3,RgbwColor(r,g,b,w)); break; + case 8: bus->SetPixelColor((_skipFirstMode)?o+10:o+9,RgbwColor(r,g,b,w)); break; + case 9: bus->SetPixelColor((_skipFirstMode)?o+5:o+4,RgbwColor(r,g,b,w)); break; + default: break; } } - - _counter_mode_step++; - if (odd) { - if (_counter_mode_step > _led_count) { - _counter_mode_step = 0; - } - } else { - if (_counter_mode_step >= _led_count) { - _counter_mode_step = 0; - } - } - - show(); - - _mode_delay = 5 + ((50 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - -/* - * Lights all LEDs after each other up starting from the outer edges and - * finishing in the middle. Then turns them in that order off. Repeat. - */ -void WS2812FX::mode_dual_color_wipe_in_in(void) { - bool odd = (_led_count % 2); - int mid = _led_count / 2; - if (odd) { - if (_counter_mode_step <= mid) { - if (!_locked[_counter_mode_step]) - setPixelColor(_counter_mode_step, _color); - if (!_locked[_led_count - _counter_mode_step - 1]) - setPixelColor(_led_count - _counter_mode_step - 1, _color); - } else { - int i = _counter_mode_step - mid; - if (!_locked[i-1]) - setPixelColor(i - 1, _color_sec); - if (!_locked[_led_count - i]) - setPixelColor(_led_count - i, _color_sec); - } - } else { - if (_counter_mode_step < mid) { - if (!_locked[_counter_mode_step]) - setPixelColor(_counter_mode_step, _color); - if (!_locked[_led_count - _counter_mode_step - 1]) - setPixelColor(_led_count - _counter_mode_step - 1, _color); - } else { - int i = _counter_mode_step - mid; - if (!_locked[i]) - setPixelColor(i, _color_sec); - if (!_locked[_led_count - i -1]) - setPixelColor(_led_count - i - 1, _color_sec); - } - } - - _counter_mode_step++; - if (odd) { - if (_counter_mode_step > _led_count) { - _counter_mode_step = 0; - } - } else { - if (_counter_mode_step >= _led_count) { - _counter_mode_step = 0; - } - } - - show(); - - _mode_delay = 5 + ((50 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - -/* - * Lights all LEDs after each other up starting from the middle and - * finishing at the edges. Then turns them in that order off. Repeat. - */ -void WS2812FX::mode_dual_color_wipe_out_out(void) { - int end = _led_count - _counter_mode_step - 1; - bool odd = (_led_count % 2); - int mid = _led_count / 2; - - if (odd) { - if (_counter_mode_step <= mid) { - if (!_locked[mid + _counter_mode_step]) - setPixelColor(mid + _counter_mode_step, _color); - if (!_locked[mid - _counter_mode_step]) - setPixelColor(mid - _counter_mode_step, _color); - } else { - if (!_locked[_counter_mode_step -1]) - setPixelColor(_counter_mode_step - 1, _color_sec); - if (!_locked[end +1]) - setPixelColor(end + 1, _color_sec); - } - } else { - if (_counter_mode_step < mid) { - if (!_locked[mid - _counter_mode_step -1]) - setPixelColor(mid - _counter_mode_step - 1, _color); - if (!_locked[mid + _counter_mode_step]) - setPixelColor(mid + _counter_mode_step, _color); - } else { - if (!_locked[_counter_mode_step]) - setPixelColor(_counter_mode_step, 0); - if (!_locked[end]) - setPixelColor(end, _color_sec); - } - } - - _counter_mode_step++; - if (odd) { - if (_counter_mode_step > _led_count) { - _counter_mode_step = 0; - } - } else { - if (_counter_mode_step >= _led_count) { - _counter_mode_step = 0; - } - } - - show(); - - _mode_delay = 5 + ((50 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - -/* - * Lights all LEDs after each other up starting from the middle and - * finishing at the edges. Then turns them in reverse order off. Repeat. - */ -void WS2812FX::mode_dual_color_wipe_out_in(void) { - bool odd = (_led_count % 2); - int mid = _led_count / 2; - - if (odd) { - if (_counter_mode_step <= mid) { - if (!_locked[mid + _counter_mode_step]) - setPixelColor(mid + _counter_mode_step, _color); - if (!_locked[mid - _counter_mode_step]) - setPixelColor(mid - _counter_mode_step, _color); - } else { - int i = _counter_mode_step - mid; - if (!_locked[i -1]) - setPixelColor(i - 1, _color_sec); - if (!_locked[_led_count - i]) - setPixelColor(_led_count - i, _color_sec); - } - } else { - if (_counter_mode_step < mid) { - if (!_locked[mid - _counter_mode_step -1]) - setPixelColor(mid - _counter_mode_step - 1, _color); - if (!_locked[mid + _counter_mode_step]) - setPixelColor(mid + _counter_mode_step, _color); - } else { - int i = _counter_mode_step - mid; - if (!_locked[i]) - setPixelColor(i, _color_sec); - if (!_locked[_led_count - i -1]) - setPixelColor(_led_count - i - 1, _color_sec); - } - } - - _counter_mode_step++; - if (odd) { - if (_counter_mode_step > _led_count) { - _counter_mode_step = 0; - } - } else { - if (_counter_mode_step >= _led_count) { - _counter_mode_step = 0; - } - } - - show(); - - _mode_delay = 5 + ((50 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - -/* - * Alternating pri/sec/black pixels running. - */ -void WS2812FX::mode_circus_combustus(void) { - for(uint16_t i=0; i < _led_count; i++) { - if((i + _counter_mode_step) % 6 < 2) { - if (!_locked[i]) - setPixelColor(i, _color); - } else if((i + _color) % 6 < 4){ - if (!_locked[i]) - setPixelColor(i, _color_sec); - } else { - if (!_locked[i]) - setPixelColor(i, 0, 0, 0); - } - } - show(); - - _counter_mode_step = (_counter_mode_step + 1) % 6; - _mode_delay = 100 + ((100 * (uint32_t)(SPEED_MAX - _speed)) / _led_count); -} - -void WS2812FX::mode_cc_core() -{ - for (int k = _cc_i1; k <= _cc_i2; k = k + _cc_num1 + _cc_num2) - { - for (int i = 0; i < _cc_num1; i++) - { - int num = 0; - num = ((k + i + _counter_ccStep) % _cc_i2) +_cc_i1; - if (_cc_fs) setPixelColor(num, _color); - if (_cc_fe) setPixelColor(_cc_i2 - num, _color); - } - } - show(); - _counter_ccStep = (_counter_ccStep + _ccStep) % (_cc_i2 - _cc_i1); - _mode_delay = 10 + ((250 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX); -} - -void WS2812FX::mode_cc_standard() -{ - for(uint16_t i=0; i < _led_count; i++) - { - setPixelColor(i, (_cc_i1 <= i && i <= _cc_i2) ? _color_sec : _color); - } - mode_cc_core(); -} - -void WS2812FX::mode_cc_rainbow() -{ - uint32_t color = color_wheel(_counter_mode_step); - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, color); - } - mode_cc_core(); - _counter_mode_step = (_counter_mode_step + 1) % 256; -} - -void WS2812FX::mode_cc_cycle() -{ - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, color_wheel(((i * 256 / _led_count) + _counter_mode_step) % 256)); - } - mode_cc_core(); - _counter_mode_step = (_counter_mode_step + 1) % 256; -} - -void WS2812FX::mode_cc_blink() -{ - for(uint16_t i=0; i < _led_count; i++) - { - setPixelColor(i, (_cc_i1 <= i && i <= _cc_i2) ? _color_sec : _color); - } - if (_counter_mode_step) - { - mode_cc_core(); - _counter_mode_step = 0; - } else { - show(); - _counter_mode_step = 1; - _mode_delay = 10 + ((250 * (uint32_t)(SPEED_MAX - _speed)) / SPEED_MAX); - } -} - -void WS2812FX::mode_cc_random() -{ - for(uint16_t i=0; i < _led_count; i++) { - if (!_locked[i]) - setPixelColor(i, color_wheel(random(256))); - } - mode_cc_core(); -} - - -//WLED specific methods - -void WS2812FX::setIndividual(int i) -{ - if (i >= 0 && i < _led_count) - { - setPixelColor(i, _color); - //show(); - _locked[i] = true; - } -} - -void WS2812FX::setIndividual(int i, uint32_t col) -{ - if (i >= 0 && i < _led_count) - { - setPixelColor(i, col); - //show(); - _locked[i] = true; - } -} - -void WS2812FX::setRange(int i, int i2) -{ - if (i2 >= i) - { - for (int x = i; x <= i2; x++) - { - if (x >= 0 && x < _led_count) - { - setPixelColor(x, _color); - _locked[x] = true; - } - } - } else - { - for (int x = i2; x < _led_count; x++) - { - if (x >= 0 && x < _led_count) - { - setPixelColor(x, _color); - _locked[x] = true; - } - } - for (int x = 0; x <= i; x++) - { - if (x >= 0 && x < _led_count) - { - setPixelColor(x, _color); - _locked[x] = true; - } - } - } - //show(); -} - -void WS2812FX::setRange(int i, int i2, uint32_t col) -{ - if (i2 >= i) - { - for (int x = i; x <= i2; x++) - { - if (x >= 0 && x < _led_count) - { - setPixelColor(x, col); - _locked[x] = true; - } - } - } else - { - for (int x = i2; x < _led_count; x++) - { - if (x >= 0 && x < _led_count) - { - setPixelColor(x, col); - _locked[x] = true; - } - } - for (int x = 0; x <= i; x++) - { - if (x >= 0 && x < _led_count) - { - setPixelColor(x, col); - _locked[x] = true; - } - } - } - //show(); -} - -void WS2812FX::lock(int i) -{ - if (i >= 0 && i < _led_count) - _locked[i] = true; -} - -void WS2812FX::lockRange(int i, int i2) -{ - for (int x = i; x < i2; x++) - { - if (x >= 0 && x < _led_count) - _locked[x] = true; - } -} - -void WS2812FX::lockAll() -{ - for (int x = 0; x < _led_count; x++) - _locked[x] = true; -} - -void WS2812FX::unlock(int i) -{ - if (i >= 0 && i < _led_count) - _locked[i] = false; -} - -void WS2812FX::unlockRange(int i, int i2) -{ - for (int x = i; x < i2; x++) - { - if (x >= 0 && x < _led_count) - _locked[x] = false; - } -} - -void WS2812FX::unlockAll() -{ - for (int x = 0; x < _led_count; x++) - _locked[x] = false; -} - -void WS2812FX::setFastUpdateMode(bool y) -{ - _fastStandard = y; - if (_mode_index == 0) _mode_delay = 20; } void WS2812FX::setReverseMode(bool b) @@ -1910,6 +149,7 @@ void WS2812FX::setReverseMode(bool b) void WS2812FX::driverModeCronixie(bool b) { _cronixieMode = b; + if (b) _segments[0].stop = 5; } void WS2812FX::setCronixieBacklight(bool b) @@ -1925,12 +165,242 @@ void WS2812FX::setCronixieDigits(byte d[]) } } +void WS2812FX::show(void) { + bus->Show(); +} + +void WS2812FX::trigger() { + _triggered = true; +} + +void WS2812FX::setMode(uint8_t m) { + RESET_RUNTIME; + bool ua = _segments[0].mode == FX_MODE_FIRE_2012 && m != FX_MODE_FIRE_2012; + _segments[0].mode = constrain(m, 0, MODE_COUNT - 1); + if (ua) unlockAll(); + setBrightness(_brightness); +} + +//TODO transitions + +void WS2812FX::setSpeed(uint8_t s) { + _segments[0].speed = s; +} + +void WS2812FX::setIntensity(uint8_t in) { + _segments[0].intensity = in; +} + +void WS2812FX::setColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w) { + setColor(((uint32_t)w << 24) |((uint32_t)r << 16) | ((uint32_t)g << 8) | b); +} + +void WS2812FX::setSecondaryColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w) { + setSecondaryColor(((uint32_t)w << 24) |((uint32_t)r << 16) | ((uint32_t)g << 8) | b); +} + +void WS2812FX::setColor(uint32_t c) { + _segments[0].colors[0] = c; + _triggered = true; +} + +void WS2812FX::setSecondaryColor(uint32_t c) { + _segments[0].colors[1] = c; + if (_cronixieMode) _cronixieSecMultiplier = getSafePowerMultiplier(900, 100, c, _brightness); + _triggered = true; +} + +void WS2812FX::setBrightness(uint8_t b) { + _brightness = b; + bus->SetBrightness(_brightness); + show(); +} + +uint8_t WS2812FX::getMode(void) { + return _segments[0].mode; +} + +uint8_t WS2812FX::getSpeed(void) { + return _segments[0].speed; +} + +uint8_t WS2812FX::getBrightness(void) { + return _brightness; +} + +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]; +} + +uint32_t WS2812FX::getPixelColor(uint16_t i) +{ + if (_reverseMode) i = _length- 1 -i; + if (_skipFirstMode) i++; + if (_cronixieMode) + { + if(i>6)return 0; + byte o = 10*i; + switch(_cronixieDigits[i]) + { + case 0: i=o+5; break; + case 1: i=o+0; break; + case 2: i=o+6; break; + case 3: i=o+1; break; + case 4: i=o+7; break; + case 5: i=o+2; break; + case 6: i=o+8; break; + case 7: i=o+3; break; + case 8: i=o+9; break; + case 9: i=o+4; break; + default: return 0; + } + } + RgbwColor lColor = bus->GetPixelColorRgbw(i); + return lColor.W*16777216 + lColor.R*65536 + lColor.G*256 + lColor.B; +} + +WS2812FX::Segment WS2812FX::getSegment(void) { + return SEGMENT; +} + +WS2812FX::Segment_runtime WS2812FX::getSegmentRuntime(void) { + return SEGMENT_RUNTIME; +} + +WS2812FX::Segment* WS2812FX::getSegments(void) { + return _segments; +} + +void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint8_t speed, uint8_t intensity, bool reverse) { + uint32_t colors[] = {color, 0, 0}; + setSegment(n, start, stop, mode, colors, speed, intensity, reverse); +} + +void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint8_t speed, uint8_t intensity, bool reverse) { + setSegment(n, start, stop, mode, colors, speed, intensity, (uint8_t)(reverse ? REVERSE : NO_OPTIONS)); +} + +void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint8_t speed, uint8_t intensity, uint8_t options) { + if(n < (sizeof(_segments) / sizeof(_segments[0]))) { + 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].intensity = intensity; + _segments[n].options = options; + + for(uint8_t i=0; i= 0 && i < _length) + { + _locked[i] = false; + setPixelColor(i, col); + _locked[i] = true; + } +} + +void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) +{ + if (i2 >= i) + { + for (uint16_t x = i; x <= i2; x++) setIndividual(x,col); + } else + { + for (uint16_t x = i2; x <= i; x++) setIndividual(x,col); + } +} + +void WS2812FX::lock(uint16_t i) +{ + if (SEGMENT.mode == FX_MODE_FIRE_2012) return; + if (i >= 0 && i < _length) _locked[i] = true; +} + +void WS2812FX::lockRange(uint16_t i, uint16_t i2) +{ + if (SEGMENT.mode == FX_MODE_FIRE_2012) return; + for (uint16_t x = i; x <= i2; x++) + { + if (i >= 0 && i < _length) _locked[i] = true; + } +} + +void WS2812FX::unlock(uint16_t i) +{ + if (SEGMENT.mode == FX_MODE_FIRE_2012) return; + if (i >= 0 && i < _length) _locked[i] = false; +} + +void WS2812FX::unlockRange(uint16_t i, uint16_t i2) +{ + if (SEGMENT.mode == FX_MODE_FIRE_2012) return; + for (uint16_t x = i; x < i2; x++) + { + if (x >= 0 && x < _length) _locked[x] = false; + } +} + +void WS2812FX::unlockAll() +{ + for (int i=0; i < _length; i++) _locked[i] = false; +} + +/* + * color blend function + */ +uint32_t WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint8_t blend) { + if(blend == 0) return color1; + if(blend == 255) return color2; + + int w1 = (color1 >> 24) & 0xff; + int r1 = (color1 >> 16) & 0xff; + int g1 = (color1 >> 8) & 0xff; + int b1 = color1 & 0xff; + + int w2 = (color2 >> 24) & 0xff; + int r2 = (color2 >> 16) & 0xff; + int g2 = (color2 >> 8) & 0xff; + int b2 = color2 & 0xff; + + uint32_t w3 = ((w2 * blend) + (w1 * (255 - blend))) / 256; + uint32_t r3 = ((r2 * blend) + (r1 * (255 - blend))) / 256; + uint32_t g3 = ((g2 * blend) + (g1 * (255 - blend))) / 256; + uint32_t b3 = ((b2 * blend) + (b1 * (255 - blend))) / 256; + + return ((w3 << 24) | (r3 << 16) | (g3 << 8) | (b3)); +} + + double WS2812FX::getPowerEstimate(uint16_t leds, uint32_t c, byte b) { double _mARequired = 100; //ESP power double _mul = (double)b/255; double _sum = ((c & 0xFF000000) >> 24) + ((c & 0x00FF0000) >> 16) + ((c & 0x0000FF00) >> 8) + ((c & 0x000000FF) >> 0); - _sum /= (_rgbwMode)? 1024:768; + _sum /= (_rgbwMode)?1024:768; double _mAPerLed = 50*(_mul*_sum); _mARequired += leds*_mAPerLed; return _mARequired; @@ -1951,215 +421,1723 @@ double WS2812FX::getSafePowerMultiplier(double safeMilliAmps, uint16_t leds, uin return 1.0; } -void WS2812FX::setCCIndex1(byte i1) -{ - if (i1 < _led_count-1) _cc_i1 = i1; - if (_cc_i2 <= i1) _cc_i2 = i1+1; - _counter_ccStep = 0; + +/* ##################################################### +# +# Color and Blinken Functions +# +##################################################### */ + +/* + * Turns everything off. Doh. + */ +void WS2812FX::strip_off() { + clear(); + show(); } -void WS2812FX::setCCIndex2(uint16_t i2) -{ - if (i2 > _cc_i1) _cc_i2 = i2; - if (_cc_i2 >= _led_count) _cc_i2 = _led_count-1; - _counter_ccStep = 0; + +/* + * Put a value 0 to 255 in to get a color value. + * The colours are a transition r -> 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); + } } -void WS2812FX::setCCStart(byte is) -{ - _cc_is = (is < _cc_i1 || is > _cc_i2) ? _cc_i1 : is; - _counter_ccStep = 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 = random8(); + x = abs(pos - r); + y = 255 - x; + d = min(x, y); + } + + return r; } -void WS2812FX::setCCNum1(byte np) -{ - _cc_num1 = np; - _counter_ccStep = 0; + +/* + * No blinking. Just plain old static light. + */ +uint16_t WS2812FX::mode_static(void) { + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + setPixelColor(i, SEGMENT.colors[0]); + } + return 500; } -void WS2812FX::setCCNum2(byte ns) -{ - _cc_num2 = ns; - _counter_ccStep = 0; + +/* + * 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(IS_REVERSE) color = (color == color1) ? color2 : color1; + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + setPixelColor(i, color); + } + + if((SEGMENT_RUNTIME.counter_mode_call & 1) == 0) { + return strobe ? 20 : (100 + ((1986 * (uint32_t)(255 - SEGMENT.speed)) / 255))*(float)(SEGMENT.intensity/128.0); + } else { + return strobe ? 50 + ((1986 * (uint32_t)(255 - SEGMENT.speed)) / 255) : (100 + ((1986 * (uint32_t)(255 - SEGMENT.speed)) / 255))*(float)(2.0-(SEGMENT.intensity/128.0)); + } } -void WS2812FX::setCCStep(byte stp) -{ - _ccStep = stp; - _counter_ccStep = 0; + +/* + * Normal blinking. 50% on/off time. + */ +uint16_t WS2812FX::mode_blink(void) { + return blink(SEGMENT.colors[0], SEGMENT.colors[1], false); } -void WS2812FX::setCCFS(bool fs) -{ - _cc_fs = fs; - _cc_fe = (fs) ? _cc_fe : true; - _counter_ccStep = 0; + +/* + * 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); } -void WS2812FX::setCCFE(bool fe) -{ - _cc_fe = fe; - _cc_fs = (fe) ? _cc_fs : true; - _counter_ccStep = 0; + +/* + * Classic Strobe effect. + */ +uint16_t WS2812FX::mode_strobe(void) { + return blink(SEGMENT.colors[0], SEGMENT.colors[1], true); } -void WS2812FX::setCustomChase(byte i1, uint16_t i2, byte is, byte np, byte ns, byte stp, bool fs, bool fe) -{ - setCCIndex1(i1); - setCCIndex2(i2); - setCCStart(is); - _cc_num1 = np; - _cc_num2 = ns; - _ccStep = stp; - setCCFS(fs); - setCCFE(fe); + +/* + * 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); } -//Added for quick NeoPixelBus compatibility with Adafruit syntax -void WS2812FX::setPixelColorRaw(uint16_t i, byte r, byte g, byte b, byte w) -{ - if (_rgbwMode) + +/* + * 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; + setPixelColor(SEGMENT.start + led_offset, color1); + } else { + uint32_t led_offset = SEGMENT_RUNTIME.counter_mode_step - SEGMENT_LENGTH; + if(rev) { + setPixelColor(SEGMENT.stop - led_offset, color2); + } else { + setPixelColor(SEGMENT.start + led_offset, color2); + } + } + + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % (SEGMENT_LENGTH * 2); + return SPEED_FORMULA_L; +} + + +/* + * Lights all LEDs one after another. + */ +uint16_t WS2812FX::mode_color_wipe(void) { + return color_wipe(SEGMENT.colors[0], SEGMENT.colors[1], false); +} + +/* + * Lights all LEDs one after another. Turns off opposite + */ +uint16_t WS2812FX::mode_color_sweep(void) { + return color_wipe(SEGMENT.colors[0], SEGMENT.colors[1], 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); +} + + +/* + * Random color introduced 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); +} + + +/* + * 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++) { + setPixelColor(i, color); + } + return 50 + (20 * (uint32_t)(255 - SEGMENT.speed)); +} + + +/* + * Lights every LED in a random color. Changes all LED at the same time + * to new random colors. + */ +uint16_t WS2812FX::mode_dynamic(void) { + if(SEGMENT.intensity > 127 || SEGMENT_RUNTIME.counter_mode_call == 0) { + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + setPixelColor(i, color_wheel(random8())); + } + } + setPixelColor(SEGMENT.start + random(SEGMENT_LENGTH), color_wheel(random8())); + return 50 + (15 * (uint32_t)(255 - 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) { + int lum = SEGMENT_RUNTIME.counter_mode_step; + if(lum > 255) lum = 511 - lum; // lum = 15 -> 255 -> 15 + + uint16_t delay; + if(lum == 15) delay = 970; // 970 pause before each breath + else if(lum <= 25) delay = 38; // 19 + else if(lum <= 50) delay = 36; // 18 + else if(lum <= 75) delay = 28; // 14 + else if(lum <= 100) delay = 20; // 10 + else if(lum <= 125) delay = 14; // 7 + else if(lum <= 150) delay = 11; // 5 + else delay = 10; // 4 + + uint32_t color = SEGMENT.colors[0]; + uint8_t w = (color >> 24 & 0xFF) * lum / 256; + uint8_t r = (color >> 16 & 0xFF) * lum / 256; + uint8_t g = (color >> 8 & 0xFF) * lum / 256; + uint8_t b = (color & 0xFF) * lum / 256; + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + setPixelColor(i, r, g, b, w); + } + + SEGMENT_RUNTIME.counter_mode_step += 2; + if(SEGMENT_RUNTIME.counter_mode_step > (512-15)) SEGMENT_RUNTIME.counter_mode_step = 15; + return delay; +} + + +/* + * Fades the LEDs between two colors + */ +uint16_t WS2812FX::mode_fade(void) { + int lum = SEGMENT_RUNTIME.counter_mode_step; + if(lum > 255) lum = 511 - lum; // lum = 0 -> 255 -> 0 + + uint32_t color = color_blend(SEGMENT.colors[0], SEGMENT.colors[1], lum); + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + setPixelColor(i, color); + } + + SEGMENT_RUNTIME.counter_mode_step += 4; + if(SEGMENT_RUNTIME.counter_mode_step > 511) SEGMENT_RUNTIME.counter_mode_step = 0; + return 5 + ((15 * (uint32_t)(255 - SEGMENT.speed)) / 255); +} + + +//TODO add intensity (more than 1 pixel lit) +/* + * 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++) { + setPixelColor(i, SEGMENT.colors[1]); + } + + int led_offset = SEGMENT_RUNTIME.counter_mode_step - (SEGMENT_LENGTH - 1); + led_offset = abs(led_offset); + setPixelColor(SEGMENT.start + led_offset, SEGMENT.colors[0]); + + SEGMENT_RUNTIME.counter_mode_step++; + return SPEED_FORMULA_L; +} + + +/* + * 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++) { + setPixelColor(i, BLACK); + } + + int led_offset = SEGMENT_RUNTIME.counter_mode_step - (SEGMENT_LENGTH - 1); + led_offset = abs(led_offset); + + setPixelColor(SEGMENT.start + led_offset, SEGMENT.colors[0]); + setPixelColor(SEGMENT.start + SEGMENT_LENGTH - led_offset - 1, SEGMENT.colors[0]); + + SEGMENT_RUNTIME.counter_mode_step++; + return SPEED_FORMULA_L; +} + + +/* + * 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++) { + setPixelColor(i, color); + } + + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) & 0xFF; + return 1 + (((uint32_t)(255 - SEGMENT.speed)) / 5); +} + + +/* + * 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 / ((uint16_t)(SEGMENT_LENGTH*(float)(SEGMENT.intensity/128.0))+1)) + SEGMENT_RUNTIME.counter_mode_step) & 0xFF); + setPixelColor(SEGMENT.start + i, color); + } + + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) & 0xFF; + return 1 + (((uint32_t)(255 - SEGMENT.speed)) / 5); +} + + +/* + * 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) { + setPixelColor(SEGMENT.start + i, color1); + } else { + setPixelColor(SEGMENT.start + i, color2); + } + } + return 50 + (2 * (uint32_t)(255 - SEGMENT.speed)); +} + + +/* + * Theatre-style crawling lights. + * Inspired by the Adafruit examples. + */ +uint16_t WS2812FX::mode_theater_chase(void) { + return theater_chase(SEGMENT.colors[0], SEGMENT.colors[1]); +} + + +/* + * 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), SEGMENT.colors[1]); +} + + +/* + * 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); + + for(uint16_t i=0; i < SEGMENT_LENGTH; i++) { + int s = (sin(i+SEGMENT_RUNTIME.counter_mode_call) * 127) + 128; + setPixelColor(SEGMENT.start + i, (((uint32_t)(r * s)) / 255), (((uint32_t)(g * s)) / 255), (((uint32_t)(b * s)) / 255), (((uint32_t)(w * s)) / 255)); + } + + return 10 + (uint16_t)(255 - SEGMENT.speed); +} + + +/* + * 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++) { + setPixelColor(i, SEGMENT.colors[1]); + } + 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); + } + + setPixelColor(SEGMENT.start + random(SEGMENT_LENGTH), color); + + SEGMENT_RUNTIME.counter_mode_step--; + return 50 + (8 * (uint16_t)(255 - SEGMENT.speed)); +} + +/* + * 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(random8())); +} + + +/* + * fade out function + * fades out the current segment by dividing each pixel's intensity by 2 + */ +void WS2812FX::fade_out(uint8_t rate) { + static const float rateMap[] = {1.1, 1.20, 1.5, 2.0, 4.0, 8.0, 16.0, 64.0}; + if (rate > 7) rate = 7; + float mappedRate = rateMap[rate]; + + uint32_t color = SEGMENT.colors[1]; // target color + int w2 = (color >> 24) & 0xff; + int r2 = (color >> 16) & 0xff; + int g2 = (color >> 8) & 0xff; + int b2 = color & 0xff; + + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + color = getPixelColor(i); + if(rate == 0) { // old fade-to-black algorithm + setPixelColor(i, (color >> 1) & 0x7F7F7F7F); + } else { // new fade-to-color algorithm + int w1 = (color >> 24) & 0xff; + int r1 = (color >> 16) & 0xff; + int g1 = (color >> 8) & 0xff; + int b1 = color & 0xff; + + int wdelta = (w2 - w1) / mappedRate; + int rdelta = (r2 - r1) / mappedRate; + int gdelta = (g2 - g1) / mappedRate; + int bdelta = (b2 - b1) / mappedRate; + + // if fade isn't complete, make sure delta is at least 1 (fixes rounding issues) + wdelta += (w2 == w1) ? 0 : (w2 > w1) ? 1 : -1; + rdelta += (r2 == r1) ? 0 : (r2 > r1) ? 1 : -1; + gdelta += (g2 == g1) ? 0 : (g2 > g1) ? 1 : -1; + bdelta += (b2 == b1) ? 0 : (b2 > b1) ? 1 : -1; + + setPixelColor(i, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta); + } + } +} + + +/* + * twinkle_fade function + */ +uint16_t WS2812FX::twinkle_fade(uint32_t color) { + fade_out((255-SEGMENT.intensity) / 32); + + if(random8(3) == 0) { + setPixelColor(SEGMENT.start + random(SEGMENT_LENGTH), color); + } + return 100 + ((uint32_t)(255 - SEGMENT.speed)) / 3; +} + + +/* + * 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(random8())); +} + + +/* + * Blinks one LED at a time. + * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ + */ +uint16_t WS2812FX::mode_sparkle(void) { + setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.aux_param, SEGMENT.colors[1]); + SEGMENT_RUNTIME.aux_param = random(SEGMENT_LENGTH); // aux_param stores the random led index + setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.aux_param, SEGMENT.colors[0]); + return 10 + (uint16_t)(255 - SEGMENT.speed); +} + + +/* + * 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++) { + setPixelColor(i, SEGMENT.colors[0]); + } + } + + setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.aux_param, SEGMENT.colors[0]); + + if(random8(5) == 0) { + SEGMENT_RUNTIME.aux_param = random(SEGMENT_LENGTH); // aux_param stores the random led index + setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.aux_param, SEGMENT.colors[1]); + return 20; + } + return 20 + (uint16_t)(255-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++) { + setPixelColor(i, SEGMENT.colors[0]); + } + + if(random8(5) < 2) { + for(uint16_t i=0; i < max(1, SEGMENT_LENGTH/3); i++) { + setPixelColor(SEGMENT.start + random(SEGMENT_LENGTH), SEGMENT.colors[1]); + } + return 20; + } + return 20 + (uint16_t)(255-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++) { + setPixelColor(i, SEGMENT.colors[1]); + } + + uint16_t delay = 50 + 20*(uint16_t)(255-SEGMENT.speed); + uint16_t count = 2 * ((SEGMENT.speed / 10) + 1); + if(SEGMENT_RUNTIME.counter_mode_step < count) { + if((SEGMENT_RUNTIME.counter_mode_step & 1) == 0) { + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + setPixelColor(i, SEGMENT.colors[0]); + } + delay = 20; + } else { + delay = 50; + } + } + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % (count + 1); + return delay; +} + +/* + * Android loading circle + */ +uint16_t WS2812FX::mode_android(void) { + if (SEGMENT_RUNTIME.counter_mode_call == 0) { - bus->SetPixelColor(i, RgbwColor(r,g,b,w)); + SEGMENT_RUNTIME.aux_param = 0; + SEGMENT_RUNTIME.counter_mode_step = SEGMENT.start; + } + + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + setPixelColor(i, SEGMENT.colors[1]); + } + + if (SEGMENT_RUNTIME.aux_param2 > ((float)SEGMENT.intensity/255.0)*(float)SEGMENT_LENGTH) + { + SEGMENT_RUNTIME.aux_param = 1; } else { - bus->SetPixelColor(i, RgbColor(r,g,b)); + if (SEGMENT_RUNTIME.aux_param2 < 2) SEGMENT_RUNTIME.aux_param = 0; } + + uint16_t a = SEGMENT_RUNTIME.counter_mode_step; + + if (SEGMENT_RUNTIME.aux_param == 0) + { + if (SEGMENT_RUNTIME.counter_mode_call %3 == 1) {a++;} + else {SEGMENT_RUNTIME.aux_param2++;} + } else + { + a++; + if (SEGMENT_RUNTIME.counter_mode_call %3 != 1) SEGMENT_RUNTIME.aux_param2--; + } + + if (a > SEGMENT.stop) a = SEGMENT.start; + + if (a + SEGMENT_RUNTIME.aux_param2 <= SEGMENT.stop) + { + for(int i = a; i < a+SEGMENT_RUNTIME.aux_param2; i++) { + setPixelColor(i, SEGMENT.colors[0]); + } + } else + { + for(int i = a; i <= SEGMENT.stop; i++) { + setPixelColor(i, SEGMENT.colors[0]); + } + for(int i = SEGMENT.start; i < SEGMENT_RUNTIME.aux_param2 - (SEGMENT.stop +1 -a); i++) { + setPixelColor(i, SEGMENT.colors[0]); + } + } + SEGMENT_RUNTIME.counter_mode_step = a; + + return 3 + ((8 * (uint32_t)(255 - SEGMENT.speed)) / SEGMENT_LENGTH); } -void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) -{ - if (_reverseMode) i = _led_count - 1 -i; - if (!_cronixieMode) +/* + * 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; + setPixelColor(SEGMENT.start + a, color1); + setPixelColor(SEGMENT.start + b, color2); + setPixelColor(SEGMENT.start + c, color3); + + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % SEGMENT_LENGTH; + return SPEED_FORMULA_L; +} + +/* + * Tricolor chase mode + */ +uint16_t WS2812FX::mode_tricolor_chase(void) { + return chase(SEGMENT.colors[0], SEGMENT.colors[1], SEGMENT.colors[2]); +} + +/* + * Bicolor chase, more primary color. + */ +uint16_t WS2812FX::mode_chase_color(void) { + return chase(SEGMENT.colors[1], SEGMENT.colors[0], SEGMENT.colors[0]); +} + +/* + * Primary 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), SEGMENT.colors[0], SEGMENT.colors[0]); +} + + +/* + * Primary running on rainbow. + */ +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(SEGMENT.colors[0], color2, color3); +} + + +/* + * Red - Amber - Green - Blue lights running + */ +uint16_t WS2812FX::mode_colorful(void) { + uint32_t cols[]{0x00FF0000,0x00EEBB00,0x0000EE00,0x000077CC,0x00FF0000,0x00EEBB00,0x0000EE00}; + if (SEGMENT.intensity < 127) //pastel (easter) colors { - if (_skipFirstMode) {i++;if(i==1)setPixelColorRaw(0,0,0,0,0);} - if (_rgbwMode) + cols[0] = 0x00FF8040; + cols[1] = 0x00E5D241; + cols[2] = 0x0077FF77; + cols[3] = 0x0077F0F0; + for (uint8_t i = 4; i < 7; i++) cols[i] = cols[i-4]; + } + int i = SEGMENT.start; + for (i; i <= SEGMENT.stop ; i+=4) + { + setPixelColor(i, cols[SEGMENT_RUNTIME.counter_mode_step]); + setPixelColor(i+1, cols[SEGMENT_RUNTIME.counter_mode_step+1]); + setPixelColor(i+2, cols[SEGMENT_RUNTIME.counter_mode_step+2]); + setPixelColor(i+3, cols[SEGMENT_RUNTIME.counter_mode_step+3]); + } + i+=4; + if(i <= SEGMENT.stop) + { + setPixelColor(i, cols[SEGMENT_RUNTIME.counter_mode_step]); + + if(i+1 <= SEGMENT.stop) { - bus->SetPixelColor(i, RgbwColor(r,g,b,w)); - } else - { - bus->SetPixelColor(i, RgbColor(r,g,b)); - } - } else { - if(i>6)return; - byte o = 10*i; - if (_cronixieBacklightEnabled && _cronixieDigits[i] <11) - { - byte rCorr = (int)(((double)((_color_sec>>16) & 0xFF))*_cronixieSecMultiplier); - byte gCorr = (int)(((double)((_color_sec>>8) & 0xFF))*_cronixieSecMultiplier); - byte bCorr = (int)(((double)((_color_sec) & 0xFF))*_cronixieSecMultiplier); - byte wCorr = (int)(((double)((_color_sec>>24) & 0xFF))*_cronixieSecMultiplier); - for (int j=o; j< o+19; j++) + setPixelColor(i+1, cols[SEGMENT_RUNTIME.counter_mode_step+1]); + + if(i+2 <= SEGMENT.stop) { - setPixelColorRaw((_skipFirstMode)?j+1:j,rCorr,gCorr,bCorr,wCorr); - } - } else - { - for (int j=o; j< o+19; j++) - { - setPixelColorRaw((_skipFirstMode)?j+1:j,0,0,0,0); + setPixelColor(i+2, cols[SEGMENT_RUNTIME.counter_mode_step+2]); } } - switch(_cronixieDigits[i]) + } + + if (SEGMENT.speed > 0) SEGMENT_RUNTIME.counter_mode_step++; //static if lowest speed + if (SEGMENT_RUNTIME.counter_mode_step >3) SEGMENT_RUNTIME.counter_mode_step = 0; + return 50 + (15 * (uint32_t)(255 - SEGMENT.speed)); +} + + +/* + * Emulates a traffic light. + */ +uint16_t WS2812FX::mode_traffic_light(void) { + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) setPixelColor(i, SEGMENT.colors[1]); + uint32_t mdelay = 500; + for (int i = SEGMENT.start; i < SEGMENT.stop-1 ; i+=3) + { + switch (SEGMENT_RUNTIME.counter_mode_step) { - case 0: setPixelColorRaw((_skipFirstMode)?o+6:o+5,r,g,b,w); break; - case 1: setPixelColorRaw((_skipFirstMode)?o+1:o+0,r,g,b,w); break; - case 2: setPixelColorRaw((_skipFirstMode)?o+7:o+6,r,g,b,w); break; - case 3: setPixelColorRaw((_skipFirstMode)?o+2:o+1,r,g,b,w); break; - case 4: setPixelColorRaw((_skipFirstMode)?o+8:o+7,r,g,b,w); break; - case 5: setPixelColorRaw((_skipFirstMode)?o+3:o+2,r,g,b,w); break; - case 6: setPixelColorRaw((_skipFirstMode)?o+9:o+8,r,g,b,w); break; - case 7: setPixelColorRaw((_skipFirstMode)?o+4:o+3,r,g,b,w); break; - case 8: setPixelColorRaw((_skipFirstMode)?o+10:o+9,r,g,b,w); break; - case 9: setPixelColorRaw((_skipFirstMode)?o+5:o+4,r,g,b,w); break; - default: break; + case 0: setPixelColor(i, 0x00FF0000); mdelay = 150 + (100 * (uint32_t)(255 - SEGMENT.speed));break; + case 1: setPixelColor(i, 0x00FF0000); mdelay = 150 + (20 * (uint32_t)(255 - SEGMENT.speed)); setPixelColor(i+1, 0x00EECC00); break; + case 2: setPixelColor(i+2, 0x0000FF00); mdelay = 150 + (100 * (uint32_t)(255 - SEGMENT.speed));break; + case 3: setPixelColor(i+1, 0x00EECC00); mdelay = 150 + (20 * (uint32_t)(255 - SEGMENT.speed));break; } } + + SEGMENT_RUNTIME.counter_mode_step++; + if (SEGMENT_RUNTIME.counter_mode_step >3) SEGMENT_RUNTIME.counter_mode_step = 0; + return mdelay; } -void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b) -{ - setPixelColor(i,r,g,b,0); + +/* + * Primary, secondary 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, SEGMENT.colors[0],SEGMENT.colors[1]); } -void WS2812FX::setPixelColor(uint16_t i, uint32_t c) -{ - setPixelColor(i,(c>>16) & 0xFF,(c>>8) & 0xFF,(c) & 0xFF,(c>>24) & 0xFF); -} -uint32_t WS2812FX::getPixelColor(uint16_t i) -{ - if (_cronixieMode) - { - if(i>6)return 0; - byte o = 10*i; - switch(_cronixieDigits[i]) - { - case 0: i=o+5; break; - case 1: i=o+0; break; - case 2: i=o+6; break; - case 3: i=o+1; break; - case 4: i=o+7; break; - case 5: i=o+2; break; - case 6: i=o+8; break; - case 7: i=o+3; break; - case 8: i=o+9; break; - case 9: i=o+4; break; - default: return 0; - } +/* + * Sec flashes running on prim. + */ +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++) { + setPixelColor(i, SEGMENT.colors[0]); } - if (_rgbwMode) - { - RgbwColor lColor = bus->GetPixelColorRgbw(i); - return lColor.W*16777216 + lColor.R*65536 + lColor.G*256 + lColor.B; + + uint16_t delay = 10 + ((30 * (uint16_t)(255 - 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; + setPixelColor(SEGMENT.start + n, SEGMENT.colors[1]); + setPixelColor(SEGMENT.start + m, SEGMENT.colors[1]); + delay = 20; + } else { + delay = 30; + } } else { - RgbColor lColor = bus->GetPixelColor(i); - return lColor.R*65536 + lColor.G*256 + lColor.B; + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % SEGMENT_LENGTH; } + return delay; } -void WS2812FX::setBrightness(byte b) + +/* + * Prim 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++) { + setPixelColor(SEGMENT.start + i, color_wheel(SEGMENT_RUNTIME.aux_param)); + } + + uint16_t delay = 1 + ((10 * (uint16_t)(255 - 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) { + setPixelColor(SEGMENT.start + n, SEGMENT.colors[0]); + setPixelColor(SEGMENT.start + m, SEGMENT.colors[0]); + delay = 20; + } else { + setPixelColor(SEGMENT.start + n, color_wheel(SEGMENT_RUNTIME.aux_param)); + setPixelColor(SEGMENT.start + m, SEGMENT.colors[1]); + 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) { + setPixelColor(SEGMENT.stop - i, color1); + } else { + setPixelColor(SEGMENT.stop - i, color2); + } + } + + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) & 0x3; + return 35 + ((350 * (uint32_t)(255 - SEGMENT.speed)) / 255); +} + +/* + * 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--) { + setPixelColor(SEGMENT.start + i, getPixelColor(SEGMENT.start + i - 1)); + } + + if(SEGMENT_RUNTIME.counter_mode_step == 0) { + SEGMENT_RUNTIME.aux_param = get_random_wheel_index(SEGMENT_RUNTIME.aux_param); + setPixelColor(SEGMENT.start, color_wheel(SEGMENT_RUNTIME.aux_param)); + } + + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step == 0) ? 1 : 0; + return SPEED_FORMULA_L; +} + + +/* + * K.I.T.T. + */ +uint16_t WS2812FX::mode_larson_scanner(void) { + fade_out((255-SEGMENT.intensity) / 32); + + if(SEGMENT_RUNTIME.counter_mode_step < SEGMENT_LENGTH) { + setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + } else { + 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 SPEED_FORMULA_L; +} + + +/* + * Firing comets from one end. + */ +uint16_t WS2812FX::mode_comet(void) { + fade_out((255-SEGMENT.intensity) / 32); + + if(IS_REVERSE) { + setPixelColor(SEGMENT.stop - SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + } else { + 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 SPEED_FORMULA_L; +} + + +/* + * Fireworks function. + */ +uint16_t WS2812FX::fireworks(uint32_t color) { + uint32_t prevLed, thisLed, nextLed; + + fade_out((255-SEGMENT.intensity) / 32); + + // set brightness(i) = ((brightness(i-1)/4 + brightness(i+1))/4) + brightness(i) + for(uint16_t i=SEGMENT.start + 1; i > 2) & 0x3F3F3F3F; + thisLed = getPixelColor(i); + nextLed = (getPixelColor(i+1) >> 2) & 0x3F3F3F3F; + setPixelColor(i, prevLed + thisLed + nextLed); + } + + 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)))/(((256-SEGMENT.intensity)/16)+1); + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + int flicker = random8(lum); + setPixelColor(i, max(r - flicker, 0), max(g - flicker, 0), max(b - flicker, 0), max(w - flicker, 0)); + } + return 10 + (2 * (uint16_t)(255 - SEGMENT.speed)); +} + + +/* + * Gradient run + */ +uint16_t WS2812FX::mode_gradient(void) { + if (SEGMENT_RUNTIME.counter_mode_call == 0) SEGMENT_RUNTIME.counter_mode_step = 0; + byte p_w = (SEGMENT.colors[0] & 0xFF000000) >> 24; + byte p_r = (SEGMENT.colors[0] & 0x00FF0000) >> 16; + byte p_g = (SEGMENT.colors[0] & 0x0000FF00) >> 8; + byte p_b = (SEGMENT.colors[0] & 0x000000FF) >> 0; + byte p_w2 = (SEGMENT.colors[1] & 0xFF000000) >> 24; + byte p_r2 = (SEGMENT.colors[1] & 0x00FF0000) >> 16; + byte p_g2 = (SEGMENT.colors[1] & 0x0000FF00) >> 8; + byte p_b2 = (SEGMENT.colors[1] & 0x000000FF) >> 0; + byte nw,nr,ng,nb; + float per,val; //0.0 = sec 1.0 = pri + float brd = SEGMENT.intensity/2; if (brd <1.0) brd = 1.0; + int pp = SEGMENT_RUNTIME.counter_mode_step; + int p1 = pp-SEGMENT_LENGTH; + int p2 = pp+SEGMENT_LENGTH; + + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) + { + val = min(abs(pp-i),min(abs(p1-i),abs(p2-i))); + per = val/brd; + if (per >1.0) per = 1.0; + nw = p_w+((p_w2 - p_w)*per); + nr = p_r+((p_r2 - p_r)*per); + ng = p_g+((p_g2 - p_g)*per); + nb = p_b+((p_b2 - p_b)*per); + setPixelColor(i,nr,ng,nb,nw); + } + + SEGMENT_RUNTIME.counter_mode_step++; + if (SEGMENT_RUNTIME.counter_mode_step > SEGMENT.stop) SEGMENT_RUNTIME.counter_mode_step = SEGMENT.start; + if (SEGMENT.speed == 0) SEGMENT_RUNTIME.counter_mode_step = SEGMENT.start + (SEGMENT_LENGTH >> 1); + return SPEED_FORMULA_L; +} + + +/* + * Gradient run with hard transition + */ +uint16_t WS2812FX::mode_loading(void) { + if (SEGMENT_RUNTIME.counter_mode_call == 0) SEGMENT_RUNTIME.counter_mode_step = 0; + byte p_w = (SEGMENT.colors[0] & 0xFF000000) >> 24; + byte p_r = (SEGMENT.colors[0] & 0x00FF0000) >> 16; + byte p_g = (SEGMENT.colors[0] & 0x0000FF00) >> 8; + byte p_b = (SEGMENT.colors[0] & 0x000000FF) >> 0; + byte p_w2 = (SEGMENT.colors[1] & 0xFF000000) >> 24; + byte p_r2 = (SEGMENT.colors[1] & 0x00FF0000) >> 16; + byte p_g2 = (SEGMENT.colors[1] & 0x0000FF00) >> 8; + byte p_b2 = (SEGMENT.colors[1] & 0x000000FF) >> 0; + byte nw,nr,ng,nb; + float per,val; //0.0 = sec 1.0 = pri + float brd = SEGMENT.intensity; if (brd <1.0) brd = 1.0; + int pp = SEGMENT_RUNTIME.counter_mode_step; + int p1 = pp+SEGMENT_LENGTH; + + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) + { + pp = SEGMENT_RUNTIME.counter_mode_step; + if (i > pp) pp+=SEGMENT_LENGTH; + val = abs(pp-i); + per = val/brd; + if (per >1.0) per = 1.0; + nw = p_w+((p_w2 - p_w)*per); + nr = p_r+((p_r2 - p_r)*per); + ng = p_g+((p_g2 - p_g)*per); + nb = p_b+((p_b2 - p_b)*per); + setPixelColor(i,nr,ng,nb,nw); + } + + SEGMENT_RUNTIME.counter_mode_step++; + if (SEGMENT_RUNTIME.counter_mode_step > SEGMENT.stop) SEGMENT_RUNTIME.counter_mode_step = SEGMENT.start; + if (SEGMENT.speed == 0) SEGMENT_RUNTIME.counter_mode_step = SEGMENT.stop; + return SPEED_FORMULA_L; +} + + +/* + * Lights all LEDs after each other up starting from the outer edges and + * finishing in the middle. Then turns them in reverse order off. Repeat. + */ +uint16_t WS2812FX::mode_dual_color_wipe_in_out(void) { + int end = SEGMENT_LENGTH - SEGMENT_RUNTIME.counter_mode_step - 1; + bool odd = (SEGMENT_LENGTH % 2) == 1; + int mid = odd ? ((SEGMENT_LENGTH / 2) + 1) : (SEGMENT_LENGTH / 2); + if (SEGMENT_RUNTIME.counter_mode_step < mid) { + setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + setPixelColor(SEGMENT.start + end, SEGMENT.colors[0]); + } else { + if (odd) { + // If odd, we need to 'double count' the center LED (once to turn it on, + // once to turn it off). So trail one behind after the middle LED. + setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.counter_mode_step - 1, 0); + setPixelColor(SEGMENT.start + end + 1, 0); + } else { + setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.counter_mode_step, 0); + setPixelColor(SEGMENT.start + end, 0); + } + } + SEGMENT_RUNTIME.counter_mode_step++; + if (odd) { + if (SEGMENT_RUNTIME.counter_mode_step > SEGMENT_LENGTH) { + SEGMENT_RUNTIME.counter_mode_step = 0; + } + } else { + if (SEGMENT_RUNTIME.counter_mode_step >= SEGMENT_LENGTH) { + SEGMENT_RUNTIME.counter_mode_step = 0; + } + } + return SPEED_FORMULA_L; +} + + + /* + * Lights all LEDs after each other up starting from the outer edges and + * finishing in the middle. Then turns them in that order off. Repeat. + */ +uint16_t WS2812FX::mode_dual_color_wipe_in_in(void) { + bool odd = (SEGMENT_LENGTH % 2) == 1; + int mid = SEGMENT_LENGTH / 2; + if (odd) { + if (SEGMENT_RUNTIME.counter_mode_step <= mid) { + setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + setPixelColor(SEGMENT.start + SEGMENT_LENGTH - SEGMENT_RUNTIME.counter_mode_step - 1, SEGMENT.colors[0]); + } else { + int i = SEGMENT_RUNTIME.counter_mode_step - mid; + setPixelColor(SEGMENT.start + i - 1, 0); + setPixelColor(SEGMENT.start + SEGMENT_LENGTH - i, 0); + } + } else { + if (SEGMENT_RUNTIME.counter_mode_step < mid) { + setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + setPixelColor(SEGMENT.start + SEGMENT_LENGTH - SEGMENT_RUNTIME.counter_mode_step - 1, SEGMENT.colors[0]); + } else { + int i = SEGMENT_RUNTIME.counter_mode_step - mid; + setPixelColor(SEGMENT.start + i, 0); + setPixelColor(SEGMENT.start + SEGMENT_LENGTH - i - 1, 0); + } + } + SEGMENT_RUNTIME.counter_mode_step++; + if (odd) { + if (SEGMENT_RUNTIME.counter_mode_step > SEGMENT_LENGTH) { + SEGMENT_RUNTIME.counter_mode_step = 0; + } + } else { + if (SEGMENT_RUNTIME.counter_mode_step >= SEGMENT_LENGTH) { + SEGMENT_RUNTIME.counter_mode_step = 0; + } + } + return SPEED_FORMULA_L; +} + + + /* + * Lights all LEDs after each other up starting from the middle and + * finishing at the edges. Then turns them off in that order. Repeat. + */ +uint16_t WS2812FX::mode_dual_color_wipe_out_out(void) { + int end = SEGMENT_LENGTH - SEGMENT_RUNTIME.counter_mode_step - 1; + bool odd = (SEGMENT_LENGTH % 2) == 1; + int mid = SEGMENT_LENGTH / 2; + if (odd) { + if (SEGMENT_RUNTIME.counter_mode_step <= mid) { + setPixelColor(SEGMENT.start + mid + SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + setPixelColor(SEGMENT.start + mid - SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + } else { + setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.counter_mode_step - 1, 0); + setPixelColor(SEGMENT.start + end + 1, 0); + } + } else { + if (SEGMENT_RUNTIME.counter_mode_step < mid) { + setPixelColor(SEGMENT.start + mid - SEGMENT_RUNTIME.counter_mode_step - 1, SEGMENT.colors[0]); + setPixelColor(SEGMENT.start + mid + SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + } else { + setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.counter_mode_step, 0); + setPixelColor(SEGMENT.start + end, 0); + } + } + SEGMENT_RUNTIME.counter_mode_step++; + if (odd) { + if (SEGMENT_RUNTIME.counter_mode_step > SEGMENT_LENGTH) { + SEGMENT_RUNTIME.counter_mode_step = 0; + } + } else { + if (SEGMENT_RUNTIME.counter_mode_step >= SEGMENT_LENGTH) { + SEGMENT_RUNTIME.counter_mode_step = 0; + } + } + return SPEED_FORMULA_L; +} + + + /* + * Lights all LEDs after each other up starting from the middle and + * finishing at the edges. Then turns them off in reverse order. Repeat. + */ +uint16_t WS2812FX::mode_dual_color_wipe_out_in(void) { + bool odd = (SEGMENT_LENGTH % 2) == 1; + int mid = SEGMENT_LENGTH / 2; + if (odd) { + if (SEGMENT_RUNTIME.counter_mode_step <= mid) { + setPixelColor(SEGMENT.start + mid + SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + setPixelColor(SEGMENT.start + mid - SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + } else { + int i = SEGMENT_RUNTIME.counter_mode_step - mid; + setPixelColor(SEGMENT.start + i - 1, 0); + setPixelColor(SEGMENT.start + SEGMENT_LENGTH - i, 0); + } + } else { + if (SEGMENT_RUNTIME.counter_mode_step < mid) { + setPixelColor(SEGMENT.start + mid - SEGMENT_RUNTIME.counter_mode_step - 1, SEGMENT.colors[0]); + setPixelColor(SEGMENT.start + mid + SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + } else { + int i = SEGMENT_RUNTIME.counter_mode_step - mid; + setPixelColor(SEGMENT.start + i, 0); + setPixelColor(SEGMENT.start + SEGMENT_LENGTH - i - 1, 0); + } + } + SEGMENT_RUNTIME.counter_mode_step++; + if (odd) { + if (SEGMENT_RUNTIME.counter_mode_step > SEGMENT_LENGTH) { + SEGMENT_RUNTIME.counter_mode_step = 0; + } + } else { + if (SEGMENT_RUNTIME.counter_mode_step >= SEGMENT_LENGTH) { + SEGMENT_RUNTIME.counter_mode_step = 0; + } + } + return SPEED_FORMULA_L; +} + + +/* + * Alternating white/red/black pixels running. + */ +uint16_t WS2812FX::mode_circus_combustus(void) { + return chase(RED, WHITE, BLACK); +} + + +/* + * ICU mode + */ +uint16_t WS2812FX::mode_icu(void) { + uint16_t dest = SEGMENT_RUNTIME.counter_mode_step & 0xFFFF; + + setPixelColor(SEGMENT.start + dest, SEGMENT.colors[0]); + setPixelColor(SEGMENT.start + dest + SEGMENT_LENGTH/2, SEGMENT.colors[0]); + + if(SEGMENT_RUNTIME.aux_param == dest) { // pause between eye movements + if(random8(6) == 0) { // blink once in a while + setPixelColor(SEGMENT.start + dest, BLACK); + setPixelColor(SEGMENT.start + dest + SEGMENT_LENGTH/2, BLACK); + return 200; + } + SEGMENT_RUNTIME.aux_param = random(SEGMENT_LENGTH/2); + return 1000 + random(2000); + } + + setPixelColor(SEGMENT.start + dest, BLACK); + setPixelColor(SEGMENT.start + dest + SEGMENT_LENGTH/2, BLACK); + + 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--; + } + + setPixelColor(SEGMENT.start + dest, SEGMENT.colors[0]); + setPixelColor(SEGMENT.start + dest + SEGMENT_LENGTH/2, SEGMENT.colors[0]); + + return SPEED_FORMULA_L; +} + + +/* + * Custom mode by Aircoookie. Color Wipe, but with 3 colors + */ +uint16_t WS2812FX::mode_tricolor_wipe(void) { - _brightness = constrain(b, BRIGHTNESS_MIN, BRIGHTNESS_MAX); - bus->SetBrightness(_brightness); - if (_mode_last_call_time + _mode_delay > millis()+50 || b == 0) show(); //only update right away if long time until next refresh + if(SEGMENT_RUNTIME.counter_mode_step < SEGMENT_LENGTH) { + uint32_t led_offset = SEGMENT_RUNTIME.counter_mode_step; + setPixelColor(SEGMENT.start + led_offset, SEGMENT.colors[0]); + } else if (SEGMENT_RUNTIME.counter_mode_step < SEGMENT_LENGTH*2) { + uint32_t led_offset = SEGMENT_RUNTIME.counter_mode_step - SEGMENT_LENGTH; + setPixelColor(SEGMENT.start + led_offset, SEGMENT.colors[1]); + } else + { + uint32_t led_offset = SEGMENT_RUNTIME.counter_mode_step - SEGMENT_LENGTH*2; + setPixelColor(SEGMENT.start + led_offset, SEGMENT.colors[2]); + } + + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % (SEGMENT_LENGTH * 3); + return SPEED_FORMULA_L; } -void WS2812FX::show() + +/* + * Fades between 3 colors + * Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/TriFade.h + * Modified by Aircoookie + */ +uint16_t WS2812FX::mode_tricolor_fade(void) { - bus->Show(); + static uint32_t color1 = 0, color2 = 0; + + if(SEGMENT_RUNTIME.counter_mode_step == 0) { + color1 = SEGMENT.colors[0]; + color2 = SEGMENT.colors[1]; + } else if(SEGMENT_RUNTIME.counter_mode_step == 256) { + color1 = SEGMENT.colors[1]; + color2 = SEGMENT.colors[2]; + } else if(SEGMENT_RUNTIME.counter_mode_step == 512) { + color1 = SEGMENT.colors[2]; + color2 = SEGMENT.colors[0]; + } + + uint32_t color = color_blend(color1, color2, SEGMENT_RUNTIME.counter_mode_step % 256); + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + setPixelColor(i, color); + } + + SEGMENT_RUNTIME.counter_mode_step += 4; + if(SEGMENT_RUNTIME.counter_mode_step >= 768) SEGMENT_RUNTIME.counter_mode_step = 0; + + return 5 + ((uint32_t)(255 - SEGMENT.speed) / 10); } -void WS2812FX::clear() + +/* + * Creates random comets + * Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/MultiComet.h + */ +uint16_t WS2812FX::mode_multi_comet(void) { - bus->ClearTo(RgbColor(0)); + fade_out((255-SEGMENT.intensity) / 32); + + static uint16_t comets[] = {UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX}; + + for(uint8_t i=0; i < 6; i++) { + if(comets[i] < SEGMENT_LENGTH) { + if (SEGMENT.colors[2] != SEGMENT.colors[1]) + { + setPixelColor(SEGMENT.start + comets[i], i % 2 ? SEGMENT.colors[0] : SEGMENT.colors[2]); + } else + { + setPixelColor(SEGMENT.start + comets[i], SEGMENT.colors[0]); + } + comets[i]++; + } else { + if(!random(SEGMENT_LENGTH)) { + comets[i] = 0; + } + } + } + return SPEED_FORMULA_L; } -void WS2812FX::begin(bool supportWhite, uint16_t countPixels, uint8_t pin, bool skipFirst) + +/* + * Creates two Larson scanners moving in opposite directions + * Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/DualLarson.h + */ +uint16_t WS2812FX::mode_dual_larson_scanner(void){ + if (SEGMENT_RUNTIME.aux_param) + { + SEGMENT_RUNTIME.counter_mode_step--; + } else + { + SEGMENT_RUNTIME.counter_mode_step++; + } + + fade_out((255-SEGMENT.intensity) / 32); + + setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + if (SEGMENT.colors[2] != SEGMENT.colors[1]) + { + setPixelColor(SEGMENT.stop - SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[2]); + } else + { + setPixelColor(SEGMENT.stop - SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + } + + if(SEGMENT_RUNTIME.counter_mode_step >= (SEGMENT.stop - SEGMENT.start) || SEGMENT_RUNTIME.counter_mode_step <= 0) + SEGMENT_RUNTIME.aux_param = !SEGMENT_RUNTIME.aux_param; + + return SPEED_FORMULA_L; +} + + +/* + * Running random pixels + * Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/RandomChase.h + */ +uint16_t WS2812FX::mode_random_chase(void) { - if (supportWhite == _rgbwMode && countPixels == _led_count && _locked != NULL) return; - _rgbwMode = supportWhite; - _skipFirstMode = skipFirst; - _led_count = countPixels; - _cc_i2 = _led_count -1; - if (_skipFirstMode) _led_count++; - uint8_t ty = 1; - if (supportWhite) ty =2; - bus->Begin((NeoPixelType)ty, _led_count, pin); - if (_locked != NULL) delete _locked; - _locked = new bool[_led_count]; + for(uint16_t i=SEGMENT.stop; i>SEGMENT.start; i--) { + setPixelColor(i, getPixelColor(i-1)); + } + uint32_t color = getPixelColor(SEGMENT.start + 1); + int r = random(6) != 0 ? (color >> 16 & 0xFF) : random(256); + int g = random(6) != 0 ? (color >> 8 & 0xFF) : random(256); + int b = random(6) != 0 ? (color & 0xFF) : random(256); + setPixelColor(SEGMENT.start, r, g, b); + + return 15 + (15 * (uint32_t)(255 - SEGMENT.speed)); } -//For some reason min and max are not declared here +typedef struct Oscillator { + int16_t pos; + int8_t size; + int8_t dir; + int8_t speed; +} oscillator; -uint16_t WS2812FX::minval (uint16_t v, uint16_t w) +uint16_t WS2812FX::mode_oscillate(void) { - if (w > v) return v; - return w; + static oscillator oscillators[NUM_COLORS] = { + {SEGMENT_LENGTH/4, SEGMENT_LENGTH/8, 1, 1}, + {SEGMENT_LENGTH/4*2, SEGMENT_LENGTH/8, -1, 1}, + {SEGMENT_LENGTH/4*3, SEGMENT_LENGTH/8, 1, 2} + }; + + for(int8_t i=0; i < sizeof(oscillators)/sizeof(oscillators[0]); i++) { + oscillators[i].pos += oscillators[i].dir * oscillators[i].speed; + if((oscillators[i].dir == -1) && (oscillators[i].pos <= 0)) { + oscillators[i].pos = 0; + oscillators[i].dir = 1; + oscillators[i].speed = random(1, 3); + } + if((oscillators[i].dir == 1) && (oscillators[i].pos >= (SEGMENT_LENGTH - 1))) { + oscillators[i].pos = SEGMENT_LENGTH - 1; + oscillators[i].dir = -1; + oscillators[i].speed = random(1, 3); + } + } + + for(int16_t i=0; i < SEGMENT_LENGTH; i++) { + uint32_t color = BLACK; + for(int8_t j=0; j < sizeof(oscillators)/sizeof(oscillators[0]); j++) { + if(i >= oscillators[j].pos - oscillators[j].size && i <= oscillators[j].pos + oscillators[j].size) { + color = (color == BLACK) ? SEGMENT.colors[j] : color_blend(color, SEGMENT.colors[j], 128); + } + } + setPixelColor(SEGMENT.start + i, color); + } + return 15 + (uint32_t)(255 - SEGMENT.speed); } -uint16_t WS2812FX::maxval (uint16_t v, uint16_t w) + +// WLED limitation: Analog Clock overlay will NOT work when Fire2012 is active +// Fire2012 by Mark Kriegsman, July 2012 +// as part of "Five Elements" shown here: http://youtu.be/knWiGsmgycY +//// +// This basic one-dimensional 'fire' simulation works roughly as follows: +// There's a underlying array of 'heat' cells, that model the temperature +// at each point along the line. Every cycle through the simulation, +// four steps are performed: +// 1) All cells cool down a little bit, losing heat to the air +// 2) The heat from each cell drifts 'up' and diffuses a little +// 3) Sometimes randomly new 'sparks' of heat are added at the bottom +// 4) The heat from each cell is rendered as a color into the leds array +// The heat-to-color mapping uses a black-body radiation approximation. +// +// Temperature is in arbitrary units from 0 (cold black) to 255 (white hot). +// +// This simulation scales it self a bit depending on NUM_LEDS; it should look +// "OK" on anywhere from 20 to 100 LEDs without too much tweaking. +// +// I recommend running this simulation at anywhere from 30-100 frames per second, +// meaning an interframe delay of about 10-35 milliseconds. +// +// Looks best on a high-density LED setup (60+ pixels/meter). +// +// +// There are two main parameters you can play with to control the look and +// feel of your fire: COOLING (used in step 1 above), and SPARKING (used +// in step 3 above) (Effect Intensity = Sparking). +// +// COOLING: How much does the air cool as it rises? +// Less cooling = taller flames. More cooling = shorter flames. +// Default 50, suggested range 20-100 +#define COOLING 75 + +uint16_t WS2812FX::mode_fire_2012(void) { - if (w > v) return w; - return v; + // Step 1. Cool down every cell a little + for( int i = SEGMENT.start; i <= SEGMENT.stop; i++) { + _locked[i] = qsub8(_locked[i], random8(0, ((COOLING * 10) / SEGMENT_LENGTH) + 2)); + } + + // Step 2. Heat from each cell drifts 'up' and diffuses a little + for( int k= SEGMENT.stop; k >= SEGMENT.start + 2; k--) { + _locked[k] = (_locked[k - 1] + _locked[k - 2] + _locked[k - 2] ) / 3; + } + + // Step 3. Randomly ignite new 'sparks' of heat near the bottom + if( random8() <= SEGMENT.intensity ) { + int y = SEGMENT.start + random8(7); + if (y <= SEGMENT.stop) _locked[y] = qadd8(_locked[y], random8(160,255) ); + } + + // Step 4. Map from heat cells to LED colors + for( int j = SEGMENT.start; j <= SEGMENT.stop; j++) { + CRGB color = HeatColor(_locked[j]); + setPixelColor(j, color.red, color.green, color.blue); + } + return 10 + (uint16_t)(255 - SEGMENT.speed)/6; } + + +uint16_t WS2812FX::mode_bpm(void) +{ + CRGB fastled_col; + CRGBPalette16 palette = PartyColors_p; + uint8_t beat = beatsin8(SEGMENT.speed, 64, 255); + for ( int i = SEGMENT.start; i <= SEGMENT.stop; i++) { + fastled_col = ColorFromPalette(palette, SEGMENT_RUNTIME.counter_mode_step + (i * 2), beat - SEGMENT_RUNTIME.counter_mode_step + (i * 10)); + setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + } + SEGMENT_RUNTIME.counter_mode_step++; + if (SEGMENT_RUNTIME.counter_mode_step >= 255) SEGMENT_RUNTIME.counter_mode_step = 0; + return 20; +} + + +uint16_t WS2812FX::mode_juggle(void){ + fade_out((255-SEGMENT.intensity) / 32); + CRGB fastled_col; + byte dothue = 0; + for ( byte i = 0; i < 8; i++) { + uint16_t index = SEGMENT.start + beatsin16(i + 7, 0, SEGMENT_LENGTH); + uint32_t color = getPixelColor(index); + fastled_col.red = (color >> 16 & 0xFF); + fastled_col.green = (color >> 8 & 0xFF); + fastled_col.blue = (color & 0xFF); + fastled_col |= CHSV(dothue, 220, 255); + setPixelColor(index, fastled_col.red, fastled_col.green, fastled_col.blue); + dothue += 32; + } + return 10 + (uint16_t)(255 - SEGMENT.speed)/4; +} + + +CRGBPalette16 currentPalette(CRGB::Black); +CRGBPalette16 targetPalette(CloudColors_p); + + +uint16_t WS2812FX::mode_palette(void) +{ + CRGB fastled_col; + + for (uint16_t i = SEGMENT.start; i <= SEGMENT.stop; i++) + { + uint8_t colorIndex = map(i,SEGMENT.start,SEGMENT.stop,0,255) + (SEGMENT_RUNTIME.counter_mode_step >> 8 & 0xFF); + fastled_col = ColorFromPalette( PartyColors_p, colorIndex, 255, LINEARBLEND); + setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + } + SEGMENT_RUNTIME.counter_mode_step += SEGMENT.speed; + return 20; +} + + +uint16_t WS2812FX::mode_fillnoise8(void) +{ + if (SEGMENT_RUNTIME.counter_mode_call == 0) SEGMENT_RUNTIME.counter_mode_step = random(12345); + CRGB fastled_col; + nblendPaletteTowardPalette(currentPalette, targetPalette, 42); + for (int i = SEGMENT.start; i <= SEGMENT.stop; i++) { + uint8_t index = inoise8(i * SEGMENT_LENGTH, SEGMENT_RUNTIME.counter_mode_step + i * SEGMENT_LENGTH) % 255; + fastled_col = ColorFromPalette(currentPalette, index, 255, LINEARBLEND); + setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + } + SEGMENT_RUNTIME.counter_mode_step += beatsin8(10, 1, 4); + + if (SEGMENT_RUNTIME.counter_mode_call >= 20 + (255 - SEGMENT.intensity) *4) //swap to new random palette + { + targetPalette = CRGBPalette16( + CHSV(random8(), 255, random8(128, 255)), + CHSV(random8(), 255, random8(128, 255)), + CHSV(random8(), 192, random8(128, 255)), + CHSV(random8(), 255, random8(128, 255))); + SEGMENT_RUNTIME.counter_mode_call = 1; + } + + return 15 + (uint16_t)(255 - SEGMENT.speed); +} + + +uint16_t WS2812FX::mode_noise16_1(void) +{ + uint16_t scale = 100 + SEGMENT.intensity*7; // the "zoom factor" for the noise + CRGBPalette16 palette = OceanColors_p; + CRGB fastled_col; + SEGMENT_RUNTIME.counter_mode_step += (1 + SEGMENT.speed/16); + + for (int i = SEGMENT.start; i <= SEGMENT.stop; i++) { + + uint16_t shift_x = beatsin8(5); // the x position of the noise field swings @ 17 bpm + uint16_t shift_y = SEGMENT_RUNTIME.counter_mode_step/50; // the y position becomes slowly incremented + + + uint16_t real_x = (i + shift_x) * scale; // the x position of the noise field swings @ 17 bpm + uint16_t real_y = (i + shift_y) * scale; // the y position becomes slowly incremented + uint32_t real_z = SEGMENT_RUNTIME.counter_mode_step*2; // the z position becomes quickly incremented + + uint8_t noise = inoise16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down + + uint8_t index = sin8(noise * 3); // map LED color based on noise data + + fastled_col = ColorFromPalette(palette, index, noise, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. + setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + } + + return 20; +} + + +uint16_t WS2812FX::mode_noise16_2(void) +{ + uint8_t scale = 100 + SEGMENT.intensity*7; // the "zoom factor" for the noise + CRGBPalette16 palette = LavaColors_p; + CRGB fastled_col; + SEGMENT_RUNTIME.counter_mode_step += (1 + SEGMENT.speed); + + for (int i = SEGMENT.start; i <= SEGMENT.stop; i++) { + + uint16_t shift_x = SEGMENT_RUNTIME.counter_mode_step/64; // x as a function of time + uint16_t shift_y = 0; + + uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field + uint32_t real_y = (i + shift_y) * scale; // based on the precalculated positions + uint32_t real_z = 4223; + + uint8_t noise = inoise16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down + + uint8_t index = sin8(noise * 3); // map led color based on noise data + + fastled_col = ColorFromPalette(palette, index, noise, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. + setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + } + + return 20; +} + + +uint16_t WS2812FX::mode_noise16_3(void) +{ + uint8_t scale = 100 + SEGMENT.intensity*7; // the "zoom factor" for the noise + CRGBPalette16 palette = CloudColors_p; + CRGB fastled_col; + SEGMENT_RUNTIME.counter_mode_step += (1 + SEGMENT.speed); + + for (int i = SEGMENT.start; i <= SEGMENT.stop; i++) { + + uint16_t shift_x = 4223; // no movement along x and y + uint16_t shift_y = 1234; + + uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field + uint32_t real_y = (i + shift_y) * scale; // based on the precalculated positions + uint32_t real_z = SEGMENT_RUNTIME.counter_mode_step/16; + + uint8_t noise = inoise16(real_x, real_y, real_z) >> 7; // get the noise data and scale it down + + uint8_t index = sin8(noise * 3); // map led color based on noise data + + fastled_col = ColorFromPalette(palette, index, noise, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. + setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + } + + return 20; +} + + +//https://github.com/aykevl/ledstrip-spark/blob/master/ledstrip.ino +uint16_t WS2812FX::mode_noise16_4(void) +{ + CRGBPalette16 palette = OceanColors_p; + CRGB fastled_col; + SEGMENT_RUNTIME.counter_mode_step += SEGMENT.speed; + for (int i = SEGMENT.start; i <= SEGMENT.stop; i++) { + int16_t index = inoise16(uint32_t(i - SEGMENT.start) << 12, SEGMENT_RUNTIME.counter_mode_step/8); + fastled_col = ColorFromPalette(palette, index); + setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + } + return 20; +} + + +uint16_t WS2812FX::mode_lightning(void) +{ + uint16_t ledstart = SEGMENT.start + random8(SEGMENT_LENGTH); // Determine starting location of flash + uint16_t ledlen = random8(SEGMENT.stop - ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1) + uint8_t bri = 255/random8(1, 3); + + if (SEGMENT_RUNTIME.counter_mode_step == 0) + { + SEGMENT_RUNTIME.aux_param = random8(3, 3 + SEGMENT.intensity/20); //number of flashes + bri = 52; + SEGMENT_RUNTIME.aux_param2 = 1; + } + + for (int i = SEGMENT.start; i <= SEGMENT.stop; i++) + { + setPixelColor(i,SEGMENT.colors[1]); + } + + if (SEGMENT_RUNTIME.aux_param2) { + for (int i = ledstart; i < ledstart + ledlen; i++) + { + setPixelColor(i,bri,bri,bri,bri); + } + SEGMENT_RUNTIME.aux_param2 = 0; + SEGMENT_RUNTIME.counter_mode_step++; + return random8(4, 10); // each flash only lasts 4-10 milliseconds + } + + SEGMENT_RUNTIME.aux_param2 = 1; + if (SEGMENT_RUNTIME.counter_mode_step == 1) return (200); // longer delay until next flash after the leader + + if (SEGMENT_RUNTIME.counter_mode_step <= SEGMENT_RUNTIME.aux_param) return (50 + random8(100)); // shorter delay between strokes + + SEGMENT_RUNTIME.counter_mode_step = 0; + return (random8(255 - SEGMENT.speed) * 100); // delay between strikes +} + + diff --git a/wled00/WS2812FX.h b/wled00/WS2812FX.h index 2ea09e4f8..f1537938e 100644 --- a/wled00/WS2812FX.h +++ b/wled00/WS2812FX.h @@ -2,18 +2,17 @@ /* 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 + * WS2812FX can be used as drop-in replacement for Adafruit NeoPixel Library NOTES - * Uses the Adafruit Neopixel library. Get it here: + * Uses the Adafruit NeoPixel library. Get it here: https://github.com/adafruit/Adafruit_NeoPixel LICENSE The MIT License (MIT) - Copyright (c) 2016 Harm Aldick + 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 @@ -29,27 +28,61 @@ 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. - Heavily modified to work with WLED - differs from Github WS2812FX + 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) + Modified for WLED */ #ifndef WS2812FX_h #define WS2812FX_h -#include "Arduino.h" #include "NpbWrapper.h" -#define DEFAULT_BRIGHTNESS 50 -#define DEFAULT_MODE 0 -#define DEFAULT_SPEED 150 -#define DEFAULT_COLOR 0xFFAA00 +#define DEFAULT_BRIGHTNESS (uint8_t)50 +#define DEFAULT_MODE (uint8_t)0 +#define DEFAULT_SPEED (uint16_t)1000 +#define DEFAULT_COLOR (uint32_t)0xFF0000 -#define SPEED_MIN 0 -#define SPEED_MAX 255 +#define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)>(b)?(a):(b)) -#define BRIGHTNESS_MIN 0 -#define BRIGHTNESS_MAX 255 +/* each segment uses 38 bytes of SRAM memory, so if you're application fails because of + insufficient memory, decreasing MAX_NUM_SEGMENTS may help */ +#define MAX_NUM_SEGMENTS 12 +#define NUM_COLORS 3 /* 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 SPEED_FORMULA_L 5 + (50*(255 - SEGMENT.speed))/SEGMENT_LENGTH +#define RESET_RUNTIME memset(_segment_runtimes, 0, sizeof(_segment_runtimes)) -#define MODE_COUNT 58 +// some common colors +#define RED (uint32_t)0xFF0000 +#define GREEN (uint32_t)0x00FF00 +#define BLUE (uint32_t)0x0000FF +#define WHITE (uint32_t)0xFFFFFF +#define BLACK (uint32_t)0x000000 +#define YELLOW (uint32_t)0xFFFF00 +#define CYAN (uint32_t)0x00FFFF +#define MAGENTA (uint32_t)0xFF00FF +#define PURPLE (uint32_t)0x400080 +#define ORANGE (uint32_t)0xFF3000 +#define PINK (uint32_t)0xFF1493 +#define ULTRAWHITE (uint32_t)0xFFFFFFFF + +// options +// bit 8: reverse animation +// bits 5-7: fade rate (0-7) +// bit 4: gamma correction +// bits 1-3: TBD +#define NO_OPTIONS (uint8_t)0x00 +#define REVERSE (uint8_t)0x80 +#define IS_REVERSE ((SEGMENT.options & REVERSE) == REVERSE) + +#define MODE_COUNT 72 #define FX_MODE_STATIC 0 #define FX_MODE_BLINK 1 @@ -57,7 +90,7 @@ #define FX_MODE_COLOR_WIPE 3 #define FX_MODE_COLOR_WIPE_RANDOM 4 #define FX_MODE_RANDOM_COLOR 5 -#define FX_MODE_EASTER 6 +#define FX_MODE_COLOR_SWEEP 6 #define FX_MODE_DYNAMIC 7 #define FX_MODE_RAINBOW 8 #define FX_MODE_RAINBOW_CYCLE 9 @@ -104,193 +137,225 @@ #define FX_MODE_DUAL_COLOR_WIPE_OUT_OUT 50 #define FX_MODE_DUAL_COLOR_WIPE_OUT_IN 51 #define FX_MODE_CIRCUS_COMBUSTUS 52 -#define FX_MODE_CUSTOM_CHASE 53 -#define FX_MODE_CC_ON_RAINBOW 54 -#define FX_MODE_CC_ON_RAINBOW_CYCLE 55 -#define FX_MODE_CC_BLINK 56 -#define FX_MODE_CC_RANDOM 57 - +#define FX_MODE_HALLOWEEN 53 +#define FX_MODE_TRICOLOR_CHASE 54 +#define FX_MODE_TRICOLOR_WIPE 55 +#define FX_MODE_TRICOLOR_FADE 56 +#define FX_MODE_LIGHTNING 57 +#define FX_MODE_ICU 58 +#define FX_MODE_MULTI_COMET 59 +#define FX_MODE_DUAL_LARSON_SCANNER 60 +#define FX_MODE_RANDOM_CHASE 61 +#define FX_MODE_OSCILLATE 62 +#define FX_MODE_FIRE_2012 63 +#define FX_MODE_JUGGLE 64 +#define FX_MODE_PALETTE 65 +#define FX_MODE_BPM 66 +#define FX_MODE_FILLNOISE8 67 +#define FX_MODE_NOISE16_1 68 +#define FX_MODE_NOISE16_2 69 +#define FX_MODE_NOISE16_3 70 +#define FX_MODE_NOISE16_4 71 class WS2812FX { - typedef void (WS2812FX::*mode_ptr)(void); + typedef uint16_t (WS2812FX::*mode_ptr)(void); + + // segment parameters public: - WS2812FX(){ + typedef struct Segment { // 20 bytes + uint16_t start; + uint16_t stop; + uint8_t speed; + uint8_t intensity; + uint8_t mode; + uint8_t options; + uint32_t colors[NUM_COLORS]; + } segment; - _mode[FX_MODE_STATIC] = &WS2812FX::mode_static; - _mode[FX_MODE_BLINK] = &WS2812FX::mode_blink; - _mode[FX_MODE_BREATH] = &WS2812FX::mode_breath; - _mode[FX_MODE_COLOR_WIPE] = &WS2812FX::mode_color_wipe; - _mode[FX_MODE_COLOR_WIPE_RANDOM] = &WS2812FX::mode_color_wipe_random; - _mode[FX_MODE_RANDOM_COLOR] = &WS2812FX::mode_random_color; - _mode[FX_MODE_EASTER] = &WS2812FX::mode_easter; - _mode[FX_MODE_DYNAMIC] = &WS2812FX::mode_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_RUNNING_LIGHTS] = &WS2812FX::mode_running_lights; - _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_ANDROID] = &WS2812FX::mode_android; - _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_COLORFUL] = &WS2812FX::mode_colorful; - _mode[FX_MODE_TRAFFIC_LIGHT] = &WS2812FX::mode_traffic_light; - _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_FIRE_FLICKER] = &WS2812FX::mode_fire_flicker; - _mode[FX_MODE_GRADIENT] = &WS2812FX::mode_gradient; - _mode[FX_MODE_LOADING] = &WS2812FX::mode_loading; + // segment runtime parameters + typedef struct Segment_runtime { // 17 bytes + unsigned long next_time; + uint32_t counter_mode_step; + uint32_t counter_mode_call; + uint16_t aux_param; + uint16_t aux_param2; + bool trans_act; + } segment_runtime; + + WS2812FX() { + _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_RANDOM] = &WS2812FX::mode_color_wipe_random; + _mode[FX_MODE_RANDOM_COLOR] = &WS2812FX::mode_random_color; + _mode[FX_MODE_COLOR_SWEEP] = &WS2812FX::mode_color_sweep; + _mode[FX_MODE_DYNAMIC] = &WS2812FX::mode_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_ANDROID] = &WS2812FX::mode_android; + _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_COLORFUL] = &WS2812FX::mode_colorful; + _mode[FX_MODE_TRAFFIC_LIGHT] = &WS2812FX::mode_traffic_light; + _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_FIRE_FLICKER] = &WS2812FX::mode_fire_flicker; + _mode[FX_MODE_GRADIENT] = &WS2812FX::mode_gradient; + _mode[FX_MODE_LOADING] = &WS2812FX::mode_loading; _mode[FX_MODE_DUAL_COLOR_WIPE_IN_OUT] = &WS2812FX::mode_dual_color_wipe_in_out; _mode[FX_MODE_DUAL_COLOR_WIPE_IN_IN] = &WS2812FX::mode_dual_color_wipe_in_in; _mode[FX_MODE_DUAL_COLOR_WIPE_OUT_OUT] = &WS2812FX::mode_dual_color_wipe_out_out; _mode[FX_MODE_DUAL_COLOR_WIPE_OUT_IN] = &WS2812FX::mode_dual_color_wipe_out_in; _mode[FX_MODE_CIRCUS_COMBUSTUS] = &WS2812FX::mode_circus_combustus; - _mode[FX_MODE_CUSTOM_CHASE] = &WS2812FX::mode_cc_standard; - _mode[FX_MODE_CC_ON_RAINBOW] = &WS2812FX::mode_cc_rainbow; - _mode[FX_MODE_CC_ON_RAINBOW_CYCLE] = &WS2812FX::mode_cc_cycle; - _mode[FX_MODE_CC_BLINK] = &WS2812FX::mode_cc_blink; - _mode[FX_MODE_CC_RANDOM] = &WS2812FX::mode_cc_random; + _mode[FX_MODE_HALLOWEEN] = &WS2812FX::mode_halloween; + _mode[FX_MODE_TRICOLOR_CHASE] = &WS2812FX::mode_tricolor_chase; + _mode[FX_MODE_TRICOLOR_WIPE] = &WS2812FX::mode_tricolor_wipe; + _mode[FX_MODE_TRICOLOR_FADE] = &WS2812FX::mode_tricolor_fade; + _mode[FX_MODE_BREATH] = &WS2812FX::mode_breath; + _mode[FX_MODE_RUNNING_LIGHTS] = &WS2812FX::mode_running_lights; + _mode[FX_MODE_LIGHTNING] = &WS2812FX::mode_lightning; + _mode[FX_MODE_ICU] = &WS2812FX::mode_icu; + _mode[FX_MODE_MULTI_COMET] = &WS2812FX::mode_multi_comet; + _mode[FX_MODE_DUAL_LARSON_SCANNER] = &WS2812FX::mode_dual_larson_scanner; + _mode[FX_MODE_RANDOM_CHASE] = &WS2812FX::mode_random_chase; + _mode[FX_MODE_OSCILLATE] = &WS2812FX::mode_oscillate; + _mode[FX_MODE_FIRE_2012] = &WS2812FX::mode_fire_2012; + _mode[FX_MODE_BPM] = &WS2812FX::mode_bpm; + _mode[FX_MODE_JUGGLE] = &WS2812FX::mode_juggle; + _mode[FX_MODE_PALETTE] = &WS2812FX::mode_palette; + _mode[FX_MODE_FILLNOISE8] = &WS2812FX::mode_fillnoise8; + _mode[FX_MODE_NOISE16_1] = &WS2812FX::mode_noise16_1; + _mode[FX_MODE_NOISE16_2] = &WS2812FX::mode_noise16_2; + _mode[FX_MODE_NOISE16_3] = &WS2812FX::mode_noise16_3; + _mode[FX_MODE_NOISE16_4] = &WS2812FX::mode_noise16_4; - _mode_index = DEFAULT_MODE; - _speed = DEFAULT_SPEED; _brightness = DEFAULT_BRIGHTNESS; _running = false; - _led_count = 255; - _mode_last_call_time = 0; - _mode_delay = 0; - _color = DEFAULT_COLOR; - _mode_color = DEFAULT_COLOR; - _color_sec = 0; - _mode_var1 = 0; - _cc_fs = true; - _cc_fe = false; - _cc_is = 0; - _cc_i1 = 0; - _cc_i2 = 254; - _cc_num1 = 5; - _cc_num2 = 5; - _ccStep = 1; - _counter_mode_call = 0; - _counter_mode_step = 0; - _counter_ccStep = 0; - _fastStandard = false; + _num_segments = 1; + _segments[0].mode = DEFAULT_MODE; + _segments[0].colors[0] = DEFAULT_COLOR; + _segments[0].start = 0; + _segments[0].speed = DEFAULT_SPEED; _reverseMode = false; _skipFirstMode = false; _locked = NULL; _cronixieDigits = new byte[6]; bus = new NeoPixelWrapper(); + RESET_RUNTIME; } void - show(void), - setPixelColor(uint16_t i, byte r, byte g, byte b), - setPixelColor(uint16_t i, byte r, byte g, byte b, byte w), - init(bool supportWhite, uint16_t countPixels, uint8_t pin, bool skipFirst), + init(bool supportWhite, uint16_t countPixels, bool skipFirst), service(void), - start(void), - stop(void), - setMode(byte m), - setCustomChase(byte i1, uint16_t i2, byte is, byte np, byte ns, byte stp, bool fs, bool fe), - setCCIndex1(byte i1), - setCCIndex2(uint16_t i2), - setCCStart(byte is), - setCCNum1(byte np), - setCCNum2(byte ns), - setCCStep(byte stp), - setCCFS(bool fs), - setCCFE(bool fe), - setSpeed(byte s), - setIntensity(byte in), - increaseSpeed(byte s), - decreaseSpeed(byte s), - setColor(byte r, byte g, byte b), - setColor(byte r, byte g, byte b, byte w), + clear(void), + strip_off(void), + fade_out(uint8_t r), + setMode(uint8_t m), + setSpeed(uint8_t s), + setIntensity(uint8_t i), + setColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), + setSecondaryColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), setColor(uint32_t c), - setSecondaryColor(byte r, byte g, byte b), - setSecondaryColor(byte r, byte g, byte b, byte w), setSecondaryColor(uint32_t c), - setBrightness(byte b), - increaseBrightness(byte s), - decreaseBrightness(byte s), + setBrightness(uint8_t b), setReverseMode(bool b), driverModeCronixie(bool b), setCronixieDigits(byte* d), setCronixieBacklight(bool b), - setIndividual(int i), - setIndividual(int i, uint32_t col), - setRange(int i, int i2), - setRange(int i, int i2, uint32_t col), - lock(int i), - lockRange(int i, int i2), - lockAll(void), - unlock(int i), - unlockRange(int i, int i2), + setIndividual(uint16_t i, uint32_t col), + setRange(uint16_t i, uint16_t i2, uint32_t col), + lock(uint16_t i), + lockRange(uint16_t i, uint16_t i2), + unlock(uint16_t i), + unlockRange(uint16_t i, uint16_t i2), unlockAll(void), - setFastUpdateMode(bool b), trigger(void), - setFade(int sp); + setNumSegments(uint8_t n), + setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint8_t speed, uint8_t intensity, bool reverse), + setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint8_t speed, uint8_t intensity, bool reverse), + setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint8_t speed, uint8_t intensity, uint8_t options), + resetSegments(), + setPixelColor(uint16_t n, uint32_t c), + setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), + show(void); - bool - isRunning(void), - isLocked(int i); - - byte - get_random_wheel_index(byte), + uint8_t + getBrightness(void), getMode(void), getSpeed(void), - getIntensity(void), - getBrightness(void), - getModeCount(void); + getNumSegments(void), + get_random_wheel_index(uint8_t); uint32_t - color_wheel(byte), + color_wheel(uint8_t), + color_blend(uint32_t,uint32_t,uint8_t), + getPixelColor(uint16_t), getColor(void); double getPowerEstimate(uint16_t leds, uint32_t c, byte b), getSafePowerMultiplier(double safeMilliAmps, uint16_t leds, uint32_t c, byte b); - private: - NeoPixelWrapper *bus; + WS2812FX::Segment + getSegment(void); - void - begin(bool supportWhite, uint16_t countPixels, uint8_t pin, bool skipFirst), - clear(void), - setPixelColor(uint16_t i, uint32_t c), - setPixelColorRaw(uint16_t i, byte r, byte g, byte b, byte w), - dofade(void), - strip_off(void), - strip_off_respectLock(void), + WS2812FX::Segment_runtime + getSegmentRuntime(void); + + WS2812FX::Segment* + getSegments(void); + + // mode helper functions + uint16_t + blink(uint32_t, uint32_t, bool strobe), + color_wipe(uint32_t, uint32_t, bool), + theater_chase(uint32_t, uint32_t), + twinkle(uint32_t), + twinkle_fade(uint32_t), + chase(uint32_t, uint32_t, uint32_t), + running(uint32_t, uint32_t), + fireworks(uint32_t), + tricolor_chase(uint32_t, uint32_t, uint32_t); + + // builtin modes + uint16_t mode_static(void), mode_blink(void), + mode_blink_rainbow(void), + mode_strobe(void), + mode_strobe_rainbow(void), mode_color_wipe(void), + mode_color_sweep(void), mode_color_wipe_random(void), + mode_color_sweep_random(void), mode_random_color(void), - mode_easter(void), mode_dynamic(void), mode_breath(void), mode_fade(void), @@ -308,10 +373,7 @@ class WS2812FX { mode_sparkle(void), mode_flash_sparkle(void), mode_hyper_sparkle(void), - mode_strobe(void), - mode_strobe_rainbow(void), mode_multi_strobe(void), - mode_blink_rainbow(void), mode_android(void), mode_chase_color(void), mode_chase_random(void), @@ -320,9 +382,7 @@ class WS2812FX { mode_chase_flash_random(void), mode_chase_rainbow_white(void), mode_colorful(void), - mode_colorful_internal(uint32_t*), mode_traffic_light(void), - mode_color_sweep_random(void), mode_running_color(void), mode_running_red_blue(void), mode_running_random(void), @@ -331,6 +391,7 @@ class WS2812FX { mode_fireworks(void), mode_fireworks_random(void), mode_merry_christmas(void), + mode_halloween(void), mode_fire_flicker(void), mode_gradient(void), mode_loading(void), @@ -339,67 +400,57 @@ class WS2812FX { mode_dual_color_wipe_out_out(void), mode_dual_color_wipe_out_in(void), mode_circus_combustus(void), - mode_cc_core(void), - mode_cc_standard(void), - mode_cc_rainbow(void), - mode_cc_cycle(void), - mode_cc_blink(void), - mode_cc_random(void); + mode_bicolor_chase(void), + mode_tricolor_chase(void), + mode_tricolor_wipe(void), + mode_tricolor_fade(void), + mode_icu(void), + mode_multi_comet(void), + mode_dual_larson_scanner(void), + mode_random_chase(void), + mode_oscillate(void), + mode_fire_2012(void), + mode_bpm(void), + mode_juggle(void), + mode_palette(void), + mode_fillnoise8(void), + mode_noise16_1(void), + mode_noise16_2(void), + mode_noise16_3(void), + mode_noise16_4(void), + mode_lightning(void); - bool - _triggered, - _rgbwMode, - _skipFirstMode, - _fastStandard, - _reverseMode, - _cronixieMode, - _cronixieBacklightEnabled, - _cc_fs, - _cc_fe, - _running; - - bool* - _locked; - - byte - _mode_index, - _speed, - _intensity, - _cc_i1, - _cc_is, - _cc_num1, - _cc_num2, - _ccStep, - _brightness; - - byte* - _cronixieDigits; - - uint16_t - minval(uint16_t v, uint16_t w), - maxval(uint16_t v, uint16_t w), - _cc_i2, - _led_count; - - uint32_t - getPixelColor(uint16_t i), - _color, - _color_sec, - _counter_mode_call, - _counter_mode_step, - _counter_ccStep, - _mode_var1, - _mode_color, - _mode_delay; + private: + NeoPixelWrapper *bus; + + uint16_t _length; + uint16_t _rand16seed; + uint8_t _brightness; double _cronixieSecMultiplier; - unsigned long - _mode_last_call_time; + boolean + _running, + _rgbwMode, + _reverseMode, + _cronixieMode, + _cronixieBacklightEnabled, + _skipFirstMode, + _triggered; - mode_ptr - _mode[MODE_COUNT]; + byte* _locked; + byte* _cronixieDigits; + + 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 + // start, stop, speed, intensity, mode, options, color[] + { 0, 7, DEFAULT_SPEED, 128, FX_MODE_STATIC, NO_OPTIONS, {DEFAULT_COLOR}} + }; + segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 17 bytes per element }; #endif diff --git a/wled00/data/index.htm b/wled00/data/index.htm index 1903a027e..9d8e935f6 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -2,7 +2,7 @@ - WLED 0.7.1 + WLED 0.8.0-a +WLED 0.8.0-a + )====="; //head1 (css) @@ -101,92 +97,21 @@ const char PAGE_index3[] PROGMEM = R"=====( - - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
Effect Panel



-

Set secondary color to @@ -194,43 +119,27 @@ Set secondary color to -or -
-
Custom Theater Chase
-using primary and - secondary color LEDs,
-doing steps per tick, -from start and end. -
-
-
-
-
-
-
+or
+
FastLED Palette

+
+
+
Favorite Presets




-Click checkmark to apply brightness, color and effects.

-Cycle through presets to , keep each for ms:

-
-
+Click checkmark to apply brightness, color and effects.

+Cycle through presets to , keep each for ms:

+
Timed Light



Gradually dim down
-1st slider sets duration (1-255min), 2nd sets target brightness. -
-
-
-
-
-
- -
- - - +1st slider sets duration (1-255min), 2nd sets target brightness.
+
+
+
+ )====="; diff --git a/wled00/htmls01.h b/wled00/htmls01.h index 035dd9599..3090e6835 100644 --- a/wled00/htmls01.h +++ b/wled00/htmls01.h @@ -110,7 +110,6 @@ Use Gamma correction for color:
Brightness factor: %

Transitions

Fade:
-Sweep: Invert direction:
Transition Time: ms
Enable transition for secondary color:

Timed light

@@ -278,10 +277,9 @@ Current local time is unknown. Clock Overlay:
First LED: Last LED:
@@ -358,7 +356,7 @@ HTTP traffic is unencrypted. An attacker in the same network can intercept form
Enable ArduinoOTA:

About

-WLED version 0.7.1

+WLED version 0.8.0-a

Contributors:
StormPie (Mobile HTML UI)

Thank you so much!

diff --git a/wled00/wled00.ino b/wled00/wled00.ino index c4afae66f..0845b46c4 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -3,7 +3,7 @@ */ /* * @title WLED project sketch - * @version 0.7.1 + * @version 0.8.0-a * @author Christian Schwinne */ @@ -39,8 +39,8 @@ #include "src/dependencies/e131/E131.h" //version in format yymmddb (b = daily build) -#define VERSION 1808111 -char versionString[] = "0.7.1"; +#define VERSION 1809041 +char versionString[] = "0.8.0-a"; //AP and OTA default passwords (change them!) char apPass[65] = "wled1234"; @@ -53,7 +53,7 @@ char otaPass[33] = "wledota"; //#define DEBUG //Hardware-settings (only changeble via code) -#define PIN 2 //strip pin. Only change for ESP32 +//strip pin changeable in NpbWrapper.h. Only change for ESP32 byte buttonPin = 0; //needs pull-up byte auxPin = 15; //use e.g. for external relay byte auxDefaultState = 0; //0: input 1: high 2: low @@ -87,7 +87,6 @@ byte whiteSecS = 0; byte briS = 127; byte nightlightTargetBri = 0; bool fadeTransition = true; -bool sweepTransition = false, sweepDirection = true; bool disableSecTransition = true; uint16_t transitionDelay = 1200, transitionDelayDefault = transitionDelay; bool reverseMode = false; @@ -103,17 +102,10 @@ uint16_t udpPort = 21324, udpRgbPort = 19446; byte effectDefault = 0; byte effectSpeedDefault = 75; byte effectIntensityDefault = 128; +byte effectPaletteDefault = 0; //NTP stuff bool ntpEnabled = false; char ntpServerName[] = "0.wled.pool.ntp.org"; -//custom chase -byte ccNumPrimary = 2; -byte ccNumSecondary = 4; -byte ccIndex1 = 0; -uint16_t ccIndex2 = ledCount -1; -bool ccFromStart = true, ccFromEnd = false; -byte ccStep = 1; -byte ccStart = 0; //alexa bool alexaEnabled = true; @@ -170,6 +162,7 @@ byte briNlT = 0; byte effectCurrent = 0; byte effectSpeed = 75; byte effectIntensity = 128; +byte effectPalette = 0; bool onlyAP = false; bool udpConnected = false, udpRgbConnected = false; char cssCol[9][5]={"","","","","",""}; diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino index 682b53f0c..5111e680a 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled01_eeprom.ino @@ -15,6 +15,7 @@ //5 -> 0.5.1 and up //6 -> 0.6.0 and up //7 -> 0.7.1 and up +//8 -> 0.8.0 and up /* * Erase all configuration data @@ -119,8 +120,7 @@ void saveSettingsToEEPROM() EEPROM.write(370, useHSBDefault); EEPROM.write(371, whiteS); EEPROM.write(372, useRGBW); - EEPROM.write(373, sweepTransition); - EEPROM.write(374, sweepDirection); + EEPROM.write(375, apWaitTimeSecs); EEPROM.write(376, recoveryAPDisabled); EEPROM.write(377, EEPVER); //eeprom was updated to latest @@ -128,13 +128,7 @@ void saveSettingsToEEPROM() EEPROM.write(379, colSecS[1]); EEPROM.write(380, colSecS[2]); EEPROM.write(381, whiteSecS); - EEPROM.write(382, ccIndex1); - EEPROM.write(383, ccIndex2); - EEPROM.write(384, ccNumPrimary); - EEPROM.write(385, ccNumSecondary); - EEPROM.write(386, ccFromStart); - EEPROM.write(387, ccFromEnd); - EEPROM.write(388, ccStep); + EEPROM.write(389, bootPreset); EEPROM.write(390, aOtaEnabled); EEPROM.write(391, receiveNotificationColor); @@ -344,6 +338,8 @@ void loadSettingsFromEEPROM(bool first) useGammaCorrectionBri = EEPROM.read(330); useGammaCorrectionRGB = EEPROM.read(331); overlayDefault = EEPROM.read(332); + if (lastEEPROMversion < 8 && overlayDefault > 0) overlayDefault--; //overlay mode 1 (solid) was removed + alexaEnabled = EEPROM.read(333); for (int i = 334; i < 366; ++i) @@ -358,8 +354,7 @@ void loadSettingsFromEEPROM(bool first) useHSBDefault = EEPROM.read(370); whiteS = EEPROM.read(371); white = whiteS; useRGBW = EEPROM.read(372); - sweepTransition = EEPROM.read(373); - sweepDirection = EEPROM.read(374); + if (lastEEPROMversion > 0) { apWaitTimeSecs = EEPROM.read(375); recoveryAPDisabled = EEPROM.read(376); @@ -369,15 +364,7 @@ void loadSettingsFromEEPROM(bool first) colSecS[0] = EEPROM.read(378); colSec[0] = colSecS[0]; colSecS[1] = EEPROM.read(379); colSec[1] = colSecS[1]; colSecS[2] = EEPROM.read(380); colSec[2] = colSecS[2]; - whiteSecS = EEPROM.read(381); whiteSec = whiteSecS; - ccIndex1 = EEPROM.read(382); - ccIndex2 = EEPROM.read(383); - ccNumPrimary = EEPROM.read(384); - ccNumSecondary = EEPROM.read(385); - ccFromStart = EEPROM.read(386); - ccFromEnd = EEPROM.read(387); - ccStep = EEPROM.read(388); - strip.setCustomChase(ccIndex1, ccIndex2, ccStart, ccNumPrimary, ccNumSecondary, ccStep, ccFromStart, ccFromEnd); + whiteSecS = EEPROM.read(381); whiteSec = whiteSecS; } if (lastEEPROMversion > 3) { effectIntensityDefault = EEPROM.read(326); effectIntensity = effectIntensityDefault; @@ -545,12 +532,6 @@ void applyPreset(byte index, bool loadBri, bool loadCol, bool loadFX) effectCurrent = EEPROM.read(i+10); effectSpeed = EEPROM.read(i+11); effectIntensity = EEPROM.read(i+16); - ccNumPrimary = EEPROM.read(i+12); - ccNumSecondary = EEPROM.read(i+13); - ccFromEnd = EEPROM.read(i+14); - ccFromStart = (EEPROM.read(i+14)<2); - ccStep = EEPROM.read(i+15); - strip.setCustomChase(ccIndex1, ccIndex2, ccStart, ccNumPrimary, ccNumSecondary, ccStep, ccFromStart, ccFromEnd); if (lastfx != effectCurrent) strip.setMode(effectCurrent); strip.setSpeed(effectSpeed); strip.setIntensity(effectIntensity); @@ -574,13 +555,7 @@ void savePreset(byte index) EEPROM.write(i+9, whiteSec); EEPROM.write(i+10, effectCurrent); EEPROM.write(i+11, effectSpeed); - EEPROM.write(i+12, ccNumPrimary); - EEPROM.write(i+13, ccNumSecondary); - byte m = 1; - if (!ccFromStart) m = 2; - if (!ccFromEnd) m = 0; - EEPROM.write(i+14, m); - EEPROM.write(i+15, ccStep); + EEPROM.write(i+16, effectIntensity); EEPROM.commit(); } diff --git a/wled00/wled02_xml.ino b/wled00/wled02_xml.ino index 414d5b946..19dfdfa8f 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/wled02_xml.ino @@ -188,8 +188,6 @@ void getSettingsJS(byte subPage) //get values for settings form in javascript sappend('c',"GB",useGammaCorrectionBri); sappend('c',"GC",useGammaCorrectionRGB); sappend('c',"TF",fadeTransition); - sappend('c',"TS",sweepTransition); - sappend('c',"TI",!sweepDirection); sappend('v',"TD",transitionDelay); sappend('c',"T2",!disableSecTransition); sappend('v',"BF",briMultiplier); diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index a43fac2b2..ca90b6911 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -74,7 +74,6 @@ void handleSettingsSet(byte subPage) if (ledCount > 600) ledCount = 600; #endif } - ccIndex2 = ledCount -1; useRGBW = server.hasArg("EW"); autoRGBtoRGBW = server.hasArg("AW"); if (server.hasArg("IS")) //ignore settings and save current brightness, colors and fx as default @@ -158,8 +157,6 @@ void handleSettingsSet(byte subPage) useGammaCorrectionBri = server.hasArg("GB"); useGammaCorrectionRGB = server.hasArg("GC"); fadeTransition = server.hasArg("TF"); - sweepTransition = server.hasArg("TS"); - sweepDirection = !server.hasArg("TI"); if (server.hasArg("TD")) { int i = server.arg("TD").toInt(); @@ -288,7 +285,7 @@ void handleSettingsSet(byte subPage) if (server.hasArg("OL")){ overlayDefault = server.arg("OL").toInt(); overlayCurrent = overlayDefault; - strip.unlockAll(); + ; } if (server.hasArg("O1")) overlayMin = server.arg("O1").toInt(); if (server.hasArg("O2")) overlayMax = server.arg("O2").toInt(); @@ -357,7 +354,7 @@ void handleSettingsSet(byte subPage) } } saveSettingsToEEPROM(); - if (subPage == 2) strip.init(useRGBW,ledCount,PIN,skipFirstLed); + if (subPage == 2) strip.init(useRGBW,ledCount,skipFirstLed); } bool handleSet(String req) @@ -462,6 +459,7 @@ bool handleSet(String req) colSec[2] = 255; } } + //set 2nd to black pos = req.indexOf("SB"); if (pos > 0) { @@ -470,6 +468,7 @@ bool handleSet(String req) colSec[1] = 0; colSec[2] = 0; } + //set to random hue SR=0->1st SR=1->2nd pos = req.indexOf("SR"); if (pos > 0) { @@ -553,19 +552,6 @@ bool handleSet(String req) overlayCurrent = req.substring(pos + 3).toInt(); strip.unlockAll(); } - //set individual pixel (range) to current color - pos = req.indexOf("&I="); - if (pos > 0){ - int index = req.substring(pos + 3).toInt(); - pos = req.indexOf("I2="); - if (pos > 0){ - int index2 = req.substring(pos + 3).toInt(); - strip.setRange(index, index2); - } else - { - strip.setIndividual(index); - } - } //(un)lock pixel (ranges) pos = req.indexOf("&L="); if (pos > 0){ @@ -591,6 +577,7 @@ bool handleSet(String req) } } } + //apply macro pos = req.indexOf("&M="); if (pos > 0) { @@ -605,6 +592,7 @@ bool handleSet(String req) notifyDirect = false; } } + //toggle receive UDP direct notifications if (req.indexOf("RN=") > 0) { @@ -614,6 +602,7 @@ bool handleSet(String req) receiveNotifications = false; } } + //toggle nightlight mode bool aNlDef = false; if (req.indexOf("&ND") > 0) aNlDef = true; @@ -634,12 +623,14 @@ bool handleSet(String req) nightlightActive = true; nightlightStartTime = millis(); } + //set nightlight target brightness pos = req.indexOf("NT="); if (pos > 0) { nightlightTargetBri = req.substring(pos + 3).toInt(); nightlightActiveOld = false; //re-init } + //toggle nightlight fade if (req.indexOf("NF=") > 0) { @@ -651,6 +642,7 @@ bool handleSet(String req) } nightlightActiveOld = false; //re-init } + //toggle general purpose output pos = req.indexOf("AX="); if (pos > 0) { @@ -662,6 +654,7 @@ bool handleSet(String req) if (pos > 0) { transitionDelay = req.substring(pos + 3).toInt(); } + //main toggle on/off pos = req.indexOf("&T="); if (pos > 0) { @@ -679,6 +672,7 @@ bool handleSet(String req) } } } + //deactivate nightlight if target brightness is reached if (bri == nightlightTargetBri) nightlightActive = false; //set time (unix timestamp) @@ -686,6 +680,7 @@ bool handleSet(String req) if (pos > 0) { setTime(req.substring(pos+3).toInt()); } + //set countdown goal (unix timestamp) pos = req.indexOf("CT="); if (pos > 0) { @@ -693,19 +688,6 @@ bool handleSet(String req) if (countdownTime - now() > 0) countdownOverTriggered = false; } - //set custom chase data - bool _cc_updated = false; - pos = req.indexOf("C0="); if (pos > 0) {ccStart = (req.substring(pos + 3).toInt()); _cc_updated = true;} - pos = req.indexOf("C1="); if (pos > 0) {ccIndex1 = (req.substring(pos + 3).toInt()); _cc_updated = true;} - pos = req.indexOf("C2="); if (pos > 0) {ccIndex2 = (req.substring(pos + 3).toInt()); _cc_updated = true;} - pos = req.indexOf("CP="); if (pos > 0) {ccNumPrimary = (req.substring(pos + 3).toInt()); _cc_updated = true;} - pos = req.indexOf("CS="); if (pos > 0) {ccNumSecondary = (req.substring(pos + 3).toInt()); _cc_updated = true;} - pos = req.indexOf("CM="); if (pos > 0) {ccStep = (req.substring(pos + 3).toInt()); _cc_updated = true;} - pos = req.indexOf("CF="); if (pos > 0) {ccFromStart = (req.substring(pos + 3).toInt()); _cc_updated = true;} - pos = req.indexOf("CE="); if (pos > 0) {ccFromEnd = (req.substring(pos + 3).toInt()); _cc_updated = true;} - if (ccIndex2 == 255) ccIndex2 = ledCount-1; - if (_cc_updated) strip.setCustomChase(ccIndex1, ccIndex2, ccStart, ccNumPrimary, ccNumSecondary, ccStep, ccFromStart, ccFromEnd); - //set presets pos = req.indexOf("P1="); //sets first preset for cycle if (pos > 0) presetCycleMin = req.substring(pos + 3).toInt(); diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index e11d9f9fe..66fea65db 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -11,7 +11,7 @@ void wledInit() #ifdef ARDUINO_ARCH_ESP32 if (ledCount > 600) ledCount = 600; #endif - if (!EEPROM.read(397)) strip.init(EEPROM.read(372),ledCount,PIN,EEPROM.read(2204)); //quick init + if (!EEPROM.read(397)) strip.init(EEPROM.read(372),ledCount,EEPROM.read(2204)); //quick init Serial.begin(115200); Serial.setTimeout(50); @@ -273,11 +273,10 @@ void wledInit() void initStrip() { // Initialize NeoPixel Strip and button - if (initLedsLast) strip.init(useRGBW,ledCount,PIN,skipFirstLed); + if (initLedsLast) strip.init(useRGBW,ledCount,skipFirstLed); strip.setReverseMode(reverseMode); strip.setColor(0); strip.setBrightness(255); - strip.start(); pinMode(buttonPin, INPUT_PULLUP); pinMode(4,OUTPUT); //this is only needed in special cases @@ -572,11 +571,11 @@ void getBuildInfo() oappend("\r\n"); #ifdef ARDUINO_ARCH_ESP32 oappend("strip-pin: gpio"); - oappendi(PIN); + oappendi(LEDPIN); #else oappend("strip-pin: gpio2"); #endif - oappend("\r\nbuild-type: src\r\n"); + oappend("\r\nbuild-type: dev\r\n"); } bool checkClientIsMobile(String useragent) diff --git a/wled00/wled08_led.ino b/wled00/wled08_led.ino index e3b001cc6..46e5aed0b 100644 --- a/wled00/wled08_led.ino +++ b/wled00/wled08_led.ino @@ -93,7 +93,7 @@ void colorUpdated(int callMode) briIT = bri; if (bri > 0) briLast = bri; notify(callMode); - if (fadeTransition || sweepTransition) + if (fadeTransition) { //set correct delay if not using notification delay if (callMode != 3) transitionDelayTemp = transitionDelay; @@ -114,7 +114,6 @@ void colorUpdated(int callMode) } transitionActive = true; transitionStartTime = millis(); - strip.setFastUpdateMode(true); } else { setLedsStandard(); @@ -132,9 +131,7 @@ void handleTransitions() { transitionActive = false; tperLast = 0; - if (sweepTransition) strip.unlockAll(); setLedsStandard(); - strip.setFastUpdateMode(false); return; } if (tper - tperLast < 0.004) @@ -153,21 +150,7 @@ void handleTransitions() whiteSecT = whiteSecOld +((whiteSec - whiteSecOld )*tper); briT = briOld +((bri - briOld )*tper); } - if (sweepTransition) - { - strip.lockAll(); - if (sweepDirection) - { - strip.unlockRange(0, (int)(tper*(double)ledCount)); - } else - { - strip.unlockRange(ledCount - (int)(tper*(double)ledCount), ledCount); - } - if (!fadeTransition) - { - setLedsStandard(); - } - } + //TODO: properly remove sweep transition if (fadeTransition) setAllLeds(); } } diff --git a/wled00/wled11_ol.ino b/wled00/wled11_ol.ino index 1cdc9cddb..8695d51af 100644 --- a/wled00/wled11_ol.ino +++ b/wled00/wled11_ol.ino @@ -125,24 +125,15 @@ void handleOverlays() switch (overlayCurrent) { case 0: break;//no overlay - case 1: _overlaySolid(); break;//solid secondary color - case 2: _overlayAnalogClock(); break;//2 analog clock - case 3: _overlayNixieClock(); break;//nixie 1-digit - case 4: _overlayCronixie();//Diamex cronixie clock kit + case 1: _overlayAnalogClock(); break;//2 analog clock + case 2: _overlayNixieClock(); break;//nixie 1-digit + case 3: _overlayCronixie();//Diamex cronixie clock kit } if (!countdownMode || overlayCurrent < 2) checkCountdown(); //countdown macro activation must work overlayRefreshedTime = millis(); } } -void _overlaySolid() -{ - strip.unlockAll(); - uint32_t cls = (useGammaCorrectionRGB)? gamma8[whiteSec*16777216] + gamma8[colSec[0]]*65536 + gamma8[colSec[1]]*256 + gamma8[colSec[2]]:whiteSec*16777216 + colSec[0]*65536 + colSec[1]*256 + colSec[2]; - strip.setRange(overlayMin,overlayMax,cls); - overlayRefreshMs = 1902; -} - void _overlayAnalogClock() { int overlaySize = overlayMax - overlayMin +1; @@ -151,7 +142,6 @@ void _overlayAnalogClock() { _overlayAnalogCountdown(); return; } - _overlaySolid(); double hourP = ((double)(hour(local)%12))/12; double minuteP = ((double)minute(local))/60; hourP = hourP + minuteP/12; diff --git a/wled00/wled16_blynk.ino b/wled00/wled16_blynk.ino index 4120f03b6..82aae164d 100644 --- a/wled00/wled16_blynk.ino +++ b/wled00/wled16_blynk.ino @@ -58,18 +58,21 @@ BLYNK_WRITE(V3) BLYNK_WRITE(V4) { effectCurrent = param.asInt()-1;//fx - colorUpdated(9); + strip.setMode(effectCurrent); + colorUpdated(6); } BLYNK_WRITE(V5) { effectSpeed = param.asInt();//sx + strip.setSpeed(effectSpeed); colorUpdated(6); } BLYNK_WRITE(V6) { effectIntensity = param.asInt();//ix + strip.setIntensity(effectIntensity); colorUpdated(6); } From 6d4339b034316e4089d1732ab6155f9ba23c5628 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Thu, 6 Sep 2018 02:05:56 +0200 Subject: [PATCH 02/22] Added first palette functions Attempted to fix welcome page --- wled00/WS2812FX.cpp | 267 +++++++++++++++++++++++----------- wled00/WS2812FX.h | 15 +- wled00/data/index.htm | 2 +- wled00/data/settings_leds.htm | Bin 8336 -> 8516 bytes wled00/htmls00.h | 2 +- wled00/htmls01.h | 1 + wled00/wled00.ino | 2 +- wled00/wled01_eeprom.ino | 9 +- wled00/wled02_xml.ino | 5 +- wled00/wled03_set.ino | 17 +++ wled00/wled05_init.ino | 14 +- wled00/wled07_notify.ino | 8 +- wled00/wled08_led.ino | 2 + 13 files changed, 240 insertions(+), 104 deletions(-) diff --git a/wled00/WS2812FX.cpp b/wled00/WS2812FX.cpp index 505f0ba84..34b304403 100644 --- a/wled00/WS2812FX.cpp +++ b/wled00/WS2812FX.cpp @@ -99,7 +99,7 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) { if (_reverseMode) i = _length - 1 -i; if (_locked[i] && SEGMENT.mode != FX_MODE_FIRE_2012) return; - if (IS_REVERSE) i = SEGMENT.stop - (i - SEGMENT.start); //reverse just individual segment + if (IS_REVERSE) i = SEGMENT.stop - (i - SEGMENT.start); //reverse just individual segment if (!_cronixieMode) { if (_skipFirstMode) {i++;if(i==1)bus->SetPixelColor(i, RgbwColor(0,0,0,0));} @@ -191,6 +191,10 @@ void WS2812FX::setIntensity(uint8_t in) { _segments[0].intensity = in; } +void WS2812FX::setPalette(uint8_t p) { + _segments[0].palette = p; +} + void WS2812FX::setColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w) { setColor(((uint32_t)w << 24) |((uint32_t)r << 16) | ((uint32_t)g << 8) | b); } @@ -201,13 +205,11 @@ void WS2812FX::setSecondaryColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w) { void WS2812FX::setColor(uint32_t c) { _segments[0].colors[0] = c; - _triggered = true; } void WS2812FX::setSecondaryColor(uint32_t c) { _segments[0].colors[1] = c; if (_cronixieMode) _cronixieSecMultiplier = getSafePowerMultiplier(900, 100, c, _brightness); - _triggered = true; } void WS2812FX::setBrightness(uint8_t b) { @@ -369,6 +371,14 @@ void WS2812FX::unlockAll() for (int i=0; i < _length; i++) _locked[i] = false; } +void WS2812FX::setTransitionMode(bool t) +{ + SEGMENT_RUNTIME.trans_act = (t) ? 1:2; + if (!t) return; + unsigned long waitMax = millis() + 20; //refresh after 20 seconds if transition enabled + if (SEGMENT.mode == FX_MODE_STATIC && SEGMENT_RUNTIME.next_time > waitMax) SEGMENT_RUNTIME.next_time = waitMax; +} + /* * color blend function */ @@ -483,7 +493,7 @@ uint16_t WS2812FX::mode_static(void) { for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { setPixelColor(i, SEGMENT.colors[0]); } - return 500; + return (SEGMENT_RUNTIME.trans_act == 1) ? 20 : 500; } @@ -1864,6 +1874,44 @@ uint16_t WS2812FX::mode_oscillate(void) } +uint16_t WS2812FX::mode_lightning(void) +{ + uint16_t ledstart = SEGMENT.start + random8(SEGMENT_LENGTH); // Determine starting location of flash + uint16_t ledlen = random8(SEGMENT.stop - ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1) + uint8_t bri = 255/random8(1, 3); + + if (SEGMENT_RUNTIME.counter_mode_step == 0) + { + SEGMENT_RUNTIME.aux_param = random8(3, 3 + SEGMENT.intensity/20); //number of flashes + bri = 52; + SEGMENT_RUNTIME.aux_param2 = 1; + } + + for (int i = SEGMENT.start; i <= SEGMENT.stop; i++) + { + setPixelColor(i,SEGMENT.colors[1]); + } + + if (SEGMENT_RUNTIME.aux_param2) { + for (int i = ledstart; i < ledstart + ledlen; i++) + { + setPixelColor(i,bri,bri,bri,bri); + } + SEGMENT_RUNTIME.aux_param2 = 0; + SEGMENT_RUNTIME.counter_mode_step++; + return random8(4, 10); // each flash only lasts 4-10 milliseconds + } + + SEGMENT_RUNTIME.aux_param2 = 1; + if (SEGMENT_RUNTIME.counter_mode_step == 1) return (200); // longer delay until next flash after the leader + + if (SEGMENT_RUNTIME.counter_mode_step <= SEGMENT_RUNTIME.aux_param) return (50 + random8(100)); // shorter delay between strokes + + SEGMENT_RUNTIME.counter_mode_step = 0; + return (random8(255 - SEGMENT.speed) * 100); // delay between strikes +} + + // WLED limitation: Analog Clock overlay will NOT work when Fire2012 is active // Fire2012 by Mark Kriegsman, July 2012 // as part of "Five Elements" shown here: http://youtu.be/knWiGsmgycY @@ -1925,21 +1973,6 @@ uint16_t WS2812FX::mode_fire_2012(void) } -uint16_t WS2812FX::mode_bpm(void) -{ - CRGB fastled_col; - CRGBPalette16 palette = PartyColors_p; - uint8_t beat = beatsin8(SEGMENT.speed, 64, 255); - for ( int i = SEGMENT.start; i <= SEGMENT.stop; i++) { - fastled_col = ColorFromPalette(palette, SEGMENT_RUNTIME.counter_mode_step + (i * 2), beat - SEGMENT_RUNTIME.counter_mode_step + (i * 10)); - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); - } - SEGMENT_RUNTIME.counter_mode_step++; - if (SEGMENT_RUNTIME.counter_mode_step >= 255) SEGMENT_RUNTIME.counter_mode_step = 0; - return 20; -} - - uint16_t WS2812FX::mode_juggle(void){ fade_out((255-SEGMENT.intensity) / 32); CRGB fastled_col; @@ -1957,22 +1990,134 @@ uint16_t WS2812FX::mode_juggle(void){ return 10 + (uint16_t)(255 - SEGMENT.speed)/4; } - +/* + * FastLED palette modes helper function. Limitation: Due to memory reasons, multiple active segments with FastLED will disable the Palette transitions + */ CRGBPalette16 currentPalette(CRGB::Black); CRGBPalette16 targetPalette(CloudColors_p); +void WS2812FX::handle_palette(void) +{ + bool singleSegmentMode = (_segment_index == _segment_index_palette_last); + _segment_index_palette_last = _segment_index; + + switch (SEGMENT.palette) + { + case 0: {//periodically replace palette with a random one. Doesn't work with multiple FastLED segments + if (!singleSegmentMode) + { + targetPalette = PartyColors_p; break; //fallback + } + if (millis() - _lastPaletteChange > 1000 + ((uint32_t)(255-SEGMENT.intensity))*100) + { + targetPalette = CRGBPalette16( + CHSV(random8(), 255, random8(128, 255)), + CHSV(random8(), 255, random8(128, 255)), + CHSV(random8(), 192, random8(128, 255)), + CHSV(random8(), 255, random8(128, 255))); + _lastPaletteChange = millis(); + } break;} + case 1: {//primary color only + CRGB prim; + prim.red = (SEGMENT.colors[0] >> 16 & 0xFF); + prim.green = (SEGMENT.colors[0] >> 8 & 0xFF); + prim.blue = (SEGMENT.colors[0] & 0xFF); + targetPalette = CRGBPalette16(prim); break;} + case 2: {//based on primary + //considering performance implications + CRGB prim; + prim.red = (SEGMENT.colors[0] >> 16 & 0xFF); + prim.green = (SEGMENT.colors[0] >> 8 & 0xFF); + prim.blue = (SEGMENT.colors[0] & 0xFF); + CHSV prim_hsv = rgb2hsv_approximate(prim); + targetPalette = CRGBPalette16( + CHSV(prim_hsv.h, prim_hsv.s, prim_hsv.v), //color itself + CHSV(prim_hsv.h, max(prim_hsv.s - 50,0), prim_hsv.v), //less saturated + CHSV(prim_hsv.h, prim_hsv.s, max(prim_hsv.h - 50,0)), //darker + CHSV(prim_hsv.h, prim_hsv.s, prim_hsv.v)); //color itself + break;} + case 3: {//primary + secondary + CRGB prim; + prim.red = (SEGMENT.colors[0] >> 16 & 0xFF); + prim.green = (SEGMENT.colors[0] >> 8 & 0xFF); + prim.blue = (SEGMENT.colors[0] & 0xFF); + CRGB sec; + sec.red = (SEGMENT.colors[1] >> 16 & 0xFF); + sec.green = (SEGMENT.colors[1] >> 8 & 0xFF); + sec.blue = (SEGMENT.colors[1] & 0xFF); + targetPalette = CRGBPalette16(prim,sec,prim); break;} + case 4: {//based on primary + secondary + CRGB prim; + prim.red = (SEGMENT.colors[0] >> 16 & 0xFF); + prim.green = (SEGMENT.colors[0] >> 8 & 0xFF); + prim.blue = (SEGMENT.colors[0] & 0xFF); + CRGB sec; + sec.red = (SEGMENT.colors[1] >> 16 & 0xFF); + sec.green = (SEGMENT.colors[1] >> 8 & 0xFF); + sec.blue = (SEGMENT.colors[1] & 0xFF); + CHSV prim_hsv = rgb2hsv_approximate(prim); + CHSV sec_hsv = rgb2hsv_approximate(sec ); + targetPalette = CRGBPalette16( + CHSV(prim_hsv.h, prim_hsv.s, prim_hsv.v), //color itself + CHSV(prim_hsv.h, max(prim_hsv.s - 50,0), prim_hsv.v), //less saturated + CHSV(sec_hsv.h, sec_hsv.s, max(sec_hsv.v - 50,0)), //darker + CHSV(sec_hsv.h, sec_hsv.s, sec_hsv.v)); //color itself + break;} + case 5: //Party colors + targetPalette = PartyColors_p; break; + case 6: //Cloud colors + targetPalette = CloudColors_p; break; + case 7: //Lava colors + targetPalette = LavaColors_p; break; + case 8: //Ocean colors + targetPalette = OceanColors_p; break; + case 9: //Forest colors + targetPalette = ForestColors_p; break; + case 10: //Rainbow colors + targetPalette = RainbowColors_p; break; + case 11: //Rainbow stripe colors + targetPalette = RainbowStripeColors_p; break; + default: //fallback for illegal values + targetPalette = PartyColors_p; break; + } + + if (singleSegmentMode) //only blend if just one segment uses FastLED mode + { + nblendPaletteTowardPalette(currentPalette, targetPalette, 42); + } else + { + currentPalette = targetPalette; + } +} + uint16_t WS2812FX::mode_palette(void) { + handle_palette(); CRGB fastled_col; - for (uint16_t i = SEGMENT.start; i <= SEGMENT.stop; i++) { - uint8_t colorIndex = map(i,SEGMENT.start,SEGMENT.stop,0,255) + (SEGMENT_RUNTIME.counter_mode_step >> 8 & 0xFF); - fastled_col = ColorFromPalette( PartyColors_p, colorIndex, 255, LINEARBLEND); + uint8_t colorIndex = map(i,SEGMENT.start,SEGMENT.stop,0,255) + (SEGMENT_RUNTIME.counter_mode_step >> 6 & 0xFF); + fastled_col = ColorFromPalette( currentPalette, colorIndex, 255, LINEARBLEND); setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); } SEGMENT_RUNTIME.counter_mode_step += SEGMENT.speed; + if (SEGMENT.speed == 0) SEGMENT_RUNTIME.counter_mode_step = 0; + return 20; +} + + +uint16_t WS2812FX::mode_bpm(void) +{ + handle_palette(); + CRGB fastled_col; + uint8_t beat = beatsin8(SEGMENT.speed, 64, 255); + for ( int i = SEGMENT.start; i <= SEGMENT.stop; i++) { + fastled_col = ColorFromPalette(currentPalette, SEGMENT_RUNTIME.counter_mode_step + (i * 2), beat - SEGMENT_RUNTIME.counter_mode_step + (i * 10)); + setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + } + SEGMENT_RUNTIME.counter_mode_step++; + if (SEGMENT_RUNTIME.counter_mode_step >= 255) SEGMENT_RUNTIME.counter_mode_step = 0; return 20; } @@ -1980,33 +2125,23 @@ uint16_t WS2812FX::mode_palette(void) uint16_t WS2812FX::mode_fillnoise8(void) { if (SEGMENT_RUNTIME.counter_mode_call == 0) SEGMENT_RUNTIME.counter_mode_step = random(12345); + handle_palette(); CRGB fastled_col; - nblendPaletteTowardPalette(currentPalette, targetPalette, 42); for (int i = SEGMENT.start; i <= SEGMENT.stop; i++) { uint8_t index = inoise8(i * SEGMENT_LENGTH, SEGMENT_RUNTIME.counter_mode_step + i * SEGMENT_LENGTH) % 255; fastled_col = ColorFromPalette(currentPalette, index, 255, LINEARBLEND); setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); } - SEGMENT_RUNTIME.counter_mode_step += beatsin8(10, 1, 4); + SEGMENT_RUNTIME.counter_mode_step += beatsin8(SEGMENT.speed, 1, 4); //10,1,4 - if (SEGMENT_RUNTIME.counter_mode_call >= 20 + (255 - SEGMENT.intensity) *4) //swap to new random palette - { - targetPalette = CRGBPalette16( - CHSV(random8(), 255, random8(128, 255)), - CHSV(random8(), 255, random8(128, 255)), - CHSV(random8(), 192, random8(128, 255)), - CHSV(random8(), 255, random8(128, 255))); - SEGMENT_RUNTIME.counter_mode_call = 1; - } - - return 15 + (uint16_t)(255 - SEGMENT.speed); + return 20; } uint16_t WS2812FX::mode_noise16_1(void) { - uint16_t scale = 100 + SEGMENT.intensity*7; // the "zoom factor" for the noise - CRGBPalette16 palette = OceanColors_p; + uint16_t scale = 750; // the "zoom factor" for the noise + handle_palette(); CRGB fastled_col; SEGMENT_RUNTIME.counter_mode_step += (1 + SEGMENT.speed/16); @@ -2024,7 +2159,7 @@ uint16_t WS2812FX::mode_noise16_1(void) uint8_t index = sin8(noise * 3); // map LED color based on noise data - fastled_col = ColorFromPalette(palette, index, noise, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. + fastled_col = ColorFromPalette(currentPalette, index, 255, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); } @@ -2034,8 +2169,8 @@ uint16_t WS2812FX::mode_noise16_1(void) uint16_t WS2812FX::mode_noise16_2(void) { - uint8_t scale = 100 + SEGMENT.intensity*7; // the "zoom factor" for the noise - CRGBPalette16 palette = LavaColors_p; + uint8_t scale = 750; // the "zoom factor" for the noise + handle_palette(); CRGB fastled_col; SEGMENT_RUNTIME.counter_mode_step += (1 + SEGMENT.speed); @@ -2052,7 +2187,7 @@ uint16_t WS2812FX::mode_noise16_2(void) uint8_t index = sin8(noise * 3); // map led color based on noise data - fastled_col = ColorFromPalette(palette, index, noise, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. + fastled_col = ColorFromPalette(currentPalette, index, noise, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); } @@ -2062,8 +2197,8 @@ uint16_t WS2812FX::mode_noise16_2(void) uint16_t WS2812FX::mode_noise16_3(void) { - uint8_t scale = 100 + SEGMENT.intensity*7; // the "zoom factor" for the noise - CRGBPalette16 palette = CloudColors_p; + uint8_t scale = 750; // the "zoom factor" for the noise + handle_palette(); CRGB fastled_col; SEGMENT_RUNTIME.counter_mode_step += (1 + SEGMENT.speed); @@ -2080,7 +2215,7 @@ uint16_t WS2812FX::mode_noise16_3(void) uint8_t index = sin8(noise * 3); // map led color based on noise data - fastled_col = ColorFromPalette(palette, index, noise, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. + fastled_col = ColorFromPalette(currentPalette, index, noise, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); } @@ -2091,53 +2226,15 @@ uint16_t WS2812FX::mode_noise16_3(void) //https://github.com/aykevl/ledstrip-spark/blob/master/ledstrip.ino uint16_t WS2812FX::mode_noise16_4(void) { - CRGBPalette16 palette = OceanColors_p; + handle_palette(); CRGB fastled_col; SEGMENT_RUNTIME.counter_mode_step += SEGMENT.speed; for (int i = SEGMENT.start; i <= SEGMENT.stop; i++) { int16_t index = inoise16(uint32_t(i - SEGMENT.start) << 12, SEGMENT_RUNTIME.counter_mode_step/8); - fastled_col = ColorFromPalette(palette, index); + fastled_col = ColorFromPalette(currentPalette, index); setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); } return 20; } -uint16_t WS2812FX::mode_lightning(void) -{ - uint16_t ledstart = SEGMENT.start + random8(SEGMENT_LENGTH); // Determine starting location of flash - uint16_t ledlen = random8(SEGMENT.stop - ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1) - uint8_t bri = 255/random8(1, 3); - - if (SEGMENT_RUNTIME.counter_mode_step == 0) - { - SEGMENT_RUNTIME.aux_param = random8(3, 3 + SEGMENT.intensity/20); //number of flashes - bri = 52; - SEGMENT_RUNTIME.aux_param2 = 1; - } - - for (int i = SEGMENT.start; i <= SEGMENT.stop; i++) - { - setPixelColor(i,SEGMENT.colors[1]); - } - - if (SEGMENT_RUNTIME.aux_param2) { - for (int i = ledstart; i < ledstart + ledlen; i++) - { - setPixelColor(i,bri,bri,bri,bri); - } - SEGMENT_RUNTIME.aux_param2 = 0; - SEGMENT_RUNTIME.counter_mode_step++; - return random8(4, 10); // each flash only lasts 4-10 milliseconds - } - - SEGMENT_RUNTIME.aux_param2 = 1; - if (SEGMENT_RUNTIME.counter_mode_step == 1) return (200); // longer delay until next flash after the leader - - if (SEGMENT_RUNTIME.counter_mode_step <= SEGMENT_RUNTIME.aux_param) return (50 + random8(100)); // shorter delay between strokes - - SEGMENT_RUNTIME.counter_mode_step = 0; - return (random8(255 - SEGMENT.speed) * 100); // delay between strikes -} - - diff --git a/wled00/WS2812FX.h b/wled00/WS2812FX.h index f1537938e..36f613fac 100644 --- a/wled00/WS2812FX.h +++ b/wled00/WS2812FX.h @@ -51,7 +51,7 @@ /* each segment uses 38 bytes of SRAM memory, so if you're application fails because of insufficient memory, decreasing MAX_NUM_SEGMENTS may help */ -#define MAX_NUM_SEGMENTS 12 +#define MAX_NUM_SEGMENTS 10 #define NUM_COLORS 3 /* number of colors per segment */ #define SEGMENT _segments[_segment_index] #define SEGMENT_RUNTIME _segment_runtimes[_segment_index] @@ -162,11 +162,12 @@ class WS2812FX { // segment parameters public: - typedef struct Segment { // 20 bytes + typedef struct Segment { // 21 bytes uint16_t start; uint16_t stop; uint8_t speed; uint8_t intensity; + uint8_t palette; uint8_t mode; uint8_t options; uint32_t colors[NUM_COLORS]; @@ -179,7 +180,7 @@ class WS2812FX { uint32_t counter_mode_call; uint16_t aux_param; uint16_t aux_param2; - bool trans_act; + uint8_t trans_act; } segment_runtime; WS2812FX() { @@ -280,6 +281,7 @@ class WS2812FX { setMode(uint8_t m), setSpeed(uint8_t s), setIntensity(uint8_t i), + setPalette(uint8_t p), setColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), setSecondaryColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), setColor(uint32_t c), @@ -296,6 +298,7 @@ class WS2812FX { unlock(uint16_t i), unlockRange(uint16_t i, uint16_t i2), unlockAll(void), + setTransitionMode(bool t), trigger(void), setNumSegments(uint8_t n), setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint8_t speed, uint8_t intensity, bool reverse), @@ -427,6 +430,8 @@ class WS2812FX { uint16_t _rand16seed; uint8_t _brightness; + void handle_palette(void); + double _cronixieSecMultiplier; @@ -444,7 +449,11 @@ class WS2812FX { mode_ptr _mode[MODE_COUNT]; // SRAM footprint: 4 bytes per element + + uint32_t _lastPaletteChange = 0; + uint8_t _segment_index = 0; + uint8_t _segment_index_palette_last = 99; uint8_t _num_segments = 1; segment _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 20 bytes per element // start, stop, speed, intensity, mode, options, color[] diff --git a/wled00/data/index.htm b/wled00/data/index.htm index 9d8e935f6..e3f3cb89e 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -122,7 +122,7 @@ } function GP() { - resp+= "&FP=" + Cf.TX.selectedIndex; + resp+= "&FP=" + Cf.FP.selectedIndex; GIO(); } function CV(v) diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index fd0b3264565313f8a5471bb3acd89d3a50d4c333..a3dd1ea6c793f51df6cb0d14ff4e3846014984eb 100644 GIT binary patch delta 40 wcmbQ>c*JRgmB?f*AwKQ`hD3%OhE#?UhLXu2r4*Un7y>3wWLED 0.8.0-a - + )====="; //head1 (css) diff --git a/wled00/htmls01.h b/wled00/htmls01.h index 3090e6835..ab67d9db2 100644 --- a/wled00/htmls01.h +++ b/wled00/htmls01.h @@ -97,6 +97,7 @@ Default brightness: ( Default effect ID:
Default effect speed:
Default effect intensity:
+Default effect palette:
Default secondary RGB(W):
diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 0845b46c4..7a7342c90 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -39,7 +39,7 @@ #include "src/dependencies/e131/E131.h" //version in format yymmddb (b = daily build) -#define VERSION 1809041 +#define VERSION 1809062 char versionString[] = "0.8.0-a"; //AP and OTA default passwords (change them!) diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino index 5111e680a..8ad3e524f 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled01_eeprom.ino @@ -120,7 +120,8 @@ void saveSettingsToEEPROM() EEPROM.write(370, useHSBDefault); EEPROM.write(371, whiteS); EEPROM.write(372, useRGBW); - + EEPROM.write(373, effectPaletteDefault); + EEPROM.write(375, apWaitTimeSecs); EEPROM.write(376, recoveryAPDisabled); EEPROM.write(377, EEPVER); //eeprom was updated to latest @@ -354,6 +355,7 @@ void loadSettingsFromEEPROM(bool first) useHSBDefault = EEPROM.read(370); whiteS = EEPROM.read(371); white = whiteS; useRGBW = EEPROM.read(372); + effectPaletteDefault = EEPROM.read(373); effectPalette = effectPaletteDefault; if (lastEEPROMversion > 0) { apWaitTimeSecs = EEPROM.read(375); @@ -501,12 +503,13 @@ void loadSettingsFromEEPROM(bool first) strip.setMode(effectCurrent); strip.setSpeed(effectSpeed); strip.setIntensity(effectIntensity); + strip.setPalette(effectPalette); overlayCurrent = overlayDefault; } //PRESET PROTOCOL 20 bytes //0: preset purpose byte 0:invalid 1:valid preset 1.0 -//1:a 2:r 3:g 4:b 5:w 6:er 7:eg 8:eb 9:ew 10:fx 11:sx | custom chase 12:numP 13:numS 14:(0:fs 1:both 2:fe) 15:step 16:ix 17-19:Zeros +//1:a 2:r 3:g 4:b 5:w 6:er 7:eg 8:eb 9:ew 10:fx 11:sx | custom chase 12:numP 13:numS 14:(0:fs 1:both 2:fe) 15:step 16:ix 17: fp 18-19:Zeros void applyPreset(byte index, bool loadBri, bool loadCol, bool loadFX) { @@ -535,6 +538,7 @@ void applyPreset(byte index, bool loadBri, bool loadCol, bool loadFX) if (lastfx != effectCurrent) strip.setMode(effectCurrent); strip.setSpeed(effectSpeed); strip.setIntensity(effectIntensity); + strip.setPalette(effectPalette); } } @@ -557,6 +561,7 @@ void savePreset(byte index) EEPROM.write(i+11, effectSpeed); EEPROM.write(i+16, effectIntensity); + EEPROM.write(i+17, effectPalette); EEPROM.commit(); } diff --git a/wled00/wled02_xml.ino b/wled00/wled02_xml.ino index 19dfdfa8f..440aca3dc 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/wled02_xml.ino @@ -39,7 +39,9 @@ void XML_response() oappendi(effectSpeed); oappend(""); oappendi(effectIntensity); - oappend(""); + oappend(""); + oappendi(effectPalette); + oappend(""); if (useRGBW && !autoRGBtoRGBW) { oappendi(white); } else { @@ -185,6 +187,7 @@ void getSettingsJS(byte subPage) //get values for settings form in javascript sappend('v',"FX",effectDefault); sappend('v',"SX",effectSpeedDefault); sappend('v',"IX",effectIntensityDefault); + sappend('v',"FP",effectPaletteDefault); sappend('c',"GB",useGammaCorrectionBri); sappend('c',"GC",useGammaCorrectionRGB); sappend('c',"TF",fadeTransition); diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index ca90b6911..0dfcf063a 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -85,6 +85,8 @@ void handleSettingsSet(byte subPage) briS = bri; effectDefault = effectCurrent; effectSpeedDefault = effectSpeed; + effectIntensityDefault = effectIntensity; + effectPaletteDefault = effectPalette; } else { if (server.hasArg("CR")) { @@ -146,6 +148,11 @@ void handleSettingsSet(byte subPage) int i = server.arg("IX").toInt(); if (i >= 0 && i <= 255) effectIntensityDefault = i; } + if (server.hasArg("FP")) + { + int i = server.arg("FP").toInt(); + if (i >= 0 && i <= 255) effectPaletteDefault = i; + } } saveCurrPresetCycConf = server.hasArg("PC"); turnOnAtBoot = server.hasArg("BO"); @@ -527,6 +534,16 @@ bool handleSet(String req) effectUpdated = true; } } + //set effect palette (only for FastLED effects) + pos = req.indexOf("FP="); + if (pos > 0) { + if (effectPalette != req.substring(pos + 3).toInt()) + { + effectPalette = req.substring(pos + 3).toInt(); + strip.setPalette(effectPalette); + effectUpdated = true; + } + } //set hue polling light: 0 -off pos = req.indexOf("HP="); diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index 66fea65db..29e17354c 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -98,11 +98,11 @@ void wledInit() }); server.on("/generate_204", HTTP_GET, [](){ - serveIndexOrWelcome(); + serveIndex(); }); server.on("/fwlink", HTTP_GET, [](){ - serveIndexOrWelcome(); + serveIndex(); }); server.on("/sliders", HTTP_GET, serveIndex); @@ -189,7 +189,7 @@ void wledInit() //if OTA is allowed if (!otaLock){ server.on("/edit", HTTP_GET, [](){ - if(!handleFileRead("/edit.htm")) server.send(200, "text/html", PAGE_edit); + server.send(200, "text/html", PAGE_edit); }); #ifdef USEFS server.on("/edit", HTTP_PUT, handleFileCreate); @@ -395,13 +395,9 @@ void buildCssColorString() void serveIndexOrWelcome() { if (!showWelcomePage){ - if(!handleFileRead("/index.htm")) { - serveIndex(); - } + serveIndex(); }else{ - if(!handleFileRead("/welcome.htm")) { - serveSettings(255); - } + serveSettings(255); showWelcomePage = false; } } diff --git a/wled00/wled07_notify.ino b/wled00/wled07_notify.ino index 78fa78483..7bff20128 100644 --- a/wled00/wled07_notify.ino +++ b/wled00/wled07_notify.ino @@ -31,7 +31,7 @@ void notify(byte callMode, bool followUp=false) udpOut[8] = effectCurrent; udpOut[9] = effectSpeed; udpOut[10] = white; - udpOut[11] = 4; //compatibilityVersionByte: 0: old 1: supports white 2: supports secondary color 3: supports FX intensity, 24 byte packet 4: supports transitionDelay + udpOut[11] = 5; //compatibilityVersionByte: 0: old 1: supports white 2: supports secondary color 3: supports FX intensity, 24 byte packet 4: supports transitionDelay 5: sup palette udpOut[12] = colSec[0]; udpOut[13] = colSec[1]; udpOut[14] = colSec[2]; @@ -39,6 +39,7 @@ void notify(byte callMode, bool followUp=false) udpOut[16] = effectIntensity; udpOut[17] = (transitionDelay >> 0) & 0xFF; udpOut[18] = (transitionDelay >> 8) & 0xFF; + udpOut[19] = effectPalette; IPAddress broadcastIp; broadcastIp = ~WiFi.subnetMask() | WiFi.gatewayIP(); @@ -170,6 +171,11 @@ void handleNotifications() { transitionDelayTemp = ((udpIn[17] << 0) & 0xFF) + ((udpIn[18] << 8) & 0xFF00); } + if (udpIn[11] > 4 && udpIn[19] != effectPalette && receiveNotificationEffects) + { + effectPalette = udpIn[19]; + strip.setPalette(effectPalette); + } nightlightActive = udpIn[6]; if (!nightlightActive) { diff --git a/wled00/wled08_led.ino b/wled00/wled08_led.ino index 46e5aed0b..fba4e5264 100644 --- a/wled00/wled08_led.ino +++ b/wled00/wled08_led.ino @@ -112,6 +112,7 @@ void colorUpdated(int callMode) briOld = briT; tperLast = 0; } + strip.setTransitionMode(true); transitionActive = true; transitionStartTime = millis(); } else @@ -129,6 +130,7 @@ void handleTransitions() float tper = (millis() - transitionStartTime)/(float)transitionDelayTemp; if (tper >= 1.0) { + strip.setTransitionMode(false); transitionActive = false; tperLast = 0; setLedsStandard(); From ce5fec4d5f0777eb4d4173d51d0cd11de23432b9 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sat, 8 Sep 2018 16:21:44 +0200 Subject: [PATCH 03/22] Added Pride2012 and Colorwaves effects Adjusted Noise16 effects Added palette fade transition option --- wled00/WS2812FX.cpp | 143 +++++++++++++-- wled00/WS2812FX.h | 27 ++- wled00/data/index.htm | 26 +-- wled00/data/index_mobile.htm | 334 +++++++++++++++++++--------------- wled00/data/settings_leds.htm | Bin 8516 -> 8646 bytes wled00/htmls00.h | 191 ++++++++++++++++--- wled00/htmls01.h | 3 +- wled00/wled00.ino | 2 +- wled00/wled01_eeprom.ino | 3 +- wled00/wled02_xml.ino | 1 + wled00/wled03_set.ino | 1 + wled00/wled05_init.ino | 2 +- 12 files changed, 520 insertions(+), 213 deletions(-) diff --git a/wled00/WS2812FX.cpp b/wled00/WS2812FX.cpp index 34b304403..fa567421b 100644 --- a/wled00/WS2812FX.cpp +++ b/wled00/WS2812FX.cpp @@ -1667,6 +1667,11 @@ uint16_t WS2812FX::mode_circus_combustus(void) { */ uint16_t WS2812FX::mode_icu(void) { uint16_t dest = SEGMENT_RUNTIME.counter_mode_step & 0xFFFF; + + for (uint16_t i = SEGMENT.start; i <= SEGMENT.stop; i++) + { + setPixelColor(i, SEGMENT.colors[1]); + } setPixelColor(SEGMENT.start + dest, SEGMENT.colors[0]); setPixelColor(SEGMENT.start + dest + SEGMENT_LENGTH/2, SEGMENT.colors[0]); @@ -1973,12 +1978,62 @@ uint16_t WS2812FX::mode_fire_2012(void) } +// Pride2015 +// Animated, ever-changing rainbows. +// by Mark Kriegsman: https://gist.github.com/kriegsman/964de772d64c502760e5 +uint16_t WS2812FX::mode_pride_2015(void) +{ + uint16_t duration = 10 + SEGMENT.speed; + uint16_t sPseudotime = SEGMENT_RUNTIME.counter_mode_step; + uint16_t sHue16 = SEGMENT_RUNTIME.aux_param; + + uint8_t sat8 = beatsin88( 87, 220, 250); + uint8_t brightdepth = beatsin88( 341, 96, 224); + uint16_t brightnessthetainc16 = beatsin88( 203, (25 * 256), (40 * 256)); + uint8_t msmultiplier = beatsin88(147, 23, 60); + + uint16_t hue16 = sHue16;//gHue * 256; + uint16_t hueinc16 = beatsin88(113, 1, 3000); + + sPseudotime += duration * msmultiplier; + sHue16 += duration * beatsin88( 400, 5,9); + uint16_t brightnesstheta16 = sPseudotime; + CRGB fastled_col; + + for( uint16_t i = SEGMENT.start ; i <= SEGMENT.stop; i++) { + hue16 += hueinc16; + uint8_t hue8 = hue16 >> 8; + + brightnesstheta16 += brightnessthetainc16; + uint16_t b16 = sin16( brightnesstheta16 ) + 32768; + + uint16_t bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536; + uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536; + bri8 += (255 - brightdepth); + + CRGB newcolor = CHSV( hue8, sat8, bri8); + + uint32_t color = getPixelColor(i); + fastled_col.red = (color >> 16 & 0xFF); + fastled_col.green = (color >> 8 & 0xFF); + fastled_col.blue = (color & 0xFF); + + nblend( fastled_col, newcolor, 64); + setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + } + SEGMENT_RUNTIME.counter_mode_step = sPseudotime; + SEGMENT_RUNTIME.aux_param = sHue16; + return 20; +} + + +// eight colored dots, weaving in and out of sync with each other uint16_t WS2812FX::mode_juggle(void){ fade_out((255-SEGMENT.intensity) / 32); CRGB fastled_col; byte dothue = 0; for ( byte i = 0; i < 8; i++) { - uint16_t index = SEGMENT.start + beatsin16(i + 7, 0, SEGMENT_LENGTH); + uint16_t index = SEGMENT.start + beatsin16(i + 7, 0, SEGMENT_LENGTH -1); uint32_t color = getPixelColor(index); fastled_col.red = (color >> 16 & 0xFF); fastled_col.green = (color >> 8 & 0xFF); @@ -1990,6 +2045,7 @@ uint16_t WS2812FX::mode_juggle(void){ return 10 + (uint16_t)(255 - SEGMENT.speed)/4; } + /* * FastLED palette modes helper function. Limitation: Due to memory reasons, multiple active segments with FastLED will disable the Palette transitions */ @@ -2081,7 +2137,7 @@ void WS2812FX::handle_palette(void) targetPalette = PartyColors_p; break; } - if (singleSegmentMode) //only blend if just one segment uses FastLED mode + if (singleSegmentMode && paletteFade) //only blend if just one segment uses FastLED mode { nblendPaletteTowardPalette(currentPalette, targetPalette, 42); } else @@ -2107,6 +2163,66 @@ uint16_t WS2812FX::mode_palette(void) } +// ColorWavesWithPalettes by Mark Kriegsman: https://gist.github.com/kriegsman/8281905786e8b2632aeb +// This function draws color waves with an ever-changing, +// widely-varying set of parameters, using a color palette. +uint16_t WS2812FX::mode_colorwaves(void) +{ + handle_palette(); + uint16_t duration = 10 + SEGMENT.speed; + uint16_t sPseudotime = SEGMENT_RUNTIME.counter_mode_step; + uint16_t sHue16 = SEGMENT_RUNTIME.aux_param; + + uint8_t brightdepth = beatsin88( 341, 96, 224); + uint16_t brightnessthetainc16 = beatsin88( 203, (25 * 256), (40 * 256)); + uint8_t msmultiplier = beatsin88(147, 23, 60); + + uint16_t hue16 = sHue16;//gHue * 256; + uint16_t hueinc16 = beatsin88(113, 300, 1500); + + sPseudotime += duration * msmultiplier; + sHue16 += duration * beatsin88( 400, 5, 9); + uint16_t brightnesstheta16 = sPseudotime; + CRGB fastled_col; + + for ( uint16_t i = SEGMENT.start ; i <= SEGMENT.stop; i++) { + hue16 += hueinc16; + uint8_t hue8 = hue16 / 256; + uint16_t h16_128 = hue16 >> 7; + if ( h16_128 & 0x100) { + hue8 = 255 - (h16_128 >> 1); + } else { + hue8 = h16_128 >> 1; + } + + brightnesstheta16 += brightnessthetainc16; + uint16_t b16 = sin16( brightnesstheta16 ) + 32768; + + uint16_t bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536; + uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536; + bri8 += (255 - brightdepth); + + uint8_t index = hue8; + //index = triwave8( index); + index = scale8( index, 240); + + CRGB newcolor = ColorFromPalette(currentPalette, index, bri8); + + uint32_t color = getPixelColor(i); + fastled_col.red = (color >> 16 & 0xFF); + fastled_col.green = (color >> 8 & 0xFF); + fastled_col.blue = (color & 0xFF); + + nblend(fastled_col, newcolor, 128); + setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + } + SEGMENT_RUNTIME.counter_mode_step = sPseudotime; + SEGMENT_RUNTIME.aux_param = sHue16; + return 20; +} + + +// colored stripes pulsing at a defined Beats-Per-Minute (BPM) uint16_t WS2812FX::mode_bpm(void) { handle_palette(); @@ -2137,23 +2253,22 @@ uint16_t WS2812FX::mode_fillnoise8(void) return 20; } - uint16_t WS2812FX::mode_noise16_1(void) { - uint16_t scale = 750; // the "zoom factor" for the noise + uint16_t scale = 320; // the "zoom factor" for the noise handle_palette(); CRGB fastled_col; SEGMENT_RUNTIME.counter_mode_step += (1 + SEGMENT.speed/16); for (int i = SEGMENT.start; i <= SEGMENT.stop; i++) { - uint16_t shift_x = beatsin8(5); // the x position of the noise field swings @ 17 bpm - uint16_t shift_y = SEGMENT_RUNTIME.counter_mode_step/50; // the y position becomes slowly incremented + uint16_t shift_x = beatsin8(11); // the x position of the noise field swings @ 17 bpm + uint16_t shift_y = SEGMENT_RUNTIME.counter_mode_step/42; // the y position becomes slowly incremented uint16_t real_x = (i + shift_x) * scale; // the x position of the noise field swings @ 17 bpm uint16_t real_y = (i + shift_y) * scale; // the y position becomes slowly incremented - uint32_t real_z = SEGMENT_RUNTIME.counter_mode_step*2; // the z position becomes quickly incremented + uint32_t real_z = SEGMENT_RUNTIME.counter_mode_step; // the z position becomes quickly incremented uint8_t noise = inoise16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down @@ -2169,21 +2284,21 @@ uint16_t WS2812FX::mode_noise16_1(void) uint16_t WS2812FX::mode_noise16_2(void) { - uint8_t scale = 750; // the "zoom factor" for the noise + uint16_t scale = 1000; // the "zoom factor" for the noise handle_palette(); CRGB fastled_col; SEGMENT_RUNTIME.counter_mode_step += (1 + SEGMENT.speed); for (int i = SEGMENT.start; i <= SEGMENT.stop; i++) { - uint16_t shift_x = SEGMENT_RUNTIME.counter_mode_step/64; // x as a function of time - uint16_t shift_y = 0; + uint16_t shift_x = SEGMENT_RUNTIME.counter_mode_step >> 6; // x as a function of time + uint16_t shift_y = SEGMENT_RUNTIME.counter_mode_step/42; uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field uint32_t real_y = (i + shift_y) * scale; // based on the precalculated positions uint32_t real_z = 4223; - uint8_t noise = inoise16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down + uint8_t noise = inoise16(real_x, 0, 4223) >> 8; // get the noise data and scale it down uint8_t index = sin8(noise * 3); // map led color based on noise data @@ -2197,7 +2312,7 @@ uint16_t WS2812FX::mode_noise16_2(void) uint16_t WS2812FX::mode_noise16_3(void) { - uint8_t scale = 750; // the "zoom factor" for the noise + uint16_t scale = 800; // the "zoom factor" for the noise handle_palette(); CRGB fastled_col; SEGMENT_RUNTIME.counter_mode_step += (1 + SEGMENT.speed); @@ -2209,9 +2324,9 @@ uint16_t WS2812FX::mode_noise16_3(void) uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field uint32_t real_y = (i + shift_y) * scale; // based on the precalculated positions - uint32_t real_z = SEGMENT_RUNTIME.counter_mode_step/16; + uint32_t real_z = SEGMENT_RUNTIME.counter_mode_step*8; - uint8_t noise = inoise16(real_x, real_y, real_z) >> 7; // get the noise data and scale it down + uint8_t noise = inoise16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down uint8_t index = sin8(noise * 3); // map led color based on noise data diff --git a/wled00/WS2812FX.h b/wled00/WS2812FX.h index 36f613fac..4c18e694e 100644 --- a/wled00/WS2812FX.h +++ b/wled00/WS2812FX.h @@ -82,7 +82,7 @@ #define REVERSE (uint8_t)0x80 #define IS_REVERSE ((SEGMENT.options & REVERSE) == REVERSE) -#define MODE_COUNT 72 +#define MODE_COUNT 74 #define FX_MODE_STATIC 0 #define FX_MODE_BLINK 1 @@ -147,15 +147,18 @@ #define FX_MODE_DUAL_LARSON_SCANNER 60 #define FX_MODE_RANDOM_CHASE 61 #define FX_MODE_OSCILLATE 62 +//Modes that use FastLED --> #define FX_MODE_FIRE_2012 63 -#define FX_MODE_JUGGLE 64 -#define FX_MODE_PALETTE 65 -#define FX_MODE_BPM 66 -#define FX_MODE_FILLNOISE8 67 -#define FX_MODE_NOISE16_1 68 -#define FX_MODE_NOISE16_2 69 -#define FX_MODE_NOISE16_3 70 -#define FX_MODE_NOISE16_4 71 +#define FX_MODE_PRIDE_2015 64 +#define FX_MODE_JUGGLE 65 +#define FX_MODE_PALETTE 66 +#define FX_MODE_COLORWAVES 67 +#define FX_MODE_BPM 68 +#define FX_MODE_FILLNOISE8 69 +#define FX_MODE_NOISE16_1 70 +#define FX_MODE_NOISE16_2 71 +#define FX_MODE_NOISE16_3 72 +#define FX_MODE_NOISE16_4 73 class WS2812FX { typedef uint16_t (WS2812FX::*mode_ptr)(void); @@ -248,9 +251,11 @@ class WS2812FX { _mode[FX_MODE_RANDOM_CHASE] = &WS2812FX::mode_random_chase; _mode[FX_MODE_OSCILLATE] = &WS2812FX::mode_oscillate; _mode[FX_MODE_FIRE_2012] = &WS2812FX::mode_fire_2012; + _mode[FX_MODE_PRIDE_2015] = &WS2812FX::mode_pride_2015; _mode[FX_MODE_BPM] = &WS2812FX::mode_bpm; _mode[FX_MODE_JUGGLE] = &WS2812FX::mode_juggle; _mode[FX_MODE_PALETTE] = &WS2812FX::mode_palette; + _mode[FX_MODE_COLORWAVES] = &WS2812FX::mode_colorwaves; _mode[FX_MODE_FILLNOISE8] = &WS2812FX::mode_fillnoise8; _mode[FX_MODE_NOISE16_1] = &WS2812FX::mode_noise16_1; _mode[FX_MODE_NOISE16_2] = &WS2812FX::mode_noise16_2; @@ -326,6 +331,8 @@ class WS2812FX { getPowerEstimate(uint16_t leds, uint32_t c, byte b), getSafePowerMultiplier(double safeMilliAmps, uint16_t leds, uint32_t c, byte b); + bool paletteFade; + WS2812FX::Segment getSegment(void); @@ -413,9 +420,11 @@ class WS2812FX { mode_random_chase(void), mode_oscillate(void), mode_fire_2012(void), + mode_pride_2015(void), mode_bpm(void), mode_juggle(void), mode_palette(void), + mode_colorwaves(void), mode_fillnoise8(void), mode_noise16_1(void), mode_noise16_2(void), diff --git a/wled00/data/index.htm b/wled00/data/index.htm index e3f3cb89e..b53bebc00 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -171,8 +171,8 @@ case 3: gId("path1").style.fill = aC; gId("path2").style.fill = aC; } tgb.style.fill=(Cf.SA.value>0)?aC:dC; - fpX.style.display=(Cf.TX.selectedIndex>64)?"block":"none"; - fof.style.fill=(Cf.TX.selectedIndex>64)?aC:dC; + fpX.style.display=(Cf.TX.selectedIndex>65)?"block":"none"; + fof.style.fill=(Cf.TX.selectedIndex>65)?aC:dC; fmr.style.fill=(Cf.TX.selectedIndex<1)?aC:dC; } function TgT() @@ -191,10 +191,10 @@ function SwFX(s) { var n=Cf.TX.selectedIndex+s; - if (n==-1||n==72) return; + if (n==-1||n==74) return; Cf.TX.selectedIndex =n; if (n < 0) Cf.TX.selectedIndex = 0; - if (n > 71) Cf.TX.selectedIndex = 65; + if (n > 73) Cf.TX.selectedIndex = 66; GX(); } function TgHSB() @@ -649,14 +649,16 @@ - - - - - - - - + + + + + + + + + +

Set secondary color to diff --git a/wled00/data/index_mobile.htm b/wled00/data/index_mobile.htm index 98b03a43d..b040e9571 100644 --- a/wled00/data/index_mobile.htm +++ b/wled00/data/index_mobile.htm @@ -7,7 +7,7 @@ - WLED 0.7.1 + WLED 0.8.0-a
Loading WLED UI...

WLED

FX 2nd Color

Sync Lights

White Channel

Master Brightness



 
 
 
 

 
 
 
 

Edit Presets

Presets

1
2
3
4

5
6
7
8

9
10
11
12

13
14
15
16

Cycle Mode

Cycle Range

  • Color
  • Effects
  • Both

Preset Duration

Color Transition

FX Speed

FX Intensity

  • Solid
  • Blink
  • Breathe
  • Wipe
  • Wipe Random
  • Random Colors
  • Easter
  • Multi Colors
  • Rainbow Full
  • Rainbow Cycle
  • Scan
  • Double Scan
  • Fade
  • Chase
  • Chase Rainbow
  • Running
  • Twinkle
  • Twinkle Random
  • Twinkle Fade
  • Twinkle Random Fade
  • Sparkle
  • Dark Sparkle
  • Dark Sparkle+
  • Strobe
  • Strobe Rainbow
  • Double Strobe
  • Blink Rainbow
  • Android
  • Dark Chase
  • Dark Chase Random
  • Dark Chase Rainbow
  • Chase Flash
  • Dark Chase Random
  • Rainbow Runner
  • Colorful
  • Traffic Light
  • Sweep Random
  • Running 2
  • Red & Blue
  • Running 2 Random
  • Scanner
  • Lighthouse
  • Fireworks
  • Fireworks Random
  • Merry Christmas
  • Fire Flicker
  • Gradient
  • Gradient Loading
  • In Out
  • In In
  • Out Out
  • Out In
  • Circus
  •  
  • Go to top
+ + + +WLED 0.8.0-a + )====="; //head1 (css) @@ -97,21 +119,108 @@ const char PAGE_index3[] PROGMEM = R"=====( -
-
-
-
-
-
-
-
-
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Effect Panel



- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Set secondary color to @@ -119,12 +228,30 @@ Set secondary color to -or
+or +

FastLED Palette

-
-
-
+ +
+
+
+
+
+
+
Favorite Presets

@@ -133,13 +260,21 @@ Favorite Presets




Click checkmark to apply brightness, color and effects.

Cycle through presets to , keep each for ms:

-
+
+
+
Timed Light



Gradually dim down
-1st slider sets duration (1-255min), 2nd sets target brightness.
-
-
-
- +1st slider sets duration (1-255min), 2nd sets target brightness. +
+
+
+
+
+
+ + + + )====="; diff --git a/wled00/htmls01.h b/wled00/htmls01.h index ab67d9db2..c5d08aa86 100644 --- a/wled00/htmls01.h +++ b/wled00/htmls01.h @@ -112,7 +112,8 @@ Brightness factor: %

Transitions

Fade:
Transition Time: ms
-Enable transition for secondary color:
+Enable transition for secondary color: +Enable Palette transitions:

Timed light

Default Duration: min
Default Target brightness:
diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 7a7342c90..8209d3e1a 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -39,7 +39,7 @@ #include "src/dependencies/e131/E131.h" //version in format yymmddb (b = daily build) -#define VERSION 1809062 +#define VERSION 1809081 char versionString[] = "0.8.0-a"; //AP and OTA default passwords (change them!) diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino index 8ad3e524f..357d46478 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled01_eeprom.ino @@ -121,7 +121,7 @@ void saveSettingsToEEPROM() EEPROM.write(371, whiteS); EEPROM.write(372, useRGBW); EEPROM.write(373, effectPaletteDefault); - + EEPROM.write(374, strip.paletteFade); EEPROM.write(375, apWaitTimeSecs); EEPROM.write(376, recoveryAPDisabled); EEPROM.write(377, EEPVER); //eeprom was updated to latest @@ -356,6 +356,7 @@ void loadSettingsFromEEPROM(bool first) whiteS = EEPROM.read(371); white = whiteS; useRGBW = EEPROM.read(372); effectPaletteDefault = EEPROM.read(373); effectPalette = effectPaletteDefault; + strip.paletteFade = EEPROM.read(374); if (lastEEPROMversion > 0) { apWaitTimeSecs = EEPROM.read(375); diff --git a/wled00/wled02_xml.ino b/wled00/wled02_xml.ino index 440aca3dc..d8b1a7e96 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/wled02_xml.ino @@ -192,6 +192,7 @@ void getSettingsJS(byte subPage) //get values for settings form in javascript sappend('c',"GC",useGammaCorrectionRGB); sappend('c',"TF",fadeTransition); sappend('v',"TD",transitionDelay); + sappend('c',"PF",strip.paletteFade); sappend('c',"T2",!disableSecTransition); sappend('v',"BF",briMultiplier); sappend('v',"TB",nightlightTargetBri); diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index 0dfcf063a..6a237b845 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -171,6 +171,7 @@ void handleSettingsSet(byte subPage) transitionDelay = i; } } + strip.paletteFade = server.hasArg("PF"); disableSecTransition = !server.hasArg("T2"); if (server.hasArg("TB")) { diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index 29e17354c..dfb778f61 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -5,7 +5,7 @@ void wledInit() { EEPROM.begin(EEPSIZE); - showWelcomePage = (EEPROM.read(233) != 233); + if (EEPROM.read(233) != 233) showWelcomePage = true; ledCount = ((EEPROM.read(229) << 0) & 0xFF) + ((EEPROM.read(398) << 8) & 0xFF00); if (ledCount > 1200 || ledCount == 0) ledCount = 10; //RMT eats up too much RAM #ifdef ARDUINO_ARCH_ESP32 From c55e3a37aef4b5b8537bb10fbca2cc96335be448 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sat, 8 Sep 2018 20:26:04 +0200 Subject: [PATCH 04/22] Added 33 gradient palette presets Fixed welcome page not showing on fresh install Fixed device not turning on on fresh install --- wled00/WS2812FX.cpp | 5 +- wled00/WS2812FX.h | 1 + wled00/data/settings_sec.htm | Bin 8446 -> 8878 bytes wled00/htmls01.h | 6 +- wled00/palettes.h | 588 +++++++++++++++++++++++++++++++++++ wled00/wled00.ino | 6 +- wled00/wled03_set.ino | 2 +- wled00/wled05_init.ino | 3 +- 8 files changed, 601 insertions(+), 10 deletions(-) create mode 100644 wled00/palettes.h diff --git a/wled00/WS2812FX.cpp b/wled00/WS2812FX.cpp index fa567421b..d94c78ffa 100644 --- a/wled00/WS2812FX.cpp +++ b/wled00/WS2812FX.cpp @@ -40,6 +40,7 @@ #include "WS2812FX.h" #include "FastLED.h" +#include "palettes.h"; void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst) { @@ -2133,8 +2134,8 @@ void WS2812FX::handle_palette(void) targetPalette = RainbowColors_p; break; case 11: //Rainbow stripe colors targetPalette = RainbowStripeColors_p; break; - default: //fallback for illegal values - targetPalette = PartyColors_p; break; + default: //progmem palettes + targetPalette = gGradientPalettes[constrain(SEGMENT.palette -12, 0, gGradientPaletteCount -1)]; } if (singleSegmentMode && paletteFade) //only blend if just one segment uses FastLED mode diff --git a/wled00/WS2812FX.h b/wled00/WS2812FX.h index 4c18e694e..7b5aa9253 100644 --- a/wled00/WS2812FX.h +++ b/wled00/WS2812FX.h @@ -271,6 +271,7 @@ class WS2812FX { _segments[0].speed = DEFAULT_SPEED; _reverseMode = false; _skipFirstMode = false; + paletteFade = true; _locked = NULL; _cronixieDigits = new byte[6]; bus = new NeoPixelWrapper(); diff --git a/wled00/data/settings_sec.htm b/wled00/data/settings_sec.htm index 9ae3b3a4552600fb46c9e108bcf8a3e2ab407bda..baf366a63dd2544ef408d645575aeb91ecf5987e 100644 GIT binary patch delta 234 zcmez8xXyJ$kr<~1gC2tcgDykj %

Transitions

Fade:
Transition Time: ms
-Enable transition for secondary color: -Enable Palette transitions:
+Enable transition for secondary color:
+Enable Palette transitions:

Timed light

Default Duration: min
Default Target brightness:
@@ -366,6 +366,8 @@ Thank you so much!

Licensed under the MIT license

Uses libraries:
ESP8266/ESP32 Arduino Core
+NeoPixelBus by Makuna (svenihoney fork)
+FastLED library
(ESP32) WebServer_tng by bbx10
WS2812FX by kitesurfer1404 (modified)
Timezone library by JChristensen
diff --git a/wled00/palettes.h b/wled00/palettes.h new file mode 100644 index 000000000..4d31f1660 --- /dev/null +++ b/wled00/palettes.h @@ -0,0 +1,588 @@ +// From ColorWavesWithPalettes by Mark Kriegsman: https://gist.github.com/kriegsman/8281905786e8b2632aeb + +// Gradient palette "ib_jul01_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/ing/xmas/tn/ib_jul01.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 16 bytes of program space. + +#ifndef PalettesWLED_h +#define PalettesWLED_h + +DEFINE_GRADIENT_PALETTE( ib_jul01_gp ) { + 0, 194, 1, 1, + 94, 1, 29, 18, + 132, 57,131, 28, + 255, 113, 1, 1}; + +// Gradient palette "es_vintage_57_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/vintage/tn/es_vintage_57.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 20 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_vintage_57_gp ) { + 0, 2, 1, 1, + 53, 18, 1, 0, + 104, 69, 29, 1, + 153, 167,135, 10, + 255, 46, 56, 4}; + +// Gradient palette "es_vintage_01_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/vintage/tn/es_vintage_01.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 32 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_vintage_01_gp ) { + 0, 4, 1, 1, + 51, 16, 0, 1, + 76, 97,104, 3, + 101, 255,131, 19, + 127, 67, 9, 4, + 153, 16, 0, 1, + 229, 4, 1, 1, + 255, 4, 1, 1}; + +// Gradient palette "es_rivendell_15_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/rivendell/tn/es_rivendell_15.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 20 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_rivendell_15_gp ) { + 0, 1, 14, 5, + 101, 16, 36, 14, + 165, 56, 68, 30, + 242, 150,156, 99, + 255, 150,156, 99}; + +// Gradient palette "rgi_15_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/ds/rgi/tn/rgi_15.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 36 bytes of program space. + +DEFINE_GRADIENT_PALETTE( rgi_15_gp ) { + 0, 4, 1, 31, + 31, 55, 1, 16, + 63, 197, 3, 7, + 95, 59, 2, 17, + 127, 6, 2, 34, + 159, 39, 6, 33, + 191, 112, 13, 32, + 223, 56, 9, 35, + 255, 22, 6, 38}; + +// Gradient palette "retro2_16_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/ma/retro2/tn/retro2_16.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 8 bytes of program space. + +DEFINE_GRADIENT_PALETTE( retro2_16_gp ) { + 0, 188,135, 1, + 255, 46, 7, 1}; + +// Gradient palette "Analogous_1_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/red/tn/Analogous_1.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 20 bytes of program space. + +DEFINE_GRADIENT_PALETTE( Analogous_1_gp ) { + 0, 3, 0,255, + 63, 23, 0,255, + 127, 67, 0,255, + 191, 142, 0, 45, + 255, 255, 0, 0}; + +// Gradient palette "es_pinksplash_08_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/pink_splash/tn/es_pinksplash_08.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 20 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_pinksplash_08_gp ) { + 0, 126, 11,255, + 127, 197, 1, 22, + 175, 210,157,172, + 221, 157, 3,112, + 255, 157, 3,112}; + +// Gradient palette "es_pinksplash_07_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/pink_splash/tn/es_pinksplash_07.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 28 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_pinksplash_07_gp ) { + 0, 229, 1, 1, + 61, 242, 4, 63, + 101, 255, 12,255, + 127, 249, 81,252, + 153, 255, 11,235, + 193, 244, 5, 68, + 255, 232, 1, 5}; + +// Gradient palette "Coral_reef_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/other/tn/Coral_reef.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 24 bytes of program space. + +DEFINE_GRADIENT_PALETTE( Coral_reef_gp ) { + 0, 40,199,197, + 50, 10,152,155, + 96, 1,111,120, + 96, 43,127,162, + 139, 10, 73,111, + 255, 1, 34, 71}; + +// Gradient palette "es_ocean_breeze_068_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/ocean_breeze/tn/es_ocean_breeze_068.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 24 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_ocean_breeze_068_gp ) { + 0, 100,156,153, + 51, 1, 99,137, + 101, 1, 68, 84, + 104, 35,142,168, + 178, 0, 63,117, + 255, 1, 10, 10}; + +// Gradient palette "es_ocean_breeze_036_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/ocean_breeze/tn/es_ocean_breeze_036.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 16 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_ocean_breeze_036_gp ) { + 0, 1, 6, 7, + 89, 1, 99,111, + 153, 144,209,255, + 255, 0, 73, 82}; + +// Gradient palette "departure_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/mjf/tn/departure.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 88 bytes of program space. + +DEFINE_GRADIENT_PALETTE( departure_gp ) { + 0, 8, 3, 0, + 42, 23, 7, 0, + 63, 75, 38, 6, + 84, 169, 99, 38, + 106, 213,169,119, + 116, 255,255,255, + 138, 135,255,138, + 148, 22,255, 24, + 170, 0,255, 0, + 191, 0,136, 0, + 212, 0, 55, 0, + 255, 0, 55, 0}; + +// Gradient palette "es_landscape_64_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/landscape/tn/es_landscape_64.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 36 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_landscape_64_gp ) { + 0, 0, 0, 0, + 37, 2, 25, 1, + 76, 15,115, 5, + 127, 79,213, 1, + 128, 126,211, 47, + 130, 188,209,247, + 153, 144,182,205, + 204, 59,117,250, + 255, 1, 37,192}; + +// Gradient palette "es_landscape_33_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/landscape/tn/es_landscape_33.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 24 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_landscape_33_gp ) { + 0, 1, 5, 0, + 19, 32, 23, 1, + 38, 161, 55, 1, + 63, 229,144, 1, + 66, 39,142, 74, + 255, 1, 4, 1}; + +// Gradient palette "rainbowsherbet_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/ma/icecream/tn/rainbowsherbet.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 28 bytes of program space. + +DEFINE_GRADIENT_PALETTE( rainbowsherbet_gp ) { + 0, 255, 33, 4, + 43, 255, 68, 25, + 86, 255, 7, 25, + 127, 255, 82,103, + 170, 255,255,242, + 209, 42,255, 22, + 255, 87,255, 65}; + +// Gradient palette "gr65_hult_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/hult/tn/gr65_hult.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 24 bytes of program space. + +DEFINE_GRADIENT_PALETTE( gr65_hult_gp ) { + 0, 247,176,247, + 48, 255,136,255, + 89, 220, 29,226, + 160, 7, 82,178, + 216, 1,124,109, + 255, 1,124,109}; + +// Gradient palette "gr64_hult_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/hult/tn/gr64_hult.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 32 bytes of program space. + +DEFINE_GRADIENT_PALETTE( gr64_hult_gp ) { + 0, 1,124,109, + 66, 1, 93, 79, + 104, 52, 65, 1, + 130, 115,127, 1, + 150, 52, 65, 1, + 201, 1, 86, 72, + 239, 0, 55, 45, + 255, 0, 55, 45}; + +// Gradient palette "GMT_drywet_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/gmt/tn/GMT_drywet.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 28 bytes of program space. + +DEFINE_GRADIENT_PALETTE( GMT_drywet_gp ) { + 0, 47, 30, 2, + 42, 213,147, 24, + 84, 103,219, 52, + 127, 3,219,207, + 170, 1, 48,214, + 212, 1, 1,111, + 255, 1, 7, 33}; + +// Gradient palette "ib15_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/ing/general/tn/ib15.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 24 bytes of program space. + +DEFINE_GRADIENT_PALETTE( ib15_gp ) { + 0, 113, 91,147, + 72, 157, 88, 78, + 89, 208, 85, 33, + 107, 255, 29, 11, + 141, 137, 31, 39, + 255, 59, 33, 89}; + +// Gradient palette "Fuschia_7_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/ds/fuschia/tn/Fuschia-7.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 20 bytes of program space. + +DEFINE_GRADIENT_PALETTE( Fuschia_7_gp ) { + 0, 43, 3,153, + 63, 100, 4,103, + 127, 188, 5, 66, + 191, 161, 11,115, + 255, 135, 20,182}; + +// Gradient palette "es_emerald_dragon_08_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/emerald_dragon/tn/es_emerald_dragon_08.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 16 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_emerald_dragon_08_gp ) { + 0, 97,255, 1, + 101, 47,133, 1, + 178, 13, 43, 1, + 255, 2, 10, 1}; + +// Gradient palette "lava_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/neota/elem/tn/lava.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 52 bytes of program space. + +DEFINE_GRADIENT_PALETTE( lava_gp ) { + 0, 0, 0, 0, + 46, 18, 0, 0, + 96, 113, 0, 0, + 108, 142, 3, 1, + 119, 175, 17, 1, + 146, 213, 44, 2, + 174, 255, 82, 4, + 188, 255,115, 4, + 202, 255,156, 4, + 218, 255,203, 4, + 234, 255,255, 4, + 244, 255,255, 71, + 255, 255,255,255}; + +// Gradient palette "fire_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/neota/elem/tn/fire.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 28 bytes of program space. + +DEFINE_GRADIENT_PALETTE( fire_gp ) { + 0, 1, 1, 0, + 76, 32, 5, 0, + 146, 192, 24, 0, + 197, 220,105, 5, + 240, 252,255, 31, + 250, 252,255,111, + 255, 255,255,255}; + +// Gradient palette "Colorfull_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/atmospheric/tn/Colorfull.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 44 bytes of program space. + +DEFINE_GRADIENT_PALETTE( Colorfull_gp ) { + 0, 10, 85, 5, + 25, 29,109, 18, + 60, 59,138, 42, + 93, 83, 99, 52, + 106, 110, 66, 64, + 109, 123, 49, 65, + 113, 139, 35, 66, + 116, 192,117, 98, + 124, 255,255,137, + 168, 100,180,155, + 255, 22,121,174}; + +// Gradient palette "Magenta_Evening_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/atmospheric/tn/Magenta_Evening.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 28 bytes of program space. + +DEFINE_GRADIENT_PALETTE( Magenta_Evening_gp ) { + 0, 71, 27, 39, + 31, 130, 11, 51, + 63, 213, 2, 64, + 70, 232, 1, 66, + 76, 252, 1, 69, + 108, 123, 2, 51, + 255, 46, 9, 35}; + +// Gradient palette "Pink_Purple_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/atmospheric/tn/Pink_Purple.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 44 bytes of program space. + +DEFINE_GRADIENT_PALETTE( Pink_Purple_gp ) { + 0, 19, 2, 39, + 25, 26, 4, 45, + 51, 33, 6, 52, + 76, 68, 62,125, + 102, 118,187,240, + 109, 163,215,247, + 114, 217,244,255, + 122, 159,149,221, + 149, 113, 78,188, + 183, 128, 57,155, + 255, 146, 40,123}; + +// Gradient palette "Sunset_Real_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/atmospheric/tn/Sunset_Real.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 28 bytes of program space. + +DEFINE_GRADIENT_PALETTE( Sunset_Real_gp ) { + 0, 120, 0, 0, + 22, 179, 22, 0, + 51, 255,104, 0, + 85, 167, 22, 18, + 135, 100, 0,103, + 198, 16, 0,130, + 255, 0, 0,160}; + +// Gradient palette "es_autumn_19_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/autumn/tn/es_autumn_19.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 52 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_autumn_19_gp ) { + 0, 26, 1, 1, + 51, 67, 4, 1, + 84, 118, 14, 1, + 104, 137,152, 52, + 112, 113, 65, 1, + 122, 133,149, 59, + 124, 137,152, 52, + 135, 113, 65, 1, + 142, 139,154, 46, + 163, 113, 13, 1, + 204, 55, 3, 1, + 249, 17, 1, 1, + 255, 17, 1, 1}; + +// Gradient palette "BlacK_Blue_Magenta_White_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/BlacK_Blue_Magenta_White.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 28 bytes of program space. + +DEFINE_GRADIENT_PALETTE( BlacK_Blue_Magenta_White_gp ) { + 0, 0, 0, 0, + 42, 0, 0, 45, + 84, 0, 0,255, + 127, 42, 0,255, + 170, 255, 0,255, + 212, 255, 55,255, + 255, 255,255,255}; + +// Gradient palette "BlacK_Magenta_Red_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/BlacK_Magenta_Red.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 20 bytes of program space. + +DEFINE_GRADIENT_PALETTE( BlacK_Magenta_Red_gp ) { + 0, 0, 0, 0, + 63, 42, 0, 45, + 127, 255, 0,255, + 191, 255, 0, 45, + 255, 255, 0, 0}; + +// Gradient palette "BlacK_Red_Magenta_Yellow_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/BlacK_Red_Magenta_Yellow.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 28 bytes of program space. + +DEFINE_GRADIENT_PALETTE( BlacK_Red_Magenta_Yellow_gp ) { + 0, 0, 0, 0, + 42, 42, 0, 0, + 84, 255, 0, 0, + 127, 255, 0, 45, + 170, 255, 0,255, + 212, 255, 55, 45, + 255, 255,255, 0}; + +// Gradient palette "Blue_Cyan_Yellow_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/Blue_Cyan_Yellow.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 20 bytes of program space. + +DEFINE_GRADIENT_PALETTE( Blue_Cyan_Yellow_gp ) { + 0, 0, 0,255, + 63, 0, 55,255, + 127, 0,255,255, + 191, 42,255, 45, + 255, 255,255, 0}; + + + + + + + + +// Gradient palette "BlacK_Blue_Cyan_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/BlacK_Blue_Cyan.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 20 bytes of program space. + +DEFINE_GRADIENT_PALETTE( BlacK_Blue_Cyan_gp ) { + 0, 0, 0, 0, + 63, 0, 0, 45, + 127, 0, 0,255, + 191, 0, 55,255, + 255, 0,255,255}; + + +// Gradient palette "BlacK_Blue_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/BlacK_Blue.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 12 bytes of program space. + +DEFINE_GRADIENT_PALETTE( BlacK_Blue_gp ) { + 0, 0, 0, 0, + 127, 0, 0, 45, + 255, 0, 0,255}; + + +// Gradient palette "Red_Black_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/Red_Black.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 12 bytes of program space. + +DEFINE_GRADIENT_PALETTE( Red_Black_gp ) { + 0, 255, 0, 0, + 127, 42, 0, 0, + 255, 0, 0, 0}; + + +// Gradient palette "Yellow_Black_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/Yellow_Black.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 12 bytes of program space. + +DEFINE_GRADIENT_PALETTE( Yellow_Black_gp ) { + 0, 255,255, 0, + 127, 42, 55, 0, + 255, 0, 0, 0}; + + +// Gradient palette "bhw1_hello_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/bhw/bhw1/tn/bhw1_hello.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 32 bytes of program space. + +DEFINE_GRADIENT_PALETTE( bhw1_hello_gp ) { + 0, 237,156,197, + 35, 244,189,230, + 56, 255,255,255, + 79, 244,189,230, + 109, 237,156,197, + 160, 121,255,255, + 196, 255,255,255, + 255, 121,255,255}; + + + +// Single array of defined cpt-city color palettes. +// This will let us programmatically choose one based on +// a number, rather than having to activate each explicitly +// by name every time. +// Since it is const, this array could also be moved +// into PROGMEM to save SRAM, but for simplicity of illustration +// we'll keep it in a regular SRAM array. +// +// This list of color palettes acts as a "playlist"; you can +// add or delete, or re-arrange as you wish. +const TProgmemRGBGradientPalettePtr gGradientPalettes[] = { + Sunset_Real_gp, //12 + es_rivendell_15_gp, //13 + es_ocean_breeze_036_gp, //14 + rgi_15_gp, //15 + retro2_16_gp, //16 + Analogous_1_gp, //17 + es_pinksplash_08_gp, //18 + Coral_reef_gp, //19 + es_ocean_breeze_068_gp, //20 + es_pinksplash_07_gp, //21 + es_vintage_01_gp, //22 + departure_gp, //23 + es_landscape_64_gp, //24 + es_landscape_33_gp, //25 + rainbowsherbet_gp, //26 + gr65_hult_gp, //27 + gr64_hult_gp, //28 + GMT_drywet_gp, //29 + ib_jul01_gp, //30 + es_vintage_57_gp, //31 + ib15_gp, //32 + Fuschia_7_gp, //33 + es_emerald_dragon_08_gp, //34 + lava_gp, //35 + fire_gp, //36 + Colorfull_gp, //37 + Magenta_Evening_gp, //38 + Pink_Purple_gp, //39 + es_autumn_19_gp, //40 + BlacK_Blue_Magenta_White_gp, //41 + BlacK_Magenta_Red_gp, //42 + BlacK_Red_Magenta_Yellow_gp, //43 + Blue_Cyan_Yellow_gp, //44 + }; + + +// Count of how many cpt-city gradients are defined: +const uint8_t gGradientPaletteCount = + sizeof( gGradientPalettes) / sizeof( TProgmemRGBGradientPalettePtr ); + +#endif + diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 8209d3e1a..3b1b560c5 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -39,7 +39,7 @@ #include "src/dependencies/e131/E131.h" //version in format yymmddb (b = daily build) -#define VERSION 1809081 +#define VERSION 1809084 char versionString[] = "0.8.0-a"; //AP and OTA default passwords (change them!) @@ -129,7 +129,7 @@ bool hueApplyOnOff = true, hueApplyBri = true, hueApplyColor = true; uint16_t userVar0 = 0, userVar1 = 0; //Internal vars -byte col[]{0, 0, 0}; +byte col[]{255, 159, 0}; byte colOld[]{0, 0, 0}; byte colT[]{0, 0, 0}; byte colIT[]{0, 0, 0}; @@ -144,7 +144,7 @@ uint16_t transitionDelayTemp = transitionDelay; unsigned long transitionStartTime; unsigned long nightlightStartTime; float tperLast = 0; -byte bri = 0; +byte bri = 127; byte briOld = 0; byte briT = 0; byte briIT = 0; diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index 6a237b845..7bde55abe 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -333,7 +333,7 @@ void handleSettingsSet(byte subPage) //SECURITY if (subPage == 6) { - if (server.hasArg("RS")) + if (server.hasArg("RS")) //complete factory reset { clearEEPROM(); serveMessage(200, "All Settings erased.", "Connect to WLED-AP to setup again...",255); diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index dfb778f61..58bd73ee0 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -5,7 +5,6 @@ void wledInit() { EEPROM.begin(EEPSIZE); - if (EEPROM.read(233) != 233) showWelcomePage = true; ledCount = ((EEPROM.read(229) << 0) & 0xFF) + ((EEPROM.read(398) << 8) & 0xFF00); if (ledCount > 1200 || ledCount == 0) ledCount = 10; //RMT eats up too much RAM #ifdef ARDUINO_ARCH_ESP32 @@ -27,6 +26,7 @@ void wledInit() DEBUG_PRINT(clientSSID); buildCssColorString(); userBeginPreConnection(); + if (strcmp(clientSSID,"Your_Network") == 0) showWelcomePage = true; initCon(); @@ -398,7 +398,6 @@ void serveIndexOrWelcome() serveIndex(); }else{ serveSettings(255); - showWelcomePage = false; } } From 0505baff38529e86a04f9188b4a8a47520463450 Mon Sep 17 00:00:00 2001 From: Aircoookie Date: Sat, 8 Sep 2018 20:34:59 +0200 Subject: [PATCH 05/22] Resolve version code merge conflict --- wled00/wled00.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 2ca913990..aa707fdd6 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -39,8 +39,8 @@ #include "src/dependencies/e131/E131.h" //version in format yymmddb (b = daily build) -#define VERSION 1808311 -char versionString[] = "0.7.1"; +#define VERSION 1809084 +char versionString[] = "0.8.0-a"; //AP and OTA default passwords (change them!) char apPass[65] = "wled1234"; From 4b316101695108ede7190c7dff81545b733486ab Mon Sep 17 00:00:00 2001 From: cschwinne Date: Tue, 11 Sep 2018 00:20:12 +0200 Subject: [PATCH 06/22] Updated UI with all new palettes FastLED effects now have default palettes per effect Fire2012 can now use Palettes Option for palette blending added Added new Palettes --- wled00/WS2812FX.cpp | 202 ++++++++++---------- wled00/WS2812FX.h | 15 +- wled00/data/index.htm | 72 ++++++-- wled00/data/index_mobile.htm | 66 +++++-- wled00/data/settings_leds.htm | Bin 8646 -> 9734 bytes wled00/htmls00.h | 164 +++-------------- wled00/htmls01.h | 18 +- wled00/palettes.h | 336 +++++++++++++++------------------- wled00/wled00.ino | 2 +- wled00/wled01_eeprom.ino | 13 +- wled00/wled02_xml.ino | 1 + wled00/wled03_set.ino | 7 + 12 files changed, 424 insertions(+), 472 deletions(-) diff --git a/wled00/WS2812FX.cpp b/wled00/WS2812FX.cpp index d94c78ffa..d357fe822 100644 --- a/wled00/WS2812FX.cpp +++ b/wled00/WS2812FX.cpp @@ -1918,67 +1918,6 @@ uint16_t WS2812FX::mode_lightning(void) } -// WLED limitation: Analog Clock overlay will NOT work when Fire2012 is active -// Fire2012 by Mark Kriegsman, July 2012 -// as part of "Five Elements" shown here: http://youtu.be/knWiGsmgycY -//// -// This basic one-dimensional 'fire' simulation works roughly as follows: -// There's a underlying array of 'heat' cells, that model the temperature -// at each point along the line. Every cycle through the simulation, -// four steps are performed: -// 1) All cells cool down a little bit, losing heat to the air -// 2) The heat from each cell drifts 'up' and diffuses a little -// 3) Sometimes randomly new 'sparks' of heat are added at the bottom -// 4) The heat from each cell is rendered as a color into the leds array -// The heat-to-color mapping uses a black-body radiation approximation. -// -// Temperature is in arbitrary units from 0 (cold black) to 255 (white hot). -// -// This simulation scales it self a bit depending on NUM_LEDS; it should look -// "OK" on anywhere from 20 to 100 LEDs without too much tweaking. -// -// I recommend running this simulation at anywhere from 30-100 frames per second, -// meaning an interframe delay of about 10-35 milliseconds. -// -// Looks best on a high-density LED setup (60+ pixels/meter). -// -// -// There are two main parameters you can play with to control the look and -// feel of your fire: COOLING (used in step 1 above), and SPARKING (used -// in step 3 above) (Effect Intensity = Sparking). -// -// COOLING: How much does the air cool as it rises? -// Less cooling = taller flames. More cooling = shorter flames. -// Default 50, suggested range 20-100 -#define COOLING 75 - -uint16_t WS2812FX::mode_fire_2012(void) -{ - // Step 1. Cool down every cell a little - for( int i = SEGMENT.start; i <= SEGMENT.stop; i++) { - _locked[i] = qsub8(_locked[i], random8(0, ((COOLING * 10) / SEGMENT_LENGTH) + 2)); - } - - // Step 2. Heat from each cell drifts 'up' and diffuses a little - for( int k= SEGMENT.stop; k >= SEGMENT.start + 2; k--) { - _locked[k] = (_locked[k - 1] + _locked[k - 2] + _locked[k - 2] ) / 3; - } - - // Step 3. Randomly ignite new 'sparks' of heat near the bottom - if( random8() <= SEGMENT.intensity ) { - int y = SEGMENT.start + random8(7); - if (y <= SEGMENT.stop) _locked[y] = qadd8(_locked[y], random8(160,255) ); - } - - // Step 4. Map from heat cells to LED colors - for( int j = SEGMENT.start; j <= SEGMENT.stop; j++) { - CRGB color = HeatColor(_locked[j]); - setPixelColor(j, color.red, color.green, color.blue); - } - return 10 + (uint16_t)(255 - SEGMENT.speed)/6; -} - - // Pride2015 // Animated, ever-changing rainbows. // by Mark Kriegsman: https://gist.github.com/kriegsman/964de772d64c502760e5 @@ -2060,7 +1999,21 @@ void WS2812FX::handle_palette(void) switch (SEGMENT.palette) { - case 0: {//periodically replace palette with a random one. Doesn't work with multiple FastLED segments + case 0: {//default palette. Differs depending on effect + switch (SEGMENT.mode) + { + case FX_MODE_FIRE_2012 : targetPalette = gGradientPalettes[22]; break;//heat palette + case FX_MODE_COLORWAVES : targetPalette = gGradientPalettes[13]; break;//landscape 33 + case FX_MODE_FILLNOISE8 : targetPalette = OceanColors_p; break; + case FX_MODE_NOISE16_1 : targetPalette = gGradientPalettes[17]; break;//Drywet + case FX_MODE_NOISE16_2 : targetPalette = gGradientPalettes[30]; break;//Blue cyan yellow + case FX_MODE_NOISE16_3 : targetPalette = gGradientPalettes[22]; break;//heat palette + case FX_MODE_NOISE16_4 : targetPalette = gGradientPalettes[13]; break;//landscape 33 + + default: targetPalette = PartyColors_p; break;//palette, bpm + } + break;} + case 1: {//periodically replace palette with a random one. Doesn't work with multiple FastLED segments if (!singleSegmentMode) { targetPalette = PartyColors_p; break; //fallback @@ -2074,13 +2027,13 @@ void WS2812FX::handle_palette(void) CHSV(random8(), 255, random8(128, 255))); _lastPaletteChange = millis(); } break;} - case 1: {//primary color only + case 2: {//primary color only CRGB prim; prim.red = (SEGMENT.colors[0] >> 16 & 0xFF); prim.green = (SEGMENT.colors[0] >> 8 & 0xFF); prim.blue = (SEGMENT.colors[0] & 0xFF); targetPalette = CRGBPalette16(prim); break;} - case 2: {//based on primary + case 3: {//based on primary //considering performance implications CRGB prim; prim.red = (SEGMENT.colors[0] >> 16 & 0xFF); @@ -2090,10 +2043,10 @@ void WS2812FX::handle_palette(void) targetPalette = CRGBPalette16( CHSV(prim_hsv.h, prim_hsv.s, prim_hsv.v), //color itself CHSV(prim_hsv.h, max(prim_hsv.s - 50,0), prim_hsv.v), //less saturated - CHSV(prim_hsv.h, prim_hsv.s, max(prim_hsv.h - 50,0)), //darker + CHSV(prim_hsv.h, prim_hsv.s, max(prim_hsv.v - 50,0)), //darker CHSV(prim_hsv.h, prim_hsv.s, prim_hsv.v)); //color itself break;} - case 3: {//primary + secondary + case 4: {//primary + secondary CRGB prim; prim.red = (SEGMENT.colors[0] >> 16 & 0xFF); prim.green = (SEGMENT.colors[0] >> 8 & 0xFF); @@ -2102,8 +2055,8 @@ void WS2812FX::handle_palette(void) sec.red = (SEGMENT.colors[1] >> 16 & 0xFF); sec.green = (SEGMENT.colors[1] >> 8 & 0xFF); sec.blue = (SEGMENT.colors[1] & 0xFF); - targetPalette = CRGBPalette16(prim,sec,prim); break;} - case 4: {//based on primary + secondary + targetPalette = CRGBPalette16(sec,prim); break;} + case 5: {//based on primary + secondary CRGB prim; prim.red = (SEGMENT.colors[0] >> 16 & 0xFF); prim.green = (SEGMENT.colors[0] >> 8 & 0xFF); @@ -2112,35 +2065,28 @@ void WS2812FX::handle_palette(void) sec.red = (SEGMENT.colors[1] >> 16 & 0xFF); sec.green = (SEGMENT.colors[1] >> 8 & 0xFF); sec.blue = (SEGMENT.colors[1] & 0xFF); - CHSV prim_hsv = rgb2hsv_approximate(prim); - CHSV sec_hsv = rgb2hsv_approximate(sec ); - targetPalette = CRGBPalette16( - CHSV(prim_hsv.h, prim_hsv.s, prim_hsv.v), //color itself - CHSV(prim_hsv.h, max(prim_hsv.s - 50,0), prim_hsv.v), //less saturated - CHSV(sec_hsv.h, sec_hsv.s, max(sec_hsv.v - 50,0)), //darker - CHSV(sec_hsv.h, sec_hsv.s, sec_hsv.v)); //color itself - break;} - case 5: //Party colors + targetPalette = CRGBPalette16(sec,prim,CRGB::White); break;} + case 6: //Party colors targetPalette = PartyColors_p; break; - case 6: //Cloud colors + case 7: //Cloud colors targetPalette = CloudColors_p; break; - case 7: //Lava colors + case 8: //Lava colors targetPalette = LavaColors_p; break; - case 8: //Ocean colors + case 9: //Ocean colors targetPalette = OceanColors_p; break; - case 9: //Forest colors + case 10: //Forest colors targetPalette = ForestColors_p; break; - case 10: //Rainbow colors + case 11: //Rainbow colors targetPalette = RainbowColors_p; break; - case 11: //Rainbow stripe colors + case 12: //Rainbow stripe colors targetPalette = RainbowStripeColors_p; break; default: //progmem palettes - targetPalette = gGradientPalettes[constrain(SEGMENT.palette -12, 0, gGradientPaletteCount -1)]; + targetPalette = gGradientPalettes[constrain(SEGMENT.palette -13, 0, gGradientPaletteCount -1)]; } if (singleSegmentMode && paletteFade) //only blend if just one segment uses FastLED mode { - nblendPaletteTowardPalette(currentPalette, targetPalette, 42); + nblendPaletteTowardPalette(currentPalette, targetPalette, 48); } else { currentPalette = targetPalette; @@ -2152,18 +2098,84 @@ uint16_t WS2812FX::mode_palette(void) { handle_palette(); CRGB fastled_col; + bool noWrap = (paletteBlend == 2 || (paletteBlend == 0 && SEGMENT.speed == 0)); for (uint16_t i = SEGMENT.start; i <= SEGMENT.stop; i++) { - uint8_t colorIndex = map(i,SEGMENT.start,SEGMENT.stop,0,255) + (SEGMENT_RUNTIME.counter_mode_step >> 6 & 0xFF); - fastled_col = ColorFromPalette( currentPalette, colorIndex, 255, LINEARBLEND); + uint8_t colorIndex = map(i,SEGMENT.start,SEGMENT.stop,0,255) - (SEGMENT_RUNTIME.counter_mode_step >> 6 & 0xFF); + + if (noWrap) colorIndex = map(colorIndex, 0, 255, 0, 240); //cut off blend at palette "end" + + fastled_col = ColorFromPalette( currentPalette, colorIndex, 255, (paletteBlend == 3)? NOBLEND:LINEARBLEND); setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); } - SEGMENT_RUNTIME.counter_mode_step += SEGMENT.speed; + SEGMENT_RUNTIME.counter_mode_step += SEGMENT.speed *2; if (SEGMENT.speed == 0) SEGMENT_RUNTIME.counter_mode_step = 0; return 20; } +// WLED limitation: Analog Clock overlay will NOT work when Fire2012 is active +// Fire2012 by Mark Kriegsman, July 2012 +// as part of "Five Elements" shown here: http://youtu.be/knWiGsmgycY +//// +// This basic one-dimensional 'fire' simulation works roughly as follows: +// There's a underlying array of 'heat' cells, that model the temperature +// at each point along the line. Every cycle through the simulation, +// four steps are performed: +// 1) All cells cool down a little bit, losing heat to the air +// 2) The heat from each cell drifts 'up' and diffuses a little +// 3) Sometimes randomly new 'sparks' of heat are added at the bottom +// 4) The heat from each cell is rendered as a color into the leds array +// The heat-to-color mapping uses a black-body radiation approximation. +// +// Temperature is in arbitrary units from 0 (cold black) to 255 (white hot). +// +// This simulation scales it self a bit depending on NUM_LEDS; it should look +// "OK" on anywhere from 20 to 100 LEDs without too much tweaking. +// +// I recommend running this simulation at anywhere from 30-100 frames per second, +// meaning an interframe delay of about 10-35 milliseconds. +// +// Looks best on a high-density LED setup (60+ pixels/meter). +// +// +// There are two main parameters you can play with to control the look and +// feel of your fire: COOLING (used in step 1 above), and SPARKING (used +// in step 3 above) (Effect Intensity = Sparking). +// +// COOLING: How much does the air cool as it rises? +// Less cooling = taller flames. More cooling = shorter flames. +// Default 50, suggested range 20-100 +#define COOLING 75 + +uint16_t WS2812FX::mode_fire_2012(void) +{ + handle_palette(); + // Step 1. Cool down every cell a little + for( int i = SEGMENT.start; i <= SEGMENT.stop; i++) { + _locked[i] = qsub8(_locked[i], random8(0, ((COOLING * 10) / SEGMENT_LENGTH) + 2)); + } + + // Step 2. Heat from each cell drifts 'up' and diffuses a little + for( int k= SEGMENT.stop; k >= SEGMENT.start + 2; k--) { + _locked[k] = (_locked[k - 1] + _locked[k - 2] + _locked[k - 2] ) / 3; + } + + // Step 3. Randomly ignite new 'sparks' of heat near the bottom + if( random8() <= SEGMENT.intensity ) { + int y = SEGMENT.start + random8(7); + if (y <= SEGMENT.stop) _locked[y] = qadd8(_locked[y], random8(160,255) ); + } + + // Step 4. Map from heat cells to LED colors + for( int j = SEGMENT.start; j <= SEGMENT.stop; j++) { + CRGB color = ColorFromPalette( currentPalette, min(_locked[j],240), 255, LINEARBLEND); + setPixelColor(j, color.red, color.green, color.blue); + } + return 10 + (uint16_t)(255 - SEGMENT.speed)/6; +} + + // ColorWavesWithPalettes by Mark Kriegsman: https://gist.github.com/kriegsman/8281905786e8b2632aeb // This function draws color waves with an ever-changing, // widely-varying set of parameters, using a color palette. @@ -2229,7 +2241,7 @@ uint16_t WS2812FX::mode_bpm(void) handle_palette(); CRGB fastled_col; uint8_t beat = beatsin8(SEGMENT.speed, 64, 255); - for ( int i = SEGMENT.start; i <= SEGMENT.stop; i++) { + for (uint16_t i = SEGMENT.start; i <= SEGMENT.stop; i++) { fastled_col = ColorFromPalette(currentPalette, SEGMENT_RUNTIME.counter_mode_step + (i * 2), beat - SEGMENT_RUNTIME.counter_mode_step + (i * 10)); setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); } @@ -2244,12 +2256,12 @@ uint16_t WS2812FX::mode_fillnoise8(void) if (SEGMENT_RUNTIME.counter_mode_call == 0) SEGMENT_RUNTIME.counter_mode_step = random(12345); handle_palette(); CRGB fastled_col; - for (int i = SEGMENT.start; i <= SEGMENT.stop; i++) { + for (uint16_t i = SEGMENT.start; i <= SEGMENT.stop; i++) { uint8_t index = inoise8(i * SEGMENT_LENGTH, SEGMENT_RUNTIME.counter_mode_step + i * SEGMENT_LENGTH) % 255; fastled_col = ColorFromPalette(currentPalette, index, 255, LINEARBLEND); setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); } - SEGMENT_RUNTIME.counter_mode_step += beatsin8(SEGMENT.speed, 1, 4); //10,1,4 + SEGMENT_RUNTIME.counter_mode_step += beatsin8(SEGMENT.speed, 1, 6); //10,1,4 return 20; } @@ -2261,7 +2273,7 @@ uint16_t WS2812FX::mode_noise16_1(void) CRGB fastled_col; SEGMENT_RUNTIME.counter_mode_step += (1 + SEGMENT.speed/16); - for (int i = SEGMENT.start; i <= SEGMENT.stop; i++) { + for (uint16_t i = SEGMENT.start; i <= SEGMENT.stop; i++) { uint16_t shift_x = beatsin8(11); // the x position of the noise field swings @ 17 bpm uint16_t shift_y = SEGMENT_RUNTIME.counter_mode_step/42; // the y position becomes slowly incremented @@ -2290,7 +2302,7 @@ uint16_t WS2812FX::mode_noise16_2(void) CRGB fastled_col; SEGMENT_RUNTIME.counter_mode_step += (1 + SEGMENT.speed); - for (int i = SEGMENT.start; i <= SEGMENT.stop; i++) { + for (uint16_t i = SEGMENT.start; i <= SEGMENT.stop; i++) { uint16_t shift_x = SEGMENT_RUNTIME.counter_mode_step >> 6; // x as a function of time uint16_t shift_y = SEGMENT_RUNTIME.counter_mode_step/42; @@ -2318,7 +2330,7 @@ uint16_t WS2812FX::mode_noise16_3(void) CRGB fastled_col; SEGMENT_RUNTIME.counter_mode_step += (1 + SEGMENT.speed); - for (int i = SEGMENT.start; i <= SEGMENT.stop; i++) { + for (uint16_t i = SEGMENT.start; i <= SEGMENT.stop; i++) { uint16_t shift_x = 4223; // no movement along x and y uint16_t shift_y = 1234; @@ -2345,7 +2357,7 @@ uint16_t WS2812FX::mode_noise16_4(void) handle_palette(); CRGB fastled_col; SEGMENT_RUNTIME.counter_mode_step += SEGMENT.speed; - for (int i = SEGMENT.start; i <= SEGMENT.stop; i++) { + for (uint16_t i = SEGMENT.start; i <= SEGMENT.stop; i++) { int16_t index = inoise16(uint32_t(i - SEGMENT.start) << 12, SEGMENT_RUNTIME.counter_mode_step/8); fastled_col = ColorFromPalette(currentPalette, index); setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); diff --git a/wled00/WS2812FX.h b/wled00/WS2812FX.h index 7b5aa9253..d074bbf74 100644 --- a/wled00/WS2812FX.h +++ b/wled00/WS2812FX.h @@ -148,10 +148,10 @@ #define FX_MODE_RANDOM_CHASE 61 #define FX_MODE_OSCILLATE 62 //Modes that use FastLED --> -#define FX_MODE_FIRE_2012 63 -#define FX_MODE_PRIDE_2015 64 -#define FX_MODE_JUGGLE 65 -#define FX_MODE_PALETTE 66 +#define FX_MODE_PRIDE_2015 63 +#define FX_MODE_JUGGLE 64 +#define FX_MODE_PALETTE 65 +#define FX_MODE_FIRE_2012 66 #define FX_MODE_COLORWAVES 67 #define FX_MODE_BPM 68 #define FX_MODE_FILLNOISE8 69 @@ -271,7 +271,8 @@ class WS2812FX { _segments[0].speed = DEFAULT_SPEED; _reverseMode = false; _skipFirstMode = false; - paletteFade = true; + paletteFade = 0; + paletteBlend = 0; _locked = NULL; _cronixieDigits = new byte[6]; bus = new NeoPixelWrapper(); @@ -316,6 +317,8 @@ class WS2812FX { show(void); uint8_t + paletteFade, + paletteBlend, getBrightness(void), getMode(void), getSpeed(void), @@ -332,8 +335,6 @@ class WS2812FX { getPowerEstimate(uint16_t leds, uint32_t c, byte b), getSafePowerMultiplier(double safeMilliAmps, uint16_t leds, uint32_t c, byte b); - bool paletteFade; - WS2812FX::Segment getSegment(void); diff --git a/wled00/data/index.htm b/wled00/data/index.htm index b53bebc00..353498808 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -58,6 +58,7 @@ uwv = false; } Cf.TX.selectedIndex = this.responseXML.getElementsByTagName('fx')[0].childNodes[0].nodeValue; + Cf.FP.selectedIndex = this.responseXML.getElementsByTagName('fp')[0].childNodes[0].nodeValue; d.Cf.SX.value = this.responseXML.getElementsByTagName('sx')[0].childNodes[0].nodeValue; d.Cf.IX.value = this.responseXML.getElementsByTagName('ix')[0].childNodes[0].nodeValue; nla = (this.responseXML.getElementsByTagName('nl')[0].innerHTML)!=0?true:false; @@ -171,8 +172,8 @@ case 3: gId("path1").style.fill = aC; gId("path2").style.fill = aC; } tgb.style.fill=(Cf.SA.value>0)?aC:dC; - fpX.style.display=(Cf.TX.selectedIndex>65)?"block":"none"; - fof.style.fill=(Cf.TX.selectedIndex>65)?aC:dC; + fpX.style.display=(Cf.TX.selectedIndex>64)?"block":"none"; + fof.style.fill=(Cf.TX.selectedIndex>64)?aC:dC; fmr.style.fill=(Cf.TX.selectedIndex<1)?aC:dC; } function TgT() @@ -194,7 +195,7 @@ if (n==-1||n==74) return; Cf.TX.selectedIndex =n; if (n < 0) Cf.TX.selectedIndex = 0; - if (n > 73) Cf.TX.selectedIndex = 66; + if (n > 73) Cf.TX.selectedIndex = 65; GX(); } function TgHSB() @@ -648,10 +649,10 @@ - - - - + + + + @@ -670,18 +671,51 @@

FastLED Palette

diff --git a/wled00/data/index_mobile.htm b/wled00/data/index_mobile.htm index b040e9571..e8834c928 100644 --- a/wled00/data/index_mobile.htm +++ b/wled00/data/index_mobile.htm @@ -422,10 +422,10 @@
  • Dual Scanner
  • Random Chase
  • Oscillate
  • -
  • Fire 2012
  • -
  • Pride 2015
  • -
  • Juggle
  • -
  • Palette
  • +
  • Pride 2015
  • +
  • Juggle
  • +
  • Palette
  • +
  • Fire 2012
  • Colorwaves
  • BPM
  • Fill Noise 8
  • @@ -433,19 +433,53 @@
  • Noise 16 2
  • Noise 16 3
  • Noise 16 4
  •   +
  • Go to top
  •  

    FastLED Palette (Effects 56-73)

    -
  • Random Change
  • -
  • Primary Only
  • -
  • Based on Primary
  • -
  • Set Colors Only
  • -
  • Based on Set
  • -
  • Party
  • -
  • Cloud
  • -
  • Lava
  • -
  • Ocean
  • -
  • Forest
  • -
  • Rainbow
  • -
  • Rainbow Stripe
  • +
  • Default
  • +
  • Random Cycle
  • +
  • Primary Color Only
  • +
  • Based on Primary
  • +
  • Set Colors Only
  • +
  • Based on Set Colors
  • +
  • Party
  • +
  • Cloud
  • +
  • Lava
  • +
  • Ocean
  • +
  • Forest
  • +
  • Rainbow
  • +
  • Rainbow Stripe
  • +
  • Sunset
  • +
  • Rivendell
  • +
  • Breeze
  • +
  • Red & Blue
  • +
  • Yellowout
  • +
  • Analogous
  • +
  • Splash
  • +
  • Pastel
  • +
  • Sunset2
  • +
  • Beech
  • +
  • Vintage
  • +
  • Departure
  • +
  • Landscape
  • +
  • Beach
  • +
  • Sherbet
  • +
  • Hult
  • +
  • Hult64
  • +
  • Drywet
  • +
  • Jul
  • +
  • Grintage
  • +
  • Rewhi
  • +
  • Tertiary
  • +
  • Fire
  • +
  • Icefire
  • +
  • Cyane
  • +
  • Light Pink
  • +
  • Autumn
  • +
  • Magenta
  • +
  • Magred
  • +
  • Yelmag
  • +
  • Yelblu
  • +
  • Orange & Teal
  •  
  • Go to top
  • diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 80149dbcda4636955e3589083f7544c69ce75fe7..0573c494522ffdebe82f5558aac6f449c139ca15 100644 GIT binary patch delta 739 zcmX@++~%{Phl9JAA(bHqh?5ygCUdg&PFCWym|VclF?j>CfN&W@B0~{_27@jTCNsD( zoxr?=Aa}UQCmdORY4<^r&=$pJrNMUmVpB2|+2}!5P-y{Qc6@a>m8A^b< z^MSh3fi_e!C;)jNd-54_fiM*)o&qFOfM$wI&Q%8K$OFoi;8Eu)^_<%oY-Tag^t8>o z(#9?ZuOt95f47?0n44gn{1JnffEKp@0P$$SCwhT%P0Srz+ z(hjZ`q&lCW0BB|=*b52_puossD21vw0IT+4$gBq%lL`(ekPpg%u1f@JQvhm91CqHw z&1F!BXabGYM=_Lm>kMJmq1Xm;P<=Vj!b+ge6yTOqVv!M6i$Gp11L7h~Tk4SliFp4Q igRSuc2Q4Vl2*y3muoDEuls*B6$_D*P8FFAx^kyhy-`Yw{QVzR7E(dnUUn3QT?@)dZ$

    Cycle Range

    -
    • Color
    • Effects
    • Both

    Preset Duration

    Color Transition

    FX Speed

    FX Intensity

    • Solid
    • Blink
    • Breathe
    • Wipe
    • Wipe Random
    • Random Colors
    • Sweep
    • Dynamic
    • Colorloop
    • Rainbow
    • Scan
    • Double Scan
    • Fade
    • Chase
    • Chase Rainbow
    • Running
    • Twinkle
    • Twinkle Random
    • Twinkle Fade
    • Twinkle Random Fade
    • Sparkle
    • Dark Sparkle
    • Dark Sparkle+
    • Strobe
    • Strobe Rainbow
    • Double Strobe
    • Blink Rainbow
    • Android
    • Dark Chase
    • Dark Chase Random
    • Dark Chase Rainbow
    • Chase Flash
    • Dark Chase Random
    • Rainbow Runner
    • Colorful
    • Traffic Light
    • Sweep Random
    • Running 2
    • Red & Blue
    • Running 2 Random
    • Scanner
    • Lighthouse
    • Fireworks
    • Fireworks Random
    • Merry Christmas
    • Fire Flicker
    • Gradient
    • Loading
    • In Out
    • In In
    • Out Out
    • Out In
    • Circus
    • Halloween
    • Tri Chase
    • Tri Wipe
    • Tri Fade
    • Lightning
    • ICU
    • Multi Comet
    • Dual Scanner
    • Random Chase
    • Oscillate
    • Fire 2012
    • -
    • Juggle
    • Palette
    • BPM
    • Fill Noise 8
    • Noise 16 1
    • Noise 16 2
    • Noise 16 3
    • Noise 16 4
    • Pride 2015
    • Juggle
    • Palette
    • Colorwaves
    • BPM
    • Fill Noise 8
    • Noise 16 1
    • Noise 16 2
    • Noise 16 3
    • Noise 16 4
    •  

      FastLED Palette (Effects 55-71)

    • Random Change
    • Primary Only
    • Based on Primary
    • Set Colors Only
    • Based on Set
    • Party
    • Cloud
    • Lava
    • Ocean
    • Forest
    • Rainbow
    • Rainbow Stripe
    • +
      • Color
      • Effects
      • Both

      Preset Duration

      Color Transition

      FX Speed

      FX Intensity

      • Solid
      • Blink
      • Breathe
      • Wipe
      • Wipe Random
      • Random Colors
      • Sweep
      • Dynamic
      • Colorloop
      • Rainbow
      • Scan
      • Double Scan
      • Fade
      • Chase
      • Chase Rainbow
      • Running
      • Twinkle
      • Twinkle Random
      • Twinkle Fade
      • Twinkle Random Fade
      • Sparkle
      • Dark Sparkle
      • Dark Sparkle+
      • Strobe
      • Strobe Rainbow
      • Double Strobe
      • Blink Rainbow
      • Android
      • Dark Chase
      • Dark Chase Random
      • Dark Chase Rainbow
      • Chase Flash
      • Dark Chase Random
      • Rainbow Runner
      • Colorful
      • Traffic Light
      • Sweep Random
      • Running 2
      • Red & Blue
      • Running 2 Random
      • Scanner
      • Lighthouse
      • Fireworks
      • Fireworks Random
      • Merry Christmas
      • Fire Flicker
      • Gradient
      • Loading
      • In Out
      • In In
      • Out Out
      • Out In
      • Circus
      • Halloween
      • Tri Chase
      • Tri Wipe
      • Tri Fade
      • Lightning
      • ICU
      • Multi Comet
      • Dual Scanner
      • Random Chase
      • Oscillate
      • Pride 2015
      • Juggle
      • Palette
      • Fire 2012
      • Colorwaves
      • BPM
      • Fill Noise 8
      • Noise 16 1
      • Noise 16 2
      • Noise 16 3
      • Noise 16 4
      •  
      • Go to top
      •  

        FastLED Palette (Effects 56-73)

      • Default
      • Random Cycle
      • Primary Color Only
      • Based on Primary
      • Set Colors Only
      • Based on Set Colors
      • Party
      • Cloud
      • Lava
      • Ocean
      • Forest
      • Rainbow
      • Rainbow Stripe
      • Sunset
      • Rivendell
      • Breeze
      • Red & Blue
      • Yellowout
      • Analogous
      • Splash
      • Pastel
      • Sunset2
      • Beech
      • Vintage
      • Departure
      • Landscape
      • Beach
      • Sherbet
      • Hult
      • Hult64
      • Drywet
      • Jul
      • Grintage
      • Rewhi
      • Tertiary
      • Fire
      • Icefire
      • Cyane
      • Light Pink
      • Autumn
      • Magenta
      • Magred
      • Yelmag
      • Yelblu
      • Orange & Teal
      •  
      • Go to top
      • @@ -74,10 +73,8 @@ The Mobile UI is not supported due to limited flash storage. Please go to IP/set */ //head0 (js) const char PAGE_index0[] PROGMEM = R"=====( - - -WLED 0.8.0-a - +WLED 0.8.0-a + )====="; //head1 (css) @@ -119,108 +116,21 @@ const char PAGE_index3[] PROGMEM = R"=====( - - -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        +
        +
        +
        +
        +
        +
        +
        +
        +
        Effect Panel



        -

        Set secondary color to @@ -228,30 +138,12 @@ Set secondary color to -or -
        +or

        FastLED Palette

        - -
        -
        -
        -
        -
        -
        -
        +
        +
        +
        Favorite Presets

        @@ -260,21 +152,13 @@ Favorite Presets




        Click checkmark to apply brightness, color and effects.

        Cycle through presets to , keep each for ms:

        -
        -
        -
        +
        Timed Light



        Gradually dim down
        -1st slider sets duration (1-255min), 2nd sets target brightness. -
        -
        -
        -
        -
        -
        - -
        - - +1st slider sets duration (1-255min), 2nd sets target brightness.
        +
        +
        +
        + )====="; diff --git a/wled00/htmls01.h b/wled00/htmls01.h index 4819289a5..52d5dbb60 100644 --- a/wled00/htmls01.h +++ b/wled00/htmls01.h @@ -86,7 +86,10 @@ const char PAGE_settings_leds1[] PROGMEM = R"=====(

        LED setup

        LED count:
        LEDs are 4-channel type (RGBW):
        +
        Apply preset at boot (0 uses defaults)
        +Turn on after power up/reset:
        +
        Default RGB color: @@ -105,12 +108,12 @@ Default secondary RGB(W):

        Ignore and use current color, brightness and effects:
        Save current preset cycle configuration as boot default:
        -Turn on after power up/reset:
        -Use Gamma correction for brightness:
        -Use Gamma correction for color:
        +
        +Use Gamma correction for color: (strongly recommended)
        +Use Gamma correction for brightness: (not recommended)
        Brightness factor: %

        Transitions

        -Fade:
        +Crossfade:
        Transition Time: ms
        Enable transition for secondary color:
        Enable Palette transitions: @@ -119,6 +122,13 @@ Default Duration: min Default Target brightness:
        Fade down:

        Advanced

        +Palette blending: +
        Reverse LED order (rotate 180):
        Init LEDs after WiFi:
        Skip first LED:
        diff --git a/wled00/palettes.h b/wled00/palettes.h index 4d31f1660..a65b74cfc 100644 --- a/wled00/palettes.h +++ b/wled00/palettes.h @@ -26,6 +26,7 @@ DEFINE_GRADIENT_PALETTE( es_vintage_57_gp ) { 153, 167,135, 10, 255, 46, 56, 4}; + // Gradient palette "es_vintage_01_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/vintage/tn/es_vintage_01.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) @@ -41,6 +42,7 @@ DEFINE_GRADIENT_PALETTE( es_vintage_01_gp ) { 229, 4, 1, 1, 255, 4, 1, 1}; + // Gradient palette "es_rivendell_15_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/rivendell/tn/es_rivendell_15.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) @@ -53,21 +55,24 @@ DEFINE_GRADIENT_PALETTE( es_rivendell_15_gp ) { 242, 150,156, 99, 255, 150,156, 99}; + // Gradient palette "rgi_15_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/ds/rgi/tn/rgi_15.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 36 bytes of program space. +// Edited to be brighter DEFINE_GRADIENT_PALETTE( rgi_15_gp ) { - 0, 4, 1, 31, - 31, 55, 1, 16, - 63, 197, 3, 7, - 95, 59, 2, 17, - 127, 6, 2, 34, - 159, 39, 6, 33, - 191, 112, 13, 32, - 223, 56, 9, 35, - 255, 22, 6, 38}; + 0, 4, 1, 70, + 31, 55, 1, 30, + 63, 255, 4, 7, + 95, 59, 2, 29, + 127, 11, 3, 50, + 159, 39, 8, 60, + 191, 112, 19, 40, + 223, 78, 11, 39, + 255, 29, 8, 59}; + // Gradient palette "retro2_16_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/ma/retro2/tn/retro2_16.png.index.html @@ -78,6 +83,7 @@ DEFINE_GRADIENT_PALETTE( retro2_16_gp ) { 0, 188,135, 1, 255, 46, 7, 1}; + // Gradient palette "Analogous_1_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/red/tn/Analogous_1.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) @@ -90,6 +96,7 @@ DEFINE_GRADIENT_PALETTE( Analogous_1_gp ) { 191, 142, 0, 45, 255, 255, 0, 0}; + // Gradient palette "es_pinksplash_08_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/pink_splash/tn/es_pinksplash_08.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) @@ -102,45 +109,6 @@ DEFINE_GRADIENT_PALETTE( es_pinksplash_08_gp ) { 221, 157, 3,112, 255, 157, 3,112}; -// Gradient palette "es_pinksplash_07_gp", originally from -// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/pink_splash/tn/es_pinksplash_07.png.index.html -// converted for FastLED with gammas (2.6, 2.2, 2.5) -// Size: 28 bytes of program space. - -DEFINE_GRADIENT_PALETTE( es_pinksplash_07_gp ) { - 0, 229, 1, 1, - 61, 242, 4, 63, - 101, 255, 12,255, - 127, 249, 81,252, - 153, 255, 11,235, - 193, 244, 5, 68, - 255, 232, 1, 5}; - -// Gradient palette "Coral_reef_gp", originally from -// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/other/tn/Coral_reef.png.index.html -// converted for FastLED with gammas (2.6, 2.2, 2.5) -// Size: 24 bytes of program space. - -DEFINE_GRADIENT_PALETTE( Coral_reef_gp ) { - 0, 40,199,197, - 50, 10,152,155, - 96, 1,111,120, - 96, 43,127,162, - 139, 10, 73,111, - 255, 1, 34, 71}; - -// Gradient palette "es_ocean_breeze_068_gp", originally from -// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/ocean_breeze/tn/es_ocean_breeze_068.png.index.html -// converted for FastLED with gammas (2.6, 2.2, 2.5) -// Size: 24 bytes of program space. - -DEFINE_GRADIENT_PALETTE( es_ocean_breeze_068_gp ) { - 0, 100,156,153, - 51, 1, 99,137, - 101, 1, 68, 84, - 104, 35,142,168, - 178, 0, 63,117, - 255, 1, 10, 10}; // Gradient palette "es_ocean_breeze_036_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/ocean_breeze/tn/es_ocean_breeze_036.png.index.html @@ -153,6 +121,7 @@ DEFINE_GRADIENT_PALETTE( es_ocean_breeze_036_gp ) { 153, 144,209,255, 255, 0, 73, 82}; + // Gradient palette "departure_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/mjf/tn/departure.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) @@ -172,6 +141,7 @@ DEFINE_GRADIENT_PALETTE( departure_gp ) { 212, 0, 55, 0, 255, 0, 55, 0}; + // Gradient palette "es_landscape_64_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/landscape/tn/es_landscape_64.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) @@ -188,6 +158,7 @@ DEFINE_GRADIENT_PALETTE( es_landscape_64_gp ) { 204, 59,117,250, 255, 1, 37,192}; + // Gradient palette "es_landscape_33_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/landscape/tn/es_landscape_33.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) @@ -201,6 +172,7 @@ DEFINE_GRADIENT_PALETTE( es_landscape_33_gp ) { 66, 39,142, 74, 255, 1, 4, 1}; + // Gradient palette "rainbowsherbet_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/ma/icecream/tn/rainbowsherbet.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) @@ -215,6 +187,7 @@ DEFINE_GRADIENT_PALETTE( rainbowsherbet_gp ) { 209, 42,255, 22, 255, 87,255, 65}; + // Gradient palette "gr65_hult_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/hult/tn/gr65_hult.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) @@ -228,6 +201,7 @@ DEFINE_GRADIENT_PALETTE( gr65_hult_gp ) { 216, 1,124,109, 255, 1,124,109}; + // Gradient palette "gr64_hult_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/hult/tn/gr64_hult.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) @@ -243,6 +217,7 @@ DEFINE_GRADIENT_PALETTE( gr64_hult_gp ) { 239, 0, 55, 45, 255, 0, 55, 45}; + // Gradient palette "GMT_drywet_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/gmt/tn/GMT_drywet.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) @@ -257,6 +232,7 @@ DEFINE_GRADIENT_PALETTE( GMT_drywet_gp ) { 212, 1, 1,111, 255, 1, 7, 33}; + // Gradient palette "ib15_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/ing/general/tn/ib15.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) @@ -270,28 +246,19 @@ DEFINE_GRADIENT_PALETTE( ib15_gp ) { 141, 137, 31, 39, 255, 59, 33, 89}; -// Gradient palette "Fuschia_7_gp", originally from -// http://soliton.vm.bytemark.co.uk/pub/cpt-city/ds/fuschia/tn/Fuschia-7.png.index.html + +// Gradient palette "Tertiary_01_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/vermillion/tn/Tertiary_01.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 20 bytes of program space. -DEFINE_GRADIENT_PALETTE( Fuschia_7_gp ) { - 0, 43, 3,153, - 63, 100, 4,103, - 127, 188, 5, 66, - 191, 161, 11,115, - 255, 135, 20,182}; +DEFINE_GRADIENT_PALETTE( Tertiary_01_gp ) { + 0, 0, 1,255, + 63, 3, 68, 45, + 127, 23,255, 0, + 191, 100, 68, 1, + 255, 255, 1, 4}; -// Gradient palette "es_emerald_dragon_08_gp", originally from -// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/emerald_dragon/tn/es_emerald_dragon_08.png.index.html -// converted for FastLED with gammas (2.6, 2.2, 2.5) -// Size: 16 bytes of program space. - -DEFINE_GRADIENT_PALETTE( es_emerald_dragon_08_gp ) { - 0, 97,255, 1, - 101, 47,133, 1, - 178, 13, 43, 1, - 255, 2, 10, 1}; // Gradient palette "lava_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/neota/elem/tn/lava.png.index.html @@ -313,20 +280,22 @@ DEFINE_GRADIENT_PALETTE( lava_gp ) { 244, 255,255, 71, 255, 255,255,255}; -// Gradient palette "fire_gp", originally from -// http://soliton.vm.bytemark.co.uk/pub/cpt-city/neota/elem/tn/fire.png.index.html + +// Gradient palette "fierce_ice_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/neota/elem/tn/fierce-ice.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 28 bytes of program space. -DEFINE_GRADIENT_PALETTE( fire_gp ) { - 0, 1, 1, 0, - 76, 32, 5, 0, - 146, 192, 24, 0, - 197, 220,105, 5, - 240, 252,255, 31, - 250, 252,255,111, +DEFINE_GRADIENT_PALETTE( fierce_ice_gp ) { + 0, 0, 0, 0, + 59, 0, 9, 45, + 119, 0, 38,255, + 149, 3,100,255, + 180, 23,199,255, + 217, 100,235,255, 255, 255,255,255}; + // Gradient palette "Colorfull_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/atmospheric/tn/Colorfull.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) @@ -345,19 +314,6 @@ DEFINE_GRADIENT_PALETTE( Colorfull_gp ) { 168, 100,180,155, 255, 22,121,174}; -// Gradient palette "Magenta_Evening_gp", originally from -// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/atmospheric/tn/Magenta_Evening.png.index.html -// converted for FastLED with gammas (2.6, 2.2, 2.5) -// Size: 28 bytes of program space. - -DEFINE_GRADIENT_PALETTE( Magenta_Evening_gp ) { - 0, 71, 27, 39, - 31, 130, 11, 51, - 63, 213, 2, 64, - 70, 232, 1, 66, - 76, 252, 1, 69, - 108, 123, 2, 51, - 255, 46, 9, 35}; // Gradient palette "Pink_Purple_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/atmospheric/tn/Pink_Purple.png.index.html @@ -377,6 +333,7 @@ DEFINE_GRADIENT_PALETTE( Pink_Purple_gp ) { 183, 128, 57,155, 255, 146, 40,123}; + // Gradient palette "Sunset_Real_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/atmospheric/tn/Sunset_Real.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) @@ -391,6 +348,68 @@ DEFINE_GRADIENT_PALETTE( Sunset_Real_gp ) { 198, 16, 0,130, 255, 0, 0,160}; + +// Gradient palette "Sunset_Yellow_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/atmospheric/tn/Sunset_Yellow.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 44 bytes of program space. + +DEFINE_GRADIENT_PALETTE( Sunset_Yellow_gp ) { + 0, 10, 62,123, + 36, 56,130,103, + 87, 153,225, 85, + 100, 199,217, 68, + 107, 255,207, 54, + 115, 247,152, 57, + 120, 239,107, 61, + 128, 247,152, 57, + 180, 255,207, 54, + 223, 255,227, 48, + 255, 255,248, 42}; + + +// Gradient palette "Beech_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/atmospheric/tn/Beech.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 60 bytes of program space. + +DEFINE_GRADIENT_PALETTE( Beech_gp ) { + 0, 255,252,214, + 12, 255,252,214, + 22, 255,252,214, + 26, 190,191,115, + 28, 137,141, 52, + 28, 112,255,205, + 50, 51,246,214, + 71, 17,235,226, + 93, 2,193,199, + 120, 0,156,174, + 133, 1,101,115, + 136, 1, 59, 71, + 136, 7,131,170, + 208, 1, 90,151, + 255, 0, 56,133}; + + +// Gradient palette "Another_Sunset_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/atmospheric/tn/Another_Sunset.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 32 bytes of program space. + +DEFINE_GRADIENT_PALETTE( Another_Sunset_gp ) { + 0, 110, 49, 11, + 29, 55, 34, 10, + 68, 22, 22, 9, + 68, 239,124, 8, + 97, 220,156, 27, + 124, 203,193, 61, + 178, 33, 53, 56, + 255, 0, 1, 52}; + + + + + // Gradient palette "es_autumn_19_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/autumn/tn/es_autumn_19.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) @@ -411,6 +430,7 @@ DEFINE_GRADIENT_PALETTE( es_autumn_19_gp ) { 249, 17, 1, 1, 255, 17, 1, 1}; + // Gradient palette "BlacK_Blue_Magenta_White_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/BlacK_Blue_Magenta_White.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) @@ -425,6 +445,7 @@ DEFINE_GRADIENT_PALETTE( BlacK_Blue_Magenta_White_gp ) { 212, 255, 55,255, 255, 255,255,255}; + // Gradient palette "BlacK_Magenta_Red_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/BlacK_Magenta_Red.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) @@ -437,6 +458,7 @@ DEFINE_GRADIENT_PALETTE( BlacK_Magenta_Red_gp ) { 191, 255, 0, 45, 255, 255, 0, 0}; + // Gradient palette "BlacK_Red_Magenta_Yellow_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/BlacK_Red_Magenta_Yellow.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) @@ -451,6 +473,7 @@ DEFINE_GRADIENT_PALETTE( BlacK_Red_Magenta_Yellow_gp ) { 212, 255, 55, 45, 255, 255,255, 0}; + // Gradient palette "Blue_Cyan_Yellow_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/Blue_Cyan_Yellow.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) @@ -464,73 +487,13 @@ DEFINE_GRADIENT_PALETTE( Blue_Cyan_Yellow_gp ) { 255, 255,255, 0}; +//Custom palette by Aircoookie - - - - - -// Gradient palette "BlacK_Blue_Cyan_gp", originally from -// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/BlacK_Blue_Cyan.png.index.html -// converted for FastLED with gammas (2.6, 2.2, 2.5) -// Size: 20 bytes of program space. - -DEFINE_GRADIENT_PALETTE( BlacK_Blue_Cyan_gp ) { - 0, 0, 0, 0, - 63, 0, 0, 45, - 127, 0, 0,255, - 191, 0, 55,255, - 255, 0,255,255}; - - -// Gradient palette "BlacK_Blue_gp", originally from -// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/BlacK_Blue.png.index.html -// converted for FastLED with gammas (2.6, 2.2, 2.5) -// Size: 12 bytes of program space. - -DEFINE_GRADIENT_PALETTE( BlacK_Blue_gp ) { - 0, 0, 0, 0, - 127, 0, 0, 45, - 255, 0, 0,255}; - - -// Gradient palette "Red_Black_gp", originally from -// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/Red_Black.png.index.html -// converted for FastLED with gammas (2.6, 2.2, 2.5) -// Size: 12 bytes of program space. - -DEFINE_GRADIENT_PALETTE( Red_Black_gp ) { - 0, 255, 0, 0, - 127, 42, 0, 0, - 255, 0, 0, 0}; - - -// Gradient palette "Yellow_Black_gp", originally from -// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/Yellow_Black.png.index.html -// converted for FastLED with gammas (2.6, 2.2, 2.5) -// Size: 12 bytes of program space. - -DEFINE_GRADIENT_PALETTE( Yellow_Black_gp ) { - 0, 255,255, 0, - 127, 42, 55, 0, - 255, 0, 0, 0}; - - -// Gradient palette "bhw1_hello_gp", originally from -// http://soliton.vm.bytemark.co.uk/pub/cpt-city/bhw/bhw1/tn/bhw1_hello.png.index.html -// converted for FastLED with gammas (2.6, 2.2, 2.5) -// Size: 32 bytes of program space. - -DEFINE_GRADIENT_PALETTE( bhw1_hello_gp ) { - 0, 237,156,197, - 35, 244,189,230, - 56, 255,255,255, - 79, 244,189,230, - 109, 237,156,197, - 160, 121,255,255, - 196, 255,255,255, - 255, 121,255,255}; - +DEFINE_GRADIENT_PALETTE( Orange_Teal_gp ) { + 0, 0,150, 92, + 55, 0,150, 92, + 200, 255, 72, 0, + 255, 255, 72, 0}; // Single array of defined cpt-city color palettes. @@ -544,39 +507,38 @@ DEFINE_GRADIENT_PALETTE( bhw1_hello_gp ) { // This list of color palettes acts as a "playlist"; you can // add or delete, or re-arrange as you wish. const TProgmemRGBGradientPalettePtr gGradientPalettes[] = { - Sunset_Real_gp, //12 - es_rivendell_15_gp, //13 - es_ocean_breeze_036_gp, //14 - rgi_15_gp, //15 - retro2_16_gp, //16 - Analogous_1_gp, //17 - es_pinksplash_08_gp, //18 - Coral_reef_gp, //19 - es_ocean_breeze_068_gp, //20 - es_pinksplash_07_gp, //21 - es_vintage_01_gp, //22 - departure_gp, //23 - es_landscape_64_gp, //24 - es_landscape_33_gp, //25 - rainbowsherbet_gp, //26 - gr65_hult_gp, //27 - gr64_hult_gp, //28 - GMT_drywet_gp, //29 - ib_jul01_gp, //30 - es_vintage_57_gp, //31 - ib15_gp, //32 - Fuschia_7_gp, //33 - es_emerald_dragon_08_gp, //34 - lava_gp, //35 - fire_gp, //36 - Colorfull_gp, //37 - Magenta_Evening_gp, //38 - Pink_Purple_gp, //39 - es_autumn_19_gp, //40 - BlacK_Blue_Magenta_White_gp, //41 - BlacK_Magenta_Red_gp, //42 - BlacK_Red_Magenta_Yellow_gp, //43 - Blue_Cyan_Yellow_gp, //44 + Sunset_Real_gp, //13-00 Sunset + es_rivendell_15_gp, //14-01 Rivendell + es_ocean_breeze_036_gp, //15-02 Breeze + rgi_15_gp, //16-03 Red & Blue + retro2_16_gp, //17-04 Yellowout + Analogous_1_gp, //18-05 Analogous + es_pinksplash_08_gp, //19-06 Splash + Sunset_Yellow_gp, //20-07 Pastel + Another_Sunset_gp, //21-08 Sunset2 + Beech_gp, //22-09 Beech + es_vintage_01_gp, //23-10 Vintage + departure_gp, //24-11 Departure + es_landscape_64_gp, //25-12 Landscape + es_landscape_33_gp, //26-13 Beach + rainbowsherbet_gp, //27-14 Sherbet + gr65_hult_gp, //28-15 Hult + gr64_hult_gp, //29-16 Hult64 + GMT_drywet_gp, //30-17 Drywet + ib_jul01_gp, //31-18 Jul + es_vintage_57_gp, //32-19 Grintage + ib15_gp, //33-20 Rewhi + Tertiary_01_gp, //34-21 Tertiary + lava_gp, //35-22 Fire + fierce_ice_gp, //36-23 Icefire + Colorfull_gp, //37-24 Cyane + Pink_Purple_gp, //38-25 Light Pink + es_autumn_19_gp, //39-26 Autumn + BlacK_Blue_Magenta_White_gp, //40-27 Magenta + BlacK_Magenta_Red_gp, //41-28 Magred + BlacK_Red_Magenta_Yellow_gp, //42-29 Yelmag + Blue_Cyan_Yellow_gp, //43-30 Yelblu + Orange_Teal_gp //44-31 Orange & Teal }; diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 3b1b560c5..bb076d920 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -39,7 +39,7 @@ #include "src/dependencies/e131/E131.h" //version in format yymmddb (b = daily build) -#define VERSION 1809084 +#define VERSION 1809103 char versionString[] = "0.8.0-a"; //AP and OTA default passwords (change them!) diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino index 357d46478..1536d788f 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled01_eeprom.ino @@ -6,7 +6,7 @@ #define EEPSIZE 3072 //eeprom Version code, enables default settings instead of 0 init on update -#define EEPVER 7 +#define EEPVER 8 //0 -> old version, default //1 -> 0.4p 1711272 and up //2 -> 0.4p 1711302 and up @@ -129,7 +129,8 @@ void saveSettingsToEEPROM() EEPROM.write(379, colSecS[1]); EEPROM.write(380, colSecS[2]); EEPROM.write(381, whiteSecS); - + EEPROM.write(382, strip.paletteBlend); + EEPROM.write(389, bootPreset); EEPROM.write(390, aOtaEnabled); EEPROM.write(391, receiveNotificationColor); @@ -356,7 +357,7 @@ void loadSettingsFromEEPROM(bool first) whiteS = EEPROM.read(371); white = whiteS; useRGBW = EEPROM.read(372); effectPaletteDefault = EEPROM.read(373); effectPalette = effectPaletteDefault; - strip.paletteFade = EEPROM.read(374); + //374 - strip.paletteFade if (lastEEPROMversion > 0) { apWaitTimeSecs = EEPROM.read(375); @@ -444,6 +445,12 @@ void loadSettingsFromEEPROM(bool first) arlsForceMaxBri = EEPROM.read(2195); arlsDisableGammaCorrection = EEPROM.read(2196); } + + if (lastEEPROMversion > 7) + { + strip.paletteFade = EEPROM.read(374); + strip.paletteBlend = EEPROM.read(382); + } receiveDirect = !EEPROM.read(2200); enableRealtimeUI = EEPROM.read(2201); diff --git a/wled00/wled02_xml.ino b/wled00/wled02_xml.ino index d8b1a7e96..8ca849688 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/wled02_xml.ino @@ -198,6 +198,7 @@ void getSettingsJS(byte subPage) //get values for settings form in javascript sappend('v',"TB",nightlightTargetBri); sappend('v',"TL",nightlightDelayMins); sappend('c',"TW",nightlightFade); + sappend('i',"PB",strip.paletteBlend); sappend('c',"RV",reverseMode); sappend('c',"EI",initLedsLast); sappend('c',"SL",skipFirstLed); diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index 7bde55abe..e1268a4ac 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -184,6 +184,13 @@ void handleSettingsSet(byte subPage) } nightlightFade = server.hasArg("TW"); reverseMode = server.hasArg("RV"); + if (server.hasArg("PB")) + { + int i = server.arg("PB").toInt(); + if (i >= 0){ + strip.paletteBlend = i; + } + } initLedsLast = server.hasArg("EI"); strip.setReverseMode(reverseMode); skipFirstLed = server.hasArg("SL"); From 4715180a32092288f6f7316c3b4da7245679d598 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sat, 15 Sep 2018 17:29:01 +0200 Subject: [PATCH 07/22] Refactored wled00.ino Attempted to fix AP mode lags --- wled00/WS2812FX.cpp | 2 +- wled00/wled00.ino | 412 ++++++++++++++++++++++++--------------- wled00/wled01_eeprom.ino | 16 +- wled00/wled02_xml.ino | 6 +- wled00/wled03_set.ino | 8 +- wled00/wled04_file.ino | 4 +- wled00/wled05_init.ino | 14 +- wled00/wled07_notify.ino | 18 +- wled00/wled08_led.ino | 5 +- wled00/wled12_alexa.ino | 6 +- 10 files changed, 298 insertions(+), 193 deletions(-) diff --git a/wled00/WS2812FX.cpp b/wled00/WS2812FX.cpp index d357fe822..f3d63cd14 100644 --- a/wled00/WS2812FX.cpp +++ b/wled00/WS2812FX.cpp @@ -1835,7 +1835,7 @@ uint16_t WS2812FX::mode_random_chase(void) int b = random(6) != 0 ? (color & 0xFF) : random(256); setPixelColor(SEGMENT.start, r, g, b); - return 15 + (15 * (uint32_t)(255 - SEGMENT.speed)); + return 15 + 2*(uint16_t)(255 - SEGMENT.speed); } typedef struct Oscillator { diff --git a/wled00/wled00.ino b/wled00/wled00.ino index bb076d920..97fd045ff 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -1,5 +1,5 @@ /* - * Main sketch + * Main sketch, global variable declarations */ /* * @title WLED project sketch @@ -10,7 +10,10 @@ //ESP8266-01 got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.3.0 and the setting 512K(64K SPIFFS). //Uncomment the following line to disable some features (currently Mobile UI) to compile for ESP8266-01 //#define WLED_FLASH_512K_MODE +//NOT SUPPORTED IN CURRENT VERSION + +//library inclusions #include #ifdef ARDUINO_ARCH_ESP32 #include @@ -23,6 +26,7 @@ #include #include #endif + #include #include #include @@ -38,141 +42,234 @@ #include "src/dependencies/blynk/BlynkSimpleEsp.h" #include "src/dependencies/e131/E131.h" -//version in format yymmddb (b = daily build) -#define VERSION 1809103 + +//version code in format yymmddb (b = daily build) +#define VERSION 1809151 char versionString[] = "0.8.0-a"; -//AP and OTA default passwords (change them!) + +//AP and OTA default passwords (for maximum change them!) char apPass[65] = "wled1234"; char otaPass[33] = "wledota"; + //spiffs FS only useful for debug (only ESP8266) //#define USEFS + //to toggle usb serial debug (un)comment following line(s) -//#define DEBUG +#define DEBUG -//Hardware-settings (only changeble via code) -//strip pin changeable in NpbWrapper.h. Only change for ESP32 -byte buttonPin = 0; //needs pull-up -byte auxPin = 15; //use e.g. for external relay -byte auxDefaultState = 0; //0: input 1: high 2: low -byte auxTriggeredState = 0; //0: input 1: high 2: low -//Default CONFIG -char serverDescription[33] = "WLED Light"; -byte currentTheme = 0; -byte uiConfiguration = 0; //0: auto 1: classic 2: mobile +//Hardware CONFIG (only changeble HERE, not at runtime) +//LED strip pin changeable in NpbWrapper.h. Only change for ESP32 +byte buttonPin = 0; //needs pull-up +byte auxPin = 15; //debug feature, use e.g. for external relay with API call AX= +byte auxDefaultState = 0; //0: input 1: high 2: low +byte auxTriggeredState = 0; //0: input 1: high 2: low +char ntpServerName[] = "0.wled.pool.ntp.org"; //NTP server to use + + +//WiFi CONFIG (all these can be changed via web UI, no need to set them here) char clientSSID[33] = "Your_Network"; char clientPass[65] = ""; -char cmDNS[33] = "led"; -uint16_t ledCount = 10; //lowered to prevent accidental overcurrent -char apSSID[65] = ""; //AP off by default (unless setup) -byte apChannel = 1; -byte apHide = 0; -byte apWaitTimeSecs = 32; -bool recoveryAPDisabled = false; -IPAddress staticIP(0, 0, 0, 0); -IPAddress staticGateway(0, 0, 0, 0); -IPAddress staticSubnet(255, 255, 255, 0); -IPAddress staticDNS(8, 8, 8, 8); //only for NTP -bool useHSB = true, useHSBDefault = true, useRGBW = false, autoRGBtoRGBW = false; -bool turnOnAtBoot = true; -bool initLedsLast = false, skipFirstLed = false; -byte bootPreset = 0; -byte colS[]{255, 159, 0}; -byte colSecS[]{0, 0, 0}; -byte whiteS = 0; -byte whiteSecS = 0; -byte briS = 127; -byte nightlightTargetBri = 0; -bool fadeTransition = true; -bool disableSecTransition = true; -uint16_t transitionDelay = 1200, transitionDelayDefault = transitionDelay; -bool reverseMode = false; -bool otaLock = false, wifiLock = false; -bool aOtaEnabled = true; -bool buttonEnabled = true; -bool notifyDirect = true, notifyButton = true, notifyDirectDefault = true, alexaNotify = false, macroNotify = false, notifyTwice = false; -bool receiveNotifications = true, receiveNotificationBrightness = true, receiveNotificationColor = true, receiveNotificationEffects = true; -byte briMultiplier = 100; -byte nightlightDelayMins = 60; -bool nightlightFade = true; -uint16_t udpPort = 21324, udpRgbPort = 19446; -byte effectDefault = 0; +char cmDNS[33] = "led"; //mDNS address (x.local), only for Apple and Windows, if Bonjour installed +char apSSID[65] = ""; //AP off by default (unless setup) +byte apChannel = 1; //2.4GHz WiFi AP channel (1-13) +byte apHide = 0; //hidden AP SSID +byte apWaitTimeSecs = 32; //time to wait for connection before opening AP +bool recoveryAPDisabled = false; //never open AP (not recommended) +IPAddress staticIP(0, 0, 0, 0); //static IP of ESP +IPAddress staticGateway(0, 0, 0, 0); //gateway (router) IP +IPAddress staticSubnet(255, 255, 255, 0); //most common subnet in home networks +IPAddress staticDNS(8, 8, 8, 8); //only for NTP, google DNS server + + +//LED CONFIG +uint16_t ledCount = 10; //lowered to prevent accidental overcurrent +bool useRGBW = false; //SK6812 strips can contain an extra White channel +bool autoRGBtoRGBW = false; //if RGBW enabled, calculate White channel from RGB +bool turnOnAtBoot = true; //turn on LEDs at power-up +byte bootPreset = 0; //save preset to load after power-up + +byte colS[]{255, 159, 0}; //default RGB color +byte colSecS[]{0, 0, 0}; //default RGB secondary color +byte whiteS = 0; //default White channel +byte whiteSecS = 0; //default secondary White channel +byte briS = 127; //default brightness +byte effectDefault = 0; byte effectSpeedDefault = 75; -byte effectIntensityDefault = 128; -byte effectPaletteDefault = 0; -//NTP stuff -bool ntpEnabled = false; -char ntpServerName[] = "0.wled.pool.ntp.org"; +byte effectIntensityDefault = 128; //intensity is supported on some effects as an additional parameter (e.g. for blink you can change the duty cycle) +byte effectPaletteDefault = 0; //palette is supported on the FastLED effects, otherwise it has no effect -//alexa -bool alexaEnabled = true; -char alexaInvocationName[33] = "Light"; +bool useGammaCorrectionBri = false; //gamma correct brightness (not recommended) +bool useGammaCorrectionRGB = true; //gamma correct colors (strongly recommended) -byte macroBoot = 0, macroNl = 0; +byte nightlightTargetBri = 0; //brightness after nightlight is over +byte nightlightDelayMins = 60; +bool nightlightFade = true; //if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over +bool fadeTransition = true; //enable crossfading color transition +bool enableSecTransition = true; //also enable transition for secondary color +uint16_t transitionDelay = 1200; //default crossfade duration in ms + +bool reverseMode = false; //flip entire LED strip (reverses all effect directions) +bool initLedsLast = false; //turn on LEDs only after WiFi connected/AP open +bool skipFirstLed = false; //ignore first LED in strip (useful if you need the LED as signal repeater) +byte briMultiplier = 100; //% of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) + + +//User Interface CONFIG +char serverDescription[33] = "WLED Light"; //Name of module +byte currentTheme = 0; //UI theme index for settings and classic UI +byte uiConfiguration = 0; //0: automatic (depends on user-agent) 1: classic UI 2: mobile UI +bool useHSB = true; //classic UI: use HSB sliders instead of RGB by default +char cssFont[33] = "Verdana"; //font to use in classic UI + +bool useHSBDefault = useHSB; + + +//Sync CONFIG +bool buttonEnabled = true; + +uint16_t udpPort = 21324; //WLED notifier default port +uint16_t udpRgbPort = 19446; //Hyperion port + +bool receiveNotificationBrightness = true; //apply brightness from incoming notifications +bool receiveNotificationColor = true; //apply color +bool receiveNotificationEffects = true; //apply effects setup +bool notifyDirect = true; //send notification if change via UI or HTTP API +bool notifyButton = true; +bool notifyAlexa = false; //send notification if updated via Alexa +bool notifyMacro = false; //send notification for macro +bool notifyHue = true; //send notification if Hue light changes +bool notifyTwice = false; //notifications use UDP: enable if devices don't sync reliably + +bool alexaEnabled = true; //enable device discovery by Amazon Echo +char alexaInvocationName[33] = "Light"; //speech control name of device. Choose something voice-to-text can understand + +char blynkApiKey[36] = ""; //Auth token for Blynk server. If empty, no connection will be made + +uint16_t realtimeTimeoutMs = 2500; //ms timeout of realtime mode before returning to normal mode +int arlsOffset = 0; //realtime LED offset +bool receiveDirect = true; //receive UDP realtime +bool enableRealtimeUI = false; //web UI accessible during realtime mode (works on ESP32, lags out ESP8266) +bool arlsDisableGammaCorrection = true; //activate if gamma correction is handled by the source +bool arlsForceMaxBri = false; //enable to force max brightness if source has very dark colors that would be black + +bool e131Enabled = true; //settings for E1.31 (sACN) protocol +byte e131Universe = 1; +bool e131Multicast = false; + +bool huePollingEnabled = false; //poll hue bridge for light state +uint16_t huePollIntervalMs = 2500; //low values (< 1sec) may cause lag but offer quicker response +char hueApiKey[65] = "api"; //key token will be obtained from bridge +byte huePollLightId = 1; //ID of hue lamp to sync to. Find the ID in the hue app ("about" section) +IPAddress hueIP = (0,0,0,0); //IP address of the bridge +bool hueApplyOnOff = true; +bool hueApplyBri = true; +bool hueApplyColor = true; + + +//Time CONFIG +bool ntpEnabled = false; //get internet time. Only required if you use clock overlays or time-activated macros +bool useAMPM = false; //12h/24h clock format +byte currentTimezone = 0; //Timezone ID. Refer to timezones array in wled10_ntp.ino +int utcOffsetSecs = 0; //Seconds to offset from UTC before timzone calculation + +byte overlayDefault = 0; //0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie +byte overlayMin = 0, overlayMax = ledCount-1; //boundaries of overlay mode + +byte analogClock12pixel = 0; //The pixel in your strip where "midnight" would be +bool analogClockSecondsTrail = false; //Display seconds as trail of LEDs instead of a single pixel +bool analogClock5MinuteMarks = false; //Light pixels at every 5-minute position + +char cronixieDisplay[] = "HHMMSS"; //Cronixie Display mask. See wled13_cronixie.ino +bool cronixieBacklight = true; //Allow digits to be back-illuminated + +bool countdownMode = false; //Clock will count down towards date +byte countdownYear = 19, countdownMonth = 1; //Countdown target date, year is last two digits +byte countdownDay = 1, countdownHour = 0; +byte countdownMin = 0, countdownSec = 0; + + +byte macroBoot = 0; //macro loaded after startup +byte macroNl = 0; //after nightlight delay over +byte macroCountdown = 0; byte macroAlexaOn = 0, macroAlexaOff = 0; -byte macroButton = 0, macroCountdown = 0, macroLongPress = 0; +byte macroButton = 0, macroLongPress = 0; -unsigned long countdownTime = 1514764800L; -//hue -bool huePollingEnabled = false, hueAttempt = false; -uint16_t huePollIntervalMs = 2500; -char hueApiKey[65] = "api"; -byte huePollLightId = 1; -IPAddress hueIP = (0,0,0,0); -bool notifyHue = true; -bool hueApplyOnOff = true, hueApplyBri = true, hueApplyColor = true; +//Security CONFIG +bool otaLock = false; //prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks +bool wifiLock = false; //prevents access to WiFi settings when OTA lock is enabled +bool aOtaEnabled = true; //ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on + uint16_t userVar0 = 0, userVar1 = 0; -//Internal vars -byte col[]{255, 159, 0}; -byte colOld[]{0, 0, 0}; -byte colT[]{0, 0, 0}; -byte colIT[]{0, 0, 0}; + + +//internal global variable declarations +//color +byte col[]{255, 159, 0}; //target RGB color +byte colOld[]{0, 0, 0}; //color before transition +byte colT[]{0, 0, 0}; //current color +byte colIT[]{0, 0, 0}; //color that was last sent to LEDs byte colSec[]{0, 0, 0}; byte colSecT[]{0, 0, 0}; byte colSecOld[]{0, 0, 0}; byte colSecIT[]{0, 0, 0}; -byte white = 0, whiteOld, whiteT, whiteIT; -byte whiteSec = 0, whiteSecOld, whiteSecT, whiteSecIT; -byte lastRandomIndex = 0; +byte white = whiteS, whiteOld, whiteT, whiteIT; +byte whiteSec = whiteSecS, whiteSecOld, whiteSecT, whiteSecIT; + +byte lastRandomIndex = 0; //used to save last random color so the new one is not the same + +//transitions +bool transitionActive = false; +uint16_t transitionDelayDefault = transitionDelay; uint16_t transitionDelayTemp = transitionDelay; unsigned long transitionStartTime; -unsigned long nightlightStartTime; -float tperLast = 0; -byte bri = 127; -byte briOld = 0; -byte briT = 0; -byte briIT = 0; -byte briLast = 127; -bool transitionActive = false; -bool buttonPressedBefore = false; -unsigned long buttonPressedTime = 0; -unsigned long notificationSentTime = 0; -byte notificationSentCallMode = 0; -bool notificationTwoRequired = false; +float tperLast = 0; //crossfade transition progress, 0.0f - 1.0f + +//nightlight bool nightlightActive = false; bool nightlightActiveOld = false; uint32_t nightlightDelayMs = 10; -byte briNlT = 0; -byte effectCurrent = 0; -byte effectSpeed = 75; -byte effectIntensity = 128; -byte effectPalette = 0; -bool onlyAP = false; +unsigned long nightlightStartTime; +byte briNlT = 0; //current nightlight brightness + +//brightness +byte bri = briS; +byte briOld = 0; +byte briT = 0; +byte briIT = 0; +byte briLast = 127; //brightness before turned off. Used for toggle function + +//button +bool buttonPressedBefore = false; +unsigned long buttonPressedTime = 0; + +//notifications +bool notifyDirectDefault = notifyDirect; +bool receiveNotifications = true; +unsigned long notificationSentTime = 0; +byte notificationSentCallMode = 0; +bool notificationTwoRequired = false; + +//effects +byte effectCurrent = effectDefault; +byte effectSpeed = effectSpeedDefault; +byte effectIntensity = effectIntensityDefault; +byte effectPalette = effectPaletteDefault; + +//network +bool onlyAP = false; //only Access Point active, no connection to home network bool udpConnected = false, udpRgbConnected = false; + +//ui style char cssCol[9][5]={"","","","","",""}; -char cssFont[33]="Verdana"; String cssColorString=""; -//NTP stuff -bool ntpConnected = false; -byte currentTimezone = 0; -time_t local = 0; -int utcOffsetSecs = 0; +bool showWelcomePage = false; //hue char hueError[25] = "Inactive"; @@ -180,25 +277,12 @@ uint16_t hueFailCount = 0; float hueXLast=0, hueYLast=0; uint16_t hueHueLast=0, hueCtLast=0; byte hueSatLast=0, hueBriLast=0; -long hueLastRequestSent = 0; -uint32_t huePollIntervalMsTemp = huePollIntervalMs; +unsigned long hueLastRequestSent = 0; +unsigned long huePollIntervalMsTemp = huePollIntervalMs; +bool hueAttempt = false; -//blynk -char blynkApiKey[36] = ""; -bool blynkEnabled = false; - -//e1.31 -bool e131Enabled = true; -byte e131Universe = 1; -bool e131Multicast = false; - -//overlay stuff -byte overlayDefault = 0; -byte overlayCurrent = 0; -byte overlayMin = 0, overlayMax = ledCount-1; -byte analogClock12pixel = 0; -bool analogClockSecondsTrail = false; -bool analogClock5MinuteMarks = false; +//overlays +byte overlayCurrent = overlayDefault; byte overlaySpeed = 200; unsigned long overlayRefreshMs = 200; unsigned long overlayRefreshedTime; @@ -206,37 +290,36 @@ int overlayArr[6]; uint16_t overlayDur[6]; uint16_t overlayPauseDur[6]; int nixieClockI = -1; -bool nixiePause; -byte countdownYear=19, countdownMonth=1, countdownDay=1, countdownHour=0, countdownMin=0, countdownSec=0; //year is actual year -2000 -bool countdownOverTriggered = true; +bool nixiePause = false; + //cronixie -char cronixieDisplay[] = "HHMMSS"; byte dP[]{0,0,0,0,0,0}; -bool useAMPM = false; -bool cronixieBacklight = true; -bool countdownMode = false; bool cronixieInit = false; +//countdown +unsigned long countdownTime = 1514764800L; +bool countdownOverTriggered = true; + +//blynk +bool blynkEnabled = false; + +//preset cycling bool presetCyclingEnabled = false; byte presetCycleMin = 1, presetCycleMax = 5; uint16_t presetCycleTime = 1250; unsigned long presetCycledTime = 0; byte presetCycCurr = presetCycleMin; bool presetApplyBri = true, presetApplyCol = true, presetApplyFx = true; bool saveCurrPresetCycConf = false; -uint16_t arlsTimeoutMillis = 2500; -bool arlsTimeout = false; -bool receiveDirect = true, enableRealtimeUI = false; -bool arlsDisableGammaCorrection = true, arlsForceMaxBri = false; + +//realtime +bool realtimeActive = false; IPAddress realtimeIP = (0,0,0,0); -unsigned long arlsTimeoutTime = 0; +unsigned long realtimeTimeout = 0; + +//auxiliary debug pin byte auxTime = 0; unsigned long auxStartTime = 0; bool auxActive = false, auxActiveBefore = false; -bool showWelcomePage = false; - -bool useGammaCorrectionBri = false; -bool useGammaCorrectionRGB = true; -int arlsOffset = 0; //alexa udp WiFiUDP UDP; @@ -248,50 +331,58 @@ String escapedMac; DNSServer dnsServer; bool dnsActive = false; +//network time +bool ntpConnected = false; +time_t local = 0; +unsigned long ntpLastSyncTime = 999000000L; +unsigned long ntpPacketSentTime = 999000000L; +IPAddress ntpServerIP; +unsigned int ntpLocalPort = 2390; +#define NTP_PACKET_SIZE 48 + //string temp buffer #define OMAX 1750 char obuf[OMAX]; uint16_t olen = 0; +//server library objects #ifdef ARDUINO_ARCH_ESP32 WebServer server(80); #else ESP8266WebServer server(80); #endif -E131 e131; HTTPClient hueClient; ESP8266HTTPUpdateServer httpUpdater; + +//udp interface objects WiFiUDP notifierUdp, rgbUdp; WiFiUDP ntpUdp; -IPAddress ntpServerIP; -unsigned int ntpLocalPort = 2390; -#define NTP_PACKET_SIZE 48 -unsigned long ntpLastSyncTime = 999000000L; -unsigned long ntpPacketSentTime = 999000000L; +E131 e131; +//led fx library object WS2812FX strip = WS2812FX(); +//debug macros #ifdef DEBUG #define DEBUG_PRINT(x) Serial.print (x) #define DEBUG_PRINTLN(x) Serial.println (x) #define DEBUG_PRINTF(x) Serial.printf (x) + unsigned long debugTime = 0; + int lastWifiState = 3; + unsigned long wifiStateChangedTime = 0; #else #define DEBUG_PRINT(x) #define DEBUG_PRINTLN(x) #define DEBUG_PRINTF(x) #endif +//filesystem #ifdef USEFS #include ; File fsUploadFile; #endif -#ifdef DEBUG -long debugTime = 0; -int lastWifiState = 3; -long wifiStateChangedTime = 0; -#endif - +//gamma 2.4 lookup table used for color correction const byte gamma8[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, @@ -312,8 +403,11 @@ const byte gamma8[] = { String txd = "Please disable OTA Lock in security settings!"; +//function prototypes void serveMessage(int,String,String,int=255); + +//turns all LEDs off and restarts ESP void reset() { briT = 0; @@ -322,7 +416,9 @@ void reset() ESP.restart(); } -bool oappend(char* txt) //append new c string to temp buffer efficiently + +//append new c string to temp buffer efficiently +bool oappend(char* txt) { uint16_t len = strlen(txt); if (olen + len >= OMAX) return false; //buffer full @@ -331,30 +427,38 @@ bool oappend(char* txt) //append new c string to temp buffer efficiently return true; } -bool oappendi(int i) //append new number to temp buffer efficiently + +//append new number to temp buffer efficiently +bool oappendi(int i) { char s[11]; sprintf(s,"%ld", i); return oappend(s); } + +//boot starts here void setup() { wledInit(); } + +//main program loop void loop() { server.handleClient(); handleSerial(); handleNotifications(); handleTransitions(); userLoop(); + yield(); handleButton(); handleNetworkTime(); if (aOtaEnabled) ArduinoOTA.handle(); handleAlexa(); handleOverlays(); - if (!arlsTimeout) //block stuff if WARLS/Adalight is enabled + + if (!realtimeActive) //block stuff if WARLS/Adalight is enabled { if (dnsActive) dnsServer.processNextRequest(); handleHue(); @@ -363,7 +467,7 @@ void loop() { if (briT) strip.service(); //do not update strip if off, prevents flicker on ESP32 } - //DEBUG + //DEBUG serial logging #ifdef DEBUG if (millis() - debugTime > 5000) { @@ -377,7 +481,7 @@ void loop() { wifiStateChangedTime = millis(); } lastWifiState = WiFi.status(); - DEBUG_PRINT("Wifi state: "); DEBUG_PRINTLN(wifiStateChangedTime); + DEBUG_PRINT("State time: "); DEBUG_PRINTLN(wifiStateChangedTime); DEBUG_PRINT("NTP last sync: "); DEBUG_PRINTLN(ntpLastSyncTime); DEBUG_PRINT("Client IP: "); DEBUG_PRINTLN(WiFi.localIP()); debugTime = millis(); diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino index 1536d788f..161b87de0 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled01_eeprom.ino @@ -113,7 +113,7 @@ void saveSettingsToEEPROM() { EEPROM.write(i, alexaInvocationName[i-334]); } - EEPROM.write(366, alexaNotify); + EEPROM.write(366, notifyAlexa); EEPROM.write(367, (arlsOffset>=0)); EEPROM.write(368, abs(arlsOffset)); EEPROM.write(369, turnOnAtBoot); @@ -141,7 +141,7 @@ void saveSettingsToEEPROM() EEPROM.write(396, (utcOffsetSecs<0)); //is negative EEPROM.write(397, initLedsLast); EEPROM.write(398, (ledCount >> 8) & 0xFF); - EEPROM.write(399, disableSecTransition); + EEPROM.write(399, !enableSecTransition); for (int k=0;k<6;k++){ int in = 900+k*8; @@ -206,8 +206,8 @@ void saveSettingsToEEPROM() EEPROM.write(2190, (e131Universe >> 0) & 0xFF); EEPROM.write(2191, (e131Universe >> 8) & 0xFF); EEPROM.write(2192, e131Multicast); - EEPROM.write(2193, (arlsTimeoutMillis >> 0) & 0xFF); - EEPROM.write(2194, (arlsTimeoutMillis >> 8) & 0xFF); + EEPROM.write(2193, (realtimeTimeoutMs >> 0) & 0xFF); + EEPROM.write(2194, (realtimeTimeoutMs >> 8) & 0xFF); EEPROM.write(2195, arlsForceMaxBri); EEPROM.write(2196, arlsDisableGammaCorrection); @@ -349,7 +349,7 @@ void loadSettingsFromEEPROM(bool first) alexaInvocationName[i-334] = EEPROM.read(i); if (alexaInvocationName[i-334] == 0) break; } - alexaNotify = EEPROM.read(366); + notifyAlexa = EEPROM.read(366); arlsOffset = EEPROM.read(368); if (!EEPROM.read(367)) arlsOffset = -arlsOffset; turnOnAtBoot = EEPROM.read(369); @@ -441,7 +441,7 @@ void loadSettingsFromEEPROM(bool first) { e131Universe = ((EEPROM.read(2190) << 0) & 0xFF) + ((EEPROM.read(2191) << 8) & 0xFF00); e131Multicast = EEPROM.read(2192); - arlsTimeoutMillis = ((EEPROM.read(2193) << 0) & 0xFF) + ((EEPROM.read(2194) << 8) & 0xFF00); + realtimeTimeoutMs = ((EEPROM.read(2193) << 0) & 0xFF) + ((EEPROM.read(2194) << 8) & 0xFF00); arlsForceMaxBri = EEPROM.read(2195); arlsDisableGammaCorrection = EEPROM.read(2196); } @@ -486,7 +486,7 @@ void loadSettingsFromEEPROM(bool first) utcOffsetSecs = ((EEPROM.read(394) << 0) & 0xFF) + ((EEPROM.read(395) << 8) & 0xFF00); if (EEPROM.read(396)) utcOffsetSecs = -utcOffsetSecs; //negative initLedsLast = EEPROM.read(397); - disableSecTransition = EEPROM.read(399); + enableSecTransition = !EEPROM.read(399); //favorite setting (preset) memory (25 slots/ each 20byte) //400 - 899 reserved @@ -594,7 +594,7 @@ void applyMacro(byte index) String mc="win&"; mc += loadMacro(index+1); mc += "&IN"; //internal, no XML response - if (!macroNotify) mc += "&NN"; + if (!notifyMacro) mc += "&NN"; String forbidden = "&M="; //dont apply if called by the macro itself to prevent loop /* * NOTE: loop is still possible if you call a different macro from a macro, which then calls the first macro again. diff --git a/wled00/wled02_xml.ino b/wled00/wled02_xml.ino index 8ca849688..8bb76bc93 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/wled02_xml.ino @@ -193,7 +193,7 @@ void getSettingsJS(byte subPage) //get values for settings form in javascript sappend('c',"TF",fadeTransition); sappend('v',"TD",transitionDelay); sappend('c',"PF",strip.paletteFade); - sappend('c',"T2",!disableSecTransition); + sappend('c',"T2",enableSecTransition); sappend('v',"BF",briMultiplier); sappend('v',"TB",nightlightTargetBri); sappend('v',"TL",nightlightDelayMins); @@ -233,14 +233,14 @@ void getSettingsJS(byte subPage) //get values for settings form in javascript sappend('c',"RD",receiveDirect); sappend('c',"EM",e131Multicast); sappend('v',"EU",e131Universe); - sappend('v',"ET",arlsTimeoutMillis); + sappend('v',"ET",realtimeTimeoutMs); sappend('c',"FB",arlsForceMaxBri); sappend('c',"RG",arlsDisableGammaCorrection); sappend('v',"WO",arlsOffset); sappend('c',"RU",enableRealtimeUI); sappend('c',"AL",alexaEnabled); sappends('s',"AI",alexaInvocationName); - sappend('c',"SA",alexaNotify); + sappend('c',"SA",notifyAlexa); sappends('s',"BK",(char*)((blynkEnabled)?"Hidden":"")); sappend('v',"H0",hueIP[0]); sappend('v',"H1",hueIP[1]); diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index e1268a4ac..12f996abb 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -172,7 +172,7 @@ void handleSettingsSet(byte subPage) } } strip.paletteFade = server.hasArg("PF"); - disableSecTransition = !server.hasArg("T2"); + enableSecTransition = server.hasArg("T2"); if (server.hasArg("TB")) { nightlightTargetBri = server.arg("TB").toInt(); @@ -239,12 +239,12 @@ void handleSettingsSet(byte subPage) if (server.hasArg("EU")) { int i = server.arg("EU").toInt(); - if (i > 0 && i <= 63999) arlsTimeoutMillis = i; + if (i > 0 && i <= 63999) realtimeTimeoutMs = i; } if (server.hasArg("ET")) { int i = server.arg("ET").toInt(); - if (i > 99 && i <= 65000) arlsTimeoutMillis = i; + if (i > 99 && i <= 65000) realtimeTimeoutMs = i; } arlsForceMaxBri = server.hasArg("FB"); arlsDisableGammaCorrection = server.hasArg("RG"); @@ -256,7 +256,7 @@ void handleSettingsSet(byte subPage) enableRealtimeUI = server.hasArg("RU"); alexaEnabled = server.hasArg("AL"); if (server.hasArg("AI")) strcpy(alexaInvocationName,server.arg("AI").c_str()); - alexaNotify = server.hasArg("SA"); + notifyAlexa = server.hasArg("SA"); if (server.hasArg("BK") && !server.arg("BK").equals("Hidden")) {strcpy(blynkApiKey,server.arg("BK").c_str()); initBlynk(blynkApiKey);} notifyHue = server.hasArg("SH"); for (int i=0;i<4;i++){ diff --git a/wled00/wled04_file.ino b/wled00/wled04_file.ino index 66a103471..955a499ff 100644 --- a/wled00/wled04_file.ino +++ b/wled00/wled04_file.ino @@ -7,12 +7,12 @@ void handleSerial() { if (Serial.find("Ada")) { - if (!arlsTimeout){ + if (!realtimeActive){ if (bri == 0) strip.setBrightness(briLast); strip.setRange(0, ledCount-1, 0); strip.setMode(0); } - arlsLock(arlsTimeoutMillis); + arlsLock(realtimeTimeoutMs); delay(1); byte hi = Serial.read(); byte ledc = Serial.read(); diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index 58bd73ee0..a717f7510 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -52,7 +52,7 @@ void wledInit() //start captive portal if (onlyAP || strlen(apSSID) > 0) { - dnsServer.setErrorReplyCode(DNSReplyCode::NoError); + //dnsServer.setErrorReplyCode(DNSReplyCode::NoError); dnsServer.start(53, "*", WiFi.softAPIP()); dnsActive = true; } @@ -260,9 +260,11 @@ void wledInit() MDNS.addService("http", "tcp", 80); } - initBlynk(blynkApiKey); - - initE131(); + if (!onlyAP) + { + initBlynk(blynkApiKey); + initE131(); + } if (initLedsLast) initStrip(); userBegin(); @@ -428,7 +430,7 @@ void serveIndex() if (uiConfiguration == 0) serveMobile = checkClientIsMobile(server.header("User-Agent")); else if (uiConfiguration == 2) serveMobile = true; - if (!arlsTimeout || enableRealtimeUI) //do not serve while receiving realtime + if (!realtimeActive || enableRealtimeUI) //do not serve while receiving realtime { if (serveMobile) { @@ -484,7 +486,7 @@ void serveMessage(int code, String headl, String subl="", int optionType) void serveSettings(byte subPage) { //0: menu 1: wifi 2: leds 3: ui 4: sync 5: time 6: sec 255: welcomepage - if (!arlsTimeout || enableRealtimeUI) //do not serve while receiving realtime + if (!realtimeActive || enableRealtimeUI) //do not serve while receiving realtime { int pl0, pl1; switch (subPage) diff --git a/wled00/wled07_notify.ino b/wled00/wled07_notify.ino index 7bff20128..e8ab5eabb 100644 --- a/wled00/wled07_notify.ino +++ b/wled00/wled07_notify.ino @@ -54,12 +54,12 @@ void notify(byte callMode, bool followUp=false) void arlsLock(uint32_t timeoutMs) { - if (!arlsTimeout){ + if (!realtimeActive){ strip.setRange(0, ledCount-1, 0); strip.setMode(0); } - arlsTimeout = true; - arlsTimeoutTime = millis() + timeoutMs; + realtimeActive = true; + realtimeTimeout = millis() + timeoutMs; if (arlsForceMaxBri) strip.setBrightness(255); } @@ -83,7 +83,7 @@ void handleNotifications() if(e131Enabled) { uint16_t len = e131.parsePacket(); if (len && e131.universe == e131Universe) { - arlsLock(arlsTimeoutMillis); + arlsLock(realtimeTimeoutMs); if (len > ledCount) len = ledCount; for (uint16_t i = 0; i < len; i++) { int j = i * 3; @@ -95,11 +95,11 @@ void handleNotifications() } //unlock strip when realtime UDP times out - if (arlsTimeout && millis() > arlsTimeoutTime) + if (realtimeActive && millis() > realtimeTimeout) { strip.unlockAll(); strip.setBrightness(bri); - arlsTimeout = false; + realtimeActive = false; strip.setMode(effectCurrent); realtimeIP[0] = 0; } @@ -116,7 +116,7 @@ void handleNotifications() if (packetSize > 1026 || packetSize < 3) return; byte udpIn[packetSize]; rgbUdp.read(udpIn, packetSize); - arlsLock(arlsTimeoutMillis); + arlsLock(realtimeTimeoutMs); uint16_t id = 0; for (uint16_t i = 0; i < packetSize -2; i += 3) { @@ -133,7 +133,7 @@ void handleNotifications() { byte udpIn[packetSize]; notifierUdp.read(udpIn, packetSize); - if (udpIn[0] == 0 && !arlsTimeout && receiveNotifications) //wled notifier, block if realtime packets active + if (udpIn[0] == 0 && !realtimeActive && receiveNotifications) //wled notifier, block if realtime packets active { if (receiveNotificationColor) { @@ -188,7 +188,7 @@ void handleNotifications() if (packetSize > 1) { if (udpIn[1] == 0) { - arlsTimeout = false; + realtimeActive = false; } else { arlsLock(udpIn[1]*1000); } diff --git a/wled00/wled08_led.ino b/wled00/wled08_led.ino index fba4e5264..c71deda14 100644 --- a/wled00/wled08_led.ino +++ b/wled00/wled08_led.ino @@ -3,7 +3,7 @@ */ void setAllLeds() { - if (!arlsTimeout || !arlsForceMaxBri) + if (!realtimeActive || !arlsForceMaxBri) { double d = briT*briMultiplier; int val = d/100; @@ -15,7 +15,7 @@ void setAllLeds() { strip.setBrightness(val); } } - if (disableSecTransition) + if (!enableSecTransition) { for (byte i = 0; i<3; i++) { @@ -152,7 +152,6 @@ void handleTransitions() whiteSecT = whiteSecOld +((whiteSec - whiteSecOld )*tper); briT = briOld +((bri - briOld )*tper); } - //TODO: properly remove sweep transition if (fadeTransition) setAllLeds(); } } diff --git a/wled00/wled12_alexa.ino b/wled00/wled12_alexa.ino index bece759a8..f501a6947 100644 --- a/wled00/wled12_alexa.ino +++ b/wled00/wled12_alexa.ino @@ -47,7 +47,7 @@ void alexaOn() { if (macroAlexaOn == 0) { - handleSet((alexaNotify)?"win&T=1&IN":"win&T=1&NN&IN"); + handleSet((notifyAlexa)?"win&T=1&IN":"win&T=1&NN&IN"); } else { applyMacro(macroAlexaOn); @@ -60,7 +60,7 @@ void alexaOff() { if (macroAlexaOff == 0) { - handleSet((alexaNotify)?"win&T=0&IN":"win&T=0&NN&IN"); + handleSet((notifyAlexa)?"win&T=0&IN":"win&T=0&NN&IN"); } else { applyMacro(macroAlexaOff); @@ -78,7 +78,7 @@ void alexaDim(byte briL) server.send(200, "application/json", obuf); - String ct = (alexaNotify)?"win&IN&A=":"win&NN&IN&A="; + String ct = (notifyAlexa)?"win&IN&A=":"win&NN&IN&A="; if (briL < 255) { ct = ct + (briL+1); From bb7f673ff9cc023e3906e7a9e1603e5b66e146e1 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Mon, 17 Sep 2018 11:15:08 +0200 Subject: [PATCH 08/22] Re-added support for 512K flash ESPs --- wled00/htmls00.h | 3 +- wled00/htmls01.h | 22 +++ wled00/htmls02.h | 24 ++- wled00/palettes.h | 5 + wled00/wled00.ino | 11 +- wled00/wled03_set.ino | 309 +++++++++++++------------------------ wled00/wled05_init.ino | 6 +- wled00/wled11_ol.ino | 8 + wled00/wled13_cronixie.ino | 6 + 9 files changed, 181 insertions(+), 213 deletions(-) diff --git a/wled00/htmls00.h b/wled00/htmls00.h index 60d995202..32d153868 100644 --- a/wled00/htmls00.h +++ b/wled00/htmls00.h @@ -64,10 +64,11 @@ var displayElement=pr.getElementsByClassName("rangeValues")[0];displayElement.in )====="; #else const char PAGE_indexM[] PROGMEM = R"=====( -The Mobile UI is not supported due to limited flash storage. Please go to IP/settings/ui and change the UI mode to "Classic". +Mobile UI is unsupported (limited storage). Go to /settings/ui and change UI mode to "Classic". )====="; #endif + /* * Classic UI Index html */ diff --git a/wled00/htmls01.h b/wled00/htmls01.h index 52d5dbb60..43f11927a 100644 --- a/wled00/htmls01.h +++ b/wled00/htmls01.h @@ -1,10 +1,14 @@ /* * Settings html */ + +//common CSS of settings pages const char PAGE_settingsCss[] PROGMEM = R"=====( body{font-family:var(--cFn),sans-serif;text-align:center;background:var(--cCol);color:var(--tCol);line-height:200%;margin:0;background-attachment:fixed}hr{border-color:var(--dCol);filter:drop-shadow(-5px -5px 5px var(--sCol))}button{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:.3ch solid var(--bCol);display:inline-block;filter:drop-shadow(-5px -5px 5px var(--sCol));font-size:20px;margin:8px;margin-top:12px}.helpB{text-align:left;position:absolute;width:60px}input{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:.5ch solid var(--bCol);filter:drop-shadow(-5px -5px 5px var(--sCol))}input[type=number]{width:3em}select{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:0.5ch solid var(--bCol);filter:drop-shadow( -5px -5px 5px var(--sCol) );} )====="; + +//settings menu const char PAGE_settings0[] PROGMEM = R"=====( WLED Settings @@ -26,11 +30,14 @@ body{text-align:center;background:var(--cCol);height:100%;margin:0;background-at )====="; + +//wifi settings const char PAGE_settings_wifi0[] PROGMEM = R"=====( WiFi Settings )====="; + const char PAGE_msg1[] PROGMEM = R"=====( button{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:.3ch solid var(--bCol);display:inline-block;filter:drop-shadow(-5px -5px 5px var(--sCol));font-size:20px;margin:8px;margin-top:12px}body{font-family:var(--cFn),sans-serif;text-align:center;background:var(--cCol);color:var(--tCol);line-height:200%;margin:0;background-attachment:fixed} )====="; + +//new user welcome page +#ifndef WLED_FLASH_512K_MODE const char PAGE_welcome0[] PROGMEM = R"=====( WLED Welcome! )====="; + const char PAGE_welcome1[] PROGMEM = R"=====( body{font-family:var(--cFn),sans-serif;text-align:center;background:linear-gradient(var(--bCol),black);height:100%;margin:0;background-repeat:no-repeat;background-attachment: fixed;color: var(--tCol);}svg {fill: var(--dCol);} @@ -41,6 +50,11 @@ Connect the module to your local WiFi here!

        Just trying this out in AP mode? Here are the controls.
        )====="; +#else +const char PAGE_welcome0[] PROGMEM = ""; +const char PAGE_welcome1[] PROGMEM = ""; +#endif + /* * SPIFFS editor html @@ -54,6 +68,8 @@ eval(function(p,a,c,k,e,r){e=function(c){return(c= 0 && i <= 255) apWaitTimeSecs = i; - } - if (server.hasArg("AS")) strcpy(apSSID,server.arg("AS").c_str()); + strcpy(clientSSID,server.arg("CS").c_str()); + if (server.arg("CP").charAt(0) != '*') strcpy(clientPass, server.arg("CP").c_str()); + + strcpy(cmDNS, server.arg("CM").c_str()); + + int t = server.arg("AT").toInt(); if (t > 9 && t <= 255) apWaitTimeSecs = t; + strcpy(apSSID, server.arg("AS").c_str()); apHide = server.hasArg("AH"); - if (server.hasArg("AP")) - { - if (server.arg("AP").charAt(0) != '*') strcpy(apPass,server.arg("AP").c_str()); - } - if (server.hasArg("AC")) - { - int chan = server.arg("AC").toInt(); - if (chan > 0 && chan < 14) apChannel = chan; - } - char k[3]; k[2] = 0; int j = 0; + if (server.arg("AP").charAt(0) != '*') strcpy(apPass, server.arg("AP").c_str()); + t = server.arg("AC").toInt(); if (t > 0 && t < 14) apChannel = t; + + char k[3]; k[2] = 0; for (int i = 0; i<4; i++) { - k[1] = i+48; + k[1] = i+48;//ascii 0,1,2,3 + k[0] = 'I'; //static IP - if (server.hasArg(k)) j = server.arg(k).toInt(); - if (j >= 0 && j <= 255) staticIP[i] = j; + staticIP[i] = server.arg(k).toInt(); + k[0] = 'G'; //gateway - if (server.hasArg(k)) j = server.arg(k).toInt(); - if (j >= 0 && j <= 255) staticGateway[i] = j; + staticGateway[i] = server.arg(k).toInt(); + k[0] = 'S'; //subnet - if (server.hasArg(k)) j = server.arg(k).toInt(); - if (j >= 0 && j <= 255) staticSubnet[i] = j; + staticSubnet[i] = server.arg(k).toInt(); } } //LED SETTINGS if (subPage == 2) { - if (server.hasArg("LC")) - { - int i = server.arg("LC").toInt(); - if (i > 0 && i <= 1200) ledCount = i; - //RMT eats up too much RAM - #ifdef ARDUINO_ARCH_ESP32 - if (ledCount > 600) ledCount = 600; - #endif - } + int t = server.arg("LC").toInt(); + if (t > 0 && t <= 1200) ledCount = t; + //RMT eats up too much RAM + #ifdef ARDUINO_ARCH_ESP32 + if (ledCount > 600) ledCount = 600; + #endif useRGBW = server.hasArg("EW"); autoRGBtoRGBW = server.hasArg("AW"); if (server.hasArg("IS")) //ignore settings and save current brightness, colors and fx as default @@ -81,141 +66,75 @@ void handleSettingsSet(byte subPage) colS[0] = col[0]; colS[1] = col[1]; colS[2] = col[2]; - if (useRGBW) whiteS = white; + colSecS[0] = colSec[0]; + colSecS[1] = colSec[1]; + colSecS[2] = colSec[2]; + whiteS = white; + whiteSecS = whiteSec; briS = bri; effectDefault = effectCurrent; effectSpeedDefault = effectSpeed; effectIntensityDefault = effectIntensity; effectPaletteDefault = effectPalette; } else { - if (server.hasArg("CR")) - { - int i = server.arg("CR").toInt(); - if (i >= 0 && i <= 255) colS[0] = i; - } - if (server.hasArg("CG")) - { - int i = server.arg("CG").toInt(); - if (i >= 0 && i <= 255) colS[1] = i; - } - if (server.hasArg("CB")) - { - int i = server.arg("CB").toInt(); - if (i >= 0 && i <= 255) colS[2] = i; - } - if (server.hasArg("SR")) - { - int i = server.arg("SR").toInt(); - if (i >= 0 && i <= 255) colSecS[0] = i; - } - if (server.hasArg("SG")) - { - int i = server.arg("SG").toInt(); - if (i >= 0 && i <= 255) colSecS[1] = i; - } - if (server.hasArg("SB")) - { - int i = server.arg("SB").toInt(); - if (i >= 0 && i <= 255) colSecS[2] = i; - } - if (server.hasArg("SW")) - { - int i = server.arg("SW").toInt(); - if (i >= 0 && i <= 255) whiteSecS = i; - } - if (server.hasArg("CW")) - { - int i = server.arg("CW").toInt(); - if (i >= 0 && i <= 255) whiteS = i; - } - if (server.hasArg("CA")) - { - int i = server.arg("CA").toInt(); - if (i >= 0 && i <= 255) briS = i; - } - if (server.hasArg("FX")) - { - int i = server.arg("FX").toInt(); - if (i >= 0 && i <= 255) effectDefault = i; - } - if (server.hasArg("SX")) - { - int i = server.arg("SX").toInt(); - if (i >= 0 && i <= 255) effectSpeedDefault = i; - } - if (server.hasArg("IX")) - { - int i = server.arg("IX").toInt(); - if (i >= 0 && i <= 255) effectIntensityDefault = i; - } - if (server.hasArg("FP")) - { - int i = server.arg("FP").toInt(); - if (i >= 0 && i <= 255) effectPaletteDefault = i; - } + colS[0] = server.arg("CR").toInt(); + colS[1] = server.arg("CG").toInt(); + colS[2] = server.arg("CB").toInt(); + colSecS[0] = server.arg("SR").toInt(); + colSecS[1] = server.arg("SG").toInt(); + colSecS[2] = server.arg("SB").toInt(); + whiteS = server.arg("CW").toInt(); + whiteSecS = server.arg("SW").toInt(); + briS = server.arg("CA").toInt(); + effectDefault = server.arg("FX").toInt(); + effectSpeedDefault = server.arg("SX").toInt(); + effectIntensityDefault = server.arg("IX").toInt(); + effectPaletteDefault = server.arg("FP").toInt(); } saveCurrPresetCycConf = server.hasArg("PC"); turnOnAtBoot = server.hasArg("BO"); - if (server.hasArg("BP")) - { - int i = server.arg("BP").toInt(); - if (i >= 0 && i <= 25) bootPreset = i; - } + t = server.arg("BP").toInt(); + if (t <= 25) bootPreset = t; useGammaCorrectionBri = server.hasArg("GB"); useGammaCorrectionRGB = server.hasArg("GC"); + fadeTransition = server.hasArg("TF"); - if (server.hasArg("TD")) - { - int i = server.arg("TD").toInt(); - if (i > 0){ - transitionDelay = i; - } - } + t = server.arg("TD").toInt(); + if (t > 0) transitionDelay = t; strip.paletteFade = server.hasArg("PF"); enableSecTransition = server.hasArg("T2"); - if (server.hasArg("TB")) - { - nightlightTargetBri = server.arg("TB").toInt(); - } - if (server.hasArg("TL")) - { - int i = server.arg("TL").toInt(); - if (i > 0) nightlightDelayMins = i; - } + + nightlightTargetBri = server.arg("TB").toInt(); + t = server.arg("TL").toInt(); + if (t > 0) nightlightDelayMins = t; nightlightFade = server.hasArg("TW"); - reverseMode = server.hasArg("RV"); - if (server.hasArg("PB")) - { - int i = server.arg("PB").toInt(); - if (i >= 0){ - strip.paletteBlend = i; - } - } + + t = server.arg("PB").toInt(); + if (t >= 0 && t < 4) strip.paletteBlend = t; initLedsLast = server.hasArg("EI"); + reverseMode = server.hasArg("RV"); strip.setReverseMode(reverseMode); skipFirstLed = server.hasArg("SL"); - if (server.hasArg("BF")) - { - int i = server.arg("BF").toInt(); - if (i > 0) briMultiplier = i; - } + t = server.arg("BF").toInt(); + if (t > 0) briMultiplier = t; } //UI if (subPage == 3) { - if (server.hasArg("UI")) uiConfiguration = server.arg("UI").toInt(); - if (server.hasArg("DS")) strcpy(serverDescription,server.arg("DS").c_str()); + int t = server.arg("UI").toInt(); + if (t >= 0 && t < 3) uiConfiguration = t; + strcpy(serverDescription, server.arg("DS").c_str()); useHSBDefault = server.hasArg("MD"); useHSB = useHSBDefault; - if (server.hasArg("TH")) currentTheme = server.arg("TH").toInt(); + currentTheme = server.arg("TH").toInt(); char k[3]; k[0]='C'; k[2]=0; for(int i=0;i<6;i++) { k[1] = i+48; - if (server.hasArg(k)) strcpy(cssCol[i],server.arg(k).c_str()); + strcpy(cssCol[i],server.arg(k).c_str()); } - if (server.hasArg("CF")) strcpy(cssFont,server.arg("CF").c_str()); + strcpy(cssFont,server.arg("CF").c_str()); buildCssColorString(); } @@ -223,10 +142,8 @@ void handleSettingsSet(byte subPage) if (subPage == 4) { buttonEnabled = server.hasArg("BT"); - if (server.hasArg("UP")) - { - udpPort = server.arg("UP").toInt(); - } + int t = server.arg("UP").toInt(); + if (t > 0) udpPort = t; receiveNotificationBrightness = server.hasArg("RB"); receiveNotificationColor = server.hasArg("RC"); receiveNotificationEffects = server.hasArg("RX"); @@ -235,45 +152,36 @@ void handleSettingsSet(byte subPage) notifyDirect = notifyDirectDefault; notifyButton = server.hasArg("SB"); notifyTwice = server.hasArg("S2"); + receiveDirect = server.hasArg("RD"); - if (server.hasArg("EU")) - { - int i = server.arg("EU").toInt(); - if (i > 0 && i <= 63999) realtimeTimeoutMs = i; - } - if (server.hasArg("ET")) - { - int i = server.arg("ET").toInt(); - if (i > 99 && i <= 65000) realtimeTimeoutMs = i; - } + t = server.arg("EU").toInt(); + if (t > 0 && t <= 63999) e131Universe = t; + t = server.arg("ET").toInt(); + if (t > 99 && t <= 65000) realtimeTimeoutMs = t; arlsForceMaxBri = server.hasArg("FB"); arlsDisableGammaCorrection = server.hasArg("RG"); - if (server.hasArg("WO")) - { - int i = server.arg("WO").toInt(); - if (i >= -255 && i <= 255) arlsOffset = i; - } + t = server.arg("WO").toInt(); + if (t >= -255 && t <= 255) arlsOffset = t; enableRealtimeUI = server.hasArg("RU"); + alexaEnabled = server.hasArg("AL"); - if (server.hasArg("AI")) strcpy(alexaInvocationName,server.arg("AI").c_str()); + strcpy(alexaInvocationName, server.arg("AI").c_str()); notifyAlexa = server.hasArg("SA"); + if (server.hasArg("BK") && !server.arg("BK").equals("Hidden")) {strcpy(blynkApiKey,server.arg("BK").c_str()); initBlynk(blynkApiKey);} + notifyHue = server.hasArg("SH"); for (int i=0;i<4;i++){ String a = "H"+String(i); - if (server.hasArg(a)) - hueIP[i] = server.arg(a).toInt(); - } - if (server.hasArg("HL")) - { - int i = server.arg("HL").toInt(); - if (i > 0) huePollLightId = i; - } - if (server.hasArg("HI")) - { - int i = server.arg("HI").toInt(); - if (i > 50) huePollIntervalMs = i; + hueIP[i] = server.arg(a).toInt(); } + + t = server.arg("HL").toInt(); + if (t > 0) huePollLightId = t; + + t = server.arg("HI").toInt(); + if (t > 50) huePollIntervalMs = t; + hueApplyOnOff = server.hasArg("HO"); hueApplyBri = server.hasArg("HB"); hueApplyColor = server.hasArg("HC"); @@ -293,22 +201,22 @@ void handleSettingsSet(byte subPage) { ntpEnabled = server.hasArg("NT"); useAMPM = !server.hasArg("CF"); - if (server.hasArg("TZ")) currentTimezone = server.arg("TZ").toInt(); - if (server.hasArg("UO")) utcOffsetSecs = server.arg("UO").toInt(); + currentTimezone = server.arg("TZ").toInt(); + utcOffsetSecs = server.arg("UO").toInt(); if (ntpEnabled && WiFi.status() == WL_CONNECTED && !ntpConnected) ntpConnected = ntpUdp.begin(ntpLocalPort); //start if not already connected if (server.hasArg("OL")){ overlayDefault = server.arg("OL").toInt(); overlayCurrent = overlayDefault; - ; } - if (server.hasArg("O1")) overlayMin = server.arg("O1").toInt(); - if (server.hasArg("O2")) overlayMax = server.arg("O2").toInt(); - if (server.hasArg("OM")) analogClock12pixel = server.arg("OM").toInt(); + + overlayMin = server.arg("O1").toInt(); + overlayMax = server.arg("O2").toInt(); + analogClock12pixel = server.arg("OM").toInt(); analogClock5MinuteMarks = server.hasArg("O5"); analogClockSecondsTrail = server.hasArg("OS"); - if (server.hasArg("CX")) strcpy(cronixieDisplay,server.arg("CX").c_str()); + strcpy(cronixieDisplay,server.arg("CX").c_str()); bool cbOld = cronixieBacklight; cronixieBacklight = server.hasArg("CB"); if (cbOld != cronixieBacklight && overlayCurrent == 4) @@ -316,25 +224,25 @@ void handleSettingsSet(byte subPage) strip.setCronixieBacklight(cronixieBacklight); overlayRefreshedTime = 0; } countdownMode = server.hasArg("CE"); - if (server.hasArg("CY")) countdownYear = server.arg("CY").toInt(); - if (server.hasArg("CI")) countdownMonth = server.arg("CI").toInt(); - if (server.hasArg("CD")) countdownDay = server.arg("CD").toInt(); - if (server.hasArg("CH")) countdownHour = server.arg("CH").toInt(); - if (server.hasArg("CM")) countdownMin = server.arg("CM").toInt(); - if (server.hasArg("CS")) countdownSec = server.arg("CS").toInt(); + countdownYear = server.arg("CY").toInt(); + countdownMonth = server.arg("CI").toInt(); + countdownDay = server.arg("CD").toInt(); + countdownHour = server.arg("CH").toInt(); + countdownMin = server.arg("CM").toInt(); + countdownSec = server.arg("CS").toInt(); for (int i=1;i<17;i++) { String a = "M"+String(i); if (server.hasArg(a)) saveMacro(i,server.arg(a),false); } - if (server.hasArg("MB")) macroBoot = server.arg("MB").toInt(); - if (server.hasArg("A0")) macroAlexaOn = server.arg("A0").toInt(); - if (server.hasArg("A1")) macroAlexaOff = server.arg("A1").toInt(); - if (server.hasArg("MP")) macroButton = server.arg("MP").toInt(); - if (server.hasArg("ML")) macroLongPress = server.arg("ML").toInt(); - if (server.hasArg("MC")) macroCountdown = server.arg("MC").toInt(); - if (server.hasArg("MN")) macroNl = server.arg("MN").toInt(); + macroBoot = server.arg("MB").toInt(); + macroAlexaOn = server.arg("A0").toInt(); + macroAlexaOff = server.arg("A1").toInt(); + macroButton = server.arg("MP").toInt(); + macroLongPress = server.arg("ML").toInt(); + macroCountdown = server.arg("MC").toInt(); + macroNl = server.arg("MN").toInt(); } //SECURITY @@ -375,9 +283,8 @@ void handleSettingsSet(byte subPage) bool handleSet(String req) { bool effectUpdated = false; - if (!(req.indexOf("win") >= 0)) { - return false; - } + if (!(req.indexOf("win") >= 0)) return false; + int pos = 0; DEBUG_PRINT("API req: "); DEBUG_PRINTLN(req); diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index a717f7510..00472f9a1 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -118,7 +118,7 @@ void wledInit() server.on("/settings/wifi", HTTP_POST, [](){ if (!(wifiLock && otaLock)) handleSettingsSet(1); - serveMessage(200,"WiFi settings saved.","Rebooting now... (takes ~20 seconds, wait for auto-redirect)",139); + serveMessage(200,"WiFi settings saved.","Rebooting now...",255); reset(); }); @@ -488,6 +488,10 @@ void serveSettings(byte subPage) //0: menu 1: wifi 2: leds 3: ui 4: sync 5: time 6: sec 255: welcomepage if (!realtimeActive || enableRealtimeUI) //do not serve while receiving realtime { + #ifdef WLED_FLASH_512K_MODE //disable welcome page if not enough storage + if (subPage == 255) {serveIndex(); return;} + #endif + int pl0, pl1; switch (subPage) { diff --git a/wled00/wled11_ol.ino b/wled00/wled11_ol.ino index 8695d51af..969a3e1a3 100644 --- a/wled00/wled11_ol.ino +++ b/wled00/wled11_ol.ino @@ -16,6 +16,7 @@ void initCronixie() } } + void _nixieDisplay(int num[], uint16_t dur[], uint16_t pausedur[], byte cnt) { strip.setRange(overlayMin, overlayMax, 0); @@ -116,6 +117,7 @@ void _nixieNumber(int number, int dur) } } + void handleOverlays() { if (millis() - overlayRefreshedTime > overlayRefreshMs) @@ -181,6 +183,10 @@ void _overlayAnalogClock() void _overlayNixieClock() { + #ifdef WLED_FLASH_512K_MODE + if (countdownMode) checkCountdown(); + #else + if (countdownMode) { _overlayNixieCountdown(); return; @@ -265,6 +271,7 @@ void _overlayNixieClock() { _nixieDisplay(overlayArr, overlayDur, overlayPauseDur, 6); } + #endif } void _overlayAnalogCountdown() @@ -312,6 +319,7 @@ void _overlayAnalogCountdown() overlayRefreshMs = 998; } + void _overlayNixieCountdown() { if (now() >= countdownTime) diff --git a/wled00/wled13_cronixie.ino b/wled00/wled13_cronixie.ino index ae31072af..845aaf690 100644 --- a/wled00/wled13_cronixie.ino +++ b/wled00/wled13_cronixie.ino @@ -19,6 +19,7 @@ byte getSameCodeLength(char code, int index, char const cronixieDisplay[]) void setCronixie() { + #ifndef WLED_FLASH_512K_MODE /* * digit purpose index * 0-9 | 0-9 (incl. random) @@ -139,11 +140,14 @@ void setCronixie() DEBUG_PRINTLN((int)dP[5]); _overlayCronixie(); //refresh + #endif } void _overlayCronixie() { if (countdownMode) checkCountdown(); + #ifndef WLED_FLASH_512K_MODE + byte h = hour(local); byte h0 = h; byte m = minute(local); @@ -208,4 +212,6 @@ void _overlayCronixie() } strip.setCronixieDigits(_digitOut); //strip.trigger(); //this has a drawback, no effects slower than RefreshMs. advantage: Quick update, not dependant on effect time + #endif } + From 5f59487a88ca85303ce6f1eaf2d6fed239603265 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sat, 22 Sep 2018 22:49:24 +0200 Subject: [PATCH 09/22] Added basic Time Activated Macros function Fixed Cronixie overlay 512K Flash mode (temporarily) unavailable --- wled00/WS2812FX.cpp | 3 +-- wled00/data/settings_time.htm | Bin 12424 -> 15046 bytes wled00/htmls01.h | 20 ++++++++++++++++++-- wled00/wled00.ino | 13 +++++++++++-- wled00/wled01_eeprom.ino | 19 ++++++++++++++++++- wled00/wled02_xml.ino | 9 +++++++++ wled00/wled03_set.ino | 20 ++++++++++++++++++-- wled00/wled10_ntp.ino | 18 ++++++++++++++++++ wled00/wled11_ol.ino | 5 +++-- 9 files changed, 96 insertions(+), 11 deletions(-) diff --git a/wled00/WS2812FX.cpp b/wled00/WS2812FX.cpp index f3d63cd14..8473b14b0 100644 --- a/wled00/WS2812FX.cpp +++ b/wled00/WS2812FX.cpp @@ -137,7 +137,6 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) case 7: bus->SetPixelColor((_skipFirstMode)?o+4:o+3,RgbwColor(r,g,b,w)); break; case 8: bus->SetPixelColor((_skipFirstMode)?o+10:o+9,RgbwColor(r,g,b,w)); break; case 9: bus->SetPixelColor((_skipFirstMode)?o+5:o+4,RgbwColor(r,g,b,w)); break; - default: break; } } } @@ -150,7 +149,7 @@ void WS2812FX::setReverseMode(bool b) void WS2812FX::driverModeCronixie(bool b) { _cronixieMode = b; - if (b) _segments[0].stop = 5; + _segments[0].stop = (b) ? 5 : _length-1; } void WS2812FX::setCronixieBacklight(bool b) diff --git a/wled00/data/settings_time.htm b/wled00/data/settings_time.htm index 793811727cd085ab5b6a94dec5db173285f9d89a..3c06dff4a85c7a074c8271a376c0309c60f2df2b 100644 GIT binary patch delta 710 zcmeB3JXX4aOM7#Iwi(Oh4@!LXAq<%exeTccx(v6|R9!q4b2*0QYEwK>z>% delta 82 zcmX?B+L5?{OM9}PK8LLWLo!1?Lmop3LlKb90m4*<6b1!`T!uuT3`ndP$OEa#Wyl1o XO9%3bfjp2N#mzsp+*u}HvHS!8Kn)bC diff --git a/wled00/htmls01.h b/wled00/htmls01.h index 43f11927a..b9a2b8d25 100644 --- a/wled00/htmls01.h +++ b/wled00/htmls01.h @@ -345,12 +345,28 @@ Define API macros here:
        15:
        16:

        Use 0 for the default action instead of a macro
        -Time controlled macros coming soon!
        Boot Macro:
        Alexa On/Off Macros:
        Button Macro: Long Press:
        Countdown-Over Macro:
        -Timed-Light-Over Macro:
        +Timed-Light-Over Macro:
        +Time-Controlled Macros (Hours/Minutes > Macro):
        + +>
        + +>
        + +>
        + +>
        + +>
        + +>
        + +>
        + +>
        diff --git a/wled00/wled00.ino b/wled00/wled00.ino index f383d0a69..508a980fe 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -9,7 +9,8 @@ //ESP8266-01 got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.3.0 and the setting 512K(64K SPIFFS). //Uncomment the following line to disable some features (currently Mobile UI, welcome page and single digit + cronixie overlays) to compile for ESP8266-01 -#define WLED_FLASH_512K_MODE +//#define WLED_FLASH_512K_MODE +//CURRENTLY NOT WORKING //library inclusions @@ -43,7 +44,7 @@ //version code in format yymmddb (b = daily build) -#define VERSION 1809162 +#define VERSION 1809222 char versionString[] = "0.8.0-a"; @@ -299,6 +300,14 @@ bool cronixieInit = false; unsigned long countdownTime = 1514764800L; bool countdownOverTriggered = true; +//timer +byte lastTimerMinute = 0; +byte timerHours[] = {0,0,0,0,0,0,0,0}; +byte timerMinutes[] = {0,0,0,0,0,0,0,0}; +byte timerMacro[] = {0,0,0,0,0,0,0,0}; +byte timerWeekday[] = {255,255,255,255,255,255,255,255}; //weekdays to activate on +//bit pattern of arr elem: 0b11111111: sat,fri,thu,wed,tue,mon,sun,validity + //blynk bool blynkEnabled = false; diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino index 161b87de0..235a2b36b 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled01_eeprom.ino @@ -234,6 +234,14 @@ void saveSettingsToEEPROM() { EEPROM.write(i, blynkApiKey[i-2220]); } + + for (int i = 0; i < 8; ++i) + { + EEPROM.write(2260 + i, timerHours[i] ); + EEPROM.write(2270 + i, timerMinutes[i]); + EEPROM.write(2280 + i, timerWeekday[i]); + EEPROM.write(2290 + i, timerMacro[i] ); + } EEPROM.commit(); } @@ -448,8 +456,17 @@ void loadSettingsFromEEPROM(bool first) if (lastEEPROMversion > 7) { - strip.paletteFade = EEPROM.read(374); + strip.paletteFade = EEPROM.read(374); strip.paletteBlend = EEPROM.read(382); + + for (int i = 0; i < 8; ++i) + { + timerHours[i] = EEPROM.read(2260 + i); + timerMinutes[i] = EEPROM.read(2270 + i); + timerWeekday[i] = EEPROM.read(2280 + i); + if (timerWeekday[i] == 0) timerWeekday[i] = 255; + timerMacro[i] = EEPROM.read(2290 + i); + } } receiveDirect = !EEPROM.read(2200); diff --git a/wled00/wled02_xml.ino b/wled00/wled02_xml.ino index 8bb76bc93..9dfc24511 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/wled02_xml.ino @@ -292,6 +292,15 @@ void getSettingsJS(byte subPage) //get values for settings form in javascript sappend('v',"ML",macroLongPress); sappend('v',"MC",macroCountdown); sappend('v',"MN",macroNl); + + k[2] = 0; //Time macros + for (int i = 0; i<8; i++) + { + k[1] = 48+i; //ascii 0,1,2,3 + k[0] = 'H'; sappend('v',k,timerHours[i]); + k[0] = 'N'; sappend('v',k,timerMinutes[i]); + k[0] = 'T'; sappend('v',k,timerMacro[i]); + } } if (subPage == 6) diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index cae759eb0..714c8f5a0 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -219,7 +219,7 @@ void handleSettingsSet(byte subPage) strcpy(cronixieDisplay,server.arg("CX").c_str()); bool cbOld = cronixieBacklight; cronixieBacklight = server.hasArg("CB"); - if (cbOld != cronixieBacklight && overlayCurrent == 4) + if (cbOld != cronixieBacklight && overlayCurrent == 3) { strip.setCronixieBacklight(cronixieBacklight); overlayRefreshedTime = 0; } @@ -236,6 +236,7 @@ void handleSettingsSet(byte subPage) String a = "M"+String(i); if (server.hasArg(a)) saveMacro(i,server.arg(a),false); } + macroBoot = server.arg("MB").toInt(); macroAlexaOn = server.arg("A0").toInt(); macroAlexaOff = server.arg("A1").toInt(); @@ -243,6 +244,21 @@ void handleSettingsSet(byte subPage) macroLongPress = server.arg("ML").toInt(); macroCountdown = server.arg("MC").toInt(); macroNl = server.arg("MN").toInt(); + + char k[3]; k[2] = 0; + for (int i = 0; i<8; i++) + { + k[1] = i+48;//ascii 0,1,2,3 + + k[0] = 'H'; //timer hours + timerHours[i] = server.arg(k).toInt(); + + k[0] = 'N'; //minutes + timerMinutes[i] = server.arg(k).toInt(); + + k[0] = 'T'; //macros + timerMacro[i] = server.arg(k).toInt(); + } } //SECURITY @@ -687,7 +703,7 @@ bool handleSet(String req) { cronixieBacklight = false; } - if (overlayCurrent == 4) strip.setCronixieBacklight(cronixieBacklight); + if (overlayCurrent == 3) strip.setCronixieBacklight(cronixieBacklight); overlayRefreshedTime = 0; } pos = req.indexOf("U0="); //user var 0 diff --git a/wled00/wled10_ntp.ino b/wled00/wled10_ntp.ino index 2656a1062..31f335810 100644 --- a/wled00/wled10_ntp.ino +++ b/wled00/wled10_ntp.ino @@ -166,3 +166,21 @@ bool checkCountdown() return false; } +void checkTimers() +{ + if (lastTimerMinute != minute(local)) //only check once a new minute begins + { + lastTimerMinute = minute(local); + for (uint8_t i = 0; i < 8; i++) + { + if (timerMacro[i] != 0 + && (timerHours[i] == hour(local) || timerHours[i] == 24) //if hour is set to 24, activate every hour + && timerMinutes[i] == minute(local) + && timerWeekday[i] >> weekday(local) & 0x01) //timer should activate at current day of week + { + applyMacro(timerMacro[i]); + } + } + } +} + diff --git a/wled00/wled11_ol.ino b/wled00/wled11_ol.ino index 969a3e1a3..a3ac6d166 100644 --- a/wled00/wled11_ol.ino +++ b/wled00/wled11_ol.ino @@ -3,13 +3,13 @@ */ void initCronixie() { - if (overlayCurrent == 4 && !cronixieInit) + if (overlayCurrent == 3 && !cronixieInit) { strip.driverModeCronixie(true); strip.setCronixieBacklight(cronixieBacklight); setCronixie(); cronixieInit = true; - } else if (cronixieInit && overlayCurrent != 4) + } else if (cronixieInit && overlayCurrent != 3) { strip.driverModeCronixie(false); cronixieInit = false; @@ -124,6 +124,7 @@ void handleOverlays() { initCronixie(); updateLocalTime(); + checkTimers(); switch (overlayCurrent) { case 0: break;//no overlay From dbd6f134c192c7e93b471836a3de84b6817734ea Mon Sep 17 00:00:00 2001 From: cschwinne Date: Fri, 28 Sep 2018 23:53:51 +0200 Subject: [PATCH 10/22] Added CL= and C2= API calls to set HEX or DEC RGB or WRGB color Started to add MQTT support Pre server and init split-up --- wled00/WS2812FX.cpp | 1 + .../src/dependencies/pubsubclient/LICENSE.txt | 20 + .../pubsubclient/PubSubClient.cpp | 601 ++++++++++++++++++ .../dependencies/pubsubclient/PubSubClient.h | 144 +++++ wled00/wled00.ino | 23 +- wled00/wled02_xml.ino | 4 +- wled00/wled03_set.ino | 15 +- wled00/wled05_init.ino | 5 + wled00/wled12_alexa.ino | 24 +- wled00/wled14_colors.ino | 20 + wled00/wled17_mqtt.ino | 96 +++ 11 files changed, 932 insertions(+), 21 deletions(-) create mode 100644 wled00/src/dependencies/pubsubclient/LICENSE.txt create mode 100644 wled00/src/dependencies/pubsubclient/PubSubClient.cpp create mode 100644 wled00/src/dependencies/pubsubclient/PubSubClient.h create mode 100644 wled00/wled17_mqtt.ino diff --git a/wled00/WS2812FX.cpp b/wled00/WS2812FX.cpp index 8473b14b0..8f4af282a 100644 --- a/wled00/WS2812FX.cpp +++ b/wled00/WS2812FX.cpp @@ -38,6 +38,7 @@ Modified for WLED */ + #include "WS2812FX.h" #include "FastLED.h" #include "palettes.h"; diff --git a/wled00/src/dependencies/pubsubclient/LICENSE.txt b/wled00/src/dependencies/pubsubclient/LICENSE.txt new file mode 100644 index 000000000..217df35cc --- /dev/null +++ b/wled00/src/dependencies/pubsubclient/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2008-2015 Nicholas O'Leary + +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. diff --git a/wled00/src/dependencies/pubsubclient/PubSubClient.cpp b/wled00/src/dependencies/pubsubclient/PubSubClient.cpp new file mode 100644 index 000000000..6ea7e0553 --- /dev/null +++ b/wled00/src/dependencies/pubsubclient/PubSubClient.cpp @@ -0,0 +1,601 @@ +/* + PubSubClient.cpp - A simple client for MQTT. + Nick O'Leary + http://knolleary.net +*/ + +#include "PubSubClient.h" +#include "Arduino.h" + +PubSubClient::PubSubClient() { + this->_state = MQTT_DISCONNECTED; + this->_client = NULL; + this->stream = NULL; + setCallback(NULL); +} + +PubSubClient::PubSubClient(Client& client) { + this->_state = MQTT_DISCONNECTED; + setClient(client); + this->stream = NULL; +} + +PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client) { + this->_state = MQTT_DISCONNECTED; + setServer(addr, port); + setClient(client); + this->stream = NULL; +} +PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client, Stream& stream) { + this->_state = MQTT_DISCONNECTED; + setServer(addr,port); + setClient(client); + setStream(stream); +} +PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) { + this->_state = MQTT_DISCONNECTED; + setServer(addr, port); + setCallback(callback); + setClient(client); + this->stream = NULL; +} +PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) { + this->_state = MQTT_DISCONNECTED; + setServer(addr,port); + setCallback(callback); + setClient(client); + setStream(stream); +} + +PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client) { + this->_state = MQTT_DISCONNECTED; + setServer(ip, port); + setClient(client); + this->stream = NULL; +} +PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client, Stream& stream) { + this->_state = MQTT_DISCONNECTED; + setServer(ip,port); + setClient(client); + setStream(stream); +} +PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) { + this->_state = MQTT_DISCONNECTED; + setServer(ip, port); + setCallback(callback); + setClient(client); + this->stream = NULL; +} +PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) { + this->_state = MQTT_DISCONNECTED; + setServer(ip,port); + setCallback(callback); + setClient(client); + setStream(stream); +} + +PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client) { + this->_state = MQTT_DISCONNECTED; + setServer(domain,port); + setClient(client); + this->stream = NULL; +} +PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client, Stream& stream) { + this->_state = MQTT_DISCONNECTED; + setServer(domain,port); + setClient(client); + setStream(stream); +} +PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) { + this->_state = MQTT_DISCONNECTED; + setServer(domain,port); + setCallback(callback); + setClient(client); + this->stream = NULL; +} +PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) { + this->_state = MQTT_DISCONNECTED; + setServer(domain,port); + setCallback(callback); + setClient(client); + setStream(stream); +} + +boolean PubSubClient::connect(const char *id) { + return connect(id,NULL,NULL,0,0,0,0); +} + +boolean PubSubClient::connect(const char *id, const char *user, const char *pass) { + return connect(id,user,pass,0,0,0,0); +} + +boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) { + return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage); +} + +boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) { + if (!connected()) { + int result = 0; + + if (domain != NULL) { + result = _client->connect(this->domain, this->port); + } else { + result = _client->connect(this->ip, this->port); + } + if (result == 1) { + nextMsgId = 1; + // Leave room in the buffer for header and variable length field + uint16_t length = 5; + unsigned int j; + +#if MQTT_VERSION == MQTT_VERSION_3_1 + uint8_t d[9] = {0x00,0x06,'M','Q','I','s','d','p', MQTT_VERSION}; +#define MQTT_HEADER_VERSION_LENGTH 9 +#elif MQTT_VERSION == MQTT_VERSION_3_1_1 + uint8_t d[7] = {0x00,0x04,'M','Q','T','T',MQTT_VERSION}; +#define MQTT_HEADER_VERSION_LENGTH 7 +#endif + for (j = 0;j>1); + } + } + + buffer[length++] = v; + + buffer[length++] = ((MQTT_KEEPALIVE) >> 8); + buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF); + length = writeString(id,buffer,length); + if (willTopic) { + length = writeString(willTopic,buffer,length); + length = writeString(willMessage,buffer,length); + } + + if(user != NULL) { + length = writeString(user,buffer,length); + if(pass != NULL) { + length = writeString(pass,buffer,length); + } + } + + write(MQTTCONNECT,buffer,length-5); + + lastInActivity = lastOutActivity = millis(); + + while (!_client->available()) { + unsigned long t = millis(); + if (t-lastInActivity >= ((int32_t) MQTT_SOCKET_TIMEOUT*1000UL)) { + _state = MQTT_CONNECTION_TIMEOUT; + _client->stop(); + return false; + } + } + uint8_t llen; + uint16_t len = readPacket(&llen); + + if (len == 4) { + if (buffer[3] == 0) { + lastInActivity = millis(); + pingOutstanding = false; + _state = MQTT_CONNECTED; + return true; + } else { + _state = buffer[3]; + } + } + _client->stop(); + } else { + _state = MQTT_CONNECT_FAILED; + } + return false; + } + return true; +} + +// reads a byte into result +boolean PubSubClient::readByte(uint8_t * result) { + uint32_t previousMillis = millis(); + while(!_client->available()) { + uint32_t currentMillis = millis(); + if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){ + return false; + } + } + *result = _client->read(); + return true; +} + +// reads a byte into result[*index] and increments index +boolean PubSubClient::readByte(uint8_t * result, uint16_t * index){ + uint16_t current_index = *index; + uint8_t * write_address = &(result[current_index]); + if(readByte(write_address)){ + *index = current_index + 1; + return true; + } + return false; +} + +uint16_t PubSubClient::readPacket(uint8_t* lengthLength) { + uint16_t len = 0; + if(!readByte(buffer, &len)) return 0; + bool isPublish = (buffer[0]&0xF0) == MQTTPUBLISH; + uint32_t multiplier = 1; + uint16_t length = 0; + uint8_t digit = 0; + uint16_t skip = 0; + uint8_t start = 0; + + do { + if (len == 6) { + // Invalid remaining length encoding - kill the connection + _state = MQTT_DISCONNECTED; + _client->stop(); + return 0; + } + if(!readByte(&digit)) return 0; + buffer[len++] = digit; + length += (digit & 127) * multiplier; + multiplier *= 128; + } while ((digit & 128) != 0); + *lengthLength = len-1; + + if (isPublish) { + // Read in topic length to calculate bytes to skip over for Stream writing + if(!readByte(buffer, &len)) return 0; + if(!readByte(buffer, &len)) return 0; + skip = (buffer[*lengthLength+1]<<8)+buffer[*lengthLength+2]; + start = 2; + if (buffer[0]&MQTTQOS1) { + // skip message id + skip += 2; + } + } + + for (uint16_t i = start;istream) { + if (isPublish && len-*lengthLength-2>skip) { + this->stream->write(digit); + } + } + if (len < MQTT_MAX_PACKET_SIZE) { + buffer[len] = digit; + } + len++; + } + + if (!this->stream && len > MQTT_MAX_PACKET_SIZE) { + len = 0; // This will cause the packet to be ignored. + } + + return len; +} + +boolean PubSubClient::loop() { + if (connected()) { + unsigned long t = millis(); + if ((t - lastInActivity > MQTT_KEEPALIVE*1000UL) || (t - lastOutActivity > MQTT_KEEPALIVE*1000UL)) { + if (pingOutstanding) { + this->_state = MQTT_CONNECTION_TIMEOUT; + _client->stop(); + return false; + } else { + buffer[0] = MQTTPINGREQ; + buffer[1] = 0; + _client->write(buffer,2); + lastOutActivity = t; + lastInActivity = t; + pingOutstanding = true; + } + } + if (_client->available()) { + uint8_t llen; + uint16_t len = readPacket(&llen); + uint16_t msgId = 0; + uint8_t *payload; + if (len > 0) { + lastInActivity = t; + uint8_t type = buffer[0]&0xF0; + if (type == MQTTPUBLISH) { + if (callback) { + uint16_t tl = (buffer[llen+1]<<8)+buffer[llen+2]; /* topic length in bytes */ + memmove(buffer+llen+2,buffer+llen+3,tl); /* move topic inside buffer 1 byte to front */ + buffer[llen+2+tl] = 0; /* end the topic as a 'C' string with \x00 */ + char *topic = (char*) buffer+llen+2; + + // make sure payload can be interpreted as 'C' string + buffer[(len < MQTT_MAX_PACKET_SIZE) ? len -1 : MQTT_MAX_PACKET_SIZE -1] = 0; + + // msgId only present for QOS>0 + if ((buffer[0]&0x06) == MQTTQOS1) { + msgId = (buffer[llen+3+tl]<<8)+buffer[llen+3+tl+1]; + payload = buffer+llen+3+tl+2; + callback(topic,payload,len-llen-3-tl-2); + + buffer[0] = MQTTPUBACK; + buffer[1] = 2; + buffer[2] = (msgId >> 8); + buffer[3] = (msgId & 0xFF); + _client->write(buffer,4); + lastOutActivity = t; + + } else { + payload = buffer+llen+3+tl; + callback(topic,payload,len-llen-3-tl); + } + } + } else if (type == MQTTPINGREQ) { + buffer[0] = MQTTPINGRESP; + buffer[1] = 0; + _client->write(buffer,2); + } else if (type == MQTTPINGRESP) { + pingOutstanding = false; + } + } else if (!connected()) { + // readPacket has closed the connection + return false; + } + } + return true; + } + return false; +} + +boolean PubSubClient::publish(const char* topic, const char* payload) { + return publish(topic,(const uint8_t*)payload,strlen(payload),false); +} + +boolean PubSubClient::publish(const char* topic, const char* payload, boolean retained) { + return publish(topic,(const uint8_t*)payload,strlen(payload),retained); +} + +boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) { + return publish(topic, payload, plength, false); +} + +boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) { + if (connected()) { + if (MQTT_MAX_PACKET_SIZE < 5 + 2+strlen(topic) + plength) { + // Too long + return false; + } + // Leave room in the buffer for header and variable length field + uint16_t length = 5; + length = writeString(topic,buffer,length); + uint16_t i; + for (i=0;i 0) { + digit |= 0x80; + } + buffer[pos++] = digit; + llen++; + } while(len>0); + + pos = writeString(topic,buffer,pos); + + rc += _client->write(buffer,pos); + + for (i=0;iwrite((char)pgm_read_byte_near(payload + i)); + } + + lastOutActivity = millis(); + + return rc == tlen + 4 + plength; +} + +boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) { + uint8_t lenBuf[4]; + uint8_t llen = 0; + uint8_t digit; + uint8_t pos = 0; + uint16_t rc; + uint16_t len = length; + do { + digit = len % 128; + len = len / 128; + if (len > 0) { + digit |= 0x80; + } + lenBuf[pos++] = digit; + llen++; + } while(len>0); + + buf[4-llen] = header; + for (int i=0;i 0) && result) { + bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining; + rc = _client->write(writeBuf,bytesToWrite); + result = (rc == bytesToWrite); + bytesRemaining -= rc; + writeBuf += rc; + } + return result; +#else + rc = _client->write(buf+(4-llen),length+1+llen); + lastOutActivity = millis(); + return (rc == 1+llen+length); +#endif +} + +boolean PubSubClient::subscribe(const char* topic) { + return subscribe(topic, 0); +} + +boolean PubSubClient::subscribe(const char* topic, uint8_t qos) { + if (qos > 1) { + return false; + } + if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) { + // Too long + return false; + } + if (connected()) { + // Leave room in the buffer for header and variable length field + uint16_t length = 5; + nextMsgId++; + if (nextMsgId == 0) { + nextMsgId = 1; + } + buffer[length++] = (nextMsgId >> 8); + buffer[length++] = (nextMsgId & 0xFF); + length = writeString((char*)topic, buffer,length); + buffer[length++] = qos; + return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-5); + } + return false; +} + +boolean PubSubClient::unsubscribe(const char* topic) { + if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) { + // Too long + return false; + } + if (connected()) { + uint16_t length = 5; + nextMsgId++; + if (nextMsgId == 0) { + nextMsgId = 1; + } + buffer[length++] = (nextMsgId >> 8); + buffer[length++] = (nextMsgId & 0xFF); + length = writeString(topic, buffer,length); + return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-5); + } + return false; +} + +void PubSubClient::disconnect() { + buffer[0] = MQTTDISCONNECT; + buffer[1] = 0; + _client->write(buffer,2); + _state = MQTT_DISCONNECTED; + _client->stop(); + lastInActivity = lastOutActivity = millis(); +} + +uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, uint16_t pos) { + const char* idp = string; + uint16_t i = 0; + pos += 2; + while (*idp) { + buf[pos++] = *idp++; + i++; + } + buf[pos-i-2] = (i >> 8); + buf[pos-i-1] = (i & 0xFF); + return pos; +} + + +boolean PubSubClient::connected() { + boolean rc; + if (_client == NULL ) { + rc = false; + } else { + rc = (int)_client->connected(); + if (!rc) { + if (this->_state == MQTT_CONNECTED) { + this->_state = MQTT_CONNECTION_LOST; + _client->flush(); + _client->stop(); + } + } + } + return rc; +} + +PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port) { + IPAddress addr(ip[0],ip[1],ip[2],ip[3]); + return setServer(addr,port); +} + +PubSubClient& PubSubClient::setServer(IPAddress ip, uint16_t port) { + this->ip = ip; + this->port = port; + this->domain = NULL; + return *this; +} + +PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port) { + this->domain = domain; + this->port = port; + return *this; +} + +PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE) { + this->callback = callback; + return *this; +} + +PubSubClient& PubSubClient::setClient(Client& client){ + this->_client = &client; + return *this; +} + +PubSubClient& PubSubClient::setStream(Stream& stream){ + this->stream = &stream; + return *this; +} + +int PubSubClient::state() { + return this->_state; +} diff --git a/wled00/src/dependencies/pubsubclient/PubSubClient.h b/wled00/src/dependencies/pubsubclient/PubSubClient.h new file mode 100644 index 000000000..be4bd6747 --- /dev/null +++ b/wled00/src/dependencies/pubsubclient/PubSubClient.h @@ -0,0 +1,144 @@ +/* + PubSubClient.h - A simple client for MQTT. + Nick O'Leary + http://knolleary.net +*/ + +#ifndef PubSubClient_h +#define PubSubClient_h + +#include +#include "IPAddress.h" +#include "Client.h" +#include "Stream.h" + +#define MQTT_VERSION_3_1 3 +#define MQTT_VERSION_3_1_1 4 + +// MQTT_VERSION : Pick the version +//#define MQTT_VERSION MQTT_VERSION_3_1 +#ifndef MQTT_VERSION +#define MQTT_VERSION MQTT_VERSION_3_1_1 +#endif + +// MQTT_MAX_PACKET_SIZE : Maximum packet size +#ifndef MQTT_MAX_PACKET_SIZE +#define MQTT_MAX_PACKET_SIZE 128 +#endif + +// MQTT_KEEPALIVE : keepAlive interval in Seconds +#ifndef MQTT_KEEPALIVE +#define MQTT_KEEPALIVE 15 +#endif + +// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds +#ifndef MQTT_SOCKET_TIMEOUT +#define MQTT_SOCKET_TIMEOUT 15 +#endif + +// MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client +// in each write call. Needed for the Arduino Wifi Shield. Leave undefined to +// pass the entire MQTT packet in each write call. +//#define MQTT_MAX_TRANSFER_SIZE 80 + +// Possible values for client.state() +#define MQTT_CONNECTION_TIMEOUT -4 +#define MQTT_CONNECTION_LOST -3 +#define MQTT_CONNECT_FAILED -2 +#define MQTT_DISCONNECTED -1 +#define MQTT_CONNECTED 0 +#define MQTT_CONNECT_BAD_PROTOCOL 1 +#define MQTT_CONNECT_BAD_CLIENT_ID 2 +#define MQTT_CONNECT_UNAVAILABLE 3 +#define MQTT_CONNECT_BAD_CREDENTIALS 4 +#define MQTT_CONNECT_UNAUTHORIZED 5 + +#define MQTTCONNECT 1 << 4 // Client request to connect to Server +#define MQTTCONNACK 2 << 4 // Connect Acknowledgment +#define MQTTPUBLISH 3 << 4 // Publish message +#define MQTTPUBACK 4 << 4 // Publish Acknowledgment +#define MQTTPUBREC 5 << 4 // Publish Received (assured delivery part 1) +#define MQTTPUBREL 6 << 4 // Publish Release (assured delivery part 2) +#define MQTTPUBCOMP 7 << 4 // Publish Complete (assured delivery part 3) +#define MQTTSUBSCRIBE 8 << 4 // Client Subscribe request +#define MQTTSUBACK 9 << 4 // Subscribe Acknowledgment +#define MQTTUNSUBSCRIBE 10 << 4 // Client Unsubscribe request +#define MQTTUNSUBACK 11 << 4 // Unsubscribe Acknowledgment +#define MQTTPINGREQ 12 << 4 // PING Request +#define MQTTPINGRESP 13 << 4 // PING Response +#define MQTTDISCONNECT 14 << 4 // Client is Disconnecting +#define MQTTReserved 15 << 4 // Reserved + +#define MQTTQOS0 (0 << 1) +#define MQTTQOS1 (1 << 1) +#define MQTTQOS2 (2 << 1) + +#ifdef ESP8266 +#include +#define MQTT_CALLBACK_SIGNATURE std::function callback +#else +#define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int) +#endif + +class PubSubClient { +private: + Client* _client; + uint8_t buffer[MQTT_MAX_PACKET_SIZE]; + uint16_t nextMsgId; + unsigned long lastOutActivity; + unsigned long lastInActivity; + bool pingOutstanding; + MQTT_CALLBACK_SIGNATURE; + uint16_t readPacket(uint8_t*); + boolean readByte(uint8_t * result); + boolean readByte(uint8_t * result, uint16_t * index); + boolean write(uint8_t header, uint8_t* buf, uint16_t length); + uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos); + IPAddress ip; + const char* domain; + uint16_t port; + Stream* stream; + int _state; +public: + PubSubClient(); + PubSubClient(Client& client); + PubSubClient(IPAddress, uint16_t, Client& client); + PubSubClient(IPAddress, uint16_t, Client& client, Stream&); + PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client); + PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&); + PubSubClient(uint8_t *, uint16_t, Client& client); + PubSubClient(uint8_t *, uint16_t, Client& client, Stream&); + PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client); + PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&); + PubSubClient(const char*, uint16_t, Client& client); + PubSubClient(const char*, uint16_t, Client& client, Stream&); + PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client); + PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&); + + PubSubClient& setServer(IPAddress ip, uint16_t port); + PubSubClient& setServer(uint8_t * ip, uint16_t port); + PubSubClient& setServer(const char * domain, uint16_t port); + PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE); + PubSubClient& setClient(Client& client); + PubSubClient& setStream(Stream& stream); + + boolean connect(const char* id); + boolean connect(const char* id, const char* user, const char* pass); + boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage); + boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage); + void disconnect(); + boolean publish(const char* topic, const char* payload); + boolean publish(const char* topic, const char* payload, boolean retained); + boolean publish(const char* topic, const uint8_t * payload, unsigned int plength); + boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained); + boolean publish_P(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained); + boolean subscribe(const char* topic); + boolean subscribe(const char* topic, uint8_t qos); + boolean unsubscribe(const char* topic); + boolean loop(); + boolean connected(); + int state(); +}; + + +#endif diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 508a980fe..e04bffea2 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -35,16 +35,17 @@ #include "src/dependencies/time/Time.h" #include "src/dependencies/time/TimeLib.h" #include "src/dependencies/timezone/Timezone.h" +#include "src/dependencies/blynk/BlynkSimpleEsp.h" +#include "src/dependencies/e131/E131.h" +#include "src/dependencies/pubsubclient/PubSubClient.h" #include "htmls00.h" #include "htmls01.h" #include "htmls02.h" #include "WS2812FX.h" -#include "src/dependencies/blynk/BlynkSimpleEsp.h" -#include "src/dependencies/e131/E131.h" //version code in format yymmddb (b = daily build) -#define VERSION 1809222 +#define VERSION 1809281 char versionString[] = "0.8.0-a"; @@ -160,6 +161,10 @@ bool e131Enabled = true; //settings for E1.31 (sACN) protoc uint16_t e131Universe = 1; bool e131Multicast = false; +char mqttTopic0[33] = ""; //main MQTT topic (individual per device, default is wled/mac) +char mqttTopic1[33] = "wled/all"; //second MQTT topic (for example to group devices) +char mqttServer[33] = "37.187.106.16"; //both domains and IPs should work (no SSL) 37.187.106.16 + bool huePollingEnabled = false; //poll hue bridge for light state uint16_t huePollIntervalMs = 2500; //low values (< 1sec) may cause lag but offer quicker response char hueApiKey[65] = "api"; //key token will be obtained from bridge @@ -324,13 +329,17 @@ bool realtimeActive = false; IPAddress realtimeIP = (0,0,0,0); unsigned long realtimeTimeout = 0; +//mqtt +bool mqttInit = false; +long lastMQTTReconnectAttempt = 0; + //auxiliary debug pin byte auxTime = 0; unsigned long auxStartTime = 0; bool auxActive = false, auxActiveBefore = false; //alexa udp -WiFiUDP UDP; +WiFiUDP alexaUDP; IPAddress ipMulti(239, 255, 255, 250); unsigned int portMulti = 1900; String escapedMac; @@ -360,6 +369,9 @@ WebServer server(80); ESP8266WebServer server(80); #endif HTTPClient hueClient; +WiFiClient mqttTCPClient; +PubSubClient mqtt(mqttTCPClient); + ESP8266HTTPUpdateServer httpUpdater; //udp interface objects @@ -465,6 +477,9 @@ void loop() { if (aOtaEnabled) ArduinoOTA.handle(); handleAlexa(); handleOverlays(); + + yield(); + handleMQTT(); if (!realtimeActive) //block stuff if WARLS/Adalight is enabled { diff --git a/wled00/wled02_xml.ino b/wled00/wled02_xml.ino index 9dfc24511..8aaca5638 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/wled02_xml.ino @@ -2,7 +2,7 @@ * Sending XML status files to client */ -void XML_response() +void XML_response(bool isHTTP) { olen = 0; oappend(""); @@ -52,7 +52,7 @@ void XML_response() oappend(""); oappend(serverDescription); oappend(""); - server.send(200, "text/xml", obuf); + if (isHTTP) server.send(200, "text/xml", obuf); } void sappend(char stype, char* key, int val) //append a setting to string buffer diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index 714c8f5a0..487c6fa29 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -101,6 +101,7 @@ void handleSettingsSet(byte subPage) fadeTransition = server.hasArg("TF"); t = server.arg("TD").toInt(); if (t > 0) transitionDelay = t; + transitionDelayDefault = t; strip.paletteFade = server.hasArg("PF"); enableSecTransition = server.hasArg("T2"); @@ -318,7 +319,7 @@ bool handleSet(String req) } pos = req.indexOf("IN"); - if (pos < 1) XML_response(); + if (pos < 1) XML_response(true); return true; //if you save a macro in one request, other commands in that request are ignored due to unwanted behavior otherwise } @@ -383,6 +384,16 @@ bool handleSet(String req) whiteSec = req.substring(pos + 3).toInt(); } + //set color from HEX or 32bit DEC + pos = req.indexOf("CL="); + if (pos > 0) { + colorFromDecOrHexString(col, &white, (char*)req.substring(pos + 3).c_str()); + } + pos = req.indexOf("C2="); + if (pos > 0) { + colorFromDecOrHexString(colSec, &whiteSec, (char*)req.substring(pos + 3).c_str()); + } + //set 2nd to white pos = req.indexOf("SW"); if (pos > 0) { @@ -718,7 +729,7 @@ bool handleSet(String req) //internal call, does not send XML response pos = req.indexOf("IN"); - if (pos < 1) XML_response(); + if (pos < 1) XML_response(true); //do not send UDP notifications this time pos = req.indexOf("NN"); if (pos > 0) diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index 00472f9a1..ac20e2ee7 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -56,6 +56,10 @@ void wledInit() dnsServer.start(53, "*", WiFi.softAPIP()); dnsActive = true; } + + prepareIds(); //UUID from MAC (for Alexa and MQTT) + if (mqttTopic0[0] == 0) strcpy(mqttTopic0, strcat("wled/", escapedMac.c_str())); + if (!initLedsLast) strip.service(); //SERVER INIT //settings page @@ -264,6 +268,7 @@ void wledInit() { initBlynk(blynkApiKey); initE131(); + mqttInit = initMQTT(); } if (initLedsLast) initStrip(); diff --git a/wled00/wled12_alexa.ino b/wled00/wled12_alexa.ino index f501a6947..05f3ebbf0 100644 --- a/wled00/wled12_alexa.ino +++ b/wled00/wled12_alexa.ino @@ -10,8 +10,6 @@ void alexaInit() { if (alexaEnabled && WiFi.status() == WL_CONNECTED) { - prepareIds(); - udpConnected = connectUDP(); if (udpConnected) alexaInitPages(); @@ -24,10 +22,10 @@ void handleAlexa() { if(udpConnected){ // if there’s data available, read a packet - int packetSize = UDP.parsePacket(); + int packetSize = alexaUDP.parsePacket(); if(packetSize>0) { - IPAddress remote = UDP.remoteIP(); - int len = UDP.read(obuf, 254); + IPAddress remote = alexaUDP.remoteIP(); + int len = alexaUDP.read(obuf, 254); if (len > 0) { obuf[len] = 0; } @@ -98,9 +96,9 @@ void prepareIds() { void respondToSearch() { DEBUG_PRINTLN(""); DEBUG_PRINT("Send resp to "); - DEBUG_PRINTLN(UDP.remoteIP()); + DEBUG_PRINTLN(alexaUDP.remoteIP()); DEBUG_PRINT("Port : "); - DEBUG_PRINTLN(UDP.remotePort()); + DEBUG_PRINTLN(alexaUDP.remotePort()); IPAddress localIP = WiFi.localIP(); char s[16]; @@ -124,13 +122,13 @@ void respondToSearch() { oappend("::upnp:rootdevice\r\n" // _uuid::_deviceType "\r\n"); - UDP.beginPacket(UDP.remoteIP(), UDP.remotePort()); + alexaUDP.beginPacket(alexaUDP.remoteIP(), alexaUDP.remotePort()); #ifdef ARDUINO_ARCH_ESP32 - UDP.write((byte*)obuf, olen); + alexaUDP.write((byte*)obuf, olen); #else - UDP.write(obuf); + alexaUDP.write(obuf); #endif - UDP.endPacket(); + alexaUDP.endPacket(); DEBUG_PRINTLN("Response sent!"); } @@ -276,9 +274,9 @@ bool connectUDP(){ DEBUG_PRINTLN("Con UDP"); #ifdef ARDUINO_ARCH_ESP32 - if(UDP.beginMulticast(ipMulti, portMulti)) + if(alexaUDP.beginMulticast(ipMulti, portMulti)) #else - if(UDP.beginMulticast(WiFi.localIP(), ipMulti, portMulti)) + if(alexaUDP.beginMulticast(WiFi.localIP(), ipMulti, portMulti)) #endif { DEBUG_PRINTLN("Con success"); diff --git a/wled00/wled14_colors.ino b/wled00/wled14_colors.ino index 6f8822997..ed008a84c 100644 --- a/wled00/wled14_colors.ino +++ b/wled00/wled14_colors.ino @@ -106,6 +106,26 @@ void colorXYtoRGB(float x, float y, byte* rgb) //coordinates to rgb (https://www rgb[2] = 255.0*b; } +void colorFromDecOrHexString(byte* rgb, byte* wht, char* in) +{ + if (in[0] == 0) return; + char first = in[0]; + uint32_t c = 0; + + if (first == '#' || first == 'h' || first == 'H') //is HEX encoded + { + c = strtoul(in +1, NULL, 16); + } else + { + c = strtoul(in, NULL, 10); + } + + *wht = (c >> 24) & 0xFF; + rgb[0] = (c >> 16) & 0xFF; + rgb[1] = (c >> 8) & 0xFF; + rgb[2] = c & 0xFF; +} + void colorRGBtoXY(byte* rgb, float* xy) //rgb to coordinates (https://www.developers.meethue.com/documentation/color-conversions-rgb-xy) { float X = rgb[0] * 0.664511f + rgb[1] * 0.154324f + rgb[2] * 0.162028f; diff --git a/wled00/wled17_mqtt.ino b/wled00/wled17_mqtt.ino new file mode 100644 index 000000000..85918c653 --- /dev/null +++ b/wled00/wled17_mqtt.ino @@ -0,0 +1,96 @@ +/* + * MQTT communication protocol for home automation + */ + +void callbackMQTT(char* topic, byte* payload, unsigned int length) { + + if (strcmp(topic, mqttTopic0) == 0 || + strcmp(topic, mqttTopic1) == 0) + { + if (strcmp((char*)payload, "ON") == 0) {bri = briLast;} + else if (strcmp((char*)payload, "T" ) == 0) {handleSet("win&T=2");} + else { + uint8_t in = strtoul((char*)payload, NULL, 10); + if (in == 0 && bri > 0) briLast = bri; + bri = in; + } + colorUpdated(1); + return; + } + + if (strcmp(topic, strcat(mqttTopic0, "/col")) == 0 || + strcmp(topic, strcat(mqttTopic1, "/col")) == 0) + { + colorFromDecOrHexString(col, &white, (char*)payload); + colorUpdated(1); + return; + } + + if (strcmp(topic, strcat(mqttTopic0, "/api")) == 0 || + strcmp(topic, strcat(mqttTopic1, "/api")) == 0) + { + handleSet(String((char*)payload)); + return; + } +} + +void publishStatus() +{ + if (!mqtt.connected()) return; + + char s[4]; + sprintf(s,"%ld", bri); + mqtt.publish(strcat(mqttTopic0, "/g") , s); + XML_response(false); + mqtt.publish(strcat(mqttTopic0, "/vs"), obuf); +} + +bool reconnectMQTT() +{ + if (mqtt.connect(escapedMac.c_str())) + { + //re-subscribe to required topics + + if (mqttTopic0[0] != 0) + { + mqtt.subscribe(mqttTopic0); + mqtt.subscribe(strcat(mqttTopic0, "/col")); + mqtt.subscribe(strcat(mqttTopic0, "/api")); + } + + if (mqttTopic1[0] != 0) + { + mqtt.subscribe(mqttTopic1); + mqtt.subscribe(strcat(mqttTopic1, "/col")); + mqtt.subscribe(strcat(mqttTopic1, "/api")); + } + } + return mqtt.connected(); +} + +bool initMQTT() +{ + if (WiFi.status() != WL_CONNECTED) return false; + if (mqttServer[0] == 0) return false; + + IPAddress mqttIP; + if (mqttIP.fromString(mqttServer)) //see if server is IP or domain + { + mqtt.setServer(mqttIP,1883); + } else { + mqtt.setServer(mqttServer,1883); + } + mqtt.setCallback(callbackMQTT); + return true; +} + +void handleMQTT() +{ + if (WiFi.status() != WL_CONNECTED || !mqttInit) return; + if (!mqtt.connected() && millis() - lastMQTTReconnectAttempt > 5000) + { + lastMQTTReconnectAttempt = millis(); + if (!reconnectMQTT()) return; + } + mqtt.loop(); +} From 92b4b69b3fe205a3ab0e4ba2551309c943a91d5f Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sun, 30 Sep 2018 20:24:57 +0200 Subject: [PATCH 11/22] Split Server init code from wled05_init.ino to improve readability Added MQTT logic --- .../pubsubclient/PubSubClient.cpp | 2 +- .../dependencies/pubsubclient/PubSubClient.h | 4 +- wled00/wled00.ino | 4 +- wled00/wled05_init.ino | 360 +---------------- wled00/wled17_mqtt.ino | 71 ++-- wled00/wled18_server.ino | 374 ++++++++++++++++++ 6 files changed, 426 insertions(+), 389 deletions(-) create mode 100644 wled00/wled18_server.ino diff --git a/wled00/src/dependencies/pubsubclient/PubSubClient.cpp b/wled00/src/dependencies/pubsubclient/PubSubClient.cpp index 6ea7e0553..d3e5ca5fc 100644 --- a/wled00/src/dependencies/pubsubclient/PubSubClient.cpp +++ b/wled00/src/dependencies/pubsubclient/PubSubClient.cpp @@ -318,7 +318,7 @@ boolean PubSubClient::loop() { char *topic = (char*) buffer+llen+2; // make sure payload can be interpreted as 'C' string - buffer[(len < MQTT_MAX_PACKET_SIZE) ? len -1 : MQTT_MAX_PACKET_SIZE -1] = 0; + buffer[(len < MQTT_MAX_PACKET_SIZE) ? len : MQTT_MAX_PACKET_SIZE -1] = 0; // msgId only present for QOS>0 if ((buffer[0]&0x06) == MQTTQOS1) { diff --git a/wled00/src/dependencies/pubsubclient/PubSubClient.h b/wled00/src/dependencies/pubsubclient/PubSubClient.h index be4bd6747..9eaa32961 100644 --- a/wled00/src/dependencies/pubsubclient/PubSubClient.h +++ b/wled00/src/dependencies/pubsubclient/PubSubClient.h @@ -28,12 +28,12 @@ // MQTT_KEEPALIVE : keepAlive interval in Seconds #ifndef MQTT_KEEPALIVE -#define MQTT_KEEPALIVE 15 +#define MQTT_KEEPALIVE 60 #endif // MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds #ifndef MQTT_SOCKET_TIMEOUT -#define MQTT_SOCKET_TIMEOUT 15 +#define MQTT_SOCKET_TIMEOUT 62 #endif // MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client diff --git a/wled00/wled00.ino b/wled00/wled00.ino index e04bffea2..1af0e10c9 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -45,7 +45,7 @@ //version code in format yymmddb (b = daily build) -#define VERSION 1809281 +#define VERSION 1809292 char versionString[] = "0.8.0-a"; @@ -59,7 +59,7 @@ char otaPass[33] = "wledota"; //to toggle usb serial debug (un)comment following line(s) -//#define DEBUG +#define DEBUG //Hardware CONFIG (only changeble HERE, not at runtime) diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index ac20e2ee7..6ca487b7a 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -59,180 +59,12 @@ void wledInit() prepareIds(); //UUID from MAC (for Alexa and MQTT) if (mqttTopic0[0] == 0) strcpy(mqttTopic0, strcat("wled/", escapedMac.c_str())); + if (!onlyAP) mqttInit = initMQTT(); if (!initLedsLast) strip.service(); - //SERVER INIT - //settings page - server.on("/settings", HTTP_GET, [](){ - serveSettings(0); - }); - server.on("/settings/wifi", HTTP_GET, [](){ - if (!(wifiLock && otaLock)) - { - serveSettings(1); - }else{ - serveMessage(500, "Access Denied", txd, 254); - } - }); - server.on("/settings/leds", HTTP_GET, [](){ - serveSettings(2); - }); - server.on("/settings/ui", HTTP_GET, [](){ - serveSettings(3); - }); - server.on("/settings/sync", HTTP_GET, [](){ - serveSettings(4); - }); - server.on("/settings/time", HTTP_GET, [](){ - serveSettings(5); - }); - server.on("/settings/sec", HTTP_GET, [](){ - serveSettings(6); - }); - - server.on("/favicon.ico", HTTP_GET, [](){ - if(!handleFileRead("/favicon.ico")) - { - server.send_P(200, "image/x-icon", favicon, 156); - } - }); - - server.on("/", HTTP_GET, [](){ - serveIndexOrWelcome(); - }); - server.on("/generate_204", HTTP_GET, [](){ - serveIndex(); - }); - - server.on("/fwlink", HTTP_GET, [](){ - serveIndex(); - }); - - server.on("/sliders", HTTP_GET, serveIndex); - - server.on("/welcome", HTTP_GET, [](){ - serveSettings(255); - }); - - server.on("/reset", HTTP_GET, [](){ - serveMessage(200,"Rebooting now...","(takes ~20 seconds, wait for auto-redirect)",79); - reset(); - }); - - server.on("/settings/wifi", HTTP_POST, [](){ - if (!(wifiLock && otaLock)) handleSettingsSet(1); - serveMessage(200,"WiFi settings saved.","Rebooting now...",255); - reset(); - }); - - server.on("/settings/leds", HTTP_POST, [](){ - handleSettingsSet(2); - serveMessage(200,"LED settings saved.","Redirecting...",1); - }); - - server.on("/settings/ui", HTTP_POST, [](){ - handleSettingsSet(3); - serveMessage(200,"UI settings saved.","Reloading to apply theme...",122); - }); - - server.on("/settings/sync", HTTP_POST, [](){ - handleSettingsSet(4); - if (hueAttempt) - { - serveMessage(200,"Hue setup result",hueError,253); - } else { - serveMessage(200,"Sync settings saved.","Redirecting...",1); - } - hueAttempt = false; - }); - - server.on("/settings/time", HTTP_POST, [](){ - handleSettingsSet(5); - serveMessage(200,"Time settings saved.","Redirecting...",1); - }); - - server.on("/settings/sec", HTTP_POST, [](){ - handleSettingsSet(6); - serveMessage(200,"Security settings saved.","Rebooting now... (takes ~20 seconds, wait for auto-redirect)",139); - reset(); - }); - - server.on("/version", HTTP_GET, [](){ - server.send(200, "text/plain", (String)VERSION); - }); - - server.on("/uptime", HTTP_GET, [](){ - server.send(200, "text/plain", (String)millis()); - }); - - server.on("/freeheap", HTTP_GET, [](){ - server.send(200, "text/plain", (String)ESP.getFreeHeap()); - }); - - server.on("/power", HTTP_GET, [](){ - String val = (String)(int)strip.getPowerEstimate(ledCount,strip.getColor(),strip.getBrightness()); - val += "mA currently"; - serveMessage(200,val,"This is just an estimate (does not take into account several factors like effects and wire resistance). It is NOT an accurate measurement!",254); - }); - - server.on("/u", HTTP_GET, [](){ - server.setContentLength(strlen_P(PAGE_usermod)); - server.send(200, "text/html", ""); - server.sendContent_P(PAGE_usermod); - }); - - server.on("/teapot", HTTP_GET, [](){ - serveMessage(418, "418. I'm a teapot.","(Tangible Embedded Advanced Project Of Twinkling)",254); - }); - - server.on("/build", HTTP_GET, [](){ - getBuildInfo(); - server.send(200, "text/plain", obuf); - }); - //if OTA is allowed - if (!otaLock){ - server.on("/edit", HTTP_GET, [](){ - server.send(200, "text/html", PAGE_edit); - }); - #ifdef USEFS - server.on("/edit", HTTP_PUT, handleFileCreate); - server.on("/edit", HTTP_DELETE, handleFileDelete); - server.on("/edit", HTTP_POST, [](){ server.send(200, "text/plain", ""); }, handleFileUpload); - server.on("/list", HTTP_GET, handleFileList); - #endif - //init ota page - httpUpdater.setup(&server); - } else - { - server.on("/edit", HTTP_GET, [](){ - serveMessage(500, "Access Denied", txd, 254); - }); - server.on("/update", HTTP_GET, [](){ - serveMessage(500, "Access Denied", txd, 254); - }); - server.on("/list", HTTP_GET, [](){ - serveMessage(500, "Access Denied", txd, 254); - }); - } - //called when the url is not defined here, ajax-in; get-settings - server.onNotFound([](){ - DEBUG_PRINTLN("Not-Found HTTP call:"); - DEBUG_PRINTLN("URI: " + server.uri()); - DEBUG_PRINTLN("Body: " + server.arg(0)); - if(!handleSet(server.uri())){ - if(!handleAlexaApiCall(server.uri(),server.arg(0))) - server.send(404, "text/plain", "Not Found"); - } - }); - - #ifndef ARDUINO_ARCH_ESP32 - const char * headerkeys[] = {"User-Agent"}; - server.collectHeaders(headerkeys,sizeof(headerkeys)/sizeof(char*)); - #else - String ua = "User-Agent"; - server.collectHeaders(ua); - #endif + //HTTP server page init + initServer(); if (!initLedsLast) strip.service(); //init Alexa hue emulation @@ -268,7 +100,6 @@ void wledInit() { initBlynk(blynkApiKey); initE131(); - mqttInit = initMQTT(); } if (initLedsLast) initStrip(); @@ -361,190 +192,6 @@ void initCon() } } -void buildCssColorString() -{ - String cs[]={"","","","","",""}; - switch (currentTheme) - { - default: cs[0]="D9B310"; cs[1]="0B3C5D"; cs[2]="1D2731"; cs[3]="328CC1"; cs[4]="000"; cs[5]="328CC1"; break; //night - case 1: cs[0]="eee"; cs[1]="ddd"; cs[2]="b9b9b9"; cs[3]="049"; cs[4]="777"; cs[5]="049"; break; //modern - case 2: cs[0]="abc"; cs[1]="fff"; cs[2]="ddd"; cs[3]="000"; cs[4]="0004"; cs[5]="000"; break; //bright - case 3: cs[0]="c09f80"; cs[1]="d7cec7"; cs[2]="76323f"; cs[3]="888"; cs[4]="3334"; cs[5]="888"; break; //wine - case 4: cs[0]="3cc47c"; cs[1]="828081"; cs[2]="d9a803"; cs[3]="1e392a"; cs[4]="000a"; cs[5]="1e392a"; break; //electric - case 5: cs[0]="57bc90"; cs[1]="a5a5af"; cs[2]="015249"; cs[3]="88c9d4"; cs[4]="0004"; cs[5]="88c9d4"; break; //mint - case 6: cs[0]="f7c331"; cs[1]="dcc7aa"; cs[2]="6b7a8f"; cs[3]="f7882f"; cs[4]="0007"; cs[5]="f7882f"; break; //amber - case 7: cs[0]="fc3"; cs[1]="124"; cs[2]="334"; cs[3]="f1d"; cs[4]="f00"; cs[5]="f1d"; break;//club - case 8: cs[0]="0ac"; cs[1]="124"; cs[2]="224"; cs[3]="003eff"; cs[4]="003eff"; cs[5]="003eff"; break;//air - case 9: cs[0]="f70"; cs[1]="421"; cs[2]="221"; cs[3]="a50"; cs[4]="f70"; cs[5]="f70"; break;//nixie - case 10: cs[0]="2d2"; cs[1]="010"; cs[2]="121"; cs[3]="060"; cs[4]="040"; cs[5]="3f3"; break; //terminal - case 11: cs[0]="867ADE"; cs[1]="4033A3"; cs[2]="483AAA"; cs[3]="483AAA"; cs[4]=""; cs[5]="867ADE"; break; //c64 - case 12: cs[0]="fbe8a6"; cs[1]="d2fdff"; cs[2]="b4dfe5"; cs[3]="f4976c"; cs[4]=""; cs[5]="303c6c"; break; //c64 - case 14: cs[0]="fc7"; cs[1]="49274a"; cs[2]="94618e"; cs[3]="f4decb"; cs[4]="0008"; cs[5]="f4decb"; break; //end - case 15: for (int i=0;i<6;i++)cs[i]=cssCol[i];//custom - } - cssColorString="
        Loading WLED UI...

        WLED

        @@ -74,7 +74,7 @@ Mobile UI is unsupported (limited storage). Go to /settings/ui and change UI mod */ //head0 (js) const char PAGE_index0[] PROGMEM = R"=====( -WLED 0.8.0-a +WLED 0.8.0 )====="; diff --git a/wled00/htmls01.h b/wled00/htmls01.h index b9a2b8d25..b015bf583 100644 --- a/wled00/htmls01.h +++ b/wled00/htmls01.h @@ -250,6 +250,11 @@ Alexa invocation name:

        Blynk

        Device Auth token:
        Clear the token field to disable. Setup info +

        MQTT

        +Broker:
        +Device Topic:
        +Group Topic:
        +Reboot required to apply changes. MQTT info

        Philips Hue

        You can find the bridge IP and the light number in the 'About' section of the hue app.
        Poll Hue light every ms:
        @@ -406,7 +411,7 @@ HTTP traffic is unencrypted. An attacker in the same network can intercept form
        Enable ArduinoOTA:

        About

        -WLED version 0.8.0-a

        +WLED version 0.8.0

        Contributors:
        StormPie (Mobile HTML UI)

        Thank you so much!

        @@ -421,6 +426,7 @@ Thank you so much!

        Timezone library by JChristensen
        Blynk library (compacted)
        E1.31 library by forkineye (modified)
        +PubSubClient by knolleary (modified)
        Espalexa by Aircoookie (modified)

        UI icons by Linearicons created by Perxis! (CC-BY-SA 4.0)

        Server message: Response error!
        diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 1eb429ea8..cf212f011 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -3,7 +3,7 @@ */ /* * @title WLED project sketch - * @version 0.8.0-a + * @version 0.8.0 * @author Christian Schwinne */ @@ -45,8 +45,8 @@ //version code in format yymmddb (b = daily build) -#define VERSION 1810011 -char versionString[] = "0.8.0-a"; +#define VERSION 1810031 +char versionString[] = "0.8.0"; //AP and OTA default passwords (for maximum change them!) @@ -59,7 +59,7 @@ char otaPass[33] = "wledota"; //to toggle usb serial debug (un)comment following line(s) -#define DEBUG +//#define DEBUG //Hardware CONFIG (only changeble HERE, not at runtime) @@ -111,7 +111,7 @@ byte nightlightDelayMins = 60; bool nightlightFade = true; //if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over bool fadeTransition = true; //enable crossfading color transition bool enableSecTransition = true; //also enable transition for secondary color -uint16_t transitionDelay = 1200; //default crossfade duration in ms +uint16_t transitionDelay = 900; //default crossfade duration in ms bool reverseMode = false; //flip entire LED strip (reverses all effect directions) bool initLedsLast = false; //turn on LEDs only after WiFi connected/AP open @@ -161,9 +161,9 @@ bool e131Enabled = true; //settings for E1.31 (sACN) protoc uint16_t e131Universe = 1; bool e131Multicast = false; -char mqttTopic0[33] = ""; //main MQTT topic (individual per device, default is wled/mac) -char mqttTopic1[33] = "wled/all"; //second MQTT topic (for example to group devices) -char mqttServer[33] = "37.187.106.16"; //both domains and IPs should work (no SSL) 37.187.106.16 +char mqttDeviceTopic[33] = ""; //main MQTT topic (individual per device, default is wled/mac) +char mqttGroupTopic[33] = "wled/all"; //second MQTT topic (for example to group devices) +char mqttServer[33] = ""; //both domains and IPs should work (no SSL) bool huePollingEnabled = false; //poll hue bridge for light state uint16_t huePollIntervalMs = 2500; //low values (< 1sec) may cause lag but offer quicker response @@ -272,7 +272,7 @@ bool onlyAP = false; //only Access Point active, no con bool udpConnected = false, udpRgbConnected = false; //ui style -char cssCol[9][5]={"","","","","",""}; +char cssCol[6][9]={"","","","","",""}; String cssColorString=""; bool showWelcomePage = false; @@ -332,6 +332,9 @@ unsigned long realtimeTimeout = 0; //mqtt bool mqttInit = false; long lastMQTTReconnectAttempt = 0; +long lastInterfaceUpdate = 0; +byte interfaceUpdateCallMode = 0; +uint32_t mqttFailedConAttempts = 0; //auxiliary debug pin byte auxTime = 0; diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino index f3c0459d7..c5b2cfad2 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled01_eeprom.ino @@ -6,7 +6,7 @@ #define EEPSIZE 3072 //eeprom Version code, enables default settings instead of 0 init on update -#define EEPVER 8 +#define EEPVER 9 //0 -> old version, default //1 -> 0.4p 1711272 and up //2 -> 0.4p 1711302 and up @@ -15,7 +15,8 @@ //5 -> 0.5.1 and up //6 -> 0.6.0 and up //7 -> 0.7.1 and up -//8 -> 0.8.0 and up +//8 -> 0.8.0-a and up +//9 -> 0.8.0 /* * Erase all configuration data @@ -147,7 +148,7 @@ void saveSettingsToEEPROM() int in = 900+k*8; for (int i=in; i < in+8; ++i) { - EEPROM.write(i, cssCol[i-in][k]); + EEPROM.write(i, cssCol[k][i-in]); }} EEPROM.write(948,currentTheme); @@ -242,6 +243,19 @@ void saveSettingsToEEPROM() EEPROM.write(2280 + i, timerWeekday[i]); EEPROM.write(2290 + i, timerMacro[i] ); } + + for (int i = 2300; i < 2333; ++i) + { + EEPROM.write(i, mqttServer[i-2300]); + } + for (int i = 2333; i < 2366; ++i) + { + EEPROM.write(i, mqttDeviceTopic[i-2333]); + } + for (int i = 2366; i < 2399; ++i) + { + EEPROM.write(i, mqttGroupTopic[i-2366]); + } EEPROM.commit(); } @@ -468,6 +482,25 @@ void loadSettingsFromEEPROM(bool first) timerMacro[i] = EEPROM.read(2290 + i); } } + + if (lastEEPROMversion > 8) + { + for (int i = 2300; i < 2333; ++i) + { + mqttServer[i-2300] = EEPROM.read(i); + if (mqttServer[i-2300] == 0) break; + } + for (int i = 2333; i < 2366; ++i) + { + mqttDeviceTopic[i-2333] = EEPROM.read(i); + if (mqttDeviceTopic[i-2333] == 0) break; + } + for (int i = 2366; i < 2399; ++i) + { + mqttGroupTopic[i-2366] = EEPROM.read(i); + if (mqttGroupTopic[i-2366] == 0) break; + } + } receiveDirect = !EEPROM.read(2200); enableRealtimeUI = EEPROM.read(2201); @@ -491,12 +524,6 @@ void loadSettingsFromEEPROM(bool first) presetApplyCol = EEPROM.read(2211); presetApplyFx = EEPROM.read(2212); } - - for (int i = 2220; i < 2255; ++i) - { - blynkApiKey[i-2220] = EEPROM.read(i); - if (blynkApiKey[i-2220] == 0) break; - } bootPreset = EEPROM.read(389); wifiLock = EEPROM.read(393); @@ -514,12 +541,18 @@ void loadSettingsFromEEPROM(bool first) for (int i=in; i < in+8; ++i) { if (EEPROM.read(i) == 0) break; - cssCol[i-in][k] =EEPROM.read(i); + cssCol[k][i-in] =EEPROM.read(i); }} //custom macro memory (16 slots/ each 64byte) //1024-2047 reserved + for (int i = 2220; i < 2255; ++i) + { + blynkApiKey[i-2220] = EEPROM.read(i); + if (blynkApiKey[i-2220] == 0) break; + } + //user MOD memory //2944 - 3071 reserved diff --git a/wled00/wled02_xml.ino b/wled00/wled02_xml.ino index 8aaca5638..07f55921c 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/wled02_xml.ino @@ -241,7 +241,9 @@ void getSettingsJS(byte subPage) //get values for settings form in javascript sappend('c',"AL",alexaEnabled); sappends('s',"AI",alexaInvocationName); sappend('c',"SA",notifyAlexa); - sappends('s',"BK",(char*)((blynkEnabled)?"Hidden":"")); + sappends('s',"MS",mqttServer); + sappends('s',"MD",mqttDeviceTopic); + sappends('s',"MG",mqttGroupTopic); sappend('v',"H0",hueIP[0]); sappend('v',"H1",hueIP[1]); sappend('v',"H2",hueIP[2]); diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index 487c6fa29..4d1fac9b7 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -170,6 +170,10 @@ void handleSettingsSet(byte subPage) notifyAlexa = server.hasArg("SA"); if (server.hasArg("BK") && !server.arg("BK").equals("Hidden")) {strcpy(blynkApiKey,server.arg("BK").c_str()); initBlynk(blynkApiKey);} + + strcpy(mqttServer, server.arg("MS").c_str()); + strcpy(mqttDeviceTopic, server.arg("MD").c_str()); + strcpy(mqttGroupTopic, server.arg("MG").c_str()); notifyHue = server.hasArg("SH"); for (int i=0;i<4;i++){ diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index 6ca487b7a..d29c15816 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -58,7 +58,7 @@ void wledInit() } prepareIds(); //UUID from MAC (for Alexa and MQTT) - if (mqttTopic0[0] == 0) strcpy(mqttTopic0, strcat("wled/", escapedMac.c_str())); + if (mqttDeviceTopic[0] == 0) strcpy(mqttDeviceTopic, strcat("wled/", escapedMac.c_str())); if (!onlyAP) mqttInit = initMQTT(); if (!initLedsLast) strip.service(); diff --git a/wled00/wled08_led.ino b/wled00/wled08_led.ino index c71deda14..f45e9b92a 100644 --- a/wled00/wled08_led.ino +++ b/wled00/wled08_led.ino @@ -92,12 +92,14 @@ void colorUpdated(int callMode) whiteSecIT = whiteSec; briIT = bri; if (bri > 0) briLast = bri; + notify(callMode); + if (fadeTransition) { //set correct delay if not using notification delay if (callMode != 3) transitionDelayTemp = transitionDelay; - if (transitionDelayTemp == 0) {setLedsStandard();strip.trigger();return;} + if (transitionDelayTemp == 0) {setLedsStandard(); strip.trigger(); return;} if (transitionActive) { @@ -120,11 +122,33 @@ void colorUpdated(int callMode) setLedsStandard(); strip.trigger(); } - if (callMode != 9 && callMode != 5 && callMode != 8) updateBlynk(); + + if (callMode == 8) return; + //only update Blynk and mqtt every 2 seconds to reduce lag + if (millis() - lastInterfaceUpdate <= 2000) + { + interfaceUpdateCallMode = callMode; + return; + } + updateInterfaces(callMode); +} + +void updateInterfaces(uint8_t callMode) +{ + if (callMode != 9 && callMode != 5) updateBlynk(); + publishMQTT(); + lastInterfaceUpdate = millis(); } void handleTransitions() { + //handle still pending interface update + if (interfaceUpdateCallMode && millis() - lastInterfaceUpdate > 2000) + { + updateInterfaces(interfaceUpdateCallMode); + interfaceUpdateCallMode = 0; //disable + } + if (transitionActive && transitionDelayTemp > 0) { float tper = (millis() - transitionStartTime)/(float)transitionDelayTemp; diff --git a/wled00/wled17_mqtt.ino b/wled00/wled17_mqtt.ino index 82df40eb3..93be98ed1 100644 --- a/wled00/wled17_mqtt.ino +++ b/wled00/wled17_mqtt.ino @@ -28,23 +28,38 @@ void callbackMQTT(char* topic, byte* payload, unsigned int length) { colorUpdated(1); } else if (strstr(topic, "/api")) { - handleSet(String((char*)payload)); + String apireq = "win&"; + handleSet(apireq += (char*)payload)); } else { parseMQTTBriPayload((char*)payload); } } -void publishStatus() +void publishMQTT() { if (!mqtt.connected()) return; DEBUG_PRINTLN("Publish MQTT"); - char s[4]; - sprintf(s,"%ld", bri); - mqtt.publish(strcat(mqttTopic0, "/g") , s); - XML_response(false); - mqtt.publish(strcat(mqttTopic0, "/vs"), obuf); + char s[10]; + char subuf[38]; + + sprintf(s, "%ld", bri); + strcpy(subuf, mqttDeviceTopic); + strcat(subuf, "/g"); + mqtt.publish(subuf, s); + + sprintf(s, "#%X", white*16777216 + col[0]*65536 + col[1]*256 + col[2]); + strcpy(subuf, mqttDeviceTopic); + strcat(subuf, "/c"); + mqtt.publish(subuf, s); + + //if you want to use this, increase the MQTT buffer in PubSubClient.h to 350+ + //it will publish the API response to MQTT + /*XML_response(false); + strcpy(subuf, mqttDeviceTopic); + strcat(subuf, "/v"); + mqtt.publish(subuf, obuf);*/ } bool reconnectMQTT() @@ -53,26 +68,26 @@ bool reconnectMQTT() { //re-subscribe to required topics char subuf[38]; - strcpy(subuf, mqttTopic0); + strcpy(subuf, mqttDeviceTopic); - if (mqttTopic0[0] != 0) + if (mqttDeviceTopic[0] != 0) { - strcpy(subuf, mqttTopic0); + strcpy(subuf, mqttDeviceTopic); mqtt.subscribe(subuf); strcat(subuf, "/col"); mqtt.subscribe(subuf); - strcpy(subuf, mqttTopic0); + strcpy(subuf, mqttDeviceTopic); strcat(subuf, "/api"); mqtt.subscribe(subuf); } - if (mqttTopic1[0] != 0) + if (mqttGroupTopic[0] != 0) { - strcpy(subuf, mqttTopic1); + strcpy(subuf, mqttGroupTopic); mqtt.subscribe(subuf); strcat(subuf, "/col"); mqtt.subscribe(subuf); - strcpy(subuf, mqttTopic1); + strcpy(subuf, mqttGroupTopic); strcat(subuf, "/api"); mqtt.subscribe(subuf); } @@ -100,12 +115,20 @@ bool initMQTT() void handleMQTT() { if (WiFi.status() != WL_CONNECTED || !mqttInit) return; - if (!mqtt.connected() && millis() - lastMQTTReconnectAttempt > 5000) + + //every time connection is unsuccessful, the attempt interval is increased, since attempt will block program for 7 sec each time + if (!mqtt.connected() && millis() - lastMQTTReconnectAttempt > 5000 + (5000 * mqttFailedConAttempts * mqttFailedConAttempts)) { DEBUG_PRINTLN("Attempting to connect MQTT..."); lastMQTTReconnectAttempt = millis(); - if (!reconnectMQTT()) return; + if (!reconnectMQTT()) + { + //still attempt reconnect about once daily + if (mqttFailedConAttempts < 120) mqttFailedConAttempts++; + return; + } DEBUG_PRINTLN("MQTT con!"); + mqttFailedConAttempts = 0; } mqtt.loop(); } diff --git a/wled_logo.png b/wled_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..905c793f7040843ded56cdc2b90ae3757c5afb1b GIT binary patch literal 47543 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

        -DOqULh=T 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 JnpEuL{twr1z83%h literal 0 HcmV?d00001 From c2972786f5b826810e939babd389f81921da9f39 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Thu, 4 Oct 2018 18:17:01 +0200 Subject: [PATCH 14/22] Attempted to improve AP only stability --- wled00/wled00.ino | 25 ++++++++----- wled00/wled05_init.ino | 76 +++++++++++++++++++++++----------------- wled00/wled07_notify.ino | 9 ++--- wled00/wled15_hue.ino | 12 +++---- wled00/wled16_blynk.ino | 1 + wled00/wled17_mqtt.ino | 38 ++++++++++---------- 6 files changed, 91 insertions(+), 70 deletions(-) diff --git a/wled00/wled00.ino b/wled00/wled00.ino index cf212f011..4f2646bea 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -59,7 +59,7 @@ char otaPass[33] = "wledota"; //to toggle usb serial debug (un)comment following line(s) -//#define DEBUG +#define DEBUG //Hardware CONFIG (only changeble HERE, not at runtime) @@ -371,16 +371,16 @@ WebServer server(80); #else ESP8266WebServer server(80); #endif -HTTPClient hueClient; -WiFiClient mqttTCPClient; -PubSubClient mqtt(mqttTCPClient); +HTTPClient* hueClient = NULL; +WiFiClient* mqttTCPClient = NULL; +PubSubClient* mqtt = NULL; ESP8266HTTPUpdateServer httpUpdater; //udp interface objects WiFiUDP notifierUdp, rgbUdp; WiFiUDP ntpUdp; -E131 e131; +E131* e131; //led fx library object WS2812FX strip = WS2812FX(); @@ -477,19 +477,26 @@ void loop() { yield(); handleButton(); handleNetworkTime(); - handleAlexa(); + if (!onlyAP) + { + handleAlexa(); + handleMQTT(); + } + handleOverlays(); yield(); - handleMQTT(); + if (!realtimeActive) //block stuff if WARLS/Adalight is enabled { if (dnsActive) dnsServer.processNextRequest(); if (aOtaEnabled) ArduinoOTA.handle(); - handleHue(); handleNightlight(); - handleBlynk(); + if (!onlyAP) { + handleHue(); + handleBlynk(); + } if (briT) strip.service(); //do not update strip if off, prevents flicker on ESP32 } diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index d29c15816..a233b8fbd 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -49,18 +49,24 @@ void wledInit() if (ntpEnabled && WiFi.status() == WL_CONNECTED) ntpConnected = ntpUdp.begin(ntpLocalPort); - //start captive portal - if (onlyAP || strlen(apSSID) > 0) + //start captive portal if AP active + if (onlyAP || strlen(apSSID) > 0) { - //dnsServer.setErrorReplyCode(DNSReplyCode::NoError); dnsServer.start(53, "*", WiFi.softAPIP()); dnsActive = true; } - + prepareIds(); //UUID from MAC (for Alexa and MQTT) if (mqttDeviceTopic[0] == 0) strcpy(mqttDeviceTopic, strcat("wled/", escapedMac.c_str())); - if (!onlyAP) mqttInit = initMQTT(); - + + //smartInit, we only init some resources when connected + if (!onlyAP && WiFi.status() == WL_CONNECTED) + { + mqttTCPClient = new WiFiClient(); + mqtt = new PubSubClient(*mqttTCPClient); + mqttInit = initMQTT(); + } + if (!initLedsLast) strip.service(); //HTTP server page init @@ -68,38 +74,42 @@ void wledInit() if (!initLedsLast) strip.service(); //init Alexa hue emulation - if (alexaEnabled) alexaInit(); + if (alexaEnabled && !onlyAP) alexaInit(); server.begin(); DEBUG_PRINTLN("HTTP server started"); //init ArduinoOTA - if (aOtaEnabled) - { - ArduinoOTA.onStart([]() { - #ifndef ARDUINO_ARCH_ESP32 - wifi_set_sleep_type(NONE_SLEEP_T); - #endif - DEBUG_PRINTLN("Start ArduinoOTA"); - }); - if (strlen(cmDNS) > 0) ArduinoOTA.setHostname(cmDNS); - ArduinoOTA.begin(); - } + if (!onlyAP) { + if (aOtaEnabled) + { + ArduinoOTA.onStart([]() { + #ifndef ARDUINO_ARCH_ESP32 + wifi_set_sleep_type(NONE_SLEEP_T); + #endif + DEBUG_PRINTLN("Start ArduinoOTA"); + }); + if (strlen(cmDNS) > 0) ArduinoOTA.setHostname(cmDNS); + ArduinoOTA.begin(); + } + + if (!initLedsLast) strip.service(); + // Set up mDNS responder: + if (strlen(cmDNS) > 0 && !onlyAP) + { + MDNS.begin(cmDNS); + DEBUG_PRINTLN("mDNS responder started"); + // Add service to MDNS + MDNS.addService("http", "tcp", 80); + } + if (!initLedsLast) strip.service(); - if (!initLedsLast) strip.service(); - // Set up mDNS responder: - if (strlen(cmDNS) > 0 && !onlyAP) - { - MDNS.begin(cmDNS); - DEBUG_PRINTLN("mDNS responder started"); - // Add service to MDNS - MDNS.addService("http", "tcp", 80); - } + initBlynk(blynkApiKey); + initE131(); - if (!onlyAP) - { - initBlynk(blynkApiKey); - initE131(); + hueClient = new HTTPClient(); + } else { + e131Enabled = false; } if (initLedsLast) initStrip(); @@ -146,12 +156,12 @@ void initCon() if (strlen(apSSID)>0) { - DEBUG_PRINT("USING AP"); + DEBUG_PRINT(" USING AP"); DEBUG_PRINTLN(strlen(apSSID)); initAP(); } else { - DEBUG_PRINTLN("NO AP"); + DEBUG_PRINTLN(" NO AP"); WiFi.softAPdisconnect(true); } int fail_count = 0; diff --git a/wled00/wled07_notify.ino b/wled00/wled07_notify.ino index 0242d3978..d77f5bba8 100644 --- a/wled00/wled07_notify.ino +++ b/wled00/wled07_notify.ino @@ -69,7 +69,8 @@ void arlsLock(uint32_t timeoutMs) void initE131(){ if (WiFi.status() == WL_CONNECTED && e131Enabled) { - e131.begin((e131Multicast) ? E131_MULTICAST : E131_UNICAST , e131Universe); + e131 = new E131(); + e131->begin((e131Multicast) ? E131_MULTICAST : E131_UNICAST , e131Universe); } else { e131Enabled = false; } @@ -84,14 +85,14 @@ void handleNotifications() //E1.31 protocol support if(e131Enabled) { - uint16_t len = e131.parsePacket(); - if (len && e131.universe == e131Universe) { + uint16_t len = e131->parsePacket(); + if (len && e131->universe == e131Universe) { arlsLock(realtimeTimeoutMs); if (len > ledCount) len = ledCount; for (uint16_t i = 0; i < len; i++) { int j = i * 3; - setRealtimePixel(i, e131.data[j], e131.data[j+1], e131.data[j+2], 0); + setRealtimePixel(i, e131->data[j], e131->data[j+1], e131->data[j+2], 0); } strip.show(); } diff --git a/wled00/wled15_hue.ino b/wled00/wled15_hue.ino index b7f565453..dbc9f73ad 100644 --- a/wled00/wled15_hue.ino +++ b/wled00/wled15_hue.ino @@ -4,7 +4,7 @@ void handleHue() { - if (huePollingEnabled && WiFi.status() == WL_CONNECTED) + if (huePollingEnabled && WiFi.status() == WL_CONNECTED && hueClient != NULL) { if (millis() - hueLastRequestSent > huePollIntervalMsTemp) { @@ -44,8 +44,8 @@ bool setupHue() bool sendHuePoll(bool sAuth) { bool st; - hueClient.setReuse(true); - hueClient.setTimeout(450); + hueClient->setReuse(true); + hueClient->setTimeout(450); String hueURL = "http://"; hueURL += hueIP.toString(); hueURL += "/api/"; @@ -53,12 +53,12 @@ bool sendHuePoll(bool sAuth) hueURL += hueApiKey; hueURL += "/lights/" + String(huePollLightId); } - hueClient.begin(hueURL); - int httpCode = (sAuth)? hueClient.POST("{\"devicetype\":\"wled#esp\"}"):hueClient.GET(); + hueClient->begin(hueURL); + int httpCode = (sAuth)? hueClient->POST("{\"devicetype\":\"wled#esp\"}"):hueClient->GET(); //TODO this request may block operation for ages if (httpCode>0){ - st = handleHueResponse(hueClient.getString(),sAuth); + st = handleHueResponse(hueClient->getString(),sAuth); } else { strcpy(hueError,"Request timed out"); st = false; diff --git a/wled00/wled16_blynk.ino b/wled00/wled16_blynk.ino index 82aae164d..e8c22f4c7 100644 --- a/wled00/wled16_blynk.ino +++ b/wled00/wled16_blynk.ino @@ -20,6 +20,7 @@ void handleBlynk() void updateBlynk() { + if (onlyAP) return; Blynk.virtualWrite(V0,bri); //we need a RGB -> HSB convert here Blynk.virtualWrite(V3,bri); diff --git a/wled00/wled17_mqtt.ino b/wled00/wled17_mqtt.ino index 93be98ed1..5b69ee512 100644 --- a/wled00/wled17_mqtt.ino +++ b/wled00/wled17_mqtt.ino @@ -29,7 +29,8 @@ void callbackMQTT(char* topic, byte* payload, unsigned int length) { } else if (strstr(topic, "/api")) { String apireq = "win&"; - handleSet(apireq += (char*)payload)); + apireq += (char*)payload; + handleSet(apireq); } else { parseMQTTBriPayload((char*)payload); @@ -38,7 +39,8 @@ void callbackMQTT(char* topic, byte* payload, unsigned int length) { void publishMQTT() { - if (!mqtt.connected()) return; + if (mqtt == NULL) return; + if (!mqtt->connected()) return; DEBUG_PRINTLN("Publish MQTT"); char s[10]; @@ -47,24 +49,24 @@ void publishMQTT() sprintf(s, "%ld", bri); strcpy(subuf, mqttDeviceTopic); strcat(subuf, "/g"); - mqtt.publish(subuf, s); + mqtt->publish(subuf, s); sprintf(s, "#%X", white*16777216 + col[0]*65536 + col[1]*256 + col[2]); strcpy(subuf, mqttDeviceTopic); strcat(subuf, "/c"); - mqtt.publish(subuf, s); + mqtt->publish(subuf, s); //if you want to use this, increase the MQTT buffer in PubSubClient.h to 350+ //it will publish the API response to MQTT /*XML_response(false); strcpy(subuf, mqttDeviceTopic); strcat(subuf, "/v"); - mqtt.publish(subuf, obuf);*/ + mqtt->publish(subuf, obuf);*/ } bool reconnectMQTT() { - if (mqtt.connect(escapedMac.c_str())) + if (mqtt->connect(escapedMac.c_str())) { //re-subscribe to required topics char subuf[38]; @@ -73,26 +75,26 @@ bool reconnectMQTT() if (mqttDeviceTopic[0] != 0) { strcpy(subuf, mqttDeviceTopic); - mqtt.subscribe(subuf); + mqtt->subscribe(subuf); strcat(subuf, "/col"); - mqtt.subscribe(subuf); + mqtt->subscribe(subuf); strcpy(subuf, mqttDeviceTopic); strcat(subuf, "/api"); - mqtt.subscribe(subuf); + mqtt->subscribe(subuf); } if (mqttGroupTopic[0] != 0) { strcpy(subuf, mqttGroupTopic); - mqtt.subscribe(subuf); + mqtt->subscribe(subuf); strcat(subuf, "/col"); - mqtt.subscribe(subuf); + mqtt->subscribe(subuf); strcpy(subuf, mqttGroupTopic); strcat(subuf, "/api"); - mqtt.subscribe(subuf); + mqtt->subscribe(subuf); } } - return mqtt.connected(); + return mqtt->connected(); } bool initMQTT() @@ -103,11 +105,11 @@ bool initMQTT() IPAddress mqttIP; if (mqttIP.fromString(mqttServer)) //see if server is IP or domain { - mqtt.setServer(mqttIP,1883); + mqtt->setServer(mqttIP,1883); } else { - mqtt.setServer(mqttServer,1883); + mqtt->setServer(mqttServer,1883); } - mqtt.setCallback(callbackMQTT); + mqtt->setCallback(callbackMQTT); DEBUG_PRINTLN("MQTT ready."); return true; } @@ -117,7 +119,7 @@ void handleMQTT() if (WiFi.status() != WL_CONNECTED || !mqttInit) return; //every time connection is unsuccessful, the attempt interval is increased, since attempt will block program for 7 sec each time - if (!mqtt.connected() && millis() - lastMQTTReconnectAttempt > 5000 + (5000 * mqttFailedConAttempts * mqttFailedConAttempts)) + if (!mqtt->connected() && millis() - lastMQTTReconnectAttempt > 5000 + (5000 * mqttFailedConAttempts * mqttFailedConAttempts)) { DEBUG_PRINTLN("Attempting to connect MQTT..."); lastMQTTReconnectAttempt = millis(); @@ -130,5 +132,5 @@ void handleMQTT() DEBUG_PRINTLN("MQTT con!"); mqttFailedConAttempts = 0; } - mqtt.loop(); + mqtt->loop(); } From 43e97436453c0dffb759a1c5fead19d2dcc09943 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Thu, 4 Oct 2018 22:38:39 +0200 Subject: [PATCH 15/22] Fixed ESP32 version Fixed Adalight serial Ambilight --- wled00/wled00.ino | 10 +++++----- wled00/wled04_file.ino | 6 +----- wled00/wled05_init.ino | 8 ++++++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 4f2646bea..af0920b1d 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -45,7 +45,7 @@ //version code in format yymmddb (b = daily build) -#define VERSION 1810031 +#define VERSION 1810041 char versionString[] = "0.8.0"; @@ -54,14 +54,14 @@ char apPass[65] = "wled1234"; char otaPass[33] = "wledota"; +//to toggle usb serial debug (un)comment following line(s) +//#define DEBUG + + //spiffs FS only useful for debug (only ESP8266) //#define USEFS -//to toggle usb serial debug (un)comment following line(s) -#define DEBUG - - //Hardware CONFIG (only changeble HERE, not at runtime) //LED strip pin changeable in NpbWrapper.h. Only change for ESP32 byte buttonPin = 0; //needs pull-up diff --git a/wled00/wled04_file.ino b/wled00/wled04_file.ino index 955a499ff..d36aaf460 100644 --- a/wled00/wled04_file.ino +++ b/wled00/wled04_file.ino @@ -7,11 +7,7 @@ void handleSerial() { if (Serial.find("Ada")) { - if (!realtimeActive){ - if (bri == 0) strip.setBrightness(briLast); - strip.setRange(0, ledCount-1, 0); - strip.setMode(0); - } + if (!realtimeActive && bri == 0) strip.setBrightness(briLast); arlsLock(realtimeTimeoutMs); delay(1); byte hi = Serial.read(); diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index a233b8fbd..af2646419 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -57,8 +57,12 @@ void wledInit() } prepareIds(); //UUID from MAC (for Alexa and MQTT) - if (mqttDeviceTopic[0] == 0) strcpy(mqttDeviceTopic, strcat("wled/", escapedMac.c_str())); - + if (mqttDeviceTopic[0] == 0) + { + strcpy(mqttDeviceTopic, "wled/"); + strcat(mqttDeviceTopic, escapedMac.c_str()); + } + //smartInit, we only init some resources when connected if (!onlyAP && WiFi.status() == WL_CONNECTED) { From c1cdf27507c5a3cc556bab373e221c5f59e897ed Mon Sep 17 00:00:00 2001 From: cschwinne Date: Thu, 4 Oct 2018 22:41:35 +0200 Subject: [PATCH 16/22] Release of v0.8.0 to dev branch Updating master after more testing --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 6e66a6950..88b8b3e49 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -![WLED logo](https://raw.githubusercontent.com/Aircoookie/WLED/master/wled_logo.png) +![WLED logo](https://raw.githubusercontent.com/Aircoookie/WLED/development/wled_logo.png) ## Welcome to my project WLED! From 75a61f85db04620954daa2b7c08586550308f7b2 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Thu, 4 Oct 2018 23:16:25 +0200 Subject: [PATCH 17/22] Cleaned up readme --- readme.md | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/readme.md b/readme.md index 88b8b3e49..08a325b73 100644 --- a/readme.md +++ b/readme.md @@ -1,32 +1,34 @@ ![WLED logo](https://raw.githubusercontent.com/Aircoookie/WLED/development/wled_logo.png) -## Welcome to my project WLED! +## Welcome to my project WLED! (v0.8.0) -WLED is a fast, advanced and (relatively) secure implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B) LEDs! +A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B) LEDs! -### Features: (v0.8.0) -- RGB, HSB, and brightness sliders -- All new, mobile-friendly web UI! +### Features: +- WS2812FX library integrated for over 70 special effects +- FastLED noise effects and palettes +- Customizable Mobile and desktop UI with color and effect controls - Settings page - configuration over network - Access Point and station mode - automatic failsafe AP -- Support of Blynk IoT cloud and MQTT -- WS2812FX library integrated for over 70 special effects (with FastLED palettes)! -- Secondary color support lets you use even more effect combinations -- Alexa smart home device server (including dimming) -- Beta syncronization to Philips hue lights -- Realtime UDP Packet Control (E1.31, Hyperion, WARLS, DRGB, DRGBW) - Support for RGBW strips -- 25 user presets! Save colors and effects and apply them easily! Supports cycling through them. -- HTTP request API for simple integration +- 25 user presets to save and load colors/effects easily, supports cycling through them. - Macro functions to automatically execute API calls - Nightlight function (gradually dims down) -- Notifier function (multiple ESPs sync color via UDP broadcast) -- Support for power pushbutton -- Support for the Adalight serial ambilight protocol! -- Full OTA software update capability (HTTP and ArduinoOTA) -- Password protected OTA page for added security (OTA lock) -- NTP and configurable analog clock function -- Support for the Cronixie Clock kit by Diamex +- Full OTA software updatability (HTTP + ArduinoOTA), password protectable +- Configurable analog clock + support for the Cronixie kit by Diamex + +### Supported light control interfaces: +- HTTP request API +- Blynk IoT +- MQTT +- E1.31 +- Hyperion +- UDP realtime +- Alexa smart device (including dimming) +- Sync to Philips hue lights +- Adalight (PC ambilight via serial) +- Sync color of multiple WLED devices (UDP notifier) +- Simple timers/schedules (time from NTP, timezones/DST supported) ### Quick start guide and documentation: @@ -40,8 +42,8 @@ Credits in About page! Uses Linearicons by Perxis! Join the Discord [server](https://discord.gg/KuqP7NE) to discuss everything about WLED! -You can also send me mails to [dev.aircoookie@gmail.com](mailto:dev.aircoookie@gmail.com). -If you insist that you just love WLED too much, you can [send me a gift](https://paypal.me/aircoookie) +You can also send me mails to [dev.aircoookie@gmail.com](mailto:dev.aircoookie@gmail.com). +If you insist that you just love WLED too much, you can [send me a gift](https://paypal.me/aircoookie)! From f98b0beee55e1031aa034c95399179f6832a586b Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sat, 6 Oct 2018 14:53:15 +0200 Subject: [PATCH 18/22] Added Mobile UI power indicator --- wled00/data/index_mobile.htm | 41 ++++++++++++++++++++++++++++++++---- wled00/htmls00.h | 40 ++++++++++++++--------------------- wled00/wled00.ino | 2 +- wled00/wled03_set.ino | 1 + 4 files changed, 55 insertions(+), 29 deletions(-) diff --git a/wled00/data/index_mobile.htm b/wled00/data/index_mobile.htm index 2d063bf0c..d198111a5 100644 --- a/wled00/data/index_mobile.htm +++ b/wled00/data/index_mobile.htm @@ -272,9 +272,9 @@

        WLED

        - + - +

        FX 2nd Color

        @@ -494,9 +494,11 @@ !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.iro=e():t.iro=e()}(this,function(){return function(t){function e(s){if(r[s])return r[s].exports;var n=r[s]={i:s,l:!1,exports:{}};return t[s].call(n.exports,n,n.exports,e),n.l=!0,n.exports}var r={};return e.m=t,e.c=r,e.i=function(t){return t},e.d=function(t,r,s){e.o(t,r)||Object.defineProperty(t,r,{configurable:!1,enumerable:!0,get:s})},e.n=function(t){var r=t&&t.t?function(){return t.default}:function(){return t};return e.d(r,"a",r),r},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=4)}([function(t,e,r){"use strict";function s(t){var e,r,s,n,i,o,a,h,c=t.h/360,u=t.s/100,l=t.v/100;switch(n=k(6*c),i=6*c-n,o=l*(1-u),a=l*(1-i*u),h=l*(1-(1-i)*u),n%6){case 0:e=l,r=h,s=o;break;case 1:e=a,r=l,s=o;break;case 2:e=o,r=l,s=h;break;case 3:e=o,r=a,s=l;break;case 4:e=h,r=o,s=l;break;case 5:e=l,r=o,s=a}return{r:w(255*e),g:w(255*r),b:w(255*s)}}function n(t){var e,r=t.r/255,s=t.g/255,n=t.b/255,i=Math.max(r,s,n),o=Math.min(r,s,n),a=i-o;switch(i){case o:e=0;break;case r:e=(s-n)/a+(s>2*n&s)*i,g:(e>>n&s)*i,b:(e&s)*i}}function p(t){return t instanceof x?t:new x(t)}function d(t,e,r){return t<=e?e:t>=r?r:t}function g(t,e){var r={};for(var s in t)r[s]=e[s]!=t[s];return r}function _(t,e,r){var s=p(t).rgb,n=p(e).rgb;return r=d(r/100||.5,0,1),new x({r:k(s.r+(n.r-s.r)*r),g:k(s.g+(n.g-s.g)*r),b:k(s.b+(n.b-s.b)*r)})}function m(t,e){var r=p(t),s=r.hsv;return s.v=d(s.v+e,0,100),r.hsv=s,r}function y(t,e){var r=p(t),s=r.hsv;return s.v=d(s.v-e,0,100),r.hsv=s,r}var b="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},w=Math.round,k=Math.floor,x=function(t){this.e=!1,this.u={h:void 0,s:void 0,v:void 0},t&&this.set(t)};x.mix=_,x.lighten=m,x.darken=y,x.hsv2Rgb=s,x.rgb2Hsv=n,x.hsv2Hsl=i,x.hsl2Hsv=o,x.hsl2Str=h,x.rgb2Str=a,x.rgb2Hex=c,x.parseHexStr=v,x.parseHslStr=f,x.parseRgbStr=l,x.prototype={constructor:x,set:function(t){"object"==(void 0===t?"undefined":b(t))?t instanceof x?this.hsv=x.hsv:"r"in t?this.rgb=t:"v"in t?this.hsv=t:"l"in t&&(this.hsl=t):"string"==typeof t&&(/^rgb/.test(t)?this.rgbString=t:/^hsl/.test(t)?this.hslString=t:/^#[0-9A-Fa-f]/.test(t)&&(this.hexString=t))},setChannel:function(t,e,r){var s=this[t];s[e]=r,this[t]=s},clone:function(){return new x(this)},compare:function(t,e){return e=e||"hsv",g(this[e],p(t)[e])},mix:function(t,e){this.hsv=_(this,t,e).hsv},lighten:function(t){m(this,t)},darken:function(t){y(this,t)}},Object.defineProperties(x.prototype,{hsv:{get:function(){var t=this.u;return{h:t.h,s:t.s,v:t.v}},set:function(t){if(this.e){var e=this.u;for(var r in e)t.hasOwnProperty(r)||(t[r]=e[r]);var s=g(e,t);this.u=t,(s.h||s.s||s.v)&&this.e(this,s)}else this.u=t}},rgb:{get:function(){return s(this.u)},set:function(t){this.hsv=n(t)}},hsl:{get:function(){return i(this.u)},set:function(t){this.hsv=o(t)}},rgbString:{get:function(){return a(this.rgb)},set:function(t){this.rgb=l(t)}},hexString:{get:function(){return c(this.rgb)},set:function(t){this.rgb=v(t)}},hslString:{get:function(){return h(this.hsl)},set:function(t){this.hsl=x.parseHslStr(t)}}}),t.exports=x},function(t,e,r){"use strict";var s=function(){var t=document.createElement("style");document.head.appendChild(t),t.appendChild(document.createTextNode("")),this.style=t;var e=t.sheet;this.sheet=e,this.rules=e.rules||e.cssRules,this.map={}};s.prototype={constructor:s,setRule:function(t,e,r){var s=this.sheet,n=s.rules||s.cssRules,i=this.map;if(e=e.replace(/([A-Z])/g,function(t){return"-"+t.toLowerCase()}),i.hasOwnProperty(t))i[t].setProperty(e,r);else{var o=n.length,a=e+": "+r;try{s.insertRule(t+" {"+a+";}",o)}catch(e){s.addRule(t,a,o)}finally{n=s.rules||s.cssRules,i[t]=n[o].style}}}},Object.defineProperties(s.prototype,{enabled:{get:function(){return!this.sheet.disabled},set:function(t){this.sheet.disabled=!t}},cssText:{get:function(){var t=this.map,e=[];for(var r in t)e.push(r.replace(/,\W/g,",\n")+" {\n\t"+t[r].cssText.replace(/;\W/g,";\n\t")+"\n}");return e.join("\n")}},css:{get:function(){var t=this.map,e={};for(var r in t){var s=t[r];e[r]={};for(var n=0;n1?s-1:0),i=1;ir.x&&tr.y&&e -