diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 9f6250cb48..2cb6964472 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -48,7 +48,7 @@ body: attributes: label: What version/release of MM WLED? description: You can find this in by going to Config -> Security & Updates -> Scroll to Bottom. Copy and paste the entire line after "Server message" - placeholder: "e.g. build 2401290, WLEDMM_0.14.1-b30.37_esp32_4MB_M.bin" + placeholder: "e.g. build 2401290, WLEDMM_0.14.1-b31.38_esp32_4MB_M.bin" validations: required: true - type: dropdown diff --git a/package-lock.json b/package-lock.json index eead546113..b4bcd63e51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "wled", - "version": "0.14.1-b30.37", + "version": "0.14.1-b31.38", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "wled", - "version": "0.14.1-b30.37", + "version": "0.14.1-b31.38", "license": "GPL-3.0-or-later", "dependencies": { "clean-css": "^4.2.3", diff --git a/package.json b/package.json index 920024ce10..347fec2557 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wled", - "version": "0.14.1-b30.37", + "version": "0.14.1-b31.38", "description": "Tools for WLED project", "main": "tools/cdata.js", "directories": { diff --git a/pio-scripts/obj-dump.py b/pio-scripts/obj-dump.py index 91bc3de581..174df509c3 100644 --- a/pio-scripts/obj-dump.py +++ b/pio-scripts/obj-dump.py @@ -1,9 +1,24 @@ # Little convenience script to get an object dump +# You may add "-S" to the objdump commandline (i.e. replace "-D -C " with "-d -S -C ") +# to get source code intermixed with disassembly (SLOW !) Import('env') def obj_dump_after_elf(source, target, env): + platform = env.PioPlatform() + board = env.BoardConfig() + mcu = board.get("build.mcu", "esp32") + print("Create firmware.asm") - env.Execute("xtensa-lx106-elf-objdump "+ "-D " + str(target[0]) + " > "+ "${PROGNAME}.asm") - + if mcu == "esp8266": + env.Execute("xtensa-lx106-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm") + if mcu == "esp32": + env.Execute("xtensa-esp32-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm") + if mcu == "esp32s2": + env.Execute("xtensa-esp32s2-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm") + if mcu == "esp32s3": + env.Execute("xtensa-esp32s3-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm") + if mcu == "esp32c3": + env.Execute("riscv32-esp-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm") + env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", [obj_dump_after_elf]) diff --git a/pio-scripts/output_bins.py b/pio-scripts/output_bins.py index 2b047780a9..3a55ced8cd 100644 --- a/pio-scripts/output_bins.py +++ b/pio-scripts/output_bins.py @@ -22,9 +22,93 @@ def _create_dirs(dirs=["firmware", "map"]): if not os.path.isdir("{}{}".format(OUTPUT_DIR, d)): os.mkdir("{}{}".format(OUTPUT_DIR, d)) + +# trick for py2/3 compatibility +if 'basestring' not in globals(): + basestring = str + +# WLEDMM : custom print function +def print_my_item(items, flag = False): + if flag: print(" -D", end='') + else: print(" ", end='') + if isinstance(items, basestring): + # print a single string + print(items, end='') + else: + # print a list + first = True + for item in items: + if not first: print("=", end='') + print(item, end='') + first = False + +# WLEDMM : dump out buildflags : usermods, disable, enable, use_.. +def wledmm_print_build_info(env): + all_flags = env["CPPDEFINES"] + first = True + + found = False + for item in all_flags: + if 'WLED_RELEASE_NAME' in item[0] or 'WLED_VERSION' in item[0] or 'ARDUINO_USB_CDC_ON_BOOT' in item[0]: + if first: print("\nUsermods and Features:") + print_my_item(item) + first = False + found = True + if found: print("") + + found = False + for item in all_flags: + if 'USERMOD_' in item or 'UM_' in item: + if first: print("\nUsermods and Features:") + print_my_item(item) + first = False + found = True + if found: print("") + + found = False + for item in all_flags: + if 'WLED_DISABLE' in item or 'WIFI_FIX' in item: + if first: print("\nUsermods and Features:") + print_my_item(item) + first = False + found = True + if found: print("") + + found = False + for item in all_flags: + if 'WLED_' in item or 'WLED_' in item[0] or 'MAX_LED' in item[0]: + if not 'WLED_RELEASE_NAME' in item[0] and not 'WLED_VERSION' in item[0] and not 'WLED_WATCHDOG_TIMEOUT' in item[0] and not 'WLED_DISABLE' in item and not 'WLED_USE_MY_CONFIG' in item and not 'ARDUINO_PARTITION' in item: + if first: print("\nUsermods and Features:") + print_my_item(item) + first = False + found = True + if found: print("") + + first = True + found = False + for item in all_flags: + if 'WLEDMM_' in item[0] or 'WLEDMM_' in item or 'TROYHACKS' in item: + if first: print("\nWLEDMM Features:") + print_my_item(item) + first = False + found = True + if found: print("\n") + +def wledmm_print_all_defines(env): + all_flags = env["CPPDEFINES"] + found = False + for item in all_flags: + if not found: print("\nBuild Flags:") + print_my_item(item, True) + found = True + if found: print("\n") + + def bin_rename_copy(source, target, env): _create_dirs() variant = env["PIOENV"] + builddir = os.path.join(env["PROJECT_BUILD_DIR"], variant) + source_map = os.path.join(builddir, env["PROGNAME"] + ".map") # create string with location and file names based on variant map_file = "{}map{}{}.map".format(OUTPUT_DIR, os.path.sep, variant) @@ -48,7 +132,14 @@ def bin_rename_copy(source, target, env): # copy firmware.map to map/.map if os.path.isfile("firmware.map"): - shutil.move("firmware.map", map_file) + print("Found linker mapfile firmware.map") + shutil.copy("firmware.map", map_file) + if os.path.isfile(source_map): + print(f"Found linker mapfile {source_map}") + shutil.copy(source_map, map_file) + + # wledmm_print_all_defines(env) + # wledmm_print_build_info(env) def bin_gzip(source, target, env): _create_dirs() diff --git a/platformio.ini b/platformio.ini index 2da68bd7a5..5ada7d4770 100644 --- a/platformio.ini +++ b/platformio.ini @@ -74,7 +74,9 @@ default_envs = ; esp32_4MB_PSRAM_REV3_S ;; experimental, optimized for WROVER-E with "revision3" chip esp32S3_8MB_S ;; experimental, optimized for speed esp32S3_8MB_M - ;; esp32S3_8MB_PSRAM_M ;; experiemental + esp32S3_4MB_PSRAM_S ;; for lolin s3 mini, S3 zero, S3 super mini - optimized for speed + esp32S3_4MB_PSRAM_M ;; for lolin s3 mini, S3 zero, S3 super mini + esp32S3_8MB_PSRAM_M ;; experiemental ;; esp32s2_tinyUF2_PSRAM_S ;; experimental - only for adafruit -S2 boards with tinyUF2 bootloader !!! esp32s2_PSRAM_M ;; experimental esp32c3dev_4MB_M ;; experimental @@ -213,6 +215,7 @@ extra_scripts = post:pio-scripts/output_bins.py post:pio-scripts/strip-floats.py pre:pio-scripts/user_config_copy.py + ; post:pio-scripts/obj-dump.py ;; convenience script to create a disassembly dump of the firmware (hardcore debugging) # ------------------------------------------------------------------------------ # COMMON SETTINGS: @@ -239,8 +242,8 @@ lib_deps = IRremoteESP8266 @ 2.8.2 ;;makuna/NeoPixelBus @ 2.7.5 ;; WLEDMM will be added in board specific sections ;;https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.7 - https://github.com/lost-hope/ESPAsyncWebServer.git#master ;; WLEDMM to display .log and .wled files in /edit - ;; https://github.com/Aircoookie/ESPAsyncWebServer.git @ ^2.2.1 + ;; https://github.com/lost-hope/ESPAsyncWebServer.git#master ;; WLEDMM to display .log and .wled files in /edit + https://github.com/Aircoookie/ESPAsyncWebServer.git @ 2.2.1 ;; newer with bugfixes and stability improvements #For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line #TFT_eSPI #For compatible OLED display uncomment following @@ -302,19 +305,32 @@ build_flags = -g -D CONFIG_ASYNC_TCP_TASK_STACK_SIZE=9472 ;; WLEDMM increase stack by 1.25Kb, as audioreactive needs bigger SETTINGS_STACK_BUF_SIZE #use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x -D LOROL_LITTLEFS + ; -D WLEDMM_TWOPATH ;; use I2S1 as the second bus --> ~15% faster on "V3" builds - may flicker a bit more + ; -D WLEDMM_SLOWPATH ;; don't use I2S for LED bus ; -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3 default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv ;; WLED standard for 4MB flash: 1.4MB firmware, 1MB filesystem ;default_partitions = tools/WLED_ESP32_4MB_256KB_FS.csv ;; WLEDMM alternative for 4MB flash: 1.8MB firmware, 256KB filesystem (esptool erase_flash needed before changing) +tiny_partitions = tools/WLED_ESP32_2MB_noOTA.csv +extended_partitions = tools/WLED_ESP32_4MB_700k_FS.csv +big_partitions = tools/WLED_ESP32_4MB_256KB_FS.csv ;; 1.8MB firmware, 256KB filesystem, coredump support +large_partitions = tools/WLED_ESP32_8MB.csv +extreme_partitions = tools/WLED_ESP32_16MB_9MB_FS.csv + lib_deps = 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 !! ; https://github.com/lorol/LITTLEFS.git ; WLEDMM specific: use patched version of lorol LittleFS https://github.com/softhack007/LITTLEFS-threadsafe.git#master makuna/NeoPixelBus @ 2.7.5 + ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental ${env.lib_deps} +;; Compatibility with upstream --> you should prefer using ${common_mm.build_flags_S} and ${common_mm.lib_deps_S} +AR_build_flags = ${common_mm.AR_build_flags} +AR_lib_deps = ${common_mm.AR_lib_deps} ;; optimized version, 10% faster on -S2/-C3 + ;; WLEDMM begin ;; ** For compiling with latest Frameworks (IDF4.4.x and arduino-esp32 v2.0.x) ** @@ -323,19 +339,20 @@ platformV4_pre = espressif32@5.2.0 platformV4_packages_pre = toolchain-riscv32-esp @ 8.4.0+2021r2-patch5 ; required for platform version < 5.3.0, remove this line when upgrading to platform >=5.3.0 ;;; standard V4 platform -platformV4 = espressif32@5.3.0 -platformV4_packages = +platformV4 = espressif32@ ~6.3.2 +platformV4_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) -;;; experimental: latest V4 platform with latest arduino-esp32 2.0.9 + ESP-IDF 4.4.4 (may or may not work) -platformV4_xp = espressif32@ ~6.4.0 -platformV4_packages_xp = platformio/framework-arduinoespressif32 @ ~3.20009.0 ;; arduino-esp32 v2.0.9+ -;; platformV4_packages_xp = platformio/framework-arduinoespressif32 @ ~3.20011.0 ;; arduino-esp32 v2.0.11 (latest one supported in platformio) +;;; experimental: latest V4 platform with latest arduino-esp32 2.0.14 + ESP-IDF 4.4.6 (may or may not work) +platformV4_xp = espressif32@ ~6.5.0 +platformV4_packages_xp = platformio/framework-arduinoespressif32 @ 3.20014.231204 ;; arduino-esp32 2.0.14 build_flagsV4 = -g -DARDUINO_ARCH_ESP32 -DESP32 -DCONFIG_LITTLEFS_FOR_IDF_3_2 -DLFS_THREADSAFE -D CONFIG_ASYNC_TCP_USE_WDT=0 -D CONFIG_ASYNC_TCP_TASK_STACK_SIZE=9472 ;; WLEDMM increase stack by 1.25Kb, as audioreactive needs bigger SETTINGS_STACK_BUF_SIZE + ; -D WLEDMM_TWOPATH ;; use I2S1 as the second bus --> slightly faster on some setups + ; -D WLEDMM_SLOWPATH ;; don't use I2S for LED bus ; -DARDUINO_USB_CDC_ON_BOOT=0 ;; mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3 -D NO_GFX ; Disable the use of Adafruit_GFX by the HUB75 driver @@ -343,8 +360,9 @@ build_flagsV4 = -g lib_depsV4 = https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 ;; WLEDMM this must be first in the list, otherwise Aircoookie/ESPAsyncWebServer pulls in an older version of AsyncTCP !! makuna/NeoPixelBus @ 2.7.5 + ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental + ${common_mm.HUB75_lib_deps} ${env.lib_deps} - https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-DMA.git @ 3.0.10 ;; WLEDMM end @@ -355,8 +373,8 @@ lib_depsV4 = ;; ;; please note that you can NOT update existing ESP32 installs with a "V4" build. Also updating by OTA will not work properly. ;; You need to completely erase your device (esptool erase_flash) first, then install the "V4" build from VSCode+platformio. -platform = espressif32@5.3.0 -platform_packages = +platform = espressif32@ ~6.3.2 +platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) build_flags = -g -Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one -DARDUINO_ARCH_ESP32 -DESP32 @@ -364,18 +382,24 @@ build_flags = -g -D CONFIG_ASYNC_TCP_USE_WDT=0 -D CONFIG_ASYNC_TCP_TASK_STACK_SIZE=9472 ;; WLEDMM increase stack by 1.25Kb, as audioreactive needs bigger SETTINGS_STACK_BUF_SIZE -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3 + ; -D WLEDMM_TWOPATH ;; use I2S1 as the second bus --> slightly faster on some setups + ; -D WLEDMM_SLOWPATH ;; don't use I2S for LED bus default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv lib_deps = https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 makuna/NeoPixelBus @ 2.7.5 + ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental ${env.lib_deps} [esp32s2] ;; generic definitions for all ESP32-S2 boards -platform = espressif32@5.2.0 -platform_packages = - toolchain-riscv32-esp @ 8.4.0+2021r2-patch5 - toolchain-xtensa-esp32s2 @ 8.4.0+2021r2-patch5 +;; platform = espressif32@5.2.0 +;; platform_packages = +;; toolchain-riscv32-esp @ 8.4.0+2021r2-patch5 +;; toolchain-xtensa-esp32s2 @ 8.4.0+2021r2-patch5 +platform = espressif32@ ~6.3.2 +platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) + build_flags = -g -DARDUINO_ARCH_ESP32 -DESP32 ;; WLEDMM -DARDUINO_ARCH_ESP32S2 @@ -391,13 +415,14 @@ build_flags = -g lib_deps = https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 - makuna/NeoPixelBus @ 2.7.5 + ;; makuna/NeoPixelBus @ 2.7.5 ;; standard + makuna/NeoPixelBus @ 2.7.9 ;; experimental - reduces LED glitches on -S2 ${env.lib_deps} [esp32c3] ;; generic definitions for all ESP32-C3 boards -platform = espressif32@5.3.0 -platform_packages = +platform = espressif32@ ~6.3.2 +platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) build_flags = -g -DARDUINO_ARCH_ESP32 -DESP32 ;; WLEDMM -DARDUINO_ARCH_ESP32C3 @@ -413,12 +438,13 @@ build_flags = -g lib_deps = https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 makuna/NeoPixelBus @ 2.7.5 + ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental ${env.lib_deps} [esp32s3] ;; generic definitions for all ESP32-S3 boards -platform = espressif32@5.3.0 -platform_packages = +platform = espressif32@ ~6.3.2 +platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) build_flags = -g -DESP32 -DARDUINO_ARCH_ESP32 @@ -431,10 +457,10 @@ build_flags = -g -DCO ;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry: ;; ARDUINO_USB_MODE, ARDUINO_USB_CDC_ON_BOOT - lib_deps = https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 makuna/NeoPixelBus @ 2.7.5 + ;; makuna/NeoPixelBus @ 2.7.9 ;; experimental ${env.lib_deps} @@ -602,13 +628,30 @@ build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME -DARDUINO_USB_CDC_ON_BOOT=1 lib_deps = ${esp32s2.lib_deps} +;; WLEDMM: use WLEDMM build environments +;; [env:esp32_wrover] +;; extends = esp32_idf_V4 +;; platform = ${esp32_idf_V4.platform} +;; platform_packages = ${esp32_idf_V4.platform_packages} +;; board = ttgo-t7-v14-mini32 +;; board_build.f_flash = 80000000L +;; board_build.flash_mode = qio +;; board_build.partitions = ${esp32.extended_partitions} +;; build_unflags = ${common.build_unflags} +;; build_flags = ${common.build_flags_esp32_V4} -D WLED_RELEASE_NAME=ESP32_WROVER +;; -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue ;; Older ESP32 (rev.<3) need a PSRAM fix (increases static RAM used) https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/external-ram.html +;; -D LEDPIN=25 +;; ; ${esp32.AR_build_flags} +;; lib_deps = ${esp32_idf_V4.lib_deps} +;; ; ${esp32.AR_lib_deps} + [env:esp32c3dev] extends = esp32c3 platform = ${esp32c3.platform} platform_packages = ${esp32c3.platform_packages} framework = arduino board = esp32-c3-devkitm-1 -board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv +board_build.partitions = ${esp32.default_partitions} build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=ESP32-C3 -D WLED_WATCHDOG_TIMEOUT=0 -DLOLIN_WIFI_FIX ; seems to work much better with this @@ -631,7 +674,8 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME= ;-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") ;-D WLED_DEBUG lib_deps = ${esp32s3.lib_deps} -board_build.partitions = tools/WLED_ESP32_8MB.csv + ${esp32.AR_lib_deps} +board_build.partitions = ${esp32.large_partitions} board_build.f_flash = 80000000L board_build.flash_mode = qio ; board_build.flash_mode = dio ;; try this if you have problems at startup @@ -652,7 +696,8 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} ; -D WLED_RELEASE_NAME=ESP32-S3_PSRAM -D WLED_USE_PSRAM -DBOARD_HAS_PSRAM ; tells WLED that PSRAM shall be used lib_deps = ${esp32s3.lib_deps} -board_build.partitions = tools/WLED_ESP32_8MB.csv + ${esp32.AR_lib_deps} +board_build.partitions = ${esp32.large_partitions} board_build.f_flash = 80000000L board_build.flash_mode = qio monitor_filters = esp32_exception_decoder @@ -735,8 +780,10 @@ lib_deps = ${esp8266.lib_deps} platform = ${esp32s2.platform} platform_packages = ${esp32s2.platform_packages} board = lolin_s2_mini -board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv +board_build.partitions = ${esp32.default_partitions} build_unflags = ${common.build_unflags} #-DARDUINO_USB_CDC_ON_BOOT=1 +;; board_build.flash_mode = qio +;; board_build.f_flash = 80000000L build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=ESP32-S2 -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT=1 # try disabling and enabling unflag above in case of board-specific issues, will disable Serial @@ -970,6 +1017,29 @@ build_disable_sync_interfaces = -D WLED_DISABLE_ADALIGHT ;; WLEDMM this board does not have a serial-to-USB chip. Better to disable serial protocols, to avoid crashes (see upstream #3128) -D WLED_DISABLE_ESPNOW ;; ESP-NOW requires wifi, may crash with ethernet only +AR_build_flags = -D USERMOD_AUDIOREACTIVE -D UM_AUDIOREACTIVE_USE_NEW_FFT ;; WLEDMM audioreactive usermod, licensed under GPLv3 +AR_lib_deps = https://github.com/softhack007/arduinoFFT.git#develop @ 1.9.2 ;; used for USERMOD_AUDIOREACTIVE - optimized version, 10% faster on -S2/-C3 + +animartrix_build_flags = -D USERMOD_ANIMARTRIX ;; WLEDMM usermod: CC BY-NC 3.0 licensed effects by Stefan Petrick +animartrix_lib_deps = https://github.com/netmindz/animartrix.git#18bf17389e57c69f11bc8d04ebe1d215422c7fb7 +animartrix_lib_ignore = animartrix ;; to remove the animartrix lib dependancy (saves a few bytes) + +DMXin_build_flags = -D WLED_ENABLE_DMX_INPUT ;; WLEDMM DMX physical input - requires ESP-IDF v4.4.x +DMXin_lib_deps = https://github.com/someweisguy/esp_dmx.git#47db25d ;; for DMX_INPUT +DMXin_lib_ignore = esp_dmx ;; to remove the esp-dmx lib dependancy (saves a few bytes) + +HUB75_build_flags = + -D WLED_ENABLE_HUB75MATRIX ;-D SPIRAM_FRAMEBUFFER ;; WLEDMM HUB75 support - requires ESP-IDF v4.4.x + -D NO_GFX ; Disable the use of Adafruit_GFX by the HUB75 driver +HUB75_lib_deps = https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-DMA.git @ 3.0.10 +HUB75_lib_ignore = ESP32 HUB75 LED MATRIX PANEL DMA Display ;; to remove the HUB75 lib dependancy (saves a few bytes) + +NetDebug_build_flags = + ;; WLEDMM: only setting WLED_DEBUG_HOST is enough, ip and port can be defined in sync settings as well + -D WLED_DEBUG_HOST='"192.168.x.x"' ;; to send debug messages over network to host 192.168.x.y - FQDN is also possible + -D WLED_DEBUG_PORT=1768 ;; port for network debugging. default = 7868 + + build_flags_S = -Wall -Wformat -Woverflow -Wuninitialized -Winit-self -Warray-bounds ; enables more warnings -Wno-attributes -Wno-unused-variable -Wno-unused-function -Wno-deprecated-declarations ;disables some stupid warnings @@ -977,21 +1047,15 @@ build_flags_S = -D WLED_USE_MY_CONFIG ; -D WLED_DISABLE_2D ;; un-comment to build a firmware without 2D matrix support ; -D WLED_USE_CIE_BRIGHTNESS_TABLE ;; experimental: use different color / brightness lookup table - -D USERMOD_AUDIOREACTIVE + ${common_mm.AR_build_flags} ; use latest (upstream) FFTLib, instead of older library modified by blazoncek. Slightly faster, more accurate, needs 2KB RAM extra -D USERMOD_AUTO_PLAYLIST - -D UM_AUDIOREACTIVE_USE_NEW_FFT ; use latest (upstream) FFTLib, instead of older library modified by blazoncek. Slightly faster, more accurate, needs 2KB RAM extra ; -D USERMOD_ARTIFX ;; WLEDMM usermod - temporarily moved into "_M", due to problems in "_S" when compiling with -O2 -D WLEDMM_FASTPATH ;; WLEDMM experimental option. Reduces audio lag (latency), and allows for faster LED framerates. May break compatibility with previous versions. ; -D WLED_DEBUG_HEAP ;; WLEDMM enable heap debugging - ${common_mm.build_disable_sync_interfaces} lib_deps_S = ;; https://github.com/kosme/arduinoFFT#develop @ 1.9.2+sha.419d7b0 ;; used for USERMOD_AUDIOREACTIVE - using "known working" hash - https://github.com/softhack007/arduinoFFT.git#develop @ 1.9.2 ;; used for USERMOD_AUDIOREACTIVE - optimized version, 10% faster on -S2/-C3 - - -animartrix_build_flags = -D USERMOD_ANIMARTRIX ;; WLEDMM usermod: CC BY-NC 3.0 licensed effects by Stefan Petrick -animartrix_lib_deps = https://github.com/netmindz/animartrix.git#18bf17389e57c69f11bc8d04ebe1d215422c7fb7 + ${common_mm.AR_lib_deps} ;; used for USERMOD_AUDIOREACTIVE - optimized version, 10% faster on -S2/-C3 build_flags_M = -D USERMOD_ARTIFX ; WLEDMM usermod - temporarily moved into "_M", due to problems in "_S" when compiling with -O2 @@ -1004,9 +1068,7 @@ build_flags_M = -D USERMOD_ROTARY_ENCODER_UI -D USERMOD_AUTO_SAVE ${common_mm.animartrix_build_flags} - ;WLEDMM: only setting WLED_DEBUG_HOST is enough, ip and port can be defined in sync settings as well - -D WLED_DEBUG_HOST='"192.168.x.x"' ;; to send debug messages over network to host 192.168.x.y - FQDN is also possible - -D WLED_DEBUG_PORT=1768 ;; port for network debugging. default = 7868 + ${common_mm.NetDebug_build_flags} lib_deps_M = ;https://github.com/blazoncek/OneWire.git ; includes bugfixes for inconsistent readings @@ -1021,6 +1083,7 @@ lib_deps_V4_M = ${common_mm.animartrix_lib_deps} build_flags_XL = + -D WLEDMM_SAVE_FLASH -D USERMOD_WEATHER ; WLEDMM usermod -D USERMOD_MPU6050_IMU ; gyro/accelero for USERMOD_GAMES (ONLY WORKS IF USERMOD_FOUR_LINE_DISPLAY NOT INCLUDED - I2C SHARING BUG) -D USERMOD_GAMES ; WLEDMM usermod @@ -1066,7 +1129,7 @@ platform = ${esp32.platform} upload_speed = 460800 ; or 921600 platform_packages = ${esp32.platform_packages} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags_esp32} ${common_mm.build_flags_S} +build_flags = ${common.build_flags_esp32} ${common_mm.build_flags_S} ${common_mm.build_disable_sync_interfaces} lib_deps = ${esp32.lib_deps} ${common_mm.lib_deps_S} board_build.partitions = ${esp32.default_partitions} board_build.f_flash = 80000000L ; use full 80MHz speed for flash (default = 40Mhz) @@ -1076,17 +1139,15 @@ monitor_filters = esp32_exception_decoder ;common default for all max environments [esp32_4MB_M_base] extends = esp32_4MB_S_base -build_flags = ${esp32_4MB_S_base.build_flags} ${common_mm.build_flags_M} -build_unflags = ${esp32_4MB_S_base.build_unflags} ${common_mm.build_disable_sync_interfaces} +build_flags = ${common.build_flags_esp32} ${common_mm.build_flags_S} ${common_mm.build_flags_M} ;; we don't want common_mm.build_disable_sync_interfaces, so we cannot inherit from esp32_4MB_S_base +build_unflags = ${esp32_4MB_S_base.build_unflags} lib_deps = ${esp32_4MB_S_base.lib_deps} ${common_mm.lib_deps_M} -; board_build.partitions = tools/WLED_ESP32-wrover_4MB.csv [esp32_4MB_XL_base] extends = esp32_4MB_M_base build_flags = ${esp32_4MB_M_base.build_flags} ${common_mm.build_flags_XL} -build_unflags = ${esp32_4MB_M_base.build_unflags} ${common_mm.build_disable_sync_interfaces} +build_unflags = ${esp32_4MB_M_base.build_unflags} lib_deps = ${esp32_4MB_M_base.lib_deps} ${common_mm.lib_deps_XL} -; board_build.partitions = tools/WLED_ESP32-wrover_4MB.csv ;common default for all V4 min environments, including -S3, -S2, -C3 [esp32_4MB_V4_S_base] @@ -1095,14 +1156,14 @@ upload_speed = 460800 ; or 921600 platform = ${esp32.platformV4} platform_packages = ${esp32.platformV4_packages} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} ${common_mm.build_flags_S} ;; do not include ${esp32.build_flagsV4} here !!!! +build_flags = ${common.build_flags} ${common_mm.build_flags_S} ${common_mm.build_disable_sync_interfaces} ;; do not include ${esp32.build_flagsV4} here !!!! -Wno-misleading-indentation -Wno-format-truncation -Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one ;-Wstack-usage=2732 ;; warn if a function needs more that 30% of availeable stack ("stack usage might be unbounded", "stack usage is 2824 bytes") ;-Wsuggest-attribute=const -Wsuggest-attribute=pure ;; ask compiler for hints on attributes - -D WLED_ENABLE_DMX_INPUT + ${common_mm.DMXin_build_flags} lib_deps = ${common_mm.lib_deps_S} ;; do not include ${esp32.lib_depsV4} here !!!! - https://github.com/someweisguy/esp_dmx.git#47db25d ;; for DMX_INPUT + ${common_mm.DMXin_lib_deps} esp32_build_flags = ${esp32.build_flagsV4} ${esp32_4MB_V4_S_base.build_flags} ;; this is for esp32 only, including specific "V4" flags esp32_lib_deps = ${esp32.lib_depsV4} ${esp32_4MB_V4_S_base.lib_deps} ;; this is for esp32 only, including specific "V4" flags board_build.partitions = ${esp32.default_partitions} @@ -1113,7 +1174,14 @@ board_build.flash_mode = dio ; (dio = dual i/o; more compatible than qio = quad [esp32_4MB_V4_M_base] extends = esp32_4MB_V4_S_base -build_flags = ${esp32_4MB_V4_S_base.build_flags} ${common_mm.build_flags_M} ;; generic, for all variants +build_flags = + ${common.build_flags} ${common_mm.build_flags_S} ;; do not include ${esp32.build_flagsV4} here !!!! + -Wno-misleading-indentation -Wno-format-truncation + -Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one + ;-Wstack-usage=2732 ;; warn if a function needs more that 30% of availeable stack ("stack usage might be unbounded", "stack usage is 2824 bytes") + ;-Wsuggest-attribute=const -Wsuggest-attribute=pure ;; ask compiler for hints on attributes + -D WLED_ENABLE_DMX_INPUT + ${common_mm.build_flags_M} ;; generic, for all variants lib_deps = ${esp32_4MB_V4_S_base.lib_deps} ${common_mm.lib_deps_V4_M} esp32_build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} ${common_mm.build_flags_M} ;; for esp32 only, including specific "V4" flags esp32_lib_deps = ${esp32_4MB_V4_S_base.esp32_lib_deps} ${common_mm.lib_deps_V4_M} @@ -1185,22 +1253,22 @@ build_flags = ${esp32_4MB_S_base.build_flags} -D WLED_DISABLE_LOXONE ;-D WLED_DISABLE_MQTT ;-D WLED_DISABLE_INFRARED - ;WLEDMM: disable the next two lines if you don't need "net Debug". It will free ~2% of flash - -D WLED_DEBUG_HOST='"192.168.x.x"' ;; to send debug messages over network to host 192.168.x.y - FQDN is also possible - -D WLED_DEBUG_PORT=1768 ;; port for network debugging. default = 7868 + -D WLEDMM_SAVE_FLASH + ;WLEDMM: disable the next line if you don't need "net Debug". It will free ~2% of flash + ${common_mm.NetDebug_build_flags} ;; normal build ; RAM: [=== ] 25.0% (used 81988 bytes from 327680 bytes) ; Flash: [========= ] 87.4% (used 1374677 bytes from 1572864 bytes) WLEDMM: Earlier 85.7 ;; optimized-for-speed build -; RAM: [=== ] 25.8% (used 84628 bytes from 327680 bytes) -; Flash: [==========] 99.4% (used 1562869 bytes from 1572864 bytes) +; RAM: [=== ] 25.4% (used 83244 bytes from 327680 bytes) +; Flash: [========= ] 93.3% (used 1466821 bytes from 1572864 bytes) [env:esp32_4MB_M] extends = esp32_4MB_M_base build_flags = ${esp32_4MB_M_base.build_flags} -D WLED_RELEASE_NAME=esp32_4MB_M -; RAM: [=== ] 25.2% (used 82628 bytes from 327680 bytes) -; Flash: [========= ] 94.3% (used 1483793 bytes from 1572864 bytes) WLEDMM: earlier 91.1 +; RAM: [=== ] 26.0% (used 85244 bytes from 327680 bytes) +; Flash: [==========] 97.4% (used 1532125 bytes from 1572864 bytes) [env:esp32_4MB_M_eth] extends = esp32_4MB_M_base @@ -1223,8 +1291,8 @@ build_flags = ${esp32_4MB_XL_base.build_flags} -D WLEDMM_SAVE_FLASH ;; a humble attempt to save a few extra bytes build_unflags = ${esp32_4MB_XL_base.build_unflags} board_build.partitions = tools/WLED_ESP32_4MB_256KB_FS.csv -; RAM: [=== ] 26.3% (used 86172 bytes from 327680 bytes) -; Flash: [========= ] 85.0% (used 1615569 bytes from 1900544 bytes) +; RAM: [=== ] 26.3% (used 86188 bytes from 327680 bytes) +; Flash: [========= ] 85.2% (used 1619513 bytes from 1900544 bytes) ;; standard framework build for 16MB flash, optimized for speed [env:esp32_16MB_S] @@ -1257,8 +1325,8 @@ build_flags = ${esp32_4MB_M_base.build_flags} board = esp32_16MB board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem ;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem - ; RAM: [== ] 24.4% (used 79916 bytes from 327680 bytes) - ; Flash: [======= ] 67.0% (used 1405701 bytes from 2097152 bytes) + ; RAM: [=== ] 26.0% (used 85244 bytes from 327680 bytes) + ; Flash: [======= ] 73.1% (used 1532125 bytes from 2097152 bytes) ;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation [env:esp32_4MB_M_debug] @@ -1311,8 +1379,8 @@ build_flags = ${esp32_4MB_M_base.build_flags} -D WLED_RELEASE_NAME=esp32_16MB_M_eth ; This will be included in the firmware.bin filename -D WLED_USE_ETHERNET -D WLED_DISABLE_ESPNOW ;; ESP-NOW requires wifi, may crash with ethernet only -; RAM: [== ] 24.5% (used 80348 bytes from 327680 bytes) -; Flash: [======= ] 69.4% (used 1455233 bytes from 2097152 bytes) +; RAM: [=== ] 26.1% (used 85452 bytes from 327680 bytes) +; Flash: [======= ] 73.3% (used 1537945 bytes from 2097152 bytes) [env:esp8266_2MB_S] @@ -1329,8 +1397,7 @@ build_flags = ${common.build_flags_esp8266} ;; -D WLED_DISABLE_INFRARED ;RAM 136 bytes; FLASH 24492 bytes ; -D WLED_DISABLE_2D ; -UWLED_USE_MY_CONFIG - -D WLED_DEBUG_HOST='"192.168.x.x"' ;; to send debug messages over network to host 192.168.x.y - FQDN is also possible - -D WLED_DEBUG_PORT=1768 ;; port for network debugging. default = 7868 + ${common_mm.NetDebug_build_flags} ; -D WLED_DEBUG ; monitor_filters = esp8266_exception_decoder ;; lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation @@ -1351,8 +1418,7 @@ build_flags = ${common.build_flags_esp8266} ;; -D WLED_DISABLE_INFRARED ;RAM 136 bytes; FLASH 24492 bytes ; -D WLED_DISABLE_2D ; -UWLED_USE_MY_CONFIG - -D WLED_DEBUG_HOST='"192.168.x.x"' ;; to send debug messages over network to host 192.168.x.y - FQDN is also possible - -D WLED_DEBUG_PORT=1768 ;; port for network debugging. default = 7868 + ${common_mm.NetDebug_build_flags} ; -D WLED_DEBUG ; monitor_filters = esp8266_exception_decoder ;; lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation @@ -1380,8 +1446,7 @@ build_flags = ${common.build_flags_esp8266} ; -D USERMOD_ARTIFX ; this is compiling but not working due to low memory on 8266 -D USERMOD_BATTERY ;; enable Battery usermod -D USERMOD_BATTERY_USE_LIPO ;; use new "discharging curve" for LiPo cells - -D WLED_DEBUG_HOST='"192.168.x.x"' ;; to send debug messages over network to host 192.168.x.y - FQDN is also possible - -D WLED_DEBUG_PORT=1768 ;; port for network debugging. default = 7868 + ${common_mm.NetDebug_build_flags} ; -D WLED_DEBUG monitor_filters = esp8266_exception_decoder lib_deps = ${esp8266.lib_deps} @@ -1436,8 +1501,7 @@ build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_ALEXA -D WLED_DISABLE_HUESYNC ; -D WLED_DEBUG ${common.debug_flags} ;; un-comment for debug messages - -D WLED_DEBUG_HOST='"192.168.x.x"' ;; to send debug messages over network to host 192.168.x.y - FQDN is also possible - -D WLED_DEBUG_PORT=1768 ;; port for network debugging. default = 7868 + ${common_mm.NetDebug_build_flags} ;; -D WLED_DISABLE_ESPNOW ;; might help in case of WiFi connectivity problems ; -D WLED_DISABLE_LOXONE ; FLASH 1272 bytes ; -D WLED_DISABLE_MQTT ; RAM 216 bytes; FLASH 16496 bytes @@ -1477,8 +1541,7 @@ build_flags = ${common.build_flags_esp8266} -D USERMOD_FOUR_LINE_DISPLAY -D USERMOD_MPU6050_IMU ; gyro/accelero for USERMOD_GAMES (ONLY WORKS IF USERMOD_FOUR_LINE_DISPLAY NOT INCLUDED - I2C SHARING BUG) -D USERMOD_GAMES ; WLEDMM usermod - -D WLED_DEBUG_HOST='"192.168.x.x"' ;; to send debug messages over network to host 192.168.x.y - FQDN is also possible - -D WLED_DEBUG_PORT=1768 ;; port for network debugging. default = 7868 + ${common_mm.NetDebug_build_flags} ; -D WLED_DEBUG monitor_filters = esp8266_exception_decoder lib_deps = ${esp8266.lib_deps} @@ -1529,8 +1592,8 @@ build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} lib_deps = ${esp32_4MB_V4_S_base.esp32_lib_deps} lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation - ; RAM: [=== ] 27.7% (used 90664 bytes from 327680 bytes) - ; Flash: [==========] 95.1% (used 1495497 bytes from 1572864 bytes) + ; RAM: [=== ] 28.1% (used 91960 bytes from 327680 bytes) + ; Flash: [==========] 97.8% (used 1537777 bytes from 1572864 bytes) ; compiled with ESP-IDF 4.4.1 [env:esp32_4MB_V4_M] extends = esp32_4MB_V4_M_base @@ -1541,16 +1604,21 @@ build_flags = ${esp32_4MB_V4_M_base.esp32_build_flags} -D WLED_DISABLE_LOXONE ; FLASH 1272 bytes -D WLED_DISABLE_ALEXA ; RAM 116 bytes; FLASH 13524 bytes -D WLED_DISABLE_HUESYNC ;RAM 122 bytes; FLASH 6308 bytes - ;-D WLED_DISABLE_MQTT ; RAM 216 bytes; FLASH 16496 bytes + -D WLED_DISABLE_MQTT ; RAM 216 bytes; FLASH 16496 bytes ;; softhack007 disabled to stay below 100% flash size -D WLED_DISABLE_INFRARED ;RAM 136 bytes; FLASH 24492 bytes ;; softhack007 disabled to stay below 100% flash size + -D WLEDMM_SAVE_FLASH lib_deps = ${esp32_4MB_V4_M_base.esp32_lib_deps} lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation + ${common_mm.HUB75_lib_ignore} ;; over the flash size limit + ${common_mm.animartrix_lib_ignore} build_unflags = ${esp32_4MB_V4_M_base.build_unflags} -D USERMOD_ANIMARTRIX ;; Tips our memory usage over the limit + -D USERMOD_ARTIFX + -D USERMOD_AUTO_SAVE -D WLED_ENABLE_HUB75MATRIX -;; RAM: [=== ] 25.9% (used 84708 bytes from 327680 bytes) -;; Flash: [==========] 98.5% (used 1549033 bytes from 1572864 bytes) +;; RAM: [=== ] 28.0% (used 91664 bytes from 327680 bytes) +;; Flash: [==========] 99.5% (used 1564945 bytes from 1572864 bytes) ;; V4 build for 16MB flash, optimized for speed [env:esp32_16MB_V4_S] @@ -1621,13 +1689,14 @@ monitor_filters = esp32_exception_decoder extends = esp32_4MB_V4_S_base board = lolin_d32_pro ;board = esp32cam +build_unflags = ${esp32_4MB_V4_S_base.build_unflags} + -D WLED_ENABLE_HUB75MATRIX ;; uses too much flash build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} -D WLED_RELEASE_NAME=esp32_4MB_PSRAM_S -D WLED_WATCHDOG_TIMEOUT=0 #-D WLED_DISABLE_BROWNOUT_DET -D ARDUINO_USB_CDC_ON_BOOT=0 ; needed for arduino-esp32 >=2.0.4; avoids errors on startup -D WLEDMM_FASTPATH ; WLEDMM experimental option. Reduces audio lag (latency), and allows for faster LED framerates. May break compatibility with previous versions. -DBOARD_HAS_PSRAM -D WLED_USE_PSRAM_JSON ;; -D WLED_USE_PSRAM ;; WLED_USE_PSRAM causes major slow-down (slow LEDs) on some ESP32 boards - ;; -DALL_JSON_TO_PSRAM ;; WLEDMM experimental --> try to force all JSON stuff into PSRAM; gives more free heap. -D WLED_DISABLE_LOXONE ; FLASH 1272 bytes -D WLED_DISABLE_HUESYNC ; RAM 122 bytes; FLASH 6308 bytes -D WLED_DISABLE_ALEXA ; RAM 116 bytes; FLASH 13524 bytes @@ -1639,8 +1708,8 @@ build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} ; -D MIC_LOGGER lib_deps = ${esp32_4MB_V4_S_base.esp32_lib_deps} lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation -;; RAM: [== ] 24.3% (used 79524 bytes from 327680 bytes) -;; Flash: [========= ] 93.2% (used 1466389 bytes from 1572864 bytes) +;; RAM: [== ] 20.4% (used 66960 bytes from 327680 bytes) +;; Flash: [==========] 98.7% (used 1553129 bytes from 1572864 bytes) ;; similar to 4MB_PSRAM_S, but optimized for WROVER-E (chip revision >= 3) that doesn't need any workarounds for PSRAM any more ;; tl;dr: its faster on PSRAM. But it will not work on all boards. @@ -1654,6 +1723,7 @@ build_unflags = ${esp32_4MB_V4_S_base.build_unflags} -DARDUINO_EVENT_RUNNING_CORE=1 ;; we want to run wifi on core0, so remove the standard flag -mfix-esp32-psram-cache-issue ;; this fix is not needed any more for revision 3 -mfix-esp32-psram-cache-strategy=memw ;; same as above + -D WLED_ENABLE_HUB75MATRIX ;; uses too much flash build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} -DARDUINO_EVENT_RUNNING_CORE=0 ;; assign Wifi to core0, to have more CPU on core#1 (arduino loop) @@ -1662,7 +1732,6 @@ build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} ;;${Speed_Flags.build_flags} ;; optimize for speed instead of size --> over 100% flash, but works with 256KB filesystem (alternative partitions file) -D WLEDMM_FASTPATH ; WLEDMM experimental option. Reduces audio lag (latency), and allows for faster LED framerates. May break compatibility with previous versions. -DBOARD_HAS_PSRAM -D WLED_USE_PSRAM_JSON ;; -D WLED_USE_PSRAM ;; WLED_USE_PSRAM causes major slow-down (slow LEDs) on some ESP32 boards - ;; -DALL_JSON_TO_PSRAM ;; WLEDMM experimental --> try to force all JSON stuff into PSRAM; gives more free heap. ;;-D CONFIG_ESP32_REV_MIN=3 ;; disables PSRAM bug workarounds in the core, reducing the code size and improving overall performance. -D WLED_RELEASE_NAME=esp32_4MB_PSRAM_REV3_S -D WLED_WATCHDOG_TIMEOUT=0 #-D WLED_DISABLE_BROWNOUT_DET @@ -1679,8 +1748,8 @@ build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} ; -D MIC_LOGGER lib_deps = ${esp32_4MB_V4_S_base.esp32_lib_deps} lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation -;; RAM: [==== ] 35.9% (used 117688 bytes from 327680 bytes) -;; Flash: [========= ] 94.5% (used 1487097 bytes from 1572864 bytes) +;; RAM: [== ] 20.4% (used 66976 bytes from 327680 bytes) +;; Flash: [==========] 97.0% (used 1525833 bytes from 1572864 bytes) ;; PSRAM build env that only leaves 300Kb for filesystem (instead of 1MB), but adds 300kB for program space [env:esp32_4MB_PSRAM_M] @@ -1704,8 +1773,13 @@ build_flags = ${esp32_4MB_V4_M_base.esp32_build_flags} ; -D MIC_LOGGER lib_deps = ${esp32_4MB_V4_M_base.esp32_lib_deps} ;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation -;; RAM: [== ] 24.9% (used 81484 bytes from 327680 bytes) -;; Flash: [======== ] 84.6% (used 1607857 bytes from 1900544 bytes) +;; RAM: [== ] 20.7% (used 67672 bytes from 327680 bytes) +;; Flash: [========= ] 90.4% (used 1718421 bytes from 1900544 bytes) + + +# ------------------------------------------------------------------------------ +# esp32-S3 environments +# ------------------------------------------------------------------------------ [env:esp32S3_8MB_M] @@ -1840,6 +1914,82 @@ board_build.partitions = tools/WLED_ESP32_8MB.csv ; RAM: [=== ] 25.8% (used 84544 bytes from 327680 bytes) ; Flash: [======== ] 78.1% (used 1638737 bytes from 2097152 bytes) +;; MM for esp32-s3 zero/supermini and lolin S3 mini boards - fastpath, optimize for speed +[env:esp32S3_4MB_PSRAM_S] +extends = env:esp32S3_8MB_S +board = lolin_s3_mini ;; -S3 mini: 4MB flash 2MB PSRAM +board_build.partitions = tools/WLED_ESP32_4MB_256KB_FS.csv ;; 1.8MB firmware, 256KB filesystem (esptool erase_flash needed when changing from "standard WLED" partitions) +;; board_build.partitions = tools/WLED_ESP32_4MB_512KB_FS.csv ;; 1.7MB firmware, 500KB filesystem (esptool erase_flash needed when changing from "standard WLED" partitions) +build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-indentation -Wno-format-truncation + ${common_mm.build_flags_S} + -D WLED_RELEASE_NAME=esp32S3_4MB_S + -DBOARD_HAS_PSRAM -D WLED_USE_PSRAM_JSON ;; -D WLED_USE_PSRAM + -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=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} ;; optimize for speed instead of size + -D WLEDMM_FASTPATH ;; WLEDMM experimental option. Reduces audio lag (latency), and allows for faster LED framerates. May break compatibility with previous versions. + ${common_mm.animartrix_build_flags} + -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0 + -D WLED_DISABLE_LOXONE ; FLASH 1272 bytes + -D WLED_DISABLE_ALEXA ; RAM 116 bytes; FLASH 13524 bytes + -D WLED_DISABLE_MQTT ; RAM 216 bytes; FLASH 16496 bytes + -D WLED_DISABLE_HUESYNC ;RAM 122 bytes; FLASH 6308 bytes + -D WLED_DISABLE_INFRARED ;RAM 136 bytes; FLASH 24492 bytes + -D LEDPIN=21 + -D BTNPIN=-1 -D RLYPIN=-1 -D IRPIN=-1 + -D HW_PIN_SDA=12 -D HW_PIN_SCL=13 + -D AUDIOPIN=-1 + -D SR_DMTYPE=1 -D I2S_SDPIN=5 -D I2S_WSPIN=6 -D I2S_CKPIN=4 -D MCLK_PIN=7 + ; -D WLED_DEBUG + ; -D SR_DEBUG + ; -D MIC_LOGGER +lib_ignore = + IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation +; RAM: [== ] 20.4% (used 66908 bytes from 327680 bytes) +; Flash: [========= ] 87.1% (used 1655529 bytes from 1900544 bytes) + +;; MM for esp32-s3 zero/supermini and lolin S3 mini boards - standard +[env:esp32S3_4MB_PSRAM_M] +extends = env:esp32S3_8MB_M +board = lolin_s3_mini ;; -S3 mini: 4MB flash 2MB PSRAM +board_build.partitions = ${esp32.default_partitions} +build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-indentation -Wno-format-truncation + ${common_mm.build_flags_S} ${common_mm.build_flags_M} + -D WLED_RELEASE_NAME=esp32S3_4MB_M + -DBOARD_HAS_PSRAM -D WLED_USE_PSRAM_JSON ;; -D WLED_USE_PSRAM + -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=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} + -D LEDPIN=21 + -D BTNPIN=-1 -D RLYPIN=-1 -D IRPIN=-1 -D AUDIOPIN=-1 + -D HW_PIN_SDA=12 -D HW_PIN_SCL=13 + -D SR_DMTYPE=1 -D I2S_SDPIN=5 -D I2S_WSPIN=6 -D I2S_CKPIN=4 -D MCLK_PIN=7 + -D WLED_DISABLE_LOXONE ; FLASH 1272 bytes - disabled to stay below 100% + -D WLED_DISABLE_HUESYNC ; RAM 122 bytes; FLASH 6308 bytes - disabled to stay below 100% + -D WLED_DISABLE_INFRARED ; RAM 136 bytes; FLASH 24492 bytes - disabled to stay below 100% + ;; -D WLED_DISABLE_ALEXA ; RAM 116 bytes; FLASH 13524 bytes + ;; -D WLED_DISABLE_MQTT ; RAM 216 bytes; FLASH 16496 bytes + -D WLEDMM_SAVE_FLASH + ; -D WLED_DEBUG + ; -D SR_DEBUG + ; -D MIC_LOGGER +lib_deps = ${esp32s3.lib_deps} ${common_mm.lib_deps_S} ${common_mm.lib_deps_V4_M} +lib_ignore = + IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation +; RAM: [== ] 20.6% (used 67532 bytes from 327680 bytes) +; Flash: [==========] 98.4% (used 1547445 bytes from 1572864 bytes) + + +# ------------------------------------------------------------------------------ +# esp32-S2 environments +# ------------------------------------------------------------------------------ + ;; MM for Adafruit QT Py ESP32-S2 -> 4MB flash, PSRAM, and tinyUF2 bootloader ;; to ewowi - i'll optimize this entry later, as a few things can be inherited for sure. To softhack: sure ;-) @@ -1858,6 +2008,8 @@ build_unflags = ${common.build_unflags} -D WLED_ENABLE_DMX ;; disabled because it does not work with ESP-IDF 4.4.x (buggy driver in SparkFunDMX) -D WLED_ENABLE_DMX_INPUT ;; needs more testing -DWLEDMM_FASTPATH ;; needs more testing on -S2 + -DUSERMOD_RTC + -D WLED_ENABLE_HUB75MATRIX build_flags = ${common.build_flags} ${esp32s2.build_flags} ; ${Debug_Flags.build_flags} ;ewowi: enabling debug causes Error: The program size (1463330 bytes) is greater than maximum allowed (1441792 bytes) @@ -1871,12 +2023,12 @@ build_flags = ${common.build_flags} ${esp32s2.build_flags} -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 -D SERVERNAME='"WLED-S2"' -DBOARD_HAS_PSRAM -D WLED_USE_PSRAM_JSON ;; -D WLED_USE_PSRAM - -DALL_JSON_TO_PSRAM ;; WLEDMM experimental --> try to force all JSON stuff into PSRAM; gives more free heap. -D WLED_DISABLE_LOXONE ;; FLASH 1272 bytes -D WLED_DISABLE_HUESYNC ;; RAM 122 bytes; FLASH 6308 bytes -D WLED_DISABLE_ALEXA ;; RAM 116 bytes; FLASH 13524 bytes ; -D WLED_DISABLE_MQTT ;; RAM 216 bytes; FLASH 16496 bytes -D WLED_DISABLE_INFRARED ;; RAM 136 bytes; FLASH 24492 bytes + -D WLEDMM_SAVE_FLASH -D LEDPIN=39 ;; onboard neopixel LED. Attach your own LEDs to GPIO 7 or GPIO 6 -D BTNPIN=0 ;-D RLYPIN=6 @@ -1894,8 +2046,8 @@ lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation OneWire ; not needed as we don't include USERMOD_DALLASTEMPERATURE monitor_filters = esp32_exception_decoder -; RAM: [=== ] 33.0% (used 108276 bytes from 327680 bytes) -; Flash: [==========] 97.9% (used 1411114 bytes from 1441792 bytes) !!! 98% +; RAM: [== ] 21.5% (used 70448 bytes from 327680 bytes) +; Flash: [==========] 99.4% (used 1432846 bytes from 1441792 bytes) ;; MM environment for generic ESP32-S2, with PSRAM, 4MB flash (300kB filesystem to have more program space) ;; PINs assignments optimized for use with serg74 "mini shield" @@ -1915,6 +2067,7 @@ build_unflags = ${common.build_unflags} -D WLED_ENABLE_DMX ;; disabled because it does not work with ESP-IDF 4.4.x (buggy driver in SparkFunDMX) -D WLED_ENABLE_DMX_INPUT ;; needs more testing -DWLEDMM_FASTPATH ;; needs more testing on -S2 + -D WLED_ENABLE_HUB75MATRIX build_flags = ${common.build_flags} ${esp32s2.build_flags} ;; ${Debug_Flags.build_flags} -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0 @@ -1922,7 +2075,6 @@ build_flags = ${common.build_flags} ${esp32s2.build_flags} -Wno-misleading-indentation -Wno-format-truncation -D WLED_RELEASE_NAME=esp32s2_4MB_M -DBOARD_HAS_PSRAM -D WLED_USE_PSRAM_JSON ;; -D WLED_USE_PSRAM - ;; -DALL_JSON_TO_PSRAM ;; WLEDMM experimental --> try to force all JSON stuff into PSRAM; gives more free heap. -DLOLIN_WIFI_FIX -DWLEDMM_WIFI_POWERON_HACK ;; seems to work much better with this -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 -D WLED_DISABLE_ADALIGHT ;; disables serial protocols, as the board only has CDC USB @@ -1930,6 +2082,7 @@ build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_DISABLE_ALEXA ;; save flash space -D WLED_DISABLE_HUESYNC ;; save flash space -D WLED_DISABLE_LOXONE ;; save flash space + -D WLEDMM_SAVE_FLASH -D AUDIOPIN=-1 -D BTNPIN=-1 -D IRPIN=-1 -D LEDPIN=16 ;; second led pin = 18 @@ -1945,8 +2098,13 @@ lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation OneWire ; not needed as we don't include USERMOD_DALLASTEMPERATURE monitor_filters = esp32_exception_decoder -; RAM: [=== ] 29.7% (used 97376 bytes from 327680 bytes) -; Flash: [======== ] 81.4% (used 1547834 bytes from 1900544 bytes) +; RAM: [== ] 21.8% (used 71304 bytes from 327680 bytes) +; Flash: [======== ] 84.0% (used 1596970 bytes from 1900544 bytes) + + +# ------------------------------------------------------------------------------ +# esp32-C3 environments +# ------------------------------------------------------------------------------ ;; MM environment for generic ESP32-C3 -> 4MB flash, no PSRAM @@ -1986,6 +2144,7 @@ build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLEDMM_WIFI_POWERON_HACK -DLOLIN_WIFI_FIX ;; use this _only_ if your device is not able to make a WiFI connection! ;-D WLED_DISABLE_INFRARED ;; save flash space ;-D WLED_DISABLE_ALEXA ;; save flash space + -D WLEDMM_SAVE_FLASH -D LEDPIN=8 ;; onboard neopixel 5x5 Matrix. Attach your own LEDs to GPIO 20 -D BTNPIN=9 ; -D STATUSLED=10 ;; onboard LED @@ -1999,8 +2158,8 @@ lib_ignore = ;IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation OneWire ; not needed as we don't include USERMOD_DALLASTEMPERATURE U8g2 ; not needed as we don't include USERMOD_FOUR_LINE_DISPLAY -; RAM: [== ] 23.7% (used 77780 bytes from 327680 bytes) -; Flash: [========= ] 93.9% (used 1477456 bytes from 1572864 bytes) +; RAM: [=== ] 25.9% (used 84884 bytes from 327680 bytes) +; Flash: [==========] 98.9% (used 1555608 bytes from 1572864 bytes) ;; MM environment for ESP32-C3 "mini" and "super mini" -> flash mode "dio" instead of "qio" (see #101) [env:esp32c3mini_dio_4MB_M] @@ -2018,6 +2177,8 @@ build_flags = ${env:esp32c3dev_4MB_M.build_flags} -D WLED_DISABLE_BROWNOUT_DET ;; the board only has a 500mA LDO, better to disable brownout detection -D WLED_DISABLE_ADALIGHT ;; to disable serial protocols for boards with CDC USB (Serial RX will receive junk commands, unless its pulled down by resistor) -D HW_PIN_SDA=0 -D HW_PIN_SCL=1 ;; avoid pin conflicts +; RAM: [=== ] 25.8% (used 84700 bytes from 327680 bytes) +; Flash: [==========] 98.7% (used 1552582 bytes from 1572864 bytes) ;; MM environment for "seeed xiao -C3" boards [env:seeed_esp32c3_4MB_S] @@ -2029,6 +2190,7 @@ board_build.flash_mode = qio upload_speed = 460800 build_unflags = ${env:esp32c3dev_4MB_M.build_unflags} -DWLEDMM_FASTPATH ;; needs more testing on -C3 + -D WLED_ENABLE_HUB75MATRIX ;; not enough pins build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0 ${common_mm.build_flags_S} -Wno-misleading-indentation -Wno-format-truncation @@ -2048,21 +2210,24 @@ build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_USE_MY_CONFIG ;-D WLED_DEBUG -D SR_DEBUG lib_deps = ${esp32c3.lib_deps} ${common_mm.lib_deps_S} -;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation +lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation monitor_filters = esp32_exception_decoder -; RAM: [== ] 23.8% (used 77908 bytes from 327680 bytes) -; Flash: [========= ] 90.9% (used 1429302 bytes from 1572864 bytes) - +; RAM: [== ] 23.6% (used 77460 bytes from 327680 bytes) +; Flash: [========= ] 91.7% (used 1442092 bytes from 1572864 bytes) # ------------------------------------------------------------------------------ # custom board environments # ------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ +# wemos shields +# ------------------------------------------------------------------------------ ;https://www.tindie.com/products/serg74/wled-shield-board-for-addressable-leds/ ;https://www.tindie.com/products/moonmodules/shield-board-for-esp32-for-wled-addressable-leds/ [wemos_shield_esp32_4MB_S_base] extends = esp32_4MB_S_base -build_flags = ${esp32_4MB_S_base.build_flags} +pinout_build_flags = -D ABL_MILLIAMPS_DEFAULT=9500 ; Wemos max 10A -D LEDPIN=16 -D RLYPIN=19 @@ -2077,10 +2242,11 @@ build_flags = ${esp32_4MB_S_base.build_flags} -D PIR_SENSOR_PIN=-1 -D PWM_PIN=-1 ; -D WLED_USE_MY_CONFIG +build_flags = ${esp32_4MB_S_base.build_flags} ${common_mm.build_disable_sync_interfaces} ${wemos_shield_esp32_4MB_S_base.pinout_build_flags} [wemos_shield_esp32_4MB_M_base] extends = wemos_shield_esp32_4MB_S_base -build_flags = ${wemos_shield_esp32_4MB_S_base.build_flags} ${common_mm.build_flags_M} +build_flags = ${esp32_4MB_M_base.build_flags} ${wemos_shield_esp32_4MB_S_base.pinout_build_flags} lib_deps = ${wemos_shield_esp32_4MB_S_base.lib_deps} ${common_mm.lib_deps_M} [wemos_shield_esp32_4MB_XL_base] @@ -2092,13 +2258,15 @@ lib_deps = ${wemos_shield_esp32_4MB_M_base.lib_deps} ${common_mm.lib_deps_XL} extends = wemos_shield_esp32_4MB_S_base build_flags = ${wemos_shield_esp32_4MB_S_base.build_flags} -D WLED_RELEASE_NAME=wemos_shield_esp32_4MB_S +; RAM: [=== ] 25.4% (used 83124 bytes from 327680 bytes) +; Flash: [======== ] 83.4% (used 1311629 bytes from 1572864 bytes) [env:wemos_shield_esp32_4MB_M] extends = wemos_shield_esp32_4MB_M_base build_flags = ${wemos_shield_esp32_4MB_M_base.build_flags} -D WLED_RELEASE_NAME=wemos_shield_esp32_4MB_M -; RAM: [== ] 24.4% (used 79820 bytes from 327680 bytes) -; Flash: [========= ] 88.6% (used 1393421 bytes from 1572864 bytes) +; RAM: [=== ] 26.0% (used 85252 bytes from 327680 bytes) +; Flash: [==========] 97.4% (used 1531865 bytes from 1572864 bytes) [env:wemos_shield_esp32_4MB_ICS4343x_M] extends = wemos_shield_esp32_4MB_M_base @@ -2186,16 +2354,22 @@ board = esp32_16MB board_build.partitions = tools/WLED_ESP32_16MB.csv ;; WLED standard for 16MB flash: 2MB firmware, 12 MB filesystem ;board_build.partitions = tools/WLED_ESP32_16MB_9MB_FS.csv ;; WLED extended for 16MB flash: 3.2MB firmware, 9 MB filesystem + +# ------------------------------------------------------------------------------ +# special boards and controlers +# ------------------------------------------------------------------------------ + ;https://www.athom.tech/blank-1/wled-esp32-music-addressable-led-strip-controller [env:athom_music_esp32_4MB_M] extends = esp32_4MB_M_base -build_flags = ${esp32_4MB_M_base.build_flags} ${Athom_PDMmic.build_flags} +build_flags = ${esp32_4MB_M_base.build_flags} + ${Athom_PDMmic.build_flags} -D WLED_AP_SSID_UNIQUE -D WLED_RELEASE_NAME=athom_music_esp32_4MB_M -D ABL_MILLIAMPS_DEFAULT=14500 ; max 15A -D WLED_DISABLE_MQTT -D WLED_DISABLE_LOXONE -D WLED_DISABLE_ADALIGHT ;to get 4ld working - -D BTNPIN=0 -D RLYPIN=2 -D IRPIN=25 -D IRTYPE=9 -D LEDPIN=18 -D + -D BTNPIN=0 -D RLYPIN=2 -D IRPIN=25 -D IRTYPE=9 -D LEDPIN=18 -D AUDIOPIN=-1 ; -D TEMPERATURE_PIN=23 -D FLD_PIN_SCL=-1 -D FLD_PIN_SDA=-1 ; use global! @@ -2205,6 +2379,8 @@ build_flags = ${esp32_4MB_M_base.build_flags} ${Athom_PDMmic.build_flags} ; -D PIR_SENSOR_PIN=-1 ; -D PWM_PIN=-1 ; -D WLED_USE_MY_CONFIG +; RAM: [=== ] 25.9% (used 84948 bytes from 327680 bytes) +; Flash: [==========] 95.9% (used 1509113 bytes from 1572864 bytes) ;https://shop.myhome-control.de/Elektronik/ [env:abc_wled_controller_v43_S] @@ -2224,6 +2400,8 @@ build_flags = ${esp32_4MB_S_base.build_flags} -D SR_DMTYPE=4 -D I2S_SDPIN=32 -D I2S_WSPIN=15 -D I2S_CKPIN=14 -D MCLK_PIN=0 ; generic i2s with mclk 0 -D SR_SQUELCH=1 -D SR_GAIN=60 ; increrase squelch if noise, in test 0 is okay, but only slightly -D SR_FREQ_PROF=1 ; Generic line in +; RAM: [=== ] 25.9% (used 84884 bytes from 327680 bytes) +; Flash: [========= ] 88.7% (used 1395249 bytes from 1572864 bytes) ; ESP32 WLED pico board with builtin ICS-43432 microphpone [env:esp32_pico_4MB_M] @@ -2257,19 +2435,18 @@ build_flags = ${esp32_4MB_M_base.build_flags} ; -D WLED_DISABLE_MQTT ; -D WLED_DISABLE_INFRARED ; -D WLED_ENABLE_DMX -; RAM: [== ] 24.4% (used 79812 bytes from 327680 bytes) -; Flash: [========= ] 90.4% (used 1422581 bytes from 1572864 bytes) - +; RAM: [=== ] 26.0% (used 85236 bytes from 327680 bytes) +; Flash: [==========] 97.1% (used 1527049 bytes from 1572864 bytes) ;; experimental -;; PICO environment with ESP-IDF v4.4.1 / arduino-esp32 v2.0.4 +;; PICO environment with ESP-IDF v4.4.4 / arduino-esp32 v2.0.9 [env:esp32_pico_4MB_V4_S] extends = esp32_4MB_V4_S_base board = pico32 -;platform = espressif32@~5.2.0 ;; alternative platform, might help in case you experience bootloops due to corrupted flash filesystem -;platform_packages = upload_speed = 256000 ;; or 115200 ;; or 460800 ; or 921600 (slower speeds are better when flashing without a soldered connection) +build_unflags = ${esp32_4MB_V4_S_base.build_unflags} + -D WLED_ENABLE_HUB75MATRIX ;; not enough pins build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} -D ARDUINO_USB_CDC_ON_BOOT=0 ; needed for arduino-esp32 >=2.0.4; avoids errors on startup -D WLED_RELEASE_NAME=esp32_pico_4MB_V4_S @@ -2277,13 +2454,8 @@ build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} -D SERVERNAME='"WLED-pico32-V4"' -D WLED_WATCHDOG_TIMEOUT=0 ; -D WLED_WATCHDOG_TIMEOUT=60 - ; -D WLEDMM_FASTPATH ; WLEDMM experimental option. Reduces audio lag (latency), and allows for faster LED framerates. May break compatibility with previous versions. -D WLED_DISABLE_ADALIGHT ;; WLEDMM this board does not have a serial-to-USB chip. Better to disable serial protocols, to avoid crashes (see upstream #3128) - ; -D WLED_DISABLE_LOXONE - ; -D WLED_DISABLE_ALEXA - ; -D WLED_DISABLE_HUESYNC - ; -D WLED_DISABLE_MQTT - ; -D WLED_DISABLE_INFRARED + ${common_mm.NetDebug_build_flags} ;; net debug is not included in normal _S builds ; -D WLED_DEBUG ; -D SR_DEBUG -D LEDPIN=2 @@ -2295,30 +2467,46 @@ build_flags = ${esp32_4MB_V4_S_base.esp32_build_flags} -D SR_ENABLE_DEFAULT ;; enable audioreactive at first start - no need to manually set "enable", then reboot ; -D WLED_USE_MY_CONFIG lib_deps = ${esp32_4MB_V4_S_base.esp32_lib_deps} -;lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation -; RAM: [=== ] 25.4% (used 83144 bytes from 327680 bytes) -; Flash: [==========] 96.4% (used 1516029 bytes from 1572864 bytes) +lib_ignore = + IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation + ${common_mm.HUB75_lib_ignore} +; RAM: [=== ] 27.9% (used 91528 bytes from 327680 bytes) +; Flash: [==========] 96.7% (used 1521457 bytes from 1572864 bytes) ; [env:adafruit_matrixportal_esp32s3] -platform = espressif32 ; latest +; ESP32-S3 processor, 8 MB flash, 2 MB of PSRAM, dedicated driver pins for HUB75 +extends = esp32_4MB_V4_M_base +platform = ${esp32.platformV4_xp} ;; 6.5.0 = first platform release supporting matrixportal +platform_packages = ${esp32.platformV4_packages_xp} ;; arduino-esp32 2.0.14 needed - previous versions were missing files for matrixportal board = adafruit_matrixportal_esp32s3 -extends = esp32_4MB_V4_S_base + +board_build.partitions = ${esp32.large_partitions} +board_build.f_flash = 80000000L +board_build.flash_mode = qio + build_unflags = ${env:esp32S3_8MB_M.build_unflags} ;; use the same as "normal" S3 buildenv + -D ARDUINO_USB_CDC_ON_BOOT=1 ;; fix warning: "ARDUINO_USB_CDC_ON_BOOT" redefined; comment out for Serial debug build_flags = ${common.build_flags} ${esp32s3.build_flags} -Wno-misleading-indentation -Wno-format-truncation ${common_mm.build_flags_S} -D WLED_RELEASE_NAME=matrixportal_esp32s3 - -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode + ; Serial debug enabled -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=1 ;; for Hardware-CDC USB mode + -D ARDUINO_USB_CDC_ON_BOOT=0 -D WLED_DISABLE_ADALIGHT ;; disables serial protocols - recommended for Hardware-CDC USB (Serial RX will receive junk commands when RX pin is unconnected, unless its pulled down by resistor) ${common_mm.animartrix_build_flags} ${common_mm.build_disable_sync_interfaces} -D LOLIN_WIFI_FIX ;; try this in case Wifi does not work -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0 -D WLED_USE_PSRAM -DBOARD_HAS_PSRAM ; tells WLED that PSRAM shall be used - -D WLED_ENABLE_HUB75MATRIX -D NO_GFX ;-D SPIRAM_FRAMEBUFFER + ${common_mm.HUB75_build_flags} -D DEFAULT_LED_TYPE=101 -lib_deps = ${esp32_4MB_V4_S_base.esp32_lib_deps} - ${common_mm.animartrix_lib_deps} +lib_deps = ${esp32s3.lib_deps} ${common_mm.lib_deps_S} ;; ;; do not include ${esp32.lib_depsV4} !!!! + ${common_mm.animartrix_lib_deps} + ${common_mm.HUB75_lib_deps} + lib_ignore = IRremoteESP8266 ; use with WLED_DISABLE_INFRARED for faster compilation monitor_filters = esp32_exception_decoder +; +; RAM: [== ] 20.4% (used 66984 bytes from 327680 bytes) +; Flash: [========= ] 94.8% (used 1491489 bytes from 1572864 bytes) diff --git a/tools/ESP32-Chip_info.hpp b/tools/ESP32-Chip_info.hpp index 893165985a..417ee44915 100644 --- a/tools/ESP32-Chip_info.hpp +++ b/tools/ESP32-Chip_info.hpp @@ -543,6 +543,8 @@ void show_psram_info_part2(void) void showRealSpeed() { //Serial.begin(115200); + if (!Serial) return; // Avoid writing to unconnected USB-CDC + Serial.flush(); Serial.println(F("\n")); for(int aa=0; aa<65; aa++) Serial.print("="); Serial.println(); diff --git a/tools/WLED_ESP32_4MB_512KB_FS.csv b/tools/WLED_ESP32_4MB_512KB_FS.csv new file mode 100644 index 0000000000..5281a61244 --- /dev/null +++ b/tools/WLED_ESP32_4MB_512KB_FS.csv @@ -0,0 +1,7 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x1B0000, +app1, app, ota_1, 0x1C0000,0x1B0000, +spiffs, data, spiffs, 0x370000,0x80000, +coredump, data, coredump,,64K \ No newline at end of file diff --git a/tools/WLED_ESP32_4MB_700k_FS.csv b/tools/WLED_ESP32_4MB_700k_FS.csv new file mode 100644 index 0000000000..39c88e5437 --- /dev/null +++ b/tools/WLED_ESP32_4MB_700k_FS.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x1A0000, +app1, app, ota_1, 0x1B0000,0x1A0000, +spiffs, data, spiffs, 0x350000,0xB0000, diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index be86186ea8..82211e8274 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -1,5 +1,24 @@ #pragma once +/* + @title MoonModules WLED - audioreactive usermod + @file audio_reactive.h + @repo https://github.com/MoonModules/WLED, submit changes to this file as PRs to MoonModules/WLED + @Authors https://github.com/MoonModules/WLED/commits/mdev/ + @Copyright © 2024 Github MoonModules Commit Authors (contact moonmodules@icloud.com for details) + @license GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 + + This file is part of the MoonModules WLED fork also known as "WLED-MM". + WLED-MM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + WLED-MM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with WLED-MM. If not, see . +*/ + + #include "wled.h" #ifdef ARDUINO_ARCH_ESP32 @@ -353,9 +372,6 @@ constexpr uint16_t samplesFFT_2 = 256; // meaningfull part of FFT resul // These are the input and output vectors. Input vectors receive computed results from FFT. static float vReal[samplesFFT] = {0.0f}; // FFT sample inputs / freq output - these are our raw result bins static float vImag[samplesFFT] = {0.0f}; // imaginary parts -#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT -static float windowWeighingFactors[samplesFFT] = {0.0f}; -#endif #ifdef FFT_MAJORPEAK_HUMAN_EAR static float pinkFactors[samplesFFT] = {0.0f}; // "pink noise" correction factors @@ -381,7 +397,14 @@ constexpr float binWidth = SAMPLE_RATE / (float)samplesFFT; // frequency range o #include #ifdef UM_AUDIOREACTIVE_USE_NEW_FFT -static ArduinoFFT FFT = ArduinoFFT( vReal, vImag, samplesFFT, SAMPLE_RATE, windowWeighingFactors); +#if defined(FFT_LIB_REV) && FFT_LIB_REV > 0x19 + // arduinoFFT 2.x has a slightly different API + static ArduinoFFT FFT = ArduinoFFT( vReal, vImag, samplesFFT, SAMPLE_RATE, true); +#else + // recommended version optimized by @softhack007 (API version 1.9) + static float windowWeighingFactors[samplesFFT] = {0.0f}; // cache for FFT windowing factors + static ArduinoFFT FFT = ArduinoFFT( vReal, vImag, samplesFFT, SAMPLE_RATE, windowWeighingFactors); +#endif #else static arduinoFFT FFT = arduinoFFT(vReal, vImag, samplesFFT, SAMPLE_RATE); #endif @@ -564,7 +587,7 @@ void FFTcode(void * parameter) newZeroCrossingCount++; } } - newZeroCrossingCount = (newZeroCrossingCount*2)/3; // reduce value so it typicially stays below 256 + newZeroCrossingCount = (newZeroCrossingCount*2)/3; // reduce value so it typically stays below 256 zeroCrossingCount = newZeroCrossingCount; // update only once, to avoid that effects pick up an intermediate value // release highest sample to volume reactive effects early - not strictly necessary here - could also be done at the end of the function @@ -602,6 +625,7 @@ void FFTcode(void * parameter) #endif FFT.compute( FFTDirection::Forward ); // Compute FFT FFT.complexToMagnitude(); // Compute magnitudes + vReal[0] = 0; // The remaining DC offset on the signal produces a strong spike on position 0 that should be eliminated to avoid issues. #else FFT.DCRemoval(); // let FFT lib remove DC component, so we don't need to care about this in getSamples() @@ -616,6 +640,9 @@ void FFTcode(void * parameter) FFT.ComplexToMagnitude(); // Compute magnitudes #endif + float last_majorpeak = FFT_MajorPeak; + float last_magnitude = FFT_Magnitude; + #ifdef FFT_MAJORPEAK_HUMAN_EAR // scale FFT results for(uint_fast16_t binInd = 0; binInd < samplesFFT; binInd++) @@ -623,11 +650,19 @@ void FFTcode(void * parameter) #endif #ifdef UM_AUDIOREACTIVE_USE_NEW_FFT + #if defined(FFT_LIB_REV) && FFT_LIB_REV > 0x19 + // arduinoFFT 2.x has a slightly different API + FFT.majorPeak(&FFT_MajorPeak, &FFT_Magnitude); + #else FFT.majorPeak(FFT_MajorPeak, FFT_Magnitude); // let the effects know which freq was most dominant + #endif #else - FFT.MajorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant + FFT.MajorPeak(&FFT_MajorPeak, &FFT_Magnitude); #endif + if (FFT_MajorPeak < (SAMPLE_RATE / samplesFFT)) {FFT_MajorPeak = 1.0f; FFT_Magnitude = 0;} // too low - use zero + if (FFT_MajorPeak > (0.42f * SAMPLE_RATE)) {FFT_MajorPeak = last_majorpeak; FFT_Magnitude = last_magnitude;} // too high - keep last peak + #ifdef FFT_MAJORPEAK_HUMAN_EAR // undo scaling - we want unmodified values for FFTResult[] computations for(uint_fast16_t binInd = 0; binInd < samplesFFT; binInd++) @@ -914,7 +949,7 @@ static void postProcessFFTResults(bool noiseGateOpen, int numberOfChannels) // p if (post_gain < 1.0f) post_gain = ((post_gain -1.0f) * 0.8f) +1.0f; currentResult *= post_gain; } - fftResult[i] = constrain((int)currentResult, 0, 255); + fftResult[i] = max(min((int)(currentResult+0.5f), 255), 0); // +0.5 for proper rounding } } //////////////////// @@ -1564,7 +1599,7 @@ class AudioReactive : public Usermod { transmitData.zeroCrossingCount = zeroCrossingCount; for (int i = 0; i < NUM_GEQ_CHANNELS; i++) { - transmitData.fftResult[i] = (uint8_t)constrain(fftResult[i], 0, 254); + transmitData.fftResult[i] = fftResult[i]; } transmitData.FFT_Magnitude = my_magnitude; @@ -1793,6 +1828,11 @@ class AudioReactive : public Usermod { #endif delay(100); // Give that poor microphone some time to setup. + #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) + if ((i2sckPin == I2S_PIN_NO_CHANGE) && (i2ssdPin >= 0) && (i2swsPin >= 0) + && ((dmType == 1) || (dmType == 4)) ) dmType = 51; // dummy user support: SCK == -1 --means--> PDM microphone + #endif + useInputFilter = 2; // default: DC blocker switch (dmType) { #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) diff --git a/usermods/audioreactive/audio_source.h b/usermods/audioreactive/audio_source.h index 8f552bbc62..54d357e70a 100644 --- a/usermods/audioreactive/audio_source.h +++ b/usermods/audioreactive/audio_source.h @@ -1,4 +1,25 @@ #pragma once + +/* + @title MoonModules WLED - audioreactive usermod + @file audio_source.h + @repo https://github.com/MoonModules/WLED, submit changes to this file as PRs to MoonModules/WLED + @Authors https://github.com/MoonModules/WLED/commits/mdev/ + @Copyright © 2024 Github MoonModules Commit Authors (contact moonmodules@icloud.com for details) + @license GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 + + This file is part of the MoonModules WLED fork also known as "WLED-MM". + WLED-MM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + WLED-MM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with WLED-MM. If not, see . + +*/ + + #ifdef ARDUINO_ARCH_ESP32 #include #include "wled.h" @@ -186,8 +207,12 @@ class I2SSource : public AudioSource { .communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_I2S), //.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, #ifdef WLEDMM_FASTPATH + #if CONFIG_IDF_TARGET_ESP32 && !defined(BOARD_HAS_PSRAM) // still need to test on boards with PSRAM + .intr_alloc_flags = ESP_INTR_FLAG_IRAM|ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3, // IRAM flag reduces missed samples + #else .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3, // seems to reduce noise - .dma_buf_count = 28, // 160ms buffer (128 * dma_buf_count / sampleRate) + #endif + .dma_buf_count = 24, // 140ms buffer (128 * dma_buf_count / sampleRate) #else .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2, .dma_buf_count = 8, 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 c15f116ee8..adb5b37e3f 100644 --- a/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h +++ b/usermods/usermod_v2_auto_playlist/usermod_v2_auto_playlist.h @@ -1,5 +1,25 @@ #pragma once +/* + @title MoonModules WLED - auto-playlist usermod + @file usermod_v2_auto_playlist.h + @repo https://github.com/MoonModules/WLED, submit changes to this file as PRs to MoonModules/WLED + @Authors https://github.com/MoonModules/WLED/commits/mdev/ + @Copyright © 2024 Github MoonModules Commit Authors (contact moonmodules@icloud.com for details) + @license GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 + + This file is part of the MoonModules WLED fork also known as "WLED-MM". + WLED-MM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + WLED-MM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with WLED-MM. If not, see . + +*/ + + #ifdef WLED_DEBUG #ifndef USERMOD_AUTO_PLAYLIST_DEBUG #define USERMOD_AUTO_PLAYLIST_DEBUG @@ -85,7 +105,8 @@ class AutoPlaylistUsermod : public Usermod { // gets called once at boot. Do all initialization that doesn't depend on // network here void setup() { - USER_PRINTLN("AutoPlaylistUsermod"); + USER_PRINT(F("AutoPlaylistUsermod startup; enabled = ")); + USER_PRINT(enabled ? F("true"):F("false")); USER_PRINTLN(F(".")); initDone = true; } @@ -301,6 +322,8 @@ class AutoPlaylistUsermod : public Usermod { if (bri == 0) return; + if(!functionality_enabled) return; + um_data_t *um_data; if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { diff --git a/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h b/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h index cc6d7b2943..898545716d 100644 --- a/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h +++ b/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h @@ -488,7 +488,7 @@ void FourLineDisplayUsermod::draw2x2String(uint8_t col, uint8_t row, const char if (!typeOK || !enabled) return; if (u8x8 == nullptr) return; if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex -#if defined(ARDUINO_ARCH_ESP32) && !defined(OLD_4LD_FONTS) // WLEDMM use nicer 2x2 font on ESP32 +#if defined(ARDUINO_ARCH_ESP32) && !defined(OLD_4LD_FONTS) && !defined(WLEDMM_SAVE_FLASH) // WLEDMM use nicer 2x2 font on ESP32 if (lineHeight>1) { // WLEDMM use 2x3 on 128x64 displays //u8x8->setFont(u8x8_font_profont29_2x3_r); // sans serif 2x3 u8x8->setFont(u8x8_font_courB18_2x3_r); // courier bold 2x3 diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 5fa0a0fe07..77efb26feb 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -1126,7 +1126,7 @@ static const char _data_FX_MODE_RUNNING_RANDOM[] PROGMEM = "Stream ☾@!,Zone si uint16_t larson_scanner(bool dual) { uint16_t counter = strip.now * ((SEGMENT.speed >> 2) +8); uint16_t index = counter * SEGLEN >> 16; - if (SEGENV.call == 0) SEGENV.setUpLeds(); // WLEDMM use lossless getPixelColor() + if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} // WLEDMM use lossless getPixelColor() SEGMENT.fade_out(SEGMENT.intensity); @@ -1333,12 +1333,12 @@ uint16_t mode_fire_flicker(void) { byte r = (SEGCOLOR(0) >> 16); byte g = (SEGCOLOR(0) >> 8); byte b = (SEGCOLOR(0) ); - byte lum = (SEGMENT.palette == 0) ? MAX(w, MAX(r, MAX(g, b))) : 255; + byte lum = (SEGMENT.palette == 0) ? max(w, max(r, max(g, b))) : 255; lum /= (((256-SEGMENT.intensity)/16)+1); for (int i = 0; i < SEGLEN; i++) { byte flicker = random8(lum); if (SEGMENT.palette == 0) { - SEGMENT.setPixelColor(i, MAX(r - flicker, 0), MAX(g - flicker, 0), MAX(b - flicker, 0), MAX(w - flicker, 0)); + SEGMENT.setPixelColor(i, max(r - flicker, 0), max(g - flicker, 0), max(b - flicker, 0), max(w - flicker, 0)); } else { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, 255 - flicker)); } @@ -1369,7 +1369,7 @@ uint16_t gradient_base(bool loading) { { val = abs(((i>pp) ? p2:pp) -i); } else { - val = MIN(abs(pp-i),MIN(abs(p1-i),abs(p2-i))); + val = min(abs(pp-i), min(abs(p1-i), abs(p2-i))); } val = (brd > val) ? val/brd * 255 : 255; SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(0), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), val)); @@ -1738,7 +1738,7 @@ uint16_t mode_multi_comet(void) { if (SEGENV.step == it) return FRAMETIME; if (!SEGENV.allocateData(sizeof(uint16_t) * 8)) return mode_static(); //allocation failed - if (SEGENV.call == 0) SEGENV.setUpLeds(); // WLEDMM use lossless getPixelColor() + if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} // WLEDMM use lossless getPixelColor() SEGMENT.fade_out(SEGMENT.intensity); uint16_t* comets = reinterpret_cast(SEGENV.data); @@ -2096,7 +2096,7 @@ uint16_t mode_fire_2012() { const uint16_t strips = SEGMENT.nrOfVStrips(); if (!SEGENV.allocateData(strips * SEGLEN)) return mode_static(); //allocation failed byte* heat = SEGENV.data; - if (SEGENV.call == 0) SEGENV.setUpLeds(); // WLEDMM use lossless getPixelColor() + if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} // WLEDMM use lossless getPixelColor() const uint32_t it = strip.now >> 5; //div 32 @@ -2129,7 +2129,7 @@ uint16_t mode_fire_2012() { // Step 4. Map from heat cells to LED colors for (int j = 0; j < SEGLEN; j++) { - SEGMENT.setPixelColor(indexToVStrip(j, stripNr), ColorFromPalette(SEGPALETTE, MIN(heat[j],240), 255, NOBLEND)); + SEGMENT.setPixelColor(indexToVStrip(j, stripNr), ColorFromPalette(SEGPALETTE, min(heat[j],byte(240)), 255, NOBLEND)); } } }; @@ -2529,7 +2529,7 @@ uint16_t ripple_base() uint16_t dataSize = sizeof(ripple) * maxRipples; if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed - if (SEGENV.call == 0) SEGENV.setUpLeds(); // WLEDMM use lossless getPixelColor() + if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} // WLEDMM use lossless getPixelColor() Ripple* ripples = reinterpret_cast(SEGENV.data); @@ -2561,7 +2561,7 @@ uint16_t ripple_base() if ((v >= 0) && (v < SEGLEN)) // WLEDMM bugfix: v and w can be negative or out-of-range SEGMENT.setPixelColor(v, color_blend(SEGMENT.getPixelColor(v), col, mag)); // TODO int w = left + propI*2 + 3 -(v-left); - if ((v >= 0) && (v < SEGLEN)) // WLEDMM bugfix: v and w can be negative or out-of-range + if ((w >= 0) && (w < SEGLEN)) // WLEDMM bugfix: v and w can be negative or out-of-range SEGMENT.setPixelColor(w, color_blend(SEGMENT.getPixelColor(w), col, mag)); // TODO } } @@ -2971,7 +2971,7 @@ uint16_t mode_bouncing_balls(void) { uint32_t color = SEGCOLOR(0); if (SEGMENT.palette) { - color = SEGMENT.color_wheel(i*(256/MAX(numBalls, 8))); + color = SEGMENT.color_wheel(i*(256/max(numBalls, uint16_t(8)))); } else if (hasCol2) { color = SEGCOLOR(i % NUM_COLORS); } @@ -3552,7 +3552,10 @@ uint16_t mode_exploding_fireworks(void) if (SEGLEN == 1) return mode_static(); const uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1; const uint16_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength(); - if (SEGENV.call == 0) SEGENV.setUpLeds(); // WLEDMM use lossless getPixelColor() + if (SEGENV.call == 0) { + SEGENV.setUpLeds(); // WLEDMM use lossless getPixelColor() + SEGMENT.fill(BLACK); + } //allocate segment data uint16_t maxData = FAIR_DATA_PER_SEG; //ESP8266: 256 ESP32: 640 @@ -3693,6 +3696,7 @@ uint16_t mode_drip(void) if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed Spark* drops = reinterpret_cast(SEGENV.data); + if (SEGENV.call == 0) SEGMENT.fill(BLACK); // WLEDMM clear LEDs at startup if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); struct virtualStrip { @@ -3700,9 +3704,9 @@ uint16_t mode_drip(void) uint8_t numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3 - float gravity = -0.0005 - (SEGMENT.speed/25000.0); //increased gravity (50000 to 25000) - gravity *= max(1, SEGLEN-1); - int sourcedrop = 12; + float gravity = -0.0005f - (float(SEGMENT.speed)/35000.0f); //increased gravity (50000 to 35000) + gravity *= min(max(1, SEGLEN-1), 255); //WLEDMM speed limit 255 + const int sourcedrop = 12; for (int j=0;j 1% ... 20% probalibity drops[j].colIndex=2; //fall drops[j].col=255; } } if (drops[j].colIndex > 1) { // falling - if (drops[j].pos > 0) { // fall until end of segment + if (drops[j].pos > 0.01f) { // fall until end of segment drops[j].pos += drops[j].vel; if (drops[j].pos < 0) drops[j].pos = 0; drops[j].vel += gravity; // gravity is negative for (int i=1;i<7-drops[j].colIndex;i++) { // some minor math so we don't expand bouncing droplets - uint16_t pos = constrain(uint16_t(drops[j].pos) +i, 0, SEGLEN-1); //this is BAD, returns a pos >= SEGLEN occasionally + int intPos = roundf(drops[j].pos) +i; // WLEDMM round it first + uint16_t pos = constrain(intPos, 0, SEGLEN-1); //this is BAD, returns a pos >= SEGLEN occasionally // WLEDMM bad cast to uint16_t removed SEGMENT.setPixelColor(indexToVStrip(pos, stripNr), color_blend(BLACK,dropColor,drops[j].col/i)); //spread pixel with fade while falling } if (drops[j].colIndex > 2) { // during bounce, some water is on the floor - SEGMENT.setPixelColor(indexToVStrip(0, stripNr), color_blend(dropColor,BLACK,drops[j].col)); + SEGMENT.setPixelColor(indexToVStrip(0, stripNr), color_blend(dropColor,BLACK, (2 * drops[j].col)/3)); // WLEDMM reduced brightness } } else { // we hit bottom if (drops[j].colIndex > 2) { // already hit once, so back to forming @@ -3765,7 +3771,7 @@ uint16_t mode_drip(void) return FRAMETIME; } -static const char _data_FX_MODE_DRIP[] PROGMEM = "Drip@Gravity,# of drips,,,,,Overlay;!,!;!;1.5d;m12=1"; //bar WLEDMM 1.5d +static const char _data_FX_MODE_DRIP[] PROGMEM = "Drip@Gravity,# of drips,Fall ratio,,,,Overlay;!,!;!;1.5d;c1=127,m12=1"; //bar WLEDMM 1.5d /* @@ -4306,7 +4312,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 (SEGENV.call == 0) SEGENV.setUpLeds(); // WLEDMM use lossless getPixelColor() + if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} // WLEDMM use lossless getPixelColor() SEGMENT.fade_out(254); // add a bit of trail uint16_t counter = strip.now * (6 + (SEGMENT.speed >> 4)); uint16_t numBirds = 2 + (SEGLEN >> 3); // 2 + 1/8 of a segment @@ -4822,7 +4828,7 @@ static const char _data_FX_MODE_AURORA[] PROGMEM = "Aurora@!,!;1,2,3;!;;sx=24,pa // Controls are speed, # of pixels, faderate. uint16_t mode_perlinmove(void) { if (SEGLEN == 1) return mode_static(); - if (SEGENV.call == 0) SEGENV.setUpLeds(); // WLEDMM use lossless getPixelColor() + 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++) { uint16_t locn = inoise16(strip.now*128/(260-SEGMENT.speed)+i*15000, strip.now*128/(260-SEGMENT.speed)); // Get a new pixel location from moving noise. @@ -5093,25 +5099,25 @@ uint16_t mode_2Dfirenoise(void) { // firenoise2d. By Andrew Tuline SEGMENT.fill(BLACK); } - uint16_t xscale = SEGMENT.intensity*4; - uint32_t yscale = SEGMENT.speed*8; - uint8_t indexx = 0; + unsigned xscale = SEGMENT.intensity*4; + unsigned yscale = SEGMENT.speed*8; + unsigned indexx = 0; - SEGPALETTE = CRGBPalette16( CRGB(0,0,0), CRGB(0,0,0), CRGB(0,0,0), CRGB(0,0,0), - CRGB::Red, CRGB::Red, CRGB::Red, CRGB::DarkOrange, - CRGB::DarkOrange,CRGB::DarkOrange, CRGB::Orange, CRGB::Orange, - CRGB::Yellow, CRGB::Orange, CRGB::Yellow, CRGB::Yellow); + CRGBPalette16 pal = SEGMENT.check1 ? SEGPALETTE : CRGBPalette16(CRGB::Black, CRGB::Black, CRGB::Black, CRGB::Black, + CRGB::Red, CRGB::Red, CRGB::Red, CRGB::DarkOrange, + CRGB::DarkOrange,CRGB::DarkOrange, CRGB::Orange, CRGB::Orange, + CRGB::Yellow, CRGB::Orange, CRGB::Yellow, CRGB::Yellow); for (int j=0; j < cols; j++) { for (int i=0; i < rows; i++) { - indexx = inoise8(j*yscale*rows/255, i*xscale+strip.now/4); // We're moving along our Perlin map. - SEGMENT.setPixelColorXY(j, i, ColorFromPalette(SEGPALETTE, min(i*(indexx)>>4, 255), i*255/cols, LINEARBLEND)); // With that value, look up the 8 bit colour palette value and assign it to the current LED. + indexx = inoise8(j*yscale*rows/255, i*xscale+strip.now/4); // We're moving along our Perlin map. + SEGMENT.setPixelColorXY(j, i, ColorFromPalette(pal, min(i*(indexx)>>4, 255U), i*255/cols, LINEARBLEND)); // With that value, look up the 8 bit colour palette value and assign it to the current LED. } // for i } // for j return FRAMETIME; } // mode_2Dfirenoise() -static const char _data_FX_MODE_2DFIRENOISE[] PROGMEM = "Firenoise@X scale,Y scale;;!;2;pal=0"; //WLEDMM pal=0 +static const char _data_FX_MODE_2DFIRENOISE[] PROGMEM = "Firenoise@X scale,Y scale,,,,Palette;;!;2;pal=0"; //WLEDMM pal=0 ////////////////////////////// @@ -5144,117 +5150,191 @@ static const char _data_FX_MODE_2DFRIZZLES[] PROGMEM = "Frizzles@X frequency,Y f /////////////////////////////////////////// // 2D Cellular Automata Game of life // /////////////////////////////////////////// -typedef struct ColorCount { - CRGB color; - int8_t count; -} colorCount; - -uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https://natureofcode.com/book/chapter-7-cellular-automata/ and https://github.com/DougHaber/nlife-color +static bool getBitValue(const uint8_t* byteArray, size_t n) { + size_t byteIndex = n / 8; + size_t bitIndex = n % 8; + uint8_t byte = byteArray[byteIndex]; + return (byte >> bitIndex) & 1; +} +static void setBitValue(uint8_t* byteArray, size_t n, bool value) { + size_t byteIndex = n / 8; + size_t bitIndex = n % 8; + if (value) + byteArray[byteIndex] |= (1 << bitIndex); + else + byteArray[byteIndex] &= ~(1 << bitIndex); +} +// create game of life struct to hold cells and future cells +struct gameOfLife { + uint8_t* cells; + uint8_t* futureCells; + uint8_t gliderLength; + uint16_t oscillatorCRC; + uint16_t spaceshipCRC; +}; +uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https://natureofcode.com/book/chapter-7-cellular-automata/ + // and https://github.com/DougHaber/nlife-color , Modified By: Brandon Butler if (!strip.isMatrix) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - const uint16_t dataSize = sizeof(CRGB) * SEGMENT.length(); // using width*height prevents reallocation if mirroring is enabled - const uint16_t crcBufferLen = 2; //(SEGMENT.width() + SEGMENT.height())*71/100; // roughly sqrt(2)/2 for better repetition detection (Ewowi) + const size_t dataSize = (SEGMENT.length() / 8) + (((SEGMENT.length() % 8) != 0) ? 1 : 0); // add one byte when extra bits needed (length not a multiple of 8) + const size_t totalSize = dataSize*2 + sizeof(gameOfLife); - if (!SEGENV.allocateData(dataSize + sizeof(uint16_t)*crcBufferLen)) return mode_static(); //allocation failed - CRGB *prevLeds = reinterpret_cast(SEGENV.data); - uint16_t *crcBuffer = reinterpret_cast(SEGENV.data + dataSize); + if (!SEGENV.allocateData(totalSize)) return mode_static(); //allocation failed + gameOfLife* gol = reinterpret_cast(SEGENV.data); - CRGB backgroundColor = SEGCOLOR(1); + if (gol->cells == nullptr) { + gol->cells = new uint8_t[dataSize]; + gol->futureCells = new uint8_t[dataSize]; + } - if (SEGENV.call == 0) SEGMENT.setUpLeds(); + uint16_t &generation = SEGENV.aux0; //rename aux0 and aux1 for readability (not needed) + uint16_t &pauseFrames = SEGENV.aux1; + CRGB backgroundColor = SEGCOLOR(1); + CRGB color; - if (SEGENV.call == 0 || strip.now - SEGMENT.step > 3000) { - SEGENV.step = strip.now; - SEGENV.aux0 = 0; + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); // to make sure that segment buffer and physical leds are aligned initially + } + //start new game of life + if ((SEGENV.call == 0 || generation == 0) && pauseFrames == 0) { + SEGENV.step = strip.now; // .step = previous call time + generation = 1; + pauseFrames = 75; // show initial state for longer random16_set_seed(strip.now>>2); //seed the random generator - - //give the leds random state and colors (based on intensity, colors from palette or all posible colors are chosen) + //Setup Grid for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { - uint8_t state = random8()%2; - if (state == 0) - SEGMENT.setPixelColorXY(x,y, backgroundColor); - else - SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0): random16()*random16()); //WLEDMM support all colors + uint8_t state = (random8() < 82) ? 1 : 0; // ~32% chance of being alive + if (state == 0) { + setBitValue(gol->cells, y * cols + x, false); + setBitValue(gol->futureCells, y * cols + x, false); + if (SEGMENT.check2) continue; + SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?backgroundColor : RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0)); + } + else { + setBitValue(gol->cells, y * cols + x, true); + setBitValue(gol->futureCells, y * cols + x, true); + color = SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0); + SEGMENT.setPixelColorXY(x,y,!SEGMENT.check1?color : RGBW32(color.r, color.g, color.b, 0)); + } } - for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) prevLeds[XY(x,y)] = CRGB::Black; - memset(crcBuffer, 0, sizeof(uint16_t)*crcBufferLen); - } else if (strip.now - SEGENV.step < FRAMETIME_FIXED * (uint32_t)map(SEGMENT.speed,0,255,64,4)) { - // update only when appropriate time passes (in 42 FPS slots) + //Clear CRCs + gol->oscillatorCRC = 0; + gol->spaceshipCRC = 0; + + //Calculate glider length LCM(rows,cols)*4 + uint8_t a = rows; + uint8_t b = cols; + while (b) { + uint8_t t = b; + b = a % b; + a = t; + } + gol->gliderLength = cols * rows / a * 4; return FRAMETIME; } - - //copy previous leds (save previous generation) - //NOTE: using lossy getPixelColor() is a benefit as endlessly repeating patterns will eventually fade out causing a reset - for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) prevLeds[XY(x,y)] = SEGMENT.getPixelColorXY(x,y); - - //calculate new leds + //Redraw immediately if overlay to avoid flicker + if (SEGMENT.check2) { + for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { + //redraw foreground/alive + if (getBitValue(gol->cells, y * cols + x)) { + color = SEGMENT.getPixelColorXY(x,y); + SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?color : RGBW32(color.r, color.g, color.b, 0)); + } + } + } + if (pauseFrames || strip.now - SEGENV.step < FRAMETIME_FIXED * (uint32_t)map(SEGMENT.speed,0,255,64,2)) { + if(pauseFrames) pauseFrames--; + return FRAMETIME; //skip if not enough time has passed + } + //Update Game of Life + bool cellChanged = false; // Detect still live and dead grids + //cell index and coordinates + uint16_t cIndex; + uint16_t cX; + uint16_t cY; + //Loop through all cells. Count neighbors, apply rules, setPixel for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { + byte neighbors = 0; + byte colorCount = 0; //track number of valid colors + CRGB nColors[3]; // track 3 colors, dying cells may overwrite but this wont be used - colorCount colorsCount[9]; // count the different colors in the 3*3 matrix - for (int i=0; i<9; i++) colorsCount[i] = {backgroundColor, 0}; // init colorsCount - - // iterate through neighbors and count them and their different colors - int neighbors = 0; for (int i = -1; i <= 1; i++) for (int j = -1; j <= 1; j++) { // iterate through 3*3 matrix if (i==0 && j==0) continue; // ignore itself - // wrap around segment - int16_t xx = x+i, yy = y+j; - if (x+i < 0) xx = cols-1; else if (x+i >= cols) xx = 0; - if (y+j < 0) yy = rows-1; else if (y+j >= rows) yy = 0; - - uint16_t xy = XY(xx, yy); // previous cell xy to check - // count different neighbours and colors - if (prevLeds[xy] != backgroundColor) { + if (!SEGMENT.check3 || generation % 1500 == 0) { //no wrap disable wrap every 1500 generations to prevent undetected repeats + cX = x+i; + cY = y+j; + if (cX < 0 || cY < 0 || cX >= cols || cY >= rows) continue; //skip if out of bounds + } else { //wrap around + cX = (x+i+cols) % cols; + cY = (y+j+rows) % rows; + } + cIndex = cY * cols + cX; + // count neighbors and store upto 3 neighbor colors + if (getBitValue(gol->cells, cIndex)) { //if alive neighbors++; - bool colorFound = false; - int k; - for (k=0; k<9 && colorsCount[i].count != 0; k++) - if (colorsCount[k].color == prevLeds[xy]) { - colorsCount[k].count++; - colorFound = true; - } - if (!colorFound) colorsCount[k] = {prevLeds[xy], 1}; //add new color found in the array + color = SEGMENT.getPixelColorXY(cX, cY); + if (color == backgroundColor) continue; //parent just died, color lost + nColors[colorCount%3] = color; + colorCount++; } - } // i,j + } // Rules of Life - CRGB preCol = prevLeds[XY(x,y)]; - uint32_t col = RGBW32(preCol.r, preCol.g, preCol.b, 0); // WLEDMM explicit color conversion CRGB -> RGB - uint32_t bgc = RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0); - if ((col != bgc) && (neighbors < 2)) SEGMENT.setPixelColorXY(x,y, bgc); // Loneliness - else if ((col != bgc) && (neighbors > 3)) SEGMENT.setPixelColorXY(x,y, bgc); // Overpopulation - else if ((col == bgc) && (neighbors == 3)) { // Reproduction + bool cellValue = getBitValue(gol->cells, y * cols + x); + if ((cellValue) && (neighbors < 2 || neighbors > 3)) { + // Loneliness or overpopulation + cellChanged = true; + setBitValue(gol->futureCells, y * cols + x, false); + if (!SEGMENT.check2) SEGMENT.setPixelColorXY(x,y, !SEGMENT.check1?backgroundColor : RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0)); + } + else if (!(cellValue) && (neighbors == 3)) { + // Reproduction + setBitValue(gol->futureCells, y * cols + x, true); + cellChanged = true; // find dominant color and assign it to a cell - colorCount dominantColorCount = {backgroundColor, 0}; - for (int i=0; i<9 && colorsCount[i].count != 0; i++) - if (colorsCount[i].count > dominantColorCount.count) dominantColorCount = colorsCount[i]; - // assign the dominant color w/ a bit of randomness to avoid "gliders" - if (dominantColorCount.count > 0 && random8(128)) SEGMENT.setPixelColorXY(x,y, dominantColorCount.color); - } else if ((col == bgc) && (neighbors == 2) && !random8(128)) { // Mutation - SEGMENT.setPixelColorXY(x,y, SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 255)); - } - // else do nothing! - } //x,y - - // calculate CRC16 of leds - uint16_t crc = crc16((const unsigned char*)prevLeds, dataSize); - // check if we had same CRC and reset if needed + // no longer storing colors, if parent dies the color is lost + CRGB dominantColor; + if (colorCount == 3) { //All parents survived + if ((nColors[0] == nColors[1]) || (nColors[0] == nColors[2])) dominantColor = nColors[0]; + else if (nColors[1] == nColors[2]) dominantColor = nColors[1]; + else dominantColor = nColors[random8()%3]; + } + else if (colorCount == 2) dominantColor = nColors[random8()%2]; // 1 leading parent died + else if (colorCount == 1) dominantColor = nColors[0]; // 2 leading parents died + else dominantColor = color; // all parents died last used color + // mutate color chance + if (random8() < SEGMENT.intensity) dominantColor = !SEGMENT.check1?SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0): random16()*random16(); + + if (SEGMENT.check1) dominantColor = RGBW32(dominantColor.r, dominantColor.g, dominantColor.b, 0); //WLEDMM support all colors + SEGMENT.setPixelColorXY(x,y, dominantColor); + } + } + //update cell values + memcpy(gol->cells, gol->futureCells, dataSize); + + // Get current crc value + uint16_t crc = crc16((const unsigned char*)gol->cells, dataSize); + bool repetition = false; - for (int i=0; i softhack007: not exacly. Different CRC means different image; same CRC means nothing (could be same or slightly different). - if (!repetition) SEGENV.step = strip.now; //if no repetition avoid reset - // remember CRCs across frames - crcBuffer[SEGENV.aux0] = crc; - ++SEGENV.aux0 %= crcBufferLen; + if (!cellChanged || crc == gol->oscillatorCRC || crc == gol->spaceshipCRC) repetition = true; //check if cell changed this gen and compare previous stored crc values + if (repetition) { + generation = 0; // reset on next call + pauseFrames = 50; + return FRAMETIME; + } + // Update CRC values + if (generation % 16 == 0) gol->oscillatorCRC = crc; + if (generation % gol->gliderLength == 0) gol->spaceshipCRC = crc; + generation++; + SEGENV.step = strip.now; return FRAMETIME; } // mode_2Dgameoflife() -static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!,,,,,All colors ☾;!,!;!;2;c1=0"; //WLEDMM support all colors - +static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!,Color Mutation ☾,,,,All Colors ☾,Overlay ☾,Wrap ☾,;!,!;!;2;sx=200,ix=12,c1=0,o3=1"; ///////////////////////// // 2D Hiphotic // @@ -5627,7 +5707,7 @@ uint16_t mode_2DPlasmaball(void) { // By: Stepko https://edito (rows - 1 - cy == 0)) ? ColorFromPalette(SEGPALETTE, beat8(5), thisVal, LINEARBLEND) : CRGB::Black); } } - SEGMENT.blur(SEGMENT.custom2>>5); + SEGMENT.blur(SEGMENT.custom2>>5, (SEGMENT.custom2 > 132)); // WLEDMM return FRAMETIME; } // mode_2DPlasmaball() @@ -5645,6 +5725,8 @@ uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); + const float maxRows = (rows <= 32) ? 32.0f : (rows <= 64) ? 64.0f : 128.0f; // WLEDMM safe up to 128x128 + const float minScale = (rows <= 32) ? 12.0f : (rows <= 64) ? 4.6f : 2.1f; // WLEDMM const CRGBPalette16 auroraPalette = {0x000000, 0x003300, 0x006600, 0x009900, 0x00cc00, 0x00ff00, 0x33ff00, 0x66ff00, 0x99ff00, 0xccff00, 0xffff00, 0xffcc00, 0xff9900, 0xff6600, 0xff3300, 0xff0000}; @@ -5653,8 +5735,11 @@ uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https SEGMENT.fill(BLACK); } - float adjustHeight = mapf(rows, 8, 32, 28, 12); // maybe use mapf() ??? // WLEDMM yes! + float adjustHeight = mapf(rows, 8, maxRows, 28, minScale); // maybe use mapf() ??? // WLEDMM yes! uint16_t adjScale = map(cols, 8, 64, 310, 63); + + adjustHeight = max(min(adjustHeight, 28.0f), minScale); // WLEDMM bugfix for larger fixtures + adjScale = max(min(adjScale, uint16_t(310)), uint16_t(63)); // WLEDMM /* if (SEGENV.aux1 != SEGMENT.custom1/12) { // Hacky palette rotation. We need that black. SEGENV.aux1 = SEGMENT.custom1/12; @@ -5813,6 +5898,8 @@ uint16_t mode_2DSunradiation(void) { // By: ldirko https://edi 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 byte *bump = reinterpret_cast(SEGENV.data); @@ -5841,10 +5928,10 @@ uint16_t mode_2DSunradiation(void) { // By: ldirko https://edi ++vlx; int8_t nx = bump[x + yindex + 1] - bump[x + yindex - 1]; int8_t ny = bump[x + yindex + (cols + 2)] - bump[x + yindex - (cols + 2)]; - byte difx = abs8(vlx * 7 - nx); - byte dify = abs8(vly * 7 - ny); + byte difx = min(abs(vlx * 7 - nx), 255); // WLEDMM replaced abs8 as it does not work for numbers >127 + byte dify = min(abs(vly * 7 - ny), 255); // WLEDMM int temp = difx * difx + dify * dify; - int col = 255 - temp / 8; //8 its a size of effect + int col = 255 - temp / magnify; //8 its a size of effect // WLEDMM size adjusts to matrix dimensions if (col < 0) col = 0; SEGMENT.setPixelColorXY(x, y, HeatColor(col / (3.0f-(float)(SEGMENT.intensity)/128.f))); } @@ -5946,19 +6033,20 @@ static const char _data_FX_MODE_2DSPACESHIPS[] PROGMEM = "Spaceships@!,Blur;;!;2 // 2D Crazy Bees // ///////////////////////// //// Crazy bees by stepko (c)12.02.21 [https://editor.soulmatelights.com/gallery/651-crazy-bees], adapted by Blaz Kristan (AKA blazoncek) -#define MAX_BEES 5 +constexpr uint_fast16_t MAX_BEES = 5; uint16_t mode_2Dcrazybees(void) { if (!strip.isMatrix) return mode_static(); // not a 2D set-up - const uint16_t cols = SEGMENT.virtualWidth(); - const uint16_t rows = SEGMENT.virtualHeight(); + const uint_fast16_t cols = SEGMENT.virtualWidth(); + const uint_fast16_t rows = SEGMENT.virtualHeight(); - byte n = MIN(MAX_BEES, (rows * cols) / 256 + 1); + const byte n = min(MAX_BEES, (rows * cols) / 256 + 1); typedef struct Bee { uint8_t posX, posY, aimX, aimY, hue; - int8_t deltaX, deltaY, signX, signY, error; - void aimed(uint16_t w, uint16_t h) { + int8_t signX, signY; + int16_t deltaX, deltaY, error; + void aimed(uint_fast16_t w, uint_fast16_t h) { if (!true) //WLEDMM SuperSync random16_set_seed(strip.now); aimX = random8(0, w); @@ -5999,7 +6087,7 @@ uint16_t mode_2Dcrazybees(void) { SEGMENT.addPixelColorXY(bee[i].aimX, bee[i].aimY - 1, CHSV(bee[i].hue, 255, 255)); if (bee[i].posX != bee[i].aimX || bee[i].posY != bee[i].aimY) { SEGMENT.setPixelColorXY(bee[i].posX, bee[i].posY, CRGB(CHSV(bee[i].hue, 60, 255))); - int8_t error2 = bee[i].error * 2; + int_fast16_t error2 = bee[i].error * 2; if (error2 > -bee[i].deltaY) { bee[i].error -= bee[i].deltaY; bee[i].posX += bee[i].signX; @@ -6048,7 +6136,7 @@ uint16_t mode_2Dghostrider(void) { const size_t maxLighters = min(cols + rows, LIGHTERS_AM); - if (SEGENV.call == 0) SEGMENT.setUpLeds(); + if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) { SEGENV.aux0 = cols; SEGENV.aux1 = rows; @@ -6133,7 +6221,7 @@ uint16_t mode_2Dfloatingblobs(void) { if (!SEGENV.allocateData(sizeof(blob_t))) return mode_static(); //allocation failed blob_t *blob = reinterpret_cast(SEGENV.data); - if (SEGENV.call == 0) SEGMENT.setUpLeds(); + if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) { SEGENV.aux0 = cols; // re-initialise if virtual size changes SEGENV.aux1 = rows; @@ -6160,7 +6248,7 @@ uint16_t mode_2Dfloatingblobs(void) { if (blob->grow[i]) { // enlarge radius until it is >= 4 blob->r[i] += (fabsf(blob->sX[i]) > fabsf(blob->sY[i]) ? fabsf(blob->sX[i]) : fabsf(blob->sY[i])) * 0.05f; - if (blob->r[i] >= MIN(cols/4.f,2.f)) { + if (blob->r[i] >= min(cols/4.f,2.f)) { blob->grow[i] = false; } } else { @@ -6401,6 +6489,7 @@ uint16_t mode_ripplepeak(void) { // * Ripple peak. By Andrew Tuli if (SEGENV.call == 0) { SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); SEGENV.aux0 = 255; SEGMENT.custom1 = *binNum; SEGMENT.custom2 = *maxVol * 2; @@ -6759,18 +6848,19 @@ uint16_t mode_juggles(void) { // Juggles. By Andrew Tuline. um_data = simulateSound(SEGMENT.soundSim); } float volumeSmth = *(float*) um_data->u_data[0]; - if (SEGENV.call == 0) SEGENV.setUpLeds(); // WLEDMM use lossless getPixelColor() + if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} // WLEDMM use lossless getPixelColor() SEGMENT.fade_out(224); // 6.25% uint16_t my_sampleAgc = fmax(fmin(volumeSmth, 255.0), 0); for (size_t i=0; iu_data[0]; int16_t volumeRaw = *(int16_t*)um_data->u_data[1]; - if (SEGENV.call == 0) SEGENV.setUpLeds(); // WLEDMM use lossless getPixelColor() + if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} // WLEDMM use lossless getPixelColor() //uint8_t fadeRate = map(SEGMENT.speed,0,255,224,255); uint8_t fadeRate = map(SEGMENT.speed,0,255,200,254); @@ -6970,7 +7060,7 @@ uint16_t mode_pixelwave(void) { // Pixelwave. By Andrew Tuline. return FRAMETIME; } // mode_pixelwave() -static const char _data_FX_MODE_PIXELWAVE[] PROGMEM = "Pixelwave@!,Sensitivity;!,!;!;1v;ix=64,m12=2,si=0"; // Arc, Beatsin +static const char _data_FX_MODE_PIXELWAVE[] PROGMEM = "Pixelwave@!,Sensitivity;!,!;!;01v;ix=64,m12=2,si=0"; // Arc, Beatsin ////////////////////// @@ -7016,7 +7106,7 @@ uint16_t mode_plasmoid(void) { // Plasmoid. By Andrew Tuline. return FRAMETIME; } // mode_plasmoid() -static const char _data_FX_MODE_PLASMOID[] PROGMEM = "Plasmoid@Phase,# of pixels;!,!;!;1v;sx=128,ix=80,pal=8,m12=0,si=0"; // Pixels, Beatsin, Lava Palette +static const char _data_FX_MODE_PLASMOID[] PROGMEM = "Plasmoid@Phase,# of pixels;!,!;!;01v;sx=128,ix=80,pal=8,m12=0,si=0"; // Pixels, Beatsin, Lava Palette /////////////////////// @@ -7115,7 +7205,7 @@ uint16_t mode_pixels(void) { // Pixels. By Andrew Tuline. um_data = simulateSound(SEGMENT.soundSim); } float volumeSmth = *(float*) um_data->u_data[0]; - if (SEGENV.call == 0) SEGENV.setUpLeds(); // WLEDMM use lossless getPixelColor() + if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} // WLEDMM use lossless getPixelColor() myVals[strip.now%32] = volumeSmth; // filling values semi randomly @@ -7171,7 +7261,7 @@ uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline. return FRAMETIME; } // mode_blurz() -static const char _data_FX_MODE_BLURZ[] PROGMEM = "Blurz@Fade rate,Blur;!,Color mix;!;1f;m12=0,si=0"; // Pixels, Beatsin +static const char _data_FX_MODE_BLURZ[] PROGMEM = "Blurz@Fade rate,Blur;!,Color mix;!;01f;m12=0,si=0"; // Pixels, Beatsin #else // original version from SR 0.13, with some enhancements by @softhack007 uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline. @@ -7202,6 +7292,7 @@ uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline. if ((SEGENV.aux1 < SEGLEN) && (volumeSmth > 1.0f)) SEGMENT.setPixelColor(SEGENV.aux1,SEGENV.step); // "repaint" last pixel after blur uint16_t segLoc = random16(SEGLEN); + if (SEGLEN < 2) segLoc = 0; // WLEDMM just to be sure unsigned pixColor = (2*fftResult[SEGENV.aux0%16]*240)/max(1, SEGLEN-1); // WLEDMM avoid uint8 overflow, and preserve pixel parameters for redraw unsigned pixIntensity = min((unsigned)(2.0f*fftResult[SEGENV.aux0%16]), 255U); @@ -7218,13 +7309,14 @@ uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline. return FRAMETIME_FIXED; } // mode_blurz() -static const char _data_FX_MODE_BLURZ[] PROGMEM = "Blurz ☾@Fade rate,Blur;!,Color mix;!;1f;sx=48,ix=127,m12=0,si=0"; // Pixels, Beatsin +static const char _data_FX_MODE_BLURZ[] PROGMEM = "Blurz ☾@Fade rate,Blur;!,Color mix;!;01f;sx=48,ix=127,m12=0,si=0"; // Pixels, Beatsin #endif ///////////////////////// // ** DJLight // ///////////////////////// uint16_t mode_DJLight(void) { // Written by Stefan Petrick, Adapted by Will Tatam. + // No need to prevent from executing on single led strips, only mid will be set (mid = 0) const int mid = SEGLEN / 2; um_data_t *um_data; @@ -7280,13 +7372,14 @@ uint16_t mode_DJLight(void) { // Written by Stefan Petrick, Ad if (SEGENV.check1) fadeVal = constrain(fadeVal, 0, 176); // "candy factory" mode - avoid complete fade-out SEGMENT.setPixelColor(mid, color.fadeToBlackBy(fadeVal)); + // if SEGLEN equals 1 these loops won't execute for (int i = SEGLEN - 1; i > mid; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); // move to the left for (int i = 0; i < mid; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right } return FRAMETIME; } // mode_DJLight() -static const char _data_FX_MODE_DJLIGHT[] PROGMEM = "DJ Light@Speed,,,,,Candy Factory;;;1f;m12=2,si=0"; // Arc, Beatsin +static const char _data_FX_MODE_DJLIGHT[] PROGMEM = "DJ Light@Speed,,,,,Candy Factory;;;01f;m12=2,si=0"; // Arc, Beatsin //////////////////// @@ -7340,6 +7433,7 @@ static const char _data_FX_MODE_FREQMAP[] PROGMEM = "Freqmap@Fade rate,Starting // ** Freqmatrix // /////////////////////// uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Pleschung. + // No need to prevent from executing on single led strips, we simply change pixel 0 each time and avoid the shift um_data_t *um_data; if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { // add support for no audio @@ -7382,12 +7476,13 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch // shift the pixels one pixel up SEGMENT.setPixelColor(0, color); + // if SEGLEN equals 1 this loop won't execute for (int i = SEGLEN - 1; i > 0; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left } return FRAMETIME; } // mode_freqmatrix() -static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Speed,Sound effect,Low bin,High bin,Sensitivity;;;1f;c1=18,c2=48,c3=6,m12=3,si=0"; // Corner, Beatsin; notes range C3 to C7 +static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Speed,Sound effect,Low bin,High bin,Sensitivity;;;01f;c1=18,c2=48,c3=6,m12=3,si=0"; // Corner, Beatsin; notes range C3 to C7 ////////////////////// @@ -7444,6 +7539,7 @@ static const char _data_FX_MODE_FREQPIXELS[] PROGMEM = "Freqpixels@Fade rate,Sta // As a compromise between speed and accuracy we are currently sampling with 10240Hz, from which we can then determine with a 512bin FFT our max frequency is 5120Hz. // Depending on the music stream you have you might find it useful to change the frequency mapping. uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschung. With some enhancements by @softhack007 + // As before, this effect can also work on single pixels, we just lose the shifting effect um_data_t *um_data; if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { // add support for no audio @@ -7499,13 +7595,14 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun SEGMENT.setPixelColor(SEGLEN/2, color); // shift the pixels one pixel outwards + // if SEGLEN equals 1 these loops won't execute for (int i = SEGLEN - 1; i > SEGLEN/2; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left for (int i = 0; i < SEGLEN/2; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right } return FRAMETIME; } // mode_freqwave() -static const char _data_FX_MODE_FREQWAVE[] PROGMEM = "Freqwave@Speed,Sound effect,Low bin,High bin,Pre-amp,Musical Scale ☾;;;1f;c1=18,c2=48,m12=2,si=0"; // notes range C3 to C7, Arc, Beatsin +static const char _data_FX_MODE_FREQWAVE[] PROGMEM = "Freqwave@Speed,Sound effect,Low bin,High bin,Pre-amp,Musical Scale ☾;;;01f;c1=18,c2=48,m12=2,si=0"; // notes range C3 to C7, Arc, Beatsin /////////////////////// @@ -7578,8 +7675,8 @@ uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuli uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; if (SEGENV.call == 0) { - SEGMENT.fill(BLACK); SEGENV.setUpLeds(); // WLEDMM use lossless getPixelColor() + SEGMENT.fill(BLACK); } //SEGMENT.fade_out(224); // Just in case something doesn't get faded. int fadeoutDelay = (256 - SEGMENT.speed) / 96; @@ -7588,13 +7685,14 @@ uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuli uint8_t numBins = map(SEGMENT.intensity,0,255,0,16); // Map slider to fftResult bins. for (int i=0; i> 2) +2)) & 0xFFFF; counter = counter >> 8; @@ -8042,7 +8143,8 @@ uint16_t mode_2Dsoap() { random16_set_seed(535); USER_PRINTF("SuperSync\n"); } - SEGMENT.setUpLeds(); + SEGENV.setUpLeds(); + SEGMENT.fill(BLACK); *noise32_x = random16(); *noise32_y = random16(); *noise32_z = random16(); diff --git a/wled00/FX.h b/wled00/FX.h index 8df5285514..b8864db4fd 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -106,7 +106,7 @@ bool strip_uses_global_leds(void); // WLEDMM implemented in FX_fcn. //#define SEGCOLOR(x) strip._segments[strip.getCurrSegmentId()].currentColor(x, strip._segments[strip.getCurrSegmentId()].colors[x]) //#define SEGLEN strip._segments[strip.getCurrSegmentId()].virtualLength() #define SEGCOLOR(x) strip.segColor(x) /* saves us a few kbytes of code */ -#define SEGPALETTE strip._currentPalette +#define SEGPALETTE Segment::getCurrentPalette() #define SEGLEN strip._virtualSegmentLength /* saves us a few kbytes of code */ #define SPEED_FORMULA_L (5U + (50U*(255U - SEGMENT.speed))/SEGLEN) @@ -355,7 +355,7 @@ typedef enum mapping1D2D { M12_jMap = 4, //WLEDMM jMap M12_sCircle = 5, //WLEDMM Circle M12_sBlock = 6, //WLEDMM Block - M12_sPinWheel = 7 //WLEDMM PinWheel + M12_sPinwheel = 7 //WLEDMM Pinwheel } mapping1D2D_t; // segment, 72 bytes @@ -428,6 +428,9 @@ typedef struct Segment { size_t _dataLen; // WLEDMM uint16_t is too small static size_t _usedSegmentData; // WLEDMM uint16_t is too small + // perhaps this should be per segment, not static + static CRGBPalette16 _currentPalette; // palette used for current effect (includes transition, used in color_from_palette()) + // transition data, valid only if transitional==true, holds values during transition struct Transition { uint32_t _colorT[NUM_COLORS]; @@ -561,6 +564,7 @@ typedef struct Segment { static void addUsedSegmentData(int len) { _usedSegmentData += len; } void allocLeds(); //WLEDMM + inline static const CRGBPalette16 &getCurrentPalette(void) { return Segment::_currentPalette; } void setUp(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1); bool setColor(uint8_t slot, uint32_t c); //returns true if changed @@ -605,7 +609,7 @@ typedef struct Segment { uint8_t currentMode(uint8_t modeNew); uint32_t currentColor(uint8_t slot, uint32_t colorNew); CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal); - CRGBPalette16 ¤tPalette(CRGBPalette16 &tgt, uint8_t paletteID); + void setCurrentPalette(void); // 1D strip uint16_t virtualLength(void) const; @@ -617,7 +621,7 @@ typedef struct Segment { void setPixelColor(float i, CRGB c, bool aa = true) { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); } uint32_t __attribute__((pure)) getPixelColor(int i); // WLEDMM attribute added // 1D support functions (some implement 2D as well) - void blur(uint8_t); + void blur(uint8_t, bool smear = false); void fill(uint32_t c); void fade_out(uint8_t r); void fadeToBlackBy(uint8_t fadeBy); @@ -658,12 +662,15 @@ typedef struct Segment { return (x%width) + (y%height) * width; } void setPixelColorXY(int x, int y, uint32_t c); // set relative pixel within segment with color - 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 - void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } // automatically inline - void setPixelColorXY(float x, float y, uint32_t c, bool aa = true, bool fast = true); - 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); } - void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), aa); } - uint32_t __attribute__((pure)) getPixelColorXY(uint16_t x, uint16_t y); // WLEDMM attribute pure + 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)); } + //#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); } + //#endif + uint32_t __attribute__((pure)) getPixelColorXY(int x, int y); // 2D support functions void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend); 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); } @@ -672,8 +679,8 @@ typedef struct Segment { void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), 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(uint16_t row, fract8 blur_amount); - void blurCol(uint16_t col, fract8 blur_amount); + void blurRow(uint32_t row, fract8 blur_amount, bool smear = false); + void blurCol(uint32_t col, fract8 blur_amount, bool smear = false); void moveX(int8_t delta, bool wrap = false); void moveY(int8_t delta, bool wrap = false); void move(uint8_t dir, uint8_t delta, bool wrap = false); @@ -692,32 +699,36 @@ typedef struct Segment { void nscale8(uint8_t scale); bool jsonToPixels(char *name, uint8_t fileNr); //WLEDMM for artifx #else - uint16_t XY(uint16_t x, uint16_t y) { return x; } - void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(x, c); } - void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColor(x, RGBW32(r,g,b,w)); } - void setPixelColorXY(int x, int y, CRGB c) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0)); } - void setPixelColorXY(float x, float y, uint32_t c, bool aa = true) { setPixelColor(x, c, aa); } - 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); } - void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0), aa); } - uint32_t getPixelColorXY(uint16_t x, uint16_t y) { return getPixelColor(x); } - void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t c, uint8_t blend) { blendPixelColor(x, c, blend); } - void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColor(x, RGBW32(c.r,c.g,c.b,0), blend); } - void addPixelColorXY(int x, int y, uint32_t color, bool fast = false) { addPixelColor(x, color, fast); } - 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); } - void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColor(x, RGBW32(c.r,c.g,c.b,0), fast); } - void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { fadePixelColor(x, fade); } - void box_blur(uint16_t i, bool vertical, fract8 blur_amount) {} - void blurRow(uint16_t row, fract8 blur_amount) {} - void blurCol(uint16_t col, fract8 blur_amount) {} - void moveX(int8_t delta, bool wrap = false) {} - void moveY(int8_t delta, bool wrap = false) {} - void move(uint8_t dir, uint8_t delta, bool wrap = false) {} - void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c) {} - void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) {} - void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) {} - void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color) {} - void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB color) {} - void wu_pixel(uint32_t x, uint32_t y, CRGB c) {} + inline uint16_t XY(uint16_t x, uint16_t y) { return x; } + 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)); } + //#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); } + //#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 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 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) {} + inline void blurCol(uint32_t col, fract8 blur_amount, bool smear = false) {} + inline void moveX(int8_t delta, bool wrap = false) {} + inline void moveY(int8_t delta, bool wrap = false) {} + inline void move(uint8_t dir, uint8_t delta, bool wrap = false) {} + inline void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c) {} + inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) {} + inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) {} + inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t = 0, int8_t = 0) {} + inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB color) {} + inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0) {} + inline void wu_pixel(uint32_t x, uint32_t y, CRGB c) {} #endif uint8_t * getAudioPalette(int pal); //WLEDMM netmindz ar palette } segment; @@ -752,7 +763,6 @@ class WS2812FX { // 96 bytes panels(1), #endif // semi-private (just obscured) used in effect functions through macros - _currentPalette(CRGBPalette16(CRGB::Black)), _colors_t{0,0,0}, _virtualSegmentLength(0), // true private variables @@ -971,7 +981,6 @@ class WS2812FX { // 96 bytes // end 2D support void loadCustomPalettes(void); // loads custom palettes from JSON - CRGBPalette16 _currentPalette; // palette used for current effect (includes transition) std::vector customPalettes; // TODO: move custom palettes out of WS2812FX class // using public variables to reduce code size increase due to inline function getSegment() (with bounds checking) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 2844d4923c..1d217ff071 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -214,24 +214,37 @@ uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) { void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM: IRAM_ATTR conditionally { if (Segment::maxHeight==1) return; // not a matrix set-up - if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit - - if (ledsrgb) ledsrgb[XY(x,y)] = col; - + if (x<0 || y<0 || x >= virtualWidth() || y >= virtualHeight()) return; // if pixel would fall out of virtual segment just exit + + unsigned i = UINT_MAX; + bool sameColor = false; + if (ledsrgb) { // WLEDMM small optimization + i = XY(x,y); + CRGB fastled_col = CRGB(col); + if (ledsrgb[i] == fastled_col) sameColor = true; + else ledsrgb[i] = fastled_col; + } uint8_t _bri_t = currentBri(on ? opacity : 0); if (!_bri_t && !transitional) return; if (_bri_t < 255) { - byte r = scale8(R(col), _bri_t); - byte g = scale8(G(col), _bri_t); - byte b = scale8(B(col), _bri_t); - byte w = scale8(W(col), _bri_t); - col = RGBW32(r, g, b, w); + col = color_fade(col, _bri_t); } +#if 0 // this is a dangerous optimization + if ((i < UINT_MAX) && sameColor && (call > 0) && (ledsrgb[i] == CRGB(col)) && (_globalLeds == nullptr)) return; // WLEDMM looks like nothing to do (but we don't trust globalleds) +#endif + if (reverse ) x = virtualWidth() - x - 1; if (reverse_y) y = virtualHeight() - y - 1; if (transpose) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed + // WLEDMM shortcut when no grouping/spacing used + bool simpleSegment = !mirror && !mirror_y && (grouping == 1) && (spacing == 0); + if (simpleSegment) { + strip.setPixelColorXY(start + x, startY + y, col); + return; + } + x *= groupLength(); // expand to physical pixels y *= groupLength(); // expand to physical pixels if (x >= width() || y >= height()) return; // if pixel would fall out of segment just exit @@ -310,10 +323,12 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa, bool fast } // returns RGBW values of pixel -uint32_t Segment::getPixelColorXY(uint16_t x, uint16_t y) { - if (!isActive()) return 0; // not active - int i = XY(x,y); - if (ledsrgb) return RGBW32(ledsrgb[i].r, ledsrgb[i].g, ledsrgb[i].b, 0); +uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) { + if (x<0 || y<0 || !isActive()) return 0; // not active or out-of range + if (ledsrgb) { + int i = XY(x,y); + return RGBW32(ledsrgb[i].r, ledsrgb[i].g, ledsrgb[i].b, 0); + } if (reverse ) x = virtualWidth() - x - 1; if (reverse_y) y = virtualHeight() - y - 1; if (transpose) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed @@ -329,23 +344,11 @@ void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t } // Adds the specified color with the existing pixel color perserving color balance. -void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) { +void IRAM_ATTR_YN Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) { if (!isActive()) return; // not active if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit uint32_t col = getPixelColorXY(x,y); - uint8_t r = R(col); - uint8_t g = G(col); - uint8_t b = B(col); - uint8_t w = W(col); - if (fast) { - r = qadd8(r, R(color)); - g = qadd8(g, G(color)); - b = qadd8(b, B(color)); - w = qadd8(w, W(color)); - col = RGBW32(r,g,b,w); - } else { - col = color_add(col, color); - } + col = color_add(col, color, fast); setPixelColorXY(x, y, col); } @@ -356,59 +359,71 @@ void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { } // blurRow: perform a blur on a row of a rectangular matrix -void Segment::blurRow(uint16_t row, fract8 blur_amount) { +void Segment::blurRow(uint32_t row, fract8 blur_amount, bool smear){ if (!isActive()) return; // not active const uint_fast16_t cols = virtualWidth(); const uint_fast16_t rows = virtualHeight(); if (row >= rows) return; // blur one row - uint8_t keep = 255 - blur_amount; + uint8_t keep = smear ? 255 : 255 - blur_amount; uint8_t seep = blur_amount >> 1; - CRGB carryover = CRGB::Black; - for (uint_fast16_t x = 0; x < cols; x++) { - CRGB cur = getPixelColorXY(x, row); - uint32_t before = uint32_t(cur); // remember color before blur - CRGB part = cur; - part.nscale8(seep); - cur.nscale8(keep); - cur += carryover; - if (x>0) { - CRGB prev = CRGB(getPixelColorXY(x-1, row)) + part; - setPixelColorXY(x-1, row, prev); + uint32_t carryover = BLACK; + uint32_t lastnew; + uint32_t last; + uint32_t curnew = 0; + for (unsigned x = 0; x < cols; x++) { + uint32_t cur = getPixelColorXY(x, row); + uint32_t part = color_fade(cur, seep); + curnew = color_fade(cur, keep); + if (x > 0) { + if (carryover) + curnew = color_add(curnew, carryover, !smear); // WLEDMM don't use "fast" when smear==true (better handling of bright colors) + uint32_t prev = color_add(lastnew, part, !smear);// WLEDMM + if (last != prev) // optimization: only set pixel if color has changed + setPixelColorXY(int(x - 1), int(row), prev); } - if (before != uint32_t(cur)) // optimization: only set pixel if color has changed - setPixelColorXY(x, row, cur); + else // first pixel + setPixelColorXY(int(x), int(row), curnew); + lastnew = curnew; + last = cur; // save original value for comparison on next iteration carryover = part; } + setPixelColorXY(int(cols-1), int(row), curnew); // set last pixel } // blurCol: perform a blur on a column of a rectangular matrix -void Segment::blurCol(uint16_t col, fract8 blur_amount) { +void Segment::blurCol(uint32_t col, fract8 blur_amount, bool smear) { if (!isActive()) return; // not active const uint_fast16_t cols = virtualWidth(); const uint_fast16_t rows = virtualHeight(); if (col >= cols) return; // blur one column - uint8_t keep = 255 - blur_amount; + uint8_t keep = smear ? 255 : 255 - blur_amount; uint8_t seep = blur_amount >> 1; - CRGB carryover = CRGB::Black; - for (uint_fast16_t y = 0; y < rows; y++) { - CRGB cur = getPixelColorXY(col, y); - CRGB part = cur; - uint32_t before = uint32_t(cur); // remember color before blur - part.nscale8(seep); - cur.nscale8(keep); - cur += carryover; - if (y>0) { - CRGB prev = CRGB(getPixelColorXY(col, y-1)) + part; - setPixelColorXY(col, y-1, prev); + uint32_t carryover = BLACK; + uint32_t lastnew; + uint32_t last; + uint32_t curnew = 0; + for (unsigned y = 0; y < rows; y++) { + uint32_t cur = getPixelColorXY(col, y); + uint32_t part = color_fade(cur, seep); + curnew = color_fade(cur, keep); + if (y > 0) { + if (carryover) + curnew = color_add(curnew, carryover, !smear); // WLEDMM don't use "fast" when smear==true (better handling of bright colors) + uint32_t prev = color_add(lastnew, part, !smear); // WLEDMM + if (last != prev) // optimization: only set pixel if color has changed + setPixelColorXY(int(col), int(y - 1), prev); } - if (before != uint32_t(cur)) // optimization: only set pixel if color has changed - setPixelColorXY(col, y, cur); - carryover = part; + else // first pixel + setPixelColorXY(int(col), int(y), curnew); + lastnew = curnew; + last = cur; //save original value for comparison on next iteration + carryover = part; } + setPixelColorXY(int(col), int(rows - 1), curnew); } // 1D Box blur (with added weight - blur_amount: [0=no blur, 255=max blur]) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 0bf310dc67..34e59e40f2 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -95,6 +95,8 @@ CRGB *Segment::_globalLeds = nullptr; uint16_t Segment::maxWidth = DEFAULT_LED_COUNT; uint16_t Segment::maxHeight = 1; +CRGBPalette16 Segment::_currentPalette = CRGBPalette16(CRGB::Black); + // copy constructor - creates a new segment by copy from orig, but does not copy buffers. Does not modify orig! Segment::Segment(const Segment &orig) { DEBUG_PRINTLN(F("-- Copy segment constructor --")); @@ -295,7 +297,7 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) { static CRGBPalette16 prevRandomPalette = CRGBPalette16(CRGB(BLACK)); byte tcp[76] = { 255 }; //WLEDMM: prevent out-of-range access in loadDynamicGradientPalette() if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0; - if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0; + if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0; // TODO remove strip dependency by moving customPalettes out of strip //default palette. Differs depending on effect if (pal == 0) switch (mode) { case FX_MODE_FIRE_2012 : pal = 35; break; // heat palette @@ -458,18 +460,17 @@ uint32_t Segment::currentColor(uint8_t slot, uint32_t colorNew) { return transitional && _t ? color_blend(_t->_colorT[slot], colorNew, progress(), true) : colorNew; } -CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) { - loadPalette(targetPalette, pal); +void Segment::setCurrentPalette() { + loadPalette(_currentPalette, palette); if (transitional && _t && progress() < 0xFFFFU) { // blend palettes // there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time) // minimum blend time is 100ms maximum is 65535ms unsigned long timeMS = millis() - _t->_start; uint16_t noOfBlends = (255U * timeMS / _t->_dur) - _t->_prevPaletteBlends; - for (int i=0; i_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, targetPalette, 48); - targetPalette = _t->_palT; // copy transitioning/temporary palette + for (unsigned i = 0; i < noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, _currentPalette, 48); + _currentPalette = _t->_palT; // copy transitioning/temporary palette } - return targetPalette; } void Segment::handleTransition() { @@ -793,14 +794,41 @@ void Segment::deletejMap() { } -// WLEDMM constants for mapping mode "Pinwheel" -constexpr int Pinwheel_Steps_Medium = 208; // no holes up to 32x32; 60fps -constexpr int Pinwheel_Size_Medium = 32; // larger than this -> use "Big" -constexpr int Pinwheel_Steps_Big = 360; // no holes expected up to 58x58; 40fps -constexpr float Int_to_Rad_Med = (DEG_TO_RAD * 360) / Pinwheel_Steps_Medium; // conversion: from 0...208 to Radians -constexpr float Int_to_Rad_Big = (DEG_TO_RAD * 360) / Pinwheel_Steps_Big; // conversion: from 0...360 to Radians -// WLEDMM end - +// Constants for mapping mode "Pinwheel" +#ifndef WLED_DISABLE_2D +constexpr int Pinwheel_Steps_Small = 72; // no holes up to 16x16 +constexpr int Pinwheel_Size_Small = 16; // larger than this -> use "Medium" +constexpr int Pinwheel_Steps_Medium = 192; // no holes up to 32x32 +constexpr int Pinwheel_Size_Medium = 32; // larger than this -> use "Big" +constexpr int Pinwheel_Steps_Big = 304; // no holes up to 50x50 +constexpr int Pinwheel_Size_Big = 50; // larger than this -> use "XL" +constexpr int Pinwheel_Steps_XL = 368; +constexpr float Int_to_Rad_Small = (DEG_TO_RAD * 360) / Pinwheel_Steps_Small; // conversion: from 0...72 to Radians +constexpr float Int_to_Rad_Med = (DEG_TO_RAD * 360) / Pinwheel_Steps_Medium; // conversion: from 0...192 to Radians +constexpr float Int_to_Rad_Big = (DEG_TO_RAD * 360) / Pinwheel_Steps_Big; // conversion: from 0...304 to Radians +constexpr float Int_to_Rad_XL = (DEG_TO_RAD * 360) / Pinwheel_Steps_XL; // conversion: from 0...368 to Radians + +constexpr int Fixed_Scale = 512; // fixpoint scaling factor (9bit for fraction) + +// Pinwheel helper function: pixel index to radians +static float getPinwheelAngle(int i, int vW, int vH) { + int maxXY = max(vW, vH); + if (maxXY <= Pinwheel_Size_Small) return float(i) * Int_to_Rad_Small; + if (maxXY <= Pinwheel_Size_Medium) return float(i) * Int_to_Rad_Med; + if (maxXY <= Pinwheel_Size_Big) return float(i) * Int_to_Rad_Big; + // else + return float(i) * Int_to_Rad_XL; +} +// Pinwheel helper function: matrix dimensions to number of rays +static int getPinwheelLength(int vW, int vH) { + int maxXY = max(vW, vH); + if (maxXY <= Pinwheel_Size_Small) return Pinwheel_Steps_Small; + if (maxXY <= Pinwheel_Size_Medium) return Pinwheel_Steps_Medium; + if (maxXY <= Pinwheel_Size_Big) return Pinwheel_Steps_Big; + // else + return Pinwheel_Steps_XL; +} +#endif // 1D strip uint16_t Segment::virtualLength() const { @@ -831,12 +859,8 @@ uint16_t Segment::virtualLength() const { else vLen = max(vW,vH) * 0.5; // get the longest dimension break; - case M12_sPinWheel: //WLEDMM - //vLen = full circle - if (max(vW,vH) <= Pinwheel_Size_Medium) - vLen = Pinwheel_Steps_Medium; - else - vLen = Pinwheel_Steps_Big; + case M12_sPinwheel: + vLen = getPinwheelLength(vW, vH); break; } return vLen; @@ -978,32 +1002,46 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATT } } break; - case M12_sPinWheel: { // WLEDMM - // i = angle --> 0 through 359 (Big), OR 0 through 208 (Medium) + case M12_sPinwheel: { + // i = angle --> 0 - 296 (Big), 0 - 192 (Medium), 0 - 72 (Small) float centerX = roundf((vW-1) / 2.0f); float centerY = roundf((vH-1) / 2.0f); - // int maxDistance = sqrt(centerX * centerX + centerY * centerY) + 1; - float angleRad = (max(vW,vH) > Pinwheel_Size_Medium) ? float(i) * Int_to_Rad_Big : float(i) * Int_to_Rad_Med; // angle in radians + float angleRad = getPinwheelAngle(i, vW, vH); // angle in radians float cosVal = cosf(angleRad); float sinVal = sinf(angleRad); + // avoid re-painting the same pixel + int lastX = INT_MIN; // impossible position + int lastY = INT_MIN; // impossible position // draw line at angle, starting at center and ending at the segment edge // we use fixed point math for better speed. Starting distance is 0.5 for better rounding - constexpr int_fast32_t Fixed_Scale = 512; // fixpoint scaling factor - int_fast32_t posx = (centerX + 0.5f * cosVal) * Fixed_Scale; // X starting position in fixed point - int_fast32_t posy = (centerY + 0.5f * sinVal) * Fixed_Scale; // Y starting position in fixed point - int_fast16_t inc_x = cosVal * Fixed_Scale; // X increment per step (fixed point) - int_fast16_t inc_y = sinVal * Fixed_Scale; // Y increment per step (fixed point) + // int_fast16_t and int_fast32_t types changed to int, minimum bits commented + int posx = (centerX + 0.5f * cosVal) * Fixed_Scale; // X starting position in fixed point 18 bit + int posy = (centerY + 0.5f * sinVal) * Fixed_Scale; // Y starting position in fixed point 18 bit + int inc_x = cosVal * Fixed_Scale; // X increment per step (fixed point) 10 bit + int inc_y = sinVal * Fixed_Scale; // Y increment per step (fixed point) 10 bit int32_t maxX = vW * Fixed_Scale; // X edge in fixedpoint int32_t maxY = vH * Fixed_Scale; // Y edge in fixedpoint - // draw until we hit any edge - while ((posx > 0) && (posy > 0) && (posx < maxX) && (posy < maxY)) { + + // Odd rays start further from center if prevRay started at center. + static int prevRay = INT_MIN; // previous ray number + if ((i % 2 == 1) && (i - 1 == prevRay || i + 1 == prevRay)) { + int jump = min(vW/3, vH/3); // can add 2 if using medium pinwheel + posx += inc_x * jump; + posy += inc_y * jump; + } + prevRay = i; + + // draw ray until we hit any edge + while ((posx >= 0) && (posy >= 0) && (posx < maxX) && (posy < maxY)) { // scale down to integer (compiler will replace division with appropriate bitshift) int x = posx / Fixed_Scale; int y = posy / Fixed_Scale; // set pixel - setPixelColorXY(x, y, col); + if (x != lastX || y != lastY) setPixelColorXY(x, y, col); // only paint if pixel position is different + lastX = x; + lastY = y; // advance to next position posx += inc_x; posy += inc_y; @@ -1030,11 +1068,7 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATT uint8_t _bri_t = currentBri(on ? opacity : 0); if (!_bri_t && !transitional && fadeTransition) return; // if _bri_t == 0 && segment is not transitioning && transitions are enabled then save a few CPU cycles if (_bri_t < 255) { - byte r = scale8(R(col), _bri_t); - byte g = scale8(G(col), _bri_t); - byte b = scale8(B(col), _bri_t); - byte w = scale8(W(col), _bri_t); - col = RGBW32(r, g, b, w); + col = color_fade(col, _bri_t); } // expand pixel (taking into account start, grouping, spacing [and offset]) @@ -1048,6 +1082,17 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATT } i += start; // starting pixel in a group +#if 0 // needs more testing + // WLEDMM shortcut when no grouping/spacing used + bool simpleSegment = !mirror && (grouping == 1) && (spacing == 0); + if (simpleSegment) { + int indexSet = i + offset; // offset/phase + if (indexSet >= stop) indexSet -= len; // wrap + strip.setPixelColor(indexSet, col); + return; + } +#endif + // set all the pixels in the group for (int j = 0; j < grouping; j++) { uint16_t indexSet = i + ((reverse) ? -j : j); @@ -1147,16 +1192,36 @@ uint32_t Segment::getPixelColor(int i) else return getPixelColorXY(vW / 2, vH / 2 - i - 1); break; - case M12_sPinWheel: //WLEDMM - // not 100% accurate, returns outer edge of circle - float distance = max(1.0f, min(vH-1, vW-1) / 2.0f); - float centerX = (vW - 1) / 2.0f; - float centerY = (vH - 1) / 2.0f; - float angleRad = (max(vW,vH) > Pinwheel_Size_Medium) ? float(i) * Int_to_Rad_Big : float(i) * Int_to_Rad_Med; // angle in radians - int x = roundf(centerX + distance * cosf(angleRad)); - int y = roundf(centerY + distance * sinf(angleRad)); + case M12_sPinwheel: + // not 100% accurate, returns pixel at outer edge + // i = angle --> 0 - 296 (Big), 0 - 192 (Medium), 0 - 72 (Small) + float centerX = roundf((vW-1) / 2.0f); + float centerY = roundf((vH-1) / 2.0f); + float angleRad = getPinwheelAngle(i, vW, vH); // angle in radians + float cosVal = cosf(angleRad); + float sinVal = sinf(angleRad); + + int posx = (centerX + 0.5f * cosVal) * Fixed_Scale; // X starting position in fixed point 18 bit + int posy = (centerY + 0.5f * sinVal) * Fixed_Scale; // Y starting position in fixed point 18 bit + int inc_x = cosVal * Fixed_Scale; // X increment per step (fixed point) 10 bit + int inc_y = sinVal * Fixed_Scale; // Y increment per step (fixed point) 10 bit + int32_t maxX = vW * Fixed_Scale; // X edge in fixedpoint + int32_t maxY = vH * Fixed_Scale; // Y edge in fixedpoint + + // trace ray from center until we hit any edge - to avoid rounding problems, we use the same method as in setPixelColor + int x = INT_MIN; + int y = INT_MIN; + while ((posx >= 0) && (posy >= 0) && (posx < maxX) && (posy < maxY)) { + // scale down to integer (compiler will replace division with appropriate bitshift) + x = posx / Fixed_Scale; + y = posy / Fixed_Scale; + // advance to next position + posx += inc_x; + posy += inc_y; + } return getPixelColorXY(x, y); - } + break; + } return 0; } #endif @@ -1357,42 +1422,43 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) { /* * blurs segment content, source: FastLED colorutils.cpp */ -void Segment::blur(uint8_t blur_amount) -{ +void Segment::blur(uint8_t blur_amount, bool smear) { if (!isActive() || blur_amount == 0) return; // optimization: 0 means "don't blur" #ifndef WLED_DISABLE_2D if (is2D()) { // compatibility with 2D - const uint_fast16_t cols = virtualWidth(); - const uint_fast16_t rows = virtualHeight(); - for (uint_fast16_t i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows - for (uint_fast16_t k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns + const uint_fast32_t cols = virtualWidth(); + const uint_fast32_t rows = virtualHeight(); + for (uint_fast32_t i = 0; i < rows; i++) blurRow(i, blur_amount, smear); // blur all rows + for (uint_fast32_t k = 0; k < cols; k++) blurCol(k, blur_amount, smear); // blur all columns return; } #endif - uint8_t keep = 255 - blur_amount; + uint8_t keep = smear ? 255 : 255 - blur_amount; uint8_t seep = blur_amount >> 1; - CRGB carryover = CRGB::Black; - uint_fast16_t vlength = virtualLength(); - for(uint_fast16_t i = 0; i < vlength; i++) - { - CRGB cur = CRGB(getPixelColor(i)); - CRGB part = cur; - uint32_t before = uint32_t(cur); // remember color before blur - part.nscale8(seep); - cur.nscale8(keep); - cur += carryover; - if(i > 0) { - uint32_t c = getPixelColor(i-1); - uint8_t r = R(c); - uint8_t g = G(c); - uint8_t b = B(c); - setPixelColor((uint16_t)(i-1), qadd8(r, part.red), qadd8(g, part.green), qadd8(b, part.blue)); + unsigned vlength = virtualLength(); + uint32_t carryover = BLACK; + uint32_t lastnew; + uint32_t last; + uint32_t curnew = 0; + for (unsigned i = 0; i < vlength; i++) { + uint32_t cur = getPixelColor(i); + uint32_t part = color_fade(cur, seep); + curnew = color_fade(cur, keep); + if (i > 0) { + if (carryover) + curnew = color_add(curnew, carryover, !smear); // WLEDMM + uint32_t prev = color_add(lastnew, part, !smear); // WLEDMM + if (last != prev) // optimization: only set pixel if color has changed + setPixelColor(int(i - 1), prev); } - if (before != uint32_t(cur)) // optimization: only set pixel if color has changed - setPixelColor((uint16_t)i,cur.red, cur.green, cur.blue); + else // first pixel + setPixelColor(int(i), curnew); + lastnew = curnew; + last = cur; // save original value for comparison on next iteration carryover = part; } + setPixelColor(int(vlength - 1), curnew); } /* @@ -1452,11 +1518,7 @@ uint32_t Segment::color_from_palette(uint_fast16_t i, bool mapping, bool wrap, u uint_fast16_t vLen = mapping ? virtualLength() : 1; if (mapping && vLen > 1) paletteIndex = (i*255)/(vLen -1); if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end" - CRGB fastled_col; - CRGBPalette16 curPal; - if (transitional && _t) curPal = _t->_palT; - else loadPalette(curPal, palette); - fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global + CRGB fastled_col = ColorFromPalette(_currentPalette, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, 0); } @@ -1734,7 +1796,7 @@ void WS2812FX::service() { _colors_t[0] = seg.currentColor(0, seg.colors[0]); _colors_t[1] = seg.currentColor(1, seg.colors[1]); _colors_t[2] = seg.currentColor(2, seg.colors[2]); - seg.currentPalette(_currentPalette, seg.palette); + seg.setCurrentPalette(); // load actual palette if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(seg.cct, true), correctWB); for (uint8_t c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]); @@ -2143,7 +2205,7 @@ void WS2812FX::makeAutoSegments(bool forceReset) { segStops[s] = segStarts[s] + b->getLength(); #ifndef WLED_DISABLE_2D - if (isMatrix && segStops[s] < Segment::maxWidth*Segment::maxHeight) continue; // ignore buses comprising matrix + if (isMatrix && segStops[s] <= Segment::maxWidth*Segment::maxHeight) continue; // ignore buses comprising matrix if (isMatrix && segStarts[s] < Segment::maxWidth*Segment::maxHeight) segStarts[s] = Segment::maxWidth*Segment::maxHeight; #endif @@ -2216,6 +2278,8 @@ void WS2812FX::fixInvalidSegments() { if (_segments[i].stop > _length) _segments[i].stop = _length; } } + // if any segments were deleted free memory + purgeSegments(); // this is always called as the last step after finalizeInit(), update covered bus types for (segment &seg : _segments) seg.refreshLightCapabilities(); diff --git a/wled00/alexa.cpp b/wled00/alexa.cpp index 179a522c01..b02c10e527 100644 --- a/wled00/alexa.cpp +++ b/wled00/alexa.cpp @@ -139,4 +139,5 @@ void onAlexaChange(EspalexaDevice* dev) #else void alexaInit(){} void handleAlexa(){} + #pragma message "Alexa interface disabled" #endif diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 3801c534de..fff5ecb384 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -40,6 +40,11 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte #define DEBUG_PRINTF(x...) #endif #else + // un-define USER_PRINT macros from bus_wrapper.h + #undef USER_PRINT + #undef USER_PRINTF + #undef USER_PRINTLN + #undef USER_FLUSH // WLEDMM use wled.h #include "wled.h" #endif @@ -175,7 +180,7 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { PolyBus::setPixelColor(_busPtr, _iType, pix, c, co); } -uint32_t BusDigital::getPixelColor(uint16_t pix) { +uint32_t IRAM_ATTR_YN BusDigital::getPixelColor(uint16_t pix) { if (reversed) pix = _len - pix -1; else pix += _skip; uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder); @@ -309,7 +314,27 @@ void BusPwm::setPixelColor(uint16_t pix, uint32_t c) { //does no index check uint32_t BusPwm::getPixelColor(uint16_t pix) { if (!_valid) return 0; +#if 1 + // WLEDMM stick with the old code - we don't have cctICused return RGBW32(_data[0], _data[1], _data[2], _data[3]); +#else + // TODO getting the reverse from CCT is involved (a quick approximation when CCT blending is ste to 0 implemented) + switch (_type) { + case TYPE_ANALOG_1CH: //one channel (white), relies on auto white calculation + return RGBW32(0, 0, 0, _data[0]); + case TYPE_ANALOG_2CH: //warm white + cold white + if (cctICused) return RGBW32(0, 0, 0, _data[0]); + else return RGBW32(0, 0, 0, _data[0] + _data[1]); + case TYPE_ANALOG_5CH: //RGB + warm white + cold white + if (cctICused) return RGBW32(_data[0], _data[1], _data[2], _data[3]); + else return RGBW32(_data[0], _data[1], _data[2], _data[3] + _data[4]); + case TYPE_ANALOG_4CH: //RGBW + return RGBW32(_data[0], _data[1], _data[2], _data[3]); + case TYPE_ANALOG_3CH: //standard dumb RGB + return RGBW32(_data[0], _data[1], _data[2], 0); + } + return RGBW32(_data[0], _data[0], _data[0], _data[0]); +#endif } void BusPwm::show() { @@ -461,6 +486,7 @@ void BusNetwork::cleanup() { // *************************************************************************** #ifdef WLED_ENABLE_HUB75MATRIX +#warning "HUB75 driver enabled (experimental)" BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { @@ -709,12 +735,12 @@ uint32_t BusManager::memUsage(BusConfig &bc) { int BusManager::add(BusConfig &bc) { if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1; - USER_PRINTF("BusManager::add(bc.type=%u)\n", bc.type); + DEBUG_PRINTF("BusManager::add(bc.type=%u)\n", bc.type); if (bc.type >= TYPE_NET_DDP_RGB && bc.type < 96) { busses[numBusses] = new BusNetwork(bc); #ifdef WLED_ENABLE_HUB75MATRIX } else if (bc.type >= TYPE_HUB75MATRIX && bc.type <= (TYPE_HUB75MATRIX + 10)) { - USER_PRINTLN("BusManager::add - Adding BusHub75Matrix"); + DEBUG_PRINTLN("BusManager::add - Adding BusHub75Matrix"); busses[numBusses] = new BusHub75Matrix(bc); #endif } else if (IS_DIGITAL(bc.type)) { @@ -724,6 +750,10 @@ int BusManager::add(BusConfig &bc) { } else { busses[numBusses] = new BusPwm(bc); } + // WLEDMM clear cached Bus info + lastBus = nullptr; + laststart = 0; + lastend = 0; return numBusses++; } @@ -734,6 +764,10 @@ void BusManager::removeAll() { while (!canAllShow()) yield(); for (uint8_t i = 0; i < numBusses; i++) delete busses[i]; numBusses = 0; + // WLEDMM clear cached Bus info + lastBus = nullptr; + laststart = 0; + lastend = 0; } void BusManager::show() { @@ -749,11 +783,24 @@ void BusManager::setStatusPixel(uint32_t c) { } void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c, int16_t cct) { + if ((pix >= laststart) && (pix < lastend ) && (lastBus != nullptr)) { + // WLEDMM same bus as last time - no need to search again + lastBus->setPixelColor(pix - laststart, c); + return; + } + for (uint_fast8_t i = 0; i < numBusses; i++) { // WLEDMM use fast native types Bus* b = busses[i]; uint_fast16_t bstart = b->getStart(); if (pix < bstart || pix >= bstart + b->getLength()) continue; - busses[i]->setPixelColor(pix - bstart, c); + else { + // WLEDMM remember last Bus we took + lastBus = b; + laststart = bstart; + lastend = bstart + b->getLength(); + b->setPixelColor(pix - bstart, c); + break; // WLEDMM found the right Bus -> so we can stop searching + } } } @@ -772,12 +819,23 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) { Bus::setCCT(cct); } -uint32_t BusManager::getPixelColor(uint_fast16_t pix) { // WLEDMM use fast native types +uint32_t IRAM_ATTR BusManager::getPixelColor(uint_fast16_t pix) { // WLEDMM use fast native types, IRAM_ATTR + if ((pix >= laststart) && (pix < lastend ) && (lastBus != nullptr)) { + // WLEDMM same bus as last time - no need to search again + return lastBus->getPixelColor(pix - laststart); + } + for (uint_fast8_t i = 0; i < numBusses; i++) { Bus* b = busses[i]; uint_fast16_t bstart = b->getStart(); if (pix < bstart || pix >= bstart + b->getLength()) continue; - return b->getPixelColor(pix - bstart); + else { + // WLEDMM remember last Bus we took + lastBus = b; + laststart = bstart; + lastend = bstart + b->getLength(); + return b->getPixelColor(pix - bstart); + } } return 0; } diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index f089b52985..0bfd3e04de 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -427,8 +427,12 @@ class BusManager { private: uint8_t numBusses = 0; - Bus* busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES]; + Bus* busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES] = {nullptr}; // WLEDMM init array ColorOrderMap colorOrderMap; + // WLEDMM cache last used Bus -> 20% to 30% speedup when using many LED pins + Bus *lastBus = nullptr; + unsigned laststart = 0; + unsigned lastend = 0; inline uint8_t getNumVirtualBusses() { int j = 0; diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h index 5e41f263d3..64dcd5b979 100644 --- a/wled00/bus_wrapper.h +++ b/wled00/bus_wrapper.h @@ -18,6 +18,28 @@ #endif // temporary end +// WLEDMM TroyHacks support - SLOWPATH has priority over TWOPATH +#ifdef WLEDMM_SLOWPATH +#undef WLEDMM_TWOPATH +#endif + +// WLEDMM repeat definition of USER_PRINT +bool canUseSerial(void); // WLEDMM (wled_serial.cpp) returns true if Serial can be used for debug output (i.e. not configured for other purpose) +#if defined(WLED_DEBUG_HOST) + #include "net_debug.h" + extern bool netDebugEnabled; + #define USER_PRINT(x) (netDebugEnabled || !canUseSerial())?NetDebug.print(x):Serial.print(x) + #define USER_PRINTLN(x) (netDebugEnabled || !canUseSerial())?NetDebug.println(x):Serial.println(x) + #define USER_PRINTF(x...) (netDebugEnabled || !canUseSerial())?NetDebug.printf(x):Serial.printf(x) + #define USER_FLUSH() (netDebugEnabled || !canUseSerial())?NetDebug.flush():Serial.flush() +#else + #define USER_PRINT(x) {if (canUseSerial()) Serial.print(x);} + #define USER_PRINTLN(x) {if (canUseSerial()) Serial.println(x);} + #define USER_PRINTF(x...) {if (canUseSerial()) Serial.printf(x);} + #define USER_FLUSH() {if (canUseSerial()) Serial.flush();} +#endif +// WLEDMM end + //Hardware SPI Pins #define P_8266_HS_MOSI 13 #define P_8266_HS_CLK 14 @@ -389,6 +411,17 @@ class PolyBus { } }; static void* create(uint8_t busType, uint8_t* pins, uint16_t len, uint8_t channel, uint16_t clock_kHz = 0U) { + #if defined(ARDUINO_ARCH_ESP32) && !(defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3)) + #if defined(WLEDMM_FASTPATH) && !defined(WLEDMM_SLOWPATH) // WLEDMM only for fastpath builds. + // NOTE: "channel" is only used on ESP32 (and its variants) for RMT channel allocation + // since 0.15.0-b3 I2S1 is favoured for classic ESP32 and moved to position 0 (channel 0) so we need to subtract 1 for correct RMT allocation + #if defined(WLEDMM_TWOPATH) + if (channel > 1) channel--; // accommodate I2S1 which is used as 2nd bus on classic ESP32 + #else + if (channel > 0) channel--; // accommodate I2S1 which is used as 1st bus on classic ESP32 + #endif + #endif + #endif void* busPtr = nullptr; switch (busType) { case I_NONE: break; @@ -423,20 +456,20 @@ class PolyBus { case I_8266_BB_UCS_4: busPtr = new B_8266_BB_UCS_4(len, pins[0]); break; #endif #ifdef ARDUINO_ARCH_ESP32 - case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); break; + case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); USER_PRINTF("(RMT #%u) ", channel); break; #ifndef WLED_NO_I2S0_PIXELBUS - case I_32_I0_NEO_3: busPtr = new B_32_I0_NEO_3(len, pins[0]); break; + case I_32_I0_NEO_3: busPtr = new B_32_I0_NEO_3(len, pins[0]); USER_PRINT("(I2S #0) "); break; #endif #ifndef WLED_NO_I2S1_PIXELBUS - case I_32_I1_NEO_3: busPtr = new B_32_I1_NEO_3(len, pins[0]); break; + case I_32_I1_NEO_3: busPtr = new B_32_I1_NEO_3(len, pins[0]); USER_PRINT("(I2S #1) "); break; #endif // case I_32_BB_NEO_3: busPtr = new B_32_BB_NEO_3(len, pins[0], (NeoBusChannel)channel); break; - case I_32_RN_NEO_4: busPtr = new B_32_RN_NEO_4(len, pins[0], (NeoBusChannel)channel); break; + case I_32_RN_NEO_4: busPtr = new B_32_RN_NEO_4(len, pins[0], (NeoBusChannel)channel); USER_PRINTF("(RGBW RMT #%u) ", channel); break; #ifndef WLED_NO_I2S0_PIXELBUS - case I_32_I0_NEO_4: busPtr = new B_32_I0_NEO_4(len, pins[0]); break; + case I_32_I0_NEO_4: busPtr = new B_32_I0_NEO_4(len, pins[0]); USER_PRINT("(RGBW I2S #0) "); break; #endif #ifndef WLED_NO_I2S1_PIXELBUS - case I_32_I1_NEO_4: busPtr = new B_32_I1_NEO_4(len, pins[0]); break; + case I_32_I1_NEO_4: busPtr = new B_32_I1_NEO_4(len, pins[0]); USER_PRINT("(RGBW I2S #1) "); break; #endif // case I_32_BB_NEO_4: busPtr = new B_32_BB_NEO_4(len, pins[0], (NeoBusChannel)channel); break; case I_32_RN_400_3: busPtr = new B_32_RN_400_3(len, pins[0], (NeoBusChannel)channel); break; @@ -1179,14 +1212,25 @@ class PolyBus { #else // standard ESP32 has 8 RMT and 2 I2S channels #ifndef WLEDMM_FASTPATH - if (num > 9) return I_NONE; - if (num == 8) offset = 2; // first use I2S#1 (so #0 stays available for audio) - if (num == 9) offset = 1; // use I2S#0 as the last driver + #ifdef WLEDMM_SLOWPATH // I2S flickers on large installs. Favor stability over framerate. + if (num > 7) return I_NONE; + #else + if (num == 8) offset = 2; // first use I2S#1 (so #0 stays available for audio) + if (num == 9) offset = 1; // use I2S#0 as the last driver + if (num > 9) return I_NONE; + #endif #else - // ESP32 "audio_fastpath" - 8 RMT and 1 I2S channels. RMT 5-8 have sending delays, so use I2S#1 before going for RMT 5-8 - if (num > 8) return I_NONE; - if (num == 1) offset = 2; // use I2S#1 as 2nd bus - seems to be a good compromise for performance, and reduces flickering for some users - //if (num == 0) offset = 2; // un-comment to use I2S#1 as 1st bus - sometimes helps, if you experience flickering during Wifi or filesystem activity. + // ESP32 "audio_fastpath" - 8 RMT and 1 I2S channels. RMT 5-8 have sending delays, so use I2S#1 before going for RMT 5-8 + #ifdef WLEDMM_SLOWPATH // I2S flickers on large installs. Favor stability over framerate. + if (num > 7) return I_NONE; + #else + if (num > 8) return I_NONE; + #if defined(WLEDMM_TWOPATH) + if (num == 1) offset = 2; // use I2S#1 as 2nd bus - seems to be a good compromise for performance, and reduces flickering for some users + #else + if (num == 0) offset = 2; // use I2S#1 as 1st bus - sometimes helps, if you experience flickering during Wifi or filesystem activity. + #endif + #endif #endif #endif switch (busType) { diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 54c32f9c07..24e923c8e9 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -795,7 +795,7 @@ void serializeConfig() { matrix["psl"] = strip.panelO.serpentine; JsonArray panels = matrix.createNestedArray(F("panels")); - for (uint8_t i=0; i max) max = g; - if (b > max) max = b; - if (w > max) max = w; - if (max < 256) return RGBW32(r, g, b, w); - else return RGBW32(r * 255 / max, g * 255 / max, b * 255 / max, w * 255 / max); + if (c2 == 0) return c1; // WLEDMM shortcut + if (c1 == 0) return c2; // WLEDMM shortcut + + if (fast) { + uint8_t r = R(c1); + uint8_t g = G(c1); + uint8_t b = B(c1); + uint8_t w = W(c1); + r = qadd8(r, R(c2)); + g = qadd8(g, G(c2)); + b = qadd8(b, B(c2)); + w = qadd8(w, W(c2)); + return RGBW32(r,g,b,w); + } else { + uint32_t r = R(c1) + R(c2); + uint32_t g = G(c1) + G(c2); + uint32_t b = B(c1) + B(c2); + uint32_t w = W(c1) + W(c2); + uint32_t max = r; + if (g > max) max = g; + if (b > max) max = b; + if (w > max) max = w; + if (max < 256) return RGBW32(r, g, b, w); + else return RGBW32(r * 255 / max, g * 255 / max, b * 255 / max, w * 255 / max); + } +} + +/* + * fades color toward black + * if using "video" method the resulting color will never become black unless it is already black + */ + +IRAM_ATTR_YN uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) +{ + if (amount == 0) return 0; // WLEDMM shortcut + + uint32_t scaledcolor; // color order is: W R G B from MSB to LSB + uint32_t r = R(c1); + uint32_t g = G(c1); + uint32_t b = B(c1); + uint32_t w = W(c1); + if (video) { + uint32_t scale = amount; // 32bit for faster calculation + scaledcolor = (((r * scale) >> 8) << 16) + ((r && scale) ? 1 : 0); + scaledcolor |= (((g * scale) >> 8) << 8) + ((g && scale) ? 1 : 0); + scaledcolor |= ((b * scale) >> 8) + ((b && scale) ? 1 : 0); + if (w>0) scaledcolor |= (((w * scale) >> 8) << 24) + ((scale) ? 1 : 0); // WLEDMM small speedup when no white channel + return scaledcolor; + } + else { + uint32_t scale = 1 + amount; + scaledcolor = ((r * scale) >> 8) << 16; + scaledcolor |= ((g * scale) >> 8) << 8; + scaledcolor |= (b * scale) >> 8; + if (w>0) scaledcolor |= ((w * scale) >> 8) << 24; // WLEDMM small speedup when no white channel + return scaledcolor; + } } void setRandomColor(byte* rgb) diff --git a/wled00/const.h b/wled00/const.h index 14f419c224..7857a1d33d 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -253,7 +253,7 @@ #define TYPE_NET_ARTNET_RGB 82 //network ArtNet RGB bus (master broadcast bus, unused) #define TYPE_NET_DDP_RGBW 88 //network DDP RGBW bus (master broadcast bus) -#define IS_DIGITAL(t) ((t) & 0x10) //digital are 16-31 and 48-63 +#define IS_DIGITAL(t) (((t) & 0x10) || ((t)==TYPE_HUB75MATRIX)) //digital are 16-31 and 48-63 // WLEDMM added HUB75 #define IS_PWM(t) ((t) > 40 && (t) < 46) #define NUM_PWM_PINS(t) ((t) - 40) //for analog PWM 41-45 only #define IS_2PIN(t) ((t) > 47) @@ -351,6 +351,7 @@ #define ERR_LOW_MEM 33 // WLEDMM: low memory (RAM) #define ERR_LOW_SEG_MEM 34 // WLEDMM: low memory (segment data RAM) #define ERR_LOW_WS_MEM 35 // WLEDMM: low memory (ws) +#define ERR_LOW_AJAX_MEM 36 // WLEDMM: low memory (oappend) // Timer mode types #define NL_MODE_SET 0 //After nightlight time elapsed, set to target brightness diff --git a/wled00/data/index.js b/wled00/data/index.js index 2a4b51baed..1fcbfcaf5e 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -680,6 +680,7 @@ function populateInfo(i) if (i.ver.includes("0.14.0-b2")) vcn = "This is the way"; // recently watched The Mandalorian? I have spoken ;-) if (i.ver.includes("0.14.0-b15.22")) vcn = "Lupo"; if (i.ver.includes("0.14.1-b3")) vcn = "Fried Chicken"; // final line of "One Vision" by Queen + if (i.ver.includes("0.14.3-b")) vcn = "Fried Chicken"; cn += `v${i.ver}  "${vcn}"

