From 67d777f474aee1a60416bd5b52e517278d7f8da8 Mon Sep 17 00:00:00 2001 From: Brandon502 <105077712+Brandon502@users.noreply.github.com> Date: Wed, 17 Jul 2024 14:33:23 -0400 Subject: [PATCH 001/232] 2D Effect Snow Fall Added --- wled00/FX.cpp | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++ wled00/FX.h | 3 +- 2 files changed, 117 insertions(+), 1 deletion(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 1ea7f2ac..d0dcc920 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5367,6 +5367,120 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: } // mode_2Dgameoflife() static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!,Color Mutation ☾,Blur ☾,,,All Colors ☾,Overlay BG ☾,Wrap ☾,;!,!;!;2;sx=56,ix=2,c1=128,o1=0,o2=0,o3=1"; +///////////////////////// +// 2D SnowFall // +///////////////////////// + +uint16_t mode_2DSnowFall(void) { // By: Brandon Butler + // Uses Game of Life style bit array to track snow/particles + if (!strip.isMatrix) return mode_static(); // Not a 2D set-up + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + const size_t dataSize = (SEGMENT.length() + 7) / 8; // Round up to nearest byte + + if (!SEGENV.allocateData(dataSize)) return mode_static(); // Allocation failed + byte *grid = reinterpret_cast(SEGENV.data); + + bool overlay = SEGMENT.check2; // Overlay is inverted. Only draws non-snow. Layer 1 controls snow color + uint32_t bgColor = SEGCOLOR(1); + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(bgColor); + SEGENV.aux0 = 0; // Overflow value + memset(grid, 0, dataSize); + } + + // Draw non snow for inverted overlay + if (overlay) { + for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { + if (!getBitValue(grid, y * cols + x)) SEGMENT.setPixelColorXY(x, y, bgColor); + } + } + + uint8_t speed = map(SEGMENT.speed, 0, 255, 0, 60); // Updates per second + if (!speed || strip.now - SEGENV.step < 1000 / speed) return FRAMETIME; // Not enough time passed + + uint8_t blur = map(SEGMENT.custom2, 0, 255, 255, 0); + uint8_t sway = SEGMENT.custom3; + + // Despawn snow + bool overflow = SEGENV.aux0 && SEGMENT.check3; + int despawnChance = SEGMENT.custom1 == 255 ? 256 : map(SEGMENT.custom1, 0, 255, 0, 100); // 255 goes to 256, allows always despawn + int y = rows - 1; + for (int x = 0; x < cols; x++) { + if (overflow || random8() < despawnChance) setBitValue(grid, y * cols + x, 0); + if (overlay || getBitValue(grid, y * cols + x)) continue; // Skip drawing if inverted overlay or snow + SEGMENT.blendPixelColorXY(x, y, bgColor, blur); + } + if (SEGENV.aux0) --SEGENV.aux0; // Decrease overflow + + // Precompute shuffled indices, helps randomize snow movement + int shuffledIndices[cols]; + for (int i = 0; i < cols; i++) shuffledIndices[i] = i; + std::random_shuffle(shuffledIndices, shuffledIndices + cols); + + // Update snow, loop from 2nd bottom row to top with precomputed random order + for (int y = rows - 2; y >= 0; y--) { + for (int i = 0; i < cols; i++) { + int x = shuffledIndices[i]; + + int pos = XY(x, y); + + uint32_t xyColor = SEGMENT.getPixelColorXY(x, y); // Limit getPixelColorXY calls + if (!getBitValue(grid, pos)) { // No snow, fade if needed and skip + if (!overlay && blur) SEGMENT.setPixelColorXY(x, y, color_blend(xyColor, bgColor, blur)); + continue; + } + + int newX = x, newY = y + 1; + int newPos = XY(newX, newY); + // Open Position Booleans + bool down = !getBitValue(grid, newPos); + bool downLeft = x > 0 && !getBitValue(grid, newPos - 1); + bool downRight = x < cols - 1 && !getBitValue(grid, newPos + 1); + + if (!down) { + if (downLeft && downRight) newX = random8(2) ? x - 1 : x + 1; + else if (downLeft) newX = x - 1; + else if (downRight) newX = x + 1; + else newY = y; // Snow is stuck + } + else if (sway && random8(30) < sway) { // Sway falling snow if horizontal and diagonal directions are open + if (x % 2 == 1 && downLeft && !getBitValue(grid, pos - 1)) newX = x - 1; // Odd Columns Move Left + else if (x % 2 == 0 && downRight && !getBitValue(grid, pos + 1)) newX = x + 1; // Even Columns Move Right + } + + if (newY == y && newX == x) continue; // Snow didn't move. Don't update grid or pixels + + setBitValue(grid, pos, 0); // Clear old + setBitValue(grid, XY(newX, newY), 1); // Set new + + if (overlay) continue; // Skip drawing if inverted overlay + + SEGMENT.setPixelColorXY(newX, newY, xyColor); // Draw new + SEGMENT.setPixelColorXY(x, y, color_blend(xyColor, bgColor, blur)); // Fade old + } + } + + // Spawn snow + int spawnChance = map(SEGMENT.intensity, 0, 255, 0, 100); + for (int x = 0; x < cols; x++) { // y = 0 + if (random8() >= spawnChance) continue; + if (getBitValue(grid, x)) {SEGENV.aux0 = rows; continue;} // Snow exists, overflowing + + setBitValue(grid, x, 1); // Spawn snow + if (overlay) continue; // Skip drawing if inverted overlay + + if (SEGMENT.check1) SEGMENT.setPixelColorXY(x, 0, ColorFromPalette(SEGPALETTE, random8())); // Use palette + else {int c = random8(80,200); SEGMENT.setPixelColorXY(x, 0, c, c, c);} // Use snow color + } + + SEGENV.step = strip.now; + return FRAMETIME; +} // mode_2DSnowFall() +static const char _data_FX_MODE_2DSNOWFALL[] PROGMEM = "Snow Fall@!,Spawn Rate,Despawn Rate,Blur,Sway Chance,Use Palette,Inverted Overlay,Prevent Overflow,;!,!;!;2;sx=128,ix=16,c1=17,c2=0,c3=0,o1=0,o2=0,o3=1"; + ///////////////////////// // 2D Hiphotic // ///////////////////////// @@ -8792,6 +8906,7 @@ void WS2812FX::setupEffectData() { addEffect(FX_MODE_2DSOAP, &mode_2Dsoap, _data_FX_MODE_2DSOAP); addEffect(FX_MODE_2DOCTOPUS, &mode_2Doctopus, _data_FX_MODE_2DOCTOPUS); addEffect(FX_MODE_2DWAVINGCELL, &mode_2Dwavingcell, _data_FX_MODE_2DWAVINGCELL); + addEffect(FX_MODE_2DSNOWFALL, &mode_2DSnowFall, _data_FX_MODE_2DSNOWFALL); addEffect(FX_MODE_2DAKEMI, &mode_2DAkemi, _data_FX_MODE_2DAKEMI); // audio diff --git a/wled00/FX.h b/wled00/FX.h index af707b41..15d3436a 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -349,8 +349,9 @@ bool strip_uses_global_leds(void); // WLEDMM implemented in FX_fcn. // #define FX_MODE_PALETTE_AR 193 // WLED-SR audioreactive palette #define FX_MODE_FIREWORKS_AR 194 // WLED-SR audioreactive fireworks #define FX_MODE_GEQLASER 195 // WLED-MM GEQ Laser +#define FX_MODE_2DSNOWFALL 196 // WLED-MM Snowfall -#define MODE_COUNT 196 +#define MODE_COUNT 197 typedef enum mapping1D2D { M12_Pixels = 0, From e759c4bd16f435465802e0cc388094926e3a50a5 Mon Sep 17 00:00:00 2001 From: Brandon502 <105077712+Brandon502@users.noreply.github.com> Date: Wed, 17 Jul 2024 14:34:17 -0400 Subject: [PATCH 002/232] blendPixelColor Shortcut Don't call color_blend if not needed. --- wled00/FX_2Dfcn.cpp | 3 ++- wled00/FX_fcn.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 83ec0f54..eb1c5aee 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -403,7 +403,8 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) { // Blends the specified color with the existing pixel color. void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend) { - setPixelColorXY(x, y, color_blend(getPixelColorXY(x,y), color, blend)); + if (blend == UINT8_MAX) setPixelColorXY(x, y, color); + else setPixelColorXY(x, y, color_blend(getPixelColorXY(x,y), color, blend)); } // Adds the specified color with the existing pixel color perserving color balance. diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 65849ff1..8cf35b7d 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1362,7 +1362,8 @@ void Segment::fill(uint32_t c) { // Blends the specified color with the existing pixel color. void Segment::blendPixelColor(int n, uint32_t color, uint8_t blend) { - setPixelColor(n, color_blend(getPixelColor(n), color, blend)); + if (blend == UINT8_MAX) setPixelColor(n, color); + else setPixelColor(n, color_blend(getPixelColor(n), color, blend)); } // Adds the specified color with the existing pixel color perserving color balance. From 9500f7badf144a0abc8e658a8af59e43505a513c Mon Sep 17 00:00:00 2001 From: Brandon502 <105077712+Brandon502@users.noreply.github.com> Date: Wed, 17 Jul 2024 18:04:51 -0400 Subject: [PATCH 003/232] Snow Fall - Drawing change Fixes snow disappearing due to auto brightness limiter (?) by redrawing stuck snow. --- wled00/FX.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index d0dcc920..2f79662e 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5451,15 +5451,12 @@ uint16_t mode_2DSnowFall(void) { // By: Brandon Butler else if (x % 2 == 0 && downRight && !getBitValue(grid, pos + 1)) newX = x + 1; // Even Columns Move Right } - if (newY == y && newX == x) continue; // Snow didn't move. Don't update grid or pixels - - setBitValue(grid, pos, 0); // Clear old - setBitValue(grid, XY(newX, newY), 1); // Set new - - if (overlay) continue; // Skip drawing if inverted overlay - - SEGMENT.setPixelColorXY(newX, newY, xyColor); // Draw new - SEGMENT.setPixelColorXY(x, y, color_blend(xyColor, bgColor, blur)); // Fade old + if (newY != y || newX != x) { // Snow moved + setBitValue(grid, pos, 0); // Clear old + setBitValue(grid, XY(newX, newY), 1); // Set new + if (!overlay) SEGMENT.setPixelColorXY(x, y, color_blend(xyColor, bgColor, blur)); // Fade old + } + if (!overlay) SEGMENT.setPixelColorXY(newX, newY, xyColor); // Draw new / redraw stuck } } From c2adb5be34240183678e2db64477b08d6c3596c2 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:47:49 -0400 Subject: [PATCH 004/232] HUB75 PSRAM Buffers --- wled00/bus_manager.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ wled00/bus_manager.h | 2 +- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 5f10ec77..a214e901 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -588,6 +588,29 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh mxconfig.gpio.d = 35; mxconfig.gpio.e = 21; +#elif defined(CONFIG_IDF_TARGET_ESP32S3) && defined(HUB75_TROYHACKS) // ESP32-S3 + + // TroyHacks HUB75 + + USER_PRINTLN("MatrixPanel_I2S_DMA - TroyHacks with PSRAM"); + + mxconfig.gpio.r1 = 1; + mxconfig.gpio.g1 = 2; + mxconfig.gpio.b1 = 42; + // 4th pin is GND + mxconfig.gpio.r2 = 41; + mxconfig.gpio.g2 = 40; + mxconfig.gpio.b2 = 39; + mxconfig.gpio.e = 38; + mxconfig.gpio.a = 45; + mxconfig.gpio.b = 48; + mxconfig.gpio.c = 47; + mxconfig.gpio.d = 21; // this says GND but should be the "D" pin + mxconfig.gpio.clk = 20; + mxconfig.gpio.lat = 19; + mxconfig.gpio.oe = 0; + // 16th pin is GND + #elif defined(CONFIG_IDF_TARGET_ESP32S3) // ESP32-S3 // Huidu HD-WF2 ESP32-S3 @@ -704,6 +727,7 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh #endif + mxconfig.clkphase = false; mxconfig.chain_length = max((u_int8_t) 1, min(bc.pins[0], (u_int8_t) 4)); // prevent bad data preventing boot due to low memory USER_PRINTF("MatrixPanel_I2S_DMA config - %ux%u length: %u\n", mxconfig.mx_width, mxconfig.mx_height, mxconfig.chain_length); @@ -750,7 +774,16 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh if (_ledBuffer) free(_ledBuffer); // should not happen if (_ledsDirty) free(_ledsDirty); // should not happen + + #if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM) + if (psramFound()){ + _ledsDirty = (byte*) ps_malloc(getBitArrayBytes(_len)); // create LEDs dirty bits + } else { + _ledsDirty = (byte*) malloc(getBitArrayBytes(_len)); // create LEDs dirty bits + } + #else _ledsDirty = (byte*) malloc(getBitArrayBytes(_len)); // create LEDs dirty bits + #endif if (_ledsDirty == nullptr) { display->stopDMAoutput(); @@ -762,7 +795,15 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh setBitArray(_ledsDirty, _len, false); // reset dirty bits if (mxconfig.double_buff == false) { + #if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM) + if (psramFound()){ + _ledBuffer = (CRGB*) ps_calloc(_len, sizeof(CRGB)); // create LEDs buffer (initialized to BLACK) + } else { + _ledBuffer = (CRGB*) calloc(_len, sizeof(CRGB)); // create LEDs buffer (initialized to BLACK) + } + #else _ledBuffer = (CRGB*) calloc(_len, sizeof(CRGB)); // create LEDs buffer (initialized to BLACK) + #endif } } diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 2379f430..b3360881 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -360,7 +360,7 @@ class BusHub75Matrix : public Bus { public: BusHub75Matrix(BusConfig &bc); - uint16_t getMaxPixels() const override { return 4096; }; + uint16_t getMaxPixels() const override { return MAX_LEDS; }; bool hasRGB() const override { return true; } bool hasWhite() const override { return false; } From 3ee05a74b4263c15a9a6520e356840ae6e55cda5 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Tue, 10 Sep 2024 15:11:17 -0400 Subject: [PATCH 005/232] PSRAM buffer fixes as per SoftHack007 --- wled00/bus_manager.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index bbae989d..a29ae468 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -719,7 +719,12 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh #endif - mxconfig.clkphase = false; + // mxconfig.double_buff = true; // <------------- Turn on double buffer + // mxconfig.driver = HUB75_I2S_CFG::ICN2038S; // experimental - use specific shift register driver + // mxconfig.latch_blanking = 3; + // mxconfig.i2sspeed = HUB75_I2S_CFG::HZ_10M; // experimental - 5MHZ should be enugh, but colours looks slightly better at 10MHz + // mxconfig.min_refresh_rate = 90; + mxconfig.clkphase = false; // can help in case that the leftmost column is invisible, or pixels on the right side "bleeds out" to the left. mxconfig.chain_length = max((u_int8_t) 1, min(bc.pins[0], (u_int8_t) 4)); // prevent bad data preventing boot due to low memory USER_PRINTF("MatrixPanel_I2S_DMA config - %ux%u length: %u\n", mxconfig.mx_width, mxconfig.mx_height, mxconfig.chain_length); @@ -769,15 +774,7 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh if (_ledBuffer) free(_ledBuffer); // should not happen if (_ledsDirty) free(_ledsDirty); // should not happen - #if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM) - if (psramFound()){ - _ledsDirty = (byte*) ps_malloc(getBitArrayBytes(_len)); // create LEDs dirty bits - } else { - _ledsDirty = (byte*) malloc(getBitArrayBytes(_len)); // create LEDs dirty bits - } - #else _ledsDirty = (byte*) malloc(getBitArrayBytes(_len)); // create LEDs dirty bits - #endif if (_ledsDirty == nullptr) { display->stopDMAoutput(); @@ -789,7 +786,7 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh setBitArray(_ledsDirty, _len, false); // reset dirty bits if (mxconfig.double_buff == false) { - #if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM) + #if defined(CONFIG_IDF_TARGET_ESP32S3) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM) && defined(CONFIG_SPIRAM_MODE_OCT) if (psramFound()){ _ledBuffer = (CRGB*) ps_calloc(_len, sizeof(CRGB)); // create LEDs buffer (initialized to BLACK) } else { From 645b45a22d8540ca7203f2cea605e6b952a17a17 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Mon, 16 Sep 2024 14:19:06 -0400 Subject: [PATCH 006/232] Added WLEDMM_NO_MAP_RESET Allows use of ledmaps with segments without messing with the segments. LEDs can remain remapped even with segments on top of them. (For Dom) --- wled00/FX_fcn.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 1f086fe0..95e0028c 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -2558,6 +2558,7 @@ bool WS2812FX::deserializeMap(uint8_t n) { uint16_t maxHeight = atoi(cleanUpName(fileName)); //DEBUG_PRINTF(" (\"height\": %s) \n", fileName) + #ifndef WLEDMM_NO_MAP_RESET //WLEDMM: support ledmap file properties width and height: if found change segment if (maxWidth * maxHeight > 0) { Segment::maxWidth = maxWidth; @@ -2566,6 +2567,7 @@ bool WS2812FX::deserializeMap(uint8_t n) { } else setUpMatrix(); //reset segment sizes to panels + #endif } USER_PRINTF("deserializeMap %d x %d\n", Segment::maxWidth, Segment::maxHeight); From 9fc279a3a6b5bd7ba4f2d70d6e5e2968f7b203bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Sat, 14 Sep 2024 22:51:19 +0200 Subject: [PATCH 007/232] Merge pull request #4142 from willmmiles/fix-webserver-pin Fix AsyncWebServer version pin --- platformio.ini | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index b5b93187..34b118e7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -248,7 +248,7 @@ lib_deps = ;;makuna/NeoPixelBus @ 2.7.5 ;; WLEDMM will be added in board specific sections ;;https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.7 ;; https://github.com/lost-hope/ESPAsyncWebServer.git#master ;; WLEDMM to display .log and .wled files in /edit - https://github.com/Aircoookie/ESPAsyncWebServer.git @ 2.2.1 ;; newer with bugfixes and stability improvements + https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.2.1 ;; newer with bugfixes and stability improvements #For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line #TFT_eSPI #For compatible OLED display uncomment following @@ -297,6 +297,37 @@ lib_deps = makuna/NeoPixelBus @ 2.7.5 ${env.lib_deps} +;; compatibilty flags - same as 0.14.0 which seems to work better on some 8266 boards. Not using PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48 +build_flags_compat = + -DESP8266 + -DFP_IN_IROM + ;;-Wno-deprecated-declarations + -Wno-misleading-indentation + ;;-Wno-attributes ;; silence warnings about unknown attribute 'maybe_unused' in NeoPixelBus + -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703 + -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH + -DVTABLES_IN_FLASH + -DMIMETYPE_MINIMAL + -DWLED_SAVE_IRAM ;; needed to prevent linker error + +;; this platform version was used for WLED 0.14.0 +platform_compat = espressif8266@4.2.0 +platform_packages_compat = + platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502 + platformio/tool-esptool #@ ~1.413.0 + platformio/tool-esptoolpy #@ ~1.30000.0 + +;; experimental - for using older NeoPixelBus 2.7.9 +lib_deps_compat = + ESPAsyncTCP @ 1.2.2 + ESPAsyncUDP + ESP8266PWM + fastled/FastLED @ 3.6.0 + IRremoteESP8266 @ 2.8.2 + makuna/NeoPixelBus @ 2.7.9 + https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.2.1 + + [esp32] #platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip platform = espressif32@3.5.0 From 08c2446f61ce8f8d185f83ed2b96f42f53b46039 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 17 Sep 2024 18:11:06 +0200 Subject: [PATCH 008/232] better PSRAM flags handling (minor) in MM, we can have WLED_USE_PSRAM_JSON instead of WLED_USE_PSRAM. --- wled00/bus_manager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index a29ae468..0811ea5d 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -786,8 +786,8 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh setBitArray(_ledsDirty, _len, false); // reset dirty bits if (mxconfig.double_buff == false) { - #if defined(CONFIG_IDF_TARGET_ESP32S3) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM) && defined(CONFIG_SPIRAM_MODE_OCT) - if (psramFound()){ + #if defined(CONFIG_IDF_TARGET_ESP32S3) && CONFIG_SPIRAM_MODE_OCT && defined(BOARD_HAS_PSRAM) && (defined(WLED_USE_PSRAM) || defined(WLED_USE_PSRAM_JSON)) + if (psramFound()) { _ledBuffer = (CRGB*) ps_calloc(_len, sizeof(CRGB)); // create LEDs buffer (initialized to BLACK) } else { _ledBuffer = (CRGB*) calloc(_len, sizeof(CRGB)); // create LEDs buffer (initialized to BLACK) From 22bd52198f16a5a2e560dd034a852be7cbbd9bcb Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 21 Sep 2024 15:59:35 +0100 Subject: [PATCH 009/232] Cleanup Hub75 config --- wled00/bus_manager.cpp | 51 ++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 0811ea5d..2c5f0b37 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -525,10 +525,32 @@ void BusNetwork::cleanup() { BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { _valid = false; - mxconfig.double_buff = false; // default to off, known to cause issue with some effects but needs more memory - fourScanPanel = nullptr; + mxconfig.double_buff = false; // Use our own memory-optimised buffer rather than the driver's own double-buffer + + // mxconfig.driver = HUB75_I2S_CFG::ICN2038S; // experimental - use specific shift register driver + // mxconfig.latch_blanking = 3; + // mxconfig.i2sspeed = HUB75_I2S_CFG::HZ_10M; // experimental - 5MHZ should be enugh, but colours looks slightly better at 10MHz + // mxconfig.min_refresh_rate = 90; + mxconfig.clkphase = false; // can help in case that the leftmost column is invisible, or pixels on the right side "bleeds out" to the left. + + // How many panels we have connected, cap at sane value + mxconfig.chain_length = max((u_int8_t) 1, min(bc.pins[0], (u_int8_t) 4)); // prevent bad data preventing boot due to low memory + + #if defined(CONFIG_IDF_TARGET_ESP32S3) && defined(BOARD_HAS_PSRAM) + if(bc.pins[0] > 4) { + USER_PRINT("WARNING, chain limited to 4"); + } + # else + // Disable this check if you are want to try bigger setups and accept you + // might need to do full erase to recover from memory relayed boot-loop if you push too far + if(mxconfig.mx_height >= 64 && (bc.pins[0] > 1)) { + USER_PRINT("WARNING, only single panel can be used of 64 pixel boards due to memory"); + mxconfig.chain_length = 1; + } + #endif + switch(bc.type) { case 101: mxconfig.mx_width = 32; @@ -556,21 +578,12 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh break; } - if(mxconfig.mx_height >= 64 && (bc.pins[0] > 1)) { - USER_PRINT("WARNING, only single panel can be used of 64 pixel boards due to memory"); - mxconfig.chain_length = 1; - } - - // mxconfig.driver = HUB75_I2S_CFG::SHIFTREG; - #if defined(ARDUINO_ADAFRUIT_MATRIXPORTAL_ESP32S3) // MatrixPortal ESP32-S3 // https://www.adafruit.com/product/5778 USER_PRINTLN("MatrixPanel_I2S_DMA - Matrix Portal S3 config"); - //mxconfig.double_buff = true; // <------------- Turn on double buffer - mxconfig.gpio.r1 = 42; mxconfig.gpio.g1 = 41; mxconfig.gpio.b1 = 40; @@ -588,11 +601,9 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh mxconfig.gpio.d = 35; mxconfig.gpio.e = 21; -#elif defined(CONFIG_IDF_TARGET_ESP32S3) && defined(HUB75_TROYHACKS) // ESP32-S3 +#elif defined(CONFIG_IDF_TARGET_ESP32S3) && defined(BOARD_HAS_PSRAM)// ESP32-S3 - // TroyHacks HUB75 - - USER_PRINTLN("MatrixPanel_I2S_DMA - TroyHacks with PSRAM"); + USER_PRINTLN("MatrixPanel_I2S_DMA - S3 with PSRAM"); mxconfig.gpio.r1 = 1; mxconfig.gpio.g1 = 2; @@ -719,14 +730,6 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh #endif - // mxconfig.double_buff = true; // <------------- Turn on double buffer - // mxconfig.driver = HUB75_I2S_CFG::ICN2038S; // experimental - use specific shift register driver - // mxconfig.latch_blanking = 3; - // mxconfig.i2sspeed = HUB75_I2S_CFG::HZ_10M; // experimental - 5MHZ should be enugh, but colours looks slightly better at 10MHz - // mxconfig.min_refresh_rate = 90; - mxconfig.clkphase = false; // can help in case that the leftmost column is invisible, or pixels on the right side "bleeds out" to the left. - mxconfig.chain_length = max((u_int8_t) 1, min(bc.pins[0], (u_int8_t) 4)); // prevent bad data preventing boot due to low memory - USER_PRINTF("MatrixPanel_I2S_DMA config - %ux%u length: %u\n", mxconfig.mx_width, mxconfig.mx_height, mxconfig.chain_length); // OK, now we can create our matrix object @@ -996,7 +999,7 @@ int BusManager::add(BusConfig &bc) { busses[numBusses] = new BusHub75Matrix(bc); USER_PRINTLN("[BusHub75Matrix] "); #else - USER_PRINTLN("[unsupported! BusHub75Matrix] "); + USER_PRINTLN("[unsupported! BusHub75Matrix - add flag -D WLED_ENABLE_HUB75MATRIX] "); return -1; #endif } else if (IS_DIGITAL(bc.type)) { From 2781be375f6458970416f91a98f2d28e49b1773c Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 21 Sep 2024 16:52:32 +0100 Subject: [PATCH 010/232] Swap pins used for S3 with PSRAM to avoid conflict --- wled00/bus_manager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 2c5f0b37..287f3b51 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -617,8 +617,8 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh mxconfig.gpio.b = 48; mxconfig.gpio.c = 47; mxconfig.gpio.d = 21; // this says GND but should be the "D" pin - mxconfig.gpio.clk = 20; - mxconfig.gpio.lat = 19; + mxconfig.gpio.clk = 18; + mxconfig.gpio.lat = 8; mxconfig.gpio.oe = 0; // 16th pin is GND From 24d2a43767dff331f3b388a10e21d122c7d9ce34 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 21 Sep 2024 16:53:13 +0100 Subject: [PATCH 011/232] Enable HUB75 for esp32S3_8MB_PSRAM_M --- platformio.ini | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index 34b118e7..05be0617 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1974,6 +1974,7 @@ board_build.flash_mode = qio ;; use "dio" if your board gets unstable with "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} ${common_mm.build_flags_M} + ${common_mm.HUB75_build_flags} -DBOARD_HAS_PSRAM -D WLED_USE_PSRAM_JSON ;; -D WLED_USE_PSRAM ;; your board supports PSRAM -D WLED_RELEASE_NAME=esp32S3_8MB_PSRAM_M -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; for Serial-to-USB chip @@ -1998,10 +1999,12 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden ; -D SR_DEBUG ; -D MIC_LOGGER lib_deps = ${esp32s3.lib_deps} ${common_mm.lib_deps_S} ${common_mm.lib_deps_V4_M} + ${common_mm.HUB75_lib_deps} + ;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation board_build.partitions = tools/WLED_ESP32_8MB.csv -; RAM: [==== ] 36.3% (used 118828 bytes from 327680 bytes) -; Flash: [======= ] 70.7% (used 1483465 bytes from 2097152 bytes) +; RAM: [== ] 21.1% (used 69156 bytes from 327680 bytes) +; Flash: [======== ] 75.9% (used 1591817 bytes from 2097152 bytes) ;; MM for ESP32-S3 boards - FASTPATH + optimize for speed [env:esp32S3_8MB_S] From 095d18bca59bc076e441eba212b2917e96a640c8 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sat, 21 Sep 2024 19:27:17 +0200 Subject: [PATCH 012/232] board definition for T7-S3 (LILYGO / TTGO) --- boards/lilygo-t7-s3.json | 47 ++++++++++++++++++++++++++++++++++++++++ wled00/pin_manager.cpp | 7 ++++++ 2 files changed, 54 insertions(+) create mode 100644 boards/lilygo-t7-s3.json diff --git a/boards/lilygo-t7-s3.json b/boards/lilygo-t7-s3.json new file mode 100644 index 00000000..4bf071fc --- /dev/null +++ b/boards/lilygo-t7-s3.json @@ -0,0 +1,47 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32s3_out.ld", + "memory_type": "qio_opi", + "partitions": "default_16MB.csv" + }, + "core": "esp32", + "extra_flags": [ + "-DARDUINO_TTGO_T7_S3", + "-DBOARD_HAS_PSRAM", + "-DARDUINO_USB_MODE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [ + [ + "0X303A", + "0x1001" + ] + ], + "mcu": "esp32s3", + "variant": "esp32s3" + }, + "connectivity": [ + "wifi", + "bluetooth" + ], + "debug": { + "openocd_target": "esp32s3.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "LILYGO T3-S3", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "require_upload_port": true, + "speed": 921600 + }, + "url": "https://www.aliexpress.us/item/3256804591247074.html", + "vendor": "LILYGO" +} \ No newline at end of file diff --git a/wled00/pin_manager.cpp b/wled00/pin_manager.cpp index d1e7a18b..07b5d42d 100644 --- a/wled00/pin_manager.cpp +++ b/wled00/pin_manager.cpp @@ -120,6 +120,13 @@ String PinManagerClass::getPinSpecialText(int gpio) { // special purpose PIN in if (gpio > 32 && gpio < 38) return (F("(reserved) Octal PSRAM or Octal Flash")); #endif //if (gpio == 0 || gpio == 3 || gpio == 45 || gpio == 46) return (F("(strapping pin)")); + #ifdef ARDUINO_TTGO_T7_S3 + // experimental: a few special pins of the T7-S3 board + if (gpio == 2) return (F("(reserved) _VBAT voltage monitoring")); + if (gpio == 17) return (F("onboard LED")); + //if (gpio == 3) return (F("(cross-connected to pin 3-1)")); // WLEDMM experimental + //if (gpio == 12) return (F("(cross-connected to pin 12-1)")); // WLEDMM experimental + #endif #elif defined(CONFIG_IDF_TARGET_ESP32S2) // ESP32-S2 From 569ba1c6267280fd3d246c38f618d50d6e78e8b2 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sat, 21 Sep 2024 19:29:28 +0200 Subject: [PATCH 013/232] s3: better handling of PSRAM pins --- wled00/pin_manager.cpp | 16 ++++++++++------ wled00/wled.cpp | 12 +++++++++--- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/wled00/pin_manager.cpp b/wled00/pin_manager.cpp index 07b5d42d..b9d90a7d 100644 --- a/wled00/pin_manager.cpp +++ b/wled00/pin_manager.cpp @@ -113,10 +113,8 @@ String PinManagerClass::getPinSpecialText(int gpio) { // special purpose PIN in #ifdef ARDUINO_ARCH_ESP32 #if defined(CONFIG_IDF_TARGET_ESP32S3) // ESP32-S3 - if (gpio > 18 && gpio < 21) return (F("USB (CDC) / JTAG")); - #if !defined(BOARD_HAS_PSRAM) - if (gpio > 32 && gpio < 38) return (F("(optional) Octal Flash or PSRAM")); - #else + if (gpio > 18 && gpio < 21) return (F("USB (CDC) or JTAG")); + #if CONFIG_SPIRAM_MODE_OCT && defined(BOARD_HAS_PSRAM) if (gpio > 32 && gpio < 38) return (F("(reserved) Octal PSRAM or Octal Flash")); #endif //if (gpio == 0 || gpio == 3 || gpio == 45 || gpio == 46) return (F("(strapping pin)")); @@ -136,7 +134,7 @@ String PinManagerClass::getPinSpecialText(int gpio) { // special purpose PIN in #elif defined(CONFIG_IDF_TARGET_ESP32C3) // ESP32-C3 - if (gpio > 17 && gpio < 20) return (F("USB (CDC) / JTAG")); + if (gpio > 17 && gpio < 20) return (F("USB (CDC) or JTAG")); //if (gpio == 2 || gpio == 8 || gpio == 9) return (F("(strapping pin)")); #else @@ -737,12 +735,18 @@ bool PinManagerClass::isPinOk(byte gpio, bool output) const #if defined(CONFIG_IDF_TARGET_ESP32C3) // strapping pins: 2, 8, & 9 if (gpio > 11 && gpio < 18) return false; // 11-17 SPI FLASH + #if ARDUINO_USB_CDC_ON_BOOT == 1 || ARDUINO_USB_DFU_ON_BOOT == 1 if (gpio > 17 && gpio < 20) return false; // 18-19 USB-JTAG + #endif #elif defined(CONFIG_IDF_TARGET_ESP32S3) // 00 to 18 are for general use. Be careful about straping pins GPIO0 and GPIO3 - these may be pulled-up or pulled-down on your board. + #if ARDUINO_USB_CDC_ON_BOOT == 1 || ARDUINO_USB_DFU_ON_BOOT == 1 if (gpio > 18 && gpio < 21) return false; // 19 + 20 = USB-JTAG. Not recommended for other uses. + #endif if (gpio > 21 && gpio < 33) return false; // 22 to 32: not connected + SPI FLASH - //if (gpio > 32 && gpio < 38) return false; // 33 to 37: not available if using _octal_ SPI Flash or _octal_ PSRAM + // #if CONFIG_SPIRAM_MODE_OCT && defined(BOARD_HAS_PSRAM) + // if (gpio > 32 && gpio < 38) return !psramFound(); // 33 to 37: not available if using _octal_ SPI Flash or _octal_ PSRAM + // #endif // 38 to 48 are for general use. Be careful about straping pins GPIO45 and GPIO46 - these may be pull-up or pulled-down on your board. #elif defined(CONFIG_IDF_TARGET_ESP32S2) // strapping pins: 0, 45 & 46 diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 9d54631f..52eb569a 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -594,9 +594,11 @@ void WLED::setup() #if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) //psramInit(); //WLEDMM?? softhack007: not sure if explicit init is really needed ... lets disable it here and see if that works #if defined(CONFIG_IDF_TARGET_ESP32S3) - // S3: reserve GPIO 33-37 for "octal" PSRAM - managed_pin_type pins[] = { {33, true}, {34, true}, {35, true}, {36, true}, {37, true} }; - pinManager.allocateMultiplePins(pins, sizeof(pins)/sizeof(managed_pin_type), PinOwner::SPI_RAM); + #if CONFIG_SPIRAM_MODE_OCT && defined(BOARD_HAS_PSRAM) + // S3: reserve GPIO 33-37 for "octal" PSRAM + managed_pin_type pins[] = { {33, true}, {34, true}, {35, true}, {36, true}, {37, true} }; + pinManager.allocateMultiplePins(pins, sizeof(pins)/sizeof(managed_pin_type), PinOwner::SPI_RAM); + #endif #elif defined(CONFIG_IDF_TARGET_ESP32S2) // S2: reserve GPIO 26-32 for PSRAM (may fail due to isPinOk() but that will also prevent other allocation) //managed_pin_type pins[] = { {26, true}, {27, true}, {28, true}, {29, true}, {30, true}, {31, true}, {32, true} }; @@ -815,7 +817,11 @@ void WLED::setup() USER_PRINTLN(F("\nGPIO\t| Assigned to\t\t| Info")); USER_PRINTLN(F("--------|-----------------------|------------")); for(int pinNr = 0; pinNr < WLED_NUM_PINS; pinNr++) { // 49 = highest PIN on ESP32-S3 +#if defined(CONFIG_IDF_TARGET_ESP32S3) + if((pinManager.isPinOk(pinNr, false)) || (pinNr > 18 && pinNr < 21)) { // softhack007: list USB pins +#else if(pinManager.isPinOk(pinNr, false)) { +#endif //if ((!pinManager.isPinAllocated(pinNr)) && (pinManager.getPinSpecialText(pinNr).length() == 0)) continue; // un-comment to hide no-name,unused GPIO pins bool is_inOut = pinManager.isPinOk(pinNr, true); #if 0 // for testing From 80a2f2b85b096c49e6bc24dd4b317f9865b9e031 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sat, 21 Sep 2024 19:41:51 +0200 Subject: [PATCH 014/232] minor corrections * fix some override problems bus_manager (canShow() must not be const!!!) * fixing some "comparing integer with different signedness" warnings --- wled00/FX_2Dfcn.cpp | 6 +++--- wled00/FX_fcn.cpp | 18 +++++++++--------- wled00/bus_manager.h | 28 ++++++++++++++-------------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 56d81443..ea5c4307 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -351,9 +351,9 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM: return; } - const int_fast16_t glen_ = groupLength(); // WLEDMM optimization - const int_fast16_t wid_ = width(); - const int_fast16_t hei_ = height(); + const uint_fast16_t glen_ = groupLength(); // WLEDMM optimization + const uint_fast16_t wid_ = width(); + const uint_fast16_t hei_ = height(); x *= glen_; // expand to physical pixels y *= glen_; // expand to physical pixels diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 95e0028c..e2250a47 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1413,12 +1413,12 @@ void __attribute__((hot)) Segment::fill(uint32_t c) { if (_bri_t < 255) scaled_col = color_fade(c, _bri_t); } // fill 2D segment - for(int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { + for(unsigned y = 0; y < rows; y++) for (unsigned x = 0; x < cols; x++) { if (simpleSegment) setPixelColorXY_fast(x, y, c, scaled_col, cols, rows); else setPixelColorXY_slow(x, y, c); } } else { // fill 1D strip - for (int x = 0; x < cols; x++) setPixelColor(x, c); + for (unsigned x = 0; x < cols; x++) setPixelColor(int(x), c); } } @@ -1471,8 +1471,8 @@ void __attribute__((hot)) Segment::fade_out(uint8_t rate) { int g2 = G(color2); int b2 = B(color2); - for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { - uint32_t color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x); + for (unsigned y = 0; y < rows; y++) for (unsigned x = 0; x < cols; x++) { + uint32_t color = is2D() ? getPixelColorXY(int(x), int(y)) : getPixelColor(int(x)); if (color == color2) continue; // WLEDMM speedup - pixel color = target color, so nothing to do int w1 = W(color); int r1 = R(color); @@ -1492,8 +1492,8 @@ void __attribute__((hot)) Segment::fade_out(uint8_t rate) { uint32_t colorNew = RGBW32(r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta); // WLEDMM if (colorNew != color) { // WLEDMM speedup - do not repaint the same color - if (is2D()) setPixelColorXY(x, y, colorNew); - else setPixelColor(x, colorNew); + if (is2D()) setPixelColorXY(int(x), int(y), colorNew); + else setPixelColor(int(x), colorNew); } } } @@ -1507,11 +1507,11 @@ void __attribute__((hot)) Segment::fadeToBlackBy(uint8_t fadeBy) { // WLEDMM minor optimization if(is2D()) { - for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { - uint32_t cc = getPixelColorXY(x,y); // WLEDMM avoid RGBW32 -> CRGB -> RGBW32 conversion + for (unsigned y = 0; y < rows; y++) for (unsigned x = 0; x < cols; x++) { + uint32_t cc = getPixelColorXY(int(x),int(y)); // WLEDMM avoid RGBW32 -> CRGB -> RGBW32 conversion uint32_t cc2 = color_fade(cc, scaledown); // fade //if (cc2 != cc) // WLEDMM only re-paint if faded color is different - disabled - causes problem with text overlay - setPixelColorXY((uint16_t)x, (uint16_t)y, cc2); + setPixelColorXY(int(x), int(y), cc2); } } else { for (uint_fast16_t x = 0; x < cols; x++) { diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index b3360881..b7a50cf0 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -65,7 +65,7 @@ struct BusConfig { else if (type > 47) nPins = 2; else if (type > 40 && type < 46) nPins = NUM_PWM_PINS(type); else if (type >= TYPE_HUB75MATRIX && type <= (TYPE_HUB75MATRIX + 10)) nPins = 0; - for (uint8_t i = 0; i < nPins; i++) pins[i] = ppins[i]; + for (uint8_t i = 0; i < min(unsigned(nPins),5U); i++) pins[i] = ppins[i]; //softhack007 fix for potential array out-of-bounds access } //validates start and length and extends total if needed @@ -135,21 +135,21 @@ class Bus { virtual void setStatusPixel(uint32_t c) {} virtual void setPixelColor(uint16_t pix, uint32_t c) = 0; virtual uint32_t getPixelColor(uint16_t pix) const { return 0; } - virtual void setBrightness(uint8_t b, bool immediate=false) { _bri = b; }; + virtual void setBrightness(uint8_t b, bool immediate=false) { _bri = b; } virtual void cleanup() = 0; virtual uint8_t getPins(uint8_t* pinArray) const { return 0; } - virtual uint16_t getLength() const { return _len; } + virtual inline uint16_t getLength() const { return _len; } virtual void setColorOrder() {} virtual uint8_t getColorOrder() const { return COL_ORDER_RGB; } - virtual uint8_t skippedLeds() { return 0; } + virtual uint8_t skippedLeds() const { return 0; } virtual uint16_t getFrequency() const { return 0U; } inline uint16_t getStart() const { return _start; } inline void setStart(uint16_t start) { _start = start; } inline uint8_t getType() const { return _type; } inline bool isOk() const { return _valid; } inline bool isOffRefreshRequired() const { return _needsRefresh; } - bool containsPixel(uint16_t pix) const { return pix >= _start && pix < _start+_len; } - virtual uint16_t getMaxPixels() const { return MAX_LEDS_PER_BUS; }; + //inline bool containsPixel(uint16_t pix) const { return pix >= _start && pix < _start+_len; } // WLEDMM not used, plus wrong - it does not consider skipped pixels + virtual uint16_t getMaxPixels() const { return MAX_LEDS_PER_BUS; } virtual bool hasRGB() const { if ((_type >= TYPE_WS2812_1CH && _type <= TYPE_WS2812_WWA) || _type == TYPE_ANALOG_1CH || _type == TYPE_ANALOG_2CH || _type == TYPE_ONOFF) return false; @@ -207,7 +207,7 @@ class BusDigital : public Bus { inline void show(); - bool canShow() const; + bool canShow() override; void setBrightness(uint8_t b, bool immediate); @@ -215,13 +215,13 @@ class BusDigital : public Bus { void setPixelColor(uint16_t pix, uint32_t c); - uint32_t getPixelColor(uint16_t pix) const; + uint32_t getPixelColor(uint16_t pix) const override; uint8_t getColorOrder() const { return _colorOrder; } - uint16_t getLength() const { + uint16_t getLength() const override { return _len - _skip; } @@ -229,11 +229,11 @@ class BusDigital : public Bus { void setColorOrder(uint8_t colorOrder); - uint8_t skippedLeds() const { + uint8_t skippedLeds() const override { return _skip; } - uint16_t getFrequency() const { return _frequencykHz; } + uint16_t getFrequency() const override { return _frequencykHz; } void reinit(); @@ -267,7 +267,7 @@ class BusPwm : public Bus { uint8_t getPins(uint8_t* pinArray) const; - uint16_t getFrequency() const { return _frequency; } + uint16_t getFrequency() const override { return _frequency; } void cleanup() { deallocatePins(); @@ -329,14 +329,14 @@ class BusNetwork : public Bus { void show(); - bool canShow() const { + bool canShow() override { // this should be a return value from UDP routine if it is still sending data out return !_broadcastLock; } uint8_t getPins(uint8_t* pinArray) const; - uint16_t getLength() const { + uint16_t getLength() const override { return _len; } From 087f156d35aa89185524f3fe039ef7446d120311 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 21 Sep 2024 18:51:55 +0100 Subject: [PATCH 015/232] Reset ESP32-HUB75-MatrixPanel-DMA library version back to one with the S3_LCD_DIV_NUM fix --- platformio.ini | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/platformio.ini b/platformio.ini index 05be0617..57f0337d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1071,10 +1071,7 @@ HUB75_build_flags = -D NO_FAST_FUNCTIONS ;; If you are not using AdafruitGFX than you probably do not need this either, save memory/code size -D NO_CIE1931 ;; Do not use LED brightness compensation described in CIE 1931. We use FastLED dimming already -D S3_LCD_DIV_NUM=20 ;; Attempt to fix wifi performance issue when panel active with S3 chips -;; HUB75_lib_deps = https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA.git @ 3.0.11 ;; breaks the build (2024-07-30) -;; HUB75_lib_deps = https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA.git#1e4c80a26454aca7b8129bd5a966b0af329d2703 ;; 3.0.10 - something strange is going on here ... -;; HUB75_lib_deps = https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA.git#1e4c80a26454aca7b8129bd5a966b0af329d2703 ;; 3.0.10 - something strange is going on here ... -HUB75_lib_deps = https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA.git#c4ecdcfeeb5aa668d92ddf3c3c74bc93316f6e10 ;; 3.0.11 +HUB75_lib_deps = https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA.git#aa28e2a9 ;; S3_LCD_DIV_NUM fix HUB75_lib_ignore = ESP32 HUB75 LED MATRIX PANEL DMA Display ;; to remove the HUB75 lib dependancy (saves a few bytes) NetDebug_build_flags = From 3669946a09206816160aaf683672c481e276443c Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 21 Sep 2024 18:54:38 +0100 Subject: [PATCH 016/232] Swap pins used for S3 to avoid gpio0 --- wled00/bus_manager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 287f3b51..d0fc80fa 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -616,10 +616,10 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh mxconfig.gpio.a = 45; mxconfig.gpio.b = 48; mxconfig.gpio.c = 47; - mxconfig.gpio.d = 21; // this says GND but should be the "D" pin + mxconfig.gpio.d = 21; mxconfig.gpio.clk = 18; mxconfig.gpio.lat = 8; - mxconfig.gpio.oe = 0; + mxconfig.gpio.oe = 3; // 16th pin is GND #elif defined(CONFIG_IDF_TARGET_ESP32S3) // ESP32-S3 From 28fe5cbd8b5eb70cc5eb725b1cf718ac5d9a1f3e Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sat, 21 Sep 2024 20:04:06 +0200 Subject: [PATCH 017/232] forgot one --- wled00/bus_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index d0fc80fa..61cc90fb 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -162,7 +162,7 @@ void BusDigital::show() { PolyBus::show(_busPtr, _iType); } -bool BusDigital::canShow() const { +bool BusDigital::canShow() { return PolyBus::canShow(_busPtr, _iType); } From e100a2d69a36b4f08bc7d76a44efa4909bcc1cc7 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sat, 21 Sep 2024 20:07:52 +0200 Subject: [PATCH 018/232] switch off brightness reduction this was added due to a recommendation from MrF (HUB75) but it seems to work without, too. --- wled00/bus_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 61cc90fb..e0e875e3 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -885,7 +885,7 @@ uint32_t BusHub75Matrix::getPixelColor(uint16_t pix) const { void BusHub75Matrix::setBrightness(uint8_t b, bool immediate) { _bri = b; - if (_bri > 238) _bri=238; + // if (_bri > 238) _bri=238; // not strictly needed. Enable this line if you see glitches at highest brightness. display->setBrightness(_bri); } From f1088bb5c0376e8a6d2e954cb5fda02694359db6 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sat, 21 Sep 2024 20:41:05 +0200 Subject: [PATCH 019/232] (experimental) loop2 to get fresh audio just before drawing * introducing usermod::loop2() - runs just before strip.service() --> expecting to reduce lagging between audio and visual to an absolute minimum. --- usermods/audioreactive/audio_reactive.h | 5 +++++ wled00/fcn_declare.h | 2 ++ wled00/um_manager.cpp | 1 + wled00/wled.cpp | 13 +++++++++++++ wled00/wled.h | 2 +- 5 files changed, 22 insertions(+), 1 deletion(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 709a5960..1c46e33a 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -2352,6 +2352,11 @@ class AudioReactive : public Usermod { #endif } +#if defined(_MoonModules_WLED_) && defined(WLEDMM_FASTPATH) + void loop2(void) { + loop(); + } +#endif bool getUMData(um_data_t **data) { diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 09f9724b..31b82293 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -299,6 +299,7 @@ class Usermod { virtual ~Usermod() { if (um_data) delete um_data; } virtual void setup() = 0; // pure virtual, has to be overriden virtual void loop() = 0; // pure virtual, has to be overriden + virtual void loop2() {} // WLEDMM called just before effects will be processed virtual void handleOverlayDraw() {} // called after all effects have been processed, just before strip.show() virtual bool handleButton(uint8_t b) { return false; } // button overrides are possible here virtual bool getUMData(um_data_t **data) { if (data) *data = nullptr; return false; }; // usermod data exchange [see examples for audio effects] @@ -329,6 +330,7 @@ class UsermodManager { public: void loop(); + void loop2(); // WLEDMM loop just before drawing effects (presets and everything already handled) void handleOverlayDraw(); bool handleButton(uint8_t b); bool getUMData(um_data_t **um_data, uint8_t mod_id = USERMOD_ID_RESERVED); // USERMOD_ID_RESERVED will poll all usermods diff --git a/wled00/um_manager.cpp b/wled00/um_manager.cpp index 2fe44db9..45647d60 100644 --- a/wled00/um_manager.cpp +++ b/wled00/um_manager.cpp @@ -9,6 +9,7 @@ void UsermodManager::connected() { for (byte i = 0; i < numMods; i++) um void UsermodManager::loop() { for (byte i = 0; i < numMods; i++) ums[i]->loop(); } void UsermodManager::handleOverlayDraw() { for (byte i = 0; i < numMods; i++) ums[i]->handleOverlayDraw(); } // void UsermodManager::appendConfigData() { for (byte i = 0; i < numMods; i++) ums[i]->appendConfigData(); } //WLEDMM not used +void UsermodManager::loop2() { for (unsigned i = 0; i < numMods; i++) ums[i]->loop2(); } bool UsermodManager::handleButton(uint8_t b) { bool overrideIO = false; for (byte i = 0; i < numMods; i++) { diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 52eb569a..41e04d1a 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -213,6 +213,19 @@ void WLED::loop() handlePresets(); yield(); +#if defined(_MoonModules_WLED_) && defined(WLEDMM_FASTPATH) + #ifdef WLED_DEBUG + unsigned long usermod2Millis = millis(); + #endif + usermods.loop2(); + #ifdef WLED_DEBUG + usermod2Millis = millis() - usermod2Millis; + avgUsermodMillis += usermod2Millis; + if (usermod2Millis > maxUsermodMillis) maxUsermodMillis = usermod2Millis; + #endif + yield(); +#endif + #ifdef WLED_DEBUG unsigned long stripMillis = millis(); #endif diff --git a/wled00/wled.h b/wled00/wled.h index 9bfd7dd1..82116ee6 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2409060 +#define VERSION 2409210 // 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 18b35d11f8ed27cb507511ce7a6152ffb5d1089d Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sat, 21 Sep 2024 20:42:03 +0200 Subject: [PATCH 020/232] usermod manager - small optimization * use native types only (faster, smaller) --- wled00/fcn_declare.h | 2 +- wled00/um_manager.cpp | 36 ++++++++++++++++++------------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 31b82293..694e9c9f 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -326,7 +326,7 @@ class Usermod { class UsermodManager { private: Usermod* ums[WLED_MAX_USERMODS]; - byte numMods = 0; + unsigned numMods = 0; public: void loop(); diff --git a/wled00/um_manager.cpp b/wled00/um_manager.cpp index 45647d60..7c6153f5 100644 --- a/wled00/um_manager.cpp +++ b/wled00/um_manager.cpp @@ -4,50 +4,50 @@ */ //Usermod Manager internals -void UsermodManager::setup() { for (byte i = 0; i < numMods; i++) ums[i]->setup(); } -void UsermodManager::connected() { for (byte i = 0; i < numMods; i++) ums[i]->connected(); } -void UsermodManager::loop() { for (byte i = 0; i < numMods; i++) ums[i]->loop(); } -void UsermodManager::handleOverlayDraw() { for (byte i = 0; i < numMods; i++) ums[i]->handleOverlayDraw(); } -// void UsermodManager::appendConfigData() { for (byte i = 0; i < numMods; i++) ums[i]->appendConfigData(); } //WLEDMM not used +void UsermodManager::setup() { for (unsigned i = 0; i < numMods; i++) ums[i]->setup(); } +void UsermodManager::connected() { for (unsigned i = 0; i < numMods; i++) ums[i]->connected(); } +void UsermodManager::loop() { for (unsigned i = 0; i < numMods; i++) ums[i]->loop(); } void UsermodManager::loop2() { for (unsigned i = 0; i < numMods; i++) ums[i]->loop2(); } +void UsermodManager::handleOverlayDraw() { for (unsigned i = 0; i < numMods; i++) ums[i]->handleOverlayDraw(); } +// void UsermodManager::appendConfigData() { for (unsigned i = 0; i < numMods; i++) ums[i]->appendConfigData(); } //WLEDMM not used bool UsermodManager::handleButton(uint8_t b) { bool overrideIO = false; - for (byte i = 0; i < numMods; i++) { + for (unsigned i = 0; i < numMods; i++) { if (ums[i]->handleButton(b)) overrideIO = true; } return overrideIO; } bool UsermodManager::getUMData(um_data_t **data, uint8_t mod_id) { - for (byte i = 0; i < numMods; i++) { + for (unsigned i = 0; i < numMods; i++) { if (mod_id > 0 && ums[i]->getId() != mod_id) continue; // only get data form requested usermod if provided if (ums[i]->getUMData(data)) return true; // if usermod does provide data return immediately (only one usermod can provide data at one time) } return false; } -void UsermodManager::addToJsonState(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->addToJsonState(obj); } -void UsermodManager::addToJsonInfo(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->addToJsonInfo(obj); } -void UsermodManager::readFromJsonState(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->readFromJsonState(obj); } -void UsermodManager::addToConfig(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->addToConfig(obj); } +void UsermodManager::addToJsonState(JsonObject& obj) { for (unsigned i = 0; i < numMods; i++) ums[i]->addToJsonState(obj); } +void UsermodManager::addToJsonInfo(JsonObject& obj) { for (unsigned i = 0; i < numMods; i++) ums[i]->addToJsonInfo(obj); } +void UsermodManager::readFromJsonState(JsonObject& obj) { for (unsigned i = 0; i < numMods; i++) ums[i]->readFromJsonState(obj); } +void UsermodManager::addToConfig(JsonObject& obj) { for (unsigned i = 0; i < numMods; i++) ums[i]->addToConfig(obj); } bool UsermodManager::readFromConfig(JsonObject& obj) { bool allComplete = true; - for (byte i = 0; i < numMods; i++) { + for (unsigned i = 0; i < numMods; i++) { if (!ums[i]->readFromConfig(obj)) allComplete = false; } return allComplete; } -void UsermodManager::onMqttConnect(bool sessionPresent) { for (byte i = 0; i < numMods; i++) ums[i]->onMqttConnect(sessionPresent); } +void UsermodManager::onMqttConnect(bool sessionPresent) { for (unsigned i = 0; i < numMods; i++) ums[i]->onMqttConnect(sessionPresent); } bool UsermodManager::onMqttMessage(char* topic, char* payload) { - for (byte i = 0; i < numMods; i++) if (ums[i]->onMqttMessage(topic, payload)) return true; + for (unsigned i = 0; i < numMods; i++) if (ums[i]->onMqttMessage(topic, payload)) return true; return false; } -void UsermodManager::onUpdateBegin(bool init) { for (byte i = 0; i < numMods; i++) ums[i]->onUpdateBegin(init); } // notify usermods that update is to begin -void UsermodManager::onStateChange(uint8_t mode) { for (byte i = 0; i < numMods; i++) ums[i]->onStateChange(mode); } // notify usermods that WLED state changed +void UsermodManager::onUpdateBegin(bool init) { for (unsigned i = 0; i < numMods; i++) ums[i]->onUpdateBegin(init); } // notify usermods that update is to begin +void UsermodManager::onStateChange(uint8_t mode) { for (unsigned i = 0; i < numMods; i++) ums[i]->onStateChange(mode); } // notify usermods that WLED state changed /* * Enables usermods to lookup another Usermod. */ Usermod* UsermodManager::lookup(uint16_t mod_id) { - for (byte i = 0; i < numMods; i++) { + for (unsigned i = 0; i < numMods; i++) { if (ums[i]->getId() == mod_id) { return ums[i]; } @@ -58,7 +58,7 @@ Usermod* UsermodManager::lookup(uint16_t mod_id) { //WLEDMM: used by Usermods in xml.cpp Usermod* UsermodManager::lookupName(const char *mod_name) { //WLEDMM: hack to get the usermod object with the mod_name (better would be to store the usermod name in the class but that requires change to all usermods) - for (byte i = 0; i < numMods; i++) { + for (unsigned i = 0; i < numMods; i++) { // StaticJsonDocument <1024> docx; JsonObject um = doc.createNestedObject("um"); //WLEDMM reuse the global doc variable here From 3a638bb396b68d13d84867bf4342981eca645e76 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sun, 22 Sep 2024 16:22:53 +0200 Subject: [PATCH 021/232] busmanger cannot handle more than 5 pins (hardcoded) sizeof(pins)/sizeof(pins[0]) is the number of array elements in pins[]. --- wled00/bus_manager.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index b7a50cf0..eb431333 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -54,7 +54,7 @@ struct BusConfig { uint8_t skipAmount; bool refreshReq; uint8_t autoWhite; - uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255}; + uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255}; // WLEDMM warning: this means that BusConfig cannot handle nore than 5 pins per bus! uint16_t frequency; BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U) { refreshReq = (bool) GET_BIT(busType,7); @@ -65,7 +65,7 @@ struct BusConfig { else if (type > 47) nPins = 2; else if (type > 40 && type < 46) nPins = NUM_PWM_PINS(type); else if (type >= TYPE_HUB75MATRIX && type <= (TYPE_HUB75MATRIX + 10)) nPins = 0; - for (uint8_t i = 0; i < min(unsigned(nPins),5U); i++) pins[i] = ppins[i]; //softhack007 fix for potential array out-of-bounds access + for (uint8_t i = 0; i < min(unsigned(nPins), sizeof(pins)/sizeof(pins[0])); i++) pins[i] = ppins[i]; //softhack007 fix for potential array out-of-bounds access } //validates start and length and extends total if needed From ffc9ec3cfbd11034dd345add23cb8875c69279ca Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sun, 22 Sep 2024 17:07:26 +0200 Subject: [PATCH 022/232] exclude HUB75 from auto brightness limiter For a 64x64 panel, ABL assumes 4A of standby current. This does not make any sense. --- wled00/FX.h | 1 + wled00/FX_fcn.cpp | 18 ++++++++++++++++-- wled00/const.h | 2 ++ wled00/json.cpp | 2 +- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index a0f3e90f..505721c7 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -963,6 +963,7 @@ class WS2812FX { // 96 bytes ablMilliampsMax, currentMilliamps, getLengthPhysical(void) const, + getLengthPhysical2(void) const, // WLEDMM total length including HUB75, network busses excluded __attribute__((pure)) getLengthTotal(void) const, // will include virtual/nonexistent pixels in matrix //WLEDMM attribute added getFps() const; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index e2250a47..9e2368ab 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1985,7 +1985,8 @@ void WS2812FX::estimateCurrentAndLimitBri() { for (uint_fast8_t bNum = 0; bNum < busses.getNumBusses(); bNum++) { Bus *bus = busses.getBus(bNum); - if (bus->getType() >= TYPE_NET_DDP_RGB) continue; //exclude non-physical network busses + auto btype = bus->getType(); + if (EXCLUDE_FROM_ABL(btype)) continue; // WLEDMM exclude non-ABL and network busses uint16_t len = bus->getLength(); uint32_t busPowerSum = 0; for (uint_fast16_t i = 0; i < len; i++) { //sum up the usage of each LED @@ -2196,7 +2197,20 @@ uint16_t WS2812FX::getLengthPhysical(void) const { // WLEDMM fast int types uint_fast16_t len = 0; for (unsigned b = 0; b < busses.getNumBusses(); b++) { // WLEDMM use native (fast) types Bus *bus = busses.getBus(b); - if (bus->getType() >= TYPE_NET_DDP_RGB) continue; //exclude non-physical network busses + auto btype = bus->getType(); + if (EXCLUDE_FROM_ABL(btype)) continue; //exclude HUB75, and non-physical network busses + len += bus->getLength(); + } + return len; +} + +//WLEDMM - getLengthPhysical plus plysical busses not supporting ABL (i.e. HUB75) +uint16_t WS2812FX::getLengthPhysical2(void) const { + uint_fast16_t len = 0; + for (unsigned b = 0; b < busses.getNumBusses(); b++) { + Bus *bus = busses.getBus(b); + auto btype = bus->getType(); + if (IS_VIRTUAL(btype)) continue; len += bus->getLength(); } return len; diff --git a/wled00/const.h b/wled00/const.h index ff8b0371..2792f158 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -257,6 +257,8 @@ #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) +#define IS_VIRTUAL(t) ( ((t) <= TYPE_RESERVED) || (((t) >= TYPE_NET_DDP_RGB) && ((t) < (TYPE_NET_DDP_RGB + 16))) ) // WLEDMM 80..95 are network "virtual" busses +#define EXCLUDE_FROM_ABL(t) ( IS_VIRTUAL(t) || ( (t) >= (TYPE_HUB75MATRIX + 10) && (t) < (TYPE_HUB75MATRIX + 10))) // WLEDMM do not apply ato-brightness-limiter on these bus types //Color orders #define COL_ORDER_GRB 0 //GRB(w),defaut diff --git a/wled00/json.cpp b/wled00/json.cpp index f0f189b2..f3a84061 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -904,7 +904,7 @@ void serializeInfo(JsonObject root) JsonObject leds = root.createNestedObject("leds"); leds[F("count")] = strip.getLengthTotal(); - leds[F("countP")] = strip.getLengthPhysical(); //WLEDMM + leds[F("countP")] = strip.getLengthPhysical2(); //WLEDMM - getLengthPhysical plus plysical busses not supporting ABL (i.e. HUB75) leds[F("pwr")] = strip.currentMilliamps; leds["fps"] = strip.getFps(); leds[F("maxpwr")] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0; From c5a0ab33eabe469650804371412fe6956b34da39 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sun, 22 Sep 2024 17:10:21 +0200 Subject: [PATCH 023/232] clean up BusConfig nPins chaos still very confusing logic, but documented in a better way. --- wled00/bus_manager.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index eb431333..089020a0 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -60,11 +60,11 @@ struct BusConfig { refreshReq = (bool) GET_BIT(busType,7); type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh) count = len; start = pstart; colorOrder = pcolorOrder; reversed = rev; skipAmount = skip; autoWhite = aw; frequency = clock_kHz; - uint8_t nPins = 1; - if (type >= TYPE_NET_DDP_RGB && type < 96) nPins = 4; //virtual network bus. 4 "pins" store IP address - else if (type > 47) nPins = 2; - else if (type > 40 && type < 46) nPins = NUM_PWM_PINS(type); - else if (type >= TYPE_HUB75MATRIX && type <= (TYPE_HUB75MATRIX + 10)) nPins = 0; + uint8_t nPins = 1; // default = only one pin (clockless LEDs like WS281x) + if ((type >= TYPE_NET_DDP_RGB) && (type < (TYPE_NET_DDP_RGB + 16))) nPins = 4; // virtual network bus. 4 "pins" store IP address + else if ((type > 47) && (type < 63)) nPins = 2; // (data + clock / SPI) busses - two pins + else if (IS_PWM(type)) nPins = NUM_PWM_PINS(type); // PWM needs 1..5 pins + else if (type >= TYPE_HUB75MATRIX && type <= (TYPE_HUB75MATRIX + 10)) nPins = 0; // HUB75 does not use LED pins for (uint8_t i = 0; i < min(unsigned(nPins), sizeof(pins)/sizeof(pins[0])); i++) pins[i] = ppins[i]; //softhack007 fix for potential array out-of-bounds access } From 0a0192675858e5aed2340587e820a008d832b8e4 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sun, 22 Sep 2024 17:13:34 +0200 Subject: [PATCH 024/232] docu --- wled00/const.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wled00/const.h b/wled00/const.h index 2792f158..fe9b9ca6 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -245,7 +245,9 @@ #define TYPE_P9813 53 #define TYPE_LPD6803 54 +// WLEDMM additional types #define TYPE_HUB75MATRIX 100 // 100 - 110 +// WLEDMM caution - do not use bus types > 127 //Network types (master broadcast) (80-95) #define TYPE_NET_DDP_RGB 80 //network DDP RGB bus (master broadcast bus) From 1ddc9e38a25428ba01f3c913fbb30c6ab69b3d3f Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:55:40 +0200 Subject: [PATCH 025/232] HUB75 bugfix - preserve chain length parameter chain length was always replaced with "4" --- wled00/bus_manager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 089020a0..80488b6a 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -64,7 +64,7 @@ struct BusConfig { if ((type >= TYPE_NET_DDP_RGB) && (type < (TYPE_NET_DDP_RGB + 16))) nPins = 4; // virtual network bus. 4 "pins" store IP address else if ((type > 47) && (type < 63)) nPins = 2; // (data + clock / SPI) busses - two pins else if (IS_PWM(type)) nPins = NUM_PWM_PINS(type); // PWM needs 1..5 pins - else if (type >= TYPE_HUB75MATRIX && type <= (TYPE_HUB75MATRIX + 10)) nPins = 0; // HUB75 does not use LED pins + else if (type >= TYPE_HUB75MATRIX && type <= (TYPE_HUB75MATRIX + 10)) nPins = 1; // HUB75 does not use LED pins, but we need to preserve the "chain length" parameter for (uint8_t i = 0; i < min(unsigned(nPins), sizeof(pins)/sizeof(pins[0])); i++) pins[i] = ppins[i]; //softhack007 fix for potential array out-of-bounds access } From bc11ec4b4e99a3f81c31a886a31f57aff08b0d8e Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Thu, 26 Sep 2024 17:33:40 +0200 Subject: [PATCH 026/232] HUB75 bugfixes * exclude HUB75 from ABL (bad macro in const.h; "pwr" item in json.cpp) * show HUB75 as "physical" (LEDs preferences) * minor corrections --- wled00/bus_manager.cpp | 2 +- wled00/const.h | 2 +- wled00/data/settings_leds.htm | 2 +- wled00/json.cpp | 2 +- wled00/wled.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index e0e875e3..963409c6 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -536,7 +536,7 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh mxconfig.clkphase = false; // can help in case that the leftmost column is invisible, or pixels on the right side "bleeds out" to the left. // How many panels we have connected, cap at sane value - mxconfig.chain_length = max((u_int8_t) 1, min(bc.pins[0], (u_int8_t) 4)); // prevent bad data preventing boot due to low memory + mxconfig.chain_length = max((uint8_t) 1, min(bc.pins[0], (uint8_t) 4)); // prevent bad data preventing boot due to low memory #if defined(CONFIG_IDF_TARGET_ESP32S3) && defined(BOARD_HAS_PSRAM) if(bc.pins[0] > 4) { diff --git a/wled00/const.h b/wled00/const.h index fe9b9ca6..55f9f780 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -260,7 +260,7 @@ #define NUM_PWM_PINS(t) ((t) - 40) //for analog PWM 41-45 only #define IS_2PIN(t) ((t) > 47) #define IS_VIRTUAL(t) ( ((t) <= TYPE_RESERVED) || (((t) >= TYPE_NET_DDP_RGB) && ((t) < (TYPE_NET_DDP_RGB + 16))) ) // WLEDMM 80..95 are network "virtual" busses -#define EXCLUDE_FROM_ABL(t) ( IS_VIRTUAL(t) || ( (t) >= (TYPE_HUB75MATRIX + 10) && (t) < (TYPE_HUB75MATRIX + 10))) // WLEDMM do not apply ato-brightness-limiter on these bus types +#define EXCLUDE_FROM_ABL(t) ( IS_VIRTUAL(t) || ( (t) >= (TYPE_HUB75MATRIX) && (t) < (TYPE_HUB75MATRIX + 10))) // WLEDMM do not apply auto-brightness-limiter on these bus types //Color orders #define COL_ORDER_GRB 0 //GRB(w),defaut diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 6d7c36c9..1fd0e4ba 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -250,7 +250,7 @@ if (s+c > sLC) sLC = s+c; //update total count if(c>maxLC)maxLC=c; //max per output var t = parseInt(d.getElementsByName("LT"+n)[0].value); // LED type SELECT - if (t<80) sPC+=c; //virtual out busses do not count towards physical LEDs + if ((t<128) && ((t<80) || ((t>99)))) sPC+=c; //virtual out busses do not count towards physical LEDs //WLEDMM include HUB75 (>=100) } // increase led count continue; } diff --git a/wled00/json.cpp b/wled00/json.cpp index f3a84061..a2b9514b 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -905,7 +905,7 @@ void serializeInfo(JsonObject root) JsonObject leds = root.createNestedObject("leds"); leds[F("count")] = strip.getLengthTotal(); leds[F("countP")] = strip.getLengthPhysical2(); //WLEDMM - getLengthPhysical plus plysical busses not supporting ABL (i.e. HUB75) - leds[F("pwr")] = strip.currentMilliamps; + leds[F("pwr")] = strip.currentMilliamps > 100 ? strip.currentMilliamps : 0; // WLEDMM show "not calculated" for HUB75, or when all LEDs are out leds["fps"] = strip.getFps(); leds[F("maxpwr")] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0; leds[F("maxseg")] = strip.getMaxSegments(); diff --git a/wled00/wled.h b/wled00/wled.h index 82116ee6..74deeace 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2409210 +#define VERSION 2409260 // 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 0ca3f136b12b560d95be671cad57720c9fbf4641 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sat, 28 Sep 2024 02:06:48 +0200 Subject: [PATCH 027/232] ws error printing improvement * limit to 3 messages per second * print size when alloc failed --- wled00/ws.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/wled00/ws.cpp b/wled00/ws.cpp index 22c5afd2..85bad64d 100644 --- a/wled00/ws.cpp +++ b/wled00/ws.cpp @@ -215,7 +215,11 @@ static bool sendLiveLedsWs(uint32_t wsClient) // WLEDMM added "static" if ((bufSize < 1) || (used < 1)) return(false); // WLEDMM should not happen AsyncWebSocketBuffer wsBuf(bufSize); if (!wsBuf) { - USER_PRINTLN(F("WS buffer allocation failed.")); + static unsigned long last_err_time = 0; + if (millis() - last_err_time > 300) { // WLEDMM limit to 3 messages per second + USER_PRINTF("WS buffer allocation failed (!wsBuf %u bytes).\n", bufSize); + last_err_time = millis(); + } errorFlag = ERR_LOW_WS_MEM; return false; //out of memory } From 3a506aded272cb8de323078d2aa94082fac497e7 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sat, 28 Sep 2024 02:12:53 +0200 Subject: [PATCH 028/232] trying to keep scrolling text readable the problem (partly solved) is that scrolling text does not erase any previous text, but simply paints pixels in addition. * add a "shadow" area around each letter, which is explicitly painted black * only possible when Trail=0, and overlay option selected --- wled00/FX.cpp | 5 +++-- wled00/FX.h | 2 +- wled00/FX_2Dfcn.cpp | 35 ++++++++++++++++++++++++++++------- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 1fbc74e6..ea5b7415 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -6371,7 +6371,7 @@ uint16_t mode_2Dscrollingtext(void) { if (SEGMENT.name) for (size_t i=0,j=0; i31 && SEGMENT.name[i]<128) text[j++] = SEGMENT.name[i]; const bool zero = strchr(text, '0') != nullptr; - if (!strlen(text) || !strncmp_P(text,PSTR("#F"),2) || !strncmp_P(text,PSTR("#P"),2) || !strncmp_P(text,PSTR("#DATE"),5) || !strncmp_P(text,PSTR("#DDMM"),5) || !strncmp_P(text,PSTR("#MMDD"),5) || !strncmp_P(text,PSTR("#TIME"),5) || !strncmp_P(text,PSTR("#HH"),3) || !strncmp_P(text,PSTR("#MM"),3)) { // fallback if empty segment name: display date and time + if (!strlen(text) || !strncmp_P(text,PSTR("#F"),2) || !strncmp_P(text,PSTR("#P"),2) || !strncmp_P(text,PSTR("#A"),2) || !strncmp_P(text,PSTR("#DATE"),5) || !strncmp_P(text,PSTR("#DDMM"),5) || !strncmp_P(text,PSTR("#MMDD"),5) || !strncmp_P(text,PSTR("#TIME"),5) || !strncmp_P(text,PSTR("#HH"),3) || !strncmp_P(text,PSTR("#MM"),3)) { // fallback if empty segment name: display date and time char sec[5]= {'\0'}; byte AmPmHour = hour(localTime); boolean isitAM = true; @@ -6404,6 +6404,7 @@ uint16_t mode_2Dscrollingtext(void) { SEGMENT.blendPixelColorXY(x, y, SEGCOLOR(1), 255 - (SEGMENT.custom1>>1)); } } + bool drawShadow = (SEGMENT.check2) && (SEGMENT.custom1 == 0); for (int i = 0; i < numberOfLetters; i++) { if (int(cols) - int(SEGENV.aux0) + letterWidth*(i+1) < 0) continue; // don't draw characters off-screen uint32_t col1 = SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0); @@ -6412,7 +6413,7 @@ uint16_t mode_2Dscrollingtext(void) { col1 = SEGCOLOR(0); col2 = SEGCOLOR(2); } - SEGMENT.drawCharacter(text[i], int(cols) - int(SEGENV.aux0) + letterWidth*i, yoffset, letterWidth, letterHeight, col1, col2); + SEGMENT.drawCharacter(text[i], int(cols) - int(SEGENV.aux0) + letterWidth*i, yoffset, letterWidth, letterHeight, col1, col2, drawShadow); } return FRAMETIME; diff --git a/wled00/FX.h b/wled00/FX.h index 505721c7..677005f2 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -761,7 +761,7 @@ typedef struct Segment { inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, bool soft = false, uint8_t depth = UINT8_MAX) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0), soft, depth); } // automatic inline void drawArc(unsigned x0, unsigned y0, int radius, uint32_t color, uint32_t fillColor = 0); inline void drawArc(unsigned x0, unsigned y0, int radius, CRGB color, CRGB fillColor = BLACK) { drawArc(x0, y0, radius, RGBW32(color.r,color.g,color.b,0), RGBW32(fillColor.r,fillColor.g,fillColor.b,0)); } // automatic inline - void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0); + void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0, bool drawShadow = false); inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0)); } // automatic inline void wu_pixel(uint32_t x, uint32_t y, CRGB c); //void blur1d(fract8 blur_amount); // blur all rows in 1 dimension diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index ea5c4307..7a090416 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -880,7 +880,7 @@ bool Segment::jsonToPixels(char * name, uint8_t fileNr) { // draws a raster font character on canvas // only supports: 4x6=24, 5x8=40, 5x12=60, 6x8=48 and 7x9=63 fonts ATM -void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2) { +void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2, bool drawShadow) { if (!isActive()) return; // not active if (chr < 32 || chr > 126) return; // only ASCII 32-126 supported chr -= 32; // align with font table entries @@ -890,6 +890,7 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, CRGB col = CRGB(color); CRGBPalette16 grad = CRGBPalette16(col, col2 ? CRGB(col2) : col); + uint32_t bgCol = SEGCOLOR(1); //if (w<5 || w>6 || h!=8) return; for (int i = 0; i= rows) break; // drawing off-screen uint8_t bits = 0; + uint8_t bits_up = 0; // WLEDMM this is the previous line: font[(chr * h) + i -1] switch (font) { - case 24: bits = pgm_read_byte_near(&console_font_4x6[(chr * h) + i]); break; // 5x8 font - case 40: bits = pgm_read_byte_near(&console_font_5x8[(chr * h) + i]); break; // 5x8 font - case 48: bits = pgm_read_byte_near(&console_font_6x8[(chr * h) + i]); break; // 6x8 font - case 63: bits = pgm_read_byte_near(&console_font_7x9[(chr * h) + i]); break; // 7x9 font - case 60: bits = pgm_read_byte_near(&console_font_5x12[(chr * h) + i]); break; // 5x12 font + case 24: bits = pgm_read_byte_near(&console_font_4x6[(chr * h) + i]); + if ((i>0) && drawShadow) bits_up = pgm_read_byte_near(&console_font_4x6[(chr * h) + i -1]); + break; // 5x8 font + case 40: bits = pgm_read_byte_near(&console_font_5x8[(chr * h) + i]); + if ((i>0) && drawShadow) bits_up = pgm_read_byte_near(&console_font_5x8[(chr * h) + i -1]); + break; // 5x8 font + case 48: bits = pgm_read_byte_near(&console_font_6x8[(chr * h) + i]); + if ((i>0) && drawShadow) bits_up = pgm_read_byte_near(&console_font_6x8[(chr * h) + i -1]); + break; // 6x8 font + case 63: bits = pgm_read_byte_near(&console_font_7x9[(chr * h) + i]); + if ((i>0) && drawShadow) bits_up = pgm_read_byte_near(&console_font_7x9[(chr * h) + i -1]); + break; // 7x9 font + case 60: bits = pgm_read_byte_near(&console_font_5x12[(chr * h) + i]); + if ((i>0) && drawShadow) bits_up = pgm_read_byte_near(&console_font_5x12[(chr * h) + i -1]); + break; // 5x12 font default: return; } col = ColorFromPalette(grad, (i+1)*255/h, 255, NOBLEND); for (int j = 0; j= 0 || x0 < cols) && ((bits>>(j+(8-w))) & 0x01)) { // bit set & drawing on-screen + if ((x0 >= 0) || (x0 < cols)) { + if ((bits>>(j+(8-w))) & 0x01) { // bit set & drawing on-screen setPixelColorXY(x0, y0, col); + } else { + if (drawShadow) { + // WLEDMM + if ((j < (w-1)) && (bits>>(j+(8-w) +1)) & 0x01) setPixelColorXY(x0, y0, bgCol); // blank when pixel to the right is set + else if ((j > 0) && (bits>>(j+(8-w) -1)) & 0x01) setPixelColorXY(x0, y0, bgCol);// blank when pixel to the left is set + else if ((bits_up>>(j+(8-w))) & 0x01) setPixelColorXY(x0, y0, bgCol); // blank when pixel above is set + } + } } } } From 8338b58b8846954f92ad7196edcc81b2cc993457 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sat, 28 Sep 2024 02:15:41 +0200 Subject: [PATCH 029/232] HUB75 memory usage info on serial --- wled00/bus_manager.cpp | 15 +++++++++++---- wled00/wled.cpp | 1 + 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 963409c6..fa205c44 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -523,6 +523,7 @@ void BusNetwork::cleanup() { #warning "HUB75 driver enabled (experimental)" BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { + size_t lastHeap = ESP.getFreeHeap(); _valid = false; fourScanPanel = nullptr; @@ -540,14 +541,14 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh #if defined(CONFIG_IDF_TARGET_ESP32S3) && defined(BOARD_HAS_PSRAM) if(bc.pins[0] > 4) { - USER_PRINT("WARNING, chain limited to 4"); + USER_PRINTLN("WARNING, chain limited to 4"); } # else // Disable this check if you are want to try bigger setups and accept you // might need to do full erase to recover from memory relayed boot-loop if you push too far if(mxconfig.mx_height >= 64 && (bc.pins[0] > 1)) { - USER_PRINT("WARNING, only single panel can be used of 64 pixel boards due to memory"); - mxconfig.chain_length = 1; + USER_PRINTLN("WARNING, only single panel can be used of 64 pixel boards due to memory"); + //mxconfig.chain_length = 1; } #endif @@ -730,7 +731,8 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh #endif - USER_PRINTF("MatrixPanel_I2S_DMA config - %ux%u length: %u\n", mxconfig.mx_width, mxconfig.mx_height, mxconfig.chain_length); + USER_PRINTF("MatrixPanel_I2S_DMA config - %ux%u (type %u) length: %u, %u bits/pixel.\n", mxconfig.mx_width, mxconfig.mx_height, bc.type, mxconfig.chain_length, mxconfig.getPixelColorDepthBits() * 3); + DEBUG_PRINT(F("Free heap: ")); DEBUG_PRINTLN(ESP.getFreeHeap()); lastHeap = ESP.getFreeHeap(); // OK, now we can create our matrix object display = new MatrixPanel_I2S_DMA(mxconfig); @@ -762,13 +764,16 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh _bri = 25; delay(24); // experimental + DEBUG_PRINT(F("heap usage: ")); DEBUG_PRINTLN(lastHeap - ESP.getFreeHeap()); // Allocate memory and start DMA display if( not display->begin() ) { USER_PRINTLN("****** MatrixPanel_I2S_DMA !KABOOM! I2S memory allocation failed ***********"); + USER_PRINT(F("heap usage: ")); USER_PRINTLN(lastHeap - ESP.getFreeHeap()); return; } else { USER_PRINTLN("MatrixPanel_I2S_DMA begin ok"); + USER_PRINT(F("heap usage: ")); USER_PRINTLN(lastHeap - ESP.getFreeHeap()); delay(18); // experiment - give the driver a moment (~ one full frame @ 60hz) to settle _valid = true; display->clearScreen(); // initially clear the screen buffer @@ -784,6 +789,7 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh delete display; display = nullptr; _valid = false; USER_PRINTLN(F("MatrixPanel_I2S_DMA not started - not enough memory for dirty bits!")); + USER_PRINT(F("heap usage: ")); USER_PRINTLN(lastHeap - ESP.getFreeHeap()); return; // fail is we cannot get memory for the buffer } setBitArray(_ledsDirty, _len, false); // reset dirty bits @@ -837,6 +843,7 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh USER_PRINT((_ledBuffer? _len*sizeof(CRGB) :0) + (_ledsDirty? getBitArrayBytes(_len) :0)); USER_PRINTLN(F(" bytes.")); } + USER_PRINT(F("heap usage: ")); USER_PRINTLN(lastHeap - ESP.getFreeHeap()); } void __attribute__((hot)) BusHub75Matrix::setPixelColor(uint16_t pix, uint32_t c) { diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 41e04d1a..5858f800 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -876,6 +876,7 @@ void WLED::setup() USER_PRINTLN(F("\n")); #endif + USER_PRINT(F("Free heap ")); USER_PRINTLN(ESP.getFreeHeap());USER_PRINTLN(); USER_PRINTLN(F("WLED initialization done.\n")); delay(50); // repeat Ada prompt From 0555b8e5f258415e7d038689c6636cbd659bdffe Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sat, 28 Sep 2024 02:18:24 +0200 Subject: [PATCH 030/232] HUB75: avoid allocating ledMap based on an idea by @troyhacks : fight heap fragmentation by avoiding to allocate ledMap when not needed. --- wled00/FX_2Dfcn.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 7a090416..73c7bf77 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -66,9 +66,26 @@ void WS2812FX::setUpMatrix() { USER_PRINTF("setUpMatrix %d x %d\n", Segment::maxWidth, Segment::maxHeight); + // WLEDMM check if mapping table is necessary (avoiding heap fragmentation) +#if defined(WLED_ENABLE_HUB75MATRIX) + bool needLedMap = (loadedLedmap >0); // ledmap loaded + needLedMap |= WLED_FS.exists(F("/2d-gaps.json")); // gapFile found + needLedMap |= panel.size() > 1; // 2D config: more than one panel + if (panel.size() == 1) { + Panel &p = panel[0]; + needLedMap |= p.serpentine; // panel serpentine + needLedMap |= p.vertical; // panel not horizotal + needLedMap |= p.bottomStart | p.rightStart; // panel not top left, or not left->light + needLedMap |= (p.xOffset > 0) || (p.yOffset > 0); // panel does not start at (0,0) + } +#else + bool needLedMap = true; // always use ledMaps on non-HUB75 builds +#endif + //WLEDMM recreate customMappingTable if more space needed if (Segment::maxWidth * Segment::maxHeight > customMappingTableSize) { size_t size = max(ledmapMaxSize, size_t(Segment::maxWidth * Segment::maxHeight)); // TroyHacks + if (!needLedMap) size = 0; // softhack007 USER_PRINTF("setupmatrix customMappingTable alloc %d from %d\n", size, customMappingTableSize); //if (customMappingTable != nullptr) delete[] customMappingTable; //customMappingTable = new uint16_t[size]; @@ -88,8 +105,9 @@ void WS2812FX::setUpMatrix() { if (customMappingTable != nullptr) customMappingTableSize = size; } - if (customMappingTable != nullptr) { + if ((customMappingTable != nullptr) || (!needLedMap)) { // softhack007 customMappingSize = Segment::maxWidth * Segment::maxHeight; + if (!needLedMap) customMappingSize = 0; // softhack007 // fill with empty in case we don't fill the entire matrix for (size_t i = 0; i< customMappingTableSize; i++) { //WLEDMM use customMappingTableSize @@ -130,6 +148,7 @@ void WS2812FX::setUpMatrix() { releaseJSONBufferLock(); } + if (needLedMap && customMappingTable != nullptr) { // softhack007 uint16_t x, y, pix=0; //pixel for (size_t pan = 0; pan < panel.size(); pan++) { Panel &p = panel[pan]; @@ -146,6 +165,7 @@ void WS2812FX::setUpMatrix() { } } } + } // delete gap array as we no longer need it if (gapTable) {delete[] gapTable; gapTable=nullptr;} // softhack prevent dangling pointer From b245112d2a80b0df5534fc957be09f6799bf374b Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sat, 28 Sep 2024 02:19:53 +0200 Subject: [PATCH 031/232] some drawing speedups * speedups for addPixelColorXY, fadePixelColorXY, fadeToBlackBy --- wled00/FX.cpp | 3 +++ wled00/FX_2Dfcn.cpp | 16 +++++++++------- wled00/FX_fcn.cpp | 4 +++- wled00/wled.h | 2 +- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index ea5b7415..9e7a25cf 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -37,6 +37,9 @@ #define indexToVStrip(index, stripNr) ((index) | (int((stripNr)+1)<<16)) +// WLEDMM replace abs8 by abs, as abs8 does not work for numbers >127 +#define abs8(x) abs(x) + // effect utility functions static uint8_t sin_gap(uint16_t in) { if (in & 0x100) return 0; diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 73c7bf77..9ac62e1d 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -463,8 +463,9 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) const { if (reverse ) x = virtualWidth() - x - 1; if (reverse_y) y = virtualHeight() - y - 1; if (transpose) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed - x *= groupLength(); // expand to physical pixels - y *= groupLength(); // expand to physical pixels + const uint_fast16_t groupLength_ = groupLength(); // WLEDMM small optimization + x *= groupLength_; // expand to physical pixels + y *= groupLength_; // expand to physical pixels if (x >= width() || y >= height()) return 0; return strip.getPixelColorXY(start + x, startY + y); } @@ -479,15 +480,16 @@ void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t void IRAM_ATTR_YN Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) { // if (!isActive()) return; // not active //WLEDMM sanity check is repeated in getPixelColorXY / setPixelColorXY // if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit //WLEDMM - uint32_t col = getPixelColorXY(x,y); - col = color_add(col, color, fast); - setPixelColorXY(x, y, col); + uint32_t oldCol = getPixelColorXY(x,y); + uint32_t col = color_add(oldCol, color, fast); + if (col != oldCol) setPixelColorXY(x, y, col); } void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { // if (!isActive()) return; // not active //WLEDMM sanity check is repeated in getPixelColorXY / setPixelColorXY - CRGB pix = CRGB(getPixelColorXY(x,y)).nscale8_video(fade); - setPixelColorXY(x, y, pix); + CRGB oldPix = CRGB(getPixelColorXY(x,y)); + CRGB pix = oldPix.nscale8_video(fade); + if (pix != oldPix) setPixelColorXY(int(x), int(y), pix); } // blurRow: perform a blur on a row of a rectangular matrix diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 9e2368ab..51087562 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1510,7 +1510,9 @@ void __attribute__((hot)) Segment::fadeToBlackBy(uint8_t fadeBy) { for (unsigned y = 0; y < rows; y++) for (unsigned x = 0; x < cols; x++) { uint32_t cc = getPixelColorXY(int(x),int(y)); // WLEDMM avoid RGBW32 -> CRGB -> RGBW32 conversion uint32_t cc2 = color_fade(cc, scaledown); // fade - //if (cc2 != cc) // WLEDMM only re-paint if faded color is different - disabled - causes problem with text overlay +#ifdef WLEDMM_FASTPATH + if (cc2 != cc) // WLEDMM only re-paint if faded color is different - normally disabled - causes problem with text overlay +#endif setPixelColorXY(int(x), int(y), cc2); } } else { diff --git a/wled00/wled.h b/wled00/wled.h index 74deeace..33174183 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2409260 +#define VERSION 2409280 // 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 2aa0d4703687c5c61d8786bb9986753c483b61da Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sat, 28 Sep 2024 02:43:12 +0200 Subject: [PATCH 032/232] adding getPixelColorRestored() similar to getPixelColor, but returns the original pixel without brightness adjustments. * getPixelColorRestored is used by segment::sPC and segment::gPC * getPixelColor is still used by ABL (auto brightness limiter) --- wled00/FX.h | 3 +++ wled00/FX_2Dfcn.cpp | 13 ++++++++++++- wled00/FX_fcn.cpp | 8 +++++++- wled00/bus_manager.cpp | 29 +++++++++++++++++++++++++++++ wled00/bus_manager.h | 16 ++++++++++++++++ wled00/ws.cpp | 1 + 6 files changed, 68 insertions(+), 2 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index 677005f2..4e408254 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -976,6 +976,7 @@ class WS2812FX { // 96 bytes now, timebase; uint32_t __attribute__((pure)) getPixelColor(uint_fast16_t) const; // WLEDMM attribute pure = does not have side-effects + uint32_t __attribute__((pure)) getPixelColorRestored(uint_fast16_t i) const;// WLEDMM gets the original color from the driver (without downscaling by _bri) inline uint32_t getLastShow(void) const { return _lastShow; } inline uint32_t segColor(uint8_t i) const { return _colors_t[i]; } @@ -1068,6 +1069,8 @@ class WS2812FX { // 96 bytes std::vector _segments; friend class Segment; + uint32_t getPixelColorXYRestored(uint16_t x, uint16_t y) const; // WLEDMM gets the original color from the driver (without downscaling by _bri) + private: uint16_t _length; uint8_t _brightness; diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 9ac62e1d..0803ad1a 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -249,6 +249,17 @@ uint32_t __attribute__((hot)) WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) return busses.getPixelColor(index); } +uint32_t __attribute__((hot)) WS2812FX::getPixelColorXYRestored(uint16_t x, uint16_t y) const { // WLEDMM gets the original color from the driver (without downscaling by _bri) + #ifndef WLED_DISABLE_2D + uint_fast16_t index = (y * Segment::maxWidth + x); //WLEDMM: use fast types + #else + uint16_t index = x; + #endif + if (index < customMappingSize) index = customMappingTable[index]; + if (index >= _length) return 0; + return busses.getPixelColorRestored(index); +} + /////////////////////////////////////////////////////////// // Segment:: routines /////////////////////////////////////////////////////////// @@ -467,7 +478,7 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) const { x *= groupLength_; // expand to physical pixels y *= groupLength_; // expand to physical pixels if (x >= width() || y >= height()) return 0; - return strip.getPixelColorXY(start + x, startY + y); + return strip.getPixelColorXYRestored(start + x, startY + y); } // Blends the specified color with the existing pixel color. diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 51087562..f2ed3e20 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1304,7 +1304,7 @@ uint32_t __attribute__((hot)) Segment::getPixelColor(int i) const if (offset < INT16_MAX) i += offset; // WLEDMM if ((i >= stop) && (stop>0)) i -= length(); // WLEDMM avoid negative index (stop = 0 is a possible value) if (i<0) i=0; // WLEDMM just to be 100% sure - return strip.getPixelColor(i); + return strip.getPixelColorRestored(i); } uint8_t Segment::differs(Segment& b) const { @@ -1943,6 +1943,12 @@ uint32_t WS2812FX::getPixelColor(uint_fast16_t i) const // WLEDMM fast int types return busses.getPixelColor(i); } +uint32_t WS2812FX::getPixelColorRestored(uint_fast16_t i) const // WLEDMM gets the original color from the driver (without downscaling by _bri) +{ + if (i < customMappingSize) i = customMappingTable[i]; + if (i >= _length) return 0; + return busses.getPixelColorRestored(i); +} //DISCLAIMER //The following function attemps to calculate the current LED power usage, diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index fa205c44..66a417e8 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -890,6 +890,14 @@ uint32_t BusHub75Matrix::getPixelColor(uint16_t pix) const { return getBitFromArray(_ledsDirty, pix) ? DARKGREY: BLACK; // just a hack - we only know if the pixel is black or not } +uint32_t __attribute__((hot)) BusHub75Matrix::getPixelColorRestored(uint16_t pix) const { + if (!_valid || pix >= _len) return BLACK; + if (_ledBuffer) + return uint32_t(_ledBuffer[pix]) & 0x00FFFFFF; + else + return getBitFromArray(_ledsDirty, pix) ? DARKGREY: BLACK; // just a hack - we only know if the pixel is black or not +} + void BusHub75Matrix::setBrightness(uint8_t b, bool immediate) { _bri = b; // if (_bri > 238) _bri=238; // not strictly needed. Enable this line if you see glitches at highest brightness. @@ -1106,6 +1114,27 @@ uint32_t IRAM_ATTR __attribute__((hot)) BusManager::getPixelColor(uint_fast16_t return 0; } +uint32_t IRAM_ATTR __attribute__((hot)) BusManager::getPixelColorRestored(uint_fast16_t pix) { // WLEDMM uses bus::getPixelColorRestored() + if ((pix >= laststart) && (pix < lastend ) && (lastBus != nullptr)) { + // WLEDMM same bus as last time - no need to search again + return lastBus->getPixelColorRestored(pix - laststart); + } + + for (uint_fast8_t i = 0; i < numBusses; i++) { + Bus* b = busses[i]; + uint_fast16_t bstart = b->getStart(); + if (pix < bstart || pix >= bstart + b->getLength()) continue; + else { + // WLEDMM remember last Bus we took + lastBus = b; + laststart = bstart; + lastend = bstart + b->getLength(); + return b->getPixelColorRestored(pix - bstart); + } + } + return 0; +} + bool BusManager::canAllShow() const { for (uint8_t i = 0; i < numBusses; i++) { if (!busses[i]->canShow()) return false; diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 80488b6a..677e71de 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -135,6 +135,7 @@ class Bus { virtual void setStatusPixel(uint32_t c) {} virtual void setPixelColor(uint16_t pix, uint32_t c) = 0; virtual uint32_t getPixelColor(uint16_t pix) const { return 0; } + virtual uint32_t getPixelColorRestored(uint16_t pix) const { return restore_Color_Lossy(getPixelColor(pix), _bri); } // override in case your bus has a lossless buffer (HUB75, FastLED, Art-Net) virtual void setBrightness(uint8_t b, bool immediate=false) { _bri = b; } virtual void cleanup() = 0; virtual uint8_t getPins(uint8_t* pinArray) const { return 0; } @@ -183,6 +184,17 @@ class Bus { inline static void setGlobalAWMode(uint8_t m) { if (m < 5) _gAWM = m; else _gAWM = AW_GLOBAL_DISABLED; } inline static uint8_t getGlobalAWMode() { return _gAWM; } + inline uint32_t restore_Color_Lossy(uint32_t c, uint8_t restoreBri) const { // shamelessly grabbed from upstream, who grabbed from NPB, who .. + if (restoreBri < 255) { + uint8_t* chan = (uint8_t*) &c; + for (uint_fast8_t i=0; i<4; i++) { + uint_fast16_t val = chan[i]; + chan[i] = ((val << 8) + restoreBri) / (restoreBri + 1); //adding _bri slightly improves recovery / stops degradation on re-scale + } + } + return c; + } + bool reversed = false; protected: @@ -296,6 +308,7 @@ class BusOnOff : public Bus { void setPixelColor(uint16_t pix, uint32_t c); uint32_t getPixelColor(uint16_t pix) const; + uint32_t getPixelColorRestored(uint16_t pix) const override { return getPixelColor(pix);} // WLEDMM BusOnOff ignores brightness void show(); @@ -326,6 +339,7 @@ class BusNetwork : public Bus { void setPixelColor(uint16_t pix, uint32_t c); uint32_t __attribute__((pure)) getPixelColor(uint16_t pix) const; // WLEDMM attribute added + uint32_t __attribute__((pure)) getPixelColorRestored(uint16_t pix) const override { return getPixelColor(pix);} // WLEDMM BusNetwork ignores brightness void show(); @@ -367,6 +381,7 @@ class BusHub75Matrix : public Bus { void setPixelColor(uint16_t pix, uint32_t c) override; uint32_t getPixelColor(uint16_t pix) const override; + uint32_t getPixelColorRestored(uint16_t pix) const override; // lossless getPixelColor supported void show(void) override; @@ -418,6 +433,7 @@ class BusManager { void setSegmentCCT(int16_t cct, bool allowWBCorrection = false); uint32_t __attribute__((pure)) getPixelColor(uint_fast16_t pix); // WLEDMM attribute added + uint32_t __attribute__((pure)) getPixelColorRestored(uint_fast16_t pix); // WLEDMM bool canAllShow() const; diff --git a/wled00/ws.cpp b/wled00/ws.cpp index 85bad64d..f6c1e862 100644 --- a/wled00/ws.cpp +++ b/wled00/ws.cpp @@ -251,6 +251,7 @@ static bool sendLiveLedsWs(uint32_t wsClient) // WLEDMM added "static" } #endif uint32_t c = restoreColorLossy(strip.getPixelColor(i), stripBrightness); // WLEDMM full bright preview - does _not_ recover ABL reductions + //uint32_t c = strip.getPixelColorRestored(i); // WLEDMM begin: preview with color gamma correction if (gammaCorrectPreview) { uint8_t w = W(c); // not sure why, but it looks better if using "white" without corrections From 59ca792c9d7bc6b90f04ae640c6e2827ea826f15 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sat, 28 Sep 2024 02:45:16 +0200 Subject: [PATCH 033/232] HUB75: trade bit depth against memory needs gradually reduce bitdepts on esp32 --- wled00/bus_manager.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 66a417e8..cb3d8fac 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -579,6 +579,14 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh break; } +#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2)// classic esp32, or esp32-s2: reduce bitdepth for large panels + if (mxconfig.mx_height >= 64) { + if (mxconfig.chain_length * mxconfig.mx_width > 192) mxconfig.setPixelColorDepthBits(3); + else if (mxconfig.chain_length * mxconfig.mx_width > 64) mxconfig.setPixelColorDepthBits(4); + else mxconfig.setPixelColorDepthBits(8); + } else mxconfig.setPixelColorDepthBits(8); +#endif + #if defined(ARDUINO_ADAFRUIT_MATRIXPORTAL_ESP32S3) // MatrixPortal ESP32-S3 // https://www.adafruit.com/product/5778 From 559d86256c213c8eccb8097a39651141c2741402 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sat, 28 Sep 2024 02:56:46 +0200 Subject: [PATCH 034/232] live preview speedup --- wled00/ws.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/wled00/ws.cpp b/wled00/ws.cpp index f6c1e862..5a7f34eb 100644 --- a/wled00/ws.cpp +++ b/wled00/ws.cpp @@ -10,9 +10,11 @@ static volatile unsigned long wsLastLiveTime = 0; // WLEDMM //uint8_t* wsFrameBuffer = nullptr; #if !defined(ARDUINO_ARCH_ESP32) || defined(WLEDMM_FASTPATH) // WLEDMM -#define WS_LIVE_INTERVAL 120 +#define WS_LIVE_INTERVAL_MAX 120 +#define WS_LIVE_INTERVAL_MIN 25 #else -#define WS_LIVE_INTERVAL 80 +#define WS_LIVE_INTERVAL_MAX 80 +#define WS_LIVE_INTERVAL_MIN 40 #endif void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) @@ -274,7 +276,7 @@ static bool sendLiveLedsWs(uint32_t wsClient) // WLEDMM added "static" void handleWs() { - if ((millis() - wsLastLiveTime) > (unsigned long)(max((strip.getLengthTotal()/20), WS_LIVE_INTERVAL))) //WLEDMM dynamic nr of peek frames per second + if ((millis() - wsLastLiveTime) > (unsigned long)(max(WS_LIVE_INTERVAL_MIN, min((strip.getLengthTotal()/80), WS_LIVE_INTERVAL_MAX)))) //WLEDMM dynamic nr of peek frames per second { #ifdef ESP8266 ws.cleanupClients(3); From b650c15997193a8bf2372b37145f2ce08afcae63 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sun, 29 Sep 2024 02:01:57 +0200 Subject: [PATCH 035/232] platformio.ini updates for HUB75 * -D WLEDMM_SLOWPATH prevents using I2S#1 for ws281x leds - matrixPortal S3: minor update to use more PSRAM (and keep more RAM availeable) --- platformio.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platformio.ini b/platformio.ini index 57f0337d..07c03129 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1071,6 +1071,7 @@ HUB75_build_flags = -D NO_FAST_FUNCTIONS ;; If you are not using AdafruitGFX than you probably do not need this either, save memory/code size -D NO_CIE1931 ;; Do not use LED brightness compensation described in CIE 1931. We use FastLED dimming already -D S3_LCD_DIV_NUM=20 ;; Attempt to fix wifi performance issue when panel active with S3 chips + -D WLEDMM_SLOWPATH ;; WLEDMM: do not use I2S for driving ws2812 LEDs (HUB75 driver needs I2S#1) HUB75_lib_deps = https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA.git#aa28e2a9 ;; S3_LCD_DIV_NUM fix HUB75_lib_ignore = ESP32 HUB75 LED MATRIX PANEL DMA Display ;; to remove the HUB75 lib dependancy (saves a few bytes) @@ -2667,6 +2668,8 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden -D LOLIN_WIFI_FIX ;; try this in case Wifi does not work -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0 -D WLED_USE_PSRAM -DBOARD_HAS_PSRAM ; tells WLED that PSRAM shall be used + -D WLED_USE_PSRAM_JSON -DALL_JSON_TO_PSRAM ; WLEDMM --> force all JSON stuff into PSRAM; gives more free heap + ; -DCONFIG_MBEDTLS_DYNAMIC_BUFFER=1 ;; optional - seems to move more buffers into PSRAM ${common_mm.HUB75_build_flags} -D DEFAULT_LED_TYPE=101 lib_deps = ${esp32s3.lib_deps} ${common_mm.lib_deps_S} ;; ;; do not include ${esp32.lib_depsV4} !!!! From a0f07cabcb82e5a3e8cb65d41e0eb2bf88e19505 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sun, 29 Sep 2024 14:59:54 +0200 Subject: [PATCH 036/232] make a few getters "constexpr" as they just return a constant constexpr ensures that the compiler will replace the "getter" call with the value return, saving function call overhead. based on an idea from @blazoncek --- wled00/FX.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index 4e408254..04eabe70 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -951,13 +951,13 @@ class WS2812FX { // 96 bytes setPixelSegment(uint8_t n); inline uint8_t getBrightness(void) const { return _brightness; } - inline uint8_t getMaxSegments(void) const { return MAX_NUM_SEGMENTS; } // returns maximum number of supported segments (fixed value) inline uint8_t getSegmentsNum(void) const { return _segments.size(); } // returns currently present segments inline uint8_t getCurrSegmentId(void) const { return _segment_index; } inline uint8_t getMainSegmentId(void) const { return _mainSegment; } - inline uint8_t getPaletteCount() const { return 13 + GRADIENT_PALETTE_COUNT; } // will only return built-in palette count inline uint8_t getTargetFps() const { return _targetFps; } inline uint8_t getModeCount() const { return _modeCount; } + inline static constexpr uint8_t getMaxSegments(void) { return MAX_NUM_SEGMENTS; } // returns maximum number of supported segments (fixed value) + inline static constexpr uint8_t getPaletteCount() { return 13 + GRADIENT_PALETTE_COUNT; } // will only return built-in palette count uint16_t ablMilliampsMax, From 7e9474e78ce9caabc863eb8cb0ed62952fd152d7 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sun, 29 Sep 2024 17:51:44 +0200 Subject: [PATCH 037/232] depricate sPC(float, float) the function is already removed in upstream. You can use wu_pixel() instead, which is much faster and more accurate. --- wled00/FX.cpp | 5 ++++- wled00/FX_2Dfcn.cpp | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 9e7a25cf..931f74b2 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5539,7 +5539,10 @@ uint16_t mode_2DLissajous(void) { // By: Andrew Tuline float ylocn = float(cos8(phase/2 + i*2)) / 255.0f; //SEGMENT.setPixelColorXY(xlocn, ylocn, SEGMENT.color_from_palette(strip.now/100+i, false, PALETTE_SOLID_WRAP, 0)); // draw pixel with anti-aliasing unsigned palIndex = (256*ylocn) + phase/2 + (i* SEGMENT.speed)/64; - SEGMENT.setPixelColorXY(xlocn, ylocn, SEGMENT.color_from_palette(palIndex, false, PALETTE_SOLID_WRAP, 0)); // draw pixel with anti-aliasing - color follows rotation + //SEGMENT.setPixelColorXY(xlocn, ylocn, SEGMENT.color_from_palette(palIndex, false, PALETTE_SOLID_WRAP, 0)); // draw pixel with anti-aliasing - color follows rotation + // WLEDMM wu_pixel is 50% faster, and still lokks better + SEGMENT.wu_pixel(uint32_t(xlocn * (cols <<8)), uint32_t(ylocn * (rows <<8)), + CRGB(SEGMENT.color_from_palette(palIndex, false, PALETTE_SOLID_WRAP, 0))); } } else for (int i=0; i < 256; i ++) { diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 0803ad1a..a277c521 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -413,6 +413,8 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM: } } +#if 0 +// WLEDMM setPixelColorXY(float x, float y, uint32_t col, ..) is depricated. use wu_pixel(x,y,col) instead. // anti-aliased version of setPixelColorXY() void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa, bool fast) // WLEDMM some speedups due to fast int and faster sqrt16 { @@ -463,6 +465,7 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa, bool fast setPixelColorXY(uint16_t(roundf(fX)), uint16_t(roundf(fY)), col); } } +#endif // returns RGBW values of pixel uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) const { From 3110b0a4769e3f8206258f0a8e2511b03773809e Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sun, 29 Sep 2024 18:02:39 +0200 Subject: [PATCH 038/232] fix for XL builds some usermod still seems to used sPC(float, float, c). > Linking .pio/build/esp32_4MB_XL/firmware.elf .pio/build/esp32_4MB_XL/src/usermods_list.cpp.o:(.literal._ZN7Frame3DD5Ev[Frame3D::~Frame3D()]+0x4): undefined reference to `Segment::setPixelColorXY(float, float, unsigned int, bool, bool)' --- wled00/FX_2Dfcn.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index a277c521..42865933 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -413,7 +413,6 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM: } } -#if 0 // WLEDMM setPixelColorXY(float x, float y, uint32_t col, ..) is depricated. use wu_pixel(x,y,col) instead. // anti-aliased version of setPixelColorXY() void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa, bool fast) // WLEDMM some speedups due to fast int and faster sqrt16 @@ -421,6 +420,7 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa, bool fast if (Segment::maxHeight==1) return; // not a matrix set-up if (x<0.0f || x>1.0f || y<0.0f || y>1.0f) return; // not normalized +#if 0 // depricated const uint_fast16_t cols = virtualWidth(); const uint_fast16_t rows = virtualHeight(); @@ -464,8 +464,13 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa, bool fast } else { setPixelColorXY(uint16_t(roundf(fX)), uint16_t(roundf(fY)), col); } -} + +#else // replacement using wu_pixel + unsigned px = x * (virtualWidth() <<8); + unsigned py = y * (virtualHeight() <<8); + wu_pixel(px, py, CRGB(col)); #endif +} // returns RGBW values of pixel uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) const { From 63ff7205d61c4bdf7e9b952e392222e46b93e1d6 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sun, 29 Sep 2024 18:37:18 +0200 Subject: [PATCH 039/232] wu_pixel small optimization 5% faster --- wled00/FX_2Dfcn.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 42865933..03919f8e 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -987,12 +987,14 @@ void Segment::wu_pixel(uint32_t x, uint32_t y, CRGB c) { //awesome wu_pixel WU_WEIGHT(ix, yy), WU_WEIGHT(xx, yy)}; // multiply the intensities by the colour, and saturating-add them to the pixels for (int i = 0; i < 4; i++) { - CRGB led = getPixelColorXY((x >> 8) + (i & 1), (y >> 8) + ((i >> 1) & 1)); + int wu_x = (x >> 8) + (i & 1); // WLEDMM precalculate x + int wu_y = (y >> 8) + ((i >> 1) & 1); // WLEDMM precalculate y + CRGB led = getPixelColorXY(wu_x, wu_y); CRGB oldLed = led; led.r = qadd8(led.r, c.r * wu[i] >> 8); led.g = qadd8(led.g, c.g * wu[i] >> 8); led.b = qadd8(led.b, c.b * wu[i] >> 8); - if (led != oldLed) setPixelColorXY(int((x >> 8) + (i & 1)), int((y >> 8) + ((i >> 1) & 1)), led); // WLEDMM don't repaint same color + if (led != oldLed) setPixelColorXY(wu_x, wu_y, led); // WLEDMM don't repaint same color } } #undef WU_WEIGHT From 813b3705c1fb823851e8293e575c7d2d89e0d3c2 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 30 Sep 2024 17:33:25 +0200 Subject: [PATCH 040/232] Akemi bugfix for panel width > 32 due to a math accident, Akemi did not show proper GEQ bands in its hands when width>32. --- wled00/FX.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 931f74b2..2d44708d 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -7998,11 +7998,12 @@ uint16_t mode_2DAkemi(void) { //add geq left and right if (um_data) { - for (int x=0; x < cols/8; x++) { - uint16_t band = x * cols/8; + int xMax = cols/8; + for (int x=0; x < xMax; x++) { + size_t band = map2(x, 0, max(xMax,4), 0, 15); // map 0..cols/8 to 16 GEQ bands + CRGB color = SEGMENT.color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0); band = constrain(band, 0, 15); uint16_t barHeight = map(fftResult[band], 0, 255, 0, 17*rows/32); - CRGB color = SEGMENT.color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0); for (int y=0; y < barHeight; y++) { SEGMENT.setPixelColorXY(x, rows/2-y, color); From b0a7330a310ba1cf58b0f4b6715630ab4a3e6b23 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 30 Sep 2024 17:35:14 +0200 Subject: [PATCH 041/232] Akemi memory saver lossless getPixelColor buffer is not needed for Akemi. --- wled00/FX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 2d44708d..c2b66d83 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -7957,7 +7957,7 @@ uint16_t mode_2DAkemi(void) { const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - if (SEGENV.call == 0) {SEGMENT.setUpLeds(); SEGMENT.fill(BLACK);} + if (SEGENV.call == 0) {SEGMENT.fill(BLACK);} uint16_t counter = (strip.now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF; counter = counter >> 8; From 9ed787c0860bb0297a25a962cc83dcc92a9db0ff Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 30 Sep 2024 18:35:14 +0200 Subject: [PATCH 042/232] colored burst effect bugfix (swapped XY dimensions) fixing a bug where width and height got swapped (visible on non-square panels) --- wled00/FX.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index c2b66d83..494d03d7 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -4982,8 +4982,8 @@ uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.so SEGMENT.fadeToBlackBy(40); for (size_t i = 0; i < numLines; i++) { byte x1 = beatsin8(2 + SEGMENT.speed/16, 0, (cols - 1)); - byte x2 = beatsin8(1 + SEGMENT.speed/16, 0, (cols - 1)); - byte y1 = beatsin8(5 + SEGMENT.speed/16, 0, (rows - 1), 0, i * 24); + byte x2 = beatsin8(1 + SEGMENT.speed/16, 0, (rows - 1)); + byte y1 = beatsin8(5 + SEGMENT.speed/16, 0, (cols - 1), 0, i * 24); byte y2 = beatsin8(3 + SEGMENT.speed/16, 0, (rows - 1), 0, i * 48 + 64); CRGB color = ColorFromPalette(SEGPALETTE, i * 255 / numLines + (SEGENV.aux0&0xFF), 255, LINEARBLEND); From 283a7284b491bf3d11f663b25d86bb17f20549b3 Mon Sep 17 00:00:00 2001 From: Troy <5659019+troyhacks@users.noreply.github.com> Date: Wed, 2 Oct 2024 09:15:46 -0400 Subject: [PATCH 043/232] ES8311 support added for the ESP32-P4 EV board with mic. --- usermods/audioreactive/audio_reactive.h | 16 +++- usermods/audioreactive/audio_source.h | 100 ++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 1c46e33a..cbf21523 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -2022,7 +2022,16 @@ class AudioReactive : public Usermod { if ((sclPin >= 0) && (i2c_scl < 0)) i2c_scl = sclPin; if (i2c_sda >= 0) sdaPin = -1; // -1 = use global if (i2c_scl >= 0) sclPin = -1; - + case 9: + DEBUGSR_PRINTLN(F("AR: ES8311 Source (Mic)")); + audioSource = new ES8311Source(SAMPLE_RATE, BLOCK_SIZE, 1.0f); + //useInputFilter = 0; // to disable low-cut software filtering and restore previous behaviour + delay(100); + // WLEDMM align global pins + if ((sdaPin >= 0) && (i2c_sda < 0)) i2c_sda = sdaPin; // copy usermod prefs into globals (if globals not defined) + if ((sclPin >= 0) && (i2c_scl < 0)) i2c_scl = sclPin; + if (i2c_sda >= 0) sdaPin = -1; // -1 = use global + if (i2c_scl >= 0) sclPin = -1; if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin); break; @@ -2934,6 +2943,11 @@ class AudioReactive : public Usermod { #else oappend(SET_F("addOption(dd,'AC101 ☾',8);")); #endif + #if SR_DMTYPE==9 + oappend(SET_F("addOption(dd,'ES8311 ☾ (⎌)',9);")); + #else + oappend(SET_F("addOption(dd,'ES8311 ☾',9);")); + #endif #ifdef SR_SQUELCH oappend(SET_F("addInfo(ux+':config:squelch',1,'⎌ ")); oappendi(SR_SQUELCH); oappend("');"); // 0 is field type, 1 is actual field #endif diff --git a/usermods/audioreactive/audio_source.h b/usermods/audioreactive/audio_source.h index 77111446..61f37c4f 100644 --- a/usermods/audioreactive/audio_source.h +++ b/usermods/audioreactive/audio_source.h @@ -640,6 +640,106 @@ class ES8388Source : public I2SSource { }; +/* ES8311 Sound Module + This is an I2S sound processing unit that requires initialization over + I2C before I2S data can be received. +*/ +class ES8311Source : public I2SSource { + private: + // I2C initialization functions for es8311 + void _es8311I2cBegin() { + Wire.setClock(100000); + } + + void _es8311I2cWrite(uint8_t reg, uint8_t val) { + #ifndef ES8311_ADDR + #define ES8311_ADDR 0x18 // default address is... foggy + #endif + Wire.beginTransmission(ES8311_ADDR); + Wire.write((uint8_t)reg); + Wire.write((uint8_t)val); + uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK + if (i2cErr != 0) { + DEBUGSR_PRINTF("AR: ES8311 I2C write failed with error=%d (addr=0x%X, reg 0x%X, val 0x%X).\n", i2cErr, ES8311_ADDR, reg, val); + } + } + + void _es8311InitAdc() { + // + // Currently only tested with the ESP32-P4 EV board with the onboard mic. + // Datasheet with I2C commands: https://dl.xkwy2018.com/downloads/RK3588/01_Official%20Release/04_Product%20Line%20Branch_NVR/02_Key%20Device%20Specifications/ES8311%20DS.pdf + // + _es8311I2cBegin(); + _es8311I2cWrite(0x00, 0b00011111); // RESET, default value + _es8311I2cWrite(0x45, 0b00000000); // GP, default value + _es8311I2cWrite(0x01, 0b00111010); // CLOCK MANAGER was 0b00110000 trying 0b00111010 (MCLK enable?) + + _es8311I2cWrite(0x02, 0b00000000); // 22050hz calculated + _es8311I2cWrite(0x05, 0b00000000); // 22050hz calculated + _es8311I2cWrite(0x03, 0b00010000); // 22050hz calculated + _es8311I2cWrite(0x04, 0b00010000); // 22050hz calculated + _es8311I2cWrite(0x07, 0b00000000); // 22050hz calculated + _es8311I2cWrite(0x08, 0b11111111); // 22050hz calculated + _es8311I2cWrite(0x06, 0b11100011); // 22050hz calculated + + _es8311I2cWrite(0x16, 0b00100000); // ADC was 0b00000011 trying 0b00100100 now + _es8311I2cWrite(0x0B, 0b00000000); // SYSTEM at default + _es8311I2cWrite(0x0C, 0b00100000); // SYSTEM was 0b00001111 trying 0b00100000 + _es8311I2cWrite(0x10, 0b00010011); // SYSTEM was 0b00011111 trying 0b00010011 + _es8311I2cWrite(0x11, 0b01111100); // SYSTEM was 0b01111111 trying 0b01111100 + _es8311I2cWrite(0x00, 0b11000000); // *** RESET (again - seems important?) + _es8311I2cWrite(0x01, 0b00111010); // CLOCK MANAGER was 0b00111111 trying 0b00111010 (again??) + _es8311I2cWrite(0x14, 0b00010000); // *** SYSTEM was 0b00011010 trying 0b00010000 (or 0b01111010) (PGA gain) + _es8311I2cWrite(0x12, 0b00000000); // SYSTEM - DAC, likely don't care + _es8311I2cWrite(0x13, 0b00010000); // SYSTEM - output, likely don't cate + _es8311I2cWrite(0x09, 0b00001000); // SDP IN (likely don't care) was 0b00001100 (16-bit) - changed to 0b00001000 (I2S 32-bit) + _es8311I2cWrite(0x0A, 0b00001000); // *** SDP OUT, was 0b00001100 trying 0b00001000 (I2S 32-bit) + _es8311I2cWrite(0x0E, 0b00000010); // *** SYSTEM was 0b00000010 trying 0b00011010 (seems best so far!) (or 0b00000010) + _es8311I2cWrite(0x0F, 0b01000100); // SYSTEM was 0b01000100 + _es8311I2cWrite(0x15, 0b00000000); // ADC soft ramp (disabled) + _es8311I2cWrite(0x1B, 0b00000101); // ADC soft-mute was 0b00000101 + _es8311I2cWrite(0x1C, 0b01100101); // ADC EQ and offset freeze was 0b01100101 (bad at 0b00101100) + _es8311I2cWrite(0x17, 0b10111111); // ADC volume was 0b11111111 trying ADC volume 0b10111111 = 0db (maxgain) 0x16 + _es8311I2cWrite(0x44, 0b00000000); // 0b10000000 - loopback test. on: 0x88; off: 0x00; mic--" speak + + } + + public: + ES8311Source(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f, bool i2sMaster=true) : + I2SSource(sampleRate, blockSize, sampleScale, i2sMaster) { + _config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT; + }; + + void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) { + DEBUGSR_PRINTLN("es8311Source:: initialize();"); + + // if ((i2sckPin < 0) || (mclkPin < 0)) { // WLEDMM not sure if this check is needed here, too + // ERRORSR_PRINTF("\nAR: invalid I2S es8311 pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin); + // return; + // } + // BUG: "use global I2C pins" are valid as -1, and -1 is seen as invalid here. + // Workaround: Set I2C pins here, which will also set them globally. + // Bug also exists in ES7243. + if ((i2c_sda < 0) || (i2c_scl < 0)) { // check that global I2C pins are not "undefined" + ERRORSR_PRINTF("\nAR: invalid es8311 global I2C pins: SDA=%d, SCL=%d\n", i2c_sda, i2c_scl); + return; + } + if (!pinManager.joinWire(i2c_sda, i2c_scl)) { // WLEDMM specific: start I2C with globally defined pins + ERRORSR_PRINTF("\nAR: failed to join I2C bus with SDA=%d, SCL=%d\n", i2c_sda, i2c_scl); + return; + } + + // First route mclk, then configure ADC over I2C, then configure I2S + _es8311InitAdc(); + I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin); + } + + void deinitialize() { + I2SSource::deinitialize(); + } + +}; + class WM8978Source : public I2SSource { private: // I2C initialization functions for WM8978 From 79f5db6d65a49a0252831562d763c91651999d5b Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Wed, 2 Oct 2024 16:12:48 +0200 Subject: [PATCH 044/232] fadePixelColorXY bugfix --- wled00/FX_2Dfcn.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 03919f8e..6ca95e13 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -506,8 +506,9 @@ void IRAM_ATTR_YN Segment::addPixelColorXY(int x, int y, uint32_t color, bool fa void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { // if (!isActive()) return; // not active //WLEDMM sanity check is repeated in getPixelColorXY / setPixelColorXY - CRGB oldPix = CRGB(getPixelColorXY(x,y)); - CRGB pix = oldPix.nscale8_video(fade); + CRGB pix = CRGB(getPixelColorXY(x,y)); + CRGB oldPix = pix; + pix = pix.nscale8_video(fade); if (pix != oldPix) setPixelColorXY(int(x), int(y), pix); } From b436a595ef042686b96d3401de974f1569d31812 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Wed, 2 Oct 2024 16:34:36 +0200 Subject: [PATCH 045/232] bugfix for holes in 2D DNA Spiral Holes were visible at height > 32. Root cause: "lerp8x8" seems to be inaccurate --> replaced by a simple linear calculation. --- wled00/FX.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 494d03d7..01b9e517 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5067,10 +5067,12 @@ uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulma if ((i + ms / 8) & 3) { // draw a gradient line between x and x1 x = x / 2; x1 = x1 / 2; - uint8_t steps = abs8(x - x1) + 1; + unsigned steps = abs8(x - x1) + 1; + bool positive = (x1 >= x); // direction of drawing for (size_t k = 1; k <= steps; k++) { - uint8_t rate = k * 255 / steps; - uint8_t dx = lerp8by8(x, x1, rate); + unsigned rate = k * 255 / steps; + //unsigned dx = lerp8by8(x, x1, rate); + unsigned dx = positive? (x + k-1) : (x - k+1); // behaves the same as "lerp8by8" but does not create holes //SEGMENT.setPixelColorXY(dx, i, ColorFromPalette(SEGPALETTE, hue, 255, LINEARBLEND).nscale8_video(rate)); SEGMENT.addPixelColorXY(dx, i, ColorFromPalette(SEGPALETTE, hue, 255, LINEARBLEND)); // use setPixelColorXY for different look SEGMENT.fadePixelColorXY(dx, i, rate); From 16644f838ae4819dd5002df5ed6e239bf526d93f Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Wed, 2 Oct 2024 19:20:19 +0100 Subject: [PATCH 046/232] Remove stray commma from _data_FX_MODE_2DGAMEOFLIFE --- wled00/FX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 01b9e517..f96deabe 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5379,7 +5379,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: SEGENV.step = strip.now; return FRAMETIME; } // mode_2Dgameoflife() -static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!,Color Mutation ☾,Blur ☾,,,All Colors ☾,Overlay BG ☾,Wrap ☾,;!,!;!;2;sx=56,ix=2,c1=128,o1=0,o2=0,o3=1"; +static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!,Color Mutation ☾,Blur ☾,,,All Colors ☾,Overlay BG ☾,Wrap ☾;!,!;!;2;sx=56,ix=2,c1=128,o1=0,o2=0,o3=1"; ///////////////////////// // 2D Hiphotic // From 3a38d0cfed9485bb635e3c037f153a4252435f77 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Wed, 2 Oct 2024 20:15:58 +0200 Subject: [PATCH 047/232] Fix for #4005 --- wled00/wled.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 5858f800..3c86c4b0 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -308,6 +308,7 @@ void WLED::loop() delete busConfigs[i]; busConfigs[i] = nullptr; } strip.finalizeInit(); + busses.setBrightness(bri); // fix re-initialised bus' brightness #4005 loadLedmap = true; if (aligned) strip.makeAutoSegments(); else strip.fixInvalidSegments(); From ebd16f6be4f39a1309b69545216325676e9decc6 Mon Sep 17 00:00:00 2001 From: Brandon502 <105077712+Brandon502@users.noreply.github.com> Date: Wed, 2 Oct 2024 18:12:03 -0400 Subject: [PATCH 048/232] Snow Fall - Minor Changes --- wled00/FX.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index ba13aaa5..5b1f3515 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5421,16 +5421,16 @@ uint16_t mode_2DSnowFall(void) { // By: Brandon Butler // Despawn snow bool overflow = SEGENV.aux0 && SEGMENT.check3; int despawnChance = SEGMENT.custom1 == 255 ? 256 : map(SEGMENT.custom1, 0, 255, 0, 100); // 255 goes to 256, allows always despawn - int y = rows - 1; + int lastY = rows - 1; for (int x = 0; x < cols; x++) { - if (overflow || random8() < despawnChance) setBitValue(grid, y * cols + x, 0); - if (overlay || getBitValue(grid, y * cols + x)) continue; // Skip drawing if inverted overlay or snow - SEGMENT.blendPixelColorXY(x, y, bgColor, blur); + if (overflow || random8() < despawnChance) setBitValue(grid, lastY * cols + x, 0); + if (overlay || getBitValue(grid, lastY * cols + x)) continue; // Skip drawing if inverted overlay or snow + SEGMENT.blendPixelColorXY(x, lastY, bgColor, blur); } if (SEGENV.aux0) --SEGENV.aux0; // Decrease overflow // Precompute shuffled indices, helps randomize snow movement - int shuffledIndices[cols]; + uint16_t shuffledIndices[cols]; for (int i = 0; i < cols; i++) shuffledIndices[i] = i; std::random_shuffle(shuffledIndices, shuffledIndices + cols); @@ -5484,13 +5484,13 @@ uint16_t mode_2DSnowFall(void) { // By: Brandon Butler if (overlay) continue; // Skip drawing if inverted overlay if (SEGMENT.check1) SEGMENT.setPixelColorXY(x, 0, ColorFromPalette(SEGPALETTE, random8())); // Use palette - else {int c = random8(80,200); SEGMENT.setPixelColorXY(x, 0, c, c, c);} // Use snow color + else {int c = random8(120,200); SEGMENT.setPixelColorXY(x, 0, c, c, c);} // Use snow color } SEGENV.step = strip.now; return FRAMETIME; } // mode_2DSnowFall() -static const char _data_FX_MODE_2DSNOWFALL[] PROGMEM = "Snow Fall@!,Spawn Rate,Despawn Rate,Blur,Sway Chance,Use Palette,Inverted Overlay,Prevent Overflow,;!,!;!;2;sx=128,ix=16,c1=17,c2=0,c3=0,o1=0,o2=0,o3=1"; +static const char _data_FX_MODE_2DSNOWFALL[] PROGMEM = "Snow Fall ☾@!,Spawn Rate,Despawn Rate,Blur,Sway Chance,Use Palette,Inverted Overlay,Prevent Overflow,;!,!;!;2;sx=128,ix=16,c1=17,c2=0,c3=0,o1=0,o2=0,o3=1"; ///////////////////////// // 2D Hiphotic // From 1e5b246871b1b034f1e93c4c5d3f4a9863f1b597 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Thu, 3 Oct 2024 00:19:18 +0200 Subject: [PATCH 049/232] small fixes very minor changes: * HUB75 bus don't leave _len uninitialized, handle display == nullptr before using display members * __attribute__((pure)) was in the wrong location * wled00.ino: made lps (loops-per-second) calculation a bit more accurate --- wled00/FX.h | 4 ++-- wled00/bus_manager.cpp | 6 ++++++ wled00/wled00.ino | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index 04eabe70..19175985 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -945,9 +945,9 @@ class WS2812FX { // 96 bytes milliampsPerLed, cctBlending, getActiveSegmentsNum(void) const, - getFirstSelectedSegId(void) __attribute__((pure)), + __attribute__((pure)) getFirstSelectedSegId(void), getLastActiveSegmentId(void) const, - getActiveSegsLightCapabilities(bool selectedOnly = false) __attribute__((pure)), + __attribute__((pure)) getActiveSegsLightCapabilities(bool selectedOnly = false), setPixelSegment(uint8_t n); inline uint8_t getBrightness(void) const { return _brightness; } diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index cb3d8fac..82e2c015 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -527,6 +527,7 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh _valid = false; fourScanPanel = nullptr; + _len = 0; mxconfig.double_buff = false; // Use our own memory-optimised buffer rather than the driver's own double-buffer @@ -744,6 +745,11 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh // OK, now we can create our matrix object display = new MatrixPanel_I2S_DMA(mxconfig); + if (display == nullptr) { + USER_PRINTLN("****** MatrixPanel_I2S_DMA !KABOOM! driver allocation failed ***********"); + USER_PRINT(F("heap usage: ")); USER_PRINTLN(lastHeap - ESP.getFreeHeap()); + return; + } this->_len = (display->width() * display->height()); diff --git a/wled00/wled00.ino b/wled00/wled00.ino index b19441a2..6f1f22d5 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -74,7 +74,10 @@ void loop() { //WLEDMM show loops per second loopCounter++; if (millis() - lastMillis >= 10000) { - //USER_PRINTF("%lu lps\n",loopCounter/10); + long delta = millis() - lastMillis; + if (delta > 0) { + //USER_PRINTF("%lu lps\n",(loopCounter*1000U) / delta); + } lastMillis = millis(); loopCounter = 0; } From 5b1877e343d763581cf569a1fa908c08b1fa9339 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Thu, 3 Oct 2024 20:12:28 +0200 Subject: [PATCH 050/232] Change to EUPL-1.2 --- Aircoookie_LICENSE | 24 -- LICENSE | 965 ++++++++++++++------------------------------- package-lock.json | 2 +- package.json | 2 +- readme.md | 3 + 5 files changed, 296 insertions(+), 700 deletions(-) delete mode 100644 Aircoookie_LICENSE diff --git a/Aircoookie_LICENSE b/Aircoookie_LICENSE deleted file mode 100644 index 30fd7534..00000000 --- a/Aircoookie_LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -The license below is valid for the code that is common with upstream "AC WLED" from https://github.com/Aircoookie/WLED. -Additions and code changes that are exclusive to this fork ("MoonModules WLED") are under GPLv3 license, see LICENSE. - -MIT License - -Copyright (c) 2016 Christian Schwinne - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/LICENSE b/LICENSE index f288702d..22953c3c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,674 +1,291 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program 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. - - This program 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 this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. + EUROPEAN UNION PUBLIC LICENCE v. 1.2 + EUPL © the European Union 2007, 2016 + +This European Union Public Licence (the ‘EUPL’) applies to the Work (as +defined below) which is provided under the terms of this Licence. Any use of +the Work, other than as authorised under this Licence is prohibited (to the +extent such use is covered by a right of the copyright holder of the Work). + +The Work is provided under the terms of this Licence when the Licensor (as +defined below) has placed the following notice immediately following the +copyright notice for the Work: + + Licensed under the EUPL + +or has expressed by any other means his willingness to license under the EUPL. + +1. Definitions + +In this Licence, the following terms have the following meaning: + +- ‘The Licence’: this Licence. + +- ‘The Original Work’: the work or software distributed or communicated by the + Licensor under this Licence, available as Source Code and also as Executable + Code as the case may be. + +- ‘Derivative Works’: the works or software that could be created by the + Licensee, based upon the Original Work or modifications thereof. This + Licence does not define the extent of modification or dependence on the + Original Work required in order to classify a work as a Derivative Work; + this extent is determined by copyright law applicable in the country + mentioned in Article 15. + +- ‘The Work’: the Original Work or its Derivative Works. + +- ‘The Source Code’: the human-readable form of the Work which is the most + convenient for people to study and modify. + +- ‘The Executable Code’: any code which has generally been compiled and which + is meant to be interpreted by a computer as a program. + +- ‘The Licensor’: the natural or legal person that distributes or communicates + the Work under the Licence. + +- ‘Contributor(s)’: any natural or legal person who modifies the Work under + the Licence, or otherwise contributes to the creation of a Derivative Work. + +- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of + the Work under the terms of the Licence. + +- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending, + renting, distributing, communicating, transmitting, or otherwise making + available, online or offline, copies of the Work or providing access to its + essential functionalities at the disposal of any other natural or legal + person. + +2. Scope of the rights granted by the Licence + +The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, +sublicensable licence to do the following, for the duration of copyright +vested in the Original Work: + +- use the Work in any circumstance and for all usage, +- reproduce the Work, +- modify the Work, and make Derivative Works based upon the Work, +- communicate to the public, including the right to make available or display + the Work or copies thereof to the public and perform publicly, as the case + may be, the Work, +- distribute the Work or copies thereof, +- lend and rent the Work or copies thereof, +- sublicense rights in the Work or copies thereof. + +Those rights can be exercised on any media, supports and formats, whether now +known or later invented, as far as the applicable law permits so. + +In the countries where moral rights apply, the Licensor waives his right to +exercise his moral right to the extent allowed by law in order to make +effective the licence of the economic rights here above listed. + +The Licensor grants to the Licensee royalty-free, non-exclusive usage rights +to any patents held by the Licensor, to the extent necessary to make use of +the rights granted on the Work under this Licence. + +3. Communication of the Source Code + +The Licensor may provide the Work either in its Source Code form, or as +Executable Code. If the Work is provided as Executable Code, the Licensor +provides in addition a machine-readable copy of the Source Code of the Work +along with each copy of the Work that the Licensor distributes or indicates, +in a notice following the copyright notice attached to the Work, a repository +where the Source Code is easily and freely accessible for as long as the +Licensor continues to distribute or communicate the Work. + +4. Limitations on copyright + +Nothing in this Licence is intended to deprive the Licensee of the benefits +from any exception or limitation to the exclusive rights of the rights owners +in the Work, of the exhaustion of those rights or of other applicable +limitations thereto. + +5. Obligations of the Licensee + +The grant of the rights mentioned above is subject to some restrictions and +obligations imposed on the Licensee. Those obligations are the following: + +Attribution right: The Licensee shall keep intact all copyright, patent or +trademarks notices and all notices that refer to the Licence and to the +disclaimer of warranties. The Licensee must include a copy of such notices and +a copy of the Licence with every copy of the Work he/she distributes or +communicates. The Licensee must cause any Derivative Work to carry prominent +notices stating that the Work has been modified and the date of modification. + +Copyleft clause: If the Licensee distributes or communicates copies of the +Original Works or Derivative Works, this Distribution or Communication will be +done under the terms of this Licence or of a later version of this Licence +unless the Original Work is expressly distributed only under this version of +the Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee +(becoming Licensor) cannot offer or impose any additional terms or conditions +on the Work or Derivative Work that alter or restrict the terms of the +Licence. + +Compatibility clause: If the Licensee Distributes or Communicates Derivative +Works or copies thereof based upon both the Work and another work licensed +under a Compatible Licence, this Distribution or Communication can be done +under the terms of this Compatible Licence. For the sake of this clause, +‘Compatible Licence’ refers to the licences listed in the appendix attached to +this Licence. Should the Licensee's obligations under the Compatible Licence +conflict with his/her obligations under this Licence, the obligations of the +Compatible Licence shall prevail. + +Provision of Source Code: When distributing or communicating copies of the +Work, the Licensee will provide a machine-readable copy of the Source Code or +indicate a repository where this Source will be easily and freely available +for as long as the Licensee continues to distribute or communicate the Work. + +Legal Protection: This Licence does not grant permission to use the trade +names, trademarks, service marks, or names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the copyright notice. + +6. Chain of Authorship + +The original Licensor warrants that the copyright in the Original Work granted +hereunder is owned by him/her or licensed to him/her and that he/she has the +power and authority to grant the Licence. + +Each Contributor warrants that the copyright in the modifications he/she +brings to the Work are owned by him/her or licensed to him/her and that he/she +has the power and authority to grant the Licence. + +Each time You accept the Licence, the original Licensor and subsequent +Contributors grant You a licence to their contributions to the Work, under the +terms of this Licence. + +7. Disclaimer of Warranty + +The Work is a work in progress, which is continuously improved by numerous +Contributors. It is not a finished work and may therefore contain defects or +‘bugs’ inherent to this type of development. + +For the above reason, the Work is provided under the Licence on an ‘as is’ +basis and without warranties of any kind concerning the Work, including +without limitation merchantability, fitness for a particular purpose, absence +of defects or errors, accuracy, non-infringement of intellectual property +rights other than copyright as stated in Article 6 of this Licence. + +This disclaimer of warranty is an essential part of the Licence and a +condition for the grant of any rights to the Work. + +8. Disclaimer of Liability + +Except in the cases of wilful misconduct or damages directly caused to natural +persons, the Licensor will in no event be liable for any direct or indirect, +material or moral, damages of any kind, arising out of the Licence or of the +use of the Work, including without limitation, damages for loss of goodwill, +work stoppage, computer failure or malfunction, loss of data or any commercial +damage, even if the Licensor has been advised of the possibility of such +damage. However, the Licensor will be liable under statutory product liability +laws as far such laws apply to the Work. + +9. Additional agreements + +While distributing the Work, You may choose to conclude an additional +agreement, defining obligations or services consistent with this Licence. +However, if accepting obligations, You may act only on your own behalf and on +your sole responsibility, not on behalf of the original Licensor or any other +Contributor, and only if You agree to indemnify, defend, and hold each +Contributor harmless for any liability incurred by, or claims asserted against +such Contributor by the fact You have accepted any warranty or additional +liability. + +10. Acceptance of the Licence + +The provisions of this Licence can be accepted by clicking on an icon ‘I +agree’ placed under the bottom of a window displaying the text of this Licence +or by affirming consent in any other similar way, in accordance with the rules +of applicable law. Clicking on that icon indicates your clear and irrevocable +acceptance of this Licence and all of its terms and conditions. + +Similarly, you irrevocably accept this Licence and all of its terms and +conditions by exercising any rights granted to You by Article 2 of this +Licence, such as the use of the Work, the creation by You of a Derivative Work +or the Distribution or Communication by You of the Work or copies thereof. + +11. Information to the public + +In case of any Distribution or Communication of the Work by means of +electronic communication by You (for example, by offering to download the Work +from a remote location) the distribution channel or media (for example, a +website) must at least provide to the public the information requested by the +applicable law regarding the Licensor, the Licence and the way it may be +accessible, concluded, stored and reproduced by the Licensee. + +12. Termination of the Licence + +The Licence and the rights granted hereunder will terminate automatically upon +any breach by the Licensee of the terms of the Licence. + +Such a termination will not terminate the licences of any person who has +received the Work from the Licensee under the Licence, provided such persons +remain in full compliance with the Licence. + +13. Miscellaneous + +Without prejudice of Article 9 above, the Licence represents the complete +agreement between the Parties as to the Work. + +If any provision of the Licence is invalid or unenforceable under applicable +law, this will not affect the validity or enforceability of the Licence as a +whole. Such provision will be construed or reformed so as necessary to make it +valid and enforceable. + +The European Commission may publish other linguistic versions or new versions +of this Licence or updated versions of the Appendix, so far this is required +and reasonable, without reducing the scope of the rights granted by the +Licence. New versions of the Licence will be published with a unique version +number. + +All linguistic versions of this Licence, approved by the European Commission, +have identical value. Parties can take advantage of the linguistic version of +their choice. + +14. Jurisdiction + +Without prejudice to specific agreement between parties, + +- any litigation resulting from the interpretation of this License, arising + between the European Union institutions, bodies, offices or agencies, as a + Licensor, and any Licensee, will be subject to the jurisdiction of the Court + of Justice of the European Union, as laid down in article 272 of the Treaty + on the Functioning of the European Union, + +- any litigation arising between other parties and resulting from the + interpretation of this License, will be subject to the exclusive + jurisdiction of the competent court where the Licensor resides or conducts + its primary business. + +15. Applicable Law + +Without prejudice to specific agreement between parties, + +- this Licence shall be governed by the law of the European Union Member State + where the Licensor has his seat, resides or has his registered office, + +- this licence shall be governed by Belgian law if the Licensor has no seat, + residence or registered office inside a European Union Member State. + +Appendix + +‘Compatible Licences’ according to Article 5 EUPL are: + +- GNU General Public License (GPL) v. 2, v. 3 +- GNU Affero General Public License (AGPL) v. 3 +- Open Software License (OSL) v. 2.1, v. 3.0 +- Eclipse Public License (EPL) v. 1.0 +- CeCILL v. 2.0, v. 2.1 +- Mozilla Public Licence (MPL) v. 2 +- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 +- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for + works other than software +- European Union Public Licence (EUPL) v. 1.1, v. 1.2 +- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong + Reciprocity (LiLiQ-R+). + +The European Commission may update this Appendix to later versions of the +above licences without producing a new version of the EUPL, as long as they +provide the rights granted in Article 2 of this Licence and protect the +covered Source Code from exclusive appropriation. + +All other changes or additions to this Appendix require the production of a +new EUPL version. diff --git a/package-lock.json b/package-lock.json index aa060721..59697131 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,7 @@ "": { "name": "wled", "version": "0.14.1-b32.41.dev", - "license": "GPL-3.0-or-later", + "license": "EUPL-1.2", "dependencies": { "clean-css": "^4.2.3", "html-minifier-terser": "^5.1.1", diff --git a/package.json b/package.json index c73dd26f..b6763b33 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "url": "git+https://github.com/MoonModules/WLED.git" }, "author": "", - "license": "GPL-3.0-or-later", + "license": "EUPL-1.2", "bugs": { "url": "https://github.com/MoonModules/WLED/issues" }, diff --git a/readme.md b/readme.md index c1d5efab..323a712e 100644 --- a/readme.md +++ b/readme.md @@ -13,6 +13,9 @@ # Welcome to WLED MoonModules! ✨ image +Licensed under the [EUPL-1.2](https://joinup.ec.europa.eu/collection/eupl) or later. The official license text is [available in 23 languages](https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12). + + MoonModules/WLED is a fork from [Aircoookie/WLED](https://github.com/Aircoookie/WLED) which contains latest merge of v0.14 of WLED with [additional features](https://mm.kno.wled.ge/moonmodules/what-is-moonmodules/). From ea9dad8918431b070fe78f34ff688c7bd2b9a298 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Thu, 3 Oct 2024 20:14:50 +0200 Subject: [PATCH 051/232] Update readme.md minor edit --- readme.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 323a712e..054bd055 100644 --- a/readme.md +++ b/readme.md @@ -13,9 +13,8 @@ # Welcome to WLED MoonModules! ✨ image -Licensed under the [EUPL-1.2](https://joinup.ec.europa.eu/collection/eupl) or later. The official license text is [available in 23 languages](https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12). - +Licensed under the [EUPL-1.2](https://joinup.ec.europa.eu/collection/eupl) or later. The official license text is [available in 23 languages](https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12). MoonModules/WLED is a fork from [Aircoookie/WLED](https://github.com/Aircoookie/WLED) which contains latest merge of v0.14 of WLED with [additional features](https://mm.kno.wled.ge/moonmodules/what-is-moonmodules/). From f4e9292849572495cefe6bf7666460cb88629a3d Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Fri, 4 Oct 2024 22:49:00 +0100 Subject: [PATCH 052/232] Download all artifacts --- .github/workflows/wled-ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/wled-ci.yml b/.github/workflows/wled-ci.yml index e875ca18..fe50e092 100644 --- a/.github/workflows/wled-ci.yml +++ b/.github/workflows/wled-ci.yml @@ -82,8 +82,6 @@ jobs: if: startsWith(github.ref, 'refs/tags/') steps: - uses: actions/download-artifact@v4 - with: - name: firmware-release-* - name: List Artifacts run: find ./ - name: Create draft release From 2bbb890fb933e2f0d02d7f927de05c1d3fcf7fcd Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Fri, 4 Oct 2024 22:51:10 +0100 Subject: [PATCH 053/232] tweak caching of build --- .github/workflows/wled-ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/wled-ci.yml b/.github/workflows/wled-ci.yml index fe50e092..deed4e69 100644 --- a/.github/workflows/wled-ci.yml +++ b/.github/workflows/wled-ci.yml @@ -50,10 +50,7 @@ jobs: uses: actions/cache@v4 with: path: ~/.platformio - key: ${{ runner.os }}-${{ matrix.environment}}-${{ hashFiles('platformio.ini') }} - restore-keys: | - ${{ runner.os }}-${{ matrix.environment}} - + key: ${{ runner.os }}-${{ matrix.environment}} - name: Set up Python uses: actions/setup-python@v5 with: From e665b3423687c3caddd921e405883fc88043c9fd Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Fri, 4 Oct 2024 23:02:03 +0100 Subject: [PATCH 054/232] Move bins to single directory --- .github/workflows/wled-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/wled-ci.yml b/.github/workflows/wled-ci.yml index deed4e69..4c4de1d7 100644 --- a/.github/workflows/wled-ci.yml +++ b/.github/workflows/wled-ci.yml @@ -80,7 +80,7 @@ jobs: steps: - uses: actions/download-artifact@v4 - name: List Artifacts - run: find ./ + run: find ./ -type f -name *.bin -exec mv -v {} ./ \; - name: Create draft release uses: softprops/action-gh-release@v1 with: From d429293741dc7ac9aa6ee9396d12ffedb0537ea8 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Fri, 4 Oct 2024 23:10:35 +0100 Subject: [PATCH 055/232] WLEDMM*.bin only --- .github/workflows/wled-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/wled-ci.yml b/.github/workflows/wled-ci.yml index 4c4de1d7..d311af4a 100644 --- a/.github/workflows/wled-ci.yml +++ b/.github/workflows/wled-ci.yml @@ -86,6 +86,6 @@ jobs: with: draft: True files: | - *.bin + WLEDMM*.bin env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 4fadcade939f7063114118286f2e6542cff1291a Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sun, 6 Oct 2024 19:59:59 +0200 Subject: [PATCH 056/232] fix for off-by-one error in Lissajous (float version) --- wled00/FX.cpp | 2 +- wled00/FX_2Dfcn.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 5b1f3515..a93c2843 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5654,7 +5654,7 @@ uint16_t mode_2DLissajous(void) { // By: Andrew Tuline unsigned palIndex = (256*ylocn) + phase/2 + (i* SEGMENT.speed)/64; //SEGMENT.setPixelColorXY(xlocn, ylocn, SEGMENT.color_from_palette(palIndex, false, PALETTE_SOLID_WRAP, 0)); // draw pixel with anti-aliasing - color follows rotation // WLEDMM wu_pixel is 50% faster, and still lokks better - SEGMENT.wu_pixel(uint32_t(xlocn * (cols <<8)), uint32_t(ylocn * (rows <<8)), + SEGMENT.wu_pixel(uint32_t(xlocn * ((cols-1) <<8)), uint32_t(ylocn * ((rows-1) <<8)), CRGB(SEGMENT.color_from_palette(palIndex, false, PALETTE_SOLID_WRAP, 0))); } } else diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 6ca95e13..34eefb58 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -466,8 +466,8 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa, bool fast } #else // replacement using wu_pixel - unsigned px = x * (virtualWidth() <<8); - unsigned py = y * (virtualHeight() <<8); + unsigned px = x * ((virtualWidth()-1) <<8); + unsigned py = y * ((virtualHeight()-1) <<8); wu_pixel(px, py, CRGB(col)); #endif } From 1b7cae0c593ebbb885956c67ac2132950bd0ff50 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sun, 6 Oct 2024 20:12:56 +0200 Subject: [PATCH 057/232] comment added --- wled00/bus_manager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 82e2c015..ab124b93 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -532,6 +532,8 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh mxconfig.double_buff = false; // Use our own memory-optimised buffer rather than the driver's own double-buffer // mxconfig.driver = HUB75_I2S_CFG::ICN2038S; // experimental - use specific shift register driver + // mxconfig.driver = HUB75_I2S_CFG::FM6124; // try this driver in case you panel stays dark, or when colors look too pastel + // mxconfig.latch_blanking = 3; // mxconfig.i2sspeed = HUB75_I2S_CFG::HZ_10M; // experimental - 5MHZ should be enugh, but colours looks slightly better at 10MHz // mxconfig.min_refresh_rate = 90; From 16564007466d33411cf656af40f7b55c7b480c23 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sun, 6 Oct 2024 21:32:07 +0200 Subject: [PATCH 058/232] bugfix for broken presets when segment name is very long another off-by-one --- wled00/FX_fcn.cpp | 2 +- wled00/json.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index f2ed3e20..d6c80731 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1747,7 +1747,7 @@ void WS2812FX::enumerateLedmaps() { //WLEDMM add segment names to be used as ledmap names uint8_t segment_index = 0; for (segment &seg : _segments) { - if (seg.name != nullptr && strcmp(seg.name, "") != 0) { + if (seg.name != nullptr && strlen(seg.name) > 0) { char fileName[33]; snprintf_P(fileName, sizeof(fileName), PSTR("/lm%s.json"), seg.name); bool isFile = WLED_FS.exists(fileName); diff --git a/wled00/json.cpp b/wled00/json.cpp index a2b9514b..ac4d1ca5 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -145,9 +145,9 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId) const char * name = elem["n"].as(); size_t len = 0; if (name != nullptr) len = strlen(name); - if (len > 0 && len < 33) { + if (len > 0 && len < 32) { seg.name = new char[len+1]; - if (seg.name) strlcpy(seg.name, name, 33); + if (seg.name) strlcpy(seg.name, name, len+1); } else { // but is empty (already deleted above) elem.remove("n"); From 8a0888ba8802ff66fbb92a1e440bc47492a22c45 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sun, 6 Oct 2024 21:38:09 +0200 Subject: [PATCH 059/232] Pinwheel update for 128x128 pixels --- wled00/FX_fcn.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index d6c80731..1db62a15 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -841,11 +841,14 @@ constexpr int Pinwheel_Size_Big = 50; // larger than this -> use "XL" constexpr int Pinwheel_Steps_XL = 368; constexpr int Pinwheel_Size_XL = 58; // larger than this -> use "XXL" constexpr int Pinwheel_Steps_XXL = 456; +constexpr int Pinwheel_Size_XXL = 68; // larger than this -> use "LL" +constexpr int Pinwheel_Steps_LL = 592; // 128x64 no holes, 28fps constexpr float Int_to_Rad_Small = (DEG_TO_RAD * 360) / Pinwheel_Steps_Small; // conversion: from 0...72 to Radians constexpr float Int_to_Rad_Med = (DEG_TO_RAD * 360) / Pinwheel_Steps_Medium; // conversion: from 0...192 to Radians constexpr float Int_to_Rad_Big = (DEG_TO_RAD * 360) / Pinwheel_Steps_Big; // conversion: from 0...304 to Radians constexpr float Int_to_Rad_XL = (DEG_TO_RAD * 360) / Pinwheel_Steps_XL; // conversion: from 0...368 to Radians constexpr float Int_to_Rad_XXL = (DEG_TO_RAD * 360) / Pinwheel_Steps_XXL; // conversion: from 0...456 to Radians +constexpr float Int_to_Rad_LL = (DEG_TO_RAD * 360) / Pinwheel_Steps_LL; // conversion: from 0...456 to Radians constexpr int Fixed_Scale = 512; // fixpoint scaling factor (9bit for fraction) @@ -856,8 +859,9 @@ static float getPinwheelAngle(int i, int vW, int vH) { if (maxXY <= Pinwheel_Size_Medium) return float(i) * Int_to_Rad_Med; if (maxXY <= Pinwheel_Size_Big) return float(i) * Int_to_Rad_Big; if (maxXY <= Pinwheel_Size_XL) return float(i) * Int_to_Rad_XL; + if (maxXY <= Pinwheel_Size_XXL) return float(i) * Int_to_Rad_XXL; // else - return float(i) * Int_to_Rad_XXL; + return float(i) * Int_to_Rad_LL; } // Pinwheel helper function: matrix dimensions to number of rays static int getPinwheelLength(int vW, int vH) { @@ -866,8 +870,9 @@ static int getPinwheelLength(int vW, int vH) { if (maxXY <= Pinwheel_Size_Medium) return Pinwheel_Steps_Medium; if (maxXY <= Pinwheel_Size_Big) return Pinwheel_Steps_Big; if (maxXY <= Pinwheel_Size_XL) return Pinwheel_Steps_XL; + if (maxXY <= Pinwheel_Size_XXL) return Pinwheel_Steps_XXL; // else - return Pinwheel_Steps_XXL; + return Pinwheel_Steps_LL; } #endif From e400720e40991edeeb7bf41b28f15bd204dcbc8e Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sun, 6 Oct 2024 21:44:19 +0200 Subject: [PATCH 060/232] correct code comment --- wled00/FX_fcn.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 1db62a15..65f42a39 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -848,7 +848,7 @@ constexpr float Int_to_Rad_Med = (DEG_TO_RAD * 360) / Pinwheel_Steps_Medium; / constexpr float Int_to_Rad_Big = (DEG_TO_RAD * 360) / Pinwheel_Steps_Big; // conversion: from 0...304 to Radians constexpr float Int_to_Rad_XL = (DEG_TO_RAD * 360) / Pinwheel_Steps_XL; // conversion: from 0...368 to Radians constexpr float Int_to_Rad_XXL = (DEG_TO_RAD * 360) / Pinwheel_Steps_XXL; // conversion: from 0...456 to Radians -constexpr float Int_to_Rad_LL = (DEG_TO_RAD * 360) / Pinwheel_Steps_LL; // conversion: from 0...456 to Radians +constexpr float Int_to_Rad_LL = (DEG_TO_RAD * 360) / Pinwheel_Steps_LL; // conversion: from 0...592 to Radians constexpr int Fixed_Scale = 512; // fixpoint scaling factor (9bit for fraction) From a84216947bfb28488e58837d5a6732836be93d6c Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sun, 6 Oct 2024 22:19:08 +0200 Subject: [PATCH 061/232] forgot one --- wled00/FX_fcn.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 65f42a39..88f1e1b6 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -850,7 +850,7 @@ constexpr float Int_to_Rad_XL = (DEG_TO_RAD * 360) / Pinwheel_Steps_XL; / constexpr float Int_to_Rad_XXL = (DEG_TO_RAD * 360) / Pinwheel_Steps_XXL; // conversion: from 0...456 to Radians constexpr float Int_to_Rad_LL = (DEG_TO_RAD * 360) / Pinwheel_Steps_LL; // conversion: from 0...592 to Radians -constexpr int Fixed_Scale = 512; // fixpoint scaling factor (9bit for fraction) +constexpr int Fixed_Scale = 32768; // fixpoint scaling factor (15bit for fraction) // Pinwheel helper function: pixel index to radians static float getPinwheelAngle(int i, int vW, int vH) { From 7740c7dc62fadf6f8b39d7fb3e5b421938e27d48 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 7 Oct 2024 13:14:50 +0200 Subject: [PATCH 062/232] drip effect rework * made effect time-defendant (instead of framerate dependent) * made effect a bit slower * fixed physics * made dropping more random (avoids skipped rows) * drop traces look better now * made bounce more realistic * prevent off-screen drawing --- wled00/FX.cpp | 67 ++++++++++++++++++++++++++++++++++++--------------- wled00/wled.h | 2 +- 2 files changed, 49 insertions(+), 20 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index a93c2843..d345ff79 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -3713,6 +3713,17 @@ uint16_t mode_exploding_fireworks(void) static const char _data_FX_MODE_EXPLODING_FIREWORKS[] PROGMEM = "Fireworks 1D@Gravity,Firing side;!,!;!;12;pal=11,ix=128"; + +//SparkDrop type is used for drip +typedef struct __attribute__ ((packed)) SparkDrop { + float pos; + float boost; // speed "kick" when dropping + float vel; + uint32_t aux; // aux variable (RGBW color) + uint16_t col; + uint8_t colIndex; +} sparkdrop; + /* * Drip Effect * ported of: https://www.youtube.com/watch?v=sru2fXh4r7k @@ -3723,20 +3734,24 @@ uint16_t mode_drip(void) //allocate segment data uint16_t strips = SEGMENT.nrOfVStrips(); const int maxNumDrops = 4; - uint16_t dataSize = sizeof(spark) * maxNumDrops; + uint16_t dataSize = sizeof(sparkdrop) * maxNumDrops; if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed - Spark* drops = reinterpret_cast(SEGENV.data); + SparkDrop* drops = reinterpret_cast(SEGENV.data); - if (SEGENV.call == 0) SEGMENT.fill(BLACK); // WLEDMM clear LEDs at startup + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); // WLEDMM clear LEDs at startup + SEGENV.step = strip.now; // initial time + } if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); struct virtualStrip { - static void runStrip(uint16_t stripNr, Spark* drops) { + static void runStrip(uint16_t stripNr, SparkDrop* drops, float deltaTime) { // WLEDMM added deltaTime uint8_t numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3 - 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 + float theSpeed = (SEGMENT.speed * SEGMENT.speed) / 255.0f; // WLEDMM + float gravity = -0.0002f - theSpeed/42000.0f; //gravity // WLEDMM adjusted + gravity *= min(max(1, SEGLEN-1), 255); // WLEDMM speed limit 255 const int sourcedrop = 12; for (int j=0;j255) drops[j].col=255; - SEGMENT.setPixelColor(indexToVStrip(uint16_t(drops[j].pos), stripNr), color_blend(BLACK,dropColor,drops[j].col)); + int intPos = max(0.0f, roundf(drops[j].pos)); // WLEDMM round it first + SEGMENT.setPixelColor(int(indexToVStrip(intPos, stripNr)), color_blend(BLACK,dropColor,drops[j].col)); - drops[j].col += map(SEGMENT.custom1, 0, 255, 1, 6); // swelling + unsigned swell = map(SEGMENT.custom1, 0, 255, 1, 6); // swelling + drops[j].col += swell; + if (drops[j].boost < 4.0f) drops[j].boost += 0.012f * float(swell); // increase mass when swelling uint32_t fallrate = (drops[j].col * (1 + SEGMENT.custom1 * SEGMENT.custom1)) / 192; // WLEDMM specific if (random16() <= (fallrate / 10)) { // random drop => 1% ... 20% probalibity drops[j].colIndex=2; //fall drops[j].col=255; + drops[j].vel = gravity * 2.0f * drops[j].boost; // WLEDMM initial kick } } if (drops[j].colIndex > 1) { // falling if (drops[j].pos > 0.01f) { // fall until end of segment - drops[j].pos += drops[j].vel; + drops[j].pos += drops[j].vel * deltaTime; if (drops[j].pos < 0) drops[j].pos = 0; - drops[j].vel += gravity; // gravity is negative + drops[j].vel += gravity * deltaTime; // gravity is negative - for (int i=1;i<7-drops[j].colIndex;i++) { // some minor math so we don't expand bouncing droplets - int intPos = roundf(drops[j].pos) +i; // WLEDMM round it first + int maxLen = 8 + SEGMENT.speed/64; + for (int i=1; i < maxLen-drops[j].colIndex; i++) { // some minor math so we don't expand bouncing droplets + int intPos = roundf(drops[j].pos + float(i)); // WLEDMM round it first + if ((intPos >= SEGLEN) || (intPos < 0)) break; // WLEDMM skip off-screen pixels 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, (2 * drops[j].col)/3)); // WLEDMM reduced brightness + SEGMENT.addPixelColor(indexToVStrip(0, stripNr), color_blend(dropColor,BLACK, drops[j].col*4)); // WLEDMM darker } } else { // we hit bottom if (drops[j].colIndex > 2) { // already hit once, so back to forming @@ -3785,8 +3807,11 @@ uint16_t mode_drip(void) } else { if (drops[j].colIndex==2) { // init bounce - drops[j].vel = -drops[j].vel/4;// reverse velocity with damping - drops[j].pos += drops[j].vel; + // reverse velocity with damping + if (SEGLEN > 16) drops[j].vel = -drops[j].vel/3.5f; + else drops[j].vel = -drops[j].vel/4.5f; + // do bounce + drops[j].pos += drops[j].vel * deltaTime * drops[j].boost*0.5f; } drops[j].col = sourcedrop*2; drops[j].colIndex = 5; // bouncing @@ -3797,12 +3822,16 @@ uint16_t mode_drip(void) } }; + // WLEDMM calculate time passed + uint32_t millisPassed = min(max(1UL, strip.now - SEGENV.step), 180UL); // constrain between 1 and 180 + SEGENV.step = strip.now; + float deltaTime = float(millisPassed) / 20.0f; // base speed 50 FPS for (int stripNr=0; stripNr Date: Mon, 7 Oct 2024 13:35:31 +0200 Subject: [PATCH 063/232] make all compilers happy fixing CI build error --- wled00/FX.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index d345ff79..0243472f 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -3208,7 +3208,7 @@ static const char _data_FX_MODE_SOLID_GLITTER[] PROGMEM = "Solid Glitter@,!;Bg,, //each needs 19 bytes -//Spark type is used for popcorn, 1D fireworks, and drip +//Spark type is used for popcorn, 1D fireworks typedef struct Spark { float pos, posX; float vel, velX; @@ -3823,7 +3823,7 @@ uint16_t mode_drip(void) }; // WLEDMM calculate time passed - uint32_t millisPassed = min(max(1UL, strip.now - SEGENV.step), 180UL); // constrain between 1 and 180 + uint32_t millisPassed = min(max(1U, unsigned(strip.now - SEGENV.step)), 180U); // constrain between 1 and 180 SEGENV.step = strip.now; float deltaTime = float(millisPassed) / 20.0f; // base speed 50 FPS for (int stripNr=0; stripNr Date: Mon, 7 Oct 2024 15:54:04 +0200 Subject: [PATCH 064/232] popcorn FX minor rework * made effect time dependent (instead of framerate dependent) * speed tuning * add a small trail to prevent jumping dots and blank rows --- wled00/FX.cpp | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 0243472f..dd9c9b66 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -3236,6 +3236,11 @@ static uint16_t mode_popcorn_core(bool useaudio) { Spark* popcorn = reinterpret_cast(SEGENV.data); + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); // WLEDMM clear LEDs at startup + SEGENV.step = strip.now; // initial time + } + bool hasCol2 = SEGCOLOR(2); if (!SEGMENT.check2) SEGMENT.fill(hasCol2 ? BLACK : SEGCOLOR(1)); @@ -3248,9 +3253,9 @@ static uint16_t mode_popcorn_core(bool useaudio) { } struct virtualStrip { - static void runStrip(uint16_t stripNr, Spark* popcorn, bool useaudio, um_data_t *um_data) { // WLEDMM added useaudio and um_data - float gravity = -0.0001f - (SEGMENT.speed/200000.0f); // m/s/s - gravity *= SEGLEN; + static void runStrip(uint16_t stripNr, Spark* popcorn, bool useaudio, um_data_t *um_data, float deltaTime) { // WLEDMM added useaudio and um_data + float gravity = -0.0001f - (SEGMENT.speed/180000.0f); // m/s/s // WLEDMM original value was "-0.0001f - (SEGMENT.speed/200000.0f)" + gravity *= min(max(1, SEGLEN-1), 255); // WLEDMM speed limit 255 uint8_t numPopcorn = SEGMENT.intensity*maxNumPopcorn/255; if (numPopcorn == 0) numPopcorn = 1; @@ -3261,8 +3266,8 @@ static uint16_t mode_popcorn_core(bool useaudio) { for(int i = 0; i < numPopcorn; i++) { if (popcorn[i].pos >= 0.0f) { // if kernel is active, update its position - popcorn[i].pos += popcorn[i].vel; - popcorn[i].vel += gravity; + popcorn[i].pos += popcorn[i].vel * deltaTime; + popcorn[i].vel += gravity * deltaTime; } else { // if kernel is inactive, randomly pop it bool doPopCorn = false; // WLEDMM allows to inhibit new pops // WLEDMM begin @@ -3281,7 +3286,7 @@ static uint16_t mode_popcorn_core(bool useaudio) { uint16_t peakHeight = 128 + random8(128); //0-255 peakHeight = (peakHeight * (SEGLEN -1)) >> 8; - popcorn[i].vel = sqrtf(-2.0f * gravity * peakHeight); + popcorn[i].vel = sqrtf(-2.01f * gravity * peakHeight); if (SEGMENT.palette) { @@ -3298,19 +3303,31 @@ static uint16_t mode_popcorn_core(bool useaudio) { if (!SEGMENT.palette && popcorn[i].colIndex < NUM_COLORS) col = SEGCOLOR(popcorn[i].colIndex); uint16_t ledIndex = popcorn[i].pos; if (ledIndex < SEGLEN) SEGMENT.setPixelColor(indexToVStrip(ledIndex, stripNr), col); + // WLEDMM add small trail + for (int n=1; n<4; n++) { + float spdLimit = n; + unsigned fade = 128 - 32*n; + uint32_t trailColor = color_fade(col, fade, true); + if ((popcorn[i].vel < -spdLimit) && (ledIndex+n < SEGLEN)) SEGMENT.setPixelColor(indexToVStrip(ledIndex+n, stripNr), trailColor); + if ((popcorn[i].vel > spdLimit) && (ledIndex >= n)) SEGMENT.setPixelColor(indexToVStrip(ledIndex-n, stripNr), trailColor); + } } } } }; + // WLEDMM calculate time passed + uint32_t millisPassed = min(max(1U, unsigned(strip.now - SEGENV.step)), 200U); // constrain between 1 and 200 + SEGENV.step = strip.now; + float deltaTime = useaudio ? float(millisPassed) / 8.0f : float(millisPassed) / 16.0f; // base speed: 64 FPS (normal) / 120fps (audioreactive) for (int stripNr=0; stripNr Date: Tue, 8 Oct 2024 19:00:59 +0200 Subject: [PATCH 065/232] AR: new input type 'None' for network receive only --- usermods/audioreactive/audio_reactive.h | 47 +++++++++++++++++-------- usermods/audioreactive/audio_source.h | 20 ++++++----- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 1c46e33a..a6fcbd7c 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -199,7 +199,6 @@ static uint8_t binNum = 8; // Used to select the bin for FFT based bea // use audio source class (ESP32 specific) #include "audio_source.h" -constexpr i2s_port_t I2S_PORT = I2S_NUM_0; // I2S port to use (do not change !) constexpr int BLOCK_SIZE = 128; // I2S buffer size (samples) // globals @@ -1083,6 +1082,11 @@ class AudioReactive : public Usermod { private: #ifdef ARDUINO_ARCH_ESP32 +// HUB75 workaround - audio receive only +#ifdef WLED_ENABLE_HUB75MATRIX +#undef SR_DMTYPE +#define SR_DMTYPE 254 // "network receive only" +#endif #ifndef AUDIOPIN int8_t audioPin = -1; #else @@ -1907,12 +1911,14 @@ class AudioReactive : public Usermod { #ifdef ARDUINO_ARCH_ESP32 - // Reset I2S peripheral for good measure + // Reset I2S peripheral for good measure - not needed in esp-idf v4.4.x and later. + #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 0) i2s_driver_uninstall(I2S_NUM_0); // E (696) I2S: i2s_driver_uninstall(2006): I2S port 0 has not installed #if !defined(CONFIG_IDF_TARGET_ESP32C3) delay(100); periph_module_reset(PERIPH_I2S0_MODULE); // not possible on -C3 #endif + #endif delay(100); // Give that poor microphone some time to setup. #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) @@ -2026,6 +2032,14 @@ class AudioReactive : public Usermod { if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin); break; + case 255: // falls through + case 254: // dummy "network receive only" driver + if (audioSource) delete audioSource; + audioSource = nullptr; + disableSoundProcessing = true; + audioSyncEnabled = AUDIOSYNC_REC; // force udp sound receive mode + break; + #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) // ADC over I2S is only possible on "classic" ESP32 case 0: @@ -2040,18 +2054,16 @@ class AudioReactive : public Usermod { } delay(250); // give microphone enough time to initialise - if (!audioSource) enabled = false; // audio failed to initialise + if (!audioSource && (dmType < 254)) enabled = false; // audio failed to initialise #endif - if (enabled) onUpdateBegin(false); // create FFT task, and initialize network + if (enabled) onUpdateBegin(false); // create FFT task, and initialize network #ifdef ARDUINO_ARCH_ESP32 - if (FFT_Task == nullptr) enabled = false; // FFT task creation failed + if (audioSource && FFT_Task == nullptr) enabled = false; // FFT task creation failed if((!audioSource) || (!audioSource->isInitialized())) { // audio source failed to initialize. Still stay "enabled", as there might be input arriving via UDP Sound Sync - #ifdef WLED_DEBUG - DEBUG_PRINTLN(F("AR: Failed to initialize sound input driver. Please check input PIN settings.")); - #else - USER_PRINTLN(F("AR: Failed to initialize sound input driver. Please check input PIN settings.")); - #endif + + if (dmType < 254) { USER_PRINTLN(F("AR: Failed to initialize sound input driver. Please check input PIN settings."));} + else { USER_PRINTLN(F("AR: No sound input driver configured - network receive only."));} disableSoundProcessing = true; } else { USER_PRINTLN(F("AR: sound input driver initialized successfully.")); @@ -2191,7 +2203,7 @@ class AudioReactive : public Usermod { if (audioSyncEnabled == AUDIOSYNC_REC) disableSoundProcessing = true; // make sure everything is disabled IF in audio Receive mode if (audioSyncEnabled == AUDIOSYNC_SEND) disableSoundProcessing = false; // keep running audio IF we're in audio Transmit mode #ifdef ARDUINO_ARCH_ESP32 - if (!audioSource->isInitialized()) { // no audio source + if (!audioSource || !audioSource->isInitialized()) { // no audio source disableSoundProcessing = true; if (audioSyncEnabled > AUDIOSYNC_SEND) useNetworkAudio = true; } @@ -2404,7 +2416,8 @@ class AudioReactive : public Usermod { if (FFT_Task) { vTaskResume(FFT_Task); connected(); // resume UDP - } else + } else { + if (audioSource) // WLEDMM only create FFT task if we have a valid audio source // xTaskCreatePinnedToCore( // xTaskCreate( // no need to "pin" this task to core #0 xTaskCreateUniversal( @@ -2416,9 +2429,10 @@ class AudioReactive : public Usermod { &FFT_Task // Task handle , 0 // Core where the task should run ); + } } micDataReal = 0.0f; // just to be sure - if (enabled) disableSoundProcessing = false; + if (enabled && audioSource) disableSoundProcessing = false; updateIsRunning = init; #if defined(ARDUINO_ARCH_ESP32) && defined(SR_DEBUG) @@ -2573,7 +2587,7 @@ class AudioReactive : public Usermod { } else { // error during audio source setup infoArr.add(F("not initialized")); - infoArr.add(F(" - check pin settings")); + if (dmType < 254) infoArr.add(F(" - check pin settings")); } } @@ -2880,6 +2894,11 @@ class AudioReactive : public Usermod { #endif oappend(SET_F("dd=addDropdown(ux,'digitalmic:type');")); + #if SR_DMTYPE==254 + oappend(SET_F("addOption(dd,'None - network receive only (⎌)',254);")); + #else + oappend(SET_F("addOption(dd,'None - network receive only',254);")); + #endif #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) #if SR_DMTYPE==0 oappend(SET_F("addOption(dd,'Generic Analog (⎌)',0);")); diff --git a/usermods/audioreactive/audio_source.h b/usermods/audioreactive/audio_source.h index 77111446..f78a3358 100644 --- a/usermods/audioreactive/audio_source.h +++ b/usermods/audioreactive/audio_source.h @@ -37,6 +37,8 @@ #define SRate_t int #endif +constexpr i2s_port_t AR_I2S_PORT = I2S_NUM_0; // I2S port to use (do not change ! I2S_NUM_1 possible but this has limitation -> no MCLK routing, no ADC support) + //#include //#include //#include @@ -326,7 +328,7 @@ class I2SSource : public AudioSource { //DEBUGSR_PRINTF("[AR] I2S: SD=%d, WS=%d, SCK=%d, MCLK=%d\n", i2ssdPin, i2swsPin, i2sckPin, mclkPin); - esp_err_t err = i2s_driver_install(I2S_NUM_0, &_config, 0, nullptr); + esp_err_t err = i2s_driver_install(AR_I2S_PORT, &_config, 0, nullptr); if (err != ESP_OK) { ERRORSR_PRINTF("AR: Failed to install i2s driver: %d\n", err); return; @@ -344,18 +346,18 @@ class I2SSource : public AudioSource { DEBUGSR_PRINTLN(F("AR: I2S#0 driver installed in SLAVE mode.")); } - err = i2s_set_pin(I2S_NUM_0, &_pinConfig); + err = i2s_set_pin(AR_I2S_PORT, &_pinConfig); if (err != ESP_OK) { ERRORSR_PRINTF("AR: Failed to set i2s pin config: %d\n", err); - i2s_driver_uninstall(I2S_NUM_0); // uninstall already-installed driver + i2s_driver_uninstall(AR_I2S_PORT); // uninstall already-installed driver return; } #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) - err = i2s_set_clk(I2S_NUM_0, _sampleRate, I2S_SAMPLE_RESOLUTION, I2S_CHANNEL_MONO); // set bit clocks. Also takes care of MCLK routing if needed. + err = i2s_set_clk(AR_I2S_PORT, _sampleRate, I2S_SAMPLE_RESOLUTION, I2S_CHANNEL_MONO); // set bit clocks. Also takes care of MCLK routing if needed. if (err != ESP_OK) { ERRORSR_PRINTF("AR: Failed to configure i2s clocks: %d\n", err); - i2s_driver_uninstall(I2S_NUM_0); // uninstall already-installed driver + i2s_driver_uninstall(AR_I2S_PORT); // uninstall already-installed driver return; } #endif @@ -364,7 +366,7 @@ class I2SSource : public AudioSource { virtual void deinitialize() { _initialized = false; - esp_err_t err = i2s_driver_uninstall(I2S_NUM_0); + esp_err_t err = i2s_driver_uninstall(AR_I2S_PORT); if (err != ESP_OK) { DEBUGSR_PRINTF("Failed to uninstall i2s driver: %d\n", err); return; @@ -385,7 +387,7 @@ class I2SSource : public AudioSource { I2S_datatype *newSamples = newSampleBuffer; // use global input buffer if (num_samples > I2S_SAMPLES_MAX) num_samples = I2S_SAMPLES_MAX; // protect the buffer from overflow - err = i2s_read(I2S_NUM_0, (void *)newSamples, num_samples * sizeof(I2S_datatype), &bytes_read, portMAX_DELAY); + err = i2s_read(AR_I2S_PORT, (void *)newSamples, num_samples * sizeof(I2S_datatype), &bytes_read, portMAX_DELAY); if (err != ESP_OK) { DEBUGSR_PRINTF("Failed to get samples: %d\n", err); return; @@ -1056,8 +1058,8 @@ class SPH0654 : public I2SSource { I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin); #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) // these registers are only existing in "classic" ESP32 - REG_SET_BIT(I2S_TIMING_REG(I2S_NUM_0), BIT(9)); - REG_SET_BIT(I2S_CONF_REG(I2S_NUM_0), I2S_RX_MSB_SHIFT); + REG_SET_BIT(I2S_TIMING_REG(AR_I2S_PORT), BIT(9)); + REG_SET_BIT(I2S_CONF_REG(AR_I2S_PORT), I2S_RX_MSB_SHIFT); #else #warning FIX ME! Please. #endif From 1fcb15bcc0f86eaffe56c6a9d2b252583c944a52 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 8 Oct 2024 19:02:55 +0200 Subject: [PATCH 066/232] build number 2410080 --- wled00/wled.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/wled.h b/wled00/wled.h index c046d3e3..e3cadc5d 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2410070 +#define VERSION 2410080 // 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 98bdbd1eb2e3c6a07a17a7f966cf1ec0cb83804b Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 8 Oct 2024 21:24:06 +0200 Subject: [PATCH 067/232] AR: trying to improve co-existance with HUB75 DMA bad news: wifi still crashes very frequently when I2S audio is in use. --- usermods/audioreactive/audio_source.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/usermods/audioreactive/audio_source.h b/usermods/audioreactive/audio_source.h index f78a3358..91573956 100644 --- a/usermods/audioreactive/audio_source.h +++ b/usermods/audioreactive/audio_source.h @@ -208,14 +208,23 @@ class I2SSource : public AudioSource { .communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_I2S), //.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, #ifdef WLEDMM_FASTPATH + #ifdef WLED_ENABLE_HUB75MATRIX + .intr_alloc_flags = ESP_INTR_FLAG_IRAM|ESP_INTR_FLAG_LEVEL1, // HUB75 seems to get into trouble if we allocate a higher priority interrupt + .dma_buf_count = 18, // 100ms buffer (128 * dma_buf_count / sampleRate) + #else #if CONFIG_IDF_TARGET_ESP32 && !defined(BOARD_HAS_PSRAM) // still need to test on boards with PSRAM .intr_alloc_flags = ESP_INTR_FLAG_IRAM|ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3, // IRAM flag reduces missed samples #else .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3, // seems to reduce noise #endif .dma_buf_count = 24, // 140ms buffer (128 * dma_buf_count / sampleRate) + #endif #else + #ifdef WLED_ENABLE_HUB75MATRIX + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // HUB75 seems to get into trouble if we allocate a higher priority interrupt + #else .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2, + #endif .dma_buf_count = 8, #endif .dma_buf_len = _blockSize, From 54467473f81a5f62ff48b449297eaa7444aae9c4 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Fri, 11 Oct 2024 21:19:22 +0200 Subject: [PATCH 068/232] HUB75: fix for crash on -S3 * use a fork of the HUB75 with fixed Bus_Parallel16::release() * prevent accessing an invalid display object --- platformio.ini | 3 ++- wled00/bus_manager.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index 07c03129..0e7bf6da 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1072,7 +1072,8 @@ HUB75_build_flags = -D NO_CIE1931 ;; Do not use LED brightness compensation described in CIE 1931. We use FastLED dimming already -D S3_LCD_DIV_NUM=20 ;; Attempt to fix wifi performance issue when panel active with S3 chips -D WLEDMM_SLOWPATH ;; WLEDMM: do not use I2S for driving ws2812 LEDs (HUB75 driver needs I2S#1) -HUB75_lib_deps = https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA.git#aa28e2a9 ;; S3_LCD_DIV_NUM fix +;; HUB75_lib_deps = https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA.git#aa28e2a9 ;; S3_LCD_DIV_NUM fix +HUB75_lib_deps = https://github.com/softhack007/ESP32-HUB75-MatrixPanel-DMA_sh7.git#fix_dangling_pointer ;; S3 bugfix for crash in ~MatrixPanel_I2S_DMA() HUB75_lib_ignore = ESP32 HUB75 LED MATRIX PANEL DMA Display ;; to remove the HUB75 lib dependancy (saves a few bytes) NetDebug_build_flags = diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index ab124b93..f53cfca6 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -916,6 +916,7 @@ uint32_t __attribute__((hot)) BusHub75Matrix::getPixelColorRestored(uint16_t pix void BusHub75Matrix::setBrightness(uint8_t b, bool immediate) { _bri = b; + if (!_valid) return; // if (_bri > 238) _bri=238; // not strictly needed. Enable this line if you see glitches at highest brightness. display->setBrightness(_bri); } @@ -967,7 +968,7 @@ void BusHub75Matrix::cleanup() { USER_PRINTLN("HUB75 output ended."); //if (fourScanPanel != nullptr) delete fourScanPanel; // warning: deleting object of polymorphic class type 'VirtualMatrixPanel' which has non-virtual destructor might cause undefined behavior - delete display; + if (display) delete display; display = nullptr; fourScanPanel = nullptr; if (_ledBuffer != nullptr) free(_ledBuffer); _ledBuffer = nullptr; From 06590890bdee0e252983dc9f9ab595c853608049 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sat, 12 Oct 2024 23:00:35 +0200 Subject: [PATCH 069/232] HUB75: remove driver double-buffer support (obsolete) We use our own memory-optimised buffer rather than the driver's own double-buffer. --- wled00/bus_manager.cpp | 52 +++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index f53cfca6..6b1aeb09 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -529,7 +529,7 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh fourScanPanel = nullptr; _len = 0; - mxconfig.double_buff = false; // Use our own memory-optimised buffer rather than the driver's own double-buffer + mxconfig.double_buff = false; // Use our own memory-optimised buffer rather than the driver's own double-buffer // mxconfig.driver = HUB75_I2S_CFG::ICN2038S; // experimental - use specific shift register driver // mxconfig.driver = HUB75_I2S_CFG::FM6124; // try this driver in case you panel stays dark, or when colors look too pastel @@ -782,9 +782,10 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh delay(24); // experimental DEBUG_PRINT(F("heap usage: ")); DEBUG_PRINTLN(lastHeap - ESP.getFreeHeap()); // Allocate memory and start DMA display - if( not display->begin() ) { + if (display->begin() == false) { USER_PRINTLN("****** MatrixPanel_I2S_DMA !KABOOM! I2S memory allocation failed ***********"); USER_PRINT(F("heap usage: ")); USER_PRINTLN(lastHeap - ESP.getFreeHeap()); + _valid = false; return; } else { @@ -799,28 +800,27 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh if (_ledsDirty) free(_ledsDirty); // should not happen _ledsDirty = (byte*) malloc(getBitArrayBytes(_len)); // create LEDs dirty bits + if (_ledsDirty) setBitArray(_ledsDirty, _len, false); // reset dirty bits - if (_ledsDirty == nullptr) { - display->stopDMAoutput(); - delete display; display = nullptr; - _valid = false; - USER_PRINTLN(F("MatrixPanel_I2S_DMA not started - not enough memory for dirty bits!")); - USER_PRINT(F("heap usage: ")); USER_PRINTLN(lastHeap - ESP.getFreeHeap()); - return; // fail is we cannot get memory for the buffer - } - setBitArray(_ledsDirty, _len, false); // reset dirty bits - - if (mxconfig.double_buff == false) { - #if defined(CONFIG_IDF_TARGET_ESP32S3) && CONFIG_SPIRAM_MODE_OCT && defined(BOARD_HAS_PSRAM) && (defined(WLED_USE_PSRAM) || defined(WLED_USE_PSRAM_JSON)) + #if defined(CONFIG_IDF_TARGET_ESP32S3) && CONFIG_SPIRAM_MODE_OCT && defined(BOARD_HAS_PSRAM) && (defined(WLED_USE_PSRAM) || defined(WLED_USE_PSRAM_JSON)) if (psramFound()) { _ledBuffer = (CRGB*) ps_calloc(_len, sizeof(CRGB)); // create LEDs buffer (initialized to BLACK) } else { _ledBuffer = (CRGB*) calloc(_len, sizeof(CRGB)); // create LEDs buffer (initialized to BLACK) } - #else + #else _ledBuffer = (CRGB*) calloc(_len, sizeof(CRGB)); // create LEDs buffer (initialized to BLACK) - #endif - } + #endif + } + + if ((_ledBuffer == nullptr) || (_ledsDirty == nullptr)) { + // fail is we cannot get memory for the buffer + errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag + USER_PRINTLN(F("MatrixPanel_I2S_DMA not started - not enough memory for leds bufer!")); + cleanup(); // free buffers, and deallocate pins + _valid = false; + USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - ESP.getFreeHeap())); + return; // fail } switch(bc.type) { @@ -851,7 +851,6 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh USER_PRINT(F("MatrixPanel_I2S_DMA ")); USER_PRINTF("%sstarted, width=%u, %u pixels.\n", _valid? "":"not ", _panelWidth, _len); - if (mxconfig.double_buff == true) USER_PRINTLN(F("MatrixPanel_I2S_DMA driver native double-buffering enabled.")); if (_ledBuffer != nullptr) USER_PRINTLN(F("MatrixPanel_I2S_DMA LEDS buffer enabled.")); if (_ledsDirty != nullptr) USER_PRINTLN(F("MatrixPanel_I2S_DMA LEDS dirty bit optimization enabled.")); if ((_ledBuffer != nullptr) || (_ledsDirty != nullptr)) { @@ -859,7 +858,7 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh USER_PRINT((_ledBuffer? _len*sizeof(CRGB) :0) + (_ledsDirty? getBitArrayBytes(_len) :0)); USER_PRINTLN(F(" bytes.")); } - USER_PRINT(F("heap usage: ")); USER_PRINTLN(lastHeap - ESP.getFreeHeap()); + USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - ESP.getFreeHeap())); } void __attribute__((hot)) BusHub75Matrix::setPixelColor(uint16_t pix, uint32_t c) { @@ -874,9 +873,7 @@ void __attribute__((hot)) BusHub75Matrix::setPixelColor(uint16_t pix, uint32_t c } } else { - if ((c == BLACK) && (getBitFromArray(_ledsDirty, pix) == false)) return; // ignore black if pixel is already black - setBitInArray(_ledsDirty, pix, c != BLACK); // dirty = true means "color is not BLACK" - + // no double buffer allocated --> directly draw pixel #ifndef NO_CIE1931 c = unGamma24(c); // to use the driver linear brightness feature, we first need to undo WLED gamma correction #endif @@ -903,7 +900,7 @@ uint32_t BusHub75Matrix::getPixelColor(uint16_t pix) const { if (_ledBuffer) return uint32_t(_ledBuffer[pix].scale8(_bri)) & 0x00FFFFFF; // scale8() is needed to mimic NeoPixelBus, which returns scaled-down colours else - return getBitFromArray(_ledsDirty, pix) ? DARKGREY: BLACK; // just a hack - we only know if the pixel is black or not + return BLACK; // we don't know anything about the pixel } uint32_t __attribute__((hot)) BusHub75Matrix::getPixelColorRestored(uint16_t pix) const { @@ -911,7 +908,7 @@ uint32_t __attribute__((hot)) BusHub75Matrix::getPixelColorRestored(uint16_t pix if (_ledBuffer) return uint32_t(_ledBuffer[pix]) & 0x00FFFFFF; else - return getBitFromArray(_ledsDirty, pix) ? DARKGREY: BLACK; // just a hack - we only know if the pixel is black or not + return BLACK; // we don't know anything about the pixel } void BusHub75Matrix::setBrightness(uint8_t b, bool immediate) { @@ -951,13 +948,6 @@ void __attribute__((hot)) BusHub75Matrix::show(void) { } setBitArray(_ledsDirty, _len, false); // buffer shown - reset all dirty bits } - - if(mxconfig.double_buff) { - display->flipDMABuffer(); // Show the back buffer, set current output buffer to the back (i.e. no longer being sent to LED panels) - // while(!previousBufferFree) delay(1); // experimental - Wait before we allow any writing to the buffer. Stop flicker. - display->clearScreen(); // Now clear the back-buffer - setBitArray(_ledsDirty, _len, false); // dislay buffer is blank - reset all dirty bits - } } void BusHub75Matrix::cleanup() { From d21b9b068788623352a18ec398b4e05ae0193582 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sat, 12 Oct 2024 23:05:08 +0200 Subject: [PATCH 070/232] typo fixo --- wled00/bus_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 6b1aeb09..1b59b50b 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -816,7 +816,7 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh if ((_ledBuffer == nullptr) || (_ledsDirty == nullptr)) { // fail is we cannot get memory for the buffer errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag - USER_PRINTLN(F("MatrixPanel_I2S_DMA not started - not enough memory for leds bufer!")); + USER_PRINTLN(F("MatrixPanel_I2S_DMA not started - not enough memory for leds buffer!")); cleanup(); // free buffers, and deallocate pins _valid = false; USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - ESP.getFreeHeap())); From 0dd036cdced71faa02ad9654288ffdf07a45ed62 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sun, 13 Oct 2024 23:06:50 +0200 Subject: [PATCH 071/232] AR: trying to improve coexistence with HUB75 Hub75 is very memory hungry. So we try to make a bit more RAM available * use 16bit samples when compiling with HUB75 support --> 3KB saved * avoid using aPLL (HUB75 needs it) * move audio buffers from BSS (always allocated) to heap (only allocated when FFTtask runs) --> 10Kb saved * suspend live preview for 6 seconds when out-of-memory --- usermods/audioreactive/audio_reactive.h | 72 ++++++++++++++++++------- usermods/audioreactive/audio_source.h | 11 +++- wled00/ws.cpp | 10 ++++ 3 files changed, 74 insertions(+), 19 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index a6fcbd7c..7c22cf8d 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -284,7 +284,7 @@ static volatile float micReal_max2 = 0.0f; // MicIn data max afte // some prototypes, to ensure consistent interfaces static float mapf(float x, float in_min, float in_max, float out_min, float out_max); // map function for float static float fftAddAvg(int from, int to); // average of several FFT result bins -void FFTcode(void * parameter) __attribute__((noreturn)); // audio processing task: read samples, run FFT, fill GEQ channels from FFT results +void FFTcode(void * parameter); // audio processing task: read samples, run FFT, fill GEQ channels from FFT results static void runMicFilter(uint16_t numSamples, float *sampleBuffer); // pre-filtering of raw samples (band-pass) static void postProcessFFTResults(bool noiseGateOpen, int numberOfChannels, bool i2sFastpath); // post-processing and post-amp of GEQ channels @@ -393,11 +393,11 @@ constexpr uint16_t samplesFFT_2 = 256; // meaningful part of FFT result #define LOG_256 5.54517744f // log(256) // 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 +static float* vReal = nullptr; // FFT sample inputs / freq output - these are our raw result bins +static float* vImag = nullptr; // imaginary parts #ifdef FFT_MAJORPEAK_HUMAN_EAR -static float pinkFactors[samplesFFT] = {0.0f}; // "pink noise" correction factors +static float* pinkFactors = nullptr; // "pink noise" correction factors constexpr float pinkcenter = 23.66; // sqrt(560) - center freq for scaling is 560 hz. constexpr float binWidth = SAMPLE_RATE / (float)samplesFFT; // frequency range of each FFT result bin #endif @@ -414,15 +414,6 @@ constexpr float binWidth = SAMPLE_RATE / (float)samplesFFT; // frequency range o #define sqrt_internal sqrtf // see https://github.com/kosme/arduinoFFT/pull/83 #include -#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 - // Helper functions // float version of map() @@ -460,6 +451,30 @@ constexpr bool skipSecondFFT = true; constexpr bool skipSecondFFT = false; #endif +// allocate FFT sample buffers from heap +static bool alocateFFTBuffers(void) { + #ifdef SR_DEBUG + USER_PRINT(F("\nFree heap ")); USER_PRINTLN(ESP.getFreeHeap()); + #endif + + if (vReal) free(vReal); // should not happen + if (vImag) free(vImag); // should not happen + if ((vReal = (float*) calloc(sizeof(float), samplesFFT)) == nullptr) return false; // calloc or die + if ((vImag = (float*) calloc(sizeof(float), samplesFFT)) == nullptr) return false; +#ifdef FFT_MAJORPEAK_HUMAN_EAR + if (pinkFactors) free(pinkFactors); + if ((pinkFactors = (float*) calloc(sizeof(float), samplesFFT)) == nullptr) return false; +#endif + + #ifdef SR_DEBUG + USER_PRINTLN("\nalocateFFTBuffers() completed successfully."); + USER_PRINT(F("Free heap: ")); USER_PRINTLN(ESP.getFreeHeap()); + USER_PRINT("FFTtask free stack: "); USER_PRINTLN(uxTaskGetStackHighWaterMark(NULL)); + USER_FLUSH(); + #endif + return(true); // success +} + // High-Pass "DC blocker" filter // see https://www.dsprelated.com/freebooks/filters/DC_Blocker.html static void runDCBlocker(uint_fast16_t numSamples, float *sampleBuffer) { @@ -496,9 +511,30 @@ void FFTcode(void * parameter) static bool isFirstRun = false; #ifdef FFT_USE_SLIDING_WINDOW - static float oldSamples[samplesFFT_2] = {0.0f}; // previous 50% of samples + static float* oldSamples = nullptr; // previous 50% of samples static bool haveOldSamples = false; // for sliding window FFT bool usingOldSamples = false; + if (!oldSamples) oldSamples = (float*) calloc(sizeof(float), samplesFFT_2); // allocate on first run + if (!oldSamples) { disableSoundProcessing = true; return; } // no memory -> die +#endif + + bool success = true; + if ((vReal == nullptr) || (vImag == nullptr)) success = alocateFFTBuffers(); // allocate sample buffers on first run + if (success == false) { disableSoundProcessing = true; return; } // no memory -> die + + // create FFT object - we have to do if after allocating buffers +#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) + #if defined(WLED_ENABLE_HUB75MATRIX) && defined(CONFIG_IDF_TARGET_ESP32) + static float* windowWeighingFactors = nullptr; + if (!windowWeighingFactors) windowWeighingFactors = (float*) calloc(sizeof(float), samplesFFT); // cache for FFT windowing factors - use heap + #else + static float windowWeighingFactors[samplesFFT] = {0.0f}; // cache for FFT windowing factors - use global RAM + #endif + static ArduinoFFT FFT = ArduinoFFT( vReal, vImag, samplesFFT, SAMPLE_RATE, windowWeighingFactors); #endif #ifdef FFT_MAJORPEAK_HUMAN_EAR @@ -542,7 +578,7 @@ void FFTcode(void * parameter) #endif // get a fresh batch of samples from I2S - memset(vReal, 0, sizeof(vReal)); // start clean + memset(vReal, 0, sizeof(float) * samplesFFT); // start clean #ifdef FFT_USE_SLIDING_WINDOW uint16_t readOffset; if (haveOldSamples && (doSlidingFFT > 0)) { @@ -635,7 +671,7 @@ void FFTcode(void * parameter) #endif // set imaginary parts to 0 - memset(vImag, 0, sizeof(vImag)); + memset(vImag, 0, sizeof(float) * samplesFFT); #ifdef FFT_USE_SLIDING_WINDOW memcpy(oldSamples, vReal+samplesFFT_2, sizeof(float) * samplesFFT_2); // copy last 50% to buffer (for sliding window FFT) @@ -762,14 +798,14 @@ void FFTcode(void * parameter) FFT_MajPeakSmth = FFT_MajPeakSmth + 0.42 * (FFT_MajorPeak - FFT_MajPeakSmth); // I like this "swooping peak" look } else { // skip second run --> clear fft results, keep peaks - memset(vReal, 0, sizeof(vReal)); + memset(vReal, 0, sizeof(float) * samplesFFT); } #if defined(WLED_DEBUG) || defined(SR_DEBUG) || defined(SR_STATS) haveDoneFFT = true; #endif } else { // noise gate closed - only clear results as FFT was skipped. MIC samples are still valid when we do this. - memset(vReal, 0, sizeof(vReal)); + memset(vReal, 0, sizeof(float) * samplesFFT); FFT_MajorPeak = 1; FFT_Magnitude = 0.001; } diff --git a/usermods/audioreactive/audio_source.h b/usermods/audioreactive/audio_source.h index 91573956..fe065bbf 100644 --- a/usermods/audioreactive/audio_source.h +++ b/usermods/audioreactive/audio_source.h @@ -37,7 +37,8 @@ #define SRate_t int #endif -constexpr i2s_port_t AR_I2S_PORT = I2S_NUM_0; // I2S port to use (do not change ! I2S_NUM_1 possible but this has limitation -> no MCLK routing, no ADC support) +constexpr i2s_port_t AR_I2S_PORT = I2S_NUM_0; // I2S port to use (do not change! I2S_NUM_1 possible but this has + // strong limitations -> no MCLK routing, no ADC support, no PDM support //#include //#include @@ -73,6 +74,11 @@ constexpr i2s_port_t AR_I2S_PORT = I2S_NUM_0; // I2S port to use (do not c // data type requested from the I2S driver - currently we always use 32bit //#define I2S_USE_16BIT_SAMPLES // (experimental) define this to request 16bit - more efficient but possibly less compatible +#if defined(WLED_ENABLE_HUB75MATRIX) && defined(CONFIG_IDF_TARGET_ESP32) + // this is bitter, but necessary to survive + #define I2S_USE_16BIT_SAMPLES +#endif + #ifdef I2S_USE_16BIT_SAMPLES #define I2S_SAMPLE_RESOLUTION I2S_BITS_PER_SAMPLE_16BIT #define I2S_datatype int16_t @@ -301,6 +307,9 @@ class I2SSource : public AudioSource { #if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) if (ESP.getChipRevision() == 0) _config.use_apll = false; // APLL is broken on ESP32 revision 0 #endif + #if defined(WLED_ENABLE_HUB75MATRIX) + _config.use_apll = false; // APLL needed for HUB75 DMA driver ? + #endif #endif if (_i2sMaster == false) { diff --git a/wled00/ws.cpp b/wled00/ws.cpp index 5a7f34eb..df838248 100644 --- a/wled00/ws.cpp +++ b/wled00/ws.cpp @@ -186,6 +186,12 @@ static bool sendLiveLedsWs(uint32_t wsClient) // WLEDMM added "static" AsyncWebSocketClient * wsc = ws.client(wsClient); if (!wsc || wsc->queueLength() > 0) return false; //only send if queue free + #ifdef ARDUINO_ARCH_ESP32 + static unsigned long ws_delay = 0; + if ((ws_delay > 0) && (millis() - ws_delay < 6000)) return false; // out of memory -> suspend for 6 seconds + else ws_delay = 0; + #endif + #ifdef ESP8266 constexpr size_t MAX_LIVE_LEDS_WS = 256U; #else @@ -223,6 +229,10 @@ static bool sendLiveLedsWs(uint32_t wsClient) // WLEDMM added "static" last_err_time = millis(); } errorFlag = ERR_LOW_WS_MEM; + #ifdef ARDUINO_ARCH_ESP32 + ws_delay = millis(); // suspend for next 6 seconds + USER_PRINTLN("out of memory - live preview suspended for 6 seconds."); + #endif return false; //out of memory } uint8_t* buffer = reinterpret_cast(wsBuf.data()); From 68536635d7870c5641768c566330fb5049f4edd7 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 14 Oct 2024 11:25:41 +0200 Subject: [PATCH 072/232] show toast when reboot is needed, or error restart happened less WTF, more information --- usermods/audioreactive/audio_reactive.h | 19 +++++++++++++++++++ wled00/const.h | 4 ++++ wled00/data/index.js | 14 +++++++++++++- wled00/wled.cpp | 3 +++ 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 7c22cf8d..fe084105 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -2859,6 +2859,14 @@ class AudioReactive : public Usermod { JsonObject top = root[FPSTR(_name)]; bool configComplete = !top.isNull(); + // remember previous values + auto oldEnabled = enabled; + auto oldDMType = dmType; + auto oldI2SsdPin = i2ssdPin; + auto oldI2SwsPin = i2swsPin; + auto oldI2SckPin = i2sckPin; + auto oldI2SmclkPin = mclkPin; + configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled); #ifdef ARDUINO_ARCH_ESP32 #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) @@ -2911,6 +2919,17 @@ class AudioReactive : public Usermod { configComplete &= getJsonValue(top["sync"][F("mode")], audioSyncEnabled); configComplete &= getJsonValue(top["sync"][F("check_sequence")], audioSyncSequence); + // WLEDMM notify user when a reboot is necessary + #ifdef ARDUINO_ARCH_ESP32 + if (initDone) { + if ((audioSource != nullptr) && (oldDMType != dmType)) errorFlag = ERR_REBOOT_NEEDED; // changing mic type requires reboot + if ( (audioSource != nullptr) && (enabled==true) + && ((oldI2SsdPin != i2ssdPin) || (oldI2SsdPin != i2ssdPin) || (oldI2SckPin != i2sckPin)) ) errorFlag = ERR_REBOOT_NEEDED; // changing mic pins requires reboot + if ((audioSource != nullptr) && (oldI2SmclkPin != mclkPin)) errorFlag = ERR_REBOOT_NEEDED; // changing MCLK pin requires reboot + if ((oldDMType != dmType) && (oldDMType == 0)) errorFlag = ERR_POWEROFF_NEEDED; // changing from analog mic requires power cycle + if ((oldDMType != dmType) && (dmType == 0)) errorFlag = ERR_POWEROFF_NEEDED; // changing to analog mic requires power cycle + } + #endif return configComplete; } diff --git a/wled00/const.h b/wled00/const.h index 55f9f780..26f31ca1 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -357,6 +357,10 @@ #define ERR_LOW_WS_MEM 35 // WLEDMM: low memory (ws) #define ERR_LOW_AJAX_MEM 36 // WLEDMM: low memory (oappend) #define ERR_LOW_BUF 37 // WLEDMM: low memory (LED buffer from allocLEDs) +#define ERR_SYS_REBOOT 90 // WLEDMM: reboot after error +#define ERR_SYS_BROWNOUT 91 // WLEDMM: reboot after brownout alert +#define ERR_REBOOT_NEEDED 98 // WLEDMM: reboot needed after changing hardware setting +#define ERR_POWEROFF_NEEDED 99 // WLEDMM: power-cycle needed after changing hardware setting // Timer mode types #define NL_MODE_SET 0 //After nightlight time elapsed, set to target brightness diff --git a/wled00/data/index.js b/wled00/data/index.js index 05aaee05..247ab222 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -2011,8 +2011,20 @@ function readState(s,command=false) case 37: errstr = "no memory for LEDs buffer."; break; + case 90: + errstr = "Unexpected Restart. Check serial monitor."; + break; + case 91: + errstr = "Brownout Restart."; + break; + case 98: + errstr = "Please reboot WLED to activate changed settings."; + break; + case 99: + errstr = "Please switch your device off and back on."; + break; } - showToast('Error ' + s.error + ": " + errstr, true); + showToast(((s.error < 33)?'Error ':'Warning ') + s.error + ": " + errstr, (s.error < 35)||(s.error > 90)); } selectedPal = i.pal; diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 3c86c4b0..69f3d44b 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -548,6 +548,9 @@ void WLED::setup() USER_PRINTF("Core#0 %s (%d)", resetCode2Info(core0code).c_str(), core0code); if (core1code > 0) {USER_PRINTF("; Core#1 %s (%d)", resetCode2Info(core1code).c_str(), core1code);} USER_PRINTLN(F(".")); + if ((core0code > 1) && (core0code <= 20) && (core0code != 3) && (core0code != 12) && (core0code != 14)) errorFlag = ERR_SYS_REBOOT; // abnormal reboot + if ((resetReason >= 4) && (resetReason < 10)) errorFlag = ERR_SYS_REBOOT; // abnormal reboot (crash, brownout, watchdog, etc) + if ((resetReason == ESP_RST_BROWNOUT) || (core0code == 15)) errorFlag = ERR_SYS_BROWNOUT; // brownout detected // WLEDMM end USER_PRINT(F("FLASH: ")); USER_PRINT((ESP.getFlashChipSize()/1024)/1024); From db983d80ca95ae7e5b7511053fde1dd2d191106e Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 14 Oct 2024 11:34:56 +0200 Subject: [PATCH 073/232] fir for 8266 build --- usermods/audioreactive/audio_reactive.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index fe084105..6a7aa58f 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -2859,6 +2859,7 @@ class AudioReactive : public Usermod { JsonObject top = root[FPSTR(_name)]; bool configComplete = !top.isNull(); +#ifdef ARDUINO_ARCH_ESP32 // remember previous values auto oldEnabled = enabled; auto oldDMType = dmType; @@ -2866,6 +2867,7 @@ class AudioReactive : public Usermod { auto oldI2SwsPin = i2swsPin; auto oldI2SckPin = i2sckPin; auto oldI2SmclkPin = mclkPin; +#endif configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled); #ifdef ARDUINO_ARCH_ESP32 From 3fa5d0c5ba5f8cb39a7e1c2506616af1869f7921 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 14 Oct 2024 13:05:27 +0200 Subject: [PATCH 074/232] HUB75 on S3: workaround for not working runtime reconfiguration * instead of deleting the driver object, we keep a "secret" reference and re-use it * prevent having more than one HUB75 object --- wled00/bus_manager.cpp | 158 ++++++++++++++++++++++++++++++++--------- wled00/bus_manager.h | 12 ++-- 2 files changed, 131 insertions(+), 39 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 1b59b50b..baf73cfe 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -522,19 +522,35 @@ void BusNetwork::cleanup() { #ifdef WLED_ENABLE_HUB75MATRIX #warning "HUB75 driver enabled (experimental)" +// BusHub75Matrix "global" variables (static members) +MatrixPanel_I2S_DMA* BusHub75Matrix::activeDisplay = nullptr; +VirtualMatrixPanel* BusHub75Matrix::activeFourScanPanel = nullptr; +HUB75_I2S_CFG BusHub75Matrix::activeMXconfig = HUB75_I2S_CFG(); +uint8_t BusHub75Matrix::activeType = 0; +uint8_t BusHub75Matrix::instanceCount = 0; + BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { + MatrixPanel_I2S_DMA* display = nullptr; + VirtualMatrixPanel* fourScanPanel = nullptr; + HUB75_I2S_CFG mxconfig; size_t lastHeap = ESP.getFreeHeap(); _valid = false; - fourScanPanel = nullptr; _len = 0; + // allow exactly one instance + if (instanceCount > 0) { + USER_PRINTLN("****** MatrixPanel_I2S_DMA !KABOOM! already active - preventing attempt to create more than one driver instance."); + return; + } + mxconfig.double_buff = false; // Use our own memory-optimised buffer rather than the driver's own double-buffer // mxconfig.driver = HUB75_I2S_CFG::ICN2038S; // experimental - use specific shift register driver // mxconfig.driver = HUB75_I2S_CFG::FM6124; // try this driver in case you panel stays dark, or when colors look too pastel - // mxconfig.latch_blanking = 3; + // mxconfig.latch_blanking = 1; // needed for some ICS panels + // mxconfig.latch_blanking = 3; // use in case you see gost images // mxconfig.i2sspeed = HUB75_I2S_CFG::HZ_10M; // experimental - 5MHZ should be enugh, but colours looks slightly better at 10MHz // mxconfig.min_refresh_rate = 90; mxconfig.clkphase = false; // can help in case that the leftmost column is invisible, or pixels on the right side "bleeds out" to the left. @@ -745,11 +761,50 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh USER_PRINTF("MatrixPanel_I2S_DMA config - %ux%u (type %u) length: %u, %u bits/pixel.\n", mxconfig.mx_width, mxconfig.mx_height, bc.type, mxconfig.chain_length, mxconfig.getPixelColorDepthBits() * 3); DEBUG_PRINT(F("Free heap: ")); DEBUG_PRINTLN(ESP.getFreeHeap()); lastHeap = ESP.getFreeHeap(); + // check if we can re-use the existing display driver + if (activeDisplay) { + if ( (memcmp(&(activeMXconfig.gpio), &(mxconfig.gpio), sizeof(mxconfig.gpio)) != 0) // other pins? + || (activeMXconfig.chain_length != mxconfig.chain_length) // other chain length? + || (activeMXconfig.mx_width != mxconfig.mx_width) || (activeMXconfig.mx_height != mxconfig.mx_height) // other size? + || (bc.type != activeType) // different panel type ? + || (activeMXconfig.clkphase != mxconfig.clkphase) // different driver options ? + || (activeMXconfig.latch_blanking != mxconfig.latch_blanking) + || (activeMXconfig.i2sspeed != mxconfig.i2sspeed) + || (activeMXconfig.driver != mxconfig.driver) + || (activeMXconfig.min_refresh_rate != mxconfig.min_refresh_rate) + || (activeMXconfig.getPixelColorDepthBits() != mxconfig.getPixelColorDepthBits()) ) + { + // not the same as before - delete old driver + DEBUG_PRINTLN("MatrixPanel_I2S_DMA deleting old driver!"); + activeDisplay->stopDMAoutput(); + delay(28); + //#if !defined(CONFIG_IDF_TARGET_ESP32S3) // prevent crash + delete activeDisplay; + //#endif + activeDisplay = nullptr; + activeFourScanPanel = nullptr; + #if defined(CONFIG_IDF_TARGET_ESP32S3) // runtime reconfiguration is not working on -S3 + USER_PRINTLN("\n\n****** MatrixPanel_I2S_DMA !KABOOM WARNING! Reboot needed to change driver options ***********\n"); + errorFlag = ERR_REBOOT_NEEDED; + #endif + } + } + // OK, now we can create our matrix object - display = new MatrixPanel_I2S_DMA(mxconfig); + bool newDisplay = false; // true when the previous display object wasn't re-used + if (!activeDisplay) { + display = new MatrixPanel_I2S_DMA(mxconfig); // create new matrix object + newDisplay = true; + } else { + display = activeDisplay; // continue with existing matrix object + fourScanPanel = activeFourScanPanel; + } + if (display == nullptr) { USER_PRINTLN("****** MatrixPanel_I2S_DMA !KABOOM! driver allocation failed ***********"); - USER_PRINT(F("heap usage: ")); USER_PRINTLN(lastHeap - ESP.getFreeHeap()); + activeDisplay = nullptr; + activeFourScanPanel = nullptr; + USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - ESP.getFreeHeap())); return; } @@ -776,21 +831,23 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh USER_PRINTLN("MatrixPanel_I2S_DMA created"); // let's adjust default brightness - display->setBrightness8(25); // range is 0-255, 0 - 0%, 255 - 100% + //display->setBrightness8(25); // range is 0-255, 0 - 0%, 255 - 100% // [setBrightness()] Tried to set output brightness before begin() _bri = 25; delay(24); // experimental - DEBUG_PRINT(F("heap usage: ")); DEBUG_PRINTLN(lastHeap - ESP.getFreeHeap()); + DEBUG_PRINT(F("heap usage: ")); DEBUG_PRINTLN(int(lastHeap - ESP.getFreeHeap())); // Allocate memory and start DMA display - if (display->begin() == false) { + if (newDisplay && (display->begin() == false)) { USER_PRINTLN("****** MatrixPanel_I2S_DMA !KABOOM! I2S memory allocation failed ***********"); - USER_PRINT(F("heap usage: ")); USER_PRINTLN(lastHeap - ESP.getFreeHeap()); + USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - ESP.getFreeHeap())); _valid = false; return; } else { - USER_PRINTLN("MatrixPanel_I2S_DMA begin ok"); - USER_PRINT(F("heap usage: ")); USER_PRINTLN(lastHeap - ESP.getFreeHeap()); + if (newDisplay) { USER_PRINTLN("MatrixPanel_I2S_DMA begin, started ok"); } + else { USER_PRINTLN("MatrixPanel_I2S_DMA begin, using existing display."); } + + USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - ESP.getFreeHeap())); delay(18); // experiment - give the driver a moment (~ one full frame @ 60hz) to settle _valid = true; display->clearScreen(); // initially clear the screen buffer @@ -826,19 +883,19 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh switch(bc.type) { case 105: USER_PRINTLN("MatrixPanel_I2S_DMA FOUR_SCAN_32PX_HIGH - 32x32"); - fourScanPanel = new VirtualMatrixPanel((*display), 1, 1, 32, 32); + if (!fourScanPanel) fourScanPanel = new VirtualMatrixPanel((*display), 1, 1, 32, 32); fourScanPanel->setPhysicalPanelScanRate(FOUR_SCAN_32PX_HIGH); fourScanPanel->setRotation(0); break; case 106: USER_PRINTLN("MatrixPanel_I2S_DMA FOUR_SCAN_32PX_HIGH - 64x32"); - fourScanPanel = new VirtualMatrixPanel((*display), 1, 1, 64, 32); + if (!fourScanPanel) fourScanPanel = new VirtualMatrixPanel((*display), 1, 1, 64, 32); fourScanPanel->setPhysicalPanelScanRate(FOUR_SCAN_32PX_HIGH); fourScanPanel->setRotation(0); break; case 107: USER_PRINTLN("MatrixPanel_I2S_DMA FOUR_SCAN_64PX_HIGH"); - fourScanPanel = new VirtualMatrixPanel((*display), 1, 1, 64, 64); + if (!fourScanPanel) fourScanPanel = new VirtualMatrixPanel((*display), 1, 1, 64, 64); fourScanPanel->setPhysicalPanelScanRate(FOUR_SCAN_64PX_HIGH); fourScanPanel->setRotation(0); break; @@ -858,6 +915,15 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh USER_PRINT((_ledBuffer? _len*sizeof(CRGB) :0) + (_ledsDirty? getBitArrayBytes(_len) :0)); USER_PRINTLN(F(" bytes.")); } + + if (_valid) { + // config is active, copy to global + activeType = bc.type; + activeDisplay = display; + activeFourScanPanel = fourScanPanel; + if (newDisplay) memcpy(&activeMXconfig, &mxconfig, sizeof(mxconfig)); + } + instanceCount++; USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - ESP.getFreeHeap())); } @@ -874,6 +940,8 @@ void __attribute__((hot)) BusHub75Matrix::setPixelColor(uint16_t pix, uint32_t c } else { // no double buffer allocated --> directly draw pixel + MatrixPanel_I2S_DMA* display = BusHub75Matrix::activeDisplay; + VirtualMatrixPanel* fourScanPanel = BusHub75Matrix::activeFourScanPanel; #ifndef NO_CIE1931 c = unGamma24(c); // to use the driver linear brightness feature, we first need to undo WLED gamma correction #endif @@ -914,16 +982,21 @@ uint32_t __attribute__((hot)) BusHub75Matrix::getPixelColorRestored(uint16_t pix void BusHub75Matrix::setBrightness(uint8_t b, bool immediate) { _bri = b; if (!_valid) return; + MatrixPanel_I2S_DMA* display = BusHub75Matrix::activeDisplay; // if (_bri > 238) _bri=238; // not strictly needed. Enable this line if you see glitches at highest brightness. - display->setBrightness(_bri); + if ((_bri > 253) && (activeMXconfig.latch_blanking < 2)) _bri=253; // prevent glitches at highest brightness. + if (display) display->setBrightness(_bri); } void __attribute__((hot)) BusHub75Matrix::show(void) { if (!_valid) return; + MatrixPanel_I2S_DMA* display = BusHub75Matrix::activeDisplay; + if (!display) return; display->setBrightness(_bri); if (_ledBuffer) { // write out buffered LEDs + VirtualMatrixPanel* fourScanPanel = BusHub75Matrix::activeFourScanPanel; bool isFourScan = (fourScanPanel != nullptr); //if (isFourScan) fourScanPanel->setRotation(0); unsigned height = isFourScan ? fourScanPanel->height() : display->height(); @@ -951,38 +1024,53 @@ void __attribute__((hot)) BusHub75Matrix::show(void) { } void BusHub75Matrix::cleanup() { - if (display && _valid) display->stopDMAoutput(); // terminate DMA driver (display goes black) - _valid = false; - _panelWidth = 0; - deallocatePins(); - USER_PRINTLN("HUB75 output ended."); + MatrixPanel_I2S_DMA* display = BusHub75Matrix::activeDisplay; + VirtualMatrixPanel* fourScanPanel = BusHub75Matrix::activeFourScanPanel; + if (display) display->clearScreen(); +#if !defined(CONFIG_IDF_TARGET_ESP32S3) // S3: don't stop, as we want to re-use the driver later + if (display && _valid) display->stopDMAoutput(); // terminate DMA driver (display goes black) + _panelWidth = 0; + USER_PRINTLN("HUB75 output ended."); +#else + USER_PRINTLN("HUB75 output paused."); +#endif + + _valid = false; + deallocatePins(); //if (fourScanPanel != nullptr) delete fourScanPanel; // warning: deleting object of polymorphic class type 'VirtualMatrixPanel' which has non-virtual destructor might cause undefined behavior +#if !defined(CONFIG_IDF_TARGET_ESP32S3) // S3: don't delete, as we want to re-use the driver later if (display) delete display; - display = nullptr; - fourScanPanel = nullptr; + activeDisplay = nullptr; + activeFourScanPanel = nullptr; + USER_PRINTLN("HUB75 deleted."); +#else + USER_PRINTLN("HUB75 cleanup done."); +#endif + + if (instanceCount > 0) instanceCount--; if (_ledBuffer != nullptr) free(_ledBuffer); _ledBuffer = nullptr; if (_ledsDirty != nullptr) free(_ledsDirty); _ledsDirty = nullptr; } void BusHub75Matrix::deallocatePins() { - pinManager.deallocatePin(mxconfig.gpio.r1, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.g1, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.b1, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.r2, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.g2, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.b2, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.r1, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.g1, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.b1, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.r2, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.g2, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.b2, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.lat, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.oe, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.clk, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.lat, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.oe, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.clk, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.a, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.b, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.c, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.d, PinOwner::HUB75); - pinManager.deallocatePin(mxconfig.gpio.e, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.a, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.b, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.c, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.d, PinOwner::HUB75); + pinManager.deallocatePin(activeMXconfig.gpio.e, PinOwner::HUB75); } #endif diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 677e71de..350a5f3f 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -388,7 +388,7 @@ class BusHub75Matrix : public Bus { void setBrightness(uint8_t b, bool immediate) override; uint8_t getPins(uint8_t* pinArray) const override { - pinArray[0] = mxconfig.chain_length; + pinArray[0] = activeMXconfig.chain_length; return 1; } // Fake value due to keep finaliseInit happy @@ -401,12 +401,16 @@ class BusHub75Matrix : public Bus { } private: - MatrixPanel_I2S_DMA *display = nullptr; - VirtualMatrixPanel *fourScanPanel = nullptr; - HUB75_I2S_CFG mxconfig; unsigned _panelWidth = 0; CRGB *_ledBuffer = nullptr; byte *_ledsDirty = nullptr; + // C++ dirty trick: private static variables are actually _not_ part of the class (however only visibile to class instances). + // These variables persist when BusHub75Matrix gets deleted. + static MatrixPanel_I2S_DMA *activeDisplay; // active display object + static VirtualMatrixPanel *activeFourScanPanel; // active fourScan object + static HUB75_I2S_CFG activeMXconfig; // last used mxconfig + static uint8_t activeType; // last used type + static uint8_t instanceCount; // active instances - 0 or 1 }; #endif From d66720ade818e8a0b66731b2067be580328e9c2b Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 14 Oct 2024 13:19:24 +0200 Subject: [PATCH 075/232] HUB75 128x64 (untested) 128x64 panels are normally "64x64 with chain length 2". But some newer ones appear to be native "128x64" --- wled00/bus_manager.cpp | 14 ++++++++++++++ wled00/data/settings_leds.htm | 2 ++ 2 files changed, 16 insertions(+) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index baf73cfe..79000b0f 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -584,6 +584,10 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh mxconfig.mx_width = 64; mxconfig.mx_height = 64; break; + case 104: // untested + mxconfig.mx_width = 128; + mxconfig.mx_height = 64; + break; case 105: mxconfig.mx_width = 32 * 2; mxconfig.mx_height = 32 / 2; @@ -596,6 +600,10 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh mxconfig.mx_width = 64 * 2; mxconfig.mx_height = 64 / 2; break; + case 108: // untested + mxconfig.mx_width = 128 * 2; + mxconfig.mx_height = 64 / 2; + break; } #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2)// classic esp32, or esp32-s2: reduce bitdepth for large panels @@ -899,6 +907,12 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh fourScanPanel->setPhysicalPanelScanRate(FOUR_SCAN_64PX_HIGH); fourScanPanel->setRotation(0); break; + case 108: // untested + USER_PRINTLN("MatrixPanel_I2S_DMA 128x64 FOUR_SCAN_64PX_HIGH"); + if (!fourScanPanel) fourScanPanel = new VirtualMatrixPanel((*display), 1, 1, 128, 64); + fourScanPanel->setPhysicalPanelScanRate(FOUR_SCAN_64PX_HIGH); + fourScanPanel->setRotation(0); + break; } if (_valid) { diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 1fd0e4ba..ef3671b5 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -383,9 +383,11 @@ ${i+1}: + +
Color Order:
Color Order: