diff --git a/.gitignore b/.gitignore index 5a928325..41341648 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ .pioenvs .piolibdeps .vscode +.vscode/extensions.json esp01-update.sh platformio_override.ini diff --git a/CHANGELOG.md b/CHANGELOG.md index 6369804d..fc4d034d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## [WLED upstream](https://github.com/Aircoookie/WLED/tree/0_14_1) changelog +## [WLED upstream](https://github.com/wled/WLED/tree/0_14_1) changelog #### Build 2403290 - WLED 0.14.3 release diff --git a/package-lock.json b/package-lock.json index 74a18035..796f683b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "wled", - "version": "14.5.1-dev", + "version": "14.7.0-dev", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "wled", - "version": "14.5.1-dev", + "version": "14.7.0-dev", "license": "EUPL-1.2", "dependencies": { "clean-css": "^4.2.3", diff --git a/package.json b/package.json index 2e826712..9487b088 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wled", - "version": "14.5.1-dev", + "version": "14.7.0-dev", "description": "Tools for WLED project", "main": "tools/cdata.js", "directories": { @@ -13,14 +13,14 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/MoonModules/WLED.git" + "url": "git+https://github.com/MoonModules/WLED-MM.git" }, "author": "", "license": "EUPL-1.2", "bugs": { - "url": "https://github.com/MoonModules/WLED/issues" + "url": "https://github.com/MoonModules/WLED-MM/issues" }, - "homepage": "https://github.com/MoonModules/WLED#readme", + "homepage": "https://github.com/MoonModules/WLED-MM#readme", "dependencies": { "clean-css": "^4.2.3", "html-minifier-terser": "^5.1.1", diff --git a/pio-scripts/conditional_usb_mode.py b/pio-scripts/conditional_usb_mode.py new file mode 100644 index 00000000..cb737300 --- /dev/null +++ b/pio-scripts/conditional_usb_mode.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +""" +Conditional USB Mode Script for WLED-MM + +This script automatically manages ARDUINO_USB_MODE based on the build context: +- For development builds: ARDUINO_USB_MODE=1 (allows USB debugging) +- For release builds (CI): ARDUINO_USB_MODE is removed (prevents hanging, allows normal boot) + +The script detects release builds by checking for the WLED_RELEASE environment variable +which is set to True in the GitHub Actions CI workflow. + +CRITICAL: This change only applies to boards with USB-OTG (ARDUINO_USB_CDC_ON_BOOT=1). +For boards with classical UART-to-USB chips (ARDUINO_USB_CDC_ON_BOOT=0), +ARDUINO_USB_MODE=1 is harmless and left unchanged. + +IMPORTANT: We remove ARDUINO_USB_MODE entirely for release builds rather than setting +it to 0, because ARDUINO_USB_MODE=0 breaks Serial functionality when CDC_ON_BOOT=1. +When ARDUINO_USB_MODE is undefined, the framework uses appropriate defaults. +""" +## This script was created with the help of an AI, reviewed and tested by @softhack007 + +Import('env') +import os + +def has_cdc_on_boot_enabled(env): + """ + Check if ARDUINO_USB_CDC_ON_BOOT is set to 1 in the build configuration. + + Returns True if CDC_ON_BOOT=1, False otherwise. + This is used to identify boards with USB-OTG (native USB) vs UART-to-USB chips. + """ + build_unflags = env.get('BUILD_UNFLAGS', []) + build_flags = env.get('BUILD_FLAGS', []) + cpp_defines = env.get('CPPDEFINES', []) + + # Check in raw build unflags - unflags have priority over flags + for unflag in build_unflags: + if isinstance(unflag, str) and 'ARDUINO_USB_CDC_ON_BOOT=1' in unflag: + return False + + # Check in CPPDEFINES + for define in cpp_defines: + if isinstance(define, (list, tuple)) and len(define) == 2: + if define[0] == 'ARDUINO_USB_CDC_ON_BOOT' and define[1] == '1': + return True + + # # Check in raw build flags - results can be misleading, because this ignores build_unflags + for flag in build_flags: + if isinstance(flag, str) and 'ARDUINO_USB_CDC_ON_BOOT=1' in flag: + return True + + return False + +def has_usb_mode_enabled(env): + """ + Check if ARDUINO_USB_MODE is set to 1 in the build configuration. + + Returns True if USB_MODE=1, False otherwise. + """ + build_unflags = env.get('BUILD_UNFLAGS', []) + build_flags = env.get('BUILD_FLAGS', []) + cpp_defines = env.get('CPPDEFINES', []) + # Check in raw build unflags - unflags have priority over flags + for unflag in build_unflags: + if isinstance(unflag, str) and 'ARDUINO_USB_MODE=1' in unflag: + return False + + # Check in CPPDEFINES + for define in cpp_defines: + if isinstance(define, (list, tuple)) and len(define) == 2: + if define[0] == 'ARDUINO_USB_MODE' and define[1] == '1': + return True + + # Check in raw build flags - results can be misleading, because this ignores build_unflags + for flag in build_flags: + if isinstance(flag, str) and 'ARDUINO_USB_MODE=1' in flag: + return True + + return False + +def conditional_usb_mode(env): + """ + Conditionally manage ARDUINO_USB_MODE based on build context. + + For ESP32-C3, ESP32-S2, and ESP32-S3 variants with USB-OTG (CDC_ON_BOOT=1): + - Development builds: ARDUINO_USB_MODE=1 (default, good for debugging) + - Release builds: ARDUINO_USB_MODE removed (prevents hanging, preserves Serial functionality) + + For boards with classical UART-to-USB chip (CDC_ON_BOOT=0): + - ARDUINO_USB_MODE=1 is harmless and left unchanged + + Note: We remove the flag entirely rather than setting to 0, because + ARDUINO_USB_MODE=0 breaks Serial functionality with CDC_ON_BOOT=1. + """ + + # Check if this is a release build (CI sets WLED_RELEASE=True) + is_release_build = os.environ.get('WLED_RELEASE', '').lower() in ('true', '1', 'yes') + + if is_release_build: + # Check if this board uses USB-OTG (CDC_ON_BOOT=1) + if not has_cdc_on_boot_enabled(env): + print("WLED Release build detected - board uses UART-to-USB chip (CDC_ON_BOOT=0)") + print(" Keeping ARDUINO_USB_MODE=1 (harmless for UART-to-USB boards)") + return + + print("WLED Release build detected - board uses USB-OTG (CDC_ON_BOOT=1)") + print(" Removing ARDUINO_USB_MODE definition for production") + + # Check if ARDUINO_USB_MODE=1 is present + if has_usb_mode_enabled(env): + # Remove ARDUINO_USB_MODE entirely - don't set it to 0 as that breaks Serial + # When undefined, the framework uses appropriate defaults based on CDC_ON_BOOT + env.Append(BUILD_UNFLAGS=["-DARDUINO_USB_MODE=1"]) + print(f" Removed ARDUINO_USB_MODE definition (was 1)") + + else: + # Development build + has_usb_mode = has_usb_mode_enabled(env) + has_cdc_boot = has_cdc_on_boot_enabled(env) + + if has_usb_mode and has_cdc_boot: + print("Development build detected - keeping ARDUINO_USB_MODE=1 for USB-OTG debugging") + # Warning in orange/yellow using ANSI color codes + print("\033[93m WARNING: This build is NOT suitable for production devices!\033[0m") + print("\033[93m Production builds require WLED_RELEASE=True environment variable.\033[0m") + elif has_cdc_boot: + # CDC_ON_BOOT=1 present but not USB_MODE=1 + print("Development build detected - USB-OTG enabled, but ARDUINO_USB_MODE=1 missing for debugging") + elif has_usb_mode: + # USB_MODE=1 present but not CDC_ON_BOOT=1 (UART-to-USB board) + print("Development build detected - board uses UART-to-USB chip") + # If neither flag is present, don't print anything + +# Apply the conditional USB mode logic +conditional_usb_mode(env) diff --git a/pio-scripts/output_bins.py b/pio-scripts/output_bins.py index 3a55ced8..6b5ed337 100644 --- a/pio-scripts/output_bins.py +++ b/pio-scripts/output_bins.py @@ -49,7 +49,7 @@ def wledmm_print_build_info(env): found = False for item in all_flags: - if 'WLED_RELEASE_NAME' in item[0] or 'WLED_VERSION' in item[0] or 'ARDUINO_USB_CDC_ON_BOOT' in item[0]: + if 'WLED_RELEASE_NAME' in item[0] or 'WLED_VERSION' in item[0] or 'ARDUINO_USB_CDC_ON_BOOT' in item[0] or 'ARDUINO_USB_MODE' in item[0]: if first: print("\nUsermods and Features:") print_my_item(item) first = False diff --git a/platformio.ini b/platformio.ini index 96c06b9e..cd2e7e41 100644 --- a/platformio.ini +++ b/platformio.ini @@ -73,22 +73,23 @@ default_envs = ; wemos_shield_esp32_16MB_SPM1423_XL ; wemos_shield_esp32_16MB_LineIn_M ;; - esp32_4MB_V4_S ;; experimental; HUB75 supported - esp32_4MB_V4_HUB75_forum ;; experimental; HUB75 supported (forum pinout) - esp32_16MB_V4_S ;; experimental - optimized for speed; HUB75 supported - esp32_16MB_V4_M ;; experimental; HUB75 supported - ; esp32_16MB_V4_M_debug ;; experimental - ; esp32_pico_4MB_V4_S ;; experimental - may work better in case you experience wifi connectivity problems - ;; - esp32_4MB_PSRAM_S ;; WROVER experimental; HUB75 supported + esp32dev_compat ;; legacy + esp32_pico_4MB_V4_S ;; PICO D4 board + esp32_4MB_V4_S ;; esp32 4MB - HUB75 supported + esp32_4MB_V4_HUB75_forum ;; esp32 4MB - HUB75 supported (forum pinout) + esp32_4MB_M_eth ;; esp32 4MB with ethernet support ; esp32_4MB_PSRAM_REV3_S ;; WROVER-E experimental, optimized for WROVER-E with "revision3" chip + esp32_4MB_PSRAM_S ;; esp32 WROVER with PSRAM; HUB75 supported + esp32_16MB_V4_S ;; esp32 16MB - HUB75 supported, optimized for speed + esp32_16MB_V4_M ;; esp32 16MB - HUB75 supported + esp32_16MB_V4_M_debug ;; esp32 16MB - for out-of-the-box debugging ;; - esp32S3_4MB_S ;; for HD-WF2 (HUB75 supported) + esp32S3_4MB_S ;; experimental, for HD-WF2 (HUB75 supported) esp32S3_8MB_S ;; experimental, optimized for speed, HUB75 supported - esp32S3_8MB_M ;; HUB75 supported + ;; esp32S3_8MB_M ;; HUB75 supported esp32S3_4MB_PSRAM_S ;; for lolin s3 mini, S3 zero, S3 super mini - optimized for speed (no HUB75 support) esp32S3_4MB_PSRAM_M ;; for lolin s3 mini, S3 zero, S3 super mini (no HUB75 support) - esp32S3_8MB_PSRAM_M ;; experiemental; HUB75 supported + esp32S3_8MB_PSRAM_M ;; for S3 with 8MB flash, HUB75 supported esp32S3_16MB_PSRAM_M_HUB75 ;; for S3 with 16MB flash, HUB75 supported (MOONHUB HUB75 adapter board) esp32S3_WROOM-2_M ;; for S3 WROOM-2; HUB75 supported ;; @@ -220,6 +221,7 @@ ldscript_16m14m = eagle.flash.16m14m.ld extra_scripts = pre:pio-scripts/set_version.py pre:pio-scripts/build-html.py + pre:pio-scripts/conditional_usb_mode.py post:pio-scripts/output_bins.py post:pio-scripts/strip-floats.py pre:pio-scripts/user_config_copy.py @@ -246,12 +248,12 @@ upload_speed = 115200 # ------------------------------------------------------------------------------ lib_compat_mode = strict lib_deps = - fastled/FastLED @ 3.6.0 - ;; fastled/FastLED @ 3.7.1 + ;;fastled/FastLED @ 3.6.0 ;; default from upstream WLED + fastled/FastLED @ 3.7.1 ;; needed to prevent compiler errors when using newer framework versions ;; https://github.com/softhack007/FastLED.git#ESP32-C6 ;; patched version needed for -C6 IRremoteESP8266 @ 2.8.2 ;;makuna/NeoPixelBus @ 2.7.5 ;; WLEDMM will be added in board specific sections - https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.4.0 + https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.4.2 bitbank2/AnimatedGIF@^1.4.7 https://github.com/Aircoookie/GifDecoder#bc3af18 #For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line @@ -325,9 +327,36 @@ platform_packages_compat = platformio/tool-esptoolpy #@ ~1.30000.0 +;; old V3 platform for esp32. Can be a fallback option when hitting flash size limits +[esp32_legacy] +platform = espressif32@3.5.0 +platform_packages = framework-arduinoespressif32 @ https://github.com/Aircoookie/arduino-esp32.git#1.0.6.4 +build_unflags = ${common.build_unflags} + -Wshadow=compatible-local ;; not supported by older compilers +build_flags = -g + -Wno-unused-variable -Wno-unused-function ;; removes noise + -DARDUINO_ARCH_ESP32 + -D CONFIG_ASYNC_TCP_USE_WDT=0 + -D CONFIG_ASYNC_TCP_TASK_STACK_SIZE=9472 ;; WLEDMM increase stack by 1.25Kb, as audioreactive needs bigger SETTINGS_STACK_BUF_SIZE + -D CONFIG_ASYNC_TCP_STACK_SIZE=9472 + -D LOROL_LITTLEFS ;; use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x + -DCONFIG_LITTLEFS_FOR_IDF_3_2 -DLFS_THREADSAFE +lib_deps = + esp32async/AsyncTCP @ 3.4.7 + ; https://github.com/lorol/LITTLEFS.git + https://github.com/softhack007/LITTLEFS-threadsafe.git#master ;; WLEDMM specific: patched version of lorol LittleFS with improved thread safety + makuna/NeoPixelBus @ 2.7.5 + ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental + ${env.lib_deps} +board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs +default_partitions = ${esp32.default_partitions} ;; backwards compatibility +board_build.f_flash = 80000000L +board_build.flash_mode = dout ;; avoid dio/quot/qio - these are broken in arduino-esp32 1.0.6.x + +;; standard platform for esp32 [esp32] -platform = ${esp32_idf_V4.platform} +platform = ${esp32_idf_V4.platform} ;; default = tasmota (= default from esp32_idf_V4) platform_packages = ${esp32_idf_V4.platform_packages} build_flags = ${esp32_idf_V4.build_flags} @@ -341,13 +370,19 @@ large_partitions = tools/WLED_ESP32_8MB.csv extreme_partitions = tools/WLED_ESP32_16MB_9MB_FS.csv lib_deps = ${esp32_idf_V4.lib_deps} +monitor_filters = esp32_exception_decoder ;; Compatibility with upstream --> you should prefer using ${common_mm.build_flags_S} and ${common_mm.lib_deps_S} AR_build_flags = ${common_mm.AR_build_flags} AR_lib_deps = ${common_mm.AR_lib_deps} ;; optimized version, 10% faster on -S2/-C3 +board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs ;; WLEDMM begin +;; tasmota platform - reduces firmaware size by ~280KB +platformTasmota = https://github.com/tasmota/platform-espressif32/releases/download/2023.06.02/platform-espressif32.zip ;; Tasmota Arduino Core 2.0.9 with IPv6 support, based on IDF 4.4.4 +platform_packagesTasmota = + ;; ** For compiling with latest Frameworks (IDF4.4.x and arduino-esp32 v2.0.x) ** ;;; previous standard V4 platform platformV4_pre = espressif32@5.2.0 @@ -366,6 +401,7 @@ build_flagsV4 = -g -DCONFIG_LITTLEFS_FOR_IDF_3_2 -DLFS_THREADSAFE -D CONFIG_ASYNC_TCP_USE_WDT=0 -D CONFIG_ASYNC_TCP_TASK_STACK_SIZE=9472 ;; WLEDMM increase stack by 1.25Kb, as audioreactive needs bigger SETTINGS_STACK_BUF_SIZE + -D CONFIG_ASYNC_TCP_STACK_SIZE=9472 ;; *sigh* newer asyncTCP uses this instead of .._TASK_.. ; -D WLEDMM_TWOPATH ;; use I2S1 as the second bus --> slightly faster on some setups ; -D WLEDMM_SLOWPATH ;; don't use I2S for LED bus ; -DARDUINO_USB_CDC_ON_BOOT=0 ;; mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3 @@ -373,7 +409,7 @@ build_flagsV4 = -g ;;; V4.4.x libraries (without LOROL_LITTLEFS; with newer NeoPixelBus) lib_depsV4 = - https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 ;; WLEDMM this must be first in the list, otherwise Aircoookie/ESPAsyncWebServer pulls in an older version of AsyncTCP !! + esp32async/AsyncTCP @ 3.4.7 makuna/NeoPixelBus @ 2.7.5 ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental ${common_mm.HUB75_lib_deps} @@ -385,13 +421,16 @@ lib_depsV4 = [esp32_idf_V4] ;; please note that you can NOT update existing ESP32 installs with a "V4" build. Also updating by OTA will not work properly. ;; You need to completely erase your device (esptool erase_flash) first, then install the "V4" build from VSCode+platformio. +;; uses arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) -;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) +;; standard platform - RECOMMENDED for debugging! The tasmota platform has removed all kernel error reporting code ;; platform = espressif32@ ~6.3.2 ;; platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) -platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.06.02/platform-espressif32.zip ;; Tasmota Arduino Core 2.0.9 with IPv6 support, based on IDF 4.4.4 -platform_packages = +;; Tasmota Arduino Core 2.0.9 with IPv6 support, based on IDF 4.4.4. Warning: all kernel error asserts removed +platform = ${esp32.platformTasmota} +platform_packages = ${esp32.platform_packagesTasmota} + build_unflags = ${common.build_unflags} build_flags = -g -Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one @@ -399,15 +438,17 @@ build_flags = -g #-DCONFIG_LITTLEFS_FOR_IDF_3_2 -D CONFIG_ASYNC_TCP_USE_WDT=0 -D CONFIG_ASYNC_TCP_TASK_STACK_SIZE=9472 ;; WLEDMM increase stack by 1.25Kb, as audioreactive needs bigger SETTINGS_STACK_BUF_SIZE + -D CONFIG_ASYNC_TCP_STACK_SIZE=9472 ;; *sigh* newer asyncTCP uses this instead of .._TASK_.. -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3 ; -D WLEDMM_TWOPATH ;; use I2S1 as the second bus --> slightly faster on some setups ; -D WLEDMM_SLOWPATH ;; don't use I2S for LED bus default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv lib_deps = - https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 + esp32async/AsyncTCP @ 3.4.7 makuna/NeoPixelBus @ 2.7.5 ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental ${env.lib_deps} +board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs [esp32s2] ;; generic definitions for all ESP32-S2 boards @@ -415,12 +456,12 @@ lib_deps = ;; platform_packages = ;; toolchain-riscv32-esp @ 8.4.0+2021r2-patch5 ;; toolchain-xtensa-esp32s2 @ 8.4.0+2021r2-patch5 -;; standard platform -platform = espressif32@ ~6.3.2 -platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) -;; tasmota platform (optional) -;; platform = ${esp32_idf_V4.platform} -;; platform_packages = ${esp32_idf_V4.platform_packages} +;; standard espressif platform +;; platform = espressif32@ ~6.3.2 +;; platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) +;; inherit tasmota platform from esp32_idf_V4 +platform = ${esp32_idf_V4.platform} +platform_packages = ${esp32_idf_V4.platform_packages} board_build.flash_mode = dio ;; prevents build error: sdkconfig.h: No such file or directory build_flags = -g @@ -430,6 +471,7 @@ build_flags = -g -DCONFIG_LITTLEFS_FOR_IDF_3_2 -DLFS_THREADSAFE ;; WLEDMM -D CONFIG_ASYNC_TCP_USE_WDT=0 -D CONFIG_ASYNC_TCP_TASK_STACK_SIZE=8614 ;; WLEDMM increase stack by 1Kb, as audioreactive needs bigger SETTINGS_STACK_BUF_SIZE + -D CONFIG_ASYNC_TCP_STACK_SIZE=8614 ;; *sigh* newer asyncTCP uses this instead of .._TASK_.. -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 -DCO -DARDUINO_USB_MODE=0 ;; this flag is mandatory for ESP32-S2 ! @@ -437,13 +479,16 @@ build_flags = -g ;; ARDUINO_USB_CDC_ON_BOOT lib_deps = - https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 + esp32async/AsyncTCP @ 3.4.7 ;; makuna/NeoPixelBus @ 2.7.5 ;; standard makuna/NeoPixelBus @ 2.7.9 ;; experimental - reduces LED glitches on -S2 ${env.lib_deps} +board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs +monitor_filters = esp32_exception_decoder [esp32c3] ;; generic definitions for all ESP32-C3 boards +;; inherit tasmota platform from esp32_idf_V4 platform = ${esp32_idf_V4.platform} platform_packages = ${esp32_idf_V4.platform_packages} build_unflags = ${common.build_unflags} @@ -454,19 +499,23 @@ build_flags = -g -DCONFIG_LITTLEFS_FOR_IDF_3_2 -DLFS_THREADSAFE ;; WLEDMM -D CONFIG_ASYNC_TCP_USE_WDT=0 -D CONFIG_ASYNC_TCP_TASK_STACK_SIZE=9472 ;; WLEDMM increase stack by 1.25Kb, as audioreactive needs bigger SETTINGS_STACK_BUF_SIZE + -D CONFIG_ASYNC_TCP_STACK_SIZE=9472 ;; *sigh* newer asyncTCP uses this instead of .._TASK_.. -DCO -DARDUINO_USB_MODE=1 ;; this flag is mandatory for ESP32-C3 ;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry: ;; ARDUINO_USB_CDC_ON_BOOT lib_deps = - https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 + esp32async/AsyncTCP @ 3.4.7 makuna/NeoPixelBus @ 2.7.5 ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental ${env.lib_deps} +board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs +monitor_filters = esp32_exception_decoder [esp32s3] ;; generic definitions for all ESP32-S3 boards +;; inherit tasmota platform from esp32_idf_V4 platform = ${esp32_idf_V4.platform} platform_packages = ${esp32_idf_V4.platform_packages} build_unflags = ${common.build_unflags} @@ -479,15 +528,18 @@ build_flags = -g -DWLEDMM_NO_SERIAL_WAIT ;; WLEDMM don't wait for serial on -S3 (unless WLED_DEBUG is set) -D CONFIG_ASYNC_TCP_USE_WDT=0 -D CONFIG_ASYNC_TCP_TASK_STACK_SIZE=9472 ;; WLEDMM increase stack by 1.25Kb, as audioreactive needs bigger SETTINGS_STACK_BUF_SIZE + -D CONFIG_ASYNC_TCP_STACK_SIZE=9472 ;; *sigh* newer asyncTCP uses this instead of .._TASK_.. -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_DFU_ON_BOOT=0 -DCO ;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry: ;; ARDUINO_USB_MODE, ARDUINO_USB_CDC_ON_BOOT lib_deps = - https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 + esp32async/AsyncTCP @ 3.4.7 makuna/NeoPixelBus @ 2.7.5 ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental ${env.lib_deps} +board_build.partitions = ${esp32.large_partitions} ;; default partioning for 8MB flash - can be overridden in build envs +monitor_filters = esp32_exception_decoder # ------------------------------------------------------------------------------ @@ -577,66 +629,90 @@ build_unflags = ${common.build_unflags} build_flags = ${common.build_flags_esp8266} -D LEDPIN=1 -D WLED_DISABLE_INFRARED lib_deps = ${esp8266.lib_deps} +;; two aliases for lazy users [env:esp32dev] -board = esp32dev -platform = ${esp32.platform} -platform_packages = ${esp32.platform_packages} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 #-D WLED_DISABLE_BROWNOUT_DET -lib_deps = ${esp32.lib_deps} -monitor_filters = esp32_exception_decoder -board_build.partitions = ${esp32.default_partitions} +extends = env:esp32dev_V4 +[env:esp32_wrover] +extends = env:esp32_4MB_PSRAM_S -[env:esp32dev_qio80] +;; legacy build for OTA compatibility with upstream 0.15.x, slow but safe +[env:esp32dev_compat] board = esp32dev -platform = ${esp32.platform} -platform_packages = ${esp32.platform_packages} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_qio80 #-D WLED_DISABLE_BROWNOUT_DET -lib_deps = ${esp32.lib_deps} +platform = ${esp32_legacy.platform} +platform_packages = ${esp32_legacy.platform_packages} +build_unflags = ${esp32_legacy.build_unflags} +build_flags = ${common.build_flags} ${esp32_legacy.build_flags} -D WLED_RELEASE_NAME=ESP32_compat #-D WLED_DISABLE_BROWNOUT_DET + ${esp32.AR_build_flags} +lib_deps = ${esp32_legacy.lib_deps} + ${esp32.AR_lib_deps} +board_build.partitions = ${esp32_legacy.default_partitions} monitor_filters = esp32_exception_decoder -board_build.partitions = ${esp32.default_partitions} +;; RAM: [== ] 23.2% (used 75944 bytes from 327680 bytes) +;; Flash: [========= ] 88.5% (used 1391917 bytes from 1572864 bytes) + +;; legacy build for OTA compatibility with upstream 0.15.x, faster due to qio mode +[env:esp32dev_qio80_compat] +board = esp32dev +platform = ${esp32_legacy.platform} +platform_packages = ${esp32_legacy.platform_packages} +build_unflags = ${esp32_legacy.build_unflags} +build_flags = ${common.build_flags} ${esp32_legacy.build_flags} -D WLED_RELEASE_NAME=ESP32_qio80_compat #-D WLED_DISABLE_BROWNOUT_DET + ${esp32.AR_build_flags} +lib_deps = ${esp32_legacy.lib_deps} + ${esp32.AR_lib_deps} +board_build.partitions = ${esp32_legacy.default_partitions} board_build.f_flash = 80000000L board_build.flash_mode = qio +monitor_filters = esp32_exception_decoder -[env:esp32dev_V4_dio80] -;; experimental ESP32 env using ESP-IDF V4.4.x -;; Warning: this build environment is not stable!! -;; please erase your device before installing. +[env:esp32dev_V4] board = esp32dev platform = ${esp32_idf_V4.platform} +platform_packages = ${esp32_idf_V4.platform_packages} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_V4_qio80 #-D WLED_DISABLE_BROWNOUT_DET +build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_V4 #-D WLED_DISABLE_BROWNOUT_DET + ${esp32.AR_build_flags} lib_deps = ${esp32_idf_V4.lib_deps} + ${esp32.AR_lib_deps} monitor_filters = esp32_exception_decoder board_build.partitions = ${esp32_idf_V4.default_partitions} board_build.f_flash = 80000000L board_build.flash_mode = dio +;; alias name for env:esp32_4MB_M_eth; improves compatibility with old platformio_override.ini entries [env:esp32_eth] -board = esp32-poe -platform = ${esp32.platform} -platform_packages = ${esp32.platform_packages} -upload_speed = 921600 -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1 - -D WLED_DISABLE_ESPNOW ;; ESP-NOW requires wifi, may crash with ethernet only -lib_deps = ${esp32.lib_deps} -board_build.partitions = ${esp32.default_partitions} +extends = env:esp32_4MB_M_eth -[env:esp32s2_saola] -board = esp32-s2-saola-1 -platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.2/platform-tasmota-espressif32-2.0.2.zip -platform_packages = -framework = arduino -board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv -board_build.flash_mode = qio -upload_speed = 460800 -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=S2_saola - ;-DLOLIN_WIFI_FIX ;; try this in case Wifi does not work - -DARDUINO_USB_CDC_ON_BOOT=1 -lib_deps = ${esp32s2.lib_deps} +;; legacy build for OTA compatibility with upstream 0.15.x +;; --> use [env:esp32_4MB_M_eth] (4MB) or [env:esp32_16MB_M_eth] +[env:esp32_eth_compat] +board = esp32-poe +platform = ${esp32_legacy.platform} +platform_packages = ${esp32_legacy.platform_packages} +upload_speed = 921600 +build_unflags = ${esp32_legacy.build_unflags} +build_flags = ${common.build_flags} ${esp32_legacy.build_flags} -D WLED_RELEASE_NAME=ESP32_Ethernet_compat -D RLYPIN=-1 + -D WLED_USE_ETHERNET -D BTNPIN=-1 + -D WLED_DISABLE_ESPNOW ;; ESP-NOW requires wifi, may crash with ethernet only + ${esp32.AR_build_flags} +lib_deps = ${esp32_legacy.lib_deps} + ${esp32.AR_lib_deps} +board_build.partitions = ${esp32_legacy.default_partitions} + +;; WLEDMM: use WLEDMM build environments +;;[env:esp32s2_saola] +;;board = esp32-s2-saola-1 +;;platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.2/platform-tasmota-espressif32-2.0.2.zip +;;platform_packages = +;;framework = arduino +;;board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv +;;board_build.flash_mode = qio +;;upload_speed = 460800 +;;build_unflags = ${common.build_unflags} +;;build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=S2_saola +;; ;-DLOLIN_WIFI_FIX ;; try this in case Wifi does not work +;; -DARDUINO_USB_CDC_ON_BOOT=1 +;; lib_deps = ${esp32s2.lib_deps} ;; WLEDMM: use WLEDMM build environments ;; [env:esp32_wrover] @@ -658,6 +734,7 @@ lib_deps = ${esp32s2.lib_deps} [env:esp32c3dev] extends = esp32c3 platform = ${esp32c3.platform} +platform_packages = ${esp32c3.platform_packages} framework = arduino board = esp32-c3-devkitm-1 board_build.partitions = ${esp32.default_partitions} @@ -669,61 +746,64 @@ build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME= upload_speed = 460800 build_unflags = ${common.build_unflags} lib_deps = ${esp32c3.lib_deps} -board_build.flash_mode = qio +board_build.flash_mode = dio ;; some boards do not boot with the faster "qio" mode +; RAM: [== ] 24.0% (used 78624 bytes from 327680 bytes) +; Flash: [========= ] 91.7% (used 1441933 bytes from 1572864 bytes) -[env:esp32s3dev_8MB] +;; WLEDMM: use WLEDMM build environments +;; [env:esp32s3dev_8MB] ;; ESP32-S3-DevKitC-1 development board, with 8MB FLASH, no PSRAM (flash_mode: qio) -board = esp32-s3-devkitc-1 -platform = ${esp32s3.platform} -platform_packages = ${esp32s3.platform_packages} -upload_speed = 921600 ; or 460800 -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_8MB - -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 - -D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip - ;-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") - ;-D WLED_DEBUG -lib_deps = ${esp32s3.lib_deps} - ${esp32.AR_lib_deps} -board_build.partitions = ${esp32.large_partitions} -board_build.f_flash = 80000000L -board_build.flash_mode = qio -; board_build.flash_mode = dio ;; try this if you have problems at startup -monitor_filters = esp32_exception_decoder +;; board = esp32-s3-devkitc-1 +;; platform = ${esp32s3.platform} +;; platform_packages = ${esp32s3.platform_packages} +;; upload_speed = 921600 ; or 460800 +;; build_unflags = ${common.build_unflags} +;; build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_8MB +;; -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 +;; -D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip +;; ;-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") +;; ;-D WLED_DEBUG +;; lib_deps = ${esp32s3.lib_deps} +;; ${esp32.AR_lib_deps} +;; board_build.partitions = ${esp32.large_partitions} +;; board_build.f_flash = 80000000L +;; board_build.flash_mode = qio +;; ; board_build.flash_mode = dio ;; try this if you have problems at startup +;; monitor_filters = esp32_exception_decoder -[env:esp32s3dev_8MB_PSRAM_opi] +;; [env:esp32s3dev_8MB_PSRAM_opi] ;; ESP32-S3 development board, with 8MB FLASH and >= 8MB PSRAM (memory_type: qio_opi) -board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support -board_build.arduino.memory_type = qio_opi ;; use with PSRAM: 8MB or 16MB -platform = ${esp32s3.platform} -platform_packages = ${esp32s3.platform_packages} -upload_speed = 921600 -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${esp32s3.build_flags} - -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 - ;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip - -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") - ; -D WLED_RELEASE_NAME=ESP32-S3_PSRAM - -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 -lib_deps = ${esp32s3.lib_deps} - ${esp32.AR_lib_deps} -board_build.partitions = ${esp32.large_partitions} -board_build.f_flash = 80000000L -board_build.flash_mode = qio -monitor_filters = esp32_exception_decoder +;; board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support +;; board_build.arduino.memory_type = qio_opi ;; use with PSRAM: 8MB or 16MB +;; platform = ${esp32s3.platform} +;; platform_packages = ${esp32s3.platform_packages} +;; upload_speed = 921600 +;; build_unflags = ${common.build_unflags} +;; build_flags = ${common.build_flags} ${esp32s3.build_flags} +;; -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 +;; ;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip +;; -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") +;; ; -D WLED_RELEASE_NAME=ESP32-S3_PSRAM +;; -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 +;; lib_deps = ${esp32s3.lib_deps} +;; ${esp32.AR_lib_deps} +;; board_build.partitions = ${esp32.large_partitions} +;; board_build.f_flash = 80000000L +;; board_build.flash_mode = qio +;; monitor_filters = esp32_exception_decoder -[env:esp32s3dev_16MB_PSRAM_opi] -extends = env:esp32s3dev_8MB_PSRAM_opi -board_build.partitions = tools/WLED_ESP32_16MB.csv -board_upload.flash_size = 16MB +;; [env:esp32s3dev_16MB_PSRAM_opi] +;; extends = env:esp32s3dev_8MB_PSRAM_opi +;; board_build.partitions = tools/WLED_ESP32_16MB.csv +;; board_upload.flash_size = 16MB -[env:esp32s3dev_8MB_PSRAM_qspi] +;; [env:esp32s3dev_8MB_PSRAM_qspi] ;; ESP32-TinyS3 development board, with 8MB FLASH and PSRAM (memory_type: qio_qspi) -extends = env:esp32s3dev_8MB_PSRAM_opi -;board = um_tinys3 ; -> needs workaround from https://github.com/Aircoookie/WLED/pull/2905#issuecomment-1328049860 -board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support -board_build.arduino.memory_type = qio_qspi ;; use with PSRAM: 2MB or 4MB +;; extends = env:esp32s3dev_8MB_PSRAM_opi +;; ;board = um_tinys3 ; -> needs workaround from https://github.com/Aircoookie/WLED/pull/2905#issuecomment-1328049860 +;; board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support +;; board_build.arduino.memory_type = qio_qspi ;; use with PSRAM: 2MB or 4MB [env:esp8285_4CH_MagicHome] board = esp8285 @@ -823,25 +903,26 @@ lib_deps = ${esp32s2.lib_deps} # custom board configurations # ------------------------------------------------------------------------------ -[env:esp32c3dev_2MB] -;; for ESP32-C3 boards with 2MB flash (instead of 4MB). -;; this board need a specific partition file. OTA not possible. -extends = esp32c3 -platform = ${esp32c3.platform} -platform_packages = ${esp32c3.platform_packages} -board = esp32-c3-devkitm-1 -build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3 - -D WLED_WATCHDOG_TIMEOUT=0 - -D WLED_DISABLE_OTA - ; -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB - -DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip -build_unflags = ${common.build_unflags} -upload_speed = 115200 -lib_deps = ${esp32c3.lib_deps} -board_build.partitions = tools/WLED_ESP32_2MB_noOTA.csv -board_build.flash_mode = dio -board_upload.flash_size = 2MB -board_upload.maximum_size = 2097152 +;; WLEDMM: use [env:esp32c3dev_2MB_M] (WLEDMM build environment) +;; [env:esp32c3dev_2MB] +;; ;; for ESP32-C3 boards with 2MB flash (instead of 4MB). +;; ;; this board need a specific partition file. OTA not possible. +;; extends = esp32c3 +;; platform = ${esp32c3.platform} +;; platform_packages = ${esp32c3.platform_packages} +;; board = esp32-c3-devkitm-1 +;; build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3 +;; -D WLED_WATCHDOG_TIMEOUT=0 +;; -D WLED_DISABLE_OTA +;; ; -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB +;; -DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip +;; build_unflags = ${common.build_unflags} +;; upload_speed = 115200 +;; lib_deps = ${esp32c3.lib_deps} +;; board_build.partitions = tools/WLED_ESP32_2MB_noOTA.csv +;; board_build.flash_mode = dio +;; board_upload.flash_size = 2MB +;; board_upload.maximum_size = 2097152 ;WLEDMM: see below ; [env:wemos_shield_esp32] @@ -1072,6 +1153,7 @@ build_flags_S = -D WLEDMM_FASTPATH ;; WLEDMM experimental option. Reduces audio lag (latency), and allows for faster LED framerates. May break compatibility with previous versions. ; -D WLED_DEBUG_HEAP ;; WLEDMM enable heap debugging -D WLED_ENABLE_GIF + ; -D WLED_ENABLE_FULL_FONTS ;; uncomment for (limited) unicode support in scrolling text - warning: increases firmware size by 6848 bytes lib_deps_S = ;; https://github.com/kosme/arduinoFFT#develop @ 1.9.2+sha.419d7b0 ;; used for USERMOD_AUDIOREACTIVE - using "known working" hash @@ -1087,6 +1169,7 @@ build_flags_M = -D USERMOD_FOUR_LINE_DISPLAY -D USERMOD_ROTARY_ENCODER_UI -D USERMOD_AUTO_SAVE + -D WLED_ENABLE_FULL_FONTS ;; enables (limited) unicode support in scrolling text - warning: increases firmware size by 6848 bytes ${common_mm.animartrix_build_flags} ${common_mm.NetDebug_build_flags} @@ -1145,12 +1228,20 @@ lib_deps_XL = ; common defaults for all MM environments [esp32_4MB_S_base] board = esp32dev -platform = ${esp32.platform} -platform_packages = ${esp32.platform_packages} +;; legacy V3 platform +platform = ${esp32_legacy.platform} +platform_packages = ${esp32_legacy.platform_packages} +build_unflags = ${esp32_legacy.build_unflags} +build_flags = ${common.build_flags} ${esp32_legacy.build_flags} ${common_mm.build_flags_S} ${common_mm.build_disable_sync_interfaces} +lib_deps = ${esp32_legacy.lib_deps} ${common_mm.lib_deps_S} +lib_ignore = ${common_mm.DMXin_lib_ignore} ;; requires V4 framework upload_speed = 460800 ; or 921600 -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags_esp32} ${common_mm.build_flags_S} ${common_mm.build_disable_sync_interfaces} -lib_deps = ${esp32.lib_deps} ${common_mm.lib_deps_S} +;; new V4 platform +;;platform = ${esp32.platform} +;;platform_packages = ${esp32.platform_packages} +;;build_unflags = ${common.build_unflags} +;;build_flags = ${common.build_flags_esp32} ${common_mm.build_flags_S} ${common_mm.build_disable_sync_interfaces} +;;lib_deps = ${esp32.lib_deps} ${common_mm.lib_deps_S} board_build.partitions = ${esp32.default_partitions} board_build.f_flash = 80000000L ; use full 80MHz speed for flash (default = 40Mhz) board_build.flash_mode = dio ; (dio = dual i/o; more compatible than qio = quad i/o) @@ -1159,7 +1250,7 @@ monitor_filters = esp32_exception_decoder ;common default for all max environments [esp32_4MB_M_base] extends = esp32_4MB_S_base -build_flags = ${common.build_flags_esp32} ${common_mm.build_flags_S} ${common_mm.build_flags_M} ;; we don't want common_mm.build_disable_sync_interfaces, so we cannot inherit from esp32_4MB_S_base +build_flags = ${common.build_flags} ${esp32_legacy.build_flags} ${common_mm.build_flags_S} ${common_mm.build_flags_M} ;; we don't want common_mm.build_disable_sync_interfaces, so we cannot inherit from esp32_4MB_S_base build_unflags = ${esp32_4MB_S_base.build_unflags} lib_deps = ${esp32_4MB_S_base.lib_deps} ${common_mm.lib_deps_M} @@ -1173,6 +1264,9 @@ lib_deps = ${esp32_4MB_M_base.lib_deps} ${common_mm.lib_deps_XL} [esp32_4MB_V4_S_base] board = esp32dev upload_speed = 460800 ; or 921600 +;;platform = ${esp32.platform} ;; "V4" = tasmota = standard +;;platform_packages = ${esp32.platform_packages} +;; softhack007: back to standard espressif platform, due to bad UX when OTA updating a previous versions - slow, unresponsive, wifi lost, out of RAM platform = ${esp32.platformV4} platform_packages = ${esp32.platformV4_packages} build_unflags = ${common.build_unflags} @@ -1186,7 +1280,8 @@ lib_deps = ${common_mm.lib_deps_S} ;; do not incl ${common_mm.DMXin_lib_deps} esp32_build_flags = ${esp32.build_flagsV4} ${esp32_4MB_V4_S_base.build_flags} ;; this is for esp32 only, including specific "V4" flags esp32_lib_deps = ${esp32.lib_depsV4} ${esp32_4MB_V4_S_base.lib_deps} ;; this is for esp32 only, including specific "V4" flags -board_build.partitions = ${esp32.default_partitions} +;;board_build.partitions = ${esp32.default_partitions} +board_build.partitions = ${esp32.extended_partitions} ;; 1.65MB firmware, 700KB filesystem board_build.f_flash = 80000000L ; use full 80MHz speed for flash (default = 40Mhz) board_build.flash_mode = dio ; (dio = dual i/o; more compatible than qio = quad i/o) ;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation @@ -1287,14 +1382,23 @@ build_flags = ${esp32_4MB_M_base.build_flags} [env:esp32_4MB_M_eth] extends = esp32_4MB_M_base +;;board_build.partitions = ${esp32.default_partitions} +board_build.partitions = ${esp32.extended_partitions} ;; 1.65MB firmware, 700KB filesystem build_flags = ${esp32_4MB_M_base.build_flags} -D WLED_RELEASE_NAME=esp32_4MB_M_eth -D WLED_USE_ETHERNET -D RLYPIN=-1 -D BTNPIN=-1 ;; Prevent clash -D WLED_DISABLE_ESPNOW ;; ESP-NOW requires wifi, may crash with ethernet only -D WLEDMM_SAVE_FLASH -; RAM: [== ] 24.2% (used 79404 bytes from 327680 bytes) -; Flash: [==========] 97.8% (used 1538025 bytes from 1572864 bytes) + ;-D WLED_DISABLE_ALEXA + ;-D WLED_DISABLE_HUESYNC ;; Over the limits ? + ;-D WLED_DISABLE_LOXONE ;; Over the limits + ;-D WLED_DISABLE_MQTT + -D WLED_DISABLE_INFRARED ;; Over the limit + -D WLED_DISABLE_PARTICLESYSTEM1D ;; exceeds flash size limit + -D WLED_DISABLE_PARTICLESYSTEM2D ;; exceeds flash size limit +; RAM: [== ] 24.1% (used 78904 bytes from 327680 bytes) +; Flash: [==========] 98.5% (used 1548489 bytes from 1572864 bytes [env:esp32_4MB_XL] extends = esp32_4MB_XL_base @@ -1315,8 +1419,8 @@ board_build.partitions = tools/WLED_ESP32_4MB_256KB_FS.csv [env:esp32_16MB_S] extends = esp32_4MB_S_base board = esp32_16MB -board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem -;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem +;board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +board_build.partitions = ${esp32.extreme_partitions} ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem build_unflags = ${esp32_4MB_S_base.build_unflags} ${Speed_Flags.build_unflags} ;; to override -Os build_flags = ${esp32_4MB_S_base.build_flags} @@ -1335,9 +1439,9 @@ lib_deps = ${esp32_4MB_S_base.lib_deps} extends = esp32_4MB_M_base build_flags = ${esp32_4MB_M_base.build_flags} -D WLED_RELEASE_NAME=esp32_16MB_M -board = esp32_16MB -board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem -;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem +board = esp32_16MB +;board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +board_build.partitions = ${esp32.extreme_partitions} ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem ;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation ; RAM: [== ] 24.2% (used 79196 bytes from 327680 bytes) ; Flash: [======= ] 73.6% (used 1542905 bytes from 2097152 bytes) @@ -1368,8 +1472,8 @@ build_flags = ${esp32_4MB_M_base.build_flags} -D WLED_RELEASE_NAME=esp32_16MB_M_debug monitor_filters = esp32_exception_decoder board = esp32_16MB -board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem -;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem +;board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +board_build.partitions = ${esp32.extreme_partitions} ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem ; RAM: [== ] 24.3% (used 79468 bytes from 327680 bytes) ; Flash: [======== ] 76.7% (used 1609205 bytes from 2097152 bytes) @@ -1377,17 +1481,17 @@ board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for extends = esp32_4MB_XL_base build_flags = ${esp32_4MB_XL_base.build_flags} -D WLED_RELEASE_NAME=esp32_16MB_XL -board = esp32_16MB -board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem -;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem +board = esp32_16MB +;board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +board_build.partitions = ${esp32.extreme_partitions} ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem ;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation ; RAM: [== ] 24.5% (used 80140 bytes from 327680 bytes) ; Flash: [======== ] 77.8% (used 1631929 bytes from 2097152 bytes) [env:esp32_16MB_M_eth] extends = esp32_4MB_M_base -board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem -;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem +;board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +board_build.partitions = ${esp32.extreme_partitions} ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem board = esp32_16MB-poe ;; needed for ethernet boards (selects "esp32-poe" as variant) build_flags = ${esp32_4MB_M_base.build_flags} -D WLED_RELEASE_NAME=esp32_16MB_M_eth ; This will be included in the firmware.bin filename @@ -1403,10 +1507,14 @@ upload_speed = 460800 ;115200 board_build.f_cpu = 160000000L ;; we want 160Mhz (default = 80Mhz) build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=esp8266_2MB_S + -D WLED_USE_UNREAL_MATH ;; may cause some wrong sunset/sunrise times, but saves 7064 bytes FLASH and 975 bytes RAM + -D WLEDMM_SAVE_FLASH -D WLED_DISABLE_ALEXA -D WLED_DISABLE_HUESYNC -D WLED_DISABLE_ESPNOW ;; might help in case of WiFi connectivity problems -D WLED_DISABLE_LOXONE ; FLASH 1272 bytes + -D WLED_DISABLE_PARTICLESYSTEM1D ;; exceeds flash size limit + -D WLED_DISABLE_PARTICLESYSTEM2D ;; exceeds flash size limit ;; -D WLED_DISABLE_MQTT ; RAM 216 bytes; FLASH 16496 bytes ;; -D WLED_DISABLE_INFRARED ;RAM 136 bytes; FLASH 24492 bytes ; -D WLED_DISABLE_2D @@ -1415,8 +1523,8 @@ build_flags = ${common.build_flags_esp8266} ; -D WLED_DEBUG ; monitor_filters = esp8266_exception_decoder ;; lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation -; RAM: [====== ] 60.8% (used 49836 bytes from 81920 bytes) -; Flash: [======== ] 83.3% (used 869783 bytes from 1044464 bytes) +; RAM: [====== ] 61.3% (used 50224 bytes from 81920 bytes) +; Flash: [========= ] 87.6% (used 915303 bytes from 1044464 bytes) [env:esp8266_4MB_S] extends = env:d1_mini @@ -1431,13 +1539,17 @@ build_flags = ${common.build_flags_esp8266} ;; -D WLED_DISABLE_MQTT ; RAM 216 bytes; FLASH 16496 bytes ;; -D WLED_DISABLE_INFRARED ;RAM 136 bytes; FLASH 24492 bytes ; -D WLED_DISABLE_2D + ;; -D WLED_DISABLE_PARTICLESYSTEM1D + -D WLED_DISABLE_PARTICLESYSTEM2D ;; exceeds flash size limit + -D WLEDMM_SAVE_FLASH + -D WLED_USE_UNREAL_MATH ; -UWLED_USE_MY_CONFIG ${common_mm.NetDebug_build_flags} ; -D WLED_DEBUG ; monitor_filters = esp8266_exception_decoder ;; lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation -; RAM: [====== ] 60.8% (used 49824 bytes from 81920 bytes) -; Flash: [======== ] 83.3% (used 869779 bytes from 1044464 bytes) +; RAM: [====== ] 61.3% (used 50220 bytes from 81920 bytes) +; Flash: [========= ] 89.5% (used 934483 bytes from 1044464 bytes) [env:esp8266_4MB_M] extends = env:d1_mini upload_speed = 460800 ;115200 @@ -1447,6 +1559,10 @@ build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_ALEXA -D WLED_DISABLE_HUESYNC -D WLED_DISABLE_LOXONE + -D WLEDMM_SAVE_FLASH + -D WLED_USE_UNREAL_MATH + ;; -D WLED_DISABLE_PARTICLESYSTEM1D + ;; -D WLED_DISABLE_PARTICLESYSTEM2D ;; exceeds flash size limit ; -D USERMOD_AUDIOREACTIVE ; -UWLED_USE_MY_CONFIG ; -D USERMOD_PIRSWITCH @@ -1467,8 +1583,8 @@ lib_deps = ${esp8266.lib_deps} OneWire@~2.3.5 ; used for USERMOD_FOUR_LINE_DISPLAY and USERMOD_DALLASTEMPERATURE olikraus/U8g2 @ ^2.28.8 ; used for USERMOD_FOUR_LINE_DISPLAY ElectronicCats/MPU6050 @ 0.6.0 ; used for USERMOD_MPU6050_IMU -; RAM: [====== ] 63.0% (used 51632 bytes from 81920 bytes) -; Flash: [========= ] 88.5% (used 924179 bytes from 1044464 bytes) +; RAM: [====== ] 63.0% (used 51608 bytes from 81920 bytes) +; Flash: [========= ] 94.9% (used 991039 bytes from 1044464 bytes) ; Blaz env (for reference purposes) [env:d1_mini_temp] @@ -1478,7 +1594,6 @@ build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D WLED_DISABLE_ALEXA -D WLED_DISABLE_HUESYNC -D WLED_DISABLE_LOXONE - -D WLED_DISABLE_AUDIO ;WLEDMM not used anywhere -D WLED_ENABLE_SIMPLE_UI -D USERMOD_FOUR_LINE_DISPLAY -D USE_ALT_DISPlAY @@ -1515,6 +1630,10 @@ build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_ALEXA -D WLED_DISABLE_HUESYNC ; -D WLED_DEBUG ${common.debug_flags} ;; un-comment for debug messages + ;; -D WLED_DISABLE_PARTICLESYSTEM1D + ;; -D WLED_DISABLE_PARTICLESYSTEM2D ;; exceeds flash size limit + -D WLEDMM_SAVE_FLASH + ;; -D WLED_USE_UNREAL_MATH ${common_mm.NetDebug_build_flags} ;; -D WLED_DISABLE_ESPNOW ;; might help in case of WiFi connectivity problems ; -D WLED_DISABLE_LOXONE ; FLASH 1272 bytes @@ -1526,8 +1645,8 @@ build_flags = ${common.build_flags_esp8266} ; -UWLED_USE_MY_CONFIG monitor_filters = esp8266_exception_decoder ; lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation -; RAM: [====== ] 59.3% (used 48616 bytes from 81920 bytes) -; Flash: [======== ] 77.0% (used 804236 bytes from 1044464 bytes) +; RAM: [====== ] 61.5% (used 50400 bytes from 81920 bytes) +; Flash: [========= ] 90.1% (used 941059 bytes from 1044464 bytes) [env:esp8266pro_16MB_M] extends = env:d1_mini @@ -1547,7 +1666,11 @@ build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_LOXONE ; -D USERMOD_AUDIOREACTIVE ; -D USERMOD_ARTIFX ; to be done - ;; -D WLED_DISABLE_ESPNOW ;; might help in case of WiFi connectivity problems + -D WLED_DISABLE_ESPNOW ;; might help in case of WiFi connectivity problems + ;; -D WLED_DISABLE_PARTICLESYSTEM1D + -D WLED_DISABLE_PARTICLESYSTEM2D ;; exceeds flash size limit + -D WLEDMM_SAVE_FLASH + ;; -D WLED_USE_UNREAL_MATH -D USERMOD_PIRSWITCH -D USERMOD_DALLASTEMPERATURE ;; disabled because it hangs during usermod setup on -S3 (autodetect broken?) -D USERMOD_MULTI_RELAY @@ -1562,8 +1685,8 @@ lib_deps = ${esp8266.lib_deps} OneWire@~2.3.5 ; used for USERMOD_FOUR_LINE_DISPLAY and USERMOD_DALLASTEMPERATURE olikraus/U8g2 @ ^2.28.8 ; used for USERMOD_FOUR_LINE_DISPLAY ElectronicCats/MPU6050 @ 0.6.0 ; used for USERMOD_MPU6050_IMU -; RAM: [====== ] 63.8% (used 52272 bytes from 81920 bytes) -; Flash: [========= ] 90.4% (used 944487 bytes from 1044464 bytes) +; RAM: [====== ] 62.9% (used 51508 bytes from 81920 bytes) +; Flash: [==========] 96.2% (used 1005115 bytes from 1044464 bytes) [env:esp01_1MB_S] board = esp01_1m @@ -1573,17 +1696,21 @@ board_build.ldscript = ${common.ldscript_1m128k} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA -D WLED_RELEASE_NAME=esp01_1MB_S + -D WLED_USE_UNREAL_MATH ;; may cause some wrong sunset/sunrise times, but saves 7064 bytes FLASH and 975 bytes RAM -D WLED_DISABLE_ALEXA -D WLED_DISABLE_HUESYNC -D WLED_DISABLE_ESPNOW ;; exceeds flash size limits -D WLED_DISABLE_INFRARED ;; exceeds flash size limits -D WLED_DISABLE_PARTICLESYSTEM1D ;; exceeds flash size limit -D WLED_DISABLE_PARTICLESYSTEM2D ;; exceeds flash size limit - + -D WLEDMM_SAVE_FLASH + ;; -D WLED_DISABLE_LOXONE + ;; -D WLED_DISABLE_MQTT + ;; -D WLED_DISABLE_2D ;; last resort to reduce flash size lib_deps = ${esp8266.lib_deps} lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation -; RAM: [====== ] 60.6% (used 49616 bytes from 81920 bytes) -; Flash: [==========] 99.8% (used 890835 bytes from 892912 bytes) +; RAM: [====== ] 60.3% (used 49408 bytes from 81920 bytes) +; Flash: [==========] 98.6% (used 880331 bytes from 892912 bytes) # ------------------------------------------------------------------------------ @@ -1595,6 +1722,9 @@ lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compila ; compiled with ESP-IDF 4.4.1; HUB75 supported [env:esp32_4MB_V4_S] extends = esp32_4MB_V4_S_base +board_build.partitions = ${esp32.default_partitions} ;; compatible with upstream +build_unflags = ${esp32_4MB_V4_S_base.build_unflags} + ${common_mm.DMXin_build_flags} ;; exceeds flash size limits build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} -D WLED_RELEASE_NAME=esp32_4MB_V4_S -D WLED_WATCHDOG_TIMEOUT=0 #-D WLED_DISABLE_BROWNOUT_DET @@ -1606,19 +1736,25 @@ build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} -D WLED_DISABLE_HUESYNC -D WLED_DISABLE_MQTT -D WLED_DISABLE_INFRARED + -D WLED_DISABLE_ESPNOW ;; might help in case of WiFi connectivity problems ; -D WLED_DEBUG ; -D SR_DEBUG ; -D MIC_LOGGER ${common_mm.HUB75_build_flags} + -D SR_DMTYPE=254 ;; HUB75 driver needs the I2S unit - set AR default mode to 'Network Receive Only' to prevent driver conflicts. -D WLED_DISABLE_PARTICLESYSTEM1D ;; exceeds flash size limit + ;; -D WLED_DISABLE_PARTICLESYSTEM2D lib_deps = ${esp32_4MB_V4_S_base.esp32_lib_deps} ${common_mm.HUB75_lib_deps} lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation -; RAM: [=== ] 25.5% (used 83400 bytes from 327680 bytes) -; Flash: [==========] 97.3% (used 1530013 bytes from 1572864 bytes) + ${common_mm.DMXin_lib_ignore} +; RAM: [=== ] 25.3% (used 83052 bytes from 327680 bytes) +; Flash: [==========] 98.2% (used 1544273 bytes from 1572864 bytes) [env:esp32_4MB_V4_S_eth] extends = esp32_4MB_V4_S_base +;;board_build.partitions = ${esp32.default_partitions} +board_build.partitions = ${esp32.extended_partitions} ;; 1.65MB firmware, 700KB filesystem build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} -D WLED_RELEASE_NAME=esp32_4MB_V4_S -D WLED_WATCHDOG_TIMEOUT=0 #-D WLED_DISABLE_BROWNOUT_DET @@ -1634,6 +1770,7 @@ build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} ; -D SR_DEBUG ; -D MIC_LOGGER ${common_mm.HUB75_build_flags} + -D SR_DMTYPE=254 ;; HUB75 driver needs the I2S unit - set AR default mode to 'Network Receive Only' to prevent driver conflicts. -D WLED_USE_ETHERNET -D WLED_DISABLE_ESPNOW ;; ESP-NOW requires wifi, may crash with ethernet only lib_deps = ${esp32_4MB_V4_S_base.esp32_lib_deps} @@ -1671,11 +1808,13 @@ build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} -D WLED_DISABLE_MQTT -D WLED_DISABLE_INFRARED -D WLED_DISABLE_ADALIGHT + -D WLED_ENABLE_FULL_FONTS ;; increases firmware size by 6848 bytes ;; -D WLED_DEBUG ;; -D SR_DEBUG -D WLED_BOOTUPDELAY=350 ${common_mm.HUB75_build_flags} - -DESP32_FORUM_PINOUT + -DESP32_FORUM_PINOUT + -D SR_DMTYPE=254 ;; HUB75 driver needs the I2S unit - set AR default mode to 'Network Receive Only' to prevent driver conflicts. ${common_mm.animartrix_build_flags} ;;-DARDUINO_EVENT_RUNNING_CORE=0 ;; assign Wifi to core0, to have more CPU on core#1 (arduino loop) ;;-DARDUINO_RUNNING_CORE=1 ;; should be default, but does not hurt @@ -1701,16 +1840,21 @@ board_build.flash_mode = qio ; (dio = dual i/o; more compatible than qio = quad ; compiled with ESP-IDF 4.4.1 [env:esp32_4MB_V4_M] extends = esp32_4MB_V4_M_base +;; needs tasmota framework; will exceed flash limits with default framework +platform = ${esp32.platform} ;; "V4" tasmota +platform_packages = ${esp32.platform_packages} build_flags = ${esp32_4MB_V4_M_base.esp32_build_flags} -D WLED_RELEASE_NAME=esp32_4MB_V4_M -D WLED_WATCHDOG_TIMEOUT=0 #-D WLED_DISABLE_BROWNOUT_DET -D ARDUINO_USB_CDC_ON_BOOT=0 ; needed for arduino-esp32 >=2.0.4; avoids errors on startup - -D WLED_DISABLE_LOXONE ; FLASH 1272 bytes - -D WLED_DISABLE_ALEXA ; RAM 116 bytes; FLASH 13524 bytes - -D WLED_DISABLE_HUESYNC ;RAM 122 bytes; FLASH 6308 bytes - -D WLED_DISABLE_MQTT ; RAM 216 bytes; FLASH 16496 bytes ;; softhack007 disabled to stay below 100% flash size + ;;-D WLED_DISABLE_LOXONE ; FLASH 1272 bytes + ;;-D WLED_DISABLE_ALEXA ; RAM 116 bytes; FLASH 13524 bytes + ;;-D WLED_DISABLE_HUESYNC ;RAM 122 bytes; FLASH 6308 bytes + ;;-D WLED_DISABLE_MQTT ; RAM 216 bytes; FLASH 16496 bytes ;; softhack007 disabled to stay below 100% flash size -D WLED_DISABLE_INFRARED ;RAM 136 bytes; FLASH 24492 bytes ;; softhack007 disabled to stay below 100% flash size - -D WLEDMM_SAVE_FLASH + ;;-D WLEDMM_SAVE_FLASH + ;;-D WLED_DISABLE_PARTICLESYSTEM1D ;; exceeds flash size limit + ;;-D WLED_DISABLE_PARTICLESYSTEM2D ;; exceeds flash size limit lib_deps = ${esp32_4MB_V4_M_base.esp32_lib_deps} lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation ${common_mm.HUB75_lib_ignore} ;; over the flash size limit @@ -1718,10 +1862,10 @@ lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compila build_unflags = ${esp32_4MB_V4_M_base.build_unflags} -D USERMOD_ANIMARTRIX ;; Tips our memory usage over the limit -D USERMOD_ARTIFX - -D USERMOD_AUTO_SAVE + ;; -D USERMOD_AUTO_SAVE -D WLED_ENABLE_HUB75MATRIX -; RAM: [=== ] 26.1% (used 85632 bytes from 327680 bytes) -; Flash: [==========] 99.5% (used 1565065 bytes from 1572864 bytes) +; RAM: [== ] 23.9% (used 78204 bytes from 327680 bytes) +; Flash: [========= ] 85.8% (used 1349585 bytes from 1572864 bytes) ;; V4 build for 16MB flash, optimized for speed; HUB75 supported [env:esp32_16MB_V4_S] @@ -1738,13 +1882,14 @@ build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} -D JSON_BUFFER_SIZE=18432 -D MIN_HEAP_SIZE=6144 -D MAX_SEGMENT_DATA=40960 ;; default 32767 ${common_mm.HUB75_build_flags} + -D SR_DMTYPE=254 ;; HUB75 driver needs the I2S unit - set AR default mode to 'Network Receive Only' to prevent driver conflicts. ${common_mm.animartrix_build_flags} lib_deps = ${esp32_4MB_V4_S_base.esp32_lib_deps} ${common_mm.HUB75_lib_deps} ${common_mm.animartrix_lib_deps} board = esp32_16MB -board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem -;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem +;board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +board_build.partitions = ${esp32.extreme_partitions} ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem ;; optimized-for-speed build ; RAM: [== ] 23.7% (used 77704 bytes from 327680 bytes) ; Flash: [======== ] 84.4% (used 1770341 bytes from 2097152 bytes) @@ -1764,13 +1909,14 @@ build_flags = ${esp32_4MB_V4_M_base.esp32_build_flags} -D FLD_PIN_SCL=-1 -D FLD_PIN_SDA=-1 ; use global! -D HW_PIN_SCL=22 -D HW_PIN_SDA=21 ${common_mm.HUB75_build_flags} + -D SR_DMTYPE=254 ;; HUB75 driver needs the I2S unit - set AR default mode to 'Network Receive Only' to prevent driver conflicts. ${common_mm.animartrix_build_flags} lib_deps = ${esp32_4MB_V4_M_base.esp32_lib_deps} ${common_mm.HUB75_lib_deps} ${common_mm.animartrix_lib_deps} board = esp32_16MB -board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem -;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem +;board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +board_build.partitions = ${esp32.extreme_partitions} ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem ; RAM: [=== ] 25.7% (used 84104 bytes from 327680 bytes) ; Flash: [======== ] 80.7% (used 1692269 bytes from 2097152 bytes) @@ -1788,8 +1934,8 @@ build_flags = ${esp32_4MB_V4_M_base.esp32_build_flags} -D ARDUINO_USB_CDC_ON_BOOT=0 ; needed for arduino-esp32 >=2.0.4; avoids errors on startup lib_deps = ${esp32_4MB_V4_M_base.esp32_lib_deps} board = esp32_16MB -board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem -;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem +;board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +board_build.partitions = ${esp32.extreme_partitions} ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem monitor_filters = esp32_exception_decoder ; RAM: [=== ] 26.4% (used 86356 bytes from 327680 bytes) ; Flash: [======== ] 83.6% (used 1753461 bytes from 2097152 bytes) @@ -1800,9 +1946,10 @@ extends = esp32_4MB_V4_S_base board = lolin_d32_pro ;board = esp32cam ;board = ttgo-t7-v14-mini32 -board_build.partitions = ${esp32.extended_partitions} board_build.f_flash = 80000000L -;board_build.flash_mode = dio +;;board_build.flash_mode = qio +board_build.flash_mode = dio +board_build.partitions = ${esp32.extended_partitions} ;; 1.65MB firmware, 700KB filesystem build_unflags = ${esp32_4MB_V4_S_base.build_unflags} -D WLED_ENABLE_DMX @@ -1815,6 +1962,8 @@ build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} -D ARDUINO_USB_CDC_ON_BOOT=0 ; needed for arduino-esp32 >=2.0.4; avoids errors on startup -D WLEDMM_FASTPATH ; WLEDMM experimental option. Reduces audio lag (latency), and allows for faster LED framerates. May break compatibility with previous versions. -D WLEDMM_SAVE_FLASH + -mfix-esp32-psram-cache-issue ;; Older ESP32 (rev.<3) need a PSRAM fix (increases static RAM used) https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/external-ram.html + -D LEDPIN=25 -D DATA_PINS=25 ; avoid pin 16 = PSRAM -DBOARD_HAS_PSRAM ;; -D WLED_USE_PSRAM ;; WLED_USE_PSRAM causes major slow-down (slow LEDs) on some ESP32 boards -D WLED_USE_PSRAM_JSON -DALL_JSON_TO_PSRAM ; WLEDMM --> force all JSON stuff into PSRAM; gives more free heap -D WLED_DISABLE_LOXONE ; FLASH 1272 bytes @@ -1826,6 +1975,7 @@ build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} ; -D SR_DEBUG ; -D MIC_LOGGER ${common_mm.HUB75_build_flags} + -D SR_DMTYPE=254 ;; HUB75 driver needs the I2S unit - set AR default mode to 'Network Receive Only' to prevent driver conflicts. lib_deps = ${esp32_4MB_V4_S_base.esp32_lib_deps} ${common_mm.HUB75_lib_deps} lib_ignore = @@ -1833,8 +1983,8 @@ lib_ignore = ;; ${common_mm.HUB75_lib_ignore} ${common_mm.animartrix_lib_ignore} ${common_mm.DMXin_lib_ignore} -;; RAM: [== ] 17.8% (used 58236 bytes from 327680 bytes) -;; Flash: [========= ] 90.0% (used 1534209 bytes from 1703936 bytes) +;; RAM: [== ] 15.5% (used 50664 bytes from 327680 bytes) +;; Flash: [======== ] 75.7% (used 1289229 bytes from 1703936 bytes) ;; similar to 4MB_PSRAM_S, but optimized for WROVER-E (chip revision >= 3) that doesn't need any workarounds for PSRAM any more ;; tl;dr: its faster on PSRAM. But it will not work on all boards. @@ -1843,9 +1993,9 @@ extends = esp32_4MB_V4_S_base ;board = esp32cam board = lolin_d32_pro ;board = ttgo-t7-v14-mini32 -board_build.partitions = ${esp32.extended_partitions} +board_build.partitions = ${esp32.extended_partitions} ;; 1.65MB firmware, 700KB filesystem board_build.f_flash = 80000000L -board_build.flash_mode = dio +board_build.flash_mode = qio build_unflags = ${esp32_4MB_V4_S_base.build_unflags} ;;${Speed_Flags.build_unflags} ;; to override -Os @@ -1866,6 +2016,7 @@ build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} -D WLED_RELEASE_NAME=esp32_4MB_PSRAM_REV3_S -D WLED_WATCHDOG_TIMEOUT=0 #-D WLED_DISABLE_BROWNOUT_DET -D ARDUINO_USB_CDC_ON_BOOT=0 ; needed for arduino-esp32 >=2.0.4; avoids errors on startup + -D LEDPIN=25 -D DATA_PINS=25 ; avoid pin 16 = PSRAM -D WLED_DISABLE_LOXONE ; FLASH 1272 bytes -D WLED_DISABLE_HUESYNC ; RAM 122 bytes; FLASH 6308 bytes -D WLED_DISABLE_ALEXA ; RAM 116 bytes; FLASH 13524 bytes @@ -1877,14 +2028,15 @@ build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} ; -D SR_DEBUG ; -D MIC_LOGGER ${common_mm.HUB75_build_flags} + -D SR_DMTYPE=254 ;; HUB75 driver needs the I2S unit - set AR default mode to 'Network Receive Only' to prevent driver conflicts. lib_deps = ${esp32_4MB_V4_S_base.esp32_lib_deps} ${common_mm.HUB75_lib_deps} lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation ${common_mm.animartrix_lib_ignore} ;; ${common_mm.HUB75_lib_ignore} -;; RAM: [== ] 18.0% (used 58920 bytes from 327680 bytes) -;; Flash: [========= ] 90.6% (used 1542929 bytes from 1703936 bytes) +;; RAM: [== ] 15.7% (used 51340 bytes from 327680 bytes) +;; Flash: [======== ] 75.4% (used 1284377 bytes from 1703936 bytes) # ------------------------------------------------------------------------------ @@ -1894,6 +2046,9 @@ lib_ignore = [env:esp32S3_4MB_S] ;; Use for HD-WF2 extends = esp32_4MB_V4_M_base +;; use standard espressif platform - tasmota platform seems to have problems when there is no PSRAM (sdkconfig.h file not found) +platform = ${esp32.platformV4} +platform_packages = ${esp32.platformV4_packages} board = esp32-s3-devkitc-1 build_unflags = -D USERMOD_DALLASTEMPERATURE ;; disabled because it hangs during usermod setup on -S3 (autodetect broken?) @@ -1919,10 +2074,16 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden lib_deps = ${esp32s3.lib_deps} ${common_mm.lib_deps_S} ${common_mm.HUB75_lib_deps} ${common_mm.animartrix_lib_deps} +board_build.f_flash = 80000000L +board_build.flash_mode = dio +board_build.partitions = ${esp32.extended_partitions} ;; 1.65MB firmware, 700KB filesystem [env:esp32S3_8MB_M] extends = esp32_4MB_V4_M_base +;; use standard espressif platform - tasmota platform seems to have problems when there is no PSRAM (sdkconfig.h file not found) +platform = ${esp32.platformV4} +platform_packages = ${esp32.platformV4_packages} board = esp32-s3-devkitc-1 build_unflags = -D USERMOD_DALLASTEMPERATURE ;; disabled because it hangs during usermod setup on -S3 (autodetect broken?) @@ -1976,6 +2137,9 @@ board_build.flash_mode = qio ;; HUB75 supported, but may still hve pin conflicts [env:esp32S3_8MB_PSRAM_M] extends = esp32_4MB_V4_M_base +;; we have enough flash - use standard espressif platform instead of tasmota +platform = ${esp32.platformV4} +platform_packages = ${esp32.platformV4_packages} board = esp32-s3-devkitc-1 ;; generic S3 dev board; the next line adds PSRAM support board_build.arduino.memory_type = qio_opi ;; use with PSRAM: 8MB or 16MB @@ -2020,6 +2184,9 @@ board_build.partitions = tools/WLED_ESP32_8MB.csv [env:esp32S3_8MB_S] ;; MM for ESP32-S3 boards - FASTPATH + optimize for speed; ; HUB75 support included (may still have pin conflicts) extends = esp32_4MB_V4_M_base +;; use standard espressif platform - tasmota platform seems to have problems when there is no PSRAM (sdkconfig.h file not found) +platform = ${esp32.platformV4} +platform_packages = ${esp32.platformV4_packages} board = esp32-s3-devkitc-1 ;; generic S3 dev board 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 @@ -2067,6 +2234,9 @@ board_build.partitions = tools/WLED_ESP32_8MB.csv ;; MOONHUB HUB75 adapter board [env:esp32S3_16MB_PSRAM_M_HUB75] extends = env:esp32S3_8MB_PSRAM_M +;; we have enough flash - use standard espressif platform instead of tasmota +platform = ${esp32.platformV4} +platform_packages = ${esp32.platformV4_packages} board = lilygo-t7-s3 board_build.arduino.memory_type = qio_opi board_build.flash_mode = qio @@ -2106,15 +2276,21 @@ lib_deps = ${esp32s3.lib_deps} ${common_mm.lib_deps_S} ${common_mm.lib_deps_V4_ board_build.partitions = ${esp32.extreme_partitions} board_upload.flash_size = 16MB board_upload.maximum_size =16777216 -; RAM: [== ] 19.0% (used 62332 bytes from 327680 bytes) -; Flash: [====== ] 57.7% (used 1813585 bytes from 3145728 bytes) +; RAM: [== ] 19.1% (used 62712 bytes from 327680 bytes) +; Flash: [====== ] 60.1% (used 1891929 bytes from 3145728 bytes) ;; MM for ESP32-S3 WROOM-2, a.k.a. ESP32-S3 DevKitC-1 v1.1 -;; with >= 16MB FLASH and >= 8MB PSRAM (memory_type: opi_opi) +;; with 16MB FLASH and >= 8MB PSRAM (memory_type: opi_opi) ;; includes HUB75 and AnimArtix [env:esp32S3_WROOM-2_M] extends = env:esp32S3_8MB_PSRAM_M +;; tasmota platform +;; platform = ${esp32s3.platform} +;; platform_packages = ${esp32s3.platform_packages} +;; we have enough flash - use standard espressif platform instead of tasmota +platform = ${esp32.platformV4} +platform_packages = ${esp32.platformV4_packages} board_build.flash_mode = dout ;; dummy value - bootloader will switch to "opi" board_build.arduino.memory_type = opi_opi board = esp32s3camlcd ;; this is the only standard board with "opi_opi" @@ -2145,9 +2321,24 @@ lib_deps = ${esp32s3.lib_deps} ${common_mm.lib_deps_S} ${common_mm.lib_deps_V4_ ${common_mm.HUB75_lib_deps} ${common_mm.animartrix_lib_deps} +;; MM for ESP32-S3 WROOM-2 with 32MB flash and PSRAM +[env:esp32S3_WROOM-2_M_32MB] +extends = env:esp32S3_WROOM-2_M +board_build.partitions = tools/WLED_ESP32_32MB.csv +board_upload.flash_size = 32MB +board_upload.maximum_size = 33554432 +;; replace WLED_RELEASE_NAME, keep everything else +build_unflags = ${env:esp32S3_WROOM-2_M.build_unflags} + -D WLED_RELEASE_NAME=esp32S3_WROOM-2_M +build_flags = ${env:esp32S3_WROOM-2_M.build_flags} + -D WLED_RELEASE_NAME=esp32S3_WROOM-2_M_32MB + + ;; MM for esp32-s3 zero/supermini and lolin S3 mini boards - fastpath, optimize for speed [env:esp32S3_4MB_PSRAM_S] extends = env:esp32S3_8MB_S +platform = ${esp32s3.platform} +platform_packages = ${esp32s3.platform_packages} board = lolin_s3_mini ;; -S3 mini: 4MB flash 2MB PSRAM board_build.partitions = tools/WLED_ESP32_4MB_256KB_FS.csv ;; 1.8MB firmware, 256KB filesystem (esptool erase_flash needed when changing from "standard WLED" partitions) ;; board_build.partitions = tools/WLED_ESP32_4MB_512KB_FS.csv ;; 1.7MB firmware, 500KB filesystem (esptool erase_flash needed when changing from "standard WLED" partitions) @@ -2165,13 +2356,13 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden -D WLED_DISABLE_ADALIGHT ;; disables serial protocols - recommended for Hardware-CDC USB (Serial RX will receive junk commands when RX pin is unconnected, unless its pulled down by resistor) ${Speed_Flags.build_flags_V4} ;; optimize for speed instead of size -D WLEDMM_FASTPATH ;; WLEDMM experimental option. Reduces audio lag (latency), and allows for faster LED framerates. May break compatibility with previous versions. - -D WLEDMM_SAVE_FLASH + ;; -D WLEDMM_SAVE_FLASH ${common_mm.animartrix_build_flags} -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0 -D WLED_DISABLE_LOXONE ; FLASH 1272 bytes - -D WLED_DISABLE_ALEXA ; RAM 116 bytes; FLASH 13524 bytes - -D WLED_DISABLE_MQTT ; RAM 216 bytes; FLASH 16496 bytes - -D WLED_DISABLE_HUESYNC ;RAM 122 bytes; FLASH 6308 bytes + ;; -D WLED_DISABLE_ALEXA ; RAM 116 bytes; FLASH 13524 bytes + ;; -D WLED_DISABLE_MQTT ; RAM 216 bytes; FLASH 16496 bytes + ;; -D WLED_DISABLE_HUESYNC ;RAM 122 bytes; FLASH 6308 bytes -D WLED_DISABLE_INFRARED ;RAM 136 bytes; FLASH 24492 bytes -D LEDPIN=21 -D BTNPIN=-1 -D RLYPIN=-1 -D IRPIN=-1 @@ -2187,14 +2378,16 @@ lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation ${common_mm.HUB75_lib_ignore} ${common_mm.DMXin_lib_ignore} -; RAM: [== ] 18.6% (used 60828 bytes from 327680 bytes) -; Flash: [======== ] 76.9% (used 1461829 bytes from 1900544 bytes) +; RAM: [== ] 16.1% (used 52912 bytes from 327680 bytes) +; Flash: [======= ] 66.2% (used 1257281 bytes from 1900544 bytes) ;; MM for esp32-s3 zero/supermini and lolin S3 mini boards - standard [env:esp32S3_4MB_PSRAM_M] extends = env:esp32S3_8MB_M +platform = ${esp32s3.platform} +platform_packages = ${esp32s3.platform_packages} board = lolin_s3_mini ;; -S3 mini: 4MB flash 2MB PSRAM -board_build.partitions = ${esp32.default_partitions} +board_build.partitions = ${esp32.extended_partitions} ;; 1.65MB firmware, 700KB filesystem build_unflags = ${common.build_unflags} -D WLED_ENABLE_HUB75MATRIX ;; board does not have enough pins for HUB75 -D USERMOD_ANIMARTRIX ;; not enough flash @@ -2218,21 +2411,20 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden -D WLED_DISABLE_INFRARED ; RAM 136 bytes; FLASH 24492 bytes - disabled to stay below 100% ;; -D WLED_DISABLE_ALEXA ; RAM 116 bytes; FLASH 13524 bytes ;; -D WLED_DISABLE_MQTT ; RAM 216 bytes; FLASH 16496 bytes - -D WLEDMM_SAVE_FLASH + ;; -D WLEDMM_SAVE_FLASH ; -D WLED_DEBUG ; -D SR_DEBUG ; -D MIC_LOGGER - -D WLED_DISABLE_PARTICLESYSTEM1D ;; exceeds flash size limit - -D WLED_DISABLE_PARTICLESYSTEM2D ;; exceeds flash size limit + ;; -D WLED_DISABLE_PARTICLESYSTEM1D ;; exceeds flash size limit + ;; -D WLED_DISABLE_PARTICLESYSTEM2D ;; exceeds flash size limit lib_deps = ${esp32s3.lib_deps} ${common_mm.lib_deps_S} ${common_mm.lib_deps_V4_M} lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation ${common_mm.HUB75_lib_ignore} ${common_mm.DMXin_lib_ignore} ${common_mm.animartrix_lib_ignore} -; RAM: [== ] 18.7% (used 61172 bytes from 327680 bytes) -; Flash: [==========] 97.7% (used 1536157 bytes from 1572864 bytes) - +; RAM: [== ] 16.1% (used 52744 bytes from 327680 bytes) +; Flash: [======== ] 83.5% (used 1312937 bytes from 1572864 bytes) # ------------------------------------------------------------------------------ # esp32-S2 environments @@ -2243,12 +2435,9 @@ lib_ignore = ;; to ewowi - i'll optimize this entry later, as a few things can be inherited for sure. To softhack: sure ;-) [env:esp32s2_tinyUF2_PSRAM_S] extends = esp32_4MB_V4_S_base -;;platform = ${esp32s2.platform} ;; using 5.2.0, due to massive connectivity problems on -S2 with 5.3.0 -;;platform_packages = ${esp32s2.platform_packages} - ;; tasmota platform needed, as we are over flash size limits for the standard platform -platform = ${esp32_idf_V4.platform} -platform_packages = ${esp32_idf_V4.platform_packages} +platform = ${esp32s2.platform} +platform_packages = ${esp32s2.platform_packages} board = adafruit_qtpy_esp32s2 board_build.partitions = tools/partitions-4MB_spiffs-tinyuf2.csv ;; this is needed for tinyUF2 bootloader! Filename has to end in "tinyuf2.csv" @@ -2299,19 +2488,19 @@ lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation OneWire ; not needed as we don't include USERMOD_DALLASTEMPERATURE monitor_filters = esp32_exception_decoder -; RAM: [== ] 17.9% (used 58548 bytes from 327680 bytes) -; Flash: [======== ] 82.0% (used 1182554 bytes from 1441792 bytes) +; RAM: [== ] 17.9% (used 58728 bytes from 327680 bytes) +; Flash: [======== ] 83.6% (used 1204774 bytes from 1441792 bytes) ;; MM environment for generic ESP32-S2, with PSRAM, 4MB flash (300kB filesystem to have more program space) ;; PINs assignments optimized for use with serg74 "mini shield" [env:esp32s2_PSRAM_M] extends = esp32_4MB_V4_M_base -;; standard platform -platform = ${esp32s2.platform} ;; using 5.2.0, due to massive connectivity problems on -S2 with 5.3.0 +;; default tasmota platform +platform = ${esp32s2.platform} platform_packages = ${esp32s2.platform_packages} -;; tasmota platform (optional) -;; platform = ${esp32_idf_V4.platform} -;; platform_packages = ${esp32_idf_V4.platform_packages} +;; standard espressif platform (optional) +;; platform = ${esp32.platformV4} +;; platform_packages = ${esp32.platformV4_packages} board = lolin_s2_mini board_build.partitions = ${esp32.extended_partitions} ;; 1.65MB firmware, 700KB filesystem (esptool erase_flash needed when changing from "standard WLED" partitions) @@ -2357,8 +2546,8 @@ lib_ignore = ${common_mm.HUB75_lib_ignore} ${common_mm.DMXin_lib_ignore} monitor_filters = esp32_exception_decoder -; RAM: [== ] 20.5% (used 67256 bytes from 327680 bytes) -; Flash: [========= ] 93.3% (used 1590266 bytes from 1703936 bytes) +; RAM: [== ] 18.2% (used 59640 bytes from 327680 bytes) +; Flash: [======== ] 80.3% (used 1368130 bytes from 1703936 bytes) [env:esp32s2_PSRAM_S] extends = env:esp32s2_PSRAM_M @@ -2377,21 +2566,20 @@ lib_deps = ${env:esp32s2_PSRAM_M.lib_deps} lib_ignore = ${env:esp32s2_PSRAM_M.lib_ignore} U8g2 ${common_mm.animartrix_lib_ignore} -; RAM: [== ] 20.4% (used 66792 bytes from 327680 bytes) -; Flash: [========= ] 94.8% (used 1490390 bytes from 1572864 bytes) - +; RAM: [== ] 18.1% (used 59176 bytes from 327680 bytes) +; Flash: [======== ] 81.0% (used 1273946 bytes from 1572864 bytes) # ------------------------------------------------------------------------------ # esp32-C3 environments # ------------------------------------------------------------------------------ -;; MM environment for generic ESP32-C3 -> 4MB flash, no PSRAM +;; MM environment for generic ESP32-C3 -> 4MB flash, qio mode, no PSRAM [env:esp32c3dev_4MB_M] extends = esp32_4MB_V4_S_base ;board_build.flash_mode = dout -platform = ${esp32.platformV4} -platform_packages = ${esp32.platformV4_packages} +platform = ${esp32c3.platform} +platform_packages = ${esp32c3.platform_packages} board_build.flash_mode = qio board = esp32-c3-devkitm-1 ;board_build.partitions = tools/WLED_ESP32_4MB_256KB_FS.csv ;; 1.8MB firmware, 256KB filesystem (esptool erase_flash needed when changing from "standard WLED" partitions) @@ -2406,9 +2594,9 @@ build_unflags = ${common.build_unflags} -D WLED_ENABLE_DMX ;; disabled because it does not work with ESP-IDF 4.4.x (buggy driver in SparkFunDMX) -D WLED_ENABLE_DMX_INPUT ;; needs more testing ;-D WLED_DEBUG_HOST='"192.168.x.x"' ;; to disable net print - -D USERMOD_ANIMARTRIX ;; Tips our memory usage over the limit + ${common_mm.animartrix_build_flags} ;; Tips our memory usage over the limit -DUSERMOD_ARTIFX ;; over the limit - -DWLEDMM_FASTPATH ;; needs more testing on -C3 + ; -DWLED_ENABLE_FULL_FONTS ;; removing full unicode support saves 10KB of flash build_flags = ${common.build_flags} ${esp32c3.build_flags} ; -D WLED_DISABLE_OTA ;; OTA is not possible for boards with 2MB flash only (like some Ai-Thinker ESP32-C3-12F models) @@ -2424,7 +2612,7 @@ build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLEDMM_WIFI_POWERON_HACK -DLOLIN_WIFI_FIX ;; use this _only_ if your device is not able to make a WiFI connection! ;-D WLED_DISABLE_INFRARED ;; save flash space ;-D WLED_DISABLE_ALEXA ;; save flash space - -D WLEDMM_SAVE_FLASH + ;-D WLEDMM_SAVE_FLASH -D LEDPIN=8 ;; onboard neopixel 5x5 Matrix. Attach your own LEDs to GPIO 20 -D BTNPIN=9 ; -D STATUSLED=10 ;; onboard LED @@ -2432,15 +2620,17 @@ build_flags = ${common.build_flags} ${esp32c3.build_flags} ;-D HW_PIN_SDA=0 -D HW_PIN_SCL=1 ;; for I2C Qwiic connector -D SR_DMTYPE=1 -D I2S_SDPIN=5 -D I2S_WSPIN=6 -D I2S_CKPIN=4 -D MCLK_PIN=7 ; -D WLED_USE_MY_CONFIG - -D WLED_DISABLE_PARTICLESYSTEM1D ;; exceeds flash size limit - -D WLED_DISABLE_PARTICLESYSTEM2D ;; exceeds flash size limit + ;; -D WLED_DISABLE_PARTICLESYSTEM1D ;; exceeds flash size limit + ;; -D WLED_DISABLE_PARTICLESYSTEM2D ;; exceeds flash size limit lib_deps = ${esp32c3.lib_deps} ${common_mm.lib_deps_S} ${common_mm.lib_deps_V4_M} lib_ignore = ;IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation OneWire ; not needed as we don't include USERMOD_DALLASTEMPERATURE U8g2 ; not needed as we don't include USERMOD_FOUR_LINE_DISPLAY -; RAM: [== ] 24.6% (used 80732 bytes from 327680 bytes) -; Flash: [==========] 97.5% (used 1533360 bytes from 1572864 bytes) + ${common_mm.animartrix_lib_ignore} ;; Tips our memory usage over the limit +; RAM: [== ] 22.1% (used 72408 bytes from 327680 bytes) +; Flash: [======== ] 83.5% (used 1313420 bytes from 1572864 bytes) + ;; MM environment for ESP32-C3 "mini" and "super mini" -> flash mode "dio" instead of "qio" (see #101) [env:esp32c3mini_dio_4MB_M] extends = env:esp32c3dev_4MB_M @@ -2455,18 +2645,16 @@ build_flags = ${env:esp32c3dev_4MB_M.build_flags} -DARDUINO_USB_CDC_ON_BOOT=0 -D WLED_RELEASE_NAME=esp32c3mini_dio_4MB_M -D WLED_DISABLE_BROWNOUT_DET ;; the board only has a 500mA LDO, better to disable brownout detection - -D WLED_DISABLE_ADALIGHT ;; to disable serial protocols for boards with CDC USB (Serial RX will receive junk commands, unless its pulled down by resistor) -D HW_PIN_SDA=0 -D HW_PIN_SCL=1 ;; avoid pin conflicts - -D WLED_DISABLE_PARTICLESYSTEM1D ;; exceeds flash size limit - -D WLED_DISABLE_PARTICLESYSTEM2D ;; exceeds flash size limit -; RAM: [== ] 24.6% (used 80556 bytes from 327680 bytes) -; Flash: [==========] 97.3% (used 1530346 bytes from 1572864 bytes) +; RAM: [== ] 22.0% (used 72240 bytes from 327680 bytes) +; Flash: [======== ] 83.4% (used 1311474 bytes from 1572864 bytes) [env:esp32c3dev_2MB_M] extends = env:esp32c3dev_4MB_M board = lolin_c3_mini ;;; replace WLED_RELEASE_NAME, disable CDC_ON_BOOT build_unflags = ${env:esp32c3dev_4MB_M.build_unflags} + ;; -DWLED_ENABLE_FULL_FONTS ;; removing full unicode support saves 10KB of flash -DARDUINO_USB_CDC_ON_BOOT=1 -D WLED_RELEASE_NAME=esp32c3dev_4MB_M @@ -2483,34 +2671,38 @@ build_flags = ${env:esp32c3dev_4MB_M.build_flags} -DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip -D WLED_RELEASE_NAME=esp32c3dev_2MB_M -D WLED_DISABLE_BROWNOUT_DET ;; the board only has a 500mA LDO, better to disable brownout detection - -D WLED_DISABLE_ADALIGHT ;; to disable serial protocols for boards with CDC USB (Serial RX will receive junk commands, unless its pulled down by resistor) + ; -D WLED_DISABLE_ADALIGHT ;; to disable serial protocols for boards with CDC USB (Serial RX will receive junk commands, unless its pulled down by resistor) -D HW_PIN_SDA=0 -D HW_PIN_SCL=1 ;; avoid pin conflicts - -D WLED_DISABLE_PARTICLESYSTEM1D ;; exceeds flash size limit - -D WLED_DISABLE_PARTICLESYSTEM2D ;; exceeds flash size limit -; RAM: [== ] 24.0% (used 78676 bytes from 327680 bytes) -; Flash: [==========] 96.4% (used 1516068 bytes from 1572864 bytes) + ;; -D WLED_DISABLE_PARTICLESYSTEM1D ;; exceeds flash size limit + ;; -D WLED_DISABLE_PARTICLESYSTEM2D ;; exceeds flash size limit +; RAM: [== ] 21.5% (used 70296 bytes from 327680 bytes) +; Flash: [======== ] 82.5% (used 1297438 bytes from 1572864 bytes) ;; MM environment for "seeed xiao -C3" boards [env:seeed_esp32c3_4MB_S] extends = env:esp32c3dev_4MB_M board = seeed_xiao_esp32c3 -platform = ${esp32.platformV4_pre} ;; standard IDF 4.4.1 platform - seems to work better for seeed xiao board +platform = ${esp32.platformV4_pre} ;; older espressif ESP-IDF v4.4.1 platform - seems to work better for seeed xiao board platform_packages = ${esp32.platformV4_packages_pre} -board_build.flash_mode = qio +board_build.flash_mode = dio upload_speed = 460800 build_unflags = ${env:esp32c3dev_4MB_M.build_unflags} - -DWLEDMM_FASTPATH ;; needs more testing on -C3 -D WLED_ENABLE_HUB75MATRIX ;; not enough pins + -DARDUINO_USB_CDC_ON_BOOT=1 ;; hangs on boot + ; -DWLED_ENABLE_FULL_FONTS ;; removing full unicode support saves 10KB of flash build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0 ${common_mm.build_flags_S} -Wno-misleading-indentation -Wno-format-truncation -D WLED_RELEASE_NAME=seeed_esp32c3_4MB_S - -DARDUINO_USB_CDC_ON_BOOT=1 ;; enable CDC USB -> needed for debugging over serial USB + -DARDUINO_USB_CDC_ON_BOOT=0 ;; disable CDC USB, as the older framework has problems with it -D WLED_DISABLE_ADALIGHT ;; to disable serial protocols when using CDC USB (Serial RX pin will receive junk commands, unless its pulled down by resistor) -D WLED_DISABLE_INFRARED ;; save flash space ;-D WLED_DISABLE_ALEXA ;; save flash space ;-D WLED_DISABLE_HUESYNC ;; save flash space ;-D WLED_DISABLE_LOXONE ;; save flash space + ;; -D WLED_DISABLE_PARTICLESYSTEM1D ;; exceeds flash size limit + ;; -D WLED_DISABLE_PARTICLESYSTEM2D ;; exceeds flash size limit + ;; -D WLEDMM_SAVE_FLASH ;-D WLEDMM_WIFI_POWERON_HACK -DLOLIN_WIFI_FIX ;; use this _only_ if your device is not able to make a WiFI connection! -D LEDPIN=3 ;; attach your LEDs to GPIO3 aka "D1" / "A1" -D BTNPIN=9 @@ -2522,8 +2714,9 @@ build_flags = ${common.build_flags} ${esp32c3.build_flags} lib_deps = ${esp32c3.lib_deps} ${common_mm.lib_deps_S} lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation monitor_filters = esp32_exception_decoder -; RAM: [== ] 22.4% (used 73388 bytes from 327680 bytes) -; Flash: [========= ] 92.7% (used 1458598 bytes from 1572864 bytes) +; RAM: [== ] 22.5% (used 73740 bytes from 327680 bytes) +; Flash: [==========] 96.3% (used 1515336 bytes from 1572864 bytes) + # ------------------------------------------------------------------------------ # custom board environments # ------------------------------------------------------------------------------ @@ -2602,15 +2795,16 @@ extends = wemos_shield_esp32_4MB_S_base build_flags = ${wemos_shield_esp32_4MB_S_base.build_flags} -D WLED_RELEASE_NAME=wemos_shield_esp32_16MB_S board = esp32_16MB -board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +;board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +board_build.partitions = ${esp32.extreme_partitions} ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem [env:wemos_shield_esp32_16MB_M] extends = wemos_shield_esp32_4MB_M_base build_flags = ${wemos_shield_esp32_4MB_M_base.build_flags} -D WLED_RELEASE_NAME=wemos_shield_esp32_16MB_M -board = esp32_16MB -board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem -;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem +board = esp32_16MB +;board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +board_build.partitions = ${esp32.extreme_partitions} ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem ;board_build.flash_mode = qio ; RAM: [== ] 24.4% (used 79820 bytes from 327680 bytes) ; Flash: [======= ] 66.4% (used 1393421 bytes from 2097152 bytes) @@ -2629,8 +2823,8 @@ extends = wemos_shield_esp32_4MB_XL_base build_flags = ${wemos_shield_esp32_4MB_XL_base.build_flags} ${Shield_ICS4343x.build_flags} -D WLED_RELEASE_NAME=wemos_shield_esp32_16MB_ICS4343x_XL board = esp32_16MB -board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem -;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem +;board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +board_build.partitions = ${esp32.extreme_partitions} ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem ; RAM: [== ] 24.4% (used 80044 bytes from 327680 bytes) ; Flash: [======= ] 67.9% (used 1424185 bytes from 2097152 bytes) @@ -2638,9 +2832,9 @@ board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for extends = wemos_shield_esp32_4MB_M_base build_flags = ${wemos_shield_esp32_4MB_M_base.build_flags} ${Shield_SPM1423.build_flags} -D WLED_RELEASE_NAME=wemos_shield_esp32_16MB_SPM1423_M -board = esp32_16MB -board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem -;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem +board = esp32_16MB +;board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +board_build.partitions = ${esp32.extreme_partitions} ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem ; RAM: [== ] 24.4% (used 79820 bytes from 327680 bytes) ; Flash: [========= ] 88.6% (used 1393421 bytes from 1572864 bytes) @@ -2649,8 +2843,8 @@ extends = wemos_shield_esp32_4MB_XL_base build_flags = ${wemos_shield_esp32_4MB_XL_base.build_flags} ${Shield_SPM1423.build_flags} -D WLED_RELEASE_NAME=wemos_shield_esp32_16MB_SPM1423_XL board = esp32_16MB -board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem -;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem +;board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +board_build.partitions = ${esp32.extreme_partitions} ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem ; RAM: [== ] 24.4% (used 79820 bytes from 327680 bytes) ; Flash: [========= ] 88.6% (used 1393421 bytes from 1572864 bytes) @@ -2660,8 +2854,8 @@ build_unflags = ${common.build_unflags} ${Shield_LineIn.build_unflags} build_flags = ${wemos_shield_esp32_4MB_M_base.build_flags} ${Shield_LineIn.build_flags} -D WLED_RELEASE_NAME=wemos_shield_esp32_16MB_LineIn_M board = esp32_16MB -board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem -;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem +;board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem +board_build.partitions = ${esp32.extreme_partitions} ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem # ------------------------------------------------------------------------------ @@ -2671,10 +2865,11 @@ board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for ;https://www.athom.tech/blank-1/wled-esp32-music-addressable-led-strip-controller [env:athom_music_esp32_4MB_M] extends = esp32_4MB_M_base -build_unflags = ${common.build_unflags} +build_unflags = ${esp32_legacy.build_unflags} -D USERMOD_ARTIFX ;; disabled to save some program space in flash -D USERMOD_DALLASTEMPERATURE ;; disabled - flash space is too tight for this -D USERMOD_ROTARY_ENCODER_UI ;; see above + ${common_mm.DMXin_build_flags} build_flags = ${esp32_4MB_M_base.build_flags} ${Athom_PDMmic.build_flags} -D WLED_AP_SSID_UNIQUE @@ -2694,6 +2889,8 @@ build_flags = ${esp32_4MB_M_base.build_flags} ; -D PWM_PIN=-1 ; -D WLED_USE_MY_CONFIG -D WLEDMM_SAVE_FLASH +lib_ignore = ${esp32_4MB_M_base.lib_ignore} + ${common_mm.DMXin_lib_ignore} ; RAM: [=== ] 26.3% (used 86204 bytes from 327680 bytes) ; Flash: [========= ] 93.6% (used 1471681 bytes from 1572864 bytes) @@ -2744,15 +2941,18 @@ build_flags = ${esp32_4MB_M_base.build_flags} ; -D MCLK_PIN=0 -D SR_ENABLE_DEFAULT ;; enable at first start - no need to manually set "enable", then reboot ; -D WLED_USE_MY_CONFIG - ; -D WLED_DISABLE_LOXONE + -D WLED_DISABLE_LOXONE ;; exceeds flash size limits ; -D WLED_DISABLE_ALEXA ; -D WLED_DISABLE_HUESYNC ; -D WLED_DISABLE_MQTT ; -D WLED_DISABLE_INFRARED ; -D WLED_ENABLE_DMX -; RAM: [=== ] 26.0% (used 85236 bytes from 327680 bytes) -; Flash: [==========] 97.1% (used 1527049 bytes from 1572864 bytes) - + -D WLEDMM_SAVE_FLASH + -D WLED_DISABLE_ESPNOW ;; might help in case of WiFi connectivity problems + -D WLED_DISABLE_PARTICLESYSTEM1D ;; exceeds flash size limit + ;; -D WLED_DISABLE_PARTICLESYSTEM2D +; RAM: [== ] 24.2% (used 79424 bytes from 327680 bytes) +; Flash: [==========] 99.9% (used 1571177 bytes from 1572864 bytes) ;; experimental ;; PICO environment with ESP-IDF v4.4.4 / arduino-esp32 v2.0.9 [env:esp32_pico_4MB_V4_S] @@ -2762,6 +2962,7 @@ upload_speed = 256000 ;; or 115200 ;; or 460800 ; or 921600 (slower speeds are build_unflags = ${esp32_4MB_V4_S_base.build_unflags} -D WLED_ENABLE_HUB75MATRIX ;; not enough pins + ${common_mm.DMXin_build_flags} ;; board is too small, no pins build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} -D ARDUINO_USB_CDC_ON_BOOT=0 ; needed for arduino-esp32 >=2.0.4; avoids errors on startup -D WLED_RELEASE_NAME=esp32_pico_4MB_V4_S @@ -2781,14 +2982,17 @@ build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} ; -D MCLK_PIN=0 -D SR_ENABLE_DEFAULT ;; enable audioreactive at first start - no need to manually set "enable", then reboot ; -D WLED_USE_MY_CONFIG + -D WLEDMM_SAVE_FLASH + -D WLED_DISABLE_ESPNOW ;; might help in case of WiFi connectivity problems + ;;-D WLED_DISABLE_PARTICLESYSTEM1D ;; exceeds flash size limit (default_partitions only) + ;;-D WLED_DISABLE_PARTICLESYSTEM2D ;; exceeds flash size limit (default_partitions only) lib_deps = ${esp32_4MB_V4_S_base.esp32_lib_deps} lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation ${common_mm.HUB75_lib_ignore} -; RAM: [=== ] 27.9% (used 91528 bytes from 327680 bytes) -; Flash: [==========] 96.7% (used 1521457 bytes from 1572864 bytes) -; - + ${common_mm.DMXin_lib_ignore} +; RAM: [=== ] 26.0% (used 85100 bytes from 327680 bytes) +; Flash: [========= ] 90.0% (used 1534269 bytes from 1703936 bytes) [env:adafruit_matrixportal_esp32s3_legacy] ; ESP32-S3 processor, 8 MB flash, 2 MB of PSRAM, dedicated driver pins for HUB75 diff --git a/readme.md b/readme.md index a44a82b3..7404cd5f 100644 --- a/readme.md +++ b/readme.md @@ -1,12 +1,11 @@

