Merge remote-tracking branch 'upstream/main' into pxmagic

This commit is contained in:
Alerson Jorge 2023-09-28 09:10:47 -03:00
commit f2d00e6e42
50 changed files with 6256 additions and 5446 deletions

View File

@ -1,11 +1,46 @@
## WLED changelog ## WLED changelog
#### Build 2309240
- Release of WLED beta version 0.14.0-b6 "Hoshi"
- Effect bugfixes and improvements (Meteor, Meteor Smooth, Scrolling Text)
- audioreactive: bugfixes for ES8388 and ES7243 init; minor improvements for analog inputs
#### Build 2309100
- Release of WLED beta version 0.14.0-b5 "Hoshi"
- New standard esp32 build with audioreactive
- Effect blending bugfixes, and minor optimizations
#### Build 2309050
- Effect blending (#3311) (finally effect transitions!)
*WARNING*: May not work well with ESP8266, with plenty of segments or usermods (low RAM condition)!!!
- Added receive and send sync groups to JSON API (#3317) (you can change sync groups using preset)
- Internal temperature usermod (#3246)
- MQTT server and topic length overrides (#3354) (new build flags)
- Animated Staircase usermod enhancement (#3348) (on/off toggle/relay control)
- Added local time info to Info page (#3351)
- New effect: Rolling Balls (a.k.a. linear bounce) (#1039)
- Various bug fixes and enhancements.
#### Build 2308110
- Release of WLED beta version 0.14.0-b4 "Hoshi"
- Reset effect data immediately upon mode change
#### Build 2308030
- Improved random palette handling and blending
- Soap bugfix
- Fix ESP-NOW crash with AP mode Always
#### Build 2307180
- Bus-level global buffering (#3280)
- Removed per-segment LED buffer (SEGMENT.leds)
- various fixes and improvements (ESP variants platform 5.3.0, effect optimizations, /json/cfg pin allocation)
#### Build 2307130 #### Build 2307130
- larger `oappend()` stack buffer (3.5k) for ESP32 - larger `oappend()` stack buffer (3.5k) for ESP32
- Preset cycle bugfix (#3262) - Preset cycle bugfix (#3262)
- Rotary encoder ALT fix for large LED count (#3276) - Rotary encoder ALT fix for large LED count (#3276)
- effect updates (2D Plasmaball), `blur()` speedup - effect updates (2D Plasmaball), `blur()` speedup
- On/Off toggle from nodes view (may show unknow device type on older versions) (#3291) - On/Off toggle from nodes view (may show unknown device type on older versions) (#3291)
- various fixes and improvements (ABL, crashes when changing presets with different segments) - various fixes and improvements (ABL, crashes when changing presets with different segments)
#### Build 2306270 #### Build 2306270
@ -18,7 +53,7 @@
#### Build 2306210 #### Build 2306210
- 0.14.0-b3 release - 0.14.0-b3 release
- respect global I2C in all usermods (no local initilaisation of I2C bus) - respect global I2C in all usermods (no local initialization of I2C bus)
- Multi relay usermod compile-time enabled option (-D MULTI_RELAY_ENABLED=true|false) - Multi relay usermod compile-time enabled option (-D MULTI_RELAY_ENABLED=true|false)
#### Build 2306180 #### Build 2306180
@ -44,7 +79,7 @@
#### Build 2306020 #### Build 2306020
- Support for segment sets (PR #3171) - Support for segment sets (PR #3171)
- Reduce sound simulation modes to 2 to facilitiate segment sets - Reduce sound simulation modes to 2 to facilitate segment sets
- Trigger button immediately on press if all configured presets are the same (PR #3226) - Trigger button immediately on press if all configured presets are the same (PR #3226)
- Changes for allowing Alexa to change light color to White when auto-calculating from RGB (PR #3211) - Changes for allowing Alexa to change light color to White when auto-calculating from RGB (PR #3211)

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "wled", "name": "wled",
"version": "0.14.0-b3", "version": "0.14.0-b6",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "wled", "name": "wled",
"version": "0.14.0-b3", "version": "0.14.0-b6",
"description": "Tools for WLED project", "description": "Tools for WLED project",
"main": "tools/cdata.js", "main": "tools/cdata.js",
"directories": { "directories": {

View File

@ -11,7 +11,7 @@
# CI binaries # CI binaries
; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth # ESP32 variant builds are temporarily excluded from CI due to toolchain issues on the GitHub Actions Linux environment ; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth # ESP32 variant builds are temporarily excluded from CI due to toolchain issues on the GitHub Actions Linux environment
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, esp32dev_audioreactive, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB, esp32s3dev_8MB_PSRAM_opi
# Release binaries # Release binaries
; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB ; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB
@ -87,9 +87,11 @@ platform_packages = platformio/framework-arduinoespressif8266
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# FLAGS: DEBUG # FLAGS: DEBUG
# # esp8266 : see https://docs.platformio.org/en/latest/platforms/espressif8266.html#debug-level
# esp32 : see https://docs.platformio.org/en/latest/platforms/espressif32.html#debug-level
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
debug_flags = -D DEBUG=1 -D WLED_DEBUG -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM debug_flags = -D DEBUG=1 -D WLED_DEBUG
-DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM ;; for esp8266
# if needed (for memleaks etc) also add; -DDEBUG_ESP_OOM -include "umm_malloc/umm_malloc_cfg.h" # if needed (for memleaks etc) also add; -DDEBUG_ESP_OOM -include "umm_malloc/umm_malloc_cfg.h"
# -DDEBUG_ESP_CORE is not working right now # -DDEBUG_ESP_CORE is not working right now
@ -175,7 +177,7 @@ upload_speed = 115200
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
lib_compat_mode = strict lib_compat_mode = strict
lib_deps = lib_deps =
fastled/FastLED @ 3.5.0 fastled/FastLED @ 3.6.0
IRremoteESP8266 @ 2.8.2 IRremoteESP8266 @ 2.8.2
makuna/NeoPixelBus @ 2.7.5 makuna/NeoPixelBus @ 2.7.5
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.7 https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.7
@ -201,7 +203,7 @@ build_flags =
-DESP8266 -DESP8266
-DFP_IN_IROM -DFP_IN_IROM
;-Wno-deprecated-declarations ;-Wno-deprecated-declarations
-Wno-register ;; leaves some warnings when compiling C files: command-line option '-Wno-register' is valid for C++/ObjC++ but not for C ;-Wno-register ;; leaves some warnings when compiling C files: command-line option '-Wno-register' is valid for C++/ObjC++ but not for C
;-Dregister= # remove warnings in C++17 due to use of deprecated register keyword by the FastLED library ;; warning: this can be dangerous ;-Dregister= # remove warnings in C++17 due to use of deprecated register keyword by the FastLED library ;; warning: this can be dangerous
-Wno-misleading-indentation -Wno-misleading-indentation
; NONOSDK22x_190703 = 2.2.2-dev(38a443e) ; NONOSDK22x_190703 = 2.2.2-dev(38a443e)
@ -214,6 +216,9 @@ build_flags =
-DVTABLES_IN_FLASH -DVTABLES_IN_FLASH
; restrict to minimal mime-types ; restrict to minimal mime-types
-DMIMETYPE_MINIMAL -DMIMETYPE_MINIMAL
; other special-purpose framework flags (see https://docs.platformio.org/en/latest/platforms/espressif8266.html)
; -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48 ;; in case of linker errors like "section `.text1' will not fit in region `iram1_0_seg'"
; -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED ;; (experimental) adds some extra heap, but may cause slowdown
lib_deps = lib_deps =
#https://github.com/lorol/LITTLEFS.git #https://github.com/lorol/LITTLEFS.git
@ -224,9 +229,7 @@ lib_deps =
[esp32] [esp32]
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip #platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip
platform = espressif32@3.5.0 platform = espressif32@3.5.0
platform_packages = framework-arduinoespressif32 @ https://github.com/Aircoookie/arduino-esp32.git#1.0.6.4 platform_packages = framework-arduinoespressif32 @ https://github.com/Aircoookie/arduino-esp32.git#1.0.6.4
build_flags = -g build_flags = -g
-DARDUINO_ARCH_ESP32 -DARDUINO_ARCH_ESP32
#-DCONFIG_LITTLEFS_FOR_IDF_3_2 #-DCONFIG_LITTLEFS_FOR_IDF_3_2
@ -234,14 +237,14 @@ build_flags = -g
#use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x #use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x
-D LOROL_LITTLEFS -D LOROL_LITTLEFS
; -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3 ; -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 default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
lib_deps = lib_deps =
https://github.com/lorol/LITTLEFS.git https://github.com/lorol/LITTLEFS.git
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps} ${env.lib_deps}
# additional build flags for audioreactive
AR_build_flags = -D USERMOD_AUDIOREACTIVE -D UM_AUDIOREACTIVE_USE_NEW_FFT
AR_lib_deps = https://github.com/kosme/arduinoFFT#develop @ ^1.9.2
[esp32_idf_V4] [esp32_idf_V4]
;; experimental build environment for ESP32 using ESP-IDF 4.4.x / arduino-esp32 v2.0.5 ;; experimental build environment for ESP32 using ESP-IDF 4.4.x / arduino-esp32 v2.0.5
@ -249,9 +252,8 @@ lib_deps =
;; ;;
;; please note that you can NOT update existing ESP32 installs with a "V4" build. Also updating by OTA will not work properly. ;; 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. ;; You need to completely erase your device (esptool erase_flash) first, then install the "V4" build from VSCode+platformio.
platform = espressif32@5.2.0 platform = espressif32@5.3.0
platform_packages = platform_packages =
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
build_flags = -g build_flags = -g
-Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one -Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one
-DARDUINO_ARCH_ESP32 -DESP32 -DARDUINO_ARCH_ESP32 -DESP32
@ -265,9 +267,8 @@ lib_deps =
[esp32s2] [esp32s2]
;; generic definitions for all ESP32-S2 boards ;; generic definitions for all ESP32-S2 boards
platform = espressif32@5.2.0 platform = espressif32@5.3.0
platform_packages = platform_packages =
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
build_flags = -g build_flags = -g
-DARDUINO_ARCH_ESP32 -DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32S2 -DARDUINO_ARCH_ESP32S2
@ -285,9 +286,8 @@ lib_deps =
[esp32c3] [esp32c3]
;; generic definitions for all ESP32-C3 boards ;; generic definitions for all ESP32-C3 boards
platform = espressif32@5.2.0 platform = espressif32@5.3.0
platform_packages = platform_packages =
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
build_flags = -g build_flags = -g
-DARDUINO_ARCH_ESP32 -DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32C3 -DARDUINO_ARCH_ESP32C3
@ -304,9 +304,8 @@ lib_deps =
[esp32s3] [esp32s3]
;; generic definitions for all ESP32-S3 boards ;; generic definitions for all ESP32-S3 boards
platform = espressif32@5.2.0 platform = espressif32@5.3.0
platform_packages = platform_packages =
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
build_flags = -g build_flags = -g
-DESP32 -DESP32
-DARDUINO_ARCH_ESP32 -DARDUINO_ARCH_ESP32
@ -403,6 +402,20 @@ lib_deps = ${esp32.lib_deps}
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions} board_build.partitions = ${esp32.default_partitions}
[env:esp32dev_audioreactive]
board = esp32dev
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_audioreactive #-D WLED_DISABLE_BROWNOUT_DET
${esp32.AR_build_flags}
lib_deps = ${esp32.lib_deps}
${esp32.AR_lib_deps}
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions}
; board_build.f_flash = 80000000L
; board_build.flash_mode = dio
[env:esp32dev_qio80] [env:esp32dev_qio80]
board = esp32dev board = esp32dev
platform = ${esp32.platform} platform = ${esp32.platform}
@ -437,6 +450,7 @@ platform_packages = ${esp32.platform_packages}
upload_speed = 921600 upload_speed = 921600
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1 build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1
-D WLED_DISABLE_ESPNOW ;; ESP-NOW requires wifi, may crash with ethernet only
lib_deps = ${esp32.lib_deps} lib_deps = ${esp32.lib_deps}
board_build.partitions = ${esp32.default_partitions} board_build.partitions = ${esp32.default_partitions}
@ -450,6 +464,7 @@ board_build.flash_mode = qio
upload_speed = 460800 upload_speed = 460800
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=S2_saola build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=S2_saola
;-DLOLIN_WIFI_FIX ;; try this in case Wifi does not work
-DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_CDC_ON_BOOT=1
lib_deps = ${esp32s2.lib_deps} lib_deps = ${esp32s2.lib_deps}
@ -460,8 +475,9 @@ platform_packages = ${esp32c3.platform_packages}
framework = arduino framework = arduino
board = esp32-c3-devkitm-1 board = esp32-c3-devkitm-1
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3 build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=ESP32-C3
-D WLED_WATCHDOG_TIMEOUT=0 -D WLED_WATCHDOG_TIMEOUT=0
-DLOLIN_WIFI_FIX ; seems to work much better with this
-DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB
;-DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip ;-DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip
upload_speed = 460800 upload_speed = 460800
@ -475,10 +491,10 @@ platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages} platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600 ; or 460800 upload_speed = 921600 ; or 460800
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_8MB
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip -D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
;-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=0 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") ;-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 ;-D WLED_DEBUG
lib_deps = ${esp32s3.lib_deps} lib_deps = ${esp32s3.lib_deps}
board_build.partitions = tools/WLED_ESP32_8MB.csv board_build.partitions = tools/WLED_ESP32_8MB.csv
@ -487,11 +503,10 @@ board_build.flash_mode = qio
; board_build.flash_mode = dio ;; try this if you have problems at startup ; board_build.flash_mode = dio ;; try this if you have problems at startup
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
[env:esp32s3dev_8MB_PSRAM] [env:esp32s3dev_8MB_PSRAM_opi]
;; ESP32-TinyS3 development board, with 8MB FLASH and 8MB PSRAM (memory_type: qio_opi, qio_qspi, or opi_opi) ;; ESP32-S3 development board, with 8MB FLASH and >= 8MB PSRAM (memory_type: qio_opi)
;board = um_tinys3 ; -> needs workaround from https://github.com/Aircoookie/WLED/pull/2905#issuecomment-1328049860 board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support
;board = esp32s3box ; -> error: 'esp32_adc2gpio' was not declared in this scope board_build.arduino.memory_type = qio_opi ;; use with PSRAM: 8MB or 16MB
board = esp32-s3-devkitc-1 ; -> compiles, but does not support PSRAM
platform = ${esp32s3.platform} platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages} platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600 upload_speed = 921600
@ -499,7 +514,7 @@ build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} build_flags = ${common.build_flags} ${esp32s3.build_flags}
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip ;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=0 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
; -D WLED_RELEASE_NAME=ESP32-S3_PSRAM ; -D WLED_RELEASE_NAME=ESP32-S3_PSRAM
-D WLED_USE_PSRAM -DBOARD_HAS_PSRAM ; tells WLED that PSRAM shall be used -D WLED_USE_PSRAM -DBOARD_HAS_PSRAM ; tells WLED that PSRAM shall be used
lib_deps = ${esp32s3.lib_deps} lib_deps = ${esp32s3.lib_deps}
@ -508,6 +523,13 @@ board_build.f_flash = 80000000L
board_build.flash_mode = qio board_build.flash_mode = qio
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
[env:esp32s3dev_8MB_PSRAM_qspi]
;; ESP32-TinyS3 development board, with 8MB FLASH and PSRAM (memory_type: qio_qspi)
extends = env:esp32s3dev_8MB_PSRAM_opi
;board = um_tinys3 ; -> needs workaround from https://github.com/Aircoookie/WLED/pull/2905#issuecomment-1328049860
board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support
board_build.arduino.memory_type = qio_qspi ;; use with PSRAM: 2MB or 4MB
[env:esp8285_4CH_MagicHome] [env:esp8285_4CH_MagicHome]
board = esp8285 board = esp8285
platform = ${common.platform_wled_default} platform = ${common.platform_wled_default}
@ -576,7 +598,7 @@ platform_packages = ${esp32s2.platform_packages}
board = lolin_s2_mini board = lolin_s2_mini
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
build_unflags = ${common.build_unflags} #-DARDUINO_USB_CDC_ON_BOOT=1 build_unflags = ${common.build_unflags} #-DARDUINO_USB_CDC_ON_BOOT=1
build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=LolinS2 build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=ESP32-S2
-DBOARD_HAS_PSRAM -DBOARD_HAS_PSRAM
-DARDUINO_USB_CDC_ON_BOOT=1 # try disabling and enabling unflag above in case of board-specific issues, will disable Serial -DARDUINO_USB_CDC_ON_BOOT=1 # try disabling and enabling unflag above in case of board-specific issues, will disable Serial
-DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0

View File

@ -12,7 +12,7 @@ anyio==3.6.2
# via starlette # via starlette
bottle==0.12.25 bottle==0.12.25
# via platformio # via platformio
certifi==2022.12.7 certifi==2023.7.22
# via requests # via requests
charset-normalizer==3.1.0 charset-normalizer==3.1.0
# via requests # via requests

View File

@ -0,0 +1,8 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x300000,
app1, app, ota_1, 0x310000,0x300000,
spiffs, data, spiffs, 0x610000,0x9E0000,
coredump, data, coredump,,64K
# to create/use ffat, see https://github.com/marcmerlin/esp32_fatfsimage
1 # Name, Type, SubType, Offset, Size, Flags
2 nvs, data, nvs, 0x9000, 0x5000,
3 otadata, data, ota, 0xe000, 0x2000,
4 app0, app, ota_0, 0x10000, 0x300000,
5 app1, app, ota_1, 0x310000,0x300000,
6 spiffs, data, spiffs, 0x610000,0x9E0000,
7 coredump, data, coredump,,64K
8 # to create/use ffat, see https://github.com/marcmerlin/esp32_fatfsimage

View File

@ -3,4 +3,5 @@ nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000, otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x200000, app0, app, ota_0, 0x10000, 0x200000,
app1, app, ota_1, 0x210000,0x200000, app1, app, ota_1, 0x210000,0x200000,
spiffs, data, spiffs, 0x410000,0x3F0000, spiffs, data, spiffs, 0x410000,0x3E0000,
coredump, data, coredump,,64K

1 # Name # Name, Type, SubType, Offset, Size, Flags Type SubType Offset Size Flags
3 otadata otadata, data, ota, 0xe000, 0x2000, data ota 0xe000 0x2000
4 app0 app0, app, ota_0, 0x10000, 0x200000, app ota_0 0x10000 0x200000
5 app1 app1, app, ota_1, 0x210000,0x200000, app ota_1 0x210000 0x200000
6 spiffs spiffs, data, spiffs, 0x410000,0x3E0000, data spiffs 0x410000 0x3F0000
7 coredump, data, coredump,,64K

View File

@ -25,6 +25,7 @@ class Animated_Staircase : public Usermod {
bool useUSSensorBottom = false; // using PIR or UltraSound sensor? bool useUSSensorBottom = false; // using PIR or UltraSound sensor?
unsigned int topMaxDist = 50; // default maximum measured distance in cm, top unsigned int topMaxDist = 50; // default maximum measured distance in cm, top
unsigned int bottomMaxDist = 50; // default maximum measured distance in cm, bottom unsigned int bottomMaxDist = 50; // default maximum measured distance in cm, bottom
bool togglePower = false; // toggle power on/off with staircase on/off
/* runtime variables */ /* runtime variables */
bool initDone = false; bool initDone = false;
@ -90,6 +91,7 @@ class Animated_Staircase : public Usermod {
static const char _bottomEcho_pin[]; static const char _bottomEcho_pin[];
static const char _topEchoCm[]; static const char _topEchoCm[];
static const char _bottomEchoCm[]; static const char _bottomEchoCm[];
static const char _togglePower[];
void publishMqtt(bool bottom, const char* state) { void publishMqtt(bool bottom, const char* state) {
#ifndef WLED_DISABLE_MQTT #ifndef WLED_DISABLE_MQTT
@ -196,6 +198,7 @@ class Animated_Staircase : public Usermod {
if (on) { if (on) {
lastSensor = topSensorRead; lastSensor = topSensorRead;
} else { } else {
if (togglePower && onIndex == offIndex && offMode) toggleOnOff(); // toggle power on if off
// If the bottom sensor triggered, we need to swipe up, ON // If the bottom sensor triggered, we need to swipe up, ON
swipe = bottomSensorRead; swipe = bottomSensorRead;
@ -249,7 +252,10 @@ class Animated_Staircase : public Usermod {
offIndex = MAX(onIndex, offIndex - 1); offIndex = MAX(onIndex, offIndex - 1);
} }
} }
if (oldOn != onIndex || oldOff != offIndex) updateSegments(); // reduce the number of updates to necessary ones if (oldOn != onIndex || oldOff != offIndex) {
updateSegments(); // reduce the number of updates to necessary ones
if (togglePower && onIndex == offIndex && !offMode && !on) toggleOnOff(); // toggle power off for all segments off
}
} }
} }
@ -295,6 +301,7 @@ class Animated_Staircase : public Usermod {
strip.setTransition(segment_delay_ms/100); strip.setTransition(segment_delay_ms/100);
strip.trigger(); strip.trigger();
} else { } else {
if (togglePower && !on && offMode) toggleOnOff(); // toggle power on if off
// Restore segment options // Restore segment options
for (int i = 0; i <= strip.getLastActiveSegmentId(); i++) { for (int i = 0; i <= strip.getLastActiveSegmentId(); i++) {
Segment &seg = strip.getSegment(i); Segment &seg = strip.getSegment(i);
@ -444,6 +451,7 @@ class Animated_Staircase : public Usermod {
staircase[FPSTR(_bottomEcho_pin)] = useUSSensorBottom ? bottomEchoPin : -1; staircase[FPSTR(_bottomEcho_pin)] = useUSSensorBottom ? bottomEchoPin : -1;
staircase[FPSTR(_topEchoCm)] = topMaxDist; staircase[FPSTR(_topEchoCm)] = topMaxDist;
staircase[FPSTR(_bottomEchoCm)] = bottomMaxDist; staircase[FPSTR(_bottomEchoCm)] = bottomMaxDist;
staircase[FPSTR(_togglePower)] = togglePower;
DEBUG_PRINTLN(F("Staircase config saved.")); DEBUG_PRINTLN(F("Staircase config saved."));
} }
@ -488,6 +496,8 @@ class Animated_Staircase : public Usermod {
bottomMaxDist = top[FPSTR(_bottomEchoCm)] | bottomMaxDist; bottomMaxDist = top[FPSTR(_bottomEchoCm)] | bottomMaxDist;
bottomMaxDist = min(150,max(30,(int)bottomMaxDist)); // max distance ~1.5m (a lag of 9ms may be expected) bottomMaxDist = min(150,max(30,(int)bottomMaxDist)); // max distance ~1.5m (a lag of 9ms may be expected)
togglePower = top[FPSTR(_togglePower)] | togglePower; // staircase toggles power on/off
DEBUG_PRINT(FPSTR(_name)); DEBUG_PRINT(FPSTR(_name));
if (!initDone) { if (!initDone) {
// first run: reading from cfg.json // first run: reading from cfg.json
@ -511,7 +521,7 @@ class Animated_Staircase : public Usermod {
if (changed) setup(); if (changed) setup();
} }
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features // use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return true; return !top[FPSTR(_togglePower)].isNull();
} }
/* /*
@ -551,3 +561,4 @@ const char Animated_Staircase::_bottomPIRorTrigger_pin[] PROGMEM = "bottomPIR
const char Animated_Staircase::_bottomEcho_pin[] PROGMEM = "bottomEcho_pin"; const char Animated_Staircase::_bottomEcho_pin[] PROGMEM = "bottomEcho_pin";
const char Animated_Staircase::_topEchoCm[] PROGMEM = "top-dist-cm"; const char Animated_Staircase::_topEchoCm[] PROGMEM = "top-dist-cm";
const char Animated_Staircase::_bottomEchoCm[] PROGMEM = "bottom-dist-cm"; const char Animated_Staircase::_bottomEchoCm[] PROGMEM = "bottom-dist-cm";
const char Animated_Staircase::_togglePower[] PROGMEM = "toggle-on-off";

View File

@ -0,0 +1,17 @@
# Internal Temperature Usermod
This usermod adds the temperature readout to the Info tab and also publishes that over the topic `mcutemp` topic.
## Important
A shown temp of 53,33°C might indicate that the internal temp is not supported.
ESP8266 does not have a internal temp sensor
ESP32S2 seems to crash on reading the sensor -> disabled
## Installation
Add a build flag `-D USERMOD_INTERNAL_TEMPERATURE` to your `platformio.ini` (or `platformio_override.ini`).
## Authors
Soeren Willrodt [@lost-hope](https://github.com/lost-hope)
Dimitry Zhemkov [@dima-zhemkov](https://github.com/dima-zhemkov)

View File

@ -0,0 +1,117 @@
#pragma once
#include "wled.h"
class InternalTemperatureUsermod : public Usermod
{
private:
unsigned long loopInterval = 10000;
unsigned long lastTime = 0;
bool isEnabled = false;
float temperature = 0;
static const char _name[];
static const char _enabled[];
static const char _loopInterval[];
// any private methods should go here (non-inline methosd should be defined out of class)
void publishMqtt(const char *state, bool retain = false); // example for publishing MQTT message
public:
void setup()
{
}
void loop()
{
// if usermod is disabled or called during strip updating just exit
// NOTE: on very long strips strip.isUpdating() may always return true so update accordingly
if (!isEnabled || strip.isUpdating() || millis() - lastTime <= loopInterval)
return;
lastTime = millis();
#ifdef ESP8266 // ESP8266
// does not seem possible
temperature = -1;
#elif defined(CONFIG_IDF_TARGET_ESP32S2) // ESP32S2
temperature = -1;
#else // ESP32 ESP32S3 and ESP32C3
temperature = roundf(temperatureRead() * 10) / 10;
#endif
#ifndef WLED_DISABLE_MQTT
if (WLED_MQTT_CONNECTED)
{
char array[10];
snprintf(array, sizeof(array), "%f", temperature);
publishMqtt(array);
}
#endif
}
void addToJsonInfo(JsonObject &root)
{
if (!isEnabled)
return;
// if "u" object does not exist yet wee need to create it
JsonObject user = root["u"];
if (user.isNull())
user = root.createNestedObject("u");
JsonArray userTempArr = user.createNestedArray(FPSTR(_name));
userTempArr.add(temperature);
userTempArr.add(F(" °C"));
// if "sensor" object does not exist yet wee need to create it
JsonObject sensor = root[F("sensor")];
if (sensor.isNull())
sensor = root.createNestedObject(F("sensor"));
JsonArray sensorTempArr = sensor.createNestedArray(FPSTR(_name));
sensorTempArr.add(temperature);
sensorTempArr.add(F("°C"));
}
void addToConfig(JsonObject &root)
{
JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = isEnabled;
top[FPSTR(_loopInterval)] = loopInterval;
}
bool readFromConfig(JsonObject &root)
{
JsonObject top = root[FPSTR(_name)];
bool configComplete = !top.isNull();
configComplete &= getJsonValue(top[FPSTR(_enabled)], isEnabled);
configComplete &= getJsonValue(top[FPSTR(_loopInterval)], loopInterval);
return configComplete;
}
uint16_t getId()
{
return USERMOD_ID_INTERNAL_TEMPERATURE;
}
};
const char InternalTemperatureUsermod::_name[] PROGMEM = "Internal Temperature";
const char InternalTemperatureUsermod::_enabled[] PROGMEM = "Enabled";
const char InternalTemperatureUsermod::_loopInterval[] PROGMEM = "Loop Interval";
void InternalTemperatureUsermod::publishMqtt(const char *state, bool retain)
{
#ifndef WLED_DISABLE_MQTT
// Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED)
{
char subuf[64];
strcpy(subuf, mqttDeviceTopic);
strcat_P(subuf, PSTR("/mcutemp"));
mqtt->publish(subuf, 0, retain, state);
}
#endif
}

View File

@ -20,6 +20,12 @@
* .... * ....
*/ */
#if !defined(FFTTASK_PRIORITY)
#define FFTTASK_PRIORITY 1 // standard: looptask prio
//#define FFTTASK_PRIORITY 2 // above looptask, below asyc_tcp
//#define FFTTASK_PRIORITY 4 // above asyc_tcp
#endif
// Comment/Uncomment to toggle usb serial debugging // Comment/Uncomment to toggle usb serial debugging
// #define MIC_LOGGER // MIC sampling & sound input debugging (serial plotter) // #define MIC_LOGGER // MIC sampling & sound input debugging (serial plotter)
// #define FFT_SAMPLING_LOG // FFT result debugging // #define FFT_SAMPLING_LOG // FFT result debugging
@ -104,7 +110,7 @@ static float sampleAgc = 0.0f; // Smoothed AGC sample
// peak detection // peak detection
static bool samplePeak = false; // Boolean flag for peak - used in effects. Responding routine may reset this flag. Auto-reset after strip.getMinShowDelay() static bool samplePeak = false; // Boolean flag for peak - used in effects. Responding routine may reset this flag. Auto-reset after strip.getMinShowDelay()
static uint8_t maxVol = 10; // Reasonable value for constant volume for 'peak detector', as it won't always trigger (deprecated) static uint8_t maxVol = 31; // Reasonable value for constant volume for 'peak detector', as it won't always trigger (deprecated)
static uint8_t binNum = 8; // Used to select the bin for FFT based beat detection (deprecated) static uint8_t binNum = 8; // Used to select the bin for FFT based beat detection (deprecated)
static bool udpSamplePeak = false; // Boolean flag for peak. Set at the same tiem as samplePeak, but reset by transmitAudioData static bool udpSamplePeak = false; // Boolean flag for peak. Set at the same tiem as samplePeak, but reset by transmitAudioData
static unsigned long timeOfPeak = 0; // time of last sample peak detection. static unsigned long timeOfPeak = 0; // time of last sample peak detection.
@ -174,12 +180,17 @@ static float windowWeighingFactors[samplesFFT] = {0.0f};
// Create FFT object // Create FFT object
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT #ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
// lib_deps += https://github.com/kosme/arduinoFFT#develop @ 1.9.2 // lib_deps += https://github.com/kosme/arduinoFFT#develop @ 1.9.2
#define FFT_SPEED_OVER_PRECISION // enables use of reciprocals (1/x etc), and an a few other speedups // these options actually cause slow-downs on all esp32 processors, don't use them.
#define FFT_SQRT_APPROXIMATION // enables "quake3" style inverse sqrt // #define FFT_SPEED_OVER_PRECISION // enables use of reciprocals (1/x etc) - not faster on ESP32
#define sqrt(x) sqrtf(x) // little hack that reduces FFT time by 50% on ESP32 (as alternative to FFT_SQRT_APPROXIMATION) // #define FFT_SQRT_APPROXIMATION // enables "quake3" style inverse sqrt - slower on ESP32
// Below options are forcing ArduinoFFT to use sqrtf() instead of sqrt()
#define sqrt(x) sqrtf(x) // little hack that reduces FFT time by 10-50% on ESP32
#define sqrt_internal sqrtf // see https://github.com/kosme/arduinoFFT/pull/83
#else #else
// around 40% slower on -S2
// lib_deps += https://github.com/blazoncek/arduinoFFT.git // lib_deps += https://github.com/blazoncek/arduinoFFT.git
#endif #endif
#include <arduinoFFT.h> #include <arduinoFFT.h>
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT #ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
@ -411,7 +422,7 @@ static void runMicFilter(uint16_t numSamples, float *sampleBuffer) // p
//constexpr float beta1 = 0.8285f; // 18Khz //constexpr float beta1 = 0.8285f; // 18Khz
constexpr float beta1 = 0.85f; // 20Khz constexpr float beta1 = 0.85f; // 20Khz
constexpr float beta2 = (1.0f - beta1) / 2.0; constexpr float beta2 = (1.0f - beta1) / 2.0f;
static float last_vals[2] = { 0.0f }; // FIR high freq cutoff filter static float last_vals[2] = { 0.0f }; // FIR high freq cutoff filter
static float lowfilt = 0.0f; // IIR low frequency cutoff filter static float lowfilt = 0.0f; // IIR low frequency cutoff filter
@ -464,17 +475,17 @@ static void postProcessFFTResults(bool noiseGateOpen, int numberOfChannels) // p
switch (FFTScalingMode) { switch (FFTScalingMode) {
case 1: case 1:
// Logarithmic scaling // Logarithmic scaling
currentResult *= 0.42; // 42 is the answer ;-) currentResult *= 0.42f; // 42 is the answer ;-)
currentResult -= 8.0; // this skips the lowest row, giving some room for peaks currentResult -= 8.0f; // this skips the lowest row, giving some room for peaks
if (currentResult > 1.0) currentResult = logf(currentResult); // log to base "e", which is the fastest log() function if (currentResult > 1.0f) currentResult = logf(currentResult); // log to base "e", which is the fastest log() function
else currentResult = 0.0; // special handling, because log(1) = 0; log(0) = undefined else currentResult = 0.0f; // special handling, because log(1) = 0; log(0) = undefined
currentResult *= 0.85f + (float(i)/18.0f); // extra up-scaling for high frequencies currentResult *= 0.85f + (float(i)/18.0f); // extra up-scaling for high frequencies
currentResult = mapf(currentResult, 0, LOG_256, 0, 255); // map [log(1) ... log(255)] to [0 ... 255] currentResult = mapf(currentResult, 0, LOG_256, 0, 255); // map [log(1) ... log(255)] to [0 ... 255]
break; break;
case 2: case 2:
// Linear scaling // Linear scaling
currentResult *= 0.30f; // needs a bit more damping, get stay below 255 currentResult *= 0.30f; // needs a bit more damping, get stay below 255
currentResult -= 4.0; // giving a bit more room for peaks currentResult -= 4.0f; // giving a bit more room for peaks
if (currentResult < 1.0f) currentResult = 0.0f; if (currentResult < 1.0f) currentResult = 0.0f;
currentResult *= 0.85f + (float(i)/1.8f); // extra up-scaling for high frequencies currentResult *= 0.85f + (float(i)/1.8f); // extra up-scaling for high frequencies
break; break;
@ -482,8 +493,8 @@ static void postProcessFFTResults(bool noiseGateOpen, int numberOfChannels) // p
// square root scaling // square root scaling
currentResult *= 0.38f; currentResult *= 0.38f;
currentResult -= 6.0f; currentResult -= 6.0f;
if (currentResult > 1.0) currentResult = sqrtf(currentResult); if (currentResult > 1.0f) currentResult = sqrtf(currentResult);
else currentResult = 0.0; // special handling, because sqrt(0) = undefined else currentResult = 0.0f; // special handling, because sqrt(0) = undefined
currentResult *= 0.85f + (float(i)/4.5f); // extra up-scaling for high frequencies currentResult *= 0.85f + (float(i)/4.5f); // extra up-scaling for high frequencies
currentResult = mapf(currentResult, 0.0, 16.0, 0.0, 255.0); // map [sqrt(1) ... sqrt(256)] to [0 ... 255] currentResult = mapf(currentResult, 0.0, 16.0, 0.0, 255.0); // map [sqrt(1) ... sqrt(256)] to [0 ... 255]
break; break;
@ -511,11 +522,11 @@ static void postProcessFFTResults(bool noiseGateOpen, int numberOfChannels) // p
// peak detection is called from FFT task when vReal[] contains valid FFT results // peak detection is called from FFT task when vReal[] contains valid FFT results
static void detectSamplePeak(void) { static void detectSamplePeak(void) {
bool havePeak = false; bool havePeak = false;
// softhack007: this code continuously triggers while amplitude in the selected bin is above a certain threshold. So it does not detect peaks - it detects high activity in a frequency bin.
// Poor man's beat detection by seeing if sample > Average + some value. // Poor man's beat detection by seeing if sample > Average + some value.
// This goes through ALL of the 255 bins - but ignores stupid settings // This goes through ALL of the 255 bins - but ignores stupid settings
// Then we got a peak, else we don't. The peak has to time out on its own in order to support UDP sound sync. // Then we got a peak, else we don't. The peak has to time out on its own in order to support UDP sound sync.
if ((sampleAvg > 1) && (maxVol > 0) && (binNum > 1) && (vReal[binNum] > maxVol) && ((millis() - timeOfPeak) > 100)) { if ((sampleAvg > 1) && (maxVol > 0) && (binNum > 4) && (vReal[binNum] > maxVol) && ((millis() - timeOfPeak) > 100)) {
havePeak = true; havePeak = true;
} }
@ -758,7 +769,7 @@ class AudioReactive : public Usermod {
if (time_now - last_time > 2) { if (time_now - last_time > 2) {
last_time = time_now; last_time = time_now;
if((fabsf(sampleReal) < 2.0f) || (sampleMax < 1.0f)) { if((fabsf(sampleReal) < 2.0f) || (sampleMax < 1.0)) {
// MIC signal is "squelched" - deliver silence // MIC signal is "squelched" - deliver silence
tmpAgc = 0; tmpAgc = 0;
// we need to "spin down" the intgrated error buffer // we need to "spin down" the intgrated error buffer
@ -873,8 +884,8 @@ class AudioReactive : public Usermod {
// keep "peak" sample, but decay value if current sample is below peak // keep "peak" sample, but decay value if current sample is below peak
if ((sampleMax < sampleReal) && (sampleReal > 0.5f)) { if ((sampleMax < sampleReal) && (sampleReal > 0.5f)) {
sampleMax = sampleMax + 0.5f * (sampleReal - sampleMax); // new peak - with some filtering sampleMax = sampleMax + 0.5f * (sampleReal - sampleMax); // new peak - with some filtering
// another simple way to detect samplePeak // another simple way to detect samplePeak - cannot detect beats, but reacts on peak volume
if ((binNum < 10) && (millis() - timeOfPeak > 80) && (sampleAvg > 1)) { if (((binNum < 12) || ((maxVol < 1))) && (millis() - timeOfPeak > 80) && (sampleAvg > 1)) {
samplePeak = true; samplePeak = true;
timeOfPeak = millis(); timeOfPeak = millis();
udpSamplePeak = true; udpSamplePeak = true;
@ -964,9 +975,10 @@ class AudioReactive : public Usermod {
transmitData.FFT_Magnitude = my_magnitude; transmitData.FFT_Magnitude = my_magnitude;
transmitData.FFT_MajorPeak = FFT_MajorPeak; transmitData.FFT_MajorPeak = FFT_MajorPeak;
fftUdp.beginMulticastPacket(); if (fftUdp.beginMulticastPacket() != 0) { // beginMulticastPacket returns 0 in case of error
fftUdp.write(reinterpret_cast<uint8_t *>(&transmitData), sizeof(transmitData)); fftUdp.write(reinterpret_cast<uint8_t *>(&transmitData), sizeof(transmitData));
fftUdp.endPacket(); fftUdp.endPacket();
}
return; return;
} // transmitAudioData() } // transmitAudioData()
@ -1163,6 +1175,7 @@ class AudioReactive : public Usermod {
DEBUGSR_PRINTLN(F("AR: Analog Microphone (left channel only).")); DEBUGSR_PRINTLN(F("AR: Analog Microphone (left channel only)."));
audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE); audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE);
delay(100); delay(100);
useBandPassFilter = true; // PDM bandpass filter seems to help for bad quality analog
if (audioSource) audioSource->initialize(audioPin); if (audioSource) audioSource->initialize(audioPin);
break; break;
#endif #endif
@ -1272,9 +1285,10 @@ class AudioReactive : public Usermod {
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
// complain when audio userloop has been delayed for long time. Currently we need userloop running between 500 and 1500 times per second. // complain when audio userloop has been delayed for long time. Currently we need userloop running between 500 and 1500 times per second.
if ((userloopDelay > 23) && !disableSoundProcessing && (audioSyncEnabled == 0)) { // softhack007 disabled temporarily - avoid serial console spam with MANY leds and low FPS
DEBUG_PRINTF("[AR userLoop] hickup detected -> was inactive for last %d millis!\n", userloopDelay); //if ((userloopDelay > 65) && !disableSoundProcessing && (audioSyncEnabled == 0)) {
} //DEBUG_PRINTF("[AR userLoop] hickup detected -> was inactive for last %d millis!\n", userloopDelay);
//}
#endif #endif
// run filters, and repeat in case of loop delays (hick-up compensation) // run filters, and repeat in case of loop delays (hick-up compensation)
@ -1311,6 +1325,9 @@ class AudioReactive : public Usermod {
if (millis() - lastTime > delayMs) { if (millis() - lastTime > delayMs) {
have_new_sample = receiveAudioData(); have_new_sample = receiveAudioData();
if (have_new_sample) last_UDPTime = millis(); if (have_new_sample) last_UDPTime = millis();
#ifdef ARDUINO_ARCH_ESP32
else fftUdp.flush(); // Flush udp input buffers if we haven't read it - avoids hickups in receive mode. Does not work on 8266.
#endif
lastTime = millis(); lastTime = millis();
} }
if (have_new_sample) syncVolumeSmth = volumeSmth; // remember received sample if (have_new_sample) syncVolumeSmth = volumeSmth; // remember received sample
@ -1329,7 +1346,7 @@ class AudioReactive : public Usermod {
// Info Page: keep max sample from last 5 seconds // Info Page: keep max sample from last 5 seconds
if ((millis() - sampleMaxTimer) > CYCLE_SAMPLEMAX) { if ((millis() - sampleMaxTimer) > CYCLE_SAMPLEMAX) {
sampleMaxTimer = millis(); sampleMaxTimer = millis();
maxSample5sec = (0.15 * maxSample5sec) + 0.85 *((soundAgc) ? sampleAgc : sampleAvg); // reset, and start with some smoothing maxSample5sec = (0.15f * maxSample5sec) + 0.85f *((soundAgc) ? sampleAgc : sampleAvg); // reset, and start with some smoothing
if (sampleAvg < 1) maxSample5sec = 0; // noise gate if (sampleAvg < 1) maxSample5sec = 0; // noise gate
} else { } else {
if ((sampleAvg >= 1)) maxSample5sec = fmaxf(maxSample5sec, (soundAgc) ? rawSampleAgc : sampleRaw); // follow maximum volume if ((sampleAvg >= 1)) maxSample5sec = fmaxf(maxSample5sec, (soundAgc) ? rawSampleAgc : sampleRaw); // follow maximum volume
@ -1373,10 +1390,11 @@ class AudioReactive : public Usermod {
memset(fftAvg, 0, sizeof(fftAvg)); memset(fftAvg, 0, sizeof(fftAvg));
memset(fftResult, 0, sizeof(fftResult)); memset(fftResult, 0, sizeof(fftResult));
for(int i=(init?0:1); i<NUM_GEQ_CHANNELS; i+=2) fftResult[i] = 16; // make a tiny pattern for(int i=(init?0:1); i<NUM_GEQ_CHANNELS; i+=2) fftResult[i] = 16; // make a tiny pattern
inputLevel = 128; // resset level slider to default inputLevel = 128; // reset level slider to default
autoResetPeak(); autoResetPeak();
if (init && FFT_Task) { if (init && FFT_Task) {
delay(25); // give some time for I2S driver to finish sampling before we suspend it
vTaskSuspend(FFT_Task); // update is about to begin, disable task to prevent crash vTaskSuspend(FFT_Task); // update is about to begin, disable task to prevent crash
if (udpSyncConnected) { // close UDP sync connection (if open) if (udpSyncConnected) { // close UDP sync connection (if open)
udpSyncConnected = false; udpSyncConnected = false;
@ -1388,15 +1406,14 @@ class AudioReactive : public Usermod {
vTaskResume(FFT_Task); vTaskResume(FFT_Task);
connected(); // resume UDP connected(); // resume UDP
} else } else
// xTaskCreatePinnedToCore( xTaskCreateUniversal( // xTaskCreateUniversal also works on -S2 and -C3 with single core
xTaskCreate( // no need to "pin" this task to core #0
FFTcode, // Function to implement the task FFTcode, // Function to implement the task
"FFT", // Name of the task "FFT", // Name of the task
5000, // Stack size in words 3592, // Stack size in words // 3592 leaves 800-1024 bytes of task stack free
NULL, // Task input parameter NULL, // Task input parameter
1, // Priority of the task FFTTASK_PRIORITY, // Priority of the task
&FFT_Task // Task handle &FFT_Task // Task handle
// , 0 // Core where the task should run , 0 // Core where the task should run
); );
} }
micDataReal = 0.0f; // just to be sure micDataReal = 0.0f; // just to be sure
@ -1493,7 +1510,7 @@ class AudioReactive : public Usermod {
infoArr.add(F("I2S digital")); infoArr.add(F("I2S digital"));
} }
// input level or "silence" // input level or "silence"
if (maxSample5sec > 1.0) { if (maxSample5sec > 1.0f) {
float my_usage = 100.0f * (maxSample5sec / 255.0f); float my_usage = 100.0f * (maxSample5sec / 255.0f);
snprintf_P(myStringBuffer, 15, PSTR(" - peak %3d%%"), int(my_usage)); snprintf_P(myStringBuffer, 15, PSTR(" - peak %3d%%"), int(my_usage));
infoArr.add(myStringBuffer); infoArr.add(myStringBuffer);
@ -1503,7 +1520,7 @@ class AudioReactive : public Usermod {
} else { } else {
// error during audio source setup // error during audio source setup
infoArr.add(F("not initialized")); infoArr.add(F("not initialized"));
infoArr.add(F(" - check GPIO config")); infoArr.add(F(" - check pin settings"));
} }
} }
@ -1807,7 +1824,9 @@ class AudioReactive : public Usermod {
const char AudioReactive::_name[] PROGMEM = "AudioReactive"; const char AudioReactive::_name[] PROGMEM = "AudioReactive";
const char AudioReactive::_enabled[] PROGMEM = "enabled"; const char AudioReactive::_enabled[] PROGMEM = "enabled";
const char AudioReactive::_inputLvl[] PROGMEM = "inputLevel"; const char AudioReactive::_inputLvl[] PROGMEM = "inputLevel";
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
const char AudioReactive::_analogmic[] PROGMEM = "analogmic"; const char AudioReactive::_analogmic[] PROGMEM = "analogmic";
#endif
const char AudioReactive::_digitalmic[] PROGMEM = "digitalmic"; const char AudioReactive::_digitalmic[] PROGMEM = "digitalmic";
const char AudioReactive::UDP_SYNC_HEADER[] PROGMEM = "00002"; // new sync header version, as format no longer compatible with previous structure const char AudioReactive::UDP_SYNC_HEADER[] PROGMEM = "00002"; // new sync header version, as format no longer compatible with previous structure
const char AudioReactive::UDP_SYNC_HEADER_v1[] PROGMEM = "00001"; // old sync header version - need to add backwards-compatibility feature const char AudioReactive::UDP_SYNC_HEADER_v1[] PROGMEM = "00001"; // old sync header version - need to add backwards-compatibility feature

View File

@ -1,5 +1,5 @@
#pragma once #pragma once
#ifdef ARDUINO_ARCH_ESP32
#include "wled.h" #include "wled.h"
#include <driver/i2s.h> #include <driver/i2s.h>
#include <driver/adc.h> #include <driver/adc.h>
@ -22,14 +22,14 @@
// see https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/chip-series-comparison.html#related-documents // see https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/chip-series-comparison.html#related-documents
// and https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/i2s.html#overview-of-all-modes // and https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/i2s.html#overview-of-all-modes
#if defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) || defined(ESP8266) || defined(ESP8265) #if defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) || defined(ESP8266) || defined(ESP8265)
// there are two things in these MCUs that could lead to problems with audio processing: // there are two things in these MCUs that could lead to problems with audio processing:
// * no floating point hardware (FPU) support - FFT uses float calculations. If done in software, a strong slow-down can be expected (between 8x and 20x) // * no floating point hardware (FPU) support - FFT uses float calculations. If done in software, a strong slow-down can be expected (between 8x and 20x)
// * single core, so FFT task might slow down other things like LED updates // * single core, so FFT task might slow down other things like LED updates
#if !defined(SOC_I2S_NUM) || (SOC_I2S_NUM < 1) #if !defined(SOC_I2S_NUM) || (SOC_I2S_NUM < 1)
#error This audio reactive usermod does not support ESP32-C2, ESP32-C3 or ESP32-S2. #error This audio reactive usermod does not support ESP32-C2 or ESP32-C3.
#else #else
#warning This audio reactive usermod does not support ESP32-C2, ESP32-C3 or ESP32-S2. #warning This audio reactive usermod does not support ESP32-C2 and ESP32-C3.
#endif #endif
#endif #endif
@ -71,7 +71,7 @@
* if you want to receive two channels, one is the actual data from microphone and another channel is suppose to receive 0, it's different data in two channels, you need to choose I2S_CHANNEL_FMT_RIGHT_LEFT in this case. * if you want to receive two channels, one is the actual data from microphone and another channel is suppose to receive 0, it's different data in two channels, you need to choose I2S_CHANNEL_FMT_RIGHT_LEFT in this case.
*/ */
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)) && (ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(4, 4, 3)) #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)) && (ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(4, 4, 4))
// espressif bug: only_left has no sound, left and right are swapped // espressif bug: only_left has no sound, left and right are swapped
// https://github.com/espressif/esp-idf/issues/9635 I2S mic not working since 4.4 (IDFGH-8138) // https://github.com/espressif/esp-idf/issues/9635 I2S mic not working since 4.4 (IDFGH-8138)
// https://github.com/espressif/esp-idf/issues/8538 I2S channel selection issue? (IDFGH-6918) // https://github.com/espressif/esp-idf/issues/8538 I2S channel selection issue? (IDFGH-6918)
@ -122,7 +122,7 @@ class AudioSource {
This function needs to take care of anything that needs to be done This function needs to take care of anything that needs to be done
before samples can be obtained from the microphone. before samples can be obtained from the microphone.
*/ */
virtual void initialize(int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) = 0; virtual void initialize(int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) = 0;
/* Deinitialize /* Deinitialize
Release all resources and deactivate any functionality that is used Release all resources and deactivate any functionality that is used
@ -191,7 +191,8 @@ class I2SSource : public AudioSource {
}; };
} }
virtual void initialize(int8_t i2swsPin = I2S_PIN_NO_CHANGE, int8_t i2ssdPin = I2S_PIN_NO_CHANGE, int8_t i2sckPin = I2S_PIN_NO_CHANGE, int8_t mclkPin = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) { virtual void initialize(int8_t i2swsPin = I2S_PIN_NO_CHANGE, int8_t i2ssdPin = I2S_PIN_NO_CHANGE, int8_t i2sckPin = I2S_PIN_NO_CHANGE, int8_t mclkPin = I2S_PIN_NO_CHANGE) {
DEBUGSR_PRINTLN("I2SSource:: initialize().");
if (i2swsPin != I2S_PIN_NO_CHANGE && i2ssdPin != I2S_PIN_NO_CHANGE) { if (i2swsPin != I2S_PIN_NO_CHANGE && i2ssdPin != I2S_PIN_NO_CHANGE) {
if (!pinManager.allocatePin(i2swsPin, true, PinOwner::UM_Audioreactive) || if (!pinManager.allocatePin(i2swsPin, true, PinOwner::UM_Audioreactive) ||
!pinManager.allocatePin(i2ssdPin, false, PinOwner::UM_Audioreactive)) { // #206 !pinManager.allocatePin(i2ssdPin, false, PinOwner::UM_Audioreactive)) { // #206
@ -412,6 +413,7 @@ public:
}; };
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) { void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
DEBUGSR_PRINTLN("ES7243:: initialize();");
if ((i2sckPin < 0) || (mclkPin < 0)) { if ((i2sckPin < 0) || (mclkPin < 0)) {
DEBUGSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin); DEBUGSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
return; return;
@ -527,7 +529,7 @@ class ES8388Source : public I2SSource {
}; };
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) { void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
DEBUGSR_PRINTLN("ES8388Source:: initialize();");
if ((i2sckPin < 0) || (mclkPin < 0)) { if ((i2sckPin < 0) || (mclkPin < 0)) {
DEBUGSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin); DEBUGSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
return; return;
@ -584,7 +586,8 @@ class I2SAdcSource : public I2SSource {
/* identify Audiosource type - I2S-ADC*/ /* identify Audiosource type - I2S-ADC*/
AudioSourceType getType(void) {return(Type_I2SAdc);} AudioSourceType getType(void) {return(Type_I2SAdc);}
void initialize(int8_t audioPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) { void initialize(int8_t audioPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
DEBUGSR_PRINTLN("I2SAdcSource:: initialize().");
_myADCchannel = 0x0F; _myADCchannel = 0x0F;
if(!pinManager.allocatePin(audioPin, false, PinOwner::UM_Audioreactive)) { if(!pinManager.allocatePin(audioPin, false, PinOwner::UM_Audioreactive)) {
DEBUGSR_PRINTF("failed to allocate GPIO for audio analog input: %d\n", audioPin); DEBUGSR_PRINTF("failed to allocate GPIO for audio analog input: %d\n", audioPin);
@ -755,7 +758,8 @@ class SPH0654 : public I2SSource {
I2SSource(sampleRate, blockSize, sampleScale) I2SSource(sampleRate, blockSize, sampleScale)
{} {}
void initialize(uint8_t i2swsPin, uint8_t i2ssdPin, uint8_t i2sckPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) { void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t = I2S_PIN_NO_CHANGE) {
DEBUGSR_PRINTLN("SPH0654:: initialize();");
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin); I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin);
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
// these registers are only existing in "classic" ESP32 // these registers are only existing in "classic" ESP32
@ -766,3 +770,4 @@ class SPH0654 : public I2SSource {
#endif #endif
} }
}; };
#endif

View File

@ -1081,7 +1081,7 @@ bool FourLineDisplayUsermod::handleButton(uint8_t b) {
static bool buttonLongPressed = false; static bool buttonLongPressed = false;
static unsigned long buttonPressedTime = 0; static unsigned long buttonPressedTime = 0;
static unsigned long buttonWaitTime = 0; static unsigned long buttonWaitTime = 0;
bool handled = true; bool handled = false;
//momentary button logic //momentary button logic
if (isButtonPressed(b)) { //pressed if (isButtonPressed(b)) { //pressed
@ -1090,11 +1090,12 @@ bool FourLineDisplayUsermod::handleButton(uint8_t b) {
buttonPressedBefore = true; buttonPressedBefore = true;
if (now - buttonPressedTime > 600) { //long press if (now - buttonPressedTime > 600) { //long press
buttonLongPressed = true;
//TODO: handleButton() handles button 0 without preset in a different way for double click //TODO: handleButton() handles button 0 without preset in a different way for double click
//so we need to override with same behaviour //so we need to override with same behaviour
longPressAction(0); //DEBUG_PRINTLN(F("4LD action."));
//handled = false; //if (!buttonLongPressed) longPressAction(0);
buttonLongPressed = true;
return false;
} }
} else if (!isButtonPressed(b) && buttonPressedBefore) { //released } else if (!isButtonPressed(b) && buttonPressedBefore) { //released
@ -1126,7 +1127,7 @@ bool FourLineDisplayUsermod::handleButton(uint8_t b) {
buttonWaitTime = 0; buttonWaitTime = 0;
//TODO: handleButton() handles button 0 without preset in a different way for double click //TODO: handleButton() handles button 0 without preset in a different way for double click
//so we need to override with same behaviour //so we need to override with same behaviour
shortPressAction(0); //shortPressAction(0);
//handled = false; //handled = false;
} }
return handled; return handled;

View File

@ -398,8 +398,14 @@ void RotaryEncoderUIUsermod::sortModesAndPalettes() {
modes_alpha_indexes = re_initIndexArray(strip.getModeCount()); modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT); re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount()); palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount()+strip.customPalettes.size());
palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount()); // only use internal palettes palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount()+strip.customPalettes.size());
if (strip.customPalettes.size()) {
for (int i=0; i<strip.customPalettes.size(); i++) {
palettes_alpha_indexes[strip.getPaletteCount()+i] = 255-i;
palettes_qstrings[strip.getPaletteCount()+i] = PSTR("~Custom~");
}
}
// How many palette names start with '*' and should not be sorted? // How many palette names start with '*' and should not be sorted?
// (Also skipping the first one, 'Default'). // (Also skipping the first one, 'Default').
@ -496,7 +502,7 @@ void RotaryEncoderUIUsermod::setup()
} }
} else { } else {
PinManagerPinType pins[3] = { { pinA, false }, { pinB, false }, { pinC, false } }; PinManagerPinType pins[3] = { { pinA, false }, { pinB, false }, { pinC, false } };
if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_RotaryEncoderUI)) { if (pinA<0 || pinB<0 || !pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_RotaryEncoderUI)) {
pinA = pinB = pinC = -1; pinA = pinB = pinC = -1;
enabled = false; enabled = false;
return; return;
@ -507,7 +513,7 @@ void RotaryEncoderUIUsermod::setup()
#endif #endif
pinMode(pinA, USERMOD_ROTARY_ENCODER_GPIO); pinMode(pinA, USERMOD_ROTARY_ENCODER_GPIO);
pinMode(pinB, USERMOD_ROTARY_ENCODER_GPIO); pinMode(pinB, USERMOD_ROTARY_ENCODER_GPIO);
pinMode(pinC, USERMOD_ROTARY_ENCODER_GPIO); if (pinC>=0) pinMode(pinC, USERMOD_ROTARY_ENCODER_GPIO);
} }
loopTime = millis(); loopTime = millis();
@ -682,21 +688,25 @@ void RotaryEncoderUIUsermod::displayNetworkInfo() {
void RotaryEncoderUIUsermod::findCurrentEffectAndPalette() { void RotaryEncoderUIUsermod::findCurrentEffectAndPalette() {
DEBUG_PRINTLN(F("Finding current mode and palette.")); DEBUG_PRINTLN(F("Finding current mode and palette."));
currentEffectAndPaletteInitialized = true; currentEffectAndPaletteInitialized = true;
for (uint8_t i = 0; i < strip.getModeCount(); i++) {
effectCurrentIndex = 0;
for (int i = 0; i < strip.getModeCount(); i++) {
if (modes_alpha_indexes[i] == effectCurrent) { if (modes_alpha_indexes[i] == effectCurrent) {
effectCurrentIndex = i; effectCurrentIndex = i;
DEBUG_PRINTLN(F("Found current mode."));
break; break;
} }
} }
DEBUG_PRINTLN(F("Found current mode."));
for (uint8_t i = 0; i < strip.getPaletteCount(); i++) { effectPaletteIndex = 0;
DEBUG_PRINTLN(effectPalette);
for (uint8_t i = 0; i < strip.getPaletteCount()+strip.customPalettes.size(); i++) {
if (palettes_alpha_indexes[i] == effectPalette) { if (palettes_alpha_indexes[i] == effectPalette) {
effectPaletteIndex = i; effectPaletteIndex = i;
DEBUG_PRINTLN(F("Found palette."));
break; break;
} }
} }
DEBUG_PRINTLN(F("Found palette."));
} }
bool RotaryEncoderUIUsermod::changeState(const char *stateName, byte markedLine, byte markedCol, byte glyph) { bool RotaryEncoderUIUsermod::changeState(const char *stateName, byte markedLine, byte markedCol, byte glyph) {
@ -731,7 +741,9 @@ void RotaryEncoderUIUsermod::changeBrightness(bool increase) {
} }
display->updateRedrawTime(); display->updateRedrawTime();
#endif #endif
bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0); //bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0);
if (bri < 40) bri = max(min((increase ? bri+fadeAmount/2 : bri-fadeAmount/2), 255), 0); // slower steps when brightness < 16%
else bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0);
lampUdated(); lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
display->updateBrightness(); display->updateBrightness();
@ -878,7 +890,7 @@ void RotaryEncoderUIUsermod::changePalette(bool increase) {
} }
display->updateRedrawTime(); display->updateRedrawTime();
#endif #endif
effectPaletteIndex = max(min((increase ? effectPaletteIndex+1 : effectPaletteIndex-1), strip.getPaletteCount()-1), 0); effectPaletteIndex = max(min((unsigned)(increase ? effectPaletteIndex+1 : effectPaletteIndex-1), strip.getPaletteCount()+strip.customPalettes.size()-1), 0U);
effectPalette = palettes_alpha_indexes[effectPaletteIndex]; effectPalette = palettes_alpha_indexes[effectPaletteIndex];
stateChanged = true; stateChanged = true;
if (applyToAll) { if (applyToAll) {

View File

@ -0,0 +1,22 @@
# Example PlatformIO Project Configuration Override for WireGuard
# ------------------------------------------------------------------------------
# Copy to platformio_override.ini to activate.
# ------------------------------------------------------------------------------
# Please visit documentation: https://docs.platformio.org/page/projectconf.html
[platformio]
default_envs = WLED_ESP32-WireGuard
[env:WLED_ESP32-WireGuard]
board = esp32dev
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32}
-D WLED_RELEASE_NAME=ESP32-WireGuard
-D USERMOD_WIREGUARD
lib_deps = ${esp32.lib_deps}
https://github.com/kienvu58/WireGuard-ESP32-Arduino.git
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions}
upload_speed = 921600

View File

@ -0,0 +1,19 @@
# WireGuard VPN
This usermod will connect your WLED instance to a remote WireGuard subnet.
Configuration is performed via the Usermod menu. There are no parameters to set in code!
## Installation
Copy the `platformio_override.ini` file to the root project directory, review the build options, and select the `WLED_ESP32-WireGuard` environment.
## Author
Aiden Vigue [vigue.me](https://vigue.me)
[@acvigue](https://github.com/acvigue)
aiden@vigue.me

View File

@ -0,0 +1,127 @@
#pragma once
#include <WireGuard-ESP32.h>
#include "wled.h"
class WireguardUsermod : public Usermod {
public:
void setup() { configTzTime(posix_tz, ntpServerName); }
void connected() {
if (wg.is_initialized()) {
wg.end();
}
}
void loop() {
if (millis() - lastTime > 5000) {
if (is_enabled && WLED_CONNECTED) {
if (!wg.is_initialized()) {
struct tm timeinfo;
if (getLocalTime(&timeinfo, 0)) {
if (strlen(preshared_key) < 1) {
wg.begin(local_ip, private_key, endpoint_address, public_key, endpoint_port, NULL);
} else {
wg.begin(local_ip, private_key, endpoint_address, public_key, endpoint_port, preshared_key);
}
}
}
}
lastTime = millis();
}
}
void addToJsonInfo(JsonObject& root) {
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
JsonArray infoArr = user.createNestedArray(F("WireGuard"));
String uiDomString;
struct tm timeinfo;
if (!getLocalTime(&timeinfo, 0)) {
uiDomString = "Time out of sync!";
} else {
if (wg.is_initialized()) {
uiDomString = "netif up!";
} else {
uiDomString = "netif down :(";
}
}
if (is_enabled) infoArr.add(uiDomString);
}
void appendConfigData() {
oappend(SET_F("addInfo('WireGuard:host',1,'Server Hostname');")); // 0 is field type, 1 is actual field
oappend(SET_F("addInfo('WireGuard:port',1,'Server Port');")); // 0 is field type, 1 is actual field
oappend(SET_F("addInfo('WireGuard:ip',1,'Device IP');")); // 0 is field type, 1 is actual field
oappend(SET_F("addInfo('WireGuard:psk',1,'Pre Shared Key (optional)');")); // 0 is field type, 1 is actual field
oappend(SET_F("addInfo('WireGuard:pem',1,'Private Key');")); // 0 is field type, 1 is actual field
oappend(SET_F("addInfo('WireGuard:pub',1,'Public Key');")); // 0 is field type, 1 is actual field
oappend(SET_F("addInfo('WireGuard:tz',1,'POSIX timezone string');")); // 0 is field type, 1 is actual field
}
void addToConfig(JsonObject& root) {
JsonObject top = root.createNestedObject(F("WireGuard"));
top[F("host")] = endpoint_address;
top[F("port")] = endpoint_port;
top[F("ip")] = local_ip.toString();
top[F("psk")] = preshared_key;
top[F("pem")] = private_key;
top[F("pub")] = public_key;
top[F("tz")] = posix_tz;
}
bool readFromConfig(JsonObject& root) {
JsonObject top = root[F("WireGuard")];
if (top["host"].isNull() || top["port"].isNull() || top["ip"].isNull() || top["pem"].isNull() || top["pub"].isNull() || top["tz"].isNull()) {
is_enabled = false;
return false;
} else {
const char* host = top["host"];
strncpy(endpoint_address, host, 100);
const char* ip_s = top["ip"];
uint8_t ip[4];
sscanf(ip_s, "%u.%u.%u.%u", &ip[0], &ip[1], &ip[2], &ip[3]);
local_ip = IPAddress(ip[0], ip[1], ip[2], ip[3]);
const char* pem = top["pem"];
strncpy(private_key, pem, 45);
const char* pub = top["pub"];
strncpy(public_key, pub, 45);
const char* tz = top["tz"];
strncpy(posix_tz, tz, 150);
endpoint_port = top["port"];
if (!top["psk"].isNull()) {
const char* psk = top["psk"];
strncpy(preshared_key, psk, 45);
}
is_enabled = true;
}
return is_enabled;
}
uint16_t getId() { return USERMOD_ID_WIREGUARD; }
private:
WireGuard wg;
char preshared_key[45];
char private_key[45];
IPAddress local_ip;
char public_key[45];
char endpoint_address[100];
char posix_tz[150];
int endpoint_port = 0;
bool is_enabled = false;
unsigned long lastTime = 0;
};

File diff suppressed because it is too large Load Diff

View File

@ -176,7 +176,7 @@
#define FX_MODE_FIRE_FLICKER 45 #define FX_MODE_FIRE_FLICKER 45
#define FX_MODE_GRADIENT 46 #define FX_MODE_GRADIENT 46
#define FX_MODE_LOADING 47 #define FX_MODE_LOADING 47
// #define FX_MODE_POLICE 48 // removed in 0.14! #define FX_MODE_ROLLINGBALLS 48 //was Police before 0.14
#define FX_MODE_FAIRY 49 //was Police All prior to 0.13.0-b6 (use "Two Dots" with Red/Blue and full intensity) #define FX_MODE_FAIRY 49 //was Police All prior to 0.13.0-b6 (use "Two Dots" with Red/Blue and full intensity)
#define FX_MODE_TWO_DOTS 50 #define FX_MODE_TWO_DOTS 50
#define FX_MODE_FAIRYTWINKLE 51 //was Two Areas prior to 0.13.0-b6 (use "Two Dots" with full intensity) #define FX_MODE_FAIRYTWINKLE 51 //was Two Areas prior to 0.13.0-b6 (use "Two Dots" with full intensity)
@ -329,7 +329,7 @@ typedef enum mapping1D2D {
M12_pCorner = 3 M12_pCorner = 3
} mapping1D2D_t; } mapping1D2D_t;
// segment, 72 bytes // segment, 80 bytes
typedef struct Segment { typedef struct Segment {
public: public:
uint16_t start; // start index / start X coordinate 2D (left) uint16_t start; // start index / start X coordinate 2D (left)
@ -379,10 +379,32 @@ typedef struct Segment {
uint16_t aux0; // custom var uint16_t aux0; // custom var
uint16_t aux1; // custom var uint16_t aux1; // custom var
byte *data; // effect data pointer byte *data; // effect data pointer
CRGB* leds; // local leds[] array (may be a pointer to global)
static CRGB *_globalLeds; // global leds[] array
static uint16_t maxWidth, maxHeight; // these define matrix width & height (max. segment dimensions) static uint16_t maxWidth, maxHeight; // these define matrix width & height (max. segment dimensions)
typedef struct TemporarySegmentData {
uint16_t _optionsT;
uint32_t _colorT[NUM_COLORS];
uint8_t _speedT;
uint8_t _intensityT;
uint8_t _custom1T, _custom2T; // custom FX parameters/sliders
struct {
uint8_t _custom3T : 5; // reduced range slider (0-31)
bool _check1T : 1; // checkmark 1
bool _check2T : 1; // checkmark 2
bool _check3T : 1; // checkmark 3
};
uint16_t _aux0T;
uint16_t _aux1T;
uint32_t _stepT;
uint32_t _callT;
uint8_t *_dataT;
uint16_t _dataLenT;
TemporarySegmentData()
: _dataT(nullptr) // just in case...
, _dataLenT(0)
{}
} tmpsegd_t;
private: private:
union { union {
uint8_t _capabilities; uint8_t _capabilities;
@ -397,39 +419,34 @@ typedef struct Segment {
uint16_t _dataLen; uint16_t _dataLen;
static uint16_t _usedSegmentData; static uint16_t _usedSegmentData;
// transition data, valid only if transitional==true, holds values during transition // perhaps this should be per segment, not static
static CRGBPalette16 _randomPalette; // actual random palette
static CRGBPalette16 _newRandomPalette; // target random palette
static unsigned long _lastPaletteChange; // last random palette change time in millis()
#ifndef WLED_DISABLE_MODE_BLEND
static bool _modeBlend; // mode/effect blending semaphore
#endif
// transition data, valid only if transitional==true, holds values during transition (72 bytes)
struct Transition { struct Transition {
#ifndef WLED_DISABLE_MODE_BLEND
tmpsegd_t _segT; // previous segment environment
uint8_t _modeT; // previous mode/effect
#else
uint32_t _colorT[NUM_COLORS]; uint32_t _colorT[NUM_COLORS];
#endif
uint8_t _briT; // temporary brightness uint8_t _briT; // temporary brightness
uint8_t _cctT; // temporary CCT uint8_t _cctT; // temporary CCT
CRGBPalette16 _palT; // temporary palette CRGBPalette16 _palT; // temporary palette
uint8_t _prevPaletteBlends; // number of previous palette blends (there are max 255 belnds possible) uint8_t _prevPaletteBlends; // number of previous palette blends (there are max 255 belnds possible)
uint8_t _modeP; // previous mode/effect unsigned long _start; // must accommodate millis()
//uint16_t _aux0, _aux1; // previous mode/effect runtime data
//uint32_t _step, _call; // previous mode/effect runtime data
//byte *_data; // previous mode/effect runtime data
uint32_t _start;
uint16_t _dur; uint16_t _dur;
Transition(uint16_t dur=750) Transition(uint16_t dur=750)
: _briT(255) : _palT(CRGBPalette16(CRGB::Black))
, _cctT(127)
, _palT(CRGBPalette16(CRGB::Black))
, _prevPaletteBlends(0) , _prevPaletteBlends(0)
, _modeP(FX_MODE_STATIC)
, _start(millis()) , _start(millis())
, _dur(dur) , _dur(dur)
{} {}
Transition(uint16_t d, uint8_t b, uint8_t c, const uint32_t *o)
: _briT(b)
, _cctT(c)
, _palT(CRGBPalette16(CRGB::Black))
, _prevPaletteBlends(0)
, _modeP(FX_MODE_STATIC)
, _start(millis())
, _dur(d)
{
for (size_t i=0; i<NUM_COLORS; i++) _colorT[i] = o[i];
}
} *_t; } *_t;
public: public:
@ -463,12 +480,14 @@ typedef struct Segment {
aux0(0), aux0(0),
aux1(0), aux1(0),
data(nullptr), data(nullptr),
leds(nullptr),
_capabilities(0), _capabilities(0),
_dataLen(0), _dataLen(0),
_t(nullptr) _t(nullptr)
{ {
//refreshLightCapabilities(); //refreshLightCapabilities();
#ifdef WLED_DEBUG
//Serial.printf("-- Creating segment: %p\n", this);
#endif
} }
Segment(uint16_t sStartX, uint16_t sStopX, uint16_t sStartY, uint16_t sStopY) : Segment(sStartX, sStopX) { Segment(uint16_t sStartX, uint16_t sStopX, uint16_t sStartY, uint16_t sStopY) : Segment(sStartX, sStopX) {
@ -480,16 +499,14 @@ typedef struct Segment {
Segment(Segment &&orig) noexcept; // move constructor Segment(Segment &&orig) noexcept; // move constructor
~Segment() { ~Segment() {
//#ifdef WLED_DEBUG #ifdef WLED_DEBUG
//Serial.print(F("Destroying segment:")); //Serial.printf("-- Destroying segment: %p\n", this);
//if (name) Serial.printf(" %s (%p)", name, name); //if (name) Serial.printf(" %s (%p)", name, name);
//if (data) Serial.printf(" %d (%p)", (int)_dataLen, data); //if (data) Serial.printf(" %d (%p)", (int)_dataLen, data);
//if (leds) Serial.printf(" [%u]", length()*sizeof(CRGB));
//Serial.println(); //Serial.println();
//#endif #endif
if (!Segment::_globalLeds && leds) { free(leds); leds = nullptr;} // reset to nullptr, to avoid race conditions if (name) { delete[] name; name = nullptr; }
if (name) delete[] name; stopTransition();
if (_t) delete _t;
deallocateData(); deallocateData();
} }
@ -497,7 +514,7 @@ typedef struct Segment {
Segment& operator= (Segment &&orig) noexcept; // move assignment Segment& operator= (Segment &&orig) noexcept; // move assignment
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
size_t getSize() const { return sizeof(Segment) + (data?_dataLen:0) + (name?strlen(name):0) + (_t?sizeof(Transition):0) + (!Segment::_globalLeds && leds?sizeof(CRGB)*length():0); } size_t getSize() const { return sizeof(Segment) + (data?_dataLen:0) + (name?strlen(name):0) + (_t?sizeof(Transition):0); }
#endif #endif
inline bool getOption(uint8_t n) const { return ((options >> n) & 0x01); } inline bool getOption(uint8_t n) const { return ((options >> n) & 0x01); }
@ -507,16 +524,20 @@ typedef struct Segment {
inline bool hasRGB(void) const { return _isRGB; } inline bool hasRGB(void) const { return _isRGB; }
inline bool hasWhite(void) const { return _hasW; } inline bool hasWhite(void) const { return _hasW; }
inline bool isCCT(void) const { return _isCCT; } inline bool isCCT(void) const { return _isCCT; }
inline uint16_t width(void) const { return (stop > start) ? (stop - start) : 0; } // segment width in physical pixels (length if 1D) inline uint16_t width(void) const { return isActive() ? (stop - start) : 0; } // segment width in physical pixels (length if 1D)
inline uint16_t height(void) const { return (stopY > startY) ? (stopY - startY) : 0; } // segment height (if 2D) in physical pixels // softhack007: make sure its always > 0 inline uint16_t height(void) const { return stopY - startY; } // segment height (if 2D) in physical pixels (it *is* always >=1)
inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels
inline uint16_t groupLength(void) const { return grouping + spacing; } inline uint16_t groupLength(void) const { return grouping + spacing; }
inline uint8_t getLightCapabilities(void) const { return _capabilities; } inline uint8_t getLightCapabilities(void) const { return _capabilities; }
static uint16_t getUsedSegmentData(void) { return _usedSegmentData; } static uint16_t getUsedSegmentData(void) { return _usedSegmentData; }
static void addUsedSegmentData(int len) { _usedSegmentData += len; } static void addUsedSegmentData(int len) { _usedSegmentData += len; }
#ifndef WLED_DISABLE_MODE_BLEND
static void modeBlend(bool blend) { _modeBlend = blend; }
#endif
static void handleRandomPalette();
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); 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, uint8_t segId = 255);
bool setColor(uint8_t slot, uint32_t c); //returns true if changed bool setColor(uint8_t slot, uint32_t c); //returns true if changed
void setCCT(uint16_t k); void setCCT(uint16_t k);
void setOpacity(uint8_t o); void setOpacity(uint8_t o);
@ -538,11 +559,15 @@ typedef struct Segment {
* Safe to call from interrupts and network requests. * Safe to call from interrupts and network requests.
*/ */
inline void markForReset(void) { reset = true; } // setOption(SEG_OPTION_RESET, true) inline void markForReset(void) { reset = true; } // setOption(SEG_OPTION_RESET, true)
void setUpLeds(void); // set up leds[] array for loseless getPixelColor()
// transition functions // transition functions
void startTransition(uint16_t dur); // transition has to start before actual segment values change void startTransition(uint16_t dur); // transition has to start before actual segment values change
void stopTransition(void);
void handleTransition(void); void handleTransition(void);
#ifndef WLED_DISABLE_MODE_BLEND
void swapSegenv(tmpsegd_t &tmpSegD);
void restoreSegenv(tmpsegd_t &tmpSegD);
#endif
uint16_t progress(void); //transition progression between 0-65535 uint16_t progress(void); //transition progression between 0-65535
uint8_t currentBri(uint8_t briNew, bool useCct = false); uint8_t currentBri(uint8_t briNew, bool useCct = false);
uint8_t currentMode(uint8_t modeNew); uint8_t currentMode(uint8_t modeNew);
@ -579,7 +604,7 @@ typedef struct Segment {
uint16_t virtualHeight(void) const; uint16_t virtualHeight(void) const;
uint16_t nrOfVStrips(void) const; uint16_t nrOfVStrips(void) const;
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
uint16_t XY(uint16_t x, uint16_t y); // support function to get relative index within segment (for leds[]) uint16_t XY(uint16_t x, uint16_t y); // support function to get relative index within segment
void setPixelColorXY(int x, int y, uint32_t c); // set relative pixel within segment with color 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, 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(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } // automatically inline
@ -604,9 +629,9 @@ typedef struct Segment {
void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c); 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, uint32_t c);
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0)); } // automatic inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0)); } // automatic inline
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0); void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0, int8_t rotate = 0);
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0)); } // automatic inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0)); } // automatic inline
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0)); } // automatic inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0), rotate); } // automatic inline
void wu_pixel(uint32_t x, uint32_t y, CRGB c); void wu_pixel(uint32_t x, uint32_t y, CRGB c);
void blur1d(fract8 blur_amount); // blur all rows in 1 dimension void blur1d(fract8 blur_amount); // blur all rows in 1 dimension
void blur2d(fract8 blur_amount) { blur(blur_amount); } void blur2d(fract8 blur_amount) { blur(blur_amount); }
@ -636,8 +661,9 @@ typedef struct Segment {
void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c) {} 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, uint32_t c) {}
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB 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, uint32_t color, uint32_t = 0, int8_t = 0) {}
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB color) {} void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB color) {}
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) {}
void wu_pixel(uint32_t x, uint32_t y, CRGB c) {} void wu_pixel(uint32_t x, uint32_t y, CRGB c) {}
#endif #endif
} segment; } segment;
@ -692,7 +718,15 @@ class WS2812FX { // 96 bytes
customMappingSize(0), customMappingSize(0),
_lastShow(0), _lastShow(0),
_segment_index(0), _segment_index(0),
_mainSegment(0) _mainSegment(0),
_queuedChangesSegId(255),
_qStart(0),
_qStop(0),
_qStartY(0),
_qStopY(0),
_qGrouping(0),
_qSpacing(0),
_qOffset(0)
{ {
WS2812FX::instance = this; WS2812FX::instance = this;
_mode.reserve(_modeCount); // allocate memory to prevent initial fragmentation (does not increase size()) _mode.reserve(_modeCount); // allocate memory to prevent initial fragmentation (does not increase size())
@ -710,7 +744,6 @@ class WS2812FX { // 96 bytes
panel.clear(); panel.clear();
#endif #endif
customPalettes.clear(); customPalettes.clear();
if (useLedsArray && Segment::_globalLeds) {free(Segment::_globalLeds); Segment::_globalLeds = nullptr;} // reset to nullptr, to avoid race conditions
} }
static WS2812FX* getInstance(void) { return instance; } static WS2812FX* getInstance(void) { return instance; }
@ -749,7 +782,7 @@ class WS2812FX { // 96 bytes
inline void trigger(void) { _triggered = true; } // Forces the next frame to be computed on all active segments. inline void trigger(void) { _triggered = true; } // Forces the next frame to be computed on all active segments.
inline void setShowCallback(show_callback cb) { _callback = cb; } inline void setShowCallback(show_callback cb) { _callback = cb; }
inline void setTransition(uint16_t t) { _transitionDur = t; } inline void setTransition(uint16_t t) { _transitionDur = t; }
inline void appendSegment(const Segment &seg = Segment()) { _segments.push_back(seg); } inline void appendSegment(const Segment &seg = Segment()) { if (_segments.size() < getMaxSegments()) _segments.push_back(seg); }
bool bool
checkSegmentAlignment(void), checkSegmentAlignment(void),
@ -757,8 +790,7 @@ class WS2812FX { // 96 bytes
hasCCTBus(void), hasCCTBus(void),
// return true if the strip is being sent pixel updates // return true if the strip is being sent pixel updates
isUpdating(void), isUpdating(void),
deserializeMap(uint8_t n=0), deserializeMap(uint8_t n=0);
useLedsArray = false;
inline bool isServicing(void) { return _isServicing; } inline bool isServicing(void) { return _isServicing; }
inline bool hasWhiteChannel(void) {return _hasWhiteChannel;} inline bool hasWhiteChannel(void) {return _hasWhiteChannel;}
@ -900,13 +932,20 @@ class WS2812FX { // 96 bytes
uint16_t* customMappingTable; uint16_t* customMappingTable;
uint16_t customMappingSize; uint16_t customMappingSize;
uint32_t _lastShow; unsigned long _lastShow;
uint8_t _segment_index; uint8_t _segment_index;
uint8_t _mainSegment; uint8_t _mainSegment;
uint8_t _queuedChangesSegId;
uint16_t _qStart, _qStop, _qStartY, _qStopY;
uint8_t _qGrouping, _qSpacing;
uint16_t _qOffset;
uint8_t
estimateCurrentAndLimitBri(void);
void void
estimateCurrentAndLimitBri(void); setUpSegmentFromQueuedChanges(void);
}; };
extern const char JSON_mode_names[]; extern const char JSON_mode_names[];

View File

@ -189,20 +189,16 @@ uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) {
// XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element) // XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element)
uint16_t /*IRAM_ATTR*/ Segment::XY(uint16_t x, uint16_t y) { uint16_t /*IRAM_ATTR*/ Segment::XY(uint16_t x, uint16_t y) {
uint16_t width = virtualWidth(); // segment width in logical pixels uint16_t width = virtualWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
uint16_t height = virtualHeight(); // segment height in logical pixels uint16_t height = virtualHeight(); // segment height in logical pixels (is always >= 1)
if (width == 0) return 0; // softhack007 avoid div/0 return isActive() ? (x%width) + (y%height) * width : 0;
if (height == 0) return (x%width); // softhack007 avoid div/0
return (x%width) + (y%height) * width;
} }
void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col) void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col)
{ {
if (Segment::maxHeight==1) return; // not a matrix set-up 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 if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit
if (leds) leds[XY(x,y)] = col;
uint8_t _bri_t = currentBri(on ? opacity : 0); uint8_t _bri_t = currentBri(on ? opacity : 0);
if (_bri_t < 255) { if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t); byte r = scale8(R(col), _bri_t);
@ -220,23 +216,29 @@ void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col)
y *= 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 if (x >= width() || y >= height()) return; // if pixel would fall out of segment just exit
uint32_t tmpCol = col;
for (int j = 0; j < grouping; j++) { // groupping vertically for (int j = 0; j < grouping; j++) { // groupping vertically
for (int g = 0; g < grouping; g++) { // groupping horizontally for (int g = 0; g < grouping; g++) { // groupping horizontally
uint16_t xX = (x+g), yY = (y+j); uint16_t xX = (x+g), yY = (y+j);
if (xX >= width() || yY >= height()) continue; // we have reached one dimension's end if (xX >= width() || yY >= height()) continue; // we have reached one dimension's end
strip.setPixelColorXY(start + xX, startY + yY, col); #ifndef WLED_DISABLE_MODE_BLEND
// if blending modes, blend with underlying pixel
if (_modeBlend) tmpCol = color_blend(strip.getPixelColorXY(start + xX, startY + yY), col, 0xFFFFU - progress(), true);
#endif
strip.setPixelColorXY(start + xX, startY + yY, tmpCol);
if (mirror) { //set the corresponding horizontally mirrored pixel if (mirror) { //set the corresponding horizontally mirrored pixel
if (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col); if (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, tmpCol);
else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col); else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, tmpCol);
} }
if (mirror_y) { //set the corresponding vertically mirrored pixel if (mirror_y) { //set the corresponding vertically mirrored pixel
if (transpose) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col); if (transpose) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, tmpCol);
else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col); else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, tmpCol);
} }
if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel
strip.setPixelColorXY(width() - xX - 1, height() - yY - 1, col); strip.setPixelColorXY(width() - xX - 1, height() - yY - 1, tmpCol);
} }
} }
} }
@ -245,7 +247,7 @@ void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col)
// anti-aliased version of setPixelColorXY() // anti-aliased version of setPixelColorXY()
void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa) void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa)
{ {
if (Segment::maxHeight==1) return; // not a matrix set-up if (!isActive()) return; // not active
if (x<0.0f || x>1.0f || y<0.0f || y>1.0f) return; // not normalized if (x<0.0f || x>1.0f || y<0.0f || y>1.0f) return; // not normalized
const uint16_t cols = virtualWidth(); const uint16_t cols = virtualWidth();
@ -288,8 +290,8 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa)
// returns RGBW values of pixel // returns RGBW values of pixel
uint32_t Segment::getPixelColorXY(uint16_t x, uint16_t y) { uint32_t Segment::getPixelColorXY(uint16_t x, uint16_t y) {
int i = XY(x,y); if (!isActive()) return 0; // not active
if (leds) return RGBW32(leds[i].r, leds[i].g, leds[i].b, 0); if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return 0; // if pixel would fall out of virtual segment just exit
if (reverse ) x = virtualWidth() - x - 1; if (reverse ) x = virtualWidth() - x - 1;
if (reverse_y) y = virtualHeight() - y - 1; if (reverse_y) y = virtualHeight() - y - 1;
if (transpose) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed if (transpose) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed
@ -306,6 +308,7 @@ 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. // 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 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 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); uint32_t col = getPixelColorXY(x,y);
uint8_t r = R(col); uint8_t r = R(col);
@ -325,12 +328,14 @@ void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) {
} }
void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) {
if (!isActive()) return; // not active
CRGB pix = CRGB(getPixelColorXY(x,y)).nscale8_video(fade); CRGB pix = CRGB(getPixelColorXY(x,y)).nscale8_video(fade);
setPixelColorXY(x, y, pix); setPixelColorXY(x, y, pix);
} }
// blurRow: perform a blur on a row of a rectangular matrix // blurRow: perform a blur on a row of a rectangular matrix
void Segment::blurRow(uint16_t row, fract8 blur_amount) { void Segment::blurRow(uint16_t row, fract8 blur_amount) {
if (!isActive()) return; // not active
const uint_fast16_t cols = virtualWidth(); const uint_fast16_t cols = virtualWidth();
const uint_fast16_t rows = virtualHeight(); const uint_fast16_t rows = virtualHeight();
@ -358,6 +363,7 @@ void Segment::blurRow(uint16_t row, fract8 blur_amount) {
// blurCol: perform a blur on a column of a rectangular matrix // blurCol: perform a blur on a column of a rectangular matrix
void Segment::blurCol(uint16_t col, fract8 blur_amount) { void Segment::blurCol(uint16_t col, fract8 blur_amount) {
if (!isActive()) return; // not active
const uint_fast16_t cols = virtualWidth(); const uint_fast16_t cols = virtualWidth();
const uint_fast16_t rows = virtualHeight(); const uint_fast16_t rows = virtualHeight();
@ -385,6 +391,7 @@ void Segment::blurCol(uint16_t col, fract8 blur_amount) {
// 1D Box blur (with added weight - blur_amount: [0=no blur, 255=max blur]) // 1D Box blur (with added weight - blur_amount: [0=no blur, 255=max blur])
void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) { void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint16_t rows = virtualHeight();
const uint16_t dim1 = vertical ? rows : cols; const uint16_t dim1 = vertical ? rows : cols;
@ -437,6 +444,7 @@ void Segment::blur1d(fract8 blur_amount) {
} }
void Segment::moveX(int8_t delta, bool wrap) { void Segment::moveX(int8_t delta, bool wrap) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint16_t rows = virtualHeight();
if (!delta || abs(delta) >= cols) return; if (!delta || abs(delta) >= cols) return;
@ -454,6 +462,7 @@ void Segment::moveX(int8_t delta, bool wrap) {
} }
void Segment::moveY(int8_t delta, bool wrap) { void Segment::moveY(int8_t delta, bool wrap) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint16_t rows = virtualHeight();
if (!delta || abs(delta) >= rows) return; if (!delta || abs(delta) >= rows) return;
@ -489,6 +498,7 @@ void Segment::move(uint8_t dir, uint8_t delta, bool wrap) {
} }
void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
if (!isActive()) return; // not active
// Bresenhams Algorithm // Bresenhams Algorithm
int d = 3 - (2*radius); int d = 3 - (2*radius);
int y = radius, x = 0; int y = radius, x = 0;
@ -513,6 +523,7 @@ void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
// by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs // by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs
void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint16_t rows = virtualHeight();
for (int16_t y = -radius; y <= radius; y++) { for (int16_t y = -radius; y <= radius; y++) {
@ -526,6 +537,7 @@ void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
} }
void Segment::nscale8(uint8_t scale) { void Segment::nscale8(uint8_t scale) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint16_t rows = virtualHeight();
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
@ -535,6 +547,7 @@ void Segment::nscale8(uint8_t scale) {
//line function //line function
void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) { void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint16_t rows = virtualHeight();
if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return; if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return;
@ -558,7 +571,8 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3
// draws a raster font character on canvas // draws a raster font character on canvas
// only supports: 4x6=24, 5x8=40, 5x12=60, 6x8=48 and 7x9=63 fonts ATM // only supports: 4x6=24, 5x8=40, 5x12=60, 6x8=48 and 7x9=63 fonts ATM
void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2) { void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2, int8_t rotate) {
if (!isActive()) return; // not active
if (chr < 32 || chr > 126) return; // only ASCII 32-126 supported if (chr < 32 || chr > 126) return; // only ASCII 32-126 supported
chr -= 32; // align with font table entries chr -= 32; // align with font table entries
const uint16_t cols = virtualWidth(); const uint16_t cols = virtualWidth();
@ -570,9 +584,6 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
//if (w<5 || w>6 || h!=8) return; //if (w<5 || w>6 || h!=8) return;
for (int i = 0; i<h; i++) { // character height for (int i = 0; i<h; i++) { // character height
int16_t y0 = y + i;
if (y0 < 0) continue; // drawing off-screen
if (y0 >= rows) break; // drawing off-screen
uint8_t bits = 0; uint8_t bits = 0;
switch (font) { switch (font) {
case 24: bits = pgm_read_byte_near(&console_font_4x6[(chr * h) + i]); break; // 5x8 font case 24: bits = pgm_read_byte_near(&console_font_4x6[(chr * h) + i]); break; // 5x8 font
@ -584,8 +595,16 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
} }
col = ColorFromPalette(grad, (i+1)*255/h, 255, NOBLEND); col = ColorFromPalette(grad, (i+1)*255/h, 255, NOBLEND);
for (int j = 0; j<w; j++) { // character width for (int j = 0; j<w; j++) { // character width
int16_t x0 = x + (w-1) - j; int x0, y0;
if ((x0 >= 0 || x0 < cols) && ((bits>>(j+(8-w))) & 0x01)) { // bit set & drawing on-screen switch (rotate) {
case -1: x0 = x + (h-1) - i; y0 = y + (w-1) - j; break; // -90 deg
case -2:
case 2: x0 = x + j; y0 = y + (h-1) - i; break; // 180 deg
case 1: x0 = x + i; y0 = y + j; break; // +90 deg
default: x0 = x + (w-1) - j; y0 = y + i; break; // no rotation
}
if (x0 < 0 || x0 >= cols || y0 < 0 || y0 >= rows) continue; // drawing off-screen
if (((bits>>(j+(8-w))) & 0x01)) { // bit set
setPixelColorXY(x0, y0, col); setPixelColorXY(x0, y0, col);
} }
} }
@ -594,6 +613,7 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
#define WU_WEIGHT(a,b) ((uint8_t) (((a)*(b)+(a)+(b))>>8)) #define WU_WEIGHT(a,b) ((uint8_t) (((a)*(b)+(a)+(b))>>8))
void Segment::wu_pixel(uint32_t x, uint32_t y, CRGB c) { //awesome wu_pixel procedure by reddit u/sutaburosu void Segment::wu_pixel(uint32_t x, uint32_t y, CRGB c) { //awesome wu_pixel procedure by reddit u/sutaburosu
if (!isActive()) return; // not active
// extract the fractional parts and derive their inverses // extract the fractional parts and derive their inverses
uint8_t xx = x & 0xff, yy = y & 0xff, ix = 255 - xx, iy = 255 - yy; uint8_t xx = x & 0xff, yy = y & 0xff, ix = 255 - xx, iy = 255 - yy;
// calculate the intensities for each affected pixel // calculate the intensities for each affected pixel

View File

@ -74,30 +74,36 @@
// Segment class implementation // Segment class implementation
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
uint16_t Segment::_usedSegmentData = 0U; // amount of RAM all segments use for their data[] uint16_t Segment::_usedSegmentData = 0U; // amount of RAM all segments use for their data[]
CRGB *Segment::_globalLeds = nullptr;
uint16_t Segment::maxWidth = DEFAULT_LED_COUNT; uint16_t Segment::maxWidth = DEFAULT_LED_COUNT;
uint16_t Segment::maxHeight = 1; uint16_t Segment::maxHeight = 1;
CRGBPalette16 Segment::_randomPalette = CRGBPalette16(DEFAULT_COLOR);
CRGBPalette16 Segment::_newRandomPalette = CRGBPalette16(DEFAULT_COLOR);
unsigned long Segment::_lastPaletteChange = 0; // perhaps it should be per segment
#ifndef WLED_DISABLE_MODE_BLEND
bool Segment::_modeBlend = false;
#endif
// copy constructor // copy constructor
Segment::Segment(const Segment &orig) { Segment::Segment(const Segment &orig) {
//DEBUG_PRINTLN(F("-- Copy segment constructor --")); //DEBUG_PRINTF("-- Copy segment constructor: %p -> %p\n", &orig, this);
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
transitional = false; // copied segment cannot be in transition
name = nullptr; name = nullptr;
data = nullptr; data = nullptr;
_dataLen = 0; _dataLen = 0;
_t = nullptr; _t = nullptr;
if (leds && !Segment::_globalLeds) leds = nullptr;
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); } //if (orig._t) { _t = new Transition(orig._t->_dur); }
if (orig.leds && !Segment::_globalLeds && length() > 0) { leds = (CRGB*)malloc(sizeof(CRGB)*length()); if (leds) memcpy(leds, orig.leds, sizeof(CRGB)*length()); }
} }
// move constructor // move constructor
Segment::Segment(Segment &&orig) noexcept { Segment::Segment(Segment &&orig) noexcept {
//DEBUG_PRINTLN(F("-- Move segment constructor --")); //DEBUG_PRINTF("-- Move segment constructor: %p -> %p\n", &orig, this);
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.leds = nullptr; orig.transitional = false; // old segment cannot be in transition any more
orig.name = nullptr; orig.name = nullptr;
orig.data = nullptr; orig.data = nullptr;
orig._dataLen = 0; orig._dataLen = 0;
@ -106,71 +112,93 @@ Segment::Segment(Segment &&orig) noexcept {
// copy assignment // copy assignment
Segment& Segment::operator= (const Segment &orig) { Segment& Segment::operator= (const Segment &orig) {
//DEBUG_PRINTLN(F("-- Copying segment --")); //DEBUG_PRINTF("-- Copying segment: %p -> %p\n", &orig, this);
if (this != &orig) { if (this != &orig) {
// clean destination // clean destination
transitional = false; // copied segment cannot be in transition
if (name) delete[] name; if (name) delete[] name;
if (_t) delete _t; if (_t) {
if (leds && !Segment::_globalLeds) {free(leds); leds=nullptr;} #ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT) free(_t->_segT._dataT);
#endif
delete _t;
}
deallocateData(); deallocateData();
// copy source // copy source
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
transitional = false;
// erase pointers to allocated data // erase pointers to allocated data
name = nullptr; name = nullptr;
data = nullptr; data = nullptr;
_dataLen = 0; _dataLen = 0;
_t = nullptr; _t = nullptr;
if (!Segment::_globalLeds) leds = nullptr;
// copy source data // copy source data
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); } //if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
if (orig.leds && !Segment::_globalLeds && length() > 0) { leds = (CRGB*)malloc(sizeof(CRGB)*length()); if (leds) memcpy(leds, orig.leds, sizeof(CRGB)*length()); }
} }
return *this; return *this;
} }
// move assignment // move assignment
Segment& Segment::operator= (Segment &&orig) noexcept { Segment& Segment::operator= (Segment &&orig) noexcept {
//DEBUG_PRINTLN(F("-- Moving segment --")); //DEBUG_PRINTF("-- Moving segment: %p -> %p\n", &orig, this);
if (this != &orig) { if (this != &orig) {
if (name) delete[] name; // free old name transitional = false; // just temporary
if (name) { delete[] name; name = nullptr; } // free old name
deallocateData(); // free old runtime data deallocateData(); // free old runtime data
if (_t) delete _t; if (_t) {
if (leds && !Segment::_globalLeds) {free(leds); leds=nullptr;} #ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT) free(_t->_segT._dataT);
#endif
delete _t;
_t = nullptr;
}
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.transitional = false; // old segment cannot be in transition
orig.name = nullptr; orig.name = nullptr;
orig.data = nullptr; orig.data = nullptr;
orig._dataLen = 0; orig._dataLen = 0;
orig._t = nullptr; orig._t = nullptr;
orig.leds = nullptr;
} }
return *this; return *this;
} }
bool Segment::allocateData(size_t len) { bool Segment::allocateData(size_t len) {
if (data && _dataLen == len) return true; //already allocated if (data && _dataLen == len) return true; //already allocated
//DEBUG_PRINTF("-- Allocating data (%d): %p\n", len, this);
deallocateData(); deallocateData();
if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) return false; //not enough memory if (len == 0) return(false); // nothing to do
if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) {
// not enough memory
DEBUG_PRINT(F("!!! Effect RAM depleted: "));
DEBUG_PRINTF("%d/%d !!!\n", len, Segment::getUsedSegmentData());
return false;
}
// do not use SPI RAM on ESP32 since it is slow // do not use SPI RAM on ESP32 since it is slow
//#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM)
//if (psramFound())
// data = (byte*) ps_malloc(len);
//else
//#endif
data = (byte*) malloc(len); data = (byte*) malloc(len);
if (!data) return false; //allocation failed if (!data) { DEBUG_PRINTLN(F("!!! Allocation failed. !!!")); return false; } //allocation failed
Segment::addUsedSegmentData(len); Segment::addUsedSegmentData(len);
//DEBUG_PRINTF("--- Allocated data (%p): %d/%d -> %p\n", this, len, Segment::getUsedSegmentData(), data);
_dataLen = len; _dataLen = len;
memset(data, 0, len); memset(data, 0, len);
return true; return true;
} }
void Segment::deallocateData() { void Segment::deallocateData() {
if (!data) return; if (!data) { _dataLen = 0; return; }
//DEBUG_PRINTF("--- Released data (%p): %d/%d -> %p\n", this, _dataLen, Segment::getUsedSegmentData(), data);
if ((Segment::getUsedSegmentData() > 0) && (_dataLen > 0)) { // check that we don't have a dangling / inconsistent data pointer
free(data); free(data);
} else {
DEBUG_PRINT(F("---- Released data "));
DEBUG_PRINTF("(%p): ", this);
DEBUG_PRINT(F("inconsistent UsedSegmentData "));
DEBUG_PRINTF("(%d/%d)", _dataLen, Segment::getUsedSegmentData());
DEBUG_PRINTLN(F(", cowardly refusing to free nothing."));
}
data = nullptr; data = nullptr;
Segment::addUsedSegmentData(-_dataLen); Segment::addUsedSegmentData(_dataLen <= Segment::getUsedSegmentData() ? -_dataLen : -Segment::getUsedSegmentData());
_dataLen = 0; _dataLen = 0;
} }
@ -182,38 +210,14 @@ void Segment::deallocateData() {
* may free that data buffer. * may free that data buffer.
*/ */
void Segment::resetIfRequired() { void Segment::resetIfRequired() {
if (reset) { if (!reset) return;
if (leds && !Segment::_globalLeds) { free(leds); leds = nullptr; } //DEBUG_PRINTF("-- Segment reset: %p\n", this);
//if (transitional && _t) { transitional = false; delete _t; _t = nullptr; }
deallocateData(); deallocateData();
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0; next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
reset = false; // setOption(SEG_OPTION_RESET, false); reset = false;
}
}
void Segment::setUpLeds() {
// deallocation happens in resetIfRequired() as it is called when segment changes or in destructor
if (Segment::_globalLeds)
#ifndef WLED_DISABLE_2D
leds = &Segment::_globalLeds[start + startY*Segment::maxWidth];
#else
leds = &Segment::_globalLeds[start];
#endif
else if (leds == nullptr && length() > 0) { //softhack007 quickfix - avoid malloc(0) which is undefined behaviour (should not happen, but i've seen it)
//#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
//if (psramFound())
// leds = (CRGB*)ps_malloc(sizeof(CRGB)*length()); // softhack007 disabled; putting leds into psram leads to horrible slowdown on WROVER boards
//else
//#endif
leds = (CRGB*)malloc(sizeof(CRGB)*length());
}
} }
CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) { CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
static unsigned long _lastPaletteChange = 0; // perhaps it should be per segment
static CRGBPalette16 randomPalette = CRGBPalette16(DEFAULT_COLOR);
static CRGBPalette16 prevRandomPalette = CRGBPalette16(CRGB(BLACK));
byte tcp[72];
if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0; 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;
//default palette. Differs depending on effect //default palette. Differs depending on effect
@ -234,27 +238,18 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
case 0: //default palette. Exceptions for specific effects above case 0: //default palette. Exceptions for specific effects above
targetPalette = PartyColors_p; break; targetPalette = PartyColors_p; break;
case 1: {//periodically replace palette with a random one. Transition palette change in 500ms case 1: {//periodically replace palette with a random one. Transition palette change in 500ms
uint32_t timeSinceLastChange = millis() - _lastPaletteChange; unsigned long timeSinceLastChange = millis() - _lastPaletteChange;
if (timeSinceLastChange > randomPaletteChangeTime * 1000U) { if (timeSinceLastChange > randomPaletteChangeTime * 1000U) {
prevRandomPalette = randomPalette; _randomPalette = _newRandomPalette;
randomPalette = CRGBPalette16( _newRandomPalette = CRGBPalette16(
CHSV(random8(), random8(160, 255), random8(128, 255)), CHSV(random8(), random8(160, 255), random8(128, 255)),
CHSV(random8(), random8(160, 255), random8(128, 255)), CHSV(random8(), random8(160, 255), random8(128, 255)),
CHSV(random8(), random8(160, 255), random8(128, 255)), CHSV(random8(), random8(160, 255), random8(128, 255)),
CHSV(random8(), random8(160, 255), random8(128, 255))); CHSV(random8(), random8(160, 255), random8(128, 255)));
_lastPaletteChange = millis(); _lastPaletteChange = millis();
timeSinceLastChange = 0; handleRandomPalette(); // do a 1st pass of blend
}
if (timeSinceLastChange <= 250) {
targetPalette = prevRandomPalette;
// there needs to be 255 palette blends (48) for full blend but that is too resource intensive
// so 128 is a compromise (we need to perform full blend of the two palettes as each segment can have random
// palette selected but only 2 static palettes are used)
size_t noOfBlends = ((128U * timeSinceLastChange) / 250U);
for (size_t i=0; i<noOfBlends; i++) nblendPaletteTowardPalette(targetPalette, randomPalette, 48);
} else {
targetPalette = randomPalette;
} }
targetPalette = _randomPalette;
break;} break;}
case 2: {//primary color only case 2: {//primary color only
CRGB prim = gamma32(colors[0]); CRGB prim = gamma32(colors[0]);
@ -296,6 +291,7 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
if (pal>245) { if (pal>245) {
targetPalette = strip.customPalettes[255-pal]; // we checked bounds above targetPalette = strip.customPalettes[255-pal]; // we checked bounds above
} else { } else {
byte tcp[72];
memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[pal-13])), 72); memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[pal-13])), 72);
targetPalette.loadDynamicGradientPalette(tcp); targetPalette.loadDynamicGradientPalette(tcp);
} }
@ -306,11 +302,8 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
void Segment::startTransition(uint16_t dur) { void Segment::startTransition(uint16_t dur) {
if (!dur) { if (!dur) {
transitional = false; if (_t) _t->_dur = dur; // this will stop transition in next handleTransisiton()
if (_t) { else transitional = false;
delete _t;
_t = nullptr;
}
return; return;
} }
if (transitional && _t) return; // already in transition no need to store anything if (transitional && _t) return; // already in transition no need to store anything
@ -319,48 +312,169 @@ void Segment::startTransition(uint16_t dur) {
_t = new Transition(dur); // no previous transition running _t = new Transition(dur); // no previous transition running
if (!_t) return; // failed to allocate data if (!_t) return; // failed to allocate data
//DEBUG_PRINTF("-- Started transition: %p\n", this);
CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette); CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette);
_t->_palT = _palT;
_t->_briT = on ? opacity : 0; _t->_briT = on ? opacity : 0;
_t->_cctT = cct; _t->_cctT = cct;
_t->_palT = _palT; #ifndef WLED_DISABLE_MODE_BLEND
_t->_modeP = mode; swapSegenv(_t->_segT);
_t->_modeT = mode;
_t->_segT._optionsT |= 0b0000000001000000; // mark old segment transitional
_t->_segT._dataLenT = 0;
_t->_segT._dataT = nullptr;
if (_dataLen > 0 && data) {
_t->_segT._dataT = (byte *)malloc(_dataLen);
if (_t->_segT._dataT) {
//DEBUG_PRINTF("-- Allocated duplicate data (%d): %p\n", _dataLen, _t->_segT._dataT);
memcpy(_t->_segT._dataT, data, _dataLen);
_t->_segT._dataLenT = _dataLen;
}
}
#else
for (size_t i=0; i<NUM_COLORS; i++) _t->_colorT[i] = colors[i]; for (size_t i=0; i<NUM_COLORS; i++) _t->_colorT[i] = colors[i];
#endif
transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true); transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true);
} }
void Segment::stopTransition() {
if (!transitional) return;
transitional = false; // finish transitioning segment
//DEBUG_PRINTF("-- Stopping transition: %p\n", this);
if (_t) {
#ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT && _t->_segT._dataLenT > 0) {
//DEBUG_PRINTF("-- Released duplicate data (%d): %p\n", _t->_segT._dataLenT, _t->_segT._dataT);
free(_t->_segT._dataT);
_t->_segT._dataT = nullptr;
_t->_segT._dataLenT = 0;
}
#endif
delete _t;
_t = nullptr;
}
}
void Segment::handleTransition() {
if (!transitional) return;
uint16_t _progress = progress();
if (_progress == 0xFFFFU) stopTransition();
}
// transition progression between 0-65535 // transition progression between 0-65535
uint16_t Segment::progress() { uint16_t Segment::progress() {
if (!transitional || !_t) return 0xFFFFU; if (transitional && _t) {
uint32_t timeNow = millis(); unsigned long timeNow = millis();
if (timeNow - _t->_start > _t->_dur || _t->_dur == 0) return 0xFFFFU; if (_t->_dur > 0 && timeNow - _t->_start < _t->_dur) return (timeNow - _t->_start) * 0xFFFFU / _t->_dur;
return (timeNow - _t->_start) * 0xFFFFU / _t->_dur;
} }
return 0xFFFFU;
}
#ifndef WLED_DISABLE_MODE_BLEND
void Segment::swapSegenv(tmpsegd_t &tmpSeg) {
//DEBUG_PRINTF("-- Saving temp seg: %p (%p)\n", this, tmpSeg);
tmpSeg._optionsT = options;
for (size_t i=0; i<NUM_COLORS; i++) tmpSeg._colorT[i] = colors[i];
tmpSeg._speedT = speed;
tmpSeg._intensityT = intensity;
tmpSeg._custom1T = custom1;
tmpSeg._custom2T = custom2;
tmpSeg._custom3T = custom3;
tmpSeg._check1T = check1;
tmpSeg._check2T = check2;
tmpSeg._check3T = check3;
tmpSeg._aux0T = aux0;
tmpSeg._aux1T = aux1;
tmpSeg._stepT = step;
tmpSeg._callT = call;
tmpSeg._dataT = data;
tmpSeg._dataLenT = _dataLen;
if (_t && &tmpSeg != &(_t->_segT)) {
// swap SEGENV with transitional data
options = _t->_segT._optionsT;
for (size_t i=0; i<NUM_COLORS; i++) colors[i] = _t->_segT._colorT[i];
speed = _t->_segT._speedT;
intensity = _t->_segT._intensityT;
custom1 = _t->_segT._custom1T;
custom2 = _t->_segT._custom2T;
custom3 = _t->_segT._custom3T;
check1 = _t->_segT._check1T;
check2 = _t->_segT._check2T;
check3 = _t->_segT._check3T;
aux0 = _t->_segT._aux0T;
aux1 = _t->_segT._aux1T;
step = _t->_segT._stepT;
call = _t->_segT._callT;
data = _t->_segT._dataT;
_dataLen = _t->_segT._dataLenT;
}
//DEBUG_PRINTF("-- temp seg data: %p (%d,%p)\n", this, _dataLen, data);
}
void Segment::restoreSegenv(tmpsegd_t &tmpSeg) {
//DEBUG_PRINTF("-- Restoring temp seg: %p (%p)\n", this, tmpSeg);
if (_t && &(_t->_segT) != &tmpSeg) {
// update possibly changed variables to keep old effect running correctly
_t->_segT._aux0T = aux0;
_t->_segT._aux1T = aux1;
_t->_segT._stepT = step;
_t->_segT._callT = call;
//if (_t->_segT._dataT != data) DEBUG_PRINTF("--- data re-allocated: (%p) %p -> %p\n", this, _t->_segT._dataT, data);
_t->_segT._dataT = data; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!)
_t->_segT._dataLenT = _dataLen; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!)
}
options = tmpSeg._optionsT;
for (size_t i=0; i<NUM_COLORS; i++) colors[i] = tmpSeg._colorT[i];
speed = tmpSeg._speedT;
intensity = tmpSeg._intensityT;
custom1 = tmpSeg._custom1T;
custom2 = tmpSeg._custom2T;
custom3 = tmpSeg._custom3T;
check1 = tmpSeg._check1T;
check2 = tmpSeg._check2T;
check3 = tmpSeg._check3T;
aux0 = tmpSeg._aux0T;
aux1 = tmpSeg._aux1T;
step = tmpSeg._stepT;
call = tmpSeg._callT;
data = tmpSeg._dataT;
_dataLen = tmpSeg._dataLenT;
//DEBUG_PRINTF("-- temp seg data: %p (%d,%p)\n", this, _dataLen, data);
}
#endif
uint8_t Segment::currentBri(uint8_t briNew, bool useCct) { uint8_t Segment::currentBri(uint8_t briNew, bool useCct) {
uint32_t prog = progress(); uint32_t prog = progress();
if (transitional && _t && prog < 0xFFFFU) { if (prog < 0xFFFFU) {
if (useCct) return ((briNew * prog) + _t->_cctT * (0xFFFFU - prog)) >> 16; if (useCct) return ((briNew * prog) + _t->_cctT * (0xFFFFU - prog)) >> 16;
else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16; else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16;
} else {
return briNew;
} }
return briNew;
} }
uint8_t Segment::currentMode(uint8_t newMode) { uint8_t Segment::currentMode(uint8_t newMode) {
return (progress()>32767U) ? newMode : _t->_modeP; // change effect in the middle of transition #ifndef WLED_DISABLE_MODE_BLEND
uint16_t prog = progress(); // implicit check for transitional & _t in progress()
if (prog < 0xFFFFU) return _t->_modeT;
#endif
return newMode;
} }
uint32_t Segment::currentColor(uint8_t slot, uint32_t colorNew) { uint32_t Segment::currentColor(uint8_t slot, uint32_t colorNew) {
#ifndef WLED_DISABLE_MODE_BLEND
return transitional && _t ? color_blend(_t->_segT._colorT[slot], colorNew, progress(), true) : colorNew;
#else
return transitional && _t ? color_blend(_t->_colorT[slot], colorNew, progress(), true) : colorNew; return transitional && _t ? color_blend(_t->_colorT[slot], colorNew, progress(), true) : colorNew;
#endif
} }
CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) { CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
loadPalette(targetPalette, pal); loadPalette(targetPalette, pal);
if (transitional && _t && progress() < 0xFFFFU) { if (progress() < 0xFFFFU) {
// blend palettes // blend palettes
// there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time) // 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 // minimum blend time is 100ms maximum is 65535ms
uint32_t timeMS = millis() - _t->_start; unsigned long timeMS = millis() - _t->_start;
uint16_t noOfBlends = (255U * timeMS / _t->_dur) - _t->_prevPaletteBlends; uint16_t noOfBlends = (255U * timeMS / _t->_dur) - _t->_prevPaletteBlends;
for (int i=0; i<noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, targetPalette, 48); for (int i=0; i<noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, targetPalette, 48);
targetPalette = _t->_palT; // copy transitioning/temporary palette targetPalette = _t->_palT; // copy transitioning/temporary palette
@ -368,20 +482,15 @@ CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal
return targetPalette; return targetPalette;
} }
void Segment::handleTransition() { // relies on WS2812FX::service() to call it max every 8ms or more (MIN_SHOW_DELAY)
if (!transitional) return; void Segment::handleRandomPalette() {
uint16_t _progress = progress(); // just do a blend; if the palettes are identical it will just compare 48 bytes (same as _randomPalette == _newRandomPalette)
if (_progress == 0xFFFFU) transitional = false; // finish transitioning segment // this will slowly blend _newRandomPalette into _randomPalette every 15ms or 8ms (depending on MIN_SHOW_DELAY)
if (_t) { // thanks to @nXm AKA https://github.com/NMeirer nblendPaletteTowardPalette(_randomPalette, _newRandomPalette, 48);
if (_progress >= 32767U && _t->_modeP != mode) markForReset();
if (_progress == 0xFFFFU) {
delete _t;
_t = nullptr;
}
}
} }
void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y) { // segId is given when called from network callback, changes are queued if that segment is currently in its effect function
void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y, uint8_t segId) {
// return if neither bounds nor grouping have changed // return if neither bounds nor grouping have changed
bool boundsUnchanged = (start == i1 && stop == i2); bool boundsUnchanged = (start == i1 && stop == i2);
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
@ -391,10 +500,22 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t
&& (!grp || (grouping == grp && spacing == spc)) && (!grp || (grouping == grp && spacing == spc))
&& (ofs == UINT16_MAX || ofs == offset)) return; && (ofs == UINT16_MAX || ofs == offset)) return;
if (stop) fill(BLACK); //turn old segment range off if (stop) fill(BLACK); // turn old segment range off (clears pixels if changing spacing)
if (grp) { // prevent assignment of 0
grouping = grp;
spacing = spc;
} else {
grouping = 1;
spacing = 0;
}
if (ofs < UINT16_MAX) offset = ofs;
markForReset();
if (boundsUnchanged) return;
// apply change immediately
if (i2 <= i1) { //disable segment if (i2 <= i1) { //disable segment
stop = 0; stop = 0;
markForReset();
return; return;
} }
if (i1 < Segment::maxWidth || (i1 >= Segment::maxWidth*Segment::maxHeight && i1 < strip.getLengthTotal())) start = i1; // Segment::maxWidth equals strip.getLengthTotal() for 1D if (i1 < Segment::maxWidth || (i1 >= Segment::maxWidth*Segment::maxHeight && i1 < strip.getLengthTotal())) start = i1; // Segment::maxWidth equals strip.getLengthTotal() for 1D
@ -407,13 +528,12 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t
stopY = i2Y > Segment::maxHeight ? Segment::maxHeight : MAX(1,i2Y); stopY = i2Y > Segment::maxHeight ? Segment::maxHeight : MAX(1,i2Y);
} }
#endif #endif
if (grp) { // safety check
grouping = grp; if (start >= stop || startY >= stopY) {
spacing = spc; stop = 0;
return;
} }
if (ofs < UINT16_MAX) offset = ofs; refreshLightCapabilities();
markForReset();
if (!boundsUnchanged) refreshLightCapabilities();
} }
@ -444,6 +564,7 @@ void Segment::setCCT(uint16_t k) {
void Segment::setOpacity(uint8_t o) { void Segment::setOpacity(uint8_t o) {
if (opacity == o) return; if (opacity == o) return;
if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change
DEBUG_PRINT(F("-- Setting opacity: ")); DEBUG_PRINTLN(o);
opacity = o; opacity = o;
stateChanged = true; // send UDP/WS broadcast stateChanged = true; // send UDP/WS broadcast
} }
@ -460,10 +581,8 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
// if we have a valid mode & is not reserved // if we have a valid mode & is not reserved
if (fx < strip.getModeCount() && strncmp_P("RSVD", strip.getModeData(fx), 4)) { if (fx < strip.getModeCount() && strncmp_P("RSVD", strip.getModeData(fx), 4)) {
if (fx != mode) { if (fx != mode) {
startTransition(strip.getTransition()); // set effect transitions if (fadeTransition) startTransition(strip.getTransition()); // set effect transitions
//markForReset(); // transition will handle this
mode = fx; mode = fx;
// load default values from effect string // load default values from effect string
if (loadDefaults) { if (loadDefaults) {
int16_t sOpt; int16_t sOpt;
@ -483,6 +602,7 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
sOpt = extractModeDefaults(fx, "mY"); if (sOpt >= 0) mirror_y = (bool)sOpt; // NOTE: setting this option is a risky business sOpt = extractModeDefaults(fx, "mY"); if (sOpt >= 0) mirror_y = (bool)sOpt; // NOTE: setting this option is a risky business
sOpt = extractModeDefaults(fx, "pal"); if (sOpt >= 0) setPalette(sOpt); //else setPalette(0); sOpt = extractModeDefaults(fx, "pal"); if (sOpt >= 0) setPalette(sOpt); //else setPalette(0);
} }
markForReset();
stateChanged = true; // send UDP/WS broadcast stateChanged = true; // send UDP/WS broadcast
} }
} }
@ -546,8 +666,7 @@ uint16_t Segment::virtualLength() const {
return vLen; return vLen;
} }
#endif #endif
uint16_t groupLen = groupLength(); uint16_t groupLen = groupLength(); // is always >= 1
if (groupLen < 1) groupLen = 1; // prevent division by zero - better safe than sorry ...
uint16_t vLength = (length() + groupLen - 1) / groupLen; uint16_t vLength = (length() + groupLen - 1) / groupLen;
if (mirror) vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED if (mirror) vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED
return vLength; return vLength;
@ -555,6 +674,7 @@ uint16_t Segment::virtualLength() const {
void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col) void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
{ {
if (!isActive()) return; // not active
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
int vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows) int vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows)
#endif #endif
@ -622,8 +742,6 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
} }
#endif #endif
if (leds) leds[i] = col;
uint16_t len = length(); uint16_t len = length();
uint8_t _bri_t = currentBri(on ? opacity : 0); uint8_t _bri_t = currentBri(on ? opacity : 0);
if (_bri_t < 255) { if (_bri_t < 255) {
@ -645,6 +763,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
} }
i += start; // starting pixel in a group i += start; // starting pixel in a group
uint32_t tmpCol = col;
// set all the pixels in the group // set all the pixels in the group
for (int j = 0; j < grouping; j++) { for (int j = 0; j < grouping; j++) {
uint16_t indexSet = i + ((reverse) ? -j : j); uint16_t indexSet = i + ((reverse) ? -j : j);
@ -653,11 +772,17 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
uint16_t indexMir = stop - indexSet + start - 1; uint16_t indexMir = stop - indexSet + start - 1;
indexMir += offset; // offset/phase indexMir += offset; // offset/phase
if (indexMir >= stop) indexMir -= len; // wrap if (indexMir >= stop) indexMir -= len; // wrap
strip.setPixelColor(indexMir, col); #ifndef WLED_DISABLE_MODE_BLEND
if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexMir), col, 0xFFFFU - progress(), true);
#endif
strip.setPixelColor(indexMir, tmpCol);
} }
indexSet += offset; // offset/phase indexSet += offset; // offset/phase
if (indexSet >= stop) indexSet -= len; // wrap if (indexSet >= stop) indexSet -= len; // wrap
strip.setPixelColor(indexSet, col); #ifndef WLED_DISABLE_MODE_BLEND
if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexSet), col, 0xFFFFU - progress(), true);
#endif
strip.setPixelColor(indexSet, tmpCol);
} }
} }
} }
@ -665,6 +790,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
// anti-aliased normalized version of setPixelColor() // anti-aliased normalized version of setPixelColor()
void Segment::setPixelColor(float i, uint32_t col, bool aa) void Segment::setPixelColor(float i, uint32_t col, bool aa)
{ {
if (!isActive()) return; // not active
int vStrip = int(i/10.0f); // hack to allow running on virtual strips (2D segment columns/rows) int vStrip = int(i/10.0f); // hack to allow running on virtual strips (2D segment columns/rows)
i -= int(i); i -= int(i);
@ -696,6 +822,7 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa)
uint32_t Segment::getPixelColor(int i) uint32_t Segment::getPixelColor(int i)
{ {
if (!isActive()) return 0; // not active
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
int vStrip = i>>16; int vStrip = i>>16;
#endif #endif
@ -723,8 +850,6 @@ uint32_t Segment::getPixelColor(int i)
} }
#endif #endif
if (leds) return RGBW32(leds[i].r, leds[i].g, leds[i].b, 0);
if (reverse) i = virtualLength() - i - 1; if (reverse) i = virtualLength() - i - 1;
i *= groupLength(); i *= groupLength();
i += start; i += start;
@ -765,6 +890,11 @@ void Segment::refreshLightCapabilities() {
uint16_t segStartIdx = 0xFFFFU; uint16_t segStartIdx = 0xFFFFU;
uint16_t segStopIdx = 0; uint16_t segStopIdx = 0;
if (!isActive()) {
_capabilities = 0;
return;
}
if (start < Segment::maxWidth * Segment::maxHeight) { if (start < Segment::maxWidth * Segment::maxHeight) {
// we are withing 2D matrix (includes 1D segments) // we are withing 2D matrix (includes 1D segments)
for (int y = startY; y < stopY; y++) for (int x = start; x < stop; x++) { for (int y = startY; y < stopY; y++) for (int x = start; x < stop; x++) {
@ -809,6 +939,7 @@ void Segment::refreshLightCapabilities() {
* Fills segment with color * Fills segment with color
*/ */
void Segment::fill(uint32_t c) { void Segment::fill(uint32_t c) {
if (!isActive()) return; // not active
const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
const uint16_t rows = virtualHeight(); // will be 1 for 1D const uint16_t rows = virtualHeight(); // will be 1 for 1D
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
@ -824,6 +955,7 @@ void Segment::blendPixelColor(int n, uint32_t color, uint8_t blend) {
// Adds the specified color with the existing pixel color perserving color balance. // Adds the specified color with the existing pixel color perserving color balance.
void Segment::addPixelColor(int n, uint32_t color, bool fast) { void Segment::addPixelColor(int n, uint32_t color, bool fast) {
if (!isActive()) return; // not active
uint32_t col = getPixelColor(n); uint32_t col = getPixelColor(n);
uint8_t r = R(col); uint8_t r = R(col);
uint8_t g = G(col); uint8_t g = G(col);
@ -842,6 +974,7 @@ void Segment::addPixelColor(int n, uint32_t color, bool fast) {
} }
void Segment::fadePixelColor(uint16_t n, uint8_t fade) { void Segment::fadePixelColor(uint16_t n, uint8_t fade) {
if (!isActive()) return; // not active
CRGB pix = CRGB(getPixelColor(n)).nscale8_video(fade); CRGB pix = CRGB(getPixelColor(n)).nscale8_video(fade);
setPixelColor(n, pix); setPixelColor(n, pix);
} }
@ -850,11 +983,12 @@ void Segment::fadePixelColor(uint16_t n, uint8_t fade) {
* fade out function, higher rate = quicker fade * fade out function, higher rate = quicker fade
*/ */
void Segment::fade_out(uint8_t rate) { void Segment::fade_out(uint8_t rate) {
if (!isActive()) return; // not active
const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
const uint16_t rows = virtualHeight(); // will be 1 for 1D const uint16_t rows = virtualHeight(); // will be 1 for 1D
rate = (255-rate) >> 1; rate = (255-rate) >> 1;
float mappedRate = float(rate) +1.1; float mappedRate = float(rate) +1.1f;
uint32_t color = colors[1]; // SEGCOLOR(1); // target color uint32_t color = colors[1]; // SEGCOLOR(1); // target color
int w2 = W(color); int w2 = W(color);
@ -887,7 +1021,7 @@ void Segment::fade_out(uint8_t rate) {
// fades all pixels to black using nscale8() // fades all pixels to black using nscale8()
void Segment::fadeToBlackBy(uint8_t fadeBy) { void Segment::fadeToBlackBy(uint8_t fadeBy) {
if (fadeBy == 0) return; // optimization - no scaling to apply if (!isActive() || fadeBy == 0) return; // optimization - no scaling to apply
const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
const uint16_t rows = virtualHeight(); // will be 1 for 1D const uint16_t rows = virtualHeight(); // will be 1 for 1D
@ -902,7 +1036,7 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) {
*/ */
void Segment::blur(uint8_t blur_amount) void Segment::blur(uint8_t blur_amount)
{ {
if (blur_amount == 0) return; // optimization: 0 means "don't blur" if (!isActive() || blur_amount == 0) return; // optimization: 0 means "don't blur"
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
if (is2D()) { if (is2D()) {
// compatibility with 2D // compatibility with 2D
@ -1068,24 +1202,6 @@ void WS2812FX::finalizeInit(void)
Segment::maxHeight = 1; Segment::maxHeight = 1;
} }
//initialize leds array. TBD: realloc if nr of leds change
if (Segment::_globalLeds) {
purgeSegments(true);
free(Segment::_globalLeds);
Segment::_globalLeds = nullptr;
}
if (useLedsArray) {
size_t arrSize = sizeof(CRGB) * getLengthTotal();
// softhack007 disabled; putting leds into psram leads to horrible slowdown on WROVER boards (see setUpLeds())
//#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
//if (psramFound())
// Segment::_globalLeds = (CRGB*) ps_malloc(arrSize);
//else
//#endif
Segment::_globalLeds = (CRGB*) malloc(arrSize);
if (Segment::_globalLeds && (arrSize > 0)) memset(Segment::_globalLeds, 0, arrSize);
}
//segments are created in makeAutoSegments(); //segments are created in makeAutoSegments();
DEBUG_PRINTLN(F("Loading custom palettes")); DEBUG_PRINTLN(F("Loading custom palettes"));
loadCustomPalettes(); // (re)load all custom palettes loadCustomPalettes(); // (re)load all custom palettes
@ -1094,13 +1210,14 @@ void WS2812FX::finalizeInit(void)
} }
void WS2812FX::service() { void WS2812FX::service() {
uint32_t nowUp = millis(); // Be aware, millis() rolls over every 49 days unsigned long nowUp = millis(); // Be aware, millis() rolls over every 49 days
now = nowUp + timebase; now = nowUp + timebase;
if (nowUp - _lastShow < MIN_SHOW_DELAY) return; if (nowUp - _lastShow < MIN_SHOW_DELAY) return;
bool doShow = false; bool doShow = false;
_isServicing = true; _isServicing = true;
_segment_index = 0; _segment_index = 0;
Segment::handleRandomPalette(); // move it into for loop when each segment has individual random palette
for (segment &seg : _segments) { for (segment &seg : _segments) {
// process transition (mode changes in the middle of transition) // process transition (mode changes in the middle of transition)
seg.handleTransition(); seg.handleTransition();
@ -1112,7 +1229,6 @@ void WS2812FX::service() {
// last condition ensures all solid segments are updated at the same time // last condition ensures all solid segments are updated at the same time
if (nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC)) if (nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC))
{ {
if (seg.grouping == 0) seg.grouping = 1; //sanity check
doShow = true; doShow = true;
uint16_t delay = FRAMETIME; uint16_t delay = FRAMETIME;
@ -1126,26 +1242,49 @@ void WS2812FX::service() {
if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(seg.cct, true), correctWB); 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]); for (uint8_t c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]);
// effect blending (execute previous effect) // Effect blending
// actual code may be a bit more involved as effects have runtime data including allocated memory // When two effects are being blended, each may have different segment data, this
//if (seg.transitional && seg._modeP) (*_mode[seg._modeP])(progress()); // data needs to be saved first and then restored before running previous/transitional mode.
delay = (*_mode[seg.currentMode(seg.mode)])(); // The blending will largely depend on the effect behaviour since actual output (LEDs) may be
// overwritten by later effect. To enable seamless blending for every effect, additional LED buffer
// would need to be allocated for each effect and then blended together for each pixel.
[[maybe_unused]] uint8_t tmpMode = seg.currentMode(seg.mode); // this will return old mode while in transition
delay = (*_mode[seg.mode])(); // run new/current mode
#ifndef WLED_DISABLE_MODE_BLEND
if (seg.mode != tmpMode) {
Segment::tmpsegd_t _tmpSegData;
Segment::modeBlend(true); // set semaphore
seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state)
uint16_t d2 = (*_mode[tmpMode])(); // run old mode
seg.restoreSegenv(_tmpSegData); // restore mode state (will also update transitional state)
delay = MIN(delay,d2); // use shortest delay
Segment::modeBlend(false); // unset semaphore
}
#endif
if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++; if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++;
if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
} }
seg.next_time = nowUp + delay; seg.next_time = nowUp + delay;
} }
if (_segment_index == _queuedChangesSegId) setUpSegmentFromQueuedChanges();
_segment_index++; _segment_index++;
} }
_virtualSegmentLength = 0; _virtualSegmentLength = 0;
busses.setSegmentCCT(-1); busses.setSegmentCCT(-1);
_isServicing = false;
_triggered = false;
#ifdef WLED_DEBUG
if (millis() - nowUp > _frametime) DEBUG_PRINTLN(F("Slow effects."));
#endif
if (doShow) { if (doShow) {
yield(); yield();
show(); show();
} }
_triggered = false; #ifdef WLED_DEBUG
_isServicing = false; if (millis() - nowUp > _frametime) DEBUG_PRINTLN(F("Slow strip."));
#endif
} }
void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col) void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col)
@ -1174,7 +1313,7 @@ uint32_t WS2812FX::getPixelColor(uint16_t i)
#define MA_FOR_ESP 100 //how much mA does the ESP use (Wemos D1 about 80mA, ESP32 about 120mA) #define MA_FOR_ESP 100 //how much mA does the ESP use (Wemos D1 about 80mA, ESP32 about 120mA)
//you can set it to 0 if the ESP is powered by USB and the LEDs by external //you can set it to 0 if the ESP is powered by USB and the LEDs by external
void WS2812FX::estimateCurrentAndLimitBri() { uint8_t WS2812FX::estimateCurrentAndLimitBri() {
//power limit calculation //power limit calculation
//each LED can draw up 195075 "power units" (approx. 53mA) //each LED can draw up 195075 "power units" (approx. 53mA)
//one PU is the power it takes to have 1 channel 1 step brighter per brightness step //one PU is the power it takes to have 1 channel 1 step brighter per brightness step
@ -1182,35 +1321,28 @@ void WS2812FX::estimateCurrentAndLimitBri() {
bool useWackyWS2815PowerModel = false; bool useWackyWS2815PowerModel = false;
byte actualMilliampsPerLed = milliampsPerLed; byte actualMilliampsPerLed = milliampsPerLed;
if (ablMilliampsMax < 150 || actualMilliampsPerLed == 0) { //0 mA per LED and too low numbers turn off calculation
currentMilliamps = 0;
return _brightness;
}
if (milliampsPerLed == 255) { if (milliampsPerLed == 255) {
useWackyWS2815PowerModel = true; useWackyWS2815PowerModel = true;
actualMilliampsPerLed = 12; // from testing an actual strip actualMilliampsPerLed = 12; // from testing an actual strip
} }
if (ablMilliampsMax < 150 || actualMilliampsPerLed == 0) { //0 mA per LED and too low numbers turn off calculation size_t powerBudget = (ablMilliampsMax - MA_FOR_ESP); //100mA for ESP power
currentMilliamps = 0;
busses.setBrightness(_brightness);
return;
}
uint16_t pLen = getLengthPhysical();
uint32_t puPerMilliamp = 195075 / actualMilliampsPerLed;
uint32_t powerBudget = (ablMilliampsMax - MA_FOR_ESP) * puPerMilliamp; //100mA for ESP power
if (powerBudget > puPerMilliamp * pLen) { //each LED uses about 1mA in standby, exclude that from power budget
powerBudget -= puPerMilliamp * pLen;
} else {
powerBudget = 0;
}
uint32_t powerSum = 0;
size_t pLen = 0; //getLengthPhysical();
size_t powerSum = 0;
for (uint_fast8_t bNum = 0; bNum < busses.getNumBusses(); bNum++) { for (uint_fast8_t bNum = 0; bNum < busses.getNumBusses(); bNum++) {
Bus *bus = busses.getBus(bNum); Bus *bus = busses.getBus(bNum);
if (bus->getType() >= TYPE_NET_DDP_RGB) continue; //exclude non-physical network busses if (!IS_DIGITAL(bus->getType())) continue; //exclude non-digital network busses
uint16_t len = bus->getLength(); uint16_t len = bus->getLength();
pLen += len;
uint32_t busPowerSum = 0; uint32_t busPowerSum = 0;
for (uint_fast16_t i = 0; i < len; i++) { //sum up the usage of each LED for (uint_fast16_t i = 0; i < len; i++) { //sum up the usage of each LED
uint32_t c = bus->getPixelColor(i); uint32_t c = bus->getPixelColor(i); // always returns original or restored color without brightness scaling
byte r = R(c), g = G(c), b = B(c), w = W(c); byte r = R(c), g = G(c), b = B(c), w = W(c);
if(useWackyWS2815PowerModel) { //ignore white component on WS2815 power calculation if(useWackyWS2815PowerModel) { //ignore white component on WS2815 power calculation
@ -1222,51 +1354,56 @@ void WS2812FX::estimateCurrentAndLimitBri() {
if (bus->hasWhite()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less if (bus->hasWhite()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
busPowerSum *= 3; busPowerSum *= 3;
busPowerSum = busPowerSum >> 2; //same as /= 4 busPowerSum >>= 2; //same as /= 4
} }
powerSum += busPowerSum; powerSum += busPowerSum;
} }
uint32_t powerSum0 = powerSum; if (powerBudget > pLen) { //each LED uses about 1mA in standby, exclude that from power budget
//powerSum *= _brightness; // for NPBrightnessBus powerBudget -= pLen;
powerSum *= 255; // no need to scale down powerSum - NPB-LG getPixelColor returns colors scaled down by brightness } else {
powerBudget = 0;
}
if (powerSum > powerBudget) //scale brightness down to stay in current limit // powerSum has all the values of channels summed (max would be pLen*765 as white is excluded) so convert to milliAmps
{ powerSum = (powerSum * actualMilliampsPerLed) / 765;
float scale = (float)powerBudget / (float)powerSum;
uint8_t newBri = _brightness;
if (powerSum * _brightness / 255 > powerBudget) { //scale brightness down to stay in current limit
float scale = (float)(powerBudget * 255) / (float)(powerSum * _brightness);
uint16_t scaleI = scale * 255; uint16_t scaleI = scale * 255;
uint8_t scaleB = (scaleI > 255) ? 255 : scaleI; uint8_t scaleB = (scaleI > 255) ? 255 : scaleI;
uint8_t newBri = scale8(_brightness, scaleB); newBri = scale8(_brightness, scaleB) + 1;
// to keep brightness uniform, sets virtual busses too - softhack007: apply reductions immediately
if (scaleB < 255) busses.setBrightness(scaleB, true); // NPB-LG has already applied brightness, so its suffifient to post-apply scaling ==> use scaleB instead of newBri
busses.setBrightness(newBri, false); // set new brightness for next frame
//currentMilliamps = (powerSum0 * newBri) / puPerMilliamp; // for NPBrightnessBus
currentMilliamps = (powerSum0 * scaleB) / puPerMilliamp; // for NPBus-LG
} else {
currentMilliamps = powerSum / puPerMilliamp;
busses.setBrightness(_brightness, false); // set new brightness for next frame
} }
currentMilliamps = (powerSum * newBri) / 255;
currentMilliamps += MA_FOR_ESP; //add power of ESP back to estimate currentMilliamps += MA_FOR_ESP; //add power of ESP back to estimate
currentMilliamps += pLen; //add standby power back to estimate currentMilliamps += pLen; //add standby power (1mA/LED) back to estimate
return newBri;
} }
void WS2812FX::show(void) { void WS2812FX::show(void) {
// avoid race condition, caputre _callback value // avoid race condition, caputre _callback value
show_callback callback = _callback; show_callback callback = _callback;
if (callback) callback(); if (callback) callback();
estimateCurrentAndLimitBri(); uint8_t newBri = estimateCurrentAndLimitBri();
busses.setBrightness(newBri); // "repaints" all pixels if brightness changed
// some buses send asynchronously and this method will return before // some buses send asynchronously and this method will return before
// all of the data has been sent. // all of the data has been sent.
// See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods // See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
busses.show(); busses.show();
// restore bus brightness to its original value
// this is done right after show, so this is only OK if LED updates are completed before show() returns
// or async show has a separate buffer (ESP32 RMT and I2S are ok)
if (newBri < _brightness) busses.setBrightness(_brightness);
unsigned long now = millis(); unsigned long now = millis();
unsigned long diff = now - _lastShow; size_t diff = now - _lastShow;
uint16_t fpsCurr = 200; size_t fpsCurr = 200;
if (diff > 0) fpsCurr = 1000 / diff; if (diff > 0) fpsCurr = 1000 / diff;
_cumulativeFps = (3 * _cumulativeFps + fpsCurr) >> 2; _cumulativeFps = (3 * _cumulativeFps + fpsCurr +2) >> 2; // "+2" for proper rounding (2/4 = 0.5)
_lastShow = now; _lastShow = now;
} }
@ -1298,9 +1435,7 @@ void WS2812FX::setMode(uint8_t segid, uint8_t m) {
if (m >= getModeCount()) m = getModeCount() - 1; if (m >= getModeCount()) m = getModeCount() - 1;
if (_segments[segid].mode != m) { if (_segments[segid].mode != m) {
_segments[segid].startTransition(_transitionDur); // set effect transitions _segments[segid].setMode(m); // do not load defaults
//_segments[segid].markForReset();
_segments[segid].mode = m;
} }
} }
@ -1323,6 +1458,8 @@ void WS2812FX::setCCT(uint16_t k) {
} }
} }
// direct=true either expects the caller to call show() themselves (realtime modes) or be ok waiting for the next frame for the change to apply
// direct=false immediately triggers an effect redraw
void WS2812FX::setBrightness(uint8_t b, bool direct) { void WS2812FX::setBrightness(uint8_t b, bool direct) {
if (gammaCorrectBri) b = gamma8(b); if (gammaCorrectBri) b = gamma8(b);
if (_brightness == b) return; if (_brightness == b) return;
@ -1332,12 +1469,12 @@ void WS2812FX::setBrightness(uint8_t b, bool direct) {
seg.freeze = false; seg.freeze = false;
} }
} }
if (direct) { // setting brightness with NeoPixelBusLg has no effect on already painted pixels,
// would be dangerous if applied immediately (could exceed ABL), but will not output until the next show() // so we need to force an update to existing buffer
busses.setBrightness(b); busses.setBrightness(b);
} else { if (!direct) {
unsigned long t = millis(); unsigned long t = millis();
if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) show(); //apply brightness change immediately if no refresh soon if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) trigger(); //apply brightness change immediately if no refresh soon
} }
} }
@ -1444,9 +1581,32 @@ Segment& WS2812FX::getSegment(uint8_t id) {
return _segments[id >= _segments.size() ? getMainSegmentId() : id]; // vectors return _segments[id >= _segments.size() ? getMainSegmentId() : id]; // vectors
} }
void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing, uint16_t offset, uint16_t startY, uint16_t stopY) { // sets new segment bounds, queues if that segment is currently running
if (n >= _segments.size()) return; void WS2812FX::setSegment(uint8_t segId, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing, uint16_t offset, uint16_t startY, uint16_t stopY) {
_segments[n].setUp(i1, i2, grouping, spacing, offset, startY, stopY); if (segId >= getSegmentsNum()) {
if (i2 <= i1) return; // do not append empty/inactive segments
appendSegment(Segment(0, strip.getLengthTotal()));
segId = getSegmentsNum()-1; // segments are added at the end of list
}
if (_queuedChangesSegId == segId) _queuedChangesSegId = 255; // cancel queued change if already queued for this segment
if (segId < getMaxSegments() && segId == getCurrSegmentId() && isServicing()) { // queue change to prevent concurrent access
// queuing a change for a second segment will lead to the loss of the first change if not yet applied
// however this is not a problem as the queued change is applied immediately after the effect function in that segment returns
_qStart = i1; _qStop = i2; _qStartY = startY; _qStopY = stopY;
_qGrouping = grouping; _qSpacing = spacing; _qOffset = offset;
_queuedChangesSegId = segId;
return; // queued changes are applied immediately after effect function returns
}
_segments[segId].setUp(i1, i2, grouping, spacing, offset, startY, stopY);
}
void WS2812FX::setUpSegmentFromQueuedChanges() {
if (_queuedChangesSegId >= getSegmentsNum()) return;
getSegment(_queuedChangesSegId).setUp(_qStart, _qStop, _qGrouping, _qSpacing, _qOffset, _qStartY, _qStopY);
_queuedChangesSegId = 255;
} }
void WS2812FX::restartRuntime() { void WS2812FX::restartRuntime() {
@ -1614,7 +1774,7 @@ void WS2812FX::printSize() {
DEBUG_PRINTF("Data: %d*%d=%uB\n", sizeof(const char *), _modeData.size(), (_modeData.capacity()*sizeof(const char *))); DEBUG_PRINTF("Data: %d*%d=%uB\n", sizeof(const char *), _modeData.size(), (_modeData.capacity()*sizeof(const char *)));
DEBUG_PRINTF("Map: %d*%d=%uB\n", sizeof(uint16_t), (int)customMappingSize, customMappingSize*sizeof(uint16_t)); DEBUG_PRINTF("Map: %d*%d=%uB\n", sizeof(uint16_t), (int)customMappingSize, customMappingSize*sizeof(uint16_t));
size = getLengthTotal(); size = getLengthTotal();
if (useLedsArray) DEBUG_PRINTF("Buffer: %d*%u=%uB\n", sizeof(CRGB), size, size*sizeof(CRGB)); if (useGlobalLedBuffer) DEBUG_PRINTF("Buffer: %d*%u=%uB\n", sizeof(CRGB), size, size*sizeof(CRGB));
} }
#endif #endif

View File

@ -91,71 +91,132 @@ uint32_t Bus::autoWhiteCalc(uint32_t c) {
return RGBW32(r, g, b, w); return RGBW32(r, g, b, w);
} }
uint8_t *Bus::allocData(size_t size) {
if (_data) free(_data); // should not happen, but for safety
return _data = (uint8_t *)(size>0 ? calloc(size, sizeof(uint8_t)) : nullptr);
}
BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) : Bus(bc.type, bc.start, bc.autoWhite), _colorOrderMap(com) {
BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com)
: Bus(bc.type, bc.start, bc.autoWhite, bc.count, bc.reversed, (bc.refreshReq || bc.type == TYPE_TM1814))
, _skip(bc.skipAmount) //sacrificial pixels
, _colorOrder(bc.colorOrder)
, _colorOrderMap(com)
{
if (!IS_DIGITAL(bc.type) || !bc.count) return; if (!IS_DIGITAL(bc.type) || !bc.count) return;
if (!pinManager.allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return; if (!pinManager.allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return;
_frequencykHz = 0U; _frequencykHz = 0U;
_pins[0] = bc.pins[0]; _pins[0] = bc.pins[0];
if (IS_2PIN(bc.type)) { if (IS_2PIN(bc.type)) {
if (!pinManager.allocatePin(bc.pins[1], true, PinOwner::BusDigital)) { if (!pinManager.allocatePin(bc.pins[1], true, PinOwner::BusDigital)) {
cleanup(); return; cleanup();
return;
} }
_pins[1] = bc.pins[1]; _pins[1] = bc.pins[1];
_frequencykHz = bc.frequency ? bc.frequency : 2000U; // 2MHz clock if undefined _frequencykHz = bc.frequency ? bc.frequency : 2000U; // 2MHz clock if undefined
} }
reversed = bc.reversed;
_needsRefresh = bc.refreshReq || bc.type == TYPE_TM1814;
_skip = bc.skipAmount; //sacrificial pixels
_len = bc.count + _skip;
_iType = PolyBus::getI(bc.type, _pins, nr); _iType = PolyBus::getI(bc.type, _pins, nr);
if (_iType == I_NONE) return; if (_iType == I_NONE) return;
uint16_t lenToCreate = _len; if (bc.doubleBuffer && !allocData(bc.count * (Bus::hasWhite(_type) + 3*Bus::hasRGB(_type)))) return; //warning: hardcoded channel count
if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(_len); // only needs a third of "RGB" LEDs for NeoPixelBus _buffering = bc.doubleBuffer;
_busPtr = PolyBus::create(_iType, _pins, lenToCreate, nr, _frequencykHz); uint16_t lenToCreate = bc.count;
if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(bc.count); // only needs a third of "RGB" LEDs for NeoPixelBus
_busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip, nr, _frequencykHz);
_valid = (_busPtr != nullptr); _valid = (_busPtr != nullptr);
_colorOrder = bc.colorOrder; DEBUG_PRINTF("%successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u)\n", _valid?"S":"Uns", nr, bc.count, bc.type, _pins[0], _pins[1], _iType);
DEBUG_PRINTF("%successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u)\n", _valid?"S":"Uns", nr, _len, bc.type, _pins[0],_pins[1],_iType);
} }
void BusDigital::show() { void BusDigital::show() {
PolyBus::show(_busPtr, _iType); if (!_valid) return;
if (_buffering) { // should be _data != nullptr, but that causes ~20% FPS drop
size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type);
for (size_t i=0; i<_len; i++) {
size_t offset = i*channels;
uint8_t co = _colorOrderMap.getPixelColorOrder(i+_start, _colorOrder);
uint32_t c;
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs (_len is always a multiple of 3)
switch (i%3) {
case 0: c = RGBW32(_data[offset] , _data[offset+1], _data[offset+2], 0); break;
case 1: c = RGBW32(_data[offset-1], _data[offset] , _data[offset+1], 0); break;
case 2: c = RGBW32(_data[offset-2], _data[offset-1], _data[offset] , 0); break;
}
} else {
c = RGBW32(_data[offset],_data[offset+1],_data[offset+2],(Bus::hasWhite(_type)?_data[offset+3]:0));
}
uint16_t pix = i;
if (_reversed) pix = _len - pix -1;
pix += _skip;
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co);
}
#if !defined(STATUSLED) || STATUSLED>=0
if (_skip) PolyBus::setPixelColor(_busPtr, _iType, 0, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black
#endif
for (int i=1; i<_skip; i++) PolyBus::setPixelColor(_busPtr, _iType, i, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black
}
PolyBus::show(_busPtr, _iType, !_buffering); // faster if buffer consistency is not important
} }
bool BusDigital::canShow() { bool BusDigital::canShow() {
if (!_valid) return true;
return PolyBus::canShow(_busPtr, _iType); return PolyBus::canShow(_busPtr, _iType);
} }
void BusDigital::setBrightness(uint8_t b, bool immediate) { void BusDigital::setBrightness(uint8_t b) {
if (_bri == b) return;
//Fix for turning off onboard LED breaking bus //Fix for turning off onboard LED breaking bus
#ifdef LED_BUILTIN #ifdef LED_BUILTIN
if (_bri == 0 && b > 0) { if (_bri == 0) { // && b > 0, covered by guard if above
if (_pins[0] == LED_BUILTIN || _pins[1] == LED_BUILTIN) PolyBus::begin(_busPtr, _iType, _pins); if (_pins[0] == LED_BUILTIN || _pins[1] == LED_BUILTIN) reinit();
} }
#endif #endif
Bus::setBrightness(b, immediate); uint8_t prevBri = _bri;
PolyBus::setBrightness(_busPtr, _iType, b, immediate); Bus::setBrightness(b);
PolyBus::setBrightness(_busPtr, _iType, b);
if (_buffering) return;
// must update/repaint every LED in the NeoPixelBus buffer to the new brightness
// the only case where repainting is unnecessary is when all pixels are set after the brightness change but before the next show
// (which we can't rely on)
uint16_t hwLen = _len;
if (_type == TYPE_WS2812_1CH_X3) hwLen = NUM_ICS_WS2812_1CH_3X(_len); // only needs a third of "RGB" LEDs for NeoPixelBus
for (uint_fast16_t i = 0; i < hwLen; i++) {
// use 0 as color order, actual order does not matter here as we just update the channel values as-is
uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, i, 0),prevBri);
PolyBus::setPixelColor(_busPtr, _iType, i, c, 0);
}
} }
//If LEDs are skipped, it is possible to use the first as a status LED. //If LEDs are skipped, it is possible to use the first as a status LED.
//TODO only show if no new show due in the next 50ms //TODO only show if no new show due in the next 50ms
void BusDigital::setStatusPixel(uint32_t c) { void BusDigital::setStatusPixel(uint32_t c) {
if (_skip && canShow()) { if (_valid && _skip) {
PolyBus::setPixelColor(_busPtr, _iType, 0, c, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); PolyBus::setPixelColor(_busPtr, _iType, 0, c, _colorOrderMap.getPixelColorOrder(_start, _colorOrder));
PolyBus::show(_busPtr, _iType); if (canShow()) PolyBus::show(_busPtr, _iType);
} }
} }
void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
if (_type == TYPE_SK6812_RGBW || _type == TYPE_TM1814 || _type == TYPE_WS2812_1CH_X3) c = autoWhiteCalc(c); if (!_valid) return;
if (Bus::hasWhite(_type)) c = autoWhiteCalc(c);
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
if (reversed) pix = _len - pix -1; if (_buffering) { // should be _data != nullptr, but that causes ~20% FPS drop
else pix += _skip; size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type);
size_t offset = pix*channels;
if (Bus::hasRGB(_type)) {
_data[offset++] = R(c);
_data[offset++] = G(c);
_data[offset++] = B(c);
}
if (Bus::hasWhite(_type)) _data[offset] = W(c);
} else {
if (_reversed) pix = _len - pix -1;
pix += _skip;
uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder); uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs
uint16_t pOld = pix; uint16_t pOld = pix;
pix = IC_INDEX_WS2812_1CH_3X(pix); pix = IC_INDEX_WS2812_1CH_3X(pix);
uint32_t cOld = PolyBus::getPixelColor(_busPtr, _iType, pix, co); uint32_t cOld = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, pix, co),_bri);
switch (pOld % 3) { // change only the single channel (TODO: this can cause loss because of get/set) switch (pOld % 3) { // change only the single channel (TODO: this can cause loss because of get/set)
case 0: c = RGBW32(R(cOld), W(c) , B(cOld), 0); break; case 0: c = RGBW32(R(cOld), W(c) , B(cOld), 0); break;
case 1: c = RGBW32(W(c) , G(cOld), B(cOld), 0); break; case 1: c = RGBW32(W(c) , G(cOld), B(cOld), 0); break;
@ -164,23 +225,38 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
} }
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co); PolyBus::setPixelColor(_busPtr, _iType, pix, c, co);
} }
}
// returns original color if global buffering is enabled, else returns lossly restored color from bus
uint32_t BusDigital::getPixelColor(uint16_t pix) { uint32_t BusDigital::getPixelColor(uint16_t pix) {
if (reversed) pix = _len - pix -1; if (!_valid) return 0;
else pix += _skip; if (_buffering) { // should be _data != nullptr, but that causes ~20% FPS drop
size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type);
size_t offset = pix*channels;
uint32_t c;
if (!Bus::hasRGB(_type)) {
c = RGBW32(_data[offset], _data[offset], _data[offset], _data[offset]);
} else {
c = RGBW32(_data[offset], _data[offset+1], _data[offset+2], Bus::hasWhite(_type) ? _data[offset+3] : 0);
}
return c;
} else {
if (_reversed) pix = _len - pix -1;
pix += _skip;
uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder); uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, (_type==TYPE_WS2812_1CH_X3) ? IC_INDEX_WS2812_1CH_3X(pix) : pix, co),_bri);
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs
uint16_t pOld = pix; uint8_t r = R(c);
pix = IC_INDEX_WS2812_1CH_3X(pix); uint8_t g = _reversed ? B(c) : G(c); // should G and B be switched if _reversed?
uint32_t c = PolyBus::getPixelColor(_busPtr, _iType, pix, co); uint8_t b = _reversed ? G(c) : B(c);
switch (pOld % 3) { // get only the single channel switch (pix % 3) { // get only the single channel
case 0: c = RGBW32(G(c), G(c), G(c), G(c)); break; case 0: c = RGBW32(g, g, g, g); break;
case 1: c = RGBW32(R(c), R(c), R(c), R(c)); break; case 1: c = RGBW32(r, r, r, r); break;
case 2: c = RGBW32(B(c), B(c), B(c), B(c)); break; case 2: c = RGBW32(b, b, b, b); break;
}
} }
return c; return c;
} }
return PolyBus::getPixelColor(_busPtr, _iType, pix, co);
} }
uint8_t BusDigital::getPins(uint8_t* pinArray) { uint8_t BusDigital::getPins(uint8_t* pinArray) {
@ -196,6 +272,7 @@ void BusDigital::setColorOrder(uint8_t colorOrder) {
} }
void BusDigital::reinit() { void BusDigital::reinit() {
if (!_valid) return;
PolyBus::begin(_busPtr, _iType, _pins); PolyBus::begin(_busPtr, _iType, _pins);
} }
@ -205,13 +282,15 @@ void BusDigital::cleanup() {
_iType = I_NONE; _iType = I_NONE;
_valid = false; _valid = false;
_busPtr = nullptr; _busPtr = nullptr;
if (_data != nullptr) freeData();
pinManager.deallocatePin(_pins[1], PinOwner::BusDigital); pinManager.deallocatePin(_pins[1], PinOwner::BusDigital);
pinManager.deallocatePin(_pins[0], PinOwner::BusDigital); pinManager.deallocatePin(_pins[0], PinOwner::BusDigital);
} }
BusPwm::BusPwm(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { BusPwm::BusPwm(BusConfig &bc)
_valid = false; : Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed)
{
if (!IS_PWM(bc.type)) return; if (!IS_PWM(bc.type)) return;
uint8_t numPins = NUM_PWM_PINS(bc.type); uint8_t numPins = NUM_PWM_PINS(bc.type);
_frequency = bc.frequency ? bc.frequency : WLED_PWM_FREQ; _frequency = bc.frequency ? bc.frequency : WLED_PWM_FREQ;
@ -239,7 +318,7 @@ BusPwm::BusPwm(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
ledcAttachPin(_pins[i], _ledcStart + i); ledcAttachPin(_pins[i], _ledcStart + i);
#endif #endif
} }
reversed = bc.reversed; _data = _pwmdata; // avoid malloc() and use stack
_valid = true; _valid = true;
} }
@ -307,7 +386,7 @@ void BusPwm::show() {
uint8_t numPins = NUM_PWM_PINS(_type); uint8_t numPins = NUM_PWM_PINS(_type);
for (uint8_t i = 0; i < numPins; i++) { for (uint8_t i = 0; i < numPins; i++) {
uint8_t scaled = (_data[i] * _bri) / 255; uint8_t scaled = (_data[i] * _bri) / 255;
if (reversed) scaled = 255 - scaled; if (_reversed) scaled = 255 - scaled;
#ifdef ESP8266 #ifdef ESP8266
analogWrite(_pins[i], scaled); analogWrite(_pins[i], scaled);
#else #else
@ -342,8 +421,10 @@ void BusPwm::deallocatePins() {
} }
BusOnOff::BusOnOff(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { BusOnOff::BusOnOff(BusConfig &bc)
_valid = false; : Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed)
, _onoffdata(0)
{
if (bc.type != TYPE_ONOFF) return; if (bc.type != TYPE_ONOFF) return;
uint8_t currentPin = bc.pins[0]; uint8_t currentPin = bc.pins[0];
@ -352,7 +433,7 @@ BusOnOff::BusOnOff(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
} }
_pin = currentPin; //store only after allocatePin() succeeds _pin = currentPin; //store only after allocatePin() succeeds
pinMode(_pin, OUTPUT); pinMode(_pin, OUTPUT);
reversed = bc.reversed; _data = &_onoffdata; // avoid malloc() and use stack
_valid = true; _valid = true;
} }
@ -363,18 +444,17 @@ void BusOnOff::setPixelColor(uint16_t pix, uint32_t c) {
uint8_t g = G(c); uint8_t g = G(c);
uint8_t b = B(c); uint8_t b = B(c);
uint8_t w = W(c); uint8_t w = W(c);
_data[0] = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0;
_data = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0;
} }
uint32_t BusOnOff::getPixelColor(uint16_t pix) { uint32_t BusOnOff::getPixelColor(uint16_t pix) {
if (!_valid) return 0; if (!_valid) return 0;
return RGBW32(_data, _data, _data, _data); return RGBW32(_data[0], _data[0], _data[0], _data[0]);
} }
void BusOnOff::show() { void BusOnOff::show() {
if (!_valid) return; if (!_valid) return;
digitalWrite(_pin, reversed ? !(bool)_data : (bool)_data); digitalWrite(_pin, _reversed ? !(bool)_data[0] : (bool)_data[0]);
} }
uint8_t BusOnOff::getPins(uint8_t* pinArray) { uint8_t BusOnOff::getPins(uint8_t* pinArray) {
@ -384,8 +464,10 @@ uint8_t BusOnOff::getPins(uint8_t* pinArray) {
} }
BusNetwork::BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { BusNetwork::BusNetwork(BusConfig &bc)
_valid = false; : Bus(bc.type, bc.start, bc.autoWhite, bc.count)
, _broadcastLock(false)
{
switch (bc.type) { switch (bc.type) {
case TYPE_NET_ARTNET_RGB: case TYPE_NET_ARTNET_RGB:
_rgbw = false; _rgbw = false;
@ -401,18 +483,13 @@ BusNetwork::BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
break; break;
} }
_UDPchannels = _rgbw ? 4 : 3; _UDPchannels = _rgbw ? 4 : 3;
_data = (byte *)malloc(bc.count * _UDPchannels);
if (_data == nullptr) return;
memset(_data, 0, bc.count * _UDPchannels);
_len = bc.count;
_client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]); _client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]);
_broadcastLock = false; _valid = (allocData(_len * _UDPchannels) != nullptr);
_valid = true;
} }
void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) { void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) {
if (!_valid || pix >= _len) return; if (!_valid || pix >= _len) return;
if (hasWhite()) c = autoWhiteCalc(c); if (_rgbw) c = autoWhiteCalc(c);
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
uint16_t offset = pix * _UDPchannels; uint16_t offset = pix * _UDPchannels;
_data[offset] = R(c); _data[offset] = R(c);
@ -424,7 +501,7 @@ void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) {
uint32_t BusNetwork::getPixelColor(uint16_t pix) { uint32_t BusNetwork::getPixelColor(uint16_t pix) {
if (!_valid || pix >= _len) return 0; if (!_valid || pix >= _len) return 0;
uint16_t offset = pix * _UDPchannels; uint16_t offset = pix * _UDPchannels;
return RGBW32(_data[offset], _data[offset+1], _data[offset+2], _rgbw ? (_data[offset+3] << 24) : 0); return RGBW32(_data[offset], _data[offset+1], _data[offset+2], (_rgbw ? _data[offset+3] : 0));
} }
void BusNetwork::show() { void BusNetwork::show() {
@ -444,8 +521,7 @@ uint8_t BusNetwork::getPins(uint8_t* pinArray) {
void BusNetwork::cleanup() { void BusNetwork::cleanup() {
_type = I_NONE; _type = I_NONE;
_valid = false; _valid = false;
if (_data != nullptr) free(_data); freeData();
_data = nullptr;
} }
@ -506,7 +582,7 @@ void BusManager::setStatusPixel(uint32_t c) {
} }
} }
void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c, int16_t cct) { void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c) {
for (uint8_t i = 0; i < numBusses; i++) { for (uint8_t i = 0; i < numBusses; i++) {
Bus* b = busses[i]; Bus* b = busses[i];
uint16_t bstart = b->getStart(); uint16_t bstart = b->getStart();
@ -515,9 +591,9 @@ void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c, int16_t cct)
} }
} }
void BusManager::setBrightness(uint8_t b, bool immediate) { void BusManager::setBrightness(uint8_t b) {
for (uint8_t i = 0; i < numBusses; i++) { for (uint8_t i = 0; i < numBusses; i++) {
busses[i]->setBrightness(b, immediate); busses[i]->setBrightness(b);
} }
} }

