From d298f72727abe36433fcbf77b7de6e03da95f156 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 20 Sep 2025 11:29:54 +0000
Subject: [PATCH 001/170] Initial plan
From 2a01b5862aded9bdbe787e3f6008fc38bf64f26b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 20 Sep 2025 11:31:57 +0000
Subject: [PATCH 002/170] Initial plan
From bb10ec3b75a502dd30365e7964d527d5c20171df Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 20 Sep 2025 11:36:51 +0000
Subject: [PATCH 003/170] Fix ARDUINO_USB_MODE issue: change from 1 to 0 to
allow normal boot
Co-authored-by: netmindz <442066+netmindz@users.noreply.github.com>
---
platformio.ini | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/platformio.ini b/platformio.ini
index 96c06b9e..b5f51b1e 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -455,7 +455,7 @@ build_flags = -g
-D CONFIG_ASYNC_TCP_USE_WDT=0
-D CONFIG_ASYNC_TCP_TASK_STACK_SIZE=9472 ;; WLEDMM increase stack by 1.25Kb, as audioreactive needs bigger SETTINGS_STACK_BUF_SIZE
-DCO
- -DARDUINO_USB_MODE=1 ;; this flag is mandatory for ESP32-C3
+ -DARDUINO_USB_MODE=0 ;; changed from 1 to 0 to allow normal boot without USB debugger
;; 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
@@ -702,7 +702,7 @@ 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 ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=0 ;; changed from 1 to 0 to allow normal boot without USB debugger
; -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
@@ -1907,7 +1907,7 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden
${common_mm.HUB75_build_flags}
${common_mm.animartrix_build_flags}
-D WLED_RELEASE_NAME=esp32S3_4MB_S
- -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; for Serial-to-USB chip
+ -DARDUINO_USB_MODE=0 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; changed from 1 to 0 to allow normal boot without USB debugger
;;-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode
-D 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)
-D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0
@@ -1938,7 +1938,7 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden
; -DBOARD_HAS_PSRAM -D WLED_USE_PSRAM_JSON -D WLED_USE_PSRAM ;; un-comment in case your board supports PSRAM
;; -D WLED_USE_PSRAM_JSON -DALL_JSON_TO_PSRAM ; WLEDMM --> force all JSON stuff into PSRAM; gives more free heap
-D WLED_RELEASE_NAME=esp32S3_8MB_M
- -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; for Serial-to-USB chip
+ -DARDUINO_USB_MODE=0 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; changed from 1 to 0 to allow normal boot without USB debugger
;;-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode
;;-D 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)
-D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0
@@ -1989,7 +1989,7 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden
-DBOARD_HAS_PSRAM ;; -D WLED_USE_PSRAM ;; your board supports PSRAM
-D WLED_USE_PSRAM_JSON -DALL_JSON_TO_PSRAM ; WLEDMM --> force all JSON stuff into PSRAM; gives more free heap
-D WLED_RELEASE_NAME=esp32S3_8MB_PSRAM_M
- -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; for Serial-to-USB chip
+ -DARDUINO_USB_MODE=0 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; changed from 1 to 0 to allow normal boot without USB debugger
;;-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode
;;-D 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)
-D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0
@@ -2027,7 +2027,7 @@ build_unflags = ${env:esp32S3_8MB_M.build_unflags} ;; use the same as "normal"
build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-indentation -Wno-format-truncation
${common_mm.build_flags_S}
-D WLED_RELEASE_NAME=esp32S3_8MB_S
- -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; for Serial-to-USB chip
+ -DARDUINO_USB_MODE=0 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; changed from 1 to 0 to allow normal boot without USB debugger
;;-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode
;;-D 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
@@ -2090,7 +2090,7 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden
-D MAX_SEGMENT_DATA=40960 ;; default 32767
-DBOARD_HAS_PSRAM ;; -D WLED_USE_PSRAM ;; your board supports PSRAM
-D WLED_USE_PSRAM_JSON -DALL_JSON_TO_PSRAM ; WLEDMM --> force all JSON stuff into PSRAM; gives more free heap
- -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; for Serial-to-USB chip
+ -DARDUINO_USB_MODE=0 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; changed from 1 to 0 to allow normal boot without USB debugger
;; -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode
-D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0
;; -DUSERMOD_BATTERY_MEASUREMENT_PIN=2 ;; battery voltage pin
@@ -2134,7 +2134,7 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden
-D WLEDMM_FASTPATH
-DBOARD_HAS_PSRAM ;; -D WLED_USE_PSRAM ;; your board supports PSRAM
-D WLED_USE_PSRAM_JSON -DALL_JSON_TO_PSRAM ; WLEDMM --> force all JSON stuff into PSRAM; gives more free heap
- -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; for Serial-to-USB chip
+ -DARDUINO_USB_MODE=0 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; changed from 1 to 0 to allow normal boot without USB debugger
;;-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode
-D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0
;; -D LEDPIN=38 ;; buildin LED
@@ -2160,7 +2160,7 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden
-D WLED_USE_PSRAM_JSON -DALL_JSON_TO_PSRAM ; WLEDMM --> force all JSON stuff into PSRAM; gives more free heap
-DCONFIG_MBEDTLS_DYNAMIC_BUFFER=1 ;; optional - allows some buffers to use PSRAM
-DLOLIN_WIFI_FIX -DWLEDMM_WIFI_POWERON_HACK ;; seems to work much better with this
- -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Serial-to-USB chip
+ -DARDUINO_USB_MODE=0 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; changed from 1 to 0 to allow normal boot without USB debugger
;;-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode
-D 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
@@ -2205,7 +2205,7 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden
-D WLED_USE_PSRAM_JSON -DALL_JSON_TO_PSRAM ; WLEDMM --> force all JSON stuff into PSRAM; gives more free heap
-DCONFIG_MBEDTLS_DYNAMIC_BUFFER=1 ;; optional - allows some buffers to use PSRAM
-DLOLIN_WIFI_FIX -DWLEDMM_WIFI_POWERON_HACK ;; seems to work much better with this
- -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Serial-to-USB chip
+ -DARDUINO_USB_MODE=0 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; changed from 1 to 0 to allow normal boot without USB debugger
;;-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode
-D WLED_DISABLE_ADALIGHT ;; disables serial protocols - recommended for Hardware-CDC USB (Serial RX will receive junk commands when RX pin is unconnected, unless its pulled down by resistor)
;; ${common_mm.animartrix_build_flags} ;; not enough flash
From 49a1399ed676d4b87eeb3275ecf84b495b1452a1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 20 Sep 2025 11:38:25 +0000
Subject: [PATCH 004/170] Fix AutoPlaylist race condition: use
CALL_MODE_NOTIFICATION for AutoChange presets
Co-authored-by: netmindz <442066+netmindz@users.noreply.github.com>
---
usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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..15a55e27 100644
--- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h
+++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h
@@ -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);
From e094d60552c98583249ccf374b55c842158aaa1f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 20 Sep 2025 12:06:19 +0000
Subject: [PATCH 005/170] Implement conditional ARDUINO_USB_MODE: dev=1,
release=0
Co-authored-by: netmindz <442066+netmindz@users.noreply.github.com>
---
pio-scripts/conditional_usb_mode.py | 73 +++++++++++++++++++++++++++++
platformio.ini | 21 +++++----
2 files changed, 84 insertions(+), 10 deletions(-)
create mode 100644 pio-scripts/conditional_usb_mode.py
diff --git a/pio-scripts/conditional_usb_mode.py b/pio-scripts/conditional_usb_mode.py
new file mode 100644
index 00000000..170eb923
--- /dev/null
+++ b/pio-scripts/conditional_usb_mode.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python3
+"""
+Conditional USB Mode Script for WLED-MM
+
+This script automatically sets 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=0 (allows normal boot without USB debugger)
+
+The script detects release builds by checking for the WLED_RELEASE environment variable
+which is set to True in the GitHub Actions CI workflow.
+"""
+
+Import('env')
+import os
+
+def conditional_usb_mode(env):
+ """
+ Conditionally set ARDUINO_USB_MODE based on build context.
+
+ For ESP32-C3, ESP32-S2, and ESP32-S3 variants:
+ - Development builds: ARDUINO_USB_MODE=1 (default, good for debugging)
+ - Release builds: ARDUINO_USB_MODE=0 (prevents hanging without USB debugger)
+ """
+
+ # 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:
+ print("WLED Release build detected - setting ARDUINO_USB_MODE=0 for production")
+
+ # Find and modify ARDUINO_USB_MODE in build flags
+ build_flags = env.get('BUILD_FLAGS', [])
+ cpp_defines = env.get('CPPDEFINES', [])
+
+ # Look through CPPDEFINES and modify ARDUINO_USB_MODE if found
+ modified = False
+ new_defines = []
+
+ for define in cpp_defines:
+ if isinstance(define, (list, tuple)) and len(define) == 2:
+ if define[0] == 'ARDUINO_USB_MODE' and define[1] == '1':
+ # Change ARDUINO_USB_MODE from 1 to 0 for release builds
+ new_defines.append(('ARDUINO_USB_MODE', '0'))
+ modified = True
+ print(f" Changed ARDUINO_USB_MODE from 1 to 0")
+ else:
+ new_defines.append(define)
+ else:
+ new_defines.append(define)
+
+ if modified:
+ env.Replace(CPPDEFINES=new_defines)
+
+ # Also check raw build flags for -DARDUINO_USB_MODE=1
+ new_build_flags = []
+ for flag in build_flags:
+ if isinstance(flag, str) and 'ARDUINO_USB_MODE=1' in flag:
+ # Replace ARDUINO_USB_MODE=1 with ARDUINO_USB_MODE=0
+ new_flag = flag.replace('ARDUINO_USB_MODE=1', 'ARDUINO_USB_MODE=0')
+ new_build_flags.append(new_flag)
+ modified = True
+ print(f" Modified build flag: {flag} -> {new_flag}")
+ else:
+ new_build_flags.append(flag)
+
+ if modified:
+ env.Replace(BUILD_FLAGS=new_build_flags)
+
+ else:
+ print("Development build detected - keeping ARDUINO_USB_MODE=1 for debugging")
+
+# Apply the conditional USB mode logic
+conditional_usb_mode(env)
\ No newline at end of file
diff --git a/platformio.ini b/platformio.ini
index b5f51b1e..ecdb65ce 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -220,6 +220,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
@@ -455,7 +456,7 @@ build_flags = -g
-D CONFIG_ASYNC_TCP_USE_WDT=0
-D CONFIG_ASYNC_TCP_TASK_STACK_SIZE=9472 ;; WLEDMM increase stack by 1.25Kb, as audioreactive needs bigger SETTINGS_STACK_BUF_SIZE
-DCO
- -DARDUINO_USB_MODE=0 ;; changed from 1 to 0 to allow normal boot without USB debugger
+ -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
@@ -702,7 +703,7 @@ 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=0 ;; changed from 1 to 0 to allow normal boot without USB debugger
+ -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
@@ -1907,7 +1908,7 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden
${common_mm.HUB75_build_flags}
${common_mm.animartrix_build_flags}
-D WLED_RELEASE_NAME=esp32S3_4MB_S
- -DARDUINO_USB_MODE=0 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; changed from 1 to 0 to allow normal boot without USB debugger
+ -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; for Serial-to-USB chip
;;-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode
-D 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)
-D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0
@@ -1938,7 +1939,7 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden
; -DBOARD_HAS_PSRAM -D WLED_USE_PSRAM_JSON -D WLED_USE_PSRAM ;; un-comment in case your board supports PSRAM
;; -D WLED_USE_PSRAM_JSON -DALL_JSON_TO_PSRAM ; WLEDMM --> force all JSON stuff into PSRAM; gives more free heap
-D WLED_RELEASE_NAME=esp32S3_8MB_M
- -DARDUINO_USB_MODE=0 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; changed from 1 to 0 to allow normal boot without USB debugger
+ -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; for Serial-to-USB chip
;;-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode
;;-D 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)
-D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0
@@ -1989,7 +1990,7 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden
-DBOARD_HAS_PSRAM ;; -D WLED_USE_PSRAM ;; your board supports PSRAM
-D WLED_USE_PSRAM_JSON -DALL_JSON_TO_PSRAM ; WLEDMM --> force all JSON stuff into PSRAM; gives more free heap
-D WLED_RELEASE_NAME=esp32S3_8MB_PSRAM_M
- -DARDUINO_USB_MODE=0 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; changed from 1 to 0 to allow normal boot without USB debugger
+ -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; for Serial-to-USB chip
;;-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode
;;-D 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)
-D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0
@@ -2027,7 +2028,7 @@ build_unflags = ${env:esp32S3_8MB_M.build_unflags} ;; use the same as "normal"
build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-indentation -Wno-format-truncation
${common_mm.build_flags_S}
-D WLED_RELEASE_NAME=esp32S3_8MB_S
- -DARDUINO_USB_MODE=0 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; changed from 1 to 0 to allow normal boot without USB debugger
+ -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; for Serial-to-USB chip
;;-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode
;;-D 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
@@ -2090,7 +2091,7 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden
-D MAX_SEGMENT_DATA=40960 ;; default 32767
-DBOARD_HAS_PSRAM ;; -D WLED_USE_PSRAM ;; your board supports PSRAM
-D WLED_USE_PSRAM_JSON -DALL_JSON_TO_PSRAM ; WLEDMM --> force all JSON stuff into PSRAM; gives more free heap
- -DARDUINO_USB_MODE=0 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; changed from 1 to 0 to allow normal boot without USB debugger
+ -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; for Serial-to-USB chip
;; -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode
-D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0
;; -DUSERMOD_BATTERY_MEASUREMENT_PIN=2 ;; battery voltage pin
@@ -2134,7 +2135,7 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden
-D WLEDMM_FASTPATH
-DBOARD_HAS_PSRAM ;; -D WLED_USE_PSRAM ;; your board supports PSRAM
-D WLED_USE_PSRAM_JSON -DALL_JSON_TO_PSRAM ; WLEDMM --> force all JSON stuff into PSRAM; gives more free heap
- -DARDUINO_USB_MODE=0 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; changed from 1 to 0 to allow normal boot without USB debugger
+ -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 ;; for Serial-to-USB chip
;;-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode
-D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0
;; -D LEDPIN=38 ;; buildin LED
@@ -2160,7 +2161,7 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden
-D WLED_USE_PSRAM_JSON -DALL_JSON_TO_PSRAM ; WLEDMM --> force all JSON stuff into PSRAM; gives more free heap
-DCONFIG_MBEDTLS_DYNAMIC_BUFFER=1 ;; optional - allows some buffers to use PSRAM
-DLOLIN_WIFI_FIX -DWLEDMM_WIFI_POWERON_HACK ;; seems to work much better with this
- -DARDUINO_USB_MODE=0 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; changed from 1 to 0 to allow normal boot without USB debugger
+ -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Serial-to-USB chip
;;-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode
-D 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
@@ -2205,7 +2206,7 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-inden
-D WLED_USE_PSRAM_JSON -DALL_JSON_TO_PSRAM ; WLEDMM --> force all JSON stuff into PSRAM; gives more free heap
-DCONFIG_MBEDTLS_DYNAMIC_BUFFER=1 ;; optional - allows some buffers to use PSRAM
-DLOLIN_WIFI_FIX -DWLEDMM_WIFI_POWERON_HACK ;; seems to work much better with this
- -DARDUINO_USB_MODE=0 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; changed from 1 to 0 to allow normal boot without USB debugger
+ -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Serial-to-USB chip
;;-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode
-D WLED_DISABLE_ADALIGHT ;; disables serial protocols - recommended for Hardware-CDC USB (Serial RX will receive junk commands when RX pin is unconnected, unless its pulled down by resistor)
;; ${common_mm.animartrix_build_flags} ;; not enough flash
From 8067f1f70c9c773b60b4b4ccf376488052209fa0 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Mon, 20 Oct 2025 21:49:09 +0200
Subject: [PATCH 006/170] bus.setPicelColor optimization: move
colorBalanceFromKelvin into busmanager.cpp
colorBalanceFromKelvin() is only called from inside bus_manager.cpp, so we can help the compiler optimize by making it a local (static) fuction
---
wled00/bus_manager.cpp | 18 ++++++++++++++++++
wled00/colors.cpp | 3 +++
wled00/fcn_declare.h | 2 +-
3 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp
index e4e70507..73072e97 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) {
diff --git a/wled00/colors.cpp b/wled00/colors.cpp
index 37241959..8e72d46b 100644
--- a/wled00/colors.cpp
+++ b/wled00/colors.cpp
@@ -310,6 +310,8 @@ static float maxf (float v, float w) // WLEDMM better use standard library fmax
// adjust RGB values based on color temperature in K (range [2800-10200]) (https://en.wikipedia.org/wiki/Color_balance)
// called from bus manager when color correction is enabled!
+#if 0
+// WLEDMM moved into bus_manager.cpp for better optimization
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()
@@ -324,6 +326,7 @@ uint32_t __attribute__((hot)) IRAM_ATTR_YN colorBalanceFromKelvin(uint16_t kelvi
rgbw[3] = W(rgb);
return RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3]);
}
+#endif
//approximates a Kelvin color temperature from an RGB color.
//this does no check for the "whiteness" of the color,
diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h
index 1a9247a2..188c10ee 100644
--- a/wled00/fcn_declare.h
+++ b/wled00/fcn_declare.h
@@ -66,7 +66,7 @@ void colorXYtoRGB(float x, float y, byte* rgb); // only defined if huesync disab
void colorRGBtoXY(byte* rgb, float* xy); // only defined if huesync disabled TODO
void colorFromDecOrHexString(byte* rgb, char* in);
bool colorFromHexString(byte* rgb, const char* in);
-uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
+//uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb); // WLEDMM function moved into bus_manager.cpp for better optimization
uint16_t __attribute__((const)) approximateKelvinFromRGB(uint32_t rgb); // WLEDMM: added attribute const
void setRandomColor(byte* rgb);
uint8_t gamma8_cal(uint8_t b, float gamma);
From 6e8ab94ee9d102c4a5d780eef2a8c9339639c33d Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Mon, 20 Oct 2025 21:53:37 +0200
Subject: [PATCH 007/170] colorKtoRGB optimization and bugfix
* bug: logf(temp-10) result becomes NaN when kelvin < 1200
* bug (RISC-V only): parameter of Bus::setCCT must be signed, to avoid undefined behaviour
* minor optimization by replacing constrain() with min(max())
---
wled00/bus_manager.h | 2 +-
wled00/colors.cpp | 22 +++++++++++++++++-----
2 files changed, 18 insertions(+), 6 deletions(-)
diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h
index 04062215..e49238d6 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) {
diff --git a/wled00/colors.cpp b/wled00/colors.cpp
index 8e72d46b..36cb1b4b 100644
--- a/wled00/colors.cpp
+++ b/wled00/colors.cpp
@@ -142,25 +142,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;
}
From 541b2018558fd00afec6f7dc09fca11a1d124433 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Mon, 20 Oct 2025 21:58:23 +0200
Subject: [PATCH 008/170] bus.setPicelColor optimization: optimizing some CPU
cycles out of the hot path in sPC and gPC
* optimize loops that scan through all busses
* small speedups for Bus::autoWhiteCalc()
* small speedups for ColorOrderMap::getPixelColorOrder()
thanks to github Copilot for giving me the right ideas for this optimization
---
wled00/bus_manager.cpp | 107 ++++++++++++++++++++++++-----------------
wled00/bus_manager.h | 7 +--
2 files changed, 67 insertions(+), 47 deletions(-)
diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp
index 73072e97..d843f4e6 100644
--- a/wled00/bus_manager.cpp
+++ b/wled00/bus_manager.cpp
@@ -114,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);
}
@@ -1231,7 +1240,7 @@ uint32_t BusManager::memUsage(BusConfig &bc) {
int BusManager::add(BusConfig &bc) {
if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1;
// WLEDMM clear cached Bus info first
- lastend = 0;
+ lastlen = 0;
laststart = 0;
lastBus = nullptr;
slowMode = false;
@@ -1272,7 +1281,7 @@ void BusManager::removeAll() {
// WLEDMM clear cached Bus info
lastBus = nullptr;
laststart = 0;
- lastend = 0;
+ lastlen = 0;
slowMode = false;
}
@@ -1293,24 +1302,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
@@ -1334,46 +1347,52 @@ 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 {
+ 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;
}
- return b->getPixelColor(pix - bstart);
+ 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 {
+ uint_fast16_t blen = b->getLength();
+
+ if ((uint_fast16_t)(pix - bstart) < blen) { // Unsigned arithmetic trick for 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;
}
return b->getPixelColorRestored(pix - bstart);
}
diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h
index e49238d6..ea886eaf 100644
--- a/wled00/bus_manager.h
+++ b/wled00/bus_manager.h
@@ -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 {
From e6b15a1c84df9a0c04cc97c4d11878de9dc81feb Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Thu, 23 Oct 2025 01:18:18 +0200
Subject: [PATCH 009/170] Add .vscode/extensions.json to .gitignore
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
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
From e6876caba1bea95d9025607563d56f7feccd5a6e Mon Sep 17 00:00:00 2001
From: MoonModules <91013628+MoonModules@users.noreply.github.com>
Date: Thu, 23 Oct 2025 19:24:45 +0200
Subject: [PATCH 010/170] Update GitHub links to MM-Effects repository
WLED-Effects repo has been renamed to MM-Effects.
ArtiFX links to this repo so updated in artifx.js
---
usermods/artifx/artifx.js | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
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
+}
From 1f99aeb5067bced846903048b6b7e9e66458d086 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Fri, 24 Oct 2025 00:05:44 +0200
Subject: [PATCH 011/170] small optimization: always allow gPC to use cached
busses
previously gPC cached busses were disabled in slowMode (one pixel mapped to several outputs). However this should not be needed, because the first copy found should be like all other.
---
wled00/bus_manager.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp
index d843f4e6..ddd53483 100644
--- a/wled00/bus_manager.cpp
+++ b/wled00/bus_manager.cpp
@@ -1361,12 +1361,12 @@ uint32_t IRAM_ATTR __attribute__((hot)) BusManager::getPixelColor(uint_fast16_t
uint_fast16_t blen = b->getLength();
if ((uint_fast16_t)(pix - bstart) < blen) { // Unsigned arithmetic trick for fast range check
- if (!slowMode) {
+ //if (!slowMode) {
// Cache bus info for next call
lastBus = b;
laststart = bstart;
lastlen = blen;
- }
+ //}
return b->getPixelColor(pix - bstart); // done - found one
}
}
@@ -1388,12 +1388,12 @@ uint32_t IRAM_ATTR __attribute__((hot)) BusManager::getPixelColorRestored(uint_
uint_fast16_t blen = b->getLength();
if ((uint_fast16_t)(pix - bstart) < blen) { // Unsigned arithmetic trick for range check
- if (!slowMode) {
+ //if (!slowMode) {
// Cache bus info for next call
lastBus = b;
laststart = bstart;
lastlen = blen;
- }
+ //}
return b->getPixelColorRestored(pix - bstart);
}
}
From 6ce663784d431f293fde39cded0e7fa9d4e5dcc0 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Fri, 24 Oct 2025 16:24:33 +0200
Subject: [PATCH 012/170] bugfix to prevent random corruption of presets.json
accroding to the docs of setBufferSize() and svbuf():
> setBufferSize() has to be called right after opening file before any other operation!
so changing buffer sizes on an already opened and partially read file can cause file corruption as the internal buffer is exchanged "mid-flight".
Fix: move all f.setBufferSize() so they appear directly after f.open().
---
wled00/file.cpp | 25 ++++++++++++-------------
1 file changed, 12 insertions(+), 13 deletions(-)
diff --git a/wled00/file.cpp b/wled00/file.cpp
index 39813ba0..43458a6b 100644
--- a/wled00/file.cpp
+++ b/wled00/file.cpp
@@ -18,7 +18,7 @@
* 1. File must be a string representation of a valid JSON object
* 2. File must have '{' as first character
* 3. There must not be any additional characters between a root-level key and its value object (e.g. space, tab, newline)
- * 4. There must not be any characters between an root object-separating ',' and the next object key string
+ * 4. There must not be any characters between a root object-separating ',' and the next object key string
* 5. There may be any number of spaces, tabs, and/or newlines before such object-separating ','
* 6. There must not be more than 5 consecutive spaces at any point except for those permitted in condition 5
* 7. If it is desired to delete the first usable object (e.g. preset file), a dummy object '"0":{}' is inserted at the beginning.
@@ -66,9 +66,6 @@ static bool bufferedFind(const char *target, bool fromStart = true) {
size_t index = 0;
byte buf[FS_BUFSIZE];
- #if ESP_IDF_VERSION_MAJOR >= 4
- f.setBufferSize(FS_BUFSIZE);
- #endif
if (fromStart) f.seek(0);
while (f.position() < f.size() -1) {
@@ -110,9 +107,6 @@ static bool bufferedFindSpace(size_t targetLen, bool fromStart = true) {
size_t index = 0; // better to use size_t instead if uint16_t
byte buf[FS_BUFSIZE];
- #if ESP_IDF_VERSION_MAJOR >= 4
- f.setBufferSize(FS_BUFSIZE);
- #endif
if (fromStart) f.seek(0);
while (f.position() < f.size() -1) {
@@ -156,9 +150,6 @@ static bool bufferedFindObjectEnd() {
uint16_t objDepth = 0; //num of '{' minus num of '}'. return once 0
//size_t start = f.position();
byte buf[FS_BUFSIZE];
- #if ESP_IDF_VERSION_MAJOR >= 4
- f.setBufferSize(FS_BUFSIZE);
- #endif
while (f.position() < f.size() -1) {
size_t bufsize = f.read(buf, FS_BUFSIZE); // better to use size_t instead of uint16_t
size_t count = 0;
@@ -183,9 +174,6 @@ static void writeSpace(size_t l)
{
byte buf[FS_BUFSIZE];
memset(buf, ' ', FS_BUFSIZE);
- #if ESP_IDF_VERSION_MAJOR >= 4
- f.setBufferSize(FS_BUFSIZE);
- #endif
while (l > 0) {
size_t block = (l>FS_BUFSIZE) ? FS_BUFSIZE : l;
f.write(buf, block);
@@ -299,6 +287,9 @@ bool writeObjectToFile(const char* file, const char* key, JsonDocument* content)
DEBUGFS_PRINTLN(F("Failed to open!"));
return false;
}
+ #if ESP_IDF_VERSION_MAJOR >= 4
+ f.setBufferSize(FS_BUFSIZE); // reduced internal buffer leads to shorter blocking delay, and might prevent LED glitches
+ #endif
if (!bufferedFind(key)) //key does not exist in file
{
@@ -366,6 +357,10 @@ bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest)
if (!f) return false;
else { DEBUG_PRINTF(PSTR("FILE '%s' open to read, size %d bytes\n"), file, (int)f.size());} // WLEDMM additional debug message
+ #if ESP_IDF_VERSION_MAJOR >= 4
+ f.setBufferSize(FS_BUFSIZE*2); // reduced internal buffer leads to shorter blocking delay, and might prevent LED glitches
+ #endif
+
if (key != nullptr && !bufferedFind(key)) //key does not exist in file
{
f.close();
@@ -444,6 +439,10 @@ static const uint8_t *getPresetCache(size_t &size) {
if (!presetsCached) {
File file = WLED_FS.open("/presets.json", "r");
+
+ #if ESP_IDF_VERSION_MAJOR >= 4
+ if (file) f.setBufferSize(FS_BUFSIZE*2); // reduced internal buffer leads to shorter blocking delay, and might prevent LED glitches
+ #endif
if (file) {
presetsCachedTime = presetsModifiedTime;
presetsCachedValidate = cacheInvalidate;
From 80a6866f16dd5d7ce38c7d0e58daf3cc1f5f3fb1 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Fri, 24 Oct 2025 16:35:11 +0200
Subject: [PATCH 013/170] small bugfix for previous commit (PSRAM caching)
---
wled00/file.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/wled00/file.cpp b/wled00/file.cpp
index 43458a6b..d3727d16 100644
--- a/wled00/file.cpp
+++ b/wled00/file.cpp
@@ -441,7 +441,7 @@ static const uint8_t *getPresetCache(size_t &size) {
File file = WLED_FS.open("/presets.json", "r");
#if ESP_IDF_VERSION_MAJOR >= 4
- if (file) f.setBufferSize(FS_BUFSIZE*2); // reduced internal buffer leads to shorter blocking delay, and might prevent LED glitches
+ if (file) file.setBufferSize(FS_BUFSIZE*2); // reduced internal buffer leads to shorter blocking delay, and might prevent LED glitches
#endif
if (file) {
presetsCachedTime = presetsModifiedTime;
From 1af94dc9a22d418ec16d14bf85aa894b45622b88 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Fri, 24 Oct 2025 19:11:06 +0200
Subject: [PATCH 014/170] add error message when effects cannot be added
---
wled00/FX.cpp | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index 138e9690..88626635 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -11773,18 +11773,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() {
From 22d142942ae531054a5e1577990f1a4d25f13d81 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Fri, 24 Oct 2025 19:15:20 +0200
Subject: [PATCH 015/170] build 2510241, version 14.7.0-dev
ongoing development
---
package-lock.json | 4 ++--
package.json | 2 +-
wled00/data/index.js | 2 ++
wled00/wled.h | 2 +-
4 files changed, 6 insertions(+), 4 deletions(-)
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..0e0d78d7 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": {
diff --git a/wled00/data/index.js b/wled00/data/index.js
index 2c1f143a..29cd854a 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}
diff --git a/wled00/wled.h b/wled00/wled.h
index 442473da..ffd179fd 100644
--- a/wled00/wled.h
+++ b/wled00/wled.h
@@ -7,7 +7,7 @@
*/
// version code in format yymmddb (b = daily build)
-#define VERSION 2501170
+#define VERSION 2510241
// WLEDMM - you can check for this define in usermods, to only enabled WLEDMM specific code in the "right" fork. Its not defined in AC WLED.
#define _MoonModules_WLED_
From 60878305fb866eb8ebcd66c6a94285bfdb0b265f Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Sat, 25 Oct 2025 00:53:25 +0200
Subject: [PATCH 016/170] debug code updates & fixes
better output when using WLED_DEBUG_HEAP without WLED_DEBUG
---
wled00/FXparticleSystem.cpp | 2 +-
wled00/wled.cpp | 10 +++++-----
wled00/wled.h | 2 +-
wled00/wled00.ino | 13 +++++++------
4 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/wled00/FXparticleSystem.cpp b/wled00/FXparticleSystem.cpp
index 9e5162c8..908a1d46 100644
--- a/wled00/FXparticleSystem.cpp
+++ b/wled00/FXparticleSystem.cpp
@@ -1789,7 +1789,7 @@ uint32_t calculateNumberOfParticles1D(const uint32_t fraction, const bool isadva
numberofParticles = numberofParticles < 20 ? 20 : numberofParticles; // 20 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;
}
diff --git a/wled00/wled.cpp b/wled00/wled.cpp
index 47826138..1842d6ad 100644
--- a/wled00/wled.cpp
+++ b/wled00/wled.cpp
@@ -382,13 +382,13 @@ void WLED::loop()
#endif
#ifdef WLED_DEBUG_HEAP
if (millis() - debugTime > 4999 ) { // WLEDMM: Special case for debugging heap faster
- DEBUG_PRINT(F("*** Free heap: ")); DEBUG_PRINT(heap_caps_get_free_size(0x1800));
- DEBUG_PRINT(F("\tLargest free block: ")); DEBUG_PRINT(heap_caps_get_largest_free_block(0x1800));
- DEBUG_PRINT(F(" *** \t\tArduino min free stack: ")); DEBUG_PRINT(uxTaskGetStackHighWaterMark(NULL));
+ USER_PRINT(F("*** Free heap: ")); USER_PRINT(heap_caps_get_free_size(0x1800));
+ USER_PRINT(F("\tLargest free block: ")); USER_PRINT(heap_caps_get_largest_free_block(0x1800));
+ USER_PRINT(F(" *** \t\tArduino min free stack: ")); USER_PRINT(uxTaskGetStackHighWaterMark(NULL));
#if INCLUDE_xTaskGetHandle
- DEBUG_PRINT(F(" TCP min free stack: ")); DEBUG_PRINT(wledmm_get_tcp_stacksize());
+ USER_PRINT(F(" TCP min free stack: ")); USER_PRINT(wledmm_get_tcp_stacksize());
#endif
- DEBUG_PRINTLN(F(" ***"));
+ USER_PRINTLN(F(" ***"));
debugTime = millis();
}
#endif // WLED_DEBUG_HEAP
diff --git a/wled00/wled.h b/wled00/wled.h
index ffd179fd..dd1ba6da 100644
--- a/wled00/wled.h
+++ b/wled00/wled.h
@@ -880,7 +880,7 @@ WLED_GLOBAL volatile uint8_t jsonBufferLock _INIT(0);
#endif
// debug macro variable definitions
-#ifdef WLED_DEBUG
+#if defined(WLED_DEBUG) || defined(WLED_DEBUG_HEAP)
WLED_GLOBAL unsigned long debugTime _INIT(0);
WLED_GLOBAL int lastWifiState _INIT(3);
WLED_GLOBAL unsigned long wifiStateChangedTime _INIT(0);
diff --git a/wled00/wled00.ino b/wled00/wled00.ino
index 83d3f959..087607ab 100644
--- a/wled00/wled00.ino
+++ b/wled00/wled00.ino
@@ -76,19 +76,20 @@ void setup() {
void loop() __attribute__((used)); // needed for -flto
void loop() {
//WLEDMM show loops per second
-#ifdef WLED_DEBUG
+#if defined(WLED_DEBUG) || defined(WLED_DEBUG_HEAP)
loopCounter++;
//if (millis() - lastMillis >= 10000) {
if (millis() - lastMillis >= 8000) {
long delta = millis() - lastMillis;
- if (delta > 0) {
+ if ((delta > 0) && (loopCounter > 0)) {
lps = (loopCounter*1000U) / delta;
//if (delta > (showtime / 1000)) lps2 = (loopCounter*1000U) / (delta - (showtime / 1000));
- USER_PRINTF("%lu lps\t", lps);
- USER_PRINTF("%u fps\t", strip.getFps());
+ USER_PRINTF("%3lu lps %5.1fms \t", lps, float(delta) / float(loopCounter));
+ USER_PRINTF("%3u fps\t\t", strip.getFps());
//USER_PRINTF("%lu lps without show\t\t", lps2);
- USER_PRINTF("target frametime %dms\t", int(strip.getFrameTime()));
- USER_PRINTF("target FPS %d\n", int(strip.getTargetFps()));
+ //USER_PRINTF("target frametime %dms\t", int(strip.getFrameTime()));
+ //USER_PRINTF("target FPS %d", int(strip.getTargetFps()));
+ USER_PRINTLN("");
}
lastMillis = millis();
loopCounter = 0;
From f78edc43dcab1cbc67c6c6cf6ebe405dd54c92a0 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Sat, 25 Oct 2025 16:52:42 +0200
Subject: [PATCH 017/170] bugfix for #272 (only affects rotary usermod)
Refactor name copying logic to avoid use-after-free issue.
---
wled00/util.cpp | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/wled00/util.cpp b/wled00/util.cpp
index c31b20b6..a913feed 100644
--- a/wled00/util.cpp
+++ b/wled00/util.cpp
@@ -340,10 +340,14 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL
strncpy_P(dest, tmpstr, maxLen); // copy the name into buffer (replacing previous)
dest[maxLen-1] = '\0';
} else {
- if (nameEnd<0) tmpstr = names.substring(nameBegin).c_str(); // did not find ",", last name?
- else tmpstr = names.substring(nameBegin, nameEnd).c_str();
- strlcpy(dest, tmpstr, maxLen); // copy the name into buffer (replacing previous)
- }
+ // WLEDMM bugfix for WLED-MM #272
+ // names.substring(...).c_str() returns a pointer to a temporary; it’s invalid by the next statement. Added result buffer "sub" to avoid use-after-free
+ // if (nameEnd<0) tmpstr = names.substring(nameBegin).c_str(); // did not find ",", last name?
+ // else tmpstr = names.substring(nameBegin, nameEnd).c_str();
+ // strlcpy(dest, tmpstr, maxLen); // copy the name into buffer (replacing previous)
+ String sub = (nameEnd<0) ? names.substring(nameBegin) : names.substring(nameBegin, nameEnd);
+ strlcpy(dest, sub.c_str(), maxLen); // copy the name into buffer (replacing previous)
+ }
}
nameBegin = nameEnd+1; // next name (if "," is not found it will be 0)
} // next slider
From 12b3bedba78d53fd3fe1bbee7e5c3d7f7db2671c Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Sat, 25 Oct 2025 16:55:52 +0200
Subject: [PATCH 018/170] cleanup - removed old code in comments
---
wled00/util.cpp | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/wled00/util.cpp b/wled00/util.cpp
index a913feed..8ae062ae 100644
--- a/wled00/util.cpp
+++ b/wled00/util.cpp
@@ -342,10 +342,7 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL
} else {
// WLEDMM bugfix for WLED-MM #272
// names.substring(...).c_str() returns a pointer to a temporary; it’s invalid by the next statement. Added result buffer "sub" to avoid use-after-free
- // if (nameEnd<0) tmpstr = names.substring(nameBegin).c_str(); // did not find ",", last name?
- // else tmpstr = names.substring(nameBegin, nameEnd).c_str();
- // strlcpy(dest, tmpstr, maxLen); // copy the name into buffer (replacing previous)
- String sub = (nameEnd<0) ? names.substring(nameBegin) : names.substring(nameBegin, nameEnd);
+ String sub = (nameEnd<0) ? names.substring(nameBegin) : names.substring(nameBegin, nameEnd); // special handling in case we did not find "," (last name)
strlcpy(dest, sub.c_str(), maxLen); // copy the name into buffer (replacing previous)
}
}
From 8fbaf63565fb8a77cb09d8301745fe1be4c003cf Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Sat, 25 Oct 2025 19:57:59 +0200
Subject: [PATCH 019/170] decodeIRJson bugfix: avoid buffer overrun #272
Increase the size of objKey to accommodate 32bit code number.
The previous buffer size was too small.
---
wled00/ir.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/wled00/ir.cpp b/wled00/ir.cpp
index bfb79264..c3d2c531 100644
--- a/wled00/ir.cpp
+++ b/wled00/ir.cpp
@@ -673,7 +673,7 @@ Sample:
*/
void decodeIRJson(uint32_t code)
{
- char objKey[10];
+ char objKey[16] = {'\0'}; // WLEDMM: 13 chars, not 9! '"0x' + 'FFFFFFFF' + '":' + '\0'
String cmdStr;
JsonObject fdo;
JsonObject jsonCmdObj;
From f373044e77a7656093a65a4ae64b7f8c4a5a459c Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Sat, 25 Oct 2025 20:30:55 +0200
Subject: [PATCH 020/170] Update repository link to upstream
---
readme.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/readme.md b/readme.md
index a44a82b3..37258c3e 100644
--- a/readme.md
+++ b/readme.md
@@ -14,7 +14,7 @@
-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).
From d856749bea3de2dfaab9a6ebf5ebddf7a83fbc80 Mon Sep 17 00:00:00 2001
From: Damian Schneider
Date: Thu, 24 Jul 2025 18:30:51 +0200
Subject: [PATCH 021/170] fix change that got lost
---
wled00/FX.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index 88626635..bff8ac3f 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -9744,7 +9744,7 @@ uint16_t mode_particleimpact(void) {
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
}
From 86dda32d11a8a023fe03f9ff0890e92478c86d71 Mon Sep 17 00:00:00 2001
From: Will Miles
Date: Wed, 30 Jul 2025 22:17:44 -0400
Subject: [PATCH 022/170] Update AsyncWebServer and AsyncTCP
This should fix (or at least improve) some of the crash cases under
excessive web server load.
---
platformio.ini | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/platformio.ini b/platformio.ini
index 96c06b9e..a1b8e5b0 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -251,7 +251,7 @@ lib_deps =
;; 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.1
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
@@ -373,7 +373,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.6
makuna/NeoPixelBus @ 2.7.5
;; makuna/NeoPixelBus @ 2.7.9 ;; experimental
${common_mm.HUB75_lib_deps}
@@ -404,7 +404,7 @@ build_flags = -g
; -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.6
makuna/NeoPixelBus @ 2.7.5
;; makuna/NeoPixelBus @ 2.7.9 ;; experimental
${env.lib_deps}
@@ -437,7 +437,7 @@ build_flags = -g
;; ARDUINO_USB_CDC_ON_BOOT
lib_deps =
- https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
+ esp32async/AsyncTCP @ 3.4.6
;; makuna/NeoPixelBus @ 2.7.5 ;; standard
makuna/NeoPixelBus @ 2.7.9 ;; experimental - reduces LED glitches on -S2
${env.lib_deps}
@@ -460,7 +460,7 @@ build_flags = -g
;; ARDUINO_USB_CDC_ON_BOOT
lib_deps =
- https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
+ esp32async/AsyncTCP @ 3.4.6
makuna/NeoPixelBus @ 2.7.5
;; makuna/NeoPixelBus @ 2.7.9 ;; experimental
${env.lib_deps}
@@ -484,7 +484,7 @@ build_flags = -g
;; 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.6
makuna/NeoPixelBus @ 2.7.5
;; makuna/NeoPixelBus @ 2.7.9 ;; experimental
${env.lib_deps}
From 832c0c4bf4fa6625f8abac74d1dbbb1de6a7a776 Mon Sep 17 00:00:00 2001
From: Will Miles
Date: Sat, 2 Aug 2025 15:49:47 -0400
Subject: [PATCH 023/170] Update to AsyncTCP 3.4.7
Bugfix on 3.4.6
---
platformio.ini | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/platformio.ini b/platformio.ini
index a1b8e5b0..1aaaeb0c 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -251,7 +251,7 @@ lib_deps =
;; 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.1
+ 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
@@ -373,7 +373,7 @@ build_flagsV4 = -g
;;; V4.4.x libraries (without LOROL_LITTLEFS; with newer NeoPixelBus)
lib_depsV4 =
- esp32async/AsyncTCP @ 3.4.6
+ esp32async/AsyncTCP @ 3.4.7
makuna/NeoPixelBus @ 2.7.5
;; makuna/NeoPixelBus @ 2.7.9 ;; experimental
${common_mm.HUB75_lib_deps}
@@ -404,7 +404,7 @@ build_flags = -g
; -D WLEDMM_SLOWPATH ;; don't use I2S for LED bus
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
lib_deps =
- esp32async/AsyncTCP @ 3.4.6
+ esp32async/AsyncTCP @ 3.4.7
makuna/NeoPixelBus @ 2.7.5
;; makuna/NeoPixelBus @ 2.7.9 ;; experimental
${env.lib_deps}
@@ -437,7 +437,7 @@ build_flags = -g
;; ARDUINO_USB_CDC_ON_BOOT
lib_deps =
- esp32async/AsyncTCP @ 3.4.6
+ 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}
@@ -460,7 +460,7 @@ build_flags = -g
;; ARDUINO_USB_CDC_ON_BOOT
lib_deps =
- esp32async/AsyncTCP @ 3.4.6
+ esp32async/AsyncTCP @ 3.4.7
makuna/NeoPixelBus @ 2.7.5
;; makuna/NeoPixelBus @ 2.7.9 ;; experimental
${env.lib_deps}
@@ -484,7 +484,7 @@ build_flags = -g
;; 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 =
- esp32async/AsyncTCP @ 3.4.6
+ esp32async/AsyncTCP @ 3.4.7
makuna/NeoPixelBus @ 2.7.5
;; makuna/NeoPixelBus @ 2.7.9 ;; experimental
${env.lib_deps}
From b215c8209dd9376f9b9376af330f3ff5cfa15756 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Sat, 25 Oct 2025 21:48:55 +0200
Subject: [PATCH 024/170] post-merge updates
CONFIG_ASYNC_TCP_TASK_STACK_SIZE => CONFIG_ASYNC_TCP_STACK_SIZE
---
platformio.ini | 5 +++++
usermods/audioreactive/audio_reactive.h | 6 +++---
wled00/const.h | 2 +-
3 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/platformio.ini b/platformio.ini
index 1aaaeb0c..f5e96051 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -366,6 +366,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
@@ -399,6 +400,7 @@ 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
@@ -430,6 +432,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 !
@@ -454,6 +457,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=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:
@@ -479,6 +483,7 @@ 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:
diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h
index 0be1bd79..cf34b547 100644
--- a/usermods/audioreactive/audio_reactive.h
+++ b/usermods/audioreactive/audio_reactive.h
@@ -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
diff --git a/wled00/const.h b/wled00/const.h
index f2247512..3257bc62 100644
--- a/wled00/const.h
+++ b/wled00/const.h
@@ -435,7 +435,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
From 82ba119402adef3febfcf23be601e02b21829fd8 Mon Sep 17 00:00:00 2001
From: Damian Schneider
Date: Sun, 12 Oct 2025 15:18:48 +0200
Subject: [PATCH 025/170] fix low brightness gradient "jumpyness"
during testing at low brightness I noticed that gradients can be "jumping" in colors quite wildly, turning a smooth gradient into a flickering mess. This is due to the color hue preservation being inaccurate and a bit too aggressive. This can be seen for example using a gradient palette and "Running" FX.
Removing the hue preservation completely fixes it but leaves color artefacts for example visible in PS Fire at very low brightness: the bright part of the flames gets a pink hue. This change is a compromise to fix both problems to a "good enough" state
---
wled00/colorTools.hpp | 40 ++++++++++++++++------------------------
wled00/colors.cpp | 43 +++++++++++++++++--------------------------
2 files changed, 33 insertions(+), 50 deletions(-)
diff --git a/wled00/colorTools.hpp b/wled00/colorTools.hpp
index c47f1934..9d600b3e 100644
--- a/wled00/colorTools.hpp
+++ b/wled00/colorTools.hpp
@@ -100,32 +100,24 @@ inline __attribute__((hot,const)) uint32_t color_add(uint32_t c1, uint32_t c2, b
inline __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..07d4020f 100644
--- a/wled00/colors.cpp
+++ b/wled00/colors.cpp
@@ -82,34 +82,25 @@ IRAM_ATTR_YN uint32_t color_add(uint32_t c1, uint32_t c2, bool fast) // WLEDMM
IRAM_ATTR_YN __attribute__((hot)) uint32_t 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;
+ 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
}
- 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;
- }
-}
-#endif
+ 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;
+}#endif
void setRandomColor(byte* rgb)
{
From 9968ed9a22d5e31b24ae3b0b11f4babceb9ab68c Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Sat, 25 Oct 2025 22:17:13 +0200
Subject: [PATCH 026/170] fix merge error
---
wled00/colors.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/wled00/colors.cpp b/wled00/colors.cpp
index 07d4020f..23151ecb 100644
--- a/wled00/colors.cpp
+++ b/wled00/colors.cpp
@@ -100,7 +100,8 @@ IRAM_ATTR_YN __attribute__((hot)) uint32_t color_fade(uint32_t c1, uint8_t amoun
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;
-}#endif
+}
+#endif
void setRandomColor(byte* rgb)
{
From 4eb9f0855dcd72ebd76b706673c2b574e2f1744e Mon Sep 17 00:00:00 2001
From: Damian Schneider
Date: Sun, 7 Sep 2025 20:34:57 +0200
Subject: [PATCH 027/170] Merge pull request #4913 from
DedeHai/percentFX_UI_fix
fix ancient UI bug that hides the speed slider in percent FX
---
wled00/FX.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index bff8ac3f..ae2d4a1a 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -4102,7 +4102,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;!,!;!";
/*
From 4713f26bb86aa11058580c552f0468ada4976a47 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Sat, 25 Oct 2025 23:43:44 +0200
Subject: [PATCH 028/170] additional constants to improve upstream
compatibility
* a few new error constants
* WLED_O2_ATTR - ask the compiler for stronger optimization of a single function
* WLED_O3_ATTR (WLEDMM) optimize even more
---
wled00/FX_fcn.cpp | 4 ++--
wled00/FXparticleSystem.cpp | 8 +++----
wled00/colors.cpp | 6 ++---
wled00/const.h | 47 +++++++++++++++++++++++++++++++++----
wled00/data/index.js | 3 +++
5 files changed, 54 insertions(+), 14 deletions(-)
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 4941529f..688596eb 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -938,7 +938,7 @@ static void xyFromBlock(uint16_t &x,uint16_t &y, uint16_t i, uint16_t vW, uint16
}
-void IRAM_ATTR_YN __attribute__((hot)) Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATTR conditionally
+void 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
@@ -1212,7 +1212,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
diff --git a/wled00/FXparticleSystem.cpp b/wled00/FXparticleSystem.cpp
index 908a1d46..cd537e8c 100644
--- a/wled00/FXparticleSystem.cpp
+++ b/wled00/FXparticleSystem.cpp
@@ -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;
@@ -1494,7 +1494,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 +1658,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
diff --git a/wled00/colors.cpp b/wled00/colors.cpp
index 23151ecb..379afecf 100644
--- a/wled00/colors.cpp
+++ b/wled00/colors.cpp
@@ -8,7 +8,7 @@
/*
* color blend function
*/
-IRAM_ATTR_YN __attribute__((hot)) uint32_t color_blend(uint32_t color1, uint32_t color2, uint_fast16_t blend, bool b16) {
+uint32_t WLED_O2_ATTR IRAM_ATTR __attribute__((hot)) color_blend(uint32_t color1, uint32_t color2, uint_fast16_t blend, bool b16) {
if ((color1 == color2) || (blend == 0)) return color1; // WLEDMM
const uint_fast16_t blendmax = b16 ? 0xFFFF : 0xFF;
if(blend >= blendmax) return color2;
@@ -46,7 +46,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 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,7 +80,7 @@ IRAM_ATTR_YN uint32_t color_add(uint32_t c1, uint32_t c2, bool fast) // WLEDMM
* if using "video" method the resulting color will never become black unless it is already black
*/
-IRAM_ATTR_YN __attribute__((hot)) uint32_t color_fade(uint32_t c1, uint8_t amount, bool video)
+uint32_t IRAM_ATTR_YN __attribute__((hot)) color_fade(uint32_t c1, uint8_t amount, bool video)
{
if (c1 == 0 || amount == 0) return 0; // black or no change
if (amount == 255) return c1;
diff --git a/wled00/const.h b/wled00/const.h
index 3257bc62..00facc76 100644
--- a/wled00/const.h
+++ b/wled00/const.h
@@ -149,14 +149,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 +275,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 +290,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 +340,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 +350,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
@@ -497,6 +509,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
@@ -569,4 +598,12 @@
#define IRAM_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/index.js b/wled00/data/index.js
index 29cd854a..3581442d 100644
--- a/wled00/data/index.js
+++ b/wled00/data/index.js
@@ -1976,6 +1976,9 @@ function readState(s,command=false)
case 3:
errstr = "Buffer locked!";
break;
+ case 7:
+ errstr = "No RAM for buffer!";
+ break;
case 8:
errstr = "Effect RAM depleted!";
break;
From 0071677340601dbee8690318bcc4f5141bdecc5a Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Sat, 25 Oct 2025 23:59:29 +0200
Subject: [PATCH 029/170] (experimental) tweak optimization level of color
functions
---
wled00/colorTools.hpp | 9 +++++----
wled00/colors.cpp | 7 ++++---
2 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/wled00/colorTools.hpp b/wled00/colorTools.hpp
index 9d600b3e..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,7 +99,7 @@ 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 (c1 == 0 || amount == 0) return 0; // black or no change
if (amount == 255) return c1;
diff --git a/wled00/colors.cpp b/wled00/colors.cpp
index 379afecf..9b5a03d3 100644
--- a/wled00/colors.cpp
+++ b/wled00/colors.cpp
@@ -8,8 +8,9 @@
/*
* color blend function
*/
-uint32_t WLED_O2_ATTR IRAM_ATTR __attribute__((hot)) 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;
@@ -80,7 +81,7 @@ uint32_t WLED_O2_ATTR 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
*/
-uint32_t IRAM_ATTR_YN __attribute__((hot)) 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 (c1 == 0 || amount == 0) return 0; // black or no change
if (amount == 255) return c1;
From 3e99bb8643444736fa686c49e7d3881c4ed9efe4 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Sun, 26 Oct 2025 00:35:06 +0200
Subject: [PATCH 030/170] Fix fast-math attribute syntax in const.h
a fatal typo
---
wled00/const.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/wled00/const.h b/wled00/const.h
index 00facc76..bf0e62ad 100644
--- a/wled00/const.h
+++ b/wled00/const.h
@@ -601,7 +601,7 @@
#define WLED_O2_ATTR __attribute__((optimize("O2")))
#if !defined(WLEDMM_SAVE_FLASH) // WLEDMM
-#define WLED_O3_ATTR __attribute__((optimize("O3,fast_math")))
+#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
From 56380ce04c4620fdd069ef433b60387a2c930891 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Sun, 26 Oct 2025 01:41:30 +0200
Subject: [PATCH 031/170] slightly faster to keep IRAM_ATTR for sPC and
color_add
---
wled00/FX_fcn.cpp | 2 +-
wled00/colors.cpp | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 688596eb..e3c30c68 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -938,7 +938,7 @@ static void xyFromBlock(uint16_t &x,uint16_t &y, uint16_t i, uint16_t vW, uint16
}
-void WLED_O2_ATTR __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
diff --git a/wled00/colors.cpp b/wled00/colors.cpp
index 9b5a03d3..41028092 100644
--- a/wled00/colors.cpp
+++ b/wled00/colors.cpp
@@ -47,7 +47,7 @@ uint32_t WLED_O3_ATTR IRAM_ATTR_YN __attribute__((hot)) color_blend(uint32_t col
* color add function that preserves ratio
* idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule
*/
-uint32_t WLED_O2_ATTR 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
From bb84f859242f55c3a9f6d4817490877574cc631c Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Mon, 27 Oct 2025 01:12:30 +0100
Subject: [PATCH 032/170] FxParticlesystem Fix memory calculation of
framebuffer size #275
Updated memory calculation for CRGB to use virtual width and height. Somehow virtualLength is not reliable, so use vWidth * vHeight
---
wled00/FXparticleSystem.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/wled00/FXparticleSystem.cpp b/wled00/FXparticleSystem.cpp
index cd537e8c..d5342f04 100644
--- a/wled00/FXparticleSystem.cpp
+++ b/wled00/FXparticleSystem.cpp
@@ -1103,7 +1103,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));
@@ -1938,4 +1938,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))
From 3752409b46d6d4f3333619d892d0e7d4985ce006 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Mon, 27 Oct 2025 11:24:12 +0100
Subject: [PATCH 033/170] no more ORANGE on effect memory failure
For maintainers of custom effects: replace "return mode_solid()" with "return mode_oops()"
Effect errors fall back to Akemi (2D) or Rainbow(1D) instead of SOLID ORANGE.
---
wled00/FX.cpp | 379 +++++++++++++++++++++++--------------------
wled00/FX_fcn.cpp | 4 +-
wled00/data/index.js | 2 +-
3 files changed, 207 insertions(+), 178 deletions(-)
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index ae2d4a1a..63a1f154 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -118,8 +118,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_oops();
+ 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 +358,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 +833,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 +1048,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 +1081,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 +1111,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 +1246,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 +1278,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 +1353,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 +1468,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 +1597,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 +1805,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 +1842,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 +1941,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 +1990,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 +2141,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 +2211,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 +2445,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 +2527,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 +2567,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 +2605,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 +2663,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 +2719,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 +2728,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 +2893,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 +3003,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 +3059,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 +3145,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 +3230,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 +3329,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 +3339,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 +3554,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 +3565,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 +3704,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 +3721,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 +3858,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 +3977,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
@@ -4259,7 +4286,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 +4400,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 +4499,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 +4552,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()
@@ -4667,7 +4694,7 @@ static const char _data_FX_MODE_WASHING_MACHINE[] PROGMEM = "Washing Machine@!,!
*/
uint16_t mode_image(void) {
#ifndef WLED_ENABLE_GIF
- return mode_static();
+ return mode_oops();
#else
renderImageToSegment(SEGMENT);
return FRAMETIME;
@@ -4686,7 +4713,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 +4763,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 +4978,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 +5033,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 +5096,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 +5137,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 +5191,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 +5236,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 +5285,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 +5339,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 +5374,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 +5464,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 +5686,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 +5798,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 +5830,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 +5954,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 +5996,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 +6072,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 +6131,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 +6154,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 +6200,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 +6276,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 +6304,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 +6336,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 +6376,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 +6429,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 +6469,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 +6517,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 +6540,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 +6592,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 +6610,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 +6682,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 +6697,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 +6788,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();
@@ -6867,7 +6894,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 +6996,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 +7075,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 +7118,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 +7175,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 +7223,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 +7274,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 +7557,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 +7669,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 +8073,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 +8267,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 +8276,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 +8376,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 +8466,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 +8481,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 +8538,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 +8593,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 +8714,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 +8726,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 +8799,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();
@@ -8803,10 +8832,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)
@@ -8959,12 +8988,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 +9051,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 +9075,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 +9170,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 +9184,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));
@@ -9288,7 +9317,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 +9338,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
@@ -9358,14 +9387,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);
@@ -9449,7 +9478,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 +9486,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 +9550,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 +9571,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 +9621,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 +9630,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 +9702,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 +9713,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,7 +9767,7 @@ 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
@@ -9753,7 +9782,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 +9877,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 +9898,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 +9970,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 +9983,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 +10055,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
@@ -10103,7 +10132,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 +10148,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 +10201,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 +10214,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 +10280,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 +10291,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 +10363,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 +10372,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 +10468,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 +10479,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 +10578,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 +10587,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 +10696,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 +10816,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 +10887,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 +11012,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 +11020,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 +11065,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 +11144,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 +11152,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 +11240,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 +11249,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 +11302,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 +11374,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 +11439,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 +11448,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 +11542,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 +11632,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
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index e3c30c68..7a270949 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -249,7 +249,7 @@ bool Segment::allocateData(size_t len) {
Segment::addUsedSegmentData(len);
_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;
}
@@ -1844,7 +1844,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();
diff --git a/wled00/data/index.js b/wled00/data/index.js
index 3581442d..5d08a3f8 100644
--- a/wled00/data/index.js
+++ b/wled00/data/index.js
@@ -1977,7 +1977,7 @@ function readState(s,command=false)
errstr = "Buffer locked!";
break;
case 7:
- errstr = "No RAM for buffer!";
+ errstr = "No RAM for pixel buffer!";
break;
case 8:
errstr = "Effect RAM depleted!";
From c896768c0b4bff79cb1c3ca9ee64f283a188dc78 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Mon, 27 Oct 2025 11:43:23 +0100
Subject: [PATCH 034/170] small correction
mode_oops() should not call mode_oops()
---
wled00/FX.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index 63a1f154..93d29c17 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -135,7 +135,7 @@ static uint16_t mode_oops(void) {
strip._colors_t[1] = BLUE;
strip._colors_t[2] = GREEN;
errorFlag = ERR_NORAM_PX;
- if (SEGLEN <= 1) return mode_oops();
+ if (SEGLEN <= 1) return mode_static();
const uint16_t width = SEGMENT.is2D() ? SEGMENT.virtualWidth() : SEGMENT.virtualLength();
const uint16_t height = SEGMENT.virtualHeight();
From b3f1c5d06cc6aaa97f6820be918246183096bdd0 Mon Sep 17 00:00:00 2001
From: Damian Schneider
Date: Mon, 27 Oct 2025 19:28:50 +0100
Subject: [PATCH 035/170] adding fixes and improvements from AC
---
wled00/FXparticleSystem.cpp | 75 +++++++++++++++++--------------------
wled00/FXparticleSystem.h | 37 +++++++++++-------
2 files changed, 58 insertions(+), 54 deletions(-)
diff --git a/wled00/FXparticleSystem.cpp b/wled00/FXparticleSystem.cpp
index cd537e8c..73de6a2b 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
}
}
@@ -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;
@@ -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
}
}
@@ -1775,18 +1770,12 @@ 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));
@@ -1794,13 +1783,7 @@ uint32_t calculateNumberOfParticles1D(const uint32_t fraction, const bool isadva
}
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;
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)
From c4f6d3d2cb463bcc51dd70b6eb0b2c5d58147b4c Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Mon, 27 Oct 2025 19:34:51 +0100
Subject: [PATCH 036/170] ParticleFX better defaults for 64 pixel height
change default sliders of PS Fireworks, PS Volcano and PS GEQ 2D for better visibility on 64x64 panel
---
wled00/FX.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index 93d29c17..f25de8c4 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -9299,7 +9299,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
@@ -9373,7 +9373,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
@@ -9427,7 +9427,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)
}
@@ -10116,7 +10116,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
From 74c701b7459f8aed27769d440a43368c84786705 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Mon, 27 Oct 2025 21:19:40 +0100
Subject: [PATCH 037/170] HUB75: no color temperature correction for
performance reasons
Enable building color correction code if you really need it.
The effect of color correction is less noticeable on HUB75 than on ws2812b, however it can reduce your framerate by up to 10%.
---
wled00/bus_manager.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp
index e4e70507..a3806b00 100644
--- a/wled00/bus_manager.cpp
+++ b/wled00/bus_manager.cpp
@@ -1059,8 +1059,9 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh
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 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) {
CRGB fastled_col = CRGB(c);
if (_ledBuffer[pix] != fastled_col) {
From 13331e3de01caa21c28988a0253c1ba28d879c73 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Fri, 31 Oct 2025 11:09:45 +0100
Subject: [PATCH 038/170] const WS2812FX::setPixelColorXY
small optimization for speed
---
wled00/FX.h | 8 ++++----
wled00/FX_2Dfcn.cpp | 4 ++--
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/wled00/FX.h b/wled00/FX.h
index 7559b09e..fedca84a 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -1095,12 +1095,12 @@ class WS2812FX { // 96 bytes
void
setUpMatrix(),
- setPixelColorXY_fast(int x, int y, uint32_t c),
- setPixelColorXY(int x, int y, uint32_t c);
+ setPixelColorXY_fast(int x, int y, uint32_t c) const,
+ setPixelColorXY(int x, int y, uint32_t c) const;
// 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)); }
+ 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, RGBW32(c.r,c.g,c.b,0)); }
uint32_t
getPixelColorXY(uint16_t, uint16_t) const;
diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp
index 247f166e..dd2b280a 100644
--- a/wled00/FX_2Dfcn.cpp
+++ b/wled00/FX_2Dfcn.cpp
@@ -195,7 +195,7 @@ void WS2812FX::setUpMatrix() {
}
// 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
+void IRAM_ATTR __attribute__((hot)) WS2812FX::setPixelColorXY_fast(int x, int y, uint32_t col) const //WLEDMM: IRAM_ATTR conditionally
{
uint_fast16_t index = y * Segment::maxWidth + x;
if (index < customMappingSize) index = customMappingTable[index];
@@ -204,7 +204,7 @@ void IRAM_ATTR __attribute__((hot)) WS2812FX::setPixelColorXY_fast(int x, int y,
}
// absolute matrix version of setPixelColor()
-void IRAM_ATTR_YN WS2812FX::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM: IRAM_ATTR conditionally
+void IRAM_ATTR_YN WS2812FX::setPixelColorXY(int x, int y, uint32_t col) const //WLEDMM: IRAM_ATTR conditionally
{
#ifndef WLED_DISABLE_2D
if (!isMatrix) return; // not a matrix set-up
From 96c32443187152b106ed51224c3a98ca7f60ded8 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Sat, 1 Nov 2025 13:30:51 +0100
Subject: [PATCH 039/170] removing deactivated code
---
wled00/colors.cpp | 22 +++-------------------
1 file changed, 3 insertions(+), 19 deletions(-)
diff --git a/wled00/colors.cpp b/wled00/colors.cpp
index 36cb1b4b..336a8e22 100644
--- a/wled00/colors.cpp
+++ b/wled00/colors.cpp
@@ -320,25 +320,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!
-#if 0
-// WLEDMM moved into bus_manager.cpp for better optimization
-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]);
-}
-#endif
+
+// 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,
From 297a2c2b9cf3e5e1ced32b5195e44882586bc1e2 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Sat, 1 Nov 2025 14:16:47 +0100
Subject: [PATCH 040/170] bring back lost function ColorFromPaletteWLED
(WLEDMM_SAVE_FLASH only)
this change was somehow lost - ColorFromPaletteWLED existed in colorTools.hpp (for fast inline) but was missing in colors.cpp (WLEDMM_SAVE_FLASH fallback).
---
wled00/colors.cpp | 34 ++++++++++++++++++++++++++++++++++
wled00/fcn_declare.h | 7 +++++++
2 files changed, 41 insertions(+)
diff --git a/wled00/colors.cpp b/wled00/colors.cpp
index f741e7a6..f1bbea0a 100644
--- a/wled00/colors.cpp
+++ b/wled00/colors.cpp
@@ -102,6 +102,40 @@ uint32_t WLED_O2_ATTR IRAM_ATTR_YN __attribute__((hot)) color_fade(uint32_t c1,
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)
diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h
index 188c10ee..c5e95604 100644
--- a/wled00/fcn_declare.h
+++ b/wled00/fcn_declare.h
@@ -51,9 +51,16 @@ bool getJsonValue(const JsonVariant& element, DestType& destination, const Defau
//colors.cpp
#if !defined(ARDUINO_ARCH_ESP32) || !defined(WLEDMM_FASTPATH) || defined(WLEDMM_SAVE_FLASH) // WLEDMM: color utils moved into colorTools.hpp, so the compiler may inline these functions (faster)
+#if !defined(FASTLED_VERSION) // pull in FastLED if we don't have it yet (we need the CRGB type)
+ #define FASTLED_INTERNAL
+ #include
+#endif
uint32_t __attribute__((const)) color_blend(uint32_t,uint32_t,uint_fast16_t,bool b16=false); // WLEDMM: added attribute const
uint32_t __attribute__((const)) color_add(uint32_t,uint32_t, bool fast=false); // WLEDMM: added attribute const
uint32_t __attribute__((const)) color_fade(uint32_t c1, uint8_t amount, bool video=false);
+#undef ColorFromPalette // overwrite any existing override
+CRGB __attribute__((hot,const)) ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t brightness=255, TBlendType blendType=LINEARBLEND);
+#define ColorFromPalette ColorFromPaletteWLED // override fastled function
#else
#include "colorTools.hpp"
#endif
From 40064490ad39b161077572fe64e7f393fb4b37f8 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Sat, 1 Nov 2025 16:50:20 +0100
Subject: [PATCH 041/170] strip level setPixelColor and getPixelColor
optimization
* moved sPC and gPC functions out of their .cpp files into FX.h, so the compiler can optimize better.
depending of effect used, this gives a 2% up to 8% speedup.
---
wled00/FX.h | 101 ++++++++++++++++++++++++++++++++++++++------
wled00/FX_2Dfcn.cpp | 46 +++-----------------
wled00/FX_fcn.cpp | 21 ++-------
3 files changed, 97 insertions(+), 71 deletions(-)
diff --git a/wled00/FX.h b/wled00/FX.h
index fedca84a..327c6476 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -21,6 +21,11 @@ bool strip_uses_global_leds(void) __attribute__((pure)); // WLEDMM implemented
#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
@@ -957,7 +962,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 +972,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, c.red, c.green, c.blue); }
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 +1028,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,18 +1122,64 @@ class WS2812FX { // 96 bytes
std::vector panel;
#endif
- void
- setUpMatrix(),
- setPixelColorXY_fast(int x, int y, uint32_t c) const,
- setPixelColorXY(int x, int y, uint32_t c) const;
+ 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) const { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline
inline void setPixelColorXY(int x, int y, CRGB c) const { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); }
- uint32_t
- getPixelColorXY(uint16_t, uint16_t) const;
-
// end 2D support
void loadCustomPalettes(void); // loads custom palettes from JSON
@@ -1121,8 +1196,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 dd2b280a..a8545113 100644
--- a/wled00/FX_2Dfcn.cpp
+++ b/wled00/FX_2Dfcn.cpp
@@ -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) 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, 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) 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, 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
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 7a270949..eb0c97e8 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -1969,26 +1969,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,
From d5dee6f58a27a66a1f687d748672c644bbe70bd4 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Sat, 1 Nov 2025 17:06:58 +0100
Subject: [PATCH 042/170] small segment level setPixelColor and getPixelColor
optimization
replaced CRGB -> RGBW32 conversion with FastLED native ``uint32_t(c) & 0x00FFFFFF`` operator. `
`& 0x00FFFFFF`` is needed to cut out the "alpha" channel, because the fastLED operator return RGBA not RGBW.
--> slightly faster
---
wled00/FX.h | 44 ++++++++++++++++++++++----------------------
1 file changed, 22 insertions(+), 22 deletions(-)
diff --git a/wled00/FX.h b/wled00/FX.h
index 327c6476..c52c8869 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -678,10 +678,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);
@@ -689,10 +689,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);
@@ -784,21 +784,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);
@@ -807,19 +807,19 @@ 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
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
@@ -827,19 +827,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) {}
@@ -973,7 +973,7 @@ class WS2812FX { // 96 bytes
// outsmart the compiler :) by correctly overloading
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, c.red, c.green, c.blue); }
+ 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; }
@@ -1178,7 +1178,7 @@ class WS2812FX { // 96 bytes
// outsmart the compiler :) by correctly overloading
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, RGBW32(c.r,c.g,c.b,0)); }
+ inline void setPixelColorXY(int x, int y, CRGB c) const { setPixelColorXY(x, y, uint32_t(c) & 0x00FFFFFF); }
// end 2D support
From 0510fd308f9f7605007c994c3a3bda9b91108b64 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Sun, 2 Nov 2025 16:33:10 +0100
Subject: [PATCH 043/170] HUB75: small delay at cleanup, to ensure that DMA
finishes before deleting the driver
based on a suggestion by @DedeHai
---
wled00/bus_manager.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp
index d2404915..a9b479b2 100644
--- a/wled00/bus_manager.cpp
+++ b/wled00/bus_manager.cpp
@@ -1174,6 +1174,7 @@ void BusHub75Matrix::cleanup() {
#else
USER_PRINTLN("HUB75 output paused.");
#endif
+ delay(30); // give some time to finish DMA
_valid = false;
deallocatePins();
From d9e542663634812e9136b66bd1d53f187979d33a Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Sun, 2 Nov 2025 16:38:03 +0100
Subject: [PATCH 044/170] HUB75: small correction of order
first set _valid=false, then perform delay.
---
wled00/bus_manager.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp
index a9b479b2..eaa46caa 100644
--- a/wled00/bus_manager.cpp
+++ b/wled00/bus_manager.cpp
@@ -1174,9 +1174,9 @@ void BusHub75Matrix::cleanup() {
#else
USER_PRINTLN("HUB75 output paused.");
#endif
- delay(30); // give some time to finish DMA
_valid = false;
+ delay(30); // give some time to finish DMA
deallocatePins();
_len = 0;
//if (fourScanPanel != nullptr) delete fourScanPanel; // warning: deleting object of polymorphic class type 'VirtualMatrixPanel' which has non-virtual destructor might cause undefined behavior
From 2c62cd5e4118d3e96b735826c6251fb888839d67 Mon Sep 17 00:00:00 2001
From: netmindz
Date: Sat, 14 Jun 2025 21:17:01 +0100
Subject: [PATCH 045/170] Merge pull request #4690 from Arcitec/ar-agc-control
AR: add compile-time flag for "Automatic Gain Control" option
---
usermods/audioreactive/audio_reactive.h | 14 +++++++++-----
usermods/audioreactive/readme.md | 5 +++--
2 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h
index cf34b547..406ae316 100644
--- a/usermods/audioreactive/audio_reactive.h
+++ b/usermods/audioreactive/audio_reactive.h
@@ -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
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.
From 69f9499e257929ae4837f72d1e13ffe0e9c9e9b7 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Tue, 4 Nov 2025 16:39:02 +0100
Subject: [PATCH 046/170] CSS bugfix: #pql and #rgbwrap should be hidden
initially
a missing comma caused unexpected UI behaviour in some corner cases.
---
wled00/data/index.css | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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;
From bb44dd861634a980a2988c6c910bc3efda08e562 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Tue, 4 Nov 2025 20:28:26 +0100
Subject: [PATCH 047/170] robustness improvements for image presets
prevent errors / crashes when drawing "i" images from a preset.
- prevent out-of-bounds writing (segment smaller than image)
- make sure that segment properties are cached correctly
- always do "show" when strip was triggered (avoids lost frames)
---
wled00/FX_2Dfcn.cpp | 4 ++--
wled00/FX_fcn.cpp | 7 ++++---
wled00/json.cpp | 10 +++++++++-
3 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp
index a8545113..9491e134 100644
--- a/wled00/FX_2Dfcn.cpp
+++ b/wled00/FX_2Dfcn.cpp
@@ -249,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
@@ -311,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;
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index eb0c97e8..75d7c828 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -945,8 +945,7 @@ void IRAM_ATTR_YN WLED_O2_ATTR __attribute__((hot)) Segment::setPixelColor(int i
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()) {
@@ -1914,7 +1913,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 +1949,8 @@ void WS2812FX::service() {
}
_segment_index++;
}
+ if (_triggered) doShow = true; // WLEDMM "triggered" always means "show"
+
_virtualSegmentLength = 0;
busses.setSegmentCCT(-1);
if(doShow) {
diff --git a/wled00/json.cpp b/wled00/json.cpp
index c51deefa..f9f9f5c4 100644
--- a/wled00/json.cpp
+++ b/wled00/json.cpp
@@ -329,6 +329,8 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
uint8_t oldMap1D2D = seg.map1D2D;
seg.map1D2D = M12_Pixels; // no mapping
// WLEDMM begin - we need to init segment caches before putting any pixels
+ auto oldLock = suspendStripService; // remember pevious lock status
+ suspendStripService = true;
if (strip.isServicing()) {
USER_PRINTLN(F("deserializeSegment() image: strip is still drawing effects."));
strip.waitUntilIdle();
@@ -350,6 +352,8 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
start = 0, stop = 0;
set = 0; //0 nothing set, 1 start set, 2 range set
+ seg.startFrame(); // WLEDMM do it again, to be sure that all segment properties get cached
+ unsigned seg_len = seg.calc_virtualLength(); // WLEDMM prevent out-of-bounds writing
for (size_t i = 0; i < iarr.size(); i++) {
if(iarr[i].is()) {
if (!set) {
@@ -375,12 +379,16 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
if (set < 2 || stop <= start) stop = start + 1;
uint32_t c = gamma32(RGBW32(rgbw[0], rgbw[1], rgbw[2], rgbw[3]));
- while (start < stop) seg.setPixelColor(start++, c);
+ while (start < stop) {
+ if (unsigned(start) < seg_len) seg.setPixelColor(start, c); // WLEDMM don't write out-of-bounds
+ start++;
+ }
set = 0;
}
}
seg.map1D2D = oldMap1D2D; // restore mapping
strip.trigger(); // force segment update
+ suspendStripService = oldLock; // restore previous lock status
}
// send UDP/WS if segment options changed (except selection; will also deselect current preset)
uint8_t diffresult = seg.differs(prev) & 0x7F;
From 5122a80f503870246def3c084ca1499ab7490b64 Mon Sep 17 00:00:00 2001
From: MoonModules <91013628+MoonModules@users.noreply.github.com>
Date: Wed, 5 Nov 2025 09:55:50 +0100
Subject: [PATCH 048/170] Fix typo in disclaimer about liability
---
readme.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/readme.md b/readme.md
index 37258c3e..4323fe7e 100644
--- a/readme.md
+++ b/readme.md
@@ -33,4 +33,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
From a956b22a166d80ef5a98662037b3c92f8adb4239 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Wed, 5 Nov 2025 22:01:07 +0100
Subject: [PATCH 049/170] unGamma8() accuracy improvement
based on upstream changes by @DedeHai
---
wled00/colors.cpp | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/wled00/colors.cpp b/wled00/colors.cpp
index f1bbea0a..69846272 100644
--- a/wled00/colors.cpp
+++ b/wled00/colors.cpp
@@ -441,19 +441,20 @@ 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) {
+IRAM_ATTR_YN uint8_t __attribute__((hot)) unGamma8(uint8_t value) {
//if (!gammaCorrectCol || (value == 0) || (value == 255)) return value;
- if ((value == 0) || (value == 255)) return value;
if ((gammaCorrectVal < 0.999f) || (gammaCorrectVal > 3.0f)) return value;
if (gammaTinv[255] == 0) calcInvGammaTable(gammaCorrectVal);
return gammaTinv[value];
}
-uint32_t __attribute__((hot)) unGamma24(uint32_t c) {
+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));
@@ -462,6 +463,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);
}
@@ -469,9 +472,11 @@ 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
}
From 5177ebd271147f1befce8c00b8a732eceff8034b Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Thu, 6 Nov 2025 22:09:11 +0100
Subject: [PATCH 050/170] fix false warnings when temporarily exceeding
MAX_SEGMENT_DATA
The segment copy constructors can temporarily exceed the budget of MAX_SEGMENT_DATA. The original segment will be de-allocated a few milliseconds later.
This change introduced an "overdraft" budget of 50% that can be used for temporary segment copies.
---
wled00/FX.h | 6 ++++--
wled00/FX_fcn.cpp | 32 ++++++++++++++++++++++----------
2 files changed, 26 insertions(+), 12 deletions(-)
diff --git a/wled00/FX.h b/wled00/FX.h
index c52c8869..32086c50 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -87,6 +87,8 @@ extern BusManager busses; // same as wled.h
#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())
@@ -611,7 +613,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; }
@@ -628,7 +630,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
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 75d7c828..34b77f1f 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -94,7 +94,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 +179,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 +218,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 +228,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,12 +243,15 @@ 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_LOW_MEM) || (errorFlag == ERR_NORAM_PX)) errorFlag = ERR_NONE; // WLEDMM reset errorflag on success
@@ -254,10 +259,17 @@ bool Segment::allocateData(size_t len) {
}
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;
}
From 10b3ac0eb1e72c932b8b753bfcfdbec5b609c8e7 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Thu, 6 Nov 2025 22:50:59 +0100
Subject: [PATCH 051/170] gifdecoder speedup and bugfixes
* fixed a bug that caused wrong behavior with segment mirroring
(effects must use virtualHeight() / virtualWidth() instead of height() / width())
* added image blur as an option (second slider)
* added very basic error reporting for users
* up to 25% faster, especially with big animated gifs
* made all local variables "static" (don't pollute global namespace)
* drawPixelCallback: cache calculation that do not depend on x/y position
* reduced memory allocations on boards without PSRAM, to avoid crashes
---
wled00/FX.cpp | 4 +-
wled00/image_loader.cpp | 87 +++++++++++++++++++++++++++++++----------
2 files changed, 70 insertions(+), 21 deletions(-)
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index f25de8c4..e9d8c01e 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -4693,9 +4693,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_oops();
#else
+ if (max(SEGMENT.virtualWidth(),SEGMENT.virtualHeight()) < 4) return mode_oops(); // too small
renderImageToSegment(SEGMENT);
return FRAMETIME;
#endif
@@ -4704,7 +4706,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
diff --git a/wled00/image_loader.cpp b/wled00/image_loader.cpp
index 2bb465f9..ef283956 100644
--- a/wled00/image_loader.cpp
+++ b/wled00/image_loader.cpp
@@ -9,11 +9,15 @@
* Functions to render images from filesystem to segments, used by the "Image" effect
*/
-File file;
-char lastFilename[34] = "/";
-GifDecoder<320,320,12,true> decoder;
-bool gifDecodeFailed = false;
-unsigned long lastFrameDisplayTime = 0, currentFrameDelay = 0;
+static File file;
+static char lastFilename[34] = "/";
+#if !defined(BOARD_HAS_PSRAM)
+ static GifDecoder<256,256,11,true> decoder; // WLEDMM use less RAM on boards without PSRAM - avoids crashes due to out-of-memory
+#else
+ static GifDecoder<320,320,12,true> decoder;
+#endif
+static bool gifDecodeFailed = false;
+static unsigned long lastFrameDisplayTime = 0, currentFrameDelay = 0;
bool fileSeekCallback(unsigned long position) {
return file.seek(position);
@@ -35,29 +39,49 @@ int fileSizeCallback(void) {
return file.size();
}
-bool openGif(const char *filename) {
+bool openGif(const char *filename) { // side-effect: updates "file"
file = WLED_FS.open(filename, "r");
+ DEBUG_PRINTF("opening GIF file %s\n", filename);
if (!file) return false;
return true;
}
-Segment* activeSeg;
-uint16_t gifWidth, gifHeight;
+static Segment* activeSeg;
+static uint16_t gifWidth, gifHeight; // these two must stay uint16_t, because they are passed by reference
+static unsigned seg_cols = 1;
+static unsigned seg_rows = 1;
+static int expandX = 1;
+static int expandY = 1;
+static int lastX = -1, lastY = -1;
void screenClearCallback(void) {
activeSeg->fill(0);
}
-void updateScreenCallback(void) {}
-
+void updateScreenCallback(void) {
+ // this callback runs when the decoder has finished painting all pixels
+ // perfect time for adding blur
+ if (activeSeg->intensity > 1) {
+ uint8_t blurAmount = activeSeg->intensity >> 2;
+ if ((blurAmount < 24) && (activeSeg->is2D())) activeSeg->blurRows(activeSeg->intensity >> 1); // some blur - fast
+ else activeSeg->blur(blurAmount); // more blur - slower
+ }
+ lastX = lastY = -1; // invalidate last position
+}
void drawPixelCallback(int16_t x, int16_t y, uint8_t red, uint8_t green, uint8_t blue) {
- // simple nearest-neighbor scaling
- int16_t outY = y * activeSeg->height() / gifHeight;
- int16_t outX = x * activeSeg->width() / gifWidth;
+ // simple nearest-neighbor downscaling
+ int outY = y * seg_rows / gifHeight;
+ int outX = x * seg_cols / gifWidth;
+
+ if ((unsigned(outX) >= seg_cols) || (unsigned(outY) >= seg_rows)) return; // out of range
+ if ((lastX == outX) && (lastY == outY)) return; // downscaling optimization: skip re-painting same pixel
+ lastX = outX; lastY = outY;
+
// set multiple pixels if upscaling
- for (int16_t i = 0; i < (activeSeg->width()+(gifWidth-1)) / gifWidth; i++) {
- for (int16_t j = 0; j < (activeSeg->height()+(gifHeight-1)) / gifHeight; j++) {
+ // softhack007: changed loop x/y order -> minor speedup from better cache locality
+ for (int j = 0; j < expandY; j++) {
+ for (int i = 0; i < expandX; i++) {
activeSeg->setPixelColorXY(outX + i, outY + j, gamma8(red), gamma8(green), gamma8(blue));
}
}
@@ -81,17 +105,24 @@ byte renderImageToSegment(Segment &seg) {
// TODO: if (seg.mode != seg.currentMode()) return IMAGE_ERROR_WAITING;
if (activeSeg && activeSeg != &seg) return IMAGE_ERROR_SEG_LIMIT; // only one segment at a time
activeSeg = &seg;
+ seg_cols = activeSeg->virtualWidth();
+ seg_rows = activeSeg->virtualHeight();
if (strncmp(lastFilename +1, seg.name, 32) != 0) { // segment name changed, load new image
strncpy(lastFilename +1, seg.name, 32);
gifDecodeFailed = false;
if (strcmp(lastFilename + strlen(lastFilename) - 4, ".gif") != 0) {
gifDecodeFailed = true;
+ USER_PRINTF("Unsupported format: %s\n", lastFilename);
return IMAGE_ERROR_UNSUPPORTED_FORMAT;
}
if (file) file.close();
- openGif(lastFilename);
- if (!file) { gifDecodeFailed = true; return IMAGE_ERROR_FILE_MISSING; }
+
+ if (!openGif(lastFilename)) {
+ gifDecodeFailed = true;
+ USER_PRINTF("GIF file not found: %s\n", lastFilename);
+ return IMAGE_ERROR_FILE_MISSING;
+ }
decoder.setScreenClearCallback(screenClearCallback);
decoder.setUpdateScreenCallback(updateScreenCallback);
decoder.setDrawPixelCallback(drawPixelCallback);
@@ -100,9 +131,18 @@ byte renderImageToSegment(Segment &seg) {
decoder.setFileReadCallback(fileReadCallback);
decoder.setFileReadBlockCallback(fileReadBlockCallback);
decoder.setFileSizeCallback(fileSizeCallback);
- decoder.alloc();
+ decoder.alloc(); // WLEDMM this function may throw out-of memory and cause a crash
+
DEBUG_PRINTLN(F("Starting decoding"));
- if(decoder.startDecoding() < 0) { gifDecodeFailed = true; return IMAGE_ERROR_GIF_DECODE; }
+ int derr = 0;
+ if((derr = decoder.startDecoding()) < 0) {
+ gifDecodeFailed = true;
+ USER_PRINTF("GIF Decoding error %d\n", derr);
+ if ((derr == ERROR_GIF_TOO_WIDE) || (derr == ERROR_GIF_UNSUPPORTED_FEATURE) || (derr == ERROR_GIF_INVALID_PARAMETER))
+ errorFlag = ERR_NORAM_PX;
+ return IMAGE_ERROR_GIF_DECODE;
+ }
+ if ((errorFlag == ERR_NORAM_PX) || (errorFlag == ERR_NORAM)) errorFlag = ERR_NONE; // success -> reset previous memory error codes
DEBUG_PRINTLN(F("Decoding started"));
}
@@ -118,9 +158,16 @@ byte renderImageToSegment(Segment &seg) {
if (millis() - lastFrameDisplayTime < wait) return IMAGE_ERROR_WAITING;
decoder.getSize(&gifWidth, &gifHeight);
+ // softhack007: pre-calculate upscaling for speedup
+ expandX = (seg_cols+(gifWidth-1)) / gifWidth;
+ expandY = (seg_rows+(gifHeight-1)) / gifHeight;
int result = decoder.decodeFrame(false);
- if (result < 0) { gifDecodeFailed = true; return IMAGE_ERROR_FRAME_DECODE; }
+ if (result < 0) {
+ gifDecodeFailed = true;
+ USER_PRINTF("GIF Frame decode failed %d\n", result);
+ return IMAGE_ERROR_FRAME_DECODE;
+ }
currentFrameDelay = decoder.getFrameDelay_ms();
unsigned long tooSlowBy = (millis() - lastFrameDisplayTime) - wait; // if last frame was longer than intended, compensate
From 698da84b4ac058e080668a08645e000f7009028e Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Thu, 6 Nov 2025 23:09:09 +0100
Subject: [PATCH 052/170] prevent division by zero when image width or height
is 0
catch error early before starting the decode/paint loop
---
wled00/image_loader.cpp | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/wled00/image_loader.cpp b/wled00/image_loader.cpp
index ef283956..5ba7af0a 100644
--- a/wled00/image_loader.cpp
+++ b/wled00/image_loader.cpp
@@ -158,6 +158,12 @@ byte renderImageToSegment(Segment &seg) {
if (millis() - lastFrameDisplayTime < wait) return IMAGE_ERROR_WAITING;
decoder.getSize(&gifWidth, &gifHeight);
+ // bad gif size: prevent division by zero
+ if (gifWidth == 0 || gifHeight == 0) {
+ gifDecodeFailed = true;
+ USER_PRINTF("Invalid GIF dimensions: %dx%d\n", gifWidth, gifHeight);
+ return IMAGE_ERROR_GIF_DECODE;
+ }
// softhack007: pre-calculate upscaling for speedup
expandX = (seg_cols+(gifWidth-1)) / gifWidth;
expandY = (seg_rows+(gifHeight-1)) / gifHeight;
From 25391fe765f72c0c079f5589afc717bb8fed25a8 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Fri, 7 Nov 2025 00:11:14 +0100
Subject: [PATCH 053/170] try to catch / handle Gif decoder OOM gracefully
... by adding an exception handler that will catch OOM inside decoder.alloc().
not tested yet.
---
wled00/image_loader.cpp | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/wled00/image_loader.cpp b/wled00/image_loader.cpp
index 5ba7af0a..6ad0dc2d 100644
--- a/wled00/image_loader.cpp
+++ b/wled00/image_loader.cpp
@@ -131,7 +131,18 @@ byte renderImageToSegment(Segment &seg) {
decoder.setFileReadCallback(fileReadCallback);
decoder.setFileReadBlockCallback(fileReadBlockCallback);
decoder.setFileSizeCallback(fileSizeCallback);
+#if __cpp_exceptions // use exception handler if we can (some targets don't support exceptions)
+ try {
+#endif
decoder.alloc(); // WLEDMM this function may throw out-of memory and cause a crash
+#if __cpp_exceptions
+ } catch (...) { // if we arrive here, the decoder has thrown an OOM exception
+ gifDecodeFailed = true;
+ errorFlag = ERR_NORAM_PX;
+ USER_PRINTLN("\nGIF decoder out of memory. I'm going to shoot myself now.\n");
+ return IMAGE_ERROR_DECODER_ALLOC;
+ }
+#endif
DEBUG_PRINTLN(F("Starting decoding"));
int derr = 0;
From 956479164098c78df0a012d78a674b6fe9b86727 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Fri, 7 Nov 2025 00:28:40 +0100
Subject: [PATCH 054/170] prevent string underflow
if seg.name is empty or shorter than four characters, ``strlen(lastFilename) - 4`` underflows (size_t), so the pointer passed to strcmp lands far before the buffer and triggers undefined behavior.
This patch catches too-short segment names and aborts decoding.
---
wled00/image_loader.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/wled00/image_loader.cpp b/wled00/image_loader.cpp
index 6ad0dc2d..a7bd816e 100644
--- a/wled00/image_loader.cpp
+++ b/wled00/image_loader.cpp
@@ -111,9 +111,10 @@ byte renderImageToSegment(Segment &seg) {
if (strncmp(lastFilename +1, seg.name, 32) != 0) { // segment name changed, load new image
strncpy(lastFilename +1, seg.name, 32);
gifDecodeFailed = false;
- if (strcmp(lastFilename + strlen(lastFilename) - 4, ".gif") != 0) {
+ size_t fnameLen = strlen(lastFilename);
+ if ((fnameLen < 4) || strcmp(lastFilename + strlen(lastFilename) - 4, ".gif") != 0) { // empty segment name, name too short, or name not ending in .gif
gifDecodeFailed = true;
- USER_PRINTF("Unsupported format: %s\n", lastFilename);
+ USER_PRINTF("GIF decoder unsupported file: %s\n", lastFilename);
return IMAGE_ERROR_UNSUPPORTED_FORMAT;
}
if (file) file.close();
From f955df04880120999bedffff75252376d625f167 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Fri, 7 Nov 2025 11:05:44 +0100
Subject: [PATCH 055/170] camelCase, future support, minor cleanup
* use camelCase for local variables
* furure support: rename drawPixelCallback to draw2DPixelCallback
---
wled00/image_loader.cpp | 25 +++++++++++++------------
1 file changed, 13 insertions(+), 12 deletions(-)
diff --git a/wled00/image_loader.cpp b/wled00/image_loader.cpp
index a7bd816e..808ab4c1 100644
--- a/wled00/image_loader.cpp
+++ b/wled00/image_loader.cpp
@@ -49,8 +49,9 @@ bool openGif(const char *filename) { // side-effect: updates "file"
static Segment* activeSeg;
static uint16_t gifWidth, gifHeight; // these two must stay uint16_t, because they are passed by reference
-static unsigned seg_cols = 1;
-static unsigned seg_rows = 1;
+static unsigned segCols = 1;
+static unsigned segRows = 1;
+//static unsigned segLen = 1; // for future 1D support
static int expandX = 1;
static int expandY = 1;
static int lastX = -1, lastY = -1;
@@ -69,12 +70,11 @@ void updateScreenCallback(void) {
}
lastX = lastY = -1; // invalidate last position
}
-void drawPixelCallback(int16_t x, int16_t y, uint8_t red, uint8_t green, uint8_t blue) {
+void draw2DPixelCallback(int16_t x, int16_t y, uint8_t red, uint8_t green, uint8_t blue) {
// simple nearest-neighbor downscaling
- int outY = y * seg_rows / gifHeight;
- int outX = x * seg_cols / gifWidth;
-
- if ((unsigned(outX) >= seg_cols) || (unsigned(outY) >= seg_rows)) return; // out of range
+ int outY = y * segRows / gifHeight;
+ int outX = x * segCols / gifWidth;
+ if ((unsigned(outX) >= segCols) || (unsigned(outY) >= segRows)) return; // out of range
if ((lastX == outX) && (lastY == outY)) return; // downscaling optimization: skip re-painting same pixel
lastX = outX; lastY = outY;
@@ -105,8 +105,9 @@ byte renderImageToSegment(Segment &seg) {
// TODO: if (seg.mode != seg.currentMode()) return IMAGE_ERROR_WAITING;
if (activeSeg && activeSeg != &seg) return IMAGE_ERROR_SEG_LIMIT; // only one segment at a time
activeSeg = &seg;
- seg_cols = activeSeg->virtualWidth();
- seg_rows = activeSeg->virtualHeight();
+ segCols = activeSeg->virtualWidth();
+ segRows = activeSeg->virtualHeight();
+ // segLen = activeSeg->virtualLength(); // for future 1D and expand1D support
if (strncmp(lastFilename +1, seg.name, 32) != 0) { // segment name changed, load new image
strncpy(lastFilename +1, seg.name, 32);
@@ -126,7 +127,7 @@ byte renderImageToSegment(Segment &seg) {
}
decoder.setScreenClearCallback(screenClearCallback);
decoder.setUpdateScreenCallback(updateScreenCallback);
- decoder.setDrawPixelCallback(drawPixelCallback);
+ decoder.setDrawPixelCallback(draw2DPixelCallback);
decoder.setFileSeekCallback(fileSeekCallback);
decoder.setFilePositionCallback(filePositionCallback);
decoder.setFileReadCallback(fileReadCallback);
@@ -177,8 +178,8 @@ byte renderImageToSegment(Segment &seg) {
return IMAGE_ERROR_GIF_DECODE;
}
// softhack007: pre-calculate upscaling for speedup
- expandX = (seg_cols+(gifWidth-1)) / gifWidth;
- expandY = (seg_rows+(gifHeight-1)) / gifHeight;
+ expandX = (segCols+(gifWidth-1)) / gifWidth;
+ expandY = (segRows+(gifHeight-1)) / gifHeight;
int result = decoder.decodeFrame(false);
if (result < 0) {
From 9115cd17af00599fbbfe0a92e5ce1645e77af6dd Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Fri, 7 Nov 2025 11:45:46 +0100
Subject: [PATCH 056/170] tiny cleanup, ready to merge
---
wled00/image_loader.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/wled00/image_loader.cpp b/wled00/image_loader.cpp
index 808ab4c1..b5a17644 100644
--- a/wled00/image_loader.cpp
+++ b/wled00/image_loader.cpp
@@ -113,7 +113,7 @@ byte renderImageToSegment(Segment &seg) {
strncpy(lastFilename +1, seg.name, 32);
gifDecodeFailed = false;
size_t fnameLen = strlen(lastFilename);
- if ((fnameLen < 4) || strcmp(lastFilename + strlen(lastFilename) - 4, ".gif") != 0) { // empty segment name, name too short, or name not ending in .gif
+ if ((fnameLen < 4) || strcmp(lastFilename + fnameLen - 4, ".gif") != 0) { // empty segment name, name too short, or name not ending in .gif
gifDecodeFailed = true;
USER_PRINTF("GIF decoder unsupported file: %s\n", lastFilename);
return IMAGE_ERROR_UNSUPPORTED_FORMAT;
From c58c6ef26887e68168a13e85765181bb7ab81bd0 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Fri, 7 Nov 2025 11:58:55 +0100
Subject: [PATCH 057/170] minor robustness improvement
If seg.name is at least 32 characters long, strncpy will fill the 32-byte window without appending '\0'. This could lead to sporadic errors.
---
wled00/image_loader.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/wled00/image_loader.cpp b/wled00/image_loader.cpp
index b5a17644..8046fe78 100644
--- a/wled00/image_loader.cpp
+++ b/wled00/image_loader.cpp
@@ -111,6 +111,7 @@ byte renderImageToSegment(Segment &seg) {
if (strncmp(lastFilename +1, seg.name, 32) != 0) { // segment name changed, load new image
strncpy(lastFilename +1, seg.name, 32);
+ lastFilename[33] = '\0'; // make sure that lastFilename is always null-terminated
gifDecodeFailed = false;
size_t fnameLen = strlen(lastFilename);
if ((fnameLen < 4) || strcmp(lastFilename + fnameLen - 4, ".gif") != 0) { // empty segment name, name too short, or name not ending in .gif
From c1979f8bc7849049e7e8a0013296ef817ab7cb63 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Fri, 7 Nov 2025 12:08:31 +0100
Subject: [PATCH 058/170] no fun in Germany ;-)
no fun, just rabbits. Lets stay professional.
---
wled00/image_loader.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/wled00/image_loader.cpp b/wled00/image_loader.cpp
index 8046fe78..d8c21243 100644
--- a/wled00/image_loader.cpp
+++ b/wled00/image_loader.cpp
@@ -142,7 +142,8 @@ byte renderImageToSegment(Segment &seg) {
} catch (...) { // if we arrive here, the decoder has thrown an OOM exception
gifDecodeFailed = true;
errorFlag = ERR_NORAM_PX;
- USER_PRINTLN("\nGIF decoder out of memory. I'm going to shoot myself now.\n");
+ USER_PRINTLN("\nGIF decoder out of memory. Please try a smaller image file.\n");
+ //USER_PRINTLN("I'm going to shoot myself now.");
return IMAGE_ERROR_DECODER_ALLOC;
}
#endif
From d78ea5ae2edd5a4fb03b8ad9947b0b1c64d7165f Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Fri, 7 Nov 2025 15:07:13 +0100
Subject: [PATCH 059/170] prevent bad filenames in "segment names as ledmap
names"
Increase buffer size for fileName to accommodate long segment names (max 32 chars) without producing broken filenames.
---
wled00/FX_fcn.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 34b77f1f..5847309e 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -1765,8 +1765,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[33+11] = { '\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);
}
From f9a2099240e31eece1ce24cb1eb06eb4a80df2a1 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Sun, 9 Nov 2025 20:30:43 +0100
Subject: [PATCH 060/170] always use same GifDecoder initialization
Removed conditional compilation for GifDecoder based on PSRAM availability.
After inspecting the library code, it became clear that any parameter changes for GifDecoder will not impact memory needs - decoder.alloc() always requires 24KB.
---
wled00/image_loader.cpp | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/wled00/image_loader.cpp b/wled00/image_loader.cpp
index d8c21243..3a8fd99d 100644
--- a/wled00/image_loader.cpp
+++ b/wled00/image_loader.cpp
@@ -11,11 +11,7 @@
static File file;
static char lastFilename[34] = "/";
-#if !defined(BOARD_HAS_PSRAM)
- static GifDecoder<256,256,11,true> decoder; // WLEDMM use less RAM on boards without PSRAM - avoids crashes due to out-of-memory
-#else
- static GifDecoder<320,320,12,true> decoder;
-#endif
+static GifDecoder<320,320,12,true> decoder; // this creates the basic object; parameter lzwMaxBits is not used; decoder.alloc() always allocated "everything else" = 24Kb
static bool gifDecodeFailed = false;
static unsigned long lastFrameDisplayTime = 0, currentFrameDelay = 0;
@@ -209,4 +205,4 @@ void endImagePlayback(Segment *seg) {
DEBUG_PRINTLN(F("Image playback ended"));
}
-#endif
\ No newline at end of file
+#endif
From 71b7121dedfc5abd9110d5d093a3ba0acd605076 Mon Sep 17 00:00:00 2001
From: Damian Schneider
Date: Sun, 28 Sep 2025 17:06:31 +0200
Subject: [PATCH 061/170] bugfix: do not reset segments if unchanged #4969
lines were swapped, causing segment reset on every preset call.
---
wled00/FX_fcn.cpp | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 5847309e..50c9ffc7 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -537,7 +537,7 @@ 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
stop = 0;
markForReset();
@@ -558,8 +558,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();
+ }
}
From b94fc7c3f04a661308c1450ecbb8ff1a89ce650a Mon Sep 17 00:00:00 2001
From: netmindz
Date: Wed, 20 Aug 2025 07:02:01 +0100
Subject: [PATCH 062/170] Merge pull request #4849 from
Arcitec/0_15_x-improve-version-info
(0.15.2 backport): Make version information consistent across update interfaces
---
wled00/data/settings_sec.htm | 2 +-
wled00/data/update.htm | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/wled00/data/settings_sec.htm b/wled00/data/settings_sec.htm
index b6fe5590..b012e0b9 100644
--- a/wled00/data/settings_sec.htm
+++ b/wled00/data/settings_sec.htm
@@ -122,7 +122,7 @@
A huge thank you to everyone who helped me create WLED!
WLED core (c) 2016-2024 Christian Schwinne WLED MM Licensed under the EUPL-1.2 license