From 5fe09e978744e6311a6488b69df1b795b4adfe9c Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 1 Aug 2023 11:53:32 +0200 Subject: [PATCH 01/41] esp-now remote: fix crash with AP = Always initialize ESPNOW only when Wifi (STA or AP) is running --- wled00/remote.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wled00/remote.cpp b/wled00/remote.cpp index 0a833d6e..922c5738 100644 --- a/wled00/remote.cpp +++ b/wled00/remote.cpp @@ -39,7 +39,7 @@ typedef struct message_structure { } message_structure; static int esp_now_state = ESP_NOW_STATE_UNINIT; -static uint32_t last_seq = -1; +static uint32_t last_seq = UINT32_MAX; static int brightnessBeforeNightMode = NIGHT_MODE_DEACTIVATED; static message_structure incoming; @@ -168,7 +168,7 @@ void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { void handleRemote() { if (enable_espnow_remote) { - if (esp_now_state == ESP_NOW_STATE_UNINIT) { + if ((esp_now_state == ESP_NOW_STATE_UNINIT) && (interfacesInited || apActive)) { // ESPNOW requires Wifi to be initialized (either STA, or AP Mode) DEBUG_PRINTLN(F("Initializing ESP_NOW listener")); // Init ESP-NOW if (esp_now_init() != 0) { @@ -197,4 +197,4 @@ void handleRemote() { } } -#endif \ No newline at end of file +#endif From 5df197e81450f664080f30cb1267a76985828d32 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 1 Aug 2023 13:11:06 +0200 Subject: [PATCH 02/41] disable ESP-NOW remotes in ethernet build (will crash without wifi) --- platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/platformio.ini b/platformio.ini index 73da6250..9321eea0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -433,6 +433,7 @@ platform_packages = ${esp32.platform_packages} upload_speed = 921600 build_unflags = ${common.build_unflags} build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1 + -D WLED_DISABLE_ESPNOW ;; ESP-NOW requires wifi, may crash with ethernet only lib_deps = ${esp32.lib_deps} board_build.partitions = ${esp32.default_partitions} From bb8223d4ffc741ce103c0382e9963d68d12d1971 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Wed, 2 Aug 2023 22:01:15 +0200 Subject: [PATCH 03/41] Soap bugfix. --- wled00/FX.cpp | 9 +++++---- wled00/wled.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 7ddd9988..834c17e9 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -7419,6 +7419,7 @@ uint16_t mode_2Dsoap() { int amplitude; int8_t shiftX = 0; //(SEGMENT.custom1 - 128) / 4; int8_t shiftY = 0; //(SEGMENT.custom2 - 128) / 4; + CRGB ledsbuff[MAX(cols,rows)]; amplitude = (cols >= 16) ? (cols-8)/8 : 1; for (int y = 0; y < rows; y++) { @@ -7439,9 +7440,9 @@ uint16_t mode_2Dsoap() { CRGB PixelB = CRGB::Black; if ((zF >= 0) && (zF < cols)) PixelB = SEGMENT.getPixelColorXY(zF, y); else PixelB = ColorFromPalette(SEGPALETTE, ~noise3d[XY(abs(zF),y)]*3); - CRGB pix = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction))); - SEGMENT.setPixelColorXY(x, y, pix); + ledsbuff[x] = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction))); } + for (int x = 0; x < cols; x++) SEGMENT.setPixelColorXY(x, y, ledsbuff[x]); } amplitude = (rows >= 16) ? (rows-8)/8 : 1; @@ -7463,9 +7464,9 @@ uint16_t mode_2Dsoap() { CRGB PixelB = CRGB::Black; if ((zF >= 0) && (zF < rows)) PixelB = SEGMENT.getPixelColorXY(x, zF); else PixelB = ColorFromPalette(SEGPALETTE, ~noise3d[XY(x,abs(zF))]*3); - CRGB pix = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction))); - SEGMENT.setPixelColorXY(x, y, pix); + ledsbuff[y] = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction))); } + for (int y = 0; y < rows; y++) SEGMENT.setPixelColorXY(x, y, ledsbuff[y]); } return FRAMETIME; diff --git a/wled00/wled.h b/wled00/wled.h index e2cbf93d..696b957e 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2307180 +#define VERSION 2308020 //uncomment this if you have a "my_config.h" file you'd like to use //#define WLED_USE_MY_CONFIG From 8503aba5838d3fc18df4f69d0e47f1a5c2abbfc5 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Thu, 3 Aug 2023 22:28:53 +0200 Subject: [PATCH 04/41] Better random palette handling. Remove unnecessary debug timing. --- wled00/FX.h | 7 ++++++ wled00/FX_fcn.cpp | 60 ++++++++++++++++++----------------------------- wled00/wled.h | 2 +- 3 files changed, 31 insertions(+), 38 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index d9426624..fc8f16b9 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -395,6 +395,12 @@ typedef struct Segment { uint16_t _dataLen; static uint16_t _usedSegmentData; + // perhaps this should be per segment, not static + static CRGBPalette16 _randomPalette; + static CRGBPalette16 _newRandomPalette; + static unsigned long _lastPaletteChange; + static uint8_t _noOfBlendsRemaining; + // transition data, valid only if transitional==true, holds values during transition (72 bytes) struct Transition { uint32_t _colorT[NUM_COLORS]; @@ -510,6 +516,7 @@ typedef struct Segment { static uint16_t getUsedSegmentData(void) { return _usedSegmentData; } static void addUsedSegmentData(int len) { _usedSegmentData += len; } + static void handleRandomPalette(); void setUp(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1, uint8_t segId = 255); bool setColor(uint8_t slot, uint32_t c); //returns true if changed diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 74d61cb4..c76e22a2 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -77,6 +77,11 @@ uint16_t Segment::_usedSegmentData = 0U; // amount of RAM all segments use for t uint16_t Segment::maxWidth = DEFAULT_LED_COUNT; uint16_t Segment::maxHeight = 1; +CRGBPalette16 Segment::_randomPalette = CRGBPalette16(DEFAULT_COLOR); +CRGBPalette16 Segment::_newRandomPalette = CRGBPalette16(DEFAULT_COLOR); +unsigned long Segment::_lastPaletteChange = 0; // perhaps it should be per segment +uint8_t Segment::_noOfBlendsRemaining = 0; + // copy constructor Segment::Segment(const Segment &orig) { //DEBUG_PRINTLN(F("-- Copy segment constructor --")); @@ -182,10 +187,6 @@ void Segment::resetIfRequired() { } CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) { - static unsigned long _lastPaletteChange = 0; // perhaps it should be per segment - static CRGBPalette16 randomPalette = CRGBPalette16(DEFAULT_COLOR); - static CRGBPalette16 prevRandomPalette = CRGBPalette16(CRGB(BLACK)); - byte tcp[72]; if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0; if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0; //default palette. Differs depending on effect @@ -206,27 +207,18 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) { case 0: //default palette. Exceptions for specific effects above targetPalette = PartyColors_p; break; case 1: {//periodically replace palette with a random one. Transition palette change in 500ms - uint32_t timeSinceLastChange = millis() - _lastPaletteChange; + unsigned long timeSinceLastChange = millis() - _lastPaletteChange; if (timeSinceLastChange > randomPaletteChangeTime * 1000U) { - prevRandomPalette = randomPalette; - randomPalette = CRGBPalette16( + _randomPalette = _newRandomPalette; + _newRandomPalette = CRGBPalette16( CHSV(random8(), random8(160, 255), random8(128, 255)), CHSV(random8(), random8(160, 255), random8(128, 255)), CHSV(random8(), random8(160, 255), random8(128, 255)), CHSV(random8(), random8(160, 255), random8(128, 255))); _lastPaletteChange = millis(); - timeSinceLastChange = 0; - } - if (timeSinceLastChange <= 250) { - targetPalette = prevRandomPalette; - // there needs to be 255 palette blends (48) for full blend but that is too resource intensive - // so 128 is a compromise (we need to perform full blend of the two palettes as each segment can have random - // palette selected but only 2 static palettes are used) - size_t noOfBlends = ((128U * timeSinceLastChange) / 250U); - for (size_t i=0; i245) { targetPalette = strip.customPalettes[255-pal]; // we checked bounds above } else { + byte tcp[72]; memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[pal-13])), 72); targetPalette.loadDynamicGradientPalette(tcp); } @@ -353,6 +346,17 @@ void Segment::handleTransition() { } } +// relies on WS2812FX::service() to call it max every 8ms or more (MIN_SHOW_DELAY) +void Segment::handleRandomPalette() { + if (_noOfBlendsRemaining > 0) { + // there needs to be 255 palette blends (48) for full blend + size_t noOfBlends = 3; // blending time ~850ms when MIN_SHOW_DELAY>10 + if (noOfBlends > _noOfBlendsRemaining) noOfBlends = _noOfBlendsRemaining; + for (size_t i=0; i Date: Sat, 5 Aug 2023 13:50:08 +0200 Subject: [PATCH 05/41] FX blending POC --- wled00/FX.h | 50 ++++++++++----- wled00/FX_fcn.cpp | 155 ++++++++++++++++++++++++++++++++++++---------- wled00/led.cpp | 8 +-- 3 files changed, 161 insertions(+), 52 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index fc8f16b9..bca67a7e 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -381,6 +381,28 @@ typedef struct Segment { byte *data; // effect data pointer static uint16_t maxWidth, maxHeight; // these define matrix width & height (max. segment dimensions) + typedef struct TemporarySegmentData { + uint32_t _colorT[NUM_COLORS]; + //uint8_t _opacityT; + //uint8_t _cctT; // temporary CCT + //uint16_t _optionsT; + uint8_t _speedT; + uint8_t _intensityT; + uint8_t _custom1T, _custom2T; // custom FX parameters/sliders + struct { + uint8_t _custom3T : 5; // reduced range slider (0-31) + bool _check1T : 1; // checkmark 1 + bool _check2T : 1; // checkmark 2 + bool _check3T : 1; // checkmark 3 + }; + uint16_t _aux0T; + uint16_t _aux1T; + uint32_t _stepT; + uint32_t _callT; + uint8_t *_dataT; + uint16_t _dataLenT; + } tmpsegd_t; + private: union { uint8_t _capabilities; @@ -399,41 +421,35 @@ typedef struct Segment { static CRGBPalette16 _randomPalette; static CRGBPalette16 _newRandomPalette; static unsigned long _lastPaletteChange; - static uint8_t _noOfBlendsRemaining; // transition data, valid only if transitional==true, holds values during transition (72 bytes) struct Transition { - uint32_t _colorT[NUM_COLORS]; + tmpsegd_t _tmpSeg; uint8_t _briT; // temporary brightness uint8_t _cctT; // temporary CCT + uint8_t _modeT; // previous mode/effect CRGBPalette16 _palT; // temporary palette uint8_t _prevPaletteBlends; // number of previous palette blends (there are max 255 belnds possible) - uint8_t _modeP; // previous mode/effect - //uint16_t _aux0, _aux1; // previous mode/effect runtime data - //uint32_t _step, _call; // previous mode/effect runtime data - //byte *_data; // previous mode/effect runtime data - unsigned long _start; // must accommodate millis() + unsigned long _start; // must accommodate millis() uint16_t _dur; Transition(uint16_t dur=750) - : _briT(255) - , _cctT(127) - , _palT(CRGBPalette16(CRGB::Black)) + : _palT(CRGBPalette16(CRGB::Black)) , _prevPaletteBlends(0) - , _modeP(FX_MODE_STATIC) , _start(millis()) , _dur(dur) {} + /* Transition(uint16_t d, uint8_t b, uint8_t c, const uint32_t *o) - : _briT(b) - , _cctT(c) - , _palT(CRGBPalette16(CRGB::Black)) + : _palT(CRGBPalette16(CRGB::Black)) , _prevPaletteBlends(0) - , _modeP(FX_MODE_STATIC) , _start(millis()) , _dur(d) { - for (size_t i=0; i_tmpSeg._dataT && _t->_tmpSeg._dataLenT > 0) { + free(_t->_tmpSeg._dataT); + _t->_tmpSeg._dataT = nullptr; + } delete _t; _t = nullptr; } @@ -284,44 +288,111 @@ void Segment::startTransition(uint16_t dur) { _t = new Transition(dur); // no previous transition running if (!_t) return; // failed to allocate data + DEBUG_PRINT(F("-- Saving transition environment. ")); + DEBUG_PRINTLN(on ? opacity : 0); + saveSegenv(&(_t->_tmpSeg)); CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette); - _t->_briT = on ? opacity : 0; - _t->_cctT = cct; - _t->_palT = _palT; - _t->_modeP = mode; - for (size_t i=0; i_colorT[i] = colors[i]; + _t->_palT = _palT; + _t->_modeT = mode; + _t->_briT = on ? opacity : 0; + _t->_cctT = cct; + _t->_tmpSeg._dataLenT = 0; + _t->_tmpSeg._dataT = nullptr; + if (_dataLen > 0 && data) { + _t->_tmpSeg._dataT = (byte *)malloc(_dataLen); + if (_t->_tmpSeg._dataT) { + DEBUG_PRINTLN(F("-- Allocated duplicate data.")); + memcpy(_t->_tmpSeg._dataT, data, _dataLen); + _t->_tmpSeg._dataLenT = _dataLen; + } + } transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true); } // transition progression between 0-65535 uint16_t Segment::progress() { - if (!transitional || !_t) return 0xFFFFU; - unsigned long timeNow = millis(); - if (timeNow - _t->_start > _t->_dur || _t->_dur == 0) return 0xFFFFU; - return (timeNow - _t->_start) * 0xFFFFU / _t->_dur; + if (transitional && _t) { + unsigned long timeNow = millis(); + if (_t->_dur > 0 && timeNow - _t->_start < _t->_dur) return (timeNow - _t->_start) * 0xFFFFU / _t->_dur; + } + return 0xFFFFU; } uint8_t Segment::currentBri(uint8_t briNew, bool useCct) { uint32_t prog = progress(); - if (transitional && _t && prog < 0xFFFFU) { + if (prog < 0xFFFFU) { if (useCct) return ((briNew * prog) + _t->_cctT * (0xFFFFU - prog)) >> 16; else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16; - } else { - return briNew; } + return briNew; } uint8_t Segment::currentMode(uint8_t newMode) { - return (progress()>32767U) ? newMode : _t->_modeP; // change effect in the middle of transition + uint16_t prog = progress(); + if (prog < 0xFFFFU) { // implicit check for transitional & _t in progress() + restoreSegenv(&(_t->_tmpSeg)); + opacity -= (uint32_t)opacity * prog / 0xFFFFU; + return _t->_modeT; + } + return newMode; +} + +void Segment::saveSegenv(tmpsegd_t *tmpSeg) { + //tmpSeg._opacityT = on ? opacity : 0; + //tmpSeg._optionsT = options; + for (size_t i=0; i_colorT[i] = colors[i]; + tmpSeg->_speedT = speed; + tmpSeg->_intensityT = intensity; + tmpSeg->_custom1T = custom1; + tmpSeg->_custom2T = custom2; + tmpSeg->_custom3T = custom3; + tmpSeg->_check1T = check1; + tmpSeg->_check2T = check2; + tmpSeg->_check3T = check3; + tmpSeg->_aux0T = aux0; + tmpSeg->_aux1T = aux1; + tmpSeg->_stepT = step; + tmpSeg->_callT = call; + tmpSeg->_dataT = data; + tmpSeg->_dataLenT = _dataLen; +} + +void Segment::restoreSegenv(tmpsegd_t *tmpSeg) { + if (&(_t->_tmpSeg) != tmpSeg) { + DEBUG_PRINTF("Temp: %p != %p\n", &(_t->_tmpSeg), tmpSeg); + DEBUG_PRINTLN(F("-- Restoring OLD environment.")); + // update possibly changed variables to keep old effect running correctly + _t->_tmpSeg._aux0T = aux0; + _t->_tmpSeg._aux1T = aux1; + _t->_tmpSeg._stepT = step; + _t->_tmpSeg._callT = call; + } + //opacity = tmpSeg._opacityT; + //options = tmpSeg._optionsT; + for (size_t i=0; i_colorT[i]; + speed = tmpSeg->_speedT; + intensity = tmpSeg->_intensityT; + custom1 = tmpSeg->_custom1T; + custom2 = tmpSeg->_custom2T; + custom3 = tmpSeg->_custom3T; + check1 = tmpSeg->_check1T; + check2 = tmpSeg->_check2T; + check3 = tmpSeg->_check3T; + aux0 = tmpSeg->_aux0T; + aux1 = tmpSeg->_aux1T; + step = tmpSeg->_stepT; + call = tmpSeg->_callT; + data = tmpSeg->_dataT; + _dataLen = tmpSeg->_dataLenT; } uint32_t Segment::currentColor(uint8_t slot, uint32_t colorNew) { - return transitional && _t ? color_blend(_t->_colorT[slot], colorNew, progress(), true) : colorNew; + return transitional && _t ? color_blend(_t->_tmpSeg._colorT[slot], colorNew, progress(), true) : colorNew; } CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) { loadPalette(targetPalette, pal); - if (transitional && _t && progress() < 0xFFFFU) { + if (progress() < 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 @@ -338,8 +409,13 @@ void Segment::handleTransition() { uint16_t _progress = progress(); if (_progress == 0xFFFFU) transitional = false; // finish transitioning segment if (_t) { // thanks to @nXm AKA https://github.com/NMeirer - if (_progress >= 32767U && _t->_modeP != mode) markForReset(); + //if (_progress >= 32767U && _t->_modeP != mode) markForReset(); if (_progress == 0xFFFFU) { + DEBUG_PRINTLN(F("-- Stopping transition.")); + if (_t->_tmpSeg._dataT && _t->_tmpSeg._dataLenT > 0) { + free(_t->_tmpSeg._dataT); + _t->_tmpSeg._dataT = nullptr; + } delete _t; _t = nullptr; } @@ -348,13 +424,9 @@ void Segment::handleTransition() { // relies on WS2812FX::service() to call it max every 8ms or more (MIN_SHOW_DELAY) void Segment::handleRandomPalette() { - if (_noOfBlendsRemaining > 0) { - // there needs to be 255 palette blends (48) for full blend - size_t noOfBlends = 3; // blending time ~850ms when MIN_SHOW_DELAY>10 - if (noOfBlends > _noOfBlendsRemaining) noOfBlends = _noOfBlendsRemaining; - for (size_t i=0; i seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC))) + if (nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC)) { doShow = true; uint16_t delay = FRAMETIME; @@ -1100,10 +1175,26 @@ void WS2812FX::service() { if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(seg.cct, true), correctWB); for (uint8_t c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]); - // effect blending (execute previous effect) - // actual code may be a bit more involved as effects have runtime data including allocated memory - //if (seg.transitional && seg._modeP) (*_mode[seg._modeP])(progress()); - delay = (*_mode[seg.currentMode(seg.mode)])(); + // Effect blending (execute previous effect then new effect while in transition) + // WARNING: seg.currentMode(mode) (while in transition) will overwrite SEGENV variables!!! + // so they need to be saved first and then restored before running new mode. + // The blending will largely depend on the effect behaviour since actual output (LEDs) may be + // overwritten by later effect. To enable seamless blending for every effect, additional LED buffer + // would need to be allocated for each effect and then blended together for each pixel. + Segment::tmpsegd_t _tmpSegData; + seg.saveSegenv(&_tmpSegData); + uint8_t newMode = seg.mode; + uint8_t newOpacity = seg.opacity; + uint8_t tmpMode = seg.currentMode(seg.mode); + delay = (*_mode[tmpMode])(); // run old mode + if (newMode != tmpMode) { + if (tmpMode != FX_MODE_HALLOWEEN_EYES) seg.call++; + seg.restoreSegenv(&_tmpSegData); // restore mode state + seg.opacity = (uint32_t)newOpacity * seg.progress() / 0xFFFFU; + delay += (*_mode[seg.mode])(); // run new mode + delay /= 2; // average the delay + seg.opacity = newOpacity; + } if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++; if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition } diff --git a/wled00/led.cpp b/wled00/led.cpp index 97499e76..db49ec2a 100644 --- a/wled00/led.cpp +++ b/wled00/led.cpp @@ -37,12 +37,12 @@ void applyValuesToSelectedSegs() if (effectSpeed != selsegPrev.speed) {seg.speed = effectSpeed; stateChanged = true;} if (effectIntensity != selsegPrev.intensity) {seg.intensity = effectIntensity; stateChanged = true;} - if (effectPalette != selsegPrev.palette) {seg.setPalette(effectPalette); stateChanged = true;} - if (effectCurrent != selsegPrev.mode) {seg.setMode(effectCurrent); stateChanged = true;} + if (effectPalette != selsegPrev.palette) {seg.setPalette(effectPalette);} + if (effectCurrent != selsegPrev.mode) {seg.setMode(effectCurrent);} uint32_t col0 = RGBW32( col[0], col[1], col[2], col[3]); uint32_t col1 = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]); - if (col0 != selsegPrev.colors[0]) {seg.setColor(0, col0); stateChanged = true;} - if (col1 != selsegPrev.colors[1]) {seg.setColor(1, col1); stateChanged = true;} + if (col0 != selsegPrev.colors[0]) {seg.setColor(0, col0);} + if (col1 != selsegPrev.colors[1]) {seg.setColor(1, col1);} } } From bdff05feafe2e0c8af5aed444a56089439c1440d Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sat, 5 Aug 2023 13:53:12 +0200 Subject: [PATCH 06/41] Palette blending optimisation. --- wled00/FX.h | 1 - wled00/FX_fcn.cpp | 13 ++++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index fc8f16b9..a1627a29 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -399,7 +399,6 @@ typedef struct Segment { static CRGBPalette16 _randomPalette; static CRGBPalette16 _newRandomPalette; static unsigned long _lastPaletteChange; - static uint8_t _noOfBlendsRemaining; // transition data, valid only if transitional==true, holds values during transition (72 bytes) struct Transition { diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index c76e22a2..5cbebe1e 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -80,7 +80,6 @@ uint16_t Segment::maxHeight = 1; CRGBPalette16 Segment::_randomPalette = CRGBPalette16(DEFAULT_COLOR); CRGBPalette16 Segment::_newRandomPalette = CRGBPalette16(DEFAULT_COLOR); unsigned long Segment::_lastPaletteChange = 0; // perhaps it should be per segment -uint8_t Segment::_noOfBlendsRemaining = 0; // copy constructor Segment::Segment(const Segment &orig) { @@ -216,7 +215,7 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) { CHSV(random8(), random8(160, 255), random8(128, 255)), CHSV(random8(), random8(160, 255), random8(128, 255))); _lastPaletteChange = millis(); - _noOfBlendsRemaining = 255; + handleRandomPalette(); // do initial blend } targetPalette = _randomPalette; break;} @@ -348,13 +347,9 @@ void Segment::handleTransition() { // relies on WS2812FX::service() to call it max every 8ms or more (MIN_SHOW_DELAY) void Segment::handleRandomPalette() { - if (_noOfBlendsRemaining > 0) { - // there needs to be 255 palette blends (48) for full blend - size_t noOfBlends = 3; // blending time ~850ms when MIN_SHOW_DELAY>10 - if (noOfBlends > _noOfBlendsRemaining) noOfBlends = _noOfBlendsRemaining; - for (size_t i=0; i Date: Sat, 5 Aug 2023 17:35:14 +0200 Subject: [PATCH 07/41] Blending --- wled00/FX.h | 8 +++--- wled00/FX_2Dfcn.cpp | 16 ++++++----- wled00/FX_fcn.cpp | 65 +++++++++++++++++++++------------------------ wled00/wled.h | 2 +- 4 files changed, 45 insertions(+), 46 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index bca67a7e..6d6465cc 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -382,10 +382,8 @@ typedef struct Segment { static uint16_t maxWidth, maxHeight; // these define matrix width & height (max. segment dimensions) typedef struct TemporarySegmentData { + uint16_t _optionsT; uint32_t _colorT[NUM_COLORS]; - //uint8_t _opacityT; - //uint8_t _cctT; // temporary CCT - //uint16_t _optionsT; uint8_t _speedT; uint8_t _intensityT; uint8_t _custom1T, _custom2T; // custom FX parameters/sliders @@ -560,8 +558,8 @@ typedef struct Segment { // transition functions void startTransition(uint16_t dur); // transition has to start before actual segment values change void handleTransition(void); - void saveSegenv(tmpsegd_t *tmpSegD); - void restoreSegenv(tmpsegd_t *tmpSegD); + void saveSegenv(tmpsegd_t *tmpSegD = nullptr); + void restoreSegenv(tmpsegd_t *tmpSegD = nullptr); uint16_t progress(void); //transition progression between 0-65535 uint8_t currentBri(uint8_t briNew, bool useCct = false); uint8_t currentMode(uint8_t modeNew); diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 71000e90..e2b60c81 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -216,23 +216,27 @@ void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col) y *= groupLength(); // expand to physical pixels if (x >= width() || y >= height()) return; // if pixel would fall out of segment just exit + uint32_t tmpCol = col; for (int j = 0; j < grouping; j++) { // groupping vertically for (int g = 0; g < grouping; g++) { // groupping horizontally uint16_t xX = (x+g), yY = (y+j); if (xX >= width() || yY >= height()) continue; // we have reached one dimension's end - strip.setPixelColorXY(start + xX, startY + yY, col); + // if blending modes, blend with underlying pixel + if (transitional && _t && currentMode(mode) != _t->_modeT) tmpCol = color_blend(strip.getPixelColorXY(start + xX, startY + yY), col, progress(), true); + + strip.setPixelColorXY(start + xX, startY + yY, tmpCol); if (mirror) { //set the corresponding horizontally mirrored pixel - if (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col); - else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col); + if (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, tmpCol); + else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, tmpCol); } if (mirror_y) { //set the corresponding vertically mirrored pixel - if (transpose) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col); - else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col); + if (transpose) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, tmpCol); + else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, tmpCol); } if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel - strip.setPixelColorXY(width() - xX - 1, height() - yY - 1, col); + strip.setPixelColorXY(width() - xX - 1, height() - yY - 1, tmpCol); } } } diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index efffa6b4..b991524c 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -288,9 +288,7 @@ void Segment::startTransition(uint16_t dur) { _t = new Transition(dur); // no previous transition running if (!_t) return; // failed to allocate data - DEBUG_PRINT(F("-- Saving transition environment. ")); - DEBUG_PRINTLN(on ? opacity : 0); - saveSegenv(&(_t->_tmpSeg)); + saveSegenv(); CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette); _t->_palT = _palT; _t->_modeT = mode; @@ -328,18 +326,14 @@ uint8_t Segment::currentBri(uint8_t briNew, bool useCct) { } uint8_t Segment::currentMode(uint8_t newMode) { - uint16_t prog = progress(); - if (prog < 0xFFFFU) { // implicit check for transitional & _t in progress() - restoreSegenv(&(_t->_tmpSeg)); - opacity -= (uint32_t)opacity * prog / 0xFFFFU; - return _t->_modeT; - } + uint16_t prog = progress(); // implicit check for transitional & _t in progress() + if (prog < 0xFFFFU) return _t->_modeT; return newMode; } void Segment::saveSegenv(tmpsegd_t *tmpSeg) { - //tmpSeg._opacityT = on ? opacity : 0; - //tmpSeg._optionsT = options; + if (tmpSeg == nullptr) { if (_t) tmpSeg = &(_t->_tmpSeg); else return; } + tmpSeg->_optionsT = options; for (size_t i=0; i_colorT[i] = colors[i]; tmpSeg->_speedT = speed; tmpSeg->_intensityT = intensity; @@ -358,17 +352,19 @@ void Segment::saveSegenv(tmpsegd_t *tmpSeg) { } void Segment::restoreSegenv(tmpsegd_t *tmpSeg) { - if (&(_t->_tmpSeg) != tmpSeg) { - DEBUG_PRINTF("Temp: %p != %p\n", &(_t->_tmpSeg), tmpSeg); - DEBUG_PRINTLN(F("-- Restoring OLD environment.")); - // update possibly changed variables to keep old effect running correctly - _t->_tmpSeg._aux0T = aux0; - _t->_tmpSeg._aux1T = aux1; - _t->_tmpSeg._stepT = step; - _t->_tmpSeg._callT = call; + if (tmpSeg == nullptr) { + if (_t) tmpSeg = &(_t->_tmpSeg); + else return; + } else { + if (&(_t->_tmpSeg) != tmpSeg) { + // update possibly changed variables to keep old effect running correctly + _t->_tmpSeg._aux0T = aux0; + _t->_tmpSeg._aux1T = aux1; + _t->_tmpSeg._stepT = step; + _t->_tmpSeg._callT = call; + } } - //opacity = tmpSeg._opacityT; - //options = tmpSeg._optionsT; + options = tmpSeg->_optionsT; for (size_t i=0; i_colorT[i]; speed = tmpSeg->_speedT; intensity = tmpSeg->_intensityT; @@ -703,6 +699,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col) } i += start; // starting pixel in a group + uint32_t tmpCol = col; // set all the pixels in the group for (int j = 0; j < grouping; j++) { uint16_t indexSet = i + ((reverse) ? -j : j); @@ -711,11 +708,13 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col) uint16_t indexMir = stop - indexSet + start - 1; indexMir += offset; // offset/phase if (indexMir >= stop) indexMir -= len; // wrap - strip.setPixelColor(indexMir, col); + if (transitional && _t && currentMode(mode) != _t->_modeT) tmpCol = color_blend(strip.getPixelColor(indexMir), col, progress(), true); + strip.setPixelColor(indexMir, tmpCol); } indexSet += offset; // offset/phase if (indexSet >= stop) indexSet -= len; // wrap - strip.setPixelColor(indexSet, col); + if (transitional && _t && currentMode(mode) != _t->_modeT) tmpCol = color_blend(strip.getPixelColor(indexSet), col, progress(), true); + strip.setPixelColor(indexSet, tmpCol); } } } @@ -1176,24 +1175,22 @@ void WS2812FX::service() { for (uint8_t c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]); // Effect blending (execute previous effect then new effect while in transition) - // WARNING: seg.currentMode(mode) (while in transition) will overwrite SEGENV variables!!! - // so they need to be saved first and then restored before running new mode. + // If two effects are being blended (each may have different segment settings), those + // settings need to be saved first and then restored before running new mode. + // The seetings need to be applied from transition buffer for previous effect. // The blending will largely depend on the effect behaviour since actual output (LEDs) may be // overwritten by later effect. To enable seamless blending for every effect, additional LED buffer // would need to be allocated for each effect and then blended together for each pixel. Segment::tmpsegd_t _tmpSegData; seg.saveSegenv(&_tmpSegData); - uint8_t newMode = seg.mode; - uint8_t newOpacity = seg.opacity; - uint8_t tmpMode = seg.currentMode(seg.mode); + uint8_t tmpMode = seg.currentMode(seg.mode); + if (seg.mode != tmpMode) seg.restoreSegenv(nullptr); // restore transition data (including temporary opacity) delay = (*_mode[tmpMode])(); // run old mode - if (newMode != tmpMode) { + if (seg.mode != tmpMode) { if (tmpMode != FX_MODE_HALLOWEEN_EYES) seg.call++; - seg.restoreSegenv(&_tmpSegData); // restore mode state - seg.opacity = (uint32_t)newOpacity * seg.progress() / 0xFFFFU; - delay += (*_mode[seg.mode])(); // run new mode - delay /= 2; // average the delay - seg.opacity = newOpacity; + seg.restoreSegenv(&_tmpSegData); // restore mode state + uint16_t d2 = (*_mode[seg.mode])(); // run new mode + delay = MIN(delay,d2); // use shortest delay } if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++; if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition diff --git a/wled00/wled.h b/wled00/wled.h index 789a0730..703776cd 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2308030 +#define VERSION 2308050 //uncomment this if you have a "my_config.h" file you'd like to use //#define WLED_USE_MY_CONFIG From 93a1616933907513249f5c143800066048af2952 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sat, 5 Aug 2023 21:01:06 +0200 Subject: [PATCH 08/41] Blend tweaking. --- wled00/FX.cpp | 18 ++++++------------ wled00/FX.h | 10 ++++++---- wled00/FX_2Dfcn.cpp | 2 +- wled00/FX_fcn.cpp | 12 ++++++++---- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 834c17e9..255e0661 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -2475,12 +2475,6 @@ static const char _data_FX_MODE_RIPPLE_RAINBOW[] PROGMEM = "Ripple Rainbow@!,Wav // // TwinkleFOX: Twinkling 'holiday' lights that fade in and out. // Colors are chosen from a palette. Read more about this effect using the link above! - -// If COOL_LIKE_INCANDESCENT is set to 1, colors will -// fade out slighted 'reddened', similar to how -// incandescent bulbs change color as they get dim down. -#define COOL_LIKE_INCANDESCENT 1 - CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat) { // Overall twinkle speed (changed) @@ -2519,7 +2513,7 @@ CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat) CRGB c; if (bright > 0) { c = ColorFromPalette(SEGPALETTE, hue, bright, NOBLEND); - if(COOL_LIKE_INCANDESCENT == 1) { + if (!SEGMENT.check1) { // This code takes a pixel, and if its in the 'fading down' // part of the cycle, it adjusts the color a little bit like the // way that incandescent bulbs fade toward 'red' as they dim. @@ -2605,14 +2599,14 @@ uint16_t mode_twinklefox() { return twinklefox_base(false); } -static const char _data_FX_MODE_TWINKLEFOX[] PROGMEM = "Twinklefox@!,Twinkle rate;;!"; +static const char _data_FX_MODE_TWINKLEFOX[] PROGMEM = "Twinklefox@!,Twinkle rate,,,,Cool;;!"; uint16_t mode_twinklecat() { return twinklefox_base(true); } -static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rate;;!"; +static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rate,,,,Cool;;!"; //inspired by https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectBlinkingHalloweenEyes @@ -5763,7 +5757,7 @@ uint16_t mode_2Dfloatingblobs(void) { if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) { SEGENV.aux0 = cols; // re-initialise if virtual size changes SEGENV.aux1 = rows; - SEGMENT.fill(BLACK); + //SEGMENT.fill(BLACK); for (size_t i = 0; i < MAX_BLOBS; i++) { blob->r[i] = random8(1, cols>8 ? (cols/4) : 2); blob->sX[i] = (float) random8(3, cols) / (float)(256 - SEGMENT.speed); // speed x @@ -5777,7 +5771,7 @@ uint16_t mode_2Dfloatingblobs(void) { } } - SEGMENT.fadeToBlackBy(20); + SEGMENT.fadeToBlackBy((SEGMENT.custom2>>3)+1); // Bounce balls around for (size_t i = 0; i < Amount; i++) { @@ -5833,7 +5827,7 @@ uint16_t mode_2Dfloatingblobs(void) { return FRAMETIME; } #undef MAX_BLOBS -static const char _data_FX_MODE_2DBLOBS[] PROGMEM = "Blobs@!,# blobs,Blur;!;!;2;c1=8"; +static const char _data_FX_MODE_2DBLOBS[] PROGMEM = "Blobs@!,# blobs,Blur,Trail;!;!;2;c1=8"; //////////////////////////// diff --git a/wled00/FX.h b/wled00/FX.h index 6d6465cc..ecb7f309 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -416,13 +416,14 @@ typedef struct Segment { static uint16_t _usedSegmentData; // perhaps this should be per segment, not static - static CRGBPalette16 _randomPalette; - static CRGBPalette16 _newRandomPalette; - static unsigned long _lastPaletteChange; + static CRGBPalette16 _randomPalette; // actual random palette + static CRGBPalette16 _newRandomPalette; // target random palette + static unsigned long _lastPaletteChange; // last random palette change time in millis() + static bool _modeBlend; // mode/effect blending semaphore // transition data, valid only if transitional==true, holds values during transition (72 bytes) struct Transition { - tmpsegd_t _tmpSeg; + tmpsegd_t _tmpSeg; // previous segment environment uint8_t _briT; // temporary brightness uint8_t _cctT; // temporary CCT uint8_t _modeT; // previous mode/effect @@ -530,6 +531,7 @@ typedef struct Segment { static uint16_t getUsedSegmentData(void) { return _usedSegmentData; } static void addUsedSegmentData(int len) { _usedSegmentData += len; } + static void modeBlend(bool blend) { _modeBlend = blend; } static void handleRandomPalette(); void setUp(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1, uint8_t segId = 255); diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index e2b60c81..2d3a93e4 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -223,7 +223,7 @@ void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col) if (xX >= width() || yY >= height()) continue; // we have reached one dimension's end // if blending modes, blend with underlying pixel - if (transitional && _t && currentMode(mode) != _t->_modeT) tmpCol = color_blend(strip.getPixelColorXY(start + xX, startY + yY), col, progress(), true); + if (_modeBlend) tmpCol = color_blend(strip.getPixelColorXY(start + xX, startY + yY), col, progress(), true); strip.setPixelColorXY(start + xX, startY + yY, tmpCol); diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index b991524c..78172831 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -81,6 +81,8 @@ CRGBPalette16 Segment::_randomPalette = CRGBPalette16(DEFAULT_COLOR); CRGBPalette16 Segment::_newRandomPalette = CRGBPalette16(DEFAULT_COLOR); unsigned long Segment::_lastPaletteChange = 0; // perhaps it should be per segment +bool Segment::_modeBlend = false; + // copy constructor Segment::Segment(const Segment &orig) { //DEBUG_PRINTLN(F("-- Copy segment constructor --")); @@ -708,12 +710,12 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col) uint16_t indexMir = stop - indexSet + start - 1; indexMir += offset; // offset/phase if (indexMir >= stop) indexMir -= len; // wrap - if (transitional && _t && currentMode(mode) != _t->_modeT) tmpCol = color_blend(strip.getPixelColor(indexMir), col, progress(), true); + if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexMir), col, progress(), true); strip.setPixelColor(indexMir, tmpCol); } indexSet += offset; // offset/phase if (indexSet >= stop) indexSet -= len; // wrap - if (transitional && _t && currentMode(mode) != _t->_modeT) tmpCol = color_blend(strip.getPixelColor(indexSet), col, progress(), true); + if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexSet), col, progress(), true); strip.setPixelColor(indexSet, tmpCol); } } @@ -920,7 +922,7 @@ void Segment::fade_out(uint8_t rate) { const uint16_t rows = virtualHeight(); // will be 1 for 1D rate = (255-rate) >> 1; - float mappedRate = float(rate) +1.1; + float mappedRate = float(rate) +1.1f; uint32_t color = colors[1]; // SEGCOLOR(1); // target color int w2 = W(color); @@ -1184,12 +1186,14 @@ void WS2812FX::service() { Segment::tmpsegd_t _tmpSegData; seg.saveSegenv(&_tmpSegData); uint8_t tmpMode = seg.currentMode(seg.mode); - if (seg.mode != tmpMode) seg.restoreSegenv(nullptr); // restore transition data (including temporary opacity) + if (seg.mode != tmpMode) seg.restoreSegenv(); // restore transition data (including temporary opacity) delay = (*_mode[tmpMode])(); // run old mode if (seg.mode != tmpMode) { if (tmpMode != FX_MODE_HALLOWEEN_EYES) seg.call++; seg.restoreSegenv(&_tmpSegData); // restore mode state + Segment::modeBlend(true); // set semaphore uint16_t d2 = (*_mode[seg.mode])(); // run new mode + Segment::modeBlend(false); // unset semaphore delay = MIN(delay,d2); // use shortest delay } if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++; From 45d7e664884a5d561435fa9a915f8ea96323a650 Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 5 Aug 2023 23:29:23 +0200 Subject: [PATCH 09/41] effects bugfix: prevent crash when SEGLEN==1 * Blurz and a few other effects would crash (or behave unexpectedly) for single pixel segments * replaced a few "MAX" by "max", because MAX will evaluate its arguments twice so its very inefficient. --- wled00/FX.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 834c17e9..b962bb58 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -713,7 +713,7 @@ uint16_t mode_hyper_sparkle(void) { if (strip.now - SEGENV.aux0 > SEGENV.step) { if (random8((255-SEGMENT.intensity) >> 4) == 0) { - for (int i = 0; i < MAX(1, SEGLEN/3); i++) { + for (int i = 0; i < max(1, SEGLEN/3); i++) { SEGMENT.setPixelColor(random16(SEGLEN), SEGCOLOR(1)); } } @@ -1220,7 +1220,7 @@ uint16_t mode_fireworks() { 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); x = index % width; @@ -3023,7 +3023,7 @@ uint16_t candle(bool multi) if (multi) { //allocate segment data - uint16_t dataSize = (SEGLEN -1) *3; //max. 1365 pixels (ESP8266) + uint16_t dataSize = max(1, SEGLEN -1) *3; //max. 1365 pixels (ESP8266) if (!SEGENV.allocateData(dataSize)) return candle(false); //allocation failed } @@ -3156,7 +3156,7 @@ uint16_t mode_starburst(void) { if (random8((144-(SEGMENT.speed >> 1))) == 0 && stars[j].birth == 0) { // Pick a random color and location. - uint16_t startPos = random16(SEGLEN-1); + uint16_t startPos = (SEGLEN > 1) ? random16(SEGLEN-1) : 0; float multiplier = (float)(random8())/255.0 * 1.0; stars[j].color = CRGB(SEGMENT.color_wheel(random8())); @@ -3400,7 +3400,7 @@ uint16_t mode_drip(void) uint8_t numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3 float gravity = -0.0005 - (SEGMENT.speed/50000.0); - gravity *= SEGLEN-1; + gravity *= max(1, SEGLEN-1); int sourcedrop = 12; for (int j=0;j SPEED_FORMULA_L) { uint16_t segLoc = random16(SEGLEN); - SEGMENT.setPixelColor(segLoc, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(2*fftResult[SEGENV.aux0%16]*240/(SEGLEN-1), false, PALETTE_SOLID_WRAP, 0), 2*fftResult[SEGENV.aux0%16])); + SEGMENT.setPixelColor(segLoc, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(2*fftResult[SEGENV.aux0%16]*240/max(1, SEGLEN-1), false, PALETTE_SOLID_WRAP, 0), 2*fftResult[SEGENV.aux0%16])); ++(SEGENV.aux0) %= 16; // make sure it doesn't cross 16 SEGENV.step = 1; From cb42ca8765619ba6a865706efbc1e54b4383a4a8 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sun, 6 Aug 2023 19:11:44 +0200 Subject: [PATCH 10/41] FX optimisation --- wled00/FX.cpp | 163 ++++++++++++++++++-------------------------------- 1 file changed, 57 insertions(+), 106 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 255e0661..d4f1e4da 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -604,11 +604,6 @@ static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!,!;!,!;!;;m12=0"; * Dissolve function */ uint16_t dissolve(uint32_t color) { - //bool wa = (SEGCOLOR(1) != 0 && strip.getBrightness() < 255); //workaround, can't compare getPixel to color if not full brightness - if (SEGENV.call == 0) { - SEGMENT.fill(SEGCOLOR(1)); - } - for (int j = 0; j <= SEGLEN / 15; j++) { if (random8() <= SEGMENT.intensity) { for (size_t times = 0; times < 10; times++) //attempt to spawn a new pixel 10 times @@ -713,7 +708,7 @@ uint16_t mode_hyper_sparkle(void) { if (strip.now - SEGENV.aux0 > SEGENV.step) { if (random8((255-SEGMENT.intensity) >> 4) == 0) { - for (int i = 0; i < MAX(1, SEGLEN/3); i++) { + for (int i = 0; i < max(1, SEGLEN/3); i++) { SEGMENT.setPixelColor(random16(SEGLEN), SEGCOLOR(1)); } } @@ -764,7 +759,7 @@ uint16_t mode_android(void) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); } - if (SEGENV.aux1 > ((float)SEGMENT.intensity/255.0)*(float)SEGLEN) + if (SEGENV.aux1 > (SEGMENT.intensity*SEGLEN)/255) { SEGENV.aux0 = 1; } else @@ -814,7 +809,7 @@ static const char _data_FX_MODE_ANDROID[] PROGMEM = "Android@!,Width;!,!;!;;m12= */ uint16_t chase(uint32_t color1, uint32_t color2, uint32_t color3, bool do_palette) { uint16_t counter = strip.now * ((SEGMENT.speed >> 2) + 1); - uint16_t a = counter * SEGLEN >> 16; + uint16_t a = (counter * SEGLEN) >> 16; bool chase_random = (SEGMENT.mode == FX_MODE_CHASE_RANDOM); if (chase_random) { @@ -828,7 +823,7 @@ uint16_t chase(uint32_t color1, uint32_t color2, uint32_t color3, bool do_palett SEGENV.step = a; // Use intensity setting to vary chase up to 1/2 string length - uint8_t size = 1 + (SEGMENT.intensity * SEGLEN >> 10); + uint8_t size = 1 + ((SEGMENT.intensity * SEGLEN) >> 10); uint16_t b = a + size; //"trail" of chase, filled with color1 if (b > SEGLEN) b -= SEGLEN; @@ -1116,8 +1111,9 @@ static const char _data_FX_MODE_RUNNING_RANDOM[] PROGMEM = "Stream@!,Zone size;; uint16_t larson_scanner(bool dual) { + if (SEGLEN == 1) return mode_static(); uint16_t counter = strip.now * ((SEGMENT.speed >> 2) +8); - uint16_t index = counter * SEGLEN >> 16; + uint16_t index = (counter * SEGLEN) >> 16; SEGMENT.fade_out(SEGMENT.intensity); @@ -1204,7 +1200,6 @@ uint16_t mode_fireworks() { const uint16_t height = SEGMENT.virtualHeight(); if (SEGENV.call == 0) { - SEGMENT.fill(SEGCOLOR(1)); SEGENV.aux0 = UINT16_MAX; SEGENV.aux1 = UINT16_MAX; } @@ -1305,24 +1300,23 @@ static const char _data_FX_MODE_FIRE_FLICKER[] PROGMEM = "Fire Flicker@!,!;!;!;0 * Gradient run base function */ uint16_t gradient_base(bool loading) { + if (SEGLEN == 1) return mode_static(); uint16_t counter = strip.now * ((SEGMENT.speed >> 2) + 1); - uint16_t pp = counter * SEGLEN >> 16; + uint16_t pp = (counter * SEGLEN) >> 16; if (SEGENV.call == 0) pp = 0; - float val; //0.0 = sec 1.0 = pri - float brd = loading ? SEGMENT.intensity : SEGMENT.intensity/2; - if (brd <1.0) brd = 1.0; + int val; //0 = sec 1 = pri + int brd = 1 + loading ? SEGMENT.intensity/2 : SEGMENT.intensity/4; + //if (brd < 1) brd = 1; int p1 = pp-SEGLEN; int p2 = pp+SEGLEN; - for (int i = 0; i < SEGLEN; i++) - { - if (loading) - { - val = abs(((i>pp) ? p2:pp) -i); + for (int i = 0; i < SEGLEN; i++) { + if (loading) { + val = abs(((i>pp) ? p2:pp) - i); } else { - val = MIN(abs(pp-i),MIN(abs(p1-i),abs(p2-i))); + val = min(abs(pp-i),min(abs(p1-i),abs(p2-i))); } - val = (brd > val) ? val/brd * 255 : 255; + val = (brd > val) ? (val * 255) / brd : 255; SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(0), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), val)); } @@ -1903,12 +1897,8 @@ static const char _data_FX_MODE_PRIDE_2015[] PROGMEM = "Pride 2015@!;;"; //eight colored dots, weaving in and out of sync with each other uint16_t mode_juggle(void) { if (SEGLEN == 1) return mode_static(); - if (SEGENV.call == 0) { - SEGMENT.fill(BLACK); - } SEGMENT.fadeToBlackBy(192 - (3*SEGMENT.intensity/4)); - CRGB fastled_col; byte dothue = 0; for (int i = 0; i < 8; i++) { @@ -2073,12 +2063,9 @@ static const char _data_FX_MODE_COLORWAVES[] PROGMEM = "Colorwaves@!,Hue;!;!"; // colored stripes pulsing at a defined Beats-Per-Minute (BPM) uint16_t mode_bpm() { - //CRGB fastled_col; uint32_t stp = (strip.now / 20) & 0xFF; uint8_t beat = beatsin8(SEGMENT.speed, 64, 255); for (int i = 0; i < SEGLEN; i++) { - //fastled_col = ColorFromPalette(SEGPALETTE, stp + (i * 2), beat - stp + (i * 10)); - //SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(stp + (i * 2), false, PALETTE_SOLID_WRAP, 0, beat - stp + (i * 10))); } @@ -2924,7 +2911,7 @@ uint16_t mode_glitter() static const char _data_FX_MODE_GLITTER[] PROGMEM = "Glitter@!,!,,,,,Overlay;1,2,Glitter color;!;;pal=0,m12=0"; //pixels -//Solid colour background with glitter +//Solid colour background with glitter (can be replaced by Glitter) uint16_t mode_solid_glitter() { SEGMENT.fill(SEGCOLOR(0)); @@ -3014,8 +3001,7 @@ static const char _data_FX_MODE_POPCORN[] PROGMEM = "Popcorn@!,!,,,,,Overlay;!,! uint16_t candle(bool multi) { - if (multi) - { + if (multi && SEGLEN > 1) { //allocate segment data uint16_t dataSize = (SEGLEN -1) *3; //max. 1365 pixels (ESP8266) if (!SEGENV.allocateData(dataSize)) return candle(false); //allocation failed @@ -3584,8 +3570,8 @@ uint16_t mode_percent(void) { uint8_t percent = SEGMENT.intensity; percent = constrain(percent, 0, 200); - uint16_t active_leds = (percent < 100) ? SEGLEN * percent / 100.0 - : SEGLEN * (200 - percent) / 100.0; + uint16_t active_leds = (percent < 100) ? roundf(SEGLEN * percent / 100.0f) + : roundf(SEGLEN * (200 - percent) / 100.0f); uint8_t size = (1 + ((SEGMENT.speed * SEGLEN) >> 11)); if (SEGMENT.speed == 255) size = 255; @@ -4576,11 +4562,6 @@ uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulma const uint16_t rows = SEGMENT.virtualHeight(); uint16_t x, y; - // initialize on first call - if (SEGENV.call == 0) { - SEGMENT.fill(BLACK); - } - SEGMENT.fadeToBlackBy(16 + (SEGMENT.speed>>3)); // create fading trails unsigned long t = millis()/128; // timebase // outer stars @@ -4615,7 +4596,6 @@ uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.so const uint16_t rows = SEGMENT.virtualHeight(); if (SEGENV.call == 0) { - SEGMENT.fill(BLACK); SEGENV.aux0 = 0; // start with red hue } @@ -4667,12 +4647,7 @@ uint16_t mode_2Ddna(void) { // dna originally by by ldirko at https://pa const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - if (SEGENV.call == 0) { - SEGMENT.fill(BLACK); - } - SEGMENT.fadeToBlackBy(64); - for (int i = 0; i < cols; i++) { SEGMENT.setPixelColorXY(i, beatsin8(SEGMENT.speed/8, 0, rows-1, 0, i*4 ), ColorFromPalette(SEGPALETTE, i*5+millis()/17, beatsin8(5, 55, 255, 0, i*10), LINEARBLEND)); SEGMENT.setPixelColorXY(i, beatsin8(SEGMENT.speed/8, 0, rows-1, 0, i*4+128), ColorFromPalette(SEGPALETTE, i*5+128+millis()/17, beatsin8(5, 55, 255, 0, i*10+128), LINEARBLEND)); @@ -4738,12 +4713,7 @@ uint16_t mode_2DDrift() { // By: Stepko https://editor.soulmateli const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - if (SEGENV.call == 0) { - SEGMENT.fill(BLACK); - } - SEGMENT.fadeToBlackBy(128); - const uint16_t maxDim = MAX(cols, rows)/2; unsigned long t = millis() / (32 - (SEGMENT.speed>>3)); unsigned long t_20 = t/20; // softhack007: pre-calculating this gives about 10% speedup @@ -4803,10 +4773,6 @@ uint16_t mode_2DFrizzles(void) { // By: Stepko https://editor.so const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - if (SEGENV.call == 0) { - SEGMENT.fill(BLACK); - } - SEGMENT.fadeToBlackBy(16); for (size_t i = 8; i > 0; i--) { SEGMENT.addPixelColorXY(beatsin8(SEGMENT.speed/8 + i, 0, cols - 1), @@ -5250,12 +5216,7 @@ uint16_t mode_2DPlasmaball(void) { // By: Stepko https://edito const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - if (SEGENV.call == 0) { - SEGMENT.fill(BLACK); - } - SEGMENT.fadeToBlackBy(SEGMENT.custom1>>2); - uint_fast32_t t = (millis() * 8) / (256 - SEGMENT.speed); // optimized to avoid float for (int i = 0; i < cols; i++) { uint16_t thisVal = inoise8(i * 30, t, t); @@ -5346,12 +5307,7 @@ uint16_t mode_2DPulser(void) { // By: ldirko https://edi const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - if (SEGENV.call == 0) { - SEGMENT.fill(BLACK); - } - SEGMENT.fadeToBlackBy(8 - (SEGMENT.intensity>>5)); - uint32_t a = strip.now / (18 - SEGMENT.speed / 16); uint16_t x = (a / 14) % cols; uint16_t y = map((sin8(a * 5) + sin8(a * 4) + sin8(a * 2)), 0, 765, rows-1, 0); @@ -5404,14 +5360,9 @@ uint16_t mode_2Dsquaredswirl(void) { // By: Mark Kriegsman. https://g const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - if (SEGENV.call == 0) { - SEGMENT.fill(BLACK); - } - const uint8_t kBorderWidth = 2; SEGMENT.fadeToBlackBy(24); - uint8_t blurAmount = SEGMENT.custom3>>1; // reduced resolution slider SEGMENT.blur(blurAmount); @@ -5532,10 +5483,6 @@ uint16_t mode_2Dspaceships(void) { //// Space ships by stepko (c)05.02.21 [ht const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - if (SEGENV.call == 0) { - SEGMENT.fill(BLACK); - } - uint32_t tb = strip.now >> 12; // every ~4s if (tb > SEGENV.step) { int8_t dir = ++SEGENV.aux0; @@ -5601,7 +5548,6 @@ uint16_t mode_2Dcrazybees(void) { bee_t *bee = reinterpret_cast(SEGENV.data); if (SEGENV.call == 0) { - SEGMENT.fill(BLACK); for (size_t i = 0; i < n; i++) { bee[i].posX = random8(0, cols); bee[i].posY = random8(0, rows); @@ -5610,7 +5556,7 @@ uint16_t mode_2Dcrazybees(void) { } if (millis() > SEGENV.step) { - SEGENV.step = millis() + (FRAMETIME * 8 / ((SEGMENT.speed>>5)+1)); + SEGENV.step = millis() + (FRAMETIME * 16 / ((SEGMENT.speed>>4)+1)); SEGMENT.fadeToBlackBy(32); @@ -5673,7 +5619,6 @@ uint16_t mode_2Dghostrider(void) { if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) { SEGENV.aux0 = cols; SEGENV.aux1 = rows; - SEGMENT.fill(BLACK); random16_set_seed(strip.now); lighter->angleSpeed = random8(0,20) - 10; lighter->Vspeed = 5; @@ -5918,10 +5863,6 @@ uint16_t mode_2Ddriftrose(void) { const float CY = (rows-rows%2)/2.f - .5f; const float L = min(cols, rows) / 2.f; - if (SEGENV.call == 0) { - SEGMENT.fill(BLACK); - } - SEGMENT.fadeToBlackBy(32+(SEGMENT.speed>>3)); for (size_t i = 1; i < 37; i++) { uint32_t x = (CX + (sin_t(radians(i * 10)) * (beatsin8(i, 0, L*2)-L))) * 255.f; @@ -6094,8 +6035,6 @@ uint16_t mode_2DSwirl(void) { float volumeSmth = *(float*) um_data->u_data[0]; //ewowi: use instead of sampleAvg??? int16_t volumeRaw = *(int16_t*) um_data->u_data[1]; - // printUmData(); - SEGMENT.addPixelColorXY( i, j, ColorFromPalette(SEGPALETTE, (ms / 11 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 11, 200, 255); SEGMENT.addPixelColorXY( j, i, ColorFromPalette(SEGPALETTE, (ms / 13 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 13, 200, 255); SEGMENT.addPixelColorXY(ni,nj, ColorFromPalette(SEGPALETTE, (ms / 17 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 17, 200, 255); @@ -6118,10 +6057,6 @@ uint16_t mode_2DWaverly(void) { const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - if (SEGENV.call == 0) { - SEGMENT.fill(BLACK); - } - um_data_t *um_data; if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { // add support for no audio @@ -6169,6 +6104,7 @@ typedef struct Gravity { // * GRAVCENTER // /////////////////////// uint16_t mode_gravcenter(void) { // Gravcenter. By Andrew Tuline. + if (SEGLEN == 1) return mode_static(); const uint16_t dataSize = sizeof(gravity); if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed @@ -6187,7 +6123,7 @@ uint16_t mode_gravcenter(void) { // Gravcenter. By Andrew Tuline. float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f; segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivty" upscaling - float mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 32, 0, (float)SEGLEN/2.0); // map to pixels available in current segment + float mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 32, 0, (float)SEGLEN/2.0f); // map to pixels available in current segment uint16_t tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing. uint8_t gravity = 8 - SEGMENT.speed/32; @@ -6217,6 +6153,7 @@ static const char _data_FX_MODE_GRAVCENTER[] PROGMEM = "Gravcenter@Rate of fall, // * GRAVCENTRIC // /////////////////////// uint16_t mode_gravcentric(void) { // Gravcentric. By Andrew Tuline. + if (SEGLEN == 1) return mode_static(); uint16_t dataSize = sizeof(gravity); if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed @@ -6235,10 +6172,10 @@ uint16_t mode_gravcentric(void) { // Gravcentric. By Andrew //SEGMENT.fade_out(240); // twice? really? SEGMENT.fade_out(253); // 50% - float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0; + float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f; segmentSampleAvg *= 0.125f; // divide by 8, to compensate for later "sensitivty" upscaling - float mySampleAvg = mapf(segmentSampleAvg*2.0, 0.0f, 32.0f, 0.0f, (float)SEGLEN/2.0); // map to pixels availeable in current segment + float mySampleAvg = mapf(segmentSampleAvg*2.0, 0.0f, 32.0f, 0.0f, (float)SEGLEN/2.0f); // map to pixels availeable in current segment int tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing. uint8_t gravity = 8 - SEGMENT.speed/32; @@ -6268,6 +6205,7 @@ static const char _data_FX_MODE_GRAVCENTRIC[] PROGMEM = "Gravcentric@Rate of fal // * GRAVIMETER // /////////////////////// uint16_t mode_gravimeter(void) { // Gravmeter. By Andrew Tuline. + if (SEGLEN == 1) return mode_static(); uint16_t dataSize = sizeof(gravity); if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed @@ -6314,6 +6252,7 @@ static const char _data_FX_MODE_GRAVIMETER[] PROGMEM = "Gravimeter@Rate of fall, // * JUGGLES // ////////////////////// uint16_t mode_juggles(void) { // Juggles. By Andrew Tuline. + if (SEGLEN == 1) return mode_static(); um_data_t *um_data; if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { // add support for no audio @@ -6337,6 +6276,7 @@ static const char _data_FX_MODE_JUGGLES[] PROGMEM = "Juggles@!,# of balls;!,!;!; // * MATRIPIX // ////////////////////// uint16_t mode_matripix(void) { // Matripix. By Andrew Tuline. + if (SEGLEN == 1) return mode_static(); // even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment um_data_t *um_data; @@ -6368,6 +6308,7 @@ static const char _data_FX_MODE_MATRIPIX[] PROGMEM = "Matripix@!,Brightness;!,!; // * MIDNOISE // ////////////////////// uint16_t mode_midnoise(void) { // Midnoise. By Andrew Tuline. + if (SEGLEN == 1) return mode_static(); // Changing xdist to SEGENV.aux0 and ydist to SEGENV.aux1. um_data_t *um_data; @@ -6471,6 +6412,7 @@ static const char _data_FX_MODE_NOISEMETER[] PROGMEM = "Noisemeter@Fade rate,Wid // * PIXELWAVE // ////////////////////// uint16_t mode_pixelwave(void) { // Pixelwave. By Andrew Tuline. + if (SEGLEN == 1) return mode_static(); // even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment if (SEGENV.call == 0) { @@ -6546,6 +6488,7 @@ static const char _data_FX_MODE_PLASMOID[] PROGMEM = "Plasmoid@Phase,# of pixels /////////////////////// // Andrew's crappy peak detector. If I were 40+ years younger, I'd learn signal processing. uint16_t mode_puddlepeak(void) { // Puddlepeak. By Andrew Tuline. + if (SEGLEN == 1) return mode_static(); uint16_t size = 0; uint8_t fadeVal = map(SEGMENT.speed,0,255, 224, 254); @@ -6589,6 +6532,7 @@ static const char _data_FX_MODE_PUDDLEPEAK[] PROGMEM = "Puddlepeak@Fade rate,Pud // * PUDDLES // ////////////////////// uint16_t mode_puddles(void) { // Puddles. By Andrew Tuline. + if (SEGLEN == 1) return mode_static(); uint16_t size = 0; uint8_t fadeVal = map(SEGMENT.speed, 0, 255, 224, 254); uint16_t pos = random16(SEGLEN); // Set a random starting position. @@ -6620,6 +6564,7 @@ static const char _data_FX_MODE_PUDDLES[] PROGMEM = "Puddles@Fade rate,Puddle si // * PIXELS // ////////////////////// uint16_t mode_pixels(void) { // Pixels. By Andrew Tuline. + if (SEGLEN == 1) return mode_static(); if (!SEGENV.allocateData(32*sizeof(uint8_t))) return mode_static(); //allocation failed uint8_t *myVals = reinterpret_cast(SEGENV.data); // Used to store a pile of samples because WLED frame rate and WLED sample rate are not synchronized. Frame rate is too low. @@ -6653,6 +6598,7 @@ static const char _data_FX_MODE_PIXELS[] PROGMEM = "Pixels@Fade rate,# of pixels // ** Blurz // ////////////////////// uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline. + if (SEGLEN == 1) return mode_static(); // even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment um_data_t *um_data; @@ -6689,6 +6635,7 @@ static const char _data_FX_MODE_BLURZ[] PROGMEM = "Blurz@Fade rate,Blur;!,Color // ** DJLight // ///////////////////////// uint16_t mode_DJLight(void) { // Written by ??? Adapted by Will Tatam. + if (SEGLEN == 1) return mode_static(); const int mid = SEGLEN / 2; um_data_t *um_data; @@ -6722,6 +6669,7 @@ static const char _data_FX_MODE_DJLIGHT[] PROGMEM = "DJ Light@Speed;;;1f;m12=2,s // ** Freqmap // //////////////////// uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN. Would be better if a higher framerate. + if (SEGLEN == 1) return mode_static(); // Start frequency = 60 Hz and log10(60) = 1.78 // End frequency = MAX_FREQUENCY in Hz and lo10(MAX_FREQUENCY) = MAX_FREQ_LOG10 @@ -6758,6 +6706,7 @@ static const char _data_FX_MODE_FREQMAP[] PROGMEM = "Freqmap@Fade rate,Starting // ** Freqmatrix // /////////////////////// uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Pleschung. + if (SEGLEN == 1) return mode_static(); um_data_t *um_data; if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { // add support for no audio @@ -6823,24 +6772,27 @@ uint16_t mode_freqpixels(void) { // Freqpixel. By Andrew Tuline. } float FFT_MajorPeak = *(float*)um_data->u_data[4]; float my_magnitude = *(float*)um_data->u_data[5] / 16.0f; - if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception) + if (FFT_MajorPeak < 1) FFT_MajorPeak = 1.0f; // log10(0) is "forbidden" (throws exception) - uint16_t fadeRate = 2*SEGMENT.speed - SEGMENT.speed*SEGMENT.speed/255; // Get to 255 as quick as you can. + // this code translates to speed * (2 - speed/255) which is a) speed*2 or b) speed (when speed is 255) + // and since fade_out() can only take 0-255 it will behave incorrectly when speed > 127 + //uint16_t fadeRate = 2*SEGMENT.speed - SEGMENT.speed*SEGMENT.speed/255; // Get to 255 as quick as you can. + uint16_t fadeRate = SEGMENT.speed*SEGMENT.speed; // Get to 255 as quick as you can. + fadeRate = map(fadeRate, 0, 65535, 1, 255); - if (SEGENV.call == 0) SEGMENT.fill(BLACK); int fadeoutDelay = (256 - SEGMENT.speed) / 64; if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fade_out(fadeRate); + uint8_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(MAX_FREQ_LOG10 - 1.78f); // Scale log10 of frequency values to the 255 colour index. + if (FFT_MajorPeak < 61.0f) pixCol = 0; // handle underflow for (int i=0; i < SEGMENT.intensity/32+1; i++) { uint16_t locn = random16(0,SEGLEN); - uint8_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(MAX_FREQ_LOG10 - 1.78f); // Scale log10 of frequency values to the 255 colour index. - if (FFT_MajorPeak < 61.0f) pixCol = 0; // handle underflow SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude)); } return FRAMETIME; } // mode_freqpixels() -static const char _data_FX_MODE_FREQPIXELS[] PROGMEM = "Freqpixels@Fade rate,Starting color and # of pixels;;;1f;m12=0,si=0"; // Pixels, Beatsin +static const char _data_FX_MODE_FREQPIXELS[] PROGMEM = "Freqpixels@Fade rate,Starting color and # of pixels;!,!,;!;1f;m12=0,si=0"; // Pixels, Beatsin ////////////////////// @@ -6859,6 +6811,7 @@ static const char _data_FX_MODE_FREQPIXELS[] PROGMEM = "Freqpixels@Fade rate,Sta // As a compromise between speed and accuracy we are currently sampling with 10240Hz, from which we can then determine with a 512bin FFT our max frequency is 5120Hz. // Depending on the music stream you have you might find it useful to change the frequency mapping. uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschung. + if (SEGLEN == 1) return mode_static(); um_data_t *um_data; if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { // add support for no audio @@ -6915,7 +6868,7 @@ static const char _data_FX_MODE_FREQWAVE[] PROGMEM = "Freqwave@Speed,Sound effec // ** Gravfreq // /////////////////////// uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline. - + if (SEGLEN == 1) return mode_static(); uint16_t dataSize = sizeof(gravity); if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed Gravity* gravcen = reinterpret_cast(SEGENV.data); @@ -6932,9 +6885,9 @@ uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline. SEGMENT.fade_out(250); float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f; - segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivty" upscaling + segmentSampleAvg *= 0.125f; // divide by 8, to compensate for later "sensitivty" upscaling - float mySampleAvg = mapf(segmentSampleAvg*2.0f, 0,32, 0, (float)SEGLEN/2.0); // map to pixels availeable in current segment + float mySampleAvg = mapf(segmentSampleAvg*2.0f, 0,32, 0, (float)SEGLEN/2.0f); // map to pixels availeable in current segment int tempsamp = constrain(mySampleAvg,0,SEGLEN/2); // Keep the sample from overflowing. uint8_t gravity = 8 - SEGMENT.speed/32; @@ -6967,6 +6920,7 @@ static const char _data_FX_MODE_GRAVFREQ[] PROGMEM = "Gravfreq@Rate of fall,Sens // ** Noisemove // ////////////////////// uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuline + if (SEGLEN == 1) return mode_static(); um_data_t *um_data; if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { // add support for no audio @@ -6974,8 +6928,6 @@ uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuli } uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; - if (SEGENV.call == 0) SEGMENT.fill(BLACK); - //SEGMENT.fade_out(224); // Just in case something doesn't get faded. int fadeoutDelay = (256 - SEGMENT.speed) / 96; if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(4+ SEGMENT.speed/4); @@ -6995,6 +6947,7 @@ static const char _data_FX_MODE_NOISEMOVE[] PROGMEM = "Noisemove@Speed of perlin // ** Rocktaves // ////////////////////// uint16_t mode_rocktaves(void) { // Rocktaves. Same note from each octave is same colour. By: Andrew Tuline + if (SEGLEN == 1) return mode_static(); um_data_t *um_data; if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { // add support for no audio @@ -7003,8 +6956,7 @@ uint16_t mode_rocktaves(void) { // Rocktaves. Same note from eac float FFT_MajorPeak = *(float*) um_data->u_data[4]; float my_magnitude = *(float*) um_data->u_data[5] / 16.0f; - if (SEGENV.call == 0) SEGMENT.fill(BLACK); - SEGMENT.fadeToBlackBy(16); // Just in case something doesn't get faded. + SEGMENT.fadeToBlackBy(16); // Just in case something doesn't get faded. float frTemp = FFT_MajorPeak; uint8_t octCount = 0; // Octave counter. @@ -7019,8 +6971,8 @@ uint16_t mode_rocktaves(void) { // Rocktaves. Same note from eac frTemp = frTemp/2; } - frTemp -=132; // This should give us a base musical note of C3 - frTemp = fabsf(frTemp * 2.1f); // Fudge factors to compress octave range starting at 0 and going to 255; + frTemp -= 132.0f; // This should give us a base musical note of C3 + frTemp = fabsf(frTemp * 2.1f); // Fudge factors to compress octave range starting at 0 and going to 255; uint16_t i = map(beatsin8(8+octCount*4, 0, 255, 0, octCount*8), 0, 255, 0, SEGLEN-1); i = constrain(i, 0, SEGLEN-1); @@ -7036,7 +6988,7 @@ static const char _data_FX_MODE_ROCKTAVES[] PROGMEM = "Rocktaves@;!,!;!;1f;m12=1 /////////////////////// // Combines peak detection with FFT_MajorPeak and FFT_Magnitude. uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tuline - if (SEGENV.call == 0) SEGMENT.fill(BLACK); + if (SEGLEN == 1) return mode_static(); um_data_t *um_data; if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { @@ -7111,7 +7063,6 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma. rippleTime = true; } - if (SEGENV.call == 0) SEGMENT.fill(BLACK); int fadeoutDelay = (256 - SEGMENT.speed) / 64; if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(SEGMENT.speed); From 084070475dabd3815578b6fe42105ee8ca8b4f9a Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Mon, 7 Aug 2023 16:50:18 +0200 Subject: [PATCH 11/41] Chasing memory corruption/leaks. --- wled00/FX.cpp | 2 + wled00/FX.h | 9 ++-- wled00/FX_fcn.cpp | 126 ++++++++++++++++++++++++++-------------------- wled00/json.cpp | 3 ++ 4 files changed, 81 insertions(+), 59 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index d4f1e4da..750653fd 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5621,6 +5621,7 @@ uint16_t mode_2Dghostrider(void) { SEGENV.aux1 = rows; random16_set_seed(strip.now); lighter->angleSpeed = random8(0,20) - 10; + lighter->gAngle = random16(); lighter->Vspeed = 5; lighter->gPosX = (cols/2) * 10; lighter->gPosY = (rows/2) * 10; @@ -5628,6 +5629,7 @@ uint16_t mode_2Dghostrider(void) { lighter->lightersPosX[i] = lighter->gPosX; lighter->lightersPosY[i] = lighter->gPosY + i; lighter->time[i] = i * 2; + lighter->reg[i] = false; } } diff --git a/wled00/FX.h b/wled00/FX.h index ecb7f309..a38aac33 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -498,14 +498,14 @@ typedef struct Segment { Segment(Segment &&orig) noexcept; // move constructor ~Segment() { - //#ifdef WLED_DEBUG - //Serial.print(F("Destroying segment:")); + #ifdef WLED_DEBUG + Serial.printf("-- Destroying segment: %p\n", this); //if (name) Serial.printf(" %s (%p)", name, name); //if (data) Serial.printf(" %d (%p)", (int)_dataLen, data); //Serial.println(); - //#endif + #endif if (name) { delete[] name; name = nullptr; } - if (_t) { transitional = false; delete _t; _t = nullptr; } + stopTransition(); deallocateData(); } @@ -559,6 +559,7 @@ typedef struct Segment { // transition functions void startTransition(uint16_t dur); // transition has to start before actual segment values change + void stopTransition(void); void handleTransition(void); void saveSegenv(tmpsegd_t *tmpSegD = nullptr); void restoreSegenv(tmpsegd_t *tmpSegD = nullptr); diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 78172831..d89a37ff 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -85,7 +85,7 @@ bool Segment::_modeBlend = false; // copy constructor Segment::Segment(const Segment &orig) { - //DEBUG_PRINTLN(F("-- Copy segment constructor --")); + //DEBUG_PRINTF("-- Copy segment constructor: %p -> %p\n", &orig, this); memcpy((void*)this, (void*)&orig, sizeof(Segment)); transitional = false; // copied segment cannot be in transition name = nullptr; @@ -94,12 +94,12 @@ Segment::Segment(const Segment &orig) { _t = nullptr; if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } - //if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); } + //if (orig._t) { _t = new Transition(orig._t->_dur); } } // move constructor Segment::Segment(Segment &&orig) noexcept { - //DEBUG_PRINTLN(F("-- Move segment constructor --")); + //DEBUG_PRINTF("-- Move segment constructor: %p -> %p\n", &orig, this); memcpy((void*)this, (void*)&orig, sizeof(Segment)); orig.transitional = false; // old segment cannot be in transition any more orig.name = nullptr; @@ -110,12 +110,15 @@ Segment::Segment(Segment &&orig) noexcept { // copy assignment Segment& Segment::operator= (const Segment &orig) { - //DEBUG_PRINTLN(F("-- Copying segment --")); + //DEBUG_PRINTF("-- Copying segment: %p -> %p\n", &orig, this); if (this != &orig) { // clean destination transitional = false; // copied segment cannot be in transition if (name) delete[] name; - if (_t) delete _t; + if (_t) { + if (_t->_tmpSeg._dataT) free(_t->_tmpSeg._dataT); + delete _t; + } deallocateData(); // copy source memcpy((void*)this, (void*)&orig, sizeof(Segment)); @@ -135,12 +138,16 @@ Segment& Segment::operator= (const Segment &orig) { // move assignment Segment& Segment::operator= (Segment &&orig) noexcept { - //DEBUG_PRINTLN(F("-- Moving segment --")); + //DEBUG_PRINTF("-- Moving segment: %p -> %p\n", &orig, this); if (this != &orig) { transitional = false; // just temporary if (name) { delete[] name; name = nullptr; } // free old name deallocateData(); // free old runtime data - if (_t) { delete _t; _t = nullptr; } + if (_t) { + if (_t->_tmpSeg._dataT) free(_t->_tmpSeg._dataT); + delete _t; + _t = nullptr; + } memcpy((void*)this, (void*)&orig, sizeof(Segment)); orig.transitional = false; // old segment cannot be in transition orig.name = nullptr; @@ -153,12 +160,14 @@ Segment& Segment::operator= (Segment &&orig) noexcept { bool Segment::allocateData(size_t len) { if (data && _dataLen == len) return true; //already allocated + //DEBUG_PRINTF("-- Allocating data (%d): %p\n", len, this); deallocateData(); - if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) return false; //not enough memory + if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) { DEBUG_PRINTF("!!! Effect RAM depleted: %d/%d !!!\n", len, Segment::getUsedSegmentData()); return false; } //not enough memory // do not use SPI RAM on ESP32 since it is slow data = (byte*) malloc(len); - if (!data) return false; //allocation failed + if (!data) { DEBUG_PRINTLN(F("!!! Allocation failed. !!!")); return false; } //allocation failed Segment::addUsedSegmentData(len); + DEBUG_PRINTF("--- Allocated data (%p): %d/%d -> %p\n", this, len, Segment::getUsedSegmentData(), data); _dataLen = len; memset(data, 0, len); return true; @@ -166,9 +175,11 @@ bool Segment::allocateData(size_t len) { void Segment::deallocateData() { if (!data) return; + DEBUG_PRINTF("--- Released data (%d/%d): %p -> %p\n", _dataLen, Segment::getUsedSegmentData(), this, data); free(data); data = nullptr; - Segment::addUsedSegmentData(-_dataLen); + // WARNING it looks like we have a memory leak somewhere + Segment::addUsedSegmentData(_dataLen <= Segment::getUsedSegmentData() ? -_dataLen : -Segment::getUsedSegmentData()); _dataLen = 0; } @@ -181,7 +192,7 @@ void Segment::deallocateData() { */ void Segment::resetIfRequired() { if (!reset) return; - DEBUG_PRINTLN(F("-- Segment reset.")); + //DEBUG_PRINTF("-- Segment reset: %p\n", this); startTransition(0); // stop pending transition deallocateData(); next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0; @@ -273,15 +284,8 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) { void Segment::startTransition(uint16_t dur) { if (!dur) { - transitional = false; - if (_t) { - if (_t->_tmpSeg._dataT && _t->_tmpSeg._dataLenT > 0) { - free(_t->_tmpSeg._dataT); - _t->_tmpSeg._dataT = nullptr; - } - delete _t; - _t = nullptr; - } + if (_t) _t->_dur = dur; // this will stop transition in next handleTransisiton() + else transitional = false; return; } if (transitional && _t) return; // already in transition no need to store anything @@ -290,18 +294,20 @@ void Segment::startTransition(uint16_t dur) { _t = new Transition(dur); // no previous transition running if (!_t) return; // failed to allocate data + //DEBUG_PRINTF("-- Started transition: %p\n", this); saveSegenv(); CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette); _t->_palT = _palT; _t->_modeT = mode; _t->_briT = on ? opacity : 0; _t->_cctT = cct; + _t->_tmpSeg._optionsT |= 0b0000000001000000; // mark old segment transitional _t->_tmpSeg._dataLenT = 0; _t->_tmpSeg._dataT = nullptr; if (_dataLen > 0 && data) { _t->_tmpSeg._dataT = (byte *)malloc(_dataLen); if (_t->_tmpSeg._dataT) { - DEBUG_PRINTLN(F("-- Allocated duplicate data.")); + //DEBUG_PRINTF("-- Allocated duplicate data (%d): %p\n", _dataLen, _t->_tmpSeg._dataT); memcpy(_t->_tmpSeg._dataT, data, _dataLen); _t->_tmpSeg._dataLenT = _dataLen; } @@ -309,6 +315,27 @@ void Segment::startTransition(uint16_t dur) { transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true); } +void Segment::stopTransition() { + if (!transitional) return; + transitional = false; // finish transitioning segment + //DEBUG_PRINTF("-- Stopping transition: %p\n", this); + if (_t) { + if (_t->_tmpSeg._dataT && _t->_tmpSeg._dataLenT > 0) { + //DEBUG_PRINTF("-- Released duplicate data (%d): %p\n", _t->_tmpSeg._dataLenT, _t->_tmpSeg._dataT); + free(_t->_tmpSeg._dataT); + _t->_tmpSeg._dataT = nullptr; + } + delete _t; + _t = nullptr; + } +} + +void Segment::handleTransition() { + if (!transitional) return; + uint16_t _progress = progress(); + if (_progress == 0xFFFFU) stopTransition(); +} + // transition progression between 0-65535 uint16_t Segment::progress() { if (transitional && _t) { @@ -318,22 +345,8 @@ uint16_t Segment::progress() { return 0xFFFFU; } -uint8_t Segment::currentBri(uint8_t briNew, bool useCct) { - uint32_t prog = progress(); - if (prog < 0xFFFFU) { - if (useCct) return ((briNew * prog) + _t->_cctT * (0xFFFFU - prog)) >> 16; - else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16; - } - return briNew; -} - -uint8_t Segment::currentMode(uint8_t newMode) { - uint16_t prog = progress(); // implicit check for transitional & _t in progress() - if (prog < 0xFFFFU) return _t->_modeT; - return newMode; -} - void Segment::saveSegenv(tmpsegd_t *tmpSeg) { + //DEBUG_PRINTF("-- Saving temp seg: %p (%p)\n", this, tmpSeg); if (tmpSeg == nullptr) { if (_t) tmpSeg = &(_t->_tmpSeg); else return; } tmpSeg->_optionsT = options; for (size_t i=0; i_colorT[i] = colors[i]; @@ -351,9 +364,11 @@ void Segment::saveSegenv(tmpsegd_t *tmpSeg) { tmpSeg->_callT = call; tmpSeg->_dataT = data; tmpSeg->_dataLenT = _dataLen; + //DEBUG_PRINTF("-- temp seg data: %p (%d,%p)\n", this, _dataLen, data); } void Segment::restoreSegenv(tmpsegd_t *tmpSeg) { + //DEBUG_PRINTF("-- Restoring temp seg: %p (%p)\n", this, tmpSeg); if (tmpSeg == nullptr) { if (_t) tmpSeg = &(_t->_tmpSeg); else return; @@ -364,6 +379,9 @@ void Segment::restoreSegenv(tmpsegd_t *tmpSeg) { _t->_tmpSeg._aux1T = aux1; _t->_tmpSeg._stepT = step; _t->_tmpSeg._callT = call; + if (_t->_tmpSeg._dataT != data) DEBUG_PRINTF("--- data re-allocated: (%p) %p -> %p\n", this, _t->_tmpSeg._dataT, data); + _t->_tmpSeg._dataT = data; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!) + _t->_tmpSeg._dataLenT = _dataLen; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!) } } options = tmpSeg->_optionsT; @@ -382,6 +400,22 @@ void Segment::restoreSegenv(tmpsegd_t *tmpSeg) { call = tmpSeg->_callT; data = tmpSeg->_dataT; _dataLen = tmpSeg->_dataLenT; + //DEBUG_PRINTF("-- temp seg data: %p (%d,%p)\n", this, _dataLen, data); +} + +uint8_t Segment::currentBri(uint8_t briNew, bool useCct) { + uint32_t prog = progress(); + if (prog < 0xFFFFU) { + if (useCct) return ((briNew * prog) + _t->_cctT * (0xFFFFU - prog)) >> 16; + else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16; + } + return briNew; +} + +uint8_t Segment::currentMode(uint8_t newMode) { + uint16_t prog = progress(); // implicit check for transitional & _t in progress() + if (prog < 0xFFFFU) return _t->_modeT; + return newMode; } uint32_t Segment::currentColor(uint8_t slot, uint32_t colorNew) { @@ -402,24 +436,6 @@ CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal return targetPalette; } -void Segment::handleTransition() { - if (!transitional) return; - uint16_t _progress = progress(); - if (_progress == 0xFFFFU) transitional = false; // finish transitioning segment - if (_t) { // thanks to @nXm AKA https://github.com/NMeirer - //if (_progress >= 32767U && _t->_modeP != mode) markForReset(); - if (_progress == 0xFFFFU) { - DEBUG_PRINTLN(F("-- Stopping transition.")); - if (_t->_tmpSeg._dataT && _t->_tmpSeg._dataLenT > 0) { - free(_t->_tmpSeg._dataT); - _t->_tmpSeg._dataT = nullptr; - } - delete _t; - _t = nullptr; - } - } -} - // relies on WS2812FX::service() to call it max every 8ms or more (MIN_SHOW_DELAY) void Segment::handleRandomPalette() { // just do a blend; if the palettes are identical it will just compare 48 bytes (same as _randomPalette == _newRandomPalette) @@ -1186,7 +1202,7 @@ void WS2812FX::service() { Segment::tmpsegd_t _tmpSegData; seg.saveSegenv(&_tmpSegData); uint8_t tmpMode = seg.currentMode(seg.mode); - if (seg.mode != tmpMode) seg.restoreSegenv(); // restore transition data (including temporary opacity) + if (seg.mode != tmpMode) seg.restoreSegenv(); // restore transition data delay = (*_mode[tmpMode])(); // run old mode if (seg.mode != tmpMode) { if (tmpMode != FX_MODE_HALLOWEEN_EYES) seg.call++; diff --git a/wled00/json.cpp b/wled00/json.cpp index c0c72989..5fa4c04f 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -29,8 +29,11 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId) id = strip.getSegmentsNum()-1; // segments are added at the end of list } + //DEBUG_PRINTLN("-- JSON deserialize segment."); Segment& seg = strip.getSegment(id); + //DEBUG_PRINTF("-- Original segment: %p\n", &seg); Segment prev = seg; //make a backup so we can tell if something changed + //DEBUG_PRINTF("-- Duplicate segment: %p\n", &prev); uint16_t start = elem["start"] | seg.start; if (stop < 0) { From 9832fbe0422dbc77ebd96ba7eeaef2979732a068 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Tue, 8 Aug 2023 20:40:19 +0200 Subject: [PATCH 12/41] Segment reset --- wled00/FX.h | 5 ++++- wled00/FX_fcn.cpp | 9 +++++---- wled00/wled.h | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index a38aac33..029afa60 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -487,6 +487,9 @@ typedef struct Segment { _t(nullptr) { //refreshLightCapabilities(); + #ifdef WLED_DEBUG + //Serial.printf("-- Creating segment: %p\n", this); + #endif } Segment(uint16_t sStartX, uint16_t sStopX, uint16_t sStartY, uint16_t sStopY) : Segment(sStartX, sStopX) { @@ -499,7 +502,7 @@ typedef struct Segment { ~Segment() { #ifdef WLED_DEBUG - Serial.printf("-- Destroying segment: %p\n", this); + //Serial.printf("-- Destroying segment: %p\n", this); //if (name) Serial.printf(" %s (%p)", name, name); //if (data) Serial.printf(" %d (%p)", (int)_dataLen, data); //Serial.println(); diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index d89a37ff..513b5c2f 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -175,7 +175,7 @@ bool Segment::allocateData(size_t len) { void Segment::deallocateData() { if (!data) return; - DEBUG_PRINTF("--- Released data (%d/%d): %p -> %p\n", _dataLen, Segment::getUsedSegmentData(), this, data); + DEBUG_PRINTF("--- Released data (%p): %d/%d -> %p\n", this, _dataLen, Segment::getUsedSegmentData(), data); free(data); data = nullptr; // WARNING it looks like we have a memory leak somewhere @@ -193,7 +193,6 @@ void Segment::deallocateData() { void Segment::resetIfRequired() { if (!reset) return; //DEBUG_PRINTF("-- Segment reset: %p\n", this); - startTransition(0); // stop pending transition deallocateData(); next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0; reset = false; @@ -307,7 +306,7 @@ void Segment::startTransition(uint16_t dur) { if (_dataLen > 0 && data) { _t->_tmpSeg._dataT = (byte *)malloc(_dataLen); if (_t->_tmpSeg._dataT) { - //DEBUG_PRINTF("-- Allocated duplicate data (%d): %p\n", _dataLen, _t->_tmpSeg._dataT); + DEBUG_PRINTF("-- Allocated duplicate data (%d): %p\n", _dataLen, _t->_tmpSeg._dataT); memcpy(_t->_tmpSeg._dataT, data, _dataLen); _t->_tmpSeg._dataLenT = _dataLen; } @@ -321,7 +320,7 @@ void Segment::stopTransition() { //DEBUG_PRINTF("-- Stopping transition: %p\n", this); if (_t) { if (_t->_tmpSeg._dataT && _t->_tmpSeg._dataLenT > 0) { - //DEBUG_PRINTF("-- Released duplicate data (%d): %p\n", _t->_tmpSeg._dataLenT, _t->_tmpSeg._dataT); + DEBUG_PRINTF("-- Released duplicate data (%d): %p\n", _t->_tmpSeg._dataLenT, _t->_tmpSeg._dataT); free(_t->_tmpSeg._dataT); _t->_tmpSeg._dataT = nullptr; } @@ -380,6 +379,7 @@ void Segment::restoreSegenv(tmpsegd_t *tmpSeg) { _t->_tmpSeg._stepT = step; _t->_tmpSeg._callT = call; if (_t->_tmpSeg._dataT != data) DEBUG_PRINTF("--- data re-allocated: (%p) %p -> %p\n", this, _t->_tmpSeg._dataT, data); + //if (_t->_tmpSeg._dataT && _t->_tmpSeg._dataLenT > 0) free(_t->_tmpSeg._dataT); // not good _t->_tmpSeg._dataT = data; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!) _t->_tmpSeg._dataLenT = _dataLen; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!) } @@ -557,6 +557,7 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) { sOpt = extractModeDefaults(fx, "mY"); if (sOpt >= 0) mirror_y = (bool)sOpt; // NOTE: setting this option is a risky business sOpt = extractModeDefaults(fx, "pal"); if (sOpt >= 0) setPalette(sOpt); //else setPalette(0); } + markForReset(); stateChanged = true; // send UDP/WS broadcast } } diff --git a/wled00/wled.h b/wled00/wled.h index 703776cd..cae5d732 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2308050 +#define VERSION 2308080 //uncomment this if you have a "my_config.h" file you'd like to use //#define WLED_USE_MY_CONFIG From 7b28387bb470ddd686d17c2306252013f3bdd3c6 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Wed, 9 Aug 2023 17:23:21 +0200 Subject: [PATCH 13/41] Comments from Aircoookie addressed --- wled00/FX_fcn.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 513b5c2f..3d9b7d31 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1170,13 +1170,13 @@ void WS2812FX::service() { _segment_index = 0; Segment::handleRandomPalette(); // move it into for loop when each segment has individual random palette for (segment &seg : _segments) { - if (!seg.isActive()) continue; - // process transition (mode changes in the middle of transition) seg.handleTransition(); // reset the segment runtime data if needed seg.resetIfRequired(); + if (!seg.isActive()) continue; + // last condition ensures all solid segments are updated at the same time if (nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC)) { @@ -1387,9 +1387,7 @@ void WS2812FX::setMode(uint8_t segid, uint8_t m) { if (m >= getModeCount()) m = getModeCount() - 1; if (_segments[segid].mode != m) { - _segments[segid].startTransition(_transitionDur); // set effect transitions - //_segments[segid].markForReset(); - _segments[segid].mode = m; + _segments[segid].setMode(m); // do not load defaults } } From 353e97a4c17e3356627cfe4d0e47139175c7950d Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Fri, 11 Aug 2023 00:48:32 +0200 Subject: [PATCH 14/41] Clear status pixels --- wled00/bus_manager.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 7ea44b15..cd692f8f 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -123,6 +123,9 @@ BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(bc.count); // only needs a third of "RGB" LEDs for NeoPixelBus _busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip, nr, _frequencykHz); _valid = (_busPtr != nullptr); + if (_valid) + for (int i=0; i<_skip; i++) + PolyBus::setPixelColor(_busPtr, _iType, i, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // clear sacrificial pixels DEBUG_PRINTF("%successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u)\n", _valid?"S":"Uns", nr, bc.count, bc.type, _pins[0], _pins[1], _iType); } @@ -187,6 +190,8 @@ void BusDigital::setBrightness(uint8_t b) { //TODO only show if no new show due in the next 50ms void BusDigital::setStatusPixel(uint32_t c) { if (_valid && _skip) { + for (int i=1; i<_skip; i++) + PolyBus::setPixelColor(_busPtr, _iType, i, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); PolyBus::setPixelColor(_busPtr, _iType, 0, c, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); if (canShow()) PolyBus::show(_busPtr, _iType); } From 04aa9f0e617c152894087aaec843f9060b1c86a2 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Fri, 11 Aug 2023 23:11:08 +0200 Subject: [PATCH 15/41] Release of WLED beta 0.14.0-b4 --- CHANGELOG.md | 14 ++++++++++++++ package-lock.json | 2 +- package.json | 2 +- platformio.ini | 6 +++--- wled00/FX_fcn.cpp | 4 ++-- wled00/html_settings.h | 4 ++-- wled00/improv.cpp | 2 +- wled00/wled.h | 4 ++-- 8 files changed, 26 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d792e73..7b8cec19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ ## WLED changelog +#### Build 2308110 +- Release of WLED beta version 0.14.0-b4 "Hoshi" +- Reset effect data immediately upon mode change + +#### Build 2308030 +- Improved random palette handling and blending +- Soap bugfix +- Fix ESP-NOW crash with AP mode Always + +#### Build 2307180 +- Bus-level global buffering (#3280) +- Removed per-segment LED buffer (SEGMENT.leds) +- various fixes and improvements (ESP variants platform 5.3.0, effect optimizations, /json/cfg pin allocation) + #### Build 2307130 - larger `oappend()` stack buffer (3.5k) for ESP32 - Preset cycle bugfix (#3262) diff --git a/package-lock.json b/package-lock.json index c4fe9b1c..dc3b2b98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "wled", - "version": "0.14.0-b3", + "version": "0.14.0-b4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index d57c87d5..ab0622b8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wled", - "version": "0.14.0-b3", + "version": "0.14.0-b4", "description": "Tools for WLED project", "main": "tools/cdata.js", "directories": { diff --git a/platformio.ini b/platformio.ini index 9321eea0..29f1ced7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -458,7 +458,7 @@ platform_packages = ${esp32c3.platform_packages} framework = arduino board = esp32-c3-devkitm-1 board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv -build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3 +build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=ESP32-C3 -D WLED_WATCHDOG_TIMEOUT=0 -DLOLIN_WIFI_FIX ; seems to work much better with this -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB @@ -474,7 +474,7 @@ platform = ${esp32s3.platform} platform_packages = ${esp32s3.platform_packages} upload_speed = 921600 ; or 460800 build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32s3.build_flags} +build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_8MB -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 -D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip ;-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") @@ -581,7 +581,7 @@ platform_packages = ${esp32s2.platform_packages} board = lolin_s2_mini board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv build_unflags = ${common.build_unflags} #-DARDUINO_USB_CDC_ON_BOOT=1 -build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=LolinS2 +build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=ESP32-S2 -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT=1 # try disabling and enabling unflag above in case of board-specific issues, will disable Serial -DARDUINO_USB_MSC_ON_BOOT=0 diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 5cbebe1e..6497f1ad 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -337,7 +337,6 @@ void Segment::handleTransition() { uint16_t _progress = progress(); if (_progress == 0xFFFFU) transitional = false; // finish transitioning segment if (_t) { // thanks to @nXm AKA https://github.com/NMeirer - if (_progress >= 32767U && _t->_modeP != mode) markForReset(); if (_progress == 0xFFFFU) { delete _t; _t = nullptr; @@ -465,6 +464,7 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) { sOpt = extractModeDefaults(fx, "mY"); if (sOpt >= 0) mirror_y = (bool)sOpt; // NOTE: setting this option is a risky business sOpt = extractModeDefaults(fx, "pal"); if (sOpt >= 0) setPalette(sOpt); //else setPalette(0); } + markForReset(); stateChanged = true; // send UDP/WS broadcast } } @@ -1274,7 +1274,7 @@ void WS2812FX::setMode(uint8_t segid, uint8_t m) { if (_segments[segid].mode != m) { _segments[segid].startTransition(_transitionDur); // set effect transitions - //_segments[segid].markForReset(); + _segments[segid].markForReset(); _segments[segid].mode = m; } } diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 8082d0b8..a3613531 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -1754,7 +1754,7 @@ const uint8_t PAGE_settings_sec[] PROGMEM = { 0xb7, 0x4d, 0x92, 0x26, 0x11, 0xda, 0xcf, 0x48, 0x28, 0xb8, 0x59, 0x35, 0x09, 0x71, 0x75, 0xef, 0x9d, 0x48, 0x9d, 0x28, 0xa5, 0xe6, 0x52, 0xf4, 0xa8, 0x07, 0xef, 0xa1, 0x4e, 0xc7, 0x7a, 0x46, 0x9f, 0xbb, 0x3e, 0x4d, 0x72, 0x94, 0x48, 0x3e, 0xa4, 0x61, 0x0a, 0x82, 0x0e, 0xbb, 0x17, 0x9a, - 0x68, 0x86, 0xf5, 0xc3, 0xfd, 0x97, 0x61, 0x7f, 0x6f, 0xf2, 0x62, 0xcd, 0x55, 0x7f, 0x46, 0xc2, + 0x68, 0x86, 0xf5, 0xc3, 0xfd, 0x97, 0x61, 0x7f, 0x6f, 0xf2, 0x72, 0xcd, 0x55, 0x7f, 0x46, 0xc2, 0x52, 0xce, 0x65, 0xcf, 0x5d, 0xdb, 0xa8, 0x83, 0x55, 0xda, 0xec, 0x81, 0x0d, 0xf7, 0x12, 0x2d, 0x52, 0x49, 0x41, 0xda, 0x79, 0x28, 0x7f, 0x7b, 0x29, 0xaa, 0xb5, 0xa0, 0x0b, 0x28, 0xf8, 0x4f, 0x8a, 0x9a, 0x46, 0x4d, 0x29, 0x12, 0x09, 0xce, 0xb1, 0xa8, 0x08, 0xf3, 0x75, 0xea, 0x76, 0x4e, @@ -1768,7 +1768,7 @@ const uint8_t PAGE_settings_sec[] PROGMEM = { 0x19, 0xc6, 0xa0, 0x40, 0x82, 0x50, 0x71, 0x5d, 0x2e, 0x5a, 0x26, 0x30, 0xb2, 0xe4, 0x94, 0xb3, 0xee, 0x93, 0x02, 0x73, 0x5f, 0x09, 0x88, 0xea, 0x69, 0x49, 0xcd, 0xab, 0x94, 0x27, 0xc4, 0x12, 0xf5, 0x67, 0x98, 0xdf, 0x6d, 0x64, 0xbf, 0xef, 0x63, 0x3b, 0x7f, 0xaa, 0x91, 0xed, 0x51, 0xb5, - 0xc1, 0x0f, 0xf5, 0xef, 0xd4, 0xcc, 0xd3, 0x77, 0xe0, 0xff, 0x02, 0xe8, 0x93, 0xa8, 0x82, 0x17, + 0xc1, 0x0f, 0xf5, 0xef, 0xd4, 0xcc, 0xd3, 0x77, 0xe0, 0xff, 0x02, 0x6f, 0x8b, 0xcf, 0x3d, 0x17, 0x16, 0x00, 0x00 }; diff --git a/wled00/improv.cpp b/wled00/improv.cpp index d99be68d..c7bb5aa6 100644 --- a/wled00/improv.cpp +++ b/wled00/improv.cpp @@ -210,7 +210,7 @@ void sendImprovInfoResponse() { //Use serverDescription if it has been changed from the default "WLED", else mDNS name bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0); char vString[20]; - sprintf_P(vString, PSTR("0.14.0-b3/%i"), VERSION); + sprintf_P(vString, PSTR("0.14.0-b4/%i"), VERSION); const char *str[4] = {"WLED", vString, bString, useMdnsName ? cmDNS : serverDescription}; sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str); diff --git a/wled00/wled.h b/wled00/wled.h index 789a0730..0d22e23d 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -3,12 +3,12 @@ /* Main sketch, global variable declarations @title WLED project sketch - @version 0.14.0-b3 + @version 0.14.0-b4 @author Christian Schwinne */ // version code in format yymmddb (b = daily build) -#define VERSION 2308030 +#define VERSION 2308110 //uncomment this if you have a "my_config.h" file you'd like to use //#define WLED_USE_MY_CONFIG From ac83b6763280e8f36652d9e619a12d5c25131746 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sat, 12 Aug 2023 12:45:11 +0200 Subject: [PATCH 16/41] Cleaner transition code. Fixed skipped pixel flashing. --- wled00/FX.h | 18 +--- wled00/FX_2Dfcn.cpp | 2 +- wled00/FX_fcn.cpp | 190 ++++++++++++++++++++++------------------- wled00/bus_manager.cpp | 9 +- 4 files changed, 110 insertions(+), 109 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index 029afa60..56323f15 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -423,7 +423,7 @@ typedef struct Segment { // transition data, valid only if transitional==true, holds values during transition (72 bytes) struct Transition { - tmpsegd_t _tmpSeg; // previous segment environment + tmpsegd_t _segT; // previous segment environment uint8_t _briT; // temporary brightness uint8_t _cctT; // temporary CCT uint8_t _modeT; // previous mode/effect @@ -437,18 +437,6 @@ typedef struct Segment { , _start(millis()) , _dur(dur) {} - /* - Transition(uint16_t d, uint8_t b, uint8_t c, const uint32_t *o) - : _palT(CRGBPalette16(CRGB::Black)) - , _prevPaletteBlends(0) - , _start(millis()) - , _dur(d) - { - _tmpSeg._opacityT = b; - _tmpSeg._cctT = c; - for (size_t i=0; i= width() || yY >= height()) continue; // we have reached one dimension's end // if blending modes, blend with underlying pixel - if (_modeBlend) tmpCol = color_blend(strip.getPixelColorXY(start + xX, startY + yY), col, progress(), true); + if (_modeBlend) tmpCol = color_blend(strip.getPixelColorXY(start + xX, startY + yY), col, 0xFFFFU - progress(), true); strip.setPixelColorXY(start + xX, startY + yY, tmpCol); diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 3d9b7d31..bbb5cd5d 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -116,7 +116,7 @@ Segment& Segment::operator= (const Segment &orig) { transitional = false; // copied segment cannot be in transition if (name) delete[] name; if (_t) { - if (_t->_tmpSeg._dataT) free(_t->_tmpSeg._dataT); + if (_t->_segT._dataT) free(_t->_segT._dataT); delete _t; } deallocateData(); @@ -144,7 +144,7 @@ Segment& Segment::operator= (Segment &&orig) noexcept { if (name) { delete[] name; name = nullptr; } // free old name deallocateData(); // free old runtime data if (_t) { - if (_t->_tmpSeg._dataT) free(_t->_tmpSeg._dataT); + if (_t->_segT._dataT) free(_t->_segT._dataT); delete _t; _t = nullptr; } @@ -162,12 +162,16 @@ bool Segment::allocateData(size_t len) { if (data && _dataLen == len) return true; //already allocated //DEBUG_PRINTF("-- Allocating data (%d): %p\n", len, this); deallocateData(); - if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) { DEBUG_PRINTF("!!! Effect RAM depleted: %d/%d !!!\n", len, Segment::getUsedSegmentData()); return false; } //not enough memory + if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) { + // not enough memory + DEBUG_PRINTF("!!! Effect RAM depleted: %d/%d !!!\n", len, Segment::getUsedSegmentData()); + return false; + } // do not use SPI RAM on ESP32 since it is slow data = (byte*) malloc(len); if (!data) { DEBUG_PRINTLN(F("!!! Allocation failed. !!!")); return false; } //allocation failed Segment::addUsedSegmentData(len); - DEBUG_PRINTF("--- Allocated data (%p): %d/%d -> %p\n", this, len, Segment::getUsedSegmentData(), data); + //DEBUG_PRINTF("--- Allocated data (%p): %d/%d -> %p\n", this, len, Segment::getUsedSegmentData(), data); _dataLen = len; memset(data, 0, len); return true; @@ -175,7 +179,7 @@ bool Segment::allocateData(size_t len) { void Segment::deallocateData() { if (!data) return; - DEBUG_PRINTF("--- Released data (%p): %d/%d -> %p\n", this, _dataLen, Segment::getUsedSegmentData(), data); + //DEBUG_PRINTF("--- Released data (%p): %d/%d -> %p\n", this, _dataLen, Segment::getUsedSegmentData(), data); free(data); data = nullptr; // WARNING it looks like we have a memory leak somewhere @@ -294,21 +298,21 @@ void Segment::startTransition(uint16_t dur) { if (!_t) return; // failed to allocate data //DEBUG_PRINTF("-- Started transition: %p\n", this); - saveSegenv(); + swapSegenv(_t->_segT); CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette); - _t->_palT = _palT; - _t->_modeT = mode; - _t->_briT = on ? opacity : 0; - _t->_cctT = cct; - _t->_tmpSeg._optionsT |= 0b0000000001000000; // mark old segment transitional - _t->_tmpSeg._dataLenT = 0; - _t->_tmpSeg._dataT = nullptr; + _t->_palT = _palT; + _t->_modeT = mode; + _t->_briT = on ? opacity : 0; + _t->_cctT = cct; + _t->_segT._optionsT |= 0b0000000001000000; // mark old segment transitional + _t->_segT._dataLenT = 0; + _t->_segT._dataT = nullptr; if (_dataLen > 0 && data) { - _t->_tmpSeg._dataT = (byte *)malloc(_dataLen); - if (_t->_tmpSeg._dataT) { - DEBUG_PRINTF("-- Allocated duplicate data (%d): %p\n", _dataLen, _t->_tmpSeg._dataT); - memcpy(_t->_tmpSeg._dataT, data, _dataLen); - _t->_tmpSeg._dataLenT = _dataLen; + _t->_segT._dataT = (byte *)malloc(_dataLen); + if (_t->_segT._dataT) { + //DEBUG_PRINTF("-- Allocated duplicate data (%d): %p\n", _dataLen, _t->_segT._dataT); + memcpy(_t->_segT._dataT, data, _dataLen); + _t->_segT._dataLenT = _dataLen; } } transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true); @@ -319,10 +323,10 @@ void Segment::stopTransition() { transitional = false; // finish transitioning segment //DEBUG_PRINTF("-- Stopping transition: %p\n", this); if (_t) { - if (_t->_tmpSeg._dataT && _t->_tmpSeg._dataLenT > 0) { - DEBUG_PRINTF("-- Released duplicate data (%d): %p\n", _t->_tmpSeg._dataLenT, _t->_tmpSeg._dataT); - free(_t->_tmpSeg._dataT); - _t->_tmpSeg._dataT = nullptr; + if (_t->_segT._dataT && _t->_segT._dataLenT > 0) { + //DEBUG_PRINTF("-- Released duplicate data (%d): %p\n", _t->_segT._dataLenT, _t->_segT._dataT); + free(_t->_segT._dataT); + _t->_segT._dataT = nullptr; } delete _t; _t = nullptr; @@ -344,62 +348,75 @@ uint16_t Segment::progress() { return 0xFFFFU; } -void Segment::saveSegenv(tmpsegd_t *tmpSeg) { +void Segment::swapSegenv(tmpsegd_t &tmpSeg) { + if (!_t) return; //DEBUG_PRINTF("-- Saving temp seg: %p (%p)\n", this, tmpSeg); - if (tmpSeg == nullptr) { if (_t) tmpSeg = &(_t->_tmpSeg); else return; } - tmpSeg->_optionsT = options; - for (size_t i=0; i_colorT[i] = colors[i]; - tmpSeg->_speedT = speed; - tmpSeg->_intensityT = intensity; - tmpSeg->_custom1T = custom1; - tmpSeg->_custom2T = custom2; - tmpSeg->_custom3T = custom3; - tmpSeg->_check1T = check1; - tmpSeg->_check2T = check2; - tmpSeg->_check3T = check3; - tmpSeg->_aux0T = aux0; - tmpSeg->_aux1T = aux1; - tmpSeg->_stepT = step; - tmpSeg->_callT = call; - tmpSeg->_dataT = data; - tmpSeg->_dataLenT = _dataLen; + tmpSeg._optionsT = options; + for (size_t i=0; i_segT)) { + // swap SEGENV with transitional data + options = _t->_segT._optionsT; + for (size_t i=0; i_segT._colorT[i]; + speed = _t->_segT._speedT; + intensity = _t->_segT._intensityT; + custom1 = _t->_segT._custom1T; + custom2 = _t->_segT._custom2T; + custom3 = _t->_segT._custom3T; + check1 = _t->_segT._check1T; + check2 = _t->_segT._check2T; + check3 = _t->_segT._check3T; + aux0 = _t->_segT._aux0T; + aux1 = _t->_segT._aux1T; + step = _t->_segT._stepT; + call = _t->_segT._callT; + data = _t->_segT._dataT; + _dataLen = _t->_segT._dataLenT; + } //DEBUG_PRINTF("-- temp seg data: %p (%d,%p)\n", this, _dataLen, data); } -void Segment::restoreSegenv(tmpsegd_t *tmpSeg) { +void Segment::restoreSegenv(tmpsegd_t &tmpSeg) { //DEBUG_PRINTF("-- Restoring temp seg: %p (%p)\n", this, tmpSeg); - if (tmpSeg == nullptr) { - if (_t) tmpSeg = &(_t->_tmpSeg); - else return; - } else { - if (&(_t->_tmpSeg) != tmpSeg) { - // update possibly changed variables to keep old effect running correctly - _t->_tmpSeg._aux0T = aux0; - _t->_tmpSeg._aux1T = aux1; - _t->_tmpSeg._stepT = step; - _t->_tmpSeg._callT = call; - if (_t->_tmpSeg._dataT != data) DEBUG_PRINTF("--- data re-allocated: (%p) %p -> %p\n", this, _t->_tmpSeg._dataT, data); - //if (_t->_tmpSeg._dataT && _t->_tmpSeg._dataLenT > 0) free(_t->_tmpSeg._dataT); // not good - _t->_tmpSeg._dataT = data; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!) - _t->_tmpSeg._dataLenT = _dataLen; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!) - } + if (_t && &(_t->_segT) != &tmpSeg) { + // update possibly changed variables to keep old effect running correctly + _t->_segT._aux0T = aux0; + _t->_segT._aux1T = aux1; + _t->_segT._stepT = step; + _t->_segT._callT = call; + //if (_t->_segT._dataT != data) DEBUG_PRINTF("--- data re-allocated: (%p) %p -> %p\n", this, _t->_segT._dataT, data); + _t->_segT._dataT = data; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!) + _t->_segT._dataLenT = _dataLen; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!) } - options = tmpSeg->_optionsT; - for (size_t i=0; i_colorT[i]; - speed = tmpSeg->_speedT; - intensity = tmpSeg->_intensityT; - custom1 = tmpSeg->_custom1T; - custom2 = tmpSeg->_custom2T; - custom3 = tmpSeg->_custom3T; - check1 = tmpSeg->_check1T; - check2 = tmpSeg->_check2T; - check3 = tmpSeg->_check3T; - aux0 = tmpSeg->_aux0T; - aux1 = tmpSeg->_aux1T; - step = tmpSeg->_stepT; - call = tmpSeg->_callT; - data = tmpSeg->_dataT; - _dataLen = tmpSeg->_dataLenT; + options = tmpSeg._optionsT; + for (size_t i=0; i_tmpSeg._colorT[slot], colorNew, progress(), true) : colorNew; + return transitional && _t ? color_blend(_t->_segT._colorT[slot], colorNew, progress(), true) : colorNew; } CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) { @@ -727,12 +744,12 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col) uint16_t indexMir = stop - indexSet + start - 1; indexMir += offset; // offset/phase if (indexMir >= stop) indexMir -= len; // wrap - if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexMir), col, progress(), true); + if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexMir), col, 0xFFFFU - progress(), true); strip.setPixelColor(indexMir, tmpCol); } indexSet += offset; // offset/phase if (indexSet >= stop) indexSet -= len; // wrap - if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexSet), col, progress(), true); + if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexSet), col, 0xFFFFU - progress(), true); strip.setPixelColor(indexSet, tmpCol); } } @@ -1193,25 +1210,22 @@ void WS2812FX::service() { if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(seg.cct, true), correctWB); for (uint8_t c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]); - // Effect blending (execute previous effect then new effect while in transition) - // If two effects are being blended (each may have different segment settings), those - // settings need to be saved first and then restored before running new mode. - // The seetings need to be applied from transition buffer for previous effect. + // Effect blending + // When two effects are being blended, each may have different segment data, this + // data needs to be saved first and then restored before running previous/transitional mode. // The blending will largely depend on the effect behaviour since actual output (LEDs) may be // overwritten by later effect. To enable seamless blending for every effect, additional LED buffer // would need to be allocated for each effect and then blended together for each pixel. - Segment::tmpsegd_t _tmpSegData; - seg.saveSegenv(&_tmpSegData); - uint8_t tmpMode = seg.currentMode(seg.mode); - if (seg.mode != tmpMode) seg.restoreSegenv(); // restore transition data - delay = (*_mode[tmpMode])(); // run old mode + uint8_t tmpMode = seg.currentMode(seg.mode); // this will return old mode while in transition + delay = (*_mode[seg.mode])(); // run new/current mode if (seg.mode != tmpMode) { - if (tmpMode != FX_MODE_HALLOWEEN_EYES) seg.call++; - seg.restoreSegenv(&_tmpSegData); // restore mode state + Segment::tmpsegd_t _tmpSegData; Segment::modeBlend(true); // set semaphore - uint16_t d2 = (*_mode[seg.mode])(); // run new mode - Segment::modeBlend(false); // unset semaphore + seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state) + uint16_t d2 = (*_mode[tmpMode])(); // run old mode + seg.restoreSegenv(_tmpSegData); // restore mode state (will also update transitional state) delay = MIN(delay,d2); // use shortest delay + Segment::modeBlend(false); // unset semaphore } if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++; if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index cd692f8f..13b7bd82 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -123,9 +123,6 @@ BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(bc.count); // only needs a third of "RGB" LEDs for NeoPixelBus _busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip, nr, _frequencykHz); _valid = (_busPtr != nullptr); - if (_valid) - for (int i=0; i<_skip; i++) - PolyBus::setPixelColor(_busPtr, _iType, i, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // clear sacrificial pixels DEBUG_PRINTF("%successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u)\n", _valid?"S":"Uns", nr, bc.count, bc.type, _pins[0], _pins[1], _iType); } @@ -151,6 +148,10 @@ void BusDigital::show() { else pix += _skip; PolyBus::setPixelColor(_busPtr, _iType, pix, c, co); } + #if !defined(STATUSLED) || STATUSLED>=0 + if (_skip) PolyBus::setPixelColor(_busPtr, _iType, 0, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black + #endif + for (int i=1; i<_skip; i++) PolyBus::setPixelColor(_busPtr, _iType, i, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black } PolyBus::show(_busPtr, _iType, !_buffering); // faster if buffer consistency is not important } @@ -190,8 +191,6 @@ void BusDigital::setBrightness(uint8_t b) { //TODO only show if no new show due in the next 50ms void BusDigital::setStatusPixel(uint32_t c) { if (_valid && _skip) { - for (int i=1; i<_skip; i++) - PolyBus::setPixelColor(_busPtr, _iType, i, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); PolyBus::setPixelColor(_busPtr, _iType, 0, c, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); if (canShow()) PolyBus::show(_busPtr, _iType); } From 800abc605f615d90d77f82b8b6a108e0b92748b9 Mon Sep 17 00:00:00 2001 From: Shlomo Zippel Date: Thu, 10 Aug 2023 12:52:29 -0700 Subject: [PATCH 17/41] Add send and receive groups to json api --- wled00/json.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wled00/json.cpp b/wled00/json.cpp index c0c72989..bf7ff102 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -347,7 +347,9 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId) JsonObject udpn = root["udpn"]; notifyDirect = udpn["send"] | notifyDirect; + syncGroups = udpn["sgrp"] | syncGroups; receiveNotifications = udpn["recv"] | receiveNotifications; + receiveGroups = udpn["rgrp"] | receiveGroups; if ((bool)udpn[F("nn")]) callMode = CALL_MODE_NO_NOTIFY; //send no notification just for this request unsigned long timein = root["time"] | UINT32_MAX; //backup time source if NTP not synced @@ -564,6 +566,8 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme JsonObject udpn = root.createNestedObject("udpn"); udpn["send"] = notifyDirect; udpn["recv"] = receiveNotifications; + udpn["sgrp"] = syncGroups; + udpn["rgrp"] = receiveGroups; root[F("lor")] = realtimeOverride; } From 4911a74cac910076d4749a5fca78f2753e73d33b Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Wed, 16 Aug 2023 21:02:00 +0200 Subject: [PATCH 18/41] Scrolling text enhancement. - breaking change - remove leading 0 checkmark - add reverse scroll checkmark - add vertical scroll if text fits into segment (intensity ==0 or ==255) - rotated characters - leading 0 check added to short texts (i.e. #DDMM0) Fixes #3322 --- wled00/FX.cpp | 96 ++++++++++++++++++++++++++++----------------- wled00/FX.h | 4 +- wled00/FX_2Dfcn.cpp | 16 +++++--- 3 files changed, 72 insertions(+), 44 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 14f5e6d1..10dd0fd2 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5786,8 +5786,8 @@ uint16_t mode_2Dscrollingtext(void) { const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - int letterWidth; - int letterHeight; + int letterWidth, rotLW; + int letterHeight, rotLH; switch (map(SEGMENT.custom2, 0, 255, 1, 5)) { default: case 1: letterWidth = 4; letterHeight = 6; break; @@ -5796,59 +5796,83 @@ uint16_t mode_2Dscrollingtext(void) { case 4: letterWidth = 7; letterHeight = 9; break; case 5: letterWidth = 5; letterHeight = 12; break; } - const bool zero = SEGMENT.check3; - const int yoffset = map(SEGMENT.intensity, 0, 255, -rows/2, rows/2) + (rows-letterHeight)/2; + // letters are rotated + if (((SEGMENT.custom3+1)>>3) % 2) { + rotLH = letterWidth; + rotLW = letterHeight; + } else { + rotLW = letterWidth; + rotLH = letterHeight; + } + char text[WLED_MAX_SEGNAME_LEN+1] = {'\0'}; if (SEGMENT.name) for (size_t i=0,j=0; i31 && SEGMENT.name[i]<128) text[j++] = SEGMENT.name[i]; + const bool zero = strchr(text, '0') != nullptr; + const int numberOfLetters = strlen(text); - if (!strlen(text) - || !strncmp_P(text,PSTR("#DATE"),5) - || !strncmp_P(text,PSTR("#DDMM"),5) - || !strncmp_P(text,PSTR("#MMDD"),5) - || !strncmp_P(text,PSTR("#TIME"),5) - || !strncmp_P(text,PSTR("#HHMM"),5)) { // fallback if empty segment name: display date and time - char sec[5]; - byte AmPmHour = hour(localTime); - boolean isitAM = true; - if (useAMPM) { - if (AmPmHour > 11) { AmPmHour -= 12; isitAM = false; } - if (AmPmHour == 0) { AmPmHour = 12; } - } - if (useAMPM) sprintf_P(sec, PSTR(" %2s"), (isitAM ? "AM" : "PM")); - else sprintf_P(sec, PSTR(":%02d"), second(localTime)); - if (!strncmp_P(text,PSTR("#DATE"),5)) sprintf_P(text, zero?PSTR("%02d.%02d.%04d"):PSTR("%d.%d.%d"), day(localTime), month(localTime), year(localTime)); - else if (!strncmp_P(text,PSTR("#DDMM"),5)) sprintf_P(text, zero?PSTR("%02d.%02d"):PSTR("%d.%d"), day(localTime), month(localTime)); - else if (!strncmp_P(text,PSTR("#MMDD"),5)) sprintf_P(text, zero?PSTR("%02d/%02d"):PSTR("%d/%d"), month(localTime), day(localTime)); - else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, zero?PSTR("%02d:%02d%s"):PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec); - else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, zero?PSTR("%02d:%02d"):PSTR("%d:%02d"), AmPmHour, minute(localTime)); - else sprintf_P(text, zero?PSTR("%s %02d, %04d %02d:%02d%s"):PSTR("%s %d, %d %d:%02d%s"), monthShortStr(month(localTime)), day(localTime), year(localTime), AmPmHour, minute(localTime), sec); + char sec[5]; + int AmPmHour = hour(localTime); + bool isitAM = true; + if (useAMPM) { + if (AmPmHour > 11) { AmPmHour -= 12; isitAM = false; } + if (AmPmHour == 0) { AmPmHour = 12; } + sprintf_P(sec, PSTR(" %2s"), (isitAM ? "AM" : "PM")); + } else { + sprintf_P(sec, PSTR(":%02d"), second(localTime)); + } + + if (!numberOfLetters) { // fallback if empty segment name: display date and time + sprintf_P(text, PSTR("%s %d, %d %d:%02d%s"), monthShortStr(month(localTime)), day(localTime), year(localTime), AmPmHour, minute(localTime), sec); + } else { + if (!strncmp_P(text,PSTR("#DATE"),5)) sprintf_P(text, zero?PSTR("%02d.%02d.%04d"):PSTR("%d.%d.%d"), day(localTime), month(localTime), year(localTime)); + else if (!strncmp_P(text,PSTR("#DDMM"),5)) sprintf_P(text, zero?PSTR("%02d.%02d") :PSTR("%d.%d"), day(localTime), month(localTime)); + else if (!strncmp_P(text,PSTR("#MMDD"),5)) sprintf_P(text, zero?PSTR("%02d/%02d") :PSTR("%d/%d"), month(localTime), day(localTime)); + else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, zero?PSTR("%02d:%02d%s") :PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec); + else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, zero?PSTR("%02d:%02d") :PSTR("%d:%02d"), AmPmHour, minute(localTime)); + } + + int width = (numberOfLetters * rotLW); + int yoffset = map(SEGMENT.intensity, 0, 255, -rows/2, rows/2) + (rows-rotLH)/2; + if (width <= cols) { + // scroll vertically (e.g. ^^ Way out ^^) if it fits + int speed = map(SEGMENT.speed, 0, 255, 5000, 1000); + int frac = millis()%speed + 1; + if (SEGMENT.intensity == 255) { + yoffset = (2 * frac * rows)/speed - rows; + } else if (SEGMENT.intensity == 0) { + yoffset = rows - (2 * frac * rows)/speed; + } } - const int numberOfLetters = strlen(text); if (SEGENV.step < millis()) { - if ((numberOfLetters * letterWidth) > cols) ++SEGENV.aux0 %= (numberOfLetters * letterWidth) + cols; // offset - else SEGENV.aux0 = (cols + (numberOfLetters * letterWidth))/2; + // calculate start offset + if (width > cols) { + if (SEGMENT.check3) { + if (SEGENV.aux0 == 0) SEGENV.aux0 = width + cols - 1; + else --SEGENV.aux0; + } else ++SEGENV.aux0 %= width + cols; + } else SEGENV.aux0 = (cols + width)/2; ++SEGENV.aux1 &= 0xFF; // color shift - SEGENV.step = millis() + map(SEGMENT.speed, 0, 255, 10*FRAMETIME_FIXED, 2*FRAMETIME_FIXED); - if (!SEGMENT.check2) { - for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++ ) - SEGMENT.blendPixelColorXY(x, y, SEGCOLOR(1), 255 - (SEGMENT.custom1>>1)); - } + SEGENV.step = millis() + map(SEGMENT.speed, 0, 255, 10*FRAMETIME_FIXED, 2*FRAMETIME_FIXED); // shift letters every 238ms or 46ms } + + if (!SEGMENT.check2) SEGMENT.fade_out(255 - (SEGMENT.custom1>>4)); // trail + for (int i = 0; i < numberOfLetters; i++) { - if (int(cols) - int(SEGENV.aux0) + letterWidth*(i+1) < 0) continue; // don't draw characters off-screen + int xoffset = int(cols) - int(SEGENV.aux0) + rotLW*i; + if (xoffset + rotLW < 0) continue; // don't draw characters off-screen uint32_t col1 = SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0); uint32_t col2 = BLACK; if (SEGMENT.check1 && SEGMENT.palette == 0) { col1 = SEGCOLOR(0); col2 = SEGCOLOR(2); } - SEGMENT.drawCharacter(text[i], int(cols) - int(SEGENV.aux0) + letterWidth*i, yoffset, letterWidth, letterHeight, col1, col2); + SEGMENT.drawCharacter(text[i], xoffset, yoffset, letterWidth, letterHeight, col1, col2, (SEGMENT.custom3+1)>>3); } return FRAMETIME; } -static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,,Gradient,Overlay,0;!,!,Gradient;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0"; +static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,Rotate letters,Gradient,Overlay,Reverse dir.;!,!,Gradient;!;2;ix=128,c1=0,c3=0,rev=0,mi=0,rY=0,mY=0"; //////////////////////////// diff --git a/wled00/FX.h b/wled00/FX.h index 56323f15..e4272fe1 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -615,9 +615,9 @@ typedef struct Segment { void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c); void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c); void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0)); } // automatic inline - void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0); + void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0, uint8_t rotate = 0); void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0)); } // automatic inline - void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0)); } // automatic inline + void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, uint8_t rotate = 0) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0), rotate); } // automatic inline void wu_pixel(uint32_t x, uint32_t y, CRGB c); void blur1d(fract8 blur_amount); // blur all rows in 1 dimension void blur2d(fract8 blur_amount) { blur(blur_amount); } diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 0dfeb360..9043646f 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -569,7 +569,7 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3 // draws a raster font character on canvas // only supports: 4x6=24, 5x8=40, 5x12=60, 6x8=48 and 7x9=63 fonts ATM -void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2) { +void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2, uint8_t rotate) { if (!isActive()) return; // not active if (chr < 32 || chr > 126) return; // only ASCII 32-126 supported chr -= 32; // align with font table entries @@ -582,9 +582,6 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, //if (w<5 || w>6 || h!=8) return; for (int i = 0; i= rows) break; // drawing off-screen uint8_t bits = 0; switch (font) { case 24: bits = pgm_read_byte_near(&console_font_4x6[(chr * h) + i]); break; // 5x8 font @@ -596,8 +593,15 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, } col = ColorFromPalette(grad, (i+1)*255/h, 255, NOBLEND); for (int j = 0; j= 0 || x0 < cols) && ((bits>>(j+(8-w))) & 0x01)) { // bit set & drawing on-screen + int x0, y0; + switch (rotate) { + case 3: x0 = x + (h-1) - i; y0 = y + (w-1) - j; break; + case 2: x0 = x + j; y0 = y + (h-1) - i; break; + case 1: x0 = x + i; y0 = y + j; break; + default: x0 = x + (w-1) - j; y0 = y + i; break; + } + if (x0 < 0 || x0 >= cols || y0 < 0 || y0 >= rows) continue; // drawing off-screen + if (((bits>>(j+(8-w))) & 0x01)) { // bit set setPixelColorXY(x0, y0, col); } } From bf452e922a92efc08c688da3d5af10c832f36828 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Wed, 16 Aug 2023 22:11:16 +0200 Subject: [PATCH 19/41] Playlist saving bugfix. Fixes #3324 --- wled00/playlist.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wled00/playlist.cpp b/wled00/playlist.cpp index d74ac5a3..8a6227e1 100644 --- a/wled00/playlist.cpp +++ b/wled00/playlist.cpp @@ -112,7 +112,7 @@ int16_t loadPlaylist(JsonObject playlistObj, byte presetId) { if (playlistEndPreset == 255 && currentPreset > 0) playlistEndPreset = currentPreset; if (playlistEndPreset > 250) playlistEndPreset = 0; shuffle = shuffle || playlistObj["r"]; - if (shuffle) playlistOptions += PL_OPTION_SHUFFLE; + if (shuffle) playlistOptions |= PL_OPTION_SHUFFLE; currentPlaylist = presetId; DEBUG_PRINTLN(F("Playlist loaded.")); @@ -156,7 +156,7 @@ void serializePlaylist(JsonObject sObj) { JsonArray ps = playlist.createNestedArray("ps"); JsonArray dur = playlist.createNestedArray("dur"); JsonArray transition = playlist.createNestedArray(F("transition")); - playlist[F("repeat")] = (playlistIndex < 0) ? playlistRepeat - 1 : playlistRepeat; // remove added repetition count (if not yet running) + playlist[F("repeat")] = (playlistIndex < 0 && playlistRepeat > 0) ? playlistRepeat - 1 : playlistRepeat; // remove added repetition count (if not yet running) playlist["end"] = playlistEndPreset; playlist["r"] = playlistOptions & PL_OPTION_SHUFFLE; for (int i=0; i Date: Thu, 17 Aug 2023 16:24:16 +0200 Subject: [PATCH 20/41] Too long metadata string bugfix. --- wled00/json.cpp | 13 ++++++++----- wled00/util.cpp | 9 +++++---- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/wled00/json.cpp b/wled00/json.cpp index 5fa4c04f..6ac19189 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -974,9 +974,10 @@ void serializeNodes(JsonObject root) // deserializes mode data string into JsonArray void serializeModeData(JsonArray fxdata) { - char lineBuffer[128]; + char lineBuffer[256]; for (size_t i = 0; i < strip.getModeCount(); i++) { - strncpy_P(lineBuffer, strip.getModeData(i), 127); + strncpy_P(lineBuffer, strip.getModeData(i), 255); + lineBuffer[255] = '\0'; // terminate string if (lineBuffer[0] != 0) { char* dataPtr = strchr(lineBuffer,'@'); if (dataPtr) fxdata.add(dataPtr+1); @@ -987,10 +988,12 @@ void serializeModeData(JsonArray fxdata) // deserializes mode names string into JsonArray // also removes effect data extensions (@...) from deserialised names -void serializeModeNames(JsonArray arr) { - char lineBuffer[128]; +void serializeModeNames(JsonArray arr) +{ + char lineBuffer[256]; for (size_t i = 0; i < strip.getModeCount(); i++) { - strncpy_P(lineBuffer, strip.getModeData(i), 127); + strncpy_P(lineBuffer, strip.getModeData(i), 255); + lineBuffer[255] = '\0'; // terminate string if (lineBuffer[0] != 0) { char* dataPtr = strchr(lineBuffer,'@'); if (dataPtr) *dataPtr = 0; // terminate mode data after name diff --git a/wled00/util.cpp b/wled00/util.cpp index b8dea255..220b4773 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -233,7 +233,8 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe if (mode < strip.getModeCount()) { char lineBuffer[256]; //strcpy_P(lineBuffer, (const char*)pgm_read_dword(&(WS2812FX::_modeData[mode]))); - strcpy_P(lineBuffer, strip.getModeData(mode)); + strncpy_P(lineBuffer, strip.getModeData(mode), 255); + lineBuffer[255] = '\0'; // terminate string size_t len = strlen(lineBuffer); size_t j = 0; for (; j < maxLen && j < len; j++) { @@ -355,9 +356,9 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL int16_t extractModeDefaults(uint8_t mode, const char *segVar) { if (mode < strip.getModeCount()) { - char lineBuffer[128] = ""; - strncpy_P(lineBuffer, strip.getModeData(mode), 127); - lineBuffer[127] = '\0'; // terminate string + char lineBuffer[256]; + strncpy_P(lineBuffer, strip.getModeData(mode), 255); + lineBuffer[255] = '\0'; // terminate string if (lineBuffer[0] != 0) { char* startPtr = strrchr(lineBuffer, ';'); // last ";" in FX data if (!startPtr) return -1; From dfc33389d843f702241d454c7bb2fdefed6e3c28 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Thu, 17 Aug 2023 16:46:31 +0200 Subject: [PATCH 21/41] Optimisation. --- wled00/FX.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 10dd0fd2..160e24a2 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5831,12 +5831,13 @@ uint16_t mode_2Dscrollingtext(void) { else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, zero?PSTR("%02d:%02d") :PSTR("%d:%02d"), AmPmHour, minute(localTime)); } + const unsigned long now = millis(); // reduce millis() calls int width = (numberOfLetters * rotLW); int yoffset = map(SEGMENT.intensity, 0, 255, -rows/2, rows/2) + (rows-rotLH)/2; if (width <= cols) { // scroll vertically (e.g. ^^ Way out ^^) if it fits int speed = map(SEGMENT.speed, 0, 255, 5000, 1000); - int frac = millis()%speed + 1; + int frac = now % speed + 1; if (SEGMENT.intensity == 255) { yoffset = (2 * frac * rows)/speed - rows; } else if (SEGMENT.intensity == 0) { @@ -5844,7 +5845,7 @@ uint16_t mode_2Dscrollingtext(void) { } } - if (SEGENV.step < millis()) { + if (SEGENV.step < now) { // calculate start offset if (width > cols) { if (SEGMENT.check3) { @@ -5853,7 +5854,7 @@ uint16_t mode_2Dscrollingtext(void) { } else ++SEGENV.aux0 %= width + cols; } else SEGENV.aux0 = (cols + width)/2; ++SEGENV.aux1 &= 0xFF; // color shift - SEGENV.step = millis() + map(SEGMENT.speed, 0, 255, 10*FRAMETIME_FIXED, 2*FRAMETIME_FIXED); // shift letters every 238ms or 46ms + SEGENV.step = now + map(SEGMENT.speed, 0, 255, 250, 50); // shift letters every ~250ms to ~50ms } if (!SEGMENT.check2) SEGMENT.fade_out(255 - (SEGMENT.custom1>>4)); // trail @@ -5872,7 +5873,7 @@ uint16_t mode_2Dscrollingtext(void) { return FRAMETIME; } -static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,Rotate letters,Gradient,Overlay,Reverse dir.;!,!,Gradient;!;2;ix=128,c1=0,c3=0,rev=0,mi=0,rY=0,mY=0"; +static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,Rotate,Gradient,Overlay,Reverse;!,!,Gradient;!;2;ix=128,c1=0,c3=0,rev=0,mi=0,rY=0,mY=0"; //////////////////////////// From 06402cf1c6d4778f5bba50e2404344c40584ec3e Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Thu, 17 Aug 2023 17:10:29 +0200 Subject: [PATCH 22/41] Making buffer fix easy to maintain and portable. Thanks to @softhack007 --- wled00/json.cpp | 8 ++++---- wled00/util.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/wled00/json.cpp b/wled00/json.cpp index 6fc0e979..c85324d7 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -980,8 +980,8 @@ void serializeModeData(JsonArray fxdata) { char lineBuffer[256]; for (size_t i = 0; i < strip.getModeCount(); i++) { - strncpy_P(lineBuffer, strip.getModeData(i), 255); - lineBuffer[255] = '\0'; // terminate string + strncpy_P(lineBuffer, strip.getModeData(i), sizeof(lineBuffer)/sizeof(char)-1); + lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string if (lineBuffer[0] != 0) { char* dataPtr = strchr(lineBuffer,'@'); if (dataPtr) fxdata.add(dataPtr+1); @@ -996,8 +996,8 @@ void serializeModeNames(JsonArray arr) { char lineBuffer[256]; for (size_t i = 0; i < strip.getModeCount(); i++) { - strncpy_P(lineBuffer, strip.getModeData(i), 255); - lineBuffer[255] = '\0'; // terminate string + strncpy_P(lineBuffer, strip.getModeData(i), sizeof(lineBuffer)/sizeof(char)-1); + lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string if (lineBuffer[0] != 0) { char* dataPtr = strchr(lineBuffer,'@'); if (dataPtr) *dataPtr = 0; // terminate mode data after name diff --git a/wled00/util.cpp b/wled00/util.cpp index 220b4773..3c58bf9c 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -233,8 +233,8 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe if (mode < strip.getModeCount()) { char lineBuffer[256]; //strcpy_P(lineBuffer, (const char*)pgm_read_dword(&(WS2812FX::_modeData[mode]))); - strncpy_P(lineBuffer, strip.getModeData(mode), 255); - lineBuffer[255] = '\0'; // terminate string + strncpy_P(lineBuffer, strip.getModeData(mode), sizeof(lineBuffer)/sizeof(char)-1); + lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string size_t len = strlen(lineBuffer); size_t j = 0; for (; j < maxLen && j < len; j++) { @@ -357,8 +357,8 @@ int16_t extractModeDefaults(uint8_t mode, const char *segVar) { if (mode < strip.getModeCount()) { char lineBuffer[256]; - strncpy_P(lineBuffer, strip.getModeData(mode), 255); - lineBuffer[255] = '\0'; // terminate string + strncpy_P(lineBuffer, strip.getModeData(mode), sizeof(lineBuffer)/sizeof(char)-1); + lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string if (lineBuffer[0] != 0) { char* startPtr = strrchr(lineBuffer, ';'); // last ";" in FX data if (!startPtr) return -1; From ba1b6f321ede6b578ebc86c6ff8ee9032b1a2b32 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Wed, 23 Aug 2023 14:50:46 +0200 Subject: [PATCH 23/41] Button bugfix. --- .../usermod_v2_four_line_display_ALT.h | 11 ++++++----- wled00/button.cpp | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h b/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h index 5a99c3cd..48dab8f0 100644 --- a/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h +++ b/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h @@ -1081,7 +1081,7 @@ bool FourLineDisplayUsermod::handleButton(uint8_t b) { static bool buttonLongPressed = false; static unsigned long buttonPressedTime = 0; static unsigned long buttonWaitTime = 0; - bool handled = true; + bool handled = false; //momentary button logic if (isButtonPressed(b)) { //pressed @@ -1090,11 +1090,12 @@ bool FourLineDisplayUsermod::handleButton(uint8_t b) { buttonPressedBefore = true; if (now - buttonPressedTime > 600) { //long press - buttonLongPressed = true; //TODO: handleButton() handles button 0 without preset in a different way for double click //so we need to override with same behaviour - longPressAction(0); - //handled = false; + //DEBUG_PRINTLN(F("4LD action.")); + //if (!buttonLongPressed) longPressAction(0); + buttonLongPressed = true; + return false; } } else if (!isButtonPressed(b) && buttonPressedBefore) { //released @@ -1126,7 +1127,7 @@ bool FourLineDisplayUsermod::handleButton(uint8_t b) { buttonWaitTime = 0; //TODO: handleButton() handles button 0 without preset in a different way for double click //so we need to override with same behaviour - shortPressAction(0); + //shortPressAction(0); //handled = false; } return handled; diff --git a/wled00/button.cpp b/wled00/button.cpp index d45274a6..4d43fb5b 100644 --- a/wled00/button.cpp +++ b/wled00/button.cpp @@ -262,7 +262,7 @@ void handleButton() shortPressAction(b); buttonPressedBefore[b] = true; buttonPressedTime[b] = now; // continually update (for debouncing to work in release handler) - return; + continue; } if (!buttonPressedBefore[b]) buttonPressedTime[b] = now; @@ -283,7 +283,7 @@ void handleButton() // released after rising-edge short press action if (macroButton[b] && macroButton[b] == macroLongPress[b] && macroButton[b] == macroDoublePress[b]) { if (dur > WLED_DEBOUNCE_THRESHOLD) buttonPressedBefore[b] = false; // debounce, blocks button for 50 ms once it has been released - return; + continue; } if (dur < WLED_DEBOUNCE_THRESHOLD) {buttonPressedBefore[b] = false; continue;} // too short "press", debounce From fc1dd2daac2890e13a1dda4958a6bead38927b45 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Fri, 25 Aug 2023 18:34:11 +0200 Subject: [PATCH 24/41] Fix for #3346 --- wled00/bus_manager.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 7ea44b15..e526ea67 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -144,8 +144,8 @@ void BusDigital::show() { c = RGBW32(_data[offset],_data[offset+1],_data[offset+2],(Bus::hasWhite(_type)?_data[offset+3]:0)); } uint16_t pix = i; - if (_reversed) pix = _len - pix -1; - else pix += _skip; + if (_reversed) pix = _len - pix -1; + pix += _skip; PolyBus::setPixelColor(_busPtr, _iType, pix, c, co); } } @@ -206,8 +206,8 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { } if (Bus::hasWhite(_type)) _data[offset] = W(c); } else { - if (_reversed) pix = _len - pix -1; - else pix += _skip; + if (_reversed) pix = _len - pix -1; + pix += _skip; uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder); if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs uint16_t pOld = pix; @@ -237,8 +237,8 @@ uint32_t BusDigital::getPixelColor(uint16_t pix) { } return c; } else { - if (_reversed) pix = _len - pix -1; - else pix += _skip; + if (_reversed) pix = _len - pix -1; + pix += _skip; uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder); uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, (_type==TYPE_WS2812_1CH_X3) ? IC_INDEX_WS2812_1CH_3X(pix) : pix, co),_bri); if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs From 246f0b21fd10b7912623fdc9048433df4f2bcb89 Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Sat, 26 Aug 2023 22:19:52 -0500 Subject: [PATCH 25/41] staircase toggle power on/off option bool --- .../Animated_Staircase/Animated_Staircase.h | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/usermods/Animated_Staircase/Animated_Staircase.h b/usermods/Animated_Staircase/Animated_Staircase.h index 151cf1d4..6d02dce7 100644 --- a/usermods/Animated_Staircase/Animated_Staircase.h +++ b/usermods/Animated_Staircase/Animated_Staircase.h @@ -25,6 +25,7 @@ class Animated_Staircase : public Usermod { bool useUSSensorBottom = false; // using PIR or UltraSound sensor? unsigned int topMaxDist = 50; // default maximum measured distance in cm, top unsigned int bottomMaxDist = 50; // default maximum measured distance in cm, bottom + bool togglePower = false; // toggle power on/off with staircase on/off /* runtime variables */ bool initDone = false; @@ -90,7 +91,8 @@ class Animated_Staircase : public Usermod { static const char _bottomEcho_pin[]; static const char _topEchoCm[]; static const char _bottomEchoCm[]; - + static const char _togglePower[]; + void publishMqtt(bool bottom, const char* state) { #ifndef WLED_DISABLE_MQTT //Check if MQTT Connected, otherwise it will crash the 8266 @@ -196,6 +198,7 @@ class Animated_Staircase : public Usermod { if (on) { lastSensor = topSensorRead; } else { + if (togglePower && onIndex == offIndex && offMode) toggleOnOff(); // toggle power on if off // If the bottom sensor triggered, we need to swipe up, ON swipe = bottomSensorRead; @@ -249,7 +252,10 @@ class Animated_Staircase : public Usermod { offIndex = MAX(onIndex, offIndex - 1); } } - if (oldOn != onIndex || oldOff != offIndex) updateSegments(); // reduce the number of updates to necessary ones + if (oldOn != onIndex || oldOff != offIndex) { + updateSegments(); // reduce the number of updates to necessary ones + if (togglePower && onIndex == offIndex && !offMode && !on) toggleOnOff(); // toggle power off for all segments off + } } } @@ -295,6 +301,7 @@ class Animated_Staircase : public Usermod { strip.setTransition(segment_delay_ms/100); strip.trigger(); } else { + if (togglePower && !on && offMode) toggleOnOff(); // toggle power on if off // Restore segment options for (int i = 0; i <= strip.getLastActiveSegmentId(); i++) { Segment &seg = strip.getSegment(i); @@ -444,6 +451,7 @@ class Animated_Staircase : public Usermod { staircase[FPSTR(_bottomEcho_pin)] = useUSSensorBottom ? bottomEchoPin : -1; staircase[FPSTR(_topEchoCm)] = topMaxDist; staircase[FPSTR(_bottomEchoCm)] = bottomMaxDist; + staircase[FPSTR(_togglePower)] = togglePower; DEBUG_PRINTLN(F("Staircase config saved.")); } @@ -488,6 +496,8 @@ class Animated_Staircase : public Usermod { bottomMaxDist = top[FPSTR(_bottomEchoCm)] | bottomMaxDist; bottomMaxDist = min(150,max(30,(int)bottomMaxDist)); // max distance ~1.5m (a lag of 9ms may be expected) + togglePower = top[FPSTR(_togglePower)] | togglePower; // staircase toggles power on/off + DEBUG_PRINT(FPSTR(_name)); if (!initDone) { // first run: reading from cfg.json @@ -511,7 +521,7 @@ class Animated_Staircase : public Usermod { if (changed) setup(); } // use "return !top["newestParameter"].isNull();" when updating Usermod with new features - return true; + return !top[FPSTR(_togglePower)].isNull(); } /* @@ -551,3 +561,4 @@ const char Animated_Staircase::_bottomPIRorTrigger_pin[] PROGMEM = "bottomPIR const char Animated_Staircase::_bottomEcho_pin[] PROGMEM = "bottomEcho_pin"; const char Animated_Staircase::_topEchoCm[] PROGMEM = "top-dist-cm"; const char Animated_Staircase::_bottomEchoCm[] PROGMEM = "bottom-dist-cm"; +const char Animated_Staircase::_togglePower[] PROGMEM = "toggle-on-off"; From bb45bee7f870989bc7688f4ef4f82f7bb5581c0e Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sun, 27 Aug 2023 23:01:55 +0200 Subject: [PATCH 26/41] oappend() debug message when buffer is full oappend() silently discards strings when the buffer is full, leading to strange effects like half-working UI pages. The new debug message will help developers to understand what could be wrong. --- wled00/util.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/wled00/util.cpp b/wled00/util.cpp index b8dea255..57f221d4 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -148,8 +148,14 @@ bool oappendi(int i) bool oappend(const char* txt) { uint16_t len = strlen(txt); - if (olen + len >= SETTINGS_STACK_BUF_SIZE) + if (olen + len >= SETTINGS_STACK_BUF_SIZE) { +#ifdef WLED_DEBUG + DEBUG_PRINT(F("oappend() buffer overflow. Cannnot append ")); + DEBUG_PRINT(len); DEBUG_PRINT(F(" bytes \t\"")); + DEBUG_PRINT(txt); DEBUG_PRINTLN(F("\"")); +#endif return false; // buffer full + } strcpy(obuf + olen, txt); olen += len; return true; From d1b00ba95d9aae34ffc82dd8d0d03b5978d65444 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Mon, 28 Aug 2023 17:58:30 +0200 Subject: [PATCH 27/41] Bugfix. - feed WDT even if strip is updating - provide custom palette names - handle interface cooldown properly - rotary encoder ALT fix for custom palettes --- .../usermod_v2_rotary_encoder_ui_ALT.h | 32 +++++++++++++------ wled00/led.cpp | 4 ++- wled00/util.cpp | 8 ++++- wled00/wled.cpp | 5 ++- 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h b/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h index b142f903..858e1654 100644 --- a/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h +++ b/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h @@ -398,8 +398,14 @@ void RotaryEncoderUIUsermod::sortModesAndPalettes() { modes_alpha_indexes = re_initIndexArray(strip.getModeCount()); re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT); - palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount()); - palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount()); // only use internal palettes + palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount()+strip.customPalettes.size()); + palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount()+strip.customPalettes.size()); + if (strip.customPalettes.size()) { + for (int i=0; i=0) pinMode(pinC, USERMOD_ROTARY_ENCODER_GPIO); } loopTime = millis(); @@ -682,21 +688,25 @@ void RotaryEncoderUIUsermod::displayNetworkInfo() { void RotaryEncoderUIUsermod::findCurrentEffectAndPalette() { DEBUG_PRINTLN(F("Finding current mode and palette.")); currentEffectAndPaletteInitialized = true; - for (uint8_t i = 0; i < strip.getModeCount(); i++) { + + effectCurrentIndex = 0; + for (int i = 0; i < strip.getModeCount(); i++) { if (modes_alpha_indexes[i] == effectCurrent) { effectCurrentIndex = i; + DEBUG_PRINTLN(F("Found current mode.")); break; } } - DEBUG_PRINTLN(F("Found current mode.")); - for (uint8_t i = 0; i < strip.getPaletteCount(); i++) { + effectPaletteIndex = 0; + DEBUG_PRINTLN(effectPalette); + for (uint8_t i = 0; i < strip.getPaletteCount()+strip.customPalettes.size(); i++) { if (palettes_alpha_indexes[i] == effectPalette) { effectPaletteIndex = i; + DEBUG_PRINTLN(F("Found palette.")); break; } } - DEBUG_PRINTLN(F("Found palette.")); } bool RotaryEncoderUIUsermod::changeState(const char *stateName, byte markedLine, byte markedCol, byte glyph) { @@ -731,7 +741,9 @@ void RotaryEncoderUIUsermod::changeBrightness(bool increase) { } display->updateRedrawTime(); #endif - bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0); + //bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0); + if (bri < 40) bri = max(min((increase ? bri+fadeAmount/2 : bri-fadeAmount/2), 255), 0); // slower steps when brightness < 16% + else bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0); lampUdated(); #ifdef USERMOD_FOUR_LINE_DISPLAY display->updateBrightness(); @@ -878,7 +890,7 @@ void RotaryEncoderUIUsermod::changePalette(bool increase) { } display->updateRedrawTime(); #endif - effectPaletteIndex = max(min((increase ? effectPaletteIndex+1 : effectPaletteIndex-1), strip.getPaletteCount()-1), 0); + effectPaletteIndex = max(min((unsigned)(increase ? effectPaletteIndex+1 : effectPaletteIndex-1), strip.getPaletteCount()+strip.customPalettes.size()-1), 0U); effectPalette = palettes_alpha_indexes[effectPaletteIndex]; stateChanged = true; if (applyToAll) { diff --git a/wled00/led.cpp b/wled00/led.cpp index 97499e76..a70a0aef 100644 --- a/wled00/led.cpp +++ b/wled00/led.cpp @@ -161,6 +161,8 @@ void stateUpdated(byte callMode) { void updateInterfaces(uint8_t callMode) { + if (!interfaceUpdateCallMode || millis() - lastInterfaceUpdate < INTERFACE_UPDATE_COOLDOWN) return; + sendDataWs(); lastInterfaceUpdate = millis(); if (callMode == CALL_MODE_WS_SEND) return; @@ -179,7 +181,7 @@ void updateInterfaces(uint8_t callMode) void handleTransitions() { //handle still pending interface update - if (interfaceUpdateCallMode && millis() - lastInterfaceUpdate > INTERFACE_UPDATE_COOLDOWN) updateInterfaces(interfaceUpdateCallMode); + updateInterfaces(interfaceUpdateCallMode); #ifndef WLED_DISABLE_MQTT if (doPublishMqtt) publishMqtt(); #endif diff --git a/wled00/util.cpp b/wled00/util.cpp index 57f221d4..95fbfd30 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -251,6 +251,12 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe } else return 0; } + if (src == JSON_palette_names && mode > GRADIENT_PALETTE_COUNT) { + snprintf_P(dest, maxLen, PSTR("~ Custom %d~"), 255-mode); + dest[maxLen-1] = '\0'; + return strlen(dest); + } + uint8_t qComma = 0; bool insideQuotes = false; uint8_t printedChars = 0; @@ -565,4 +571,4 @@ void enumerateLedmaps() { } } -} \ No newline at end of file +} diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 1c1ee976..a527f296 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -195,12 +195,15 @@ void WLED::loop() #if WLED_WATCHDOG_TIMEOUT > 0 // we finished our mainloop, reset the watchdog timer - if (!strip.isUpdating()) + static unsigned long lastWDTFeed = 0; + if (!strip.isUpdating() || millis() - lastWDTFeed > (WLED_WATCHDOG_TIMEOUT*500)) { #ifdef ARDUINO_ARCH_ESP32 esp_task_wdt_reset(); #else ESP.wdtFeed(); #endif + lastWDTFeed = millis(); + } #endif if (doReboot && (!doInitBusses || !doSerializeConfig)) // if busses have to be inited & saved, wait until next iteration From a79c9d5f4f898f239d73f411a36ca3c4bbf304c7 Mon Sep 17 00:00:00 2001 From: Sandro Pischinger Date: Tue, 29 Aug 2023 23:44:08 +0200 Subject: [PATCH 28/41] info page: add time --- wled00/data/index.js | 1 + wled00/data/simple.js | 1 + wled00/json.cpp | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/wled00/data/index.js b/wled00/data/index.js index 145ed2f2..5ddcccc8 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -683,6 +683,7 @@ ${i.opt&0x100?inforow("Debug","