Merge remote-tracking branch 'upstream/0_15' into fix_usermod_tetisai
20
.github/stale.yml
vendored
@ -1,20 +0,0 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 120
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- keep
|
||||
- enhancement
|
||||
- confirmed
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
Hey! This issue has been open for quite some time without any new comments now.
|
||||
It will be closed automatically in a week if no further activity occurs.
|
||||
|
||||
Thank you for using WLED!
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
30
.github/workflows/stale.yml
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
name: 'Close stale issues and PRs'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 12 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
days-before-stale: 120
|
||||
days-before-close: 7
|
||||
stale-issue-label: 'stale'
|
||||
stale-pr-label: 'stale'
|
||||
exempt-issue-labels: 'pinned,keep,enhancement,confirmed'
|
||||
exempt-pr-labels: 'pinned,keep,enhancement,confirmed'
|
||||
exempt-all-milestones: true
|
||||
operations-per-run: 1000
|
||||
stale-issue-message: >
|
||||
Hey! This issue has been open for quite some time without any new comments now.
|
||||
It will be closed automatically in a week if no further activity occurs.
|
||||
|
||||
Thank you for using WLED! ✨
|
||||
stale-pr-message: >
|
||||
Hey! This pull request has been open for quite some time without any new comments now.
|
||||
It will be closed automatically in a week if no further activity occurs.
|
||||
|
||||
Thank you for contributing to WLED! ❤️
|
2
.gitignore
vendored
@ -21,4 +21,4 @@ wled-update.sh
|
||||
/wled00/my_config.h
|
||||
/wled00/Release
|
||||
/wled00/wled00.ino.cpp
|
||||
/wled00/html_*.h
|
||||
/wled00/html_*.h
|
||||
|
41
CHANGELOG.md
@ -1,5 +1,41 @@
|
||||
## WLED changelog
|
||||
|
||||
#### Build 2405180
|
||||
- Official 0.15.0-b3 release
|
||||
- Merge 0.14.3 fixes
|
||||
- Added Pinwheel Expand 1D->2D effect mapping mode (#3961 by @Brandon502)
|
||||
- Add changeable i2c address to BME280 usermod (#3966 by @LordMike)
|
||||
- Effect: Firenoise - add palette selection
|
||||
- Experimental parallel I2S support for ESP32 (compile time option)
|
||||
- increased outputs to 17
|
||||
- increased max possible color order overrides
|
||||
- use WLED_USE_PARALLEL_I2S during compile
|
||||
WARNING: Do not set up more than 256 LEDs per output when using parallel I2S with NeoPixelBus less than 2.9.0
|
||||
- Update Usermod: Battery (#3964 by @adamsthws)
|
||||
- Update Usermod: BME280 (#3965 by @LordMike)
|
||||
- TM1914 chip support (#3913)
|
||||
- Ignore brightness in Peek
|
||||
- Antialiased line & circle drawing functions
|
||||
- Enabled some audioreactive effects for single pixel strips/segments (#3942 by @gaaat98)
|
||||
- Usermod Battery: Added Support for different battery types, Optimized file structure (#3003 by @itCarl)
|
||||
- Skip playlist entry API (#3946 by @freakintoddles2)
|
||||
- various optimisations and bugfixes (#3987, #3978)
|
||||
|
||||
#### Build 2405030
|
||||
- Using brightness in analog clock overlay (#3944 by @paspiz85)
|
||||
- Add Webpage shortcuts (#3945 by @w00000dy)
|
||||
- ArtNet Poll reply (#3892 by @askask)
|
||||
- Improved brightness change via long button presses (#3933 by @gaaat98)
|
||||
- Relay open drain output (#3920 by @Suxsem)
|
||||
- NEW JSON API: release info (update page, `info.release`)
|
||||
- update esp32 platform to arduino-esp32 v2.0.9 (#3902)
|
||||
- various optimisations and bugfixes (#3952, #3922, #3878, #3926, #3919, #3904 @DedeHai)
|
||||
|
||||
#### Build 2404120
|
||||
- v0.15.0-b3
|
||||
- fix for #3896 & WS2815 current saving
|
||||
- conditional compile for AA setPixelColor()
|
||||
|
||||
#### Build 2404100
|
||||
- Internals: #3859, #3862, #3873, #3875
|
||||
- Prefer I2S1 over RMT on ESP32
|
||||
@ -17,6 +53,11 @@
|
||||
- Fix for #3889
|
||||
- BREAKING: Effect: modified KITT (Scanner) (#3763)
|
||||
|
||||
#### Build 2404040
|
||||
- WLED 0.14.3 release
|
||||
- Fix for transition 0 (#3854, #3832, #3720)
|
||||
- Fix for #3855 via #3873 (by @willmmiles)
|
||||
|
||||
#### Build 2403280
|
||||
- Individual color channel control for JSON API (fixes #3860)
|
||||
- "col":[int|string|object|array, int|string|object|array, int|string|object|array]
|
||||
|
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "wled",
|
||||
"version": "0.15.0-b2",
|
||||
"version": "0.15.0-b3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "wled",
|
||||
"version": "0.15.0-b2",
|
||||
"version": "0.15.0-b3",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"clean-css": "^5.3.3",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wled",
|
||||
"version": "0.15.0-b2",
|
||||
"version": "0.15.0-b3",
|
||||
"description": "Tools for WLED project",
|
||||
"main": "tools/cdata.js",
|
||||
"directories": {
|
||||
|
@ -1,9 +1,24 @@
|
||||
# Little convenience script to get an object dump
|
||||
# You may add "-S" to the objdump commandline (i.e. replace "-D -C " with "-d -S -C ")
|
||||
# to get source code intermixed with disassembly (SLOW !)
|
||||
|
||||
Import('env')
|
||||
|
||||
def obj_dump_after_elf(source, target, env):
|
||||
platform = env.PioPlatform()
|
||||
board = env.BoardConfig()
|
||||
mcu = board.get("build.mcu", "esp32")
|
||||
|
||||
print("Create firmware.asm")
|
||||
env.Execute("xtensa-lx106-elf-objdump "+ "-D " + str(target[0]) + " > "+ "${PROGNAME}.asm")
|
||||
|
||||
if mcu == "esp8266":
|
||||
env.Execute("xtensa-lx106-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
|
||||
if mcu == "esp32":
|
||||
env.Execute("xtensa-esp32-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
|
||||
if mcu == "esp32s2":
|
||||
env.Execute("xtensa-esp32s2-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
|
||||
if mcu == "esp32s3":
|
||||
env.Execute("xtensa-esp32s3-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
|
||||
if mcu == "esp32c3":
|
||||
env.Execute("riscv32-esp-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
|
||||
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", [obj_dump_after_elf])
|
||||
|
@ -36,6 +36,8 @@ def create_release(source):
|
||||
def bin_rename_copy(source, target, env):
|
||||
_create_dirs()
|
||||
variant = env["PIOENV"]
|
||||
builddir = os.path.join(env["PROJECT_BUILD_DIR"], variant)
|
||||
source_map = os.path.join(builddir, env["PROGNAME"] + ".map")
|
||||
|
||||
# create string with location and file names based on variant
|
||||
map_file = "{}map{}{}.map".format(OUTPUT_DIR, os.path.sep, variant)
|
||||
@ -44,7 +46,11 @@ def bin_rename_copy(source, target, env):
|
||||
|
||||
# copy firmware.map to map/<variant>.map
|
||||
if os.path.isfile("firmware.map"):
|
||||
shutil.move("firmware.map", map_file)
|
||||
print("Found linker mapfile firmware.map")
|
||||
shutil.copy("firmware.map", map_file)
|
||||
if os.path.isfile(source_map):
|
||||
print(f"Found linker mapfile {source_map}")
|
||||
shutil.copy(source_map, map_file)
|
||||
|
||||
def bin_gzip(source, target):
|
||||
# only create gzip for esp8266
|
||||
|
150
platformio.ini
@ -10,7 +10,7 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# CI/release binaries
|
||||
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, nodemcuv2_160, esp8266_2m_160, esp01_1m_full_160, esp32dev, esp32_eth, esp32dev_audioreactive, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB, esp32s3dev_8MB_PSRAM_opi, esp32s3_4M_PSRAM_qspi, esp32_wrover
|
||||
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, nodemcuv2_160, esp8266_2m_160, esp01_1m_full_160, esp32dev, esp32_eth, esp32dev_audioreactive, lolin_s2_mini, esp32c3dev, esp32s3dev_16MB_opi, esp32s3dev_8MB_opi, esp32s3_4M_qspi, esp32_wrover
|
||||
|
||||
src_dir = ./wled00
|
||||
data_dir = ./wled00/data
|
||||
@ -86,7 +86,6 @@ debug_flags = -D DEBUG=1 -D WLED_DEBUG
|
||||
# This reduces the OTA size with ~45KB, so it's especially useful on low memory boards (512k/1m).
|
||||
# ------------------------------------------------------------------------------
|
||||
build_flags =
|
||||
-Wno-attributes
|
||||
-DMQTT_MAX_PACKET_SIZE=1024
|
||||
-DSECURE_CLIENT=SECURE_CLIENT_BEARSSL
|
||||
-DBEARSSL_SSL_BASIC
|
||||
@ -104,10 +103,6 @@ build_flags =
|
||||
|
||||
build_unflags =
|
||||
|
||||
build_flags_esp8266 = ${common.build_flags} ${esp8266.build_flags}
|
||||
build_flags_esp32 = ${common.build_flags} ${esp32.build_flags}
|
||||
build_flags_esp32_V4= ${common.build_flags} ${esp32_idf_V4.build_flags}
|
||||
|
||||
ldscript_1m128k = eagle.flash.1m128.ld
|
||||
ldscript_2m512k = eagle.flash.2m512.ld
|
||||
ldscript_2m1m = eagle.flash.2m1m.ld
|
||||
@ -120,6 +115,7 @@ extra_scripts =
|
||||
post:pio-scripts/strip-floats.py
|
||||
pre:pio-scripts/user_config_copy.py
|
||||
pre:pio-scripts/build_ui.py
|
||||
; post:pio-scripts/obj-dump.py ;; convenience script to create a disassembly dump of the firmware (hardcore debugging)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# COMMON SETTINGS:
|
||||
@ -174,7 +170,7 @@ lib_deps =
|
||||
# SHT85
|
||||
;robtillaart/SHT85@~0.3.3
|
||||
# Audioreactive usermod
|
||||
;kosme/arduinoFFT @ 2.0.0
|
||||
;kosme/arduinoFFT @ 2.0.1
|
||||
|
||||
extra_scripts = ${scripts_defaults.extra_scripts}
|
||||
|
||||
@ -218,14 +214,19 @@ 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
|
||||
tiny_partitions = tools/WLED_ESP32_2MB_noOTA.csv
|
||||
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||
extended_partitions = tools/WLED_ESP32_4MB_700k_FS.csv
|
||||
big_partitions = tools/WLED_ESP32_4MB_256KB_FS.csv ;; 1.8MB firmware, 256KB filesystem, coredump support
|
||||
large_partitions = tools/WLED_ESP32_8MB.csv
|
||||
extreme_partitions = tools/WLED_ESP32_16MB_9MB_FS.csv
|
||||
lib_deps =
|
||||
https://github.com/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
|
||||
AR_lib_deps = kosme/arduinoFFT @ 2.0.0
|
||||
AR_lib_deps = kosme/arduinoFFT @ 2.0.1
|
||||
|
||||
[esp32_idf_V4]
|
||||
;; experimental build environment for ESP32 using ESP-IDF 4.4.x / arduino-esp32 v2.0.5
|
||||
@ -233,24 +234,21 @@ AR_lib_deps = kosme/arduinoFFT @ 2.0.0
|
||||
;;
|
||||
;; please note that you can NOT update existing ESP32 installs with a "V4" build. Also updating by OTA will not work properly.
|
||||
;; You need to completely erase your device (esptool erase_flash) first, then install the "V4" build from VSCode+platformio.
|
||||
platform = espressif32@5.3.0
|
||||
platform_packages =
|
||||
platform = espressif32@ ~6.3.2
|
||||
platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them)
|
||||
build_flags = -g
|
||||
-Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one
|
||||
-DARDUINO_ARCH_ESP32 -DESP32
|
||||
#-DCONFIG_LITTLEFS_FOR_IDF_3_2
|
||||
-D CONFIG_ASYNC_TCP_USE_WDT=0
|
||||
-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/pbolduc/AsyncTCP.git @ 1.2.0
|
||||
${env.lib_deps}
|
||||
|
||||
[esp32s2]
|
||||
;; generic definitions for all ESP32-S2 boards
|
||||
platform = espressif32@5.3.0
|
||||
platform_packages =
|
||||
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||
platform = espressif32@ ~6.3.2
|
||||
platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them)
|
||||
build_flags = -g
|
||||
-DARDUINO_ARCH_ESP32
|
||||
-DARDUINO_ARCH_ESP32S2
|
||||
@ -267,8 +265,8 @@ lib_deps =
|
||||
|
||||
[esp32c3]
|
||||
;; generic definitions for all ESP32-C3 boards
|
||||
platform = espressif32@5.3.0
|
||||
platform_packages =
|
||||
platform = espressif32@ ~6.3.2
|
||||
platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them)
|
||||
build_flags = -g
|
||||
-DARDUINO_ARCH_ESP32
|
||||
-DARDUINO_ARCH_ESP32C3
|
||||
@ -284,8 +282,8 @@ lib_deps =
|
||||
|
||||
[esp32s3]
|
||||
;; generic definitions for all ESP32-S3 boards
|
||||
platform = espressif32@5.3.0
|
||||
platform_packages =
|
||||
platform = espressif32@ ~6.3.2
|
||||
platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them)
|
||||
build_flags = -g
|
||||
-DESP32
|
||||
-DARDUINO_ARCH_ESP32
|
||||
@ -296,7 +294,6 @@ build_flags = -g
|
||||
-DCO
|
||||
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
|
||||
;; ARDUINO_USB_MODE, ARDUINO_USB_CDC_ON_BOOT
|
||||
|
||||
lib_deps =
|
||||
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
|
||||
${env.lib_deps}
|
||||
@ -312,14 +309,14 @@ platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 #-DWLED_DISABLE_2D
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP8266 #-DWLED_DISABLE_2D
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
monitor_filters = esp8266_exception_decoder
|
||||
|
||||
[env:nodemcuv2_160]
|
||||
extends = env:nodemcuv2
|
||||
board_build.f_cpu = 160000000L
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266_160 #-DWLED_DISABLE_2D
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP8266_160 #-DWLED_DISABLE_2D
|
||||
|
||||
[env:esp8266_2m]
|
||||
board = esp_wroom_02
|
||||
@ -327,13 +324,13 @@ platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP02
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp8266_2m_160]
|
||||
extends = env:esp8266_2m
|
||||
board_build.f_cpu = 160000000L
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02_160
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP02_160
|
||||
|
||||
[env:esp01_1m_full]
|
||||
board = esp01_1m
|
||||
@ -341,32 +338,45 @@ platform = ${common.platform_wled_default}
|
||||
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
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA
|
||||
; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp01_1m_full_160]
|
||||
extends = env:esp01_1m_full
|
||||
board_build.f_cpu = 160000000L
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01_160 -D WLED_DISABLE_OTA
|
||||
; -D WLED_USE_UNREAL_MATH ;; may cause wrong sunset/sunrise times, but saves 7064 bytes FLASH and 975 bytes RAM
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP01_160 -D WLED_DISABLE_OTA
|
||||
; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM
|
||||
|
||||
[env:esp32dev]
|
||||
board = esp32dev
|
||||
platform = ${esp32.platform}
|
||||
platform_packages = ${esp32.platform_packages}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 #-D WLED_DISABLE_BROWNOUT_DET
|
||||
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=ESP32 #-D WLED_DISABLE_BROWNOUT_DET
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
[env:esp32dev_8M]
|
||||
board = esp32dev
|
||||
platform = ${esp32_idf_V4.platform}
|
||||
platform_packages = ${esp32_idf_V4.platform_packages}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_8M #-D WLED_DISABLE_BROWNOUT_DET
|
||||
${esp32.AR_build_flags}
|
||||
lib_deps = ${esp32_idf_V4.lib_deps}
|
||||
${esp32.AR_lib_deps}
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.partitions = ${esp32.large_partitions}
|
||||
; board_build.f_flash = 80000000L
|
||||
|
||||
[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
|
||||
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=ESP32_audioreactive #-D WLED_DISABLE_BROWNOUT_DET
|
||||
${esp32.AR_build_flags}
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
${esp32.AR_lib_deps}
|
||||
@ -381,22 +391,26 @@ platform = ${esp32.platform}
|
||||
platform_packages = ${esp32.platform_packages}
|
||||
upload_speed = 921600
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1
|
||||
build_flags = ${common.build_flags} ${esp32.build_flags} -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}
|
||||
|
||||
[env:esp32_wrover]
|
||||
platform = ${esp32.platform}
|
||||
extends = esp32_idf_V4
|
||||
platform = ${esp32_idf_V4.platform}
|
||||
platform_packages = ${esp32_idf_V4.platform_packages}
|
||||
board = ttgo-t7-v14-mini32
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
board_build.partitions = tools/WLED_ESP32-wrover_4MB.csv
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_WROVER
|
||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_WROVER
|
||||
-DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue ;; Older ESP32 (rev.<3) need a PSRAM fix (increases static RAM used) https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/external-ram.html
|
||||
-D LEDPIN=25
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
; ${esp32.AR_build_flags}
|
||||
lib_deps = ${esp32_idf_V4.lib_deps}
|
||||
; ${esp32.AR_lib_deps}
|
||||
|
||||
[env:esp32c3dev]
|
||||
extends = esp32c3
|
||||
@ -404,7 +418,7 @@ platform = ${esp32c3.platform}
|
||||
platform_packages = ${esp32c3.platform_packages}
|
||||
framework = arduino
|
||||
board = esp32-c3-devkitm-1
|
||||
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=ESP32-C3
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
-DLOLIN_WIFI_FIX ; seems to work much better with this
|
||||
@ -414,35 +428,15 @@ upload_speed = 460800
|
||||
build_unflags = ${common.build_unflags}
|
||||
lib_deps = ${esp32c3.lib_deps}
|
||||
|
||||
[env:esp32s3dev_8MB]
|
||||
;; ESP32-S3-DevKitC-1 development board, with 8MB FLASH, no PSRAM (flash_mode: qio)
|
||||
board = esp32-s3-devkitc-1
|
||||
platform = ${esp32s3.platform}
|
||||
platform_packages = ${esp32s3.platform_packages}
|
||||
upload_speed = 921600 ; or 460800
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_8MB
|
||||
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
|
||||
-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
|
||||
;-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
|
||||
${esp32.AR_build_flags}
|
||||
lib_deps = ${esp32s3.lib_deps}
|
||||
${esp32.AR_lib_deps}
|
||||
board_build.partitions = tools/WLED_ESP32_8MB.csv
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
; board_build.flash_mode = dio ;; try this if you have problems at startup
|
||||
monitor_filters = esp32_exception_decoder
|
||||
|
||||
[env:esp32s3dev_8MB_PSRAM_opi]
|
||||
;; ESP32-S3 development board, with 8MB FLASH and >= 8MB PSRAM (memory_type: qio_opi)
|
||||
[env:esp32s3dev_16MB_opi]
|
||||
;; ESP32-S3 development board, with 16MB 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 WLED_RELEASE_NAME=ESP32-S3_8MB_PSRAM_opi
|
||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_16MB_opi
|
||||
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
|
||||
;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
|
||||
-D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
|
||||
@ -450,27 +444,47 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=
|
||||
${esp32.AR_build_flags}
|
||||
lib_deps = ${esp32s3.lib_deps}
|
||||
${esp32.AR_lib_deps}
|
||||
board_build.partitions = tools/WLED_ESP32_8MB.csv
|
||||
board_build.partitions = ${esp32.extreme_partitions}
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
monitor_filters = esp32_exception_decoder
|
||||
|
||||
[env:esp32s3_4M_PSRAM_qspi]
|
||||
;; ESP32-S3, with 4MB FLASH and <= 4MB PSRAM (memory_type: qio_qspi)
|
||||
[env:esp32s3dev_8MB_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_qspi ;; use with PSRAM: 2MB or 4MB
|
||||
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 WLED_RELEASE_NAME=ESP32-S3_4M_PSRAM_qspi
|
||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_8MB_opi
|
||||
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
|
||||
;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
|
||||
-D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
|
||||
-DBOARD_HAS_PSRAM
|
||||
${esp32.AR_build_flags}
|
||||
lib_deps = ${esp32s3.lib_deps}
|
||||
${esp32.AR_lib_deps}
|
||||
board_build.partitions = ${esp32.large_partitions}
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
monitor_filters = esp32_exception_decoder
|
||||
|
||||
[env:esp32s3_4M_qspi]
|
||||
;; ESP32-S3, with 4MB FLASH and <= 4MB PSRAM (memory_type: qio_qspi)
|
||||
board = lolin_s3_mini ;; -S3 mini, 4MB flash 2MB PSRAM
|
||||
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 WLED_RELEASE_NAME=ESP32-S3_4M_qspi
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
|
||||
-DBOARD_HAS_PSRAM
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
${esp32.AR_build_flags}
|
||||
lib_deps = ${esp32s3.lib_deps}
|
||||
${esp32.AR_lib_deps}
|
||||
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
monitor_filters = esp32_exception_decoder
|
||||
@ -479,9 +493,9 @@ monitor_filters = esp32_exception_decoder
|
||||
platform = ${esp32s2.platform}
|
||||
platform_packages = ${esp32s2.platform_packages}
|
||||
board = lolin_s2_mini
|
||||
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||
;board_build.flash_mode = qio
|
||||
;board_build.f_flash = 80000000L
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
board_build.flash_mode = qio
|
||||
board_build.f_flash = 80000000L
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=ESP32-S2
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
|
@ -28,12 +28,15 @@ lib_deps = ${esp8266.lib_deps}
|
||||
; robtillaart/SHT85@~0.3.3
|
||||
; gmag11/QuickESPNow ;@ 0.6.2
|
||||
; https://github.com/blazoncek/QuickESPNow.git#optional-debug ;; exludes debug library
|
||||
; https://github.com/kosme/arduinoFFT#develop @ 1.9.2+sha.419d7b0 ;; used for USERMOD_AUDIOREACTIVE - using "known working" hash
|
||||
; build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
; https://github.com/kosme/arduinoFFT#develop @ 2.0.1 ;; used for USERMOD_AUDIOREACTIVE
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
;
|
||||
; *** To use the below defines/overrides, copy and paste each onto it's own line just below build_flags in the section above.
|
||||
;
|
||||
; Set a release name that may be used to distinguish required binary for flashing
|
||||
; -D WLED_RELEASE_NAME=ESP32_MULTI_USREMODS
|
||||
;
|
||||
; disable specific features
|
||||
; -D WLED_DISABLE_OTA
|
||||
; -D WLED_DISABLE_ALEXA
|
||||
@ -56,6 +59,7 @@ build_flags = ${common.build_flags_esp8266}
|
||||
; -D IRPIN=4
|
||||
; -D RLYPIN=12
|
||||
; -D RLYMDE=1
|
||||
; -D RLYODRAIN=0
|
||||
; -D LED_BUILTIN=2 # GPIO of built-in LED
|
||||
;
|
||||
; Limit max buses
|
||||
@ -90,6 +94,12 @@ build_flags = ${common.build_flags_esp8266}
|
||||
; -D USERMOD_AUTO_SAVE
|
||||
; -D AUTOSAVE_AFTER_SEC=90
|
||||
;
|
||||
; Use AHT10/AHT15/AHT20 usermod
|
||||
; -D USERMOD_AHT10
|
||||
;
|
||||
; Use INA226 usermod
|
||||
; -D USERMOD_INA226
|
||||
;
|
||||
; Use 4 Line Display usermod with SPI display
|
||||
; -D USERMOD_FOUR_LINE_DISPLAY
|
||||
; -D USE_ALT_DISPlAY # mandatory
|
||||
@ -179,7 +189,7 @@ platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini]
|
||||
@ -189,7 +199,7 @@ platform_packages = ${common.platform_packages}
|
||||
upload_speed = 921600
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
monitor_filters = esp8266_exception_decoder
|
||||
|
||||
@ -199,7 +209,7 @@ platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:h803wf]
|
||||
@ -208,7 +218,7 @@ platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D LEDPIN=1 -D WLED_DISABLE_INFRARED
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D LEDPIN=1 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp32dev_qio80]
|
||||
@ -216,7 +226,7 @@ board = esp32dev
|
||||
platform = ${esp32.platform}
|
||||
platform_packages = ${esp32.platform_packages}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_qio80 #-D WLED_DISABLE_BROWNOUT_DET
|
||||
build_flags = ${common.build_flags} ${esp32.build_flags} #-D WLED_DISABLE_BROWNOUT_DET
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
@ -231,7 +241,7 @@ board = esp32dev
|
||||
platform = ${esp32_idf_V4.platform}
|
||||
platform_packages = ${esp32_idf_V4.platform_packages}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_V4_qio80 #-D WLED_DISABLE_BROWNOUT_DET
|
||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} #-D WLED_DISABLE_BROWNOUT_DET
|
||||
lib_deps = ${esp32_idf_V4.lib_deps}
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.partitions = ${esp32_idf_V4.default_partitions}
|
||||
@ -240,14 +250,14 @@ board_build.flash_mode = dio
|
||||
|
||||
[env:esp32s2_saola]
|
||||
board = esp32-s2-saola-1
|
||||
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.2/platform-tasmota-espressif32-2.0.2.zip
|
||||
platform_packages =
|
||||
platform = ${esp32s2.platform}
|
||||
platform_packages = ${esp32s2.platform_packages}
|
||||
framework = arduino
|
||||
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||
board_build.flash_mode = qio
|
||||
upload_speed = 460800
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=S2_saola
|
||||
build_flags = ${common.build_flags} ${esp32s2.build_flags}
|
||||
;-DLOLIN_WIFI_FIX ;; try this in case Wifi does not work
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
lib_deps = ${esp32s2.lib_deps}
|
||||
@ -265,7 +275,7 @@ platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_DISABLE_OTA
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp8285_H801]
|
||||
@ -274,7 +284,7 @@ platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_DISABLE_OTA
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini_5CH_Shojo_PCB]
|
||||
@ -283,7 +293,7 @@ platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_USE_SHOJO_PCB
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_USE_SHOJO_PCB
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini_debug]
|
||||
@ -293,7 +303,7 @@ platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} ${common.debug_flags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} ${common.debug_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini_ota]
|
||||
@ -305,7 +315,7 @@ platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:anavi_miracle_controller]
|
||||
@ -314,7 +324,7 @@ platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D LEDPIN=12 -D IRPIN=-1 -D RLYPIN=2
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D LEDPIN=12 -D IRPIN=-1 -D RLYPIN=2
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp32c3dev_2MB]
|
||||
@ -324,7 +334,7 @@ extends = esp32c3
|
||||
platform = ${esp32c3.platform}
|
||||
platform_packages = ${esp32c3.platform_packages}
|
||||
board = esp32-c3-devkitm-1
|
||||
build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3
|
||||
build_flags = ${common.build_flags} ${esp32c3.build_flags}
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
-D WLED_DISABLE_OTA
|
||||
; -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB
|
||||
@ -341,7 +351,7 @@ platform = ${esp32.platform}
|
||||
platform_packages = ${esp32.platform_packages}
|
||||
upload_speed = 460800
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32}
|
||||
build_flags = ${common.build_flags} ${esp32.build_flags}
|
||||
-D LEDPIN=16
|
||||
-D RLYPIN=19
|
||||
-D BTNPIN=17
|
||||
@ -361,7 +371,7 @@ board_build.partitions = ${esp32.default_partitions}
|
||||
[env:m5atom]
|
||||
board = esp32dev
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D LEDPIN=27 -D BTNPIN=39
|
||||
build_flags = ${common.build_flags} ${esp32.build_flags} -D LEDPIN=27 -D BTNPIN=39
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
platform = ${esp32.platform}
|
||||
platform_packages = ${esp32.platform_packages}
|
||||
@ -371,14 +381,14 @@ board_build.partitions = ${esp32.default_partitions}
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags_esp8266} -D LEDPIN=3 -D BTNPIN=1
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D LEDPIN=3 -D BTNPIN=1
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:sp511e]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags_esp8266} -D LEDPIN=3 -D BTNPIN=2 -D IRPIN=5 -D WLED_MAX_BUTTONS=3
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D LEDPIN=3 -D BTNPIN=2 -D IRPIN=5 -D WLED_MAX_BUTTONS=3
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:Athom_RGBCW] ;7w and 5w(GU10) bulbs
|
||||
@ -387,7 +397,7 @@ platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,13,5
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,13,5
|
||||
-D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
@ -397,7 +407,7 @@ platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,5,13
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,5,13
|
||||
-D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 -D WLED_USE_IC_CCT
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
@ -407,7 +417,7 @@ platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=0 -D RLYPIN=-1 -D LEDPIN=1 -D WLED_DISABLE_INFRARED
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=0 -D RLYPIN=-1 -D LEDPIN=1 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:Athom_4Pin_Controller] ; With clock and data interface
|
||||
@ -416,7 +426,7 @@ platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=0 -D RLYPIN=12 -D LEDPIN=1 -D WLED_DISABLE_INFRARED
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=0 -D RLYPIN=12 -D LEDPIN=1 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:Athom_5Pin_Controller] ;Analog light strip controller
|
||||
@ -425,7 +435,7 @@ platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=0 -D RLYPIN=-1 DATA_PINS=4,12,14,13 -D WLED_DISABLE_INFRARED
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=0 -D RLYPIN=-1 DATA_PINS=4,12,14,13 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:MY9291]
|
||||
@ -434,7 +444,7 @@ platform = ${common.platform_wled_default}
|
||||
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 USERMOD_MY9291
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_DISABLE_OTA -D USERMOD_MY9291
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
@ -448,7 +458,7 @@ platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:codm-controller-0_6-rev2]
|
||||
@ -457,7 +467,7 @@ platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
@ -468,7 +478,7 @@ board = esp32dev
|
||||
platform = ${esp32.platform}
|
||||
platform_packages = ${esp32.platform_packages}
|
||||
upload_speed = 921600
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED
|
||||
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED
|
||||
-D USERMOD_RTC
|
||||
-D USERMOD_ELEKSTUBE_IPS
|
||||
-D LEDPIN=12
|
||||
|
@ -28,7 +28,7 @@ h11==0.14.0
|
||||
# via
|
||||
# uvicorn
|
||||
# wsproto
|
||||
idna==3.4
|
||||
idna==3.7
|
||||
# via
|
||||
# anyio
|
||||
# requests
|
||||
@ -52,6 +52,8 @@ starlette==0.23.1
|
||||
# via platformio
|
||||
tabulate==0.9.0
|
||||
# via platformio
|
||||
typing-extensions==4.11.0
|
||||
# via starlette
|
||||
urllib3==1.26.18
|
||||
# via requests
|
||||
uvicorn==0.20.0
|
||||
|
7
tools/WLED_ESP32_4MB_256KB_FS.csv
Normal file
@ -0,0 +1,7 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x1D0000,
|
||||
app1, app, ota_1, 0x1E0000,0x1D0000,
|
||||
spiffs, data, spiffs, 0x3B0000,0x40000,
|
||||
coredump, data, coredump,,64K
|
|
6
tools/WLED_ESP32_4MB_700k_FS.csv
Normal file
@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x1A0000,
|
||||
app1, app, ota_1, 0x1B0000,0x1A0000,
|
||||
spiffs, data, spiffs, 0x350000,0xB0000,
|
|
36
usermods/AHT10_v2/README.md
Normal file
@ -0,0 +1,36 @@
|
||||
# Usermod AHT10
|
||||
This Usermod is designed to read a `AHT10`, `AHT15` or `AHT20` sensor and output the following:
|
||||
- Temperature
|
||||
- Humidity
|
||||
|
||||
Configuration is performed via the Usermod menu. The following settings can be configured in the Usermod Menu:
|
||||
- I2CAddress: The i2c address in decimal. Set it to either 56 (0x38, the default) or 57 (0x39).
|
||||
- SensorType, one of:
|
||||
- 0 - AHT10
|
||||
- 1 - AHT15
|
||||
- 2 - AHT20
|
||||
- CheckInterval: Number of seconds between readings
|
||||
- Decimals: Number of decimals to put in the output
|
||||
|
||||
Dependencies, These must be added under `lib_deps` in your `platform.ini` (or `platform_override.ini`).
|
||||
- Libraries
|
||||
- `enjoyneering/AHT10@~1.1.0` (by [enjoyneering](https://registry.platformio.org/libraries/enjoyneering/AHT10))
|
||||
- `Wire`
|
||||
|
||||
## Author
|
||||
[@LordMike](https://github.com/LordMike)
|
||||
|
||||
# Compiling
|
||||
|
||||
To enable, compile with `USERMOD_AHT10` defined (e.g. in `platformio_override.ini`)
|
||||
```ini
|
||||
[env:aht10_example]
|
||||
extends = env:esp32dev
|
||||
build_flags =
|
||||
${common.build_flags} ${esp32.build_flags}
|
||||
-D USERMOD_AHT10
|
||||
; -D USERMOD_AHT10_DEBUG ; -- add a debug status to the info modal
|
||||
lib_deps =
|
||||
${esp32.lib_deps}
|
||||
enjoyneering/AHT10@~1.1.0
|
||||
```
|
9
usermods/AHT10_v2/platformio_override.ini
Normal file
@ -0,0 +1,9 @@
|
||||
[env:aht10_example]
|
||||
extends = env:esp32dev
|
||||
build_flags =
|
||||
${common.build_flags} ${esp32.build_flags}
|
||||
-D USERMOD_AHT10
|
||||
; -D USERMOD_AHT10_DEBUG ; -- add a debug status to the info modal
|
||||
lib_deps =
|
||||
${esp32.lib_deps}
|
||||
enjoyneering/AHT10@~1.1.0
|
327
usermods/AHT10_v2/usermod_aht10.h
Normal file
@ -0,0 +1,327 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
#include <AHT10.h>
|
||||
|
||||
#define AHT10_SUCCESS 1
|
||||
|
||||
class UsermodAHT10 : public Usermod
|
||||
{
|
||||
private:
|
||||
static const char _name[];
|
||||
|
||||
unsigned long _lastLoopCheck = 0;
|
||||
|
||||
bool _settingEnabled : 1; // Enable the usermod
|
||||
bool _mqttPublish : 1; // Publish mqtt values
|
||||
bool _mqttPublishAlways : 1; // Publish always, regardless if there is a change
|
||||
bool _mqttHomeAssistant : 1; // Enable Home Assistant docs
|
||||
bool _initDone : 1; // Initialization is done
|
||||
|
||||
// Settings. Some of these are stored in a different format than they're user settings - so we don't have to convert at runtime
|
||||
uint8_t _i2cAddress = AHT10_ADDRESS_0X38;
|
||||
ASAIR_I2C_SENSOR _ahtType = AHT10_SENSOR;
|
||||
uint16_t _checkInterval = 60000; // milliseconds, user settings is in seconds
|
||||
float _decimalFactor = 100; // a power of 10 factor. 1 would be no change, 10 is one decimal, 100 is two etc. User sees a power of 10 (0, 1, 2, ..)
|
||||
|
||||
uint8_t _lastStatus = 0;
|
||||
float _lastHumidity = 0;
|
||||
float _lastTemperature = 0;
|
||||
|
||||
#ifndef WLED_MQTT_DISABLE
|
||||
float _lastHumiditySent = 0;
|
||||
float _lastTemperatureSent = 0;
|
||||
#endif
|
||||
|
||||
AHT10 *_aht = nullptr;
|
||||
|
||||
float truncateDecimals(float val)
|
||||
{
|
||||
return roundf(val * _decimalFactor) / _decimalFactor;
|
||||
}
|
||||
|
||||
void initializeAht()
|
||||
{
|
||||
if (_aht != nullptr)
|
||||
{
|
||||
delete _aht;
|
||||
}
|
||||
|
||||
_aht = new AHT10(_i2cAddress, _ahtType);
|
||||
|
||||
_lastStatus = 0;
|
||||
_lastHumidity = 0;
|
||||
_lastTemperature = 0;
|
||||
}
|
||||
|
||||
~UsermodAHT10()
|
||||
{
|
||||
delete _aht;
|
||||
_aht = nullptr;
|
||||
}
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
void mqttInitialize()
|
||||
{
|
||||
// This is a generic "setup mqtt" function, So we must abort if we're not to do mqtt
|
||||
if (!WLED_MQTT_CONNECTED || !_mqttPublish || !_mqttHomeAssistant)
|
||||
return;
|
||||
|
||||
char topic[128];
|
||||
snprintf_P(topic, 127, "%s/temperature", mqttDeviceTopic);
|
||||
mqttCreateHassSensor(F("Temperature"), topic, F("temperature"), F("°C"));
|
||||
|
||||
snprintf_P(topic, 127, "%s/humidity", mqttDeviceTopic);
|
||||
mqttCreateHassSensor(F("Humidity"), topic, F("humidity"), F("%"));
|
||||
}
|
||||
|
||||
void mqttPublishIfChanged(const __FlashStringHelper *topic, float &lastState, float state, float minChange)
|
||||
{
|
||||
// Check if MQTT Connected, otherwise it will crash the 8266
|
||||
// Only report if the change is larger than the required diff
|
||||
if (WLED_MQTT_CONNECTED && _mqttPublish && (_mqttPublishAlways || fabsf(lastState - state) > minChange))
|
||||
{
|
||||
char subuf[128];
|
||||
snprintf_P(subuf, 127, PSTR("%s/%s"), mqttDeviceTopic, (const char *)topic);
|
||||
mqtt->publish(subuf, 0, false, String(state).c_str());
|
||||
|
||||
lastState = state;
|
||||
}
|
||||
}
|
||||
|
||||
// Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop.
|
||||
void mqttCreateHassSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement)
|
||||
{
|
||||
String t = String(F("homeassistant/sensor/")) + mqttClientID + "/" + name + F("/config");
|
||||
|
||||
StaticJsonDocument<600> doc;
|
||||
|
||||
doc[F("name")] = name;
|
||||
doc[F("state_topic")] = topic;
|
||||
doc[F("unique_id")] = String(mqttClientID) + name;
|
||||
if (unitOfMeasurement != "")
|
||||
doc[F("unit_of_measurement")] = unitOfMeasurement;
|
||||
if (deviceClass != "")
|
||||
doc[F("device_class")] = deviceClass;
|
||||
doc[F("expire_after")] = 1800;
|
||||
|
||||
JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device
|
||||
device[F("name")] = serverDescription;
|
||||
device[F("identifiers")] = "wled-sensor-" + String(mqttClientID);
|
||||
device[F("manufacturer")] = F(WLED_BRAND);
|
||||
device[F("model")] = F(WLED_PRODUCT_NAME);
|
||||
device[F("sw_version")] = versionString;
|
||||
|
||||
String temp;
|
||||
serializeJson(doc, temp);
|
||||
DEBUG_PRINTLN(t);
|
||||
DEBUG_PRINTLN(temp);
|
||||
|
||||
mqtt->publish(t.c_str(), 0, true, temp.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
public:
|
||||
void setup()
|
||||
{
|
||||
initializeAht();
|
||||
}
|
||||
|
||||
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 (!_settingEnabled || strip.isUpdating())
|
||||
return;
|
||||
|
||||
// do your magic here
|
||||
unsigned long currentTime = millis();
|
||||
|
||||
if (currentTime - _lastLoopCheck < _checkInterval)
|
||||
return;
|
||||
_lastLoopCheck = currentTime;
|
||||
|
||||
_lastStatus = _aht->readRawData();
|
||||
|
||||
if (_lastStatus == AHT10_ERROR)
|
||||
{
|
||||
// Perform softReset and retry
|
||||
DEBUG_PRINTLN(F("AHTxx returned error, doing softReset"));
|
||||
if (!_aht->softReset())
|
||||
{
|
||||
DEBUG_PRINTLN(F("softReset failed"));
|
||||
return;
|
||||
}
|
||||
|
||||
_lastStatus = _aht->readRawData();
|
||||
}
|
||||
|
||||
if (_lastStatus == AHT10_SUCCESS)
|
||||
{
|
||||
float temperature = truncateDecimals(_aht->readTemperature(AHT10_USE_READ_DATA));
|
||||
float humidity = truncateDecimals(_aht->readHumidity(AHT10_USE_READ_DATA));
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
// Push to MQTT
|
||||
|
||||
// We can avoid reporting if the change is insignificant. The threshold chosen is below the level of accuracy, but way above 0.01 which is the precision of the value provided.
|
||||
// The AHT10/15/20 has an accuracy of 0.3C in the temperature readings
|
||||
mqttPublishIfChanged(F("temperature"), _lastTemperatureSent, temperature, 0.1f);
|
||||
|
||||
// The AHT10/15/20 has an accuracy in the humidity sensor of 2%
|
||||
mqttPublishIfChanged(F("humidity"), _lastHumiditySent, humidity, 0.5f);
|
||||
#endif
|
||||
|
||||
// Store
|
||||
_lastTemperature = temperature;
|
||||
_lastHumidity = humidity;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
void onMqttConnect(bool sessionPresent)
|
||||
{
|
||||
mqttInitialize();
|
||||
}
|
||||
#endif
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_AHT10;
|
||||
}
|
||||
|
||||
void addToJsonInfo(JsonObject &root) override
|
||||
{
|
||||
// if "u" object does not exist yet wee need to create it
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull())
|
||||
user = root.createNestedObject("u");
|
||||
|
||||
#ifdef USERMOD_AHT10_DEBUG
|
||||
JsonArray temp = user.createNestedArray(F("AHT last loop"));
|
||||
temp.add(_lastLoopCheck);
|
||||
|
||||
temp = user.createNestedArray(F("AHT last status"));
|
||||
temp.add(_lastStatus);
|
||||
#endif
|
||||
|
||||
JsonArray jsonTemp = user.createNestedArray(F("Temperature"));
|
||||
JsonArray jsonHumidity = user.createNestedArray(F("Humidity"));
|
||||
|
||||
if (_lastLoopCheck == 0)
|
||||
{
|
||||
// Before first run
|
||||
jsonTemp.add(F("Not read yet"));
|
||||
jsonHumidity.add(F("Not read yet"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (_lastStatus != AHT10_SUCCESS)
|
||||
{
|
||||
jsonTemp.add(F("An error occurred"));
|
||||
jsonHumidity.add(F("An error occurred"));
|
||||
return;
|
||||
}
|
||||
|
||||
jsonTemp.add(_lastTemperature);
|
||||
jsonTemp.add(F("°C"));
|
||||
|
||||
jsonHumidity.add(_lastHumidity);
|
||||
jsonHumidity.add(F("%"));
|
||||
}
|
||||
|
||||
void addToConfig(JsonObject &root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name));
|
||||
top[F("Enabled")] = _settingEnabled;
|
||||
top[F("I2CAddress")] = static_cast<uint8_t>(_i2cAddress);
|
||||
top[F("SensorType")] = _ahtType;
|
||||
top[F("CheckInterval")] = _checkInterval / 1000;
|
||||
top[F("Decimals")] = log10f(_decimalFactor);
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
top[F("MqttPublish")] = _mqttPublish;
|
||||
top[F("MqttPublishAlways")] = _mqttPublishAlways;
|
||||
top[F("MqttHomeAssistantDiscovery")] = _mqttHomeAssistant;
|
||||
#endif
|
||||
|
||||
DEBUG_PRINTLN(F("AHT10 config saved."));
|
||||
}
|
||||
|
||||
bool readFromConfig(JsonObject &root) override
|
||||
{
|
||||
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
|
||||
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
|
||||
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
|
||||
bool configComplete = !top.isNull();
|
||||
if (!configComplete)
|
||||
return false;
|
||||
|
||||
bool tmpBool = false;
|
||||
configComplete &= getJsonValue(top[F("Enabled")], tmpBool);
|
||||
if (configComplete)
|
||||
_settingEnabled = tmpBool;
|
||||
|
||||
configComplete &= getJsonValue(top[F("I2CAddress")], _i2cAddress);
|
||||
configComplete &= getJsonValue(top[F("CheckInterval")], _checkInterval);
|
||||
if (configComplete)
|
||||
{
|
||||
if (1 <= _checkInterval && _checkInterval <= 600)
|
||||
_checkInterval *= 1000;
|
||||
else
|
||||
// Invalid input
|
||||
_checkInterval = 60000;
|
||||
}
|
||||
|
||||
configComplete &= getJsonValue(top[F("Decimals")], _decimalFactor);
|
||||
if (configComplete)
|
||||
{
|
||||
if (0 <= _decimalFactor && _decimalFactor <= 5)
|
||||
_decimalFactor = pow10f(_decimalFactor);
|
||||
else
|
||||
// Invalid input
|
||||
_decimalFactor = 100;
|
||||
}
|
||||
|
||||
uint8_t tmpAhtType;
|
||||
configComplete &= getJsonValue(top[F("SensorType")], tmpAhtType);
|
||||
if (configComplete)
|
||||
{
|
||||
if (0 <= tmpAhtType && tmpAhtType <= 2)
|
||||
_ahtType = static_cast<ASAIR_I2C_SENSOR>(tmpAhtType);
|
||||
else
|
||||
// Invalid input
|
||||
_ahtType = ASAIR_I2C_SENSOR::AHT10_SENSOR;
|
||||
}
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
configComplete &= getJsonValue(top[F("MqttPublish")], tmpBool);
|
||||
if (configComplete)
|
||||
_mqttPublish = tmpBool;
|
||||
|
||||
configComplete &= getJsonValue(top[F("MqttPublishAlways")], tmpBool);
|
||||
if (configComplete)
|
||||
_mqttPublishAlways = tmpBool;
|
||||
|
||||
configComplete &= getJsonValue(top[F("MqttHomeAssistantDiscovery")], tmpBool);
|
||||
if (configComplete)
|
||||
_mqttHomeAssistant = tmpBool;
|
||||
#endif
|
||||
|
||||
if (_initDone)
|
||||
{
|
||||
// Reloading config
|
||||
initializeAht();
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
mqttInitialize();
|
||||
#endif
|
||||
}
|
||||
|
||||
_initDone = true;
|
||||
return configComplete;
|
||||
}
|
||||
};
|
||||
|
||||
const char UsermodAHT10::_name[] PROGMEM = "AHTxx";
|
@ -59,7 +59,7 @@ private:
|
||||
bool sensorFound = false;
|
||||
|
||||
// Home Assistant and MQTT
|
||||
String mqttLuminanceTopic = F("");
|
||||
String mqttLuminanceTopic;
|
||||
bool mqttInitialized = false;
|
||||
bool HomeAssistantDiscovery = true; // Publish Home Assistant Discovery messages
|
||||
|
||||
|
@ -7,6 +7,7 @@ This Usermod is designed to read a `BME280` or `BMP280` sensor and output the fo
|
||||
- Dew Point (`BME280` only)
|
||||
|
||||
Configuration is performed via the Usermod menu. There are no parameters to set in code! The following settings can be configured in the Usermod Menu:
|
||||
- The i2c address in decimal. Set it to either 118 (0x76, the default) or 119 (0x77).
|
||||
- Temperature Decimals (number of decimal places to output)
|
||||
- Humidity Decimals
|
||||
- Pressure Decimals
|
||||
|
@ -24,6 +24,7 @@ private:
|
||||
uint8_t PressureDecimals = 0; // Number of decimal places in published pressure values
|
||||
uint16_t TemperatureInterval = 5; // Interval to measure temperature (and humidity, dew point if available) in seconds
|
||||
uint16_t PressureInterval = 300; // Interval to measure pressure in seconds
|
||||
BME280I2C::I2CAddr I2CAddress = BME280I2C::I2CAddr_0x76; // i2c address, defaults to 0x76
|
||||
bool PublishAlways = false; // Publish values even when they have not changed
|
||||
bool UseCelsius = true; // Use Celsius for Reporting
|
||||
bool HomeAssistantDiscovery = false; // Publish Home Assistant Device Information
|
||||
@ -35,20 +36,7 @@ private:
|
||||
#endif
|
||||
bool initDone = false;
|
||||
|
||||
// BME280 sensor settings
|
||||
BME280I2C::Settings settings{
|
||||
BME280::OSR_X16, // Temperature oversampling x16
|
||||
BME280::OSR_X16, // Humidity oversampling x16
|
||||
BME280::OSR_X16, // Pressure oversampling x16
|
||||
// Defaults
|
||||
BME280::Mode_Forced,
|
||||
BME280::StandbyTime_1000ms,
|
||||
BME280::Filter_Off,
|
||||
BME280::SpiEnable_False,
|
||||
BME280I2C::I2CAddr_0x76 // I2C address. I2C specific. Default 0x76
|
||||
};
|
||||
|
||||
BME280I2C bme{settings};
|
||||
BME280I2C bme;
|
||||
|
||||
uint8_t sensorType;
|
||||
|
||||
@ -181,34 +169,52 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void initializeBmeComms()
|
||||
{
|
||||
BME280I2C::Settings settings{
|
||||
BME280::OSR_X16, // Temperature oversampling x16
|
||||
BME280::OSR_X16, // Humidity oversampling x16
|
||||
BME280::OSR_X16, // Pressure oversampling x16
|
||||
BME280::Mode_Forced,
|
||||
BME280::StandbyTime_1000ms,
|
||||
BME280::Filter_Off,
|
||||
BME280::SpiEnable_False,
|
||||
I2CAddress
|
||||
};
|
||||
|
||||
bme.setSettings(settings);
|
||||
|
||||
if (!bme.begin())
|
||||
{
|
||||
sensorType = 0;
|
||||
DEBUG_PRINTLN(F("Could not find BME280 I2C sensor!"));
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (bme.chipModel())
|
||||
{
|
||||
case BME280::ChipModel_BME280:
|
||||
sensorType = 1;
|
||||
DEBUG_PRINTLN(F("Found BME280 sensor! Success."));
|
||||
break;
|
||||
case BME280::ChipModel_BMP280:
|
||||
sensorType = 2;
|
||||
DEBUG_PRINTLN(F("Found BMP280 sensor! No Humidity available."));
|
||||
break;
|
||||
default:
|
||||
sensorType = 0;
|
||||
DEBUG_PRINTLN(F("Found UNKNOWN sensor! Error!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void setup()
|
||||
{
|
||||
if (i2c_scl<0 || i2c_sda<0) { enabled = false; sensorType = 0; return; }
|
||||
|
||||
if (!bme.begin())
|
||||
{
|
||||
sensorType = 0;
|
||||
DEBUG_PRINTLN(F("Could not find BME280 I2C sensor!"));
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (bme.chipModel())
|
||||
{
|
||||
case BME280::ChipModel_BME280:
|
||||
sensorType = 1;
|
||||
DEBUG_PRINTLN(F("Found BME280 sensor! Success."));
|
||||
break;
|
||||
case BME280::ChipModel_BMP280:
|
||||
sensorType = 2;
|
||||
DEBUG_PRINTLN(F("Found BMP280 sensor! No Humidity available."));
|
||||
break;
|
||||
default:
|
||||
sensorType = 0;
|
||||
DEBUG_PRINTLN(F("Found UNKNOWN sensor! Error!"));
|
||||
}
|
||||
}
|
||||
initDone=true;
|
||||
initializeBmeComms();
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
void loop()
|
||||
@ -365,12 +371,11 @@ public:
|
||||
}
|
||||
else if (sensorType==2) //BMP280
|
||||
{
|
||||
|
||||
JsonArray temperature_json = user.createNestedArray(F("Temperature"));
|
||||
JsonArray pressure_json = user.createNestedArray(F("Pressure"));
|
||||
temperature_json.add(roundf(sensorTemperature * powf(10, TemperatureDecimals)));
|
||||
temperature_json.add(roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
|
||||
temperature_json.add(tempScale);
|
||||
pressure_json.add(roundf(sensorPressure * powf(10, PressureDecimals)));
|
||||
pressure_json.add(roundf(sensorPressure * powf(10, PressureDecimals)) / powf(10, PressureDecimals));
|
||||
pressure_json.add(F("hPa"));
|
||||
}
|
||||
else if (sensorType==1) //BME280
|
||||
@ -382,9 +387,9 @@ public:
|
||||
JsonArray dewpoint_json = user.createNestedArray(F("Dew Point"));
|
||||
temperature_json.add(roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
|
||||
temperature_json.add(tempScale);
|
||||
humidity_json.add(roundf(sensorHumidity * powf(10, HumidityDecimals)));
|
||||
humidity_json.add(roundf(sensorHumidity * powf(10, HumidityDecimals)) / powf(10, HumidityDecimals));
|
||||
humidity_json.add(F("%"));
|
||||
pressure_json.add(roundf(sensorPressure * powf(10, PressureDecimals)));
|
||||
pressure_json.add(roundf(sensorPressure * powf(10, PressureDecimals)) / powf(10, PressureDecimals));
|
||||
pressure_json.add(F("hPa"));
|
||||
heatindex_json.add(roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
|
||||
heatindex_json.add(tempScale);
|
||||
@ -399,6 +404,7 @@ public:
|
||||
{
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name));
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[F("I2CAddress")] = static_cast<uint8_t>(I2CAddress);
|
||||
top[F("TemperatureDecimals")] = TemperatureDecimals;
|
||||
top[F("HumidityDecimals")] = HumidityDecimals;
|
||||
top[F("PressureDecimals")] = PressureDecimals;
|
||||
@ -426,6 +432,10 @@ public:
|
||||
|
||||
configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled);
|
||||
// A 3-argument getJsonValue() assigns the 3rd argument as a default value if the Json value is missing
|
||||
uint8_t tmpI2cAddress;
|
||||
configComplete &= getJsonValue(top[F("I2CAddress")], tmpI2cAddress, 0x76);
|
||||
I2CAddress = static_cast<BME280I2C::I2CAddr>(tmpI2cAddress);
|
||||
|
||||
configComplete &= getJsonValue(top[F("TemperatureDecimals")], TemperatureDecimals, 1);
|
||||
configComplete &= getJsonValue(top[F("HumidityDecimals")], HumidityDecimals, 0);
|
||||
configComplete &= getJsonValue(top[F("PressureDecimals")], PressureDecimals, 0);
|
||||
@ -440,8 +450,23 @@ public:
|
||||
// first run: reading from cfg.json
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
// changing parameters from settings page
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
|
||||
// Reset all known values
|
||||
sensorType = 0;
|
||||
sensorTemperature = 0;
|
||||
sensorHumidity = 0;
|
||||
sensorHeatIndex = 0;
|
||||
sensorDewPoint = 0;
|
||||
sensorPressure = 0;
|
||||
lastTemperature = 0;
|
||||
lastHumidity = 0;
|
||||
lastHeatIndex = 0;
|
||||
lastDewPoint = 0;
|
||||
lastPressure = 0;
|
||||
|
||||
initializeBmeComms();
|
||||
}
|
||||
|
||||
return configComplete;
|
||||
|
BIN
usermods/BME68X_v2/BME680.pdf
Normal file
152
usermods/BME68X_v2/README.md
Normal file
@ -0,0 +1,152 @@
|
||||
# Usermod BME68X
|
||||
This usermod was developed for a BME680/BME68X sensor. The BME68X is not compatible with the BME280/BMP280 chip. It has its own library. The original 'BSEC Software Library' from Bosch was used to develop the code. The measured values are displayed on the WLED info page.
|
||||
|
||||
<p align="center"><img src="pics/pic1.png" style="width:60%;"></p>
|
||||
|
||||
In addition, the values are published on MQTT if this is active. The topic used for this is: 'wled/[MQTT Client ID]'. The Client ID is set in the WLED MQTT settings.
|
||||
<p align="center"><img src="pics/pic2.png"></p>
|
||||
|
||||
If you use HomeAssistance discovery, the device tree for HomeAssistance is created. This is published under the topic 'homeassistant/sensor/[MQTT Client ID]' via MQTT.
|
||||
<p align="center"><img src="pics/pic3.png"></p>
|
||||
|
||||
A device with the following sensors appears in HomeAssistant. Please note that MQTT must be activated in HomeAssistant.
|
||||
<p align="center"><img src="pics/pic4.png" style="width:60%;"></p>
|
||||
|
||||
|
||||
## Features
|
||||
Raw sensor types
|
||||
|
||||
Sensor Accuracy Scale Range
|
||||
--------------------------------------------------------------------------------------------------
|
||||
Temperature +/- 1.0 °C/°F -40 to 85 °C
|
||||
Humidity +/- 3 % 0 to 100 %
|
||||
Pressure +/- 1 hPa 300 to 1100 hPa
|
||||
Gas Resistance Ohm
|
||||
|
||||
The BSEC Library calculates the following values via the gas resistance
|
||||
|
||||
Sensor Accuracy Scale Range
|
||||
--------------------------------------------------------------------------------------------------
|
||||
IAQ value between 0 and 500
|
||||
Static IAQ same as IAQ but for permanently installed devices
|
||||
CO2 PPM
|
||||
VOC PPM
|
||||
Gas-Percentage %
|
||||
|
||||
|
||||
In addition the usermod calculates
|
||||
|
||||
Sensor Accuracy Scale Range
|
||||
--------------------------------------------------------------------------------------------------
|
||||
Absolute humidity g/m³
|
||||
Dew point °C/°F
|
||||
|
||||
### IAQ (Indoor Air Quality)
|
||||
The IAQ is divided into the following value groups.
|
||||
<p align="center"><img src="pics/pic5.png"></p>
|
||||
|
||||
For more detailed information, please consult the enclosed Bosch product description (BME680.pdf).
|
||||
|
||||
|
||||
## Calibration of the device
|
||||
|
||||
The gas sensor of the BME68X must be calibrated. This differs from the BME280, which does not require any calibration.
|
||||
There is a range of additional information for this, which the driver also provides. These values can be found in HomeAssistant under Diagnostics.
|
||||
|
||||
- **STABILIZATION_STATUS**: Gas sensor stabilization status [boolean] Indicates initial stabilization status of the gas sensor element: stabilization is ongoing (0) or stabilization is finished (1).
|
||||
- **RUN_IN_STATUS**: Gas sensor run-in status [boolean] Indicates power-on stabilization status of the gas sensor element: stabilization is ongoing (0) or stabilization is finished (1)
|
||||
|
||||
Furthermore, all GAS based values have their own accuracy value. These have the following meaning:
|
||||
|
||||
- **Accuracy = 0** means the sensor is being stabilized (this can take a while on the first run)
|
||||
- **Accuracy = 1** means that the previous measured values show too few differences and cannot be used for calibration. If the sensor is at accuracy 1 for too long, you must ensure that the ambient air is chaning. Opening the windows is fine. Or sometimes it is sufficient to breathe on the sensor for approx. 5 minutes.
|
||||
- **Accuracy = 2** means the sensor is currently calibrating.
|
||||
- **Accuracy = 3** means that the sensor has been successfully calibrated. Once accuracy 3 is reached, the calibration data is automatically written to the file system. This calibration data will be used again at the next start and will speed up the calibration.
|
||||
|
||||
The IAQ index is therefore only meaningful if IAQ Accuracy = 3. In addition to the value for IAQ, BSEC also provides us with CO2 and VOC equivalent values. When using the sensor, the calibration value should also always be read out and displayed or transmitted.
|
||||
|
||||
Reasonably reliable values are therefore only achieved when accuracy displays the value 3.
|
||||
|
||||
|
||||
|
||||
## Settings
|
||||
The settings of the usermods are set in the usermod section of wled.
|
||||
<p align="center"><img src="pics/pic6.png"></p>
|
||||
|
||||
The possible settings are
|
||||
|
||||
- **Enable:** Enables / disables the usermod
|
||||
- **I2C address:** I2C address of the sensor. You can choose between 0X77 & 0X76. The default is 0x77.
|
||||
- **Interval:** Specifies the interval of seconds at which the usermod should be executed. The default is every second.
|
||||
- **Pub Chages Only:** If this item is active, the values are only published if they have changed since the last publication.
|
||||
- **Pub Accuracy:** The Accuracy values associated with the gas values are also published.
|
||||
- **Pub Calib State:** If this item is active, STABILIZATION_STATUS& RUN_IN_STATUS are also published.
|
||||
- **Temp Scale:** Here you can choose between °C and °F.
|
||||
- **Temp Offset:** The temperature offset is always set in °C. It must be converted for Fahrenheit.
|
||||
- **HA Discovery:** If this item is active, the HomeAssistant sensor tree is created.
|
||||
- **Pause While WLED Active:** If WLED has many LEDs to calculate, the computing power may no longer be sufficient to calculate the LEDs and read the sensor data. The LEDs then hang for a few microseconds, which can be seen. If this point is active, no sensor data is fetched as long as WLED is running.
|
||||
- **Del Calibration Hist:** If a check mark is set here, the calibration file saved in the file system is deleted when the settings are saved.
|
||||
|
||||
### Sensors
|
||||
Applies to all sensors. The number of decimal places is set here. If the sensor is set to -1, it will no longer be published. In addition, the IAQ values can be activated here in verbal form.
|
||||
|
||||
It is recommended to use the Static IAQ for the IAQ values. This is recommended by Bosch for statically placed devices.
|
||||
|
||||
## Output
|
||||
|
||||
Data is published over MQTT - make sure you've enabled the MQTT sync interface.
|
||||
|
||||
In addition to outputting via MQTT, you can read the values from the Info Screen on the dashboard page of the device's web interface.
|
||||
|
||||
Methods also exist to read the read/calculated values from other WLED modules through code.
|
||||
- getTemperature(); The scale °C/°F is depended to the settings
|
||||
- getHumidity();
|
||||
- getPressure();
|
||||
- getGasResistance();
|
||||
- getAbsoluteHumidity();
|
||||
- getDewPoint(); The scale °C/°F is depended to the settings
|
||||
- getIaq();
|
||||
- getStaticIaq();
|
||||
- getCo2();
|
||||
- getVoc();
|
||||
- getGasPerc();
|
||||
- getIaqAccuracy();
|
||||
- getStaticIaqAccuracy();
|
||||
- getCo2Accuracy();
|
||||
- getVocAccuracy();
|
||||
- getGasPercAccuracy();
|
||||
- getStabStatus();
|
||||
- getRunInStatus();
|
||||
|
||||
|
||||
## Compiling
|
||||
|
||||
To enable, compile with `USERMOD_BME68X` defined (e.g. in `platformio_override.ini`) and add the `BSEC Software Library` to the lib_deps.
|
||||
|
||||
```
|
||||
[env:esp32-BME680]
|
||||
board = esp32dev
|
||||
platform = ${esp32.platform}
|
||||
platform_packages = ${esp32.platform_packages}
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
boschsensortec/BSEC Software Library @ ^1.8.1492 ; USERMOD: BME680
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32}
|
||||
-D USERMOD_BME68X ; USERMOD: BME680
|
||||
```
|
||||
|
||||
## Revision History
|
||||
### Version 1.0.0
|
||||
- First version of the BME68X_v user module
|
||||
### Version 1.0.1
|
||||
- Rebased to WELD Version 0.15
|
||||
- Reworked some default settings
|
||||
- A problem with the default settings has been fixed
|
||||
|
||||
## Known problems
|
||||
- MQTT goes online at device start. Shortly afterwards it goes offline and takes quite a while until it goes online again. The problem does not come from this user module, but from the WLED core.
|
||||
- If you save the settings often, WLED can get stuck.
|
||||
- If many LEDS are connected to WLED, reading the sensor can cause a small but noticeable hang. The "Pause While WLED Active" option was introduced as a workaround.
|
||||
|
||||
<div><img src="pics/GeoGab.svg" width="20%"/> </div>
|
||||
Gabriel Sieben (gsieben@geogab.net)
|
76
usermods/BME68X_v2/pics/GeoGab.svg
Normal file
@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xml:space="preserve"
|
||||
style="enable-background:new 0 0 595.28 127.56;"
|
||||
viewBox="0 0 600 135"
|
||||
y="0px"
|
||||
x="0px"
|
||||
id="Layer_1"
|
||||
version="1.1"><metadata
|
||||
id="metadata2372"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs2370"><linearGradient
|
||||
osb:paint="solid"
|
||||
id="linearGradient3877"><stop
|
||||
id="stop3875"
|
||||
offset="0"
|
||||
style="stop-color:#808285;stop-opacity:1;" /></linearGradient><clipPath
|
||||
id="clipPath2379"
|
||||
clipPathUnits="userSpaceOnUse"><g
|
||||
style="fill:#808285;fill-opacity:1;fill-rule:nonzero;stroke:#230065;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.99607843"
|
||||
id="use2381"><path
|
||||
style="fill:#808285;fill-opacity:1;fill-rule:nonzero;stroke:#230065;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.99607843"
|
||||
class="st0"
|
||||
d="m 588.34,94.29 c 0,13.28 -10.77,24.04 -24.04,24.04 H 31.38 C 18.1,118.33 7.34,107.57 7.34,94.29 V 32.38 C 7.34,19.1 18.1,8.34 31.38,8.34 h 532.91 c 13.28,0 24.04,10.76 24.04,24.04 V 94.29 Z M 159.44,69.66 c 0,17.15 11.68,19.07 21.89,19.07 h 14.23 c 14.6,0 23.44,-3.2 23.44,-13.87 v -0.91 h -7.84 c -0.45,7.11 -4.47,8.39 -14.32,8.39 h -10.76 c -13.68,0 -18.79,-2.74 -18.79,-13.32 V 66.65 H 219 V 60.9 C 219,44.3 208.88,40.74 194.83,40.74 h -12.59 c -11.12,0 -22.8,1.92 -22.8,18.79 z m 51.72,-9.95 h -43.87 c 0.73,-10.58 3.65,-12.59 16.05,-12.59 h 11.49 c 12.77,0 16.33,4.74 16.33,9.49 z m 18.62,10.13 c 0,15.33 10.86,18.88 25.73,18.88 h 11.22 c 14.87,0 25.72,-3.56 25.72,-18.88 V 59.62 c 0,-15.32 -10.85,-18.88 -25.72,-18.88 h -11.22 c -14.87,0 -25.73,3.56 -25.73,18.88 z m 54.82,-0.46 c 0,10.86 -6.38,12.95 -15.51,12.95 h -15.96 c -9.12,0 -15.51,-2.09 -15.51,-12.95 v -9.31 c 0,-10.85 6.39,-12.95 15.51,-12.95 h 15.96 c 9.12,0 15.51,2.1 15.51,12.95 z m 173.79,18.61 v -31.2 c 0,-13.87 -9.85,-16.06 -24.45,-16.06 h -12.41 c -14.05,0 -19.43,3.47 -19.43,11.95 v 2.28 h 7.84 v -1.64 c 0,-3.83 1.92,-6.2 11.77,-6.2 h 11.49 c 12.04,0 17.33,1.09 17.33,9.49 v 8.76 h -0.18 c -2.74,-4.47 -6.39,-5.56 -16.6,-5.56 H 421.16 C 407.48,59.81 400,61.09 400,71.67 v 3.1 c 0,8.76 3.1,13.96 14.96,13.96 h 18.79 z m -7.84,-12.5 c 0,5.84 -5.47,6.84 -19.7,6.84 h -10.4 c -10.76,0 -12.59,-2.19 -12.59,-7.39 v -1.46 c 0,-5.84 2.83,-7.3 12.95,-7.3 h 12.04 c 12.04,0 17.7,0.82 17.7,7.2 z M 73.6,66.69 h 10.88 l 5.03,0.01 h 48.67 v 2.41 c 0,9.12 -5.47,12.23 -14.41,12.23 H 88.83 c -8.58,0 -15.51,-2.55 -15.51,-14.05 v -0.35 -0.26 z m -9.16,-0.01 c 0,0.06 0,0.14 0,0.2 0.67,14.68 6.68,21.76 23.47,21.76 h 36.85 c 16.51,0 22.35,-6.39 22.35,-24.36 V 59.4 H 89.51 L 85.39,59.39 H 73.32 v -0.01 -16.9 c 0,-11.49 6.93,-14.05 15.51,-14.05 H 112 c 22.07,0 25.81,0.91 25.81,13.23 h 8.39 v -2.37 c 0,-15.23 -12.68,-18.15 -24.54,-18.15 H 87.92 c -17.88,0 -23.53,8.03 -23.53,24.72 V 59.39 L 64.38,59.4 H 44.6 c -11.86,0 -24.54,2.92 -24.54,18.15 v 2.37 5.02 2.37 c 0,15.24 12.68,18.16 24.54,18.16 h 40.79 432.59 34.03 c 17.88,0 23.54,-8.03 23.54,-24.72 V 45.88 c 0,-16.69 -5.65,-24.72 -23.54,-24.72 l -0.16,-0.02 h -223 c -17.88,0 -23.53,8.03 -23.53,24.72 v 18.06 c 0,16.69 5.65,24.72 23.53,24.72 h 36.85 c 16.51,0 22.35,-6.39 22.35,-24.36 V 59.4 h -42.78 v 7.3 h 33.84 v 2.41 c 0,9.12 -5.47,12.23 -14.41,12.23 h -34.94 c -8.58,0 -15.5,-2.55 -15.5,-14.05 V 42.48 c 0,-11.49 6.93,-14.05 15.5,-14.05 l 142.5,0.02 v 61.24 l 24.35,0.73 h 10.95 c 14.23,0 24.18,-3.56 24.18,-19.61 V 62.6 c 0,-14.87 -5.75,-20.16 -24.63,-20.16 h -11.95 -14.88 -0.17 l -0.02,-14.01 h 71.75 l -0.75,0.02 c 8.58,0 15.51,2.55 15.51,14.04 v 41.62 c 0,11.49 -6.93,14.05 -15.51,14.05 H 518.97 86.87 54.27 c -22.08,0 -25.81,-0.91 -25.81,-13.23 v -5.02 c 0,-12.31 3.74,-13.23 25.81,-13.23 h 9.84 z m 459.45,4.04 c 0,9.03 -2.83,13.32 -15.6,13.32 h -12.77 c -11.67,0 -15.42,-4.93 -15.42,-13.41 v -8.85 c 0,-11.13 6.48,-12.95 17.06,-12.95 h 10.58 c 10.76,0 16.14,2.01 16.14,12.77 v 9.12 z"
|
||||
id="path3935" /><path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#230065;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.99607843"
|
||||
class="st0"
|
||||
d="m 611.07375,-48.387188 c 0,13.28 -10.77,24.039999 -24.04,24.039999 H 54.113747 c -13.27999,0 -24.04,-10.76 -24.04,-24.039999 v -61.910002 c 0,-13.28 10.76001,-24.04 24.04,-24.04 H 587.02375 c 13.28,0 24.04,10.76 24.04,24.04 v 61.910002 z"
|
||||
id="path3937" /></g></clipPath><clipPath
|
||||
id="clipPath2398"
|
||||
clipPathUnits="userSpaceOnUse"><g
|
||||
id="use2400"
|
||||
style="fill:#268298;fill-opacity:1"><g
|
||||
id="g3959"
|
||||
clip-path="url(#clipPath2407)"
|
||||
style="fill:#268298;fill-opacity:1"><g
|
||||
id="g3957"
|
||||
style="fill:#268298;fill-opacity:1"><rect
|
||||
style="opacity:1;fill:#268298;fill-opacity:1;stroke:#000000;stroke-opacity:1"
|
||||
id="rect3955"
|
||||
width="350.98587"
|
||||
height="147.57361"
|
||||
x="95.224861"
|
||||
y="-97.290329" /></g></g></g></clipPath><clipPath
|
||||
id="clipPath2407"
|
||||
clipPathUnits="userSpaceOnUse"><g
|
||||
id="use2409"
|
||||
style="fill:#268298;fill-opacity:1"><rect
|
||||
style="opacity:1;fill:#268298;fill-opacity:1;stroke:#000000;stroke-opacity:1"
|
||||
id="rect3963"
|
||||
width="350.98587"
|
||||
height="147.57361"
|
||||
x="95.224861"
|
||||
y="-97.290329" /></g></clipPath></defs>
|
||||
<style
|
||||
id="style2363"
|
||||
type="text/css">
|
||||
.st0{fill:#808285;}
|
||||
</style>
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.9859813;fill-rule:nonzero;stroke:#5c7823;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.99607843"
|
||||
class="st0"
|
||||
d="M 600,103.51782 C 600,116.79902 588.8778,127.56 575.17384,127.56 H 24.826163 C 11.111876,127.56 5.1635108e-7,116.79902 5.1635108e-7,103.51782 V 41.602186 C 5.1635108e-7,28.320979 11.111876,17.560001 24.826163,17.560001 H 575.16351 c 13.71429,0 24.82616,10.760978 24.82616,24.042185 v 61.915634 z"
|
||||
id="path2365-0" /><path
|
||||
style="fill:#808285;fill-opacity:1;fill-rule:nonzero;stroke:#230065;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.99607843"
|
||||
class="st0"
|
||||
d="M 600,103.51782 C 600,116.79902 588.8778,127.56 575.17384,127.56 H 24.826162 C 11.111877,127.56 5.1635111e-7,116.79902 5.1635111e-7,103.51782 V 41.602187 C 5.1635111e-7,28.320977 11.111876,17.559997 24.826162,17.559997 H 575.16351 c 13.71429,0 24.82616,10.76098 24.82616,24.04219 V 103.51782 Z M 157.07401,78.885567 c 0,17.15156 12.06196,19.07173 22.60585,19.07173 h 14.69535 c 15.07745,0 24.20654,-3.20029 24.20654,-13.87125 v -0.91009 h -8.09638 c -0.46472,7.11065 -4.61618,8.39076 -14.7883,8.39076 h -11.11188 c -14.12736,0 -19.40447,-2.74024 -19.40447,-13.3212 v -2.37022 h 53.40103 v -5.75052 c 0,-16.60151 -10.45094,-20.16183 -24.96041,-20.16183 h -13.00172 c -11.48365,0 -23.54561,1.92017 -23.54561,18.79171 z m 53.41136,-9.9509 h -45.30465 c 0.75387,-10.58096 3.76936,-12.59114 16.57487,-12.59114 h 11.86575 c 13.18761,0 16.86403,4.74043 16.86403,9.49086 z m 19.22891,10.13092 c 0,15.33139 11.21515,18.88171 26.57143,18.88171 h 11.58692 c 15.35629,0 26.56111,-3.56032 26.56111,-18.88171 v -10.22093 c 0,-15.32139 -11.20482,-18.88171 -26.56111,-18.88171 h -11.58692 c -15.35628,0 -26.57143,3.56032 -26.57143,18.88171 z m 56.61275,-0.46004 c 0,10.86098 -6.58865,12.95117 -16.01722,12.95117 h -16.48193 c -9.41825,0 -16.01721,-2.09019 -16.01721,-12.95117 v -9.31085 c 0,-10.85098 6.59896,-12.95117 16.01721,-12.95117 h 16.48193 c 9.41824,0 16.01722,2.10019 16.01722,12.95117 z m 179.47332,18.61169 v -31.20283 c 0,-13.87126 -10.17212,-16.06146 -25.24957,-16.06146 h -12.81584 c -14.50946,0 -20.0654,3.47031 -20.0654,11.95108 v 2.28021 h 8.09638 v -1.64015 c 0,-3.83035 1.98279,-6.20056 12.15491,-6.20056 h 11.86575 c 12.43373,0 17.89673,1.0901 17.89673,9.49086 v 8.7608 h -0.18589 c -2.8296,-4.47041 -6.59897,-5.56051 -17.14286,-5.56051 h -13.00172 c -14.12736,0 -21.85198,1.28012 -21.85198,11.86108 v 3.10028 c 0,8.76079 3.20138,13.96126 15.44923,13.96126 h 19.40447 z m -8.09639,-12.50114 c 0,5.84053 -5.64888,6.84062 -20.34423,6.84062 h -10.74011 c -11.11187,0 -13.00172,-2.19019 -13.00172,-7.39067 v -1.46013 c 0,-5.84053 2.92255,-7.30066 13.3735,-7.30066 h 12.43373 c 12.43374,0 18.27883,0.82007 18.27883,7.20065 z m -389.27711,-8.8008 h 11.2358 l 5.194492,0.01 h 50.261618 v 2.41021 c 0,9.12083 -5.64888,12.23111 -14.88124,12.23111 H 84.154905 c -8.860585,0 -16.017212,-2.55023 -16.017212,-14.05127 v -0.35003 -0.26003 z m -9.459552,-0.01 c 0,0.06 0,0.14002 0,0.20002 0.69191,14.68133 6.898451,21.76198 24.237521,21.76198 h 38.055071 c 17.04992,0 23.0809,-6.39058 23.0809,-24.36221 v -4.88045 H 84.857142 l -4.254733,-0.01 H 68.137693 v -0.01 -16.90153 c 0,-11.49104 7.156627,-14.05129 16.017212,-14.05129 h 23.927705 c 22.79174,0 26.65405,0.91009 26.65405,13.23121 h 8.66437 v -2.37021 c 0,-15.23139 -13.09466,-18.15166 -25.34251,-18.15166 H 83.215146 c -18.464717,0 -24.299483,8.03073 -24.299483,24.72226 v 13.53122 l -0.01033,0.01 h -20.42685 c -12.247849,0 -25.342513,2.92027 -25.342513,18.15165 v 2.37022 5.02045 2.37022 c 0,15.241393 13.094664,18.161653 25.342513,18.161653 h 42.123923 446.736664 35.14286 c 18.46471,0 24.30981,-8.03073 24.30981,-24.722253 v -34.87316 c 0,-16.69153 -5.83477,-24.72226 -24.30981,-24.72226 l -0.16523,-0.02 H 332.0241 c -18.46471,0 -24.29948,8.03073 -24.29948,24.72226 v 18.06163 c 0,16.69152 5.83477,24.72225 24.29948,24.72225 h 38.05508 c 17.04991,0 23.08089,-6.39058 23.08089,-24.36221 v -4.88045 h -44.179 v 7.30067 h 34.94664 v 2.41021 c 0,9.12083 -5.64888,12.23111 -14.88123,12.23111 h -36.08262 c -8.86059,0 -16.00688,-2.55023 -16.00688,-14.05127 v -24.81225 c 0,-11.49104 7.15662,-14.05129 16.00688,-14.05129 l 147.16007,0.02 v 61.24556 l 25.14629,0.73007 h 11.30809 c 14.69536,0 24.97074,-3.56033 24.97074,-19.61178 v -8.21075 c 0,-14.87135 -5.93803,-20.16183 -25.43545,-20.16183 h -12.34079 -15.36661 -0.17556 l -0.0207,-14.01128 h 74.09639 l -0.77453,0.02 c 8.86059,0 16.01721,2.55024 16.01721,14.04128 v 41.62378 c 0,11.491053 -7.15662,14.051283 -16.01721,14.051283 H 528.36145 82.130808 48.464716 c -22.802065,0 -26.654044,-0.91008 -26.654044,-13.231213 v -5.02046 c 0,-12.31111 3.862306,-13.2312 26.654044,-13.2312 h 10.161789 z m 474.475042,4.04037 c 0,9.03082 -2.92255,13.32121 -16.11015,13.32121 h -13.18761 c -12.05164,0 -15.92427,-4.93045 -15.92427,-13.41122 v -8.8508 c 0,-11.13101 6.69191,-12.95118 17.6179,-12.95118 h 10.92599 c 11.11188,0 16.66781,2.01019 16.66781,12.77116 v 9.12083 z"
|
||||
id="path2365" />
|
||||
</svg>
|
After Width: | Height: | Size: 11 KiB |
BIN
usermods/BME68X_v2/pics/pic1.png
Normal file
After Width: | Height: | Size: 135 KiB |
BIN
usermods/BME68X_v2/pics/pic2.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
usermods/BME68X_v2/pics/pic3.png
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
usermods/BME68X_v2/pics/pic4.png
Normal file
After Width: | Height: | Size: 134 KiB |
BIN
usermods/BME68X_v2/pics/pic5.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
usermods/BME68X_v2/pics/pic6.png
Normal file
After Width: | Height: | Size: 35 KiB |
1114
usermods/BME68X_v2/usermod_bme68x.h
Normal file
160
usermods/Battery/UMBattery.h
Normal file
@ -0,0 +1,160 @@
|
||||
#ifndef UMBBattery_h
|
||||
#define UMBBattery_h
|
||||
|
||||
#include "battery_defaults.h"
|
||||
|
||||
/**
|
||||
* Battery base class
|
||||
* all other battery classes should inherit from this
|
||||
*/
|
||||
class UMBattery
|
||||
{
|
||||
private:
|
||||
|
||||
protected:
|
||||
float minVoltage;
|
||||
float maxVoltage;
|
||||
float voltage;
|
||||
int8_t level = 100;
|
||||
float calibration; // offset or calibration value to fine tune the calculated voltage
|
||||
float voltageMultiplier; // ratio for the voltage divider
|
||||
|
||||
float linearMapping(float v, float min, float max, float oMin = 0.0f, float oMax = 100.0f)
|
||||
{
|
||||
return (v-min) * (oMax-oMin) / (max-min) + oMin;
|
||||
}
|
||||
|
||||
public:
|
||||
UMBattery()
|
||||
{
|
||||
this->setVoltageMultiplier(USERMOD_BATTERY_VOLTAGE_MULTIPLIER);
|
||||
this->setCalibration(USERMOD_BATTERY_CALIBRATION);
|
||||
}
|
||||
|
||||
virtual void update(batteryConfig cfg)
|
||||
{
|
||||
if(cfg.minVoltage) this->setMinVoltage(cfg.minVoltage);
|
||||
if(cfg.maxVoltage) this->setMaxVoltage(cfg.maxVoltage);
|
||||
if(cfg.level) this->setLevel(cfg.level);
|
||||
if(cfg.calibration) this->setCalibration(cfg.calibration);
|
||||
if(cfg.voltageMultiplier) this->setVoltageMultiplier(cfg.voltageMultiplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Corresponding battery curves
|
||||
* calculates the level in % (0-100) with given voltage and possible voltage range
|
||||
*/
|
||||
virtual float mapVoltage(float v, float min, float max) = 0;
|
||||
// {
|
||||
// example implementation, linear mapping
|
||||
// return (v-min) * 100 / (max-min);
|
||||
// };
|
||||
|
||||
virtual void calculateAndSetLevel(float voltage) = 0;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Getter and Setter
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Get lowest configured battery voltage
|
||||
*/
|
||||
virtual float getMinVoltage()
|
||||
{
|
||||
return this->minVoltage;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set lowest battery voltage
|
||||
* can't be below 0 volt
|
||||
*/
|
||||
virtual void setMinVoltage(float voltage)
|
||||
{
|
||||
this->minVoltage = max(0.0f, voltage);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get highest configured battery voltage
|
||||
*/
|
||||
virtual float getMaxVoltage()
|
||||
{
|
||||
return this->maxVoltage;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set highest battery voltage
|
||||
* can't be below minVoltage
|
||||
*/
|
||||
virtual void setMaxVoltage(float voltage)
|
||||
{
|
||||
this->maxVoltage = max(getMinVoltage()+.5f, voltage);
|
||||
}
|
||||
|
||||
float getVoltage()
|
||||
{
|
||||
return this->voltage;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if voltage is within specified voltage range, allow 10% over/under voltage
|
||||
*/
|
||||
void setVoltage(float voltage)
|
||||
{
|
||||
// this->voltage = ( (voltage < this->getMinVoltage() * 0.85f) || (voltage > this->getMaxVoltage() * 1.1f) )
|
||||
// ? -1.0f
|
||||
// : voltage;
|
||||
this->voltage = voltage;
|
||||
}
|
||||
|
||||
float getLevel()
|
||||
{
|
||||
return this->level;
|
||||
}
|
||||
|
||||
void setLevel(float level)
|
||||
{
|
||||
this->level = constrain(level, 0.0f, 110.0f);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the configured calibration value
|
||||
* a offset value to fine-tune the calculated voltage.
|
||||
*/
|
||||
virtual float getCalibration()
|
||||
{
|
||||
return calibration;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the voltage calibration offset value
|
||||
* a offset value to fine-tune the calculated voltage.
|
||||
*/
|
||||
virtual void setCalibration(float offset)
|
||||
{
|
||||
calibration = offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the configured calibration value
|
||||
* a value to set the voltage divider ratio
|
||||
*/
|
||||
virtual float getVoltageMultiplier()
|
||||
{
|
||||
return voltageMultiplier;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the voltage multiplier value
|
||||
* a value to set the voltage divider ratio.
|
||||
*/
|
||||
virtual void setVoltageMultiplier(float multiplier)
|
||||
{
|
||||
voltageMultiplier = multiplier;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
BIN
usermods/Battery/assets/battery_connection_schematic_esp32.png
Normal file
After Width: | Height: | Size: 129 KiB |
After Width: | Height: | Size: 64 KiB |
BIN
usermods/Battery/assets/installation_my_config_h.png
Normal file
After Width: | Height: | Size: 49 KiB |
BIN
usermods/Battery/assets/installation_platformio_override_ini.png
Normal file
After Width: | Height: | Size: 54 KiB |
@ -1,3 +1,8 @@
|
||||
#ifndef UMBDefaults_h
|
||||
#define UMBDefaults_h
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
// pin defaults
|
||||
// for the esp32 it is best to use the ADC1: GPIO32 - GPIO39
|
||||
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html
|
||||
@ -9,24 +14,66 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// The initial delay before the first battery voltage reading after power-on.
|
||||
// This allows the voltage to stabilize before readings are taken, improving accuracy of initial reading.
|
||||
#ifndef USERMOD_BATTERY_INITIAL_DELAY
|
||||
#define USERMOD_BATTERY_INITIAL_DELAY 10000 // (milliseconds)
|
||||
#endif
|
||||
|
||||
// the frequency to check the battery, 30 sec
|
||||
#ifndef USERMOD_BATTERY_MEASUREMENT_INTERVAL
|
||||
#define USERMOD_BATTERY_MEASUREMENT_INTERVAL 30000
|
||||
#endif
|
||||
|
||||
// default for 18650 battery
|
||||
// https://batterybro.com/blogs/18650-wholesale-battery-reviews/18852515-when-to-recycle-18650-batteries-and-how-to-start-a-collection-center-in-your-vape-shop
|
||||
// Discharge voltage: 2.5 volt + .1 for personal safety
|
||||
#ifndef USERMOD_BATTERY_MIN_VOLTAGE
|
||||
#ifdef USERMOD_BATTERY_USE_LIPO
|
||||
// LiPo "1S" Batteries should not be dischared below 3V !!
|
||||
#define USERMOD_BATTERY_MIN_VOLTAGE 3.2f
|
||||
#else
|
||||
#define USERMOD_BATTERY_MIN_VOLTAGE 2.6f
|
||||
#endif
|
||||
|
||||
/* Default Battery Type
|
||||
* 0 = unkown
|
||||
* 1 = Lipo
|
||||
* 2 = Lion
|
||||
*/
|
||||
#ifndef USERMOD_BATTERY_DEFAULT_TYPE
|
||||
#define USERMOD_BATTERY_DEFAULT_TYPE 0
|
||||
#endif
|
||||
/*
|
||||
*
|
||||
* Unkown 'Battery' defaults
|
||||
*
|
||||
*/
|
||||
#ifndef USERMOD_BATTERY_UNKOWN_MIN_VOLTAGE
|
||||
// Extra save defaults
|
||||
#define USERMOD_BATTERY_UNKOWN_MIN_VOLTAGE 3.3f
|
||||
#endif
|
||||
#ifndef USERMOD_BATTERY_UNKOWN_MAX_VOLTAGE
|
||||
#define USERMOD_BATTERY_UNKOWN_MAX_VOLTAGE 4.2f
|
||||
#endif
|
||||
|
||||
//the default ratio for the voltage divider
|
||||
/*
|
||||
*
|
||||
* Lithium polymer (Li-Po) defaults
|
||||
*
|
||||
*/
|
||||
#ifndef USERMOD_BATTERY_LIPO_MIN_VOLTAGE
|
||||
// LiPo "1S" Batteries should not be dischared below 3V !!
|
||||
#define USERMOD_BATTERY_LIPO_MIN_VOLTAGE 3.2f
|
||||
#endif
|
||||
#ifndef USERMOD_BATTERY_LIPO_MAX_VOLTAGE
|
||||
#define USERMOD_BATTERY_LIPO_MAX_VOLTAGE 4.2f
|
||||
#endif
|
||||
|
||||
/*
|
||||
*
|
||||
* Lithium-ion (Li-Ion) defaults
|
||||
*
|
||||
*/
|
||||
#ifndef USERMOD_BATTERY_LION_MIN_VOLTAGE
|
||||
// default for 18650 battery
|
||||
#define USERMOD_BATTERY_LION_MIN_VOLTAGE 2.6f
|
||||
#endif
|
||||
#ifndef USERMOD_BATTERY_LION_MAX_VOLTAGE
|
||||
#define USERMOD_BATTERY_LION_MAX_VOLTAGE 4.2f
|
||||
#endif
|
||||
|
||||
// the default ratio for the voltage divider
|
||||
#ifndef USERMOD_BATTERY_VOLTAGE_MULTIPLIER
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#define USERMOD_BATTERY_VOLTAGE_MULTIPLIER 2.0f
|
||||
@ -35,13 +82,8 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef USERMOD_BATTERY_MAX_VOLTAGE
|
||||
#define USERMOD_BATTERY_MAX_VOLTAGE 4.2f
|
||||
#endif
|
||||
|
||||
// a common capacity for single 18650 battery cells is between 2500 and 3600 mAh
|
||||
#ifndef USERMOD_BATTERY_TOTAL_CAPACITY
|
||||
#define USERMOD_BATTERY_TOTAL_CAPACITY 3100
|
||||
#ifndef USERMOD_BATTERY_AVERAGING_ALPHA
|
||||
#define USERMOD_BATTERY_AVERAGING_ALPHA 0.1f
|
||||
#endif
|
||||
|
||||
// offset or calibration value to fine tune the calculated voltage
|
||||
@ -49,11 +91,6 @@
|
||||
#define USERMOD_BATTERY_CALIBRATION 0
|
||||
#endif
|
||||
|
||||
// calculate remaining time / the time that is left before the battery runs out of power
|
||||
// #ifndef USERMOD_BATTERY_CALCULATE_TIME_LEFT_ENABLED
|
||||
// #define USERMOD_BATTERY_CALCULATE_TIME_LEFT_ENABLED false
|
||||
// #endif
|
||||
|
||||
// auto-off feature
|
||||
#ifndef USERMOD_BATTERY_AUTO_OFF_ENABLED
|
||||
#define USERMOD_BATTERY_AUTO_OFF_ENABLED true
|
||||
@ -78,4 +115,26 @@
|
||||
|
||||
#ifndef USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION
|
||||
#define USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION 5
|
||||
#endif
|
||||
|
||||
// battery types
|
||||
typedef enum
|
||||
{
|
||||
unknown=0,
|
||||
lipo=1,
|
||||
lion=2
|
||||
} batteryType;
|
||||
|
||||
// used for initial configuration after boot
|
||||
typedef struct bconfig_t
|
||||
{
|
||||
batteryType type;
|
||||
float minVoltage;
|
||||
float maxVoltage;
|
||||
float voltage; // current voltage
|
||||
int8_t level; // current level
|
||||
float calibration; // offset or calibration value to fine tune the calculated voltage
|
||||
float voltageMultiplier;
|
||||
} batteryConfig;
|
||||
|
||||
#endif
|
@ -6,105 +6,166 @@
|
||||
|
||||
Enables battery level monitoring of your project.
|
||||
|
||||
For this to work, the positive side of the (18650) battery must be connected to pin `A0` of the d1 mini/esp8266 with a 100k Ohm resistor (see [Useful Links](#useful-links)).
|
||||
|
||||
If you have an ESP32 board, connect the positive side of the battery to ADC1 (GPIO32 - GPIO39)
|
||||
|
||||
<p align="center">
|
||||
<img width="500" src="assets/battery_info_screen.png">
|
||||
<p align="left">
|
||||
<img width="700" src="assets/battery_info_screen.png">
|
||||
</p>
|
||||
|
||||
<br>
|
||||
|
||||
## ⚙️ Features
|
||||
|
||||
- 💯 Displays current battery voltage
|
||||
- 💯 Displays current battery voltage
|
||||
- 🚥 Displays battery level
|
||||
- 🚫 Auto-off with configurable Threshold
|
||||
- 🚫 Auto-off with configurable threshold
|
||||
- 🚨 Low power indicator with many configuration possibilities
|
||||
|
||||
<br><br>
|
||||
|
||||
## 🎈 Installation
|
||||
|
||||
define `USERMOD_BATTERY` in `wled00/my_config.h`
|
||||
| **Option 1** | **Option 2** |
|
||||
|--------------|--------------|
|
||||
| In `wled00/my_config.h`<br>Add the line: `#define USERMOD_BATTERY`<br><br>[Example: my_config.h](assets/installation_my_config_h.png) | In `platformio_override.ini` (or `platformio.ini`)<br>Under: `build_flags =`, add the line: `-D USERMOD_BATTERY`<br><br>[Example: platformio_override.ini](assets/installation_platformio_override_ini.png) |
|
||||
|
||||
### Example wiring
|
||||
<br><br>
|
||||
|
||||
<p align="center">
|
||||
<img width="300" src="assets/battery_connection_schematic_01.png">
|
||||
</p>
|
||||
## 🔌 Example wiring
|
||||
|
||||
### Define Your Options
|
||||
- (see [Useful Links](#useful-links)).
|
||||
|
||||
<table style="width: 100%; table-layout: fixed;">
|
||||
<tr>
|
||||
<!-- Column for the first image -->
|
||||
<td style="width: 50%; vertical-align: bottom;">
|
||||
<img width="300" src="assets/battery_connection_schematic_01.png" style="display: block;">
|
||||
<p><strong>ESP8266</strong><br>
|
||||
With a 100k Ohm resistor, connect the positive<br>
|
||||
side of the battery to pin `A0`.</p>
|
||||
</td>
|
||||
<!-- Column for the second image -->
|
||||
<td style="width: 50%; vertical-align: bottom;">
|
||||
<img width="250" src="assets/battery_connection_schematic_esp32.png" style="display: block;">
|
||||
<p><strong>ESP32</strong> (+S2, S3, C3 etc...)<br>
|
||||
Use a voltage divider (two resistors of equal value).<br>
|
||||
Connect to ADC1 (GPIO32 - GPIO39). GPIO35 is Default.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br><br>
|
||||
|
||||
## Define Your Options
|
||||
|
||||
| Name | Unit | Description |
|
||||
| ----------------------------------------------- | ----------- |-------------------------------------------------------------------------------------- |
|
||||
| `USERMOD_BATTERY` | | define this (in `my_config.h`) to have this usermod included wled00\usermods_list.cpp |
|
||||
| `USERMOD_BATTERY_USE_LIPO` | | define this (in `my_config.h`) if you use LiPo rechargeables (1S) |
|
||||
| `USERMOD_BATTERY_MEASUREMENT_PIN` | | defaults to A0 on ESP8266 and GPIO35 on ESP32 |
|
||||
| `USERMOD_BATTERY_MEASUREMENT_INTERVAL` | ms | battery check interval. defaults to 30 seconds |
|
||||
| `USERMOD_BATTERY_MIN_VOLTAGE` | v | minimum battery voltage. default is 2.6 (18650 battery standard) |
|
||||
| `USERMOD_BATTERY_MAX_VOLTAGE` | v | maximum battery voltage. default is 4.2 (18650 battery standard) |
|
||||
| `USERMOD_BATTERY_TOTAL_CAPACITY` | mAh | the capacity of all cells in parallel summed up |
|
||||
| `USERMOD_BATTERY_CALIBRATION` | | offset / calibration number, fine tune the measured voltage by the microcontroller |
|
||||
| `USERMOD_BATTERY` | | Define this (in `my_config.h`) to have this usermod included wled00\usermods_list.cpp |
|
||||
| `USERMOD_BATTERY_MEASUREMENT_PIN` | | Defaults to A0 on ESP8266 and GPIO35 on ESP32 |
|
||||
| `USERMOD_BATTERY_MEASUREMENT_INTERVAL` | ms | Battery check interval. defaults to 30 seconds |
|
||||
| `USERMOD_BATTERY_INITIAL_DELAY` | ms | Delay before initial reading. defaults to 10 seconds to allow voltage stabilization |
|
||||
| `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 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 |
|
||||
| `USERMOD_BATTERY_AUTO_OFF_THRESHOLD` | % (0-100) | when this threshold is reached master power turns off |
|
||||
| `USERMOD_BATTERY_AUTO_OFF_ENABLED` | true/false | Enables auto-off |
|
||||
| `USERMOD_BATTERY_AUTO_OFF_THRESHOLD` | % (0-100) | When this threshold is reached master power turns off |
|
||||
| Low-Power-Indicator | --- | --- |
|
||||
| `USERMOD_BATTERY_LOW_POWER_INDICATOR_ENABLED` | true/false | enables low power indication |
|
||||
| `USERMOD_BATTERY_LOW_POWER_INDICATOR_PRESET` | preset id | when low power is detected then use this preset to indicate low power |
|
||||
| `USERMOD_BATTERY_LOW_POWER_INDICATOR_THRESHOLD` | % (0-100) | when this threshold is reached low power gets indicated |
|
||||
| `USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION` | seconds | for this long the configured preset is played |
|
||||
| `USERMOD_BATTERY_LOW_POWER_INDICATOR_ENABLED` | true/false | Enables low power indication |
|
||||
| `USERMOD_BATTERY_LOW_POWER_INDICATOR_PRESET` | preset id | When low power is detected then use this preset to indicate low power |
|
||||
| `USERMOD_BATTERY_LOW_POWER_INDICATOR_THRESHOLD` | % (0-100) | When this threshold is reached low power gets indicated |
|
||||
| `USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION` | seconds | For this long the configured preset is played |
|
||||
|
||||
All parameters can be configured at runtime via the Usermods settings page.
|
||||
|
||||
<br>
|
||||
|
||||
**NOTICE:** Each Battery type can be pre-configured individualy (in `my_config.h`)
|
||||
|
||||
| Name | Alias | `my_config.h` example |
|
||||
| --------------- | ------------- | ------------------------------------- |
|
||||
| Lithium Polymer | lipo (Li-Po) | `USERMOD_BATTERY_lipo_MIN_VOLTAGE` |
|
||||
| Lithium Ionen | lion (Li-Ion) | `USERMOD_BATTERY_lion_TOTAL_CAPACITY` |
|
||||
|
||||
<br><br>
|
||||
|
||||
## 🔧 Calibration
|
||||
|
||||
The calibration number is a value that is added to the final computed voltage after it has been scaled by the voltage multiplier.
|
||||
|
||||
It fine-tunes the voltage reading so that it more closely matches the actual battery voltage, compensating for inaccuracies inherent in the voltage divider resistors or the ESP's ADC measurements.
|
||||
|
||||
Set calibration either in the Usermods settings page or at compile time in `my_config.h` or `platformio_override.ini`.
|
||||
|
||||
It can be either a positive or negative number.
|
||||
|
||||
<br><br>
|
||||
|
||||
## ⚠️ Important
|
||||
|
||||
- Make sure you know your battery specifications! All batteries are **NOT** the same!
|
||||
- Example:
|
||||
Make sure you know your battery specifications! All batteries are **NOT** the same!
|
||||
|
||||
| Your battery specification table | | Options you can define |
|
||||
| :-------------------------------- |:--------------- | :---------------------------- |
|
||||
| Capacity | 3500mAh 12,5 Wh | |
|
||||
| Minimum capacity | 3350mAh 11,9 Wh | |
|
||||
Example:
|
||||
|
||||
| Your battery specification table | | Options you can define |
|
||||
| --------------------------------- | --------------- | ----------------------------- |
|
||||
| Capacity | 3500mAh 12.5Wh | |
|
||||
| Minimum capacity | 3350mAh 11.9Wh | |
|
||||
| Rated voltage | 3.6V - 3.7V | |
|
||||
| **Charging end voltage** | **4,2V ± 0,05** | `USERMOD_BATTERY_MAX_VOLTAGE` |
|
||||
| **Discharge voltage** | **2,5V** | `USERMOD_BATTERY_MIN_VOLTAGE` |
|
||||
| **Charging end voltage** | **4.2V ± 0.05** | `USERMOD_BATTERY_MAX_VOLTAGE` |
|
||||
| **Discharge voltage** | **2.5V** | `USERMOD_BATTERY_MIN_VOLTAGE` |
|
||||
| Max. discharge current (constant) | 10A (10000mA) | |
|
||||
| max. charging current | 1.7A (1700mA) | |
|
||||
| ... | ... | ... |
|
||||
| .. | .. | .. |
|
||||
|
||||
Specification from: [Molicel INR18650-M35A, 3500mAh 10A Lithium-ion battery, 3.6V - 3.7V](https://www.akkuteile.de/lithium-ionen-akkus/18650/molicel/molicel-inr18650-m35a-3500mah-10a-lithium-ionen-akku-3-6v-3-7v_100833)
|
||||
Specification from: [Molicel INR18650-M35A, 3500mAh 10A Lithium-ion battery, 3.6V - 3.7V](https://www.akkuteile.de/lithium-ionen-akkus/18650/molicel/molicel-inr18650-m35a-3500mah-10a-lithium-ionen-akku-3-6v-3-7v_100833)
|
||||
|
||||
<br><br>
|
||||
|
||||
## 🌐 Useful Links
|
||||
|
||||
- https://lazyzero.de/elektronik/esp8266/wemos_d1_mini_a0/start
|
||||
- https://arduinodiy.wordpress.com/2016/12/25/monitoring-lipo-battery-voltage-with-wemos-d1-minibattery-shield-and-thingspeak/
|
||||
|
||||
<br><br>
|
||||
|
||||
## 📝 Change Log
|
||||
|
||||
2024-05-11
|
||||
|
||||
- Documentation updated
|
||||
|
||||
2024-04-30
|
||||
|
||||
- Integrate factory pattern to make it easier to add other / custom battery types
|
||||
- Update readme
|
||||
- Improved initial reading accuracy by delaying initial measurement to allow voltage to stabilize at power-on
|
||||
|
||||
2023-01-04
|
||||
|
||||
- basic support for LiPo rechargeable batteries ( `-D USERMOD_BATTERY_USE_LIPO`)
|
||||
- improved support for esp32 (read calibrated voltage)
|
||||
- corrected config saving (measurement pin, and battery min/max were lost)
|
||||
- various bugfixes
|
||||
- Basic support for LiPo rechargeable batteries (`-D USERMOD_BATTERY_USE_LIPO`)
|
||||
- Improved support for ESP32 (read calibrated voltage)
|
||||
- Corrected config saving (measurement pin, and battery min/max were lost)
|
||||
- Various bugfixes
|
||||
|
||||
2022-12-25
|
||||
|
||||
- added "auto-off" feature
|
||||
- added "low-power-indication" feature
|
||||
- added "calibration/offset" field to configuration page
|
||||
- added getter and setter, so that user usermods could interact with this one
|
||||
- update readme (added new options, made it markdownlint compliant)
|
||||
- Added "auto-off" feature
|
||||
- Added "low-power-indication" feature
|
||||
- Added "calibration/offset" field to configuration page
|
||||
- Added getter and setter, so that user usermods could interact with this one
|
||||
- Update readme (added new options, made it markdownlint compliant)
|
||||
|
||||
2021-09-02
|
||||
|
||||
- added "Battery voltage" to info
|
||||
- added circuit diagram to readme
|
||||
- added MQTT support, sending battery voltage
|
||||
- minor fixes
|
||||
- Added "Battery voltage" to info
|
||||
- Added circuit diagram to readme
|
||||
- Added MQTT support, sending battery voltage
|
||||
- Minor fixes
|
||||
|
||||
2021-08-15
|
||||
|
||||
- changed `USERMOD_BATTERY_MIN_VOLTAGE` to 2.6 volt as default for 18650 batteries
|
||||
- Changed `USERMOD_BATTERY_MIN_VOLTAGE` to 2.6 volt as default for 18650 batteries
|
||||
- Updated readme, added specification table
|
||||
|
||||
2021-08-10
|
||||
|
38
usermods/Battery/types/LionUMBattery.h
Normal file
@ -0,0 +1,38 @@
|
||||
#ifndef UMBLion_h
|
||||
#define UMBLion_h
|
||||
|
||||
#include "../battery_defaults.h"
|
||||
#include "../UMBattery.h"
|
||||
|
||||
/**
|
||||
* LiOn Battery
|
||||
*
|
||||
*/
|
||||
class LionUMBattery : public UMBattery
|
||||
{
|
||||
private:
|
||||
|
||||
public:
|
||||
LionUMBattery() : UMBattery()
|
||||
{
|
||||
this->setMinVoltage(USERMOD_BATTERY_LION_MIN_VOLTAGE);
|
||||
this->setMaxVoltage(USERMOD_BATTERY_LION_MAX_VOLTAGE);
|
||||
}
|
||||
|
||||
float mapVoltage(float v, float min, float max) override
|
||||
{
|
||||
return this->linearMapping(v, min, max); // basic mapping
|
||||
};
|
||||
|
||||
void calculateAndSetLevel(float voltage) override
|
||||
{
|
||||
this->setLevel(this->mapVoltage(voltage, this->getMinVoltage(), this->getMaxVoltage()));
|
||||
};
|
||||
|
||||
virtual void setMaxVoltage(float voltage) override
|
||||
{
|
||||
this->maxVoltage = max(getMinVoltage()+1.0f, voltage);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
54
usermods/Battery/types/LipoUMBattery.h
Normal file
@ -0,0 +1,54 @@
|
||||
#ifndef UMBLipo_h
|
||||
#define UMBLipo_h
|
||||
|
||||
#include "../battery_defaults.h"
|
||||
#include "../UMBattery.h"
|
||||
|
||||
/**
|
||||
* LiPo Battery
|
||||
*
|
||||
*/
|
||||
class LipoUMBattery : public UMBattery
|
||||
{
|
||||
private:
|
||||
|
||||
public:
|
||||
LipoUMBattery() : UMBattery()
|
||||
{
|
||||
this->setMinVoltage(USERMOD_BATTERY_LIPO_MIN_VOLTAGE);
|
||||
this->setMaxVoltage(USERMOD_BATTERY_LIPO_MAX_VOLTAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* LiPo batteries have a differnt discharge curve, see
|
||||
* https://blog.ampow.com/lipo-voltage-chart/
|
||||
*/
|
||||
float mapVoltage(float v, float min, float max) override
|
||||
{
|
||||
float lvl = 0.0f;
|
||||
lvl = this->linearMapping(v, min, max); // basic mapping
|
||||
|
||||
if (lvl < 40.0f)
|
||||
lvl = this->linearMapping(lvl, 0, 40, 0, 12); // last 45% -> drops very quickly
|
||||
else {
|
||||
if (lvl < 90.0f)
|
||||
lvl = this->linearMapping(lvl, 40, 90, 12, 95); // 90% ... 40% -> almost linear drop
|
||||
else // level > 90%
|
||||
lvl = this->linearMapping(lvl, 90, 105, 95, 100); // highest 15% -> drop slowly
|
||||
}
|
||||
|
||||
return lvl;
|
||||
};
|
||||
|
||||
void calculateAndSetLevel(float voltage) override
|
||||
{
|
||||
this->setLevel(this->mapVoltage(voltage, this->getMinVoltage(), this->getMaxVoltage()));
|
||||
};
|
||||
|
||||
virtual void setMaxVoltage(float voltage) override
|
||||
{
|
||||
this->maxVoltage = max(getMinVoltage()+0.7f, voltage);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
39
usermods/Battery/types/UnkownUMBattery.h
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef UMBUnkown_h
|
||||
#define UMBUnkown_h
|
||||
|
||||
#include "../battery_defaults.h"
|
||||
#include "../UMBattery.h"
|
||||
|
||||
/**
|
||||
* Unkown / Default Battery
|
||||
*
|
||||
*/
|
||||
class UnkownUMBattery : public UMBattery
|
||||
{
|
||||
private:
|
||||
|
||||
public:
|
||||
UnkownUMBattery() : UMBattery()
|
||||
{
|
||||
this->setMinVoltage(USERMOD_BATTERY_UNKOWN_MIN_VOLTAGE);
|
||||
this->setMaxVoltage(USERMOD_BATTERY_UNKOWN_MAX_VOLTAGE);
|
||||
}
|
||||
|
||||
void update(batteryConfig cfg)
|
||||
{
|
||||
if(cfg.minVoltage) this->setMinVoltage(cfg.minVoltage); else this->setMinVoltage(USERMOD_BATTERY_UNKOWN_MIN_VOLTAGE);
|
||||
if(cfg.maxVoltage) this->setMaxVoltage(cfg.maxVoltage); else this->setMaxVoltage(USERMOD_BATTERY_UNKOWN_MAX_VOLTAGE);
|
||||
}
|
||||
|
||||
float mapVoltage(float v, float min, float max) override
|
||||
{
|
||||
return this->linearMapping(v, min, max); // basic mapping
|
||||
};
|
||||
|
||||
void calculateAndSetLevel(float voltage) override
|
||||
{
|
||||
this->setLevel(this->mapVoltage(voltage, this->getMinVoltage(), this->getMaxVoltage()));
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
@ -2,12 +2,15 @@
|
||||
|
||||
#include "wled.h"
|
||||
#include "battery_defaults.h"
|
||||
#include "UMBattery.h"
|
||||
#include "types/UnkownUMBattery.h"
|
||||
#include "types/LionUMBattery.h"
|
||||
#include "types/LiPoUMBattery.h"
|
||||
|
||||
/*
|
||||
* Usermod by Maximilian Mewes
|
||||
* Mail: mewes.maximilian@gmx.de
|
||||
* GitHub: itCarl
|
||||
* Date: 25.12.2022
|
||||
* E-mail: mewes.maximilian@gmx.de
|
||||
* Created at: 25.12.2022
|
||||
* If you have any questions, please feel free to contact me.
|
||||
*/
|
||||
class UsermodBattery : public Usermod
|
||||
@ -15,47 +18,36 @@ class UsermodBattery : public Usermod
|
||||
private:
|
||||
// battery pin can be defined in my_config.h
|
||||
int8_t batteryPin = USERMOD_BATTERY_MEASUREMENT_PIN;
|
||||
|
||||
UMBattery* bat = new UnkownUMBattery();
|
||||
batteryConfig cfg;
|
||||
|
||||
// Initial delay before first reading to allow voltage stabilization
|
||||
unsigned long initialDelay = USERMOD_BATTERY_INITIAL_DELAY;
|
||||
bool initialDelayComplete = false;
|
||||
bool isFirstVoltageReading = true;
|
||||
// how often to read the battery voltage
|
||||
unsigned long readingInterval = USERMOD_BATTERY_MEASUREMENT_INTERVAL;
|
||||
unsigned long nextReadTime = 0;
|
||||
unsigned long lastReadTime = 0;
|
||||
// battery min. voltage
|
||||
float minBatteryVoltage = USERMOD_BATTERY_MIN_VOLTAGE;
|
||||
// battery max. voltage
|
||||
float maxBatteryVoltage = USERMOD_BATTERY_MAX_VOLTAGE;
|
||||
// all battery cells summed up
|
||||
unsigned int totalBatteryCapacity = USERMOD_BATTERY_TOTAL_CAPACITY;
|
||||
// raw analog reading
|
||||
float rawValue = 0.0f;
|
||||
// calculated voltage
|
||||
float voltage = maxBatteryVoltage;
|
||||
// between 0 and 1, to control strength of voltage smoothing filter
|
||||
float alpha = 0.05f;
|
||||
// multiplier for the voltage divider that is in place between ADC pin and battery, default will be 2 but might be adapted to readout voltages over ~5v ESP32 or ~6.6v ESP8266
|
||||
float voltageMultiplier = USERMOD_BATTERY_VOLTAGE_MULTIPLIER;
|
||||
// mapped battery level based on voltage
|
||||
int8_t batteryLevel = 100;
|
||||
// offset or calibration value to fine tune the calculated voltage
|
||||
float calibration = USERMOD_BATTERY_CALIBRATION;
|
||||
|
||||
// time left estimation feature
|
||||
// bool calculateTimeLeftEnabled = USERMOD_BATTERY_CALCULATE_TIME_LEFT_ENABLED;
|
||||
// float estimatedTimeLeft = 0.0;
|
||||
float alpha = USERMOD_BATTERY_AVERAGING_ALPHA;
|
||||
|
||||
// auto shutdown/shutoff/master off feature
|
||||
bool autoOffEnabled = USERMOD_BATTERY_AUTO_OFF_ENABLED;
|
||||
int8_t autoOffThreshold = USERMOD_BATTERY_AUTO_OFF_THRESHOLD;
|
||||
uint8_t autoOffThreshold = USERMOD_BATTERY_AUTO_OFF_THRESHOLD;
|
||||
|
||||
// low power indicator feature
|
||||
bool lowPowerIndicatorEnabled = USERMOD_BATTERY_LOW_POWER_INDICATOR_ENABLED;
|
||||
int8_t lowPowerIndicatorPreset = USERMOD_BATTERY_LOW_POWER_INDICATOR_PRESET;
|
||||
int8_t lowPowerIndicatorThreshold = USERMOD_BATTERY_LOW_POWER_INDICATOR_THRESHOLD;
|
||||
int8_t lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold+10;
|
||||
int8_t lowPowerIndicatorDuration = USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION;
|
||||
uint8_t lowPowerIndicatorPreset = USERMOD_BATTERY_LOW_POWER_INDICATOR_PRESET;
|
||||
uint8_t lowPowerIndicatorThreshold = USERMOD_BATTERY_LOW_POWER_INDICATOR_THRESHOLD;
|
||||
uint8_t lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold+10;
|
||||
uint8_t lowPowerIndicatorDuration = USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION;
|
||||
bool lowPowerIndicationDone = false;
|
||||
unsigned long lowPowerActivationTime = 0; // used temporary during active time
|
||||
int8_t lastPreset = 0;
|
||||
uint8_t lastPreset = 0;
|
||||
|
||||
//
|
||||
bool initDone = false;
|
||||
bool initializing = true;
|
||||
|
||||
@ -67,22 +59,17 @@ class UsermodBattery : public Usermod
|
||||
static const char _preset[];
|
||||
static const char _duration[];
|
||||
static const char _init[];
|
||||
|
||||
|
||||
// custom map function
|
||||
// https://forum.arduino.cc/t/floating-point-using-map-function/348113/2
|
||||
double mapf(double x, double in_min, double in_max, double out_min, double out_max)
|
||||
{
|
||||
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for rounding floating point values
|
||||
*/
|
||||
float dot2round(float x)
|
||||
{
|
||||
float nx = (int)(x * 100 + .5);
|
||||
return (float)(nx / 100);
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Turn off all leds
|
||||
*/
|
||||
void turnOff()
|
||||
@ -91,15 +78,15 @@ class UsermodBattery : public Usermod
|
||||
stateUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Indicate low power by activating a configured preset for a given time and then switching back to the preset that was selected previously
|
||||
*/
|
||||
void lowPowerIndicator()
|
||||
{
|
||||
if (!lowPowerIndicatorEnabled) return;
|
||||
if (batteryPin < 0) return; // no measurement
|
||||
if (lowPowerIndicationDone && lowPowerIndicatorReactivationThreshold <= batteryLevel) lowPowerIndicationDone = false;
|
||||
if (lowPowerIndicatorThreshold <= batteryLevel) return;
|
||||
if (lowPowerIndicationDone && lowPowerIndicatorReactivationThreshold <= bat->getLevel()) lowPowerIndicationDone = false;
|
||||
if (lowPowerIndicatorThreshold <= bat->getLevel()) return;
|
||||
if (lowPowerIndicationDone) return;
|
||||
if (lowPowerActivationTime <= 1) {
|
||||
lowPowerActivationTime = millis();
|
||||
@ -114,26 +101,39 @@ class UsermodBattery : public Usermod
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* read the battery voltage in different ways depending on the architecture
|
||||
*/
|
||||
float readVoltage()
|
||||
{
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
// use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV default attentuation) and divide by 1000 to get from milivolts to volts and multiply by voltage multiplier and apply calibration value
|
||||
return (analogReadMilliVolts(batteryPin) / 1000.0f) * voltageMultiplier + calibration;
|
||||
return (analogReadMilliVolts(batteryPin) / 1000.0f) * bat->getVoltageMultiplier() + bat->getCalibration();
|
||||
#else
|
||||
// use analog read on esp8266 ( 0V ~ 1V no attenuation options) and divide by ADC precision 1023 and multiply by voltage multiplier and apply calibration value
|
||||
return (analogRead(batteryPin) / 1023.0f) * voltageMultiplier + calibration;
|
||||
return (analogRead(batteryPin) / 1023.0f) * bat->getVoltageMultiplier() + bat->getCalibration();
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
//Functions called by WLED
|
||||
|
||||
/*
|
||||
/**
|
||||
* setup() is called once at boot. WiFi is not yet connected at this point.
|
||||
* You can use it to initialize variables, sensors or similar.
|
||||
*/
|
||||
void setup()
|
||||
{
|
||||
// plug in the right battery type
|
||||
if(cfg.type == (batteryType)lipo) {
|
||||
bat = new LipoUMBattery();
|
||||
} else if(cfg.type == (batteryType)lion) {
|
||||
bat = new LionUMBattery();
|
||||
}
|
||||
|
||||
// update the choosen battery type with configured values
|
||||
bat->update(cfg);
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
bool success = false;
|
||||
DEBUG_PRINTLN(F("Allocating battery pin..."));
|
||||
@ -141,7 +141,6 @@ class UsermodBattery : public Usermod
|
||||
if (pinManager.allocatePin(batteryPin, false, PinOwner::UM_Battery)) {
|
||||
DEBUG_PRINTLN(F("Battery pin allocation succeeded."));
|
||||
success = true;
|
||||
voltage = readVoltage();
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
@ -152,17 +151,17 @@ class UsermodBattery : public Usermod
|
||||
}
|
||||
#else //ESP8266 boards have only one analog input pin A0
|
||||
pinMode(batteryPin, INPUT);
|
||||
voltage = readVoltage();
|
||||
#endif
|
||||
|
||||
nextReadTime = millis() + readingInterval;
|
||||
// First voltage reading is delayed to allow voltage stabilization after powering up
|
||||
nextReadTime = millis() + initialDelay;
|
||||
lastReadTime = millis();
|
||||
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* connected() is called every time the WiFi is (re)connected
|
||||
* Use it to initialize network interfaces
|
||||
*/
|
||||
@ -182,6 +181,25 @@ class UsermodBattery : public Usermod
|
||||
|
||||
lowPowerIndicator();
|
||||
|
||||
// Handling the initial delay
|
||||
if (!initialDelayComplete && millis() < nextReadTime)
|
||||
return; // Continue to return until the initial delay is over
|
||||
|
||||
// Once the initial delay is over, set it as complete
|
||||
if (!initialDelayComplete)
|
||||
{
|
||||
initialDelayComplete = true;
|
||||
// Set the regular interval after initial delay
|
||||
nextReadTime = millis() + readingInterval;
|
||||
}
|
||||
|
||||
// Make the first voltage reading after the initial delay has elapsed
|
||||
if (isFirstVoltageReading)
|
||||
{
|
||||
bat->setVoltage(readVoltage());
|
||||
isFirstVoltageReading = false;
|
||||
}
|
||||
|
||||
// check the battery level every USERMOD_BATTERY_MEASUREMENT_INTERVAL (ms)
|
||||
if (millis() < nextReadTime) return;
|
||||
|
||||
@ -191,43 +209,17 @@ class UsermodBattery : public Usermod
|
||||
if (batteryPin < 0) return; // nothing to read
|
||||
|
||||
initializing = false;
|
||||
float rawValue = readVoltage();
|
||||
|
||||
rawValue = readVoltage();
|
||||
// filter with exponential smoothing because ADC in esp32 is fluctuating too much for a good single readout
|
||||
voltage = voltage + alpha * (rawValue - voltage);
|
||||
|
||||
// check if voltage is within specified voltage range, allow 10% over/under voltage - removed cause this just makes it hard for people to troubleshoot as the voltage in the web gui will say invalid instead of displaying a voltage
|
||||
//voltage = ((voltage < minBatteryVoltage * 0.85f) || (voltage > maxBatteryVoltage * 1.1f)) ? -1.0f : voltage;
|
||||
float filteredVoltage = bat->getVoltage() + alpha * (rawValue - bat->getVoltage());
|
||||
|
||||
bat->setVoltage(filteredVoltage);
|
||||
// translate battery voltage into percentage
|
||||
/*
|
||||
the standard "map" function doesn't work
|
||||
https://www.arduino.cc/reference/en/language/functions/math/map/ notes and warnings at the bottom
|
||||
*/
|
||||
#ifdef USERMOD_BATTERY_USE_LIPO
|
||||
batteryLevel = mapf(voltage, minBatteryVoltage, maxBatteryVoltage, 0, 100); // basic mapping
|
||||
// LiPo batteries have a differnt dischargin curve, see
|
||||
// https://blog.ampow.com/lipo-voltage-chart/
|
||||
if (batteryLevel < 40.0f)
|
||||
batteryLevel = mapf(batteryLevel, 0, 40, 0, 12); // last 45% -> drops very quickly
|
||||
else {
|
||||
if (batteryLevel < 90.0f)
|
||||
batteryLevel = mapf(batteryLevel, 40, 90, 12, 95); // 90% ... 40% -> almost linear drop
|
||||
else // level > 90%
|
||||
batteryLevel = mapf(batteryLevel, 90, 105, 95, 100); // highest 15% -> drop slowly
|
||||
}
|
||||
#else
|
||||
batteryLevel = mapf(voltage, minBatteryVoltage, maxBatteryVoltage, 0, 100);
|
||||
#endif
|
||||
if (voltage > -1.0f) batteryLevel = constrain(batteryLevel, 0.0f, 110.0f);
|
||||
|
||||
// if (calculateTimeLeftEnabled) {
|
||||
// float currentBatteryCapacity = totalBatteryCapacity;
|
||||
// estimatedTimeLeft = (currentBatteryCapacity/strip.currentMilliamps)*60;
|
||||
// }
|
||||
bat->calculateAndSetLevel(filteredVoltage);
|
||||
|
||||
// Auto off -- Master power off
|
||||
if (autoOffEnabled && (autoOffThreshold >= batteryLevel))
|
||||
if (autoOffEnabled && (autoOffThreshold >= bat->getLevel()))
|
||||
turnOff();
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
@ -236,13 +228,13 @@ class UsermodBattery : public Usermod
|
||||
if (WLED_MQTT_CONNECTED) {
|
||||
char buf[64]; // buffer for snprintf()
|
||||
snprintf_P(buf, 63, PSTR("%s/voltage"), mqttDeviceTopic);
|
||||
mqtt->publish(buf, 0, false, String(voltage).c_str());
|
||||
mqtt->publish(buf, 0, false, String(bat->getVoltage()).c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
|
||||
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
|
||||
* Below it is shown how this could be used for e.g. a light sensor
|
||||
@ -262,16 +254,6 @@ class UsermodBattery : public Usermod
|
||||
// info modal display names
|
||||
JsonArray infoPercentage = user.createNestedArray(F("Battery level"));
|
||||
JsonArray infoVoltage = user.createNestedArray(F("Battery voltage"));
|
||||
// if (calculateTimeLeftEnabled)
|
||||
// {
|
||||
// JsonArray infoEstimatedTimeLeft = user.createNestedArray(F("Estimated time left"));
|
||||
// if (initializing) {
|
||||
// infoEstimatedTimeLeft.add(FPSTR(_init));
|
||||
// } else {
|
||||
// infoEstimatedTimeLeft.add(estimatedTimeLeft);
|
||||
// infoEstimatedTimeLeft.add(F(" min"));
|
||||
// }
|
||||
// }
|
||||
JsonArray infoNextUpdate = user.createNestedArray(F("Next update"));
|
||||
|
||||
infoNextUpdate.add((nextReadTime - millis()) / 1000);
|
||||
@ -283,46 +265,104 @@ class UsermodBattery : public Usermod
|
||||
return;
|
||||
}
|
||||
|
||||
if (batteryLevel < 0) {
|
||||
if (bat->getLevel() < 0) {
|
||||
infoPercentage.add(F("invalid"));
|
||||
} else {
|
||||
infoPercentage.add(batteryLevel);
|
||||
infoPercentage.add(bat->getLevel());
|
||||
}
|
||||
infoPercentage.add(F(" %"));
|
||||
|
||||
if (voltage < 0) {
|
||||
if (bat->getVoltage() < 0) {
|
||||
infoVoltage.add(F("invalid"));
|
||||
} else {
|
||||
infoVoltage.add(dot2round(voltage));
|
||||
infoVoltage.add(dot2round(bat->getVoltage()));
|
||||
}
|
||||
infoVoltage.add(F(" V"));
|
||||
}
|
||||
|
||||
void addBatteryToJsonObject(JsonObject& battery, bool forJsonState)
|
||||
{
|
||||
if(forJsonState) { battery[F("type")] = cfg.type; } else {battery[F("type")] = (String)cfg.type; } // has to be a String otherwise it won't get converted to a Dropdown
|
||||
battery[F("min-voltage")] = bat->getMinVoltage();
|
||||
battery[F("max-voltage")] = bat->getMaxVoltage();
|
||||
battery[F("calibration")] = bat->getCalibration();
|
||||
battery[F("voltage-multiplier")] = bat->getVoltageMultiplier();
|
||||
battery[FPSTR(_readInterval)] = readingInterval;
|
||||
|
||||
/*
|
||||
JsonObject ao = battery.createNestedObject(F("auto-off")); // auto off section
|
||||
ao[FPSTR(_enabled)] = autoOffEnabled;
|
||||
ao[FPSTR(_threshold)] = autoOffThreshold;
|
||||
|
||||
JsonObject lp = battery.createNestedObject(F("indicator")); // low power section
|
||||
lp[FPSTR(_enabled)] = lowPowerIndicatorEnabled;
|
||||
lp[FPSTR(_preset)] = lowPowerIndicatorPreset; // dropdown trickery (String)lowPowerIndicatorPreset;
|
||||
lp[FPSTR(_threshold)] = lowPowerIndicatorThreshold;
|
||||
lp[FPSTR(_duration)] = lowPowerIndicatorDuration;
|
||||
}
|
||||
|
||||
void getUsermodConfigFromJsonObject(JsonObject& battery)
|
||||
{
|
||||
getJsonValue(battery[F("type")], cfg.type);
|
||||
getJsonValue(battery[F("min-voltage")], cfg.minVoltage);
|
||||
getJsonValue(battery[F("max-voltage")], cfg.maxVoltage);
|
||||
getJsonValue(battery[F("calibration")], cfg.calibration);
|
||||
getJsonValue(battery[F("voltage-multiplier")], cfg.voltageMultiplier);
|
||||
|
||||
setReadingInterval(battery[FPSTR(_readInterval)] | readingInterval);
|
||||
|
||||
JsonObject ao = battery[F("auto-off")];
|
||||
setAutoOffEnabled(ao[FPSTR(_enabled)] | autoOffEnabled);
|
||||
setAutoOffThreshold(ao[FPSTR(_threshold)] | autoOffThreshold);
|
||||
|
||||
JsonObject lp = battery[F("indicator")];
|
||||
setLowPowerIndicatorEnabled(lp[FPSTR(_enabled)] | lowPowerIndicatorEnabled);
|
||||
setLowPowerIndicatorPreset(lp[FPSTR(_preset)] | lowPowerIndicatorPreset);
|
||||
setLowPowerIndicatorThreshold(lp[FPSTR(_threshold)] | lowPowerIndicatorThreshold);
|
||||
lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold+10;
|
||||
setLowPowerIndicatorDuration(lp[FPSTR(_duration)] | lowPowerIndicatorDuration);
|
||||
|
||||
if(initDone)
|
||||
bat->update(cfg);
|
||||
}
|
||||
|
||||
/**
|
||||
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
/*
|
||||
void addToJsonState(JsonObject& root)
|
||||
{
|
||||
JsonObject battery = root.createNestedObject(FPSTR(_name));
|
||||
|
||||
if (battery.isNull())
|
||||
battery = root.createNestedObject(FPSTR(_name));
|
||||
|
||||
addBatteryToJsonObject(battery, true);
|
||||
|
||||
DEBUG_PRINTLN(F("Battery state exposed in JSON API."));
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
/*
|
||||
void readFromJsonState(JsonObject& root)
|
||||
{
|
||||
if (!initDone) return; // prevent crash on boot applyPreset()
|
||||
|
||||
JsonObject battery = root[FPSTR(_name)];
|
||||
|
||||
if (!battery.isNull()) {
|
||||
getUsermodConfigFromJsonObject(battery);
|
||||
|
||||
DEBUG_PRINTLN(F("Battery state read from JSON API."));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
|
||||
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
|
||||
* If you want to force saving the current state, use serializeConfig() in your loop().
|
||||
@ -359,47 +399,41 @@ class UsermodBattery : public Usermod
|
||||
*/
|
||||
void addToConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject battery = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||
JsonObject battery = root.createNestedObject(FPSTR(_name));
|
||||
|
||||
if (battery.isNull()) {
|
||||
battery = root.createNestedObject(FPSTR(_name));
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
battery[F("pin")] = batteryPin;
|
||||
#endif
|
||||
|
||||
// battery[F("time-left")] = calculateTimeLeftEnabled;
|
||||
battery[F("min-voltage")] = minBatteryVoltage;
|
||||
battery[F("max-voltage")] = maxBatteryVoltage;
|
||||
battery[F("capacity")] = totalBatteryCapacity;
|
||||
battery[F("calibration")] = calibration;
|
||||
battery[F("voltage-multiplier")] = voltageMultiplier;
|
||||
battery[FPSTR(_readInterval)] = readingInterval;
|
||||
|
||||
JsonObject ao = battery.createNestedObject(F("auto-off")); // auto off section
|
||||
ao[FPSTR(_enabled)] = autoOffEnabled;
|
||||
ao[FPSTR(_threshold)] = autoOffThreshold;
|
||||
|
||||
JsonObject lp = battery.createNestedObject(F("indicator")); // low power section
|
||||
lp[FPSTR(_enabled)] = lowPowerIndicatorEnabled;
|
||||
lp[FPSTR(_preset)] = lowPowerIndicatorPreset; // dropdown trickery (String)lowPowerIndicatorPreset;
|
||||
lp[FPSTR(_threshold)] = lowPowerIndicatorThreshold;
|
||||
lp[FPSTR(_duration)] = lowPowerIndicatorDuration;
|
||||
addBatteryToJsonObject(battery, false);
|
||||
|
||||
// read voltage in case calibration or voltage multiplier changed to see immediate effect
|
||||
voltage = readVoltage();
|
||||
bat->setVoltage(readVoltage());
|
||||
|
||||
DEBUG_PRINTLN(F("Battery config saved."));
|
||||
}
|
||||
|
||||
void appendConfigData()
|
||||
{
|
||||
oappend(SET_F("addInfo('Battery:min-voltage', 1, 'v');"));
|
||||
oappend(SET_F("addInfo('Battery:max-voltage', 1, 'v');"));
|
||||
oappend(SET_F("addInfo('Battery:capacity', 1, 'mAh');"));
|
||||
oappend(SET_F("addInfo('Battery:interval', 1, 'ms');"));
|
||||
oappend(SET_F("addInfo('Battery:auto-off:threshold', 1, '%');"));
|
||||
oappend(SET_F("addInfo('Battery:indicator:threshold', 1, '%');"));
|
||||
oappend(SET_F("addInfo('Battery:indicator:duration', 1, 's');"));
|
||||
// Total: 462 Bytes
|
||||
oappend(SET_F("td=addDropdown('Battery', 'type');")); // 35 Bytes
|
||||
oappend(SET_F("addOption(td, 'Unkown', '0');")); // 30 Bytes
|
||||
oappend(SET_F("addOption(td, 'LiPo', '1');")); // 28 Bytes
|
||||
oappend(SET_F("addOption(td, 'LiOn', '2');")); // 28 Bytes
|
||||
oappend(SET_F("addInfo('Battery:type',1,'<small style=\"color:orange\">requires reboot</small>');")); // 81 Bytes
|
||||
oappend(SET_F("addInfo('Battery:min-voltage', 1, 'v');")); // 40 Bytes
|
||||
oappend(SET_F("addInfo('Battery:max-voltage', 1, 'v');")); // 40 Bytes
|
||||
oappend(SET_F("addInfo('Battery:interval', 1, 'ms');")); // 38 Bytes
|
||||
oappend(SET_F("addInfo('Battery:auto-off:threshold', 1, '%');")); // 47 Bytes
|
||||
oappend(SET_F("addInfo('Battery:indicator:threshold', 1, '%');")); // 48 Bytes
|
||||
oappend(SET_F("addInfo('Battery:indicator:duration', 1, 's');")); // 47 Bytes
|
||||
|
||||
// cannot quite get this mf to work. its exeeding some buffer limit i think
|
||||
// what i wanted is a list of all presets to select one from
|
||||
// this option list would exeed the oappend() buffer
|
||||
// a list of all presets to select one from
|
||||
// oappend(SET_F("bd=addDropdown('Battery:low-power-indicator', 'preset');"));
|
||||
// the loop generates: oappend(SET_F("addOption(bd, 'preset name', preset id);"));
|
||||
// for(int8_t i=1; i < 42; i++) {
|
||||
@ -412,7 +446,7 @@ class UsermodBattery : public Usermod
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
|
||||
* This is called by WLED when settings are loaded (currently this only happens immediately after boot, or after saving on the Usermod Settings page)
|
||||
*
|
||||
@ -445,25 +479,13 @@ class UsermodBattery : public Usermod
|
||||
newBatteryPin = battery[F("pin")] | newBatteryPin;
|
||||
#endif
|
||||
// calculateTimeLeftEnabled = battery[F("time-left")] | calculateTimeLeftEnabled;
|
||||
setMinBatteryVoltage(battery[F("min-voltage")] | minBatteryVoltage);
|
||||
setMaxBatteryVoltage(battery[F("max-voltage")] | maxBatteryVoltage);
|
||||
setTotalBatteryCapacity(battery[F("capacity")] | totalBatteryCapacity);
|
||||
setCalibration(battery[F("calibration")] | calibration);
|
||||
setVoltageMultiplier(battery[F("voltage-multiplier")] | voltageMultiplier);
|
||||
setMinBatteryVoltage(battery[F("min-voltage")] | bat->getMinVoltage());
|
||||
setMaxBatteryVoltage(battery[F("max-voltage")] | bat->getMaxVoltage());
|
||||
setCalibration(battery[F("calibration")] | bat->getCalibration());
|
||||
setVoltageMultiplier(battery[F("voltage-multiplier")] | bat->getVoltageMultiplier());
|
||||
setReadingInterval(battery[FPSTR(_readInterval)] | readingInterval);
|
||||
|
||||
JsonObject ao = battery[F("auto-off")];
|
||||
setAutoOffEnabled(ao[FPSTR(_enabled)] | autoOffEnabled);
|
||||
setAutoOffThreshold(ao[FPSTR(_threshold)] | autoOffThreshold);
|
||||
|
||||
JsonObject lp = battery[F("indicator")];
|
||||
setLowPowerIndicatorEnabled(lp[FPSTR(_enabled)] | lowPowerIndicatorEnabled);
|
||||
setLowPowerIndicatorPreset(lp[FPSTR(_preset)] | lowPowerIndicatorPreset); // dropdown trickery (int)lp["preset"]
|
||||
setLowPowerIndicatorThreshold(lp[FPSTR(_threshold)] | lowPowerIndicatorThreshold);
|
||||
lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold+10;
|
||||
setLowPowerIndicatorDuration(lp[FPSTR(_duration)] | lowPowerIndicatorDuration);
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
getUsermodConfigFromJsonObject(battery);
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (!initDone)
|
||||
@ -491,8 +513,9 @@ class UsermodBattery : public Usermod
|
||||
return !battery[FPSTR(_readInterval)].isNull();
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a preset sample for low power indication
|
||||
/**
|
||||
* TBD: Generate a preset sample for low power indication
|
||||
* a button on the config page would be cool, currently not possible
|
||||
*/
|
||||
void generateExamplePreset()
|
||||
{
|
||||
@ -529,7 +552,7 @@ class UsermodBattery : public Usermod
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
/**
|
||||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
||||
* This could be used in the future for the system to determine whether your usermod is installed.
|
||||
*/
|
||||
@ -538,13 +561,23 @@ class UsermodBattery : public Usermod
|
||||
return USERMOD_ID_BATTERY;
|
||||
}
|
||||
|
||||
/**
|
||||
* get currently active battery type
|
||||
*/
|
||||
batteryType getBatteryType()
|
||||
{
|
||||
return cfg.type;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
unsigned long getReadingInterval()
|
||||
{
|
||||
return readingInterval;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* minimum repetition is 3000ms (3s)
|
||||
*/
|
||||
void setReadingInterval(unsigned long newReadingInterval)
|
||||
@ -552,105 +585,84 @@ class UsermodBattery : public Usermod
|
||||
readingInterval = max((unsigned long)3000, newReadingInterval);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* Get lowest configured battery voltage
|
||||
*/
|
||||
float getMinBatteryVoltage()
|
||||
{
|
||||
return minBatteryVoltage;
|
||||
return bat->getMinVoltage();
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set lowest battery voltage
|
||||
* can't be below 0 volt
|
||||
*/
|
||||
void setMinBatteryVoltage(float voltage)
|
||||
{
|
||||
minBatteryVoltage = max(0.0f, voltage);
|
||||
bat->setMinVoltage(voltage);
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Get highest configured battery voltage
|
||||
*/
|
||||
float getMaxBatteryVoltage()
|
||||
{
|
||||
return maxBatteryVoltage;
|
||||
return bat->getMaxVoltage();
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set highest battery voltage
|
||||
* can't be below minBatteryVoltage
|
||||
*/
|
||||
void setMaxBatteryVoltage(float voltage)
|
||||
{
|
||||
#ifdef USERMOD_BATTERY_USE_LIPO
|
||||
maxBatteryVoltage = max(getMinBatteryVoltage()+0.7f, voltage);
|
||||
#else
|
||||
maxBatteryVoltage = max(getMinBatteryVoltage()+1.0f, voltage);
|
||||
#endif
|
||||
bat->setMaxVoltage(voltage);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get the capacity of all cells in parralel sumed up
|
||||
* unit: mAh
|
||||
*/
|
||||
unsigned int getTotalBatteryCapacity()
|
||||
{
|
||||
return totalBatteryCapacity;
|
||||
}
|
||||
|
||||
void setTotalBatteryCapacity(unsigned int capacity)
|
||||
{
|
||||
totalBatteryCapacity = capacity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* Get the calculated voltage
|
||||
* formula: (adc pin value / adc precision * max voltage) + calibration
|
||||
*/
|
||||
float getVoltage()
|
||||
{
|
||||
return voltage;
|
||||
return bat->getVoltage();
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Get the mapped battery level (0 - 100) based on voltage
|
||||
* important: voltage can drop when a load is applied, so its only an estimate
|
||||
*/
|
||||
int8_t getBatteryLevel()
|
||||
{
|
||||
return batteryLevel;
|
||||
return bat->getLevel();
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Get the configured calibration value
|
||||
* a offset value to fine-tune the calculated voltage.
|
||||
*/
|
||||
float getCalibration()
|
||||
{
|
||||
return calibration;
|
||||
return bat->getCalibration();
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set the voltage calibration offset value
|
||||
* a offset value to fine-tune the calculated voltage.
|
||||
*/
|
||||
void setCalibration(float offset)
|
||||
{
|
||||
calibration = offset;
|
||||
bat->setCalibration(offset);
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set the voltage multiplier value
|
||||
* A multiplier that may need adjusting for different voltage divider setups
|
||||
*/
|
||||
void setVoltageMultiplier(float multiplier)
|
||||
{
|
||||
voltageMultiplier = multiplier;
|
||||
bat->setVoltageMultiplier(multiplier);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -659,10 +671,10 @@ class UsermodBattery : public Usermod
|
||||
*/
|
||||
float getVoltageMultiplier()
|
||||
{
|
||||
return voltageMultiplier;
|
||||
return bat->getVoltageMultiplier();
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Get auto-off feature enabled status
|
||||
* is auto-off enabled, true/false
|
||||
*/
|
||||
@ -671,7 +683,7 @@ class UsermodBattery : public Usermod
|
||||
return autoOffEnabled;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set auto-off feature status
|
||||
*/
|
||||
void setAutoOffEnabled(bool enabled)
|
||||
@ -679,7 +691,7 @@ class UsermodBattery : public Usermod
|
||||
autoOffEnabled = enabled;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Get auto-off threshold in percent (0-100)
|
||||
*/
|
||||
int8_t getAutoOffThreshold()
|
||||
@ -687,7 +699,7 @@ class UsermodBattery : public Usermod
|
||||
return autoOffThreshold;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set auto-off threshold in percent (0-100)
|
||||
*/
|
||||
void setAutoOffThreshold(int8_t threshold)
|
||||
@ -697,8 +709,7 @@ class UsermodBattery : public Usermod
|
||||
autoOffThreshold = lowPowerIndicatorEnabled /*&& autoOffEnabled*/ ? min(lowPowerIndicatorThreshold-1, (int)autoOffThreshold) : autoOffThreshold;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* Get low-power-indicator feature enabled status
|
||||
* is the low-power-indicator enabled, true/false
|
||||
*/
|
||||
@ -707,7 +718,7 @@ class UsermodBattery : public Usermod
|
||||
return lowPowerIndicatorEnabled;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set low-power-indicator feature status
|
||||
*/
|
||||
void setLowPowerIndicatorEnabled(bool enabled)
|
||||
@ -715,7 +726,7 @@ class UsermodBattery : public Usermod
|
||||
lowPowerIndicatorEnabled = enabled;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Get low-power-indicator preset to activate when low power is detected
|
||||
*/
|
||||
int8_t getLowPowerIndicatorPreset()
|
||||
@ -723,7 +734,7 @@ class UsermodBattery : public Usermod
|
||||
return lowPowerIndicatorPreset;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set low-power-indicator preset to activate when low power is detected
|
||||
*/
|
||||
void setLowPowerIndicatorPreset(int8_t presetId)
|
||||
@ -741,7 +752,7 @@ class UsermodBattery : public Usermod
|
||||
return lowPowerIndicatorThreshold;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set low-power-indicator threshold in percent (0-100)
|
||||
*/
|
||||
void setLowPowerIndicatorThreshold(int8_t threshold)
|
||||
@ -751,7 +762,7 @@ class UsermodBattery : public Usermod
|
||||
lowPowerIndicatorThreshold = autoOffEnabled /*&& lowPowerIndicatorEnabled*/ ? max(autoOffThreshold+1, (int)lowPowerIndicatorThreshold) : max(5, (int)lowPowerIndicatorThreshold);
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Get low-power-indicator duration in seconds
|
||||
*/
|
||||
int8_t getLowPowerIndicatorDuration()
|
||||
@ -759,7 +770,7 @@ class UsermodBattery : public Usermod
|
||||
return lowPowerIndicatorDuration;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set low-power-indicator duration in seconds
|
||||
*/
|
||||
void setLowPowerIndicatorDuration(int8_t duration)
|
||||
@ -767,9 +778,8 @@ class UsermodBattery : public Usermod
|
||||
lowPowerIndicatorDuration = duration;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get low-power-indicator status when the indication is done thsi returns true
|
||||
/**
|
||||
* Get low-power-indicator status when the indication is done this returns true
|
||||
*/
|
||||
bool getLowPowerIndicatorDone()
|
||||
{
|
||||
|
77
usermods/INA226_v2/README.md
Normal file
@ -0,0 +1,77 @@
|
||||
# Usermod INA226
|
||||
|
||||
This Usermod is designed to read values from an INA226 sensor and output the following:
|
||||
- Current
|
||||
- Voltage
|
||||
- Power
|
||||
- Shunt Voltage
|
||||
- Overflow status
|
||||
|
||||
## Configuration
|
||||
|
||||
The following settings can be configured in the Usermod Menu:
|
||||
- **Enabled**: Enable or disable the usermod.
|
||||
- **I2CAddress**: The I2C address in decimal. Default is 64 (0x40).
|
||||
- **CheckInterval**: Number of seconds between readings. This should be higher than the time it takes to make a reading, determined by the two next options.
|
||||
- **INASamples**: The number of samples to configure the INA226 to use for a measurement. Higher counts provide more accuracy. See the 'Understanding Samples and Conversion Times' section for more details.
|
||||
- **INAConversionTime**: The time to use on converting and preparing readings on the INA226. Higher times provide more precision. See the 'Understanding Samples and Conversion Times' section for more details.
|
||||
- **Decimals**: Number of decimals in the output.
|
||||
- **ShuntResistor**: Shunt resistor value in milliohms. An R100 shunt resistor should be written as "100", while R010 should be "10".
|
||||
- **CurrentRange**: Expected maximum current in milliamps (e.g., 5 A = 5000 mA).
|
||||
- **MqttPublish**: Enable or disable MQTT publishing.
|
||||
- **MqttPublishAlways**: Publish always, regardless if there is a change.
|
||||
- **MqttHomeAssistantDiscovery**: Enable Home Assistant discovery.
|
||||
|
||||
## Dependencies
|
||||
|
||||
These must be added under `lib_deps` in your `platform.ini` (or `platform_override.ini`).
|
||||
|
||||
- Libraries
|
||||
- `wollewald/INA226_WE@~1.2.9` (by [wollewald](https://registry.platformio.org/libraries/wollewald/INA226_WE))
|
||||
- `Wire`
|
||||
|
||||
## Understanding Samples and Conversion Times
|
||||
|
||||
The INA226 uses a programmable ADC with configurable conversion times and averaging to optimize the measurement accuracy and speed. The conversion time and number of samples are determined based on the `INASamples` and `INAConversionTime` settings. The following table outlines the possible combinations:
|
||||
|
||||
| Conversion Time (μs) | 1 Sample | 4 Samples | 16 Samples | 64 Samples | 128 Samples | 256 Samples | 512 Samples | 1024 Samples |
|
||||
|----------------------|----------|-----------|------------|------------|-------------|-------------|-------------|--------------|
|
||||
| 140 | 0.28 ms | 1.12 ms | 4.48 ms | 17.92 ms | 35.84 ms | 71.68 ms | 143.36 ms | 286.72 ms |
|
||||
| 204 | 0.408 ms | 1.632 ms | 6.528 ms | 26.112 ms | 52.224 ms | 104.448 ms | 208.896 ms | 417.792 ms |
|
||||
| 332 | 0.664 ms | 2.656 ms | 10.624 ms | 42.496 ms | 84.992 ms | 169.984 ms | 339.968 ms | 679.936 ms |
|
||||
| 588 | 1.176 ms | 4.704 ms | 18.816 ms | 75.264 ms | 150.528 ms | 301.056 ms | 602.112 ms | 1204.224 ms |
|
||||
| 1100 | 2.2 ms | 8.8 ms | 35.2 ms | 140.8 ms | 281.6 ms | 563.2 ms | 1126.4 ms | 2252.8 ms |
|
||||
| 2116 | 4.232 ms | 16.928 ms | 67.712 ms | 270.848 ms | 541.696 ms | 1083.392 ms | 2166.784 ms | 4333.568 ms |
|
||||
| 4156 | 8.312 ms | 33.248 ms | 132.992 ms | 531.968 ms | 1063.936 ms | 2127.872 ms | 4255.744 ms | 8511.488 ms |
|
||||
| 8244 | 16.488 ms| 65.952 ms | 263.808 ms | 1055.232 ms| 2110.464 ms | 4220.928 ms | 8441.856 ms | 16883.712 ms |
|
||||
|
||||
It is important to pick a combination that provides the needed balance between accuracy and precision while ensuring new readings within the `CheckInterval` setting. When `USERMOD_INA226_DEBUG` is defined, the info pane contains the expected time to make a reading, which can be seen in the table above.
|
||||
|
||||
As an example, if you want a new reading every 5 seconds (`CheckInterval`), a valid combination is `256 samples` and `4156 μs` which would provide new values every 2.1 seconds.
|
||||
|
||||
The picked values also slightly affect power usage. If the `CheckInterval` is set to more than 20 seconds, the INA226 is configured in `triggered` reading mode, where it only uses power as long as it's working. Then the conversion time and average samples counts determine how long the chip stays turned on every `CheckInterval` time.
|
||||
|
||||
### Calculating Current and Power
|
||||
|
||||
The INA226 calculates current by measuring the differential voltage across a shunt resistor and using the calibration register value to convert this measurement into current. Power is calculated by multiplying the current by the bus voltage.
|
||||
|
||||
For detailed programming information and register configurations, refer to the [INA226 datasheet](https://www.ti.com/product/INA226).
|
||||
|
||||
## Author
|
||||
[@LordMike](https://github.com/LordMike)
|
||||
|
||||
## Compiling
|
||||
|
||||
To enable, compile with `USERMOD_INA226` defined (e.g. in `platformio_override.ini`).
|
||||
|
||||
```ini
|
||||
[env:ina226_example]
|
||||
extends = env:esp32dev
|
||||
build_flags =
|
||||
${common.build_flags} ${esp32.build_flags}
|
||||
-D USERMOD_INA226
|
||||
; -D USERMOD_INA226_DEBUG ; -- add a debug status to the info modal
|
||||
lib_deps =
|
||||
${esp32.lib_deps}
|
||||
wollewald/INA226_WE@~1.2.9
|
||||
```
|
9
usermods/INA226_v2/platformio_override.ini
Normal file
@ -0,0 +1,9 @@
|
||||
[env:ina226_example]
|
||||
extends = env:esp32dev
|
||||
build_flags =
|
||||
${common.build_flags} ${esp32.build_flags}
|
||||
-D USERMOD_INA226
|
||||
; -D USERMOD_INA226_DEBUG ; -- add a debug status to the info modal
|
||||
lib_deps =
|
||||
${esp32.lib_deps}
|
||||
wollewald/INA226_WE@~1.2.9
|
556
usermods/INA226_v2/usermod_ina226.h
Normal file
@ -0,0 +1,556 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
#include <INA226_WE.h>
|
||||
|
||||
#define INA226_ADDRESS 0x40 // Default I2C address for INA226
|
||||
|
||||
#define DEFAULT_CHECKINTERVAL 60000
|
||||
#define DEFAULT_INASAMPLES 128
|
||||
#define DEFAULT_INASAMPLESENUM AVERAGE_128
|
||||
#define DEFAULT_INACONVERSIONTIME 1100
|
||||
#define DEFAULT_INACONVERSIONTIMEENUM CONV_TIME_1100
|
||||
|
||||
// A packed version of all INA settings enums and their human friendly counterparts packed into a 32 bit structure
|
||||
// Some values are shifted and need to be preprocessed before usage
|
||||
struct InaSettingLookup
|
||||
{
|
||||
uint16_t avgSamples : 11; // Max 1024, which could be in 10 bits if we shifted by 1; if we somehow handle the edge case with "1"
|
||||
uint8_t avgEnum : 4; // Shift by 8 to get the INA226_AVERAGES value, accepts 0x00 to 0x0F, we need 0x00 to 0x0E
|
||||
uint16_t convTimeUs : 14; // We could save 2 bits by shifting this, but we won't save anything at present.
|
||||
INA226_CONV_TIME convTimeEnum : 3; // Only the lowest 3 bits are defined in the conversion time enumerations
|
||||
};
|
||||
|
||||
const InaSettingLookup _inaSettingsLookup[] = {
|
||||
{1024, AVERAGE_1024 >> 8, 8244, CONV_TIME_8244},
|
||||
{512, AVERAGE_512 >> 8, 4156, CONV_TIME_4156},
|
||||
{256, AVERAGE_256 >> 8, 2116, CONV_TIME_2116},
|
||||
{128, AVERAGE_128 >> 8, 1100, CONV_TIME_1100},
|
||||
{64, AVERAGE_64 >> 8, 588, CONV_TIME_588},
|
||||
{16, AVERAGE_16 >> 8, 332, CONV_TIME_332},
|
||||
{4, AVERAGE_4 >> 8, 204, CONV_TIME_204},
|
||||
{1, AVERAGE_1 >> 8, 140, CONV_TIME_140}};
|
||||
|
||||
// Note: Will update the provided arg to be the correct value
|
||||
INA226_AVERAGES getAverageEnum(uint16_t &samples)
|
||||
{
|
||||
for (const auto &setting : _inaSettingsLookup)
|
||||
{
|
||||
// If a user supplies 2000 samples, we serve up the highest possible value
|
||||
if (samples >= setting.avgSamples)
|
||||
{
|
||||
samples = setting.avgSamples;
|
||||
return static_cast<INA226_AVERAGES>(setting.avgEnum << 8);
|
||||
}
|
||||
}
|
||||
// Default value if not found
|
||||
samples = DEFAULT_INASAMPLES;
|
||||
return DEFAULT_INASAMPLESENUM;
|
||||
}
|
||||
|
||||
INA226_CONV_TIME getConversionTimeEnum(uint16_t &timeUs)
|
||||
{
|
||||
for (const auto &setting : _inaSettingsLookup)
|
||||
{
|
||||
// If a user supplies 9000 μs, we serve up the highest possible value
|
||||
if (timeUs >= setting.convTimeUs)
|
||||
{
|
||||
timeUs = setting.convTimeUs;
|
||||
return setting.convTimeEnum;
|
||||
}
|
||||
}
|
||||
// Default value if not found
|
||||
timeUs = DEFAULT_INACONVERSIONTIME;
|
||||
return DEFAULT_INACONVERSIONTIMEENUM;
|
||||
}
|
||||
|
||||
class UsermodINA226 : public Usermod
|
||||
{
|
||||
private:
|
||||
static const char _name[];
|
||||
|
||||
unsigned long _lastLoopCheck = 0;
|
||||
unsigned long _lastTriggerTime = 0;
|
||||
|
||||
bool _settingEnabled : 1; // Enable the usermod
|
||||
bool _mqttPublish : 1; // Publish MQTT values
|
||||
bool _mqttPublishAlways : 1; // Publish always, regardless if there is a change
|
||||
bool _mqttHomeAssistant : 1; // Enable Home Assistant docs
|
||||
bool _initDone : 1; // Initialization is done
|
||||
bool _isTriggeredOperationMode : 1; // false = continuous, true = triggered
|
||||
bool _measurementTriggered : 1; // if triggered mode, then true indicates we're waiting for measurements
|
||||
uint16_t _settingInaConversionTimeUs : 12; // Conversion time, shift by 2
|
||||
uint16_t _settingInaSamples : 11; // Number of samples for averaging, max 1024
|
||||
|
||||
uint8_t _i2cAddress;
|
||||
uint16_t _checkInterval; // milliseconds, user settings is in seconds
|
||||
float _decimalFactor; // a power of 10 factor. 1 would be no change, 10 is one decimal, 100 is two etc. User sees a power of 10 (0, 1, 2, ..)
|
||||
uint16_t _shuntResistor; // Shunt resistor value in milliohms
|
||||
uint16_t _currentRange; // Expected maximum current in milliamps
|
||||
|
||||
uint8_t _lastStatus = 0;
|
||||
float _lastCurrent = 0;
|
||||
float _lastVoltage = 0;
|
||||
float _lastPower = 0;
|
||||
float _lastShuntVoltage = 0;
|
||||
bool _lastOverflow = false;
|
||||
|
||||
#ifndef WLED_MQTT_DISABLE
|
||||
float _lastCurrentSent = 0;
|
||||
float _lastVoltageSent = 0;
|
||||
float _lastPowerSent = 0;
|
||||
float _lastShuntVoltageSent = 0;
|
||||
bool _lastOverflowSent = false;
|
||||
#endif
|
||||
|
||||
INA226_WE *_ina226 = nullptr;
|
||||
|
||||
float truncateDecimals(float val)
|
||||
{
|
||||
return roundf(val * _decimalFactor) / _decimalFactor;
|
||||
}
|
||||
|
||||
void initializeINA226()
|
||||
{
|
||||
if (_ina226 != nullptr)
|
||||
{
|
||||
delete _ina226;
|
||||
}
|
||||
|
||||
_ina226 = new INA226_WE(_i2cAddress);
|
||||
if (!_ina226->init())
|
||||
{
|
||||
DEBUG_PRINTLN(F("INA226 initialization failed!"));
|
||||
return;
|
||||
}
|
||||
_ina226->setCorrectionFactor(1.0);
|
||||
|
||||
uint16_t tmpShort = _settingInaSamples;
|
||||
_ina226->setAverage(getAverageEnum(tmpShort));
|
||||
|
||||
tmpShort = _settingInaConversionTimeUs << 2;
|
||||
_ina226->setConversionTime(getConversionTimeEnum(tmpShort));
|
||||
|
||||
if (_checkInterval >= 20000)
|
||||
{
|
||||
_isTriggeredOperationMode = true;
|
||||
_ina226->setMeasureMode(TRIGGERED);
|
||||
}
|
||||
else
|
||||
{
|
||||
_isTriggeredOperationMode = false;
|
||||
_ina226->setMeasureMode(CONTINUOUS);
|
||||
}
|
||||
|
||||
_ina226->setResistorRange(static_cast<float>(_shuntResistor) / 1000.0, static_cast<float>(_currentRange) / 1000.0);
|
||||
}
|
||||
|
||||
void fetchAndPushValues()
|
||||
{
|
||||
_lastStatus = _ina226->getI2cErrorCode();
|
||||
|
||||
if (_lastStatus != 0)
|
||||
return;
|
||||
|
||||
float current = truncateDecimals(_ina226->getCurrent_mA() / 1000.0);
|
||||
float voltage = truncateDecimals(_ina226->getBusVoltage_V());
|
||||
float power = truncateDecimals(_ina226->getBusPower() / 1000.0);
|
||||
float shuntVoltage = truncateDecimals(_ina226->getShuntVoltage_V());
|
||||
bool overflow = _ina226->overflow;
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
mqttPublishIfChanged(F("current"), _lastCurrentSent, current, 0.01f);
|
||||
mqttPublishIfChanged(F("voltage"), _lastVoltageSent, voltage, 0.01f);
|
||||
mqttPublishIfChanged(F("power"), _lastPowerSent, power, 0.1f);
|
||||
mqttPublishIfChanged(F("shunt_voltage"), _lastShuntVoltageSent, shuntVoltage, 0.01f);
|
||||
mqttPublishIfChanged(F("overflow"), _lastOverflowSent, overflow);
|
||||
#endif
|
||||
|
||||
_lastCurrent = current;
|
||||
_lastVoltage = voltage;
|
||||
_lastPower = power;
|
||||
_lastShuntVoltage = shuntVoltage;
|
||||
_lastOverflow = overflow;
|
||||
}
|
||||
|
||||
void handleTriggeredMode(unsigned long currentTime)
|
||||
{
|
||||
if (_measurementTriggered)
|
||||
{
|
||||
// Test if we have a measurement every 400ms
|
||||
if (currentTime - _lastTriggerTime >= 400)
|
||||
{
|
||||
_lastTriggerTime = currentTime;
|
||||
if (_ina226->isBusy())
|
||||
return;
|
||||
|
||||
fetchAndPushValues();
|
||||
_measurementTriggered = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentTime - _lastLoopCheck >= _checkInterval)
|
||||
{
|
||||
// Start a measurement and use isBusy() later to determine when it is done
|
||||
_ina226->startSingleMeasurementNoWait();
|
||||
_lastLoopCheck = currentTime;
|
||||
_lastTriggerTime = currentTime;
|
||||
_measurementTriggered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleContinuousMode(unsigned long currentTime)
|
||||
{
|
||||
if (currentTime - _lastLoopCheck >= _checkInterval)
|
||||
{
|
||||
_lastLoopCheck = currentTime;
|
||||
fetchAndPushValues();
|
||||
}
|
||||
}
|
||||
|
||||
~UsermodINA226()
|
||||
{
|
||||
delete _ina226;
|
||||
_ina226 = nullptr;
|
||||
}
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
void mqttInitialize()
|
||||
{
|
||||
if (!WLED_MQTT_CONNECTED || !_mqttPublish || !_mqttHomeAssistant)
|
||||
return;
|
||||
|
||||
char topic[128];
|
||||
snprintf_P(topic, 127, "%s/current", mqttDeviceTopic);
|
||||
mqttCreateHassSensor(F("Current"), topic, F("current"), F("A"));
|
||||
|
||||
snprintf_P(topic, 127, "%s/voltage", mqttDeviceTopic);
|
||||
mqttCreateHassSensor(F("Voltage"), topic, F("voltage"), F("V"));
|
||||
|
||||
snprintf_P(topic, 127, "%s/power", mqttDeviceTopic);
|
||||
mqttCreateHassSensor(F("Power"), topic, F("power"), F("W"));
|
||||
|
||||
snprintf_P(topic, 127, "%s/shunt_voltage", mqttDeviceTopic);
|
||||
mqttCreateHassSensor(F("Shunt Voltage"), topic, F("voltage"), F("V"));
|
||||
|
||||
snprintf_P(topic, 127, "%s/overflow", mqttDeviceTopic);
|
||||
mqttCreateHassBinarySensor(F("Overflow"), topic);
|
||||
}
|
||||
|
||||
void mqttPublishIfChanged(const __FlashStringHelper *topic, float &lastState, float state, float minChange)
|
||||
{
|
||||
if (WLED_MQTT_CONNECTED && _mqttPublish && (_mqttPublishAlways || fabsf(lastState - state) > minChange))
|
||||
{
|
||||
char subuf[128];
|
||||
snprintf_P(subuf, 127, PSTR("%s/%s"), mqttDeviceTopic, (const char *)topic);
|
||||
mqtt->publish(subuf, 0, false, String(state).c_str());
|
||||
|
||||
lastState = state;
|
||||
}
|
||||
}
|
||||
|
||||
void mqttPublishIfChanged(const __FlashStringHelper *topic, bool &lastState, bool state)
|
||||
{
|
||||
if (WLED_MQTT_CONNECTED && _mqttPublish && (_mqttPublishAlways || lastState != state))
|
||||
{
|
||||
char subuf[128];
|
||||
snprintf_P(subuf, 127, PSTR("%s/%s"), mqttDeviceTopic, (const char *)topic);
|
||||
mqtt->publish(subuf, 0, false, state ? "true" : "false");
|
||||
|
||||
lastState = state;
|
||||
}
|
||||
}
|
||||
|
||||
void mqttCreateHassSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement)
|
||||
{
|
||||
String t = String(F("homeassistant/sensor/")) + mqttClientID + "/" + name + F("/config");
|
||||
|
||||
StaticJsonDocument<600> doc;
|
||||
|
||||
doc[F("name")] = name;
|
||||
doc[F("state_topic")] = topic;
|
||||
doc[F("unique_id")] = String(mqttClientID) + name;
|
||||
if (unitOfMeasurement != "")
|
||||
doc[F("unit_of_measurement")] = unitOfMeasurement;
|
||||
if (deviceClass != "")
|
||||
doc[F("device_class")] = deviceClass;
|
||||
doc[F("expire_after")] = 1800;
|
||||
|
||||
JsonObject device = doc.createNestedObject(F("device"));
|
||||
device[F("name")] = serverDescription;
|
||||
device[F("identifiers")] = "wled-sensor-" + String(mqttClientID);
|
||||
device[F("manufacturer")] = F(WLED_BRAND);
|
||||
device[F("model")] = F(WLED_PRODUCT_NAME);
|
||||
device[F("sw_version")] = versionString;
|
||||
|
||||
String temp;
|
||||
serializeJson(doc, temp);
|
||||
DEBUG_PRINTLN(t);
|
||||
DEBUG_PRINTLN(temp);
|
||||
|
||||
mqtt->publish(t.c_str(), 0, true, temp.c_str());
|
||||
}
|
||||
|
||||
void mqttCreateHassBinarySensor(const String &name, const String &topic)
|
||||
{
|
||||
String t = String(F("homeassistant/binary_sensor/")) + mqttClientID + "/" + name + F("/config");
|
||||
|
||||
StaticJsonDocument<600> doc;
|
||||
|
||||
doc[F("name")] = name;
|
||||
doc[F("state_topic")] = topic;
|
||||
doc[F("unique_id")] = String(mqttClientID) + name;
|
||||
|
||||
JsonObject device = doc.createNestedObject(F("device"));
|
||||
device[F("name")] = serverDescription;
|
||||
device[F("identifiers")] = "wled-sensor-" + String(mqttClientID);
|
||||
device[F("manufacturer")] = F(WLED_BRAND);
|
||||
device[F("model")] = F(WLED_PRODUCT_NAME);
|
||||
device[F("sw_version")] = versionString;
|
||||
|
||||
String temp;
|
||||
serializeJson(doc, temp);
|
||||
DEBUG_PRINTLN(t);
|
||||
DEBUG_PRINTLN(temp);
|
||||
|
||||
mqtt->publish(t.c_str(), 0, true, temp.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
public:
|
||||
UsermodINA226()
|
||||
{
|
||||
// Default values
|
||||
_settingInaSamples = DEFAULT_INASAMPLES;
|
||||
_settingInaConversionTimeUs = DEFAULT_INACONVERSIONTIME;
|
||||
|
||||
_i2cAddress = INA226_ADDRESS;
|
||||
_checkInterval = DEFAULT_CHECKINTERVAL;
|
||||
_decimalFactor = 100;
|
||||
_shuntResistor = 1000;
|
||||
_currentRange = 1000;
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
initializeINA226();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (!_settingEnabled || strip.isUpdating())
|
||||
return;
|
||||
|
||||
unsigned long currentTime = millis();
|
||||
|
||||
if (_isTriggeredOperationMode)
|
||||
{
|
||||
handleTriggeredMode(currentTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
handleContinuousMode(currentTime);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
void onMqttConnect(bool sessionPresent)
|
||||
{
|
||||
mqttInitialize();
|
||||
}
|
||||
#endif
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_INA226;
|
||||
}
|
||||
|
||||
void addToJsonInfo(JsonObject &root) override
|
||||
{
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull())
|
||||
user = root.createNestedObject("u");
|
||||
|
||||
#ifdef USERMOD_INA226_DEBUG
|
||||
JsonArray temp = user.createNestedArray(F("INA226 last loop"));
|
||||
temp.add(_lastLoopCheck);
|
||||
|
||||
temp = user.createNestedArray(F("INA226 last status"));
|
||||
temp.add(_lastStatus);
|
||||
|
||||
temp = user.createNestedArray(F("INA226 average samples"));
|
||||
temp.add(_settingInaSamples);
|
||||
temp.add(F("samples"));
|
||||
|
||||
temp = user.createNestedArray(F("INA226 conversion time"));
|
||||
temp.add(_settingInaConversionTimeUs << 2);
|
||||
temp.add(F("μs"));
|
||||
|
||||
// INA226 uses (2 * conversion time * samples) time to take a reading.
|
||||
temp = user.createNestedArray(F("INA226 expected sample time"));
|
||||
uint32_t sampleTimeNeededUs = (static_cast<uint32_t>(_settingInaConversionTimeUs) << 2) * _settingInaSamples * 2;
|
||||
temp.add(truncateDecimals(sampleTimeNeededUs / 1000.0));
|
||||
temp.add(F("ms"));
|
||||
|
||||
temp = user.createNestedArray(F("INA226 mode"));
|
||||
temp.add(_isTriggeredOperationMode ? F("triggered") : F("continuous"));
|
||||
|
||||
if (_isTriggeredOperationMode)
|
||||
{
|
||||
temp = user.createNestedArray(F("INA226 triggered"));
|
||||
temp.add(_measurementTriggered ? F("waiting for measurement") : F(""));
|
||||
}
|
||||
#endif
|
||||
|
||||
JsonArray jsonCurrent = user.createNestedArray(F("Current"));
|
||||
JsonArray jsonVoltage = user.createNestedArray(F("Voltage"));
|
||||
JsonArray jsonPower = user.createNestedArray(F("Power"));
|
||||
JsonArray jsonShuntVoltage = user.createNestedArray(F("Shunt Voltage"));
|
||||
JsonArray jsonOverflow = user.createNestedArray(F("Overflow"));
|
||||
|
||||
if (_lastLoopCheck == 0)
|
||||
{
|
||||
jsonCurrent.add(F("Not read yet"));
|
||||
jsonVoltage.add(F("Not read yet"));
|
||||
jsonPower.add(F("Not read yet"));
|
||||
jsonShuntVoltage.add(F("Not read yet"));
|
||||
jsonOverflow.add(F("Not read yet"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (_lastStatus != 0)
|
||||
{
|
||||
jsonCurrent.add(F("An error occurred"));
|
||||
jsonVoltage.add(F("An error occurred"));
|
||||
jsonPower.add(F("An error occurred"));
|
||||
jsonShuntVoltage.add(F("An error occurred"));
|
||||
jsonOverflow.add(F("An error occurred"));
|
||||
return;
|
||||
}
|
||||
|
||||
jsonCurrent.add(_lastCurrent);
|
||||
jsonCurrent.add(F("A"));
|
||||
|
||||
jsonVoltage.add(_lastVoltage);
|
||||
jsonVoltage.add(F("V"));
|
||||
|
||||
jsonPower.add(_lastPower);
|
||||
jsonPower.add(F("W"));
|
||||
|
||||
jsonShuntVoltage.add(_lastShuntVoltage);
|
||||
jsonShuntVoltage.add(F("V"));
|
||||
|
||||
jsonOverflow.add(_lastOverflow ? F("true") : F("false"));
|
||||
}
|
||||
|
||||
void addToConfig(JsonObject &root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name));
|
||||
top[F("Enabled")] = _settingEnabled;
|
||||
top[F("I2CAddress")] = static_cast<uint8_t>(_i2cAddress);
|
||||
top[F("CheckInterval")] = _checkInterval / 1000;
|
||||
top[F("INASamples")] = _settingInaSamples;
|
||||
top[F("INAConversionTime")] = _settingInaConversionTimeUs << 2;
|
||||
top[F("Decimals")] = log10f(_decimalFactor);
|
||||
top[F("ShuntResistor")] = _shuntResistor;
|
||||
top[F("CurrentRange")] = _currentRange;
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
top[F("MqttPublish")] = _mqttPublish;
|
||||
top[F("MqttPublishAlways")] = _mqttPublishAlways;
|
||||
top[F("MqttHomeAssistantDiscovery")] = _mqttHomeAssistant;
|
||||
#endif
|
||||
|
||||
DEBUG_PRINTLN(F("INA226 config saved."));
|
||||
}
|
||||
|
||||
bool readFromConfig(JsonObject &root) override
|
||||
{
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
|
||||
bool configComplete = !top.isNull();
|
||||
if (!configComplete)
|
||||
return false;
|
||||
|
||||
bool tmpBool;
|
||||
if (getJsonValue(top[F("Enabled")], tmpBool))
|
||||
_settingEnabled = tmpBool;
|
||||
else
|
||||
configComplete = false;
|
||||
|
||||
configComplete &= getJsonValue(top[F("I2CAddress")], _i2cAddress);
|
||||
if (getJsonValue(top[F("CheckInterval")], _checkInterval))
|
||||
{
|
||||
if (1 <= _checkInterval && _checkInterval <= 600)
|
||||
_checkInterval *= 1000;
|
||||
else
|
||||
_checkInterval = DEFAULT_CHECKINTERVAL;
|
||||
}
|
||||
else
|
||||
configComplete = false;
|
||||
|
||||
uint16_t tmpShort;
|
||||
if (getJsonValue(top[F("INASamples")], tmpShort))
|
||||
{
|
||||
// The method below will fix the provided value to a valid one
|
||||
getAverageEnum(tmpShort);
|
||||
_settingInaSamples = tmpShort;
|
||||
}
|
||||
else
|
||||
configComplete = false;
|
||||
|
||||
if (getJsonValue(top[F("INAConversionTime")], tmpShort))
|
||||
{
|
||||
// The method below will fix the provided value to a valid one
|
||||
getConversionTimeEnum(tmpShort);
|
||||
_settingInaConversionTimeUs = tmpShort >> 2;
|
||||
}
|
||||
else
|
||||
configComplete = false;
|
||||
|
||||
if (getJsonValue(top[F("Decimals")], _decimalFactor))
|
||||
{
|
||||
if (0 <= _decimalFactor && _decimalFactor <= 5)
|
||||
_decimalFactor = pow10f(_decimalFactor);
|
||||
else
|
||||
_decimalFactor = 100;
|
||||
}
|
||||
else
|
||||
configComplete = false;
|
||||
|
||||
configComplete &= getJsonValue(top[F("ShuntResistor")], _shuntResistor);
|
||||
configComplete &= getJsonValue(top[F("CurrentRange")], _currentRange);
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
if (getJsonValue(top[F("MqttPublish")], tmpBool))
|
||||
_mqttPublish = tmpBool;
|
||||
else
|
||||
configComplete = false;
|
||||
|
||||
if (getJsonValue(top[F("MqttPublishAlways")], tmpBool))
|
||||
_mqttPublishAlways = tmpBool;
|
||||
else
|
||||
configComplete = false;
|
||||
|
||||
if (getJsonValue(top[F("MqttHomeAssistantDiscovery")], tmpBool))
|
||||
_mqttHomeAssistant = tmpBool;
|
||||
else
|
||||
configComplete = false;
|
||||
#endif
|
||||
|
||||
if (_initDone)
|
||||
{
|
||||
initializeINA226();
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
mqttInitialize();
|
||||
#endif
|
||||
}
|
||||
|
||||
_initDone = true;
|
||||
return configComplete;
|
||||
}
|
||||
};
|
||||
|
||||
const char UsermodINA226::_name[] PROGMEM = "INA226";
|
@ -183,7 +183,6 @@ constexpr uint16_t samplesFFT_2 = 256; // meaningfull part of FFT resul
|
||||
// These are the input and output vectors. Input vectors receive computed results from FFT.
|
||||
static float vReal[samplesFFT] = {0.0f}; // FFT sample inputs / freq output - these are our raw result bins
|
||||
static float vImag[samplesFFT] = {0.0f}; // imaginary parts
|
||||
static float windowWeighingFactors[samplesFFT] = {0.0f};
|
||||
|
||||
// Create FFT object
|
||||
// lib_deps += https://github.com/kosme/arduinoFFT#develop @ 1.9.2
|
||||
@ -196,7 +195,8 @@ static float windowWeighingFactors[samplesFFT] = {0.0f};
|
||||
|
||||
#include <arduinoFFT.h>
|
||||
|
||||
static ArduinoFFT<float> FFT = ArduinoFFT<float>( vReal, vImag, samplesFFT, SAMPLE_RATE, windowWeighingFactors);
|
||||
/* Create FFT object with weighing factor storage */
|
||||
static ArduinoFFT<float> FFT = ArduinoFFT<float>( vReal, vImag, samplesFFT, SAMPLE_RATE, true);
|
||||
|
||||
// Helper functions
|
||||
|
||||
@ -282,6 +282,7 @@ void FFTcode(void * parameter)
|
||||
//FFT.windowing(FFTWindow::Blackman_Harris, FFTDirection::Forward); // Weigh data using "Blackman- Harris" window - sharp peaks due to excellent sideband rejection
|
||||
FFT.compute( FFTDirection::Forward ); // Compute FFT
|
||||
FFT.complexToMagnitude(); // Compute magnitudes
|
||||
vReal[0] = 0; // The remaining DC offset on the signal produces a strong spike on position 0 that should be eliminated to avoid issues.
|
||||
|
||||
FFT.majorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant
|
||||
FFT_MajorPeak = constrain(FFT_MajorPeak, 1.0f, 11025.0f); // restrict value to range expected by effects
|
||||
@ -1121,6 +1122,11 @@ class AudioReactive : public Usermod {
|
||||
delay(100); // Give that poor microphone some time to setup.
|
||||
|
||||
useBandPassFilter = false;
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
if ((i2sckPin == I2S_PIN_NO_CHANGE) && (i2ssdPin >= 0) && (i2swsPin >= 0) && ((dmType == 1) || (dmType == 4)) ) dmType = 5; // dummy user support: SCK == -1 --means--> PDM microphone
|
||||
#endif
|
||||
|
||||
switch (dmType) {
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// stub cases for not-yet-supported I2S modes on other ESP32 chips
|
||||
|
@ -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, 4))
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)) && (ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(4, 4, 6))
|
||||
// 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)
|
||||
@ -770,4 +770,4 @@ class SPH0654 : public I2SSource {
|
||||
#endif
|
||||
}
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
|
@ -27,18 +27,11 @@ Currently ESP8266 is not supported, due to low speed and small RAM of this chip.
|
||||
There are however plans to create a lightweight audioreactive for the 8266, with reduced features.
|
||||
## Installation
|
||||
|
||||
### using customised _arduinoFFT_ library for use with this usermod
|
||||
Add `-D USERMOD_AUDIOREACTIVE` to your PlatformIO environment `build_flags`, as well as `https://github.com/blazoncek/arduinoFFT.git` to your `lib_deps`.
|
||||
If you are not using PlatformIO (which you should) try adding `#define USERMOD_AUDIOREACTIVE` to *my_config.h* and make sure you have _arduinoFFT_ library downloaded and installed.
|
||||
### using latest _arduinoFFT_ library version 2.x
|
||||
The latest arduinoFFT release version should be used for audioreactive.
|
||||
|
||||
Customised _arduinoFFT_ library for use with this usermod can be found at https://github.com/blazoncek/arduinoFFT.git
|
||||
|
||||
### using latest (develop) _arduinoFFT_ library
|
||||
Alternatively, you can use the latest arduinoFFT development version.
|
||||
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`
|
||||
* `build_flags` = `-D USERMOD_AUDIOREACTIVE`
|
||||
* `lib_deps`= `kosme/arduinoFFT @ 2.0.1`
|
||||
|
||||
## Configuration
|
||||
|
||||
|
@ -667,7 +667,7 @@ void MultiRelay::addToJsonInfo(JsonObject &root) {
|
||||
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||
if (_relay[i].pin<0 || !_relay[i].external) continue;
|
||||
uiDomString = F("Relay "); uiDomString += i;
|
||||
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
|
||||
infoArr = user.createNestedArray(uiDomString); // timer value
|
||||
|
||||
uiDomString = F("<button class=\"btn btn-xs\" onclick=\"requestJson({");
|
||||
uiDomString += FPSTR(_name);
|
||||
|
@ -1135,10 +1135,12 @@ bool FourLineDisplayUsermod::handleButton(uint8_t b) {
|
||||
return handled;
|
||||
}
|
||||
|
||||
#if CONFIG_FREERTOS_UNICORE
|
||||
#define ARDUINO_RUNNING_CORE 0
|
||||
#else
|
||||
#define ARDUINO_RUNNING_CORE 1
|
||||
#ifndef ARDUINO_RUNNING_CORE
|
||||
#if CONFIG_FREERTOS_UNICORE
|
||||
#define ARDUINO_RUNNING_CORE 0
|
||||
#else
|
||||
#define ARDUINO_RUNNING_CORE 1
|
||||
#endif
|
||||
#endif
|
||||
void FourLineDisplayUsermod::onUpdateBegin(bool init) {
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
|
||||
|
@ -2506,7 +2506,7 @@ uint16_t ripple_base()
|
||||
uint16_t cx = rippleorigin >> 8;
|
||||
uint16_t cy = rippleorigin & 0xFF;
|
||||
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));
|
||||
if (propI > 0) SEGMENT.drawCircle(cx, cy, propI, color_blend(SEGMENT.getPixelColorXY(cx + propI, cy), col, mag), true);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
@ -3018,8 +3018,12 @@ uint16_t mode_bouncing_balls(void) {
|
||||
}
|
||||
|
||||
int pos = roundf(balls[i].height * (SEGLEN - 1));
|
||||
#ifdef WLED_USE_AA_PIXELS
|
||||
if (SEGLEN<32) SEGMENT.setPixelColor(indexToVStrip(pos, stripNr), color); // encode virtual strip into index
|
||||
else SEGMENT.setPixelColor(balls[i].height + (stripNr+1)*10.0f, color);
|
||||
#else
|
||||
SEGMENT.setPixelColor(indexToVStrip(pos, stripNr), color); // encode virtual strip into index
|
||||
#endif
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -3191,23 +3195,30 @@ static const char _data_FX_MODE_SINELON_RAINBOW[] PROGMEM = "Sinelon Rainbow@!,T
|
||||
|
||||
// utility function that will add random glitter to SEGMENT
|
||||
void glitter_base(uint8_t intensity, uint32_t col = ULTRAWHITE) {
|
||||
if (intensity > random8()) {
|
||||
if (SEGMENT.is2D()) {
|
||||
SEGMENT.setPixelColorXY(random16(SEGMENT.virtualWidth()),random16(SEGMENT.virtualHeight()), col);
|
||||
} else {
|
||||
SEGMENT.setPixelColor(random16(SEGLEN), col);
|
||||
}
|
||||
}
|
||||
if (intensity > random8()) SEGMENT.setPixelColor(random16(SEGLEN), col);
|
||||
}
|
||||
|
||||
//Glitter with palette background, inspired by https://gist.github.com/kriegsman/062e10f7f07ba8518af6
|
||||
uint16_t mode_glitter()
|
||||
{
|
||||
if (!SEGMENT.check2) mode_palette(); // use "* Color 1" palette for solid background (replacing "Solid glitter")
|
||||
if (!SEGMENT.check2) { // use "* Color 1" palette for solid background (replacing "Solid glitter")
|
||||
unsigned counter = 0;
|
||||
if (SEGMENT.speed != 0) {
|
||||
counter = (strip.now * ((SEGMENT.speed >> 3) +1)) & 0xFFFF;
|
||||
counter = counter >> 8;
|
||||
}
|
||||
|
||||
bool noWrap = (strip.paletteBlend == 2 || (strip.paletteBlend == 0 && SEGMENT.speed == 0));
|
||||
for (unsigned i = 0; i < SEGLEN; i++) {
|
||||
unsigned colorIndex = (i * 255 / SEGLEN) - counter;
|
||||
if (noWrap) colorIndex = map(colorIndex, 0, 255, 0, 240); //cut off blend at palette "end"
|
||||
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(colorIndex, false, true, 255));
|
||||
}
|
||||
}
|
||||
glitter_base(SEGMENT.intensity, SEGCOLOR(2) ? SEGCOLOR(2) : ULTRAWHITE);
|
||||
return FRAMETIME;
|
||||
}
|
||||
static const char _data_FX_MODE_GLITTER[] PROGMEM = "Glitter@!,!,,,,,Overlay;1,2,Glitter color;!;;pal=0,m12=0"; //pixels
|
||||
static const char _data_FX_MODE_GLITTER[] PROGMEM = "Glitter@!,!,,,,,Overlay;,,Glitter color;!;;pal=0,m12=0"; //pixels
|
||||
|
||||
|
||||
//Solid colour background with glitter (can be replaced by Glitter)
|
||||
@ -5050,25 +5061,25 @@ uint16_t mode_2Dfirenoise(void) { // firenoise2d. By Andrew Tuline
|
||||
SEGMENT.fill(BLACK);
|
||||
}
|
||||
|
||||
uint16_t xscale = SEGMENT.intensity*4;
|
||||
uint32_t yscale = SEGMENT.speed*8;
|
||||
uint8_t indexx = 0;
|
||||
unsigned xscale = SEGMENT.intensity*4;
|
||||
unsigned yscale = SEGMENT.speed*8;
|
||||
unsigned indexx = 0;
|
||||
|
||||
SEGPALETTE = CRGBPalette16( CRGB(0,0,0), CRGB(0,0,0), CRGB(0,0,0), CRGB(0,0,0),
|
||||
CRGB::Red, CRGB::Red, CRGB::Red, CRGB::DarkOrange,
|
||||
CRGB::DarkOrange,CRGB::DarkOrange, CRGB::Orange, CRGB::Orange,
|
||||
CRGB::Yellow, CRGB::Orange, CRGB::Yellow, CRGB::Yellow);
|
||||
CRGBPalette16 pal = SEGMENT.check1 ? SEGPALETTE : CRGBPalette16(CRGB::Black, CRGB::Black, CRGB::Black, CRGB::Black,
|
||||
CRGB::Red, CRGB::Red, CRGB::Red, CRGB::DarkOrange,
|
||||
CRGB::DarkOrange,CRGB::DarkOrange, CRGB::Orange, CRGB::Orange,
|
||||
CRGB::Yellow, CRGB::Orange, CRGB::Yellow, CRGB::Yellow);
|
||||
|
||||
for (int j=0; j < cols; j++) {
|
||||
for (int i=0; i < rows; i++) {
|
||||
indexx = inoise8(j*yscale*rows/255, i*xscale+strip.now/4); // We're moving along our Perlin map.
|
||||
SEGMENT.setPixelColorXY(j, i, ColorFromPalette(SEGPALETTE, min(i*(indexx)>>4, 255), i*255/cols, LINEARBLEND)); // With that value, look up the 8 bit colour palette value and assign it to the current LED.
|
||||
indexx = inoise8(j*yscale*rows/255, i*xscale+strip.now/4); // We're moving along our Perlin map.
|
||||
SEGMENT.setPixelColorXY(j, i, ColorFromPalette(pal, min(i*(indexx)>>4, 255U), i*255/cols, LINEARBLEND)); // With that value, look up the 8 bit colour palette value and assign it to the current LED.
|
||||
} // for i
|
||||
} // for j
|
||||
|
||||
return FRAMETIME;
|
||||
} // mode_2Dfirenoise()
|
||||
static const char _data_FX_MODE_2DFIRENOISE[] PROGMEM = "Firenoise@X scale,Y scale;;!;2";
|
||||
static const char _data_FX_MODE_2DFIRENOISE[] PROGMEM = "Firenoise@X scale,Y scale,,,,Palette;;!;2;pal=66";
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
@ -6052,8 +6063,8 @@ uint16_t mode_2Dfloatingblobs(void) {
|
||||
}
|
||||
}
|
||||
uint32_t c = SEGMENT.color_from_palette(blob->color[i], false, false, 0);
|
||||
if (blob->r[i] > 1.f) SEGMENT.fill_circle(blob->x[i], blob->y[i], roundf(blob->r[i]), c);
|
||||
else SEGMENT.setPixelColorXY(blob->x[i], blob->y[i], c);
|
||||
if (blob->r[i] > 1.f) SEGMENT.fillCircle(roundf(blob->x[i]), roundf(blob->y[i]), roundf(blob->r[i]), c);
|
||||
else SEGMENT.setPixelColorXY((int)roundf(blob->x[i]), (int)roundf(blob->y[i]), c);
|
||||
// move x
|
||||
if (blob->x[i] + blob->r[i] >= cols - 1) blob->x[i] += (blob->sX[i] * ((cols - 1 - blob->x[i]) / blob->r[i] + 0.005f));
|
||||
else if (blob->x[i] - blob->r[i] <= 0) blob->x[i] += (blob->sX[i] * (blob->x[i] / blob->r[i] + 0.005f));
|
||||
@ -6639,7 +6650,6 @@ 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
|
||||
@ -6651,12 +6661,13 @@ uint16_t mode_juggles(void) { // Juggles. By Andrew Tuline.
|
||||
uint16_t my_sampleAgc = fmax(fmin(volumeSmth, 255.0), 0);
|
||||
|
||||
for (size_t i=0; i<SEGMENT.intensity/32+1U; i++) {
|
||||
// if SEGLEN equals 1, we will always set color to the first and only pixel, but the effect is still good looking
|
||||
SEGMENT.setPixelColor(beatsin16(SEGMENT.speed/4+i*2,0,SEGLEN-1), color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(strip.now/4+i*2, false, PALETTE_SOLID_WRAP, 0), my_sampleAgc));
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
} // mode_juggles()
|
||||
static const char _data_FX_MODE_JUGGLES[] PROGMEM = "Juggles@!,# of balls;!,!;!;1v;m12=0,si=0"; // Pixels, Beatsin
|
||||
static const char _data_FX_MODE_JUGGLES[] PROGMEM = "Juggles@!,# of balls;!,!;!;01v;m12=0,si=0"; // Pixels, Beatsin
|
||||
|
||||
|
||||
//////////////////////
|
||||
@ -6757,7 +6768,7 @@ uint16_t mode_noisefire(void) { // Noisefire. By Andrew Tuline.
|
||||
|
||||
return FRAMETIME;
|
||||
} // mode_noisefire()
|
||||
static const char _data_FX_MODE_NOISEFIRE[] PROGMEM = "Noisefire@!,!;;;1v;m12=2,si=0"; // Circle, Beatsin
|
||||
static const char _data_FX_MODE_NOISEFIRE[] PROGMEM = "Noisefire@!,!;;;01v;m12=2,si=0"; // Circle, Beatsin
|
||||
|
||||
|
||||
///////////////////////
|
||||
@ -6867,7 +6878,7 @@ uint16_t mode_plasmoid(void) { // Plasmoid. By Andrew Tuline.
|
||||
|
||||
return FRAMETIME;
|
||||
} // mode_plasmoid()
|
||||
static const char _data_FX_MODE_PLASMOID[] PROGMEM = "Plasmoid@Phase,# of pixels;!,!;!;1v;sx=128,ix=128,m12=0,si=0"; // Pixels, Beatsin
|
||||
static const char _data_FX_MODE_PLASMOID[] PROGMEM = "Plasmoid@Phase,# of pixels;!,!;!;01v;sx=128,ix=128,m12=0,si=0"; // Pixels, Beatsin
|
||||
|
||||
|
||||
///////////////////////
|
||||
@ -7022,7 +7033,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();
|
||||
// No need to prevent from executing on single led strips, only mid will be set (mid = 0)
|
||||
const int mid = SEGLEN / 2;
|
||||
|
||||
um_data_t *um_data;
|
||||
@ -7043,13 +7054,14 @@ uint16_t mode_DJLight(void) { // Written by ??? Adapted by Wil
|
||||
CRGB color = CRGB(fftResult[15]/2, fftResult[5]/2, fftResult[0]/2); // 16-> 15 as 16 is out of bounds
|
||||
SEGMENT.setPixelColor(mid, color.fadeToBlackBy(map(fftResult[4], 0, 255, 255, 4))); // TODO - Update
|
||||
|
||||
// if SEGLEN equals 1 these loops won't execute
|
||||
for (int i = SEGLEN - 1; i > mid; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); // move to the left
|
||||
for (int i = 0; i < mid; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
} // mode_DJLight()
|
||||
static const char _data_FX_MODE_DJLIGHT[] PROGMEM = "DJ Light@Speed;;;1f;m12=2,si=0"; // Circle, Beatsin
|
||||
static const char _data_FX_MODE_DJLIGHT[] PROGMEM = "DJ Light@Speed;;;01f;m12=2,si=0"; // Circle, Beatsin
|
||||
|
||||
|
||||
////////////////////
|
||||
@ -7093,7 +7105,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();
|
||||
// No need to prevent from executing on single led strips, we simply change pixel 0 each time and avoid the shift
|
||||
um_data_t *um_data;
|
||||
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
|
||||
// add support for no audio
|
||||
@ -7136,12 +7148,13 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch
|
||||
|
||||
// shift the pixels one pixel up
|
||||
SEGMENT.setPixelColor(0, color);
|
||||
// if SEGLEN equals 1 this loop won't execute
|
||||
for (int i = SEGLEN - 1; i > 0; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
} // mode_freqmatrix()
|
||||
static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Speed,Sound effect,Low bin,High bin,Sensitivity;;;1f;m12=3,si=0"; // Corner, Beatsin
|
||||
static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Speed,Sound effect,Low bin,High bin,Sensitivity;;;01f;m12=3,si=0"; // Corner, Beatsin
|
||||
|
||||
|
||||
//////////////////////
|
||||
@ -7198,7 +7211,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();
|
||||
// As before, this effect can also work on single pixels, we just lose the shifting effect
|
||||
um_data_t *um_data;
|
||||
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
|
||||
// add support for no audio
|
||||
@ -7242,13 +7255,14 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun
|
||||
SEGMENT.setPixelColor(SEGLEN/2, color);
|
||||
|
||||
// shift the pixels one pixel outwards
|
||||
// if SEGLEN equals 1 these loops won't execute
|
||||
for (int i = SEGLEN - 1; i > SEGLEN/2; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left
|
||||
for (int i = 0; i < SEGLEN/2; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
} // mode_freqwave()
|
||||
static const char _data_FX_MODE_FREQWAVE[] PROGMEM = "Freqwave@Speed,Sound effect,Low bin,High bin,Pre-amp;;;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;;;01f;m12=2,si=0"; // Circle, Beatsin
|
||||
|
||||
|
||||
///////////////////////
|
||||
@ -7307,7 +7321,6 @@ static const char _data_FX_MODE_GRAVFREQ[] PROGMEM = "Gravfreq@Rate of fall,Sens
|
||||
// ** 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
|
||||
@ -7321,20 +7334,20 @@ uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuli
|
||||
uint8_t numBins = map(SEGMENT.intensity,0,255,0,16); // Map slider to fftResult bins.
|
||||
for (int i=0; i<numBins; i++) { // How many active bins are we using.
|
||||
uint16_t locn = inoise16(strip.now*SEGMENT.speed+i*50000, strip.now*SEGMENT.speed); // Get a new pixel location from moving noise.
|
||||
// if SEGLEN equals 1 locn will be always 0, hence we set the first pixel only
|
||||
locn = map(locn, 7500, 58000, 0, SEGLEN-1); // Map that to the length of the strand, and ensure we don't go over.
|
||||
SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i*64, false, PALETTE_SOLID_WRAP, 0), fftResult[i % 16]*4));
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
} // mode_noisemove()
|
||||
static const char _data_FX_MODE_NOISEMOVE[] PROGMEM = "Noisemove@Speed of perlin movement,Fade rate;!,!;!;1f;m12=0,si=0"; // Pixels, Beatsin
|
||||
static const char _data_FX_MODE_NOISEMOVE[] PROGMEM = "Noisemove@Speed of perlin movement,Fade rate;!,!;!;01f;m12=0,si=0"; // Pixels, Beatsin
|
||||
|
||||
|
||||
//////////////////////
|
||||
// ** 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
|
||||
@ -7362,12 +7375,13 @@ uint16_t mode_rocktaves(void) { // Rocktaves. Same note from eac
|
||||
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 will be always constrained between 0 and 0 if SEGLEN equals 1
|
||||
i = constrain(i, 0, SEGLEN-1);
|
||||
SEGMENT.addPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette((uint8_t)frTemp, false, PALETTE_SOLID_WRAP, 0), volTemp));
|
||||
|
||||
return FRAMETIME;
|
||||
} // mode_rocktaves()
|
||||
static const char _data_FX_MODE_ROCKTAVES[] PROGMEM = "Rocktaves@;!,!;!;1f;m12=1,si=0"; // Bar, Beatsin
|
||||
static const char _data_FX_MODE_ROCKTAVES[] PROGMEM = "Rocktaves@;!,!;!;01f;m12=1,si=0"; // Bar, Beatsin
|
||||
|
||||
|
||||
///////////////////////
|
||||
@ -7375,8 +7389,8 @@ 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 (SEGLEN == 1) return mode_static();
|
||||
|
||||
// effect can work on single pixels, we just lose the shifting effect
|
||||
|
||||
um_data_t *um_data;
|
||||
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
|
||||
// add support for no audio
|
||||
@ -7413,12 +7427,13 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin
|
||||
} else {
|
||||
SEGMENT.setPixelColor(SEGLEN-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(pixCol+SEGMENT.intensity, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude));
|
||||
}
|
||||
// loop will not execute if SEGLEN equals 1
|
||||
for (int i = 0; i < SEGLEN-1; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // shift left
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
} // mode_waterfall()
|
||||
static const char _data_FX_MODE_WATERFALL[] PROGMEM = "Waterfall@!,Adjust color,Select bin,Volume (min);!,!;!;1f;c2=0,m12=2,si=0"; // Circles, Beatsin
|
||||
static const char _data_FX_MODE_WATERFALL[] PROGMEM = "Waterfall@!,Adjust color,Select bin,Volume (min);!,!;!;01f;c2=0,m12=2,si=0"; // Circles, Beatsin
|
||||
|
||||
|
||||
#ifndef WLED_DISABLE_2D
|
||||
|
56
wled00/FX.h
@ -90,7 +90,7 @@
|
||||
//#define SEGCOLOR(x) strip._segments[strip.getCurrSegmentId()].currentColor(x, strip._segments[strip.getCurrSegmentId()].colors[x])
|
||||
//#define SEGLEN strip._segments[strip.getCurrSegmentId()].virtualLength()
|
||||
#define SEGCOLOR(x) strip.segColor(x) /* saves us a few kbytes of code */
|
||||
#define SEGPALETTE strip._currentPalette
|
||||
#define SEGPALETTE Segment::getCurrentPalette()
|
||||
#define SEGLEN strip._virtualSegmentLength /* saves us a few kbytes of code */
|
||||
#define SPEED_FORMULA_L (5U + (50U*(255U - SEGMENT.speed))/SEGLEN)
|
||||
|
||||
@ -106,6 +106,10 @@
|
||||
#define PURPLE (uint32_t)0x400080
|
||||
#define ORANGE (uint32_t)0xFF3000
|
||||
#define PINK (uint32_t)0xFF1493
|
||||
#define GREY (uint32_t)0x808080
|
||||
#define GRAY GREY
|
||||
#define DARKGREY (uint32_t)0x333333
|
||||
#define DARKGRAY DARKGREY
|
||||
#define ULTRAWHITE (uint32_t)0xFFFFFFFF
|
||||
#define DARKSLATEGRAY (uint32_t)0x2F4F4F
|
||||
#define DARKSLATEGREY DARKSLATEGRAY
|
||||
@ -320,7 +324,8 @@ typedef enum mapping1D2D {
|
||||
M12_Pixels = 0,
|
||||
M12_pBar = 1,
|
||||
M12_pArc = 2,
|
||||
M12_pCorner = 3
|
||||
M12_pCorner = 3,
|
||||
M12_sPinwheel = 4
|
||||
} mapping1D2D_t;
|
||||
|
||||
// segment, 80 bytes
|
||||
@ -413,6 +418,7 @@ typedef struct Segment {
|
||||
static uint16_t _usedSegmentData;
|
||||
|
||||
// perhaps this should be per segment, not static
|
||||
static CRGBPalette16 _currentPalette; // palette used for current effect (includes transition, used in color_from_palette())
|
||||
static CRGBPalette16 _randomPalette; // actual random palette
|
||||
static CRGBPalette16 _newRandomPalette; // target random palette
|
||||
static uint16_t _lastPaletteChange; // last random palette change time in millis()/1000
|
||||
@ -530,6 +536,7 @@ typedef struct Segment {
|
||||
static void modeBlend(bool blend) { _modeBlend = blend; }
|
||||
#endif
|
||||
static void handleRandomPalette();
|
||||
inline static const CRGBPalette16 &getCurrentPalette(void) { return Segment::_currentPalette; }
|
||||
|
||||
void setUp(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1);
|
||||
bool setColor(uint8_t slot, uint32_t c); //returns true if changed
|
||||
@ -567,7 +574,7 @@ typedef struct Segment {
|
||||
uint8_t currentMode(void); // currently active effect/mode (while in transition)
|
||||
uint32_t currentColor(uint8_t slot); // currently active segment color (blended while in transition)
|
||||
CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal);
|
||||
CRGBPalette16 ¤tPalette(CRGBPalette16 &tgt, uint8_t paletteID);
|
||||
void setCurrentPalette(void);
|
||||
|
||||
// 1D strip
|
||||
uint16_t virtualLength(void) const;
|
||||
@ -575,12 +582,14 @@ typedef struct Segment {
|
||||
inline void setPixelColor(unsigned n, uint32_t c) { setPixelColor(int(n), c); }
|
||||
inline void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); }
|
||||
inline void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); }
|
||||
#ifdef WLED_USE_AA_PIXELS
|
||||
void setPixelColor(float i, uint32_t c, bool aa = true);
|
||||
inline void setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = true) { setPixelColor(i, RGBW32(r,g,b,w), aa); }
|
||||
inline void setPixelColor(float i, CRGB c, bool aa = true) { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); }
|
||||
#endif
|
||||
uint32_t getPixelColor(int i);
|
||||
// 1D support functions (some implement 2D as well)
|
||||
void blur(uint8_t);
|
||||
void blur(uint8_t, bool smear = false);
|
||||
void fill(uint32_t c);
|
||||
void fade_out(uint8_t r);
|
||||
void fadeToBlackBy(uint8_t fadeBy);
|
||||
@ -603,10 +612,13 @@ typedef struct Segment {
|
||||
inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); }
|
||||
inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); }
|
||||
inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); }
|
||||
inline void setPixelColorXY(unsigned x, unsigned y, CRGB c) { setPixelColorXY(int(x), int(y), RGBW32(c.r,c.g,c.b,0)); }
|
||||
#ifdef WLED_USE_AA_PIXELS
|
||||
void setPixelColorXY(float x, float y, uint32_t c, bool aa = true);
|
||||
inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColorXY(x, y, RGBW32(r,g,b,w), aa); }
|
||||
inline void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), aa); }
|
||||
uint32_t getPixelColorXY(uint16_t x, uint16_t y);
|
||||
#endif
|
||||
uint32_t getPixelColorXY(int x, int y);
|
||||
// 2D support functions
|
||||
inline void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend) { setPixelColorXY(x, y, color_blend(getPixelColorXY(x,y), color, blend)); }
|
||||
inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), blend); }
|
||||
@ -615,32 +627,35 @@ typedef struct Segment {
|
||||
inline void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), fast); }
|
||||
inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), fade, true)); }
|
||||
void box_blur(uint16_t i, bool vertical, fract8 blur_amount); // 1D box blur (with weight)
|
||||
void blurRow(uint16_t row, fract8 blur_amount);
|
||||
void blurCol(uint16_t col, fract8 blur_amount);
|
||||
void blurRow(uint32_t row, fract8 blur_amount, bool smear = false);
|
||||
void blurCol(uint32_t col, fract8 blur_amount, bool smear = false);
|
||||
void moveX(int8_t delta, bool wrap = false);
|
||||
void moveY(int8_t delta, bool wrap = false);
|
||||
void move(uint8_t dir, uint8_t delta, bool wrap = false);
|
||||
void draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c);
|
||||
void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c);
|
||||
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c);
|
||||
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0)); } // automatic inline
|
||||
void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false);
|
||||
inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) { drawCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); }
|
||||
void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false);
|
||||
inline void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) { fillCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); }
|
||||
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft = false);
|
||||
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, bool soft = false) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0), soft); } // 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, int8_t rotate = 0);
|
||||
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0)); } // automatic inline
|
||||
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
|
||||
inline void blur2d(fract8 blur_amount) { blur(blur_amount); }
|
||||
inline void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); }
|
||||
void nscale8(uint8_t scale);
|
||||
#else
|
||||
inline uint16_t XY(uint16_t x, uint16_t y) { return x; }
|
||||
inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(x, c); }
|
||||
inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColor(int(x), c); }
|
||||
inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColor(x, RGBW32(r,g,b,w)); }
|
||||
inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0)); }
|
||||
inline void setPixelColorXY(unsigned x, unsigned y, CRGB c) { setPixelColor(int(x), RGBW32(c.r,c.g,c.b,0)); }
|
||||
#ifdef WLED_USE_AA_PIXELS
|
||||
inline void setPixelColorXY(float x, float y, uint32_t c, bool aa = true) { setPixelColor(x, c, aa); }
|
||||
inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColor(x, RGBW32(r,g,b,w), aa); }
|
||||
inline void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0), aa); }
|
||||
#endif
|
||||
inline uint32_t getPixelColorXY(uint16_t x, uint16_t y) { return getPixelColor(x); }
|
||||
inline void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t c, uint8_t blend) { blendPixelColor(x, c, blend); }
|
||||
inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColor(x, RGBW32(c.r,c.g,c.b,0), blend); }
|
||||
@ -649,14 +664,17 @@ typedef struct Segment {
|
||||
inline void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColor(x, RGBW32(c.r,c.g,c.b,0), fast); }
|
||||
inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { fadePixelColor(x, fade); }
|
||||
inline void box_blur(uint16_t i, bool vertical, fract8 blur_amount) {}
|
||||
inline void blurRow(uint16_t row, fract8 blur_amount) {}
|
||||
inline void blurCol(uint16_t col, fract8 blur_amount) {}
|
||||
inline void blurRow(uint32_t row, fract8 blur_amount, bool smear = false) {}
|
||||
inline void blurCol(uint32_t col, fract8 blur_amount, bool smear = false) {}
|
||||
inline void moveX(int8_t delta, bool wrap = false) {}
|
||||
inline void moveY(int8_t delta, bool wrap = false) {}
|
||||
inline void move(uint8_t dir, uint8_t delta, bool wrap = false) {}
|
||||
inline void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c) {}
|
||||
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) {}
|
||||
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) {}
|
||||
inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false) {}
|
||||
inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) {}
|
||||
inline void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false) {}
|
||||
inline void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) {}
|
||||
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft = false) {}
|
||||
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, bool soft = false) {}
|
||||
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t = 0, int8_t = 0) {}
|
||||
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB color) {}
|
||||
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0) {}
|
||||
@ -691,7 +709,6 @@ class WS2812FX { // 96 bytes
|
||||
panels(1),
|
||||
#endif
|
||||
// semi-private (just obscured) used in effect functions through macros
|
||||
_currentPalette(CRGBPalette16(CRGB::Black)),
|
||||
_colors_t{0,0,0},
|
||||
_virtualSegmentLength(0),
|
||||
// true private variables
|
||||
@ -886,7 +903,6 @@ class WS2812FX { // 96 bytes
|
||||
// end 2D support
|
||||
|
||||
void loadCustomPalettes(void); // loads custom palettes from JSON
|
||||
CRGBPalette16 _currentPalette; // palette used for current effect (includes transition)
|
||||
std::vector<CRGBPalette16> customPalettes; // TODO: move custom palettes out of WS2812FX class
|
||||
|
||||
// using public variables to reduce code size increase due to inline function getSegment() (with bounds checking)
|
||||
|
@ -110,11 +110,11 @@ void WS2812FX::setUpMatrix() {
|
||||
releaseJSONBufferLock();
|
||||
}
|
||||
|
||||
uint16_t x, y, pix=0; //pixel
|
||||
unsigned x, y, pix=0; //pixel
|
||||
for (size_t pan = 0; pan < panel.size(); pan++) {
|
||||
Panel &p = panel[pan];
|
||||
uint16_t h = p.vertical ? p.height : p.width;
|
||||
uint16_t v = p.vertical ? p.width : p.height;
|
||||
unsigned h = p.vertical ? p.height : p.width;
|
||||
unsigned v = p.vertical ? p.width : p.height;
|
||||
for (size_t j = 0; j < v; j++){
|
||||
for(size_t i = 0; i < h; i++) {
|
||||
y = (p.vertical?p.rightStart:p.bottomStart) ? v-j-1 : j;
|
||||
@ -163,8 +163,8 @@ void WS2812FX::setUpMatrix() {
|
||||
// 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 (can be 0 if segment is inactive)
|
||||
uint16_t height = virtualHeight(); // segment height in logical pixels (is always >= 1)
|
||||
unsigned width = virtualWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
|
||||
unsigned height = virtualHeight(); // segment height in logical pixels (is always >= 1)
|
||||
return isActive() ? (x%width) + (y%height) * width : 0;
|
||||
}
|
||||
|
||||
@ -175,16 +175,12 @@ void IRAM_ATTR Segment::setPixelColorXY(int x, int y, uint32_t col)
|
||||
|
||||
uint8_t _bri_t = currentBri();
|
||||
if (_bri_t < 255) {
|
||||
byte r = scale8(R(col), _bri_t);
|
||||
byte g = scale8(G(col), _bri_t);
|
||||
byte b = scale8(B(col), _bri_t);
|
||||
byte w = scale8(W(col), _bri_t);
|
||||
col = RGBW32(r, g, b, w);
|
||||
col = color_fade(col, _bri_t);
|
||||
}
|
||||
|
||||
if (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
|
||||
if (transpose) { unsigned t = x; x = y; y = t; } // swap X & Y if segment transposed
|
||||
|
||||
x *= groupLength(); // expand to physical pixels
|
||||
y *= groupLength(); // expand to physical pixels
|
||||
@ -193,7 +189,7 @@ void IRAM_ATTR Segment::setPixelColorXY(int x, int y, uint32_t col)
|
||||
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);
|
||||
unsigned xX = (x+g), yY = (y+j);
|
||||
if (xX >= width() || yY >= height()) continue; // we have reached one dimension's end
|
||||
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
@ -218,22 +214,23 @@ void IRAM_ATTR Segment::setPixelColorXY(int x, int y, uint32_t col)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WLED_USE_AA_PIXELS
|
||||
// anti-aliased version of setPixelColorXY()
|
||||
void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa)
|
||||
{
|
||||
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();
|
||||
const uint16_t rows = virtualHeight();
|
||||
const unsigned cols = virtualWidth();
|
||||
const unsigned rows = virtualHeight();
|
||||
|
||||
float fX = x * (cols-1);
|
||||
float fY = y * (rows-1);
|
||||
if (aa) {
|
||||
uint16_t xL = roundf(fX-0.49f);
|
||||
uint16_t xR = roundf(fX+0.49f);
|
||||
uint16_t yT = roundf(fY-0.49f);
|
||||
uint16_t yB = roundf(fY+0.49f);
|
||||
unsigned xL = roundf(fX-0.49f);
|
||||
unsigned xR = roundf(fX+0.49f);
|
||||
unsigned yT = roundf(fY-0.49f);
|
||||
unsigned yB = roundf(fY+0.49f);
|
||||
float dL = (fX - xL)*(fX - xL);
|
||||
float dR = (xR - fX)*(xR - fX);
|
||||
float dT = (fY - yT)*(fY - yT);
|
||||
@ -261,14 +258,15 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa)
|
||||
setPixelColorXY(uint16_t(roundf(fX)), uint16_t(roundf(fY)), col);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// returns RGBW values of pixel
|
||||
uint32_t IRAM_ATTR Segment::getPixelColorXY(uint16_t x, uint16_t y) {
|
||||
uint32_t IRAM_ATTR Segment::getPixelColorXY(int x, int y) {
|
||||
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
|
||||
if (transpose) { unsigned t = x; x = y; y = t; } // swap X & Y if segment transposed
|
||||
x *= groupLength(); // expand to physical pixels
|
||||
y *= groupLength(); // expand to physical pixels
|
||||
if (x >= width() || y >= height()) return 0;
|
||||
@ -276,119 +274,110 @@ uint32_t IRAM_ATTR Segment::getPixelColorXY(uint16_t x, uint16_t y) {
|
||||
}
|
||||
|
||||
// blurRow: perform a blur on a row of a rectangular matrix
|
||||
void Segment::blurRow(uint16_t row, fract8 blur_amount) {
|
||||
void Segment::blurRow(uint32_t row, fract8 blur_amount, bool smear){
|
||||
if (!isActive() || blur_amount == 0) return; // not active
|
||||
const uint_fast16_t cols = virtualWidth();
|
||||
const uint_fast16_t rows = virtualHeight();
|
||||
const unsigned cols = virtualWidth();
|
||||
const unsigned rows = virtualHeight();
|
||||
|
||||
if (row >= rows) return;
|
||||
// blur one row
|
||||
uint8_t keep = 255 - blur_amount;
|
||||
uint8_t keep = smear ? 255 : 255 - blur_amount;
|
||||
uint8_t seep = blur_amount >> 1;
|
||||
CRGB carryover = CRGB::Black;
|
||||
uint32_t carryover = BLACK;
|
||||
uint32_t lastnew;
|
||||
uint32_t last;
|
||||
uint32_t curnew = BLACK;
|
||||
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>0) {
|
||||
CRGB prev = CRGB(getPixelColorXY(x-1, row)) + part;
|
||||
setPixelColorXY(x-1, row, prev);
|
||||
}
|
||||
if (before != cur) // optimization: only set pixel if color has changed
|
||||
setPixelColorXY(x, row, cur);
|
||||
uint32_t cur = getPixelColorXY(x, row);
|
||||
uint32_t part = color_fade(cur, seep);
|
||||
curnew = color_fade(cur, keep);
|
||||
if (x > 0) {
|
||||
if (carryover)
|
||||
curnew = color_add(curnew, carryover, true);
|
||||
uint32_t prev = color_add(lastnew, part, true);
|
||||
if (last != prev) // optimization: only set pixel if color has changed
|
||||
setPixelColorXY(x - 1, row, prev);
|
||||
} else // first pixel
|
||||
setPixelColorXY(x, row, curnew);
|
||||
lastnew = curnew;
|
||||
last = cur; // save original value for comparison on next iteration
|
||||
carryover = part;
|
||||
}
|
||||
setPixelColorXY(cols-1, row, curnew); // set last pixel
|
||||
}
|
||||
|
||||
// blurCol: perform a blur on a column of a rectangular matrix
|
||||
void Segment::blurCol(uint16_t col, fract8 blur_amount) {
|
||||
void Segment::blurCol(uint32_t col, fract8 blur_amount, bool smear) {
|
||||
if (!isActive() || blur_amount == 0) return; // not active
|
||||
const uint_fast16_t cols = virtualWidth();
|
||||
const uint_fast16_t rows = virtualHeight();
|
||||
const unsigned cols = virtualWidth();
|
||||
const unsigned rows = virtualHeight();
|
||||
|
||||
if (col >= cols) return;
|
||||
// blur one column
|
||||
uint8_t keep = 255 - blur_amount;
|
||||
uint8_t keep = smear ? 255 : 255 - blur_amount;
|
||||
uint8_t seep = blur_amount >> 1;
|
||||
CRGB carryover = CRGB::Black;
|
||||
uint32_t carryover = BLACK;
|
||||
uint32_t lastnew;
|
||||
uint32_t last;
|
||||
uint32_t curnew = BLACK;
|
||||
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 (y>0) {
|
||||
CRGB prev = CRGB(getPixelColorXY(col, y-1)) + part;
|
||||
setPixelColorXY(col, y-1, prev);
|
||||
}
|
||||
if (before != cur) // optimization: only set pixel if color has changed
|
||||
setPixelColorXY(col, y, cur);
|
||||
carryover = part;
|
||||
uint32_t cur = getPixelColorXY(col, y);
|
||||
uint32_t part = color_fade(cur, seep);
|
||||
curnew = color_fade(cur, keep);
|
||||
if (y > 0) {
|
||||
if (carryover)
|
||||
curnew = color_add(curnew, carryover, true);
|
||||
uint32_t prev = color_add(lastnew, part, true);
|
||||
if (last != prev) // optimization: only set pixel if color has changed
|
||||
setPixelColorXY(col, y - 1, prev);
|
||||
} else // first pixel
|
||||
setPixelColorXY(col, y, curnew);
|
||||
lastnew = curnew;
|
||||
last = cur; //save original value for comparison on next iteration
|
||||
carryover = part;
|
||||
}
|
||||
setPixelColorXY(col, rows - 1, curnew);
|
||||
}
|
||||
|
||||
// 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;
|
||||
const uint16_t dim2 = vertical ? cols : rows;
|
||||
const int cols = virtualWidth();
|
||||
const int rows = virtualHeight();
|
||||
const int dim1 = vertical ? rows : cols;
|
||||
const int dim2 = vertical ? cols : rows;
|
||||
if (i >= dim2) return;
|
||||
const float seep = blur_amount/255.f;
|
||||
const float keep = 3.f - 2.f*seep;
|
||||
// 1D box blur
|
||||
CRGB tmp[dim1];
|
||||
uint32_t out[dim1], in[dim1];
|
||||
for (int j = 0; j < dim1; j++) {
|
||||
uint16_t x = vertical ? i : j;
|
||||
uint16_t y = vertical ? j : i;
|
||||
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);
|
||||
CRGB prev = (xp<0 || yp<0) ? CRGB::Black : getPixelColorXY(xp,yp);
|
||||
CRGB next = ((vertical && yn>=dim1) || (!vertical && xn>=dim1)) ? CRGB::Black : getPixelColorXY(xn,yn);
|
||||
uint16_t r, g, b;
|
||||
r = (curr.r*keep + (prev.r + next.r)*seep) / 3;
|
||||
g = (curr.g*keep + (prev.g + next.g)*seep) / 3;
|
||||
b = (curr.b*keep + (prev.b + next.b)*seep) / 3;
|
||||
tmp[j] = CRGB(r,g,b);
|
||||
int x = vertical ? i : j;
|
||||
int y = vertical ? j : i;
|
||||
in[j] = getPixelColorXY(x, y);
|
||||
}
|
||||
for (int j = 0; j < dim1; j++) {
|
||||
uint16_t x = vertical ? i : j;
|
||||
uint16_t y = vertical ? j : i;
|
||||
setPixelColorXY(x, y, tmp[j]);
|
||||
uint32_t curr = in[j];
|
||||
uint32_t prev = j > 0 ? in[j-1] : BLACK;
|
||||
uint32_t next = j < dim1-1 ? in[j+1] : BLACK;
|
||||
uint8_t r, g, b, w;
|
||||
r = (R(curr)*keep + (R(prev) + R(next))*seep) / 3;
|
||||
g = (G(curr)*keep + (G(prev) + G(next))*seep) / 3;
|
||||
b = (B(curr)*keep + (B(prev) + B(next))*seep) / 3;
|
||||
w = (W(curr)*keep + (W(prev) + W(next))*seep) / 3;
|
||||
out[j] = RGBW32(r,g,b,w);
|
||||
}
|
||||
for (int j = 0; j < dim1; j++) {
|
||||
int x = vertical ? i : j;
|
||||
int y = vertical ? j : i;
|
||||
setPixelColorXY(x, y, out[j]);
|
||||
}
|
||||
}
|
||||
|
||||
// blur1d: one-dimensional blur filter. Spreads light to 2 line neighbors.
|
||||
// blur2d: two-dimensional blur filter. Spreads light to 8 XY neighbors.
|
||||
//
|
||||
// 0 = no spread at all
|
||||
// 64 = moderate spreading
|
||||
// 172 = maximum smooth, even spreading
|
||||
//
|
||||
// 173..255 = wider spreading, but increasing flicker
|
||||
//
|
||||
// Total light is NOT entirely conserved, so many repeated
|
||||
// calls to 'blur' will also result in the light fading,
|
||||
// eventually all the way to black; this is by design so that
|
||||
// it can be used to (slowly) clear the LEDs to black.
|
||||
|
||||
void Segment::blur1d(fract8 blur_amount) {
|
||||
const uint16_t rows = virtualHeight();
|
||||
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();
|
||||
const int cols = virtualWidth();
|
||||
const int rows = virtualHeight();
|
||||
if (!delta || abs(delta) >= cols) return;
|
||||
uint32_t newPxCol[cols];
|
||||
for (int y = 0; y < rows; y++) {
|
||||
@ -405,8 +394,8 @@ 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();
|
||||
const int cols = virtualWidth();
|
||||
const int rows = virtualHeight();
|
||||
if (!delta || abs(delta) >= rows) return;
|
||||
uint32_t newPxCol[rows];
|
||||
for (int x = 0; x < cols; x++) {
|
||||
@ -439,69 +428,131 @@ void Segment::move(uint8_t dir, uint8_t delta, bool wrap) {
|
||||
}
|
||||
}
|
||||
|
||||
void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
|
||||
void Segment::drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, bool soft) {
|
||||
if (!isActive() || radius == 0) return; // not active
|
||||
// Bresenham’s Algorithm
|
||||
int d = 3 - (2*radius);
|
||||
int y = radius, x = 0;
|
||||
while (y >= x) {
|
||||
setPixelColorXY(cx+x, cy+y, col);
|
||||
setPixelColorXY(cx-x, cy+y, col);
|
||||
setPixelColorXY(cx+x, cy-y, col);
|
||||
setPixelColorXY(cx-x, cy-y, col);
|
||||
setPixelColorXY(cx+y, cy+x, col);
|
||||
setPixelColorXY(cx-y, cy+x, col);
|
||||
setPixelColorXY(cx+y, cy-x, col);
|
||||
setPixelColorXY(cx-y, cy-x, col);
|
||||
x++;
|
||||
if (d > 0) {
|
||||
y--;
|
||||
d += 4 * (x - y) + 10;
|
||||
} else {
|
||||
d += 4 * x + 6;
|
||||
if (soft) {
|
||||
// Xiaolin Wu’s algorithm
|
||||
int rsq = radius*radius;
|
||||
int x = 0;
|
||||
int y = radius;
|
||||
unsigned oldFade = 0;
|
||||
while (x < y) {
|
||||
float yf = sqrtf(float(rsq - x*x)); // needs to be floating point
|
||||
unsigned fade = float(0xFFFF) * (ceilf(yf) - yf); // how much color to keep
|
||||
if (oldFade > fade) y--;
|
||||
oldFade = fade;
|
||||
setPixelColorXY(cx+x, cy+y, color_blend(col, getPixelColorXY(cx+x, cy+y), fade, true));
|
||||
setPixelColorXY(cx-x, cy+y, color_blend(col, getPixelColorXY(cx-x, cy+y), fade, true));
|
||||
setPixelColorXY(cx+x, cy-y, color_blend(col, getPixelColorXY(cx+x, cy-y), fade, true));
|
||||
setPixelColorXY(cx-x, cy-y, color_blend(col, getPixelColorXY(cx-x, cy-y), fade, true));
|
||||
setPixelColorXY(cx+y, cy+x, color_blend(col, getPixelColorXY(cx+y, cy+x), fade, true));
|
||||
setPixelColorXY(cx-y, cy+x, color_blend(col, getPixelColorXY(cx-y, cy+x), fade, true));
|
||||
setPixelColorXY(cx+y, cy-x, color_blend(col, getPixelColorXY(cx+y, cy-x), fade, true));
|
||||
setPixelColorXY(cx-y, cy-x, color_blend(col, getPixelColorXY(cx-y, cy-x), fade, true));
|
||||
setPixelColorXY(cx+x, cy+y-1, color_blend(getPixelColorXY(cx+x, cy+y-1), col, fade, true));
|
||||
setPixelColorXY(cx-x, cy+y-1, color_blend(getPixelColorXY(cx-x, cy+y-1), col, fade, true));
|
||||
setPixelColorXY(cx+x, cy-y+1, color_blend(getPixelColorXY(cx+x, cy-y+1), col, fade, true));
|
||||
setPixelColorXY(cx-x, cy-y+1, color_blend(getPixelColorXY(cx-x, cy-y+1), col, fade, true));
|
||||
setPixelColorXY(cx+y-1, cy+x, color_blend(getPixelColorXY(cx+y-1, cy+x), col, fade, true));
|
||||
setPixelColorXY(cx-y+1, cy+x, color_blend(getPixelColorXY(cx-y+1, cy+x), col, fade, true));
|
||||
setPixelColorXY(cx+y-1, cy-x, color_blend(getPixelColorXY(cx+y-1, cy-x), col, fade, true));
|
||||
setPixelColorXY(cx-y+1, cy-x, color_blend(getPixelColorXY(cx-y+1, cy-x), col, fade, true));
|
||||
x++;
|
||||
}
|
||||
} else {
|
||||
// Bresenham’s Algorithm
|
||||
int d = 3 - (2*radius);
|
||||
int y = radius, x = 0;
|
||||
while (y >= x) {
|
||||
setPixelColorXY(cx+x, cy+y, col);
|
||||
setPixelColorXY(cx-x, cy+y, col);
|
||||
setPixelColorXY(cx+x, cy-y, col);
|
||||
setPixelColorXY(cx-x, cy-y, col);
|
||||
setPixelColorXY(cx+y, cy+x, col);
|
||||
setPixelColorXY(cx-y, cy+x, col);
|
||||
setPixelColorXY(cx+y, cy-x, col);
|
||||
setPixelColorXY(cx-y, cy-x, col);
|
||||
x++;
|
||||
if (d > 0) {
|
||||
y--;
|
||||
d += 4 * (x - y) + 10;
|
||||
} else {
|
||||
d += 4 * x + 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs
|
||||
void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
|
||||
void Segment::fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, bool soft) {
|
||||
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++) {
|
||||
for (int16_t x = -radius; x <= radius; x++) {
|
||||
// draw soft bounding circle
|
||||
if (soft) drawCircle(cx, cy, radius, col, soft);
|
||||
// fill it
|
||||
const int cols = virtualWidth();
|
||||
const int rows = virtualHeight();
|
||||
for (int y = -radius; y <= radius; y++) {
|
||||
for (int x = -radius; x <= radius; x++) {
|
||||
if (x * x + y * y <= radius * radius &&
|
||||
int16_t(cx)+x>=0 && int16_t(cy)+y>=0 &&
|
||||
int16_t(cx)+x<cols && int16_t(cy)+y<rows)
|
||||
int(cx)+x>=0 && int(cy)+y>=0 &&
|
||||
int(cx)+x<cols && int(cy)+y<rows)
|
||||
setPixelColorXY(cx + x, cy + y, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Segment::nscale8(uint8_t scale) {
|
||||
if (!isActive()) return; // not active
|
||||
const uint16_t cols = virtualWidth();
|
||||
const uint16_t rows = virtualHeight();
|
||||
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) {
|
||||
void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft) {
|
||||
if (!isActive()) return; // not active
|
||||
const uint16_t cols = virtualWidth();
|
||||
const uint16_t rows = virtualHeight();
|
||||
const int cols = virtualWidth();
|
||||
const int rows = virtualHeight();
|
||||
if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return;
|
||||
const int16_t dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
|
||||
const int16_t dy = abs(y1-y0), sy = y0<y1 ? 1 : -1;
|
||||
int16_t err = (dx>dy ? dx : -dy)/2, e2;
|
||||
for (;;) {
|
||||
setPixelColorXY(x0,y0,c);
|
||||
if (x0==x1 && y0==y1) break;
|
||||
e2 = err;
|
||||
if (e2 >-dx) { err -= dy; x0 += sx; }
|
||||
if (e2 < dy) { err += dx; y0 += sy; }
|
||||
|
||||
const int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1; // x distance & step
|
||||
const int dy = abs(y1-y0), sy = y0<y1 ? 1 : -1; // y distance & step
|
||||
|
||||
// single pixel (line length == 0)
|
||||
if (dx+dy == 0) {
|
||||
setPixelColorXY(x0, y0, c);
|
||||
return;
|
||||
}
|
||||
|
||||
if (soft) {
|
||||
// Xiaolin Wu’s algorithm
|
||||
const bool steep = dy > dx;
|
||||
if (steep) {
|
||||
// we need to go along longest dimension
|
||||
std::swap(x0,y0);
|
||||
std::swap(x1,y1);
|
||||
}
|
||||
if (x0 > x1) {
|
||||
// we need to go in increasing fashion
|
||||
std::swap(x0,x1);
|
||||
std::swap(y0,y1);
|
||||
}
|
||||
float gradient = x1-x0 == 0 ? 1.0f : float(y1-y0) / float(x1-x0);
|
||||
float intersectY = y0;
|
||||
for (int x = x0; x <= x1; x++) {
|
||||
unsigned keep = float(0xFFFF) * (intersectY-int(intersectY)); // how much color to keep
|
||||
unsigned seep = 0xFFFF - keep; // how much background to keep
|
||||
int y = int(intersectY);
|
||||
if (steep) std::swap(x,y); // temporaryly swap if steep
|
||||
// pixel coverage is determined by fractional part of y co-ordinate
|
||||
setPixelColorXY(x, y, color_blend(c, getPixelColorXY(x, y), keep, true));
|
||||
setPixelColorXY(x+int(steep), y+int(!steep), color_blend(c, getPixelColorXY(x+int(steep), y+int(!steep)), seep, true));
|
||||
intersectY += gradient;
|
||||
if (steep) std::swap(x,y); // restore if steep
|
||||
}
|
||||
} else {
|
||||
// Bresenham's algorithm
|
||||
int err = (dx>dy ? dx : -dy)/2; // error direction
|
||||
for (;;) {
|
||||
setPixelColorXY(x0, y0, c);
|
||||
if (x0==x1 && y0==y1) break;
|
||||
int e2 = err;
|
||||
if (e2 >-dx) { err -= dy; x0 += sx; }
|
||||
if (e2 < dy) { err += dx; y0 += sy; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -517,8 +568,8 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
|
||||
if (!isActive()) return; // not active
|
||||
if (chr < 32 || chr > 126) return; // only ASCII 32-126 supported
|
||||
chr -= 32; // align with font table entries
|
||||
const uint16_t cols = virtualWidth();
|
||||
const uint16_t rows = virtualHeight();
|
||||
const int cols = virtualWidth();
|
||||
const int rows = virtualHeight();
|
||||
const int font = w*h;
|
||||
|
||||
CRGB col = CRGB(color);
|
||||
@ -557,7 +608,7 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
|
||||
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;
|
||||
unsigned xx = x & 0xff, yy = y & 0xff, ix = 255 - xx, iy = 255 - yy;
|
||||
// calculate the intensities for each affected pixel
|
||||
uint8_t wu[4] = {WU_WEIGHT(ix, iy), WU_WEIGHT(xx, iy),
|
||||
WU_WEIGHT(ix, yy), WU_WEIGHT(xx, yy)};
|
||||
|
@ -77,6 +77,7 @@ uint16_t Segment::_usedSegmentData = 0U; // amount of RAM all segments use for t
|
||||
uint16_t Segment::maxWidth = DEFAULT_LED_COUNT;
|
||||
uint16_t Segment::maxHeight = 1;
|
||||
|
||||
CRGBPalette16 Segment::_currentPalette = CRGBPalette16(CRGB::Black);
|
||||
CRGBPalette16 Segment::_randomPalette = generateRandomPalette(); // was CRGBPalette16(DEFAULT_COLOR);
|
||||
CRGBPalette16 Segment::_newRandomPalette = generateRandomPalette(); // was CRGBPalette16(DEFAULT_COLOR);
|
||||
uint16_t Segment::_lastPaletteChange = 0; // perhaps it should be per segment
|
||||
@ -203,7 +204,7 @@ void Segment::resetIfRequired() {
|
||||
|
||||
CRGBPalette16 IRAM_ATTR &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
|
||||
if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0;
|
||||
if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0;
|
||||
if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0; // TODO remove strip dependency by moving customPalettes out of strip
|
||||
//default palette. Differs depending on effect
|
||||
if (pal == 0) switch (mode) {
|
||||
case FX_MODE_FIRE_2012 : pal = 35; break; // heat palette
|
||||
@ -327,15 +328,15 @@ void Segment::stopTransition() {
|
||||
}
|
||||
|
||||
void Segment::handleTransition() {
|
||||
uint16_t _progress = progress();
|
||||
unsigned _progress = progress();
|
||||
if (_progress == 0xFFFFU) stopTransition();
|
||||
}
|
||||
|
||||
// transition progression between 0-65535
|
||||
uint16_t IRAM_ATTR Segment::progress() {
|
||||
if (isInTransition()) {
|
||||
unsigned long timeNow = millis();
|
||||
if (_t->_dur > 0 && timeNow - _t->_start < _t->_dur) return (timeNow - _t->_start) * 0xFFFFU / _t->_dur;
|
||||
unsigned diff = millis() - _t->_start;
|
||||
if (_t->_dur > 0 && diff < _t->_dur) return diff * 0xFFFFU / _t->_dur;
|
||||
}
|
||||
return 0xFFFFU;
|
||||
}
|
||||
@ -412,9 +413,9 @@ void Segment::restoreSegenv(tmpsegd_t &tmpSeg) {
|
||||
#endif
|
||||
|
||||
uint8_t IRAM_ATTR Segment::currentBri(bool useCct) {
|
||||
uint32_t prog = progress();
|
||||
unsigned prog = progress();
|
||||
if (prog < 0xFFFFU) {
|
||||
uint32_t curBri = (useCct ? cct : (on ? opacity : 0)) * prog;
|
||||
unsigned curBri = (useCct ? cct : (on ? opacity : 0)) * prog;
|
||||
curBri += (useCct ? _t->_cctT : _t->_briT) * (0xFFFFU - prog);
|
||||
return curBri / 0xFFFFU;
|
||||
}
|
||||
@ -423,7 +424,7 @@ uint8_t IRAM_ATTR Segment::currentBri(bool useCct) {
|
||||
|
||||
uint8_t IRAM_ATTR Segment::currentMode() {
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
uint16_t prog = progress();
|
||||
unsigned prog = progress();
|
||||
if (modeBlending && prog < 0xFFFFU) return _t->_modeT;
|
||||
#endif
|
||||
return mode;
|
||||
@ -438,18 +439,17 @@ uint32_t IRAM_ATTR Segment::currentColor(uint8_t slot) {
|
||||
#endif
|
||||
}
|
||||
|
||||
CRGBPalette16 IRAM_ATTR &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
|
||||
loadPalette(targetPalette, pal);
|
||||
uint16_t prog = progress();
|
||||
void Segment::setCurrentPalette() {
|
||||
loadPalette(_currentPalette, palette);
|
||||
unsigned 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
|
||||
uint16_t noOfBlends = ((255U * prog) / 0xFFFFU) - _t->_prevPaletteBlends;
|
||||
for (int i=0; i<noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, targetPalette, 48);
|
||||
targetPalette = _t->_palT; // copy transitioning/temporary palette
|
||||
unsigned noOfBlends = ((255U * prog) / 0xFFFFU) - _t->_prevPaletteBlends;
|
||||
for (unsigned i = 0; i < noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, _currentPalette, 48);
|
||||
_currentPalette = _t->_palT; // copy transitioning/temporary palette
|
||||
}
|
||||
return targetPalette;
|
||||
}
|
||||
|
||||
// relies on WS2812FX::service() to call it for each frame
|
||||
@ -576,7 +576,7 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
|
||||
mode = fx;
|
||||
// load default values from effect string
|
||||
if (loadDefaults) {
|
||||
int16_t sOpt;
|
||||
int sOpt;
|
||||
sOpt = extractModeDefaults(fx, "sx"); speed = (sOpt >= 0) ? sOpt : DEFAULT_SPEED;
|
||||
sOpt = extractModeDefaults(fx, "ix"); intensity = (sOpt >= 0) ? sOpt : DEFAULT_INTENSITY;
|
||||
sOpt = extractModeDefaults(fx, "c1"); custom1 = (sOpt >= 0) ? sOpt : DEFAULT_C1;
|
||||
@ -610,21 +610,21 @@ void Segment::setPalette(uint8_t pal) {
|
||||
|
||||
// 2D matrix
|
||||
uint16_t IRAM_ATTR Segment::virtualWidth() const {
|
||||
uint16_t groupLen = groupLength();
|
||||
uint16_t vWidth = ((transpose ? height() : width()) + groupLen - 1) / groupLen;
|
||||
unsigned groupLen = groupLength();
|
||||
unsigned vWidth = ((transpose ? height() : width()) + groupLen - 1) / groupLen;
|
||||
if (mirror) vWidth = (vWidth + 1) /2; // divide by 2 if mirror, leave at least a single LED
|
||||
return vWidth;
|
||||
}
|
||||
|
||||
uint16_t IRAM_ATTR Segment::virtualHeight() const {
|
||||
uint16_t groupLen = groupLength();
|
||||
uint16_t vHeight = ((transpose ? width() : height()) + groupLen - 1) / groupLen;
|
||||
unsigned groupLen = groupLength();
|
||||
unsigned vHeight = ((transpose ? width() : height()) + groupLen - 1) / groupLen;
|
||||
if (mirror_y) vHeight = (vHeight + 1) /2; // divide by 2 if mirror, leave at least a single LED
|
||||
return vHeight;
|
||||
}
|
||||
|
||||
uint16_t IRAM_ATTR Segment::nrOfVStrips() const {
|
||||
uint16_t vLen = 1;
|
||||
unsigned vLen = 1;
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (is2D()) {
|
||||
switch (map1D2D) {
|
||||
@ -637,13 +637,49 @@ uint16_t IRAM_ATTR Segment::nrOfVStrips() const {
|
||||
return vLen;
|
||||
}
|
||||
|
||||
// Constants for mapping mode "Pinwheel"
|
||||
#ifndef WLED_DISABLE_2D
|
||||
constexpr int Pinwheel_Steps_Small = 72; // no holes up to 16x16
|
||||
constexpr int Pinwheel_Size_Small = 16; // larger than this -> use "Medium"
|
||||
constexpr int Pinwheel_Steps_Medium = 192; // no holes up to 32x32
|
||||
constexpr int Pinwheel_Size_Medium = 32; // larger than this -> use "Big"
|
||||
constexpr int Pinwheel_Steps_Big = 304; // no holes up to 50x50
|
||||
constexpr int Pinwheel_Size_Big = 50; // larger than this -> use "XL"
|
||||
constexpr int Pinwheel_Steps_XL = 368;
|
||||
constexpr float Int_to_Rad_Small = (DEG_TO_RAD * 360) / Pinwheel_Steps_Small; // conversion: from 0...72 to Radians
|
||||
constexpr float Int_to_Rad_Med = (DEG_TO_RAD * 360) / Pinwheel_Steps_Medium; // conversion: from 0...192 to Radians
|
||||
constexpr float Int_to_Rad_Big = (DEG_TO_RAD * 360) / Pinwheel_Steps_Big; // conversion: from 0...304 to Radians
|
||||
constexpr float Int_to_Rad_XL = (DEG_TO_RAD * 360) / Pinwheel_Steps_XL; // conversion: from 0...368 to Radians
|
||||
|
||||
constexpr int Fixed_Scale = 512; // fixpoint scaling factor (9bit for fraction)
|
||||
|
||||
// Pinwheel helper function: pixel index to radians
|
||||
static float getPinwheelAngle(int i, int vW, int vH) {
|
||||
int maxXY = max(vW, vH);
|
||||
if (maxXY <= Pinwheel_Size_Small) return float(i) * Int_to_Rad_Small;
|
||||
if (maxXY <= Pinwheel_Size_Medium) return float(i) * Int_to_Rad_Med;
|
||||
if (maxXY <= Pinwheel_Size_Big) return float(i) * Int_to_Rad_Big;
|
||||
// else
|
||||
return float(i) * Int_to_Rad_XL;
|
||||
}
|
||||
// Pinwheel helper function: matrix dimensions to number of rays
|
||||
static int getPinwheelLength(int vW, int vH) {
|
||||
int maxXY = max(vW, vH);
|
||||
if (maxXY <= Pinwheel_Size_Small) return Pinwheel_Steps_Small;
|
||||
if (maxXY <= Pinwheel_Size_Medium) return Pinwheel_Steps_Medium;
|
||||
if (maxXY <= Pinwheel_Size_Big) return Pinwheel_Steps_Big;
|
||||
// else
|
||||
return Pinwheel_Steps_XL;
|
||||
}
|
||||
#endif
|
||||
|
||||
// 1D strip
|
||||
uint16_t IRAM_ATTR Segment::virtualLength() const {
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (is2D()) {
|
||||
uint16_t vW = virtualWidth();
|
||||
uint16_t vH = virtualHeight();
|
||||
uint16_t vLen = vW * vH; // use all pixels from segment
|
||||
unsigned vW = virtualWidth();
|
||||
unsigned vH = virtualHeight();
|
||||
unsigned vLen = vW * vH; // use all pixels from segment
|
||||
switch (map1D2D) {
|
||||
case M12_pBar:
|
||||
vLen = vH;
|
||||
@ -652,12 +688,15 @@ uint16_t IRAM_ATTR Segment::virtualLength() const {
|
||||
case M12_pArc:
|
||||
vLen = max(vW,vH); // get the longest dimension
|
||||
break;
|
||||
case M12_sPinwheel:
|
||||
vLen = getPinwheelLength(vW, vH);
|
||||
break;
|
||||
}
|
||||
return vLen;
|
||||
}
|
||||
#endif
|
||||
uint16_t groupLen = groupLength(); // is always >= 1
|
||||
uint16_t vLength = (length() + groupLen - 1) / groupLen;
|
||||
unsigned groupLen = groupLength(); // is always >= 1
|
||||
unsigned vLength = (length() + groupLen - 1) / groupLen;
|
||||
if (mirror) vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED
|
||||
return vLength;
|
||||
}
|
||||
@ -674,8 +713,8 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
|
||||
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (is2D()) {
|
||||
uint16_t vH = virtualHeight(); // segment height in logical pixels
|
||||
uint16_t vW = virtualWidth();
|
||||
int vH = virtualHeight(); // segment height in logical pixels
|
||||
int vW = virtualWidth();
|
||||
switch (map1D2D) {
|
||||
case M12_Pixels:
|
||||
// use all available pixels as a long strip
|
||||
@ -718,6 +757,52 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
|
||||
for (int x = 0; x <= i; x++) setPixelColorXY(x, i, col);
|
||||
for (int y = 0; y < i; y++) setPixelColorXY(i, y, col);
|
||||
break;
|
||||
case M12_sPinwheel: {
|
||||
// i = angle --> 0 - 296 (Big), 0 - 192 (Medium), 0 - 72 (Small)
|
||||
float centerX = roundf((vW-1) / 2.0f);
|
||||
float centerY = roundf((vH-1) / 2.0f);
|
||||
float angleRad = getPinwheelAngle(i, vW, vH); // angle in radians
|
||||
float cosVal = cos_t(angleRad);
|
||||
float sinVal = sin_t(angleRad);
|
||||
|
||||
// avoid re-painting the same pixel
|
||||
int lastX = INT_MIN; // impossible position
|
||||
int lastY = INT_MIN; // impossible position
|
||||
// draw line at angle, starting at center and ending at the segment edge
|
||||
// we use fixed point math for better speed. Starting distance is 0.5 for better rounding
|
||||
// int_fast16_t and int_fast32_t types changed to int, minimum bits commented
|
||||
int posx = (centerX + 0.5f * cosVal) * Fixed_Scale; // X starting position in fixed point 18 bit
|
||||
int posy = (centerY + 0.5f * sinVal) * Fixed_Scale; // Y starting position in fixed point 18 bit
|
||||
int inc_x = cosVal * Fixed_Scale; // X increment per step (fixed point) 10 bit
|
||||
int inc_y = sinVal * Fixed_Scale; // Y increment per step (fixed point) 10 bit
|
||||
|
||||
int32_t maxX = vW * Fixed_Scale; // X edge in fixedpoint
|
||||
int32_t maxY = vH * Fixed_Scale; // Y edge in fixedpoint
|
||||
|
||||
// Odd rays start further from center if prevRay started at center.
|
||||
static int prevRay = INT_MIN; // previous ray number
|
||||
if ((i % 2 == 1) && (i - 1 == prevRay || i + 1 == prevRay)) {
|
||||
int jump = min(vW/3, vH/3); // can add 2 if using medium pinwheel
|
||||
posx += inc_x * jump;
|
||||
posy += inc_y * jump;
|
||||
}
|
||||
prevRay = i;
|
||||
|
||||
// draw ray until we hit any edge
|
||||
while ((posx >= 0) && (posy >= 0) && (posx < maxX) && (posy < maxY)) {
|
||||
// scale down to integer (compiler will replace division with appropriate bitshift)
|
||||
int x = posx / Fixed_Scale;
|
||||
int y = posy / Fixed_Scale;
|
||||
// set pixel
|
||||
if (x != lastX || y != lastY) setPixelColorXY(x, y, col); // only paint if pixel position is different
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
// advance to next position
|
||||
posx += inc_x;
|
||||
posy += inc_y;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else if (Segment::maxHeight!=1 && (width()==1 || height()==1)) {
|
||||
@ -732,14 +817,10 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
|
||||
}
|
||||
#endif
|
||||
|
||||
uint16_t len = length();
|
||||
unsigned len = length();
|
||||
uint8_t _bri_t = currentBri();
|
||||
if (_bri_t < 255) {
|
||||
byte r = scale8(R(col), _bri_t);
|
||||
byte g = scale8(G(col), _bri_t);
|
||||
byte b = scale8(B(col), _bri_t);
|
||||
byte w = scale8(W(col), _bri_t);
|
||||
col = RGBW32(r, g, b, w);
|
||||
col = color_fade(col, _bri_t);
|
||||
}
|
||||
|
||||
// expand pixel (taking into account start, grouping, spacing [and offset])
|
||||
@ -777,6 +858,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WLED_USE_AA_PIXELS
|
||||
// anti-aliased normalized version of setPixelColor()
|
||||
void Segment::setPixelColor(float i, uint32_t col, bool aa)
|
||||
{
|
||||
@ -788,8 +870,8 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa)
|
||||
|
||||
float fC = i * (virtualLength()-1);
|
||||
if (aa) {
|
||||
uint16_t iL = roundf(fC-0.49f);
|
||||
uint16_t iR = roundf(fC+0.49f);
|
||||
unsigned iL = roundf(fC-0.49f);
|
||||
unsigned iR = roundf(fC+0.49f);
|
||||
float dL = (fC - iL)*(fC - iL);
|
||||
float dR = (iR - fC)*(iR - fC);
|
||||
uint32_t cIL = getPixelColor(iL | (vStrip<<16));
|
||||
@ -806,9 +888,10 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa)
|
||||
setPixelColor(iL | (vStrip<<16), col);
|
||||
}
|
||||
} else {
|
||||
setPixelColor(uint16_t(roundf(fC)) | (vStrip<<16), col);
|
||||
setPixelColor(int(roundf(fC)) | (vStrip<<16), col);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t IRAM_ATTR Segment::getPixelColor(int i)
|
||||
{
|
||||
@ -820,8 +903,8 @@ uint32_t IRAM_ATTR Segment::getPixelColor(int i)
|
||||
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (is2D()) {
|
||||
uint16_t vH = virtualHeight(); // segment height in logical pixels
|
||||
uint16_t vW = virtualWidth();
|
||||
unsigned vH = virtualHeight(); // segment height in logical pixels
|
||||
unsigned vW = virtualWidth();
|
||||
switch (map1D2D) {
|
||||
case M12_Pixels:
|
||||
return getPixelColorXY(i % vW, i / vW);
|
||||
@ -835,7 +918,36 @@ uint32_t IRAM_ATTR Segment::getPixelColor(int i)
|
||||
// use longest dimension
|
||||
return vW>vH ? getPixelColorXY(i, 0) : getPixelColorXY(0, i);
|
||||
break;
|
||||
}
|
||||
case M12_sPinwheel:
|
||||
// not 100% accurate, returns pixel at outer edge
|
||||
// i = angle --> 0 - 296 (Big), 0 - 192 (Medium), 0 - 72 (Small)
|
||||
float centerX = roundf((vW-1) / 2.0f);
|
||||
float centerY = roundf((vH-1) / 2.0f);
|
||||
float angleRad = getPinwheelAngle(i, vW, vH); // angle in radians
|
||||
float cosVal = cos_t(angleRad);
|
||||
float sinVal = sin_t(angleRad);
|
||||
|
||||
int posx = (centerX + 0.5f * cosVal) * Fixed_Scale; // X starting position in fixed point 18 bit
|
||||
int posy = (centerY + 0.5f * sinVal) * Fixed_Scale; // Y starting position in fixed point 18 bit
|
||||
int inc_x = cosVal * Fixed_Scale; // X increment per step (fixed point) 10 bit
|
||||
int inc_y = sinVal * Fixed_Scale; // Y increment per step (fixed point) 10 bit
|
||||
int32_t maxX = vW * Fixed_Scale; // X edge in fixedpoint
|
||||
int32_t maxY = vH * Fixed_Scale; // Y edge in fixedpoint
|
||||
|
||||
// trace ray from center until we hit any edge - to avoid rounding problems, we use the same method as in setPixelColor
|
||||
int x = INT_MIN;
|
||||
int y = INT_MIN;
|
||||
while ((posx >= 0) && (posy >= 0) && (posx < maxX) && (posy < maxY)) {
|
||||
// scale down to integer (compiler will replace division with appropriate bitshift)
|
||||
x = posx / Fixed_Scale;
|
||||
y = posy / Fixed_Scale;
|
||||
// advance to next position
|
||||
posx += inc_x;
|
||||
posy += inc_y;
|
||||
}
|
||||
return getPixelColorXY(x, y);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@ -877,9 +989,9 @@ uint8_t Segment::differs(Segment& b) const {
|
||||
}
|
||||
|
||||
void Segment::refreshLightCapabilities() {
|
||||
uint8_t capabilities = 0;
|
||||
uint16_t segStartIdx = 0xFFFFU;
|
||||
uint16_t segStopIdx = 0;
|
||||
unsigned capabilities = 0;
|
||||
unsigned segStartIdx = 0xFFFFU;
|
||||
unsigned segStopIdx = 0;
|
||||
|
||||
if (!isActive()) {
|
||||
_capabilities = 0;
|
||||
@ -889,7 +1001,7 @@ void Segment::refreshLightCapabilities() {
|
||||
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++) {
|
||||
uint16_t index = strip.getMappedPixelIndex(x + Segment::maxWidth * y); // convert logical address to physical
|
||||
unsigned index = strip.getMappedPixelIndex(x + Segment::maxWidth * y); // convert logical address to physical
|
||||
if (index < 0xFFFFU) {
|
||||
if (segStartIdx > index) segStartIdx = index;
|
||||
if (segStopIdx < index) segStopIdx = index;
|
||||
@ -914,7 +1026,7 @@ void Segment::refreshLightCapabilities() {
|
||||
if (!cctFromRgb && bus->hasCCT()) capabilities |= SEG_CAPABILITY_CCT;
|
||||
if (correctWB && (bus->hasRGB() || bus->hasCCT())) capabilities |= SEG_CAPABILITY_CCT; //white balance correction (CCT slider)
|
||||
if (bus->hasWhite()) {
|
||||
uint8_t aWM = Bus::getGlobalAWMode() == AW_GLOBAL_DISABLED ? bus->getAutoWhiteMode() : Bus::getGlobalAWMode();
|
||||
unsigned aWM = Bus::getGlobalAWMode() == AW_GLOBAL_DISABLED ? bus->getAutoWhiteMode() : Bus::getGlobalAWMode();
|
||||
bool whiteSlider = (aWM == RGBW_MODE_DUAL || aWM == RGBW_MODE_MANUAL_ONLY); // white slider allowed
|
||||
// if auto white calculation from RGB is active (Accurate/Brighter), force RGB controls even if there are no RGB busses
|
||||
if (!whiteSlider) capabilities |= SEG_CAPABILITY_RGB;
|
||||
@ -930,8 +1042,8 @@ void Segment::refreshLightCapabilities() {
|
||||
*/
|
||||
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
|
||||
const int cols = is2D() ? virtualWidth() : virtualLength();
|
||||
const int rows = virtualHeight(); // will be 1 for 1D
|
||||
for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
|
||||
if (is2D()) setPixelColorXY(x, y, c);
|
||||
else setPixelColor(x, c);
|
||||
@ -943,8 +1055,8 @@ void Segment::fill(uint32_t c) {
|
||||
*/
|
||||
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
|
||||
const int cols = is2D() ? virtualWidth() : virtualLength();
|
||||
const int rows = virtualHeight(); // will be 1 for 1D
|
||||
|
||||
rate = (255-rate) >> 1;
|
||||
float mappedRate = float(rate) +1.1f;
|
||||
@ -981,8 +1093,8 @@ 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
|
||||
const int cols = is2D() ? virtualWidth() : virtualLength();
|
||||
const int rows = virtualHeight(); // will be 1 for 1D
|
||||
|
||||
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));
|
||||
@ -993,33 +1105,43 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) {
|
||||
/*
|
||||
* blurs segment content, source: FastLED colorutils.cpp
|
||||
*/
|
||||
void Segment::blur(uint8_t blur_amount) {
|
||||
void Segment::blur(uint8_t blur_amount, bool smear) {
|
||||
if (!isActive() || blur_amount == 0) return; // optimization: 0 means "don't blur"
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (is2D()) {
|
||||
// compatibility with 2D
|
||||
const 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
|
||||
for (unsigned i = 0; i < rows; i++) blurRow(i, blur_amount, smear); // blur all rows
|
||||
for (unsigned k = 0; k < cols; k++) blurCol(k, blur_amount, smear); // blur all columns
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
uint8_t keep = 255 - blur_amount;
|
||||
uint8_t keep = smear ? 255 : 255 - blur_amount;
|
||||
uint8_t seep = blur_amount >> 1;
|
||||
uint32_t carryover = BLACK;
|
||||
unsigned vlength = virtualLength();
|
||||
uint32_t carryover = BLACK;
|
||||
uint32_t lastnew;
|
||||
uint32_t last;
|
||||
uint32_t curnew = BLACK;
|
||||
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);
|
||||
curnew = color_fade(cur, keep);
|
||||
if (i > 0) {
|
||||
uint32_t c = getPixelColor(i-1);
|
||||
setPixelColor(i-1, color_add(c, part, true));
|
||||
if (carryover)
|
||||
curnew = color_add(curnew, carryover, true);
|
||||
uint32_t prev = color_add(lastnew, part, true);
|
||||
if (last != prev) // optimization: only set pixel if color has changed
|
||||
setPixelColor(i - 1, prev);
|
||||
}
|
||||
setPixelColor(i, cur);
|
||||
else // first pixel
|
||||
setPixelColor(i, curnew);
|
||||
lastnew = curnew;
|
||||
last = cur; // save original value for comparison on next iteration
|
||||
carryover = part;
|
||||
}
|
||||
setPixelColor(vlength - 1, curnew);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1057,13 +1179,11 @@ 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) return (pbri == 255) ? color : color_fade(color, pbri, true);
|
||||
|
||||
uint8_t paletteIndex = i;
|
||||
unsigned paletteIndex = i;
|
||||
if (mapping && virtualLength() > 1) paletteIndex = (i*255)/(virtualLength() -1);
|
||||
// paletteBlend: 0 - wrap when moving, 1 - always wrap, 2 - never wrap, 3 - none (undefined)
|
||||
if (!wrap && strip.paletteBlend != 3) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
|
||||
CRGBPalette16 curPal;
|
||||
curPal = currentPalette(curPal, palette);
|
||||
CRGB fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global
|
||||
CRGB fastled_col = ColorFromPalette(_currentPalette, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global
|
||||
|
||||
return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, W(color));
|
||||
}
|
||||
@ -1091,21 +1211,24 @@ void WS2812FX::finalizeInit(void) {
|
||||
//if busses failed to load, add default (fresh install, FS issue, ...)
|
||||
if (BusManager::getNumBusses() == 0) {
|
||||
DEBUG_PRINTLN(F("No busses, init default"));
|
||||
const uint8_t defDataPins[] = {DATA_PINS};
|
||||
const uint16_t defCounts[] = {PIXEL_COUNTS};
|
||||
const uint8_t defNumBusses = ((sizeof defDataPins) / (sizeof defDataPins[0]));
|
||||
const uint8_t defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0]));
|
||||
uint16_t prevLen = 0;
|
||||
for (int i = 0; i < defNumBusses && i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
|
||||
uint8_t defPin[] = {defDataPins[i]};
|
||||
const unsigned defDataPins[] = {DATA_PINS};
|
||||
const unsigned defCounts[] = {PIXEL_COUNTS};
|
||||
const unsigned defNumPins = ((sizeof defDataPins) / (sizeof defDataPins[0]));
|
||||
const unsigned defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0]));
|
||||
const unsigned defNumBusses = defNumPins > defNumCounts && defNumCounts > 1 && defNumPins%defNumCounts == 0 ? defNumCounts : defNumPins;
|
||||
const unsigned pinsPerBus = defNumPins / defNumBusses;
|
||||
unsigned prevLen = 0;
|
||||
for (unsigned i = 0; i < defNumBusses && i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
|
||||
uint8_t defPin[5]; // max 5 pins
|
||||
for (unsigned j = 0; j < pinsPerBus; j++) defPin[j] = defDataPins[i*pinsPerBus + j];
|
||||
// when booting without config (1st boot) we need to make sure GPIOs defined for LED output don't clash with hardware
|
||||
// i.e. DEBUG (GPIO1), DMX (2), SPI RAM/FLASH (16&17 on ESP32-WROVER/PICO), etc
|
||||
if (pinManager.isPinAllocated(defPin[0])) {
|
||||
defPin[0] = 1; // start with GPIO1 and work upwards
|
||||
while (pinManager.isPinAllocated(defPin[0]) && defPin[0] < WLED_NUM_PINS) defPin[0]++;
|
||||
}
|
||||
uint16_t start = prevLen;
|
||||
uint16_t count = defCounts[(i < defNumCounts) ? i : defNumCounts -1];
|
||||
unsigned start = prevLen;
|
||||
unsigned count = defCounts[(i < defNumCounts) ? i : defNumCounts -1];
|
||||
prevLen += count;
|
||||
BusConfig defCfg = BusConfig(DEFAULT_LED_TYPE, defPin, start, count, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY);
|
||||
if (BusManager::add(defCfg) == -1) break;
|
||||
@ -1121,7 +1244,7 @@ void WS2812FX::finalizeInit(void) {
|
||||
_hasWhiteChannel |= bus->hasWhite();
|
||||
//refresh is required to remain off if at least one of the strips requires the refresh.
|
||||
_isOffRefreshRequired |= bus->isOffRefreshRequired();
|
||||
uint16_t busEnd = bus->getStart() + bus->getLength();
|
||||
unsigned busEnd = bus->getStart() + bus->getLength();
|
||||
if (busEnd > _length) _length = busEnd;
|
||||
#ifdef ESP8266
|
||||
if ((!IS_DIGITAL(bus->getType()) || IS_2PIN(bus->getType()))) continue;
|
||||
@ -1165,15 +1288,15 @@ void WS2812FX::service() {
|
||||
if (nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC))
|
||||
{
|
||||
doShow = true;
|
||||
uint16_t delay = FRAMETIME;
|
||||
unsigned delay = FRAMETIME;
|
||||
|
||||
if (!seg.freeze) { //only run effect function if not frozen
|
||||
int16_t oldCCT = BusManager::getSegmentCCT(); // store original CCT value (actually it is not Segment based)
|
||||
int oldCCT = BusManager::getSegmentCCT(); // store original CCT value (actually it is not Segment based)
|
||||
_virtualSegmentLength = seg.virtualLength(); //SEGLEN
|
||||
_colors_t[0] = gamma32(seg.currentColor(0));
|
||||
_colors_t[1] = gamma32(seg.currentColor(1));
|
||||
_colors_t[2] = gamma32(seg.currentColor(2));
|
||||
seg.currentPalette(_currentPalette, seg.palette); // we need to pass reference
|
||||
seg.setCurrentPalette(); // load actual palette
|
||||
// when correctWB is true we need to correct/adjust RGB value according to desired CCT value, but it will also affect actual WW/CW ratio
|
||||
// when cctFromRgb is true we implicitly calculate WW and CW from RGB values
|
||||
if (cctFromRgb) BusManager::setSegmentCCT(-1);
|
||||
@ -1192,7 +1315,7 @@ void WS2812FX::service() {
|
||||
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
|
||||
unsigned 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
|
||||
@ -1216,7 +1339,7 @@ void WS2812FX::service() {
|
||||
#endif
|
||||
if (doShow) {
|
||||
yield();
|
||||
Segment::handleRandomPalette(); // slowly transtion random palette; move it into for loop when each segment has individual random palette
|
||||
Segment::handleRandomPalette(); // slowly transition random palette; move it into for loop when each segment has individual random palette
|
||||
show();
|
||||
}
|
||||
#ifdef WLED_DEBUG
|
||||
@ -1367,13 +1490,13 @@ uint8_t WS2812FX::getActiveSegmentsNum(void) {
|
||||
}
|
||||
|
||||
uint16_t WS2812FX::getLengthTotal(void) {
|
||||
uint16_t len = Segment::maxWidth * Segment::maxHeight; // will be _length for 1D (see finalizeInit()) but should cover whole matrix for 2D
|
||||
unsigned len = Segment::maxWidth * Segment::maxHeight; // will be _length for 1D (see finalizeInit()) but should cover whole matrix for 2D
|
||||
if (isMatrix && _length > len) len = _length; // for 2D with trailing strip
|
||||
return len;
|
||||
}
|
||||
|
||||
uint16_t WS2812FX::getLengthPhysical(void) {
|
||||
uint16_t len = 0;
|
||||
unsigned len = 0;
|
||||
for (size_t b = 0; b < BusManager::getNumBusses(); b++) {
|
||||
Bus *bus = BusManager::getBus(b);
|
||||
if (bus->getType() >= TYPE_NET_DDP_RGB) continue; //exclude non-physical network busses
|
||||
@ -1450,8 +1573,8 @@ void WS2812FX::resetSegments() {
|
||||
|
||||
void WS2812FX::makeAutoSegments(bool forceReset) {
|
||||
if (autoSegments) { //make one segment per bus
|
||||
uint16_t segStarts[MAX_NUM_SEGMENTS] = {0};
|
||||
uint16_t segStops [MAX_NUM_SEGMENTS] = {0};
|
||||
unsigned segStarts[MAX_NUM_SEGMENTS] = {0};
|
||||
unsigned segStops [MAX_NUM_SEGMENTS] = {0};
|
||||
size_t s = 0;
|
||||
|
||||
#ifndef WLED_DISABLE_2D
|
||||
@ -1470,7 +1593,7 @@ void WS2812FX::makeAutoSegments(bool forceReset) {
|
||||
segStops[s] = segStarts[s] + b->getLength();
|
||||
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (isMatrix && segStops[s] < Segment::maxWidth*Segment::maxHeight) continue; // ignore buses comprising matrix
|
||||
if (isMatrix && segStops[s] <= Segment::maxWidth*Segment::maxHeight) continue; // ignore buses comprising matrix
|
||||
if (isMatrix && segStarts[s] < Segment::maxWidth*Segment::maxHeight) segStarts[s] = Segment::maxWidth*Segment::maxHeight;
|
||||
#endif
|
||||
|
||||
@ -1498,6 +1621,7 @@ void WS2812FX::makeAutoSegments(bool forceReset) {
|
||||
for (size_t i = 1; i < s; i++) {
|
||||
_segments.push_back(Segment(segStarts[i], segStops[i]));
|
||||
}
|
||||
DEBUG_PRINTF_P(PSTR("%d auto segments created.\n"), _segments.size());
|
||||
|
||||
} else {
|
||||
|
||||
@ -1543,6 +1667,8 @@ void WS2812FX::fixInvalidSegments() {
|
||||
if (_segments[i].stop > _length) _segments[i].stop = _length;
|
||||
}
|
||||
}
|
||||
// if any segments were deleted free memory
|
||||
purgeSegments();
|
||||
// this is always called as the last step after finalizeInit(), update covered bus types
|
||||
for (segment &seg : _segments)
|
||||
seg.refreshLightCapabilities();
|
||||
@ -1573,7 +1699,8 @@ void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) {
|
||||
void WS2812FX::printSize() {
|
||||
size_t size = 0;
|
||||
for (const Segment &seg : _segments) size += seg.getSize();
|
||||
DEBUG_PRINTF_P(PSTR("Segments: %d -> %uB\n"), _segments.size(), size);
|
||||
DEBUG_PRINTF_P(PSTR("Segments: %d -> %u/%dB\n"), _segments.size(), size, Segment::getUsedSegmentData());
|
||||
for (const Segment &seg : _segments) DEBUG_PRINTF_P(PSTR(" Seg: %d,%d [A=%d, 2D=%d, RGB=%d, W=%d, CCT=%d]\n"), seg.width(), seg.height(), seg.isActive(), seg.is2D(), seg.hasRGB(), seg.hasWhite(), seg.isCCT());
|
||||
DEBUG_PRINTF_P(PSTR("Modes: %d*%d=%uB\n"), sizeof(mode_ptr), _mode.size(), (_mode.capacity()*sizeof(mode_ptr)));
|
||||
DEBUG_PRINTF_P(PSTR("Data: %d*%d=%uB\n"), sizeof(const char *), _modeData.size(), (_modeData.capacity()*sizeof(const char *)));
|
||||
DEBUG_PRINTF_P(PSTR("Map: %d*%d=%uB\n"), sizeof(uint16_t), (int)customMappingSize, customMappingSize*sizeof(uint16_t));
|
||||
@ -1656,12 +1783,18 @@ bool WS2812FX::deserializeMap(uint8_t n) {
|
||||
return false; // if file does not load properly then exit
|
||||
}
|
||||
|
||||
JsonObject root = pDoc->as<JsonObject>();
|
||||
// if we are loading default ledmap (at boot) set matrix width and height from the ledmap (compatible with WLED MM ledmaps)
|
||||
if (isMatrix && n == 0 && (!root[F("width")].isNull() || !root[F("height")].isNull())) {
|
||||
Segment::maxWidth = min(max(root[F("width")].as<int>(), 1), 128);
|
||||
Segment::maxHeight = min(max(root[F("height")].as<int>(), 1), 128);
|
||||
}
|
||||
|
||||
if (customMappingTable) delete[] customMappingTable;
|
||||
customMappingTable = new uint16_t[getLengthTotal()];
|
||||
|
||||
if (customMappingTable) {
|
||||
DEBUG_PRINT(F("Reading LED map from ")); DEBUG_PRINTLN(fileName);
|
||||
JsonObject root = pDoc->as<JsonObject>();
|
||||
JsonArray map = root[F("map")];
|
||||
if (!map.isNull() && map.size()) { // not an empty map
|
||||
customMappingSize = min((unsigned)map.size(), (unsigned)getLengthTotal());
|
||||
|
@ -425,6 +425,7 @@ BusPwm::BusPwm(BusConfig &bc)
|
||||
}
|
||||
_data = _pwmdata; // avoid malloc() and use stack
|
||||
_valid = true;
|
||||
DEBUG_PRINTF_P(PSTR("%successfully inited PWM strip with type %u and pins %u,%u,%u,%u,%u\n"), _valid?"S":"Uns", bc.type, _pins[0], _pins[1], _pins[2], _pins[3], _pins[4]);
|
||||
}
|
||||
|
||||
void BusPwm::setPixelColor(uint16_t pix, uint32_t c) {
|
||||
@ -466,7 +467,22 @@ void BusPwm::setPixelColor(uint16_t pix, uint32_t c) {
|
||||
//does no index check
|
||||
uint32_t BusPwm::getPixelColor(uint16_t pix) {
|
||||
if (!_valid) return 0;
|
||||
return RGBW32(_data[0], _data[1], _data[2], _data[3]);
|
||||
// TODO getting the reverse from CCT is involved (a quick approximation when CCT blending is ste to 0 implemented)
|
||||
switch (_type) {
|
||||
case TYPE_ANALOG_1CH: //one channel (white), relies on auto white calculation
|
||||
return RGBW32(0, 0, 0, _data[0]);
|
||||
case TYPE_ANALOG_2CH: //warm white + cold white
|
||||
if (cctICused) return RGBW32(0, 0, 0, _data[0]);
|
||||
else return RGBW32(0, 0, 0, _data[0] + _data[1]);
|
||||
case TYPE_ANALOG_5CH: //RGB + warm white + cold white
|
||||
if (cctICused) return RGBW32(_data[0], _data[1], _data[2], _data[3]);
|
||||
else return RGBW32(_data[0], _data[1], _data[2], _data[3] + _data[4]);
|
||||
case TYPE_ANALOG_4CH: //RGBW
|
||||
return RGBW32(_data[0], _data[1], _data[2], _data[3]);
|
||||
case TYPE_ANALOG_3CH: //standard dumb RGB
|
||||
return RGBW32(_data[0], _data[1], _data[2], 0);
|
||||
}
|
||||
return RGBW32(_data[0], _data[0], _data[0], _data[0]);
|
||||
}
|
||||
|
||||
#ifndef ESP8266
|
||||
@ -560,6 +576,7 @@ BusOnOff::BusOnOff(BusConfig &bc)
|
||||
pinMode(_pin, OUTPUT);
|
||||
_data = &_onoffdata; // avoid malloc() and use stack
|
||||
_valid = true;
|
||||
DEBUG_PRINTF_P(PSTR("%successfully inited On/Off strip with pin %u\n"), _valid?"S":"Uns", _pin);
|
||||
}
|
||||
|
||||
void BusOnOff::setPixelColor(uint16_t pix, uint32_t c) {
|
||||
@ -614,6 +631,7 @@ BusNetwork::BusNetwork(BusConfig &bc)
|
||||
_UDPchannels = _rgbw ? 4 : 3;
|
||||
_client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]);
|
||||
_valid = (allocData(_len * _UDPchannels) != nullptr);
|
||||
DEBUG_PRINTF_P(PSTR("%successfully inited virtual strip with type %u and IP %u.%u.%u.%u\n"), _valid?"S":"Uns", bc.type, bc.pins[0], bc.pins[1], bc.pins[2], bc.pins[3]);
|
||||
}
|
||||
|
||||
void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) {
|
||||
@ -714,9 +732,8 @@ void BusManager::setStatusPixel(uint32_t c) {
|
||||
|
||||
void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c) {
|
||||
for (unsigned i = 0; i < numBusses; i++) {
|
||||
Bus* b = busses[i];
|
||||
uint16_t bstart = b->getStart();
|
||||
if (pix < bstart || pix >= bstart + b->getLength()) continue;
|
||||
unsigned bstart = busses[i]->getStart();
|
||||
if (pix < bstart || pix >= bstart + busses[i]->getLength()) continue;
|
||||
busses[i]->setPixelColor(pix - bstart, c);
|
||||
}
|
||||
}
|
||||
@ -738,10 +755,9 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) {
|
||||
|
||||
uint32_t BusManager::getPixelColor(uint16_t pix) {
|
||||
for (unsigned i = 0; i < numBusses; i++) {
|
||||
Bus* b = busses[i];
|
||||
uint16_t bstart = b->getStart();
|
||||
if (pix < bstart || pix >= bstart + b->getLength()) continue;
|
||||
return b->getPixelColor(pix - bstart);
|
||||
unsigned bstart = busses[i]->getStart();
|
||||
if (pix < bstart || pix >= bstart + busses[i]->getLength()) continue;
|
||||
return busses[i]->getPixelColor(pix - bstart);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -17,6 +17,11 @@
|
||||
#if !defined(WLED_NO_I2S1_PIXELBUS) && (defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2))
|
||||
#define WLED_NO_I2S1_PIXELBUS
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
#ifdef WLED_USE_PARALLEL_I2S
|
||||
#warning Use less than 300 pixels per bus.
|
||||
#endif
|
||||
#endif
|
||||
// temporary end
|
||||
|
||||
//Hardware SPI Pins
|
||||
@ -74,11 +79,16 @@
|
||||
#define I_8266_U1_APA106_3 82
|
||||
#define I_8266_DM_APA106_3 83
|
||||
#define I_8266_BB_APA106_3 84
|
||||
//WS2805
|
||||
//WS2805 (RGBCW)
|
||||
#define I_8266_U0_2805_5 89
|
||||
#define I_8266_U1_2805_5 90
|
||||
#define I_8266_DM_2805_5 91
|
||||
#define I_8266_BB_2805_5 92
|
||||
//TM1914 (RGB)
|
||||
#define I_8266_U0_TM1914_3 99
|
||||
#define I_8266_U1_TM1914_3 100
|
||||
#define I_8266_DM_TM1914_3 101
|
||||
#define I_8266_BB_TM1914_3 102
|
||||
|
||||
/*** ESP32 Neopixel methods ***/
|
||||
//RGB
|
||||
@ -117,10 +127,14 @@
|
||||
#define I_32_RN_APA106_3 85
|
||||
#define I_32_I0_APA106_3 86
|
||||
#define I_32_I1_APA106_3 87
|
||||
//WS2805
|
||||
//WS2805 (RGBCW)
|
||||
#define I_32_RN_2805_5 93
|
||||
#define I_32_I0_2805_5 94
|
||||
#define I_32_I1_2805_5 95
|
||||
//TM1914 (RGB)
|
||||
#define I_32_RN_TM1914_3 96
|
||||
#define I_32_I0_TM1914_3 97
|
||||
#define I_32_I1_TM1914_3 98
|
||||
|
||||
|
||||
//APA102
|
||||
@ -170,10 +184,10 @@
|
||||
#define B_8266_DM_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266DmaTm1814Method, NeoGammaNullMethod>
|
||||
#define B_8266_BB_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266BitBangTm1814Method, NeoGammaNullMethod>
|
||||
//TM1829 (RGB)
|
||||
#define B_8266_U0_TM2_4 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266Uart0Tm1829Method, NeoGammaNullMethod>
|
||||
#define B_8266_U1_TM2_4 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266Uart1Tm1829Method, NeoGammaNullMethod>
|
||||
#define B_8266_DM_TM2_4 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266DmaTm1829Method, NeoGammaNullMethod>
|
||||
#define B_8266_BB_TM2_4 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266BitBangTm1829Method, NeoGammaNullMethod>
|
||||
#define B_8266_U0_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266Uart0Tm1829Method, NeoGammaNullMethod>
|
||||
#define B_8266_U1_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266Uart1Tm1829Method, NeoGammaNullMethod>
|
||||
#define B_8266_DM_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266DmaTm1829Method, NeoGammaNullMethod>
|
||||
#define B_8266_BB_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266BitBangTm1829Method, NeoGammaNullMethod>
|
||||
//UCS8903
|
||||
#define B_8266_U0_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod> //3 chan, esp8266, gpio1
|
||||
#define B_8266_U1_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod> //3 chan, esp8266, gpio2
|
||||
@ -199,6 +213,11 @@
|
||||
#define B_8266_U1_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp8266Uart1Ws2805Method, NeoGammaNullMethod> //esp8266, gpio2
|
||||
#define B_8266_DM_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp8266DmaWs2805Method, NeoGammaNullMethod> //esp8266, gpio3
|
||||
#define B_8266_BB_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp8266BitBangWs2805Method, NeoGammaNullMethod> //esp8266, bb
|
||||
//TM1914 (RGB)
|
||||
#define B_8266_U0_TM1914_3 NeoPixelBusLg<NeoRgbTm1914Feature, NeoEsp8266Uart0Tm1914Method, NeoGammaNullMethod>
|
||||
#define B_8266_U1_TM1914_3 NeoPixelBusLg<NeoRgbTm1914Feature, NeoEsp8266Uart1Tm1914Method, NeoGammaNullMethod>
|
||||
#define B_8266_DM_TM1914_3 NeoPixelBusLg<NeoRgbTm1914Feature, NeoEsp8266DmaTm1914Method, NeoGammaNullMethod>
|
||||
#define B_8266_BB_TM1914_3 NeoPixelBusLg<NeoRgbTm1914Feature, NeoEsp8266BitBangTm1914Method, NeoGammaNullMethod>
|
||||
#endif
|
||||
|
||||
/*** ESP32 Neopixel methods ***/
|
||||
@ -210,8 +229,11 @@
|
||||
//#define B_32_I0_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s0X8Ws2812xMethod, NeoGammaNullMethod> // parallel I2S
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
#ifndef WLED_USE_PARALLEL_I2S
|
||||
#define B_32_I1_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1Ws2812xMethod, NeoGammaNullMethod>
|
||||
//#define B_32_I1_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1X8Ws2812xMethod, NeoGammaNullMethod> // parallel I2S
|
||||
#else
|
||||
#define B_32_I1_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1X8Ws2812xMethod, NeoGammaNullMethod> // parallel I2S
|
||||
#endif
|
||||
#endif
|
||||
//RGBW
|
||||
#define B_32_RN_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32RmtNSk6812Method, NeoGammaNullMethod>
|
||||
@ -220,8 +242,11 @@
|
||||
//#define B_32_I0_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32I2s0X8Sk6812Method, NeoGammaNullMethod> // parallel I2S
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
#ifndef WLED_USE_PARALLEL_I2S
|
||||
#define B_32_I1_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32I2s1Sk6812Method, NeoGammaNullMethod>
|
||||
//#define B_32_I1_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32I2s1X8Sk6812Method, NeoGammaNullMethod> // parallel I2S
|
||||
#else
|
||||
#define B_32_I1_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32I2s1X8Sk6812Method, NeoGammaNullMethod> // parallel I2S
|
||||
#endif
|
||||
#endif
|
||||
//400Kbps
|
||||
#define B_32_RN_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32RmtN400KbpsMethod, NeoGammaNullMethod>
|
||||
@ -230,8 +255,11 @@
|
||||
//#define B_32_I0_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s0X8400KbpsMethod, NeoGammaNullMethod> // parallel I2S
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
#ifndef WLED_USE_PARALLEL_I2S
|
||||
#define B_32_I1_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1400KbpsMethod, NeoGammaNullMethod>
|
||||
//#define B_32_I1_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1X8400KbpsMethod, NeoGammaNullMethod> // parallel I2S
|
||||
#else
|
||||
#define B_32_I1_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1X8400KbpsMethod, NeoGammaNullMethod> // parallel I2S
|
||||
#endif
|
||||
#endif
|
||||
//TM1814 (RGBW)
|
||||
#define B_32_RN_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32RmtNTm1814Method, NeoGammaNullMethod>
|
||||
@ -240,8 +268,11 @@
|
||||
//#define B_32_I0_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32I2s0X8Tm1814Method, NeoGammaNullMethod> // parallel I2S
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
#ifndef WLED_USE_PARALLEL_I2S
|
||||
#define B_32_I1_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32I2s1Tm1814Method, NeoGammaNullMethod>
|
||||
//#define B_32_I1_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32I2s1X8Tm1814Method, NeoGammaNullMethod> // parallel I2S
|
||||
#else
|
||||
#define B_32_I1_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32I2s1X8Tm1814Method, NeoGammaNullMethod> // parallel I2S
|
||||
#endif
|
||||
#endif
|
||||
//TM1829 (RGB)
|
||||
#define B_32_RN_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp32RmtNTm1829Method, NeoGammaNullMethod>
|
||||
@ -250,8 +281,11 @@
|
||||
//#define B_32_I0_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp32I2s0X8Tm1829Method, NeoGammaNullMethod> // parallel I2S
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
#ifndef WLED_USE_PARALLEL_I2S
|
||||
#define B_32_I1_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp32I2s1Tm1829Method, NeoGammaNullMethod>
|
||||
//#define B_32_I1_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp32I2s1X8Tm1829Method, NeoGammaNullMethod> // parallel I2S
|
||||
#else
|
||||
#define B_32_I1_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp32I2s1X8Tm1829Method, NeoGammaNullMethod> // parallel I2S
|
||||
#endif
|
||||
#endif
|
||||
//UCS8903
|
||||
#define B_32_RN_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod>
|
||||
@ -260,8 +294,11 @@
|
||||
//#define B_32_I0_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32I2s0X8800KbpsMethod, NeoGammaNullMethod> // parallel I2S
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
#ifndef WLED_USE_PARALLEL_I2S
|
||||
#define B_32_I1_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32I2s1800KbpsMethod, NeoGammaNullMethod>
|
||||
//#define B_32_I1_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32I2s1X8800KbpsMethod, NeoGammaNullMethod> // parallel I2S
|
||||
#else
|
||||
#define B_32_I1_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32I2s1X8800KbpsMethod, NeoGammaNullMethod> // parallel I2S
|
||||
#endif
|
||||
#endif
|
||||
//UCS8904
|
||||
#define B_32_RN_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod>
|
||||
@ -270,8 +307,11 @@
|
||||
//#define B_32_I0_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32I2s0X8800KbpsMethod, NeoGammaNullMethod>// parallel I2S
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
#ifndef WLED_USE_PARALLEL_I2S
|
||||
#define B_32_I1_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32I2s1800KbpsMethod, NeoGammaNullMethod>
|
||||
//#define B_32_I1_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32I2s1X8800KbpsMethod, NeoGammaNullMethod>// parallel I2S
|
||||
#else
|
||||
#define B_32_I1_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32I2s1X8800KbpsMethod, NeoGammaNullMethod>// parallel I2S
|
||||
#endif
|
||||
#endif
|
||||
#define B_32_RN_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32RmtNApa106Method, NeoGammaNullMethod>
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
@ -279,8 +319,11 @@
|
||||
//#define B_32_I0_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s0X8Apa106Method, NeoGammaNullMethod> // parallel I2S
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
#ifndef WLED_USE_PARALLEL_I2S
|
||||
#define B_32_I1_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1Apa106Method, NeoGammaNullMethod>
|
||||
//#define B_32_I1_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1X8Apa106Method, NeoGammaNullMethod> // parallel I2S
|
||||
#else
|
||||
#define B_32_I1_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1X8Apa106Method, NeoGammaNullMethod> // parallel I2S
|
||||
#endif
|
||||
#endif
|
||||
//FW1906 GRBCW
|
||||
#define B_32_RN_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod>
|
||||
@ -289,8 +332,11 @@
|
||||
//#define B_32_I0_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp32I2s0X8800KbpsMethod, NeoGammaNullMethod> // parallel I2S
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
#ifndef WLED_USE_PARALLEL_I2S
|
||||
#define B_32_I1_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp32I2s1800KbpsMethod, NeoGammaNullMethod>
|
||||
//#define B_32_I1_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp32I2s1X8800KbpsMethod, NeoGammaNullMethod> // parallel I2S
|
||||
#else
|
||||
#define B_32_I1_FW6_5 NeoPixelBusLg<NeoGrbcwxFeature, NeoEsp32I2s1X8800KbpsMethod, NeoGammaNullMethod> // parallel I2S
|
||||
#endif
|
||||
#endif
|
||||
//WS2805 RGBWC
|
||||
#define B_32_RN_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp32RmtNWs2805Method, NeoGammaNullMethod>
|
||||
@ -299,8 +345,24 @@
|
||||
//#define B_32_I0_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp32I2s0X8Ws2805Method, NeoGammaNullMethod> // parallel I2S
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
#ifndef WLED_USE_PARALLEL_I2S
|
||||
#define B_32_I1_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp32I2s1Ws2805Method, NeoGammaNullMethod>
|
||||
//#define B_32_I1_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp32I2s1X8Ws2805Method, NeoGammaNullMethod> // parallel I2S
|
||||
#else
|
||||
#define B_32_I1_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp32I2s1X8Ws2805Method, NeoGammaNullMethod> // parallel I2S
|
||||
#endif
|
||||
#endif
|
||||
//TM1914 (RGB)
|
||||
#define B_32_RN_TM1914_3 NeoPixelBusLg<NeoGrbTm1914Feature, NeoEsp32RmtNTm1914Method, NeoGammaNullMethod>
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
#define B_32_I0_TM1914_3 NeoPixelBusLg<NeoGrbTm1914Feature, NeoEsp32I2s0Tm1914Method, NeoGammaNullMethod>
|
||||
//#define B_32_I0_TM1914_3 NeoPixelBusLg<NeoGrbTm1914Feature, NeoEsp32I2s0X8Tm1914Method, NeoGammaNullMethod>
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
#ifndef WLED_USE_PARALLEL_I2S
|
||||
#define B_32_I1_TM1914_3 NeoPixelBusLg<NeoGrbTm1914Feature, NeoEsp32I2s1Tm1914Method, NeoGammaNullMethod>
|
||||
#else
|
||||
#define B_32_I1_TM1914_3 NeoPixelBusLg<NeoGrbTm1914Feature, NeoEsp32I2s1X8Tm1914Method, NeoGammaNullMethod>
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -363,6 +425,13 @@ class PolyBus {
|
||||
tm1814_strip->SetPixelSettings(NeoTm1814Settings(/*R*/225, /*G*/225, /*B*/225, /*W*/225));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static void beginTM1914(void* busPtr) {
|
||||
T tm1914_strip = static_cast<T>(busPtr);
|
||||
tm1914_strip->Begin();
|
||||
tm1914_strip->SetPixelSettings(NeoTm1914Settings()); //NeoTm1914_Mode_DinFdinAutoSwitch, NeoTm1914_Mode_DinOnly, NeoTm1914_Mode_FdinOnly
|
||||
}
|
||||
|
||||
static void begin(void* busPtr, uint8_t busType, uint8_t* pins, uint16_t clock_kHz = 0U) {
|
||||
switch (busType) {
|
||||
case I_NONE: break;
|
||||
@ -383,10 +452,10 @@ class PolyBus {
|
||||
case I_8266_U1_TM1_4: beginTM1814<B_8266_U1_TM1_4*>(busPtr); break;
|
||||
case I_8266_DM_TM1_4: beginTM1814<B_8266_DM_TM1_4*>(busPtr); break;
|
||||
case I_8266_BB_TM1_4: beginTM1814<B_8266_BB_TM1_4*>(busPtr); break;
|
||||
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->Begin(); break;
|
||||
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->Begin(); break;
|
||||
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->Begin(); break;
|
||||
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->Begin(); break;
|
||||
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_3*>(busPtr))->Begin(); break;
|
||||
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_3*>(busPtr))->Begin(); break;
|
||||
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_3*>(busPtr))->Begin(); break;
|
||||
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_3*>(busPtr))->Begin(); break;
|
||||
case I_HS_DOT_3: beginDotStar<B_HS_DOT_3*>(busPtr, -1, -1, -1, -1, clock_kHz); break;
|
||||
case I_HS_LPD_3: beginDotStar<B_HS_LPD_3*>(busPtr, -1, -1, -1, -1, clock_kHz); break;
|
||||
case I_HS_LPO_3: beginDotStar<B_HS_LPO_3*>(busPtr, -1, -1, -1, -1, clock_kHz); break;
|
||||
@ -412,6 +481,10 @@ class PolyBus {
|
||||
case I_8266_U1_2805_5: (static_cast<B_8266_U1_2805_5*>(busPtr))->Begin(); break;
|
||||
case I_8266_DM_2805_5: (static_cast<B_8266_DM_2805_5*>(busPtr))->Begin(); break;
|
||||
case I_8266_BB_2805_5: (static_cast<B_8266_BB_2805_5*>(busPtr))->Begin(); break;
|
||||
case I_8266_U0_TM1914_3: beginTM1914<B_8266_U0_TM1914_3*>(busPtr); break;
|
||||
case I_8266_U1_TM1914_3: beginTM1914<B_8266_U1_TM1914_3*>(busPtr); break;
|
||||
case I_8266_DM_TM1914_3: beginTM1914<B_8266_DM_TM1914_3*>(busPtr); break;
|
||||
case I_8266_BB_TM1914_3: beginTM1914<B_8266_BB_TM1914_3*>(busPtr); break;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Begin(); break;
|
||||
@ -480,6 +553,13 @@ class PolyBus {
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_2805_5: (static_cast<B_32_I1_2805_5*>(busPtr))->Begin(); break;
|
||||
#endif
|
||||
case I_32_RN_TM1914_3: beginTM1914<B_32_RN_TM1914_3*>(busPtr); break;
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_TM1914_3: beginTM1914<B_32_I0_TM1914_3*>(busPtr); break;
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_TM1914_3: beginTM1914<B_32_I1_TM1914_3*>(busPtr); break;
|
||||
#endif
|
||||
// ESP32 can (and should, to avoid inadvertantly driving the chip select signal) specify the pins used for SPI, but only in begin()
|
||||
case I_HS_DOT_3: beginDotStar<B_HS_DOT_3*>(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break;
|
||||
case I_HS_LPD_3: beginDotStar<B_HS_LPD_3*>(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break;
|
||||
@ -496,6 +576,15 @@ class PolyBus {
|
||||
}
|
||||
|
||||
static void* create(uint8_t busType, uint8_t* pins, uint16_t len, uint8_t channel, uint16_t clock_kHz = 0U) {
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !(defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3))
|
||||
// NOTE: "channel" is only used on ESP32 (and its variants) for RMT channel allocation
|
||||
// since 0.15.0-b3 I2S1 is favoured for classic ESP32 and moved to position 0 (channel 0) so we need to subtract 1 for correct RMT allocation
|
||||
#ifdef WLED_USE_PARALLEL_I2S
|
||||
if (channel > 7) channel -= 8; // accommodate parallel I2S1 which is used 1st on classic ESP32
|
||||
#else
|
||||
if (channel > 0) channel--; // accommodate I2S1 which is used as 1st bus on classic ESP32
|
||||
#endif
|
||||
#endif
|
||||
void* busPtr = nullptr;
|
||||
switch (busType) {
|
||||
case I_NONE: break;
|
||||
@ -516,10 +605,10 @@ class PolyBus {
|
||||
case I_8266_U1_TM1_4: busPtr = new B_8266_U1_TM1_4(len, pins[0]); break;
|
||||
case I_8266_DM_TM1_4: busPtr = new B_8266_DM_TM1_4(len, pins[0]); break;
|
||||
case I_8266_BB_TM1_4: busPtr = new B_8266_BB_TM1_4(len, pins[0]); break;
|
||||
case I_8266_U0_TM2_3: busPtr = new B_8266_U0_TM2_4(len, pins[0]); break;
|
||||
case I_8266_U1_TM2_3: busPtr = new B_8266_U1_TM2_4(len, pins[0]); break;
|
||||
case I_8266_DM_TM2_3: busPtr = new B_8266_DM_TM2_4(len, pins[0]); break;
|
||||
case I_8266_BB_TM2_3: busPtr = new B_8266_BB_TM2_4(len, pins[0]); break;
|
||||
case I_8266_U0_TM2_3: busPtr = new B_8266_U0_TM2_3(len, pins[0]); break;
|
||||
case I_8266_U1_TM2_3: busPtr = new B_8266_U1_TM2_3(len, pins[0]); break;
|
||||
case I_8266_DM_TM2_3: busPtr = new B_8266_DM_TM2_3(len, pins[0]); break;
|
||||
case I_8266_BB_TM2_3: busPtr = new B_8266_BB_TM2_3(len, pins[0]); break;
|
||||
case I_8266_U0_UCS_3: busPtr = new B_8266_U0_UCS_3(len, pins[0]); break;
|
||||
case I_8266_U1_UCS_3: busPtr = new B_8266_U1_UCS_3(len, pins[0]); break;
|
||||
case I_8266_DM_UCS_3: busPtr = new B_8266_DM_UCS_3(len, pins[0]); break;
|
||||
@ -540,6 +629,10 @@ class PolyBus {
|
||||
case I_8266_U1_2805_5: busPtr = new B_8266_U1_2805_5(len, pins[0]); break;
|
||||
case I_8266_DM_2805_5: busPtr = new B_8266_DM_2805_5(len, pins[0]); break;
|
||||
case I_8266_BB_2805_5: busPtr = new B_8266_BB_2805_5(len, pins[0]); break;
|
||||
case I_8266_U0_TM1914_3: busPtr = new B_8266_U0_TM1914_3(len, pins[0]); break;
|
||||
case I_8266_U1_TM1914_3: busPtr = new B_8266_U1_TM1914_3(len, pins[0]); break;
|
||||
case I_8266_DM_TM1914_3: busPtr = new B_8266_DM_TM1914_3(len, pins[0]); break;
|
||||
case I_8266_BB_TM1914_3: busPtr = new B_8266_BB_TM1914_3(len, pins[0]); break;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); break;
|
||||
@ -608,6 +701,13 @@ class PolyBus {
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_2805_5: busPtr = new B_32_I1_2805_5(len, pins[0]); break;
|
||||
#endif
|
||||
case I_32_RN_TM1914_3: busPtr = new B_32_RN_TM1914_3(len, pins[0], (NeoBusChannel)channel); break;
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_TM1914_3: busPtr = new B_32_I0_TM1914_3(len, pins[0]); break;
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_TM1914_3: busPtr = new B_32_I1_TM1914_3(len, pins[0]); break;
|
||||
#endif
|
||||
#endif
|
||||
// for 2-wire: pins[1] is clk, pins[0] is dat. begin expects (len, clk, dat)
|
||||
case I_HS_DOT_3: busPtr = new B_HS_DOT_3(len, pins[1], pins[0]); break;
|
||||
@ -645,10 +745,10 @@ class PolyBus {
|
||||
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_3*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_3*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_3*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_3*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->Show(consistent); break;
|
||||
@ -669,6 +769,10 @@ class PolyBus {
|
||||
case I_8266_U1_2805_5: (static_cast<B_8266_U1_2805_5*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_DM_2805_5: (static_cast<B_8266_DM_2805_5*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_BB_2805_5: (static_cast<B_8266_BB_2805_5*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_U0_TM1914_3: (static_cast<B_8266_U0_TM1914_3*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_U1_TM1914_3: (static_cast<B_8266_U1_TM1914_3*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_DM_TM1914_3: (static_cast<B_8266_DM_TM1914_3*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_BB_TM1914_3: (static_cast<B_8266_BB_TM1914_3*>(busPtr))->Show(consistent); break;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Show(consistent); break;
|
||||
@ -737,6 +841,13 @@ class PolyBus {
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_2805_5: (static_cast<B_32_I1_2805_5*>(busPtr))->Show(consistent); break;
|
||||
#endif
|
||||
case I_32_RN_TM1914_3: (static_cast<B_32_RN_TM1914_3*>(busPtr))->Show(consistent); break;
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_TM1914_3: (static_cast<B_32_I0_TM1914_3*>(busPtr))->Show(consistent); break;
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_TM1914_3: (static_cast<B_32_I1_TM1914_3*>(busPtr))->Show(consistent); break;
|
||||
#endif
|
||||
#endif
|
||||
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Show(consistent); break;
|
||||
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->Show(consistent); break;
|
||||
@ -771,10 +882,10 @@ class PolyBus {
|
||||
case I_8266_U1_TM1_4: return (static_cast<B_8266_U1_TM1_4*>(busPtr))->CanShow(); break;
|
||||
case I_8266_DM_TM1_4: return (static_cast<B_8266_DM_TM1_4*>(busPtr))->CanShow(); break;
|
||||
case I_8266_BB_TM1_4: return (static_cast<B_8266_BB_TM1_4*>(busPtr))->CanShow(); break;
|
||||
case I_8266_U0_TM2_3: return (static_cast<B_8266_U0_TM2_4*>(busPtr))->CanShow(); break;
|
||||
case I_8266_U1_TM2_3: return (static_cast<B_8266_U1_TM2_4*>(busPtr))->CanShow(); break;
|
||||
case I_8266_DM_TM2_3: return (static_cast<B_8266_DM_TM2_4*>(busPtr))->CanShow(); break;
|
||||
case I_8266_BB_TM2_3: return (static_cast<B_8266_BB_TM2_4*>(busPtr))->CanShow(); break;
|
||||
case I_8266_U0_TM2_3: return (static_cast<B_8266_U0_TM2_3*>(busPtr))->CanShow(); break;
|
||||
case I_8266_U1_TM2_3: return (static_cast<B_8266_U1_TM2_3*>(busPtr))->CanShow(); break;
|
||||
case I_8266_DM_TM2_3: return (static_cast<B_8266_DM_TM2_3*>(busPtr))->CanShow(); break;
|
||||
case I_8266_BB_TM2_3: return (static_cast<B_8266_BB_TM2_3*>(busPtr))->CanShow(); break;
|
||||
case I_8266_U0_UCS_3: return (static_cast<B_8266_U0_UCS_3*>(busPtr))->CanShow(); break;
|
||||
case I_8266_U1_UCS_3: return (static_cast<B_8266_U1_UCS_3*>(busPtr))->CanShow(); break;
|
||||
case I_8266_DM_UCS_3: return (static_cast<B_8266_DM_UCS_3*>(busPtr))->CanShow(); break;
|
||||
@ -794,6 +905,10 @@ class PolyBus {
|
||||
case I_8266_U1_2805_5: return (static_cast<B_8266_U1_2805_5*>(busPtr))->CanShow(); break;
|
||||
case I_8266_DM_2805_5: return (static_cast<B_8266_DM_2805_5*>(busPtr))->CanShow(); break;
|
||||
case I_8266_BB_2805_5: return (static_cast<B_8266_BB_2805_5*>(busPtr))->CanShow(); break;
|
||||
case I_8266_U0_TM1914_3: return (static_cast<B_8266_U0_TM1914_3*>(busPtr))->CanShow(); break;
|
||||
case I_8266_U1_TM1914_3: return (static_cast<B_8266_U1_TM1914_3*>(busPtr))->CanShow(); break;
|
||||
case I_8266_DM_TM1914_3: return (static_cast<B_8266_DM_TM1914_3*>(busPtr))->CanShow(); break;
|
||||
case I_8266_BB_TM1914_3: return (static_cast<B_8266_BB_TM1914_3*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: return (static_cast<B_32_RN_NEO_3*>(busPtr))->CanShow(); break;
|
||||
@ -862,6 +977,13 @@ class PolyBus {
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_2805_5: return (static_cast<B_32_I1_2805_5*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
case I_32_RN_TM1914_3: return (static_cast<B_32_RN_TM1914_3*>(busPtr))->CanShow(); break;
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_TM1914_3: return (static_cast<B_32_I0_TM1914_3*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_TM1914_3: return (static_cast<B_32_I1_TM1914_3*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
#endif
|
||||
case I_HS_DOT_3: return (static_cast<B_HS_DOT_3*>(busPtr))->CanShow(); break;
|
||||
case I_SS_DOT_3: return (static_cast<B_SS_DOT_3*>(busPtr))->CanShow(); break;
|
||||
@ -921,10 +1043,10 @@ class PolyBus {
|
||||
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break;
|
||||
case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break;
|
||||
case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break;
|
||||
@ -945,6 +1067,10 @@ class PolyBus {
|
||||
case I_8266_U1_2805_5: (static_cast<B_8266_U1_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
|
||||
case I_8266_DM_2805_5: (static_cast<B_8266_DM_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
|
||||
case I_8266_BB_2805_5: (static_cast<B_8266_BB_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
|
||||
case I_8266_U0_TM1914_3: (static_cast<B_8266_U0_TM1914_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_8266_U1_TM1914_3: (static_cast<B_8266_U1_TM1914_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_8266_DM_TM1914_3: (static_cast<B_8266_DM_TM1914_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_8266_BB_TM1914_3: (static_cast<B_8266_BB_TM1914_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
@ -1013,6 +1139,13 @@ class PolyBus {
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_2805_5: (static_cast<B_32_I1_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
|
||||
#endif
|
||||
case I_32_RN_TM1914_3: (static_cast<B_32_RN_TM1914_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_TM1914_3: (static_cast<B_32_I0_TM1914_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_TM1914_3: (static_cast<B_32_I1_TM1914_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
#endif
|
||||
#endif
|
||||
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
@ -1047,10 +1180,10 @@ class PolyBus {
|
||||
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->SetLuminance(b); break;
|
||||
@ -1071,6 +1204,10 @@ class PolyBus {
|
||||
case I_8266_U1_2805_5: (static_cast<B_8266_U1_2805_5*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_DM_2805_5: (static_cast<B_8266_DM_2805_5*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_BB_2805_5: (static_cast<B_8266_BB_2805_5*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_U0_TM1914_3: (static_cast<B_8266_U0_TM1914_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_U1_TM1914_3: (static_cast<B_8266_U1_TM1914_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_DM_TM1914_3: (static_cast<B_8266_DM_TM1914_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_BB_TM1914_3: (static_cast<B_8266_BB_TM1914_3*>(busPtr))->SetLuminance(b); break;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetLuminance(b); break;
|
||||
@ -1139,6 +1276,13 @@ class PolyBus {
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_2805_5: (static_cast<B_32_I1_2805_5*>(busPtr))->SetLuminance(b); break;
|
||||
#endif
|
||||
case I_32_RN_TM1914_3: (static_cast<B_32_RN_TM1914_3*>(busPtr))->SetLuminance(b); break;
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_TM1914_3: (static_cast<B_32_I0_TM1914_3*>(busPtr))->SetLuminance(b); break;
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_TM1914_3: (static_cast<B_32_I1_TM1914_3*>(busPtr))->SetLuminance(b); break;
|
||||
#endif
|
||||
#endif
|
||||
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetLuminance(b); break;
|
||||
@ -1174,10 +1318,10 @@ class PolyBus {
|
||||
case I_8266_U1_TM1_4: col = (static_cast<B_8266_U1_TM1_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_DM_TM1_4: col = (static_cast<B_8266_DM_TM1_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_BB_TM1_4: col = (static_cast<B_8266_BB_TM1_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_U0_TM2_3: col = (static_cast<B_8266_U0_TM2_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_U1_TM2_3: col = (static_cast<B_8266_U1_TM2_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_DM_TM2_3: col = (static_cast<B_8266_DM_TM2_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_BB_TM2_3: col = (static_cast<B_8266_BB_TM2_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_U0_TM2_3: col = (static_cast<B_8266_U0_TM2_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_U1_TM2_3: col = (static_cast<B_8266_U1_TM2_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_DM_TM2_3: col = (static_cast<B_8266_DM_TM2_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_BB_TM2_3: col = (static_cast<B_8266_BB_TM2_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_U0_UCS_3: { Rgb48Color c = (static_cast<B_8266_U0_UCS_3*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,0); } break;
|
||||
case I_8266_U1_UCS_3: { Rgb48Color c = (static_cast<B_8266_U1_UCS_3*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,0); } break;
|
||||
case I_8266_DM_UCS_3: { Rgb48Color c = (static_cast<B_8266_DM_UCS_3*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,0); } break;
|
||||
@ -1198,6 +1342,10 @@ class PolyBus {
|
||||
case I_8266_U1_2805_5: { RgbwwColor c = (static_cast<B_8266_U1_2805_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
|
||||
case I_8266_DM_2805_5: { RgbwwColor c = (static_cast<B_8266_DM_2805_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
|
||||
case I_8266_BB_2805_5: { RgbwwColor c = (static_cast<B_8266_BB_2805_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
|
||||
case I_8266_U0_TM1914_3: col = (static_cast<B_8266_U0_TM1914_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_U1_TM1914_3: col = (static_cast<B_8266_U1_TM1914_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_DM_TM1914_3: col = (static_cast<B_8266_DM_TM1914_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_BB_TM1914_3: col = (static_cast<B_8266_BB_TM1914_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: col = (static_cast<B_32_RN_NEO_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
@ -1266,6 +1414,13 @@ class PolyBus {
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_2805_5: { RgbwwColor c = (static_cast<B_32_I1_2805_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
|
||||
#endif
|
||||
case I_32_RN_TM1914_3: col = (static_cast<B_32_RN_TM1914_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_TM1914_3: col = (static_cast<B_32_I0_TM1914_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_TM1914_3: col = (static_cast<B_32_I1_TM1914_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#endif
|
||||
#endif
|
||||
case I_HS_DOT_3: col = (static_cast<B_HS_DOT_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_SS_DOT_3: col = (static_cast<B_SS_DOT_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
@ -1319,10 +1474,10 @@ class PolyBus {
|
||||
case I_8266_U1_TM1_4: delete (static_cast<B_8266_U1_TM1_4*>(busPtr)); break;
|
||||
case I_8266_DM_TM1_4: delete (static_cast<B_8266_DM_TM1_4*>(busPtr)); break;
|
||||
case I_8266_BB_TM1_4: delete (static_cast<B_8266_BB_TM1_4*>(busPtr)); break;
|
||||
case I_8266_U0_TM2_3: delete (static_cast<B_8266_U0_TM2_4*>(busPtr)); break;
|
||||
case I_8266_U1_TM2_3: delete (static_cast<B_8266_U1_TM2_4*>(busPtr)); break;
|
||||
case I_8266_DM_TM2_3: delete (static_cast<B_8266_DM_TM2_4*>(busPtr)); break;
|
||||
case I_8266_BB_TM2_3: delete (static_cast<B_8266_BB_TM2_4*>(busPtr)); break;
|
||||
case I_8266_U0_TM2_3: delete (static_cast<B_8266_U0_TM2_3*>(busPtr)); break;
|
||||
case I_8266_U1_TM2_3: delete (static_cast<B_8266_U1_TM2_3*>(busPtr)); break;
|
||||
case I_8266_DM_TM2_3: delete (static_cast<B_8266_DM_TM2_3*>(busPtr)); break;
|
||||
case I_8266_BB_TM2_3: delete (static_cast<B_8266_BB_TM2_3*>(busPtr)); break;
|
||||
case I_8266_U0_UCS_3: delete (static_cast<B_8266_U0_UCS_3*>(busPtr)); break;
|
||||
case I_8266_U1_UCS_3: delete (static_cast<B_8266_U1_UCS_3*>(busPtr)); break;
|
||||
case I_8266_DM_UCS_3: delete (static_cast<B_8266_DM_UCS_3*>(busPtr)); break;
|
||||
@ -1343,6 +1498,10 @@ class PolyBus {
|
||||
case I_8266_U1_2805_5: delete (static_cast<B_8266_U1_2805_5*>(busPtr)); break;
|
||||
case I_8266_DM_2805_5: delete (static_cast<B_8266_DM_2805_5*>(busPtr)); break;
|
||||
case I_8266_BB_2805_5: delete (static_cast<B_8266_BB_2805_5*>(busPtr)); break;
|
||||
case I_8266_U0_TM1914_3: delete (static_cast<B_8266_U0_TM1914_3*>(busPtr)); break;
|
||||
case I_8266_U1_TM1914_3: delete (static_cast<B_8266_U1_TM1914_3*>(busPtr)); break;
|
||||
case I_8266_DM_TM1914_3: delete (static_cast<B_8266_DM_TM1914_3*>(busPtr)); break;
|
||||
case I_8266_BB_TM1914_3: delete (static_cast<B_8266_BB_TM1914_3*>(busPtr)); break;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: delete (static_cast<B_32_RN_NEO_3*>(busPtr)); break;
|
||||
@ -1411,6 +1570,13 @@ class PolyBus {
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_2805_5: delete (static_cast<B_32_I1_2805_5*>(busPtr)); break;
|
||||
#endif
|
||||
case I_32_RN_TM1914_3: delete (static_cast<B_32_RN_TM1914_3*>(busPtr)); break;
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_TM1914_3: delete (static_cast<B_32_I0_TM1914_3*>(busPtr)); break;
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_TM1914_3: delete (static_cast<B_32_I1_TM1914_3*>(busPtr)); break;
|
||||
#endif
|
||||
#endif
|
||||
case I_HS_DOT_3: delete (static_cast<B_HS_DOT_3*>(busPtr)); break;
|
||||
case I_SS_DOT_3: delete (static_cast<B_SS_DOT_3*>(busPtr)); break;
|
||||
@ -1476,6 +1642,8 @@ class PolyBus {
|
||||
return I_8266_U0_FW6_5 + offset;
|
||||
case TYPE_WS2805:
|
||||
return I_8266_U0_2805_5 + offset;
|
||||
case TYPE_TM1914:
|
||||
return I_8266_U0_TM1914_3 + offset;
|
||||
}
|
||||
#else //ESP32
|
||||
uint8_t offset = 0; // 0 = RMT (num 1-8), 1 = I2S0 (used by Audioreactive), 2 = I2S1
|
||||
@ -1493,9 +1661,15 @@ class PolyBus {
|
||||
//if (num > 3) offset = num -4; // I2S not supported yet
|
||||
#else
|
||||
// standard ESP32 has 8 RMT and 2 I2S channels
|
||||
#ifdef WLED_USE_PARALLEL_I2S
|
||||
if (num > 16) return I_NONE;
|
||||
if (num < 8) offset = 2; // prefer 8 parallel I2S1 channels
|
||||
if (num == 16) offset = 1;
|
||||
#else
|
||||
if (num > 9) return I_NONE;
|
||||
if (num > 8) offset = 1;
|
||||
if (num == 0) offset = 2; // prefer I2S1 for 1st bus (less flickering but more RAM needed)
|
||||
#endif
|
||||
#endif
|
||||
switch (busType) {
|
||||
case TYPE_WS2812_1CH_X3:
|
||||
@ -1521,6 +1695,8 @@ class PolyBus {
|
||||
return I_32_RN_FW6_5 + offset;
|
||||
case TYPE_WS2805:
|
||||
return I_32_RN_2805_5 + offset;
|
||||
case TYPE_TM1914:
|
||||
return I_32_RN_TM1914_3 + offset;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -7,11 +7,13 @@
|
||||
#define WLED_DEBOUNCE_THRESHOLD 50 // only consider button input of at least 50ms as valid (debouncing)
|
||||
#define WLED_LONG_PRESS 600 // long press if button is released after held for at least 600ms
|
||||
#define WLED_DOUBLE_PRESS 350 // double press if another press within 350ms after a short press
|
||||
#define WLED_LONG_REPEATED_ACTION 300 // how often a repeated action (e.g. dimming) is fired on long press on button IDs >0
|
||||
#define WLED_LONG_REPEATED_ACTION 400 // how often a repeated action (e.g. dimming) is fired on long press on button IDs >0
|
||||
#define WLED_LONG_AP 5000 // how long button 0 needs to be held to activate WLED-AP
|
||||
#define WLED_LONG_FACTORY_RESET 10000 // how long button 0 needs to be held to trigger a factory reset
|
||||
#define WLED_LONG_BRI_STEPS 16 // how much to increase/decrease the brightness with each long press repetition
|
||||
|
||||
static const char _mqtt_topic_button[] PROGMEM = "%s/button/%d"; // optimize flash usage
|
||||
static bool buttonBriDirection = false; // true: increase brightness, false: decrease brightness
|
||||
|
||||
void shortPressAction(uint8_t b)
|
||||
{
|
||||
@ -39,7 +41,19 @@ void longPressAction(uint8_t b)
|
||||
if (!macroLongPress[b]) {
|
||||
switch (b) {
|
||||
case 0: setRandomColor(col); colorUpdated(CALL_MODE_BUTTON); break;
|
||||
case 1: bri += 8; stateUpdated(CALL_MODE_BUTTON); buttonPressedTime[b] = millis(); break; // repeatable action
|
||||
case 1:
|
||||
if(buttonBriDirection) {
|
||||
if (bri == 255) break; // avoid unnecessary updates to brightness
|
||||
if (bri >= 255 - WLED_LONG_BRI_STEPS) bri = 255;
|
||||
else bri += WLED_LONG_BRI_STEPS;
|
||||
} else {
|
||||
if (bri == 1) break; // avoid unnecessary updates to brightness
|
||||
if (bri <= WLED_LONG_BRI_STEPS) bri = 1;
|
||||
else bri -= WLED_LONG_BRI_STEPS;
|
||||
}
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
buttonPressedTime[b] = millis();
|
||||
break; // repeatable action
|
||||
}
|
||||
} else {
|
||||
applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET);
|
||||
@ -284,10 +298,12 @@ void handleButton()
|
||||
buttonPressedBefore[b] = true;
|
||||
|
||||
if (now - buttonPressedTime[b] > WLED_LONG_PRESS) { //long press
|
||||
if (!buttonLongPressed[b]) longPressAction(b);
|
||||
else if (b) { //repeatable action (~3 times per s) on button > 0
|
||||
if (!buttonLongPressed[b]) {
|
||||
buttonBriDirection = !buttonBriDirection; //toggle brightness direction on long press
|
||||
longPressAction(b);
|
||||
buttonPressedTime[b] = now - WLED_LONG_REPEATED_ACTION; //333ms
|
||||
} else if (b) { //repeatable action (~5 times per s) on button > 0
|
||||
longPressAction(b);
|
||||
buttonPressedTime[b] = now - WLED_LONG_REPEATED_ACTION; //200ms
|
||||
}
|
||||
buttonLongPressed[b] = true;
|
||||
}
|
||||
@ -379,7 +395,7 @@ void handleIO()
|
||||
esp32RMTInvertIdle();
|
||||
#endif
|
||||
if (rlyPin>=0) {
|
||||
pinMode(rlyPin, OUTPUT);
|
||||
pinMode(rlyPin, rlyOpenDrain ? OUTPUT_OPEN_DRAIN : OUTPUT);
|
||||
digitalWrite(rlyPin, rlyMde);
|
||||
}
|
||||
offMode = false;
|
||||
@ -400,7 +416,7 @@ void handleIO()
|
||||
esp32RMTInvertIdle();
|
||||
#endif
|
||||
if (rlyPin>=0) {
|
||||
pinMode(rlyPin, OUTPUT);
|
||||
pinMode(rlyPin, rlyOpenDrain ? OUTPUT_OPEN_DRAIN : OUTPUT);
|
||||
digitalWrite(rlyPin, !rlyMde);
|
||||
}
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
CJSON(strip.panels, matrix[F("mpc")]);
|
||||
strip.panel.clear();
|
||||
JsonArray panels = matrix[F("panels")];
|
||||
uint8_t s = 0;
|
||||
int s = 0;
|
||||
if (!panels.isNull()) {
|
||||
strip.panel.reserve(max(1U,min((size_t)strip.panels,(size_t)WLED_MAX_PANELS))); // pre-allocate memory for panels
|
||||
for (JsonObject pnl : panels) {
|
||||
@ -156,7 +156,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
JsonArray ins = hw_led["ins"];
|
||||
|
||||
if (fromFS || !ins.isNull()) {
|
||||
uint8_t s = 0; // bus iterator
|
||||
int s = 0; // bus iterator
|
||||
if (fromFS) BusManager::removeAll(); // can't safely manipulate busses directly in network callback
|
||||
uint32_t mem = 0, globalBufMem = 0;
|
||||
uint16_t maxlen = 0;
|
||||
@ -335,12 +335,14 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
CJSON(irApplyToAllSelected, hw["ir"]["sel"]);
|
||||
|
||||
JsonObject relay = hw[F("relay")];
|
||||
|
||||
rlyOpenDrain = relay[F("odrain")] | rlyOpenDrain;
|
||||
int hw_relay_pin = relay["pin"] | -2;
|
||||
if (hw_relay_pin > -2) {
|
||||
pinManager.deallocatePin(rlyPin, PinOwner::Relay);
|
||||
if (pinManager.allocatePin(hw_relay_pin,true, PinOwner::Relay)) {
|
||||
rlyPin = hw_relay_pin;
|
||||
pinMode(rlyPin, OUTPUT);
|
||||
pinMode(rlyPin, rlyOpenDrain ? OUTPUT_OPEN_DRAIN : OUTPUT);
|
||||
} else {
|
||||
rlyPin = -1;
|
||||
}
|
||||
@ -788,7 +790,7 @@ void serializeConfig() {
|
||||
JsonObject matrix = hw_led.createNestedObject(F("matrix"));
|
||||
matrix[F("mpc")] = strip.panels;
|
||||
JsonArray panels = matrix.createNestedArray(F("panels"));
|
||||
for (uint8_t i=0; i<strip.panel.size(); i++) {
|
||||
for (size_t i = 0; i < strip.panel.size(); i++) {
|
||||
JsonObject pnl = panels.createNestedObject();
|
||||
pnl["b"] = strip.panel[i].bottomStart;
|
||||
pnl["r"] = strip.panel[i].rightStart;
|
||||
@ -804,7 +806,7 @@ void serializeConfig() {
|
||||
|
||||
JsonArray hw_led_ins = hw_led.createNestedArray("ins");
|
||||
|
||||
for (uint8_t s = 0; s < BusManager::getNumBusses(); s++) {
|
||||
for (size_t s = 0; s < BusManager::getNumBusses(); s++) {
|
||||
Bus *bus = BusManager::getBus(s);
|
||||
if (!bus || bus->getLength()==0) break;
|
||||
JsonObject ins = hw_led_ins.createNestedObject();
|
||||
@ -813,7 +815,7 @@ void serializeConfig() {
|
||||
JsonArray ins_pin = ins.createNestedArray("pin");
|
||||
uint8_t pins[5];
|
||||
uint8_t nPins = bus->getPins(pins);
|
||||
for (uint8_t i = 0; i < nPins; i++) ins_pin.add(pins[i]);
|
||||
for (int i = 0; i < nPins; i++) ins_pin.add(pins[i]);
|
||||
ins[F("order")] = bus->getColorOrder();
|
||||
ins["rev"] = bus->isReversed();
|
||||
ins[F("skip")] = bus->skippedLeds();
|
||||
@ -827,7 +829,7 @@ void serializeConfig() {
|
||||
|
||||
JsonArray hw_com = hw.createNestedArray(F("com"));
|
||||
const ColorOrderMap& com = BusManager::getColorOrderMap();
|
||||
for (uint8_t s = 0; s < com.count(); s++) {
|
||||
for (size_t s = 0; s < com.count(); s++) {
|
||||
const ColorOrderMapEntry *entry = com.get(s);
|
||||
if (!entry) break;
|
||||
|
||||
@ -844,7 +846,7 @@ void serializeConfig() {
|
||||
JsonArray hw_btn_ins = hw_btn.createNestedArray("ins");
|
||||
|
||||
// configuration for all buttons
|
||||
for (uint8_t i=0; i<WLED_MAX_BUTTONS; i++) {
|
||||
for (int i = 0; i < WLED_MAX_BUTTONS; i++) {
|
||||
JsonObject hw_btn_ins_0 = hw_btn_ins.createNestedObject();
|
||||
hw_btn_ins_0["type"] = buttonType[i];
|
||||
JsonArray hw_btn_ins_0_pin = hw_btn_ins_0.createNestedArray("pin");
|
||||
@ -868,6 +870,7 @@ void serializeConfig() {
|
||||
JsonObject hw_relay = hw.createNestedObject(F("relay"));
|
||||
hw_relay["pin"] = rlyPin;
|
||||
hw_relay["rev"] = !rlyMde;
|
||||
hw_relay[F("odrain")] = rlyOpenDrain;
|
||||
|
||||
hw[F("baud")] = serialBaud;
|
||||
|
||||
|
@ -65,24 +65,30 @@ uint32_t color_add(uint32_t c1, uint32_t c2, bool fast)
|
||||
* fades color toward black
|
||||
* if using "video" method the resulting color will never become black unless it is already black
|
||||
*/
|
||||
|
||||
uint32_t color_fade(uint32_t c1, uint8_t amount, bool video)
|
||||
{
|
||||
uint8_t r = R(c1);
|
||||
uint8_t g = G(c1);
|
||||
uint8_t b = B(c1);
|
||||
uint8_t w = W(c1);
|
||||
if (video) {
|
||||
r = scale8_video(r, amount);
|
||||
g = scale8_video(g, amount);
|
||||
b = scale8_video(b, amount);
|
||||
w = scale8_video(w, amount);
|
||||
} else {
|
||||
r = scale8(r, amount);
|
||||
g = scale8(g, amount);
|
||||
b = scale8(b, amount);
|
||||
w = scale8(w, amount);
|
||||
uint32_t scaledcolor; // color order is: W R G B from MSB to LSB
|
||||
uint32_t r = R(c1);
|
||||
uint32_t g = G(c1);
|
||||
uint32_t b = B(c1);
|
||||
uint32_t w = W(c1);
|
||||
if (video) {
|
||||
uint32_t scale = amount; // 32bit for faster calculation
|
||||
scaledcolor = (((r * scale) >> 8) << 16) + ((r && scale) ? 1 : 0);
|
||||
scaledcolor |= (((g * scale) >> 8) << 8) + ((g && scale) ? 1 : 0);
|
||||
scaledcolor |= ((b * scale) >> 8) + ((b && scale) ? 1 : 0);
|
||||
scaledcolor |= (((w * scale) >> 8) << 24) + ((w && scale) ? 1 : 0);
|
||||
return scaledcolor;
|
||||
}
|
||||
else {
|
||||
uint32_t scale = 1 + amount;
|
||||
scaledcolor = ((r * scale) >> 8) << 16;
|
||||
scaledcolor |= ((g * scale) >> 8) << 8;
|
||||
scaledcolor |= (b * scale) >> 8;
|
||||
scaledcolor |= ((w * scale) >> 8) << 24;
|
||||
return scaledcolor;
|
||||
}
|
||||
return RGBW32(r, g, b, w);
|
||||
}
|
||||
|
||||
void setRandomColor(byte* rgb)
|
||||
|
@ -60,8 +60,12 @@
|
||||
#define WLED_MAX_BUSSES 6 // will allow 4 digital & 2 analog
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 4
|
||||
#else
|
||||
// the 10th digital bus (I2S0) will prevent Audioreactive usermod from functioning (it is last used though)
|
||||
// the last digital bus (I2S0) will prevent Audioreactive usermod from functioning
|
||||
#ifndef WLED_USE_PARALLEL_I2S
|
||||
#define WLED_MAX_BUSSES 10
|
||||
#else
|
||||
#define WLED_MAX_BUSSES 17
|
||||
#endif
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 0
|
||||
#endif
|
||||
#endif
|
||||
@ -174,6 +178,9 @@
|
||||
#define USERMOD_ID_HTTP_PULL_LIGHT_CONTROL 46 //usermod "usermod_v2_HttpPullLightControl.h"
|
||||
#define USERMOD_ID_TETRISAI 47 //Usermod "usermod_v2_tetris.h"
|
||||
#define USERMOD_ID_MAX17048 48 //Usermod "usermod_max17048.h"
|
||||
#define USERMOD_ID_BME68X 49 //Usermod "usermod_bme68x.h
|
||||
#define USERMOD_ID_INA226 50 //Usermod "usermod_ina226.h"
|
||||
#define USERMOD_ID_AHT10 51 //Usermod "usermod_aht10.h"
|
||||
|
||||
//Access point behavior
|
||||
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
|
||||
@ -268,6 +275,7 @@
|
||||
#define TYPE_SK6812_RGBW 30
|
||||
#define TYPE_TM1814 31
|
||||
#define TYPE_WS2805 32 //RGB + WW + CW
|
||||
#define TYPE_TM1914 33 //RGB
|
||||
//"Analog" types (40-47)
|
||||
#define TYPE_ONOFF 40 //binary output (relays etc.; NOT PWM)
|
||||
#define TYPE_ANALOG_1CH 41 //single channel PWM. Uses value of brightest RGBW channel
|
||||
|
@ -358,7 +358,7 @@ button {
|
||||
|
||||
#putil, #segutil, #segutil2 {
|
||||
min-height: 42px;
|
||||
margin: 13px auto 0;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#segutil .segin {
|
||||
|
@ -272,6 +272,7 @@ function onLoad()
|
||||
|
||||
selectSlot(0);
|
||||
updateTablinks(0);
|
||||
handleLocationHash();
|
||||
cpick.on("input:end", () => {setColor(1);});
|
||||
cpick.on("color:change", () => {updatePSliders()});
|
||||
pmtLS = localStorage.getItem('wledPmt');
|
||||
@ -304,7 +305,6 @@ function updateTablinks(tabI)
|
||||
{
|
||||
var tablinks = gEBCN("tablinks");
|
||||
for (var i of tablinks) i.classList.remove('active');
|
||||
if (pcMode) return;
|
||||
tablinks[tabI].classList.add('active');
|
||||
}
|
||||
|
||||
@ -315,6 +315,21 @@ function openTab(tabI, force = false)
|
||||
_C.classList.toggle('smooth', false);
|
||||
_C.style.setProperty('--i', iSlide);
|
||||
updateTablinks(tabI);
|
||||
switch (tabI) {
|
||||
case 0: window.location.hash = "Colors"; break;
|
||||
case 1: window.location.hash = "Effects"; break;
|
||||
case 2: window.location.hash = "Segments"; break;
|
||||
case 3: window.location.hash = "Presets"; break;
|
||||
}
|
||||
}
|
||||
|
||||
function handleLocationHash() {
|
||||
switch (window.location.hash) {
|
||||
case "#Colors": openTab(0); break;
|
||||
case "#Effects": openTab(1); break;
|
||||
case "#Segments": openTab(2); break;
|
||||
case "#Presets": openTab(3); break;
|
||||
}
|
||||
}
|
||||
|
||||
var timeout;
|
||||
@ -430,7 +445,7 @@ function presetError(empty)
|
||||
if (bckstr.length > 10) hasBackup = true;
|
||||
} catch (e) {}
|
||||
|
||||
var cn = `<div class="pres c" ${empty?'style="padding:8px;margin-top: 16px;"':'onclick="pmtLast=0;loadPresets();" style="cursor:pointer;padding:8px;margin-top: 16px;"'}>`;
|
||||
var cn = `<div class="pres c" style="padding:8px;margin-bottom:8px;${empty?'':'cursor:pointer;'}" ${empty?'':'onclick="pmtLast=0;loadPresets();"'}>`;
|
||||
if (empty)
|
||||
cn += `You have no presets yet!`;
|
||||
else
|
||||
@ -442,8 +457,8 @@ function presetError(empty)
|
||||
cn += `However, there is backup preset data of a previous installation available.<br>(Saving a preset will hide this and overwrite the backup)`;
|
||||
else
|
||||
cn += `Here is a backup of the last known good state:`;
|
||||
cn += `<textarea id="bck"></textarea><br><button class="btn" onclick="cpBck()">Copy to clipboard</button>`;
|
||||
cn += `<br><button type="button" class="btn" onclick="restore(gId('bck').value)">Restore</button>`;
|
||||
cn += `<textarea id="bck"></textarea><br><button class="btn" style="margin-top:12px;" onclick="cpBck()">Copy to clipboard</button>`;
|
||||
cn += `<br><button type="button" class="btn" style="margin-top:12px;" onclick="restore(gId('bck').value)">Restore</button>`;
|
||||
}
|
||||
cn += `</div>`;
|
||||
gId('pcont').innerHTML = cn;
|
||||
@ -786,6 +801,7 @@ function populateSegments(s)
|
||||
`<option value="1" ${inst.m12==1?' selected':''}>Bar</option>`+
|
||||
`<option value="2" ${inst.m12==2?' selected':''}>Arc</option>`+
|
||||
`<option value="3" ${inst.m12==3?' selected':''}>Corner</option>`+
|
||||
`<option value="4" ${inst.m12==4?' selected':''}>Pinwheel</option>`+
|
||||
`</select></div>`+
|
||||
`</div>`;
|
||||
let sndSim = `<div data-snd="si" class="lbl-s hide">Sound sim<br>`+
|
||||
@ -863,14 +879,11 @@ function populateSegments(s)
|
||||
gId("segcont").classList.remove("hide");
|
||||
let noNewSegs = (lowestUnused >= maxSeg);
|
||||
resetUtil(noNewSegs);
|
||||
if (gId('selall')) gId('selall').checked = true;
|
||||
for (var i = 0; i <= lSeg; i++) {
|
||||
if (!gId(`seg${i}`)) continue;
|
||||
updateLen(i);
|
||||
updateTrail(gId(`seg${i}bri`));
|
||||
gId(`segr${i}`).classList.add("hide");
|
||||
//if (i<lSeg) gId(`segd${i}`).classList.add("hide"); // hide delete button for all but last
|
||||
if (!gId(`seg${i}sel`).checked && gId('selall')) gId('selall').checked = false; // uncheck if at least one is unselected.
|
||||
}
|
||||
if (segCount < 2) {
|
||||
gId(`segd${lSeg}`).classList.add("hide"); // hide delete if only one segment
|
||||
@ -1462,8 +1475,6 @@ function readState(s,command=false)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (s.seg.length>2) d.querySelectorAll(".pop").forEach((e)=>{e.classList.remove("hide");});
|
||||
|
||||
var cd = gId('csl').querySelectorAll("button");
|
||||
for (let e = cd.length-1; e >= 0; e--) {
|
||||
cd[e].dataset.r = i.col[e][0];
|
||||
@ -1838,7 +1849,7 @@ function makeSeg()
|
||||
});
|
||||
var cn = `<div class="seg lstI expanded">`+
|
||||
`<div class="segin">`+
|
||||
`<input type="text" id="seg${lu}t" autocomplete="off" maxlength=32 value="" placeholder="New segment ${lu}"/>`+
|
||||
`<input class="ptxt show" type="text" id="seg${lu}t" autocomplete="off" maxlength=32 value="" placeholder="New segment ${lu}"/>`+
|
||||
`<table class="segt">`+
|
||||
`<tr>`+
|
||||
`<td width="38%">${isM?'Start X':'Start LED'}</td>`+
|
||||
@ -1864,13 +1875,19 @@ function makeSeg()
|
||||
|
||||
function resetUtil(off=false)
|
||||
{
|
||||
gId('segutil').innerHTML = `<div class="seg btn btn-s${off?' off':''}" style="padding:0;">`
|
||||
gId('segutil').innerHTML = `<div class="seg btn btn-s${off?' off':''}" style="padding:0;margin-bottom:12px;">`
|
||||
+ '<label class="check schkl"><input type="checkbox" id="selall" onchange="selSegAll(this)"><span class="checkmark"></span></label>'
|
||||
+ `<div class="segname" ${off?'':'onclick="makeSeg()"'}><i class="icons btn-icon"></i>Add segment</div>`
|
||||
+ '<div class="pop hide" onclick="event.stopPropagation();">'
|
||||
+ `<i class="icons g-icon" title="Select group" onclick="this.nextElementSibling.classList.toggle('hide');"></i>`
|
||||
+ '<div class="pop-c hide"><span style="color:var(--c-f);" onclick="selGrp(0);">➊</span><span style="color:var(--c-r);" onclick="selGrp(1);">➋</span><span style="color:var(--c-g);" onclick="selGrp(2);">➌</span><span style="color:var(--c-l);" onclick="selGrp(3);">➍</span></div>'
|
||||
+ '</div></div>';
|
||||
gId('selall').checked = true;
|
||||
for (var i = 0; i <= lSeg; i++) {
|
||||
if (!gId(`seg${i}`)) continue;
|
||||
if (!gId(`seg${i}sel`).checked) gId('selall').checked = false; // uncheck if at least one is unselected.
|
||||
}
|
||||
if (lSeg>2) d.querySelectorAll("#Segments .pop").forEach((e)=>{e.classList.remove("hide");});
|
||||
}
|
||||
|
||||
function makePlSel(el, incPl=false)
|
||||
@ -3050,8 +3067,7 @@ function togglePcMode(fromB = false)
|
||||
pcMode = (wW >= 1024) && pcModeA;
|
||||
if (cpick) cpick.resize(pcMode && wW>1023 && wW<1250 ? 230 : 260); // for tablet in landscape
|
||||
if (!fromB && ((wW < 1024 && lastw < 1024) || (wW >= 1024 && lastw >= 1024))) return; // no change in size and called from size()
|
||||
openTab(0, true);
|
||||
updateTablinks(0);
|
||||
if (pcMode) openTab(0, true);
|
||||
gId('buttonPcm').className = (pcMode) ? "active":"";
|
||||
gId('bot').style.height = (pcMode && !cfg.comp.pcmbot) ? "0":"auto";
|
||||
sCol('--bh', gId('bot').clientHeight + "px");
|
||||
@ -3213,6 +3229,7 @@ size();
|
||||
_C.style.setProperty('--n', N);
|
||||
|
||||
window.addEventListener('resize', size, true);
|
||||
window.addEventListener('hashchange', handleLocationHash);
|
||||
|
||||
_C.addEventListener('mousedown', lock, false);
|
||||
_C.addEventListener('touchstart', lock, false);
|
||||
|
@ -5,12 +5,12 @@
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
|
||||
<title>LED Settings</title>
|
||||
<script>
|
||||
var d=document,laprev=55,maxB=1,maxV=0,maxM=4000,maxPB=4096,maxL=1333,maxLbquot=0; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32
|
||||
var d=document,laprev=55,maxB=1,maxV=0,maxM=4000,maxPB=4096,maxL=1333,maxCO=10,maxLbquot=0; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32
|
||||
d.um_p = [];
|
||||
d.rsvd = [];
|
||||
d.ro_gpio = [];
|
||||
d.max_gpio = 50;
|
||||
var customStarts=false,startsDirty=[],maxCOOverrides=5;
|
||||
var customStarts=false,startsDirty=[];
|
||||
var loc = false, locip, locproto = "http:";
|
||||
function H(){window.open("https://kno.wled.ge/features/settings/#led-settings");}
|
||||
function B(){window.open(getURL("/settings"),"_self");}
|
||||
@ -57,8 +57,8 @@
|
||||
x.style.animation = 'none';
|
||||
timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900);
|
||||
}
|
||||
function bLimits(b,v,p,m,l) {
|
||||
maxB = b; maxV = v; maxM = m; maxPB = p; maxL = l;
|
||||
function bLimits(b,v,p,m,l,o) {
|
||||
maxB = b; maxV = v; maxM = m; maxPB = p; maxL = l; maxCO = o;
|
||||
}
|
||||
function pinsOK() {
|
||||
var ok = true;
|
||||
@ -138,7 +138,8 @@
|
||||
gId("psuMA").style.display = ppl ? 'none' : 'inline';
|
||||
gId("ppldis").style.display = ppl ? 'inline' : 'none';
|
||||
// set PPL minimum value and clear actual PPL limit if ABL disabled
|
||||
d.Sf.querySelectorAll("#mLC input[name^=MA]").forEach((i,n)=>{
|
||||
d.Sf.querySelectorAll("#mLC input[name^=MA]").forEach((i,x)=>{
|
||||
var n = String.fromCharCode((x<10?48:55)+x);
|
||||
gId("PSU"+n).style.display = ppl ? "inline" : "none";
|
||||
const t = parseInt(d.Sf["LT"+n].value); // LED type SELECT
|
||||
const c = parseInt(d.Sf["LC"+n].value); //get LED count
|
||||
@ -153,7 +154,7 @@
|
||||
{
|
||||
const t = parseInt(d.Sf["LT"+n].value); // LED type SELECT
|
||||
gId('LAdis'+n).style.display = s.selectedIndex==5 ? "inline" : "none";
|
||||
d.Sf["LA"+n].value = s.value==="0" ? 55 : s.value;
|
||||
if (s.value!=="0") d.Sf["LA"+n].value = s.value;
|
||||
d.Sf["LA"+n].min = (isVir(t) || isAna(t)) ? 0 : 1;
|
||||
}
|
||||
function setABL()
|
||||
@ -164,8 +165,9 @@
|
||||
if (parseInt(i.value) > 0) d.Sf.ABL.checked = true;
|
||||
});
|
||||
// select appropriate LED current
|
||||
d.Sf.querySelectorAll("#mLC select[name^=LAsel]").forEach((sel,n)=>{
|
||||
d.Sf.querySelectorAll("#mLC select[name^=LAsel]").forEach((sel,x)=>{
|
||||
sel.value = 0; // set custom
|
||||
var n = String.fromCharCode((x<10?48:55)+x);
|
||||
switch (parseInt(d.Sf["LA"+n].value)) {
|
||||
case 0: break; // disable ABL
|
||||
case 15: sel.value = 15; break;
|
||||
@ -358,10 +360,11 @@
|
||||
gId("json").style.display = d.Sf.IT.value==8 ? "" : "none";
|
||||
}
|
||||
function lastEnd(i) {
|
||||
if (i<1) return 0;
|
||||
v = parseInt(d.getElementsByName("LS"+(i-1))[0].value) + parseInt(d.getElementsByName("LC"+(i-1))[0].value);
|
||||
var t = parseInt(d.getElementsByName("LT"+(i-1))[0].value);
|
||||
if (t > 31 && t < 48) v = 1; //PWM busses
|
||||
if (i-- < 1) return 0;
|
||||
var s = String.fromCharCode((i<10?48:55)+i);
|
||||
v = parseInt(d.getElementsByName("LS"+s)[0].value) + parseInt(d.getElementsByName("LC"+s)[0].value);
|
||||
var t = parseInt(d.getElementsByName("LT"+s)[0].value);
|
||||
if (isPWM(t)) v = 1; //PWM busses
|
||||
if (isNaN(v)) return 0;
|
||||
return v;
|
||||
}
|
||||
@ -371,6 +374,7 @@
|
||||
var i = o.length;
|
||||
|
||||
if ((n==1 && i>=maxB+maxV) || (n==-1 && i==0)) return;
|
||||
var s = String.fromCharCode((i<10?48:55)+i);
|
||||
|
||||
var f = gId("mLC");
|
||||
if (n==1) {
|
||||
@ -378,7 +382,7 @@
|
||||
var cn = `<div class="iST">
|
||||
<hr class="sml">
|
||||
${i+1}:
|
||||
<select name="LT${i}" onchange="UI(true)">${i>=maxB ? '' :
|
||||
<select name="LT${s}" onchange="UI(true)">${i>=maxB ? '' :
|
||||
'<option value="22" selected>WS281x</option>\
|
||||
<option value="30">SK6812/WS2814 RGBW</option>\
|
||||
<option value="31">TM1814</option>\
|
||||
@ -386,6 +390,7 @@ ${i+1}:
|
||||
<option value="25">TM1829</option>\
|
||||
<option value="26">UCS8903</option>\
|
||||
<option value="27">APA106/PL9823</option>\
|
||||
<option value="33">TM1914</option>\
|
||||
<option value="28">FW1906 GRBCW</option>\
|
||||
<option value="29">UCS8904 RGBW</option>\
|
||||
<option value="32">WS2805 RGBCW</option>\
|
||||
@ -408,8 +413,8 @@ ${i+1}:
|
||||
<option value="88">DDP RGBW (network)</option>
|
||||
<option value="89">Art-Net RGBW (network)</option>
|
||||
</select><br>
|
||||
<div id="abl${i}">
|
||||
mA/LED: <select name="LAsel${i}" onchange="enLA(this,${i});UI();">
|
||||
<div id="abl${s}">
|
||||
mA/LED: <select name="LAsel${s}" onchange="enLA(this,'${s}');UI();">
|
||||
<option value="55" selected>55mA (typ. 5V WS281x)</option>
|
||||
<option value="35">35mA (eco WS2812)</option>
|
||||
<option value="30">30mA (typ. 12V)</option>
|
||||
@ -417,11 +422,11 @@ mA/LED: <select name="LAsel${i}" onchange="enLA(this,${i});UI();">
|
||||
<option value="15">15mA (seed/fairy pixels)</option>
|
||||
<option value="0">Custom</option>
|
||||
</select><br>
|
||||
<div id="LAdis${i}" style="display: none;">max. mA/LED: <input name="LA${i}" type="number" min="1" max="254" oninput="UI()"> mA<br></div>
|
||||
<div id="PSU${i}">PSU: <input name="MA${i}" type="number" class="xl" min="250" max="65000" oninput="UI()" value="250"> mA<br></div>
|
||||
<div id="LAdis${s}" style="display: none;">max. mA/LED: <input name="LA${s}" type="number" min="1" max="255" oninput="UI()"> mA<br></div>
|
||||
<div id="PSU${s}">PSU: <input name="MA${s}" type="number" class="xl" min="250" max="65000" oninput="UI()" value="250"> mA<br></div>
|
||||
</div>
|
||||
<div id="co${i}" style="display:inline">Color Order:
|
||||
<select name="CO${i}">
|
||||
<div id="co${s}" style="display:inline">Color Order:
|
||||
<select name="CO${s}">
|
||||
<option value="0">GRB</option>
|
||||
<option value="1">RGB</option>
|
||||
<option value="2">BRG</option>
|
||||
@ -429,21 +434,21 @@ mA/LED: <select name="LAsel${i}" onchange="enLA(this,${i});UI();">
|
||||
<option value="4">BGR</option>
|
||||
<option value="5">GBR</option>
|
||||
</select></div>
|
||||
<div id="dig${i}w" style="display:none">Swap: <select name="WO${i}"><option value="0">None</option><option value="1">W & B</option><option value="2">W & G</option><option value="3">W & R</option></select></div>
|
||||
<div id="dig${i}l" style="display:none">Clock: <select name="SP${i}"><option value="0">Slowest</option><option value="1">Slow</option><option value="2">Normal</option><option value="3">Fast</option><option value="4">Fastest</option></select></div>
|
||||
<div id="dig${s}w" style="display:none">Swap: <select name="WO${s}"><option value="0">None</option><option value="1">W & B</option><option value="2">W & G</option><option value="3">W & R</option></select></div>
|
||||
<div id="dig${s}l" style="display:none">Clock: <select name="SP${s}"><option value="0">Slowest</option><option value="1">Slow</option><option value="2">Normal</option><option value="3">Fast</option><option value="4">Fastest</option></select></div>
|
||||
<div>
|
||||
<span id="psd${i}">Start:</span> <input type="number" name="LS${i}" id="ls${i}" class="l starts" min="0" max="8191" value="${lastEnd(i)}" oninput="startsDirty[${i}]=true;UI();" required />
|
||||
<div id="dig${i}c" style="display:inline">Length: <input type="number" name="LC${i}" class="l" min="1" max="${maxPB}" value="1" required oninput="UI()" /></div><br>
|
||||
<span id="psd${s}">Start:</span> <input type="number" name="LS${s}" id="ls${s}" class="l starts" min="0" max="8191" value="${lastEnd(i)}" oninput="startsDirty[${i}]=true;UI();" required />
|
||||
<div id="dig${s}c" style="display:inline">Length: <input type="number" name="LC${s}" class="l" min="1" max="${maxPB}" value="1" required oninput="UI()" /></div><br>
|
||||
</div>
|
||||
<span id="p0d${i}">GPIO:</span><input type="number" name="L0${i}" required class="s" onchange="UI();pinUpd(this);"/>
|
||||
<span id="p1d${i}"></span><input type="number" name="L1${i}" class="s" onchange="UI();pinUpd(this);"/>
|
||||
<span id="p2d${i}"></span><input type="number" name="L2${i}" class="s" onchange="UI();pinUpd(this);"/>
|
||||
<span id="p3d${i}"></span><input type="number" name="L3${i}" class="s" onchange="UI();pinUpd(this);"/>
|
||||
<span id="p4d${i}"></span><input type="number" name="L4${i}" class="s" onchange="UI();pinUpd(this);"/>
|
||||
<div id="dig${i}r" style="display:inline"><br><span id="rev${i}">Reversed</span>: <input type="checkbox" name="CV${i}"></div>
|
||||
<div id="dig${i}s" style="display:inline"><br>Skip first LEDs: <input type="number" name="SL${i}" min="0" max="255" value="0" oninput="UI()"></div>
|
||||
<div id="dig${i}f" style="display:inline"><br>Off Refresh: <input id="rf${i}" type="checkbox" name="RF${i}"></div>
|
||||
<div id="dig${i}a" style="display:inline"><br>Auto-calculate white channel from RGB:<br><select name="AW${i}"><option value=0>None</option><option value=1>Brighter</option><option value=2>Accurate</option><option value=3>Dual</option><option value=4>Max</option></select> </div>
|
||||
<span id="p0d${s}">GPIO:</span><input type="number" name="L0${s}" required class="s" onchange="UI();pinUpd(this);"/>
|
||||
<span id="p1d${s}"></span><input type="number" name="L1${s}" class="s" onchange="UI();pinUpd(this);"/>
|
||||
<span id="p2d${s}"></span><input type="number" name="L2${s}" class="s" onchange="UI();pinUpd(this);"/>
|
||||
<span id="p3d${s}"></span><input type="number" name="L3${s}" class="s" onchange="UI();pinUpd(this);"/>
|
||||
<span id="p4d${s}"></span><input type="number" name="L4${s}" class="s" onchange="UI();pinUpd(this);"/>
|
||||
<div id="dig${s}r" style="display:inline"><br><span id="rev${s}">Reversed</span>: <input type="checkbox" name="CV${s}"></div>
|
||||
<div id="dig${s}s" style="display:inline"><br>Skip first LEDs: <input type="number" name="SL${s}" min="0" max="255" value="0" oninput="UI()"></div>
|
||||
<div id="dig${s}f" style="display:inline"><br>Off Refresh: <input id="rf${s}" type="checkbox" name="RF${s}"></div>
|
||||
<div id="dig${s}a" style="display:inline"><br>Auto-calculate white channel from RGB:<br><select name="AW${s}"><option value=0>None</option><option value=1>Brighter</option><option value=2>Accurate</option><option value=3>Dual</option><option value=4>Max</option></select> </div>
|
||||
</div>`;
|
||||
f.insertAdjacentHTML("beforeend", cn);
|
||||
}
|
||||
@ -461,14 +466,14 @@ mA/LED: <select name="LAsel${i}" onchange="enLA(this,${i});UI();">
|
||||
|
||||
function addCOM(start=0,len=1,co=0) {
|
||||
var i = d.getElementsByClassName("com_entry").length;
|
||||
if (i >= 10) return;
|
||||
|
||||
if (i >= maxCO) return;
|
||||
var s = String.fromCharCode((i<10?48:55)+i);
|
||||
var b = `<div class="com_entry">
|
||||
<hr class="sml">
|
||||
${i+1}: Start: <input type="number" name="XS${i}" id="xs${i}" class="l starts" min="0" max="65535" value="${start}" oninput="UI();" required="">
|
||||
Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65535" value="${len}" required="" oninput="UI()">
|
||||
${i+1}: Start: <input type="number" name="XS${s}" id="xs${s}" class="l starts" min="0" max="65535" value="${start}" oninput="UI();" required="">
|
||||
Length: <input type="number" name="XC${s}" id="xc${s}" class="l" min="1" max="65535" value="${len}" required="" oninput="UI()">
|
||||
<div>Color Order:
|
||||
<select id="xo${i}" name="XO${i}">
|
||||
<select id="xo${s}" name="XO${s}">
|
||||
<option value="0">GRB</option>
|
||||
<option value="1">RGB</option>
|
||||
<option value="2">BRG</option>
|
||||
@ -476,7 +481,7 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
|
||||
<option value="4">BGR</option>
|
||||
<option value="5">GBR</option>
|
||||
</select>
|
||||
Swap: <select id="xw${i}" name="XW${i}">
|
||||
Swap: <select id="xw${s}" name="XW${s}">
|
||||
<option value="0">Use global</option>
|
||||
<option value="1">W & B</option>
|
||||
<option value="2">W & G</option>
|
||||
@ -484,8 +489,8 @@ Swap: <select id="xw${i}" name="XW${i}">
|
||||
</select>
|
||||
</div></div>`;
|
||||
gId("com_entries").insertAdjacentHTML("beforeend", b);
|
||||
gId("xo"+i).value = co & 0x0F;
|
||||
gId("xw"+i).value = co >> 4;
|
||||
gId("xo"+s).value = co & 0x0F;
|
||||
gId("xw"+s).value = co >> 4;
|
||||
btnCOM(i+1);
|
||||
UI();
|
||||
}
|
||||
@ -501,7 +506,7 @@ Swap: <select id="xw${i}" name="XW${i}">
|
||||
|
||||
function resetCOM(_newMaxCOOverrides=undefined) {
|
||||
if (_newMaxCOOverrides) {
|
||||
maxCOOverrides = _newMaxCOOverrides;
|
||||
maxCO = _newMaxCOOverrides;
|
||||
}
|
||||
for (let e of d.getElementsByClassName("com_entry")) {
|
||||
e.remove();
|
||||
@ -510,16 +515,15 @@ Swap: <select id="xw${i}" name="XW${i}">
|
||||
}
|
||||
|
||||
function btnCOM(i) {
|
||||
gId("com_add").style.display = (i<maxCOOverrides) ? "inline":"none";
|
||||
gId("com_add").style.display = (i<maxCO) ? "inline":"none";
|
||||
gId("com_rem").style.display = (i>0) ? "inline":"none";
|
||||
}
|
||||
|
||||
function addBtn(i,p,t) {
|
||||
var c = gId("btns").innerHTML;
|
||||
var bt = "BT" + String.fromCharCode((i<10?48:55)+i);
|
||||
var be = "BE" + String.fromCharCode((i<10?48:55)+i);
|
||||
c += `Button ${i} GPIO: <input type="number" name="${bt}" onchange="UI()" class="xs" value="${p}">`;
|
||||
c += ` <select name="${be}">`
|
||||
var s = String.fromCharCode((i<10?48:55)+i);
|
||||
c += `Button ${i} GPIO: <input type="number" name="BT${s}" onchange="UI()" class="xs" value="${p}">`;
|
||||
c += ` <select name="BE${s}">`
|
||||
c += `<option value="0" ${t==0?"selected":""}>Disabled</option>`;
|
||||
c += `<option value="2" ${t==2?"selected":""}>Pushbutton</option>`;
|
||||
c += `<option value="3" ${t==3?"selected":""}>Push inverted</option>`;
|
||||
@ -530,7 +534,7 @@ Swap: <select id="xw${i}" name="XW${i}">
|
||||
c += `<option value="8" ${t==8?"selected":""}>Analog inverted</option>`;
|
||||
c += `<option value="9" ${t==9?"selected":""}>Touch (switch)</option>`;
|
||||
c += `</select>`;
|
||||
c += `<span style="cursor: pointer;" onclick="off('${bt}')"> ✕</span><br>`;
|
||||
c += `<span style="cursor: pointer;" onclick="off('BT${s}')"> ✕</span><br>`;
|
||||
gId("btns").innerHTML = c;
|
||||
}
|
||||
function tglSi(cs) {
|
||||
@ -619,7 +623,8 @@ Swap: <select id="xw${i}" name="XW${i}">
|
||||
}
|
||||
if (c.hw.relay) {
|
||||
d.getElementsByName("RL")[0].value = c.hw.relay.pin;
|
||||
d.getElementsByName("RM")[0].checked = c.hw.relay.inv;
|
||||
d.getElementsByName("RM")[0].checked = c.hw.relay.rev;
|
||||
d.getElementsByName("RO")[0].checked = c.hw.relay.odrain;
|
||||
}
|
||||
UI();
|
||||
}
|
||||
@ -822,7 +827,7 @@ Swap: <select id="xw${i}" name="XW${i}">
|
||||
Apply IR change to main segment only: <input type="checkbox" name="MSO"><br>
|
||||
<div id="json" style="display:none;">JSON file: <input type="file" name="data" accept=".json"><button type="button" class="sml" onclick="uploadFile('/ir.json')">Upload</button><br></div>
|
||||
<a href="https://kno.wled.ge/interfaces/infrared/" target="_blank">IR info</a><br>
|
||||
Relay GPIO: <input type="number" min="-1" max="48" name="RL" onchange="UI()" class="xs"> Invert <input type="checkbox" name="RM"><span style="cursor: pointer;" onclick="off('RL')"> ✕</span><br>
|
||||
Relay GPIO: <input type="number" min="-1" max="48" name="RL" onchange="UI()" class="xs"><span style="cursor: pointer;" onclick="off('RL')"> ✕</span> Invert <input type="checkbox" name="RM"> Open drain <input type="checkbox" name="RO"><br>
|
||||
<hr class="sml">
|
||||
<h3>Defaults</h3>
|
||||
Turn LEDs on after power up/reset: <input type="checkbox" name="BO"><br>
|
||||
|
@ -137,7 +137,7 @@
|
||||
<a href="https://github.com/Aircoookie/WLED/wiki/Contributors-and-credits" target="_blank">Contributors, dependencies and special thanks</a><br>
|
||||
A huge thank you to everyone who helped me create WLED!<br><br>
|
||||
(c) 2016-2024 Christian Schwinne <br>
|
||||
<i>Licensed under the <a href="https://github.com/Aircoookie/WLED/blob/master/LICENSE" target="_blank">MIT license</a></i><br><br>
|
||||
<i>Licensed under the <a href="https://github.com/Aircoookie/WLED/blob/master/LICENSE" target="_blank">MIT license</a></i><br><br>
|
||||
Server message: <span class="sip"> Response error! </span><hr>
|
||||
<div id="toast"></div>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
||||
|
@ -52,40 +52,42 @@
|
||||
}
|
||||
scanLoops = 0;
|
||||
|
||||
let cs = d.querySelectorAll("#wifi_entries input[type=text]");
|
||||
for (let input of (cs||[])) {
|
||||
let found = false;
|
||||
let select = cE("select");
|
||||
select.id = input.id;
|
||||
select.name = input.name;
|
||||
select.setAttribute("onchange", "T(this)");
|
||||
preScanSSID = input.value;
|
||||
if (networks.length > 0) {
|
||||
let cs = d.querySelectorAll("#wifi_entries input[type=text]");
|
||||
for (let input of (cs||[])) {
|
||||
let found = false;
|
||||
let select = cE("select");
|
||||
select.id = input.id;
|
||||
select.name = input.name;
|
||||
select.setAttribute("onchange", "T(this)");
|
||||
preScanSSID = input.value;
|
||||
|
||||
for (let i = 0; i < select.children.length; i++) {
|
||||
select.removeChild(select.children[i]);
|
||||
}
|
||||
|
||||
for (let i = 0; i < networks.length; i++) {
|
||||
const option = cE("option");
|
||||
|
||||
option.setAttribute("value", networks[i].ssid);
|
||||
option.textContent = `${networks[i].ssid} (${networks[i].rssi} dBm)`;
|
||||
|
||||
if (networks[i].ssid === input.value) {
|
||||
option.setAttribute("selected", "selected");
|
||||
found = true;
|
||||
for (let i = 0; i < select.children.length; i++) {
|
||||
select.removeChild(select.children[i]);
|
||||
}
|
||||
|
||||
for (let i = 0; i < networks.length; i++) {
|
||||
const option = cE("option");
|
||||
|
||||
option.setAttribute("value", networks[i].ssid);
|
||||
option.textContent = `${networks[i].ssid} (${networks[i].rssi} dBm)`;
|
||||
|
||||
if (networks[i].ssid === input.value) {
|
||||
option.setAttribute("selected", "selected");
|
||||
found = true;
|
||||
}
|
||||
|
||||
select.appendChild(option);
|
||||
}
|
||||
const option = cE("option");
|
||||
|
||||
option.setAttribute("value", "!Cs");
|
||||
option.textContent = "Other network...";
|
||||
select.appendChild(option);
|
||||
|
||||
if (input.value === "" || input.value === "Your_Network" || found) input.replaceWith(select);
|
||||
else select.remove();
|
||||
}
|
||||
const option = cE("option");
|
||||
|
||||
option.setAttribute("value", "!Cs");
|
||||
option.textContent = "Other network...";
|
||||
select.appendChild(option);
|
||||
|
||||
if (input.value === "" || input.value === "Your_Network" || found) input.replaceWith(select);
|
||||
else select.remove();
|
||||
}
|
||||
|
||||
button.disabled = false;
|
||||
|
@ -346,7 +346,6 @@ void handleArtnetPollReply(IPAddress ipAddress) {
|
||||
|
||||
switch (DMXMode) {
|
||||
case DMX_MODE_DISABLED:
|
||||
return; // nothing to do
|
||||
break;
|
||||
|
||||
case DMX_MODE_SINGLE_RGB:
|
||||
@ -391,9 +390,17 @@ void handleArtnetPollReply(IPAddress ipAddress) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint16_t i = startUniverse; i <= endUniverse; ++i) {
|
||||
sendArtnetPollReply(&artnetPollReply, ipAddress, i);
|
||||
if (DMXMode != DMX_MODE_DISABLED) {
|
||||
for (uint16_t i = startUniverse; i <= endUniverse; ++i) {
|
||||
sendArtnetPollReply(&artnetPollReply, ipAddress, i);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
if (e131ProxyUniverse > 0 && (DMXMode == DMX_MODE_DISABLED || (e131ProxyUniverse < startUniverse || e131ProxyUniverse > endUniverse))) {
|
||||
sendArtnetPollReply(&artnetPollReply, ipAddress, e131ProxyUniverse);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void prepareArtnetPollReply(ArtPollReply *reply) {
|
||||
|
@ -143,20 +143,8 @@ void handleImprovWifiScan();
|
||||
void sendImprovIPRPCResult(ImprovRPCType type);
|
||||
|
||||
//ir.cpp
|
||||
void applyRepeatActions();
|
||||
byte relativeChange(byte property, int8_t amount, byte lowerBoundary = 0, byte higherBoundary = 0xFF);
|
||||
void decodeIR(uint32_t code);
|
||||
void decodeIR24(uint32_t code);
|
||||
void decodeIR24OLD(uint32_t code);
|
||||
void decodeIR24CT(uint32_t code);
|
||||
void decodeIR40(uint32_t code);
|
||||
void decodeIR44(uint32_t code);
|
||||
void decodeIR21(uint32_t code);
|
||||
void decodeIR6(uint32_t code);
|
||||
void decodeIR9(uint32_t code);
|
||||
void decodeIRJson(uint32_t code);
|
||||
|
||||
void initIR();
|
||||
void deInitIR();
|
||||
void handleIR();
|
||||
|
||||
//json.cpp
|
||||
@ -410,7 +398,7 @@ void clearEEPROM();
|
||||
#endif
|
||||
|
||||
//wled_math.cpp
|
||||
#ifndef WLED_USE_REAL_MATH
|
||||
#if defined(ESP8266) && !defined(WLED_USE_REAL_MATH)
|
||||
template <typename T> T atan_t(T x);
|
||||
float cos_t(float phi);
|
||||
float sin_t(float x);
|
||||
@ -421,14 +409,14 @@ void clearEEPROM();
|
||||
float fmod_t(float num, float denom);
|
||||
#else
|
||||
#include <math.h>
|
||||
#define sin_t sin
|
||||
#define cos_t cos
|
||||
#define tan_t tan
|
||||
#define asin_t asin
|
||||
#define acos_t acos
|
||||
#define atan_t atan
|
||||
#define fmod_t fmod
|
||||
#define floor_t floor
|
||||
#define sin_t sinf
|
||||
#define cos_t cosf
|
||||
#define tan_t tanf
|
||||
#define asin_t asinf
|
||||
#define acos_t acosf
|
||||
#define atan_t atanf
|
||||
#define fmod_t fmodf
|
||||
#define floor_t floorf
|
||||
#endif
|
||||
|
||||
//wled_serial.cpp
|
||||
|
@ -381,11 +381,15 @@ void updateFSInfo() {
|
||||
// original idea by @akaricchi (https://github.com/Akaricchi)
|
||||
// returns a pointer to the PSRAM buffer, updates size parameter
|
||||
static const uint8_t *getPresetCache(size_t &size) {
|
||||
static unsigned long presetsCachedTime;
|
||||
static uint8_t *presetsCached;
|
||||
static size_t presetsCachedSize;
|
||||
static unsigned long presetsCachedTime = 0;
|
||||
static uint8_t *presetsCached = nullptr;
|
||||
static size_t presetsCachedSize = 0;
|
||||
static byte presetsCachedValidate = 0;
|
||||
|
||||
if (presetsModifiedTime != presetsCachedTime) {
|
||||
//if (presetsModifiedTime != presetsCachedTime) DEBUG_PRINTLN(F("getPresetCache(): presetsModifiedTime changed."));
|
||||
//if (presetsCachedValidate != cacheInvalidate) DEBUG_PRINTLN(F("getPresetCache(): cacheInvalidate changed."));
|
||||
|
||||
if ((presetsModifiedTime != presetsCachedTime) || (presetsCachedValidate != cacheInvalidate)) {
|
||||
if (presetsCached) {
|
||||
free(presetsCached);
|
||||
presetsCached = nullptr;
|
||||
@ -396,6 +400,7 @@ static const uint8_t *getPresetCache(size_t &size) {
|
||||
File file = WLED_FS.open(FPSTR(getPresetsFileName()), "r");
|
||||
if (file) {
|
||||
presetsCachedTime = presetsModifiedTime;
|
||||
presetsCachedValidate = cacheInvalidate;
|
||||
presetsCachedSize = 0;
|
||||
presetsCached = (uint8_t*)ps_malloc(file.size() + 1);
|
||||
if (presetsCached) {
|
||||
|
@ -210,7 +210,7 @@ void sendImprovInfoResponse() {
|
||||
//Use serverDescription if it has been changed from the default "WLED", else mDNS name
|
||||
bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0);
|
||||
char vString[20];
|
||||
sprintf_P(vString, PSTR("0.15.0-b2/%i"), VERSION);
|
||||
sprintf_P(vString, PSTR("0.15.0-b3/%i"), VERSION);
|
||||
const char *str[4] = {"WLED", vString, bString, useMdnsName ? cmDNS : serverDescription};
|
||||
|
||||
sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str);
|
||||
|
254
wled00/ir.cpp
@ -1,20 +1,14 @@
|
||||
#include "wled.h"
|
||||
|
||||
#ifndef WLED_DISABLE_INFRARED
|
||||
#include "ir_codes.h"
|
||||
|
||||
/*
|
||||
* Infrared sensor support for generic 24/40/44 key RGB remotes
|
||||
* Infrared sensor support for several generic RGB remotes and custom JSON remote
|
||||
*/
|
||||
|
||||
#if defined(WLED_DISABLE_INFRARED)
|
||||
void handleIR(){}
|
||||
#else
|
||||
|
||||
IRrecv* irrecv;
|
||||
//change pin in NpbWrapper.h
|
||||
|
||||
decode_results results;
|
||||
|
||||
unsigned long irCheckedTime = 0;
|
||||
uint32_t lastValidCode = 0;
|
||||
byte lastRepeatableAction = ACTION_NONE;
|
||||
@ -35,16 +29,16 @@ uint8_t lastIR6ColourIdx = 0;
|
||||
// print("%d values: %s" % (len(result), result))
|
||||
//
|
||||
// It would be hard to maintain repeatable steps if calculating this on the fly.
|
||||
const byte brightnessSteps[] = {
|
||||
const uint8_t brightnessSteps[] = {
|
||||
5, 7, 9, 12, 16, 20, 26, 34, 43, 56, 72, 93, 119, 154, 198, 255
|
||||
};
|
||||
const size_t numBrightnessSteps = sizeof(brightnessSteps) / sizeof(uint8_t);
|
||||
|
||||
// increment `bri` to the next `brightnessSteps` value
|
||||
void incBrightness()
|
||||
static void incBrightness()
|
||||
{
|
||||
// dumb incremental search is efficient enough for so few items
|
||||
for (uint8_t index = 0; index < numBrightnessSteps; ++index)
|
||||
for (unsigned index = 0; index < numBrightnessSteps; ++index)
|
||||
{
|
||||
if (brightnessSteps[index] > bri)
|
||||
{
|
||||
@ -56,7 +50,7 @@ void incBrightness()
|
||||
}
|
||||
|
||||
// decrement `bri` to the next `brightnessSteps` value
|
||||
void decBrightness()
|
||||
static void decBrightness()
|
||||
{
|
||||
// dumb incremental search is efficient enough for so few items
|
||||
for (int index = numBrightnessSteps - 1; index >= 0; --index)
|
||||
@ -70,12 +64,12 @@ void decBrightness()
|
||||
}
|
||||
}
|
||||
|
||||
void presetFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID)
|
||||
static void presetFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID)
|
||||
{
|
||||
applyPresetWithFallback(presetID, CALL_MODE_BUTTON_PRESET, effectID, paletteID);
|
||||
}
|
||||
|
||||
byte relativeChange(byte property, int8_t amount, byte lowerBoundary, byte higherBoundary)
|
||||
static byte relativeChange(byte property, int8_t amount, byte lowerBoundary = 0, byte higherBoundary = 0xFF)
|
||||
{
|
||||
int16_t new_val = (int16_t) property + amount;
|
||||
if (lowerBoundary >= higherBoundary) return property;
|
||||
@ -84,10 +78,10 @@ byte relativeChange(byte property, int8_t amount, byte lowerBoundary, byte highe
|
||||
return (byte)constrain(new_val, 0, 255);
|
||||
}
|
||||
|
||||
void changeEffect(uint8_t fx)
|
||||
static void changeEffect(uint8_t fx)
|
||||
{
|
||||
if (irApplyToAllSelected) {
|
||||
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
strip.setMode(i, fx);
|
||||
@ -100,10 +94,10 @@ void changeEffect(uint8_t fx)
|
||||
stateChanged = true;
|
||||
}
|
||||
|
||||
void changePalette(uint8_t pal)
|
||||
static void changePalette(uint8_t pal)
|
||||
{
|
||||
if (irApplyToAllSelected) {
|
||||
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
seg.setPalette(pal);
|
||||
@ -116,13 +110,13 @@ void changePalette(uint8_t pal)
|
||||
stateChanged = true;
|
||||
}
|
||||
|
||||
void changeEffectSpeed(int8_t amount)
|
||||
static void changeEffectSpeed(int8_t amount)
|
||||
{
|
||||
if (effectCurrent != 0) {
|
||||
int16_t new_val = (int16_t) effectSpeed + amount;
|
||||
effectSpeed = (byte)constrain(new_val,0,255);
|
||||
if (irApplyToAllSelected) {
|
||||
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
seg.speed = effectSpeed;
|
||||
@ -134,10 +128,7 @@ void changeEffectSpeed(int8_t amount)
|
||||
}
|
||||
} else { // if Effect == "solid Color", change the hue of the primary color
|
||||
Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
|
||||
CRGB fastled_col;
|
||||
fastled_col.red = R(sseg.colors[0]);
|
||||
fastled_col.green = G(sseg.colors[0]);
|
||||
fastled_col.blue = B(sseg.colors[0]);
|
||||
CRGB fastled_col = CRGB(sseg.colors[0]);
|
||||
CHSV prim_hsv = rgb2hsv_approximate(fastled_col);
|
||||
int16_t new_val = (int16_t)prim_hsv.h + amount;
|
||||
if (new_val > 255) new_val -= 255; // roll-over if bigger than 255
|
||||
@ -145,7 +136,7 @@ void changeEffectSpeed(int8_t amount)
|
||||
prim_hsv.h = (byte)new_val;
|
||||
hsv2rgb_rainbow(prim_hsv, fastled_col);
|
||||
if (irApplyToAllSelected) {
|
||||
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
seg.colors[0] = RGBW32(fastled_col.red, fastled_col.green, fastled_col.blue, W(sseg.colors[0]));
|
||||
@ -163,13 +154,13 @@ void changeEffectSpeed(int8_t amount)
|
||||
lastRepeatableValue = amount;
|
||||
}
|
||||
|
||||
void changeEffectIntensity(int8_t amount)
|
||||
static void changeEffectIntensity(int8_t amount)
|
||||
{
|
||||
if (effectCurrent != 0) {
|
||||
int16_t new_val = (int16_t) effectIntensity + amount;
|
||||
effectIntensity = (byte)constrain(new_val,0,255);
|
||||
if (irApplyToAllSelected) {
|
||||
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
seg.intensity = effectIntensity;
|
||||
@ -181,16 +172,13 @@ void changeEffectIntensity(int8_t amount)
|
||||
}
|
||||
} else { // if Effect == "solid Color", change the saturation of the primary color
|
||||
Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
|
||||
CRGB fastled_col;
|
||||
fastled_col.red = R(sseg.colors[0]);
|
||||
fastled_col.green = G(sseg.colors[0]);
|
||||
fastled_col.blue = B(sseg.colors[0]);
|
||||
CRGB fastled_col = CRGB(sseg.colors[0]);
|
||||
CHSV prim_hsv = rgb2hsv_approximate(fastled_col);
|
||||
int16_t new_val = (int16_t) prim_hsv.s + amount;
|
||||
prim_hsv.s = (byte)constrain(new_val,0,255); // constrain to 0-255
|
||||
hsv2rgb_rainbow(prim_hsv, fastled_col);
|
||||
if (irApplyToAllSelected) {
|
||||
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
seg.colors[0] = RGBW32(fastled_col.red, fastled_col.green, fastled_col.blue, W(sseg.colors[0]));
|
||||
@ -208,11 +196,11 @@ void changeEffectIntensity(int8_t amount)
|
||||
lastRepeatableValue = amount;
|
||||
}
|
||||
|
||||
void changeColor(uint32_t c, int16_t cct=-1)
|
||||
static void changeColor(uint32_t c, int16_t cct=-1)
|
||||
{
|
||||
if (irApplyToAllSelected) {
|
||||
// main segment may not be selected!
|
||||
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
byte capabilities = seg.getLightCapabilities();
|
||||
@ -249,7 +237,7 @@ void changeColor(uint32_t c, int16_t cct=-1)
|
||||
stateChanged = true;
|
||||
}
|
||||
|
||||
void changeWhite(int8_t amount, int16_t cct=-1)
|
||||
static void changeWhite(int8_t amount, int16_t cct=-1)
|
||||
{
|
||||
Segment& seg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
|
||||
byte r = R(seg.colors[0]);
|
||||
@ -259,72 +247,7 @@ void changeWhite(int8_t amount, int16_t cct=-1)
|
||||
changeColor(RGBW32(r, g, b, w), cct);
|
||||
}
|
||||
|
||||
void decodeIR(uint32_t code)
|
||||
{
|
||||
if (code == 0xFFFFFFFF) {
|
||||
//repeated code, continue brightness up/down
|
||||
irTimesRepeated++;
|
||||
applyRepeatActions();
|
||||
return;
|
||||
}
|
||||
lastValidCode = 0; irTimesRepeated = 0;
|
||||
lastRepeatableAction = ACTION_NONE;
|
||||
|
||||
if (irEnabled == 8) { // any remote configurable with ir.json file
|
||||
decodeIRJson(code);
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
return;
|
||||
}
|
||||
if (code > 0xFFFFFF) return; //invalid code
|
||||
|
||||
switch (irEnabled) {
|
||||
case 1:
|
||||
if (code > 0xF80000) decodeIR24OLD(code); // white 24-key remote (old) - it sends 0xFF0000 values
|
||||
else decodeIR24(code); // 24-key remote - 0xF70000 to 0xF80000
|
||||
break;
|
||||
case 2: decodeIR24CT(code); break; // white 24-key remote with CW, WW, CT+ and CT- keys
|
||||
case 3: decodeIR40(code); break; // blue 40-key remote with 25%, 50%, 75% and 100% keys
|
||||
case 4: decodeIR44(code); break; // white 44-key remote with color-up/down keys and DIY1 to 6 keys
|
||||
case 5: decodeIR21(code); break; // white 21-key remote
|
||||
case 6: decodeIR6(code); break; // black 6-key learning remote defaults: "CH" controls brightness,
|
||||
// "VOL +" controls effect, "VOL -" controls colour/palette, "MUTE"
|
||||
// sets bright plain white
|
||||
case 7: decodeIR9(code); break;
|
||||
//case 8: return; // ir.json file, handled above switch statement
|
||||
}
|
||||
|
||||
if (nightlightActive && bri == 0) nightlightActive = false;
|
||||
stateUpdated(CALL_MODE_BUTTON); //for notifier, IR is considered a button input
|
||||
}
|
||||
|
||||
void applyRepeatActions()
|
||||
{
|
||||
if (irEnabled == 8) {
|
||||
decodeIRJson(lastValidCode);
|
||||
return;
|
||||
} else switch (lastRepeatableAction) {
|
||||
case ACTION_BRIGHT_UP : incBrightness(); stateUpdated(CALL_MODE_BUTTON); return;
|
||||
case ACTION_BRIGHT_DOWN : decBrightness(); stateUpdated(CALL_MODE_BUTTON); return;
|
||||
case ACTION_SPEED_UP : changeEffectSpeed(lastRepeatableValue); stateUpdated(CALL_MODE_BUTTON); return;
|
||||
case ACTION_SPEED_DOWN : changeEffectSpeed(lastRepeatableValue); stateUpdated(CALL_MODE_BUTTON); return;
|
||||
case ACTION_INTENSITY_UP : changeEffectIntensity(lastRepeatableValue); stateUpdated(CALL_MODE_BUTTON); return;
|
||||
case ACTION_INTENSITY_DOWN : changeEffectIntensity(lastRepeatableValue); stateUpdated(CALL_MODE_BUTTON); return;
|
||||
default: break;
|
||||
}
|
||||
if (lastValidCode == IR40_WPLUS) {
|
||||
changeWhite(10);
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
} else if (lastValidCode == IR40_WMINUS) {
|
||||
changeWhite(-10);
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
} else if ((lastValidCode == IR24_ON || lastValidCode == IR40_ON) && irTimesRepeated > 7 ) {
|
||||
nightlightActive = true;
|
||||
nightlightStartTime = millis();
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
}
|
||||
}
|
||||
|
||||
void decodeIR24(uint32_t code)
|
||||
static void decodeIR24(uint32_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case IR24_BRIGHTER : incBrightness(); break;
|
||||
@ -356,7 +279,7 @@ void decodeIR24(uint32_t code)
|
||||
lastValidCode = code;
|
||||
}
|
||||
|
||||
void decodeIR24OLD(uint32_t code)
|
||||
static void decodeIR24OLD(uint32_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case IR24_OLD_BRIGHTER : incBrightness(); break;
|
||||
@ -388,7 +311,7 @@ void decodeIR24OLD(uint32_t code)
|
||||
lastValidCode = code;
|
||||
}
|
||||
|
||||
void decodeIR24CT(uint32_t code)
|
||||
static void decodeIR24CT(uint32_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case IR24_CT_BRIGHTER : incBrightness(); break;
|
||||
@ -420,7 +343,7 @@ void decodeIR24CT(uint32_t code)
|
||||
lastValidCode = code;
|
||||
}
|
||||
|
||||
void decodeIR40(uint32_t code)
|
||||
static void decodeIR40(uint32_t code)
|
||||
{
|
||||
Segment& seg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
|
||||
byte r = R(seg.colors[0]);
|
||||
@ -473,7 +396,7 @@ void decodeIR40(uint32_t code)
|
||||
lastValidCode = code;
|
||||
}
|
||||
|
||||
void decodeIR44(uint32_t code)
|
||||
static void decodeIR44(uint32_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case IR44_BPLUS : incBrightness(); break;
|
||||
@ -525,7 +448,7 @@ void decodeIR44(uint32_t code)
|
||||
lastValidCode = code;
|
||||
}
|
||||
|
||||
void decodeIR21(uint32_t code)
|
||||
static void decodeIR21(uint32_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case IR21_BRIGHTER: incBrightness(); break;
|
||||
@ -554,7 +477,7 @@ void decodeIR21(uint32_t code)
|
||||
lastValidCode = code;
|
||||
}
|
||||
|
||||
void decodeIR6(uint32_t code)
|
||||
static void decodeIR6(uint32_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case IR6_POWER: toggleOnOff(); break;
|
||||
@ -587,7 +510,7 @@ void decodeIR6(uint32_t code)
|
||||
lastValidCode = code;
|
||||
}
|
||||
|
||||
void decodeIR9(uint32_t code)
|
||||
static void decodeIR9(uint32_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case IR9_POWER : toggleOnOff(); break;
|
||||
@ -628,7 +551,7 @@ Sample:
|
||||
"label": "Preset 1, fallback to Saw - Party if not found"},
|
||||
}
|
||||
*/
|
||||
void decodeIRJson(uint32_t code)
|
||||
static void decodeIRJson(uint32_t code)
|
||||
{
|
||||
char objKey[10];
|
||||
char fileName[16];
|
||||
@ -701,41 +624,102 @@ void decodeIRJson(uint32_t code)
|
||||
releaseJSONBufferLock();
|
||||
}
|
||||
|
||||
static void applyRepeatActions()
|
||||
{
|
||||
if (irEnabled == 8) {
|
||||
decodeIRJson(lastValidCode);
|
||||
return;
|
||||
} else switch (lastRepeatableAction) {
|
||||
case ACTION_BRIGHT_UP : incBrightness(); stateUpdated(CALL_MODE_BUTTON); return;
|
||||
case ACTION_BRIGHT_DOWN : decBrightness(); stateUpdated(CALL_MODE_BUTTON); return;
|
||||
case ACTION_SPEED_UP : changeEffectSpeed(lastRepeatableValue); stateUpdated(CALL_MODE_BUTTON); return;
|
||||
case ACTION_SPEED_DOWN : changeEffectSpeed(lastRepeatableValue); stateUpdated(CALL_MODE_BUTTON); return;
|
||||
case ACTION_INTENSITY_UP : changeEffectIntensity(lastRepeatableValue); stateUpdated(CALL_MODE_BUTTON); return;
|
||||
case ACTION_INTENSITY_DOWN : changeEffectIntensity(lastRepeatableValue); stateUpdated(CALL_MODE_BUTTON); return;
|
||||
default: break;
|
||||
}
|
||||
if (lastValidCode == IR40_WPLUS) {
|
||||
changeWhite(10);
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
} else if (lastValidCode == IR40_WMINUS) {
|
||||
changeWhite(-10);
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
} else if ((lastValidCode == IR24_ON || lastValidCode == IR40_ON) && irTimesRepeated > 7 ) {
|
||||
nightlightActive = true;
|
||||
nightlightStartTime = millis();
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
}
|
||||
}
|
||||
|
||||
static void decodeIR(uint32_t code)
|
||||
{
|
||||
if (code == 0xFFFFFFFF) {
|
||||
//repeated code, continue brightness up/down
|
||||
irTimesRepeated++;
|
||||
applyRepeatActions();
|
||||
return;
|
||||
}
|
||||
lastValidCode = 0; irTimesRepeated = 0;
|
||||
lastRepeatableAction = ACTION_NONE;
|
||||
|
||||
if (irEnabled == 8) { // any remote configurable with ir.json file
|
||||
decodeIRJson(code);
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
return;
|
||||
}
|
||||
if (code > 0xFFFFFF) return; //invalid code
|
||||
|
||||
switch (irEnabled) {
|
||||
case 1:
|
||||
if (code > 0xF80000) decodeIR24OLD(code); // white 24-key remote (old) - it sends 0xFF0000 values
|
||||
else decodeIR24(code); // 24-key remote - 0xF70000 to 0xF80000
|
||||
break;
|
||||
case 2: decodeIR24CT(code); break; // white 24-key remote with CW, WW, CT+ and CT- keys
|
||||
case 3: decodeIR40(code); break; // blue 40-key remote with 25%, 50%, 75% and 100% keys
|
||||
case 4: decodeIR44(code); break; // white 44-key remote with color-up/down keys and DIY1 to 6 keys
|
||||
case 5: decodeIR21(code); break; // white 21-key remote
|
||||
case 6: decodeIR6(code); break; // black 6-key learning remote defaults: "CH" controls brightness,
|
||||
// "VOL +" controls effect, "VOL -" controls colour/palette, "MUTE"
|
||||
// sets bright plain white
|
||||
case 7: decodeIR9(code); break;
|
||||
//case 8: return; // ir.json file, handled above switch statement
|
||||
}
|
||||
|
||||
if (nightlightActive && bri == 0) nightlightActive = false;
|
||||
stateUpdated(CALL_MODE_BUTTON); //for notifier, IR is considered a button input
|
||||
}
|
||||
|
||||
void initIR()
|
||||
{
|
||||
if (irEnabled > 0)
|
||||
{
|
||||
if (irEnabled > 0) {
|
||||
irrecv = new IRrecv(irPin);
|
||||
irrecv->enableIRIn();
|
||||
if (irrecv) irrecv->enableIRIn();
|
||||
} else irrecv = nullptr;
|
||||
}
|
||||
|
||||
void deInitIR()
|
||||
{
|
||||
if (irrecv) {
|
||||
irrecv->disableIRIn();
|
||||
delete irrecv;
|
||||
}
|
||||
irrecv = nullptr;
|
||||
}
|
||||
|
||||
void handleIR()
|
||||
{
|
||||
if (irEnabled > 0 && millis() - irCheckedTime > 120 && !strip.isUpdating())
|
||||
{
|
||||
irCheckedTime = millis();
|
||||
if (irEnabled > 0)
|
||||
{
|
||||
if (irrecv == NULL)
|
||||
{
|
||||
initIR(); return;
|
||||
unsigned long currentTime = millis();
|
||||
unsigned timeDiff = currentTime - irCheckedTime;
|
||||
if (timeDiff > 120 && irEnabled > 0 && irrecv) {
|
||||
if (strip.isUpdating() && timeDiff < 240) return; // be nice, but not too nice
|
||||
irCheckedTime = currentTime;
|
||||
if (irrecv->decode(&results)) {
|
||||
if (results.value != 0) { // only print results if anything is received ( != 0 )
|
||||
if (!pinManager.isPinAllocated(hardwareTX) || pinManager.getPinOwner(hardwareTX) == PinOwner::DebugOut) // Serial TX pin (GPIO 1 on ESP32 and ESP8266)
|
||||
Serial.printf_P(PSTR("IR recv: 0x%lX\n"), (unsigned long)results.value);
|
||||
}
|
||||
|
||||
if (irrecv->decode(&results))
|
||||
{
|
||||
if (results.value != 0) // only print results if anything is received ( != 0 )
|
||||
{
|
||||
if (!pinManager.isPinAllocated(hardwareTX) || pinManager.getPinOwner(hardwareTX) == PinOwner::DebugOut) // Serial TX pin (GPIO 1 on ESP32 and ESP8266)
|
||||
Serial.printf_P(PSTR("IR recv: 0x%lX\n"), (unsigned long)results.value);
|
||||
}
|
||||
decodeIR(results.value);
|
||||
irrecv->resume();
|
||||
}
|
||||
} else if (irrecv != NULL)
|
||||
{
|
||||
irrecv->disableIRIn();
|
||||
delete irrecv; irrecv = NULL;
|
||||
decodeIR(results.value);
|
||||
irrecv->resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -276,8 +276,8 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
seg.fill(BLACK);
|
||||
}
|
||||
|
||||
uint16_t start = 0, stop = 0;
|
||||
byte set = 0; //0 nothing set, 1 start set, 2 range set
|
||||
start = 0, stop = 0;
|
||||
set = 0; //0 nothing set, 1 start set, 2 range set
|
||||
|
||||
for (size_t i = 0; i < iarr.size(); i++) {
|
||||
if(iarr[i].is<JsonInteger>()) {
|
||||
@ -416,7 +416,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
//bool didSet = false;
|
||||
for (size_t s = 0; s < strip.getSegmentsNum(); s++) {
|
||||
Segment &sg = strip.getSegment(s);
|
||||
if (sg.isSelected()) {
|
||||
if (sg.isActive() && sg.isSelected()) {
|
||||
deserializeSegment(segVar, s, presetId);
|
||||
//didSet = true;
|
||||
}
|
||||
@ -487,6 +487,8 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
}
|
||||
}
|
||||
|
||||
doAdvancePlaylist = root[F("np")] | doAdvancePlaylist; //advances to next preset in playlist when true
|
||||
|
||||
JsonObject wifi = root[F("wifi")];
|
||||
if (!wifi.isNull()) {
|
||||
bool apMode = getBoolVal(wifi[F("ap")], apActive);
|
||||
@ -596,7 +598,7 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
|
||||
nl["dur"] = nightlightDelayMins;
|
||||
nl["mode"] = nightlightMode;
|
||||
nl[F("tbri")] = nightlightTargetBri;
|
||||
nl[F("rem")] = nightlightActive ? (nightlightDelayMs - (millis() - nightlightStartTime)) / 1000 : -1; // seconds remaining
|
||||
nl[F("rem")] = nightlightActive ? (int)(nightlightDelayMs - (millis() - nightlightStartTime)) / 1000 : -1; // seconds remaining
|
||||
|
||||
JsonObject udpn = root.createNestedObject("udpn");
|
||||
udpn[F("send")] = sendNotificationsRT;
|
||||
@ -636,6 +638,7 @@ void serializeInfo(JsonObject root)
|
||||
root[F("ver")] = versionString;
|
||||
root[F("vid")] = VERSION;
|
||||
root[F("cn")] = F(WLED_CODENAME);
|
||||
root[F("release")] = FPSTR(releaseString);
|
||||
|
||||
JsonObject leds = root.createNestedObject(F("leds"));
|
||||
leds[F("count")] = strip.getLengthTotal();
|
||||
|
391
wled00/mqtt.cpp
@ -1,197 +1,194 @@
|
||||
#include "wled.h"
|
||||
|
||||
/*
|
||||
* MQTT communication protocol for home automation
|
||||
*/
|
||||
|
||||
#ifdef WLED_ENABLE_MQTT
|
||||
#define MQTT_KEEP_ALIVE_TIME 60 // contact the MQTT broker every 60 seconds
|
||||
|
||||
void parseMQTTBriPayload(char* payload)
|
||||
{
|
||||
if (strstr(payload, "ON") || strstr(payload, "on") || strstr(payload, "true")) {bri = briLast; stateUpdated(CALL_MODE_DIRECT_CHANGE);}
|
||||
else if (strstr(payload, "T" ) || strstr(payload, "t" )) {toggleOnOff(); stateUpdated(CALL_MODE_DIRECT_CHANGE);}
|
||||
else {
|
||||
uint8_t in = strtoul(payload, NULL, 10);
|
||||
if (in == 0 && bri > 0) briLast = bri;
|
||||
bri = in;
|
||||
stateUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void onMqttConnect(bool sessionPresent)
|
||||
{
|
||||
//(re)subscribe to required topics
|
||||
char subuf[38];
|
||||
|
||||
if (mqttDeviceTopic[0] != 0) {
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
mqtt->subscribe(subuf, 0);
|
||||
strcat_P(subuf, PSTR("/col"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strcat_P(subuf, PSTR("/api"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
}
|
||||
|
||||
if (mqttGroupTopic[0] != 0) {
|
||||
strlcpy(subuf, mqttGroupTopic, 33);
|
||||
mqtt->subscribe(subuf, 0);
|
||||
strcat_P(subuf, PSTR("/col"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
strlcpy(subuf, mqttGroupTopic, 33);
|
||||
strcat_P(subuf, PSTR("/api"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
}
|
||||
|
||||
usermods.onMqttConnect(sessionPresent);
|
||||
|
||||
DEBUG_PRINTLN(F("MQTT ready"));
|
||||
publishMqtt();
|
||||
}
|
||||
|
||||
|
||||
void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
|
||||
static char *payloadStr;
|
||||
|
||||
DEBUG_PRINT(F("MQTT msg: "));
|
||||
DEBUG_PRINTLN(topic);
|
||||
|
||||
// paranoia check to avoid npe if no payload
|
||||
if (payload==nullptr) {
|
||||
DEBUG_PRINTLN(F("no payload -> leave"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (index == 0) { // start (1st partial packet or the only packet)
|
||||
if (payloadStr) delete[] payloadStr; // fail-safe: release buffer
|
||||
payloadStr = new char[total+1]; // allocate new buffer
|
||||
}
|
||||
if (payloadStr == nullptr) return; // buffer not allocated
|
||||
|
||||
// copy (partial) packet to buffer and 0-terminate it if it is last packet
|
||||
char* buff = payloadStr + index;
|
||||
memcpy(buff, payload, len);
|
||||
if (index + len >= total) { // at end
|
||||
payloadStr[total] = '\0'; // terminate c style string
|
||||
} else {
|
||||
DEBUG_PRINTLN(F("MQTT partial packet received."));
|
||||
return; // process next packet
|
||||
}
|
||||
DEBUG_PRINTLN(payloadStr);
|
||||
|
||||
size_t topicPrefixLen = strlen(mqttDeviceTopic);
|
||||
if (strncmp(topic, mqttDeviceTopic, topicPrefixLen) == 0) {
|
||||
topic += topicPrefixLen;
|
||||
} else {
|
||||
topicPrefixLen = strlen(mqttGroupTopic);
|
||||
if (strncmp(topic, mqttGroupTopic, topicPrefixLen) == 0) {
|
||||
topic += topicPrefixLen;
|
||||
} else {
|
||||
// Non-Wled Topic used here. Probably a usermod subscribed to this topic.
|
||||
usermods.onMqttMessage(topic, payloadStr);
|
||||
delete[] payloadStr;
|
||||
payloadStr = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//Prefix is stripped from the topic at this point
|
||||
|
||||
if (strcmp_P(topic, PSTR("/col")) == 0) {
|
||||
colorFromDecOrHexString(col, payloadStr);
|
||||
colorUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
} else if (strcmp_P(topic, PSTR("/api")) == 0) {
|
||||
if (!requestJSONBufferLock(15)) {
|
||||
delete[] payloadStr;
|
||||
payloadStr = nullptr;
|
||||
return;
|
||||
}
|
||||
if (payloadStr[0] == '{') { //JSON API
|
||||
deserializeJson(*pDoc, payloadStr);
|
||||
deserializeState(pDoc->as<JsonObject>());
|
||||
} else { //HTTP API
|
||||
String apireq = "win"; apireq += '&'; // reduce flash string usage
|
||||
apireq += payloadStr;
|
||||
handleSet(nullptr, apireq);
|
||||
}
|
||||
releaseJSONBufferLock();
|
||||
} else if (strlen(topic) != 0) {
|
||||
// non standard topic, check with usermods
|
||||
usermods.onMqttMessage(topic, payloadStr);
|
||||
} else {
|
||||
// topmost topic (just wled/MAC)
|
||||
parseMQTTBriPayload(payloadStr);
|
||||
}
|
||||
delete[] payloadStr;
|
||||
payloadStr = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void publishMqtt()
|
||||
{
|
||||
if (!WLED_MQTT_CONNECTED) return;
|
||||
DEBUG_PRINTLN(F("Publish MQTT"));
|
||||
|
||||
#ifndef USERMOD_SMARTNEST
|
||||
char s[10];
|
||||
char subuf[48];
|
||||
|
||||
sprintf_P(s, PSTR("%u"), bri);
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strcat_P(subuf, PSTR("/g"));
|
||||
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263)
|
||||
|
||||
sprintf_P(s, PSTR("#%06X"), (col[3] << 24) | (col[0] << 16) | (col[1] << 8) | (col[2]));
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strcat_P(subuf, PSTR("/c"));
|
||||
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263)
|
||||
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strcat_P(subuf, PSTR("/status"));
|
||||
mqtt->publish(subuf, 0, true, "online"); // retain message for a LWT
|
||||
|
||||
char apires[1024]; // allocating 1024 bytes from stack can be risky
|
||||
XML_response(nullptr, apires);
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strcat_P(subuf, PSTR("/v"));
|
||||
mqtt->publish(subuf, 0, retainMqttMsg, apires); // optionally retain message (#2263)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//HA autodiscovery was removed in favor of the native integration in HA v0.102.0
|
||||
|
||||
bool initMqtt()
|
||||
{
|
||||
if (!mqttEnabled || mqttServer[0] == 0 || !WLED_CONNECTED) return false;
|
||||
|
||||
if (mqtt == nullptr) {
|
||||
mqtt = new AsyncMqttClient();
|
||||
mqtt->onMessage(onMqttMessage);
|
||||
mqtt->onConnect(onMqttConnect);
|
||||
}
|
||||
if (mqtt->connected()) return true;
|
||||
|
||||
DEBUG_PRINTLN(F("Reconnecting MQTT"));
|
||||
IPAddress mqttIP;
|
||||
if (mqttIP.fromString(mqttServer)) //see if server is IP or domain
|
||||
{
|
||||
mqtt->setServer(mqttIP, mqttPort);
|
||||
} else {
|
||||
mqtt->setServer(mqttServer, mqttPort);
|
||||
}
|
||||
mqtt->setClientId(mqttClientID);
|
||||
if (mqttUser[0] && mqttPass[0]) mqtt->setCredentials(mqttUser, mqttPass);
|
||||
|
||||
#ifndef USERMOD_SMARTNEST
|
||||
strlcpy(mqttStatusTopic, mqttDeviceTopic, 33);
|
||||
strcat_P(mqttStatusTopic, PSTR("/status"));
|
||||
mqtt->setWill(mqttStatusTopic, 0, true, "offline"); // LWT message
|
||||
#endif
|
||||
mqtt->setKeepAlive(MQTT_KEEP_ALIVE_TIME);
|
||||
mqtt->connect();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#include "wled.h"
|
||||
|
||||
/*
|
||||
* MQTT communication protocol for home automation
|
||||
*/
|
||||
|
||||
#ifdef WLED_ENABLE_MQTT
|
||||
#define MQTT_KEEP_ALIVE_TIME 60 // contact the MQTT broker every 60 seconds
|
||||
|
||||
void parseMQTTBriPayload(char* payload)
|
||||
{
|
||||
if (strstr(payload, "ON") || strstr(payload, "on") || strstr(payload, "true")) {bri = briLast; stateUpdated(CALL_MODE_DIRECT_CHANGE);}
|
||||
else if (strstr(payload, "T" ) || strstr(payload, "t" )) {toggleOnOff(); stateUpdated(CALL_MODE_DIRECT_CHANGE);}
|
||||
else {
|
||||
uint8_t in = strtoul(payload, NULL, 10);
|
||||
if (in == 0 && bri > 0) briLast = bri;
|
||||
bri = in;
|
||||
stateUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void onMqttConnect(bool sessionPresent)
|
||||
{
|
||||
//(re)subscribe to required topics
|
||||
char subuf[38];
|
||||
|
||||
if (mqttDeviceTopic[0] != 0) {
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
mqtt->subscribe(subuf, 0);
|
||||
strcat_P(subuf, PSTR("/col"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strcat_P(subuf, PSTR("/api"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
}
|
||||
|
||||
if (mqttGroupTopic[0] != 0) {
|
||||
strlcpy(subuf, mqttGroupTopic, 33);
|
||||
mqtt->subscribe(subuf, 0);
|
||||
strcat_P(subuf, PSTR("/col"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
strlcpy(subuf, mqttGroupTopic, 33);
|
||||
strcat_P(subuf, PSTR("/api"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
}
|
||||
|
||||
usermods.onMqttConnect(sessionPresent);
|
||||
|
||||
DEBUG_PRINTLN(F("MQTT ready"));
|
||||
publishMqtt();
|
||||
}
|
||||
|
||||
|
||||
void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
|
||||
static char *payloadStr;
|
||||
|
||||
DEBUG_PRINT(F("MQTT msg: "));
|
||||
DEBUG_PRINTLN(topic);
|
||||
|
||||
// paranoia check to avoid npe if no payload
|
||||
if (payload==nullptr) {
|
||||
DEBUG_PRINTLN(F("no payload -> leave"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (index == 0) { // start (1st partial packet or the only packet)
|
||||
if (payloadStr) delete[] payloadStr; // fail-safe: release buffer
|
||||
payloadStr = new char[total+1]; // allocate new buffer
|
||||
}
|
||||
if (payloadStr == nullptr) return; // buffer not allocated
|
||||
|
||||
// copy (partial) packet to buffer and 0-terminate it if it is last packet
|
||||
char* buff = payloadStr + index;
|
||||
memcpy(buff, payload, len);
|
||||
if (index + len >= total) { // at end
|
||||
payloadStr[total] = '\0'; // terminate c style string
|
||||
} else {
|
||||
DEBUG_PRINTLN(F("MQTT partial packet received."));
|
||||
return; // process next packet
|
||||
}
|
||||
DEBUG_PRINTLN(payloadStr);
|
||||
|
||||
size_t topicPrefixLen = strlen(mqttDeviceTopic);
|
||||
if (strncmp(topic, mqttDeviceTopic, topicPrefixLen) == 0) {
|
||||
topic += topicPrefixLen;
|
||||
} else {
|
||||
topicPrefixLen = strlen(mqttGroupTopic);
|
||||
if (strncmp(topic, mqttGroupTopic, topicPrefixLen) == 0) {
|
||||
topic += topicPrefixLen;
|
||||
} else {
|
||||
// Non-Wled Topic used here. Probably a usermod subscribed to this topic.
|
||||
usermods.onMqttMessage(topic, payloadStr);
|
||||
delete[] payloadStr;
|
||||
payloadStr = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//Prefix is stripped from the topic at this point
|
||||
|
||||
if (strcmp_P(topic, PSTR("/col")) == 0) {
|
||||
colorFromDecOrHexString(col, payloadStr);
|
||||
colorUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
} else if (strcmp_P(topic, PSTR("/api")) == 0) {
|
||||
if (requestJSONBufferLock(15)) {
|
||||
if (payloadStr[0] == '{') { //JSON API
|
||||
deserializeJson(*pDoc, payloadStr);
|
||||
deserializeState(pDoc->as<JsonObject>());
|
||||
} else { //HTTP API
|
||||
String apireq = "win"; apireq += '&'; // reduce flash string usage
|
||||
apireq += payloadStr;
|
||||
handleSet(nullptr, apireq);
|
||||
}
|
||||
releaseJSONBufferLock();
|
||||
}
|
||||
} else if (strlen(topic) != 0) {
|
||||
// non standard topic, check with usermods
|
||||
usermods.onMqttMessage(topic, payloadStr);
|
||||
} else {
|
||||
// topmost topic (just wled/MAC)
|
||||
parseMQTTBriPayload(payloadStr);
|
||||
}
|
||||
delete[] payloadStr;
|
||||
payloadStr = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void publishMqtt()
|
||||
{
|
||||
if (!WLED_MQTT_CONNECTED) return;
|
||||
DEBUG_PRINTLN(F("Publish MQTT"));
|
||||
|
||||
#ifndef USERMOD_SMARTNEST
|
||||
char s[10];
|
||||
char subuf[48];
|
||||
|
||||
sprintf_P(s, PSTR("%u"), bri);
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strcat_P(subuf, PSTR("/g"));
|
||||
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263)
|
||||
|
||||
sprintf_P(s, PSTR("#%06X"), (col[3] << 24) | (col[0] << 16) | (col[1] << 8) | (col[2]));
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strcat_P(subuf, PSTR("/c"));
|
||||
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263)
|
||||
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strcat_P(subuf, PSTR("/status"));
|
||||
mqtt->publish(subuf, 0, true, "online"); // retain message for a LWT
|
||||
|
||||
char apires[1024]; // allocating 1024 bytes from stack can be risky
|
||||
XML_response(nullptr, apires);
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strcat_P(subuf, PSTR("/v"));
|
||||
mqtt->publish(subuf, 0, retainMqttMsg, apires); // optionally retain message (#2263)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//HA autodiscovery was removed in favor of the native integration in HA v0.102.0
|
||||
|
||||
bool initMqtt()
|
||||
{
|
||||
if (!mqttEnabled || mqttServer[0] == 0 || !WLED_CONNECTED) return false;
|
||||
|
||||
if (mqtt == nullptr) {
|
||||
mqtt = new AsyncMqttClient();
|
||||
mqtt->onMessage(onMqttMessage);
|
||||
mqtt->onConnect(onMqttConnect);
|
||||
}
|
||||
if (mqtt->connected()) return true;
|
||||
|
||||
DEBUG_PRINTLN(F("Reconnecting MQTT"));
|
||||
IPAddress mqttIP;
|
||||
if (mqttIP.fromString(mqttServer)) //see if server is IP or domain
|
||||
{
|
||||
mqtt->setServer(mqttIP, mqttPort);
|
||||
} else {
|
||||
mqtt->setServer(mqttServer, mqttPort);
|
||||
}
|
||||
mqtt->setClientId(mqttClientID);
|
||||
if (mqttUser[0] && mqttPass[0]) mqtt->setCredentials(mqttUser, mqttPass);
|
||||
|
||||
#ifndef USERMOD_SMARTNEST
|
||||
strlcpy(mqttStatusTopic, mqttDeviceTopic, 33);
|
||||
strcat_P(mqttStatusTopic, PSTR("/status"));
|
||||
mqtt->setWill(mqttStatusTopic, 0, true, "offline"); // LWT message
|
||||
#endif
|
||||
mqtt->setKeepAlive(MQTT_KEEP_ALIVE_TIME);
|
||||
mqtt->connect();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
@ -2,20 +2,8 @@
|
||||
#include "wled.h"
|
||||
#include "fcn_declare.h"
|
||||
|
||||
// on esp8266, building with `-D WLED_USE_UNREAL_MATH` saves around 7Kb flash and 1KB RAM
|
||||
// warning: causes errors in sunset calculations, see #3400
|
||||
#if defined(WLED_USE_UNREAL_MATH)
|
||||
#define sinf sin_t
|
||||
#define asinf asin_t
|
||||
#define cosf cos_t
|
||||
#define acosf acos_t
|
||||
#define tanf tan_t
|
||||
#define atanf atan_t
|
||||
#define fmodf fmod_t
|
||||
#define floorf floor_t
|
||||
#else
|
||||
#include <math.h>
|
||||
#endif
|
||||
// WARNING: may cause errors in sunset calculations on ESP8266, see #3400
|
||||
// building with `-D WLED_USE_REAL_MATH` will prevent those errors at the expense of flash and RAM
|
||||
|
||||
/*
|
||||
* Acquires time from NTP server
|
||||
@ -439,7 +427,7 @@ static int getSunriseUTC(int year, int month, int day, float lat, float lon, boo
|
||||
//1. first calculate the day of the year
|
||||
float N1 = 275 * month / 9;
|
||||
float N2 = (month + 9) / 12;
|
||||
float N3 = (1.0f + floorf((year - 4 * floorf(year / 4) + 2.0f) / 3.0f));
|
||||
float N3 = (1.0f + floor_t((year - 4 * floor_t(year / 4) + 2.0f) / 3.0f));
|
||||
float N = N1 - (N2 * N3) + day - 30.0f;
|
||||
|
||||
//2. convert the longitude to hour value and calculate an approximate time
|
||||
@ -450,37 +438,37 @@ static int getSunriseUTC(int year, int month, int day, float lat, float lon, boo
|
||||
float M = (0.9856f * t) - 3.289f;
|
||||
|
||||
//4. calculate the Sun's true longitude
|
||||
float L = fmodf(M + (1.916f * sinf(DEG_TO_RAD*M)) + (0.02f * sinf(2*DEG_TO_RAD*M)) + 282.634f, 360.0f);
|
||||
float L = fmod_t(M + (1.916f * sin_t(DEG_TO_RAD*M)) + (0.02f * sin_t(2*DEG_TO_RAD*M)) + 282.634f, 360.0f);
|
||||
|
||||
//5a. calculate the Sun's right ascension
|
||||
float RA = fmodf(RAD_TO_DEG*atanf(0.91764f * tanf(DEG_TO_RAD*L)), 360.0f);
|
||||
float RA = fmod_t(RAD_TO_DEG*atan_t(0.91764f * tan_t(DEG_TO_RAD*L)), 360.0f);
|
||||
|
||||
//5b. right ascension value needs to be in the same quadrant as L
|
||||
float Lquadrant = floorf( L/90) * 90;
|
||||
float RAquadrant = floorf(RA/90) * 90;
|
||||
float Lquadrant = floor_t( L/90) * 90;
|
||||
float RAquadrant = floor_t(RA/90) * 90;
|
||||
RA = RA + (Lquadrant - RAquadrant);
|
||||
|
||||
//5c. right ascension value needs to be converted into hours
|
||||
RA /= 15.0f;
|
||||
|
||||
//6. calculate the Sun's declination
|
||||
float sinDec = 0.39782f * sinf(DEG_TO_RAD*L);
|
||||
float cosDec = cosf(asinf(sinDec));
|
||||
float sinDec = 0.39782f * sin_t(DEG_TO_RAD*L);
|
||||
float cosDec = cos_t(asin_t(sinDec));
|
||||
|
||||
//7a. calculate the Sun's local hour angle
|
||||
float cosH = (sinf(DEG_TO_RAD*ZENITH) - (sinDec * sinf(DEG_TO_RAD*lat))) / (cosDec * cosf(DEG_TO_RAD*lat));
|
||||
float cosH = (sin_t(DEG_TO_RAD*ZENITH) - (sinDec * sin_t(DEG_TO_RAD*lat))) / (cosDec * cos_t(DEG_TO_RAD*lat));
|
||||
if ((cosH > 1.0f) && !sunset) return INT16_MAX; // the sun never rises on this location (on the specified date)
|
||||
if ((cosH < -1.0f) && sunset) return INT16_MAX; // the sun never sets on this location (on the specified date)
|
||||
|
||||
//7b. finish calculating H and convert into hours
|
||||
float H = sunset ? RAD_TO_DEG*acosf(cosH) : 360 - RAD_TO_DEG*acosf(cosH);
|
||||
float H = sunset ? RAD_TO_DEG*acos_t(cosH) : 360 - RAD_TO_DEG*acos_t(cosH);
|
||||
H /= 15.0f;
|
||||
|
||||
//8. calculate local mean time of rising/setting
|
||||
float T = H + RA - (0.06571f * t) - 6.622f;
|
||||
|
||||
//9. adjust back to UTC
|
||||
float UT = fmodf(T - lngHour, 24.0f);
|
||||
float UT = fmod_t(T - lngHour, 24.0f);
|
||||
|
||||
// return in minutes from midnight
|
||||
return UT*60;
|
||||
|
@ -25,11 +25,11 @@ void _overlayAnalogClock()
|
||||
{
|
||||
if (secondPixel < analogClock12pixel)
|
||||
{
|
||||
strip.setRange(analogClock12pixel, overlayMax, 0xFF0000);
|
||||
strip.setRange(overlayMin, secondPixel, 0xFF0000);
|
||||
strip.setRange(analogClock12pixel, overlayMax, color_fade(0xFF0000, bri));
|
||||
strip.setRange(overlayMin, secondPixel, color_fade(0xFF0000, bri));
|
||||
} else
|
||||
{
|
||||
strip.setRange(analogClock12pixel, secondPixel, 0xFF0000);
|
||||
strip.setRange(analogClock12pixel, secondPixel, color_fade(0xFF0000, bri));
|
||||
}
|
||||
}
|
||||
if (analogClock5MinuteMarks)
|
||||
@ -38,12 +38,12 @@ void _overlayAnalogClock()
|
||||
{
|
||||
unsigned pix = analogClock12pixel + roundf((overlaySize / 12.0f) *i);
|
||||
if (pix > overlayMax) pix -= overlaySize;
|
||||
strip.setPixelColor(pix, 0x00FFAA);
|
||||
strip.setPixelColor(pix, color_fade(0x00FFAA, bri));
|
||||
}
|
||||
}
|
||||
if (!analogClockSecondsTrail) strip.setPixelColor(secondPixel, 0xFF0000);
|
||||
strip.setPixelColor(minutePixel, 0x00FF00);
|
||||
strip.setPixelColor(hourPixel, 0x0000FF);
|
||||
if (!analogClockSecondsTrail) strip.setPixelColor(secondPixel, color_fade(0xFF0000, bri));
|
||||
strip.setPixelColor(minutePixel, color_fade(0x00FF00, bri));
|
||||
strip.setPixelColor(hourPixel, color_fade(0x0000FF, bri));
|
||||
}
|
||||
|
||||
|
||||
|
@ -54,7 +54,7 @@ enum struct PinOwner : uint8_t {
|
||||
// #define USERMOD_ID_RTC // 0x0F // Usermod "usermod_rtc.h" -- Uses "standard" HW_I2C pins
|
||||
// #define USERMOD_ID_ELEKSTUBE_IPS // 0x10 // Usermod "usermod_elekstube_ips.h" -- Uses quite a few pins ... see Hardware.h and User_Setup.h
|
||||
// #define USERMOD_ID_SN_PHOTORESISTOR // 0x11 // Usermod "usermod_sn_photoresistor.h" -- Uses hard-coded pin (PHOTORESISTOR_PIN == A0), but could be easily updated to use pinManager
|
||||
UM_BH1750 = USERMOD_ID_BH1750, // 0x14 // Usermod "usermod_bme280.h -- Uses "standard" HW_I2C pins
|
||||
UM_BH1750 = USERMOD_ID_BH1750, // 0x14 // Usermod "bh1750.h -- Uses "standard" HW_I2C pins
|
||||
UM_RGBRotaryEncoder = USERMOD_RGB_ROTARY_ENCODER, // 0x16 // Usermod "rgb-rotary-encoder.h"
|
||||
UM_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA, // 0x17 // Usermod "quinled-an-penta.h"
|
||||
UM_BME280 = USERMOD_ID_BME280, // 0x1E // Usermod "usermod_bme280.h -- Uses "standard" HW_I2C pins
|
||||
@ -62,7 +62,8 @@ enum struct PinOwner : uint8_t {
|
||||
UM_SdCard = USERMOD_ID_SD_CARD, // 0x25 // Usermod "usermod_sd_card.h"
|
||||
UM_PWM_OUTPUTS = USERMOD_ID_PWM_OUTPUTS, // 0x26 // Usermod "usermod_pwm_outputs.h"
|
||||
UM_LDR_DUSK_DAWN = USERMOD_ID_LDR_DUSK_DAWN, // 0x2B // Usermod "usermod_LDR_Dusk_Dawn_v2.h"
|
||||
UM_MAX17048 = USERMOD_ID_MAX17048 // 0x2F // Usermod "usermod_max17048.h"
|
||||
UM_MAX17048 = USERMOD_ID_MAX17048, // 0x2F // Usermod "usermod_max17048.h"
|
||||
UM_BME68X = USERMOD_ID_BME68X // 0x31 // Usermod "usermod_bme68x.h -- Uses "standard" HW_I2C pins
|
||||
};
|
||||
static_assert(0u == static_cast<uint8_t>(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected");
|
||||
|
||||
|
@ -127,7 +127,7 @@ void handlePlaylist() {
|
||||
static unsigned long presetCycledTime = 0;
|
||||
if (currentPlaylist < 0 || playlistEntries == nullptr) return;
|
||||
|
||||
if (millis() - presetCycledTime > (100*playlistEntryDur)) {
|
||||
if (millis() - presetCycledTime > (100 * playlistEntryDur) || doAdvancePlaylist) {
|
||||
presetCycledTime = millis();
|
||||
if (bri == 0 || nightlightActive) return;
|
||||
|
||||
@ -149,6 +149,7 @@ void handlePlaylist() {
|
||||
strip.setTransition(fadeTransition ? playlistEntries[playlistIndex].tr * 100 : 0);
|
||||
playlistEntryDur = playlistEntries[playlistIndex].dur;
|
||||
applyPresetFromPlaylist(playlistEntries[playlistIndex].preset);
|
||||
doAdvancePlaylist = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
}
|
||||
#ifndef WLED_DISABLE_INFRARED
|
||||
if (irPin>=0 && pinManager.isPinAllocated(irPin, PinOwner::IR)) {
|
||||
pinManager.deallocatePin(irPin, PinOwner::IR);
|
||||
deInitIR();
|
||||
pinManager.deallocatePin(irPin, PinOwner::IR);
|
||||
}
|
||||
#endif
|
||||
for (uint8_t s=0; s<WLED_MAX_BUTTONS; s++) {
|
||||
@ -135,27 +136,28 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
useGlobalLedBuffer = request->hasArg(F("LD"));
|
||||
|
||||
bool busesChanged = false;
|
||||
for (uint8_t s = 0; s < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; s++) {
|
||||
char lp[4] = "L0"; lp[2] = 48+s; lp[3] = 0; //ascii 0-9 //strip data pin
|
||||
char lc[4] = "LC"; lc[2] = 48+s; lc[3] = 0; //strip length
|
||||
char co[4] = "CO"; co[2] = 48+s; co[3] = 0; //strip color order
|
||||
char lt[4] = "LT"; lt[2] = 48+s; lt[3] = 0; //strip type
|
||||
char ls[4] = "LS"; ls[2] = 48+s; ls[3] = 0; //strip start LED
|
||||
char cv[4] = "CV"; cv[2] = 48+s; cv[3] = 0; //strip reverse
|
||||
char sl[4] = "SL"; sl[2] = 48+s; sl[3] = 0; //skip first N LEDs
|
||||
char rf[4] = "RF"; rf[2] = 48+s; rf[3] = 0; //refresh required
|
||||
char aw[4] = "AW"; aw[2] = 48+s; aw[3] = 0; //auto white mode
|
||||
char wo[4] = "WO"; wo[2] = 48+s; wo[3] = 0; //channel swap
|
||||
char sp[4] = "SP"; sp[2] = 48+s; sp[3] = 0; //bus clock speed (DotStar & PWM)
|
||||
char la[4] = "LA"; la[2] = 48+s; la[3] = 0; //LED mA
|
||||
char ma[4] = "MA"; ma[2] = 48+s; ma[3] = 0; //max mA
|
||||
for (int s = 0; s < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; s++) {
|
||||
int offset = s < 10 ? 48 : 55;
|
||||
char lp[4] = "L0"; lp[2] = offset+s; lp[3] = 0; //ascii 0-9 //strip data pin
|
||||
char lc[4] = "LC"; lc[2] = offset+s; lc[3] = 0; //strip length
|
||||
char co[4] = "CO"; co[2] = offset+s; co[3] = 0; //strip color order
|
||||
char lt[4] = "LT"; lt[2] = offset+s; lt[3] = 0; //strip type
|
||||
char ls[4] = "LS"; ls[2] = offset+s; ls[3] = 0; //strip start LED
|
||||
char cv[4] = "CV"; cv[2] = offset+s; cv[3] = 0; //strip reverse
|
||||
char sl[4] = "SL"; sl[2] = offset+s; sl[3] = 0; //skip first N LEDs
|
||||
char rf[4] = "RF"; rf[2] = offset+s; rf[3] = 0; //refresh required
|
||||
char aw[4] = "AW"; aw[2] = offset+s; aw[3] = 0; //auto white mode
|
||||
char wo[4] = "WO"; wo[2] = offset+s; wo[3] = 0; //channel swap
|
||||
char sp[4] = "SP"; sp[2] = offset+s; sp[3] = 0; //bus clock speed (DotStar & PWM)
|
||||
char la[4] = "LA"; la[2] = offset+s; la[3] = 0; //LED mA
|
||||
char ma[4] = "MA"; ma[2] = offset+s; ma[3] = 0; //max mA
|
||||
if (!request->hasArg(lp)) {
|
||||
DEBUG_PRINT(F("No data for "));
|
||||
DEBUG_PRINTLN(s);
|
||||
break;
|
||||
}
|
||||
for (uint8_t i = 0; i < 5; i++) {
|
||||
lp[1] = 48+i;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
lp[1] = offset+i;
|
||||
if (!request->hasArg(lp)) break;
|
||||
pins[i] = (request->arg(lp).length() > 0) ? request->arg(lp).toInt() : 255;
|
||||
}
|
||||
@ -209,11 +211,12 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
//doInitBusses = busesChanged; // we will do that below to ensure all input data is processed
|
||||
|
||||
ColorOrderMap com = {};
|
||||
for (uint8_t s = 0; s < WLED_MAX_COLOR_ORDER_MAPPINGS; s++) {
|
||||
char xs[4] = "XS"; xs[2] = 48+s; xs[3] = 0; //start LED
|
||||
char xc[4] = "XC"; xc[2] = 48+s; xc[3] = 0; //strip length
|
||||
char xo[4] = "XO"; xo[2] = 48+s; xo[3] = 0; //color order
|
||||
char xw[4] = "XW"; xw[2] = 48+s; xw[3] = 0; //W swap
|
||||
for (int s = 0; s < WLED_MAX_COLOR_ORDER_MAPPINGS; s++) {
|
||||
int offset = s < 10 ? 48 : 55;
|
||||
char xs[4] = "XS"; xs[2] = offset+s; xs[3] = 0; //start LED
|
||||
char xc[4] = "XC"; xc[2] = offset+s; xc[3] = 0; //strip length
|
||||
char xo[4] = "XO"; xo[2] = offset+s; xo[3] = 0; //color order
|
||||
char xw[4] = "XW"; xw[2] = offset+s; xw[3] = 0; //W swap
|
||||
if (request->hasArg(xs)) {
|
||||
start = request->arg(xs).toInt();
|
||||
length = request->arg(xc).toInt();
|
||||
@ -233,6 +236,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
irPin = -1;
|
||||
}
|
||||
irEnabled = request->arg(F("IT")).toInt();
|
||||
initIR();
|
||||
#endif
|
||||
irApplyToAllSelected = !request->hasArg(F("MSO"));
|
||||
|
||||
@ -243,12 +247,14 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
rlyPin = -1;
|
||||
}
|
||||
rlyMde = (bool)request->hasArg(F("RM"));
|
||||
rlyOpenDrain = (bool)request->hasArg(F("RO"));
|
||||
|
||||
disablePullUp = (bool)request->hasArg(F("IP"));
|
||||
touchThreshold = request->arg(F("TT")).toInt();
|
||||
for (uint8_t i=0; i<WLED_MAX_BUTTONS; i++) {
|
||||
char bt[4] = "BT"; bt[2] = (i<10?48:55)+i; bt[3] = 0; // button pin (use A,B,C,... if WLED_MAX_BUTTONS>10)
|
||||
char be[4] = "BE"; be[2] = (i<10?48:55)+i; be[3] = 0; // button type (use A,B,C,... if WLED_MAX_BUTTONS>10)
|
||||
for (int i = 0; i < WLED_MAX_BUTTONS; i++) {
|
||||
int offset = i < 10 ? 48 : 55;
|
||||
char bt[4] = "BT"; bt[2] = offset+i; bt[3] = 0; // button pin (use A,B,C,... if WLED_MAX_BUTTONS>10)
|
||||
char be[4] = "BE"; be[2] = offset+i; be[3] = 0; // button type (use A,B,C,... if WLED_MAX_BUTTONS>10)
|
||||
int hw_btn_pin = request->arg(bt).toInt();
|
||||
if (hw_btn_pin >= 0 && pinManager.allocatePin(hw_btn_pin,false,PinOwner::Button)) {
|
||||
btnPin[i] = hw_btn_pin;
|
||||
@ -900,6 +906,9 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
applyPreset(presetCycCurr);
|
||||
}
|
||||
|
||||
pos = req.indexOf(F("NP")); //advances to next preset in a playlist
|
||||
if (pos > 0) doAdvancePlaylist = true;
|
||||
|
||||
//set brightness
|
||||
updateVal(req.c_str(), "&A=", &bri);
|
||||
|
||||
|
@ -53,6 +53,11 @@
|
||||
#include "../usermods/BME280_v2/usermod_bme280.h"
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_BME68X
|
||||
#include "../usermods/BME68X_v2/usermod_bme68x.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
#include "../usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h"
|
||||
#endif
|
||||
@ -217,6 +222,14 @@
|
||||
#include "../usermods/TetrisAI_v2/usermod_v2_tetrisai.h"
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_AHT10
|
||||
#include "../usermods/AHT10_v2/usermod_aht10.h"
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_INA226
|
||||
#include "../usermods/INA226_v2/usermod_ina226.h"
|
||||
#endif
|
||||
|
||||
void registerUsermods()
|
||||
{
|
||||
/*
|
||||
@ -254,6 +267,10 @@ void registerUsermods()
|
||||
usermods.add(new UsermodBME280());
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_BME68X
|
||||
usermods.add(new UsermodBME68X());
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_SENSORSTOMQTT
|
||||
usermods.add(new UserMod_SensorsToMQTT());
|
||||
#endif
|
||||
@ -421,4 +438,12 @@ void registerUsermods()
|
||||
#ifdef USERMOD_TETRISAI
|
||||
usermods.add(new TetrisAIUsermod());
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_AHT10
|
||||
usermods.add(new UsermodAHT10());
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_INA226
|
||||
usermods.add(new UsermodINA226());
|
||||
#endif
|
||||
}
|
||||
|
@ -505,6 +505,13 @@ void WLED::setup()
|
||||
initServer();
|
||||
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
|
||||
|
||||
#ifndef WLED_DISABLE_INFRARED
|
||||
// init IR
|
||||
DEBUG_PRINTLN(F("initIR"));
|
||||
initIR();
|
||||
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
|
||||
#endif
|
||||
|
||||
// Seed FastLED random functions with an esp random value, which already works properly at this point.
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
const uint32_t seed32 = esp_random();
|
||||
|
@ -3,12 +3,12 @@
|
||||
/*
|
||||
Main sketch, global variable declarations
|
||||
@title WLED project sketch
|
||||
@version 0.15.0-b2
|
||||
@version 0.15.0-b3
|
||||
@author Christian Schwinne
|
||||
*/
|
||||
|
||||
// version code in format yymmddb (b = daily build)
|
||||
#define VERSION 2404100
|
||||
#define VERSION 2405180
|
||||
|
||||
//uncomment this if you have a "my_config.h" file you'd like to use
|
||||
//#define WLED_USE_MY_CONFIG
|
||||
@ -243,27 +243,32 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument<PSRAM_Allocator>;
|
||||
// int arr[]{0,1,2} becomes WLED_GLOBAL int arr[] _INIT_N(({0,1,2}));
|
||||
|
||||
#ifndef WLED_DEFINE_GLOBAL_VARS
|
||||
# define WLED_GLOBAL extern
|
||||
# define _INIT(x)
|
||||
# define _INIT_N(x)
|
||||
#define WLED_GLOBAL extern
|
||||
#define _INIT(x)
|
||||
#define _INIT_N(x)
|
||||
#define _INIT_PROGMEM(x)
|
||||
#else
|
||||
# define WLED_GLOBAL
|
||||
# define _INIT(x) = x
|
||||
|
||||
//needed to ignore commas in array definitions
|
||||
#define UNPACK( ... ) __VA_ARGS__
|
||||
# define _INIT_N(x) UNPACK x
|
||||
#define WLED_GLOBAL
|
||||
#define _INIT(x) = x
|
||||
//needed to ignore commas in array definitions
|
||||
#define UNPACK( ... ) __VA_ARGS__
|
||||
#define _INIT_N(x) UNPACK x
|
||||
#define _INIT_PROGMEM(x) PROGMEM = x
|
||||
#endif
|
||||
|
||||
#define STRINGIFY(X) #X
|
||||
#define TOSTRING(X) STRINGIFY(X)
|
||||
|
||||
#ifndef WLED_VERSION
|
||||
#define WLED_VERSION "dev"
|
||||
#define WLED_VERSION dev
|
||||
#endif
|
||||
#ifndef WLED_RELEASE_NAME
|
||||
#define WLED_RELEASE_NAME dev_release
|
||||
#endif
|
||||
|
||||
// Global Variable definitions
|
||||
WLED_GLOBAL char versionString[] _INIT(TOSTRING(WLED_VERSION));
|
||||
WLED_GLOBAL char releaseString[] _INIT_PROGMEM(TOSTRING(WLED_RELEASE_NAME)); // somehow this will not work if using "const char releaseString[]
|
||||
#define WLED_CODENAME "Kōsen"
|
||||
|
||||
// AP and OTA default passwords (for maximum security change them!)
|
||||
@ -288,6 +293,12 @@ WLED_GLOBAL bool rlyMde _INIT(true);
|
||||
#else
|
||||
WLED_GLOBAL bool rlyMde _INIT(RLYMDE);
|
||||
#endif
|
||||
//Use open drain (floating pin) when relay should be off
|
||||
#ifndef RLYODRAIN
|
||||
WLED_GLOBAL bool rlyOpenDrain _INIT(false);
|
||||
#else
|
||||
WLED_GLOBAL bool rlyOpenDrain _INIT(RLYODRAIN);
|
||||
#endif
|
||||
#ifndef IRPIN
|
||||
#define IRPIN -1
|
||||
#endif
|
||||
@ -634,6 +645,7 @@ WLED_GLOBAL byte timerWeekday[] _INIT_N(({ 255, 255, 255, 255, 255, 255, 255,
|
||||
WLED_GLOBAL byte timerMonth[] _INIT_N(({28,28,28,28,28,28,28,28}));
|
||||
WLED_GLOBAL byte timerDay[] _INIT_N(({1,1,1,1,1,1,1,1}));
|
||||
WLED_GLOBAL byte timerDayEnd[] _INIT_N(({31,31,31,31,31,31,31,31}));
|
||||
WLED_GLOBAL bool doAdvancePlaylist _INIT(false);
|
||||
|
||||
//improv
|
||||
WLED_GLOBAL byte improvActive _INIT(0); //0: no improv packet received, 1: improv active, 2: provisioning
|
||||
|
@ -206,9 +206,9 @@ bool sendLiveLedsWs(uint32_t wsClient)
|
||||
uint8_t g = G(c);
|
||||
uint8_t b = B(c);
|
||||
uint8_t w = W(c);
|
||||
buffer[pos++] = scale8(qadd8(w, r), strip.getBrightness()); //R, add white channel to RGB channels as a simple RGBW -> RGB map
|
||||
buffer[pos++] = scale8(qadd8(w, g), strip.getBrightness()); //G
|
||||
buffer[pos++] = scale8(qadd8(w, b), strip.getBrightness()); //B
|
||||
buffer[pos++] = bri ? qadd8(w, r) : 0; //R, add white channel to RGB channels as a simple RGBW -> RGB map
|
||||
buffer[pos++] = bri ? qadd8(w, g) : 0; //G
|
||||
buffer[pos++] = bri ? qadd8(w, b) : 0; //B
|
||||
}
|
||||
|
||||
wsc->binary(std::move(wsBuf));
|
||||
|
@ -350,7 +350,8 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
oappend(itoa(WLED_MIN_VIRTUAL_BUSSES,nS,10)); oappend(",");
|
||||
oappend(itoa(MAX_LEDS_PER_BUS,nS,10)); oappend(",");
|
||||
oappend(itoa(MAX_LED_MEMORY,nS,10)); oappend(",");
|
||||
oappend(itoa(MAX_LEDS,nS,10));
|
||||
oappend(itoa(MAX_LEDS,nS,10)); oappend(",");
|
||||
oappend(itoa(WLED_MAX_COLOR_ORDER_MAPPINGS,nS,10));
|
||||
oappend(SET_F(");"));
|
||||
|
||||
sappend('c',SET_F("MS"),autoSegments);
|
||||
@ -362,28 +363,29 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sappend('v',SET_F("AW"),Bus::getGlobalAWMode());
|
||||
sappend('c',SET_F("LD"),useGlobalLedBuffer);
|
||||
|
||||
uint16_t sumMa = 0;
|
||||
for (uint8_t s=0; s < BusManager::getNumBusses(); s++) {
|
||||
unsigned sumMa = 0;
|
||||
for (int s = 0; s < BusManager::getNumBusses(); s++) {
|
||||
Bus* bus = BusManager::getBus(s);
|
||||
if (bus == nullptr) continue;
|
||||
char lp[4] = "L0"; lp[2] = 48+s; lp[3] = 0; //ascii 0-9 //strip data pin
|
||||
char lc[4] = "LC"; lc[2] = 48+s; lc[3] = 0; //strip length
|
||||
char co[4] = "CO"; co[2] = 48+s; co[3] = 0; //strip color order
|
||||
char lt[4] = "LT"; lt[2] = 48+s; lt[3] = 0; //strip type
|
||||
char ls[4] = "LS"; ls[2] = 48+s; ls[3] = 0; //strip start LED
|
||||
char cv[4] = "CV"; cv[2] = 48+s; cv[3] = 0; //strip reverse
|
||||
char sl[4] = "SL"; sl[2] = 48+s; sl[3] = 0; //skip 1st LED
|
||||
char rf[4] = "RF"; rf[2] = 48+s; rf[3] = 0; //off refresh
|
||||
char aw[4] = "AW"; aw[2] = 48+s; aw[3] = 0; //auto white mode
|
||||
char wo[4] = "WO"; wo[2] = 48+s; wo[3] = 0; //swap channels
|
||||
char sp[4] = "SP"; sp[2] = 48+s; sp[3] = 0; //bus clock speed
|
||||
char la[4] = "LA"; la[2] = 48+s; la[3] = 0; //LED current
|
||||
char ma[4] = "MA"; ma[2] = 48+s; ma[3] = 0; //max per-port PSU current
|
||||
int offset = s < 10 ? 48 : 55;
|
||||
char lp[4] = "L0"; lp[2] = offset+s; lp[3] = 0; //ascii 0-9 //strip data pin
|
||||
char lc[4] = "LC"; lc[2] = offset+s; lc[3] = 0; //strip length
|
||||
char co[4] = "CO"; co[2] = offset+s; co[3] = 0; //strip color order
|
||||
char lt[4] = "LT"; lt[2] = offset+s; lt[3] = 0; //strip type
|
||||
char ls[4] = "LS"; ls[2] = offset+s; ls[3] = 0; //strip start LED
|
||||
char cv[4] = "CV"; cv[2] = offset+s; cv[3] = 0; //strip reverse
|
||||
char sl[4] = "SL"; sl[2] = offset+s; sl[3] = 0; //skip 1st LED
|
||||
char rf[4] = "RF"; rf[2] = offset+s; rf[3] = 0; //off refresh
|
||||
char aw[4] = "AW"; aw[2] = offset+s; aw[3] = 0; //auto white mode
|
||||
char wo[4] = "WO"; wo[2] = offset+s; wo[3] = 0; //swap channels
|
||||
char sp[4] = "SP"; sp[2] = offset+s; sp[3] = 0; //bus clock speed
|
||||
char la[4] = "LA"; la[2] = offset+s; la[3] = 0; //LED current
|
||||
char ma[4] = "MA"; ma[2] = offset+s; ma[3] = 0; //max per-port PSU current
|
||||
oappend(SET_F("addLEDs(1);"));
|
||||
uint8_t pins[5];
|
||||
uint8_t nPins = bus->getPins(pins);
|
||||
for (uint8_t i = 0; i < nPins; i++) {
|
||||
lp[1] = 48+i;
|
||||
int nPins = bus->getPins(pins);
|
||||
for (int i = 0; i < nPins; i++) {
|
||||
lp[1] = offset+i;
|
||||
if (pinManager.isPinOk(pins[i]) || IS_VIRTUAL(bus->getType())) sappend('v',lp,pins[i]);
|
||||
}
|
||||
sappend('v',lc,bus->getLength());
|
||||
@ -395,7 +397,7 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sappend('c',rf,bus->isOffRefreshRequired());
|
||||
sappend('v',aw,bus->getAutoWhiteMode());
|
||||
sappend('v',wo,bus->getColorOrder() >> 4);
|
||||
uint16_t speed = bus->getFrequency();
|
||||
unsigned speed = bus->getFrequency();
|
||||
if (IS_PWM(bus->getType())) {
|
||||
switch (speed) {
|
||||
case WLED_PWM_FREQ/2 : speed = 0; break;
|
||||
@ -428,7 +430,7 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
oappend(itoa(WLED_MAX_COLOR_ORDER_MAPPINGS,nS,10));
|
||||
oappend(SET_F(");"));
|
||||
const ColorOrderMap& com = BusManager::getColorOrderMap();
|
||||
for (uint8_t s=0; s < com.count(); s++) {
|
||||
for (int s = 0; s < com.count(); s++) {
|
||||
const ColorOrderMapEntry* entry = com.get(s);
|
||||
if (entry == nullptr) break;
|
||||
oappend(SET_F("addCOM("));
|
||||
@ -458,7 +460,8 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sappend('i',SET_F("PB"),strip.paletteBlend);
|
||||
sappend('v',SET_F("RL"),rlyPin);
|
||||
sappend('c',SET_F("RM"),rlyMde);
|
||||
for (uint8_t i=0; i<WLED_MAX_BUTTONS; i++) {
|
||||
sappend('c',SET_F("RO"),rlyOpenDrain);
|
||||
for (int i = 0; i < WLED_MAX_BUTTONS; i++) {
|
||||
oappend(SET_F("addBtn("));
|
||||
oappend(itoa(i,nS,10)); oappend(",");
|
||||
oappend(itoa(btnPin[i],nS,10)); oappend(",");
|
||||
@ -722,6 +725,8 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sappends('m',SET_F("(\"sip\")[0]"),(char*)F("WLED "));
|
||||
olen -= 2; //delete ";
|
||||
oappend(versionString);
|
||||
oappend(SET_F("<br>"));
|
||||
oappend((char*)FPSTR(releaseString));
|
||||
oappend(SET_F("<br>("));
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
oappend(ESP.getChipModel());
|
||||
|