View File

@ -18,6 +18,10 @@
#define IC_INDEX_WS2812_2CH_3X(i) ((i)*2/3) #define IC_INDEX_WS2812_2CH_3X(i) ((i)*2/3)
#define WS2812_2CH_3X_SPANS_2_ICS(i) ((i)&0x01) // every other LED zone is on two different ICs #define WS2812_2CH_3X_SPANS_2_ICS(i) ((i)&0x01) // every other LED zone is on two different ICs
// flag for using double buffering in BusDigital
extern bool useGlobalLedBuffer;
//temporary struct for passing bus configuration to bus //temporary struct for passing bus configuration to bus
struct BusConfig { struct BusConfig {
uint8_t type; uint8_t type;
@ -30,15 +34,25 @@ struct BusConfig {
uint8_t autoWhite; uint8_t autoWhite;
uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255}; uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255};
uint16_t frequency; uint16_t frequency;
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U) { bool doubleBuffer;
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U, bool dblBfr=false)
: count(len)
, start(pstart)
, colorOrder(pcolorOrder)
, reversed(rev)
, skipAmount(skip)
, autoWhite(aw)
, frequency(clock_kHz)
, doubleBuffer(dblBfr)
{
refreshReq = (bool) GET_BIT(busType,7); refreshReq = (bool) GET_BIT(busType,7);
type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh) type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh)
count = len; start = pstart; colorOrder = pcolorOrder; reversed = rev; skipAmount = skip; autoWhite = aw; frequency = clock_kHz; size_t nPins = 1;
uint8_t nPins = 1;
if (type >= TYPE_NET_DDP_RGB && type < 96) nPins = 4; //virtual network bus. 4 "pins" store IP address if (type >= TYPE_NET_DDP_RGB && type < 96) nPins = 4; //virtual network bus. 4 "pins" store IP address
else if (type > 47) nPins = 2; else if (type > 47) nPins = 2;
else if (type > 40 && type < 46) nPins = NUM_PWM_PINS(type); else if (type > 40 && type < 46) nPins = NUM_PWM_PINS(type);
for (uint8_t i = 0; i < nPins; i++) pins[i] = ppins[i]; for (size_t i = 0; i < nPins; i++) pins[i] = ppins[i];
} }
//validates start and length and extends total if needed //validates start and length and extends total if needed
@ -54,6 +68,7 @@ struct BusConfig {
} }
}; };
// Defines an LED Strip and its color ordering. // Defines an LED Strip and its color ordering.
struct ColorOrderMapEntry { struct ColorOrderMapEntry {
uint16_t start; uint16_t start;
@ -64,9 +79,7 @@ struct ColorOrderMapEntry {
struct ColorOrderMap { struct ColorOrderMap {
void add(uint16_t start, uint16_t len, uint8_t colorOrder); void add(uint16_t start, uint16_t len, uint8_t colorOrder);
uint8_t count() const { uint8_t count() const { return _count; }
return _count;
}
void reset() { void reset() {
_count = 0; _count = 0;
@ -87,17 +100,20 @@ struct ColorOrderMap {
ColorOrderMapEntry _mappings[WLED_MAX_COLOR_ORDER_MAPPINGS]; ColorOrderMapEntry _mappings[WLED_MAX_COLOR_ORDER_MAPPINGS];
}; };
//parent class of BusDigital, BusPwm, and BusNetwork //parent class of BusDigital, BusPwm, and BusNetwork
class Bus { class Bus {
public: public:
Bus(uint8_t type, uint16_t start, uint8_t aw) Bus(uint8_t type, uint16_t start, uint8_t aw, uint16_t len = 1, bool reversed = false, bool refresh = false)
: _bri(255) : _type(type)
, _len(1) , _bri(255)
, _start(start)
, _len(len)
, _reversed(reversed)
, _valid(false) , _valid(false)
, _needsRefresh(false) , _needsRefresh(refresh)
, _data(nullptr) // keep data access consistent across all types of buses
{ {
_type = type;
_start = start;
_autoWhiteMode = Bus::hasWhite(_type) ? aw : RGBW_MODE_MANUAL_ONLY; _autoWhiteMode = Bus::hasWhite(_type) ? aw : RGBW_MODE_MANUAL_ONLY;
}; };
@ -108,7 +124,7 @@ class Bus {
virtual void setStatusPixel(uint32_t c) {} virtual void setStatusPixel(uint32_t c) {}
virtual void setPixelColor(uint16_t pix, uint32_t c) = 0; virtual void setPixelColor(uint16_t pix, uint32_t c) = 0;
virtual uint32_t getPixelColor(uint16_t pix) { return 0; } virtual uint32_t getPixelColor(uint16_t pix) { return 0; }
virtual void setBrightness(uint8_t b, bool immediate=false) { _bri = b; }; virtual void setBrightness(uint8_t b) { _bri = b; };
virtual void cleanup() = 0; virtual void cleanup() = 0;
virtual uint8_t getPins(uint8_t* pinArray) { return 0; } virtual uint8_t getPins(uint8_t* pinArray) { return 0; }
virtual uint16_t getLength() { return _len; } virtual uint16_t getLength() { return _len; }
@ -116,27 +132,31 @@ class Bus {
virtual uint8_t getColorOrder() { return COL_ORDER_RGB; } virtual uint8_t getColorOrder() { return COL_ORDER_RGB; }
virtual uint8_t skippedLeds() { return 0; } virtual uint8_t skippedLeds() { return 0; }
virtual uint16_t getFrequency() { return 0U; } virtual uint16_t getFrequency() { return 0U; }
inline void setReversed(bool reversed) { _reversed = reversed; }
inline uint16_t getStart() { return _start; } inline uint16_t getStart() { return _start; }
inline void setStart(uint16_t start) { _start = start; } inline void setStart(uint16_t start) { _start = start; }
inline uint8_t getType() { return _type; } inline uint8_t getType() { return _type; }
inline bool isOk() { return _valid; } inline bool isOk() { return _valid; }
inline bool isReversed() { return _reversed; }
inline bool isOffRefreshRequired() { return _needsRefresh; } inline bool isOffRefreshRequired() { return _needsRefresh; }
bool containsPixel(uint16_t pix) { return pix >= _start && pix < _start+_len; } bool containsPixel(uint16_t pix) { return pix >= _start && pix < _start+_len; }
virtual bool hasRGB() { virtual bool hasRGB(void) { return Bus::hasRGB(_type); }
if ((_type >= TYPE_WS2812_1CH && _type <= TYPE_WS2812_WWA) || _type == TYPE_ANALOG_1CH || _type == TYPE_ANALOG_2CH || _type == TYPE_ONOFF) return false; static bool hasRGB(uint8_t type) {
if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_ANALOG_1CH || type == TYPE_ANALOG_2CH || type == TYPE_ONOFF) return false;
return true; return true;
} }
virtual bool hasWhite() { return Bus::hasWhite(_type); } virtual bool hasWhite(void) { return Bus::hasWhite(_type); }
static bool hasWhite(uint8_t type) { static bool hasWhite(uint8_t type) {
if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_SK6812_RGBW || type == TYPE_TM1814) return true; // digital types with white channel if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_SK6812_RGBW || type == TYPE_TM1814) return true; // digital types with white channel
if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true; // analog types with white channel if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true; // analog types with white channel
if (type == TYPE_NET_DDP_RGBW) return true; // network types with white channel if (type == TYPE_NET_DDP_RGBW) return true; // network types with white channel
return false; return false;
} }
virtual bool hasCCT() { virtual bool hasCCT(void) { return Bus::hasCCT(_type); }
if (_type == TYPE_WS2812_2CH_X3 || _type == TYPE_WS2812_WWA || static bool hasCCT(uint8_t type) {
_type == TYPE_ANALOG_2CH || _type == TYPE_ANALOG_5CH) return true; if (type == TYPE_WS2812_2CH_X3 || type == TYPE_WS2812_WWA ||
type == TYPE_ANALOG_2CH || type == TYPE_ANALOG_5CH) return true;
return false; return false;
} }
static void setCCT(uint16_t cct) { static void setCCT(uint16_t cct) {
@ -155,107 +175,87 @@ class Bus {
inline static void setGlobalAWMode(uint8_t m) { if (m < 5) _gAWM = m; else _gAWM = AW_GLOBAL_DISABLED; } inline static void setGlobalAWMode(uint8_t m) { if (m < 5) _gAWM = m; else _gAWM = AW_GLOBAL_DISABLED; }
inline static uint8_t getGlobalAWMode() { return _gAWM; } inline static uint8_t getGlobalAWMode() { return _gAWM; }
bool reversed = false;
protected: protected:
uint8_t _type; uint8_t _type;
uint8_t _bri; uint8_t _bri;
uint16_t _start; uint16_t _start;
uint16_t _len; uint16_t _len;
bool _reversed;
bool _valid; bool _valid;
bool _needsRefresh; bool _needsRefresh;
uint8_t _autoWhiteMode; uint8_t _autoWhiteMode;
uint8_t *_data;
static uint8_t _gAWM; static uint8_t _gAWM;
static int16_t _cct; static int16_t _cct;
static uint8_t _cctBlend; static uint8_t _cctBlend;
uint32_t autoWhiteCalc(uint32_t c); uint32_t autoWhiteCalc(uint32_t c);
uint8_t *allocData(size_t size = 1);
void freeData() { if (_data != nullptr) free(_data); _data = nullptr; }
}; };
class BusDigital : public Bus { class BusDigital : public Bus {
public: public:
BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com); BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com);
~BusDigital() { cleanup(); }
inline void show(); void show();
bool canShow(); bool canShow();
void setBrightness(uint8_t b);
void setBrightness(uint8_t b, bool immediate);
void setStatusPixel(uint32_t c); void setStatusPixel(uint32_t c);
void setPixelColor(uint16_t pix, uint32_t c); void setPixelColor(uint16_t pix, uint32_t c);
uint32_t getPixelColor(uint16_t pix);
uint8_t getColorOrder() {
return _colorOrder;
}
uint16_t getLength() {
return _len - _skip;
}
uint8_t getPins(uint8_t* pinArray);
void setColorOrder(uint8_t colorOrder); void setColorOrder(uint8_t colorOrder);
uint32_t getPixelColor(uint16_t pix);
uint8_t skippedLeds() { uint8_t getColorOrder() { return _colorOrder; }
return _skip; uint8_t getPins(uint8_t* pinArray);
} uint8_t skippedLeds() { return _skip; }
uint16_t getFrequency() { return _frequencykHz; } uint16_t getFrequency() { return _frequencykHz; }
void reinit(); void reinit();
void cleanup(); void cleanup();
~BusDigital() {
cleanup();
}
private: private:
uint8_t _colorOrder = COL_ORDER_GRB; uint8_t _skip;
uint8_t _pins[2] = {255, 255}; uint8_t _colorOrder;
uint8_t _iType = 0; //I_NONE; uint8_t _pins[2];
uint8_t _skip = 0; uint8_t _iType;
uint16_t _frequencykHz = 0U; uint16_t _frequencykHz;
void * _busPtr = nullptr; void * _busPtr;
const ColorOrderMap &_colorOrderMap; const ColorOrderMap &_colorOrderMap;
bool _buffering; // temporary until we figure out why comparison "_data != nullptr" causes severe FPS drop
inline uint32_t restoreColorLossy(uint32_t c, uint8_t restoreBri) {
if (restoreBri < 255) {
uint8_t* chan = (uint8_t*) &c;
for (uint_fast8_t i=0; i<4; i++) {
uint_fast16_t val = chan[i];
chan[i] = ((val << 8) + restoreBri) / (restoreBri + 1); //adding _bri slighly improves recovery / stops degradation on re-scale
}
}
return c;
}
}; };
class BusPwm : public Bus { class BusPwm : public Bus {
public: public:
BusPwm(BusConfig &bc); BusPwm(BusConfig &bc);
~BusPwm() { cleanup(); }
void setPixelColor(uint16_t pix, uint32_t c); void setPixelColor(uint16_t pix, uint32_t c);
uint32_t getPixelColor(uint16_t pix); //does no index check
//does no index check
uint32_t getPixelColor(uint16_t pix);
void show();
uint8_t getPins(uint8_t* pinArray); uint8_t getPins(uint8_t* pinArray);
uint16_t getFrequency() { return _frequency; } uint16_t getFrequency() { return _frequency; }
void show();
void cleanup() { void cleanup() { deallocatePins(); }
deallocatePins();
}
~BusPwm() {
cleanup();
}
private: private:
uint8_t _pins[5] = {255, 255, 255, 255, 255}; uint8_t _pins[5];
uint8_t _data[5] = {0}; uint8_t _pwmdata[5];
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
uint8_t _ledcStart = 255; uint8_t _ledcStart;
#endif #endif
uint16_t _frequency = 0U; uint16_t _frequency;
void deallocatePins(); void deallocatePins();
}; };
@ -264,72 +264,46 @@ class BusPwm : public Bus {
class BusOnOff : public Bus { class BusOnOff : public Bus {
public: public:
BusOnOff(BusConfig &bc); BusOnOff(BusConfig &bc);
~BusOnOff() { cleanup(); }
void setPixelColor(uint16_t pix, uint32_t c); void setPixelColor(uint16_t pix, uint32_t c);
uint32_t getPixelColor(uint16_t pix); uint32_t getPixelColor(uint16_t pix);
void show();
uint8_t getPins(uint8_t* pinArray); uint8_t getPins(uint8_t* pinArray);
void show();
void cleanup() { void cleanup() { pinManager.deallocatePin(_pin, PinOwner::BusOnOff); }
pinManager.deallocatePin(_pin, PinOwner::BusOnOff);
}
~BusOnOff() {
cleanup();
}
private: private:
uint8_t _pin = 255; uint8_t _pin;
uint8_t _data = 0; uint8_t _onoffdata;
}; };
class BusNetwork : public Bus { class BusNetwork : public Bus {
public: public:
BusNetwork(BusConfig &bc); BusNetwork(BusConfig &bc);
~BusNetwork() { cleanup(); }
bool hasRGB() { return true; } bool hasRGB() { return true; }
bool hasWhite() { return _rgbw; } bool hasWhite() { return _rgbw; }
bool canShow() { return !_broadcastLock; } // this should be a return value from UDP routine if it is still sending data out
void setPixelColor(uint16_t pix, uint32_t c); void setPixelColor(uint16_t pix, uint32_t c);
uint32_t getPixelColor(uint16_t pix); uint32_t getPixelColor(uint16_t pix);
void show();
bool canShow() {
// this should be a return value from UDP routine if it is still sending data out
return !_broadcastLock;
}
uint8_t getPins(uint8_t* pinArray); uint8_t getPins(uint8_t* pinArray);
void show();
uint16_t getLength() {
return _len;
}
void cleanup(); void cleanup();
~BusNetwork() {
cleanup();
}
private: private:
IPAddress _client; IPAddress _client;
uint8_t _UDPtype; uint8_t _UDPtype;
uint8_t _UDPchannels; uint8_t _UDPchannels;
bool _rgbw; bool _rgbw;
bool _broadcastLock; bool _broadcastLock;
byte *_data;
}; };
class BusManager { class BusManager {
public: public:
BusManager() {}; BusManager() : numBusses(0) {};
//utility to get the approx. memory usage of a given BusConfig //utility to get the approx. memory usage of a given BusConfig
static uint32_t memUsage(BusConfig &bc); static uint32_t memUsage(BusConfig &bc);
@ -340,38 +314,24 @@ class BusManager {
void removeAll(); void removeAll();
void show(); void show();
void setStatusPixel(uint32_t c);
void setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1);
void setBrightness(uint8_t b, bool immediate=false); // immediate=true is for use in ABL, it applies brightness immediately (warning: inefficient)
void setSegmentCCT(int16_t cct, bool allowWBCorrection = false);
uint32_t getPixelColor(uint16_t pix);
bool canAllShow(); bool canAllShow();
void setStatusPixel(uint32_t c);
void setPixelColor(uint16_t pix, uint32_t c);
void setBrightness(uint8_t b);
void setSegmentCCT(int16_t cct, bool allowWBCorrection = false);
uint32_t getPixelColor(uint16_t pix);
Bus* getBus(uint8_t busNr); Bus* getBus(uint8_t busNr);
//semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit()) //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
uint16_t getTotalLength(); uint16_t getTotalLength();
inline uint8_t getNumBusses() const { return numBusses; }
inline void updateColorOrderMap(const ColorOrderMap &com) { inline void updateColorOrderMap(const ColorOrderMap &com) { memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap)); }
memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap)); inline const ColorOrderMap& getColorOrderMap() const { return colorOrderMap; }
}
inline const ColorOrderMap& getColorOrderMap() const {
return colorOrderMap;
}
inline uint8_t getNumBusses() {
return numBusses;
}
private: private:
uint8_t numBusses = 0; uint8_t numBusses;
Bus* busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES]; Bus* busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES];
ColorOrderMap colorOrderMap; ColorOrderMap colorOrderMap;

View File

@ -280,6 +280,7 @@ class PolyBus {
#endif #endif
if (clock_kHz) dotStar_strip->SetMethodSettings(NeoSpiSettings((uint32_t)clock_kHz*1000)); if (clock_kHz) dotStar_strip->SetMethodSettings(NeoSpiSettings((uint32_t)clock_kHz*1000));
} }
// Begin & initialize the PixelSettings for TM1814 strips. // Begin & initialize the PixelSettings for TM1814 strips.
template <class T> template <class T>
static void beginTM1814(void* busPtr) { static void beginTM1814(void* busPtr) {
@ -288,6 +289,7 @@ class PolyBus {
// Max current for each LED (22.5 mA). // Max current for each LED (22.5 mA).
tm1814_strip->SetPixelSettings(NeoTm1814Settings(/*R*/225, /*G*/225, /*B*/225, /*W*/225)); tm1814_strip->SetPixelSettings(NeoTm1814Settings(/*R*/225, /*G*/225, /*B*/225, /*W*/225));
} }
static void begin(void* busPtr, uint8_t busType, uint8_t* pins, uint16_t clock_kHz = 0U) { static void begin(void* busPtr, uint8_t busType, uint8_t* pins, uint16_t clock_kHz = 0U) {
switch (busType) { switch (busType) {
case I_NONE: break; case I_NONE: break;
@ -390,7 +392,8 @@ class PolyBus {
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->Begin(); break; case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->Begin(); break;
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->Begin(); break; case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->Begin(); break;
} }
}; }
static void* create(uint8_t busType, uint8_t* pins, uint16_t len, uint8_t channel, uint16_t clock_kHz = 0U) { static void* create(uint8_t busType, uint8_t* pins, uint16_t len, uint8_t channel, uint16_t clock_kHz = 0U) {
void* busPtr = nullptr; void* busPtr = nullptr;
switch (busType) { switch (busType) {
@ -491,104 +494,106 @@ class PolyBus {
} }
begin(busPtr, busType, pins, clock_kHz); begin(busPtr, busType, pins, clock_kHz);
return busPtr; return busPtr;
}; }
static void show(void* busPtr, uint8_t busType) {
static void show(void* busPtr, uint8_t busType, bool consistent = true) {
switch (busType) { switch (busType) {
case I_NONE: break; case I_NONE: break;
#ifdef ESP8266 #ifdef ESP8266
case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->Show(); break; case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->Show(consistent); break;
case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->Show(); break; case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->Show(consistent); break;
case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->Show(); break; case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->Show(consistent); break;
case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->Show(); break; case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->Show(consistent); break;
case I_8266_U0_NEO_4: (static_cast<B_8266_U0_NEO_4*>(busPtr))->Show(); break; case I_8266_U0_NEO_4: (static_cast<B_8266_U0_NEO_4*>(busPtr))->Show(consistent); break;
case I_8266_U1_NEO_4: (static_cast<B_8266_U1_NEO_4*>(busPtr))->Show(); break; case I_8266_U1_NEO_4: (static_cast<B_8266_U1_NEO_4*>(busPtr))->Show(consistent); break;
case I_8266_DM_NEO_4: (static_cast<B_8266_DM_NEO_4*>(busPtr))->Show(); break; case I_8266_DM_NEO_4: (static_cast<B_8266_DM_NEO_4*>(busPtr))->Show(consistent); break;
case I_8266_BB_NEO_4: (static_cast<B_8266_BB_NEO_4*>(busPtr))->Show(); break; case I_8266_BB_NEO_4: (static_cast<B_8266_BB_NEO_4*>(busPtr))->Show(consistent); break;
case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->Show(); break; case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->Show(consistent); break;
case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->Show(); break; case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->Show(consistent); break;
case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->Show(); break; case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->Show(consistent); break;
case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->Show(); break; case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->Show(consistent); break;
case I_8266_U0_TM1_4: (static_cast<B_8266_U0_TM1_4*>(busPtr))->Show(); break; case I_8266_U0_TM1_4: (static_cast<B_8266_U0_TM1_4*>(busPtr))->Show(consistent); break;
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->Show(); break; case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->Show(consistent); break;
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->Show(); break; case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->Show(consistent); break;
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->Show(); break; case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->Show(consistent); break;
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->Show(); break; case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->Show(consistent); break;
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->Show(); break; case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->Show(consistent); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->Show(); break; case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->Show(consistent); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->Show(); break; case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->Show(consistent); break;
case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->Show(); break; case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->Show(consistent); break;
case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->Show(); break; case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->Show(consistent); break;
case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->Show(); break; case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->Show(consistent); break;
case I_8266_BB_UCS_3: (static_cast<B_8266_BB_UCS_3*>(busPtr))->Show(); break; case I_8266_BB_UCS_3: (static_cast<B_8266_BB_UCS_3*>(busPtr))->Show(consistent); break;
case I_8266_U0_UCS_4: (static_cast<B_8266_U0_UCS_4*>(busPtr))->Show(); break; case I_8266_U0_UCS_4: (static_cast<B_8266_U0_UCS_4*>(busPtr))->Show(consistent); break;
case I_8266_U1_UCS_4: (static_cast<B_8266_U1_UCS_4*>(busPtr))->Show(); break; case I_8266_U1_UCS_4: (static_cast<B_8266_U1_UCS_4*>(busPtr))->Show(consistent); break;
case I_8266_DM_UCS_4: (static_cast<B_8266_DM_UCS_4*>(busPtr))->Show(); break; case I_8266_DM_UCS_4: (static_cast<B_8266_DM_UCS_4*>(busPtr))->Show(consistent); break;
case I_8266_BB_UCS_4: (static_cast<B_8266_BB_UCS_4*>(busPtr))->Show(); break; case I_8266_BB_UCS_4: (static_cast<B_8266_BB_UCS_4*>(busPtr))->Show(consistent); break;
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Show(); break; case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->Show(); break; case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->Show(consistent); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->Show(); break; case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->Show(consistent); break;
#endif #endif
// case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->Show(); break; // case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->Show(consistent); break;
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->Show(); break; case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->Show(); break; case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->Show(consistent); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->Show(); break; case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->Show(consistent); break;
#endif #endif
// case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->Show(); break; // case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->Show(consistent); break;
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->Show(); break; case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->Show(); break; case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->Show(consistent); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->Show(); break; case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->Show(consistent); break;
#endif #endif
// case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->Show(); break; // case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->Show(consistent); break;
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->Show(); break; case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->Show(consistent); break;
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->Show(); break; case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->Show(); break; case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->Show(consistent); break;
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->Show(); break; case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->Show(consistent); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->Show(); break; case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->Show(consistent); break;
case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->Show(); break; case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->Show(consistent); break;
#endif #endif
case I_32_RN_UCS_3: (static_cast<B_32_RN_UCS_3*>(busPtr))->Show(); break; case I_32_RN_UCS_3: (static_cast<B_32_RN_UCS_3*>(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_3: (static_cast<B_32_I0_UCS_3*>(busPtr))->Show(); break; case I_32_I0_UCS_3: (static_cast<B_32_I0_UCS_3*>(busPtr))->Show(consistent); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->Show(); break; case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->Show(consistent); break;
#endif #endif
// case I_32_BB_UCS_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->Show(); break; // case I_32_BB_UCS_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->Show(consistent); break;
case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->Show(); break; case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->Show(); break; case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->Show(consistent); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->Show(); break; case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->Show(consistent); break;
#endif #endif
// case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->Show(); break; // case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->Show(consistent); break;
#endif #endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Show(); break; case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Show(consistent); break;
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->Show(); break; case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->Show(consistent); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->Show(); break; case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->Show(consistent); break;
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->Show(); break; case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->Show(consistent); break;
case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->Show(); break; case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->Show(consistent); break;
case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->Show(); break; case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->Show(consistent); break;
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->Show(); break; case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->Show(consistent); break;
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->Show(); break; case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->Show(consistent); break;
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->Show(); break; case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->Show(consistent); break;
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->Show(); break; case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->Show(consistent); break;
} }
}; }
static bool canShow(void* busPtr, uint8_t busType) { static bool canShow(void* busPtr, uint8_t busType) {
switch (busType) { switch (busType) {
case I_NONE: return true; case I_NONE: return true;
@ -685,7 +690,8 @@ class PolyBus {
case I_SS_P98_3: return (static_cast<B_SS_P98_3*>(busPtr))->CanShow(); break; case I_SS_P98_3: return (static_cast<B_SS_P98_3*>(busPtr))->CanShow(); break;
} }
return true; return true;
}; }
static void setPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint32_t c, uint8_t co) { static void setPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint32_t c, uint8_t co) {
uint8_t r = c >> 16; uint8_t r = c >> 16;
uint8_t g = c >> 8; uint8_t g = c >> 8;
@ -805,104 +811,106 @@ class PolyBus {
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
} }
}; }
static void setBrightness(void* busPtr, uint8_t busType, uint8_t b, bool immediate) { // immediate=true is for use in ABL, it applies brightness immediately (warning: inefficient)
static void setBrightness(void* busPtr, uint8_t busType, uint8_t b) {
switch (busType) { switch (busType) {
case I_NONE: break; case I_NONE: break;
#ifdef ESP8266 #ifdef ESP8266
case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_NEO_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_NEO_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_NEO_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_NEO_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_NEO_4: (static_cast<B_8266_U0_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_NEO_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U0_NEO_4: (static_cast<B_8266_U0_NEO_4*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_NEO_4: (static_cast<B_8266_U1_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_NEO_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U1_NEO_4: (static_cast<B_8266_U1_NEO_4*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_NEO_4: (static_cast<B_8266_DM_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_NEO_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_DM_NEO_4: (static_cast<B_8266_DM_NEO_4*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_NEO_4: (static_cast<B_8266_BB_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_NEO_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_BB_NEO_4: (static_cast<B_8266_BB_NEO_4*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_400_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_400_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_400_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_400_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_TM1_4: (static_cast<B_8266_U0_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_TM1_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U0_TM1_4: (static_cast<B_8266_U0_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_TM1_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_TM1_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_TM1_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_TM2_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_TM2_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_TM2_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_TM2_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_UCS_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_UCS_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_UCS_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_UCS_3: (static_cast<B_8266_BB_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_UCS_3*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_BB_UCS_3: (static_cast<B_8266_BB_UCS_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_UCS_4: (static_cast<B_8266_U0_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_UCS_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U0_UCS_4: (static_cast<B_8266_U0_UCS_4*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_UCS_4: (static_cast<B_8266_U1_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_UCS_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_U1_UCS_4: (static_cast<B_8266_U1_UCS_4*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_UCS_4: (static_cast<B_8266_DM_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_UCS_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_DM_UCS_4: (static_cast<B_8266_DM_UCS_4*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_UCS_4: (static_cast<B_8266_BB_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_UCS_4*>(busPtr))->ApplyPostAdjustments(); break; case I_8266_BB_UCS_4: (static_cast<B_8266_BB_UCS_4*>(busPtr))->SetLuminance(b); break;
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_NEO_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetLuminance(b); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_NEO_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->SetLuminance(b); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_NEO_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetLuminance(b); break;
#endif #endif
// case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_BB_NEO_3*>(busPtr))->ApplyPostAdjustments(); break; // case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->SetLuminance(b); break;
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_NEO_4*>(busPtr))->ApplyPostAdjustments(); break; case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetLuminance(b); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_NEO_4*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetLuminance(b); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_NEO_4*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetLuminance(b); break;
#endif #endif
// case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_BB_NEO_4*>(busPtr))->ApplyPostAdjustments(); break; // case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->SetLuminance(b); break;
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_400_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetLuminance(b); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_400_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetLuminance(b); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_400_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetLuminance(b); break;
#endif #endif
// case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_BB_400_3*>(busPtr))->ApplyPostAdjustments(); break; // case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->SetLuminance(b); break;
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_TM1_4*>(busPtr))->ApplyPostAdjustments(); break; case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_TM2_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetLuminance(b); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_TM1_4*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_TM2_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->SetLuminance(b); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_TM1_4*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_TM2_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->SetLuminance(b); break;
#endif #endif
case I_32_RN_UCS_3: (static_cast<B_32_RN_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_UCS_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_RN_UCS_3: (static_cast<B_32_RN_UCS_3*>(busPtr))->SetLuminance(b); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_3: (static_cast<B_32_I0_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_UCS_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I0_UCS_3: (static_cast<B_32_I0_UCS_3*>(busPtr))->SetLuminance(b); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_UCS_3*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->SetLuminance(b); break;
#endif #endif
// case I_32_BB_UCS_3: (static_cast<B_32_BB_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_BB_UCS_3*>(busPtr))->ApplyPostAdjustments(); break; // case I_32_BB_UCS_3: (static_cast<B_32_BB_UCS_3*>(busPtr))->SetLuminance(b); break;
case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_UCS_4*>(busPtr))->ApplyPostAdjustments(); break; case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->SetLuminance(b); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_UCS_4*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->SetLuminance(b); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_UCS_4*>(busPtr))->ApplyPostAdjustments(); break; case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->SetLuminance(b); break;
#endif #endif
// case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_BB_UCS_4*>(busPtr))->ApplyPostAdjustments(); break; // case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->SetLuminance(b); break;
#endif #endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_HS_DOT_3*>(busPtr))->ApplyPostAdjustments(); break; case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetLuminance(b); break;
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_SS_DOT_3*>(busPtr))->ApplyPostAdjustments(); break; case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetLuminance(b); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_HS_LPD_3*>(busPtr))->ApplyPostAdjustments(); break; case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->SetLuminance(b); break;
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_SS_LPD_3*>(busPtr))->ApplyPostAdjustments(); break; case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->SetLuminance(b); break;
case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_HS_LPO_3*>(busPtr))->ApplyPostAdjustments(); break; case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->SetLuminance(b); break;
case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_SS_LPO_3*>(busPtr))->ApplyPostAdjustments(); break; case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->SetLuminance(b); break;
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_HS_WS1_3*>(busPtr))->ApplyPostAdjustments(); break; case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->SetLuminance(b); break;
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_SS_WS1_3*>(busPtr))->ApplyPostAdjustments(); break; case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->SetLuminance(b); break;
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_HS_P98_3*>(busPtr))->ApplyPostAdjustments(); break; case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetLuminance(b); break;
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_SS_P98_3*>(busPtr))->ApplyPostAdjustments(); break; case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetLuminance(b); break;
} }
}; }
static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) { static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) {
RgbwColor col(0,0,0,0); RgbwColor col(0,0,0,0);
switch (busType) { switch (busType) {

View File

@ -21,6 +21,7 @@ void shortPressAction(uint8_t b)
case 1: ++effectCurrent %= strip.getModeCount(); stateChanged = true; colorUpdated(CALL_MODE_BUTTON); break; case 1: ++effectCurrent %= strip.getModeCount(); stateChanged = true; colorUpdated(CALL_MODE_BUTTON); break;
} }
} else { } else {
unloadPlaylist(); // applying a preset unloads the playlist
applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET); applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET);
} }
@ -42,6 +43,7 @@ void longPressAction(uint8_t b)
case 1: bri += 8; stateUpdated(CALL_MODE_BUTTON); buttonPressedTime[b] = millis(); break; // repeatable action case 1: bri += 8; stateUpdated(CALL_MODE_BUTTON); buttonPressedTime[b] = millis(); break; // repeatable action
} }
} else { } else {
unloadPlaylist(); // applying a preset unloads the playlist
applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET); applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET);
} }
@ -63,6 +65,7 @@ void doublePressAction(uint8_t b)
case 1: ++effectPalette %= strip.getPaletteCount(); colorUpdated(CALL_MODE_BUTTON); break; case 1: ++effectPalette %= strip.getPaletteCount(); colorUpdated(CALL_MODE_BUTTON); break;
} }
} else { } else {
unloadPlaylist(); // applying a preset unloads the playlist
applyPreset(macroDoublePress[b], CALL_MODE_BUTTON_PRESET); applyPreset(macroDoublePress[b], CALL_MODE_BUTTON_PRESET);
} }
@ -153,6 +156,7 @@ void handleAnalog(uint8_t b)
#ifdef ESP8266 #ifdef ESP8266
rawReading = analogRead(A0) << 2; // convert 10bit read to 12bit rawReading = analogRead(A0) << 2; // convert 10bit read to 12bit
#else #else
if ((btnPin[b] < 0) || (digitalPinToAnalogChannel(btnPin[b]) < 0)) return; // pin must support analog ADC - newer esp32 frameworks throw lots of warnings otherwise
rawReading = analogRead(btnPin[b]); // collect at full 12bit resolution rawReading = analogRead(btnPin[b]); // collect at full 12bit resolution
#endif #endif
yield(); // keep WiFi task running - analog read may take several millis on ESP8266 yield(); // keep WiFi task running - analog read may take several millis on ESP8266
@ -262,7 +266,7 @@ void handleButton()
shortPressAction(b); shortPressAction(b);
buttonPressedBefore[b] = true; buttonPressedBefore[b] = true;
buttonPressedTime[b] = now; // continually update (for debouncing to work in release handler) buttonPressedTime[b] = now; // continually update (for debouncing to work in release handler)
return; continue;
} }
if (!buttonPressedBefore[b]) buttonPressedTime[b] = now; if (!buttonPressedBefore[b]) buttonPressedTime[b] = now;
@ -283,7 +287,7 @@ void handleButton()
// released after rising-edge short press action // released after rising-edge short press action
if (macroButton[b] && macroButton[b] == macroLongPress[b] && macroButton[b] == macroDoublePress[b]) { if (macroButton[b] && macroButton[b] == macroLongPress[b] && macroButton[b] == macroDoublePress[b]) {
if (dur > WLED_DEBOUNCE_THRESHOLD) buttonPressedBefore[b] = false; // debounce, blocks button for 50 ms once it has been released if (dur > WLED_DEBOUNCE_THRESHOLD) buttonPressedBefore[b] = false; // debounce, blocks button for 50 ms once it has been released
return; continue;
} }
if (dur < WLED_DEBOUNCE_THRESHOLD) {buttonPressedBefore[b] = false; continue;} // too short "press", debounce if (dur < WLED_DEBOUNCE_THRESHOLD) {buttonPressedBefore[b] = false; continue;} // too short "press", debounce

