From fa6070c6804d9988abb689b67dc9c8cdf74976db Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Wed, 12 Jul 2023 20:52:34 +0200 Subject: [PATCH] Multiple updates: - additional debug timings - removed local leds[] buffer - async segment bounds change (crashes seen otherwise) - added isActive() check to Segment drawing methods - ABL simplification - palette option for Black hole (FX) - (possible) crash mitigation is Segment handling (rapid preset changes) --- wled00/FX.cpp | 51 ++-------- wled00/FX.h | 36 +++---- wled00/FX_2Dfcn.cpp | 30 ++++-- wled00/FX_fcn.cpp | 217 ++++++++++++++++++++++------------------- wled00/bus_manager.cpp | 13 --- wled00/json.cpp | 11 ++- wled00/wled.cpp | 66 ++++++++----- wled00/wled.h | 4 +- 8 files changed, 210 insertions(+), 218 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index c0b39c18..7bcd9395 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -606,7 +606,6 @@ static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!,!;!,!;!;;m12=0"; 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.setUpLeds(); //lossless getPixelColor() SEGMENT.fill(SEGCOLOR(1)); } @@ -1205,7 +1204,6 @@ uint16_t mode_fireworks() { const uint16_t height = SEGMENT.virtualHeight(); if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); //lossless getPixelColor() SEGMENT.fill(SEGCOLOR(1)); SEGENV.aux0 = UINT16_MAX; SEGENV.aux1 = UINT16_MAX; @@ -1904,7 +1902,6 @@ static const char _data_FX_MODE_PRIDE_2015[] PROGMEM = "Pride 2015@!;;"; uint16_t mode_juggle(void) { if (SEGLEN == 1) return mode_static(); if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); //lossless getPixelColor() SEGMENT.fill(BLACK); } @@ -4585,7 +4582,6 @@ uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulma // initialize on first call if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -4595,22 +4591,22 @@ uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulma for (size_t i = 0; i < 8; i++) { x = beatsin8(SEGMENT.custom1>>3, 0, cols - 1, 0, ((i % 2) ? 128 : 0) + t * i); y = beatsin8(SEGMENT.intensity>>3, 0, rows - 1, 0, ((i % 2) ? 192 : 64) + t * i); - SEGMENT.addPixelColorXY(x, y, CHSV(i*32, 255, 255)); + SEGMENT.addPixelColorXY(x, y, SEGMENT.color_from_palette(i*32, false, PALETTE_SOLID_WRAP, SEGMENT.check1?0:255)); } // inner stars for (size_t i = 0; i < 4; i++) { x = beatsin8(SEGMENT.custom2>>3, cols/4, cols - 1 - cols/4, 0, ((i % 2) ? 128 : 0) + t * i); y = beatsin8(SEGMENT.custom3 , rows/4, rows - 1 - rows/4, 0, ((i % 2) ? 192 : 64) + t * i); - SEGMENT.addPixelColorXY(x, y, CHSV(i*32, 255, 255)); + SEGMENT.addPixelColorXY(x, y, SEGMENT.color_from_palette(255-i*64, false, PALETTE_SOLID_WRAP, SEGMENT.check1?0:255)); } // central white dot - SEGMENT.setPixelColorXY(cols/2, rows/2, CHSV(0, 0, 255)); + SEGMENT.setPixelColorXY(cols/2, rows/2, WHITE); // blur everything a bit SEGMENT.blur(16); return FRAMETIME; } // mode_2DBlackHole() -static const char _data_FX_MODE_2DBLACKHOLE[] PROGMEM = "Black Hole@Fade rate,Outer Y freq.,Outer X freq.,Inner X freq.,Inner Y freq.;;;2"; +static const char _data_FX_MODE_2DBLACKHOLE[] PROGMEM = "Black Hole@Fade rate,Outer Y freq.,Outer X freq.,Inner X freq.,Inner Y freq.,Solid;!;!;2;pal=11"; //////////////////////////// @@ -4623,7 +4619,6 @@ uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.so const uint16_t rows = SEGMENT.virtualHeight(); if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); SEGENV.aux0 = 0; // start with red hue } @@ -4677,7 +4672,6 @@ uint16_t mode_2Ddna(void) { // dna originally by by ldirko at https://pa const uint16_t rows = SEGMENT.virtualHeight(); if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -4704,7 +4698,6 @@ uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulma const uint16_t rows = SEGMENT.virtualHeight(); if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -4750,7 +4743,6 @@ uint16_t mode_2DDrift() { // By: Stepko https://editor.soulmateli const uint16_t rows = SEGMENT.virtualHeight(); if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -4781,7 +4773,6 @@ uint16_t mode_2Dfirenoise(void) { // firenoise2d. By Andrew Tuline const uint16_t rows = SEGMENT.virtualHeight(); if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -4816,7 +4807,6 @@ uint16_t mode_2DFrizzles(void) { // By: Stepko https://editor.so const uint16_t rows = SEGMENT.virtualHeight(); if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -4855,8 +4845,6 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: CRGB backgroundColor = SEGCOLOR(1); - if (SEGENV.call == 0) SEGMENT.setUpLeds(); - if (SEGENV.call == 0 || strip.now - SEGMENT.step > 3000) { SEGENV.step = strip.now; SEGENV.aux0 = 0; @@ -5122,7 +5110,6 @@ uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams. const uint16_t rows = SEGMENT.virtualHeight(); if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -5267,7 +5254,6 @@ uint16_t mode_2DPlasmaball(void) { // By: Stepko https://edito const uint16_t rows = SEGMENT.virtualHeight(); if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -5315,7 +5301,6 @@ uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https CRGBPalette16 auroraPalette = {0x000000, 0x003300, 0x006600, 0x009900, 0x00cc00, 0x00ff00, 0x33ff00, 0x66ff00, 0x99ff00, 0xccff00, 0xffff00, 0xffcc00, 0xff9900, 0xff6600, 0xff3300, 0xff0000}; if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); SEGENV.step = 0; } @@ -5365,7 +5350,6 @@ uint16_t mode_2DPulser(void) { // By: ldirko https://edi const uint16_t rows = SEGMENT.virtualHeight(); if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -5393,7 +5377,6 @@ uint16_t mode_2DSindots(void) { // By: ldirko http const uint16_t rows = SEGMENT.virtualHeight(); if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -5425,7 +5408,6 @@ uint16_t mode_2Dsquaredswirl(void) { // By: Mark Kriegsman. https://g const uint16_t rows = SEGMENT.virtualHeight(); if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -5468,7 +5450,6 @@ uint16_t mode_2DSunradiation(void) { // By: ldirko https://edi byte *bump = reinterpret_cast(SEGENV.data); if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -5516,7 +5497,6 @@ uint16_t mode_2Dtartan(void) { // By: Elliott Kember https://editor.so const uint16_t rows = SEGMENT.virtualHeight(); if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -5556,7 +5536,6 @@ uint16_t mode_2Dspaceships(void) { //// Space ships by stepko (c)05.02.21 [ht const uint16_t rows = SEGMENT.virtualHeight(); if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -5625,7 +5604,6 @@ uint16_t mode_2Dcrazybees(void) { bee_t *bee = reinterpret_cast(SEGENV.data); if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); for (size_t i = 0; i < n; i++) { bee[i].posX = random8(0, cols); @@ -5695,7 +5673,6 @@ uint16_t mode_2Dghostrider(void) { const size_t maxLighters = min(cols + rows, LIGHTERS_AM); - if (SEGENV.call == 0) SEGMENT.setUpLeds(); if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) { SEGENV.aux0 = cols; SEGENV.aux1 = rows; @@ -5780,7 +5757,6 @@ uint16_t mode_2Dfloatingblobs(void) { if (!SEGENV.allocateData(sizeof(blob_t))) return mode_static(); //allocation failed blob_t *blob = reinterpret_cast(SEGENV.data); - if (SEGENV.call == 0) SEGMENT.setUpLeds(); if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) { SEGENV.aux0 = cols; // re-initialise if virtual size changes SEGENV.aux1 = rows; @@ -5946,7 +5922,6 @@ uint16_t mode_2Ddriftrose(void) { const float L = min(cols, rows) / 2.f; if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -6101,7 +6076,6 @@ uint16_t mode_2DSwirl(void) { const uint16_t rows = SEGMENT.virtualHeight(); if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -6148,7 +6122,6 @@ uint16_t mode_2DWaverly(void) { const uint16_t rows = SEGMENT.virtualHeight(); if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -6377,7 +6350,6 @@ uint16_t mode_matripix(void) { // Matripix. By Andrew Tuline. int16_t volumeRaw = *(int16_t*)um_data->u_data[1]; if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -6505,7 +6477,6 @@ uint16_t mode_pixelwave(void) { // Pixelwave. By Andrew Tuline. // even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -6731,7 +6702,6 @@ uint16_t mode_DJLight(void) { // Written by ??? Adapted by Wil uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -6800,7 +6770,6 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch float volumeSmth = *(float*)um_data->u_data[0]; if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -6902,7 +6871,6 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun float volumeSmth = *(float*)um_data->u_data[0]; if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -7087,7 +7055,6 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception) if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); SEGENV.aux0 = 255; SEGMENT.custom1 = *binNum; @@ -7204,7 +7171,6 @@ uint16_t mode_2DFunkyPlank(void) { // Written by ??? Adapted by Wil uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); } @@ -7417,7 +7383,6 @@ uint16_t mode_2Dsoap() { // init if (SEGENV.call == 0) { - SEGMENT.setUpLeds(); *noise32_x = random16(); *noise32_y = random16(); *noise32_z = random16(); @@ -7535,12 +7500,12 @@ uint16_t mode_2Doctopus() { SEGENV.aux1 = rows; *offsX = SEGMENT.custom1; *offsY = SEGMENT.custom2; - const uint8_t C_X = cols / 2 + (SEGMENT.custom1 - 128)*cols/255; - const uint8_t C_Y = rows / 2 + (SEGMENT.custom2 - 128)*rows/255; + const int C_X = (cols / 2) + ((SEGMENT.custom1 - 128)*cols)/255; + const int C_Y = (rows / 2) + ((SEGMENT.custom2 - 128)*rows)/255; for (int x = 0; x < cols; x++) { for (int y = 0; y < rows; y++) { - rMap[XY(x, y)].angle = 40.7436f * atan2f(y - C_Y, x - C_X); // avoid 128*atan2()/PI - rMap[XY(x, y)].radius = hypotf(x - C_X, y - C_Y) * mapp; //thanks Sutaburosu + rMap[XY(x, y)].angle = 40.7436f * atan2f((y - C_Y), (x - C_X)); // avoid 128*atan2()/PI + rMap[XY(x, y)].radius = hypotf((x - C_X), (y - C_Y)) * mapp; //thanks Sutaburosu } } } diff --git a/wled00/FX.h b/wled00/FX.h index 34b6f785..21f9d08c 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -329,7 +329,7 @@ typedef enum mapping1D2D { M12_pCorner = 3 } mapping1D2D_t; -// segment, 72 bytes +// segment, 80 bytes typedef struct Segment { public: uint16_t start; // start index / start X coordinate 2D (left) @@ -370,7 +370,7 @@ typedef struct Segment { }; uint8_t startY; // start Y coodrinate 2D (top); there should be no more than 255 rows uint8_t stopY; // stop Y coordinate 2D (bottom); there should be no more than 255 rows - char *name; + char *name; // runtime data unsigned long next_time; // millis() of next update @@ -378,8 +378,7 @@ typedef struct Segment { uint32_t call; // call counter uint16_t aux0; // custom var uint16_t aux1; // custom var - byte* data; // effect data pointer - uint32_t* leds; // local leds[] array (may be a pointer to global) + byte *data; // effect data pointer static uint16_t maxWidth, maxHeight; // these define matrix width & height (max. segment dimensions) private: @@ -393,10 +392,13 @@ typedef struct Segment { uint8_t _reserved : 4; }; }; - uint16_t _dataLen; + uint16_t _dataLen; static uint16_t _usedSegmentData; - // transition data, valid only if transitional==true, holds values during transition + uint16_t _qStart, _qStop, _qStartY, _qStopY; + bool _queuedChanges; + + // transition data, valid only if transitional==true, holds values during transition (72 bytes) struct Transition { uint32_t _colorT[NUM_COLORS]; uint8_t _briT; // temporary brightness @@ -407,7 +409,7 @@ typedef struct Segment { //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 - uint32_t _start; + unsigned long _start; // must accommodate millis() uint16_t _dur; Transition(uint16_t dur=750) : _briT(255) @@ -462,9 +464,9 @@ typedef struct Segment { aux0(0), aux1(0), data(nullptr), - leds(nullptr), _capabilities(0), _dataLen(0), + _queuedChanges(false), _t(nullptr) { //refreshLightCapabilities(); @@ -485,9 +487,8 @@ typedef struct Segment { //if (data) Serial.printf(" %d (%p)", (int)_dataLen, data); //Serial.println(); //#endif - if (name) delete[] name; - if (_t) delete _t; - if (leds) free(leds); + if (name) { delete[] name; name = nullptr; } + if (_t) { transitional = false; delete _t; _t = nullptr; } deallocateData(); } @@ -495,7 +496,7 @@ typedef struct Segment { Segment& operator= (Segment &&orig) noexcept; // move assignment #ifdef WLED_DEBUG - size_t getSize() const { return sizeof(Segment) + (data?_dataLen:0) + (name?strlen(name):0) + (_t?sizeof(Transition):0) + (leds?sizeof(CRGB)*length():0); } + size_t getSize() const { return sizeof(Segment) + (data?_dataLen:0) + (name?strlen(name):0) + (_t?sizeof(Transition):0); } #endif inline bool getOption(uint8_t n) const { return ((options >> n) & 0x01); } @@ -505,16 +506,16 @@ typedef struct Segment { inline bool hasRGB(void) const { return _isRGB; } inline bool hasWhite(void) const { return _hasW; } inline bool isCCT(void) const { return _isCCT; } - inline uint16_t width(void) const { return (stop > start) ? (stop - start) : 0; } // segment width in physical pixels (length if 1D) - inline uint16_t height(void) const { return (stopY > startY) ? (stopY - startY) : 0; } // segment height (if 2D) in physical pixels // softhack007: make sure its always > 0 - inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels + inline uint16_t width(void) const { return isActive() ? (stop - start) : 0; } // segment width in physical pixels (length if 1D) + inline uint16_t height(void) const { return stopY - startY; } // segment height (if 2D) in physical pixels (it *is* always >=1) + inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels inline uint16_t groupLength(void) const { return grouping + spacing; } inline uint8_t getLightCapabilities(void) const { return _capabilities; } static uint16_t getUsedSegmentData(void) { return _usedSegmentData; } static void addUsedSegmentData(int len) { _usedSegmentData += len; } - 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); + 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, bool immediate=false); bool setColor(uint8_t slot, uint32_t c); //returns true if changed void setCCT(uint16_t k); void setOpacity(uint8_t o); @@ -536,7 +537,6 @@ typedef struct Segment { * Safe to call from interrupts and network requests. */ inline void markForReset(void) { reset = true; } // setOption(SEG_OPTION_RESET, true) - void setUpLeds(void); // local double buffer/lossless getPixelColor() // transition functions void startTransition(uint16_t dur); // transition has to start before actual segment values change @@ -896,7 +896,7 @@ class WS2812FX { // 96 bytes uint16_t* customMappingTable; uint16_t customMappingSize; - uint32_t _lastShow; + unsigned long _lastShow; uint8_t _segment_index; uint8_t _mainSegment; diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 45107fe5..417d0484 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -189,20 +189,16 @@ uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) { // XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element) uint16_t /*IRAM_ATTR*/ Segment::XY(uint16_t x, uint16_t y) { - uint16_t width = virtualWidth(); // segment width in logical pixels - uint16_t height = virtualHeight(); // segment height in logical pixels - if (width == 0) return 0; // softhack007 avoid div/0 - if (height == 0) return (x%width); // softhack007 avoid div/0 - return (x%width) + (y%height) * width; + uint16_t width = virtualWidth(); // segment width in logical pixels (can be 0 if segment is inactive) + uint16_t height = virtualHeight(); // segment height in logical pixels (is always >= 1) + return isActive() ? (x%width) + (y%height) * width : 0; } void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col) { - if (Segment::maxHeight==1) return; // not a matrix set-up + if (!isActive()) return; // not active if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit - if (leds) leds[XY(x,y)] = col; - uint8_t _bri_t = currentBri(on ? opacity : 0); if (_bri_t < 255) { byte r = scale8(R(col), _bri_t); @@ -245,7 +241,7 @@ void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col) // anti-aliased version of setPixelColorXY() void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa) { - if (Segment::maxHeight==1) return; // not a matrix set-up + if (!isActive()) return; // not active if (x<0.0f || x>1.0f || y<0.0f || y>1.0f) return; // not normalized const uint16_t cols = virtualWidth(); @@ -288,7 +284,8 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa) // returns RGBW values of pixel uint32_t Segment::getPixelColorXY(uint16_t x, uint16_t y) { - if (leds) return leds[XY(x,y)]; + if (!isActive()) return 0; // not active + if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return 0; // if pixel would fall out of virtual segment just exit if (reverse ) x = virtualWidth() - x - 1; if (reverse_y) y = virtualHeight() - y - 1; if (transpose) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed @@ -305,6 +302,7 @@ void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t // Adds the specified color with the existing pixel color perserving color balance. void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) { + if (!isActive()) return; // not active if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit uint32_t col = getPixelColorXY(x,y); uint8_t r = R(col); @@ -324,12 +322,14 @@ void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) { } void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { + if (!isActive()) return; // not active CRGB pix = CRGB(getPixelColorXY(x,y)).nscale8_video(fade); setPixelColorXY(x, y, pix); } // blurRow: perform a blur on a row of a rectangular matrix void Segment::blurRow(uint16_t row, fract8 blur_amount) { + if (!isActive()) return; // not active const uint_fast16_t cols = virtualWidth(); const uint_fast16_t rows = virtualHeight(); @@ -357,6 +357,7 @@ void Segment::blurRow(uint16_t row, fract8 blur_amount) { // blurCol: perform a blur on a column of a rectangular matrix void Segment::blurCol(uint16_t col, fract8 blur_amount) { + if (!isActive()) return; // not active const uint_fast16_t cols = virtualWidth(); const uint_fast16_t rows = virtualHeight(); @@ -384,6 +385,7 @@ void Segment::blurCol(uint16_t col, fract8 blur_amount) { // 1D Box blur (with added weight - blur_amount: [0=no blur, 255=max blur]) void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) { + if (!isActive()) return; // not active const uint16_t cols = virtualWidth(); const uint16_t rows = virtualHeight(); const uint16_t dim1 = vertical ? rows : cols; @@ -436,6 +438,7 @@ void Segment::blur1d(fract8 blur_amount) { } void Segment::moveX(int8_t delta, bool wrap) { + if (!isActive()) return; // not active const uint16_t cols = virtualWidth(); const uint16_t rows = virtualHeight(); if (!delta || abs(delta) >= cols) return; @@ -453,6 +456,7 @@ void Segment::moveX(int8_t delta, bool wrap) { } void Segment::moveY(int8_t delta, bool wrap) { + if (!isActive()) return; // not active const uint16_t cols = virtualWidth(); const uint16_t rows = virtualHeight(); if (!delta || abs(delta) >= rows) return; @@ -488,6 +492,7 @@ void Segment::move(uint8_t dir, uint8_t delta, bool wrap) { } void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { + if (!isActive()) return; // not active // Bresenham’s Algorithm int d = 3 - (2*radius); int y = radius, x = 0; @@ -512,6 +517,7 @@ void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { // by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { + if (!isActive()) return; // not active const uint16_t cols = virtualWidth(); const uint16_t rows = virtualHeight(); for (int16_t y = -radius; y <= radius; y++) { @@ -525,6 +531,7 @@ void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { } void Segment::nscale8(uint8_t scale) { + if (!isActive()) return; // not active const uint16_t cols = virtualWidth(); const uint16_t rows = virtualHeight(); for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { @@ -534,6 +541,7 @@ void Segment::nscale8(uint8_t scale) { //line function void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) { + if (!isActive()) return; // not active const uint16_t cols = virtualWidth(); const uint16_t rows = virtualHeight(); if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return; @@ -558,6 +566,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) { + if (!isActive()) return; // not active if (chr < 32 || chr > 126) return; // only ASCII 32-126 supported chr -= 32; // align with font table entries const uint16_t cols = virtualWidth(); @@ -593,6 +602,7 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, #define WU_WEIGHT(a,b) ((uint8_t) (((a)*(b)+(a)+(b))>>8)) void Segment::wu_pixel(uint32_t x, uint32_t y, CRGB c) { //awesome wu_pixel procedure by reddit u/sutaburosu + if (!isActive()) return; // not active // extract the fractional parts and derive their inverses uint8_t xx = x & 0xff, yy = y & 0xff, ix = 255 - xx, iy = 255 - yy; // calculate the intensities for each affected pixel diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index b7684423..bf26dbfc 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -81,22 +81,21 @@ uint16_t Segment::maxHeight = 1; Segment::Segment(const Segment &orig) { //DEBUG_PRINTLN(F("-- Copy segment constructor --")); memcpy((void*)this, (void*)&orig, sizeof(Segment)); + transitional = false; // copied segment cannot be in transition name = nullptr; data = nullptr; _dataLen = 0; _t = nullptr; - leds = 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.leds) { leds = (uint32_t*)malloc(sizeof(uint32_t)*length()); if (leds) memcpy(leds, orig.leds, sizeof(uint32_t)*length()); } + //if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); } } // move constructor Segment::Segment(Segment &&orig) noexcept { //DEBUG_PRINTLN(F("-- Move segment constructor --")); memcpy((void*)this, (void*)&orig, sizeof(Segment)); - orig.leds = nullptr; + orig.transitional = false; // old segment cannot be in transition any more orig.name = nullptr; orig.data = nullptr; orig._dataLen = 0; @@ -108,23 +107,22 @@ Segment& Segment::operator= (const Segment &orig) { //DEBUG_PRINTLN(F("-- Copying segment --")); if (this != &orig) { // clean destination + transitional = false; // copied segment cannot be in transition if (name) delete[] name; if (_t) delete _t; - if (leds) free(leds); deallocateData(); // copy source memcpy((void*)this, (void*)&orig, sizeof(Segment)); + transitional = false; // erase pointers to allocated data name = nullptr; data = nullptr; _dataLen = 0; _t = nullptr; - leds = nullptr; // copy source data 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.leds) { leds = (uint32_t*)malloc(sizeof(uint32_t)*length()); if (leds) memcpy(leds, orig.leds, sizeof(uint32_t)*length()); } + //if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); } } return *this; } @@ -133,16 +131,16 @@ Segment& Segment::operator= (const Segment &orig) { Segment& Segment::operator= (Segment &&orig) noexcept { //DEBUG_PRINTLN(F("-- Moving segment --")); if (this != &orig) { - if (name) delete[] name; // free old name - if (leds) free(leds); + transitional = false; // just temporary + if (name) { delete[] name; name = nullptr; } // free old name deallocateData(); // free old runtime data - if (_t) delete _t; + if (_t) { delete _t; _t = nullptr; } memcpy((void*)this, (void*)&orig, sizeof(Segment)); + orig.transitional = false; // old segment cannot be in transition orig.name = nullptr; orig.data = nullptr; orig._dataLen = 0; orig._t = nullptr; - orig.leds = nullptr; } return *this; } @@ -152,12 +150,7 @@ bool Segment::allocateData(size_t len) { deallocateData(); if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) return false; //not enough memory // do not use SPI RAM on ESP32 since it is slow - //#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM) - //if (psramFound()) - // data = (byte*) ps_malloc(len); - //else - //#endif - data = (byte*) malloc(len); + data = (byte*) malloc(len); if (!data) return false; //allocation failed Segment::addUsedSegmentData(len); _dataLen = len; @@ -182,28 +175,13 @@ void Segment::deallocateData() { */ void Segment::resetIfRequired() { if (reset) { - if (leds) { free(leds); leds = nullptr; } deallocateData(); next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0; + if (_queuedChanges) setUp(_qStart, _qStop, grouping, spacing, offset, _qStartY, _qStopY, true); reset = false; // setOption(SEG_OPTION_RESET, false); } } -void Segment::setUpLeds() { - // deallocation happens in resetIfRequired() as it is called when segment changes or in destructor - if (useGlobalLedBuffer) return; // TODO optional seg buffer for FX without lossy restore due to opacity - - // no global buffer - if (leds == nullptr && length() > 0) { //softhack007 quickfix - avoid malloc(0) which is undefined behaviour (should not happen, but i've seen it) - //#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM) - //if (psramFound()) - // leds = (CRGB*)ps_malloc(sizeof(CRGB)*length()); // softhack007 disabled; putting leds into psram leads to horrible slowdown on WROVER boards - //else - //#endif - leds = (uint32_t *)calloc(length(), sizeof(uint32_t)); - } -} - 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); @@ -326,7 +304,7 @@ void Segment::startTransition(uint16_t dur) { // transition progression between 0-65535 uint16_t Segment::progress() { if (!transitional || !_t) return 0xFFFFU; - uint32_t timeNow = millis(); + unsigned long timeNow = millis(); if (timeNow - _t->_start > _t->_dur || _t->_dur == 0) return 0xFFFFU; return (timeNow - _t->_start) * 0xFFFFU / _t->_dur; } @@ -355,7 +333,7 @@ CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal // 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 - uint32_t timeMS = millis() - _t->_start; + unsigned long timeMS = millis() - _t->_start; uint16_t noOfBlends = (255U * timeMS / _t->_dur) - _t->_prevPaletteBlends; for (int i=0; i_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, targetPalette, 48); targetPalette = _t->_palT; // copy transitioning/temporary palette @@ -376,8 +354,9 @@ void Segment::handleTransition() { } } -void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y) { - //return if neither bounds nor grouping have changed +void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y, bool immediate) { + _queuedChanges = false; // cancel anything queued + // return if neither bounds nor grouping have changed bool boundsUnchanged = (start == i1 && stop == i2); #ifndef WLED_DISABLE_2D if (Segment::maxHeight>1) boundsUnchanged &= (startY == i1Y && stopY == i2Y); // 2D @@ -386,29 +365,47 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t && (!grp || (grouping == grp && spacing == spc)) && (ofs == UINT16_MAX || ofs == offset)) return; - if (stop) fill(BLACK); //turn old segment range off - if (i2 <= i1) { //disable segment - stop = 0; - markForReset(); - return; - } - if (i1 < Segment::maxWidth || (i1 >= Segment::maxWidth*Segment::maxHeight && i1 < strip.getLengthTotal())) start = i1; // Segment::maxWidth equals strip.getLengthTotal() for 1D - stop = i2 > Segment::maxWidth*Segment::maxHeight ? MIN(i2,strip.getLengthTotal()) : (i2 > Segment::maxWidth ? Segment::maxWidth : MAX(1,i2)); - startY = 0; - stopY = 1; - #ifndef WLED_DISABLE_2D - if (Segment::maxHeight>1) { // 2D - if (i1Y < Segment::maxHeight) startY = i1Y; - stopY = i2Y > Segment::maxHeight ? Segment::maxHeight : MAX(1,i2Y); - } - #endif - if (grp) { + if (stop) fill(BLACK); // turn old segment range off (clears pixels if changing spacing) + if (grp) { // prevent assignment of 0 grouping = grp; spacing = spc; + } else { + grouping = 1; + spacing = 0; } if (ofs < UINT16_MAX) offset = ofs; + markForReset(); - if (!boundsUnchanged) refreshLightCapabilities(); + if (!boundsUnchanged) { + if (immediate) { + if (i2 <= i1) { //disable segment + stop = 0; + return; + } + if (i1 < Segment::maxWidth || (i1 >= Segment::maxWidth*Segment::maxHeight && i1 < strip.getLengthTotal())) start = i1; // Segment::maxWidth equals strip.getLengthTotal() for 1D + stop = i2 > Segment::maxWidth*Segment::maxHeight ? MIN(i2,strip.getLengthTotal()) : (i2 > Segment::maxWidth ? Segment::maxWidth : MAX(1,i2)); + startY = 0; + stopY = 1; + #ifndef WLED_DISABLE_2D + if (Segment::maxHeight>1) { // 2D + if (i1Y < Segment::maxHeight) startY = i1Y; + stopY = i2Y > Segment::maxHeight ? Segment::maxHeight : MAX(1,i2Y); + } + #endif + // safety check + if (start >= stop || startY >= stopY) { + stop = 0; + return; + } + if (!boundsUnchanged) refreshLightCapabilities(); + } else { + _qStart = i1; + _qStop = i2; + _qStartY = i1Y; + _qStopY = i2Y; + _queuedChanges = true; + } + } } @@ -455,8 +452,7 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) { // if we have a valid mode & is not reserved if (fx < strip.getModeCount() && strncmp_P("RSVD", strip.getModeData(fx), 4)) { if (fx != mode) { - startTransition(strip.getTransition()); // set effect transitions - //markForReset(); // transition will handle this + if (fadeTransition) startTransition(strip.getTransition()); // set effect transitions mode = fx; // load default values from effect string @@ -541,8 +537,7 @@ uint16_t Segment::virtualLength() const { return vLen; } #endif - uint16_t groupLen = groupLength(); - if (groupLen < 1) groupLen = 1; // prevent division by zero - better safe than sorry ... + uint16_t groupLen = groupLength(); // is always >= 1 uint16_t vLength = (length() + groupLen - 1) / groupLen; if (mirror) vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED return vLength; @@ -550,6 +545,7 @@ uint16_t Segment::virtualLength() const { void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col) { + if (!isActive()) return; // not active #ifndef WLED_DISABLE_2D int vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows) #endif @@ -617,8 +613,6 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col) } #endif - if (leds) leds[i] = col; - uint16_t len = length(); uint8_t _bri_t = currentBri(on ? opacity : 0); if (_bri_t < 255) { @@ -660,6 +654,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col) // anti-aliased normalized version of setPixelColor() void Segment::setPixelColor(float i, uint32_t col, bool aa) { + if (!isActive()) return; // not active int vStrip = int(i/10.0f); // hack to allow running on virtual strips (2D segment columns/rows) i -= int(i); @@ -691,6 +686,7 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa) uint32_t Segment::getPixelColor(int i) { + if (!isActive()) return 0; // not active #ifndef WLED_DISABLE_2D int vStrip = i>>16; #endif @@ -718,8 +714,6 @@ uint32_t Segment::getPixelColor(int i) } #endif - if (leds) return leds[i]; - if (reverse) i = virtualLength() - i - 1; i *= groupLength(); i += start; @@ -760,6 +754,11 @@ void Segment::refreshLightCapabilities() { uint16_t segStartIdx = 0xFFFFU; uint16_t segStopIdx = 0; + if (!isActive()) { + _capabilities = 0; + return; + } + if (start < Segment::maxWidth * Segment::maxHeight) { // we are withing 2D matrix (includes 1D segments) for (int y = startY; y < stopY; y++) for (int x = start; x < stop; x++) { @@ -804,6 +803,7 @@ void Segment::refreshLightCapabilities() { * Fills segment with color */ void Segment::fill(uint32_t c) { + if (!isActive()) return; // not active const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); const uint16_t rows = virtualHeight(); // will be 1 for 1D for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { @@ -819,6 +819,7 @@ void Segment::blendPixelColor(int n, uint32_t color, uint8_t blend) { // Adds the specified color with the existing pixel color perserving color balance. void Segment::addPixelColor(int n, uint32_t color, bool fast) { + if (!isActive()) return; // not active uint32_t col = getPixelColor(n); uint8_t r = R(col); uint8_t g = G(col); @@ -837,6 +838,7 @@ void Segment::addPixelColor(int n, uint32_t color, bool fast) { } void Segment::fadePixelColor(uint16_t n, uint8_t fade) { + if (!isActive()) return; // not active CRGB pix = CRGB(getPixelColor(n)).nscale8_video(fade); setPixelColor(n, pix); } @@ -845,6 +847,7 @@ void Segment::fadePixelColor(uint16_t n, uint8_t fade) { * fade out function, higher rate = quicker fade */ void Segment::fade_out(uint8_t rate) { + if (!isActive()) return; // not active const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); const uint16_t rows = virtualHeight(); // will be 1 for 1D @@ -882,7 +885,7 @@ void Segment::fade_out(uint8_t rate) { // fades all pixels to black using nscale8() void Segment::fadeToBlackBy(uint8_t fadeBy) { - if (fadeBy == 0) return; // optimization - no scaling to apply + if (!isActive() || fadeBy == 0) return; // optimization - no scaling to apply const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); const uint16_t rows = virtualHeight(); // will be 1 for 1D @@ -897,7 +900,7 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) { */ void Segment::blur(uint8_t blur_amount) { - if (blur_amount == 0) return; // optimization: 0 means "don't blur" + if (!isActive() || blur_amount == 0) return; // optimization: 0 means "don't blur" #ifndef WLED_DISABLE_2D if (is2D()) { // compatibility with 2D @@ -1071,7 +1074,7 @@ void WS2812FX::finalizeInit(void) } void WS2812FX::service() { - uint32_t nowUp = millis(); // Be aware, millis() rolls over every 49 days + unsigned long nowUp = millis(); // Be aware, millis() rolls over every 49 days now = nowUp + timebase; if (nowUp - _lastShow < MIN_SHOW_DELAY) return; bool doShow = false; @@ -1089,7 +1092,6 @@ void WS2812FX::service() { // last condition ensures all solid segments are updated at the same time if(nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC)) { - if (seg.grouping == 0) seg.grouping = 1; // sanity check doShow = true; uint16_t delay = FRAMETIME; @@ -1117,12 +1119,19 @@ void WS2812FX::service() { } _virtualSegmentLength = 0; busses.setSegmentCCT(-1); - if(doShow) { + _isServicing = false; + _triggered = false; + + #ifdef WLED_DEBUG + if (millis() - nowUp > _frametime) DEBUG_PRINTLN(F("Slow effects.")); + #endif + if (doShow) { yield(); show(); } - _triggered = false; - _isServicing = false; + #ifdef WLED_DEBUG + if (millis() - nowUp > _frametime) DEBUG_PRINTLN(F("Slow strip.")); + #endif } void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col) @@ -1159,31 +1168,25 @@ uint8_t WS2812FX::estimateCurrentAndLimitBri() { bool useWackyWS2815PowerModel = false; byte actualMilliampsPerLed = milliampsPerLed; - if(milliampsPerLed == 255) { - useWackyWS2815PowerModel = true; - actualMilliampsPerLed = 12; // from testing an actual strip - } - if (ablMilliampsMax < 150 || actualMilliampsPerLed == 0) { //0 mA per LED and too low numbers turn off calculation currentMilliamps = 0; return _brightness; } - uint16_t pLen = getLengthPhysical(); - uint32_t puPerMilliamp = 195075 / actualMilliampsPerLed; - uint32_t powerBudget = (ablMilliampsMax - MA_FOR_ESP) * puPerMilliamp; //100mA for ESP power - if (powerBudget > puPerMilliamp * pLen) { //each LED uses about 1mA in standby, exclude that from power budget - powerBudget -= puPerMilliamp * pLen; - } else { - powerBudget = 0; + if (milliampsPerLed == 255) { + useWackyWS2815PowerModel = true; + actualMilliampsPerLed = 12; // from testing an actual strip } - uint32_t powerSum = 0; // could overflow if more than 22K LEDs (uint32_t MAX / 195075 PU per LED) + size_t powerBudget = (ablMilliampsMax - MA_FOR_ESP); //100mA for ESP power + size_t pLen = 0; //getLengthPhysical(); + size_t powerSum = 0; for (uint_fast8_t bNum = 0; bNum < busses.getNumBusses(); bNum++) { Bus *bus = busses.getBus(bNum); - if (bus->getType() >= TYPE_NET_DDP_RGB) continue; //exclude non-physical network busses + if (!IS_DIGITAL(bus->getType())) continue; //exclude non-digital network busses uint16_t len = bus->getLength(); + pLen += len; uint32_t busPowerSum = 0; for (uint_fast16_t i = 0; i < len; i++) { //sum up the usage of each LED uint32_t c = bus->getPixelColor(i); // always returns original or restored color without brightness scaling @@ -1198,38 +1201,44 @@ uint8_t WS2812FX::estimateCurrentAndLimitBri() { if (bus->hasWhite()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less busPowerSum *= 3; - busPowerSum = busPowerSum >> 2; //same as /= 4 + busPowerSum >>= 2; //same as /= 4 } powerSum += busPowerSum; } - uint8_t newBri = _brightness; - uint32_t powerSumUnscaled = powerSum; - powerSum *= _brightness; + if (powerBudget > pLen) { //each LED uses about 1mA in standby, exclude that from power budget + powerBudget -= pLen; + } else { + powerBudget = 0; + } - if (powerSum > powerBudget) { //scale brightness down to stay in current limit + // powerSum has all the values of channels summed (max would be pLen*765 as white is excluded) so convert to milliAmps + powerSum = (powerSum * actualMilliampsPerLed) / 765; + + uint8_t newBri = _brightness; + if (powerSum * _brightness / 255 > powerBudget) { //scale brightness down to stay in current limit float scale = (float)powerBudget / (float)powerSum; uint16_t scaleI = scale * 255; uint8_t scaleB = (scaleI > 255) ? 255 : scaleI; newBri = scale8(_brightness, scaleB); } - currentMilliamps = (powerSumUnscaled * newBri) / puPerMilliamp; + currentMilliamps = (powerSum * newBri) / 255; currentMilliamps += MA_FOR_ESP; //add power of ESP back to estimate - currentMilliamps += pLen; //add standby power back to estimate + currentMilliamps += pLen; //add standby power (1mA/LED) back to estimate return newBri; } void WS2812FX::show(void) { + // avoid race condition, caputre _callback value + show_callback callback = _callback; + if (callback) callback(); + #ifdef WLED_DEBUG static unsigned long sumMicros = 0, sumCurrent = 0; static size_t calls = 0; unsigned long microsStart = micros(); #endif - // avoid race condition, caputre _callback value - show_callback callback = _callback; - if (callback) callback(); - uint8_t busBrightness = estimateCurrentAndLimitBri(); busses.setBrightness(busBrightness); #ifdef WLED_DEBUG @@ -1240,21 +1249,22 @@ void WS2812FX::show(void) { // all of the data has been sent. // See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods busses.show(); - unsigned long now = millis(); - unsigned long diff = now - _lastShow; - uint16_t fpsCurr = 200; - if (diff > 0) fpsCurr = 1000 / diff; - _cumulativeFps = (3 * _cumulativeFps + fpsCurr) >> 2; - _lastShow = now; #ifdef WLED_DEBUG sumMicros += micros() - microsStart; if (++calls == 100) { - DEBUG_PRINTF("show calls: %d micros: %lu avg: %lu (current: %lu avg: %lu)\n", calls, sumMicros, sumMicros/calls, sumCurrent, sumCurrent/calls); + DEBUG_PRINTF("%d show calls: %lu[us] avg: %lu[us] (current: %lu[us] avg: %lu[us])\n", calls, sumMicros, sumMicros/calls, sumCurrent, sumCurrent/calls); sumMicros = sumCurrent = 0; calls = 0; } #endif + + unsigned long now = millis(); + size_t diff = now - _lastShow; + size_t fpsCurr = 200; + if (diff > 0) fpsCurr = 1000 / diff; + _cumulativeFps = (3 * _cumulativeFps + fpsCurr) >> 2; + _lastShow = now; } /** @@ -1431,6 +1441,7 @@ Segment& WS2812FX::getSegment(uint8_t id) { return _segments[id >= _segments.size() ? getMainSegmentId() : id]; // vectors } +// compatibility method (deprecated) void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing, uint16_t offset, uint16_t startY, uint16_t stopY) { if (n >= _segments.size()) return; _segments[n].setUp(i1, i2, grouping, spacing, offset, startY, stopY); diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index ed1816ea..c0d4e7e2 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -128,11 +128,6 @@ BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) void BusDigital::show() { if (!_valid) return; - #ifdef WLED_DEBUG - static unsigned long sumMicros = 0; - static size_t calls = 0; - unsigned long microsStart = micros(); - #endif PolyBus::setBrightness(_busPtr, _iType, _bri); if (buffering) { // should be _data != nullptr, but that causes ~20% FPS drop size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type); @@ -159,14 +154,6 @@ void BusDigital::show() { } PolyBus::show(_busPtr, _iType); PolyBus::setBrightness(_busPtr, _iType, 255); // restore full brightness at bus level (setting unscaled pixel color) - #ifdef WLED_DEBUG - sumMicros += micros() - microsStart; - if (++calls == 100) { - DEBUG_PRINTF("Bus calls: %d micros: %lu avg: %lu\n", calls, sumMicros, sumMicros/calls); - sumMicros = 0; - calls = 0; - } - #endif } bool BusDigital::canShow() { diff --git a/wled00/json.cpp b/wled00/json.cpp index bff9f362..8858e9c9 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -22,7 +22,7 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId) int stop = elem["stop"] | -1; - // if using vectors use this code to append segment + // append segment if (id >= strip.getSegmentsNum()) { if (stop <= 0) return false; // ignore empty/inactive segments strip.appendSegment(Segment(0, strip.getLengthTotal())); @@ -110,7 +110,10 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId) of = offsetAbs; } if (stop > start && of > len -1) of = len -1; - seg.setUp(start, stop, grp, spc, of, startY, stopY); + + // update segment (delete if necessary) + // we must not change segment dimensions during drawing of effects as that may produce undesired behaviour (crash) + seg.setUp(start, stop, grp, spc, of, startY, stopY, !strip.isServicing()); if (seg.reset && seg.stop == 0) return true; // segment was deleted & is marked for reset, no need to change anything else @@ -468,12 +471,14 @@ void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset, b if (segmentBounds) { root["start"] = seg.start; root["stop"] = seg.stop; + #ifndef WLED_DISABLE_2D if (strip.isMatrix) { root[F("startY")] = seg.startY; root[F("stopY")] = seg.stopY; } + #endif } - if (!forPreset) root["len"] = (seg.stop >= seg.start) ? (seg.stop - seg.start) : 0; + if (!forPreset) root["len"] = seg.stop - seg.start; root["grp"] = seg.grouping; root[F("spc")] = seg.spacing; root[F("of")] = seg.offset; diff --git a/wled00/wled.cpp b/wled00/wled.cpp index a732bba3..e51fc37b 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -23,7 +23,7 @@ void WLED::reset() #ifdef WLED_ENABLE_WEBSOCKETS ws.closeAll(1012); #endif - long dly = millis(); + unsigned long dly = millis(); while (millis() - dly < 450) { yield(); // enough time to send response to client } @@ -36,13 +36,17 @@ void WLED::loop() { #ifdef WLED_DEBUG static unsigned long lastRun = 0; - size_t loopDelay = (millis() - lastRun); + unsigned long loopMillis = millis(); + size_t loopDelay = loopMillis - lastRun; if (lastRun == 0) loopDelay=0; // startup - don't have valid data from last run. - if (loopDelay > 2) DEBUG_PRINTF("Loop delayed more than %dms.\n", loopDelay); + if (loopDelay > 2) DEBUG_PRINTF("Loop delayed more than %ums.\n", loopDelay); + static unsigned long maxLoopMillis = 0; + static size_t avgLoopMillis = 0; static unsigned long maxUsermodMillis = 0; static size_t avgUsermodMillis = 0; static unsigned long maxStripMillis = 0; static size_t avgStripMillis = 0; + unsigned long stripMillis; #endif handleTime(); @@ -84,6 +88,9 @@ void WLED::loop() yield(); } + #ifdef WLED_DEBUG + stripMillis = millis(); + #endif if (!realtimeMode || realtimeOverride || (realtimeMode && useMainSegmentOnly)) // block stuff if WARLS/Adalight is enabled { if (apActive) dnsServer.processNextRequest(); @@ -102,22 +109,18 @@ void WLED::loop() handlePresets(); yield(); - #ifdef WLED_DEBUG - unsigned long stripMillis = millis(); - #endif if (!offMode || strip.isOffRefreshRequired()) strip.service(); #ifdef ESP8266 else if (!noWifiSleep) delay(1); //required to make sure ESP enters modem sleep (see #1184) #endif - #ifdef WLED_DEBUG - stripMillis = millis() - stripMillis; - if (stripMillis > 50) DEBUG_PRINTLN("Slow strip."); - avgStripMillis += stripMillis; - if (stripMillis > maxStripMillis) maxStripMillis = stripMillis; - #endif } + #ifdef WLED_DEBUG + stripMillis = millis() - stripMillis; + avgStripMillis += stripMillis; + if (stripMillis > maxStripMillis) maxStripMillis = stripMillis; + #endif yield(); #ifdef ESP8266 @@ -186,8 +189,31 @@ void WLED::loop() handleWs(); handleStatusLED(); + toki.resetTick(); + +#if WLED_WATCHDOG_TIMEOUT > 0 + // we finished our mainloop, reset the watchdog timer + if (!strip.isUpdating()) + #ifdef ARDUINO_ARCH_ESP32 + esp_task_wdt_reset(); + #else + ESP.wdtFeed(); + #endif +#endif + + if (doReboot && (!doInitBusses || !doSerializeConfig)) // if busses have to be inited & saved, wait until next iteration + reset(); + // DEBUG serial logging (every 30s) #ifdef WLED_DEBUG + loopMillis = millis() - loopMillis; + if (loopMillis > 30) { + DEBUG_PRINTF("Loop took %lums.\n", loopMillis); + DEBUG_PRINTF("Usermods took %lums.\n", usermodMillis); + DEBUG_PRINTF("Strip took %lums.\n", stripMillis); + } + avgLoopMillis += loopMillis; + if (loopMillis > maxLoopMillis) maxLoopMillis = loopMillis; if (millis() - debugTime > 29999) { DEBUG_PRINTLN(F("---DEBUG INFO---")); DEBUG_PRINT(F("Runtime: ")); DEBUG_PRINTLN(millis()); @@ -210,11 +236,13 @@ void WLED::loop() DEBUG_PRINT(F("Client IP: ")); DEBUG_PRINTLN(Network.localIP()); if (loops > 0) { // avoid division by zero DEBUG_PRINT(F("Loops/sec: ")); DEBUG_PRINTLN(loops / 30); + DEBUG_PRINT(F("Loop time[ms]: ")); DEBUG_PRINT(avgLoopMillis/loops); DEBUG_PRINT("/");DEBUG_PRINTLN(maxLoopMillis); DEBUG_PRINT(F("UM time[ms]: ")); DEBUG_PRINT(avgUsermodMillis/loops); DEBUG_PRINT("/");DEBUG_PRINTLN(maxUsermodMillis); DEBUG_PRINT(F("Strip time[ms]: ")); DEBUG_PRINT(avgStripMillis/loops); DEBUG_PRINT("/"); DEBUG_PRINTLN(maxStripMillis); } strip.printSize(); loops = 0; + maxLoopMillis = 0; maxUsermodMillis = 0; maxStripMillis = 0; avgUsermodMillis = 0; @@ -224,20 +252,6 @@ void WLED::loop() loops++; lastRun = millis(); #endif // WLED_DEBUG - toki.resetTick(); - -#if WLED_WATCHDOG_TIMEOUT > 0 - // we finished our mainloop, reset the watchdog timer - if (!strip.isUpdating()) - #ifdef ARDUINO_ARCH_ESP32 - esp_task_wdt_reset(); - #else - ESP.wdtFeed(); - #endif -#endif - - if (doReboot && (!doInitBusses || !doSerializeConfig)) // if busses have to be inited & saved, wait until next iteration - reset(); } void WLED::enableWatchdog() { diff --git a/wled00/wled.h b/wled00/wled.h index 71b48138..c9487789 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2307090 +#define VERSION 2307120 //uncomment this if you have a "my_config.h" file you'd like to use //#define WLED_USE_MY_CONFIG @@ -513,7 +513,7 @@ WLED_GLOBAL uint16_t userVar0 _INIT(0), userVar1 _INIT(0); //available for use i // wifi WLED_GLOBAL bool apActive _INIT(false); WLED_GLOBAL bool forceReconnect _INIT(false); -WLED_GLOBAL uint32_t lastReconnectAttempt _INIT(0); +WLED_GLOBAL unsigned long lastReconnectAttempt _INIT(0); WLED_GLOBAL bool interfacesInited _INIT(false); WLED_GLOBAL bool wasConnected _INIT(false);