diff --git a/CHANGELOG.md b/CHANGELOG.md index 46f6df2de..0d86c8b2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ## WLED changelog +#### Build 2404050 +- Blending styles (with help from @tkadauke) + #### Build 2403280 - Individual color channel control for JSON API (fixes #3860) - "col":[int|string|object|array, int|string|object|array, int|string|object|array] diff --git a/usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h b/usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h index f712316b8..707479df1 100644 --- a/usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h +++ b/usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h @@ -96,7 +96,7 @@ void setup() { jsonTransitionOnce = true; strip.setTransition(0); //no transition effectCurrent = FX_MODE_COLOR_WIPE; - resetTimebase(); //make sure wipe starts from beginning + strip.resetTimebase(); //make sure wipe starts from beginning //set wipe direction Segment& seg = strip.getSegment(0); diff --git a/usermods/stairway_wipe_basic/wled06_usermod.ino b/usermods/stairway_wipe_basic/wled06_usermod.ino deleted file mode 100644 index c1264ebfb..000000000 --- a/usermods/stairway_wipe_basic/wled06_usermod.ino +++ /dev/null @@ -1,111 +0,0 @@ -/* - * This file allows you to add own functionality to WLED more easily - * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality - * EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in wled_eeprom.h) - * bytes 2400+ are currently ununsed, but might be used for future wled features - */ - -//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t) - -byte wipeState = 0; //0: inactive 1: wiping 2: solid -unsigned long timeStaticStart = 0; -uint16_t previousUserVar0 = 0; - -//comment this out if you want the turn off effect to be just fading out instead of reverse wipe -#define STAIRCASE_WIPE_OFF - -//gets called once at boot. Do all initialization that doesn't depend on network here -void userSetup() -{ - //setup PIR sensor here, if needed -} - -//gets called every time WiFi is (re-)connected. Initialize own network interfaces here -void userConnected() -{ - -} - -//loop. You can use "if (WLED_CONNECTED)" to check for successful connection -void userLoop() -{ - //userVar0 (U0 in HTTP API): - //has to be set to 1 if movement is detected on the PIR that is the same side of the staircase as the ESP8266 - //has to be set to 2 if movement is detected on the PIR that is the opposite side - //can be set to 0 if no movement is detected. Otherwise LEDs will turn off after a configurable timeout (userVar1 seconds) - - if (userVar0 > 0) - { - if ((previousUserVar0 == 1 && userVar0 == 2) || (previousUserVar0 == 2 && userVar0 == 1)) wipeState = 3; //turn off if other PIR triggered - previousUserVar0 = userVar0; - - if (wipeState == 0) { - startWipe(); - wipeState = 1; - } else if (wipeState == 1) { //wiping - uint32_t cycleTime = 360 + (255 - effectSpeed)*75; //this is how long one wipe takes (minus 25 ms to make sure we switch in time) - if (millis() + strip.timebase > (cycleTime - 25)) { //wipe complete - effectCurrent = FX_MODE_STATIC; - timeStaticStart = millis(); - colorUpdated(CALL_MODE_NOTIFICATION); - wipeState = 2; - } - } else if (wipeState == 2) { //static - if (userVar1 > 0) //if U1 is not set, the light will stay on until second PIR or external command is triggered - { - if (millis() - timeStaticStart > userVar1*1000) wipeState = 3; - } - } else if (wipeState == 3) { //switch to wipe off - #ifdef STAIRCASE_WIPE_OFF - effectCurrent = FX_MODE_COLOR_WIPE; - strip.timebase = 360 + (255 - effectSpeed)*75 - millis(); //make sure wipe starts fully lit - colorUpdated(CALL_MODE_NOTIFICATION); - wipeState = 4; - #else - turnOff(); - #endif - } else { //wiping off - if (millis() + strip.timebase > (725 + (255 - effectSpeed)*150)) turnOff(); //wipe complete - } - } else { - wipeState = 0; //reset for next time - if (previousUserVar0) { - #ifdef STAIRCASE_WIPE_OFF - userVar0 = previousUserVar0; - wipeState = 3; - #else - turnOff(); - #endif - } - previousUserVar0 = 0; - } -} - -void startWipe() -{ - bri = briLast; //turn on - transitionDelayTemp = 0; //no transition - effectCurrent = FX_MODE_COLOR_WIPE; - resetTimebase(); //make sure wipe starts from beginning - - //set wipe direction - Segment& seg = strip.getSegment(0); - bool doReverse = (userVar0 == 2); - seg.setOption(1, doReverse); - - colorUpdated(CALL_MODE_NOTIFICATION); -} - -void turnOff() -{ - #ifdef STAIRCASE_WIPE_OFF - transitionDelayTemp = 0; //turn off immediately after wipe completed - #else - transitionDelayTemp = 4000; //fade out slowly - #endif - bri = 0; - stateUpdated(CALL_MODE_NOTIFICATION); - wipeState = 0; - userVar0 = 0; - previousUserVar0 = 0; -} diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 14341f5b9..e75e20cd2 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -1211,8 +1211,9 @@ static const char _data_FX_MODE_COMET[] PROGMEM = "Lighthouse@!,Fade rate;!,!;!" */ uint16_t mode_fireworks() { if (SEGLEN == 1) return mode_static(); - const uint16_t width = SEGMENT.is2D() ? SEGMENT.virtualWidth() : SEGMENT.virtualLength(); - const uint16_t height = SEGMENT.virtualHeight(); + const unsigned width = SEGMENT.is2D() ? SEGMENT.virtualWidth() : SEGMENT.virtualLength(); + const unsigned height = SEGMENT.virtualHeight(); + const unsigned dimension = width * height; if (SEGENV.call == 0) { SEGENV.aux0 = UINT16_MAX; @@ -1220,19 +1221,19 @@ uint16_t mode_fireworks() { } SEGMENT.fade_out(128); - bool valid1 = (SEGENV.aux0 < width*height); - bool valid2 = (SEGENV.aux1 < width*height); - uint8_t x = SEGENV.aux0%width, y = SEGENV.aux0/width; // 2D coordinates stored in upper and lower byte + bool valid1 = (SEGENV.aux0 < dimension); + bool valid2 = (SEGENV.aux1 < dimension); + unsigned x = SEGENV.aux0%width, y = SEGENV.aux0/width; // 2D coordinates stored in upper and lower byte uint32_t sv1 = 0, sv2 = 0; if (valid1) sv1 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(x, y) : SEGMENT.getPixelColor(SEGENV.aux0); // get spark color if (valid2) sv2 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(x, y) : SEGMENT.getPixelColor(SEGENV.aux1); - if (!SEGENV.step) SEGMENT.blur(16); + if (!SEGENV.step) SEGMENT.blur(dimension > 100 ? 16 : 8); if (valid1) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(x, y, sv1); else SEGMENT.setPixelColor(SEGENV.aux0, sv1); } // restore spark color after blur if (valid2) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(x, y, sv2); else SEGMENT.setPixelColor(SEGENV.aux1, sv2); } // restore old spark color after blur - for (int i=0; i> 1)) == 0) { - uint16_t index = random16(width*height); + unsigned index = random16(dimension); x = index % width; y = index / width; uint32_t col = SEGMENT.color_from_palette(random8(), false, false, 0); @@ -2066,41 +2067,41 @@ uint16_t mode_fire_2012() { struct virtualStrip { static void runStrip(uint16_t stripNr, byte* heat, uint32_t it) { - const uint8_t ignition = max(3,SEGLEN/10); // ignition area: 10% of segment length or minimum 3 pixels + const unsigned ignition = max(3,SEGLEN/10); // ignition area: 10% of segment length or minimum 3 pixels // Step 1. Cool down every cell a little - for (int i = 0; i < SEGLEN; i++) { - uint8_t cool = (it != SEGENV.step) ? random8((((20 + SEGMENT.speed/3) * 16) / SEGLEN)+2) : random8(4); - uint8_t minTemp = (i 1; k--) { + for (unsigned k = SEGLEN -1; k > 1; k--) { heat[k] = (heat[k - 1] + (heat[k - 2]<<1) ) / 3; // heat[k-2] multiplied by 2 } // Step 3. Randomly ignite new 'sparks' of heat near the bottom if (random8() <= SEGMENT.intensity) { - uint8_t y = random8(ignition); - uint8_t boost = (17+SEGMENT.custom3) * (ignition - y/2) / ignition; // integer math! + unsigned y = random8(ignition); + unsigned boost = (17+SEGMENT.custom3) * (ignition - y/2) / ignition; // integer math! heat[y] = qadd8(heat[y], random8(96+2*boost,207+boost)); } } // Step 4. Map from heat cells to LED colors for (int j = 0; j < SEGLEN; j++) { - SEGMENT.setPixelColor(indexToVStrip(j, stripNr), ColorFromPalette(SEGPALETTE, MIN(heat[j],240), 255, NOBLEND)); + SEGMENT.setPixelColor(indexToVStrip(j, stripNr), ColorFromPalette(SEGPALETTE, MIN(heat[j],240), 255, LINEARBLEND_NOWRAP)); } } }; - for (int stripNr=0; stripNr 100 ? 32 : 0); if (it != SEGENV.step) SEGENV.step = it; @@ -4856,9 +4857,9 @@ static const char _data_FX_MODE_FLOWSTRIPE[] PROGMEM = "Flow Stripe@Hue speed,Ef uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulmatelights.com/gallery/1012 , Modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const uint16_t cols = SEGMENT.virtualWidth(); - const uint16_t rows = SEGMENT.virtualHeight(); - uint16_t x, y; + const unsigned cols = SEGMENT.virtualWidth(); + const unsigned rows = SEGMENT.virtualHeight(); + unsigned x, y; SEGMENT.fadeToBlackBy(16 + (SEGMENT.speed>>3)); // create fading trails unsigned long t = strip.now/128; // timebase @@ -4877,7 +4878,7 @@ uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulma // central white dot SEGMENT.setPixelColorXY(cols/2, rows/2, WHITE); // blur everything a bit - SEGMENT.blur(16); + SEGMENT.blur(sqrt16(cols*rows) > 100 ? 16 : 0); return FRAMETIME; } // mode_2DBlackHole() @@ -6436,8 +6437,8 @@ static const char _data_FX_MODE_2DSWIRL[] PROGMEM = "Swirl@!,Sensitivity,Blur;,B uint16_t mode_2DWaverly(void) { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const uint16_t cols = SEGMENT.virtualWidth(); - const uint16_t rows = SEGMENT.virtualHeight(); + const unsigned cols = SEGMENT.virtualWidth(); + const unsigned rows = SEGMENT.virtualHeight(); um_data_t *um_data; if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { @@ -6449,21 +6450,21 @@ uint16_t mode_2DWaverly(void) { SEGMENT.fadeToBlackBy(SEGMENT.speed); long t = strip.now / 2; - for (int i = 0; i < cols; i++) { - uint16_t thisVal = (1 + SEGMENT.intensity/64) * inoise8(i * 45 , t , t)/2; + for (unsigned i = 0; i < cols; i++) { + unsigned thisVal = (1 + SEGMENT.intensity/64) * inoise8(i * 45 , t , t)/2; // use audio if available if (um_data) { thisVal /= 32; // reduce intensity of inoise8() thisVal *= volumeSmth; } - uint16_t thisMax = map(thisVal, 0, 512, 0, rows); + unsigned thisMax = map(thisVal, 0, 512, 0, rows); - for (int j = 0; j < thisMax; j++) { + for (unsigned j = 0; j < thisMax; j++) { SEGMENT.addPixelColorXY(i, j, ColorFromPalette(SEGPALETTE, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND)); SEGMENT.addPixelColorXY((cols - 1) - i, (rows - 1) - j, ColorFromPalette(SEGPALETTE, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND)); } } - SEGMENT.blur(16); + SEGMENT.blur(sqrt16(cols*rows) > 100 ? 16 : 0); return FRAMETIME; } // mode_2DWaverly() diff --git a/wled00/FX.h b/wled00/FX.h index 44c5e1549..fd0bb297b 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -601,7 +601,7 @@ typedef struct Segment { inline void setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = true) { setPixelColor(i, RGBW32(r,g,b,w), aa); } inline void setPixelColor(float i, CRGB c, bool aa = true) { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); } #ifndef WLED_DISABLE_MODE_BLEND - inline void setClippingRect(int startX, int stopX, int startY = 0, int stopY = 1) { _clipStart = startX; _clipStop = stopX; _clipStartY = startY; _clipStopY = stopY; }; + static inline void setClippingRect(int startX, int stopX, int startY = 0, int stopY = 1) { _clipStart = startX; _clipStop = stopX; _clipStartY = startY; _clipStopY = stopY; }; #endif bool isPixelClipped(int i); uint32_t getPixelColor(int i); @@ -708,9 +708,7 @@ class WS2812FX { // 96 bytes public: WS2812FX() : - paletteFade(0), paletteBlend(0), - cctBlending(0), now(millis()), timebase(0), isMatrix(false), @@ -792,6 +790,7 @@ class WS2812FX { // 96 bytes addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name), // add effect to the list; defined in FX.cpp setupEffectData(void); // add default effects to the list; defined in FX.cpp + inline void resetTimebase() { timebase = 0U - millis(); } inline void restartRuntime() { for (Segment &seg : _segments) seg.markForReset(); } inline void setTransitionMode(bool t) { for (Segment &seg : _segments) seg.startTransition(t ? _transitionDur : 0); } inline void setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setColor(slot, RGBW32(r,g,b,w)); } @@ -806,7 +805,6 @@ class WS2812FX { // 96 bytes inline void resume(void) { _suspend = false; } // will resume strip.service() execution bool - paletteFade, checkSegmentAlignment(void), hasRGBWBus(void), hasCCTBus(void), @@ -822,7 +820,6 @@ class WS2812FX { // 96 bytes uint8_t paletteBlend, - cctBlending, getActiveSegmentsNum(void), getFirstSelectedSegId(void), getLastActiveSegmentId(void), diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 7e1419293..a37e5d11f 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -176,10 +176,10 @@ bool IRAM_ATTR Segment::isPixelXYClipped(int x, int y) { if (_clipStart != _clipStop && blendingStyle > BLEND_STYLE_FADE) { const bool invertX = _clipStart > _clipStop; const bool invertY = _clipStartY > _clipStopY; - const unsigned startX = invertX ? _clipStop : _clipStart; - const unsigned stopX = invertX ? _clipStart : _clipStop; - const unsigned startY = invertY ? _clipStopY : _clipStartY; - const unsigned stopY = invertY ? _clipStartY : _clipStopY; + const int startX = invertX ? _clipStop : _clipStart; + const int stopX = invertX ? _clipStart : _clipStop; + const int startY = invertY ? _clipStopY : _clipStartY; + const int stopY = invertY ? _clipStartY : _clipStopY; if (blendingStyle == BLEND_STYLE_FAIRY_DUST) { const unsigned width = stopX - startX; // assumes full segment width (faster than virtualWidth()) const unsigned len = width * (stopY - startY); // assumes full segment height (faster than virtualHeight()) @@ -295,7 +295,7 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa) // returns RGBW values of pixel uint32_t IRAM_ATTR Segment::getPixelColorXY(uint16_t x, uint16_t y) { if (!isActive()) return 0; // not active - if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return 0; // if pixel would fall out of virtual segment just exit + if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0 || isPixelXYClipped(x,y)) return 0; // if pixel would fall out of virtual segment just exit if (reverse ) x = virtualWidth() - x - 1; if (reverse_y) y = virtualHeight() - y - 1; if (transpose) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 00196e38e..4ef110e07 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -293,21 +293,17 @@ void Segment::startTransition(uint16_t dur) { _t->_briT = on ? opacity : 0; _t->_cctT = cct; #ifndef WLED_DISABLE_MODE_BLEND - if (modeBlending) { - swapSegenv(_t->_segT); - _t->_modeT = mode; - _t->_segT._dataLenT = 0; - _t->_segT._dataT = nullptr; - if (_dataLen > 0 && data) { - _t->_segT._dataT = (byte *)malloc(_dataLen); - if (_t->_segT._dataT) { - //DEBUG_PRINTF_P(PSTR("-- Allocated duplicate data (%d) for %p: %p\n"), _dataLen, this, _t->_segT._dataT); - memcpy(_t->_segT._dataT, data, _dataLen); - _t->_segT._dataLenT = _dataLen; - } + swapSegenv(_t->_segT); + _t->_modeT = mode; + _t->_segT._dataLenT = 0; + _t->_segT._dataT = nullptr; + if (_dataLen > 0 && data) { + _t->_segT._dataT = (byte *)malloc(_dataLen); + if (_t->_segT._dataT) { + //DEBUG_PRINTF_P(PSTR("-- Allocated duplicate data (%d) for %p: %p\n"), _dataLen, this, _t->_segT._dataT); + memcpy(_t->_segT._dataT, data, _dataLen); + _t->_segT._dataLenT = _dataLen; } - } else { - for (size_t i=0; i_segT._colorT[i] = colors[i]; } #else for (size_t i=0; i_colorT[i] = colors[i]; @@ -435,7 +431,7 @@ uint8_t IRAM_ATTR Segment::currentBri(bool useCct) { uint8_t IRAM_ATTR Segment::currentMode() { #ifndef WLED_DISABLE_MODE_BLEND uint16_t prog = progress(); - if (modeBlending && prog < 0xFFFFU) return _t->_modeT; + if (prog < 0xFFFFU) return _t->_modeT; #endif return mode; } @@ -445,7 +441,7 @@ uint32_t IRAM_ATTR Segment::currentColor(uint8_t slot) { uint32_t prog = progress(); if (prog == 0xFFFFU) return colors[slot]; #ifndef WLED_DISABLE_MODE_BLEND - if (blendingStyle > BLEND_STYLE_FADE) return _modeBlend ? _t->_segT._colorT[slot] : colors[slot]; // not fade/blend transition, each effect uses its color + if (blendingStyle > BLEND_STYLE_FADE && mode != _t->_modeT) return _modeBlend ? _t->_segT._colorT[slot] : colors[slot]; // not fade/blend transition, each effect uses its color return color_blend(_t->_segT._colorT[slot], colors[slot], prog, true); #else return color_blend(_t->_colorT[slot], colors[slot], prog, true); @@ -456,10 +452,10 @@ CRGBPalette16 IRAM_ATTR &Segment::currentPalette(CRGBPalette16 &targetPalette, u loadPalette(targetPalette, pal); uint16_t prog = progress(); #ifndef WLED_DISABLE_MODE_BLEND - if (prog < 0xFFFFU && blendingStyle > BLEND_STYLE_FADE && _modeBlend) targetPalette = _t->_palT; // not fade/blend transition, each effect uses its palette + if (prog < 0xFFFFU && blendingStyle > BLEND_STYLE_FADE && _modeBlend && mode != _t->_modeT) targetPalette = _t->_palT; // not fade/blend transition, each effect uses its palette else #endif - if (strip.paletteFade && prog < 0xFFFFU) { + if (prog < 0xFFFFU) { // blend palettes // there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time) // minimum blend time is 100ms maximum is 65535ms @@ -473,19 +469,16 @@ CRGBPalette16 IRAM_ATTR &Segment::currentPalette(CRGBPalette16 &targetPalette, u // relies on WS2812FX::service() to call it for each frame void Segment::handleRandomPalette() { // is it time to generate a new palette? - if ((uint16_t)(millis() / 1000U) - _lastPaletteChange > randomPaletteChangeTime){ - _newRandomPalette = useHarmonicRandomPalette ? generateHarmonicRandomPalette(_randomPalette) : generateRandomPalette(); - _lastPaletteChange = (uint16_t)(millis() / 1000U); - _lastPaletteBlend = (uint16_t)millis() - 512; // starts blending immediately + if ((uint16_t)(millis()/1000U) - _lastPaletteChange > randomPaletteChangeTime) { + _newRandomPalette = useHarmonicRandomPalette ? generateHarmonicRandomPalette(_randomPalette) : generateRandomPalette(); + _lastPaletteChange = (uint16_t)(millis()/1000U); + _lastPaletteBlend = (uint16_t)(millis())-512; // starts blending immediately } - // if palette transitions is enabled, blend it according to Transition Time (if longer than minimum given by service calls) - if (strip.paletteFade) { - // assumes that 128 updates are sufficient to blend a palette, so shift by 7 (can be more, can be less) - // in reality there need to be 255 blends to fully blend two entirely different palettes - if ((uint16_t)((uint16_t)millis() - _lastPaletteBlend) < strip.getTransition() >> 7) return; // not yet time to fade, delay the update - _lastPaletteBlend = (uint16_t)millis(); - } + // assumes that 128 updates are sufficient to blend a palette, so shift by 7 (can be more, can be less) + // in reality there need to be 255 blends to fully blend two entirely different palettes + if ((uint16_t)millis() - _lastPaletteBlend < strip.getTransition() >> 7) return; // not yet time to fade, delay the update + _lastPaletteBlend = (uint16_t)millis(); nblendPaletteTowardPalette(_randomPalette, _newRandomPalette, 48); } @@ -549,7 +542,7 @@ bool Segment::setColor(uint8_t slot, uint32_t c) { //returns true if changed if (slot == 0 && c == BLACK) return false; // on/off segment cannot have primary color black if (slot == 1 && c != BLACK) return false; // on/off segment cannot have secondary color non black } - if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change + startTransition(strip.getTransition()); // start transition prior to change colors[slot] = c; stateChanged = true; // send UDP/WS broadcast return true; @@ -562,21 +555,21 @@ void Segment::setCCT(uint16_t k) { k = (k - 1900) >> 5; } if (cct == k) return; - if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change + startTransition(strip.getTransition()); // start transition prior to change cct = k; stateChanged = true; // send UDP/WS broadcast } void Segment::setOpacity(uint8_t o) { if (opacity == o) return; - if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change + startTransition(strip.getTransition()); // start transition prior to change opacity = o; stateChanged = true; // send UDP/WS broadcast } void Segment::setOption(uint8_t n, bool val) { bool prevOn = on; - if (fadeTransition && n == SEG_OPTION_ON && val != prevOn) startTransition(strip.getTransition()); // start transition prior to change + if (n == SEG_OPTION_ON && val != prevOn) startTransition(strip.getTransition()); // start transition prior to change if (val) options |= 0x01 << n; else options &= ~(0x01 << n); if (!(n == SEG_OPTION_SELECTED || n == SEG_OPTION_RESET)) stateChanged = true; // send UDP/WS broadcast @@ -589,7 +582,7 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) { // if we have a valid mode & is not reserved if (fx != mode) { #ifndef WLED_DISABLE_MODE_BLEND - if (modeBlending) startTransition(strip.getTransition()); // set effect transitions + startTransition(strip.getTransition()); // set effect transitions #endif mode = fx; // load default values from effect string @@ -620,7 +613,7 @@ void Segment::setPalette(uint8_t pal) { if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0; // built in palettes if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0; // custom palettes if (pal != palette) { - if (strip.paletteFade) startTransition(strip.getTransition()); + startTransition(strip.getTransition()); palette = pal; stateChanged = true; // send UDP/WS broadcast } @@ -688,8 +681,8 @@ bool IRAM_ATTR Segment::isPixelClipped(int i) { #ifndef WLED_DISABLE_MODE_BLEND if (_clipStart != _clipStop && blendingStyle > BLEND_STYLE_FADE) { bool invert = _clipStart > _clipStop; - unsigned start = invert ? _clipStop : _clipStart; - unsigned stop = invert ? _clipStart : _clipStop; + int start = invert ? _clipStop : _clipStart; + int stop = invert ? _clipStart : _clipStop; if (blendingStyle == BLEND_STYLE_FAIRY_DUST) { unsigned len = stop - start; if (len < 2) return false; @@ -888,6 +881,8 @@ uint32_t IRAM_ATTR Segment::getPixelColor(int i) } #endif + if (isPixelClipped(i)) return 0; // handle clipping on 1D + if (reverse) i = virtualLength() - i - 1; i *= groupLength(); i += start; @@ -1234,8 +1229,8 @@ void WS2812FX::service() { // would need to be allocated for each effect and then blended together for each pixel. [[maybe_unused]] uint8_t tmpMode = seg.currentMode(); // this will return old mode while in transition #ifndef WLED_DISABLE_MODE_BLEND - seg.setClippingRect(0, 0); // disable clipping - if (modeBlending && seg.mode != tmpMode) { + Segment::setClippingRect(0, 0); // disable clipping (just in case) + if (seg.mode != tmpMode) { // could try seg.isInTransition() to allow color and palette to follow blending styles // set clipping rectangle // new mode is run inside clipping area and old mode outside clipping area unsigned p = seg.progress(); @@ -1245,48 +1240,48 @@ void WS2812FX::service() { unsigned dh = p * h / 0xFFFFU + 1; switch (blendingStyle) { case BLEND_STYLE_FAIRY_DUST: // fairy dust (must set entire segment, see isPixelXYClipped()) - seg.setClippingRect(0, w, 0, h); + Segment::setClippingRect(0, w, 0, h); break; case BLEND_STYLE_SWIPE_RIGHT: // left-to-right - seg.setClippingRect(0, dw, 0, h); + Segment::setClippingRect(0, dw, 0, h); break; case BLEND_STYLE_SWIPE_LEFT: // right-to-left - seg.setClippingRect(w - dw, w, 0, h); + Segment::setClippingRect(w - dw, w, 0, h); break; case BLEND_STYLE_PINCH_OUT: // corners - seg.setClippingRect((w + dw)/2, (w - dw)/2, (h + dh)/2, (h - dh)/2); // inverted!! + Segment::setClippingRect((w + dw)/2, (w - dw)/2, (h + dh)/2, (h - dh)/2); // inverted!! break; case BLEND_STYLE_INSIDE_OUT: // outward - seg.setClippingRect((w - dw)/2, (w + dw)/2, (h - dh)/2, (h + dh)/2); + Segment::setClippingRect((w - dw)/2, (w + dw)/2, (h - dh)/2, (h + dh)/2); break; case BLEND_STYLE_SWIPE_DOWN: // top-to-bottom (2D) - seg.setClippingRect(0, w, 0, dh); + Segment::setClippingRect(0, w, 0, dh); break; case BLEND_STYLE_SWIPE_UP: // bottom-to-top (2D) - seg.setClippingRect(0, w, h - dh, h); + Segment::setClippingRect(0, w, h - dh, h); break; case BLEND_STYLE_OPEN_H: // horizontal-outward (2D) same look as INSIDE_OUT on 1D - seg.setClippingRect((w - dw)/2, (w + dw)/2, 0, h); + Segment::setClippingRect((w - dw)/2, (w + dw)/2, 0, h); break; case BLEND_STYLE_OPEN_V: // vertical-outward (2D) - seg.setClippingRect(0, w, (h - dh)/2, (h + dh)/2); + Segment::setClippingRect(0, w, (h - dh)/2, (h + dh)/2); break; case BLEND_STYLE_PUSH_TL: // TL-to-BR (2D) - seg.setClippingRect(0, dw, 0, dh); + Segment::setClippingRect(0, dw, 0, dh); break; case BLEND_STYLE_PUSH_TR: // TR-to-BL (2D) - seg.setClippingRect(w - dw, w, 0, dh); + Segment::setClippingRect(w - dw, w, 0, dh); break; case BLEND_STYLE_PUSH_BR: // BR-to-TL (2D) - seg.setClippingRect(w - dw, w, h - dh, h); + Segment::setClippingRect(w - dw, w, h - dh, h); break; case BLEND_STYLE_PUSH_BL: // BL-to-TR (2D) - seg.setClippingRect(0, dw, h - dh, h); + Segment::setClippingRect(0, dw, h - dh, h); break; } } delay = (*_mode[seg.mode])(); // run new/current mode - if (modeBlending && seg.mode != tmpMode) { + if (seg.mode != tmpMode) { // could try seg.isInTransition() to allow color and palette to follow blending styles Segment::tmpsegd_t _tmpSegData; Segment::modeBlend(true); // set semaphore seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state) @@ -1301,13 +1296,14 @@ void WS2812FX::service() { #endif seg.call++; if (seg.isInTransition() && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition - BusManager::setSegmentCCT(oldCCT); // restore old CCT for ABL adjustments + BusManager::setSegmentCCT(oldCCT); // restore old CCT for ABL adjustments } seg.next_time = nowUp + delay; } _segment_index++; } + Segment::setClippingRect(0, 0); // disable clipping for overlays _virtualSegmentLength = 0; _isServicing = false; _triggered = false; diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 82e81a387..02a76f69d 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -391,14 +391,14 @@ BusPwm::BusPwm(BusConfig &bc) uint8_t numPins = NUM_PWM_PINS(bc.type); _frequency = bc.frequency ? bc.frequency : WLED_PWM_FREQ; - #ifdef ESP8266 +#ifdef ESP8266 // duty cycle resolution (_depth) can be extracted from this formula: 1MHz > _frequency * 2^_depth if (_frequency > 1760) _depth = 8; else if (_frequency > 880) _depth = 9; else _depth = 10; // WLED_PWM_FREQ <= 880Hz analogWriteRange((1<<_depth)-1); analogWriteFreq(_frequency); - #else +#else _ledcStart = pinManager.allocateLedc(numPins); if (_ledcStart == 255) { //no more free LEDC channels deallocatePins(); return; @@ -408,7 +408,7 @@ BusPwm::BusPwm(BusConfig &bc) else if (_frequency > 39062) _depth = 10; else if (_frequency > 19531) _depth = 11; else _depth = 12; // WLED_PWM_FREQ <= 19531Hz - #endif +#endif for (unsigned i = 0; i < numPins; i++) { uint8_t currentPin = bc.pins[i]; diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index c128f8c09..cdd1e82ff 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -173,10 +173,11 @@ class Bus { type == TYPE_FW1906 || type == TYPE_WS2805 ) return true; return false; } - static int16_t getCCT() { return _cct; } + static inline int16_t getCCT() { return _cct; } static void setCCT(int16_t cct) { _cct = cct; } + static inline uint8_t getCCTBlend() { return _cctBlend; } static void setCCTBlend(uint8_t b) { if (b > 100) b = 100; _cctBlend = (b * 127) / 100; diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 530777ab5..01c407f95 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -111,8 +111,8 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { CJSON(correctWB, hw_led["cct"]); CJSON(cctFromRgb, hw_led[F("cr")]); CJSON(cctICused, hw_led[F("ic")]); - CJSON(strip.cctBlending, hw_led[F("cb")]); - Bus::setCCTBlend(strip.cctBlending); + uint8_t cctBlending = hw_led[F("cb")] | Bus::getCCTBlend(); + Bus::setCCTBlend(cctBlending); strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS CJSON(useGlobalLedBuffer, hw_led[F("ld")]); @@ -408,12 +408,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { } JsonObject light_tr = light["tr"]; - CJSON(fadeTransition, light_tr["mode"]); - CJSON(modeBlending, light_tr["fx"]); int tdd = light_tr["dur"] | -1; if (tdd >= 0) transitionDelay = transitionDelayDefault = tdd * 100; - strip.setTransition(fadeTransition ? transitionDelayDefault : 0); - CJSON(strip.paletteFade, light_tr["pal"]); + strip.setTransition(transitionDelayDefault); CJSON(randomPaletteChangeTime, light_tr[F("rpc")]); CJSON(useHarmonicRandomPalette, light_tr[F("hrp")]); @@ -777,7 +774,7 @@ void serializeConfig() { hw_led["cct"] = correctWB; hw_led[F("cr")] = cctFromRgb; hw_led[F("ic")] = cctICused; - hw_led[F("cb")] = strip.cctBlending; + hw_led[F("cb")] = Bus::getCCTBlend(); hw_led["fps"] = strip.getTargetFps(); hw_led[F("rgbwm")] = Bus::getGlobalAWMode(); // global auto white mode override hw_led[F("ld")] = useGlobalLedBuffer; @@ -894,10 +891,7 @@ void serializeConfig() { light_gc["val"] = gammaCorrectVal; JsonObject light_tr = light.createNestedObject("tr"); - light_tr["mode"] = fadeTransition; - light_tr["fx"] = modeBlending; light_tr["dur"] = transitionDelayDefault / 100; - light_tr["pal"] = strip.paletteFade; light_tr[F("rpc")] = randomPaletteChangeTime; light_tr[F("hrp")] = useHarmonicRandomPalette; diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index dddedd471..2a5267825 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -834,12 +834,7 @@ Swap:

Brightness factor: %

Transitions

- Enable transitions:
- - Effect blending:
- Transition Time: ms
- Palette transitions:
-
+ Transition Time: ms
Random Cycle Palette Time: s
Use harmonic Random Cycle Palette:

Timed light

diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 010ad3a53..85893dfae 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -181,7 +181,6 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient = 0); void setValuesFromSegment(uint8_t s); void setValuesFromMainSeg(); void setValuesFromFirstSelectedSeg(); -void resetTimebase(); void toggleOnOff(); void applyBri(); void applyFinalBri(); diff --git a/wled00/json.cpp b/wled00/json.cpp index 269d8a7f6..2e00ec09c 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -351,7 +351,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId) tr = root[F("transition")] | -1; if (tr >= 0) { transitionDelay = tr * 100; - if (fadeTransition) strip.setTransition(transitionDelay); + strip.setTransition(transitionDelay); } } @@ -364,7 +364,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId) tr = root[F("tt")] | -1; if (tr >= 0) { jsonTransitionOnce = true; - if (fadeTransition) strip.setTransition(tr * 100); + strip.setTransition(tr * 100); } tr = root[F("tb")] | -1; @@ -779,7 +779,7 @@ void serializeInfo(JsonObject root) root[F("freeheap")] = ESP.getFreeHeap(); #if defined(ARDUINO_ARCH_ESP32) - if (psramSafe && psramFound()) root[F("psram")] = ESP.getFreePsram(); + if (psramFound()) root[F("psram")] = ESP.getFreePsram(); #endif root[F("uptime")] = millis()/1000 + rolloverMillis*4294967; diff --git a/wled00/led.cpp b/wled00/led.cpp index 23c8d03c5..acfa3ac36 100644 --- a/wled00/led.cpp +++ b/wled00/led.cpp @@ -47,12 +47,6 @@ void applyValuesToSelectedSegs() } -void resetTimebase() -{ - strip.timebase = 0 - millis(); -} - - void toggleOnOff() { if (bri == 0) @@ -76,7 +70,7 @@ byte scaledBri(byte in) } -//applies global brightness +//applies global temporary brightness (briT) to strip void applyBri() { if (!realtimeMode || !arlsForceMaxBri) { @@ -90,6 +84,7 @@ void applyFinalBri() { briOld = bri; briT = bri; applyBri(); + strip.trigger(); // force one last update } @@ -122,7 +117,7 @@ void stateUpdated(byte callMode) { nightlightStartTime = millis(); } if (briT == 0) { - if (callMode != CALL_MODE_NOTIFICATION) resetTimebase(); //effect start from beginning + if (callMode != CALL_MODE_NOTIFICATION) strip.resetTimebase(); //effect start from beginning } if (bri > 0) briLast = bri; @@ -133,31 +128,24 @@ void stateUpdated(byte callMode) { // notify usermods of state change usermods.onStateChange(callMode); - if (fadeTransition) { - if (strip.getTransition() == 0) { - jsonTransitionOnce = false; - transitionActive = false; - applyFinalBri(); - strip.trigger(); - return; - } - - if (transitionActive) { - briOld = briT; - tperLast = 0; - } else - strip.setTransitionMode(true); // force all segments to transition mode - transitionActive = true; - transitionStartTime = millis(); - } else { + if (strip.getTransition() == 0) { + jsonTransitionOnce = false; + transitionActive = false; applyFinalBri(); - strip.trigger(); + return; } + + if (transitionActive) { + briOld = briT; + tperLast = 0; + } else + strip.setTransitionMode(true); // force all segments to transition mode + transitionActive = true; + transitionStartTime = millis(); } -void updateInterfaces(uint8_t callMode) -{ +void updateInterfaces(uint8_t callMode) { if (!interfaceUpdateCallMode || millis() - lastInterfaceUpdate < INTERFACE_UPDATE_COOLDOWN) return; sendDataWs(); @@ -178,8 +166,7 @@ void updateInterfaces(uint8_t callMode) } -void handleTransitions() -{ +void handleTransitions() { //handle still pending interface update updateInterfaces(interfaceUpdateCallMode); @@ -198,7 +185,6 @@ void handleTransitions() if (tper - tperLast < 0.004f) return; tperLast = tper; briT = briOld + ((bri - briOld) * tper); - applyBri(); } } @@ -211,8 +197,7 @@ void colorUpdated(byte callMode) { } -void handleNightlight() -{ +void handleNightlight() { unsigned long now = millis(); if (now < 100 && lastNlUpdate > 0) lastNlUpdate = 0; // take care of millis() rollover if (now - lastNlUpdate < 100) return; // allow only 10 NL updates per second @@ -292,7 +277,6 @@ void handleNightlight() } //utility for FastLED to use our custom timer -uint32_t get_millisecond_timer() -{ +uint32_t get_millisecond_timer() { return strip.now; } diff --git a/wled00/playlist.cpp b/wled00/playlist.cpp index 67c4f6049..225102da6 100644 --- a/wled00/playlist.cpp +++ b/wled00/playlist.cpp @@ -146,7 +146,7 @@ void handlePlaylist() { } jsonTransitionOnce = true; - strip.setTransition(fadeTransition ? playlistEntries[playlistIndex].tr * 100 : 0); + strip.setTransition(playlistEntries[playlistIndex].tr * 100); playlistEntryDur = playlistEntries[playlistIndex].dur; applyPresetFromPlaylist(playlistEntries[playlistIndex].preset); } diff --git a/wled00/set.cpp b/wled00/set.cpp index a2e884c81..c5b9f262c 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -128,8 +128,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) correctWB = request->hasArg(F("CCT")); cctFromRgb = request->hasArg(F("CR")); cctICused = request->hasArg(F("IC")); - strip.cctBlending = request->arg(F("CB")).toInt(); - Bus::setCCTBlend(strip.cctBlending); + uint8_t cctBlending = request->arg(F("CB")).toInt(); + Bus::setCCTBlend(cctBlending); Bus::setGlobalAWMode(request->arg(F("AW")).toInt()); strip.setTargetFps(request->arg(F("FR")).toInt()); useGlobalLedBuffer = request->hasArg(F("LD")); @@ -313,11 +313,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) gammaCorrectCol = false; } - fadeTransition = request->hasArg(F("TF")); - modeBlending = request->hasArg(F("EB")); t = request->arg(F("TD")).toInt(); if (t >= 0) transitionDelayDefault = t; - strip.paletteFade = request->hasArg(F("PF")); t = request->arg(F("TP")).toInt(); randomPaletteChangeTime = MIN(255,MAX(1,t)); useHarmonicRandomPalette = request->hasArg(F("TH")); @@ -1124,7 +1121,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply) pos = req.indexOf(F("TT=")); if (pos > 0) transitionDelay = getNumVal(&req, pos); - if (fadeTransition) strip.setTransition(transitionDelay); + strip.setTransition(transitionDelay); //set time (unix timestamp) pos = req.indexOf(F("ST=")); diff --git a/wled00/udp.cpp b/wled00/udp.cpp index 100ace166..d2f49144a 100644 --- a/wled00/udp.cpp +++ b/wled00/udp.cpp @@ -225,10 +225,8 @@ void parseNotifyPacket(uint8_t *udpIn) { // set transition time before making any segment changes if (version > 3) { - if (fadeTransition) { - jsonTransitionOnce = true; - strip.setTransition(((udpIn[17] << 0) & 0xFF) + ((udpIn[18] << 8) & 0xFF00)); - } + jsonTransitionOnce = true; + strip.setTransition(((udpIn[17] << 0) & 0xFF) + ((udpIn[18] << 8) & 0xFF00)); } //apply colors from notification to main segment, only if not syncing full segments diff --git a/wled00/wled.cpp b/wled00/wled.cpp index eb7860851..8f64a10e9 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -538,10 +538,10 @@ void WLED::beginStrip() } else { // fix for #3196 if (bootPreset > 0) { - bool oldTransition = fadeTransition; // workaround if transitions are enabled - fadeTransition = false; // ignore transitions temporarily + uint16_t oldTransition = strip.getTransition(); // workaround if transitions are enabled + strip.setTransition(0); // ignore transitions temporarily strip.setColor(0, BLACK); // set all segments black - fadeTransition = oldTransition; // restore transitions + strip.setTransition(oldTransition); // restore transitions col[0] = col[1] = col[2] = col[3] = 0; // needed for colorUpdated() } briLast = briS; bri = 0; diff --git a/wled00/wled.h b/wled00/wled.h index 5a9b8dcf5..8e21343f3 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -545,8 +545,6 @@ WLED_GLOBAL bool wasConnected _INIT(false); WLED_GLOBAL byte lastRandomIndex _INIT(0); // used to save last random color so the new one is not the same // transitions -WLED_GLOBAL bool fadeTransition _INIT(true); // enable crossfading brightness/color -WLED_GLOBAL bool modeBlending _INIT(true); // enable effect blending WLED_GLOBAL uint8_t blendingStyle _INIT(0); // effect blending/transitionig style WLED_GLOBAL bool transitionActive _INIT(false); WLED_GLOBAL uint16_t transitionDelay _INIT(750); // global transition duration diff --git a/wled00/wled_eeprom.cpp b/wled00/wled_eeprom.cpp index 4f2c14d47..a43c23e8d 100755 --- a/wled00/wled_eeprom.cpp +++ b/wled00/wled_eeprom.cpp @@ -220,7 +220,7 @@ void loadSettingsFromEEPROM() 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) diff --git a/wled00/xml.cpp b/wled00/xml.cpp index 3915d9b0e..e9c1fac46 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -357,7 +357,7 @@ void getSettingsJS(byte subPage, char* dest) sappend('c',SET_F("CCT"),correctWB); sappend('c',SET_F("IC"),cctICused); sappend('c',SET_F("CR"),cctFromRgb); - sappend('v',SET_F("CB"),strip.cctBlending); + sappend('v',SET_F("CB"),Bus::getCCTBlend()); sappend('v',SET_F("FR"),strip.getTargetFps()); sappend('v',SET_F("AW"),Bus::getGlobalAWMode()); sappend('c',SET_F("LD"),useGlobalLedBuffer); @@ -445,10 +445,7 @@ void getSettingsJS(byte subPage, char* dest) sappend('c',SET_F("GB"),gammaCorrectBri); sappend('c',SET_F("GC"),gammaCorrectCol); dtostrf(gammaCorrectVal,3,1,nS); sappends('s',SET_F("GV"),nS); - sappend('c',SET_F("TF"),fadeTransition); - sappend('c',SET_F("EB"),modeBlending); sappend('v',SET_F("TD"),transitionDelayDefault); - sappend('c',SET_F("PF"),strip.paletteFade); sappend('v',SET_F("TP"),randomPaletteChangeTime); sappend('c',SET_F("TH"),useHarmonicRandomPalette); sappend('v',SET_F("BF"),briMultiplier);