(WLEDMM_${i.ver} ${i.rel}.bin)

build ${i.vid}

${urows} ${urows===""?'':''} @@ -690,24 +691,22 @@ ${inforow("Build",i.vid)} ${inforow("Estimated current",pwru)} ${inforow("Average FPS",i.leds.fps)} ${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")} +${inforow("MAC address",i.mac)} ${inforow("Uptime",getRuntimeStr(i.uptime))} + +${inforow("Filesystem",i.fs.u + "/" + i.fs.t + " kB, " +Math.round(i.fs.u*100/i.fs.t) + "%")} +${theap>0?inforow("Heap ☾",((i.totalheap-i.freeheap)/1000).toFixed(0)+"/"+theap.toFixed(0)+" kB",", "+Math.round((i.totalheap-i.freeheap)/(10*theap))+"%"):inforow("Free heap",heap," kB")} +${i.minfreeheap?inforow("Max used heap ☾",((i.totalheap-i.minfreeheap)/1000).toFixed(0)+" kB",", "+Math.round((i.totalheap-i.minfreeheap)/(10*theap))+"%"):""} +${i.psram?inforow("PSRAM ☾",((i.tpram-i.psram)/1024).toFixed(0)+"/"+(i.tpram/1024).toFixed(0)+" kB",", "+((i.tpram-i.psram)*100.0/i.tpram).toFixed(1)+"%"):""} +${i.psusedram?inforow("Max used PSRAM ☾",((i.tpram-i.psusedram)/1024).toFixed(0)+" kB",", "+((i.tpram-i.psusedram)*100.0/i.tpram).toFixed(1)+"%"):""} +${i.freestack?inforow("Free stack ☾",(i.freestack/1000).toFixed(3)," kB"):""} -${inforow("Filesystem",i.fs.u + "/" + i.fs.t + " kB (" +Math.round(i.fs.u*100/i.fs.t) + "%)")} -${theap>0?inforow("Heap ☾",((i.totalheap-i.freeheap)/1000).toFixed(0)+"/"+theap.toFixed(0)+" kB"," ("+Math.round((i.totalheap-i.freeheap)/(10*theap))+"%)"):""} -${i.minfreeheap?inforow("Max used heap ☾",((i.totalheap-i.minfreeheap)/1000).toFixed(1)+" kB"," ("+Math.round((i.totalheap-i.minfreeheap)/(10*theap))+"%)"):""} -${inforow("Free heap",heap," kB")} -${i.freestack?inforow("Free stack ☾",(i.freestack/1024).toFixed(3)," kB"):""} -${inforow("Flash Size ☾",flashsize," kB")} -${i.tpram?inforow("PSRAM ☾",(i.tpram/1024).toFixed(1)," kB"):""} -${i.psram?((i.tpram-i.psram)>16383?inforow("Used PSRAM ☾",((i.tpram-i.psram)/1024).toFixed(1)," kB"):inforow("Used PSRAM ☾",(i.tpram-i.psram)," B")):""} -${i.psusedram?((i.tpram-i.psusedram)>16383?inforow("Max used PSRAM ☾",((i.tpram-i.psusedram)/1024).toFixed(1)," kB"):inforow("Max used PSRAM ☾",(i.tpram-i.psusedram)," B")):""} -${i.psram?inforow("Free PSRAM",(i.psram/1024).toFixed(1)," kB"):""} -${inforow("MAC address",i.mac)} +${i.tpram?inforow("PSRAM " + (i.psrmode?"("+i.psrmode+" mode) ":"") + " ☾",(i.tpram/1024/1024).toFixed(0)," MB"):""} +${i.e32flash?inforow("Flash mode "+i.e32flashmode+i.e32flashtext + " ☾",i.e32flash+" MB, "+i.e32flashspeed," Mhz"):""} +${i.e32model?inforow(i.e32model + " ☾",i.e32cores +" core(s),"," "+i.e32speed+" Mhz"):""} ${inforow("Environment",i.arch + " " + i.core + " (" + i.lwip + ")")} -${i.e32model?inforow(i.e32model + " ☾",i.e32cores +" core(s)"," "+i.e32speed+" Mhz"):""} -${i.e32flash?inforow("Flash "+i.e32flash+"MB"+" mode "+i.e32flashmode+i.e32flashtext + " ☾",i.e32flashspeed," Mhz"):""} ${i.e32code?inforow("Last ESP Restart ☾",i.e32code+" "+i.e32text):""} ${i.e32core0code?inforow("Core0 rst reason ☾",i.e32core0code, " "+i.e32core0text):""} ${i.e32core1code?inforow("Core1 rst reason ☾",i.e32core1code, " "+i.e32core1text):""} @@ -1952,6 +1951,18 @@ function readState(s,command=false) if (s.error && s.error != 0) { var errstr = ""; switch (s.error) { + case 1: + errstr = "Denied!"; + break; + case 3: + errstr = "Buffer locked!"; + break; + case 8: + errstr = "Effect RAM depleted!"; + break; + case 9: + errstr = "JSON parsing error!"; + break; case 10: errstr = "Could not mount filesystem!"; break; @@ -1964,6 +1975,9 @@ function readState(s,command=false) case 13: errstr = "Missing ir.json."; break; + case 14: + errstr = "Missing remote.json."; + break; case 19: errstr = "A filesystem error has occured."; break; @@ -1976,6 +1990,9 @@ function readState(s,command=false) case 35: errstr = "Low Memory (WS data)."; break; + case 36: + errstr = "Low Memory (oappend buffer)."; + break; } showToast('Error ' + s.error + ": " + errstr, true); } diff --git a/wled00/dmx_input.cpp b/wled00/dmx_input.cpp index af0bb679dc..77ebc85992 100644 --- a/wled00/dmx_input.cpp +++ b/wled00/dmx_input.cpp @@ -1,6 +1,7 @@ #include "wled.h" #ifdef WLED_ENABLE_DMX_INPUT +#pragma message "DMX physical input driver enabled" #ifdef ESP8266 #error DMX input is only supported on ESP32 diff --git a/wled00/dmx_output.cpp b/wled00/dmx_output.cpp index aa01c8a3dc..b3f2eadd2a 100644 --- a/wled00/dmx_output.cpp +++ b/wled00/dmx_output.cpp @@ -11,6 +11,7 @@ */ #ifdef WLED_ENABLE_DMX +#pragma message "DMX network output enabled" // WLEDMM: seems that DMX output triggers watchdog resets when compiling for IDF 4.4.x #ifdef ARDUINO_ARCH_ESP32 diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index e3473043a9..38a18b09f0 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -51,7 +51,8 @@ bool getJsonValue(const JsonVariant& element, DestType& destination, const Defau //colors.cpp 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); // 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); inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); } void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb void colorKtoRGB(uint16_t kelvin, byte* rgb); @@ -92,6 +93,7 @@ bool readObjectFromFileUsingId(const char* file, uint16_t id, JsonDocument* dest bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest); void updateFSInfo(); void closeFile(); +void invalidateFileNameCache(); // WLEDMM call when new files were uploaded //hue.cpp void handleHue(); diff --git a/wled00/file.cpp b/wled00/file.cpp index bf7c1a7a9a..4e23d5ca8c 100644 --- a/wled00/file.cpp +++ b/wled00/file.cpp @@ -35,6 +35,14 @@ static File f; // don't export to other cpp files //wrapper to find out how long closing takes void closeFile() { + #ifdef ARDUINO_ARCH_ESP32 + // WLEDMM: file.close() triggers flash writing. While flash is writing, the NPB RMT driver cannot fill its buffer which may create glitches. + unsigned long t_wait = millis(); + while(strip.isUpdating() && (millis() - t_wait < 72)) delay(1); // WLEDMM try to catch a moment when strip is idle + while(strip.isUpdating() && (millis() - t_wait < 96)) delay(0); // try harder + //if (strip.isUpdating()) USER_PRINTLN("closeFile: strip still updating."); + delay(2); // might help + #endif #ifdef WLED_DEBUG_FS DEBUGFS_PRINT(F("Close -> ")); uint32_t s = millis(); @@ -399,19 +407,122 @@ static String getContentType(AsyncWebServerRequest* request, String filename){ return "text/plain"; } +#if defined(BOARD_HAS_PSRAM) && (defined(WLED_USE_PSRAM) || defined(WLED_USE_PSRAM_JSON)) +// caching presets in PSRAM may prevent occasional flashes seen when HomeAssistant polls WLED +// original idea by @akaricchi (https://github.com/Akaricchi) +// returns a pointer to the PSRAM buffer, updates size parameter +static const uint8_t *getPresetCache(size_t &size) { + static unsigned long presetsCachedTime = 0; + static uint8_t *presetsCached = nullptr; + static size_t presetsCachedSize = 0; + static byte presetsCachedValidate = 0; + + if (!psramFound()) { + size = 0; + return nullptr; + } + + //if (presetsModifiedTime != presetsCachedTime) DEBUG_PRINTLN(F("getPresetCache(): presetsModifiedTime changed.")); + //if (presetsCachedValidate != cacheInvalidate) DEBUG_PRINTLN(F("getPresetCache(): cacheInvalidate changed.")); + + if ((presetsModifiedTime != presetsCachedTime) || (presetsCachedValidate != cacheInvalidate)) { + if (presetsCached) { + free(presetsCached); + presetsCached = nullptr; + } + } + + if (!presetsCached) { + File file = WLED_FS.open("/presets.json", "r"); + if (file) { + presetsCachedTime = presetsModifiedTime; + presetsCachedValidate = cacheInvalidate; + presetsCachedSize = 0; + presetsCached = (uint8_t*)ps_malloc(file.size() + 1); + if (presetsCached) { + presetsCachedSize = file.size(); + file.read(presetsCached, presetsCachedSize); + presetsCached[presetsCachedSize] = 0; + file.close(); + //USER_PRINTLN(F("getPresetCache(): /presets.json cached in PSRAM.")); + } + } + } else { + //USER_PRINTLN(F("getPresetCache(): /presets.json served from PSRAM.")); + } + + size = presetsCachedSize; + return presetsCached; +} +#endif + +// WLEDMM +static bool haveLedmapFile = true; +static bool haveIndexFile = true; +static bool haveSkinFile = true; +static bool haveICOFile = true; +static bool haveCpalFile = true; +void invalidateFileNameCache() { // reset "file not found" cache + haveLedmapFile = true; + haveIndexFile = true; + haveSkinFile = true; + haveICOFile = true; + haveCpalFile = true; + + #if defined(BOARD_HAS_PSRAM) && (defined(WLED_USE_PSRAM) || defined(WLED_USE_PSRAM_JSON)) + // WLEDMM hack to clear presets.json cache + size_t dummy; + unsigned long realpresetsTime = presetsModifiedTime; + presetsModifiedTime = toki.second(); // pretend we have changes + (void) getPresetCache(dummy); // clear presets.json cache + presetsModifiedTime = realpresetsTime; // restore correct value +#endif + //USER_PRINTLN("WS FileRead cache cleared"); +} + bool handleFileRead(AsyncWebServerRequest* request, String path){ DEBUG_PRINTLN("WS FileRead: " + path); if(path.endsWith("/")) path += "index.htm"; if(path.indexOf("sec") > -1) return false; + + // WLEDMM shortcuts + if ((haveLedmapFile == false) && path.equals("/ledmap.json")) return false; + if ((haveIndexFile == false) && path.equals("/index.htm")) return false; + if ((haveSkinFile == false) && path.equals("/skin.css")) return false; + if ((haveICOFile == false) && path.equals("/favicon.ico")) return false; + if ((haveCpalFile == false) && path.equals("/cpal.htm")) return false; + // WLEDMM toDO: add file caching (PSRAM) for /presets.json an /cfg.json + String contentType = getContentType(request, path); /*String pathWithGz = path + ".gz"; if(WLED_FS.exists(pathWithGz)){ request->send(WLED_FS, pathWithGz, contentType); return true; }*/ - if(WLED_FS.exists(path)) { - request->send(WLED_FS, path, contentType); + + #if defined(BOARD_HAS_PSRAM) && (defined(WLED_USE_PSRAM) || defined(WLED_USE_PSRAM_JSON)) + if (path.endsWith("/presets.json")) { + size_t psize; + const uint8_t *presets = getPresetCache(psize); + if (presets) { + AsyncWebServerResponse *response = request->beginResponse_P(200, contentType, presets, psize); + request->send(response); + return true; + } + } + #endif + + if(WLED_FS.exists(path) || WLED_FS.exists(path + ".gz")) { + request->send(WLED_FS, path, String(), request->hasArg(F("download"))); return true; } + //USER_PRINTLN("WS FileRead failed: " + path + " (" + contentType + ")"); + + // WLEDMM cache "file not found" results (reduces LED flickering during UI activities) + if (path.equals("/ledmap.json")) haveLedmapFile = false; + if (path.equals("/index.htm")) haveIndexFile = false; + if (path.equals("/skin.css")) haveSkinFile = false; + if (path.equals("/favicon.ico")) haveICOFile = false; + if (path.equals("/cpal.htm")) haveCpalFile = false; return false; } diff --git a/wled00/hue.cpp b/wled00/hue.cpp index 950c548967..54cddd33d1 100644 --- a/wled00/hue.cpp +++ b/wled00/hue.cpp @@ -205,4 +205,5 @@ void onHueData(void* arg, AsyncClient* client, void *data, size_t len) #else void handleHue(){} void reconnectHue(){} +#pragma message "Philips HUE bridge interface disabled" #endif diff --git a/wled00/improv.cpp b/wled00/improv.cpp index af30065dc0..eef8ad82e7 100644 --- a/wled00/improv.cpp +++ b/wled00/improv.cpp @@ -50,6 +50,7 @@ void handleImprovPacket() { uint8_t rpcCommandType = 0; char rpcData[128]; rpcData[0] = 0; + if (!Serial) return; // WLEDMM avoid reading from unconnected USB-CDC while (!timeout) { if (Serial.available() < 1) { @@ -210,7 +211,7 @@ void sendImprovInfoResponse() { //Use serverDescription if it has been changed from the default "WLED", else mDNS name bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0); char vString[32]; - snprintf_P(vString, sizeof(vString)-1, PSTR("0.14.1-b30.37/%i"),VERSION); + snprintf_P(vString, sizeof(vString)-1, PSTR("0.14.1-b31.38/%i"),VERSION); const char *str[4] = {"WLED", vString, bString, useMdnsName ? cmDNS : serverDescription}; sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str); diff --git a/wled00/ir.cpp b/wled00/ir.cpp index c3e125449a..bfb7926433 100644 --- a/wled00/ir.cpp +++ b/wled00/ir.cpp @@ -7,6 +7,7 @@ */ #if defined(WLED_DISABLE_INFRARED) +#pragma message "IR remote support disabled" void handleIR(){} #else diff --git a/wled00/json.cpp b/wled00/json.cpp index 8bd9cd2d87..5456b45567 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -516,7 +516,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId) //bool didSet = false; for (size_t s = 0; s < strip.getSegmentsNum(); s++) { Segment &sg = strip.getSegment(s); - if (sg.isSelected()) { + if (sg.isActive() && sg.isSelected()) { inDeepCall = true; // WLEDMM remember that we are going into recursion deserializeSegment(segVar, s, presetId); if (iAmGroot) inDeepCall = false; // WLEDMM toplevel -> reset recursion flag @@ -597,6 +597,8 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId) } } + doAdvancePlaylist = root[F("np")] | doAdvancePlaylist; //advances to next preset in playlist when true + stateUpdated(callMode); if (presetToRestore) currentPreset = presetToRestore; @@ -729,11 +731,7 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme nl["dur"] = nightlightDelayMins; nl["mode"] = nightlightMode; nl[F("tbri")] = nightlightTargetBri; - if (nightlightActive) { - nl[F("rem")] = (nightlightDelayMs - (millis() - nightlightStartTime)) / 1000; // seconds remaining - } else { - nl[F("rem")] = -1; - } + nl[F("rem")] = nightlightActive ? (int)(nightlightDelayMs - (millis() - nightlightStartTime)) / 1000 : -1; // seconds remaining JsonObject udpn = root.createNestedObject("udpn"); udpn["send"] = notifyDirect; @@ -899,9 +897,10 @@ String restartCode2Info(esp_reset_reason_t reason) { void serializeInfo(JsonObject root) { root[F("ver")] = versionString; - root[F("rel")] = releaseString; //WLEDMM to add bin name root[F("vid")] = VERSION; - //root[F("cn")] = WLED_CODENAME; + //root[F("cn")] = F(WLED_CODENAME); //WLEDMM removed + root[F("release")] = FPSTR(releaseString); + root[F("rel")] = FPSTR(releaseString); //WLEDMM to add bin name JsonObject leds = root.createNestedObject("leds"); leds[F("count")] = strip.getLengthTotal(); @@ -1020,23 +1019,36 @@ void serializeInfo(JsonObject root) wifi_info[F("txPower")] = (int) WiFi.getTxPower(); wifi_info[F("sleep")] = (bool) WiFi.getSleep(); #endif - #if !defined(CONFIG_IDF_TARGET_ESP32C2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3) + //#if !defined(CONFIG_IDF_TARGET_ESP32C2) && !defined(CONFIG_IDF_TARGET_ESP32C6) && !defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3) + #if CONFIG_IDF_TARGET_ESP32 root[F("arch")] = "esp32"; #else root[F("arch")] = ESP.getChipModel(); #endif root[F("core")] = ESP.getSdkVersion(); + root[F("clock")] = ESP.getCpuFreqMHz(); + root[F("flash")] = (ESP.getFlashChipSize()/1024)/1024; //root[F("maxalloc")] = ESP.getMaxAllocHeap(); #ifdef WLED_DEBUG root[F("resetReason0")] = (int)rtc_get_reset_reason(0); if(ESP.getChipCores() > 1) // WLEDMM root[F("resetReason1")] = (int)rtc_get_reset_reason(1); #endif + + #if defined(ARDUINO_ARCH_ESP32) + unsigned long t_wait = millis(); + while(strip.isUpdating() && (millis() - t_wait < 125)) delay(1); // WLEDMM try to catch a moment when strip is idle + while(strip.isUpdating() && (millis() - t_wait < 160)) yield(); // try harder + //if (strip.isUpdating()) USER_PRINTLN("serializeInfo: strip still updating."); + #endif + root[F("lwip")] = 0; //deprecated root[F("totalheap")] = ESP.getHeapSize(); //WLEDMM #else root[F("arch")] = "esp8266"; root[F("core")] = ESP.getCoreVersion(); + root[F("clock")] = ESP.getCpuFreqMHz(); + root[F("flash")] = (ESP.getFlashChipSize()/1024)/1024; //root[F("maxalloc")] = ESP.getMaxFreeBlockSize(); #ifdef WLED_DEBUG root[F("resetReason")] = (int)ESP.getResetInfoPtr()->reason; @@ -1056,6 +1068,13 @@ void serializeInfo(JsonObject root) root[F("tpram")] = ESP.getPsramSize(); //WLEDMM root[F("psram")] = ESP.getFreePsram(); root[F("psusedram")] = ESP.getMinFreePsram(); + #if CONFIG_ESP32S3_SPIRAM_SUPPORT // WLEDMM -S3 has "qspi" or "opi" PSRAM mode + #if CONFIG_SPIRAM_MODE_OCT + root[F("psrmode")] = F("🚀 OPI"); + #elif CONFIG_SPIRAM_MODE_QUAD + root[F("psrmode")] = F("qspi 🛻"); + #endif + #endif } #else // for testing @@ -1521,6 +1540,8 @@ void serveJson(AsyncWebServerRequest* request) #ifdef WLED_ENABLE_JSONLIVE #define MAX_LIVE_LEDS 180 +#warning "JSON Live enabled" + bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient) { #ifdef WLED_ENABLE_WEBSOCKETS diff --git a/wled00/lx_parser.cpp b/wled00/lx_parser.cpp index e717f941a3..5fe7c18c31 100644 --- a/wled00/lx_parser.cpp +++ b/wled00/lx_parser.cpp @@ -68,4 +68,6 @@ void parseLxJson(int lxValue, byte segId, bool secondary) } } +#else +#pragma message "Loxone support disabled" #endif diff --git a/wled00/mqtt.cpp b/wled00/mqtt.cpp index a5caaf472e..31162148c9 100644 --- a/wled00/mqtt.cpp +++ b/wled00/mqtt.cpp @@ -195,4 +195,6 @@ bool initMqtt() mqtt->connect(); return true; } +#else +#pragma message "MQTT disabled" #endif diff --git a/wled00/net_debug.cpp b/wled00/net_debug.cpp index a345dd30e9..43c47ac13b 100644 --- a/wled00/net_debug.cpp +++ b/wled00/net_debug.cpp @@ -26,4 +26,6 @@ size_t NetworkDebugPrinter::write(const uint8_t *buf, size_t size) { NetworkDebugPrinter NetDebug; +#else +#pragma message "Net debug disabled" #endif diff --git a/wled00/network.cpp b/wled00/network.cpp index 6c94c4472c..1e414d81f3 100644 --- a/wled00/network.cpp +++ b/wled00/network.cpp @@ -4,6 +4,8 @@ #ifdef WLED_USE_ETHERNET +#pragma message "Ethernet support enabled" + // The following six pins are neither configurable nor // can they be re-assigned through IOMUX / GPIO matrix. // See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-ethernet-kit-v1.1.html#ip101gri-phy-interface diff --git a/wled00/playlist.cpp b/wled00/playlist.cpp index 236cc32432..1d6f7a99a8 100644 --- a/wled00/playlist.cpp +++ b/wled00/playlist.cpp @@ -137,7 +137,7 @@ void handlePlaylist() { return; // but don't progress to next extry, and don't shuffle } - if (millis() - presetCycledTime > (100*playlistEntryDur)) { +if (millis() - presetCycledTime > (100 * playlistEntryDur) || doAdvancePlaylist) { presetCycledTime = millis(); if (bri == 0 || nightlightActive) return; @@ -159,6 +159,7 @@ void handlePlaylist() { transitionDelayTemp = playlistEntries[playlistIndex].tr * 100; playlistEntryDur = playlistEntries[playlistIndex].dur; applyPreset(playlistEntries[playlistIndex].preset); + doAdvancePlaylist = false; } } diff --git a/wled00/remote.cpp b/wled00/remote.cpp index 5a993c295c..64c668c579 100644 --- a/wled00/remote.cpp +++ b/wled00/remote.cpp @@ -17,9 +17,15 @@ #define WIZMOTE_BUTTON_BRIGHT_UP 9 #define WIZMOTE_BUTTON_BRIGHT_DOWN 8 +#define WIZ_SMART_BUTTON_ON 100 +#define WIZ_SMART_BUTTON_OFF 101 +#define WIZ_SMART_BUTTON_BRIGHT_UP 102 +#define WIZ_SMART_BUTTON_BRIGHT_DOWN 103 + #ifdef WLED_DISABLE_ESPNOW void handleRemote(){} #else +#pragma message "ESP-NOW remote driver enabled" #if !defined(ARDUINO_ARCH_ESP32) && !defined(ESP_OK) #define ESP_OK 0 // add missing constant for stupid esp8266 @@ -148,7 +154,6 @@ void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { return; } - USER_PRINT(F("\nIncoming ESP Now Packet[")); USER_PRINT(cur_seq); USER_PRINT(F("] from sender[")); @@ -165,8 +170,11 @@ void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { case WIZMOTE_BUTTON_NIGHT : activateNightMode(); stateUpdated(CALL_MODE_BUTTON); break; case WIZMOTE_BUTTON_BRIGHT_UP : brightnessUp(); stateUpdated(CALL_MODE_BUTTON); break; case WIZMOTE_BUTTON_BRIGHT_DOWN : brightnessDown(); stateUpdated(CALL_MODE_BUTTON); break; + case WIZ_SMART_BUTTON_ON : setOn(); break; + case WIZ_SMART_BUTTON_OFF : setOff(); break; + case WIZ_SMART_BUTTON_BRIGHT_UP : brightnessUp(); break; + case WIZ_SMART_BUTTON_BRIGHT_DOWN : brightnessDown(); break; default: break; - } last_seq = cur_seq; diff --git a/wled00/set.cpp b/wled00/set.cpp index 3b7c8a9c21..31fbb78417 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -922,6 +922,9 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply) applyPreset(presetCycCurr); } + pos = req.indexOf(F("NP")); //advances to next preset in a playlist + if (pos > 0) doAdvancePlaylist = true; + //set brightness updateVal(req.c_str(), "&A=", &bri); diff --git a/wled00/util.cpp b/wled00/util.cpp index ceefcc2f2b..896b57e309 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -155,6 +155,7 @@ bool oappend(const char* txt) USER_PRINTF("%2u bytes \t\"", len /*1 + olen + len - SETTINGS_STACK_BUF_SIZE*/); USER_PRINT(txt); USER_PRINTLN(F("\"")); + errorFlag = ERR_LOW_AJAX_MEM; } return false; // buffer full } @@ -433,6 +434,7 @@ um_data_t* simulateSound(uint8_t simulationId) static float volumeSmth; static uint16_t volumeRaw; static float my_magnitude; + static uint16_t zeroCrossingCount = 0; // number of zero crossings in the current batch of 512 samples //arrays uint8_t *fftResult; @@ -447,7 +449,7 @@ um_data_t* simulateSound(uint8_t simulationId) // NOTE!!! // This may change as AudioReactive usermod may change um_data = new um_data_t; - um_data->u_size = 11; + um_data->u_size = 12; um_data->u_type = new um_types_t[um_data->u_size]; um_data->u_data = new void*[um_data->u_size]; um_data->u_data[0] = &volumeSmth; @@ -461,6 +463,7 @@ um_data_t* simulateSound(uint8_t simulationId) um_data->u_data[8] = &FFT_MajorPeak; // dummy (FFT Peak smoothed) um_data->u_data[9] = &volumeSmth; // dummy (soundPressure) um_data->u_data[10] = &volumeSmth; // dummy (agcSensitivity) + um_data->u_data[11] = &zeroCrossingCount; } else { // get arrays from um_data fftResult = (uint8_t*)um_data->u_data[2]; @@ -527,6 +530,7 @@ um_data_t* simulateSound(uint8_t simulationId) volumeRaw = volumeSmth; my_magnitude = 10000.0f / 8.0f; //no idea if 10000 is a good value for FFT_Magnitude ??? if (volumeSmth < 1 ) my_magnitude = 0.001f; // noise gate closed - mute + zeroCrossingCount = floorf(FFT_MajorPeak / 36.0f); // 9Khz max frequency => 255 zero crossings return um_data; } diff --git a/wled00/wled.cpp b/wled00/wled.cpp index e8e9eeccf6..71050d997e 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -611,10 +611,12 @@ void WLED::setup() DEBUG_PRINTLN(F("PSRAM not used.")); #endif #endif -#if defined(ARDUINO_ESP32_PICO) -// special handling for PICO-D4: gpio16+17 are in use for onboard SPI FLASH (not PSRAM) -managed_pin_type pins[] = { {16, true}, {17, true} }; -pinManager.allocateMultiplePins(pins, sizeof(pins)/sizeof(managed_pin_type), PinOwner::SPI_RAM); +#if defined(ARDUINO_ARCH_ESP32) + if (strncmp("ESP32-PICO", ESP.getChipModel(), 10) == 0) { // WLEDMM detect pico board at runtime + // special handling for PICO-D4: gpio16+17 are in use for onboard SPI FLASH (not PSRAM) + managed_pin_type pins[] = { {16, true}, {17, true} }; + pinManager.allocateMultiplePins(pins, sizeof(pins)/sizeof(managed_pin_type), PinOwner::SPI_RAM); + } #endif //DEBUG_PRINT(F("LEDs inited. heap usage ~")); @@ -681,7 +683,7 @@ pinManager.allocateMultiplePins(pins, sizeof(pins)/sizeof(managed_pin_type), Pin updateFSInfo(); USER_PRINT(F("done Mounting FS; ")); - USER_PRINT(((fsBytesTotal-fsBytesUsed)/1024)); USER_PRINTLN(F(" kB free.")); + USER_PRINT(((fsBytesTotal-fsBytesUsed)/1024)); USER_PRINTLN(F(" kB free.\n")); // generate module IDs must be done before AP setup escapedMac = WiFi.macAddress(); @@ -705,7 +707,7 @@ pinManager.allocateMultiplePins(pins, sizeof(pins)/sizeof(managed_pin_type), Pin beginStrip(); DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap()); - USER_PRINTLN(F("Usermods setup ...")); + USER_PRINTLN(F("\nUsermods setup ...")); userSetup(); usermods.setup(); DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap()); @@ -721,7 +723,7 @@ pinManager.allocateMultiplePins(pins, sizeof(pins)/sizeof(managed_pin_type), Pin //Serial RX (Adalight, Improv, Serial JSON) only possible if GPIO3 unused //Serial TX (Debug, Improv, Serial JSON) only possible if GPIO1 unused if (!pinManager.isPinAllocated(hardwareRX) && !pinManager.isPinAllocated(hardwareTX)) { - if (Serial) Serial.println(F("Ada")); + if (Serial) Serial.println(F("\nAda")); } #endif @@ -852,7 +854,7 @@ pinManager.allocateMultiplePins(pins, sizeof(pins)/sizeof(managed_pin_type), Pin // repeat Ada prompt #ifdef WLED_ENABLE_ADALIGHT if (!pinManager.isPinAllocated(hardwareRX) && !pinManager.isPinAllocated(hardwareTX)) { - if (Serial) Serial.println(F("Ada")); + if (Serial) Serial.println(F("\nAda")); } #endif @@ -899,7 +901,7 @@ void WLED::initAP(bool resetAP) USER_PRINT(F("Opening access point ")); // WLEDMM USER_PRINTLN(apSSID); // WLEDMM WiFi.softAPConfig(IPAddress(4, 3, 2, 1), IPAddress(4, 3, 2, 1), IPAddress(255, 255, 255, 0)); - WiFi.softAP(apSSID, apPass, apChannel, apHide); + WiFi.softAP(apSSID, apPass, apChannel, apHide, 8); // WLED-MM allow up to 8 clients for ad-hoc "in the field" syncing. #if defined(LOLIN_WIFI_FIX) && (defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32S3)) WiFi.setTxPower(WIFI_POWER_8_5dBm); #endif @@ -1031,6 +1033,12 @@ bool WLED::initEthernet() void WLED::initConnection() { +#ifdef ARDUINO_ARCH_ESP32 + unsigned long t_wait = millis(); + while(strip.isUpdating() && (millis() - t_wait < 86)) delay(1); // WLEDMM try to catch a moment when strip is idle + //if (strip.isUpdating()) USER_PRINTLN("WLED::initConnection: strip still updating."); +#endif + #ifdef WLED_ENABLE_WEBSOCKETS ws.onEvent(wsEvent); #endif @@ -1208,7 +1216,7 @@ void WLED::handleConnection() static unsigned retryCount = 0; // WLEDMM #ifdef ARDUINO_ARCH_ESP32 // reconnect WiFi to clear stale allocations if heap gets too low - if (now - heapTime > 5000) { // WLEDMM: updated with better logic for small heap available by block, not total. + if ((!strip.isUpdating()) && (now - heapTime > 5000)) { // WLEDMM: updated with better logic for small heap available by block, not total. // WLEDMM trying to use a moment when the strip is idle #if defined(ARDUINO_ARCH_ESP32S2) uint32_t heap = ESP.getFreeHeap(); // WLEDMM works better on -S2 #else diff --git a/wled00/wled.h b/wled00/wled.h index 233213848d..96530ac286 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2404161 +#define VERSION 2405241 // WLEDMM - you can check for this define in usermods, to only enabled WLEDMM specific code in the "right" fork. Its not defined in AC WLED. #define _MoonModules_WLED_ @@ -184,6 +184,12 @@ // There is a code that will still not use PSRAM though: // AsyncJsonResponse is a derived class that implements DynamicJsonDocument (AsyncJson-v6.h) #if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && (defined(WLED_USE_PSRAM) || defined(WLED_USE_PSRAM_JSON)) // WLEDMM +// WLEDMM the JSON_TO_PSRAM feature works, so use it by default +#undef WLED_USE_PSRAM_JSON +#define WLED_USE_PSRAM_JSON +#undef ALL_JSON_TO_PSRAM +#define ALL_JSON_TO_PSRAM + struct PSRAM_Allocator { void* allocate(size_t size) { if (psramFound()) return ps_malloc(size); // use PSRAM if it exists @@ -265,16 +271,17 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument; // int arr[]{0,1,2} becomes WLED_GLOBAL int arr[] _INIT_N(({0,1,2})); #ifndef WLED_DEFINE_GLOBAL_VARS -# define WLED_GLOBAL extern -# define _INIT(x) -# define _INIT_N(x) + #define WLED_GLOBAL extern + #define _INIT(x) + #define _INIT_N(x) + #define _INIT_PROGMEM(x) #else -# define WLED_GLOBAL -# define _INIT(x) = x - -//needed to ignore commas in array definitions -#define UNPACK( ... ) __VA_ARGS__ -# define _INIT_N(x) UNPACK x + #define WLED_GLOBAL + #define _INIT(x) = x + //needed to ignore commas in array definitions + #define UNPACK( ... ) __VA_ARGS__ + #define _INIT_N(x) UNPACK x + #define _INIT_PROGMEM(x) PROGMEM = x #endif #define STRINGIFY(X) #X @@ -284,9 +291,13 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument; #define WLED_VERSION "dev" #endif +#ifndef WLED_RELEASE_NAME + #define WLED_RELEASE_NAME mdev_release +#endif + // Global Variable definitions WLED_GLOBAL char versionString[] _INIT(TOSTRING(WLED_VERSION)); -WLED_GLOBAL char releaseString[] _INIT(TOSTRING(WLED_RELEASE_NAME)); //WLEDMM: to show on update page +WLED_GLOBAL char releaseString[] _INIT_PROGMEM(TOSTRING(WLED_RELEASE_NAME)); //WLEDMM: to show on update page // somehow this will not work if using "const char releaseString[] #define WLED_CODENAME "Hoshi" // AP and OTA default passwords (for maximum security change them!) @@ -533,7 +544,8 @@ WLED_GLOBAL byte macroDoublePress[WLED_MAX_BUTTONS] _INIT({0}); WLED_GLOBAL bool otaLock _INIT(false); // prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks WLED_GLOBAL bool wifiLock _INIT(false); // prevents access to WiFi settings when OTA lock is enabled #ifdef ARDUINO_ARCH_ESP32 -WLED_GLOBAL bool aOtaEnabled _INIT(true); // ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on +// WLEDMM disabled as default - arduinoOTA is a relic, only useful when using ArduinoIDE +WLED_GLOBAL bool aOtaEnabled _INIT(false); // ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on #else WLED_GLOBAL bool aOtaEnabled _INIT(false); // WLEDMM: start with OTA disabled, as it seems to be unstable on 8266 #endif @@ -646,6 +658,7 @@ WLED_GLOBAL byte timerWeekday[] _INIT_N(({ 255, 255, 255, 255, 255, 255, 255, WLED_GLOBAL byte timerMonth[] _INIT_N(({28,28,28,28,28,28,28,28})); WLED_GLOBAL byte timerDay[] _INIT_N(({1,1,1,1,1,1,1,1})); WLED_GLOBAL byte timerDayEnd[] _INIT_N(({31,31,31,31,31,31,31,31})); +WLED_GLOBAL bool doAdvancePlaylist _INIT(false); //improv WLED_GLOBAL byte improvActive _INIT(0); //0: no improv packet received, 1: improv active, 2: provisioning @@ -802,7 +815,7 @@ WLED_GLOBAL int8_t spi_sclk _INIT(HW_PIN_CLOCKSPI); WLED_GLOBAL PSRAMDynamicJsonDocument doc; #else WLED_GLOBAL PSRAMDynamicJsonDocument doc(JSON_BUFFER_SIZE); - #warning experimental - trying to always use dynamic JSON + //#warning trying to always use dynamic JSON in PSRAM #endif #else WLED_GLOBAL StaticJsonDocument doc; diff --git a/wled00/wled_serial.cpp b/wled00/wled_serial.cpp index 596b579114..9361891b5a 100644 --- a/wled00/wled_serial.cpp +++ b/wled00/wled_serial.cpp @@ -42,6 +42,7 @@ void updateBaudRate(uint32_t rate){ // RGB LED data return as JSON array. Slow, but easy to use on the other end. void sendJSON(){ if (!pinManager.isPinAllocated(hardwareTX) || pinManager.getPinOwner(hardwareTX) == PinOwner::DebugOut) { + if (!Serial) return; // WLEDMM avoid writing to unconnected USB-CDC uint16_t used = strip.getLengthTotal(); Serial.write('['); for (uint16_t i=0; i SERIAL_MAXTIME_MILLIS) { USER_PRINTLN(F("handleSerial(): need a break after >100ms of activity.")); } //#endif + #else + #pragma message "Serial protocols (AdaLight, Serial JSON, Serial LED driver, improv) disabled" #endif // If Continuous Serial Streaming is enabled, send new LED data as bytes diff --git a/wled00/wled_server.cpp b/wled00/wled_server.cpp index a08e9ea6e3..f3fe1c0875 100644 --- a/wled00/wled_server.cpp +++ b/wled00/wled_server.cpp @@ -58,6 +58,7 @@ void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t request->_tempFile.close(); USER_PRINT(F("File uploaded: ")); // WLEDMM USER_PRINTLN(filename); // WLEDMM + invalidateFileNameCache(); // WLEDMM if (filename.equalsIgnoreCase("/cfg.json") || filename.equalsIgnoreCase("cfg.json")) { // WLEDMM request->send(200, "text/plain", F("Configuration restore successful.\nRebooting...")); doReboot = true; diff --git a/wled00/ws.cpp b/wled00/ws.cpp index 351caad424..32420fcc88 100644 --- a/wled00/ws.cpp +++ b/wled00/ws.cpp @@ -186,6 +186,7 @@ void sendDataWs(AsyncWebSocketClient * client) // WLEDMM function to recover full-bright pixel (based on code from upstream alt-buffer, which is based on code from NeoPixelBrightnessBus) static uint32_t restoreColorLossy(uint32_t c, uint_fast8_t _restaurationBri) { if (_restaurationBri == 255) return c; + if (_restaurationBri == 0) return 0; uint8_t* chan = (uint8_t*) &c; for (uint_fast8_t i=0; i<4; i++) { uint_fast16_t val = chan[i]; @@ -313,4 +314,5 @@ void handleWs() #else void handleWs() {} void sendDataWs(AsyncWebSocketClient * client) {} +#pragma message "WebSockets disabled - no live preview." #endif diff --git a/wled00/xml.cpp b/wled00/xml.cpp index ee647217c1..72fdf219fd 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -196,7 +196,11 @@ void appendGPIOinfo() { size_t roLen = strlen(ro_gpio); char pinString[10]; for(int pinNr = 0; pinNr < WLED_NUM_PINS; pinNr++) { // 49 = highest PIN on ESP32-S3 - if(!pinManager.isPinOk(pinNr, false)) { + #if defined(ARDUINO_ARCH_ESP32) && !defined(BOARD_HAS_PSRAM) + if ((!pinManager.isPinOk(pinNr, false)) || (pinManager.getPinOwner(pinNr) == PinOwner::SPI_RAM)) { // WLEDMM add SPIRAM pins as "reserved" (pico boards) + #else + if (!pinManager.isPinOk(pinNr, false)) { + #endif sprintf(pinString, "%s%d", strlen(rsvd)==rsLen?"":",", pinNr); strcat(rsvd, pinString); } @@ -265,9 +269,9 @@ void appendGPIOinfo() { // add info about max. # of pins oappend(SET_F("d.max_gpio=")); - #if defined(ESP32) + #if defined(ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S3) oappendi(NUM_DIGITAL_PINS - 1); - #else //8266 + #else //8266 (max=17), or esp32-S3 (max=48) oappendi(NUM_DIGITAL_PINS); //WLEDMM include pin 17 for Analog #endif oappend(SET_F(";")); @@ -842,7 +846,7 @@ void getSettingsJS(AsyncWebServerRequest* request, byte subPage, char* dest) //W olen -= 2; //delete "; oappend(versionString); oappend(SET_F(" ")); - oappend(releaseString); + oappend((char*)FPSTR(releaseString)); oappend(SET_F(".bin
(")); #if defined(CONFIG_IDF_TARGET_ESP32C3) oappend(SET_F("ESP32-C3"));