diff --git a/.gitignore b/.gitignore
index 789de0a9e..c85fae0c2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,21 +1,24 @@
-.pio
.cache
+.clang-format
+.direnv
+.DS_Store
+.gitignore
+.idea
+.pio
.pioenvs
.piolibdeps
.vscode
-/wled00/Release
-/wled00/extLibs
-/platformio_override.ini
-/wled00/my_config.h
-/build_output
-.DS_Store
-.gitignore
-.clang-format
-node_modules
-.idea
-.direnv
-wled-update.sh
+
esp01-update.sh
-/wled00/LittleFS
+platformio_override.ini
replace_fs.py
-wled00/wled00.ino.cpp
+wled-update.sh
+
+/build_output/
+/node_modules/
+
+/wled00/extLibs
+/wled00/LittleFS
+/wled00/my_config.h
+/wled00/Release
+/wled00/wled00.ino.cpp
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e2900d9f5..ba6000dd5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,105 @@
## WLED changelog
-#### Build 2306180
+#### Build 2401141
+- Official release of WLED 0.14.1
+- Fix for #3566, #3665, #3672
+- Sorting of palettes in custom palette editor (#3674 by @WoodyLetsCode)
+#### Build 2401060
+- Version bump: 0.14.1-b3
+- Global JSON buffer guarding (#3648 by @willmmiles, resolves #3641, #3312, #3367, #3637, #3646, #3447)
+- Fix for #3632
+- Custom palette editor mobile UI enhancement (#3617 by @imeszaros)
+- changelog update
+
+#### Build 2312290
+- Fix for #3622, #3613, #3609
+- Various tweaks and fixes
+- changelog update
+
+#### Build 2312230
+- Version bump: 0.14.1-b2
+- Fix for Pixel Magic button
+- Fix for #2922 (option to force WiFi PHY mode to G on ESP8266)
+- Fix for #3601, #3400 (incorrect sunrise/sunset, #3612 by @softhack007)
+
+#### Build 2312180
+- Bugfixes (#3593, #3490, #3573, #3517, #3561, #3555, #3541, #3536, #3515, #3522, #3533, #3508)
+- Various other internal cleanups and optimisations
+
+#### Build 2311160
+- Version bump: 0.14.1-b1
+- Bugfixes (#3526, #3502, #3496, #3484, #3487, #3445, #3466, #3296, #3382, #3312)
+- New feature: Sort presets by ID
+- New usermod: LDR sensor (#3490 by @JeffWDH)
+- Effect: Twinklefox & Tinklecat metadata fix
+- Effect: separate #HH and #MM for Scrolling Text (#3480)
+- SSDR usermod enhancements (#3368)
+- PWM fan usermod enhancements (#3414)
+
+#### Build 2310010, build 2310130
+- Release of WLED version 0.14.0 "Hoshi"
+- Bugfixes for #3400, #3403, #3405
+- minor HTML optimizations
+- audioreactive: bugfix for UDP sound sync (partly initialized packets)
+
+#### 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
+- larger `oappend()` stack buffer (3.5k) for ESP32
+- Preset cycle bugfix (#3262)
+- Rotary encoder ALT fix for large LED count (#3276)
+- effect updates (2D Plasmaball), `blur()` speedup
+- 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)
+
+#### Build 2306270
+- ESP-NOW remote support (#3237)
+- Pixel Magic tool (display pixel art) (#3249)
+- Websocket (peek) fallback when connection cannot be established, WS retries (#3267)
+- Add WiFi network scan RPC command to Improv Serial (#3271)
+- Longer (custom option available) segment name for ESP32
+- various fixes and improvements
+
+#### Build 2306210
+- 0.14.0-b3 release
+- 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)
+
+#### Build 2306180
- Added client-side option for applying effect defaults from metadata
- Improved ESP8266 stability by reducing WebSocket response resends
- Updated ESP8266 core to 3.1.2
@@ -24,7 +122,7 @@
#### Build 2306020
- 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)
- Changes for allowing Alexa to change light color to White when auto-calculating from RGB (PR #3211)
@@ -361,7 +459,7 @@
- Added application level pong websockets reply (#2139)
- Use AsyncTCP 1.0.3 as it mitigates the flickering issue from 0.13.0-b2
-- Fixed transition manually updated in preset overriden by field value
+- Fixed transition manually updated in preset overridden by field value
#### Build 2108050
@@ -890,7 +988,7 @@
#### Build 2011040
-- Inversed Rain direction (fixes #1147)
+- Inverted Rain direction (fixes #1147)
#### Build 2011010
@@ -1101,7 +1199,7 @@
- Added module info page to web UI
- Added realtime override functionality to web UI
-- Added individial segment power and brightness to web UI
+- Added individual segment power and brightness to web UI
- Added feature to one-click select single segment only by tapping segment name
- Removed palette jumping to default if color is changed
diff --git a/package-lock.json b/package-lock.json
index a11554735..9b3fa3632 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "wled",
- "version": "0.14.0-b3",
+ "version": "0.14.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -1409,9 +1409,9 @@
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+ "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="
},
"semver-diff": {
"version": "2.1.0",
diff --git a/package.json b/package.json
index d57c87d5d..eaf0996d0 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "wled",
- "version": "0.14.0-b3",
+ "version": "0.14.1",
"description": "Tools for WLED project",
"main": "tools/cdata.js",
"directories": {
diff --git a/platformio.ini b/platformio.ini
index d3b71d3c4..d7ed06eda 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -11,7 +11,7 @@
# 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, 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
; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB
@@ -42,6 +42,7 @@ default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_
; default_envs = esp32s2_saola
; default_envs = esp32c3dev
; default_envs = lolin_s2_mini
+; default_envs = esp32s3dev_16MB_PSRAM_opi
src_dir = ./wled00
data_dir = ./wled00/data
@@ -87,11 +88,13 @@ platform_packages = platformio/framework-arduinoespressif8266
# ------------------------------------------------------------------------------
# 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
-#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
+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"
+ # -DDEBUG_ESP_CORE is not working right now
# ------------------------------------------------------------------------------
# FLAGS: ldscript (available ldscripts at https://github.com/esp8266/Arduino/tree/master/tools/sdk/ld)
@@ -175,7 +178,7 @@ upload_speed = 115200
# ------------------------------------------------------------------------------
lib_compat_mode = strict
lib_deps =
- fastled/FastLED @ 3.5.0
+ fastled/FastLED @ 3.6.0
IRremoteESP8266 @ 2.8.2
makuna/NeoPixelBus @ 2.7.5
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.7
@@ -201,7 +204,7 @@ build_flags =
-DESP8266
-DFP_IN_IROM
;-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
-Wno-misleading-indentation
; NONOSDK22x_190703 = 2.2.2-dev(38a443e)
@@ -214,6 +217,9 @@ build_flags =
-DVTABLES_IN_FLASH
; restrict to minimal mime-types
-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 =
#https://github.com/lorol/LITTLEFS.git
@@ -224,9 +230,7 @@ lib_deps =
[esp32]
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip
platform = espressif32@3.5.0
-
platform_packages = framework-arduinoespressif32 @ https://github.com/Aircoookie/arduino-esp32.git#1.0.6.4
-
build_flags = -g
-DARDUINO_ARCH_ESP32
#-DCONFIG_LITTLEFS_FOR_IDF_3_2
@@ -234,14 +238,14 @@ build_flags = -g
#use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x
-D LOROL_LITTLEFS
; -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
-
lib_deps =
https://github.com/lorol/LITTLEFS.git
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${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]
;; experimental build environment for ESP32 using ESP-IDF 4.4.x / arduino-esp32 v2.0.5
@@ -249,9 +253,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.
;; 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 =
- 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
-Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one
-DARDUINO_ARCH_ESP32 -DESP32
@@ -265,9 +268,8 @@ lib_deps =
[esp32s2]
;; generic definitions for all ESP32-S2 boards
-platform = espressif32@5.2.0
+platform = espressif32@5.3.0
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
-DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32S2
@@ -285,9 +287,8 @@ lib_deps =
[esp32c3]
;; generic definitions for all ESP32-C3 boards
-platform = espressif32@5.2.0
+platform = espressif32@5.3.0
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
-DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32C3
@@ -304,9 +305,8 @@ lib_deps =
[esp32s3]
;; generic definitions for all ESP32-S3 boards
-platform = espressif32@5.2.0
+platform = espressif32@5.3.0
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
-DESP32
-DARDUINO_ARCH_ESP32
@@ -353,6 +353,7 @@ platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA
+ ; -D WLED_USE_UNREAL_MATH ;; may cause wrong sunset/sunrise times, but saves 7064 bytes FLASH and 975 bytes RAM
lib_deps = ${esp8266.lib_deps}
[env:esp07]
@@ -403,6 +404,20 @@ lib_deps = ${esp32.lib_deps}
monitor_filters = esp32_exception_decoder
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]
board = esp32dev
platform = ${esp32.platform}
@@ -437,6 +452,7 @@ platform_packages = ${esp32.platform_packages}
upload_speed = 921600
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1
+ -D WLED_DISABLE_ESPNOW ;; ESP-NOW requires wifi, may crash with ethernet only
lib_deps = ${esp32.lib_deps}
board_build.partitions = ${esp32.default_partitions}
@@ -450,6 +466,7 @@ board_build.flash_mode = qio
upload_speed = 460800
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=S2_saola
+ ;-DLOLIN_WIFI_FIX ;; try this in case Wifi does not work
-DARDUINO_USB_CDC_ON_BOOT=1
lib_deps = ${esp32s2.lib_deps}
@@ -460,8 +477,9 @@ platform_packages = ${esp32c3.platform_packages}
framework = arduino
board = esp32-c3-devkitm-1
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
+ -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=0 ;; for serial-to-USB chip
upload_speed = 460800
@@ -475,10 +493,10 @@ platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600 ; or 460800
build_unflags = ${common.build_unflags}
-build_flags = ${common.build_flags} ${esp32s3.build_flags}
+build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_8MB
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
- ;-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=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
lib_deps = ${esp32s3.lib_deps}
board_build.partitions = tools/WLED_ESP32_8MB.csv
@@ -487,19 +505,18 @@ board_build.flash_mode = qio
; board_build.flash_mode = dio ;; try this if you have problems at startup
monitor_filters = esp32_exception_decoder
-[env:esp32s3dev_8MB_PSRAM]
-;; ESP32-TinyS3 development board, with 8MB FLASH and 8MB PSRAM (memory_type: qio_opi, qio_qspi, or opi_opi)
-;board = um_tinys3 ; -> needs workaround from https://github.com/Aircoookie/WLED/pull/2905#issuecomment-1328049860
-;board = esp32s3box ; -> error: 'esp32_adc2gpio' was not declared in this scope
-board = esp32-s3-devkitc-1 ; -> compiles, but does not support PSRAM
+[env:esp32s3dev_8MB_PSRAM_opi]
+;; ESP32-S3 development board, with 8MB FLASH and >= 8MB PSRAM (memory_type: qio_opi)
+board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support
+board_build.arduino.memory_type = qio_opi ;; use with PSRAM: 8MB or 16MB
platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags}
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
- ;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
- -D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=0 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
+ ;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
+ -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
; -D WLED_RELEASE_NAME=ESP32-S3_PSRAM
-D WLED_USE_PSRAM -DBOARD_HAS_PSRAM ; tells WLED that PSRAM shall be used
lib_deps = ${esp32s3.lib_deps}
@@ -508,6 +525,18 @@ board_build.f_flash = 80000000L
board_build.flash_mode = qio
monitor_filters = esp32_exception_decoder
+[env:esp32s3dev_16MB_PSRAM_opi]
+extends = env:esp32s3dev_8MB_PSRAM_opi
+board_build.partitions = tools/WLED_ESP32_16MB.csv
+board_upload.flash_size = 16MB
+
+[env:esp32s3dev_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]
board = esp8285
platform = ${common.platform_wled_default}
@@ -576,13 +605,14 @@ platform_packages = ${esp32s2.platform_packages}
board = lolin_s2_mini
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
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
-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_DFU_ON_BOOT=0
-DLOLIN_WIFI_FIX ; seems to work much better with this
-D WLED_USE_PSRAM
+ ; -D WLED_USE_UNREAL_MATH ;; may cause wrong sunset/sunrise times, but saves 6792 bytes FLASH
-D WLED_WATCHDOG_TIMEOUT=0
-D CONFIG_ASYNC_TCP_USE_WDT=0
-D LEDPIN=16
@@ -618,6 +648,8 @@ upload_speed = 115200
lib_deps = ${esp32c3.lib_deps}
board_build.partitions = tools/WLED_ESP32_2MB_noOTA.csv
board_build.flash_mode = dio
+board_upload.flash_size = 2MB
+board_upload.maximum_size = 2097152
[env:wemos_shield_esp32]
board = esp32dev
@@ -630,7 +662,7 @@ build_flags = ${common.build_flags_esp32}
-D RLYPIN=19
-D BTNPIN=17
-D IRPIN=18
- -D UWLED_USE_MY_CONFIG
+ -U WLED_USE_MY_CONFIG
-D USERMOD_DALLASTEMPERATURE
-D USERMOD_FOUR_LINE_DISPLAY
-D TEMPERATURE_PIN=23
diff --git a/readme.md b/readme.md
index dda6634a1..11c1733f8 100644
--- a/readme.md
+++ b/readme.md
@@ -3,7 +3,7 @@
-
+
@@ -26,7 +26,7 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control
- Up to 250 user presets to save and load colors/effects easily, supports cycling through them.
- Presets can be used to automatically execute API calls
- Nightlight function (gradually dims down)
-- Full OTA software updatability (HTTP + ArduinoOTA), password protectable
+- Full OTA software updateability (HTTP + ArduinoOTA), password protectable
- Configurable analog clock (Cronixie, 7-segment and EleksTube IPS clock support via usermods)
- Configurable Auto Brightness limit for safe operation
- Filesystem-based config for easier backup of presets and settings
@@ -66,7 +66,7 @@ Credits [here](https://kno.wled.ge/about/contributors/)!
Join the Discord server to discuss everything about WLED!
-
+
Check out the WLED [Discourse forum](https://wled.discourse.group)!
diff --git a/requirements.txt b/requirements.txt
index bc536ed07..8b55606dc 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -12,7 +12,7 @@ anyio==3.6.2
# via starlette
bottle==0.12.25
# via platformio
-certifi==2022.12.7
+certifi==2023.7.22
# via requests
charset-normalizer==3.1.0
# via requests
@@ -50,7 +50,7 @@ starlette==0.23.1
# via platformio
tabulate==0.9.0
# via platformio
-urllib3==1.26.15
+urllib3==1.26.18
# via requests
uvicorn==0.20.0
# via platformio
diff --git a/tools/WLED_ESP32_16MB_9MB_FS.csv b/tools/WLED_ESP32_16MB_9MB_FS.csv
new file mode 100644
index 000000000..f2f3f7783
--- /dev/null
+++ b/tools/WLED_ESP32_16MB_9MB_FS.csv
@@ -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
\ No newline at end of file
diff --git a/tools/WLED_ESP32_8MB.csv b/tools/WLED_ESP32_8MB.csv
index 5e930b89a..3cf3afc34 100644
--- a/tools/WLED_ESP32_8MB.csv
+++ b/tools/WLED_ESP32_8MB.csv
@@ -3,4 +3,5 @@ nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x200000,
app1, app, ota_1, 0x210000,0x200000,
-spiffs, data, spiffs, 0x410000,0x3F0000,
\ No newline at end of file
+spiffs, data, spiffs, 0x410000,0x3E0000,
+coredump, data, coredump,,64K
diff --git a/usermods/Animated_Staircase/Animated_Staircase.h b/usermods/Animated_Staircase/Animated_Staircase.h
index 151cf1d4a..8953756d3 100644
--- a/usermods/Animated_Staircase/Animated_Staircase.h
+++ b/usermods/Animated_Staircase/Animated_Staircase.h
@@ -25,6 +25,7 @@ class Animated_Staircase : public Usermod {
bool useUSSensorBottom = false; // using PIR or UltraSound sensor?
unsigned int topMaxDist = 50; // default maximum measured distance in cm, top
unsigned int bottomMaxDist = 50; // default maximum measured distance in cm, bottom
+ bool togglePower = false; // toggle power on/off with staircase on/off
/* runtime variables */
bool initDone = false;
@@ -90,7 +91,8 @@ class Animated_Staircase : public Usermod {
static const char _bottomEcho_pin[];
static const char _topEchoCm[];
static const char _bottomEchoCm[];
-
+ static const char _togglePower[];
+
void publishMqtt(bool bottom, const char* state) {
#ifndef WLED_DISABLE_MQTT
//Check if MQTT Connected, otherwise it will crash the 8266
@@ -131,7 +133,7 @@ class Animated_Staircase : public Usermod {
* received within this time, an object is detected
* and the function will return true.
*
- * The speed of sound is 343 meters per second at 20 degress Celcius.
+ * The speed of sound is 343 meters per second at 20 degrees Celsius.
* Since the sound has to travel back and forth, the detection
* distance for the sensor in cm is (0.0343 * maxTimeUs) / 2.
*
@@ -196,6 +198,7 @@ class Animated_Staircase : public Usermod {
if (on) {
lastSensor = topSensorRead;
} else {
+ if (togglePower && onIndex == offIndex && offMode) toggleOnOff(); // toggle power on if off
// If the bottom sensor triggered, we need to swipe up, ON
swipe = bottomSensorRead;
@@ -249,11 +252,14 @@ class Animated_Staircase : public Usermod {
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
+ }
}
}
- // send sesnor values to JSON API
+ // send sensor values to JSON API
void writeSensorsToJson(JsonObject& staircase) {
staircase[F("top-sensor")] = topSensorRead;
staircase[F("bottom-sensor")] = bottomSensorRead;
@@ -291,10 +297,11 @@ class Animated_Staircase : public Usermod {
offIndex = maxSegmentId = strip.getLastActiveSegmentId() + 1;
// shorten the strip transition time to be equal or shorter than segment delay
- transitionDelayTemp = transitionDelay = segment_delay_ms;
- strip.setTransition(segment_delay_ms/100);
+ transitionDelay = segment_delay_ms;
+ strip.setTransition(segment_delay_ms);
strip.trigger();
} else {
+ if (togglePower && !on && offMode) toggleOnOff(); // toggle power on if off
// Restore segment options
for (int i = 0; i <= strip.getLastActiveSegmentId(); i++) {
Segment &seg = strip.getSegment(i);
@@ -302,7 +309,7 @@ class Animated_Staircase : public Usermod {
seg.setOption(SEG_OPTION_ON, true);
}
strip.trigger(); // force strip update
- stateChanged = true; // inform external dvices/UI of change
+ stateChanged = true; // inform external devices/UI of change
colorUpdated(CALL_MODE_DIRECT_CHANGE);
DEBUG_PRINTLN(F("Animated Staircase disabled."));
}
@@ -444,6 +451,7 @@ class Animated_Staircase : public Usermod {
staircase[FPSTR(_bottomEcho_pin)] = useUSSensorBottom ? bottomEchoPin : -1;
staircase[FPSTR(_topEchoCm)] = topMaxDist;
staircase[FPSTR(_bottomEchoCm)] = bottomMaxDist;
+ staircase[FPSTR(_togglePower)] = togglePower;
DEBUG_PRINTLN(F("Staircase config saved."));
}
@@ -484,10 +492,12 @@ class Animated_Staircase : public Usermod {
bottomEchoPin = top[FPSTR(_bottomEcho_pin)] | bottomEchoPin;
topMaxDist = top[FPSTR(_topEchoCm)] | topMaxDist;
- topMaxDist = min(150,max(30,(int)topMaxDist)); // max distnace ~1.5m (a lag of 9ms may be expected)
+ topMaxDist = min(150,max(30,(int)topMaxDist)); // max distance ~1.5m (a lag of 9ms may be expected)
bottomMaxDist = top[FPSTR(_bottomEchoCm)] | bottomMaxDist;
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));
if (!initDone) {
// first run: reading from cfg.json
@@ -511,7 +521,7 @@ class Animated_Staircase : public Usermod {
if (changed) setup();
}
// 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::_topEchoCm[] PROGMEM = "top-dist-cm";
const char Animated_Staircase::_bottomEchoCm[] PROGMEM = "bottom-dist-cm";
+const char Animated_Staircase::_togglePower[] PROGMEM = "toggle-on-off";
diff --git a/usermods/Animated_Staircase/README.md b/usermods/Animated_Staircase/README.md
index 61c1cb2d6..320b744a5 100644
--- a/usermods/Animated_Staircase/README.md
+++ b/usermods/Animated_Staircase/README.md
@@ -11,7 +11,7 @@ The Animated Staircase can be controlled by the WLED API. Change settings such a
speed, on/off time and distance by sending an HTTP request, see below.
## WLED integration
-To include this usermod in your WLED setup, you have to be able to [compile WLED from source](https://github.com/Aircoookie/WLED/wiki/Compiling-WLED).
+To include this usermod in your WLED setup, you have to be able to [compile WLED from source](https://kno.wled.ge/advanced/compiling-wled/).
Before compiling, you have to make the following modifications:
@@ -38,7 +38,7 @@ Maximum distance for ultrasonic sensor can be configured as the time needed for
You _may_ need to use 10k pull-down resistors on the selected PIR pins, depending on the sensor.
## WLED configuration
-1. In the WLED UI, confgure a segment for each step. The lowest step of the stairs is the
+1. In the WLED UI, configure a segment for each step. The lowest step of the stairs is the
lowest segment id.
2. Save your segments into a preset.
3. Ideally, add the preset in the config > LED setup menu to the "apply
@@ -91,7 +91,7 @@ To enable the usermod again, use `"enabled":true`.
Alternatively you can use _Usermod_ Settings page where you can change other parameters as well.
### Changing animation parameters and detection range of the ultrasonic HC-SR04 sensor
-Using _Usermod_ Settings page you can define different usermod parameters, includng sensor pins, delay between segment activation etc.
+Using _Usermod_ Settings page you can define different usermod parameters, including sensor pins, delay between segment activation etc.
When an ultrasonic sensor is enabled you can enter maximum detection distance in centimeters separately for top and bottom sensors.
diff --git a/usermods/BH1750_v2/readme.md b/usermods/BH1750_v2/readme.md
index 05a033cf0..6e6c693d4 100644
--- a/usermods/BH1750_v2/readme.md
+++ b/usermods/BH1750_v2/readme.md
@@ -9,7 +9,7 @@ The luminance is displayed in both the Info section of the web UI, as well as pu
- This must be added under `lib_deps` in your `platformio.ini` (or `platformio_override.ini`).
- Data is published over MQTT - make sure you've enabled the MQTT sync interface.
-## Compiliation
+## Compilation
To enable, compile with `USERMOD_BH1750` defined (e.g. in `platformio_override.ini`)
```ini
diff --git a/usermods/BH1750_v2/usermod_bh1750.h b/usermods/BH1750_v2/usermod_bh1750.h
index 5e597d015..d758005d6 100644
--- a/usermods/BH1750_v2/usermod_bh1750.h
+++ b/usermods/BH1750_v2/usermod_bh1750.h
@@ -25,7 +25,7 @@
#define USERMOD_BH1750_FIRST_MEASUREMENT_AT 10000
#endif
-// only report if differance grater than offset value
+// only report if difference grater than offset value
#ifndef USERMOD_BH1750_OFFSET_VALUE
#define USERMOD_BH1750_OFFSET_VALUE 1
#endif
diff --git a/usermods/BME280_v2/usermod_bme280.h b/usermods/BME280_v2/usermod_bme280.h
index c7d25ec15..73d3c3dfc 100644
--- a/usermods/BME280_v2/usermod_bme280.h
+++ b/usermods/BME280_v2/usermod_bme280.h
@@ -31,7 +31,7 @@ private:
// set the default pins based on the architecture, these get overridden by Usermod menu settings
#ifdef ESP8266
- //uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8
+ //uint8_t RST_PIN = 16; // Un-comment for Heltec WiFi-Kit-8
#endif
bool initDone = false;
@@ -78,7 +78,7 @@ private:
static const char _name[];
static const char _enabled[];
- // Read the BME280/BMP280 Sensor (which one runs depends on whether Celsius or Farenheit being set in Usermod Menu)
+ // Read the BME280/BMP280 Sensor (which one runs depends on whether Celsius or Fahrenheit being set in Usermod Menu)
void UpdateBME280Data(int SensorType)
{
float _temperature, _humidity, _pressure;
diff --git a/usermods/Battery/readme.md b/usermods/Battery/readme.md
index e82378084..b3607482a 100644
--- a/usermods/Battery/readme.md
+++ b/usermods/Battery/readme.md
@@ -19,7 +19,7 @@ If you have an ESP32 board, connect the positive side of the battery to ADC1 (GP
- 💯 Displays current battery voltage
- 🚥 Displays battery level
- 🚫 Auto-off with configurable Threshold
-- 🚨 Low power indicator with many configuration posibilities
+- 🚨 Low power indicator with many configuration possibilities
## 🎈 Installation
@@ -40,7 +40,7 @@ define `USERMOD_BATTERY` in `wled00/my_config.h`
| `USERMOD_BATTERY_MEASUREMENT_INTERVAL` | ms | battery check interval. defaults to 30 seconds |
| `USERMOD_BATTERY_{TYPE}_MIN_VOLTAGE` | v | minimum battery voltage. default is 2.6 (18650 battery standard) |
| `USERMOD_BATTERY_{TYPE}_MAX_VOLTAGE` | v | maximum battery voltage. default is 4.2 (18650 battery standard) |
-| `USERMOD_BATTERY_{TYPE}_TOTAL_CAPACITY` | mAh | the capacity of all cells in parralel sumed up |
+| `USERMOD_BATTERY_{TYPE}_TOTAL_CAPACITY` | mAh | the capacity of all cells in parallel summed up |
| `USERMOD_BATTERY_{TYPE}_CALIBRATION` | | offset / calibration number, fine tune the measured voltage by the microcontroller |
| Auto-Off | --- | --- |
| `USERMOD_BATTERY_AUTO_OFF_ENABLED` | true/false | enables auto-off |
diff --git a/usermods/DHT/usermod_dht.h b/usermods/DHT/usermod_dht.h
index b6142f432..05a7267b5 100644
--- a/usermods/DHT/usermod_dht.h
+++ b/usermods/DHT/usermod_dht.h
@@ -49,7 +49,7 @@
#endif
// how many seconds after boot to take first measurement, 90 seconds
-// 90 gives enough time to OTA update firmware if this crashses
+// 90 gives enough time to OTA update firmware if this crashes
#ifndef USERMOD_DHT_FIRST_MEASUREMENT_AT
#define USERMOD_DHT_FIRST_MEASUREMENT_AT 90000
#endif
diff --git a/usermods/EXAMPLE_v2/usermod_v2_example.h b/usermods/EXAMPLE_v2/usermod_v2_example.h
index 43648b588..659d3e58b 100644
--- a/usermods/EXAMPLE_v2/usermod_v2_example.h
+++ b/usermods/EXAMPLE_v2/usermod_v2_example.h
@@ -46,7 +46,7 @@ class MyExampleUsermod : public Usermod {
static const char _enabled[];
- // any private methods should go here (non-inline methosd should be defined out of class)
+ // any private methods should go here (non-inline method should be defined out of class)
void publishMqtt(const char* state, bool retain = false); // example for publishing MQTT message
diff --git a/usermods/Enclosure_with_OLED_temp_ESP07/usermod.cpp b/usermods/Enclosure_with_OLED_temp_ESP07/usermod.cpp
index 1ca160501..823ad7472 100644
--- a/usermods/Enclosure_with_OLED_temp_ESP07/usermod.cpp
+++ b/usermods/Enclosure_with_OLED_temp_ESP07/usermod.cpp
@@ -15,23 +15,23 @@ OneWire oneWire(13);
DallasTemperature sensor(&oneWire);
long temptimer = millis();
long lastMeasure = 0;
-#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit
+#define Celsius // Show temperature measurement in Celsius otherwise is in Fahrenheit
// If display does not work or looks corrupted check the
// constructor reference:
// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp
// or check the gallery:
// https://github.com/olikraus/u8g2/wiki/gallery
-// --> First choise of cheap I2C OLED 128X32 0.91"
+// --> First choice of cheap I2C OLED 128X32 0.91"
U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
-// --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3"
+// --> Second choice of cheap I2C OLED 128X64 0.96" or 1.3"
//U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
// gets called once at boot. Do all initialization that doesn't depend on
// network here
void userSetup() {
sensor.begin(); //Start Dallas temperature sensor
u8x8.begin();
- //u8x8.setFlipMode(1); //Uncoment if using WLED Wemos shield
+ //u8x8.setFlipMode(1); //Un-comment if using WLED Wemos shield
u8x8.setPowerSave(0);
u8x8.setContrast(10); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
u8x8.setFont(u8x8_font_chroma48medium8_r);
@@ -71,7 +71,7 @@ void userLoop() {
if (mqtt != nullptr)
{
sensor.requestTemperatures();
-//Gets prefered temperature scale based on selection in definitions section
+//Gets preferred temperature scale based on selection in definitions section
#ifdef Celsius
float board_temperature = sensor.getTempCByIndex(0);
#else
@@ -138,11 +138,11 @@ void userLoop() {
// First row with Wifi name
u8x8.setCursor(1, 0);
u8x8.print(knownSsid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0));
- // Print `~` char to indicate that SSID is longer, than owr dicplay
+ // Print `~` char to indicate that SSID is longer than our display
if (knownSsid.length() > u8x8.getCols())
u8x8.print("~");
- // Second row with IP or Psssword
+ // Second row with IP or Password
u8x8.setCursor(1, 1);
// Print password in AP mode and if led is OFF.
if (apActive && bri == 0)
diff --git a/usermods/Enclosure_with_OLED_temp_ESP07/usermod_bme280.cpp b/usermods/Enclosure_with_OLED_temp_ESP07/usermod_bme280.cpp
index d5fd4a0c2..29a4332db 100644
--- a/usermods/Enclosure_with_OLED_temp_ESP07/usermod_bme280.cpp
+++ b/usermods/Enclosure_with_OLED_temp_ESP07/usermod_bme280.cpp
@@ -10,7 +10,7 @@
void UpdateBME280Data();
-#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit
+#define Celsius // Show temperature measurement in Celsius otherwise is in Fahrenheit
BME280I2C bme; // Default : forced mode, standby time = 1000 ms
// Oversampling = pressure ×1, temperature ×1, humidity ×1, filter off,
@@ -20,14 +20,14 @@ uint8_t SDA_PIN = 21;
#else //ESP8266 boards
uint8_t SCL_PIN = 5;
uint8_t SDA_PIN = 4;
-// uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8
+// uint8_t RST_PIN = 16; // Un-comment for Heltec WiFi-Kit-8
#endif
//The SCL and SDA pins are defined here.
//ESP8266 Wemos D1 mini board use SCL=5 SDA=4 while ESP32 Wemos32 mini board use SCL=22 SDA=21
#define U8X8_PIN_SCL SCL_PIN
#define U8X8_PIN_SDA SDA_PIN
-//#define U8X8_PIN_RESET RST_PIN // Uncoment for Heltec WiFi-Kit-8
+//#define U8X8_PIN_RESET RST_PIN // Un-comment for Heltec WiFi-Kit-8
// If display does not work or looks corrupted check the
// constructor reference:
@@ -36,9 +36,9 @@ uint8_t SDA_PIN = 4;
// https://github.com/olikraus/u8g2/wiki/gallery
// --> First choise of cheap I2C OLED 128X32 0.91"
U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
-// --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3"
+// --> Second choice of cheap I2C OLED 128X64 0.96" or 1.3"
//U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
-// --> Third choise of Heltec WiFi-Kit-8 OLED 128X32 0.91"
+// --> Third choice of Heltec WiFi-Kit-8 OLED 128X32 0.91"
//U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_RESET, U8X8_PIN_SCL, U8X8_PIN_SDA); // Constructor for Heltec WiFi-Kit-8
// gets called once at boot. Do all initialization that doesn't depend on network here
@@ -181,11 +181,11 @@ void userLoop() {
// First row with Wifi name
u8x8.setCursor(1, 0);
u8x8.print(knownSsid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0));
- // Print `~` char to indicate that SSID is longer, than owr dicplay
+ // Print `~` char to indicate that SSID is longer than our display
if (knownSsid.length() > u8x8.getCols())
u8x8.print("~");
- // Second row with IP or Psssword
+ // Second row with IP or Password
u8x8.setCursor(1, 1);
// Print password in AP mode and if led is OFF.
if (apActive && bri == 0)
diff --git a/usermods/Fix_unreachable_netservices_v2/readme.md b/usermods/Fix_unreachable_netservices_v2/readme.md
index 24d5ff5aa..006eaf9f9 100644
--- a/usermods/Fix_unreachable_netservices_v2/readme.md
+++ b/usermods/Fix_unreachable_netservices_v2/readme.md
@@ -2,7 +2,7 @@
**Attention: This usermod compiles only for ESP8266**
-This usermod-v2 modification performs a ping request to a local IP address every 60 seconds. This ensures WLED net services remain accessible in some problematic WLAN environments.
+This usermod-v2 modification performs a ping request to a local IP address every 60 seconds. This ensures WLED net services remain accessible in some problematic WiFi environments.
The modification works with static or DHCP IP address configuration.
@@ -24,7 +24,7 @@ The usermod supports the following state changes:
| JSON key | Value range | Description |
|-------------|------------------|---------------------------------|
-| PingDelayMs | 5000 to 18000000 | Deactivdate/activate the sensor |
+| PingDelayMs | 5000 to 18000000 | Deactivate/activate the sensor |
Changes also persist after a reboot.
diff --git a/usermods/Internal_Temperature_v2/readme.md b/usermods/Internal_Temperature_v2/readme.md
new file mode 100644
index 000000000..58a9e1939
--- /dev/null
+++ b/usermods/Internal_Temperature_v2/readme.md
@@ -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)
\ No newline at end of file
diff --git a/usermods/Internal_Temperature_v2/usermod_internal_temperature.h b/usermods/Internal_Temperature_v2/usermod_internal_temperature.h
new file mode 100644
index 000000000..3989e7668
--- /dev/null
+++ b/usermods/Internal_Temperature_v2/usermod_internal_temperature.h
@@ -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 method 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
+}
\ No newline at end of file
diff --git a/usermods/LDR_Dusk_Dawn_v2/README.md b/usermods/LDR_Dusk_Dawn_v2/README.md
new file mode 100644
index 000000000..5e33518a9
--- /dev/null
+++ b/usermods/LDR_Dusk_Dawn_v2/README.md
@@ -0,0 +1,26 @@
+# LDR_Dusk_Dawn_v2
+This usermod will obtain readings from a Light Dependent Resistor (LDR) and will turn on/off specific presets based on those readings. This is useful for exterior lighting situations where you want the lights to only be on when it is dark out.
+
+# Installation
+Add "-D USERMOD_LDR_DUSK_DAWN" to your platformio.ini [common] build_flags and build.
+
+Example:
+```
+[common]
+build_flags =
+ -D USERMOD_LDR_DUSK_DAWN # Enable LDR Dusk Dawn Usermod
+```
+
+# Usermod Settings
+Setting | Description | Default
+--- | --- | ---
+Enabled | Enable/Disable the LDR functionality. | Disabled
+LDR Pin | The analog capable pin your LDR is connected to. | 34
+Threshold Minutes | The number of minutes of consistent readings above/below the on/off threshold before the LED state will change. | 5
+Threshold | The analog read value threshold from the LDR. Readings lower than this number will count towards changing the LED state to off. You can see the current LDR reading by going into the info section when LDR functionality is enabled. | 1000
+On Preset | The WLED preset to be used for the LED on state. | 1
+Off Preset | The WLED preset to be used for the LED off state. | 2
+
+## Author
+[@jeffwdh](https://github.com/jeffwdh)
+jeffwdh@tarball.ca
diff --git a/usermods/LDR_Dusk_Dawn_v2/usermod_LDR_Dusk_Dawn_v2.h b/usermods/LDR_Dusk_Dawn_v2/usermod_LDR_Dusk_Dawn_v2.h
new file mode 100644
index 000000000..393fc2232
--- /dev/null
+++ b/usermods/LDR_Dusk_Dawn_v2/usermod_LDR_Dusk_Dawn_v2.h
@@ -0,0 +1,153 @@
+#pragma once
+#include "wled.h"
+
+#ifndef ARDUINO_ARCH_ESP32
+ // 8266 does not support analogRead on user selectable pins
+ #error only ESP32 is supported by usermod LDR_DUSK_DAWN
+#endif
+
+class LDR_Dusk_Dawn_v2 : public Usermod {
+ private:
+ // Defaults
+ bool ldrEnabled = false;
+ int ldrPin = 34; //A2 on Adafruit Huzzah32
+ int ldrThresholdMinutes = 5; // How many minutes of readings above/below threshold until it switches LED state
+ int ldrThreshold = 1000; // Readings higher than this number will turn off LED.
+ int ldrOnPreset = 1; // Default "On" Preset
+ int ldrOffPreset = 2; // Default "Off" Preset
+
+ // Variables
+ bool initDone = false;
+ bool ldrEnabledPreviously = false; // Was LDR enabled for the previous check? First check is always no.
+ int ldrOffCount; // Number of readings above the threshold
+ int ldrOnCount; // Number of readings below the threshold
+ int ldrReading = 0; // Last LDR reading
+ int ldrLEDState; // Current LED on/off state
+ unsigned long lastMillis = 0;
+ static const char _name[];
+
+ public:
+ void setup() {
+ // register ldrPin
+ if ((ldrPin >= 0) && (digitalPinToAnalogChannel(ldrPin) >= 0)) {
+ if(!pinManager.allocatePin(ldrPin, false, PinOwner::UM_LDR_DUSK_DAWN)) ldrEnabled = false; // pin already in use -> disable usermod
+ else pinMode(ldrPin, INPUT); // alloc success -> configure pin for input
+ } else ldrEnabled = false; // invalid pin -> disable usermod
+ initDone = true;
+ }
+
+ void loop() {
+ // Only update every 10 seconds
+ if (millis() - lastMillis > 10000) {
+ if ( (ldrEnabled == true)
+ && (ldrPin >= 0) && (digitalPinToAnalogChannel(ldrPin) >= 0) ) { // make sure that pin is valid for analogread()
+ // Default state is off
+ if (ldrEnabledPreviously == false) {
+ applyPreset(ldrOffPreset);
+ ldrEnabledPreviously = true;
+ ldrLEDState = 0;
+ }
+
+ // Get LDR reading and increment counter by number of seconds since last read
+ ldrReading = analogRead(ldrPin);
+ if (ldrReading <= ldrThreshold) {
+ ldrOnCount = ldrOnCount + 10;
+ ldrOffCount = 0;
+ } else {
+ ldrOffCount = ldrOffCount + 10;
+ ldrOnCount = 0;
+ }
+
+ if (ldrOnCount >= (ldrThresholdMinutes * 60)) {
+ ldrOnCount = 0;
+ // If LEDs were previously off, turn on
+ if (ldrLEDState == 0) {
+ applyPreset(ldrOnPreset);
+ ldrLEDState = 1;
+ }
+ }
+
+ if (ldrOffCount >= (ldrThresholdMinutes * 60)) {
+ ldrOffCount = 0;
+ // If LEDs were previously on, turn off
+ if (ldrLEDState == 1) {
+ applyPreset(ldrOffPreset);
+ ldrLEDState = 0;
+ }
+ }
+ } else {
+ // LDR is disabled, reset variables to default
+ ldrReading = 0;
+ ldrOnCount = 0;
+ ldrOffCount = 0;
+ ldrLEDState = 0;
+ ldrEnabledPreviously = false;
+ }
+ lastMillis = millis();
+ }
+ }
+
+ void addToConfig(JsonObject& root) {
+ JsonObject top = root.createNestedObject(FPSTR(_name));
+ top["Enabled"] = ldrEnabled;
+ top["LDR Pin"] = ldrPin;
+ top["Threshold Minutes"] = ldrThresholdMinutes;
+ top["Threshold"] = ldrThreshold;
+ top["On Preset"] = ldrOnPreset;
+ top["Off Preset"] = ldrOffPreset;
+ }
+
+ bool readFromConfig(JsonObject& root) {
+ int8_t oldLdrPin = ldrPin;
+ JsonObject top = root[FPSTR(_name)];
+ bool configComplete = !top.isNull();
+ configComplete &= getJsonValue(top["Enabled"], ldrEnabled);
+ configComplete &= getJsonValue(top["LDR Pin"], ldrPin);
+ configComplete &= getJsonValue(top["Threshold Minutes"], ldrThresholdMinutes);
+ configComplete &= getJsonValue(top["Threshold"], ldrThreshold);
+ configComplete &= getJsonValue(top["On Preset"], ldrOnPreset);
+ configComplete &= getJsonValue(top["Off Preset"], ldrOffPreset);
+
+ if (initDone && (ldrPin != oldLdrPin)) {
+ // pin changed - un-register previous pin, register new pin
+ if (oldLdrPin >= 0) pinManager.deallocatePin(oldLdrPin, PinOwner::UM_LDR_DUSK_DAWN);
+ setup(); // setup new pin
+ }
+ return configComplete;
+ }
+
+ void addToJsonInfo(JsonObject& root) {
+ // If "u" object does not exist yet we need to create it
+ JsonObject user = root["u"];
+ if (user.isNull()) user = root.createNestedObject("u");
+
+ JsonArray LDR_Enabled = user.createNestedArray("LDR dusk/dawn enabled");
+ LDR_Enabled.add(ldrEnabled);
+ if (!ldrEnabled) return; // do not add more if usermod is disabled
+
+ JsonArray LDR_Reading = user.createNestedArray("LDR reading");
+ LDR_Reading.add(ldrReading);
+
+ JsonArray LDR_State = user.createNestedArray("LDR turned LEDs on");
+ LDR_State.add(bool(ldrLEDState));
+
+ // Optional debug information:
+ //JsonArray LDR_On_Count = user.createNestedArray("LDR on count");
+ //LDR_On_Count.add(ldrOnCount);
+
+ //JsonArray LDR_Off_Count = user.createNestedArray("LDR off count");
+ //LDR_Off_Count.add(ldrOffCount);
+
+ //bool pinValid = ((ldrPin >= 0) && (digitalPinToAnalogChannel(ldrPin) >= 0));
+ //if (pinManager.getPinOwner(ldrPin) != PinOwner::UM_LDR_DUSK_DAWN) pinValid = false;
+ //JsonArray LDR_valid = user.createNestedArray(F("LDR pin"));
+ //LDR_valid.add(ldrPin);
+ //LDR_valid.add(pinValid ? F(" OK"): F(" invalid"));
+ }
+
+ uint16_t getId() {
+ return USERMOD_ID_LDR_DUSK_DAWN;
+ }
+};
+
+const char LDR_Dusk_Dawn_v2::_name[] PROGMEM = "LDR_Dusk_Dawn_v2";
diff --git a/usermods/PIR_sensor_switch/readme.md b/usermods/PIR_sensor_switch/readme.md
index 574bd06d8..85a5a74c0 100644
--- a/usermods/PIR_sensor_switch/readme.md
+++ b/usermods/PIR_sensor_switch/readme.md
@@ -23,7 +23,7 @@ You can also use usermod's off timer instead of sensor's. In such case rotate th
## Usermod installation
-**NOTE:** Usermod has been included in master branch of WLED so it can be compiled in directly just by defining `-D USERMOD_PIRSWITCH` and optionaly `-D PIR_SENSOR_PIN=16` to override default pin. You can also change the default off time by adding `-D PIR_SENSOR_OFF_SEC=30`.
+**NOTE:** Usermod has been included in master branch of WLED so it can be compiled in directly just by defining `-D USERMOD_PIRSWITCH` and optionally `-D PIR_SENSOR_PIN=16` to override default pin. You can also change the default off time by adding `-D PIR_SENSOR_OFF_SEC=30`.
## API to enable/disable the PIR sensor from outside. For example from another usermod:
@@ -31,7 +31,7 @@ To query or change the PIR sensor state the methods `bool PIRsensorEnabled()` an
When the PIR sensor state changes an MQTT message is broadcasted with topic `wled/deviceMAC/motion` and message `on` or `off`.
Usermod can also be configured to send just the MQTT message but not change WLED state using settings page as well as responding to motion only at night
-(assuming NTP and lattitude/longitude are set to determine sunrise/sunset times).
+(assuming NTP and latitude/longitude are set to determine sunrise/sunset times).
### There are two options to get access to the usermod instance:
@@ -85,7 +85,7 @@ Have fun - @gegu & @blazoncek
2021-11
* Added information about dynamic configuration options
-* Added option to temporary enable/disble usermod from WLED UI (Info dialog)
+* Added option to temporary enable/disable usermod from WLED UI (Info dialog)
2022-11
* Added compile time option for off timer.
diff --git a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h
index 8a4b9a608..48d48f217 100644
--- a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h
+++ b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h
@@ -101,7 +101,7 @@ private:
/**
* Read and update PIR sensor state.
- * Initilize/reset switch off timer
+ * Initialize/reset switch off timer
*/
bool updatePIRsensorState();
diff --git a/usermods/PWM_fan/readme.md b/usermods/PWM_fan/readme.md
index 1fbfe0e6c..6a44acf3b 100644
--- a/usermods/PWM_fan/readme.md
+++ b/usermods/PWM_fan/readme.md
@@ -5,7 +5,7 @@ v2 Usermod to to control PWM fan with RPM feedback and temperature control
This usermod requires the Dallas Temperature usermod to obtain temperature information. If it's not available, the fan will run at 100% speed.
If the fan does not have _tachometer_ (RPM) output you can set the _tachometer-pin_ to -1 to disable that feature.
-You can also set the thershold temperature at which fan runs at lowest speed. If the measured temperature is 3°C greater than the threshold temperature, the fan will run at 100%.
+You can also set the threshold temperature at which fan runs at lowest speed. If the measured temperature is 3°C greater than the threshold temperature, the fan will run at 100%.
If the _tachometer_ is supported, the current speed (in RPM) will be displayed on the WLED Info page.
@@ -22,7 +22,7 @@ This includes:
* PWM output pin (can be configured at compile time `-D PWM_PIN=xx`)
* tachometer input pin (can be configured at compile time `-D TACHO_PIN=xx`)
* sampling frequency in seconds
-* threshold temperature in degees C
+* threshold temperature in degrees Celsius
_NOTE:_ You may also need to tweak Dallas Temperature usermod sampling frequency to match PWM fan sampling frequency.
diff --git a/usermods/PWM_fan/usermod_PWM_fan.h b/usermods/PWM_fan/usermod_PWM_fan.h
index f7fe0e10f..05b6d9b3b 100644
--- a/usermods/PWM_fan/usermod_PWM_fan.h
+++ b/usermods/PWM_fan/usermod_PWM_fan.h
@@ -52,9 +52,15 @@ class PWMFanUsermod : public Usermod {
uint8_t tachoUpdateSec = 30;
float targetTemperature = 35.0;
uint8_t minPWMValuePct = 0;
+ uint8_t maxPWMValuePct = 100;
uint8_t numberOfInterrupsInOneSingleRotation = 2; // Number of interrupts ESP32 sees on tacho signal on a single fan rotation. All the fans I've seen trigger two interrups.
uint8_t pwmValuePct = 0;
+ // constant values
+ static const uint8_t _pwmMaxValue = 255;
+ static const uint8_t _pwmMaxStepCount = 7;
+ float _pwmTempStepSize = 0.5f;
+
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _enabled[];
@@ -63,6 +69,7 @@ class PWMFanUsermod : public Usermod {
static const char _temperature[];
static const char _tachoUpdateSec[];
static const char _minPWMValuePct[];
+ static const char _maxPWMValuePct[];
static const char _IRQperRotation[];
static const char _speed[];
static const char _lock[];
@@ -156,31 +163,25 @@ class PWMFanUsermod : public Usermod {
void setFanPWMbasedOnTemperature(void) {
float temp = getActualTemperature();
- float difftemp = temp - targetTemperature;
- // Default to run fan at full speed.
- int newPWMvalue = 255;
- int pwmStep = ((100 - minPWMValuePct) * newPWMvalue) / (7*100);
- int pwmMinimumValue = (minPWMValuePct * newPWMvalue) / 100;
+ // dividing minPercent and maxPercent into equal pwmvalue sizes
+ int pwmStepSize = ((maxPWMValuePct - minPWMValuePct) * _pwmMaxValue) / (_pwmMaxStepCount*100);
+ int pwmStep = calculatePwmStep(temp - targetTemperature);
+ // minimum based on full speed - not entered MaxPercent
+ int pwmMinimumValue = (minPWMValuePct * _pwmMaxValue) / 100;
+ updateFanSpeed(pwmMinimumValue + pwmStep*pwmStepSize);
+ }
- if ((temp == NAN) || (temp <= -100.0)) {
+ uint8_t calculatePwmStep(float diffTemp){
+ if ((diffTemp == NAN) || (diffTemp <= -100.0)) {
DEBUG_PRINTLN(F("WARNING: no temperature value available. Cannot do temperature control. Will set PWM fan to 255."));
- } else if (difftemp <= 0.0) {
- // Temperature is below target temperature. Run fan at minimum speed.
- newPWMvalue = pwmMinimumValue;
- } else if (difftemp <= 0.5) {
- newPWMvalue = pwmMinimumValue + pwmStep;
- } else if (difftemp <= 1.0) {
- newPWMvalue = pwmMinimumValue + 2*pwmStep;
- } else if (difftemp <= 1.5) {
- newPWMvalue = pwmMinimumValue + 3*pwmStep;
- } else if (difftemp <= 2.0) {
- newPWMvalue = pwmMinimumValue + 4*pwmStep;
- } else if (difftemp <= 2.5) {
- newPWMvalue = pwmMinimumValue + 5*pwmStep;
- } else if (difftemp <= 3.0) {
- newPWMvalue = pwmMinimumValue + 6*pwmStep;
+ return _pwmMaxStepCount;
}
- updateFanSpeed(newPWMvalue);
+ if(diffTemp <=0){
+ return 0;
+ }
+ int calculatedStep = (diffTemp / _pwmTempStepSize)+1;
+ // anything greater than max stepcount gets max
+ return (uint8_t)min((int)_pwmMaxStepCount,calculatedStep);
}
public:
@@ -312,6 +313,7 @@ class PWMFanUsermod : public Usermod {
top[FPSTR(_tachoUpdateSec)] = tachoUpdateSec;
top[FPSTR(_temperature)] = targetTemperature;
top[FPSTR(_minPWMValuePct)] = minPWMValuePct;
+ top[FPSTR(_maxPWMValuePct)] = maxPWMValuePct;
top[FPSTR(_IRQperRotation)] = numberOfInterrupsInOneSingleRotation;
DEBUG_PRINTLN(F("Autosave config saved."));
}
@@ -345,6 +347,8 @@ class PWMFanUsermod : public Usermod {
targetTemperature = top[FPSTR(_temperature)] | targetTemperature;
minPWMValuePct = top[FPSTR(_minPWMValuePct)] | minPWMValuePct;
minPWMValuePct = (uint8_t) min(100,max(0,(int)minPWMValuePct)); // bounds checking
+ maxPWMValuePct = top[FPSTR(_maxPWMValuePct)] | maxPWMValuePct;
+ maxPWMValuePct = (uint8_t) min(100,max((int)minPWMValuePct,(int)maxPWMValuePct)); // bounds checking
numberOfInterrupsInOneSingleRotation = top[FPSTR(_IRQperRotation)] | numberOfInterrupsInOneSingleRotation;
numberOfInterrupsInOneSingleRotation = (uint8_t) max(1,(int)numberOfInterrupsInOneSingleRotation); // bounds checking
@@ -389,6 +393,7 @@ const char PWMFanUsermod::_pwmPin[] PROGMEM = "PWM-pin";
const char PWMFanUsermod::_temperature[] PROGMEM = "target-temp-C";
const char PWMFanUsermod::_tachoUpdateSec[] PROGMEM = "tacho-update-s";
const char PWMFanUsermod::_minPWMValuePct[] PROGMEM = "min-PWM-percent";
+const char PWMFanUsermod::_maxPWMValuePct[] PROGMEM = "max-PWM-percent";
const char PWMFanUsermod::_IRQperRotation[] PROGMEM = "IRQs-per-rotation";
const char PWMFanUsermod::_speed[] PROGMEM = "speed";
const char PWMFanUsermod::_lock[] PROGMEM = "lock";
diff --git a/usermods/SN_Photoresistor/usermod_sn_photoresistor.h b/usermods/SN_Photoresistor/usermod_sn_photoresistor.h
index 60861e4c5..1e92d7d71 100644
--- a/usermods/SN_Photoresistor/usermod_sn_photoresistor.h
+++ b/usermods/SN_Photoresistor/usermod_sn_photoresistor.h
@@ -30,7 +30,7 @@
#define USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE 10000.0f
#endif
-// only report if differance grater than offset value
+// only report if difference grater than offset value
#ifndef USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE
#define USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE 5
#endif
diff --git a/usermods/TTGO-T-Display/usermod.cpp b/usermods/TTGO-T-Display/usermod.cpp
index b126d40a0..cbba07771 100644
--- a/usermods/TTGO-T-Display/usermod.cpp
+++ b/usermods/TTGO-T-Display/usermod.cpp
@@ -3,7 +3,7 @@
* This file allows you to add own functionality to WLED more easily
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
* EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in const.h)
- * bytes 2400+ are currently ununsed, but might be used for future wled features
+ * bytes 2400+ are currently unused, but might be used for future wled features
*/
/*
@@ -144,7 +144,7 @@ void userLoop() {
// First row with Wifi name
tft.setCursor(1, 1);
tft.print(knownSsid.substring(0, tftcharwidth > 1 ? tftcharwidth - 1 : 0));
- // Print `~` char to indicate that SSID is longer, than our dicplay
+ // Print `~` char to indicate that SSID is longer than our display
if (knownSsid.length() > tftcharwidth)
tft.print("~");
diff --git a/usermods/Temperature/readme.md b/usermods/Temperature/readme.md
index 2657c6c8f..b41e3e119 100644
--- a/usermods/Temperature/readme.md
+++ b/usermods/Temperature/readme.md
@@ -18,7 +18,7 @@ Copy the example `platformio_override.ini` to the root directory. This file sho
* `USERMOD_DALLASTEMPERATURE` - enables this user mod wled00/usermods_list.cpp
* `USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL` - number of milliseconds between measurements, defaults to 60000 ms (60s)
-All parameters can be configured at runtime via the Usermods settings page, including pin, temperature in degrees Celsius or Farenheit and measurement interval.
+All parameters can be configured at runtime via the Usermods settings page, including pin, temperature in degrees Celsius or Fahrenheit and measurement interval.
## Project link
diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod.cpp b/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod.cpp
index 78cc32a81..e7d1212a1 100644
--- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod.cpp
+++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod.cpp
@@ -34,30 +34,30 @@ uint8_t DALLAS_PIN =23;
uint8_t SCL_PIN = 5;
uint8_t SDA_PIN = 4;
uint8_t DALLAS_PIN =13;
-// uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8
+// uint8_t RST_PIN = 16; // Un-comment for Heltec WiFi-Kit-8
#endif
//The SCL and SDA pins are defined here.
//ESP8266 Wemos D1 mini board use SCL=5 SDA=4 while ESP32 Wemos32 mini board use SCL=22 SDA=21
#define U8X8_PIN_SCL SCL_PIN
#define U8X8_PIN_SDA SDA_PIN
-//#define U8X8_PIN_RESET RST_PIN // Uncoment for Heltec WiFi-Kit-8
+//#define U8X8_PIN_RESET RST_PIN // Un-comment for Heltec WiFi-Kit-8
// Dallas sensor reading timer
long temptimer = millis();
long lastMeasure = 0;
-#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit
+#define Celsius // Show temperature measurement in Celsius otherwise is in Fahrenheit
// If display does not work or looks corrupted check the
// constructor reference:
// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp
// or check the gallery:
// https://github.com/olikraus/u8g2/wiki/gallery
-// --> First choise of cheap I2C OLED 128X32 0.91"
+// --> First choice of cheap I2C OLED 128X32 0.91"
U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
-// --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3"
+// --> Second choice of cheap I2C OLED 128X64 0.96" or 1.3"
//U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
-// --> Third choise of Heltec WiFi-Kit-8 OLED 128X32 0.91"
+// --> Third choice of Heltec WiFi-Kit-8 OLED 128X32 0.91"
//U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_RESET, U8X8_PIN_SCL, U8X8_PIN_SDA); // Constructor for Heltec WiFi-Kit-8
// gets called once at boot. Do all initialization that doesn't depend on network here
void userSetup() {
@@ -97,7 +97,7 @@ void userLoop() {
//----> Dallas temperature sensor MQTT publishing
temptimer = millis();
-// Timer to publishe new temperature every 60 seconds
+// Timer to publish new temperature every 60 seconds
if (temptimer - lastMeasure > 60000)
{
lastMeasure = temptimer;
@@ -106,7 +106,7 @@ void userLoop() {
if (mqtt != nullptr)
{
// Serial.println(Dallas(DALLAS_PIN,0));
-//Gets prefered temperature scale based on selection in definitions section
+//Gets preferred temperature scale based on selection in definitions section
#ifdef Celsius
int16_t board_temperature = Dallas(DALLAS_PIN,0);
#else
@@ -173,11 +173,11 @@ void userLoop() {
// First row with Wifi name
u8x8.setCursor(1, 0);
u8x8.print(knownSsid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0));
- // Print `~` char to indicate that SSID is longer, than owr dicplay
+ // Print `~` char to indicate that SSID is longer than our display
if (knownSsid.length() > u8x8.getCols())
u8x8.print("~");
- // Second row with IP or Psssword
+ // Second row with IP or Password
u8x8.setCursor(1, 1);
// Print password in AP mode and if led is OFF.
if (apActive && bri == 0)
diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod_bme280.cpp b/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod_bme280.cpp
index c9d9a527e..ff1cf7e53 100644
--- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod_bme280.cpp
+++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod_bme280.cpp
@@ -6,7 +6,7 @@
void UpdateBME280Data();
-#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit
+#define Celsius // Show temperature measurement in Celsius otherwise is in Fahrenheit
BME280I2C bme; // Default : forced mode, standby time = 1000 ms
// Oversampling = pressure ×1, temperature ×1, humidity ×1, filter off,
@@ -16,25 +16,25 @@ uint8_t SDA_PIN = 21;
#else //ESP8266 boards
uint8_t SCL_PIN = 5;
uint8_t SDA_PIN = 4;
-// uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8
+// uint8_t RST_PIN = 16; // Un-comment for Heltec WiFi-Kit-8
#endif
//The SCL and SDA pins are defined here.
//ESP8266 Wemos D1 mini board use SCL=5 SDA=4 while ESP32 Wemos32 mini board use SCL=22 SDA=21
#define U8X8_PIN_SCL SCL_PIN
#define U8X8_PIN_SDA SDA_PIN
-//#define U8X8_PIN_RESET RST_PIN // Uncoment for Heltec WiFi-Kit-8
+//#define U8X8_PIN_RESET RST_PIN // Un-comment for Heltec WiFi-Kit-8
// If display does not work or looks corrupted check the
// constructor reference:
// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp
// or check the gallery:
// https://github.com/olikraus/u8g2/wiki/gallery
-// --> First choise of cheap I2C OLED 128X32 0.91"
+// --> First choice of cheap I2C OLED 128X32 0.91"
U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
-// --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3"
+// --> Second choice of cheap I2C OLED 128X64 0.96" or 1.3"
//U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
-// --> Third choise of Heltec WiFi-Kit-8 OLED 128X32 0.91"
+// --> Third choice of Heltec WiFi-Kit-8 OLED 128X32 0.91"
//U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_RESET, U8X8_PIN_SCL, U8X8_PIN_SDA); // Constructor for Heltec WiFi-Kit-8
// gets called once at boot. Do all initialization that doesn't depend on network here
@@ -179,11 +179,11 @@ void userLoop() {
// First row with Wifi name
u8x8.setCursor(1, 0);
u8x8.print(knownSsid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0));
- // Print `~` char to indicate that SSID is longer, than owr dicplay
+ // Print `~` char to indicate that SSID is longer, than our display
if (knownSsid.length() > u8x8.getCols())
u8x8.print("~");
- // Second row with IP or Psssword
+ // Second row with IP or Password
u8x8.setCursor(1, 1);
// Print password in AP mode and if led is OFF.
if (apActive && bri == 0)
diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h
index 837668ee3..4725c097c 100644
--- a/usermods/audioreactive/audio_reactive.h
+++ b/usermods/audioreactive/audio_reactive.h
@@ -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
// #define MIC_LOGGER // MIC sampling & sound input debugging (serial plotter)
// #define FFT_SAMPLING_LOG // FFT result debugging
@@ -71,7 +77,7 @@ static bool limiterOn = true; // bool: enable / disable dynamics
static uint16_t attackTime = 80; // int: attack time in milliseconds. Default 0.08sec
static uint16_t decayTime = 1400; // int: decay time in milliseconds. Default 1.40sec
// user settable options for FFTResult scaling
-static uint8_t FFTScalingMode = 3; // 0 none; 1 optimized logarithmic; 2 optimized linear; 3 optimized sqare root
+static uint8_t FFTScalingMode = 3; // 0 none; 1 optimized logarithmic; 2 optimized linear; 3 optimized square root
//
// AGC presets
@@ -104,11 +110,11 @@ static float sampleAgc = 0.0f; // Smoothed AGC sample
// 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 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 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 time as samplePeak, but reset by transmitAudioData
static unsigned long timeOfPeak = 0; // time of last sample peak detection.
-static void detectSamplePeak(void); // peak detection function (needs scaled FFT reasults in vReal[])
+static void detectSamplePeak(void); // peak detection function (needs scaled FFT results in vReal[])
static void autoResetPeak(void); // peak auto-reset function
@@ -173,13 +179,18 @@ static float windowWeighingFactors[samplesFFT] = {0.0f};
// Create FFT object
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
-// 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
-#define FFT_SQRT_APPROXIMATION // enables "quake3" style inverse sqrt
-#define sqrt(x) sqrtf(x) // little hack that reduces FFT time by 50% on ESP32 (as alternative to FFT_SQRT_APPROXIMATION)
+ // lib_deps += https://github.com/kosme/arduinoFFT#develop @ 1.9.2
+ // these options actually cause slow-downs on all esp32 processors, don't use them.
+ // #define FFT_SPEED_OVER_PRECISION // enables use of reciprocals (1/x etc) - not faster on ESP32
+ // #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
-// lib_deps += https://github.com/blazoncek/arduinoFFT.git
+ // around 40% slower on -S2
+ // lib_deps += https://github.com/blazoncek/arduinoFFT.git
#endif
+
#include
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
@@ -195,7 +206,7 @@ static float mapf(float x, float in_min, float in_max, float out_min, float out_
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
-// compute average of several FFT resut bins
+// compute average of several FFT result bins
static float fftAddAvg(int from, int to) {
float result = 0.0f;
for (int i = from; i <= to; i++) {
@@ -313,7 +324,7 @@ void FFTcode(void * parameter)
*
* Andrew's updated mapping of 256 bins down to the 16 result bins with Sample Freq = 10240, samplesFFT = 512 and some overlap.
* Based on testing, the lowest/Start frequency is 60 Hz (with bin 3) and a highest/End frequency of 5120 Hz in bin 255.
- * Now, Take the 60Hz and multiply by 1.320367784 to get the next frequency and so on until the end. Then detetermine the bins.
+ * Now, Take the 60Hz and multiply by 1.320367784 to get the next frequency and so on until the end. Then determine the bins.
* End frequency = Start frequency * multiplier ^ 16
* Multiplier = (End frequency/ Start frequency) ^ 1/16
* Multiplier = 1.320367784
@@ -372,7 +383,7 @@ void FFTcode(void * parameter)
}
}
- // post-processing of frequency channels (pink noise adjustment, AGC, smooting, scaling)
+ // post-processing of frequency channels (pink noise adjustment, AGC, smoothing, scaling)
postProcessFFTResults((fabsf(sampleAvg) > 0.25f)? true : false , NUM_GEQ_CHANNELS);
#if defined(WLED_DEBUG) || defined(SR_DEBUG)
@@ -411,7 +422,7 @@ static void runMicFilter(uint16_t numSamples, float *sampleBuffer) // p
//constexpr float beta1 = 0.8285f; // 18Khz
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 lowfilt = 0.0f; // IIR low frequency cutoff filter
@@ -419,7 +430,7 @@ static void runMicFilter(uint16_t numSamples, float *sampleBuffer) // p
// FIR lowpass, to remove high frequency noise
float highFilteredSample;
if (i < (numSamples-1)) highFilteredSample = beta1*sampleBuffer[i] + beta2*last_vals[0] + beta2*sampleBuffer[i+1]; // smooth out spikes
- else highFilteredSample = beta1*sampleBuffer[i] + beta2*last_vals[0] + beta2*last_vals[1]; // spcial handling for last sample in array
+ else highFilteredSample = beta1*sampleBuffer[i] + beta2*last_vals[0] + beta2*last_vals[1]; // special handling for last sample in array
last_vals[1] = last_vals[0];
last_vals[0] = sampleBuffer[i];
sampleBuffer[i] = highFilteredSample;
@@ -464,17 +475,17 @@ static void postProcessFFTResults(bool noiseGateOpen, int numberOfChannels) // p
switch (FFTScalingMode) {
case 1:
// Logarithmic scaling
- currentResult *= 0.42; // 42 is the answer ;-)
- currentResult -= 8.0; // 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
- else currentResult = 0.0; // special handling, because log(1) = 0; log(0) = undefined
+ currentResult *= 0.42f; // 42 is the answer ;-)
+ currentResult -= 8.0f; // this skips the lowest row, giving some room for peaks
+ if (currentResult > 1.0f) currentResult = logf(currentResult); // log to base "e", which is the fastest log() function
+ 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 = mapf(currentResult, 0, LOG_256, 0, 255); // map [log(1) ... log(255)] to [0 ... 255]
break;
case 2:
// Linear scaling
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;
currentResult *= 0.85f + (float(i)/1.8f); // extra up-scaling for high frequencies
break;
@@ -482,8 +493,8 @@ static void postProcessFFTResults(bool noiseGateOpen, int numberOfChannels) // p
// square root scaling
currentResult *= 0.38f;
currentResult -= 6.0f;
- if (currentResult > 1.0) currentResult = sqrtf(currentResult);
- else currentResult = 0.0; // special handling, because sqrt(0) = undefined
+ if (currentResult > 1.0f) currentResult = sqrtf(currentResult);
+ else currentResult = 0.0f; // special handling, because sqrt(0) = undefined
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]
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
static void detectSamplePeak(void) {
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.
// 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.
- 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;
}
@@ -616,7 +627,7 @@ class AudioReactive : public Usermod {
// variables used by getSample() and agcAvg()
int16_t micIn = 0; // Current sample starts with negative values and large values, which is why it's 16 bit signed
- double sampleMax = 0.0; // Max sample over a few seconds. Needed for AGC controler.
+ double sampleMax = 0.0; // Max sample over a few seconds. Needed for AGC controller.
double micLev = 0.0; // Used to convert returned value to have '0' as minimum. A leveller
float expAdjF = 0.0f; // Used for exponential filter.
float sampleReal = 0.0f; // "sampleRaw" as float, to provide bits that are lost otherwise (before amplification by sampleGain or inputLevel). Needed for AGC.
@@ -734,13 +745,13 @@ class AudioReactive : public Usermod {
* 2. we use two setpoints, one at ~60%, and one at ~80% of the maximum signal
* 3. the amplification depends on signal level:
* a) normal zone - very slow adjustment
- * b) emergency zome (<10% or >90%) - very fast adjustment
+ * b) emergency zone (<10% or >90%) - very fast adjustment
*/
void agcAvg(unsigned long the_time)
{
const int AGC_preset = (soundAgc > 0)? (soundAgc-1): 0; // make sure the _compiler_ knows this value will not change while we are inside the function
- float lastMultAgc = multAgc; // last muliplier used
+ float lastMultAgc = multAgc; // last multiplier used
float multAgcTemp = multAgc; // new multiplier
float tmpAgc = sampleReal * multAgc; // what-if amplified signal
@@ -758,7 +769,7 @@ class AudioReactive : public Usermod {
if (time_now - last_time > 2) {
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
tmpAgc = 0;
// we need to "spin down" the intgrated error buffer
@@ -780,13 +791,13 @@ class AudioReactive : public Usermod {
if (((multAgcTemp > 0.085f) && (multAgcTemp < 6.5f)) //integrator anti-windup by clamping
&& (multAgc*sampleMax < agcZoneStop[AGC_preset])) //integrator ceiling (>140% of max)
- control_integrated += control_error * 0.002 * 0.25; // 2ms = intgration time; 0.25 for damping
+ control_integrated += control_error * 0.002 * 0.25; // 2ms = integration time; 0.25 for damping
else
control_integrated *= 0.9; // spin down that beasty integrator
// apply PI Control
tmpAgc = sampleReal * lastMultAgc; // check "zone" of the signal using previous gain
- if ((tmpAgc > agcZoneHigh[AGC_preset]) || (tmpAgc < soundSquelch + agcZoneLow[AGC_preset])) { // upper/lower emergy zone
+ if ((tmpAgc > agcZoneHigh[AGC_preset]) || (tmpAgc < soundSquelch + agcZoneLow[AGC_preset])) { // upper/lower energy zone
multAgcTemp = lastMultAgc + agcFollowFast[AGC_preset] * agcControlKp[AGC_preset] * control_error;
multAgcTemp += agcFollowFast[AGC_preset] * agcControlKi[AGC_preset] * control_integrated;
} else { // "normal zone"
@@ -794,7 +805,7 @@ class AudioReactive : public Usermod {
multAgcTemp += agcFollowSlow[AGC_preset] * agcControlKi[AGC_preset] * control_integrated;
}
- // limit amplification again - PI controler sometimes "overshoots"
+ // limit amplification again - PI controller sometimes "overshoots"
//multAgcTemp = constrain(multAgcTemp, 0.015625f, 32.0f); // 1/64 < multAgcTemp < 32
if (multAgcTemp > 32.0f) multAgcTemp = 32.0f;
if (multAgcTemp < 1.0f/64.0f) multAgcTemp = 1.0f/64.0f;
@@ -824,7 +835,7 @@ class AudioReactive : public Usermod {
void getSample()
{
float sampleAdj; // Gain adjusted sample value
- float tmpSample; // An interim sample variable used for calculatioins.
+ float tmpSample; // An interim sample variable used for calculations.
const float weighting = 0.2f; // Exponential filter weighting. Will be adjustable in a future release.
const int AGC_preset = (soundAgc > 0)? (soundAgc-1): 0; // make sure the _compiler_ knows this value will not change while we are inside the function
@@ -873,8 +884,8 @@ class AudioReactive : public Usermod {
// keep "peak" sample, but decay value if current sample is below peak
if ((sampleMax < sampleReal) && (sampleReal > 0.5f)) {
sampleMax = sampleMax + 0.5f * (sampleReal - sampleMax); // new peak - with some filtering
- // another simple way to detect samplePeak
- if ((binNum < 10) && (millis() - timeOfPeak > 80) && (sampleAvg > 1)) {
+ // another simple way to detect samplePeak - cannot detect beats, but reacts on peak volume
+ if (((binNum < 12) || ((maxVol < 1))) && (millis() - timeOfPeak > 80) && (sampleAvg > 1)) {
samplePeak = true;
timeOfPeak = millis();
udpSamplePeak = true;
@@ -949,6 +960,8 @@ class AudioReactive : public Usermod {
//DEBUGSR_PRINTLN("Transmitting UDP Mic Packet");
audioSyncPacket transmitData;
+ memset(reinterpret_cast(&transmitData), 0, sizeof(transmitData)); // make sure that the packet - including "invisible" padding bytes added by the compiler - is fully initialized
+
strncpy_P(transmitData.header, PSTR(UDP_SYNC_HEADER), 6);
// transmit samples that were not modified by limitSampleDynamics()
transmitData.sampleRaw = (soundAgc) ? rawSampleAgc: sampleRaw;
@@ -964,9 +977,10 @@ class AudioReactive : public Usermod {
transmitData.FFT_Magnitude = my_magnitude;
transmitData.FFT_MajorPeak = FFT_MajorPeak;
- fftUdp.beginMulticastPacket();
- fftUdp.write(reinterpret_cast(&transmitData), sizeof(transmitData));
- fftUdp.endPacket();
+ if (fftUdp.beginMulticastPacket() != 0) { // beginMulticastPacket returns 0 in case of error
+ fftUdp.write(reinterpret_cast(&transmitData), sizeof(transmitData));
+ fftUdp.endPacket();
+ }
return;
} // transmitAudioData()
@@ -1149,6 +1163,13 @@ class AudioReactive : public Usermod {
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin);
break;
#endif
+ case 6:
+ DEBUGSR_PRINTLN(F("AR: ES8388 Source"));
+ audioSource = new ES8388Source(SAMPLE_RATE, BLOCK_SIZE);
+ delay(100);
+ if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
+ break;
+
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
// ADC over I2S is only possible on "classic" ESP32
case 0:
@@ -1156,6 +1177,7 @@ class AudioReactive : public Usermod {
DEBUGSR_PRINTLN(F("AR: Analog Microphone (left channel only)."));
audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE);
delay(100);
+ useBandPassFilter = true; // PDM bandpass filter seems to help for bad quality analog
if (audioSource) audioSource->initialize(audioPin);
break;
#endif
@@ -1265,9 +1287,10 @@ class AudioReactive : public Usermod {
#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.
- if ((userloopDelay > 23) && !disableSoundProcessing && (audioSyncEnabled == 0)) {
- DEBUG_PRINTF("[AR userLoop] hickup detected -> was inactive for last %d millis!\n", userloopDelay);
- }
+ // softhack007 disabled temporarily - avoid serial console spam with MANY leds and low FPS
+ //if ((userloopDelay > 65) && !disableSoundProcessing && (audioSyncEnabled == 0)) {
+ //DEBUG_PRINTF("[AR userLoop] hiccup detected -> was inactive for last %d millis!\n", userloopDelay);
+ //}
#endif
// run filters, and repeat in case of loop delays (hick-up compensation)
@@ -1304,6 +1327,9 @@ class AudioReactive : public Usermod {
if (millis() - lastTime > delayMs) {
have_new_sample = receiveAudioData();
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();
}
if (have_new_sample) syncVolumeSmth = volumeSmth; // remember received sample
@@ -1322,7 +1348,7 @@ class AudioReactive : public Usermod {
// Info Page: keep max sample from last 5 seconds
if ((millis() - sampleMaxTimer) > CYCLE_SAMPLEMAX) {
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
} else {
if ((sampleAvg >= 1)) maxSample5sec = fmaxf(maxSample5sec, (soundAgc) ? rawSampleAgc : sampleRaw); // follow maximum volume
@@ -1366,10 +1392,11 @@ class AudioReactive : public Usermod {
memset(fftAvg, 0, sizeof(fftAvg));
memset(fftResult, 0, sizeof(fftResult));
for(int i=(init?0:1); iisInitialized())) {
- // audio source sucessfully configured
+ // audio source successfully configured
if (audioSource->getType() == AudioSource::Type_I2SAdc) {
infoArr.add(F("ADC analog"));
} else {
infoArr.add(F("I2S digital"));
}
// input level or "silence"
- if (maxSample5sec > 1.0) {
+ if (maxSample5sec > 1.0f) {
float my_usage = 100.0f * (maxSample5sec / 255.0f);
snprintf_P(myStringBuffer, 15, PSTR(" - peak %3d%%"), int(my_usage));
infoArr.add(myStringBuffer);
@@ -1496,7 +1522,7 @@ class AudioReactive : public Usermod {
} else {
// error during audio source setup
infoArr.add(F("not initialized"));
- infoArr.add(F(" - check GPIO config"));
+ infoArr.add(F(" - check pin settings"));
}
}
@@ -1738,6 +1764,8 @@ class AudioReactive : public Usermod {
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
oappend(SET_F("addOption(dd,'Generic I2S PDM',5);"));
#endif
+ oappend(SET_F("addOption(dd,'ES8388',6);"));
+
oappend(SET_F("dd=addDropdown('AudioReactive','config:AGC');"));
oappend(SET_F("addOption(dd,'Off',0);"));
oappend(SET_F("addOption(dd,'Normal',1);"));
@@ -1798,7 +1826,9 @@ class AudioReactive : public Usermod {
const char AudioReactive::_name[] PROGMEM = "AudioReactive";
const char AudioReactive::_enabled[] PROGMEM = "enabled";
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";
+#endif
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_v1[] PROGMEM = "00001"; // old sync header version - need to add backwards-compatibility feature
diff --git a/usermods/audioreactive/audio_source.h b/usermods/audioreactive/audio_source.h
index 4aa057166..e970e19c6 100644
--- a/usermods/audioreactive/audio_source.h
+++ b/usermods/audioreactive/audio_source.h
@@ -1,5 +1,5 @@
#pragma once
-
+#ifdef ARDUINO_ARCH_ESP32
#include "wled.h"
#include
#include
@@ -22,14 +22,14 @@
// 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
-#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:
// * 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
#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
- #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
@@ -44,7 +44,7 @@
// benefit: analog mic inputs will be sampled contiously -> better response times and less "glitches"
// WARNING: this option WILL lock-up your device in case that any other analogRead() operation is performed;
// for example if you want to read "analog buttons"
-//#define I2S_GRAB_ADC1_COMPLETELY // (experimental) continously sample analog ADC microphone. WARNING will cause analogRead() lock-up
+//#define I2S_GRAB_ADC1_COMPLETELY // (experimental) continuously sample analog ADC microphone. WARNING will cause analogRead() lock-up
// data type requested from the I2S driver - currently we always use 32bit
//#define I2S_USE_16BIT_SAMPLES // (experimental) define this to request 16bit - more efficient but possibly less compatible
@@ -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 (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
// 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)
@@ -122,7 +122,7 @@ class AudioSource {
This function needs to take care of anything that needs to be done
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
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 (!pinManager.allocatePin(i2swsPin, true, PinOwner::UM_Audioreactive) ||
!pinManager.allocatePin(i2ssdPin, false, PinOwner::UM_Audioreactive)) { // #206
@@ -377,7 +378,7 @@ class I2SSource : public AudioSource {
};
/* ES7243 Microphone
- This is an I2S microphone that requires ininitialization over
+ This is an I2S microphone that requires initialization over
I2C before I2S data can be received
*/
class ES7243 : public I2SSource {
@@ -412,6 +413,7 @@ public:
};
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
+ DEBUGSR_PRINTLN("ES7243:: initialize();");
if ((i2sckPin < 0) || (mclkPin < 0)) {
DEBUGSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
return;
@@ -427,6 +429,122 @@ public:
}
};
+/* ES8388 Sound Module
+ This is an I2S sound processing unit that requires initialization over
+ I2C before I2S data can be received.
+*/
+class ES8388Source : public I2SSource {
+ private:
+
+ void _es8388I2cWrite(uint8_t reg, uint8_t val) {
+#ifndef ES8388_ADDR
+ Wire.beginTransmission(0x10);
+ #define ES8388_ADDR 0x10 // default address
+#else
+ Wire.beginTransmission(ES8388_ADDR);
+#endif
+ Wire.write((uint8_t)reg);
+ Wire.write((uint8_t)val);
+ uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK
+ if (i2cErr != 0) {
+ DEBUGSR_PRINTF("AR: ES8388 I2C write failed with error=%d (addr=0x%X, reg 0x%X, val 0x%X).\n", i2cErr, ES8388_ADDR, reg, val);
+ }
+ }
+
+ void _es8388InitAdc() {
+ // https://dl.radxa.com/rock2/docs/hw/ds/ES8388%20user%20Guide.pdf Section 10.1
+ // http://www.everest-semi.com/pdf/ES8388%20DS.pdf Better spec sheet, more clear.
+ // https://docs.google.com/spreadsheets/d/1CN3MvhkcPVESuxKyx1xRYqfUit5hOdsG45St9BCUm-g/edit#gid=0 generally
+ // Sets ADC to around what AudioReactive expects, and loops line-in to line-out/headphone for monitoring.
+ // Registries are decimal, settings are binary as that's how everything is listed in the docs
+ // ...which makes it easier to reference the docs.
+ //
+ _es8388I2cWrite( 8,0b00000000); // I2S to slave
+ _es8388I2cWrite( 2,0b11110011); // Power down DEM and STM
+ _es8388I2cWrite(43,0b10000000); // Set same LRCK
+ _es8388I2cWrite( 0,0b00000101); // Set chip to Play & Record Mode
+ _es8388I2cWrite(13,0b00000010); // Set MCLK/LRCK ratio to 256
+ _es8388I2cWrite( 1,0b01000000); // Power up analog and lbias
+ _es8388I2cWrite( 3,0b00000000); // Power up ADC, Analog Input, and Mic Bias
+ _es8388I2cWrite( 4,0b11111100); // Power down DAC, Turn on LOUT1 and ROUT1 and LOUT2 and ROUT2 power
+ _es8388I2cWrite( 2,0b01000000); // Power up DEM and STM and undocumented bit for "turn on line-out amp"
+
+ // #define use_es8388_mic
+
+ #ifdef use_es8388_mic
+ // The mics *and* line-in are BOTH connected to LIN2/RIN2 on the AudioKit
+ // so there's no way to completely eliminate the mics. It's also hella noisy.
+ // Line-in works OK on the AudioKit, generally speaking, as the mics really need
+ // amplification to be noticeable in a quiet room. If you're in a very loud room,
+ // the mics on the AudioKit WILL pick up sound even in line-in mode.
+ // TL;DR: Don't use the AudioKit for anything, use the LyraT.
+ //
+ // The LyraT does a reasonable job with mic input as configured below.
+
+ // Pick one of these. If you have to use the mics, use a LyraT over an AudioKit if you can:
+ _es8388I2cWrite(10,0b00000000); // Use Lin1/Rin1 for ADC input (mic on LyraT)
+ //_es8388I2cWrite(10,0b01010000); // Use Lin2/Rin2 for ADC input (mic *and* line-in on AudioKit)
+
+ _es8388I2cWrite( 9,0b10001000); // Select Analog Input PGA Gain for ADC to +24dB (L+R)
+ _es8388I2cWrite(16,0b00000000); // Set ADC digital volume attenuation to 0dB (left)
+ _es8388I2cWrite(17,0b00000000); // Set ADC digital volume attenuation to 0dB (right)
+ _es8388I2cWrite(38,0b00011011); // Mixer - route LIN1/RIN1 to output after mic gain
+
+ _es8388I2cWrite(39,0b01000000); // Mixer - route LIN to mixL, +6dB gain
+ _es8388I2cWrite(42,0b01000000); // Mixer - route RIN to mixR, +6dB gain
+ _es8388I2cWrite(46,0b00100001); // LOUT1VOL - 0b00100001 = +4.5dB
+ _es8388I2cWrite(47,0b00100001); // ROUT1VOL - 0b00100001 = +4.5dB
+ _es8388I2cWrite(48,0b00100001); // LOUT2VOL - 0b00100001 = +4.5dB
+ _es8388I2cWrite(49,0b00100001); // ROUT2VOL - 0b00100001 = +4.5dB
+
+ // Music ALC - the mics like Auto Level Control
+ // You can also use this for line-in, but it's not really needed.
+ //
+ _es8388I2cWrite(18,0b11111000); // ALC: stereo, max gain +35.5dB, min gain -12dB
+ _es8388I2cWrite(19,0b00110000); // ALC: target -1.5dB, 0ms hold time
+ _es8388I2cWrite(20,0b10100110); // ALC: gain ramp up = 420ms/93ms, gain ramp down = check manual for calc
+ _es8388I2cWrite(21,0b00000110); // ALC: use "ALC" mode, no zero-cross, window 96 samples
+ _es8388I2cWrite(22,0b01011001); // ALC: noise gate threshold, PGA gain constant, noise gate enabled
+ #else
+ _es8388I2cWrite(10,0b01010000); // Use Lin2/Rin2 for ADC input ("line-in")
+ _es8388I2cWrite( 9,0b00000000); // Select Analog Input PGA Gain for ADC to 0dB (L+R)
+ _es8388I2cWrite(16,0b01000000); // Set ADC digital volume attenuation to -32dB (left)
+ _es8388I2cWrite(17,0b01000000); // Set ADC digital volume attenuation to -32dB (right)
+ _es8388I2cWrite(38,0b00001001); // Mixer - route LIN2/RIN2 to output
+
+ _es8388I2cWrite(39,0b01010000); // Mixer - route LIN to mixL, 0dB gain
+ _es8388I2cWrite(42,0b01010000); // Mixer - route RIN to mixR, 0dB gain
+ _es8388I2cWrite(46,0b00011011); // LOUT1VOL - 0b00011110 = +0dB, 0b00011011 = LyraT balance fix
+ _es8388I2cWrite(47,0b00011110); // ROUT1VOL - 0b00011110 = +0dB
+ _es8388I2cWrite(48,0b00011110); // LOUT2VOL - 0b00011110 = +0dB
+ _es8388I2cWrite(49,0b00011110); // ROUT2VOL - 0b00011110 = +0dB
+ #endif
+
+ }
+
+ public:
+ ES8388Source(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f, bool i2sMaster=true) :
+ I2SSource(sampleRate, blockSize, sampleScale) {
+ _config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
+ };
+
+ void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
+ DEBUGSR_PRINTLN("ES8388Source:: initialize();");
+ if ((i2sckPin < 0) || (mclkPin < 0)) {
+ DEBUGSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
+ return;
+ }
+
+ // First route mclk, then configure ADC over I2C, then configure I2S
+ _es8388InitAdc();
+ I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
+ }
+
+ void deinitialize() {
+ I2SSource::deinitialize();
+ }
+
+};
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
#if !defined(SOC_I2S_SUPPORTS_ADC) && !defined(SOC_I2S_SUPPORTS_ADC_DAC)
@@ -468,7 +586,8 @@ class I2SAdcSource : public I2SSource {
/* identify Audiosource type - I2S-ADC*/
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;
if(!pinManager.allocatePin(audioPin, false, PinOwner::UM_Audioreactive)) {
DEBUGSR_PRINTF("failed to allocate GPIO for audio analog input: %d\n", audioPin);
@@ -639,7 +758,8 @@ class SPH0654 : public I2SSource {
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);
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
// these registers are only existing in "classic" ESP32
@@ -650,3 +770,4 @@ class SPH0654 : public I2SSource {
#endif
}
};
+#endif
\ No newline at end of file
diff --git a/usermods/audioreactive/readme.md b/usermods/audioreactive/readme.md
index d9f9ea783..1dd5f5d9c 100644
--- a/usermods/audioreactive/readme.md
+++ b/usermods/audioreactive/readme.md
@@ -1,6 +1,6 @@
# Audioreactive usermod
-Enabless controlling LEDs via audio input. Audio source can be a microphone or analog-in (AUX) using an appropriate adapter.
+Enables controlling LEDs via audio input. Audio source can be a microphone or analog-in (AUX) using an appropriate adapter.
Supported microphones range from analog (MAX4466, MAX9814, ...) to digital (INMP441, ICS-43434, ...).
Does audio processing and provides data structure that specially written effects can use.
@@ -19,7 +19,7 @@ This usermod is an evolution of [SR-WLED](https://github.com/atuline/WLED), and
## Supported MCUs
This audioreactive usermod works best on "classic ESP32" (dual core), and on ESP32-S3 which also has dual core and hardware floating point support.
-It will compile succesfully for ESP32-S2 and ESP32-C3, however might not work well, as other WLED functions will become slow. Audio processing requires a lot of computing power, which can be problematic on smaller MCUs like -S2 and -C3.
+It will compile successfully for ESP32-S2 and ESP32-C3, however might not work well, as other WLED functions will become slow. Audio processing requires a lot of computing power, which can be problematic on smaller MCUs like -S2 and -C3.
Analog audio is only possible on "classic" ESP32, but not on other MCUs like ESP32-S3.
@@ -35,7 +35,7 @@ Customised _arduinoFFT_ library for use with this usermod can be found at https:
### using latest (develop) _arduinoFFT_ library
Alternatively, you can use the latest arduinoFFT development version.
-ArduinoFFT `develop` library is slightly more accurate, and slighly faster than our customised library, however also needs additional 2kB RAM.
+ArduinoFFT `develop` library is slightly more accurate, and slightly faster than our customised library, however also needs additional 2kB RAM.
* `build_flags` = `-D USERMOD_AUDIOREACTIVE` `-D UM_AUDIOREACTIVE_USE_NEW_FFT`
* `lib_deps`= `https://github.com/kosme/arduinoFFT#develop @ 1.9.2`
@@ -63,7 +63,7 @@ You can use the following additional flags in your `build_flags`
* `-D SR_GAIN=x` : Default "gain" setting (60)
* `-D I2S_USE_RIGHT_CHANNEL`: Use RIGHT instead of LEFT channel (not recommended unless you strictly need this).
* `-D I2S_USE_16BIT_SAMPLES`: Use 16bit instead of 32bit for internal sample buffers. Reduces sampling quality, but frees some RAM ressources (not recommended unless you absolutely need this).
-* `-D I2S_GRAB_ADC1_COMPLETELY`: Experimental: continously sample analog ADC microphone. Only effective on ESP32. WARNING this _will_ cause conflicts(lock-up) with any analogRead() call.
+* `-D I2S_GRAB_ADC1_COMPLETELY`: Experimental: continuously sample analog ADC microphone. Only effective on ESP32. WARNING this _will_ cause conflicts(lock-up) with any analogRead() call.
* `-D MIC_LOGGER` : (debugging) Logs samples from the microphone to serial USB. Use with serial plotter (Arduino IDE)
* `-D SR_DEBUG` : (debugging) Additional error diagnostics and debug info on serial USB.
diff --git a/usermods/mqtt_switch_v2/README.md b/usermods/mqtt_switch_v2/README.md
index 148e4a564..744d7fe3c 100644
--- a/usermods/mqtt_switch_v2/README.md
+++ b/usermods/mqtt_switch_v2/README.md
@@ -50,5 +50,5 @@ This usermod listens on `[mqttDeviceTopic]/switch/0/set` (where 0 is replaced wi
Feedback about the current state is provided at `[mqttDeviceTopic]/switch/0/state`.
### Home Assistant auto-discovery
-Auto-discovery information is automatically published and you shoudn't have to do anything to register the switches in Home Assistant.
+Auto-discovery information is automatically published and you shouldn't have to do anything to register the switches in Home Assistant.
diff --git a/usermods/multi_relay/readme.md b/usermods/multi_relay/readme.md
index 71a540701..1f3bec7a6 100644
--- a/usermods/multi_relay/readme.md
+++ b/usermods/multi_relay/readme.md
@@ -2,7 +2,7 @@
This usermod-v2 modification allows the connection of multiple relays, each with individual delay and on/off mode.
Usermod supports PCF8574 I2C port expander to reduce GPIO use.
-PCF8574 supports 8 outputs and each output corresponds to a relay in WLED (relay 0 = port 0, etc). I you are using more than 8 relays with multiple PCF8574 make sure their addresses are set conscutively (e.g. 0x20 and 0x21). You can set address of first expander in settings.
+PCF8574 supports 8 outputs and each output corresponds to a relay in WLED (relay 0 = port 0, etc). I you are using more than 8 relays with multiple PCF8574 make sure their addresses are set in sequence (e.g. 0x20 and 0x21). You can set address of first expander in settings.
(**NOTE:** Will require Wire library and global I2C pins defined.)
## HTTP API
diff --git a/usermods/multi_relay/usermod_multi_relay.h b/usermods/multi_relay/usermod_multi_relay.h
index 7234df908..12c0a8da3 100644
--- a/usermods/multi_relay/usermod_multi_relay.h
+++ b/usermods/multi_relay/usermod_multi_relay.h
@@ -202,7 +202,7 @@ class MultiRelay : public Usermod {
};
-// class implementetion
+// class implementation
void MultiRelay::publishMqtt(int relay) {
#ifndef WLED_DISABLE_MQTT
@@ -366,7 +366,7 @@ void MultiRelay::switchRelay(uint8_t relay, bool mode) {
if (relay>=MULTI_RELAY_MAX_RELAYS || _relay[relay].pin<0) return;
_relay[relay].state = mode;
if (usePcf8574 && _relay[relay].pin >= 100) {
- // we need to send all ouputs at the same time
+ // we need to send all outputs at the same time
uint8_t state = 0;
for (int i=0; i☨ (Peripheral-Out-Ctrl-In) / `MISO` (deprecated) | 36 |
- | `pinPico` | GPIO that is connected to SD's `PICO`☨ (Peripheral-In-Ctrl-Out) / `MOSI` (deprecated) | 14 |
+ | `pinPico` | GPIO that is connected to SD's `PICO`☨ (Peripheral-In-Ctrl-Out) / `MOSI` (deprecated) | 15 |
| `sdEnable` | Enable to read data from the SD-card | true |
☨Following new naming convention of [OSHWA](https://www.oshwa.org/a-resolution-to-redefine-spi-signal-names/)
@@ -31,4 +31,4 @@
- checks if the specified file is available on the SD card
```cpp
bool file_onSD(const char *filepath) {...}
- ```
\ No newline at end of file
+ ```
diff --git a/usermods/seven_segment_display/readme.md b/usermods/seven_segment_display/readme.md
index a5294701c..792393a83 100644
--- a/usermods/seven_segment_display/readme.md
+++ b/usermods/seven_segment_display/readme.md
@@ -17,7 +17,7 @@ The number of individual LEDs per segment. 7 segments per digit.
#### perPeriod -- ssLEDPerPeriod
The number of individual LEDs per period. A ':' (colon) has two periods.
#### startIdx -- ssStartLED
-Index of the LED the display starts at. Enabless a seven segment display to be in the middle of a string.
+Index of the LED the display starts at. Enables a seven segment display to be in the middle of a string.
#### timeEnable -- ssTimeEnabled
When true, when displayMask is configured for a time output and no message is set, the time will be displayed.
#### scrollSpd -- ssScrollSpeed
diff --git a/usermods/seven_segment_display/usermod_v2_seven_segment_display.h b/usermods/seven_segment_display/usermod_v2_seven_segment_display.h
index e5b726e52..20fef15df 100644
--- a/usermods/seven_segment_display/usermod_v2_seven_segment_display.h
+++ b/usermods/seven_segment_display/usermod_v2_seven_segment_display.h
@@ -409,7 +409,7 @@ public:
if (mqttGroupTopic[0] != 0)
{
- //subcribe for sevenseg messages on the group topic
+ //subscribe for sevenseg messages on the group topic
sprintf_P(subBuffer, PSTR("%s/%S/+/set"), mqttGroupTopic, _str_sevenSeg);
mqtt->subscribe(subBuffer, 2);
}
@@ -417,7 +417,7 @@ public:
bool onMqttMessage(char *topic, char *payload)
{
- //If topic beings iwth sevenSeg cut it off, otherwise not our message.
+ //If topic beings with sevenSeg cut it off, otherwise not our message.
size_t topicPrefixLen = strlen_P(PSTR("/sevenSeg/"));
if (strncmp_P(topic, PSTR("/sevenSeg/"), topicPrefixLen) == 0)
topic += topicPrefixLen;
diff --git a/usermods/seven_segment_display_reloaded/readme.md b/usermods/seven_segment_display_reloaded/readme.md
index d373a7eee..a3398c3e5 100644
--- a/usermods/seven_segment_display_reloaded/readme.md
+++ b/usermods/seven_segment_display_reloaded/readme.md
@@ -24,6 +24,9 @@ Enables the inverted mode in which the background should be enabled and the digi
### Colon-blinking
Enables the blinking colon(s) if they are defined
+### Leading-Zero
+Shows the leading zero of the hour if it exists (i.e. shows `07` instead of `7`)
+
### enable-auto-brightness
Enables the auto brightness feature. Can be used only when the usermod SN_Photoresistor is installed.
diff --git a/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h b/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h
index 279774057..5c2fac0d4 100644
--- a/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h
+++ b/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h
@@ -17,6 +17,7 @@ private:
bool umSSDRDisplayTime = false;
bool umSSDRInverted = false;
bool umSSDRColonblink = true;
+ bool umSSDRLeadingZero = false;
bool umSSDREnableLDR = false;
String umSSDRHours = "";
String umSSDRMinutes = "";
@@ -79,6 +80,7 @@ private:
static const char _str_timeEnabled[];
static const char _str_inverted[];
static const char _str_colonblink[];
+ static const char _str_leadingZero[];
static const char _str_displayMask[];
static const char _str_hours[];
static const char _str_minutes[];
@@ -105,15 +107,15 @@ private:
switch (umSSDRDisplayMask[index]) {
case 'h':
timeVar = hourFormat12(localTime);
- _showElements(&umSSDRHours, timeVar, 0, 1);
+ _showElements(&umSSDRHours, timeVar, 0, !umSSDRLeadingZero);
break;
case 'H':
timeVar = hour(localTime);
- _showElements(&umSSDRHours, timeVar, 0, 1);
+ _showElements(&umSSDRHours, timeVar, 0, !umSSDRLeadingZero);
break;
case 'k':
timeVar = hour(localTime) + 1;
- _showElements(&umSSDRHours, timeVar, 0, 0);
+ _showElements(&umSSDRHours, timeVar, 0, !umSSDRLeadingZero);
break;
case 'm':
timeVar = minute(localTime);
@@ -309,6 +311,9 @@ private:
if (_cmpIntSetting_P(topic, payload, _str_colonblink, &umSSDRColonblink)) {
return true;
}
+ if (_cmpIntSetting_P(topic, payload, _str_leadingZero, &umSSDRLeadingZero)) {
+ return true;
+ }
if (strcmp_P(topic, _str_displayMask) == 0) {
umSSDRDisplayMask = String(payload);
_publishMQTTstr_P(_str_displayMask, umSSDRDisplayMask);
@@ -323,6 +328,7 @@ private:
_publishMQTTint_P(_str_ldrEnabled, umSSDREnableLDR);
_publishMQTTint_P(_str_inverted, umSSDRInverted);
_publishMQTTint_P(_str_colonblink, umSSDRColonblink);
+ _publishMQTTint_P(_str_leadingZero, umSSDRLeadingZero);
_publishMQTTstr_P(_str_hours, umSSDRHours);
_publishMQTTstr_P(_str_minutes, umSSDRMinutes);
@@ -347,6 +353,7 @@ private:
ssdrObj[FPSTR(_str_ldrEnabled)] = umSSDREnableLDR;
ssdrObj[FPSTR(_str_inverted)] = umSSDRInverted;
ssdrObj[FPSTR(_str_colonblink)] = umSSDRColonblink;
+ ssdrObj[FPSTR(_str_leadingZero)] = umSSDRLeadingZero;
ssdrObj[FPSTR(_str_displayMask)] = umSSDRDisplayMask;
ssdrObj[FPSTR(_str_hours)] = umSSDRHours;
ssdrObj[FPSTR(_str_minutes)] = umSSDRMinutes;
@@ -425,6 +432,8 @@ public:
invert.add(umSSDRInverted);
JsonArray blink = user.createNestedArray("Blinking colon");
blink.add(umSSDRColonblink);
+ JsonArray zero = user.createNestedArray("Show the hour leading zero");
+ zero.add(umSSDRLeadingZero);
JsonArray ldrEnable = user.createNestedArray("Auto Brightness enabled");
ldrEnable.add(umSSDREnableLDR);
@@ -454,6 +463,7 @@ public:
umSSDREnableLDR = ssdrObj[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR;
umSSDRInverted = ssdrObj[FPSTR(_str_inverted)] | umSSDRInverted;
umSSDRColonblink = ssdrObj[FPSTR(_str_colonblink)] | umSSDRColonblink;
+ umSSDRLeadingZero = ssdrObj[FPSTR(_str_leadingZero)] | umSSDRLeadingZero;
umSSDRDisplayMask = ssdrObj[FPSTR(_str_displayMask)] | umSSDRDisplayMask;
}
}
@@ -470,14 +480,14 @@ public:
if (mqttGroupTopic[0] != 0)
{
- //subcribe for sevenseg messages on the group topic
+ //subscribe for sevenseg messages on the group topic
sprintf_P(subBuffer, PSTR("%s/%S/+/set"), mqttGroupTopic, _str_name);
mqtt->subscribe(subBuffer, 2);
}
}
bool onMqttMessage(char *topic, char *payload) {
- //If topic beings iwth sevenSeg cut it off, otherwise not our message.
+ //If topic begins with sevenSeg cut it off, otherwise not our message.
size_t topicPrefixLen = strlen_P(PSTR("/wledSS/"));
if (strncmp_P(topic, PSTR("/wledSS/"), topicPrefixLen) == 0) {
topic += topicPrefixLen;
@@ -516,6 +526,7 @@ public:
umSSDREnableLDR = (top[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR);
umSSDRInverted = (top[FPSTR(_str_inverted)] | umSSDRInverted);
umSSDRColonblink = (top[FPSTR(_str_colonblink)] | umSSDRColonblink);
+ umSSDRLeadingZero = (top[FPSTR(_str_leadingZero)] | umSSDRLeadingZero);
umSSDRDisplayMask = top[FPSTR(_str_displayMask)] | umSSDRDisplayMask;
umSSDRHours = top[FPSTR(_str_hours)] | umSSDRHours;
@@ -546,6 +557,7 @@ const char UsermodSSDR::_str_name[] PROGMEM = "UsermodSSDR";
const char UsermodSSDR::_str_timeEnabled[] PROGMEM = "enabled";
const char UsermodSSDR::_str_inverted[] PROGMEM = "inverted";
const char UsermodSSDR::_str_colonblink[] PROGMEM = "Colon-blinking";
+const char UsermodSSDR::_str_leadingZero[] PROGMEM = "Leading-Zero";
const char UsermodSSDR::_str_displayMask[] PROGMEM = "Display-Mask";
const char UsermodSSDR::_str_hours[] PROGMEM = "LED-Numbers-Hours";
const char UsermodSSDR::_str_minutes[] PROGMEM = "LED-Numbers-Minutes";
diff --git a/usermods/sht/usermod_sht.h b/usermods/sht/usermod_sht.h
index 56cea2195..c6e17221b 100644
--- a/usermods/sht/usermod_sht.h
+++ b/usermods/sht/usermod_sht.h
@@ -290,7 +290,7 @@ void ShtUsermod::loop()
/**
* Whenever MQTT is connected, publish HA autodiscovery topics.
*
- * Is only donce once.
+ * Is only done once.
*
* @see Usermod::onMqttConnect()
* @see UsermodManager::onMqttConnect()
diff --git a/usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h b/usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h
index 238ec7d9c..9cc0bea4c 100644
--- a/usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h
+++ b/usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h
@@ -91,7 +91,8 @@ class StairwayWipeUsermod : public Usermod {
void startWipe()
{
bri = briLast; //turn on
- transitionDelayTemp = 0; //no transition
+ jsonTransitionOnce = true;
+ strip.setTransition(0); //no transition
effectCurrent = FX_MODE_COLOR_WIPE;
resetTimebase(); //make sure wipe starts from beginning
@@ -105,10 +106,11 @@ class StairwayWipeUsermod : public Usermod {
void turnOff()
{
+ jsonTransitionOnce = true;
#ifdef STAIRCASE_WIPE_OFF
- transitionDelayTemp = 0; //turn off immediately after wipe completed
+ strip.setTransition(0); //turn off immediately after wipe completed
#else
- transitionDelayTemp = 4000; //fade out slowly
+ strip.setTransition(4000); //fade out slowly
#endif
bri = 0;
stateUpdated(CALL_MODE_NOTIFICATION);
diff --git a/usermods/usermod_rotary_brightness_color/usermod_rotary_brightness_color.h b/usermods/usermod_rotary_brightness_color/usermod_rotary_brightness_color.h
index 61b76ba19..85a9a1605 100644
--- a/usermods/usermod_rotary_brightness_color/usermod_rotary_brightness_color.h
+++ b/usermods/usermod_rotary_brightness_color/usermod_rotary_brightness_color.h
@@ -23,7 +23,7 @@ private:
unsigned char Enc_B;
unsigned char Enc_A_prev = 0;
- // private class memebers configurable by Usermod Settings (defaults set inside readFromConfig())
+ // private class members configurable by Usermod Settings (defaults set inside readFromConfig())
int8_t pins[3]; // pins[0] = DT from encoder, pins[1] = CLK from encoder, pins[2] = CLK from encoder (optional)
int fadeAmount; // how many points to fade the Neopixel with each step
@@ -162,7 +162,7 @@ public:
* - configComplete is used to return false if any value is missing, not just if the main object is missing
* - The defaults are loaded every time readFromConfig() is run, not just once after boot
*
- * This ensures that missing values are added to the config, with their default values, in the rare but plauible cases of:
+ * This ensures that missing values are added to the config, with their default values, in the rare but plausible cases of:
* - a single value being missing at boot, e.g. if the Usermod was upgraded and a new setting was added
* - a single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
*
diff --git a/usermods/usermod_v2_auto_save/usermod_v2_auto_save.h b/usermods/usermod_v2_auto_save/usermod_v2_auto_save.h
index 8283aeed1..2a63dd4d8 100644
--- a/usermods/usermod_v2_auto_save/usermod_v2_auto_save.h
+++ b/usermods/usermod_v2_auto_save/usermod_v2_auto_save.h
@@ -101,7 +101,7 @@ class AutoSaveUsermod : public Usermod {
// network here
void setup() {
#ifdef USERMOD_FOUR_LINE_DISPLAY
- // This Usermod has enhanced funcionality if
+ // This Usermod has enhanced functionality if
// FourLineDisplayUsermod is available.
display = (FourLineDisplayUsermod*) usermods.lookup(USERMOD_ID_FOUR_LINE_DISP);
#endif
@@ -148,7 +148,7 @@ class AutoSaveUsermod : public Usermod {
if (autoSaveAfter && now > autoSaveAfter) {
autoSaveAfter = 0;
- // Time to auto save. You may have some flickry?
+ // Time to auto save. You may have some flickery?
saveSettings();
displayOverlay();
}
diff --git a/usermods/usermod_v2_four_line_display/readme.md b/usermods/usermod_v2_four_line_display/readme.md
index 26250cb5c..a0ed44d71 100644
--- a/usermods/usermod_v2_four_line_display/readme.md
+++ b/usermods/usermod_v2_four_line_display/readme.md
@@ -23,7 +23,7 @@ This file should be placed in the same directory as `platformio.ini`.
* `FLD_PIN_SCL` - The display SCL pin, defaults to 5
* `FLD_PIN_SDA` - The display SDA pin, defaults to 4
-All of the parameters can be configured via the Usermods settings page, inluding GPIO pins.
+All of the parameters can be configured via the Usermods settings page, including GPIO pins.
### PlatformIO requirements
diff --git a/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h b/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h
index 3fcf66128..2190c2393 100644
--- a/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h
+++ b/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h
@@ -11,7 +11,7 @@
// for WLED.
//
// Dependencies
-// * This usermod REQURES the ModeSortUsermod
+// * This usermod REQUIRES the ModeSortUsermod
// * This Usermod works best, by far, when coupled
// with RotaryEncoderUIUsermod.
//
@@ -393,7 +393,7 @@ class FourLineDisplayUsermod : public Usermod {
drawString(getCols() - 1, 0, "~");
}
- // Second row with IP or Psssword
+ // Second row with IP or Password
drawGlyph(0, lineHeight, 68, u8x8_font_open_iconic_embedded_1x1); // wifi icon
// Print password in AP mode and if led is OFF.
if (apActive && bri == 0) {
diff --git a/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h b/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h
index 5a99c3cdb..cd4201fec 100644
--- a/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h
+++ b/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h
@@ -6,7 +6,7 @@
#include "4LD_wled_fonts.c"
#ifndef FLD_ESP32_NO_THREADS
- #define FLD_ESP32_USE_THREADS // comment out to use 0.13.x behviour without parallel update task - slower, but more robust. May delay other tasks like LEDs or audioreactive!!
+ #define FLD_ESP32_USE_THREADS // comment out to use 0.13.x behaviour without parallel update task - slower, but more robust. May delay other tasks like LEDs or audioreactive!!
#endif
//
@@ -243,7 +243,7 @@ class FourLineDisplayUsermod : public Usermod {
*/
void setMarkLine(byte newMarkLineNum, byte newMarkColNum);
- //Draw the arrow for the current setting beiong changed
+ //Draw the arrow for the current setting being changed
void drawArrow();
//Display the current effect or palette (desiredEntry)
@@ -793,7 +793,7 @@ void FourLineDisplayUsermod::setMarkLine(byte newMarkLineNum, byte newMarkColNum
markColNum = newMarkColNum;
}
-//Draw the arrow for the current setting beiong changed
+//Draw the arrow for the current setting being changed
void FourLineDisplayUsermod::drawArrow() {
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
unsigned long now = millis();
@@ -1066,7 +1066,7 @@ void FourLineDisplayUsermod::networkOverlay(const char* line1, long showHowLong)
bool FourLineDisplayUsermod::handleButton(uint8_t b) {
yield();
if (!enabled
- || b // butto 0 only
+ || b // button 0 only
|| buttonType[b] == BTN_TYPE_SWITCH
|| buttonType[b] == BTN_TYPE_NONE
|| buttonType[b] == BTN_TYPE_RESERVED
@@ -1081,7 +1081,7 @@ bool FourLineDisplayUsermod::handleButton(uint8_t b) {
static bool buttonLongPressed = false;
static unsigned long buttonPressedTime = 0;
static unsigned long buttonWaitTime = 0;
- bool handled = true;
+ bool handled = false;
//momentary button logic
if (isButtonPressed(b)) { //pressed
@@ -1090,11 +1090,12 @@ bool FourLineDisplayUsermod::handleButton(uint8_t b) {
buttonPressedBefore = true;
if (now - buttonPressedTime > 600) { //long press
- buttonLongPressed = true;
//TODO: handleButton() handles button 0 without preset in a different way for double click
//so we need to override with same behaviour
- longPressAction(0);
- //handled = false;
+ //DEBUG_PRINTLN(F("4LD action."));
+ //if (!buttonLongPressed) longPressAction(0);
+ buttonLongPressed = true;
+ return false;
}
} else if (!isButtonPressed(b) && buttonPressedBefore) { //released
@@ -1126,7 +1127,7 @@ bool FourLineDisplayUsermod::handleButton(uint8_t b) {
buttonWaitTime = 0;
//TODO: handleButton() handles button 0 without preset in a different way for double click
//so we need to override with same behaviour
- shortPressAction(0);
+ //shortPressAction(0);
//handled = false;
}
return handled;
diff --git a/usermods/usermod_v2_klipper_percentage/readme.md b/usermods/usermod_v2_klipper_percentage/readme.md
index 0619bf857..e967d6b21 100644
--- a/usermods/usermod_v2_klipper_percentage/readme.md
+++ b/usermods/usermod_v2_klipper_percentage/readme.md
@@ -10,7 +10,7 @@ curl --location --request GET 'http://[]/printer/objects/query?virtual_sdcard=pr
## Usage
Compile the source with the buildflag `-D USERMOD_KLIPPER_PERCENTAGE` added.
-You can also use the WLBD bot in the Discord by simply extending an exsisting build enviroment:
+You can also use the WLBD bot in the Discord by simply extending an existing build environment:
```
[env:esp32klipper]
extends = env:esp32dev
@@ -23,7 +23,7 @@ build_flags = ${common.build_flags_esp32} -D USERMOD_KLIPPER_PERCENTAGE
Checkbox to enable or disable the overlay
### Klipper IP:
-IP adress of your Klipper instance you want to poll. ESP has to be restarted after change
+IP address of your Klipper instance you want to poll. ESP has to be restarted after change
### Direction :
0 = normal
diff --git a/usermods/usermod_v2_klipper_percentage/usermod_v2_klipper_percentage.h b/usermods/usermod_v2_klipper_percentage/usermod_v2_klipper_percentage.h
index 0e19cc80f..2f591b154 100644
--- a/usermods/usermod_v2_klipper_percentage/usermod_v2_klipper_percentage.h
+++ b/usermods/usermod_v2_klipper_percentage/usermod_v2_klipper_percentage.h
@@ -79,7 +79,7 @@ public:
httpGet(wifiClient, errorMessage);
if (strcmp(errorMessage, "") == 0)
{
- PSRAMDynamicJsonDocument klipperDoc(4096); // in practive about 2673
+ PSRAMDynamicJsonDocument klipperDoc(4096); // in practice about 2673
DeserializationError error = deserializeJson(klipperDoc, wifiClient);
if (error)
{
diff --git a/usermods/usermod_v2_ping_pong_clock/readme.md b/usermods/usermod_v2_ping_pong_clock/readme.md
index 9f01b3ebf..f8219489d 100644
--- a/usermods/usermod_v2_ping_pong_clock/readme.md
+++ b/usermods/usermod_v2_ping_pong_clock/readme.md
@@ -7,4 +7,4 @@ Contains a modification to use WLED in combination with the Ping Pong Ball LED C
To install this Usermod, you instruct PlatformIO to compile the Project with the USERMOD_PING_PONG_CLOCK flag.
WLED then automatically provides you with various settings on the Usermod Page.
-Note: Depending on the size of your clock, you may have to update the led indices for the indivdual numbers and the base indices.
+Note: Depending on the size of your clock, you may have to update the led indices for the individual numbers and the base indices.
diff --git a/usermods/usermod_v2_ping_pong_clock/usermod_v2_ping_pong_clock.h b/usermods/usermod_v2_ping_pong_clock/usermod_v2_ping_pong_clock.h
index a690c1b1e..40ff675c0 100644
--- a/usermods/usermod_v2_ping_pong_clock/usermod_v2_ping_pong_clock.h
+++ b/usermods/usermod_v2_ping_pong_clock/usermod_v2_ping_pong_clock.h
@@ -18,15 +18,15 @@ private:
// ---- Variables for correct LED numbering below, edit only if your clock is built different ----
- int baseH = 43; // Adress for the one place of the hours
- int baseHH = 7; // Adress for the tens place of the hours
- int baseM = 133; // Adress for the one place of the minutes
- int baseMM = 97; // Adress for the tens place of the minutes
- int colon1 = 79; // Adress for the first colon led
- int colon2 = 80; // Adress for the second colon led
+ int baseH = 43; // Address for the one place of the hours
+ int baseHH = 7; // Address for the tens place of the hours
+ int baseM = 133; // Address for the one place of the minutes
+ int baseMM = 97; // Address for the tens place of the minutes
+ int colon1 = 79; // Address for the first colon led
+ int colon2 = 80; // Address for the second colon led
// Matrix for the illumination of the numbers
- // Note: These only define the increments of the base adress. e.g. to define the second Minute you have to add the baseMM to every led position
+ // Note: These only define the increments of the base address. e.g. to define the second Minute you have to add the baseMM to every led position
const int numbers[10][10] =
{
{ 0, 1, 4, 6, 13, 15, 18, 19, -1, -1 }, // 0: null
diff --git a/usermods/usermod_v2_rotary_encoder_ui/usermod_v2_rotary_encoder_ui.h b/usermods/usermod_v2_rotary_encoder_ui/usermod_v2_rotary_encoder_ui.h
index 02bc0ccda..9e207339f 100644
--- a/usermods/usermod_v2_rotary_encoder_ui/usermod_v2_rotary_encoder_ui.h
+++ b/usermods/usermod_v2_rotary_encoder_ui/usermod_v2_rotary_encoder_ui.h
@@ -20,7 +20,7 @@
// Change between modes by pressing a button.
//
// Dependencies
-// * This usermod REQURES the ModeSortUsermod
+// * This usermod REQUIRES the ModeSortUsermod
// * This Usermod works best coupled with
// FourLineDisplayUsermod.
//
diff --git a/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h b/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h
index 75494dedb..20af68000 100644
--- a/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h
+++ b/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h
@@ -4,7 +4,7 @@
//
// Inspired by the original v2 usermods
-// * usermod_v2_rotaty_encoder_ui
+// * usermod_v2_rotary_encoder_ui
//
// v2 usermod that provides a rotary encoder-based UI.
//
@@ -45,6 +45,10 @@
#define ENCODER_SW_PIN 19
#endif
+#ifndef ENCODER_MAX_DELAY_MS // max delay between polling encoder pins
+#define ENCODER_MAX_DELAY_MS 8 // 8 milliseconds => max 120 change impulses in 1 second, for full turn of a 30/30 encoder (4 changes per segment, 30 segments for one turn)
+#endif
+
#ifndef USERMOD_USE_PCF8574
#undef USE_PCF8574
#define USE_PCF8574 false
@@ -95,7 +99,7 @@ static int re_qstringCmp(const void *ap, const void *bp) {
// Lowercase
bVal -= 32;
}
- // Relly we shouldn't ever get to '\0'
+ // Really we shouldn't ever get to '\0'
if (aVal == '"' || bVal == '"' || aVal == '\0' || bVal == '\0') {
// We're done. one is a substring of the other
// or something happenend and the quote didn't stop us.
@@ -394,8 +398,14 @@ void RotaryEncoderUIUsermod::sortModesAndPalettes() {
modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
- palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount());
- palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount()); // only use internal palettes
+ palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount()+strip.customPalettes.size());
+ palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount()+strip.customPalettes.size());
+ if (strip.customPalettes.size()) {
+ for (int i=0; i=0) pinMode(pinC, USERMOD_ROTARY_ENCODER_GPIO);
}
loopTime = millis();
@@ -539,8 +549,9 @@ void RotaryEncoderUIUsermod::setup()
*/
void RotaryEncoderUIUsermod::loop()
{
- if (!enabled || strip.isUpdating()) return;
+ if (!enabled) return;
unsigned long currentTime = millis(); // get the current elapsed time
+ if (strip.isUpdating() && ((currentTime - loopTime) < ENCODER_MAX_DELAY_MS)) return; // be nice, but not too nice
// Initialize effectCurrentIndex and effectPaletteIndex to
// current state. We do it here as (at least) effectCurrent
@@ -585,7 +596,7 @@ void RotaryEncoderUIUsermod::loop()
bool changedState = false;
char lineBuffer[64];
do {
- // finde new state
+ // find new state
switch (newState) {
case 0: strcpy_P(lineBuffer, PSTR("Brightness")); changedState = true; break;
case 1: if (!extractModeSlider(effectCurrent, 0, lineBuffer, 63)) newState++; else changedState = true; break; // speed
@@ -677,21 +688,25 @@ void RotaryEncoderUIUsermod::displayNetworkInfo() {
void RotaryEncoderUIUsermod::findCurrentEffectAndPalette() {
DEBUG_PRINTLN(F("Finding current mode and palette."));
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) {
effectCurrentIndex = i;
+ DEBUG_PRINTLN(F("Found current mode."));
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) {
effectPaletteIndex = i;
+ DEBUG_PRINTLN(F("Found palette."));
break;
}
}
- DEBUG_PRINTLN(F("Found palette."));
}
bool RotaryEncoderUIUsermod::changeState(const char *stateName, byte markedLine, byte markedCol, byte glyph) {
@@ -726,7 +741,9 @@ void RotaryEncoderUIUsermod::changeBrightness(bool increase) {
}
display->updateRedrawTime();
#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();
#ifdef USERMOD_FOUR_LINE_DISPLAY
display->updateBrightness();
@@ -873,7 +890,7 @@ void RotaryEncoderUIUsermod::changePalette(bool increase) {
}
display->updateRedrawTime();
#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];
stateChanged = true;
if (applyToAll) {
diff --git a/usermods/usermod_v2_word_clock/readme.md b/usermods/usermod_v2_word_clock/readme.md
index 1dde2223c..c42ee0ee4 100644
--- a/usermods/usermod_v2_word_clock/readme.md
+++ b/usermods/usermod_v2_word_clock/readme.md
@@ -8,7 +8,7 @@ active: enable/disable usermod
diplayItIs: enable/disable display of "Es ist" on the clock
ledOffset: number of LEDs before the wordclock LEDs
-### Update for alternatative wiring pattern
+### Update for alternative wiring pattern
Based on this fantastic work I added an alternative wiring pattern.
The original used a long wire to connect DO to DI, from one line to the next line.
diff --git a/usermods/usermod_v2_word_clock/usermod_v2_word_clock.h b/usermods/usermod_v2_word_clock/usermod_v2_word_clock.h
index 058b8318b..b66be290a 100644
--- a/usermods/usermod_v2_word_clock/usermod_v2_word_clock.h
+++ b/usermods/usermod_v2_word_clock/usermod_v2_word_clock.h
@@ -7,8 +7,8 @@
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
*
* This usermod can be used to drive a wordclock with a 11x10 pixel matrix with WLED. There are also 4 additional dots for the minutes.
- * The visualisation is desribed in 4 mask with LED numbers (single dots for minutes, minutes, hours and "clock/Uhr").
- * There are 2 parameters to chnage the behaviour:
+ * The visualisation is described in 4 mask with LED numbers (single dots for minutes, minutes, hours and "clock/Uhr").
+ * There are 2 parameters to change the behaviour:
*
* active: enable/disable usermod
* diplayItIs: enable/disable display of "Es ist" on the clock.
diff --git a/usermods/wireguard/platformio_override.ini b/usermods/wireguard/platformio_override.ini
new file mode 100644
index 000000000..fc0ae5fc9
--- /dev/null
+++ b/usermods/wireguard/platformio_override.ini
@@ -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
\ No newline at end of file
diff --git a/usermods/wireguard/readme.md b/usermods/wireguard/readme.md
new file mode 100644
index 000000000..071bea9f9
--- /dev/null
+++ b/usermods/wireguard/readme.md
@@ -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
+
+
+
diff --git a/usermods/wireguard/wireguard.h b/usermods/wireguard/wireguard.h
new file mode 100644
index 000000000..a83b9fe78
--- /dev/null
+++ b/usermods/wireguard/wireguard.h
@@ -0,0 +1,127 @@
+#pragma once
+
+#include
+
+#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;
+};
\ No newline at end of file
diff --git a/usermods/wizlights/readme.md b/usermods/wizlights/readme.md
index a0e0a8b8f..9e633043b 100644
--- a/usermods/wizlights/readme.md
+++ b/usermods/wizlights/readme.md
@@ -1,6 +1,6 @@
# Controlling Wiz lights
-Enabless controlling [WiZ](https://www.wizconnected.com/en/consumer/) lights that are part of the same network as the WLED controller.
+Enables controlling [WiZ](https://www.wizconnected.com/en/consumer/) lights that are part of the same network as the WLED controller.
The mod takes the colors from the first few pixels and sends them to the lights.
@@ -8,7 +8,7 @@ The mod takes the colors from the first few pixels and sends them to the lights.
- Interval (ms)
- How frequently to update the WiZ lights, in milliseconds.
- - Setting it too low may causse the ESP to become unresponsive.
+ - Setting it too low may cause the ESP to become unresponsive.
- Send Delay (ms)
- An optional millisecond delay after updating each WiZ light.
- Can help smooth out effects when using a large number of WiZ lights
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index 9baee13cd..3f720385f 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -51,7 +51,7 @@ uint16_t triwave16(uint16_t in) {
* Generates a tristate square wave w/ attac & decay
* @param x input value 0-255
* @param pulsewidth 0-127
- * @param attdec attac & decay, max. pulsewidth / 2
+ * @param attdec attack & decay, max. pulsewidth / 2
* @returns signed waveform value
*/
int8_t tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec) {
@@ -80,7 +80,7 @@ int8_t tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec) {
*/
uint16_t mode_static(void) {
SEGMENT.fill(SEGCOLOR(0));
- return 350;
+ return strip.isOffRefreshRequired() ? FRAMETIME : 350;
}
static const char _data_FX_MODE_STATIC[] PROGMEM = "Solid";
@@ -177,11 +177,11 @@ uint16_t color_wipe(bool rev, bool useRandomColors) {
SEGENV.step = 3;
}
if (SEGENV.step == 1) { //if flag set, change to new random color
- SEGENV.aux1 = SEGMENT.get_random_wheel_index(SEGENV.aux0);
+ SEGENV.aux1 = get_random_wheel_index(SEGENV.aux0);
SEGENV.step = 2;
}
if (SEGENV.step == 3) {
- SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux1);
+ SEGENV.aux0 = get_random_wheel_index(SEGENV.aux1);
SEGENV.step = 0;
}
}
@@ -271,7 +271,7 @@ uint16_t mode_random_color(void) {
if (it != SEGENV.step) //new color
{
SEGENV.aux1 = SEGENV.aux0;
- SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0); //aux0 will store our random color wheel index
+ SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0); //aux0 will store our random color wheel index
SEGENV.step = it;
}
@@ -289,7 +289,6 @@ uint16_t mode_dynamic(void) {
if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed
if(SEGENV.call == 0) {
- //SEGMENT.setUpLeds(); //lossless getPixelColor()
//SEGMENT.fill(BLACK);
for (int i = 0; i < SEGLEN; i++) SEGENV.data[i] = random8();
}
@@ -605,28 +604,36 @@ static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!,!;!,!;!;;m12=0";
* Dissolve function
*/
uint16_t dissolve(uint32_t color) {
- //bool wa = (SEGCOLOR(1) != 0 && strip.getBrightness() < 255); //workaround, can't compare getPixel to color if not full brightness
+ uint16_t dataSize = (SEGLEN+7) >> 3; //1 bit per LED
+ if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
+
if (SEGENV.call == 0) {
- SEGMENT.setUpLeds(); //lossless getPixelColor()
- SEGMENT.fill(SEGCOLOR(1));
+ memset(SEGMENT.data, 0xFF, dataSize); // start by fading pixels up
+ SEGENV.aux0 = 1;
}
for (int j = 0; j <= SEGLEN / 15; j++) {
if (random8() <= SEGMENT.intensity) {
- for (size_t times = 0; times < 10; times++) //attempt to spawn a new pixel 10 times
- {
- uint16_t i = random16(SEGLEN);
+ for (size_t times = 0; times < 10; times++) { //attempt to spawn a new pixel 10 times
+ unsigned i = random16(SEGLEN);
+ unsigned index = i >> 3;
+ unsigned bitNum = i & 0x07;
+ bool fadeUp = bitRead(SEGENV.data[index], bitNum);
if (SEGENV.aux0) { //dissolve to primary/palette
- if (SEGMENT.getPixelColor(i) == SEGCOLOR(1) /*|| wa*/) {
+ if (fadeUp) {
if (color == SEGCOLOR(0)) {
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
} else {
SEGMENT.setPixelColor(i, color);
}
+ bitWrite(SEGENV.data[index], bitNum, false);
break; //only spawn 1 new pixel per frame per 50 LEDs
}
} else { //dissolve to secondary
- if (SEGMENT.getPixelColor(i) != SEGCOLOR(1)) { SEGMENT.setPixelColor(i, SEGCOLOR(1)); break; }
+ if (!fadeUp) {
+ SEGMENT.setPixelColor(i, SEGCOLOR(1)); break;
+ bitWrite(SEGENV.data[index], bitNum, true);
+ }
}
}
}
@@ -635,6 +642,7 @@ uint16_t dissolve(uint32_t color) {
if (SEGENV.step > (255 - SEGMENT.speed) + 15U) {
SEGENV.aux0 = !SEGENV.aux0;
SEGENV.step = 0;
+ memset(SEGMENT.data, (SEGENV.aux0 ? 0xFF : 0), dataSize); // switch fading
} else {
SEGENV.step++;
}
@@ -715,7 +723,7 @@ uint16_t mode_hyper_sparkle(void) {
if (strip.now - SEGENV.aux0 > SEGENV.step) {
if (random8((255-SEGMENT.intensity) >> 4) == 0) {
- for (int i = 0; i < MAX(1, SEGLEN/3); i++) {
+ for (int i = 0; i < max(1, SEGLEN/3); i++) {
SEGMENT.setPixelColor(random16(SEGLEN), SEGCOLOR(1));
}
}
@@ -766,7 +774,7 @@ uint16_t mode_android(void) {
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1));
}
- if (SEGENV.aux1 > ((float)SEGMENT.intensity/255.0)*(float)SEGLEN)
+ if (SEGENV.aux1 > (SEGMENT.intensity*SEGLEN)/255)
{
SEGENV.aux0 = 1;
} else
@@ -816,21 +824,21 @@ static const char _data_FX_MODE_ANDROID[] PROGMEM = "Android@!,Width;!,!;!;;m12=
*/
uint16_t chase(uint32_t color1, uint32_t color2, uint32_t color3, bool do_palette) {
uint16_t counter = strip.now * ((SEGMENT.speed >> 2) + 1);
- uint16_t a = counter * SEGLEN >> 16;
+ uint16_t a = (counter * SEGLEN) >> 16;
bool chase_random = (SEGMENT.mode == FX_MODE_CHASE_RANDOM);
if (chase_random) {
if (a < SEGENV.step) //we hit the start again, choose new color for Chase random
{
SEGENV.aux1 = SEGENV.aux0; //store previous random color
- SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0);
+ SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0);
}
color1 = SEGMENT.color_wheel(SEGENV.aux0);
}
SEGENV.step = a;
// Use intensity setting to vary chase up to 1/2 string length
- uint8_t size = 1 + (SEGMENT.intensity * SEGLEN >> 10);
+ uint8_t size = 1 + ((SEGMENT.intensity * SEGLEN) >> 10);
uint16_t b = a + size; //"trail" of chase, filled with color1
if (b > SEGLEN) b -= SEGLEN;
@@ -1063,7 +1071,7 @@ uint16_t mode_chase_flash_random(void) {
SEGENV.aux1 = (SEGENV.aux1 + 1) % SEGLEN;
if (SEGENV.aux1 == 0) {
- SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0);
+ SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0);
}
}
return delay;
@@ -1118,8 +1126,9 @@ static const char _data_FX_MODE_RUNNING_RANDOM[] PROGMEM = "Stream@!,Zone size;;
uint16_t larson_scanner(bool dual) {
+ if (SEGLEN == 1) return mode_static();
uint16_t counter = strip.now * ((SEGMENT.speed >> 2) +8);
- uint16_t index = counter * SEGLEN >> 16;
+ uint16_t index = (counter * SEGLEN) >> 16;
SEGMENT.fade_out(SEGMENT.intensity);
@@ -1206,8 +1215,6 @@ uint16_t mode_fireworks() {
const uint16_t height = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
- SEGMENT.setUpLeds(); //lossless getPixelColor()
- SEGMENT.fill(SEGCOLOR(1));
SEGENV.aux0 = UINT16_MAX;
SEGENV.aux1 = UINT16_MAX;
}
@@ -1215,22 +1222,24 @@ uint16_t mode_fireworks() {
bool valid1 = (SEGENV.aux0 < width*height);
bool valid2 = (SEGENV.aux1 < width*height);
+ uint8_t x = SEGENV.aux0%width, y = SEGENV.aux0/width; // 2D coordinates stored in upper and lower byte
uint32_t sv1 = 0, sv2 = 0;
- if (valid1) sv1 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(SEGENV.aux0%width, SEGENV.aux0/width) : SEGMENT.getPixelColor(SEGENV.aux0); // get spark color
- if (valid2) sv2 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(SEGENV.aux1%width, SEGENV.aux1/width) : SEGMENT.getPixelColor(SEGENV.aux1);
+ if (valid1) sv1 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(x, y) : SEGMENT.getPixelColor(SEGENV.aux0); // get spark color
+ if (valid2) sv2 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(x, y) : SEGMENT.getPixelColor(SEGENV.aux1);
if (!SEGENV.step) SEGMENT.blur(16);
- if (valid1) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(SEGENV.aux0%width, SEGENV.aux0/width, sv1); else SEGMENT.setPixelColor(SEGENV.aux0, sv1); } // restore spark color after blur
- if (valid2) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(SEGENV.aux1%width, SEGENV.aux1/width, sv2); else SEGMENT.setPixelColor(SEGENV.aux1, sv2); } // restore old spark color after blur
+ if (valid1) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(x, y, sv1); else SEGMENT.setPixelColor(SEGENV.aux0, sv1); } // restore spark color after blur
+ if (valid2) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(x, y, sv2); else SEGMENT.setPixelColor(SEGENV.aux1, sv2); } // restore old spark color after blur
- for (int i=0; i> 1)) == 0) {
uint16_t index = random16(width*height);
- uint16_t j = index % width, k = index / width;
+ x = index % width;
+ y = index / width;
uint32_t col = SEGMENT.color_from_palette(random8(), false, false, 0);
- if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(j, k, col);
+ if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(x, y, col);
else SEGMENT.setPixelColor(index, col);
SEGENV.aux1 = SEGENV.aux0; // old spark
- SEGENV.aux0 = index; // remember where spark occured
+ SEGENV.aux0 = index; // remember where spark occurred
}
}
return FRAMETIME;
@@ -1263,8 +1272,8 @@ uint16_t mode_rain() {
SEGENV.aux0++; // increase spark index
SEGENV.aux1++;
}
- if (SEGENV.aux0 == 0) SEGENV.aux0 = UINT16_MAX; // reset previous spark positiom
- if (SEGENV.aux1 == 0) SEGENV.aux0 = UINT16_MAX; // reset previous spark positiom
+ if (SEGENV.aux0 == 0) SEGENV.aux0 = UINT16_MAX; // reset previous spark position
+ if (SEGENV.aux1 == 0) SEGENV.aux0 = UINT16_MAX; // reset previous spark position
if (SEGENV.aux0 >= width*height) SEGENV.aux0 = 0; // ignore
if (SEGENV.aux1 >= width*height) SEGENV.aux1 = 0;
}
@@ -1306,24 +1315,23 @@ static const char _data_FX_MODE_FIRE_FLICKER[] PROGMEM = "Fire Flicker@!,!;!;!;0
* Gradient run base function
*/
uint16_t gradient_base(bool loading) {
+ if (SEGLEN == 1) return mode_static();
uint16_t counter = strip.now * ((SEGMENT.speed >> 2) + 1);
- uint16_t pp = counter * SEGLEN >> 16;
+ uint16_t pp = (counter * SEGLEN) >> 16;
if (SEGENV.call == 0) pp = 0;
- float val; //0.0 = sec 1.0 = pri
- float brd = loading ? SEGMENT.intensity : SEGMENT.intensity/2;
- if (brd <1.0) brd = 1.0;
+ int val; //0 = sec 1 = pri
+ int brd = 1 + loading ? SEGMENT.intensity/2 : SEGMENT.intensity/4;
+ //if (brd < 1) brd = 1;
int p1 = pp-SEGLEN;
int p2 = pp+SEGLEN;
- for (int i = 0; i < SEGLEN; i++)
- {
- if (loading)
- {
- val = abs(((i>pp) ? p2:pp) -i);
+ for (int i = 0; i < SEGLEN; i++) {
+ if (loading) {
+ val = abs(((i>pp) ? p2:pp) - i);
} else {
- val = MIN(abs(pp-i),MIN(abs(p1-i),abs(p2-i)));
+ val = min(abs(pp-i),min(abs(p1-i),abs(p2-i)));
}
- val = (brd > val) ? val/brd * 255 : 255;
+ val = (brd > val) ? (val * 255) / brd : 255;
SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(0), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), val));
}
@@ -1904,13 +1912,8 @@ static const char _data_FX_MODE_PRIDE_2015[] PROGMEM = "Pride 2015@!;;";
//eight colored dots, weaving in and out of sync with each other
uint16_t mode_juggle(void) {
if (SEGLEN == 1) return mode_static();
- if (SEGENV.call == 0) {
- SEGMENT.setUpLeds(); //lossless getPixelColor()
- SEGMENT.fill(BLACK);
- }
SEGMENT.fadeToBlackBy(192 - (3*SEGMENT.intensity/4));
-
CRGB fastled_col;
byte dothue = 0;
for (int i = 0; i < 8; i++) {
@@ -2075,12 +2078,9 @@ static const char _data_FX_MODE_COLORWAVES[] PROGMEM = "Colorwaves@!,Hue;!;!";
// colored stripes pulsing at a defined Beats-Per-Minute (BPM)
uint16_t mode_bpm() {
- //CRGB fastled_col;
uint32_t stp = (strip.now / 20) & 0xFF;
uint8_t beat = beatsin8(SEGMENT.speed, 64, 255);
for (int i = 0; i < SEGLEN; i++) {
- //fastled_col = ColorFromPalette(SEGPALETTE, stp + (i * 2), beat - stp + (i * 10));
- //SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue);
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(stp + (i * 2), false, PALETTE_SOLID_WRAP, 0, beat - stp + (i * 10)));
}
@@ -2275,33 +2275,35 @@ uint16_t mode_meteor() {
byte* trail = SEGENV.data;
- byte meteorSize= 1+ SEGLEN / 10;
+ const unsigned meteorSize= 1 + SEGLEN / 20; // 5%
uint16_t counter = strip.now * ((SEGMENT.speed >> 2) +8);
uint16_t in = counter * SEGLEN >> 16;
+ const int max = SEGMENT.palette==5 || !SEGMENT.check1 ? 240 : 255;
// fade all leds to colors[1] in LEDs one step
for (int i = 0; i < SEGLEN; i++) {
- if (random8() <= 255 - SEGMENT.intensity)
- {
- byte meteorTrailDecay = 128 + random8(127);
+ if (random8() <= 255 - SEGMENT.intensity) {
+ byte meteorTrailDecay = 162 + random8(92);
trail[i] = scale8(trail[i], meteorTrailDecay);
- SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, false, 0, trail[i]));
+ uint32_t col = SEGMENT.check1 ? SEGMENT.color_from_palette(i, true, false, 0, trail[i]) : SEGMENT.color_from_palette(trail[i], false, true, 255);
+ SEGMENT.setPixelColor(i, col);
}
}
// draw meteor
- for (int j = 0; j < meteorSize; j++) {
+ for (unsigned j = 0; j < meteorSize; j++) {
uint16_t index = in + j;
if (index >= SEGLEN) {
index -= SEGLEN;
}
- trail[index] = 240;
- SEGMENT.setPixelColor(index, SEGMENT.color_from_palette(index, true, false, 0, 255));
+ trail[index] = max;
+ uint32_t col = SEGMENT.check1 ? SEGMENT.color_from_palette(index, true, false, 0, trail[index]) : SEGMENT.color_from_palette(trail[index], false, true, 255);
+ SEGMENT.setPixelColor(index, col);
}
return FRAMETIME;
}
-static const char _data_FX_MODE_METEOR[] PROGMEM = "Meteor@!,Trail length;!;!";
+static const char _data_FX_MODE_METEOR[] PROGMEM = "Meteor@!,Trail,,,,Gradient;;!;1";
// smooth meteor effect
@@ -2313,35 +2315,35 @@ uint16_t mode_meteor_smooth() {
byte* trail = SEGENV.data;
- byte meteorSize= 1+ SEGLEN / 10;
+ const unsigned meteorSize= 1+ SEGLEN / 20; // 5%
uint16_t in = map((SEGENV.step >> 6 & 0xFF), 0, 255, 0, SEGLEN -1);
+ const int max = SEGMENT.palette==5 || !SEGMENT.check1 ? 240 : 255;
// fade all leds to colors[1] in LEDs one step
for (int i = 0; i < SEGLEN; i++) {
- if (trail[i] != 0 && random8() <= 255 - SEGMENT.intensity)
- {
- int change = 3 - random8(12); //change each time between -8 and +3
- trail[i] += change;
- if (trail[i] > 245) trail[i] = 0;
- if (trail[i] > 240) trail[i] = 240;
- SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, false, 0, trail[i]));
+ if (/*trail[i] != 0 &&*/ random8() <= 255 - SEGMENT.intensity) {
+ int change = trail[i] + 4 - random8(24); //change each time between -20 and +4
+ trail[i] = constrain(change, 0, max);
+ uint32_t col = SEGMENT.check1 ? SEGMENT.color_from_palette(i, true, false, 0, trail[i]) : SEGMENT.color_from_palette(trail[i], false, true, 255);
+ SEGMENT.setPixelColor(i, col);
}
}
// draw meteor
- for (int j = 0; j < meteorSize; j++) {
+ for (unsigned j = 0; j < meteorSize; j++) {
uint16_t index = in + j;
if (index >= SEGLEN) {
index -= SEGLEN;
}
- trail[index] = 240;
- SEGMENT.setPixelColor(index, SEGMENT.color_from_palette(index, true, false, 0, 255));
+ trail[index] = max;
+ uint32_t col = SEGMENT.check1 ? SEGMENT.color_from_palette(index, true, false, 0, trail[index]) : SEGMENT.color_from_palette(trail[index], false, true, 255);
+ SEGMENT.setPixelColor(index, col);
}
SEGENV.step += SEGMENT.speed +1;
return FRAMETIME;
}
-static const char _data_FX_MODE_METEOR_SMOOTH[] PROGMEM = "Meteor Smooth@!,Trail length;!;!";
+static const char _data_FX_MODE_METEOR_SMOOTH[] PROGMEM = "Meteor Smooth@!,Trail,,,,Gradient;;!;1";
//Railway Crossing / Christmas Fairy lights
@@ -2415,9 +2417,10 @@ uint16_t ripple_base()
#ifndef WLED_DISABLE_2D
if (SEGMENT.is2D()) {
+ propI /= 2;
uint16_t cx = rippleorigin >> 8;
uint16_t cy = rippleorigin & 0xFF;
- uint8_t mag = scale8(cubicwave8((propF>>2)), amp);
+ uint8_t mag = scale8(sin8((propF>>2)), amp);
if (propI > 0) SEGMENT.draw_circle(cx, cy, propI, color_blend(SEGMENT.getPixelColorXY(cx + propI, cy), col, mag));
} else
#endif
@@ -2433,7 +2436,7 @@ uint16_t ripple_base()
ripplestate += rippledecay;
ripples[i].state = (ripplestate > 254) ? 0 : ripplestate;
} else {//randomly create new wave
- if (random16(IBN + 10000) <= SEGMENT.intensity) {
+ if (random16(IBN + 10000) <= (SEGMENT.intensity >> (SEGMENT.is2D()*3))) {
ripples[i].state = 1;
ripples[i].pos = SEGMENT.is2D() ? ((random8(SEGENV.virtualWidth())<<8) | (random8(SEGENV.virtualHeight()))) : random16(SEGLEN);
ripples[i].color = random8(); //color
@@ -2449,6 +2452,7 @@ uint16_t ripple_base()
uint16_t mode_ripple(void) {
if (SEGLEN == 1) return mode_static();
if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1));
+ else SEGMENT.fade_out(250);
return ripple_base();
}
static const char _data_FX_MODE_RIPPLE[] PROGMEM = "Ripple@!,Wave #,,,,,Overlay;,!;!;12";
@@ -2477,12 +2481,6 @@ static const char _data_FX_MODE_RIPPLE_RAINBOW[] PROGMEM = "Ripple Rainbow@!,Wav
//
// TwinkleFOX: Twinkling 'holiday' lights that fade in and out.
// Colors are chosen from a palette. Read more about this effect using the link above!
-
-// If COOL_LIKE_INCANDESCENT is set to 1, colors will
-// fade out slighted 'reddened', similar to how
-// incandescent bulbs change color as they get dim down.
-#define COOL_LIKE_INCANDESCENT 1
-
CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat)
{
// Overall twinkle speed (changed)
@@ -2521,7 +2519,7 @@ CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat)
CRGB c;
if (bright > 0) {
c = ColorFromPalette(SEGPALETTE, hue, bright, NOBLEND);
- if(COOL_LIKE_INCANDESCENT == 1) {
+ if (!SEGMENT.check1) {
// This code takes a pixel, and if its in the 'fading down'
// part of the cycle, it adjusts the color a little bit like the
// way that incandescent bulbs fade toward 'red' as they dim.
@@ -2607,14 +2605,14 @@ uint16_t mode_twinklefox()
{
return twinklefox_base(false);
}
-static const char _data_FX_MODE_TWINKLEFOX[] PROGMEM = "Twinklefox@!,Twinkle rate;;!";
+static const char _data_FX_MODE_TWINKLEFOX[] PROGMEM = "Twinklefox@!,Twinkle rate,,,,Cool;!,!;!";
uint16_t mode_twinklecat()
{
return twinklefox_base(true);
}
-static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rate;;!";
+static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rate,,,,Cool;!,!;!";
//inspired by https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectBlinkingHalloweenEyes
@@ -2855,6 +2853,107 @@ uint16_t mode_bouncing_balls(void) {
static const char _data_FX_MODE_BOUNCINGBALLS[] PROGMEM = "Bouncing Balls@Gravity,# of balls,,,,,Overlay;!,!,!;!;1;m12=1"; //bar
+/*
+ * bouncing balls on a track track Effect modified from Aircoookie's bouncing balls
+ * Courtesy of pjhatch (https://github.com/pjhatch)
+ * https://github.com/Aircoookie/WLED/pull/1039
+ */
+// modified for balltrack mode
+typedef struct RollingBall {
+ unsigned long lastBounceUpdate;
+ float mass; // could fix this to be = 1. if memory is an issue
+ float velocity;
+ float height;
+} rball_t;
+
+static uint16_t rolling_balls(void) {
+ //allocate segment data
+ const uint16_t maxNumBalls = 16; // 255/16 + 1
+ uint16_t dataSize = sizeof(rball_t) * maxNumBalls;
+ if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
+
+ rball_t *balls = reinterpret_cast(SEGENV.data);
+
+ // number of balls based on intensity setting to max of 16 (cycles colors)
+ // non-chosen color is a random color
+ uint8_t numBalls = SEGMENT.intensity/16 + 1;
+ bool hasCol2 = SEGCOLOR(2);
+
+ if (SEGENV.call == 0) {
+ SEGMENT.fill(hasCol2 ? BLACK : SEGCOLOR(1)); // start clean
+ for (int i = 0; i < maxNumBalls; i++) {
+ balls[i].lastBounceUpdate = strip.now;
+ balls[i].velocity = 20.0f * float(random16(1000, 10000))/10000.0f; // number from 1 to 10
+ if (random8()<128) balls[i].velocity = -balls[i].velocity; // 50% chance of reverse direction
+ balls[i].height = (float(random16(0, 10000)) / 10000.0f); // from 0. to 1.
+ balls[i].mass = (float(random16(1000, 10000)) / 10000.0f); // from .1 to 1.
+ }
+ }
+
+ float cfac = float(scale8(8, 255-SEGMENT.speed) +1)*20000.0f; // this uses the Aircoookie conversion factor for scaling time using speed slider
+
+ if (SEGMENT.check3) SEGMENT.fade_out(250); // 2-8 pixel trails (optional)
+ else {
+ if (!SEGMENT.check2) SEGMENT.fill(hasCol2 ? BLACK : SEGCOLOR(1)); // don't fill with background color if user wants to see trails
+ }
+
+ for (int i = 0; i < numBalls; i++) {
+ float timeSinceLastUpdate = float((strip.now - balls[i].lastBounceUpdate))/cfac;
+ float thisHeight = balls[i].height + balls[i].velocity * timeSinceLastUpdate; // this method keeps higher resolution
+ // test if intensity level was increased and some balls are way off the track then put them back
+ if (thisHeight < -0.5f || thisHeight > 1.5f) {
+ thisHeight = balls[i].height = (float(random16(0, 10000)) / 10000.0f); // from 0. to 1.
+ balls[i].lastBounceUpdate = strip.now;
+ }
+ // check if reached ends of the strip
+ if ((thisHeight <= 0.0f && balls[i].velocity < 0.0f) || (thisHeight >= 1.0f && balls[i].velocity > 0.0f)) {
+ balls[i].velocity = -balls[i].velocity; // reverse velocity
+ balls[i].lastBounceUpdate = strip.now;
+ balls[i].height = thisHeight;
+ }
+ // check for collisions
+ if (SEGMENT.check1) {
+ for (int j = i+1; j < numBalls; j++) {
+ if (balls[j].velocity != balls[i].velocity) {
+ // tcollided + balls[j].lastBounceUpdate is acutal time of collision (this keeps precision with long to float conversions)
+ float tcollided = (cfac*(balls[i].height - balls[j].height) +
+ balls[i].velocity*float(balls[j].lastBounceUpdate - balls[i].lastBounceUpdate))/(balls[j].velocity - balls[i].velocity);
+
+ if ((tcollided > 2.0f) && (tcollided < float(strip.now - balls[j].lastBounceUpdate))) { // 2ms minimum to avoid duplicate bounces
+ balls[i].height = balls[i].height + balls[i].velocity*(tcollided + float(balls[j].lastBounceUpdate - balls[i].lastBounceUpdate))/cfac;
+ balls[j].height = balls[i].height;
+ balls[i].lastBounceUpdate = (unsigned long)(tcollided + 0.5f) + balls[j].lastBounceUpdate;
+ balls[j].lastBounceUpdate = balls[i].lastBounceUpdate;
+ float vtmp = balls[i].velocity;
+ balls[i].velocity = ((balls[i].mass - balls[j].mass)*vtmp + 2.0f*balls[j].mass*balls[j].velocity)/(balls[i].mass + balls[j].mass);
+ balls[j].velocity = ((balls[j].mass - balls[i].mass)*balls[j].velocity + 2.0f*balls[i].mass*vtmp) /(balls[i].mass + balls[j].mass);
+ thisHeight = balls[i].height + balls[i].velocity*(strip.now - balls[i].lastBounceUpdate)/cfac;
+ }
+ }
+ }
+ }
+
+ uint32_t color = SEGCOLOR(0);
+ if (SEGMENT.palette) {
+ //color = SEGMENT.color_wheel(i*(256/MAX(numBalls, 8)));
+ color = SEGMENT.color_from_palette(i*255/numBalls, false, PALETTE_SOLID_WRAP, 0);
+ } else if (hasCol2) {
+ color = SEGCOLOR(i % NUM_COLORS);
+ }
+
+ if (thisHeight < 0.0f) thisHeight = 0.0f;
+ if (thisHeight > 1.0f) thisHeight = 1.0f;
+ uint16_t pos = round(thisHeight * (SEGLEN - 1));
+ SEGMENT.setPixelColor(pos, color);
+ balls[i].lastBounceUpdate = strip.now;
+ balls[i].height = thisHeight;
+ }
+
+ return FRAMETIME;
+}
+static const char _data_FX_MODE_ROLLINGBALLS[] PROGMEM = "Rolling Balls@!,# of balls,,,,Collisions,Overlay,Trails;!,!,!;!;1;m12=1"; //bar
+
+
/*
* Sinelon stolen from FASTLED examples
*/
@@ -2932,7 +3031,7 @@ uint16_t mode_glitter()
static const char _data_FX_MODE_GLITTER[] PROGMEM = "Glitter@!,!,,,,,Overlay;1,2,Glitter color;!;;pal=0,m12=0"; //pixels
-//Solid colour background with glitter
+//Solid colour background with glitter (can be replaced by Glitter)
uint16_t mode_solid_glitter()
{
SEGMENT.fill(SEGCOLOR(0));
@@ -3022,10 +3121,9 @@ static const char _data_FX_MODE_POPCORN[] PROGMEM = "Popcorn@!,!,,,,,Overlay;!,!
uint16_t candle(bool multi)
{
- if (multi)
- {
+ if (multi && SEGLEN > 1) {
//allocate segment data
- uint16_t dataSize = (SEGLEN -1) *3; //max. 1365 pixels (ESP8266)
+ uint16_t dataSize = max(1, SEGLEN -1) *3; //max. 1365 pixels (ESP8266)
if (!SEGENV.allocateData(dataSize)) return candle(false); //allocation failed
}
@@ -3158,7 +3256,7 @@ uint16_t mode_starburst(void) {
if (random8((144-(SEGMENT.speed >> 1))) == 0 && stars[j].birth == 0)
{
// Pick a random color and location.
- uint16_t startPos = random16(SEGLEN-1);
+ uint16_t startPos = (SEGLEN > 1) ? random16(SEGLEN-1) : 0;
float multiplier = (float)(random8())/255.0 * 1.0;
stars[j].color = CRGB(SEGMENT.color_wheel(random8()));
@@ -3402,7 +3500,7 @@ uint16_t mode_drip(void)
uint8_t numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3
float gravity = -0.0005 - (SEGMENT.speed/50000.0);
- gravity *= SEGLEN-1;
+ gravity *= max(1, SEGLEN-1);
int sourcedrop = 12;
for (int j=0;jstep == 0) { // init brick
- // speed calcualtion: a single brick should reach bottom of strip in X seconds
+ // speed calculation: a single brick should reach bottom of strip in X seconds
// if the speed is set to 1 this should take 5s and at 255 it should take 0.25s
// as this is dependant on SEGLEN it should be taken into account and the fact that effect runs every FRAMETIME s
int speed = SEGMENT.speed ? SEGMENT.speed : random8(1,255);
@@ -3586,14 +3684,14 @@ static const char _data_FX_MODE_PLASMA[] PROGMEM = "Plasma@Phase,!;!;!";
/*
* Percentage display
- * Intesity values from 0-100 turn on the leds.
+ * Intensity values from 0-100 turn on the leds.
*/
uint16_t mode_percent(void) {
uint8_t percent = SEGMENT.intensity;
percent = constrain(percent, 0, 200);
- uint16_t active_leds = (percent < 100) ? SEGLEN * percent / 100.0
- : SEGLEN * (200 - percent) / 100.0;
+ uint16_t active_leds = (percent < 100) ? roundf(SEGLEN * percent / 100.0f)
+ : roundf(SEGLEN * (200 - percent) / 100.0f);
uint8_t size = (1 + ((SEGMENT.speed * SEGLEN) >> 11));
if (SEGMENT.speed == 255) size = 255;
@@ -3639,7 +3737,7 @@ static const char _data_FX_MODE_PERCENT[] PROGMEM = "Percent@,% of fill,,,,One c
/*
* Modulates the brightness similar to a heartbeat
- * (unimplemented?) tries to draw an ECG aproximation on a 2D matrix
+ * (unimplemented?) tries to draw an ECG approximation on a 2D matrix
*/
uint16_t mode_heartbeat(void) {
uint8_t bpm = 40 + (SEGMENT.speed >> 3);
@@ -4317,7 +4415,7 @@ uint16_t mode_tv_simulator(void) {
// how much time is elapsed ?
tvSimulator->elapsed = millis() - tvSimulator->startTime;
- // fade from prev volor to next color
+ // fade from prev color to next color
if (tvSimulator->elapsed < tvSimulator->fadeTime) {
r = map(tvSimulator->elapsed, 0, tvSimulator->fadeTime, tvSimulator->pr, nr);
g = map(tvSimulator->elapsed, 0, tvSimulator->fadeTime, tvSimulator->pg, ng);
@@ -4584,34 +4682,28 @@ uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulma
const uint16_t rows = SEGMENT.virtualHeight();
uint16_t x, y;
- // initialize on first call
- if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
- SEGMENT.fill(BLACK);
- }
-
SEGMENT.fadeToBlackBy(16 + (SEGMENT.speed>>3)); // create fading trails
unsigned long t = millis()/128; // timebase
// outer stars
for (size_t i = 0; i < 8; i++) {
x = beatsin8(SEGMENT.custom1>>3, 0, cols - 1, 0, ((i % 2) ? 128 : 0) + t * i);
y = beatsin8(SEGMENT.intensity>>3, 0, rows - 1, 0, ((i % 2) ? 192 : 64) + t * i);
- SEGMENT.addPixelColorXY(x, y, CHSV(i*32, 255, 255));
+ SEGMENT.addPixelColorXY(x, y, SEGMENT.color_from_palette(i*32, false, PALETTE_SOLID_WRAP, SEGMENT.check1?0:255));
}
// inner stars
for (size_t i = 0; i < 4; i++) {
x = beatsin8(SEGMENT.custom2>>3, cols/4, cols - 1 - cols/4, 0, ((i % 2) ? 128 : 0) + t * i);
y = beatsin8(SEGMENT.custom3 , rows/4, rows - 1 - rows/4, 0, ((i % 2) ? 192 : 64) + t * i);
- SEGMENT.addPixelColorXY(x, y, CHSV(i*32, 255, 255));
+ SEGMENT.addPixelColorXY(x, y, SEGMENT.color_from_palette(255-i*64, false, PALETTE_SOLID_WRAP, SEGMENT.check1?0:255));
}
// central white dot
- SEGMENT.setPixelColorXY(cols/2, rows/2, CHSV(0, 0, 255));
+ SEGMENT.setPixelColorXY(cols/2, rows/2, WHITE);
// blur everything a bit
SEGMENT.blur(16);
return FRAMETIME;
} // mode_2DBlackHole()
-static const char _data_FX_MODE_2DBLACKHOLE[] PROGMEM = "Black Hole@Fade rate,Outer Y freq.,Outer X freq.,Inner X freq.,Inner Y freq.;;;2";
+static const char _data_FX_MODE_2DBLACKHOLE[] PROGMEM = "Black Hole@Fade rate,Outer Y freq.,Outer X freq.,Inner X freq.,Inner Y freq.,Solid;!;!;2;pal=11";
////////////////////////////
@@ -4624,8 +4716,6 @@ uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.so
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
- SEGMENT.fill(BLACK);
SEGENV.aux0 = 0; // start with red hue
}
@@ -4677,13 +4767,7 @@ uint16_t mode_2Ddna(void) { // dna originally by by ldirko at https://pa
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
- if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
- SEGMENT.fill(BLACK);
- }
-
SEGMENT.fadeToBlackBy(64);
-
for (int i = 0; i < cols; i++) {
SEGMENT.setPixelColorXY(i, beatsin8(SEGMENT.speed/8, 0, rows-1, 0, i*4 ), ColorFromPalette(SEGPALETTE, i*5+millis()/17, beatsin8(5, 55, 255, 0, i*10), LINEARBLEND));
SEGMENT.setPixelColorXY(i, beatsin8(SEGMENT.speed/8, 0, rows-1, 0, i*4+128), ColorFromPalette(SEGPALETTE, i*5+128+millis()/17, beatsin8(5, 55, 255, 0, i*10+128), LINEARBLEND));
@@ -4705,7 +4789,6 @@ uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulma
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@@ -4750,20 +4833,15 @@ uint16_t mode_2DDrift() { // By: Stepko https://editor.soulmateli
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
- if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
- SEGMENT.fill(BLACK);
- }
-
SEGMENT.fadeToBlackBy(128);
-
const uint16_t maxDim = MAX(cols, rows)/2;
unsigned long t = millis() / (32 - (SEGMENT.speed>>3));
+ unsigned long t_20 = t/20; // softhack007: pre-calculating this gives about 10% speedup
for (float i = 1; i < maxDim; i += 0.25) {
float angle = radians(t * (maxDim - i));
uint16_t myX = (cols>>1) + (uint16_t)(sin_t(angle) * i) + (cols%2);
uint16_t myY = (rows>>1) + (uint16_t)(cos_t(angle) * i) + (rows%2);
- SEGMENT.setPixelColorXY(myX, myY, ColorFromPalette(SEGPALETTE, (i * 20) + (t / 20), 255, LINEARBLEND));
+ SEGMENT.setPixelColorXY(myX, myY, ColorFromPalette(SEGPALETTE, (i * 20) + t_20, 255, LINEARBLEND));
}
SEGMENT.blur(SEGMENT.intensity>>3);
@@ -4782,7 +4860,6 @@ uint16_t mode_2Dfirenoise(void) { // firenoise2d. By Andrew Tuline
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@@ -4816,11 +4893,6 @@ uint16_t mode_2DFrizzles(void) { // By: Stepko https://editor.so
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
- if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
- SEGMENT.fill(BLACK);
- }
-
SEGMENT.fadeToBlackBy(16);
for (size_t i = 8; i > 0; i--) {
SEGMENT.addPixelColorXY(beatsin8(SEGMENT.speed/8 + i, 0, cols - 1),
@@ -4856,8 +4928,6 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
CRGB backgroundColor = SEGCOLOR(1);
- if (SEGENV.call == 0) SEGMENT.setUpLeds();
-
if (SEGENV.call == 0 || strip.now - SEGMENT.step > 3000) {
SEGENV.step = strip.now;
SEGENV.aux0 = 0;
@@ -4914,7 +4984,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
} // i,j
// Rules of Life
- uint32_t col = prevLeds[XY(x,y)];
+ uint32_t col = uint32_t(prevLeds[XY(x,y)]) & 0x00FFFFFF; // uint32_t operator returns RGBA, we want RGBW -> cut off "alpha" byte
uint32_t bgc = RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0);
if ((col != bgc) && (neighbors < 2)) SEGMENT.setPixelColorXY(x,y, bgc); // Loneliness
else if ((col != bgc) && (neighbors > 3)) SEGMENT.setPixelColorXY(x,y, bgc); // Overpopulation
@@ -5116,15 +5186,16 @@ static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,F
///////////////////////
// 2D Matrix //
///////////////////////
-uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams. Adapted by Andrew Tuline & improved by merkisoft and ewowi.
+uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams. Adapted by Andrew Tuline & improved by merkisoft and ewowi, and softhack007.
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
+ SEGENV.aux0 = SEGENV.aux1 = UINT16_MAX;
+ SEGENV.step = 0;
}
uint8_t fade = map(SEGMENT.custom1, 0, 255, 50, 250); // equals trail size
@@ -5142,32 +5213,43 @@ uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams.
if (strip.now - SEGENV.step >= speed) {
SEGENV.step = strip.now;
+ // find out what color value is returned by gPC for a "falling code" example pixel
+ // the color values returned may differ from the previously set values, due to
+ // - auto brightness limiter (dimming)
+ // - lossy color buffer (when not using global buffer)
+ // - color balance correction
+ // - segment opacity
+ CRGB oldSpawnColor = spawnColor;
+ if ((SEGENV.aux0 < cols) && (SEGENV.aux1 < rows)) { // we have a hint from last run
+ oldSpawnColor = SEGMENT.getPixelColorXY(SEGENV.aux0, SEGENV.aux1); // find color of previous spawns
+ SEGENV.aux1 ++; // our sample pixel will be one row down the next time
+ }
+ if ((oldSpawnColor == CRGB::Black) || (oldSpawnColor == trailColor)) oldSpawnColor = spawnColor; // reject "black", as it would mean that ALL pixels create trails
+
+ // move pixels one row down. Falling codes keep color and add trail pixels; all others pixels are faded
for (int row=rows-1; row>=0; row--) {
for (int col=0; col= rows); // empty screen means that the last falling code has moved out of screen area
// spawn new falling code
- if (random8() < SEGMENT.intensity || emptyScreen) {
+ if (random8() <= SEGMENT.intensity || emptyScreen) {
uint8_t spawnX = random8(cols);
SEGMENT.setPixelColorXY(spawnX, 0, spawnColor);
+ // update hint for next run
+ SEGENV.aux0 = spawnX;
+ SEGENV.aux1 = 0;
}
} // if millis
@@ -5267,14 +5349,8 @@ uint16_t mode_2DPlasmaball(void) { // By: Stepko https://edito
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
- if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
- SEGMENT.fill(BLACK);
- }
-
SEGMENT.fadeToBlackBy(SEGMENT.custom1>>2);
-
- float t = millis() / (33 - SEGMENT.speed/8);
+ uint_fast32_t t = (millis() * 8) / (256 - SEGMENT.speed); // optimized to avoid float
for (int i = 0; i < cols; i++) {
uint16_t thisVal = inoise8(i * 30, t, t);
uint16_t thisMax = map(thisVal, 0, 255, 0, cols-1);
@@ -5316,7 +5392,6 @@ uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https
CRGBPalette16 auroraPalette = {0x000000, 0x003300, 0x006600, 0x009900, 0x00cc00, 0x00ff00, 0x33ff00, 0x66ff00, 0x99ff00, 0xccff00, 0xffff00, 0xffcc00, 0xff9900, 0xff6600, 0xff3300, 0xff0000};
if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
SEGENV.step = 0;
}
@@ -5365,13 +5440,7 @@ uint16_t mode_2DPulser(void) { // By: ldirko https://edi
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
- if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
- SEGMENT.fill(BLACK);
- }
-
SEGMENT.fadeToBlackBy(8 - (SEGMENT.intensity>>5));
-
uint32_t a = strip.now / (18 - SEGMENT.speed / 16);
uint16_t x = (a / 14) % cols;
uint16_t y = map((sin8(a * 5) + sin8(a * 4) + sin8(a * 2)), 0, 765, rows-1, 0);
@@ -5394,7 +5463,6 @@ uint16_t mode_2DSindots(void) { // By: ldirko http
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@@ -5425,15 +5493,9 @@ uint16_t mode_2Dsquaredswirl(void) { // By: Mark Kriegsman. https://g
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
- if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
- SEGMENT.fill(BLACK);
- }
-
const uint8_t kBorderWidth = 2;
SEGMENT.fadeToBlackBy(24);
-
uint8_t blurAmount = SEGMENT.custom3>>1; // reduced resolution slider
SEGMENT.blur(blurAmount);
@@ -5469,7 +5531,6 @@ uint16_t mode_2DSunradiation(void) { // By: ldirko https://edi
byte *bump = reinterpret_cast(SEGENV.data);
if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@@ -5517,7 +5578,6 @@ uint16_t mode_2Dtartan(void) { // By: Elliott Kember https://editor.so
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@@ -5556,11 +5616,6 @@ uint16_t mode_2Dspaceships(void) { //// Space ships by stepko (c)05.02.21 [ht
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
- if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
- SEGMENT.fill(BLACK);
- }
-
uint32_t tb = strip.now >> 12; // every ~4s
if (tb > SEGENV.step) {
int8_t dir = ++SEGENV.aux0;
@@ -5626,8 +5681,6 @@ uint16_t mode_2Dcrazybees(void) {
bee_t *bee = reinterpret_cast(SEGENV.data);
if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
- SEGMENT.fill(BLACK);
for (size_t i = 0; i < n; i++) {
bee[i].posX = random8(0, cols);
bee[i].posY = random8(0, rows);
@@ -5636,7 +5689,7 @@ uint16_t mode_2Dcrazybees(void) {
}
if (millis() > SEGENV.step) {
- SEGENV.step = millis() + (FRAMETIME * 8 / ((SEGMENT.speed>>5)+1));
+ SEGENV.step = millis() + (FRAMETIME * 16 / ((SEGMENT.speed>>4)+1));
SEGMENT.fadeToBlackBy(32);
@@ -5696,13 +5749,12 @@ uint16_t mode_2Dghostrider(void) {
const size_t maxLighters = min(cols + rows, LIGHTERS_AM);
- if (SEGENV.call == 0) SEGMENT.setUpLeds();
if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) {
SEGENV.aux0 = cols;
SEGENV.aux1 = rows;
- SEGMENT.fill(BLACK);
random16_set_seed(strip.now);
lighter->angleSpeed = random8(0,20) - 10;
+ lighter->gAngle = random16();
lighter->Vspeed = 5;
lighter->gPosX = (cols/2) * 10;
lighter->gPosY = (rows/2) * 10;
@@ -5710,6 +5762,7 @@ uint16_t mode_2Dghostrider(void) {
lighter->lightersPosX[i] = lighter->gPosX;
lighter->lightersPosY[i] = lighter->gPosY + i;
lighter->time[i] = i * 2;
+ lighter->reg[i] = false;
}
}
@@ -5781,11 +5834,10 @@ uint16_t mode_2Dfloatingblobs(void) {
if (!SEGENV.allocateData(sizeof(blob_t))) return mode_static(); //allocation failed
blob_t *blob = reinterpret_cast(SEGENV.data);
- if (SEGENV.call == 0) SEGMENT.setUpLeds();
if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) {
SEGENV.aux0 = cols; // re-initialise if virtual size changes
SEGENV.aux1 = rows;
- SEGMENT.fill(BLACK);
+ //SEGMENT.fill(BLACK);
for (size_t i = 0; i < MAX_BLOBS; i++) {
blob->r[i] = random8(1, cols>8 ? (cols/4) : 2);
blob->sX[i] = (float) random8(3, cols) / (float)(256 - SEGMENT.speed); // speed x
@@ -5799,7 +5851,7 @@ uint16_t mode_2Dfloatingblobs(void) {
}
}
- SEGMENT.fadeToBlackBy(20);
+ SEGMENT.fadeToBlackBy((SEGMENT.custom2>>3)+1);
// Bounce balls around
for (size_t i = 0; i < Amount; i++) {
@@ -5855,7 +5907,7 @@ uint16_t mode_2Dfloatingblobs(void) {
return FRAMETIME;
}
#undef MAX_BLOBS
-static const char _data_FX_MODE_2DBLOBS[] PROGMEM = "Blobs@!,# blobs,Blur;!;!;2;c1=8";
+static const char _data_FX_MODE_2DBLOBS[] PROGMEM = "Blobs@!,# blobs,Blur,Trail;!;!;2;c1=8";
////////////////////////////
@@ -5867,8 +5919,8 @@ uint16_t mode_2Dscrollingtext(void) {
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
- int letterWidth;
- int letterHeight;
+ int letterWidth, rotLW;
+ int letterHeight, rotLH;
switch (map(SEGMENT.custom2, 0, 255, 1, 5)) {
default:
case 1: letterWidth = 4; letterHeight = 6; break;
@@ -5877,59 +5929,86 @@ uint16_t mode_2Dscrollingtext(void) {
case 4: letterWidth = 7; letterHeight = 9; break;
case 5: letterWidth = 5; letterHeight = 12; break;
}
- const bool zero = SEGMENT.check3;
- const int yoffset = map(SEGMENT.intensity, 0, 255, -rows/2, rows/2) + (rows-letterHeight)/2;
+ // letters are rotated
+ if (((SEGMENT.custom3+1)>>3) % 2) {
+ rotLH = letterWidth;
+ rotLW = letterHeight;
+ } else {
+ rotLW = letterWidth;
+ rotLH = letterHeight;
+ }
+
char text[WLED_MAX_SEGNAME_LEN+1] = {'\0'};
if (SEGMENT.name) for (size_t i=0,j=0; i31 && SEGMENT.name[i]<128) text[j++] = SEGMENT.name[i];
+ const bool zero = strchr(text, '0') != nullptr;
- if (!strlen(text)
- || !strncmp_P(text,PSTR("#DATE"),5)
- || !strncmp_P(text,PSTR("#DDMM"),5)
- || !strncmp_P(text,PSTR("#MMDD"),5)
- || !strncmp_P(text,PSTR("#TIME"),5)
- || !strncmp_P(text,PSTR("#HHMM"),5)) { // fallback if empty segment name: display date and time
- char sec[5];
- byte AmPmHour = hour(localTime);
- boolean isitAM = true;
- if (useAMPM) {
- if (AmPmHour > 11) { AmPmHour -= 12; isitAM = false; }
- if (AmPmHour == 0) { AmPmHour = 12; }
- }
- if (useAMPM) sprintf_P(sec, PSTR(" %2s"), (isitAM ? "AM" : "PM"));
- else sprintf_P(sec, PSTR(":%02d"), second(localTime));
- if (!strncmp_P(text,PSTR("#DATE"),5)) sprintf_P(text, zero?PSTR("%02d.%02d.%04d"):PSTR("%d.%d.%d"), day(localTime), month(localTime), year(localTime));
- else if (!strncmp_P(text,PSTR("#DDMM"),5)) sprintf_P(text, zero?PSTR("%02d.%02d"):PSTR("%d.%d"), day(localTime), month(localTime));
- else if (!strncmp_P(text,PSTR("#MMDD"),5)) sprintf_P(text, zero?PSTR("%02d/%02d"):PSTR("%d/%d"), month(localTime), day(localTime));
- else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, zero?PSTR("%02d:%02d%s"):PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec);
- else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, zero?PSTR("%02d:%02d"):PSTR("%d:%02d"), AmPmHour, minute(localTime));
- else sprintf_P(text, zero?PSTR("%s %02d, %04d %02d:%02d%s"):PSTR("%s %d, %d %d:%02d%s"), monthShortStr(month(localTime)), day(localTime), year(localTime), AmPmHour, minute(localTime), sec);
+ char sec[5];
+ int AmPmHour = hour(localTime);
+ bool isitAM = true;
+ if (useAMPM) {
+ if (AmPmHour > 11) { AmPmHour -= 12; isitAM = false; }
+ if (AmPmHour == 0) { AmPmHour = 12; }
+ sprintf_P(sec, PSTR(" %2s"), (isitAM ? "AM" : "PM"));
+ } else {
+ sprintf_P(sec, PSTR(":%02d"), second(localTime));
}
- const int numberOfLetters = strlen(text);
- if (SEGENV.step < millis()) {
- if ((numberOfLetters * letterWidth) > cols) ++SEGENV.aux0 %= (numberOfLetters * letterWidth) + cols; // offset
- else SEGENV.aux0 = (cols + (numberOfLetters * letterWidth))/2;
+ if (!strlen(text)) { // fallback if empty segment name: display date and time
+ sprintf_P(text, PSTR("%s %d, %d %d:%02d%s"), monthShortStr(month(localTime)), day(localTime), year(localTime), AmPmHour, minute(localTime), sec);
+ } else {
+ if (!strncmp_P(text,PSTR("#DATE"),5)) sprintf_P(text, zero?PSTR("%02d.%02d.%04d"):PSTR("%d.%d.%d"), day(localTime), month(localTime), year(localTime));
+ else if (!strncmp_P(text,PSTR("#DDMM"),5)) sprintf_P(text, zero?PSTR("%02d.%02d") :PSTR("%d.%d"), day(localTime), month(localTime));
+ else if (!strncmp_P(text,PSTR("#MMDD"),5)) sprintf_P(text, zero?PSTR("%02d/%02d") :PSTR("%d/%d"), month(localTime), day(localTime));
+ else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, zero?PSTR("%02d:%02d%s") :PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec);
+ else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, zero?PSTR("%02d:%02d") :PSTR("%d:%02d"), AmPmHour, minute(localTime));
+ else if (!strncmp_P(text,PSTR("#HH"),3)) sprintf_P(text, zero?PSTR("%02d") :PSTR("%d"), AmPmHour);
+ else if (!strncmp_P(text,PSTR("#MM"),3)) sprintf_P(text, zero?PSTR("%02d") :PSTR("%d"), minute(localTime));
+ }
+
+ const int numberOfLetters = strlen(text);
+ const unsigned long now = millis(); // reduce millis() calls
+ int width = (numberOfLetters * rotLW);
+ int yoffset = map(SEGMENT.intensity, 0, 255, -rows/2, rows/2) + (rows-rotLH)/2;
+ if (width <= cols) {
+ // scroll vertically (e.g. ^^ Way out ^^) if it fits
+ int speed = map(SEGMENT.speed, 0, 255, 5000, 1000);
+ int frac = now % speed + 1;
+ if (SEGMENT.intensity == 255) {
+ yoffset = (2 * frac * rows)/speed - rows;
+ } else if (SEGMENT.intensity == 0) {
+ yoffset = rows - (2 * frac * rows)/speed;
+ }
+ }
+
+ if (SEGENV.step < now) {
+ // calculate start offset
+ if (width > cols) {
+ if (SEGMENT.check3) {
+ if (SEGENV.aux0 == 0) SEGENV.aux0 = width + cols - 1;
+ else --SEGENV.aux0;
+ } else ++SEGENV.aux0 %= width + cols;
+ } else SEGENV.aux0 = (cols + width)/2;
++SEGENV.aux1 &= 0xFF; // color shift
- SEGENV.step = millis() + map(SEGMENT.speed, 0, 255, 10*FRAMETIME_FIXED, 2*FRAMETIME_FIXED);
- if (!SEGMENT.check2) {
- for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++ )
- SEGMENT.blendPixelColorXY(x, y, SEGCOLOR(1), 255 - (SEGMENT.custom1>>1));
- }
+ SEGENV.step = now + map(SEGMENT.speed, 0, 255, 250, 50); // shift letters every ~250ms to ~50ms
}
+
+ if (!SEGMENT.check2) SEGMENT.fade_out(255 - (SEGMENT.custom1>>4)); // trail
+
for (int i = 0; i < numberOfLetters; i++) {
- if (int(cols) - int(SEGENV.aux0) + letterWidth*(i+1) < 0) continue; // don't draw characters off-screen
+ int xoffset = int(cols) - int(SEGENV.aux0) + rotLW*i;
+ if (xoffset + rotLW < 0) continue; // don't draw characters off-screen
uint32_t col1 = SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0);
uint32_t col2 = BLACK;
if (SEGMENT.check1 && SEGMENT.palette == 0) {
col1 = SEGCOLOR(0);
col2 = SEGCOLOR(2);
}
- SEGMENT.drawCharacter(text[i], int(cols) - int(SEGENV.aux0) + letterWidth*i, yoffset, letterWidth, letterHeight, col1, col2);
+ SEGMENT.drawCharacter(text[i], xoffset, yoffset, letterWidth, letterHeight, col1, col2, map(SEGMENT.custom3, 0, 31, -2, 2));
}
return FRAMETIME;
}
-static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,,Gradient,Overlay,0;!,!,Gradient;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0";
+static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,Rotate,Gradient,Overlay,Reverse;!,!,Gradient;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0";
////////////////////////////
@@ -5946,11 +6025,6 @@ uint16_t mode_2Ddriftrose(void) {
const float CY = (rows-rows%2)/2.f - .5f;
const float L = min(cols, rows) / 2.f;
- if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
- SEGMENT.fill(BLACK);
- }
-
SEGMENT.fadeToBlackBy(32+(SEGMENT.speed>>3));
for (size_t i = 1; i < 37; i++) {
uint32_t x = (CX + (sin_t(radians(i * 10)) * (beatsin8(i, 0, L*2)-L))) * 255.f;
@@ -6102,7 +6176,6 @@ uint16_t mode_2DSwirl(void) {
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@@ -6124,8 +6197,6 @@ uint16_t mode_2DSwirl(void) {
float volumeSmth = *(float*) um_data->u_data[0]; //ewowi: use instead of sampleAvg???
int16_t volumeRaw = *(int16_t*) um_data->u_data[1];
- // printUmData();
-
SEGMENT.addPixelColorXY( i, j, ColorFromPalette(SEGPALETTE, (ms / 11 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 11, 200, 255);
SEGMENT.addPixelColorXY( j, i, ColorFromPalette(SEGPALETTE, (ms / 13 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 13, 200, 255);
SEGMENT.addPixelColorXY(ni,nj, ColorFromPalette(SEGPALETTE, (ms / 17 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 17, 200, 255);
@@ -6148,11 +6219,6 @@ uint16_t mode_2DWaverly(void) {
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
- if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
- SEGMENT.fill(BLACK);
- }
-
um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
@@ -6200,6 +6266,7 @@ typedef struct Gravity {
// * GRAVCENTER //
///////////////////////
uint16_t mode_gravcenter(void) { // Gravcenter. By Andrew Tuline.
+ if (SEGLEN == 1) return mode_static();
const uint16_t dataSize = sizeof(gravity);
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@@ -6216,9 +6283,9 @@ uint16_t mode_gravcenter(void) { // Gravcenter. By Andrew Tuline.
SEGMENT.fade_out(251); // 30%
float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f;
- segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivty" upscaling
+ segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivity" upscaling
- float mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 32, 0, (float)SEGLEN/2.0); // map to pixels available in current segment
+ float mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 32, 0, (float)SEGLEN/2.0f); // map to pixels available in current segment
uint16_t tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing.
uint8_t gravity = 8 - SEGMENT.speed/32;
@@ -6248,6 +6315,7 @@ static const char _data_FX_MODE_GRAVCENTER[] PROGMEM = "Gravcenter@Rate of fall,
// * GRAVCENTRIC //
///////////////////////
uint16_t mode_gravcentric(void) { // Gravcentric. By Andrew Tuline.
+ if (SEGLEN == 1) return mode_static();
uint16_t dataSize = sizeof(gravity);
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@@ -6266,10 +6334,10 @@ uint16_t mode_gravcentric(void) { // Gravcentric. By Andrew
//SEGMENT.fade_out(240); // twice? really?
SEGMENT.fade_out(253); // 50%
- float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0;
- segmentSampleAvg *= 0.125f; // divide by 8, to compensate for later "sensitivty" upscaling
+ float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f;
+ segmentSampleAvg *= 0.125f; // divide by 8, to compensate for later "sensitivity" upscaling
- float mySampleAvg = mapf(segmentSampleAvg*2.0, 0.0f, 32.0f, 0.0f, (float)SEGLEN/2.0); // map to pixels availeable in current segment
+ float mySampleAvg = mapf(segmentSampleAvg*2.0, 0.0f, 32.0f, 0.0f, (float)SEGLEN/2.0f); // map to pixels availeable in current segment
int tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing.
uint8_t gravity = 8 - SEGMENT.speed/32;
@@ -6299,6 +6367,7 @@ static const char _data_FX_MODE_GRAVCENTRIC[] PROGMEM = "Gravcentric@Rate of fal
// * GRAVIMETER //
///////////////////////
uint16_t mode_gravimeter(void) { // Gravmeter. By Andrew Tuline.
+ if (SEGLEN == 1) return mode_static();
uint16_t dataSize = sizeof(gravity);
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@@ -6315,7 +6384,7 @@ uint16_t mode_gravimeter(void) { // Gravmeter. By Andrew Tuline.
SEGMENT.fade_out(249); // 25%
float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0;
- segmentSampleAvg *= 0.25; // divide by 4, to compensate for later "sensitivty" upscaling
+ segmentSampleAvg *= 0.25; // divide by 4, to compensate for later "sensitivity" upscaling
float mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 64, 0, (SEGLEN-1)); // map to pixels availeable in current segment
int tempsamp = constrain(mySampleAvg,0,SEGLEN-1); // Keep the sample from overflowing.
@@ -6345,6 +6414,7 @@ static const char _data_FX_MODE_GRAVIMETER[] PROGMEM = "Gravimeter@Rate of fall,
// * JUGGLES //
//////////////////////
uint16_t mode_juggles(void) { // Juggles. By Andrew Tuline.
+ if (SEGLEN == 1) return mode_static();
um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
@@ -6368,6 +6438,7 @@ static const char _data_FX_MODE_JUGGLES[] PROGMEM = "Juggles@!,# of balls;!,!;!;
// * MATRIPIX //
//////////////////////
uint16_t mode_matripix(void) { // Matripix. By Andrew Tuline.
+ if (SEGLEN == 1) return mode_static();
// even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment
um_data_t *um_data;
@@ -6378,7 +6449,6 @@ uint16_t mode_matripix(void) { // Matripix. By Andrew Tuline.
int16_t volumeRaw = *(int16_t*)um_data->u_data[1];
if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@@ -6400,6 +6470,7 @@ static const char _data_FX_MODE_MATRIPIX[] PROGMEM = "Matripix@!,Brightness;!,!;
// * MIDNOISE //
//////////////////////
uint16_t mode_midnoise(void) { // Midnoise. By Andrew Tuline.
+ if (SEGLEN == 1) return mode_static();
// Changing xdist to SEGENV.aux0 and ydist to SEGENV.aux1.
um_data_t *um_data;
@@ -6413,7 +6484,7 @@ uint16_t mode_midnoise(void) { // Midnoise. By Andrew Tuline.
SEGMENT.fade_out(SEGMENT.speed);
float tmpSound2 = volumeSmth * (float)SEGMENT.intensity / 256.0; // Too sensitive.
- tmpSound2 *= (float)SEGMENT.intensity / 128.0; // Reduce sensitity/length.
+ tmpSound2 *= (float)SEGMENT.intensity / 128.0; // Reduce sensitivity/length.
int maxLen = mapf(tmpSound2, 0, 127, 0, SEGLEN/2);
if (maxLen >SEGLEN/2) maxLen = SEGLEN/2;
@@ -6503,10 +6574,10 @@ static const char _data_FX_MODE_NOISEMETER[] PROGMEM = "Noisemeter@Fade rate,Wid
// * PIXELWAVE //
//////////////////////
uint16_t mode_pixelwave(void) { // Pixelwave. By Andrew Tuline.
+ if (SEGLEN == 1) return mode_static();
// even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment
if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@@ -6579,6 +6650,7 @@ static const char _data_FX_MODE_PLASMOID[] PROGMEM = "Plasmoid@Phase,# of pixels
///////////////////////
// Andrew's crappy peak detector. If I were 40+ years younger, I'd learn signal processing.
uint16_t mode_puddlepeak(void) { // Puddlepeak. By Andrew Tuline.
+ if (SEGLEN == 1) return mode_static();
uint16_t size = 0;
uint8_t fadeVal = map(SEGMENT.speed,0,255, 224, 254);
@@ -6622,6 +6694,7 @@ static const char _data_FX_MODE_PUDDLEPEAK[] PROGMEM = "Puddlepeak@Fade rate,Pud
// * PUDDLES //
//////////////////////
uint16_t mode_puddles(void) { // Puddles. By Andrew Tuline.
+ if (SEGLEN == 1) return mode_static();
uint16_t size = 0;
uint8_t fadeVal = map(SEGMENT.speed, 0, 255, 224, 254);
uint16_t pos = random16(SEGLEN); // Set a random starting position.
@@ -6653,6 +6726,7 @@ static const char _data_FX_MODE_PUDDLES[] PROGMEM = "Puddles@Fade rate,Puddle si
// * PIXELS //
//////////////////////
uint16_t mode_pixels(void) { // Pixels. By Andrew Tuline.
+ if (SEGLEN == 1) return mode_static();
if (!SEGENV.allocateData(32*sizeof(uint8_t))) return mode_static(); //allocation failed
uint8_t *myVals = reinterpret_cast(SEGENV.data); // Used to store a pile of samples because WLED frame rate and WLED sample rate are not synchronized. Frame rate is too low.
@@ -6686,6 +6760,7 @@ static const char _data_FX_MODE_PIXELS[] PROGMEM = "Pixels@Fade rate,# of pixels
// ** Blurz //
//////////////////////
uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline.
+ if (SEGLEN == 1) return mode_static();
// even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment
um_data_t *um_data;
@@ -6706,7 +6781,7 @@ uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline.
SEGENV.step += FRAMETIME;
if (SEGENV.step > SPEED_FORMULA_L) {
uint16_t segLoc = random16(SEGLEN);
- SEGMENT.setPixelColor(segLoc, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(2*fftResult[SEGENV.aux0%16]*240/(SEGLEN-1), false, PALETTE_SOLID_WRAP, 0), 2*fftResult[SEGENV.aux0%16]));
+ SEGMENT.setPixelColor(segLoc, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(2*fftResult[SEGENV.aux0%16]*240/max(1, SEGLEN-1), false, PALETTE_SOLID_WRAP, 0), 2*fftResult[SEGENV.aux0%16]));
++(SEGENV.aux0) %= 16; // make sure it doesn't cross 16
SEGENV.step = 1;
@@ -6722,6 +6797,7 @@ static const char _data_FX_MODE_BLURZ[] PROGMEM = "Blurz@Fade rate,Blur;!,Color
// ** DJLight //
/////////////////////////
uint16_t mode_DJLight(void) { // Written by ??? Adapted by Will Tatam.
+ if (SEGLEN == 1) return mode_static();
const int mid = SEGLEN / 2;
um_data_t *um_data;
@@ -6732,7 +6808,6 @@ uint16_t mode_DJLight(void) { // Written by ??? Adapted by Wil
uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@@ -6756,6 +6831,7 @@ static const char _data_FX_MODE_DJLIGHT[] PROGMEM = "DJ Light@Speed;;;1f;m12=2,s
// ** Freqmap //
////////////////////
uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN. Would be better if a higher framerate.
+ if (SEGLEN == 1) return mode_static();
// Start frequency = 60 Hz and log10(60) = 1.78
// End frequency = MAX_FREQUENCY in Hz and lo10(MAX_FREQUENCY) = MAX_FREQ_LOG10
@@ -6792,6 +6868,7 @@ static const char _data_FX_MODE_FREQMAP[] PROGMEM = "Freqmap@Fade rate,Starting
// ** Freqmatrix //
///////////////////////
uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Pleschung.
+ if (SEGLEN == 1) return mode_static();
um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
@@ -6801,7 +6878,6 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch
float volumeSmth = *(float*)um_data->u_data[0];
if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@@ -6819,7 +6895,7 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch
if (FFT_MajorPeak > MAX_FREQUENCY) FFT_MajorPeak = 1;
// MajorPeak holds the freq. value which is most abundant in the last sample.
- // With our sampling rate of 10240Hz we have a usable freq range from roughtly 80Hz to 10240/2 Hz
+ // With our sampling rate of 10240Hz we have a usable freq range from roughly 80Hz to 10240/2 Hz
// we will treat everything with less than 65Hz as 0
if (FFT_MajorPeak < 80) {
@@ -6840,7 +6916,7 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch
return FRAMETIME;
} // mode_freqmatrix()
-static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Time delay,Sound effect,Low bin,High bin,Sensivity;;;1f;m12=3,si=0"; // Corner, Beatsin
+static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Speed,Sound effect,Low bin,High bin,Sensitivity;;;1f;m12=3,si=0"; // Corner, Beatsin
//////////////////////
@@ -6858,24 +6934,27 @@ uint16_t mode_freqpixels(void) { // Freqpixel. By Andrew Tuline.
}
float FFT_MajorPeak = *(float*)um_data->u_data[4];
float my_magnitude = *(float*)um_data->u_data[5] / 16.0f;
- if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception)
+ if (FFT_MajorPeak < 1) FFT_MajorPeak = 1.0f; // log10(0) is "forbidden" (throws exception)
- uint16_t fadeRate = 2*SEGMENT.speed - SEGMENT.speed*SEGMENT.speed/255; // Get to 255 as quick as you can.
+ // this code translates to speed * (2 - speed/255) which is a) speed*2 or b) speed (when speed is 255)
+ // and since fade_out() can only take 0-255 it will behave incorrectly when speed > 127
+ //uint16_t fadeRate = 2*SEGMENT.speed - SEGMENT.speed*SEGMENT.speed/255; // Get to 255 as quick as you can.
+ uint16_t fadeRate = SEGMENT.speed*SEGMENT.speed; // Get to 255 as quick as you can.
+ fadeRate = map(fadeRate, 0, 65535, 1, 255);
- if (SEGENV.call == 0) SEGMENT.fill(BLACK);
int fadeoutDelay = (256 - SEGMENT.speed) / 64;
if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fade_out(fadeRate);
+ uint8_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(MAX_FREQ_LOG10 - 1.78f); // Scale log10 of frequency values to the 255 colour index.
+ if (FFT_MajorPeak < 61.0f) pixCol = 0; // handle underflow
for (int i=0; i < SEGMENT.intensity/32+1; i++) {
uint16_t locn = random16(0,SEGLEN);
- uint8_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(MAX_FREQ_LOG10 - 1.78f); // Scale log10 of frequency values to the 255 colour index.
- if (FFT_MajorPeak < 61.0f) pixCol = 0; // handle underflow
SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude));
}
return FRAMETIME;
} // mode_freqpixels()
-static const char _data_FX_MODE_FREQPIXELS[] PROGMEM = "Freqpixels@Fade rate,Starting color and # of pixels;;;1f;m12=0,si=0"; // Pixels, Beatsin
+static const char _data_FX_MODE_FREQPIXELS[] PROGMEM = "Freqpixels@Fade rate,Starting color and # of pixels;!,!,;!;1f;m12=0,si=0"; // Pixels, Beatsin
//////////////////////
@@ -6894,6 +6973,7 @@ static const char _data_FX_MODE_FREQPIXELS[] PROGMEM = "Freqpixels@Fade rate,Sta
// As a compromise between speed and accuracy we are currently sampling with 10240Hz, from which we can then determine with a 512bin FFT our max frequency is 5120Hz.
// Depending on the music stream you have you might find it useful to change the frequency mapping.
uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschung.
+ if (SEGLEN == 1) return mode_static();
um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
@@ -6903,7 +6983,6 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun
float volumeSmth = *(float*)um_data->u_data[0];
if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@@ -6921,7 +7000,7 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun
if (FFT_MajorPeak > MAX_FREQUENCY) FFT_MajorPeak = 1.0f;
// MajorPeak holds the freq. value which is most abundant in the last sample.
- // With our sampling rate of 10240Hz we have a usable freq range from roughtly 80Hz to 10240/2 Hz
+ // With our sampling rate of 10240Hz we have a usable freq range from roughly 80Hz to 10240/2 Hz
// we will treat everything with less than 65Hz as 0
if (FFT_MajorPeak < 80) {
@@ -6944,14 +7023,14 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun
return FRAMETIME;
} // mode_freqwave()
-static const char _data_FX_MODE_FREQWAVE[] PROGMEM = "Freqwave@Time delay,Sound effect,Low bin,High bin,Pre-amp;;;1f;m12=2,si=0"; // Circle, Beatsin
+static const char _data_FX_MODE_FREQWAVE[] PROGMEM = "Freqwave@Speed,Sound effect,Low bin,High bin,Pre-amp;;;1f;m12=2,si=0"; // Circle, Beatsin
///////////////////////
// ** Gravfreq //
///////////////////////
uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline.
-
+ if (SEGLEN == 1) return mode_static();
uint16_t dataSize = sizeof(gravity);
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
Gravity* gravcen = reinterpret_cast(SEGENV.data);
@@ -6968,9 +7047,9 @@ uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline.
SEGMENT.fade_out(250);
float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f;
- segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivty" upscaling
+ segmentSampleAvg *= 0.125f; // divide by 8, to compensate for later "sensitivity" upscaling
- float mySampleAvg = mapf(segmentSampleAvg*2.0f, 0,32, 0, (float)SEGLEN/2.0); // map to pixels availeable in current segment
+ float mySampleAvg = mapf(segmentSampleAvg*2.0f, 0,32, 0, (float)SEGLEN/2.0f); // map to pixels availeable in current segment
int tempsamp = constrain(mySampleAvg,0,SEGLEN/2); // Keep the sample from overflowing.
uint8_t gravity = 8 - SEGMENT.speed/32;
@@ -6996,13 +7075,14 @@ uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline.
return FRAMETIME;
} // mode_gravfreq()
-static const char _data_FX_MODE_GRAVFREQ[] PROGMEM = "Gravfreq@Rate of fall,Sensivity;!,!;!;1f;ix=128,m12=0,si=0"; // Pixels, Beatsin
+static const char _data_FX_MODE_GRAVFREQ[] PROGMEM = "Gravfreq@Rate of fall,Sensitivity;!,!;!;1f;ix=128,m12=0,si=0"; // Pixels, Beatsin
//////////////////////
// ** Noisemove //
//////////////////////
uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuline
+ if (SEGLEN == 1) return mode_static();
um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
@@ -7010,8 +7090,6 @@ uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuli
}
uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
- if (SEGENV.call == 0) SEGMENT.fill(BLACK);
- //SEGMENT.fade_out(224); // Just in case something doesn't get faded.
int fadeoutDelay = (256 - SEGMENT.speed) / 96;
if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(4+ SEGMENT.speed/4);
@@ -7031,6 +7109,7 @@ static const char _data_FX_MODE_NOISEMOVE[] PROGMEM = "Noisemove@Speed of perlin
// ** Rocktaves //
//////////////////////
uint16_t mode_rocktaves(void) { // Rocktaves. Same note from each octave is same colour. By: Andrew Tuline
+ if (SEGLEN == 1) return mode_static();
um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
@@ -7039,8 +7118,7 @@ uint16_t mode_rocktaves(void) { // Rocktaves. Same note from eac
float FFT_MajorPeak = *(float*) um_data->u_data[4];
float my_magnitude = *(float*) um_data->u_data[5] / 16.0f;
- if (SEGENV.call == 0) SEGMENT.fill(BLACK);
- SEGMENT.fadeToBlackBy(16); // Just in case something doesn't get faded.
+ SEGMENT.fadeToBlackBy(16); // Just in case something doesn't get faded.
float frTemp = FFT_MajorPeak;
uint8_t octCount = 0; // Octave counter.
@@ -7055,8 +7133,8 @@ uint16_t mode_rocktaves(void) { // Rocktaves. Same note from eac
frTemp = frTemp/2;
}
- frTemp -=132; // This should give us a base musical note of C3
- frTemp = fabsf(frTemp * 2.1f); // Fudge factors to compress octave range starting at 0 and going to 255;
+ frTemp -= 132.0f; // This should give us a base musical note of C3
+ frTemp = fabsf(frTemp * 2.1f); // Fudge factors to compress octave range starting at 0 and going to 255;
uint16_t i = map(beatsin8(8+octCount*4, 0, 255, 0, octCount*8), 0, 255, 0, SEGLEN-1);
i = constrain(i, 0, SEGLEN-1);
@@ -7072,7 +7150,7 @@ static const char _data_FX_MODE_ROCKTAVES[] PROGMEM = "Rocktaves@;!,!;!;1f;m12=1
///////////////////////
// Combines peak detection with FFT_MajorPeak and FFT_Magnitude.
uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tuline
- if (SEGENV.call == 0) SEGMENT.fill(BLACK);
+ if (SEGLEN == 1) return mode_static();
um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
@@ -7088,7 +7166,6 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin
if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception)
if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
SEGENV.aux0 = 255;
SEGMENT.custom1 = *binNum;
@@ -7148,7 +7225,6 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma.
rippleTime = true;
}
- if (SEGENV.call == 0) SEGMENT.fill(BLACK);
int fadeoutDelay = (256 - SEGMENT.speed) / 64;
if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(SEGMENT.speed);
@@ -7205,7 +7281,6 @@ uint16_t mode_2DFunkyPlank(void) { // Written by ??? Adapted by Wil
uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@@ -7418,7 +7493,6 @@ uint16_t mode_2Dsoap() {
// init
if (SEGENV.call == 0) {
- SEGMENT.setUpLeds();
*noise32_x = random16();
*noise32_y = random16();
*noise32_z = random16();
@@ -7452,6 +7526,7 @@ uint16_t mode_2Dsoap() {
int amplitude;
int8_t shiftX = 0; //(SEGMENT.custom1 - 128) / 4;
int8_t shiftY = 0; //(SEGMENT.custom2 - 128) / 4;
+ CRGB ledsbuff[MAX(cols,rows)];
amplitude = (cols >= 16) ? (cols-8)/8 : 1;
for (int y = 0; y < rows; y++) {
@@ -7472,9 +7547,9 @@ uint16_t mode_2Dsoap() {
CRGB PixelB = CRGB::Black;
if ((zF >= 0) && (zF < cols)) PixelB = SEGMENT.getPixelColorXY(zF, y);
else PixelB = ColorFromPalette(SEGPALETTE, ~noise3d[XY(abs(zF),y)]*3);
- CRGB pix = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction)));
- SEGMENT.setPixelColorXY(x, y, pix);
+ ledsbuff[x] = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction)));
}
+ for (int x = 0; x < cols; x++) SEGMENT.setPixelColorXY(x, y, ledsbuff[x]);
}
amplitude = (rows >= 16) ? (rows-8)/8 : 1;
@@ -7496,9 +7571,9 @@ uint16_t mode_2Dsoap() {
CRGB PixelB = CRGB::Black;
if ((zF >= 0) && (zF < rows)) PixelB = SEGMENT.getPixelColorXY(x, zF);
else PixelB = ColorFromPalette(SEGPALETTE, ~noise3d[XY(x,abs(zF))]*3);
- CRGB pix = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction)));
- SEGMENT.setPixelColorXY(x, y, pix);
+ ledsbuff[y] = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction)));
}
+ for (int y = 0; y < rows; y++) SEGMENT.setPixelColorXY(x, y, ledsbuff[y]);
}
return FRAMETIME;
@@ -7536,12 +7611,12 @@ uint16_t mode_2Doctopus() {
SEGENV.aux1 = rows;
*offsX = SEGMENT.custom1;
*offsY = SEGMENT.custom2;
- const uint8_t C_X = cols / 2 + (SEGMENT.custom1 - 128)*cols/255;
- const uint8_t C_Y = rows / 2 + (SEGMENT.custom2 - 128)*rows/255;
+ const int C_X = (cols / 2) + ((SEGMENT.custom1 - 128)*cols)/255;
+ const int C_Y = (rows / 2) + ((SEGMENT.custom2 - 128)*rows)/255;
for (int x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) {
- rMap[XY(x, y)].angle = 40.7436f * atan2f(y - C_Y, x - C_X); // avoid 128*atan2()/PI
- rMap[XY(x, y)].radius = hypotf(x - C_X, y - C_Y) * mapp; //thanks Sutaburosu
+ rMap[XY(x, y)].angle = 40.7436f * atan2f((y - C_Y), (x - C_X)); // avoid 128*atan2()/PI
+ rMap[XY(x, y)].radius = hypotf((x - C_X), (y - C_Y)) * mapp; //thanks Sutaburosu
}
}
}
@@ -7592,7 +7667,7 @@ static const char _data_FX_MODE_2DWAVINGCELL[] PROGMEM = "Waving Cell@!,,Amplitu
static const char _data_RESERVED[] PROGMEM = "RSVD";
// add (or replace reserved) effect mode and data into vector
-// use id==255 to find unallocatd gaps (with "Reserved" data string)
+// use id==255 to find unallocated gaps (with "Reserved" data string)
// if vector size() is smaller than id (single) data is appended at the end (regardless of id)
void WS2812FX::addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name) {
if (id == 255) { // find empty slot
@@ -7667,6 +7742,7 @@ void WS2812FX::setupEffectData() {
addEffect(FX_MODE_FIRE_FLICKER, &mode_fire_flicker, _data_FX_MODE_FIRE_FLICKER);
addEffect(FX_MODE_GRADIENT, &mode_gradient, _data_FX_MODE_GRADIENT);
addEffect(FX_MODE_LOADING, &mode_loading, _data_FX_MODE_LOADING);
+ addEffect(FX_MODE_ROLLINGBALLS, &rolling_balls, _data_FX_MODE_ROLLINGBALLS);
addEffect(FX_MODE_FAIRY, &mode_fairy, _data_FX_MODE_FAIRY);
addEffect(FX_MODE_TWO_DOTS, &mode_two_dots, _data_FX_MODE_TWO_DOTS);
diff --git a/wled00/FX.h b/wled00/FX.h
index 19b1fc4ac..0d679ba64 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -109,20 +109,15 @@
#define PINK (uint32_t)0xFF1493
#define ULTRAWHITE (uint32_t)0xFFFFFFFF
#define DARKSLATEGRAY (uint32_t)0x2F4F4F
-#define DARKSLATEGREY (uint32_t)0x2F4F4F
+#define DARKSLATEGREY DARKSLATEGRAY
-// options
-// bit 7: segment is in transition mode
-// bits 4-6: TBD
-// bit 3: mirror effect within segment
-// bit 2: segment is on
-// bit 1: reverse segment
-// bit 0: segment is selected
+// segment options
#define NO_OPTIONS (uint16_t)0x0000
-#define TRANSPOSED (uint16_t)0x0400 // rotated 90deg & reversed
-#define REVERSE_Y_2D (uint16_t)0x0200
-#define MIRROR_Y_2D (uint16_t)0x0100
-#define TRANSITIONAL (uint16_t)0x0080
+#define TRANSPOSED (uint16_t)0x0100 // rotated 90deg & reversed
+#define MIRROR_Y_2D (uint16_t)0x0080
+#define REVERSE_Y_2D (uint16_t)0x0040
+#define RESET_REQ (uint16_t)0x0020
+#define FROZEN (uint16_t)0x0010
#define MIRROR (uint16_t)0x0008
#define SEGMENT_ON (uint16_t)0x0004
#define REVERSE (uint16_t)0x0002
@@ -176,7 +171,7 @@
#define FX_MODE_FIRE_FLICKER 45
#define FX_MODE_GRADIENT 46
#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_TWO_DOTS 50
#define FX_MODE_FAIRYTWINKLE 51 //was Two Areas prior to 0.13.0-b6 (use "Two Dots" with full intensity)
@@ -329,7 +324,7 @@ typedef enum mapping1D2D {
M12_pCorner = 3
} mapping1D2D_t;
-// segment, 72 bytes
+// segment, 80 bytes
typedef struct Segment {
public:
uint16_t start; // start index / start X coordinate 2D (left)
@@ -348,12 +343,11 @@ typedef struct Segment {
bool mirror : 1; // 3 : mirrored
bool freeze : 1; // 4 : paused/frozen
bool reset : 1; // 5 : indicates that Segment runtime requires reset
- bool transitional: 1; // 6 : transitional (there is transition occuring)
- bool reverse_y : 1; // 7 : reversed Y (2D)
- bool mirror_y : 1; // 8 : mirrored Y (2D)
- bool transpose : 1; // 9 : transposed (2D, swapped X & Y)
- uint8_t map1D2D : 3; // 10-12 : mapping for 1D effect on 2D (0-use as strip, 1-expand vertically, 2-circular/arc, 3-rectangular/corner, ...)
- uint8_t soundSim : 1; // 13 : 0-1 sound simulation types ("soft" & "hard" or "on"/"off")
+ bool reverse_y : 1; // 6 : reversed Y (2D)
+ bool mirror_y : 1; // 7 : mirrored Y (2D)
+ bool transpose : 1; // 8 : transposed (2D, swapped X & Y)
+ uint8_t map1D2D : 3; // 9-11 : mapping for 1D effect on 2D (0-use as strip, 1-expand vertically, 2-circular/arc, 3-rectangular/corner, ...)
+ uint8_t soundSim : 2; // 12-13 : 0-3 sound simulation types ("soft" & "hard" or "on"/"off")
uint8_t set : 2; // 14-15 : 0-3 UI segment sets/groups
};
};
@@ -370,7 +364,7 @@ typedef struct Segment {
};
uint8_t startY; // start Y coodrinate 2D (top); there should be no more than 255 rows
uint8_t stopY; // stop Y coordinate 2D (bottom); there should be no more than 255 rows
- char *name;
+ char *name;
// runtime data
unsigned long next_time; // millis() of next update
@@ -378,11 +372,33 @@ typedef struct Segment {
uint32_t call; // call counter
uint16_t aux0; // custom var
uint16_t aux1; // custom var
- byte* data; // effect data pointer
- CRGB* leds; // local leds[] array (may be a pointer to global)
- static CRGB *_globalLeds; // global leds[] array
+ byte *data; // effect data pointer
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:
union {
uint8_t _capabilities;
@@ -394,42 +410,37 @@ typedef struct Segment {
uint8_t _reserved : 4;
};
};
- uint16_t _dataLen;
+ uint16_t _dataLen;
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 {
+ #ifndef WLED_DISABLE_MODE_BLEND
+ tmpsegd_t _segT; // previous segment environment
+ uint8_t _modeT; // previous mode/effect
+ #else
uint32_t _colorT[NUM_COLORS];
+ #endif
uint8_t _briT; // temporary brightness
uint8_t _cctT; // temporary CCT
CRGBPalette16 _palT; // temporary palette
- uint8_t _prevPaletteBlends; // number of previous palette blends (there are max 255 belnds possible)
- uint8_t _modeP; // previous mode/effect
- //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;
+ uint8_t _prevPaletteBlends; // number of previous palette blends (there are max 255 blends possible)
+ unsigned long _start; // must accommodate millis()
uint16_t _dur;
Transition(uint16_t dur=750)
- : _briT(255)
- , _cctT(127)
- , _palT(CRGBPalette16(CRGB::Black))
+ : _palT(CRGBPalette16(CRGB::Black))
, _prevPaletteBlends(0)
- , _modeP(FX_MODE_STATIC)
, _start(millis())
, _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> n) & 0x01); }
inline bool isSelected(void) const { return selected; }
+ inline bool isInTransition(void) const { return _t != nullptr; }
inline bool isActive(void) const { return stop > start; }
inline bool is2D(void) const { return (width()>1 && height()>1); }
inline bool hasRGB(void) const { return _isRGB; }
inline bool hasWhite(void) const { return _hasW; }
inline bool isCCT(void) const { return _isCCT; }
- inline uint16_t width(void) const { return stop - start; } // segment width in physical pixels (length if 1D)
- inline uint16_t height(void) const { return stopY - startY; } // segment height (if 2D) in physical pixels
- inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels
+ 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; } // 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 groupLength(void) const { return grouping + spacing; }
inline uint8_t getLightCapabilities(void) const { return _capabilities; }
static uint16_t getUsedSegmentData(void) { return _usedSegmentData; }
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
void setCCT(uint16_t k);
void setOpacity(uint8_t o);
@@ -538,21 +553,26 @@ typedef struct Segment {
* Safe to call from interrupts and network requests.
*/
inline void markForReset(void) { reset = true; } // setOption(SEG_OPTION_RESET, true)
- void setUpLeds(void); // set up leds[] array for loseless getPixelColor()
// transition functions
void startTransition(uint16_t dur); // transition has to start before actual segment values change
+ void stopTransition(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
- uint8_t currentBri(uint8_t briNew, bool useCct = false);
- uint8_t currentMode(uint8_t modeNew);
- uint32_t currentColor(uint8_t slot, uint32_t colorNew);
+ uint8_t currentBri(bool useCct = false);
+ uint8_t currentMode(void);
+ uint32_t currentColor(uint8_t slot);
CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal);
CRGBPalette16 ¤tPalette(CRGBPalette16 &tgt, uint8_t paletteID);
// 1D strip
uint16_t virtualLength(void) const;
void setPixelColor(int n, uint32_t c); // set relative pixel within segment with color
+ void setPixelColor(unsigned n, uint32_t c) { setPixelColor(int(n), c); }
void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } // automatically inline
void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } // automatically inline
void setPixelColor(float i, uint32_t c, bool aa = true);
@@ -570,7 +590,6 @@ typedef struct Segment {
void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(n, RGBW32(r,g,b,w), fast); } // automatically inline
void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); } // automatically inline
void fadePixelColor(uint16_t n, uint8_t fade);
- uint8_t get_random_wheel_index(uint8_t pos);
uint32_t color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255);
uint32_t color_wheel(uint8_t pos);
@@ -579,8 +598,9 @@ typedef struct Segment {
uint16_t virtualHeight(void) const;
uint16_t nrOfVStrips(void) const;
#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(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); }
void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline
void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } // automatically inline
void setPixelColorXY(float x, float y, uint32_t c, bool aa = true);
@@ -604,9 +624,9 @@ typedef struct Segment {
void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c);
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c);
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) { 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, 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 blur1d(fract8 blur_amount); // blur all rows in 1 dimension
void blur2d(fract8 blur_amount) { blur(blur_amount); }
@@ -636,8 +656,9 @@ typedef struct Segment {
void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c) {}
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) {}
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) {}
- void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color) {}
+ void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, 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 c, CRGB c2, int8_t rotate = 0) {}
void wu_pixel(uint32_t x, uint32_t y, CRGB c) {}
#endif
} segment;
@@ -692,7 +713,15 @@ class WS2812FX { // 96 bytes
customMappingSize(0),
_lastShow(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;
_mode.reserve(_modeCount); // allocate memory to prevent initial fragmentation (does not increase size())
@@ -710,7 +739,6 @@ class WS2812FX { // 96 bytes
panel.clear();
#endif
customPalettes.clear();
- if (useLedsArray && Segment::_globalLeds) free(Segment::_globalLeds);
}
static WS2812FX* getInstance(void) { return instance; }
@@ -749,7 +777,7 @@ class WS2812FX { // 96 bytes
inline void trigger(void) { _triggered = true; } // Forces the next frame to be computed on all active segments.
inline void setShowCallback(show_callback cb) { _callback = cb; }
inline void setTransition(uint16_t t) { _transitionDur = t; }
- 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
checkSegmentAlignment(void),
@@ -757,8 +785,7 @@ class WS2812FX { // 96 bytes
hasCCTBus(void),
// return true if the strip is being sent pixel updates
isUpdating(void),
- deserializeMap(uint8_t n=0),
- useLedsArray = false;
+ deserializeMap(uint8_t n=0);
inline bool isServicing(void) { return _isServicing; }
inline bool hasWhiteChannel(void) {return _hasWhiteChannel;}
@@ -849,16 +876,14 @@ class WS2812FX { // 96 bytes
std::vector panel;
#endif
- void
- setUpMatrix(),
- setPixelColorXY(int x, int y, uint32_t c);
+ void setUpMatrix();
// outsmart the compiler :) by correctly overloading
- inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline
- inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); }
+ inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(y * Segment::maxWidth + x, c); }
+ inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); }
+ inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); }
- uint32_t
- getPixelColorXY(uint16_t, uint16_t);
+ inline uint32_t getPixelColorXY(uint16_t x, uint16_t y) { return getPixelColor(isMatrix ? y * Segment::maxWidth + x : x);}
// end 2D support
@@ -900,13 +925,20 @@ class WS2812FX { // 96 bytes
uint16_t* customMappingTable;
uint16_t customMappingSize;
- uint32_t _lastShow;
+ unsigned long _lastShow;
uint8_t _segment_index;
uint8_t _mainSegment;
+ uint8_t _queuedChangesSegId;
+ uint16_t _qStart, _qStop, _qStartY, _qStopY;
+ uint8_t _qGrouping, _qSpacing;
+ uint16_t _qOffset;
+
+ uint8_t
+ estimateCurrentAndLimitBri(void);
void
- estimateCurrentAndLimitBri(void);
+ setUpSegmentFromQueuedChanges(void);
};
extern const char JSON_mode_names[];
diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp
index f4dac68d7..5dc9e9ff2 100644
--- a/wled00/FX_2Dfcn.cpp
+++ b/wled00/FX_2Dfcn.cpp
@@ -134,7 +134,7 @@ void WS2812FX::setUpMatrix() {
#ifdef WLED_DEBUG
DEBUG_PRINT(F("Matrix ledmap:"));
- for (uint16_t i=0; i= _length) return;
- busses.setPixelColor(index, col);
-}
-
-// returns RGBW values of pixel
-uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) {
-#ifndef WLED_DISABLE_2D
- uint16_t index = (y * Segment::maxWidth + x);
-#else
- uint16_t index = x;
-#endif
- if (index < customMappingSize) index = customMappingTable[index];
- if (index >= _length) return 0;
- return busses.getPixelColor(index);
-}
///////////////////////////////////////////////////////////
// Segment:: routines
@@ -188,20 +163,19 @@ uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) {
#ifndef WLED_DISABLE_2D
// 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 width = virtualWidth(); // segment width in logical pixels
- uint16_t height = virtualHeight(); // segment height in logical pixels
- return (x%width) + (y%height) * width;
+uint16_t IRAM_ATTR Segment::XY(uint16_t x, uint16_t y)
+{
+ 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 (is always >= 1)
+ return isActive() ? (x%width) + (y%height) * width : 0;
}
-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 (leds) leds[XY(x,y)] = col;
-
- uint8_t _bri_t = currentBri(on ? opacity : 0);
+ uint8_t _bri_t = currentBri();
if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t);
byte g = scale8(G(col), _bri_t);
@@ -218,23 +192,29 @@ void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col)
y *= groupLength(); // expand to physical pixels
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 g = 0; g < grouping; g++) { // groupping horizontally
uint16_t xX = (x+g), yY = (y+j);
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 (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col);
- else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col);
+ if (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, tmpCol);
+ else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, tmpCol);
}
if (mirror_y) { //set the corresponding vertically mirrored pixel
- if (transpose) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col);
- else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col);
+ if (transpose) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, tmpCol);
+ else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, tmpCol);
}
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);
}
}
}
@@ -243,7 +223,7 @@ void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col)
// anti-aliased version of setPixelColorXY()
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
const uint16_t cols = virtualWidth();
@@ -286,8 +266,8 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa)
// returns RGBW values of pixel
uint32_t Segment::getPixelColorXY(uint16_t x, uint16_t y) {
- int i = XY(x,y);
- if (leds) return RGBW32(leds[i].r, leds[i].g, leds[i].b, 0);
+ if (!isActive()) return 0; // not active
+ 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_y) y = virtualHeight() - y - 1;
if (transpose) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed
@@ -304,80 +284,75 @@ void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t
// Adds the specified color with the existing pixel color perserving color balance.
void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) {
- uint32_t col = getPixelColorXY(x,y);
- uint8_t r = R(col);
- uint8_t g = G(col);
- uint8_t b = B(col);
- uint8_t w = W(col);
- if (fast) {
- r = qadd8(r, R(color));
- g = qadd8(g, G(color));
- b = qadd8(b, B(color));
- w = qadd8(w, W(color));
- col = RGBW32(r,g,b,w);
- } else {
- col = color_add(col, color);
- }
- setPixelColorXY(x, y, col);
+ 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
+ setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color, fast));
}
void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) {
- CRGB pix = CRGB(getPixelColorXY(x,y)).nscale8_video(fade);
- setPixelColorXY(x, y, pix);
+ if (!isActive()) return; // not active
+ setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), fade, true));
}
// blurRow: perform a blur on a row of a rectangular matrix
void Segment::blurRow(uint16_t row, fract8 blur_amount) {
- const uint16_t cols = virtualWidth();
- const uint16_t rows = virtualHeight();
+ if (!isActive() || blur_amount == 0) return; // not active
+ const uint_fast16_t cols = virtualWidth();
+ const uint_fast16_t rows = virtualHeight();
if (row >= rows) return;
// blur one row
uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black;
- for (uint16_t x = 0; x < cols; x++) {
+ for (unsigned x = 0; x < cols; x++) {
CRGB cur = getPixelColorXY(x, row);
+ CRGB before = cur; // remember color before blur
CRGB part = cur;
part.nscale8(seep);
cur.nscale8(keep);
cur += carryover;
- if (x) {
+ if (x>0) {
CRGB prev = CRGB(getPixelColorXY(x-1, row)) + part;
setPixelColorXY(x-1, row, prev);
}
- setPixelColorXY(x, row, cur);
+ if (before != cur) // optimization: only set pixel if color has changed
+ setPixelColorXY(x, row, cur);
carryover = part;
}
}
// blurCol: perform a blur on a column of a rectangular matrix
void Segment::blurCol(uint16_t col, fract8 blur_amount) {
- const uint16_t cols = virtualWidth();
- const uint16_t rows = virtualHeight();
+ if (!isActive() || blur_amount == 0) return; // not active
+ const uint_fast16_t cols = virtualWidth();
+ const uint_fast16_t rows = virtualHeight();
if (col >= cols) return;
// blur one column
uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black;
- for (uint16_t i = 0; i < rows; i++) {
- CRGB cur = getPixelColorXY(col, i);
+ for (unsigned y = 0; y < rows; y++) {
+ CRGB cur = getPixelColorXY(col, y);
CRGB part = cur;
+ CRGB before = cur; // remember color before blur
part.nscale8(seep);
cur.nscale8(keep);
cur += carryover;
- if (i) {
- CRGB prev = CRGB(getPixelColorXY(col, i-1)) + part;
- setPixelColorXY(col, i-1, prev);
+ if (y>0) {
+ CRGB prev = CRGB(getPixelColorXY(col, y-1)) + part;
+ setPixelColorXY(col, y-1, prev);
}
- setPixelColorXY(col, i, cur);
+ if (before != cur) // optimization: only set pixel if color has changed
+ setPixelColorXY(col, y, cur);
carryover = part;
}
}
// 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) {
+ if (!isActive() || blur_amount == 0) return; // not active
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
const uint16_t dim1 = vertical ? rows : cols;
@@ -387,11 +362,11 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
const float keep = 3.f - 2.f*seep;
// 1D box blur
CRGB tmp[dim1];
- for (uint16_t j = 0; j < dim1; j++) {
+ for (int j = 0; j < dim1; j++) {
uint16_t x = vertical ? i : j;
uint16_t y = vertical ? j : i;
- uint16_t xp = vertical ? x : x-1;
- uint16_t yp = vertical ? y-1 : y;
+ int16_t xp = vertical ? x : x-1; // "signed" to prevent underflow
+ int16_t yp = vertical ? y-1 : y; // "signed" to prevent underflow
uint16_t xn = vertical ? x : x+1;
uint16_t yn = vertical ? y+1 : y;
CRGB curr = getPixelColorXY(x,y);
@@ -403,7 +378,7 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
b = (curr.b*keep + (prev.b + next.b)*seep) / 3;
tmp[j] = CRGB(r,g,b);
}
- for (uint16_t j = 0; j < dim1; j++) {
+ for (int j = 0; j < dim1; j++) {
uint16_t x = vertical ? i : j;
uint16_t y = vertical ? j : i;
setPixelColorXY(x, y, tmp[j]);
@@ -426,10 +401,11 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
void Segment::blur1d(fract8 blur_amount) {
const uint16_t rows = virtualHeight();
- for (uint16_t y = 0; y < rows; y++) blurRow(y, blur_amount);
+ for (unsigned y = 0; y < rows; y++) blurRow(y, blur_amount);
}
void Segment::moveX(int8_t delta, bool wrap) {
+ if (!isActive()) return; // not active
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
if (!delta || abs(delta) >= cols) return;
@@ -447,6 +423,7 @@ void Segment::moveX(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 rows = virtualHeight();
if (!delta || abs(delta) >= rows) return;
@@ -482,6 +459,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) {
+ if (!isActive() || radius == 0) return; // not active
// Bresenham’s Algorithm
int d = 3 - (2*radius);
int y = radius, x = 0;
@@ -506,6 +484,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
void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
+ if (!isActive() || radius == 0) return; // not active
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
for (int16_t y = -radius; y <= radius; y++) {
@@ -519,15 +498,17 @@ void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
}
void Segment::nscale8(uint8_t scale) {
+ if (!isActive()) return; // not active
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
- for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
+ for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
setPixelColorXY(x, y, CRGB(getPixelColorXY(x, y)).nscale8(scale));
}
}
//line function
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 rows = virtualHeight();
if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return;
@@ -551,7 +532,8 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3
// draws a raster font character on canvas
// only supports: 4x6=24, 5x8=40, 5x12=60, 6x8=48 and 7x9=63 fonts ATM
-void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2) {
+void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2, int8_t rotate) {
+ if (!isActive()) return; // not active
if (chr < 32 || chr > 126) return; // only ASCII 32-126 supported
chr -= 32; // align with font table entries
const uint16_t cols = virtualWidth();
@@ -563,9 +545,6 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
//if (w<5 || w>6 || h!=8) return;
for (int i = 0; i= rows) break; // drawing off-screen
uint8_t bits = 0;
switch (font) {
case 24: bits = pgm_read_byte_near(&console_font_4x6[(chr * h) + i]); break; // 5x8 font
@@ -577,8 +556,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);
for (int j = 0; j= 0 || x0 < cols) && ((bits>>(j+(8-w))) & 0x01)) { // bit set & drawing on-screen
+ int x0, y0;
+ 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);
}
}
@@ -587,6 +574,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))
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
uint8_t xx = x & 0xff, yy = y & 0xff, ix = 255 - xx, iy = 255 - yy;
// calculate the intensities for each affected pixel
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 50d3f2f40..f9cf3e1e7 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -74,103 +74,113 @@
// Segment class implementation
///////////////////////////////////////////////////////////////////////////////
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::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
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));
+ _t = nullptr; // copied segment cannot be in transition
name = nullptr;
data = nullptr;
_dataLen = 0;
- _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.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.leds && !Segment::_globalLeds) { leds = (CRGB*)malloc(sizeof(CRGB)*length()); if (leds) memcpy(leds, orig.leds, sizeof(CRGB)*length()); }
}
// move constructor
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));
+ orig._t = nullptr; // old segment cannot be in transition any more
orig.name = nullptr;
orig.data = nullptr;
orig._dataLen = 0;
- orig._t = nullptr;
- orig.leds = nullptr;
}
// copy assignment
Segment& Segment::operator= (const Segment &orig) {
- //DEBUG_PRINTLN(F("-- Copying segment --"));
+ //DEBUG_PRINTF("-- Copying segment: %p -> %p\n", &orig, this);
if (this != &orig) {
// clean destination
- if (name) delete[] name;
- if (_t) delete _t;
- if (leds && !Segment::_globalLeds) free(leds);
+ if (name) { delete[] name; name = nullptr; }
+ stopTransition();
deallocateData();
// copy source
memcpy((void*)this, (void*)&orig, sizeof(Segment));
// erase pointers to allocated data
- name = nullptr;
data = nullptr;
_dataLen = 0;
- _t = nullptr;
- if (!Segment::_globalLeds) leds = nullptr;
// copy source data
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._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
- if (orig.leds && !Segment::_globalLeds) { leds = (CRGB*)malloc(sizeof(CRGB)*length()); if (leds) memcpy(leds, orig.leds, sizeof(CRGB)*length()); }
}
return *this;
}
// move assignment
Segment& Segment::operator= (Segment &&orig) noexcept {
- //DEBUG_PRINTLN(F("-- Moving segment --"));
+ //DEBUG_PRINTF("-- Moving segment: %p -> %p\n", &orig, this);
if (this != &orig) {
- if (name) delete[] name; // free old name
+ if (name) { delete[] name; name = nullptr; } // free old name
+ stopTransition();
deallocateData(); // free old runtime data
- if (_t) delete _t;
- if (leds && !Segment::_globalLeds) free(leds);
memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.name = nullptr;
orig.data = nullptr;
orig._dataLen = 0;
- orig._t = nullptr;
- orig.leds = nullptr;
+ orig._t = nullptr; // old segment cannot be in transition
}
return *this;
}
bool Segment::allocateData(size_t len) {
- if (data && _dataLen == len) return true; //already allocated
+ if (data && _dataLen >= len) { // already allocated enough (reduce fragmentation)
+ if (call == 0) memset(data, 0, len); // erase buffer if called during effect initialisation
+ return true;
+ }
+ //DEBUG_PRINTF("-- Allocating data (%d): %p\n", len, this);
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
- //#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);
- if (!data) return false; //allocation failed
+ data = (byte*) malloc(len);
+ if (!data) { DEBUG_PRINTLN(F("!!! Allocation failed. !!!")); return false; } //allocation failed
Segment::addUsedSegmentData(len);
+ //DEBUG_PRINTF("--- Allocated data (%p): %d/%d -> %p\n", this, len, Segment::getUsedSegmentData(), data);
_dataLen = len;
memset(data, 0, len);
return true;
}
void Segment::deallocateData() {
- if (!data) return;
- free(data);
+ 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);
+ } 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;
- Segment::addUsedSegmentData(-_dataLen);
+ Segment::addUsedSegmentData(_dataLen <= Segment::getUsedSegmentData() ? -_dataLen : -Segment::getUsedSegmentData());
_dataLen = 0;
}
@@ -182,38 +192,14 @@ void Segment::deallocateData() {
* may free that data buffer.
*/
void Segment::resetIfRequired() {
- if (reset) {
- if (leds && !Segment::_globalLeds) { free(leds); leds = nullptr; }
- //if (transitional && _t) { transitional = false; delete _t; _t = nullptr; }
- deallocateData();
- next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
- reset = false; // setOption(SEG_OPTION_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());
- }
+ if (!reset) return;
+ //DEBUG_PRINTF("-- Segment reset: %p\n", this);
+ deallocateData();
+ next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
+ reset = false;
}
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 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0;
//default palette. Differs depending on effect
@@ -233,28 +219,19 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
switch (pal) {
case 0: //default palette. Exceptions for specific effects above
targetPalette = PartyColors_p; break;
- case 1: {//periodically replace palette with a random one. Transition palette change in 500ms
- uint32_t timeSinceLastChange = millis() - _lastPaletteChange;
+ case 1: {//periodically replace palette with a random one
+ unsigned long timeSinceLastChange = millis() - _lastPaletteChange;
if (timeSinceLastChange > randomPaletteChangeTime * 1000U) {
- prevRandomPalette = randomPalette;
- randomPalette = CRGBPalette16(
+ _randomPalette = _newRandomPalette;
+ _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)));
_lastPaletteChange = millis();
- timeSinceLastChange = 0;
- }
- 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; i245) {
targetPalette = strip.customPalettes[255-pal]; // we checked bounds above
} else {
+ byte tcp[72];
memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[pal-13])), 72);
targetPalette.loadDynamicGradientPalette(tcp);
}
@@ -305,84 +283,195 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
}
void Segment::startTransition(uint16_t dur) {
- if (!dur) {
- transitional = false;
- if (_t) {
- delete _t;
- _t = nullptr;
- }
+ if (dur == 0) {
+ if (isInTransition()) _t->_dur = dur; // this will stop transition in next handleTransition()
return;
}
- if (transitional && _t) return; // already in transition no need to store anything
+ if (isInTransition()) return; // already in transition no need to store anything
// starting a transition has to occur before change so we get current values 1st
_t = new Transition(dur); // no previous transition running
if (!_t) return; // failed to allocate data
- CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette);
- _t->_briT = on ? opacity : 0;
- _t->_cctT = cct;
- _t->_palT = _palT;
- _t->_modeP = mode;
+ //DEBUG_PRINTF("-- Started transition: %p\n", this);
+ loadPalette(_t->_palT, palette);
+ _t->_briT = on ? opacity : 0;
+ _t->_cctT = cct;
+#ifndef WLED_DISABLE_MODE_BLEND
+ if (modeBlending) {
+ swapSegenv(_t->_segT);
+ _t->_modeT = mode;
+ _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_segT._colorT[i] = colors[i];
+ }
+#else
for (size_t i=0; i_colorT[i] = colors[i];
- transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true);
+#endif
+}
+
+void Segment::stopTransition() {
+ //DEBUG_PRINTF("-- Stopping transition: %p\n", this);
+ if (isInTransition()) {
+ #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() {
+ uint16_t _progress = progress();
+ if (_progress == 0xFFFFU) stopTransition();
}
// transition progression between 0-65535
uint16_t Segment::progress() {
- if (!transitional || !_t) return 0xFFFFU;
- uint32_t timeNow = millis();
- if (timeNow - _t->_start > _t->_dur || _t->_dur == 0) return 0xFFFFU;
- return (timeNow - _t->_start) * 0xFFFFU / _t->_dur;
-}
-
-uint8_t Segment::currentBri(uint8_t briNew, bool useCct) {
- uint32_t prog = progress();
- if (transitional && _t && prog < 0xFFFFU) {
- if (useCct) return ((briNew * prog) + _t->_cctT * (0xFFFFU - prog)) >> 16;
- else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16;
- } else {
- return briNew;
+ if (isInTransition()) {
+ unsigned long timeNow = millis();
+ if (_t->_dur > 0 && timeNow - _t->_start < _t->_dur) return (timeNow - _t->_start) * 0xFFFFU / _t->_dur;
}
+ return 0xFFFFU;
}
-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
+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_segT)) {
+ // swap SEGENV with transitional data
+ options = _t->_segT._optionsT;
+ for (size_t i=0; i_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);
}
-uint32_t Segment::currentColor(uint8_t slot, uint32_t colorNew) {
- return transitional && _t ? color_blend(_t->_colorT[slot], colorNew, progress(), true) : colorNew;
+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;
+ _t->_segT._dataLenT = _dataLen;
+ }
+ options = tmpSeg._optionsT;
+ for (size_t i=0; i_cctT : _t->_briT) * (0xFFFFU - prog);
+ return curBri / 0xFFFFU;
+ }
+ return (useCct ? cct : (on ? opacity : 0));
+}
+
+uint8_t Segment::currentMode() {
+#ifndef WLED_DISABLE_MODE_BLEND
+ uint16_t prog = progress();
+ if (modeBlending && prog < 0xFFFFU) return _t->_modeT;
+#endif
+ return mode;
+}
+
+uint32_t Segment::currentColor(uint8_t slot) {
+#ifndef WLED_DISABLE_MODE_BLEND
+ return isInTransition() ? color_blend(_t->_segT._colorT[slot], colors[slot], progress(), true) : colors[slot];
+#else
+ return isInTransition() ? color_blend(_t->_colorT[slot], colors[slot], progress(), true) : colors[slot];
+#endif
}
CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
loadPalette(targetPalette, pal);
- if (transitional && _t && progress() < 0xFFFFU) {
+ uint16_t prog = progress();
+ if (strip.paletteFade && prog < 0xFFFFU) {
// blend palettes
// there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time)
// minimum blend time is 100ms maximum is 65535ms
- uint32_t timeMS = millis() - _t->_start;
- uint16_t noOfBlends = (255U * timeMS / _t->_dur) - _t->_prevPaletteBlends;
+ uint16_t noOfBlends = ((255U * prog) / 0xFFFFU) - _t->_prevPaletteBlends;
for (int i=0; i_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, targetPalette, 48);
targetPalette = _t->_palT; // copy transitioning/temporary palette
}
return targetPalette;
}
-void Segment::handleTransition() {
- if (!transitional) return;
- uint16_t _progress = progress();
- if (_progress == 0xFFFFU) transitional = false; // finish transitioning segment
- if (_t) { // thanks to @nXm AKA https://github.com/NMeirer
- if (_progress >= 32767U && _t->_modeP != mode) markForReset();
- if (_progress == 0xFFFFU) {
- delete _t;
- _t = nullptr;
- }
- }
+// relies on WS2812FX::service() to call it max every 8ms or more (MIN_SHOW_DELAY)
+void Segment::handleRandomPalette() {
+ // just do a blend; if the palettes are identical it will just compare 48 bytes (same as _randomPalette == _newRandomPalette)
+ // this will slowly blend _newRandomPalette into _randomPalette every 15ms or 8ms (depending on MIN_SHOW_DELAY)
+ nblendPaletteTowardPalette(_randomPalette, _newRandomPalette, 48);
}
-void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y) {
- //return if neither bounds nor grouping have changed
+// 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
bool boundsUnchanged = (start == i1 && stop == i2);
#ifndef WLED_DISABLE_2D
if (Segment::maxHeight>1) boundsUnchanged &= (startY == i1Y && stopY == i2Y); // 2D
@@ -391,10 +480,28 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t
&& (!grp || (grouping == grp && spacing == spc))
&& (ofs == UINT16_MAX || ofs == offset)) return;
- if (stop) fill(BLACK); //turn old segment range off
+ stateChanged = true; // send UDP/WS broadcast
+
+ 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;
+
+ DEBUG_PRINT(F("setUp segment: ")); DEBUG_PRINT(i1);
+ DEBUG_PRINT(','); DEBUG_PRINT(i2);
+ DEBUG_PRINT(F(" -> ")); DEBUG_PRINT(i1Y);
+ DEBUG_PRINT(','); DEBUG_PRINTLN(i2Y);
+ markForReset();
+ if (boundsUnchanged) return;
+
+ // apply change immediately
if (i2 <= i1) { //disable segment
stop = 0;
- markForReset();
return;
}
if (i1 < Segment::maxWidth || (i1 >= Segment::maxWidth*Segment::maxHeight && i1 < strip.getLengthTotal())) start = i1; // Segment::maxWidth equals strip.getLengthTotal() for 1D
@@ -407,13 +514,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);
}
#endif
- if (grp) {
- grouping = grp;
- spacing = spc;
+ // safety check
+ if (start >= stop || startY >= stopY) {
+ stop = 0;
+ return;
}
- if (ofs < UINT16_MAX) offset = ofs;
- markForReset();
- if (!boundsUnchanged) refreshLightCapabilities();
+ refreshLightCapabilities();
}
@@ -453,17 +559,17 @@ void Segment::setOption(uint8_t n, bool val) {
if (fadeTransition && n == SEG_OPTION_ON && val != prevOn) startTransition(strip.getTransition()); // start transition prior to change
if (val) options |= 0x01 << n;
else options &= ~(0x01 << n);
- if (!(n == SEG_OPTION_SELECTED || n == SEG_OPTION_RESET || n == SEG_OPTION_TRANSITIONAL)) stateChanged = true; // send UDP/WS broadcast
+ if (!(n == SEG_OPTION_SELECTED || n == SEG_OPTION_RESET)) stateChanged = true; // send UDP/WS broadcast
}
void Segment::setMode(uint8_t fx, bool loadDefaults) {
// if we have a valid mode & is not reserved
if (fx < strip.getModeCount() && strncmp_P("RSVD", strip.getModeData(fx), 4)) {
if (fx != mode) {
- startTransition(strip.getTransition()); // set effect transitions
- //markForReset(); // transition will handle this
+#ifndef WLED_DISABLE_MODE_BLEND
+ if (modeBlending) startTransition(strip.getTransition()); // set effect transitions
+#endif
mode = fx;
-
// load default values from effect string
if (loadDefaults) {
int16_t sOpt;
@@ -475,7 +581,7 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
sOpt = extractModeDefaults(fx, "o1"); check1 = (sOpt >= 0) ? (bool)sOpt : false;
sOpt = extractModeDefaults(fx, "o2"); check2 = (sOpt >= 0) ? (bool)sOpt : false;
sOpt = extractModeDefaults(fx, "o3"); check3 = (sOpt >= 0) ? (bool)sOpt : false;
- sOpt = extractModeDefaults(fx, "m12"); if (sOpt >= 0) map1D2D = constrain(sOpt, 0, 7);
+ sOpt = extractModeDefaults(fx, "m12"); if (sOpt >= 0) map1D2D = constrain(sOpt, 0, 7); else map1D2D = M12_Pixels; // reset mapping if not defined (2D FX may not work)
sOpt = extractModeDefaults(fx, "si"); if (sOpt >= 0) soundSim = constrain(sOpt, 0, 1);
sOpt = extractModeDefaults(fx, "rev"); if (sOpt >= 0) reverse = (bool)sOpt;
sOpt = extractModeDefaults(fx, "mi"); if (sOpt >= 0) mirror = (bool)sOpt; // NOTE: setting this option is a risky business
@@ -483,6 +589,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, "pal"); if (sOpt >= 0) setPalette(sOpt); //else setPalette(0);
}
+ markForReset();
stateChanged = true; // send UDP/WS broadcast
}
}
@@ -546,7 +653,7 @@ uint16_t Segment::virtualLength() const {
return vLen;
}
#endif
- uint16_t groupLen = groupLength();
+ uint16_t groupLen = groupLength(); // is always >= 1
uint16_t vLength = (length() + groupLen - 1) / groupLen;
if (mirror) vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED
return vLength;
@@ -554,6 +661,7 @@ uint16_t Segment::virtualLength() const {
void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
{
+ if (!isActive()) return; // not active
#ifndef WLED_DISABLE_2D
int vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows)
#endif
@@ -621,10 +729,8 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
}
#endif
- if (leds) leds[i] = col;
-
uint16_t len = length();
- uint8_t _bri_t = currentBri(on ? opacity : 0);
+ uint8_t _bri_t = currentBri();
if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t);
byte g = scale8(G(col), _bri_t);
@@ -644,6 +750,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
}
i += start; // starting pixel in a group
+ uint32_t tmpCol = col;
// set all the pixels in the group
for (int j = 0; j < grouping; j++) {
uint16_t indexSet = i + ((reverse) ? -j : j);
@@ -652,11 +759,17 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
uint16_t indexMir = stop - indexSet + start - 1;
indexMir += offset; // offset/phase
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
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);
}
}
}
@@ -664,6 +777,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
// anti-aliased normalized version of setPixelColor()
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)
i -= int(i);
@@ -695,6 +809,7 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa)
uint32_t Segment::getPixelColor(int i)
{
+ if (!isActive()) return 0; // not active
#ifndef WLED_DISABLE_2D
int vStrip = i>>16;
#endif
@@ -722,14 +837,12 @@ uint32_t Segment::getPixelColor(int i)
}
#endif
- if (leds) return RGBW32(leds[i].r, leds[i].g, leds[i].b, 0);
-
if (reverse) i = virtualLength() - i - 1;
i *= groupLength();
i += start;
/* offset/phase */
i += offset;
- if (i >= stop) i -= length();
+ if ((i >= stop) && (stop>0)) i -= length(); // avoids negative pixel index (stop = 0 is a possible value)
return strip.getPixelColor(i);
}
@@ -751,10 +864,11 @@ uint8_t Segment::differs(Segment& b) const {
if (startY != b.startY) d |= SEG_DIFFERS_BOUNDS;
if (stopY != b.stopY) d |= SEG_DIFFERS_BOUNDS;
- //bit pattern: (msb first) set:2, sound:1, mapping:3, transposed, mirrorY, reverseY, [transitional, reset,] paused, mirrored, on, reverse, [selected]
- if ((options & 0b1111111110011110U) != (b.options & 0b1111111110011110U)) d |= SEG_DIFFERS_OPT;
+ //bit pattern: (msb first)
+ // set:2, sound:2, mapping:3, transposed, mirrorY, reverseY, [reset,] paused, mirrored, on, reverse, [selected]
+ if ((options & 0b1111111111011110U) != (b.options & 0b1111111111011110U)) d |= SEG_DIFFERS_OPT;
if ((options & 0x0001U) != (b.options & 0x0001U)) d |= SEG_DIFFERS_SEL;
- for (uint8_t i = 0; i < NUM_COLORS; i++) if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL;
+ for (unsigned i = 0; i < NUM_COLORS; i++) if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL;
return d;
}
@@ -764,6 +878,11 @@ void Segment::refreshLightCapabilities() {
uint16_t segStartIdx = 0xFFFFU;
uint16_t segStopIdx = 0;
+ if (!isActive()) {
+ _capabilities = 0;
+ return;
+ }
+
if (start < Segment::maxWidth * Segment::maxHeight) {
// we are withing 2D matrix (includes 1D segments)
for (int y = startY; y < stopY; y++) for (int x = start; x < stop; x++) {
@@ -781,7 +900,7 @@ void Segment::refreshLightCapabilities() {
segStopIdx = stop;
}
- for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
+ for (unsigned b = 0; b < busses.getNumBusses(); b++) {
Bus *bus = busses.getBus(b);
if (bus == nullptr || bus->getLength()==0) break;
if (!bus->isOk()) continue;
@@ -808,9 +927,10 @@ void Segment::refreshLightCapabilities() {
* Fills segment with color
*/
void Segment::fill(uint32_t c) {
+ if (!isActive()) return; // not active
const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
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 (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
if (is2D()) setPixelColorXY(x, y, c);
else setPixelColor(x, c);
}
@@ -823,37 +943,25 @@ void Segment::blendPixelColor(int n, uint32_t color, uint8_t blend) {
// Adds the specified color with the existing pixel color perserving color balance.
void Segment::addPixelColor(int n, uint32_t color, bool fast) {
- uint32_t col = getPixelColor(n);
- uint8_t r = R(col);
- uint8_t g = G(col);
- uint8_t b = B(col);
- uint8_t w = W(col);
- if (fast) {
- r = qadd8(r, R(color));
- g = qadd8(g, G(color));
- b = qadd8(b, B(color));
- w = qadd8(w, W(color));
- col = RGBW32(r,g,b,w);
- } else {
- col = color_add(col, color);
- }
- setPixelColor(n, col);
+ if (!isActive()) return; // not active
+ setPixelColor(n, color_add(getPixelColor(n), color, fast));
}
void Segment::fadePixelColor(uint16_t n, uint8_t fade) {
- CRGB pix = CRGB(getPixelColor(n)).nscale8_video(fade);
- setPixelColor(n, pix);
+ if (!isActive()) return; // not active
+ setPixelColor(n, color_fade(getPixelColor(n), fade, true));
}
/*
* fade out function, higher rate = quicker fade
*/
void Segment::fade_out(uint8_t rate) {
+ if (!isActive()) return; // not active
const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
const uint16_t rows = virtualHeight(); // will be 1 for 1D
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
int w2 = W(color);
@@ -861,7 +969,7 @@ void Segment::fade_out(uint8_t rate) {
int g2 = G(color);
int b2 = B(color);
- for (uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
+ for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x);
int w1 = W(color);
int r1 = R(color);
@@ -886,12 +994,13 @@ void Segment::fade_out(uint8_t rate) {
// fades all pixels to black using nscale8()
void Segment::fadeToBlackBy(uint8_t fadeBy) {
+ if (!isActive() || fadeBy == 0) return; // optimization - no scaling to apply
const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
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++) {
- if (is2D()) setPixelColorXY(x, y, CRGB(getPixelColorXY(x,y)).nscale8(255-fadeBy));
- else setPixelColor(x, CRGB(getPixelColor(x)).nscale8(255-fadeBy));
+ for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
+ if (is2D()) setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), 255-fadeBy));
+ else setPixelColor(x, color_fade(getPixelColor(x), 255-fadeBy));
}
}
@@ -900,34 +1009,30 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) {
*/
void Segment::blur(uint8_t blur_amount)
{
+ if (!isActive() || blur_amount == 0) return; // optimization: 0 means "don't blur"
#ifndef WLED_DISABLE_2D
if (is2D()) {
// compatibility with 2D
- const uint16_t cols = virtualWidth();
- const uint16_t rows = virtualHeight();
- for (uint16_t i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows
- for (uint16_t k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns
+ const unsigned cols = virtualWidth();
+ const unsigned rows = virtualHeight();
+ for (unsigned i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows
+ for (unsigned k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns
return;
}
#endif
uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1;
- CRGB carryover = CRGB::Black;
- for(uint16_t i = 0; i < virtualLength(); i++)
- {
- CRGB cur = CRGB(getPixelColor(i));
- CRGB part = cur;
- part.nscale8(seep);
- cur.nscale8(keep);
- cur += carryover;
- if(i > 0) {
+ uint32_t carryover = BLACK;
+ unsigned vlength = virtualLength();
+ for (unsigned i = 0; i < vlength; i++) {
+ uint32_t cur = getPixelColor(i);
+ uint32_t part = color_fade(cur, seep);
+ cur = color_add(color_fade(cur, keep), carryover, true);
+ if (i > 0) {
uint32_t c = getPixelColor(i-1);
- uint8_t r = R(c);
- uint8_t g = G(c);
- uint8_t b = B(c);
- setPixelColor(i-1, qadd8(r, part.red), qadd8(g, part.green), qadd8(b, part.blue));
+ setPixelColor(i-1, color_add(c, part, true));
}
- setPixelColor(i,cur.red, cur.green, cur.blue);
+ setPixelColor(i, cur);
carryover = part;
}
}
@@ -940,7 +1045,7 @@ void Segment::blur(uint8_t blur_amount)
uint32_t Segment::color_wheel(uint8_t pos) {
if (palette) return color_from_palette(pos, false, true, 0);
pos = 255 - pos;
- if(pos < 85) {
+ if (pos < 85) {
return ((uint32_t)(255 - pos * 3) << 16) | ((uint32_t)(0) << 8) | (pos * 3);
} else if(pos < 170) {
pos -= 85;
@@ -951,21 +1056,6 @@ uint32_t Segment::color_wheel(uint8_t pos) {
}
}
-/*
- * Returns a new, random wheel index with a minimum distance of 42 from pos.
- */
-uint8_t Segment::get_random_wheel_index(uint8_t pos) {
- uint8_t r = 0, x = 0, y = 0, d = 0;
-
- while(d < 42) {
- r = random8();
- x = abs(pos - r);
- y = 255 - x;
- d = MIN(x, y);
- }
- return r;
-}
-
/*
* Gets a single color from the currently selected palette.
* @param i Palette Index (if mapping is true, the full palette will be _virtualSegmentLength long, if false, 255). Will wrap around automatically.
@@ -979,20 +1069,20 @@ uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_
{
// default palette or no RGB support on segment
if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) {
- uint32_t color = currentColor(mcol, colors[mcol]);
+ uint32_t color = currentColor(mcol);
color = gamma32(color);
if (pbri == 255) return color;
- return RGBW32(scale8_video(R(color),pbri), scale8_video(G(color),pbri), scale8_video(B(color),pbri), scale8_video(W(color),pbri));
+ return color_fade(color, pbri, true);
}
uint8_t paletteIndex = i;
if (mapping && virtualLength() > 1) paletteIndex = (i*255)/(virtualLength() -1);
- if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
- CRGB fastled_col;
+ if (!wrap && strip.paletteBlend != 3) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
CRGBPalette16 curPal;
- if (transitional && _t) curPal = _t->_palT;
- else loadPalette(curPal, palette);
- fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global
+ curPal = currentPalette(curPal, palette);
+ //if (isInTransition()) curPal = _t->_palT;
+ //else loadPalette(curPal, palette);
+ CRGB fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global
return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, 0);
}
@@ -1026,7 +1116,7 @@ void WS2812FX::finalizeInit(void)
const uint8_t defNumBusses = ((sizeof defDataPins) / (sizeof defDataPins[0]));
const uint8_t defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0]));
uint16_t prevLen = 0;
- for (uint8_t i = 0; i < defNumBusses && i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
+ for (int i = 0; i < defNumBusses && i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
uint8_t defPin[] = {defDataPins[i]};
uint16_t start = prevLen;
uint16_t count = defCounts[(i < defNumCounts) ? i : defNumCounts -1];
@@ -1037,7 +1127,7 @@ void WS2812FX::finalizeInit(void)
}
_length = 0;
- for (uint8_t i=0; igetStart() + bus->getLength() > MAX_LEDS) break;
@@ -1062,24 +1152,6 @@ void WS2812FX::finalizeInit(void)
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);
- memset(Segment::_globalLeds, 0, arrSize);
- }
-
//segments are created in makeAutoSegments();
DEBUG_PRINTLN(F("Loading custom palettes"));
loadCustomPalettes(); // (re)load all custom palettes
@@ -1088,13 +1160,14 @@ void WS2812FX::finalizeInit(void)
}
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;
if (nowUp - _lastShow < MIN_SHOW_DELAY) return;
bool doShow = false;
_isServicing = true;
_segment_index = 0;
+ Segment::handleRandomPalette(); // move it into for loop when each segment has individual random palette
for (segment &seg : _segments) {
// process transition (mode changes in the middle of transition)
seg.handleTransition();
@@ -1104,42 +1177,65 @@ void WS2812FX::service() {
if (!seg.isActive()) continue;
// 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;
uint16_t delay = FRAMETIME;
if (!seg.freeze) { //only run effect function if not frozen
_virtualSegmentLength = seg.virtualLength();
- _colors_t[0] = seg.currentColor(0, seg.colors[0]);
- _colors_t[1] = seg.currentColor(1, seg.colors[1]);
- _colors_t[2] = seg.currentColor(2, seg.colors[2]);
- seg.currentPalette(_currentPalette, seg.palette);
+ _colors_t[0] = seg.currentColor(0);
+ _colors_t[1] = seg.currentColor(1);
+ _colors_t[2] = seg.currentColor(2);
+ seg.currentPalette(_currentPalette, seg.palette); // we need to pass reference
- 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]);
+ if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(true), correctWB);
+ for (int c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]);
- // effect blending (execute previous effect)
- // actual code may be a bit more involved as effects have runtime data including allocated memory
- //if (seg.transitional && seg._modeP) (*_mode[seg._modeP])(progress());
- delay = (*_mode[seg.currentMode(seg.mode)])();
+ // Effect blending
+ // When two effects are being blended, each may have different segment data, this
+ // data needs to be saved first and then restored before running previous 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(); // this will return old mode while in transition
+ delay = (*_mode[seg.mode])(); // run new/current mode
+#ifndef WLED_DISABLE_MODE_BLEND
+ if (modeBlending && 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)
+ _virtualSegmentLength = seg.virtualLength(); // update SEGLEN (mapping may have changed)
+ 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.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
+ if (seg.isInTransition() && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
}
seg.next_time = nowUp + delay;
}
+ if (_segment_index == _queuedChangesSegId) setUpSegmentFromQueuedChanges();
_segment_index++;
}
_virtualSegmentLength = 0;
busses.setSegmentCCT(-1);
- if(doShow) {
+ _isServicing = false;
+ _triggered = false;
+
+ #ifdef WLED_DEBUG
+ if (millis() - nowUp > _frametime) DEBUG_PRINTLN(F("Slow effects."));
+ #endif
+ if (doShow) {
yield();
show();
}
- _triggered = false;
- _isServicing = false;
+ #ifdef WLED_DEBUG
+ if (millis() - nowUp > _frametime) DEBUG_PRINTLN(F("Slow strip."));
+ #endif
}
void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col)
@@ -1168,7 +1264,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)
//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
//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
@@ -1176,35 +1272,28 @@ void WS2812FX::estimateCurrentAndLimitBri() {
bool useWackyWS2815PowerModel = false;
byte actualMilliampsPerLed = milliampsPerLed;
- if(milliampsPerLed == 255) {
+ if (ablMilliampsMax < 150 || actualMilliampsPerLed == 0) { //0 mA per LED and too low numbers turn off calculation
+ currentMilliamps = 0;
+ return _brightness;
+ }
+
+ if (milliampsPerLed == 255) {
useWackyWS2815PowerModel = true;
actualMilliampsPerLed = 12; // from testing an actual strip
}
- if (ablMilliampsMax < 150 || actualMilliampsPerLed == 0) { //0 mA per LED and too low numbers turn off calculation
- 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 powerBudget = (ablMilliampsMax - MA_FOR_ESP); //100mA for ESP power
+ size_t pLen = 0; //getLengthPhysical();
+ size_t powerSum = 0;
for (uint_fast8_t bNum = 0; bNum < busses.getNumBusses(); bNum++) {
Bus *bus = busses.getBus(bNum);
- if (bus->getType() >= TYPE_NET_DDP_RGB) continue; //exclude non-physical network busses
+ if (!IS_DIGITAL(bus->getType())) continue; //exclude non-digital network busses
uint16_t len = bus->getLength();
+ pLen += len;
uint32_t busPowerSum = 0;
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);
if(useWackyWS2815PowerModel) { //ignore white component on WS2815 power calculation
@@ -1216,48 +1305,57 @@ void WS2812FX::estimateCurrentAndLimitBri() {
if (bus->hasWhite()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
busPowerSum *= 3;
- busPowerSum = busPowerSum >> 2; //same as /= 4
+ busPowerSum >>= 2; //same as /= 4
}
powerSum += busPowerSum;
}
- uint32_t powerSum0 = powerSum;
- powerSum *= _brightness;
+ if (powerBudget > pLen) { //each LED uses about 1mA in standby, exclude that from power budget
+ powerBudget -= pLen;
+ } else {
+ powerBudget = 0;
+ }
- if (powerSum > powerBudget) //scale brightness down to stay in current limit
- {
- float scale = (float)powerBudget / (float)powerSum;
+ // 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;
+
+ 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;
uint8_t scaleB = (scaleI > 255) ? 255 : scaleI;
- uint8_t newBri = scale8(_brightness, scaleB);
- busses.setBrightness(newBri); //to keep brightness uniform, sets virtual busses too
- currentMilliamps = (powerSum0 * newBri) / puPerMilliamp;
- } else {
- currentMilliamps = powerSum / puPerMilliamp;
- busses.setBrightness(_brightness);
+ newBri = scale8(_brightness, scaleB) + 1;
}
+ currentMilliamps = (powerSum * newBri) / 255;
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) {
-
- // avoid race condition, caputre _callback value
+ // avoid race condition, capture _callback value
show_callback 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
// all of the data has been sent.
// See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
busses.show();
- unsigned long now = millis();
- unsigned long diff = now - _lastShow;
- uint16_t fpsCurr = 200;
+
+ // 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 showNow = millis();
+ size_t diff = showNow - _lastShow;
+ size_t fpsCurr = 200;
if (diff > 0) fpsCurr = 1000 / diff;
- _cumulativeFps = (3 * _cumulativeFps + fpsCurr) >> 2;
- _lastShow = now;
+ _cumulativeFps = (3 * _cumulativeFps + fpsCurr +2) >> 2; // "+2" for proper rounding (2/4 = 0.5)
+ _lastShow = showNow;
}
/**
@@ -1270,7 +1368,7 @@ bool WS2812FX::isUpdating() {
/**
* Returns the refresh rate of the LED strip. Useful for finding out whether a given setup is fast enough.
- * Only updates on show() or is set to 0 fps if last show is more than 2 secs ago, so accurary varies
+ * Only updates on show() or is set to 0 fps if last show is more than 2 secs ago, so accuracy varies
*/
uint16_t WS2812FX::getFps() {
if (millis() - _lastShow > 2000) return 0;
@@ -1288,9 +1386,7 @@ void WS2812FX::setMode(uint8_t segid, uint8_t m) {
if (m >= getModeCount()) m = getModeCount() - 1;
if (_segments[segid].mode != m) {
- _segments[segid].startTransition(_transitionDur); // set effect transitions
- //_segments[segid].markForReset();
- _segments[segid].mode = m;
+ _segments[segid].setMode(m); // do not load defaults
}
}
@@ -1313,6 +1409,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) {
if (gammaCorrectBri) b = gamma8(b);
if (_brightness == b) return;
@@ -1322,12 +1420,12 @@ void WS2812FX::setBrightness(uint8_t b, bool direct) {
seg.freeze = false;
}
}
- if (direct) {
- // would be dangerous if applied immediately (could exceed ABL), but will not output until the next show()
- busses.setBrightness(b);
- } else {
+ // setting brightness with NeoPixelBusLg has no effect on already painted pixels,
+ // so we need to force an update to existing buffer
+ busses.setBrightness(b);
+ if (!direct) {
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
}
}
@@ -1426,7 +1524,7 @@ void WS2812FX::purgeSegments(bool force) {
}
if (deleted) {
_segments.shrink_to_fit();
- if (_mainSegment >= _segments.size()) setMainSegmentId(0);
+ /*if (_mainSegment >= _segments.size())*/ setMainSegmentId(0);
}
}
@@ -1434,9 +1532,34 @@ Segment& WS2812FX::getSegment(uint8_t id) {
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) {
- if (n >= _segments.size()) return;
- _segments[n].setUp(i1, i2, grouping, spacing, offset, startY, stopY);
+// sets new segment bounds, queues if that segment is currently running
+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) {
+ 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;
+ DEBUG_PRINT(F("Segment queued: ")); DEBUG_PRINTLN(segId);
+ return; // queued changes are applied immediately after effect function returns
+ }
+
+ _segments[segId].setUp(i1, i2, grouping, spacing, offset, startY, stopY);
+ if (segId > 0 && segId == getSegmentsNum()-1 && i2 <= i1) _segments.pop_back(); // if last segment was deleted remove it from vector
+}
+
+void WS2812FX::setUpSegmentFromQueuedChanges() {
+ if (_queuedChangesSegId >= getSegmentsNum()) return;
+ getSegment(_queuedChangesSegId).setUp(_qStart, _qStop, _qGrouping, _qSpacing, _qOffset, _qStartY, _qStopY);
+ _queuedChangesSegId = 255;
}
void WS2812FX::restartRuntime() {
@@ -1559,7 +1682,7 @@ void WS2812FX::fixInvalidSegments() {
bool WS2812FX::checkSegmentAlignment() {
bool aligned = false;
for (segment &seg : _segments) {
- for (uint8_t b = 0; bgetStart() && seg.stop == bus->getStart() + bus->getLength()) aligned = true;
}
@@ -1582,13 +1705,8 @@ uint8_t WS2812FX::setPixelSegment(uint8_t n) {
}
void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) {
- if (i2 >= i)
- {
- for (uint16_t x = i; x <= i2; x++) setPixelColor(x, col);
- } else
- {
- for (uint16_t x = i2; x <= i; x++) setPixelColor(x, col);
- }
+ if (i2 < i) std::swap(i,i2);
+ for (unsigned x = i; x <= i2; x++) setPixelColor(x, col);
}
void WS2812FX::setTransitionMode(bool t) {
@@ -1604,7 +1722,7 @@ void WS2812FX::printSize() {
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));
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
@@ -1623,7 +1741,7 @@ void WS2812FX::loadCustomPalettes() {
if (readObjectFromFile(fileName, nullptr, &pDoc)) {
JsonArray pal = pDoc[F("palette")];
- if (!pal.isNull() && pal.size()>4) { // not an empty palette (at least 2 entries)
+ if (!pal.isNull() && pal.size()>3) { // not an empty palette (at least 2 entries)
if (pal[0].is() && pal[1].is()) {
// we have an array of index & hex strings
size_t palSize = MIN(pal.size(), 36);
@@ -1632,7 +1750,7 @@ void WS2812FX::loadCustomPalettes() {
uint8_t rgbw[] = {0,0,0,0};
tcp[ j ] = (uint8_t) pal[ i ].as(); // index
colorFromHexString(rgbw, pal[i+1].as()); // will catch non-string entires
- for (size_t c=0; c<3; c++) tcp[j+1+c] = rgbw[c]; // only use RGB component
+ for (size_t c=0; c<3; c++) tcp[j+1+c] = gamma8(rgbw[c]); // only use RGB component
DEBUG_PRINTF("%d(%d) : %d %d %d\n", i, int(tcp[j]), int(tcp[j+1]), int(tcp[j+2]), int(tcp[j+3]));
}
} else {
@@ -1640,13 +1758,15 @@ void WS2812FX::loadCustomPalettes() {
palSize -= palSize % 4; // make sure size is multiple of 4
for (size_t i=0; i()<256; i+=4) {
tcp[ i ] = (uint8_t) pal[ i ].as(); // index
- tcp[i+1] = (uint8_t) pal[i+1].as(); // R
- tcp[i+2] = (uint8_t) pal[i+2].as(); // G
- tcp[i+3] = (uint8_t) pal[i+3].as(); // B
+ tcp[i+1] = gamma8((uint8_t) pal[i+1].as()); // R
+ tcp[i+2] = gamma8((uint8_t) pal[i+2].as()); // G
+ tcp[i+3] = gamma8((uint8_t) pal[i+3].as()); // B
DEBUG_PRINTF("%d(%d) : %d %d %d\n", i, int(tcp[i]), int(tcp[i+1]), int(tcp[i+2]), int(tcp[i+3]));
}
}
customPalettes.push_back(targetPalette.loadDynamicGradientPalette(tcp));
+ } else {
+ DEBUG_PRINTLN(F("Wrong palette format."));
}
}
} else {
@@ -1662,7 +1782,7 @@ bool WS2812FX::deserializeMap(uint8_t n) {
char fileName[32];
strcpy_P(fileName, PSTR("/ledmap"));
if (n) sprintf(fileName +7, "%d", n);
- strcat(fileName, ".json");
+ strcat_P(fileName, PSTR(".json"));
bool isFile = WLED_FS.exists(fileName);
if (!isFile) {
@@ -1696,7 +1816,7 @@ bool WS2812FX::deserializeMap(uint8_t n) {
if (!map.isNull() && map.size()) { // not an empty map
customMappingSize = map.size();
customMappingTable = new uint16_t[customMappingSize];
- for (uint16_t i=0; i
#define NODE_TYPE_ID_UNDEFINED 0
-#define NODE_TYPE_ID_ESP8266 82
-#define NODE_TYPE_ID_ESP32 32
-#define NODE_TYPE_ID_ESP32S2 33
+#define NODE_TYPE_ID_ESP8266 82 // should be 1
+#define NODE_TYPE_ID_ESP32 32 // should be 2
+#define NODE_TYPE_ID_ESP32S2 33 // etc
#define NODE_TYPE_ID_ESP32S3 34
#define NODE_TYPE_ID_ESP32C3 35
@@ -23,7 +23,13 @@ struct NodeStruct
String nodeName;
IPAddress ip;
uint8_t age;
- uint8_t nodeType;
+ union {
+ uint8_t nodeType; // a waste of space as we only have 5 types
+ struct {
+ uint8_t type : 7; // still a waste of space (4 bits would be enough and future-proof)
+ bool on : 1;
+ };
+ };
uint32_t build;
NodeStruct() : age(0), nodeType(0), build(0)
diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp
index 4b96060ef..4e514f4f1 100644
--- a/wled00/bus_manager.cpp
+++ b/wled00/bus_manager.cpp
@@ -64,7 +64,7 @@ void ColorOrderMap::add(uint16_t start, uint16_t len, uint8_t colorOrder) {
uint8_t IRAM_ATTR ColorOrderMap::getPixelColorOrder(uint16_t pix, uint8_t defaultColorOrder) const {
if (_count == 0) return defaultColorOrder;
- // upper nibble containd W swap information
+ // upper nibble contains W swap information
uint8_t swapW = defaultColorOrder >> 4;
for (uint8_t i = 0; i < _count; i++) {
if (pix >= _mappings[i].start && pix < (_mappings[i].start + _mappings[i].len)) {
@@ -77,7 +77,7 @@ uint8_t IRAM_ATTR ColorOrderMap::getPixelColorOrder(uint16_t pix, uint8_t defaul
uint32_t Bus::autoWhiteCalc(uint32_t c) {
uint8_t aWM = _autoWhiteMode;
- if (_gAWM < 255) aWM = _gAWM;
+ if (_gAWM != AW_GLOBAL_DISABLED) aWM = _gAWM;
if (aWM == RGBW_MODE_MANUAL_ONLY) return c;
uint8_t w = W(c);
//ignore auto-white calculation if w>0 and mode DUAL (DUAL behaves as BRIGHTER if w==0)
@@ -91,96 +91,172 @@ uint32_t Bus::autoWhiteCalc(uint32_t c) {
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 (!pinManager.allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return;
_frequencykHz = 0U;
_pins[0] = bc.pins[0];
if (IS_2PIN(bc.type)) {
if (!pinManager.allocatePin(bc.pins[1], true, PinOwner::BusDigital)) {
- cleanup(); return;
+ cleanup();
+ return;
}
_pins[1] = bc.pins[1];
_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);
if (_iType == I_NONE) return;
- uint16_t lenToCreate = _len;
- if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(_len); // only needs a third of "RGB" LEDs for NeoPixelBus
- _busPtr = PolyBus::create(_iType, _pins, lenToCreate, nr, _frequencykHz);
+ if (bc.doubleBuffer && !allocData(bc.count * (Bus::hasWhite(_type) + 3*Bus::hasRGB(_type)))) return; //warning: hardcoded channel count
+ _buffering = bc.doubleBuffer;
+ 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);
- _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, _len, 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, bc.count, bc.type, _pins[0], _pins[1], _iType);
}
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() {
+ if (!_valid) return true;
return PolyBus::canShow(_busPtr, _iType);
}
void BusDigital::setBrightness(uint8_t b) {
+ if (_bri == b) return;
//Fix for turning off onboard LED breaking bus
#ifdef LED_BUILTIN
- if (_bri == 0 && b > 0) {
- if (_pins[0] == LED_BUILTIN || _pins[1] == LED_BUILTIN) PolyBus::begin(_busPtr, _iType, _pins);
+ if (_bri == 0) { // && b > 0, covered by guard if above
+ if (_pins[0] == LED_BUILTIN || _pins[1] == LED_BUILTIN) reinit();
}
#endif
+ uint8_t prevBri = _bri;
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.
//TODO only show if no new show due in the next 50ms
void BusDigital::setStatusPixel(uint32_t c) {
- if (_skip && canShow()) {
+ if (_valid && _skip) {
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) {
- 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 (reversed) pix = _len - pix -1;
- else pix += _skip;
- uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
- if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs
- uint16_t pOld = pix;
- pix = IC_INDEX_WS2812_1CH_3X(pix);
- uint32_t cOld = PolyBus::getPixelColor(_busPtr, _iType, pix, co);
- 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 1: c = RGBW32(W(c) , G(cOld), B(cOld), 0); break;
- case 2: c = RGBW32(R(cOld), G(cOld), W(c) , 0); break;
+ 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;
+ 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);
+ if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs
+ uint16_t pOld = pix;
+ pix = IC_INDEX_WS2812_1CH_3X(pix);
+ 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)
+ 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 2: c = RGBW32(R(cOld), G(cOld), W(c) , 0); break;
+ }
+ }
+ 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) {
- if (reversed) pix = _len - pix -1;
- else pix += _skip;
- uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
- if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs
- uint16_t pOld = pix;
- pix = IC_INDEX_WS2812_1CH_3X(pix);
- uint32_t c = PolyBus::getPixelColor(_busPtr, _iType, pix, co);
- switch (pOld % 3) { // get only the single channel
- case 0: c = RGBW32(G(c), G(c), G(c), G(c)); break;
- case 1: c = RGBW32(R(c), R(c), R(c), R(c)); break;
- case 2: c = RGBW32(B(c), B(c), B(c), B(c)); break;
+ if (!_valid) return 0;
+ 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);
+ 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
+ uint8_t r = R(c);
+ uint8_t g = _reversed ? B(c) : G(c); // should G and B be switched if _reversed?
+ uint8_t b = _reversed ? G(c) : B(c);
+ switch (pix % 3) { // get only the single channel
+ case 0: c = RGBW32(g, g, g, g); break;
+ case 1: c = RGBW32(r, r, r, r); break;
+ case 2: c = RGBW32(b, b, b, b); break;
+ }
}
return c;
}
- return PolyBus::getPixelColor(_busPtr, _iType, pix, co);
}
uint8_t BusDigital::getPins(uint8_t* pinArray) {
@@ -196,6 +272,7 @@ void BusDigital::setColorOrder(uint8_t colorOrder) {
}
void BusDigital::reinit() {
+ if (!_valid) return;
PolyBus::begin(_busPtr, _iType, _pins);
}
@@ -205,13 +282,15 @@ void BusDigital::cleanup() {
_iType = I_NONE;
_valid = false;
_busPtr = nullptr;
+ if (_data != nullptr) freeData();
pinManager.deallocatePin(_pins[1], PinOwner::BusDigital);
pinManager.deallocatePin(_pins[0], PinOwner::BusDigital);
}
-BusPwm::BusPwm(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
- _valid = false;
+BusPwm::BusPwm(BusConfig &bc)
+: Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed)
+{
if (!IS_PWM(bc.type)) return;
uint8_t numPins = NUM_PWM_PINS(bc.type);
_frequency = bc.frequency ? bc.frequency : WLED_PWM_FREQ;
@@ -229,7 +308,7 @@ BusPwm::BusPwm(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
for (uint8_t i = 0; i < numPins; i++) {
uint8_t currentPin = bc.pins[i];
if (!pinManager.allocatePin(currentPin, true, PinOwner::BusPwm)) {
- deallocatePins(); return;
+ deallocatePins(); return;
}
_pins[i] = currentPin; //store only after allocatePin() succeeds
#ifdef ESP8266
@@ -239,7 +318,7 @@ BusPwm::BusPwm(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
ledcAttachPin(_pins[i], _ledcStart + i);
#endif
}
- reversed = bc.reversed;
+ _data = _pwmdata; // avoid malloc() and use stack
_valid = true;
}
@@ -307,7 +386,7 @@ void BusPwm::show() {
uint8_t numPins = NUM_PWM_PINS(_type);
for (uint8_t i = 0; i < numPins; i++) {
uint8_t scaled = (_data[i] * _bri) / 255;
- if (reversed) scaled = 255 - scaled;
+ if (_reversed) scaled = 255 - scaled;
#ifdef ESP8266
analogWrite(_pins[i], scaled);
#else
@@ -342,8 +421,10 @@ void BusPwm::deallocatePins() {
}
-BusOnOff::BusOnOff(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
- _valid = false;
+BusOnOff::BusOnOff(BusConfig &bc)
+: Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed)
+, _onoffdata(0)
+{
if (bc.type != TYPE_ONOFF) return;
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
pinMode(_pin, OUTPUT);
- reversed = bc.reversed;
+ _data = &_onoffdata; // avoid malloc() and use stack
_valid = true;
}
@@ -363,18 +444,17 @@ void BusOnOff::setPixelColor(uint16_t pix, uint32_t c) {
uint8_t g = G(c);
uint8_t b = B(c);
uint8_t w = W(c);
-
- _data = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0;
+ _data[0] = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0;
}
uint32_t BusOnOff::getPixelColor(uint16_t pix) {
if (!_valid) return 0;
- return RGBW32(_data, _data, _data, _data);
+ return RGBW32(_data[0], _data[0], _data[0], _data[0]);
}
void BusOnOff::show() {
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) {
@@ -384,8 +464,10 @@ uint8_t BusOnOff::getPins(uint8_t* pinArray) {
}
-BusNetwork::BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
- _valid = false;
+BusNetwork::BusNetwork(BusConfig &bc)
+: Bus(bc.type, bc.start, bc.autoWhite, bc.count)
+, _broadcastLock(false)
+{
switch (bc.type) {
case TYPE_NET_ARTNET_RGB:
_rgbw = false;
@@ -401,18 +483,13 @@ BusNetwork::BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
break;
}
_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]);
- _broadcastLock = false;
- _valid = true;
+ _valid = (allocData(_len * _UDPchannels) != nullptr);
}
void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) {
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
uint16_t offset = pix * _UDPchannels;
_data[offset] = R(c);
@@ -424,7 +501,7 @@ void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) {
uint32_t BusNetwork::getPixelColor(uint16_t pix) {
if (!_valid || pix >= _len) return 0;
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() {
@@ -444,8 +521,7 @@ uint8_t BusNetwork::getPins(uint8_t* pinArray) {
void BusNetwork::cleanup() {
_type = I_NONE;
_valid = false;
- if (_data != nullptr) free(_data);
- _data = nullptr;
+ freeData();
}
@@ -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++) {
Bus* b = busses[i];
uint16_t bstart = b->getStart();
diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h
index b6d79d077..bff98629d 100644
--- a/wled00/bus_manager.h
+++ b/wled00/bus_manager.h
@@ -18,6 +18,10 @@
#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
+// flag for using double buffering in BusDigital
+extern bool useGlobalLedBuffer;
+
+
//temporary struct for passing bus configuration to bus
struct BusConfig {
uint8_t type;
@@ -30,15 +34,25 @@ struct BusConfig {
uint8_t autoWhite;
uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255};
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);
type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh)
- count = len; start = pstart; colorOrder = pcolorOrder; reversed = rev; skipAmount = skip; autoWhite = aw; frequency = clock_kHz;
- uint8_t nPins = 1;
+ size_t nPins = 1;
if (type >= TYPE_NET_DDP_RGB && type < 96) nPins = 4; //virtual network bus. 4 "pins" store IP address
else if (type > 47) nPins = 2;
else if (type > 40 && type < 46) nPins = NUM_PWM_PINS(type);
- 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
@@ -54,6 +68,7 @@ struct BusConfig {
}
};
+
// Defines an LED Strip and its color ordering.
struct ColorOrderMapEntry {
uint16_t start;
@@ -64,9 +79,7 @@ struct ColorOrderMapEntry {
struct ColorOrderMap {
void add(uint16_t start, uint16_t len, uint8_t colorOrder);
- uint8_t count() const {
- return _count;
- }
+ uint8_t count() const { return _count; }
void reset() {
_count = 0;
@@ -87,56 +100,63 @@ struct ColorOrderMap {
ColorOrderMapEntry _mappings[WLED_MAX_COLOR_ORDER_MAPPINGS];
};
+
//parent class of BusDigital, BusPwm, and BusNetwork
class Bus {
public:
- Bus(uint8_t type, uint16_t start, uint8_t aw)
- : _bri(255)
- , _len(1)
+ Bus(uint8_t type, uint16_t start, uint8_t aw, uint16_t len = 1, bool reversed = false, bool refresh = false)
+ : _type(type)
+ , _bri(255)
+ , _start(start)
+ , _len(len)
+ , _reversed(reversed)
, _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;
};
virtual ~Bus() {} //throw the bus under the bus
virtual void show() = 0;
- virtual bool canShow() { return true; }
- virtual void setStatusPixel(uint32_t c) {}
+ virtual bool canShow() { return true; }
+ virtual void setStatusPixel(uint32_t c) {}
virtual void setPixelColor(uint16_t pix, uint32_t c) = 0;
virtual uint32_t getPixelColor(uint16_t pix) { return 0; }
- virtual void setBrightness(uint8_t b) { _bri = b; };
+ virtual void setBrightness(uint8_t b) { _bri = b; };
virtual void cleanup() = 0;
- virtual uint8_t getPins(uint8_t* pinArray) { return 0; }
- virtual uint16_t getLength() { return _len; }
- virtual void setColorOrder() {}
- virtual uint8_t getColorOrder() { return COL_ORDER_RGB; }
- virtual uint8_t skippedLeds() { return 0; }
- virtual uint16_t getFrequency() { return 0U; }
- inline uint16_t getStart() { return _start; }
- inline void setStart(uint16_t start) { _start = start; }
- inline uint8_t getType() { return _type; }
- inline bool isOk() { return _valid; }
- inline bool isOffRefreshRequired() { return _needsRefresh; }
+ virtual uint8_t getPins(uint8_t* pinArray) { return 0; }
+ virtual uint16_t getLength() { return _len; }
+ virtual void setColorOrder() {}
+ virtual uint8_t getColorOrder() { return COL_ORDER_RGB; }
+ virtual uint8_t skippedLeds() { return 0; }
+ virtual uint16_t getFrequency() { return 0U; }
+ inline void setReversed(bool reversed) { _reversed = reversed; }
+ inline uint16_t getStart() { return _start; }
+ inline void setStart(uint16_t start) { _start = start; }
+ inline uint8_t getType() { return _type; }
+ inline bool isOk() { return _valid; }
+ inline bool isReversed() { return _reversed; }
+ inline bool isOffRefreshRequired() { return _needsRefresh; }
bool containsPixel(uint16_t pix) { return pix >= _start && pix < _start+_len; }
- virtual bool hasRGB() {
- if ((_type >= TYPE_WS2812_1CH && _type <= TYPE_WS2812_WWA) || _type == TYPE_ANALOG_1CH || _type == TYPE_ANALOG_2CH || _type == TYPE_ONOFF) return false;
+ virtual bool hasRGB(void) { return Bus::hasRGB(_type); }
+ 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;
}
- virtual bool hasWhite() { return Bus::hasWhite(_type); }
+ virtual bool hasWhite(void) { return Bus::hasWhite(_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 || type == TYPE_UCS8904) 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_NET_DDP_RGBW) return true; // network types with white channel
return false;
}
- virtual bool hasCCT() {
- if (_type == TYPE_WS2812_2CH_X3 || _type == TYPE_WS2812_WWA ||
- _type == TYPE_ANALOG_2CH || _type == TYPE_ANALOG_5CH) return true;
+ virtual bool hasCCT(void) { return Bus::hasCCT(_type); }
+ static bool hasCCT(uint8_t type) {
+ if (type == TYPE_WS2812_2CH_X3 || type == TYPE_WS2812_WWA ||
+ type == TYPE_ANALOG_2CH || type == TYPE_ANALOG_5CH) return true;
return false;
}
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 uint8_t getGlobalAWMode() { return _gAWM; }
- bool reversed = false;
-
protected:
uint8_t _type;
uint8_t _bri;
uint16_t _start;
uint16_t _len;
+ bool _reversed;
bool _valid;
bool _needsRefresh;
uint8_t _autoWhiteMode;
+ uint8_t *_data;
static uint8_t _gAWM;
static int16_t _cct;
static uint8_t _cctBlend;
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 {
public:
BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com);
+ ~BusDigital() { cleanup(); }
- inline void show();
-
+ void show();
bool canShow();
-
void setBrightness(uint8_t b);
-
void setStatusPixel(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);
-
- uint8_t skippedLeds() {
- return _skip;
- }
-
- uint16_t getFrequency() { return _frequencykHz; }
-
+ uint32_t getPixelColor(uint16_t pix);
+ uint8_t getColorOrder() { return _colorOrder; }
+ uint8_t getPins(uint8_t* pinArray);
+ uint8_t skippedLeds() { return _skip; }
+ uint16_t getFrequency() { return _frequencykHz; }
void reinit();
-
void cleanup();
- ~BusDigital() {
- cleanup();
- }
-
private:
- uint8_t _colorOrder = COL_ORDER_GRB;
- uint8_t _pins[2] = {255, 255};
- uint8_t _iType = 0; //I_NONE;
- uint8_t _skip = 0;
- uint16_t _frequencykHz = 0U;
- void * _busPtr = nullptr;
+ uint8_t _skip;
+ uint8_t _colorOrder;
+ uint8_t _pins[2];
+ uint8_t _iType;
+ uint16_t _frequencykHz;
+ void * _busPtr;
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 slightly improves recovery / stops degradation on re-scale
+ }
+ }
+ return c;
+ }
};
class BusPwm : public Bus {
public:
BusPwm(BusConfig &bc);
+ ~BusPwm() { cleanup(); }
void setPixelColor(uint16_t pix, uint32_t c);
-
- //does no index check
- uint32_t getPixelColor(uint16_t pix);
-
- void show();
-
- uint8_t getPins(uint8_t* pinArray);
-
+ uint32_t getPixelColor(uint16_t pix); //does no index check
+ uint8_t getPins(uint8_t* pinArray);
uint16_t getFrequency() { return _frequency; }
-
- void cleanup() {
- deallocatePins();
- }
-
- ~BusPwm() {
- cleanup();
- }
+ void show();
+ void cleanup() { deallocatePins(); }
private:
- uint8_t _pins[5] = {255, 255, 255, 255, 255};
- uint8_t _data[5] = {0};
+ uint8_t _pins[5];
+ uint8_t _pwmdata[5];
#ifdef ARDUINO_ARCH_ESP32
- uint8_t _ledcStart = 255;
+ uint8_t _ledcStart;
#endif
- uint16_t _frequency = 0U;
+ uint16_t _frequency;
void deallocatePins();
};
@@ -264,72 +264,46 @@ class BusPwm : public Bus {
class BusOnOff : public Bus {
public:
BusOnOff(BusConfig &bc);
+ ~BusOnOff() { cleanup(); }
void setPixelColor(uint16_t pix, uint32_t c);
-
uint32_t getPixelColor(uint16_t pix);
-
+ uint8_t getPins(uint8_t* pinArray);
void show();
-
- uint8_t getPins(uint8_t* pinArray);
-
- void cleanup() {
- pinManager.deallocatePin(_pin, PinOwner::BusOnOff);
- }
-
- ~BusOnOff() {
- cleanup();
- }
+ void cleanup() { pinManager.deallocatePin(_pin, PinOwner::BusOnOff); }
private:
- uint8_t _pin = 255;
- uint8_t _data = 0;
+ uint8_t _pin;
+ uint8_t _onoffdata;
};
class BusNetwork : public Bus {
public:
BusNetwork(BusConfig &bc);
+ ~BusNetwork() { cleanup(); }
- bool hasRGB() { return true; }
+ bool hasRGB() { return true; }
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);
-
uint32_t getPixelColor(uint16_t pix);
-
+ uint8_t getPins(uint8_t* pinArray);
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);
-
- uint16_t getLength() {
- return _len;
- }
-
void cleanup();
- ~BusNetwork() {
- cleanup();
- }
-
private:
IPAddress _client;
uint8_t _UDPtype;
uint8_t _UDPchannels;
bool _rgbw;
bool _broadcastLock;
- byte *_data;
};
class BusManager {
public:
- BusManager() {};
+ BusManager() : numBusses(0) {};
//utility to get the approx. memory usage of a given BusConfig
static uint32_t memUsage(BusConfig &bc);
@@ -340,38 +314,24 @@ class BusManager {
void removeAll();
void show();
-
- void setStatusPixel(uint32_t c);
-
- void setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1);
-
- void setBrightness(uint8_t b);
-
- void setSegmentCCT(int16_t cct, bool allowWBCorrection = false);
-
- uint32_t getPixelColor(uint16_t pix);
-
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);
//semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
uint16_t getTotalLength();
+ inline uint8_t getNumBusses() const { return numBusses; }
- inline void updateColorOrderMap(const ColorOrderMap &com) {
- memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap));
- }
-
- inline const ColorOrderMap& getColorOrderMap() const {
- return colorOrderMap;
- }
-
- inline uint8_t getNumBusses() {
- return numBusses;
- }
+ inline void updateColorOrderMap(const ColorOrderMap &com) { memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap)); }
+ inline const ColorOrderMap& getColorOrderMap() const { return colorOrderMap; }
private:
- uint8_t numBusses = 0;
+ uint8_t numBusses;
Bus* busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES];
ColorOrderMap colorOrderMap;
@@ -381,4 +341,4 @@ class BusManager {
return j;
}
};
-#endif
\ No newline at end of file
+#endif
diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h
index dce23478d..45ace99d8 100644
--- a/wled00/bus_wrapper.h
+++ b/wled00/bus_wrapper.h
@@ -69,17 +69,17 @@
#define I_32_RN_NEO_3 21
#define I_32_I0_NEO_3 22
#define I_32_I1_NEO_3 23
-#define I_32_BB_NEO_3 24 // bitbangging on ESP32 not recommended
+#define I_32_BB_NEO_3 24 // bitbanging on ESP32 not recommended
//RGBW
#define I_32_RN_NEO_4 25
#define I_32_I0_NEO_4 26
#define I_32_I1_NEO_4 27
-#define I_32_BB_NEO_4 28 // bitbangging on ESP32 not recommended
+#define I_32_BB_NEO_4 28 // bitbanging on ESP32 not recommended
//400Kbps
#define I_32_RN_400_3 29
#define I_32_I0_400_3 30
#define I_32_I1_400_3 31
-#define I_32_BB_400_3 32 // bitbangging on ESP32 not recommended
+#define I_32_BB_400_3 32 // bitbanging on ESP32 not recommended
//TM1814 (RGBW)
#define I_32_RN_TM1_4 33
#define I_32_I0_TM1_4 34
@@ -280,6 +280,7 @@ class PolyBus {
#endif
if (clock_kHz) dotStar_strip->SetMethodSettings(NeoSpiSettings((uint32_t)clock_kHz*1000));
}
+
// Begin & initialize the PixelSettings for TM1814 strips.
template
static void beginTM1814(void* busPtr) {
@@ -288,6 +289,7 @@ class PolyBus {
// Max current for each LED (22.5 mA).
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) {
switch (busType) {
case I_NONE: break;
@@ -377,7 +379,7 @@ class PolyBus {
case I_32_I1_UCS_4: (static_cast(busPtr))->Begin(); break;
#endif
// case I_32_BB_UCS_4: (static_cast(busPtr))->Begin(); break;
- // ESP32 can (and should, to avoid inadvertantly driving the chip select signal) specify the pins used for SPI, but only in begin()
+ // ESP32 can (and should, to avoid inadvertently driving the chip select signal) specify the pins used for SPI, but only in begin()
case I_HS_DOT_3: beginDotStar(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break;
case I_HS_LPD_3: beginDotStar(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break;
case I_HS_LPO_3: beginDotStar(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break;
@@ -390,7 +392,8 @@ class PolyBus {
case I_SS_WS1_3: (static_cast(busPtr))->Begin(); break;
case I_SS_P98_3: (static_cast(busPtr))->Begin(); break;
}
- };
+ }
+
static void* create(uint8_t busType, uint8_t* pins, uint16_t len, uint8_t channel, uint16_t clock_kHz = 0U) {
void* busPtr = nullptr;
switch (busType) {
@@ -491,104 +494,106 @@ class PolyBus {
}
begin(busPtr, busType, pins, clock_kHz);
return busPtr;
- };
- static void show(void* busPtr, uint8_t busType) {
+ }
+
+ static void show(void* busPtr, uint8_t busType, bool consistent = true) {
switch (busType) {
case I_NONE: break;
#ifdef ESP8266
- case I_8266_U0_NEO_3: (static_cast(busPtr))->Show(); break;
- case I_8266_U1_NEO_3: (static_cast(busPtr))->Show(); break;
- case I_8266_DM_NEO_3: (static_cast(busPtr))->Show(); break;
- case I_8266_BB_NEO_3: (static_cast(busPtr))->Show(); break;
- case I_8266_U0_NEO_4: (static_cast(busPtr))->Show(); break;
- case I_8266_U1_NEO_4: (static_cast(busPtr))->Show(); break;
- case I_8266_DM_NEO_4: (static_cast(busPtr))->Show(); break;
- case I_8266_BB_NEO_4: (static_cast(busPtr))->Show(); break;
- case I_8266_U0_400_3: (static_cast(busPtr))->Show(); break;
- case I_8266_U1_400_3: (static_cast(busPtr))->Show(); break;
- case I_8266_DM_400_3: (static_cast(busPtr))->Show(); break;
- case I_8266_BB_400_3: (static_cast(busPtr))->Show(); break;
- case I_8266_U0_TM1_4: (static_cast(busPtr))->Show(); break;
- case I_8266_U1_TM1_4: (static_cast(busPtr))->Show(); break;
- case I_8266_DM_TM1_4: (static_cast(busPtr))->Show(); break;
- case I_8266_BB_TM1_4: (static_cast(busPtr))->Show(); break;
- case I_8266_U0_TM2_3: (static_cast(busPtr))->Show(); break;
- case I_8266_U1_TM2_3: (static_cast(busPtr))->Show(); break;
- case I_8266_DM_TM2_3: (static_cast(busPtr))->Show(); break;
- case I_8266_BB_TM2_3: (static_cast(busPtr))->Show(); break;
- case I_8266_U0_UCS_3: (static_cast(busPtr))->Show(); break;
- case I_8266_U1_UCS_3: (static_cast(busPtr))->Show(); break;
- case I_8266_DM_UCS_3: (static_cast(busPtr))->Show(); break;
- case I_8266_BB_UCS_3: (static_cast(busPtr))->Show(); break;
- case I_8266_U0_UCS_4: (static_cast(busPtr))->Show(); break;
- case I_8266_U1_UCS_4: (static_cast(busPtr))->Show(); break;
- case I_8266_DM_UCS_4: (static_cast(busPtr))->Show(); break;
- case I_8266_BB_UCS_4: (static_cast(busPtr))->Show(); break;
+ case I_8266_U0_NEO_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_U1_NEO_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_DM_NEO_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_BB_NEO_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_U0_NEO_4: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_U1_NEO_4: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_DM_NEO_4: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_BB_NEO_4: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_U0_400_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_U1_400_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_DM_400_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_BB_400_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_U0_TM1_4: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_U1_TM1_4: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_DM_TM1_4: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_BB_TM1_4: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_U0_TM2_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_U1_TM2_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_DM_TM2_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_BB_TM2_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_U0_UCS_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_U1_UCS_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_DM_UCS_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_BB_UCS_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_U0_UCS_4: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_U1_UCS_4: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_DM_UCS_4: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_BB_UCS_4: (static_cast(busPtr))->Show(consistent); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
- case I_32_RN_NEO_3: (static_cast(busPtr))->Show(); break;
+ case I_32_RN_NEO_3: (static_cast(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS
- case I_32_I0_NEO_3: (static_cast(busPtr))->Show(); break;
+ case I_32_I0_NEO_3: (static_cast(busPtr))->Show(consistent); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
- case I_32_I1_NEO_3: (static_cast(busPtr))->Show(); break;
+ case I_32_I1_NEO_3: (static_cast(busPtr))->Show(consistent); break;
#endif
-// case I_32_BB_NEO_3: (static_cast(busPtr))->Show(); break;
- case I_32_RN_NEO_4: (static_cast(busPtr))->Show(); break;
+// case I_32_BB_NEO_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_32_RN_NEO_4: (static_cast(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS
- case I_32_I0_NEO_4: (static_cast(busPtr))->Show(); break;
+ case I_32_I0_NEO_4: (static_cast(busPtr))->Show(consistent); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
- case I_32_I1_NEO_4: (static_cast(busPtr))->Show(); break;
+ case I_32_I1_NEO_4: (static_cast(busPtr))->Show(consistent); break;
#endif
-// case I_32_BB_NEO_4: (static_cast(busPtr))->Show(); break;
- case I_32_RN_400_3: (static_cast(busPtr))->Show(); break;
+// case I_32_BB_NEO_4: (static_cast(busPtr))->Show(consistent); break;
+ case I_32_RN_400_3: (static_cast(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS
- case I_32_I0_400_3: (static_cast(busPtr))->Show(); break;
+ case I_32_I0_400_3: (static_cast(busPtr))->Show(consistent); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
- case I_32_I1_400_3: (static_cast(busPtr))->Show(); break;
+ case I_32_I1_400_3: (static_cast(busPtr))->Show(consistent); break;
#endif
-// case I_32_BB_400_3: (static_cast(busPtr))->Show(); break;
- case I_32_RN_TM1_4: (static_cast(busPtr))->Show(); break;
- case I_32_RN_TM2_3: (static_cast(busPtr))->Show(); break;
+// case I_32_BB_400_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_32_RN_TM1_4: (static_cast(busPtr))->Show(consistent); break;
+ case I_32_RN_TM2_3: (static_cast(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS
- case I_32_I0_TM1_4: (static_cast(busPtr))->Show(); break;
- case I_32_I0_TM2_3: (static_cast(busPtr))->Show(); break;
+ case I_32_I0_TM1_4: (static_cast(busPtr))->Show(consistent); break;
+ case I_32_I0_TM2_3: (static_cast(busPtr))->Show(consistent); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
- case I_32_I1_TM1_4: (static_cast(busPtr))->Show(); break;
- case I_32_I1_TM2_3: (static_cast(busPtr))->Show(); break;
+ case I_32_I1_TM1_4: (static_cast(busPtr))->Show(consistent); break;
+ case I_32_I1_TM2_3: (static_cast(busPtr))->Show(consistent); break;
#endif
- case I_32_RN_UCS_3: (static_cast(busPtr))->Show(); break;
+ case I_32_RN_UCS_3: (static_cast(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS
- case I_32_I0_UCS_3: (static_cast(busPtr))->Show(); break;
+ case I_32_I0_UCS_3: (static_cast(busPtr))->Show(consistent); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
- case I_32_I1_UCS_3: (static_cast(busPtr))->Show(); break;
+ case I_32_I1_UCS_3: (static_cast(busPtr))->Show(consistent); break;
#endif
-// case I_32_BB_UCS_3: (static_cast(busPtr))->Show(); break;
- case I_32_RN_UCS_4: (static_cast(busPtr))->Show(); break;
+// case I_32_BB_UCS_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_32_RN_UCS_4: (static_cast(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS
- case I_32_I0_UCS_4: (static_cast(busPtr))->Show(); break;
+ case I_32_I0_UCS_4: (static_cast(busPtr))->Show(consistent); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
- case I_32_I1_UCS_4: (static_cast(busPtr))->Show(); break;
+ case I_32_I1_UCS_4: (static_cast(busPtr))->Show(consistent); break;
#endif
-// case I_32_BB_UCS_4: (static_cast(busPtr))->Show(); break;
+// case I_32_BB_UCS_4: (static_cast(busPtr))->Show(consistent); break;
#endif
- case I_HS_DOT_3: (static_cast(busPtr))->Show(); break;
- case I_SS_DOT_3: (static_cast(busPtr))->Show(); break;
- case I_HS_LPD_3: (static_cast(busPtr))->Show(); break;
- case I_SS_LPD_3: (static_cast(busPtr))->Show(); break;
- case I_HS_LPO_3: (static_cast(busPtr))->Show(); break;
- case I_SS_LPO_3: (static_cast(busPtr))->Show(); break;
- case I_HS_WS1_3: (static_cast(busPtr))->Show(); break;
- case I_SS_WS1_3: (static_cast(busPtr))->Show(); break;
- case I_HS_P98_3: (static_cast(busPtr))->Show(); break;
- case I_SS_P98_3: (static_cast(busPtr))->Show(); break;
+ case I_HS_DOT_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_SS_DOT_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_HS_LPD_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_SS_LPD_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_HS_LPO_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_SS_LPO_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_HS_WS1_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_SS_WS1_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_HS_P98_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_SS_P98_3: (static_cast(busPtr))->Show(consistent); break;
}
- };
+ }
+
static bool canShow(void* busPtr, uint8_t busType) {
switch (busType) {
case I_NONE: return true;
@@ -685,7 +690,8 @@ class PolyBus {
case I_SS_P98_3: return (static_cast(busPtr))->CanShow(); break;
}
return true;
- };
+ }
+
static void setPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint32_t c, uint8_t co) {
uint8_t r = c >> 16;
uint8_t g = c >> 8;
@@ -805,104 +811,106 @@ class PolyBus {
case I_HS_P98_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_SS_P98_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
}
- };
+ }
+
static void setBrightness(void* busPtr, uint8_t busType, uint8_t b) {
switch (busType) {
case I_NONE: break;
#ifdef ESP8266
- case I_8266_U0_NEO_3: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_U1_NEO_3: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_DM_NEO_3: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_BB_NEO_3: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_U0_NEO_4: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_U1_NEO_4: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_DM_NEO_4: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_BB_NEO_4: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_U0_400_3: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_U1_400_3: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_DM_400_3: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_BB_400_3: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_U0_TM1_4: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_U1_TM1_4: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_DM_TM1_4: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_BB_TM1_4: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_U0_TM2_3: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_U1_TM2_3: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_DM_TM2_3: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_BB_TM2_3: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_U0_UCS_3: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_U1_UCS_3: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_DM_UCS_3: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_BB_UCS_3: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_U0_UCS_4: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_U1_UCS_4: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_DM_UCS_4: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
- case I_8266_BB_UCS_4: (static_cast(busPtr))->SetLuminance(b); (static_cast(busPtr))->ApplyPostAdjustments(); break;
+ case I_8266_U0_NEO_3: (static_cast(busPtr))->SetLuminance(b); break;
+ case I_8266_U1_NEO_3: (static_cast(busPtr))->SetLuminance(b); break;
+ case I_8266_DM_NEO_3: (static_cast(busPtr))->SetLuminance(b); break;
+ case I_8266_BB_NEO_3: (static_cast(busPtr))->SetLuminance(b); break;
+ case I_8266_U0_NEO_4: (static_cast(busPtr))->SetLuminance(b); break;
+ case I_8266_U1_NEO_4: (static_cast(busPtr))->SetLuminance(b); break;
+ case I_8266_DM_NEO_4: (static_cast(busPtr))->SetLuminance(b); break;
+ case I_8266_BB_NEO_4: (static_cast(busPtr))->SetLuminance(b); break;
+ case I_8266_U0_400_3: (static_cast(busPtr))->SetLuminance(b); break;
+ case I_8266_U1_400_3: (static_cast(busPtr))->SetLuminance(b); break;
+ case I_8266_DM_400_3: (static_cast(busPtr))->SetLuminance(b); break;
+ case I_8266_BB_400_3: (static_cast(busPtr))->SetLuminance(b); break;
+ case I_8266_U0_TM1_4: (static_cast