diff --git a/platformio.ini b/platformio.ini index 59b43b1f..93ca4974 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1036,9 +1036,10 @@ HUB75_build_flags = -D NO_FAST_FUNCTIONS ;; If you are not using AdafruitGFX than you probably do not need this either, save memory/code size -D NO_CIE1931 ;; Do not use LED brightness compensation described in CIE 1931. We use FastLED dimming already -D S3_LCD_DIV_NUM=20 ;; Attempt to fix wifi performance issue when panel active with S3 chips -;; HUB75_lib_deps = https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-DMA.git @ 3.0.10 ;; does not work any more ;; HUB75_lib_deps = https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA.git @ 3.0.11 ;; breaks the build (2024-07-30) -HUB75_lib_deps = https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA.git#1e4c80a26454aca7b8129bd5a966b0af329d2703 ;; something strange is going on here ... +;; HUB75_lib_deps = https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA.git#1e4c80a26454aca7b8129bd5a966b0af329d2703 ;; 3.0.10 - something strange is going on here ... +HUB75_lib_deps = https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA.git#1e4c80a26454aca7b8129bd5a966b0af329d2703 ;; 3.0.10 - something strange is going on here ... +;; HUB75_lib_deps = https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA.git#c4ecdcfeeb5aa668d92ddf3c3c74bc93316f6e10 ;; 3.0.11 HUB75_lib_ignore = ESP32 HUB75 LED MATRIX PANEL DMA Display ;; to remove the HUB75 lib dependancy (saves a few bytes) NetDebug_build_flags = diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index f7c91594..709a5960 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -285,7 +285,7 @@ static volatile float micReal_max2 = 0.0f; // MicIn data max afte // some prototypes, to ensure consistent interfaces static float mapf(float x, float in_min, float in_max, float out_min, float out_max); // map function for float static float fftAddAvg(int from, int to); // average of several FFT result bins -void FFTcode(void * parameter); // audio processing task: read samples, run FFT, fill GEQ channels from FFT results +void FFTcode(void * parameter) __attribute__((noreturn)); // audio processing task: read samples, run FFT, fill GEQ channels from FFT results static void runMicFilter(uint16_t numSamples, float *sampleBuffer); // pre-filtering of raw samples (band-pass) static void postProcessFFTResults(bool noiseGateOpen, int numberOfChannels, bool i2sFastpath); // post-processing and post-amp of GEQ channels diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 9a6ba980..ad7e95d3 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -2148,7 +2148,7 @@ uint16_t mode_fire_2012() { // Step 4. Map from heat cells to LED colors for (int j = 0; j < SEGLEN; j++) { - SEGMENT.setPixelColor(indexToVStrip(j, stripNr), ColorFromPalette(SEGPALETTE, min(heat[j],byte(240)), 255, NOBLEND)); + SEGMENT.setPixelColor(indexToVStrip(j, stripNr), ColorFromPalette(SEGPALETTE, min(heat[j], byte(240)), 255, NOBLEND)); } } }; @@ -2156,14 +2156,19 @@ uint16_t mode_fire_2012() { for (int stripNr=0; stripNr> 2; + if (blurAmount > 48) blurAmount += blurAmount-48; // extra blur when slider > 192 (bush burn) + if (blurAmount < 16) SEGMENT.blurCols(SEGMENT.custom2 >> 1); // no side-burn when slider < 64 (faster) + else SEGMENT.blur(blurAmount); + } if (it != SEGENV.step) SEGENV.step = it; return FRAMETIME; } -static const char _data_FX_MODE_FIRE_2012[] PROGMEM = "Fire 2012@Cooling,Spark rate,,,Boost;;!;1.5d;sx=64,ix=160,m12=1"; // bars WLEDMM 1.5d, +static const char _data_FX_MODE_FIRE_2012[] PROGMEM = "Fire 2012@Cooling,Spark rate,,2D Blur,Boost;;!;1.5d;sx=64,ix=160,c2=128,m12=1"; // bars WLEDMM 1.5d, // ColorWavesWithPalettes by Mark Kriegsman: https://gist.github.com/kriegsman/8281905786e8b2632aeb @@ -4922,17 +4927,18 @@ uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulma } SEGMENT.fadeToBlackBy(16 + (SEGMENT.speed>>3)); // create fading trails - unsigned long t = strip.now/128; // timebase + const unsigned long ratio = 128; // rotation speed + unsigned long t = strip.now; // timebase // outer stars - 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); + for (unsigned i = 0; i < 8; i++) { + x = beatsin8(SEGMENT.custom1>>3, 0, cols - 1, 0, ((i % 2) ? 128 : 0) + (t * i)/ratio); + y = beatsin8(SEGMENT.intensity>>3, 0, rows - 1, 0, ((i % 2) ? 192 : 64) + (t * i)/ratio); SEGMENT.addPixelColorXY(x, y, CHSV(i*32, 255, 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); + x = beatsin8(SEGMENT.custom2>>3, cols/4, cols - 1 - cols/4, 0, ((i % 2) ? 128 : 0) + (t * i)/ratio); + y = beatsin8(SEGMENT.custom3 , rows/4, rows - 1 - rows/4, 0, ((i % 2) ? 192 : 64) + (t * i)/ratio); SEGMENT.addPixelColorXY(x, y, CHSV(i*32, 255, 255)); } // central white dot @@ -6270,6 +6276,10 @@ uint16_t mode_2Dfloatingblobs(void) { } SEGMENT.fadeToBlackBy(20); + bool drawAA = (SEGMENT.custom1 > 0) && (SEGMENT.custom1 < 6); //WLEDMM + const uint16_t minDim = min(cols, rows); // WLEDMM use smaller dimension to find good blob size + float max_grow = min(minDim/4.f,2.f); + if (minDim>=24) max_grow =(minDim/8.0f); // WLEDMM allow bigger blobs // Bounce balls around for (size_t i = 0; i < Amount; i++) { @@ -6278,18 +6288,18 @@ uint16_t mode_2Dfloatingblobs(void) { if (blob->grow[i]) { // enlarge radius until it is >= 4 blob->r[i] += (fabsf(blob->sX[i]) > fabsf(blob->sY[i]) ? fabsf(blob->sX[i]) : fabsf(blob->sY[i])) * 0.05f; - if (blob->r[i] >= min(cols/4.f,2.f)) { + if (blob->r[i] >= max_grow) { blob->grow[i] = false; } } else { // reduce radius until it is < 1 blob->r[i] -= (fabsf(blob->sX[i]) > fabsf(blob->sY[i]) ? fabsf(blob->sX[i]) : fabsf(blob->sY[i])) * 0.05f; - if (blob->r[i] < 1.f) { + if (blob->r[i] < 0.8f) { blob->grow[i] = true; } } uint32_t c = SEGMENT.color_from_palette(blob->color[i], false, false, 0); - if (blob->r[i] > 1.f) SEGMENT.fillCircle(roundf(blob->x[i]), roundf(blob->y[i]), roundf(blob->r[i]), c); + if (blob->r[i] > 1.f) SEGMENT.fillCircle(roundf(blob->x[i]), roundf(blob->y[i]), roundf(blob->r[i]), c, drawAA); else SEGMENT.setPixelColorXY((int)roundf(blob->x[i]), (int)roundf(blob->y[i]), c); // move x if (blob->x[i] + blob->r[i] >= cols - 1) blob->x[i] += (blob->sX[i] * ((cols - 1 - blob->x[i]) / blob->r[i] + 0.005f)); @@ -6662,20 +6672,26 @@ uint16_t mode_2DWaverly(void) { long t = strip.now / 2; for (int i = 0; i < cols; i++) { - uint16_t thisVal = volumeSmth*SEGMENT.intensity/64 * inoise8(i * 45 , t , t)/64; // WLEDMM back to SR code - uint16_t thisMax = map(thisVal, 0, 512, 0, rows); + //uint16_t thisVal = volumeSmth*SEGMENT.intensity/64 * inoise8(i * 45 , t , t)/64; // WLEDMM back to SR code + unsigned thisVal = unsigned(volumeSmth*SEGMENT.intensity) * inoise8(i * 45 , t , t) / (64*64); // WLEDMM same result but more accurate - for (int j = 0; j < thisMax; j++) { + //int thisMax = map(thisVal, 0, 512, 0, rows); + int thisMax = (thisVal * rows) / 512; // WLEDMM same result, just faster + int thisMax2 = min(int(rows), thisMax); // WLEDMM limit height to visible are + + for (int j = 0; j < thisMax2; j++) { + //int jmap = map(j, 0, thisMax, 250, 0); + int jmap = 250 - ((j * 250) / thisMax); // WLEDMM same result, just faster if (!SEGENV.check1) - SEGMENT.addPixelColorXY(i, j, ColorFromPalette(SEGPALETTE, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND)); - SEGMENT.addPixelColorXY((cols - 1) - i, (rows - 1) - j, ColorFromPalette(SEGPALETTE, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND)); + SEGMENT.addPixelColorXY(i, j, ColorFromPalette(SEGPALETTE, jmap, 255, LINEARBLEND)); + SEGMENT.addPixelColorXY((cols - 1) - i, (rows - 1) - j, ColorFromPalette(SEGPALETTE, jmap, 255, LINEARBLEND)); } } SEGMENT.blur(16); return FRAMETIME; } // mode_2DWaverly() -static const char _data_FX_MODE_2DWAVERLY[] PROGMEM = "Waverly ☾@Amplification,Sensitivity,,,,No Clouds,Sound Pressure,AGC debug;;!;2v;ix=64,si=0"; // Beatsin +static const char _data_FX_MODE_2DWAVERLY[] PROGMEM = "Waverly ☾@Fade Rate,Amplification,,,,No Clouds,Sound Pressure,AGC debug;;!;2v;ix=64,si=0"; // Beatsin #endif // WLED_DISABLE_2D @@ -7511,9 +7527,9 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch } // shift the pixels one pixel up - SEGMENT.setPixelColor(0, color); // if SEGLEN equals 1 this loop won't execute for (int i = SEGLEN - 1; i > 0; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left + SEGMENT.setPixelColor(0, color); } return FRAMETIME; diff --git a/wled00/FX.h b/wled00/FX.h index c32c3b47..f45556e6 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -33,7 +33,7 @@ bool canUseSerial(void); // WLEDMM implemented in wled_serial.cpp void strip_wait_until_idle(String whoCalledMe); // WLEDMM implemented in FX_fcn.cpp -bool strip_uses_global_leds(void); // WLEDMM implemented in FX_fcn.cpp +bool strip_uses_global_leds(void) __attribute__((pure)); // WLEDMM implemented in FX_fcn.cpp #define FASTLED_INTERNAL //remove annoying pragma messages #define USE_GET_MILLISECOND_TIMER @@ -434,6 +434,20 @@ typedef struct Segment { static size_t _usedSegmentData; // WLEDMM uint16_t is too small void setPixelColorXY_fast(int x, int y,uint32_t c, uint32_t scaled_col, int cols, int rows); // set relative pixel within segment with color - faster, but no error checking!!! +#ifdef WLEDMM_FASTPATH + // WLEDMM cache some values that won't change while drawing a frame + bool _isSimpleSegment = false; + bool _isValid2D = false; + uint8_t _brightness = 255; // final pixel brightness - including transitions and segment opacity + bool _firstFill = true; // dirty HACK support + uint16_t _2dWidth = 0; // virtualWidth + uint16_t _2dHeight = 0; // virtualHeight + + void setPixelColorXY_slow(int x, int y, uint32_t c); // set relative pixel within segment with color - full slow version +#else + void setPixelColorXY_slow(int x, int y, uint32_t c) { setPixelColorXY(x,y,c); } // not FASTPATH - slow is the normal +#endif + // perhaps this should be per segment, not static static CRGBPalette16 _currentPalette; // palette used for current effect (includes transition, used in color_from_palette()) @@ -587,6 +601,7 @@ typedef struct Segment { bool allocateData(size_t len); void deallocateData(void); void resetIfRequired(void); + void startFrame(void); // cache a few values that don't change while an effect is drawing /** * Flags that before the next effect is calculated, * the internal segment state should be reset. @@ -625,7 +640,7 @@ typedef struct Segment { void setPixelColor(float i, uint32_t c, bool aa = true); inline void setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = true) { setPixelColor(i, RGBW32(r,g,b,w), aa); } inline void setPixelColor(float i, CRGB c, bool aa = true) { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); } - uint32_t __attribute__((pure)) getPixelColor(int i); // WLEDMM attribute added + uint32_t __attribute__((pure)) getPixelColor(int i) const; // WLEDMM attribute added // 1D support functions (some implement 2D as well) void blur(uint8_t, bool smear = false); void fill(uint32_t c); @@ -637,11 +652,22 @@ typedef struct Segment { inline void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(n, RGBW32(r,g,b,w), fast); } // automatically inline inline void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); } // automatically inline void fadePixelColor(uint16_t n, uint8_t fade); - uint8_t get_random_wheel_index(uint8_t pos); + uint8_t get_random_wheel_index(uint8_t pos) const; uint32_t __attribute__((pure)) color_from_palette(uint_fast16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255); uint32_t __attribute__((pure)) color_wheel(uint8_t pos); + // 2D Blur: shortcuts for bluring columns or rows only (50% faster than full 2D blur) + inline void blurCols(fract8 blur_amount, bool smear = false) { // blur all columns + const unsigned cols = virtualWidth(); + for (unsigned k = 0; k < cols; k++) blurCol(k, blur_amount, smear); + } + inline void blurRows(fract8 blur_amount, bool smear = false) { // blur all rows + const unsigned rows = virtualHeight(); + for ( unsigned i = 0; i < rows; i++) blurRow(i, blur_amount, smear); + } + // 2D matrix +#ifndef WLEDMM_FASTPATH inline uint16_t virtualWidth() const { // WLEDMM use fast types, and make function inline uint_fast16_t groupLen = groupLength(); uint_fast16_t vWidth = ((transpose ? height() : width()) + groupLen - 1) / groupLen; @@ -654,22 +680,53 @@ typedef struct Segment { if (mirror_y) vHeight = (vHeight + 1) /2; // divide by 2 if mirror, leave at least a single LED return vHeight; } +#else + inline uint16_t virtualWidth() const { return(_2dWidth);} // WLEDMM get pre-calculated virtualWidth + inline uint16_t virtualHeight() const { return(_2dHeight);} // WLEDMM get pre-calculated virtualHeight + + uint16_t calc_virtualWidth() const { + uint_fast16_t groupLen = groupLength(); + uint_fast16_t vWidth = ((transpose ? height() : width()) + groupLen - 1) / groupLen; + if (mirror) vWidth = (vWidth + 1) /2; // divide by 2 if mirror, leave at least a single LED + return vWidth; + } + uint16_t calc_virtualHeight() const { + uint_fast16_t groupLen = groupLength(); + uint_fast16_t vHeight = ((transpose ? width() : height()) + groupLen - 1) / groupLen; + if (mirror_y) vHeight = (vHeight + 1) /2; // divide by 2 if mirror, leave at least a single LED + return vHeight; + } +#endif uint16_t nrOfVStrips(void) const; void createjMap(); //WLEDMM jMap void deletejMap(); //WLEDMM jMap #ifndef WLED_DISABLE_2D - inline uint16_t XY(uint_fast16_t x, uint_fast16_t y) { // support function to get relative index within segment (for leds[]) // WLEDMM inline for speed - uint_fast16_t width = virtualWidth(); // segment width in logical pixels - uint_fast16_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 + inline uint16_t XY(uint_fast16_t x, uint_fast16_t y) const { // support function to get relative index within segment (for leds[]) // WLEDMM inline for speed + uint_fast16_t width = max(uint16_t(1), virtualWidth()); // segment width in logical pixels -- softhack007 avoid div/0 + uint_fast16_t height = max(uint16_t(1), virtualHeight()); // segment height in logical pixels -- softhack007 avoid div/0 return (x%width) + (y%height) * width; } - //void setPixelColorXY_fast(int x, int y,uint32_t c); // set relative pixel within segment with color - wrapper for _fast +#ifdef WLEDMM_FASTPATH + // WLEDMM this is a "gateway" function - we either call _fast or fall back to "slow" + inline void setPixelColorXY(int x, int y, uint32_t col) { + if (!_isSimpleSegment) { // slow path + setPixelColorXY_slow(x, y, col); + } else { // fast path + // some sanity checks + if (!_isValid2D) return; // not active + if ((unsigned(x) >= _2dWidth) || (unsigned(y) >= _2dHeight)) return; // check if (x,y) are out-of-range - due to 2's complement, this also catches negative values + if (!_brightness && !transitional) return; // black-out + + uint32_t scaled_col = (_brightness == 255) ? col : color_fade(col, _brightness); // calculate final color + setPixelColorXY_fast(x, y, col, scaled_col, int(_2dWidth), int(_2dHeight)); // call "fast" function + } +} +#else void setPixelColorXY(int x, int y, uint32_t c); // set relative pixel within segment with color +#endif inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); } inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } @@ -679,7 +736,7 @@ typedef struct Segment { inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColorXY(x, y, RGBW32(r,g,b,w), aa); } inline void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), aa); } //#endif - uint32_t __attribute__((pure)) getPixelColorXY(int x, int y); + uint32_t __attribute__((pure)) getPixelColorXY(int x, int y) const; // 2D support functions void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend); inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), blend); } @@ -695,12 +752,12 @@ typedef struct Segment { void move(uint8_t dir, uint8_t delta, bool wrap = false); void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false); inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) { drawCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); } - void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false); - inline void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) { fillCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); } + void fillCircle(unsigned cx, unsigned cy, int radius, uint32_t col, bool soft); + inline void fillCircle(unsigned cx, unsigned cy, int radius, CRGB c, bool soft = false) { fillCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); } void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft = false, uint8_t depth = UINT8_MAX); inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, bool soft = false, uint8_t depth = UINT8_MAX) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0), soft, depth); } // automatic inline - void drawArc(uint16_t x0, uint16_t y0, uint16_t radius, uint32_t color, uint32_t fillColor = 0); - inline void drawArc(uint16_t x0, uint16_t y0, uint16_t radius, CRGB color, CRGB fillColor = BLACK) { drawArc(x0, y0, radius, RGBW32(color.r,color.g,color.b,0), RGBW32(fillColor.r,fillColor.g,fillColor.b,0)); } // automatic inline + void drawArc(unsigned x0, unsigned y0, int radius, uint32_t color, uint32_t fillColor = 0); + inline void drawArc(unsigned x0, unsigned y0, int radius, CRGB color, CRGB fillColor = BLACK) { drawArc(x0, y0, radius, RGBW32(color.r,color.g,color.b,0), RGBW32(fillColor.r,fillColor.g,fillColor.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); 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 wu_pixel(uint32_t x, uint32_t y, CRGB c); @@ -745,7 +802,7 @@ typedef struct Segment { inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0) {} inline void wu_pixel(uint32_t x, uint32_t y, CRGB c) {} #endif - uint8_t * getAudioPalette(int pal); //WLEDMM netmindz ar palette + uint8_t * getAudioPalette(int pal) const; //WLEDMM netmindz ar palette } segment; //static int segSize = sizeof(Segment); @@ -868,64 +925,64 @@ class WS2812FX { // 96 bytes bool checkSegmentAlignment(void), - hasRGBWBus(void), - hasCCTBus(void), + hasRGBWBus(void) const, + hasCCTBus(void) const, // return true if the strip is being sent pixel updates - isUpdating(void), + isUpdating(void) const, deserializeMap(uint8_t n=0), useLedsArray = false; - inline bool isServicing(void) { return _isServicing; } - inline bool hasWhiteChannel(void) {return _hasWhiteChannel;} - inline bool isOffRefreshRequired(void) {return _isOffRefreshRequired;} + inline bool isServicing(void) const { return _isServicing; } + inline bool hasWhiteChannel(void) const {return _hasWhiteChannel;} + inline bool isOffRefreshRequired(void) const {return _isOffRefreshRequired;} uint8_t paletteFade, paletteBlend, milliampsPerLed, cctBlending, - getActiveSegmentsNum(void), - getFirstSelectedSegId(void), - getLastActiveSegmentId(void), - getActiveSegsLightCapabilities(bool selectedOnly = false), + getActiveSegmentsNum(void) const, + getFirstSelectedSegId(void) __attribute__((pure)), + getLastActiveSegmentId(void) const, + getActiveSegsLightCapabilities(bool selectedOnly = false) __attribute__((pure)), setPixelSegment(uint8_t n); - inline uint8_t getBrightness(void) { return _brightness; } - inline uint8_t getMaxSegments(void) { return MAX_NUM_SEGMENTS; } // returns maximum number of supported segments (fixed value) - inline uint8_t getSegmentsNum(void) { return _segments.size(); } // returns currently present segments - inline uint8_t getCurrSegmentId(void) { return _segment_index; } - inline uint8_t getMainSegmentId(void) { return _mainSegment; } - inline uint8_t getPaletteCount() { return 13 + GRADIENT_PALETTE_COUNT; } // will only return built-in palette count - inline uint8_t getTargetFps() { return _targetFps; } - inline uint8_t getModeCount() { return _modeCount; } + inline uint8_t getBrightness(void) const { return _brightness; } + inline uint8_t getMaxSegments(void) const { return MAX_NUM_SEGMENTS; } // returns maximum number of supported segments (fixed value) + inline uint8_t getSegmentsNum(void) const { return _segments.size(); } // returns currently present segments + inline uint8_t getCurrSegmentId(void) const { return _segment_index; } + inline uint8_t getMainSegmentId(void) const { return _mainSegment; } + inline uint8_t getPaletteCount() const { return 13 + GRADIENT_PALETTE_COUNT; } // will only return built-in palette count + inline uint8_t getTargetFps() const { return _targetFps; } + inline uint8_t getModeCount() const { return _modeCount; } uint16_t ablMilliampsMax, currentMilliamps, - getLengthPhysical(void), - __attribute__((pure)) getLengthTotal(void), // will include virtual/nonexistent pixels in matrix //WLEDMM attribute added - getFps(); + getLengthPhysical(void) const, + __attribute__((pure)) getLengthTotal(void) const, // will include virtual/nonexistent pixels in matrix //WLEDMM attribute added + getFps() const; - inline uint16_t getFrameTime(void) { return _frametime; } - inline uint16_t getMinShowDelay(void) { return MIN_SHOW_DELAY; } - inline uint16_t getLength(void) { return _length; } // 2D matrix may have less pixels than W*H - inline uint16_t getTransition(void) { return _transitionDur; } + inline uint16_t getFrameTime(void) const { return _frametime; } + inline uint16_t getMinShowDelay(void) const { return MIN_SHOW_DELAY; } + inline uint16_t getLength(void) const { return _length; } // 2D matrix may have less pixels than W*H + inline uint16_t getTransition(void) const { return _transitionDur; } uint32_t now, timebase; - uint32_t __attribute__((pure)) getPixelColor(uint_fast16_t); // WLEDMM attribute pure = does not have side-effects + uint32_t __attribute__((pure)) getPixelColor(uint_fast16_t) const; // WLEDMM attribute pure = does not have side-effects - inline uint32_t getLastShow(void) { return _lastShow; } - inline uint32_t segColor(uint8_t i) { return _colors_t[i]; } + inline uint32_t getLastShow(void) const { return _lastShow; } + inline uint32_t segColor(uint8_t i) const { return _colors_t[i]; } const char * - getModeData(uint8_t id = 0) { return (id && id<_modeCount) ? _modeData[id] : PSTR("Solid"); } + getModeData(uint8_t id = 0) const { return (id && id<_modeCount) ? _modeData[id] : PSTR("Solid"); } const char ** getModeDataSrc(void) { return &(_modeData[0]); } // vectors use arrays for underlying data - Segment& getSegment(uint8_t id); + Segment& getSegment(uint8_t id) __attribute__((pure)); inline Segment& getFirstSelectedSeg(void) { return _segments[getFirstSelectedSegId()]; } inline Segment& getMainSegment(void) { return _segments[getMainSegmentId()]; } inline Segment* getSegments(void) { return &(_segments[0]); } @@ -992,7 +1049,7 @@ class WS2812FX { // 96 bytes inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } uint32_t - getPixelColorXY(uint16_t, uint16_t); + getPixelColorXY(uint16_t, uint16_t) const; // end 2D support diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 83ec0f54..9660df63 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -171,13 +171,31 @@ void WS2812FX::setUpMatrix() { //WLEDMM: no resetSegments here, only do it in set.cpp/handleSettingsSet - as we want t0 maintain the segment settings after setup has changed } } + +#ifdef WLED_ENABLE_HUB75MATRIX + // softhack007 hack: delete mapping table in case it only contains "identity" + if (customMappingTable != nullptr && customMappingTableSize > 0) { + bool isIdentity = true; + for (size_t i = 0; (i< customMappingSize) && isIdentity; i++) { //WLEDMM use customMappingTableSize + if (customMappingTable[i] != (uint16_t)i ) isIdentity = false; + } + if (isIdentity) { + free(customMappingTable); customMappingTable = nullptr; + USER_PRINTF("!setupmatrix: customMappingTable is not needed. Dropping %d bytes.\n", customMappingTableSize * sizeof(uint16_t)); + customMappingTableSize = 0; + customMappingSize = 0; + loadedLedmap = 0; //WLEDMM + } + } +#endif + #else isMatrix = false; // no matter what config says #endif } // absolute matrix version of setPixelColor(), without error checking -void IRAM_ATTR WS2812FX::setPixelColorXY_fast(int x, int y, uint32_t col) //WLEDMM: IRAM_ATTR conditionally +void IRAM_ATTR __attribute__((hot)) WS2812FX::setPixelColorXY_fast(int x, int y, uint32_t col) //WLEDMM: IRAM_ATTR conditionally { uint_fast16_t index = y * Segment::maxWidth + x; if (index < customMappingSize) index = customMappingTable[index]; @@ -200,7 +218,7 @@ void IRAM_ATTR_YN WS2812FX::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM } // returns RGBW values of pixel -uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) { +uint32_t __attribute__((hot)) WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) const { #ifndef WLED_DISABLE_2D uint_fast16_t index = (y * Segment::maxWidth + x); //WLEDMM: use fast types #else @@ -217,13 +235,29 @@ uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) { #ifndef WLED_DISABLE_2D +// WLEDMM cache some values so we don't need to re-calc then for each pixel +void Segment::startFrame(void) { +#ifdef WLEDMM_FASTPATH + _isValid2D = isActive() && is2D(); + _brightness = currentBri(on ? opacity : 0); + _isSimpleSegment = (grouping == 1) && (spacing == 0); // we can handle pixels faster when no grouping or spacing is involved + // if (reverse_y) _isSimpleSegment = false; // for A/B testing + _2dWidth = is2D() ? calc_virtualWidth() : virtualLength(); + _2dHeight = calc_virtualHeight(); + #if 0 && defined(WLED_ENABLE_HUB75MATRIX) + _firstFill = true; // dirty HACK + #endif +#endif +} +// WLEDMM end + // XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element) // WLEDMM Segment::XY()is declared inline, see FX.h // Simplified version of Segment::setPixelColorXY - without error checking. Does not support grouping or spacing // * expects scaled color (final brightness) as additional input parameter, plus segment virtualWidth() and virtualHeight() -void IRAM_ATTR Segment::setPixelColorXY_fast(int x, int y, uint32_t col, uint32_t scaled_col, int cols, int rows) //WLEDMM +void IRAM_ATTR __attribute__((hot)) Segment::setPixelColorXY_fast(int x, int y, uint32_t col, uint32_t scaled_col, int cols, int rows) //WLEDMM { unsigned i = UINT_MAX; bool sameColor = false; @@ -266,7 +300,11 @@ void IRAM_ATTR Segment::setPixelColorXY_fast(int x, int y, uint32_t col, uint32_ // normal Segment::setPixelColorXY with error checking, and support for grouping / spacing +#ifdef WLEDMM_FASTPATH +void IRAM_ATTR_YN Segment::setPixelColorXY_slow(int x, int y, uint32_t col) //WLEDMM: IRAM_ATTR conditionally, renamed to "_slow" +#else void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM: IRAM_ATTR conditionally +#endif { if (Segment::maxHeight==1) return; // not a matrix set-up const int_fast16_t cols = virtualWidth(); // WLEDMM optimization @@ -386,7 +424,7 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa, bool fast } // returns RGBW values of pixel -uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) { +uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) const { if (x<0 || y<0 || !isActive()) return 0; // not active or out-of range if (ledsrgb) { int i = XY(x,y); @@ -520,7 +558,7 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) { for (int j = 0; j < dim1; j++) { int x = vertical ? i : j; int y = vertical ? j : i; - setPixelColorXY(x, y, out[j]); + if (in[j] != out[j]) setPixelColorXY(x, y, out[j]); } } @@ -634,19 +672,26 @@ void Segment::drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, } // by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs -void Segment::fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, bool soft) { - if (!isActive() || radius == 0) return; // not active +void Segment::fillCircle(unsigned cx, unsigned cy, int radius, uint32_t col, bool soft) { + if (!isActive() || radius <= 0) return; // not active // draw soft bounding circle if (soft) drawCircle(cx, cy, radius, col, soft); - // fill it const int cols = virtualWidth(); const int rows = virtualHeight(); - for (int y = -radius; y <= radius; y++) { - for (int x = -radius; x <= radius; x++) { - if (x * x + y * y <= radius * radius && - int16_t(cx)+x>=0 && int16_t(cy)+y>=0 && - int16_t(cx)+x 3) && !soft) ? 1:0); // WLEDMM pre-compute r^2; '-1' removes spikes from bigger blobs + // WLEDMM pre-compute boundaries + const int startx = max(-radius, -int(cx)); + const int endx = min(radius, cols-1-int(cx)); + const int starty = max(-radius, -int(cy)); + const int endy = min(radius, rows-1-int(cy)); + + // fill it - WLEDMM optimized + for (int y = starty; y <= endy; y++) { + for (int x = startx; x <= endx; x++) { + if ((x * x + y * y) <= maxRadius2) { setPixelColorXY(cx + x, cy + y, col); + } } } } @@ -673,7 +718,11 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3 uint32_t scaled_col = c; if (simpleSegment) { // segment brightness must be pre-calculated for the "fast" setPixelColorXY variant! + #ifdef WLEDMM_FASTPATH + uint8_t _bri_t = _brightness; + #else uint8_t _bri_t = currentBri(on ? opacity : 0); + #endif if (!_bri_t && !transitional) return; if (_bri_t < 255) scaled_col = color_fade(c, _bri_t); } @@ -698,7 +747,7 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3 // single pixel (line length == 0) if (dx+dy == 0) { if (simpleSegment) setPixelColorXY_fast(x0, y0, c, scaled_col, cols, rows); - else setPixelColorXY(x0, y0, c); + else setPixelColorXY_slow(x0, y0, c); return; } @@ -734,7 +783,7 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3 for (;;) { // if (x0 >= cols || y0 >= rows) break; // WLEDMM we hit the edge - should never happen if (simpleSegment) setPixelColorXY_fast(x0, y0, c, scaled_col, cols, rows); - else setPixelColorXY(x0, y0, c); + else setPixelColorXY_slow(x0, y0, c); if (x0==x1 && y0==y1) break; int e2 = err; if (e2 >-dx) { err -= dy; x0 += sx; } @@ -743,27 +792,34 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3 } } -void Segment::drawArc(uint16_t x0, uint16_t y0, uint16_t radius, uint32_t color, uint32_t fillColor) { - if (!isActive()) return; // not active - // float step = degrees / (2.85f*MAX(radius,1)); - // for (float rad = 0.0f; rad <= degrees+step/2; rad += step) { - // // may want to try float version as well (with or without antialiasing) - // int x = roundf(sin_t(rad) * radius); - // int y = roundf(cos_t(rad) * radius); - // setPixelColorXY(x+x0, y+y0, c); - // } - float minradius = radius - .5; - float maxradius = radius + .5; - for (int x=0; x= minradius * minradius && newX*newX + newY*newY <= maxradius * maxradius) + for (int x=startx; x= minradius2) && (distance2 <= maxradius2)) { setPixelColorXY(x, y, color); + } else { if (fillColor != 0) - if (newX*newX + newY*newY < minradius * minradius) + if (distance2 < minradius2) setPixelColorXY(x, y, fillColor); + } } } @@ -859,10 +915,11 @@ void Segment::wu_pixel(uint32_t x, uint32_t y, CRGB c) { //awesome wu_pixel // multiply the intensities by the colour, and saturating-add them to the pixels for (int i = 0; i < 4; i++) { CRGB led = getPixelColorXY((x >> 8) + (i & 1), (y >> 8) + ((i >> 1) & 1)); + CRGB oldLed = led; led.r = qadd8(led.r, c.r * wu[i] >> 8); led.g = qadd8(led.g, c.g * wu[i] >> 8); led.b = qadd8(led.b, c.b * wu[i] >> 8); - setPixelColorXY(int((x >> 8) + (i & 1)), int((y >> 8) + ((i >> 1) & 1)), led); + if (led != oldLed) setPixelColorXY(int((x >> 8) + (i & 1)), int((y >> 8) + ((i >> 1) & 1)), led); // WLEDMM don't repaint same color } } #undef WU_WEIGHT diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 65849ff1..0f6eb6da 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -132,7 +132,7 @@ void Segment::allocLeds() { ledsrgbSize = ledsrgb?size:0; if (ledsrgb == nullptr) { USER_PRINTLN("allocLeds failed!!"); - errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag + errorFlag = ERR_LOW_BUF; // WLEDMM raise errorflag } } else { @@ -266,6 +266,7 @@ void Segment::resetIfRequired() { deallocateData(); next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0; reset = false; // setOption(SEG_OPTION_RESET, false); + startFrame(); // WLEDMM update cached propoerties } } @@ -673,7 +674,11 @@ class JMapC { if (size > 0) return size; else +#ifndef WLEDMM_FASTPATH return SEGMENT.virtualWidth() * SEGMENT.virtualHeight(); //pixels +#else + return SEGMENT.calc_virtualWidth() * SEGMENT.calc_virtualHeight(); // calc pixel sizes +#endif } void setPixelColor(uint16_t i, uint32_t col) { updatejMapDoc(); @@ -765,7 +770,11 @@ class JMapC { jMapFile.close(); maxWidth++; maxHeight++; +#ifndef WLEDMM_FASTPATH scale = min(SEGMENT.virtualWidth() / maxWidth, SEGMENT.virtualHeight() / maxHeight); // WLEDMM use native min/max +#else + scale = min(SEGMENT.calc_virtualWidth() / maxWidth, SEGMENT.calc_virtualHeight() / maxHeight); // WLEDMM re-calc width/heiht from active settings +#endif dataSize += sizeof(jVectorMap); USER_PRINT("dataSize "); USER_PRINT(dataSize); @@ -803,10 +812,13 @@ constexpr int Pinwheel_Size_Medium = 32; // larger than this -> use "Big" constexpr int Pinwheel_Steps_Big = 304; // no holes up to 50x50 constexpr int Pinwheel_Size_Big = 50; // larger than this -> use "XL" constexpr int Pinwheel_Steps_XL = 368; +constexpr int Pinwheel_Size_XL = 58; // larger than this -> use "XXL" +constexpr int Pinwheel_Steps_XXL = 456; constexpr float Int_to_Rad_Small = (DEG_TO_RAD * 360) / Pinwheel_Steps_Small; // conversion: from 0...72 to Radians constexpr float Int_to_Rad_Med = (DEG_TO_RAD * 360) / Pinwheel_Steps_Medium; // conversion: from 0...192 to Radians constexpr float Int_to_Rad_Big = (DEG_TO_RAD * 360) / Pinwheel_Steps_Big; // conversion: from 0...304 to Radians constexpr float Int_to_Rad_XL = (DEG_TO_RAD * 360) / Pinwheel_Steps_XL; // conversion: from 0...368 to Radians +constexpr float Int_to_Rad_XXL = (DEG_TO_RAD * 360) / Pinwheel_Steps_XXL; // conversion: from 0...456 to Radians constexpr int Fixed_Scale = 512; // fixpoint scaling factor (9bit for fraction) @@ -816,8 +828,9 @@ static float getPinwheelAngle(int i, int vW, int vH) { if (maxXY <= Pinwheel_Size_Small) return float(i) * Int_to_Rad_Small; if (maxXY <= Pinwheel_Size_Medium) return float(i) * Int_to_Rad_Med; if (maxXY <= Pinwheel_Size_Big) return float(i) * Int_to_Rad_Big; + if (maxXY <= Pinwheel_Size_XL) return float(i) * Int_to_Rad_XL; // else - return float(i) * Int_to_Rad_XL; + return float(i) * Int_to_Rad_XXL; } // Pinwheel helper function: matrix dimensions to number of rays static int getPinwheelLength(int vW, int vH) { @@ -825,8 +838,9 @@ static int getPinwheelLength(int vW, int vH) { if (maxXY <= Pinwheel_Size_Small) return Pinwheel_Steps_Small; if (maxXY <= Pinwheel_Size_Medium) return Pinwheel_Steps_Medium; if (maxXY <= Pinwheel_Size_Big) return Pinwheel_Steps_Big; + if (maxXY <= Pinwheel_Size_XL) return Pinwheel_Steps_XL; // else - return Pinwheel_Steps_XL; + return Pinwheel_Steps_XXL; } #endif @@ -873,7 +887,7 @@ uint16_t Segment::virtualLength() const { } //WLEDMM used for M12_sBlock -void xyFromBlock(uint16_t &x,uint16_t &y, uint16_t i, uint16_t vW, uint16_t vH, uint16_t vStrip) { +static void xyFromBlock(uint16_t &x,uint16_t &y, uint16_t i, uint16_t vW, uint16_t vH, uint16_t vStrip) { float i2; if (i<=SEGLEN*0.25) { //top, left to right i2 = i/(SEGLEN*0.25); @@ -898,7 +912,7 @@ void xyFromBlock(uint16_t &x,uint16_t &y, uint16_t i, uint16_t vW, uint16_t vH, } -void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATTR conditionally +void IRAM_ATTR_YN __attribute__((hot)) Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATTR conditionally { if (!isActive()) return; // not active #ifndef WLED_DISABLE_2D @@ -1057,7 +1071,7 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATT // set pixel if (x != lastX || y != lastY) { // only paint if pixel position is different if (simpleSegment) setPixelColorXY_fast(x, y, col, scaled_col, vW, vH); - else setPixelColorXY(x, y, col); + else setPixelColorXY_slow(x, y, col); } lastX = x; lastY = y; @@ -1162,7 +1176,7 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa) } } -uint32_t Segment::getPixelColor(int i) +uint32_t __attribute__((hot)) Segment::getPixelColor(int i) const { if (!isActive()) return 0; // not active #ifndef WLED_DISABLE_2D @@ -1336,8 +1350,20 @@ void Segment::refreshLightCapabilities() { /* * Fills segment with color - WLEDMM using faster sPC if possible */ -void Segment::fill(uint32_t c) { +void __attribute__((hot)) Segment::fill(uint32_t c) { if (!isActive()) return; // not active + + #if 0 && defined(WLED_ENABLE_HUB75MATRIX) && defined(WLEDMM_FASTPATH) + // DIRTY HACK - this ignores the first fill(black) in each frame, knowing that HUB75 has already blanked out the display. + if (_firstFill) { + _firstFill = false; + if (c == BLACK) { + if (ledsrgb && ledsrgbSize > 0) memset(ledsrgb, 0, ledsrgbSize); + return; + } + } + #endif + const uint_fast16_t cols = is2D() ? virtualWidth() : virtualLength(); // WLEDMM use fast int types const uint_fast16_t rows = virtualHeight(); // will be 1 for 1D @@ -1353,7 +1379,7 @@ void Segment::fill(uint32_t c) { // fill 2D segment for(int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { if (simpleSegment) setPixelColorXY_fast(x, y, c, scaled_col, cols, rows); - else setPixelColorXY(x, y, c); + else setPixelColorXY_slow(x, y, c); } } else { // fill 1D strip for (int x = 0; x < cols; x++) setPixelColor(x, c); @@ -1408,7 +1434,7 @@ void Segment::fade_out(uint8_t rate) { int g2 = G(color2); int b2 = B(color2); - for (uint_fast16_t y = 0; y < rows; y++) for (uint_fast16_t x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { uint32_t color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x); if (color == color2) continue; // WLEDMM speedup - pixel color = target color, so nothing to do int w1 = W(color); @@ -1426,15 +1452,17 @@ void Segment::fade_out(uint8_t rate) { rdelta += (r2 == r1) ? 0 : (r2 > r1) ? 1 : -1; gdelta += (g2 == g1) ? 0 : (g2 > g1) ? 1 : -1; bdelta += (b2 == b1) ? 0 : (b2 > b1) ? 1 : -1; + uint32_t colorNew = RGBW32(r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta); // WLEDMM - //if ((wdelta == 0) && (rdelta == 0) && (gdelta == 0) && (bdelta == 0)) continue; // WLEDMM delta = zero => no change // causes problem with text overlay - if (is2D()) setPixelColorXY((uint16_t)x, (uint16_t)y, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta); - else setPixelColor((uint16_t)x, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta); + if (colorNew != color) { // WLEDMM speedup - do not repaint the same color + if (is2D()) setPixelColorXY(x, y, colorNew); + else setPixelColor(x, colorNew); + } } } // fades all pixels to black using nscale8() -void Segment::fadeToBlackBy(uint8_t fadeBy) { +void __attribute__((hot)) Segment::fadeToBlackBy(uint8_t fadeBy) { if (!isActive() || fadeBy == 0) return; // optimization - no scaling to apply const uint_fast16_t cols = is2D() ? virtualWidth() : virtualLength(); // WLEDMM use fast int types const uint_fast16_t rows = virtualHeight(); // will be 1 for 1D @@ -1442,8 +1470,11 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) { // WLEDMM minor optimization if(is2D()) { - for (uint_fast16_t y = 0; y < rows; y++) for (uint_fast16_t x = 0; x < cols; x++) { - setPixelColorXY((uint16_t)x, (uint16_t)y, CRGB(getPixelColorXY(x,y)).nscale8(scaledown)); + for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { + uint32_t cc = getPixelColorXY(x,y); // WLEDMM avoid RGBW32 -> CRGB -> RGBW32 conversion + uint32_t cc2 = color_fade(cc, scaledown); // fade + //if (cc2 != cc) // WLEDMM only re-paint if faded color is different - disabled - causes problem with text overlay + setPixelColorXY((uint16_t)x, (uint16_t)y, cc2); } } else { for (uint_fast16_t x = 0; x < cols; x++) { @@ -1455,7 +1486,7 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) { /* * blurs segment content, source: FastLED colorutils.cpp */ -void Segment::blur(uint8_t blur_amount, bool smear) { +void __attribute__((hot)) Segment::blur(uint8_t blur_amount, bool smear) { if (!isActive() || blur_amount == 0) return; // optimization: 0 means "don't blur" #ifndef WLED_DISABLE_2D if (is2D()) { @@ -1516,7 +1547,7 @@ uint32_t Segment::color_wheel(uint8_t pos) { /* * Returns a new, random wheel index with a minimum distance of 42 from pos. */ -uint8_t Segment::get_random_wheel_index(uint8_t pos) { // WLEDMM use fast int types, use native min/max +uint8_t Segment::get_random_wheel_index(uint8_t pos) const { // WLEDMM use fast int types, use native min/max uint_fast8_t r = 0, x = 0, y = 0, d = 0; while(d < 42) { @@ -1537,7 +1568,7 @@ uint8_t Segment::get_random_wheel_index(uint8_t pos) { // WLEDMM use fast int ty * @param pbri Value to scale the brightness of the returned color by. Default is 255. (no scaling) * @returns Single color from palette */ -uint32_t Segment::color_from_palette(uint_fast16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri) // WLEDMM use fast int types +uint32_t __attribute__((hot)) Segment::color_from_palette(uint_fast16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri) // WLEDMM use fast int types { // default palette or no RGB support on segment if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) { @@ -1557,7 +1588,7 @@ uint32_t Segment::color_from_palette(uint_fast16_t i, bool mapping, bool wrap, u } //WLEDMM netmindz ar palette -uint8_t * Segment::getAudioPalette(int pal) { +uint8_t * Segment::getAudioPalette(int pal) const { // https://forum.makerforums.info/t/hi-is-it-possible-to-define-a-gradient-palette-at-runtime-the-define-gradient-palette-uses-the/63339 um_data_t *um_data; @@ -1834,6 +1865,7 @@ 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]); + seg.startFrame(); // WLEDMM // 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()); @@ -1865,7 +1897,7 @@ void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col) busses.setPixelColor(i, col); } -uint32_t WS2812FX::getPixelColor(uint_fast16_t i) // WLEDMM fast int types +uint32_t WS2812FX::getPixelColor(uint_fast16_t i) const // WLEDMM fast int types { if (i < customMappingSize) i = customMappingTable[i]; if (i >= _length) return 0; @@ -2003,7 +2035,7 @@ void WS2812FX::show(void) { * Returns a true value if any of the strips are still being updated. * On some hardware (ESP32), strip updates are done asynchronously. */ -bool WS2812FX::isUpdating() { +bool WS2812FX::isUpdating() const { return !busses.canAllShow(); } @@ -2011,7 +2043,7 @@ bool WS2812FX::isUpdating() { * Returns the refresh rate of the LED strip. Useful for finding out whether a given setup is fast enough. * Only updates on show() or is set to 0 fps if last show is more than 2 secs ago, so accuracy varies */ -uint16_t WS2812FX::getFps() { +uint16_t WS2812FX::getFps() const { if (millis() - _lastShow > 2000) return 0; #ifdef ARDUINO_ARCH_ESP32 return ((_cumulativeFps500 + 250) / 500); // +250 for proper rounding @@ -2102,14 +2134,14 @@ void WS2812FX::setMainSegmentId(uint8_t n) { return; } -uint8_t WS2812FX::getLastActiveSegmentId(void) { +uint8_t WS2812FX::getLastActiveSegmentId(void) const { for (size_t i = _segments.size() -1; i > 0; i--) { if (_segments[i].isActive()) return i; } return 0; } -uint8_t WS2812FX::getActiveSegmentsNum(void) { +uint8_t WS2812FX::getActiveSegmentsNum(void) const { uint8_t c = 0; for (size_t i = 0; i < _segments.size(); i++) { if (_segments[i].isActive()) c++; @@ -2117,13 +2149,13 @@ uint8_t WS2812FX::getActiveSegmentsNum(void) { return c; } -uint16_t WS2812FX::getLengthTotal(void) { // WLEDMM fast int types +uint16_t WS2812FX::getLengthTotal(void) const { // WLEDMM fast int types uint_fast16_t len = Segment::maxWidth * Segment::maxHeight; // will be _length for 1D (see finalizeInit()) but should cover whole matrix for 2D if (isMatrix && _length > len) len = _length; // for 2D with trailing strip return len; } -uint16_t WS2812FX::getLengthPhysical(void) { // WLEDMM fast int types +uint16_t WS2812FX::getLengthPhysical(void) const { // WLEDMM fast int types uint_fast16_t len = 0; for (unsigned b = 0; b < busses.getNumBusses(); b++) { // WLEDMM use native (fast) types Bus *bus = busses.getBus(b); @@ -2136,7 +2168,7 @@ uint16_t WS2812FX::getLengthPhysical(void) { // WLEDMM fast int types //used for JSON API info.leds.rgbw. Little practical use, deprecate with info.leds.rgbw. //returns if there is an RGBW bus (supports RGB and White, not only white) //not influenced by auto-white mode, also true if white slider does not affect output white channel -bool WS2812FX::hasRGBWBus(void) { +bool WS2812FX::hasRGBWBus(void) const { for (unsigned b = 0; b < busses.getNumBusses(); b++) { // WLEDMM use native (fast) types Bus *bus = busses.getBus(b); if (bus == nullptr || bus->getLength()==0) break; @@ -2145,7 +2177,7 @@ bool WS2812FX::hasRGBWBus(void) { return false; } -bool WS2812FX::hasCCTBus(void) { +bool WS2812FX::hasCCTBus(void) const { if (cctFromRgb && !correctWB) return false; for (unsigned b = 0; b < busses.getNumBusses(); b++) { // WLEDMM use native (fast) types Bus *bus = busses.getBus(b); diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index a24fdf28..66cbd7f3 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -9,6 +9,36 @@ #include "bus_wrapper.h" #include "bus_manager.h" +// WLEDMM functions to get/set bits in an array - based on functions created by Brandon for GOL +// toDo : make this a class that's completely defined in a header file +bool getBitFromArray(const uint8_t* byteArray, size_t position) { // get bit value + size_t byteIndex = position / 8; + unsigned bitIndex = position % 8; + uint8_t byteValue = byteArray[byteIndex]; + return (byteValue >> bitIndex) & 1; +} + +void setBitInArray(uint8_t* byteArray, size_t position, bool value) { // set bit - with error handling for nullptr + //if (byteArray == nullptr) return; + size_t byteIndex = position / 8; + unsigned bitIndex = position % 8; + if (value) + byteArray[byteIndex] |= (1 << bitIndex); + else + byteArray[byteIndex] &= ~(1 << bitIndex); +} + +size_t getBitArrayBytes(size_t num_bits) { // number of bytes needed for an array with num_bits bits + return (num_bits + 7) / 8; +} + +void setBitArray(uint8_t* byteArray, size_t numBits, bool value) { // set all bits to same value + if (byteArray == nullptr) return; + size_t len = getBitArrayBytes(numBits); + if (value) memset(byteArray, 0xFF, len); + else memset(byteArray, 0x00, len); +} + //WLEDMM: #define DEBUGOUT(x) netDebugEnabled?NetDebug.print(x):Serial.print(x) not supported in this file as netDebugEnabled not in scope #if 0 //colors.cpp @@ -49,13 +79,6 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte #include "wled.h" #endif -//color mangling macros -#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b)))) -#define R(c) (byte((c) >> 16)) -#define G(c) (byte((c) >> 8)) -#define B(c) (byte(c)) -#define W(c) (byte((c) >> 24)) - void ColorOrderMap::add(uint16_t start, uint16_t len, uint8_t colorOrder) { if (_count >= WLED_MAX_COLOR_ORDER_MAPPINGS) { @@ -86,7 +109,7 @@ uint8_t IRAM_ATTR ColorOrderMap::getPixelColorOrder(uint16_t pix, uint8_t defaul } -uint32_t Bus::autoWhiteCalc(uint32_t c) { +uint32_t Bus::autoWhiteCalc(uint32_t c) const { uint8_t aWM = _autoWhiteMode; if (_gAWM != AW_GLOBAL_DISABLED) aWM = _gAWM; if (aWM == RGBW_MODE_MANUAL_ONLY) return c; @@ -139,7 +162,7 @@ void BusDigital::show() { PolyBus::show(_busPtr, _iType); } -bool BusDigital::canShow() { +bool BusDigital::canShow() const { return PolyBus::canShow(_busPtr, _iType); } @@ -182,7 +205,7 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { PolyBus::setPixelColor(_busPtr, _iType, pix, c, co); } -uint32_t IRAM_ATTR_YN BusDigital::getPixelColor(uint16_t pix) { +uint32_t IRAM_ATTR_YN BusDigital::getPixelColor(uint16_t pix) const { if (reversed) pix = _len - pix -1; else pix += _skip; uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder); @@ -200,7 +223,7 @@ uint32_t IRAM_ATTR_YN BusDigital::getPixelColor(uint16_t pix) { return PolyBus::getPixelColor(_busPtr, _iType, pix, co); } -uint8_t BusDigital::getPins(uint8_t* pinArray) { +uint8_t BusDigital::getPins(uint8_t* pinArray) const { uint8_t numPins = IS_2PIN(_type) ? 2 : 1; for (uint8_t i = 0; i < numPins; i++) pinArray[i] = _pins[i]; return numPins; @@ -317,7 +340,7 @@ void BusPwm::setPixelColor(uint16_t pix, uint32_t c) { } //does no index check -uint32_t BusPwm::getPixelColor(uint16_t pix) { +uint32_t BusPwm::getPixelColor(uint16_t pix) const { if (!_valid) return 0; #if 1 // WLEDMM stick with the old code - we don't have cctICused @@ -356,7 +379,7 @@ void BusPwm::show() { } } -uint8_t BusPwm::getPins(uint8_t* pinArray) { +uint8_t BusPwm::getPins(uint8_t* pinArray) const { if (!_valid) return 0; uint8_t numPins = NUM_PWM_PINS(_type); for (uint8_t i = 0; i < numPins; i++) { @@ -408,7 +431,7 @@ void BusOnOff::setPixelColor(uint16_t pix, uint32_t c) { _data = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0; } -uint32_t BusOnOff::getPixelColor(uint16_t pix) { +uint32_t BusOnOff::getPixelColor(uint16_t pix) const { if (!_valid) return 0; return RGBW32(_data, _data, _data, _data); } @@ -418,7 +441,7 @@ void BusOnOff::show() { digitalWrite(_pin, reversed ? !(bool)_data : (bool)_data); } -uint8_t BusOnOff::getPins(uint8_t* pinArray) { +uint8_t BusOnOff::getPins(uint8_t* pinArray) const { if (!_valid) return 0; pinArray[0] = _pin; return 1; @@ -467,7 +490,7 @@ void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) { if (_rgbw) _data[offset+3] = W(c); } -uint32_t BusNetwork::getPixelColor(uint16_t pix) { +uint32_t BusNetwork::getPixelColor(uint16_t pix) const { if (!_valid || pix >= _len) return 0; uint16_t offset = pix * _UDPchannels; return RGBW32(_data[offset], _data[offset+1], _data[offset+2], _rgbw ? (_data[offset+3] << 24) : 0); @@ -480,7 +503,7 @@ void BusNetwork::show() { _broadcastLock = false; } -uint8_t BusNetwork::getPins(uint8_t* pinArray) { +uint8_t BusNetwork::getPins(uint8_t* pinArray) const { for (uint8_t i = 0; i < 4; i++) { pinArray[i] = _client[i]; } @@ -501,9 +524,9 @@ void BusNetwork::cleanup() { BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { + _valid = false; mxconfig.double_buff = false; // default to off, known to cause issue with some effects but needs more memory - fourScanPanel = nullptr; switch(bc.type) { @@ -546,7 +569,7 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh USER_PRINTLN("MatrixPanel_I2S_DMA - Matrix Portal S3 config"); - mxconfig.double_buff = true; // <------------- Turn on double buffer + //mxconfig.double_buff = true; // <------------- Turn on double buffer mxconfig.gpio.r1 = 42; mxconfig.gpio.g1 = 41; @@ -594,6 +617,13 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh mxconfig.gpio.d = 21; mxconfig.gpio.e = 12; + // mxconfig.double_buff = true; // <------------- Turn on double buffer + // mxconfig.driver = HUB75_I2S_CFG::ICN2038S; // experimental - use specific shift register driver + //mxconfig.latch_blanking = 3; + // mxconfig.i2sspeed = HUB75_I2S_CFG::HZ_10M; // experimental - 5MHZ should be enugh, but colours looks slightly better at 10MHz + //mxconfig.min_refresh_rate = 90; + //mxconfig.min_refresh_rate = 120; + #else USER_PRINTLN("MatrixPanel_I2S_DMA - Default pins"); /* @@ -655,14 +685,35 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh USER_PRINTLN("MatrixPanel_I2S_DMA created"); // let's adjust default brightness display->setBrightness8(25); // range is 0-255, 0 - 0%, 255 - 100% + _bri = 25; + delay(24); // experimental // Allocate memory and start DMA display if( not display->begin() ) { USER_PRINTLN("****** MatrixPanel_I2S_DMA !KABOOM! I2S memory allocation failed ***********"); return; } else { + delay(18); // experiment - give the driver a moment (~ one full frame @ 60hz) to settle _valid = true; + display->clearScreen(); // initially clear the screen buffer + + if (_ledBuffer) free(_ledBuffer); // should not happen + if (_ledsDirty) free(_ledsDirty); // should not happen + _ledsDirty = (byte*) malloc(getBitArrayBytes(_len)); // create LEDs dirty bits + + if (_ledsDirty == nullptr) { + display->stopDMAoutput(); + delete display; display = nullptr; + _valid = false; + USER_PRINTLN(F("MatrixPanel_I2S_DMA not started - not enough memory for dirty bits!")); + return; // fail is we cannot get memory for the buffer + } + setBitArray(_ledsDirty, _len, false); // reset dirty bits + + if (mxconfig.double_buff == false) { + _ledBuffer = (CRGB*) calloc(_len, sizeof(CRGB)); // create LEDs buffer (initialized to BLACK) + } } switch(bc.type) { @@ -686,28 +737,125 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh break; } + if (_valid) { + _panelWidth = fourScanPanel ? fourScanPanel->width() : display->width(); // cache width - it will never change + } - USER_PRINTLN("MatrixPanel_I2S_DMA started"); + USER_PRINT(F("MatrixPanel_I2S_DMA ")); + USER_PRINTF("%sstarted, width=%u, %u pixels.\n", _valid? "":"not ", _panelWidth, _len); + + if (mxconfig.double_buff == true) USER_PRINTLN(F("MatrixPanel_I2S_DMA driver native double-buffering enabled.")); + if (_ledBuffer != nullptr) USER_PRINTLN(F("MatrixPanel_I2S_DMA LEDS buffer enabled.")); + if (_ledsDirty != nullptr) USER_PRINTLN(F("MatrixPanel_I2S_DMA LEDS dirty bit optimization enabled.")); + if ((_ledBuffer != nullptr) || (_ledsDirty != nullptr)) { + USER_PRINT(F("MatrixPanel_I2S_DMA LEDS buffer uses ")); + USER_PRINT((_ledBuffer? _len*sizeof(CRGB) :0) + (_ledsDirty? getBitArrayBytes(_len) :0)); + USER_PRINTLN(F(" bytes.")); + } } -void BusHub75Matrix::setPixelColor(uint16_t pix, uint32_t c) { - r = R(c); - g = G(c); - b = B(c); - if(fourScanPanel != nullptr) { - x = pix % fourScanPanel->width(); - y = floor(pix / fourScanPanel->width()); - fourScanPanel->drawPixelRGB888(x, y, r, g, b); +void __attribute__((hot)) BusHub75Matrix::setPixelColor(uint16_t pix, uint32_t c) { + if (!_valid || pix >= _len) return; + // if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT + + if (_ledBuffer) { + CRGB fastled_col = CRGB(c); + if (_ledBuffer[pix] != fastled_col) { + _ledBuffer[pix] = fastled_col; + setBitInArray(_ledsDirty, pix, true); // flag pixel as "dirty" + } } else { - x = pix % display->width(); - y = floor(pix / display->width()); - display->drawPixelRGB888(x, y, r, g, b); + if ((c == BLACK) && (getBitFromArray(_ledsDirty, pix) == false)) return; // ignore black if pixel is already black + setBitInArray(_ledsDirty, pix, c != BLACK); // dirty = true means "color is not BLACK" + + #ifndef NO_CIE1931 + c = unGamma24(c); // to use the driver linear brightness feature, we first need to undo WLED gamma correction + #endif + uint8_t r = R(c); + uint8_t g = G(c); + uint8_t b = B(c); + + if(fourScanPanel != nullptr) { + int width = _panelWidth; + int x = pix % width; + int y = pix / width; + fourScanPanel->drawPixelRGB888(int16_t(x), int16_t(y), r, g, b); + } else { + int width = _panelWidth; + int x = pix % width; + int y = pix / width; + display->drawPixelRGB888(int16_t(x), int16_t(y), r, g, b); + } } } +uint32_t BusHub75Matrix::getPixelColor(uint16_t pix) const { + if (!_valid || pix >= _len) return BLACK; + if (_ledBuffer) + return uint32_t(_ledBuffer[pix].scale8(_bri)) & 0x00FFFFFF; // scale8() is needed to mimic NeoPixelBus, which returns scaled-down colours + else + return getBitFromArray(_ledsDirty, pix) ? DARKGREY: BLACK; // just a hack - we only know if the pixel is black or not +} + void BusHub75Matrix::setBrightness(uint8_t b, bool immediate) { - this->display->setBrightness(b); + _bri = b; + if (_bri > 238) _bri=238; + display->setBrightness(_bri); +} + +void __attribute__((hot)) BusHub75Matrix::show(void) { + if (!_valid) return; + display->setBrightness(_bri); + + if (_ledBuffer) { + // write out buffered LEDs + bool isFourScan = (fourScanPanel != nullptr); + //if (isFourScan) fourScanPanel->setRotation(0); + unsigned height = isFourScan ? fourScanPanel->height() : display->height(); + unsigned width = _panelWidth; + + //while(!previousBufferFree) delay(1); // experimental - Wait before we allow any writing to the buffer. Stop flicker. + + size_t pix = 0; // running pixel index + for (int y=0; ydrawPixelRGB888(int16_t(x), int16_t(y), r, g, b); + else display->drawPixelRGB888(int16_t(x), int16_t(y), r, g, b); + } + pix ++; + } + setBitArray(_ledsDirty, _len, false); // buffer shown - reset all dirty bits + } + + if(mxconfig.double_buff) { + display->flipDMABuffer(); // Show the back buffer, set current output buffer to the back (i.e. no longer being sent to LED panels) + // while(!previousBufferFree) delay(1); // experimental - Wait before we allow any writing to the buffer. Stop flicker. + display->clearScreen(); // Now clear the back-buffer + setBitArray(_ledsDirty, _len, false); // dislay buffer is blank - reset all dirty bits + } +} + +void BusHub75Matrix::cleanup() { + if (display && _valid) display->stopDMAoutput(); // terminate DMA driver (display goes black) + _valid = false; + _panelWidth = 0; + deallocatePins(); + USER_PRINTLN("HUB75 output ended."); + + //if (fourScanPanel != nullptr) delete fourScanPanel; // warning: deleting object of polymorphic class type 'VirtualMatrixPanel' which has non-virtual destructor might cause undefined behavior + delete display; + display = nullptr; + fourScanPanel = nullptr; + if (_ledBuffer != nullptr) free(_ledBuffer); _ledBuffer = nullptr; + if (_ledsDirty != nullptr) free(_ledsDirty); _ledsDirty = nullptr; } void BusHub75Matrix::deallocatePins() { @@ -808,7 +956,7 @@ void BusManager::setStatusPixel(uint32_t c) { } } -void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c, int16_t cct) { +void IRAM_ATTR __attribute__((hot)) BusManager::setPixelColor(uint16_t pix, uint32_t c, int16_t cct) { if ((pix >= laststart) && (pix < lastend ) && (lastBus != nullptr)) { // WLEDMM same bus as last time - no need to search again lastBus->setPixelColor(pix - laststart, c); @@ -836,7 +984,7 @@ void BusManager::setBrightness(uint8_t b, bool immediate) { } } -void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) { +void __attribute__((cold)) BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) { if (cct > 255) cct = 255; if (cct >= 0) { //if white balance correction allowed, save as kelvin value instead of 0-255 @@ -845,7 +993,7 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) { Bus::setCCT(cct); } -uint32_t IRAM_ATTR BusManager::getPixelColor(uint_fast16_t pix) { // WLEDMM use fast native types, IRAM_ATTR +uint32_t IRAM_ATTR __attribute__((hot)) BusManager::getPixelColor(uint_fast16_t pix) { // WLEDMM use fast native types, IRAM_ATTR if ((pix >= laststart) && (pix < lastend ) && (lastBus != nullptr)) { // WLEDMM same bus as last time - no need to search again return lastBus->getPixelColor(pix - laststart); @@ -866,20 +1014,20 @@ uint32_t IRAM_ATTR BusManager::getPixelColor(uint_fast16_t pix) { // WLEDMM return 0; } -bool BusManager::canAllShow() { +bool BusManager::canAllShow() const { for (uint8_t i = 0; i < numBusses; i++) { if (!busses[i]->canShow()) return false; } return true; } -Bus* BusManager::getBus(uint8_t busNr) { +Bus* BusManager::getBus(uint8_t busNr) const { if (busNr >= numBusses) return nullptr; return busses[busNr]; } //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit()) -uint16_t BusManager::getTotalLength() { +uint16_t BusManager::getTotalLength() const { uint_fast16_t len = 0; for (uint_fast8_t i=0; igetLength(); // WLEDMM use fast native types return len; diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 0bfd3e04..2379f430 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -4,6 +4,7 @@ #ifdef WLED_ENABLE_HUB75MATRIX #include #include +//extern volatile bool previousBufferFree; // experimental #endif /* * Class for addressing various light types @@ -11,6 +12,27 @@ #include "const.h" +#if !defined(FASTLED_VERSION) // only pull in FastLED if we don't have it yet + #define FASTLED_INTERNAL + #include +#endif + +//color mangling macros +#if !defined(RGBW32) + #define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b)))) + #define R(c) (byte((c) >> 16)) + #define G(c) (byte((c) >> 8)) + #define B(c) (byte(c)) + #define W(c) (byte((c) >> 24)) +#endif + +// WLEDMM bitarray utilities +void setBitInArray(uint8_t* byteArray, size_t position, bool value); // set bit +bool getBitFromArray(const uint8_t* byteArray, size_t position) __attribute__((pure)); // get bit value +size_t getBitArrayBytes(size_t num_bits) __attribute__((const)); // number of bytes needed for an array with num_bits bits +void setBitArray(uint8_t* byteArray, size_t numBits, bool value); // set all bits to same value + + #define GET_BIT(var,bit) (((var)>>(bit))&0x01) #define SET_BIT(var,bit) ((var)|=(uint16_t)(0x0001<<(bit))) #define UNSET_BIT(var,bit) ((var)&=(~(uint16_t)(0x0001<<(bit)))) @@ -112,35 +134,35 @@ class Bus { virtual bool canShow() { return true; } virtual void setStatusPixel(uint32_t c) {} virtual void setPixelColor(uint16_t pix, uint32_t c) = 0; - virtual uint32_t getPixelColor(uint16_t pix) { return 0; } + virtual uint32_t getPixelColor(uint16_t pix) const { return 0; } virtual void setBrightness(uint8_t b, bool immediate=false) { _bri = b; }; virtual void cleanup() = 0; - virtual uint8_t getPins(uint8_t* pinArray) { return 0; } - virtual uint16_t getLength() { return _len; } + virtual uint8_t getPins(uint8_t* pinArray) const { return 0; } + virtual uint16_t getLength() const { return _len; } virtual void setColorOrder() {} - virtual uint8_t getColorOrder() { return COL_ORDER_RGB; } + virtual uint8_t getColorOrder() const { return COL_ORDER_RGB; } virtual uint8_t skippedLeds() { return 0; } - virtual uint16_t getFrequency() { return 0U; } - inline uint16_t getStart() { return _start; } + virtual uint16_t getFrequency() const { return 0U; } + inline uint16_t getStart() const { return _start; } inline void setStart(uint16_t start) { _start = start; } - inline uint8_t getType() { return _type; } - inline bool isOk() { return _valid; } - inline bool isOffRefreshRequired() { return _needsRefresh; } - bool containsPixel(uint16_t pix) { return pix >= _start && pix < _start+_len; } - virtual uint16_t getMaxPixels() { return MAX_LEDS_PER_BUS; }; + inline uint8_t getType() const { return _type; } + inline bool isOk() const { return _valid; } + inline bool isOffRefreshRequired() const { return _needsRefresh; } + bool containsPixel(uint16_t pix) const { return pix >= _start && pix < _start+_len; } + virtual uint16_t getMaxPixels() const { return MAX_LEDS_PER_BUS; }; - virtual bool hasRGB() { + virtual bool hasRGB() const { if ((_type >= TYPE_WS2812_1CH && _type <= TYPE_WS2812_WWA) || _type == TYPE_ANALOG_1CH || _type == TYPE_ANALOG_2CH || _type == TYPE_ONOFF) return false; return true; } - virtual bool hasWhite() { return Bus::hasWhite(_type); } + virtual bool hasWhite() const { return Bus::hasWhite(_type); } static bool hasWhite(uint8_t type) { if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_SK6812_RGBW || type == TYPE_TM1814 || type == TYPE_UCS8904) return true; // digital types with white channel if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true; // analog types with white channel if (type == TYPE_NET_DDP_RGBW) return true; // network types with white channel return false; } - virtual bool hasCCT() { + virtual bool hasCCT() const { if (_type == TYPE_WS2812_2CH_X3 || _type == TYPE_WS2812_WWA || _type == TYPE_ANALOG_2CH || _type == TYPE_ANALOG_5CH) return true; return false; @@ -157,7 +179,7 @@ class Bus { #endif } inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; } - inline uint8_t getAutoWhiteMode() { return _autoWhiteMode; } + inline uint8_t getAutoWhiteMode() const { return _autoWhiteMode; } inline static void setGlobalAWMode(uint8_t m) { if (m < 5) _gAWM = m; else _gAWM = AW_GLOBAL_DISABLED; } inline static uint8_t getGlobalAWMode() { return _gAWM; } @@ -175,7 +197,7 @@ class Bus { static int16_t _cct; static uint8_t _cctBlend; - uint32_t autoWhiteCalc(uint32_t c); + uint32_t autoWhiteCalc(uint32_t c) const; }; @@ -185,7 +207,7 @@ class BusDigital : public Bus { inline void show(); - bool canShow(); + bool canShow() const; void setBrightness(uint8_t b, bool immediate); @@ -193,25 +215,25 @@ class BusDigital : public Bus { void setPixelColor(uint16_t pix, uint32_t c); - uint32_t getPixelColor(uint16_t pix); + uint32_t getPixelColor(uint16_t pix) const; - uint8_t getColorOrder() { + uint8_t getColorOrder() const { return _colorOrder; } - uint16_t getLength() { + uint16_t getLength() const { return _len - _skip; } - uint8_t getPins(uint8_t* pinArray); + uint8_t getPins(uint8_t* pinArray) const; void setColorOrder(uint8_t colorOrder); - uint8_t skippedLeds() { + uint8_t skippedLeds() const { return _skip; } - uint16_t getFrequency() { return _frequencykHz; } + uint16_t getFrequency() const { return _frequencykHz; } void reinit(); @@ -239,13 +261,13 @@ class BusPwm : public Bus { void setPixelColor(uint16_t pix, uint32_t c); //does no index check - uint32_t getPixelColor(uint16_t pix); + uint32_t getPixelColor(uint16_t pix) const; void show(); - uint8_t getPins(uint8_t* pinArray); + uint8_t getPins(uint8_t* pinArray) const; - uint16_t getFrequency() { return _frequency; } + uint16_t getFrequency() const { return _frequency; } void cleanup() { deallocatePins(); @@ -273,11 +295,11 @@ class BusOnOff : public Bus { void setPixelColor(uint16_t pix, uint32_t c); - uint32_t getPixelColor(uint16_t pix); + uint32_t getPixelColor(uint16_t pix) const; void show(); - uint8_t getPins(uint8_t* pinArray); + uint8_t getPins(uint8_t* pinArray) const; void cleanup() { pinManager.deallocatePin(_pin, PinOwner::BusOnOff); @@ -297,24 +319,24 @@ class BusNetwork : public Bus { public: BusNetwork(BusConfig &bc); - uint16_t getMaxPixels() override { return 4096; }; - bool hasRGB() { return true; } - bool hasWhite() { return _rgbw; } + uint16_t getMaxPixels() const override { return 4096; }; + bool hasRGB() const { return true; } + bool hasWhite() const { return _rgbw; } void setPixelColor(uint16_t pix, uint32_t c); - uint32_t __attribute__((pure)) getPixelColor(uint16_t pix); // WLEDMM attribute added + uint32_t __attribute__((pure)) getPixelColor(uint16_t pix) const; // WLEDMM attribute added void show(); - bool canShow() { + bool canShow() const { // this should be a return value from UDP routine if it is still sending data out return !_broadcastLock; } - uint8_t getPins(uint8_t* pinArray); + uint8_t getPins(uint8_t* pinArray) const; - uint16_t getLength() { + uint16_t getLength() const { return _len; } @@ -338,36 +360,26 @@ class BusHub75Matrix : public Bus { public: BusHub75Matrix(BusConfig &bc); - uint16_t getMaxPixels() override { return 4096; }; + uint16_t getMaxPixels() const override { return 4096; }; - bool hasRGB() { return true; } - bool hasWhite() { return false; } + bool hasRGB() const override { return true; } + bool hasWhite() const override { return false; } - void setPixelColor(uint16_t pix, uint32_t c); + void setPixelColor(uint16_t pix, uint32_t c) override; + uint32_t getPixelColor(uint16_t pix) const override; - void show() { - if(mxconfig.double_buff) { - display->flipDMABuffer(); // Show the back buffer, set currently output buffer to the back (i.e. no longer being sent to LED panels) - display->clearScreen(); // Now clear the back-buffer - } - } + void show(void) override; - void setBrightness(uint8_t b, bool immediate); + void setBrightness(uint8_t b, bool immediate) override; - uint8_t getPins(uint8_t* pinArray) { + uint8_t getPins(uint8_t* pinArray) const override { pinArray[0] = mxconfig.chain_length; return 1; } // Fake value due to keep finaliseInit happy void deallocatePins(); - void cleanup() { - deallocatePins(); - fourScanPanel = nullptr; - // delete fourScanPanel; - delete display; - _valid = false; - } + void cleanup(void) override; ~BusHub75Matrix() { cleanup(); @@ -377,8 +389,9 @@ class BusHub75Matrix : public Bus { MatrixPanel_I2S_DMA *display = nullptr; VirtualMatrixPanel *fourScanPanel = nullptr; HUB75_I2S_CFG mxconfig; - uint8_t r, g, b, x, y; - + unsigned _panelWidth = 0; + CRGB *_ledBuffer = nullptr; + byte *_ledsDirty = nullptr; }; #endif @@ -387,7 +400,7 @@ class BusManager { BusManager() {}; //utility to get the approx. memory usage of a given BusConfig - static uint32_t memUsage(BusConfig &bc); + static uint32_t memUsage(BusConfig &bc) __attribute__((pure)); int add(BusConfig &bc); @@ -406,12 +419,12 @@ class BusManager { uint32_t __attribute__((pure)) getPixelColor(uint_fast16_t pix); // WLEDMM attribute added - bool canAllShow(); + bool canAllShow() const; - Bus* getBus(uint8_t busNr); + Bus* getBus(uint8_t busNr) const; //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit()) - uint16_t getTotalLength(); + uint16_t getTotalLength() const; inline void updateColorOrderMap(const ColorOrderMap &com) { memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap)); @@ -421,7 +434,7 @@ class BusManager { return colorOrderMap; } - inline uint8_t getNumBusses() { + inline uint8_t getNumBusses() const { return numBusses; } @@ -434,7 +447,7 @@ class BusManager { unsigned laststart = 0; unsigned lastend = 0; - inline uint8_t getNumVirtualBusses() { + inline uint8_t getNumVirtualBusses() const { int j = 0; for (int i=0; igetType() >= TYPE_NET_DDP_RGB && busses[i]->getType() < 96) j++; return j; diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 465fefbb..23cf2f13 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -7,7 +7,7 @@ /* * color blend function */ -IRAM_ATTR_YN uint32_t color_blend(uint32_t color1, uint32_t color2, uint_fast16_t blend, bool b16) { +IRAM_ATTR_YN __attribute__((hot)) uint32_t color_blend(uint32_t color1, uint32_t color2, uint_fast16_t blend, bool b16) { if(blend == 0) return color1; if (color1 == color2) return color1; // WLEDMM shortcut const uint_fast16_t blendmax = b16 ? 0xFFFF : 0xFF; @@ -71,7 +71,7 @@ IRAM_ATTR_YN uint32_t color_add(uint32_t c1, uint32_t c2, bool fast) // WLEDMM * if using "video" method the resulting color will never become black unless it is already black */ -IRAM_ATTR_YN uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) +IRAM_ATTR_YN __attribute__((hot)) uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) { if (amount == 0) return 0; // WLEDMM shortcut @@ -297,7 +297,7 @@ static float maxf (float v, float w) // WLEDMM better use standard library fmax // adjust RGB values based on color temperature in K (range [2800-10200]) (https://en.wikipedia.org/wiki/Color_balance) // called from bus manager when color correction is enabled! -uint32_t IRAM_ATTR_YN colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb) // WLEDMM: IRAM_ATTR_YN +uint32_t __attribute__((hot)) IRAM_ATTR_YN colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb) // WLEDMM: IRAM_ATTR_YN { //remember so that slow colorKtoRGB() doesn't have to run for every setPixelColor() static byte correctionRGB[4] = {0,0,0,0}; @@ -406,13 +406,19 @@ static void calcInvGammaTable(float gamma) gammaTinv[i] = (int)(powf((float)i / 255.0f, gammaInv) * 255.0f + 0.5f); } } -uint8_t unGamma8(uint8_t value) { +uint8_t __attribute__((hot)) unGamma8(uint8_t value) { //if (!gammaCorrectCol || (value == 0) || (value == 255)) return value; if ((value == 0) || (value == 255)) return value; if ((gammaCorrectVal < 0.999f) || (gammaCorrectVal > 3.0f)) return value; if (gammaTinv[255] == 0) calcInvGammaTable(gammaCorrectVal); return gammaTinv[value]; } + +uint32_t __attribute__((hot)) unGamma24(uint32_t c) { + if ((gammaCorrectVal < 0.999f) || (gammaCorrectVal > 3.0f)) return c; + if (gammaTinv[255] == 0) calcInvGammaTable(gammaCorrectVal); + return RGBW32(gammaTinv[R(c)], gammaTinv[G(c)], gammaTinv[B(c)], W(c)); +} // wleDMM end uint8_t gamma8_cal(uint8_t b, float gamma) @@ -432,13 +438,13 @@ void calcGammaTable(float gamma) } // used for individual channel or brightness gamma correction -IRAM_ATTR_YN uint8_t gamma8(uint8_t b) // WLEDMM added IRAM_ATTR_YN +IRAM_ATTR_YN __attribute__((hot)) uint8_t gamma8(uint8_t b) // WLEDMM added IRAM_ATTR_YN { return gammaT[b]; } // used for color gamma correction -uint32_t gamma32(uint32_t color) +uint32_t __attribute__((hot)) gamma32(uint32_t color) { if (!gammaCorrectCol) return color; uint8_t w = W(color); diff --git a/wled00/const.h b/wled00/const.h index 5203d774..ff8b0371 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -352,6 +352,7 @@ #define ERR_LOW_SEG_MEM 34 // WLEDMM: low memory (segment data RAM) #define ERR_LOW_WS_MEM 35 // WLEDMM: low memory (ws) #define ERR_LOW_AJAX_MEM 36 // WLEDMM: low memory (oappend) +#define ERR_LOW_BUF 37 // WLEDMM: low memory (LED buffer from allocLEDs) // Timer mode types #define NL_MODE_SET 0 //After nightlight time elapsed, set to target brightness @@ -480,7 +481,9 @@ #endif //#define MIN_HEAP_SIZE (8k for AsyncWebServer) +#if !defined(MIN_HEAP_SIZE) #define MIN_HEAP_SIZE 8192 +#endif // Maximum size of node map (list of other WLED instances) #ifdef ESP8266 diff --git a/wled00/data/index.js b/wled00/data/index.js index e89ad78e..61480d5e 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -2008,6 +2008,9 @@ function readState(s,command=false) case 36: errstr = "Low Memory (oappend buffer)."; break; + case 37: + errstr = "no memory for LEDs buffer."; + break; } showToast('Error ' + s.error + ": " + errstr, true); } diff --git a/wled00/dmx_input.cpp b/wled00/dmx_input.cpp index 821481e5..3be973cc 100644 --- a/wled00/dmx_input.cpp +++ b/wled00/dmx_input.cpp @@ -187,6 +187,9 @@ void DMXInput::updateInternal() unsigned long now = millis(); if (dmx_receive(inputPortNum, &packet, DMX_TIMEOUT_TICK)) { if (!packet.err) { + if(!connected) { + USER_PRINTLN("DMX Input - connected"); + } connected = true; identify = isIdentifyOn(); if (!packet.is_rdm) { @@ -199,6 +202,9 @@ void DMXInput::updateInternal() } } else { + if(connected) { + USER_PRINTLN("DMX Input - disconnected"); + } connected = false; } } diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index aa0645d0..09f9724b 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -69,6 +69,7 @@ void calcGammaTable(float gamma); uint8_t __attribute__((pure)) gamma8(uint8_t b); // WLEDMM: added attribute pure uint32_t __attribute__((pure)) gamma32(uint32_t); // WLEDMM: added attribute pure uint8_t unGamma8(uint8_t value); // WLEDMM revert gamma correction +uint32_t unGamma24(uint32_t c); // WLEDMM for 24bit color (white left as-is) //dmx_output.cpp void initDMXOutput(); @@ -249,7 +250,7 @@ void refreshNodeList(); void sendSysInfoUDP(); //network.cpp -int getSignalQuality(int rssi); +int getSignalQuality(int rssi) __attribute__((const)); void WiFiEvent(WiFiEvent_t event); //um_manager.cpp @@ -368,7 +369,7 @@ bool oappendi(int i); // append new number to temp buffer efficiently void sappend(char stype, const char* key, int val); void sappends(char stype, const char* key, char* val); void prepareHostname(char* hostname); -bool isAsterisksOnly(const char* str, byte maxLen); +bool isAsterisksOnly(const char* str, byte maxLen) __attribute__((pure)); bool requestJSONBufferLock(uint8_t module=255); void releaseJSONBufferLock(); uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen); @@ -409,13 +410,14 @@ void clearEEPROM(); //wled_math.cpp #ifndef WLED_USE_REAL_MATH template T atan_t(T x); - float cos_t(float phi); - float sin_t(float x); - float tan_t(float x); + float cos_t(float phi) __attribute__((const)); + float sin_t(float x) __attribute__((const)); + float tan_t(float x) __attribute__((const)); float acos_t(float x); float asin_t(float x); - float floor_t(float x); - float fmod_t(float num, float denom); + float atan_t(float x) __attribute__((const)); + float floor_t(float x) __attribute__((const)); + float fmod_t(float num, float denom) __attribute__((const)); #else #include // WLEDMM use "float" variants #define sin_t sinf diff --git a/wled00/led.cpp b/wled00/led.cpp index 70863396..74520147 100644 --- a/wled00/led.cpp +++ b/wled00/led.cpp @@ -68,7 +68,7 @@ void toggleOnOff() //scales the brightness with the briMultiplier factor -IRAM_ATTR_YN byte scaledBri(byte in) // WLEDMM added IRAM_ATTR_YN +IRAM_ATTR_YN __attribute__((hot)) byte scaledBri(byte in) // WLEDMM added IRAM_ATTR_YN { if (briMultiplier == 100) return(in); // WLEDMM shortcut uint_fast16_t val = ((uint_fast16_t)in*(uint_fast16_t)briMultiplier)/100; // WLEDMM diff --git a/wled00/network.cpp b/wled00/network.cpp index 1e414d81..7e0048d8 100644 --- a/wled00/network.cpp +++ b/wled00/network.cpp @@ -89,7 +89,7 @@ const ethernet_settings ethernetBoards[] = { // ESP32-ETHERNET-KIT-VE { - 0, // eth_address, + 1, // eth_address, WLED-MM: Changed from 0 to 1 based on not working with 0 on same devkit. 5, // eth_power, 23, // eth_mdc, 18, // eth_mdio, diff --git a/wled00/pin_manager.cpp b/wled00/pin_manager.cpp index a9c31bde..d1e7a18b 100644 --- a/wled00/pin_manager.cpp +++ b/wled00/pin_manager.cpp @@ -723,7 +723,7 @@ bool PinManagerClass::joinWire(int8_t pinSDA, int8_t pinSCL) { */ // Check if supplied GPIO is ok to use -bool PinManagerClass::isPinOk(byte gpio, bool output) +bool PinManagerClass::isPinOk(byte gpio, bool output) const { #ifdef ESP32 if (digitalPinIsValid(gpio)) { @@ -757,7 +757,7 @@ bool PinManagerClass::isPinOk(byte gpio, bool output) return false; } -PinOwner PinManagerClass::getPinOwner(byte gpio) { +PinOwner PinManagerClass::getPinOwner(byte gpio) const { if (gpio >= WLED_NUM_PINS) return PinOwner::None; // catch error case, to avoid array out-of-bounds access if (!isPinOk(gpio, false)) return PinOwner::None; return ownerTag[gpio]; diff --git a/wled00/pin_manager.h b/wled00/pin_manager.h index cb107f49..23d8d951 100644 --- a/wled00/pin_manager.h +++ b/wled00/pin_manager.h @@ -125,9 +125,9 @@ class PinManagerClass { // will return true for reserved pins bool isPinAllocated(byte gpio, PinOwner tag = PinOwner::None); // will return false for reserved pins - bool isPinOk(byte gpio, bool output = true); + bool isPinOk(byte gpio, bool output = true) const; - PinOwner getPinOwner(byte gpio); + PinOwner getPinOwner(byte gpio) const; // WLEDMM begin String getOwnerText(PinOwner tag); // WLEDMM - return PIN owner tag as text diff --git a/wled00/src/dependencies/time/TimeLib.h b/wled00/src/dependencies/time/TimeLib.h index 5004f071..a4388a03 100644 --- a/wled00/src/dependencies/time/TimeLib.h +++ b/wled00/src/dependencies/time/TimeLib.h @@ -116,7 +116,7 @@ char* dayShortStr(uint8_t day); /* low level functions to convert to and from system time */ void breakTime(time_t time, tmElements_t &tm); // break time_t into elements -time_t makeTime(tmElements_t &tm); // convert time elements into time_t +time_t makeTime(tmElements_t &tm) __attribute__((pure)); // convert time elements into time_t } // extern "C++" #endif // __cplusplus diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 171be5c7..dfc7e75c 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -1220,7 +1220,7 @@ void WLED::handleConnection() #ifdef ARDUINO_ARCH_ESP32 // reconnect WiFi to clear stale allocations if heap gets too low if ((!strip.isUpdating()) && (now - heapTime > 5000)) { // WLEDMM: updated with better logic for small heap available by block, not total. // WLEDMM trying to use a moment when the strip is idle -#if defined(ARDUINO_ARCH_ESP32S2) +#if defined(ARDUINO_ARCH_ESP32S2) || defined(WLED_ENABLE_HUB75MATRIX) uint32_t heap = ESP.getFreeHeap(); // WLEDMM works better on -S2 #else uint32_t heap = heap_caps_get_largest_free_block(0x1800); // WLEDMM: This is a better metric for free heap. diff --git a/wled00/wled.h b/wled00/wled.h index 5d881cf0..c1f56732 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2407171 +#define VERSION 2408080 // WLEDMM - you can check for this define in usermods, to only enabled WLEDMM specific code in the "right" fork. Its not defined in AC WLED. #define _MoonModules_WLED_ @@ -928,9 +928,9 @@ public: } // boot starts here - void setup(); + void setup() __attribute__((used)); - void loop(); + void loop() __attribute__((used)); void reset(); void beginStrip(); diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 056d8f28..b19441a2 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -61,6 +61,7 @@ void esp_heap_trace_free_hook(void* ptr) unsigned long lastMillis = 0; //WLEDMM unsigned long loopCounter = 0; //WLEDMM +void setup() __attribute__((used)); // needed for -flto void setup() { #ifdef WLED_DEBUG_HEAP esp_err_t error = heap_caps_register_failed_alloc_callback(heap_caps_alloc_failed_hook); @@ -68,6 +69,7 @@ void setup() { WLED::instance().setup(); } +void loop() __attribute__((used)); // needed for -flto void loop() { //WLEDMM show loops per second loopCounter++;