- - + + -

@@ -14,7 +13,7 @@ image -MoonModules/WLED is a fork of [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/). +MoonModules/WLED is a fork of [wled/WLED](https://github.com/wled/WLED) which contains latest merge of v0.14 of WLED with [additional features](https://mm.kno.wled.ge/moonmodules/what-is-moonmodules/). This fork is created by members of the [Atuline/WLED](https://github.com/atuline/WLED) team to make development against v0.14 possible while still preserving [Atuline/WLED v0.13.x](https://github.com/atuline/WLED/tree/dev) as a stable and supported version. The Atuline/WLED fork is also called WLED SR (Sound Reactive). @@ -33,4 +32,4 @@ We welcome contributions to this project! See [contributing](https://github.com/ ## *Disclaimer:* -Using this software is the users responsibility as it is not bug free. Therefore contributors of this repo are not reliable for anything including but not limited to spontaneous combustion of the entire led strip, the house and the inevitable heat death of the universe +Using this software is the users responsibility as it is not bug free. Therefore contributors of this repo are not liable for anything including but not limited to spontaneous combustion of the entire led strip, the house and the inevitable heat death of the universe diff --git a/tools/WLED_ESP32_32MB.csv b/tools/WLED_ESP32_32MB.csv new file mode 100644 index 00000000..2aa06e6f --- /dev/null +++ b/tools/WLED_ESP32_32MB.csv @@ -0,0 +1,7 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x300000, +app1, app, ota_1, 0x310000,0x300000, +spiffs, data, spiffs, 0x610000,0x19E0000, +coredump, data, coredump,,64K diff --git a/tools/cdata.js b/tools/cdata.js index 561d390e..d9664551 100644 --- a/tools/cdata.js +++ b/tools/cdata.js @@ -63,7 +63,10 @@ function adoptVersionAndRepo(html) { // Replace we html = strReplace(html, "https://github.com/atuline/WLED", repoUrl); html = strReplace(html, "https://github.com/Aircoookie/WLED", repoUrl); - html = strReplace(html, "https://github.com/MoonModules/WLED", repoUrl); //WLEDMM + // html = strReplace(html, "https://github.com/wled-dev/WLED", repoUrl); // replacing upstream break "credits" + // html = strReplace(html, "https://github.com/wled/WLED", repoUrl); + // html = strReplace(html, "https://github.com/MoonModules/WLED", repoUrl); //WLEDMM + // html = strReplace(html, "https://github.com/MoonModules/WLED-MM", repoUrl); //WLEDMM - not necessary to replace ourselves ;-) } let version = packageJson.version; if (version) { @@ -416,6 +419,12 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()====="; method: "gzip", filter: "html-minify", }, + { + file: "404mini.htm", + name: "PAGE_404_mini", + method: "gzip", + filter: "html-minify", + }, { file: "favicon.ico", name: "favicon", diff --git a/tools/partitions-16MB_spiffs-tinyuf2.csv b/tools/partitions-16MB_spiffs-tinyuf2.csv new file mode 100644 index 00000000..238710e9 --- /dev/null +++ b/tools/partitions-16MB_spiffs-tinyuf2.csv @@ -0,0 +1,10 @@ +# ESP-IDF Partition Table +# Name, Type, SubType, Offset, Size, Flags +# bootloader.bin,, 0x1000, 32K +# partition table,, 0x8000, 4K +nvs, data, nvs, 0x9000, 20K, +otadata, data, ota, 0xe000, 8K, +ota_0, app, ota_0, 0x10000, 2048K, +ota_1, app, ota_1, 0x210000, 2048K, +uf2, app, factory,0x410000, 256K, +spiffs, data, spiffs, 0x450000, 11968K, diff --git a/usermods/artifx/artifx.js b/usermods/artifx/artifx.js index f2386448..19819ca6 100644 --- a/usermods/artifx/artifx.js +++ b/usermods/artifx/artifx.js @@ -78,7 +78,7 @@ function populateCEEditor(name, segID)

- +

Compile and Run Log

@@ -100,10 +100,10 @@ function populateCEEditor(name, segID) } function downloadGHFile(url, name, save=false, warn=false) { //Githubfile - if (url == "CE") url = "https://raw.githubusercontent.com/MoonModules/WLED-Effects/master/ARTIFX/wled/"; - if (url == "HBB") url = "https://raw.githubusercontent.com/MoonModules/WLED-Effects/master/Presets/HB_PresetPack210808_32x32_16seg/Base%20pack/"; - if (url == "HBE") url = "https://raw.githubusercontent.com/MoonModules/WLED-Effects/master/Presets/HB_PresetPack210808_32x32_16seg/Effects%20pack/"; - if (url == "LM") url = "https://raw.githubusercontent.com/MoonModules/WLED-Effects/master/Ledmaps/"; + if (url == "CE") url = "https://raw.githubusercontent.com/MoonModules/MM-Effects/master/ARTIFX/wled/"; + if (url == "HBB") url = "https://raw.githubusercontent.com/MoonModules/MM-Effects/master/Presets/HB_PresetPack210808_32x32_16seg/Base%20pack/"; + if (url == "HBE") url = "https://raw.githubusercontent.com/MoonModules/MM-Effects/master/Presets/HB_PresetPack210808_32x32_16seg/Effects%20pack/"; + if (url == "LM") url = "https://raw.githubusercontent.com/MoonModules/MM-Effects/master/Ledmaps/"; fetchAndExecute(url, name, null, function(parms,text) { if (save) { @@ -153,4 +153,4 @@ function loadCETemplate(name) { } }`; -} \ No newline at end of file +} diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 0be1bd79..731f96c5 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -3,9 +3,9 @@ /* @title MoonModules WLED - audioreactive usermod @file audio_reactive.h - @repo https://github.com/MoonModules/WLED, submit changes to this file as PRs to MoonModules/WLED - @Authors https://github.com/MoonModules/WLED/commits/mdev/ - @Copyright © 2024 Github MoonModules Commit Authors (contact moonmodules@icloud.com for details) + @repo https://github.com/MoonModules/WLED-MM, submit changes to this file as PRs to MoonModules/WLED-MM + @Authors https://github.com/MoonModules/WLED-MM/commits/mdev/ + @Copyright © 2024,2025 Github MoonModules Commit Authors (contact moonmodules@icloud.com for details) @license Licensed under the EUPL-1.2 or later */ @@ -111,12 +111,12 @@ // sanity checks #ifdef ARDUINO_ARCH_ESP32 - // we need more space in for oappend() stack buffer -> SETTINGS_STACK_BUF_SIZE and CONFIG_ASYNC_TCP_TASK_STACK_SIZE + // we need more space in for oappend() stack buffer -> SETTINGS_STACK_BUF_SIZE and CONFIG_ASYNC_TCP_STACK_SIZE #if SETTINGS_STACK_BUF_SIZE < 3904 // 3904 is required for WLEDMM-0.14.0-b28 #warning please increase SETTINGS_STACK_BUF_SIZE >= 3904 #endif - #if (CONFIG_ASYNC_TCP_TASK_STACK_SIZE - SETTINGS_STACK_BUF_SIZE) < 4352 // at least 4096+256 words of free task stack is needed by async_tcp alone - #error remaining async_tcp stack will be too low - please increase CONFIG_ASYNC_TCP_TASK_STACK_SIZE + #if (CONFIG_ASYNC_TCP_STACK_SIZE - SETTINGS_STACK_BUF_SIZE) < 4352 // at least 4096+256 words of free task stack is needed by async_tcp alone + #error remaining async_tcp stack will be too low - please increase CONFIG_ASYNC_TCP_STACK_SIZE #endif #endif @@ -136,15 +136,19 @@ static bool udpSyncConnected = false; // UDP connection status -> true i // audioreactive variables #ifdef ARDUINO_ARCH_ESP32 +#ifndef SR_AGC // Automatic gain control mode + #ifdef SR_SQUELCH + #define SR_AGC 1 // default "squelch" was provided --> default mode = on + #else + #define SR_AGC 0 // default mode = off + #endif +#endif + static float micDataReal = 0.0f; // MicIn data with full 24bit resolution - lowest 8bit after decimal point static float multAgc = 1.0f; // sample * multAgc = sampleAgc. Our AGC multiplier static float sampleAvg = 0.0f; // Smoothed Average sample - sampleAvg < 1 means "quiet" (simple noise gate) static float sampleAgc = 0.0f; // Smoothed AGC sample -#ifdef SR_SQUELCH -static uint8_t soundAgc = 1; // Automagic gain control: 0 - none, 1 - normal, 2 - vivid, 3 - lazy (config value) - enable AGC if default "squelch" was provided -#else -static uint8_t soundAgc = 0; // Automagic gain control: 0 - none, 1 - normal, 2 - vivid, 3 - lazy (config value) -#endif +static uint8_t soundAgc = SR_AGC; // Automagic gain control: 0 - none, 1 - normal, 2 - vivid, 3 - lazy (config value) - enable AGC if default "squelch" was provided #endif static float volumeSmth = 0.0f; // either sampleAvg or sampleAgc depending on soundAgc; smoothed sample @@ -1750,7 +1754,15 @@ class AudioReactive : public Usermod { // add info for UI if ((receivedPacket.frameCounter > 0) && (lastFrameCounter > 0)) receivedFormat = 3; // v2+ else receivedFormat = 2; // v2 - // check sequence + + // Simpler 8-bit rollover-safe sequence check: + // (int8_t)(cur - prev) > 0 => cur is ahead of prev by 1..127 + // 0 => duplicate, + // < 0 => older + // bool sequenceOK = !audioSyncSequence || receivedPacket.frameCounter == 0 || // always accept legacy "0" + // ((int8_t)(receivedPacket.frameCounter - lastFrameCounter) > 0); + + // check sequence bool sequenceOK = false; if(receivedPacket.frameCounter > lastFrameCounter) sequenceOK = true; // sequence OK if((lastFrameCounter < 12) && (receivedPacket.frameCounter > 248)) sequenceOK = false; // prevent sequence "roll-back" due to late packets (1->254) diff --git a/usermods/audioreactive/audio_source.h b/usermods/audioreactive/audio_source.h index c4de4e33..6d31e029 100644 --- a/usermods/audioreactive/audio_source.h +++ b/usermods/audioreactive/audio_source.h @@ -3,9 +3,9 @@ /* @title MoonModules WLED - audioreactive usermod @file audio_source.h - @repo https://github.com/MoonModules/WLED, submit changes to this file as PRs to MoonModules/WLED - @Authors https://github.com/MoonModules/WLED/commits/mdev/ - @Copyright © 2024 Github MoonModules Commit Authors (contact moonmodules@icloud.com for details) + @repo https://github.com/MoonModules/WLED-MM, submit changes to this file as PRs to MoonModules/WLED-MM + @Authors https://github.com/MoonModules/WLED-MM/commits/mdev/ + @Copyright © 2024,2025 Github MoonModules Commit Authors (contact moonmodules@icloud.com for details) @license Licensed under the EUPL-1.2 or later */ diff --git a/usermods/audioreactive/readme.md b/usermods/audioreactive/readme.md index a80fa681..2054d6b5 100644 --- a/usermods/audioreactive/readme.md +++ b/usermods/audioreactive/readme.md @@ -58,9 +58,10 @@ Other options: You can use the following additional flags in your `build_flags` * `-D SR_SQUELCH=x` : Default "squelch" setting (10) * `-D SR_GAIN=x` : Default "gain" setting (60) +* `-D SR_AGC=x` : (Only ESP32) Default "AGC (Automatic Gain Control)" setting (0): 0=off, 1=normal, 2=vivid, 3=lazy * `-D I2S_USE_RIGHT_CHANNEL`: Use RIGHT instead of LEFT channel (not recommended unless you strictly need this). -* `-D I2S_USE_16BIT_SAMPLES`: Use 16bit instead of 32bit for internal sample buffers. Reduces sampling quality, but frees some RAM ressources (not recommended unless you absolutely need this). -* `-D I2S_GRAB_ADC1_COMPLETELY`: Experimental: continuously sample analog ADC microphone. Only effective on ESP32. WARNING this _will_ cause conflicts(lock-up) with any analogRead() call. +* `-D I2S_USE_16BIT_SAMPLES`: Use 16bit instead of 32bit for internal sample buffers. Reduces sampling quality, but frees some RAM resources (not recommended unless you absolutely need this). +* `-D I2S_GRAB_ADC1_COMPLETELY`: Experimental: continuously sample analog ADC microphone. Only effective on ESP32. WARNING this *will* cause conflicts(lock-up) with any analogRead() call. * `-D MIC_LOGGER` : (debugging) Logs samples from the microphone to serial USB. Use with serial plotter (Arduino IDE) * `-D SR_DEBUG` : (debugging) Additional error diagnostics and debug info on serial USB. diff --git a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h index a4c557dd..1e608ad3 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -3,9 +3,9 @@ /* @title MoonModules WLED - auto-playlist usermod @file usermod_v2_auto_playlist.h - @repo https://github.com/MoonModules/WLED, submit changes to this file as PRs to MoonModules/WLED - @Authors https://github.com/MoonModules/WLED/commits/mdev/ - @Copyright © 2024 Github MoonModules Commit Authors (contact moonmodules@icloud.com for details) + @repo https://github.com/MoonModules/WLED-MM, submit changes to this file as PRs to MoonModules/WLED-MM + @Authors https://github.com/MoonModules/WLED-MM/commits/mdev/ + @Copyright © 2024,2025 Github MoonModules Commit Authors (contact moonmodules@icloud.com for details) @license Licensed under the EUPL-1.2 or later */ @@ -257,7 +257,7 @@ class AutoPlaylistUsermod : public Usermod { // after change_lockout. Better for smaller change_lockout values. suspendPlaylist(); // suspend the playlist engine before changing to another preset - applyPreset(newpreset); + applyPreset(newpreset, CALL_MODE_NOTIFICATION); #ifdef USERMOD_AUTO_PLAYLIST_DEBUG USER_PRINTF("*** CHANGE distance = %4lu - change_interval was %5ldms - next change_threshold is %4u (%4u diff aprox)\n",(unsigned long)distance,change_interval,change_threshold,change_threshold_change); diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 138e9690..fc01a567 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -10,6 +10,10 @@ #include "FX.h" #include "fcn_declare.h" +#if defined(WLED_ENABLE_FULL_FONTS) +#include "src/font/codepages.h" +#endif + #ifdef WLEDMM_FASTPATH #undef SEGMENT #undef SEGENV @@ -94,6 +98,7 @@ static float mapf(float x, float in_min, float in_max, float out_min, float out_ // more accurate integer version of map() - based on map3() proposed in https://forum.arduino.cc/t/how-map-loses-precision-and-how-to-fix-it/371026/3 // rounding instead of truncation, better handling of inverted ranges +// Important: don't use when the input range is very small, because the output is such cases is worse than map() static long map2(long x, long in_min, long in_max, long out_min, long out_max) { long out_range = out_max - out_min; @@ -109,6 +114,19 @@ static long map2(long x, long in_min, long in_max, long out_min, long out_max) return ((x - in_min) * out_range) / in_range + out_min; } +// better "map" that can be used when in_min = out_min = 0. +// Fast and accurate (error always below 0.5) +static inline uint32_t map0(uint32_t val, uint32_t in_max, uint32_t out_max) { + if (in_max == 0) return 0; // avoid division by zero + return ( (val*out_max) + (in_max/2) ) / in_max; // +(in_max/2) for rounding +} +// a variant of map0() that handles output ranges not starting at 0 - but still requires val in [0 ... in_max] +static inline int32_t map0(uint32_t val, uint32_t in_max, int32_t out_min, int32_t out_max) { + if (out_min > out_max) std::swap(out_min, out_max); // a hack, just to treat inverted ranges without producing overflows + int range = out_max - out_min; + return int(map0(val, in_max, unsigned(range))) + out_min; +} + static um_data_t* getAudioData() { um_data_t *um_data; @@ -118,8 +136,35 @@ static um_data_t* getAudioData() { } return um_data; } + // effect functions +// forward declarations +static uint16_t mode_oops(void); +uint16_t mode_rainbow_cycle(void); +static uint16_t mode_2DAkemi_core(bool withSound = true); +uint16_t mode_static(void); + +/* + * replacement for mode_static in case of out-of-memory. + */ +static uint16_t mode_oops(void) { + strip._colors_t[0] = RED; + strip._colors_t[1] = BLUE; + strip._colors_t[2] = GREEN; + errorFlag = ERR_NORAM_PX; + if (SEGLEN <= 1) return mode_static(); + const uint16_t width = SEGMENT.is2D() ? SEGMENT.virtualWidth() : SEGMENT.virtualLength(); + const uint16_t height = SEGMENT.virtualHeight(); + + // 2D fallback: akemi in blue + if (SEGMENT.is2D() && (width > 3) && (height > 3)) + return mode_2DAkemi_core(false); + + // 1D fallback: rainbow + return mode_rainbow_cycle(); +} + /* * No blinking. Just plain old static light. */ @@ -331,7 +376,7 @@ static const char _data_FX_MODE_RANDOM_COLOR[] PROGMEM = "Random Colors@!,Fade t * to new random colors. */ uint16_t mode_dynamic(void) { - if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(SEGLEN)) return mode_oops(); //allocation failed if(SEGENV.call == 0) { SEGMENT.setUpLeds(); // WLEDMM use lossless getPixelColor() @@ -806,7 +851,7 @@ static const char _data_FX_MODE_MULTI_STROBE[] PROGMEM = "Strobe Mega@!,!;!,!;!; * Android loading circle */ uint16_t mode_android(void) { - if (SEGLEN <= 1) return mode_static(); // WLEDMM to prevent division by zero + if (SEGLEN <= 1) return mode_oops(); // WLEDMM to prevent division by zero for (int i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); } @@ -1021,7 +1066,7 @@ static const char _data_FX_MODE_COLORFUL[] PROGMEM = "Colorful@!,Saturation;1,2, * Emulates a traffic light. */ uint16_t mode_traffic_light(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); for (int i=0; i < SEGLEN; i++) SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); uint32_t mdelay = 500; @@ -1054,7 +1099,7 @@ static const char _data_FX_MODE_TRAFFIC_LIGHT[] PROGMEM = "Traffic Light@!,US st */ #define FLASH_COUNT 4 uint16_t mode_chase_flash(void) { - if (SEGLEN <= 1) return mode_static(); + if (SEGLEN <= 1) return mode_oops(); uint8_t flash_step = SEGENV.call % ((FLASH_COUNT * 2) + 1); for (int i = 0; i < SEGLEN; i++) { @@ -1084,7 +1129,7 @@ static const char _data_FX_MODE_CHASE_FLASH[] PROGMEM = "Chase Flash@!;Bg,Fx;!"; * Prim flashes running, followed by random color. */ uint16_t mode_chase_flash_random(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); uint8_t flash_step = SEGENV.call % ((FLASH_COUNT * 2) + 1); for (int i = 0; i < SEGENV.aux1; i++) { @@ -1219,7 +1264,7 @@ static const char _data_FX_MODE_DUAL_LARSON_SCANNER[] PROGMEM = "Scanner Dual@!, * Firing comets from one end. "Lighthouse" */ uint16_t mode_comet(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); uint16_t counter = strip.now * ((SEGMENT.speed >>2) +1); uint16_t index = (counter * SEGLEN) >> 16; if (SEGENV.call == 0) { @@ -1251,7 +1296,7 @@ static const char _data_FX_MODE_COMET[] PROGMEM = "Lighthouse@!,Fade rate;!,!;!" * Fireworks function. */ static uint16_t mode_fireworks_core(bool useaudio) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); const uint16_t width = SEGMENT.is2D() ? SEGMENT.virtualWidth() : SEGMENT.virtualLength(); const uint16_t height = SEGMENT.virtualHeight(); @@ -1326,7 +1371,7 @@ static const char _data_FX_MODE_FIREWORKS_AR[] PROGMEM = "Fireworks audio ☾@,F //Twinkling LEDs running. Inspired by https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Rain.h uint16_t mode_rain() { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); const uint16_t width = SEGMENT.virtualWidth(); const uint16_t height = SEGMENT.virtualHeight(); if(SEGENV.call == 0) { @@ -1441,7 +1486,7 @@ static const char _data_FX_MODE_LOADING[] PROGMEM = "Loading@!,Fade;!,!;!;;ix=16 //American Police Light with all LEDs Red and Blue uint16_t police_base(uint32_t color1, uint32_t color2) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); uint16_t delay = 1 + (FRAMETIME<<3) / SEGLEN; // longer segments should change faster uint32_t it = strip.now / map(SEGMENT.speed, 0, 255, delay<<4, delay); uint16_t offset = it % SEGLEN; @@ -1570,7 +1615,7 @@ static const char _data_FX_MODE_FAIRY[] PROGMEM = "Fairy@!,# of flashers;!,!;!"; */ uint16_t mode_fairytwinkle() { uint16_t dataSize = sizeof(flasher) * SEGLEN; - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize)) return mode_oops(); //allocation failed Flasher* flashers = reinterpret_cast(SEGENV.data); uint16_t now16 = strip.now & 0xFFFF; uint16_t PRNG16 = 5100 + strip.getCurrSegmentId(); @@ -1778,7 +1823,7 @@ uint16_t mode_multi_comet(void) { uint32_t cycleTime = 10 + (uint32_t)(255 - SEGMENT.speed); uint32_t it = strip.now / cycleTime; if (SEGENV.step == it) return FRAMETIME; - if (!SEGENV.allocateData(sizeof(uint16_t) * 8)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(sizeof(uint16_t) * 8)) return mode_oops(); //allocation failed if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} // WLEDMM use lossless getPixelColor() SEGMENT.fade_out(SEGMENT.intensity); @@ -1815,7 +1860,7 @@ uint16_t mode_multi_comet_ar(void) { uint32_t it = strip.now / cycleTime; if (SEGENV.step == it) return FRAMETIME; // too early - if (!SEGENV.allocateData(sizeof(uint16_t) * MAX_COMETS)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(sizeof(uint16_t) * MAX_COMETS)) return mode_oops(); //allocation failed uint16_t* comets = reinterpret_cast(SEGENV.data); if (SEGENV.call == 0) { // do some initializations SEGMENT.setUpLeds(); SEGMENT.fill(BLACK); @@ -1914,7 +1959,7 @@ uint16_t mode_oscillate(void) { uint8_t numOscillators = 3; uint16_t dataSize = sizeof(oscillator) * numOscillators; - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize)) return mode_oops(); //allocation failed Oscillator* oscillators = reinterpret_cast(SEGENV.data); @@ -1963,7 +2008,7 @@ static const char _data_FX_MODE_OSCILLATE[] PROGMEM = "Oscillate"; //TODO uint16_t mode_lightning(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); uint16_t ledstart = random16(SEGLEN); // Determine starting location of flash uint16_t ledlen = 1 + random16(SEGLEN -ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1) uint8_t bri = 255/random8(1, 3); @@ -2114,7 +2159,7 @@ static const char _data_FX_MODE_PARTYJERK[] PROGMEM = "Party jerk@Effect speed,S //eight colored dots, weaving in and out of sync with each other uint16_t mode_juggle(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); if (SEGENV.call == 0) { SEGMENT.setUpLeds(); //lossless getPixelColor() SEGMENT.fill(BLACK); @@ -2184,9 +2229,9 @@ static const char _data_FX_MODE_PALETTE[] PROGMEM = "Palette@Cycle speed;;!;;c3= // feel of your fire: COOLING (used in step 1 above) (Speed = COOLING), and SPARKING (used // in step 3 above) (Effect Intensity = Sparking). uint16_t mode_fire_2012() { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); const uint16_t strips = SEGMENT.nrOfVStrips(); - if (!SEGENV.allocateData(strips * SEGLEN)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(strips * SEGLEN)) return mode_oops(); //allocation failed byte* heat = SEGENV.data; if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} // WLEDMM use lossless getPixelColor() @@ -2418,7 +2463,7 @@ static const char _data_FX_MODE_NOISE16_4[] PROGMEM = "Noise 4@!;!;!"; //based on https://gist.github.com/kriegsman/5408ecd397744ba0393e uint16_t mode_colortwinkle() { uint16_t dataSize = (SEGLEN+7) >> 3; //1 bit per LED - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize)) return mode_oops(); //allocation failed if(SEGENV.call == 0) { SEGMENT.setUpLeds(); // WLEDMM use lossless getPixelColor() SEGMENT.fill(BLACK); @@ -2500,8 +2545,8 @@ static const char _data_FX_MODE_LAKE[] PROGMEM = "Lake@!;Fx;!"; // send a meteor from begining to to the end of the strip with a trail that randomly decays. // adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain uint16_t mode_meteor() { - if (SEGLEN == 1) return mode_static(); - if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed + if (SEGLEN == 1) return mode_oops(); + if (!SEGENV.allocateData(SEGLEN)) return mode_oops(); //allocation failed byte* trail = SEGENV.data; @@ -2540,8 +2585,8 @@ static const char _data_FX_MODE_METEOR[] PROGMEM = "Meteor@!,Trail,,,,Gradient;; // send a meteor from begining to to the end of the strip with a trail that randomly decays. // adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain uint16_t mode_meteor_smooth() { - if (SEGLEN == 1) return mode_static(); - if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed + if (SEGLEN == 1) return mode_oops(); + if (!SEGENV.allocateData(SEGLEN)) return mode_oops(); //allocation failed byte* trail = SEGENV.data; @@ -2578,7 +2623,7 @@ static const char _data_FX_MODE_METEOR_SMOOTH[] PROGMEM = "Meteor Smooth@!,Trail //Railway Crossing / Christmas Fairy lights uint16_t mode_railway() { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); uint16_t dur = (256 - SEGMENT.speed) * 40; uint16_t rampdur = (dur * SEGMENT.intensity) >> 8; if (SEGENV.step > dur) @@ -2636,7 +2681,7 @@ uint16_t ripple_base() random16_add_entropy(esp_random() & 0xFFFF); // improve randomness (esp32) #endif - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize)) return mode_oops(); //allocation failed if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} // WLEDMM use lossless getPixelColor() Ripple* ripples = reinterpret_cast(SEGENV.data); @@ -2692,7 +2737,7 @@ uint16_t ripple_base() uint16_t mode_ripple(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); else SEGMENT.fade_out(250); return ripple_base(); @@ -2701,7 +2746,7 @@ static const char _data_FX_MODE_RIPPLE[] PROGMEM = "Ripple@!,Wave #,,,,,Overlay; uint16_t mode_ripple_rainbow(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); if (SEGENV.call ==0) { SEGENV.aux0 = random8(); SEGENV.aux1 = random8(); @@ -2866,12 +2911,12 @@ static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rat //inspired by https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectBlinkingHalloweenEyes uint16_t mode_halloween_eyes() { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); const uint16_t maxWidth = strip.isMatrix ? SEGMENT.virtualWidth() : SEGLEN; const uint16_t HALLOWEEN_EYE_SPACE = MAX(2, strip.isMatrix ? SEGMENT.virtualWidth()>>4: SEGLEN>>5); const uint16_t HALLOWEEN_EYE_WIDTH = HALLOWEEN_EYE_SPACE/2; uint16_t eyeLength = (2*HALLOWEEN_EYE_WIDTH) + HALLOWEEN_EYE_SPACE; - if (eyeLength >= maxWidth) return mode_static(); //bail if segment too short + if (eyeLength >= maxWidth) return mode_oops(); //bail if segment too short if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); //fill background @@ -2976,7 +3021,7 @@ static const char _data_FX_MODE_TRI_STATIC_PATTERN[] PROGMEM = "Solid Pattern Tr uint16_t spots_base(uint16_t threshold) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); uint16_t maxZones = SEGLEN >> 2; @@ -3032,12 +3077,12 @@ typedef struct Ball { * Bouncing Balls Effect */ uint16_t mode_bouncing_balls(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); //allocate segment data const uint16_t strips = SEGMENT.nrOfVStrips(); // adapt for 2D constexpr size_t maxNumBalls = 16; uint16_t dataSize = sizeof(ball) * maxNumBalls; - if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize * strips)) return mode_oops(); //allocation failed Ball* balls = reinterpret_cast(SEGENV.data); @@ -3118,7 +3163,7 @@ static uint16_t rolling_balls(void) { //allocate segment data const uint16_t maxNumBalls = 16; // 255/16 + 1 uint16_t dataSize = sizeof(rball_t) * maxNumBalls; - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize)) return mode_oops(); //allocation failed rball_t *balls = reinterpret_cast(SEGENV.data); @@ -3203,7 +3248,7 @@ static const char _data_FX_MODE_ROLLINGBALLS[] PROGMEM = "Rolling Balls@!,# of b * Sinelon stolen from FASTLED examples */ uint16_t sinelon_base(bool dual, bool rainbow=false) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); if (SEGENV.call == 0) { SEGENV.setUpLeds(); SEGMENT.fill(BLACK); } // WLEDMM use lossless getPixelColor() SEGMENT.fade_out(SEGMENT.intensity); uint16_t pos = beatsin16_t(SEGMENT.speed/10,0,SEGLEN-1); @@ -3302,7 +3347,7 @@ typedef struct Spark { * modified from https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Popcorn.h */ static uint16_t mode_popcorn_core(bool useaudio) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); //allocate segment data uint16_t strips = SEGMENT.nrOfVStrips(); size_t dataSize = sizeof(spark) * maxNumPopcorn; @@ -3312,7 +3357,7 @@ static uint16_t mode_popcorn_core(bool useaudio) { neededPopcorn = min(max(neededPopcorn, uint8_t(2)), uint8_t(maxNumPopcorn)); dataSize = sizeof(spark) * neededPopcorn; } - if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize * strips)) return mode_oops(); //allocation failed Spark* popcorn = reinterpret_cast(SEGENV.data); @@ -3527,7 +3572,7 @@ typedef struct particle { } star; static uint16_t mode_starburst_core(bool useaudio) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); uint16_t maxData = FAIR_DATA_PER_SEG; //ESP8266: 256 ESP32: 640 uint8_t segs = strip.getActiveSegmentsNum(); if (segs <= (strip.getMaxSegments() /2)) maxData *= 2; //ESP8266: 512 if <= 8 segs ESP32: 1280 if <= 16 segs @@ -3538,7 +3583,7 @@ static uint16_t mode_starburst_core(bool useaudio) { if (numStars > maxStars) numStars = maxStars; uint16_t dataSize = sizeof(star) * numStars; - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize)) return mode_oops(); //allocation failed uint32_t it = strip.now; @@ -3677,7 +3722,7 @@ static const char _data_FX_MODE_STARBURST_AR[] PROGMEM = "Fw Starburst audio ☾ */ uint16_t mode_exploding_fireworks(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); const uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1; const uint16_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength(); if (SEGENV.call == 0) { @@ -3694,7 +3739,7 @@ uint16_t mode_exploding_fireworks(void) unsigned numSparks = min(5 + ((rows*cols) >> 1), maxSparks); unsigned dataSize = sizeof(spark) * numSparks; - if (!SEGENV.allocateData(dataSize + sizeof(float))) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize + sizeof(float))) return mode_oops(); //allocation failed float *dying_gravity = reinterpret_cast(SEGENV.data + dataSize); if (dataSize != SEGENV.aux1) { //reset to flare if sparks were reallocated (it may be good idea to reset segment if bounds change) @@ -3831,12 +3876,12 @@ typedef struct __attribute__ ((packed)) SparkDrop { */ uint16_t mode_drip(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); //allocate segment data uint16_t strips = SEGMENT.nrOfVStrips(); constexpr int maxNumDrops = 4; uint16_t dataSize = sizeof(sparkdrop) * maxNumDrops; - if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize * strips)) return mode_oops(); //allocation failed SparkDrop* drops = reinterpret_cast(SEGENV.data); if (SEGENV.call == 0) { @@ -3950,10 +3995,10 @@ typedef struct Tetris { } tetris; uint16_t mode_tetrix(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); uint16_t strips = SEGMENT.nrOfVStrips(); // allow running on virtual strips (columns in 2D segment) uint16_t dataSize = sizeof(tetris); - if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize * strips)) return mode_oops(); //allocation failed Tetris* drops = reinterpret_cast(SEGENV.data); //if (SEGENV.call == 0) SEGMENT.fill(SEGCOLOR(1)); // will fill entire segment (1D or 2D), then use drop->step = 0 below @@ -4102,7 +4147,7 @@ uint16_t mode_percent(void) { return FRAMETIME; } -static const char _data_FX_MODE_PERCENT[] PROGMEM = "Percent@,% of fill,,,,One color;!,!;!"; +static const char _data_FX_MODE_PERCENT[] PROGMEM = "Percent@!,% of fill,,,,One color;!,!;!"; /* @@ -4259,7 +4304,7 @@ static const char _data_FX_MODE_PACIFICA[] PROGMEM = "Pacifica@!,Angle;;!;;pal=5 * Mode simulates a gradual sunrise */ uint16_t mode_sunrise() { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); //speed 0 - static sun //speed 1 - 60: sunrise time in minutes //speed 60 - 120 : sunset time in minutes - 60; @@ -4373,7 +4418,7 @@ uint16_t mode_noisepal(void) { // Slow noise //#define scale 30 uint16_t dataSize = sizeof(CRGBPalette16) * 2; //allocate space for 2 Palettes (2 * 16 * 3 = 96 bytes) - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize)) return mode_oops(); //allocation failed CRGBPalette16* palettes = reinterpret_cast(SEGENV.data); @@ -4472,7 +4517,7 @@ static const char _data_FX_MODE_FLOW[] PROGMEM = "Flow@!,Zones;;!;;m12=1"; //ver */ uint16_t mode_chunchun(void) { - if (SEGLEN <= 1) return mode_static(); + if (SEGLEN <= 1) return mode_oops(); if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} // WLEDMM use lossless getPixelColor() SEGMENT.fade_out(253); // add a bit of trail // WLEDMM fade rate above 253 has no effect uint32_t counter = ((strip.now * (96 + SEGMENT.speed)) >> 4); // WLEDMM same result, better resolution @@ -4525,13 +4570,13 @@ typedef struct Spotlight { */ uint16_t mode_dancing_shadows(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); uint8_t numSpotlights = map(SEGMENT.intensity, 0, 255, 2, SPOT_MAX_COUNT); // 49 on 32 segment ESP32, 17 on 16 segment ESP8266 bool initialize = SEGENV.aux0 != numSpotlights; SEGENV.aux0 = numSpotlights; uint16_t dataSize = sizeof(spotlight) * numSpotlights; - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize)) return mode_oops(); //allocation failed Spotlight* spotlights = reinterpret_cast(SEGENV.data); if (SEGENV.call == 0) SEGENV.setUpLeds(); // WLEDMM use lossless getPixelColor() @@ -4666,9 +4711,11 @@ static const char _data_FX_MODE_WASHING_MACHINE[] PROGMEM = "Washing Machine@!,! Draws a .gif image from filesystem on the matrix/strip */ uint16_t mode_image(void) { + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up #ifndef WLED_ENABLE_GIF - return mode_static(); + return mode_oops(); #else + if (max(SEGMENT.virtualWidth(),SEGMENT.virtualHeight()) < 4) return mode_oops(); // too small renderImageToSegment(SEGMENT); return FRAMETIME; #endif @@ -4677,7 +4724,7 @@ uint16_t mode_image(void) { // Serial.println(status); // } } -static const char _data_FX_MODE_IMAGE[] PROGMEM = "Image@!,;;;12;sx=128"; +static const char _data_FX_MODE_IMAGE[] PROGMEM = "Image@!,Blur,;;;12;sx=128,ix=0"; /* Blends random colors across palette @@ -4686,7 +4733,7 @@ static const char _data_FX_MODE_IMAGE[] PROGMEM = "Image@!,;;;12;sx=128"; uint16_t mode_blends(void) { uint16_t pixelLen = SEGLEN > UINT8_MAX ? UINT8_MAX : SEGLEN; uint16_t dataSize = sizeof(uint32_t) * (pixelLen + 1); // max segment length of 56 pixels on 16 segment ESP8266 - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize)) return mode_oops(); //allocation failed uint32_t* pixels = reinterpret_cast(SEGENV.data); uint8_t blendSpeed = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 128); uint8_t shift = (strip.now * ((SEGMENT.speed >> 3) +1)) >> 8; @@ -4736,7 +4783,7 @@ uint16_t mode_tv_simulator(void) { uint16_t nr, ng, nb, r, g, b, i, hue; uint8_t sat, bri, j; - if (!SEGENV.allocateData(sizeof(tvSim))) return mode_static(); //allocation failed + if (!SEGENV.allocateData(sizeof(tvSim))) return mode_oops(); //allocation failed TvSim* tvSimulator = reinterpret_cast(SEGENV.data); uint8_t colorSpeed = map(SEGMENT.speed, 0, UINT8_MAX, 1, 20); @@ -4951,7 +4998,7 @@ uint16_t mode_aurora(void) { SEGENV.aux0 = SEGMENT.intensity; if(!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) { // 26 on 32 segment ESP32, 9 on 16 segment ESP8266 - return mode_static(); //allocation failed + return mode_oops(); //allocation failed } waves = reinterpret_cast(SEGENV.data); @@ -5006,7 +5053,7 @@ static const char _data_FX_MODE_AURORA[] PROGMEM = "Aurora@!,!;1,2,3;!;;sx=24,pa // 16 bit perlinmove. Use Perlin Noise instead of sinewaves for movement. By Andrew Tuline. // Controls are speed, # of pixels, faderate. uint16_t mode_perlinmove(void) { - if (SEGLEN == 1) return mode_static(); + if (SEGLEN == 1) return mode_oops(); if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} // WLEDMM use lossless getPixelColor() SEGMENT.fade_out(255-SEGMENT.custom1); for (int i = 0; i < SEGMENT.intensity/16 + 1; i++) { @@ -5069,7 +5116,7 @@ static const char _data_FX_MODE_FLOWSTRIPE[] PROGMEM = "Flow Stripe@Hue speed,Ef // Black hole uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulmatelights.com/gallery/1012 , Modified by: Andrew Tuline - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5110,7 +5157,7 @@ static const char _data_FX_MODE_2DBLACKHOLE[] PROGMEM = "Black Hole@Fade rate,Ou // 2D Colored Bursts // //////////////////////////// uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.soulmatelights.com/gallery/819-colored-bursts , modified by: Andrew Tuline - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5164,7 +5211,7 @@ static const char _data_FX_MODE_2DCOLOREDBURSTS[] PROGMEM = "Colored Bursts@Spee // 2D DNA // ///////////////////// uint16_t mode_2Ddna(void) { // dna originally by by ldirko at https://pastebin.com/pCkkkzcs. Updated by Preyy. WLED conversion by Andrew Tuline. Phases added by @ewoudwijma - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5209,7 +5256,7 @@ static const char _data_FX_MODE_2DDNA[] PROGMEM = "DNA@Scroll speed,Blur,Phases; // 2D DNA Spiral // ///////////////////////// uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulmatelights.com/gallery/512-dna-spiral-variation , modified by: Andrew Tuline - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5258,7 +5305,7 @@ static const char _data_FX_MODE_2DDNASPIRAL[] PROGMEM = "DNA Spiral@Scroll speed ///////////////////////// uint16_t mode_2DDrift() { // By: Stepko https://editor.soulmatelights.com/gallery/884-drift , Modified by: Andrew Tuline // optimized for large panels by @softhack007 - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5312,7 +5359,7 @@ static const char _data_FX_MODE_2DDRIFT[] PROGMEM = "Drift@Rotation speed,Blur,, // 2D Firenoise // ////////////////////////// uint16_t mode_2Dfirenoise(void) { // firenoise2d. By Andrew Tuline. Yet another short routine. - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5347,7 +5394,7 @@ static const char _data_FX_MODE_2DFIRENOISE[] PROGMEM = "Firenoise@X scale,Y sca // 2D Frizzles // ////////////////////////////// uint16_t mode_2DFrizzles(void) { // By: Stepko https://editor.soulmatelights.com/gallery/640-color-frizzles , Modified by: Andrew Tuline - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5437,14 +5484,14 @@ class GameOfLifeGrid { uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https://natureofcode.com/book/chapter-7-cellular-automata/ // and https://github.com/DougHaber/nlife-color , Modified By: Brandon Butler - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); const size_t dataSize = SEGMENT.length() * sizeof(Cell); // Cell = 2 bytes const size_t totalSize = dataSize + 6; // 6 bytes for prevRows(2), prevCols(2), prevPalette, prevWrap - if (!SEGENV.allocateData(totalSize)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(totalSize)) return mode_oops(); //allocation failed uint16_t *prevRows = reinterpret_cast(SEGENV.data); uint16_t *prevCols = reinterpret_cast(SEGENV.data + 2); uint8_t *prevPalette = reinterpret_cast (SEGENV.data + 4); @@ -5659,12 +5706,12 @@ static void setBitValue(uint8_t* byteArray, size_t n, bool value) { } uint16_t mode_2DSnowFall(void) { // By: Brandon Butler // Uses bit array to track snow/particles - if (!strip.isMatrix) return mode_static(); // Not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // 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 + if (!SEGENV.allocateData(dataSize)) return mode_oops(); // Allocation failed byte *grid = reinterpret_cast(SEGENV.data); bool overlay = SEGMENT.check2; // Overlay is inverted. Only draws non-snow. Layer 1 controls snow color @@ -5771,7 +5818,7 @@ static const char _data_FX_MODE_2DSNOWFALL[] PROGMEM = "Snow Fall ☾@!,Spawn Ra // 2D Hiphotic // ///////////////////////// uint16_t mode_2DHiphotic() { // By: ldirko https://editor.soulmatelights.com/gallery/810 , Modified by: Andrew Tuline - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5803,12 +5850,12 @@ typedef struct Julia { } julia; uint16_t mode_2DJulia(void) { // An animated Julia set by Andrew Tuline. - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - if (!SEGENV.allocateData(sizeof(julia))) return mode_static(); + if (!SEGENV.allocateData(sizeof(julia))) return mode_oops(); Julia* julias = reinterpret_cast(SEGENV.data); float reAl; @@ -5927,7 +5974,7 @@ static const char _data_FX_MODE_2DJULIA[] PROGMEM = "Julia@,Max iterations per p // 2D Lissajous // ////////////////////////////// uint16_t mode_2DLissajous(void) { // By: Andrew Tuline - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5969,7 +6016,7 @@ static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous ☾@X frequen // 2D Matrix // /////////////////////// uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams. Adapted by Andrew Tuline & improved by merkisoft and ewowi, and softhack007. - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -6045,7 +6092,7 @@ static const char _data_FX_MODE_2DMATRIX[] PROGMEM = "Matrix@!,Spawning rate,Tra // 2D Metaballs // ///////////////////////// uint16_t mode_2Dmetaballs(void) { // Metaballs by Stefan Petrick. Cannot have one of the dimensions be 2 or less. Adapted by Andrew Tuline. - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -6104,7 +6151,7 @@ static const char _data_FX_MODE_2DMETABALLS[] PROGMEM = "Metaballs@!;;!;2"; // 2D Noise // ////////////////////// uint16_t mode_2Dnoise(void) { // By Andrew Tuline - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -6127,7 +6174,7 @@ static const char _data_FX_MODE_2DNOISE[] PROGMEM = "Noise2D@!,Scale;;!;2"; // 2D Plasma Ball // ////////////////////////////// uint16_t mode_2DPlasmaball(void) { // By: Stepko https://editor.soulmatelights.com/gallery/659-plasm-ball , Modified by: Andrew Tuline - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -6173,7 +6220,7 @@ static const char _data_FX_MODE_2DPLASMABALL[] PROGMEM = "Plasma Ball@Speed,,Fad // return (out_max - out_min) * (x - in_min) / (in_max - in_min) + out_min; //} uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https://editor.soulmatelights.com/gallery/762-polar-lights , Modified by: Andrew Tuline - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -6249,7 +6296,7 @@ static const char _data_FX_MODE_2DPOLARLIGHTS[] PROGMEM = "Polar Lights@!,Scale, // 2D Pulser // ///////////////////////// uint16_t mode_2DPulser(void) { // By: ldirko https://editor.soulmatelights.com/gallery/878-pulse-test , modifed by: Andrew Tuline - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -6277,7 +6324,7 @@ static const char _data_FX_MODE_2DPULSER[] PROGMEM = "Pulser@!,Blur;;!;2"; // 2D Sindots // ///////////////////////// uint16_t mode_2DSindots(void) { // By: ldirko https://editor.soulmatelights.com/gallery/597-sin-dots , modified by: Andrew Tuline - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -6309,7 +6356,7 @@ static const char _data_FX_MODE_2DSINDOTS[] PROGMEM = "Sindots@!,Dot distance,Fa // custom3 affects the blur amount. uint16_t mode_2Dsquaredswirl(void) { // By: Mark Kriegsman. https://gist.github.com/kriegsman/368b316c55221134b160 // Modifed by: Andrew Tuline - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -6349,14 +6396,14 @@ static const char _data_FX_MODE_2DSQUAREDSWIRL[] PROGMEM = "Squared Swirl@,,,,Bl // 2D Sun Radiation // ////////////////////////////// uint16_t mode_2DSunradiation(void) { // By: ldirko https://editor.soulmatelights.com/gallery/599-sun-radiation , modified by: Andrew Tuline - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); const int minSize = min(cols, rows); // WLEDMM const int magnify = (minSize <= 32) ? 8 : 46; // WLEDMM - if (!SEGENV.allocateData(sizeof(byte)*(cols+2)*(rows+2))) return mode_static(); //allocation failed + if (!SEGENV.allocateData(sizeof(byte)*(cols+2)*(rows+2))) return mode_oops(); //allocation failed byte *bump = reinterpret_cast(SEGENV.data); if (SEGENV.call == 0) { @@ -6402,7 +6449,7 @@ static const char _data_FX_MODE_2DSUNRADIATION[] PROGMEM = "Sun Radiation@Varian // 2D Tartan // ///////////////////////// uint16_t mode_2Dtartan(void) { // By: Elliott Kember https://editor.soulmatelights.com/gallery/3-tartan , Modified by: Andrew Tuline - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -6442,7 +6489,7 @@ static const char _data_FX_MODE_2DTARTAN[] PROGMEM = "Tartan@X scale,Y scale,,,S // 2D spaceships // ///////////////////////// uint16_t mode_2Dspaceships(void) { //// Space ships by stepko (c)05.02.21 [https://editor.soulmatelights.com/gallery/639-space-ships], adapted by Blaz Kristan (AKA blazoncek) - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -6490,7 +6537,7 @@ static const char _data_FX_MODE_2DSPACESHIPS[] PROGMEM = "Spaceships@!,Blur;;!;2 //// Crazy bees by stepko (c)12.02.21 [https://editor.soulmatelights.com/gallery/651-crazy-bees], adapted by Blaz Kristan (AKA blazoncek) constexpr uint_fast16_t MAX_BEES = 5; uint16_t mode_2Dcrazybees(void) { - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint_fast16_t cols = SEGMENT.virtualWidth(); const uint_fast16_t rows = SEGMENT.virtualHeight(); @@ -6513,7 +6560,7 @@ uint16_t mode_2Dcrazybees(void) { }; } bee_t; - if (!SEGENV.allocateData(sizeof(bee_t)*MAX_BEES)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(sizeof(bee_t)*MAX_BEES)) return mode_oops(); //allocation failed bee_t *bee = reinterpret_cast(SEGENV.data); if (SEGENV.call == 0) { @@ -6565,7 +6612,7 @@ static const char _data_FX_MODE_2DCRAZYBEES[] PROGMEM = "Crazy Bees@!,Blur;;;2"; //// Ghost Rider by stepko (c)2021 [https://editor.soulmatelights.com/gallery/716-ghost-rider], adapted by Blaz Kristan (AKA blazoncek) #define LIGHTERS_AM 64 // max lighters (adequate for 32x32 matrix) uint16_t mode_2Dghostrider(void) { - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -6583,7 +6630,7 @@ uint16_t mode_2Dghostrider(void) { int8_t Vspeed; } lighter_t; - if (!SEGENV.allocateData(sizeof(lighter_t))) return mode_static(); //allocation failed + if (!SEGENV.allocateData(sizeof(lighter_t))) return mode_oops(); //allocation failed lighter_t *lighter = reinterpret_cast(SEGENV.data); const size_t maxLighters = min(cols + rows, LIGHTERS_AM); @@ -6655,7 +6702,7 @@ static const char _data_FX_MODE_2DGHOSTRIDER[] PROGMEM = "Ghost Rider@Fade rate, //// Floating Blobs by stepko (c)2021 [https://editor.soulmatelights.com/gallery/573-blobs], adapted by Blaz Kristan (AKA blazoncek) #define MAX_BLOBS 8 uint16_t mode_2Dfloatingblobs(void) { - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -6670,7 +6717,7 @@ uint16_t mode_2Dfloatingblobs(void) { uint8_t Amount = (SEGMENT.intensity>>5) + 1; // NOTE: be sure to update MAX_BLOBS if you change this - if (!SEGENV.allocateData(sizeof(blob_t))) return mode_static(); //allocation failed + if (!SEGENV.allocateData(sizeof(blob_t))) return mode_oops(); //allocation failed blob_t *blob = reinterpret_cast(SEGENV.data); // WLEDMM fix SEGENV.step in case that timebase jumps @@ -6761,7 +6808,7 @@ static const char _data_FX_MODE_2DBLOBS[] PROGMEM = "Blobs@!,# blobs,Blur;!;!;2; // 2D Scrolling text // //////////////////////////// uint16_t mode_2Dscrollingtext(void) { - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -6781,9 +6828,15 @@ uint16_t mode_2Dscrollingtext(void) { case 5: letterWidth = 5; letterHeight = 12; break; } const int yoffset = map(SEGMENT.intensity, 0, 255, -rows/2, rows/2) + (rows-letterHeight)/2; - char text[33] = {'\0'}; - unsigned maxLen = (SEGMENT.name) ? min(32, (int)strlen(SEGMENT.name)) : 0; // WLEDMM make it robust against too long segment names - if (SEGMENT.name) for (size_t i=0,j=0; i31 && SEGMENT.name[i]<128) text[j++] = SEGMENT.name[i]; + char text[WLED_MAX_SEGNAME_LEN+1] = {'\0'}; + unsigned maxLen = (SEGMENT.name) ? min(WLED_MAX_SEGNAME_LEN, (int)strlen(SEGMENT.name)) : 0; // WLEDMM make it robust against too long segment names + +#if !defined(WLED_ENABLE_FULL_FONTS) || defined(ESP8266) + if (SEGMENT.name) for (size_t i=0,j=0; i31 && SEGMENT.name[i]<128) text[j++] = SEGMENT.name[i]; // unicode killer +#else + if (SEGMENT.name) for (size_t i=0,j=0; i= 0) || (abs(delayTime) > 1500)) { // WLEDMM keep on scrolling if timebase jumps (supersync, or brightness off, or wifi delay) if ((numberOfLetters * letterWidth) > cols) ++SEGENV.aux0 %= (numberOfLetters * letterWidth) + cols; // offset else SEGENV.aux0 = (cols + (numberOfLetters * letterWidth))/2; SEGENV.aux1 = (SEGENV.aux1 + 1) & 0xFF; // color shift // WLEDMM changed to prevent overflow - SEGENV.step = strip.now + map2(SEGMENT.speed, 0, 255, 10*FRAMETIME_FIXED, 2*FRAMETIME_FIXED); + long minDelay = max((FRAMETIME_FIXED/2 + FRAMETIME_FIXED/4), int(FRAMETIME)); + SEGENV.step = strip.now + map2(SEGMENT.speed, 0, 255, 10*FRAMETIME_FIXED, minDelay); // WLEDMM scroll faster if (!SEGMENT.check2) { for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++ ) SEGMENT.blendPixelColorXY(x, y, SEGCOLOR(1), 255 - (SEGMENT.custom1>>1)); @@ -6846,6 +6906,7 @@ uint16_t mode_2Dscrollingtext(void) { } if (SEGENV.check2 && ((numberOfLetters * letterWidth) > cols)) drawShadow = true; // scrolling overlay is easier to read with shadow +#if !defined(WLED_ENABLE_FULL_FONTS) 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); @@ -6856,10 +6917,26 @@ uint16_t mode_2Dscrollingtext(void) { } SEGMENT.drawCharacter(text[i], int(cols) - int(SEGENV.aux0) + letterWidth*i, yoffset, letterWidth, letterHeight, col1, col2, drawShadow); } +#else + uint32_t col1 = SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0); + uint32_t col2 = BLACK; + if (SEGMENT.check1 && SEGMENT.palette == 0) { + col1 = SEGCOLOR(0); + col2 = SEGCOLOR(2); + } + maxLen = numberOfChars; // Ensure maxLen reflects the actual rendered text, not the original SEGMENT.name length + SEGMENT.drawText((unsigned char*)text, maxLen, int(cols) - int(SEGENV.aux0), yoffset, letterWidth, letterHeight, col1, col2, drawShadow); +#endif + + // WLEDMM add some blur + if (SEGMENT.check3 && !SEGMENT.check2) { // blur looks ugly together with "overlay" + if (SEGMENT.custom1 < 16) SEGMENT.blurRows(16); // only blur rows when no trail + SEGMENT.blurCols(20); + } return FRAMETIME; } -static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,,Gradient,Overlay;!,!,Gradient;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0"; +static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,,Gradient,Overlay,Soft;!,!,Gradient;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0"; //////////////////////////// @@ -6867,7 +6944,7 @@ static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Off //////////////////////////// //// Drift Rose by stepko (c)2021 [https://editor.soulmatelights.com/gallery/1369-drift-rose-pattern], adapted by Blaz Kristan (AKA blazoncek) uint16_t mode_2Ddriftrose(void) { - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -6969,7 +7046,7 @@ uint16_t mode_ripplepeak(void) { // * Ripple peak. By Andrew Tuli uint16_t maxRipples = 16; uint16_t dataSize = sizeof(Ripple) * maxRipples; - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize)) return mode_oops(); //allocation failed Ripple* ripples = reinterpret_cast(SEGENV.data); um_data_t *um_data = getAudioData(); @@ -7048,7 +7125,7 @@ static const char _data_FX_MODE_RIPPLEPEAK[] PROGMEM = "Ripple Peak@Fade rate,Ma ///////////////////////// // By: Mark Kriegsman https://gist.github.com/kriegsman/5adca44e14ad025e6d3b , modified by Andrew Tuline uint16_t mode_2DSwirl(void) { - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -7091,7 +7168,7 @@ static const char _data_FX_MODE_2DSWIRL[] PROGMEM = "Swirl@!,Sensitivity,Blur;,B ///////////////////////// // By: Stepko, https://editor.soulmatelights.com/gallery/652-wave , modified by Andrew Tuline uint16_t mode_2DWaverly(void) { - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -7148,7 +7225,7 @@ typedef struct Gravity { uint16_t mode_gravcenter(void) { // Gravcenter. By Andrew Tuline. constexpr uint16_t dataSize = sizeof(gravity); - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize)) return mode_oops(); //allocation failed Gravity* gravcen = reinterpret_cast(SEGENV.data); if (SEGENV.call == 0) { SEGMENT.setUpLeds(); @@ -7196,7 +7273,7 @@ static const char _data_FX_MODE_GRAVCENTER[] PROGMEM = "Gravcenter@Rate of fall, uint16_t mode_gravcentric(void) { // Gravcentric. By Andrew Tuline. uint16_t dataSize = sizeof(gravity); - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize)) return mode_oops(); //allocation failed Gravity* gravcen = reinterpret_cast(SEGENV.data); if (SEGENV.call == 0) { SEGMENT.setUpLeds(); @@ -7247,7 +7324,7 @@ static const char _data_FX_MODE_GRAVCENTRIC[] PROGMEM = "Gravcentric@Rate of fal uint16_t mode_gravimeter(void) { // Gravmeter. By Andrew Tuline. uint16_t dataSize = sizeof(gravity); - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize)) return mode_oops(); //allocation failed Gravity* gravcen = reinterpret_cast(SEGENV.data); um_data_t *um_data = getAudioData(); @@ -7530,7 +7607,7 @@ typedef struct Plasphase { uint16_t mode_plasmoid(void) { // Plasmoid. By Andrew Tuline. // even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment - if (!SEGENV.allocateData(sizeof(plasphase))) return mode_static(); //allocation failed + if (!SEGENV.allocateData(sizeof(plasphase))) return mode_oops(); //allocation failed Plasphase* plasmoip = reinterpret_cast(SEGENV.data); um_data_t *um_data = getAudioData(); @@ -7642,7 +7719,7 @@ static const char _data_FX_MODE_PUDDLES[] PROGMEM = "Puddles@Fade rate,Puddle si ////////////////////// uint16_t mode_pixels(void) { // Pixels. By Andrew Tuline. - if (!SEGENV.allocateData(32*sizeof(uint8_t))) return mode_static(); //allocation failed + if (!SEGENV.allocateData(32*sizeof(uint8_t))) return mode_oops(); //allocation failed uint8_t *myVals = reinterpret_cast(SEGENV.data); // Used to store a pile of samples because WLED frame rate and WLED sample rate are not synchronized. Frame rate is too low. um_data_t *um_data = getAudioData(); @@ -8046,7 +8123,7 @@ static const char _data_FX_MODE_FREQWAVE[] PROGMEM = "Freqwave@Speed,Sound effec uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline. uint16_t dataSize = sizeof(gravity); - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize)) return mode_oops(); //allocation failed Gravity* gravcen = reinterpret_cast(SEGENV.data); um_data_t *um_data = getAudioData(); @@ -8240,7 +8317,7 @@ static void setFlatPixelXY(bool flatMode, int x, int y, uint32_t color, unsigned } uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma. Flat Mode added by softhack007 - //if (!strip.isMatrix) return mode_static(); // not a 2D set-up, not a problem + //if (!strip.isMatrix) return mode_oops(); // not a 2D set-up, not a problem bool flatMode = !SEGMENT.is2D() || (SEGMENT.width() < 3) || (SEGMENT.height() < 3); // also use flat mode when less than 3 colums or rows const int NUM_BANDS = map2(SEGMENT.custom1, 0, 255, 1, 16); @@ -8249,9 +8326,9 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma. Fla const uint16_t rows = flatMode ? vLength / cols : SEGMENT.virtualHeight(); const unsigned offset = flatMode ? max(0, (vLength - rows*cols +1) / 2) : 0; // flatmode: always center effect - if ((cols <=1) || (rows <=1)) return mode_static(); // too small + if ((cols <=1) || (rows <=1)) return mode_oops(); // too small - if (!SEGENV.allocateData(cols*sizeof(uint16_t))) return mode_static(); //allocation failed + if (!SEGENV.allocateData(cols*sizeof(uint16_t))) return mode_oops(); //allocation failed uint16_t *previousBarHeight = reinterpret_cast(SEGENV.data); //array of previous bar heights per frequency band um_data_t *um_data = getAudioData(); @@ -8349,7 +8426,7 @@ static const char _data_FX_MODE_2DGEQ[] PROGMEM = "GEQ ☾@Fade speed,Ripple dec // ** 2D Funky plank // ///////////////////////// uint16_t mode_2DFunkyPlank(void) { // Written by ??? Adapted by Will Tatam. - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -8439,8 +8516,10 @@ static uint8_t akemi[] PROGMEM = { }; uint16_t mode_2DAkemi(void) { - if (!strip.isMatrix) return mode_static(); // not a 2D set-up - + return mode_2DAkemi_core(true); +} +static uint16_t mode_2DAkemi_core(bool withSound) { + if ((withSound == true) && (!strip.isMatrix)) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -8452,7 +8531,7 @@ uint16_t mode_2DAkemi(void) { const float lightFactor = 0.15f; const float normalFactor = 0.4f; - um_data_t *um_data = getAudioData(); + um_data_t *um_data = withSound ? getAudioData() : simulateSound(SEGMENT.soundSim); uint8_t fftResult[NUM_GEQ_CHANNELS] = {0}; if (um_data->u_data != nullptr) memcpy(fftResult, um_data->u_data[2], sizeof(fftResult)); // WLEDMM buffer curent values float base = fftResult[0]/255.0f; @@ -8509,7 +8588,7 @@ static const char _data_FX_MODE_2DAKEMI[] PROGMEM = "Akemi@Color speed,Dance;Hea // https://editor.soulmatelights.com/gallery/1089-distorsion-waves // adapted for WLED by @blazoncek uint16_t mode_2Ddistortionwaves() { - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -8564,13 +8643,13 @@ static const char _data_FX_MODE_2DDISTORTIONWAVES[] PROGMEM = "Distortion Waves@ //Idea from https://www.youtube.com/watch?v=DiHBgITrZck&ab_channel=StefanPetrick // adapted for WLED by @blazoncek uint16_t mode_2Dsoap() { - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); const size_t dataSize = SEGMENT.width() * SEGMENT.height() * sizeof(uint8_t); // prevent reallocation if mirrored or grouped - if (!SEGENV.allocateData(dataSize + sizeof(uint32_t)*3)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize + sizeof(uint32_t)*3)) return mode_oops(); //allocation failed uint8_t *noise3d = reinterpret_cast(SEGENV.data); uint32_t *noise32_x = reinterpret_cast(SEGENV.data + dataSize); @@ -8685,7 +8764,7 @@ static const char _data_FX_MODE_2DSOAP[] PROGMEM = "Soap@!,Smoothness;;!;2"; // adapted for WLED by @blazoncek // RadialWave mode added by @softhack007, based on https://editor.soulmatelights.com/gallery/1090-radialwave uint16_t mode_2Doctopus() { - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -8697,7 +8776,7 @@ uint16_t mode_2Doctopus() { } map_t; const size_t dataSize = SEGMENT.width() * SEGMENT.height() * sizeof(map_t); // prevent reallocation if mirrored or grouped - if (!SEGENV.allocateData(dataSize + 2)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize + 2)) return mode_oops(); //allocation failed map_t *rMap = reinterpret_cast(SEGENV.data); uint8_t *offsX = reinterpret_cast(SEGENV.data + dataSize); @@ -8770,7 +8849,7 @@ static const char _data_FX_MODE_2DOCTOPUS[] PROGMEM = "Octopus@!,,Offset X,Offse //@Stepko (https://editor.soulmatelights.com/gallery/1704-wavingcells) // adapted for WLED by @blazoncek uint16_t mode_2Dwavingcell() { - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -8789,8 +8868,8 @@ static const char _data_FX_MODE_2DWAVINGCELL[] PROGMEM = "Waving Cell@!,,Amplitu /* @title MoonModules WLED - GEQ 3D Effect @file included in FX.cpp - @repo https://github.com/MoonModules/WLED, submit changes to this file as PRs to MoonModules/WLED - @Authors https://github.com/MoonModules/WLED/commits/mdev/ + @repo https://github.com/MoonModules/WLED-MM, submit changes to this file as PRs to MoonModules/WLED-MM + @Authors https://github.com/MoonModules/WLED-MM/commits/mdev/ @Copyright © 2024 Github MoonModules Commit Authors (contact moonmodules@icloud.com for details) @license Licensed under the EUPL-1.2 or later */ @@ -8803,10 +8882,10 @@ uint16_t mode_GEQLASER(void) { // Author: @TroyHacks // @license GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const int cols = SEGMENT.virtualWidth(); const int rows = SEGMENT.virtualHeight(); - if ((cols < 3) || (rows < 3)) return mode_static(); // too small + if ((cols < 3) || (rows < 3)) return mode_oops(); // too small int16_t *projector = reinterpret_cast(&(SEGENV.aux0)); // *projector is an alias for aux0 (uint16_t) int16_t *projector_dir = reinterpret_cast(&(SEGENV.aux1)); // *projector_dir is an alias for aux1 (uint16_t) @@ -8936,8 +9015,8 @@ static const char _data_FX_MODE_GEQLASER[] PROGMEM = "GEQ 3D ☾@Speed,Front Fil /* @title MoonModules WLED - Painbrush Effect @file included in FX.cpp - @repo https://github.com/MoonModules/WLED, submit changes to this file as PRs to MoonModules/WLED - @Authors https://github.com/MoonModules/WLED/commits/mdev/ + @repo https://github.com/MoonModules/WLED-MM, submit changes to this file as PRs to MoonModules/WLED-MM + @Authors https://github.com/MoonModules/WLED-MM/commits/mdev/ @Copyright © 2024 Github MoonModules Commit Authors (contact moonmodules@icloud.com for details) @license GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 @@ -8959,12 +9038,12 @@ uint16_t mode_2DPaintbrush() { // Author: @TroyHacks // @license GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix) return mode_oops(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - if (!SEGENV.allocateData(4)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(4)) return mode_oops(); //allocation failed if (SEGENV.call == 0) { SEGMENT.setUpLeds(); @@ -9022,13 +9101,13 @@ static const char _data_FX_MODE_2DPAINTBRUSH[] PROGMEM = "Paintbrush ☾@Oscilla #define NUMBEROFSOURCES 8 uint16_t mode_particlevortex(void) { if (SEGLEN == 1) - return mode_static(); + return mode_oops(); ParticleSystem2D *PartSys = nullptr; uint32_t i, j; if (SEGMENT.call == 0) { // initialization if (!initParticleSystem2D(PartSys, NUMBEROFSOURCES)) - return mode_static(); // allocation failed + return mode_oops(); // allocation failed #ifdef ESP8266 PartSys->setMotionBlur(180); #else @@ -9046,7 +9125,7 @@ uint16_t mode_particlevortex(void) { PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! PartSys->updateSystem(); // update system properties (dimensions and data pointers) uint32_t spraycount = min(PartSys->numSources, (uint32_t)(1 + (SEGMENT.custom1 >> 5))); // number of sprays to display, 1-8 @@ -9141,7 +9220,7 @@ uint16_t mode_particlefireworks(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem2D(PartSys, NUMBEROFSOURCES)) - return mode_static(); // allocation failed + return mode_oops(); // allocation failed PartSys->setKillOutOfBounds(true); // out of bounds particles dont return (except on top, taken care of by gravity setting) PartSys->setWallHardness(120); // ground bounce is fixed @@ -9155,7 +9234,7 @@ uint16_t mode_particlefireworks(void) { PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! PartSys->updateSystem(); // update system properties (dimensions and data pointers) numRockets = map(SEGMENT.speed, 0 , 255, 4, min(PartSys->numSources, (uint32_t)NUMBEROFSOURCES)); @@ -9270,7 +9349,7 @@ uint16_t mode_particlefireworks(void) { return FRAMETIME; } #undef NUMBEROFSOURCES -static const char _data_FX_MODE_PARTICLEFIREWORKS[] PROGMEM = "PS Fireworks@Launches,Explosion Size,Fuse,Blur,Gravity,Cylinder,Ground,Fast;;!;2;pal=11,ix=50,c1=40,c2=0,c3=12"; +static const char _data_FX_MODE_PARTICLEFIREWORKS[] PROGMEM = "PS Fireworks@Launches,Explosion Size,Fuse,Blur,Gravity,Cylinder,Ground,Fast;;!;2;pal=11,ix=200,sx=180,c1=196,c2=0,c3=10,o3=1"; /* Particle Volcano @@ -9288,7 +9367,7 @@ uint16_t mode_particlevolcano(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem2D(PartSys, NUMBEROFSOURCES)) // init, no additional data needed - return mode_static(); // allocation failed or not 2D + return mode_oops(); // allocation failed or not 2D PartSys->setBounceY(true); PartSys->setGravity(); // enable with default gforce @@ -9309,7 +9388,7 @@ uint16_t mode_particlevolcano(void) { PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! numSprays = min(PartSys->numSources, (uint32_t)NUMBEROFSOURCES); // number of volcanoes @@ -9344,7 +9423,7 @@ uint16_t mode_particlevolcano(void) { return FRAMETIME; } #undef NUMBEROFSOURCES -static const char _data_FX_MODE_PARTICLEVOLCANO[] PROGMEM = "PS Volcano@Speed,Intensity,Move,Bounce,Spread,AgeColor,Walls,Collide;;!;2;pal=35,sx=100,ix=190,c1=0,c2=160,c3=6,o1=1"; +static const char _data_FX_MODE_PARTICLEVOLCANO[] PROGMEM = "PS Volcano@Speed,Intensity,Move,Bounce,Spread,AgeColor,Walls,Collide;;!;2;pal=35,sx=208,ix=190,c1=0,c2=190,c3=16,o1=1"; /* Particle Fire @@ -9358,14 +9437,14 @@ uint16_t mode_particlefire(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem2D(PartSys, SEGMENT.virtualWidth(), 4)) //maximum number of source (PS may limit based on segment size); need 4 additional bytes for time keeping (uint32_t lastcall) - return mode_static(); // allocation failed or not 2D + return mode_oops(); // allocation failed or not 2D SEGENV.aux0 = hw_random16(); // aux0 is wind position (index) in the perlin noise } else PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! PartSys->updateSystem(); // update system properties (dimensions and data pointers) PartSys->setWrapX(SEGMENT.check2); @@ -9398,7 +9477,7 @@ uint16_t mode_particlefire(void) { PartSys->sources[i].source.ttl = 20 + hw_random16((SEGMENT.custom1 * SEGMENT.custom1) >> 8) / (1 + (firespeed >> 5)); //'hotness' of fire, faster flames reduce the effect or flame height will scale too much with speed PartSys->sources[i].maxLife = hw_random16(SEGMENT.virtualHeight() >> 1) + 16; // defines flame height together with the vy speed, vy speed*maxlife/PS_P_RADIUS is the average flame height PartSys->sources[i].minLife = PartSys->sources[i].maxLife >> 1; - PartSys->sources[i].vx = hw_random16(4) - 2; // emitting speed (sideways) + PartSys->sources[i].vx = hw_random16(5) - 2; // emitting speed (sideways) PartSys->sources[i].vy = (SEGMENT.virtualHeight() >> 1) + (firespeed >> 4) + (SEGMENT.custom1 >> 4); // emitting speed (upwards) PartSys->sources[i].var = 2 + hw_random16(2 + (firespeed >> 4)); // speed variation around vx,vy (+/- var) } @@ -9449,7 +9528,7 @@ uint16_t mode_particlepit(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem2D(PartSys, 0, 0, true, false)) // init - return mode_static(); // allocation failed or not 2D + return mode_oops(); // allocation failed or not 2D PartSys->setKillOutOfBounds(true); PartSys->setGravity(); // enable with default gravity PartSys->setUsedParticles(170); // use 75% of available particles @@ -9457,7 +9536,7 @@ uint16_t mode_particlepit(void) { else PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! PartSys->updateSystem(); // update system properties (dimensions and data pointers) @@ -9521,7 +9600,7 @@ uint16_t mode_particlewaterfall(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem2D(PartSys, 12)) // init, request 12 sources, no additional data needed - return mode_static(); // allocation failed or not 2D + return mode_oops(); // allocation failed or not 2D PartSys->setGravity(); // enable with default gforce PartSys->setKillOutOfBounds(true); // out of bounds particles dont return (except on top, taken care of by gravity setting) @@ -9542,7 +9621,7 @@ uint16_t mode_particlewaterfall(void) { else PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! // Particle System settings PartSys->updateSystem(); // update system properties (dimensions and data pointers) @@ -9592,7 +9671,7 @@ uint16_t mode_particlebox(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem2D(PartSys, 1)) // init - return mode_static(); // allocation failed or not 2D + return mode_oops(); // allocation failed or not 2D PartSys->setBounceX(true); PartSys->setBounceY(true); SEGENV.aux0 = hw_random16(); // position in perlin noise @@ -9601,7 +9680,7 @@ uint16_t mode_particlebox(void) { PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! PartSys->updateSystem(); // update system properties (dimensions and data pointers) PartSys->setParticleSize(SEGMENT.custom3<<3); @@ -9673,7 +9752,7 @@ uint16_t mode_particleperlin(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem2D(PartSys, 1, 0, true)) // init with 1 source and advanced properties - return mode_static(); // allocation failed or not 2D + return mode_oops(); // allocation failed or not 2D PartSys->setKillOutOfBounds(true); // should never happen, but lets make sure there are no stray particles PartSys->setMotionBlur(230); // anable motion blur @@ -9684,7 +9763,7 @@ uint16_t mode_particleperlin(void) { PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! PartSys->updateSystem(); // update system properties (dimensions and data pointers) PartSys->setWrapX(SEGMENT.check1); @@ -9738,13 +9817,13 @@ uint16_t mode_particleimpact(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem2D(PartSys, NUMBEROFSOURCES)) // init, no additional data needed - return mode_static(); // allocation failed or not 2D + return mode_oops(); // allocation failed or not 2D PartSys->setKillOutOfBounds(true); PartSys->setGravity(); // enable default gravity PartSys->setBounceY(true); // always use ground bounce PartSys->setWallRoughness(220); // high roughness numMeteors = min(PartSys->numSources, (uint32_t)NUMBEROFSOURCES); - for (i = 0; i < numMeteors; i++) { + for (uint32_t i = 0; i < numMeteors; i++) { PartSys->sources[i].source.ttl = hw_random16(10 * i); // set initial delay for meteors PartSys->sources[i].source.vy = 10; // at positive speeds, no particles are emitted and if particle dies, it will be relaunched } @@ -9753,7 +9832,7 @@ uint16_t mode_particleimpact(void) { PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! // Particle System settings PartSys->updateSystem(); // update system properties (dimensions and data pointers) @@ -9848,7 +9927,7 @@ uint16_t mode_particleattractor(void) { PSparticle *attractor; // particle pointer to the attractor if (SEGMENT.call == 0) { // initialization if (!initParticleSystem2D(PartSys, 1, sizeof(PSparticle), true)) // init using 1 source and advanced particle settings - return mode_static(); // allocation failed or not 2D + return mode_oops(); // allocation failed or not 2D PartSys->sources[0].source.hue = hw_random16(); PartSys->sources[0].source.vx = -7; // will collied with wall and get random bounce direction PartSys->sources[0].sourceFlags.collide = true; // seeded particles will collide @@ -9869,7 +9948,7 @@ uint16_t mode_particleattractor(void) { } if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! // Particle System settings PartSys->updateSystem(); // update system properties (dimensions and data pointers) @@ -9941,7 +10020,7 @@ uint16_t mode_particlespray(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem2D(PartSys, 1)) // init, no additional data needed - return mode_static(); // allocation failed or not 2D + return mode_oops(); // allocation failed or not 2D PartSys->setKillOutOfBounds(true); // out of bounds particles dont return (except on top, taken care of by gravity setting) PartSys->setBounceY(true); PartSys->setMotionBlur(200); // anable motion blur @@ -9954,7 +10033,7 @@ uint16_t mode_particlespray(void) { PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! // Particle System settings PartSys->updateSystem(); // update system properties (dimensions and data pointers) @@ -10026,14 +10105,14 @@ uint16_t mode_particleGEQ(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem2D(PartSys, 1)) - return mode_static(); // allocation failed or not 2D + return mode_oops(); // allocation failed or not 2D PartSys->setKillOutOfBounds(true); PartSys->setUsedParticles(170); // use 2/3 of available particles } else PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! uint32_t i; // set particle system properties @@ -10087,7 +10166,7 @@ uint16_t mode_particleGEQ(void) { return FRAMETIME; } -static const char _data_FX_MODE_PARTICLEGEQ[] PROGMEM = "PS GEQ 2D@Speed,Intensity,Diverge,Bounce,Gravity,Cylinder,Walls,Floor;;!;2f;pal=0,sx=155,ix=200,c1=0"; +static const char _data_FX_MODE_PARTICLEGEQ[] PROGMEM = "PS GEQ 2D@Speed,Intensity,Diverge,Bounce,Gravity,Cylinder,Walls,Floor;;!;2f;pal=0,sx=232,ix=200,c1=0,c3=9"; /* Particle rotating GEQ @@ -10103,7 +10182,7 @@ uint16_t mode_particlecenterGEQ(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem2D(PartSys, NUMBEROFSOURCES)) // init, request 16 sources - return mode_static(); // allocation failed or not 2D + return mode_oops(); // allocation failed or not 2D numSprays = min(PartSys->numSources, (uint32_t)NUMBEROFSOURCES); for (i = 0; i < numSprays; i++) { @@ -10119,7 +10198,7 @@ uint16_t mode_particlecenterGEQ(void) { PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! PartSys->updateSystem(); // update system properties (dimensions and data pointers) numSprays = min(PartSys->numSources, (uint32_t)NUMBEROFSOURCES); @@ -10172,7 +10251,7 @@ uint16_t mode_particleghostrider(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem2D(PartSys, 1)) // init, no additional data needed - return mode_static(); // allocation failed or not 2D + return mode_oops(); // allocation failed or not 2D PartSys->setKillOutOfBounds(true); // out of bounds particles dont return (except on top, taken care of by gravity setting) PartSys->sources[0].maxLife = 260; // lifetime in frames PartSys->sources[0].minLife = 250; @@ -10185,7 +10264,7 @@ uint16_t mode_particleghostrider(void) { } if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! if (SEGMENT.intensity > 0) { // spiraling if (SEGENV.aux1) { @@ -10251,7 +10330,7 @@ uint16_t mode_particleblobs(void) { if (SEGMENT.call == 0) { if (!initParticleSystem2D(PartSys, 0, 0, true, true)) //init, no additional bytes, advanced size & size control - return mode_static(); // allocation failed or not 2D + return mode_oops(); // allocation failed or not 2D PartSys->setBounceX(true); PartSys->setBounceY(true); PartSys->setWallHardness(255); @@ -10262,7 +10341,7 @@ uint16_t mode_particleblobs(void) { PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! PartSys->updateSystem(); // update system properties (dimensions and data pointers) PartSys->setUsedParticles(map(SEGMENT.intensity, 0, 255, 25, 128)); // minimum 10%, maximum 50% of available particles (note: PS ensures at least 1) @@ -10334,7 +10413,7 @@ uint16_t mode_particleDrip(void) { //uint8_t numSprays; if (SEGMENT.call == 0) { // initialization if (!initParticleSystem1D(PartSys, 4)) // init - return mode_static(); // allocation failed or single pixel + return mode_oops(); // allocation failed or single pixel PartSys->setKillOutOfBounds(true); // out of bounds particles dont return (except on top, taken care of by gravity setting) PartSys->sources[0].source.hue = hw_random16(); SEGENV.aux1 = 0xFFFF; // invalidate @@ -10343,7 +10422,7 @@ uint16_t mode_particleDrip(void) { PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! // Particle System settings PartSys->updateSystem(); // update system properties (dimensions and data pointers) @@ -10439,7 +10518,7 @@ uint16_t mode_particlePinball(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem1D(PartSys, 1, 128, 0, true)) // init - return mode_static(); // allocation failed or is single pixel + return mode_oops(); // allocation failed or is single pixel PartSys->sources[0].sourceFlags.collide = true; // seeded particles will collide (if enabled) PartSys->sources[0].source.x = PS_P_RADIUS_1D; //emit at bottom PartSys->setKillOutOfBounds(true); // out of bounds particles dont return @@ -10450,7 +10529,7 @@ uint16_t mode_particlePinball(void) { PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! // Particle System settings //uint32_t hardness = 240 + (SEGMENT.custom1>>4); @@ -10549,7 +10628,7 @@ uint16_t mode_particleDancingShadows(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem1D(PartSys, 1)) // init, one source - return mode_static(); // allocation failed or is single pixel + return mode_oops(); // allocation failed or is single pixel PartSys->sources[0].maxLife = 1000; //set long life (kill out of bounds is done in custom way) PartSys->sources[0].minLife = PartSys->sources[0].maxLife; } @@ -10558,7 +10637,7 @@ uint16_t mode_particleDancingShadows(void) { } if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! // Particle System settings PartSys->updateSystem(); // update system properties (dimensions and data pointers) @@ -10667,14 +10746,14 @@ uint16_t mode_particleFireworks1D(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem1D(PartSys, 4, 150, 4, true)) // init advanced particle system if (!initParticleSystem1D(PartSys, 4, 150, 4, true)) // init advanced particle system - return mode_static(); // allocation failed or is single pixel + return mode_oops(); // allocation failed or is single pixel PartSys->setKillOutOfBounds(true); PartSys->sources[0].sourceFlags.custom1 = 1; // set rocket state to standby } else PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! // Particle System settings PartSys->updateSystem(); // update system properties (dimensions and data pointers) @@ -10787,11 +10866,11 @@ uint16_t mode_particleSparkler(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem1D(PartSys, 16, 128 ,0, true)) // init, no additional data needed - return mode_static(); // allocation failed or is single pixel + return mode_oops(); // allocation failed or is single pixel } else PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! // Particle System settings PartSys->updateSystem(); // update system properties (dimensions and data pointers) @@ -10858,14 +10937,14 @@ uint16_t mode_particleHourglass(void) { uint32_t* settingTracker; if (SEGMENT.call == 0) { // initialization if (!initParticleSystem1D(PartSys, 0, 255, 8, false)) // init - return mode_static(); // allocation failed or is single pixel + return mode_oops(); // allocation failed or is single pixel PartSys->setBounce(true); PartSys->setWallHardness(100); } else PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! // Particle System settings PartSys->updateSystem(); // update system properties (dimensions and data pointers) @@ -10983,7 +11062,7 @@ uint16_t mode_particle1Dspray(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem1D(PartSys, 1)) - return mode_static(); // allocation failed or is single pixel + return mode_oops(); // allocation failed or is single pixel PartSys->setKillOutOfBounds(true); PartSys->setWallHardness(150); PartSys->setParticleSize(1); @@ -10991,7 +11070,7 @@ uint16_t mode_particle1Dspray(void) { else PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! // Particle System settings PartSys->updateSystem(); // update system properties (dimensions and data pointers) @@ -11036,13 +11115,13 @@ uint16_t mode_particleBalance(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem1D(PartSys, 1, 128)) // init, no additional data needed, use half of max particles - return mode_static(); // allocation failed or is single pixel + return mode_oops(); // allocation failed or is single pixel PartSys->setParticleSize(1); } else PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! // Particle System settings PartSys->updateSystem(); // update system properties (dimensions and data pointers) @@ -11115,7 +11194,7 @@ uint16_t mode_particleChase(void) { ParticleSystem1D *PartSys = nullptr; if (SEGMENT.call == 0) { // initialization if (!initParticleSystem1D(PartSys, 1, 255, 2, true)) // init - return mode_static(); // allocation failed or is single pixel + return mode_oops(); // allocation failed or is single pixel SEGENV.aux0 = 0xFFFF; // invalidate *PartSys->PSdataEnd = 1; // huedir *(PartSys->PSdataEnd + 1) = 1; // sizedir @@ -11123,7 +11202,7 @@ uint16_t mode_particleChase(void) { else PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! // Particle System settings PartSys->updateSystem(); // update system properties (dimensions and data pointers) PartSys->setColorByPosition(SEGMENT.check3); @@ -11211,7 +11290,7 @@ uint16_t mode_particleStarburst(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem1D(PartSys, 1, 200, 0, true)) // init - return mode_static(); // allocation failed or is single pixel + return mode_oops(); // allocation failed or is single pixel PartSys->setKillOutOfBounds(true); PartSys->enableParticleCollisions(true, 200); PartSys->sources[0].source.ttl = 1; // set initial stanby time @@ -11220,7 +11299,7 @@ uint16_t mode_particleStarburst(void) { else PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! // Particle System settings PartSys->updateSystem(); // update system properties (dimensions and data pointers) @@ -11273,12 +11352,12 @@ uint16_t mode_particle1DGEQ(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem1D(PartSys, 16, 255, 0, true)) // init, no additional data needed - return mode_static(); // allocation failed or is single pixel + return mode_oops(); // allocation failed or is single pixel } else PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! // Particle System settings PartSys->updateSystem(); // update system properties (dimensions and data pointers) @@ -11345,14 +11424,14 @@ uint16_t mode_particleFire1D(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem1D(PartSys, 5)) // init - return mode_static(); // allocation failed or is single pixel + return mode_oops(); // allocation failed or is single pixel PartSys->setKillOutOfBounds(true); PartSys->setParticleSize(1); } else PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! // Particle System settings PartSys->updateSystem(); // update system properties (dimensions and data pointers) @@ -11410,7 +11489,7 @@ uint16_t mode_particle1DsonicStream(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem1D(PartSys, 1, 255, 0, true)) // init, no additional data needed - return mode_static(); // allocation failed or is single pixel + return mode_oops(); // allocation failed or is single pixel PartSys->setKillOutOfBounds(true); PartSys->sources[0].source.x = 0; // at start //PartSys->sources[1].source.x = PartSys->maxX; // at end @@ -11419,7 +11498,7 @@ uint16_t mode_particle1DsonicStream(void) { else PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! // Particle System settings PartSys->updateSystem(); // update system properties (dimensions and data pointers) @@ -11513,13 +11592,13 @@ uint16_t mode_particle1DsonicBoom(void) { ParticleSystem1D *PartSys = nullptr; if (SEGMENT.call == 0) { // initialization if (!initParticleSystem1D(PartSys, 1, 255, 0, true)) // init, no additional data needed - return mode_static(); // allocation failed or is single pixel + return mode_oops(); // allocation failed or is single pixel PartSys->setKillOutOfBounds(true); } else PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! // Particle System settings PartSys->updateSystem(); // update system properties (dimensions and data pointers) @@ -11603,13 +11682,13 @@ uint16_t mode_particleSpringy(void) { ParticleSystem1D *PartSys = nullptr; if (SEGMENT.call == 0) { // initialization if (!initParticleSystem1D(PartSys, 1, 128, 0, true)) // init - return mode_static(); // allocation failed or is single pixel + return mode_oops(); // allocation failed or is single pixel SEGENV.aux0 = SEGENV.aux1 = 0xFFFF; // invalidate settings } else PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! + return mode_oops(); // something went wrong, no data! // Particle System settings PartSys->updateSystem(); // update system properties (dimensions and data pointers) PartSys->setMotionBlur(220 * SEGMENT.check1); // anable motion blur @@ -11773,18 +11852,35 @@ static const char _data_RESERVED[] PROGMEM = "RSVD"; // use id==255 to find unallocated gaps (with "Reserved" data string) // if vector size() is smaller than id (single) data is appended at the end (regardless of id) void WS2812FX::addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name) { + if ((id < _mode.size()) && (_modeData[id] != _data_RESERVED)) { + DEBUG_PRINTF("addEffect(%d) -> ", id); + DEBUG_PRINTF(" already in use, finding a new slot for -> %s\n", mode_name); + id = 255; + } + if ((id >= _mode.size()) && (id != 255)) { + DEBUG_PRINTF("!addEffect(%d) -> slot not existing, finding new slot\n", id); + } if (id == 255) { // find empty slot for (size_t i=1; i<_mode.size(); i++) if (_modeData[i] == _data_RESERVED) { id = i; break; } } + if (id < _mode.size()) { - if (_modeData[id] != _data_RESERVED) return; // do not overwrite alerady added effect + if (_modeData[id] != _data_RESERVED) { + USER_PRINTF("!addEffect(%d) failed - existing effect cannot be replaced. <=> %s\n", id, mode_name); + return; // do not overwrite alerady added effect + } _mode[id] = mode_fn; _modeData[id] = mode_name; } else { + if (_modeCount > 253) { + USER_PRINTF("!addEffect(%d) failed - mode list is full! %s\n", id, mode_name); + return; // mode list is full - cannot add + } _mode.push_back(mode_fn); _modeData.push_back(mode_name); if (_modeCount < _mode.size()) _modeCount++; } + DEBUG_PRINTF("addEffect(%d) => %s\n", id, mode_name); } void WS2812FX::setupEffectData() { diff --git a/wled00/FX.h b/wled00/FX.h index 7559b09e..ee39887f 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -16,11 +16,20 @@ bool canUseSerial(void); // WLEDMM implemented in wled_serial.cpp void strip_wait_until_idle(String whoCalledMe); // WLEDMM implemented in FX_fcn.cpp bool strip_uses_global_leds(void) __attribute__((pure)); // WLEDMM implemented in FX_fcn.cpp +#ifdef WLED_ENABLE_GIF +struct Segment; // forward declaration +void endImagePlayback(Segment *seg); // implemented in image_loader.cpp +#endif #define FASTLED_INTERNAL //remove annoying pragma messages #define USE_GET_MILLISECOND_TIMER #include "FastLED.h" + // WLEDMM strip.sPC() needs to know "busses", so we pull in the declarition +#include "pin_manager.h" // BusManager needs to know pinManager +#include "bus_manager.h" +extern BusManager busses; // same as wled.h + #define DEFAULT_BRIGHTNESS (uint8_t)127 #define DEFAULT_MODE (uint8_t)0 #define DEFAULT_SPEED (uint8_t)128 @@ -82,6 +91,8 @@ bool strip_uses_global_leds(void) __attribute__((pure)); // WLEDMM implemented #endif #endif +#define MAX_SEGMENT_OVERDATA ((MAX_SEGMENT_DATA) + (MAX_SEGMENT_DATA)/2) // WLEDMM 50% extra overdraft budget + /* How much data bytes each segment should max allocate to leave enough space for other segments, assuming each segment uses the same amount of data. 256 for ESP8266, 640 for ESP32. */ #define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / strip.getMaxSegments()) @@ -578,6 +589,9 @@ typedef struct Segment { #ifdef ARDUINO_ARCH_ESP32 strip_wait_until_idle("~Segment()"); #endif + #ifdef WLED_ENABLE_GIF + endImagePlayback(this); + #endif if ((Segment::_globalLeds == nullptr) && !strip_uses_global_leds() && (ledsrgb != nullptr)) {free(ledsrgb); ledsrgb = nullptr;} // WLEDMM we need "!strip_uses_global_leds()" to avoid crashes (#104) if (name) { delete[] name; name = nullptr; } @@ -606,7 +620,7 @@ typedef struct Segment { inline uint8_t getLightCapabilities(void) const { return _capabilities; } static size_t getUsedSegmentData(void) { return _usedSegmentData; } // WLEDMM size_t - static void addUsedSegmentData(int len) { _usedSegmentData += len; } + static void addUsedSegmentData(int len) { _usedSegmentData = max(0, int(_usedSegmentData + len)); } // WLEDMM prevent negative alloc void allocLeds(); //WLEDMM inline static const CRGBPalette16 &getCurrentPalette(void) { return Segment::_currentPalette; } @@ -623,7 +637,7 @@ typedef struct Segment { // runtime data functions inline size_t dataSize(void) const { return _dataLen; } - bool allocateData(size_t len); + bool allocateData(size_t len, bool allowOverdraft = false); void deallocateData(void); void resetIfRequired(void); void startFrame(void); // cache a few values that don't change while an effect is drawing @@ -673,10 +687,10 @@ typedef struct Segment { #endif void setPixelColor(int n, uint32_t c); // set relative pixel within segment with color inline void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } // automatically inline - inline void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } // automatically inline + inline void setPixelColor(int n, CRGB c) { setPixelColor(n, uint32_t(c) & 0x00FFFFFF); } // automatically inline void setPixelColor(float i, uint32_t c, bool aa = true); inline void setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = true) { setPixelColor(i, RGBW32(r,g,b,w), aa); } - inline void setPixelColor(float i, CRGB c, bool aa = true) { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); } + inline void setPixelColor(float i, CRGB c, bool aa = true) { setPixelColor(i, uint32_t(c) & 0x00FFFFFF, aa); } uint32_t __attribute__((pure)) getPixelColor(int i) const; // WLEDMM attribute added // 1D support functions (some implement 2D as well) void blur(uint8_t, bool smear = false); @@ -684,10 +698,10 @@ typedef struct Segment { void fade_out(uint8_t r); void fadeToBlackBy(uint8_t fadeBy); void blendPixelColor(int n, uint32_t color, uint8_t blend); - inline void blendPixelColor(int n, CRGB c, uint8_t blend) { blendPixelColor(n, RGBW32(c.r,c.g,c.b,0), blend); } + inline void blendPixelColor(int n, CRGB c, uint8_t blend) { blendPixelColor(n, uint32_t(c) & 0x00FFFFFF, blend); } void addPixelColor(int n, uint32_t color, bool fast = false); inline void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(n, RGBW32(r,g,b,w), fast); } // automatically inline - inline void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); } // automatically inline + inline void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, uint32_t(c) & 0x00FFFFFF, fast); } // automatically inline void fadePixelColor(uint16_t n, uint8_t fade); uint8_t get_random_wheel_index(uint8_t pos) const; uint32_t __attribute__((pure)) color_from_palette(uint_fast16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255); @@ -779,21 +793,21 @@ typedef struct Segment { #endif inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); } inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } - inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } - inline void setPixelColorXY(unsigned x, unsigned y, CRGB c) { setPixelColorXY(int(x), int(y), RGBW32(c.r,c.g,c.b,0)); } + inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, uint32_t(c) & 0x00FFFFFF); } + inline void setPixelColorXY(unsigned x, unsigned y, CRGB c) { setPixelColorXY(int(x), int(y), uint32_t(c) & 0x00FFFFFF); } //#ifdef WLED_USE_AA_PIXELS void setPixelColorXY(float x, float y, uint32_t c, bool aa = true, bool fast=true); inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColorXY(x, y, RGBW32(r,g,b,w), aa); } - inline void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), aa); } + inline void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColorXY(x, y, uint32_t(c) & 0x00FFFFFF, aa); } //#endif uint32_t __attribute__((pure)) getPixelColorXY_part2(int x, int y, int cols, int rows) const; uint32_t __attribute__((pure)) getPixelColorXY_slow(int x, int y) const; // 2D support functions void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend); - inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), blend); } + inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColorXY(x, y, uint32_t(c) & 0x00FFFFFF, blend); } void addPixelColorXY(int x, int y, uint32_t color, bool fast = false); inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColorXY(x, y, RGBW32(r,g,b,w), fast); } // automatically inline - inline void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), fast); } + inline void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColorXY(x, y, uint32_t(c) & 0x00FFFFFF, fast); } void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade); void box_blur(uint16_t i, bool vertical, fract8 blur_amount); // 1D box blur (with weight) void blurRow(uint32_t row, fract8 blur_amount, bool smear = false); @@ -802,19 +816,24 @@ typedef struct Segment { void moveY(int8_t delta, bool wrap = false); void move(uint8_t dir, uint8_t delta, bool wrap = false); void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false); - inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) { drawCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); } + inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) { drawCircle(cx, cy, radius, uint32_t(c) & 0x00FFFFFF, soft); } void fillCircle(unsigned cx, unsigned cy, int radius, uint32_t col, bool soft); - inline void fillCircle(unsigned cx, unsigned cy, int radius, CRGB c, bool soft = false) { fillCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); } + inline void fillCircle(unsigned cx, unsigned cy, int radius, CRGB c, bool soft = false) { fillCircle(cx, cy, radius, uint32_t(c) & 0x00FFFFFF, soft); } void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft = false, uint8_t depth = UINT8_MAX); - inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, bool soft = false, uint8_t depth = UINT8_MAX) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0), soft, depth); } // automatic inline + 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, uint32_t(c) & 0x00FFFFFF, 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 + inline void drawArc(unsigned x0, unsigned y0, int radius, CRGB color, CRGB fillColor = BLACK) { drawArc(x0, y0, radius, uint32_t(color) & 0x00FFFFFF, uint32_t(fillColor) & 0x00FFFFFF); } // 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, 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 + 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, uint32_t(c) & 0x00FFFFFF, uint32_t(c2) & 0x00FFFFFF); } // automatic inline + // unicode-aware wrapper for drawCharacter(), to be called from mode_2Dscrollingtext() + void drawText(const unsigned char* text, size_t maxLen, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0, bool drawShadow = false); + // #if !WLED_ENABLE_FULL_FONTS => drawText() will fall back to just forwarding each char to drawCharacter() + void wu_pixel(uint32_t x, uint32_t y, CRGB c); //void blur1d(fract8 blur_amount); // blur all rows in 1 dimension void blur2d(fract8 blur_amount) { blur(blur_amount); } - inline void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); } + inline void fill_solid(CRGB c) { fill(uint32_t(c) & 0x00FFFFFF); } void nscale8(uint8_t scale); bool jsonToPixels(char *name, uint8_t fileNr); //WLEDMM for artifx #else @@ -822,19 +841,19 @@ typedef struct Segment { inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(x, c); } inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColor(int(x), c); } inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColor(x, RGBW32(r,g,b,w)); } - inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0)); } - inline void setPixelColorXY(unsigned x, unsigned y, CRGB c) { setPixelColor(int(x), RGBW32(c.r,c.g,c.b,0)); } + inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColor(x, uint32_t(c) & 0x00FFFFFF); } + inline void setPixelColorXY(unsigned x, unsigned y, CRGB c) { setPixelColor(int(x), uint32_t(c) & 0x00FFFFFF); } //#ifdef WLED_USE_AA_PIXELS inline void setPixelColorXY(float x, float y, uint32_t c, bool aa = true) { setPixelColor(x, c, aa); } inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColor(x, RGBW32(r,g,b,w), aa); } - inline void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0), aa); } + inline void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColor(x, uint32_t(c) & 0x00FFFFFF, aa); } //#endif inline uint32_t getPixelColorXY(uint16_t x, uint16_t y) { return getPixelColor(x); } inline void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t c, uint8_t blend) { blendPixelColor(x, c, blend); } - inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColor(x, RGBW32(c.r,c.g,c.b,0), blend); } + inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColor(x, uint32_t(c) & 0x00FFFFFF, blend); } inline void addPixelColorXY(int x, int y, uint32_t color, bool fast = false) { addPixelColor(x, color, fast); } inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(x, RGBW32(r,g,b,w), fast); } - inline void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColor(x, RGBW32(c.r,c.g,c.b,0), fast); } + inline void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColor(x, uint32_t(c) & 0x00FFFFFF, fast); } inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { fadePixelColor(x, fade); } inline void box_blur(uint16_t i, bool vertical, fract8 blur_amount) {} inline void blurRow(uint32_t row, fract8 blur_amount, bool smear = false) {} @@ -957,7 +976,6 @@ class WS2812FX { // 96 bytes resetSegments(bool boundsOnly = false), //WLEDMM add boundsOnly makeAutoSegments(bool forceReset = false), fixInvalidSegments(), - setPixelColor(int n, uint32_t c), show(void), setTargetFps(uint8_t fps), enumerateLedmaps(); //WLEDMM (from fcn_declare) @@ -968,8 +986,8 @@ class WS2812FX { // 96 bytes void setupEffectData(void); // add default effects to the list; defined in FX.cpp // outsmart the compiler :) by correctly overloading - inline void setPixelColor(int n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } - inline void setPixelColor(int n, CRGB c) { setPixelColor(n, c.red, c.green, c.blue); } + inline void setPixelColor(int n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) const { setPixelColor(n, RGBW32(r,g,b,w)); } + inline void setPixelColor(int n, CRGB c) const { setPixelColor(n, uint32_t(c) & 0x00FFFFFF); } inline void trigger(void) { _triggered = true; } // Forces the next frame to be computed on all active segments. inline void setShowCallback(show_callback cb) { _callback = cb; } inline void setTransition(uint16_t t) { _transitionDur = t; } @@ -1024,8 +1042,33 @@ class WS2812FX { // 96 bytes uint32_t 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 methods for 1D + // WLEDMM setPixelColor and getPixelColor moved from FX_fcn.cpp for speed (inlining) + + inline void setPixelColor(int i, uint32_t color32) const + { + if (i < customMappingSize) i = customMappingTable[i]; + if (i >= _length) return; + busses.setPixelColor(i, color32); + } + + inline uint32_t getPixelColor(uint_fast16_t i) const + { + if (i < customMappingSize) i = customMappingTable[i]; + if (i >= _length) return 0; + return busses.getPixelColor(i); + } + + // WLEDMM gets the original color from the driver (without downscaling by _bri) + inline uint32_t getPixelColorRestored(uint_fast16_t i) const + { + if (i < customMappingSize) i = customMappingTable[i]; + if (i >= _length) return 0; + return busses.getPixelColorRestored(i); + } + inline uint32_t getLastShow(void) const { return _lastShow; } inline uint32_t segColor(uint8_t i) const { return _colors_t[i]; } @@ -1093,17 +1136,63 @@ class WS2812FX { // 96 bytes std::vector panel; #endif - void - setUpMatrix(), - setPixelColorXY_fast(int x, int y, uint32_t c), - setPixelColorXY(int x, int y, uint32_t c); + void setUpMatrix(); + + // ////////////// + // inline methods for 2D + // WLEDMM setPixelColorXY and getPixelColorXY moved from FX_2Dfcn.cpp for speed (inlining) + + // absolute matrix version of setPixelColor(), without error checking // WLEDMM + inline void __attribute__((hot)) setPixelColorXY_fast(int x, int y, uint32_t color32) const //WLEDMM: IRAM_ATTR conditionally + { + uint_fast16_t index = y * Segment::maxWidth + x; + if (index < customMappingSize) index = customMappingTable[index]; + if (index >= _length) return; + busses.setPixelColor(index, color32); + } + + // absolute matrix version of setPixelColor() + inline void setPixelColorXY(int x, int y, uint32_t color32) const //WLEDMM: IRAM_ATTR conditionally + { + #ifndef WLED_DISABLE_2D + if (!isMatrix) return; // not a matrix set-up + uint_fast16_t index = y * Segment::maxWidth + x; + #else + uint16_t index = x; + #endif + if (index < customMappingSize) index = customMappingTable[index]; + if (index >= _length) return; + busses.setPixelColor(index, color32); + } + + // returns RGBW values of pixel + inline uint32_t __attribute__((hot)) getPixelColorXY(uint16_t x, uint16_t y) const { + #ifndef WLED_DISABLE_2D + uint_fast16_t index = (y * Segment::maxWidth + x); //WLEDMM: use fast types + #else + uint16_t index = x; + #endif + if (index < customMappingSize) index = customMappingTable[index]; + if (index >= _length) return 0; + return busses.getPixelColor(index); + } + + // WLEDMM gets the original color from the driver (without downscaling by _bri) + inline uint32_t __attribute__((hot)) getPixelColorXYRestored(uint16_t x, uint16_t y) const { + #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); + } + // outsmart the compiler :) by correctly overloading - inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline - inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } - - uint32_t - getPixelColorXY(uint16_t, uint16_t) const; + inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) const { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline + inline void setPixelColorXY(int x, int y, CRGB c) const { setPixelColorXY(x, y, uint32_t(c) & 0x00FFFFFF); } // end 2D support @@ -1121,8 +1210,6 @@ class WS2812FX { // 96 bytes std::vector _segments; friend struct 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 247f166e..f58ba016 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -101,7 +101,7 @@ void WS2812FX::setUpMatrix() { // content of the file is just raw JSON array in the form of [val1,val2,val3,...] // there are no other "key":"value" pairs in it // allowed values are: -1 (missing pixel/no LED attached), 0 (inactive/unused pixel), 1 (active/used pixel) - char fileName[32]; strcpy_P(fileName, PSTR("/2d-gaps.json")); // reduce flash footprint + char fileName[32]; strcpy_P(fileName, PSTR("/2d-gaps.json")); // reduce flash footprint //WLEDMM you sure? bool isFile = WLED_FS.exists(fileName); size_t gapSize = 0; int8_t *gapTable = nullptr; @@ -194,51 +194,19 @@ void WS2812FX::setUpMatrix() { #endif } + // absolute matrix version of setPixelColor(), without error checking -void IRAM_ATTR __attribute__((hot)) WS2812FX::setPixelColorXY_fast(int x, int y, uint32_t col) //WLEDMM: IRAM_ATTR conditionally -{ - uint_fast16_t index = y * Segment::maxWidth + x; - if (index < customMappingSize) index = customMappingTable[index]; - if (index >= _length) return; - busses.setPixelColor(index, col); -} +// WLEDMM: WS2812FX::setPixelColorXY_fast() moved FX.h for speed (inlining) // absolute matrix version of setPixelColor() -void IRAM_ATTR_YN WS2812FX::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM: IRAM_ATTR conditionally -{ -#ifndef WLED_DISABLE_2D - if (!isMatrix) return; // not a matrix set-up - uint_fast16_t index = y * Segment::maxWidth + x; -#else - uint16_t index = x; -#endif - if (index < customMappingSize) index = customMappingTable[index]; - if (index >= _length) return; - busses.setPixelColor(index, col); -} +// WLEDMM: WS2812FX::setPixelColorXY() moved FX.h for speed (inlining) // returns RGBW values of pixel -uint32_t __attribute__((hot)) WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) const { -#ifndef WLED_DISABLE_2D - uint_fast16_t index = (y * Segment::maxWidth + x); //WLEDMM: use fast types -#else - uint16_t index = x; -#endif - if (index < customMappingSize) index = customMappingTable[index]; - if (index >= _length) return 0; - return busses.getPixelColor(index); -} +// WLEDMM: WS2812FX::getPixelColorXY() moved FX.h for speed (inlining) + +// WLEDMM gets the original color from the driver (without downscaling by _bri) +// WLEDMM: WS2812FX::getPixelColorXYRestored() moved FX.h for speed (inlining) -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 @@ -281,7 +249,7 @@ void IRAM_ATTR __attribute__((hot)) Segment::setPixelColorXY_fast(int x, int y, } #if 0 // this is still a dangerous optimization - if ((i < UINT_MAX) && sameColor && (call > 0) && (!transitional) && (mode != FX_MODE_2DSCROLLTEXT) && (ledsrgb[i] == CRGB(scaled_col))) return; // WLEDMM looks like nothing to do + if ((i < UINT_MAX) && sameColor && (call > 0) && (!transitional) && (!freeze) && (mode != FX_MODE_2DSCROLLTEXT) && (ledsrgb[i] == CRGB(scaled_col))) return; // WLEDMM looks like nothing to do #endif // handle reverse and transpose @@ -343,7 +311,7 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM: } #if 0 // this is a dangerous optimization - if ((i < UINT_MAX) && sameColor && (call > 0) && (!transitional) && (mode != FX_MODE_2DSCROLLTEXT) && (ledsrgb[i] == CRGB(col))) return; // WLEDMM looks like nothing to do + if ((i < UINT_MAX) && sameColor && (call > 0) && (!transitional) && (!freeze) && (mode != FX_MODE_2DSCROLLTEXT) && (ledsrgb[i] == CRGB(col))) return; // WLEDMM looks like nothing to do #endif if (reverse ) x = cols - x - 1; @@ -874,11 +842,11 @@ void Segment::drawArc(unsigned x0, unsigned y0, int radius, uint32_t color, uint //WLEDMM for artifx bool Segment::jsonToPixels(char * name, uint8_t fileNr) { if (!isActive()) return true; // segment not active, nothing to do - char fileName[32] = { '\0' }; + char fileName[WLED_MAX_SEGNAME_LEN+12] = { '\0' }; // we need up to 40 bytes (seg.name is 32 bytes max) //WLEDMM: als support segment name ledmaps bool isFile = false; // strcpy_P(fileName, PSTR("/mario")); - snprintf(fileName, sizeof(fileName), "/%s%d.json", name, fileNr); //WLEDMM: trick to not include 0 in ledmap.json + snprintf(fileName, sizeof(fileName)-1, "/%s%d.json", name, fileNr); //WLEDMM: trick to not include 0 in ledmap.json // strcat(fileName, ".json"); isFile = WLED_FS.exists(fileName); @@ -909,21 +877,51 @@ bool Segment::jsonToPixels(char * name, uint8_t fileNr) { return true; } -#include "src/font/console_font_4x6.h" -#include "src/font/console_font_5x8.h" -#include "src/font/console_font_5x12.h" -#include "src/font/console_font_6x8.h" -#include "src/font/console_font_7x9.h" +#include "wled_fonts.hpp" +#if defined(WLED_ENABLE_FULL_FONTS) +#include "src/font/codepages.h" +#endif + +// unicode-aware wrapper for drawCharacter(), to be called from mode_2Dscrollingtext() +void Segment::drawText(const unsigned char* text, size_t maxLen, 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 + //size_t maxLetters = WLED_MAX_SEGNAME_LEN; + const size_t numberOfChars = strnlen((const char *) text, maxLen); // size in bytes // toDo check if this is needed - duplicate of maxLen? + +#if defined(WLED_ENABLE_FULL_FONTS) + FontInfo_t font = getFontInfo(w, h); // use central font selection legic + uint16_t decoded_text[WLED_MAX_SEGNAME_LEN+1] = { 0 }; // UTF-16 converted text. Cannot be longer than WLED_MAX_SEGNAME_LEN + size_t utf16_index = 0; + for(const unsigned char* now = text; now != nullptr && now[0] != '\0'; now = nextUnicode(now, maxLen)) { + if (utf16_index < WLED_MAX_SEGNAME_LEN) { + decoded_text[utf16_index] = unicodeToWchar16(now, maxLen); // UTF-8 decode into decoded_text + decoded_text[utf16_index] = wchar16ToCodepage437(decoded_text[utf16_index]); // decoded_text to CP437 (in-place conversion) + if ((decoded_text[utf16_index] >= font.firstChar) && ((decoded_text[utf16_index] <= font.lastChar))) // don't advance on NUL, or on codes not suppoted in DrawCharacter + utf16_index++; + } + } + decoded_text[utf16_index] = 0; // NUL terminate string + size_t textLength = min(utf16_index, numberOfChars); +#else + const unsigned char* decoded_text = text; // fallback + size_t textLength = min(strnlen((char*)text, maxLen), numberOfChars); +#endif + // pass characters to drawCharacter() + for (int i = 0; i < textLength; i++) { + SEGMENT.drawCharacter((unsigned char) decoded_text[i], x + w*i, y, w, h, color, col2, drawShadow); + } +} // 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, 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 const uint16_t cols = virtualWidth(); const uint16_t rows = virtualHeight(); - const int font = w*h; + FontInfo_t font = getFontInfo(w, h); // use central font selection logic + if (!font.isProgMem || font.width_bytes > 1) return; // do nothing for not (yet) supported font features: width_bytes > 1, !isProgMem + if (chr < font.firstChar || chr > font.lastChar) return; // do nothing when out of limits + chr = chr - font.firstChar; // adjust chr to point to the first allowed character byte CRGB col = CRGB(color); CRGBPalette16 grad = CRGBPalette16(col, (col2 != BLACK) ? CRGB(col2) : col); @@ -931,41 +929,30 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, //if (w<5 || w>6 || h!=8) return; if (drawShadow) w++; // one more column for shadow on right side - 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]); - 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; - } + + // for wide fonts, we can add a loop here :for(offset=0, offset < font.width_bytes; offset++) {} + + // get 8 pixels (byte) from raw font data + bits = pgm_read_byte_near(&font.raw[(chr * h) + i]); + if ((i>0) && drawShadow) bits_up = pgm_read_byte_near(&font.raw[(chr * h) + i -1]); + if (col2 != BLACK) col = ColorFromPalette(grad, (i+1)*255/h, 255, NOBLEND); uint32_t fgCol = uint32_t(col) & 0x00FFFFFF; // WLEDMM cache color value - for (int j = 0; j= 0) || (x0 < cols)) { + for (int j = 0; j 0 && x0 < cols" if ((bits>>(j+(8-w))) & 0x01) { // bit set & drawing on-screen setPixelColorXY(x0, y0, fgCol); } else { if (drawShadow) { - // WLEDMM + // 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 diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 4941529f..d307b5b8 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -8,7 +8,10 @@ #include "FX.h" #include "palettes.h" #ifdef ARDUINO_ARCH_ESP32 -#include // WLEDMM to get esp_timer_get_time() +#include // WLEDMM to get esp_timer_get_time() +#include "freertos/FreeRTOS.h" +#include "freertos/portmacro.h" +static portMUX_TYPE s_wled_strip_mux = portMUX_INITIALIZER_UNLOCKED; // to protect deleting Segment::_globalLeds #endif /* @@ -53,7 +56,7 @@ #error "Max segments must be at least max number of busses!" #endif -// WLEDMM experimental . this is a "C style" wrapper for strip.waitUntilIdle() +// WLEDMM experimental . this is a "C style" wrapper for strip.waitUntilIdle(); // This workaround is just needed for the segment class, that does't know about "strip" void strip_wait_until_idle(String whoCalledMe) { #if defined(ARDUINO_ARCH_ESP32) && defined(WLEDMM_PROTECT_SERVICE) // WLEDMM experimental @@ -94,7 +97,7 @@ Segment::Segment(const Segment &orig) { _t = nullptr; if (ledsrgb && !Segment::_globalLeds) {ledsrgb = nullptr; ledsrgbSize = 0;} // WLEDMM if (orig.name) { name = new(std::nothrow) char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } - if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } + if (orig.data) { if (allocateData(orig._dataLen, true)) memcpy(data, orig.data, orig._dataLen); } //if (orig._t) { _t = new(std::nothrow) Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); } //else markForReset(); // WLEDMM // if (orig.ledsrgb && !Segment::_globalLeds) { allocLeds(); if (ledsrgb) memcpy(ledsrgb, orig.ledsrgb, sizeof(CRGB)*length()); } // WLEDMM @@ -179,7 +182,7 @@ Segment& Segment::operator= (const Segment &orig) { if (!Segment::_globalLeds) {ledsrgb = nullptr; ledsrgbSize = 0;}; // WLEDMM copy has no buffers (yet) // copy source data if (orig.name) { name = new(std::nothrow) char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } - if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } + if (orig.data) { if (allocateData(orig._dataLen, true)) memcpy(data, orig.data, orig._dataLen); } //if (orig._t) { _t = new(std::nothrow) Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); } //else markForReset(); // WLEDMM //if (orig.ledsrgb && !Segment::_globalLeds) { allocLeds(); if (ledsrgb) memcpy(ledsrgb, orig.ledsrgb, sizeof(CRGB)*length()); } // WLEDMM don't copy old buffer @@ -218,7 +221,7 @@ Segment& Segment::operator= (Segment &&orig) noexcept { return *this; } -bool Segment::allocateData(size_t len) { +bool Segment::allocateData(size_t len, bool allowOverdraft) { // WLEDMM allowOverdraft for temporary overdraft by segment copy constructor // WLEDMM if (data && _dataLen >= len) { // already allocated enough (reduce fragmentation) if ((call == 0) && (len > 0)) memset(data, 0, len); // erase buffer if called during effect initialisation @@ -228,9 +231,11 @@ bool Segment::allocateData(size_t len) { deallocateData(); if (len == 0) return false; // nothing to do if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) { - //USER_PRINTF("Segment::allocateData: Segment data quota exceeded! used:%u request:%u max:%d\n", Segment::getUsedSegmentData(), len, MAX_SEGMENT_DATA); - if (len > 0) errorFlag = ERR_LOW_SEG_MEM; // WLEDMM raise errorflag - return false; //not enough memory + if (!allowOverdraft || (Segment::getUsedSegmentData() + len > MAX_SEGMENT_OVERDATA)) { // WLEDMM 50% overdraft allowed temporarily + //USER_PRINTF("Segment::allocateData: Segment data quota exceeded! used:%u request:%u max:%d\n", Segment::getUsedSegmentData(), len, MAX_SEGMENT_DATA); + if (len > 0) errorFlag = ERR_LOW_SEG_MEM; // WLEDMM raise errorflag + return false; //not enough memory + } } // do not use SPI RAM on ESP32 since it is slow //#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM) @@ -241,23 +246,33 @@ bool Segment::allocateData(size_t len) { data = (byte*) malloc(len); if (!data) { _dataLen = 0; // WLEDMM reset dataLen + if ((errorFlag != ERR_LOW_MEM) && (errorFlag != ERR_LOW_SEG_MEM)) { // spam filter + USER_PRINT(F("Segment::allocateData: FAILED to allocate ")); + USER_PRINT(len); USER_PRINTLN(F(" bytes.")); + } errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag - USER_PRINT(F("Segment::allocateData: FAILED to allocate ")); - USER_PRINT(len); USER_PRINTLN(F(" bytes.")); return false; } //allocation failed Segment::addUsedSegmentData(len); + DEBUG_PRINTF("Segment::allocateData: %u bytes allocated (%u used)\n", len, Segment::getUsedSegmentData()); _dataLen = len; memset(data, 0, len); - if (errorFlag == ERR_LOW_SEG_MEM) errorFlag = ERR_NONE; // WLEDMM reset errorflag on success + if ((errorFlag == ERR_LOW_SEG_MEM) || (errorFlag == ERR_LOW_MEM) || (errorFlag == ERR_NORAM_PX)) errorFlag = ERR_NONE; // WLEDMM reset errorflag on success return true; } void Segment::deallocateData() { - if (!data) {_dataLen = 0; return;} // WLEDMM reset dataLen + if (!data) { + if (_dataLen>0) { + Segment::addUsedSegmentData(-_dataLen); // WLEDMM fix housekeeping + DEBUG_PRINTF("Segment::deallocateData unregistering %u bytes as unused.", _dataLen); + } + _dataLen = 0; + return; + } // WLEDMM reset dataLen free(data); data = nullptr; - //USER_PRINTF("Segment::deallocateData: free'd %d bytes.\n", _dataLen); + DEBUG_PRINTF("Segment::deallocateData: free'd %d bytes.\n", _dataLen); Segment::addUsedSegmentData(-_dataLen); _dataLen = 0; } @@ -525,8 +540,11 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t stateChanged = true; // send UDP/WS broadcast - if (stop>start) markForBlank(); //turn old segment range off // WLEDMM stop > start + if (stop>start) markForBlank(); //turn old segment range off // WLEDMM stop > start // toDo: check if this can be skipped when boundsUnchanged if (i2 <= i1) { //disable segment + #ifdef WLED_ENABLE_GIF + endImagePlayback(this); + #endif stop = 0; markForReset(); return; @@ -546,8 +564,11 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t spacing = spc; } if (ofs < UINT16_MAX) offset = ofs; - markForReset(); - if (!boundsUnchanged) refreshLightCapabilities(); + + if (!boundsUnchanged) { + markForReset(); + refreshLightCapabilities(); + } } @@ -672,12 +693,12 @@ struct XandY { uint8_t y; }; struct ArrayAndSize { - uint8_t size; + uint16_t size; XandY *array; }; class JMapC { public: - char previousSegmentName[50] = ""; + char previousSegmentName[WLED_MAX_SEGNAME_LEN+12] = ""; ~JMapC() { DEBUG_PRINTLN("~JMapC"); @@ -712,7 +733,7 @@ class JMapC { } uint32_t getPixelColor(uint16_t i) { updatejMapDoc(); - if (jVectorMap.size() > 0) + if (jVectorMap.size() > i) // implies jVectorMap.size() > 0, because i is unsigned return SEGMENT.getPixelColorXY(jVectorMap[i].array[0].x * scale, jVectorMap[i].array[0].y * scale); else return 0; @@ -720,7 +741,7 @@ class JMapC { private: std::vector jVectorMap; StaticJsonDocument<4096> docChunk; //must fit forks with about 32 points each - uint8_t scale; + uint8_t scale=1; void updatejMapDoc() { if (SEGMENT.name == nullptr && jVectorMap.size() > 0) { @@ -730,7 +751,7 @@ class JMapC { uint32_t dataSize = 0; deletejVectorMap(); DEBUG_PRINT("New "); DEBUG_PRINTLN(SEGMENT.name); - char jMapFileName[50]; + char jMapFileName[WLED_MAX_SEGNAME_LEN+12] = {'\0'}; // we need at most 32 + 7 bytes strcpy(jMapFileName, "/"); strcat(jMapFileName, SEGMENT.name); strcat(jMapFileName, ".json"); @@ -750,7 +771,10 @@ class JMapC { { USER_PRINTF("deserializeJson() of parseTree failed with code %s\n", err.c_str()); USER_FLUSH(); - if (SEGMENT.name) delete[] SEGMENT.name; SEGMENT.name = nullptr; //need to clear the name as otherwise continuously loaded // softhack007 avoid deleting nullptr + // softhack007: DO NOT delete SEGMENT.name - it's owned by Segment class, deleting it from outside can lead to use-after-free + // if (SEGMENT.name) delete[] SEGMENT.name; SEGMENT.name = nullptr; //need to clear the name as otherwise continuously loaded // softhack007 avoid deleting nullptr + strlcpy(previousSegmentName, SEGMENT.name, sizeof(previousSegmentName)); // Mark name as processed to avoid reload loop + if (jMapFile) jMapFile.close(); // make sure the file is closed return; } @@ -795,7 +819,7 @@ class JMapC { USER_PRINT(dataSize); USER_PRINT(" scale "); USER_PRINTLN(scale); - strcpy(previousSegmentName, SEGMENT.name); + strlcpy(previousSegmentName, SEGMENT.name, sizeof(previousSegmentName)); } } //updatejMapDoc }; //class JMapC @@ -935,18 +959,19 @@ static void xyFromBlock(uint16_t &x,uint16_t &y, uint16_t i, uint16_t vW, uint16 x = vW / 2 - vStrip - 1; y = vH / 2 + vStrip - i2 * vStrip * 2; } - + // softhack007 not sure if clamping is necessary + //x = min(x, uint16_t(vW-1)); // clamp x at vW-1 + //y = min(y, uint16_t(vH-1)); // clamp y at vH-1 } -void IRAM_ATTR_YN __attribute__((hot)) Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATTR conditionally +void IRAM_ATTR_YN WLED_O2_ATTR __attribute__((hot)) Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATTR conditionally { if (!isActive()) return; // not active #ifndef WLED_DISABLE_2D int vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows) #endif i &= 0xFFFF; - - if (i >= virtualLength() || i<0) return; // if pixel would fall out of segment just exit + if (unsigned(i) >= virtualLength()) return; // if pixel would fall out of segment just exit //WLEDMM unsigned(i)>SEGLEN also catches "i<0" #ifndef WLED_DISABLE_2D if (is2D()) { @@ -1212,7 +1237,7 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa) } } -uint32_t __attribute__((hot)) Segment::getPixelColor(int i) const +uint32_t WLED_O2_ATTR __attribute__((hot)) Segment::getPixelColor(int i) const { if (!isActive()) return 0; // not active #ifndef WLED_DISABLE_2D @@ -1703,22 +1728,22 @@ void WS2812FX::enumerateLedmaps() { f = WLED_FS.open(fileName, "r"); if (f) { f.find("\"n\":"); - char name[34] = { '\0' }; // ensure string termination + char name[WLED_MAX_SEGNAME_LEN+2] = { '\0' }; // ensure string termination f.readBytesUntil('\n', name, sizeof(name)-1); size_t len = strlen(name); - if (len > 0 && len < 33) { + if (len > 0 && len < (sizeof(name)-1)) { (void) cleanUpName(name); len = strlen(name); ledmapNames[i-1] = new(std::nothrow) char[len+1]; // +1 to include terminating \0 - if (ledmapNames[i-1]) strlcpy(ledmapNames[i-1], name, 33); + if (ledmapNames[i-1]) strlcpy(ledmapNames[i-1], name, len+1); } if (!ledmapNames[i-1]) { char tmp[33]; snprintf_P(tmp, 32, PSTR("ledmap%d.json"), i); - len = strlen(tmp); - ledmapNames[i-1] = new(std::nothrow) char[len+1]; - if (ledmapNames[i-1]) strlcpy(ledmapNames[i-1], tmp, 33); + size_t tmplen = strlen(tmp); + ledmapNames[i-1] = new(std::nothrow) char[tmplen+1]; + if (ledmapNames[i-1]) strlcpy(ledmapNames[i-1], tmp, tmplen+1); } USER_PRINTF("enumerateLedmaps %s \"%s\"", fileName, name); @@ -1754,8 +1779,8 @@ void WS2812FX::enumerateLedmaps() { uint8_t segment_index = 0; for (segment &seg : _segments) { if (seg.name != nullptr && strlen(seg.name) > 0) { - char fileName[33]; - snprintf_P(fileName, sizeof(fileName), PSTR("/lm%s.json"), seg.name); + char fileName[WLED_MAX_SEGNAME_LEN+12] = { '\0' }; // segment name is 32 chars max, so we need 43 chars in worst case + snprintf_P(fileName, sizeof(fileName)-1, PSTR("/lm%s.json"), seg.name); bool isFile = WLED_FS.exists(fileName); if (isFile) ledMaps |= 1 << (10+segment_index); } @@ -1830,9 +1855,19 @@ void WS2812FX::finalizeInit(void) //initialize leds array. TBD: realloc if nr of leds change if (Segment::_globalLeds) { + // DONG - Valkyrie is about to die + // this is a critical section that will be removed with PR #278 which removes _globalLeds + // problem: suspendStripService provides interlocking, but there’s a window before service() observes it, + // and ESP32 is dual-core. A critical section closes that window so the pointer swap is atomic across cores. +#if defined(ARDUINO_ARCH_ESP32) + taskENTER_CRITICAL(&s_wled_strip_mux); +#endif free(Segment::_globalLeds); Segment::_globalLeds = nullptr; purgeSegments(true); // WLEDMM moved here, because it seems to improve stability. +#if defined(ARDUINO_ARCH_ESP32) + taskEXIT_CRITICAL(&s_wled_strip_mux); +#endif } if (useLedsArray && getLengthTotal()>0) { // WLEDMM avoid malloc(0) size_t arrSize = sizeof(CRGB) * getLengthTotal(); @@ -1844,7 +1879,7 @@ void WS2812FX::finalizeInit(void) //#endif if (arrSize > 0) Segment::_globalLeds = (CRGB*) malloc(arrSize); // WLEDMM avoid malloc(0) if ((Segment::_globalLeds != nullptr) && (arrSize > 0)) memset(Segment::_globalLeds, 0, arrSize); // WLEDMM avoid dereferencing nullptr - if ((Segment::_globalLeds == nullptr) && (arrSize > 0)) errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag + if ((Segment::_globalLeds == nullptr) && (arrSize > 0)) errorFlag = ERR_NORAM_PX; // WLEDMM raise errorflag } //segments are created in makeAutoSegments(); @@ -1858,7 +1893,8 @@ void WS2812FX::finalizeInit(void) // WLEDMM wait until strip is idle (=not servicing). // on 8266 this function does nothing, because we can only do "busy waiting" on ESP32 -#define MAX_IDLE_WAIT_MS 50 // seems to work in most cases +//#define MAX_IDLE_WAIT_MS 50 // seems to work in most cases +#define MAX_IDLE_WAIT_MS 120 // better safe than sorry - similar to the timeout used by upstream WLED void WS2812FX::waitUntilIdle(void) { #if defined(ARDUINO_ARCH_ESP32) && defined(WLEDMM_PROTECT_SERVICE) if (isServicing()) { @@ -1914,7 +1950,7 @@ void WS2812FX::service() { if(nowUp >= seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC)) // WLEDMM ">=" instead of ">" { if (seg.grouping == 0) seg.grouping = 1; //sanity check - if (!seg.freeze) doShow = true; + if ((!seg.freeze) || _triggered) doShow = true; // WLEDMM "triggered" overrules "freeze" uint16_t frameDelay = FRAMETIME; // WLEDMM avoid name clash with "delay" function if (!seg.freeze) { //only run effect function if not frozen @@ -1950,6 +1986,8 @@ void WS2812FX::service() { } _segment_index++; } + if (_triggered) doShow = true; // WLEDMM "triggered" always means "show" + _virtualSegmentLength = 0; busses.setSegmentCCT(-1); if(doShow) { @@ -1969,26 +2007,11 @@ void WS2812FX::service() { _isServicing = false; } -void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col) -{ - if (i < customMappingSize) i = customMappingTable[i]; - if (i >= _length) return; - busses.setPixelColor(i, col); -} -uint32_t WS2812FX::getPixelColor(uint_fast16_t i) const // WLEDMM fast int types -{ - if (i < customMappingSize) i = customMappingTable[i]; - if (i >= _length) return 0; - return busses.getPixelColor(i); -} +// WLEDMM: WS2812FX::setPixelColor() moved FX.h for speed (inlining) +// WLEDMM: WS2812FX::getPixelColor() moved FX.h for speed (inlining) +// WLEDMM: WS2812FX::getPixelColorRestored() moved FX.h for speed (inlining) -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, @@ -2033,6 +2056,7 @@ void WS2812FX::estimateCurrentAndLimitBri() { for (uint_fast8_t bNum = 0; bNum < busses.getNumBusses(); bNum++) { Bus *bus = busses.getBus(bNum); + if (!bus || !bus->isOk()) continue; // WLEDMM skip busses that are not initialized yet auto btype = bus->getType(); if (EXCLUDE_FROM_ABL(btype)) continue; // WLEDMM exclude non-ABL and network busses uint16_t len = bus->getLength(); @@ -2563,7 +2587,7 @@ void WS2812FX::loadCustomPalettes() { bool WS2812FX::deserializeMap(uint8_t n) { // 2D support creates its own ledmap (on the fly) if a ledmap.json exists it will overwrite built one. - char fileName[32] = {'\0'}; + char fileName[WLED_MAX_SEGNAME_LEN+10] = {'\0'}; // WLEDMM we need at least 32 + 7 bytes //WLEDMM: als support segment name ledmaps bool isFile = false;; if (n<10) { @@ -2575,7 +2599,8 @@ bool WS2812FX::deserializeMap(uint8_t n) { uint8_t segment_index = 0; for (segment &seg : _segments) { if (n == 10 + segment_index && !isFile && seg.name != nullptr) { - sprintf_P(fileName, PSTR("/%s.json"), seg.name); + snprintf_P(fileName, sizeof(fileName) -1, PSTR("/%s.json"), seg.name); + fileName[sizeof(fileName) -1] = '\0'; isFile = WLED_FS.exists(fileName); } if (isFile) break; @@ -2617,13 +2642,13 @@ bool WS2812FX::deserializeMap(uint8_t n) { //WLEDMM: read width and height memset(fileName, 0, sizeof(fileName)); // clear old buffer - readBytesUntil() does not terminate strings !!! f.find("\"width\":"); - f.readBytesUntil('\n', fileName, sizeof(fileName)); //hack: use fileName as we have this allocated already + f.readBytesUntil('\n', fileName, sizeof(fileName)-1); //hack: use fileName as we have this allocated already uint16_t maxWidth = atoi(cleanUpName(fileName)); //DEBUG_PRINTF(" (\"width\": %s) ", fileName) memset(fileName, 0, sizeof(fileName)); // clear old buffer f.find("\"height\":"); - f.readBytesUntil('\n', fileName, sizeof(fileName)); + f.readBytesUntil('\n', fileName, sizeof(fileName)-1); uint16_t maxHeight = atoi(cleanUpName(fileName)); //DEBUG_PRINTF(" (\"height\": %s) \n", fileName) @@ -2660,6 +2685,7 @@ bool WS2812FX::deserializeMap(uint8_t n) { errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag } } + if ((errorFlag == ERR_LOW_MEM) && (size > 0) && (customMappingTable != nullptr)) errorFlag = ERR_NONE; // reset error flag if (customMappingTable != nullptr) customMappingTableSize = size; } @@ -2676,7 +2702,7 @@ bool WS2812FX::deserializeMap(uint8_t n) { int mapi = f.readStringUntil(',').toInt(); // USER_PRINTF(", %d(%d)", mapi, i); if (i < customMappingSize) customMappingTable[i++] = (uint16_t) (mapi<0 ? 0xFFFFU : mapi); // WLEDMM do not write past array bounds - } while (f.available()); + } while (f.available() && (i < customMappingSize)); loadedLedmap = n; f.close(); @@ -2691,6 +2717,7 @@ bool WS2812FX::deserializeMap(uint8_t n) { #endif } else { // memory allocation error customMappingTableSize = 0; + errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag USER_PRINTLN(F("Deserializemap: Ledmap alloc error.")); USER_FLUSH(); } diff --git a/wled00/FXparticleSystem.cpp b/wled00/FXparticleSystem.cpp index 9e5162c8..d7233054 100644 --- a/wled00/FXparticleSystem.cpp +++ b/wled00/FXparticleSystem.cpp @@ -593,7 +593,7 @@ void ParticleSystem2D::render() { baseRGB = ColorFromPalette(SEGPALETTE, particles[i].hue, 255, blend); if (particles[i].sat < 255) { CHSV baseHSV = rgb2hsv_approximate(baseRGB); // convert to HSV - baseHSV.s = particles[i].sat; // set the saturation + baseHSV.s = min(baseHSV.s, particles[i].sat); // set the saturation but don't increase it hsv2rgb_spectrum(baseHSV, baseRGB); // convert back to RGB } } @@ -628,7 +628,7 @@ void ParticleSystem2D::render() { } // calculate pixel positions and brightness distribution and render the particle to local buffer or global buffer -__attribute__((optimize("O2"))) void ParticleSystem2D::renderParticle(const uint32_t particleindex, const uint8_t brightness, const CRGB& color, const bool wrapX, const bool wrapY) { +void WLED_O3_ATTR ParticleSystem2D::renderParticle(const uint32_t particleindex, const uint8_t brightness, const CRGB& color, const bool wrapX, const bool wrapY) { uint32_t size = particlesize; if (advPartProps && advPartProps[particleindex].size > 0) // use advanced size properties (0 means use global size including single pixel rendering) size = advPartProps[particleindex].size; @@ -846,7 +846,7 @@ void ParticleSystem2D::handleCollisions() { // handle a collision if close proximity is detected, i.e. dx and/or dy smaller than 2*PS_P_RADIUS // takes two pointers to the particles to collide and the particle hardness (softer means more energy lost in collision, 255 means full hard) -__attribute__((optimize("O2"))) void ParticleSystem2D::collideParticles(PSparticle &particle1, PSparticle &particle2, int32_t dx, int32_t dy, const uint32_t collDistSq) { +void WLED_O3_ATTR ParticleSystem2D::collideParticles(PSparticle &particle1, PSparticle &particle2, int32_t dx, int32_t dy, const uint32_t collDistSq) { int32_t distanceSquared = dx * dx + dy * dy; // Calculate relative velocity note: could zero check but that does not improve overall speed but deminish it as that is rarely the case and pushing is still required int32_t relativeVx = (int32_t)particle2.vx - (int32_t)particle1.vx; @@ -1056,13 +1056,7 @@ void blur2D(CRGB *colorbuffer, uint32_t xsize, uint32_t ysize, uint32_t xblur, u //non class functions to use for initialization uint32_t calculateNumberOfParticles2D(uint32_t const pixels, const bool isadvanced, const bool sizecontrol) { uint32_t numberofParticles = pixels; // 1 particle per pixel (for example 512 particles on 32x16) -#ifdef ESP8266 - uint32_t particlelimit = ESP8266_MAXPARTICLES; // maximum number of paticles allowed (based on one segment of 16x16 and 4k effect ram) -#elif ARDUINO_ARCH_ESP32S2 - uint32_t particlelimit = ESP32S2_MAXPARTICLES; // maximum number of paticles allowed (based on one segment of 32x32 and 24k effect ram) -#else - uint32_t particlelimit = ESP32_MAXPARTICLES; // maximum number of paticles allowed (based on two segments of 32x32 and 40k effect ram) -#endif + uint32_t particlelimit = MAXPARTICLES_2D; // maximum number of paticles allowed numberofParticles = max((uint32_t)4, min(numberofParticles, particlelimit)); // limit to 4 - particlelimit if (isadvanced) // advanced property array needs ram, reduce number of particles to use the same amount numberofParticles = (numberofParticles * sizeof(PSparticle)) / (sizeof(PSparticle) + sizeof(PSadvancedParticle)); @@ -1075,16 +1069,8 @@ uint32_t calculateNumberOfParticles2D(uint32_t const pixels, const bool isadvanc } uint32_t calculateNumberOfSources2D(uint32_t pixels, uint32_t requestedsources) { -#ifdef ESP8266 - int numberofSources = min((pixels) / 8, (uint32_t)requestedsources); - numberofSources = max(1, min(numberofSources, ESP8266_MAXSOURCES)); // limit -#elif ARDUINO_ARCH_ESP32S2 - int numberofSources = min((pixels) / 6, (uint32_t)requestedsources); - numberofSources = max(1, min(numberofSources, ESP32S2_MAXSOURCES)); // limit -#else - int numberofSources = min((pixels) / 4, (uint32_t)requestedsources); - numberofSources = max(1, min(numberofSources, ESP32_MAXSOURCES)); // limit -#endif + int numberofSources = min((pixels) / SOURCEREDUCTIONFACTOR, (uint32_t)requestedsources); + numberofSources = max(1, min(numberofSources, MAXSOURCES_2D)); // limit // make sure it is a multiple of 4 for proper memory alignment numberofSources = (numberofSources+3) & ~0x03; return numberofSources; @@ -1103,7 +1089,7 @@ bool allocateParticleSystemMemory2D(uint32_t numparticles, uint32_t numsources, if (sizecontrol) requiredmemory += sizeof(PSsizeControl) * numparticles; requiredmemory += sizeof(PSsource) * numsources; - requiredmemory += sizeof(CRGB) * SEGMENT.virtualLength(); // virtualLength is witdh * height + requiredmemory += sizeof(CRGB) * uint32_t(SEGMENT.calc_virtualWidth()) * uint32_t(SEGMENT.calc_virtualHeight()); // virtualLength not reliable, better to use vWidth * vHeight requiredmemory += additionalbytes + 3; // add 3 to ensure there is room for stuffing bytes //requiredmemory = (requiredmemory + 3) & ~0x03; // align memory block to next 4-byte boundary PSPRINTLN("mem alloc: " + String(requiredmemory)); @@ -1122,10 +1108,19 @@ bool initParticleSystem2D(ParticleSystem2D *&PartSys, uint32_t requestedsources, PSPRINT(" segmentsize:" + String(cols) + " x " + String(rows)); PSPRINT(" request numparticles:" + String(numparticles)); uint32_t numsources = calculateNumberOfSources2D(pixels, requestedsources); - if (!allocateParticleSystemMemory2D(numparticles, numsources, advanced, sizecontrol, additionalbytes)) - { - DEBUG_PRINT(F("PS init failed: memory depleted")); - return false; + bool allocsuccess = false; + while(numparticles >= 4) { // make sure we have at least 4 particles or quit + if (allocateParticleSystemMemory2D(numparticles, numsources, advanced, sizecontrol, additionalbytes)) { + PSPRINTLN(F("PS 2D alloc succeeded")); + allocsuccess = true; + break; // allocation succeeded + } + numparticles = ((numparticles / 2) + 3) & ~0x03; // cut number of particles in half and try again, must be 4 byte aligned + PSPRINTLN(F("PS 2D alloc failed, trying with less particles...")); + } + if (!allocsuccess) { + PSPRINTLN(F("PS 2D alloc failed, not enough memory!")); + return false; // allocation failed } PartSys = new (SEGENV.data) ParticleSystem2D(cols, rows, numparticles, numsources, advanced, sizecontrol); // particle system constructor @@ -1457,7 +1452,7 @@ void ParticleSystem1D::render() { if (advPartProps) { //saturation is advanced property in 1D system if (advPartProps[i].sat < 255) { CHSV baseHSV = rgb2hsv_approximate(baseRGB); // convert to HSV - baseHSV.s = advPartProps[i].sat; + baseHSV.s = min(baseHSV.s, advPartProps[i].sat); // set the saturation but don't increase it hsv2rgb_spectrum(baseHSV, baseRGB); // convert back to RGB } } @@ -1494,7 +1489,7 @@ void ParticleSystem1D::render() { } // calculate pixel positions and brightness distribution and render the particle to local buffer or global buffer -__attribute__((optimize("O2"))) void ParticleSystem1D::renderParticle(const uint32_t particleindex, const uint8_t brightness, const CRGB &color, const bool wrap) { +void WLED_O2_ATTR ParticleSystem1D::renderParticle(const uint32_t particleindex, const uint8_t brightness, const CRGB &color, const bool wrap) { uint32_t size = particlesize; if (advPartProps) // use advanced size properties (1D system has no large size global rendering TODO: add large global rendering?) size = advPartProps[particleindex].size; @@ -1658,7 +1653,7 @@ void ParticleSystem1D::handleCollisions() { } // handle a collision if close proximity is detected, i.e. dx and/or dy smaller than 2*PS_P_RADIUS // takes two pointers to the particles to collide and the particle hardness (softer means more energy lost in collision, 255 means full hard) -__attribute__((optimize("O2"))) void ParticleSystem1D::collideParticles(PSparticle1D &particle1, const PSparticleFlags1D &particle1flags, PSparticle1D &particle2, const PSparticleFlags1D &particle2flags, const int32_t dx, const uint32_t dx_abs, const uint32_t collisiondistance) { +void WLED_O2_ATTR ParticleSystem1D::collideParticles(PSparticle1D &particle1, const PSparticleFlags1D &particle1flags, PSparticle1D &particle2, const PSparticleFlags1D &particle2flags, const int32_t dx, const uint32_t dx_abs, const uint32_t collisiondistance) { int32_t dv = particle2.vx - particle1.vx; int32_t dotProduct = (dx * dv); // is always negative if moving towards each other @@ -1775,32 +1770,20 @@ void ParticleSystem1D::updatePSpointers(bool isadvanced) { //non class functions to use for initialization, fraction is uint8_t: 255 means 100% uint32_t calculateNumberOfParticles1D(const uint32_t fraction, const bool isadvanced) { uint32_t numberofParticles = SEGMENT.virtualLength(); // one particle per pixel (if possible) -#ifdef ESP8266 - uint32_t particlelimit = ESP8266_MAXPARTICLES_1D; // maximum number of paticles allowed -#elif ARDUINO_ARCH_ESP32S2 - uint32_t particlelimit = ESP32S2_MAXPARTICLES_1D; // maximum number of paticles allowed -#else - uint32_t particlelimit = ESP32_MAXPARTICLES_1D; // maximum number of paticles allowed -#endif + uint32_t particlelimit = MAXPARTICLES_1D; // maximum number of paticles allowed numberofParticles = min(numberofParticles, particlelimit); // limit to particlelimit if (isadvanced) // advanced property array needs ram, reduce number of particles to use the same amount numberofParticles = (numberofParticles * sizeof(PSparticle1D)) / (sizeof(PSparticle1D) + sizeof(PSadvancedParticle1D)); numberofParticles = (numberofParticles * (fraction + 1)) >> 8; // calculate fraction of particles - numberofParticles = numberofParticles < 20 ? 20 : numberofParticles; // 20 minimum + numberofParticles = numberofParticles < 10 ? 10 : numberofParticles; // 10 minimum //make sure it is a multiple of 4 for proper memory alignment (easier than using padding bytes) numberofParticles = (numberofParticles+3) & ~0x03; // note: with a separate particle buffer, this is probably unnecessary - PSPRINTLN(" calc numparticles:" + String(numberofParticles)) + PSPRINTLN(" calc numparticles:" + String(numberofParticles)); return numberofParticles; } uint32_t calculateNumberOfSources1D(const uint32_t requestedsources) { -#ifdef ESP8266 - int numberofSources = max(1, min((int)requestedsources,ESP8266_MAXSOURCES_1D)); // limit -#elif ARDUINO_ARCH_ESP32S2 - int numberofSources = max(1, min((int)requestedsources, ESP32S2_MAXSOURCES_1D)); // limit -#else - int numberofSources = max(1, min((int)requestedsources, ESP32_MAXSOURCES_1D)); // limit -#endif + int numberofSources = max(1, min((int)requestedsources, MAXSOURCES_1D)); // limit // make sure it is a multiple of 4 for proper memory alignment (so minimum is acutally 4) numberofSources = (numberofSources+3) & ~0x03; return numberofSources; @@ -1828,9 +1811,19 @@ bool initParticleSystem1D(ParticleSystem1D *&PartSys, const uint32_t requestedso if (SEGLEN == 1) return false; // single pixel not supported uint32_t numparticles = calculateNumberOfParticles1D(fractionofparticles, advanced); uint32_t numsources = calculateNumberOfSources1D(requestedsources); - if (!allocateParticleSystemMemory1D(numparticles, numsources, advanced, additionalbytes)) { - DEBUG_PRINT(F("PS init failed: memory depleted")); - return false; + bool allocsuccess = false; + while(numparticles >= 10) { // make sure we have at least 10 particles or quit + if (allocateParticleSystemMemory1D(numparticles, numsources, advanced, additionalbytes)) { + PSPRINT(F("PS 1D alloc succeeded")); + allocsuccess = true; + break; // allocation succeeded + } + numparticles = ((numparticles / 2) + 3) & ~0x03; // cut number of particles in half and try again, must be 4 byte aligned + PSPRINTLN(F("PS 1D alloc failed, trying with less particles...")); + } + if (!allocsuccess) { + PSPRINTLN(F("PS init failed: memory depleted")); + return false; // allocation failed } PartSys = new (SEGENV.data) ParticleSystem1D(SEGMENT.virtualLength(), numparticles, numsources, advanced); // particle system constructor return true; @@ -1938,4 +1931,4 @@ static bool checkBoundsAndWrap(int32_t &position, const int32_t max, const int32 c.b = ((c.b * scale) >> 8); } -#endif // !(defined(WLED_DISABLE_PARTICLESYSTEM2D) && defined(WLED_DISABLE_PARTICLESYSTEM1D)) \ No newline at end of file +#endif // !(defined(WLED_DISABLE_PARTICLESYSTEM2D) && defined(WLED_DISABLE_PARTICLESYSTEM1D)) diff --git a/wled00/FXparticleSystem.h b/wled00/FXparticleSystem.h index a1d273a7..d295bf85 100644 --- a/wled00/FXparticleSystem.h +++ b/wled00/FXparticleSystem.h @@ -37,13 +37,20 @@ static inline int32_t limitSpeed(const int32_t speed) { #endif #ifndef WLED_DISABLE_PARTICLESYSTEM2D -// memory allocation -#define ESP8266_MAXPARTICLES 256 // enough up to 16x16 pixels -#define ESP8266_MAXSOURCES 24 -#define ESP32S2_MAXPARTICLES 1024 // enough up to 32x32 pixels -#define ESP32S2_MAXSOURCES 64 -#define ESP32_MAXPARTICLES 2048 // enough up to 64x32 pixels -#define ESP32_MAXSOURCES 128 +// memory allocation (based on reasonable segment size and available FX memory) +#ifdef ESP8266 + #define MAXPARTICLES_2D 256 + #define MAXSOURCES_2D 24 + #define SOURCEREDUCTIONFACTOR 8 +#elif ARDUINO_ARCH_ESP32S2 + #define MAXPARTICLES_2D 1024 + #define MAXSOURCES_2D 64 + #define SOURCEREDUCTIONFACTOR 6 +#else + #define MAXPARTICLES_2D 2048 + #define MAXSOURCES_2D 128 + #define SOURCEREDUCTIONFACTOR 4 +#endif // particle dimensions (subpixel division) #define PS_P_RADIUS 64 // subpixel size, each pixel is divided by this for particle movement (must be a power of 2) @@ -232,12 +239,16 @@ bool allocateParticleSystemMemory2D(const uint32_t numparticles, const uint32_t //////////////////////// #ifndef WLED_DISABLE_PARTICLESYSTEM1D // memory allocation -#define ESP8266_MAXPARTICLES_1D 320 -#define ESP8266_MAXSOURCES_1D 16 -#define ESP32S2_MAXPARTICLES_1D 1300 -#define ESP32S2_MAXSOURCES_1D 32 -#define ESP32_MAXPARTICLES_1D 2600 -#define ESP32_MAXSOURCES_1D 64 +#ifdef ESP8266 + #define MAXPARTICLES_1D 320 + #define MAXSOURCES_1D 16 +#elif ARDUINO_ARCH_ESP32S2 + #define MAXPARTICLES_1D 1300 + #define MAXSOURCES_1D 32 +#else + #define MAXPARTICLES_1D 2600 + #define MAXSOURCES_1D 64 +#endif // particle dimensions (subpixel division) #define PS_P_RADIUS_1D 32 // subpixel size, each pixel is divided by this for particle movement, if this value is changed, also change the shift defines (next two lines) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index e4e70507..8d4a6f01 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -79,6 +79,24 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte #include "wled.h" #endif +// WLEDMM moved here (from colors.cpp) for better optimization +static inline uint32_t __attribute__((hot)) colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb) // WLEDMM: IRAM_ATTR removed, inline for speed +{ + //remember so that slow colorKtoRGB() doesn't have to run for every setPixelColor() + static byte correctionRGB[4] = {255,255,255,0}; // default to neutral + static uint16_t lastKelvin = 0; + if (lastKelvin != kelvin) { + colorKtoRGB(kelvin, correctionRGB); // convert Kelvin to RGB (slow) + lastKelvin = kelvin; + } + byte rgbw[4]; + rgbw[0] = ((uint_fast16_t) correctionRGB[0] * R(rgb)) /255; // correct R //WLEDMM changed to fast type + rgbw[1] = ((uint_fast16_t) correctionRGB[1] * G(rgb)) /255; // correct G + rgbw[2] = ((uint_fast16_t) correctionRGB[2] * B(rgb)) /255; // correct B + rgbw[3] = W(rgb); + return RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3]); +} + void ColorOrderMap::add(uint16_t start, uint16_t len, uint8_t colorOrder) { if (_count >= WLED_MAX_COLOR_ORDER_MAPPINGS) { @@ -96,31 +114,40 @@ void ColorOrderMap::add(uint16_t start, uint16_t len, uint8_t colorOrder) { _count++; } -uint8_t IRAM_ATTR ColorOrderMap::getPixelColorOrder(uint16_t pix, uint8_t defaultColorOrder) const { +uint8_t __attribute__((hot)) ColorOrderMap::getPixelColorOrder(uint16_t pix, uint8_t defaultColorOrder) const { if (_count == 0) return defaultColorOrder; - // upper nibble contains W swap information - uint8_t swapW = defaultColorOrder >> 4; - for (uint8_t i = 0; i < _count; i++) { - if (pix >= _mappings[i].start && pix < (_mappings[i].start + _mappings[i].len)) { - return _mappings[i].colorOrder | (swapW << 4); + // upper nibble contains W swap information // WLEDMM optimization: avoid shifting >>4 and later undo by <<4 + uint8_t swapW = defaultColorOrder & 0xF0; + // Scan mappings, using unsigned range test: pix in [start, start+len) + for (uint_fast8_t i = 0, n = _count; i < n; i++) { // WLEDMM small speedup, by avoiding repeated class member access + const auto &m = _mappings[i]; // WLEDMM help the compiler to optimize + if ((uint16_t)(pix - m.start) < m.len) { // True iff m.len > 0 and pix >= m.start and pix < m.start + m.len + return (m.colorOrder & 0x0F) | swapW; // add W swap information } } return defaultColorOrder; } -uint32_t Bus::autoWhiteCalc(uint32_t c) const { - uint8_t aWM = _autoWhiteMode; - if (_gAWM != AW_GLOBAL_DISABLED) aWM = _gAWM; +uint32_t __attribute__((hot)) Bus::autoWhiteCalc(uint32_t c) const { + uint8_t aWM = (_gAWM != AW_GLOBAL_DISABLED) ? _gAWM : _autoWhiteMode; if (aWM == RGBW_MODE_MANUAL_ONLY) return c; - uint8_t w = W(c); + uint_fast8_t w = W(c); //ignore auto-white calculation if w>0 and mode DUAL (DUAL behaves as BRIGHTER if w==0) if (w > 0 && aWM == RGBW_MODE_DUAL) return c; - uint8_t r = R(c); - uint8_t g = G(c); - uint8_t b = B(c); - if (aWM == RGBW_MODE_MAX) return RGBW32(r, g, b, r > g ? (r > b ? r : b) : (g > b ? g : b)); // brightest RGB channel - w = r < g ? (r < b ? r : b) : (g < b ? g : b); + + uint_fast8_t r = R(c); + uint_fast8_t g = G(c); + uint_fast8_t b = B(c); + // brightest RGB channel + if (aWM == RGBW_MODE_MAX) { // WLEDMM use max() instead of several nested conditions + w = max(r, g); + w = max(w, b); + return RGBW32(r, g, b, w); + } + // Other modes: smallest RGB channel // WLEDMM use min() instead of several nested conditions + w = min(r, g); + w = min(w, b); if (aWM == RGBW_MODE_AUTO_ACCURATE) { r -= w; g -= w; b -= w; } //subtract w in ACCURATE mode return RGBW32(r, g, b, w); } @@ -583,6 +610,20 @@ uint8_t BusHub75Matrix::activeType = 0; uint8_t BusHub75Matrix::instanceCount = 0; uint8_t BusHub75Matrix::last_bri = 0; +#ifndef NO_CIE1931 + +// WLEDMM speedup: create a version of "unGamma8" that can be inlined by the compiler +extern uint8_t gammaTinv[256]; // defined in colors.cpp +static uint8_t const* myGammaTable = gammaTinv; // local alias for gammaTinv + +static inline uint8_t unGamma8_bus(uint8_t value) { + return myGammaTable[value]; +} +static inline uint32_t unGamma24_bus(uint32_t c) { + return RGBW32(myGammaTable[R(c)], myGammaTable[G(c)], myGammaTable[B(c)], W(c)); +} + +#endif // -------------------------- // Bitdepth reduction based on panel size @@ -882,6 +923,8 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh #endif 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); + USER_PRINTF("MatrixPanel_I2S_DMA config - clock phase = %s, latch_blanking = %d, min refresh = %d fps.\n", + mxconfig.clkphase ? "positive edge":"negative edge", int(mxconfig.latch_blanking), int(mxconfig.min_refresh_rate)); DEBUG_PRINT(F("Free heap: ")); DEBUG_PRINTLN(ESP.getFreeHeap()); lastHeap = ESP.getFreeHeap(); // check if we can re-use the existing display driver @@ -1053,30 +1096,43 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh activeFourScanPanel = fourScanPanel; if (newDisplay) memcpy(&activeMXconfig, &mxconfig, sizeof(mxconfig)); } + +#ifndef NO_CIE1931 + // force initial calculation of gamma correction tables + if ((gammaCorrectVal < 0.999f) || (gammaCorrectVal > 3.0f)) calcGammaTable(1.0f); + else calcGammaTable(gammaCorrectVal); +#endif + instanceCount++; USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - ESP.getFreeHeap())); } void __attribute__((hot)) IRAM_ATTR BusHub75Matrix::setPixelColor(uint16_t pix, uint32_t c) { - if ( pix >= _len) return; - // if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT - - if (_ledBuffer) { +// if ( pix >= _len) return; // not necessary - this was already checked at busses.setPixelColor() + #if 0 + if ((correctWB) && (_cct >= 1900)) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT - reduces framerate by up to 10%. If you still want it, change the line above to "#if 1" + #endif +// if (_ledBuffer) { // not necessary - isOk() would return false if buffer is not availeable CRGB fastled_col = CRGB(c); if (_ledBuffer[pix] != fastled_col) { _ledBuffer[pix] = fastled_col; setBitInArray(_ledsDirty, pix, true); // flag pixel as "dirty" } - } +// } } +// needed to mimic NeoPixelBus, which returns scaled-down colours uint32_t IRAM_ATTR BusHub75Matrix::getPixelColor(uint16_t pix) const { - if (pix >= _len || !_ledBuffer) return BLACK; - return uint32_t(_ledBuffer[pix].scale8(_bri)) & 0x00FFFFFF; // scale8() is needed to mimic NeoPixelBus, which returns scaled-down colours +// if (pix >= _len || !_ledBuffer) return BLACK; // not necessary - this was already checked at busses.getPixelColor() +#if defined(WLEDMM_FASTPATH) && !defined(WLEDMM_SAVE_FLASH) + return color_fade(uint32_t(_ledBuffer[pix]) & 0x00FFFFFF, _bri); // this is slightly faster if we have inline color_fade() +#else + return uint32_t(_ledBuffer[pix].scale8(_bri)) & 0x00FFFFFF; // do it the FastLED way +#endif } uint32_t __attribute__((hot)) IRAM_ATTR BusHub75Matrix::getPixelColorRestored(uint16_t pix) const { - if (pix >= _len || !_ledBuffer) return BLACK; +// if (pix >= _len || !_ledBuffer) return BLACK; // not necessary - this was already checked at busses.getPixelColorRestored() return uint32_t(_ledBuffer[pix]) & 0x00FFFFFF; } @@ -1114,13 +1170,12 @@ void __attribute__((hot)) IRAM_ATTR BusHub75Matrix::show(void) { for (int y=0; y= WLED_MAX_BUSSES) return -1; // WLEDMM clear cached Bus info first - lastend = 0; + lastlen = 0; laststart = 0; lastBus = nullptr; slowMode = false; @@ -1254,7 +1310,7 @@ void BusManager::removeAll() { // WLEDMM clear cached Bus info lastBus = nullptr; laststart = 0; - lastend = 0; + lastlen = 0; slowMode = false; } @@ -1275,24 +1331,28 @@ void BusManager::setStatusPixel(uint32_t c) { } } -void IRAM_ATTR __attribute__((hot)) BusManager::setPixelColor(uint16_t pix, uint32_t c, int16_t cct) { - if (!slowMode && (pix >= laststart) && (pix < lastend ) && lastBus->isOk()) { - // WLEDMM same bus as last time - no need to search again +void IRAM_ATTR __attribute__((hot)) BusManager::setPixelColor(uint16_t pix, uint32_t c) { + // Fast path: check cached bus first (with proper nullptr check) + // optimization: below is True iff lastlen > 0 and pix >= laststart and pix < laststart + lastlen + if (!slowMode && lastBus && ((uint_fast16_t)(pix - laststart) < lastlen) && lastBus->isOk()) { // WLEDMM saves us a few cycles for each pixel lastBus->setPixelColor(pix - laststart, c); return; } - for (uint_fast8_t i = 0; i < numBusses; i++) { // WLEDMM use fast native types - Bus* b = busses[i]; - if (b->isOk() == false) continue; // WLEDMM ignore invalid (=not ready) busses + // Slow path: search through all buses + uint_fast8_t count = numBusses; // Cache to avoid repeated member access + for (uint_fast8_t i = 0; i < count; i++) { + Bus* const b = busses[i]; // Use const pointer for optimization hint + if ((!b) || (b->isOk() == false)) continue; // WLEDMM ignore invalid (=not ready) busses uint_fast16_t bstart = b->getStart(); - if (pix < bstart || pix >= bstart + b->getLength()) continue; - else { + uint_fast16_t blen = b->getLength(); + + if ((uint_fast16_t)(pix - bstart) < blen) { // Unsigned arithmetic trick for fast range check if (!slowMode) { - // WLEDMM remember last Bus we took + // Cache bus info for next call lastBus = b; laststart = bstart; - lastend = bstart + b->getLength(); + lastlen = blen; } b->setPixelColor(pix - bstart, c); if (!slowMode) break; // WLEDMM found the right Bus -> so we can stop searching - unless we have busses that overlap @@ -1316,47 +1376,53 @@ void __attribute__((cold)) BusManager::setSegmentCCT(int16_t cct, bool allowWBCo } uint32_t IRAM_ATTR __attribute__((hot)) BusManager::getPixelColor(uint_fast16_t pix) { // WLEDMM use fast native types, IRAM_ATTR - if ((pix >= laststart) && (pix < lastend ) && (lastBus != nullptr) && lastBus->isOk()) { + // Fast path: check cached bus first (with proper null check, and unsigned arithmetic trick for faster range check) + if (lastBus && ((uint_fast16_t)(pix - laststart) < lastlen) && lastBus->isOk()) { // WLEDMM same bus as last time - no need to search again return lastBus->getPixelColor(pix - laststart); } - for (uint_fast8_t i = 0; i < numBusses; i++) { - Bus* b = busses[i]; - if (b->isOk() == false) continue; // WLEDMM ignore invalid (=not ready) busses + uint_fast8_t count = numBusses; // Cache to avoid repeated member access + for (uint_fast8_t i = 0; i < count; i++) { + Bus* const b = busses[i]; // Use const pointer for optimization hint + if ((!b) || (b->isOk() == false)) continue; // WLEDMM ignore invalid (=not ready) busses uint_fast16_t bstart = b->getStart(); - if (pix < bstart || pix >= bstart + b->getLength()) continue; - else { - if (!slowMode) { - // WLEDMM remember last Bus we took + uint_fast16_t blen = b->getLength(); + + if ((uint_fast16_t)(pix - bstart) < blen) { // Unsigned arithmetic trick for fast range check + //if (!slowMode) { + // Cache bus info for next call lastBus = b; laststart = bstart; - lastend = bstart + b->getLength(); - } - return b->getPixelColor(pix - bstart); + lastlen = blen; + //} + return b->getPixelColor(pix - bstart); // done - found one } } 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) && lastBus->isOk()) { + // Fast path: check cached bus first (with proper null check, and unsigned arithmetic trick for faster range check) + if (lastBus && ((uint_fast16_t)(pix - laststart) < lastlen) && lastBus->isOk()) { // 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]; - if (b->isOk() == false) continue; // WLEDMM ignore invalid (=not ready) busses + uint_fast8_t count = numBusses; // Cache to avoid repeated member access + for (uint_fast8_t i = 0; i < count; i++) { + Bus* const b = busses[i]; // Use const pointer for optimization hint + if ((!b) || (b->isOk() == false)) continue; // WLEDMM ignore invalid (=not ready) busses uint_fast16_t bstart = b->getStart(); - if (pix < bstart || pix >= bstart + b->getLength()) continue; - else { - if (!slowMode) { - // WLEDMM remember last Bus we took + uint_fast16_t blen = b->getLength(); + + if ((uint_fast16_t)(pix - bstart) < blen) { // Unsigned arithmetic trick for range check + //if (!slowMode) { + // Cache bus info for next call lastBus = b; laststart = bstart; - lastend = bstart + b->getLength(); - } + lastlen = blen; + //} return b->getPixelColorRestored(pix - bstart); } } diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 04062215..ea886eaf 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -175,7 +175,7 @@ class Bus { _type == TYPE_ANALOG_2CH || _type == TYPE_ANALOG_5CH) return true; return false; } - static void setCCT(uint16_t cct) { + static void setCCT(int16_t cct) { // WLEDMM bugfix: parameter must be signed, otherwise "-1" becomes 65535 --> undefined behaviour on RISC-V _cct = cct; } static void setCCTBlend(uint8_t b) { @@ -463,13 +463,14 @@ class BusManager { // WLEDMM clear cached Bus info lastBus = nullptr; laststart = 0; - lastend = 0; + lastlen= 0; slowMode = isRTMode; } void setStatusPixel(uint32_t c); - void setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1); + void setPixelColor(uint16_t pix, uint32_t c); // WLEDMM third parameter "cct" is never used - removed + //void setPixelColor(uint16_t pix, uint32_t c, int16_t cct) {Bus::setCCT(cct); setPixelColor(pix, c);}; // WLEDMM legacy support - slow, avoid using it void setBrightness(uint8_t b, bool immediate=false); // immediate=true is for use in ABL, it applies brightness immediately (warning: inefficient) @@ -504,7 +505,7 @@ class BusManager { // WLEDMM cache last used Bus -> 20% to 30% speedup when using many LED pins Bus *lastBus = nullptr; unsigned laststart = 0; - unsigned lastend = 0; + unsigned lastlen = 0; bool slowMode = false; // WLEDMM not sure why we need this. But its necessary. inline uint8_t getNumVirtualBusses() const { diff --git a/wled00/colorTools.hpp b/wled00/colorTools.hpp index c47f1934..27249e31 100644 --- a/wled00/colorTools.hpp +++ b/wled00/colorTools.hpp @@ -25,8 +25,9 @@ /* * color blend function (from colors.cpp) */ -inline __attribute__((hot,const)) uint32_t color_blend(uint32_t color1, uint32_t color2, uint_fast16_t blend, bool b16=false) { - if ((color1 == color2) || (blend == 0)) return color1; // WLEDMM +inline WLED_O3_ATTR __attribute__((hot,const)) uint32_t color_blend(uint32_t color1, uint32_t color2, uint_fast16_t blend, bool b16=false) { + // min / max blend checking is omitted: calls with 0 or 255 are rare, checking lowers overall performance + // if ((color1 == color2) || (blend == 0)) return color1; // WLEDMM const uint_fast16_t blendmax = b16 ? 0xFFFF : 0xFF; if(blend >= blendmax) return color2; const uint_fast8_t shift = b16 ? 16 : 8; @@ -64,7 +65,7 @@ inline __attribute__((hot,const)) uint32_t color_blend(uint32_t color1, uint32_t * idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule */ -inline __attribute__((hot,const)) uint32_t color_add(uint32_t c1, uint32_t c2, bool fast=false) +inline WLED_O3_ATTR __attribute__((hot,const)) uint32_t color_add(uint32_t c1, uint32_t c2, bool fast=false) { if (c2 == 0) return c1; // WLEDMM shortcut if (c1 == 0) return c2; // WLEDMM shortcut @@ -98,34 +99,26 @@ inline __attribute__((hot,const)) uint32_t color_add(uint32_t c1, uint32_t c2, b * if using "video" method the resulting color will never become black unless it is already black */ -inline __attribute__((hot,const)) uint32_t color_fade(uint32_t c1, uint8_t amount, bool video=false) +inline WLED_O2_ATTR __attribute__((hot,const)) uint32_t color_fade(uint32_t c1, uint8_t amount, bool video=false) { - if (amount == 255) return c1; // WLEDMM small optimization - plus it avoids over-fading in "video" mode - if (amount == 0) return 0; // WLEDMM shortcut + if (c1 == 0 || amount == 0) return 0; // black or no change + if (amount == 255) return c1; + uint32_t addRemains = 0; - uint32_t scaledcolor = 0; // color order is: W R G B from MSB to LSB - uint16_t w = W(c1); // WLEDMM 16bit to make sure the compiler uses 32bit (not 64bit) for the math - uint16_t r = R(c1); - uint16_t g = G(c1); - uint16_t b = B(c1); - if (video) { - uint16_t scale = amount; // 32bit for faster calculation - // bugfix: doing "+1" after shifting is obviously wrong - // optimization: ((r && scale) ? 1 : 0) can be simplified to "if (r > 0) +1" ; if we arive here, then scale != 0 and scale < 255 - if (w>0) scaledcolor |= (((w * scale) >> 8) +1) << 24; // WLEDMM small speedup when no white channel - if (r>0) scaledcolor |= (((r * scale) >> 8) +1) << 16; - if (g>0) scaledcolor |= (((g * scale) >> 8) +1) << 8; - if (b>0) scaledcolor |= ((b * scale) >> 8) +1; - return scaledcolor; - } - else { - uint16_t scale = 1 + amount; - if (w>0) scaledcolor |= ((w * scale) >> 8) << 24; // WLEDMM small speedup when no white channel - scaledcolor |= ((r * scale) >> 8) << 16; - scaledcolor |= (g * scale) & 0x0000FF00; // WLEDMM faster than right-left shift "" >>8 ) <<8" - scaledcolor |= (b * scale) >> 8; - return scaledcolor; + if (!video) amount++; // add one for correct scaling using bitshifts + else { + // video scaling: make sure colors do not dim to zero if they started non-zero unless they distort the hue + uint8_t r = byte(c1>>16), g = byte(c1>>8), b = byte(c1), w = byte(c1>>24); // extract r, g, b, w channels + uint8_t maxc = (r > g) ? ((r > b) ? r : b) : ((g > b) ? g : b); // determine dominant channel for hue preservation + addRemains = r && (r<<5) > maxc ? 0x00010000 : 0; // note: setting color preservation threshold too high results in flickering and + addRemains |= g && (g<<5) > maxc ? 0x00000100 : 0; // jumping colors in low brightness gradients. Multiplying the color preserves + addRemains |= b && (b<<5) > maxc ? 0x00000001 : 0; // better accuracy than dividing the maxc. Shifting by 5 is a good compromise + addRemains |= w ? 0x01000000 : 0; // i.e. remove color channel if <13% of max } + const uint32_t TWO_CHANNEL_MASK = 0x00FF00FF; + uint32_t rb = (((c1 & TWO_CHANNEL_MASK) * amount) >> 8) & TWO_CHANNEL_MASK; // scale red and blue + uint32_t wg = (((c1 >> 8) & TWO_CHANNEL_MASK) * amount) & ~TWO_CHANNEL_MASK; // scale white and green + return (rb | wg) + addRemains; } //scales the brightness with the briMultiplier factor (from led.cpp) diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 37241959..33b37180 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -8,8 +8,9 @@ /* * color blend function */ -IRAM_ATTR_YN __attribute__((hot)) uint32_t color_blend(uint32_t color1, uint32_t color2, uint_fast16_t blend, bool b16) { - if ((color1 == color2) || (blend == 0)) return color1; // WLEDMM +uint32_t WLED_O3_ATTR IRAM_ATTR_YN __attribute__((hot)) color_blend(uint32_t color1, uint32_t color2, uint_fast16_t blend, bool b16) { + // min / max blend checking is omitted: calls with 0 or 255 are rare, checking lowers overall performance + //if ((color1 == color2) || (blend == 0)) return color1; // WLEDMM const uint_fast16_t blendmax = b16 ? 0xFFFF : 0xFF; if(blend >= blendmax) return color2; const uint_fast8_t shift = b16 ? 16 : 8; @@ -46,7 +47,7 @@ IRAM_ATTR_YN __attribute__((hot)) uint32_t color_blend(uint32_t color1, uint32_t * color add function that preserves ratio * idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule */ -IRAM_ATTR_YN uint32_t color_add(uint32_t c1, uint32_t c2, bool fast) // WLEDMM added IRAM_ATTR_YN +uint32_t WLED_O2_ATTR IRAM_ATTR_YN color_add(uint32_t c1, uint32_t c2, bool fast) // WLEDMM added IRAM_ATTR_YN { if (c2 == 0) return c1; // WLEDMM shortcut if (c1 == 0) return c2; // WLEDMM shortcut @@ -80,35 +81,61 @@ IRAM_ATTR_YN uint32_t color_add(uint32_t c1, uint32_t c2, bool fast) // WLEDMM * if using "video" method the resulting color will never become black unless it is already black */ -IRAM_ATTR_YN __attribute__((hot)) uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) +uint32_t WLED_O2_ATTR IRAM_ATTR_YN __attribute__((hot)) color_fade(uint32_t c1, uint8_t amount, bool video) { - if (amount == 255) return c1; // WLEDMM small optimization - plus it avoids over-fading in "video" mode - if (amount == 0) return 0; // WLEDMM shortcut + if (c1 == 0 || amount == 0) return 0; // black or no change + if (amount == 255) return c1; + uint32_t addRemains = 0; - uint32_t scaledcolor = 0; // color order is: W R G B from MSB to LSB - uint16_t w = W(c1); // WLEDMM 16bit to make sure the compiler uses 32bit (not 64bit) for the math - uint16_t r = R(c1); - uint16_t g = G(c1); - uint16_t b = B(c1); - if (video) { - uint16_t scale = amount; // 32bit for faster calculation - // bugfix: doing "+1" after shifting is obviously wrong - // optimization: ((r && scale) ? 1 : 0) can be simplified to "if (r > 0) +1" ; if we arive here, then scale != 0 and scale < 255 - if (w>0) scaledcolor |= (((w * scale) >> 8) +1) << 24; // WLEDMM small speedup when no white channel - if (r>0) scaledcolor |= (((r * scale) >> 8) +1) << 16; - if (g>0) scaledcolor |= (((g * scale) >> 8) +1) << 8; - if (b>0) scaledcolor |= ((b * scale) >> 8) +1; - return scaledcolor; - } - else { - uint16_t scale = 1 + amount; - if (w>0) scaledcolor |= ((w * scale) >> 8) << 24; // WLEDMM small speedup when no white channel - scaledcolor |= ((r * scale) >> 8) << 16; - scaledcolor |= (g * scale) & 0x0000FF00; // WLEDMM faster than right-left shift "" >>8 ) <<8" - scaledcolor |= (b * scale) >> 8; - return scaledcolor; + if (!video) amount++; // add one for correct scaling using bitshifts + else { + // video scaling: make sure colors do not dim to zero if they started non-zero unless they distort the hue + uint8_t r = byte(c1>>16), g = byte(c1>>8), b = byte(c1), w = byte(c1>>24); // extract r, g, b, w channels + uint8_t maxc = (r > g) ? ((r > b) ? r : b) : ((g > b) ? g : b); // determine dominant channel for hue preservation + addRemains = r && (r<<5) > maxc ? 0x00010000 : 0; // note: setting color preservation threshold too high results in flickering and + addRemains |= g && (g<<5) > maxc ? 0x00000100 : 0; // jumping colors in low brightness gradients. Multiplying the color preserves + addRemains |= b && (b<<5) > maxc ? 0x00000001 : 0; // better accuracy than dividing the maxc. Shifting by 5 is a good compromise + addRemains |= w ? 0x01000000 : 0; // i.e. remove color channel if <13% of max } + const uint32_t TWO_CHANNEL_MASK = 0x00FF00FF; + uint32_t rb = (((c1 & TWO_CHANNEL_MASK) * amount) >> 8) & TWO_CHANNEL_MASK; // scale red and blue + uint32_t wg = (((c1 >> 8) & TWO_CHANNEL_MASK) * amount) & ~TWO_CHANNEL_MASK; // scale white and green + return (rb | wg) + addRemains; } + +// +// overwrite FastLed colorFromPalette with an optimized version created by dedehai (https://github.com/Aircoookie/WLED/pull/4138) +// +// 1:1 replacement of fastled function optimized for ESP, slightly faster, more accurate and uses less flash (~ -200bytes) +CRGB IRAM_ATTR_YN __attribute__((hot)) ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t brightness, TBlendType blendType) +{ + if (blendType == LINEARBLEND_NOWRAP) { + index = (index*240) >> 8; // Blend range is affected by lo4 blend of values, remap to avoid wrapping + } + unsigned hi4 = byte(index) >> 4; + const CRGB* entry = (CRGB*)((uint8_t*)(&(pal[0])) + (hi4 * sizeof(CRGB))); + unsigned red1 = entry->r; + unsigned green1 = entry->g; + unsigned blue1 = entry->b; + if (blendType != NOBLEND) { + if (hi4 == 15) entry = &(pal[0]); + else ++entry; + unsigned f2 = ((index & 0x0F) << 4) + 1; // +1 so we scale by 256 as a max value, then result can just be shifted by 8 + unsigned f1 = (257 - f2); // f2 is 1 minimum, so this is 256 max + red1 = (red1 * f1 + (unsigned)entry->r * f2) >> 8; + green1 = (green1 * f1 + (unsigned)entry->g * f2) >> 8; + blue1 = (blue1 * f1 + (unsigned)entry->b * f2) >> 8; + } + if (brightness < 255) { // note: zero checking could be done to return black but that is hardly ever used so it is omitted + uint32_t scale = brightness + 1; // adjust for rounding (bitshift) + red1 = (red1 * scale) >> 8; + green1 = (green1 * scale) >> 8; + blue1 = (blue1 * scale) >> 8; + } + return RGBW32(red1,green1,blue1,0); + //return CRGB(red1,green1,blue1); +} + #endif void setRandomColor(byte* rgb) @@ -142,25 +169,37 @@ void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) //hue, sat to rgb //get RGB values from color temperature in K (https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html) void colorKtoRGB(uint16_t kelvin, byte* rgb) //white spectrum to rgb, calc { + // WLEDMM safe exit (do nothing) to avoid logf domain errors. argument to logf must be >= 1.0f to avoid bad result 0 or -inf; + // kelvin >65k might be a signed/unsigned conversion error + if ((kelvin < 1200) || (kelvin > 65000)) { + rgb[0] = 255; + rgb[1] = 255; + rgb[2] = 255; + rgb[3] = 0; + return; + } + int r = 0, g = 0, b = 0; - float temp = kelvin / 100.0f; + float temp = float(kelvin) / 100.0f; // WLEDMM "float()" added - to make sure its done in float, not in double or int if (temp <= 66.0f) { r = 255; g = roundf(99.4708025861f * logf(temp) - 161.1195681661f); if (temp <= 19.0f) { b = 0; } else { - b = roundf(138.5177312231f * logf((temp - 10.0f)) - 305.0447927307f); + b = roundf(138.5177312231f * logf((temp - 10.0f)) - 305.0447927307f); // safe because temp > 19.0f } } else { + // temp-60.0f is always > 0 here (since temp>66) r = roundf(329.698727446f * powf((temp - 60.0f), -0.1332047592f)); g = roundf(288.1221695283f * powf((temp - 60.0f), -0.0755148492f)); b = 255; } //g += 12; //mod by Aircoookie, a bit less accurate but visibly less pinkish - rgb[0] = (uint8_t) constrain(r, 0, 255); - rgb[1] = (uint8_t) constrain(g, 0, 255); - rgb[2] = (uint8_t) constrain(b, 0, 255); + // WLEDMM min(max()) is faster than constrain() + rgb[0] = (uint8_t) min(max(r, 0), 255); + rgb[1] = (uint8_t) min(max(g, 0), 255); + rgb[2] = (uint8_t) min(max(b, 0), 255); rgb[3] = 0; } @@ -308,22 +347,9 @@ static float maxf (float v, float w) // WLEDMM better use standard library fmax } #endif -// adjust RGB values based on color temperature in K (range [2800-10200]) (https://en.wikipedia.org/wiki/Color_balance) -// called from bus manager when color correction is enabled! -uint32_t __attribute__((hot)) IRAM_ATTR_YN colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb) // WLEDMM: IRAM_ATTR_YN -{ - //remember so that slow colorKtoRGB() doesn't have to run for every setPixelColor() - static byte correctionRGB[4] = {0,0,0,0}; - static uint16_t lastKelvin = 0; - if (lastKelvin != kelvin) colorKtoRGB(kelvin, correctionRGB); // convert Kelvin to RGB - lastKelvin = kelvin; - byte rgbw[4]; - rgbw[0] = ((uint16_t) correctionRGB[0] * R(rgb)) /255; // correct R - rgbw[1] = ((uint16_t) correctionRGB[1] * G(rgb)) /255; // correct G - rgbw[2] = ((uint16_t) correctionRGB[2] * B(rgb)) /255; // correct B - rgbw[3] = W(rgb); - return RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3]); -} + +// WLEDMM colorBalanceFromKelvin moved into bus_manager.cpp for better optimization + //approximates a Kelvin color temperature from an RGB color. //this does no check for the "whiteness" of the color, @@ -366,7 +392,7 @@ uint16_t approximateKelvinFromRGB(uint32_t rgb) { #if !defined(WLED_USE_CIE_BRIGHTNESS_TABLE) //gamma 2.8 lookup table used for color correction -static byte gammaT[256] = { +byte DRAM_ATTR_YN gammaT[256] = { // WLEDMM: DRAM_ATTR to ensure that this table is in RAM (faster) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, @@ -389,7 +415,7 @@ static byte gammaT[256] = { // https://github.com/Aircoookie/WLED/issues/2767#issuecomment-1310961308 // unfortunately NeoPixelBus has its own internal table, that kills low brightness values similar to the original WLED table. // see https://github.com/Makuna/NeoPixelBus/blob/master/src/internal/NeoGamma.h -static const byte gammaT[256] = { +const DRAM_ATTR_YN byte gammaT[256] = { // WLEDMM make sure this table is in RAM (faster) 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, @@ -410,24 +436,24 @@ static const byte gammaT[256] = { #endif // WLEDMM begin -static uint8_t gammaTinv[256] = { 0 }; +uint8_t DRAM_ATTR_YN gammaTinv[256] = { 0 }; static void calcInvGammaTable(float gamma) { float gammaInv = 1.0f / 2.4f; // surprise surprise: WLED palettes use a fixed gamma of 2.4 !!! //float gammaInv = 1.0f / gamma; // if we go by the book, 1.0/gamma will revert gamma corrections - for (size_t i = 0; i < 256; i++) { - gammaTinv[i] = (int)(powf((float)i / 255.0f, gammaInv) * 255.0f + 0.5f); + for (size_t i = 1; i < 256; i++) { + gammaTinv[i] = (int)(powf(((float)i - 0.5f) / 255.0f, gammaInv) * 255.0f + 0.5f); // improved by @dedehai } + gammaTinv[0]=0; + gammaTinv[255]=255; } -uint8_t __attribute__((hot)) unGamma8(uint8_t value) { - //if (!gammaCorrectCol || (value == 0) || (value == 255)) return value; - if ((value == 0) || (value == 255)) return value; - if ((gammaCorrectVal < 0.999f) || (gammaCorrectVal > 3.0f)) return value; +IRAM_ATTR_YN uint8_t __attribute__((hot)) unGamma8(uint8_t value) { if (gammaTinv[255] == 0) calcInvGammaTable(gammaCorrectVal); + //if ((gammaCorrectVal < 0.999f) || (gammaCorrectVal > 3.0f)) return value; // WLEDMM yes, looks stupid return gammaTinv[value]; } -uint32_t __attribute__((hot)) unGamma24(uint32_t c) { +IRAM_ATTR_YN uint32_t __attribute__((hot)) unGamma24(uint32_t c) { if ((gammaCorrectVal < 0.999f) || (gammaCorrectVal > 3.0f)) return c; if (gammaTinv[255] == 0) calcInvGammaTable(gammaCorrectVal); return RGBW32(gammaTinv[R(c)], gammaTinv[G(c)], gammaTinv[B(c)], W(c)); @@ -436,6 +462,8 @@ uint32_t __attribute__((hot)) unGamma24(uint32_t c) { uint8_t gamma8_cal(uint8_t b, float gamma) { + if (b==0) return 0; + if (b==255) return 255; return (int)(powf((float)b / 255.0f, gamma) * 255.0f + 0.5f); } @@ -443,21 +471,23 @@ uint8_t gamma8_cal(uint8_t b, float gamma) void calcGammaTable(float gamma) { #if !defined(WLED_USE_CIE_BRIGHTNESS_TABLE) // WLEDMM not possible when using the CIE table - for (uint16_t i = 0; i < 256; i++) { + for (uint16_t i = 1; i < 256; i++) { gammaT[i] = gamma8_cal(i, gamma); } + gammaT[0]=0; + gammaT[255]=255; #endif calcInvGammaTable(gamma); // WLEDMM } // used for individual channel or brightness gamma correction -IRAM_ATTR_YN __attribute__((hot)) uint8_t gamma8(uint8_t b) // WLEDMM added IRAM_ATTR_YN +IRAM_ATTR_YN __attribute__((hot)) uint8_t gamma8_slow(uint8_t b) // WLEDMM added IRAM_ATTR_YN { return gammaT[b]; } // used for color gamma correction -uint32_t __attribute__((hot)) gamma32(uint32_t color) +IRAM_ATTR_YN uint32_t __attribute__((hot)) gamma32(uint32_t color) { if (!gammaCorrectCol) return color; uint8_t w = W(color); diff --git a/wled00/const.h b/wled00/const.h index f2247512..e152bc7f 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -91,6 +91,22 @@ #endif #endif +#ifndef WLED_MAX_SEGNAME_LEN + #ifdef ESP8266 + #define WLED_MAX_SEGNAME_LEN 32 + #else + #define WLED_MAX_SEGNAME_LEN 48 // WLEDMM upstream uses 64, but 48 seems to be a good compromise between flexibility and memory needed + #endif +#else + #if WLED_MAX_SEGNAME_LEN<32 + #undef WLED_MAX_SEGNAME_LEN + #define WLED_MAX_SEGNAME_LEN 32 + #else + #warning WLED UI does not support your modified maximum segment name length! + #endif +#endif + + //Usermod IDs #define USERMOD_ID_RESERVED 0 //Unused. Might indicate no usermod present #define USERMOD_ID_UNSPECIFIED 1 //Default value for a general user mod that does not specify a custom ID @@ -149,14 +165,17 @@ #define AP_BEHAVIOR_NO_CONN 1 //Open when no connection (either after boot or if connection is lost) #define AP_BEHAVIOR_ALWAYS 2 //Always open #define AP_BEHAVIOR_BUTTON_ONLY 3 //Only when button pressed for 6 sec - +#define AP_BEHAVIOR_TEMPORARY 4 //Open AP when no connection after boot but only temporary //WLEDMM not yet supported +#ifndef WLED_AP_TIMEOUT + #define WLED_AP_TIMEOUT 300000 //Temporary AP timeout +#endif //Notifier callMode #define CALL_MODE_INIT 0 //no updates on init, can be used to disable updates #define CALL_MODE_DIRECT_CHANGE 1 #define CALL_MODE_BUTTON 2 //default button actions applied to selected segments -#define CALL_MODE_NOTIFICATION 3 -#define CALL_MODE_NIGHTLIGHT 4 -#define CALL_MODE_NO_NOTIFY 5 +#define CALL_MODE_NOTIFICATION 3 //caused by incoming notification (UDP or DMX preset) +#define CALL_MODE_NIGHTLIGHT 4 //nightlight progress +#define CALL_MODE_NO_NOTIFY 5 //change state but do not send notifications (UDP) #define CALL_MODE_FX_CHANGED 6 //no longer used #define CALL_MODE_HUE 7 #define CALL_MODE_PRESET_CYCLE 8 //no longer used @@ -272,6 +291,10 @@ #define COL_ORDER_GBR 5 #define COL_ORDER_MAX 5 +//ESP-NOW //WLEDMM not yet supported +#define ESP_NOW_STATE_UNINIT 0 +#define ESP_NOW_STATE_ON 1 +#define ESP_NOW_STATE_ERROR 2 //Button type #define BTN_TYPE_NONE 0 @@ -283,6 +306,7 @@ #define BTN_TYPE_TOUCH 6 #define BTN_TYPE_ANALOG 7 #define BTN_TYPE_ANALOG_INVERTED 8 +#define BTN_TYPE_TOUCH_SWITCH 9 //WLEDMM not yet supported //Ethernet board types #define WLED_NUM_ETH_TYPES 12 //WLEDMM +1 for Olimex ESP32-Gateway @@ -332,6 +356,7 @@ //Playlist option byte #define PL_OPTION_SHUFFLE 0x01 +#define PL_OPTION_RESTORE 0x02 //WLEDMM not yet supported // Segment capability byte #define SEG_CAPABILITY_RGB 0x01 @@ -341,8 +366,11 @@ // WLED Error modes #define ERR_NONE 0 // All good :) #define ERR_DENIED 1 // Permission denied -#define ERR_EEP_COMMIT 2 // Could not commit to EEPROM (wrong flash layout?) OBSOLETE +#define ERR_CONCURRENCY 2 // Conurrency (client active) //WLEDMM was ERR_EEP_COMMIT (obsolete) #define ERR_NOBUF 3 // JSON buffer was not released in time, request cannot be handled at this time +#define ERR_NOT_IMPL 4 // Not implemented +#define ERR_NORAM_PX 7 // not enough RAM for pixels +#define ERR_NORAM 8 // effect RAM depleted #define ERR_JSON 9 // JSON parsing failed (input too large?) #define ERR_FS_BEGIN 10 // Could not init filesystem (no partition?) #define ERR_FS_QUOTA 11 // The FS is full or the maximum file size is reached @@ -435,7 +463,7 @@ #if !defined(USERMOD_AUDIOREACTIVE) #define SETTINGS_STACK_BUF_SIZE 3834 // WLEDMM added 696+32 bytes of margin (was 3096) #else - #define SETTINGS_STACK_BUF_SIZE 4000 // WLEDMM more buffer for audioreactive UI (add '-D CONFIG_ASYNC_TCP_TASK_STACK_SIZE=9216' to your build_flags) + #define SETTINGS_STACK_BUF_SIZE 4000 // WLEDMM more buffer for audioreactive UI (add '-D CONFIG_ASYNC_TCP_STACK_SIZE=9216' to your build_flags) #endif #endif @@ -497,6 +525,23 @@ #endif #endif +// Web server limits +#ifdef ESP8266 +// Minimum heap to consider handling a request +#define WLED_REQUEST_MIN_HEAP (8*1024) +// Estimated maximum heap required by any one request +#define WLED_REQUEST_HEAP_USAGE (6*1024) +#else +// ESP32 TCP stack needs much more RAM than ESP8266 +// Minimum heap remaining before queuing a request +#define WLED_REQUEST_MIN_HEAP (12*1024) +// Estimated maximum heap required by any one request +#define WLED_REQUEST_HEAP_USAGE (12*1024) +#endif +// Maximum number of requests in queue; absolute cap on web server resource usage. +// Websockets do not count against this limit. +#define WLED_REQUEST_MAX_QUEUE 6 + //#define MIN_HEAP_SIZE (8k for AsyncWebServer) #if !defined(MIN_HEAP_SIZE) #define MIN_HEAP_SIZE 8192 @@ -565,8 +610,18 @@ // error only in MM, not in upstream... tbd: find out why #ifdef ARDUINO_ARCH_ESP32 #define IRAM_ATTR_YN IRAM_ATTR + #define DRAM_ATTR_YN DRAM_ATTR #else #define IRAM_ATTR_YN + #define DRAM_ATTR_YN +#endif + +#define WLED_O2_ATTR __attribute__((optimize("O2"))) + +#if !defined(WLEDMM_SAVE_FLASH) // WLEDMM +#define WLED_O3_ATTR __attribute__((optimize("O3,fast-math"))) +#else +#define WLED_O3_ATTR WLED_O2_ATTR // -O3 increases flash size due to loop unrolling #endif #endif diff --git a/wled00/data/404.htm b/wled00/data/404.htm index 260253a3..bf6cc25e 100644 --- a/wled00/data/404.htm +++ b/wled00/data/404.htm @@ -1,5 +1,5 @@ - + @@ -9,11 +9,14 @@ body { font-family: Verdana, Helvetica, sans-serif; text-align: center; + font-size: 15px; background-color: #222; margin: 0; color: #fff; } - + ul li { + margin-bottom: 12px; + } img { width: 400px; max-width: 50%; @@ -29,11 +32,11 @@ margin: 10px; width: 230px; text-transform: uppercase; - font-family: helvetica; + font-family: helvetica, sans-serif; font-size: 19px; background-color: #333; color: white; - border: 0px solid white; + border: 0 solid white; border-radius: 25px; } @@ -41,7 +44,34 @@

404 Not Found

- Akemi does not know where you are headed...

- - +

+ +
+

+ +

+ Congratulations! You have found the page that exists in the place where no page should exist - a peculiar phenomenon + of the web that continues to baffle researchers.

Akemi has compiled a list of other mysteries that she recommends for further investigation. +

+

+ +

Akemi's Top 10 Mysteries Worth Exploring:

+

+

    + +
  • Dark Matter - Accounts for 85% of the universe's mass yet remains completely invisible. Akemi notes this is considerably more elusive than your car keys.
  • +
  • Dark Energy - An unknown force accelerating the universe's expansion at an alarming rate. Akemi suggests it may simply be late for something important.
  • +
  • The Missing Antimatter - After the Big Bang, matter inexplicably survived while antimatter did not. Akemi calls this "a fortunate asymmetry for carbon-based life forms."
  • +
  • The Origin of Life - How non-living chemistry spontaneously organized itself into living cells. Akemi admits this transition remains "inconveniently unexplained."
  • +
  • Consciousness - The phenomenon by which organized matter becomes aware of itself. How does a bunch of squishy brain stuff create the feeling of being "you"? Philosophy meets neuroscience and nobody wins. Akemi recommends not thinking about this too hard.
  • +
  • The Identity of Satoshi Nakamoto - Creator of Bitcoin who vanished completely from the digital realm. Akemi notes this as "masterful execution of the Irish goodbye."
  • +
  • Sailing Stones of Death Valley - Rocks that traverse the desert floor leaving trails. While ice and wind are implicated, Akemi maintains they're "just showing off."
  • +
  • Anomalous Deep Ocean Acoustics - Unexplained sounds from the ocean depths, including the famous "Bloop." Akemi has no comment, but looks nervous.
  • +
  • Quantum Gravity - The incompatibility of general relativity and quantum mechanics. Akemi cheerfully notes we don't actually understand how reality works.
  • +
  • The Arrow of Time - Why causality flows in only one direction despite physics allowing both. Akemi suggests entropy is "quite insistent about this."
  • +
  • Turritopsis dohrnii - A jellyfish capable of reversing its aging process. Akemi lists this under "Biological Cheat Codes."
  • +
+
+

+ \ No newline at end of file diff --git a/wled00/data/404mini.htm b/wled00/data/404mini.htm new file mode 100644 index 00000000..28044502 --- /dev/null +++ b/wled00/data/404mini.htm @@ -0,0 +1,51 @@ + + + + + + + Not found + + + + +

404 Not Found

+ Akemi does not know where you are headed...

+ +

+ Congratulations! You have found the page that exists in the place where no page should exist – a peculiar phenomenon + of the web that continues to baffle researchers.
+

+ + \ No newline at end of file diff --git a/wled00/data/index.css b/wled00/data/index.css index deb0cc64..1c3a08c5 100644 --- a/wled00/data/index.css +++ b/wled00/data/index.css @@ -707,7 +707,7 @@ img { #wbal .sliderdisplay { background: linear-gradient(90deg, #ff8f1f 0%, #fff 50%, #cbdbff); } /* wrapper divs hidden by default */ -#liveview, #liveview2D, #roverstar, #pql +#liveview, #liveview2D, #roverstar, #pql, #rgbwrap, #swrap, #hwrap, #kwrap, #wwrap, #wbal, #qcs-w, #hexw, .clear-icon, .edit-icon, .ptxt { display: none; diff --git a/wled00/data/index.js b/wled00/data/index.js index bd27f952..68b72209 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -697,6 +697,8 @@ function populateInfo(i) //if (i.ver.includes("0.14.1-b")) vcn = "Fried Chicken"; // final line of "One Vision" by Queen if (i.ver.includes("0.14.3-b")) vcn = "Fried Chicken"; if (i.ver.includes("14.5.")) vcn = "Small Step"; + if (i.ver.includes("14.6.")) vcn = "New Light"; + if (i.ver.includes("14.7.")) vcn = "Next Step"; cn += `v${i.ver}  "${vcn}"

(WLEDMM ${i.rel}.bin)

build ${i.vid}

${urows} @@ -825,7 +827,7 @@ function populateSegments(s) (cfg.comp.segpwr ? segp : '') + `
`+ `` + // - ``+ + ``+ `
`+ ``+ ``+ @@ -1974,6 +1976,9 @@ function readState(s,command=false) case 3: errstr = "Buffer locked!"; break; + case 7: + errstr = "No RAM for pixel buffer!"; + break; case 8: errstr = "Effect RAM depleted!"; break; @@ -3220,7 +3225,7 @@ setInterval(()=>{ gId('heartMM').style.color = `hsl(${hc}, 100%, 50%)`; }, 910); -function openGH() { window.open("https://github.com/Aircoookie/WLED/wiki"); } +function openGH() { window.open("https://mm.kno.wled.ge/"); } var cnfr = false; function cnfReset() diff --git a/wled00/data/settings_2D.htm b/wled00/data/settings_2D.htm index 85f9dddb..fc3e196c 100644 --- a/wled00/data/settings_2D.htm +++ b/wled00/data/settings_2D.htm @@ -9,7 +9,7 @@ var loc = false, locip; var maxPanels=64; var ctx = null; // WLEDMM - function H(){window.open("https://mm.kno.wled.ge/features/2D");} + function H(){window.open("https://mm.kno.wled.ge/WLEDSR/LED-Preferences/");} var ledIndex = 0; // WLEDMM var wasAdvanced = -1; //WLEDMM function B(){window.open("/settings","_self");} diff --git a/wled00/data/settings_dmx.htm b/wled00/data/settings_dmx.htm index 30a62fe5..17fba354 100644 --- a/wled00/data/settings_dmx.htm +++ b/wled00/data/settings_dmx.htm @@ -7,7 +7,7 @@
${isMSeg?'Start X':'Start LED'}