From 7b890986d856e93014b5ed086a86379ec8db8df7 Mon Sep 17 00:00:00 2001 From: Brandon502 <105077712+Brandon502@users.noreply.github.com> Date: Sun, 25 Feb 2024 21:06:18 -0500 Subject: [PATCH 01/49] Game of Life - Rework No longer uses ColorCount struct. Removed randomness. Improved infinite pattern recognition. Adds color mutation slider and wrap option. --- wled00/FX.cpp | 185 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 111 insertions(+), 74 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index c4b1e0e9..87e3e6b1 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5138,117 +5138,154 @@ static const char _data_FX_MODE_2DFRIZZLES[] PROGMEM = "Frizzles@X frequency,Y f /////////////////////////////////////////// // 2D Cellular Automata Game of life // /////////////////////////////////////////// -typedef struct ColorCount { - CRGB color; - int8_t count; -} colorCount; - -uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https://natureofcode.com/book/chapter-7-cellular-automata/ and https://github.com/DougHaber/nlife-color +uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https://natureofcode.com/book/chapter-7-cellular-automata/ + // and https://github.com/DougHaber/nlife-color , Modified By: Brandon Butler if (!strip.isMatrix) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t dataSize = sizeof(CRGB) * SEGMENT.length(); // using width*height prevents reallocation if mirroring is enabled - const uint16_t crcBufferLen = 2; //(SEGMENT.width() + SEGMENT.height())*71/100; // roughly sqrt(2)/2 for better repetition detection (Ewowi) + const uint16_t repeatDetectionLen = 4; // {crc % 16 gen, crc % 4*r*w gen, prevAlive, changeCount} + // crc can handle basically all patterns, but detecting gliders may take multiple full trips + // tracking alive counts will allow detecting gliders in 1 full trip - if (!SEGENV.allocateData(dataSize + sizeof(uint16_t)*crcBufferLen)) return mode_static(); //allocation failed - CRGB *prevLeds = reinterpret_cast(SEGENV.data); - uint16_t *crcBuffer = reinterpret_cast(SEGENV.data + dataSize); + if (!SEGENV.allocateData(dataSize + sizeof(uint16_t)*repeatDetectionLen)) return mode_static(); //allocation failed + CRGB *leds = reinterpret_cast(SEGENV.data); + uint16_t *repeatDetection = reinterpret_cast(SEGENV.data + dataSize); CRGB backgroundColor = SEGCOLOR(1); + uint16_t aliveCount = 0; if (SEGENV.call == 0) SEGMENT.setUpLeds(); - if (SEGENV.call == 0 || strip.now - SEGMENT.step > 3000) { - SEGENV.step = strip.now; - SEGENV.aux0 = 0; + //start new game of life + if (SEGENV.call == 0 || SEGENV.aux0 == 0) { + SEGENV.step = strip.now; // .step = previous call time + SEGENV.aux0 = 1; // .aux0 = generation counter random16_set_seed(strip.now>>2); //seed the random generator //give the leds random state and colors (based on intensity, colors from palette or all posible colors are chosen) + //Setup Grid for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { - uint8_t state = random8()%2; - if (state == 0) - SEGMENT.setPixelColorXY(x,y, backgroundColor); - else - SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0): random16()*random16()); //WLEDMM support all colors + uint8_t state = (random8() < 82) ? 1 : 0; // ~32% chance of being alive + // state = 0; // Uncomment to use test pattern + if (state == 0) SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?backgroundColor : RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0)); + else { + SEGMENT.setPixelColorXY(x,y,!SEGMENT.check1?SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0): random16()*random16()); //WLEDMM support all colors + aliveCount++; + } } + // // create cross test pattern period 3 (oscillator) >= 16x16 matrix + // int patternLen = 56; + // byte testPattern[56] = {7,1,10,1,7,2,10,2,6,3,7,3,10,3,11,3,4,4,5,4,6,4,11,4,12,4,13,4,4,7,5,7,6,7,11,7,12,7,13,7,6,8,7,8,10,8,11,8,7,9,10,9,7,10,10,10}; - for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) prevLeds[XY(x,y)] = CRGB::Black; - memset(crcBuffer, 0, sizeof(uint16_t)*crcBufferLen); + // // Apply Test Pattern + // for (int i = 0; i < patternLen/2; i++) { //Uncomment state = 0 line above + // SEGMENT.setPixelColorXY(testPattern[i*2],testPattern[i*2+1], SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0)); + // aliveCount++; + // } + + //Clear repeatDetection + memset(repeatDetection, 0, sizeof(uint16_t)*repeatDetectionLen); + repeatDetection[2] = aliveCount; + //Display the initial state + return FRAMETIME; } else if (strip.now - SEGENV.step < FRAMETIME_FIXED * (uint32_t)map(SEGMENT.speed,0,255,64,4)) { // update only when appropriate time passes (in 42 FPS slots) return FRAMETIME; } - //copy previous leds (save previous generation) - //NOTE: using lossy getPixelColor() is a benefit as endlessly repeating patterns will eventually fade out causing a reset - for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) prevLeds[XY(x,y)] = SEGMENT.getPixelColorXY(x,y); - - //calculate new leds + //copy previous leds from pixels for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { + leds[XY(x,y)] = CRGB(SEGMENT.getPixelColorXY(x,y)); + } - colorCount colorsCount[9]; // count the different colors in the 3*3 matrix - for (int i=0; i<9; i++) colorsCount[i] = {backgroundColor, 0}; // init colorsCount + aliveCount = repeatDetection[2]; + + //Loop through all cells. Count neighbors, apply rules, setPixel + for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { + byte neighbors = 0; + CRGB nColors[3]; // track 3 colors, dying cells may overwrite but this wont be used - // iterate through neighbors and count them and their different colors - int neighbors = 0; for (int i = -1; i <= 1; i++) for (int j = -1; j <= 1; j++) { // iterate through 3*3 matrix if (i==0 && j==0) continue; // ignore itself - // wrap around segment - int16_t xx = x+i, yy = y+j; - if (x+i < 0) xx = cols-1; else if (x+i >= cols) xx = 0; - if (y+j < 0) yy = rows-1; else if (y+j >= rows) yy = 0; - - uint16_t xy = XY(xx, yy); // previous cell xy to check - // count different neighbours and colors - if (prevLeds[xy] != backgroundColor) { - neighbors++; - bool colorFound = false; - int k; - for (k=0; k<9 && colorsCount[i].count != 0; k++) - if (colorsCount[k].color == prevLeds[xy]) { - colorsCount[k].count++; - colorFound = true; - } - if (!colorFound) colorsCount[k] = {prevLeds[xy], 1}; //add new color found in the array + uint16_t xy; + if (SEGMENT.check2) { // wrap around option checked + xy = XY((x+i+cols)%cols, (y+j+rows)%rows); } - } // i,j + else { // no wrap around + if (x+i < 0 || x+i >= cols || y+j < 0 || y+j >= rows) continue; // ignore out of bounds + xy = XY(x+i, y+j); + } + // count neighbors and store upto 3 neighbor colors + if (leds[xy] != backgroundColor) { + nColors[neighbors%3] = leds[xy]; + neighbors++; + } + } // Rules of Life - CRGB preCol = prevLeds[XY(x,y)]; - uint32_t col = RGBW32(preCol.r, preCol.g, preCol.b, 0); // WLEDMM explicit color conversion CRGB -> RGB - uint32_t bgc = RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0); - if ((col != bgc) && (neighbors < 2)) SEGMENT.setPixelColorXY(x,y, bgc); // Loneliness - else if ((col != bgc) && (neighbors > 3)) SEGMENT.setPixelColorXY(x,y, bgc); // Overpopulation - else if ((col == bgc) && (neighbors == 3)) { // Reproduction - // find dominant color and assign it to a cell - colorCount dominantColorCount = {backgroundColor, 0}; - for (int i=0; i<9 && colorsCount[i].count != 0; i++) - if (colorsCount[i].count > dominantColorCount.count) dominantColorCount = colorsCount[i]; - // assign the dominant color w/ a bit of randomness to avoid "gliders" - if (dominantColorCount.count > 0 && random8(128)) SEGMENT.setPixelColorXY(x,y, dominantColorCount.color); - } else if ((col == bgc) && (neighbors == 2) && !random8(128)) { // Mutation - SEGMENT.setPixelColorXY(x,y, SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 255)); - } - // else do nothing! - } //x,y + CRGB color = leds[XY(x,y)]; + CRGB bgc = backgroundColor; + + if ((color != bgc) && (neighbors < 2 || neighbors > 3)) { + // Loneliness or overpopulation + SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?backgroundColor : RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0)); + aliveCount--; + } + else if ((color == bgc) && (neighbors == 3)) { + // Reproduction + // find dominant color and assign it to a cell + CRGB dominantColor; + if ((nColors[0] == nColors[1]) || (nColors[0] == nColors[2])) dominantColor = nColors[0]; + else if (nColors[1] == nColors[2]) dominantColor = nColors[1]; + else dominantColor = nColors[random8()%3]; + + // mutate color chance (1/256) + if (random8() < SEGMENT.intensity) dominantColor = !SEGMENT.check1?SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0): random16()*random16(); + + SEGMENT.setPixelColorXY(x,y, dominantColor); + aliveCount++; + } + } + + // track CRC16 of leds every 16 frames to detect all basic repeating patterns + // track CRC16 of leds every 4*max(rows,cols) frames to detect all infinite gliders / spaceships + // rectanglular grids with a side of length <= 6 create extremely long repeating patterns + + // current crc + uint16_t crc = crc16((const unsigned char*)leds, dataSize); - // calculate CRC16 of leds - uint16_t crc = crc16((const unsigned char*)prevLeds, dataSize); - // check if we had same CRC and reset if needed bool repetition = false; - for (int i=0; i softhack007: not exacly. Different CRC means different image; same CRC means nothing (could be same or slightly different). - if (!repetition) SEGENV.step = strip.now; //if no repetition avoid reset - // remember CRCs across frames - crcBuffer[SEGENV.aux0] = crc; - ++SEGENV.aux0 %= crcBufferLen; + // Update Alive/Counter + if (abs8(repeatDetection[2] - aliveCount) < 2) repeatDetection[3]++; // alive count needs to change by 2 or more to reset the repetition counter + else repeatDetection[3] = 0; + + if (repeatDetection[3] > (4 * max(rows,cols))) { + repetition = true; // if alive count did not change for 4 * max(rows, col) frames, infinite glider + } + + if (repetition) { + SEGENV.aux0 = 0; // reset on next call + return FRAMETIME; + } + // Update CRC buffer and alive count + if (SEGENV.aux0 % 16 == 0) repeatDetection[0] = crc; + if (SEGENV.aux0 % (4*rows*cols+1) == 0) repeatDetection[1] = crc; + repeatDetection[2] = aliveCount; + + // increase generation counter + SEGENV.aux0++; + SEGENV.step = strip.now; return FRAMETIME; } // mode_2Dgameoflife() -static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!,,,,,All colors ☾;!,!;!;2;c1=0"; //WLEDMM support all colors - +static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!,Color Mutation ☾,,,,All Colors ☾,Wrap ☾;!,!;!;2;sx=200,ix=12,c1=0,c2=1,o2=1"; ///////////////////////// // 2D Hiphotic // From ce1b81166760ad46d5f1dbc3f77b2714ec4dcf45 Mon Sep 17 00:00:00 2001 From: Brandon502 <105077712+Brandon502@users.noreply.github.com> Date: Sun, 25 Feb 2024 21:25:20 -0500 Subject: [PATCH 02/49] Game of Life - Added overlay option Added option to overlay alive cells. Required more memory usage. --- wled00/FX.cpp | 70 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 87e3e6b1..510b37ac 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5149,13 +5149,13 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: // crc can handle basically all patterns, but detecting gliders may take multiple full trips // tracking alive counts will allow detecting gliders in 1 full trip - if (!SEGENV.allocateData(dataSize + sizeof(uint16_t)*repeatDetectionLen)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize*2 + sizeof(uint16_t)*repeatDetectionLen)) return mode_static(); //allocation failed CRGB *leds = reinterpret_cast(SEGENV.data); - uint16_t *repeatDetection = reinterpret_cast(SEGENV.data + dataSize); + CRGB *futureLeds = reinterpret_cast(SEGENV.data + dataSize); // only needed for overlay + uint16_t *repeatDetection = reinterpret_cast(SEGENV.data + dataSize*2); CRGB backgroundColor = SEGCOLOR(1); uint16_t aliveCount = 0; - if (SEGENV.call == 0) SEGMENT.setUpLeds(); //start new game of life @@ -5168,38 +5168,46 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: //Setup Grid for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { uint8_t state = (random8() < 82) ? 1 : 0; // ~32% chance of being alive - // state = 0; // Uncomment to use test pattern - if (state == 0) SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?backgroundColor : RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0)); - else { - SEGMENT.setPixelColorXY(x,y,!SEGMENT.check1?SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0): random16()*random16()); //WLEDMM support all colors - aliveCount++; + if (state == 0) { //dead + leds[XY(x,y)] = backgroundColor; + //check overlay option + if (!SEGMENT.check2) SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?backgroundColor : RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0)); + } + else { //alive + CRGB color = !SEGMENT.check1? SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0) : random16()*random16(); + leds[XY(x,y)] = color; + aliveCount++; + + SEGMENT.setPixelColorXY(x,y,!SEGMENT.check1?color : RGBW32(color.r, color.g, color.b, 0)); } } - // // create cross test pattern period 3 (oscillator) >= 16x16 matrix - // int patternLen = 56; - // byte testPattern[56] = {7,1,10,1,7,2,10,2,6,3,7,3,10,3,11,3,4,4,5,4,6,4,11,4,12,4,13,4,4,7,5,7,6,7,11,7,12,7,13,7,6,8,7,8,10,8,11,8,7,9,10,9,7,10,10,10}; - - // // Apply Test Pattern - // for (int i = 0; i < patternLen/2; i++) { //Uncomment state = 0 line above - // SEGMENT.setPixelColorXY(testPattern[i*2],testPattern[i*2+1], SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0)); - // aliveCount++; - // } //Clear repeatDetection memset(repeatDetection, 0, sizeof(uint16_t)*repeatDetectionLen); repeatDetection[2] = aliveCount; - //Display the initial state + return FRAMETIME; } else if (strip.now - SEGENV.step < FRAMETIME_FIXED * (uint32_t)map(SEGMENT.speed,0,255,64,4)) { // update only when appropriate time passes (in 42 FPS slots) + // Redraw Overlay if needed + if (!SEGMENT.check2) return FRAMETIME; + for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { + //redraw foreground/alive + if (leds[XY(x,y)] != backgroundColor) SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?leds[XY(x,y)] : RGBW32(leds[XY(x,y)].r, leds[XY(x,y)].g, leds[XY(x,y)].b, 0)); + } return FRAMETIME; } - - //copy previous leds from pixels - for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { - leds[XY(x,y)] = CRGB(SEGMENT.getPixelColorXY(x,y)); + //Update Game of Life + //Redraw immediately if overlay to avoid flicker + if (SEGMENT.check2) { + for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { + if (leds[XY(x,y)] != backgroundColor) SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?leds[XY(x,y)] : RGBW32(leds[XY(x,y)].r, leds[XY(x,y)].g, leds[XY(x,y)].b, 0)); + } } + //copy leds to futureLeds + memcpy(futureLeds, leds, dataSize); + aliveCount = repeatDetection[2]; //Loop through all cells. Count neighbors, apply rules, setPixel @@ -5210,7 +5218,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: for (int i = -1; i <= 1; i++) for (int j = -1; j <= 1; j++) { // iterate through 3*3 matrix if (i==0 && j==0) continue; // ignore itself uint16_t xy; - if (SEGMENT.check2) { // wrap around option checked + if (SEGMENT.check3) { // wrap around option checked xy = XY((x+i+cols)%cols, (y+j+rows)%rows); } else { // no wrap around @@ -5230,7 +5238,8 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: if ((color != bgc) && (neighbors < 2 || neighbors > 3)) { // Loneliness or overpopulation - SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?backgroundColor : RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0)); + futureLeds[XY(x,y)] = backgroundColor; + if (!SEGMENT.check2) SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?backgroundColor : RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0)); aliveCount--; } else if ((color == bgc) && (neighbors == 3)) { @@ -5241,14 +5250,18 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: else if (nColors[1] == nColors[2]) dominantColor = nColors[1]; else dominantColor = nColors[random8()%3]; - // mutate color chance (1/256) + futureLeds[XY(x,y)] = dominantColor; + // mutate color chance if (random8() < SEGMENT.intensity) dominantColor = !SEGMENT.check1?SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0): random16()*random16(); - - SEGMENT.setPixelColorXY(x,y, dominantColor); + SEGMENT.setPixelColorXY(x,y,!SEGMENT.check1?dominantColor : RGBW32(dominantColor.r, dominantColor.g, dominantColor.b, 0)); //WLEDMM support all colors + aliveCount++; } } + // copy futureLeds to leds + memcpy(leds, futureLeds, dataSize); + // track CRC16 of leds every 16 frames to detect all basic repeating patterns // track CRC16 of leds every 4*max(rows,cols) frames to detect all infinite gliders / spaceships // rectanglular grids with a side of length <= 6 create extremely long repeating patterns @@ -5283,9 +5296,10 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: // increase generation counter SEGENV.aux0++; SEGENV.step = strip.now; + return FRAMETIME; } // mode_2Dgameoflife() -static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!,Color Mutation ☾,,,,All Colors ☾,Wrap ☾;!,!;!;2;sx=200,ix=12,c1=0,c2=1,o2=1"; +static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!,Color Mutation ☾,,,,All Colors ☾,Overlay ☾,Wrap ☾,;!,!;!;2;sx=200,ix=12,c1=0,o3=1"; ///////////////////////// // 2D Hiphotic // From 73091d170cd3dca90449e4f9d3ebc89d84bc37e2 Mon Sep 17 00:00:00 2001 From: Brandon502 <105077712+Brandon502@users.noreply.github.com> Date: Mon, 26 Feb 2024 20:04:48 -0500 Subject: [PATCH 03/49] Game Of Life - Reduced Memory Usage Each cell now only needs 2 bits of data instead of 6 bytes. --- wled00/FX.cpp | 127 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 81 insertions(+), 46 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 510b37ac..dd52b92e 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5138,24 +5138,38 @@ static const char _data_FX_MODE_2DFRIZZLES[] PROGMEM = "Frizzles@X frequency,Y f /////////////////////////////////////////// // 2D Cellular Automata Game of life // /////////////////////////////////////////// +bool getBitValue(const uint8_t* byteArray, size_t arraySize, size_t n) { + size_t byteIndex = n / 8; + size_t bitIndex = n % 8; + uint8_t byte = byteArray[byteIndex]; + return (byte >> bitIndex) & 1; +} +void setBitValue(uint8_t* byteArray, size_t arraySize, size_t n, bool value) { + size_t byteIndex = n / 8; + size_t bitIndex = n % 8; + if (value) + byteArray[byteIndex] |= (1 << bitIndex); + else + byteArray[byteIndex] &= ~(1 << bitIndex); +} uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https://natureofcode.com/book/chapter-7-cellular-automata/ // and https://github.com/DougHaber/nlife-color , Modified By: Brandon Butler if (!strip.isMatrix) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - const uint16_t dataSize = sizeof(CRGB) * SEGMENT.length(); // using width*height prevents reallocation if mirroring is enabled + const uint16_t dataSize = sizeof(byte) * SEGMENT.length()/8; // using width*height prevents reallocation if mirroring is enabled const uint16_t repeatDetectionLen = 4; // {crc % 16 gen, crc % 4*r*w gen, prevAlive, changeCount} - // crc can handle basically all patterns, but detecting gliders may take multiple full trips - // tracking alive counts will allow detecting gliders in 1 full trip if (!SEGENV.allocateData(dataSize*2 + sizeof(uint16_t)*repeatDetectionLen)) return mode_static(); //allocation failed - CRGB *leds = reinterpret_cast(SEGENV.data); - CRGB *futureLeds = reinterpret_cast(SEGENV.data + dataSize); // only needed for overlay - uint16_t *repeatDetection = reinterpret_cast(SEGENV.data + dataSize*2); + byte *cells = reinterpret_cast(SEGENV.data); + byte *futureCells = reinterpret_cast(SEGENV.data + dataSize); + uint16_t *repeatDetection = reinterpret_cast(SEGENV.data + dataSize*2); CRGB backgroundColor = SEGCOLOR(1); + CRGB color; uint16_t aliveCount = 0; + if (SEGENV.call == 0) SEGMENT.setUpLeds(); //start new game of life @@ -5168,24 +5182,24 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: //Setup Grid for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { uint8_t state = (random8() < 82) ? 1 : 0; // ~32% chance of being alive - if (state == 0) { //dead - leds[XY(x,y)] = backgroundColor; - //check overlay option - if (!SEGMENT.check2) SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?backgroundColor : RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0)); + if (state == 0) { + setBitValue(cells, dataSize, y * cols + x, false); + setBitValue(futureCells, dataSize, y * cols + x, false); + if (SEGMENT.check2) continue; + SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?backgroundColor : RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0)); } - else { //alive - CRGB color = !SEGMENT.check1? SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0) : random16()*random16(); - leds[XY(x,y)] = color; - aliveCount++; - + else { + setBitValue(cells, dataSize, y * cols + x, true); + setBitValue(futureCells, dataSize, y * cols + x, true); + color = SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0); SEGMENT.setPixelColorXY(x,y,!SEGMENT.check1?color : RGBW32(color.r, color.g, color.b, 0)); + aliveCount++; } } //Clear repeatDetection memset(repeatDetection, 0, sizeof(uint16_t)*repeatDetectionLen); repeatDetection[2] = aliveCount; - return FRAMETIME; } else if (strip.now - SEGENV.step < FRAMETIME_FIXED * (uint32_t)map(SEGMENT.speed,0,255,64,4)) { // update only when appropriate time passes (in 42 FPS slots) @@ -5193,7 +5207,10 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: if (!SEGMENT.check2) return FRAMETIME; for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { //redraw foreground/alive - if (leds[XY(x,y)] != backgroundColor) SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?leds[XY(x,y)] : RGBW32(leds[XY(x,y)].r, leds[XY(x,y)].g, leds[XY(x,y)].b, 0)); + if (getBitValue(cells, dataSize, y * cols + x)) { + color = SEGMENT.getPixelColorXY(x,y); + SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?color : RGBW32(color.r, color.g, color.b, 0)); + } } return FRAMETIME; } @@ -5201,73 +5218,92 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: //Redraw immediately if overlay to avoid flicker if (SEGMENT.check2) { for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { - if (leds[XY(x,y)] != backgroundColor) SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?leds[XY(x,y)] : RGBW32(leds[XY(x,y)].r, leds[XY(x,y)].g, leds[XY(x,y)].b, 0)); + //redraw foreground/alive + if (getBitValue(cells, dataSize, y * cols + x)) { + color = SEGMENT.getPixelColorXY(x,y); + SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?color : RGBW32(color.r, color.g, color.b, 0)); + } } } - //copy leds to futureLeds - memcpy(futureLeds, leds, dataSize); - aliveCount = repeatDetection[2]; + aliveCount = repeatDetection[2]; //get alive count from memory + //cell index and coordinates + uint16_t cIndex; + uint16_t cX; + uint16_t cY; //Loop through all cells. Count neighbors, apply rules, setPixel for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { byte neighbors = 0; + byte colorCount = 0; //track number of valid colors CRGB nColors[3]; // track 3 colors, dying cells may overwrite but this wont be used for (int i = -1; i <= 1; i++) for (int j = -1; j <= 1; j++) { // iterate through 3*3 matrix if (i==0 && j==0) continue; // ignore itself - uint16_t xy; - if (SEGMENT.check3) { // wrap around option checked - xy = XY((x+i+cols)%cols, (y+j+rows)%rows); - } - else { // no wrap around - if (x+i < 0 || x+i >= cols || y+j < 0 || y+j >= rows) continue; // ignore out of bounds - xy = XY(x+i, y+j); + + if (SEGMENT.check3) { //wrap around + cX = (x+i+cols) % cols; + cY = (y+j+rows) % rows; + } else { + cX = x+i; + cY = y+j; + if (cX < 0 || cY < 0 || cX >= cols || cY >= rows) continue; //skip if out of bounds } + + cIndex = cY * cols + cX; // count neighbors and store upto 3 neighbor colors - if (leds[xy] != backgroundColor) { - nColors[neighbors%3] = leds[xy]; + if (getBitValue(cells, dataSize, cIndex)) { //if alive neighbors++; + CRGB color = SEGMENT.getPixelColorXY(cX, cY); + if (color == backgroundColor) continue; //parent just died, color lost + nColors[colorCount%3] = color; + colorCount++; } } // Rules of Life - CRGB color = leds[XY(x,y)]; - CRGB bgc = backgroundColor; - - if ((color != bgc) && (neighbors < 2 || neighbors > 3)) { + bool cellValue = getBitValue(cells, dataSize, y * cols + x); + if ((cellValue) && (neighbors < 2 || neighbors > 3)) { // Loneliness or overpopulation - futureLeds[XY(x,y)] = backgroundColor; + setBitValue(futureCells, dataSize, y * cols + x, false); if (!SEGMENT.check2) SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?backgroundColor : RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0)); aliveCount--; } - else if ((color == bgc) && (neighbors == 3)) { + else if (!(cellValue) && (neighbors == 3)) { // Reproduction + setBitValue(futureCells, dataSize, y * cols + x, true); // find dominant color and assign it to a cell CRGB dominantColor; - if ((nColors[0] == nColors[1]) || (nColors[0] == nColors[2])) dominantColor = nColors[0]; - else if (nColors[1] == nColors[2]) dominantColor = nColors[1]; - else dominantColor = nColors[random8()%3]; - futureLeds[XY(x,y)] = dominantColor; + //Alternative dominant color calculation + //ncolors may have fewer than 3 colors + if (colorCount == 3) { + if ((nColors[0] == nColors[1]) || (nColors[0] == nColors[2])) dominantColor = nColors[0]; + else if (nColors[1] == nColors[2]) dominantColor = nColors[1]; + else dominantColor = nColors[random8()%3]; + } + else if (colorCount == 2) dominantColor = nColors[random8()%2]; + else if (colorCount == 1) dominantColor = nColors[0]; + else dominantColor = SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0); // mutate color chance if (random8() < SEGMENT.intensity) dominantColor = !SEGMENT.check1?SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0): random16()*random16(); - SEGMENT.setPixelColorXY(x,y,!SEGMENT.check1?dominantColor : RGBW32(dominantColor.r, dominantColor.g, dominantColor.b, 0)); //WLEDMM support all colors - + + if (SEGMENT.check1) dominantColor = RGBW32(dominantColor.r, dominantColor.g, dominantColor.b, 0); //WLEDMM support all colors) + SEGMENT.setPixelColorXY(x,y, dominantColor); aliveCount++; } } - // copy futureLeds to leds - memcpy(leds, futureLeds, dataSize); + //update cell values + memcpy(cells, futureCells, dataSize); // track CRC16 of leds every 16 frames to detect all basic repeating patterns // track CRC16 of leds every 4*max(rows,cols) frames to detect all infinite gliders / spaceships // rectanglular grids with a side of length <= 6 create extremely long repeating patterns // current crc - uint16_t crc = crc16((const unsigned char*)leds, dataSize); + uint16_t crc = crc16((const unsigned char*)cells, dataSize); bool repetition = false; if (aliveCount == 0) repetition = true; // if no alive cells, infinite repetition @@ -5296,7 +5332,6 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: // increase generation counter SEGENV.aux0++; SEGENV.step = strip.now; - return FRAMETIME; } // mode_2Dgameoflife() static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!,Color Mutation ☾,,,,All Colors ☾,Overlay ☾,Wrap ☾,;!,!;!;2;sx=200,ix=12,c1=0,o3=1"; From ba5731654f60ca12f03bc705aa0278bdf9f62804 Mon Sep 17 00:00:00 2001 From: Brandon502 <105077712+Brandon502@users.noreply.github.com> Date: Wed, 28 Feb 2024 15:20:12 -0500 Subject: [PATCH 04/49] Game of Life - changed repeat detection Simplified repeat detection and code cleanup. Only storing 2 crc values. prevAlive and counter no longer needed. --- wled00/FX.cpp | 83 ++++++++++++++++----------------------------------- 1 file changed, 26 insertions(+), 57 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index dd52b92e..b966d387 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5159,26 +5159,24 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t dataSize = sizeof(byte) * SEGMENT.length()/8; // using width*height prevents reallocation if mirroring is enabled - const uint16_t repeatDetectionLen = 4; // {crc % 16 gen, crc % 4*r*w gen, prevAlive, changeCount} + const uint16_t crcBufferLen = 2; + const uint16_t totalSize = dataSize*2 + sizeof(uint16_t)*crcBufferLen; - if (!SEGENV.allocateData(dataSize*2 + sizeof(uint16_t)*repeatDetectionLen)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize*2 + sizeof(uint16_t)*crcBufferLen)) return mode_static(); //allocation failed byte *cells = reinterpret_cast(SEGENV.data); byte *futureCells = reinterpret_cast(SEGENV.data + dataSize); - uint16_t *repeatDetection = reinterpret_cast(SEGENV.data + dataSize*2); + uint16_t *crcBuffer = reinterpret_cast(SEGENV.data + dataSize*2); + uint16_t &generation = SEGENV.aux0; CRGB backgroundColor = SEGCOLOR(1); CRGB color; - uint16_t aliveCount = 0; if (SEGENV.call == 0) SEGMENT.setUpLeds(); - //start new game of life - if (SEGENV.call == 0 || SEGENV.aux0 == 0) { + if (SEGENV.call == 0 || generation == 0) { SEGENV.step = strip.now; // .step = previous call time - SEGENV.aux0 = 1; // .aux0 = generation counter + generation = 1; random16_set_seed(strip.now>>2); //seed the random generator - - //give the leds random state and colors (based on intensity, colors from palette or all posible colors are chosen) //Setup Grid for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { uint8_t state = (random8() < 82) ? 1 : 0; // ~32% chance of being alive @@ -5193,15 +5191,12 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: setBitValue(futureCells, dataSize, y * cols + x, true); color = SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0); SEGMENT.setPixelColorXY(x,y,!SEGMENT.check1?color : RGBW32(color.r, color.g, color.b, 0)); - aliveCount++; } } - //Clear repeatDetection - memset(repeatDetection, 0, sizeof(uint16_t)*repeatDetectionLen); - repeatDetection[2] = aliveCount; + memset(crcBuffer, 0, sizeof(uint16_t)*crcBufferLen); return FRAMETIME; - } else if (strip.now - SEGENV.step < FRAMETIME_FIXED * (uint32_t)map(SEGMENT.speed,0,255,64,4)) { + } else if (strip.now - SEGENV.step < FRAMETIME_FIXED * (uint32_t)map(SEGMENT.speed,0,255,64,2)) { // update only when appropriate time passes (in 42 FPS slots) // Redraw Overlay if needed if (!SEGMENT.check2) return FRAMETIME; @@ -5225,10 +5220,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: } } } - - - aliveCount = repeatDetection[2]; //get alive count from memory - + bool newCellCheck = false; // Detect still live and dead grids //cell index and coordinates uint16_t cIndex; uint16_t cX; @@ -5241,7 +5233,6 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: for (int i = -1; i <= 1; i++) for (int j = -1; j <= 1; j++) { // iterate through 3*3 matrix if (i==0 && j==0) continue; // ignore itself - if (SEGMENT.check3) { //wrap around cX = (x+i+cols) % cols; cY = (y+j+rows) % rows; @@ -5250,7 +5241,6 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: cY = y+j; if (cX < 0 || cY < 0 || cX >= cols || cY >= rows) continue; //skip if out of bounds } - cIndex = cY * cols + cX; // count neighbors and store upto 3 neighbor colors if (getBitValue(cells, dataSize, cIndex)) { //if alive @@ -5268,69 +5258,48 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: // Loneliness or overpopulation setBitValue(futureCells, dataSize, y * cols + x, false); if (!SEGMENT.check2) SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?backgroundColor : RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0)); - aliveCount--; } else if (!(cellValue) && (neighbors == 3)) { // Reproduction setBitValue(futureCells, dataSize, y * cols + x, true); + newCellCheck = true; // find dominant color and assign it to a cell + // no longer storing colors, if parent dies the color is lost CRGB dominantColor; - - //Alternative dominant color calculation - //ncolors may have fewer than 3 colors - if (colorCount == 3) { + if (colorCount == 3) { //All parents survived if ((nColors[0] == nColors[1]) || (nColors[0] == nColors[2])) dominantColor = nColors[0]; else if (nColors[1] == nColors[2]) dominantColor = nColors[1]; else dominantColor = nColors[random8()%3]; } - else if (colorCount == 2) dominantColor = nColors[random8()%2]; - else if (colorCount == 1) dominantColor = nColors[0]; - else dominantColor = SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0); + else if (colorCount == 2) dominantColor = nColors[random8()%2]; // 1 leading parent died + else if (colorCount == 1) dominantColor = nColors[0]; // 2 leading parents survived + else dominantColor = SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0); // all parents died // mutate color chance if (random8() < SEGMENT.intensity) dominantColor = !SEGMENT.check1?SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0): random16()*random16(); if (SEGMENT.check1) dominantColor = RGBW32(dominantColor.r, dominantColor.g, dominantColor.b, 0); //WLEDMM support all colors) SEGMENT.setPixelColorXY(x,y, dominantColor); - aliveCount++; } } - //update cell values memcpy(cells, futureCells, dataSize); - - // track CRC16 of leds every 16 frames to detect all basic repeating patterns - // track CRC16 of leds every 4*max(rows,cols) frames to detect all infinite gliders / spaceships - // rectanglular grids with a side of length <= 6 create extremely long repeating patterns - - // current crc + /////////Repeat Detection///////// + const byte oscillatorCheck = 0; + const byte spaceshipCheck = 1; + // Get current crc value uint16_t crc = crc16((const unsigned char*)cells, dataSize); bool repetition = false; - if (aliveCount == 0) repetition = true; // if no alive cells, infinite repetition - // check if we had same CRC and reset if needed - for (int i=0; i softhack007: not exacly. Different CRC means different image; same CRC means nothing (could be same or slightly different). - - // Update Alive/Counter - if (abs8(repeatDetection[2] - aliveCount) < 2) repeatDetection[3]++; // alive count needs to change by 2 or more to reset the repetition counter - else repeatDetection[3] = 0; - - if (repeatDetection[3] > (4 * max(rows,cols))) { - repetition = true; // if alive count did not change for 4 * max(rows, col) frames, infinite glider - } - + if (!newCellCheck || crc == crcBuffer[oscillatorCheck] || crc == crcBuffer[spaceshipCheck]) repetition = true; //check if cell born this gen and previous stored crc values if (repetition) { - SEGENV.aux0 = 0; // reset on next call + generation = 0; // reset on next call return FRAMETIME; } - // Update CRC buffer and alive count - if (SEGENV.aux0 % 16 == 0) repeatDetection[0] = crc; - if (SEGENV.aux0 % (4*rows*cols+1) == 0) repeatDetection[1] = crc; - repeatDetection[2] = aliveCount; + // Update CRC values + if (generation % 16 == 0) crcBuffer[oscillatorCheck] = crc; + if (generation % (4*max(rows,cols)) == 0) crcBuffer[spaceshipCheck] = crc; - // increase generation counter - SEGENV.aux0++; + generation++; SEGENV.step = strip.now; return FRAMETIME; } // mode_2Dgameoflife() From 987a662977916e2fe499c20cf2be7fed3eed2d6a Mon Sep 17 00:00:00 2001 From: Brandon502 <105077712+Brandon502@users.noreply.github.com> Date: Wed, 28 Feb 2024 19:40:40 -0500 Subject: [PATCH 05/49] Game of Life - Bug fix Fixed small bug in new detection method. Start and final frames are displayed slightly longer. --- wled00/FX.cpp | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index b966d387..23bcf960 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5168,14 +5168,16 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: uint16_t *crcBuffer = reinterpret_cast(SEGENV.data + dataSize*2); uint16_t &generation = SEGENV.aux0; + uint16_t &pauseFrames = SEGENV.aux1; CRGB backgroundColor = SEGCOLOR(1); CRGB color; if (SEGENV.call == 0) SEGMENT.setUpLeds(); //start new game of life - if (SEGENV.call == 0 || generation == 0) { + if ((SEGENV.call == 0 || generation == 0) && pauseFrames == 0) { SEGENV.step = strip.now; // .step = previous call time generation = 1; + pauseFrames = 75; // show initial state for longer random16_set_seed(strip.now>>2); //seed the random generator //Setup Grid for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { @@ -5196,20 +5198,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: //Clear repeatDetection memset(crcBuffer, 0, sizeof(uint16_t)*crcBufferLen); return FRAMETIME; - } else if (strip.now - SEGENV.step < FRAMETIME_FIXED * (uint32_t)map(SEGMENT.speed,0,255,64,2)) { - // update only when appropriate time passes (in 42 FPS slots) - // Redraw Overlay if needed - if (!SEGMENT.check2) return FRAMETIME; - for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { - //redraw foreground/alive - if (getBitValue(cells, dataSize, y * cols + x)) { - color = SEGMENT.getPixelColorXY(x,y); - SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?color : RGBW32(color.r, color.g, color.b, 0)); - } - } - return FRAMETIME; } - //Update Game of Life //Redraw immediately if overlay to avoid flicker if (SEGMENT.check2) { for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { @@ -5220,7 +5209,12 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: } } } - bool newCellCheck = false; // Detect still live and dead grids + if (pauseFrames || strip.now - SEGENV.step < FRAMETIME_FIXED * (uint32_t)map(SEGMENT.speed,0,255,64,2)) { + if(pauseFrames) pauseFrames--; + return FRAMETIME; //skip if not enough time has passed + } + //Update Game of Life + bool cellChanged = false; // Detect still live and dead grids //cell index and coordinates uint16_t cIndex; uint16_t cX; @@ -5245,7 +5239,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: // count neighbors and store upto 3 neighbor colors if (getBitValue(cells, dataSize, cIndex)) { //if alive neighbors++; - CRGB color = SEGMENT.getPixelColorXY(cX, cY); + color = SEGMENT.getPixelColorXY(cX, cY); if (color == backgroundColor) continue; //parent just died, color lost nColors[colorCount%3] = color; colorCount++; @@ -5256,13 +5250,14 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: bool cellValue = getBitValue(cells, dataSize, y * cols + x); if ((cellValue) && (neighbors < 2 || neighbors > 3)) { // Loneliness or overpopulation + cellChanged = true; setBitValue(futureCells, dataSize, y * cols + x, false); if (!SEGMENT.check2) SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?backgroundColor : RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0)); } else if (!(cellValue) && (neighbors == 3)) { // Reproduction setBitValue(futureCells, dataSize, y * cols + x, true); - newCellCheck = true; + cellChanged = true; // find dominant color and assign it to a cell // no longer storing colors, if parent dies the color is lost CRGB dominantColor; @@ -5273,7 +5268,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: } else if (colorCount == 2) dominantColor = nColors[random8()%2]; // 1 leading parent died else if (colorCount == 1) dominantColor = nColors[0]; // 2 leading parents survived - else dominantColor = SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0); // all parents died + else dominantColor = color; // all parents died last used color // mutate color chance if (random8() < SEGMENT.intensity) dominantColor = !SEGMENT.check1?SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0): random16()*random16(); @@ -5290,9 +5285,10 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: uint16_t crc = crc16((const unsigned char*)cells, dataSize); bool repetition = false; - if (!newCellCheck || crc == crcBuffer[oscillatorCheck] || crc == crcBuffer[spaceshipCheck]) repetition = true; //check if cell born this gen and previous stored crc values + if (!cellChanged || crc == crcBuffer[oscillatorCheck] || crc == crcBuffer[spaceshipCheck]) repetition = true; //check if cell born this gen and previous stored crc values if (repetition) { generation = 0; // reset on next call + pauseFrames = 50; return FRAMETIME; } // Update CRC values From 3ee99e13fa99c3ad9d3200e4a95308df846b6456 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 7 May 2024 16:02:46 +0200 Subject: [PATCH 06/49] (bugfix) add SPIRAM pins as "reserved" --- wled00/xml.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/xml.cpp b/wled00/xml.cpp index ee647217..f7ee4f4d 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -196,7 +196,7 @@ void appendGPIOinfo() { size_t roLen = strlen(ro_gpio); char pinString[10]; for(int pinNr = 0; pinNr < WLED_NUM_PINS; pinNr++) { // 49 = highest PIN on ESP32-S3 - if(!pinManager.isPinOk(pinNr, false)) { + if ((!pinManager.isPinOk(pinNr, false)) || (pinManager.getPinOwner(pinNr) == PinOwner::SPI_RAM)) { // WLEDMM add SPIRAM pins as "reserved" sprintf(pinString, "%s%d", strlen(rsvd)==rsLen?"":",", pinNr); strcat(rsvd, pinString); } From 546f843f84696dbdf57bae97751fe2acd37c889b Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 7 May 2024 16:24:00 +0200 Subject: [PATCH 07/49] ... bugfix fix avoid double entries in rsvd[] --- boards/lolin_s3_mini.json | 47 +++++++++++++++++++++++++++++++++++++++ wled00/xml.cpp | 6 ++++- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 boards/lolin_s3_mini.json diff --git a/boards/lolin_s3_mini.json b/boards/lolin_s3_mini.json new file mode 100644 index 00000000..f8560fa7 --- /dev/null +++ b/boards/lolin_s3_mini.json @@ -0,0 +1,47 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "memory_type": "qio_qspi" + }, + "core": "esp32", + "extra_flags": [ + "-DBOARD_HAS_PSRAM", + "-DARDUINO_LOLIN_S3_MINI", + "-DARDUINO_USB_MODE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [ + [ + "0x303A", + "0x8167" + ] + ], + "mcu": "esp32s3", + "variant": "esp32s3" + }, + "connectivity": [ + "bluetooth", + "wifi" + ], + "debug": { + "openocd_target": "esp32s3.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "WEMOS LOLIN S3 Mini", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://www.wemos.cc/en/latest/s3/index.html", + "vendor": "WEMOS" +} + \ No newline at end of file diff --git a/wled00/xml.cpp b/wled00/xml.cpp index f7ee4f4d..dbb9ce75 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -196,7 +196,11 @@ void appendGPIOinfo() { size_t roLen = strlen(ro_gpio); char pinString[10]; for(int pinNr = 0; pinNr < WLED_NUM_PINS; pinNr++) { // 49 = highest PIN on ESP32-S3 - if ((!pinManager.isPinOk(pinNr, false)) || (pinManager.getPinOwner(pinNr) == PinOwner::SPI_RAM)) { // WLEDMM add SPIRAM pins as "reserved" + #if defined(ARDUINO_ARCH_ESP32) && !defined(BOARD_HAS_PSRAM) + if ((!pinManager.isPinOk(pinNr, false)) || (pinManager.getPinOwner(pinNr) == PinOwner::SPI_RAM)) { // WLEDMM add SPIRAM pins as "reserved" (pico boards) + #else + if (!pinManager.isPinOk(pinNr, false)) { + #endif sprintf(pinString, "%s%d", strlen(rsvd)==rsLen?"":",", pinNr); strcat(rsvd, pinString); } From 1235cd5a532da65ff5f557473e208b8ef3e12806 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 7 May 2024 17:19:18 +0200 Subject: [PATCH 08/49] oops remove accidentally committed file --- boards/lolin_s3_mini.json | 47 --------------------------------------- 1 file changed, 47 deletions(-) delete mode 100644 boards/lolin_s3_mini.json diff --git a/boards/lolin_s3_mini.json b/boards/lolin_s3_mini.json deleted file mode 100644 index f8560fa7..00000000 --- a/boards/lolin_s3_mini.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "build": { - "arduino": { - "ldscript": "esp32s3_out.ld", - "memory_type": "qio_qspi" - }, - "core": "esp32", - "extra_flags": [ - "-DBOARD_HAS_PSRAM", - "-DARDUINO_LOLIN_S3_MINI", - "-DARDUINO_USB_MODE=1" - ], - "f_cpu": "240000000L", - "f_flash": "80000000L", - "flash_mode": "qio", - "hwids": [ - [ - "0x303A", - "0x8167" - ] - ], - "mcu": "esp32s3", - "variant": "esp32s3" - }, - "connectivity": [ - "bluetooth", - "wifi" - ], - "debug": { - "openocd_target": "esp32s3.cfg" - }, - "frameworks": [ - "arduino", - "espidf" - ], - "name": "WEMOS LOLIN S3 Mini", - "upload": { - "flash_size": "4MB", - "maximum_ram_size": 327680, - "maximum_size": 4194304, - "require_upload_port": true, - "speed": 460800 - }, - "url": "https://www.wemos.cc/en/latest/s3/index.html", - "vendor": "WEMOS" -} - \ No newline at end of file From 9b3f644737aff61f52cea801642d51b30e33e03f Mon Sep 17 00:00:00 2001 From: Brandon502 <105077712+Brandon502@users.noreply.github.com> Date: Tue, 7 May 2024 15:57:30 -0400 Subject: [PATCH 09/49] Game of Life changes Uses struct to store values. Changed glider check method. --- wled00/FX.cpp | 72 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 26 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 23bcf960..da695134 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5152,6 +5152,14 @@ void setBitValue(uint8_t* byteArray, size_t arraySize, size_t n, bool value) { else byteArray[byteIndex] &= ~(1 << bitIndex); } +// create game of life struct to hold cells and future cells +struct gameOfLife { + uint8_t* cells; + uint8_t* futureCells; + uint8_t gliderLength; + uint16_t oscillatorCRC; + uint16_t spaceshipCRC; +}; uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https://natureofcode.com/book/chapter-7-cellular-automata/ // and https://github.com/DougHaber/nlife-color , Modified By: Brandon Butler if (!strip.isMatrix) return mode_static(); // not a 2D set-up @@ -5159,13 +5167,15 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t dataSize = sizeof(byte) * SEGMENT.length()/8; // using width*height prevents reallocation if mirroring is enabled - const uint16_t crcBufferLen = 2; - const uint16_t totalSize = dataSize*2 + sizeof(uint16_t)*crcBufferLen; + const uint16_t totalSize = dataSize*2 + sizeof(gameOfLife); - if (!SEGENV.allocateData(dataSize*2 + sizeof(uint16_t)*crcBufferLen)) return mode_static(); //allocation failed - byte *cells = reinterpret_cast(SEGENV.data); - byte *futureCells = reinterpret_cast(SEGENV.data + dataSize); - uint16_t *crcBuffer = reinterpret_cast(SEGENV.data + dataSize*2); + if (!SEGENV.allocateData(totalSize)) return mode_static(); //allocation failed + gameOfLife* gol = reinterpret_cast(SEGENV.data); + + if (gol->cells == nullptr) { + gol->cells = new uint8_t[dataSize]; + gol->futureCells = new uint8_t[dataSize]; + } uint16_t &generation = SEGENV.aux0; uint16_t &pauseFrames = SEGENV.aux1; @@ -5183,27 +5193,39 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { uint8_t state = (random8() < 82) ? 1 : 0; // ~32% chance of being alive if (state == 0) { - setBitValue(cells, dataSize, y * cols + x, false); - setBitValue(futureCells, dataSize, y * cols + x, false); + setBitValue(gol->cells, dataSize, y * cols + x, false); + setBitValue(gol->futureCells, dataSize, y * cols + x, false); if (SEGMENT.check2) continue; SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?backgroundColor : RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0)); } else { - setBitValue(cells, dataSize, y * cols + x, true); - setBitValue(futureCells, dataSize, y * cols + x, true); + setBitValue(gol->cells, dataSize, y * cols + x, true); + setBitValue(gol->futureCells, dataSize, y * cols + x, true); color = SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0); SEGMENT.setPixelColorXY(x,y,!SEGMENT.check1?color : RGBW32(color.r, color.g, color.b, 0)); } } - //Clear repeatDetection - memset(crcBuffer, 0, sizeof(uint16_t)*crcBufferLen); + + //Clear CRCs + gol->oscillatorCRC = 0; + gol->spaceshipCRC = 0; + + //Calculate glider length LCM(rows,cols)*4 + uint8_t a = rows; + uint8_t b = cols; + while (b) { + uint8_t t = b; + b = a % b; + a = t; + } + gol->gliderLength = cols * rows / a * 4; return FRAMETIME; } //Redraw immediately if overlay to avoid flicker if (SEGMENT.check2) { for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { //redraw foreground/alive - if (getBitValue(cells, dataSize, y * cols + x)) { + if (getBitValue(gol->cells, dataSize, y * cols + x)) { color = SEGMENT.getPixelColorXY(x,y); SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?color : RGBW32(color.r, color.g, color.b, 0)); } @@ -5237,7 +5259,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: } cIndex = cY * cols + cX; // count neighbors and store upto 3 neighbor colors - if (getBitValue(cells, dataSize, cIndex)) { //if alive + if (getBitValue(gol->cells, dataSize, cIndex)) { //if alive neighbors++; color = SEGMENT.getPixelColorXY(cX, cY); if (color == backgroundColor) continue; //parent just died, color lost @@ -5247,16 +5269,16 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: } // Rules of Life - bool cellValue = getBitValue(cells, dataSize, y * cols + x); + bool cellValue = getBitValue(gol->cells, dataSize, y * cols + x); if ((cellValue) && (neighbors < 2 || neighbors > 3)) { // Loneliness or overpopulation cellChanged = true; - setBitValue(futureCells, dataSize, y * cols + x, false); + setBitValue(gol->futureCells, dataSize, y * cols + x, false); if (!SEGMENT.check2) SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?backgroundColor : RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0)); } else if (!(cellValue) && (neighbors == 3)) { // Reproduction - setBitValue(futureCells, dataSize, y * cols + x, true); + setBitValue(gol->futureCells, dataSize, y * cols + x, true); cellChanged = true; // find dominant color and assign it to a cell // no longer storing colors, if parent dies the color is lost @@ -5267,7 +5289,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: else dominantColor = nColors[random8()%3]; } else if (colorCount == 2) dominantColor = nColors[random8()%2]; // 1 leading parent died - else if (colorCount == 1) dominantColor = nColors[0]; // 2 leading parents survived + else if (colorCount == 1) dominantColor = nColors[0]; // 2 leading parents died else dominantColor = color; // all parents died last used color // mutate color chance if (random8() < SEGMENT.intensity) dominantColor = !SEGMENT.check1?SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0): random16()*random16(); @@ -5277,23 +5299,21 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: } } //update cell values - memcpy(cells, futureCells, dataSize); - /////////Repeat Detection///////// - const byte oscillatorCheck = 0; - const byte spaceshipCheck = 1; + memcpy(gol->cells, gol->futureCells, dataSize); + // Get current crc value - uint16_t crc = crc16((const unsigned char*)cells, dataSize); + uint16_t crc = crc16((const unsigned char*)gol->cells, dataSize); bool repetition = false; - if (!cellChanged || crc == crcBuffer[oscillatorCheck] || crc == crcBuffer[spaceshipCheck]) repetition = true; //check if cell born this gen and previous stored crc values + if (!cellChanged || crc == gol->oscillatorCRC || crc == gol->spaceshipCRC) repetition = true; //check if cell changed this gen and compare previous stored crc values if (repetition) { generation = 0; // reset on next call pauseFrames = 50; return FRAMETIME; } // Update CRC values - if (generation % 16 == 0) crcBuffer[oscillatorCheck] = crc; - if (generation % (4*max(rows,cols)) == 0) crcBuffer[spaceshipCheck] = crc; + if (generation % 16 == 0) gol->oscillatorCRC = crc; + if (generation % gol->gliderLength == 0) gol->spaceshipCRC = crc; generation++; SEGENV.step = strip.now; From 86a18ce6c2ea16ddb51b80cd3f5997c39103a19c Mon Sep 17 00:00:00 2001 From: Brandon502 <105077712+Brandon502@users.noreply.github.com> Date: Tue, 7 May 2024 17:26:48 -0400 Subject: [PATCH 10/49] Game of Life changes Helper functions are now static and the arraySize parameter was removed. Added failsafe for repeat patterns. --- wled00/FX.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index da695134..585c5353 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5138,13 +5138,13 @@ static const char _data_FX_MODE_2DFRIZZLES[] PROGMEM = "Frizzles@X frequency,Y f /////////////////////////////////////////// // 2D Cellular Automata Game of life // /////////////////////////////////////////// -bool getBitValue(const uint8_t* byteArray, size_t arraySize, size_t n) { +static bool getBitValue(const uint8_t* byteArray, size_t n) { size_t byteIndex = n / 8; size_t bitIndex = n % 8; uint8_t byte = byteArray[byteIndex]; return (byte >> bitIndex) & 1; } -void setBitValue(uint8_t* byteArray, size_t arraySize, size_t n, bool value) { +static void setBitValue(uint8_t* byteArray, size_t n, bool value) { size_t byteIndex = n / 8; size_t bitIndex = n % 8; if (value) @@ -5177,7 +5177,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: gol->futureCells = new uint8_t[dataSize]; } - uint16_t &generation = SEGENV.aux0; + uint16_t &generation = SEGENV.aux0; //rename aux0 and aux1 for readability (not needed) uint16_t &pauseFrames = SEGENV.aux1; CRGB backgroundColor = SEGCOLOR(1); CRGB color; @@ -5193,14 +5193,14 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { uint8_t state = (random8() < 82) ? 1 : 0; // ~32% chance of being alive if (state == 0) { - setBitValue(gol->cells, dataSize, y * cols + x, false); - setBitValue(gol->futureCells, dataSize, y * cols + x, false); + setBitValue(gol->cells, y * cols + x, false); + setBitValue(gol->futureCells, y * cols + x, false); if (SEGMENT.check2) continue; SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?backgroundColor : RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0)); } else { - setBitValue(gol->cells, dataSize, y * cols + x, true); - setBitValue(gol->futureCells, dataSize, y * cols + x, true); + setBitValue(gol->cells, y * cols + x, true); + setBitValue(gol->futureCells, y * cols + x, true); color = SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0); SEGMENT.setPixelColorXY(x,y,!SEGMENT.check1?color : RGBW32(color.r, color.g, color.b, 0)); } @@ -5225,7 +5225,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: if (SEGMENT.check2) { for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { //redraw foreground/alive - if (getBitValue(gol->cells, dataSize, y * cols + x)) { + if (getBitValue(gol->cells, y * cols + x)) { color = SEGMENT.getPixelColorXY(x,y); SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?color : RGBW32(color.r, color.g, color.b, 0)); } @@ -5249,17 +5249,17 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: for (int i = -1; i <= 1; i++) for (int j = -1; j <= 1; j++) { // iterate through 3*3 matrix if (i==0 && j==0) continue; // ignore itself - if (SEGMENT.check3) { //wrap around - cX = (x+i+cols) % cols; - cY = (y+j+rows) % rows; - } else { + if (!SEGMENT.check3 || generation % 1500 == 0) { //no wrap disable wrap every 1500 generations to prevent undetected repeats cX = x+i; cY = y+j; if (cX < 0 || cY < 0 || cX >= cols || cY >= rows) continue; //skip if out of bounds + } else { //wrap around + cX = (x+i+cols) % cols; + cY = (y+j+rows) % rows; } cIndex = cY * cols + cX; // count neighbors and store upto 3 neighbor colors - if (getBitValue(gol->cells, dataSize, cIndex)) { //if alive + if (getBitValue(gol->cells, cIndex)) { //if alive neighbors++; color = SEGMENT.getPixelColorXY(cX, cY); if (color == backgroundColor) continue; //parent just died, color lost @@ -5269,16 +5269,16 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: } // Rules of Life - bool cellValue = getBitValue(gol->cells, dataSize, y * cols + x); + bool cellValue = getBitValue(gol->cells, y * cols + x); if ((cellValue) && (neighbors < 2 || neighbors > 3)) { // Loneliness or overpopulation cellChanged = true; - setBitValue(gol->futureCells, dataSize, y * cols + x, false); + setBitValue(gol->futureCells, y * cols + x, false); if (!SEGMENT.check2) SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?backgroundColor : RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0)); } else if (!(cellValue) && (neighbors == 3)) { // Reproduction - setBitValue(gol->futureCells, dataSize, y * cols + x, true); + setBitValue(gol->futureCells, y * cols + x, true); cellChanged = true; // find dominant color and assign it to a cell // no longer storing colors, if parent dies the color is lost From adf5afe84aac439e39e0b3e6ebace2a79f759049 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Wed, 8 May 2024 12:52:07 +0200 Subject: [PATCH 11/49] include the GPLv3 permission statement in some MM-specific files Please note that these statements are included for clarification purposes only. WLED-MM specific source code is always provided under GPLv3, see LICENSE. --- usermods/audioreactive/audio_reactive.h | 19 +++++++++++++++++ usermods/audioreactive/audio_source.h | 21 +++++++++++++++++++ .../usermod_v2_auto_playlist.h | 20 ++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 6e4189fe..971c4b1c 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -1,5 +1,24 @@ #pragma once +/* + @title MoonModules WLED - audioreactive usermod + @file audio_reactive.h + @repo https://github.com/MoonModules/WLED, submit changes to this file as PRs to MoonModules/WLED + @Authors https://github.com/MoonModules/WLED/commits/mdev/ + @Copyright © 2024 Github MoonModules Commit Authors (contact moonmodules@icloud.com for details) + @license GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 + + This file is part of the MoonModules WLED fork also known as "WLED-MM". + WLED-MM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + WLED-MM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with WLED-MM. If not, see . +*/ + + #include "wled.h" #ifdef ARDUINO_ARCH_ESP32 diff --git a/usermods/audioreactive/audio_source.h b/usermods/audioreactive/audio_source.h index 63ee584a..cf3e3b74 100644 --- a/usermods/audioreactive/audio_source.h +++ b/usermods/audioreactive/audio_source.h @@ -1,4 +1,25 @@ #pragma once + +/* + @title MoonModules WLED - audioreactive usermod + @file audio_source.h + @repo https://github.com/MoonModules/WLED, submit changes to this file as PRs to MoonModules/WLED + @Authors https://github.com/MoonModules/WLED/commits/mdev/ + @Copyright © 2024 Github MoonModules Commit Authors (contact moonmodules@icloud.com for details) + @license GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 + + This file is part of the MoonModules WLED fork also known as "WLED-MM". + WLED-MM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + WLED-MM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with WLED-MM. If not, see . + +*/ + + #ifdef ARDUINO_ARCH_ESP32 #include #include "wled.h" diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index d08f295f..88d62654 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -1,5 +1,25 @@ #pragma once +/* + @title MoonModules WLED - auto-playlist usermod + @file usermod_v2_auto_playlist.h + @repo https://github.com/MoonModules/WLED, submit changes to this file as PRs to MoonModules/WLED + @Authors https://github.com/MoonModules/WLED/commits/mdev/ + @Copyright © 2024 Github MoonModules Commit Authors (contact moonmodules@icloud.com for details) + @license GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 + + This file is part of the MoonModules WLED fork also known as "WLED-MM". + WLED-MM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + WLED-MM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with WLED-MM. If not, see . + +*/ + + #ifdef WLED_DEBUG #ifndef USERMOD_AUTO_PLAYLIST_DEBUG #define USERMOD_AUTO_PLAYLIST_DEBUG From e08ae84a83b19504e401be86efacd5c9e7a918e7 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Wed, 8 May 2024 12:18:31 -0400 Subject: [PATCH 12/49] Allow 8 AoftAP clients (for Dom) Increases the allowed SoftAP clients so folks doing syncing "in the field" can connect more devices without a proper WiFi network. --- wled00/wled.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 11ebcf72..71050d99 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -901,7 +901,7 @@ void WLED::initAP(bool resetAP) USER_PRINT(F("Opening access point ")); // WLEDMM USER_PRINTLN(apSSID); // WLEDMM WiFi.softAPConfig(IPAddress(4, 3, 2, 1), IPAddress(4, 3, 2, 1), IPAddress(255, 255, 255, 0)); - WiFi.softAP(apSSID, apPass, apChannel, apHide); + WiFi.softAP(apSSID, apPass, apChannel, apHide, 8); // WLED-MM allow up to 8 clients for ad-hoc "in the field" syncing. #if defined(LOLIN_WIFI_FIX) && (defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32S3)) WiFi.setTxPower(WIFI_POWER_8_5dBm); #endif From 5a377103b5e0edca0d1908bd3c8f080c93bc431f Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 8 May 2024 21:04:07 +0200 Subject: [PATCH 13/49] small accuracy improvement (int)currentResult is a truncation, so we need to add 0.5 for proper rounding. also changed inefficient "constrain" into faster max(min( ...)) --- usermods/audioreactive/audio_reactive.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 971c4b1c..f9effa2a 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -940,7 +940,7 @@ static void postProcessFFTResults(bool noiseGateOpen, int numberOfChannels) // p if (post_gain < 1.0f) post_gain = ((post_gain -1.0f) * 0.8f) +1.0f; currentResult *= post_gain; } - fftResult[i] = constrain((int)currentResult, 0, 255); + fftResult[i] = max(min((int)(currentResult+0.5f), 255), 0); // +0.5 for proper rounding } } //////////////////// From 7b587272549d7aba742e52f5f812d77d643c9808 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 8 May 2024 21:39:38 +0200 Subject: [PATCH 14/49] bugfix for sound sync - fftResult[] did not use 255 transmitData.fftResult[] and fftResult[] are both uint8, no need to constrain the value. --- usermods/audioreactive/audio_reactive.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index f9effa2a..e8580f1e 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -1590,7 +1590,7 @@ class AudioReactive : public Usermod { transmitData.zeroCrossingCount = zeroCrossingCount; for (int i = 0; i < NUM_GEQ_CHANNELS; i++) { - transmitData.fftResult[i] = (uint8_t)constrain(fftResult[i], 0, 254); + transmitData.fftResult[i] = fftResult[i]; } transmitData.FFT_Magnitude = my_magnitude; From 1cd18e7f08db075891e4aa64425e87c94f58e46e Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Fri, 10 May 2024 19:22:41 +0200 Subject: [PATCH 15/49] Live preview: faster handling of "off" --- wled00/ws.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/wled00/ws.cpp b/wled00/ws.cpp index 08624b1e..32420fcc 100644 --- a/wled00/ws.cpp +++ b/wled00/ws.cpp @@ -186,6 +186,7 @@ void sendDataWs(AsyncWebSocketClient * client) // WLEDMM function to recover full-bright pixel (based on code from upstream alt-buffer, which is based on code from NeoPixelBrightnessBus) static uint32_t restoreColorLossy(uint32_t c, uint_fast8_t _restaurationBri) { if (_restaurationBri == 255) return c; + if (_restaurationBri == 0) return 0; uint8_t* chan = (uint8_t*) &c; for (uint_fast8_t i=0; i<4; i++) { uint_fast16_t val = chan[i]; From 214461342365c3c1cb734b62480328a05ef8e8dd Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 11 May 2024 13:57:49 +0200 Subject: [PATCH 16/49] compatibility with upstream arduinoFFT 2.x its still recommended to use the softhack007 version - -> optimized for ESP32 boards, and reduced memory footprint https://github.com/softhack007/arduinoFFT.git#develop @ 1.9.2 --- usermods/audioreactive/audio_reactive.h | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index e8580f1e..b958184c 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -372,9 +372,6 @@ constexpr uint16_t samplesFFT_2 = 256; // meaningfull part of FFT resul // These are the input and output vectors. Input vectors receive computed results from FFT. static float vReal[samplesFFT] = {0.0f}; // FFT sample inputs / freq output - these are our raw result bins static float vImag[samplesFFT] = {0.0f}; // imaginary parts -#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT -static float windowWeighingFactors[samplesFFT] = {0.0f}; -#endif #ifdef FFT_MAJORPEAK_HUMAN_EAR static float pinkFactors[samplesFFT] = {0.0f}; // "pink noise" correction factors @@ -400,7 +397,14 @@ constexpr float binWidth = SAMPLE_RATE / (float)samplesFFT; // frequency range o #include #ifdef UM_AUDIOREACTIVE_USE_NEW_FFT -static ArduinoFFT FFT = ArduinoFFT( vReal, vImag, samplesFFT, SAMPLE_RATE, windowWeighingFactors); +#if defined(FFT_LIB_REV) && FFT_LIB_REV > 0x19 + // arduinoFFT 2.x has a slightly different API + static ArduinoFFT FFT = ArduinoFFT( vReal, vImag, samplesFFT, SAMPLE_RATE, true); +#else + // recommended version optimized by @softhack007 (API version 1.9) + static float windowWeighingFactors[samplesFFT] = {0.0f}; // cache for FFT windowing factors + static ArduinoFFT FFT = ArduinoFFT( vReal, vImag, samplesFFT, SAMPLE_RATE, windowWeighingFactors); +#endif #else static arduinoFFT FFT = arduinoFFT(vReal, vImag, samplesFFT, SAMPLE_RATE); #endif @@ -646,9 +650,14 @@ void FFTcode(void * parameter) #endif #ifdef UM_AUDIOREACTIVE_USE_NEW_FFT - FFT.majorPeak(FFT_MajorPeak, FFT_Magnitude); // let the effects know which freq was most dominant + #if defined(FFT_LIB_REV) && FFT_LIB_REV > 0x19 + // arduinoFFT 2.x has a slightly different API + FFT.majorPeak(&FFT_MajorPeak, &FFT_Magnitude); #else - FFT.MajorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant + FFT.majorPeak(FFT_MajorPeak, FFT_Magnitude); // let the effects know which freq was most dominant + #endif + #else + FFT.MajorPeak(&FFT_MajorPeak, &FFT_Magnitude); #endif if (FFT_MajorPeak < (SAMPLE_RATE / samplesFFT)) {FFT_MajorPeak = 1.0f; FFT_Magnitude = 0;} // too low - use zero From 250000de08f2825bd999443a6dd6887dc64006ed Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Sat, 11 May 2024 12:37:39 -0400 Subject: [PATCH 17/49] Removed check for WLEDMM_SLOWPATH as it breaks WLEDMM_SLOWPATH Seemingly this breaks WLEDMM_SLOWPATH, which forces RMT for all channels to reduce glitching in big installations. This check actually makes the glitching much worse versus completely fixed in these situations, for reasons I don't fully understand yet. --- wled00/bus_wrapper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h index 327da0ad..e2a494d0 100644 --- a/wled00/bus_wrapper.h +++ b/wled00/bus_wrapper.h @@ -390,7 +390,7 @@ class PolyBus { }; static void* create(uint8_t busType, uint8_t* pins, uint16_t len, uint8_t channel, uint16_t clock_kHz = 0U) { #if defined(ARDUINO_ARCH_ESP32) && !(defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3)) - #if defined(WLEDMM_FASTPATH) && !defined(WLEDMM_SLOWPATH) // WLEDMM only for fastpath builds + #if defined(WLEDMM_FASTPATH) // && !defined(WLEDMM_SLOWPATH) // WLEDMM only for fastpath builds. TroyHacks: Removed the WLEDMM_SLOWPATH as it breaks WLEDMM_SLOWPATH // NOTE: "channel" is only used on ESP32 (and its variants) for RMT channel allocation // since 0.15.0-b3 I2S1 is favoured for classic ESP32 and moved to position 0 (channel 0) so we need to subtract 1 for correct RMT allocation if (channel > 0) channel--; // accommodate I2S1 which is used as 1st bus on classic ESP32 From 6d2fc5040cdac7684e1fbef088a707a31b639d0b Mon Sep 17 00:00:00 2001 From: Brandon502 <105077712+Brandon502@users.noreply.github.com> Date: Sat, 11 May 2024 17:03:58 -0400 Subject: [PATCH 18/49] Game of Life Change Removed sizeof(byte) --- wled00/FX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 585c5353..ae02998d 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5166,7 +5166,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - const uint16_t dataSize = sizeof(byte) * SEGMENT.length()/8; // using width*height prevents reallocation if mirroring is enabled + const uint16_t dataSize = SEGMENT.length() / 8; const uint16_t totalSize = dataSize*2 + sizeof(gameOfLife); if (!SEGENV.allocateData(totalSize)) return mode_static(); //allocation failed From 118163d982a1cfa5a11652310d8ec5701bebb936 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Mon, 13 May 2024 01:16:27 -0400 Subject: [PATCH 19/49] Didn't work. Reverting. --- wled00/bus_wrapper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h index e2a494d0..ca3c89a1 100644 --- a/wled00/bus_wrapper.h +++ b/wled00/bus_wrapper.h @@ -390,7 +390,7 @@ class PolyBus { }; static void* create(uint8_t busType, uint8_t* pins, uint16_t len, uint8_t channel, uint16_t clock_kHz = 0U) { #if defined(ARDUINO_ARCH_ESP32) && !(defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3)) - #if defined(WLEDMM_FASTPATH) // && !defined(WLEDMM_SLOWPATH) // WLEDMM only for fastpath builds. TroyHacks: Removed the WLEDMM_SLOWPATH as it breaks WLEDMM_SLOWPATH + #if defined(WLEDMM_FASTPATH) && !defined(WLEDMM_SLOWPATH) // WLEDMM only for fastpath builds. // NOTE: "channel" is only used on ESP32 (and its variants) for RMT channel allocation // since 0.15.0-b3 I2S1 is favoured for classic ESP32 and moved to position 0 (channel 0) so we need to subtract 1 for correct RMT allocation if (channel > 0) channel--; // accommodate I2S1 which is used as 1st bus on classic ESP32 From e17c30ebd702287db8d90380f483a46339508f05 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Wed, 15 May 2024 14:22:10 +0200 Subject: [PATCH 20/49] Merge pull request #3961 from Brandon502/0_15 Added Pinwheel Expand 1D ->2D effect mapping mode --- wled00/FX.h | 2 +- wled00/FX_fcn.cpp | 125 +++++++++++++++++++++++++++++++++------------- 2 files changed, 92 insertions(+), 35 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index 037ef181..acd03139 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -355,7 +355,7 @@ typedef enum mapping1D2D { M12_jMap = 4, //WLEDMM jMap M12_sCircle = 5, //WLEDMM Circle M12_sBlock = 6, //WLEDMM Block - M12_sPinWheel = 7 //WLEDMM PinWheel + M12_sPinwheel = 7 //WLEDMM Pinwheel } mapping1D2D_t; // segment, 72 bytes diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 01af8b52..9ce628bd 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -793,14 +793,41 @@ void Segment::deletejMap() { } -// WLEDMM constants for mapping mode "Pinwheel" -constexpr int Pinwheel_Steps_Medium = 208; // no holes up to 32x32; 60fps -constexpr int Pinwheel_Size_Medium = 32; // larger than this -> use "Big" -constexpr int Pinwheel_Steps_Big = 360; // no holes expected up to 58x58; 40fps -constexpr float Int_to_Rad_Med = (DEG_TO_RAD * 360) / Pinwheel_Steps_Medium; // conversion: from 0...208 to Radians -constexpr float Int_to_Rad_Big = (DEG_TO_RAD * 360) / Pinwheel_Steps_Big; // conversion: from 0...360 to Radians -// WLEDMM end +// Constants for mapping mode "Pinwheel" +#ifndef WLED_DISABLE_2D +constexpr int Pinwheel_Steps_Small = 72; // no holes up to 16x16 +constexpr int Pinwheel_Size_Small = 16; // larger than this -> use "Medium" +constexpr int Pinwheel_Steps_Medium = 192; // no holes up to 32x32 +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 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 int Fixed_Scale = 512; // fixpoint scaling factor (9bit for fraction) + +// Pinwheel helper function: pixel index to radians +static float getPinwheelAngle(int i, int vW, int vH) { + int maxXY = max(vW, 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; + // else + return float(i) * Int_to_Rad_XL; +} +// Pinwheel helper function: matrix dimensions to number of rays +static int getPinwheelLength(int vW, int vH) { + int maxXY = max(vW, 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; + // else + return Pinwheel_Steps_XL; +} +#endif // 1D strip uint16_t Segment::virtualLength() const { @@ -831,12 +858,8 @@ uint16_t Segment::virtualLength() const { else vLen = max(vW,vH) * 0.5; // get the longest dimension break; - case M12_sPinWheel: //WLEDMM - //vLen = full circle - if (max(vW,vH) <= Pinwheel_Size_Medium) - vLen = Pinwheel_Steps_Medium; - else - vLen = Pinwheel_Steps_Big; + case M12_sPinwheel: + vLen = getPinwheelLength(vW, vH); break; } return vLen; @@ -978,32 +1001,46 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATT } } break; - case M12_sPinWheel: { // WLEDMM - // i = angle --> 0 through 359 (Big), OR 0 through 208 (Medium) + case M12_sPinwheel: { + // i = angle --> 0 - 296 (Big), 0 - 192 (Medium), 0 - 72 (Small) float centerX = roundf((vW-1) / 2.0f); float centerY = roundf((vH-1) / 2.0f); - // int maxDistance = sqrt(centerX * centerX + centerY * centerY) + 1; - float angleRad = (max(vW,vH) > Pinwheel_Size_Medium) ? float(i) * Int_to_Rad_Big : float(i) * Int_to_Rad_Med; // angle in radians + float angleRad = getPinwheelAngle(i, vW, vH); // angle in radians float cosVal = cosf(angleRad); float sinVal = sinf(angleRad); + // avoid re-painting the same pixel + int lastX = INT_MIN; // impossible position + int lastY = INT_MIN; // impossible position // draw line at angle, starting at center and ending at the segment edge // we use fixed point math for better speed. Starting distance is 0.5 for better rounding - constexpr int_fast32_t Fixed_Scale = 512; // fixpoint scaling factor - int_fast32_t posx = (centerX + 0.5f * cosVal) * Fixed_Scale; // X starting position in fixed point - int_fast32_t posy = (centerY + 0.5f * sinVal) * Fixed_Scale; // Y starting position in fixed point - int_fast16_t inc_x = cosVal * Fixed_Scale; // X increment per step (fixed point) - int_fast16_t inc_y = sinVal * Fixed_Scale; // Y increment per step (fixed point) + // int_fast16_t and int_fast32_t types changed to int, minimum bits commented + int posx = (centerX + 0.5f * cosVal) * Fixed_Scale; // X starting position in fixed point 18 bit + int posy = (centerY + 0.5f * sinVal) * Fixed_Scale; // Y starting position in fixed point 18 bit + int inc_x = cosVal * Fixed_Scale; // X increment per step (fixed point) 10 bit + int inc_y = sinVal * Fixed_Scale; // Y increment per step (fixed point) 10 bit int32_t maxX = vW * Fixed_Scale; // X edge in fixedpoint int32_t maxY = vH * Fixed_Scale; // Y edge in fixedpoint - // draw until we hit any edge - while ((posx > 0) && (posy > 0) && (posx < maxX) && (posy < maxY)) { + + // Odd rays start further from center if prevRay started at center. + static int prevRay = INT_MIN; // previous ray number + if ((i % 2 == 1) && (i - 1 == prevRay || i + 1 == prevRay)) { + int jump = min(vW/3, vH/3); // can add 2 if using medium pinwheel + posx += inc_x * jump; + posy += inc_y * jump; + } + prevRay = i; + + // draw ray until we hit any edge + while ((posx >= 0) && (posy >= 0) && (posx < maxX) && (posy < maxY)) { // scale down to integer (compiler will replace division with appropriate bitshift) int x = posx / Fixed_Scale; int y = posy / Fixed_Scale; // set pixel - setPixelColorXY(x, y, col); + if (x != lastX || y != lastY) setPixelColorXY(x, y, col); // only paint if pixel position is different + lastX = x; + lastY = y; // advance to next position posx += inc_x; posy += inc_y; @@ -1154,16 +1191,36 @@ uint32_t Segment::getPixelColor(int i) else return getPixelColorXY(vW / 2, vH / 2 - i - 1); break; - case M12_sPinWheel: //WLEDMM - // not 100% accurate, returns outer edge of circle - float distance = max(1.0f, min(vH-1, vW-1) / 2.0f); - float centerX = (vW - 1) / 2.0f; - float centerY = (vH - 1) / 2.0f; - float angleRad = (max(vW,vH) > Pinwheel_Size_Medium) ? float(i) * Int_to_Rad_Big : float(i) * Int_to_Rad_Med; // angle in radians - int x = roundf(centerX + distance * cosf(angleRad)); - int y = roundf(centerY + distance * sinf(angleRad)); + case M12_sPinwheel: + // not 100% accurate, returns pixel at outer edge + // i = angle --> 0 - 296 (Big), 0 - 192 (Medium), 0 - 72 (Small) + float centerX = roundf((vW-1) / 2.0f); + float centerY = roundf((vH-1) / 2.0f); + float angleRad = getPinwheelAngle(i, vW, vH); // angle in radians + float cosVal = cosf(angleRad); + float sinVal = sinf(angleRad); + + int posx = (centerX + 0.5f * cosVal) * Fixed_Scale; // X starting position in fixed point 18 bit + int posy = (centerY + 0.5f * sinVal) * Fixed_Scale; // Y starting position in fixed point 18 bit + int inc_x = cosVal * Fixed_Scale; // X increment per step (fixed point) 10 bit + int inc_y = sinVal * Fixed_Scale; // Y increment per step (fixed point) 10 bit + int32_t maxX = vW * Fixed_Scale; // X edge in fixedpoint + int32_t maxY = vH * Fixed_Scale; // Y edge in fixedpoint + + // trace ray from center until we hit any edge - to avoid rounding problems, we use the same method as in setPixelColor + int x = INT_MIN; + int y = INT_MIN; + while ((posx >= 0) && (posy >= 0) && (posx < maxX) && (posy < maxY)) { + // scale down to integer (compiler will replace division with appropriate bitshift) + x = posx / Fixed_Scale; + y = posy / Fixed_Scale; + // advance to next position + posx += inc_x; + posy += inc_y; + } return getPixelColorXY(x, y); - } + break; + } return 0; } #endif From 64b1d768935b2bbae70021fcd5ae410de7a3882a Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Wed, 15 May 2024 15:34:53 +0200 Subject: [PATCH 21/49] Palette loading optimisation - fixes #3978 - FX: Firenoise can use selected palette --- wled00/FX.cpp | 20 ++++++++++---------- wled00/FX.h | 10 ++++++---- wled00/FX_fcn.cpp | 21 +++++++++------------ wled00/wled.h | 2 +- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 0565c0b2..b4ddf801 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5096,25 +5096,25 @@ uint16_t mode_2Dfirenoise(void) { // firenoise2d. By Andrew Tuline SEGMENT.fill(BLACK); } - uint16_t xscale = SEGMENT.intensity*4; - uint32_t yscale = SEGMENT.speed*8; - uint8_t indexx = 0; + unsigned xscale = SEGMENT.intensity*4; + unsigned yscale = SEGMENT.speed*8; + unsigned indexx = 0; - SEGPALETTE = CRGBPalette16( CRGB(0,0,0), CRGB(0,0,0), CRGB(0,0,0), CRGB(0,0,0), - CRGB::Red, CRGB::Red, CRGB::Red, CRGB::DarkOrange, - CRGB::DarkOrange,CRGB::DarkOrange, CRGB::Orange, CRGB::Orange, - CRGB::Yellow, CRGB::Orange, CRGB::Yellow, CRGB::Yellow); + CRGBPalette16 pal = SEGMENT.check1 ? SEGPALETTE : CRGBPalette16(CRGB::Black, CRGB::Black, CRGB::Black, CRGB::Black, + CRGB::Red, CRGB::Red, CRGB::Red, CRGB::DarkOrange, + CRGB::DarkOrange,CRGB::DarkOrange, CRGB::Orange, CRGB::Orange, + CRGB::Yellow, CRGB::Orange, CRGB::Yellow, CRGB::Yellow); for (int j=0; j < cols; j++) { for (int i=0; i < rows; i++) { - indexx = inoise8(j*yscale*rows/255, i*xscale+strip.now/4); // We're moving along our Perlin map. - SEGMENT.setPixelColorXY(j, i, ColorFromPalette(SEGPALETTE, min(i*(indexx)>>4, 255), i*255/cols, LINEARBLEND)); // With that value, look up the 8 bit colour palette value and assign it to the current LED. + indexx = inoise8(j*yscale*rows/255, i*xscale+strip.now/4); // We're moving along our Perlin map. + SEGMENT.setPixelColorXY(j, i, ColorFromPalette(pal, min(i*(indexx)>>4, 255U), i*255/cols, LINEARBLEND)); // With that value, look up the 8 bit colour palette value and assign it to the current LED. } // for i } // for j return FRAMETIME; } // mode_2Dfirenoise() -static const char _data_FX_MODE_2DFIRENOISE[] PROGMEM = "Firenoise@X scale,Y scale;;!;2;pal=0"; //WLEDMM pal=0 +static const char _data_FX_MODE_2DFIRENOISE[] PROGMEM = "Firenoise@X scale,Y scale,,,,Palette;;!;2;pal=0"; //WLEDMM pal=0 ////////////////////////////// diff --git a/wled00/FX.h b/wled00/FX.h index acd03139..b8864db4 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -106,7 +106,7 @@ bool strip_uses_global_leds(void); // WLEDMM implemented in FX_fcn. //#define SEGCOLOR(x) strip._segments[strip.getCurrSegmentId()].currentColor(x, strip._segments[strip.getCurrSegmentId()].colors[x]) //#define SEGLEN strip._segments[strip.getCurrSegmentId()].virtualLength() #define SEGCOLOR(x) strip.segColor(x) /* saves us a few kbytes of code */ -#define SEGPALETTE strip._currentPalette +#define SEGPALETTE Segment::getCurrentPalette() #define SEGLEN strip._virtualSegmentLength /* saves us a few kbytes of code */ #define SPEED_FORMULA_L (5U + (50U*(255U - SEGMENT.speed))/SEGLEN) @@ -428,6 +428,9 @@ typedef struct Segment { size_t _dataLen; // WLEDMM uint16_t is too small static size_t _usedSegmentData; // WLEDMM uint16_t is too small + // perhaps this should be per segment, not static + static CRGBPalette16 _currentPalette; // palette used for current effect (includes transition, used in color_from_palette()) + // transition data, valid only if transitional==true, holds values during transition struct Transition { uint32_t _colorT[NUM_COLORS]; @@ -561,6 +564,7 @@ typedef struct Segment { static void addUsedSegmentData(int len) { _usedSegmentData += len; } void allocLeds(); //WLEDMM + inline static const CRGBPalette16 &getCurrentPalette(void) { return Segment::_currentPalette; } 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 setColor(uint8_t slot, uint32_t c); //returns true if changed @@ -605,7 +609,7 @@ typedef struct Segment { uint8_t currentMode(uint8_t modeNew); uint32_t currentColor(uint8_t slot, uint32_t colorNew); CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal); - CRGBPalette16 ¤tPalette(CRGBPalette16 &tgt, uint8_t paletteID); + void setCurrentPalette(void); // 1D strip uint16_t virtualLength(void) const; @@ -759,7 +763,6 @@ class WS2812FX { // 96 bytes panels(1), #endif // semi-private (just obscured) used in effect functions through macros - _currentPalette(CRGBPalette16(CRGB::Black)), _colors_t{0,0,0}, _virtualSegmentLength(0), // true private variables @@ -978,7 +981,6 @@ class WS2812FX { // 96 bytes // end 2D support void loadCustomPalettes(void); // loads custom palettes from JSON - CRGBPalette16 _currentPalette; // palette used for current effect (includes transition) std::vector customPalettes; // TODO: move custom palettes out of WS2812FX class // using public variables to reduce code size increase due to inline function getSegment() (with bounds checking) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 9ce628bd..cd5b69cf 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -95,6 +95,8 @@ CRGB *Segment::_globalLeds = nullptr; uint16_t Segment::maxWidth = DEFAULT_LED_COUNT; uint16_t Segment::maxHeight = 1; +CRGBPalette16 Segment::_currentPalette = CRGBPalette16(CRGB::Black); + // copy constructor - creates a new segment by copy from orig, but does not copy buffers. Does not modify orig! Segment::Segment(const Segment &orig) { DEBUG_PRINTLN(F("-- Copy segment constructor --")); @@ -295,7 +297,7 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) { static CRGBPalette16 prevRandomPalette = CRGBPalette16(CRGB(BLACK)); byte tcp[76] = { 255 }; //WLEDMM: prevent out-of-range access in loadDynamicGradientPalette() if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0; - if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0; + if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0; // TODO remove strip dependency by moving customPalettes out of strip //default palette. Differs depending on effect if (pal == 0) switch (mode) { case FX_MODE_FIRE_2012 : pal = 35; break; // heat palette @@ -458,18 +460,17 @@ uint32_t Segment::currentColor(uint8_t slot, uint32_t colorNew) { return transitional && _t ? color_blend(_t->_colorT[slot], colorNew, progress(), true) : colorNew; } -CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) { - loadPalette(targetPalette, pal); +void Segment::setCurrentPalette() { + loadPalette(_currentPalette, palette); if (transitional && _t && progress() < 0xFFFFU) { // blend palettes // there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time) // minimum blend time is 100ms maximum is 65535ms 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 + for (unsigned i = 0; i < noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, _currentPalette, 48); + _currentPalette = _t->_palT; // copy transitioning/temporary palette } - return targetPalette; } void Segment::handleTransition() { @@ -1517,11 +1518,7 @@ uint32_t Segment::color_from_palette(uint_fast16_t i, bool mapping, bool wrap, u uint_fast16_t vLen = mapping ? virtualLength() : 1; if (mapping && vLen > 1) paletteIndex = (i*255)/(vLen -1); if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end" - CRGB fastled_col; - CRGBPalette16 curPal; - if (transitional && _t) curPal = _t->_palT; - else loadPalette(curPal, palette); - fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global + CRGB fastled_col = ColorFromPalette(_currentPalette, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, 0); } @@ -1799,7 +1796,7 @@ void WS2812FX::service() { _colors_t[0] = seg.currentColor(0, seg.colors[0]); _colors_t[1] = seg.currentColor(1, seg.colors[1]); _colors_t[2] = seg.currentColor(2, seg.colors[2]); - seg.currentPalette(_currentPalette, seg.palette); + seg.setCurrentPalette(); // load actual palette 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]); diff --git a/wled00/wled.h b/wled00/wled.h index a0e4283e..b69956bd 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2405040 +#define VERSION 2405150 // 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_ From 9feec7f24ea906be811b1295096226e400de47e9 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Wed, 15 May 2024 15:35:14 +0200 Subject: [PATCH 22/49] Compiler warning fix --- wled00/cfg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 54c32f9c..24e923c8 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -795,7 +795,7 @@ void serializeConfig() { matrix["psl"] = strip.panelO.serpentine; JsonArray panels = matrix.createNestedArray(F("panels")); - for (uint8_t i=0; i Date: Thu, 16 May 2024 17:31:46 +0200 Subject: [PATCH 23/49] robustness improvements for game of life * fixed: dataSize could be too small if size is not a multiple of 8 * use size_t for memory size * clear LEDS on first run - just to make sure that buffer optimizations in segment class work as expected --- wled00/FX.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index ef03b80c..5d53b53d 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5175,8 +5175,8 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - const uint16_t dataSize = SEGMENT.length() / 8; - const uint16_t totalSize = dataSize*2 + sizeof(gameOfLife); + const size_t dataSize = (SEGMENT.length() / 8) + ((SEGMENT.length() % 8 != 0) ? 1 : 0); // add one byte when extra bits needed (length not a multiple of 8) + const size_t totalSize = dataSize*2 + sizeof(gameOfLife); if (!SEGENV.allocateData(totalSize)) return mode_static(); //allocation failed gameOfLife* gol = reinterpret_cast(SEGENV.data); @@ -5191,7 +5191,10 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: CRGB backgroundColor = SEGCOLOR(1); CRGB color; - if (SEGENV.call == 0) SEGMENT.setUpLeds(); + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); // to make sure that segment buffer and physical leds are aligned initially + } //start new game of life if ((SEGENV.call == 0 || generation == 0) && pauseFrames == 0) { SEGENV.step = strip.now; // .step = previous call time @@ -5303,7 +5306,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: // mutate color chance if (random8() < SEGMENT.intensity) dominantColor = !SEGMENT.check1?SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0): random16()*random16(); - if (SEGMENT.check1) dominantColor = RGBW32(dominantColor.r, dominantColor.g, dominantColor.b, 0); //WLEDMM support all colors) + if (SEGMENT.check1) dominantColor = RGBW32(dominantColor.r, dominantColor.g, dominantColor.b, 0); //WLEDMM support all colors SEGMENT.setPixelColorXY(x,y, dominantColor); } } From d29a4e3ead956eaa4e029dce79179504aaca5d2b Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Fri, 17 May 2024 21:20:55 +0200 Subject: [PATCH 24/49] (experimental) adding NeoPixelBus 2.7.9 --- platformio.ini | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/platformio.ini b/platformio.ini index 9471b484..c2e8f729 100644 --- a/platformio.ini +++ b/platformio.ini @@ -305,6 +305,8 @@ build_flags = -g -D CONFIG_ASYNC_TCP_TASK_STACK_SIZE=9472 ;; WLEDMM increase stack by 1.25Kb, as audioreactive needs bigger SETTINGS_STACK_BUF_SIZE #use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x -D LOROL_LITTLEFS + -D WLEDMM_TWOPATH ;; use I2S1 as the second bus --> ~15% faster on "V3" builds + ; -D WLEDMM_SLOWPATH ;; don't use I2S for LED bus ; -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3 default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv ;; WLED standard for 4MB flash: 1.4MB firmware, 1MB filesystem @@ -322,6 +324,7 @@ lib_deps = ; WLEDMM specific: use patched version of lorol LittleFS https://github.com/softhack007/LITTLEFS-threadsafe.git#master makuna/NeoPixelBus @ 2.7.5 + ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental ${env.lib_deps} ;; Compatibility with upstream --> you should prefer using ${common_mm.build_flags_S} and ${common_mm.lib_deps_S} @@ -348,6 +351,8 @@ build_flagsV4 = -g -DCONFIG_LITTLEFS_FOR_IDF_3_2 -DLFS_THREADSAFE -D CONFIG_ASYNC_TCP_USE_WDT=0 -D CONFIG_ASYNC_TCP_TASK_STACK_SIZE=9472 ;; WLEDMM increase stack by 1.25Kb, as audioreactive needs bigger SETTINGS_STACK_BUF_SIZE + ; -D WLEDMM_TWOPATH ;; use I2S1 as the second bus --> slightly faster on some setups + ; -D WLEDMM_SLOWPATH ;; don't use I2S for LED bus ; -DARDUINO_USB_CDC_ON_BOOT=0 ;; mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3 -D NO_GFX ; Disable the use of Adafruit_GFX by the HUB75 driver @@ -355,6 +360,7 @@ build_flagsV4 = -g lib_depsV4 = https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 ;; WLEDMM this must be first in the list, otherwise Aircoookie/ESPAsyncWebServer pulls in an older version of AsyncTCP !! makuna/NeoPixelBus @ 2.7.5 + ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental ${common_mm.HUB75_lib_deps} ${env.lib_deps} @@ -380,6 +386,7 @@ default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv lib_deps = https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 makuna/NeoPixelBus @ 2.7.5 + ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental ${env.lib_deps} [esp32s2] @@ -407,6 +414,7 @@ build_flags = -g lib_deps = https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 makuna/NeoPixelBus @ 2.7.5 + ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental ${env.lib_deps} [esp32c3] @@ -428,6 +436,7 @@ build_flags = -g lib_deps = https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 makuna/NeoPixelBus @ 2.7.5 + ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental ${env.lib_deps} [esp32s3] @@ -449,6 +458,7 @@ build_flags = -g lib_deps = https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 makuna/NeoPixelBus @ 2.7.5 + ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental ${env.lib_deps} From 0c7450407e2276a2dd8a70f60f0daf96bfd8f530 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Fri, 17 May 2024 21:24:10 +0200 Subject: [PATCH 25/49] WLEDMM_TWOPATH - use I2S driver for second output * TWOPATH is up to 20% faster in some situations. * user_print for showing the driver unit (I2S#n, RMT#x) on serial --- wled00/bus_manager.cpp | 5 +++++ wled00/bus_wrapper.h | 45 ++++++++++++++++++++++++++++++++++-------- wled00/wled.h | 2 +- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index af4e16ef..93ff1564 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -40,6 +40,11 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte #define DEBUG_PRINTF(x...) #endif #else + // un-define USER_PRINT macros from bus_wrapper.h + #undef DEBUG_PRINT + #undef DEBUG_PRINTF + #undef DEBUG_PRINTLN + #undef DEBUG_FLUSH // WLEDMM use wled.h #include "wled.h" #endif diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h index ca3c89a1..64dcd5b9 100644 --- a/wled00/bus_wrapper.h +++ b/wled00/bus_wrapper.h @@ -18,6 +18,28 @@ #endif // temporary end +// WLEDMM TroyHacks support - SLOWPATH has priority over TWOPATH +#ifdef WLEDMM_SLOWPATH +#undef WLEDMM_TWOPATH +#endif + +// WLEDMM repeat definition of USER_PRINT +bool canUseSerial(void); // WLEDMM (wled_serial.cpp) returns true if Serial can be used for debug output (i.e. not configured for other purpose) +#if defined(WLED_DEBUG_HOST) + #include "net_debug.h" + extern bool netDebugEnabled; + #define USER_PRINT(x) (netDebugEnabled || !canUseSerial())?NetDebug.print(x):Serial.print(x) + #define USER_PRINTLN(x) (netDebugEnabled || !canUseSerial())?NetDebug.println(x):Serial.println(x) + #define USER_PRINTF(x...) (netDebugEnabled || !canUseSerial())?NetDebug.printf(x):Serial.printf(x) + #define USER_FLUSH() (netDebugEnabled || !canUseSerial())?NetDebug.flush():Serial.flush() +#else + #define USER_PRINT(x) {if (canUseSerial()) Serial.print(x);} + #define USER_PRINTLN(x) {if (canUseSerial()) Serial.println(x);} + #define USER_PRINTF(x...) {if (canUseSerial()) Serial.printf(x);} + #define USER_FLUSH() {if (canUseSerial()) Serial.flush();} +#endif +// WLEDMM end + //Hardware SPI Pins #define P_8266_HS_MOSI 13 #define P_8266_HS_CLK 14 @@ -393,7 +415,11 @@ class PolyBus { #if defined(WLEDMM_FASTPATH) && !defined(WLEDMM_SLOWPATH) // WLEDMM only for fastpath builds. // NOTE: "channel" is only used on ESP32 (and its variants) for RMT channel allocation // since 0.15.0-b3 I2S1 is favoured for classic ESP32 and moved to position 0 (channel 0) so we need to subtract 1 for correct RMT allocation + #if defined(WLEDMM_TWOPATH) + if (channel > 1) channel--; // accommodate I2S1 which is used as 2nd bus on classic ESP32 + #else if (channel > 0) channel--; // accommodate I2S1 which is used as 1st bus on classic ESP32 + #endif #endif #endif void* busPtr = nullptr; @@ -430,20 +456,20 @@ class PolyBus { case I_8266_BB_UCS_4: busPtr = new B_8266_BB_UCS_4(len, pins[0]); break; #endif #ifdef ARDUINO_ARCH_ESP32 - case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); break; + case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); USER_PRINTF("(RMT #%u) ", channel); break; #ifndef WLED_NO_I2S0_PIXELBUS - case I_32_I0_NEO_3: busPtr = new B_32_I0_NEO_3(len, pins[0]); break; + case I_32_I0_NEO_3: busPtr = new B_32_I0_NEO_3(len, pins[0]); USER_PRINT("(I2S #0) "); break; #endif #ifndef WLED_NO_I2S1_PIXELBUS - case I_32_I1_NEO_3: busPtr = new B_32_I1_NEO_3(len, pins[0]); break; + case I_32_I1_NEO_3: busPtr = new B_32_I1_NEO_3(len, pins[0]); USER_PRINT("(I2S #1) "); break; #endif // case I_32_BB_NEO_3: busPtr = new B_32_BB_NEO_3(len, pins[0], (NeoBusChannel)channel); break; - case I_32_RN_NEO_4: busPtr = new B_32_RN_NEO_4(len, pins[0], (NeoBusChannel)channel); break; + case I_32_RN_NEO_4: busPtr = new B_32_RN_NEO_4(len, pins[0], (NeoBusChannel)channel); USER_PRINTF("(RGBW RMT #%u) ", channel); break; #ifndef WLED_NO_I2S0_PIXELBUS - case I_32_I0_NEO_4: busPtr = new B_32_I0_NEO_4(len, pins[0]); break; + case I_32_I0_NEO_4: busPtr = new B_32_I0_NEO_4(len, pins[0]); USER_PRINT("(RGBW I2S #0) "); break; #endif #ifndef WLED_NO_I2S1_PIXELBUS - case I_32_I1_NEO_4: busPtr = new B_32_I1_NEO_4(len, pins[0]); break; + case I_32_I1_NEO_4: busPtr = new B_32_I1_NEO_4(len, pins[0]); USER_PRINT("(RGBW I2S #1) "); break; #endif // case I_32_BB_NEO_4: busPtr = new B_32_BB_NEO_4(len, pins[0], (NeoBusChannel)channel); break; case I_32_RN_400_3: busPtr = new B_32_RN_400_3(len, pins[0], (NeoBusChannel)channel); break; @@ -1199,8 +1225,11 @@ class PolyBus { if (num > 7) return I_NONE; #else if (num > 8) return I_NONE; - //if (num == 1) offset = 2; // use I2S#1 as 2nd bus - seems to be a good compromise for performance, and reduces flickering for some users - if (num == 0) offset = 2; // un-comment to use I2S#1 as 1st bus - sometimes helps, if you experience flickering during Wifi or filesystem activity. + #if defined(WLEDMM_TWOPATH) + if (num == 1) offset = 2; // use I2S#1 as 2nd bus - seems to be a good compromise for performance, and reduces flickering for some users + #else + if (num == 0) offset = 2; // use I2S#1 as 1st bus - sometimes helps, if you experience flickering during Wifi or filesystem activity. + #endif #endif #endif #endif diff --git a/wled00/wled.h b/wled00/wled.h index b69956bd..3153e913 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2405150 +#define VERSION 2405170 // 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_ From a0514bb7ee66a41d00bd94ff908ac8a1b2ed553f Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Fri, 17 May 2024 21:40:59 +0200 Subject: [PATCH 26/49] fix compiler warning --- wled00/bus_manager.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 93ff1564..fff5ecb3 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -41,10 +41,10 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte #endif #else // un-define USER_PRINT macros from bus_wrapper.h - #undef DEBUG_PRINT - #undef DEBUG_PRINTF - #undef DEBUG_PRINTLN - #undef DEBUG_FLUSH + #undef USER_PRINT + #undef USER_PRINTF + #undef USER_PRINTLN + #undef USER_FLUSH // WLEDMM use wled.h #include "wled.h" #endif From 31cca47024059b806973fad3bad0052b05ea4805 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Fri, 17 May 2024 21:45:50 +0200 Subject: [PATCH 27/49] disable TWOPATH as default flag needs more testing - may bring flickering back .... --- platformio.ini | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index c2e8f729..441c6315 100644 --- a/platformio.ini +++ b/platformio.ini @@ -305,7 +305,7 @@ build_flags = -g -D CONFIG_ASYNC_TCP_TASK_STACK_SIZE=9472 ;; WLEDMM increase stack by 1.25Kb, as audioreactive needs bigger SETTINGS_STACK_BUF_SIZE #use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x -D LOROL_LITTLEFS - -D WLEDMM_TWOPATH ;; use I2S1 as the second bus --> ~15% faster on "V3" builds + ; -D WLEDMM_TWOPATH ;; use I2S1 as the second bus --> ~15% faster on "V3" builds - may flicker a bit more ; -D WLEDMM_SLOWPATH ;; don't use I2S for LED bus ; -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3 @@ -382,6 +382,8 @@ build_flags = -g -D CONFIG_ASYNC_TCP_USE_WDT=0 -D CONFIG_ASYNC_TCP_TASK_STACK_SIZE=9472 ;; WLEDMM increase stack by 1.25Kb, as audioreactive needs bigger SETTINGS_STACK_BUF_SIZE -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3 + ; -D WLEDMM_TWOPATH ;; use I2S1 as the second bus --> slightly faster on some setups + ; -D WLEDMM_SLOWPATH ;; don't use I2S for LED bus default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv lib_deps = https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 From 1b9b2dcffed3e719a485426ea64dc4339d7ccf6d Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 18 May 2024 21:49:09 +0200 Subject: [PATCH 28/49] minimal Auto segment creation bugfix based on https://github.com/Aircoookie/WLED/commit/13bfda56efa8af56e92456480c35dffb2785a038 --- wled00/FX_fcn.cpp | 4 +++- wled00/json.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index cd5b69cf..34e59e40 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -2205,7 +2205,7 @@ void WS2812FX::makeAutoSegments(bool forceReset) { segStops[s] = segStarts[s] + b->getLength(); #ifndef WLED_DISABLE_2D - if (isMatrix && segStops[s] < Segment::maxWidth*Segment::maxHeight) continue; // ignore buses comprising matrix + if (isMatrix && segStops[s] <= Segment::maxWidth*Segment::maxHeight) continue; // ignore buses comprising matrix if (isMatrix && segStarts[s] < Segment::maxWidth*Segment::maxHeight) segStarts[s] = Segment::maxWidth*Segment::maxHeight; #endif @@ -2278,6 +2278,8 @@ void WS2812FX::fixInvalidSegments() { if (_segments[i].stop > _length) _segments[i].stop = _length; } } + // if any segments were deleted free memory + purgeSegments(); // this is always called as the last step after finalizeInit(), update covered bus types for (segment &seg : _segments) seg.refreshLightCapabilities(); diff --git a/wled00/json.cpp b/wled00/json.cpp index c03a61f0..ce7e9505 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -516,7 +516,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId) //bool didSet = false; for (size_t s = 0; s < strip.getSegmentsNum(); s++) { Segment &sg = strip.getSegment(s); - if (sg.isSelected()) { + if (sg.isActive() && sg.isSelected()) { inDeepCall = true; // WLEDMM remember that we are going into recursion deserializeSegment(segVar, s, presetId); if (iAmGroot) inDeepCall = false; // WLEDMM toplevel -> reset recursion flag From a25e608e2b37dc031bf2d22329f644a33ca2933d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Mon, 20 May 2024 12:24:26 +0200 Subject: [PATCH 29/49] Fix for #3991 --- wled00/json.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/wled00/json.cpp b/wled00/json.cpp index ce7e9505..eeb61a42 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -731,11 +731,7 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme nl["dur"] = nightlightDelayMins; nl["mode"] = nightlightMode; nl[F("tbri")] = nightlightTargetBri; - if (nightlightActive) { - nl[F("rem")] = (nightlightDelayMs - (millis() - nightlightStartTime)) / 1000; // seconds remaining - } else { - nl[F("rem")] = -1; - } + nl[F("rem")] = nightlightActive ? (int)(nightlightDelayMs - (millis() - nightlightStartTime)) / 1000 : -1; // seconds remaining JsonObject udpn = root.createNestedObject("udpn"); udpn["send"] = notifyDirect; From eb3200bb291de1ee213647b6cd56b3153347bd99 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Wed, 22 May 2024 13:03:30 +0200 Subject: [PATCH 30/49] align JSON release info with upstream 0_15 (update page, JSON "info") align with upstream /json/info * info.release * info.arch * info.clock * info.flash --- wled00/json.cpp | 12 +++++++++--- wled00/wled.h | 27 ++++++++++++++++----------- wled00/xml.cpp | 2 +- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/wled00/json.cpp b/wled00/json.cpp index eeb61a42..5456b455 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -897,9 +897,10 @@ String restartCode2Info(esp_reset_reason_t reason) { void serializeInfo(JsonObject root) { root[F("ver")] = versionString; - root[F("rel")] = releaseString; //WLEDMM to add bin name root[F("vid")] = VERSION; - //root[F("cn")] = WLED_CODENAME; + //root[F("cn")] = F(WLED_CODENAME); //WLEDMM removed + root[F("release")] = FPSTR(releaseString); + root[F("rel")] = FPSTR(releaseString); //WLEDMM to add bin name JsonObject leds = root.createNestedObject("leds"); leds[F("count")] = strip.getLengthTotal(); @@ -1018,12 +1019,15 @@ void serializeInfo(JsonObject root) wifi_info[F("txPower")] = (int) WiFi.getTxPower(); wifi_info[F("sleep")] = (bool) WiFi.getSleep(); #endif - #if !defined(CONFIG_IDF_TARGET_ESP32C2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3) + //#if !defined(CONFIG_IDF_TARGET_ESP32C2) && !defined(CONFIG_IDF_TARGET_ESP32C6) && !defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3) + #if CONFIG_IDF_TARGET_ESP32 root[F("arch")] = "esp32"; #else root[F("arch")] = ESP.getChipModel(); #endif root[F("core")] = ESP.getSdkVersion(); + root[F("clock")] = ESP.getCpuFreqMHz(); + root[F("flash")] = (ESP.getFlashChipSize()/1024)/1024; //root[F("maxalloc")] = ESP.getMaxAllocHeap(); #ifdef WLED_DEBUG root[F("resetReason0")] = (int)rtc_get_reset_reason(0); @@ -1043,6 +1047,8 @@ void serializeInfo(JsonObject root) #else root[F("arch")] = "esp8266"; root[F("core")] = ESP.getCoreVersion(); + root[F("clock")] = ESP.getCpuFreqMHz(); + root[F("flash")] = (ESP.getFlashChipSize()/1024)/1024; //root[F("maxalloc")] = ESP.getMaxFreeBlockSize(); #ifdef WLED_DEBUG root[F("resetReason")] = (int)ESP.getResetInfoPtr()->reason; diff --git a/wled00/wled.h b/wled00/wled.h index 3153e913..711b3d35 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2405170 +#define VERSION 2405220 // 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_ @@ -271,16 +271,17 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument; // int arr[]{0,1,2} becomes WLED_GLOBAL int arr[] _INIT_N(({0,1,2})); #ifndef WLED_DEFINE_GLOBAL_VARS -# define WLED_GLOBAL extern -# define _INIT(x) -# define _INIT_N(x) + #define WLED_GLOBAL extern + #define _INIT(x) + #define _INIT_N(x) + #define _INIT_PROGMEM(x) #else -# define WLED_GLOBAL -# define _INIT(x) = x - -//needed to ignore commas in array definitions -#define UNPACK( ... ) __VA_ARGS__ -# define _INIT_N(x) UNPACK x + #define WLED_GLOBAL + #define _INIT(x) = x + //needed to ignore commas in array definitions + #define UNPACK( ... ) __VA_ARGS__ + #define _INIT_N(x) UNPACK x + #define _INIT_PROGMEM(x) PROGMEM = x #endif #define STRINGIFY(X) #X @@ -290,9 +291,13 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument; #define WLED_VERSION "dev" #endif +#ifndef WLED_RELEASE_NAME + #define WLED_RELEASE_NAME mdev_release +#endif + // Global Variable definitions WLED_GLOBAL char versionString[] _INIT(TOSTRING(WLED_VERSION)); -WLED_GLOBAL char releaseString[] _INIT(TOSTRING(WLED_RELEASE_NAME)); //WLEDMM: to show on update page +WLED_GLOBAL char releaseString[] _INIT_PROGMEM(TOSTRING(WLED_RELEASE_NAME)); //WLEDMM: to show on update page // somehow this will not work if using "const char releaseString[] #define WLED_CODENAME "Hoshi" // AP and OTA default passwords (for maximum security change them!) diff --git a/wled00/xml.cpp b/wled00/xml.cpp index dbb9ce75..0c6a1566 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -846,7 +846,7 @@ void getSettingsJS(AsyncWebServerRequest* request, byte subPage, char* dest) //W olen -= 2; //delete "; oappend(versionString); oappend(SET_F(" ")); - oappend(releaseString); + oappend((char*)FPSTR(releaseString)); oappend(SET_F(".bin
(")); #if defined(CONFIG_IDF_TARGET_ESP32C3) oappend(SET_F("ESP32-C3")); From 42e7805e5f71733cef86ff33e851078885312ed8 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Thu, 23 May 2024 20:03:53 +0200 Subject: [PATCH 31/49] dump important build_flags after compile Usermods and Features: USERMOD_AUDIOREACTIVE UM_AUDIOREACTIVE_USE_NEW_FFT USERMOD_AUTO_PLAYLIST WLED_DISABLE_LOXONE WLED_DISABLE_ALEXA WLED_DISABLE_HUESYNC WLED_DISABLE_MQTT WLED_DISABLE_INFRARED WLED_DISABLE_ADALIGHT WLED_DISABLE_ESPNOW WLED_ENABLE_DMX_INPUT WLED_USE_PSRAM_JSON WLEDMM Features: WLEDMM_FASTPATH note1: I have no idea about specialties of python - it could surely be done in a "more elegant" way. note2: the lists sometimes contain duplicates. --- pio-scripts/output_bins.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/pio-scripts/output_bins.py b/pio-scripts/output_bins.py index 4df40ed4..caf40bf9 100644 --- a/pio-scripts/output_bins.py +++ b/pio-scripts/output_bins.py @@ -22,6 +22,41 @@ def _create_dirs(dirs=["firmware", "map"]): if not os.path.isdir("{}{}".format(OUTPUT_DIR, d)): os.mkdir("{}{}".format(OUTPUT_DIR, d)) + +# WLEDMM : dump out buildflags : usermods, disable, enable, use_.. +def wledmm_print_build_info(env): + first = True + + for item in env["CPPDEFINES"]: + if 'USERMOD_' in item or 'UM_' in item: + if first: print("\nUsermods and Features:") + print(" " + item, end='') + first = False + if not first: print("") + + for item in env["CPPDEFINES"]: + if 'WLED_DISABLE' in item or 'NET_DEBUG_' in item or 'WIFI_FIX' in item: + if first: print("\nUsermods and Features:") + print(" " + item, end='') + first = False + if not first: print("") + + for item in env["CPPDEFINES"]: + if 'WLED_' in item and not 'WLED_USE_MY_CONFIG' in item and not 'WLED_RELEASE_NAME' in item and not 'WLED_VESION' in item and not 'WLED_WATCHDOG_TIMEOUT' in item and not 'WLED_DISABLE' in item and not 'ARDUINO_PARTITION' in item: + if first: print("\nUsermods and Features:") + print(" " + item, end='') + first = False + if not first: print("") + + first = True + for item in env["CPPDEFINES"]: + if 'WLEDMM_' in item or 'O2' in item or 'O3' in item or 'fast_' in item: + if first: print("\nWLEDMM Features:") + print(" " + item, end='') + first = False + if not first: print("\n") + + def bin_rename_copy(source, target, env): _create_dirs() variant = env["PIOENV"] @@ -56,6 +91,8 @@ def bin_rename_copy(source, target, env): print(f"Found linker mapfile {source_map}") shutil.copy(source_map, map_file) + wledmm_print_build_info(env) + def bin_gzip(source, target, env): _create_dirs() variant = env["PIOENV"] From d9247fc1ad0dc9cc92ba5e34d1592a630c6fdf0d Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Thu, 23 May 2024 23:42:06 +0200 Subject: [PATCH 32/49] dump build flags with value ... I'm still trying to write C code in python ;-) --- pio-scripts/output_bins.py | 71 +++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 17 deletions(-) diff --git a/pio-scripts/output_bins.py b/pio-scripts/output_bins.py index caf40bf9..dc9400aa 100644 --- a/pio-scripts/output_bins.py +++ b/pio-scripts/output_bins.py @@ -23,38 +23,75 @@ def _create_dirs(dirs=["firmware", "map"]): os.mkdir("{}{}".format(OUTPUT_DIR, d)) +# trick for py2/3 compatibility +if 'basestring' not in globals(): + basestring = str + +# WLEDMM : custom print function +def print_my_item(items): + print(" ", end='') + if isinstance(items, basestring): + # print a single string + print(items, end='') + else: + # print a list + first = True + for item in items: + if not first: print("=", end='') + print(item, end='') + first = False + # WLEDMM : dump out buildflags : usermods, disable, enable, use_.. def wledmm_print_build_info(env): + all_flags = env["CPPDEFINES"] first = True - for item in env["CPPDEFINES"]: + found = False + for item in all_flags: + if 'WLED_RELEASE_NAME' in item[0] or 'WLED_VERSION' in item[0] or 'ARDUINO_USB_CDC_ON_BOOT' in item[0]: + if first: print("\nUsermods and Features:") + print_my_item(item) + first = False + found = True + if found: print("") + + found = False + for item in all_flags: if 'USERMOD_' in item or 'UM_' in item: if first: print("\nUsermods and Features:") - print(" " + item, end='') + print_my_item(item) first = False - if not first: print("") + found = True + if found: print("") - for item in env["CPPDEFINES"]: - if 'WLED_DISABLE' in item or 'NET_DEBUG_' in item or 'WIFI_FIX' in item: + found = False + for item in all_flags: + if 'WLED_DISABLE' in item or 'WIFI_FIX' in item: if first: print("\nUsermods and Features:") - print(" " + item, end='') + print_my_item(item) first = False - if not first: print("") + found = True + if found: print("") - for item in env["CPPDEFINES"]: - if 'WLED_' in item and not 'WLED_USE_MY_CONFIG' in item and not 'WLED_RELEASE_NAME' in item and not 'WLED_VESION' in item and not 'WLED_WATCHDOG_TIMEOUT' in item and not 'WLED_DISABLE' in item and not 'ARDUINO_PARTITION' in item: - if first: print("\nUsermods and Features:") - print(" " + item, end='') - first = False - if not first: print("") + found = False + for item in all_flags: + if 'WLED_' in item or 'WLED_' in item[0] or 'MAX_LED' in item[0]: + if not 'WLED_RELEASE_NAME' in item[0] and not 'WLED_VERSION' in item[0] and not 'WLED_WATCHDOG_TIMEOUT' in item[0] and not 'WLED_DISABLE' in item and not 'WLED_USE_MY_CONFIG' in item and not 'ARDUINO_PARTITION' in item: + if first: print("\nUsermods and Features:") + print_my_item(item) + first = False + found = True + if found: print("") first = True - for item in env["CPPDEFINES"]: - if 'WLEDMM_' in item or 'O2' in item or 'O3' in item or 'fast_' in item: + found = False + for item in all_flags: + if 'WLEDMM_' in item[0] or 'WLEDMM_' in item or 'TROYHACKS' in item: if first: print("\nWLEDMM Features:") - print(" " + item, end='') + print_my_item(item) first = False - if not first: print("\n") + found = True + if found: print("\n") def bin_rename_copy(source, target, env): From 016499fccb47520b31bba6fe8e01504c251f7d46 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 24 May 2024 01:38:04 +0200 Subject: [PATCH 33/49] wledmm_print_all_defines (optional) for pio.ini debugging --- pio-scripts/output_bins.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/pio-scripts/output_bins.py b/pio-scripts/output_bins.py index dc9400aa..6889ea1c 100644 --- a/pio-scripts/output_bins.py +++ b/pio-scripts/output_bins.py @@ -28,8 +28,9 @@ if 'basestring' not in globals(): basestring = str # WLEDMM : custom print function -def print_my_item(items): - print(" ", end='') +def print_my_item(items, flag = False): + if flag: print(" -D", end='') + else: print(" ", end='') if isinstance(items, basestring): # print a single string print(items, end='') @@ -93,6 +94,15 @@ def wledmm_print_build_info(env): found = True if found: print("\n") +def wledmm_print_all_defines(env): + all_flags = env["CPPDEFINES"] + found = False + for item in all_flags: + if not found: print("\nBuild Flags:") + print_my_item(item, True) + found = True + if found: print("\n") + def bin_rename_copy(source, target, env): _create_dirs() @@ -128,6 +138,7 @@ def bin_rename_copy(source, target, env): print(f"Found linker mapfile {source_map}") shutil.copy(source_map, map_file) + # wledmm_print_all_defines(env) wledmm_print_build_info(env) def bin_gzip(source, target, env): From 14efcceceab2cd79bf3ab6f86e44f9519e3219c0 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Fri, 24 May 2024 10:46:58 +0200 Subject: [PATCH 34/49] minor --- wled00/FX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 5d53b53d..b8622526 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5175,7 +5175,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - const size_t dataSize = (SEGMENT.length() / 8) + ((SEGMENT.length() % 8 != 0) ? 1 : 0); // add one byte when extra bits needed (length not a multiple of 8) + const size_t dataSize = (SEGMENT.length() / 8) + (((SEGMENT.length() % 8) != 0) ? 1 : 0); // add one byte when extra bits needed (length not a multiple of 8) const size_t totalSize = dataSize*2 + sizeof(gameOfLife); if (!SEGENV.allocateData(totalSize)) return mode_static(); //allocation failed From a50e77db263ed1e6bd3588b12d34fb95612c59f5 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Fri, 24 May 2024 11:12:50 +0200 Subject: [PATCH 35/49] 2D Crazy Bees bugfixes * solve int8 overflow on "error2" (large fixtures) - based on https://github.com/cschill2020/WLED/commit/2c87f7e752527418eb4330a906a954e996568000 * use fast int types where possible --- wled00/FX.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index b8622526..f17e2556 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -6030,19 +6030,20 @@ static const char _data_FX_MODE_2DSPACESHIPS[] PROGMEM = "Spaceships@!,Blur;;!;2 // 2D Crazy Bees // ///////////////////////// //// Crazy bees by stepko (c)12.02.21 [https://editor.soulmatelights.com/gallery/651-crazy-bees], adapted by Blaz Kristan (AKA blazoncek) -#define MAX_BEES 5 +constexpr uint_fast16_t MAX_BEES = 5; uint16_t mode_2Dcrazybees(void) { if (!strip.isMatrix) return mode_static(); // not a 2D set-up - const uint16_t cols = SEGMENT.virtualWidth(); - const uint16_t rows = SEGMENT.virtualHeight(); + const uint_fast16_t cols = SEGMENT.virtualWidth(); + const uint_fast16_t rows = SEGMENT.virtualHeight(); - byte n = MIN(MAX_BEES, (rows * cols) / 256 + 1); + const byte n = min(MAX_BEES, (rows * cols) / 256 + 1); typedef struct Bee { uint8_t posX, posY, aimX, aimY, hue; - int8_t deltaX, deltaY, signX, signY, error; - void aimed(uint16_t w, uint16_t h) { + int8_t signX, signY; + int16_t deltaX, deltaY, error; + void aimed(uint_fast16_t w, uint_fast16_t h) { if (!true) //WLEDMM SuperSync random16_set_seed(strip.now); aimX = random8(0, w); @@ -6083,7 +6084,7 @@ uint16_t mode_2Dcrazybees(void) { SEGMENT.addPixelColorXY(bee[i].aimX, bee[i].aimY - 1, CHSV(bee[i].hue, 255, 255)); if (bee[i].posX != bee[i].aimX || bee[i].posY != bee[i].aimY) { SEGMENT.setPixelColorXY(bee[i].posX, bee[i].posY, CRGB(CHSV(bee[i].hue, 60, 255))); - int8_t error2 = bee[i].error * 2; + int_fast16_t error2 = bee[i].error * 2; if (error2 > -bee[i].deltaY) { bee[i].error -= bee[i].deltaY; bee[i].posX += bee[i].signX; From 21e6b4d62fa366f368ae1d4016a193290cadf8c8 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Fri, 24 May 2024 11:26:28 +0200 Subject: [PATCH 36/49] optimization replacing MIN / MAX (arduino macros) by more efficient min/max (libc templates) --- wled00/FX.cpp | 12 ++++++------ wled00/wled.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index f17e2556..dce51a4a 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -1333,12 +1333,12 @@ uint16_t mode_fire_flicker(void) { byte r = (SEGCOLOR(0) >> 16); byte g = (SEGCOLOR(0) >> 8); byte b = (SEGCOLOR(0) ); - byte lum = (SEGMENT.palette == 0) ? MAX(w, MAX(r, MAX(g, b))) : 255; + byte lum = (SEGMENT.palette == 0) ? max(w, max(r, max(g, b))) : 255; lum /= (((256-SEGMENT.intensity)/16)+1); for (int i = 0; i < SEGLEN; i++) { byte flicker = random8(lum); if (SEGMENT.palette == 0) { - SEGMENT.setPixelColor(i, MAX(r - flicker, 0), MAX(g - flicker, 0), MAX(b - flicker, 0), MAX(w - flicker, 0)); + SEGMENT.setPixelColor(i, max(r - flicker, 0), max(g - flicker, 0), max(b - flicker, 0), max(w - flicker, 0)); } else { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, 255 - flicker)); } @@ -1369,7 +1369,7 @@ uint16_t gradient_base(bool loading) { { val = abs(((i>pp) ? p2:pp) -i); } else { - val = MIN(abs(pp-i),MIN(abs(p1-i),abs(p2-i))); + val = min(abs(pp-i), min(abs(p1-i), abs(p2-i))); } val = (brd > val) ? val/brd * 255 : 255; SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(0), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), val)); @@ -2129,7 +2129,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],240), 255, NOBLEND)); + SEGMENT.setPixelColor(indexToVStrip(j, stripNr), ColorFromPalette(SEGPALETTE, min(heat[j],byte(240)), 255, NOBLEND)); } } }; @@ -2971,7 +2971,7 @@ uint16_t mode_bouncing_balls(void) { uint32_t color = SEGCOLOR(0); if (SEGMENT.palette) { - color = SEGMENT.color_wheel(i*(256/MAX(numBalls, 8))); + color = SEGMENT.color_wheel(i*(256/max(numBalls, uint16_t(8)))); } else if (hasCol2) { color = SEGCOLOR(i % NUM_COLORS); } @@ -6245,7 +6245,7 @@ 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] >= min(cols/4.f,2.f)) { blob->grow[i] = false; } } else { diff --git a/wled00/wled.h b/wled00/wled.h index 711b3d35..2e9f2027 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2405220 +#define VERSION 2405240 // 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_ From 738078c167bdd379dfd6330831fd9232c3fe27d2 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Fri, 24 May 2024 17:23:12 +0200 Subject: [PATCH 37/49] too much information (pio) --- pio-scripts/output_bins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pio-scripts/output_bins.py b/pio-scripts/output_bins.py index 6889ea1c..3a55ced8 100644 --- a/pio-scripts/output_bins.py +++ b/pio-scripts/output_bins.py @@ -139,7 +139,7 @@ def bin_rename_copy(source, target, env): shutil.copy(source_map, map_file) # wledmm_print_all_defines(env) - wledmm_print_build_info(env) + # wledmm_print_build_info(env) def bin_gzip(source, target, env): _create_dirs() From 6f6ba307c85e33e8481f3262913d942c9c3c288f Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Fri, 24 May 2024 17:28:19 +0200 Subject: [PATCH 38/49] drip effect bugfixing * bring back lost parameter "Fall ratio" (MM specific) * stabilize the math, avoid rounding errors and unsigned overflows * speed limit for long strips added * gravity reduced slightly --- wled00/FX.cpp | 19 +++++++++++-------- wled00/wled.h | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index dce51a4a..77efb26f 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -3696,6 +3696,7 @@ uint16_t mode_drip(void) if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed Spark* drops = reinterpret_cast(SEGENV.data); + if (SEGENV.call == 0) SEGMENT.fill(BLACK); // WLEDMM clear LEDs at startup if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); struct virtualStrip { @@ -3703,9 +3704,9 @@ uint16_t mode_drip(void) uint8_t numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3 - float gravity = -0.0005 - (SEGMENT.speed/25000.0); //increased gravity (50000 to 25000) - gravity *= max(1, SEGLEN-1); - int sourcedrop = 12; + float gravity = -0.0005f - (float(SEGMENT.speed)/35000.0f); //increased gravity (50000 to 35000) + gravity *= min(max(1, SEGLEN-1), 255); //WLEDMM speed limit 255 + const int sourcedrop = 12; for (int j=0;j 1% ... 20% probalibity drops[j].colIndex=2; //fall drops[j].col=255; } } if (drops[j].colIndex > 1) { // falling - if (drops[j].pos > 0) { // fall until end of segment + if (drops[j].pos > 0.01f) { // fall until end of segment drops[j].pos += drops[j].vel; if (drops[j].pos < 0) drops[j].pos = 0; drops[j].vel += gravity; // gravity is negative for (int i=1;i<7-drops[j].colIndex;i++) { // some minor math so we don't expand bouncing droplets - uint16_t pos = constrain(uint16_t(drops[j].pos) +i, 0, SEGLEN-1); //this is BAD, returns a pos >= SEGLEN occasionally + int intPos = roundf(drops[j].pos) +i; // WLEDMM round it first + uint16_t pos = constrain(intPos, 0, SEGLEN-1); //this is BAD, returns a pos >= SEGLEN occasionally // WLEDMM bad cast to uint16_t removed SEGMENT.setPixelColor(indexToVStrip(pos, stripNr), color_blend(BLACK,dropColor,drops[j].col/i)); //spread pixel with fade while falling } if (drops[j].colIndex > 2) { // during bounce, some water is on the floor - SEGMENT.setPixelColor(indexToVStrip(0, stripNr), color_blend(dropColor,BLACK,drops[j].col)); + SEGMENT.setPixelColor(indexToVStrip(0, stripNr), color_blend(dropColor,BLACK, (2 * drops[j].col)/3)); // WLEDMM reduced brightness } } else { // we hit bottom if (drops[j].colIndex > 2) { // already hit once, so back to forming @@ -3768,7 +3771,7 @@ uint16_t mode_drip(void) return FRAMETIME; } -static const char _data_FX_MODE_DRIP[] PROGMEM = "Drip@Gravity,# of drips,,,,,Overlay;!,!;!;1.5d;m12=1"; //bar WLEDMM 1.5d +static const char _data_FX_MODE_DRIP[] PROGMEM = "Drip@Gravity,# of drips,Fall ratio,,,,Overlay;!,!;!;1.5d;c1=127,m12=1"; //bar WLEDMM 1.5d /* diff --git a/wled00/wled.h b/wled00/wled.h index 2e9f2027..96530ac2 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2405240 +#define VERSION 2405241 // 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_ From 7d364bcc040afe39d822efe6d73f05691673c537 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Fri, 24 May 2024 18:03:26 +0200 Subject: [PATCH 39/49] Update platformio.ini - matrixportal flash size use correct partitions file for 8MB --- platformio.ini | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/platformio.ini b/platformio.ini index 441c6315..7c5b02a6 100644 --- a/platformio.ini +++ b/platformio.ini @@ -2476,11 +2476,16 @@ lib_ignore = [env:adafruit_matrixportal_esp32s3] +; ESP32-S3 processor, 8 MB flash, 2 MB of PSRAM, dedicated driver pins for HUB75 extends = esp32_4MB_V4_M_base platform = ${esp32.platformV4_xp} ;; 6.5.0 = first platform release supporting matrixportal platform_packages = ${esp32.platformV4_packages_xp} ;; arduino-esp32 2.0.14 needed - previous versions were missing files for matrixportal board = adafruit_matrixportal_esp32s3 +board_build.partitions = ${esp32.large_partitions} +board_build.f_flash = 80000000L +board_build.flash_mode = qio + build_unflags = ${env:esp32S3_8MB_M.build_unflags} ;; use the same as "normal" S3 buildenv build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-indentation -Wno-format-truncation ${common_mm.build_flags_S} From 827cdc2f5b57b9fa7c35dca404dace2d06317b3a Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Wed, 17 Apr 2024 19:57:14 +0100 Subject: [PATCH 40/49] AutoPlaylist - prevent swapping playlist after silence ended when Suspended --- usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index 88d62654..adb5b37e 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -322,6 +322,8 @@ class AutoPlaylistUsermod : public Usermod { if (bri == 0) return; + if(!functionality_enabled) return; + um_data_t *um_data; if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { From 6a93f46881ba7c25603f9bd5f1e200b3cb930608 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sun, 26 May 2024 14:42:16 +0100 Subject: [PATCH 41/49] Disable serial console on default adafruit_matrixportal_esp32s3 builds so they boot without usb --- platformio.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 7c5b02a6..0e303f8d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -2490,7 +2490,8 @@ build_unflags = ${env:esp32S3_8MB_M.build_unflags} ;; use the same as "normal" build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-indentation -Wno-format-truncation ${common_mm.build_flags_S} -D WLED_RELEASE_NAME=matrixportal_esp32s3 - -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode + ; Serial debug enabled -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode + -D ARDUINO_USB_CDC_ON_BOOT=0 -D WLED_DISABLE_ADALIGHT ;; disables serial protocols - recommended for Hardware-CDC USB (Serial RX will receive junk commands when RX pin is unconnected, unless its pulled down by resistor) ${common_mm.animartrix_build_flags} ${common_mm.build_disable_sync_interfaces} From 15199dc711616b8190219afafc07f5ae529dbe15 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Wed, 29 May 2024 15:43:24 +0200 Subject: [PATCH 42/49] protect unconnected USB-CDC from being used found a few more places where Serial was used without first checking if its connected. Arduino docs: `if (Serial)` indicates whether or not the USB CDC serial connection is open. For all non-USB CDC ports, this will always return true --- tools/ESP32-Chip_info.hpp | 2 ++ wled00/wled_serial.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tools/ESP32-Chip_info.hpp b/tools/ESP32-Chip_info.hpp index 89316598..417ee449 100644 --- a/tools/ESP32-Chip_info.hpp +++ b/tools/ESP32-Chip_info.hpp @@ -543,6 +543,8 @@ void show_psram_info_part2(void) void showRealSpeed() { //Serial.begin(115200); + if (!Serial) return; // Avoid writing to unconnected USB-CDC + Serial.flush(); Serial.println(F("\n")); for(int aa=0; aa<65; aa++) Serial.print("="); Serial.println(); diff --git a/wled00/wled_serial.cpp b/wled00/wled_serial.cpp index 221f21ad..9361891b 100644 --- a/wled00/wled_serial.cpp +++ b/wled00/wled_serial.cpp @@ -42,6 +42,7 @@ void updateBaudRate(uint32_t rate){ // RGB LED data return as JSON array. Slow, but easy to use on the other end. void sendJSON(){ if (!pinManager.isPinAllocated(hardwareTX) || pinManager.getPinOwner(hardwareTX) == PinOwner::DebugOut) { + if (!Serial) return; // WLEDMM avoid writing to unconnected USB-CDC uint16_t used = strip.getLengthTotal(); Serial.write('['); for (uint16_t i=0; i Date: Wed, 29 May 2024 15:51:44 +0200 Subject: [PATCH 43/49] another unprotected Serial real --- wled00/improv.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/wled00/improv.cpp b/wled00/improv.cpp index 646201d3..eef8ad82 100644 --- a/wled00/improv.cpp +++ b/wled00/improv.cpp @@ -50,6 +50,7 @@ void handleImprovPacket() { uint8_t rpcCommandType = 0; char rpcData[128]; rpcData[0] = 0; + if (!Serial) return; // WLEDMM avoid reading from unconnected USB-CDC while (!timeout) { if (Serial.available() < 1) { From d0941fd8767091aacb16649fd099d4b2846971ce Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Fri, 31 May 2024 15:50:10 +0200 Subject: [PATCH 44/49] Add UI error description. --- wled00/data/index.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/wled00/data/index.js b/wled00/data/index.js index 4744fb38..7a9b5d57 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -1951,6 +1951,18 @@ function readState(s,command=false) if (s.error && s.error != 0) { var errstr = ""; switch (s.error) { + case 1: + errstr = "Denied!"; + break; + case 3: + errstr = "Buffer locked!"; + break; + case 8: + errstr = "Effect RAM depleted!"; + break; + case 9: + errstr = "JSON parsing error!"; + break; case 10: errstr = "Could not mount filesystem!"; break; From c532b5d31ae96dd2a99f2e583ca4a0346565d478 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Fri, 31 May 2024 18:18:58 +0200 Subject: [PATCH 45/49] Add UI error 14 description --- wled00/data/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wled00/data/index.js b/wled00/data/index.js index 7a9b5d57..1fcbfcaf 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -1975,6 +1975,9 @@ function readState(s,command=false) case 13: errstr = "Missing ir.json."; break; + case 14: + errstr = "Missing remote.json."; + break; case 19: errstr = "A filesystem error has occured."; break; From c617584f0bdc3c29a5d259ba58b97fad7c08445d Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Wed, 5 Jun 2024 13:28:36 +0200 Subject: [PATCH 46/49] bugfix for -S3: allow to use pin 48 for LEDs needed on boards like "S3 supermini" with ws2812 on gpio48 --- wled00/xml.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wled00/xml.cpp b/wled00/xml.cpp index 0c6a1566..72fdf219 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -269,9 +269,9 @@ void appendGPIOinfo() { // add info about max. # of pins oappend(SET_F("d.max_gpio=")); - #if defined(ESP32) + #if defined(ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S3) oappendi(NUM_DIGITAL_PINS - 1); - #else //8266 + #else //8266 (max=17), or esp32-S3 (max=48) oappendi(NUM_DIGITAL_PINS); //WLEDMM include pin 17 for Analog #endif oappend(SET_F(";")); From b79a0b4fd75d70bf67a2e8c210a1f9cf5a276cbc Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Wed, 5 Jun 2024 17:29:57 +0200 Subject: [PATCH 47/49] use NPB 2.7.9 on -S2 -> reduces led flickering --- platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index 0e303f8d..290f2ef3 100644 --- a/platformio.ini +++ b/platformio.ini @@ -415,8 +415,8 @@ build_flags = -g lib_deps = https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 - makuna/NeoPixelBus @ 2.7.5 - ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental + ;; makuna/NeoPixelBus @ 2.7.5 ;; standard + makuna/NeoPixelBus @ 2.7.9 ;; experimental - reduces LED glitches on -S2 ${env.lib_deps} [esp32c3] From 578c6bb9d899dbf21bcd8e0e0207d42ed1a52891 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Wed, 5 Jun 2024 23:05:34 +0200 Subject: [PATCH 48/49] make sure that HUB75 is recognized as "adressable leds" when IS_DIGITAL and IS_PWM are false, palettes and color wheel are not shown in UI --- wled00/const.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/const.h b/wled00/const.h index 8f690ac9..7857a1d3 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -253,7 +253,7 @@ #define TYPE_NET_ARTNET_RGB 82 //network ArtNet RGB bus (master broadcast bus, unused) #define TYPE_NET_DDP_RGBW 88 //network DDP RGBW bus (master broadcast bus) -#define IS_DIGITAL(t) ((t) & 0x10) //digital are 16-31 and 48-63 +#define IS_DIGITAL(t) (((t) & 0x10) || ((t)==TYPE_HUB75MATRIX)) //digital are 16-31 and 48-63 // WLEDMM added HUB75 #define IS_PWM(t) ((t) > 40 && (t) < 46) #define NUM_PWM_PINS(t) ((t) - 40) //for analog PWM 41-45 only #define IS_2PIN(t) ((t) > 47) From fc173b3bc00694e59b653ca230133052b5476c05 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Thu, 6 Jun 2024 14:39:56 +0200 Subject: [PATCH 49/49] fix compiler warning for matrixportal buildenv --- platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/platformio.ini b/platformio.ini index 290f2ef3..5ada7d47 100644 --- a/platformio.ini +++ b/platformio.ini @@ -2487,6 +2487,7 @@ board_build.f_flash = 80000000L board_build.flash_mode = qio build_unflags = ${env:esp32S3_8MB_M.build_unflags} ;; use the same as "normal" S3 buildenv + -D ARDUINO_USB_CDC_ON_BOOT=1 ;; fix warning: "ARDUINO_USB_CDC_ON_BOOT" redefined; comment out for Serial debug build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-indentation -Wno-format-truncation ${common_mm.build_flags_S} -D WLED_RELEASE_NAME=matrixportal_esp32s3