View File

@ -90,7 +90,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(strip.cctBlending, hw_led[F("cb")]); CJSON(strip.cctBlending, hw_led[F("cb")]);
Bus::setCCTBlend(strip.cctBlending); Bus::setCCTBlend(strip.cctBlending);
strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS
CJSON(strip.useLedsArray, hw_led[F("ld")]); CJSON(useGlobalLedBuffer, hw_led[F("ld")]);
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
// 2D Matrix Settings // 2D Matrix Settings
@ -134,7 +134,8 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (fromFS || !ins.isNull()) { if (fromFS || !ins.isNull()) {
uint8_t s = 0; // bus iterator uint8_t s = 0; // bus iterator
if (fromFS) busses.removeAll(); // can't safely manipulate busses directly in network callback if (fromFS) busses.removeAll(); // can't safely manipulate busses directly in network callback
uint32_t mem = 0; uint32_t mem = 0, globalBufMem = 0;
uint16_t maxlen = 0;
bool busesChanged = false; bool busesChanged = false;
for (JsonObject elm : ins) { for (JsonObject elm : ins) {
if (s >= WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES) break; if (s >= WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES) break;
@ -160,12 +161,16 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh
uint8_t AWmode = elm[F("rgbwm")] | autoWhiteMode; uint8_t AWmode = elm[F("rgbwm")] | autoWhiteMode;
if (fromFS) { if (fromFS) {
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz); BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer);
mem += BusManager::memUsage(bc); mem += BusManager::memUsage(bc);
if (mem <= MAX_LED_MEMORY) if (busses.add(bc) == -1) break; // finalization will be done in WLED::beginStrip() if (useGlobalLedBuffer && start + length > maxlen) {
maxlen = start + length;
globalBufMem = maxlen * 4;
}
if (mem + globalBufMem <= MAX_LED_MEMORY) if (busses.add(bc) == -1) break; // finalization will be done in WLED::beginStrip()
} else { } else {
if (busConfigs[s] != nullptr) delete busConfigs[s]; if (busConfigs[s] != nullptr) delete busConfigs[s];
busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode); busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer);
busesChanged = true; busesChanged = true;
} }
s++; s++;
@ -173,7 +178,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
doInitBusses = busesChanged; doInitBusses = busesChanged;
// finalization done in beginStrip() // finalization done in beginStrip()
} }
if (hw_led["rev"]) busses.getBus(0)->reversed = true; //set 0.11 global reversed setting for first bus if (hw_led["rev"]) busses.getBus(0)->setReversed(true); //set 0.11 global reversed setting for first bus
// read color order map configuration // read color order map configuration
JsonArray hw_com = hw[F("com")]; JsonArray hw_com = hw[F("com")];
@ -437,14 +442,14 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
#ifdef WLED_ENABLE_MQTT #ifdef WLED_ENABLE_MQTT
JsonObject if_mqtt = interfaces["mqtt"]; JsonObject if_mqtt = interfaces["mqtt"];
CJSON(mqttEnabled, if_mqtt["en"]); CJSON(mqttEnabled, if_mqtt["en"]);
getStringFromJson(mqttServer, if_mqtt[F("broker")], 33); getStringFromJson(mqttServer, if_mqtt[F("broker")], MQTT_MAX_SERVER_LEN+1);
CJSON(mqttPort, if_mqtt["port"]); // 1883 CJSON(mqttPort, if_mqtt["port"]); // 1883
getStringFromJson(mqttUser, if_mqtt[F("user")], 41); getStringFromJson(mqttUser, if_mqtt[F("user")], 41);
getStringFromJson(mqttPass, if_mqtt["psk"], 65); //normally not present due to security getStringFromJson(mqttPass, if_mqtt["psk"], 65); //normally not present due to security
getStringFromJson(mqttClientID, if_mqtt[F("cid")], 41); getStringFromJson(mqttClientID, if_mqtt[F("cid")], 41);
getStringFromJson(mqttDeviceTopic, if_mqtt[F("topics")][F("device")], 33); // "wled/test" getStringFromJson(mqttDeviceTopic, if_mqtt[F("topics")][F("device")], MQTT_MAX_TOPIC_LEN+1); // "wled/test"
getStringFromJson(mqttGroupTopic, if_mqtt[F("topics")][F("group")], 33); // "" getStringFromJson(mqttGroupTopic, if_mqtt[F("topics")][F("group")], MQTT_MAX_TOPIC_LEN+1); // ""
CJSON(retainMqttMsg, if_mqtt[F("rtn")]); CJSON(retainMqttMsg, if_mqtt[F("rtn")]);
#endif #endif
@ -711,7 +716,7 @@ void serializeConfig() {
hw_led[F("cb")] = strip.cctBlending; hw_led[F("cb")] = strip.cctBlending;
hw_led["fps"] = strip.getTargetFps(); hw_led["fps"] = strip.getTargetFps();
hw_led[F("rgbwm")] = Bus::getGlobalAWMode(); // global auto white mode override hw_led[F("rgbwm")] = Bus::getGlobalAWMode(); // global auto white mode override
hw_led[F("ld")] = strip.useLedsArray; hw_led[F("ld")] = useGlobalLedBuffer;
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
// 2D Matrix Settings // 2D Matrix Settings
@ -746,7 +751,7 @@ void serializeConfig() {
uint8_t nPins = bus->getPins(pins); uint8_t nPins = bus->getPins(pins);
for (uint8_t i = 0; i < nPins; i++) ins_pin.add(pins[i]); for (uint8_t i = 0; i < nPins; i++) ins_pin.add(pins[i]);
ins[F("order")] = bus->getColorOrder(); ins[F("order")] = bus->getColorOrder();
ins["rev"] = bus->reversed; ins["rev"] = bus->isReversed();
ins[F("skip")] = bus->skippedLeds(); ins[F("skip")] = bus->skippedLeds();
ins["type"] = bus->getType() & 0x7F; ins["type"] = bus->getType() & 0x7F;
ins["ref"] = bus->isOffRefreshRequired(); ins["ref"] = bus->isOffRefreshRequired();

View File

@ -148,6 +148,8 @@
#define USERMOD_ID_PWM_OUTPUTS 38 //Usermod "usermod_pwm_outputs.h #define USERMOD_ID_PWM_OUTPUTS 38 //Usermod "usermod_pwm_outputs.h
#define USERMOD_ID_SHT 39 //Usermod "usermod_sht.h #define USERMOD_ID_SHT 39 //Usermod "usermod_sht.h
#define USERMOD_ID_KLIPPER 40 //Usermod Klipper percentage #define USERMOD_ID_KLIPPER 40 //Usermod Klipper percentage
#define USERMOD_ID_WIREGUARD 41 //Usermod "wireguard.h"
#define USERMOD_ID_INTERNAL_TEMPERATURE 42 //Usermod "usermod_internal_temperature.h"
//Access point behavior //Access point behavior
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot #define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot

View File

@ -681,6 +681,7 @@ ${i.opt&0x100?inforow("Debug","<button class=\"btn btn-xs\" onclick=\"requestJso
${inforow("Build",i.vid)} ${inforow("Build",i.vid)}
${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")} ${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")}
${inforow("Uptime",getRuntimeStr(i.uptime))} ${inforow("Uptime",getRuntimeStr(i.uptime))}
${inforow("Time",i.time)}
${inforow("Free heap",heap," kB")} ${inforow("Free heap",heap," kB")}
${i.psram?inforow("Free PSRAM",(i.psram/1024).toFixed(1)," kB"):""} ${i.psram?inforow("Free PSRAM",(i.psram/1024).toFixed(1)," kB"):""}
${inforow("Estimated current",pwru)} ${inforow("Estimated current",pwru)}
@ -1245,7 +1246,7 @@ function updateSelectedPalette(s)
if (s > 1 && s < 6) { if (s > 1 && s < 6) {
cd[0].classList.remove('hide'); // * Color 1 cd[0].classList.remove('hide'); // * Color 1
if (s > 2) cd[1].classList.remove('hide'); // * Color 1 & 2 if (s > 2) cd[1].classList.remove('hide'); // * Color 1 & 2
if (s == 5) cd[2].classList.remove('hide'); // all colors if (s > 3) cd[2].classList.remove('hide'); // all colors
} else { } else {
for (let i of cd) if (i.dataset.hide == '1') i.classList.add('hide'); for (let i of cd) if (i.dataset.hide == '1') i.classList.add('hide');
} }

View File

@ -141,7 +141,7 @@
let len = parseInt(d.getElementsByName("LC"+n)[0].value); let len = parseInt(d.getElementsByName("LC"+n)[0].value);
len += parseInt(d.getElementsByName("SL"+n)[0].value); // skipped LEDs are allocated too len += parseInt(d.getElementsByName("SL"+n)[0].value); // skipped LEDs are allocated too
let dbl = 0; let dbl = 0;
if (d.Sf.LD.checked) dbl = len * 3; // double buffering if (d.Sf.LD.checked) dbl = len * 4; // double buffering
if (t < 32) { if (t < 32) {
if (t==26 || t==29) len *= 2; // 16 bit LEDs if (t==26 || t==29) len *= 2; // 16 bit LEDs
if (maxM < 10000 && d.getElementsByName("L0"+n)[0].value == 3) { //8266 DMA uses 5x the mem if (maxM < 10000 && d.getElementsByName("L0"+n)[0].value == 3) { //8266 DMA uses 5x the mem

View File

@ -523,6 +523,7 @@ ${urows}
${inforow("Build",i.vid)} ${inforow("Build",i.vid)}
${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")} ${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")}
${inforow("Uptime",getRuntimeStr(i.uptime))} ${inforow("Uptime",getRuntimeStr(i.uptime))}
${inforow("Time",i.time)}
${inforow("Free heap",heap," kB")} ${inforow("Free heap",heap," kB")}
${i.psram?inforow("Free PSRAM",(i.psram/1024).toFixed(1)," kB"):""} ${i.psram?inforow("Free PSRAM",(i.psram/1024).toFixed(1)," kB"):""}
${inforow("Estimated current",pwru)} ${inforow("Estimated current",pwru)}

View File

@ -9,7 +9,7 @@
// Autogenerated from wled00/data/cpal/cpal.htm, do not edit!! // Autogenerated from wled00/data/cpal/cpal.htm, do not edit!!
const uint16_t PAGE_cpal_L = 4721; const uint16_t PAGE_cpal_L = 4721;
const uint8_t PAGE_cpal[] PROGMEM = { const uint8_t PAGE_cpal[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0xbd, 0x3b, 0x7f, 0x73, 0xdb, 0xb6, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0xbd, 0x3b, 0x7f, 0x73, 0xdb, 0xb6,
0x92, 0xff, 0xe7, 0x53, 0x20, 0x4c, 0x5f, 0x42, 0xd6, 0x14, 0x45, 0xd2, 0xb6, 0x64, 0x4b, 0xa2, 0x92, 0xff, 0xe7, 0x53, 0x20, 0x4c, 0x5f, 0x42, 0xd6, 0x14, 0x45, 0xd2, 0xb6, 0x64, 0x4b, 0xa2,
0x3b, 0xa9, 0x93, 0x77, 0xce, 0x8d, 0xdd, 0x64, 0x5e, 0x7c, 0x6e, 0x7b, 0x3e, 0xbf, 0x31, 0x4d, 0x3b, 0xa9, 0x93, 0x77, 0xce, 0x8d, 0xdd, 0x64, 0x5e, 0x7c, 0x6e, 0x7b, 0x3e, 0xbf, 0x31, 0x4d,
0x42, 0x12, 0x1b, 0x8a, 0xe0, 0x03, 0x21, 0xd9, 0xae, 0xac, 0xef, 0x7e, 0xbb, 0x00, 0x48, 0x91, 0x42, 0x12, 0x1b, 0x8a, 0xe0, 0x03, 0x21, 0xd9, 0xae, 0xac, 0xef, 0x7e, 0xbb, 0x00, 0x48, 0x91,

View File

@ -43,45 +43,45 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()=====";
// Autogenerated from wled00/data/update.htm, do not edit!! // Autogenerated from wled00/data/update.htm, do not edit!!
const uint16_t PAGE_update_length = 616; const uint16_t PAGE_update_length = 616;
const uint8_t PAGE_update[] PROGMEM = { const uint8_t PAGE_update[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0x75, 0x53, 0x4d, 0x6f, 0xd4, 0x30, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0x75, 0x53, 0x5d, 0x6f, 0xd4, 0x30,
0x10, 0xbd, 0xe7, 0x57, 0x18, 0x9f, 0x76, 0x25, 0xd6, 0x2e, 0x1f, 0x17, 0x4a, 0x92, 0x42, 0x69, 0x10, 0x7c, 0xcf, 0xaf, 0x30, 0x7e, 0xba, 0x93, 0x38, 0xbb, 0x20, 0x84, 0x44, 0x49, 0x52, 0x28,
0x85, 0x2a, 0x21, 0xb5, 0x52, 0x5b, 0x10, 0x27, 0xe4, 0xd8, 0x93, 0x8d, 0x59, 0xc7, 0x4e, 0xed, 0xad, 0x50, 0x25, 0xa4, 0x56, 0x6a, 0x0b, 0xe2, 0x09, 0x39, 0xf6, 0xe6, 0x62, 0xce, 0xb1, 0x53,
0xc9, 0xae, 0x56, 0xa8, 0xff, 0x9d, 0x89, 0xb3, 0x5b, 0x10, 0x1f, 0x97, 0x28, 0xce, 0xbc, 0x79, 0x7b, 0x73, 0xa7, 0x13, 0xea, 0x7f, 0x67, 0xe3, 0xdc, 0x15, 0xc4, 0xc7, 0x4b, 0x14, 0x67, 0x67,
0x9e, 0x79, 0xef, 0xa5, 0x7c, 0x76, 0x71, 0xfd, 0xe1, 0xee, 0xeb, 0xcd, 0x25, 0xeb, 0xb0, 0x77, 0xc7, 0xbb, 0x33, 0x93, 0xf2, 0xd9, 0xc5, 0xf5, 0x87, 0xbb, 0xaf, 0x37, 0x97, 0xac, 0xc3, 0xde,
0x75, 0x79, 0x78, 0x82, 0x32, 0x75, 0xd9, 0x03, 0x2a, 0xa6, 0x83, 0x47, 0xf0, 0x58, 0xf1, 0x9d, 0xd5, 0xe5, 0xe1, 0x09, 0xca, 0xd4, 0x65, 0x0f, 0xa8, 0x98, 0x0e, 0x1e, 0xc1, 0x63, 0xc5, 0x77,
0x35, 0xd8, 0x55, 0x06, 0xb6, 0x56, 0xc3, 0x2a, 0x1f, 0x38, 0xf3, 0xaa, 0x87, 0x8a, 0x6f, 0x2d, 0xd6, 0x60, 0x57, 0x19, 0xd8, 0x5a, 0x0d, 0xab, 0x7c, 0xe0, 0xcc, 0xab, 0x1e, 0x2a, 0xbe, 0xb5,
0xec, 0x86, 0x10, 0x91, 0xd7, 0x45, 0x89, 0x16, 0x1d, 0xd4, 0x5f, 0x3e, 0x5d, 0x5e, 0xb0, 0xfb, 0xb0, 0x1b, 0x42, 0x44, 0x5e, 0x17, 0x25, 0x5a, 0x74, 0x50, 0x7f, 0xf9, 0x74, 0x79, 0xc1, 0xee,
0xc1, 0x28, 0x84, 0x52, 0xce, 0x9f, 0xca, 0xa4, 0xa3, 0x1d, 0xb0, 0x2e, 0xda, 0xd1, 0x6b, 0xb4, 0x07, 0xa3, 0x10, 0x4a, 0x39, 0x7f, 0x2a, 0x93, 0x8e, 0x76, 0xc0, 0xba, 0x68, 0x47, 0xaf, 0xd1,
0xc1, 0xb3, 0xf3, 0xc5, 0xf2, 0xc7, 0xce, 0x7a, 0x13, 0x76, 0xa2, 0xb3, 0x09, 0x43, 0xdc, 0x8b, 0x06, 0xcf, 0xce, 0x17, 0xcb, 0x1f, 0x3b, 0xeb, 0x4d, 0xd8, 0x89, 0xce, 0x26, 0x0c, 0x71, 0x2f,
0x46, 0xe9, 0xcd, 0x62, 0xf9, 0xf8, 0x04, 0xb9, 0x27, 0x88, 0x09, 0x7a, 0xec, 0x69, 0x02, 0xb1, 0x1a, 0xa5, 0x37, 0x8b, 0xe5, 0xe3, 0x13, 0xe4, 0x9e, 0x20, 0x26, 0xe8, 0xb1, 0xa7, 0x09, 0xc4,
0x06, 0xbc, 0x74, 0x30, 0xbd, 0x9e, 0xef, 0xaf, 0xcc, 0x82, 0x8f, 0x2d, 0x5f, 0x8a, 0x84, 0x7b, 0x1a, 0xf0, 0xd2, 0xc1, 0xf4, 0x7a, 0xbe, 0xbf, 0x32, 0x0b, 0x3e, 0xb6, 0x7c, 0x29, 0x12, 0xee,
0x07, 0xc2, 0xd8, 0x34, 0x38, 0xb5, 0xaf, 0xb8, 0x0f, 0x1e, 0xf8, 0xf3, 0xff, 0xb6, 0xf4, 0x69, 0x1d, 0x08, 0x63, 0xd3, 0xe0, 0xd4, 0xbe, 0xe2, 0x3e, 0x78, 0xe0, 0xcf, 0xff, 0xdb, 0xd2, 0xa7,
0xfd, 0x77, 0x4f, 0xe3, 0x82, 0xde, 0xf0, 0xc7, 0xa2, 0x94, 0x87, 0x11, 0x0f, 0xa3, 0xb2, 0x14, 0xf5, 0xdf, 0x3d, 0x8d, 0x0b, 0x7a, 0xc3, 0x1f, 0x8b, 0x52, 0x1e, 0x46, 0x3c, 0x8c, 0xca, 0x52,
0x75, 0xc5, 0x65, 0x02, 0x44, 0xeb, 0xd7, 0x49, 0x26, 0xf1, 0x3d, 0x9d, 0x0d, 0xd5, 0x1b, 0x5e, 0xd4, 0x15, 0x97, 0x09, 0x10, 0xad, 0x5f, 0x27, 0x99, 0xc4, 0xf7, 0x74, 0x36, 0x54, 0x6f, 0x78,
0xff, 0x86, 0x9c, 0xa8, 0xea, 0xe2, 0x9d, 0xed, 0x27, 0x01, 0xd8, 0x18, 0xdd, 0x82, 0xcf, 0xf4, 0xfd, 0x1b, 0x72, 0xa2, 0xaa, 0x8b, 0x77, 0xb6, 0x9f, 0x04, 0x60, 0x63, 0x74, 0x0b, 0x3e, 0xd3,
0x3a, 0x25, 0xbe, 0x7c, 0x4b, 0xc8, 0x8c, 0x28, 0xe5, 0x2c, 0x69, 0x13, 0xcc, 0x9e, 0x05, 0xef, 0xeb, 0x94, 0xf8, 0xf2, 0x2d, 0x21, 0x33, 0xa2, 0x94, 0xb3, 0xa4, 0x4d, 0x30, 0x7b, 0x16, 0xbc,
0x82, 0x32, 0x15, 0xff, 0x08, 0xf8, 0x79, 0xb1, 0x24, 0xba, 0xee, 0x65, 0x5d, 0x64, 0xc9, 0x6e, 0x0b, 0xca, 0x54, 0xfc, 0x23, 0xe0, 0xe7, 0xc5, 0x92, 0xe8, 0xba, 0x97, 0x75, 0x91, 0x25, 0xbb,
0x43, 0x8b, 0x3b, 0x15, 0xe1, 0x49, 0x3b, 0xaa, 0x94, 0x6d, 0x88, 0x3d, 0x23, 0x2f, 0xba, 0x40, 0x0d, 0x2d, 0xee, 0x54, 0x84, 0x27, 0xed, 0xa8, 0x52, 0xb6, 0x21, 0xf6, 0x8c, 0xbc, 0xe8, 0x02,
0x3d, 0x37, 0xd7, 0xb7, 0x77, 0x9c, 0xa9, 0x2c, 0x4f, 0xc5, 0x85, 0x1c, 0x33, 0x90, 0x33, 0x4b, 0xf5, 0xdc, 0x5c, 0xdf, 0xde, 0x71, 0xa6, 0xb2, 0x3c, 0x15, 0x17, 0x72, 0xcc, 0x40, 0xce, 0x2c,
0x35, 0x12, 0x84, 0x15, 0x40, 0xd2, 0xed, 0x07, 0x72, 0xa5, 0x1f, 0x1d, 0xda, 0x41, 0x45, 0x94, 0xd5, 0x48, 0x10, 0x56, 0x00, 0x49, 0xb7, 0x1f, 0xc8, 0x95, 0x7e, 0x74, 0x68, 0x07, 0x15, 0x51,
0x13, 0xc1, 0x8a, 0x60, 0x8a, 0xd3, 0xd5, 0x69, 0x6c, 0x7a, 0x4b, 0x76, 0xde, 0x4f, 0x37, 0x5f, 0x4e, 0x04, 0x2b, 0x82, 0x29, 0x4e, 0x57, 0xa7, 0xb1, 0xe9, 0x2d, 0xd9, 0x79, 0x3f, 0xdd, 0x7c,
0xf9, 0x84, 0xca, 0x39, 0x30, 0x6c, 0x0b, 0x31, 0x11, 0xe5, 0x29, 0x2b, 0xd3, 0xa0, 0x3c, 0x2b, 0xe5, 0x13, 0x2a, 0xe7, 0xc0, 0xb0, 0x2d, 0xc4, 0x44, 0x94, 0xa7, 0xac, 0x4c, 0x83, 0xf2, 0xac,
0xb4, 0x53, 0x29, 0x55, 0x3c, 0xd9, 0x81, 0xd7, 0x27, 0xe2, 0xc5, 0x6b, 0x71, 0xb2, 0x6a, 0x5e, 0xd0, 0x4e, 0xa5, 0x54, 0xf1, 0x64, 0x07, 0x5e, 0x9f, 0x88, 0x17, 0xaf, 0xc4, 0xc9, 0xaa, 0x79,
0xd1, 0x36, 0x54, 0xa4, 0x2d, 0x62, 0x7d, 0x11, 0x76, 0x79, 0x0b, 0x86, 0x1d, 0x30, 0x47, 0x23, 0x4d, 0xdb, 0x50, 0x91, 0xb6, 0x88, 0xf5, 0x45, 0xd8, 0xe5, 0x2d, 0x18, 0x76, 0xc0, 0x1c, 0x8d,
0x24, 0x64, 0x8d, 0xf5, 0x2a, 0xee, 0x89, 0x42, 0xb1, 0xa2, 0x8b, 0xd0, 0x56, 0xbc, 0x43, 0x1c, 0x90, 0x90, 0x35, 0xd6, 0xab, 0xb8, 0x27, 0x0a, 0xc5, 0x8a, 0x2e, 0x42, 0x5b, 0xf1, 0x0e, 0x71,
0xd2, 0xa9, 0x94, 0x6b, 0x8b, 0xdd, 0xd8, 0x08, 0x1d, 0x7a, 0xf9, 0xde, 0x46, 0x1d, 0x42, 0xd8, 0x48, 0xa7, 0x52, 0xae, 0x2d, 0x76, 0x63, 0x23, 0x74, 0xe8, 0xe5, 0x7b, 0x1b, 0x75, 0x08, 0x61,
0x58, 0x90, 0xd3, 0xca, 0x32, 0x82, 0x03, 0x95, 0x20, 0x71, 0x86, 0x2a, 0x92, 0x5f, 0x15, 0xff, 0x63, 0x41, 0x4e, 0x2b, 0xcb, 0x08, 0x0e, 0x54, 0x82, 0xc4, 0x19, 0xaa, 0x48, 0x7e, 0x55, 0xfc,
0xd6, 0x38, 0xe5, 0x37, 0x24, 0x8b, 0xed, 0xd7, 0xac, 0xc8, 0x26, 0x1c, 0x79, 0xe8, 0x8b, 0x48, 0x5b, 0xe3, 0x94, 0xdf, 0x90, 0x2c, 0xb6, 0x5f, 0xb3, 0x22, 0x9b, 0x70, 0xe4, 0xa1, 0x2f, 0x22,
0x9d, 0x05, 0x67, 0x92, 0xb0, 0xe1, 0x40, 0x7b, 0xa4, 0xf8, 0x93, 0x5a, 0xa4, 0xed, 0xfa, 0x2c, 0x75, 0x16, 0x9c, 0x49, 0xc2, 0x86, 0x03, 0xed, 0x91, 0xe2, 0x4f, 0x6a, 0x91, 0xb6, 0xeb, 0xb3,
0xcb, 0x5f, 0xb5, 0x34, 0xe1, 0x2a, 0x3d, 0x8c, 0x24, 0xed, 0x14, 0x52, 0xa9, 0xf2, 0x0e, 0xa5, 0x2c, 0x7f, 0xd5, 0xd2, 0x84, 0xab, 0xf4, 0x30, 0x92, 0xb4, 0x53, 0x48, 0xa5, 0xca, 0x3b, 0x94,
0xf5, 0xc3, 0x88, 0x6c, 0x96, 0xab, 0xb5, 0x0e, 0x8e, 0x81, 0x3e, 0x8a, 0x1a, 0xe1, 0x61, 0xb4, 0xd6, 0x0f, 0x23, 0xb2, 0x59, 0xae, 0xd6, 0x3a, 0x38, 0x06, 0xfa, 0x28, 0x6a, 0x84, 0x87, 0xd1,
0x11, 0xcc, 0x8c, 0x6e, 0x46, 0x44, 0xca, 0xe4, 0x0c, 0x9f, 0x65, 0x24, 0xb2, 0xd9, 0xa9, 0x67, 0x46, 0x30, 0x33, 0xba, 0x19, 0x11, 0x29, 0x93, 0x33, 0x7c, 0x96, 0x91, 0xc8, 0x66, 0xa7, 0x9e,
0xa5, 0x9c, 0xcb, 0xff, 0x80, 0xce, 0x87, 0x49, 0x7b, 0xed, 0xac, 0xde, 0x54, 0xfc, 0x7c, 0x92, 0x95, 0x72, 0x2e, 0xff, 0x03, 0x3a, 0x1f, 0x26, 0xed, 0xb5, 0xb3, 0x7a, 0x53, 0xf1, 0xf3, 0x49,
0xfe, 0x9c, 0xa2, 0xfe, 0xab, 0x29, 0x7b, 0x54, 0x97, 0xc6, 0x6e, 0x8b, 0x6c, 0xe5, 0x14, 0x54, 0xfa, 0x73, 0x8a, 0xfa, 0xaf, 0xa6, 0xec, 0x51, 0x5d, 0x1a, 0xbb, 0x2d, 0xb2, 0x95, 0x53, 0x50,
0xa2, 0xa9, 0x33, 0x3b, 0xa5, 0x4f, 0x08, 0x41, 0xe0, 0x4c, 0x7e, 0x93, 0x97, 0x65, 0x26, 0x30, 0x89, 0xa6, 0xce, 0xec, 0x94, 0x3e, 0x21, 0x04, 0x81, 0x33, 0xf9, 0x4d, 0x5e, 0x96, 0x99, 0xc0,
0x1f, 0x90, 0x69, 0x17, 0xe8, 0x10, 0x22, 0xcd, 0xda, 0x46, 0x48, 0x5d, 0xf6, 0x63, 0x50, 0x6b, 0x7c, 0x40, 0xa6, 0x5d, 0xa0, 0x43, 0x88, 0x34, 0x6b, 0x1b, 0x21, 0x75, 0xd9, 0x8f, 0x41, 0xad,
0x60, 0xa7, 0xcb, 0x52, 0x12, 0xdf, 0xb4, 0xee, 0x94, 0xba, 0x29, 0x82, 0xd3, 0xbf, 0xfd, 0x13, 0x81, 0x9d, 0x2e, 0x4b, 0x49, 0x7c, 0xd3, 0xba, 0x53, 0xea, 0xa6, 0x08, 0x4e, 0xff, 0xf6, 0x4f,
0x46, 0x22, 0xf9, 0xe1, 0xf1, 0x03, 0x00, 0x00 0x0a, 0x8a, 0xa8, 0xd6, 0xf1, 0x03, 0x00, 0x00
}; };

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -210,7 +210,7 @@ void sendImprovInfoResponse() {
//Use serverDescription if it has been changed from the default "WLED", else mDNS name //Use serverDescription if it has been changed from the default "WLED", else mDNS name
bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0); bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0);
char vString[20]; char vString[20];
sprintf_P(vString, PSTR("0.14.0-b3/%i"), VERSION); sprintf_P(vString, PSTR("0.14.0-b6/%i"), VERSION);
const char *str[4] = {"WLED", vString, bString, useMdnsName ? cmDNS : serverDescription}; const char *str[4] = {"WLED", vString, bString, useMdnsName ? cmDNS : serverDescription};
sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str); sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str);

View File

@ -22,15 +22,18 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
int stop = elem["stop"] | -1; int stop = elem["stop"] | -1;
// if using vectors use this code to append segment // append segment
if (id >= strip.getSegmentsNum()) { if (id >= strip.getSegmentsNum()) {
if (stop <= 0) return false; // ignore empty/inactive segments if (stop <= 0) return false; // ignore empty/inactive segments
strip.appendSegment(Segment(0, strip.getLengthTotal())); strip.appendSegment(Segment(0, strip.getLengthTotal()));
id = strip.getSegmentsNum()-1; // segments are added at the end of list id = strip.getSegmentsNum()-1; // segments are added at the end of list
} }
//DEBUG_PRINTLN("-- JSON deserialize segment.");
Segment& seg = strip.getSegment(id); Segment& seg = strip.getSegment(id);
//DEBUG_PRINTF("-- Original segment: %p\n", &seg);
Segment prev = seg; //make a backup so we can tell if something changed Segment prev = seg; //make a backup so we can tell if something changed
//DEBUG_PRINTF("-- Duplicate segment: %p\n", &prev);
uint16_t start = elem["start"] | seg.start; uint16_t start = elem["start"] | seg.start;
if (stop < 0) { if (stop < 0) {
@ -110,7 +113,11 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
of = offsetAbs; of = offsetAbs;
} }
if (stop > start && of > len -1) of = len -1; if (stop > start && of > len -1) of = len -1;
seg.setUp(start, stop, grp, spc, of, startY, stopY);
// update segment (delete if necessary)
// do not call seg.setUp() here, as it may cause a crash due to concurrent access if the segment is currently drawing effects
// WS2812FX handles queueing of the change
strip.setSegment(id, start, stop, grp, spc, of, startY, stopY);
if (seg.reset && seg.stop == 0) return true; // segment was deleted & is marked for reset, no need to change anything else if (seg.reset && seg.stop == 0) return true; // segment was deleted & is marked for reset, no need to change anything else
@ -343,7 +350,9 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
JsonObject udpn = root["udpn"]; JsonObject udpn = root["udpn"];
notifyDirect = udpn["send"] | notifyDirect; notifyDirect = udpn["send"] | notifyDirect;
syncGroups = udpn["sgrp"] | syncGroups;
receiveNotifications = udpn["recv"] | receiveNotifications; receiveNotifications = udpn["recv"] | receiveNotifications;
receiveGroups = udpn["rgrp"] | receiveGroups;
if ((bool)udpn[F("nn")]) callMode = CALL_MODE_NO_NOTIFY; //send no notification just for this request if ((bool)udpn[F("nn")]) callMode = CALL_MODE_NO_NOTIFY; //send no notification just for this request
unsigned long timein = root["time"] | UINT32_MAX; //backup time source if NTP not synced unsigned long timein = root["time"] | UINT32_MAX; //backup time source if NTP not synced
@ -468,12 +477,14 @@ void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset, b
if (segmentBounds) { if (segmentBounds) {
root["start"] = seg.start; root["start"] = seg.start;
root["stop"] = seg.stop; root["stop"] = seg.stop;
#ifndef WLED_DISABLE_2D
if (strip.isMatrix) { if (strip.isMatrix) {
root[F("startY")] = seg.startY; root[F("startY")] = seg.startY;
root[F("stopY")] = seg.stopY; root[F("stopY")] = seg.stopY;
} }
#endif
} }
if (!forPreset) root["len"] = (seg.stop >= seg.start) ? (seg.stop - seg.start) : 0; if (!forPreset) root["len"] = seg.stop - seg.start;
root["grp"] = seg.grouping; root["grp"] = seg.grouping;
root[F("spc")] = seg.spacing; root[F("spc")] = seg.spacing;
root[F("of")] = seg.offset; root[F("of")] = seg.offset;
@ -558,6 +569,8 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
JsonObject udpn = root.createNestedObject("udpn"); JsonObject udpn = root.createNestedObject("udpn");
udpn["send"] = notifyDirect; udpn["send"] = notifyDirect;
udpn["recv"] = receiveNotifications; udpn["recv"] = receiveNotifications;
udpn["sgrp"] = syncGroups;
udpn["rgrp"] = receiveGroups;
root[F("lor")] = realtimeOverride; root[F("lor")] = realtimeOverride;
} }
@ -729,6 +742,10 @@ void serializeInfo(JsonObject root)
#endif #endif
root[F("uptime")] = millis()/1000 + rolloverMillis*4294967; root[F("uptime")] = millis()/1000 + rolloverMillis*4294967;
char time[32];
getTimeString(time);
root[F("time")] = time;
usermods.addToJsonInfo(root); usermods.addToJsonInfo(root);
uint16_t os = 0; uint16_t os = 0;
@ -965,9 +982,10 @@ void serializeNodes(JsonObject root)
// deserializes mode data string into JsonArray // deserializes mode data string into JsonArray
void serializeModeData(JsonArray fxdata) void serializeModeData(JsonArray fxdata)
{ {
char lineBuffer[128]; char lineBuffer[256];
for (size_t i = 0; i < strip.getModeCount(); i++) { for (size_t i = 0; i < strip.getModeCount(); i++) {
strncpy_P(lineBuffer, strip.getModeData(i), 127); strncpy_P(lineBuffer, strip.getModeData(i), sizeof(lineBuffer)/sizeof(char)-1);
lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string
if (lineBuffer[0] != 0) { if (lineBuffer[0] != 0) {
char* dataPtr = strchr(lineBuffer,'@'); char* dataPtr = strchr(lineBuffer,'@');
if (dataPtr) fxdata.add(dataPtr+1); if (dataPtr) fxdata.add(dataPtr+1);
@ -978,10 +996,12 @@ void serializeModeData(JsonArray fxdata)
// deserializes mode names string into JsonArray // deserializes mode names string into JsonArray
// also removes effect data extensions (@...) from deserialised names // also removes effect data extensions (@...) from deserialised names
void serializeModeNames(JsonArray arr) { void serializeModeNames(JsonArray arr)
char lineBuffer[128]; {
char lineBuffer[256];
for (size_t i = 0; i < strip.getModeCount(); i++) { for (size_t i = 0; i < strip.getModeCount(); i++) {
strncpy_P(lineBuffer, strip.getModeData(i), 127); strncpy_P(lineBuffer, strip.getModeData(i), sizeof(lineBuffer)/sizeof(char)-1);
lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string
if (lineBuffer[0] != 0) { if (lineBuffer[0] != 0) {
char* dataPtr = strchr(lineBuffer,'@'); char* dataPtr = strchr(lineBuffer,'@');
if (dataPtr) *dataPtr = 0; // terminate mode data after name if (dataPtr) *dataPtr = 0; // terminate mode data after name
@ -1060,7 +1080,10 @@ void serveJson(AsyncWebServerRequest* request)
DEBUG_PRINTF("JSON buffer size: %u for request: %d\n", lDoc.memoryUsage(), subJson); DEBUG_PRINTF("JSON buffer size: %u for request: %d\n", lDoc.memoryUsage(), subJson);
size_t len = response->setLength(); #ifdef WLED_DEBUG
size_t len =
#endif
response->setLength();
DEBUG_PRINT(F("JSON content length: ")); DEBUG_PRINTLN(len); DEBUG_PRINT(F("JSON content length: ")); DEBUG_PRINTLN(len);
request->send(response); request->send(response);
@ -1090,9 +1113,13 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
for (size_t i= 0; i < used; i += n) for (size_t i= 0; i < used; i += n)
{ {
uint32_t c = strip.getPixelColor(i); uint32_t c = strip.getPixelColor(i);
uint8_t r = qadd8(W(c), R(c)); //add white channel to RGB channels as a simple RGBW -> RGB map uint8_t r = R(c);
uint8_t g = qadd8(W(c), G(c)); uint8_t g = G(c);
uint8_t b = qadd8(W(c), B(c)); uint8_t b = B(c);
uint8_t w = W(c);
r = scale8(qadd8(w, r), strip.getBrightness()); //R, add white channel to RGB channels as a simple RGBW -> RGB map
g = scale8(qadd8(w, g), strip.getBrightness()); //G
b = scale8(qadd8(w, b), strip.getBrightness()); //B
olen += sprintf(obuf + olen, "\"%06X\",", RGBW32(r,g,b,0)); olen += sprintf(obuf + olen, "\"%06X\",", RGBW32(r,g,b,0));
} }
olen -= 1; olen -= 1;

View File

@ -37,12 +37,12 @@ void applyValuesToSelectedSegs()
if (effectSpeed != selsegPrev.speed) {seg.speed = effectSpeed; stateChanged = true;} if (effectSpeed != selsegPrev.speed) {seg.speed = effectSpeed; stateChanged = true;}
if (effectIntensity != selsegPrev.intensity) {seg.intensity = effectIntensity; stateChanged = true;} if (effectIntensity != selsegPrev.intensity) {seg.intensity = effectIntensity; stateChanged = true;}
if (effectPalette != selsegPrev.palette) {seg.setPalette(effectPalette); stateChanged = true;} if (effectPalette != selsegPrev.palette) {seg.setPalette(effectPalette);}
if (effectCurrent != selsegPrev.mode) {seg.setMode(effectCurrent); stateChanged = true;} if (effectCurrent != selsegPrev.mode) {seg.setMode(effectCurrent);}
uint32_t col0 = RGBW32( col[0], col[1], col[2], col[3]); uint32_t col0 = RGBW32( col[0], col[1], col[2], col[3]);
uint32_t col1 = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]); uint32_t col1 = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]);
if (col0 != selsegPrev.colors[0]) {seg.setColor(0, col0); stateChanged = true;} if (col0 != selsegPrev.colors[0]) {seg.setColor(0, col0);}
if (col1 != selsegPrev.colors[1]) {seg.setColor(1, col1); stateChanged = true;} if (col1 != selsegPrev.colors[1]) {seg.setColor(1, col1);}
} }
} }
@ -161,6 +161,8 @@ void stateUpdated(byte callMode) {
void updateInterfaces(uint8_t callMode) void updateInterfaces(uint8_t callMode)
{ {
if (!interfaceUpdateCallMode || millis() - lastInterfaceUpdate < INTERFACE_UPDATE_COOLDOWN) return;
sendDataWs(); sendDataWs();
lastInterfaceUpdate = millis(); lastInterfaceUpdate = millis();
if (callMode == CALL_MODE_WS_SEND) return; if (callMode == CALL_MODE_WS_SEND) return;
@ -179,7 +181,7 @@ void updateInterfaces(uint8_t callMode)
void handleTransitions() void handleTransitions()
{ {
//handle still pending interface update //handle still pending interface update
if (interfaceUpdateCallMode && millis() - lastInterfaceUpdate > INTERFACE_UPDATE_COOLDOWN) updateInterfaces(interfaceUpdateCallMode); updateInterfaces(interfaceUpdateCallMode);
#ifndef WLED_DISABLE_MQTT #ifndef WLED_DISABLE_MQTT
if (doPublishMqtt) publishMqtt(); if (doPublishMqtt) publishMqtt();
#endif #endif
@ -187,7 +189,7 @@ void handleTransitions()
if (transitionActive && transitionDelayTemp > 0) if (transitionActive && transitionDelayTemp > 0)
{ {
float tper = (millis() - transitionStartTime)/(float)transitionDelayTemp; float tper = (millis() - transitionStartTime)/(float)transitionDelayTemp;
if (tper >= 1.0) if (tper >= 1.0f)
{ {
strip.setTransitionMode(false); strip.setTransitionMode(false);
transitionActive = false; transitionActive = false;
@ -195,7 +197,7 @@ void handleTransitions()
applyFinalBri(); applyFinalBri();
return; return;
} }
if (tper - tperLast < 0.004) return; if (tper - tperLast < 0.004f) return;
tperLast = tper; tperLast = tper;
briT = briOld + ((bri - briOld) * tper); briT = briOld + ((bri - briOld) * tper);

View File

@ -25,7 +25,7 @@ bool parseLx(int lxValue, byte* rgbw)
float tmpBri = floor((lxValue - 200000000) / 10000); ; float tmpBri = floor((lxValue - 200000000) / 10000); ;
uint16_t ct = (lxValue - 200000000) - (((uint8_t)tmpBri) * 10000); uint16_t ct = (lxValue - 200000000) - (((uint8_t)tmpBri) * 10000);
tmpBri *= 2.55; tmpBri *= 2.55f;
tmpBri = constrain(tmpBri, 0, 255); tmpBri = constrain(tmpBri, 0, 255);
colorKtoRGB(ct, rgbw); colorKtoRGB(ct, rgbw);

View File

@ -112,7 +112,7 @@ int16_t loadPlaylist(JsonObject playlistObj, byte presetId) {
if (playlistEndPreset == 255 && currentPreset > 0) playlistEndPreset = currentPreset; if (playlistEndPreset == 255 && currentPreset > 0) playlistEndPreset = currentPreset;
if (playlistEndPreset > 250) playlistEndPreset = 0; if (playlistEndPreset > 250) playlistEndPreset = 0;
shuffle = shuffle || playlistObj["r"]; shuffle = shuffle || playlistObj["r"];
if (shuffle) playlistOptions += PL_OPTION_SHUFFLE; if (shuffle) playlistOptions |= PL_OPTION_SHUFFLE;
currentPlaylist = presetId; currentPlaylist = presetId;
DEBUG_PRINTLN(F("Playlist loaded.")); DEBUG_PRINTLN(F("Playlist loaded."));
@ -156,7 +156,7 @@ void serializePlaylist(JsonObject sObj) {
JsonArray ps = playlist.createNestedArray("ps"); JsonArray ps = playlist.createNestedArray("ps");
JsonArray dur = playlist.createNestedArray("dur"); JsonArray dur = playlist.createNestedArray("dur");
JsonArray transition = playlist.createNestedArray(F("transition")); JsonArray transition = playlist.createNestedArray(F("transition"));
playlist[F("repeat")] = (playlistIndex < 0) ? playlistRepeat - 1 : playlistRepeat; // remove added repetition count (if not yet running) playlist[F("repeat")] = (playlistIndex < 0 && playlistRepeat > 0) ? playlistRepeat - 1 : playlistRepeat; // remove added repetition count (if not yet running)
playlist["end"] = playlistEndPreset; playlist["end"] = playlistEndPreset;
playlist["r"] = playlistOptions & PL_OPTION_SHUFFLE; playlist["r"] = playlistOptions & PL_OPTION_SHUFFLE;
for (int i=0; i<playlistLen; i++) { for (int i=0; i<playlistLen; i++) {

View File

@ -39,26 +39,26 @@ typedef struct message_structure {
} message_structure; } message_structure;
static int esp_now_state = ESP_NOW_STATE_UNINIT; static int esp_now_state = ESP_NOW_STATE_UNINIT;
static uint32_t last_seq = -1; static uint32_t last_seq = UINT32_MAX;
static int brightnessBeforeNightMode = NIGHT_MODE_DEACTIVATED; static int brightnessBeforeNightMode = NIGHT_MODE_DEACTIVATED;
static message_structure incoming; static message_structure incoming;
// Pulled from the IR Remote logic but reduced to 10 steps with a constant of 3 // Pulled from the IR Remote logic but reduced to 10 steps with a constant of 3
const byte brightnessSteps[] = { static const byte brightnessSteps[] = {
6, 9, 14, 22, 33, 50, 75, 113, 170, 255 6, 9, 14, 22, 33, 50, 75, 113, 170, 255
}; };
const size_t numBrightnessSteps = sizeof(brightnessSteps) / sizeof(uint8_t); static const size_t numBrightnessSteps = sizeof(brightnessSteps) / sizeof(uint8_t);
bool nightModeActive() { static bool nightModeActive() {
return brightnessBeforeNightMode != NIGHT_MODE_DEACTIVATED; return brightnessBeforeNightMode != NIGHT_MODE_DEACTIVATED;
} }
void activateNightMode() { static void activateNightMode() {
brightnessBeforeNightMode = bri; brightnessBeforeNightMode = bri;
bri = NIGHT_MODE_BRIGHTNESS; bri = NIGHT_MODE_BRIGHTNESS;
} }
bool resetNightMode() { static bool resetNightMode() {
if (!nightModeActive()) { if (!nightModeActive()) {
return false; return false;
} }
@ -68,7 +68,7 @@ bool resetNightMode() {
} }
// increment `bri` to the next `brightnessSteps` value // increment `bri` to the next `brightnessSteps` value
void brightnessUp() { static void brightnessUp() {
if (nightModeActive()) { return; } if (nightModeActive()) { return; }
// dumb incremental search is efficient enough for so few items // dumb incremental search is efficient enough for so few items
for (uint8_t index = 0; index < numBrightnessSteps; ++index) { for (uint8_t index = 0; index < numBrightnessSteps; ++index) {
@ -80,7 +80,7 @@ void brightnessUp() {
} }
// decrement `bri` to the next `brightnessSteps` value // decrement `bri` to the next `brightnessSteps` value
void brightnessDown() { static void brightnessDown() {
if (nightModeActive()) { return; } if (nightModeActive()) { return; }
// dumb incremental search is efficient enough for so few items // dumb incremental search is efficient enough for so few items
for (int index = numBrightnessSteps - 1; index >= 0; --index) { for (int index = numBrightnessSteps - 1; index >= 0; --index) {
@ -91,7 +91,7 @@ void brightnessDown() {
} }
} }
void setOn() { static void setOn() {
if (resetNightMode()) { if (resetNightMode()) {
stateUpdated(CALL_MODE_BUTTON); stateUpdated(CALL_MODE_BUTTON);
} }
@ -100,7 +100,7 @@ void setOn() {
} }
} }
void setOff() { static void setOff() {
if (resetNightMode()) { if (resetNightMode()) {
stateUpdated(CALL_MODE_BUTTON); stateUpdated(CALL_MODE_BUTTON);
} }
@ -109,7 +109,7 @@ void setOff() {
} }
} }
void presetWithFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID) { static void presetWithFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID) {
applyPresetWithFallback(presetID, CALL_MODE_BUTTON_PRESET, effectID, paletteID); applyPresetWithFallback(presetID, CALL_MODE_BUTTON_PRESET, effectID, paletteID);
} }
@ -168,7 +168,7 @@ void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
void handleRemote() { void handleRemote() {
if (enable_espnow_remote) { if (enable_espnow_remote) {
if (esp_now_state == ESP_NOW_STATE_UNINIT) { if ((esp_now_state == ESP_NOW_STATE_UNINIT) && (interfacesInited || apActive)) { // ESPNOW requires Wifi to be initialized (either STA, or AP Mode)
DEBUG_PRINTLN(F("Initializing ESP_NOW listener")); DEBUG_PRINTLN(F("Initializing ESP_NOW listener"));
// Init ESP-NOW // Init ESP-NOW
if (esp_now_init() != 0) { if (esp_now_init() != 0) {

View File

@ -91,7 +91,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
Bus::setCCTBlend(strip.cctBlending); Bus::setCCTBlend(strip.cctBlending);
Bus::setGlobalAWMode(request->arg(F("AW")).toInt()); Bus::setGlobalAWMode(request->arg(F("AW")).toInt());
strip.setTargetFps(request->arg(F("FR")).toInt()); strip.setTargetFps(request->arg(F("FR")).toInt());
strip.useLedsArray = request->hasArg(F("LD")); useGlobalLedBuffer = request->hasArg(F("LD"));
bool busesChanged = false; bool busesChanged = false;
for (uint8_t s = 0; s < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; s++) { for (uint8_t s = 0; s < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; s++) {
@ -153,7 +153,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
// actual finalization is done in WLED::loop() (removing old busses and adding new) // actual finalization is done in WLED::loop() (removing old busses and adding new)
// this may happen even before this loop is finished so we do "doInitBusses" after the loop // this may happen even before this loop is finished so we do "doInitBusses" after the loop
if (busConfigs[s] != nullptr) delete busConfigs[s]; if (busConfigs[s] != nullptr) delete busConfigs[s];
busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freqHz); busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freqHz, useGlobalLedBuffer);
busesChanged = true; busesChanged = true;
} }
//doInitBusses = busesChanged; // we will do that below to ensure all input data is processed //doInitBusses = busesChanged; // we will do that below to ensure all input data is processed
@ -347,14 +347,14 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
#ifdef WLED_ENABLE_MQTT #ifdef WLED_ENABLE_MQTT
mqttEnabled = request->hasArg(F("MQ")); mqttEnabled = request->hasArg(F("MQ"));
strlcpy(mqttServer, request->arg(F("MS")).c_str(), 33); strlcpy(mqttServer, request->arg(F("MS")).c_str(), MQTT_MAX_SERVER_LEN+1);
t = request->arg(F("MQPORT")).toInt(); t = request->arg(F("MQPORT")).toInt();
if (t > 0) mqttPort = t; if (t > 0) mqttPort = t;
strlcpy(mqttUser, request->arg(F("MQUSER")).c_str(), 41); strlcpy(mqttUser, request->arg(F("MQUSER")).c_str(), 41);
if (!isAsterisksOnly(request->arg(F("MQPASS")).c_str(), 41)) strlcpy(mqttPass, request->arg(F("MQPASS")).c_str(), 65); if (!isAsterisksOnly(request->arg(F("MQPASS")).c_str(), 41)) strlcpy(mqttPass, request->arg(F("MQPASS")).c_str(), 65);
strlcpy(mqttClientID, request->arg(F("MQCID")).c_str(), 41); strlcpy(mqttClientID, request->arg(F("MQCID")).c_str(), 41);
strlcpy(mqttDeviceTopic, request->arg(F("MD")).c_str(), 33); strlcpy(mqttDeviceTopic, request->arg(F("MD")).c_str(), MQTT_MAX_TOPIC_LEN+1);
strlcpy(mqttGroupTopic, request->arg(F("MG")).c_str(), 33); strlcpy(mqttGroupTopic, request->arg(F("MG")).c_str(), MQTT_MAX_TOPIC_LEN+1);
buttonPublishMqtt = request->hasArg(F("BM")); buttonPublishMqtt = request->hasArg(F("BM"));
retainMqttMsg = request->hasArg(F("RT")); retainMqttMsg = request->hasArg(F("RT"));
#endif #endif
@ -797,7 +797,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
if (pos > 0) { if (pos > 0) {
spcI = getNumVal(&req, pos); spcI = getNumVal(&req, pos);
} }
selseg.setUp(startI, stopI, grpI, spcI, UINT16_MAX, startY, stopY); strip.setSegment(selectedSeg, startI, stopI, grpI, spcI, UINT16_MAX, startY, stopY);
pos = req.indexOf(F("RV=")); //Segment reverse pos = req.indexOf(F("RV=")); //Segment reverse
if (pos > 0) selseg.reverse = req.charAt(pos+3) != '0'; if (pos > 0) selseg.reverse = req.charAt(pos+3) != '0';

View File

@ -133,6 +133,10 @@
#include "../usermods/wizlights/wizlights.h" #include "../usermods/wizlights/wizlights.h"
#endif #endif
#ifdef USERMOD_WIREGUARD
#include "../usermods/wireguard/wireguard.h"
#endif
#ifdef USERMOD_WORDCLOCK #ifdef USERMOD_WORDCLOCK
#include "../usermods/usermod_v2_word_clock/usermod_v2_word_clock.h" #include "../usermods/usermod_v2_word_clock/usermod_v2_word_clock.h"
#endif #endif
@ -173,6 +177,10 @@
#include "../usermods/boblight/boblight.h" #include "../usermods/boblight/boblight.h"
#endif #endif
#ifdef USERMOD_INTERNAL_TEMPERATURE
#include "../usermods/Internal_Temperature_v2/usermod_internal_temperature.h"
#endif
#if defined(WLED_USE_SD_MMC) || defined(WLED_USE_SD_SPI) #if defined(WLED_USE_SD_MMC) || defined(WLED_USE_SD_SPI)
// This include of SD.h and SD_MMC.h must happen here, else they won't be // This include of SD.h and SD_MMC.h must happen here, else they won't be
// resolved correctly (when included in mod's header only) // resolved correctly (when included in mod's header only)
@ -306,6 +314,10 @@ void registerUsermods()
usermods.add(new WizLightsUsermod()); usermods.add(new WizLightsUsermod());
#endif #endif
#ifdef USERMOD_WIREGUARD
usermods.add(new WireguardUsermod());
#endif
#ifdef USERMOD_WORDCLOCK #ifdef USERMOD_WORDCLOCK
usermods.add(new WordClockUsermod()); usermods.add(new WordClockUsermod());
#endif #endif
@ -357,4 +369,8 @@ void registerUsermods()
#ifdef USERMOD_SHT #ifdef USERMOD_SHT
usermods.add(new ShtUsermod()); usermods.add(new ShtUsermod());
#endif #endif
#ifdef USERMOD_INTERNAL_TEMPERATURE
usermods.add(new InternalTemperatureUsermod());
#endif
} }

View File

@ -148,8 +148,14 @@ bool oappendi(int i)
bool oappend(const char* txt) bool oappend(const char* txt)
{ {
uint16_t len = strlen(txt); uint16_t len = strlen(txt);
if (olen + len >= SETTINGS_STACK_BUF_SIZE) if (olen + len >= SETTINGS_STACK_BUF_SIZE) {
#ifdef WLED_DEBUG
DEBUG_PRINT(F("oappend() buffer overflow. Cannnot append "));
DEBUG_PRINT(len); DEBUG_PRINT(F(" bytes \t\""));
DEBUG_PRINT(txt); DEBUG_PRINTLN(F("\""));
#endif
return false; // buffer full return false; // buffer full
}
strcpy(obuf + olen, txt); strcpy(obuf + olen, txt);
olen += len; olen += len;
return true; return true;
@ -233,7 +239,8 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe
if (mode < strip.getModeCount()) { if (mode < strip.getModeCount()) {
char lineBuffer[256]; char lineBuffer[256];
//strcpy_P(lineBuffer, (const char*)pgm_read_dword(&(WS2812FX::_modeData[mode]))); //strcpy_P(lineBuffer, (const char*)pgm_read_dword(&(WS2812FX::_modeData[mode])));
strcpy_P(lineBuffer, strip.getModeData(mode)); strncpy_P(lineBuffer, strip.getModeData(mode), sizeof(lineBuffer)/sizeof(char)-1);
lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string
size_t len = strlen(lineBuffer); size_t len = strlen(lineBuffer);
size_t j = 0; size_t j = 0;
for (; j < maxLen && j < len; j++) { for (; j < maxLen && j < len; j++) {
@ -245,6 +252,12 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe
} else return 0; } else return 0;
} }
if (src == JSON_palette_names && mode > GRADIENT_PALETTE_COUNT) {
snprintf_P(dest, maxLen, PSTR("~ Custom %d~"), 255-mode);
dest[maxLen-1] = '\0';
return strlen(dest);
}
uint8_t qComma = 0; uint8_t qComma = 0;
bool insideQuotes = false; bool insideQuotes = false;
uint8_t printedChars = 0; uint8_t printedChars = 0;
@ -355,9 +368,9 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL
int16_t extractModeDefaults(uint8_t mode, const char *segVar) int16_t extractModeDefaults(uint8_t mode, const char *segVar)
{ {
if (mode < strip.getModeCount()) { if (mode < strip.getModeCount()) {
char lineBuffer[128] = ""; char lineBuffer[256];
strncpy_P(lineBuffer, strip.getModeData(mode), 127); strncpy_P(lineBuffer, strip.getModeData(mode), sizeof(lineBuffer)/sizeof(char)-1);
lineBuffer[127] = '\0'; // terminate string lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string
if (lineBuffer[0] != 0) { if (lineBuffer[0] != 0) {
char* startPtr = strrchr(lineBuffer, ';'); // last ";" in FX data char* startPtr = strrchr(lineBuffer, ';'); // last ";" in FX data
if (!startPtr) return -1; if (!startPtr) return -1;
@ -507,7 +520,7 @@ um_data_t* simulateSound(uint8_t simulationId)
maxVol = 31; // this gets feedback fro UI maxVol = 31; // this gets feedback fro UI
binNum = 8; // this gets feedback fro UI binNum = 8; // this gets feedback fro UI
volumeRaw = volumeSmth; volumeRaw = volumeSmth;
my_magnitude = 10000.0 / 8.0f; //no idea if 10000 is a good value for FFT_Magnitude ??? 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 if (volumeSmth < 1 ) my_magnitude = 0.001f; // noise gate closed - mute
return um_data; return um_data;

View File

@ -23,7 +23,7 @@ void WLED::reset()
#ifdef WLED_ENABLE_WEBSOCKETS #ifdef WLED_ENABLE_WEBSOCKETS
ws.closeAll(1012); ws.closeAll(1012);
#endif #endif
long dly = millis(); unsigned long dly = millis();
while (millis() - dly < 450) { while (millis() - dly < 450) {
yield(); // enough time to send response to client yield(); // enough time to send response to client
} }
@ -35,10 +35,18 @@ void WLED::reset()
void WLED::loop() void WLED::loop()
{ {
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
static unsigned long lastRun = 0;
unsigned long loopMillis = millis();
size_t loopDelay = loopMillis - lastRun;
if (lastRun == 0) loopDelay=0; // startup - don't have valid data from last run.
if (loopDelay > 2) DEBUG_PRINTF("Loop delayed more than %ums.\n", loopDelay);
static unsigned long maxLoopMillis = 0;
static size_t avgLoopMillis = 0;
static unsigned long maxUsermodMillis = 0; static unsigned long maxUsermodMillis = 0;
static uint16_t avgUsermodMillis = 0; static size_t avgUsermodMillis = 0;
static unsigned long maxStripMillis = 0; static unsigned long maxStripMillis = 0;
static uint16_t avgStripMillis = 0; static size_t avgStripMillis = 0;
unsigned long stripMillis;
#endif #endif
handleTime(); handleTime();
@ -46,7 +54,9 @@ void WLED::loop()
handleIR(); // 2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too handleIR(); // 2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too
#endif #endif
handleConnection(); handleConnection();
#ifndef WLED_DISABLE_ESPNOW
handleRemote(); handleRemote();
#endif
handleSerial(); handleSerial();
handleImprovWifiScan(); handleImprovWifiScan();
handleNotifications(); handleNotifications();
@ -80,6 +90,9 @@ void WLED::loop()
yield(); yield();
} }
#ifdef WLED_DEBUG
stripMillis = millis();
#endif
if (!realtimeMode || realtimeOverride || (realtimeMode && useMainSegmentOnly)) // block stuff if WARLS/Adalight is enabled if (!realtimeMode || realtimeOverride || (realtimeMode && useMainSegmentOnly)) // block stuff if WARLS/Adalight is enabled
{ {
if (apActive) dnsServer.processNextRequest(); if (apActive) dnsServer.processNextRequest();
@ -98,22 +111,18 @@ void WLED::loop()
handlePresets(); handlePresets();
yield(); yield();
#ifdef WLED_DEBUG
unsigned long stripMillis = millis();
#endif
if (!offMode || strip.isOffRefreshRequired()) if (!offMode || strip.isOffRefreshRequired())
strip.service(); strip.service();
#ifdef ESP8266 #ifdef ESP8266
else if (!noWifiSleep) else if (!noWifiSleep)
delay(1); //required to make sure ESP enters modem sleep (see #1184) delay(1); //required to make sure ESP enters modem sleep (see #1184)
#endif #endif
}
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
stripMillis = millis() - stripMillis; stripMillis = millis() - stripMillis;
if (stripMillis > 50) DEBUG_PRINTLN("Slow strip.");
avgStripMillis += stripMillis; avgStripMillis += stripMillis;
if (stripMillis > maxStripMillis) maxStripMillis = stripMillis; if (stripMillis > maxStripMillis) maxStripMillis = stripMillis;
#endif #endif
}
yield(); yield();
#ifdef ESP8266 #ifdef ESP8266
@ -152,11 +161,16 @@ void WLED::loop()
DEBUG_PRINTLN(F("Re-init busses.")); DEBUG_PRINTLN(F("Re-init busses."));
bool aligned = strip.checkSegmentAlignment(); //see if old segments match old bus(ses) bool aligned = strip.checkSegmentAlignment(); //see if old segments match old bus(ses)
busses.removeAll(); busses.removeAll();
uint32_t mem = 0; uint32_t mem = 0, globalBufMem = 0;
uint16_t maxlen = 0;
for (uint8_t i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) { for (uint8_t i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
if (busConfigs[i] == nullptr) break; if (busConfigs[i] == nullptr) break;
mem += BusManager::memUsage(*busConfigs[i]); mem += BusManager::memUsage(*busConfigs[i]);
if (mem <= MAX_LED_MEMORY) { if (useGlobalLedBuffer && busConfigs[i]->start + busConfigs[i]->count > maxlen) {
maxlen = busConfigs[i]->start + busConfigs[i]->count;
globalBufMem = maxlen * 4;
}
if (mem + globalBufMem <= MAX_LED_MEMORY) {
busses.add(*busConfigs[i]); busses.add(*busConfigs[i]);
} }
delete busConfigs[i]; busConfigs[i] = nullptr; delete busConfigs[i]; busConfigs[i] = nullptr;
@ -177,8 +191,34 @@ void WLED::loop()
handleWs(); handleWs();
handleStatusLED(); handleStatusLED();
toki.resetTick();
#if WLED_WATCHDOG_TIMEOUT > 0
// we finished our mainloop, reset the watchdog timer
static unsigned long lastWDTFeed = 0;
if (!strip.isUpdating() || millis() - lastWDTFeed > (WLED_WATCHDOG_TIMEOUT*500)) {
#ifdef ARDUINO_ARCH_ESP32
esp_task_wdt_reset();
#else
ESP.wdtFeed();
#endif
lastWDTFeed = millis();
}
#endif
if (doReboot && (!doInitBusses || !doSerializeConfig)) // if busses have to be inited & saved, wait until next iteration
reset();
// DEBUG serial logging (every 30s) // DEBUG serial logging (every 30s)
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
loopMillis = millis() - loopMillis;
if (loopMillis > 30) {
DEBUG_PRINTF("Loop took %lums.\n", loopMillis);
DEBUG_PRINTF("Usermods took %lums.\n", usermodMillis);
DEBUG_PRINTF("Strip took %lums.\n", stripMillis);
}
avgLoopMillis += loopMillis;
if (loopMillis > maxLoopMillis) maxLoopMillis = loopMillis;
if (millis() - debugTime > 29999) { if (millis() - debugTime > 29999) {
DEBUG_PRINTLN(F("---DEBUG INFO---")); DEBUG_PRINTLN(F("---DEBUG INFO---"));
DEBUG_PRINT(F("Runtime: ")); DEBUG_PRINTLN(millis()); DEBUG_PRINT(F("Runtime: ")); DEBUG_PRINTLN(millis());
@ -201,11 +241,13 @@ void WLED::loop()
DEBUG_PRINT(F("Client IP: ")); DEBUG_PRINTLN(Network.localIP()); DEBUG_PRINT(F("Client IP: ")); DEBUG_PRINTLN(Network.localIP());
if (loops > 0) { // avoid division by zero if (loops > 0) { // avoid division by zero
DEBUG_PRINT(F("Loops/sec: ")); DEBUG_PRINTLN(loops / 30); DEBUG_PRINT(F("Loops/sec: ")); DEBUG_PRINTLN(loops / 30);
DEBUG_PRINT(F("Loop time[ms]: ")); DEBUG_PRINT(avgLoopMillis/loops); DEBUG_PRINT("/");DEBUG_PRINTLN(maxLoopMillis);
DEBUG_PRINT(F("UM time[ms]: ")); DEBUG_PRINT(avgUsermodMillis/loops); DEBUG_PRINT("/");DEBUG_PRINTLN(maxUsermodMillis); DEBUG_PRINT(F("UM time[ms]: ")); DEBUG_PRINT(avgUsermodMillis/loops); DEBUG_PRINT("/");DEBUG_PRINTLN(maxUsermodMillis);
DEBUG_PRINT(F("Strip time[ms]: ")); DEBUG_PRINT(avgStripMillis/loops); DEBUG_PRINT("/"); DEBUG_PRINTLN(maxStripMillis); DEBUG_PRINT(F("Strip time[ms]: ")); DEBUG_PRINT(avgStripMillis/loops); DEBUG_PRINT("/"); DEBUG_PRINTLN(maxStripMillis);
} }
strip.printSize(); strip.printSize();
loops = 0; loops = 0;
maxLoopMillis = 0;
maxUsermodMillis = 0; maxUsermodMillis = 0;
maxStripMillis = 0; maxStripMillis = 0;
avgUsermodMillis = 0; avgUsermodMillis = 0;
@ -213,21 +255,8 @@ void WLED::loop()
debugTime = millis(); debugTime = millis();
} }
loops++; loops++;
lastRun = millis();
#endif // WLED_DEBUG #endif // WLED_DEBUG
toki.resetTick();
#if WLED_WATCHDOG_TIMEOUT > 0
// we finished our mainloop, reset the watchdog timer
if (!strip.isUpdating())
#ifdef ARDUINO_ARCH_ESP32
esp_task_wdt_reset();
#else
ESP.wdtFeed();
#endif
#endif
if (doReboot && (!doInitBusses || !doSerializeConfig)) // if busses have to be inited & saved, wait until next iteration
reset();
} }
void WLED::enableWatchdog() { void WLED::enableWatchdog() {
@ -512,7 +541,7 @@ void WLED::initAP(bool resetAP)
DEBUG_PRINTLN(apSSID); DEBUG_PRINTLN(apSSID);
WiFi.softAPConfig(IPAddress(4, 3, 2, 1), IPAddress(4, 3, 2, 1), IPAddress(255, 255, 255, 0)); 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);
#if defined(LOLIN_WIFI_FIX) && (defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32S2)) #if defined(LOLIN_WIFI_FIX) && (defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32S3))
WiFi.setTxPower(WIFI_POWER_8_5dBm); WiFi.setTxPower(WIFI_POWER_8_5dBm);
#endif #endif
@ -690,7 +719,7 @@ void WLED::initConnection()
WiFi.begin(clientSSID, clientPass); WiFi.begin(clientSSID, clientPass);
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
#if defined(LOLIN_WIFI_FIX) && (defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32S2)) #if defined(LOLIN_WIFI_FIX) && (defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32S3))
WiFi.setTxPower(WIFI_POWER_8_5dBm); WiFi.setTxPower(WIFI_POWER_8_5dBm);
#endif #endif
WiFi.setSleep(!noWifiSleep); WiFi.setSleep(!noWifiSleep);

View File

@ -3,12 +3,12 @@
/* /*
Main sketch, global variable declarations Main sketch, global variable declarations
@title WLED project sketch @title WLED project sketch
@version 0.14.0-b3 @version 0.14.0-b4
@author Christian Schwinne @author Christian Schwinne
*/ */
// version code in format yymmddb (b = daily build) // version code in format yymmddb (b = daily build)
#define VERSION 2307130 #define VERSION 2309241
//uncomment this if you have a "my_config.h" file you'd like to use //uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG //#define WLED_USE_MY_CONFIG
@ -332,6 +332,11 @@ WLED_GLOBAL byte bootPreset _INIT(0); // save preset to load
//if false, only one segment spanning the total LEDs is created, //if false, only one segment spanning the total LEDs is created,
//but not on LED settings save if there is more than one segment currently //but not on LED settings save if there is more than one segment currently
WLED_GLOBAL bool autoSegments _INIT(false); WLED_GLOBAL bool autoSegments _INIT(false);
#ifdef ESP8266
WLED_GLOBAL bool useGlobalLedBuffer _INIT(false); // double buffering disabled on ESP8266
#else
WLED_GLOBAL bool useGlobalLedBuffer _INIT(true); // double buffering enabled on ESP32
#endif
WLED_GLOBAL bool correctWB _INIT(false); // CCT color correction of RGB color WLED_GLOBAL bool correctWB _INIT(false); // CCT color correction of RGB color
WLED_GLOBAL bool cctFromRgb _INIT(false); // CCT is calculated from RGB instead of using seg.cct WLED_GLOBAL bool cctFromRgb _INIT(false); // CCT is calculated from RGB instead of using seg.cct
WLED_GLOBAL bool gammaCorrectCol _INIT(true); // use gamma correction on colors WLED_GLOBAL bool gammaCorrectCol _INIT(true); // use gamma correction on colors
@ -424,12 +429,18 @@ WLED_GLOBAL uint16_t pollReplyCount _INIT(0); // count numbe
// mqtt // mqtt
WLED_GLOBAL unsigned long lastMqttReconnectAttempt _INIT(0); // used for other periodic tasks too WLED_GLOBAL unsigned long lastMqttReconnectAttempt _INIT(0); // used for other periodic tasks too
#ifndef WLED_DISABLE_MQTT #ifndef WLED_DISABLE_MQTT
#ifndef MQTT_MAX_TOPIC_LEN
#define MQTT_MAX_TOPIC_LEN 32
#endif
#ifndef MQTT_MAX_SERVER_LEN
#define MQTT_MAX_SERVER_LEN 32
#endif
WLED_GLOBAL AsyncMqttClient *mqtt _INIT(NULL); WLED_GLOBAL AsyncMqttClient *mqtt _INIT(NULL);
WLED_GLOBAL bool mqttEnabled _INIT(false); WLED_GLOBAL bool mqttEnabled _INIT(false);
WLED_GLOBAL char mqttStatusTopic[40] _INIT(""); // this must be global because of async handlers WLED_GLOBAL char mqttStatusTopic[40] _INIT(""); // this must be global because of async handlers
WLED_GLOBAL char mqttDeviceTopic[33] _INIT(""); // main MQTT topic (individual per device, default is wled/mac) WLED_GLOBAL char mqttDeviceTopic[MQTT_MAX_TOPIC_LEN+1] _INIT(""); // main MQTT topic (individual per device, default is wled/mac)
WLED_GLOBAL char mqttGroupTopic[33] _INIT("wled/all"); // second MQTT topic (for example to group devices) WLED_GLOBAL char mqttGroupTopic[MQTT_MAX_TOPIC_LEN+1] _INIT("wled/all"); // second MQTT topic (for example to group devices)
WLED_GLOBAL char mqttServer[33] _INIT(""); // both domains and IPs should work (no SSL) WLED_GLOBAL char mqttServer[MQTT_MAX_SERVER_LEN+1] _INIT(""); // both domains and IPs should work (no SSL)
WLED_GLOBAL char mqttUser[41] _INIT(""); // optional: username for MQTT auth WLED_GLOBAL char mqttUser[41] _INIT(""); // optional: username for MQTT auth
WLED_GLOBAL char mqttPass[65] _INIT(""); // optional: password for MQTT auth WLED_GLOBAL char mqttPass[65] _INIT(""); // optional: password for MQTT auth
WLED_GLOBAL char mqttClientID[41] _INIT(""); // override the client ID WLED_GLOBAL char mqttClientID[41] _INIT(""); // override the client ID
@ -508,7 +519,7 @@ WLED_GLOBAL uint16_t userVar0 _INIT(0), userVar1 _INIT(0); //available for use i
// wifi // wifi
WLED_GLOBAL bool apActive _INIT(false); WLED_GLOBAL bool apActive _INIT(false);
WLED_GLOBAL bool forceReconnect _INIT(false); WLED_GLOBAL bool forceReconnect _INIT(false);
WLED_GLOBAL uint32_t lastReconnectAttempt _INIT(0); WLED_GLOBAL unsigned long lastReconnectAttempt _INIT(0);
WLED_GLOBAL bool interfacesInited _INIT(false); WLED_GLOBAL bool interfacesInited _INIT(false);
WLED_GLOBAL bool wasConnected _INIT(false); WLED_GLOBAL bool wasConnected _INIT(false);

View File

@ -41,7 +41,7 @@ float sin_t(float x) {
float tan_t(float x) { float tan_t(float x) {
float c = cos_t(x); float c = cos_t(x);
if (c==0.0) return 0; if (c==0.0f) return 0;
float res = sin_t(x) / c; float res = sin_t(x) / c;
#ifdef WLED_DEBUG_MATH #ifdef WLED_DEBUG_MATH
Serial.printf("tan: %f,%f,%f,(%f)\n",x,res,tan(x),res-tan(x)); Serial.printf("tan: %f,%f,%f,(%f)\n",x,res,tan(x),res-tan(x));
@ -54,14 +54,14 @@ float tan_t(float x) {
float acos_t(float x) { float acos_t(float x) {
float negate = float(x < 0); float negate = float(x < 0);
float xabs = std::abs(x); float xabs = std::abs(x);
float ret = -0.0187293; float ret = -0.0187293f;
ret = ret * xabs; ret = ret * xabs;
ret = ret + 0.0742610; ret = ret + 0.0742610f;
ret = ret * xabs; ret = ret * xabs;
ret = ret - 0.2121144; ret = ret - 0.2121144f;
ret = ret * xabs; ret = ret * xabs;
ret = ret + HALF_PI; ret = ret + HALF_PI;
ret = ret * sqrt(1.0-xabs); ret = ret * sqrt(1.0f-xabs);
ret = ret - 2 * negate * ret; ret = ret - 2 * negate * ret;
float res = negate * PI + ret; float res = negate * PI + ret;
#ifdef WLED_DEBUG_MATH #ifdef WLED_DEBUG_MATH

View File

@ -166,23 +166,24 @@ bool sendLiveLedsWs(uint32_t wsClient)
size_t n = ((used -1)/MAX_LIVE_LEDS_WS) +1; //only serve every n'th LED if count over MAX_LIVE_LEDS_WS size_t n = ((used -1)/MAX_LIVE_LEDS_WS) +1; //only serve every n'th LED if count over MAX_LIVE_LEDS_WS
size_t pos = (strip.isMatrix ? 4 : 2); // start of data size_t pos = (strip.isMatrix ? 4 : 2); // start of data
size_t bufSize = pos + (used/n)*3; size_t bufSize = pos + (used/n)*3;
size_t skipLines = 0;
AsyncWebSocketMessageBuffer * wsBuf = ws.makeBuffer(bufSize); AsyncWebSocketMessageBuffer * wsBuf = ws.makeBuffer(bufSize);
if (!wsBuf) return false; //out of memory if (!wsBuf) return false; //out of memory
uint8_t* buffer = wsBuf->get(); uint8_t* buffer = wsBuf->get();
buffer[0] = 'L'; buffer[0] = 'L';
buffer[1] = 1; //version buffer[1] = 1; //version
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
size_t skipLines = 0;
if (strip.isMatrix) { if (strip.isMatrix) {
buffer[1] = 2; //version buffer[1] = 2; //version
buffer[2] = Segment::maxWidth; buffer[2] = Segment::maxWidth;
buffer[3] = Segment::maxHeight; buffer[3] = Segment::maxHeight;
if (Segment::maxWidth * Segment::maxHeight > MAX_LIVE_LEDS_WS*4) { if (used > MAX_LIVE_LEDS_WS*4) {
buffer[2] = Segment::maxWidth/4; buffer[2] = Segment::maxWidth/4;
buffer[3] = Segment::maxHeight/4; buffer[3] = Segment::maxHeight/4;
skipLines = 3; skipLines = 3;
} else if (Segment::maxWidth * Segment::maxHeight > MAX_LIVE_LEDS_WS) { } else if (used > MAX_LIVE_LEDS_WS) {
buffer[2] = Segment::maxWidth/2; buffer[2] = Segment::maxWidth/2;
buffer[3] = Segment::maxHeight/2; buffer[3] = Segment::maxHeight/2;
skipLines = 1; skipLines = 1;
@ -198,9 +199,13 @@ bool sendLiveLedsWs(uint32_t wsClient)
} }
#endif #endif
uint32_t c = strip.getPixelColor(i); uint32_t c = strip.getPixelColor(i);
buffer[pos++] = qadd8(W(c), R(c)); //R, add white channel to RGB channels as a simple RGBW -> RGB map uint8_t r = R(c);
buffer[pos++] = qadd8(W(c), G(c)); //G uint8_t g = G(c);
buffer[pos++] = qadd8(W(c), B(c)); //B uint8_t b = B(c);
uint8_t w = W(c);
buffer[pos++] = scale8(qadd8(w, r), strip.getBrightness()); //R, add white channel to RGB channels as a simple RGBW -> RGB map
buffer[pos++] = scale8(qadd8(w, g), strip.getBrightness()); //G
buffer[pos++] = scale8(qadd8(w, b), strip.getBrightness()); //B
} }
wsc->binary(wsBuf); wsc->binary(wsBuf);

View File

@ -355,7 +355,7 @@ void getSettingsJS(byte subPage, char* dest)
sappend('v',SET_F("CB"),strip.cctBlending); sappend('v',SET_F("CB"),strip.cctBlending);
sappend('v',SET_F("FR"),strip.getTargetFps()); sappend('v',SET_F("FR"),strip.getTargetFps());
sappend('v',SET_F("AW"),Bus::getGlobalAWMode()); sappend('v',SET_F("AW"),Bus::getGlobalAWMode());
sappend('c',SET_F("LD"),strip.useLedsArray); sappend('c',SET_F("LD"),useGlobalLedBuffer);
for (uint8_t s=0; s < busses.getNumBusses(); s++) { for (uint8_t s=0; s < busses.getNumBusses(); s++) {
Bus* bus = busses.getBus(s); Bus* bus = busses.getBus(s);
@ -382,7 +382,7 @@ void getSettingsJS(byte subPage, char* dest)
sappend('v',lt,bus->getType()); sappend('v',lt,bus->getType());
sappend('v',co,bus->getColorOrder() & 0x0F); sappend('v',co,bus->getColorOrder() & 0x0F);
sappend('v',ls,bus->getStart()); sappend('v',ls,bus->getStart());
sappend('c',cv,bus->reversed); sappend('c',cv,bus->isReversed());
sappend('v',sl,bus->skippedLeds()); sappend('v',sl,bus->skippedLeds());
sappend('c',rf,bus->isOffRefreshRequired()); sappend('c',rf,bus->isOffRefreshRequired());
sappend('v',aw,bus->getAutoWhiteMode()); sappend('v',aw,bus->getAutoWhiteMode());
@ -478,6 +478,7 @@ void getSettingsJS(byte subPage, char* dest)
if (subPage == SUBPAGE_SYNC) if (subPage == SUBPAGE_SYNC)
{ {
char nS[32];
sappend('v',SET_F("UP"),udpPort); sappend('v',SET_F("UP"),udpPort);
sappend('v',SET_F("U2"),udpPort2); sappend('v',SET_F("U2"),udpPort2);
sappend('v',SET_F("GS"),syncGroups); sappend('v',SET_F("GS"),syncGroups);
@ -534,6 +535,9 @@ void getSettingsJS(byte subPage, char* dest)
sappends('s',SET_F("MG"),mqttGroupTopic); sappends('s',SET_F("MG"),mqttGroupTopic);
sappend('c',SET_F("BM"),buttonPublishMqtt); sappend('c',SET_F("BM"),buttonPublishMqtt);
sappend('c',SET_F("RT"),retainMqttMsg); sappend('c',SET_F("RT"),retainMqttMsg);
oappend(SET_F("d.Sf.MD.maxlength=")); oappend(itoa(MQTT_MAX_TOPIC_LEN,nS,10)); oappend(SET_F(";"));
oappend(SET_F("d.Sf.MG.maxlength=")); oappend(itoa(MQTT_MAX_TOPIC_LEN,nS,10)); oappend(SET_F(";"));
oappend(SET_F("d.Sf.MS.maxlength=")); oappend(itoa(MQTT_MAX_SERVER_LEN,nS,10)); oappend(SET_F(";"));
#else #else
oappend(SET_F("toggle('MQTT');")); // hide MQTT settings oappend(SET_F("toggle('MQTT');")); // hide MQTT settings
#endif #endif
@ -583,7 +587,7 @@ void getSettingsJS(byte subPage, char* dest)
sappends('s',SET_F("LT"),tm); sappends('s',SET_F("LT"),tm);
getTimeString(tm); getTimeString(tm);
sappends('m',SET_F("(\"times\")[0]"),tm); sappends('m',SET_F("(\"times\")[0]"),tm);
if ((int)(longitude*10.) || (int)(latitude*10.)) { if ((int)(longitude*10.0f) || (int)(latitude*10.0f)) {
sprintf_P(tm, PSTR("Sunrise: %02d:%02d Sunset: %02d:%02d"), hour(sunrise), minute(sunrise), hour(sunset), minute(sunset)); sprintf_P(tm, PSTR("Sunrise: %02d:%02d Sunset: %02d:%02d"), hour(sunrise), minute(sunrise), hour(sunset), minute(sunset));
sappends('m',SET_F("(\"times\")[1]"),tm); sappends('m',SET_F("(\"times\")[1]"),tm);
} }