Compare commits
97 Commits
v0.13.0-b7
...
v0.13.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
102a28aef4 | ||
|
|
cade1800f4 | ||
|
|
8176f1141e | ||
|
|
515827c745 | ||
|
|
420f858d9b | ||
|
|
902c11d074 | ||
|
|
38330b735c | ||
|
|
bda3c4ab7a | ||
|
|
d8d01ac353 | ||
|
|
51d935f419 | ||
|
|
c96f83b076 | ||
|
|
7308f5993c | ||
|
|
22ac12dc36 | ||
|
|
866296fefd | ||
|
|
9d574397bc | ||
|
|
bee48dae7e | ||
|
|
e12f7b67e5 | ||
|
|
a8908238d5 | ||
|
|
fd4c0e795a | ||
|
|
c79eb43347 | ||
|
|
860e74bffa | ||
|
|
ed374684a6 | ||
|
|
169a46c38c | ||
|
|
1dbea434a3 | ||
|
|
0dd12cf0a6 | ||
|
|
19c8b4fe2d | ||
|
|
26fa38d052 | ||
|
|
db8e1dec3e | ||
|
|
213e3e998a | ||
|
|
bef9c68f81 | ||
|
|
099d2fd03d | ||
|
|
23d39e5366 | ||
|
|
1a513c7bbf | ||
|
|
d1f76042e1 | ||
|
|
9cd8acab43 | ||
|
|
8b79a9708b | ||
|
|
e362b3b6aa | ||
|
|
d2ced93e58 | ||
|
|
958cd35e21 | ||
|
|
46eae410c3 | ||
|
|
73a9e1c316 | ||
|
|
03862d4b6c | ||
|
|
ae90aa4ccc | ||
|
|
b583def913 | ||
|
|
dd85da053f | ||
|
|
6079effae3 | ||
|
|
8d2fe315db | ||
|
|
22c3ac5be3 | ||
|
|
a517f0df1d | ||
|
|
9c9854b6bf | ||
|
|
d280e16723 | ||
|
|
b93a9cb8bc | ||
|
|
8601052179 | ||
|
|
eaa20ff4bf | ||
|
|
e4c6e4bc48 | ||
|
|
c52597205e | ||
|
|
c73033c0b4 | ||
|
|
522e752582 | ||
|
|
854ed8cfa9 | ||
|
|
4642205768 | ||
|
|
40dbfbe092 | ||
|
|
6c315e5a9c | ||
|
|
ef0f91d8d0 | ||
|
|
9552784e72 | ||
|
|
f068327307 | ||
|
|
1bc698ae78 | ||
|
|
1b2134d7a8 | ||
|
|
f922268af7 | ||
|
|
4865ddb377 | ||
|
|
a556732e4f | ||
|
|
0ea31cb088 | ||
|
|
b626c7620e | ||
|
|
5d90d8930e | ||
|
|
b01309c3bf | ||
|
|
961d5591bd | ||
|
|
eca3f12fed | ||
|
|
a2c8796e04 | ||
|
|
ad301fd087 | ||
|
|
02b08939cd | ||
|
|
9b0d583f1b | ||
|
|
4a0a07f158 | ||
|
|
9c864c9759 | ||
|
|
85b1c309d1 | ||
|
|
6fe43b7b5c | ||
|
|
25427ee60d | ||
|
|
be90bf0188 | ||
|
|
adcdaba199 | ||
|
|
17907589cc | ||
|
|
f333df181f | ||
|
|
4ce557a829 | ||
|
|
fc845dc936 | ||
|
|
7beae93441 | ||
|
|
afaa001738 | ||
|
|
71520f6709 | ||
|
|
3f5a09229d | ||
|
|
5609771993 | ||
|
|
79b01cdc3c |
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -45,7 +45,7 @@ body:
|
||||
attributes:
|
||||
label: What version of WLED?
|
||||
description: You can find this in by going to Config -> Security & Updates -> Scroll to Bottom. Copy and paste the entire line after "Server message"
|
||||
placeholder: "e.g. WLED 0.13.0-b7 (build 2202222)"
|
||||
placeholder: "e.g. WLED 0.13.1 (build 2203150)"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
|
||||
83
CHANGELOG.md
83
CHANGELOG.md
@@ -1,6 +1,87 @@
|
||||
## WLED changelog
|
||||
|
||||
### Builds after release 0.12.0
|
||||
### WLED release 0.13.3
|
||||
|
||||
- Version bump to v0.13.3 "Toki"
|
||||
- Disable ESP watchdog by default (fixes flickering and boot issues on a fresh install)
|
||||
- Added support for LPD6803
|
||||
|
||||
### WLED release 0.13.2
|
||||
|
||||
#### Build 2208140
|
||||
|
||||
- Version bump to v0.13.2 "Toki"
|
||||
- Added option to receive live data on the main segment only (PR #2601)
|
||||
- Enable ESP watchdog by default (PR #2657)
|
||||
- Fixed race condition when saving bus config
|
||||
- Better potentiometer filtering (PR #2693)
|
||||
- More suitable DMX libraries (PR #2652)
|
||||
- Fixed outgoing serial TPM2 message length (PR #2628)
|
||||
- Fixed next universe overflow and Art-Net DMX start address (PR #2607)
|
||||
- Fixed relative segment brightness (PR #2665)
|
||||
|
||||
### Builds between releases 0.13.1 and 0.13.2
|
||||
|
||||
#### Build 2203191
|
||||
|
||||
- Fixed sunrise/set calculation (once again)
|
||||
|
||||
#### Build 2203190
|
||||
|
||||
- Fixed `/json/cfg` unable to set busses (#2589)
|
||||
- Fixed Peek with odd LED counts > 255 (#2586)
|
||||
|
||||
#### Build 2203160
|
||||
|
||||
- Version bump to v0.13.2-a0 "Toki"
|
||||
- Add ability to skip up to 255 LEDs
|
||||
- Dependency version bumps
|
||||
|
||||
### WLED release 0.13.1
|
||||
|
||||
#### Build 2203150
|
||||
|
||||
- Version bump to v0.13.1 "Toki"
|
||||
- Fix persistent preset bug, preventing save of new presets
|
||||
|
||||
### WLED release 0.13.0
|
||||
|
||||
#### Build 2203142
|
||||
|
||||
- Release of WLED v0.13.0 "Toki"
|
||||
- Reduce APA102 hardware SPI frequency to 5Mhz
|
||||
- Remove `persistent` parameter in `savePreset()`
|
||||
|
||||
### Builds between releases 0.12.0 and 0.13.0
|
||||
|
||||
#### Build 2203140
|
||||
|
||||
- Added factory reset by pressing button 0 for >10 seconds
|
||||
- Added ability to set presets from DMX Effect mode
|
||||
- Simplified label hiding JS in user interface
|
||||
- Fixed JSON `{"live":true}` indefinite realtime mode
|
||||
|
||||
#### Build 2203080
|
||||
|
||||
- Disabled auto white mode in segments with no RGB bus
|
||||
- Fixed hostname string not 0-terminated
|
||||
- Fixed Popcorn mode not lighting first LED on pop
|
||||
|
||||
#### Build 2203060
|
||||
|
||||
- Dynamic hiding of unused color controls in UI (PR #2567)
|
||||
- Removed native Cronixie support and added Cronixie usermod
|
||||
- Fixed disabled timed preset expanding calendar
|
||||
- Fixed Color Order setting shown for analog busses
|
||||
- Fixed incorrect operator (#2566)
|
||||
|
||||
#### Build 2203011
|
||||
|
||||
- IR rewrite (PR #2561), supports CCT
|
||||
- Added locate button to Time settings
|
||||
- CSS fixes and adjustments
|
||||
- Consistent Tab indentation in index JS and CSS
|
||||
- Added initial contribution style guideline
|
||||
|
||||
#### Build 2202222
|
||||
|
||||
|
||||
78
CONTRIBUTING.md
Normal file
78
CONTRIBUTING.md
Normal file
@@ -0,0 +1,78 @@
|
||||
## Thank you for making WLED better!
|
||||
|
||||
Here are a few suggestions to make it easier for you to contribute!
|
||||
|
||||
### Code style
|
||||
|
||||
When in doubt, it is easiest to replicate the code style you find in the files you want to edit :)
|
||||
Below are the guidelines we use in the WLED repository.
|
||||
|
||||
#### Indentation
|
||||
|
||||
We use tabs for Indentation in Web files (.html/.css/.js) and spaces (2 per indentation level) for all other files.
|
||||
You are all set if you have enabled `Editor: Detect Indentation` in VS Code.
|
||||
|
||||
#### Blocks
|
||||
|
||||
Whether the opening bracket of e.g. an `if` block is in the same line as the condition or in a separate line is up to your discretion. If there is only one statement, leaving out block braches is acceptable.
|
||||
|
||||
Good:
|
||||
```cpp
|
||||
if (a == b) {
|
||||
doStuff(a);
|
||||
}
|
||||
```
|
||||
|
||||
```cpp
|
||||
if (a == b)
|
||||
{
|
||||
doStuff(a);
|
||||
}
|
||||
```
|
||||
|
||||
```cpp
|
||||
if (a == b) doStuff(a);
|
||||
```
|
||||
|
||||
There should always be a space between a keyword and its condition and between the condition and brace.
|
||||
Within the condition, no space should be between the paranthesis and variables.
|
||||
Spaces between variables and operators are up to the authors discretion.
|
||||
There should be no space between function names and their argument parenthesis.
|
||||
|
||||
Good:
|
||||
```cpp
|
||||
if (a == b) {
|
||||
doStuff(a);
|
||||
}
|
||||
```
|
||||
|
||||
Not good:
|
||||
```cpp
|
||||
if( a==b ){
|
||||
doStuff ( a);
|
||||
}
|
||||
```
|
||||
|
||||
#### Comments
|
||||
|
||||
Comments should have a space between the delimiting characters (e.g. `//`) and the comment text.
|
||||
Note: This is a recent change, the majority of the codebase still has comments without spaces.
|
||||
|
||||
Good:
|
||||
```
|
||||
// This is a comment.
|
||||
|
||||
/* This is a CSS inline comment */
|
||||
|
||||
/*
|
||||
* This is a comment
|
||||
* wrapping over multiple lines,
|
||||
* used in WLED for file headers and function explanations
|
||||
*/
|
||||
|
||||
<!-- This is an HTML comment -->
|
||||
```
|
||||
|
||||
There is no set character limit for a comment within a line,
|
||||
though as a rule of thumb you should wrap your comment if it exceeds the width of your editor window.
|
||||
Inline comments are OK if they describe that line only and are not exceedingly wide.
|
||||
14
package-lock.json
generated
14
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wled",
|
||||
"version": "0.13.0-b7",
|
||||
"version": "0.13.3",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -1339,9 +1339,9 @@
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.5",
|
||||
@@ -2067,9 +2067,9 @@
|
||||
"integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw=="
|
||||
},
|
||||
"terser": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
|
||||
"integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz",
|
||||
"integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==",
|
||||
"requires": {
|
||||
"commander": "^2.20.0",
|
||||
"source-map": "~0.6.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wled",
|
||||
"version": "0.13.0-b7",
|
||||
"version": "0.13.3",
|
||||
"description": "Tools for WLED project",
|
||||
"main": "tools/cdata.js",
|
||||
"directories": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
; default_envs = travis_esp8266, travis_esp32
|
||||
|
||||
# Release binaries
|
||||
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, esp32_eth_ota1mapp, esp32s2_saola, esp32c3
|
||||
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, esp32s2_saola, esp32c3
|
||||
|
||||
# Build everything
|
||||
; default_envs = esp32dev, esp8285_4CH_MagicHome, codm-controller-0.6-rev2, codm-controller-0.6, esp32s2_saola, d1_mini_5CH_Shojo_PCB, d1_mini, sp501e, travis_esp8266, travis_esp32, nodemcuv2, esp32_eth, anavi_miracle_controller, esp07, esp01_1m_full, m5atom, h803wf, d1_mini_ota, heltec_wifi_kit_8, esp8285_H801, d1_mini_debug, wemos_shield_esp32, elekstube_ips
|
||||
@@ -35,6 +35,7 @@ default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, esp32_
|
||||
; default_envs = wemos_shield_esp32
|
||||
; default_envs = m5atom
|
||||
; default_envs = esp32_eth
|
||||
; default_envs = esp32dev_qio80
|
||||
; default_envs = esp32_eth_ota1mapp
|
||||
; default_envs = esp32s2_saola
|
||||
|
||||
@@ -161,7 +162,7 @@ upload_speed = 115200
|
||||
lib_compat_mode = strict
|
||||
lib_deps =
|
||||
fastled/FastLED @ 3.5.0
|
||||
IRremoteESP8266 @ 2.8.1
|
||||
IRremoteESP8266 @ 2.8.2
|
||||
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.4
|
||||
#For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line
|
||||
#TFT_eSPI
|
||||
@@ -199,14 +200,16 @@ build_flags =
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
#https://github.com/lorol/LITTLEFS.git
|
||||
# ESPAsyncTCP @ 1.2.0
|
||||
ESPAsyncTCP @ 1.2.2
|
||||
ESPAsyncUDP
|
||||
makuna/NeoPixelBus @ 2.6.7 # 2.6.5/2.6.6 and newer do not compile on ESP core < 3.0.0
|
||||
makuna/NeoPixelBus @ 2.6.9
|
||||
|
||||
[esp32]
|
||||
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2/platform-tasmota-espressif32-2.0.2.zip
|
||||
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip
|
||||
platform = espressif32@3.5.0
|
||||
|
||||
platform_packages = framework-arduinoespressif32 @ https://github.com/Aircoookie/arduino-esp32.git#1.0.6.4
|
||||
|
||||
build_flags = -g
|
||||
-DARDUINO_ARCH_ESP32
|
||||
#-DCONFIG_LITTLEFS_FOR_IDF_3_2
|
||||
@@ -219,7 +222,7 @@ default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
https://github.com/lorol/LITTLEFS.git
|
||||
makuna/NeoPixelBus @ 2.6.7
|
||||
makuna/NeoPixelBus @ 2.6.9
|
||||
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
|
||||
|
||||
[esp32s2]
|
||||
@@ -232,7 +235,7 @@ build_flags = -g
|
||||
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
makuna/NeoPixelBus @ 2.6.7
|
||||
makuna/NeoPixelBus @ 2.6.9
|
||||
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
|
||||
|
||||
[esp32c3]
|
||||
@@ -245,7 +248,7 @@ build_flags = -g
|
||||
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
makuna/NeoPixelBus @ 2.6.7
|
||||
makuna/NeoPixelBus @ 2.6.9
|
||||
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
@@ -320,26 +323,35 @@ lib_deps = ${esp8266.lib_deps}
|
||||
[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_BLYNK #-D WLED_DISABLE_BROWNOUT_DET
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 #-D WLED_DISABLE_BLYNK #-D WLED_DISABLE_BROWNOUT_DET
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
[env:esp32dev_qio80]
|
||||
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_BLYNK #-D WLED_DISABLE_BROWNOUT_DET
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
|
||||
[env:esp32_eth]
|
||||
board = esp32-poe
|
||||
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} -D WLED_RELEASE_NAME=ESP32_Ethernet -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1 -D WLED_DISABLE_BLYNK
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
# ESP32 ETH build that fits in old 1M app space (disables Blynk, Cronixie, and Hue sync)
|
||||
[env:esp32_eth_ota1mapp]
|
||||
extends = env:esp32_eth
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet_OTA -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1 -D WLED_DISABLE_BLYNK -D WLED_DISABLE_CRONIXIE -D WLED_DISABLE_HUESYNC
|
||||
|
||||
[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
|
||||
@@ -482,6 +494,15 @@ board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_USE_IC_CCT -D BTNPIN=-1 -D IRPIN=-1 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:MY9291]
|
||||
board = esp01_1m
|
||||
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
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# travis test board configurations
|
||||
# ------------------------------------------------------------------------------
|
||||
@@ -534,7 +555,7 @@ build_flags = ${common.build_flags_esp32} -D WLED_DISABLE_BROWNOUT_DET -D WLED_D
|
||||
-D LEDPIN=12
|
||||
-D RLYPIN=27
|
||||
-D BTNPIN=34
|
||||
-D WLED_DISABLE_INFRARED
|
||||
-D WLED_DISABLE_BLYNK
|
||||
-D DEFAULT_LED_COUNT=6
|
||||
# Display config
|
||||
-D ST7789_DRIVER
|
||||
|
||||
@@ -19,10 +19,14 @@ build_flags = ${common.build_flags_esp8266}
|
||||
; *** Use custom settings from file my_config.h
|
||||
-DWLED_USE_MY_CONFIG
|
||||
; *********************************************************************
|
||||
;
|
||||
;
|
||||
; *** To use the below defines/overrides, copy and paste each onto it's own line just below build_flags in the section above.
|
||||
;
|
||||
; disable specific features
|
||||
; -D WLED_DISABLE_OTA
|
||||
; -D WLED_DISABLE_ALEXA
|
||||
; -D WLED_DISABLE_BLYNK
|
||||
; -D WLED_DISABLE_CRONIXIE
|
||||
; -D WLED_DISABLE_HUESYNC
|
||||
; -D WLED_DISABLE_INFRARED
|
||||
; -D WLED_DISABLE_WEBSOCKETS
|
||||
@@ -45,3 +49,18 @@ build_flags = ${common.build_flags_esp8266}
|
||||
; for the Magic Home LED Controller use PWM pins 5,12,13,15
|
||||
; for the H801 controller use PINs 15,13,12,14 (W2 = 04)
|
||||
; for the BW-LT11 controller use PINs 12,4,14,5
|
||||
;
|
||||
; set the name of the module - make sure there is a quote-backslash-quote before the name and a backslash-quote-quote after the name
|
||||
; -D SERVERNAME="\"WLED\""
|
||||
;
|
||||
; set the number of LEDs
|
||||
; -D DEFAULT_LED_COUNT=30
|
||||
;
|
||||
; set milliampere limit when using ESP pin to power leds
|
||||
; -D ABL_MILLIAMPS_DEFAULT=850
|
||||
;
|
||||
; enable IR by setting remote type
|
||||
; -D IRTYPE=0 ;0 Remote disabled | 1 24-key RGB | 2 24-key with CT | 3 40-key blue | 4 40-key RGB | 5 21-key RGB | 6 6-key black | 7 9-key red | 8 JSON remote
|
||||
;
|
||||
; set default color order of your led strip
|
||||
; -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB
|
||||
|
||||
@@ -27,7 +27,7 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control
|
||||
- Presets can be used to automatically execute API calls
|
||||
- Nightlight function (gradually dims down)
|
||||
- Full OTA software updatability (HTTP + ArduinoOTA), password protectable
|
||||
- Configurable analog clock + support for the Cronixie kit by Diamex
|
||||
- Configurable analog clock (Cronixie, 7-segment and EleksTube IPS clock support via usermods)
|
||||
- Configurable Auto Brightness limit for safer operation
|
||||
- Filesystem-based config for easier backup of presets and settings
|
||||
|
||||
|
||||
@@ -1,54 +1,70 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile
|
||||
# This file is autogenerated by pip-compile with python 3.8
|
||||
# To update, run:
|
||||
#
|
||||
# pip-compile
|
||||
#
|
||||
aiofiles==0.6.0
|
||||
aiofiles==0.8.0
|
||||
# via platformio
|
||||
ajsonrpc==1.1.0
|
||||
ajsonrpc==1.2.0
|
||||
# via platformio
|
||||
bottle==0.12.19
|
||||
anyio==3.6.1
|
||||
# via starlette
|
||||
async-timeout==4.0.2
|
||||
# via zeroconf
|
||||
bottle==0.12.23
|
||||
# via platformio
|
||||
certifi==2020.12.5
|
||||
certifi==2022.6.15
|
||||
# via requests
|
||||
chardet==4.0.0
|
||||
charset-normalizer==2.1.1
|
||||
# via requests
|
||||
click==7.1.2
|
||||
click==8.1.3
|
||||
# via
|
||||
# platformio
|
||||
# uvicorn
|
||||
colorama==0.4.4
|
||||
# via platformio
|
||||
h11==0.12.0
|
||||
colorama==0.4.5
|
||||
# via
|
||||
# click
|
||||
# platformio
|
||||
h11==0.13.0
|
||||
# via
|
||||
# uvicorn
|
||||
# wsproto
|
||||
idna==2.10
|
||||
# via requests
|
||||
ifaddr==0.1.7
|
||||
idna==3.3
|
||||
# via
|
||||
# anyio
|
||||
# requests
|
||||
ifaddr==0.2.0
|
||||
# via zeroconf
|
||||
marshmallow==3.11.1
|
||||
marshmallow==3.17.0
|
||||
# via platformio
|
||||
platformio==5.1.1
|
||||
packaging==21.3
|
||||
# via marshmallow
|
||||
platformio==6.1.4
|
||||
# via -r requirements.in
|
||||
pyelftools==0.27
|
||||
pyelftools==0.29
|
||||
# via platformio
|
||||
pyparsing==3.0.9
|
||||
# via packaging
|
||||
pyserial==3.5
|
||||
# via platformio
|
||||
requests==2.25.1
|
||||
requests==2.28.1
|
||||
# via platformio
|
||||
semantic-version==2.8.5
|
||||
semantic-version==2.10.0
|
||||
# via platformio
|
||||
starlette==0.14.2
|
||||
sniffio==1.2.0
|
||||
# via anyio
|
||||
starlette==0.20.4
|
||||
# via platformio
|
||||
tabulate==0.8.9
|
||||
tabulate==0.8.10
|
||||
# via platformio
|
||||
urllib3==1.26.5
|
||||
typing-extensions==4.3.0
|
||||
# via starlette
|
||||
urllib3==1.26.11
|
||||
# via requests
|
||||
uvicorn==0.13.4
|
||||
uvicorn==0.18.2
|
||||
# via platformio
|
||||
wsproto==1.0.0
|
||||
wsproto==1.1.0
|
||||
# via platformio
|
||||
zeroconf==0.28.8
|
||||
zeroconf==0.39.0
|
||||
# via platformio
|
||||
|
||||
@@ -1,40 +1,90 @@
|
||||
Hello! I have written a v2 usermod for the BME280/BMP280 sensor based on the [existing v1 usermod](https://github.com/Aircoookie/WLED/blob/master/usermods/Wemos_D1_mini%2BWemos32_mini_shield/usermod_bme280.cpp). It is not just a refactor, there are many changes which I made to fit my use case, and I hope they will fit the use cases of others as well! Most notably, this usermod is *just* for the BME280 and does not control a display like in the v1 usermod designed for the WeMos shield.
|
||||
# Usermod BME280
|
||||
This Usermod is designed to read a `BME280` or `BMP280` sensor and output the following:
|
||||
- Temperature
|
||||
- Humidity (`BME280` only)
|
||||
- Pressure
|
||||
- Heat Index (`BME280` only)
|
||||
- Dew Point (`BME280` only)
|
||||
|
||||
- Requires libraries `BME280@~3.0.0` (by [finitespace](https://github.com/finitespace/BME280)) and `Wire`. Please add these under `lib_deps` in your `platform.ini` (or `platform_override.ini`).
|
||||
- Data is published over MQTT so make sure you've enabled the MQTT sync interface.
|
||||
Configuration is all completed via the Usermod menu. There are no settings to set in code! The following settings can be configured in the Usermod Menu:
|
||||
- Temperature Decimals (number of decimal places to output)
|
||||
- Humidity Decimals
|
||||
- Pressure Decimals
|
||||
- Temperature Interval (how many seconds between reads of temperature and humidity)
|
||||
- Pressure Interval
|
||||
- Publish Always (turn off to only publish changes, on to publish whether or not value changed)
|
||||
- Use Celsius (turn off to use Farenheit)
|
||||
- Home Assistant Discovery (turn on to sent MQTT Discovery entries for Home Assistant)
|
||||
- SCL/SDA GPIO Pins
|
||||
|
||||
Dependencies
|
||||
- Libraries
|
||||
- `BME280@~3.0.0` (by [finitespace](https://github.com/finitespace/BME280))
|
||||
- `Wire`
|
||||
- These must be added under `lib_deps` in your `platform.ini` (or `platform_override.ini`).
|
||||
- Data is published over MQTT - make sure you've enabled the MQTT sync interface.
|
||||
- This usermod also writes to serial (GPIO1 on ESP8266). Please make sure nothing else listening on the serial TX pin of your board will get confused by log messages!
|
||||
|
||||
To enable, compile with `USERMOD_BME280` defined (i.e. `platformio_override.ini`)
|
||||
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.
|
||||
- `getTemperatureC()`
|
||||
- `getTemperatureF()`
|
||||
- `getHumidity()`
|
||||
- `getPressure()`
|
||||
- `getDewPointC()`
|
||||
- `getDewPointF()`
|
||||
- `getHeatIndexC()`
|
||||
- `getHeatIndexF()`
|
||||
|
||||
# Complilation
|
||||
|
||||
To enable, compile with `USERMOD_BME280` defined (e.g. in `platformio_override.ini`)
|
||||
```ini
|
||||
[env:usermod_bme280_d1_mini]
|
||||
extends = env:d1_mini
|
||||
build_flags =
|
||||
${common.build_flags_esp8266}
|
||||
-D USERMOD_BME280
|
||||
```
|
||||
or define `USERMOD_BME280` in `my_config.h`
|
||||
```c++
|
||||
#define USERMOD_BME280
|
||||
lib_deps =
|
||||
${esp8266.lib_deps}
|
||||
BME280@~3.0.0
|
||||
Wire
|
||||
```
|
||||
|
||||
Changes include:
|
||||
- Adjustable measure intervals
|
||||
- Temperature and pressure have separate intervals due to pressure not frequently changing at any constant altitude
|
||||
- Adjustment of number of decimal places in published sensor values
|
||||
- Separate adjustment for temperature, humidity and pressure values
|
||||
- Values are rounded to the specified number of decimal places
|
||||
- Pressure measured in units of hPa instead of Pa
|
||||
- Calculation of heat index (apparent temperature) and dew point
|
||||
- These, along with humidity measurements, are disabled if the sensor is a BMP280
|
||||
- 16x oversampling of sensor during measurement
|
||||
- Values are only published if they are different from the previous value
|
||||
- Values are published on startup (continually until the MQTT broker acknowledges a successful publication)
|
||||
|
||||
Adjustments are made through preprocessor definitions at the start of the class definition.
|
||||
|
||||
MQTT topics are as follows:
|
||||
# MQTT
|
||||
MQTT topics are as follows (`<deviceTopic>` is set in MQTT section of Sync Setup menu):
|
||||
Measurement type | MQTT topic
|
||||
--- | ---
|
||||
Temperature | `<deviceTopic>/temperature`
|
||||
Humidity | `<deviceTopic>/humidity`
|
||||
Pressure | `<deviceTopic>/pressure`
|
||||
Heat index | `<deviceTopic>/heat_index`
|
||||
Dew point | `<deviceTopic>/dew_point`
|
||||
Dew point | `<deviceTopic>/dew_point`
|
||||
|
||||
If you are using Home Assistant, and `Home Assistant Discovery` is turned on, Home Assistant should automatically detect a new device, provided you have the MQTT integration installed. The device is seperate from the main WLED device and will contain sensors for Pressure, Humidity, Temperature, Dew Point and Heat Index.
|
||||
|
||||
# Revision History
|
||||
Jul 2022
|
||||
- Added Home Assistant Discovery
|
||||
- Added API interface to output data
|
||||
- Removed compile-time variables
|
||||
- Added usermod menu interface
|
||||
- Added value outputs to info screen
|
||||
- Updated `readme.md`
|
||||
- Registered usermod
|
||||
- Implemented PinManager for usermod
|
||||
- Implemented reallocation of pins without reboot
|
||||
|
||||
Apr 2021
|
||||
- Added `Publish Always` option
|
||||
|
||||
Dec 2020
|
||||
- Ported to V2 Usermod format
|
||||
- Customisable `measure intervals`
|
||||
- Customisable number of `decimal places` in published sensor values
|
||||
- Pressure measured in units of hPa instead of Pa
|
||||
- Calculation of heat index (apparent temperature) and dew point
|
||||
- `16x oversampling` of sensor during measurement
|
||||
- Values only published if they are different from the previous value
|
||||
@@ -1,3 +1,6 @@
|
||||
// force the compiler to show a warning to confirm that this file is included
|
||||
#warning **** Included USERMOD_BME280 version 2.0 ****
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
@@ -9,43 +12,30 @@
|
||||
class UsermodBME280 : public Usermod
|
||||
{
|
||||
private:
|
||||
// User-defined configuration
|
||||
#define Celsius // Show temperature mesaurement in Celcius. Comment out for Fahrenheit
|
||||
#define TemperatureDecimals 1 // Number of decimal places in published temperaure values
|
||||
#define HumidityDecimals 2 // Number of decimal places in published humidity values
|
||||
#define PressureDecimals 2 // Number of decimal places in published pressure values
|
||||
#define TemperatureInterval 5 // Interval to measure temperature (and humidity, dew point if available) in seconds
|
||||
#define PressureInterval 300 // Interval to measure pressure in seconds
|
||||
#define PublishAlways 0 // Publish values even when they have not changed
|
||||
|
||||
// NOTE: Do not implement any compile-time variables, anything the user needs to configure
|
||||
// should be configurable from the Usermod menu using the methods below
|
||||
// key settings set via usermod menu
|
||||
unsigned long TemperatureDecimals = 0; // Number of decimal places in published temperaure values
|
||||
unsigned long HumidityDecimals = 0; // Number of decimal places in published humidity values
|
||||
unsigned long PressureDecimals = 0; // Number of decimal places in published pressure values
|
||||
unsigned long TemperatureInterval = 5; // Interval to measure temperature (and humidity, dew point if available) in seconds
|
||||
unsigned long PressureInterval = 300; // Interval to measure pressure in seconds
|
||||
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
|
||||
|
||||
// Sanity checks
|
||||
#if !defined(TemperatureDecimals) || TemperatureDecimals < 0
|
||||
#define TemperatureDecimals 0
|
||||
#endif
|
||||
#if !defined(HumidityDecimals) || HumidityDecimals < 0
|
||||
#define HumidityDecimals 0
|
||||
#endif
|
||||
#if !defined(PressureDecimals) || PressureDecimals < 0
|
||||
#define PressureDecimals 0
|
||||
#endif
|
||||
#if !defined(TemperatureInterval) || TemperatureInterval < 0
|
||||
#define TemperatureInterval 1
|
||||
#endif
|
||||
#if !defined(PressureInterval) || PressureInterval < 0
|
||||
#define PressureInterval TemperatureInterval
|
||||
#endif
|
||||
#if !defined(PublishAlways)
|
||||
#define PublishAlways 0
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32 // ESP32 boards
|
||||
uint8_t SCL_PIN = 22;
|
||||
uint8_t SDA_PIN = 21;
|
||||
#else // ESP8266 boards
|
||||
uint8_t SCL_PIN = 5;
|
||||
uint8_t SDA_PIN = 4;
|
||||
//uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8
|
||||
#endif
|
||||
// set the default pins based on the architecture, these get overridden by Usermod menu settings
|
||||
#ifdef ARDUINO_ARCH_ESP32 // ESP32 boards
|
||||
#define HW_PIN_SCL 22
|
||||
#define HW_PIN_SDA 21
|
||||
#else // ESP8266 boards
|
||||
#define HW_PIN_SCL 5
|
||||
#define HW_PIN_SDA 4
|
||||
//uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8
|
||||
#endif
|
||||
int8_t ioPin[2] = {HW_PIN_SCL, HW_PIN_SDA}; // I2C pins: SCL, SDA...defaults to Arch hardware pins but overridden at setup()
|
||||
bool initDone = false;
|
||||
|
||||
// BME280 sensor settings
|
||||
BME280I2C::Settings settings{
|
||||
@@ -75,6 +65,7 @@ private:
|
||||
float sensorHeatIndex;
|
||||
float sensorDewPoint;
|
||||
float sensorPressure;
|
||||
String tempScale;
|
||||
// Track previous sensor values
|
||||
float lastTemperature;
|
||||
float lastHumidity;
|
||||
@@ -82,43 +73,122 @@ private:
|
||||
float lastDewPoint;
|
||||
float lastPressure;
|
||||
|
||||
// MQTT topic strings for publishing Home Assistant discovery topics
|
||||
bool mqttInitialized = false;
|
||||
String mqttTemperatureTopic = "";
|
||||
String mqttHumidityTopic = "";
|
||||
String mqttPressureTopic = "";
|
||||
String mqttHeatIndexTopic = "";
|
||||
String mqttDewPointTopic = "";
|
||||
|
||||
// Store packet IDs of MQTT publications
|
||||
uint16_t mqttTemperaturePub = 0;
|
||||
uint16_t mqttPressurePub = 0;
|
||||
|
||||
// Read the BME280/BMP280 Sensor (which one runs depends on whether Celsius or Farenheit being set in Usermod Menu)
|
||||
void UpdateBME280Data(int SensorType)
|
||||
{
|
||||
float _temperature, _humidity, _pressure;
|
||||
#ifdef Celsius
|
||||
|
||||
if (UseCelsius) {
|
||||
BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
|
||||
EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Celsius);
|
||||
#else
|
||||
BME280::PresUnit presUnit(BME280::PresUnit_hPa);
|
||||
|
||||
bme.read(_pressure, _temperature, _humidity, tempUnit, presUnit);
|
||||
|
||||
sensorTemperature = _temperature;
|
||||
sensorHumidity = _humidity;
|
||||
sensorPressure = _pressure;
|
||||
tempScale = "°C";
|
||||
if (sensorType == 1)
|
||||
{
|
||||
sensorHeatIndex = EnvironmentCalculations::HeatIndex(_temperature, _humidity, envTempUnit);
|
||||
sensorDewPoint = EnvironmentCalculations::DewPoint(_temperature, _humidity, envTempUnit);
|
||||
}
|
||||
} else {
|
||||
BME280::TempUnit tempUnit(BME280::TempUnit_Fahrenheit);
|
||||
EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Fahrenheit);
|
||||
#endif
|
||||
BME280::PresUnit presUnit(BME280::PresUnit_hPa);
|
||||
BME280::PresUnit presUnit(BME280::PresUnit_hPa);
|
||||
|
||||
bme.read(_pressure, _temperature, _humidity, tempUnit, presUnit);
|
||||
bme.read(_pressure, _temperature, _humidity, tempUnit, presUnit);
|
||||
|
||||
sensorTemperature = _temperature;
|
||||
sensorHumidity = _humidity;
|
||||
sensorPressure = _pressure;
|
||||
if (sensorType == 1)
|
||||
{
|
||||
sensorHeatIndex = EnvironmentCalculations::HeatIndex(_temperature, _humidity, envTempUnit);
|
||||
sensorDewPoint = EnvironmentCalculations::DewPoint(_temperature, _humidity, envTempUnit);
|
||||
sensorTemperature = _temperature;
|
||||
sensorHumidity = _humidity;
|
||||
sensorPressure = _pressure;
|
||||
tempScale = "°F";
|
||||
if (sensorType == 1)
|
||||
{
|
||||
sensorHeatIndex = EnvironmentCalculations::HeatIndex(_temperature, _humidity, envTempUnit);
|
||||
sensorDewPoint = EnvironmentCalculations::DewPoint(_temperature, _humidity, envTempUnit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Procedure to define all MQTT discovery Topics
|
||||
void _mqttInitialize()
|
||||
{
|
||||
mqttTemperatureTopic = String(mqttDeviceTopic) + F("/temperature");
|
||||
mqttPressureTopic = String(mqttDeviceTopic) + F("/pressure");
|
||||
mqttHumidityTopic = String(mqttDeviceTopic) + F("/humidity");
|
||||
mqttHeatIndexTopic = String(mqttDeviceTopic) + F("/heat_index");
|
||||
mqttDewPointTopic = String(mqttDeviceTopic) + F("/dew_point");
|
||||
|
||||
if (HomeAssistantDiscovery) {
|
||||
_createMqttSensor(F("Temperature"), mqttTemperatureTopic, F("temperature"), tempScale);
|
||||
_createMqttSensor(F("Pressure"), mqttPressureTopic, F("pressure"), F("hPa"));
|
||||
_createMqttSensor(F("Humidity"), mqttHumidityTopic, F("humidity"), F("%"));
|
||||
_createMqttSensor(F("HeatIndex"), mqttHeatIndexTopic, F("temperature"), tempScale);
|
||||
_createMqttSensor(F("DewPoint"), mqttDewPointTopic, F("temperature"), tempScale);
|
||||
}
|
||||
}
|
||||
|
||||
// Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop.
|
||||
void _createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement)
|
||||
{
|
||||
String t = String(F("homeassistant/sensor/")) + mqttClientID + F("/") + name + F("/config");
|
||||
|
||||
StaticJsonDocument<600> doc;
|
||||
|
||||
doc[F("name")] = String(serverDescription) + " " + 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");
|
||||
device[F("model")] = F("FOSS");
|
||||
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());
|
||||
}
|
||||
|
||||
public:
|
||||
void setup()
|
||||
{
|
||||
Wire.begin(SDA_PIN, SCL_PIN);
|
||||
bool HW_Pins_Used = (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA); // note whether architecture-based hardware SCL/SDA pins used
|
||||
PinOwner po = PinOwner::UM_BME280; // defaults to being pinowner for SCL/SDA pins
|
||||
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } }; // allocate pins
|
||||
if (HW_Pins_Used) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
|
||||
if (!pinManager.allocateMultiplePins(pins, 2, po)) { sensorType=0; return; }
|
||||
|
||||
Wire.begin(ioPin[1], ioPin[0]);
|
||||
|
||||
if (!bme.begin())
|
||||
{
|
||||
sensorType = 0;
|
||||
Serial.println("Could not find BME280I2C sensor!");
|
||||
DEBUG_PRINTLN(F("Could not find BME280I2C sensor!"));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -126,24 +196,25 @@ public:
|
||||
{
|
||||
case BME280::ChipModel_BME280:
|
||||
sensorType = 1;
|
||||
Serial.println("Found BME280 sensor! Success.");
|
||||
DEBUG_PRINTLN(F("Found BME280 sensor! Success."));
|
||||
break;
|
||||
case BME280::ChipModel_BMP280:
|
||||
sensorType = 2;
|
||||
Serial.println("Found BMP280 sensor! No Humidity available.");
|
||||
DEBUG_PRINTLN(F("Found BMP280 sensor! No Humidity available."));
|
||||
break;
|
||||
default:
|
||||
sensorType = 0;
|
||||
Serial.println("Found UNKNOWN sensor! Error!");
|
||||
DEBUG_PRINTLN(F("Found UNKNOWN sensor! Error!"));
|
||||
}
|
||||
}
|
||||
initDone=true;
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// BME280 sensor MQTT publishing
|
||||
// Check if sensor present and MQTT Connected, otherwise it will crash the MCU
|
||||
if (sensorType != 0 && mqtt != nullptr)
|
||||
if (sensorType != 0 && WLED_MQTT_CONNECTED)
|
||||
{
|
||||
// Timer to fetch new temperature, humidity and pressure data at intervals
|
||||
timer = millis();
|
||||
@@ -154,9 +225,15 @@ public:
|
||||
|
||||
UpdateBME280Data(sensorType);
|
||||
|
||||
float temperature = roundf(sensorTemperature * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals);
|
||||
float temperature = roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
|
||||
float humidity, heatIndex, dewPoint;
|
||||
|
||||
if (WLED_MQTT_CONNECTED && !mqttInitialized)
|
||||
{
|
||||
_mqttInitialize();
|
||||
mqttInitialized = true;
|
||||
}
|
||||
|
||||
// If temperature has changed since last measure, create string populated with device topic
|
||||
// from the UI and values read from sensor, then publish to broker
|
||||
if (temperature != lastTemperature || PublishAlways)
|
||||
@@ -169,25 +246,25 @@ public:
|
||||
|
||||
if (sensorType == 1) // Only if sensor is a BME280
|
||||
{
|
||||
humidity = roundf(sensorHumidity * pow(10, HumidityDecimals)) / pow(10, HumidityDecimals);
|
||||
heatIndex = roundf(sensorHeatIndex * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals);
|
||||
dewPoint = roundf(sensorDewPoint * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals);
|
||||
humidity = roundf(sensorHumidity * powf(10, HumidityDecimals)) / powf(10, HumidityDecimals);
|
||||
heatIndex = roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
|
||||
dewPoint = roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
|
||||
|
||||
if (humidity != lastHumidity || PublishAlways)
|
||||
{
|
||||
String topic = String(mqttDeviceTopic) + "/humidity";
|
||||
String topic = String(mqttDeviceTopic) + F("/humidity");
|
||||
mqtt->publish(topic.c_str(), 0, false, String(humidity, HumidityDecimals).c_str());
|
||||
}
|
||||
|
||||
if (heatIndex != lastHeatIndex || PublishAlways)
|
||||
{
|
||||
String topic = String(mqttDeviceTopic) + "/heat_index";
|
||||
String topic = String(mqttDeviceTopic) + F("/heat_index");
|
||||
mqtt->publish(topic.c_str(), 0, false, String(heatIndex, TemperatureDecimals).c_str());
|
||||
}
|
||||
|
||||
if (dewPoint != lastDewPoint || PublishAlways)
|
||||
{
|
||||
String topic = String(mqttDeviceTopic) + "/dew_point";
|
||||
String topic = String(mqttDeviceTopic) + F("/dew_point");
|
||||
mqtt->publish(topic.c_str(), 0, false, String(dewPoint, TemperatureDecimals).c_str());
|
||||
}
|
||||
|
||||
@@ -201,11 +278,11 @@ public:
|
||||
{
|
||||
lastPressureMeasure = timer;
|
||||
|
||||
float pressure = roundf(sensorPressure * pow(10, PressureDecimals)) / pow(10, PressureDecimals);
|
||||
float pressure = roundf(sensorPressure * powf(10, PressureDecimals)) / powf(10, PressureDecimals);
|
||||
|
||||
if (pressure != lastPressure || PublishAlways)
|
||||
{
|
||||
String topic = String(mqttDeviceTopic) + "/pressure";
|
||||
String topic = String(mqttDeviceTopic) + F("/pressure");
|
||||
mqttPressurePub = mqtt->publish(topic.c_str(), 0, true, String(pressure, PressureDecimals).c_str());
|
||||
}
|
||||
|
||||
@@ -213,4 +290,173 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* API calls te enable data exchange between WLED modules
|
||||
*/
|
||||
inline float getTemperatureC() {
|
||||
if (UseCelsius) {
|
||||
return (float)roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
|
||||
} else {
|
||||
return (float)roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) * 1.8f + 32;
|
||||
}
|
||||
|
||||
}
|
||||
inline float getTemperatureF() {
|
||||
if (UseCelsius) {
|
||||
return ((float)roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) -32) * 0.56f;
|
||||
} else {
|
||||
return (float)roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
|
||||
}
|
||||
}
|
||||
inline float getHumidity() {
|
||||
return (float)roundf(sensorHumidity * powf(10, HumidityDecimals));
|
||||
}
|
||||
inline float getPressure() {
|
||||
return (float)roundf(sensorPressure * powf(10, PressureDecimals));
|
||||
}
|
||||
inline float getDewPointC() {
|
||||
if (UseCelsius) {
|
||||
return (float)roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
|
||||
} else {
|
||||
return (float)roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) * 1.8f + 32;
|
||||
}
|
||||
}
|
||||
inline float getDewPointF() {
|
||||
if (UseCelsius) {
|
||||
return ((float)roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) -32) * 0.56f;
|
||||
} else {
|
||||
return (float)roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
|
||||
}
|
||||
}
|
||||
inline float getHeatIndexC() {
|
||||
if (UseCelsius) {
|
||||
return (float)roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
|
||||
} else {
|
||||
return (float)roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) * 1.8f + 32;
|
||||
}
|
||||
}inline float getHeatIndexF() {
|
||||
if (UseCelsius) {
|
||||
return ((float)roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) -32) * 0.56f;
|
||||
} else {
|
||||
return (float)roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
|
||||
}
|
||||
}
|
||||
|
||||
// Publish Sensor Information to Info Page
|
||||
void addToJsonInfo(JsonObject &root)
|
||||
{
|
||||
JsonObject user = root[F("u")];
|
||||
if (user.isNull()) user = root.createNestedObject(F("u"));
|
||||
|
||||
if (sensorType==0) //No Sensor
|
||||
{
|
||||
// if we sensor not detected, let the user know
|
||||
JsonArray temperature_json = user.createNestedArray(F("BME/BMP280 Sensor"));
|
||||
temperature_json.add(F("Not Found"));
|
||||
}
|
||||
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(tempScale);
|
||||
pressure_json.add(roundf(sensorPressure * powf(10, PressureDecimals)));
|
||||
pressure_json.add(F("hPa"));
|
||||
}
|
||||
else if (sensorType==1) //BME280
|
||||
{
|
||||
JsonArray temperature_json = user.createNestedArray(F("Temperature"));
|
||||
JsonArray humidity_json = user.createNestedArray(F("Humidity"));
|
||||
JsonArray pressure_json = user.createNestedArray(F("Pressure"));
|
||||
JsonArray heatindex_json = user.createNestedArray(F("Heat Index"));
|
||||
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(F("%"));
|
||||
pressure_json.add(roundf(sensorPressure * powf(10, PressureDecimals)));
|
||||
pressure_json.add(F("hPa"));
|
||||
heatindex_json.add(roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
|
||||
heatindex_json.add(tempScale);
|
||||
dewpoint_json.add(roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
|
||||
dewpoint_json.add(tempScale);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Save Usermod Config Settings
|
||||
void addToConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject(F("BME280/BMP280"));
|
||||
top[F("TemperatureDecimals")] = TemperatureDecimals;
|
||||
top[F("HumidityDecimals")] = HumidityDecimals;
|
||||
top[F("PressureDecimals")] = PressureDecimals;
|
||||
top[F("TemperatureInterval")] = TemperatureInterval;
|
||||
top[F("PressureInterval")] = PressureInterval;
|
||||
top[F("PublishAlways")] = PublishAlways;
|
||||
top[F("UseCelsius")] = UseCelsius;
|
||||
top[F("HomeAssistantDiscovery")] = HomeAssistantDiscovery;
|
||||
JsonArray io_pin = top.createNestedArray(F("pin"));
|
||||
for (byte i=0; i<2; i++) io_pin.add(ioPin[i]);
|
||||
top[F("help4Pins")] = F("SCL,SDA"); // help for Settings page
|
||||
DEBUG_PRINTLN(F("BME280 config saved."));
|
||||
}
|
||||
|
||||
// Read Usermod Config Settings
|
||||
bool readFromConfig(JsonObject& root)
|
||||
{
|
||||
// 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)
|
||||
|
||||
|
||||
int8_t newPin[2]; for (byte i=0; i<2; i++) newPin[i] = ioPin[i]; // prepare to note changed pins
|
||||
|
||||
JsonObject top = root[F("BME280/BMP280")];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(F("BME280/BMP280"));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
bool configComplete = !top.isNull();
|
||||
|
||||
// A 3-argument getJsonValue() assigns the 3rd argument as a default value if the Json value is missing
|
||||
configComplete &= getJsonValue(top[F("TemperatureDecimals")], TemperatureDecimals, 1);
|
||||
configComplete &= getJsonValue(top[F("HumidityDecimals")], HumidityDecimals, 0);
|
||||
configComplete &= getJsonValue(top[F("PressureDecimals")], PressureDecimals, 0);
|
||||
configComplete &= getJsonValue(top[F("TemperatureInterval")], TemperatureInterval, 30);
|
||||
configComplete &= getJsonValue(top[F("PressureInterval")], PressureInterval, 30);
|
||||
configComplete &= getJsonValue(top[F("PublishAlways")], PublishAlways, false);
|
||||
configComplete &= getJsonValue(top[F("UseCelsius")], UseCelsius, true);
|
||||
configComplete &= getJsonValue(top[F("HomeAssistantDiscovery")], HomeAssistantDiscovery, false);
|
||||
for (byte i=0; i<2; i++) configComplete &= getJsonValue(top[F("pin")][i], newPin[i], ioPin[i]);
|
||||
|
||||
DEBUG_PRINT(FPSTR(F("BME280/BMP280")));
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
for (byte i=0; i<2; i++) ioPin[i] = newPin[i];
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
// changing parameters from settings page
|
||||
bool pinsChanged = false;
|
||||
for (byte i=0; i<2; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; } // check if any pins changed
|
||||
if (pinsChanged) { //if pins changed, deallocate old pins and allocate new ones
|
||||
PinOwner po = PinOwner::UM_BME280;
|
||||
if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
|
||||
pinManager.deallocateMultiplePins((const uint8_t *)ioPin, 2, po); // deallocate pins
|
||||
for (byte i=0; i<2; i++) ioPin[i] = newPin[i];
|
||||
setup();
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[F("pin")].isNull();
|
||||
}
|
||||
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
uint16_t getId() {
|
||||
return USERMOD_ID_BME280;
|
||||
}
|
||||
};
|
||||
8
usermods/Cronixie/readme.md
Normal file
8
usermods/Cronixie/readme.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Cronixie clock usermod
|
||||
|
||||
This usermod supports driving the Cronixie M and L clock kits by Diamex.
|
||||
|
||||
## Installation
|
||||
|
||||
Compile and upload after adding `-D USERMOD_CRONIXIE` to `build_flags` of your PlatformIO environment.
|
||||
Make sure the Auto Brightness Limiter is enabled at 420mA (!) and configure 60 WS281x LEDs.
|
||||
301
usermods/Cronixie/usermod_cronixie.h
Normal file
301
usermods/Cronixie/usermod_cronixie.h
Normal file
@@ -0,0 +1,301 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
class UsermodCronixie : public Usermod {
|
||||
private:
|
||||
unsigned long lastTime = 0;
|
||||
char cronixieDisplay[7] = "HHMMSS";
|
||||
byte _digitOut[6] = {10,10,10,10,10,10};
|
||||
byte dP[6] = {255, 255, 255, 255, 255, 255};
|
||||
|
||||
// set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer)
|
||||
bool backlight = true;
|
||||
|
||||
public:
|
||||
void initCronixie()
|
||||
{
|
||||
if (dP[0] == 255) // if dP[0] is 255, cronixie is not yet init'ed
|
||||
{
|
||||
setCronixie();
|
||||
strip.getSegment(0).grouping = 10; // 10 LEDs per digit
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (!toki.isTick()) return;
|
||||
initCronixie();
|
||||
_overlayCronixie();
|
||||
strip.trigger();
|
||||
}
|
||||
|
||||
byte getSameCodeLength(char code, int index, char const cronixieDisplay[])
|
||||
{
|
||||
byte counter = 0;
|
||||
|
||||
for (int i = index+1; i < 6; i++)
|
||||
{
|
||||
if (cronixieDisplay[i] == code)
|
||||
{
|
||||
counter++;
|
||||
} else {
|
||||
return counter;
|
||||
}
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
|
||||
void setCronixie()
|
||||
{
|
||||
/*
|
||||
* digit purpose index
|
||||
* 0-9 | 0-9 (incl. random)
|
||||
* 10 | blank
|
||||
* 11 | blank, bg off
|
||||
* 12 | test upw.
|
||||
* 13 | test dnw.
|
||||
* 14 | binary AM/PM
|
||||
* 15 | BB upper +50 for no trailing 0
|
||||
* 16 | BBB
|
||||
* 17 | BBBB
|
||||
* 18 | BBBBB
|
||||
* 19 | BBBBBB
|
||||
* 20 | H
|
||||
* 21 | HH
|
||||
* 22 | HHH
|
||||
* 23 | HHHH
|
||||
* 24 | M
|
||||
* 25 | MM
|
||||
* 26 | MMM
|
||||
* 27 | MMMM
|
||||
* 28 | MMMMM
|
||||
* 29 | MMMMMM
|
||||
* 30 | S
|
||||
* 31 | SS
|
||||
* 32 | SSS
|
||||
* 33 | SSSS
|
||||
* 34 | SSSSS
|
||||
* 35 | SSSSSS
|
||||
* 36 | Y
|
||||
* 37 | YY
|
||||
* 38 | YYYY
|
||||
* 39 | I
|
||||
* 40 | II
|
||||
* 41 | W
|
||||
* 42 | WW
|
||||
* 43 | D
|
||||
* 44 | DD
|
||||
* 45 | DDD
|
||||
* 46 | V
|
||||
* 47 | VV
|
||||
* 48 | VVV
|
||||
* 49 | VVVV
|
||||
* 50 | VVVVV
|
||||
* 51 | VVVVVV
|
||||
* 52 | v
|
||||
* 53 | vv
|
||||
* 54 | vvv
|
||||
* 55 | vvvv
|
||||
* 56 | vvvvv
|
||||
* 57 | vvvvvv
|
||||
*/
|
||||
|
||||
//H HourLower | HH - Hour 24. | AH - Hour 12. | HHH Hour of Month | HHHH Hour of Year
|
||||
//M MinuteUpper | MM Minute of Hour | MMM Minute of 12h | MMMM Minute of Day | MMMMM Minute of Month | MMMMMM Minute of Year
|
||||
//S SecondUpper | SS Second of Minute | SSS Second of 10 Minute | SSSS Second of Hour | SSSSS Second of Day | SSSSSS Second of Week
|
||||
//B AM/PM | BB 0-6/6-12/12-18/18-24 | BBB 0-3... | BBBB 0-1.5... | BBBBB 0-1 | BBBBBB 0-0.5
|
||||
|
||||
//Y YearLower | YY - Year LU | YYYY - Std.
|
||||
//I MonthLower | II - Month of Year
|
||||
//W Week of Month | WW Week of Year
|
||||
//D Day of Week | DD Day Of Month | DDD Day Of Year
|
||||
|
||||
DEBUG_PRINT("cset ");
|
||||
DEBUG_PRINTLN(cronixieDisplay);
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
dP[i] = 10;
|
||||
switch (cronixieDisplay[i])
|
||||
{
|
||||
case '_': dP[i] = 10; break;
|
||||
case '-': dP[i] = 11; break;
|
||||
case 'r': dP[i] = random(1,7); break; //random btw. 1-6
|
||||
case 'R': dP[i] = random(0,10); break; //random btw. 0-9
|
||||
//case 't': break; //Test upw.
|
||||
//case 'T': break; //Test dnw.
|
||||
case 'b': dP[i] = 14 + getSameCodeLength('b',i,cronixieDisplay); i = i+dP[i]-14; break;
|
||||
case 'B': dP[i] = 14 + getSameCodeLength('B',i,cronixieDisplay); i = i+dP[i]-14; break;
|
||||
case 'h': dP[i] = 70 + getSameCodeLength('h',i,cronixieDisplay); i = i+dP[i]-70; break;
|
||||
case 'H': dP[i] = 20 + getSameCodeLength('H',i,cronixieDisplay); i = i+dP[i]-20; break;
|
||||
case 'A': dP[i] = 108; i++; break;
|
||||
case 'a': dP[i] = 58; i++; break;
|
||||
case 'm': dP[i] = 74 + getSameCodeLength('m',i,cronixieDisplay); i = i+dP[i]-74; break;
|
||||
case 'M': dP[i] = 24 + getSameCodeLength('M',i,cronixieDisplay); i = i+dP[i]-24; break;
|
||||
case 's': dP[i] = 80 + getSameCodeLength('s',i,cronixieDisplay); i = i+dP[i]-80; break; //refresh more often bc. of secs
|
||||
case 'S': dP[i] = 30 + getSameCodeLength('S',i,cronixieDisplay); i = i+dP[i]-30; break;
|
||||
case 'Y': dP[i] = 36 + getSameCodeLength('Y',i,cronixieDisplay); i = i+dP[i]-36; break;
|
||||
case 'y': dP[i] = 86 + getSameCodeLength('y',i,cronixieDisplay); i = i+dP[i]-86; break;
|
||||
case 'I': dP[i] = 39 + getSameCodeLength('I',i,cronixieDisplay); i = i+dP[i]-39; break; //Month. Don't ask me why month and minute both start with M.
|
||||
case 'i': dP[i] = 89 + getSameCodeLength('i',i,cronixieDisplay); i = i+dP[i]-89; break;
|
||||
//case 'W': break;
|
||||
//case 'w': break;
|
||||
case 'D': dP[i] = 43 + getSameCodeLength('D',i,cronixieDisplay); i = i+dP[i]-43; break;
|
||||
case 'd': dP[i] = 93 + getSameCodeLength('d',i,cronixieDisplay); i = i+dP[i]-93; break;
|
||||
case '0': dP[i] = 0; break;
|
||||
case '1': dP[i] = 1; break;
|
||||
case '2': dP[i] = 2; break;
|
||||
case '3': dP[i] = 3; break;
|
||||
case '4': dP[i] = 4; break;
|
||||
case '5': dP[i] = 5; break;
|
||||
case '6': dP[i] = 6; break;
|
||||
case '7': dP[i] = 7; break;
|
||||
case '8': dP[i] = 8; break;
|
||||
case '9': dP[i] = 9; break;
|
||||
//case 'V': break; //user var0
|
||||
//case 'v': break; //user var1
|
||||
}
|
||||
}
|
||||
DEBUG_PRINT("result ");
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
DEBUG_PRINT((int)dP[i]);
|
||||
DEBUG_PRINT(" ");
|
||||
}
|
||||
DEBUG_PRINTLN((int)dP[5]);
|
||||
|
||||
_overlayCronixie(); // refresh
|
||||
}
|
||||
|
||||
void _overlayCronixie()
|
||||
{
|
||||
byte h = hour(localTime);
|
||||
byte h0 = h;
|
||||
byte m = minute(localTime);
|
||||
byte s = second(localTime);
|
||||
byte d = day(localTime);
|
||||
byte mi = month(localTime);
|
||||
int y = year(localTime);
|
||||
//this has to be changed in time for 22nd century
|
||||
y -= 2000; if (y<0) y += 30; //makes countdown work
|
||||
|
||||
if (useAMPM && !countdownMode)
|
||||
{
|
||||
if (h>12) h-=12;
|
||||
else if (h==0) h+=12;
|
||||
}
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
if (dP[i] < 12) _digitOut[i] = dP[i];
|
||||
else {
|
||||
if (dP[i] < 65)
|
||||
{
|
||||
switch(dP[i])
|
||||
{
|
||||
case 21: _digitOut[i] = h/10; _digitOut[i+1] = h- _digitOut[i]*10; i++; break; //HH
|
||||
case 25: _digitOut[i] = m/10; _digitOut[i+1] = m- _digitOut[i]*10; i++; break; //MM
|
||||
case 31: _digitOut[i] = s/10; _digitOut[i+1] = s- _digitOut[i]*10; i++; break; //SS
|
||||
|
||||
case 20: _digitOut[i] = h- (h/10)*10; break; //H
|
||||
case 24: _digitOut[i] = m/10; break; //M
|
||||
case 30: _digitOut[i] = s/10; break; //S
|
||||
|
||||
case 43: _digitOut[i] = weekday(localTime); _digitOut[i]--; if (_digitOut[i]<1) _digitOut[i]= 7; break; //D
|
||||
case 44: _digitOut[i] = d/10; _digitOut[i+1] = d- _digitOut[i]*10; i++; break; //DD
|
||||
case 40: _digitOut[i] = mi/10; _digitOut[i+1] = mi- _digitOut[i]*10; i++; break; //II
|
||||
case 37: _digitOut[i] = y/10; _digitOut[i+1] = y- _digitOut[i]*10; i++; break; //YY
|
||||
case 39: _digitOut[i] = 2; _digitOut[i+1] = 0; _digitOut[i+2] = y/10; _digitOut[i+3] = y- _digitOut[i+2]*10; i+=3; break; //YYYY
|
||||
|
||||
//case 16: _digitOut[i+2] = ((h0/3)&1)?1:0; i++; //BBB (BBBB NI)
|
||||
//case 15: _digitOut[i+1] = (h0>17 || (h0>5 && h0<12))?1:0; i++; //BB
|
||||
case 14: _digitOut[i] = (h0>11)?1:0; break; //B
|
||||
}
|
||||
} else
|
||||
{
|
||||
switch(dP[i])
|
||||
{
|
||||
case 71: _digitOut[i] = h/10; _digitOut[i+1] = h- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //hh
|
||||
case 75: _digitOut[i] = m/10; _digitOut[i+1] = m- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //mm
|
||||
case 81: _digitOut[i] = s/10; _digitOut[i+1] = s- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //ss
|
||||
//case 66: _digitOut[i+2] = ((h0/3)&1)?1:10; i++; //bbb (bbbb NI)
|
||||
//case 65: _digitOut[i+1] = (h0>17 || (h0>5 && h0<12))?1:10; i++; //bb
|
||||
case 64: _digitOut[i] = (h0>11)?1:10; break; //b
|
||||
|
||||
case 93: _digitOut[i] = weekday(localTime); _digitOut[i]--; if (_digitOut[i]<1) _digitOut[i]= 7; break; //d
|
||||
case 94: _digitOut[i] = d/10; _digitOut[i+1] = d- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //dd
|
||||
case 90: _digitOut[i] = mi/10; _digitOut[i+1] = mi- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //ii
|
||||
case 87: _digitOut[i] = y/10; _digitOut[i+1] = y- _digitOut[i]*10; i++; break; //yy
|
||||
case 89: _digitOut[i] = 2; _digitOut[i+1] = 0; _digitOut[i+2] = y/10; _digitOut[i+3] = y- _digitOut[i+2]*10; i+=3; break; //yyyy
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleOverlayDraw()
|
||||
{
|
||||
byte offsets[] = {5, 0, 6, 1, 7, 2, 8, 3, 9, 4};
|
||||
|
||||
for (uint16_t i = 0; i < 6; i++)
|
||||
{
|
||||
byte o = 10*i;
|
||||
byte excl = 10;
|
||||
if(_digitOut[i] < 10) excl = offsets[_digitOut[i]];
|
||||
excl += o;
|
||||
|
||||
if (backlight && _digitOut[i] <11)
|
||||
{
|
||||
uint32_t col = strip.gamma32(strip.getSegment(0).colors[1]);
|
||||
for (uint16_t j=o; j< o+10; j++) {
|
||||
if (j != excl) strip.setPixelColor(j, col);
|
||||
}
|
||||
} else
|
||||
{
|
||||
for (uint16_t j=o; j< o+10; j++) {
|
||||
if (j != excl) strip.setPixelColor(j, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addToJsonState(JsonObject& root)
|
||||
{
|
||||
root["nx"] = cronixieDisplay;
|
||||
}
|
||||
|
||||
void readFromJsonState(JsonObject& root)
|
||||
{
|
||||
if (root["nx"].is<const char*>()) {
|
||||
strncpy(cronixieDisplay, root["nx"], 6);
|
||||
}
|
||||
}
|
||||
|
||||
void addToConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject(F("Cronixie"));
|
||||
top["backlight"] = backlight;
|
||||
}
|
||||
|
||||
bool readFromConfig(JsonObject& root)
|
||||
{
|
||||
// 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[F("Cronixie")];
|
||||
|
||||
bool configComplete = !top.isNull();
|
||||
|
||||
configComplete &= getJsonValue(top["backlight"], backlight);
|
||||
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_CRONIXIE;
|
||||
}
|
||||
};
|
||||
@@ -207,6 +207,17 @@ class MyExampleUsermod : public Usermod {
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* handleOverlayDraw() is called just before every show() (LED strip update frame) after effects have set the colors.
|
||||
* Use this to blank out some LEDs or set them to a different color regardless of the set effect mode.
|
||||
* Commonly used for custom clocks (Cronixie, 7 segment)
|
||||
*/
|
||||
void handleOverlayDraw()
|
||||
{
|
||||
//strip.setPixelColor(0, RGBW32(0,0,0,0)) // set the first pixel to black
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
||||
|
||||
@@ -12,6 +12,7 @@ class TFTs : public TFT_eSPI {
|
||||
private:
|
||||
uint8_t digits[NUM_DIGITS];
|
||||
|
||||
|
||||
// These read 16- and 32-bit types from the SD card file.
|
||||
// BMP data is stored little-endian, Arduino is little-endian too.
|
||||
// May need to reverse subscript order if porting elsewhere.
|
||||
@@ -33,7 +34,16 @@ private:
|
||||
}
|
||||
|
||||
uint16_t output_buffer[TFT_HEIGHT][TFT_WIDTH];
|
||||
|
||||
int16_t w = 135, h = 240, x = 0, y = 0, bufferedDigit = 255;
|
||||
uint16_t digitR, digitG, digitB, dimming = 255;
|
||||
uint32_t digitColor = 0;
|
||||
|
||||
void drawBuffer() {
|
||||
bool oldSwapBytes = getSwapBytes();
|
||||
setSwapBytes(true);
|
||||
pushImage(x, y, w, h, (uint16_t *)output_buffer);
|
||||
setSwapBytes(oldSwapBytes);
|
||||
}
|
||||
|
||||
// These BMP functions are stolen directly from the TFT_SPIFFS_BMP example in the TFT_eSPI library.
|
||||
// Unfortunately, they aren't part of the library itself, so I had to copy them.
|
||||
@@ -41,44 +51,69 @@ private:
|
||||
|
||||
//// BEGIN STOLEN CODE
|
||||
|
||||
// Draw directly from file stored in RGB565 format
|
||||
// Draw directly from file stored in RGB565 format. Fastest
|
||||
bool drawBin(const char *filename) {
|
||||
fs::File bmpFS;
|
||||
|
||||
|
||||
// Open requested file on SD card
|
||||
bmpFS = WLED_FS.open(filename, "r");
|
||||
|
||||
if (!bmpFS)
|
||||
{
|
||||
Serial.print(F("File not found: "));
|
||||
Serial.println(filename);
|
||||
return(false);
|
||||
}
|
||||
|
||||
size_t sz = bmpFS.size();
|
||||
if (sz <= 64800)
|
||||
{
|
||||
bool oldSwapBytes = getSwapBytes();
|
||||
setSwapBytes(true);
|
||||
|
||||
int16_t h = sz / (135 * 2);
|
||||
|
||||
//draw img that is shorter than 240pix into the center
|
||||
int16_t y = (height() - h) /2;
|
||||
|
||||
bmpFS.read((uint8_t *) output_buffer,sz);
|
||||
|
||||
if (!realtimeMode || realtimeOverride) strip.service();
|
||||
|
||||
pushImage(0, y, 135, h, (uint16_t *)output_buffer);
|
||||
|
||||
setSwapBytes(oldSwapBytes);
|
||||
if (sz > 64800) {
|
||||
bmpFS.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t r, g, b, dimming = 255;
|
||||
int16_t row, col;
|
||||
|
||||
//draw img that is shorter than 240pix into the center
|
||||
w = 135;
|
||||
h = sz / (w * 2);
|
||||
x = 0;
|
||||
y = (height() - h) /2;
|
||||
|
||||
uint8_t lineBuffer[w * 2];
|
||||
|
||||
if (!realtimeMode || realtimeOverride) strip.service();
|
||||
|
||||
// 0,0 coordinates are top left
|
||||
for (row = 0; row < h; row++) {
|
||||
|
||||
bmpFS.read(lineBuffer, sizeof(lineBuffer));
|
||||
uint8_t PixM, PixL;
|
||||
|
||||
// Colors are already in 16-bit R5, G6, B5 format
|
||||
for (col = 0; col < w; col++)
|
||||
{
|
||||
if (dimming == 255 && !digitColor) { // not needed, copy directly
|
||||
output_buffer[row][col] = (lineBuffer[col*2+1] << 8) | (lineBuffer[col*2]);
|
||||
} else {
|
||||
// 16 BPP pixel format: R5, G6, B5 ; bin: RRRR RGGG GGGB BBBB
|
||||
PixM = lineBuffer[col*2+1];
|
||||
PixL = lineBuffer[col*2];
|
||||
// align to 8-bit value (MSB left aligned)
|
||||
r = (PixM) & 0xF8;
|
||||
g = ((PixM << 5) | (PixL >> 3)) & 0xFC;
|
||||
b = (PixL << 3) & 0xF8;
|
||||
r *= dimming; g *= dimming; b *= dimming;
|
||||
r = r >> 8; g = g >> 8; b = b >> 8;
|
||||
if (digitColor) { // grayscale pixel coloring
|
||||
uint8_t l = (r > g) ? ((r > b) ? r:b) : ((g > b) ? g:b);
|
||||
r = g = b = l;
|
||||
r *= digitR; g *= digitG; b *= digitB;
|
||||
r = r >> 8; g = g >> 8; b = b >> 8;
|
||||
}
|
||||
output_buffer[row][col] = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawBuffer();
|
||||
|
||||
bmpFS.close();
|
||||
|
||||
return(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool drawBmp(const char *filename) {
|
||||
@@ -87,53 +122,52 @@ private:
|
||||
// Open requested file on SD card
|
||||
bmpFS = WLED_FS.open(filename, "r");
|
||||
|
||||
if (!bmpFS)
|
||||
{
|
||||
Serial.print(F("File not found: "));
|
||||
Serial.println(filename);
|
||||
return(false);
|
||||
}
|
||||
|
||||
uint32_t seekOffset;
|
||||
int16_t w, h, row;
|
||||
uint8_t r, g, b;
|
||||
uint32_t seekOffset, headerSize, paletteSize = 0;
|
||||
int16_t row;
|
||||
uint16_t r, g, b, dimming = 255, bitDepth;
|
||||
|
||||
uint16_t magic = read16(bmpFS);
|
||||
if (magic == 0xFFFF) {
|
||||
if (magic != ('B' | ('M' << 8))) { // File not found or not a BMP
|
||||
Serial.println(F("BMP not found!"));
|
||||
bmpFS.close();
|
||||
return(false);
|
||||
}
|
||||
|
||||
if (magic != 0x4D42) {
|
||||
Serial.print(F("File not a BMP. Magic: "));
|
||||
Serial.println(magic);
|
||||
bmpFS.close();
|
||||
return(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
read32(bmpFS);
|
||||
read32(bmpFS);
|
||||
seekOffset = read32(bmpFS);
|
||||
read32(bmpFS);
|
||||
w = read32(bmpFS);
|
||||
h = read32(bmpFS);
|
||||
read32(bmpFS); // filesize in bytes
|
||||
read32(bmpFS); // reserved
|
||||
seekOffset = read32(bmpFS); // start of bitmap
|
||||
headerSize = read32(bmpFS); // header size
|
||||
w = read32(bmpFS); // width
|
||||
h = read32(bmpFS); // height
|
||||
read16(bmpFS); // color planes (must be 1)
|
||||
bitDepth = read16(bmpFS);
|
||||
|
||||
if ((read16(bmpFS) != 1) || (read16(bmpFS) != 24) || (read32(bmpFS) != 0)) {
|
||||
if (read32(bmpFS) != 0 || (bitDepth != 24 && bitDepth != 1 && bitDepth != 4 && bitDepth != 8)) {
|
||||
Serial.println(F("BMP format not recognized."));
|
||||
bmpFS.close();
|
||||
return(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
//draw img that is shorter than 240pix into the center
|
||||
int16_t y = (height() - h) /2;
|
||||
uint32_t palette[256];
|
||||
if (bitDepth <= 8) // 1,4,8 bit bitmap: read color palette
|
||||
{
|
||||
read32(bmpFS); read32(bmpFS); read32(bmpFS); // size, w resolution, h resolution
|
||||
paletteSize = read32(bmpFS);
|
||||
if (paletteSize == 0) paletteSize = bitDepth * bitDepth; //if 0, size is 2^bitDepth
|
||||
bmpFS.seek(14 + headerSize); // start of color palette
|
||||
for (uint16_t i = 0; i < paletteSize; i++) {
|
||||
palette[i] = read32(bmpFS);
|
||||
}
|
||||
}
|
||||
|
||||
// draw img that is shorter than 240pix into the center
|
||||
x = (width() - w) /2;
|
||||
y = (height() - h) /2;
|
||||
|
||||
bool oldSwapBytes = getSwapBytes();
|
||||
setSwapBytes(true);
|
||||
bmpFS.seek(seekOffset);
|
||||
|
||||
uint16_t padding = (4 - ((w * 3) & 3)) & 3;
|
||||
uint8_t lineBuffer[w * 3 + padding];
|
||||
uint32_t lineSize = ((bitDepth * w +31) >> 5) * 4;
|
||||
uint8_t lineBuffer[lineSize];
|
||||
|
||||
uint8_t serviceStrip = (!realtimeMode || realtimeOverride) ? 7 : 0;
|
||||
// row is decremented as the BMP image is drawn bottom up
|
||||
@@ -142,23 +176,121 @@ private:
|
||||
bmpFS.read(lineBuffer, sizeof(lineBuffer));
|
||||
uint8_t* bptr = lineBuffer;
|
||||
|
||||
// Convert 24 to 16 bit colours while copying to output buffer.
|
||||
// Convert 24 to 16 bit colors while copying to output buffer.
|
||||
for (uint16_t col = 0; col < w; col++)
|
||||
{
|
||||
b = *bptr++;
|
||||
g = *bptr++;
|
||||
r = *bptr++;
|
||||
output_buffer[row][col] = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
|
||||
if (bitDepth == 24) {
|
||||
b = *bptr++;
|
||||
g = *bptr++;
|
||||
r = *bptr++;
|
||||
} else {
|
||||
uint32_t c = 0;
|
||||
if (bitDepth == 8) {
|
||||
c = palette[*bptr++];
|
||||
}
|
||||
else if (bitDepth == 4) {
|
||||
c = palette[(*bptr >> ((col & 0x01)?0:4)) & 0x0F];
|
||||
if (col & 0x01) bptr++;
|
||||
}
|
||||
else { // bitDepth == 1
|
||||
c = palette[(*bptr >> (7 - (col & 0x07))) & 0x01];
|
||||
if ((col & 0x07) == 0x07) bptr++;
|
||||
}
|
||||
b = c; g = c >> 8; r = c >> 16;
|
||||
}
|
||||
if (dimming != 255) { // only dimm when needed
|
||||
r *= dimming; g *= dimming; b *= dimming;
|
||||
r = r >> 8; g = g >> 8; b = b >> 8;
|
||||
}
|
||||
if (digitColor) { // grayscale pixel coloring
|
||||
uint8_t l = (r > g) ? ((r > b) ? r:b) : ((g > b) ? g:b);
|
||||
r = g = b = l;
|
||||
|
||||
r *= digitR; g *= digitG; b *= digitB;
|
||||
r = r >> 8; g = g >> 8; b = b >> 8;
|
||||
}
|
||||
output_buffer[row][col] = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xFF) >> 3);
|
||||
}
|
||||
}
|
||||
|
||||
pushImage(0, y, w, h, (uint16_t *)output_buffer);
|
||||
setSwapBytes(oldSwapBytes);
|
||||
drawBuffer();
|
||||
|
||||
bmpFS.close();
|
||||
return(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool drawClk(const char *filename) {
|
||||
fs::File bmpFS;
|
||||
|
||||
// Open requested file on SD card
|
||||
bmpFS = WLED_FS.open(filename, "r");
|
||||
|
||||
if (!bmpFS)
|
||||
{
|
||||
Serial.print("File not found: ");
|
||||
Serial.println(filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t r, g, b, dimming = 255, magic;
|
||||
int16_t row, col;
|
||||
|
||||
magic = read16(bmpFS);
|
||||
if (magic != 0x4B43) { // look for "CK" header
|
||||
Serial.print(F("File not a CLK. Magic: "));
|
||||
Serial.println(magic);
|
||||
bmpFS.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
w = read16(bmpFS);
|
||||
h = read16(bmpFS);
|
||||
x = (width() - w) / 2;
|
||||
y = (height() - h) / 2;
|
||||
|
||||
uint8_t lineBuffer[w * 2];
|
||||
|
||||
if (!realtimeMode || realtimeOverride) strip.service();
|
||||
|
||||
// 0,0 coordinates are top left
|
||||
for (row = 0; row < h; row++) {
|
||||
|
||||
bmpFS.read(lineBuffer, sizeof(lineBuffer));
|
||||
uint8_t PixM, PixL;
|
||||
|
||||
// Colors are already in 16-bit R5, G6, B5 format
|
||||
for (col = 0; col < w; col++)
|
||||
{
|
||||
if (dimming == 255 && !digitColor) { // not needed, copy directly
|
||||
output_buffer[row][col+x] = (lineBuffer[col*2+1] << 8) | (lineBuffer[col*2]);
|
||||
} else {
|
||||
// 16 BPP pixel format: R5, G6, B5 ; bin: RRRR RGGG GGGB BBBB
|
||||
PixM = lineBuffer[col*2+1];
|
||||
PixL = lineBuffer[col*2];
|
||||
// align to 8-bit value (MSB left aligned)
|
||||
r = (PixM) & 0xF8;
|
||||
g = ((PixM << 5) | (PixL >> 3)) & 0xFC;
|
||||
b = (PixL << 3) & 0xF8;
|
||||
r *= dimming; g *= dimming; b *= dimming;
|
||||
r = r >> 8; g = g >> 8; b = b >> 8;
|
||||
if (digitColor) { // grayscale pixel coloring
|
||||
uint8_t l = (r > g) ? ((r > b) ? r:b) : ((g > b) ? g:b);
|
||||
r = g = b = l;
|
||||
r *= digitR; g *= digitG; b *= digitB;
|
||||
r = r >> 8; g = g >> 8; b = b >> 8;
|
||||
}
|
||||
output_buffer[row][col+x] = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawBuffer();
|
||||
|
||||
bmpFS.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
TFTs() : TFT_eSPI(), chip_select()
|
||||
{ for (uint8_t digit=0; digit < NUM_DIGITS; digit++) digits[digit] = 0; }
|
||||
@@ -167,6 +299,9 @@ public:
|
||||
enum show_t { no, yes, force };
|
||||
// A digit of 0xFF means blank the screen.
|
||||
const static uint8_t blanked = 255;
|
||||
|
||||
uint8_t tubeSegment = 1;
|
||||
uint8_t digitOffset = 0;
|
||||
|
||||
void begin() {
|
||||
pinMode(TFT_ENABLE_PIN, OUTPUT);
|
||||
@@ -182,34 +317,60 @@ public:
|
||||
|
||||
void showDigit(uint8_t digit) {
|
||||
chip_select.setDigit(digit);
|
||||
uint8_t digitToDraw = digits[digit];
|
||||
if (digitToDraw < 10) digitToDraw += digitOffset;
|
||||
|
||||
if (digits[digit] == blanked) {
|
||||
fillScreen(TFT_BLACK);
|
||||
if (digitToDraw == blanked) {
|
||||
fillScreen(TFT_BLACK); return;
|
||||
}
|
||||
else {
|
||||
// Filenames are no bigger than "255.bmp\0"
|
||||
char file_name[10];
|
||||
sprintf(file_name, "/%d.bmp", digits[digit]);
|
||||
if (WLED_FS.exists(file_name)) {
|
||||
drawBmp(file_name);
|
||||
} else {
|
||||
sprintf(file_name, "/%d.bin", digits[digit]);
|
||||
drawBin(file_name);
|
||||
}
|
||||
|
||||
// if last digit was the same, skip loading from FS to buffer
|
||||
if (!digitColor && digitToDraw == bufferedDigit) drawBuffer();
|
||||
digitR = R(digitColor); digitG = G(digitColor); digitB = B(digitColor);
|
||||
|
||||
// Filenames are no bigger than "254.bmp\0"
|
||||
char file_name[10];
|
||||
// Fastest, raw RGB565
|
||||
sprintf(file_name, "/%d.bin", digitToDraw);
|
||||
if (WLED_FS.exists(file_name)) {
|
||||
if (drawBin(file_name)) bufferedDigit = digitToDraw;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Fast, raw RGB565, see https://github.com/aly-fly/EleksTubeHAX on how to create this clk format
|
||||
sprintf(file_name, "/%d.clk", digitToDraw);
|
||||
if (WLED_FS.exists(file_name)) {
|
||||
if (drawClk(file_name)) bufferedDigit = digitToDraw;
|
||||
return;
|
||||
}
|
||||
// Slow, regular RGB888 or 1,4,8 bit palette BMP
|
||||
sprintf(file_name, "/%d.bmp", digitToDraw);
|
||||
if (drawBmp(file_name)) bufferedDigit = digitToDraw;
|
||||
return;
|
||||
}
|
||||
|
||||
void setDigit(uint8_t digit, uint8_t value, show_t show=yes) {
|
||||
uint8_t old_value = digits[digit];
|
||||
digits[digit] = value;
|
||||
|
||||
|
||||
// Color in grayscale bitmaps if Segment 1 exists
|
||||
// TODO If secondary and tertiary are black, color all in primary,
|
||||
// else color first three from Seg 1 color slots and last three from Seg 2 color slots
|
||||
WS2812FX::Segment& seg1 = strip.getSegment(tubeSegment);
|
||||
if (seg1.isActive()) {
|
||||
digitColor = strip.getPixelColor(seg1.start + digit);
|
||||
dimming = seg1.opacity;
|
||||
} else {
|
||||
digitColor = 0;
|
||||
dimming = 255;
|
||||
}
|
||||
|
||||
if (show != no && (old_value != value || show == force)) {
|
||||
showDigit(digit);
|
||||
}
|
||||
}
|
||||
uint8_t getDigit(uint8_t digit) { return digits[digit]; }
|
||||
uint8_t getDigit(uint8_t digit) {return digits[digit];}
|
||||
|
||||
void showAllDigits() { for (uint8_t digit=0; digit < NUM_DIGITS; digit++) showDigit(digit); }
|
||||
void showAllDigits() {for (uint8_t digit=0; digit < NUM_DIGITS; digit++) showDigit(digit);}
|
||||
|
||||
// Making chip_select public so we don't have to proxy all methods, and the caller can just use it directly.
|
||||
ChipSelect chip_select;
|
||||
|
||||
@@ -5,16 +5,17 @@ It enables running all WLED effects on the background SK6812 lighting, while dis
|
||||
Code is largely based on https://github.com/SmittyHalibut/EleksTubeHAX by Mark Smith!
|
||||
|
||||
Supported:
|
||||
- Display with custom bitmaps or raw RGB565 images (.bin) from filesystem
|
||||
- Display with custom bitmaps (.bmp) or raw RGB565 images (.bin) from filesystem
|
||||
- Background lighting
|
||||
- Power button
|
||||
- All 4 hardware buttons
|
||||
- RTC (with RTC usermod)
|
||||
- Standard WLED time features (NTP, DST, timezones)
|
||||
|
||||
Not supported:
|
||||
- 3 navigation buttons, on-device setup
|
||||
- On-device setup with buttons (WiFi setup only)
|
||||
|
||||
Your images must be exactly 135 pixels wide and 1-240 pixels high.
|
||||
Your images must be 1-135 pixels wide and 1-240 pixels high.
|
||||
For BMP, 1, 4, 8, and 24 bits per pixel formats are supported.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -25,7 +26,20 @@ Use LED pin 12, relay pin 27 and button pin 34.
|
||||
|
||||
## Use of RGB565 images
|
||||
|
||||
Binary 16-bit per pixel RGB565 format `.bin` images are now supported. This has the benefit of only using 2/3rds of the file size a `.bmp` has.
|
||||
Binary 16-bit per pixel RGB565 format `.bin` and `.clk` images are now supported. This has the benefit of only using 2/3rds of the file size a 24 BPP `.bmp` has.
|
||||
The drawback is that this format cannot be handled by common image programs and that an extra conversion step is needed.
|
||||
You can use https://lvgl.io/tools/imageconverter to convert your .bmp to a .bin file (settings `True color` and `Binary RGB565`)
|
||||
Thank you to @RedNax67 for adding .bin support.
|
||||
You can use https://lvgl.io/tools/imageconverter to convert your .bmp to a .bin file (settings `True color` and `Binary RGB565`).
|
||||
Thank you to @RedNax67 for adding .bin and .clk support.
|
||||
For most clockface designs, using 4 or 8 BPP BMP formats will save even more file size:
|
||||
|
||||
| Bits per pixel | File size in kB (for 135x240 img) | % of 24 BPP BMP | Max unique colors
|
||||
| --- | --- | --- | --- |
|
||||
24 | 98 | 100% | 16M (66K)
|
||||
16 (.clk) | 64.8 | 66% | 66K
|
||||
8 | 33.7 | 34% | 256
|
||||
4 | 16.4 | 17% | 16
|
||||
1 | 4.9 | 5% | 2
|
||||
|
||||
Comparison 1 vs. 4 vs. 8 vs. 24 BPP. With this clockface on the actual clock, 4 bit looks good, and 8 bit is almost indistinguishable from 24 bit.
|
||||
|
||||

|
||||
@@ -6,6 +6,13 @@
|
||||
|
||||
class ElekstubeIPSUsermod : public Usermod {
|
||||
private:
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _tubeSeg[];
|
||||
static const char _digitOffset[];
|
||||
|
||||
char cronixieDisplay[7] = "HHMMSS";
|
||||
|
||||
TFTs tfts;
|
||||
void updateClockDisplay(TFTs::show_t show=TFTs::yes) {
|
||||
bool set[6] = {false};
|
||||
@@ -21,6 +28,8 @@ class ElekstubeIPSUsermod : public Usermod {
|
||||
set[i] = false; //display HHMMSS time
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint8_t hr = hour(localTime);
|
||||
uint8_t hrTens = hr/10;
|
||||
uint8_t mi = minute(localTime);
|
||||
@@ -37,6 +46,10 @@ class ElekstubeIPSUsermod : public Usermod {
|
||||
unsigned long lastTime = 0;
|
||||
public:
|
||||
|
||||
uint8_t lastBri;
|
||||
uint32_t lastCols[6];
|
||||
TFTs::show_t fshow=TFTs::yes;
|
||||
|
||||
void setup() {
|
||||
tfts.begin();
|
||||
tfts.fillScreen(TFT_BLACK);
|
||||
@@ -47,14 +60,99 @@ class ElekstubeIPSUsermod : public Usermod {
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (toki.isTick()) {
|
||||
updateLocalTime();
|
||||
updateClockDisplay();
|
||||
if (!toki.isTick()) return;
|
||||
updateLocalTime();
|
||||
|
||||
WS2812FX::Segment& seg1 = strip.getSegment(tfts.tubeSegment);
|
||||
if (seg1.isActive()) {
|
||||
bool update = false;
|
||||
if (seg1.opacity != lastBri) update = true;
|
||||
lastBri = seg1.opacity;
|
||||
for (uint8_t i = 0; i < 6; i++) {
|
||||
uint32_t c = strip.getPixelColor(seg1.start + i);
|
||||
if (c != lastCols[i]) update = true;
|
||||
lastCols[i] = c;
|
||||
}
|
||||
if (update) fshow=TFTs::force;
|
||||
} else if (lastCols[0] != 0) { // Segment 1 deleted
|
||||
fshow=TFTs::force;
|
||||
lastCols[0] = 0;
|
||||
}
|
||||
|
||||
updateClockDisplay(fshow);
|
||||
fshow=TFTs::yes;
|
||||
}
|
||||
|
||||
/**
|
||||
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
|
||||
*/
|
||||
void addToConfig(JsonObject &root) {
|
||||
// we add JSON object: {"EleksTubeIPS": {"tubeSegment": 1, "digitOffset": 0}}
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||
top[FPSTR(_tubeSeg)] = tfts.tubeSegment;
|
||||
top[FPSTR(_digitOffset)] = tfts.digitOffset;
|
||||
DEBUG_PRINTLN(F("EleksTube config saved."));
|
||||
}
|
||||
|
||||
/**
|
||||
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
|
||||
*
|
||||
* The function should return true if configuration was successfully loaded or false if there was no configuration.
|
||||
*/
|
||||
bool readFromConfig(JsonObject &root) {
|
||||
// we look for JSON object: {"EleksTubeIPS": {"tubeSegment": 1, "digitOffset": 0}}
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
tfts.tubeSegment = top[FPSTR(_tubeSeg)] | tfts.tubeSegment;
|
||||
uint8_t digitOffsetPrev = tfts.digitOffset;
|
||||
tfts.digitOffset = top[FPSTR(_digitOffset)] | tfts.digitOffset;
|
||||
if (tfts.digitOffset > 240) tfts.digitOffset = 240;
|
||||
if (tfts.digitOffset != digitOffsetPrev) fshow=TFTs::force;
|
||||
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[FPSTR(_digitOffset)].isNull();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
root["nx"] = cronixieDisplay;
|
||||
root[FPSTR(_digitOffset)] = tfts.digitOffset;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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 (root["nx"].is<const char*>()) {
|
||||
strncpy(cronixieDisplay, root["nx"], 6);
|
||||
}
|
||||
|
||||
uint8_t digitOffsetPrev = tfts.digitOffset;
|
||||
tfts.digitOffset = root[FPSTR(_digitOffset)] | tfts.digitOffset;
|
||||
if (tfts.digitOffset > 240) tfts.digitOffset = 240;
|
||||
if (tfts.digitOffset != digitOffsetPrev) fshow=TFTs::force;
|
||||
}
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_ELEKSTUBE_IPS;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char ElekstubeIPSUsermod::_name[] PROGMEM = "EleksTubeIPS";
|
||||
const char ElekstubeIPSUsermod::_tubeSeg[] PROGMEM = "tubeSegment";
|
||||
const char ElekstubeIPSUsermod::_digitOffset[] PROGMEM = "digitOffset";
|
||||
|
||||
321
usermods/MY9291/MY92xx.h
Normal file
321
usermods/MY9291/MY92xx.h
Normal file
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
|
||||
MY92XX LED Driver for Arduino
|
||||
Based on the C driver by MaiKe Labs
|
||||
|
||||
Copyright (c) 2016 - 2026 MaiKe Labs
|
||||
Copyright (C) 2017 - 2018 Xose Pérez for the Arduino compatible library
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _my92xx_h
|
||||
#define _my92xx_h
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef DEBUG_MY92XX
|
||||
#if ARDUINO_ARCH_ESP8266
|
||||
#define DEBUG_MSG_MY92XX(...) DEBUG_MY92XX.printf( __VA_ARGS__ )
|
||||
#elif ARDUINO_ARCH_AVR
|
||||
#define DEBUG_MSG_MY92XX(...) { char buffer[80]; snprintf(buffer, sizeof(buffer), __VA_ARGS__ ); DEBUG_MY92XX.print(buffer); }
|
||||
#endif
|
||||
#else
|
||||
#define DEBUG_MSG_MY92XX(...)
|
||||
#endif
|
||||
|
||||
typedef enum my92xx_model_t {
|
||||
MY92XX_MODEL_MY9291 = 0X00,
|
||||
MY92XX_MODEL_MY9231 = 0X01,
|
||||
} my92xx_model_t;
|
||||
|
||||
typedef enum my92xx_cmd_one_shot_t {
|
||||
MY92XX_CMD_ONE_SHOT_DISABLE = 0X00,
|
||||
MY92XX_CMD_ONE_SHOT_ENFORCE = 0X01,
|
||||
} my92xx_cmd_one_shot_t;
|
||||
|
||||
typedef enum my92xx_cmd_reaction_t {
|
||||
MY92XX_CMD_REACTION_FAST = 0X00,
|
||||
MY92XX_CMD_REACTION_SLOW = 0X01,
|
||||
} my92xx_cmd_reaction_t;
|
||||
|
||||
typedef enum my92xx_cmd_bit_width_t {
|
||||
MY92XX_CMD_BIT_WIDTH_16 = 0X00,
|
||||
MY92XX_CMD_BIT_WIDTH_14 = 0X01,
|
||||
MY92XX_CMD_BIT_WIDTH_12 = 0X02,
|
||||
MY92XX_CMD_BIT_WIDTH_8 = 0X03,
|
||||
} my92xx_cmd_bit_width_t;
|
||||
|
||||
typedef enum my92xx_cmd_frequency_t {
|
||||
MY92XX_CMD_FREQUENCY_DIVIDE_1 = 0X00,
|
||||
MY92XX_CMD_FREQUENCY_DIVIDE_4 = 0X01,
|
||||
MY92XX_CMD_FREQUENCY_DIVIDE_16 = 0X02,
|
||||
MY92XX_CMD_FREQUENCY_DIVIDE_64 = 0X03,
|
||||
} my92xx_cmd_frequency_t;
|
||||
|
||||
typedef enum my92xx_cmd_scatter_t {
|
||||
MY92XX_CMD_SCATTER_APDM = 0X00,
|
||||
MY92XX_CMD_SCATTER_PWM = 0X01,
|
||||
} my92xx_cmd_scatter_t;
|
||||
|
||||
typedef struct {
|
||||
my92xx_cmd_scatter_t scatter : 1;
|
||||
my92xx_cmd_frequency_t frequency : 2;
|
||||
my92xx_cmd_bit_width_t bit_width : 2;
|
||||
my92xx_cmd_reaction_t reaction : 1;
|
||||
my92xx_cmd_one_shot_t one_shot : 1;
|
||||
unsigned char resv : 1;
|
||||
} __attribute__((aligned(1), packed)) my92xx_cmd_t;
|
||||
|
||||
#define MY92XX_COMMAND_DEFAULT { \
|
||||
.scatter = MY92XX_CMD_SCATTER_APDM, \
|
||||
.frequency = MY92XX_CMD_FREQUENCY_DIVIDE_1, \
|
||||
.bit_width = MY92XX_CMD_BIT_WIDTH_8, \
|
||||
.reaction = MY92XX_CMD_REACTION_FAST, \
|
||||
.one_shot = MY92XX_CMD_ONE_SHOT_DISABLE, \
|
||||
.resv = 0 \
|
||||
}
|
||||
|
||||
class my92xx {
|
||||
|
||||
public:
|
||||
|
||||
my92xx(my92xx_model_t model, unsigned char chips, unsigned char di, unsigned char dcki, my92xx_cmd_t command);
|
||||
unsigned char getChannels();
|
||||
void setChannel(unsigned char channel, unsigned int value);
|
||||
unsigned int getChannel(unsigned char channel);
|
||||
void setState(bool state);
|
||||
bool getState();
|
||||
void update();
|
||||
|
||||
private:
|
||||
|
||||
void _di_pulse(unsigned int times);
|
||||
void _dcki_pulse(unsigned int times);
|
||||
void _set_cmd(my92xx_cmd_t command);
|
||||
void _send();
|
||||
void _write(unsigned int data, unsigned char bit_length);
|
||||
|
||||
my92xx_cmd_t _command;
|
||||
my92xx_model_t _model = MY92XX_MODEL_MY9291;
|
||||
unsigned char _chips = 1;
|
||||
unsigned char _channels;
|
||||
uint16_t* _value;
|
||||
bool _state = false;
|
||||
unsigned char _pin_di;
|
||||
unsigned char _pin_dcki;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
#if ARDUINO_ARCH_ESP8266
|
||||
|
||||
extern "C" {
|
||||
void os_delay_us(unsigned int);
|
||||
}
|
||||
|
||||
#elif ARDUINO_ARCH_AVR
|
||||
|
||||
#define os_delay_us delayMicroseconds
|
||||
|
||||
#endif
|
||||
|
||||
void my92xx::_di_pulse(unsigned int times) {
|
||||
for (unsigned int i = 0; i < times; i++) {
|
||||
digitalWrite(_pin_di, HIGH);
|
||||
digitalWrite(_pin_di, LOW);
|
||||
}
|
||||
}
|
||||
|
||||
void my92xx::_dcki_pulse(unsigned int times) {
|
||||
for (unsigned int i = 0; i < times; i++) {
|
||||
digitalWrite(_pin_dcki, HIGH);
|
||||
digitalWrite(_pin_dcki, LOW);
|
||||
}
|
||||
}
|
||||
|
||||
void my92xx::_write(unsigned int data, unsigned char bit_length) {
|
||||
|
||||
unsigned int mask = (0x01 << (bit_length - 1));
|
||||
|
||||
for (unsigned int i = 0; i < bit_length / 2; i++) {
|
||||
digitalWrite(_pin_dcki, LOW);
|
||||
digitalWrite(_pin_di, (data & mask) ? HIGH : LOW);
|
||||
digitalWrite(_pin_dcki, HIGH);
|
||||
data = data << 1;
|
||||
digitalWrite(_pin_di, (data & mask) ? HIGH : LOW);
|
||||
digitalWrite(_pin_dcki, LOW);
|
||||
digitalWrite(_pin_di, LOW);
|
||||
data = data << 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void my92xx::_set_cmd(my92xx_cmd_t command) {
|
||||
|
||||
// ets_intr_lock();
|
||||
|
||||
// TStop > 12us.
|
||||
os_delay_us(12);
|
||||
|
||||
// Send 12 DI pulse, after 6 pulse's falling edge store duty data, and 12
|
||||
// pulse's rising edge convert to command mode.
|
||||
_di_pulse(12);
|
||||
|
||||
// Delay >12us, begin send CMD data
|
||||
os_delay_us(12);
|
||||
|
||||
// Send CMD data
|
||||
unsigned char command_data = *(unsigned char*)(&command);
|
||||
for (unsigned char i = 0; i < _chips; i++) {
|
||||
_write(command_data, 8);
|
||||
}
|
||||
|
||||
// TStart > 12us. Delay 12 us.
|
||||
os_delay_us(12);
|
||||
|
||||
// Send 16 DI pulse,at 14 pulse's falling edge store CMD data, and
|
||||
// at 16 pulse's falling edge convert to duty mode.
|
||||
_di_pulse(16);
|
||||
|
||||
// TStop > 12us.
|
||||
os_delay_us(12);
|
||||
|
||||
// ets_intr_unlock();
|
||||
|
||||
}
|
||||
|
||||
void my92xx::_send() {
|
||||
|
||||
#ifdef DEBUG_MY92XX
|
||||
DEBUG_MSG_MY92XX("[MY92XX] Refresh: %s (", _state ? "ON" : "OFF");
|
||||
for (unsigned char channel = 0; channel < _channels; channel++) {
|
||||
DEBUG_MSG_MY92XX(" %d", _value[channel]);
|
||||
}
|
||||
DEBUG_MSG_MY92XX(" )\n");
|
||||
#endif
|
||||
|
||||
unsigned char bit_length = 8;
|
||||
switch (_command.bit_width) {
|
||||
case MY92XX_CMD_BIT_WIDTH_16:
|
||||
bit_length = 16;
|
||||
break;
|
||||
case MY92XX_CMD_BIT_WIDTH_14:
|
||||
bit_length = 14;
|
||||
break;
|
||||
case MY92XX_CMD_BIT_WIDTH_12:
|
||||
bit_length = 12;
|
||||
break;
|
||||
case MY92XX_CMD_BIT_WIDTH_8:
|
||||
bit_length = 8;
|
||||
break;
|
||||
default:
|
||||
bit_length = 8;
|
||||
break;
|
||||
}
|
||||
|
||||
// ets_intr_lock();
|
||||
|
||||
// TStop > 12us.
|
||||
os_delay_us(12);
|
||||
|
||||
// Send color data
|
||||
for (unsigned char channel = 0; channel < _channels; channel++) {
|
||||
_write(_state ? _value[channel] : 0, bit_length);
|
||||
}
|
||||
|
||||
// TStart > 12us. Ready for send DI pulse.
|
||||
os_delay_us(12);
|
||||
|
||||
// Send 8 DI pulse. After 8 pulse falling edge, store old data.
|
||||
_di_pulse(8);
|
||||
|
||||
// TStop > 12us.
|
||||
os_delay_us(12);
|
||||
|
||||
// ets_intr_unlock();
|
||||
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
unsigned char my92xx::getChannels() {
|
||||
return _channels;
|
||||
}
|
||||
|
||||
void my92xx::setChannel(unsigned char channel, unsigned int value) {
|
||||
if (channel < _channels) {
|
||||
_value[channel] = value;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int my92xx::getChannel(unsigned char channel) {
|
||||
if (channel < _channels) {
|
||||
return _value[channel];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool my92xx::getState() {
|
||||
return _state;
|
||||
}
|
||||
|
||||
void my92xx::setState(bool state) {
|
||||
_state = state;
|
||||
}
|
||||
|
||||
void my92xx::update() {
|
||||
_send();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
my92xx::my92xx(my92xx_model_t model, unsigned char chips, unsigned char di, unsigned char dcki, my92xx_cmd_t command) : _command(command) {
|
||||
|
||||
_model = model;
|
||||
_chips = chips;
|
||||
_pin_di = di;
|
||||
_pin_dcki = dcki;
|
||||
|
||||
// Init channels
|
||||
if (_model == MY92XX_MODEL_MY9291) {
|
||||
_channels = 4 * _chips;
|
||||
}
|
||||
else if (_model == MY92XX_MODEL_MY9231) {
|
||||
_channels = 3 * _chips;
|
||||
}
|
||||
_value = new uint16_t[_channels];
|
||||
for (unsigned char i = 0; i < _channels; i++) {
|
||||
_value[i] = 0;
|
||||
}
|
||||
|
||||
// Init GPIO
|
||||
pinMode(_pin_di, OUTPUT);
|
||||
pinMode(_pin_dcki, OUTPUT);
|
||||
digitalWrite(_pin_di, LOW);
|
||||
digitalWrite(_pin_dcki, LOW);
|
||||
|
||||
// Clear all duty register
|
||||
_dcki_pulse(32 * _chips);
|
||||
|
||||
// Send init command
|
||||
_set_cmd(command);
|
||||
|
||||
DEBUG_MSG_MY92XX("[MY92XX] Initialized\n");
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
45
usermods/MY9291/usermode_MY9291.h
Normal file
45
usermods/MY9291/usermode_MY9291.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
#include "MY92xx.h"
|
||||
|
||||
#define MY92XX_MODEL MY92XX_MODEL_MY9291
|
||||
#define MY92XX_CHIPS 1
|
||||
#define MY92XX_DI_PIN 13
|
||||
#define MY92XX_DCKI_PIN 15
|
||||
|
||||
#define MY92XX_RED 0
|
||||
#define MY92XX_GREEN 1
|
||||
#define MY92XX_BLUE 2
|
||||
#define MY92XX_WHITE 3
|
||||
|
||||
class MY9291Usermod : public Usermod {
|
||||
private:
|
||||
my92xx _my92xx = my92xx(MY92XX_MODEL, MY92XX_CHIPS, MY92XX_DI_PIN, MY92XX_DCKI_PIN, MY92XX_COMMAND_DEFAULT);
|
||||
|
||||
public:
|
||||
|
||||
void setup() {
|
||||
_my92xx.setState(true);
|
||||
}
|
||||
|
||||
void connected() {
|
||||
}
|
||||
|
||||
void loop() {
|
||||
uint32_t c = strip.getPixelColor(0);
|
||||
int w = ((c >> 24) & 0xff) * bri / 255.0;
|
||||
int r = ((c >> 16) & 0xff) * bri / 255.0;
|
||||
int g = ((c >> 8) & 0xff) * bri / 255.0;
|
||||
int b = (c & 0xff) * bri / 255.0;
|
||||
_my92xx.setChannel(MY92XX_RED, r);
|
||||
_my92xx.setChannel(MY92XX_GREEN, g);
|
||||
_my92xx.setChannel(MY92XX_BLUE, b);
|
||||
_my92xx.setChannel(MY92XX_WHITE, w);
|
||||
_my92xx.update();
|
||||
}
|
||||
|
||||
uint16_t getId() {
|
||||
return USERMOD_ID_MY9291;
|
||||
}
|
||||
};
|
||||
@@ -19,8 +19,8 @@ You will also need `-D USERMOD_DALLASTEMPERATURE`.
|
||||
All of the parameters are configured during run-time using Usermods settings page.
|
||||
This includes:
|
||||
|
||||
* PWM output pin
|
||||
* tacho input pin
|
||||
* PWM output pin (can be configured at compile time `-D PWM_PIN=xx`)
|
||||
* tacho input pin (can be configured at compile time `-D TACHO_PIN=xx`)
|
||||
* sampling frequency in seconds
|
||||
* threshold temperature in degees C
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@
|
||||
// https://github.com/KlausMu/esp32-fan-controller/tree/main/src
|
||||
// adapted for WLED usermod by @blazoncek
|
||||
|
||||
#ifndef TACHO_PIN
|
||||
#define TACHO_PIN -1
|
||||
#endif
|
||||
|
||||
#ifndef PWM_PIN
|
||||
#define PWM_PIN -1
|
||||
#endif
|
||||
|
||||
// tacho counter
|
||||
static volatile unsigned long counter_rpm = 0;
|
||||
@@ -37,8 +44,8 @@ class PWMFanUsermod : public Usermod {
|
||||
#endif
|
||||
|
||||
// configurable parameters
|
||||
int8_t tachoPin = -1;
|
||||
int8_t pwmPin = -1;
|
||||
int8_t tachoPin = TACHO_PIN;
|
||||
int8_t pwmPin = PWM_PIN;
|
||||
uint8_t tachoUpdateSec = 30;
|
||||
float targetTemperature = 25.0;
|
||||
uint8_t minPWMValuePct = 50;
|
||||
|
||||
76
usermods/RelayBlinds/index.htm
Normal file
76
usermods/RelayBlinds/index.htm
Normal file
@@ -0,0 +1,76 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta charset="utf-8">
|
||||
<title>Blinds</title>
|
||||
<script>
|
||||
strA = "";
|
||||
function send()
|
||||
{
|
||||
nocache = "&nocache=" + Math.random() * 1000000;
|
||||
var request = new XMLHttpRequest();
|
||||
// send HTTP request
|
||||
request.open("GET", "win/" + strA +nocache, true);
|
||||
request.send(null);
|
||||
strA = "";
|
||||
}
|
||||
function up()
|
||||
{
|
||||
strA = "&U0=2";
|
||||
send();
|
||||
}
|
||||
function down()
|
||||
{
|
||||
strA = "&U0=1";
|
||||
send();
|
||||
}
|
||||
function OpenSettings()
|
||||
{
|
||||
window.open("/settings", "_self");
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
text-align: center;
|
||||
background: linear-gradient(45deg,#0ca,#0ac);
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
svg {
|
||||
width: 30vw;
|
||||
padding: 2vh;
|
||||
}
|
||||
.tool_box {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
</style>
|
||||
<style id="holderjs-style" type="text/css"></style></head>
|
||||
<body class=" __plain_text_READY__">
|
||||
<svg style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<symbol id="icon-box-add" viewBox="0 0 32 32">
|
||||
<path d="M26 2h-20l-6 6v21c0 0.552 0.448 1 1 1h30c0.552 0 1-0.448 1-1v-21l-6-6zM16 26l-10-8h6v-6h8v6h6l-10 8zM4.828 6l2-2h18.343l2 2h-22.343z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-box-remove" viewBox="0 0 32 32">
|
||||
<path d="M26 2h-20l-6 6v21c0 0.552 0.448 1 1 1h30c0.552 0 1-0.448 1-1v-21l-6-6zM20 20v6h-8v-6h-6l10-8 10 8h-6zM4.828 6l2-2h18.343l2 2h-22.343z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-cog" viewBox="0 0 32 32">
|
||||
<path d="M29.181 19.070c-1.679-2.908-0.669-6.634 2.255-8.328l-3.145-5.447c-0.898 0.527-1.943 0.829-3.058 0.829-3.361 0-6.085-2.742-6.085-6.125h-6.289c0.008 1.044-0.252 2.103-0.811 3.070-1.679 2.908-5.411 3.897-8.339 2.211l-3.144 5.447c0.905 0.515 1.689 1.268 2.246 2.234 1.676 2.903 0.672 6.623-2.241 8.319l3.145 5.447c0.895-0.522 1.935-0.82 3.044-0.82 3.35 0 6.067 2.725 6.084 6.092h6.289c-0.003-1.034 0.259-2.080 0.811-3.038 1.676-2.903 5.399-3.894 8.325-2.219l3.145-5.447c-0.899-0.515-1.678-1.266-2.232-2.226zM16 22.479c-3.578 0-6.479-2.901-6.479-6.479s2.901-6.479 6.479-6.479c3.578 0 6.479 2.901 6.479 6.479s-2.901 6.479-6.479 6.479z"></path>
|
||||
</symbol>
|
||||
</defs>
|
||||
</svg>
|
||||
<div id="tbB" class="tool_box">
|
||||
<svg id="upb" onclick="up()"><use xlink:href="#icon-box-remove"></use></svg>
|
||||
<svg id="dnb" onclick="down()"><use xlink:href="#icon-box-add"></use></svg>
|
||||
<svg id="stb" onclick="OpenSettings()"><use xlink:href="#icon-cog"></use></svg>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
1
usermods/RelayBlinds/presets.json
Normal file
1
usermods/RelayBlinds/presets.json
Normal file
@@ -0,0 +1 @@
|
||||
{"0":{},"2":{"n":"▲","win":"U0=2"},"1":{"n":"▼","win":"U0=1"}}
|
||||
8
usermods/RelayBlinds/readme.md
Normal file
8
usermods/RelayBlinds/readme.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# RelayBlinds usermod
|
||||
|
||||
This simple usermod toggles two relay pins momentarily (default for 500ms) when `userVar0` is set.
|
||||
This can be used to e.g. "push" the buttons of a window blinds motor controller.
|
||||
|
||||
v1 usermod. Please replace usermod.cpp in the `wled00` directory with the one in this file.
|
||||
You may upload `index.htm` to `[WLED-IP]/edit` to replace the default lighting UI with a simple Up/Down button one.
|
||||
Also, a simple `presets.json` file is available, this makes the relay actions controllable via two presets to facilitate control e.g. via the default UI or Alexa.
|
||||
83
usermods/RelayBlinds/usermod.cpp
Normal file
83
usermods/RelayBlinds/usermod.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "wled.h"
|
||||
|
||||
//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t)
|
||||
|
||||
//gets called once at boot. Do all initialization that doesn't depend on network here
|
||||
void userSetup()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
|
||||
void userConnected()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Physical IO
|
||||
*/
|
||||
#define PIN_UP_RELAY 4
|
||||
#define PIN_DN_RELAY 5
|
||||
#define PIN_ON_TIME 500
|
||||
bool upActive = false, upActiveBefore = false, downActive = false, downActiveBefore = false;
|
||||
unsigned long upStartTime = 0, downStartTime = 0;
|
||||
|
||||
void handleRelay()
|
||||
{
|
||||
//up and down relays
|
||||
if (userVar0) {
|
||||
upActive = true;
|
||||
if (userVar0 == 1) {
|
||||
upActive = false;
|
||||
downActive = true;
|
||||
}
|
||||
userVar0 = 0;
|
||||
}
|
||||
|
||||
if (upActive)
|
||||
{
|
||||
if(!upActiveBefore)
|
||||
{
|
||||
pinMode(PIN_UP_RELAY, OUTPUT);
|
||||
digitalWrite(PIN_UP_RELAY, LOW);
|
||||
upActiveBefore = true;
|
||||
upStartTime = millis();
|
||||
DEBUG_PRINTLN("UPA");
|
||||
}
|
||||
if (millis()- upStartTime > PIN_ON_TIME)
|
||||
{
|
||||
upActive = false;
|
||||
DEBUG_PRINTLN("UPN");
|
||||
}
|
||||
} else if (upActiveBefore)
|
||||
{
|
||||
pinMode(PIN_UP_RELAY, INPUT);
|
||||
upActiveBefore = false;
|
||||
}
|
||||
|
||||
if (downActive)
|
||||
{
|
||||
if(!downActiveBefore)
|
||||
{
|
||||
pinMode(PIN_DN_RELAY, OUTPUT);
|
||||
digitalWrite(PIN_DN_RELAY, LOW);
|
||||
downActiveBefore = true;
|
||||
downStartTime = millis();
|
||||
}
|
||||
if (millis()- downStartTime > PIN_ON_TIME)
|
||||
{
|
||||
downActive = false;
|
||||
}
|
||||
} else if (downActiveBefore)
|
||||
{
|
||||
pinMode(PIN_DN_RELAY, INPUT);
|
||||
downActiveBefore = false;
|
||||
}
|
||||
}
|
||||
|
||||
//loop. You can use "if (WLED_CONNECTED)" to check for successful connection
|
||||
void userLoop()
|
||||
{
|
||||
handleRelay();
|
||||
}
|
||||
69
usermods/Si7021_MQTT_HA/readme.md
Normal file
69
usermods/Si7021_MQTT_HA/readme.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# Si7021 to MQTT (with Home Assistant Auto Discovery) usermod
|
||||
|
||||
This usermod implements support for [Si7021 I²C temperature and humidity sensors](https://www.silabs.com/documents/public/data-sheets/Si7021-A20.pdf).
|
||||
|
||||
The sensor data will *not* be shown on the WLED UI (so far) but published via MQTT to WLED's "build in" MQTT device topic.
|
||||
|
||||
```
|
||||
temperature: $mqttDeviceTopic/si7021_temperature
|
||||
humidity: $mqttDeviceTopic/si7021_humidity
|
||||
```
|
||||
|
||||
Additionally the following sensors can be published:
|
||||
|
||||
```
|
||||
heat_index: $mqttDeviceTopic/si7021_heat_index
|
||||
dew_point: $mqttDeviceTopic/si7021_dew_point
|
||||
absolute_humidity: $mqttDeviceTopic/si7021_absolute_humidity
|
||||
```
|
||||
|
||||
Sensor data will be updated/send every 60 seconds.
|
||||
|
||||
This usermod also supports Home Assistant Auto Discovery.
|
||||
|
||||
## Settings via Usermod Setup
|
||||
|
||||
- `enabled`: Enables this usermod
|
||||
- `Send Dew Point, Abs. Humidity and Heat Index`: Enables additional sensors
|
||||
- `Home Assistant MQTT Auto-Discovery`: Enables Home Assistant Auto Discovery
|
||||
|
||||
# Installation
|
||||
|
||||
## Hardware
|
||||
|
||||
Attach the Si7021 sensor to the I²C interface.
|
||||
|
||||
Default PINs ESP32:
|
||||
|
||||
```
|
||||
SCL_PIN = 22;
|
||||
SDA_PIN = 21;
|
||||
```
|
||||
|
||||
Default PINs ESP8266:
|
||||
|
||||
```
|
||||
SCL_PIN = 5;
|
||||
SDA_PIN = 4;
|
||||
```
|
||||
|
||||
## Software
|
||||
|
||||
Add to `build_flags` in platformio.ini:
|
||||
|
||||
```
|
||||
-D USERMOD_SI7021_MQTT_HA
|
||||
```
|
||||
|
||||
Add to `lib_deps` in platformio.ini:
|
||||
|
||||
```
|
||||
adafruit/Adafruit Si7021 Library @ 1.4.0
|
||||
BME280@~3.0.0
|
||||
```
|
||||
|
||||
# Credits
|
||||
|
||||
- Aircoookie for making WLED
|
||||
- Other usermod creators for example code (`sensors_to_mqtt` and `multi_relay` especially)
|
||||
- You, for reading this
|
||||
236
usermods/Si7021_MQTT_HA/usermod_si7021_mqtt_ha.h
Normal file
236
usermods/Si7021_MQTT_HA/usermod_si7021_mqtt_ha.h
Normal file
@@ -0,0 +1,236 @@
|
||||
#pragma once
|
||||
|
||||
// this is remixed from usermod_v2_SensorsToMqtt.h (sensors_to_mqtt usermod)
|
||||
// and usermod_multi_relay.h (multi_relay usermod)
|
||||
|
||||
#include "wled.h"
|
||||
#include <Adafruit_Si7021.h>
|
||||
#include <EnvironmentCalculations.h> // EnvironmentCalculations::HeatIndex(), ::DewPoint(), ::AbsoluteHumidity()
|
||||
|
||||
Adafruit_Si7021 si7021;
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32 //ESP32 boards
|
||||
uint8_t SCL_PIN = 22;
|
||||
uint8_t SDA_PIN = 21;
|
||||
#else //ESP8266 boards
|
||||
uint8_t SCL_PIN = 5;
|
||||
uint8_t SDA_PIN = 4;
|
||||
#endif
|
||||
|
||||
class Si7021_MQTT_HA : public Usermod
|
||||
{
|
||||
private:
|
||||
bool sensorInitialized = false;
|
||||
bool mqttInitialized = false;
|
||||
float sensorTemperature = 0;
|
||||
float sensorHumidity = 0;
|
||||
float sensorHeatIndex = 0;
|
||||
float sensorDewPoint = 0;
|
||||
float sensorAbsoluteHumidity= 0;
|
||||
String mqttTemperatureTopic = "";
|
||||
String mqttHumidityTopic = "";
|
||||
String mqttHeatIndexTopic = "";
|
||||
String mqttDewPointTopic = "";
|
||||
String mqttAbsoluteHumidityTopic = "";
|
||||
unsigned long nextMeasure = 0;
|
||||
bool enabled = false;
|
||||
bool haAutoDiscovery = true;
|
||||
bool sendAdditionalSensors = true;
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
static const char _sendAdditionalSensors[];
|
||||
static const char _haAutoDiscovery[];
|
||||
|
||||
void _initializeSensor()
|
||||
{
|
||||
sensorInitialized = si7021.begin();
|
||||
Serial.printf("Si7021_MQTT_HA: sensorInitialized = %d\n", sensorInitialized);
|
||||
}
|
||||
|
||||
void _initializeMqtt()
|
||||
{
|
||||
mqttTemperatureTopic = String(mqttDeviceTopic) + "/si7021_temperature";
|
||||
mqttHumidityTopic = String(mqttDeviceTopic) + "/si7021_humidity";
|
||||
mqttHeatIndexTopic = String(mqttDeviceTopic) + "/si7021_heat_index";
|
||||
mqttDewPointTopic = String(mqttDeviceTopic) + "/si7021_dew_point";
|
||||
mqttAbsoluteHumidityTopic = String(mqttDeviceTopic) + "/si7021_absolute_humidity";
|
||||
|
||||
// Update and publish sensor data
|
||||
_updateSensorData();
|
||||
_publishSensorData();
|
||||
|
||||
if (haAutoDiscovery) {
|
||||
_publishHAMqttSensor("temperature", "Temperature", mqttTemperatureTopic, "temperature", "°C");
|
||||
_publishHAMqttSensor("humidity", "Humidity", mqttHumidityTopic, "humidity", "%");
|
||||
if (sendAdditionalSensors) {
|
||||
_publishHAMqttSensor("heat_index", "Heat Index", mqttHeatIndexTopic, "temperature", "°C");
|
||||
_publishHAMqttSensor("dew_point", "Dew Point", mqttDewPointTopic, "", "°C");
|
||||
_publishHAMqttSensor("absolute_humidity", "Absolute Humidity", mqttAbsoluteHumidityTopic, "", "g/m³");
|
||||
}
|
||||
}
|
||||
|
||||
mqttInitialized = true;
|
||||
}
|
||||
|
||||
void _publishHAMqttSensor(
|
||||
const String &name,
|
||||
const String &friendly_name,
|
||||
const String &state_topic,
|
||||
const String &deviceClass,
|
||||
const String &unitOfMeasurement)
|
||||
{
|
||||
if (WLED_MQTT_CONNECTED) {
|
||||
String topic = String("homeassistant/sensor/") + mqttClientID + "/" + name + "/config";
|
||||
|
||||
StaticJsonDocument<300> doc;
|
||||
|
||||
doc["name"] = String(serverDescription) + " " + friendly_name;
|
||||
doc["state_topic"] = state_topic;
|
||||
doc["unique_id"] = String(mqttClientID) + name;
|
||||
if (unitOfMeasurement != "")
|
||||
doc["unit_of_measurement"] = unitOfMeasurement;
|
||||
if (deviceClass != "")
|
||||
doc["device_class"] = deviceClass;
|
||||
doc["expire_after"] = 1800;
|
||||
|
||||
JsonObject device = doc.createNestedObject("device"); // attach the sensor to the same device
|
||||
device["name"] = String(serverDescription);
|
||||
device["model"] = "WLED";
|
||||
device["manufacturer"] = "Aircoookie";
|
||||
device["identifiers"] = String("wled-") + String(serverDescription);
|
||||
device["sw_version"] = VERSION;
|
||||
|
||||
String payload;
|
||||
serializeJson(doc, payload);
|
||||
|
||||
mqtt->publish(topic.c_str(), 0, true, payload.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void _updateSensorData()
|
||||
{
|
||||
sensorTemperature = si7021.readTemperature();
|
||||
sensorHumidity = si7021.readHumidity();
|
||||
|
||||
// Serial.print("Si7021_MQTT_HA: Temperature: ");
|
||||
// Serial.print(sensorTemperature, 2);
|
||||
// Serial.print("\tHumidity: ");
|
||||
// Serial.print(sensorHumidity, 2);
|
||||
|
||||
if (sendAdditionalSensors) {
|
||||
EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Celsius);
|
||||
sensorHeatIndex = EnvironmentCalculations::HeatIndex(sensorTemperature, sensorHumidity, envTempUnit);
|
||||
sensorDewPoint = EnvironmentCalculations::DewPoint(sensorTemperature, sensorHumidity, envTempUnit);
|
||||
sensorAbsoluteHumidity = EnvironmentCalculations::AbsoluteHumidity(sensorTemperature, sensorHumidity, envTempUnit);
|
||||
|
||||
// Serial.print("\tHeat Index: ");
|
||||
// Serial.print(sensorHeatIndex, 2);
|
||||
// Serial.print("\tDew Point: ");
|
||||
// Serial.print(sensorDewPoint, 2);
|
||||
// Serial.print("\tAbsolute Humidity: ");
|
||||
// Serial.println(sensorAbsoluteHumidity, 2);
|
||||
}
|
||||
// else
|
||||
// Serial.println("");
|
||||
}
|
||||
|
||||
void _publishSensorData()
|
||||
{
|
||||
if (WLED_MQTT_CONNECTED) {
|
||||
mqtt->publish(mqttTemperatureTopic.c_str(), 0, false, String(sensorTemperature).c_str());
|
||||
mqtt->publish(mqttHumidityTopic.c_str(), 0, false, String(sensorHumidity).c_str());
|
||||
if (sendAdditionalSensors) {
|
||||
mqtt->publish(mqttHeatIndexTopic.c_str(), 0, false, String(sensorHeatIndex).c_str());
|
||||
mqtt->publish(mqttDewPointTopic.c_str(), 0, false, String(sensorDewPoint).c_str());
|
||||
mqtt->publish(mqttAbsoluteHumidityTopic.c_str(), 0, false, String(sensorAbsoluteHumidity).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void addToConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name));
|
||||
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[FPSTR(_sendAdditionalSensors)] = sendAdditionalSensors;
|
||||
top[FPSTR(_haAutoDiscovery)] = haAutoDiscovery;
|
||||
}
|
||||
|
||||
bool readFromConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
|
||||
bool configComplete = !top.isNull();
|
||||
configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled);
|
||||
configComplete &= getJsonValue(top[FPSTR(_sendAdditionalSensors)], sendAdditionalSensors);
|
||||
configComplete &= getJsonValue(top[FPSTR(_haAutoDiscovery)], haAutoDiscovery);
|
||||
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
void onMqttConnect(bool sessionPresent) {
|
||||
if (mqttDeviceTopic[0] != 0)
|
||||
_initializeMqtt();
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
if (enabled) {
|
||||
Serial.println("Si7021_MQTT_HA: Starting!");
|
||||
Wire.begin(SDA_PIN, SCL_PIN);
|
||||
Serial.println("Si7021_MQTT_HA: Initializing sensors.. ");
|
||||
_initializeSensor();
|
||||
}
|
||||
}
|
||||
|
||||
// gets called every time WiFi is (re-)connected.
|
||||
void connected()
|
||||
{
|
||||
nextMeasure = millis() + 5000; // Schedule next measure in 5 seconds
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
yield();
|
||||
if (!enabled || strip.isUpdating()) return; // !sensorFound ||
|
||||
|
||||
unsigned long tempTimer = millis();
|
||||
|
||||
if (tempTimer > nextMeasure) {
|
||||
nextMeasure = tempTimer + 60000; // Schedule next measure in 60 seconds
|
||||
|
||||
if (!sensorInitialized) {
|
||||
Serial.println("Si7021_MQTT_HA: Error! Sensors not initialized in loop()!");
|
||||
_initializeSensor();
|
||||
return; // lets try again next loop
|
||||
}
|
||||
|
||||
if (WLED_MQTT_CONNECTED) {
|
||||
if (!mqttInitialized)
|
||||
_initializeMqtt();
|
||||
|
||||
// Update and publish sensor data
|
||||
_updateSensorData();
|
||||
_publishSensorData();
|
||||
}
|
||||
else {
|
||||
Serial.println("Si7021_MQTT_HA: Missing MQTT connection. Not publishing data");
|
||||
mqttInitialized = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_SI7021_MQTT_HA;
|
||||
}
|
||||
};
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char Si7021_MQTT_HA::_name[] PROGMEM = "Si7021 MQTT (Home Assistant)";
|
||||
const char Si7021_MQTT_HA::_enabled[] PROGMEM = "enabled";
|
||||
const char Si7021_MQTT_HA::_sendAdditionalSensors[] PROGMEM = "Send Dew Point, Abs. Humidity and Heat Index";
|
||||
const char Si7021_MQTT_HA::_haAutoDiscovery[] PROGMEM = "Home Assistant MQTT Auto-Discovery";
|
||||
@@ -81,11 +81,13 @@ void registerUsermods()
|
||||
Usermod can be configured in Usermods settings page.
|
||||
|
||||
* `enabled` - enable/disable usermod
|
||||
* `pin` - GPIO pin where relay is attached to ESP
|
||||
* `pin` - GPIO pin where relay is attached to ESP (can be configured at compile time `-D MULTI_RELAY_PINS=xx,xx,...`)
|
||||
* `delay-s` - delay in seconds after on/off command is received
|
||||
* `active-high` - toggle high/low activation of relay (can be used to reverse relay states)
|
||||
* `external` - if enabled WLED does not control relay, it can only be triggered by external command (MQTT, HTTP, JSON or button)
|
||||
* `button` - button (from LED Settings) that controls this relay
|
||||
* `broadcast`- time in seconds between state broadcasts using MQTT
|
||||
* `HA-discovery`- enable Home Assistant auto discovery
|
||||
|
||||
If there is no MultiRelay section, just save current configuration and re-open Usermods settings page.
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
#define MULTI_RELAY_MAX_RELAYS 4
|
||||
#endif
|
||||
|
||||
#ifndef MULTI_RELAY_PINS
|
||||
#define MULTI_RELAY_PINS -1
|
||||
#endif
|
||||
|
||||
#define WLED_DEBOUNCE_THRESHOLD 50 //only consider button input of at least 50ms as valid (debouncing)
|
||||
|
||||
#define ON true
|
||||
@@ -177,8 +181,9 @@ class MultiRelay : public Usermod {
|
||||
* constructor
|
||||
*/
|
||||
MultiRelay() {
|
||||
const int8_t defPins[] = {MULTI_RELAY_PINS};
|
||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||
_relay[i].pin = -1;
|
||||
_relay[i].pin = i<sizeof(defPins) ? defPins[i] : -1;
|
||||
_relay[i].delay = 0;
|
||||
_relay[i].mode = false;
|
||||
_relay[i].active = false;
|
||||
@@ -619,7 +624,7 @@ class MultiRelay : public Usermod {
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[FPSTR(_broadcast)].isNull();
|
||||
return !top[FPSTR(_HAautodiscovery)].isNull();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -64,7 +64,7 @@ class AutoSaveUsermod : public Usermod {
|
||||
PSTR("~ %02d-%02d %02d:%02d:%02d ~"),
|
||||
month(localTime), day(localTime),
|
||||
hour(localTime), minute(localTime), second(localTime));
|
||||
savePreset(autoSavePreset, true, presetNameBuffer);
|
||||
savePreset(autoSavePreset, presetNameBuffer);
|
||||
}
|
||||
|
||||
void inline displayOverlay() {
|
||||
|
||||
26
usermods/usermod_v2_word_clock/readme.md
Normal file
26
usermods/usermod_v2_word_clock/readme.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Word Clock Usermod V2
|
||||
|
||||
This usermod can be used to drive a wordclock with a 11x10 pixel matrix with WLED. There are also 4 additional dots for the minutes.
|
||||
The visualisation is desribed in 4 mask with LED numbers (single dots for minutes, minutes, hours and "clock/Uhr").
|
||||
There are 2 parameters to chnage the behaviour:
|
||||
|
||||
active: enable/disable usermod
|
||||
diplayItIs: enable/disable display of "Es ist" on the clock.
|
||||
|
||||
## Installation
|
||||
|
||||
Copy and update the example `platformio_override.ini.sample`
|
||||
from the Rotary Encoder UI usermode folder to the root directory of your particular build.
|
||||
This file should be placed in the same directory as `platformio.ini`.
|
||||
|
||||
### Define Your Options
|
||||
|
||||
* `USERMOD_WORDCLOCK` - define this to have this the Auto Save usermod included wled00\usermods_list.cpp
|
||||
|
||||
### PlatformIO requirements
|
||||
|
||||
No special requirements.
|
||||
|
||||
## Change Log
|
||||
|
||||
2022/03/30 initial commit
|
||||
427
usermods/usermod_v2_word_clock/usermod_v2_word_clock.h
Normal file
427
usermods/usermod_v2_word_clock/usermod_v2_word_clock.h
Normal file
@@ -0,0 +1,427 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
/*
|
||||
* Usermods allow you to add own functionality to WLED more easily
|
||||
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
|
||||
*
|
||||
* This usermod can be used to drive a wordclock with a 11x10 pixel matrix with WLED. There are also 4 additional dots for the minutes.
|
||||
* The visualisation is desribed in 4 mask with LED numbers (single dots for minutes, minutes, hours and "clock/Uhr").
|
||||
* There are 2 parameters to chnage the behaviour:
|
||||
*
|
||||
* active: enable/disable usermod
|
||||
* diplayItIs: enable/disable display of "Es ist" on the clock.
|
||||
*/
|
||||
|
||||
class WordClockUsermod : public Usermod
|
||||
{
|
||||
private:
|
||||
unsigned long lastTime = 0;
|
||||
int lastTimeMinutes = -1;
|
||||
|
||||
// set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer)
|
||||
bool usermodActive = false;
|
||||
bool displayItIs = false;
|
||||
|
||||
// defines for mask sizes
|
||||
#define maskSizeLeds 114
|
||||
#define maskSizeMinutes 12
|
||||
#define maskSizeHours 6
|
||||
#define maskSizeItIs 5
|
||||
#define maskSizeMinuteDots 4
|
||||
|
||||
// "minute" masks
|
||||
const int maskMinutes[12][maskSizeMinutes] =
|
||||
{
|
||||
{107, 108, 109, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // :00
|
||||
{ 7, 8, 9, 10, 40, 41, 42, 43, -1, -1, -1, -1}, // :05 fünf nach
|
||||
{ 11, 12, 13, 14, 40, 41, 42, 43, -1, -1, -1, -1}, // :10 zehn nach
|
||||
{ 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1}, // :15 viertel
|
||||
{ 15, 16, 17, 18, 19, 20, 21, 40, 41, 42, 43, -1}, // :20 zwanzig nach
|
||||
{ 7, 8, 9, 10, 33, 34, 35, 44, 45, 46, 47, -1}, // :25 fünf vor halb
|
||||
{ 44, 45, 46, 47, -1, -1, -1, -1, -1, -1, -1, -1}, // :30 halb
|
||||
{ 7, 8, 9, 10, 40, 41, 42, 43, 44, 45, 46, 47}, // :35 fünf nach halb
|
||||
{ 15, 16, 17, 18, 19, 20, 21, 33, 34, 35, -1, -1}, // :40 zwanzig vor
|
||||
{ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1}, // :45 dreiviertel
|
||||
{ 11, 12, 13, 14, 33, 34, 35, -1, -1, -1, -1, -1}, // :50 zehn vor
|
||||
{ 7, 8, 9, 10, 33, 34, 35, -1, -1, -1, -1, -1} // :55 fünf vor
|
||||
};
|
||||
|
||||
// hour masks
|
||||
const int maskHours[13][maskSizeHours] =
|
||||
{
|
||||
{ 55, 56, 57, -1, -1, -1}, // 01: ein
|
||||
{ 55, 56, 57, 58, -1, -1}, // 01: eins
|
||||
{ 62, 63, 64, 65, -1, -1}, // 02: zwei
|
||||
{ 66, 67, 68, 69, -1, -1}, // 03: drei
|
||||
{ 73, 74, 75, 76, -1, -1}, // 04: vier
|
||||
{ 51, 52, 53, 54, -1, -1}, // 05: fünf
|
||||
{ 77, 78, 79, 80, 81, -1}, // 06: sechs
|
||||
{ 88, 89, 90, 91, 92, 93}, // 07: sieben
|
||||
{ 84, 85, 86, 87, -1, -1}, // 08: acht
|
||||
{102, 103, 104, 105, -1, -1}, // 09: neun
|
||||
{ 99, 100, 101, 102, -1, -1}, // 10: zehn
|
||||
{ 49, 50, 51, -1, -1, -1}, // 11: elf
|
||||
{ 94, 95, 96, 97, 98, -1} // 12: zwölf and 00: null
|
||||
};
|
||||
|
||||
// mask "it is"
|
||||
const int maskItIs[maskSizeItIs] = {0, 1, 3, 4, 5};
|
||||
|
||||
// mask minute dots
|
||||
const int maskMinuteDots[maskSizeMinuteDots] = {110, 111, 112, 113};
|
||||
|
||||
// overall mask to define which LEDs are on
|
||||
int maskLedsOn[maskSizeLeds] =
|
||||
{
|
||||
0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0
|
||||
};
|
||||
|
||||
// update led mask
|
||||
void updateLedMask(const int wordMask[], int arraySize)
|
||||
{
|
||||
// loop over array
|
||||
for (int x=0; x < arraySize; x++)
|
||||
{
|
||||
// check if mask has a valid LED number
|
||||
if (wordMask[x] >= 0 && wordMask[x] < maskSizeLeds)
|
||||
{
|
||||
// turn LED on
|
||||
maskLedsOn[wordMask[x]] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set hours
|
||||
void setHours(int hours, bool fullClock)
|
||||
{
|
||||
int index = hours;
|
||||
|
||||
// handle 00:xx as 12:xx
|
||||
if (hours == 0)
|
||||
{
|
||||
index = 12;
|
||||
}
|
||||
|
||||
// check if we get an overrun of 12 o´clock
|
||||
if (hours == 13)
|
||||
{
|
||||
index = 1;
|
||||
}
|
||||
|
||||
// special handling for "ein Uhr" instead of "eins Uhr"
|
||||
if (hours == 1 && fullClock == true)
|
||||
{
|
||||
index = 0;
|
||||
}
|
||||
|
||||
// update led mask
|
||||
updateLedMask(maskHours[index], maskSizeHours);
|
||||
}
|
||||
|
||||
// set minutes
|
||||
void setMinutes(int index)
|
||||
{
|
||||
// update led mask
|
||||
updateLedMask(maskMinutes[index], maskSizeMinutes);
|
||||
}
|
||||
|
||||
// set minutes dot
|
||||
void setSingleMinuteDots(int minutes)
|
||||
{
|
||||
// modulo to get minute dots
|
||||
int minutesDotCount = minutes % 5;
|
||||
|
||||
// check if minute dots are active
|
||||
if (minutesDotCount > 0)
|
||||
{
|
||||
// activate all minute dots until number is reached
|
||||
for (int i = 0; i < minutesDotCount; i++)
|
||||
{
|
||||
// activate LED
|
||||
maskLedsOn[maskMinuteDots[i]] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update the display
|
||||
void updateDisplay(uint8_t hours, uint8_t minutes)
|
||||
{
|
||||
// disable complete matrix at the bigging
|
||||
for (int x = 0; x < maskSizeLeds; x++)
|
||||
{
|
||||
maskLedsOn[x] = 0;
|
||||
}
|
||||
|
||||
// display it is/es ist if activated
|
||||
if (displayItIs)
|
||||
{
|
||||
updateLedMask(maskItIs, maskSizeItIs);
|
||||
}
|
||||
|
||||
// set single minute dots
|
||||
setSingleMinuteDots(minutes);
|
||||
|
||||
// switch minutes
|
||||
switch (minutes / 5)
|
||||
{
|
||||
case 0:
|
||||
// full hour
|
||||
setMinutes(0);
|
||||
setHours(hours, true);
|
||||
break;
|
||||
case 1:
|
||||
// 5 nach
|
||||
setMinutes(1);
|
||||
setHours(hours, false);
|
||||
break;
|
||||
case 2:
|
||||
// 10 nach
|
||||
setMinutes(2);
|
||||
setHours(hours, false);
|
||||
break;
|
||||
case 3:
|
||||
// viertel
|
||||
setMinutes(3);
|
||||
setHours(hours + 1, false);
|
||||
break;
|
||||
case 4:
|
||||
// 20 nach
|
||||
setMinutes(4);
|
||||
setHours(hours, false);
|
||||
break;
|
||||
case 5:
|
||||
// 5 vor halb
|
||||
setMinutes(5);
|
||||
setHours(hours + 1, false);
|
||||
break;
|
||||
case 6:
|
||||
// halb
|
||||
setMinutes(6);
|
||||
setHours(hours + 1, false);
|
||||
break;
|
||||
case 7:
|
||||
// 5 nach halb
|
||||
setMinutes(7);
|
||||
setHours(hours + 1, false);
|
||||
break;
|
||||
case 8:
|
||||
// 20 vor
|
||||
setMinutes(8);
|
||||
setHours(hours + 1, false);
|
||||
break;
|
||||
case 9:
|
||||
// viertel vor
|
||||
setMinutes(9);
|
||||
setHours(hours + 1, false);
|
||||
break;
|
||||
case 10:
|
||||
// 10 vor
|
||||
setMinutes(10);
|
||||
setHours(hours + 1, false);
|
||||
break;
|
||||
case 11:
|
||||
// 5 vor
|
||||
setMinutes(11);
|
||||
setHours(hours + 1, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* connected() is called every time the WiFi is (re)connected
|
||||
* Use it to initialize network interfaces
|
||||
*/
|
||||
void connected()
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* loop() is called continuously. Here you can check for events, read sensors, etc.
|
||||
*
|
||||
* Tips:
|
||||
* 1. You can use "if (WLED_CONNECTED)" to check for a successful network connection.
|
||||
* Additionally, "if (WLED_MQTT_CONNECTED)" is available to check for a connection to an MQTT broker.
|
||||
*
|
||||
* 2. Try to avoid using the delay() function. NEVER use delays longer than 10 milliseconds.
|
||||
* Instead, use a timer check as shown here.
|
||||
*/
|
||||
void loop() {
|
||||
|
||||
// do it every 5 seconds
|
||||
if (millis() - lastTime > 5000)
|
||||
{
|
||||
// check the time
|
||||
int minutes = minute(localTime);
|
||||
|
||||
// check if we already updated this minute
|
||||
if (lastTimeMinutes != minutes)
|
||||
{
|
||||
// update the display with new time
|
||||
updateDisplay(hourFormat12(localTime), minute(localTime));
|
||||
|
||||
// remember last update time
|
||||
lastTimeMinutes = minutes;
|
||||
}
|
||||
|
||||
// remember last update
|
||||
lastTime = millis();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
/*
|
||||
void addToJsonInfo(JsonObject& root)
|
||||
{
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* 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().
|
||||
*
|
||||
* CAUTION: serializeConfig() will initiate a filesystem write operation.
|
||||
* It might cause the LEDs to stutter and will cause flash wear if called too often.
|
||||
* Use it sparingly and always in the loop, never in network callbacks!
|
||||
*
|
||||
* addToConfig() will make your settings editable through the Usermod Settings page automatically.
|
||||
*
|
||||
* Usermod Settings Overview:
|
||||
* - Numeric values are treated as floats in the browser.
|
||||
* - If the numeric value entered into the browser contains a decimal point, it will be parsed as a C float
|
||||
* before being returned to the Usermod. The float data type has only 6-7 decimal digits of precision, and
|
||||
* doubles are not supported, numbers will be rounded to the nearest float value when being parsed.
|
||||
* The range accepted by the input field is +/- 1.175494351e-38 to +/- 3.402823466e+38.
|
||||
* - If the numeric value entered into the browser doesn't contain a decimal point, it will be parsed as a
|
||||
* C int32_t (range: -2147483648 to 2147483647) before being returned to the usermod.
|
||||
* Overflows or underflows are truncated to the max/min value for an int32_t, and again truncated to the type
|
||||
* used in the Usermod when reading the value from ArduinoJson.
|
||||
* - Pin values can be treated differently from an integer value by using the key name "pin"
|
||||
* - "pin" can contain a single or array of integer values
|
||||
* - On the Usermod Settings page there is simple checking for pin conflicts and warnings for special pins
|
||||
* - Red color indicates a conflict. Yellow color indicates a pin with a warning (e.g. an input-only pin)
|
||||
* - Tip: use int8_t to store the pin value in the Usermod, so a -1 value (pin not set) can be used
|
||||
*
|
||||
* See usermod_v2_auto_save.h for an example that saves Flash space by reusing ArduinoJson key name strings
|
||||
*
|
||||
* If you need a dedicated settings page with custom layout for your Usermod, that takes a lot more work.
|
||||
* You will have to add the setting to the HTML, xml.cpp and set.cpp manually.
|
||||
* See the WLED Soundreactive fork (code and wiki) for reference. https://github.com/atuline/WLED
|
||||
*
|
||||
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
|
||||
*/
|
||||
void addToConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject("WordClockUsermod");
|
||||
top["active"] = usermodActive;
|
||||
top["displayItIs"] = displayItIs;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
*
|
||||
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
|
||||
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
|
||||
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
|
||||
*
|
||||
* Return true in case the config values returned from Usermod Settings were complete, or false if you'd like WLED to save your defaults to disk (so any missing values are editable in Usermod Settings)
|
||||
*
|
||||
* getJsonValue() returns false if the value is missing, or copies the value into the variable provided and returns true if the value is present
|
||||
* The configComplete variable is true only if the "exampleUsermod" object and all values are present. If any values are missing, WLED will know to call addToConfig() to save them
|
||||
*
|
||||
* This function is guaranteed to be called on boot, but could also be called every time settings are updated
|
||||
*/
|
||||
bool readFromConfig(JsonObject& root)
|
||||
{
|
||||
// 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["WordClockUsermod"];
|
||||
|
||||
bool configComplete = !top.isNull();
|
||||
|
||||
configComplete &= getJsonValue(top["active"], usermodActive);
|
||||
configComplete &= getJsonValue(top["displayItIs"], displayItIs);
|
||||
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
/*
|
||||
* handleOverlayDraw() is called just before every show() (LED strip update frame) after effects have set the colors.
|
||||
* Use this to blank out some LEDs or set them to a different color regardless of the set effect mode.
|
||||
* Commonly used for custom clocks (Cronixie, 7 segment)
|
||||
*/
|
||||
void handleOverlayDraw()
|
||||
{
|
||||
// check if usermod is active
|
||||
if (usermodActive == true)
|
||||
{
|
||||
// loop over all leds
|
||||
for (int x = 0; x < maskSizeLeds; x++)
|
||||
{
|
||||
// check mask
|
||||
if (maskLedsOn[x] == 0)
|
||||
{
|
||||
// set pixel off
|
||||
strip.setPixelColor(x, RGBW32(0,0,0,0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_WORDCLOCK;
|
||||
}
|
||||
|
||||
//More methods can be added in the future, this example will then be extended.
|
||||
//Your usermod will remain compatible as it does not need to implement all methods from the Usermod base class!
|
||||
};
|
||||
35
usermods/wizlights/readme.md
Normal file
35
usermods/wizlights/readme.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Controlling Wiz lights
|
||||
|
||||
This usermod allows the control of [WiZ](https://www.wizconnected.com/en/consumer/) lights that are in the same network as the WLED controller.
|
||||
|
||||
The mod takes the colors from the first few pixels and sends them to the lights.
|
||||
|
||||
## Configuration
|
||||
|
||||
- Interval (ms)
|
||||
- How frequently to update the WiZ lights, in milliseconds.
|
||||
- Setting too low may causse ESP to become unresponsive.
|
||||
- Send Delay (ms)
|
||||
- An optional millisecond delay after updating each WiZ light.
|
||||
- Can help smooth out effects when using a larger number of WiZ lights
|
||||
- Use Enhanced White
|
||||
- Enables using the WiZ lights onboard white LEDs instead of sending maximum RGB values.
|
||||
- Tunable with warm and cool LEDs as supported by WiZ bulbs
|
||||
- Note: Only sent when max RGB value is set, need to have automatic brightness limiter disabled
|
||||
- ToDo: Have better logic for white value mixing to better take advantage of the lights capabilities
|
||||
- Always Force Update
|
||||
- Can be enabled to always send update message to light, even when color matches what was previously sent.
|
||||
- Force update every x minutes
|
||||
- Configuration option to allow adjusting the default force update timeout of 5 minutes.
|
||||
- Setting to 0 has the same impact as enabling Always Force Update
|
||||
-
|
||||
Then enter the IPs for the lights to be controlled, in order. There is currently a limit of 15 devices that can be controled, but that number
|
||||
can be easily changed by updating _MAX_WIZ_LIGHTS_.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related project
|
||||
|
||||
If you use these lights and python, make sure to check out the [pywizlight](https://github.com/sbidy/pywizlight) project. I learned how to
|
||||
format the messages to control the lights from that project.
|
||||
154
usermods/wizlights/wizlights.h
Normal file
154
usermods/wizlights/wizlights.h
Normal file
@@ -0,0 +1,154 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
// Maximum number of lights supported
|
||||
#define MAX_WIZ_LIGHTS 15
|
||||
|
||||
WiFiUDP UDP;
|
||||
|
||||
|
||||
|
||||
|
||||
class WizLightsUsermod : public Usermod {
|
||||
|
||||
private:
|
||||
unsigned long lastTime = 0;
|
||||
long updateInterval;
|
||||
long sendDelay;
|
||||
|
||||
long forceUpdateMinutes;
|
||||
bool forceUpdate;
|
||||
|
||||
bool useEnhancedWhite;
|
||||
long warmWhite;
|
||||
long coldWhite;
|
||||
|
||||
IPAddress lightsIP[MAX_WIZ_LIGHTS]; // Stores Light IP addresses
|
||||
bool lightsValid[MAX_WIZ_LIGHTS]; // Stores Light IP address validity
|
||||
uint32_t colorsSent[MAX_WIZ_LIGHTS]; // Stores last color sent for each light
|
||||
|
||||
|
||||
|
||||
public:
|
||||
|
||||
|
||||
|
||||
// Send JSON blob to WiZ Light over UDP
|
||||
// RGB or C/W white
|
||||
// TODO:
|
||||
// Better utilize WLED existing white mixing logic
|
||||
void wizSendColor(IPAddress ip, uint32_t color) {
|
||||
UDP.beginPacket(ip, 38899);
|
||||
|
||||
// If no LED color, turn light off. Note wiz light setting for "Off fade-out" will be applied by the light itself.
|
||||
if (color == 0) {
|
||||
UDP.print("{\"method\":\"setPilot\",\"params\":{\"state\":false}}");
|
||||
|
||||
// If color is WHITE, try and use the lights WHITE LEDs instead of mixing RGB LEDs
|
||||
} else if (color == 16777215 && useEnhancedWhite){
|
||||
|
||||
// set cold white light only
|
||||
if (coldWhite > 0 && warmWhite == 0){
|
||||
UDP.print("{\"method\":\"setPilot\",\"params\":{\"c\":"); UDP.print(coldWhite) ;UDP.print("}}");}
|
||||
|
||||
// set warm white light only
|
||||
if (warmWhite > 0 && coldWhite == 0){
|
||||
UDP.print("{\"method\":\"setPilot\",\"params\":{\"w\":"); UDP.print(warmWhite) ;UDP.print("}}");}
|
||||
|
||||
// set combination of warm and cold white light
|
||||
if (coldWhite > 0 && warmWhite > 0){
|
||||
UDP.print("{\"method\":\"setPilot\",\"params\":{\"c\":"); UDP.print(coldWhite) ;UDP.print(",\"w\":"); UDP.print(warmWhite); UDP.print("}}");}
|
||||
|
||||
// Send color as RGB
|
||||
} else {
|
||||
UDP.print("{\"method\":\"setPilot\",\"params\":{\"r\":");
|
||||
UDP.print(R(color));
|
||||
UDP.print(",\"g\":");
|
||||
UDP.print(G(color));
|
||||
UDP.print(",\"b\":");
|
||||
UDP.print(B(color));
|
||||
UDP.print("}}");
|
||||
}
|
||||
|
||||
UDP.endPacket();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO: Check millis() rollover
|
||||
void loop() {
|
||||
|
||||
// Make sure we are connected first
|
||||
if (!WLED_CONNECTED) return;
|
||||
|
||||
unsigned long ellapsedTime = millis() - lastTime;
|
||||
if (ellapsedTime > updateInterval) {
|
||||
bool update = false;
|
||||
for (uint8_t i = 0; i < MAX_WIZ_LIGHTS; i++) {
|
||||
if (!lightsValid[i]) { continue; }
|
||||
uint32_t newColor = strip.getPixelColor(i);
|
||||
if (forceUpdate || (newColor != colorsSent[i]) || (ellapsedTime > forceUpdateMinutes*60000)){
|
||||
wizSendColor(lightsIP[i], newColor);
|
||||
colorsSent[i] = newColor;
|
||||
update = true;
|
||||
delay(sendDelay);
|
||||
}
|
||||
}
|
||||
if (update) lastTime = millis();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void addToConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject("wizLightsUsermod");
|
||||
top["Interval (ms)"] = updateInterval;
|
||||
top["Send Delay (ms)"] = sendDelay;
|
||||
top["Use Enhanced White *"] = useEnhancedWhite;
|
||||
top["* Warm White Value (0-255)"] = warmWhite;
|
||||
top["* Cold White Value (0-255)"] = coldWhite;
|
||||
top["Always Force Update"] = forceUpdate;
|
||||
top["Force Update Every x Minutes"] = forceUpdateMinutes;
|
||||
|
||||
for (uint8_t i = 0; i < MAX_WIZ_LIGHTS; i++) {
|
||||
top[getJsonLabel(i)] = lightsIP[i].toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool readFromConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root["wizLightsUsermod"];
|
||||
bool configComplete = !top.isNull();
|
||||
|
||||
configComplete &= getJsonValue(top["Interval (ms)"], updateInterval, 1000); // How frequently to update the wiz lights
|
||||
configComplete &= getJsonValue(top["Send Delay (ms)"], sendDelay, 0); // Optional delay after sending each UDP message
|
||||
configComplete &= getJsonValue(top["Use Enhanced White *"], useEnhancedWhite, false); // When color is white use wiz white LEDs instead of mixing RGB
|
||||
configComplete &= getJsonValue(top["* Warm White Value (0-255)"], warmWhite, 0); // Warm White LED value for Enhanced White
|
||||
configComplete &= getJsonValue(top["* Cold White Value (0-255)"], coldWhite, 50); // Cold White LED value for Enhanced White
|
||||
configComplete &= getJsonValue(top["Always Force Update"], forceUpdate, false); // Update wiz light every loop, even if color value has not changed
|
||||
configComplete &= getJsonValue(top["Force Update Every x Minutes"], forceUpdateMinutes, 5); // Update wiz light if color value has not changed, every x minutes
|
||||
|
||||
// Read list of IPs
|
||||
String tempIp;
|
||||
for (uint8_t i = 0; i < MAX_WIZ_LIGHTS; i++) {
|
||||
configComplete &= getJsonValue(top[getJsonLabel(i)], tempIp, "0.0.0.0");
|
||||
lightsValid[i] = lightsIP[i].fromString(tempIp);
|
||||
|
||||
// If the IP is not valid, force the value to be empty
|
||||
if (!lightsValid[i]){lightsIP[i].fromString("0.0.0.0");}
|
||||
}
|
||||
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
|
||||
// Create label for the usermod page (I cannot make it work with JSON arrays...)
|
||||
String getJsonLabel(uint8_t i) {return "WiZ Light IP #" + String(i+1);}
|
||||
|
||||
uint16_t getId(){return USERMOD_ID_WIZLIGHTS;}
|
||||
};
|
||||
@@ -65,7 +65,7 @@ void hourChime()
|
||||
//strip.resetSegments();
|
||||
selectWordSegments(true);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
savePreset(13, false);
|
||||
//savePreset(255);
|
||||
selectWordSegments(false);
|
||||
//strip.getSegment(0).setOption(0, true);
|
||||
strip.getSegment(0).setOption(2, true);
|
||||
@@ -299,7 +299,7 @@ void userLoop()
|
||||
if (minute(localTime) == 1){
|
||||
//turn off background segment;
|
||||
strip.getSegment(0).setOption(2, false);
|
||||
//applyPreset(13);
|
||||
//applyPreset(255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2346,7 +2346,7 @@ uint16_t WS2812FX::mode_ripple_rainbow(void) {
|
||||
// incandescent bulbs change color as they get dim down.
|
||||
#define COOL_LIKE_INCANDESCENT 1
|
||||
|
||||
CRGB WS2812FX::twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat)
|
||||
CRGB IRAM_ATTR WS2812FX::twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat)
|
||||
{
|
||||
// Overall twinkle speed (changed)
|
||||
uint16_t ticks = ms / SEGENV.aux0;
|
||||
@@ -2784,16 +2784,9 @@ uint16_t WS2812FX::mode_popcorn(void) {
|
||||
if (numPopcorn == 0) numPopcorn = 1;
|
||||
|
||||
for(uint8_t i = 0; i < numPopcorn; i++) {
|
||||
bool isActive = popcorn[i].pos >= 0.0f;
|
||||
|
||||
if (isActive) { // if kernel is active, update its position
|
||||
if (popcorn[i].pos >= 0.0f) { // if kernel is active, update its position
|
||||
popcorn[i].pos += popcorn[i].vel;
|
||||
popcorn[i].vel += gravity;
|
||||
uint32_t col = color_wheel(popcorn[i].colIndex);
|
||||
if (!SEGMENT.palette && popcorn[i].colIndex < NUM_COLORS) col = SEGCOLOR(popcorn[i].colIndex);
|
||||
|
||||
uint16_t ledIndex = popcorn[i].pos;
|
||||
if (ledIndex < SEGLEN) setPixelColor(ledIndex, col);
|
||||
} else { // if kernel is inactive, randomly pop it
|
||||
if (random8() < 2) { // POP!!!
|
||||
popcorn[i].pos = 0.01f;
|
||||
@@ -2812,6 +2805,13 @@ uint16_t WS2812FX::mode_popcorn(void) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (popcorn[i].pos >= 0.0f) { // draw now active popcorn (either active before or just popped)
|
||||
uint32_t col = color_wheel(popcorn[i].colIndex);
|
||||
if (!SEGMENT.palette && popcorn[i].colIndex < NUM_COLORS) col = SEGCOLOR(popcorn[i].colIndex);
|
||||
|
||||
uint16_t ledIndex = popcorn[i].pos;
|
||||
if (ledIndex < SEGLEN) setPixelColor(ledIndex, col);
|
||||
}
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
|
||||
34
wled00/FX.h
34
wled00/FX.h
@@ -46,6 +46,11 @@
|
||||
#define MAX(a,b) ((a)>(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
//color mangling macros
|
||||
#ifndef RGBW32
|
||||
#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b))))
|
||||
#endif
|
||||
|
||||
/* Not used in all effects yet */
|
||||
#define WLED_FPS 42
|
||||
#define FRAMETIME_FIXED (1000/WLED_FPS)
|
||||
@@ -71,7 +76,6 @@
|
||||
assuming each segment uses the same amount of data. 256 for ESP8266, 640 for ESP32. */
|
||||
#define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / MAX_NUM_SEGMENTS)
|
||||
|
||||
#define LED_SKIP_AMOUNT 1
|
||||
#define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15)
|
||||
|
||||
#define NUM_COLORS 3 /* number of colors per segment */
|
||||
@@ -247,7 +251,7 @@ class WS2812FX {
|
||||
|
||||
// segment parameters
|
||||
public:
|
||||
typedef struct Segment { // 30 (32 in memory) bytes
|
||||
typedef struct Segment { // 31 (32 in memory) bytes
|
||||
uint16_t start;
|
||||
uint16_t stop; //segment invalid if stop == 0
|
||||
uint16_t offset;
|
||||
@@ -260,6 +264,7 @@ class WS2812FX {
|
||||
uint8_t opacity;
|
||||
uint32_t colors[NUM_COLORS];
|
||||
uint8_t cct; //0==1900K, 255==10091K
|
||||
uint8_t _capabilities;
|
||||
char *name;
|
||||
bool setColor(uint8_t slot, uint32_t c, uint8_t segn) { //returns true if changed
|
||||
if (slot >= NUM_COLORS || segn >= MAX_NUM_SEGMENTS) return false;
|
||||
@@ -335,7 +340,8 @@ class WS2812FX {
|
||||
return vLength;
|
||||
}
|
||||
uint8_t differs(Segment& b);
|
||||
uint8_t getLightCapabilities();
|
||||
inline uint8_t getLightCapabilities() {return _capabilities;}
|
||||
void refreshLightCapabilities();
|
||||
} segment;
|
||||
|
||||
// segment runtime parameters
|
||||
@@ -607,7 +613,7 @@ class WS2812FX {
|
||||
_brightness = DEFAULT_BRIGHTNESS;
|
||||
currentPalette = CRGBPalette16(CRGB::Black);
|
||||
targetPalette = CloudColors_p;
|
||||
ablMilliampsMax = 850;
|
||||
ablMilliampsMax = ABL_MILLIAMPS_DEFAULT;
|
||||
currentMilliamps = 0;
|
||||
timebase = 0;
|
||||
resetSegments();
|
||||
@@ -623,7 +629,7 @@ class WS2812FX {
|
||||
setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
|
||||
setColor(uint8_t slot, uint32_t c),
|
||||
setCCT(uint16_t k),
|
||||
setBrightness(uint8_t b),
|
||||
setBrightness(uint8_t b, bool direct = false),
|
||||
setRange(uint16_t i, uint16_t i2, uint32_t col),
|
||||
setShowCallback(show_callback cb),
|
||||
setTransition(uint16_t t),
|
||||
@@ -636,12 +642,13 @@ class WS2812FX {
|
||||
resetSegments(),
|
||||
makeAutoSegments(bool forceReset = false),
|
||||
fixInvalidSegments(),
|
||||
setPixelColor(uint16_t n, uint32_t c),
|
||||
setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
|
||||
show(void),
|
||||
setTargetFps(uint8_t fps),
|
||||
deserializeMap(uint8_t n=0);
|
||||
|
||||
inline void setPixelColor(uint16_t n, uint32_t c) {setPixelColor(n, byte(c>>16), byte(c>>8), byte(c), byte(c>>24));}
|
||||
|
||||
bool
|
||||
gammaCorrectBri = false,
|
||||
gammaCorrectCol = true,
|
||||
@@ -655,7 +662,8 @@ class WS2812FX {
|
||||
paletteFade = 0,
|
||||
paletteBlend = 0,
|
||||
milliampsPerLed = 55,
|
||||
cctBlending = 0,
|
||||
autoWhiteMode = RGBW_MODE_DUAL,
|
||||
cctBlending = 0,
|
||||
getBrightness(void),
|
||||
getModeCount(void),
|
||||
getPaletteCount(void),
|
||||
@@ -668,9 +676,13 @@ class WS2812FX {
|
||||
setPixelSegment(uint8_t n),
|
||||
gamma8(uint8_t),
|
||||
gamma8_cal(uint8_t, float),
|
||||
sin_gap(uint16_t),
|
||||
get_random_wheel_index(uint8_t);
|
||||
|
||||
inline uint8_t sin_gap(uint16_t in) {
|
||||
if (in & 0x100) return 0;
|
||||
return sin8(in + 192); // correct phase shift of sine so that it starts and stops at 0
|
||||
}
|
||||
|
||||
int8_t
|
||||
tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec);
|
||||
|
||||
@@ -887,14 +899,15 @@ class WS2812FX {
|
||||
|
||||
uint32_t _colors_t[3];
|
||||
uint8_t _bri_t;
|
||||
bool _no_rgb = false;
|
||||
|
||||
uint8_t _segment_index = 0;
|
||||
uint8_t _segment_index_palette_last = 99;
|
||||
uint8_t _mainSegment;
|
||||
|
||||
segment _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 24 bytes per element
|
||||
// start, stop, offset, speed, intensity, palette, mode, options, grouping, spacing, opacity (unused), color[]
|
||||
{0, 7, 0, DEFAULT_SPEED, 128, 0, DEFAULT_MODE, NO_OPTIONS, 1, 0, 255, {DEFAULT_COLOR}}
|
||||
// start, stop, offset, speed, intensity, palette, mode, options, grouping, spacing, opacity (unused), color[], capabilities
|
||||
{0, 7, 0, DEFAULT_SPEED, 128, 0, DEFAULT_MODE, NO_OPTIONS, 1, 0, 255, {DEFAULT_COLOR}, 0}
|
||||
};
|
||||
segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 28 bytes per element
|
||||
friend class Segment_runtime;
|
||||
@@ -903,7 +916,6 @@ class WS2812FX {
|
||||
friend class ColorTransition;
|
||||
|
||||
uint16_t
|
||||
realPixelIndex(uint16_t i),
|
||||
transitionProgress(uint8_t tNr);
|
||||
|
||||
public:
|
||||
|
||||
@@ -60,6 +60,11 @@
|
||||
#define DEFAULT_LED_TYPE TYPE_WS2812_RGB
|
||||
#endif
|
||||
|
||||
#ifndef DEFAULT_LED_COLOR_ORDER
|
||||
#define DEFAULT_LED_COLOR_ORDER COL_ORDER_GRB //default to GRB
|
||||
#endif
|
||||
|
||||
|
||||
#if MAX_NUM_SEGMENTS < WLED_MAX_BUSSES
|
||||
#error "Max segments must be at least max number of busses!"
|
||||
#endif
|
||||
@@ -77,6 +82,7 @@ void WS2812FX::finalizeInit(void)
|
||||
|
||||
//if busses failed to load, add default (fresh install, FS issue, ...)
|
||||
if (busses.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]));
|
||||
@@ -87,7 +93,7 @@ void WS2812FX::finalizeInit(void)
|
||||
uint16_t start = prevLen;
|
||||
uint16_t count = defCounts[(i < defNumCounts) ? i : defNumCounts -1];
|
||||
prevLen += count;
|
||||
BusConfig defCfg = BusConfig(DEFAULT_LED_TYPE, defPin, start, count, COL_ORDER_GRB);
|
||||
BusConfig defCfg = BusConfig(DEFAULT_LED_TYPE, defPin, start, count, DEFAULT_LED_COLOR_ORDER);
|
||||
busses.add(defCfg);
|
||||
}
|
||||
}
|
||||
@@ -125,6 +131,8 @@ void WS2812FX::service() {
|
||||
|
||||
for(uint8_t i=0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
//if (realtimeMode && useMainSegmentOnly && i == getMainSegmentId()) continue;
|
||||
|
||||
_segment_index = i;
|
||||
|
||||
// reset the segment runtime data if needed, called before isActive to ensure deleted
|
||||
@@ -133,7 +141,8 @@ void WS2812FX::service() {
|
||||
|
||||
if (!SEGMENT.isActive()) continue;
|
||||
|
||||
if(nowUp > SEGENV.next_time || _triggered || (doShow && SEGMENT.mode == 0)) //last is temporary
|
||||
// last condition ensures all solid segments are updated at the same time
|
||||
if(nowUp > SEGENV.next_time || _triggered || (doShow && SEGMENT.mode == 0))
|
||||
{
|
||||
if (SEGMENT.grouping == 0) SEGMENT.grouping = 1; //sanity check
|
||||
doShow = true;
|
||||
@@ -152,10 +161,18 @@ void WS2812FX::service() {
|
||||
_colors_t[slot] = transitions[t].currentColor(SEGMENT.colors[slot]);
|
||||
}
|
||||
if (!cctFromRgb || correctWB) busses.setSegmentCCT(_cct_t, correctWB);
|
||||
for (uint8_t c = 0; c < 3; c++) _colors_t[c] = gamma32(_colors_t[c]);
|
||||
for (uint8_t c = 0; c < NUM_COLORS; c++) {
|
||||
_colors_t[c] = gamma32(_colors_t[c]);
|
||||
}
|
||||
handle_palette();
|
||||
|
||||
// if segment is not RGB capable, force None auto white mode
|
||||
// If not RGB capable, also treat palette as if default (0), as palettes set white channel to 0
|
||||
_no_rgb = !(SEGMENT.getLightCapabilities() & 0x01);
|
||||
if (_no_rgb) Bus::setAutoWhiteMode(RGBW_MODE_MANUAL_ONLY);
|
||||
delay = (this->*_mode[SEGMENT.mode])(); //effect function
|
||||
if (SEGMENT.mode != FX_MODE_HALLOWEEN_EYES) SEGENV.call++;
|
||||
Bus::setAutoWhiteMode(strip.autoWhiteMode);
|
||||
}
|
||||
|
||||
SEGENV.next_time = nowUp + delay;
|
||||
@@ -170,34 +187,11 @@ void WS2812FX::service() {
|
||||
_triggered = false;
|
||||
}
|
||||
|
||||
void IRAM_ATTR WS2812FX::setPixelColor(uint16_t n, uint32_t c) {
|
||||
setPixelColor(n, R(c), G(c), B(c), W(c));
|
||||
}
|
||||
|
||||
//used to map from segment index to physical pixel, taking into account grouping, offsets, reverse and mirroring
|
||||
uint16_t IRAM_ATTR WS2812FX::realPixelIndex(uint16_t i) {
|
||||
int16_t iGroup = i * SEGMENT.groupLength();
|
||||
|
||||
/* reverse just an individual segment */
|
||||
int16_t realIndex = iGroup;
|
||||
if (IS_REVERSE) {
|
||||
if (IS_MIRROR) {
|
||||
realIndex = (SEGMENT.length() - 1) / 2 - iGroup; //only need to index half the pixels
|
||||
} else {
|
||||
realIndex = (SEGMENT.length() - 1) - iGroup;
|
||||
}
|
||||
}
|
||||
|
||||
realIndex += SEGMENT.start;
|
||||
return realIndex;
|
||||
}
|
||||
|
||||
void IRAM_ATTR WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
|
||||
{
|
||||
if (SEGLEN) {//from segment
|
||||
uint16_t realIndex = realPixelIndex(i);
|
||||
uint16_t len = SEGMENT.length();
|
||||
uint8_t segIdx;
|
||||
|
||||
if (SEGLEN) { // SEGLEN!=0 -> from segment/FX
|
||||
//color_blend(getpixel, col, _bri_t); (pseudocode for future blending of segments)
|
||||
if (_bri_t < 255) {
|
||||
r = scale8(r, _bri_t);
|
||||
@@ -205,30 +199,48 @@ void IRAM_ATTR WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte
|
||||
b = scale8(b, _bri_t);
|
||||
w = scale8(w, _bri_t);
|
||||
}
|
||||
segIdx = _segment_index;
|
||||
} else // from live/realtime
|
||||
segIdx = _mainSegment;
|
||||
|
||||
if (SEGLEN || (realtimeMode && useMainSegmentOnly)) {
|
||||
uint32_t col = RGBW32(r, g, b, w);
|
||||
uint16_t len = _segments[segIdx].length();
|
||||
|
||||
/* Set all the pixels in the group */
|
||||
for (uint16_t j = 0; j < SEGMENT.grouping; j++) {
|
||||
uint16_t indexSet = realIndex + (IS_REVERSE ? -j : j);
|
||||
if (indexSet >= SEGMENT.start && indexSet < SEGMENT.stop) {
|
||||
if (IS_MIRROR) { //set the corresponding mirrored pixel
|
||||
uint16_t indexMir = SEGMENT.stop - indexSet + SEGMENT.start - 1;
|
||||
/* offset/phase */
|
||||
indexMir += SEGMENT.offset;
|
||||
if (indexMir >= SEGMENT.stop) indexMir -= len;
|
||||
// get physical pixel address (taking into account start, grouping, spacing [and offset])
|
||||
i = i * _segments[segIdx].groupLength();
|
||||
if (_segments[segIdx].options & REVERSE) { // is segment reversed?
|
||||
if (_segments[segIdx].options & MIRROR) { // is segment mirrored?
|
||||
i = (len - 1) / 2 - i; //only need to index half the pixels
|
||||
} else {
|
||||
i = (len - 1) - i;
|
||||
}
|
||||
}
|
||||
i += _segments[segIdx].start;
|
||||
|
||||
// set all the pixels in the group
|
||||
for (uint16_t j = 0; j < _segments[segIdx].grouping; j++) {
|
||||
uint16_t indexSet = i + ((_segments[segIdx].options & REVERSE) ? -j : j);
|
||||
if (indexSet >= _segments[segIdx].start && indexSet < _segments[segIdx].stop) {
|
||||
|
||||
if (_segments[segIdx].options & MIRROR) { //set the corresponding mirrored pixel
|
||||
uint16_t indexMir = _segments[segIdx].stop - indexSet + _segments[segIdx].start - 1;
|
||||
indexMir += _segments[segIdx].offset; // offset/phase
|
||||
|
||||
if (indexMir >= _segments[segIdx].stop) indexMir -= len;
|
||||
if (indexMir < customMappingSize) indexMir = customMappingTable[indexMir];
|
||||
|
||||
busses.setPixelColor(indexMir, col);
|
||||
}
|
||||
/* offset/phase */
|
||||
indexSet += SEGMENT.offset;
|
||||
if (indexSet >= SEGMENT.stop) indexSet -= len;
|
||||
indexSet += _segments[segIdx].offset; // offset/phase
|
||||
|
||||
if (indexSet >= _segments[segIdx].stop) indexSet -= len;
|
||||
if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet];
|
||||
|
||||
busses.setPixelColor(indexSet, col);
|
||||
}
|
||||
}
|
||||
} else { //live data, etc.
|
||||
} else {
|
||||
if (i < customMappingSize) i = customMappingTable[i];
|
||||
busses.setPixelColor(i, RGBW32(r, g, b, w));
|
||||
}
|
||||
@@ -418,7 +430,7 @@ void WS2812FX::setCCT(uint16_t k) {
|
||||
}
|
||||
}
|
||||
|
||||
void WS2812FX::setBrightness(uint8_t b) {
|
||||
void WS2812FX::setBrightness(uint8_t b, bool direct) {
|
||||
if (gammaCorrectBri) b = gamma8(b);
|
||||
if (_brightness == b) return;
|
||||
_brightness = b;
|
||||
@@ -428,8 +440,13 @@ void WS2812FX::setBrightness(uint8_t b) {
|
||||
_segments[i].setOption(SEG_OPTION_FREEZE, false);
|
||||
}
|
||||
}
|
||||
unsigned long t = millis();
|
||||
if (_segment_runtimes[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) show(); //apply brightness change immediately if no refresh soon
|
||||
if (direct) {
|
||||
// would be dangerous if applied immediately (could exceed ABL), but will not output until the next show()
|
||||
busses.setBrightness(b);
|
||||
} else {
|
||||
unsigned long t = millis();
|
||||
if (_segment_runtimes[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) show(); //apply brightness change immediately if no refresh soon
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getBrightness(void) {
|
||||
@@ -488,7 +505,13 @@ uint8_t WS2812FX::getActiveSegmentsNum(void) {
|
||||
|
||||
uint32_t WS2812FX::getPixelColor(uint16_t i)
|
||||
{
|
||||
i = realPixelIndex(i);
|
||||
// get physical pixel
|
||||
i = i * SEGMENT.groupLength();;
|
||||
if (IS_REVERSE) {
|
||||
if (IS_MIRROR) i = (SEGMENT.length() - 1) / 2 - i; //only need to index half the pixels
|
||||
else i = (SEGMENT.length() - 1) - i;
|
||||
}
|
||||
i += SEGMENT.start;
|
||||
|
||||
if (SEGLEN) {
|
||||
/* offset/phase */
|
||||
@@ -503,7 +526,7 @@ uint32_t WS2812FX::getPixelColor(uint16_t i)
|
||||
}
|
||||
|
||||
WS2812FX::Segment& WS2812FX::getSegment(uint8_t id) {
|
||||
if (id >= MAX_NUM_SEGMENTS) return _segments[0];
|
||||
if (id >= MAX_NUM_SEGMENTS) return _segments[getMainSegmentId()];
|
||||
return _segments[id];
|
||||
}
|
||||
|
||||
@@ -561,11 +584,14 @@ uint8_t WS2812FX::Segment::differs(Segment& b) {
|
||||
return d;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::Segment::getLightCapabilities() {
|
||||
if (!isActive()) return 0;
|
||||
void WS2812FX::Segment::refreshLightCapabilities() {
|
||||
if (!isActive()) {
|
||||
_capabilities = 0; return;
|
||||
}
|
||||
uint8_t capabilities = 0;
|
||||
uint8_t awm = Bus::getAutoWhiteMode();
|
||||
uint8_t awm = instance->autoWhiteMode;
|
||||
bool whiteSlider = (awm == RGBW_MODE_DUAL || awm == RGBW_MODE_MANUAL_ONLY);
|
||||
bool segHasValidBus = false;
|
||||
|
||||
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
|
||||
Bus *bus = busses.getBus(b);
|
||||
@@ -573,12 +599,13 @@ uint8_t WS2812FX::Segment::getLightCapabilities() {
|
||||
if (bus->getStart() >= stop) continue;
|
||||
if (bus->getStart() + bus->getLength() <= start) continue;
|
||||
|
||||
segHasValidBus = true;
|
||||
uint8_t type = bus->getType();
|
||||
if (!whiteSlider || (type != TYPE_ANALOG_1CH && (cctFromRgb || type != TYPE_ANALOG_2CH)))
|
||||
if (type != TYPE_ANALOG_1CH && (cctFromRgb || type != TYPE_ANALOG_2CH))
|
||||
{
|
||||
capabilities |= 0x01; //segment supports RGB (full color)
|
||||
capabilities |= 0x01; // segment supports RGB (full color)
|
||||
}
|
||||
if (bus->isRgbw() && whiteSlider) capabilities |= 0x02; //segment supports white channel
|
||||
if (bus->isRgbw() && whiteSlider) capabilities |= 0x02; // segment supports white channel
|
||||
if (!cctFromRgb) {
|
||||
switch (type) {
|
||||
case TYPE_ANALOG_5CH:
|
||||
@@ -588,7 +615,10 @@ uint8_t WS2812FX::Segment::getLightCapabilities() {
|
||||
}
|
||||
if (correctWB && type != TYPE_ANALOG_1CH) capabilities |= 0x04; //white balance correction (uses CCT slider)
|
||||
}
|
||||
return capabilities;
|
||||
// if seg has any bus, but no bus has RGB, it by definition supports white (at least for now)
|
||||
// In case of no RGB, disregard auto white mode and always show a white slider
|
||||
if (segHasValidBus && !(capabilities & 0x01)) capabilities |= 0x02; // segment supports white channel
|
||||
_capabilities = capabilities;
|
||||
}
|
||||
|
||||
//used for JSON API info.leds.rgbw. Little practical use, deprecate with info.leds.rgbw.
|
||||
@@ -627,7 +657,8 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping,
|
||||
Segment& seg = _segments[n];
|
||||
|
||||
//return if neither bounds nor grouping have changed
|
||||
if (seg.start == i1 && seg.stop == i2
|
||||
bool boundsUnchanged = (seg.start == i1 && seg.stop == i2);
|
||||
if (boundsUnchanged
|
||||
&& (!grouping || (seg.grouping == grouping && seg.spacing == spacing))
|
||||
&& (offset == UINT16_MAX || offset == seg.offset)) return;
|
||||
|
||||
@@ -652,6 +683,7 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping,
|
||||
}
|
||||
if (offset < UINT16_MAX) seg.offset = offset;
|
||||
_segment_runtimes[n].markForReset();
|
||||
if (!boundsUnchanged) seg.refreshLightCapabilities();
|
||||
}
|
||||
|
||||
void WS2812FX::restartRuntime() {
|
||||
@@ -741,6 +773,8 @@ void WS2812FX::fixInvalidSegments() {
|
||||
{
|
||||
if (_segments[i].start >= _length) setSegment(i, 0, 0);
|
||||
if (_segments[i].stop > _length) setSegment(i, _segments[i].start, _length);
|
||||
// this is always called as the last step after finalizeInit(), update covered bus types
|
||||
getSegment(i).refreshLightCapabilities();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -918,12 +952,6 @@ uint16_t IRAM_ATTR WS2812FX::triwave16(uint16_t in)
|
||||
return 0xFFFF - (in - 0x8000)*2;
|
||||
}
|
||||
|
||||
uint8_t IRAM_ATTR WS2812FX::sin_gap(uint16_t in) {
|
||||
if (in & 0x100) return 0;
|
||||
//if (in > 255) return 0;
|
||||
return sin8(in + 192); //correct phase shift of sine so that it starts and stops at 0
|
||||
}
|
||||
|
||||
/*
|
||||
* Generates a tristate square wave w/ attac & decay
|
||||
* @param x input value 0-255
|
||||
@@ -1116,22 +1144,17 @@ void WS2812FX::handle_palette(void)
|
||||
*/
|
||||
uint32_t IRAM_ATTR WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri)
|
||||
{
|
||||
if (SEGMENT.palette == 0 && mcol < 3) {
|
||||
if ((SEGMENT.palette == 0 && mcol < 3) || _no_rgb) {
|
||||
uint32_t color = SEGCOLOR(mcol);
|
||||
if (pbri != 255) {
|
||||
CRGB crgb_color = col_to_crgb(color);
|
||||
crgb_color.nscale8_video(pbri);
|
||||
return crgb_to_col(crgb_color);
|
||||
} else {
|
||||
return color;
|
||||
}
|
||||
if (pbri == 255) return color;
|
||||
return RGBW32(scale8_video(R(color),pbri), scale8_video(G(color),pbri), scale8_video(B(color),pbri), scale8_video(W(color),pbri));
|
||||
}
|
||||
|
||||
uint8_t paletteIndex = i;
|
||||
if (mapping && SEGLEN > 1) paletteIndex = (i*255)/(SEGLEN -1);
|
||||
if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
|
||||
CRGB fastled_col;
|
||||
fastled_col = ColorFromPalette( currentPalette, paletteIndex, pbri, (paletteBlend == 3)? NOBLEND:LINEARBLEND);
|
||||
fastled_col = ColorFromPalette(currentPalette, paletteIndex, pbri, (paletteBlend == 3)? NOBLEND:LINEARBLEND);
|
||||
|
||||
return crgb_to_col(fastled_col);
|
||||
}
|
||||
@@ -1242,4 +1265,4 @@ WS2812FX* WS2812FX::instance = nullptr;
|
||||
//Bus static member definition, would belong in bus_manager.cpp
|
||||
int16_t Bus::_cct = -1;
|
||||
uint8_t Bus::_cctBlend = 0;
|
||||
uint8_t Bus::_autoWhiteMode = RGBW_MODE_DUAL;
|
||||
uint8_t Bus::_autoWhiteMode = RGBW_MODE_DUAL;
|
||||
|
||||
@@ -588,7 +588,7 @@ class BusManager {
|
||||
//utility to get the approx. memory usage of a given BusConfig
|
||||
static uint32_t memUsage(BusConfig &bc) {
|
||||
uint8_t type = bc.type;
|
||||
uint16_t len = bc.count;
|
||||
uint16_t len = bc.count + bc.skipAmount;
|
||||
if (type > 15 && type < 32) {
|
||||
#ifdef ESP8266
|
||||
if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem
|
||||
|
||||
@@ -69,6 +69,10 @@
|
||||
#define I_HS_P98_3 35
|
||||
#define I_SS_P98_3 36
|
||||
|
||||
//LPD6803
|
||||
#define I_HS_LPO_3 37
|
||||
#define I_SS_LPO_3 38
|
||||
|
||||
|
||||
/*** ESP8266 Neopixel methods ***/
|
||||
#ifdef ESP8266
|
||||
@@ -133,13 +137,17 @@
|
||||
#endif
|
||||
|
||||
//APA102
|
||||
#define B_HS_DOT_3 NeoPixelBrightnessBus<DotStarBgrFeature, DotStarSpiMethod> //hardware SPI
|
||||
#define B_HS_DOT_3 NeoPixelBrightnessBus<DotStarBgrFeature, DotStarSpi5MhzMethod> //hardware SPI
|
||||
#define B_SS_DOT_3 NeoPixelBrightnessBus<DotStarBgrFeature, DotStarMethod> //soft SPI
|
||||
|
||||
//LPD8806
|
||||
#define B_HS_LPD_3 NeoPixelBrightnessBus<Lpd8806GrbFeature, Lpd8806SpiMethod>
|
||||
#define B_SS_LPD_3 NeoPixelBrightnessBus<Lpd8806GrbFeature, Lpd8806Method>
|
||||
|
||||
//LPD6803
|
||||
#define B_HS_LPO_3 NeoPixelBrightnessBus<Lpd6803GrbFeature, Lpd6803SpiMethod>
|
||||
#define B_SS_LPO_3 NeoPixelBrightnessBus<Lpd6803GrbFeature, Lpd6803Method>
|
||||
|
||||
//WS2801
|
||||
//#define B_HS_WS1_3 NeoPixelBrightnessBus<NeoRbgFeature, NeoWs2801Spi40MhzMethod>
|
||||
//#define B_HS_WS1_3 NeoPixelBrightnessBus<NeoRbgFeature, NeoWs2801Spi20MhzMethod>
|
||||
@@ -184,6 +192,7 @@ class PolyBus {
|
||||
case I_8266_BB_TM1_4: beginTM1814<B_8266_BB_TM1_4*>(busPtr); break;
|
||||
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Begin(); break;
|
||||
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->Begin(); break;
|
||||
case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->Begin(); break;
|
||||
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->Begin(); break;
|
||||
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->Begin(); break;
|
||||
#endif
|
||||
@@ -219,11 +228,13 @@ class PolyBus {
|
||||
// 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: (static_cast<B_HS_DOT_3*>(busPtr))->Begin(pins[1], -1, pins[0], -1); break;
|
||||
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->Begin(pins[1], -1, pins[0], -1); break;
|
||||
case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->Begin(pins[1], -1, pins[0], -1); break;
|
||||
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->Begin(pins[1], -1, pins[0], -1); break;
|
||||
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->Begin(pins[1], -1, pins[0], -1); break;
|
||||
#endif
|
||||
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->Begin(); break;
|
||||
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->Begin(); break;
|
||||
case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->Begin(); break;
|
||||
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->Begin(); break;
|
||||
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->Begin(); break;
|
||||
}
|
||||
@@ -285,6 +296,8 @@ class PolyBus {
|
||||
case I_SS_DOT_3: busPtr = new B_SS_DOT_3(len, pins[1], pins[0]); break;
|
||||
case I_HS_LPD_3: busPtr = new B_HS_LPD_3(len, pins[1], pins[0]); break;
|
||||
case I_SS_LPD_3: busPtr = new B_SS_LPD_3(len, pins[1], pins[0]); break;
|
||||
case I_HS_LPO_3: busPtr = new B_HS_LPO_3(len, pins[1], pins[0]); break;
|
||||
case I_SS_LPO_3: busPtr = new B_SS_LPO_3(len, pins[1], pins[0]); break;
|
||||
case I_HS_WS1_3: busPtr = new B_HS_WS1_3(len, pins[1], pins[0]); break;
|
||||
case I_SS_WS1_3: busPtr = new B_SS_WS1_3(len, pins[1], pins[0]); break;
|
||||
case I_HS_P98_3: busPtr = new B_HS_P98_3(len, pins[1], pins[0]); break;
|
||||
@@ -348,6 +361,8 @@ class PolyBus {
|
||||
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->Show(); break;
|
||||
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->Show(); break;
|
||||
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->Show(); break;
|
||||
case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->Show(); break;
|
||||
case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->Show(); break;
|
||||
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->Show(); break;
|
||||
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->Show(); break;
|
||||
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->Show(); break;
|
||||
@@ -409,6 +424,8 @@ class PolyBus {
|
||||
case I_SS_DOT_3: return (static_cast<B_SS_DOT_3*>(busPtr))->CanShow(); break;
|
||||
case I_HS_LPD_3: return (static_cast<B_HS_LPD_3*>(busPtr))->CanShow(); break;
|
||||
case I_SS_LPD_3: return (static_cast<B_SS_LPD_3*>(busPtr))->CanShow(); break;
|
||||
case I_HS_LPO_3: return (static_cast<B_HS_LPO_3*>(busPtr))->CanShow(); break;
|
||||
case I_SS_LPO_3: return (static_cast<B_SS_LPO_3*>(busPtr))->CanShow(); break;
|
||||
case I_HS_WS1_3: return (static_cast<B_HS_WS1_3*>(busPtr))->CanShow(); break;
|
||||
case I_SS_WS1_3: return (static_cast<B_SS_WS1_3*>(busPtr))->CanShow(); break;
|
||||
case I_HS_P98_3: return (static_cast<B_HS_P98_3*>(busPtr))->CanShow(); break;
|
||||
@@ -494,6 +511,8 @@ class PolyBus {
|
||||
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
@@ -555,6 +574,8 @@ class PolyBus {
|
||||
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetBrightness(b); break;
|
||||
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->SetBrightness(b); break;
|
||||
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->SetBrightness(b); break;
|
||||
case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->SetBrightness(b); break;
|
||||
case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->SetBrightness(b); break;
|
||||
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->SetBrightness(b); break;
|
||||
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->SetBrightness(b); break;
|
||||
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetBrightness(b); break;
|
||||
@@ -617,6 +638,8 @@ class PolyBus {
|
||||
case I_SS_DOT_3: col = (static_cast<B_SS_DOT_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_HS_LPD_3: col = (static_cast<B_HS_LPD_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_SS_LPD_3: col = (static_cast<B_SS_LPD_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_HS_LPO_3: col = (static_cast<B_HS_LPO_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_SS_LPO_3: col = (static_cast<B_SS_LPO_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_HS_WS1_3: col = (static_cast<B_HS_WS1_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_SS_WS1_3: col = (static_cast<B_SS_WS1_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_HS_P98_3: col = (static_cast<B_HS_P98_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
@@ -696,6 +719,8 @@ class PolyBus {
|
||||
case I_SS_DOT_3: delete (static_cast<B_SS_DOT_3*>(busPtr)); break;
|
||||
case I_HS_LPD_3: delete (static_cast<B_HS_LPD_3*>(busPtr)); break;
|
||||
case I_SS_LPD_3: delete (static_cast<B_SS_LPD_3*>(busPtr)); break;
|
||||
case I_HS_LPO_3: delete (static_cast<B_HS_LPO_3*>(busPtr)); break;
|
||||
case I_SS_LPO_3: delete (static_cast<B_SS_LPO_3*>(busPtr)); break;
|
||||
case I_HS_WS1_3: delete (static_cast<B_HS_WS1_3*>(busPtr)); break;
|
||||
case I_SS_WS1_3: delete (static_cast<B_SS_WS1_3*>(busPtr)); break;
|
||||
case I_HS_P98_3: delete (static_cast<B_HS_P98_3*>(busPtr)); break;
|
||||
@@ -717,6 +742,7 @@ class PolyBus {
|
||||
switch (busType) {
|
||||
case TYPE_APA102: t = I_SS_DOT_3; break;
|
||||
case TYPE_LPD8806: t = I_SS_LPD_3; break;
|
||||
case TYPE_LPD6803: t = I_SS_LPO_3; break;
|
||||
case TYPE_WS2801: t = I_SS_WS1_3; break;
|
||||
case TYPE_P9813: t = I_SS_P98_3; break;
|
||||
default: t=I_NONE;
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
* Physical IO
|
||||
*/
|
||||
|
||||
#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_AP 6000 //how long the button needs to be held to activate WLED-AP
|
||||
#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_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
|
||||
|
||||
static const char _mqtt_topic_button[] PROGMEM = "%s/button/%d"; // optimize flash usage
|
||||
|
||||
@@ -130,21 +131,42 @@ void handleSwitch(uint8_t b)
|
||||
}
|
||||
}
|
||||
|
||||
#define ANALOG_BTN_READ_CYCLE 250 // min time between two analog reading cycles
|
||||
#define STRIP_WAIT_TIME 6 // max wait time in case of strip.isUpdating()
|
||||
#define POT_SMOOTHING 0.25f // smoothing factor for raw potentiometer readings
|
||||
#define POT_SENSITIVITY 4 // changes below this amount are noise (POT scratching, or ADC noise)
|
||||
|
||||
void handleAnalog(uint8_t b)
|
||||
{
|
||||
static uint8_t oldRead[WLED_MAX_BUTTONS];
|
||||
static uint8_t oldRead[WLED_MAX_BUTTONS] = {0};
|
||||
static float filteredReading[WLED_MAX_BUTTONS] = {0.0f};
|
||||
uint16_t rawReading; // raw value from analogRead, scaled to 12bit
|
||||
|
||||
#ifdef ESP8266
|
||||
uint16_t aRead = analogRead(A0) >> 2; // convert 10bit read to 8bit
|
||||
rawReading = analogRead(A0) << 2; // convert 10bit read to 12bit
|
||||
#else
|
||||
uint16_t aRead = analogRead(btnPin[b]) >> 4; // convert 12bit read to 8bit
|
||||
rawReading = analogRead(btnPin[b]); // collect at full 12bit resolution
|
||||
#endif
|
||||
yield(); // keep WiFi task running - analog read may take several millis on ESP8266
|
||||
|
||||
filteredReading[b] += POT_SMOOTHING * ((float(rawReading) / 16.0f) - filteredReading[b]); // filter raw input, and scale to [0..255]
|
||||
uint16_t aRead = max(min(int(filteredReading[b]), 255), 0); // squash into 8bit
|
||||
if(aRead <= POT_SENSITIVITY) aRead = 0; // make sure that 0 and 255 are used
|
||||
if(aRead >= 255-POT_SENSITIVITY) aRead = 255;
|
||||
|
||||
if (buttonType[b] == BTN_TYPE_ANALOG_INVERTED) aRead = 255 - aRead;
|
||||
|
||||
// remove noise & reduce frequency of UI updates
|
||||
aRead &= 0xFC;
|
||||
if (abs(int(aRead) - int(oldRead[b])) <= POT_SENSITIVITY) return; // no significant change in reading
|
||||
|
||||
// Unomment the next lines if you still see flickering related to potentiometer
|
||||
// This waits until strip finishes updating (why: strip was not updating at the start of handleButton() but may have started during analogRead()?)
|
||||
//unsigned long wait_started = millis();
|
||||
//while(strip.isUpdating() && (millis() - wait_started < STRIP_WAIT_TIME)) {
|
||||
// delay(1);
|
||||
//}
|
||||
//if (strip.isUpdating()) return; // give up
|
||||
|
||||
if (oldRead[b] == aRead) return; // no change in reading
|
||||
oldRead[b] = aRead;
|
||||
|
||||
// if no macro for "short press" and "long press" is defined use brightness control
|
||||
@@ -167,6 +189,7 @@ void handleAnalog(uint8_t b)
|
||||
} else if (macroDoublePress[b] == 247) {
|
||||
// selected palette
|
||||
effectPalette = map(aRead, 0, 252, 0, strip.getPaletteCount()-1);
|
||||
effectPalette = constrain(effectPalette, 0, strip.getPaletteCount()-1); // map is allowed to "overshoot", so we need to contrain the result
|
||||
} else if (macroDoublePress[b] == 200) {
|
||||
// primary color, hue, full saturation
|
||||
colorHStoRGB(aRead*256,255,col);
|
||||
@@ -195,6 +218,8 @@ void handleButton()
|
||||
static unsigned long lastRead = 0UL;
|
||||
bool analog = false;
|
||||
|
||||
if (strip.isUpdating()) return; // don't interfere with strip updates. Our button will still be there in 1ms (next cycle)
|
||||
|
||||
for (uint8_t b=0; b<WLED_MAX_BUTTONS; b++) {
|
||||
#ifdef ESP8266
|
||||
if ((btnPin[b]<0 && !(buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED)) || buttonType[b] == BTN_TYPE_NONE) continue;
|
||||
@@ -204,7 +229,7 @@ void handleButton()
|
||||
|
||||
if (usermods.handleButton(b)) continue; // did usermod handle buttons
|
||||
|
||||
if ((buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) && millis() - lastRead > 250) { // button is not a button but a potentiometer
|
||||
if ((buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) && millis() - lastRead > ANALOG_BTN_READ_CYCLE) { // button is not a button but a potentiometer
|
||||
analog = true;
|
||||
handleAnalog(b); continue;
|
||||
}
|
||||
@@ -236,8 +261,14 @@ void handleButton()
|
||||
bool doublePress = buttonWaitTime[b]; //did we have a short press before?
|
||||
buttonWaitTime[b] = 0;
|
||||
|
||||
if (b == 0 && dur > WLED_LONG_AP) { //long press on button 0 (when released)
|
||||
WLED::instance().initAP(true);
|
||||
if (b == 0 && dur > WLED_LONG_AP) { // long press on button 0 (when released)
|
||||
if (dur > WLED_LONG_FACTORY_RESET) { // factory reset if pressed > 10 seconds
|
||||
WLED_FS.format();
|
||||
clearEEPROM();
|
||||
doReboot = true;
|
||||
} else {
|
||||
WLED::instance().initAP(true);
|
||||
}
|
||||
} else if (!buttonLongPressed[b]) { //short press
|
||||
if (b != 1 && !macroDoublePress[b]) { //don't wait for double press on buttons without a default action if no double press macro set
|
||||
shortPressAction(b);
|
||||
|
||||
@@ -80,19 +80,22 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
|
||||
CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]);
|
||||
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]);
|
||||
Bus::setAutoWhiteMode(hw_led[F("rgbwm")] | Bus::getAutoWhiteMode());
|
||||
CJSON(strip.autoWhiteMode, hw_led[F("rgbwm")]);
|
||||
Bus::setAutoWhiteMode(strip.autoWhiteMode);
|
||||
strip.fixInvalidSegments(); // refreshes segment light capabilities (in case auto white mode changed)
|
||||
CJSON(correctWB, hw_led["cct"]);
|
||||
CJSON(cctFromRgb, hw_led[F("cr")]);
|
||||
CJSON(strip.cctBlending, hw_led[F("cb")]);
|
||||
Bus::setCCTBlend(strip.cctBlending);
|
||||
strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS
|
||||
CJSON(strip.cctBlending, hw_led[F("cb")]);
|
||||
Bus::setCCTBlend(strip.cctBlending);
|
||||
strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS
|
||||
|
||||
JsonArray ins = hw_led["ins"];
|
||||
|
||||
if (fromFS || !ins.isNull()) {
|
||||
uint8_t s = 0; // bus iterator
|
||||
busses.removeAll();
|
||||
if (fromFS) busses.removeAll(); // can't safely manipulate busses directly in network callback
|
||||
uint32_t mem = 0;
|
||||
bool busesChanged = false;
|
||||
for (JsonObject elm : ins) {
|
||||
if (s >= WLED_MAX_BUSSES) break;
|
||||
uint8_t pins[5] = {255, 255, 255, 255, 255};
|
||||
@@ -113,12 +116,19 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
uint8_t ledType = elm["type"] | TYPE_WS2812_RGB;
|
||||
bool reversed = elm["rev"];
|
||||
bool refresh = elm["ref"] | false;
|
||||
ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh
|
||||
ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh
|
||||
if (fromFS) {
|
||||
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst);
|
||||
mem += BusManager::memUsage(bc);
|
||||
if (mem <= MAX_LED_MEMORY && busses.getNumBusses() <= WLED_MAX_BUSSES) busses.add(bc); // finalization will be done in WLED::beginStrip()
|
||||
} else {
|
||||
if (busConfigs[s] != nullptr) delete busConfigs[s];
|
||||
busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst);
|
||||
busesChanged = true;
|
||||
}
|
||||
s++;
|
||||
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst);
|
||||
mem += BusManager::memUsage(bc);
|
||||
if (mem <= MAX_LED_MEMORY && busses.getNumBusses() <= WLED_MAX_BUSSES) busses.add(bc); // finalization will be done in WLED::beginStrip()
|
||||
}
|
||||
doInitBusses = busesChanged;
|
||||
// finalization done in beginStrip()
|
||||
}
|
||||
if (hw_led["rev"]) busses.getBus(0)->reversed = true; //set 0.11 global reversed setting for first bus
|
||||
@@ -196,6 +206,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
}
|
||||
}
|
||||
CJSON(irEnabled, hw["ir"]["type"]);
|
||||
CJSON(irApplyToAllSelected, hw["ir"]["sel"]);
|
||||
|
||||
JsonObject relay = hw[F("relay")];
|
||||
int hw_relay_pin = relay["pin"] | -2;
|
||||
@@ -282,6 +293,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
|
||||
JsonObject if_live = interfaces["live"];
|
||||
CJSON(receiveDirect, if_live["en"]);
|
||||
CJSON(useMainSegmentOnly, if_live[F("mso")]);
|
||||
CJSON(e131Port, if_live["port"]); // 5568
|
||||
if (e131Port == DDP_DEFAULT_PORT) e131Port = E131_DEFAULT_PORT; // prevent double DDP port allocation
|
||||
CJSON(e131Multicast, if_live[F("mc")]);
|
||||
@@ -355,10 +367,8 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
CJSON(latitude, if_ntp[F("lt")]);
|
||||
|
||||
JsonObject ol = doc[F("ol")];
|
||||
prev = overlayDefault;
|
||||
CJSON(overlayDefault ,ol[F("clock")]); // 0
|
||||
CJSON(overlayCurrent ,ol[F("clock")]); // 0
|
||||
CJSON(countdownMode, ol[F("cntdwn")]);
|
||||
if (prev != overlayDefault) overlayCurrent = overlayDefault;
|
||||
|
||||
CJSON(overlayMin, ol["min"]);
|
||||
CJSON(overlayMax, ol[F("max")]);
|
||||
@@ -399,15 +409,15 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
if (act) timerWeekday[it]++;
|
||||
}
|
||||
if (it<8) {
|
||||
JsonObject start = timer["start"];
|
||||
byte startm = start["mon"];
|
||||
if (startm) timerMonth[it] = (startm << 4);
|
||||
JsonObject start = timer["start"];
|
||||
byte startm = start["mon"];
|
||||
if (startm) timerMonth[it] = (startm << 4);
|
||||
CJSON(timerDay[it], start["day"]);
|
||||
JsonObject end = timer["end"];
|
||||
CJSON(timerDayEnd[it], end["day"]);
|
||||
byte endm = end["mon"];
|
||||
if (startm) timerMonth[it] += endm & 0x0F;
|
||||
if (!(timerMonth[it] & 0x0F)) timerMonth[it] += 12; //default end month to 12
|
||||
JsonObject end = timer["end"];
|
||||
CJSON(timerDayEnd[it], end["day"]);
|
||||
byte endm = end["mon"];
|
||||
if (startm) timerMonth[it] += endm & 0x0F;
|
||||
if (!(timerMonth[it] & 0x0F)) timerMonth[it] += 12; //default end month to 12
|
||||
}
|
||||
it++;
|
||||
}
|
||||
@@ -448,7 +458,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
}
|
||||
|
||||
if (fromFS) return needsSave;
|
||||
// if from /json/cfg
|
||||
doReboot = doc[F("rb")] | doReboot;
|
||||
if (doInitBusses) return false; // no save needed, will do after bus init in wled.cpp loop
|
||||
return (doc["sv"] | true);
|
||||
}
|
||||
|
||||
@@ -571,9 +583,9 @@ void serializeConfig() {
|
||||
hw_led[F("ledma")] = strip.milliampsPerLed;
|
||||
hw_led["cct"] = correctWB;
|
||||
hw_led[F("cr")] = cctFromRgb;
|
||||
hw_led[F("cb")] = strip.cctBlending;
|
||||
hw_led["fps"] = strip.getTargetFps();
|
||||
hw_led[F("rgbwm")] = Bus::getAutoWhiteMode();
|
||||
hw_led[F("cb")] = strip.cctBlending;
|
||||
hw_led["fps"] = strip.getTargetFps();
|
||||
hw_led[F("rgbwm")] = strip.autoWhiteMode;
|
||||
|
||||
JsonArray hw_led_ins = hw_led.createNestedArray("ins");
|
||||
|
||||
@@ -630,6 +642,7 @@ void serializeConfig() {
|
||||
JsonObject hw_ir = hw.createNestedObject("ir");
|
||||
hw_ir["pin"] = irPin;
|
||||
hw_ir["type"] = irEnabled; // the byte 'irEnabled' does contain the IR-Remote Type ( 0=disabled )
|
||||
hw_ir["sel"] = irApplyToAllSelected;
|
||||
|
||||
JsonObject hw_relay = hw.createNestedObject(F("relay"));
|
||||
hw_relay["pin"] = rlyPin;
|
||||
@@ -694,6 +707,7 @@ void serializeConfig() {
|
||||
|
||||
JsonObject if_live = interfaces.createNestedObject("live");
|
||||
if_live["en"] = receiveDirect;
|
||||
if_live[F("mso")] = useMainSegmentOnly;
|
||||
if_live["port"] = e131Port;
|
||||
if_live[F("mc")] = e131Multicast;
|
||||
|
||||
@@ -763,7 +777,7 @@ void serializeConfig() {
|
||||
if_ntp[F("lt")] = latitude;
|
||||
|
||||
JsonObject ol = doc.createNestedObject("ol");
|
||||
ol[F("clock")] = overlayDefault;
|
||||
ol[F("clock")] = overlayCurrent;
|
||||
ol[F("cntdwn")] = countdownMode;
|
||||
|
||||
ol["min"] = overlayMin;
|
||||
@@ -791,11 +805,11 @@ void serializeConfig() {
|
||||
timers_ins0["macro"] = timerMacro[i];
|
||||
timers_ins0[F("dow")] = timerWeekday[i] >> 1;
|
||||
if (i<8) {
|
||||
JsonObject start = timers_ins0.createNestedObject("start");
|
||||
JsonObject start = timers_ins0.createNestedObject("start");
|
||||
start["mon"] = (timerMonth[i] >> 4) & 0xF;
|
||||
start["day"] = timerDay[i];
|
||||
JsonObject end = timers_ins0.createNestedObject("end");
|
||||
end["mon"] = timerMonth[i] & 0xF;
|
||||
JsonObject end = timers_ins0.createNestedObject("end");
|
||||
end["mon"] = timerMonth[i] & 0xF;
|
||||
end["day"] = timerDayEnd[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,33 +10,6 @@ void setRandomColor(byte* rgb)
|
||||
colorHStoRGB(lastRandomIndex*256,255,rgb);
|
||||
}
|
||||
|
||||
void colorFromUint32(uint32_t in, bool secondary)
|
||||
{
|
||||
byte *_col = secondary ? colSec : col;
|
||||
_col[0] = R(in);
|
||||
_col[1] = G(in);
|
||||
_col[2] = B(in);
|
||||
_col[3] = W(in);
|
||||
}
|
||||
|
||||
//load a color without affecting the white channel
|
||||
void colorFromUint24(uint32_t in, bool secondary)
|
||||
{
|
||||
byte *_col = secondary ? colSec : col;
|
||||
_col[0] = R(in);
|
||||
_col[1] = G(in);
|
||||
_col[2] = B(in);
|
||||
}
|
||||
|
||||
//relatively change white brightness, minumum A=5
|
||||
void relativeChangeWhite(int8_t amount, byte lowerBoundary)
|
||||
{
|
||||
int16_t new_val = (int16_t) col[3] + amount;
|
||||
if (new_val > 0xFF) new_val = 0xFF;
|
||||
else if (new_val < lowerBoundary) new_val = lowerBoundary;
|
||||
col[3] = new_val;
|
||||
}
|
||||
|
||||
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) //hue, sat to rgb
|
||||
{
|
||||
float h = ((float)hue)/65535.0;
|
||||
|
||||
@@ -71,6 +71,12 @@
|
||||
#define USERMOD_RGB_ROTARY_ENCODER 22 //Usermod "rgb-rotary-encoder.h"
|
||||
#define USERMOD_ID_QUINLED_AN_PENTA 23 //Usermod "quinled-an-penta.h"
|
||||
#define USERMOD_ID_SSDR 24 //Usermod "usermod_v2_seven_segment_display_reloaded.h"
|
||||
#define USERMOD_ID_CRONIXIE 25 //Usermod "usermod_cronixie.h"
|
||||
#define USERMOD_ID_WIZLIGHTS 26 //Usermod "wizlights.h"
|
||||
#define USERMOD_ID_WORDCLOCK 27 //Usermod "usermod_v2_word_clock.h"
|
||||
#define USERMOD_ID_MY9291 28 //Usermod "usermod_MY9291.h"
|
||||
#define USERMOD_ID_SI7021_MQTT_HA 29 //Usermod "usermod_si7021_mqtt_ha.h"
|
||||
#define USERMOD_ID_BME280 30 //Usermod "usermod_bme280.h
|
||||
|
||||
//Access point behavior
|
||||
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
|
||||
@@ -159,6 +165,7 @@
|
||||
#define TYPE_APA102 51
|
||||
#define TYPE_LPD8806 52
|
||||
#define TYPE_P9813 53
|
||||
#define TYPE_LPD6803 54
|
||||
//Network types (master broadcast) (80-95)
|
||||
#define TYPE_NET_DDP_RGB 80 //network DDP RGB bus (master broadcast bus)
|
||||
#define TYPE_NET_E131_RGB 81 //network E131 RGB bus (master broadcast bus)
|
||||
@@ -191,7 +198,7 @@
|
||||
#define BTN_TYPE_ANALOG_INVERTED 8
|
||||
|
||||
//Ethernet board types
|
||||
#define WLED_NUM_ETH_TYPES 7
|
||||
#define WLED_NUM_ETH_TYPES 8
|
||||
|
||||
#define WLED_ETH_NONE 0
|
||||
#define WLED_ETH_WT32_ETH01 1
|
||||
@@ -291,7 +298,13 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define ABL_MILLIAMPS_DEFAULT 850 // auto lower brightness to stay close to milliampere limit
|
||||
#ifndef ABL_MILLIAMPS_DEFAULT
|
||||
#define ABL_MILLIAMPS_DEFAULT 850 // auto lower brightness to stay close to milliampere limit
|
||||
#else
|
||||
#if ABL_MILLIAMPS_DEFAULT < 250 // make sure value is at least 250
|
||||
#define ABL_MILLIAMPS_DEFAULT 250
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// PWM settings
|
||||
#ifndef WLED_PWM_FREQ
|
||||
|
||||
7
wled00/data/icons-ui/Read Me.txt
Normal file
7
wled00/data/icons-ui/Read Me.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
Open *demo.html* to see a list of all the glyphs in your font along with their codes/ligatures.
|
||||
|
||||
To use the generated font in desktop programs, you can install the TTF font. In order to copy the character associated with each icon, refer to the text box at the bottom right corner of each glyph in demo.html. The character inside this text box may be invisible; but it can still be copied. See this guide for more info: https://icomoon.io/#docs/local-fonts
|
||||
|
||||
You won't need any of the files located under the *demo-files* directory when including the generated font in your own projects.
|
||||
|
||||
You can import *selection.json* back to the IcoMoon app using the *Import Icons* button (or via Main Menu → Manage Projects) to retrieve your icon selection.
|
||||
152
wled00/data/icons-ui/demo-files/demo.css
Normal file
152
wled00/data/icons-ui/demo-files/demo.css
Normal file
@@ -0,0 +1,152 @@
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
font-size: 1em;
|
||||
line-height: 1.5;
|
||||
color: #555;
|
||||
background: #fff;
|
||||
}
|
||||
h1 {
|
||||
font-size: 1.5em;
|
||||
font-weight: normal;
|
||||
}
|
||||
small {
|
||||
font-size: .66666667em;
|
||||
}
|
||||
a {
|
||||
color: #e74c3c;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover, a:focus {
|
||||
box-shadow: 0 1px #e74c3c;
|
||||
}
|
||||
.bshadow0, input {
|
||||
box-shadow: inset 0 -2px #e7e7e7;
|
||||
}
|
||||
input:hover {
|
||||
box-shadow: inset 0 -2px #ccc;
|
||||
}
|
||||
input, fieldset {
|
||||
font-family: sans-serif;
|
||||
font-size: 1em;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
input {
|
||||
color: inherit;
|
||||
line-height: 1.5;
|
||||
height: 1.5em;
|
||||
padding: .25em 0;
|
||||
}
|
||||
input:focus {
|
||||
outline: none;
|
||||
box-shadow: inset 0 -2px #449fdb;
|
||||
}
|
||||
.glyph {
|
||||
font-size: 16px;
|
||||
width: 15em;
|
||||
padding-bottom: 1em;
|
||||
margin-right: 4em;
|
||||
margin-bottom: 1em;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
}
|
||||
.liga {
|
||||
width: 80%;
|
||||
width: calc(100% - 2.5em);
|
||||
}
|
||||
.talign-right {
|
||||
text-align: right;
|
||||
}
|
||||
.talign-center {
|
||||
text-align: center;
|
||||
}
|
||||
.bgc1 {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
.fgc1 {
|
||||
color: #999;
|
||||
}
|
||||
.fgc0 {
|
||||
color: #000;
|
||||
}
|
||||
p {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
.mvm {
|
||||
margin-top: .75em;
|
||||
margin-bottom: .75em;
|
||||
}
|
||||
.mtn {
|
||||
margin-top: 0;
|
||||
}
|
||||
.mtl, .mal {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
.mbl, .mal {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
.mal, .mhl {
|
||||
margin-left: 1.5em;
|
||||
margin-right: 1.5em;
|
||||
}
|
||||
.mhmm {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
}
|
||||
.mls {
|
||||
margin-left: .25em;
|
||||
}
|
||||
.ptl {
|
||||
padding-top: 1.5em;
|
||||
}
|
||||
.pbs, .pvs {
|
||||
padding-bottom: .25em;
|
||||
}
|
||||
.pvs, .pts {
|
||||
padding-top: .25em;
|
||||
}
|
||||
.unit {
|
||||
float: left;
|
||||
}
|
||||
.unitRight {
|
||||
float: right;
|
||||
}
|
||||
.size1of2 {
|
||||
width: 50%;
|
||||
}
|
||||
.size1of1 {
|
||||
width: 100%;
|
||||
}
|
||||
.clearfix:before, .clearfix:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
.clearfix:after {
|
||||
clear: both;
|
||||
}
|
||||
.hidden-true {
|
||||
display: none;
|
||||
}
|
||||
.textbox0 {
|
||||
width: 3em;
|
||||
background: #f1f1f1;
|
||||
padding: .25em .5em;
|
||||
line-height: 1.5;
|
||||
height: 1.5em;
|
||||
}
|
||||
#testDrive {
|
||||
display: block;
|
||||
padding-top: 24px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.fs0 {
|
||||
font-size: 16px;
|
||||
}
|
||||
.fs1 {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
30
wled00/data/icons-ui/demo-files/demo.js
Normal file
30
wled00/data/icons-ui/demo-files/demo.js
Normal file
@@ -0,0 +1,30 @@
|
||||
if (!('boxShadow' in document.body.style)) {
|
||||
document.body.setAttribute('class', 'noBoxShadow');
|
||||
}
|
||||
|
||||
document.body.addEventListener("click", function(e) {
|
||||
var target = e.target;
|
||||
if (target.tagName === "INPUT" &&
|
||||
target.getAttribute('class').indexOf('liga') === -1) {
|
||||
target.select();
|
||||
}
|
||||
});
|
||||
|
||||
(function() {
|
||||
var fontSize = document.getElementById('fontSize'),
|
||||
testDrive = document.getElementById('testDrive'),
|
||||
testText = document.getElementById('testText');
|
||||
function updateTest() {
|
||||
testDrive.innerHTML = testText.value || String.fromCharCode(160);
|
||||
if (window.icomoonLiga) {
|
||||
window.icomoonLiga(testDrive);
|
||||
}
|
||||
}
|
||||
function updateSize() {
|
||||
testDrive.style.fontSize = fontSize.value + 'px';
|
||||
}
|
||||
fontSize.addEventListener('change', updateSize, false);
|
||||
testText.addEventListener('input', updateTest, false);
|
||||
testText.addEventListener('change', updateTest, false);
|
||||
updateSize();
|
||||
}());
|
||||
360
wled00/data/icons-ui/demo.html
Normal file
360
wled00/data/icons-ui/demo.html
Normal file
@@ -0,0 +1,360 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>IcoMoon Demo</title>
|
||||
<meta name="description" content="An Icon Font Generated By IcoMoon.io">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="demo-files/demo.css">
|
||||
<link rel="stylesheet" href="style.css"></head>
|
||||
<body>
|
||||
<div class="bgc1 clearfix">
|
||||
<h1 class="mhmm mvm"><span class="fgc1">Font Name:</span> wled122 <small class="fgc1">(Glyphs: 23)</small></h1>
|
||||
</div>
|
||||
<div class="clearfix mhl ptl">
|
||||
<h1 class="mvm mtn fgc1">Grid Size: Unknown</h1>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-pattern"></span>
|
||||
<span class="mls"> i-pattern</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e23d" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-segments"></span>
|
||||
<span class="mls"> i-segments</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e34b" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-sun"></span>
|
||||
<span class="mls"> i-sun</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e333" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-palette"></span>
|
||||
<span class="mls"> i-palette</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e2b3" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-eye"></span>
|
||||
<span class="mls"> i-eye</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e0e8" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-speed"></span>
|
||||
<span class="mls"> i-speed</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e325" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-expand"></span>
|
||||
<span class="mls"> i-expand</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e395" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-power"></span>
|
||||
<span class="mls"> i-power</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e08f" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-settings"></span>
|
||||
<span class="mls"> i-settings</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e0a2" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-playlist"></span>
|
||||
<span class="mls"> i-playlist</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e139" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-night"></span>
|
||||
<span class="mls"> i-night</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e2a2" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-cancel"></span>
|
||||
<span class="mls"> i-cancel</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e38f" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-sync"></span>
|
||||
<span class="mls"> i-sync</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e116" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-confirm"></span>
|
||||
<span class="mls"> i-confirm</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e390" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-brightness"></span>
|
||||
<span class="mls"> i-brightness</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e2a6" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-nodes"></span>
|
||||
<span class="mls"> i-nodes</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e22d" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-add"></span>
|
||||
<span class="mls"> i-add</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e18a" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-edit"></span>
|
||||
<span class="mls"> i-edit</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e2c6" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-intensity"></span>
|
||||
<span class="mls"> i-intensity</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e409" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-star"></span>
|
||||
<span class="mls"> i-star</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e410" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-info"></span>
|
||||
<span class="mls"> i-info</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e066" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-del"></span>
|
||||
<span class="mls"> i-del</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e037" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="glyph fs1">
|
||||
<div class="clearfix bshadow0 pbs">
|
||||
<span class="i-presets"></span>
|
||||
<span class="mls"> i-presets</span>
|
||||
</div>
|
||||
<fieldset class="fs0 size1of1 clearfix hidden-false">
|
||||
<input type="text" readonly value="e04c" class="unit size1of2" />
|
||||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" />
|
||||
</fieldset>
|
||||
<div class="fs0 bshadow0 clearfix hidden-true">
|
||||
<span class="unit pvs fgc1">liga: </span>
|
||||
<input type="text" readonly value="" class="liga unitRight" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--[if gt IE 8]><!-->
|
||||
<div class="mhl clearfix mbl">
|
||||
<h1>Font Test Drive</h1>
|
||||
<label>
|
||||
Font Size: <input id="fontSize" type="number" class="textbox0 mbm"
|
||||
min="8" value="48" />
|
||||
px
|
||||
</label>
|
||||
<input id="testText" type="text" class="phl size1of1 mvl"
|
||||
placeholder="Type some text to test..." value=""/>
|
||||
<div id="testDrive" class="i-" style="font-family: wled122">
|
||||
</div>
|
||||
</div>
|
||||
<!--<![endif]-->
|
||||
<div class="bgc1 clearfix">
|
||||
<p class="mhl">Generated by <a href="https://icomoon.io/app">IcoMoon</a></p>
|
||||
</div>
|
||||
|
||||
<script src="demo-files/demo.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
wled00/data/icons-ui/fonts/wled122.woff
Normal file
BIN
wled00/data/icons-ui/fonts/wled122.woff
Normal file
Binary file not shown.
BIN
wled00/data/icons-ui/fonts/wled122.woff2
Normal file
BIN
wled00/data/icons-ui/fonts/wled122.woff2
Normal file
Binary file not shown.
1
wled00/data/icons-ui/selection.json
Normal file
1
wled00/data/icons-ui/selection.json
Normal file
File diff suppressed because one or more lines are too long
96
wled00/data/icons-ui/style.css
Normal file
96
wled00/data/icons-ui/style.css
Normal file
@@ -0,0 +1,96 @@
|
||||
@font-face {
|
||||
font-family: 'wled122';
|
||||
src:
|
||||
url('fonts/wled122.woff2?e3eban') format('woff2'),
|
||||
url('fonts/wled122.ttf?e3eban') format('truetype'),
|
||||
url('fonts/wled122.woff?e3eban') format('woff'),
|
||||
url('fonts/wled122.svg?e3eban#wled122') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
[class^="i-"], [class*=" i-"] {
|
||||
/* use !important to prevent issues with browser extensions that change fonts */
|
||||
font-family: 'wled122' !important;
|
||||
speak: never;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
line-height: 1;
|
||||
|
||||
/* Better Font Rendering =========== */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.i-pattern:before {
|
||||
content: "\e23d";
|
||||
}
|
||||
.i-segments:before {
|
||||
content: "\e34b";
|
||||
}
|
||||
.i-sun:before {
|
||||
content: "\e333";
|
||||
}
|
||||
.i-palette:before {
|
||||
content: "\e2b3";
|
||||
}
|
||||
.i-eye:before {
|
||||
content: "\e0e8";
|
||||
}
|
||||
.i-speed:before {
|
||||
content: "\e325";
|
||||
}
|
||||
.i-expand:before {
|
||||
content: "\e395";
|
||||
}
|
||||
.i-power:before {
|
||||
content: "\e08f";
|
||||
}
|
||||
.i-settings:before {
|
||||
content: "\e0a2";
|
||||
}
|
||||
.i-playlist:before {
|
||||
content: "\e139";
|
||||
}
|
||||
.i-night:before {
|
||||
content: "\e2a2";
|
||||
}
|
||||
.i-cancel:before {
|
||||
content: "\e38f";
|
||||
}
|
||||
.i-sync:before {
|
||||
content: "\e116";
|
||||
}
|
||||
.i-confirm:before {
|
||||
content: "\e390";
|
||||
}
|
||||
.i-brightness:before {
|
||||
content: "\e2a6";
|
||||
}
|
||||
.i-nodes:before {
|
||||
content: "\e22d";
|
||||
}
|
||||
.i-add:before {
|
||||
content: "\e18a";
|
||||
}
|
||||
.i-edit:before {
|
||||
content: "\e2c6";
|
||||
}
|
||||
.i-intensity:before {
|
||||
content: "\e409";
|
||||
}
|
||||
.i-star:before {
|
||||
content: "\e410";
|
||||
}
|
||||
.i-info:before {
|
||||
content: "\e066";
|
||||
}
|
||||
.i-del:before {
|
||||
content: "\e037";
|
||||
}
|
||||
.i-presets:before {
|
||||
content: "\e04c";
|
||||
}
|
||||
@@ -45,12 +45,10 @@ body {
|
||||
text-align: center;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
scrollbar-width: 6px;
|
||||
scrollbar-color: var(--c-sb) transparent;
|
||||
scrollbar-width: 6px;
|
||||
scrollbar-color: var(--c-sb) transparent;
|
||||
}
|
||||
|
||||
html,
|
||||
@@ -93,7 +91,7 @@ button {
|
||||
bottom: calc(var(--bh) + 6px);
|
||||
right: 4px;
|
||||
color: var(--c-6);
|
||||
cursor: pointer;
|
||||
cursor: pointer;
|
||||
writing-mode: vertical-rl;
|
||||
}
|
||||
|
||||
@@ -128,11 +126,6 @@ button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.segt {
|
||||
table-layout: fixed;
|
||||
width: 76%;
|
||||
}
|
||||
|
||||
.segtd {
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
@@ -167,25 +160,25 @@ button {
|
||||
}
|
||||
|
||||
.edit-icon {
|
||||
position: absolute;
|
||||
right: -26px;
|
||||
top: 10px;
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: -26px;
|
||||
top: 10px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
top: 10px;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
top: 10px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.search-cancel-icon {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 9px;
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 9px;
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.flr {
|
||||
@@ -286,7 +279,7 @@ button {
|
||||
padding-top: 0;
|
||||
margin-top: 11px;
|
||||
height: calc(100% - 11px);
|
||||
-webkit-overflow-scrolling: touch;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.smooth { transition: transform calc(var(--f, 1)*.5s) ease-out }
|
||||
@@ -294,6 +287,7 @@ button {
|
||||
.tab-label {
|
||||
margin: 0 0 -5px 0;
|
||||
padding-bottom: 4px;
|
||||
display: var(--bhd);
|
||||
}
|
||||
|
||||
.overlay {
|
||||
@@ -332,7 +326,7 @@ button {
|
||||
|
||||
#fxb0 {
|
||||
margin-bottom: 2px;
|
||||
filter: drop-shadow(0 0 1px #000);
|
||||
filter: drop-shadow(0 0 1px #000);
|
||||
}
|
||||
|
||||
.first {
|
||||
@@ -382,7 +376,7 @@ button {
|
||||
}
|
||||
|
||||
.modal button:hover {
|
||||
background-color: var(--c-4);
|
||||
background-color: var(--c-4);
|
||||
}
|
||||
|
||||
#info {
|
||||
@@ -394,7 +388,7 @@ button {
|
||||
}
|
||||
|
||||
#ndlt {
|
||||
margin: 12px 0;
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
.valtd i {
|
||||
@@ -430,7 +424,7 @@ button {
|
||||
}
|
||||
|
||||
#kn td {
|
||||
padding-bottom: 12px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
#lv {
|
||||
@@ -469,27 +463,27 @@ img {
|
||||
border-radius: 17px;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
--bg: var(--c-f);
|
||||
--bg: var(--c-f);
|
||||
}
|
||||
|
||||
#rwrap .sliderdisplay { --bg: #f00; }
|
||||
#gwrap .sliderdisplay { --bg: #0f0; }
|
||||
#bwrap .sliderdisplay { --bg: #00f; }
|
||||
#wbal .sliderdisplay, #kwrap .sliderdisplay {
|
||||
background: linear-gradient(90deg, #ff8f1f 0%, #fff 50%, #cbdbff);
|
||||
background: linear-gradient(90deg, #ff8f1f 0%, #fff 50%, #cbdbff);
|
||||
}
|
||||
|
||||
.sliderbubble {
|
||||
width: 36px;
|
||||
line-height: 24px;
|
||||
background: var(--c-3);
|
||||
position: absolute;
|
||||
transform: translateX(-50%);
|
||||
border-radius: 12px;
|
||||
margin-left: 12px;
|
||||
margin-top: 3px;
|
||||
padding: 0px;
|
||||
display: inline;
|
||||
width: 36px;
|
||||
line-height: 24px;
|
||||
background: var(--c-3);
|
||||
position: absolute;
|
||||
transform: translateX(-50%);
|
||||
border-radius: 12px;
|
||||
margin-left: 12px;
|
||||
margin-top: 3px;
|
||||
padding: 0px;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
@@ -540,28 +534,29 @@ input[type=range]:active + .sliderbubble {
|
||||
display: inline;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
#wwrap, #wbal {
|
||||
/* hide color controls until enabled in updateUI() */
|
||||
#pwrap, #wwrap, #wbal, #rgbwrap, #palwrap {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Slider wrapper div */
|
||||
.sliderwrap {
|
||||
height: 30px;
|
||||
width: 240px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Segment power button + brightness slider wrapper div */
|
||||
.sbs {
|
||||
margin: 0px -20px 5px -6px;
|
||||
}
|
||||
|
||||
/* Segment brightness slider wrapper div */
|
||||
.sws {
|
||||
width: 230px;
|
||||
}
|
||||
|
||||
.sis {
|
||||
width: 214px !important;
|
||||
margin-left: -7px;
|
||||
}
|
||||
|
||||
/* Dynamically hide brightness slider label */
|
||||
.hd {
|
||||
display: var(--bhd);
|
||||
}
|
||||
@@ -576,10 +571,6 @@ input[type=range]:active + .sliderbubble {
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
#rgbwrap {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 8px;
|
||||
margin: 10px;
|
||||
@@ -587,7 +578,7 @@ input[type=range]:active + .sliderbubble {
|
||||
font-size: 19px;
|
||||
background-color: var(--c-3);
|
||||
color: var(--c-f);
|
||||
cursor: pointer;
|
||||
cursor: pointer;
|
||||
border: 0px solid white;
|
||||
border-radius: 25px;
|
||||
transition-duration: 0.5s;
|
||||
@@ -595,6 +586,14 @@ input[type=range]:active + .sliderbubble {
|
||||
-webkit-transform:translate3d(0,0,0);*/
|
||||
}
|
||||
|
||||
/* Small round button (color selectors, icon-only round buttons) */
|
||||
.xxs {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
/* Segments/presets auxiliary buttons (Add segment, Create preset, ...) */
|
||||
.btn-s {
|
||||
width: 276px;
|
||||
background-color: var(--c-2);
|
||||
@@ -606,61 +605,80 @@ input[type=range]:active + .sliderbubble {
|
||||
margin: 0px 8px 4px 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.btna-icon {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* Wide button used in presets (Save, playlist test, delete) */
|
||||
.btn-p {
|
||||
width: 216px;
|
||||
}
|
||||
.btn-xs {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
margin: 2px 0 0 0;
|
||||
|
||||
/* Delete preset from playlist button */
|
||||
.btn-pl-del {
|
||||
margin: 0 0 0 3px;
|
||||
}
|
||||
|
||||
/* Add preset to playlist "+" button */
|
||||
.btn-pl-add {
|
||||
margin-left: 5px;
|
||||
margin: 3px 0 0 8px;
|
||||
}
|
||||
|
||||
|
||||
/* Quick color select buttons wrapper div */
|
||||
#qcs-w {
|
||||
margin-top: 10px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Quick color select buttons */
|
||||
.qcs {
|
||||
padding: 14px;
|
||||
margin: 2px;
|
||||
border-radius: 14px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Quick color select Black button (has white border) */
|
||||
.qcsb {
|
||||
padding: 13px;
|
||||
border: 1px solid var(--c-f);
|
||||
}
|
||||
|
||||
/* Hex color input wrapper div */
|
||||
#hexw {
|
||||
margin-top: 5px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Transition time input */
|
||||
#tt {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Color slot select buttons (1,2,3) */
|
||||
.cl {
|
||||
width: 42px;
|
||||
width: 38.5px;
|
||||
height: 38.5px;
|
||||
margin: 7px;
|
||||
background-color: #000;
|
||||
box-shadow: 0 0 0 1.5px #fff;
|
||||
}
|
||||
.selected.cl {
|
||||
box-shadow: 0 0 0 5px #fff;
|
||||
}
|
||||
|
||||
/* Playlist preset select */
|
||||
.sel-pl {
|
||||
width: 192px;
|
||||
background-position: 168px 16px;
|
||||
margin: 8px 3px 0 0;
|
||||
width: 192px;
|
||||
background-position: 168px 16px;
|
||||
margin: 8px 3px 0 0;
|
||||
}
|
||||
|
||||
/* Playlist end preset select */
|
||||
.sel-ple {
|
||||
width: 216px;
|
||||
background-position: 192px 16px;
|
||||
background-position: 192px 16px;
|
||||
}
|
||||
|
||||
select {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' fill='white'><polygon points='0,0 100,0 50,50'/></svg>") no-repeat;
|
||||
background-size: 12px;
|
||||
@@ -691,7 +709,6 @@ input[type=number], input[type=text] {
|
||||
outline: none;
|
||||
width: 50px;
|
||||
-webkit-appearance: textfield;
|
||||
-moz-appearance: textfield;
|
||||
appearance: textfield;
|
||||
}
|
||||
|
||||
@@ -708,7 +725,7 @@ textarea {
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: var(--c-b);
|
||||
background: var(--c-b);
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
@@ -716,18 +733,18 @@ input[type=text] {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ptxt {
|
||||
width: 200px !important;
|
||||
margin: 26px 0 6px 12px !important;
|
||||
input[type=text].ptxt {
|
||||
width: 200px;
|
||||
margin: 26px 0 6px 12px;
|
||||
}
|
||||
|
||||
.stxt {
|
||||
display: none;
|
||||
margin-top: 6px !important;
|
||||
input[type=text].stxt {
|
||||
display: none;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.qltxt {
|
||||
width: 50px !important;
|
||||
input[type=text].qltxt {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
input[type=number]:focus, input[type=text]:focus {
|
||||
@@ -756,42 +773,50 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
}
|
||||
|
||||
.segn {
|
||||
border-radius: 5px !important;
|
||||
border-radius: 5px !important;
|
||||
margin: 3px 0 6px 0 !important;
|
||||
}
|
||||
|
||||
.segname {
|
||||
.pname, .plname, .segname {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 50%;
|
||||
padding: 9px 0;
|
||||
transform: translateX(-50%);
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.segntxt {
|
||||
max-width: 160px;
|
||||
overflow: hidden;
|
||||
text-overflow: clip;
|
||||
max-width: 160px;
|
||||
overflow: hidden;
|
||||
text-overflow: clip;
|
||||
}
|
||||
|
||||
.pname {
|
||||
.segname {
|
||||
top: 0px;
|
||||
padding: 9px 0;
|
||||
}
|
||||
.pname, .plname {
|
||||
width: 208px;
|
||||
padding: 8px 0;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
text-overflow: clip;
|
||||
}
|
||||
.pname {
|
||||
top: 1px;
|
||||
}
|
||||
.plname {
|
||||
top:0;
|
||||
}
|
||||
|
||||
.pid {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
padding: 11px 0px 0px 11px;
|
||||
font-size: 16px;
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
padding: 11px 0px 0px 11px;
|
||||
font-size: 16px;
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
color: var(--c-b);
|
||||
}
|
||||
|
||||
@@ -803,11 +828,7 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
padding: 6px 0 0 0;
|
||||
}
|
||||
|
||||
.xxs {
|
||||
width: 40px;
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
/* Quick preset select buttons */
|
||||
.psts {
|
||||
background-color: var(--c-3);
|
||||
color: var(--c-f);
|
||||
@@ -816,20 +837,17 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
/* Segment apply button (checkmark) */
|
||||
.cnf {
|
||||
color: var(--c-f);
|
||||
cursor: pointer;
|
||||
background: var(--c-3);
|
||||
border-radius: 5px;
|
||||
padding: 8.5px 21px 5px;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.cnf-s {
|
||||
position: absolute;
|
||||
bottom: 100px;
|
||||
right: 23px;
|
||||
padding: 7px 22px;
|
||||
}
|
||||
|
||||
/* Segment power button icon */
|
||||
.pwr {
|
||||
color: var(--c-6);
|
||||
transform: translate(2px, 3px);
|
||||
@@ -840,17 +858,23 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
color: var(--c-f);
|
||||
}
|
||||
|
||||
.half {
|
||||
padding: 7.5px;
|
||||
bottom: 35px;
|
||||
}
|
||||
|
||||
.del {
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
right: 8px;
|
||||
color: var(--c-f);
|
||||
}
|
||||
|
||||
.frz {
|
||||
left: 36px;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
/* TODO expanded does not seem to apply to .frz, what is this for? */
|
||||
.expanded .frz {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.check, .radio {
|
||||
@@ -874,9 +898,9 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
}
|
||||
|
||||
.fxchkl {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 8px;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 8px;
|
||||
}
|
||||
|
||||
.check input, .radio input {
|
||||
@@ -889,19 +913,22 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
|
||||
.checkmark, .radiomark {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
background-color: var(--c-3);
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
background-color: var(--c-4);
|
||||
border-radius: 10px;
|
||||
/*border: 1px solid var(--c-2);*/
|
||||
}
|
||||
|
||||
.checkmark {
|
||||
top: 6px;
|
||||
}
|
||||
|
||||
.radiomark {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
border-radius: 50%;
|
||||
background-color: transparent;
|
||||
background-color: transparent;
|
||||
top: 7px;
|
||||
}
|
||||
|
||||
.schk {
|
||||
@@ -923,7 +950,7 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
|
||||
|
||||
.check:hover input ~ .checkmark {
|
||||
background-color: var(--c-4);
|
||||
background-color: var(--c-5);
|
||||
}
|
||||
|
||||
.check input:checked ~ .checkmark {
|
||||
@@ -941,8 +968,8 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
}
|
||||
|
||||
.check .checkmark:after {
|
||||
left: 9px;
|
||||
top: 5px;
|
||||
left: 8px;
|
||||
top: 4px;
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
border: solid var(--c-f);
|
||||
@@ -972,7 +999,7 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.seg {
|
||||
.seg, .pres {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
padding: 8px;
|
||||
@@ -985,29 +1012,41 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
border-radius: 20px;
|
||||
text-align: left;
|
||||
transition: background-color 0.5s;
|
||||
filter: brightness(1);
|
||||
filter: brightness(1); /* required for slider background to render? */
|
||||
}
|
||||
|
||||
.selected {
|
||||
background-color: var(--c-4);
|
||||
}
|
||||
/* "selected" CSS class is applied to the segment when it is the main segment.
|
||||
By default, do not highlight. Can be overridden by skin.css */
|
||||
.selected.seg {
|
||||
background-color: var(--c-2); /* var(--c-4); */
|
||||
}
|
||||
.selected .checkmark, .selected .radiokmark {
|
||||
background-color: var(--c-4); /* var(--c-6); */
|
||||
}
|
||||
|
||||
.list {
|
||||
position: relative;
|
||||
transition: background-color 0.5s;
|
||||
margin: auto auto 10px;
|
||||
padding-bottom: 10px;
|
||||
width: 230px;
|
||||
margin: auto auto 10px;
|
||||
padding-bottom: 10px;
|
||||
width: 230px;
|
||||
}
|
||||
|
||||
.lstI {
|
||||
position: sticky;
|
||||
overflow: hidden;
|
||||
position: sticky;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.fxbtn {
|
||||
margin: 20px auto;
|
||||
padding: 8px 0;
|
||||
margin: 20px auto;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.lstI:hover {
|
||||
background: var(--c-4);
|
||||
background: var(--c-4);
|
||||
}
|
||||
|
||||
.lstI:last-child {
|
||||
@@ -1030,7 +1069,7 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
.lstI.selected {
|
||||
background: var(--c-5);
|
||||
top: 95px;
|
||||
bottom: -11px;
|
||||
bottom: -11px;
|
||||
}
|
||||
|
||||
.lstI.sticky {
|
||||
@@ -1041,26 +1080,26 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
margin: 3px 0;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
font-size: 19px;
|
||||
font-size: 19px;
|
||||
}
|
||||
|
||||
.lstIprev {
|
||||
width: 220px;
|
||||
height: 5px;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
left: 5px;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
left: 5px;
|
||||
}
|
||||
|
||||
input[type="text"].search {
|
||||
display: block;
|
||||
width: 230px;
|
||||
box-sizing: border-box;
|
||||
padding: 8px 8px 9px 38px;
|
||||
margin: 6px auto 0 auto;
|
||||
padding: 8px 8px 9px 38px;
|
||||
margin: 6px auto 0 auto;
|
||||
text-align: left;
|
||||
background-color: var(--c-3);
|
||||
background-color: var(--c-3);
|
||||
}
|
||||
|
||||
input[type="text"].search:focus {
|
||||
@@ -1098,9 +1137,13 @@ input[type="text"].search:not(:placeholder-shown) {
|
||||
}
|
||||
|
||||
.hrz {
|
||||
width: auto;
|
||||
height: 2px;
|
||||
background-color: var(--c-b);
|
||||
width: auto;
|
||||
height: 2px;
|
||||
background-color: var(--c-b);
|
||||
}
|
||||
|
||||
.no-margin {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
@@ -1120,8 +1163,8 @@ input[type="text"].search:not(:placeholder-shown) {
|
||||
|
||||
@media all and (max-width: 335px) {
|
||||
.sliderbubble {
|
||||
display: none;
|
||||
}
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 550px) and (min-width: 374px) {
|
||||
@@ -1146,6 +1189,6 @@ input[type="text"].search:not(:placeholder-shown) {
|
||||
|
||||
@media all and (max-width: 1249px) {
|
||||
#buttonPcm {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,13 +44,15 @@
|
||||
|
||||
<div class ="container">
|
||||
<div id="Colors" class="tabcontent">
|
||||
<div id="picker" class="noslide"></div>
|
||||
<div id="vwrap">
|
||||
<div class="sliderwrap il" id="vwrap">
|
||||
<input id="sliderV" class="noslide" oninput="fromV()" onchange="setColor(0)" max="100" min="0" type="range" value="128" step="any" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div><br>
|
||||
</div>
|
||||
<div id="pwrap">
|
||||
<div id="picker" class="noslide"></div>
|
||||
<div id="vwrap">
|
||||
<div class="sliderwrap il" id="vwrap">
|
||||
<input id="sliderV" class="noslide" oninput="fromV()" onchange="setColor(0)" max="100" min="0" type="range" value="128" step="any" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div><br>
|
||||
</div>
|
||||
</div>
|
||||
<div id="kwrap">
|
||||
<div class="sliderwrap il">
|
||||
<input id="sliderK" class="noslide" oninput="fromK()" onchange="setColor(0)" max="10091" min="1900" type="range" value="6550" />
|
||||
@@ -106,34 +108,28 @@
|
||||
</div>
|
||||
<div id="hexw">
|
||||
<input id="hexc" type="text" class="noslide" onkeydown="hexEnter()" autocomplete="off" maxlength="8" />
|
||||
<button id="hexcnf" class="xxs btn" onclick="fromHex();"><i class="icons btna-icon"></i></button>
|
||||
<button id="hexcnf" class="xxs btn" onclick="fromHex();"><i class="icons no-margin"></i></button>
|
||||
</div>
|
||||
<p class="labels">
|
||||
<i class="icons sel-icon" onclick="tglHex()"></i>
|
||||
Color palette
|
||||
</p>
|
||||
<div class="il">
|
||||
<div id="pallist" class="list">
|
||||
<div class="lstI" data-id="0">
|
||||
<label class="check schkl">
|
||||
|
||||
<input type="radio" value="${palettes[i].id}" name="palette" onChange="setPalette()">
|
||||
<span class="radiomark schk"></span>
|
||||
</label>
|
||||
<div class="lstIcontent">
|
||||
<span class="lstIname">
|
||||
Default
|
||||
</span>
|
||||
<div id="palwrap">
|
||||
<p class="labels">
|
||||
<i class="icons sel-icon" onclick="tglHex()"></i>
|
||||
Color palette
|
||||
</p>
|
||||
<div class="il">
|
||||
<div id="pallist" class="list">
|
||||
<div class="lstI" data-id="0">
|
||||
<label class="check schkl">
|
||||
|
||||
<input type="radio" value="${palettes[i].id}" name="palette" onChange="setPalette()">
|
||||
<span class="radiomark schk"></span>
|
||||
</label>
|
||||
<div class="lstIcontent">
|
||||
<span class="lstIname">
|
||||
Default
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lstI">
|
||||
<div class="lstIcontent">
|
||||
<span class="lstIname">
|
||||
Loading...
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -141,7 +137,7 @@
|
||||
<div id="Effects" class="tabcontent">
|
||||
<p class="labels">Effect speed</p>
|
||||
<div class="staytop">
|
||||
<i class="icons slider-icon" onclick="tglFreeze()"></i>
|
||||
<i class="icons slider-icon" style="cursor: pointer;" title="Freeze" onclick="tglFreeze()"></i>
|
||||
<div class="sliderwrap il">
|
||||
<input id="sliderSpeed" class="noslide" onchange="setSpeed()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
|
||||
<output class="sliderbubble hidden"></output>
|
||||
@@ -227,7 +223,6 @@
|
||||
<button class="btn" onclick="setLor(2)">Override until reboot</button><br>
|
||||
<span class="h">For best performance, it is recommended to turn off the streaming source when not in use.</span>
|
||||
</div>
|
||||
|
||||
<i id="roverstar" class="icons huge" onclick="setLor(0)"></i><br>
|
||||
<script src="iro.js"></script>
|
||||
<script src="rangetouch.js"></script>
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
//page js
|
||||
var loc = false, locip;
|
||||
var noNewSegs = false;
|
||||
var isOn = false, nlA = false, isLv = false, isInfo = false, isNodes = false, syncSend = false, syncTglRecv = true, isRgbw = false;
|
||||
var isOn = false, nlA = false, isLv = false, isInfo = false, isNodes = false, syncSend = false, syncTglRecv = true;
|
||||
var hasWhite = false, hasRGB = false, hasCCT = false;
|
||||
var whites = [0,0,0];
|
||||
var selColors;
|
||||
var colors = [[0,0,0],[0,0,0],[0,0,0]];
|
||||
var expanded = [false];
|
||||
var powered = [true];
|
||||
var nlDur = 60, nlTar = 0;
|
||||
var nlMode = false;
|
||||
var selectedFx = 0;
|
||||
var csel = 0;
|
||||
var csel = 0; // selected color slot (0-2)
|
||||
var currentPreset = -1;
|
||||
var lastUpdate = 0;
|
||||
var segCount = 0, ledCount = 0, lowestUnused = 0, maxSeg = 0, lSeg = 0;
|
||||
@@ -27,7 +28,7 @@ var fxlist = d.getElementById('fxlist'), pallist = d.getElementById('pallist');
|
||||
var cfg = {
|
||||
theme:{base:"dark", bg:{url:""}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}},
|
||||
comp :{colors:{picker: true, rgb: false, quick: true, hex: false},
|
||||
labels:true, pcmbot:false, pid:true, seglen:false, css:true, hdays:false}
|
||||
labels:true, pcmbot:false, pid:true, seglen:false, css:true, hdays:false}
|
||||
};
|
||||
var hol = [
|
||||
[0,11,24,4,"https://aircoookie.github.io/xmas.png"], // christmas
|
||||
@@ -40,14 +41,14 @@ var hol = [
|
||||
var cpick = new iro.ColorPicker("#picker", {
|
||||
width: 260,
|
||||
wheelLightness: false,
|
||||
wheelAngle: 270,
|
||||
wheelDirection: "clockwise",
|
||||
layout: [
|
||||
{
|
||||
component: iro.ui.Wheel,
|
||||
options: {}
|
||||
}
|
||||
]
|
||||
wheelAngle: 270,
|
||||
wheelDirection: "clockwise",
|
||||
layout: [
|
||||
{
|
||||
component: iro.ui.Wheel,
|
||||
options: {}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
function handleVisibilityChange() {
|
||||
@@ -60,27 +61,48 @@ function sCol(na, col) {
|
||||
d.documentElement.style.setProperty(na, col);
|
||||
}
|
||||
|
||||
function isRgbBlack(a, s) {
|
||||
return (a[s][0] == 0 && a[s][1] == 0 && a[s][2] == 0);
|
||||
}
|
||||
|
||||
// returns RGB color from a given slot s 0-2 from color array a
|
||||
function rgbStr(a, s) {
|
||||
return "rgb(" + a[s][0] + "," + a[s][1] + "," + a[s][2] + ")";
|
||||
}
|
||||
|
||||
// brightness approximation for selecting white as text color if background bri < 127, and black if higher
|
||||
function rgbBri(a, s) {
|
||||
var R = a[s][0], G = a[s][1], B = a[s][2];
|
||||
return 0.2126*R + 0.7152*G + 0.0722*B;
|
||||
}
|
||||
|
||||
// sets background of color slot selectors
|
||||
function setCSL(s) {
|
||||
var cd = d.getElementsByClassName('cl')[s];
|
||||
var w = whites[s];
|
||||
if (hasRGB && !isRgbBlack(colors, s)) {
|
||||
cd.style.background = rgbStr(colors, s);
|
||||
cd.style.color = rgbBri(colors, s) > 127 ? "#000":"#fff";
|
||||
if (hasWhite && w > 0) {
|
||||
cd.style.background = `linear-gradient(180deg, ${rgbStr(colors, s)} 30%, ${rgbStr([[w,w,w]], 0)})`;
|
||||
}
|
||||
} else {
|
||||
if (!hasWhite) w = 0;
|
||||
cd.style.background = rgbStr([[w,w,w]], 0);
|
||||
cd.style.color = w > 127 ? "#000":"#fff";
|
||||
}
|
||||
}
|
||||
|
||||
function applyCfg()
|
||||
{
|
||||
cTheme(cfg.theme.base === "light");
|
||||
var bg = cfg.theme.color.bg;
|
||||
if (bg) sCol('--c-1', bg);
|
||||
var ccfg = cfg.comp.colors;
|
||||
d.getElementById('hexw').style.display = ccfg.hex ? "block":"none";
|
||||
d.getElementById('picker').style.display = ccfg.picker ? "block":"none";
|
||||
d.getElementById('vwrap').style.display = ccfg.picker ? "block":"none";
|
||||
d.getElementById('kwrap').style.display = ccfg.picker ? "block":"none";
|
||||
d.getElementById('rgbwrap').style.display = ccfg.rgb ? "block":"none";
|
||||
d.getElementById('qcs-w').style.display = ccfg.quick ? "block":"none";
|
||||
if (lastinfo.leds) updateUI(); // update component visibility
|
||||
var l = cfg.comp.labels;
|
||||
var e = d.querySelectorAll('.tab-label');
|
||||
for (var i=0; i<e.length; i++)
|
||||
e[i].style.display = l ? "block":"none";
|
||||
e = d.querySelector('.hd');
|
||||
e.style.display = l ? "block":"none";
|
||||
sCol('--tbp',l ? "14px 14px 10px 14px":"10px 22px 4px 22px");
|
||||
sCol('--bbp',l ? "9px 0 7px 0":"10px 0 4px 0");
|
||||
sCol('--bhd',l ? "block":"none");
|
||||
sCol('--bhd',l ? "block":"none"); // hides/shows button labels
|
||||
sCol('--bmt',l ? "0px":"5px");
|
||||
sCol('--t-b', cfg.theme.alpha.tab);
|
||||
size();
|
||||
@@ -107,45 +129,45 @@ function tglLabels()
|
||||
|
||||
function cTheme(light) {
|
||||
if (light) {
|
||||
sCol('--c-1','#eee');
|
||||
sCol('--c-f','#000');
|
||||
sCol('--c-2','#ddd');
|
||||
sCol('--c-3','#bbb');
|
||||
sCol('--c-4','#aaa');
|
||||
sCol('--c-5','#999');
|
||||
sCol('--c-6','#999');
|
||||
sCol('--c-8','#888');
|
||||
sCol('--c-b','#444');
|
||||
sCol('--c-c','#333');
|
||||
sCol('--c-e','#111');
|
||||
sCol('--c-d','#222');
|
||||
sCol('--c-r','#c42');
|
||||
sCol('--c-o','rgba(204, 204, 204, 0.9)');
|
||||
sCol('--c-sb','#0003'); sCol('--c-sbh','#0006');
|
||||
sCol('--c-tb','rgba(204, 204, 204, var(--t-b))');
|
||||
sCol('--c-tba','rgba(170, 170, 170, var(--t-b))');
|
||||
sCol('--c-tbh','rgba(204, 204, 204, var(--t-b))');
|
||||
d.getElementById('imgw').style.filter = "invert(0.8)";
|
||||
} else {
|
||||
sCol('--c-1','#111');
|
||||
sCol('--c-f','#fff');
|
||||
sCol('--c-2','#222');
|
||||
sCol('--c-3','#333');
|
||||
sCol('--c-4','#444');
|
||||
sCol('--c-5','#555');
|
||||
sCol('--c-6','#666');
|
||||
sCol('--c-8','#888');
|
||||
sCol('--c-b','#bbb');
|
||||
sCol('--c-c','#ccc');
|
||||
sCol('--c-e','#eee');
|
||||
sCol('--c-d','#ddd');
|
||||
sCol('--c-r','#831');
|
||||
sCol('--c-o','rgba(34, 34, 34, 0.9)');
|
||||
sCol('--c-sb','#fff3'); sCol('--c-sbh','#fff5');
|
||||
sCol('--c-tb','rgba(34, 34, 34, var(--t-b))');
|
||||
sCol('--c-tba','rgba(102, 102, 102, var(--t-b))');
|
||||
sCol('--c-tbh','rgba(51, 51, 51, var(--t-b))');
|
||||
d.getElementById('imgw').style.filter = "unset";
|
||||
sCol('--c-1','#eee');
|
||||
sCol('--c-f','#000');
|
||||
sCol('--c-2','#ddd');
|
||||
sCol('--c-3','#bbb');
|
||||
sCol('--c-4','#aaa');
|
||||
sCol('--c-5','#999');
|
||||
sCol('--c-6','#999');
|
||||
sCol('--c-8','#888');
|
||||
sCol('--c-b','#444');
|
||||
sCol('--c-c','#333');
|
||||
sCol('--c-e','#111');
|
||||
sCol('--c-d','#222');
|
||||
sCol('--c-r','#c42');
|
||||
sCol('--c-o','rgba(204, 204, 204, 0.9)');
|
||||
sCol('--c-sb','#0003'); sCol('--c-sbh','#0006');
|
||||
sCol('--c-tb','rgba(204, 204, 204, var(--t-b))');
|
||||
sCol('--c-tba','rgba(170, 170, 170, var(--t-b))');
|
||||
sCol('--c-tbh','rgba(204, 204, 204, var(--t-b))');
|
||||
d.getElementById('imgw').style.filter = "invert(0.8)";
|
||||
} else { // default dark theme
|
||||
sCol('--c-1','#111');
|
||||
sCol('--c-f','#fff');
|
||||
sCol('--c-2','#222');
|
||||
sCol('--c-3','#333');
|
||||
sCol('--c-4','#444');
|
||||
sCol('--c-5','#555');
|
||||
sCol('--c-6','#666');
|
||||
sCol('--c-8','#888');
|
||||
sCol('--c-b','#bbb');
|
||||
sCol('--c-c','#ccc');
|
||||
sCol('--c-e','#eee');
|
||||
sCol('--c-d','#ddd');
|
||||
sCol('--c-r','#831');
|
||||
sCol('--c-o','rgba(34, 34, 34, 0.9)');
|
||||
sCol('--c-sb','#fff3'); sCol('--c-sbh','#fff5');
|
||||
sCol('--c-tb','rgba(34, 34, 34, var(--t-b))');
|
||||
sCol('--c-tba','rgba(102, 102, 102, var(--t-b))');
|
||||
sCol('--c-tbh','rgba(51, 51, 51, var(--t-b))');
|
||||
d.getElementById('imgw').style.filter = "unset";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,10 +246,6 @@ function onLoad() {
|
||||
loadBg(cfg.theme.bg.url);
|
||||
if (cfg.comp.css) loadSkinCSS('skinCss');
|
||||
|
||||
var cd = d.getElementById('csl').children;
|
||||
for (var i = 0; i < cd.length; i++) {
|
||||
cd[i].style.backgroundColor = "rgb(0, 0, 0)";
|
||||
}
|
||||
selectSlot(0);
|
||||
updateTablinks(0);
|
||||
resetUtil();
|
||||
@@ -357,7 +375,7 @@ function cpBck() {
|
||||
copyText.select();
|
||||
copyText.setSelectionRange(0, 999999);
|
||||
d.execCommand("copy");
|
||||
|
||||
|
||||
showToast("Copied to clipboard!");
|
||||
}
|
||||
|
||||
@@ -370,7 +388,7 @@ function presetError(empty)
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
var cn = `<div class="seg c">`;
|
||||
var cn = `<div class="pres c">`;
|
||||
if (empty)
|
||||
cn += `You have no presets yet!`;
|
||||
else
|
||||
@@ -418,7 +436,7 @@ function loadPresets(callback = null)
|
||||
})
|
||||
.then(res => {
|
||||
if (!res.ok) {
|
||||
showErrorToast();
|
||||
showErrorToast();
|
||||
}
|
||||
return res.json();
|
||||
})
|
||||
@@ -479,9 +497,9 @@ function populatePresets(fromls)
|
||||
if (qll) pQL.push([i, qll]);
|
||||
is.push(i);
|
||||
|
||||
cn += `<div class="seg pres" id="p${i}o">`;
|
||||
cn += `<div class="pres" id="p${i}o">`;
|
||||
if (cfg.comp.pid) cn += `<div class="pid">${i}</div>`;
|
||||
cn += `<div class="segname pname" onclick="setPreset(${i})">${isPlaylist(i)?"<i class='icons btn-icon'></i>":""}${pName(i)}</div>
|
||||
cn += `<div class="${isPlaylist(i)?'plname':'pname'}" onclick="setPreset(${i})">${isPlaylist(i)?"<i class='icons btn-icon'></i>":""}${pName(i)}</div>
|
||||
<i class="icons e-icon flr ${expanded[i+100] ? "exp":""}" id="sege${i+100}" onclick="expand(${i+100})"></i>
|
||||
<div class="segin" id="seg${i+100}"></div>
|
||||
</div><br>`;
|
||||
@@ -524,7 +542,7 @@ function populateInfo(i)
|
||||
urows += inforow(k,val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var vcn = "Kuuhaku";
|
||||
if (i.ver.startsWith("0.13.")) vcn = "Toki";
|
||||
@@ -536,8 +554,8 @@ function populateInfo(i)
|
||||
${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")}
|
||||
${inforow("Uptime",getRuntimeStr(i.uptime))}
|
||||
${inforow("Free heap",heap," kB")}
|
||||
${inforow("Estimated current",pwru)}
|
||||
${inforow("Frames / second",i.leds.fps)}
|
||||
${inforow("Estimated current",pwru)}
|
||||
${inforow("Frames / second",i.leds.fps)}
|
||||
${inforow("MAC address",i.mac)}
|
||||
${inforow("Filesystem",i.fs.u + "/" + i.fs.t + " kB (" +Math.round(i.fs.u*100/i.fs.t) + "%)")}
|
||||
${inforow("Environment",i.arch + " " + i.core + " (" + i.lwip + ")")}
|
||||
@@ -548,6 +566,7 @@ function populateInfo(i)
|
||||
function populateSegments(s)
|
||||
{
|
||||
var cn = "";
|
||||
let li = lastinfo;
|
||||
segCount = 0; lowestUnused = 0; lSeg = 0;
|
||||
|
||||
for (var y = 0; y < (s.seg||[]).length; y++)
|
||||
@@ -560,12 +579,13 @@ function populateSegments(s)
|
||||
if (i == lowestUnused) lowestUnused = i+1;
|
||||
if (i > lSeg) lSeg = i;
|
||||
|
||||
cn += `<div class="seg">
|
||||
cn += `<div class="seg ${i==s.mainseg ? 'selected' : ''}">
|
||||
<label class="check schkl">
|
||||
|
||||
<input type="checkbox" id="seg${i}sel" onchange="selSeg(${i})" ${inst.sel ? "checked":""}>
|
||||
<span class="checkmark schk"></span>
|
||||
</label>
|
||||
<i class="icons e-icon frz" id="seg${i}frz" onclick="event.preventDefault();tglFreeze(${i});" style="display:${inst.frz?"inline":"none"}">&#x${li.live && li.liveseg==i?'e410':'e325'};</i>
|
||||
<div class="segname">
|
||||
<div class="segntxt" onclick="selSegEx(${i})">${inst.n ? inst.n : "Segment "+i}</div>
|
||||
<i class="icons edit-icon ${expanded[i] ? "expanded":""}" id="seg${i}nedit" onclick="tglSegn(${i})"></i>
|
||||
@@ -576,7 +596,7 @@ function populateSegments(s)
|
||||
<div class="sbs">
|
||||
<i class="icons e-icon pwr ${powered[i] ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})"></i>
|
||||
<div class="sliderwrap il sws">
|
||||
<input id="seg${i}bri" class="noslide sis" onchange="setSegBri(${i})" oninput="updateTrail(this)" max="255" min="1" type="range" value="${inst.bri}" />
|
||||
<input id="seg${i}bri" class="noslide" onchange="setSegBri(${i})" oninput="updateTrail(this)" max="255" min="1" type="range" value="${inst.bri}" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -601,7 +621,7 @@ function populateSegments(s)
|
||||
<tr>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${i}grp" type="number" min="1" max="255" value="${inst.grp}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${i}spc" type="number" min="0" max="255" value="${inst.spc}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
|
||||
<td class="segtd"><i class="icons e-icon cnf cnf-s" id="segc${i}" onclick="setSeg(${i})"></i></td>
|
||||
<td class="segtd"><i class="icons e-icon cnf" id="segc${i}" onclick="setSeg(${i})"></i></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="h" id="seg${i}len"></div>
|
||||
@@ -616,8 +636,8 @@ function populateSegments(s)
|
||||
<span class="checkmark schk"></span>
|
||||
</label>
|
||||
<div class="del">
|
||||
<button class="btn btn-i btn-xs" id="segr${i}" title="Repeat until end" onclick="rptSeg(${i})"><i class="icons btn-icon"></i></button>
|
||||
<button class="btn btn-i btn-xs" id="segd${i}" title="Delete" onclick="delSeg(${i})"><i class="icons btn-icon"></i></button>
|
||||
<button class="xxs btn no-margin" id="segr${i}" title="Repeat until end" onclick="rptSeg(${i})"><i class="icons no-margin"></i></button>
|
||||
<button class="xxs btn no-margin" id="segd${i}" title="Delete" onclick="delSeg(${i})"><i class="icons no-margin"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div><br>`;
|
||||
@@ -695,14 +715,13 @@ function populatePalettes(palettes)
|
||||
for (let i = 0; i < palettes.length; i++) {
|
||||
html += generateListItemHtml(
|
||||
'palette',
|
||||
palettes[i].id,
|
||||
palettes[i].name,
|
||||
'setPalette',
|
||||
palettes[i].id,
|
||||
palettes[i].name,
|
||||
'setPalette',
|
||||
`<div class="lstIprev" style="${genPalPrevCss(palettes[i].id)}"></div>`,
|
||||
palettes[i].class,
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
pallist.innerHTML=html;
|
||||
}
|
||||
|
||||
@@ -754,11 +773,11 @@ function genPalPrevCss(id)
|
||||
g = Math.random() * 255;
|
||||
b = Math.random() * 255;
|
||||
} else {
|
||||
if (selColors) {
|
||||
if (colors) {
|
||||
let pos = element[1] - 1;
|
||||
r = selColors[pos][0];
|
||||
g = selColors[pos][1];
|
||||
b = selColors[pos][2];
|
||||
r = colors[pos][0];
|
||||
g = colors[pos][1];
|
||||
b = colors[pos][2];
|
||||
}
|
||||
}
|
||||
if (index === false) {
|
||||
@@ -773,7 +792,7 @@ function genPalPrevCss(id)
|
||||
|
||||
function generateListItemHtml(listName, id, name, clickAction, extraHtml = '', extraClass = '')
|
||||
{
|
||||
return `<div class="lstI btn fxbtn ${extraClass}" data-id="${id}" onClick="${clickAction}(${id})">
|
||||
return `<div class="lstI btn fxbtn ${extraClass}" data-id="${id}" onClick="${clickAction}(${id})">
|
||||
<label class="radio fxchkl">
|
||||
<input type="radio" value="${id}" name="${listName}">
|
||||
<span class="radiomark"></span>
|
||||
@@ -784,32 +803,32 @@ function generateListItemHtml(listName, id, name, clickAction, extraHtml = '', e
|
||||
${extraHtml}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
|
||||
function btype(b){
|
||||
switch (b) {
|
||||
case 32: return "ESP32";
|
||||
case 82: return "ESP8266";
|
||||
}
|
||||
return "?";
|
||||
switch (b) {
|
||||
case 32: return "ESP32";
|
||||
case 82: return "ESP8266";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
function bname(o){
|
||||
if (o.name=="WLED") return o.ip;
|
||||
return o.name;
|
||||
if (o.name=="WLED") return o.ip;
|
||||
return o.name;
|
||||
}
|
||||
|
||||
function populateNodes(i,n)
|
||||
{
|
||||
var cn="";
|
||||
var urows="";
|
||||
var nnodes = 0;
|
||||
var nnodes = 0;
|
||||
if (n.nodes) {
|
||||
n.nodes.sort((a,b) => (a.name).localeCompare(b.name));
|
||||
for (var x=0;x<n.nodes.length;x++) {
|
||||
var o = n.nodes[x];
|
||||
if (o.name) {
|
||||
var url = `<button class="btn btna-icon tab" onclick="location.assign('http://${o.ip}');">${bname(o)}</button>`;
|
||||
var url = `<button class="btn no-margin tab" onclick="location.assign('http://${o.ip}');">${bname(o)}</button>`;
|
||||
urows += inforow(url,`${btype(o.type)}<br><i>${o.vid==0?"N/A":o.vid}</i>`);
|
||||
nnodes++;
|
||||
nnodes++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -904,20 +923,21 @@ function updateLen(s)
|
||||
//updates background color of currently selected preset
|
||||
function updatePA()
|
||||
{
|
||||
var ps = d.getElementsByClassName("seg"); //reset all preset buttons
|
||||
for (let i = 0; i < ps.length; i++) {
|
||||
ps[i].style.backgroundColor = "var(--c-2)";
|
||||
var ps = d.getElementsByClassName("pres"); //reset all preset buttons
|
||||
for (var i of ps) {
|
||||
i.classList.remove("selected");
|
||||
}
|
||||
ps = d.getElementsByClassName("psts"); //reset all quick selectors
|
||||
for (let i = 0; i < ps.length; i++) {
|
||||
ps[i].style.backgroundColor = "var(--c-2)";
|
||||
for (var i of ps) {
|
||||
i.classList.remove("selected");
|
||||
}
|
||||
if (currentPreset > 0) {
|
||||
var acv = d.getElementById(`p${currentPreset}o`);
|
||||
if (acv && !expanded[currentPreset+100])
|
||||
acv.style.background = "var(--c-6)"; //highlight current preset
|
||||
acv.classList.add("selected");
|
||||
acv = d.getElementById(`p${currentPreset}qlb`);
|
||||
if (acv) acv.style.background = "var(--c-6)"; //highlight quick selector
|
||||
if (acv)
|
||||
acv.classList.add("selected");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -930,9 +950,15 @@ function updateUI()
|
||||
updateTrail(d.getElementById('sliderBri'));
|
||||
updateTrail(d.getElementById('sliderSpeed'));
|
||||
updateTrail(d.getElementById('sliderIntensity'));
|
||||
d.getElementById('wwrap').style.display = (isRgbw) ? "block":"none";
|
||||
d.getElementById('wbal').style.display = (lastinfo.leds.cct) ? "block":"none";
|
||||
d.getElementById('kwrap').style.display = (lastinfo.leds.cct) ? "none":"block";
|
||||
d.getElementById('wwrap').style.display = (hasWhite) ? "block":"none";
|
||||
d.getElementById('wbal').style.display = (hasCCT) ? "block":"none";
|
||||
var ccfg = cfg.comp.colors;
|
||||
d.getElementById('hexw').style.display = ccfg.hex ? "block":"none";
|
||||
d.getElementById('pwrap').style.display = (hasRGB && ccfg.picker) ? "block":"none";
|
||||
d.getElementById('kwrap').style.display = (hasRGB && !hasCCT && ccfg.picker) ? "block":"none";
|
||||
d.getElementById('rgbwrap').style.display = (hasRGB && ccfg.rgb) ? "block":"none";
|
||||
d.getElementById('qcs-w').style.display = (hasRGB && ccfg.quick) ? "block":"none";
|
||||
d.getElementById('palwrap').style.display = hasRGB ? "block":"none";
|
||||
|
||||
updatePA();
|
||||
updatePSliders();
|
||||
@@ -940,7 +966,7 @@ function updateUI()
|
||||
|
||||
function displayRover(i,s)
|
||||
{
|
||||
d.getElementById('rover').style.transform = (i.live && s.lor == 0) ? "translateY(0px)":"translateY(100%)";
|
||||
d.getElementById('rover').style.transform = (i.live && s.lor == 0 && i.liveseg<0) ? "translateY(0px)":"translateY(100%)";
|
||||
var sour = i.lip ? i.lip:""; if (sour.length > 2) sour = " from " + sour;
|
||||
d.getElementById('lv').innerHTML = `WLED is receiving live ${i.lm} data${sour}`;
|
||||
d.getElementById('roverstar').style.display = (i.live && s.lor) ? "block":"none";
|
||||
@@ -952,7 +978,14 @@ function compare(a, b) {
|
||||
}
|
||||
function cmpP(a, b) {
|
||||
if (!a[1].n) return (a[0] > b[0]);
|
||||
return a[1].n.localeCompare(b[1].n,undefined, {numeric: true});
|
||||
//return a[1].n.localeCompare(b[1].n,undefined, {numeric: true});
|
||||
// sort playlists first, followed by presets with characters and last presets with special 1st character
|
||||
const c = a[1].n.charCodeAt(0);
|
||||
const d = b[1].n.charCodeAt(0);
|
||||
if ((c>47 && c<58) || (c>64 && c<91) || (c>96 && c<123) || c>255) x = '='; else x = '>';
|
||||
if ((d>47 && d<58) || (d>64 && d<91) || (d>96 && d<123) || d>255) y = '='; else y = '>';
|
||||
const n = (a[1].playlist ? '<' : x) + a[1].n;
|
||||
return n.localeCompare((b[1].playlist ? '<' : y) + b[1].n, undefined, {numeric: true});
|
||||
}
|
||||
|
||||
//forces a WebSockets reconnect if timeout (error toast), or successful HTTP response to JSON request
|
||||
@@ -964,10 +997,10 @@ function reconnectWS() {
|
||||
|
||||
function makeWS() {
|
||||
if (ws) return;
|
||||
ws = new WebSocket('ws://'+(loc?locip:window.location.hostname)+'/ws');
|
||||
ws = new WebSocket((window.location.protocol == 'https:'?'wss':'ws')+'://'+(loc?locip:window.location.hostname)+'/ws');
|
||||
ws.binaryType = "arraybuffer";
|
||||
ws.onmessage = function(event) {
|
||||
if (event.data instanceof ArrayBuffer) return; //liveview packet
|
||||
if (event.data instanceof ArrayBuffer) return; //liveview packet
|
||||
var json = JSON.parse(event.data);
|
||||
clearTimeout(jsonTimeout);
|
||||
jsonTimeout = null;
|
||||
@@ -996,90 +1029,108 @@ function makeWS() {
|
||||
}
|
||||
|
||||
function readState(s,command=false) {
|
||||
isOn = s.on;
|
||||
d.getElementById('sliderBri').value= s.bri;
|
||||
nlA = s.nl.on;
|
||||
nlDur = s.nl.dur;
|
||||
nlTar = s.nl.tbri;
|
||||
nlMode = s.nl.mode;
|
||||
syncSend = s.udpn.send;
|
||||
currentPreset = s.ps;
|
||||
tr = s.transition;
|
||||
d.getElementById('tt').value = tr/10;
|
||||
isOn = s.on;
|
||||
d.getElementById('sliderBri').value= s.bri;
|
||||
nlA = s.nl.on;
|
||||
nlDur = s.nl.dur;
|
||||
nlTar = s.nl.tbri;
|
||||
nlMode = s.nl.mode;
|
||||
syncSend = s.udpn.send;
|
||||
currentPreset = s.ps;
|
||||
tr = s.transition;
|
||||
d.getElementById('tt').value = tr/10;
|
||||
|
||||
var selc=0; var ind=0;
|
||||
populateSegments(s);
|
||||
for (let i = 0; i < (s.seg||[]).length; i++)
|
||||
{
|
||||
if(s.seg[i].sel) {selc = ind; break;} ind++;
|
||||
}
|
||||
var i=s.seg[selc];
|
||||
if (!i) {
|
||||
showToast('No Segments!', true);
|
||||
updateUI();
|
||||
return;
|
||||
}
|
||||
|
||||
selColors = i.col;
|
||||
var cd = d.getElementById('csl').children;
|
||||
for (let e = 2; e >= 0; e--)
|
||||
{
|
||||
cd[e].style.backgroundColor = "rgb(" + i.col[e][0] + "," + i.col[e][1] + "," + i.col[e][2] + ")";
|
||||
if (isRgbw) whites[e] = parseInt(i.col[e][3]);
|
||||
selectSlot(csel);
|
||||
}
|
||||
if (i.cct != null && i.cct>=0) d.getElementById("sliderA").value = i.cct;
|
||||
populateSegments(s);
|
||||
var selc=0;
|
||||
var sellvl=0; // 0: selc is invalid, 1: selc is mainseg, 2: selc is first selected
|
||||
hasRGB = hasWhite = hasCCT = false;
|
||||
for (let i = 0; i < (s.seg||[]).length; i++)
|
||||
{
|
||||
if (sellvl == 0 && s.seg[i].id == s.mainseg) {
|
||||
selc = i;
|
||||
sellvl = 1;
|
||||
}
|
||||
if (s.seg[i].sel) {
|
||||
if (sellvl < 2) selc = i; // get first selected segment
|
||||
sellvl = 2;
|
||||
var lc = lastinfo.leds.seglc[s.seg[i].id];
|
||||
hasRGB |= lc & 0x01;
|
||||
hasWhite |= lc & 0x02;
|
||||
hasCCT |= lc & 0x04;
|
||||
}
|
||||
}
|
||||
var i=s.seg[selc];
|
||||
if (sellvl == 1) {
|
||||
var lc = lastinfo.leds.seglc[i.id];
|
||||
hasRGB = lc & 0x01;
|
||||
hasWhite = lc & 0x02;
|
||||
hasCCT = lc & 0x04;
|
||||
}
|
||||
if (!i) {
|
||||
showToast('No Segments!', true);
|
||||
updateUI();
|
||||
return;
|
||||
}
|
||||
|
||||
colors = i.col;
|
||||
for (let e = 0; e < 3; e++)
|
||||
{
|
||||
if (i.col[e].length > 3) whites[e] = parseInt(i.col[e][3]);
|
||||
setCSL(e);
|
||||
}
|
||||
selectSlot(csel);
|
||||
if (i.cct != null && i.cct>=0) d.getElementById("sliderA").value = i.cct;
|
||||
|
||||
d.getElementById('sliderSpeed').value = i.sx;
|
||||
d.getElementById('sliderIntensity').value = i.ix;
|
||||
d.getElementById('sliderSpeed').value = i.sx;
|
||||
d.getElementById('sliderIntensity').value = i.ix;
|
||||
|
||||
// Effects
|
||||
var selFx = fxlist.querySelector(`input[name="fx"][value="${i.fx}"]`);
|
||||
if (selFx) selFx.checked = true;
|
||||
else location.reload(); //effect list is gone (e.g. if restoring tab). Reload.
|
||||
// Effects
|
||||
var selFx = fxlist.querySelector(`input[name="fx"][value="${i.fx}"]`);
|
||||
if (selFx) selFx.checked = true;
|
||||
else location.reload(); //effect list is gone (e.g. if restoring tab). Reload.
|
||||
|
||||
var selElement = fxlist.querySelector('.selected');
|
||||
if (selElement) {
|
||||
selElement.classList.remove('selected')
|
||||
}
|
||||
var selectedEffect = fxlist.querySelector(`.lstI[data-id="${i.fx}"]`);
|
||||
selectedEffect.classList.add('selected');
|
||||
selectedFx = i.fx;
|
||||
var selElement = fxlist.querySelector('.selected');
|
||||
if (selElement) {
|
||||
selElement.classList.remove('selected')
|
||||
}
|
||||
var selectedEffect = fxlist.querySelector(`.lstI[data-id="${i.fx}"]`);
|
||||
selectedEffect.classList.add('selected');
|
||||
selectedFx = i.fx;
|
||||
|
||||
// Palettes
|
||||
pallist.querySelector(`input[name="palette"][value="${i.pal}"]`).checked = true;
|
||||
selElement = pallist.querySelector('.selected');
|
||||
if (selElement) {
|
||||
selElement.classList.remove('selected')
|
||||
}
|
||||
pallist.querySelector(`.lstI[data-id="${i.pal}"]`).classList.add('selected');
|
||||
// Palettes
|
||||
pallist.querySelector(`input[name="palette"][value="${i.pal}"]`).checked = true;
|
||||
selElement = pallist.querySelector('.selected');
|
||||
if (selElement) {
|
||||
selElement.classList.remove('selected')
|
||||
}
|
||||
pallist.querySelector(`.lstI[data-id="${i.pal}"]`).classList.add('selected');
|
||||
|
||||
if (!command) {
|
||||
selectedEffect.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'nearest',
|
||||
});
|
||||
}
|
||||
if (!command) {
|
||||
selectedEffect.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'nearest',
|
||||
});
|
||||
}
|
||||
|
||||
if (s.error && s.error != 0) {
|
||||
var errstr = "";
|
||||
switch (s.error) {
|
||||
case 10:
|
||||
errstr = "Could not mount filesystem!";
|
||||
break;
|
||||
case 11:
|
||||
errstr = "Not enough space to save preset!";
|
||||
break;
|
||||
case 12:
|
||||
errstr = "Preset not found.";
|
||||
break;
|
||||
case 19:
|
||||
errstr = "A filesystem error has occured.";
|
||||
break;
|
||||
}
|
||||
showToast('Error ' + s.error + ": " + errstr, true);
|
||||
}
|
||||
updateUI();
|
||||
if (s.error && s.error != 0) {
|
||||
var errstr = "";
|
||||
switch (s.error) {
|
||||
case 10:
|
||||
errstr = "Could not mount filesystem!";
|
||||
break;
|
||||
case 11:
|
||||
errstr = "Not enough space to save preset!";
|
||||
break;
|
||||
case 12:
|
||||
errstr = "Preset not found.";
|
||||
break;
|
||||
case 19:
|
||||
errstr = "A filesystem error has occured.";
|
||||
break;
|
||||
}
|
||||
showToast('Error ' + s.error + ": " + errstr, true);
|
||||
}
|
||||
updateUI();
|
||||
}
|
||||
|
||||
var jsonTimeout;
|
||||
@@ -1102,15 +1153,15 @@ function requestJson(command, rinfo = true) {
|
||||
var type = command ? 'post':'get';
|
||||
if (command)
|
||||
{
|
||||
command.v = true; //get complete API response
|
||||
command.time = Math.floor(Date.now() / 1000);
|
||||
var t = d.getElementById('tt');
|
||||
if (t.validity.valid && command.transition===undefined) {
|
||||
var tn = parseInt(t.value*10);
|
||||
if (tn != tr) command.transition = tn;
|
||||
}
|
||||
command.v = true; //get complete API response
|
||||
command.time = Math.floor(Date.now() / 1000);
|
||||
var t = d.getElementById('tt');
|
||||
if (t.validity.valid && command.transition===undefined) {
|
||||
var tn = parseInt(t.value*10);
|
||||
if (tn != tr) command.transition = tn;
|
||||
}
|
||||
req = JSON.stringify(command);
|
||||
if (req.length > 1000) useWs = false; //do not send very long requests over websocket
|
||||
if (req.length > 1000) useWs = false; //do not send very long requests over websocket
|
||||
}
|
||||
|
||||
if (useWs) {
|
||||
@@ -1176,7 +1227,6 @@ function requestJson(command, rinfo = true) {
|
||||
name = "(L) " + name;
|
||||
}
|
||||
d.title = name;
|
||||
isRgbw = info.leds.wv;
|
||||
ledCount = info.leds.count;
|
||||
syncTglRecv = info.str;
|
||||
maxSeg = info.leds.maxseg;
|
||||
@@ -1243,7 +1293,7 @@ function toggleLiveview() {
|
||||
}
|
||||
|
||||
function toggleInfo() {
|
||||
if (isNodes) toggleNodes();
|
||||
if (isNodes) toggleNodes();
|
||||
isInfo = !isInfo;
|
||||
if (isInfo) populateInfo(lastinfo);
|
||||
d.getElementById('info').style.transform = (isInfo) ? "translateY(0px)":"translateY(100%)";
|
||||
@@ -1272,18 +1322,19 @@ function makeSeg() {
|
||||
<br>
|
||||
<div class="segin expanded">
|
||||
<input type="text" class="ptxt stxt noslide" id="seg${lowestUnused}t" autocomplete="off" maxlength=32 value="" placeholder="Enter name..."/>
|
||||
<table class="segt">
|
||||
<table class="infot">
|
||||
<tr>
|
||||
<td class="segtd">Start LED</td>
|
||||
<td class="segtd">${cfg.comp.seglen?"Length":"Stop LED"}</td>
|
||||
<td class="segtd">Apply</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${lowestUnused}s" type="number" min="0" max="${ledCount-1}" value="${ns}" oninput="updateLen(${lowestUnused})" onkeydown="segEnter(${lowestUnused})"></td>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${lowestUnused}e" type="number" min="0" max="${ledCount-(cfg.comp.seglen?ns:0)}" value="${ledCount-(cfg.comp.seglen?ns:0)}" oninput="updateLen(${lowestUnused})" onkeydown="segEnter(${lowestUnused})"></td>
|
||||
<td class="segtd"><i class="icons e-icon cnf" id="segc${lowestUnused}" onclick="setSeg(${lowestUnused}); resetUtil();"></i></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="h" id="seg${lowestUnused}len">${ledCount - ns} LED${ledCount - ns >1 ? "s":""}</div>
|
||||
<i class="icons e-icon cnf cnf-s half" id="segc${lowestUnused}" onclick="setSeg(${lowestUnused}); resetUtil();"></i>
|
||||
</div>
|
||||
</div>`;
|
||||
d.getElementById('segutil').innerHTML = cn;
|
||||
@@ -1380,8 +1431,8 @@ function plR(p) {
|
||||
}
|
||||
|
||||
function makeP(i,pl) {
|
||||
var content = "";
|
||||
if (pl) {
|
||||
var content = "";
|
||||
if (pl) {
|
||||
var rep = plJson[i].repeat ? plJson[i].repeat : 0;
|
||||
content = `<div class="first c">Playlist Entries</div>
|
||||
<div id="ple${i}"></div>
|
||||
@@ -1405,7 +1456,7 @@ function makeP(i,pl) {
|
||||
</div>
|
||||
<button class="btn btn-i btn-p" onclick="testPl(${i}, this)"><i class='icons btn-icon'></i>Test</button>`;
|
||||
}
|
||||
else content = `<label class="check revchkl">
|
||||
else content = `<label class="check revchkl">
|
||||
Include brightness
|
||||
<input type="checkbox" id="p${i}ibtgl" checked>
|
||||
<span class="checkmark schk"></span>
|
||||
@@ -1446,38 +1497,38 @@ ${(i>0)? ('<div class="h">ID ' +i+ '</div>'):""}`;
|
||||
}
|
||||
|
||||
function makePUtil() {
|
||||
d.getElementById('putil').innerHTML = `<div class="seg pres">
|
||||
<div class="segname newseg">
|
||||
d.getElementById('putil').innerHTML = `<div class="pres">
|
||||
<div class="pname newseg">
|
||||
New preset</div>
|
||||
<div class="segin expanded">
|
||||
${makeP(0)}</div></div>`;
|
||||
}
|
||||
|
||||
function makePlEntry(p,i) {
|
||||
return `
|
||||
<div class="plentry">
|
||||
<select class="btn sel sel-pl" onchange="plePs(${p},${i},this)" data-val=${plJson[p].ps[i]} data-index=${i}>
|
||||
return `
|
||||
<div class="plentry">
|
||||
<select class="btn sel sel-pl" onchange="plePs(${p},${i},this)" data-val=${plJson[p].ps[i]} data-index=${i}>
|
||||
${makePlSel()}
|
||||
</select>
|
||||
<button class="btn btn-i btn-xs btn-pl-del" onclick="delPl(${p},${i})"><i class="icons btn-icon"></i></button>
|
||||
</select>
|
||||
<button class="xxs btn btn-pl-del" onclick="delPl(${p},${i})"><i class="icons no-margin"></i></button>
|
||||
<div class="h plnl">Duration</div><div class="h plnl">Transition</div><div class="h pli">#${i+1}</div><br>
|
||||
<input class="noslide pln" type="number" max=6553.0 min=0.2 step=0.1 oninput="pleDur(${p},${i},this)" value=${plJson[p].dur[i]/10.0}>
|
||||
<input class="noslide pln" type="number" max=65.0 min=0.0 step=0.1 oninput="pleTr(${p},${i},this)" value=${plJson[p].transition[i]/10.0}> s
|
||||
<button class="btn btn-i btn-xs btn-pl-add" onclick="addPl(${p},${i})"><i class="icons btn-icon"></i></button>
|
||||
<div class="hrz"></div>
|
||||
</div>`;
|
||||
<button class="xxs btn btn-pl-add" onclick="addPl(${p},${i})"><i class="icons no-margin"></i></button>
|
||||
<div class="hrz"></div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function makePlUtil() {
|
||||
if (pNum < 1) {
|
||||
showToast("Please make a preset first!"); return;
|
||||
}
|
||||
if (pNum < 1) {
|
||||
showToast("Please make a preset first!"); return;
|
||||
}
|
||||
if (plJson[0].transition[0] < 0) plJson[0].transition[0] = tr;
|
||||
d.getElementById('putil').innerHTML = `<div class="seg pres">
|
||||
<div class="segname newseg">
|
||||
New playlist</div>
|
||||
<div class="segin expanded" id="seg100">
|
||||
${makeP(0,true)}</div></div>`;
|
||||
d.getElementById('putil').innerHTML = `<div class="pres">
|
||||
<div class="pname newseg">
|
||||
New playlist</div>
|
||||
<div class="segin expanded" id="seg100">
|
||||
${makeP(0,true)}</div></div>`;
|
||||
|
||||
refreshPlE(0);
|
||||
}
|
||||
@@ -1497,15 +1548,17 @@ function tglCs(i){
|
||||
function tglSegn(s)
|
||||
{
|
||||
d.getElementById(`seg${s}t`).style.display =
|
||||
(window.getComputedStyle(d.getElementById(`seg${s}t`)).display === "none") ? "inline":"none";
|
||||
(window.getComputedStyle(d.getElementById(`seg${s}t`)).display === "none") ? "inline":"none";
|
||||
}
|
||||
|
||||
// Select only the clicked segment and unselect all others
|
||||
function selSegEx(s)
|
||||
{
|
||||
var obj = {"seg":[]};
|
||||
for (let i=0; i<=lSeg; i++){
|
||||
obj.seg.push({"sel":(i==s)?true:false});
|
||||
}
|
||||
for (let i=0; i<=lSeg; i++) obj.seg.push({"id":i,"sel":(i==s)});
|
||||
// optionally, force mainseg to be first selected
|
||||
// WLED internally regards the first selected as mainseg regardless of this as long as any segment is selected
|
||||
//obj.mainseg = s;
|
||||
requestJson(obj);
|
||||
}
|
||||
|
||||
@@ -1520,7 +1573,7 @@ function rptSeg(s)
|
||||
var name = d.getElementById(`seg${s}t`).value;
|
||||
var start = parseInt(d.getElementById(`seg${s}s`).value);
|
||||
var stop = parseInt(d.getElementById(`seg${s}e`).value);
|
||||
if (stop == 0) {return;}
|
||||
if (stop == 0) return;
|
||||
var rev = d.getElementById(`seg${s}rev`).checked;
|
||||
var mi = d.getElementById(`seg${s}mi`).checked;
|
||||
var sel = d.getElementById(`seg${s}sel`).checked;
|
||||
@@ -1592,7 +1645,11 @@ function setSegBri(s){
|
||||
function tglFreeze(s=null)
|
||||
{
|
||||
var obj = {"seg": {"frz": "t"}}; // toggle
|
||||
if (s!==null) obj.id = s;
|
||||
if (s!==null) {
|
||||
obj.seg.id = s;
|
||||
// if live segment, enter live override (which also unfreezes)
|
||||
if (lastinfo && s==lastinfo.liveseg && lastinfo.live) obj = {"lor":1};
|
||||
}
|
||||
requestJson(obj);
|
||||
}
|
||||
|
||||
@@ -1748,16 +1805,12 @@ function delP(i) {
|
||||
|
||||
function selectSlot(b) {
|
||||
csel = b;
|
||||
var cd = d.getElementById('csl').children;
|
||||
for (let i = 0; i < cd.length; i++) {
|
||||
cd[i].style.border="2px solid white";
|
||||
cd[i].style.margin="5px";
|
||||
cd[i].style.width="42px";
|
||||
var cd = d.getElementsByClassName('cl');
|
||||
for (var i of cd) {
|
||||
i.classList.remove("selected");
|
||||
}
|
||||
cd[csel].style.border="5px solid white";
|
||||
cd[csel].style.margin="2px";
|
||||
cd[csel].style.width="50px";
|
||||
setPicker(cd[csel].style.backgroundColor);
|
||||
cd[csel].classList.add("selected");
|
||||
setPicker(rgbStr(colors, csel));
|
||||
//force slider update on initial load (picker "color:change" not fired if black)
|
||||
if (cpick.color.value == 0) updatePSliders();
|
||||
d.getElementById('sliderW').value = whites[csel];
|
||||
@@ -1792,7 +1845,7 @@ function updatePSliders() {
|
||||
s = d.getElementById('sliderB');
|
||||
s.value = col.b; updateTrail(s,3);
|
||||
|
||||
//update hex field
|
||||
//update hex field
|
||||
var str = cpick.color.hexString.substring(1);
|
||||
var w = whites[csel];
|
||||
if (w > 0) str += w.toString(16);
|
||||
@@ -1800,24 +1853,26 @@ function updatePSliders() {
|
||||
d.getElementById('hexcnf').style.backgroundColor = "var(--c-3)";
|
||||
|
||||
//update value slider
|
||||
var v = d.getElementById('sliderV');
|
||||
v.value = cpick.color.value;
|
||||
var v = d.getElementById('sliderV');
|
||||
v.value = cpick.color.value;
|
||||
//background color as if color had full value
|
||||
var hsv = {"h":cpick.color.hue,"s":cpick.color.saturation,"v":100};
|
||||
var c = iro.Color.hsvToRgb(hsv);
|
||||
var cs = 'rgb('+c.r+','+c.g+','+c.b+')';
|
||||
v.parentNode.getElementsByClassName('sliderdisplay')[0].style.setProperty('--bg',cs);
|
||||
updateTrail(v);
|
||||
var hsv = {"h":cpick.color.hue,"s":cpick.color.saturation,"v":100};
|
||||
var c = iro.Color.hsvToRgb(hsv);
|
||||
var cs = 'rgb('+c.r+','+c.g+','+c.b+')';
|
||||
v.parentNode.getElementsByClassName('sliderdisplay')[0].style.setProperty('--bg',cs);
|
||||
updateTrail(v);
|
||||
|
||||
//update Kelvin slider
|
||||
d.getElementById('sliderK').value = cpick.color.kelvin;
|
||||
// update Kelvin slider
|
||||
d.getElementById('sliderK').value = cpick.color.kelvin;
|
||||
}
|
||||
|
||||
// Fired when a key is pressed while in the HEX color input
|
||||
function hexEnter() {
|
||||
d.getElementById('hexcnf').style.backgroundColor = "var(--c-6)";
|
||||
if(event.keyCode == 13) fromHex();
|
||||
}
|
||||
|
||||
// Fired when a key is pressed while in a segment input
|
||||
function segEnter(s) {
|
||||
if(event.keyCode == 13) setSeg(s);
|
||||
}
|
||||
@@ -1861,17 +1916,13 @@ function fromRgb()
|
||||
|
||||
//sr 0: from RGB sliders, 1: from picker, 2: from hex
|
||||
function setColor(sr) {
|
||||
var cd = d.getElementById('csl').children;
|
||||
if (sr == 1 && cd[csel].style.backgroundColor == "rgb(0, 0, 0)") cpick.color.setChannel('hsv', 'v', 100);
|
||||
cd[csel].style.backgroundColor = cpick.color.rgbString;
|
||||
if (sr == 1 && colors[csel][0] == 0 && colors[csel][1] == 0 && colors[csel][2] == 0) cpick.color.setChannel('hsv', 'v', 100);
|
||||
if (sr != 2) whites[csel] = parseInt(d.getElementById('sliderW').value);
|
||||
var col = cpick.color.rgb;
|
||||
var obj = {"seg": {"col": [[col.r, col.g, col.b, whites[csel]],[],[]]}};
|
||||
if (csel == 1) {
|
||||
obj = {"seg": {"col": [[],[col.r, col.g, col.b, whites[csel]],[]]}};
|
||||
} else if (csel == 2) {
|
||||
obj = {"seg": {"col": [[],[],[col.r, col.g, col.b, whites[csel]]]}};
|
||||
}
|
||||
colors[csel] = [col.r, col.g, col.b, whites[csel]];
|
||||
setCSL(csel);
|
||||
var obj = {"seg": {"col": [[],[],[]]}};
|
||||
obj.seg.col[csel] = colors[csel];
|
||||
requestJson(obj);
|
||||
}
|
||||
|
||||
@@ -1934,7 +1985,6 @@ function loadPalettesData(callback = null)
|
||||
var d = new Date();
|
||||
if (palettesDataJson && palettesDataJson.vid == lastinfo.vid) {
|
||||
palettesData = palettesDataJson.p;
|
||||
//redrawPalPrev() //?
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
@@ -2001,10 +2051,10 @@ function search(searchField) {
|
||||
}
|
||||
|
||||
function cancelSearch(ic) {
|
||||
var searchField = ic.parentElement.getElementsByClassName('search')[0];
|
||||
searchField.value = "";
|
||||
search(searchField);
|
||||
searchField.focus();
|
||||
var searchField = ic.parentElement.getElementsByClassName('search')[0];
|
||||
searchField.value = "";
|
||||
search(searchField);
|
||||
searchField.focus();
|
||||
}
|
||||
|
||||
//make sure "dur" and "transition" are arrays with at least the length of "ps"
|
||||
@@ -2041,9 +2091,9 @@ function expand(i,a)
|
||||
d.getElementById('seg' +i).style.display = (expanded[i]) ? "block":"none";
|
||||
d.getElementById('sege' +i).style.transform = (expanded[i]) ? "rotate(180deg)":"rotate(0deg)";
|
||||
if (i < 100) {
|
||||
d.getElementById(`seg${i}nedit`).style.display = (expanded[i]) ? "inline":"none";
|
||||
return; //no preset, we are done
|
||||
}
|
||||
d.getElementById(`seg${i}nedit`).style.display = (expanded[i]) ? "inline":"none";
|
||||
return; //no preset, we are done
|
||||
}
|
||||
|
||||
var p = i-100;
|
||||
d.getElementById(`p${p}o`).style.background = (expanded[i] || p != currentPreset)?"var(--c-2)":"var(--c-6)";
|
||||
@@ -2111,9 +2161,9 @@ function move(e) {
|
||||
var f = +(s*dx/w).toFixed(2);
|
||||
|
||||
if ((clientX != 0) &&
|
||||
(iSlide > 0 || s < 0) && (iSlide < N - 1 || s > 0) &&
|
||||
f > 0.12 &&
|
||||
d.getElementsByClassName("tabcontent")[iSlide].scrollTop == scrollS) {
|
||||
(iSlide > 0 || s < 0) && (iSlide < N - 1 || s > 0) &&
|
||||
f > 0.12 &&
|
||||
d.getElementsByClassName("tabcontent")[iSlide].scrollTop == scrollS) {
|
||||
_C.style.setProperty('--i', iSlide -= s);
|
||||
f = 1 - f;
|
||||
updateTablinks(iSlide);
|
||||
|
||||
@@ -45,13 +45,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
var ws = top.window.ws;
|
||||
var ws;
|
||||
try {
|
||||
ws = top.window.ws;
|
||||
} catch (e) {}
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
console.info("Peek uses top WS");
|
||||
ws.send("{'lv':true}");
|
||||
} else {
|
||||
console.info("Peek WS opening");
|
||||
ws = new WebSocket("ws://"+document.location.host+"/ws");
|
||||
ws = new WebSocket((window.location.protocol == "https:"?"wss":"ws")+"://"+document.location.host+"/ws");
|
||||
ws.onopen = function () {
|
||||
console.info("Peek WS open");
|
||||
ws.send("{'lv':true}");
|
||||
|
||||
@@ -3,22 +3,15 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=500">
|
||||
<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,maxM=4000,maxPB=4096,maxL=1333,maxLbquot=0; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32
|
||||
var customStarts=false,startsDirty=[],maxCOOverrides=5;
|
||||
function H()
|
||||
{
|
||||
window.open("https://kno.wled.ge/features/settings/#led-settings");
|
||||
}
|
||||
function B()
|
||||
{
|
||||
window.open("/settings","_self");
|
||||
}
|
||||
function H(){window.open("https://kno.wled.ge/features/settings/#led-settings");}
|
||||
function B(){window.open("/settings","_self");}
|
||||
function gId(n){return d.getElementById(n);}
|
||||
function off(n){
|
||||
d.getElementsByName(n)[0].value = -1;
|
||||
}
|
||||
function off(n){d.getElementsByName(n)[0].value = -1;}
|
||||
var timeout;
|
||||
function showToast(text, error = false)
|
||||
{
|
||||
@@ -71,7 +64,6 @@
|
||||
if (bquot > 100) {var msg = "Too many LEDs for me to handle!"; if (maxM < 10000) msg += "\n\rConsider using an ESP32."; alert(msg);}
|
||||
if (d.Sf.checkValidity()) d.Sf.submit(); //https://stackoverflow.com/q/37323914
|
||||
}
|
||||
function S(){GetV();checkSi();setABL();}
|
||||
function enABL()
|
||||
{
|
||||
var en = gId('able').checked;
|
||||
@@ -104,9 +96,11 @@
|
||||
UI();
|
||||
}
|
||||
//returns mem usage
|
||||
function getMem(t, len, p0) {
|
||||
function getMem(t, n) {
|
||||
let len = parseInt(d.getElementsByName("LC"+n)[0].value);
|
||||
len += parseInt(d.getElementsByName("SL"+n)[0].value); // skipped LEDs are allocated too
|
||||
if (t < 32) {
|
||||
if (maxM < 10000 && p0==3) { //8266 DMA uses 5x the mem
|
||||
if (maxM < 10000 && d.getElementsByName("L0"+n)[0].value == 3) { //8266 DMA uses 5x the mem
|
||||
if (t > 29) return len*20; //RGBW
|
||||
return len*15;
|
||||
} else if (maxM >= 10000) //ESP32 RMT uses double buffer?
|
||||
@@ -141,7 +135,7 @@
|
||||
gId("p1d"+n).innerHTML = (t> 49 && t<64) ? "Clk GPIO:" : "";
|
||||
var LK = d.getElementsByName("L1"+n)[0]; // clock pin
|
||||
|
||||
memu += getMem(t, d.getElementsByName("LC"+n)[0].value, d.getElementsByName("L0"+n)[0].value); // calc memory
|
||||
memu += getMem(t, n); // calc memory
|
||||
|
||||
// enumerate pins
|
||||
for (p=1; p<5; p++) {
|
||||
@@ -165,7 +159,7 @@
|
||||
}
|
||||
gId("rf"+n).onclick = (t == 31) ? (function(){return false}) : (function(){}); // prevent change for TM1814
|
||||
isRGBW |= (t == 30 || t == 31 || (t > 40 && t < 46 && t != 43)); // RGBW checkbox, TYPE_xxxx values from const.h
|
||||
gId("co"+n).style.display = ((t>=80 && t<96) || t == 41 || t == 42) ? "none":"inline"; // hide color order for PWM W & WW/CW
|
||||
gId("co"+n).style.display = ((t>=80 && t<96) || (t > 40 && t < 48)) ? "none":"inline"; // hide color order for PWM
|
||||
gId("dig"+n+"c").style.display = (t > 40 && t < 48) ? "none":"inline"; // hide count for analog
|
||||
gId("dig"+n+"r").style.display = (t>=80 && t<96) ? "none":"inline"; // hide reversed for virtual
|
||||
gId("dig"+n+"s").style.display = ((t>=80 && t<96) || (t > 40 && t < 48)) ? "none":"inline"; // hide skip 1st for virtual & analog
|
||||
@@ -188,13 +182,13 @@
|
||||
var n = LCs[i].name.substring(2); // bus number
|
||||
// do we have a led count field
|
||||
if (nm=="LC") {
|
||||
var c=parseInt(LCs[i].value,10);
|
||||
if (!customStarts || !startsDirty[n]) gId("ls"+n).value=sLC;
|
||||
gId("ls"+n).disabled = !customStarts;
|
||||
var c=parseInt(LCs[i].value,10); //get LED count
|
||||
if (!customStarts || !startsDirty[n]) gId("ls"+n).value=sLC; //update start value
|
||||
gId("ls"+n).disabled = !customStarts; //enable/disable field editing
|
||||
if(c){
|
||||
var s = parseInt(gId("ls"+n).value);
|
||||
if (s+c > sLC) sLC = s+c;
|
||||
if(c>maxLC)maxLC=c;
|
||||
var s = parseInt(gId("ls"+n).value); //start value
|
||||
if (s+c > sLC) sLC = s+c; //update total count
|
||||
if(c>maxLC)maxLC=c; //max per output
|
||||
var t = parseInt(d.getElementsByName("LT"+n)[0].value); // LED type SELECT
|
||||
if (t<80) sPC+=c; //virtual out busses do not count towards physical LEDs
|
||||
} // increase led count
|
||||
@@ -303,6 +297,7 @@ ${i+1}:
|
||||
<option value="50">WS2801</option>
|
||||
<option value="51">APA102</option>
|
||||
<option value="52">LPD8806</option>
|
||||
<option value="54">LPD6803</option>
|
||||
<option value="53">P9813</option>
|
||||
<option value="41">PWM White</option>
|
||||
<option value="42">PWM CCT</option>
|
||||
@@ -313,7 +308,7 @@ ${i+1}:
|
||||
<option value="80">DDP RGB (network)</option>
|
||||
<!--option value="81">E1.31 RGB (network)</option-->
|
||||
<!--option value="82">ArtNet RGB (network)</option-->
|
||||
</select>
|
||||
</select><br>
|
||||
<div id="co${i}" style="display:inline">Color Order:
|
||||
<select name="CO${i}">
|
||||
<option value="0">GRB</option>
|
||||
@@ -322,8 +317,7 @@ ${i+1}:
|
||||
<option value="3">RBG</option>
|
||||
<option value="4">BGR</option>
|
||||
<option value="5">GBR</option>
|
||||
</select></div>
|
||||
<br>
|
||||
</select><br></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>
|
||||
@@ -333,7 +327,7 @@ ${i+1}:
|
||||
<span id="p3d${i}"></span><input type="number" name="L3${i}" min="0" max="33" class="xs" onchange="UI()"/>
|
||||
<span id="p4d${i}"></span><input type="number" name="L4${i}" min="0" max="33" class="xs" onchange="UI()"/>
|
||||
<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 1<sup>st</sup> LED: <input id="sl${i}" type="checkbox" name="SL${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>`;
|
||||
f.insertAdjacentHTML("beforeend", cn);
|
||||
@@ -369,6 +363,7 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
|
||||
gId("com_entries").insertAdjacentHTML("beforeend", b);
|
||||
gId("xo"+i).value = co;
|
||||
btnCOM(i+1);
|
||||
UI();
|
||||
}
|
||||
|
||||
function remCOM() {
|
||||
@@ -377,6 +372,7 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
|
||||
if (i === 0) return;
|
||||
entries[i-1].remove();
|
||||
btnCOM(i-1);
|
||||
UI();
|
||||
}
|
||||
|
||||
function resetCOM(_newMaxCOOverrides=undefined) {
|
||||
@@ -396,8 +392,8 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
|
||||
|
||||
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);;
|
||||
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" min="-1" max="40" name="${bt}" onchange="UI()" class="xs" value="${p}">`;
|
||||
c += ` <select name="${be}">`
|
||||
c += `<option value="0" ${t==0?"selected":""}>Disabled</option>`;
|
||||
@@ -463,7 +459,7 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
|
||||
let lines = e.target.result;
|
||||
var c = JSON.parse(lines);
|
||||
if (c.hw) {
|
||||
if (c.hw.led) {
|
||||
if (c.hw.led) {
|
||||
for (var i=0; i<10; i++) addLEDs(-1);
|
||||
var l = c.hw.led;
|
||||
l.ins.forEach((v,i,a)=>{
|
||||
@@ -473,7 +469,7 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
|
||||
d.getElementsByName("LS"+i)[0].value = v.start;
|
||||
d.getElementsByName("LC"+i)[0].value = v.len;
|
||||
d.getElementsByName("CO"+i)[0].value = v.order;
|
||||
d.getElementsByName("SL"+i)[0].checked = v.skip;
|
||||
d.getElementsByName("SL"+i)[0].value = v.skip;
|
||||
d.getElementsByName("RF"+i)[0].checked = v.ref;
|
||||
d.getElementsByName("CV"+i)[0].checked = v.rev;
|
||||
});
|
||||
@@ -504,6 +500,7 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
|
||||
}
|
||||
}
|
||||
}
|
||||
function S(){GetV();checkSi();setABL();}
|
||||
function GetV()
|
||||
{
|
||||
//values injected by server while sending HTML
|
||||
@@ -584,8 +581,8 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
|
||||
<option value=7>9-key red</option>
|
||||
<option value=8>JSON remote</option>
|
||||
</select><span style="cursor: pointer;" onclick="off('IR')"> ×</span><br>
|
||||
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"> <input type="button" value="Upload" onclick="uploadFile('/ir.json');"><br></div>
|
||||
<div id="toast"></div>
|
||||
<a href="https://kno.wled.ge/interfaces/infrared/" target="_blank">IR info</a><br>
|
||||
Relay GPIO: <input type="number" min="-1" max="33" name="RL" onchange="UI()" class="xs"> Invert <input type="checkbox" name="RM"><span style="cursor: pointer;" onclick="off('RL')"> ×</span><br>
|
||||
<hr style="width:260px">
|
||||
@@ -638,5 +635,6 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
|
||||
<hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
||||
</form>
|
||||
<div id="toast"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -95,7 +95,8 @@ Send notifications twice: <input type="checkbox" name="S2"><br>
|
||||
Enable instance list: <input type="checkbox" name="NL"><br>
|
||||
Make this instance discoverable: <input type="checkbox" name="NB">
|
||||
<h3>Realtime</h3>
|
||||
Receive UDP realtime: <input type="checkbox" name="RD"><br><br>
|
||||
Receive UDP realtime: <input type="checkbox" name="RD"><br>
|
||||
Use main segment only: <input type="checkbox" name="MO"><br><br>
|
||||
<i>Network DMX input</i><br>
|
||||
Type:
|
||||
<select name=DI onchange="SP(); adj();">
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<title>Time Settings</title>
|
||||
<script>
|
||||
var d=document;
|
||||
var el=false;
|
||||
var ms=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
|
||||
var cals = 'style="font-size:27px;margin-top:-6px;cursor:pointer"'; //hack as to not repeat CSS on all pages
|
||||
function H()
|
||||
@@ -31,26 +32,11 @@
|
||||
{
|
||||
var t = gId("WD"+i);
|
||||
t.style.display = t.style.display!=="none" ? "none" : "";
|
||||
o.innerHTML = t.style.display==="none" ? "🗓" : "✕";
|
||||
o.innerHTML = t.style.display==="none" ? "📅" : "✕";
|
||||
}
|
||||
function Cs()
|
||||
{
|
||||
gId("cac").style.display="none";
|
||||
gId("coc").style.display="block";
|
||||
gId("ccc").style.display="none";
|
||||
if (gId("ca").selected)
|
||||
{
|
||||
gId("cac").style.display="block";
|
||||
}
|
||||
if (gId("cc").selected)
|
||||
{
|
||||
gId("coc").style.display="none";
|
||||
gId("ccc").style.display="block";
|
||||
}
|
||||
if (gId("cn").selected)
|
||||
{
|
||||
gId("coc").style.display="none";
|
||||
}
|
||||
gId("cac").style.display=(gN("OL").checked)?"block":"none";
|
||||
}
|
||||
function BTa()
|
||||
{
|
||||
@@ -99,7 +85,7 @@
|
||||
for(j=0;j<8;j++) {
|
||||
gId("W"+i+j).checked=wd>>j&1;
|
||||
}
|
||||
if ((wd&127) != 127 || (i<8 && (gN("M"+i).value != 1 || gN("D"+i).value != 1 || gN("P"+i).value != 12 || gN("E"+i).value != 31))) {
|
||||
if ((wd&254) != 254 || (i<8 && (gN("M"+i).value != 1 || gN("D"+i).value != 1 || gN("P"+i).value != 12 || gN("E"+i).value != 31))) {
|
||||
expand(gId("CB"+i),i); //expand macros with custom DOW or date range set
|
||||
}
|
||||
}
|
||||
@@ -130,6 +116,20 @@
|
||||
td = tr.insertCell(3);
|
||||
td.innerHTML = `<input name="MD${b}" type="number" class="s" min="0" max="250" value="${d}" required>`;
|
||||
}
|
||||
function getLoc() {
|
||||
if (!el) {
|
||||
window.addEventListener("message", (event) => {
|
||||
if (event.origin !== "https://locate.wled.me") return;
|
||||
if (event.data instanceof Object) {
|
||||
d.Sf.LT.value = event.data.lat;
|
||||
d.Sf.LN.value = event.data.lon;
|
||||
updLoc();
|
||||
}
|
||||
}, false);
|
||||
el = true;
|
||||
}
|
||||
window.open("https://locate.wled.me","_blank");
|
||||
}
|
||||
function updLoc(i) {
|
||||
if (parseFloat(d.Sf.LT.value)<0) { d.Sf.LTR.value = "S"; d.Sf.LT.value = -1*parseFloat(d.Sf.LT.value); } else d.Sf.LTR.value = "N";
|
||||
if (parseFloat(d.Sf.LN.value)<0) { d.Sf.LNR.value = "W"; d.Sf.LN.value = -1*parseFloat(d.Sf.LN.value); } else d.Sf.LNR.value = "E";
|
||||
@@ -179,27 +179,18 @@
|
||||
UTC offset: <input name="UO" type="number" min="-65500" max="65500" required> seconds (max. 18 hours)<br>
|
||||
Current local time is <span class="times">unknown</span>.<br>
|
||||
Latitude: <select name="LTR"><option value="N">N</option><option value="S">S</option></select><input name="LT" type="number" class="xl" min="0" max="66.6" step="0.01"><br>
|
||||
Longitude: <select name="LNR"><option value="E">E</option><option value="W">W</option></select><input name="LN" type="number" class="xl" min="0" max="180" step="0.01">
|
||||
Longitude: <select name="LNR"><option value="E">E</option><option value="W">W</option></select><input name="LN" type="number" class="xl" min="0" max="180" step="0.01"><br>
|
||||
<button type="button" id="locbtn" onclick="getLoc()">Get location</button>
|
||||
<div><i>(opens new tab, only works in browser)</i></div>
|
||||
<div id="sun" class="times"></div>
|
||||
<h3>Clock</h3>
|
||||
Clock Overlay:
|
||||
<select name="OL" onchange="Cs()">
|
||||
<option value="0" id="cn" selected>None</option>
|
||||
<option value="1" id="ca">Analog Clock</option>
|
||||
<option value="2">Single Digit Clock</option>
|
||||
<option value="3" id="cc">Cronixie Clock</option>
|
||||
</select><br>
|
||||
<div id="coc">
|
||||
First LED: <input name="O1" type="number" min="0" max="255" required> Last LED: <input name="O2" type="number" min="0" max="255" required><br>
|
||||
<div id="cac">
|
||||
Analog Clock overlay: <input type="checkbox" name="OL" onchange="Cs()"><br>
|
||||
<div id="cac">
|
||||
First LED: <input name="O1" type="number" min="0" max="255" required> Last LED: <input name="O2" type="number" min="0" max="255" required><br>
|
||||
12h LED: <input name="OM" type="number" min="0" max="255" required><br>
|
||||
Show 5min marks: <input type="checkbox" name="O5"><br></div>
|
||||
Show 5min marks: <input type="checkbox" name="O5"><br>
|
||||
Seconds (as trail): <input type="checkbox" name="OS"><br>
|
||||
</div>
|
||||
<div id="ccc">
|
||||
Cronixie Display: <input name="CX" maxlength="6"><br>
|
||||
Cronixie Backlight: <input type="checkbox" name="CB"><br>
|
||||
</div>
|
||||
Countdown Mode: <input type="checkbox" name="CE"><br>
|
||||
Countdown Goal:<br>
|
||||
Date: <nowrap>20<input name="CY" class="xs" type="number" min="0" max="99" required>-<input name="CI" class="xs" type="number" min="1" max="12" required>-<input name="CD" class="xs" type="number" min="1" max="31" required></nowrap><br>
|
||||
|
||||
@@ -63,18 +63,19 @@
|
||||
<h3>Experimental</h3>
|
||||
Disable WiFi sleep: <input type="checkbox" name="WS"><br>
|
||||
<i>Can help with connectivity issues.<br>
|
||||
Do not enable if WiFi is working correctly, increases power consumption.</i>
|
||||
<div id="ethd">
|
||||
Do not enable if WiFi is working correctly, increases power consumption.</i>
|
||||
<div id="ethd">
|
||||
<h3>Ethernet Type</h3>
|
||||
<select name="ETH">
|
||||
<option value="0">None</option>
|
||||
<option value="2">ESP32-POE</option>
|
||||
<option value="6">ESP32Deux</option>
|
||||
<option value="4">QuinLED-ESP32</option>
|
||||
<option value="5">TwilightLord-ESP32</option>
|
||||
<option value="3">WESP32</option>
|
||||
<option value="2">ESP32-POE</option>
|
||||
<option value="6">ESP32Deux</option>
|
||||
<option value="7">KIT-VE</option>
|
||||
<option value="4">QuinLED-ESP32</option>
|
||||
<option value="5">TwilightLord-ESP32</option>
|
||||
<option value="3">WESP32</option>
|
||||
<option value="1">WT32-ETH01</option>
|
||||
</select><br><br></div>
|
||||
</select><br><br></div>
|
||||
<hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save & Connect</button>
|
||||
</form>
|
||||
|
||||
@@ -1,83 +1,83 @@
|
||||
body {
|
||||
font-family: Verdana, sans-serif;
|
||||
text-align: center;
|
||||
background: #222;
|
||||
color: #fff;
|
||||
line-height: 200%%; /* %% because of AsyncWebServer */
|
||||
margin: 0;
|
||||
font-family: Verdana, sans-serif;
|
||||
text-align: center;
|
||||
background: #222;
|
||||
color: #fff;
|
||||
line-height: 200%%; /* %% because of AsyncWebServer */
|
||||
margin: 0;
|
||||
}
|
||||
hr {
|
||||
border-color: #666;
|
||||
border-color: #666;
|
||||
}
|
||||
a {
|
||||
color: #28f;
|
||||
text-decoration: none;
|
||||
color: #28f;
|
||||
text-decoration: none;
|
||||
}
|
||||
button, .btn {
|
||||
background: #333;
|
||||
color: #fff;
|
||||
font-family: Verdana, sans-serif;
|
||||
border: 0.3ch solid #333;
|
||||
display: inline-block;
|
||||
font-size: 20px;
|
||||
margin: 12px 8px 8px;
|
||||
padding: 1px 6px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
background: #333;
|
||||
color: #fff;
|
||||
font-family: Verdana, sans-serif;
|
||||
border: 0.3ch solid #333;
|
||||
display: inline-block;
|
||||
font-size: 20px;
|
||||
margin: 12px 8px 8px;
|
||||
padding: 1px 6px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
.lnk {
|
||||
border: 0;
|
||||
border: 0;
|
||||
}
|
||||
.helpB {
|
||||
text-align: left;
|
||||
position: absolute;
|
||||
width: 60px;
|
||||
text-align: left;
|
||||
position: absolute;
|
||||
width: 60px;
|
||||
}
|
||||
input {
|
||||
background: #333;
|
||||
color: #fff;
|
||||
font-family: Verdana, sans-serif;
|
||||
border: 0.5ch solid #333;
|
||||
background: #333;
|
||||
color: #fff;
|
||||
font-family: Verdana, sans-serif;
|
||||
border: 0.5ch solid #333;
|
||||
}
|
||||
input:disabled {
|
||||
color: #888;
|
||||
color: #888;
|
||||
}
|
||||
input[type="number"] {
|
||||
width: 4em;
|
||||
margin: 2px;
|
||||
width: 4em;
|
||||
margin: 2px;
|
||||
}
|
||||
input[type="number"].xxl {
|
||||
width: 100px;
|
||||
width: 100px;
|
||||
}
|
||||
input[type="number"].xl {
|
||||
width: 85px;
|
||||
width: 85px;
|
||||
}
|
||||
input[type="number"].l {
|
||||
width: 63px;
|
||||
width: 63px;
|
||||
}
|
||||
input[type="number"].m {
|
||||
width: 56px;
|
||||
width: 56px;
|
||||
}
|
||||
input[type="number"].s {
|
||||
width: 49px;
|
||||
width: 49px;
|
||||
}
|
||||
input[type="number"].xs {
|
||||
width: 42px;
|
||||
width: 42px;
|
||||
}
|
||||
input[type="checkbox"] {
|
||||
transform: scale(1.5);
|
||||
transform: scale(1.5);
|
||||
}
|
||||
select {
|
||||
background: #333;
|
||||
color: #fff;
|
||||
font-family: Verdana, sans-serif;
|
||||
border: 0.5ch solid #333;
|
||||
background: #333;
|
||||
color: #fff;
|
||||
font-family: Verdana, sans-serif;
|
||||
border: 0.5ch solid #333;
|
||||
}
|
||||
td {
|
||||
padding: 2px;
|
||||
padding: 2px;
|
||||
}
|
||||
.d5 {
|
||||
width: 4.5em !important;
|
||||
width: 4.5em !important;
|
||||
}
|
||||
#toast {
|
||||
opacity: 0;
|
||||
@@ -92,7 +92,7 @@ td {
|
||||
text-align: center;
|
||||
z-index: 5;
|
||||
transform: translateX(-50%%); /* %% because of AsyncWebServer */
|
||||
max-width: 90%%; /* %% because of AsyncWebServer */
|
||||
max-width: 90%%; /* %% because of AsyncWebServer */
|
||||
left: 50%%; /* %% because of AsyncWebServer */
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#include "wled.h"
|
||||
|
||||
/*
|
||||
* Support for DMX via MAX485.
|
||||
* Change the output pin in src/dependencies/ESPDMX.cpp if needed.
|
||||
* Library from:
|
||||
* Support for DMX Output via MAX485.
|
||||
* Change the output pin in src/dependencies/ESPDMX.cpp, if needed (ESP8266)
|
||||
* Change the output pin in src/dependencies/SparkFunDMX.cpp, if needed (ESP32)
|
||||
* ESP8266 Library from:
|
||||
* https://github.com/Rickgg/ESP-Dmx
|
||||
* ESP32 Library from:
|
||||
* https://github.com/sparkfun/SparkFunDMX
|
||||
*/
|
||||
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
@@ -14,10 +17,16 @@ void handleDMX()
|
||||
// don't act, when in DMX Proxy mode
|
||||
if (e131ProxyUniverse != 0) return;
|
||||
|
||||
// TODO: calculate brightness manually if no shutter channel is set
|
||||
|
||||
uint8_t brightness = strip.getBrightness();
|
||||
|
||||
bool calc_brightness = true;
|
||||
|
||||
// check if no shutter channel is set
|
||||
for (byte i = 0; i < DMXChannels; i++)
|
||||
{
|
||||
if (DMXFixtureMap[i] == 5) calc_brightness = false;
|
||||
}
|
||||
|
||||
uint16_t len = strip.getLengthTotal();
|
||||
for (int i = DMXStartLED; i < len; i++) { // uses the amount of LEDs as fixture count
|
||||
|
||||
@@ -35,16 +44,16 @@ void handleDMX()
|
||||
dmx.write(DMXAddr, 0);
|
||||
break;
|
||||
case 1: // Red
|
||||
dmx.write(DMXAddr, r);
|
||||
dmx.write(DMXAddr, calc_brightness ? (r * brightness) / 255 : r);
|
||||
break;
|
||||
case 2: // Green
|
||||
dmx.write(DMXAddr, g);
|
||||
dmx.write(DMXAddr, calc_brightness ? (g * brightness) / 255 : g);
|
||||
break;
|
||||
case 3: // Blue
|
||||
dmx.write(DMXAddr, b);
|
||||
dmx.write(DMXAddr, calc_brightness ? (b * brightness) / 255 : b);
|
||||
break;
|
||||
case 4: // White
|
||||
dmx.write(DMXAddr, w);
|
||||
dmx.write(DMXAddr, calc_brightness ? (w * brightness) / 255 : w);
|
||||
break;
|
||||
case 5: // Shutter channel. Controls the brightness.
|
||||
dmx.write(DMXAddr, brightness);
|
||||
@@ -60,7 +69,11 @@ void handleDMX()
|
||||
}
|
||||
|
||||
void initDMX() {
|
||||
#ifdef ESP8266
|
||||
dmx.init(512); // initialize with bus length
|
||||
#else
|
||||
dmx.initWrite(512); // initialize with bus length
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
@@ -105,59 +105,70 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
||||
realtimeIP = clientIP;
|
||||
byte wChannel = 0;
|
||||
uint16_t totalLen = strip.getLengthTotal();
|
||||
uint16_t availDMXLen = dmxChannels - DMXAddress + 1;
|
||||
uint16_t dataOffset = DMXAddress;
|
||||
|
||||
// DMX data in Art-Net packet starts at index 0, for E1.31 at index 1
|
||||
if (protocol == P_ARTNET && dataOffset > 0) {
|
||||
dataOffset--;
|
||||
}
|
||||
|
||||
switch (DMXMode) {
|
||||
case DMX_MODE_DISABLED:
|
||||
return; // nothing to do
|
||||
break;
|
||||
|
||||
case DMX_MODE_SINGLE_RGB:
|
||||
case DMX_MODE_SINGLE_RGB: // RGB only
|
||||
if (uni != e131Universe) return;
|
||||
if (dmxChannels-DMXAddress+1 < 3) return;
|
||||
if (availDMXLen < 3) return;
|
||||
realtimeLock(realtimeTimeoutMs, mde);
|
||||
if (realtimeOverride) return;
|
||||
wChannel = (dmxChannels-DMXAddress+1 > 3) ? e131_data[DMXAddress+3] : 0;
|
||||
wChannel = (availDMXLen > 3) ? e131_data[dataOffset+3] : 0;
|
||||
for (uint16_t i = 0; i < totalLen; i++)
|
||||
setRealtimePixel(i, e131_data[DMXAddress+0], e131_data[DMXAddress+1], e131_data[DMXAddress+2], wChannel);
|
||||
setRealtimePixel(i, e131_data[dataOffset+0], e131_data[dataOffset+1], e131_data[dataOffset+2], wChannel);
|
||||
break;
|
||||
|
||||
case DMX_MODE_SINGLE_DRGB:
|
||||
case DMX_MODE_SINGLE_DRGB: // Dimmer + RGB
|
||||
if (uni != e131Universe) return;
|
||||
if (dmxChannels-DMXAddress+1 < 4) return;
|
||||
if (availDMXLen < 4) return;
|
||||
realtimeLock(realtimeTimeoutMs, mde);
|
||||
if (realtimeOverride) return;
|
||||
wChannel = (dmxChannels-DMXAddress+1 > 4) ? e131_data[DMXAddress+4] : 0;
|
||||
if (DMXOldDimmer != e131_data[DMXAddress+0]) {
|
||||
DMXOldDimmer = e131_data[DMXAddress+0];
|
||||
bri = e131_data[DMXAddress+0];
|
||||
strip.setBrightness(bri);
|
||||
wChannel = (availDMXLen > 4) ? e131_data[dataOffset+4] : 0;
|
||||
if (DMXOldDimmer != e131_data[dataOffset+0]) {
|
||||
DMXOldDimmer = e131_data[dataOffset+0];
|
||||
bri = e131_data[dataOffset+0];
|
||||
strip.setBrightness(bri, true);
|
||||
}
|
||||
for (uint16_t i = 0; i < totalLen; i++)
|
||||
setRealtimePixel(i, e131_data[DMXAddress+1], e131_data[DMXAddress+2], e131_data[DMXAddress+3], wChannel);
|
||||
setRealtimePixel(i, e131_data[dataOffset+1], e131_data[dataOffset+2], e131_data[dataOffset+3], wChannel);
|
||||
break;
|
||||
|
||||
case DMX_MODE_EFFECT:
|
||||
case DMX_MODE_EFFECT: // Length 1: Apply Preset ID, length 11-13: apply effect config
|
||||
if (uni != e131Universe) return;
|
||||
if (dmxChannels-DMXAddress+1 < 11) return;
|
||||
if (DMXOldDimmer != e131_data[DMXAddress+0]) {
|
||||
DMXOldDimmer = e131_data[DMXAddress+0];
|
||||
bri = e131_data[DMXAddress+0];
|
||||
if (availDMXLen < 11) {
|
||||
if (availDMXLen > 1) return;
|
||||
applyPreset(e131_data[dataOffset+0], CALL_MODE_NOTIFICATION);
|
||||
return;
|
||||
}
|
||||
if (e131_data[DMXAddress+1] < MODE_COUNT)
|
||||
effectCurrent = e131_data[DMXAddress+ 1];
|
||||
effectSpeed = e131_data[DMXAddress+ 2]; // flickers
|
||||
effectIntensity = e131_data[DMXAddress+ 3];
|
||||
effectPalette = e131_data[DMXAddress+ 4];
|
||||
col[0] = e131_data[DMXAddress+ 5];
|
||||
col[1] = e131_data[DMXAddress+ 6];
|
||||
col[2] = e131_data[DMXAddress+ 7];
|
||||
colSec[0] = e131_data[DMXAddress+ 8];
|
||||
colSec[1] = e131_data[DMXAddress+ 9];
|
||||
colSec[2] = e131_data[DMXAddress+10];
|
||||
if (dmxChannels-DMXAddress+1 > 11)
|
||||
if (DMXOldDimmer != e131_data[dataOffset+0]) {
|
||||
DMXOldDimmer = e131_data[dataOffset+0];
|
||||
bri = e131_data[dataOffset+0];
|
||||
}
|
||||
if (e131_data[dataOffset+1] < MODE_COUNT)
|
||||
effectCurrent = e131_data[dataOffset+ 1];
|
||||
effectSpeed = e131_data[dataOffset+ 2]; // flickers
|
||||
effectIntensity = e131_data[dataOffset+ 3];
|
||||
effectPalette = e131_data[dataOffset+ 4];
|
||||
col[0] = e131_data[dataOffset+ 5];
|
||||
col[1] = e131_data[dataOffset+ 6];
|
||||
col[2] = e131_data[dataOffset+ 7];
|
||||
colSec[0] = e131_data[dataOffset+ 8];
|
||||
colSec[1] = e131_data[dataOffset+ 9];
|
||||
colSec[2] = e131_data[dataOffset+10];
|
||||
if (availDMXLen > 11)
|
||||
{
|
||||
col[3] = e131_data[DMXAddress+11]; //white
|
||||
colSec[3] = e131_data[DMXAddress+12];
|
||||
col[3] = e131_data[dataOffset+11]; //white
|
||||
colSec[3] = e131_data[dataOffset+12];
|
||||
}
|
||||
transitionDelayTemp = 0; // act fast
|
||||
colorUpdated(CALL_MODE_NOTIFICATION); // don't send UDP
|
||||
@@ -173,22 +184,26 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
||||
const uint16_t dmxChannelsPerLed = is4Chan ? 4 : 3;
|
||||
const uint16_t ledsPerUniverse = is4Chan ? MAX_4_CH_LEDS_PER_UNIVERSE : MAX_3_CH_LEDS_PER_UNIVERSE;
|
||||
if (realtimeOverride) return;
|
||||
uint16_t previousLeds, dmxOffset;
|
||||
uint16_t previousLeds, dmxOffset, ledsTotal;
|
||||
if (previousUniverses == 0) {
|
||||
if (dmxChannels-DMXAddress < 1) return;
|
||||
dmxOffset = DMXAddress;
|
||||
if (availDMXLen < 1) return;
|
||||
dmxOffset = dataOffset;
|
||||
previousLeds = 0;
|
||||
// First DMX address is dimmer in DMX_MODE_MULTIPLE_DRGB mode.
|
||||
if (DMXMode == DMX_MODE_MULTIPLE_DRGB) {
|
||||
strip.setBrightness(e131_data[dmxOffset++]);
|
||||
strip.setBrightness(e131_data[dmxOffset++], true);
|
||||
ledsTotal = (availDMXLen - 1) / dmxChannelsPerLed;
|
||||
} else {
|
||||
ledsTotal = availDMXLen / dmxChannelsPerLed;
|
||||
}
|
||||
} else {
|
||||
// All subsequent universes start at the first channel.
|
||||
dmxOffset = (protocol == P_ARTNET) ? 0 : 1;
|
||||
uint16_t ledsInFirstUniverse = (MAX_CHANNELS_PER_UNIVERSE - DMXAddress) / dmxChannelsPerLed;
|
||||
uint16_t dimmerOffset = (DMXMode == DMX_MODE_MULTIPLE_DRGB) ? 1 : 0;
|
||||
uint16_t ledsInFirstUniverse = ((MAX_CHANNELS_PER_UNIVERSE - DMXAddress + 1) - dimmerOffset) / dmxChannelsPerLed;
|
||||
previousLeds = ledsInFirstUniverse + (previousUniverses - 1) * ledsPerUniverse;
|
||||
ledsTotal = previousLeds + (dmxChannels / dmxChannelsPerLed);
|
||||
}
|
||||
uint16_t ledsTotal = previousLeds + (dmxChannels - dmxOffset +1) / dmxChannelsPerLed;
|
||||
if (!is4Chan) {
|
||||
for (uint16_t i = previousLeds; i < ledsTotal; i++) {
|
||||
setRealtimePixel(i, e131_data[dmxOffset], e131_data[dmxOffset+1], e131_data[dmxOffset+2], 0);
|
||||
|
||||
@@ -58,10 +58,7 @@ bool getJsonValue(const JsonVariant& element, DestType& destination, const Defau
|
||||
|
||||
|
||||
//colors.cpp
|
||||
void colorFromUint32(uint32_t in, bool secondary = false);
|
||||
void colorFromUint24(uint32_t in, bool secondary = false);
|
||||
inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); }
|
||||
void relativeChangeWhite(int8_t amount, byte lowerBoundary = 0);
|
||||
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb
|
||||
void colorKtoRGB(uint16_t kelvin, byte* rgb);
|
||||
void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb
|
||||
@@ -108,11 +105,9 @@ void sendImprovInfoResponse();
|
||||
void sendImprovRPCResponse(uint8_t commandId);
|
||||
|
||||
//ir.cpp
|
||||
bool decodeIRCustom(uint32_t code);
|
||||
//bool decodeIRCustom(uint32_t code);
|
||||
void applyRepeatActions();
|
||||
void relativeChange(byte* property, int8_t amount, byte lowerBoundary = 0, byte higherBoundary = 0xFF);
|
||||
void changeEffectSpeed(int8_t amount);
|
||||
void changeEffectIntensity(int8_t amount);
|
||||
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);
|
||||
@@ -144,6 +139,8 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient = 0);
|
||||
#endif
|
||||
|
||||
//led.cpp
|
||||
void setValuesFromSegment(uint8_t s);
|
||||
void setValuesFromMainSeg();
|
||||
void setValuesFromFirstSelectedSeg();
|
||||
void resetTimebase();
|
||||
void toggleOnOff();
|
||||
@@ -180,17 +177,10 @@ void calculateSunriseAndSunset();
|
||||
void setTimeFromAPI(uint32_t timein);
|
||||
|
||||
//overlay.cpp
|
||||
void initCronixie();
|
||||
void handleOverlays();
|
||||
void handleOverlayDraw();
|
||||
void _overlayAnalogCountdown();
|
||||
void _overlayAnalogClock();
|
||||
|
||||
byte getSameCodeLength(char code, int index, char const cronixieDisplay[]);
|
||||
void setCronixie();
|
||||
void _overlayCronixie();
|
||||
void _drawOverlayCronixie();
|
||||
|
||||
//playlist.cpp
|
||||
void shufflePlaylist();
|
||||
void unloadPlaylist();
|
||||
@@ -200,8 +190,8 @@ void handlePlaylist();
|
||||
//presets.cpp
|
||||
bool applyPreset(byte index, byte callMode = CALL_MODE_DIRECT_CHANGE);
|
||||
inline bool applyTemporaryPreset() {return applyPreset(255);};
|
||||
void savePreset(byte index, bool persist = true, const char* pname = nullptr, JsonObject saveobj = JsonObject());
|
||||
inline void saveTemporaryPreset() {savePreset(255, false);};
|
||||
void savePreset(byte index, const char* pname = nullptr, JsonObject saveobj = JsonObject());
|
||||
inline void saveTemporaryPreset() {savePreset(255);};
|
||||
void deletePreset(byte index);
|
||||
|
||||
//set.cpp
|
||||
@@ -216,6 +206,7 @@ bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte
|
||||
void notify(byte callMode, bool followUp=false);
|
||||
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte *buffer, uint8_t bri=255, bool isRGBW=false);
|
||||
void realtimeLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC);
|
||||
void exitRealtime();
|
||||
void handleNotifications();
|
||||
void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w);
|
||||
void refreshNodeList();
|
||||
|
||||
@@ -42,7 +42,7 @@ function B(){window.history.back()}function U(){document.getElementById("uf").st
|
||||
.bt{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;display:inline-block;font-size:20px;margin:8px;margin-top:12px}input[type=file]{font-size:16px}body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;line-height:200%}#msg{display:none}
|
||||
</style></head><body><h2>WLED Software Update</h2><form method="POST"
|
||||
action="/update" id="uf" enctype="multipart/form-data" onsubmit="U()">
|
||||
Installed version: 0.13.0-b7<br>Download the latest binary: <a
|
||||
Installed version: 0.13.3<br>Download the latest binary: <a
|
||||
href="https://github.com/Aircoookie/WLED/releases" target="_blank"><img
|
||||
src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square">
|
||||
</a><br><input type="file" class="bt" name="update" required><br><input
|
||||
@@ -85,7 +85,7 @@ charset="utf-8"><meta name="theme-color" content="#222222"><title>
|
||||
WLED Live Preview</title><style>
|
||||
body{margin:0}#canv{background:#000;filter:brightness(175%);width:100%;height:100%;position:absolute}
|
||||
</style></head><body><div id="canv"><script>
|
||||
function updatePreview(e){var n="linear-gradient(90deg,",t=e.length;for(i=2;i<t;i+=3)n+=`rgb(${e[i]},${e[i+1]},${e[i+2]})`,i<t-3&&(n+=",");n+=")",document.getElementById("canv").style.background=n}function getLiveJson(e){try{if("[object ArrayBuffer]"===toString.call(e.data)){let e=new Uint8Array(event.data);if(76!=e[0])return;updatePreview(e)}}catch(e){console.error("Peek WS error:",e)}}var ws=top.window.ws;ws&&ws.readyState===WebSocket.OPEN?(console.info("Peek uses top WS"),ws.send("{'lv':true}")):(console.info("Peek WS opening"),(ws=new WebSocket("ws://"+document.location.host+"/ws")).onopen=function(){console.info("Peek WS open"),ws.send("{'lv':true}")}),ws.binaryType="arraybuffer",ws.addEventListener("message",getLiveJson)
|
||||
function updatePreview(e){var n="linear-gradient(90deg,",t=e.length;for(i=2;i<t;i+=3)n+=`rgb(${e[i]},${e[i+1]},${e[i+2]})`,i<t-3&&(n+=",");n+=")",document.getElementById("canv").style.background=n}function getLiveJson(e){try{if("[object ArrayBuffer]"===toString.call(e.data)){let e=new Uint8Array(event.data);if(76!=e[0])return;updatePreview(e)}}catch(e){console.error("Peek WS error:",e)}}var ws;try{ws=top.window.ws}catch(e){}ws&&ws.readyState===WebSocket.OPEN?(console.info("Peek uses top WS"),ws.send("{'lv':true}")):(console.info("Peek WS opening"),(ws=new WebSocket(("https:"==window.location.protocol?"wss":"ws")+"://"+document.location.host+"/ws")).onopen=function(){console.info("Peek WS open"),ws.send("{'lv':true}")}),ws.binaryType="arraybuffer",ws.addEventListener("message",getLiveJson)
|
||||
</script></body></html>)=====";
|
||||
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
4427
wled00/html_ui.h
4427
wled00/html_ui.h
File diff suppressed because it is too large
Load Diff
@@ -189,7 +189,7 @@ void sendImprovInfoResponse() {
|
||||
out[11] = 4; //Firmware len ("WLED")
|
||||
out[12] = 'W'; out[13] = 'L'; out[14] = 'E'; out[15] = 'D';
|
||||
uint8_t lengthSum = 17;
|
||||
uint8_t vlen = sprintf_P(out+lengthSum,PSTR("0.13.0-b7/%i"),VERSION);
|
||||
uint8_t vlen = sprintf_P(out+lengthSum,PSTR("0.13.3/%i"),VERSION);
|
||||
out[16] = vlen; lengthSum += vlen;
|
||||
uint8_t hlen = 7;
|
||||
#ifdef ESP8266
|
||||
|
||||
689
wled00/ir.cpp
689
wled00/ir.cpp
@@ -73,56 +73,94 @@ void presetFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID)
|
||||
{
|
||||
byte prevError = errorFlag;
|
||||
if (!applyPreset(presetID, CALL_MODE_BUTTON_PRESET)) {
|
||||
effectCurrent = effectID;
|
||||
effectCurrent = effectID;
|
||||
effectPalette = paletteID;
|
||||
errorFlag = prevError; //clear error 12 from non-existent preset
|
||||
}
|
||||
}
|
||||
|
||||
//Add what your custom IR codes should trigger here. Guide: https://github.com/Aircoookie/WLED/wiki/Infrared-Control
|
||||
//IR codes themselves can be defined directly after "case" or in "ir_codes.h"
|
||||
bool decodeIRCustom(uint32_t code)
|
||||
byte relativeChange(byte property, int8_t amount, byte lowerBoundary, byte higherBoundary)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
//just examples, feel free to modify or remove
|
||||
case IRCUSTOM_ONOFF : toggleOnOff(); break;
|
||||
case IRCUSTOM_MACRO1 : applyPreset(1, CALL_MODE_BUTTON_PRESET); break;
|
||||
|
||||
default: return false;
|
||||
}
|
||||
if (code != IRCUSTOM_MACRO1) colorUpdated(CALL_MODE_BUTTON); //don't update color again if we apply macro, it already does it
|
||||
return true;
|
||||
int16_t new_val = (int16_t) property + amount;
|
||||
if (lowerBoundary >= higherBoundary) return property;
|
||||
if (new_val > higherBoundary) new_val = higherBoundary;
|
||||
if (new_val < lowerBoundary) new_val = lowerBoundary;
|
||||
return (byte)constrain(new_val, 0, 255);
|
||||
}
|
||||
|
||||
void relativeChange(byte* property, int8_t amount, byte lowerBoundary, byte higherBoundary)
|
||||
void changeEffect(uint8_t fx)
|
||||
{
|
||||
int16_t new_val = (int16_t) *property + amount;
|
||||
if (new_val > higherBoundary) new_val = higherBoundary;
|
||||
else if (new_val < lowerBoundary) new_val = lowerBoundary;
|
||||
*property = (byte)constrain(new_val,0.1,255.1);
|
||||
if (irApplyToAllSelected) {
|
||||
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
|
||||
WS2812FX::Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
strip.setMode(i, fx);
|
||||
}
|
||||
setValuesFromFirstSelectedSeg();
|
||||
} else {
|
||||
strip.setMode(strip.getMainSegmentId(), fx);
|
||||
setValuesFromMainSeg();
|
||||
}
|
||||
stateChanged = true;
|
||||
}
|
||||
|
||||
void changePalette(uint8_t pal)
|
||||
{
|
||||
if (irApplyToAllSelected) {
|
||||
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
|
||||
WS2812FX::Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
seg.palette = pal;
|
||||
}
|
||||
setValuesFromFirstSelectedSeg();
|
||||
} else {
|
||||
strip.getMainSegment().palette = pal;
|
||||
setValuesFromMainSeg();
|
||||
}
|
||||
stateChanged = true;
|
||||
}
|
||||
|
||||
void changeEffectSpeed(int8_t amount)
|
||||
{
|
||||
if (effectCurrent != 0) {
|
||||
int16_t new_val = (int16_t) effectSpeed + amount;
|
||||
effectSpeed = (byte)constrain(new_val,0.1,255.1);
|
||||
} else { // if Effect == "solid Color", change the hue of the primary color
|
||||
effectSpeed = (byte)constrain(new_val,0,255);
|
||||
if (irApplyToAllSelected) {
|
||||
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
|
||||
WS2812FX::Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
seg.speed = effectSpeed;
|
||||
}
|
||||
setValuesFromFirstSelectedSeg();
|
||||
} else {
|
||||
strip.getMainSegment().speed = effectSpeed;
|
||||
setValuesFromMainSeg();
|
||||
}
|
||||
} else { // if Effect == "solid Color", change the hue of the primary color
|
||||
WS2812FX::Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
|
||||
CRGB fastled_col;
|
||||
fastled_col.red = col[0];
|
||||
fastled_col.green = col[1];
|
||||
fastled_col.blue = col[2];
|
||||
fastled_col.red = R(sseg.colors[0]);
|
||||
fastled_col.green = G(sseg.colors[0]);
|
||||
fastled_col.blue = B(sseg.colors[0]);
|
||||
CHSV prim_hsv = rgb2hsv_approximate(fastled_col);
|
||||
int16_t new_val = (int16_t) prim_hsv.h + amount;
|
||||
int16_t new_val = (int16_t)prim_hsv.h + amount;
|
||||
if (new_val > 255) new_val -= 255; // roll-over if bigger than 255
|
||||
if (new_val < 0) new_val += 255; // roll-over if smaller than 0
|
||||
prim_hsv.h = (byte)new_val;
|
||||
hsv2rgb_rainbow(prim_hsv, fastled_col);
|
||||
col[0] = fastled_col.red;
|
||||
col[1] = fastled_col.green;
|
||||
col[2] = fastled_col.blue;
|
||||
if (irApplyToAllSelected) {
|
||||
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
|
||||
WS2812FX::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]));
|
||||
}
|
||||
setValuesFromFirstSelectedSeg();
|
||||
} else {
|
||||
strip.getMainSegment().colors[0] = RGBW32(fastled_col.red, fastled_col.green, fastled_col.blue, W(sseg.colors[0]));
|
||||
setValuesFromMainSeg();
|
||||
}
|
||||
}
|
||||
stateChanged = true;
|
||||
|
||||
if(amount > 0) lastRepeatableAction = ACTION_SPEED_UP;
|
||||
if(amount < 0) lastRepeatableAction = ACTION_SPEED_DOWN;
|
||||
@@ -133,65 +171,132 @@ void changeEffectIntensity(int8_t amount)
|
||||
{
|
||||
if (effectCurrent != 0) {
|
||||
int16_t new_val = (int16_t) effectIntensity + amount;
|
||||
effectIntensity = (byte)constrain(new_val,0.1,255.1);
|
||||
} else { // if Effect == "solid Color", change the saturation of the primary color
|
||||
effectIntensity = (byte)constrain(new_val,0,255);
|
||||
if (irApplyToAllSelected) {
|
||||
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
|
||||
WS2812FX::Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
seg.intensity = effectIntensity;
|
||||
}
|
||||
setValuesFromFirstSelectedSeg();
|
||||
} else {
|
||||
strip.getMainSegment().speed = effectIntensity;
|
||||
setValuesFromMainSeg();
|
||||
}
|
||||
} else { // if Effect == "solid Color", change the saturation of the primary color
|
||||
WS2812FX::Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
|
||||
CRGB fastled_col;
|
||||
fastled_col.red = col[0];
|
||||
fastled_col.green = col[1];
|
||||
fastled_col.blue = col[2];
|
||||
fastled_col.red = R(sseg.colors[0]);
|
||||
fastled_col.green = G(sseg.colors[0]);
|
||||
fastled_col.blue = B(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.1,255.1); // constrain to 0-255
|
||||
prim_hsv.s = (byte)constrain(new_val,0,255); // constrain to 0-255
|
||||
hsv2rgb_rainbow(prim_hsv, fastled_col);
|
||||
col[0] = fastled_col.red;
|
||||
col[1] = fastled_col.green;
|
||||
col[2] = fastled_col.blue;
|
||||
if (irApplyToAllSelected) {
|
||||
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
|
||||
WS2812FX::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]));
|
||||
}
|
||||
setValuesFromFirstSelectedSeg();
|
||||
} else {
|
||||
strip.getMainSegment().colors[0] = RGBW32(fastled_col.red, fastled_col.green, fastled_col.blue, W(sseg.colors[0]));
|
||||
setValuesFromMainSeg();
|
||||
}
|
||||
}
|
||||
stateChanged = true;
|
||||
|
||||
if(amount > 0) lastRepeatableAction = ACTION_INTENSITY_UP;
|
||||
if(amount < 0) lastRepeatableAction = ACTION_INTENSITY_DOWN;
|
||||
lastRepeatableValue = amount;
|
||||
}
|
||||
|
||||
void changeColor(uint32_t c, int16_t cct=-1)
|
||||
{
|
||||
if (irApplyToAllSelected) {
|
||||
// main segment may not be selected!
|
||||
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
|
||||
WS2812FX::Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
byte capabilities = seg.getLightCapabilities();
|
||||
uint32_t mask = 0;
|
||||
bool isRGB = GET_BIT(capabilities, 0); // when RGBW_MODE_AUTO_ACCURATE this is always true
|
||||
bool hasW = GET_BIT(capabilities, 1);
|
||||
bool isCCT = GET_BIT(capabilities, 2);
|
||||
if (isRGB) mask |= 0x00FFFFFF; // RGB
|
||||
if (hasW) mask |= 0xFF000000; // white
|
||||
if (hasW && (strip.autoWhiteMode == RGBW_MODE_AUTO_ACCURATE) && (c & 0xFF000000)) { // white channel & white specified
|
||||
seg.setColor(0, c | 0xFFFFFF, i); // for accurate mode we fake white
|
||||
} else if (c & mask) seg.setColor(0, c & mask, i); // only apply if not black
|
||||
if (isCCT && cct >= 0) seg.setCCT(cct, i);
|
||||
}
|
||||
setValuesFromFirstSelectedSeg();
|
||||
} else {
|
||||
byte i = strip.getMainSegmentId();
|
||||
WS2812FX::Segment& seg = strip.getSegment(i);
|
||||
byte capabilities = seg.getLightCapabilities();
|
||||
uint32_t mask = 0;
|
||||
bool isRGB = GET_BIT(capabilities, 0);
|
||||
bool hasW = GET_BIT(capabilities, 1);
|
||||
bool isCCT = GET_BIT(capabilities, 2);
|
||||
if (isRGB) mask |= 0x00FFFFFF; // RGB
|
||||
if (hasW) mask |= 0xFF000000; // white
|
||||
if (hasW && (strip.autoWhiteMode == RGBW_MODE_AUTO_ACCURATE) && (c & 0xFF000000)) { // white channel & white specified
|
||||
seg.setColor(0, c | 0xFFFFFF, i); // for accurate mode we fake white
|
||||
} else if (c & mask) seg.setColor(0, c & mask, i); // only apply if not black
|
||||
if (isCCT && cct >= 0) seg.setCCT(cct, i);
|
||||
setValuesFromMainSeg();
|
||||
}
|
||||
stateChanged = true;
|
||||
}
|
||||
|
||||
void changeWhite(int8_t amount, int16_t cct=-1)
|
||||
{
|
||||
WS2812FX::Segment& seg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
|
||||
byte r = R(seg.colors[0]);
|
||||
byte g = G(seg.colors[0]);
|
||||
byte b = B(seg.colors[0]);
|
||||
byte w = relativeChange(W(seg.colors[0]), amount, 5);
|
||||
changeColor(RGBW32(r, g, b, w), cct);
|
||||
}
|
||||
|
||||
void decodeIR(uint32_t code)
|
||||
{
|
||||
if (code == 0xFFFFFFFF) //repeated code, continue brightness up/down
|
||||
{
|
||||
if (code == 0xFFFFFFFF) {
|
||||
//repeated code, continue brightness up/down
|
||||
irTimesRepeated++;
|
||||
applyRepeatActions();
|
||||
return;
|
||||
}
|
||||
lastValidCode = 0; irTimesRepeated = 0;
|
||||
lastRepeatableAction = ACTION_NONE;
|
||||
if (decodeIRCustom(code)) return;
|
||||
|
||||
if (irEnabled == 8) { // any remote configurable with ir.json file
|
||||
decodeIRJson(code);
|
||||
colorUpdated(CALL_MODE_BUTTON);
|
||||
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
|
||||
}
|
||||
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 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
|
||||
default: return;
|
||||
}
|
||||
|
||||
if (nightlightActive && bri == 0) nightlightActive = false;
|
||||
colorUpdated(CALL_MODE_BUTTON); //for notifier, IR is considered a button input
|
||||
stateUpdated(CALL_MODE_BUTTON); //for notifier, IR is considered a button input
|
||||
}
|
||||
|
||||
void applyRepeatActions()
|
||||
@@ -200,50 +305,50 @@ void applyRepeatActions()
|
||||
decodeIRJson(lastValidCode);
|
||||
return;
|
||||
} else switch (lastRepeatableAction) {
|
||||
case ACTION_BRIGHT_UP : incBrightness(); colorUpdated(CALL_MODE_BUTTON); return;
|
||||
case ACTION_BRIGHT_DOWN : decBrightness(); colorUpdated(CALL_MODE_BUTTON); return;
|
||||
case ACTION_SPEED_UP : changeEffectSpeed(lastRepeatableValue); colorUpdated(CALL_MODE_BUTTON); return;
|
||||
case ACTION_SPEED_DOWN : changeEffectSpeed(lastRepeatableValue); colorUpdated(CALL_MODE_BUTTON); return;
|
||||
case ACTION_INTENSITY_UP : changeEffectIntensity(lastRepeatableValue); colorUpdated(CALL_MODE_BUTTON); return;
|
||||
case ACTION_INTENSITY_DOWN : changeEffectIntensity(lastRepeatableValue); colorUpdated(CALL_MODE_BUTTON); return;
|
||||
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) {
|
||||
relativeChangeWhite(10);
|
||||
colorUpdated(CALL_MODE_BUTTON);
|
||||
if (lastValidCode == IR40_WPLUS) {
|
||||
changeWhite(10);
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
} else if (lastValidCode == IR40_WMINUS) {
|
||||
relativeChangeWhite(-10, 5);
|
||||
colorUpdated(CALL_MODE_BUTTON);
|
||||
changeWhite(-10);
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
} else if ((lastValidCode == IR24_ON || lastValidCode == IR40_ON) && irTimesRepeated > 7 ) {
|
||||
nightlightActive = true;
|
||||
nightlightStartTime = millis();
|
||||
colorUpdated(CALL_MODE_BUTTON);
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
}
|
||||
}
|
||||
|
||||
void decodeIR24(uint32_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case IR24_BRIGHTER : incBrightness(); break;
|
||||
case IR24_DARKER : decBrightness(); break;
|
||||
case IR24_OFF : if (bri > 0) briLast = bri; bri = 0; break;
|
||||
case IR24_ON : bri = briLast; break;
|
||||
case IR24_RED : colorFromUint32(COLOR_RED); break;
|
||||
case IR24_REDDISH : colorFromUint32(COLOR_REDDISH); break;
|
||||
case IR24_ORANGE : colorFromUint32(COLOR_ORANGE); break;
|
||||
case IR24_YELLOWISH : colorFromUint32(COLOR_YELLOWISH); break;
|
||||
case IR24_YELLOW : colorFromUint32(COLOR_YELLOW); break;
|
||||
case IR24_GREEN : colorFromUint32(COLOR_GREEN); break;
|
||||
case IR24_GREENISH : colorFromUint32(COLOR_GREENISH); break;
|
||||
case IR24_TURQUOISE : colorFromUint32(COLOR_TURQUOISE); break;
|
||||
case IR24_CYAN : colorFromUint32(COLOR_CYAN); break;
|
||||
case IR24_AQUA : colorFromUint32(COLOR_AQUA); break;
|
||||
case IR24_BLUE : colorFromUint32(COLOR_BLUE); break;
|
||||
case IR24_DEEPBLUE : colorFromUint32(COLOR_DEEPBLUE); break;
|
||||
case IR24_PURPLE : colorFromUint32(COLOR_PURPLE); break;
|
||||
case IR24_MAGENTA : colorFromUint32(COLOR_MAGENTA); break;
|
||||
case IR24_PINK : colorFromUint32(COLOR_PINK); break;
|
||||
case IR24_WHITE : colorFromUint32(COLOR_WHITE); effectCurrent = 0; break;
|
||||
case IR24_BRIGHTER : incBrightness(); break;
|
||||
case IR24_DARKER : decBrightness(); break;
|
||||
case IR24_OFF : if (bri > 0) briLast = bri; bri = 0; break;
|
||||
case IR24_ON : bri = briLast; break;
|
||||
case IR24_RED : changeColor(COLOR_RED); break;
|
||||
case IR24_REDDISH : changeColor(COLOR_REDDISH); break;
|
||||
case IR24_ORANGE : changeColor(COLOR_ORANGE); break;
|
||||
case IR24_YELLOWISH : changeColor(COLOR_YELLOWISH); break;
|
||||
case IR24_YELLOW : changeColor(COLOR_YELLOW); break;
|
||||
case IR24_GREEN : changeColor(COLOR_GREEN); break;
|
||||
case IR24_GREENISH : changeColor(COLOR_GREENISH); break;
|
||||
case IR24_TURQUOISE : changeColor(COLOR_TURQUOISE); break;
|
||||
case IR24_CYAN : changeColor(COLOR_CYAN); break;
|
||||
case IR24_AQUA : changeColor(COLOR_AQUA); break;
|
||||
case IR24_BLUE : changeColor(COLOR_BLUE); break;
|
||||
case IR24_DEEPBLUE : changeColor(COLOR_DEEPBLUE); break;
|
||||
case IR24_PURPLE : changeColor(COLOR_PURPLE); break;
|
||||
case IR24_MAGENTA : changeColor(COLOR_MAGENTA); break;
|
||||
case IR24_PINK : changeColor(COLOR_PINK); break;
|
||||
case IR24_WHITE : changeColor(COLOR_WHITE); changeEffect(FX_MODE_STATIC); break;
|
||||
case IR24_FLASH : presetFallback(1, FX_MODE_COLORTWINKLE, effectPalette); break;
|
||||
case IR24_STROBE : presetFallback(2, FX_MODE_RAINBOW_CYCLE, effectPalette); break;
|
||||
case IR24_FADE : presetFallback(3, FX_MODE_BREATH, effectPalette); break;
|
||||
@@ -256,30 +361,30 @@ void decodeIR24(uint32_t code)
|
||||
void decodeIR24OLD(uint32_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case IR24_OLD_BRIGHTER : incBrightness(); break;
|
||||
case IR24_OLD_DARKER : decBrightness(); break;
|
||||
case IR24_OLD_OFF : if (bri > 0) briLast = bri; bri = 0; break;
|
||||
case IR24_OLD_ON : bri = briLast; break;
|
||||
case IR24_OLD_RED : colorFromUint32(COLOR_RED); break;
|
||||
case IR24_OLD_REDDISH : colorFromUint32(COLOR_REDDISH); break;
|
||||
case IR24_OLD_ORANGE : colorFromUint32(COLOR_ORANGE); break;
|
||||
case IR24_OLD_YELLOWISH : colorFromUint32(COLOR_YELLOWISH); break;
|
||||
case IR24_OLD_YELLOW : colorFromUint32(COLOR_YELLOW); break;
|
||||
case IR24_OLD_GREEN : colorFromUint32(COLOR_GREEN); break;
|
||||
case IR24_OLD_GREENISH : colorFromUint32(COLOR_GREENISH); break;
|
||||
case IR24_OLD_TURQUOISE : colorFromUint32(COLOR_TURQUOISE); break;
|
||||
case IR24_OLD_CYAN : colorFromUint32(COLOR_CYAN); break;
|
||||
case IR24_OLD_AQUA : colorFromUint32(COLOR_AQUA); break;
|
||||
case IR24_OLD_BLUE : colorFromUint32(COLOR_BLUE); break;
|
||||
case IR24_OLD_DEEPBLUE : colorFromUint32(COLOR_DEEPBLUE); break;
|
||||
case IR24_OLD_PURPLE : colorFromUint32(COLOR_PURPLE); break;
|
||||
case IR24_OLD_MAGENTA : colorFromUint32(COLOR_MAGENTA); break;
|
||||
case IR24_OLD_PINK : colorFromUint32(COLOR_PINK); break;
|
||||
case IR24_OLD_WHITE : colorFromUint32(COLOR_WHITE); effectCurrent = 0; break;
|
||||
case IR24_OLD_FLASH : presetFallback(1, FX_MODE_COLORTWINKLE, 0); break;
|
||||
case IR24_OLD_STROBE : presetFallback(2, FX_MODE_RAINBOW_CYCLE, 0); break;
|
||||
case IR24_OLD_FADE : presetFallback(3, FX_MODE_BREATH, 0); break;
|
||||
case IR24_OLD_SMOOTH : presetFallback(4, FX_MODE_RAINBOW, 0); break;
|
||||
case IR24_OLD_BRIGHTER : incBrightness(); break;
|
||||
case IR24_OLD_DARKER : decBrightness(); break;
|
||||
case IR24_OLD_OFF : if (bri > 0) briLast = bri; bri = 0; break;
|
||||
case IR24_OLD_ON : bri = briLast; break;
|
||||
case IR24_OLD_RED : changeColor(COLOR_RED); break;
|
||||
case IR24_OLD_REDDISH : changeColor(COLOR_REDDISH); break;
|
||||
case IR24_OLD_ORANGE : changeColor(COLOR_ORANGE); break;
|
||||
case IR24_OLD_YELLOWISH : changeColor(COLOR_YELLOWISH); break;
|
||||
case IR24_OLD_YELLOW : changeColor(COLOR_YELLOW); break;
|
||||
case IR24_OLD_GREEN : changeColor(COLOR_GREEN); break;
|
||||
case IR24_OLD_GREENISH : changeColor(COLOR_GREENISH); break;
|
||||
case IR24_OLD_TURQUOISE : changeColor(COLOR_TURQUOISE); break;
|
||||
case IR24_OLD_CYAN : changeColor(COLOR_CYAN); break;
|
||||
case IR24_OLD_AQUA : changeColor(COLOR_AQUA); break;
|
||||
case IR24_OLD_BLUE : changeColor(COLOR_BLUE); break;
|
||||
case IR24_OLD_DEEPBLUE : changeColor(COLOR_DEEPBLUE); break;
|
||||
case IR24_OLD_PURPLE : changeColor(COLOR_PURPLE); break;
|
||||
case IR24_OLD_MAGENTA : changeColor(COLOR_MAGENTA); break;
|
||||
case IR24_OLD_PINK : changeColor(COLOR_PINK); break;
|
||||
case IR24_OLD_WHITE : changeColor(COLOR_WHITE); changeEffect(FX_MODE_STATIC); break;
|
||||
case IR24_OLD_FLASH : presetFallback(1, FX_MODE_COLORTWINKLE, 0); break;
|
||||
case IR24_OLD_STROBE : presetFallback(2, FX_MODE_RAINBOW_CYCLE, 0); break;
|
||||
case IR24_OLD_FADE : presetFallback(3, FX_MODE_BREATH, 0); break;
|
||||
case IR24_OLD_SMOOTH : presetFallback(4, FX_MODE_RAINBOW, 0); break;
|
||||
default: return;
|
||||
}
|
||||
lastValidCode = code;
|
||||
@@ -292,28 +397,26 @@ void decodeIR24CT(uint32_t code)
|
||||
case IR24_CT_DARKER : decBrightness(); break;
|
||||
case IR24_CT_OFF : if (bri > 0) briLast = bri; bri = 0; break;
|
||||
case IR24_CT_ON : bri = briLast; break;
|
||||
case IR24_CT_RED : colorFromUint32(COLOR_RED); break;
|
||||
case IR24_CT_REDDISH : colorFromUint32(COLOR_REDDISH); break;
|
||||
case IR24_CT_ORANGE : colorFromUint32(COLOR_ORANGE); break;
|
||||
case IR24_CT_YELLOWISH : colorFromUint32(COLOR_YELLOWISH); break;
|
||||
case IR24_CT_YELLOW : colorFromUint32(COLOR_YELLOW); break;
|
||||
case IR24_CT_GREEN : colorFromUint32(COLOR_GREEN); break;
|
||||
case IR24_CT_GREENISH : colorFromUint32(COLOR_GREENISH); break;
|
||||
case IR24_CT_TURQUOISE : colorFromUint32(COLOR_TURQUOISE); break;
|
||||
case IR24_CT_CYAN : colorFromUint32(COLOR_CYAN); break;
|
||||
case IR24_CT_AQUA : colorFromUint32(COLOR_AQUA); break;
|
||||
case IR24_CT_BLUE : colorFromUint32(COLOR_BLUE); break;
|
||||
case IR24_CT_DEEPBLUE : colorFromUint32(COLOR_DEEPBLUE); break;
|
||||
case IR24_CT_PURPLE : colorFromUint32(COLOR_PURPLE); break;
|
||||
case IR24_CT_MAGENTA : colorFromUint32(COLOR_MAGENTA); break;
|
||||
case IR24_CT_PINK : colorFromUint32(COLOR_PINK); break;
|
||||
case IR24_CT_COLDWHITE : colorFromUint32(COLOR2_COLDWHITE); effectCurrent = 0; break;
|
||||
case IR24_CT_WARMWHITE : colorFromUint32(COLOR2_WARMWHITE); effectCurrent = 0; break;
|
||||
case IR24_CT_CTPLUS : colorFromUint32(COLOR2_COLDWHITE2); effectCurrent = 0; break;
|
||||
case IR24_CT_CTMINUS : colorFromUint32(COLOR2_WARMWHITE2); effectCurrent = 0; break;
|
||||
case IR24_CT_MEMORY : {
|
||||
if (col[3] > 0) col[3] = 0;
|
||||
else colorFromUint32(COLOR2_NEUTRALWHITE); effectCurrent = 0; } break;
|
||||
case IR24_CT_RED : changeColor(COLOR_RED); break;
|
||||
case IR24_CT_REDDISH : changeColor(COLOR_REDDISH); break;
|
||||
case IR24_CT_ORANGE : changeColor(COLOR_ORANGE); break;
|
||||
case IR24_CT_YELLOWISH : changeColor(COLOR_YELLOWISH); break;
|
||||
case IR24_CT_YELLOW : changeColor(COLOR_YELLOW); break;
|
||||
case IR24_CT_GREEN : changeColor(COLOR_GREEN); break;
|
||||
case IR24_CT_GREENISH : changeColor(COLOR_GREENISH); break;
|
||||
case IR24_CT_TURQUOISE : changeColor(COLOR_TURQUOISE); break;
|
||||
case IR24_CT_CYAN : changeColor(COLOR_CYAN); break;
|
||||
case IR24_CT_AQUA : changeColor(COLOR_AQUA); break;
|
||||
case IR24_CT_BLUE : changeColor(COLOR_BLUE); break;
|
||||
case IR24_CT_DEEPBLUE : changeColor(COLOR_DEEPBLUE); break;
|
||||
case IR24_CT_PURPLE : changeColor(COLOR_PURPLE); break;
|
||||
case IR24_CT_MAGENTA : changeColor(COLOR_MAGENTA); break;
|
||||
case IR24_CT_PINK : changeColor(COLOR_PINK); break;
|
||||
case IR24_CT_COLDWHITE : changeColor(COLOR_COLDWHITE2, 255); changeEffect(FX_MODE_STATIC); break;
|
||||
case IR24_CT_WARMWHITE : changeColor(COLOR_WARMWHITE2, 0); changeEffect(FX_MODE_STATIC); break;
|
||||
case IR24_CT_CTPLUS : changeColor(COLOR_COLDWHITE, strip.getSegment(strip.getMainSegmentId()).cct+1); changeEffect(FX_MODE_STATIC); break;
|
||||
case IR24_CT_CTMINUS : changeColor(COLOR_WARMWHITE, strip.getSegment(strip.getMainSegmentId()).cct-1); changeEffect(FX_MODE_STATIC); break;
|
||||
case IR24_CT_MEMORY : changeColor(COLOR_NEUTRALWHITE, 127); changeEffect(FX_MODE_STATIC); break;
|
||||
default: return;
|
||||
}
|
||||
lastValidCode = code;
|
||||
@@ -321,57 +424,53 @@ void decodeIR24CT(uint32_t code)
|
||||
|
||||
void decodeIR40(uint32_t code)
|
||||
{
|
||||
WS2812FX::Segment& seg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
|
||||
byte r = R(seg.colors[0]);
|
||||
byte g = G(seg.colors[0]);
|
||||
byte b = B(seg.colors[0]);
|
||||
byte w = W(seg.colors[0]);
|
||||
switch (code) {
|
||||
case IR40_BPLUS : incBrightness(); break;
|
||||
case IR40_BMINUS : decBrightness(); break;
|
||||
case IR40_OFF : if (bri > 0) briLast = bri; bri = 0; break;
|
||||
case IR40_ON : bri = briLast; break;
|
||||
case IR40_RED : colorFromUint24(COLOR_RED); break;
|
||||
case IR40_REDDISH : colorFromUint24(COLOR_REDDISH); break;
|
||||
case IR40_ORANGE : colorFromUint24(COLOR_ORANGE); break;
|
||||
case IR40_YELLOWISH : colorFromUint24(COLOR_YELLOWISH); break;
|
||||
case IR40_YELLOW : colorFromUint24(COLOR_YELLOW); break;
|
||||
case IR40_GREEN : colorFromUint24(COLOR_GREEN); break;
|
||||
case IR40_GREENISH : colorFromUint24(COLOR_GREENISH); break;
|
||||
case IR40_TURQUOISE : colorFromUint24(COLOR_TURQUOISE); break;
|
||||
case IR40_CYAN : colorFromUint24(COLOR_CYAN); break;
|
||||
case IR40_AQUA : colorFromUint24(COLOR_AQUA); break;
|
||||
case IR40_BLUE : colorFromUint24(COLOR_BLUE); break;
|
||||
case IR40_DEEPBLUE : colorFromUint24(COLOR_DEEPBLUE); break;
|
||||
case IR40_PURPLE : colorFromUint24(COLOR_PURPLE); break;
|
||||
case IR40_MAGENTA : colorFromUint24(COLOR_MAGENTA); break;
|
||||
case IR40_PINK : colorFromUint24(COLOR_PINK); break;
|
||||
case IR40_WARMWHITE2 : {
|
||||
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_WARMWHITE2); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_WARMWHITE2); } break;
|
||||
case IR40_WARMWHITE : {
|
||||
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_WARMWHITE); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_WARMWHITE); } break;
|
||||
case IR40_WHITE : {
|
||||
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_NEUTRALWHITE); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_NEUTRALWHITE); } break;
|
||||
case IR40_COLDWHITE : {
|
||||
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_COLDWHITE); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_COLDWHITE); } break;
|
||||
case IR40_COLDWHITE2 : {
|
||||
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_COLDWHITE2); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_COLDWHITE2); } break;
|
||||
case IR40_WPLUS : relativeChangeWhite(10); break;
|
||||
case IR40_WMINUS : relativeChangeWhite(-10, 5); break;
|
||||
case IR40_WOFF : whiteLast = col[3]; col[3] = 0; break;
|
||||
case IR40_WON : col[3] = whiteLast; break;
|
||||
case IR40_W25 : bri = 63; break;
|
||||
case IR40_W50 : bri = 127; break;
|
||||
case IR40_W75 : bri = 191; break;
|
||||
case IR40_W100 : bri = 255; break;
|
||||
case IR40_QUICK : changeEffectSpeed( 16); break;
|
||||
case IR40_SLOW : changeEffectSpeed(-16); break;
|
||||
case IR40_JUMP7 : changeEffectIntensity( 16); break;
|
||||
case IR40_AUTO : changeEffectIntensity(-16); break;
|
||||
case IR40_BPLUS : incBrightness(); break;
|
||||
case IR40_BMINUS : decBrightness(); break;
|
||||
case IR40_OFF : if (bri > 0) briLast = bri; bri = 0; break;
|
||||
case IR40_ON : bri = briLast; break;
|
||||
case IR40_RED : changeColor(COLOR_RED); break;
|
||||
case IR40_REDDISH : changeColor(COLOR_REDDISH); break;
|
||||
case IR40_ORANGE : changeColor(COLOR_ORANGE); break;
|
||||
case IR40_YELLOWISH : changeColor(COLOR_YELLOWISH); break;
|
||||
case IR40_YELLOW : changeColor(COLOR_YELLOW); break;
|
||||
case IR40_GREEN : changeColor(COLOR_GREEN); break;
|
||||
case IR40_GREENISH : changeColor(COLOR_GREENISH); break;
|
||||
case IR40_TURQUOISE : changeColor(COLOR_TURQUOISE); break;
|
||||
case IR40_CYAN : changeColor(COLOR_CYAN); break;
|
||||
case IR40_AQUA : changeColor(COLOR_AQUA); break;
|
||||
case IR40_BLUE : changeColor(COLOR_BLUE); break;
|
||||
case IR40_DEEPBLUE : changeColor(COLOR_DEEPBLUE); break;
|
||||
case IR40_PURPLE : changeColor(COLOR_PURPLE); break;
|
||||
case IR40_MAGENTA : changeColor(COLOR_MAGENTA); break;
|
||||
case IR40_PINK : changeColor(COLOR_PINK); break;
|
||||
case IR40_WARMWHITE2 : changeColor(COLOR_WARMWHITE2, 0); changeEffect(FX_MODE_STATIC); break;
|
||||
case IR40_WARMWHITE : changeColor(COLOR_WARMWHITE, 63); changeEffect(FX_MODE_STATIC); break;
|
||||
case IR40_WHITE : changeColor(COLOR_NEUTRALWHITE, 127); changeEffect(FX_MODE_STATIC); break;
|
||||
case IR40_COLDWHITE : changeColor(COLOR_COLDWHITE, 191); changeEffect(FX_MODE_STATIC); break;
|
||||
case IR40_COLDWHITE2 : changeColor(COLOR_COLDWHITE2, 255); changeEffect(FX_MODE_STATIC); break;
|
||||
case IR40_WPLUS : changeWhite(10); break;
|
||||
case IR40_WMINUS : changeWhite(-10); break;
|
||||
case IR40_WOFF : if (w) whiteLast = w; changeColor(RGBW32(r, g, b, 0)); break;
|
||||
case IR40_WON : changeColor(RGBW32(r, g, b, whiteLast)); break;
|
||||
case IR40_W25 : bri = 63; break;
|
||||
case IR40_W50 : bri = 127; break;
|
||||
case IR40_W75 : bri = 191; break;
|
||||
case IR40_W100 : bri = 255; break;
|
||||
case IR40_QUICK : changeEffectSpeed( 16); break;
|
||||
case IR40_SLOW : changeEffectSpeed(-16); break;
|
||||
case IR40_JUMP7 : changeEffectIntensity( 16); break;
|
||||
case IR40_AUTO : changeEffectIntensity(-16); break;
|
||||
case IR40_JUMP3 : presetFallback(1, FX_MODE_STATIC, 0); break;
|
||||
case IR40_FADE3 : presetFallback(2, FX_MODE_BREATH, 0); break;
|
||||
case IR40_FADE7 : presetFallback(3, FX_MODE_FIRE_FLICKER, 0); break;
|
||||
case IR40_FLASH : presetFallback(4, FX_MODE_RAINBOW, 0); break;
|
||||
default: return;
|
||||
}
|
||||
lastValidCode = code;
|
||||
}
|
||||
@@ -379,62 +478,51 @@ void decodeIR40(uint32_t code)
|
||||
void decodeIR44(uint32_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case IR44_BPLUS : incBrightness(); break;
|
||||
case IR44_BMINUS : decBrightness(); break;
|
||||
case IR44_OFF : if (bri > 0) briLast = bri; bri = 0; break;
|
||||
case IR44_ON : bri = briLast; break;
|
||||
case IR44_RED : colorFromUint24(COLOR_RED); break;
|
||||
case IR44_REDDISH : colorFromUint24(COLOR_REDDISH); break;
|
||||
case IR44_ORANGE : colorFromUint24(COLOR_ORANGE); break;
|
||||
case IR44_YELLOWISH : colorFromUint24(COLOR_YELLOWISH); break;
|
||||
case IR44_YELLOW : colorFromUint24(COLOR_YELLOW); break;
|
||||
case IR44_GREEN : colorFromUint24(COLOR_GREEN); break;
|
||||
case IR44_GREENISH : colorFromUint24(COLOR_GREENISH); break;
|
||||
case IR44_TURQUOISE : colorFromUint24(COLOR_TURQUOISE); break;
|
||||
case IR44_CYAN : colorFromUint24(COLOR_CYAN); break;
|
||||
case IR44_AQUA : colorFromUint24(COLOR_AQUA); break;
|
||||
case IR44_BLUE : colorFromUint24(COLOR_BLUE); break;
|
||||
case IR44_DEEPBLUE : colorFromUint24(COLOR_DEEPBLUE); break;
|
||||
case IR44_PURPLE : colorFromUint24(COLOR_PURPLE); break;
|
||||
case IR44_MAGENTA : colorFromUint24(COLOR_MAGENTA); break;
|
||||
case IR44_PINK : colorFromUint24(COLOR_PINK); break;
|
||||
case IR44_WHITE : {
|
||||
if (strip.hasWhiteChannel()) {
|
||||
if (col[3] > 0) col[3] = 0;
|
||||
else { colorFromUint32(COLOR2_NEUTRALWHITE); effectCurrent = 0; }
|
||||
} else colorFromUint24(COLOR_NEUTRALWHITE); } break;
|
||||
case IR44_WARMWHITE2 : {
|
||||
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_WARMWHITE2); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_WARMWHITE2); } break;
|
||||
case IR44_WARMWHITE : {
|
||||
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_WARMWHITE); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_WARMWHITE); } break;
|
||||
case IR44_COLDWHITE : {
|
||||
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_COLDWHITE); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_COLDWHITE); } break;
|
||||
case IR44_COLDWHITE2 : {
|
||||
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_COLDWHITE2); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_COLDWHITE2); } break;
|
||||
case IR44_REDPLUS : relativeChange(&effectCurrent, 1, 0, MODE_COUNT); break;
|
||||
case IR44_REDMINUS : relativeChange(&effectCurrent, -1, 0); break;
|
||||
case IR44_GREENPLUS : relativeChange(&effectPalette, 1, 0, strip.getPaletteCount() -1); break;
|
||||
case IR44_GREENMINUS : relativeChange(&effectPalette, -1, 0); break;
|
||||
case IR44_BLUEPLUS : changeEffectIntensity( 16); break;
|
||||
case IR44_BLUEMINUS : changeEffectIntensity(-16); break;
|
||||
case IR44_QUICK : changeEffectSpeed( 16); break;
|
||||
case IR44_SLOW : changeEffectSpeed(-16); break;
|
||||
case IR44_BPLUS : incBrightness(); break;
|
||||
case IR44_BMINUS : decBrightness(); break;
|
||||
case IR44_OFF : if (bri > 0) briLast = bri; bri = 0; break;
|
||||
case IR44_ON : bri = briLast; break;
|
||||
case IR44_RED : changeColor(COLOR_RED); break;
|
||||
case IR44_REDDISH : changeColor(COLOR_REDDISH); break;
|
||||
case IR44_ORANGE : changeColor(COLOR_ORANGE); break;
|
||||
case IR44_YELLOWISH : changeColor(COLOR_YELLOWISH); break;
|
||||
case IR44_YELLOW : changeColor(COLOR_YELLOW); break;
|
||||
case IR44_GREEN : changeColor(COLOR_GREEN); break;
|
||||
case IR44_GREENISH : changeColor(COLOR_GREENISH); break;
|
||||
case IR44_TURQUOISE : changeColor(COLOR_TURQUOISE); break;
|
||||
case IR44_CYAN : changeColor(COLOR_CYAN); break;
|
||||
case IR44_AQUA : changeColor(COLOR_AQUA); break;
|
||||
case IR44_BLUE : changeColor(COLOR_BLUE); break;
|
||||
case IR44_DEEPBLUE : changeColor(COLOR_DEEPBLUE); break;
|
||||
case IR44_PURPLE : changeColor(COLOR_PURPLE); break;
|
||||
case IR44_MAGENTA : changeColor(COLOR_MAGENTA); break;
|
||||
case IR44_PINK : changeColor(COLOR_PINK); break;
|
||||
case IR44_WHITE : changeColor(COLOR_NEUTRALWHITE, 127); changeEffect(FX_MODE_STATIC); break;
|
||||
case IR44_WARMWHITE2 : changeColor(COLOR_WARMWHITE2, 0); changeEffect(FX_MODE_STATIC); break;
|
||||
case IR44_WARMWHITE : changeColor(COLOR_WARMWHITE, 63); changeEffect(FX_MODE_STATIC); break;
|
||||
case IR44_COLDWHITE : changeColor(COLOR_COLDWHITE, 191); changeEffect(FX_MODE_STATIC); break;
|
||||
case IR44_COLDWHITE2 : changeColor(COLOR_COLDWHITE2, 255); changeEffect(FX_MODE_STATIC); break;
|
||||
case IR44_REDPLUS : changeEffect(relativeChange(effectCurrent, 1, 0, MODE_COUNT -1)); break;
|
||||
case IR44_REDMINUS : changeEffect(relativeChange(effectCurrent, -1, 0, MODE_COUNT -1)); break;
|
||||
case IR44_GREENPLUS : changePalette(relativeChange(effectPalette, 1, 0, strip.getPaletteCount() -1)); break;
|
||||
case IR44_GREENMINUS : changePalette(relativeChange(effectPalette, -1, 0, strip.getPaletteCount() -1)); break;
|
||||
case IR44_BLUEPLUS : changeEffectIntensity( 16); break;
|
||||
case IR44_BLUEMINUS : changeEffectIntensity(-16); break;
|
||||
case IR44_QUICK : changeEffectSpeed( 16); break;
|
||||
case IR44_SLOW : changeEffectSpeed(-16); break;
|
||||
case IR44_DIY1 : presetFallback(1, FX_MODE_STATIC, 0); break;
|
||||
case IR44_DIY2 : presetFallback(2, FX_MODE_BREATH, 0); break;
|
||||
case IR44_DIY3 : presetFallback(3, FX_MODE_FIRE_FLICKER, 0); break;
|
||||
case IR44_DIY4 : presetFallback(4, FX_MODE_RAINBOW, 0); break;
|
||||
case IR44_DIY5 : presetFallback(5, FX_MODE_METEOR_SMOOTH, 0); break;
|
||||
case IR44_DIY6 : presetFallback(6, FX_MODE_RAIN, 0); break;
|
||||
case IR44_AUTO : effectCurrent = FX_MODE_STATIC; break;
|
||||
case IR44_FLASH : effectCurrent = FX_MODE_PALETTE; break;
|
||||
case IR44_JUMP3 : bri = 63; break;
|
||||
case IR44_JUMP7 : bri = 127; break;
|
||||
case IR44_FADE3 : bri = 191; break;
|
||||
case IR44_FADE7 : bri = 255; break;
|
||||
case IR44_AUTO : changeEffect(FX_MODE_STATIC); break;
|
||||
case IR44_FLASH : changeEffect(FX_MODE_PALETTE); break;
|
||||
case IR44_JUMP3 : bri = 63; break;
|
||||
case IR44_JUMP7 : bri = 127; break;
|
||||
case IR44_FADE3 : bri = 191; break;
|
||||
case IR44_FADE7 : bri = 255; break;
|
||||
default: return;
|
||||
}
|
||||
lastValidCode = code;
|
||||
}
|
||||
@@ -442,28 +530,28 @@ void decodeIR44(uint32_t code)
|
||||
void decodeIR21(uint32_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case IR21_BRIGHTER: incBrightness(); break;
|
||||
case IR21_DARKER: decBrightness(); break;
|
||||
case IR21_OFF: if (bri > 0) briLast = bri; bri = 0; break;
|
||||
case IR21_ON: bri = briLast; break;
|
||||
case IR21_RED: colorFromUint32(COLOR_RED); break;
|
||||
case IR21_REDDISH: colorFromUint32(COLOR_REDDISH); break;
|
||||
case IR21_ORANGE: colorFromUint32(COLOR_ORANGE); break;
|
||||
case IR21_YELLOWISH: colorFromUint32(COLOR_YELLOWISH); break;
|
||||
case IR21_GREEN: colorFromUint32(COLOR_GREEN); break;
|
||||
case IR21_GREENISH: colorFromUint32(COLOR_GREENISH); break;
|
||||
case IR21_TURQUOISE: colorFromUint32(COLOR_TURQUOISE); break;
|
||||
case IR21_CYAN: colorFromUint32(COLOR_CYAN); break;
|
||||
case IR21_BLUE: colorFromUint32(COLOR_BLUE); break;
|
||||
case IR21_DEEPBLUE: colorFromUint32(COLOR_DEEPBLUE); break;
|
||||
case IR21_PURPLE: colorFromUint32(COLOR_PURPLE); break;
|
||||
case IR21_PINK: colorFromUint32(COLOR_PINK); break;
|
||||
case IR21_WHITE: colorFromUint32(COLOR_WHITE); effectCurrent = 0; break;
|
||||
case IR21_FLASH: presetFallback(1, FX_MODE_COLORTWINKLE, 0); break;
|
||||
case IR21_STROBE: presetFallback(2, FX_MODE_RAINBOW_CYCLE, 0); break;
|
||||
case IR21_FADE: presetFallback(3, FX_MODE_BREATH, 0); break;
|
||||
case IR21_SMOOTH: presetFallback(4, FX_MODE_RAINBOW, 0); break;
|
||||
default: return;
|
||||
case IR21_BRIGHTER: incBrightness(); break;
|
||||
case IR21_DARKER: decBrightness(); break;
|
||||
case IR21_OFF: if (bri > 0) briLast = bri; bri = 0; break;
|
||||
case IR21_ON: bri = briLast; break;
|
||||
case IR21_RED: changeColor(COLOR_RED); break;
|
||||
case IR21_REDDISH: changeColor(COLOR_REDDISH); break;
|
||||
case IR21_ORANGE: changeColor(COLOR_ORANGE); break;
|
||||
case IR21_YELLOWISH: changeColor(COLOR_YELLOWISH); break;
|
||||
case IR21_GREEN: changeColor(COLOR_GREEN); break;
|
||||
case IR21_GREENISH: changeColor(COLOR_GREENISH); break;
|
||||
case IR21_TURQUOISE: changeColor(COLOR_TURQUOISE); break;
|
||||
case IR21_CYAN: changeColor(COLOR_CYAN); break;
|
||||
case IR21_BLUE: changeColor(COLOR_BLUE); break;
|
||||
case IR21_DEEPBLUE: changeColor(COLOR_DEEPBLUE); break;
|
||||
case IR21_PURPLE: changeColor(COLOR_PURPLE); break;
|
||||
case IR21_PINK: changeColor(COLOR_PINK); break;
|
||||
case IR21_WHITE: changeColor(COLOR_WHITE); changeEffect(FX_MODE_STATIC); break;
|
||||
case IR21_FLASH: presetFallback(1, FX_MODE_COLORTWINKLE, 0); break;
|
||||
case IR21_STROBE: presetFallback(2, FX_MODE_RAINBOW_CYCLE, 0); break;
|
||||
case IR21_FADE: presetFallback(3, FX_MODE_BREATH, 0); break;
|
||||
case IR21_SMOOTH: presetFallback(4, FX_MODE_RAINBOW, 0); break;
|
||||
default: return;
|
||||
}
|
||||
lastValidCode = code;
|
||||
}
|
||||
@@ -471,31 +559,32 @@ void decodeIR21(uint32_t code)
|
||||
void decodeIR6(uint32_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case IR6_POWER: toggleOnOff(); break;
|
||||
case IR6_CHANNEL_UP: incBrightness(); break;
|
||||
case IR6_CHANNEL_DOWN: decBrightness(); break;
|
||||
case IR6_VOLUME_UP: relativeChange(&effectCurrent, 1, 0, MODE_COUNT); break; // next effect
|
||||
case IR6_VOLUME_DOWN: // next palette
|
||||
relativeChange(&effectPalette, 1, 0, strip.getPaletteCount() -1);
|
||||
case IR6_POWER: toggleOnOff(); break;
|
||||
case IR6_CHANNEL_UP: incBrightness(); break;
|
||||
case IR6_CHANNEL_DOWN: decBrightness(); break;
|
||||
case IR6_VOLUME_UP: changeEffect(relativeChange(effectCurrent, 1, 0, MODE_COUNT -1)); break;
|
||||
case IR6_VOLUME_DOWN: changePalette(relativeChange(effectPalette, 1, 0, strip.getPaletteCount() -1));
|
||||
switch(lastIR6ColourIdx) {
|
||||
case 0: colorFromUint32(COLOR_RED); break;
|
||||
case 1: colorFromUint32(COLOR_REDDISH); break;
|
||||
case 2: colorFromUint32(COLOR_ORANGE); break;
|
||||
case 3: colorFromUint32(COLOR_YELLOWISH); break;
|
||||
case 4: colorFromUint32(COLOR_GREEN); break;
|
||||
case 5: colorFromUint32(COLOR_GREENISH); break;
|
||||
case 6: colorFromUint32(COLOR_TURQUOISE); break;
|
||||
case 7: colorFromUint32(COLOR_CYAN); break;
|
||||
case 8: colorFromUint32(COLOR_BLUE); break;
|
||||
case 9: colorFromUint32(COLOR_DEEPBLUE); break;
|
||||
case 10:colorFromUint32(COLOR_PURPLE); break;
|
||||
case 11:colorFromUint32(COLOR_PINK); break;
|
||||
case 12:colorFromUint32(COLOR_WHITE); break;
|
||||
default: break;
|
||||
case 0: changeColor(COLOR_RED); break;
|
||||
case 1: changeColor(COLOR_REDDISH); break;
|
||||
case 2: changeColor(COLOR_ORANGE); break;
|
||||
case 3: changeColor(COLOR_YELLOWISH); break;
|
||||
case 4: changeColor(COLOR_GREEN); break;
|
||||
case 5: changeColor(COLOR_GREENISH); break;
|
||||
case 6: changeColor(COLOR_TURQUOISE); break;
|
||||
case 7: changeColor(COLOR_CYAN); break;
|
||||
case 8: changeColor(COLOR_BLUE); break;
|
||||
case 9: changeColor(COLOR_DEEPBLUE); break;
|
||||
case 10:changeColor(COLOR_PURPLE); break;
|
||||
case 11:changeColor(COLOR_PINK); break;
|
||||
case 12:changeColor(COLOR_WHITE); break;
|
||||
default: break;
|
||||
}
|
||||
lastIR6ColourIdx++;
|
||||
if(lastIR6ColourIdx > 12) lastIR6ColourIdx = 0; break;
|
||||
case IR6_MUTE: effectCurrent = 0; effectPalette = 0; colorFromUint32(COLOR_WHITE); bri=255; break;
|
||||
if(lastIR6ColourIdx > 12) lastIR6ColourIdx = 0;
|
||||
break;
|
||||
case IR6_MUTE: changeEffect(FX_MODE_STATIC); changePalette(0); changeColor(COLOR_WHITE); bri=255; break;
|
||||
default: return;
|
||||
}
|
||||
lastValidCode = code;
|
||||
}
|
||||
@@ -503,17 +592,15 @@ void decodeIR6(uint32_t code)
|
||||
void decodeIR9(uint32_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case IR9_POWER : toggleOnOff(); break;
|
||||
case IR9_A : presetFallback(1, FX_MODE_COLORTWINKLE, effectPalette); break;
|
||||
case IR9_B : presetFallback(2, FX_MODE_RAINBOW_CYCLE, effectPalette); break;
|
||||
case IR9_C : presetFallback(3, FX_MODE_BREATH, effectPalette); break;
|
||||
case IR9_UP : incBrightness(); break;
|
||||
case IR9_DOWN : decBrightness(); break;
|
||||
//case IR9_UP : changeEffectIntensity(16); break;
|
||||
//case IR9_DOWN : changeEffectIntensity(-16); break;
|
||||
case IR9_LEFT : changeEffectSpeed(-16); break;
|
||||
case IR9_RIGHT : changeEffectSpeed(16); break;
|
||||
case IR9_SELECT : relativeChange(&effectCurrent, 1, 0, MODE_COUNT); break;
|
||||
case IR9_POWER : toggleOnOff(); break;
|
||||
case IR9_A : presetFallback(1, FX_MODE_COLORTWINKLE, effectPalette); break;
|
||||
case IR9_B : presetFallback(2, FX_MODE_RAINBOW_CYCLE, effectPalette); break;
|
||||
case IR9_C : presetFallback(3, FX_MODE_BREATH, effectPalette); break;
|
||||
case IR9_UP : incBrightness(); break;
|
||||
case IR9_DOWN : decBrightness(); break;
|
||||
case IR9_LEFT : changeEffectSpeed(-16); break;
|
||||
case IR9_RIGHT : changeEffectSpeed(16); break;
|
||||
case IR9_SELECT : changeEffect(relativeChange(effectCurrent, 1, 0, MODE_COUNT -1)); break;
|
||||
default: return;
|
||||
}
|
||||
lastValidCode = code;
|
||||
@@ -575,7 +662,7 @@ void decodeIRJson(uint32_t code)
|
||||
cmdStr = fdo["cmd"].as<String>();
|
||||
jsonCmdObj = fdo["cmd"]; //object
|
||||
|
||||
if (jsonCmdObj.isNull())
|
||||
if (jsonCmdObj.isNull()) // we could also use: fdo["cmd"].is<String>()
|
||||
{
|
||||
if (cmdStr.startsWith("!")) {
|
||||
// call limited set of C functions
|
||||
@@ -586,37 +673,33 @@ void decodeIRJson(uint32_t code)
|
||||
lastValidCode = code;
|
||||
decBrightness();
|
||||
} else if (cmdStr.startsWith(F("!presetF"))) { //!presetFallback
|
||||
uint8_t p1 = fdo["PL"] ? fdo["PL"] : 1;
|
||||
uint8_t p2 = fdo["FX"] ? fdo["FX"] : random8(MODE_COUNT);
|
||||
uint8_t p3 = fdo["FP"] ? fdo["FP"] : 0;
|
||||
uint8_t p1 = fdo["PL"] | 1;
|
||||
uint8_t p2 = fdo["FX"] | random8(MODE_COUNT -1);
|
||||
uint8_t p3 = fdo["FP"] | 0;
|
||||
presetFallback(p1, p2, p3);
|
||||
}
|
||||
} else {
|
||||
// HTTP API command
|
||||
if (cmdStr.indexOf("~") || fdo["rpt"])
|
||||
{
|
||||
// repeatable action
|
||||
lastValidCode = code;
|
||||
String apireq = "win"; apireq += '&'; // reduce flash string usage
|
||||
if (cmdStr.indexOf("~") || fdo["rpt"]) lastValidCode = code; // repeatable action
|
||||
if (!cmdStr.startsWith(apireq)) cmdStr = apireq + cmdStr; // if no "win&" prefix
|
||||
if (!irApplyToAllSelected && cmdStr.indexOf(F("SS="))<0) {
|
||||
char tmp[10];
|
||||
sprintf_P(tmp, PSTR("&SS=%d"), strip.getMainSegmentId());
|
||||
cmdStr += tmp;
|
||||
}
|
||||
if (effectCurrent == 0 && cmdStr.indexOf("FP=") > -1) {
|
||||
// setting palette but it wont show because effect is solid
|
||||
effectCurrent = FX_MODE_GRADIENT;
|
||||
}
|
||||
if (!cmdStr.startsWith("win&")) {
|
||||
cmdStr = "win&" + cmdStr;
|
||||
}
|
||||
fdo.clear(); //clear JSON buffer (it is no longer needed)
|
||||
handleSet(nullptr, cmdStr, false); // no colorUpdated() call here
|
||||
fdo.clear(); // clear JSON buffer (it is no longer needed)
|
||||
handleSet(nullptr, cmdStr, false); // no stateUpdated() call here
|
||||
}
|
||||
} else {
|
||||
// command is JSON object
|
||||
// command is JSON object (TODO: currently will not handle irApplyToAllSelected correctly)
|
||||
if (jsonCmdObj[F("psave")].isNull()) deserializeState(jsonCmdObj, CALL_MODE_BUTTON_PRESET);
|
||||
else {
|
||||
uint8_t psave = jsonCmdObj[F("psave")].as<int>();
|
||||
char pname[33];
|
||||
sprintf_P(pname, PSTR("IR Preset %d"), psave);
|
||||
fdo.clear();
|
||||
if (psave > 0 && psave < 251) savePreset(psave, true, pname, fdo);
|
||||
if (psave > 0 && psave < 251) savePreset(psave, pname, fdo);
|
||||
}
|
||||
}
|
||||
releaseJSONBufferLock();
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
//Infrared codes for 24-key remote from http://woodsgood.ca/projects/2015/02/13/rgb-led-strip-controllers-ir-codes/
|
||||
#define IR24_BRIGHTER 0xF700FF
|
||||
#define IR24_DARKER 0xF7807F
|
||||
#define IR24_OFF 0xF740BF
|
||||
#define IR24_OFF 0xF740BF
|
||||
#define IR24_ON 0xF7C03F
|
||||
#define IR24_RED 0xF720DF
|
||||
#define IR24_REDDISH 0xF710EF
|
||||
@@ -35,7 +35,7 @@
|
||||
#define IR24_YELLOW 0xF728D7
|
||||
#define IR24_GREEN 0xF7A05F
|
||||
#define IR24_GREENISH 0xF7906F
|
||||
#define IR24_TURQUOISE 0xF7B04F
|
||||
#define IR24_TURQUOISE 0xF7B04F
|
||||
#define IR24_CYAN 0xF78877
|
||||
#define IR24_AQUA 0xF7A857
|
||||
#define IR24_BLUE 0xF7609F
|
||||
@@ -76,30 +76,30 @@
|
||||
#define IR24_CT_MEMORY 0xF7E817 // MEMORY
|
||||
|
||||
// 24-key defs for old remote control
|
||||
#define IR24_OLD_BRIGHTER 0xFF906F // Brightness Up
|
||||
#define IR24_OLD_DARKER 0xFFB847 // Brightness Down
|
||||
#define IR24_OLD_OFF 0xFFF807 // Power OFF
|
||||
#define IR24_OLD_ON 0xFFB04F // Power On
|
||||
#define IR24_OLD_RED 0xFF9867 // RED
|
||||
#define IR24_OLD_REDDISH 0xFFE817 // Light RED
|
||||
#define IR24_OLD_ORANGE 0xFF02FD // Orange
|
||||
#define IR24_OLD_YELLOWISH 0xFF50AF // Light Orange
|
||||
#define IR24_OLD_YELLOW 0xFF38C7 // YELLOW
|
||||
#define IR24_OLD_GREEN 0xFFD827 // GREEN
|
||||
#define IR24_OLD_GREENISH 0xFF48B7 // Light GREEN
|
||||
#define IR24_OLD_TURQUOISE 0xFF32CD // TURQUOISE
|
||||
#define IR24_OLD_BRIGHTER 0xFF906F // Brightness Up
|
||||
#define IR24_OLD_DARKER 0xFFB847 // Brightness Down
|
||||
#define IR24_OLD_OFF 0xFFF807 // Power OFF
|
||||
#define IR24_OLD_ON 0xFFB04F // Power On
|
||||
#define IR24_OLD_RED 0xFF9867 // RED
|
||||
#define IR24_OLD_REDDISH 0xFFE817 // Light RED
|
||||
#define IR24_OLD_ORANGE 0xFF02FD // Orange
|
||||
#define IR24_OLD_YELLOWISH 0xFF50AF // Light Orange
|
||||
#define IR24_OLD_YELLOW 0xFF38C7 // YELLOW
|
||||
#define IR24_OLD_GREEN 0xFFD827 // GREEN
|
||||
#define IR24_OLD_GREENISH 0xFF48B7 // Light GREEN
|
||||
#define IR24_OLD_TURQUOISE 0xFF32CD // TURQUOISE
|
||||
#define IR24_OLD_CYAN 0xFF7887 // CYAN
|
||||
#define IR24_OLD_AQUA 0xFF28D7 // AQUA
|
||||
#define IR24_OLD_BLUE 0xFF8877 // BLUE
|
||||
#define IR24_OLD_AQUA 0xFF28D7 // AQUA
|
||||
#define IR24_OLD_BLUE 0xFF8877 // BLUE
|
||||
#define IR24_OLD_DEEPBLUE 0xFF6897 // Dark BLUE
|
||||
#define IR24_OLD_PURPLE 0xFF20DF // PURPLE
|
||||
#define IR24_OLD_MAGENTA 0xFF708F // MAGENTA
|
||||
#define IR24_OLD_PINK 0xFFF00F // PINK
|
||||
#define IR24_OLD_WHITE 0xFFA857 // WHITE
|
||||
#define IR24_OLD_FLASH 0xFFB24D // FLASH Mode
|
||||
#define IR24_OLD_STROBE 0xFF00FF // STROBE Mode
|
||||
#define IR24_OLD_FADE 0xFF58A7 // FADE Mode
|
||||
#define IR24_OLD_SMOOTH 0xFF30CF // SMOOTH Mode
|
||||
#define IR24_OLD_PURPLE 0xFF20DF // PURPLE
|
||||
#define IR24_OLD_MAGENTA 0xFF708F // MAGENTA
|
||||
#define IR24_OLD_PINK 0xFFF00F // PINK
|
||||
#define IR24_OLD_WHITE 0xFFA857 // WHITE
|
||||
#define IR24_OLD_FLASH 0xFFB24D // FLASH Mode
|
||||
#define IR24_OLD_STROBE 0xFF00FF // STROBE Mode
|
||||
#define IR24_OLD_FADE 0xFF58A7 // FADE Mode
|
||||
#define IR24_OLD_SMOOTH 0xFF30CF // SMOOTH Mode
|
||||
|
||||
// 40-key defs for blue remote control
|
||||
#define IR40_BPLUS 0xFF3AC5 //
|
||||
@@ -212,32 +212,27 @@
|
||||
#define IR21_FADE 0xFF02FD
|
||||
#define IR21_SMOOTH 0xFFC23D
|
||||
|
||||
#define COLOR_RED 0xFF0000
|
||||
#define COLOR_REDDISH 0xFF7800
|
||||
#define COLOR_ORANGE 0xFFA000
|
||||
#define COLOR_YELLOWISH 0xFFC800
|
||||
#define COLOR_YELLOW 0xFFFF00
|
||||
#define COLOR_GREEN 0x00FF00
|
||||
#define COLOR_GREENISH 0x00FF78
|
||||
#define COLOR_TURQUOISE 0x00FFA0
|
||||
#define COLOR_CYAN 0x00FFDC
|
||||
#define COLOR_AQUA 0x00C8FF
|
||||
#define COLOR_BLUE 0x00A0FF
|
||||
#define COLOR_DEEPBLUE 0x0000FF
|
||||
#define COLOR_PURPLE 0xAA00FF
|
||||
#define COLOR_MAGENTA 0xFF00DC
|
||||
#define COLOR_PINK 0xFF00A0
|
||||
#define COLOR_WHITE 0xFFFFDC
|
||||
#define COLOR_WARMWHITE2 0xFFAA69
|
||||
#define COLOR_WARMWHITE 0xFFBF8E
|
||||
#define COLOR_NEUTRALWHITE 0xFFD4B4
|
||||
#define COLOR_COLDWHITE 0xFFE9D9
|
||||
#define COLOR_COLDWHITE2 0xFFFFFF
|
||||
#define COLOR2_WARMWHITE2 0xFFFF9900
|
||||
#define COLOR2_WARMWHITE 0xFF825A00
|
||||
#define COLOR2_NEUTRALWHITE 0xFF000000
|
||||
#define COLOR2_COLDWHITE 0xFF7F7F7F
|
||||
#define COLOR2_COLDWHITE2 0xFFFFFFFF
|
||||
#define COLOR_RED 0xFF0000
|
||||
#define COLOR_REDDISH 0xFF7800
|
||||
#define COLOR_ORANGE 0xFFA000
|
||||
#define COLOR_YELLOWISH 0xFFC800
|
||||
#define COLOR_YELLOW 0xFFFF00
|
||||
#define COLOR_GREEN 0x00FF00
|
||||
#define COLOR_GREENISH 0x00FF78
|
||||
#define COLOR_TURQUOISE 0x00FFA0
|
||||
#define COLOR_CYAN 0x00FFDC
|
||||
#define COLOR_AQUA 0x00C8FF
|
||||
#define COLOR_BLUE 0x00A0FF
|
||||
#define COLOR_DEEPBLUE 0x0000FF
|
||||
#define COLOR_PURPLE 0xAA00FF
|
||||
#define COLOR_MAGENTA 0xFF00DC
|
||||
#define COLOR_PINK 0xFF00A0
|
||||
#define COLOR_WHITE 0xFFFFFFFF
|
||||
#define COLOR_WARMWHITE2 0xFFFFAA69
|
||||
#define COLOR_WARMWHITE 0xFFFFBF8E
|
||||
#define COLOR_NEUTRALWHITE 0xFFFFD4B4
|
||||
#define COLOR_COLDWHITE 0xFFFFE9D9
|
||||
#define COLOR_COLDWHITE2 0xFFFFFFFF
|
||||
|
||||
#define ACTION_NONE 0
|
||||
#define ACTION_BRIGHT_UP 1
|
||||
@@ -246,4 +241,4 @@
|
||||
#define ACTION_SPEED_DOWN 4
|
||||
#define ACTION_INTENSITY_UP 5
|
||||
#define ACTION_INTENSITY_DOWN 6
|
||||
#define ACTION_POWER 7
|
||||
#define ACTION_POWER 7
|
||||
|
||||
@@ -95,7 +95,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
if (stop > start && of > len -1) of = len -1;
|
||||
strip.setSegment(id, start, stop, grp, spc, of);
|
||||
|
||||
byte segbri = 0;
|
||||
byte segbri = seg.opacity;
|
||||
if (getVal(elem["bri"], &segbri)) {
|
||||
if (segbri > 0) seg.setOpacity(segbri, id);
|
||||
seg.setOption(SEG_OPTION_ON, segbri, id);
|
||||
@@ -177,7 +177,12 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
if (!iarr.isNull()) {
|
||||
uint8_t oldSegId = strip.setPixelSegment(id);
|
||||
|
||||
//freeze and init to black
|
||||
// set brightness immediately and disable transition
|
||||
transitionDelayTemp = 0;
|
||||
jsonTransitionOnce = true;
|
||||
strip.setBrightness(scaledBri(bri), true);
|
||||
|
||||
// freeze and init to black
|
||||
if (!seg.getOption(SEG_OPTION_FREEZE)) {
|
||||
seg.setOption(SEG_OPTION_FREEZE, true);
|
||||
strip.fill(0);
|
||||
@@ -200,7 +205,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
JsonArray icol = iarr[i];
|
||||
if (!icol.isNull()) { //array, e.g. [255,0,0]
|
||||
byte sz = icol.size();
|
||||
if (sz > 0 || sz < 5) copyArray(icol, rgbw);
|
||||
if (sz > 0 && sz < 5) copyArray(icol, rgbw);
|
||||
} else { //hex string, e.g. "FF0000"
|
||||
byte brgbw[] = {0,0,0,0};
|
||||
const char* hexCol = iarr[i];
|
||||
@@ -223,8 +228,8 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
}
|
||||
strip.setPixelSegment(oldSegId);
|
||||
strip.trigger();
|
||||
} else if (!elem["frz"] && iarr.isNull()) { //return to regular effect
|
||||
seg.setOption(SEG_OPTION_FREEZE, false);
|
||||
// } else if (!elem["frz"] && iarr.isNull()) { //return to regular effect
|
||||
// seg.setOption(SEG_OPTION_FREEZE, false);
|
||||
}
|
||||
// send UDP if not in preset and something changed that is not just selection
|
||||
//if (!presetId && (seg.differs(prev) & 0x7F)) stateChanged = true;
|
||||
@@ -238,6 +243,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
{
|
||||
bool stateResponse = root[F("v")] | false;
|
||||
|
||||
bool onBefore = bri;
|
||||
getVal(root["bri"], &bri);
|
||||
|
||||
bool on = root["on"] | (bri > 0);
|
||||
@@ -245,6 +251,15 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
|
||||
if (root["on"].is<const char*>() && root["on"].as<const char*>()[0] == 't') toggleOnOff();
|
||||
|
||||
if (bri && !onBefore) { // unfreeze all segments when turning on
|
||||
for (uint8_t s=0; s < strip.getMaxSegments(); s++) {
|
||||
strip.getSegment(s).setOption(SEG_OPTION_FREEZE, false, s);
|
||||
}
|
||||
if (realtimeMode && !realtimeOverride && useMainSegmentOnly) { // keep live segment frozen if live
|
||||
strip.getMainSegment().setOption(SEG_OPTION_FREEZE, true, strip.getMainSegmentId());
|
||||
}
|
||||
}
|
||||
|
||||
int tr = -1;
|
||||
if (!presetId || currentPlaylist < 0) { //do not apply transition time from preset if playlist active, as it would override playlist transition times
|
||||
tr = root[F("transition")] | -1;
|
||||
@@ -263,15 +278,15 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
transitionDelayTemp *= 100;
|
||||
jsonTransitionOnce = true;
|
||||
}
|
||||
strip.setTransition(transitionDelayTemp);
|
||||
strip.setTransition(transitionDelayTemp); // required here for color transitions to have correct duration
|
||||
|
||||
tr = root[F("tb")] | -1;
|
||||
if (tr >= 0) strip.timebase = ((uint32_t)tr) - millis();
|
||||
|
||||
JsonObject nl = root["nl"];
|
||||
nightlightActive = nl["on"] | nightlightActive;
|
||||
nightlightDelayMins = nl["dur"] | nightlightDelayMins;
|
||||
nightlightMode = nl["mode"] | nightlightMode;
|
||||
nightlightDelayMins = nl["dur"] | nightlightDelayMins;
|
||||
nightlightMode = nl["mode"] | nightlightMode;
|
||||
nightlightTargetBri = nl[F("tbri")] | nightlightTargetBri;
|
||||
|
||||
JsonObject udpn = root["udpn"];
|
||||
@@ -287,16 +302,23 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
|
||||
doReboot = root[F("rb")] | doReboot;
|
||||
|
||||
strip.setMainSegmentId(root[F("mainseg")] | strip.getMainSegmentId()); // must be before realtimeLock() if "live"
|
||||
|
||||
realtimeOverride = root[F("lor")] | realtimeOverride;
|
||||
if (realtimeOverride > 2) realtimeOverride = REALTIME_OVERRIDE_ALWAYS;
|
||||
|
||||
if (root.containsKey("live")) {
|
||||
bool lv = root["live"];
|
||||
if (lv) realtimeLock(65000); //enter realtime without timeout
|
||||
else realtimeTimeout = 0; //cancel realtime mode immediately
|
||||
if (realtimeMode && useMainSegmentOnly) {
|
||||
strip.getMainSegment().setOption(SEG_OPTION_FREEZE, !realtimeOverride, strip.getMainSegmentId());
|
||||
}
|
||||
|
||||
strip.setMainSegmentId(root[F("mainseg")] | strip.getMainSegmentId());
|
||||
if (root.containsKey("live")) {
|
||||
if (root["live"].as<bool>()) {
|
||||
transitionDelayTemp = 0;
|
||||
jsonTransitionOnce = true;
|
||||
realtimeLock(65000);
|
||||
} else {
|
||||
exitRealtime();
|
||||
}
|
||||
}
|
||||
|
||||
int it = 0;
|
||||
JsonVariant segVar = root["seg"];
|
||||
@@ -330,19 +352,13 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef WLED_DISABLE_CRONIXIE
|
||||
if (root["nx"].is<const char*>()) {
|
||||
strncpy(cronixieDisplay, root["nx"], 6);
|
||||
}
|
||||
#endif
|
||||
|
||||
usermods.readFromJsonState(root);
|
||||
|
||||
loadLedmap = root[F("ledmap")] | loadLedmap;
|
||||
|
||||
byte ps = root[F("psave")];
|
||||
if (ps > 0) {
|
||||
savePreset(ps, true, nullptr, root);
|
||||
savePreset(ps, nullptr, root);
|
||||
} else {
|
||||
ps = root[F("pdel")]; //deletion
|
||||
if (ps > 0) {
|
||||
@@ -503,15 +519,6 @@ void serializeInfo(JsonObject root)
|
||||
|
||||
JsonObject leds = root.createNestedObject("leds");
|
||||
leds[F("count")] = strip.getLengthTotal();
|
||||
leds[F("rgbw")] = strip.hasRGBWBus(); //deprecated, use info.leds.lc
|
||||
leds[F("wv")] = false; //deprecated, use info.leds.lc
|
||||
leds["cct"] = correctWB || strip.hasCCTBus(); //deprecated, use info.leds.lc
|
||||
switch (Bus::getAutoWhiteMode()) {
|
||||
case RGBW_MODE_MANUAL_ONLY:
|
||||
case RGBW_MODE_DUAL:
|
||||
if (strip.hasWhiteChannel()) leds[F("wv")] = true;
|
||||
break;
|
||||
}
|
||||
|
||||
leds[F("pwr")] = strip.currentMilliamps;
|
||||
leds["fps"] = strip.getFps();
|
||||
@@ -530,11 +537,17 @@ void serializeInfo(JsonObject root)
|
||||
|
||||
leds["lc"] = totalLC;
|
||||
|
||||
leds[F("rgbw")] = strip.hasRGBWBus(); // deprecated, use info.leds.lc
|
||||
leds[F("wv")] = totalLC & 0x02; // deprecated, true if white slider should be displayed for any segment
|
||||
leds["cct"] = totalLC & 0x04; // deprecated, use info.leds.lc
|
||||
|
||||
root[F("str")] = syncToggleReceive;
|
||||
|
||||
root[F("name")] = serverDescription;
|
||||
root[F("udpport")] = udpPort;
|
||||
root["live"] = (bool)realtimeMode;
|
||||
root[F("liveseg")] = useMainSegmentOnly ? strip.getMainSegmentId() : -1; // if using main segment only for live
|
||||
//root[F("mso")] = useMainSegmentOnly; // using main segment only for live
|
||||
|
||||
switch (realtimeMode) {
|
||||
case REALTIME_MODE_INACTIVE: root["lm"] = ""; break;
|
||||
@@ -619,7 +632,7 @@ void serializeInfo(JsonObject root)
|
||||
#ifndef WLED_DISABLE_BLYNK
|
||||
os += 0x20;
|
||||
#endif
|
||||
#ifndef WLED_DISABLE_CRONIXIE
|
||||
#ifdef USERMOD_CRONIXIE
|
||||
os += 0x10;
|
||||
#endif
|
||||
#ifndef WLED_DISABLE_FILESYSTEM
|
||||
|
||||
@@ -3,15 +3,24 @@
|
||||
/*
|
||||
* LED methods
|
||||
*/
|
||||
void setValuesFromFirstSelectedSeg()
|
||||
|
||||
void setValuesFromMainSeg() { setValuesFromSegment(strip.getMainSegmentId()); }
|
||||
void setValuesFromFirstSelectedSeg() { setValuesFromSegment(strip.getFirstSelectedSegId()); }
|
||||
void setValuesFromSegment(uint8_t s)
|
||||
{
|
||||
WS2812FX::Segment& seg = strip.getFirstSelectedSeg();
|
||||
colorFromUint32(seg.colors[0]);
|
||||
colorFromUint32(seg.colors[1], true);
|
||||
effectCurrent = seg.mode;
|
||||
effectSpeed = seg.speed;
|
||||
WS2812FX::Segment& seg = strip.getSegment(s);
|
||||
col[0] = R(seg.colors[0]);
|
||||
col[1] = G(seg.colors[0]);
|
||||
col[2] = B(seg.colors[0]);
|
||||
col[3] = W(seg.colors[0]);
|
||||
colSec[0] = R(seg.colors[1]);
|
||||
colSec[1] = G(seg.colors[1]);
|
||||
colSec[2] = B(seg.colors[1]);
|
||||
colSec[3] = W(seg.colors[1]);
|
||||
effectCurrent = seg.mode;
|
||||
effectSpeed = seg.speed;
|
||||
effectIntensity = seg.intensity;
|
||||
effectPalette = seg.palette;
|
||||
effectPalette = seg.palette;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,20 +35,14 @@ void applyValuesToSelectedSegs()
|
||||
WS2812FX::Segment& seg = strip.getSegment(i);
|
||||
if (i != firstSel && (!seg.isActive() || !seg.isSelected())) continue;
|
||||
|
||||
if (effectSpeed != selsegPrev.speed) {
|
||||
seg.speed = effectSpeed; stateChanged = true;}
|
||||
if (effectIntensity != selsegPrev.intensity) {
|
||||
seg.intensity = effectIntensity; stateChanged = true;}
|
||||
if (effectPalette != selsegPrev.palette) {
|
||||
seg.palette = effectPalette; stateChanged = true;}
|
||||
if (effectCurrent != selsegPrev.mode) {
|
||||
strip.setMode(i, effectCurrent); stateChanged = true;}
|
||||
uint32_t col0 = RGBW32(col[0],col[1],col[2],col[3]);
|
||||
if (effectSpeed != selsegPrev.speed) {seg.speed = effectSpeed; stateChanged = true;}
|
||||
if (effectIntensity != selsegPrev.intensity) {seg.intensity = effectIntensity; stateChanged = true;}
|
||||
if (effectPalette != selsegPrev.palette) {seg.palette = effectPalette; stateChanged = true;}
|
||||
if (effectCurrent != selsegPrev.mode) {strip.setMode(i, effectCurrent); stateChanged = true;}
|
||||
uint32_t col0 = RGBW32( col[0], col[1], col[2], col[3]);
|
||||
uint32_t col1 = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]);
|
||||
if (col0 != selsegPrev.colors[0]) {
|
||||
seg.setColor(0, col0, i); stateChanged = true;}
|
||||
if (col1 != selsegPrev.colors[1]) {
|
||||
seg.setColor(1, col1, i); stateChanged = true;}
|
||||
if (col0 != selsegPrev.colors[0]) {seg.setColor(0, col0, i); stateChanged = true;}
|
||||
if (col1 != selsegPrev.colors[1]) {seg.setColor(1, col1, i); stateChanged = true;}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +100,6 @@ void stateUpdated(byte callMode) {
|
||||
setValuesFromFirstSelectedSeg();
|
||||
|
||||
if (bri != briOld || stateChanged) {
|
||||
if (realtimeTimeout == UINT32_MAX) realtimeTimeout = 0;
|
||||
if (stateChanged) currentPreset = 0; //something changed, so we are no longer in the preset
|
||||
|
||||
if (callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) notify(callMode);
|
||||
|
||||
@@ -169,7 +169,6 @@ void handleTime() {
|
||||
updateLocalTime();
|
||||
checkTimers();
|
||||
checkCountdown();
|
||||
handleOverlays();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,6 +466,7 @@ void calculateSunriseAndSunset() {
|
||||
int minUTC = getSunriseUTC(year(localTime), month(localTime), day(localTime), latitude, longitude);
|
||||
if (minUTC) {
|
||||
// there is a sunrise
|
||||
if (minUTC < 0) minUTC += 24*60; // add a day if negative
|
||||
tim_0.tm_hour = minUTC / 60;
|
||||
tim_0.tm_min = minUTC % 60;
|
||||
sunrise = tz->toLocal(mktime(&tim_0) + utcOffsetSecs);
|
||||
@@ -478,6 +478,7 @@ void calculateSunriseAndSunset() {
|
||||
minUTC = getSunriseUTC(year(localTime), month(localTime), day(localTime), latitude, longitude, true);
|
||||
if (minUTC) {
|
||||
// there is a sunset
|
||||
if (minUTC < 0) minUTC += 24*60; // add a day if negative
|
||||
tim_0.tm_hour = minUTC / 60;
|
||||
tim_0.tm_min = minUTC % 60;
|
||||
sunset = tz->toLocal(mktime(&tim_0) + utcOffsetSecs);
|
||||
|
||||
@@ -3,31 +3,6 @@
|
||||
/*
|
||||
* Used to draw clock overlays over the strip
|
||||
*/
|
||||
|
||||
void initCronixie()
|
||||
{
|
||||
if (overlayCurrent == 3 && dP[0] == 255) //if dP[0] is 255, cronixie is not yet init'ed
|
||||
{
|
||||
setCronixie();
|
||||
strip.getSegment(0).grouping = 10; //10 LEDs per digit
|
||||
} else if (dP[0] < 255 && overlayCurrent != 3)
|
||||
{
|
||||
strip.getSegment(0).grouping = 1;
|
||||
dP[0] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//handleOverlays is essentially the equivalent of usermods.loop
|
||||
void handleOverlays()
|
||||
{
|
||||
initCronixie();
|
||||
if (overlayCurrent == 3) {
|
||||
_overlayCronixie();//Diamex cronixie clock kit
|
||||
strip.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void _overlayAnalogClock()
|
||||
{
|
||||
@@ -114,253 +89,9 @@ void _overlayAnalogCountdown()
|
||||
|
||||
void handleOverlayDraw() {
|
||||
usermods.handleOverlayDraw();
|
||||
if (!overlayCurrent) return;
|
||||
switch (overlayCurrent)
|
||||
{
|
||||
case 1: _overlayAnalogClock(); break;
|
||||
case 3: _drawOverlayCronixie(); break;
|
||||
}
|
||||
if (overlayCurrent == 1) _overlayAnalogClock();
|
||||
}
|
||||
|
||||
/*
|
||||
* Support for the Cronixie clock
|
||||
* Support for the Cronixie clock has moved to a usermod, compile with "-D USERMOD_CRONIXIE" to enable
|
||||
*/
|
||||
|
||||
#ifndef WLED_DISABLE_CRONIXIE
|
||||
byte _digitOut[6] = {10,10,10,10,10,10};
|
||||
|
||||
byte getSameCodeLength(char code, int index, char const cronixieDisplay[])
|
||||
{
|
||||
byte counter = 0;
|
||||
|
||||
for (int i = index+1; i < 6; i++)
|
||||
{
|
||||
if (cronixieDisplay[i] == code)
|
||||
{
|
||||
counter++;
|
||||
} else {
|
||||
return counter;
|
||||
}
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
|
||||
void setCronixie()
|
||||
{
|
||||
/*
|
||||
* digit purpose index
|
||||
* 0-9 | 0-9 (incl. random)
|
||||
* 10 | blank
|
||||
* 11 | blank, bg off
|
||||
* 12 | test upw.
|
||||
* 13 | test dnw.
|
||||
* 14 | binary AM/PM
|
||||
* 15 | BB upper +50 for no trailing 0
|
||||
* 16 | BBB
|
||||
* 17 | BBBB
|
||||
* 18 | BBBBB
|
||||
* 19 | BBBBBB
|
||||
* 20 | H
|
||||
* 21 | HH
|
||||
* 22 | HHH
|
||||
* 23 | HHHH
|
||||
* 24 | M
|
||||
* 25 | MM
|
||||
* 26 | MMM
|
||||
* 27 | MMMM
|
||||
* 28 | MMMMM
|
||||
* 29 | MMMMMM
|
||||
* 30 | S
|
||||
* 31 | SS
|
||||
* 32 | SSS
|
||||
* 33 | SSSS
|
||||
* 34 | SSSSS
|
||||
* 35 | SSSSSS
|
||||
* 36 | Y
|
||||
* 37 | YY
|
||||
* 38 | YYYY
|
||||
* 39 | I
|
||||
* 40 | II
|
||||
* 41 | W
|
||||
* 42 | WW
|
||||
* 43 | D
|
||||
* 44 | DD
|
||||
* 45 | DDD
|
||||
* 46 | V
|
||||
* 47 | VV
|
||||
* 48 | VVV
|
||||
* 49 | VVVV
|
||||
* 50 | VVVVV
|
||||
* 51 | VVVVVV
|
||||
* 52 | v
|
||||
* 53 | vv
|
||||
* 54 | vvv
|
||||
* 55 | vvvv
|
||||
* 56 | vvvvv
|
||||
* 57 | vvvvvv
|
||||
*/
|
||||
|
||||
//H HourLower | HH - Hour 24. | AH - Hour 12. | HHH Hour of Month | HHHH Hour of Year
|
||||
//M MinuteUpper | MM Minute of Hour | MMM Minute of 12h | MMMM Minute of Day | MMMMM Minute of Month | MMMMMM Minute of Year
|
||||
//S SecondUpper | SS Second of Minute | SSS Second of 10 Minute | SSSS Second of Hour | SSSSS Second of Day | SSSSSS Second of Week
|
||||
//B AM/PM | BB 0-6/6-12/12-18/18-24 | BBB 0-3... | BBBB 0-1.5... | BBBBB 0-1 | BBBBBB 0-0.5
|
||||
|
||||
//Y YearLower | YY - Year LU | YYYY - Std.
|
||||
//I MonthLower | II - Month of Year
|
||||
//W Week of Month | WW Week of Year
|
||||
//D Day of Week | DD Day Of Month | DDD Day Of Year
|
||||
|
||||
DEBUG_PRINT("cset ");
|
||||
DEBUG_PRINTLN(cronixieDisplay);
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
dP[i] = 10;
|
||||
switch (cronixieDisplay[i])
|
||||
{
|
||||
case '_': dP[i] = 10; break;
|
||||
case '-': dP[i] = 11; break;
|
||||
case 'r': dP[i] = random(1,7); break; //random btw. 1-6
|
||||
case 'R': dP[i] = random(0,10); break; //random btw. 0-9
|
||||
//case 't': break; //Test upw.
|
||||
//case 'T': break; //Test dnw.
|
||||
case 'b': dP[i] = 14 + getSameCodeLength('b',i,cronixieDisplay); i = i+dP[i]-14; break;
|
||||
case 'B': dP[i] = 14 + getSameCodeLength('B',i,cronixieDisplay); i = i+dP[i]-14; break;
|
||||
case 'h': dP[i] = 70 + getSameCodeLength('h',i,cronixieDisplay); i = i+dP[i]-70; break;
|
||||
case 'H': dP[i] = 20 + getSameCodeLength('H',i,cronixieDisplay); i = i+dP[i]-20; break;
|
||||
case 'A': dP[i] = 108; i++; break;
|
||||
case 'a': dP[i] = 58; i++; break;
|
||||
case 'm': dP[i] = 74 + getSameCodeLength('m',i,cronixieDisplay); i = i+dP[i]-74; break;
|
||||
case 'M': dP[i] = 24 + getSameCodeLength('M',i,cronixieDisplay); i = i+dP[i]-24; break;
|
||||
case 's': dP[i] = 80 + getSameCodeLength('s',i,cronixieDisplay); i = i+dP[i]-80; break; //refresh more often bc. of secs
|
||||
case 'S': dP[i] = 30 + getSameCodeLength('S',i,cronixieDisplay); i = i+dP[i]-30; break;
|
||||
case 'Y': dP[i] = 36 + getSameCodeLength('Y',i,cronixieDisplay); i = i+dP[i]-36; break;
|
||||
case 'y': dP[i] = 86 + getSameCodeLength('y',i,cronixieDisplay); i = i+dP[i]-86; break;
|
||||
case 'I': dP[i] = 39 + getSameCodeLength('I',i,cronixieDisplay); i = i+dP[i]-39; break; //Month. Don't ask me why month and minute both start with M.
|
||||
case 'i': dP[i] = 89 + getSameCodeLength('i',i,cronixieDisplay); i = i+dP[i]-89; break;
|
||||
//case 'W': break;
|
||||
//case 'w': break;
|
||||
case 'D': dP[i] = 43 + getSameCodeLength('D',i,cronixieDisplay); i = i+dP[i]-43; break;
|
||||
case 'd': dP[i] = 93 + getSameCodeLength('d',i,cronixieDisplay); i = i+dP[i]-93; break;
|
||||
case '0': dP[i] = 0; break;
|
||||
case '1': dP[i] = 1; break;
|
||||
case '2': dP[i] = 2; break;
|
||||
case '3': dP[i] = 3; break;
|
||||
case '4': dP[i] = 4; break;
|
||||
case '5': dP[i] = 5; break;
|
||||
case '6': dP[i] = 6; break;
|
||||
case '7': dP[i] = 7; break;
|
||||
case '8': dP[i] = 8; break;
|
||||
case '9': dP[i] = 9; break;
|
||||
//case 'V': break; //user var0
|
||||
//case 'v': break; //user var1
|
||||
}
|
||||
}
|
||||
DEBUG_PRINT("result ");
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
DEBUG_PRINT((int)dP[i]);
|
||||
DEBUG_PRINT(" ");
|
||||
}
|
||||
DEBUG_PRINTLN((int)dP[5]);
|
||||
|
||||
_overlayCronixie(); //refresh
|
||||
}
|
||||
|
||||
void _overlayCronixie()
|
||||
{
|
||||
byte h = hour(localTime);
|
||||
byte h0 = h;
|
||||
byte m = minute(localTime);
|
||||
byte s = second(localTime);
|
||||
byte d = day(localTime);
|
||||
byte mi = month(localTime);
|
||||
int y = year(localTime);
|
||||
//this has to be changed in time for 22nd century
|
||||
y -= 2000; if (y<0) y += 30; //makes countdown work
|
||||
|
||||
if (useAMPM && !countdownMode)
|
||||
{
|
||||
if (h>12) h-=12;
|
||||
else if (h==0) h+=12;
|
||||
}
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
if (dP[i] < 12) _digitOut[i] = dP[i];
|
||||
else {
|
||||
if (dP[i] < 65)
|
||||
{
|
||||
switch(dP[i])
|
||||
{
|
||||
case 21: _digitOut[i] = h/10; _digitOut[i+1] = h- _digitOut[i]*10; i++; break; //HH
|
||||
case 25: _digitOut[i] = m/10; _digitOut[i+1] = m- _digitOut[i]*10; i++; break; //MM
|
||||
case 31: _digitOut[i] = s/10; _digitOut[i+1] = s- _digitOut[i]*10; i++; break; //SS
|
||||
|
||||
case 20: _digitOut[i] = h- (h/10)*10; break; //H
|
||||
case 24: _digitOut[i] = m/10; break; //M
|
||||
case 30: _digitOut[i] = s/10; break; //S
|
||||
|
||||
case 43: _digitOut[i] = weekday(localTime); _digitOut[i]--; if (_digitOut[i]<1) _digitOut[i]= 7; break; //D
|
||||
case 44: _digitOut[i] = d/10; _digitOut[i+1] = d- _digitOut[i]*10; i++; break; //DD
|
||||
case 40: _digitOut[i] = mi/10; _digitOut[i+1] = mi- _digitOut[i]*10; i++; break; //II
|
||||
case 37: _digitOut[i] = y/10; _digitOut[i+1] = y- _digitOut[i]*10; i++; break; //YY
|
||||
case 39: _digitOut[i] = 2; _digitOut[i+1] = 0; _digitOut[i+2] = y/10; _digitOut[i+3] = y- _digitOut[i+2]*10; i+=3; break; //YYYY
|
||||
|
||||
//case 16: _digitOut[i+2] = ((h0/3)&1)?1:0; i++; //BBB (BBBB NI)
|
||||
//case 15: _digitOut[i+1] = (h0>17 || (h0>5 && h0<12))?1:0; i++; //BB
|
||||
case 14: _digitOut[i] = (h0>11)?1:0; break; //B
|
||||
}
|
||||
} else
|
||||
{
|
||||
switch(dP[i])
|
||||
{
|
||||
case 71: _digitOut[i] = h/10; _digitOut[i+1] = h- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //hh
|
||||
case 75: _digitOut[i] = m/10; _digitOut[i+1] = m- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //mm
|
||||
case 81: _digitOut[i] = s/10; _digitOut[i+1] = s- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //ss
|
||||
//case 66: _digitOut[i+2] = ((h0/3)&1)?1:10; i++; //bbb (bbbb NI)
|
||||
//case 65: _digitOut[i+1] = (h0>17 || (h0>5 && h0<12))?1:10; i++; //bb
|
||||
case 64: _digitOut[i] = (h0>11)?1:10; break; //b
|
||||
|
||||
case 93: _digitOut[i] = weekday(localTime); _digitOut[i]--; if (_digitOut[i]<1) _digitOut[i]= 7; break; //d
|
||||
case 94: _digitOut[i] = d/10; _digitOut[i+1] = d- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //dd
|
||||
case 90: _digitOut[i] = mi/10; _digitOut[i+1] = mi- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //ii
|
||||
case 87: _digitOut[i] = y/10; _digitOut[i+1] = y- _digitOut[i]*10; i++; break; //yy
|
||||
case 89: _digitOut[i] = 2; _digitOut[i+1] = 0; _digitOut[i+2] = y/10; _digitOut[i+3] = y- _digitOut[i+2]*10; i+=3; break; //yyyy
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _drawOverlayCronixie()
|
||||
{
|
||||
byte offsets[] = {5, 0, 6, 1, 7, 2, 8, 3, 9, 4};
|
||||
|
||||
for (uint16_t i = 0; i < 6; i++)
|
||||
{
|
||||
byte o = 10*i;
|
||||
byte excl = 10;
|
||||
if(_digitOut[i] < 10) excl = offsets[_digitOut[i]];
|
||||
excl += o;
|
||||
|
||||
if (cronixieBacklight && _digitOut[i] <11)
|
||||
{
|
||||
uint32_t col = strip.gamma32(strip.getSegment(0).colors[1]);
|
||||
for (uint16_t j=o; j< o+10; j++) {
|
||||
if (j != excl) strip.setPixelColor(j, col);
|
||||
}
|
||||
} else
|
||||
{
|
||||
for (uint16_t j=o; j< o+10; j++) {
|
||||
if (j != excl) strip.setPixelColor(j, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else // WLED_DISABLE_CRONIXIE
|
||||
byte getSameCodeLength(char code, int index, char const cronixieDisplay[]) { return 0; }
|
||||
void setCronixie() {}
|
||||
void _overlayCronixie() {}
|
||||
void _drawOverlayCronixie() {}
|
||||
#endif
|
||||
|
||||
@@ -53,7 +53,8 @@ enum struct PinOwner : uint8_t {
|
||||
// #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_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_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA, // 0x17 // Usermod "quinled-an-penta.h"
|
||||
UM_BME280 = USERMOD_ID_BME280 // 0x18 // Usermod "usermod_bme280.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");
|
||||
|
||||
|
||||
@@ -50,12 +50,13 @@ bool applyPreset(byte index, byte callMode)
|
||||
return false;
|
||||
}
|
||||
|
||||
void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj)
|
||||
void savePreset(byte index, const char* pname, JsonObject saveobj)
|
||||
{
|
||||
if (index == 0 || (index > 250 && persist) || (index<255 && !persist)) return;
|
||||
if (index == 0 || (index > 250 && index < 255)) return;
|
||||
char tmp[12];
|
||||
JsonObject sObj = saveobj;
|
||||
|
||||
bool persist = (index != 255);
|
||||
const char *filename = persist ? "/presets.json" : "/tmp.json";
|
||||
|
||||
if (!fileDoc) {
|
||||
|
||||
@@ -84,11 +84,13 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
autoSegments = request->hasArg(F("MS"));
|
||||
correctWB = request->hasArg(F("CCT"));
|
||||
cctFromRgb = request->hasArg(F("CR"));
|
||||
strip.cctBlending = request->arg(F("CB")).toInt();
|
||||
Bus::setCCTBlend(strip.cctBlending);
|
||||
Bus::setAutoWhiteMode(request->arg(F("AW")).toInt());
|
||||
strip.setTargetFps(request->arg(F("FR")).toInt());
|
||||
strip.cctBlending = request->arg(F("CB")).toInt();
|
||||
Bus::setCCTBlend(strip.cctBlending);
|
||||
strip.autoWhiteMode = (request->arg(F("AW")).toInt());
|
||||
Bus::setAutoWhiteMode(strip.autoWhiteMode);
|
||||
strip.setTargetFps(request->arg(F("FR")).toInt());
|
||||
|
||||
bool busesChanged = false;
|
||||
for (uint8_t s = 0; s < WLED_MAX_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
|
||||
@@ -96,7 +98,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
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 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
|
||||
if (!request->hasArg(lp)) {
|
||||
DEBUG_PRINTLN(F("No data.")); break;
|
||||
@@ -108,7 +110,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
}
|
||||
type = request->arg(lt).toInt();
|
||||
type |= request->hasArg(rf) << 7; // off refresh override
|
||||
skip = request->hasArg(sl) ? LED_SKIP_AMOUNT : 0;
|
||||
skip = request->arg(sl).toInt();
|
||||
colorOrder = request->arg(co).toInt();
|
||||
start = (request->hasArg(ls)) ? request->arg(ls).toInt() : t;
|
||||
if (request->hasArg(lc) && request->arg(lc).toInt() > 0) {
|
||||
@@ -120,8 +122,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
// actual finalization is done in WLED::loop() (removing old busses and adding new)
|
||||
if (busConfigs[s] != nullptr) delete busConfigs[s];
|
||||
busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder, request->hasArg(cv), skip);
|
||||
doInitBusses = true;
|
||||
busesChanged = true;
|
||||
}
|
||||
//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++) {
|
||||
@@ -145,6 +148,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
irPin = -1;
|
||||
}
|
||||
irEnabled = request->arg(F("IT")).toInt();
|
||||
irApplyToAllSelected = !request->hasArg(F("MSO"));
|
||||
|
||||
int hw_rly_pin = request->arg(F("RL")).toInt();
|
||||
if (pinManager.allocatePin(hw_rly_pin,true, PinOwner::Relay)) {
|
||||
@@ -196,6 +200,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
if (t >= 0 && t < 4) strip.paletteBlend = t;
|
||||
t = request->arg(F("BF")).toInt();
|
||||
if (t > 0) briMultiplier = t;
|
||||
|
||||
doInitBusses = busesChanged;
|
||||
}
|
||||
|
||||
//UI
|
||||
@@ -235,6 +241,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
nodeBroadcastEnabled = request->hasArg(F("NB"));
|
||||
|
||||
receiveDirect = request->hasArg(F("RD"));
|
||||
useMainSegmentOnly = request->hasArg(F("MO"));
|
||||
e131SkipOutOfSequence = request->hasArg(F("ES"));
|
||||
e131Multicast = request->hasArg(F("EM"));
|
||||
t = request->arg(F("EP")).toInt();
|
||||
@@ -321,10 +328,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
// force a sunrise/sunset re-calculation
|
||||
calculateSunriseAndSunset();
|
||||
|
||||
if (request->hasArg(F("OL"))) {
|
||||
overlayDefault = request->arg(F("OL")).toInt();
|
||||
overlayCurrent = overlayDefault;
|
||||
}
|
||||
overlayCurrent = request->hasArg(F("OL")) ? 1 : 0;
|
||||
|
||||
overlayMin = request->arg(F("O1")).toInt();
|
||||
overlayMax = request->arg(F("O2")).toInt();
|
||||
@@ -332,10 +336,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
analogClock5MinuteMarks = request->hasArg(F("O5"));
|
||||
analogClockSecondsTrail = request->hasArg(F("OS"));
|
||||
|
||||
#ifndef WLED_DISABLE_CRONIXIE
|
||||
strlcpy(cronixieDisplay,request->arg(F("CX")).c_str(),7);
|
||||
cronixieBacklight = request->hasArg(F("CB"));
|
||||
#endif
|
||||
countdownMode = request->hasArg(F("CE"));
|
||||
countdownYear = request->arg(F("CY")).toInt();
|
||||
countdownMonth = request->arg(F("CI")).toInt();
|
||||
@@ -551,6 +551,8 @@ void parseNumber(const char* str, byte* val, byte minv, byte maxv)
|
||||
{
|
||||
if (str == nullptr || str[0] == '\0') return;
|
||||
if (str[0] == 'r') {*val = random8(minv,maxv); return;}
|
||||
bool wrap = false;
|
||||
if (str[0] == 'w' && strlen(str) > 1) {str++; wrap = true;}
|
||||
if (str[0] == '~') {
|
||||
int out = atoi(str +1);
|
||||
if (out == 0)
|
||||
@@ -563,9 +565,13 @@ void parseNumber(const char* str, byte* val, byte minv, byte maxv)
|
||||
*val = (int)(*val +1) > (int)maxv ? minv : max((int)minv,(*val +1)); //+1, wrap around
|
||||
}
|
||||
} else {
|
||||
out += *val;
|
||||
if (out > maxv) out = maxv;
|
||||
if (out < minv) out = minv;
|
||||
if (wrap && *val == maxv && out > 0) out = minv;
|
||||
else if (wrap && *val == minv && out < 0) out = maxv;
|
||||
else {
|
||||
out += *val;
|
||||
if (out > maxv) out = maxv;
|
||||
if (out < minv) out = minv;
|
||||
}
|
||||
*val = out;
|
||||
}
|
||||
} else
|
||||
@@ -795,7 +801,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
if (pos > 0) {
|
||||
byte temp;
|
||||
for (uint8_t i=0; i<4; i++) {
|
||||
temp = colIn[i];
|
||||
temp = colIn[i];
|
||||
colIn[i] = colInSec[i];
|
||||
colInSec[i] = temp;
|
||||
}
|
||||
@@ -829,13 +835,14 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
|
||||
stateChanged |= (fxModeChanged || speedChanged || intensityChanged || paletteChanged);
|
||||
|
||||
// apply to main and all selected segments to prevent #1618.
|
||||
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
|
||||
WS2812FX::Segment& seg = strip.getSegment(i);
|
||||
if (i != selectedSeg && (singleSegment || !seg.isActive() || !seg.isSelected())) continue;
|
||||
if (i != selectedSeg && (singleSegment || !seg.isActive() || !seg.isSelected())) continue; // skip non main segments if not applying to all
|
||||
if (fxModeChanged) strip.setMode(i, effectIn);
|
||||
if (speedChanged) seg.speed = speedIn;
|
||||
if (speedChanged) seg.speed = speedIn;
|
||||
if (intensityChanged) seg.intensity = intensityIn;
|
||||
if (paletteChanged) seg.palette = paletteIn;
|
||||
if (paletteChanged) seg.palette = paletteIn;
|
||||
}
|
||||
|
||||
//set advanced overlay
|
||||
@@ -931,29 +938,17 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
if (pos > 0) {
|
||||
realtimeOverride = getNumVal(&req, pos);
|
||||
if (realtimeOverride > 2) realtimeOverride = REALTIME_OVERRIDE_ALWAYS;
|
||||
if (realtimeMode && useMainSegmentOnly) {
|
||||
strip.getMainSegment().setOption(SEG_OPTION_FREEZE, !realtimeOverride, strip.getMainSegmentId());
|
||||
}
|
||||
}
|
||||
|
||||
pos = req.indexOf(F("RB"));
|
||||
if (pos > 0) doReboot = true;
|
||||
|
||||
//cronixie
|
||||
#ifndef WLED_DISABLE_CRONIXIE
|
||||
//mode, 1 countdown
|
||||
// clock mode, 0: normal, 1: countdown
|
||||
pos = req.indexOf(F("NM="));
|
||||
if (pos > 0) countdownMode = (req.charAt(pos+3) != '0');
|
||||
|
||||
pos = req.indexOf(F("NX=")); //sets digits to code
|
||||
if (pos > 0) {
|
||||
strlcpy(cronixieDisplay, req.substring(pos + 3, pos + 9).c_str(), 7);
|
||||
setCronixie();
|
||||
}
|
||||
|
||||
pos = req.indexOf(F("NB="));
|
||||
if (pos > 0) //sets backlight
|
||||
{
|
||||
cronixieBacklight = (req.charAt(pos+3) != '0');
|
||||
}
|
||||
#endif
|
||||
|
||||
pos = req.indexOf(F("U0=")); //user var 0
|
||||
if (pos > 0) {
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
// - - - - -
|
||||
|
||||
/* ----- LIBRARIES ----- */
|
||||
#ifdef ESP8266
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "ESPDMX.h"
|
||||
@@ -29,12 +31,12 @@ bool dmxStarted = false;
|
||||
int sendPin = 2; //dafault on ESP8266
|
||||
|
||||
//DMX value array and size. Entry 0 will hold startbyte
|
||||
uint8_t dmxData[dmxMaxChannel] = {};
|
||||
int chanSize;
|
||||
uint8_t dmxDataStore[dmxMaxChannel] = {};
|
||||
int channelSize;
|
||||
|
||||
|
||||
void DMXESPSerial::init() {
|
||||
chanSize = defaultMax;
|
||||
channelSize = defaultMax;
|
||||
|
||||
Serial1.begin(DMXSPEED);
|
||||
pinMode(sendPin, OUTPUT);
|
||||
@@ -48,7 +50,7 @@ void DMXESPSerial::init(int chanQuant) {
|
||||
chanQuant = defaultMax;
|
||||
}
|
||||
|
||||
chanSize = chanQuant;
|
||||
channelSize = chanQuant;
|
||||
|
||||
Serial1.begin(DMXSPEED);
|
||||
pinMode(sendPin, OUTPUT);
|
||||
@@ -61,7 +63,7 @@ uint8_t DMXESPSerial::read(int Channel) {
|
||||
|
||||
if (Channel < 1) Channel = 1;
|
||||
if (Channel > dmxMaxChannel) Channel = dmxMaxChannel;
|
||||
return(dmxData[Channel]);
|
||||
return(dmxDataStore[Channel]);
|
||||
}
|
||||
|
||||
// Function to send DMX data
|
||||
@@ -69,15 +71,15 @@ void DMXESPSerial::write(int Channel, uint8_t value) {
|
||||
if (dmxStarted == false) init();
|
||||
|
||||
if (Channel < 1) Channel = 1;
|
||||
if (Channel > chanSize) Channel = chanSize;
|
||||
if (Channel > channelSize) Channel = channelSize;
|
||||
if (value < 0) value = 0;
|
||||
if (value > 255) value = 255;
|
||||
|
||||
dmxData[Channel] = value;
|
||||
dmxDataStore[Channel] = value;
|
||||
}
|
||||
|
||||
void DMXESPSerial::end() {
|
||||
chanSize = 0;
|
||||
channelSize = 0;
|
||||
Serial1.end();
|
||||
dmxStarted = false;
|
||||
}
|
||||
@@ -96,10 +98,12 @@ void DMXESPSerial::update() {
|
||||
//send data
|
||||
Serial1.begin(DMXSPEED, DMXFORMAT);
|
||||
digitalWrite(sendPin, LOW);
|
||||
Serial1.write(dmxData, chanSize);
|
||||
Serial1.write(dmxDataStore, channelSize);
|
||||
Serial1.flush();
|
||||
delay(1);
|
||||
Serial1.end();
|
||||
}
|
||||
|
||||
// Function to update the DMX bus
|
||||
|
||||
#endif
|
||||
55
wled00/src/dependencies/dmx/LICENSE.md
Normal file
55
wled00/src/dependencies/dmx/LICENSE.md
Normal file
@@ -0,0 +1,55 @@
|
||||
SparkFun License Information
|
||||
============================
|
||||
|
||||
SparkFun uses two different licenses for our files — one for hardware and one for code.
|
||||
|
||||
Hardware
|
||||
---------
|
||||
|
||||
**SparkFun hardware is released under [Creative Commons Share-alike 4.0 International](http://creativecommons.org/licenses/by-sa/4.0/).**
|
||||
|
||||
Note: This is a human-readable summary of (and not a substitute for) the [license](http://creativecommons.org/licenses/by-sa/4.0/legalcode).
|
||||
|
||||
You are free to:
|
||||
|
||||
Share — copy and redistribute the material in any medium or format
|
||||
Adapt — remix, transform, and build upon the material
|
||||
for any purpose, even commercially.
|
||||
The licensor cannot revoke these freedoms as long as you follow the license terms.
|
||||
Under the following terms:
|
||||
|
||||
Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
|
||||
ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.
|
||||
No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
|
||||
Notices:
|
||||
|
||||
You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation.
|
||||
No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material.
|
||||
|
||||
|
||||
Code
|
||||
--------
|
||||
|
||||
**SparkFun code, firmware, and software is released under the MIT License(http://opensource.org/licenses/MIT).**
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 SparkFun Electronics
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
160
wled00/src/dependencies/dmx/SparkFunDMX.cpp
Normal file
160
wled00/src/dependencies/dmx/SparkFunDMX.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
/******************************************************************************
|
||||
SparkFunDMX.h
|
||||
Arduino Library for the SparkFun ESP32 LED to DMX Shield
|
||||
Andy England @ SparkFun Electronics
|
||||
7/22/2019
|
||||
|
||||
Development environment specifics:
|
||||
Arduino IDE 1.6.4
|
||||
|
||||
This code is released under the [MIT License](http://opensource.org/licenses/MIT).
|
||||
Please review the LICENSE.md file included with this example. If you have any questions
|
||||
or concerns with licensing, please contact techsupport@sparkfun.com.
|
||||
Distributed as-is; no warranty is given.
|
||||
******************************************************************************/
|
||||
|
||||
/* ----- LIBRARIES ----- */
|
||||
#ifdef ESP32
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "SparkFunDMX.h"
|
||||
#include <HardwareSerial.h>
|
||||
|
||||
#define dmxMaxChannel 512
|
||||
#define defaultMax 32
|
||||
|
||||
#define DMXSPEED 250000
|
||||
#define DMXFORMAT SERIAL_8N2
|
||||
#define BREAKSPEED 83333
|
||||
#define BREAKFORMAT SERIAL_8N1
|
||||
|
||||
int enablePin = -1; // disable the enable pin because it is not needed
|
||||
int rxPin = -1; // disable the receiving pin because it is not needed
|
||||
int txPin = 2; // transmit DMX data over this pin (default is pin 2)
|
||||
|
||||
//DMX value array and size. Entry 0 will hold startbyte
|
||||
uint8_t dmxData[dmxMaxChannel] = {};
|
||||
int chanSize;
|
||||
int currentChannel = 0;
|
||||
|
||||
HardwareSerial DMXSerial(2);
|
||||
|
||||
/* Interrupt Timer for DMX Receive */
|
||||
hw_timer_t * timer = NULL;
|
||||
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
volatile int _interruptCounter;
|
||||
volatile bool _startCodeDetected = false;
|
||||
|
||||
|
||||
/* Start Code is detected by 21 low interrupts */
|
||||
void IRAM_ATTR onTimer() {
|
||||
if (digitalRead(rxPin) == 1)
|
||||
{
|
||||
_interruptCounter = 0; //If the RX Pin is high, we are not in an interrupt
|
||||
}
|
||||
else
|
||||
{
|
||||
_interruptCounter++;
|
||||
}
|
||||
if (_interruptCounter > 9)
|
||||
{
|
||||
portENTER_CRITICAL_ISR(&timerMux);
|
||||
_startCodeDetected = true;
|
||||
DMXSerial.begin(DMXSPEED, DMXFORMAT, rxPin, txPin);
|
||||
portEXIT_CRITICAL_ISR(&timerMux);
|
||||
_interruptCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SparkFunDMX::initRead(int chanQuant) {
|
||||
|
||||
timer = timerBegin(0, 1, true);
|
||||
timerAttachInterrupt(timer, &onTimer, true);
|
||||
timerAlarmWrite(timer, 320, true);
|
||||
timerAlarmEnable(timer);
|
||||
_READWRITE = _READ;
|
||||
if (chanQuant > dmxMaxChannel || chanQuant <= 0)
|
||||
{
|
||||
chanQuant = defaultMax;
|
||||
}
|
||||
chanSize = chanQuant;
|
||||
pinMode(enablePin, OUTPUT);
|
||||
digitalWrite(enablePin, LOW);
|
||||
pinMode(rxPin, INPUT);
|
||||
}
|
||||
|
||||
// Set up the DMX-Protocol
|
||||
void SparkFunDMX::initWrite (int chanQuant) {
|
||||
|
||||
_READWRITE = _WRITE;
|
||||
if (chanQuant > dmxMaxChannel || chanQuant <= 0) {
|
||||
chanQuant = defaultMax;
|
||||
}
|
||||
|
||||
chanSize = chanQuant + 1; //Add 1 for start code
|
||||
|
||||
DMXSerial.begin(DMXSPEED, DMXFORMAT, rxPin, txPin);
|
||||
pinMode(enablePin, OUTPUT);
|
||||
digitalWrite(enablePin, HIGH);
|
||||
}
|
||||
|
||||
// Function to read DMX data
|
||||
uint8_t SparkFunDMX::read(int Channel) {
|
||||
if (Channel > chanSize) Channel = chanSize;
|
||||
return(dmxData[Channel - 1]); //subtract one to account for start byte
|
||||
}
|
||||
|
||||
// Function to send DMX data
|
||||
void SparkFunDMX::write(int Channel, uint8_t value) {
|
||||
if (Channel < 0) Channel = 0;
|
||||
if (Channel > chanSize) chanSize = Channel;
|
||||
dmxData[0] = 0;
|
||||
dmxData[Channel] = value; //add one to account for start byte
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SparkFunDMX::update() {
|
||||
if (_READWRITE == _WRITE)
|
||||
{
|
||||
//Send DMX break
|
||||
digitalWrite(txPin, HIGH);
|
||||
DMXSerial.begin(BREAKSPEED, BREAKFORMAT, rxPin, txPin);//Begin the Serial port
|
||||
DMXSerial.write(0);
|
||||
DMXSerial.flush();
|
||||
delay(1);
|
||||
DMXSerial.end();
|
||||
|
||||
//Send DMX data
|
||||
DMXSerial.begin(DMXSPEED, DMXFORMAT, rxPin, txPin);//Begin the Serial port
|
||||
DMXSerial.write(dmxData, chanSize);
|
||||
DMXSerial.flush();
|
||||
DMXSerial.end();//clear our DMX array, end the Hardware Serial port
|
||||
}
|
||||
else if (_READWRITE == _READ)//In a perfect world, this function ends serial communication upon packet completion and attaches RX to a CHANGE interrupt so the start code can be read again
|
||||
{
|
||||
if (_startCodeDetected == true)
|
||||
{
|
||||
while (DMXSerial.available())
|
||||
{
|
||||
dmxData[currentChannel++] = DMXSerial.read();
|
||||
}
|
||||
if (currentChannel > chanSize) //Set the channel counter back to 0 if we reach the known end size of our packet
|
||||
{
|
||||
|
||||
portENTER_CRITICAL(&timerMux);
|
||||
_startCodeDetected = false;
|
||||
DMXSerial.flush();
|
||||
DMXSerial.end();
|
||||
portEXIT_CRITICAL(&timerMux);
|
||||
currentChannel = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function to update the DMX bus
|
||||
|
||||
#endif
|
||||
38
wled00/src/dependencies/dmx/SparkFunDMX.h
Normal file
38
wled00/src/dependencies/dmx/SparkFunDMX.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/******************************************************************************
|
||||
SparkFunDMX.h
|
||||
Arduino Library for the SparkFun ESP32 LED to DMX Shield
|
||||
Andy England @ SparkFun Electronics
|
||||
7/22/2019
|
||||
|
||||
Development environment specifics:
|
||||
Arduino IDE 1.6.4
|
||||
|
||||
This code is released under the [MIT License](http://opensource.org/licenses/MIT).
|
||||
Please review the LICENSE.md file included with this example. If you have any questions
|
||||
or concerns with licensing, please contact techsupport@sparkfun.com.
|
||||
Distributed as-is; no warranty is given.
|
||||
******************************************************************************/
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
|
||||
#ifndef SparkFunDMX_h
|
||||
#define SparkFunDMX_h
|
||||
|
||||
// ---- Methods ----
|
||||
|
||||
class SparkFunDMX {
|
||||
public:
|
||||
void initRead(int maxChan);
|
||||
void initWrite(int maxChan);
|
||||
uint8_t read(int Channel);
|
||||
void write(int channel, uint8_t value);
|
||||
void update();
|
||||
private:
|
||||
uint8_t _startCodeValue = 0xFF;
|
||||
bool _READ = true;
|
||||
bool _WRITE = false;
|
||||
bool _READWRITE;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -140,24 +140,51 @@ void notify(byte callMode, bool followUp)
|
||||
|
||||
void realtimeLock(uint32_t timeoutMs, byte md)
|
||||
{
|
||||
if (!realtimeMode && !realtimeOverride){
|
||||
uint16_t totalLen = strip.getLengthTotal();
|
||||
for (uint16_t i = 0; i < totalLen; i++)
|
||||
{
|
||||
strip.setPixelColor(i,0,0,0,0);
|
||||
if (!realtimeMode && !realtimeOverride) {
|
||||
uint16_t stop, start;
|
||||
if (useMainSegmentOnly) {
|
||||
WS2812FX::Segment& mainseg = strip.getMainSegment();
|
||||
start = mainseg.start;
|
||||
stop = mainseg.stop;
|
||||
mainseg.setOption(SEG_OPTION_FREEZE, true, strip.getMainSegmentId());
|
||||
} else {
|
||||
start = 0;
|
||||
stop = strip.getLengthTotal();
|
||||
}
|
||||
// clear strip/segment
|
||||
for (uint16_t i = start; i < stop; i++) strip.setPixelColor(i,0,0,0,0);
|
||||
// if WLED was off and using main segment only, freeze non-main segments so they stay off
|
||||
if (useMainSegmentOnly && bri == 0) {
|
||||
for (uint8_t s=0; s < strip.getMaxSegments(); s++) {
|
||||
strip.getSegment(s).setOption(SEG_OPTION_FREEZE, true, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
realtimeTimeout = millis() + timeoutMs;
|
||||
if (timeoutMs == 255001 || timeoutMs == 65000) realtimeTimeout = UINT32_MAX;
|
||||
// if strip is off (bri==0) and not already in RTM
|
||||
if (bri == 0 && !realtimeMode) {
|
||||
strip.setBrightness(scaledBri(briLast));
|
||||
if (briT == 0 && !realtimeMode && !realtimeOverride) {
|
||||
strip.setBrightness(scaledBri(briLast), true);
|
||||
}
|
||||
|
||||
if (realtimeTimeout != UINT32_MAX) {
|
||||
realtimeTimeout = (timeoutMs == 255001 || timeoutMs == 65000) ? UINT32_MAX : millis() + timeoutMs;
|
||||
}
|
||||
realtimeMode = md;
|
||||
|
||||
if (arlsForceMaxBri && !realtimeOverride) strip.setBrightness(scaledBri(255));
|
||||
if (md == REALTIME_MODE_GENERIC) strip.show();
|
||||
if (realtimeOverride) return;
|
||||
if (arlsForceMaxBri) strip.setBrightness(scaledBri(255), true);
|
||||
if (briT > 0 && md == REALTIME_MODE_GENERIC) strip.show();
|
||||
}
|
||||
|
||||
void exitRealtime() {
|
||||
if (!realtimeMode) return;
|
||||
if (realtimeOverride == REALTIME_OVERRIDE_ONCE) realtimeOverride = REALTIME_OVERRIDE_NONE;
|
||||
strip.setBrightness(scaledBri(bri));
|
||||
realtimeTimeout = 0; // cancel realtime mode immediately
|
||||
realtimeMode = REALTIME_MODE_INACTIVE; // inform UI immediately
|
||||
realtimeIP[0] = 0;
|
||||
if (useMainSegmentOnly) { // unfreeze live segment again
|
||||
strip.getMainSegment().setOption(SEG_OPTION_FREEZE, false, strip.getMainSegmentId());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -187,13 +214,7 @@ void handleNotifications()
|
||||
}
|
||||
|
||||
//unlock strip when realtime UDP times out
|
||||
if (realtimeMode && millis() > realtimeTimeout)
|
||||
{
|
||||
if (realtimeOverride == REALTIME_OVERRIDE_ONCE) realtimeOverride = REALTIME_OVERRIDE_NONE;
|
||||
strip.setBrightness(scaledBri(bri));
|
||||
realtimeMode = REALTIME_MODE_INACTIVE;
|
||||
realtimeIP[0] = 0;
|
||||
}
|
||||
if (realtimeMode && millis() > realtimeTimeout) exitRealtime();
|
||||
|
||||
//receive UDP notifications
|
||||
if (!udpConnected) return;
|
||||
@@ -652,19 +673,16 @@ void sendSysInfoUDP()
|
||||
uint8_t sequenceNumber = 0; // this needs to be shared across all outputs
|
||||
|
||||
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8_t *buffer, uint8_t bri, bool isRGBW) {
|
||||
if (!interfacesInited) return 1; // network not initialised
|
||||
if (!(apActive || interfacesInited) || !client[0] || !length) return 1; // network not initialised or dummy/unset IP address 031522 ajn added check for ap
|
||||
|
||||
WiFiUDP ddpUdp;
|
||||
|
||||
switch (type) {
|
||||
case 0: // DDP
|
||||
{
|
||||
// calclate the number of UDP packets we need to send
|
||||
// calculate the number of UDP packets we need to send
|
||||
uint16_t channelCount = length * 3; // 1 channel for every R,G,B value
|
||||
uint16_t packetCount = channelCount / DDP_CHANNELS_PER_PACKET;
|
||||
if (channelCount % DDP_CHANNELS_PER_PACKET) {
|
||||
packetCount++;
|
||||
}
|
||||
uint16_t packetCount = ((channelCount-1) / DDP_CHANNELS_PER_PACKET) +1;
|
||||
|
||||
// there are 3 channels per RGB pixel
|
||||
uint32_t channel = 0; // TODO: allow specifying the start channel
|
||||
|
||||
@@ -104,10 +104,30 @@
|
||||
#include "../usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h"
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_CRONIXIE
|
||||
#include "../usermods/Cronixie/usermod_cronixie.h"
|
||||
#endif
|
||||
|
||||
#ifdef QUINLED_AN_PENTA
|
||||
#include "../usermods/quinled-an-penta/quinled-an-penta.h"
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_WIZLIGHTS
|
||||
#include "../usermods/wizlights/wizlights.h"
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_WORDCLOCK
|
||||
#include "../usermods/usermod_v2_word_clock/usermod_v2_word_clock.h"
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_MY9291
|
||||
#include "../usermods/MY9291/usermode_MY9291.h"
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_SI7021_MQTT_HA
|
||||
#include "../usermods/Si7021_MQTT_HA/usermod_si7021_mqtt_ha.h"
|
||||
#endif
|
||||
|
||||
void registerUsermods()
|
||||
{
|
||||
/*
|
||||
@@ -200,7 +220,27 @@ void registerUsermods()
|
||||
usermods.add(new UsermodSSDR());
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_CRONIXIE
|
||||
usermods.add(new UsermodCronixie());
|
||||
#endif
|
||||
|
||||
#ifdef QUINLED_AN_PENTA
|
||||
usermods.add(new QuinLEDAnPentaUsermod());
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_WIZLIGHTS
|
||||
usermods.add(new WizLightsUsermod());
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_WORDCLOCK
|
||||
usermods.add(new WordClockUsermod());
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_MY9291
|
||||
usermods.add(new MY9291Usermod());
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_SI7021_MQTT_HA
|
||||
usermods.add(new Si7021_MQTT_HA());
|
||||
#endif
|
||||
}
|
||||
|
||||
104
wled00/wled.cpp
104
wled00/wled.cpp
@@ -28,7 +28,7 @@ void WLED::reset()
|
||||
yield(); // enough time to send response to client
|
||||
}
|
||||
applyBri();
|
||||
DEBUG_PRINTLN(F("MODULE RESET"));
|
||||
DEBUG_PRINTLN(F("WLED RESET"));
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
@@ -62,18 +62,19 @@ void prepareHostname(char* hostname)
|
||||
hostname[pos] = '-';
|
||||
pos++;
|
||||
}
|
||||
// else do nothing - no leading hyphens and do not include hyphens for all other characters.
|
||||
pC++;
|
||||
}
|
||||
// if the hostname is left blank, use the mac address/default mdns name
|
||||
if (pos < 6) {
|
||||
sprintf(hostname + 5, "%*s", 6, escapedMac.c_str() + 6);
|
||||
} else { //last character must not be hyphen
|
||||
while (pos > 0 && hostname[pos -1] == '-') {
|
||||
hostname[pos -1] = 0;
|
||||
pos--;
|
||||
}
|
||||
// else do nothing - no leading hyphens and do not include hyphens for all other characters.
|
||||
pC++;
|
||||
}
|
||||
// if the hostname is left blank, use the mac address/default mdns name
|
||||
if (pos < 6) {
|
||||
sprintf(hostname + 5, "%*s", 6, escapedMac.c_str() + 6);
|
||||
} else { //last character must not be hyphen
|
||||
hostname[pos] = '\0'; // terminate string
|
||||
while (pos > 0 && hostname[pos -1] == '-') {
|
||||
hostname[pos -1] = '\0';
|
||||
pos--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//handle Ethernet connection event
|
||||
@@ -147,33 +148,38 @@ void WLED::loop()
|
||||
yield();
|
||||
handleIO();
|
||||
handleIR();
|
||||
#ifndef WLED_DISABLE_ALEXA
|
||||
handleAlexa();
|
||||
#endif
|
||||
|
||||
yield();
|
||||
|
||||
if (doReboot)
|
||||
if (doReboot && !doInitBusses) // if busses have to be inited & saved, wait until next iteration
|
||||
reset();
|
||||
if (doCloseFile) {
|
||||
closeFile();
|
||||
yield();
|
||||
}
|
||||
|
||||
if (!realtimeMode || realtimeOverride) // block stuff if WARLS/Adalight is enabled
|
||||
if (!realtimeMode || realtimeOverride || (realtimeMode && useMainSegmentOnly)) // block stuff if WARLS/Adalight is enabled
|
||||
{
|
||||
if (apActive)
|
||||
dnsServer.processNextRequest();
|
||||
#ifndef WLED_DISABLE_OTA
|
||||
if (WLED_CONNECTED && aOtaEnabled)
|
||||
ArduinoOTA.handle();
|
||||
#endif
|
||||
if (apActive) dnsServer.processNextRequest();
|
||||
#ifndef WLED_DISABLE_OTA
|
||||
if (WLED_CONNECTED && aOtaEnabled) ArduinoOTA.handle();
|
||||
#endif
|
||||
handleNightlight();
|
||||
handlePlaylist();
|
||||
yield();
|
||||
|
||||
#ifndef WLED_DISABLE_HUESYNC
|
||||
handleHue();
|
||||
#ifndef WLED_DISABLE_BLYNK
|
||||
yield();
|
||||
#endif
|
||||
|
||||
#ifndef WLED_DISABLE_BLYNK
|
||||
handleBlynk();
|
||||
#endif
|
||||
yield();
|
||||
#endif
|
||||
|
||||
yield();
|
||||
|
||||
@@ -270,6 +276,44 @@ void WLED::loop()
|
||||
loops++;
|
||||
#endif // WLED_DEBUG
|
||||
toki.resetTick();
|
||||
|
||||
#if WLED_WATCHDOG_TIMEOUT > 0
|
||||
// we finished our mainloop, reset the watchdog timer
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
esp_task_wdt_reset();
|
||||
#else
|
||||
ESP.wdtFeed();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void WLED::enableWatchdog() {
|
||||
#if WLED_WATCHDOG_TIMEOUT > 0
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
esp_err_t watchdog = esp_task_wdt_init(WLED_WATCHDOG_TIMEOUT, true);
|
||||
DEBUG_PRINT(F("Watchdog enabled: "));
|
||||
if (watchdog == ESP_OK) {
|
||||
DEBUG_PRINTLN(F("OK"));
|
||||
} else {
|
||||
DEBUG_PRINTLN(watchdog);
|
||||
return;
|
||||
}
|
||||
esp_task_wdt_add(NULL);
|
||||
#else
|
||||
ESP.wdtEnable(WLED_WATCHDOG_TIMEOUT * 1000);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void WLED::disableWatchdog() {
|
||||
#if WLED_WATCHDOG_TIMEOUT > 0
|
||||
DEBUG_PRINTLN(F("Watchdog: disabled"));
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
esp_task_wdt_delete(NULL);
|
||||
#else
|
||||
ESP.wdtDisable();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void WLED::setup()
|
||||
@@ -296,6 +340,8 @@ void WLED::setup()
|
||||
DEBUG_PRINT(F("heap "));
|
||||
DEBUG_PRINTLN(ESP.getFreeHeap());
|
||||
|
||||
enableWatchdog();
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
|
||||
if (psramFound()) {
|
||||
// GPIO16/GPIO17 reserved for SPI RAM
|
||||
@@ -358,8 +404,8 @@ void WLED::setup()
|
||||
#endif
|
||||
|
||||
#ifdef WLED_ENABLE_ADALIGHT
|
||||
//Serial RX (Adalight, Improv, Serial JSON) only possible if GPIO3 unused
|
||||
//Serial TX (Debug, Improv, Serial JSON) only possible if GPIO1 unused
|
||||
//Serial RX (Adalight, Improv, Serial JSON) only possible if GPIO3 unused
|
||||
//Serial TX (Debug, Improv, Serial JSON) only possible if GPIO1 unused
|
||||
if (!pinManager.isPinAllocated(3) && !pinManager.isPinAllocated(1)) {
|
||||
Serial.println(F("Ada"));
|
||||
}
|
||||
@@ -383,7 +429,9 @@ void WLED::setup()
|
||||
sprintf(mqttClientID + 5, "%*s", 6, escapedMac.c_str() + 6);
|
||||
}
|
||||
|
||||
#ifdef WLED_ENABLE_ADALIGHT
|
||||
if (Serial.available() > 0 && Serial.peek() == 'I') handleImprovPacket();
|
||||
#endif
|
||||
|
||||
strip.service();
|
||||
|
||||
@@ -393,8 +441,13 @@ void WLED::setup()
|
||||
#ifdef ESP8266
|
||||
wifi_set_sleep_type(NONE_SLEEP_T);
|
||||
#endif
|
||||
WLED::instance().disableWatchdog();
|
||||
DEBUG_PRINTLN(F("Start ArduinoOTA"));
|
||||
});
|
||||
ArduinoOTA.onError([](ota_error_t error) {
|
||||
// reenable watchdog on failed update
|
||||
WLED::instance().enableWatchdog();
|
||||
});
|
||||
if (strlen(cmDNS) > 0)
|
||||
ArduinoOTA.setHostname(cmDNS);
|
||||
}
|
||||
@@ -403,7 +456,10 @@ void WLED::setup()
|
||||
initDMX();
|
||||
#endif
|
||||
|
||||
#ifdef WLED_ENABLE_ADALIGHT
|
||||
if (Serial.available() > 0 && Serial.peek() == 'I') handleImprovPacket();
|
||||
#endif
|
||||
|
||||
// HTTP server page init
|
||||
initServer();
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
/*
|
||||
Main sketch, global variable declarations
|
||||
@title WLED project sketch
|
||||
@version 0.13.0-b7
|
||||
@version 0.13.3
|
||||
@author Christian Schwinne
|
||||
*/
|
||||
|
||||
// version code in format yymmddb (b = daily build)
|
||||
#define VERSION 2202222
|
||||
#define VERSION 2208222
|
||||
|
||||
//uncomment this if you have a "my_config.h" file you'd like to use
|
||||
//#define WLED_USE_MY_CONFIG
|
||||
@@ -26,7 +26,6 @@
|
||||
// You can choose some of these features to disable:
|
||||
//#define WLED_DISABLE_ALEXA // saves 11kb
|
||||
//#define WLED_DISABLE_BLYNK // saves 6kb
|
||||
//#define WLED_DISABLE_CRONIXIE // saves 3kb
|
||||
//#define WLED_DISABLE_HUESYNC // saves 4kb
|
||||
//#define WLED_DISABLE_INFRARED // there is no pin left for this on ESP8266-01, saves 12kb
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
@@ -50,6 +49,12 @@
|
||||
// filesystem specific debugging
|
||||
//#define WLED_DEBUG_FS
|
||||
|
||||
#ifndef WLED_WATCHDOG_TIMEOUT
|
||||
// 3 seconds should be enough to detect a lockup
|
||||
// define WLED_WATCHDOG_TIMEOUT=0 to disable watchdog, default
|
||||
#define WLED_WATCHDOG_TIMEOUT 0
|
||||
#endif
|
||||
|
||||
//optionally disable brownout detector on ESP32.
|
||||
//This is generally a terrible idea, but improves boot success on boards with a 3.3v regulator + cap setup that can't provide 400mA peaks
|
||||
//#define WLED_DISABLE_BROWNOUT_DET
|
||||
@@ -79,6 +84,7 @@
|
||||
#else
|
||||
#include <LittleFS.h>
|
||||
#endif
|
||||
#include "esp_task_wdt.h"
|
||||
#endif
|
||||
|
||||
#include "src/dependencies/network/Network.h"
|
||||
@@ -112,7 +118,11 @@
|
||||
#endif
|
||||
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
#ifdef ESP8266
|
||||
#include "src/dependencies/dmx/ESPDMX.h"
|
||||
#else //ESP32
|
||||
#include "src/dependencies/dmx/SparkFunDMX.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "src/dependencies/e131/ESPAsyncE131.h"
|
||||
@@ -294,7 +304,11 @@ WLED_GLOBAL uint16_t transitionDelay _INIT(750); // default crossfade duratio
|
||||
WLED_GLOBAL byte briMultiplier _INIT(100); // % of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127)
|
||||
|
||||
// User Interface CONFIG
|
||||
WLED_GLOBAL char serverDescription[33] _INIT("WLED"); // Name of module
|
||||
#ifndef SERVERNAME
|
||||
WLED_GLOBAL char serverDescription[33] _INIT("WLED"); // Name of module - use default
|
||||
#else
|
||||
WLED_GLOBAL char serverDescription[33] _INIT(SERVERNAME); // use predefined name
|
||||
#endif
|
||||
WLED_GLOBAL bool syncToggleReceive _INIT(false); // UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise
|
||||
|
||||
// Sync CONFIG
|
||||
@@ -303,7 +317,12 @@ WLED_GLOBAL bool nodeListEnabled _INIT(true);
|
||||
WLED_GLOBAL bool nodeBroadcastEnabled _INIT(true);
|
||||
|
||||
WLED_GLOBAL byte buttonType[WLED_MAX_BUTTONS] _INIT({BTN_TYPE_PUSH});
|
||||
WLED_GLOBAL byte irEnabled _INIT(0); // Infrared receiver
|
||||
#if defined(IRTYPE) && defined(IRPIN)
|
||||
WLED_GLOBAL byte irEnabled _INIT(IRTYPE); // Infrared receiver
|
||||
#else
|
||||
WLED_GLOBAL byte irEnabled _INIT(0); // Infrared receiver disabled
|
||||
#endif
|
||||
WLED_GLOBAL bool irApplyToAllSelected _INIT(true); //apply IR to all selected segments
|
||||
|
||||
WLED_GLOBAL uint16_t udpPort _INIT(21324); // WLED notifier default port
|
||||
WLED_GLOBAL uint16_t udpPort2 _INIT(65506); // WLED notifier supplemental port
|
||||
@@ -339,7 +358,11 @@ WLED_GLOBAL bool arlsDisableGammaCorrection _INIT(true); // activate if
|
||||
WLED_GLOBAL bool arlsForceMaxBri _INIT(false); // enable to force max brightness if source has very dark colors that would be black
|
||||
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
WLED_GLOBAL DMXESPSerial dmx;
|
||||
#ifdef ESP8266
|
||||
WLED_GLOBAL DMXESPSerial dmx;
|
||||
#else //ESP32
|
||||
WLED_GLOBAL SparkFunDMX dmx;
|
||||
#endif
|
||||
WLED_GLOBAL uint16_t e131ProxyUniverse _INIT(0); // output this E1.31 (sACN) / ArtNet universe via MAX485 (0 = disabled)
|
||||
#endif
|
||||
WLED_GLOBAL uint16_t e131Universe _INIT(1); // settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes)
|
||||
@@ -379,18 +402,13 @@ WLED_GLOBAL bool useAMPM _INIT(false); // 12h/24h clock format
|
||||
WLED_GLOBAL byte currentTimezone _INIT(0); // Timezone ID. Refer to timezones array in wled10_ntp.ino
|
||||
WLED_GLOBAL int utcOffsetSecs _INIT(0); // Seconds to offset from UTC before timzone calculation
|
||||
|
||||
WLED_GLOBAL byte overlayDefault _INIT(0); // 0: no overlay 1: analog clock 2: single-digit clock 3: cronixie
|
||||
WLED_GLOBAL byte overlayCurrent _INIT(0); // 0: no overlay 1: analog clock 2: was single-digit clock 3: was cronixie
|
||||
WLED_GLOBAL byte overlayMin _INIT(0), overlayMax _INIT(DEFAULT_LED_COUNT - 1); // boundaries of overlay mode
|
||||
|
||||
WLED_GLOBAL byte analogClock12pixel _INIT(0); // The pixel in your strip where "midnight" would be
|
||||
WLED_GLOBAL bool analogClockSecondsTrail _INIT(false); // Display seconds as trail of LEDs instead of a single pixel
|
||||
WLED_GLOBAL bool analogClock5MinuteMarks _INIT(false); // Light pixels at every 5-minute position
|
||||
|
||||
#ifndef WLED_DISABLE_CRONIXIE
|
||||
WLED_GLOBAL char cronixieDisplay[7] _INIT("HHMMSS"); // Cronixie Display mask. See wled13_cronixie.ino
|
||||
WLED_GLOBAL bool cronixieBacklight _INIT(true); // Allow digits to be back-illuminated
|
||||
#endif
|
||||
|
||||
WLED_GLOBAL bool countdownMode _INIT(false); // Clock will count down towards date
|
||||
WLED_GLOBAL byte countdownYear _INIT(20), countdownMonth _INIT(1); // Countdown target date, year is last two digits
|
||||
WLED_GLOBAL byte countdownDay _INIT(1) , countdownHour _INIT(0);
|
||||
@@ -497,12 +515,6 @@ WLED_GLOBAL bool hueAuthRequired _INIT(false);
|
||||
WLED_GLOBAL bool hueReceived _INIT(false);
|
||||
WLED_GLOBAL bool hueStoreAllowed _INIT(false), hueNewKey _INIT(false);
|
||||
|
||||
// overlays
|
||||
WLED_GLOBAL byte overlayCurrent _INIT(overlayDefault);
|
||||
|
||||
// cronixie
|
||||
WLED_GLOBAL byte dP[] _INIT_N(({ 255, 255, 255, 255, 255, 255 }));
|
||||
|
||||
// countdown
|
||||
WLED_GLOBAL unsigned long countdownTime _INIT(1514764800L);
|
||||
WLED_GLOBAL bool countdownOverTriggered _INIT(true);
|
||||
@@ -540,6 +552,7 @@ WLED_GLOBAL IPAddress realtimeIP _INIT_N(((0, 0, 0, 0)));
|
||||
WLED_GLOBAL unsigned long realtimeTimeout _INIT(0);
|
||||
WLED_GLOBAL uint8_t tpmPacketCount _INIT(0);
|
||||
WLED_GLOBAL uint16_t tpmPayloadFrameSize _INIT(0);
|
||||
WLED_GLOBAL bool useMainSegmentOnly _INIT(false);
|
||||
|
||||
// mqtt
|
||||
WLED_GLOBAL unsigned long lastMqttReconnectAttempt _INIT(0);
|
||||
@@ -698,5 +711,7 @@ public:
|
||||
void initConnection();
|
||||
void initInterfaces();
|
||||
void handleStatusLED();
|
||||
void enableWatchdog();
|
||||
void disableWatchdog();
|
||||
};
|
||||
#endif // WLED_H
|
||||
|
||||
@@ -141,8 +141,7 @@ void loadSettingsFromEEPROM()
|
||||
useAMPM = EEPROM.read(329);
|
||||
strip.gammaCorrectBri = EEPROM.read(330);
|
||||
strip.gammaCorrectCol = EEPROM.read(331);
|
||||
overlayDefault = EEPROM.read(332);
|
||||
if (lastEEPROMversion < 8 && overlayDefault > 0) overlayDefault--; //overlay mode 1 (solid) was removed
|
||||
overlayCurrent = EEPROM.read(332);
|
||||
|
||||
alexaEnabled = EEPROM.read(333);
|
||||
|
||||
@@ -199,11 +198,6 @@ void loadSettingsFromEEPROM()
|
||||
countdownSec = EEPROM.read(2161);
|
||||
setCountdown();
|
||||
|
||||
#ifndef WLED_DISABLE_CRONIXIE
|
||||
readStringFromEEPROM(2165, cronixieDisplay, 6);
|
||||
cronixieBacklight = EEPROM.read(2171);
|
||||
#endif
|
||||
|
||||
//macroBoot = EEPROM.read(2175);
|
||||
macroAlexaOn = EEPROM.read(2176);
|
||||
macroAlexaOff = EEPROM.read(2177);
|
||||
@@ -365,8 +359,6 @@ void loadSettingsFromEEPROM()
|
||||
//2551 - 2559 reserved for Usermods, usable by default
|
||||
//2560 - 2943 usable, NOT reserved (need to increase EEPSIZE accordingly, new WLED core features may override this section)
|
||||
//2944 - 3071 reserved for Usermods (need to increase EEPSIZE to 3072 in const.h)
|
||||
|
||||
overlayCurrent = overlayDefault;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -31,10 +31,6 @@ const ethernet_settings ethernetBoards[] = {
|
||||
},
|
||||
|
||||
// WT32-EHT01
|
||||
// Please note, from my testing only these pins work for LED outputs:
|
||||
// IO2, IO4, IO12, IO14, IO15
|
||||
// These pins do not appear to work from my testing:
|
||||
// IO35, IO36, IO39
|
||||
{
|
||||
1, // eth_address,
|
||||
16, // eth_power,
|
||||
@@ -92,6 +88,16 @@ const ethernet_settings ethernetBoards[] = {
|
||||
18, // eth_mdio,
|
||||
ETH_PHY_LAN8720, // eth_type,
|
||||
ETH_CLOCK_GPIO17_OUT // eth_clk_mode
|
||||
},
|
||||
|
||||
// ESP32-ETHERNET-KIT-VE
|
||||
{
|
||||
0, // eth_address,
|
||||
5, // eth_power,
|
||||
23, // eth_mdc,
|
||||
18, // eth_mdio,
|
||||
ETH_PHY_IP101, // eth_type,
|
||||
ETH_CLOCK_GPIO0_IN // eth_clk_mode
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
float cos_t(float phi)
|
||||
{
|
||||
float x = modd(phi, TWO_PI);
|
||||
if (x < 0) x = -1 * x;
|
||||
int8_t sign = 1;
|
||||
if (x > PI)
|
||||
{
|
||||
|
||||
@@ -86,8 +86,8 @@ void handleSerial()
|
||||
Serial.write(0xC9); Serial.write(0xDA);
|
||||
uint16_t used = strip.getLengthTotal();
|
||||
uint16_t len = used*3;
|
||||
Serial.write((len << 8) & 0xFF);
|
||||
Serial.write( len & 0xFF);
|
||||
Serial.write(highByte(len));
|
||||
Serial.write(lowByte(len));
|
||||
for (uint16_t i=0; i < used; i++) {
|
||||
uint32_t c = strip.getPixelColor(i);
|
||||
Serial.write(qadd8(W(c), R(c))); //R, add white channel to RGB channels as a simple RGBW -> RGB map
|
||||
@@ -174,7 +174,6 @@ void handleSerial()
|
||||
if (!realtimeOverride) setRealtimePixel(pixel++, red, green, blue, 0);
|
||||
if (--count > 0) state = AdaState::Data_Red;
|
||||
else {
|
||||
if (!realtimeMode && bri == 0) strip.setBrightness(briLast);
|
||||
realtimeLock(realtimeTimeoutMs, REALTIME_MODE_ADALIGHT);
|
||||
|
||||
if (!realtimeOverride) strip.show();
|
||||
|
||||
@@ -203,6 +203,7 @@ void initServer()
|
||||
},[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){
|
||||
if(!index){
|
||||
DEBUG_PRINTLN(F("OTA Update Start"));
|
||||
WLED::instance().disableWatchdog();
|
||||
#ifdef ESP8266
|
||||
Update.runAsync(true);
|
||||
#endif
|
||||
@@ -214,6 +215,7 @@ void initServer()
|
||||
DEBUG_PRINTLN(F("Update Success"));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F("Update Failed"));
|
||||
WLED::instance().enableWatchdog();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -136,14 +136,15 @@ bool sendLiveLedsWs(uint32_t wsClient)
|
||||
|
||||
uint16_t used = strip.getLengthTotal();
|
||||
uint16_t n = ((used -1)/MAX_LIVE_LEDS_WS) +1; //only serve every n'th LED if count over MAX_LIVE_LEDS_WS
|
||||
AsyncWebSocketMessageBuffer * wsBuf = ws.makeBuffer(2 + (used*3)/n);
|
||||
uint16_t bufSize = 2 + (used/n)*3;
|
||||
AsyncWebSocketMessageBuffer * wsBuf = ws.makeBuffer(bufSize);
|
||||
if (!wsBuf) return false; //out of memory
|
||||
uint8_t* buffer = wsBuf->get();
|
||||
buffer[0] = 'L';
|
||||
buffer[1] = 1; //version
|
||||
|
||||
uint16_t pos = 2;
|
||||
for (uint16_t i= 0; i < used; i += n)
|
||||
for (uint16_t i= 0; pos < bufSize -2; i += n)
|
||||
{
|
||||
uint32_t c = strip.getPixelColor(i);
|
||||
buffer[pos++] = qadd8(W(c), R(c)); //R, add white channel to RGB channels as a simple RGBW -> RGB map
|
||||
|
||||
@@ -382,9 +382,9 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sappend('c',SET_F("MS"),autoSegments);
|
||||
sappend('c',SET_F("CCT"),correctWB);
|
||||
sappend('c',SET_F("CR"),cctFromRgb);
|
||||
sappend('v',SET_F("CB"),strip.cctBlending);
|
||||
sappend('v',SET_F("FR"),strip.getTargetFps());
|
||||
sappend('v',SET_F("AW"),Bus::getAutoWhiteMode());
|
||||
sappend('v',SET_F("CB"),strip.cctBlending);
|
||||
sappend('v',SET_F("FR"),strip.getTargetFps());
|
||||
sappend('v',SET_F("AW"),strip.autoWhiteMode);
|
||||
|
||||
for (uint8_t s=0; s < busses.getNumBusses(); s++) {
|
||||
Bus* bus = busses.getBus(s);
|
||||
@@ -409,7 +409,7 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sappend('v',co,bus->getColorOrder());
|
||||
sappend('v',ls,bus->getStart());
|
||||
sappend('c',cv,bus->reversed);
|
||||
sappend('c',sl,bus->skippedLeds());
|
||||
sappend('v',sl,bus->skippedLeds());
|
||||
sappend('c',rf,bus->isOffRefreshRequired());
|
||||
}
|
||||
sappend('v',SET_F("MA"),strip.ablMilliampsMax);
|
||||
@@ -462,6 +462,7 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sappend('v',SET_F("TT"),touchThreshold);
|
||||
sappend('v',SET_F("IR"),irPin);
|
||||
sappend('v',SET_F("IT"),irEnabled);
|
||||
sappend('c',SET_F("MSO"),!irApplyToAllSelected);
|
||||
}
|
||||
|
||||
if (subPage == 3)
|
||||
@@ -492,6 +493,7 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sappend('c',SET_F("NB"),nodeBroadcastEnabled);
|
||||
|
||||
sappend('c',SET_F("RD"),receiveDirect);
|
||||
sappend('c',SET_F("MO"),useMainSegmentOnly);
|
||||
sappend('v',SET_F("EP"),e131Port);
|
||||
sappend('c',SET_F("ES"),e131SkipOutOfSequence);
|
||||
sappend('c',SET_F("EM"),e131Multicast);
|
||||
@@ -574,16 +576,13 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sprintf_P(tm, PSTR("Sunrise: %02d:%02d Sunset: %02d:%02d"), hour(sunrise), minute(sunrise), hour(sunset), minute(sunset));
|
||||
sappends('m',SET_F("(\"times\")[1]"),tm);
|
||||
}
|
||||
sappend('i',SET_F("OL"),overlayCurrent);
|
||||
sappend('c',SET_F("OL"),overlayCurrent);
|
||||
sappend('v',SET_F("O1"),overlayMin);
|
||||
sappend('v',SET_F("O2"),overlayMax);
|
||||
sappend('v',SET_F("OM"),analogClock12pixel);
|
||||
sappend('c',SET_F("OS"),analogClockSecondsTrail);
|
||||
sappend('c',SET_F("O5"),analogClock5MinuteMarks);
|
||||
#ifndef WLED_DISABLE_CRONIXIE
|
||||
sappends('s',SET_F("CX"),cronixieDisplay);
|
||||
sappend('c',SET_F("CB"),cronixieBacklight);
|
||||
#endif
|
||||
|
||||
sappend('c',SET_F("CE"),countdownMode);
|
||||
sappend('v',SET_F("CY"),countdownYear);
|
||||
sappend('v',SET_F("CI"),countdownMonth);
|
||||
|
||||
Reference in New Issue
Block a user