Compare commits
116 Commits
v0.13.0-b6
...
v0.13.0-b7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d4a20e05e | ||
|
|
c03d4f115f | ||
|
|
ed90b638a9 | ||
|
|
94a0199955 | ||
|
|
44739c5198 | ||
|
|
5f871bc01f | ||
|
|
1f5971f15a | ||
|
|
694466a196 | ||
|
|
03311d3776 | ||
|
|
ae0eba866a | ||
|
|
906737bedb | ||
|
|
7138e891be | ||
|
|
53abe36b83 | ||
|
|
efbb7a034c | ||
|
|
05bc81bf4e | ||
|
|
f8bc0bd2b5 | ||
|
|
cf94cb1092 | ||
|
|
02d92e32c7 | ||
|
|
7f92607b85 | ||
|
|
3be4b69b44 | ||
|
|
bb9afcb304 | ||
|
|
e9a05890a5 | ||
|
|
613809c2af | ||
|
|
7b969bb8c2 | ||
|
|
7aef551292 | ||
|
|
447b811fa0 | ||
|
|
435040814d | ||
|
|
9987416a4a | ||
|
|
31e33e0a8b | ||
|
|
b211d8b085 | ||
|
|
83416ee2e0 | ||
|
|
fa981a389f | ||
|
|
55817f31f9 | ||
|
|
6d2ef4e0bf | ||
|
|
4d714cf9a4 | ||
|
|
930ded6767 | ||
|
|
4cdb18907f | ||
|
|
38bc618ee5 | ||
|
|
00b0193a43 | ||
|
|
f9bce54104 | ||
|
|
7ee14724fc | ||
|
|
4eb0dbb5a4 | ||
|
|
8c5b3fe23e | ||
|
|
97f8eea302 | ||
|
|
04d5932252 | ||
|
|
b33c5798ee | ||
|
|
6180c2f948 | ||
|
|
795c515999 | ||
|
|
00dbdc2267 | ||
|
|
32286888e5 | ||
|
|
565d8d8f04 | ||
|
|
0a5a0bef48 | ||
|
|
6e0e5c102e | ||
|
|
be8a9ae73b | ||
|
|
22fbb0e35b | ||
|
|
e17203ca1b | ||
|
|
3170fa2208 | ||
|
|
07216db864 | ||
|
|
fec870f264 | ||
|
|
2c5eba335f | ||
|
|
fb19f1ecbc | ||
|
|
e879fe5843 | ||
|
|
0ca7699fe5 | ||
|
|
7f6adfa331 | ||
|
|
5f0b102671 | ||
|
|
d28eb6ae21 | ||
|
|
eca980dfca | ||
|
|
742c792ec7 | ||
|
|
9b062f33c5 | ||
|
|
ea15c2245e | ||
|
|
26ae6d3691 | ||
|
|
f97bc9dba8 | ||
|
|
fe6b1c13c4 | ||
|
|
5608425a12 | ||
|
|
f784b01d20 | ||
|
|
2648eba5bf | ||
|
|
255347ab77 | ||
|
|
52c36ef6a4 | ||
|
|
e54819e7e5 | ||
|
|
7eb029dcb6 | ||
|
|
f8c80283e4 | ||
|
|
04f5bdb843 | ||
|
|
aba4dc7c50 | ||
|
|
7fb46cf982 | ||
|
|
ae8281f835 | ||
|
|
fa35293618 | ||
|
|
20ccca0aec | ||
|
|
10e216da6b | ||
|
|
6491353a57 | ||
|
|
9f44f989e5 | ||
|
|
33f72e40da | ||
|
|
18868a5bd6 | ||
|
|
754682577c | ||
|
|
aef0243b73 | ||
|
|
736053e24e | ||
|
|
2c14181051 | ||
|
|
296fe4b62e | ||
|
|
118f02fd11 | ||
|
|
990d0f6e3e | ||
|
|
84624666ce | ||
|
|
8bd716c056 | ||
|
|
cd95abb2a1 | ||
|
|
1270f2d577 | ||
|
|
c27117e99e | ||
|
|
28556790d6 | ||
|
|
41c9bb63a0 | ||
|
|
7d5e2466f0 | ||
|
|
d3f35955d6 | ||
|
|
fb338c0728 | ||
|
|
2ce8f1ee5d | ||
|
|
3f0258e215 | ||
|
|
e72a8d999f | ||
|
|
fed16fd14e | ||
|
|
5dbc45ecb9 | ||
|
|
094bdb29b6 | ||
|
|
eab132ed32 |
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-b4 (build 2110110)"
|
||||
placeholder: "e.g. WLED 0.13.0-b7 (build 2202222)"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
|
||||
17
.vscode/extensions.json
vendored
17
.vscode/extensions.json
vendored
@@ -1,7 +1,10 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
]
|
||||
}
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"ms-vscode.cpptools-extension-pack"
|
||||
]
|
||||
}
|
||||
|
||||
59
CHANGELOG.md
59
CHANGELOG.md
@@ -2,28 +2,65 @@
|
||||
|
||||
### Builds after release 0.12.0
|
||||
|
||||
#### Build 2202222
|
||||
|
||||
- Version bump to 0.13.0-b7 "Toki"
|
||||
- Fixed HTTP API commands not applying to all selected segments in some conditions
|
||||
- Blynk support is not compiled in by default on ESP32 builds
|
||||
|
||||
#### Build 2202210
|
||||
|
||||
- Fixed HTTP API commands not applying to all selected segments if called from JSON
|
||||
- Improved Stream effects, no longer rely on LED state and won't fade out at low brightness
|
||||
|
||||
#### Build 2202200
|
||||
|
||||
- Added `info.leds.seglc` per-segment light capability info (PR #2552)
|
||||
- Fixed `info.leds.rgbw` behavior
|
||||
- Segment bounds sync (PR #2547)
|
||||
- WebSockets auto reconnection and error handling
|
||||
- Disable relay pin by default (PR #2531)
|
||||
- Various fixes (ESP32 touch pin 33, floats, PR #2530, #2534, #2538)
|
||||
- Deprecated `info.leds.cct`, `info.leds.wv` and `info.leds.rgbw`
|
||||
- Deprecated `/url` endpoint
|
||||
|
||||
#### Build 2202030
|
||||
|
||||
- Switched to binary format for WebSockets peek (PR #2516)
|
||||
- Playlist bugfix
|
||||
- Added `extractModeName()` utility function
|
||||
- Added serial out (PR #2517)
|
||||
- Added configurable baud rate
|
||||
|
||||
#### Build 2201260
|
||||
|
||||
- Initial ESP32-C3 and ESP32-S2 support (PRs #2452, #2454, #2502)
|
||||
- Full segment sync (PR #2427)
|
||||
- Allow overriding of color order by ranges (PR #2463)
|
||||
- Added white channel to Peek
|
||||
|
||||
#### Build 2112080
|
||||
|
||||
- Version bump to 0.13.0-b6 "Toki"
|
||||
- Added "ESP02" (ESP8266 with 2M of flash) to PIO/release binaries
|
||||
- Version bump to 0.13.0-b6 "Toki"
|
||||
- Added "ESP02" (ESP8266 with 2M of flash) to PIO/release binaries
|
||||
|
||||
#### Build 2112070
|
||||
|
||||
- Added new effect "Fairy", replacing "Police All"
|
||||
- Added new effect "Fairytwinkle", replacing "Two Areas"
|
||||
- Static single JSON buffer (performance and stability improvement) (PR #2336)
|
||||
- Added new effect "Fairy", replacing "Police All"
|
||||
- Added new effect "Fairytwinkle", replacing "Two Areas"
|
||||
- Static single JSON buffer (performance and stability improvement) (PR #2336)
|
||||
|
||||
#### Build 2112030
|
||||
|
||||
- Fixed ESP32 crash on Colortwinkles brightness change
|
||||
- Fixed setting picker to black resetting hue and saturation
|
||||
- Fixed auto white mode not saved to config
|
||||
- Fixed ESP32 crash on Colortwinkles brightness change
|
||||
- Fixed setting picker to black resetting hue and saturation
|
||||
- Fixed auto white mode not saved to config
|
||||
|
||||
#### Build 2111300
|
||||
|
||||
- Added CCT and white balance correction support (PR #2285)
|
||||
- Unified UI slider style
|
||||
- Added LED settings config template upload
|
||||
- Added CCT and white balance correction support (PR #2285)
|
||||
- Unified UI slider style
|
||||
- Added LED settings config template upload
|
||||
|
||||
#### Build 2111220
|
||||
|
||||
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wled",
|
||||
"version": "0.13.0-b6",
|
||||
"version": "0.13.0-b7",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -28,9 +28,9 @@
|
||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
|
||||
},
|
||||
"ajv": {
|
||||
"version": "6.12.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
|
||||
"integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==",
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wled",
|
||||
"version": "0.13.0-b6",
|
||||
"version": "0.13.0-b7",
|
||||
"description": "Tools for WLED project",
|
||||
"main": "tools/cdata.js",
|
||||
"directories": {
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
; default_envs = travis_esp8266, travis_esp32
|
||||
|
||||
# Release binaries
|
||||
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth
|
||||
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, esp32_eth_ota1mapp, esp32s2_saola, esp32c3
|
||||
|
||||
# Build everything
|
||||
; default_envs = esp32dev, esp8285_4CH_MagicHome, esp8285_4CH_H801, 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_5CH_H801, d1_mini_debug, wemos_shield_esp32, elekstube_ips
|
||||
; 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
|
||||
|
||||
# Single binaries (uncomment your board)
|
||||
; default_envs = elekstube_ips
|
||||
@@ -30,12 +30,13 @@ default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth
|
||||
; default_envs = d1_mini_ota
|
||||
; default_envs = esp32dev
|
||||
; default_envs = esp8285_4CH_MagicHome
|
||||
; default_envs = esp8285_4CH_H801
|
||||
; default_envs = esp8285_5CH_H801
|
||||
; default_envs = esp8285_H801
|
||||
; default_envs = d1_mini_5CH_Shojo_PCB
|
||||
; default_envs = wemos_shield_esp32
|
||||
; default_envs = m5atom
|
||||
; default_envs = esp32_eth
|
||||
; default_envs = esp32_eth_ota1mapp
|
||||
; default_envs = esp32s2_saola
|
||||
|
||||
src_dir = ./wled00
|
||||
data_dir = ./wled00/data
|
||||
@@ -54,6 +55,7 @@ extra_configs =
|
||||
arduino_core_2_6_3 = espressif8266@2.3.3
|
||||
arduino_core_2_7_4 = espressif8266@2.6.2
|
||||
arduino_core_3_0_0 = espressif8266@3.0.0
|
||||
arduino_core_3_0_2 = espressif8266@3.2.0
|
||||
|
||||
# Development platforms
|
||||
arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop
|
||||
@@ -77,10 +79,8 @@ debug_flags = -D DEBUG=1 -D WLED_DEBUG -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# FLAGS: ldscript (available ldscripts at https://github.com/esp8266/Arduino/tree/master/tools/sdk/ld)
|
||||
# ldscript_1m0m (1024 KB) = 999 KB sketch, 4 KB eeprom, no spiffs, 16 KB reserved
|
||||
# ldscript_2m1m (2048 KB) = 1019 KB sketch, 4 KB eeprom, 1004 KB spiffs, 16 KB reserved
|
||||
# ldscript_4m1m (4096 KB) = 1019 KB sketch, 4 KB eeprom, 1002 KB spiffs, 16 KB reserved, 2048 KB empty/ota?
|
||||
# ldscript_4m3m (4096 KB) = 1019 KB sketch, 4 KB eeprom, 3040 KB spiffs, 16 KB reserved
|
||||
#
|
||||
# Available lwIP variants (macros):
|
||||
# -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH = v1.4 Higher Bandwidth (default)
|
||||
@@ -160,10 +160,9 @@ upload_speed = 115200
|
||||
# ------------------------------------------------------------------------------
|
||||
lib_compat_mode = strict
|
||||
lib_deps =
|
||||
fastled/FastLED @ 3.4.0
|
||||
IRremoteESP8266 @ 2.7.18
|
||||
https://github.com/lorol/LITTLEFS.git
|
||||
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.2
|
||||
fastled/FastLED @ 3.5.0
|
||||
IRremoteESP8266 @ 2.8.1
|
||||
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
|
||||
#For use SSD1306 OLED display uncomment following
|
||||
@@ -177,14 +176,15 @@ lib_deps =
|
||||
; adafruit/Adafruit CCS811 Library @ 1.0.4
|
||||
; adafruit/Adafruit Si7021 Library @ 1.4.0
|
||||
|
||||
extra_scripts = ${scripts_defaults.extra_scripts}
|
||||
extra_scripts = ${scripts_defaults.extra_scripts}
|
||||
|
||||
[esp8266]
|
||||
build_flags =
|
||||
-DESP8266
|
||||
-DFP_IN_IROM
|
||||
;-Wno-deprecated-declarations
|
||||
;-Wno-register
|
||||
-Wno-register
|
||||
-Wno-misleading-indentation
|
||||
; NONOSDK22x_190703 = 2.2.2-dev(38a443e)
|
||||
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
|
||||
; lwIP 2 - Higher Bandwidth no Features
|
||||
@@ -198,27 +198,33 @@ build_flags =
|
||||
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
#https://github.com/lorol/LITTLEFS.git
|
||||
# ESPAsyncTCP @ 1.2.0
|
||||
ESPAsyncUDP
|
||||
makuna/NeoPixelBus @ 2.6.7 # 2.6.5/2.6.6 and newer do not compile on ESP core < 3.0.0
|
||||
|
||||
[esp32]
|
||||
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2/platform-tasmota-espressif32-2.0.2.zip
|
||||
platform = espressif32@3.5.0
|
||||
|
||||
build_flags = -g
|
||||
-DARDUINO_ARCH_ESP32
|
||||
-DCONFIG_LITTLEFS_FOR_IDF_3_2
|
||||
#-DCONFIG_LITTLEFS_FOR_IDF_3_2
|
||||
-D CONFIG_ASYNC_TCP_USE_WDT=0
|
||||
#use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x
|
||||
-D LOROL_LITTLEFS
|
||||
|
||||
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
|
||||
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
|
||||
|
||||
[esp32s2]
|
||||
build_flags = -g
|
||||
-DARDUINO_ARCH_ESP32
|
||||
-DCONFIG_LITTLEFS_FOR_IDF_3_2
|
||||
-DARDUINO_ARCH_ESP32S2
|
||||
-DCONFIG_IDF_TARGET_ESP32S2
|
||||
-D CONFIG_ASYNC_TCP_USE_WDT=0
|
||||
@@ -229,6 +235,19 @@ lib_deps =
|
||||
makuna/NeoPixelBus @ 2.6.7
|
||||
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
|
||||
|
||||
[esp32c3]
|
||||
build_flags = -g
|
||||
-DARDUINO_ARCH_ESP32
|
||||
-DARDUINO_ARCH_ESP32C3
|
||||
-DCONFIG_IDF_TARGET_ESP32C3
|
||||
-D CONFIG_ASYNC_TCP_USE_WDT=0
|
||||
-DCO
|
||||
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
makuna/NeoPixelBus @ 2.6.7
|
||||
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# WLED BUILDS
|
||||
# ------------------------------------------------------------------------------
|
||||
@@ -245,7 +264,9 @@ lib_deps = ${esp8266.lib_deps}
|
||||
[env:esp8266_2m]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
@@ -298,33 +319,47 @@ lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp32dev]
|
||||
board = esp32dev
|
||||
platform = espressif32@2.0
|
||||
platform = ${esp32.platform}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 #-D WLED_DISABLE_BROWNOUT_DET
|
||||
build_flags = ${common.build_flags_esp32} -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:esp32_eth]
|
||||
board = esp32-poe
|
||||
platform = espressif32@2.0
|
||||
platform = ${esp32.platform}
|
||||
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
|
||||
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 = esp32dev
|
||||
board_build.mcu = esp32s2
|
||||
platform = espressif32
|
||||
board = esp32-s2-saola-1
|
||||
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.2/platform-tasmota-espressif32-2.0.2.zip
|
||||
platform_packages =
|
||||
framework = arduino
|
||||
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||
board_build.flash_mode = qio
|
||||
upload_speed = 460800
|
||||
build_unflags = ${common.build_unflags}
|
||||
lib_deps = ${esp32s2.lib_deps}
|
||||
|
||||
[env:esp32c3]
|
||||
board = esp32-c3-devkitm-1
|
||||
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.2/platform-tasmota-espressif32-2.0.2.zip
|
||||
platform_packages =
|
||||
toolchain-xtensa32s2
|
||||
framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.0-alpha1
|
||||
framework = arduino
|
||||
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||
upload_speed = 460800
|
||||
build_unflags = ${common.build_unflags}
|
||||
lib_deps = ${esp32s2.lib_deps}
|
||||
lib_deps = ${esp32c3.lib_deps}
|
||||
|
||||
[env:esp8285_4CH_MagicHome]
|
||||
board = esp8285
|
||||
@@ -335,16 +370,7 @@ build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp8285_4CH_H801]
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp8285_5CH_H801]
|
||||
[env:esp8285_H801]
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
@@ -449,6 +475,13 @@ board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_MAX_CCT_BLEND=0 -D BTNPIN=-1 -D IRPIN=-1 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:athom15w]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
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}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# travis test board configurations
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
27
readme.md
27
readme.md
@@ -53,39 +53,18 @@ See the [documentation on our official site](https://kno.wled.ge)!
|
||||
|
||||
[On this page](https://kno.wled.ge/basics/tutorials/) you can find excellent tutorials made by the community and helpful tools to help you get your new lamp up and running!
|
||||
|
||||
## 🖼️ Images
|
||||
## 🖼️ User interface
|
||||
<img src="/images/macbook-pro-space-gray-on-the-wooden-table.jpg" width="50%"><img src="/images/walking-with-iphone-x.jpg" width="50%">
|
||||
|
||||
## 💾 Compatible LED Strips
|
||||
Type | Voltage | Comments
|
||||
|---|---|---|
|
||||
WS2812B | 5v |
|
||||
WS2813 | 5v |
|
||||
SK6812 | 5v | RGBW
|
||||
APA102 | 5v | C/D
|
||||
WS2801 | 5v | C/D
|
||||
LPD8806 | 5v | C/D
|
||||
TM1814 | 12v | RGBW
|
||||
WS2811 | 12v | 3-LED segments
|
||||
WS2815 | 12v |
|
||||
GS8208 | 12v |
|
||||
Analog/non-addressable | any | Requires additional circuitry
|
||||
|
||||
## 🧊 Compatible PC RGB Fans and ARGB accessories
|
||||
Brand | Model | Comments
|
||||
|---|---|---|
|
||||
Corsair | HD120 Fan | Uses WS2812B, data-in only
|
||||
PCCOOLER | Moonlight 5-pack Fans | Uses WS2812B, includes Data-out connector to keep each fan uniquely addressable if wired in series like traditional LED strips
|
||||
Any | 5v 3-pin ARGB for PC | Any PC RGB device that supports the 5v 3-pin ARGB motherboard header should work fine with WLED. All the major motherboard vendors support the Corsair HD120 and PCCOOLER fans listed, so we can safely assume any device that supports motherboard ARGB 5V 3-Pin standard will work with WLED.
|
||||
## 💾 Compatible hardware
|
||||
|
||||
See [here](https://kno.wled.ge/basics/compatible-hardware)!
|
||||
|
||||
## ✌️ Other
|
||||
|
||||
Licensed under the MIT license
|
||||
Credits [here](https://kno.wled.ge/about/contributors/)!
|
||||
|
||||
Uses Linearicons by Perxis!
|
||||
|
||||
Join the Discord server to discuss everything about WLED!
|
||||
|
||||
<a href="https://discord.gg/KuqP7NE"><img src="https://discordapp.com/api/guilds/473448917040758787/widget.png?style=banner2" width="25%"></a>
|
||||
|
||||
@@ -217,7 +217,7 @@ writeChunks(
|
||||
mangle: (str) =>
|
||||
str
|
||||
.replace("%", "%%")
|
||||
.replace(/User Interface\<\/button\>\<\/form\>/gms, "User Interface\<\/button\>\<\/form\>%DMXMENU%"),
|
||||
.replace(/Usermods\<\/button\>\<\/form\>/gms, "Usermods\<\/button\>\<\/form\>%DMXMENU%"),
|
||||
},
|
||||
{
|
||||
file: "settings_wifi.htm",
|
||||
|
||||
@@ -111,17 +111,17 @@ class Animated_Staircase : public Usermod {
|
||||
}
|
||||
|
||||
if (i >= onIndex && i < offIndex) {
|
||||
segments->setOption(SEG_OPTION_ON, 1, 1);
|
||||
segments->setOption(SEG_OPTION_ON, 1, i);
|
||||
|
||||
// We may need to copy mode and colors from segment 0 to make sure
|
||||
// changes are propagated even when the config is changed during a wipe
|
||||
// segments->mode = mainsegment.mode;
|
||||
// segments->colors[0] = mainsegment.colors[0];
|
||||
} else {
|
||||
segments->setOption(SEG_OPTION_ON, 0, 1);
|
||||
segments->setOption(SEG_OPTION_ON, 0, i);
|
||||
}
|
||||
// Always mark segments as "transitional", we are animating the staircase
|
||||
segments->setOption(SEG_OPTION_TRANSITIONAL, 1, 1);
|
||||
segments->setOption(SEG_OPTION_TRANSITIONAL, 1, i);
|
||||
}
|
||||
colorUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
}
|
||||
@@ -296,7 +296,7 @@ class Animated_Staircase : public Usermod {
|
||||
maxSegmentId = i - 1;
|
||||
break;
|
||||
}
|
||||
segments->setOption(SEG_OPTION_ON, 1, 1);
|
||||
segments->setOption(SEG_OPTION_ON, 1, i);
|
||||
}
|
||||
colorUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
DEBUG_PRINTLN(F("Animated Staircase disabled."));
|
||||
@@ -508,10 +508,10 @@ class Animated_Staircase : public Usermod {
|
||||
JsonArray usermodEnabled = staircase.createNestedArray(F("Staircase")); // name
|
||||
String btn = F("<button class=\"btn infobtn\" onclick=\"requestJson({staircase:{enabled:");
|
||||
if (enabled) {
|
||||
btn += F("false}},false,false);loadInfo();\">");
|
||||
btn += F("false}});\">");
|
||||
btn += F("enabled");
|
||||
} else {
|
||||
btn += F("true}},false,false);loadInfo();\">");
|
||||
btn += F("true}});\">");
|
||||
btn += F("disabled");
|
||||
}
|
||||
btn += F("</button>");
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
# ESP32 Touch Brightness Control
|
||||
|
||||
Toggle On/Off with a long press (800ms)
|
||||
Switch through 5 brightness levels (defined in usermod_touchbrightness.h, values 0-255) with a short (100ms) touch
|
||||
|
||||
## Installation
|
||||
|
||||
Copy 'usermod_touchbrightness.h' to the wled00 directory.
|
||||
in 'usermod_list.cpp' add this:
|
||||
|
||||
> #include "usermod_touchbrightness.h"
|
||||
above "void registerUsermods()"
|
||||
|
||||
and
|
||||
|
||||
> usermods.add(new TouchBrightnessControl());
|
||||
inside the "registerUsermods()" function
|
||||
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
//
|
||||
// usermod_touchbrightness.h
|
||||
// github.com/aircoookie/WLED
|
||||
//
|
||||
// Created by Justin Kühner on 14.09.2020.
|
||||
// Copyright © 2020 NeariX. All rights reserved.
|
||||
// https://github.com/NeariX67/
|
||||
// Discord: @NeariX#4799
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
#define threshold 40 //Increase value if touches falsely accur. Decrease value if actual touches are not recognized
|
||||
#define touchPin T0 //T0 = D4 / GPIO4
|
||||
|
||||
//Define the 5 brightness levels
|
||||
//Long press to turn off / on
|
||||
#define brightness1 51
|
||||
#define brightness2 102
|
||||
#define brightness3 153
|
||||
#define brightness4 204
|
||||
#define brightness5 255
|
||||
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
|
||||
class TouchBrightnessControl : public Usermod {
|
||||
private:
|
||||
unsigned long lastTime = 0; //Interval
|
||||
unsigned long lastTouch = 0; //Timestamp of last Touch
|
||||
unsigned long lastRelease = 0; //Timestamp of last Touch release
|
||||
boolean released = true; //current Touch state (touched/released)
|
||||
uint16_t touchReading = 0; //sensor reading, maybe use uint8_t???
|
||||
uint16_t touchDuration = 0; //duration of last touch
|
||||
public:
|
||||
|
||||
void setup() {
|
||||
lastTouch = millis();
|
||||
lastRelease = millis();
|
||||
lastTime = millis();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (millis() - lastTime >= 50) { //Check every 50ms if a touch occurs
|
||||
lastTime = millis();
|
||||
touchReading = touchRead(touchPin); //Read touch sensor on pin T0 (GPIO4 / D4)
|
||||
|
||||
if(touchReading < threshold && released) { //Touch started
|
||||
released = false;
|
||||
lastTouch = millis();
|
||||
}
|
||||
else if(touchReading >= threshold && !released) { //Touch released
|
||||
released = true;
|
||||
lastRelease = millis();
|
||||
touchDuration = lastRelease - lastTouch; //Calculate duration
|
||||
}
|
||||
|
||||
//Serial.println(touchDuration);
|
||||
|
||||
if(touchDuration >= 800 && released) { //Toggle power if button press is longer than 800ms
|
||||
touchDuration = 0; //Reset touch duration to avoid multiple actions on same touch
|
||||
toggleOnOff();
|
||||
colorUpdated(2); //Refresh values
|
||||
}
|
||||
else if(touchDuration >= 100 && released) { //Switch to next brightness if touch is between 100 and 800ms
|
||||
touchDuration = 0; //Reset touch duration to avoid multiple actions on same touch
|
||||
if(bri < brightness1) {
|
||||
bri = brightness1;
|
||||
} else if(bri >= brightness1 && bri < brightness2) {
|
||||
bri = brightness2;
|
||||
} else if(bri >= brightness2 && bri < brightness3) {
|
||||
bri = brightness3;
|
||||
} else if(bri >= brightness3 && bri < brightness4) {
|
||||
bri = brightness4;
|
||||
} else if(bri >= brightness4 && bri < brightness5) {
|
||||
bri = brightness5;
|
||||
} else if(bri >= brightness5) {
|
||||
bri = brightness1;
|
||||
}
|
||||
colorUpdated(2); //Refresh values
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
@@ -100,9 +100,9 @@ void userLoop() {
|
||||
needRedraw = true;
|
||||
} else if (knownBrightness != bri) {
|
||||
needRedraw = true;
|
||||
} else if (knownMode != strip.getMode()) {
|
||||
} else if (knownMode != strip.getMainSegment().mode) {
|
||||
needRedraw = true;
|
||||
} else if (knownPalette != strip.getSegment(0).palette) {
|
||||
} else if (knownPalette != strip.getMainSegment().palette) {
|
||||
needRedraw = true;
|
||||
}
|
||||
|
||||
@@ -126,8 +126,8 @@ void userLoop() {
|
||||
#endif
|
||||
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
|
||||
knownBrightness = bri;
|
||||
knownMode = strip.getMode();
|
||||
knownPalette = strip.getSegment(0).palette;
|
||||
knownMode = strip.getMainSegment().mode;
|
||||
knownPalette = strip.getMainSegment().palette;
|
||||
u8x8.clear();
|
||||
u8x8.setFont(u8x8_font_chroma48medium8_r);
|
||||
|
||||
|
||||
@@ -143,9 +143,9 @@ void userLoop() {
|
||||
needRedraw = true;
|
||||
} else if (knownBrightness != bri) {
|
||||
needRedraw = true;
|
||||
} else if (knownMode != strip.getMode()) {
|
||||
} else if (knownMode != strip.getMainSegment().mode) {
|
||||
needRedraw = true;
|
||||
} else if (knownPalette != strip.getSegment(0).palette) {
|
||||
} else if (knownPalette != strip.getMainSegment().palette) {
|
||||
needRedraw = true;
|
||||
}
|
||||
|
||||
@@ -169,8 +169,8 @@ void userLoop() {
|
||||
#endif
|
||||
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
|
||||
knownBrightness = bri;
|
||||
knownMode = strip.getMode();
|
||||
knownPalette = strip.getSegment(0).palette;
|
||||
knownMode = strip.getMainSegment().mode;
|
||||
knownPalette = strip.getMainSegment().palette;
|
||||
u8x8.clear();
|
||||
u8x8.setFont(u8x8_font_chroma48medium8_r);
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
# PIR sensor with MQTT
|
||||
|
||||
This simple usermod allows attaching a PIR sensor like the AM312 and publish the readings over MQTT. A message is sent when motion is detected as well as when motion has stopped.
|
||||
|
||||
This usermod has only been tested with the AM312 sensor though should work for any other PIR sensor. Note that this does not control the LED strip directly, it only publishes MQTT readings for use with other integrations like Home Assistant.
|
||||
|
||||
## Installation
|
||||
|
||||
Copy and replace the file `usermod.cpp` in wled00 directory.
|
||||
@@ -1,55 +0,0 @@
|
||||
#include "wled.h"
|
||||
/*
|
||||
* This v1 usermod file allows you to add own functionality to WLED more easily
|
||||
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
|
||||
* EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in const.h)
|
||||
* If you just need 8 bytes, use 2551-2559 (you do not need to increase EEPSIZE)
|
||||
*
|
||||
* Consider the v2 usermod API if you need a more advanced feature set!
|
||||
*/
|
||||
|
||||
//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t)
|
||||
|
||||
// PIR sensor pin
|
||||
const int MOTION_PIN = 16;
|
||||
// MQTT topic for sensor values
|
||||
const char MQTT_TOPIC[] = "/motion";
|
||||
|
||||
int prevState = LOW;
|
||||
|
||||
//gets called once at boot. Do all initialization that doesn't depend on network here
|
||||
void userSetup()
|
||||
{
|
||||
pinMode(MOTION_PIN, INPUT);
|
||||
}
|
||||
|
||||
//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
|
||||
void userConnected()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void publishMqtt(String state)
|
||||
{
|
||||
//Check if MQTT Connected, otherwise it will crash the 8266
|
||||
if (mqtt != nullptr){
|
||||
char subuf[38];
|
||||
strcpy(subuf, mqttDeviceTopic);
|
||||
strcat(subuf, MQTT_TOPIC);
|
||||
mqtt->publish(subuf, 0, true, state.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
//loop. You can use "if (WLED_CONNECTED)" to check for successful connection
|
||||
void userLoop()
|
||||
{
|
||||
if (digitalRead(MOTION_PIN) == HIGH && prevState == LOW) { // Motion detected
|
||||
publishMqtt("ON");
|
||||
prevState = HIGH;
|
||||
}
|
||||
if (digitalRead(MOTION_PIN) == LOW && prevState == HIGH) { // Motion stopped
|
||||
publishMqtt("OFF");
|
||||
prevState = LOW;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ class PIRsensorSwitch : public Usermod {
|
||||
private:
|
||||
// PIR sensor pin
|
||||
const uint8_t PIRsensorPin = 13; // D7 on D1 mini
|
||||
// notification mode for colorUpdated()
|
||||
// notification mode for stateUpdated()
|
||||
const byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // CALL_MODE_DIRECT_CHANGE
|
||||
// 1 min delay before switch off after the sensor state goes LOW
|
||||
uint32_t m_switchOffDelay = 60000;
|
||||
@@ -127,7 +127,7 @@ class PIRsensorSwitch : public Usermod {
|
||||
if (bri != briHighlight) {
|
||||
bri = briHighlight; // set current highlight brightness to last set highlight brightness
|
||||
}
|
||||
colorUpdated(NotifyUpdateMode);
|
||||
stateUpdated(NotifyUpdateMode);
|
||||
highlightActive = true; // flag highlight is on
|
||||
}
|
||||
else { // **pir timer has elapsed**
|
||||
@@ -157,7 +157,7 @@ class PIRsensorSwitch : public Usermod {
|
||||
}
|
||||
applyMacro(macroLongPress); // apply standby lighting without brightness
|
||||
}
|
||||
colorUpdated(NotifyUpdateMode);
|
||||
stateUpdated(NotifyUpdateMode);
|
||||
highlightActive = false; // flag highlight is off
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,16 +9,17 @@ The LED strip is switched [using a relay](https://github.com/Aircoookie/WLED/wik
|
||||
|
||||
## Webinterface
|
||||
|
||||
The info page in the web interface shows the remaining time of the off timer.
|
||||
The info page in the web interface shows the remaining time of the off timer. Usermod can also be temporarily disbled/enabled from the info page by clicking PIR button.
|
||||
|
||||
## Sensor connection
|
||||
|
||||
My setup uses an HC-SR501 sensor, a HC-SR505 should also work.
|
||||
My setup uses an HC-SR501 or HC-SR602 sensor, a HC-SR505 should also work.
|
||||
|
||||
The usermod uses GPIO13 (D1 mini pin D7) by default for the sensor signal but can be changed in the Usermod settings page.
|
||||
[This example page](http://www.esp8266learning.com/wemos-mini-pir-sensor-example.php) describes how to connect the sensor.
|
||||
|
||||
Use the potentiometers on the sensor to set the time-delay to the minimum and the sensitivity to about half, or slightly above.
|
||||
You can also use usermod's off timer instead of sensor's. In such case rotate the potentiometer to its shortest time possible (or use SR602 which lacks such potentiometer).
|
||||
|
||||
## Usermod installation
|
||||
|
||||
@@ -59,6 +60,8 @@ void registerUsermods()
|
||||
}
|
||||
```
|
||||
|
||||
**NOTE:** Usermod has been included in master branch of WLED so it can be compiled in directly just by defining `-D USERMOD_PIRSWITCH` and optionaly `-D PIR_SENSOR_PIN=16` to override default pin.
|
||||
|
||||
## API to enable/disable the PIR sensor from outside. For example from another usermod.
|
||||
|
||||
To query or change the PIR sensor state the methods `bool PIRsensorEnabled()` and `void EnablePIRsensor(bool enable)` are available.
|
||||
@@ -95,8 +98,27 @@ class MyUsermod : public Usermod {
|
||||
};
|
||||
```
|
||||
|
||||
Have fun - @gegu
|
||||
### Configuration options
|
||||
|
||||
Usermod can be configured in Usermods settings page.
|
||||
|
||||
* `PIRenabled` - enable/disable usermod
|
||||
* `pin` - dynamically change GPIO pin where PIR sensor is attached to ESP
|
||||
* `PIRoffSec` - number of seconds after PIR sensor deactivates when usermod triggers Off preset (or turns WLED off)
|
||||
* `on-preset` - preset triggered when PIR activates (if this is 0 it will just turn WLED on)
|
||||
* `off-preset` - preset triggered when PIR deactivates (if this is 0 it will just turn WLED off)
|
||||
* `nighttime-only` - enable triggering only between sunset and sunrise (you will need to set up _NTP_, _Lat_ & _Lon_ in Time & Macro settings)
|
||||
* `mqtt-only` - only send MQTT messages, do not interact with WLED
|
||||
* `off-only` - only trigger presets or turn WLED on/off in WLED is not already on (displaying effect)
|
||||
* `notifications` - enable or disable sending notifications to other WLED instances using Sync button
|
||||
|
||||
|
||||
Have fun - @gegu & @blazoncek
|
||||
|
||||
## Change log
|
||||
2021-04
|
||||
* Adaptation for runtime configuration.
|
||||
* Adaptation for runtime configuration.
|
||||
|
||||
2021-11
|
||||
* Added information about dynamic configuration options
|
||||
* Added option to temporary enable/disble usermod from WLED UI (Info dialog)
|
||||
@@ -55,33 +55,29 @@ public:
|
||||
bool PIRsensorEnabled() { return enabled; }
|
||||
|
||||
private:
|
||||
// PIR sensor pin
|
||||
int8_t PIRsensorPin = PIR_SENSOR_PIN;
|
||||
// notification mode for colorUpdated()
|
||||
const byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // CALL_MODE_DIRECT_CHANGE
|
||||
// delay before switch off after the sensor state goes LOW
|
||||
uint32_t m_switchOffDelay = 600000; // 10min
|
||||
// off timer start time
|
||||
uint32_t m_offTimerStart = 0;
|
||||
// current PIR sensor pin state
|
||||
byte sensorPinState = LOW;
|
||||
// PIR sensor enabled
|
||||
bool enabled = true;
|
||||
// status of initialisation
|
||||
bool initDone = false;
|
||||
// on and off presets
|
||||
uint8_t m_onPreset = 0;
|
||||
uint8_t m_offPreset = 0;
|
||||
// flag to indicate that PIR sensor should activate WLED during nighttime only
|
||||
bool m_nightTimeOnly = false;
|
||||
// flag to send MQTT message only (assuming it is enabled)
|
||||
bool m_mqttOnly = false;
|
||||
// flag to enable triggering only if WLED is initially off (LEDs are not on, preventing running effect being overwritten by PIR)
|
||||
bool m_offOnly = false;
|
||||
bool PIRtriggered = false;
|
||||
|
||||
byte prevPreset = 0;
|
||||
byte prevPlaylist = 0;
|
||||
bool savedState = false;
|
||||
|
||||
uint32_t offTimerStart = 0; // off timer start time
|
||||
byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // notification mode for stateUpdated(): CALL_MODE_NO_NOTIFY or CALL_MODE_DIRECT_CHANGE
|
||||
byte sensorPinState = LOW; // current PIR sensor pin state
|
||||
bool initDone = false; // status of initialization
|
||||
bool PIRtriggered = false;
|
||||
unsigned long lastLoop = 0;
|
||||
|
||||
// configurable parameters
|
||||
bool enabled = true; // PIR sensor enabled
|
||||
int8_t PIRsensorPin = PIR_SENSOR_PIN; // PIR sensor pin
|
||||
uint32_t m_switchOffDelay = 600000; // delay before switch off after the sensor state goes LOW (10min)
|
||||
uint8_t m_onPreset = 0; // on preset
|
||||
uint8_t m_offPreset = 0; // off preset
|
||||
bool m_nightTimeOnly = false; // flag to indicate that PIR sensor should activate WLED during nighttime only
|
||||
bool m_mqttOnly = false; // flag to send MQTT message only (assuming it is enabled)
|
||||
// flag to enable triggering only if WLED is initially off (LEDs are not on, preventing running effect being overwritten by PIR)
|
||||
bool m_offOnly = false;
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _switchOffDelay[];
|
||||
@@ -91,30 +87,30 @@ private:
|
||||
static const char _nightTime[];
|
||||
static const char _mqttOnly[];
|
||||
static const char _offOnly[];
|
||||
static const char _notify[];
|
||||
|
||||
/**
|
||||
* check if it is daytime
|
||||
* if sunrise/sunset is not defined (no NTP or lat/lon) default to nighttime
|
||||
*/
|
||||
bool isDayTime() {
|
||||
bool isDayTime = false;
|
||||
updateLocalTime();
|
||||
uint8_t hr = hour(localTime);
|
||||
uint8_t mi = minute(localTime);
|
||||
|
||||
if (sunrise && sunset) {
|
||||
if (hour(sunrise)<hr && hour(sunset)>hr) {
|
||||
isDayTime = true;
|
||||
return true;
|
||||
} else {
|
||||
if (hour(sunrise)==hr && minute(sunrise)<mi) {
|
||||
isDayTime = true;
|
||||
return true;
|
||||
}
|
||||
if (hour(sunset)==hr && minute(sunset)>mi) {
|
||||
isDayTime = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isDayTime;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,17 +120,47 @@ private:
|
||||
{
|
||||
if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return;
|
||||
PIRtriggered = switchOn;
|
||||
if (switchOn && m_onPreset) {
|
||||
applyPreset(m_onPreset);
|
||||
} else if (!switchOn && m_offPreset) {
|
||||
applyPreset(m_offPreset);
|
||||
} else if (switchOn && bri == 0) {
|
||||
bri = briLast;
|
||||
colorUpdated(NotifyUpdateMode);
|
||||
} else if (!switchOn && bri != 0) {
|
||||
briLast = bri;
|
||||
bri = 0;
|
||||
colorUpdated(NotifyUpdateMode);
|
||||
if (switchOn) {
|
||||
if (m_onPreset) {
|
||||
if (currentPlaylist>0) prevPlaylist = currentPlaylist;
|
||||
else if (currentPreset>0) prevPreset = currentPreset;
|
||||
else {
|
||||
saveTemporaryPreset();
|
||||
savedState = true;
|
||||
prevPlaylist = 0;
|
||||
prevPreset = 0;
|
||||
}
|
||||
applyPreset(m_onPreset, NotifyUpdateMode);
|
||||
return;
|
||||
}
|
||||
// preset not assigned
|
||||
if (bri == 0) {
|
||||
bri = briLast;
|
||||
stateUpdated(NotifyUpdateMode);
|
||||
}
|
||||
} else {
|
||||
if (m_offPreset) {
|
||||
applyPreset(m_offPreset, NotifyUpdateMode);
|
||||
return;
|
||||
} else if (prevPlaylist) {
|
||||
applyPreset(prevPlaylist, NotifyUpdateMode);
|
||||
prevPlaylist = 0;
|
||||
return;
|
||||
} else if (prevPreset) {
|
||||
applyPreset(prevPreset, NotifyUpdateMode);
|
||||
prevPreset = 0;
|
||||
return;
|
||||
} else if (savedState) {
|
||||
applyTemporaryPreset();
|
||||
savedState = false;
|
||||
return;
|
||||
}
|
||||
// preset not assigned
|
||||
if (bri != 0) {
|
||||
briLast = bri;
|
||||
bri = 0;
|
||||
stateUpdated(NotifyUpdateMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,12 +186,12 @@ private:
|
||||
sensorPinState = pinState; // change previous state
|
||||
|
||||
if (sensorPinState == HIGH) {
|
||||
m_offTimerStart = 0;
|
||||
offTimerStart = 0;
|
||||
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true);
|
||||
publishMqtt("on");
|
||||
} else /*if (bri != 0)*/ {
|
||||
// start switch off timer
|
||||
m_offTimerStart = millis();
|
||||
offTimerStart = millis();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -177,14 +203,14 @@ private:
|
||||
*/
|
||||
bool handleOffTimer()
|
||||
{
|
||||
if (m_offTimerStart > 0 && millis() - m_offTimerStart > m_switchOffDelay)
|
||||
if (offTimerStart > 0 && millis() - offTimerStart > m_switchOffDelay)
|
||||
{
|
||||
if (enabled == true)
|
||||
{
|
||||
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(false);
|
||||
publishMqtt("off");
|
||||
}
|
||||
m_offTimerStart = 0;
|
||||
offTimerStart = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -248,15 +274,25 @@ public:
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
// off timer
|
||||
String uiDomString = F("PIR <i class=\"icons\"></i>");
|
||||
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
|
||||
if (m_offTimerStart > 0)
|
||||
String uiDomString = F("<button class=\"btn\" onclick=\"requestJson({");
|
||||
uiDomString += FPSTR(_name);
|
||||
uiDomString += F(":{");
|
||||
uiDomString += FPSTR(_enabled);
|
||||
if (enabled) {
|
||||
uiDomString += F(":false}});\">");
|
||||
uiDomString += F("PIR <i class=\"icons\"></i>");
|
||||
} else {
|
||||
uiDomString += F(":true}});\">");
|
||||
uiDomString += F("PIR <i class=\"icons\"></i>");
|
||||
}
|
||||
uiDomString += F("</button>");
|
||||
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
|
||||
|
||||
if (enabled) {
|
||||
if (offTimerStart > 0)
|
||||
{
|
||||
uiDomString = "";
|
||||
unsigned int offSeconds = (m_switchOffDelay - (millis() - m_offTimerStart)) / 1000;
|
||||
unsigned int offSeconds = (m_switchOffDelay - (millis() - offTimerStart)) / 1000;
|
||||
if (offSeconds >= 3600)
|
||||
{
|
||||
uiDomString += (offSeconds / 3600);
|
||||
@@ -282,8 +318,6 @@ public:
|
||||
infoArr.add(sensorPinState ? F("sensor on") : F("inactive"));
|
||||
}
|
||||
} else {
|
||||
String uiDomString = F("PIR sensor");
|
||||
JsonArray infoArr = user.createNestedArray(uiDomString);
|
||||
infoArr.add(F("disabled"));
|
||||
}
|
||||
}
|
||||
@@ -302,11 +336,18 @@ public:
|
||||
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
/*
|
||||
|
||||
void readFromJsonState(JsonObject &root)
|
||||
{
|
||||
if (!initDone) return; // prevent crash on boot applyPreset()
|
||||
JsonObject usermod = root[FPSTR(_name)];
|
||||
if (!usermod.isNull()) {
|
||||
if (usermod[FPSTR(_enabled)].is<bool>()) {
|
||||
enabled = usermod[FPSTR(_enabled)].as<bool>();
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* provide the changeable values
|
||||
@@ -314,14 +355,15 @@ public:
|
||||
void addToConfig(JsonObject &root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name));
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[FPSTR(_switchOffDelay)] = m_switchOffDelay / 1000;
|
||||
top["pin"] = PIRsensorPin;
|
||||
top[FPSTR(_onPreset)] = m_onPreset;
|
||||
top[FPSTR(_offPreset)] = m_offPreset;
|
||||
top[FPSTR(_nightTime)] = m_nightTimeOnly;
|
||||
top[FPSTR(_mqttOnly)] = m_mqttOnly;
|
||||
top[FPSTR(_offOnly)] = m_offOnly;
|
||||
top["pin"] = PIRsensorPin;
|
||||
top[FPSTR(_onPreset)] = m_onPreset;
|
||||
top[FPSTR(_offPreset)] = m_offPreset;
|
||||
top[FPSTR(_nightTime)] = m_nightTimeOnly;
|
||||
top[FPSTR(_mqttOnly)] = m_mqttOnly;
|
||||
top[FPSTR(_offOnly)] = m_offOnly;
|
||||
top[FPSTR(_notify)] = (NotifyUpdateMode != CALL_MODE_NO_NOTIFY);
|
||||
DEBUG_PRINTLN(F("PIR config saved."));
|
||||
}
|
||||
|
||||
@@ -336,9 +378,9 @@ public:
|
||||
bool oldEnabled = enabled;
|
||||
int8_t oldPin = PIRsensorPin;
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
@@ -351,7 +393,6 @@ public:
|
||||
|
||||
m_onPreset = top[FPSTR(_onPreset)] | m_onPreset;
|
||||
m_onPreset = max(0,min(250,(int)m_onPreset));
|
||||
|
||||
m_offPreset = top[FPSTR(_offPreset)] | m_offPreset;
|
||||
m_offPreset = max(0,min(250,(int)m_offPreset));
|
||||
|
||||
@@ -359,7 +400,8 @@ public:
|
||||
m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly;
|
||||
m_offOnly = top[FPSTR(_offOnly)] | m_offOnly;
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
NotifyUpdateMode = top[FPSTR(_notify)] ? CALL_MODE_DIRECT_CHANGE : CALL_MODE_NO_NOTIFY;
|
||||
|
||||
if (!initDone) {
|
||||
// reading config prior to setup()
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
@@ -385,7 +427,7 @@ public:
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[FPSTR(_offOnly)].isNull();
|
||||
return !top[FPSTR(_notify)].isNull();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -407,3 +449,4 @@ const char PIRsensorSwitch::_offPreset[] PROGMEM = "off-preset";
|
||||
const char PIRsensorSwitch::_nightTime[] PROGMEM = "nighttime-only";
|
||||
const char PIRsensorSwitch::_mqttOnly[] PROGMEM = "mqtt-only";
|
||||
const char PIRsensorSwitch::_offOnly[] PROGMEM = "off-only";
|
||||
const char PIRsensorSwitch::_notify[] PROGMEM = "notifications";
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
#include "src/dependencies/time/DS1307RTC.h"
|
||||
#include "wled.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#define HW_PIN_SCL 22
|
||||
#define HW_PIN_SDA 21
|
||||
#else
|
||||
#define HW_PIN_SCL 5
|
||||
#define HW_PIN_SDA 4
|
||||
#endif
|
||||
|
||||
//Connect DS1307 to standard I2C pins (ESP32: GPIO 21 (SDA)/GPIO 22 (SCL))
|
||||
|
||||
class RTCUsermod : public Usermod {
|
||||
@@ -12,6 +20,8 @@ class RTCUsermod : public Usermod {
|
||||
public:
|
||||
|
||||
void setup() {
|
||||
PinManagerPinType pins[2] = { { HW_PIN_SCL, true }, { HW_PIN_SDA, true } };
|
||||
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { disabled = true; return; }
|
||||
time_t rtcTime = RTC.get();
|
||||
if (rtcTime) {
|
||||
toki.setTime(rtcTime,TOKI_NO_MS_ACCURACY,TOKI_TS_RTC);
|
||||
@@ -22,12 +32,26 @@ class RTCUsermod : public Usermod {
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (strip.isUpdating()) return;
|
||||
if (!disabled && toki.isTick()) {
|
||||
time_t t = toki.second();
|
||||
if (t != RTC.get()) RTC.set(t); //set RTC to NTP/UI-provided value
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
* 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("RTC");
|
||||
JsonArray pins = top.createNestedArray("pin");
|
||||
pins.add(HW_PIN_SCL);
|
||||
pins.add(HW_PIN_SDA);
|
||||
}
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_RTC;
|
||||
|
||||
@@ -22,12 +22,12 @@
|
||||
|
||||
// 10 bits
|
||||
#ifndef USERMOD_SN_PHOTORESISTOR_ADC_PRECISION
|
||||
#define USERMOD_SN_PHOTORESISTOR_ADC_PRECISION 1024.0
|
||||
#define USERMOD_SN_PHOTORESISTOR_ADC_PRECISION 1024.0f
|
||||
#endif
|
||||
|
||||
// resistor size 10K hms
|
||||
#ifndef USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE
|
||||
#define USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE 10000.0
|
||||
#define USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE 10000.0f
|
||||
#endif
|
||||
|
||||
// only report if differance grater than offset value
|
||||
@@ -123,6 +123,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t getLastLDRValue()
|
||||
{
|
||||
return lastLDRValue;
|
||||
}
|
||||
|
||||
void addToJsonInfo(JsonObject &root)
|
||||
{
|
||||
JsonObject user = root[F("u")];
|
||||
|
||||
@@ -12,7 +12,7 @@ This usermod allow to use 240x240 display to display following:
|
||||
## Hardware
|
||||
|
||||
***
|
||||

|
||||

|
||||
|
||||
## Library used
|
||||
|
||||
|
||||
@@ -118,11 +118,11 @@ class St7789DisplayUsermod : public Usermod {
|
||||
{
|
||||
needRedraw = true;
|
||||
}
|
||||
else if (knownMode != strip.getMode())
|
||||
else if (knownMode != strip.getMainSegment().mode)
|
||||
{
|
||||
needRedraw = true;
|
||||
}
|
||||
else if (knownPalette != strip.getSegment(0).palette)
|
||||
else if (knownPalette != strip.getMainSegment().palette)
|
||||
{
|
||||
needRedraw = true;
|
||||
}
|
||||
@@ -148,8 +148,8 @@ class St7789DisplayUsermod : public Usermod {
|
||||
#endif
|
||||
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
|
||||
knownBrightness = bri;
|
||||
knownMode = strip.getMode();
|
||||
knownPalette = strip.getSegment(0).palette;
|
||||
knownMode = strip.getMainSegment().mode;
|
||||
knownPalette = strip.getMainSegment().palette;
|
||||
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
tft.setTextSize(2);
|
||||
|
||||
@@ -110,9 +110,9 @@ void userLoop() {
|
||||
needRedraw = true;
|
||||
} else if (knownBrightness != bri) {
|
||||
needRedraw = true;
|
||||
} else if (knownMode != strip.getMode()) {
|
||||
} else if (knownMode != strip.getMainSegment().mode) {
|
||||
needRedraw = true;
|
||||
} else if (knownPalette != strip.getSegment(0).palette) {
|
||||
} else if (knownPalette != strip.getMainSegment().palette) {
|
||||
needRedraw = true;
|
||||
}
|
||||
|
||||
@@ -136,8 +136,8 @@ void userLoop() {
|
||||
#endif
|
||||
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
|
||||
knownBrightness = bri;
|
||||
knownMode = strip.getMode();
|
||||
knownPalette = strip.getSegment(0).palette;
|
||||
knownMode = strip.getMainSegment().mode;
|
||||
knownPalette = strip.getMainSegment().palette;
|
||||
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
tft.setTextSize(2);
|
||||
|
||||
@@ -37,12 +37,12 @@ class UsermodTemperature : public Usermod {
|
||||
// used to determine when we can read the sensors temperature
|
||||
// we have to wait at least 93.75 ms after requestTemperatures() is called
|
||||
unsigned long lastTemperaturesRequest;
|
||||
float temperature = -100; // default to -100, DS18B20 only goes down to -50C
|
||||
float temperature;
|
||||
// indicates requestTemperatures has been called but the sensor measurement is not complete
|
||||
bool waitingForConversion = false;
|
||||
// flag set at startup if DS18B20 sensor not found, avoids trying to keep getting
|
||||
// temperature if flashed to a board without a sensor attached
|
||||
bool sensorFound = false;
|
||||
byte sensorFound;
|
||||
|
||||
bool enabled = true;
|
||||
|
||||
@@ -54,27 +54,47 @@ class UsermodTemperature : public Usermod {
|
||||
|
||||
//Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013
|
||||
float readDallas() {
|
||||
byte i;
|
||||
byte data[2];
|
||||
byte data[9];
|
||||
int16_t result; // raw data from sensor
|
||||
if (!oneWire->reset()) return -127.0f; // send reset command and fail fast
|
||||
oneWire->skip(); // skip ROM
|
||||
oneWire->write(0xBE); // read (temperature) from EEPROM
|
||||
for (i=0; i < 2; i++) data[i] = oneWire->read(); // first 2 bytes contain temperature
|
||||
for (i=2; i < 8; i++) oneWire->read(); // read unused bytes
|
||||
result = (data[1]<<4) | (data[0]>>4); // we only need whole part, we will add fraction when returning
|
||||
if (data[1]&0x80) result |= 0xFF00; // fix negative value
|
||||
oneWire->reset();
|
||||
oneWire->skip(); // skip ROM
|
||||
oneWire->write(0x44,parasite); // request new temperature reading (without parasite power)
|
||||
return (float)result + ((data[0]&0x0008) ? 0.5f : 0.0f);
|
||||
float retVal = -127.0f;
|
||||
if (oneWire->reset()) { // if reset() fails there are no OneWire devices
|
||||
oneWire->skip(); // skip ROM
|
||||
oneWire->write(0xBE); // read (temperature) from EEPROM
|
||||
oneWire->read_bytes(data, 9); // first 2 bytes contain temperature
|
||||
#ifdef WLED_DEBUG
|
||||
if (OneWire::crc8(data,8) != data[8]) {
|
||||
DEBUG_PRINTLN(F("CRC error reading temperature."));
|
||||
for (byte i=0; i < 9; i++) DEBUG_PRINTF("0x%02X ", data[i]);
|
||||
DEBUG_PRINT(F(" => "));
|
||||
DEBUG_PRINTF("0x%02X\n", OneWire::crc8(data,8));
|
||||
}
|
||||
#endif
|
||||
switch(sensorFound) {
|
||||
case 0x10: // DS18S20 has 9-bit precision
|
||||
result = (data[1] << 8) | data[0];
|
||||
retVal = float(result) * 0.5f;
|
||||
break;
|
||||
case 0x22: // DS18B20
|
||||
case 0x28: // DS1822
|
||||
case 0x3B: // DS1825
|
||||
case 0x42: // DS28EA00
|
||||
result = (data[1]<<4) | (data[0]>>4); // we only need whole part, we will add fraction when returning
|
||||
if (data[1] & 0x80) result |= 0xF000; // fix negative value
|
||||
retVal = float(result) + ((data[0] & 0x08) ? 0.5f : 0.0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (byte i=1; i<9; i++) data[0] &= data[i];
|
||||
return data[0]==0xFF ? -127.0f : retVal;
|
||||
}
|
||||
|
||||
void requestTemperatures() {
|
||||
readDallas();
|
||||
DEBUG_PRINTLN(F("Requesting temperature."));
|
||||
oneWire->reset();
|
||||
oneWire->skip(); // skip ROM
|
||||
oneWire->write(0x44,parasite); // request new temperature reading (TODO: parasite would need special handling)
|
||||
lastTemperaturesRequest = millis();
|
||||
waitingForConversion = true;
|
||||
DEBUG_PRINTLN(F("Requested temperature."));
|
||||
}
|
||||
|
||||
void readTemperature() {
|
||||
@@ -102,10 +122,13 @@ class UsermodTemperature : public Usermod {
|
||||
case 0x3B: // DS1825
|
||||
case 0x42: // DS28EA00
|
||||
DEBUG_PRINTLN(F("Sensor found."));
|
||||
sensorFound = deviceAddress[0];
|
||||
DEBUG_PRINTF("0x%02X\n", sensorFound);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
DEBUG_PRINTLN(F("Sensor NOT found."));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -113,16 +136,16 @@ class UsermodTemperature : public Usermod {
|
||||
|
||||
void setup() {
|
||||
int retries = 10;
|
||||
sensorFound = 0;
|
||||
temperature = -127.0f; // default to -127, DS18B20 only goes down to -50C
|
||||
if (enabled) {
|
||||
// config says we are enabled
|
||||
DEBUG_PRINTLN(F("Allocating temperature pin..."));
|
||||
// pin retrieved from cfg.json (readFromConfig()) prior to running setup()
|
||||
if (temperaturePin >= 0 && pinManager.allocatePin(temperaturePin, true, PinOwner::UM_Temperature)) {
|
||||
oneWire = new OneWire(temperaturePin);
|
||||
if (!oneWire->reset()) {
|
||||
sensorFound = false; // resetting 1-Wire bus yielded an error
|
||||
} else {
|
||||
while ((sensorFound=findSensor()) && retries--) {
|
||||
if (oneWire->reset()) {
|
||||
while (!findSensor() && retries--) {
|
||||
delay(25); // try to find sensor
|
||||
}
|
||||
}
|
||||
@@ -131,7 +154,6 @@ class UsermodTemperature : public Usermod {
|
||||
DEBUG_PRINTLN(F("Temperature pin allocation failed."));
|
||||
}
|
||||
temperaturePin = -1; // allocation failed
|
||||
sensorFound = false;
|
||||
}
|
||||
}
|
||||
lastMeasurement = millis() - readingInterval + 10000;
|
||||
@@ -139,8 +161,9 @@ class UsermodTemperature : public Usermod {
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (!enabled || strip.isUpdating()) return;
|
||||
if (!enabled || !sensorFound || strip.isUpdating()) return;
|
||||
|
||||
static uint8_t errorCount = 0;
|
||||
unsigned long now = millis();
|
||||
|
||||
// check to see if we are due for taking a measurement
|
||||
@@ -156,20 +179,26 @@ class UsermodTemperature : public Usermod {
|
||||
}
|
||||
|
||||
// we were waiting for a conversion to complete, have we waited log enough?
|
||||
if (now - lastTemperaturesRequest >= 100 /* 93.75ms per the datasheet but can be up to 750ms */) {
|
||||
if (now - lastTemperaturesRequest >= 750 /* 93.75ms per the datasheet but can be up to 750ms */) {
|
||||
readTemperature();
|
||||
if (getTemperatureC() < -100.0f) {
|
||||
if (++errorCount > 10) sensorFound = 0;
|
||||
lastMeasurement = now - readingInterval + 300; // force new measurement in 300ms
|
||||
return;
|
||||
}
|
||||
errorCount = 0;
|
||||
|
||||
if (WLED_MQTT_CONNECTED) {
|
||||
char subuf[64];
|
||||
strcpy(subuf, mqttDeviceTopic);
|
||||
if (-100 <= temperature) {
|
||||
if (temperature > -100.0f) {
|
||||
// dont publish super low temperature as the graph will get messed up
|
||||
// the DallasTemperature library returns -127C or -196.6F when problem
|
||||
// reading the sensor
|
||||
strcat_P(subuf, PSTR("/temperature"));
|
||||
mqtt->publish(subuf, 0, false, String(temperature).c_str());
|
||||
mqtt->publish(subuf, 0, false, String(getTemperatureC()).c_str());
|
||||
strcat_P(subuf, PSTR("_f"));
|
||||
mqtt->publish(subuf, 0, false, String((float)temperature * 1.8f + 32).c_str());
|
||||
mqtt->publish(subuf, 0, false, String(getTemperatureF()).c_str());
|
||||
} else {
|
||||
// publish something else to indicate status?
|
||||
}
|
||||
@@ -202,13 +231,13 @@ class UsermodTemperature : public Usermod {
|
||||
JsonArray temp = user.createNestedArray(FPSTR(_name));
|
||||
//temp.add(F("Loaded."));
|
||||
|
||||
if (temperature <= -100.0 || (!sensorFound && temperature == -1.0)) {
|
||||
if (temperature <= -100.0f) {
|
||||
temp.add(0);
|
||||
temp.add(F(" Sensor Error!"));
|
||||
return;
|
||||
}
|
||||
|
||||
temp.add(degC ? temperature : (float)temperature * 1.8f + 32);
|
||||
temp.add(degC ? getTemperatureC() : getTemperatureF());
|
||||
if (degC) temp.add(F("°C"));
|
||||
else temp.add(F("°F"));
|
||||
}
|
||||
@@ -252,23 +281,21 @@ class UsermodTemperature : public Usermod {
|
||||
bool readFromConfig(JsonObject &root) {
|
||||
// we look for JSON object: {"Temperature": {"pin": 0, "degC": true}}
|
||||
int8_t newTemperaturePin = temperaturePin;
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
newTemperaturePin = top["pin"] | newTemperaturePin;
|
||||
// newTemperaturePin = min(33,max(-1,(int)newTemperaturePin)); // bounds check
|
||||
degC = top["degC"] | degC;
|
||||
readingInterval = top[FPSTR(_readInterval)] | readingInterval/1000;
|
||||
readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms
|
||||
parasite = top[FPSTR(_parasite)] | parasite;
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
temperaturePin = newTemperaturePin;
|
||||
|
||||
@@ -21,6 +21,14 @@
|
||||
#include <Wire.h>
|
||||
#include <VL53L0X.h>
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#define HW_PIN_SCL 22
|
||||
#define HW_PIN_SDA 21
|
||||
#else
|
||||
#define HW_PIN_SCL 5
|
||||
#define HW_PIN_SDA 4
|
||||
#endif
|
||||
|
||||
#ifndef VL53L0X_MAX_RANGE_MM
|
||||
#define VL53L0X_MAX_RANGE_MM 230 // max height in millimiters to react for motions
|
||||
#endif
|
||||
@@ -42,6 +50,7 @@ class UsermodVL53L0XGestures : public Usermod {
|
||||
//Private class members. You can declare variables and functions only accessible to your usermod here
|
||||
unsigned long lastTime = 0;
|
||||
VL53L0X sensor;
|
||||
bool enabled = true;
|
||||
|
||||
bool wasMotionBefore = false;
|
||||
bool isLongMotion = false;
|
||||
@@ -50,6 +59,8 @@ class UsermodVL53L0XGestures : public Usermod {
|
||||
public:
|
||||
|
||||
void setup() {
|
||||
PinManagerPinType pins[2] = { { HW_PIN_SCL, true }, { HW_PIN_SDA, true } };
|
||||
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; }
|
||||
Wire.begin();
|
||||
|
||||
sensor.setTimeout(150);
|
||||
@@ -63,6 +74,7 @@ class UsermodVL53L0XGestures : public Usermod {
|
||||
|
||||
|
||||
void loop() {
|
||||
if (!enabled || strip.isUpdating()) return;
|
||||
if (millis() - lastTime > VL53L0X_DELAY_MS)
|
||||
{
|
||||
lastTime = millis();
|
||||
@@ -94,7 +106,7 @@ class UsermodVL53L0XGestures : public Usermod {
|
||||
// set brightness according to range
|
||||
bri = (VL53L0X_MAX_RANGE_MM - max(range, VL53L0X_MIN_RANGE_OFFSET)) * 255 / (VL53L0X_MAX_RANGE_MM - VL53L0X_MIN_RANGE_OFFSET);
|
||||
DEBUG_PRINTF(F("new brightness: %d"), bri);
|
||||
colorUpdated(1);
|
||||
stateUpdated(1);
|
||||
}
|
||||
} else if (wasMotionBefore) { //released
|
||||
long dur = millis() - motionStartTime;
|
||||
@@ -110,6 +122,19 @@ class UsermodVL53L0XGestures : public Usermod {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
* 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("VL53L0x");
|
||||
JsonArray pins = top.createNestedArray("pin");
|
||||
pins.add(HW_PIN_SCL);
|
||||
pins.add(HW_PIN_SDA);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
|
||||
@@ -137,9 +137,9 @@ void userLoop() {
|
||||
needRedraw = true;
|
||||
} else if (knownBrightness != bri) {
|
||||
needRedraw = true;
|
||||
} else if (knownMode != strip.getMode()) {
|
||||
} else if (knownMode != strip.getMainSegment().mode) {
|
||||
needRedraw = true;
|
||||
} else if (knownPalette != strip.getSegment(0).palette) {
|
||||
} else if (knownPalette != strip.getMainSegment().palette) {
|
||||
needRedraw = true;
|
||||
}
|
||||
|
||||
@@ -163,8 +163,8 @@ void userLoop() {
|
||||
#endif
|
||||
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
|
||||
knownBrightness = bri;
|
||||
knownMode = strip.getMode();
|
||||
knownPalette = strip.getSegment(0).palette;
|
||||
knownMode = strip.getMainSegment().mode;
|
||||
knownPalette = strip.getMainSegment().palette;
|
||||
u8x8.clear();
|
||||
u8x8.setFont(u8x8_font_chroma48medium8_r);
|
||||
|
||||
|
||||
@@ -143,9 +143,9 @@ void userLoop() {
|
||||
needRedraw = true;
|
||||
} else if (knownBrightness != bri) {
|
||||
needRedraw = true;
|
||||
} else if (knownMode != strip.getMode()) {
|
||||
} else if (knownMode != strip.getMainSegment().mode) {
|
||||
needRedraw = true;
|
||||
} else if (knownPalette != strip.getSegment(0).palette) {
|
||||
} else if (knownPalette != strip.getMainSegment().palette) {
|
||||
needRedraw = true;
|
||||
}
|
||||
|
||||
@@ -169,8 +169,8 @@ void userLoop() {
|
||||
#endif
|
||||
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
|
||||
knownBrightness = bri;
|
||||
knownMode = strip.getMode();
|
||||
knownPalette = strip.getSegment(0).palette;
|
||||
knownMode = strip.getMainSegment().mode;
|
||||
knownPalette = strip.getMainSegment().palette;
|
||||
u8x8.clear();
|
||||
u8x8.setFont(u8x8_font_chroma48medium8_r);
|
||||
|
||||
|
||||
@@ -54,35 +54,27 @@ void userLoop()
|
||||
switch (myKey) {
|
||||
case '1':
|
||||
applyPreset(1);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
case '2':
|
||||
applyPreset(2);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
case '3':
|
||||
applyPreset(3);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
case '4':
|
||||
applyPreset(4);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
case '5':
|
||||
applyPreset(5);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
case '6':
|
||||
applyPreset(6);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
case 'A':
|
||||
applyPreset(7);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
case 'B':
|
||||
applyPreset(8);
|
||||
colorUpdated(CALL_MODE_FX_CHANGED);
|
||||
break;
|
||||
|
||||
case '7':
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
#ifndef USERMOD_BATTERY_ADC_PRECISION
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
// 12 bits
|
||||
#define USERMOD_BATTERY_ADC_PRECISION 4095.0
|
||||
#define USERMOD_BATTERY_ADC_PRECISION 4095.0f
|
||||
#else
|
||||
// 10 bits
|
||||
#define USERMOD_BATTERY_ADC_PRECISION 1024.0
|
||||
#define USERMOD_BATTERY_ADC_PRECISION 1024.0f
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -39,11 +39,11 @@
|
||||
// https://batterybro.com/blogs/18650-wholesale-battery-reviews/18852515-when-to-recycle-18650-batteries-and-how-to-start-a-collection-center-in-your-vape-shop
|
||||
// Discharge voltage: 2.5 volt + .1 for personal safety
|
||||
#ifndef USERMOD_BATTERY_MIN_VOLTAGE
|
||||
#define USERMOD_BATTERY_MIN_VOLTAGE 2.6
|
||||
#define USERMOD_BATTERY_MIN_VOLTAGE 2.6f
|
||||
#endif
|
||||
|
||||
#ifndef USERMOD_BATTERY_MAX_VOLTAGE
|
||||
#define USERMOD_BATTERY_MAX_VOLTAGE 4.2
|
||||
#define USERMOD_BATTERY_MAX_VOLTAGE 4.2f
|
||||
#endif
|
||||
|
||||
class UsermodBatteryBasic : public Usermod
|
||||
|
||||
@@ -1,515 +0,0 @@
|
||||
//this code is a modified version of https://github.com/Makuna/NeoPixelBus/issues/103
|
||||
#ifndef NpbWrapper_h
|
||||
#define NpbWrapper_h
|
||||
|
||||
// make sure we're using esp32 platform
|
||||
#ifndef ARDUINO_ARCH_ESP32
|
||||
#error This version of NbpWrapper.h only works with ESP32 hardware.
|
||||
#endif
|
||||
|
||||
#ifndef NUM_STRIPS
|
||||
#error Need to define number of LED strips using build flag -D NUM_STRIPS=4 for 4 LED strips
|
||||
#endif
|
||||
|
||||
#ifndef PIXEL_COUNTS
|
||||
#error Need to define pixel counts using build flag -D PIXEL_COUNTS="25, 25, 25, 25" for 4 LED strips with 25 LEDs each
|
||||
#endif
|
||||
|
||||
#ifndef DATA_PINS
|
||||
#error Need to define data pins using build flag -D DATA_PINS="1, 2, 3, 4" if LED strips are on data pins 1, 2, 3, and 4
|
||||
#endif
|
||||
|
||||
// //PIN CONFIGURATION
|
||||
#ifndef LEDPIN
|
||||
#define LEDPIN 1 // Legacy pin def required by some other portions of code. This pin is not used do drive LEDs.
|
||||
#endif
|
||||
|
||||
#ifndef IRPIN
|
||||
#define IRPIN -1 //infrared pin (-1 to disable) MagicHome: 4, H801 Wifi: 0
|
||||
#endif
|
||||
|
||||
#ifndef RLYPIN
|
||||
#define RLYPIN -1 //pin for relay, will be set HIGH if LEDs are on (-1 to disable). Also usable for standby leds, triggers,...
|
||||
#endif
|
||||
|
||||
#ifndef AUXPIN
|
||||
#define AUXPIN -1 //debug auxiliary output pin (-1 to disable)
|
||||
#endif
|
||||
|
||||
#ifndef RLYMDE
|
||||
#define RLYMDE 1 //mode for relay, 0: LOW if LEDs are on 1: HIGH if LEDs are on
|
||||
#endif
|
||||
|
||||
#include <NeoPixelBrightnessBus.h>
|
||||
#include "const.h"
|
||||
|
||||
const uint8_t numStrips = NUM_STRIPS; // max 8 strips allowed on esp32
|
||||
const uint16_t pixelCounts[numStrips] = {PIXEL_COUNTS}; // number of pixels on each strip
|
||||
const uint8_t dataPins[numStrips] = {DATA_PINS}; // change these pins based on your board
|
||||
|
||||
#define PIXELFEATURE3 NeoGrbFeature
|
||||
#define PIXELFEATURE4 NeoGrbwFeature
|
||||
|
||||
// ESP32 has 8 RMT interfaces available, each of which can drive a strip of pixels
|
||||
// Convenience #defines for creating NeoPixelBrightnessBus on each RMT interface for both GRB and GRBW LED strips
|
||||
#define NeoPixelBrightnessBusGrbRmt0 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt0Ws2812xMethod>
|
||||
#define NeoPixelBrightnessBusGrbRmt1 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt1Ws2812xMethod>
|
||||
#define NeoPixelBrightnessBusGrbRmt2 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt2Ws2812xMethod>
|
||||
#define NeoPixelBrightnessBusGrbRmt3 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt3Ws2812xMethod>
|
||||
#define NeoPixelBrightnessBusGrbRmt4 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt4Ws2812xMethod>
|
||||
#define NeoPixelBrightnessBusGrbRmt5 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt5Ws2812xMethod>
|
||||
#define NeoPixelBrightnessBusGrbRmt6 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt6Ws2812xMethod>
|
||||
#define NeoPixelBrightnessBusGrbRmt7 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt7Ws2812xMethod>
|
||||
#define NeoPixelBrightnessBusGrbwRmt0 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt0Ws2812xMethod>
|
||||
#define NeoPixelBrightnessBusGrbwRmt1 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt1Ws2812xMethod>
|
||||
#define NeoPixelBrightnessBusGrbwRmt2 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt2Ws2812xMethod>
|
||||
#define NeoPixelBrightnessBusGrbwRmt3 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt3Ws2812xMethod>
|
||||
#define NeoPixelBrightnessBusGrbwRmt4 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt4Ws2812xMethod>
|
||||
#define NeoPixelBrightnessBusGrbwRmt5 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt5Ws2812xMethod>
|
||||
#define NeoPixelBrightnessBusGrbwRmt6 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt6Ws2812xMethod>
|
||||
#define NeoPixelBrightnessBusGrbwRmt7 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt7Ws2812xMethod>
|
||||
|
||||
enum NeoPixelType
|
||||
{
|
||||
NeoPixelType_None = 0,
|
||||
NeoPixelType_Grb = 1,
|
||||
NeoPixelType_Grbw = 2,
|
||||
NeoPixelType_End = 3
|
||||
};
|
||||
|
||||
class NeoPixelWrapper
|
||||
{
|
||||
public:
|
||||
NeoPixelWrapper() :
|
||||
_type(NeoPixelType_None)
|
||||
{
|
||||
// On initialization fill in the pixelStripStartIdx array with the beginning index of each strip
|
||||
// relative to th entire array.
|
||||
uint16_t totalPixels = 0;
|
||||
for (uint8_t idx = 0; idx < numStrips; idx++)
|
||||
{
|
||||
pixelStripStartIdx[idx] = totalPixels;
|
||||
totalPixels += pixelCounts[idx];
|
||||
}
|
||||
}
|
||||
|
||||
~NeoPixelWrapper()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void Begin(NeoPixelType type, uint16_t pixelCount)
|
||||
{
|
||||
|
||||
cleanup();
|
||||
|
||||
_type = type;
|
||||
|
||||
switch (_type)
|
||||
{
|
||||
case NeoPixelType_Grb:
|
||||
{
|
||||
for (uint8_t idx = 0; idx < numStrips; idx++)
|
||||
{
|
||||
switch (idx)
|
||||
{
|
||||
case 0: pGrb0 = new NeoPixelBrightnessBusGrbRmt0(pixelCounts[idx], dataPins[idx]); pGrb0->Begin(); break;
|
||||
case 1: pGrb1 = new NeoPixelBrightnessBusGrbRmt1(pixelCounts[idx], dataPins[idx]); pGrb1->Begin(); break;
|
||||
case 2: pGrb2 = new NeoPixelBrightnessBusGrbRmt2(pixelCounts[idx], dataPins[idx]); pGrb2->Begin(); break;
|
||||
case 3: pGrb3 = new NeoPixelBrightnessBusGrbRmt3(pixelCounts[idx], dataPins[idx]); pGrb3->Begin(); break;
|
||||
case 4: pGrb4 = new NeoPixelBrightnessBusGrbRmt4(pixelCounts[idx], dataPins[idx]); pGrb4->Begin(); break;
|
||||
case 5: pGrb5 = new NeoPixelBrightnessBusGrbRmt5(pixelCounts[idx], dataPins[idx]); pGrb5->Begin(); break;
|
||||
case 6: pGrb6 = new NeoPixelBrightnessBusGrbRmt6(pixelCounts[idx], dataPins[idx]); pGrb6->Begin(); break;
|
||||
case 7: pGrb7 = new NeoPixelBrightnessBusGrbRmt7(pixelCounts[idx], dataPins[idx]); pGrb7->Begin(); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case NeoPixelType_Grbw:
|
||||
{
|
||||
for (uint8_t idx = 0; idx < numStrips; idx++)
|
||||
{
|
||||
switch (idx)
|
||||
{
|
||||
case 0: pGrbw0 = new NeoPixelBrightnessBusGrbwRmt0(pixelCounts[idx], dataPins[idx]); pGrbw0->Begin(); break;
|
||||
case 1: pGrbw1 = new NeoPixelBrightnessBusGrbwRmt1(pixelCounts[idx], dataPins[idx]); pGrbw1->Begin(); break;
|
||||
case 2: pGrbw2 = new NeoPixelBrightnessBusGrbwRmt2(pixelCounts[idx], dataPins[idx]); pGrbw2->Begin(); break;
|
||||
case 3: pGrbw3 = new NeoPixelBrightnessBusGrbwRmt3(pixelCounts[idx], dataPins[idx]); pGrbw3->Begin(); break;
|
||||
case 4: pGrbw4 = new NeoPixelBrightnessBusGrbwRmt4(pixelCounts[idx], dataPins[idx]); pGrbw4->Begin(); break;
|
||||
case 5: pGrbw5 = new NeoPixelBrightnessBusGrbwRmt5(pixelCounts[idx], dataPins[idx]); pGrbw5->Begin(); break;
|
||||
case 6: pGrbw6 = new NeoPixelBrightnessBusGrbwRmt6(pixelCounts[idx], dataPins[idx]); pGrbw6->Begin(); break;
|
||||
case 7: pGrbw7 = new NeoPixelBrightnessBusGrbwRmt7(pixelCounts[idx], dataPins[idx]); pGrbw7->Begin(); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Show()
|
||||
{
|
||||
switch (_type)
|
||||
{
|
||||
case NeoPixelType_Grb:
|
||||
{
|
||||
for (uint8_t idx = 0; idx < numStrips; idx++)
|
||||
{
|
||||
switch (idx)
|
||||
{
|
||||
case 0: pGrb0->Show(); break;
|
||||
case 1: pGrb1->Show(); break;
|
||||
case 2: pGrb2->Show(); break;
|
||||
case 3: pGrb3->Show(); break;
|
||||
case 4: pGrb4->Show(); break;
|
||||
case 5: pGrb5->Show(); break;
|
||||
case 6: pGrb6->Show(); break;
|
||||
case 7: pGrb7->Show(); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NeoPixelType_Grbw:
|
||||
{
|
||||
for (uint8_t idx = 0; idx < numStrips; idx++)
|
||||
{
|
||||
switch (idx)
|
||||
{
|
||||
case 0: pGrbw0->Show(); break;
|
||||
case 1: pGrbw1->Show(); break;
|
||||
case 2: pGrbw2->Show(); break;
|
||||
case 3: pGrbw3->Show(); break;
|
||||
case 4: pGrbw4->Show(); break;
|
||||
case 5: pGrbw5->Show(); break;
|
||||
case 6: pGrbw6->Show(); break;
|
||||
case 7: pGrbw7->Show(); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CanShow()
|
||||
{
|
||||
bool canShow = true;
|
||||
switch (_type)
|
||||
{
|
||||
case NeoPixelType_Grb:
|
||||
{
|
||||
for (uint8_t idx = 0; idx < numStrips; idx++)
|
||||
{
|
||||
switch (idx)
|
||||
{
|
||||
case 0: canShow &= pGrb0->CanShow(); break;
|
||||
case 1: canShow &= pGrb1->CanShow(); break;
|
||||
case 2: canShow &= pGrb2->CanShow(); break;
|
||||
case 3: canShow &= pGrb3->CanShow(); break;
|
||||
case 4: canShow &= pGrb4->CanShow(); break;
|
||||
case 5: canShow &= pGrb5->CanShow(); break;
|
||||
case 6: canShow &= pGrb6->CanShow(); break;
|
||||
case 7: canShow &= pGrb7->CanShow(); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NeoPixelType_Grbw:
|
||||
{
|
||||
for (uint8_t idx = 0; idx < numStrips; idx++)
|
||||
{
|
||||
switch (idx)
|
||||
{
|
||||
case 0: canShow &= pGrbw0->CanShow(); break;
|
||||
case 1: canShow &= pGrbw1->CanShow(); break;
|
||||
case 2: canShow &= pGrbw2->CanShow(); break;
|
||||
case 3: canShow &= pGrbw3->CanShow(); break;
|
||||
case 4: canShow &= pGrbw4->CanShow(); break;
|
||||
case 5: canShow &= pGrbw5->CanShow(); break;
|
||||
case 6: canShow &= pGrbw6->CanShow(); break;
|
||||
case 7: canShow &= pGrbw7->CanShow(); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return canShow;
|
||||
}
|
||||
|
||||
void SetPixelColorRaw(uint16_t indexPixel, RgbwColor c)
|
||||
{
|
||||
// figure out which strip this pixel index is on
|
||||
uint8_t stripIdx = 0;
|
||||
for (uint8_t idx = 0; idx < numStrips; idx++)
|
||||
{
|
||||
if (indexPixel >= pixelStripStartIdx[idx])
|
||||
{
|
||||
stripIdx = idx;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// subtract strip start index so we're addressing just this strip instead of all pixels on all strips
|
||||
indexPixel -= pixelStripStartIdx[stripIdx];
|
||||
switch (_type)
|
||||
{
|
||||
case NeoPixelType_Grb:
|
||||
{
|
||||
RgbColor rgb = RgbColor(c.R, c.G, c.B);
|
||||
switch (stripIdx)
|
||||
{
|
||||
case 0: pGrb0->SetPixelColor(indexPixel, rgb); break;
|
||||
case 1: pGrb1->SetPixelColor(indexPixel, rgb); break;
|
||||
case 2: pGrb2->SetPixelColor(indexPixel, rgb); break;
|
||||
case 3: pGrb3->SetPixelColor(indexPixel, rgb); break;
|
||||
case 4: pGrb4->SetPixelColor(indexPixel, rgb); break;
|
||||
case 5: pGrb5->SetPixelColor(indexPixel, rgb); break;
|
||||
case 6: pGrb6->SetPixelColor(indexPixel, rgb); break;
|
||||
case 7: pGrb7->SetPixelColor(indexPixel, rgb); break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NeoPixelType_Grbw:
|
||||
{
|
||||
switch (stripIdx)
|
||||
{
|
||||
case 0: pGrbw0->SetPixelColor(indexPixel, c); break;
|
||||
case 1: pGrbw1->SetPixelColor(indexPixel, c); break;
|
||||
case 2: pGrbw2->SetPixelColor(indexPixel, c); break;
|
||||
case 3: pGrbw3->SetPixelColor(indexPixel, c); break;
|
||||
case 4: pGrbw4->SetPixelColor(indexPixel, c); break;
|
||||
case 5: pGrbw5->SetPixelColor(indexPixel, c); break;
|
||||
case 6: pGrbw6->SetPixelColor(indexPixel, c); break;
|
||||
case 7: pGrbw7->SetPixelColor(indexPixel, c); break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetPixelColor(uint16_t indexPixel, RgbwColor c)
|
||||
{
|
||||
/*
|
||||
Set pixel color with necessary color order conversion.
|
||||
*/
|
||||
|
||||
RgbwColor col;
|
||||
|
||||
uint8_t co = _colorOrder;
|
||||
#ifdef COLOR_ORDER_OVERRIDE
|
||||
if (indexPixel >= COO_MIN && indexPixel < COO_MAX) co = COO_ORDER;
|
||||
#endif
|
||||
|
||||
//reorder channels to selected order
|
||||
switch (co)
|
||||
{
|
||||
case 0: col.G = c.G; col.R = c.R; col.B = c.B; break; //0 = GRB, default
|
||||
case 1: col.G = c.R; col.R = c.G; col.B = c.B; break; //1 = RGB, common for WS2811
|
||||
case 2: col.G = c.B; col.R = c.R; col.B = c.G; break; //2 = BRG
|
||||
case 3: col.G = c.R; col.R = c.B; col.B = c.G; break; //3 = RBG
|
||||
case 4: col.G = c.B; col.R = c.G; col.B = c.R; break; //4 = BGR
|
||||
default: col.G = c.G; col.R = c.B; col.B = c.R; break; //5 = GBR
|
||||
}
|
||||
col.W = c.W;
|
||||
|
||||
SetPixelColorRaw(indexPixel, col);
|
||||
}
|
||||
|
||||
void SetBrightness(byte b)
|
||||
{
|
||||
switch (_type)
|
||||
{
|
||||
case NeoPixelType_Grb:
|
||||
{
|
||||
for (uint8_t idx = 0; idx < numStrips; idx++)
|
||||
{
|
||||
switch (idx)
|
||||
{
|
||||
case 0: pGrb0->SetBrightness(b); break;
|
||||
case 1: pGrb1->SetBrightness(b); break;
|
||||
case 2: pGrb2->SetBrightness(b); break;
|
||||
case 3: pGrb3->SetBrightness(b); break;
|
||||
case 4: pGrb4->SetBrightness(b); break;
|
||||
case 5: pGrb5->SetBrightness(b); break;
|
||||
case 6: pGrb6->SetBrightness(b); break;
|
||||
case 7: pGrb7->SetBrightness(b); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NeoPixelType_Grbw:
|
||||
{
|
||||
for (uint8_t idx = 0; idx < numStrips; idx++)
|
||||
{
|
||||
switch (idx)
|
||||
{
|
||||
case 0: pGrbw0->SetBrightness(b); break;
|
||||
case 1: pGrbw1->SetBrightness(b); break;
|
||||
case 2: pGrbw2->SetBrightness(b); break;
|
||||
case 3: pGrbw3->SetBrightness(b); break;
|
||||
case 4: pGrbw4->SetBrightness(b); break;
|
||||
case 5: pGrbw5->SetBrightness(b); break;
|
||||
case 6: pGrbw6->SetBrightness(b); break;
|
||||
case 7: pGrbw7->SetBrightness(b); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetColorOrder(byte colorOrder)
|
||||
{
|
||||
_colorOrder = colorOrder;
|
||||
}
|
||||
|
||||
uint8_t GetColorOrder()
|
||||
{
|
||||
return _colorOrder;
|
||||
}
|
||||
|
||||
RgbwColor GetPixelColorRaw(uint16_t indexPixel) const
|
||||
{
|
||||
// figure out which strip this pixel index is on
|
||||
uint8_t stripIdx = 0;
|
||||
for (uint8_t idx = 0; idx < numStrips; idx++)
|
||||
{
|
||||
if (indexPixel >= pixelStripStartIdx[idx])
|
||||
{
|
||||
stripIdx = idx;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// subtract strip start index so we're addressing just this strip instead of all pixels on all strips
|
||||
indexPixel -= pixelStripStartIdx[stripIdx];
|
||||
switch (_type)
|
||||
{
|
||||
case NeoPixelType_Grb:
|
||||
{
|
||||
switch (stripIdx)
|
||||
{
|
||||
case 0: return pGrb0->GetPixelColor(indexPixel);
|
||||
case 1: return pGrb1->GetPixelColor(indexPixel);
|
||||
case 2: return pGrb2->GetPixelColor(indexPixel);
|
||||
case 3: return pGrb3->GetPixelColor(indexPixel);
|
||||
case 4: return pGrb4->GetPixelColor(indexPixel);
|
||||
case 5: return pGrb5->GetPixelColor(indexPixel);
|
||||
case 6: return pGrb6->GetPixelColor(indexPixel);
|
||||
case 7: return pGrb7->GetPixelColor(indexPixel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NeoPixelType_Grbw:
|
||||
switch (stripIdx)
|
||||
{
|
||||
case 0: return pGrbw0->GetPixelColor(indexPixel);
|
||||
case 1: return pGrbw1->GetPixelColor(indexPixel);
|
||||
case 2: return pGrbw2->GetPixelColor(indexPixel);
|
||||
case 3: return pGrbw3->GetPixelColor(indexPixel);
|
||||
case 4: return pGrbw4->GetPixelColor(indexPixel);
|
||||
case 5: return pGrbw5->GetPixelColor(indexPixel);
|
||||
case 6: return pGrbw6->GetPixelColor(indexPixel);
|
||||
case 7: return pGrbw7->GetPixelColor(indexPixel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// NOTE: Due to feature differences, some support RGBW but the method name
|
||||
// here needs to be unique, thus GetPixeColorRgbw
|
||||
uint32_t GetPixelColorRgbw(uint16_t indexPixel) const
|
||||
{
|
||||
RgbwColor col = GetPixelColorRaw(indexPixel);
|
||||
uint8_t co = _colorOrder;
|
||||
#ifdef COLOR_ORDER_OVERRIDE
|
||||
if (indexPixel >= COO_MIN && indexPixel < COO_MAX) co = COO_ORDER;
|
||||
#endif
|
||||
|
||||
switch (co)
|
||||
{
|
||||
// W G R B
|
||||
case 0: return ((col.W << 24) | (col.G << 8) | (col.R << 16) | (col.B)); //0 = GRB, default
|
||||
case 1: return ((col.W << 24) | (col.R << 8) | (col.G << 16) | (col.B)); //1 = RGB, common for WS2811
|
||||
case 2: return ((col.W << 24) | (col.B << 8) | (col.R << 16) | (col.G)); //2 = BRG
|
||||
case 3: return ((col.W << 24) | (col.B << 8) | (col.G << 16) | (col.R)); //3 = RBG
|
||||
case 4: return ((col.W << 24) | (col.R << 8) | (col.B << 16) | (col.G)); //4 = BGR
|
||||
case 5: return ((col.W << 24) | (col.G << 8) | (col.B << 16) | (col.R)); //5 = GBR
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
NeoPixelType _type;
|
||||
byte _colorOrder = 0;
|
||||
|
||||
uint16_t pixelStripStartIdx[numStrips];
|
||||
|
||||
// pointers for every possible type for up to 8 strips
|
||||
NeoPixelBrightnessBusGrbRmt0 *pGrb0;
|
||||
NeoPixelBrightnessBusGrbRmt1 *pGrb1;
|
||||
NeoPixelBrightnessBusGrbRmt2 *pGrb2;
|
||||
NeoPixelBrightnessBusGrbRmt3 *pGrb3;
|
||||
NeoPixelBrightnessBusGrbRmt4 *pGrb4;
|
||||
NeoPixelBrightnessBusGrbRmt5 *pGrb5;
|
||||
NeoPixelBrightnessBusGrbRmt6 *pGrb6;
|
||||
NeoPixelBrightnessBusGrbRmt7 *pGrb7;
|
||||
NeoPixelBrightnessBusGrbwRmt0 *pGrbw0;
|
||||
NeoPixelBrightnessBusGrbwRmt1 *pGrbw1;
|
||||
NeoPixelBrightnessBusGrbwRmt2 *pGrbw2;
|
||||
NeoPixelBrightnessBusGrbwRmt3 *pGrbw3;
|
||||
NeoPixelBrightnessBusGrbwRmt4 *pGrbw4;
|
||||
NeoPixelBrightnessBusGrbwRmt5 *pGrbw5;
|
||||
NeoPixelBrightnessBusGrbwRmt6 *pGrbw6;
|
||||
NeoPixelBrightnessBusGrbwRmt7 *pGrbw7;
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
switch (_type)
|
||||
{
|
||||
case NeoPixelType_Grb:
|
||||
{
|
||||
for (uint8_t idx = 0; idx < numStrips; idx++)
|
||||
{
|
||||
switch (idx)
|
||||
{
|
||||
case 0: delete pGrb0; pGrb0 = NULL; break;
|
||||
case 1: delete pGrb1; pGrb1 = NULL; break;
|
||||
case 2: delete pGrb2; pGrb2 = NULL; break;
|
||||
case 3: delete pGrb3; pGrb3 = NULL; break;
|
||||
case 4: delete pGrb4; pGrb4 = NULL; break;
|
||||
case 5: delete pGrb5; pGrb5 = NULL; break;
|
||||
case 6: delete pGrb6; pGrb6 = NULL; break;
|
||||
case 7: delete pGrb7; pGrb7 = NULL; break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NeoPixelType_Grbw:
|
||||
{
|
||||
for (uint8_t idx = 0; idx < numStrips; idx++)
|
||||
{
|
||||
switch (idx)
|
||||
{
|
||||
case 0: delete pGrbw0; pGrbw0 = NULL; break;
|
||||
case 1: delete pGrbw1; pGrbw1 = NULL; break;
|
||||
case 2: delete pGrbw2; pGrbw2 = NULL; break;
|
||||
case 3: delete pGrbw3; pGrbw3 = NULL; break;
|
||||
case 4: delete pGrbw4; pGrbw4 = NULL; break;
|
||||
case 5: delete pGrbw5; pGrbw5 = NULL; break;
|
||||
case 6: delete pGrbw6; pGrbw6 = NULL; break;
|
||||
case 7: delete pGrbw7; pGrbw7 = NULL; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
@@ -1,22 +0,0 @@
|
||||
# esp32_multistrip
|
||||
|
||||
This usermod enables up to 8 data pins to be used from an esp32 module to drive separate LED strands. This only works with one-wire LEDs like the WS2812.
|
||||
|
||||
The esp32 RMT hardware is used for data output. See here for hardware driver implementation details: https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
|
||||
|
||||
Pass the following variables to the compiler as build flags:
|
||||
|
||||
- `ESP32_MULTISTRIP`
|
||||
- Define this to use usermod NpbWrapper.h instead of default one in WLED.
|
||||
- `NUM_STRIPS`
|
||||
- Number of strips in use
|
||||
- `PIXEL_COUNTS`
|
||||
- List of pixel counts in each strip
|
||||
- `DATA_PINS`
|
||||
- List of data pins each strip is attached to. There may be board-specific restrictions on which pins can be used for RTM.
|
||||
|
||||
From the perspective of WLED software, the LEDs are addressed as one long strand. The modified NbpWrapper.h file addresses the appropriate strand from the overall LED index based on the number of LEDs defined in each strand.
|
||||
|
||||
See `platformio_override.ini` for example configuration.
|
||||
|
||||
Tested on low cost ESP-WROOM-32 dev boards from Amazon, such as those sold by KeeYees.
|
||||
@@ -1,16 +0,0 @@
|
||||
; Example platformio_override.ini that shows how to configure your environment to use the multistrip usermod.
|
||||
; Copy this file to the base wled directory that contains platformio.ini.
|
||||
; Multistrip requires ESP32 because it has many more pins that can be used as LED outputs.
|
||||
; Need to define NUM_STRIPS, PIXEL_COUNTS, and DATA_PINS as shown below.
|
||||
|
||||
[platformio]
|
||||
default_envs = esp32_multistrip
|
||||
|
||||
[env:esp32_multistrip]
|
||||
extends=env:esp32dev
|
||||
build_flags = ${env:esp32dev.build_flags}
|
||||
-D ESP32_MULTISTRIP ; define this variable to use ESP32_MULTISTRIP usermod
|
||||
-D NUM_STRIPS=4 ; number of pixel strips in use
|
||||
-D PIXEL_COUNTS="50, 50, 50, 50" ; number of pixels in each strip
|
||||
-D DATA_PINS="25, 26, 32, 33" ; esp32 pins used for each pixel strip. available pins depends on esp32 module.
|
||||
|
||||
@@ -42,6 +42,14 @@
|
||||
#include "Wire.h"
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#define HW_PIN_SCL 22
|
||||
#define HW_PIN_SDA 21
|
||||
#else
|
||||
#define HW_PIN_SCL 5
|
||||
#define HW_PIN_SDA 4
|
||||
#endif
|
||||
|
||||
// ================================================================
|
||||
// === INTERRUPT DETECTION ROUTINE ===
|
||||
// ================================================================
|
||||
@@ -55,7 +63,8 @@ void IRAM_ATTR dmpDataReady() {
|
||||
class MPU6050Driver : public Usermod {
|
||||
private:
|
||||
MPU6050 mpu;
|
||||
|
||||
bool enabled = true;
|
||||
|
||||
// MPU control/status vars
|
||||
bool dmpReady = false; // set true if DMP init was successful
|
||||
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
|
||||
@@ -84,6 +93,8 @@ class MPU6050Driver : public Usermod {
|
||||
* setup() is called once at boot. WiFi is not yet connected at this point.
|
||||
*/
|
||||
void setup() {
|
||||
PinManagerPinType pins[2] = { { HW_PIN_SCL, true }, { HW_PIN_SDA, true } };
|
||||
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; }
|
||||
// join I2C bus (I2Cdev library doesn't do this automatically)
|
||||
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
|
||||
Wire.begin();
|
||||
@@ -93,16 +104,16 @@ class MPU6050Driver : public Usermod {
|
||||
#endif
|
||||
|
||||
// initialize device
|
||||
Serial.println(F("Initializing I2C devices..."));
|
||||
DEBUG_PRINTLN(F("Initializing I2C devices..."));
|
||||
mpu.initialize();
|
||||
pinMode(INTERRUPT_PIN, INPUT);
|
||||
|
||||
// verify connection
|
||||
Serial.println(F("Testing device connections..."));
|
||||
Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
|
||||
DEBUG_PRINTLN(F("Testing device connections..."));
|
||||
DEBUG_PRINTLN(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
|
||||
|
||||
// load and configure the DMP
|
||||
Serial.println(F("Initializing DMP..."));
|
||||
DEBUG_PRINTLN(F("Initializing DMP..."));
|
||||
devStatus = mpu.dmpInitialize();
|
||||
|
||||
// supply your own gyro offsets here, scaled for min sensitivity
|
||||
@@ -114,16 +125,16 @@ class MPU6050Driver : public Usermod {
|
||||
// make sure it worked (returns 0 if so)
|
||||
if (devStatus == 0) {
|
||||
// turn on the DMP, now that it's ready
|
||||
Serial.println(F("Enabling DMP..."));
|
||||
DEBUG_PRINTLN(F("Enabling DMP..."));
|
||||
mpu.setDMPEnabled(true);
|
||||
|
||||
// enable Arduino interrupt detection
|
||||
Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
|
||||
DEBUG_PRINTLN(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
|
||||
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
|
||||
mpuIntStatus = mpu.getIntStatus();
|
||||
|
||||
// set our DMP Ready flag so the main loop() function knows it's okay to use it
|
||||
Serial.println(F("DMP ready! Waiting for first interrupt..."));
|
||||
DEBUG_PRINTLN(F("DMP ready! Waiting for first interrupt..."));
|
||||
dmpReady = true;
|
||||
|
||||
// get expected DMP packet size for later comparison
|
||||
@@ -133,9 +144,9 @@ class MPU6050Driver : public Usermod {
|
||||
// 1 = initial memory load failed
|
||||
// 2 = DMP configuration updates failed
|
||||
// (if it's going to break, usually the code will be 1)
|
||||
Serial.print(F("DMP Initialization failed (code "));
|
||||
Serial.print(devStatus);
|
||||
Serial.println(F(")"));
|
||||
DEBUG_PRINT(F("DMP Initialization failed (code "));
|
||||
DEBUG_PRINT(devStatus);
|
||||
DEBUG_PRINTLN(F(")"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +155,7 @@ class MPU6050Driver : public Usermod {
|
||||
* Use it to initialize network interfaces
|
||||
*/
|
||||
void connected() {
|
||||
//Serial.println("Connected to WiFi!");
|
||||
//DEBUG_PRINTLN("Connected to WiFi!");
|
||||
}
|
||||
|
||||
|
||||
@@ -153,7 +164,7 @@ class MPU6050Driver : public Usermod {
|
||||
*/
|
||||
void loop() {
|
||||
// if programming failed, don't try to do anything
|
||||
if (!dmpReady) return;
|
||||
if (!enabled || !dmpReady || strip.isUpdating()) return;
|
||||
|
||||
// wait for MPU interrupt or extra packet(s) available
|
||||
if (!mpuInterrupt && fifoCount < packetSize) return;
|
||||
@@ -169,7 +180,7 @@ class MPU6050Driver : public Usermod {
|
||||
if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
|
||||
// reset so we can continue cleanly
|
||||
mpu.resetFIFO();
|
||||
Serial.println(F("FIFO overflow!"));
|
||||
DEBUG_PRINTLN(F("FIFO overflow!"));
|
||||
|
||||
// otherwise, check for DMP data ready interrupt (this should happen frequently)
|
||||
} else if (mpuIntStatus & 0x02) {
|
||||
@@ -259,10 +270,23 @@ class MPU6050Driver : public Usermod {
|
||||
*/
|
||||
void readFromJsonState(JsonObject& root)
|
||||
{
|
||||
//if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
|
||||
//if (root["bri"] == 255) DEBUG_PRINTLN(F("Don't burn down your garage!"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* 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)
|
||||
* 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("MPU6050_IMU");
|
||||
JsonArray pins = top.createNestedArray("pin");
|
||||
pins.add(HW_PIN_SCL);
|
||||
pins.add(HW_PIN_SDA);
|
||||
}
|
||||
|
||||
/*
|
||||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# DEPRECATION NOTICE
|
||||
This usermod is deprecated and no longer maintained. It will be removed in a future WLED release. Please use usermod multi_relay which has more features.
|
||||
|
||||
|
||||
# MQTT controllable switches
|
||||
This usermod allows controlling switches (e.g. relays) via MQTT.
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#warning "This usermod is deprecated and no longer maintained. It will be removed in a future WLED release. Please use usermod multi_relay which has more features."
|
||||
|
||||
#include "wled.h"
|
||||
#ifndef WLED_ENABLE_MQTT
|
||||
#error "This user mod requires MQTT to be enabled."
|
||||
|
||||
@@ -5,38 +5,42 @@ This usermod-v2 modification allows the connection of multiple relays each with
|
||||
## HTTP API
|
||||
All responses are returned as JSON.
|
||||
|
||||
Status Request: `http://[device-ip]/relays`
|
||||
Switch Command: `http://[device-ip]/relays?switch=1,0,1,1`
|
||||
* Status Request: `http://[device-ip]/relays`
|
||||
* Switch Command: `http://[device-ip]/relays?switch=1,0,1,1`
|
||||
|
||||
The number of numbers behind the switch parameter must correspond to the number of relays. The number 1 switches the relay on. The number 0 switches the relay off.
|
||||
|
||||
Toggle Command: `http://[device-ip]/relays?toggle=1,0,1,1`
|
||||
* Toggle Command: `http://[device-ip]/relays?toggle=1,0,1,1`
|
||||
|
||||
The number of numbers behind the parameter switch must correspond to the number of relays. The number 1 causes a toggling of the relay. The number 0 leaves the state of the device.
|
||||
|
||||
Examples
|
||||
1. 4 relays at all, relay 2 will be toggled: `http://[device-ip]/relays?toggle=0,1,0,0`
|
||||
2. 3 relays at all, relay 1&3 will be switched on: `http://[device-ip]/relays?switch=1,0,1`
|
||||
1. total of 4 relays, relay 2 will be toggled: `http://[device-ip]/relays?toggle=0,1,0,0`
|
||||
2. total of 3 relays, relay 1&3 will be switched on: `http://[device-ip]/relays?switch=1,0,1`
|
||||
|
||||
## JSON API
|
||||
You can switch relay state using the following JSON object transmitted to: `http://[device-ip]/json`
|
||||
|
||||
|
||||
Switch relay 0 on: `{"MultiRelay":{"relay":0,"on":true}}`
|
||||
|
||||
Switch relay4 3 & 4 off: `{"MultiRelay":[{"relay":2,"on":false},{"relay":3,"on":false}]}`
|
||||
|
||||
## MQTT API
|
||||
|
||||
wled/deviceMAC/relay/0/command on|off|toggle
|
||||
wled/deviceMAC/relay/1/command on|off|toggle
|
||||
* `wled`/_deviceMAC_/`relay`/`0`/`command` `on`|`off`|`toggle`
|
||||
* `wled`/_deviceMAC_/`relay`/`1`/`command` `on`|`off`|`toggle`
|
||||
|
||||
When relay is switched it will publish a message:
|
||||
|
||||
wled/deviceMAC/relay/0 on|off
|
||||
* `wled`/_deviceMAC_/`relay`/`0` `on`|`off`
|
||||
|
||||
|
||||
## Usermod installation
|
||||
|
||||
1. Register the usermod by adding `#include "../usermods/multi_relay/usermod_multi_relay.h"` at the top and `usermods.add(new MultiRelay());` at the bottom of `usermods_list.cpp`.
|
||||
or
|
||||
2. Use `#define USERMOD_MULTI_RELAY` in wled.h or `-D USERMOD_MULTI_RELAY`in your platformio.ini
|
||||
2. Use `#define USERMOD_MULTI_RELAY` in wled.h or `-D USERMOD_MULTI_RELAY` in your platformio.ini
|
||||
|
||||
You can override the default maximum number (4) of relays by defining MULTI_RELAY_MAX_RELAYS.
|
||||
|
||||
@@ -76,10 +80,21 @@ void registerUsermods()
|
||||
|
||||
Usermod can be configured in Usermods settings page.
|
||||
|
||||
* `enabled` - enable/disable usermod
|
||||
* `pin` - GPIO pin where relay is attached to ESP
|
||||
* `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
|
||||
|
||||
If there is no MultiRelay section, just save current configuration and re-open Usermods settings page.
|
||||
|
||||
Have fun - @blazoncek
|
||||
|
||||
## Change log
|
||||
2021-04
|
||||
* First implementation.
|
||||
* First implementation.
|
||||
|
||||
2021-11
|
||||
* Added information about dynamic configuration options
|
||||
* Added button support.
|
||||
@@ -45,6 +45,11 @@ class MultiRelay : public Usermod {
|
||||
// status of initialisation
|
||||
bool initDone = false;
|
||||
|
||||
bool HAautodiscovery = false;
|
||||
|
||||
uint16_t periodicBroadcastSec = 60;
|
||||
unsigned long lastBroadcast = 0;
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
@@ -53,14 +58,15 @@ class MultiRelay : public Usermod {
|
||||
static const char _activeHigh[];
|
||||
static const char _external[];
|
||||
static const char _button[];
|
||||
static const char _broadcast[];
|
||||
static const char _HAautodiscovery[];
|
||||
|
||||
|
||||
void publishMqtt(const char* state, int relay) {
|
||||
void publishMqtt(int relay) {
|
||||
//Check if MQTT Connected, otherwise it will crash the 8266
|
||||
if (WLED_MQTT_CONNECTED){
|
||||
char subuf[64];
|
||||
sprintf_P(subuf, PSTR("%s/relay/%d"), mqttDeviceTopic, relay);
|
||||
mqtt->publish(subuf, 0, false, state);
|
||||
mqtt->publish(subuf, 0, false, _relay[relay].state ? "on" : "off");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,15 +74,19 @@ class MultiRelay : public Usermod {
|
||||
* switch off the strip if the delay has elapsed
|
||||
*/
|
||||
void handleOffTimer() {
|
||||
unsigned long now = millis();
|
||||
bool activeRelays = false;
|
||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||
if (_relay[i].active && _switchTimerStart > 0 && millis() - _switchTimerStart > (_relay[i].delay*1000)) {
|
||||
if (_relay[i].active && _switchTimerStart > 0 && now - _switchTimerStart > (_relay[i].delay*1000)) {
|
||||
if (!_relay[i].external) toggleRelay(i);
|
||||
_relay[i].active = false;
|
||||
} else if (periodicBroadcastSec && now - lastBroadcast > (periodicBroadcastSec*1000)) {
|
||||
if (_relay[i].pin>=0) publishMqtt(i);
|
||||
}
|
||||
activeRelays = activeRelays || _relay[i].active;
|
||||
}
|
||||
if (!activeRelays) _switchTimerStart = 0;
|
||||
if (periodicBroadcastSec && now - lastBroadcast > (periodicBroadcastSec*1000)) lastBroadcast = now;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,7 +115,7 @@ class MultiRelay : public Usermod {
|
||||
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||
int value = getValue(p->value(), ',', i);
|
||||
if (value==-1) {
|
||||
error = F("There must be as much arugments as relays");
|
||||
error = F("There must be as many arguments as relays");
|
||||
} else {
|
||||
// Switch
|
||||
if (_relay[i].external) switchRelay(i, (bool)value);
|
||||
@@ -118,7 +128,7 @@ class MultiRelay : public Usermod {
|
||||
for (int i=0;i<MULTI_RELAY_MAX_RELAYS;i++) {
|
||||
int value = getValue(p->value(), ',', i);
|
||||
if (value==-1) {
|
||||
error = F("There must be as mutch arugments as relays");
|
||||
error = F("There must be as many arguments as relays");
|
||||
} else {
|
||||
// Toggle
|
||||
if (value && _relay[i].external) toggleRelay(i);
|
||||
@@ -199,7 +209,7 @@ class MultiRelay : public Usermod {
|
||||
_relay[relay].state = mode;
|
||||
pinMode(_relay[relay].pin, OUTPUT);
|
||||
digitalWrite(_relay[relay].pin, mode ? !_relay[relay].mode : _relay[relay].mode);
|
||||
publishMqtt(mode ? "on" : "off", relay);
|
||||
publishMqtt(relay);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,6 +262,50 @@ class MultiRelay : public Usermod {
|
||||
strcpy(subuf, mqttDeviceTopic);
|
||||
strcat_P(subuf, PSTR("/relay/#"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
if (HAautodiscovery) publishHomeAssistantAutodiscovery();
|
||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||
if (_relay[i].pin<0) continue;
|
||||
publishMqtt(i); //publish current state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void publishHomeAssistantAutodiscovery() {
|
||||
for (uint8_t i = 0; i < MULTI_RELAY_MAX_RELAYS; i++) {
|
||||
char uid[24], json_str[1024], buf[128];
|
||||
size_t payload_size;
|
||||
sprintf_P(uid, PSTR("%s_sw%d"), escapedMac.c_str(), i);
|
||||
|
||||
if (_relay[i].pin >= 0 && _relay[i].external) {
|
||||
StaticJsonDocument<1024> json;
|
||||
sprintf_P(buf, PSTR("%s Switch %d"), serverDescription, i); //max length: 33 + 8 + 3 = 44
|
||||
json[F("name")] = buf;
|
||||
|
||||
sprintf_P(buf, PSTR("%s/relay/%d"), mqttDeviceTopic, i); //max length: 33 + 7 + 3 = 43
|
||||
json["~"] = buf;
|
||||
strcat_P(buf, PSTR("/command"));
|
||||
mqtt->subscribe(buf, 0);
|
||||
|
||||
json[F("stat_t")] = "~";
|
||||
json[F("cmd_t")] = F("~/command");
|
||||
json[F("pl_off")] = F("off");
|
||||
json[F("pl_on")] = F("on");
|
||||
json[F("uniq_id")] = uid;
|
||||
|
||||
strcpy(buf, mqttDeviceTopic); //max length: 33 + 7 = 40
|
||||
strcat_P(buf, PSTR("/status"));
|
||||
json[F("avty_t")] = buf;
|
||||
json[F("pl_avail")] = F("online");
|
||||
json[F("pl_not_avail")] = F("offline");
|
||||
//TODO: dev
|
||||
payload_size = serializeJson(json, json_str);
|
||||
} else {
|
||||
//Unpublish disabled or internal relays
|
||||
json_str[0] = 0;
|
||||
payload_size = 0;
|
||||
}
|
||||
sprintf_P(buf, PSTR("homeassistant/switch/%s/config"), uid);
|
||||
mqtt->publish(buf, 0, true, json_str, payload_size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,7 +320,7 @@ class MultiRelay : public Usermod {
|
||||
if (!pinManager.allocatePin(_relay[i].pin,true, PinOwner::UM_MultiRelay)) {
|
||||
_relay[i].pin = -1; // allocation failed
|
||||
} else {
|
||||
if (!_relay[i].external) _relay[i].state = offMode;
|
||||
if (!_relay[i].external) _relay[i].state = !offMode;
|
||||
switchRelay(i, _relay[i].state);
|
||||
_relay[i].active = false;
|
||||
}
|
||||
@@ -313,13 +367,18 @@ class MultiRelay : public Usermod {
|
||||
*/
|
||||
bool handleButton(uint8_t b) {
|
||||
yield();
|
||||
if (buttonType[b] == BTN_TYPE_NONE || buttonType[b] == BTN_TYPE_RESERVED || buttonType[b] == BTN_TYPE_PIR_SENSOR || buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) {
|
||||
if (!enabled
|
||||
|| buttonType[b] == BTN_TYPE_NONE
|
||||
|| buttonType[b] == BTN_TYPE_RESERVED
|
||||
|| buttonType[b] == BTN_TYPE_PIR_SENSOR
|
||||
|| buttonType[b] == BTN_TYPE_ANALOG
|
||||
|| buttonType[b] == BTN_TYPE_ANALOG_INVERTED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool handled = false;
|
||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||
if (_relay[i].button == b) {
|
||||
if (_relay[i].button == b && _relay[i].external) {
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
@@ -355,6 +414,8 @@ class MultiRelay : public Usermod {
|
||||
buttonPressedBefore[b] = true;
|
||||
|
||||
if (now - buttonPressedTime[b] > 600) { //long press
|
||||
//longPressAction(b); //not exposed
|
||||
//handled = false; //use if you want to pass to default behaviour
|
||||
buttonLongPressed[b] = true;
|
||||
}
|
||||
|
||||
@@ -371,7 +432,8 @@ class MultiRelay : public Usermod {
|
||||
if (!buttonLongPressed[b]) { //short press
|
||||
// if this is second release within 350ms it is a double press (buttonWaitTime!=0)
|
||||
if (doublePress) {
|
||||
//doublePressAction(b);
|
||||
//doublePressAction(b); //not exposed
|
||||
//handled = false; //use if you want to pass to default behaviour
|
||||
} else {
|
||||
buttonWaitTime[b] = now;
|
||||
}
|
||||
@@ -379,9 +441,10 @@ class MultiRelay : public Usermod {
|
||||
buttonPressedBefore[b] = false;
|
||||
buttonLongPressed[b] = false;
|
||||
}
|
||||
// if 450ms elapsed since last press/release it is a short press
|
||||
// if 350ms elapsed since last press/release it is a short press
|
||||
if (buttonWaitTime[b] && now - buttonWaitTime[b] > 350 && !buttonPressedBefore[b]) {
|
||||
buttonWaitTime[b] = 0;
|
||||
//shortPressAction(b); //not exposed
|
||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||
if (_relay[i].pin>=0 && _relay[i].button == b) {
|
||||
toggleRelay(i);
|
||||
@@ -477,6 +540,7 @@ class MultiRelay : public Usermod {
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name));
|
||||
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[FPSTR(_broadcast)] = periodicBroadcastSec;
|
||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||
String parName = FPSTR(_relay_str); parName += '-'; parName += i;
|
||||
JsonObject relay = top.createNestedObject(parName);
|
||||
@@ -486,6 +550,7 @@ class MultiRelay : public Usermod {
|
||||
relay[FPSTR(_external)] = _relay[i].external;
|
||||
relay[FPSTR(_button)] = _relay[i].button;
|
||||
}
|
||||
top[FPSTR(_HAautodiscovery)] = HAautodiscovery;
|
||||
DEBUG_PRINTLN(F("MultiRelay config saved."));
|
||||
}
|
||||
|
||||
@@ -506,6 +571,9 @@ class MultiRelay : public Usermod {
|
||||
}
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
periodicBroadcastSec = top[FPSTR(_broadcast)] | periodicBroadcastSec;
|
||||
periodicBroadcastSec = min(900,max(0,(int)periodicBroadcastSec));
|
||||
HAautodiscovery = top[FPSTR(_HAautodiscovery)] | HAautodiscovery;
|
||||
|
||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||
String parName = FPSTR(_relay_str); parName += '-'; parName += i;
|
||||
@@ -539,7 +607,9 @@ class MultiRelay : public Usermod {
|
||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||
if (_relay[i].pin>=0 && pinManager.allocatePin(_relay[i].pin, true, PinOwner::UM_MultiRelay)) {
|
||||
if (!_relay[i].external) {
|
||||
switchRelay(i, offMode);
|
||||
_relay[i].state = !offMode;
|
||||
switchRelay(i, _relay[i].state);
|
||||
_oldMode = offMode;
|
||||
}
|
||||
} else {
|
||||
_relay[i].pin = -1;
|
||||
@@ -549,7 +619,7 @@ class MultiRelay : public Usermod {
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[F("relay-0")][FPSTR(_button)].isNull();
|
||||
return !top[FPSTR(_broadcast)].isNull();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -570,3 +640,5 @@ const char MultiRelay::_delay_str[] PROGMEM = "delay-s";
|
||||
const char MultiRelay::_activeHigh[] PROGMEM = "active-high";
|
||||
const char MultiRelay::_external[] PROGMEM = "external";
|
||||
const char MultiRelay::_button[] PROGMEM = "button";
|
||||
const char MultiRelay::_broadcast[] PROGMEM = "broadcast-sec";
|
||||
const char MultiRelay::_HAautodiscovery[] PROGMEM = "HA-autodiscovery";
|
||||
|
||||
@@ -61,10 +61,10 @@ class QuinLEDAnPentaUsermod : public Usermod
|
||||
float shtLastKnownHumidity = 0;
|
||||
|
||||
// Pin/IO vars
|
||||
const int8_t anPentaPins[5] = {14, 13, 12, 4, 2};
|
||||
const int8_t anPentaLEDPins[5] = {14, 13, 12, 4, 2};
|
||||
int8_t oledSpiClk = 15;
|
||||
int8_t oledSpiData = 16;
|
||||
int8_t oledSpiCs = 0;
|
||||
int8_t oledSpiCs = 27;
|
||||
int8_t oledSpiDc = 32;
|
||||
int8_t oledSpiRst = 33;
|
||||
int8_t shtSda = 1;
|
||||
@@ -75,7 +75,7 @@ class QuinLEDAnPentaUsermod : public Usermod
|
||||
{
|
||||
for(int8_t i = 0; i <= 4; i++)
|
||||
{
|
||||
if(anPentaPins[i] == pin)
|
||||
if(anPentaLEDPins[i] == pin)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -313,7 +313,7 @@ class QuinLEDAnPentaUsermod : public Usermod
|
||||
byte drawnLines = 0;
|
||||
for (int8_t app = 0; app <= 4; app++) {
|
||||
for (int8_t clp = 0; clp <= 4; clp++) {
|
||||
if (anPentaPins[app] == currentLedPins[clp]) {
|
||||
if (anPentaLEDPins[app] == currentLedPins[clp]) {
|
||||
char charCurrentLedcReads[17];
|
||||
sprintf(charCurrentLedcReads, "LED %d:", app+1);
|
||||
if (oledUseProgressBars) {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# QuinLED-An-Penta
|
||||
The (un)official usermod to get the best out of the QuinLED-An-Penta, like using the OLED and the SHT30 temperature/humidity sensor.
|
||||
The (un)official usermod to get the best out of the QuinLED-An-Penta (https://quinled.info/quinled-an-penta/), like using the OLED and the SHT30 temperature/humidity sensor.
|
||||
|
||||
## Requirements
|
||||
* "u8gs" by olikraus, v2.28 or higher: https://github.com/olikraus/u8g2
|
||||
* "SHT85" by Rob Tillaart, v0.2 or higher: https://github.com/RobTillaart/SHT85
|
||||
|
||||
## Usermod installation
|
||||
Simply copy the below block (build task) to your `platformio_override.ini` and compile WLED using this new build task. Or use an existing one and add the buildflag `-D QUINLED_AN_PENTA`.
|
||||
Simply copy the below block (build task) to your `platformio_override.ini` and compile WLED using this new build task. Or use an existing one, add the buildflag `-D QUINLED_AN_PENTA` and the below library dependencies.
|
||||
|
||||
ESP32 (**without** ethernet):
|
||||
```
|
||||
@@ -33,14 +33,19 @@ This mod has been optimized for an SSD1306 driven 128x64 OLED. Using a smaller O
|
||||
I highly recommend using these "two color monochromatic OLEDs", which have the first 16 pixels in a different color than the other 48, e.g. a yellow/blue OLED.
|
||||
Also note, you need to have an **SPI** driven OLED, **not i2c**!
|
||||
|
||||
### Limitations combined with Ethernet
|
||||
The initial development of this mod had been done with a beta version of the QuinLED-An-Penta, which had a different IO layout for the OLED: The CS pin used to be IO_0, but has been changed to IO27 with the first v1 public release. Unfortunately, IO27 is used by the Ethernet boards, so WLED will not let you enable the OLED screen, if you're using it with Ethernet. This unfortunately makes the development I've done to support/show Ethernet information void, as it cannot be used.
|
||||
However (and I've not tried this, as I don't own a v1 board): You can try to modify this mod and try to use IO27 for the OLED and share it with the Ethernet board. It is "just" the chip select pin, so there is a chance that both can coexist and use the same IO. You need to skip WLEDs PinManager for the CS pin, so WLED will not block using it. If you don't know how this works: Leave it. If you know what I'm talking about: Try it and please let me know on the Intermit.Tech (QuinLED) Discord server: https://discord.gg/WdbAauG
|
||||
|
||||
### My OLED flickers after some time, what should I do?
|
||||
That's a tricky one: During development I saw that the OLED sometimes starts to "bug out" / flicker and won't work anymore. This seems to be caused by the high PWM interference the board produces. It seems to loose it's settings and then doesn't know how to draw anymore. Turns out the only way to fix this is to call the libraries `begin()` method again which will re-initialize the display.
|
||||
That's a tricky one: During development I saw that the OLED sometimes starts to "bug out" / flicker and won't work anymore. This seems to be caused by the high PWM interference the board produces. It seems to loose its settings and then doesn't know how to draw anymore. Turns out the only way to fix this is to call the libraries `begin()` method again which will re-initialize the display.
|
||||
If you're facing this issue, you can enable a setting I've added which will call the `begin()` roughly every 60 seconds between a page change. This will make the page change take ~500ms, but will fix the display.
|
||||
|
||||
|
||||
## Configuration
|
||||
Navigate to the "Config" and then to the "Usermods" section. If you compiled WLED with `-D QUINLED_AN_PENTA`, you will see the config for it there:
|
||||
* Enable-OLED:
|
||||
* What it does: Enabled the optional SPI driven OLED that can be mounted to the 7-pin female header
|
||||
* What it does: Enables the optional SPI driven OLED that can be mounted to the 7-pin female header. Won't work with Ethernet, read above.
|
||||
* Possible values: Enabled/Disabled
|
||||
* Default: Disabled
|
||||
* OLED-Use-Progress-Bars:
|
||||
@@ -60,10 +65,15 @@ Navigate to the "Config" and then to the "Usermods" section. If you compiled WLE
|
||||
* Possible values: Enabled/Disabled
|
||||
* Default: Disabled
|
||||
* Enable-SHT30-Temp-Humidity-Sensor:
|
||||
* What it does: Enabled the onboard SHT30 temperature and humidity sensor
|
||||
* What it does: Enables the onboard SHT30 temperature and humidity sensor
|
||||
* Possible values: Enabled/Disabled
|
||||
* Default: Disabled
|
||||
|
||||
## Change log
|
||||
2021-12
|
||||
* Adjusted IO layout to match An-Penta v1r1
|
||||
2021-10
|
||||
* First implementation.
|
||||
* First implementation.
|
||||
|
||||
## Credits
|
||||
ezcGman | Andy: Find me on the Intermit.Tech (QuinLED) Discord server: https://discord.gg/WdbAauG
|
||||
@@ -1,37 +0,0 @@
|
||||
# QuinLED-Dig-Quad Preassembled Unofficial Build
|
||||
|
||||
This usermod targets the [Preassembled QuinLED-Dig-Quad](https://quinled.info/pre-assembled-quinled-dig-quad/). Tested on board revision v1r6b,
|
||||
and includes the following features:
|
||||
|
||||
* **Multi-channel Support** - enabling use of LED1, LED2, LED3, LED4 pins to work using segments
|
||||
* **Temperature Sensor Support** - pulls readings from the built-in temperature sensor and adds the reading to the *Info* page in the UI
|
||||
|
||||
## Background
|
||||
|
||||
As a starting point, you should check out this awesome video from Quindor: [How to compile WLED yourself](https://quinled.info/2020/12/22/livestream-wled-compile/). The usermod you are reading now just provides some shortcuts for parts of what were covered in that video.
|
||||
|
||||
## Build Firmware with Multi-channel and Temp Support
|
||||
|
||||
1. Copy the `platformio_override.ini` file to the project's root directory
|
||||
1. If using VS Code with the PlatformIO plugin like in the video, you will now see this new project task listed in the PLATFORMIO panel at the bottom as `env:QL-DigQuad-Pre-v0.1` (you probably need to hit the refresh button)
|
||||
|
||||
<img src="images/pio-screenshot.png" width="400px"/>
|
||||
|
||||
1. Edit this file from the root directory as needed:
|
||||
|
||||
<img src="images/params.png" width="400px"/>
|
||||
|
||||
* `PIXEL_COUNTS` may need to be adjusted for your set-up. E.g. I have lots of LEDs in Channel 1, but that's probably unusual for most
|
||||
* `DATA_PINS` may need to be changed to "16,3,1,26" instead of "16,1,3,26" apparently depending on the board revision or some such
|
||||
|
||||
1. Build the mod (e.g. click `Build` from the project task circled above) and update your firmware using the `QL-DigQuad-Pre-v0.1` file, e.g. using _Manual OTA_ from the Config menu. Based on the video and my own experience, you might need to build twice 🤷♂️.
|
||||
|
||||
## Observing Temperature
|
||||
|
||||
Hopefully you can now see the Temperature listed in the Info page. If not, use Chrome Developer Tools to find the current temperature
|
||||
|
||||
1. Open the Developer Tools Console
|
||||
2. Enter `lastinfo.u.Temperature` to view the Temperature array
|
||||
|
||||
<img src="images/json-temp.png" width="300px"/>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 296 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 69 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 321 KiB |
@@ -1,16 +0,0 @@
|
||||
; QuinLED-Dig-Quad Preassembled Unofficial
|
||||
|
||||
[env:QL-DigQuad-Pre-v0.1]
|
||||
extends = env:esp32dev
|
||||
build_flags = ${common.build_flags_esp32}
|
||||
-D ESP32_MULTISTRIP
|
||||
-D NUM_STRIPS=4
|
||||
-D PIXEL_COUNTS="600, 300, 300, 300"
|
||||
-D DATA_PINS="16,1,3,26"
|
||||
-D RLYPIN=19
|
||||
-D BTNPIN=17
|
||||
-D USERMOD_DALLASTEMPERATURE
|
||||
-D USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL=10000
|
||||
lib_deps = ${env.lib_deps}
|
||||
milesburton/DallasTemperature@^3.9.0
|
||||
OneWire@~2.3.5
|
||||
129
usermods/seven_segment_display_reloaded/readme.md
Normal file
129
usermods/seven_segment_display_reloaded/readme.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# Seven Segment Display Reloaded
|
||||
|
||||
Usermod that uses the overlay feature to create a configurable seven segment display.
|
||||
Optimized for maximum configurability and use with seven segment clocks by parallyze (https://www.instructables.com/member/parallyze/instructables/)
|
||||
Very loosely based on the existing usermod "seven segment display".
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
Add the compile-time option `-D USERMOD_SSDR` to your `platformio.ini` (or `platformio_override.ini`) or use `#define USERMOD_SSDR` in `my_config.h`.
|
||||
|
||||
For the auto brightness option, the usermod SN_Photoresistor has to be installed as well. See SN_Photoresistor/readme.md for instructions.
|
||||
|
||||
## Settings
|
||||
All settings can be controlled the usermod setting page.
|
||||
Part of the settings can be controlled through MQTT with a raw payload or through a json request to /json/state.
|
||||
|
||||
### enabled
|
||||
Enables/disables this overlay usermod
|
||||
|
||||
### inverted
|
||||
Enables the inverted mode in which the background should be enabled and the digits should be black (leds off)
|
||||
|
||||
### Colon-blinking
|
||||
Enables the blinking colon(s) if they are defined
|
||||
|
||||
### enable-auto-brightness
|
||||
Enables the auto brightness feature. Can be only used with the usermod SN_Photoresistor installed.
|
||||
|
||||
### auto-brightness-min / auto-brightness-max
|
||||
The lux value calculated from usermod SN_Photoresistor will be mapped to the values defined here.
|
||||
The mapping is 0 - 1000 lux will be mapped to auto-brightness-min - auto-brightness-max
|
||||
|
||||
The mA current protection of WLED will override the calculated value if it is too high.
|
||||
|
||||
### Display-Mask
|
||||
Defines the type of the time/date display.
|
||||
For example "H:m" (default)
|
||||
- H - 00-23 hours
|
||||
- h - 01-12 hours
|
||||
- k - 01-24 hours
|
||||
- m - 00-59 minutes
|
||||
- s - 00-59 seconds
|
||||
- d - 01-31 day of month
|
||||
- M - 01-12 month
|
||||
- y - 21 last two positions of year
|
||||
- Y - 2021 year
|
||||
- : for a colon
|
||||
|
||||
### LED-Numbers
|
||||
- LED-Numbers-Hours
|
||||
- LED-Numbers-Minutes
|
||||
- LED-Numbers-Seconds
|
||||
- LED-Numbers-Colons
|
||||
- LED-Numbers-Day
|
||||
- LED-Numbers-Month
|
||||
- LED-Numbers-Year
|
||||
|
||||
See following example for usage.
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
Example for Leds definition
|
||||
```
|
||||
< A >
|
||||
/\ /\
|
||||
F B
|
||||
\/ \/
|
||||
< G >
|
||||
/\ /\
|
||||
E C
|
||||
\/ \/
|
||||
< D >
|
||||
```
|
||||
|
||||
Leds or Range of Leds are seperated by a comma ","
|
||||
|
||||
Segments are seperated by a semicolon ";" and are read as A;B;C;D;E;F;G
|
||||
|
||||
Digits are seperated by colon ":" -> A;B;C;D;E;F;G:A;B;C;D;E;F;G
|
||||
|
||||
Ranges are defined as lower to higher (lower first)
|
||||
|
||||
For example, an clock definition for the following clock (https://www.instructables.com/Lazy-7-Quick-Build-Edition/) is
|
||||
|
||||
- hour "59,46;47-48;50-51;52-53;54-55;57-58;49,56:0,13;1-2;4-5;6-7;8-9;11-12;3,10"
|
||||
|
||||
- minute "37-38;39-40;42-43;44,31;32-33;35-36;34,41:21-22;23-24;26-27;28,15;16-17;19-20;18,25"
|
||||
|
||||
or
|
||||
|
||||
- hour "6,7;8,9;11,12;13,0;1,2;4,5;3,10:52,53;54,55;57,58;59,46;47,48;50,51;49,56"
|
||||
|
||||
- minute "15,28;16,17;19,20;21,22;23,24;26,27;18,25:31,44;32,33;35,36;37,38;39,40;42,43;34,41"
|
||||
|
||||
depending on the orientation.
|
||||
|
||||
# The example detailed:
|
||||
hour "59,46;47-48;50-51;52-53;54-55;57-58;49,56:0,13;1-2;4-5;6-7;8-9;11-12;3,10"
|
||||
|
||||
there are two digits seperated by ":"
|
||||
|
||||
- 59,46;47-48;50-51;52-53;54-55;57-58;49,56
|
||||
- 0,13;1-2;4-5;6-7;8-9;11-12;3,10
|
||||
|
||||
In the first digit,
|
||||
the **segment A** consists of the leds number **59 and 46**., **segment B** consists of the leds number **47, 48** and so on
|
||||
|
||||
The second digit starts again with **segment A** and leds **0 and 13**, **segment B** consists of the leds number **1 and 2** and so on
|
||||
|
||||
### first digit of the hour
|
||||
- Segment A: 59, 46
|
||||
- Segment B: 47, 48
|
||||
- Segment C: 50, 51
|
||||
- Segment D: 52, 53
|
||||
- Segment E: 54, 55
|
||||
- Segment F: 57, 58
|
||||
- Segment G: 49, 56
|
||||
|
||||
### second digit of the hour
|
||||
|
||||
- Segment A: 0, 13
|
||||
- Segment B: 1, 2
|
||||
- Segment C: 4, 5
|
||||
- Segment D: 6, 7
|
||||
- Segment E: 8, 9
|
||||
- Segment F: 11, 12
|
||||
- Segment G: 3, 10
|
||||
@@ -0,0 +1,555 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
class UsermodSSDR : public Usermod {
|
||||
|
||||
//#define REFRESHTIME 497
|
||||
|
||||
private:
|
||||
//Runtime variables.
|
||||
unsigned long umSSDRLastRefresh = 0;
|
||||
unsigned long umSSDRResfreshTime = 3000;
|
||||
bool umSSDRDisplayTime = false;
|
||||
bool umSSDRInverted = false;
|
||||
bool umSSDRColonblink = true;
|
||||
bool umSSDREnableLDR = false;
|
||||
String umSSDRHours = "";
|
||||
String umSSDRMinutes = "";
|
||||
String umSSDRSeconds = "";
|
||||
String umSSDRColons = "";
|
||||
String umSSDRDays = "";
|
||||
String umSSDRMonths = "";
|
||||
String umSSDRYears = "";
|
||||
uint16_t umSSDRLength = 0;
|
||||
uint16_t umSSDRBrightnessMin = 0;
|
||||
uint16_t umSSDRBrightnessMax = 255;
|
||||
|
||||
bool* umSSDRMask = 0;
|
||||
|
||||
/*// H - 00-23 hours
|
||||
// h - 01-12 hours
|
||||
// k - 01-24 hours
|
||||
// m - 00-59 minutes
|
||||
// s - 00-59 seconds
|
||||
// d - 01-31 day of month
|
||||
// M - 01-12 month
|
||||
// y - 21 last two positions of year
|
||||
// Y - 2021 year
|
||||
// : for a colon
|
||||
*/
|
||||
String umSSDRDisplayMask = "H:m"; //This should reflect physical equipment.
|
||||
|
||||
/* Segment order, seen from the front:
|
||||
|
||||
< A >
|
||||
/\ /\
|
||||
F B
|
||||
\/ \/
|
||||
< G >
|
||||
/\ /\
|
||||
E C
|
||||
\/ \/
|
||||
< D >
|
||||
|
||||
*/
|
||||
|
||||
uint8_t umSSDRNumbers[11][7] = {
|
||||
// A B C D E F G
|
||||
{ 1, 1, 1, 1, 1, 1, 0 }, // 0
|
||||
{ 0, 1, 1, 0, 0, 0, 0 }, // 1
|
||||
{ 1, 1, 0, 1, 1, 0, 1 }, // 2
|
||||
{ 1, 1, 1, 1, 0, 0, 1 }, // 3
|
||||
{ 0, 1, 1, 0, 0, 1, 1 }, // 4
|
||||
{ 1, 0, 1, 1, 0, 1, 1 }, // 5
|
||||
{ 1, 0, 1, 1, 1, 1, 1 }, // 6
|
||||
{ 1, 1, 1, 0, 0, 0, 0 }, // 7
|
||||
{ 1, 1, 1, 1, 1, 1, 1 }, // 8
|
||||
{ 1, 1, 1, 1, 0, 1, 1 }, // 9
|
||||
{ 0, 0, 0, 0, 0, 0, 0 } // blank
|
||||
};
|
||||
|
||||
//String to reduce flash memory usage
|
||||
static const char _str_name[];
|
||||
static const char _str_ldrEnabled[];
|
||||
static const char _str_timeEnabled[];
|
||||
static const char _str_inverted[];
|
||||
static const char _str_colonblink[];
|
||||
static const char _str_displayMask[];
|
||||
static const char _str_hours[];
|
||||
static const char _str_minutes[];
|
||||
static const char _str_seconds[];
|
||||
static const char _str_colons[];
|
||||
static const char _str_days[];
|
||||
static const char _str_months[];
|
||||
static const char _str_years[];
|
||||
static const char _str_minBrightness[];
|
||||
static const char _str_maxBrightness[];
|
||||
|
||||
#ifdef USERMOD_SN_PHOTORESISTOR
|
||||
Usermod_SN_Photoresistor *ptr;
|
||||
#else
|
||||
void* ptr = nullptr;
|
||||
#endif
|
||||
|
||||
void _overlaySevenSegmentDraw() {
|
||||
int displayMaskLen = static_cast<int>(umSSDRDisplayMask.length());
|
||||
bool colonsDone = false;
|
||||
_setAllFalse();
|
||||
for (int index = 0; index < displayMaskLen; index++) {
|
||||
int timeVar = 0;
|
||||
switch (umSSDRDisplayMask[index]) {
|
||||
case 'h':
|
||||
timeVar = hourFormat12(localTime);
|
||||
_showElements(&umSSDRHours, timeVar, 0, 1);
|
||||
break;
|
||||
case 'H':
|
||||
timeVar = hour(localTime);
|
||||
_showElements(&umSSDRHours, timeVar, 0, 1);
|
||||
break;
|
||||
case 'k':
|
||||
timeVar = hour(localTime) + 1;
|
||||
_showElements(&umSSDRHours, timeVar, 0, 0);
|
||||
break;
|
||||
case 'm':
|
||||
timeVar = minute(localTime);
|
||||
_showElements(&umSSDRMinutes, timeVar, 0, 0);
|
||||
break;
|
||||
case 's':
|
||||
timeVar = second(localTime);
|
||||
_showElements(&umSSDRSeconds, timeVar, 0, 0);
|
||||
break;
|
||||
case 'd':
|
||||
timeVar = day(localTime);
|
||||
_showElements(&umSSDRDays, timeVar, 0, 0);
|
||||
break;
|
||||
case 'M':
|
||||
timeVar = month(localTime);
|
||||
_showElements(&umSSDRMonths, timeVar, 0, 0);
|
||||
break;
|
||||
case 'y':
|
||||
timeVar = second(localTime);
|
||||
_showElements(&umSSDRYears, timeVar, 0, 0);
|
||||
break;
|
||||
case 'Y':
|
||||
timeVar = year(localTime);
|
||||
_showElements(&umSSDRYears, timeVar, 0, 0);
|
||||
break;
|
||||
case ':':
|
||||
if (!colonsDone) { // only call _setColons once as all colons are printed when the first colon is found
|
||||
_setColons();
|
||||
colonsDone = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
_setMaskToLeds();
|
||||
}
|
||||
|
||||
void _setColons() {
|
||||
if ( umSSDRColonblink ) {
|
||||
if ( second(localTime) % 2 == 0 ) {
|
||||
_showElements(&umSSDRColons, 0, 1, 0);
|
||||
}
|
||||
} else {
|
||||
_showElements(&umSSDRColons, 0, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void _showElements(String *map, int timevar, bool isColon, bool removeZero
|
||||
|
||||
) {
|
||||
if (!(*map).equals("") && !(*map) == NULL) {
|
||||
int length = String(timevar).length();
|
||||
bool addZero = false;
|
||||
if (length == 1) {
|
||||
length = 2;
|
||||
addZero = true;
|
||||
}
|
||||
int timeArr[length];
|
||||
if(addZero) {
|
||||
if(removeZero)
|
||||
{
|
||||
timeArr[1] = 10;
|
||||
timeArr[0] = timevar;
|
||||
}
|
||||
else
|
||||
{
|
||||
timeArr[1] = 0;
|
||||
timeArr[0] = timevar;
|
||||
}
|
||||
} else {
|
||||
int count = 0;
|
||||
while (timevar) {
|
||||
timeArr[count] = timevar%10;
|
||||
timevar /= 10;
|
||||
count++;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
int colonsLen = static_cast<int>((*map).length());
|
||||
int count = 0;
|
||||
int countSegments = 0;
|
||||
int countDigit = 0;
|
||||
bool range = false;
|
||||
int lastSeenLedNr = 0;
|
||||
|
||||
for (int index = 0; index < colonsLen; index++) {
|
||||
switch ((*map)[index]) {
|
||||
case '-':
|
||||
lastSeenLedNr = _checkForNumber(count, index, map);
|
||||
count = 0;
|
||||
range = true;
|
||||
break;
|
||||
case ':':
|
||||
_setLeds(_checkForNumber(count, index, map), lastSeenLedNr, range, countSegments, timeArr[countDigit], isColon);
|
||||
count = 0;
|
||||
range = false;
|
||||
countDigit++;
|
||||
countSegments = 0;
|
||||
break;
|
||||
case ';':
|
||||
_setLeds(_checkForNumber(count, index, map), lastSeenLedNr, range, countSegments, timeArr[countDigit], isColon);
|
||||
count = 0;
|
||||
range = false;
|
||||
countSegments++;
|
||||
break;
|
||||
case ',':
|
||||
_setLeds(_checkForNumber(count, index, map), lastSeenLedNr, range, countSegments, timeArr[countDigit], isColon);
|
||||
count = 0;
|
||||
range = false;
|
||||
break;
|
||||
default:
|
||||
count++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_setLeds(_checkForNumber(count, colonsLen, map), lastSeenLedNr, range, countSegments, timeArr[countDigit], isColon);
|
||||
}
|
||||
}
|
||||
|
||||
void _setLeds(int lednr, int lastSeenLedNr, bool range, int countSegments, int number, bool colon) {
|
||||
|
||||
if ((colon && umSSDRColonblink) || umSSDRNumbers[number][countSegments]) {
|
||||
|
||||
if (range) {
|
||||
for(int i = lastSeenLedNr; i <= lednr; i++) {
|
||||
umSSDRMask[i] = true;
|
||||
}
|
||||
} else {
|
||||
umSSDRMask[lednr] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _setMaskToLeds() {
|
||||
for(int i = 0; i <= umSSDRLength; i++) {
|
||||
if ((!umSSDRInverted && !umSSDRMask[i]) || (umSSDRInverted && umSSDRMask[i])) {
|
||||
strip.setPixelColor(i, 0x000000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _setAllFalse() {
|
||||
for(int i = 0; i <= umSSDRLength; i++) {
|
||||
umSSDRMask[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
int _checkForNumber(int count, int index, String *map) {
|
||||
String number = (*map).substring(index - count, index);
|
||||
return number.toInt();
|
||||
}
|
||||
|
||||
void _publishMQTTint_P(const char *subTopic, int value)
|
||||
{
|
||||
if(mqtt == NULL) return;
|
||||
|
||||
char buffer[64];
|
||||
char valBuffer[12];
|
||||
sprintf_P(buffer, PSTR("%s/%S/%S"), mqttDeviceTopic, _str_name, subTopic);
|
||||
sprintf_P(valBuffer, PSTR("%d"), value);
|
||||
mqtt->publish(buffer, 2, true, valBuffer);
|
||||
}
|
||||
|
||||
void _publishMQTTstr_P(const char *subTopic, String Value)
|
||||
{
|
||||
if(mqtt == NULL) return;
|
||||
char buffer[64];
|
||||
sprintf_P(buffer, PSTR("%s/%S/%S"), mqttDeviceTopic, _str_name, subTopic);
|
||||
mqtt->publish(buffer, 2, true, Value.c_str(), Value.length());
|
||||
}
|
||||
|
||||
bool _cmpIntSetting_P(char *topic, char *payload, const char *setting, void *value)
|
||||
{
|
||||
if (strcmp_P(topic, setting) == 0)
|
||||
{
|
||||
*((int *)value) = strtol(payload, NULL, 10);
|
||||
_publishMQTTint_P(setting, *((int *)value));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _handleSetting(char *topic, char *payload) {
|
||||
if (_cmpIntSetting_P(topic, payload, _str_timeEnabled, &umSSDRDisplayTime)) {
|
||||
return true;
|
||||
}
|
||||
if (_cmpIntSetting_P(topic, payload, _str_ldrEnabled, &umSSDREnableLDR)) {
|
||||
return true;
|
||||
}
|
||||
if (_cmpIntSetting_P(topic, payload, _str_inverted, &umSSDRInverted)) {
|
||||
return true;
|
||||
}
|
||||
if (_cmpIntSetting_P(topic, payload, _str_colonblink, &umSSDRColonblink)) {
|
||||
return true;
|
||||
}
|
||||
if (strcmp_P(topic, _str_displayMask) == 0) {
|
||||
umSSDRDisplayMask = String(payload);
|
||||
_publishMQTTstr_P(_str_displayMask, umSSDRDisplayMask);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void _updateMQTT()
|
||||
{
|
||||
_publishMQTTint_P(_str_timeEnabled, umSSDRDisplayTime);
|
||||
_publishMQTTint_P(_str_ldrEnabled, umSSDREnableLDR);
|
||||
_publishMQTTint_P(_str_inverted, umSSDRInverted);
|
||||
_publishMQTTint_P(_str_colonblink, umSSDRColonblink);
|
||||
|
||||
_publishMQTTstr_P(_str_hours, umSSDRHours);
|
||||
_publishMQTTstr_P(_str_minutes, umSSDRMinutes);
|
||||
_publishMQTTstr_P(_str_seconds, umSSDRSeconds);
|
||||
_publishMQTTstr_P(_str_colons, umSSDRColons);
|
||||
_publishMQTTstr_P(_str_days, umSSDRDays);
|
||||
_publishMQTTstr_P(_str_months, umSSDRMonths);
|
||||
_publishMQTTstr_P(_str_years, umSSDRYears);
|
||||
_publishMQTTstr_P(_str_displayMask, umSSDRDisplayMask);
|
||||
|
||||
_publishMQTTint_P(_str_minBrightness, umSSDRBrightnessMin);
|
||||
_publishMQTTint_P(_str_maxBrightness, umSSDRBrightnessMax);
|
||||
}
|
||||
|
||||
void _addJSONObject(JsonObject& root) {
|
||||
JsonObject ssdrObj = root[FPSTR(_str_name)];
|
||||
if (ssdrObj.isNull()) {
|
||||
ssdrObj = root.createNestedObject(FPSTR(_str_name));
|
||||
}
|
||||
|
||||
ssdrObj[FPSTR(_str_timeEnabled)] = umSSDRDisplayTime;
|
||||
ssdrObj[FPSTR(_str_ldrEnabled)] = umSSDREnableLDR;
|
||||
ssdrObj[FPSTR(_str_inverted)] = umSSDRInverted;
|
||||
ssdrObj[FPSTR(_str_colonblink)] = umSSDRColonblink;
|
||||
ssdrObj[FPSTR(_str_displayMask)] = umSSDRDisplayMask;
|
||||
ssdrObj[FPSTR(_str_hours)] = umSSDRHours;
|
||||
ssdrObj[FPSTR(_str_minutes)] = umSSDRMinutes;
|
||||
ssdrObj[FPSTR(_str_seconds)] = umSSDRSeconds;
|
||||
ssdrObj[FPSTR(_str_colons)] = umSSDRColons;
|
||||
ssdrObj[FPSTR(_str_days)] = umSSDRDays;
|
||||
ssdrObj[FPSTR(_str_months)] = umSSDRMonths;
|
||||
ssdrObj[FPSTR(_str_years)] = umSSDRYears;
|
||||
ssdrObj[FPSTR(_str_minBrightness)] = umSSDRBrightnessMin;
|
||||
ssdrObj[FPSTR(_str_maxBrightness)] = umSSDRBrightnessMax;
|
||||
}
|
||||
|
||||
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() {
|
||||
umSSDRLength = strip.getLengthTotal();
|
||||
if (umSSDRMask != 0) {
|
||||
umSSDRMask = (bool*) realloc(umSSDRMask, umSSDRLength * sizeof(bool));
|
||||
} else {
|
||||
umSSDRMask = (bool*) malloc(umSSDRLength * sizeof(bool));
|
||||
}
|
||||
_setAllFalse();
|
||||
|
||||
#ifdef USERMOD_SN_PHOTORESISTOR
|
||||
ptr = (Usermod_SN_Photoresistor*) usermods.lookup(USERMOD_ID_SN_PHOTORESISTOR);
|
||||
#endif
|
||||
DEBUG_PRINTLN(F("Setup done"));
|
||||
}
|
||||
|
||||
/*
|
||||
* loop() is called continuously. Here you can check for events, read sensors, etc.
|
||||
*/
|
||||
void loop() {
|
||||
if (!umSSDRDisplayTime || strip.isUpdating()) {
|
||||
return;
|
||||
}
|
||||
#ifdef USERMOD_ID_SN_PHOTORESISTOR
|
||||
if(bri != 0 && umSSDREnableLDR && (millis() - umSSDRLastRefresh > umSSDRResfreshTime)) {
|
||||
if (ptr != nullptr) {
|
||||
uint16_t lux = ptr->getLastLDRValue();
|
||||
uint16_t brightness = map(lux, 0, 1000, umSSDRBrightnessMin, umSSDRBrightnessMax);
|
||||
if (bri != brightness) {
|
||||
bri = brightness;
|
||||
stateUpdated(1);
|
||||
}
|
||||
}
|
||||
umSSDRLastRefresh = millis();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void handleOverlayDraw() {
|
||||
if (umSSDRDisplayTime) {
|
||||
_overlaySevenSegmentDraw();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
JsonObject user = root[F("u")];
|
||||
if (user.isNull()) {
|
||||
user = root.createNestedObject(F("u"));
|
||||
}
|
||||
JsonArray enabled = user.createNestedArray("Time enabled");
|
||||
enabled.add(umSSDRDisplayTime);
|
||||
JsonArray invert = user.createNestedArray("Time inverted");
|
||||
invert.add(umSSDRInverted);
|
||||
JsonArray blink = user.createNestedArray("Blinking colon");
|
||||
blink.add(umSSDRColonblink);
|
||||
JsonArray ldrEnable = user.createNestedArray("Auto Brightness enabled");
|
||||
ldrEnable.add(umSSDREnableLDR);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
void addToJsonState(JsonObject& root) {
|
||||
JsonObject user = root[F("u")];
|
||||
if (user.isNull()) {
|
||||
user = root.createNestedObject(F("u"));
|
||||
}
|
||||
_addJSONObject(user);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
JsonObject user = root[F("u")];
|
||||
if (!user.isNull()) {
|
||||
JsonObject ssdrObj = user[FPSTR(_str_name)];
|
||||
umSSDRDisplayTime = ssdrObj[FPSTR(_str_timeEnabled)] | umSSDRDisplayTime;
|
||||
umSSDREnableLDR = ssdrObj[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR;
|
||||
umSSDRInverted = ssdrObj[FPSTR(_str_inverted)] | umSSDRInverted;
|
||||
umSSDRColonblink = ssdrObj[FPSTR(_str_colonblink)] | umSSDRColonblink;
|
||||
umSSDRDisplayMask = ssdrObj[FPSTR(_str_displayMask)] | umSSDRDisplayMask;
|
||||
}
|
||||
}
|
||||
|
||||
void onMqttConnect(bool sessionPresent) {
|
||||
char subBuffer[48];
|
||||
if (mqttDeviceTopic[0] != 0)
|
||||
{
|
||||
_updateMQTT();
|
||||
//subscribe for sevenseg messages on the device topic
|
||||
sprintf_P(subBuffer, PSTR("%s/%S/+/set"), mqttDeviceTopic, _str_name);
|
||||
mqtt->subscribe(subBuffer, 2);
|
||||
}
|
||||
|
||||
if (mqttGroupTopic[0] != 0)
|
||||
{
|
||||
//subcribe for sevenseg messages on the group topic
|
||||
sprintf_P(subBuffer, PSTR("%s/%S/+/set"), mqttGroupTopic, _str_name);
|
||||
mqtt->subscribe(subBuffer, 2);
|
||||
}
|
||||
}
|
||||
|
||||
bool onMqttMessage(char *topic, char *payload) {
|
||||
//If topic beings iwth sevenSeg cut it off, otherwise not our message.
|
||||
size_t topicPrefixLen = strlen_P(PSTR("/wledSS/"));
|
||||
if (strncmp_P(topic, PSTR("/wledSS/"), topicPrefixLen) == 0) {
|
||||
topic += topicPrefixLen;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
//We only care if the topic ends with /set
|
||||
size_t topicLen = strlen(topic);
|
||||
if (topicLen > 4 &&
|
||||
topic[topicLen - 4] == '/' &&
|
||||
topic[topicLen - 3] == 's' &&
|
||||
topic[topicLen - 2] == 'e' &&
|
||||
topic[topicLen - 1] == 't')
|
||||
{
|
||||
//Trim /set and handle it
|
||||
topic[topicLen - 4] = '\0';
|
||||
_handleSetting(topic, payload);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void addToConfig(JsonObject &root) {
|
||||
_addJSONObject(root);
|
||||
}
|
||||
|
||||
bool readFromConfig(JsonObject &root) {
|
||||
JsonObject top = root[FPSTR(_str_name)];
|
||||
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(FPSTR(_str_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
umSSDRDisplayTime = (top[FPSTR(_str_timeEnabled)] | umSSDRDisplayTime);
|
||||
umSSDREnableLDR = (top[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR);
|
||||
umSSDRInverted = (top[FPSTR(_str_inverted)] | umSSDRInverted);
|
||||
umSSDRColonblink = (top[FPSTR(_str_colonblink)] | umSSDRColonblink);
|
||||
|
||||
umSSDRDisplayMask = top[FPSTR(_str_displayMask)] | umSSDRDisplayMask;
|
||||
umSSDRHours = top[FPSTR(_str_hours)] | umSSDRHours;
|
||||
umSSDRMinutes = top[FPSTR(_str_minutes)] | umSSDRMinutes;
|
||||
umSSDRSeconds = top[FPSTR(_str_seconds)] | umSSDRSeconds;
|
||||
umSSDRColons = top[FPSTR(_str_colons)] | umSSDRColons;
|
||||
umSSDRDays = top[FPSTR(_str_days)] | umSSDRDays;
|
||||
umSSDRMonths = top[FPSTR(_str_months)] | umSSDRMonths;
|
||||
umSSDRYears = top[FPSTR(_str_years)] | umSSDRYears;
|
||||
umSSDRBrightnessMin = top[FPSTR(_str_minBrightness)] | umSSDRBrightnessMin;
|
||||
umSSDRBrightnessMax = top[FPSTR(_str_maxBrightness)] | umSSDRBrightnessMax;
|
||||
|
||||
DEBUG_PRINT(FPSTR(_str_name));
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
* 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_SSDR;
|
||||
}
|
||||
};
|
||||
|
||||
const char UsermodSSDR::_str_name[] PROGMEM = "UsermodSSDR";
|
||||
const char UsermodSSDR::_str_timeEnabled[] PROGMEM = "enabled";
|
||||
const char UsermodSSDR::_str_inverted[] PROGMEM = "inverted";
|
||||
const char UsermodSSDR::_str_colonblink[] PROGMEM = "Colon-blinking";
|
||||
const char UsermodSSDR::_str_displayMask[] PROGMEM = "Display-Mask";
|
||||
const char UsermodSSDR::_str_hours[] PROGMEM = "LED-Numbers-Hours";
|
||||
const char UsermodSSDR::_str_minutes[] PROGMEM = "LED-Numbers-Minutes";
|
||||
const char UsermodSSDR::_str_seconds[] PROGMEM = "LED-Numbers-Seconds";
|
||||
const char UsermodSSDR::_str_colons[] PROGMEM = "LED-Numbers-Colons";
|
||||
const char UsermodSSDR::_str_days[] PROGMEM = "LED-Numbers-Day";
|
||||
const char UsermodSSDR::_str_months[] PROGMEM = "LED-Numbers-Month";
|
||||
const char UsermodSSDR::_str_years[] PROGMEM = "LED-Numbers-Year";
|
||||
const char UsermodSSDR::_str_ldrEnabled[] PROGMEM = "enable-auto-brightness";
|
||||
const char UsermodSSDR::_str_minBrightness[] PROGMEM = "auto-brightness-min";
|
||||
const char UsermodSSDR::_str_maxBrightness[] PROGMEM = "auto-brightness-max";
|
||||
@@ -1,35 +0,0 @@
|
||||
# SSD1306 128x32 OLED via I2C with u8g2
|
||||
This usermod allows to connect 128x32 Oled display to WLED controlled and show
|
||||
the next information:
|
||||
- Current SSID
|
||||
- IP address if obtained
|
||||
* in AP mode and turned off lightning AP password is shown
|
||||
- Current effect
|
||||
- Current palette
|
||||
- On/Off icon (sun/moon)
|
||||
|
||||
## Hardware
|
||||

|
||||
|
||||
## Requirements
|
||||
Functionality checked with:
|
||||
- commit 095429a7df4f9e2b34dd464f7bbfd068df6558eb
|
||||
- Wemos d1 mini
|
||||
- PlatformIO
|
||||
- Generic SSD1306 128x32 I2C OLED display from aliexpress
|
||||
|
||||
### Platformio
|
||||
Add `U8g2@~2.27.2` dependency to `lib_deps_external` under `[common]` section in `platformio.ini`:
|
||||
```ini
|
||||
# platformio.ini
|
||||
...
|
||||
[common]
|
||||
...
|
||||
lib_deps_external =
|
||||
...
|
||||
U8g2@~2.27.2
|
||||
...
|
||||
```
|
||||
|
||||
### Arduino IDE
|
||||
Install library `U8g2 by oliver` in `Tools | Include Library | Manage libraries` menu.
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 34 KiB |
@@ -1,175 +0,0 @@
|
||||
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
|
||||
|
||||
//The SCL and SDA pins are defined here.
|
||||
//Lolin32 boards use SCL=5 SDA=4
|
||||
#define U8X8_PIN_SCL 5
|
||||
#define U8X8_PIN_SDA 4
|
||||
|
||||
|
||||
// If display does not work or looks corrupted check the
|
||||
// constructor reference:
|
||||
// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp
|
||||
// or check the gallery:
|
||||
// https://github.com/olikraus/u8g2/wiki/gallery
|
||||
U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL,
|
||||
U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
|
||||
|
||||
// gets called once at boot. Do all initialization that doesn't depend on
|
||||
// network here
|
||||
void userSetup() {
|
||||
u8x8.begin();
|
||||
u8x8.setPowerSave(0);
|
||||
u8x8.setContrast(10); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
|
||||
u8x8.setFont(u8x8_font_chroma48medium8_r);
|
||||
u8x8.drawString(0, 0, "Loading...");
|
||||
}
|
||||
|
||||
// gets called every time WiFi is (re-)connected. Initialize own network
|
||||
// interfaces here
|
||||
void userConnected() {}
|
||||
|
||||
// needRedraw marks if redraw is required to prevent often redrawing.
|
||||
bool needRedraw = true;
|
||||
|
||||
// Next variables hold the previous known values to determine if redraw is
|
||||
// required.
|
||||
String knownSsid = "";
|
||||
IPAddress knownIp;
|
||||
uint8_t knownBrightness = 0;
|
||||
uint8_t knownMode = 0;
|
||||
uint8_t knownPalette = 0;
|
||||
|
||||
long lastUpdate = 0;
|
||||
long lastRedraw = 0;
|
||||
bool displayTurnedOff = false;
|
||||
// How often we are redrawing screen
|
||||
#define USER_LOOP_REFRESH_RATE_MS 5000
|
||||
|
||||
void userLoop() {
|
||||
|
||||
// Check if we time interval for redrawing passes.
|
||||
if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS) {
|
||||
return;
|
||||
}
|
||||
lastUpdate = millis();
|
||||
|
||||
// Turn off display after 3 minutes with no change.
|
||||
if(!displayTurnedOff && millis() - lastRedraw > 3*60*1000) {
|
||||
u8x8.setPowerSave(1);
|
||||
displayTurnedOff = true;
|
||||
}
|
||||
|
||||
// Check if values which are shown on display changed from the last time.
|
||||
if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) {
|
||||
needRedraw = true;
|
||||
} else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP())) {
|
||||
needRedraw = true;
|
||||
} else if (knownBrightness != bri) {
|
||||
needRedraw = true;
|
||||
} else if (knownMode != strip.getMode()) {
|
||||
needRedraw = true;
|
||||
} else if (knownPalette != strip.getSegment(0).palette) {
|
||||
needRedraw = true;
|
||||
}
|
||||
|
||||
if (!needRedraw) {
|
||||
return;
|
||||
}
|
||||
needRedraw = false;
|
||||
|
||||
if (displayTurnedOff)
|
||||
{
|
||||
u8x8.setPowerSave(0);
|
||||
displayTurnedOff = false;
|
||||
}
|
||||
lastRedraw = millis();
|
||||
|
||||
// Update last known values.
|
||||
#if defined(ESP8266)
|
||||
knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
|
||||
#else
|
||||
knownSsid = WiFi.SSID();
|
||||
#endif
|
||||
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
|
||||
knownBrightness = bri;
|
||||
knownMode = strip.getMode();
|
||||
knownPalette = strip.getSegment(0).palette;
|
||||
|
||||
u8x8.clear();
|
||||
u8x8.setFont(u8x8_font_chroma48medium8_r);
|
||||
|
||||
// First row with Wifi name
|
||||
u8x8.setCursor(1, 0);
|
||||
u8x8.print(knownSsid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0));
|
||||
// Print `~` char to indicate that SSID is longer, than owr dicplay
|
||||
if (knownSsid.length() > u8x8.getCols())
|
||||
u8x8.print("~");
|
||||
|
||||
// Second row with IP or Psssword
|
||||
u8x8.setCursor(1, 1);
|
||||
// Print password in AP mode and if led is OFF.
|
||||
if (apActive && bri == 0)
|
||||
u8x8.print(apPass);
|
||||
else
|
||||
u8x8.print(knownIp);
|
||||
|
||||
// Third row with mode name
|
||||
u8x8.setCursor(2, 2);
|
||||
uint8_t qComma = 0;
|
||||
bool insideQuotes = false;
|
||||
uint8_t printedChars = 0;
|
||||
char singleJsonSymbol;
|
||||
// Find the mode name in JSON
|
||||
for (size_t i = 0; i < strlen_P(JSON_mode_names); i++) {
|
||||
singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i);
|
||||
switch (singleJsonSymbol) {
|
||||
case '"':
|
||||
insideQuotes = !insideQuotes;
|
||||
break;
|
||||
case '[':
|
||||
case ']':
|
||||
break;
|
||||
case ',':
|
||||
qComma++;
|
||||
default:
|
||||
if (!insideQuotes || (qComma != knownMode))
|
||||
break;
|
||||
u8x8.print(singleJsonSymbol);
|
||||
printedChars++;
|
||||
}
|
||||
if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2))
|
||||
break;
|
||||
}
|
||||
// Fourth row with palette name
|
||||
u8x8.setCursor(2, 3);
|
||||
qComma = 0;
|
||||
insideQuotes = false;
|
||||
printedChars = 0;
|
||||
// Looking for palette name in JSON.
|
||||
for (size_t i = 0; i < strlen_P(JSON_palette_names); i++) {
|
||||
singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i);
|
||||
switch (singleJsonSymbol) {
|
||||
case '"':
|
||||
insideQuotes = !insideQuotes;
|
||||
break;
|
||||
case '[':
|
||||
case ']':
|
||||
break;
|
||||
case ',':
|
||||
qComma++;
|
||||
default:
|
||||
if (!insideQuotes || (qComma != knownPalette))
|
||||
break;
|
||||
u8x8.print(singleJsonSymbol);
|
||||
printedChars++;
|
||||
}
|
||||
if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2))
|
||||
break;
|
||||
}
|
||||
|
||||
u8x8.setFont(u8x8_font_open_iconic_embedded_1x1);
|
||||
u8x8.drawGlyph(0, 0, 80); // wifi icon
|
||||
u8x8.drawGlyph(0, 1, 68); // home icon
|
||||
u8x8.setFont(u8x8_font_open_iconic_weather_2x2);
|
||||
u8x8.drawGlyph(0, 2, 66 + (bri > 0 ? 3 : 0)); // sun/moon icon
|
||||
}
|
||||
@@ -111,7 +111,7 @@ class StairwayWipeUsermod : public Usermod {
|
||||
transitionDelayTemp = 4000; //fade out slowly
|
||||
#endif
|
||||
bri = 0;
|
||||
colorUpdated(CALL_MODE_NOTIFICATION);
|
||||
stateUpdated(CALL_MODE_NOTIFICATION);
|
||||
wipeState = 0;
|
||||
userVar0 = 0;
|
||||
previousUserVar0 = 0;
|
||||
|
||||
@@ -104,7 +104,7 @@ void turnOff()
|
||||
transitionDelayTemp = 4000; //fade out slowly
|
||||
#endif
|
||||
bri = 0;
|
||||
colorUpdated(CALL_MODE_NOTIFICATION);
|
||||
stateUpdated(CALL_MODE_NOTIFICATION);
|
||||
wipeState = 0;
|
||||
userVar0 = 0;
|
||||
previousUserVar0 = 0;
|
||||
|
||||
@@ -91,8 +91,8 @@ class AutoSaveUsermod : public Usermod {
|
||||
knownBrightness = bri;
|
||||
knownEffectSpeed = effectSpeed;
|
||||
knownEffectIntensity = effectIntensity;
|
||||
knownMode = strip.getMode();
|
||||
knownPalette = strip.getSegment(0).palette;
|
||||
knownMode = strip.getMainSegment().mode;
|
||||
knownPalette = strip.getMainSegment().palette;
|
||||
}
|
||||
|
||||
// gets called every time WiFi is (re-)connected. Initialize own network
|
||||
@@ -106,8 +106,8 @@ class AutoSaveUsermod : public Usermod {
|
||||
if (!autoSaveAfterSec || !enabled || strip.isUpdating() || currentPreset>0) return; // setting 0 as autosave seconds disables autosave
|
||||
|
||||
unsigned long now = millis();
|
||||
uint8_t currentMode = strip.getMode();
|
||||
uint8_t currentPalette = strip.getSegment(0).palette;
|
||||
uint8_t currentMode = strip.getMainSegment().mode;
|
||||
uint8_t currentPalette = strip.getMainSegment().palette;
|
||||
|
||||
unsigned long wouldAutoSaveAfter = now + autoSaveAfterSec*1000;
|
||||
if (knownBrightness != bri) {
|
||||
|
||||
@@ -31,9 +31,33 @@ This usermod requires the `U8g2` and `Wire` libraries. See the
|
||||
`platformio_override.ini.sample` found in the Rotary Encoder
|
||||
UI usermod folder for how to include these using `platformio_override.ini`.
|
||||
|
||||
## Configuration
|
||||
|
||||
* `enabled` - enable/disable usermod
|
||||
* `pin` - GPIO pins used for display; I2C displays use Clk & Data; SPI displays can use SCK, MOSI, CS, DC & RST
|
||||
* `type` - display type in numeric format
|
||||
* 1 = I2C SSD1306 128x32
|
||||
* 2 = I2C SH1106 128x32
|
||||
* 3 = I2C SSD1306 128x64 (4 double-height lines)
|
||||
* 4 = I2C SSD1305 128x32
|
||||
* 5 = I2C SSD1305 128x64 (4 double-height lines)
|
||||
* 6 = SPI SSD1306 128x32
|
||||
* 7 = SPI SSD1306 128x64 (4 double-height lines)
|
||||
* `contrast` - set display contrast (higher contrast may reduce display lifetime)
|
||||
* `refreshRateSec` - time in seconds for display refresh
|
||||
* `screenTimeOutSec` - screen saver time-out in seconds
|
||||
* `flip` - flip/rotate display 180°
|
||||
* `sleepMode` - enable/disable screen saver
|
||||
* `clockMode` - enable/disable clock display in screen saver mode
|
||||
* `i2c-freq-kHz` - I2C clock frequency in kHz (may help reduce dropped frames, range: 400-3400)
|
||||
|
||||
## Change Log
|
||||
|
||||
2021-02
|
||||
* First public release
|
||||
|
||||
2021-04
|
||||
* Adaptation for runtime configuration.
|
||||
* Adaptation for runtime configuration.
|
||||
|
||||
2021-11
|
||||
* Added configuration option description.
|
||||
@@ -25,6 +25,10 @@
|
||||
|
||||
//The SCL and SDA pins are defined here.
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#define HW_PIN_SCL 22
|
||||
#define HW_PIN_SDA 21
|
||||
#define HW_PIN_CLOCKSPI 18
|
||||
#define HW_PIN_DATASPI 23
|
||||
#ifndef FLD_PIN_SCL
|
||||
#define FLD_PIN_SCL 22
|
||||
#endif
|
||||
@@ -47,6 +51,10 @@
|
||||
#define FLD_PIN_RESET 26
|
||||
#endif
|
||||
#else
|
||||
#define HW_PIN_SCL 5
|
||||
#define HW_PIN_SDA 4
|
||||
#define HW_PIN_CLOCKSPI 14
|
||||
#define HW_PIN_DATASPI 13
|
||||
#ifndef FLD_PIN_SCL
|
||||
#define FLD_PIN_SCL 5
|
||||
#endif
|
||||
@@ -70,6 +78,14 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef FLD_TYPE
|
||||
#ifndef FLD_SPI_DEFAULT
|
||||
#define FLD_TYPE SSD1306
|
||||
#else
|
||||
#define FLD_TYPE SSD1306_SPI
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// When to time out to the clock or blank the screen
|
||||
// if SLEEP_MODE_ENABLED.
|
||||
#define SCREEN_TIMEOUT_MS 60*1000 // 1 min
|
||||
@@ -115,11 +131,11 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
#ifndef FLD_SPI_DEFAULT
|
||||
int8_t ioPin[5] = {FLD_PIN_SCL, FLD_PIN_SDA, -1, -1, -1}; // I2C pins: SCL, SDA
|
||||
uint32_t ioFrequency = 400000; // in Hz (minimum is 100000, baseline is 400000 and maximum should be 3400000)
|
||||
DisplayType type = SSD1306; // display type
|
||||
#else
|
||||
int8_t ioPin[5] = {FLD_PIN_CLOCKSPI, FLD_PIN_DATASPI, FLD_PIN_CS, FLD_PIN_DC, FLD_PIN_RESET}; // SPI pins: CLK, MOSI, CS, DC, RST
|
||||
DisplayType type = SSD1306_SPI; // display type
|
||||
uint32_t ioFrequency = 1000000; // in Hz (minimum is 500kHz, baseline is 1MHz and maximum should be 20MHz)
|
||||
#endif
|
||||
DisplayType type = FLD_TYPE; // display type
|
||||
bool flip = false; // flip display 180°
|
||||
uint8_t contrast = 10; // screen contrast
|
||||
uint8_t lineHeight = 1; // 1 row or 2 rows
|
||||
@@ -127,6 +143,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
uint32_t screenTimeout = SCREEN_TIMEOUT_MS; // in ms
|
||||
bool sleepMode = true; // allow screen sleep?
|
||||
bool clockMode = false; // display clock
|
||||
bool enabled = true;
|
||||
|
||||
// Next variables hold the previous known values to determine if redraw is
|
||||
// required.
|
||||
@@ -150,6 +167,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
static const char _contrast[];
|
||||
static const char _refreshRate[];
|
||||
static const char _screenTimeOut[];
|
||||
@@ -169,88 +187,72 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
// gets called once at boot. Do all initialization that doesn't depend on
|
||||
// network here
|
||||
void setup() {
|
||||
if (type == NONE) return;
|
||||
if (type == NONE || !enabled) return;
|
||||
|
||||
bool isHW;
|
||||
PinOwner po = PinOwner::UM_FourLineDisplay;
|
||||
if (type == SSD1306_SPI || type == SSD1306_SPI64) {
|
||||
PinManagerPinType pins[5] = { { ioPin[0], true }, { ioPin[1], true}, { ioPin[2], true }, { ioPin[3], true}, { ioPin[4], true }};
|
||||
if (!pinManager.allocateMultiplePins(pins, 5, PinOwner::UM_FourLineDisplay)) { type=NONE; return; }
|
||||
isHW = (ioPin[0]==HW_PIN_CLOCKSPI && ioPin[1]==HW_PIN_DATASPI);
|
||||
PinManagerPinType pins[5] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true }, { ioPin[3], true }, { ioPin[4], true }};
|
||||
if (!pinManager.allocateMultiplePins(pins, 5, po)) { type=NONE; return; }
|
||||
} else {
|
||||
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true} };
|
||||
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::UM_FourLineDisplay)) { type=NONE; return; }
|
||||
isHW = (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA);
|
||||
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } };
|
||||
if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
|
||||
if (!pinManager.allocateMultiplePins(pins, 2, po)) { type=NONE; return; }
|
||||
}
|
||||
|
||||
DEBUG_PRINTLN(F("Allocating display."));
|
||||
switch (type) {
|
||||
case SSD1306:
|
||||
#ifdef ESP8266
|
||||
if (!(ioPin[0]==5 && ioPin[1]==4))
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else
|
||||
#endif
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 1;
|
||||
break;
|
||||
case SH1106:
|
||||
#ifdef ESP8266
|
||||
if (!(ioPin[0]==5 && ioPin[1]==4))
|
||||
u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else
|
||||
#endif
|
||||
u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 2;
|
||||
break;
|
||||
case SSD1306_64:
|
||||
#ifdef ESP8266
|
||||
if (!(ioPin[0]==5 && ioPin[1]==4))
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else
|
||||
#endif
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 2;
|
||||
break;
|
||||
case SSD1305:
|
||||
#ifdef ESP8266
|
||||
if (!(ioPin[0]==5 && ioPin[1]==4))
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else
|
||||
#endif
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 1;
|
||||
break;
|
||||
case SSD1305_64:
|
||||
#ifdef ESP8266
|
||||
if (!(ioPin[0]==5 && ioPin[1]==4))
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else
|
||||
#endif
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 2;
|
||||
break;
|
||||
case SSD1306_SPI:
|
||||
if (!(ioPin[0]==FLD_PIN_CLOCKSPI && ioPin[1]==FLD_PIN_DATASPI)) // if not overridden these sould be HW accellerated
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
|
||||
else
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
|
||||
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
|
||||
lineHeight = 1;
|
||||
break;
|
||||
case SSD1306_SPI64:
|
||||
if (!(ioPin[0]==FLD_PIN_CLOCKSPI && ioPin[1]==FLD_PIN_DATASPI)) // if not overridden these sould be HW accellerated
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
|
||||
else
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
|
||||
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
|
||||
lineHeight = 2;
|
||||
break;
|
||||
default:
|
||||
u8x8 = nullptr;
|
||||
}
|
||||
|
||||
if (nullptr == u8x8) {
|
||||
DEBUG_PRINTLN(F("Display init failed."));
|
||||
for (byte i=0; i<5 && ioPin[i]>=0; i++) pinManager.deallocatePin(ioPin[i], PinOwner::UM_FourLineDisplay);
|
||||
pinManager.deallocateMultiplePins((const uint8_t*)ioPin, (type == SSD1306_SPI || type == SSD1306_SPI64) ? 5 : 2, po);
|
||||
type = NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
initDone = true;
|
||||
DEBUG_PRINTLN(F("Starting display."));
|
||||
if (!(type == SSD1306_SPI || type == SSD1306_SPI64)) u8x8->setBusClock(ioFrequency); // can be used for SPI too
|
||||
/*if (!(type == SSD1306_SPI || type == SSD1306_SPI64))*/ u8x8->setBusClock(ioFrequency); // can be used for SPI too
|
||||
u8x8->begin();
|
||||
setFlipMode(flip);
|
||||
setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
|
||||
@@ -266,7 +268,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
* Da loop.
|
||||
*/
|
||||
void loop() {
|
||||
if (millis() - lastUpdate < (clockMode?1000:refreshRate) || strip.isUpdating()) return;
|
||||
if (!enabled || millis() - lastUpdate < (clockMode?1000:refreshRate) || strip.isUpdating()) return;
|
||||
lastUpdate = millis();
|
||||
|
||||
redraw(false);
|
||||
@@ -276,40 +278,40 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
* Wrappers for screen drawing
|
||||
*/
|
||||
void setFlipMode(uint8_t mode) {
|
||||
if (type==NONE) return;
|
||||
if (type == NONE || !enabled) return;
|
||||
u8x8->setFlipMode(mode);
|
||||
}
|
||||
void setContrast(uint8_t contrast) {
|
||||
if (type==NONE) return;
|
||||
if (type == NONE || !enabled) return;
|
||||
u8x8->setContrast(contrast);
|
||||
}
|
||||
void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false) {
|
||||
if (type==NONE) return;
|
||||
if (type == NONE || !enabled) return;
|
||||
u8x8->setFont(u8x8_font_chroma48medium8_r);
|
||||
if (!ignoreLH && lineHeight==2) u8x8->draw1x2String(col, row, string);
|
||||
else u8x8->drawString(col, row, string);
|
||||
}
|
||||
void draw2x2String(uint8_t col, uint8_t row, const char *string) {
|
||||
if (type==NONE) return;
|
||||
if (type == NONE || !enabled) return;
|
||||
u8x8->setFont(u8x8_font_chroma48medium8_r);
|
||||
u8x8->draw2x2String(col, row, string);
|
||||
}
|
||||
void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH=false) {
|
||||
if (type==NONE) return;
|
||||
if (type == NONE || !enabled) return;
|
||||
u8x8->setFont(font);
|
||||
if (!ignoreLH && lineHeight==2) u8x8->draw1x2Glyph(col, row, glyph);
|
||||
else u8x8->drawGlyph(col, row, glyph);
|
||||
}
|
||||
uint8_t getCols() {
|
||||
if (type==NONE) return 0;
|
||||
if (type==NONE || !enabled) return 0;
|
||||
return u8x8->getCols();
|
||||
}
|
||||
void clear() {
|
||||
if (type==NONE) return;
|
||||
if (type == NONE || !enabled) return;
|
||||
u8x8->clear();
|
||||
}
|
||||
void setPowerSave(uint8_t save) {
|
||||
if (type==NONE) return;
|
||||
if (type == NONE || !enabled) return;
|
||||
u8x8->setPowerSave(save);
|
||||
}
|
||||
|
||||
@@ -327,7 +329,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
static bool showName = false;
|
||||
unsigned long now = millis();
|
||||
|
||||
if (type==NONE) return;
|
||||
if (type == NONE || !enabled) return;
|
||||
if (overlayUntil > 0) {
|
||||
if (now >= overlayUntil) {
|
||||
// Time to display the overlay has elapsed.
|
||||
@@ -347,8 +349,8 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
(knownBrightness != bri) ||
|
||||
(knownEffectSpeed != effectSpeed) ||
|
||||
(knownEffectIntensity != effectIntensity) ||
|
||||
(knownMode != strip.getMode()) ||
|
||||
(knownPalette != strip.getSegment(0).palette)) {
|
||||
(knownMode != strip.getMainSegment().mode) ||
|
||||
(knownPalette != strip.getMainSegment().palette)) {
|
||||
knownHour = 99; // force time update
|
||||
lastRedraw = now; // update lastRedraw marker
|
||||
} else if (sleepMode && !displayTurnedOff && ((now - lastRedraw)/1000)%5 == 0) {
|
||||
@@ -396,8 +398,8 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
|
||||
knownIp = apActive ? IPAddress(4, 3, 2, 1) : Network.localIP();
|
||||
knownBrightness = bri;
|
||||
knownMode = strip.getMode();
|
||||
knownPalette = strip.getSegment(0).palette;
|
||||
knownMode = strip.getMainSegment().mode;
|
||||
knownPalette = strip.getMainSegment().palette;
|
||||
knownEffectSpeed = effectSpeed;
|
||||
knownEffectIntensity = effectIntensity;
|
||||
|
||||
@@ -438,6 +440,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
|
||||
void drawLine(uint8_t line, Line4Type lineType) {
|
||||
char lineBuffer[LINE_BUFFER_SIZE];
|
||||
uint8_t printedChars;
|
||||
switch(lineType) {
|
||||
case FLD_LINE_BRIGHTNESS:
|
||||
sprintf_P(lineBuffer, PSTR("Brightness %3d"), bri);
|
||||
@@ -452,10 +455,16 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
drawString(2, line*lineHeight, lineBuffer);
|
||||
break;
|
||||
case FLD_LINE_MODE:
|
||||
showCurrentEffectOrPalette(knownMode, JSON_mode_names, line);
|
||||
printedChars = extractModeName(knownMode, JSON_mode_names, lineBuffer, LINE_BUFFER_SIZE-1);
|
||||
for (;printedChars < getCols()-2 && printedChars < LINE_BUFFER_SIZE-3; printedChars++) lineBuffer[printedChars]=' ';
|
||||
lineBuffer[printedChars] = 0;
|
||||
drawString(2, line*lineHeight, lineBuffer);
|
||||
break;
|
||||
case FLD_LINE_PALETTE:
|
||||
showCurrentEffectOrPalette(knownPalette, JSON_palette_names, line);
|
||||
printedChars = extractModeName(knownPalette, JSON_palette_names, lineBuffer, LINE_BUFFER_SIZE-1);
|
||||
for (;printedChars < getCols()-2 && printedChars < LINE_BUFFER_SIZE-3; printedChars++) lineBuffer[printedChars]=' ';
|
||||
lineBuffer[printedChars] = 0;
|
||||
drawString(2, line*lineHeight, lineBuffer);
|
||||
break;
|
||||
case FLD_LINE_TIME:
|
||||
default:
|
||||
@@ -464,41 +473,6 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the current effect or palette (desiredEntry)
|
||||
* on the appropriate line (row).
|
||||
*/
|
||||
void showCurrentEffectOrPalette(int knownMode, const char *qstring, uint8_t row) {
|
||||
char lineBuffer[LINE_BUFFER_SIZE];
|
||||
uint8_t qComma = 0;
|
||||
bool insideQuotes = false;
|
||||
uint8_t printedChars = 0;
|
||||
char singleJsonSymbol;
|
||||
|
||||
// Find the mode name in JSON
|
||||
for (size_t i = 0; i < strlen_P(qstring); i++) {
|
||||
singleJsonSymbol = pgm_read_byte_near(qstring + i);
|
||||
if (singleJsonSymbol == '\0') break;
|
||||
switch (singleJsonSymbol) {
|
||||
case '"':
|
||||
insideQuotes = !insideQuotes;
|
||||
break;
|
||||
case '[':
|
||||
case ']':
|
||||
break;
|
||||
case ',':
|
||||
qComma++;
|
||||
default:
|
||||
if (!insideQuotes || (qComma != knownMode)) break;
|
||||
lineBuffer[printedChars++] = singleJsonSymbol;
|
||||
}
|
||||
if ((qComma > knownMode) || (printedChars >= getCols()-2) || printedChars >= sizeof(lineBuffer)-2) break;
|
||||
}
|
||||
for (;printedChars < getCols()-2 && printedChars < sizeof(lineBuffer)-2; printedChars++) lineBuffer[printedChars]=' ';
|
||||
lineBuffer[printedChars] = 0;
|
||||
drawString(2, row*lineHeight, lineBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* If there screen is off or in clock is displayed,
|
||||
* this will return true. This allows us to throw away
|
||||
@@ -506,6 +480,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
* to wake up the screen.
|
||||
*/
|
||||
bool wakeDisplay() {
|
||||
if (type == NONE || !enabled) return false;
|
||||
knownHour = 99;
|
||||
if (displayTurnedOff) {
|
||||
// Turn the display back on
|
||||
@@ -522,6 +497,8 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
* Clears the screen and prints on the middle two lines.
|
||||
*/
|
||||
void overlay(const char* line1, const char *line2, long showHowLong) {
|
||||
if (type == NONE || !enabled) return;
|
||||
|
||||
if (displayTurnedOff) {
|
||||
// Turn the display back on (includes clear())
|
||||
sleepOrClock(false);
|
||||
@@ -583,6 +560,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
* the useAMPM configuration.
|
||||
*/
|
||||
void showTime(bool fullScreen = true) {
|
||||
if (type == NONE || !enabled) return;
|
||||
char lineBuffer[LINE_BUFFER_SIZE];
|
||||
|
||||
updateLocalTime();
|
||||
@@ -676,10 +654,12 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
*/
|
||||
void addToConfig(JsonObject& root) {
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name));
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
JsonArray io_pin = top.createNestedArray("pin");
|
||||
for (byte i=0; i<5; i++) io_pin.add(ioPin[i]);
|
||||
top["help4PinTypes"] = F("Clk,Data,CS,DC,RST"); // help for Settings page
|
||||
top["help4Pins"] = F("Clk,Data,CS,DC,RST"); // help for Settings page
|
||||
top["type"] = type;
|
||||
top["help4Type"] = F("1=SSD1306,2=SH1106,3=SSD1306_128x64,4=SSD1305,5=SSD1305_128x64,6=SSD1306_SPI,7=SSD1306_SPI_128x64"); // help for Settings page
|
||||
top[FPSTR(_flip)] = (bool) flip;
|
||||
top[FPSTR(_contrast)] = contrast;
|
||||
top[FPSTR(_refreshRate)] = refreshRate/1000;
|
||||
@@ -710,6 +690,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
return false;
|
||||
}
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
newType = top["type"] | newType;
|
||||
for (byte i=0; i<5; i++) newPin[i] = top["pin"][i] | ioPin[i];
|
||||
flip = top[FPSTR(_flip)] | flip;
|
||||
@@ -718,7 +699,10 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
screenTimeout = (top[FPSTR(_screenTimeOut)] | screenTimeout/1000) * 1000;
|
||||
sleepMode = top[FPSTR(_sleepMode)] | sleepMode;
|
||||
clockMode = top[FPSTR(_clockMode)] | clockMode;
|
||||
ioFrequency = min(3400, max(100, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
|
||||
if (newType == SSD1306_SPI || newType == SSD1306_SPI64)
|
||||
ioFrequency = min(20000, max(500, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
|
||||
else
|
||||
ioFrequency = min(3400, max(100, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
@@ -733,10 +717,10 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
for (byte i=0; i<5; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; }
|
||||
if (pinsChanged || type!=newType) {
|
||||
if (type != NONE) delete u8x8;
|
||||
for (byte i=0; i<5; i++) {
|
||||
if (ioPin[i]>=0) pinManager.deallocatePin(ioPin[i], PinOwner::UM_FourLineDisplay);
|
||||
ioPin[i] = newPin[i];
|
||||
}
|
||||
PinOwner po = PinOwner::UM_FourLineDisplay;
|
||||
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, (type == SSD1306_SPI || type == SSD1306_SPI64) ? 5 : 2, po);
|
||||
for (byte i=0; i<5; i++) ioPin[i] = newPin[i];
|
||||
if (ioPin[0]<0 || ioPin[1]<0) { // data & clock must be > -1
|
||||
type = NONE;
|
||||
return true;
|
||||
@@ -750,7 +734,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
if (needsRedraw && !wakeDisplay()) redraw(true);
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !(top[_busClkFrequency]).isNull();
|
||||
return !top[FPSTR(_enabled)].isNull();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -764,6 +748,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char FourLineDisplayUsermod::_name[] PROGMEM = "4LineDisplay";
|
||||
const char FourLineDisplayUsermod::_enabled[] PROGMEM = "enabled";
|
||||
const char FourLineDisplayUsermod::_contrast[] PROGMEM = "contrast";
|
||||
const char FourLineDisplayUsermod::_refreshRate[] PROGMEM = "refreshRateSec";
|
||||
const char FourLineDisplayUsermod::_screenTimeOut[] PROGMEM = "screenTimeOutSec";
|
||||
|
||||
477
usermods/usermod_v2_four_line_display_ALT/4LD_wled_fonts.c
Normal file
477
usermods/usermod_v2_four_line_display_ALT/4LD_wled_fonts.c
Normal file
@@ -0,0 +1,477 @@
|
||||
#pragma once
|
||||
|
||||
//WLED custom fonts, curtesy of @Benji (https://github.com/Proto-molecule)
|
||||
|
||||
|
||||
/*
|
||||
Fontname: wled_logo_akemi_4x4
|
||||
Copyright: Benji (https://github.com/proto-molecule)
|
||||
Glyphs: 3/3
|
||||
BBX Build Mode: 3
|
||||
* this logo ...WLED/images/wled_logo_akemi.png
|
||||
* encode map = 1, 2, 3
|
||||
*/
|
||||
const uint8_t u8x8_wled_logo_akemi_4x4[388] U8X8_FONT_SECTION("u8x8_wled_logo_akemi_4x4") =
|
||||
"\1\3\4\4\0\0\0\0\0\0\0\0\0\340\360\10\350\10\350\210\270\210\350\210\270\350\10\360\340\0\0\0"
|
||||
"\0\0\200\200\0\0@\340\300\340@\0\0\377\377\377\377\377\377\37\37\207\207\371\371\371\377\377\377\0\0\374"
|
||||
"\374\7\7\371\0\0\6\4\15\34x\340\200\177\177\377\351yy\376\356\357\217\177\177\177o\377\377\0\70\77"
|
||||
"\277\376~\71\0\0\0\0\0\0\0\1\3\3\3\1\0\0\37\77\353\365\77\37\0\0\0\0\5\7\2\3"
|
||||
"\7\4\0\0\300\300\300\300\200\200\200\0\0\0\0\0\0\0\200\200\300\300\300\300\200\200\0\0\0\0\0\0"
|
||||
"\0\200\200\300\371\37\37\371\371\7\7\377\374\0\0\0\374\377\377\37\37\341\341\377\377\377\377\374\0\0\0\374"
|
||||
"\377\7\7\231\371\376>\371\371>~\377\277\70\0\270\377\177\77\376\376\71\371\371\71\177\377\277\70\0\70\377"
|
||||
"\177>\376\371\377\377\0\77\77\0\0\4\7\2\7\5\0\0\0\377\377\0\77\77\0\0\0\5\7\2\7\5"
|
||||
"\0\0\377\377\300\300\300\200\200\0\0\0\0\0\0\0\200\200\300\300\300\300\300\200\200\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\231\231\231\371\377\377\374\0\0\0\374\377\347\347\371\1\1\371\371\7\7\377\374\0\0\0@\340"
|
||||
"\300\340@\0\71\371\371\71\177\377\277\70\0\70\277\377\177\71\371\370\70\371\371~\376\377\77\70\200\340x\34"
|
||||
"\15\4\6\0\0\77\77\0\0\0\5\7\2\7\5\0\0\0\377\377\0\77\77\0\0\1\3\3\1\1\0\0"
|
||||
"\0\0\0";
|
||||
|
||||
|
||||
/*
|
||||
Fontname: wled_logo_akemi_5x5
|
||||
Copyright: Benji (https://github.com/proto-molecule)
|
||||
Glyphs: 3/3
|
||||
BBX Build Mode: 3
|
||||
* this logo ...WLED/images/wled_logo_akemi.png
|
||||
* encoded = 1, 2, 3
|
||||
*/
|
||||
/*
|
||||
const uint8_t u8x8_wled_logo_akemi_5x5[604] U8X8_FONT_SECTION("u8x8_wled_logo_akemi_5x5") =
|
||||
"\1\3\5\5\0\0\0\0\0\0\0\0\0\0\0\0\340\340\374\14\354\14\354\14|\14\354\14||\14\354"
|
||||
"\14\374\340\340\0\0\0\0\0\0\0\200\0\0\0\200\200\0\200\200\0\0\0\0\377\377\377\376\377\376\377\377"
|
||||
"\377\377\77\77\307\307\307\307\306\377\377\377\0\0\0\360\374>\77\307\0\0\61cg\357\347\303\301\200\0\0"
|
||||
"\377\377\377\317\317\317\317\360\360\360\374\374\377\377\377\377\377\377\377\377\0\0\200\377\377\340\340\37\0\0\0\0"
|
||||
"\0\0\1\3\17\77\374\360\357\357\177\36\14\17\357\377\376\376>\376\360\357\17\17\14>\177o\340\300\343c"
|
||||
"{\77\17\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\17\37\37\362\375\37\37\17\0\0"
|
||||
"\0\0\1\1\1\0\1\1\1\0\0\0\200\300\300\300\300\200\200\0\0\0\0\0\0\0\0\0\0\0\200\200"
|
||||
"\300\300\300\300\200\200\0\0\0\0\0\0\0\0\0\0\0\0\200\200\307\307\377\377\307\307\307\77>\374\360\0"
|
||||
"\0\0\360\374\376\377\377\377\7\7\7\377\377\377\377\376\374\360\0\0\0\0\360\374\36\37\37\343\37\37\340\340"
|
||||
"\37\37\37\340\340\377\377\200\0\200\377\377\377\340\340\340\37\37\37\37\37\37\37\377\377\377\200\0\0\200\377\377"
|
||||
"\340\340\340\34\377\377\3\3\377\377\3\17\77{\343\303\300\303\343s\77\37\3\377\377\3\3\377\377\3\17\77"
|
||||
"{\343\303\300\300\343{\37\17\3\377\377\377\377\0\0\37\37\0\0\1\1\1\1\0\1\1\1\1\0\0\377"
|
||||
"\377\0\0\37\37\0\0\1\1\1\1\0\0\1\1\1\0\0\377\377\300\300\300\200\200\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\200\200\300\300\300\300\200\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\343\343\343\343"
|
||||
"\343\377\376\374\360\0\0\0\360\374\376\77\77\307\307\7\7\307\307\307\77>\374\360\0\0\0\0\0\200\200\0"
|
||||
"\200\200\0\0\34\34\34\37\37\377\377\377\377\200\0\200\377\377\377\377\37\37\37\0\0\37\37\37\340\340\377\377"
|
||||
"\200\0\0\0\1\303\347\357gc\61\0\3\3\377\377\3\7\37\177s\343\300\303s{\37\17\7\3\377\377"
|
||||
"\3\3\377\377\3\37\77scp<\36\17\3\1\0\0\0\0\0\0\0\37\37\0\0\0\1\1\1\0\1"
|
||||
"\1\1\0\0\0\0\377\377\0\0\37\37\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
||||
*/
|
||||
|
||||
/*
|
||||
Fontname: wled_logo_2x2
|
||||
Copyright: Benji (https://github.com/proto-molecule)
|
||||
Glyphs: 4/4
|
||||
BBX Build Mode: 3
|
||||
* this logo https://cdn.discordapp.com/attachments/706623245935444088/927361780613799956/wled_scaled.png
|
||||
* encode map = 1, 2, 3, 4
|
||||
*/
|
||||
const uint8_t u8x8_wled_logo_2x2[133] U8X8_FONT_SECTION("u8x8_wled_logo_2x2") =
|
||||
"\1\4\2\2\0\0\0\0\0\200\200\360\360\16\16\16\16\0\0\0\340\340\340\340\340\37\37\1\1\0\0\0"
|
||||
"\0\0\0\0\360\360\16\16\16\200\200\16\16\16\360\360\0\0\0\200\37\37\340\340\340\37\37\340\340\340\37\37"
|
||||
"\0\0\0\37\200~~\0\0\0\0\0\0\0\360\360\216\216\216\216\37\340\340\340\340\340\340\340\0\0\37\37"
|
||||
"\343\343\343\343\16\16\0\0ppp\16\16\376\376\16\16\16\360\360\340\340\0\0\0\0\0\340\340\377\377\340"
|
||||
"\340\340\37\37";
|
||||
|
||||
|
||||
/*
|
||||
Fontname: wled_logo_4x4
|
||||
Copyright: Created with Fony 1.4.7
|
||||
Glyphs: 4/4
|
||||
BBX Build Mode: 3
|
||||
* this logo https://cdn.discordapp.com/attachments/706623245935444088/927361780613799956/wled_scaled.png
|
||||
* encode map = 1, 2, 3, 4
|
||||
*/
|
||||
/*
|
||||
const uint8_t u8x8_wled_logo_4x4[517] U8X8_FONT_SECTION("u8x8_wled_logo_4x4") =
|
||||
"\1\4\4\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\374\374\374\374\374\374\374\374\374"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\300\300\300\300\300\377\377\377\377\377\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\17\17\17\17\17\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\370\370\370\370\370\370\370\370\370\7\7\7\7\7\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\374\374\374\374\374\0\0\0\0\0\374\374\374\374\374\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\377\377\377\377\377\0\0\0\0\0\300\300\300\300\300\0\0\0\0\0\377\377\377\377\377\0\0"
|
||||
"\0\0\300\300\0\377\377\377\377\377\0\0\0\0\0\377\377\377\377\377\0\0\0\0\0\377\377\377\377\377\0\0"
|
||||
"\0\0\377\377\0\7\7\7\7\7\370\370\370\370\370\7\7\7\7\7\370\370\370\370\370\7\7\7\7\7\0\0"
|
||||
"\0\0\7\7\0\0\0\374\374\374\374\374\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\374\374\374"
|
||||
"\374\374\374\374\300\300\300\77\77\77\77\77\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\300\300\300"
|
||||
"\300\300\300\300\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\37\37\37"
|
||||
"\37\37\37\37\7\7\7\370\370\370\370\370\370\370\370\370\370\370\370\370\0\0\0\0\7\7\7\7\7\370\370\370"
|
||||
"\370\370\370\370\374\374\374\374\374\374\0\0\0\0\0\0\0\0\374\374\374\374\374\374\374\374\374\374\374\374\374\374"
|
||||
"\0\0\0\0\300\300\0\0\0\0\0\0\0\77\77\77\77\77\0\0\0\0\377\377\377\377\377\0\0\0\0\377"
|
||||
"\377\377\377\377\37\37\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\0\0\0\0\377"
|
||||
"\377\377\377\377\370\370\370\370\370\370\0\0\0\0\0\0\0\0\370\370\370\370\377\377\377\377\377\370\370\370\370\377"
|
||||
"\7\7\7\7";
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Fontname: 4LineDisplay_WLED_icons_1x
|
||||
Copyright: Benji (https://github.com/proto-molecule)
|
||||
Glyphs: 13/13
|
||||
BBX Build Mode: 3
|
||||
* 1 = sun
|
||||
* 2 = skip forward
|
||||
* 3 = fire
|
||||
* 4 = custom palette
|
||||
* 5 = puzzle piece
|
||||
* 6 = moon
|
||||
* 7 = brush
|
||||
* 8 = contrast
|
||||
* 9 = power-standby
|
||||
* 10 = star
|
||||
* 11 = heart
|
||||
* 12 = Akemi
|
||||
*-----------
|
||||
* 20 = wifi
|
||||
* 21 = media-play
|
||||
*/
|
||||
const uint8_t u8x8_4LineDisplay_WLED_icons_1x1[172] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_1x1") =
|
||||
"\1\25\1\1\0B\30<<\30B\0~<\30\0~<\30\0p\374\77\216\340\370\360\0<n\372\377"
|
||||
"\275\277\26\34\374\374\77\77\374\374\60\60<~~\360\340``\0\200\340\360p\14\16\6\1<~\377\377"
|
||||
"\201\201B<\70D\200\217\200D\70\0\0\10x<<x\10\0\14\36>||>\36\14\64 \336\67"
|
||||
";\336 \64\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\2\1\11\311"
|
||||
"\311\1\2\0\0~<<\30\30\0";
|
||||
|
||||
|
||||
/*
|
||||
Fontname: 4LineDisplay_WLED_icons_2x1
|
||||
Copyright: Benji (https://github.com/proto-molecule)
|
||||
Glyphs: 11/11
|
||||
BBX Build Mode: 3
|
||||
* 1 = sun
|
||||
* 2 = skip forward
|
||||
* 3 = fire
|
||||
* 4 = custom palette
|
||||
* 5 = puzzle piece
|
||||
* 6 = moon
|
||||
* 7 = brush
|
||||
* 8 = contrast
|
||||
* 9 = power-standby
|
||||
* 10 = star
|
||||
* 11 = heart
|
||||
* 12 = Akemi
|
||||
*/
|
||||
const uint8_t u8x8_4LineDisplay_WLED_icons_2x1[196] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_2x1") =
|
||||
"\1\14\2\1\20\20BB\30\30<\275\275<\30\30BB\20\20\377~<<\70\30\20\0\377~<<"
|
||||
"\70\30\20\0\60p\370\374\77>\236\214\300\340\370\360\360\340\0\0\34<v\326\336\375\375\377\277\275=>"
|
||||
"\66\66<\34\374\374\374\374~\77\77~\374\374\374\374 pp \30<~~\377\370\360\360\340\340\340\340"
|
||||
"@@ \0\200\300\340\360\360p`\10\34\34\16\6\6\3\0\0\70|~\376\376\377\377\377\201\201\203\202"
|
||||
"\302Fl\70\70xL\204\200\200\217\217\200\200\204Lx\70\0\0\10\10\30\330x|\77\77|x\330\30"
|
||||
"\10\10\0\0\14\36\37\77\77\177~\374\374~\177\77\77\37\36\14\24\64 \60>\26\367\33\375\36>\60"
|
||||
" \64\24";
|
||||
|
||||
|
||||
/*
|
||||
Fontname: 4LineDisplay_WLED_icons_2x
|
||||
Copyright:
|
||||
Glyphs: 11/11
|
||||
BBX Build Mode: 3
|
||||
* 1 = sun
|
||||
* 2 = skip forward
|
||||
* 3 = fire
|
||||
* 4 = custom palette
|
||||
* 5 = puzzle piece
|
||||
* 6 = moon
|
||||
* 7 = brush
|
||||
* 8 = contrast
|
||||
* 9 = power-standby
|
||||
* 10 = star
|
||||
* 11 = heart
|
||||
* 12 = Akemi
|
||||
*/
|
||||
const uint8_t u8x8_4LineDisplay_WLED_icons_2x2[389] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_2x2") =
|
||||
"\1\14\2\2\200\200\14\14\300\340\360\363\363\360\340\300\14\14\200\200\1\1\60\60\3\7\17\317\317\17\7\3"
|
||||
"\60\60\1\1\374\370\360\340\340\300\200\0\374\370\360\340\340\300\200\0\77\37\17\7\7\3\1\0\77\37\17\7"
|
||||
"\7\3\1\0\0\200\340\360\377\376\374\360\0\0\300\200\0\0\0\0\17\77\177\377\17\7\301\340\370\374\377\377"
|
||||
"\377|\0\0\360\370\234\236\376\363\363\377\377\363\363\376><\370\360\3\17\77yy\377\377\377\377\317\17\17"
|
||||
"\17\17\7\3\360\360\360\360\366\377\377\366\360\360\360\360\0\0\0\0\377\377\377\377\237\17\17\237\377\377\377\377"
|
||||
"\6\17\17\6\340\370\374\376\377\340\200\0\0\0\0\0\0\0\0\0\3\17\37\77\177\177\177\377\376|||"
|
||||
"\70\30\14\0\0\0\0\0\0\0\0``\360\370|<\36\7\2\0\300\360\376\377\177\77\36\0\1\1\0"
|
||||
"\0\0\0\0\340\370\374\376\376\377\377\377\3\3\7\6\16<\370\340\7\37\77\177\177\377\377\377\300\300\340`"
|
||||
"p<\37\7\300\340p\30\0\0\377\377\0\0\30p\340\300\0\0\17\37\70`\340\300\300\300\300\340`\70"
|
||||
"\37\17\0\0\0@\300\300\300\300\340\374\374\340\300\300\300\300@\0\0\0\0\1s\77\37\17\17\37\77s"
|
||||
"\1\0\0\0\360\370\374\374\374\374\370\360\360\370\374\374\374\374\370\360\0\1\3\7\17\37\77\177\177\77\37\17"
|
||||
"\7\3\1\0\200\200\0\0\0\360\370\374<\334\330\360\0\0\200\200\2\2\14\30\24\37\6~\7\177\7\37"
|
||||
"\24\30\16\2";
|
||||
|
||||
/*
|
||||
Fontname: 4LineDisplay_WLED_icons_3x
|
||||
Copyright: Benji (https://github.com/proto-molecule)
|
||||
Glyphs: 11/11
|
||||
BBX Build Mode: 3
|
||||
* 1 = sun
|
||||
* 2 = skip forward
|
||||
* 3 = fire
|
||||
* 4 = custom palette
|
||||
* 5 = puzzle piece
|
||||
* 6 = moon
|
||||
* 7 = brush
|
||||
* 8 = contrast
|
||||
* 9 = power-standby
|
||||
* 10 = star
|
||||
* 11 = heart
|
||||
* 12 = Akemi
|
||||
*/
|
||||
const uint8_t u8x8_4LineDisplay_WLED_icons_3x3[868] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_3x3") =
|
||||
"\1\14\3\3\0\0\34\34\34\0\200\300\300\340\347\347\347\340\300\300\200\0\34\34\34\0\0\0\34\34\34\0"
|
||||
"\0>\377\377\377\377\377\377\377\377\377\377\377>\0\0\34\34\34\0\0\0\16\16\16\0\0\1\1\3ss"
|
||||
"s\3\1\1\0\0\34\34\34\0\0\0\370\360\340\300\300\200\0\0\0\0\0\0\370\360\340\300\300\200\0\0"
|
||||
"\0\0\0\0\377\377\377\377\377\377\377\376~<\70\20\377\377\377\377\377\377\377\376~<\70\20\37\17\17\7"
|
||||
"\3\1\1\0\0\0\0\0\37\17\17\7\3\1\1\0\0\0\0\0\0\0\0\0\0\300\361\376\374\370\360\300"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\300\370\374\376\377\377\377\377\377\177\77\17\6\0\200\342\374\370\360\340"
|
||||
"\200\0\0\0\1\17\37\77\177\377\7\3\0\200\360\370\374\376\377\377\377\377\377\377\77\0\0\0\0\200\340\360"
|
||||
"\370\370\374\316\206\206\317\377\377\377\317\206\206\316\374\374\370\360\340\200<\377\377\371\360py\377\377\377\377\377"
|
||||
"\377\377\377\377\377\377\363\341\341\363\377\177\0\1\7\17\34\70x|\377\377\377\377\367\363c\3\3\3\3\1"
|
||||
"\1\1\0\0\300\300\300\300\300\300\300\316\377\377\377\316\300\300\300\300\300\300\0\0\0\0\0\0\377\377\377\377"
|
||||
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\300\300\340\340\340\300\377\377\377\377\377\377\377\307\3\3\3\307"
|
||||
"\377\377\377\377\377\377\1\1\3\3\3\1\0\300\340\370\374\374\376\377\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0>\377\377\377\377\377\377\377\377\374\360\340\300\300\200\200\0\0\0\0\0\0\200\200\0\1\7\17"
|
||||
"\37\37\77\177\177\177\177\377\377\377\177\177\177\77\77\37\17\7\3\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\200\200\300\340\340\360\370\374|>\17\6\0\0\0\0\0\340\340\360\360\360\342\303\7\17\37\77\37\7\3\1"
|
||||
"\0\0\0\0\0\200\340\360\377\377\377\377\177\77\37\17\0\0\0\0\0\0\0\0\0\0\0\0\0\200\340\360"
|
||||
"\370\374\374\376\376\376\377\377\7\7\7\6\16\16\34\70\360\340\300\0|\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\0\0\0\0\0\0\0\0\0\377\377\377\0\3\7\17\37\77\177\177\377\377\377\377\340\340\340\340pp\70<"
|
||||
"\37\17\3\0\0\0\200\300\340\340\300\0\0\377\377\377\0\0\300\340\340\300\200\0\0\0\0\0\370\376\377\17"
|
||||
"\3\0\0\0\0\17\17\17\0\0\0\0\0\3\17\377\376\370\0\0\0\7\17\37<xp\340\340\340\340\340"
|
||||
"\340\340\340px<\37\17\3\0\0\0\0\0\0\0\0\0\0\0\0\0\300\370\376\370\200\0\0\0\0\0"
|
||||
"\0\0\0\0\0\2\6\16\36\36>~\376\376\377\377\377\377\377\376\376~>\36\16\6\6\2\0\0\0\0"
|
||||
"\0\300x<\37\17\17\7\3\7\17\17\37<x\300\0\0\0\0\200\300\340\360\360\370\370\370\360\360\340\300"
|
||||
"\200\300\340\360\360\370\370\370\360\360\340\200\17\37\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\177\77\37\17\0\0\0\0\0\1\3\7\17\37\77\177\177\77\37\17\7\3\1\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\200\300\340\360\370\370\370p`\300\200\0\0\0\0\0\0&,f\300\0\0\300\377\377\357\357\357"
|
||||
"\363\370\377\377\377\377\300\0\0\306l&\0\0\0\1\7\16\14\6\7\1\177\177\0\177\177\1\7\6\14\16"
|
||||
"\7\1\0";
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Fontname: 4LineDisplay_WLED_icons_4x
|
||||
Copyright: Benji (https://github.com/proto-molecule)
|
||||
Glyphs: 11/11
|
||||
BBX Build Mode: 3
|
||||
* 1 = sun
|
||||
* 2 = skip forward
|
||||
* 3 = fire
|
||||
* 4 = custom palette
|
||||
* 5 = puzzle piece
|
||||
* 6 = moon
|
||||
* 7 = brush
|
||||
* 8 = contrast
|
||||
* 9 = power-standby
|
||||
* 10 = star
|
||||
* 11 = heart
|
||||
* 12 = Akemi
|
||||
*/
|
||||
/*
|
||||
const uint8_t u8x8_4LineDisplay_WLED_icons_4x4[1540] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_4x4") =
|
||||
"\1\14\4\4\0\0\0\0`\360\360`\0\0\0\0\0\0\6\17\17\6\0\0\0\0\0\0`\360\360`"
|
||||
"\0\0\0\0\200\300\300\200\0\0\0\0\340\370\374\376\376\377\377\377\377\377\377\376\376\374\370\340\0\0\0\0"
|
||||
"\200\300\300\200\1\3\3\1\0\0\0\0\7\37\77\177\177\377\377\377\377\377\377\177\177\77\37\7\0\0\0\0"
|
||||
"\1\3\3\1\0\0\0\0\6\17\17\6\0\0\0\0\0\0`\360\360`\0\0\0\0\0\0\6\17\17\6"
|
||||
"\0\0\0\0\360\340\300\200\200\0\0\0\0\0\0\0\0\0\0\0\360\340\300\200\200\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\377\377\377\377\377\377\376\374\374\370\360\340\340\300\200\0\377\377\377\377\377\377\376\374\374\370\360\340"
|
||||
"\340\300\200\0\377\377\377\377\377\377\177\77\77\37\17\7\7\3\1\0\377\377\377\377\377\377\177\77\77\37\17\7"
|
||||
"\7\3\1\0\17\7\3\1\1\0\0\0\0\0\0\0\0\0\0\0\17\7\3\1\1\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\200\341\376\374\370\360\340\300\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\200\300\360\370\374\376\377\377\377\377\377\377\377\377~\0\0\0\0\20\340\300\200\0\0\0\0"
|
||||
"\0\0\0\0~\377\377\377\377\377\377\377\377\177\77\37\17\3\1\0\200\300\340\370\376\377\377\377\377\376\374\340"
|
||||
"\0\0\0\0\0\3\7\17\37\77\177\207\1\0\0\0\340\370\374\377\377\377\377\377\377\377\377\377\377\177\77\17"
|
||||
"\0\0\0\0\0\0\0\200\300\360\370\370\374\34\16\16\16\36\377\377\377\377\37\16\16\16\36\374\374\370\370\360"
|
||||
"\340\300\0\0\340\374\377\377\217\7\7\7\217\377\376\376\376\377\377\377\377\377\377\376\376\376\377\377\217\7\7\7"
|
||||
"\217\377\377\374\17\177\377\377\377\37\17\17\17\37\377\377\377\377\377\377\377\377\377\377\377\377\177\177\177\177\77\77"
|
||||
"\77\37\17\7\0\0\0\3\7\17\36>>\177\177\377\377\377\377\377\377\371p\60\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0<\376\377\377\377\377\376<\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0"
|
||||
"\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377~~\377\377"
|
||||
"\377\377~<\377\377\377\377\377\377\377\377\303\1\0\0\0\0\1\303\377\377\377\377\377\377\377\377\0\0\0\0"
|
||||
"\0\0\0\0\0\0\200\340\360\370\374\374\376\376\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\370\377\377\377\377\377\377\377\377\377\376\360\300\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\7\77\377\377\377\377\377\377\377\377\377\377\377\377\377\376\374\370\370\360\360\360\340\340\340\340\340\340"
|
||||
"\340\340\60\0\0\0\0\1\3\7\17\37\37\77\77\77\177\177\177\177\177\177\177\177\77\77\77\37\37\17\7\3"
|
||||
"\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\200\300\340\340\360\370\374\374"
|
||||
"~\77\16\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\30\34>~\377\377\377\377\177\77\37\7\3\0"
|
||||
"\0\0\0\0\0\0\0\0\0\360\374\376\377\377\377\377\377\376\374\370\0\0\0\3\3\1\0\0\0\0\0\0"
|
||||
"\0\0\0\0@@\340\370\374\377\377\377\177\177\177\77\37\17\7\1\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\200\300\340\360\370\374\374\376\376\376\377\377\377\377\17\17\17\37\36\36>|\374\370\360\340"
|
||||
"\300\200\0\0\360\376\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\1\3\37"
|
||||
"\377\377\376\360\17\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\200\300\370"
|
||||
"\377\377\177\17\0\0\1\3\7\17\37\77\77\177\177\177\377\377\377\377\360\360\360\370xx|>\77\37\17\7"
|
||||
"\3\1\0\0\0\0\0\0\0\200\300\200\0\0\0\0\377\377\377\377\0\0\0\0\200\300\200\0\0\0\0\0"
|
||||
"\0\0\0\0\300\360\374\376\177\37\7\3\3\0\0\0\377\377\377\377\0\0\0\3\3\7\37\177\376\374\360\300"
|
||||
"\0\0\0\0\77\377\377\377\340\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\340\377\377\377\77"
|
||||
"\0\0\0\0\0\0\3\7\17\37><|x\370\360\360\360\360\360\360\370x|<>\37\17\7\3\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\340\374\374\340\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\20\60p\360\360\360\360\360\360\360\360\370\377\377\377\377\377\377\370\360\360\360\360\360\360\360\360"
|
||||
"p\60\20\0\0\0\0\0\0\0\1\3\7\317\377\377\377\377\377\377\377\377\377\377\377\377\317\7\3\1\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0p>\37\17\17\7\3\1\0\0\1\3\7\17\17\37>p\0\0\0"
|
||||
"\0\0\0\0\0\200\300\340\340\360\360\360\360\360\360\340\340\300\200\0\0\200\300\340\340\360\360\360\360\360\360\340"
|
||||
"\340\300\200\0~\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\376\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\377\377\377~\0\1\3\7\17\37\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\37\17"
|
||||
"\7\3\1\0\0\0\0\0\0\0\0\0\0\1\3\7\17\37\77\177\177\77\37\17\7\3\1\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\300\340\340\360\360\360\360\340\340\300\200\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0@\340\300\340@\0\0\0\376\377\377\177\177\177\237\207\347\371\371\371\377\376\0\0\0\0@"
|
||||
"\340\300\340@\2\4\4\35x\340\200\0\30\237\377\177\36\376\376\37\37\377\377\37\177\377\237\30\0\200\340x"
|
||||
"\34\5\4\2\0\0\0\0\0\1\3\3\3\1\0\0\0\17\17\0\0\17\17\0\0\0\1\3\3\3\1\0"
|
||||
"\0\0\0";
|
||||
*/
|
||||
|
||||
/*
|
||||
Fontname: 4LineDisplay_WLED_icons_6x
|
||||
Copyright: Benji (https://github.com/proto-molecule)
|
||||
Glyphs: 11/11
|
||||
BBX Build Mode: 3
|
||||
* 1 = sun
|
||||
* 2 = skip forward
|
||||
* 3 = fire
|
||||
* 4 = custom palette
|
||||
* 5 = puzzle piece
|
||||
* 6 = moon
|
||||
* 7 = brush
|
||||
* 8 = contrast
|
||||
* 9 = power-standby
|
||||
* 10 = star
|
||||
* 11 = heart
|
||||
* 12 = Akemi
|
||||
*/
|
||||
// you can replace this (wasteful) font by using 3x3 variant with draw2x2Glyph()
|
||||
const uint8_t u8x8_4LineDisplay_WLED_icons_6x6[3460] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_6x6") =
|
||||
"\1\14\6\6\0\0\0\0\0\0\200\300\300\300\300\200\0\0\0\0\0\0\0\0\0\36\77\77\77\77\36\0"
|
||||
"\0\0\0\0\0\0\0\0\200\300\300\300\300\200\0\0\0\0\0\0\0\0\0\0\0\0\7\17\17\17\17\7"
|
||||
"\0\0\0\0\200\300\340\340\340\360\360\360\360\360\360\340\340\340\300\200\0\0\0\0\7\17\17\17\17\7\0\0"
|
||||
"\0\0\0\0\300\340\340\340\340\300\0\0\0\0\0\0\340\374\376\377\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\377\377\377\377\377\376\374\340\0\0\0\0\0\0\300\340\340\340\340\300\3\7\7\7\7\3\0\0\0\0\0\0"
|
||||
"\7\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\7\0\0\0\0\0\0\3\7"
|
||||
"\7\7\7\3\0\0\0\0\0\0\340\360\360\360\360\340\0\0\0\0\1\3\7\7\7\17\17\17\17\17\17\7"
|
||||
"\7\7\3\1\0\0\0\0\340\360\360\360\360\340\0\0\0\0\0\0\0\0\0\0\0\0\1\3\3\3\3\1"
|
||||
"\0\0\0\0\0\0\0\0\0x\374\374\374\374x\0\0\0\0\0\0\0\0\0\1\3\3\3\3\1\0\0"
|
||||
"\0\0\0\0\300\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\200\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\376\376\374\370\360\360\340\300\200"
|
||||
"\200\0\0\0\0\0\0\0\0\0\0\0\377\377\377\376\376\374\370\360\360\340\300\200\200\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\374\374\370\360\340\340\300\200\0\377\377\377\377"
|
||||
"\377\377\377\377\377\377\377\377\377\377\376\374\374\370\360\340\340\300\200\0\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\377\377\177\77\77\37\17\7\7\3\1\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\77\37\17\7"
|
||||
"\7\3\1\0\377\377\377\177\177\77\37\17\17\7\3\1\1\0\0\0\0\0\0\0\0\0\0\0\377\377\377\177"
|
||||
"\177\77\37\17\17\7\3\1\1\0\0\0\0\0\0\0\0\0\0\0\3\1\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\3\1\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\1\376\374\374\370\360\340\300\200\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\200\340\360\374"
|
||||
"\377\377\377\377\377\377\377\377\377\376\370\300\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\300\340\360\374\376\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\37\0\0\0\0"
|
||||
"\0\0\4\370\360\360\340\300\200\0\0\0\0\0\0\0\0\0\0\0\370\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\377\377\377\377\377\177\77\37\7\3\0\0\0\0\0\200\300\360\374\377\377\377\377\377\377\377\376\370\340\0\0\0"
|
||||
"\0\0\0\0\3\37\177\377\377\377\377\377\377\377\377\377\77\17\7\1\0\0\0\0\0\200\300\360\370\374\376\377"
|
||||
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\1\3\7\17\37\77\77\177\200"
|
||||
"\0\0\0\0\0\0\340\374\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\17\1\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\200\300\340\340\360\360\370|<>>>~\377\377\377\377\377\377\377\177"
|
||||
"\77\36\36\36\36<|\370\370\360\360\340\340\200\0\0\0\0\0\0\0\0\300\360\374\376\377\377\377\377\377\377"
|
||||
"\377\360\340\300\300\300\300\340\360\377\377\377\377\377\377\370\360\340\340\340\340\360\370\377\377\377\377\377\377\377\377\377"
|
||||
"\374\360\340\200\360\377\377\377\377\377\207\3\1\1\1\1\3\207\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\377\377\377\377\377\377\377\207\3\1\1\1\1\3\207\377\377\377\377\377\17\377\377\377\377\377\377\377\376~>>"
|
||||
"\77\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\376\376\376\377\377\377"
|
||||
"\177\77\37\7\0\0\3\17\77\177\377\377\360\340\300\300\300\300\340\360\377\377\377\377\377\377\377\377\377\377\77\17"
|
||||
"\17\7\7\7\7\7\7\7\7\7\3\3\3\3\1\0\0\0\0\0\0\0\0\0\0\0\0\1\3\7\17\37"
|
||||
"\37\77\77\177\177\177\377\377\377\377\377\377\377\377\377~\30\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\370\374\376\377\377\377\377\377\377\376\374\360\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\360\360\360\360\360\360\360\360\360\360\360\360"
|
||||
"\360\363\377\377\377\377\377\377\377\377\363\360\360\360\360\360\360\360\360\360\360\360\360\360\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\374\374\376\376\377\377\377\377"
|
||||
"\377\376\374\360\377\377\377\377\377\377\377\377\377\377\377\377\177\77\37\17\17\17\17\17\17\37\77\177\377\377\377\377"
|
||||
"\377\377\377\377\377\377\377\377\3\3\7\7\17\17\17\17\7\7\3\0\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\360\300\0\0\0\0\0\0\0\0\300\360\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\200\300\340\360\360\370\374\374\376\376\7\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\300\360\374\376\377\377\377\377\377\377\377"
|
||||
"\377\377\377\340\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\374\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\374\360\300\200\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\17\177\377\377\377\377\377\377\377\377\377\377"
|
||||
"\377\377\377\377\377\377\377\377\377\377\376\374\370\360\360\340\340\300\300\300\200\200\200\200\0\0\0\0\0\0\200\200"
|
||||
"\200\200\0\0\0\0\1\7\37\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\37\7\1\0\0\0\0\0\0\0\0\0\0\1\3\3\7"
|
||||
"\17\17\37\37\37\77\77\77\77\177\177\177\177\177\177\77\77\77\77\37\37\37\17\17\7\3\3\1\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\200\200\300\340\360\360\370\374\374\376\377~\34\10\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\200\300\300\340\360\360\370\374\376\376\377\377\377\377\377\377\177\77\17\7\3"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\6\17\17\37\77\177\377"
|
||||
"\377\377\377\377\377\377\77\37\7\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\370\374\376"
|
||||
"\376\377\377\377\377\377\377\376\376\374\370\340\0\0\0\0\3\17\7\3\1\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\200\360\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\17\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`px\374\376\377\377\377\377\377\377"
|
||||
"\177\177\177\77\77\37\17\7\3\1\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\200\300\340\360\360\370\374\374\374\376\376\376\377\377\377\377\377\77\77\77\77"
|
||||
"\177~~\376\374\374\374\370\360\360\340\300\200\0\0\0\0\0\0\0\0\0\340\360\374\376\377\377\377\377\377\377"
|
||||
"\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\1\1\3\7\17\37\177\377\377\376\374"
|
||||
"\360\340\0\0\370\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\1\17\377\377\377\377\377\370\37\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\360\377\377"
|
||||
"\377\377\377\37\0\0\7\17\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0"
|
||||
"\0\0\0\0\0\200\200\300\340\360\370\376\377\377\177\77\17\7\0\0\0\0\0\0\0\0\0\1\3\7\17\17"
|
||||
"\37\77\77\77\177\177\177\377\377\377\377\377\374\374\374\374\376~~\177\77\77\77\37\17\17\7\3\1\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\377\377\377\377\377\377\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\200\300\340\360\370\374\376\376|"
|
||||
"x \0\0\0\0\377\377\377\377\377\377\0\0\0\0 x|\376\376\374\370\360\340\300\200\0\0\0\0\0"
|
||||
"\0\0\0\0\300\370\376\377\377\377\177\17\7\1\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0"
|
||||
"\0\0\0\0\1\7\37\177\377\377\377\376\370\200\0\0\0\0\0\0\177\377\377\377\377\377\200\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\200\377\377\377\377\377\177\0\0"
|
||||
"\0\0\0\0\0\7\37\177\377\377\377\374\370\340\300\200\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\200\200\300\340\370\374\377\377\377\177\37\7\0\0\0\0\0\0\0\0\0\0\0\0\1\3\7\17\37\37\77"
|
||||
"\77\177~~~\374\374\374\374\374\374\374\374~~~\177\77\77\37\37\17\7\3\1\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\340\374\374\340\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\300\370\377\377\377\377\377\377\370\300\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\4\14\34<<|\374\374\374\374\374\374\374\374\374\374\374\376\377\377\377\377\377\377\377\377\377"
|
||||
"\377\376\374\374\374\374\374\374\374\374\374\374\374|<<\34\14\4\0\0\0\0\0\0\0\0\0\1\3\3\7"
|
||||
"\17\37\77\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\77\37\17\7\3\3\1\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\370\377\377\377\377\377\377\177\77\37\17\17\37\77\177"
|
||||
"\377\377\377\377\377\377\370\300\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0p>"
|
||||
"\37\17\17\7\3\1\0\0\0\0\0\0\0\0\0\0\0\0\1\3\7\17\17\37>p\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\200\200\200\300\300\300\300\300\300\200\200\200\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\200\200\200\300\300\300\300\300\300\200\200\200\0\0\0\0\0\0\200\360\370\374\376\377\377\377\377\377\377\377"
|
||||
"\377\377\377\377\377\377\377\376\374\370\360\200\200\360\370\374\376\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376"
|
||||
"\374\370\360\200\37\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\37\0\0\1\3\7\17\37\77\177\377\377\377"
|
||||
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\37\17\7"
|
||||
"\3\1\0\0\0\0\0\0\0\0\0\0\0\0\1\3\7\17\37\77\177\377\377\377\377\377\377\377\377\377\377\377"
|
||||
"\377\377\377\177\77\37\17\7\3\1\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\1\3\7\17\37\77\77\37\17\7\3\1\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\300\300\300\300\300\300\300"
|
||||
"\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\340\370\370\376\376\377\377\377\377\377\377\377\377\77\77\77>\376\370\370\340\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0 p\360\340\360p \0\0\0\0\0\0\377\377\377\377\177\177\177\177\177\207\207\340\340\377"
|
||||
"\377\377\377\377\377\377\377\0\0\0\0\0 p\360\340\360p \0\6\4\14\14\15|x\360\200\200\0\0"
|
||||
"pp\177\177\377\377\374|\374\374\374\177\177\177\377\377\377\177\377\377\377\377\177pp\0\0\200\200\360x}"
|
||||
"\14\14\4\6\0\0\0\0\0\0\0\3\37\37|ppp\34\34\37\3\3\0\377\377\377\0\0\0\377\377"
|
||||
"\377\0\3\3\37\37\34ppp~\37\37\3\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\7\7\7\0\0\0\7\7\7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0";
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Fontname: akemi_8x8
|
||||
Copyright: Benji (https://github.com/proto-molecule)
|
||||
Glyphs: 1/1
|
||||
BBX Build Mode: 3
|
||||
* 12 = Akemi
|
||||
*/
|
||||
/*
|
||||
const uint8_t u8x8_akemi_8x8[516] U8X8_FONT_SECTION("u8x8_akemi_8x8") =
|
||||
"\14\14\10\10\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"
|
||||
"\200\200\200\200\200\200\200\200\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\340\340\370\370\376\376\376\376"
|
||||
"\377\377\377\377\377\377\377\377\376\376\376\376\370\370\340\340\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\376\376\377\377\377\377\377\377\377\377"
|
||||
"\377\377\377\377\37\37\37\343\343\343\343\343\343\377\377\377\376\376\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\30\30~~\370\370~~\30\30\0\0\0\0\0\0\0\377\377\377\377\377\77\77\77\77\77"
|
||||
"\77\300\300\300\370\370\370\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\30\0f\0\200\0\0"
|
||||
"\0\0\0\0\6\6\30\30\30\31\371\370\370\340\340\0\0\0\0\0\340\340\377\377\377\377\377\376\376\376\376\376"
|
||||
"\376\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\371\346\346\6\6\6\6\6\0\340\340\340\341\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\1\1\37\37\377\376\376\340\340\200\201\201\341\341\177\177\37\37\1\1\377\377"
|
||||
"\377\377\1\1\1\1\377\377\377\377\1\1\37\37\177\177\341\341\201\201\200\200\370\370\376\376\37\37\1\1\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\7\7\7\7\7\7\1\1\0\0\0\0\0\0\377\377"
|
||||
"\377\377\0\0\0\0\377\377\377\377\0\0\0\0\0\0\1\1\7\7\7\7\7\7\1\1\0\0\0\0\0\0"
|
||||
"\0\0\0";
|
||||
*/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -290,12 +290,8 @@ public:
|
||||
}
|
||||
|
||||
void lampUdated() {
|
||||
bool fxChanged = strip.setEffectConfig(effectCurrent, effectSpeed, effectIntensity, effectPalette);
|
||||
|
||||
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification)
|
||||
// 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa
|
||||
colorUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
updateInterfaces(CALL_MODE_DIRECT_CHANGE);
|
||||
colorUpdated(CALL_MODE_BUTTON);
|
||||
updateInterfaces(CALL_MODE_BUTTON);
|
||||
}
|
||||
|
||||
void changeBrightness(bool increase) {
|
||||
|
||||
@@ -19,17 +19,20 @@
|
||||
// Change between modes by pressing a button.
|
||||
//
|
||||
// Dependencies
|
||||
// * This usermod REQURES the ModeSortUsermod
|
||||
// * This Usermod works best coupled with
|
||||
// FourLineDisplayUsermod.
|
||||
//
|
||||
// If FourLineDisplayUsermod is used the folowing options are also inabled
|
||||
// If FourLineDisplayUsermod is used the folowing options are also enabled
|
||||
//
|
||||
// * main color
|
||||
// * saturation of main color
|
||||
// * display network (long press buttion)
|
||||
//
|
||||
|
||||
#ifdef USERMOD_MODE_SORT
|
||||
#error "Usermod Mode Sort is no longer required. Remove -D USERMOD_MODE_SORT from platformio.ini"
|
||||
#endif
|
||||
|
||||
#ifndef ENCODER_DT_PIN
|
||||
#define ENCODER_DT_PIN 18
|
||||
#endif
|
||||
@@ -44,27 +47,90 @@
|
||||
|
||||
// The last UI state, remove color and saturation option if diplay not active(too many options)
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
#define LAST_UI_STATE 6
|
||||
#define LAST_UI_STATE 8
|
||||
#else
|
||||
#define LAST_UI_STATE 4
|
||||
#endif
|
||||
|
||||
// Number of modes at the start of the list to not sort
|
||||
#define MODE_SORT_SKIP_COUNT 1
|
||||
|
||||
// Which list is being sorted
|
||||
static char **listBeingSorted;
|
||||
|
||||
/**
|
||||
* Modes and palettes are stored as strings that
|
||||
* end in a quote character. Compare two of them.
|
||||
* We are comparing directly within either
|
||||
* JSON_mode_names or JSON_palette_names.
|
||||
*/
|
||||
static int re_qstringCmp(const void *ap, const void *bp) {
|
||||
char *a = listBeingSorted[*((byte *)ap)];
|
||||
char *b = listBeingSorted[*((byte *)bp)];
|
||||
int i = 0;
|
||||
do {
|
||||
char aVal = pgm_read_byte_near(a + i);
|
||||
if (aVal >= 97 && aVal <= 122) {
|
||||
// Lowercase
|
||||
aVal -= 32;
|
||||
}
|
||||
char bVal = pgm_read_byte_near(b + i);
|
||||
if (bVal >= 97 && bVal <= 122) {
|
||||
// Lowercase
|
||||
bVal -= 32;
|
||||
}
|
||||
// Relly we shouldn't ever get to '\0'
|
||||
if (aVal == '"' || bVal == '"' || aVal == '\0' || bVal == '\0') {
|
||||
// We're done. one is a substring of the other
|
||||
// or something happenend and the quote didn't stop us.
|
||||
if (aVal == bVal) {
|
||||
// Same value, probably shouldn't happen
|
||||
// with this dataset
|
||||
return 0;
|
||||
}
|
||||
else if (aVal == '"' || aVal == '\0') {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (aVal == bVal) {
|
||||
// Same characters. Move to the next.
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
// We're done
|
||||
if (aVal < bVal) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
} while (true);
|
||||
// We shouldn't get here.
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
class RotaryEncoderUIUsermod : public Usermod {
|
||||
private:
|
||||
int fadeAmount = 5; // Amount to change every step (brightness)
|
||||
unsigned long currentTime;
|
||||
int8_t fadeAmount = 5; // Amount to change every step (brightness)
|
||||
unsigned long loopTime;
|
||||
unsigned long buttonHoldTIme;
|
||||
|
||||
unsigned long buttonPressedTime = 0;
|
||||
unsigned long buttonWaitTime = 0;
|
||||
bool buttonPressedBefore = false;
|
||||
bool buttonLongPressed = false;
|
||||
|
||||
int8_t pinA = ENCODER_DT_PIN; // DT from encoder
|
||||
int8_t pinB = ENCODER_CLK_PIN; // CLK from encoder
|
||||
int8_t pinC = ENCODER_SW_PIN; // SW from encoder
|
||||
unsigned char select_state = 0; // 0: brightness, 1: effect, 2: effect speed
|
||||
unsigned char button_state = HIGH;
|
||||
unsigned char prev_button_state = HIGH;
|
||||
bool networkShown = false;
|
||||
uint16_t currentHue1 = 6425; // default reboot color
|
||||
byte currentSat1 = 255;
|
||||
|
||||
unsigned char select_state = 0; // 0: brightness, 1: effect, 2: effect speed, ...
|
||||
|
||||
uint16_t currentHue1 = 16; // default boot color
|
||||
byte currentSat1 = 255;
|
||||
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
FourLineDisplayUsermod *display;
|
||||
@@ -72,7 +138,16 @@ private:
|
||||
void* display = nullptr;
|
||||
#endif
|
||||
|
||||
// Pointers the start of the mode names within JSON_mode_names
|
||||
char **modes_qstrings = nullptr;
|
||||
|
||||
// Array of mode indexes in alphabetical order.
|
||||
byte *modes_alpha_indexes = nullptr;
|
||||
|
||||
// Pointers the start of the palette names within JSON_palette_names
|
||||
char **palettes_qstrings = nullptr;
|
||||
|
||||
// Array of palette indexes in alphabetical order.
|
||||
byte *palettes_alpha_indexes = nullptr;
|
||||
|
||||
unsigned char Enc_A;
|
||||
@@ -85,6 +160,14 @@ private:
|
||||
uint8_t knownMode = 0;
|
||||
uint8_t knownPalette = 0;
|
||||
|
||||
uint8_t currentCCT = 128;
|
||||
bool isRgbw = false;
|
||||
|
||||
byte presetHigh = 0;
|
||||
byte presetLow = 0;
|
||||
|
||||
bool applyToAll = true;
|
||||
|
||||
bool initDone = false;
|
||||
bool enabled = true;
|
||||
|
||||
@@ -94,14 +177,94 @@ private:
|
||||
static const char _DT_pin[];
|
||||
static const char _CLK_pin[];
|
||||
static const char _SW_pin[];
|
||||
static const char _presetHigh[];
|
||||
static const char _presetLow[];
|
||||
static const char _applyToAll[];
|
||||
|
||||
/**
|
||||
* Sort the modes and palettes to the index arrays
|
||||
* modes_alpha_indexes and palettes_alpha_indexes.
|
||||
*/
|
||||
void sortModesAndPalettes() {
|
||||
modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount());
|
||||
modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
|
||||
re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
|
||||
|
||||
palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount());
|
||||
palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount());
|
||||
|
||||
// How many palette names start with '*' and should not be sorted?
|
||||
// (Also skipping the first one, 'Default').
|
||||
int skipPaletteCount = 1;
|
||||
while (pgm_read_byte_near(palettes_qstrings[skipPaletteCount++]) == '*') ;
|
||||
re_sortModes(palettes_qstrings, palettes_alpha_indexes, strip.getPaletteCount(), skipPaletteCount);
|
||||
}
|
||||
|
||||
byte *re_initIndexArray(int numModes) {
|
||||
byte *indexes = (byte *)malloc(sizeof(byte) * numModes);
|
||||
for (byte i = 0; i < numModes; i++) {
|
||||
indexes[i] = i;
|
||||
}
|
||||
return indexes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of mode or palette names from the JSON string.
|
||||
* They don't end in '\0', they end in '"'.
|
||||
*/
|
||||
char **re_findModeStrings(const char json[], int numModes) {
|
||||
char **modeStrings = (char **)malloc(sizeof(char *) * numModes);
|
||||
uint8_t modeIndex = 0;
|
||||
bool insideQuotes = false;
|
||||
// advance past the mark for markLineNum that may exist.
|
||||
char singleJsonSymbol;
|
||||
|
||||
// Find the mode name in JSON
|
||||
bool complete = false;
|
||||
for (size_t i = 0; i < strlen_P(json); i++) {
|
||||
singleJsonSymbol = pgm_read_byte_near(json + i);
|
||||
if (singleJsonSymbol == '\0') break;
|
||||
switch (singleJsonSymbol) {
|
||||
case '"':
|
||||
insideQuotes = !insideQuotes;
|
||||
if (insideQuotes) {
|
||||
// We have a new mode or palette
|
||||
modeStrings[modeIndex] = (char *)(json + i + 1);
|
||||
}
|
||||
break;
|
||||
case '[':
|
||||
break;
|
||||
case ']':
|
||||
if (!insideQuotes) complete = true;
|
||||
break;
|
||||
case ',':
|
||||
if (!insideQuotes) modeIndex++;
|
||||
default:
|
||||
if (!insideQuotes) break;
|
||||
}
|
||||
if (complete) break;
|
||||
}
|
||||
return modeStrings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort either the modes or the palettes using quicksort.
|
||||
*/
|
||||
void re_sortModes(char **modeNames, byte *indexes, int count, int numSkip) {
|
||||
listBeingSorted = modeNames;
|
||||
qsort(indexes + numSkip, count - numSkip, sizeof(byte), re_qstringCmp);
|
||||
listBeingSorted = nullptr;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
/*
|
||||
* setup() is called once at boot. WiFi is not yet connected at this point.
|
||||
* You can use it to initialize variables, sensors or similar.
|
||||
*/
|
||||
* 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()
|
||||
{
|
||||
DEBUG_PRINTLN(F("Usermod Rotary Encoder init."));
|
||||
PinManagerPinType pins[3] = { { pinA, false }, { pinB, false }, { pinC, false } };
|
||||
if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_RotaryEncoderUI)) {
|
||||
// BUG: configuring this usermod with conflicting pins
|
||||
@@ -117,12 +280,17 @@ public:
|
||||
pinMode(pinA, INPUT_PULLUP);
|
||||
pinMode(pinB, INPUT_PULLUP);
|
||||
pinMode(pinC, INPUT_PULLUP);
|
||||
currentTime = millis();
|
||||
loopTime = currentTime;
|
||||
loopTime = millis();
|
||||
|
||||
ModeSortUsermod *modeSortUsermod = (ModeSortUsermod*) usermods.lookup(USERMOD_ID_MODE_SORT);
|
||||
modes_alpha_indexes = modeSortUsermod->getModesAlphaIndexes();
|
||||
palettes_alpha_indexes = modeSortUsermod->getPalettesAlphaIndexes();
|
||||
for (uint8_t s = 0; s < busses.getNumBusses(); s++) {
|
||||
Bus *bus = busses.getBus(s);
|
||||
if (!bus || bus->getLength()==0) break;
|
||||
isRgbw |= bus->isRgbw();
|
||||
}
|
||||
|
||||
currentCCT = (approximateKelvinFromRGB(RGBW32(col[0], col[1], col[2], col[3])) - 1900) >> 5;
|
||||
|
||||
if (!initDone) sortModesAndPalettes();
|
||||
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
// This Usermod uses FourLineDisplayUsermod for the best experience.
|
||||
@@ -140,91 +308,87 @@ public:
|
||||
}
|
||||
|
||||
/*
|
||||
* connected() is called every time the WiFi is (re)connected
|
||||
* Use it to initialize network interfaces
|
||||
*/
|
||||
* connected() is called every time the WiFi is (re)connected
|
||||
* Use it to initialize network interfaces
|
||||
*/
|
||||
void connected()
|
||||
{
|
||||
//Serial.println("Connected to WiFi!");
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
* 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()
|
||||
{
|
||||
currentTime = millis(); // get the current elapsed time
|
||||
if (!enabled || strip.isUpdating()) return;
|
||||
unsigned long currentTime = millis(); // get the current elapsed time
|
||||
|
||||
// Initialize effectCurrentIndex and effectPaletteIndex to
|
||||
// current state. We do it here as (at least) effectCurrent
|
||||
// is not yet initialized when setup is called.
|
||||
|
||||
if (!currentEffectAndPaletteInitialized) {
|
||||
findCurrentEffectAndPalette();}
|
||||
findCurrentEffectAndPalette();
|
||||
}
|
||||
|
||||
if(modes_alpha_indexes[effectCurrentIndex] != effectCurrent
|
||||
|| palettes_alpha_indexes[effectPaletteIndex] != effectPalette){
|
||||
if (modes_alpha_indexes[effectCurrentIndex] != effectCurrent || palettes_alpha_indexes[effectPaletteIndex] != effectPalette) {
|
||||
currentEffectAndPaletteInitialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentTime >= (loopTime + 2)) // 2ms since last check of encoder = 500Hz
|
||||
{
|
||||
button_state = digitalRead(pinC);
|
||||
if (prev_button_state != button_state)
|
||||
{
|
||||
if (button_state == HIGH && (millis()-buttonHoldTIme < 3000))
|
||||
{
|
||||
prev_button_state = button_state;
|
||||
loopTime = currentTime; // Updates loopTime
|
||||
|
||||
char newState = select_state + 1;
|
||||
if (newState > LAST_UI_STATE) newState = 0;
|
||||
|
||||
bool changedState = true;
|
||||
if (display != nullptr) {
|
||||
switch(newState) {
|
||||
case 0:
|
||||
changedState = changeState(" Brightness", 1, 0, 1);
|
||||
break;
|
||||
case 1:
|
||||
changedState = changeState(" Speed", 1, 4, 2);
|
||||
break;
|
||||
case 2:
|
||||
changedState = changeState(" Intensity", 1 ,8, 3);
|
||||
break;
|
||||
case 3:
|
||||
changedState = changeState(" Color Palette", 2, 0, 4);
|
||||
break;
|
||||
case 4:
|
||||
changedState = changeState(" Effect", 3, 0, 5);
|
||||
break;
|
||||
case 5:
|
||||
changedState = changeState(" Main Color", 255, 255, 7);
|
||||
break;
|
||||
case 6:
|
||||
changedState = changeState(" Saturation", 255, 255, 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (changedState) {
|
||||
select_state = newState;
|
||||
bool buttonPressed = !digitalRead(pinC); //0=pressed, 1=released
|
||||
if (buttonPressed) {
|
||||
if (!buttonPressedBefore) buttonPressedTime = currentTime;
|
||||
buttonPressedBefore = true;
|
||||
if (currentTime-buttonPressedTime > 3000) {
|
||||
if (!buttonLongPressed) displayNetworkInfo(); //long press for network info
|
||||
buttonLongPressed = true;
|
||||
}
|
||||
} else if (!buttonPressed && buttonPressedBefore) {
|
||||
bool doublePress = buttonWaitTime;
|
||||
buttonWaitTime = 0;
|
||||
if (!buttonLongPressed) {
|
||||
if (doublePress) {
|
||||
toggleOnOff();
|
||||
lampUdated();
|
||||
} else {
|
||||
buttonWaitTime = currentTime;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_button_state = button_state;
|
||||
networkShown = false;
|
||||
if(!prev_button_state)buttonHoldTIme = millis();
|
||||
}
|
||||
buttonLongPressed = false;
|
||||
buttonPressedBefore = false;
|
||||
}
|
||||
if (buttonWaitTime && currentTime-buttonWaitTime>350 && !buttonPressedBefore) { //same speed as in button.cpp
|
||||
buttonWaitTime = 0;
|
||||
char newState = select_state + 1;
|
||||
bool changedState = true;
|
||||
if (newState > LAST_UI_STATE || (newState == 8 && presetHigh==0 && presetLow == 0)) newState = 0;
|
||||
if (display != nullptr) {
|
||||
switch (newState) {
|
||||
case 0: changedState = changeState(PSTR("Brightness"), 1, 0, 1); break; //1 = sun
|
||||
case 1: changedState = changeState(PSTR("Speed"), 1, 4, 2); break; //2 = skip forward
|
||||
case 2: changedState = changeState(PSTR("Intensity"), 1, 8, 3); break; //3 = fire
|
||||
case 3: changedState = changeState(PSTR("Color Palette"), 2, 0, 4); break; //4 = custom palette
|
||||
case 4: changedState = changeState(PSTR("Effect"), 3, 0, 5); break; //5 = puzzle piece
|
||||
case 5: changedState = changeState(PSTR("Main Color"), 255, 255, 7); break; //7 = brush
|
||||
case 6: changedState = changeState(PSTR("Saturation"), 255, 255, 8); break; //8 = contrast
|
||||
case 7: changedState = changeState(PSTR("CCT"), 255, 255, 10); break; //10 = star
|
||||
case 8: changedState = changeState(PSTR("Preset"), 255, 255, 11); break; //11 = heart
|
||||
}
|
||||
}
|
||||
if (changedState) select_state = newState;
|
||||
}
|
||||
|
||||
if (!prev_button_state && (millis()-buttonHoldTIme > 3000) && !networkShown) displayNetworkInfo(); //long press for network info
|
||||
|
||||
Enc_A = digitalRead(pinA); // Read encoder pins
|
||||
Enc_B = digitalRead(pinB);
|
||||
@@ -233,65 +397,39 @@ public:
|
||||
if (Enc_B == LOW) //changes to LOW so that then encoder registers a change at the very end of a pulse
|
||||
{ // B is high so clockwise
|
||||
switch(select_state) {
|
||||
case 0:
|
||||
changeBrightness(true);
|
||||
break;
|
||||
case 1:
|
||||
changeEffectSpeed(true);
|
||||
break;
|
||||
case 2:
|
||||
changeEffectIntensity(true);
|
||||
break;
|
||||
case 3:
|
||||
changePalette(true);
|
||||
break;
|
||||
case 4:
|
||||
changeEffect(true);
|
||||
break;
|
||||
case 5:
|
||||
changeHue(true);
|
||||
break;
|
||||
case 6:
|
||||
changeSat(true);
|
||||
break;
|
||||
case 0: changeBrightness(true); break;
|
||||
case 1: changeEffectSpeed(true); break;
|
||||
case 2: changeEffectIntensity(true); break;
|
||||
case 3: changePalette(true); break;
|
||||
case 4: changeEffect(true); break;
|
||||
case 5: changeHue(true); break;
|
||||
case 6: changeSat(true); break;
|
||||
case 7: changeCCT(true); break;
|
||||
case 8: changePreset(true); break;
|
||||
}
|
||||
}
|
||||
else if (Enc_B == HIGH)
|
||||
{ // B is low so counter-clockwise
|
||||
switch(select_state) {
|
||||
case 0:
|
||||
changeBrightness(false);
|
||||
break;
|
||||
case 1:
|
||||
changeEffectSpeed(false);
|
||||
break;
|
||||
case 2:
|
||||
changeEffectIntensity(false);
|
||||
break;
|
||||
case 3:
|
||||
changePalette(false);
|
||||
break;
|
||||
case 4:
|
||||
changeEffect(false);
|
||||
break;
|
||||
case 5:
|
||||
changeHue(false);
|
||||
break;
|
||||
case 6:
|
||||
changeSat(false);
|
||||
break;
|
||||
case 0: changeBrightness(false); break;
|
||||
case 1: changeEffectSpeed(false); break;
|
||||
case 2: changeEffectIntensity(false); break;
|
||||
case 3: changePalette(false); break;
|
||||
case 4: changeEffect(false); break;
|
||||
case 5: changeHue(false); break;
|
||||
case 6: changeSat(false); break;
|
||||
case 7: changeCCT(false); break;
|
||||
case 8: changePreset(false); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Enc_A_prev = Enc_A; // Store value of A for next time
|
||||
loopTime = currentTime; // Updates loopTime
|
||||
}
|
||||
}
|
||||
|
||||
void displayNetworkInfo(){
|
||||
void displayNetworkInfo() {
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->networkOverlay(" NETWORK INFO", 15000);
|
||||
networkShown = true;
|
||||
display->networkOverlay(PSTR("NETWORK INFO"), 10000);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -313,180 +451,292 @@ public:
|
||||
}
|
||||
|
||||
boolean changeState(const char *stateName, byte markedLine, byte markedCol, byte glyph) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display != nullptr) {
|
||||
if (display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return false;
|
||||
}
|
||||
display->overlay(stateName, 750, glyph);
|
||||
display->setMarkLine(markedLine, markedCol);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display != nullptr) {
|
||||
if (display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
display->redraw(true);
|
||||
return false;
|
||||
}
|
||||
display->overlay(stateName, 750, glyph);
|
||||
display->setMarkLine(markedLine, markedCol);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void lampUdated() {
|
||||
//bool fxChanged = strip.setEffectConfig(effectCurrent, effectSpeed, effectIntensity, effectPalette);
|
||||
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification)
|
||||
// 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa
|
||||
colorUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
updateInterfaces(CALL_MODE_DIRECT_CHANGE);
|
||||
//setValuesFromFirstSelectedSeg(); //to make transition work on main segment (should no longer be required)
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
updateInterfaces(CALL_MODE_BUTTON);
|
||||
}
|
||||
|
||||
void changeBrightness(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (increase) bri = (bri + fadeAmount <= 255) ? (bri + fadeAmount) : 255;
|
||||
else bri = (bri - fadeAmount >= 0) ? (bri - fadeAmount) : 0;
|
||||
lampUdated();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->updateBrightness();
|
||||
#endif
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
display->redraw(true);
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
display->updateRedrawTime();
|
||||
#endif
|
||||
bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0);
|
||||
lampUdated();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->updateBrightness();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void changeEffect(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (increase) effectCurrentIndex = (effectCurrentIndex + 1 >= strip.getModeCount()) ? 0 : (effectCurrentIndex + 1);
|
||||
else effectCurrentIndex = (effectCurrentIndex - 1 < 0) ? (strip.getModeCount() - 1) : (effectCurrentIndex - 1);
|
||||
effectCurrent = modes_alpha_indexes[effectCurrentIndex];
|
||||
lampUdated();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->showCurrentEffectOrPalette(effectCurrent, JSON_mode_names, 3);
|
||||
#endif
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
display->redraw(true);
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
display->updateRedrawTime();
|
||||
#endif
|
||||
effectCurrentIndex = max(min((increase ? effectCurrentIndex+1 : effectCurrentIndex-1), strip.getModeCount()-1), 0);
|
||||
effectCurrent = modes_alpha_indexes[effectCurrentIndex];
|
||||
stateChanged = true;
|
||||
if (applyToAll) {
|
||||
for (byte i=0; i<strip.getMaxSegments(); i++) {
|
||||
WS2812FX::Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive()) continue;
|
||||
strip.setMode(i, effectCurrent);
|
||||
}
|
||||
} else {
|
||||
//WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
|
||||
strip.setMode(strip.getMainSegmentId(), effectCurrent);
|
||||
}
|
||||
lampUdated();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->showCurrentEffectOrPalette(effectCurrent, JSON_mode_names, 3);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void changeEffectSpeed(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (increase) effectSpeed = (effectSpeed + fadeAmount <= 255) ? (effectSpeed + fadeAmount) : 255;
|
||||
else effectSpeed = (effectSpeed - fadeAmount >= 0) ? (effectSpeed - fadeAmount) : 0;
|
||||
lampUdated();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->updateSpeed();
|
||||
#endif
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
display->redraw(true);
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
display->updateRedrawTime();
|
||||
#endif
|
||||
effectSpeed = max(min((increase ? effectSpeed+fadeAmount : effectSpeed-fadeAmount), 255), 0);
|
||||
stateChanged = true;
|
||||
if (applyToAll) {
|
||||
for (byte i=0; i<strip.getMaxSegments(); i++) {
|
||||
WS2812FX::Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive()) continue;
|
||||
seg.speed = effectSpeed;
|
||||
}
|
||||
} else {
|
||||
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
|
||||
seg.speed = effectSpeed;
|
||||
}
|
||||
lampUdated();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->updateSpeed();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void changeEffectIntensity(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (increase) effectIntensity = (effectIntensity + fadeAmount <= 255) ? (effectIntensity + fadeAmount) : 255;
|
||||
else effectIntensity = (effectIntensity - fadeAmount >= 0) ? (effectIntensity - fadeAmount) : 0;
|
||||
lampUdated();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->updateIntensity();
|
||||
#endif
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
display->redraw(true);
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
display->updateRedrawTime();
|
||||
#endif
|
||||
effectIntensity = max(min((increase ? effectIntensity+fadeAmount : effectIntensity-fadeAmount), 255), 0);
|
||||
stateChanged = true;
|
||||
if (applyToAll) {
|
||||
for (byte i=0; i<strip.getMaxSegments(); i++) {
|
||||
WS2812FX::Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive()) continue;
|
||||
seg.intensity = effectIntensity;
|
||||
}
|
||||
} else {
|
||||
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
|
||||
seg.intensity = effectIntensity;
|
||||
}
|
||||
lampUdated();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->updateIntensity();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void changePalette(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (increase) effectPaletteIndex = (effectPaletteIndex + 1 >= strip.getPaletteCount()) ? 0 : (effectPaletteIndex + 1);
|
||||
else effectPaletteIndex = (effectPaletteIndex - 1 < 0) ? (strip.getPaletteCount() - 1) : (effectPaletteIndex - 1);
|
||||
effectPalette = palettes_alpha_indexes[effectPaletteIndex];
|
||||
lampUdated();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->showCurrentEffectOrPalette(effectPalette, JSON_palette_names, 2);
|
||||
#endif
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
display->redraw(true);
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
display->updateRedrawTime();
|
||||
#endif
|
||||
effectPaletteIndex = max(min((increase ? effectPaletteIndex+1 : effectPaletteIndex-1), strip.getPaletteCount()-1), 0);
|
||||
effectPalette = palettes_alpha_indexes[effectPaletteIndex];
|
||||
stateChanged = true;
|
||||
if (applyToAll) {
|
||||
for (byte i=0; i<strip.getMaxSegments(); i++) {
|
||||
WS2812FX::Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive()) continue;
|
||||
seg.palette = effectPalette;
|
||||
}
|
||||
} else {
|
||||
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
|
||||
seg.palette = effectPalette;
|
||||
}
|
||||
lampUdated();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->showCurrentEffectOrPalette(effectPalette, JSON_palette_names, 2);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void changeHue(bool increase){
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(increase) currentHue1 += 321;
|
||||
else currentHue1 -= 321;
|
||||
colorHStoRGB(currentHue1, currentSat1, col);
|
||||
lampUdated();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->updateRedrawTime();
|
||||
#endif
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
display->redraw(true);
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
display->updateRedrawTime();
|
||||
#endif
|
||||
currentHue1 = max(min((increase ? currentHue1+fadeAmount : currentHue1-fadeAmount), 255), 0);
|
||||
colorHStoRGB(currentHue1*256, currentSat1, col);
|
||||
stateChanged = true;
|
||||
if (applyToAll) {
|
||||
for (byte i=0; i<strip.getMaxSegments(); i++) {
|
||||
WS2812FX::Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive()) continue;
|
||||
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
|
||||
}
|
||||
} else {
|
||||
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
|
||||
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
|
||||
}
|
||||
lampUdated();
|
||||
}
|
||||
|
||||
void changeSat(bool increase){
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
display->redraw(true);
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
display->updateRedrawTime();
|
||||
#endif
|
||||
currentSat1 = max(min((increase ? currentSat1+fadeAmount : currentSat1-fadeAmount), 255), 0);
|
||||
colorHStoRGB(currentHue1*256, currentSat1, col);
|
||||
if (applyToAll) {
|
||||
for (byte i=0; i<strip.getMaxSegments(); i++) {
|
||||
WS2812FX::Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive()) continue;
|
||||
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
|
||||
}
|
||||
} else {
|
||||
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
|
||||
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
|
||||
}
|
||||
lampUdated();
|
||||
}
|
||||
|
||||
if(increase) currentSat1 = (currentSat1 + 5 <= 255 ? (currentSat1 + 5) : 255);
|
||||
else currentSat1 = (currentSat1 - 5 >= 0 ? (currentSat1 - 5) : 0);
|
||||
colorHStoRGB(currentHue1, currentSat1, col);
|
||||
lampUdated();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->updateRedrawTime();
|
||||
#endif
|
||||
void changePreset(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
display->redraw(true);
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
display->updateRedrawTime();
|
||||
#endif
|
||||
if (presetHigh && presetLow && presetHigh > presetLow) {
|
||||
String apireq = F("win&PL=~");
|
||||
if (!increase) apireq += '-';
|
||||
apireq += F("&P1=");
|
||||
apireq += presetLow;
|
||||
apireq += F("&P2=");
|
||||
apireq += presetHigh;
|
||||
handleSet(nullptr, apireq, false);
|
||||
lampUdated();
|
||||
}
|
||||
}
|
||||
|
||||
void changeCCT(bool increase){
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
display->redraw(true);
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
display->updateRedrawTime();
|
||||
#endif
|
||||
currentCCT = max(min((increase ? currentCCT+fadeAmount : currentCCT-fadeAmount), 255), 0);
|
||||
// if (applyToAll) {
|
||||
for (byte i=0; i<strip.getMaxSegments(); i++) {
|
||||
WS2812FX::Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive()) continue;
|
||||
seg.setCCT(currentCCT, i);
|
||||
}
|
||||
// } else {
|
||||
// WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
|
||||
// seg.setCCT(currentCCT, strip.getMainSegmentId());
|
||||
// }
|
||||
lampUdated();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
* 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)
|
||||
{
|
||||
int reading = 20;
|
||||
//this code adds "u":{"Light":[20," lux"]} to the info object
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
JsonArray lightArr = user.createNestedArray("Light"); //name
|
||||
lightArr.add(reading); //value
|
||||
lightArr.add(" lux"); //unit
|
||||
}
|
||||
*/
|
||||
void addToJsonInfo(JsonObject& root)
|
||||
{
|
||||
int reading = 20;
|
||||
//this code adds "u":{"Light":[20," lux"]} to the info object
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
JsonArray lightArr = user.createNestedArray("Light"); //name
|
||||
lightArr.add(reading); //value
|
||||
lightArr.add(" lux"); //unit
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
* 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["user0"] = userVar0;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
* 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)
|
||||
{
|
||||
//userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
|
||||
//if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
|
||||
@@ -498,6 +748,9 @@ public:
|
||||
top[FPSTR(_DT_pin)] = pinA;
|
||||
top[FPSTR(_CLK_pin)] = pinB;
|
||||
top[FPSTR(_SW_pin)] = pinC;
|
||||
top[FPSTR(_presetLow)] = presetLow;
|
||||
top[FPSTR(_presetHigh)] = presetHigh;
|
||||
top[FPSTR(_applyToAll)] = applyToAll;
|
||||
DEBUG_PRINTLN(F("Rotary Encoder config saved."));
|
||||
}
|
||||
|
||||
@@ -514,14 +767,17 @@ public:
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
int8_t newDTpin = pinA;
|
||||
int8_t newCLKpin = pinB;
|
||||
int8_t newSWpin = pinC;
|
||||
int8_t newDTpin = top[FPSTR(_DT_pin)] | pinA;
|
||||
int8_t newCLKpin = top[FPSTR(_CLK_pin)] | pinB;
|
||||
int8_t newSWpin = top[FPSTR(_SW_pin)] | pinC;
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
newDTpin = top[FPSTR(_DT_pin)] | newDTpin;
|
||||
newCLKpin = top[FPSTR(_CLK_pin)] | newCLKpin;
|
||||
newSWpin = top[FPSTR(_SW_pin)] | newSWpin;
|
||||
presetHigh = top[FPSTR(_presetHigh)] | presetHigh;
|
||||
presetLow = top[FPSTR(_presetLow)] | presetLow;
|
||||
presetHigh = MIN(250,MAX(0,presetHigh));
|
||||
presetLow = MIN(250,MAX(0,presetLow));
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
applyToAll = top[FPSTR(_applyToAll)] | applyToAll;
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
@@ -548,7 +804,7 @@ public:
|
||||
}
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[FPSTR(_enabled)].isNull();
|
||||
return !top[FPSTR(_applyToAll)].isNull();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -562,8 +818,11 @@ public:
|
||||
};
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char RotaryEncoderUIUsermod::_name[] PROGMEM = "Rotary-Encoder";
|
||||
const char RotaryEncoderUIUsermod::_enabled[] PROGMEM = "enabled";
|
||||
const char RotaryEncoderUIUsermod::_DT_pin[] PROGMEM = "DT-pin";
|
||||
const char RotaryEncoderUIUsermod::_CLK_pin[] PROGMEM = "CLK-pin";
|
||||
const char RotaryEncoderUIUsermod::_SW_pin[] PROGMEM = "SW-pin";
|
||||
const char RotaryEncoderUIUsermod::_name[] PROGMEM = "Rotary-Encoder";
|
||||
const char RotaryEncoderUIUsermod::_enabled[] PROGMEM = "enabled";
|
||||
const char RotaryEncoderUIUsermod::_DT_pin[] PROGMEM = "DT-pin";
|
||||
const char RotaryEncoderUIUsermod::_CLK_pin[] PROGMEM = "CLK-pin";
|
||||
const char RotaryEncoderUIUsermod::_SW_pin[] PROGMEM = "SW-pin";
|
||||
const char RotaryEncoderUIUsermod::_presetHigh[] PROGMEM = "preset-high";
|
||||
const char RotaryEncoderUIUsermod::_presetLow[] PROGMEM = "preset-low";
|
||||
const char RotaryEncoderUIUsermod::_applyToAll[] PROGMEM = "apply-2-all-seg";
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
|
||||
#include "FX.h"
|
||||
#include "wled.h"
|
||||
|
||||
#define IBN 5100
|
||||
#define PALETTE_SOLID_WRAP (paletteBlend == 1 || paletteBlend == 3)
|
||||
@@ -936,7 +937,7 @@ uint16_t WS2812FX::mode_chase_flash_random(void) {
|
||||
} else {
|
||||
SEGENV.step = (SEGENV.step + 1) % SEGLEN;
|
||||
|
||||
if(SEGENV.step == 0) {
|
||||
if (SEGENV.step == 0) {
|
||||
SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0);
|
||||
}
|
||||
}
|
||||
@@ -965,8 +966,7 @@ uint16_t WS2812FX::running(uint32_t color1, uint32_t color2, bool theatre) {
|
||||
setPixelColor(i,col);
|
||||
}
|
||||
|
||||
if (it != SEGENV.step )
|
||||
{
|
||||
if (it != SEGENV.step) {
|
||||
SEGENV.aux0 = (SEGENV.aux0 +1) % (theatre ? width : (width<<1));
|
||||
SEGENV.step = it;
|
||||
}
|
||||
@@ -996,26 +996,34 @@ uint16_t WS2812FX::mode_halloween(void) {
|
||||
|
||||
|
||||
/*
|
||||
* Random colored pixels running.
|
||||
* Random colored pixels running. ("Stream")
|
||||
*/
|
||||
uint16_t WS2812FX::mode_running_random(void) {
|
||||
uint32_t cycleTime = 25 + (3 * (uint32_t)(255 - SEGMENT.speed));
|
||||
uint32_t it = now / cycleTime;
|
||||
if (SEGENV.aux1 == it) return FRAMETIME;
|
||||
if (SEGENV.call == 0) SEGENV.aux0 = random16(); // random seed for PRNG on start
|
||||
|
||||
for(uint16_t i=SEGLEN-1; i > 0; i--) {
|
||||
setPixelColor( i, getPixelColor( i - 1));
|
||||
}
|
||||
uint8_t zoneSize = ((255-SEGMENT.intensity) >> 4) +1;
|
||||
uint16_t PRNG16 = SEGENV.aux0;
|
||||
|
||||
if(SEGENV.step == 0) {
|
||||
SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0);
|
||||
setPixelColor(0, color_wheel(SEGENV.aux0));
|
||||
}
|
||||
|
||||
SEGENV.step++;
|
||||
if (SEGENV.step > (uint8_t)((255-SEGMENT.intensity) >> 4))
|
||||
{
|
||||
SEGENV.step = 0;
|
||||
uint8_t z = it % zoneSize;
|
||||
bool nzone = (!z && it != SEGENV.aux1);
|
||||
for (uint16_t i=SEGLEN-1; i > 0; i--) {
|
||||
if (nzone || z >= zoneSize) {
|
||||
uint8_t lastrand = PRNG16 >> 8;
|
||||
int16_t diff = 0;
|
||||
while (abs(diff) < 42) { // make sure the difference between adjacent colors is big enough
|
||||
PRNG16 = (uint16_t)(PRNG16 * 2053) + 13849; // next zone, next 'random' number
|
||||
diff = (PRNG16 >> 8) - lastrand;
|
||||
}
|
||||
if (nzone) {
|
||||
SEGENV.aux0 = PRNG16; // save next starting seed
|
||||
nzone = false;
|
||||
}
|
||||
z = 0;
|
||||
}
|
||||
setPixelColor(i, color_wheel(PRNG16 >> 8));
|
||||
z++;
|
||||
}
|
||||
|
||||
SEGENV.aux1 = it;
|
||||
@@ -1589,26 +1597,36 @@ uint16_t WS2812FX::mode_dual_larson_scanner(void){
|
||||
|
||||
|
||||
/*
|
||||
* Running random pixels
|
||||
* Running random pixels ("Stream 2")
|
||||
* Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/RandomChase.h
|
||||
*/
|
||||
uint16_t WS2812FX::mode_random_chase(void)
|
||||
{
|
||||
if (SEGENV.call == 0) {
|
||||
SEGENV.step = RGBW32(random8(), random8(), random8(), 0);
|
||||
SEGENV.aux0 = random16();
|
||||
}
|
||||
uint16_t prevSeed = random16_get_seed(); // save seed so we can restore it at the end of the function
|
||||
uint32_t cycleTime = 25 + (3 * (uint32_t)(255 - SEGMENT.speed));
|
||||
uint32_t it = now / cycleTime;
|
||||
if (SEGENV.step == it) return FRAMETIME;
|
||||
uint32_t color = SEGENV.step;
|
||||
random16_set_seed(SEGENV.aux0);
|
||||
|
||||
for(uint16_t i = SEGLEN -1; i > 0; i--) {
|
||||
setPixelColor(i, getPixelColor(i-1));
|
||||
uint8_t r = random8(6) != 0 ? (color >> 16 & 0xFF) : random8();
|
||||
uint8_t g = random8(6) != 0 ? (color >> 8 & 0xFF) : random8();
|
||||
uint8_t b = random8(6) != 0 ? (color & 0xFF) : random8();
|
||||
color = RGBW32(r, g, b, 0);
|
||||
setPixelColor(i, r, g, b);
|
||||
if (i == SEGLEN -1 && SEGENV.aux1 != (it & 0xFFFF)) { //new first color in next frame
|
||||
SEGENV.step = color;
|
||||
SEGENV.aux0 = random16_get_seed();
|
||||
}
|
||||
}
|
||||
uint32_t color = getPixelColor(0);
|
||||
if (SEGLEN > 1) color = getPixelColor( 1);
|
||||
uint8_t r = random8(6) != 0 ? (color >> 16 & 0xFF) : random8();
|
||||
uint8_t g = random8(6) != 0 ? (color >> 8 & 0xFF) : random8();
|
||||
uint8_t b = random8(6) != 0 ? (color & 0xFF) : random8();
|
||||
setPixelColor(0, r, g, b);
|
||||
|
||||
SEGENV.step = it;
|
||||
SEGENV.aux1 = it & 0xFFFF;
|
||||
|
||||
random16_set_seed(prevSeed); // restore original seed so other effects can use "random" PRNG
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
@@ -2091,7 +2109,7 @@ uint16_t WS2812FX::mode_colortwinkle()
|
||||
}
|
||||
}
|
||||
}
|
||||
return FRAMETIME;
|
||||
return FRAMETIME_FIXED;
|
||||
}
|
||||
|
||||
|
||||
@@ -2876,7 +2894,7 @@ uint16_t WS2812FX::candle(bool multi)
|
||||
}
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
return FRAMETIME_FIXED;
|
||||
}
|
||||
|
||||
uint16_t WS2812FX::mode_candle()
|
||||
@@ -3904,18 +3922,24 @@ uint16_t WS2812FX::mode_washing_machine(void) {
|
||||
Modified, originally by Mark Kriegsman https://gist.github.com/kriegsman/1f7ccbbfa492a73c015e
|
||||
*/
|
||||
uint16_t WS2812FX::mode_blends(void) {
|
||||
uint16_t dataSize = sizeof(uint32_t) * SEGLEN; // max segment length of 56 pixels on 16 segment ESP8266
|
||||
uint16_t pixelLen = SEGLEN > UINT8_MAX ? UINT8_MAX : SEGLEN;
|
||||
uint16_t dataSize = sizeof(uint32_t) * (pixelLen + 1); // max segment length of 56 pixels on 16 segment ESP8266
|
||||
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
||||
uint32_t* pixels = reinterpret_cast<uint32_t*>(SEGENV.data);
|
||||
uint8_t blendSpeed = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 128);
|
||||
uint8_t shift = (now * ((SEGMENT.speed >> 3) +1)) >> 8;
|
||||
uint8_t shift = (now * ((SEGMENT.speed >> 3) +1)) >> 8;
|
||||
|
||||
for (int i = 0; i < SEGLEN; i++) {
|
||||
for (int i = 0; i < pixelLen; i++) {
|
||||
pixels[i] = color_blend(pixels[i], color_from_palette(shift + quadwave8((i + 1) * 16), false, PALETTE_SOLID_WRAP, 255), blendSpeed);
|
||||
setPixelColor(i, pixels[i]);
|
||||
shift += 3;
|
||||
}
|
||||
|
||||
uint16_t offset = 0;
|
||||
for (int i = 0; i < SEGLEN; i++) {
|
||||
setPixelColor(i, pixels[offset++]);
|
||||
if (offset > pixelLen) offset = 0;
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
|
||||
76
wled00/FX.h
76
wled00/FX.h
@@ -48,7 +48,8 @@
|
||||
|
||||
/* Not used in all effects yet */
|
||||
#define WLED_FPS 42
|
||||
#define FRAMETIME (1000/WLED_FPS)
|
||||
#define FRAMETIME_FIXED (1000/WLED_FPS)
|
||||
#define FRAMETIME _frametime
|
||||
|
||||
/* each segment uses 52 bytes of SRAM memory, so if you're application fails because of
|
||||
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
|
||||
@@ -71,7 +72,7 @@
|
||||
#define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / MAX_NUM_SEGMENTS)
|
||||
|
||||
#define LED_SKIP_AMOUNT 1
|
||||
#define MIN_SHOW_DELAY 15
|
||||
#define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15)
|
||||
|
||||
#define NUM_COLORS 3 /* number of colors per segment */
|
||||
#define SEGMENT _segments[_segment_index]
|
||||
@@ -80,7 +81,6 @@
|
||||
#define SEGLEN _virtualSegmentLength
|
||||
#define SEGACT SEGMENT.stop
|
||||
#define SPEED_FORMULA_L 5U + (50U*(255U - SEGMENT.speed))/SEGLEN
|
||||
#define RESET_RUNTIME memset(_segment_runtimes, 0, sizeof(_segment_runtimes))
|
||||
|
||||
// some common colors
|
||||
#define RED (uint32_t)0xFF0000
|
||||
@@ -334,27 +334,8 @@ class WS2812FX {
|
||||
vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED
|
||||
return vLength;
|
||||
}
|
||||
uint8_t differs(Segment& b) {
|
||||
uint8_t d = 0;
|
||||
if (start != b.start) d |= SEG_DIFFERS_BOUNDS;
|
||||
if (stop != b.stop) d |= SEG_DIFFERS_BOUNDS;
|
||||
if (offset != b.offset) d |= SEG_DIFFERS_GSO;
|
||||
if (grouping != b.grouping) d |= SEG_DIFFERS_GSO;
|
||||
if (spacing != b.spacing) d |= SEG_DIFFERS_GSO;
|
||||
if (opacity != b.opacity) d |= SEG_DIFFERS_BRI;
|
||||
if (mode != b.mode) d |= SEG_DIFFERS_FX;
|
||||
if (speed != b.speed) d |= SEG_DIFFERS_FX;
|
||||
if (intensity != b.intensity) d |= SEG_DIFFERS_FX;
|
||||
if (palette != b.palette) d |= SEG_DIFFERS_FX;
|
||||
|
||||
if ((options & 0b00101111) != (b.options & 0b00101111)) d |= SEG_DIFFERS_OPT;
|
||||
for (uint8_t i = 0; i < NUM_COLORS; i++)
|
||||
{
|
||||
if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL;
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
uint8_t differs(Segment& b);
|
||||
uint8_t getLightCapabilities();
|
||||
} segment;
|
||||
|
||||
// segment runtime parameters
|
||||
@@ -408,8 +389,9 @@ class WS2812FX {
|
||||
* Flags that before the next effect is calculated,
|
||||
* the internal segment state should be reset.
|
||||
* Call resetIfRequired before calling the next effect function.
|
||||
* Safe to call from interrupts and network requests.
|
||||
*/
|
||||
inline void reset() { _requiresReset = true; }
|
||||
inline void markForReset() { _requiresReset = true; }
|
||||
private:
|
||||
uint16_t _dataLen = 0;
|
||||
bool _requiresReset = false;
|
||||
@@ -640,6 +622,7 @@ class WS2812FX {
|
||||
setMode(uint8_t segid, uint8_t m),
|
||||
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),
|
||||
setRange(uint16_t i, uint16_t i2, uint32_t col),
|
||||
setShowCallback(show_callback cb),
|
||||
@@ -648,42 +631,41 @@ class WS2812FX {
|
||||
calcGammaTable(float),
|
||||
trigger(void),
|
||||
setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 0, uint8_t spacing = 0, uint16_t offset = UINT16_MAX),
|
||||
setMainSegmentId(uint8_t n),
|
||||
restartRuntime(),
|
||||
resetSegments(),
|
||||
makeAutoSegments(),
|
||||
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),
|
||||
setPixelSegment(uint8_t n),
|
||||
setTargetFps(uint8_t fps),
|
||||
deserializeMap(uint8_t n=0);
|
||||
|
||||
bool
|
||||
isRgbw = false,
|
||||
isOffRefreshRequred = false, //periodic refresh is required for the strip to remain off.
|
||||
gammaCorrectBri = false,
|
||||
gammaCorrectCol = true,
|
||||
applyToAllSelected = true,
|
||||
setEffectConfig(uint8_t m, uint8_t s, uint8_t i, uint8_t p),
|
||||
checkSegmentAlignment(void),
|
||||
hasCCTBus(void),
|
||||
hasRGBWBus(void),
|
||||
hasCCTBus(void),
|
||||
// return true if the strip is being sent pixel updates
|
||||
isUpdating(void);
|
||||
|
||||
uint8_t
|
||||
mainSegment = 0,
|
||||
paletteFade = 0,
|
||||
paletteBlend = 0,
|
||||
milliampsPerLed = 55,
|
||||
cctBlending = 0,
|
||||
getBrightness(void),
|
||||
getMode(void),
|
||||
getSpeed(void),
|
||||
getModeCount(void),
|
||||
getPaletteCount(void),
|
||||
getMaxSegments(void),
|
||||
getActiveSegmentsNum(void),
|
||||
//getFirstSelectedSegment(void),
|
||||
getFirstSelectedSegId(void),
|
||||
getMainSegmentId(void),
|
||||
getLastActiveSegmentId(void),
|
||||
getTargetFps(void),
|
||||
setPixelSegment(uint8_t n),
|
||||
gamma8(uint8_t),
|
||||
gamma8_cal(uint8_t, float),
|
||||
sin_gap(uint16_t),
|
||||
@@ -709,14 +691,12 @@ class WS2812FX {
|
||||
currentColor(uint32_t colorNew, uint8_t tNr),
|
||||
gamma32(uint32_t),
|
||||
getLastShow(void),
|
||||
getPixelColor(uint16_t),
|
||||
getColor(void);
|
||||
getPixelColor(uint16_t);
|
||||
|
||||
WS2812FX::Segment&
|
||||
getSegment(uint8_t n);
|
||||
|
||||
WS2812FX::Segment_runtime
|
||||
getSegmentRuntime(void);
|
||||
WS2812FX::Segment
|
||||
&getSegment(uint8_t n),
|
||||
&getFirstSelectedSeg(void),
|
||||
&getMainSegment(void);
|
||||
|
||||
WS2812FX::Segment*
|
||||
getSegments(void);
|
||||
@@ -855,9 +835,13 @@ class WS2812FX {
|
||||
uint16_t _usedSegmentData = 0;
|
||||
uint16_t _transitionDur = 750;
|
||||
|
||||
uint8_t _targetFps = 42;
|
||||
uint16_t _frametime = (1000/42);
|
||||
uint16_t _cumulativeFps = 2;
|
||||
|
||||
bool
|
||||
_isOffRefreshRequired = false, //periodic refresh is required for the strip to remain off.
|
||||
_hasWhiteChannel = false,
|
||||
_triggered;
|
||||
|
||||
mode_ptr _mode[MODE_COUNT]; // SRAM footprint: 4 bytes per element
|
||||
@@ -906,6 +890,8 @@ class WS2812FX {
|
||||
|
||||
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}}
|
||||
@@ -919,6 +905,10 @@ class WS2812FX {
|
||||
uint16_t
|
||||
realPixelIndex(uint16_t i),
|
||||
transitionProgress(uint8_t tNr);
|
||||
|
||||
public:
|
||||
inline bool hasWhiteChannel(void) {return _hasWhiteChannel;}
|
||||
inline bool isOffRefreshRequired(void) {return _isOffRefreshRequired;}
|
||||
};
|
||||
|
||||
//10 names per line
|
||||
|
||||
@@ -67,8 +67,13 @@
|
||||
//do not call this method from system context (network callback)
|
||||
void WS2812FX::finalizeInit(void)
|
||||
{
|
||||
RESET_RUNTIME;
|
||||
isRgbw = isOffRefreshRequred = false;
|
||||
//reset segment runtimes
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) {
|
||||
_segment_runtimes[i].markForReset();
|
||||
_segment_runtimes[i].resetIfRequired();
|
||||
}
|
||||
|
||||
_hasWhiteChannel = _isOffRefreshRequired = false;
|
||||
|
||||
//if busses failed to load, add default (fresh install, FS issue, ...)
|
||||
if (busses.getNumBusses() == 0) {
|
||||
@@ -93,9 +98,9 @@ void WS2812FX::finalizeInit(void)
|
||||
if (bus == nullptr) continue;
|
||||
if (bus->getStart() + bus->getLength() > MAX_LEDS) break;
|
||||
//RGBW mode is enabled if at least one of the strips is RGBW
|
||||
isRgbw |= bus->isRgbw();
|
||||
_hasWhiteChannel |= bus->isRgbw();
|
||||
//refresh is required to remain off if at least one of the strips requires the refresh.
|
||||
isOffRefreshRequred |= bus->isOffRefreshRequired();
|
||||
_isOffRefreshRequired |= bus->isOffRefreshRequired();
|
||||
uint16_t busEnd = bus->getStart() + bus->getLength();
|
||||
if (busEnd > _length) _length = busEnd;
|
||||
#ifdef ESP8266
|
||||
@@ -165,12 +170,12 @@ void WS2812FX::service() {
|
||||
_triggered = false;
|
||||
}
|
||||
|
||||
void WS2812FX::setPixelColor(uint16_t n, uint32_t c) {
|
||||
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 WS2812FX::realPixelIndex(uint16_t i) {
|
||||
uint16_t IRAM_ATTR WS2812FX::realPixelIndex(uint16_t i) {
|
||||
int16_t iGroup = i * SEGMENT.groupLength();
|
||||
|
||||
/* reverse just an individual segment */
|
||||
@@ -187,7 +192,7 @@ uint16_t WS2812FX::realPixelIndex(uint16_t i) {
|
||||
return realIndex;
|
||||
}
|
||||
|
||||
void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
|
||||
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);
|
||||
@@ -350,6 +355,15 @@ uint16_t WS2812FX::getFps() {
|
||||
return _cumulativeFps +1;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getTargetFps() {
|
||||
return _targetFps;
|
||||
}
|
||||
|
||||
void WS2812FX::setTargetFps(uint8_t fps) {
|
||||
if (fps > 0 && fps <= 120) _targetFps = fps;
|
||||
_frametime = 1000 / _targetFps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the next frame to be computed on all active segments.
|
||||
*/
|
||||
@@ -364,7 +378,7 @@ void WS2812FX::setMode(uint8_t segid, uint8_t m) {
|
||||
|
||||
if (_segments[segid].mode != m)
|
||||
{
|
||||
_segment_runtimes[segid].reset();
|
||||
_segment_runtimes[segid].markForReset();
|
||||
_segments[segid].mode = m;
|
||||
}
|
||||
}
|
||||
@@ -379,62 +393,28 @@ uint8_t WS2812FX::getPaletteCount()
|
||||
return 13 + GRADIENT_PALETTE_COUNT;
|
||||
}
|
||||
|
||||
//TODO effect transitions
|
||||
|
||||
|
||||
bool WS2812FX::setEffectConfig(uint8_t m, uint8_t s, uint8_t in, uint8_t p) {
|
||||
Segment& seg = _segments[getMainSegmentId()];
|
||||
uint8_t modePrev = seg.mode, speedPrev = seg.speed, intensityPrev = seg.intensity, palettePrev = seg.palette;
|
||||
|
||||
bool applied = false;
|
||||
|
||||
if (applyToAllSelected) {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
if (_segments[i].isSelected())
|
||||
{
|
||||
_segments[i].speed = s;
|
||||
_segments[i].intensity = in;
|
||||
_segments[i].palette = p;
|
||||
setMode(i, m);
|
||||
applied = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!applyToAllSelected || !applied) {
|
||||
seg.speed = s;
|
||||
seg.intensity = in;
|
||||
seg.palette = p;
|
||||
setMode(mainSegment, m);
|
||||
}
|
||||
|
||||
if (seg.mode != modePrev || seg.speed != speedPrev || seg.intensity != intensityPrev || seg.palette != palettePrev) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void WS2812FX::setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
|
||||
setColor(slot, RGBW32(r, g, b, w));
|
||||
}
|
||||
|
||||
//applies to all active and selected segments
|
||||
void WS2812FX::setColor(uint8_t slot, uint32_t c) {
|
||||
if (slot >= NUM_COLORS) return;
|
||||
|
||||
bool applied = false;
|
||||
|
||||
if (applyToAllSelected) {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
if (_segments[i].isSelected()) {
|
||||
_segments[i].setColor(slot, c, i);
|
||||
applied = true;
|
||||
}
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
if (_segments[i].isActive() && _segments[i].isSelected()) {
|
||||
_segments[i].setColor(slot, c, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!applyToAllSelected || !applied) {
|
||||
uint8_t mainseg = getMainSegmentId();
|
||||
_segments[mainseg].setColor(slot, c, mainseg);
|
||||
void WS2812FX::setCCT(uint16_t k) {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
if (_segments[i].isActive() && _segments[i].isSelected()) {
|
||||
_segments[i].setCCT(k, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,14 +432,6 @@ void WS2812FX::setBrightness(uint8_t b) {
|
||||
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::getMode(void) {
|
||||
return _segments[getMainSegmentId()].mode;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getSpeed(void) {
|
||||
return _segments[getMainSegmentId()].speed;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getBrightness(void) {
|
||||
return _brightness;
|
||||
}
|
||||
@@ -468,24 +440,38 @@ uint8_t WS2812FX::getMaxSegments(void) {
|
||||
return MAX_NUM_SEGMENTS;
|
||||
}
|
||||
|
||||
/*uint8_t WS2812FX::getFirstSelectedSegment(void)
|
||||
uint8_t WS2812FX::getFirstSelectedSegId(void)
|
||||
{
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
if (_segments[i].isActive() && _segments[i].isSelected()) return i;
|
||||
}
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) //if none selected, get first active
|
||||
{
|
||||
if (_segments[i].isActive()) return i;
|
||||
// if none selected, use the main segment
|
||||
return getMainSegmentId();
|
||||
}
|
||||
|
||||
void WS2812FX::setMainSegmentId(uint8_t n) {
|
||||
if (n >= MAX_NUM_SEGMENTS) return;
|
||||
//use supplied n if active, or first active
|
||||
if (_segments[n].isActive()) {
|
||||
_mainSegment = n; return;
|
||||
}
|
||||
return 0;
|
||||
}*/
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
if (_segments[i].isActive()) {
|
||||
_mainSegment = i; return;
|
||||
}
|
||||
}
|
||||
_mainSegment = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getMainSegmentId(void) {
|
||||
if (mainSegment >= MAX_NUM_SEGMENTS) return 0;
|
||||
if (_segments[mainSegment].isActive()) return mainSegment;
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) //get first active
|
||||
{
|
||||
return _mainSegment;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getLastActiveSegmentId(void) {
|
||||
for (uint8_t i = MAX_NUM_SEGMENTS -1; i > 0; i--) {
|
||||
if (_segments[i].isActive()) return i;
|
||||
}
|
||||
return 0;
|
||||
@@ -500,10 +486,6 @@ uint8_t WS2812FX::getActiveSegmentsNum(void) {
|
||||
return c;
|
||||
}
|
||||
|
||||
uint32_t WS2812FX::getColor(void) {
|
||||
return _segments[getMainSegmentId()].colors[0];
|
||||
}
|
||||
|
||||
uint32_t WS2812FX::getPixelColor(uint16_t i)
|
||||
{
|
||||
i = realPixelIndex(i);
|
||||
@@ -525,8 +507,12 @@ WS2812FX::Segment& WS2812FX::getSegment(uint8_t id) {
|
||||
return _segments[id];
|
||||
}
|
||||
|
||||
WS2812FX::Segment_runtime WS2812FX::getSegmentRuntime(void) {
|
||||
return SEGENV;
|
||||
WS2812FX::Segment& WS2812FX::getFirstSelectedSeg(void) {
|
||||
return _segments[getFirstSelectedSegId()];
|
||||
}
|
||||
|
||||
WS2812FX::Segment& WS2812FX::getMainSegment(void) {
|
||||
return _segments[getMainSegmentId()];
|
||||
}
|
||||
|
||||
WS2812FX::Segment* WS2812FX::getSegments(void) {
|
||||
@@ -551,6 +537,77 @@ uint16_t WS2812FX::getLengthPhysical(void) {
|
||||
return len;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::Segment::differs(Segment& b) {
|
||||
uint8_t d = 0;
|
||||
if (start != b.start) d |= SEG_DIFFERS_BOUNDS;
|
||||
if (stop != b.stop) d |= SEG_DIFFERS_BOUNDS;
|
||||
if (offset != b.offset) d |= SEG_DIFFERS_GSO;
|
||||
if (grouping != b.grouping) d |= SEG_DIFFERS_GSO;
|
||||
if (spacing != b.spacing) d |= SEG_DIFFERS_GSO;
|
||||
if (opacity != b.opacity) d |= SEG_DIFFERS_BRI;
|
||||
if (mode != b.mode) d |= SEG_DIFFERS_FX;
|
||||
if (speed != b.speed) d |= SEG_DIFFERS_FX;
|
||||
if (intensity != b.intensity) d |= SEG_DIFFERS_FX;
|
||||
if (palette != b.palette) d |= SEG_DIFFERS_FX;
|
||||
|
||||
if ((options & 0b00101110) != (b.options & 0b00101110)) d |= SEG_DIFFERS_OPT;
|
||||
if ((options & 0x01) != (b.options & 0x01)) d |= SEG_DIFFERS_SEL;
|
||||
|
||||
for (uint8_t i = 0; i < NUM_COLORS; i++)
|
||||
{
|
||||
if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL;
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::Segment::getLightCapabilities() {
|
||||
if (!isActive()) return 0;
|
||||
uint8_t capabilities = 0;
|
||||
uint8_t awm = Bus::getAutoWhiteMode();
|
||||
bool whiteSlider = (awm == RGBW_MODE_DUAL || awm == RGBW_MODE_MANUAL_ONLY);
|
||||
|
||||
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
|
||||
Bus *bus = busses.getBus(b);
|
||||
if (bus == nullptr || bus->getLength()==0) break;
|
||||
if (bus->getStart() >= stop) continue;
|
||||
if (bus->getStart() + bus->getLength() <= start) continue;
|
||||
|
||||
uint8_t type = bus->getType();
|
||||
if (!whiteSlider || (type != TYPE_ANALOG_1CH && (cctFromRgb || type != TYPE_ANALOG_2CH)))
|
||||
{
|
||||
capabilities |= 0x01; //segment supports RGB (full color)
|
||||
}
|
||||
if (bus->isRgbw() && whiteSlider) capabilities |= 0x02; //segment supports white channel
|
||||
if (!cctFromRgb) {
|
||||
switch (type) {
|
||||
case TYPE_ANALOG_5CH:
|
||||
case TYPE_ANALOG_2CH:
|
||||
capabilities |= 0x04; //segment supports white CCT
|
||||
}
|
||||
}
|
||||
if (correctWB && type != TYPE_ANALOG_1CH) capabilities |= 0x04; //white balance correction (uses CCT slider)
|
||||
}
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
//used for JSON API info.leds.rgbw. Little practical use, deprecate with info.leds.rgbw.
|
||||
//returns if there is an RGBW bus (supports RGB and White, not only white)
|
||||
//not influenced by auto-white mode, also true if white slider does not affect output white channel
|
||||
bool WS2812FX::hasRGBWBus(void) {
|
||||
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
|
||||
Bus *bus = busses.getBus(b);
|
||||
if (bus == nullptr || bus->getLength()==0) break;
|
||||
switch (bus->getType()) {
|
||||
case TYPE_SK6812_RGBW:
|
||||
case TYPE_TM1814:
|
||||
case TYPE_ANALOG_4CH:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WS2812FX::hasCCTBus(void) {
|
||||
if (cctFromRgb && !correctWB) return false;
|
||||
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
|
||||
@@ -582,17 +639,8 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping,
|
||||
delete[] seg.name;
|
||||
seg.name = nullptr;
|
||||
}
|
||||
if (n == mainSegment) //if main segment is deleted, set first active as main segment
|
||||
{
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
if (_segments[i].isActive()) {
|
||||
mainSegment = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
mainSegment = 0; //should not happen (always at least one active segment)
|
||||
}
|
||||
// if main segment is deleted, set first active as main segment
|
||||
if (n == _mainSegment) setMainSegmentId(0);
|
||||
return;
|
||||
}
|
||||
if (i1 < _length) seg.start = i1;
|
||||
@@ -603,12 +651,18 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping,
|
||||
seg.spacing = spacing;
|
||||
}
|
||||
if (offset < UINT16_MAX) seg.offset = offset;
|
||||
_segment_runtimes[n].reset();
|
||||
_segment_runtimes[n].markForReset();
|
||||
}
|
||||
|
||||
void WS2812FX::restartRuntime() {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) {
|
||||
_segment_runtimes[i].markForReset();
|
||||
}
|
||||
}
|
||||
|
||||
void WS2812FX::resetSegments() {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) if (_segments[i].name) delete _segments[i].name;
|
||||
mainSegment = 0;
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) if (_segments[i].name) delete[] _segments[i].name;
|
||||
_mainSegment = 0;
|
||||
memset(_segments, 0, sizeof(_segments));
|
||||
//memset(_segment_runtimes, 0, sizeof(_segment_runtimes));
|
||||
_segment_index = 0;
|
||||
@@ -633,12 +687,12 @@ void WS2812FX::resetSegments() {
|
||||
_segments[i].cct = 127;
|
||||
_segments[i].speed = DEFAULT_SPEED;
|
||||
_segments[i].intensity = DEFAULT_INTENSITY;
|
||||
_segment_runtimes[i].reset();
|
||||
_segment_runtimes[i].markForReset();
|
||||
}
|
||||
_segment_runtimes[0].reset();
|
||||
_segment_runtimes[0].markForReset();
|
||||
}
|
||||
|
||||
void WS2812FX::makeAutoSegments() {
|
||||
void WS2812FX::makeAutoSegments(bool forceReset) {
|
||||
if (autoSegments) { //make one segment per bus
|
||||
uint16_t segStarts[MAX_NUM_SEGMENTS] = {0};
|
||||
uint16_t segStops [MAX_NUM_SEGMENTS] = {0};
|
||||
@@ -664,8 +718,14 @@ void WS2812FX::makeAutoSegments() {
|
||||
setSegment(i, segStarts[i], segStops[i]);
|
||||
}
|
||||
} else {
|
||||
//expand the main seg to the entire length, but only if there are no other segments
|
||||
//expand the main seg to the entire length, but only if there are no other segments, or reset is forced
|
||||
uint8_t mainSeg = getMainSegmentId();
|
||||
|
||||
if (forceReset) {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) {
|
||||
setSegment(i, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (getActiveSegmentsNum() < 2) {
|
||||
setSegment(mainSeg, 0, _length);
|
||||
@@ -701,36 +761,16 @@ bool WS2812FX::checkSegmentAlignment() {
|
||||
}
|
||||
|
||||
//After this function is called, setPixelColor() will use that segment (offsets, grouping, ... will apply)
|
||||
//Note: If called in an interrupt (e.g. JSON API), it must be reset with "setPixelColor(255)",
|
||||
//Note: If called in an interrupt (e.g. JSON API), original segment must be restored,
|
||||
//otherwise it can lead to a crash on ESP32 because _segment_index is modified while in use by the main thread
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
uint8_t _segment_index_prev = 0;
|
||||
uint16_t _virtualSegmentLength_prev = 0;
|
||||
bool _ps_set = false;
|
||||
#endif
|
||||
|
||||
void WS2812FX::setPixelSegment(uint8_t n)
|
||||
uint8_t WS2812FX::setPixelSegment(uint8_t n)
|
||||
{
|
||||
uint8_t prevSegId = _segment_index;
|
||||
if (n < MAX_NUM_SEGMENTS) {
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (!_ps_set) {
|
||||
_segment_index_prev = _segment_index;
|
||||
_virtualSegmentLength_prev = _virtualSegmentLength;
|
||||
_ps_set = true;
|
||||
}
|
||||
#endif
|
||||
_segment_index = n;
|
||||
_virtualSegmentLength = SEGMENT.virtualLength();
|
||||
} else {
|
||||
_virtualSegmentLength = 0;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (_ps_set) {
|
||||
_segment_index = _segment_index_prev;
|
||||
_virtualSegmentLength = _virtualSegmentLength_prev;
|
||||
_ps_set = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return prevSegId;
|
||||
}
|
||||
|
||||
void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col)
|
||||
@@ -769,7 +809,7 @@ void WS2812FX::setTransitionMode(bool t)
|
||||
/*
|
||||
* color blend function
|
||||
*/
|
||||
uint32_t WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) {
|
||||
uint32_t IRAM_ATTR WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) {
|
||||
if(blend == 0) return color1;
|
||||
uint16_t blendmax = b16 ? 0xFFFF : 0xFF;
|
||||
if(blend == blendmax) return color2;
|
||||
@@ -872,13 +912,13 @@ void WS2812FX::blur(uint8_t blur_amount)
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t WS2812FX::triwave16(uint16_t in)
|
||||
uint16_t IRAM_ATTR WS2812FX::triwave16(uint16_t in)
|
||||
{
|
||||
if (in < 0x8000) return in *2;
|
||||
return 0xFFFF - (in - 0x8000)*2;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::sin_gap(uint16_t in) {
|
||||
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
|
||||
@@ -945,13 +985,13 @@ uint8_t WS2812FX::get_random_wheel_index(uint8_t pos) {
|
||||
}
|
||||
|
||||
|
||||
uint32_t WS2812FX::crgb_to_col(CRGB fastled)
|
||||
uint32_t IRAM_ATTR WS2812FX::crgb_to_col(CRGB fastled)
|
||||
{
|
||||
return RGBW32(fastled.red, fastled.green, fastled.blue, 0);
|
||||
}
|
||||
|
||||
|
||||
CRGB WS2812FX::col_to_crgb(uint32_t color)
|
||||
CRGB IRAM_ATTR WS2812FX::col_to_crgb(uint32_t color)
|
||||
{
|
||||
CRGB fastled_col;
|
||||
fastled_col.red = R(color);
|
||||
@@ -1074,7 +1114,7 @@ void WS2812FX::handle_palette(void)
|
||||
* @param pbri Value to scale the brightness of the returned color by. Default is 255. (no scaling)
|
||||
* @returns Single color from palette
|
||||
*/
|
||||
uint32_t WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri)
|
||||
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) {
|
||||
uint32_t color = SEGCOLOR(mcol);
|
||||
|
||||
@@ -44,7 +44,7 @@ void onAlexaChange(EspalexaDevice* dev)
|
||||
if (bri == 0)
|
||||
{
|
||||
bri = briLast;
|
||||
colorUpdated(CALL_MODE_ALEXA);
|
||||
stateUpdated(CALL_MODE_ALEXA);
|
||||
}
|
||||
} else {
|
||||
applyPreset(macroAlexaOn, CALL_MODE_ALEXA);
|
||||
@@ -58,7 +58,7 @@ void onAlexaChange(EspalexaDevice* dev)
|
||||
{
|
||||
briLast = bri;
|
||||
bri = 0;
|
||||
colorUpdated(CALL_MODE_ALEXA);
|
||||
stateUpdated(CALL_MODE_ALEXA);
|
||||
}
|
||||
} else {
|
||||
applyPreset(macroAlexaOff, CALL_MODE_ALEXA);
|
||||
@@ -67,43 +67,37 @@ void onAlexaChange(EspalexaDevice* dev)
|
||||
} else if (m == EspalexaDeviceProperty::bri)
|
||||
{
|
||||
bri = espalexaDevice->getValue();
|
||||
colorUpdated(CALL_MODE_ALEXA);
|
||||
stateUpdated(CALL_MODE_ALEXA);
|
||||
} else //color
|
||||
{
|
||||
if (espalexaDevice->getColorMode() == EspalexaColorMode::ct) //shade of white
|
||||
{
|
||||
byte rgbw[4];
|
||||
uint16_t ct = espalexaDevice->getCt();
|
||||
if (!ct) return;
|
||||
uint16_t k = 1000000 / ct; //mireds to kelvin
|
||||
|
||||
if (strip.hasCCTBus()) {
|
||||
uint8_t segid = strip.getMainSegmentId();
|
||||
WS2812FX::Segment& seg = strip.getSegment(segid);
|
||||
uint8_t cctPrev = seg.cct;
|
||||
seg.setCCT(k, segid);
|
||||
if (seg.cct != cctPrev) effectChanged = true; //send UDP
|
||||
col[0]= 0; col[1]= 0; col[2]= 0; col[3]= 255;
|
||||
} else if (strip.isRgbw) {
|
||||
strip.setCCT(k);
|
||||
rgbw[0]= 0; rgbw[1]= 0; rgbw[2]= 0; rgbw[3]= 255;
|
||||
} else if (strip.hasWhiteChannel()) {
|
||||
switch (ct) { //these values empirically look good on RGBW
|
||||
case 199: col[0]=255; col[1]=255; col[2]=255; col[3]=255; break;
|
||||
case 234: col[0]=127; col[1]=127; col[2]=127; col[3]=255; break;
|
||||
case 284: col[0]= 0; col[1]= 0; col[2]= 0; col[3]=255; break;
|
||||
case 350: col[0]=130; col[1]= 90; col[2]= 0; col[3]=255; break;
|
||||
case 383: col[0]=255; col[1]=153; col[2]= 0; col[3]=255; break;
|
||||
default : colorKtoRGB(k, col);
|
||||
case 199: rgbw[0]=255; rgbw[1]=255; rgbw[2]=255; rgbw[3]=255; break;
|
||||
case 234: rgbw[0]=127; rgbw[1]=127; rgbw[2]=127; rgbw[3]=255; break;
|
||||
case 284: rgbw[0]= 0; rgbw[1]= 0; rgbw[2]= 0; rgbw[3]=255; break;
|
||||
case 350: rgbw[0]=130; rgbw[1]= 90; rgbw[2]= 0; rgbw[3]=255; break;
|
||||
case 383: rgbw[0]=255; rgbw[1]=153; rgbw[2]= 0; rgbw[3]=255; break;
|
||||
default : colorKtoRGB(k, rgbw);
|
||||
}
|
||||
} else {
|
||||
colorKtoRGB(k, col);
|
||||
colorKtoRGB(k, rgbw);
|
||||
}
|
||||
strip.setColor(0, rgbw[0], rgbw[1], rgbw[2], rgbw[3]);
|
||||
} else {
|
||||
uint32_t color = espalexaDevice->getRGB();
|
||||
|
||||
col[0] = ((color >> 16) & 0xFF);
|
||||
col[1] = ((color >> 8) & 0xFF);
|
||||
col[2] = ( color & 0xFF);
|
||||
col[3] = 0;
|
||||
strip.setColor(0, color);
|
||||
}
|
||||
colorUpdated(CALL_MODE_ALEXA);
|
||||
stateUpdated(CALL_MODE_ALEXA);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,27 +44,27 @@ void updateBlynk()
|
||||
BLYNK_WRITE(V0)
|
||||
{
|
||||
bri = param.asInt();//bri
|
||||
colorUpdated(CALL_MODE_BLYNK);
|
||||
stateUpdated(CALL_MODE_BLYNK);
|
||||
}
|
||||
|
||||
BLYNK_WRITE(V1)
|
||||
{
|
||||
blHue = param.asInt();//hue
|
||||
colorHStoRGB(blHue*10,blSat,(false)? colSec:col);
|
||||
colorHStoRGB(blHue*10,blSat,col);
|
||||
colorUpdated(CALL_MODE_BLYNK);
|
||||
}
|
||||
|
||||
BLYNK_WRITE(V2)
|
||||
{
|
||||
blSat = param.asInt();//sat
|
||||
colorHStoRGB(blHue*10,blSat,(false)? colSec:col);
|
||||
colorHStoRGB(blHue*10,blSat,col);
|
||||
colorUpdated(CALL_MODE_BLYNK);
|
||||
}
|
||||
|
||||
BLYNK_WRITE(V3)
|
||||
{
|
||||
bool on = (param.asInt()>0);
|
||||
if (!on != !bri) {toggleOnOff(); colorUpdated(CALL_MODE_BLYNK);}
|
||||
if (!on != !bri) {toggleOnOff(); stateUpdated(CALL_MODE_BLYNK);}
|
||||
}
|
||||
|
||||
BLYNK_WRITE(V4)
|
||||
|
||||
@@ -73,6 +73,62 @@ struct BusConfig {
|
||||
}
|
||||
};
|
||||
|
||||
// Defines an LED Strip and its color ordering.
|
||||
struct ColorOrderMapEntry {
|
||||
uint16_t start;
|
||||
uint16_t len;
|
||||
uint8_t colorOrder;
|
||||
};
|
||||
|
||||
struct ColorOrderMap {
|
||||
void add(uint16_t start, uint16_t len, uint8_t colorOrder) {
|
||||
if (_count >= WLED_MAX_COLOR_ORDER_MAPPINGS) {
|
||||
return;
|
||||
}
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
if (colorOrder > COL_ORDER_MAX) {
|
||||
return;
|
||||
}
|
||||
_mappings[_count].start = start;
|
||||
_mappings[_count].len = len;
|
||||
_mappings[_count].colorOrder = colorOrder;
|
||||
_count++;
|
||||
}
|
||||
|
||||
uint8_t count() const {
|
||||
return _count;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
_count = 0;
|
||||
memset(_mappings, 0, sizeof(_mappings));
|
||||
}
|
||||
|
||||
const ColorOrderMapEntry* get(uint8_t n) const {
|
||||
if (n > _count) {
|
||||
return nullptr;
|
||||
}
|
||||
return &(_mappings[n]);
|
||||
}
|
||||
|
||||
inline uint8_t IRAM_ATTR getPixelColorOrder(uint16_t pix, uint8_t defaultColorOrder) const {
|
||||
if (_count == 0) return defaultColorOrder;
|
||||
|
||||
for (uint8_t i = 0; i < _count; i++) {
|
||||
if (pix >= _mappings[i].start && pix < (_mappings[i].start + _mappings[i].len)) {
|
||||
return _mappings[i].colorOrder;
|
||||
}
|
||||
}
|
||||
return defaultColorOrder;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t _count;
|
||||
ColorOrderMapEntry _mappings[WLED_MAX_COLOR_ORDER_MAPPINGS];
|
||||
};
|
||||
|
||||
//parent class of BusDigital, BusPwm, and BusNetwork
|
||||
class Bus {
|
||||
public:
|
||||
@@ -91,7 +147,7 @@ class Bus {
|
||||
virtual void setBrightness(uint8_t b) {}
|
||||
virtual void cleanup() {}
|
||||
virtual uint8_t getPins(uint8_t* pinArray) { return 0; }
|
||||
inline uint16_t getLength() { return _len; }
|
||||
virtual uint16_t getLength() { return _len; }
|
||||
virtual void setColorOrder() {}
|
||||
virtual uint8_t getColorOrder() { return COL_ORDER_RGB; }
|
||||
virtual uint8_t skippedLeds() { return 0; }
|
||||
@@ -152,7 +208,7 @@ class Bus {
|
||||
|
||||
class BusDigital : public Bus {
|
||||
public:
|
||||
BusDigital(BusConfig &bc, uint8_t nr) : Bus(bc.type, bc.start) {
|
||||
BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) : Bus(bc.type, bc.start), _colorOrderMap(com) {
|
||||
if (!IS_DIGITAL(bc.type) || !bc.count) return;
|
||||
if (!pinManager.allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return;
|
||||
_pins[0] = bc.pins[0];
|
||||
@@ -197,7 +253,7 @@ class BusDigital : public Bus {
|
||||
//TODO only show if no new show due in the next 50ms
|
||||
void setStatusPixel(uint32_t c) {
|
||||
if (_skip && canShow()) {
|
||||
PolyBus::setPixelColor(_busPtr, _iType, 0, c, _colorOrder);
|
||||
PolyBus::setPixelColor(_busPtr, _iType, 0, c, _colorOrderMap.getPixelColorOrder(_start, _colorOrder));
|
||||
PolyBus::show(_busPtr, _iType);
|
||||
}
|
||||
}
|
||||
@@ -207,20 +263,20 @@ class BusDigital : public Bus {
|
||||
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
|
||||
if (reversed) pix = _len - pix -1;
|
||||
else pix += _skip;
|
||||
PolyBus::setPixelColor(_busPtr, _iType, pix, c, _colorOrder);
|
||||
PolyBus::setPixelColor(_busPtr, _iType, pix, c, _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder));
|
||||
}
|
||||
|
||||
uint32_t getPixelColor(uint16_t pix) {
|
||||
if (reversed) pix = _len - pix -1;
|
||||
else pix += _skip;
|
||||
return PolyBus::getPixelColor(_busPtr, _iType, pix, _colorOrder);
|
||||
return PolyBus::getPixelColor(_busPtr, _iType, pix, _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder));
|
||||
}
|
||||
|
||||
inline uint8_t getColorOrder() {
|
||||
return _colorOrder;
|
||||
}
|
||||
|
||||
inline uint16_t getLength() {
|
||||
uint16_t getLength() {
|
||||
return _len - _skip;
|
||||
}
|
||||
|
||||
@@ -263,6 +319,7 @@ class BusDigital : public Bus {
|
||||
uint8_t _iType = I_NONE;
|
||||
uint8_t _skip = 0;
|
||||
void * _busPtr = nullptr;
|
||||
const ColorOrderMap &_colorOrderMap;
|
||||
};
|
||||
|
||||
|
||||
@@ -288,7 +345,7 @@ class BusPwm : public Bus {
|
||||
if (!pinManager.allocatePin(currentPin, true, PinOwner::BusPwm)) {
|
||||
deallocatePins(); return;
|
||||
}
|
||||
_pins[i] = currentPin; // store only after allocatePin() succeeds
|
||||
_pins[i] = currentPin; //store only after allocatePin() succeeds
|
||||
#ifdef ESP8266
|
||||
pinMode(_pins[i], OUTPUT);
|
||||
#else
|
||||
@@ -397,7 +454,7 @@ class BusPwm : public Bus {
|
||||
|
||||
private:
|
||||
uint8_t _pins[5] = {255, 255, 255, 255, 255};
|
||||
uint8_t _data[5] = {255, 255, 255, 255, 255};
|
||||
uint8_t _data[5] = {0};
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
uint8_t _ledcStart = 255;
|
||||
#endif
|
||||
@@ -555,7 +612,7 @@ class BusManager {
|
||||
if (bc.type >= TYPE_NET_DDP_RGB && bc.type < 96) {
|
||||
busses[numBusses] = new BusNetwork(bc);
|
||||
} else if (IS_DIGITAL(bc.type)) {
|
||||
busses[numBusses] = new BusDigital(bc, numBusses);
|
||||
busses[numBusses] = new BusDigital(bc, numBusses, colorOrderMap);
|
||||
} else {
|
||||
busses[numBusses] = new BusPwm(bc);
|
||||
}
|
||||
@@ -583,7 +640,7 @@ class BusManager {
|
||||
}
|
||||
}
|
||||
|
||||
void setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1) {
|
||||
void IRAM_ATTR setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1) {
|
||||
for (uint8_t i = 0; i < numBusses; i++) {
|
||||
Bus* b = busses[i];
|
||||
uint16_t bstart = b->getStart();
|
||||
@@ -640,8 +697,17 @@ class BusManager {
|
||||
return len;
|
||||
}
|
||||
|
||||
void updateColorOrderMap(const ColorOrderMap &com) {
|
||||
memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap));
|
||||
}
|
||||
|
||||
const ColorOrderMap& getColorOrderMap() const {
|
||||
return colorOrderMap;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t numBusses = 0;
|
||||
Bus* busses[WLED_MAX_BUSSES];
|
||||
ColorOrderMap colorOrderMap;
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -98,26 +98,34 @@
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
//RGB
|
||||
#define B_32_RN_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32RmtNWs2812xMethod>
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#define B_32_I0_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s0800KbpsMethod>
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#define B_32_I1_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s1800KbpsMethod>
|
||||
#endif
|
||||
//RGBW
|
||||
#define B_32_RN_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32RmtNWs2812xMethod>
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#define B_32_I0_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32I2s0800KbpsMethod>
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#define B_32_I1_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32I2s1800KbpsMethod>
|
||||
#endif
|
||||
//400Kbps
|
||||
#define B_32_RN_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32RmtN400KbpsMethod>
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#define B_32_I0_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s0400KbpsMethod>
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#define B_32_I1_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s1400KbpsMethod>
|
||||
#endif
|
||||
//TM1814 (RGBW)
|
||||
#define B_32_RN_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32RmtNTm1814Method>
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#define B_32_I0_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32I2s0Tm1814Method>
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#define B_32_I1_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32I2s1Tm1814Method>
|
||||
#endif
|
||||
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
|
||||
@@ -181,23 +189,31 @@ class PolyBus {
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Begin(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->Begin(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->Begin(); break;
|
||||
#endif
|
||||
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->Begin(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->Begin(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->Begin(); break;
|
||||
#endif
|
||||
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->Begin(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->Begin(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->Begin(); break;
|
||||
#endif
|
||||
case I_32_RN_TM1_4: beginTM1814<B_32_RN_TM1_4*>(busPtr); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_TM1_4: beginTM1814<B_32_I0_TM1_4*>(busPtr); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_TM1_4: beginTM1814<B_32_I1_TM1_4*>(busPtr); break;
|
||||
#endif
|
||||
// ESP32 can (and should, to avoid inadvertantly driving the chip select signal) specify the pins used for SPI, but only in begin()
|
||||
@@ -236,23 +252,31 @@ class PolyBus {
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_NEO_3: busPtr = new B_32_I0_NEO_3(len, pins[0]); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_NEO_3: busPtr = new B_32_I1_NEO_3(len, pins[0]); break;
|
||||
#endif
|
||||
case I_32_RN_NEO_4: busPtr = new B_32_RN_NEO_4(len, pins[0], (NeoBusChannel)channel); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_NEO_4: busPtr = new B_32_I0_NEO_4(len, pins[0]); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_NEO_4: busPtr = new B_32_I1_NEO_4(len, pins[0]); break;
|
||||
#endif
|
||||
case I_32_RN_400_3: busPtr = new B_32_RN_400_3(len, pins[0], (NeoBusChannel)channel); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_400_3: busPtr = new B_32_I0_400_3(len, pins[0]); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_400_3: busPtr = new B_32_I1_400_3(len, pins[0]); break;
|
||||
#endif
|
||||
case I_32_RN_TM1_4: busPtr = new B_32_RN_TM1_4(len, pins[0], (NeoBusChannel)channel); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_TM1_4: busPtr = new B_32_I0_TM1_4(len, pins[0]); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_TM1_4: busPtr = new B_32_I1_TM1_4(len, pins[0]); break;
|
||||
#endif
|
||||
#endif
|
||||
@@ -292,23 +316,31 @@ class PolyBus {
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Show(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->Show(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->Show(); break;
|
||||
#endif
|
||||
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->Show(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->Show(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->Show(); break;
|
||||
#endif
|
||||
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->Show(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->Show(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->Show(); break;
|
||||
#endif
|
||||
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->Show(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->Show(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->Show(); break;
|
||||
#endif
|
||||
#endif
|
||||
@@ -345,23 +377,31 @@ class PolyBus {
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: return (static_cast<B_32_RN_NEO_3*>(busPtr))->CanShow(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_NEO_3: return (static_cast<B_32_I0_NEO_3*>(busPtr))->CanShow(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_NEO_3: return (static_cast<B_32_I1_NEO_3*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
case I_32_RN_NEO_4: return (static_cast<B_32_RN_NEO_4*>(busPtr))->CanShow(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_NEO_4: return (static_cast<B_32_I0_NEO_4*>(busPtr))->CanShow(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_NEO_4: return (static_cast<B_32_I1_NEO_4*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
case I_32_RN_400_3: return (static_cast<B_32_RN_400_3*>(busPtr))->CanShow(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_400_3: return (static_cast<B_32_I0_400_3*>(busPtr))->CanShow(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_400_3: return (static_cast<B_32_I1_400_3*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
case I_32_RN_TM1_4: return (static_cast<B_32_RN_TM1_4*>(busPtr))->CanShow(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_TM1_4: return (static_cast<B_32_I0_TM1_4*>(busPtr))->CanShow(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_TM1_4: return (static_cast<B_32_I1_TM1_4*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
#endif
|
||||
@@ -422,23 +462,31 @@ class PolyBus {
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
#endif
|
||||
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
#endif
|
||||
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
#endif
|
||||
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
#endif
|
||||
#endif
|
||||
@@ -475,23 +523,31 @@ class PolyBus {
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetBrightness(b); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->SetBrightness(b); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetBrightness(b); break;
|
||||
#endif
|
||||
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetBrightness(b); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetBrightness(b); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetBrightness(b); break;
|
||||
#endif
|
||||
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetBrightness(b); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetBrightness(b); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetBrightness(b); break;
|
||||
#endif
|
||||
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetBrightness(b); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetBrightness(b); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetBrightness(b); break;
|
||||
#endif
|
||||
#endif
|
||||
@@ -529,23 +585,31 @@ class PolyBus {
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: col = (static_cast<B_32_RN_NEO_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_NEO_3: col = (static_cast<B_32_I0_NEO_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_NEO_3: col = (static_cast<B_32_I1_NEO_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#endif
|
||||
case I_32_RN_NEO_4: col = (static_cast<B_32_RN_NEO_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_NEO_4: col = (static_cast<B_32_I0_NEO_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_NEO_4: col = (static_cast<B_32_I1_NEO_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
#endif
|
||||
case I_32_RN_400_3: col = (static_cast<B_32_RN_400_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_400_3: col = (static_cast<B_32_I0_400_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_400_3: col = (static_cast<B_32_I1_400_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#endif
|
||||
case I_32_RN_TM1_4: col = (static_cast<B_32_RN_TM1_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_TM1_4: col = (static_cast<B_32_I0_TM1_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_TM1_4: col = (static_cast<B_32_I1_TM1_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
#endif
|
||||
#endif
|
||||
@@ -600,23 +664,31 @@ class PolyBus {
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: delete (static_cast<B_32_RN_NEO_3*>(busPtr)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_NEO_3: delete (static_cast<B_32_I0_NEO_3*>(busPtr)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_NEO_3: delete (static_cast<B_32_I1_NEO_3*>(busPtr)); break;
|
||||
#endif
|
||||
case I_32_RN_NEO_4: delete (static_cast<B_32_RN_NEO_4*>(busPtr)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_NEO_4: delete (static_cast<B_32_I0_NEO_4*>(busPtr)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_NEO_4: delete (static_cast<B_32_I1_NEO_4*>(busPtr)); break;
|
||||
#endif
|
||||
case I_32_RN_400_3: delete (static_cast<B_32_RN_400_3*>(busPtr)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_400_3: delete (static_cast<B_32_I0_400_3*>(busPtr)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_400_3: delete (static_cast<B_32_I1_400_3*>(busPtr)); break;
|
||||
#endif
|
||||
case I_32_RN_TM1_4: delete (static_cast<B_32_RN_TM1_4*>(busPtr)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
case I_32_I0_TM1_4: delete (static_cast<B_32_I0_TM1_4*>(busPtr)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
case I_32_I1_TM1_4: delete (static_cast<B_32_I1_TM1_4*>(busPtr)); break;
|
||||
#endif
|
||||
#endif
|
||||
@@ -692,4 +764,4 @@ class PolyBus {
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -16,11 +16,11 @@ void shortPressAction(uint8_t b)
|
||||
{
|
||||
if (!macroButton[b]) {
|
||||
switch (b) {
|
||||
case 0: toggleOnOff(); colorUpdated(CALL_MODE_BUTTON); break;
|
||||
default: ++effectCurrent %= strip.getModeCount(); colorUpdated(CALL_MODE_BUTTON); break;
|
||||
case 0: toggleOnOff(); stateUpdated(CALL_MODE_BUTTON); break;
|
||||
case 1: ++effectCurrent %= strip.getModeCount(); colorUpdated(CALL_MODE_BUTTON); break;
|
||||
}
|
||||
} else {
|
||||
applyPreset(macroButton[b], CALL_MODE_BUTTON);
|
||||
applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET);
|
||||
}
|
||||
|
||||
// publish MQTT message
|
||||
@@ -35,11 +35,11 @@ void longPressAction(uint8_t b)
|
||||
{
|
||||
if (!macroLongPress[b]) {
|
||||
switch (b) {
|
||||
case 0: _setRandomColor(false,true); break;
|
||||
default: bri += 8; colorUpdated(CALL_MODE_BUTTON); buttonPressedTime[b] = millis(); break; // repeatable action
|
||||
case 0: setRandomColor(col); colorUpdated(CALL_MODE_BUTTON); break;
|
||||
case 1: bri += 8; stateUpdated(CALL_MODE_BUTTON); buttonPressedTime[b] = millis(); break; // repeatable action
|
||||
}
|
||||
} else {
|
||||
applyPreset(macroLongPress[b], CALL_MODE_BUTTON);
|
||||
applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET);
|
||||
}
|
||||
|
||||
// publish MQTT message
|
||||
@@ -55,10 +55,10 @@ void doublePressAction(uint8_t b)
|
||||
if (!macroDoublePress[b]) {
|
||||
switch (b) {
|
||||
//case 0: toggleOnOff(); colorUpdated(CALL_MODE_BUTTON); break; //instant short press on button 0 if no macro set
|
||||
default: ++effectPalette %= strip.getPaletteCount(); colorUpdated(CALL_MODE_BUTTON); break;
|
||||
case 1: ++effectPalette %= strip.getPaletteCount(); colorUpdated(CALL_MODE_BUTTON); break;
|
||||
}
|
||||
} else {
|
||||
applyPreset(macroDoublePress[b], CALL_MODE_BUTTON);
|
||||
applyPreset(macroDoublePress[b], CALL_MODE_BUTTON_PRESET);
|
||||
}
|
||||
|
||||
// publish MQTT message
|
||||
@@ -72,21 +72,23 @@ void doublePressAction(uint8_t b)
|
||||
bool isButtonPressed(uint8_t i)
|
||||
{
|
||||
if (btnPin[i]<0) return false;
|
||||
uint8_t pin = btnPin[i];
|
||||
|
||||
switch (buttonType[i]) {
|
||||
case BTN_TYPE_NONE:
|
||||
case BTN_TYPE_RESERVED:
|
||||
break;
|
||||
case BTN_TYPE_PUSH:
|
||||
case BTN_TYPE_SWITCH:
|
||||
if (digitalRead(btnPin[i]) == LOW) return true;
|
||||
if (digitalRead(pin) == LOW) return true;
|
||||
break;
|
||||
case BTN_TYPE_PUSH_ACT_HIGH:
|
||||
case BTN_TYPE_PIR_SENSOR:
|
||||
if (digitalRead(btnPin[i]) == HIGH) return true;
|
||||
if (digitalRead(pin) == HIGH) return true;
|
||||
break;
|
||||
case BTN_TYPE_TOUCH:
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (touchRead(btnPin[i]) <= touchThreshold) return true;
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
if (touchRead(pin) <= touchThreshold) return true;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -105,14 +107,14 @@ void handleSwitch(uint8_t b)
|
||||
|
||||
if (millis() - buttonPressedTime[b] > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce)
|
||||
if (!buttonPressedBefore[b]) { // on -> off
|
||||
if (macroButton[b]) applyPreset(macroButton[b], CALL_MODE_BUTTON);
|
||||
if (macroButton[b]) applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET);
|
||||
else { //turn on
|
||||
if (!bri) {toggleOnOff(); colorUpdated(CALL_MODE_BUTTON);}
|
||||
if (!bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);}
|
||||
}
|
||||
} else { // off -> on
|
||||
if (macroLongPress[b]) applyPreset(macroLongPress[b], CALL_MODE_BUTTON);
|
||||
if (macroLongPress[b]) applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET);
|
||||
else { //turn off
|
||||
if (bri) {toggleOnOff(); colorUpdated(CALL_MODE_BUTTON);}
|
||||
if (bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,36 +161,17 @@ void handleAnalog(uint8_t b)
|
||||
} else if (macroDoublePress[b] == 249) {
|
||||
// effect speed
|
||||
effectSpeed = aRead;
|
||||
effectChanged = true;
|
||||
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
|
||||
WS2812FX::Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isSelected()) continue;
|
||||
seg.speed = effectSpeed;
|
||||
}
|
||||
} else if (macroDoublePress[b] == 248) {
|
||||
// effect intensity
|
||||
effectIntensity = aRead;
|
||||
effectChanged = true;
|
||||
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
|
||||
WS2812FX::Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isSelected()) continue;
|
||||
seg.intensity = effectIntensity;
|
||||
}
|
||||
} else if (macroDoublePress[b] == 247) {
|
||||
// selected palette
|
||||
effectPalette = map(aRead, 0, 252, 0, strip.getPaletteCount()-1);
|
||||
effectChanged = true;
|
||||
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
|
||||
WS2812FX::Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isSelected()) continue;
|
||||
seg.palette = effectPalette;
|
||||
}
|
||||
} else if (macroDoublePress[b] == 200) {
|
||||
// primary color, hue, full saturation
|
||||
colorHStoRGB(aRead*256,255,col);
|
||||
} else {
|
||||
// otherwise use "double press" for segment selection
|
||||
//uint8_t mainSeg = strip.getMainSegmentId();
|
||||
WS2812FX::Segment& seg = strip.getSegment(macroDoublePress[b]);
|
||||
if (aRead == 0) {
|
||||
seg.setOption(SEG_OPTION_ON, 0); // off
|
||||
@@ -210,6 +193,7 @@ void handleAnalog(uint8_t b)
|
||||
void handleButton()
|
||||
{
|
||||
static unsigned long lastRead = 0UL;
|
||||
bool analog = false;
|
||||
|
||||
for (uint8_t b=0; b<WLED_MAX_BUTTONS; b++) {
|
||||
#ifdef ESP8266
|
||||
@@ -221,7 +205,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 (b+1 == WLED_MAX_BUTTONS) lastRead = millis();
|
||||
analog = true;
|
||||
handleAnalog(b); continue;
|
||||
}
|
||||
|
||||
@@ -255,7 +239,7 @@ void handleButton()
|
||||
if (b == 0 && dur > WLED_LONG_AP) { //long press on button 0 (when released)
|
||||
WLED::instance().initAP(true);
|
||||
} else if (!buttonLongPressed[b]) { //short press
|
||||
if (b == 0 && !macroDoublePress[b]) { //don't wait for double press on button 0 if no double press macro set
|
||||
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);
|
||||
} else { //double press if less than 350 ms between current press and previous short press release (buttonWaitTime!=0)
|
||||
if (doublePress) {
|
||||
@@ -275,6 +259,7 @@ void handleButton()
|
||||
shortPressAction(b);
|
||||
}
|
||||
}
|
||||
if (analog) lastRead = millis();
|
||||
}
|
||||
|
||||
void handleIO()
|
||||
@@ -300,7 +285,7 @@ void handleIO()
|
||||
// turn off built-in LED if strip is turned off
|
||||
// this will break digital bus so will need to be reinitialised on On
|
||||
PinOwner ledPinOwner = pinManager.getPinOwner(LED_BUILTIN);
|
||||
if (!strip.isOffRefreshRequred && (ledPinOwner == PinOwner::None || ledPinOwner == PinOwner::BusDigital)) {
|
||||
if (!strip.isOffRefreshRequired() && (ledPinOwner == PinOwner::None || ledPinOwner == PinOwner::BusDigital)) {
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
}
|
||||
@@ -312,4 +297,4 @@ void handleIO()
|
||||
}
|
||||
offMode = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
JsonObject hw = doc[F("hw")];
|
||||
|
||||
// initialize LED pins and lengths prior to other HW (except for ethernet)
|
||||
JsonObject hw_led = hw[F("led")];
|
||||
JsonObject hw_led = hw["led"];
|
||||
|
||||
CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]);
|
||||
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]);
|
||||
@@ -85,6 +85,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
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
|
||||
|
||||
JsonArray ins = hw_led["ins"];
|
||||
|
||||
@@ -104,7 +105,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
if (i>4) break;
|
||||
}
|
||||
|
||||
uint16_t length = elm[F("len")] | 1;
|
||||
uint16_t length = elm["len"] | 1;
|
||||
uint8_t colorOrder = (int)elm[F("order")];
|
||||
uint8_t skipFirst = elm[F("skip")];
|
||||
uint16_t start = elm["start"] | 0;
|
||||
@@ -122,6 +123,22 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
}
|
||||
if (hw_led["rev"]) busses.getBus(0)->reversed = true; //set 0.11 global reversed setting for first bus
|
||||
|
||||
// read color order map configuration
|
||||
JsonArray hw_com = hw[F("com")];
|
||||
if (!hw_com.isNull()) {
|
||||
ColorOrderMap com = {};
|
||||
uint8_t s = 0;
|
||||
for (JsonObject entry : hw_com) {
|
||||
if (s > WLED_MAX_COLOR_ORDER_MAPPINGS) break;
|
||||
uint16_t start = entry["start"] | 0;
|
||||
uint16_t len = entry["len"] | 0;
|
||||
uint8_t colorOrder = (int)entry[F("order")];
|
||||
com.add(start, len, colorOrder);
|
||||
s++;
|
||||
}
|
||||
busses.updateColorOrderMap(com);
|
||||
}
|
||||
|
||||
// read multiple button configuration
|
||||
JsonObject btn_obj = hw["btn"];
|
||||
JsonArray hw_btn_ins = btn_obj[F("ins")];
|
||||
@@ -194,6 +211,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
rlyMde = !relay["rev"];
|
||||
}
|
||||
|
||||
CJSON(serialBaud, hw[F("baud")]);
|
||||
if (serialBaud < 96 || serialBaud > 15000) serialBaud = 1152;
|
||||
updateBaudRate(serialBaud *100);
|
||||
|
||||
//int hw_status_pin = hw[F("status")]["pin"]; // -1
|
||||
|
||||
JsonObject light = doc[F("light")];
|
||||
@@ -208,29 +229,29 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
if (light_gc_col > 1.5) strip.gammaCorrectCol = true;
|
||||
else if (light_gc_col > 0.5) strip.gammaCorrectCol = false;
|
||||
|
||||
JsonObject light_tr = light[F("tr")];
|
||||
CJSON(fadeTransition, light_tr[F("mode")]);
|
||||
JsonObject light_tr = light["tr"];
|
||||
CJSON(fadeTransition, light_tr["mode"]);
|
||||
int tdd = light_tr["dur"] | -1;
|
||||
if (tdd >= 0) transitionDelayDefault = tdd * 100;
|
||||
CJSON(strip.paletteFade, light_tr["pal"]);
|
||||
|
||||
JsonObject light_nl = light["nl"];
|
||||
CJSON(nightlightMode, light_nl[F("mode")]);
|
||||
CJSON(nightlightMode, light_nl["mode"]);
|
||||
byte prev = nightlightDelayMinsDefault;
|
||||
CJSON(nightlightDelayMinsDefault, light_nl[F("dur")]);
|
||||
CJSON(nightlightDelayMinsDefault, light_nl["dur"]);
|
||||
if (nightlightDelayMinsDefault != prev) nightlightDelayMins = nightlightDelayMinsDefault;
|
||||
|
||||
CJSON(nightlightTargetBri, light_nl[F("tbri")]);
|
||||
CJSON(macroNl, light_nl["macro"]);
|
||||
|
||||
JsonObject def = doc[F("def")];
|
||||
JsonObject def = doc["def"];
|
||||
CJSON(bootPreset, def["ps"]);
|
||||
CJSON(turnOnAtBoot, def["on"]); // true
|
||||
CJSON(briS, def["bri"]); // 128
|
||||
|
||||
JsonObject interfaces = doc["if"];
|
||||
|
||||
JsonObject if_sync = interfaces[F("sync")];
|
||||
JsonObject if_sync = interfaces["sync"];
|
||||
CJSON(udpPort, if_sync[F("port0")]); // 21324
|
||||
CJSON(udpPort2, if_sync[F("port1")]); // 65506
|
||||
|
||||
@@ -239,8 +260,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
CJSON(receiveNotificationColor, if_sync_recv["col"]);
|
||||
CJSON(receiveNotificationEffects, if_sync_recv["fx"]);
|
||||
CJSON(receiveGroups, if_sync_recv["grp"]);
|
||||
CJSON(receiveSegmentOptions, if_sync_recv["seg"]);
|
||||
CJSON(receiveSegmentBounds, if_sync_recv["sb"]);
|
||||
//! following line might be a problem if called after boot
|
||||
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
|
||||
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects || receiveSegmentOptions);
|
||||
|
||||
JsonObject if_sync_send = if_sync["send"];
|
||||
prev = notifyDirectDefault;
|
||||
@@ -267,7 +290,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
CJSON(e131Universe, if_live_dmx[F("uni")]);
|
||||
CJSON(e131SkipOutOfSequence, if_live_dmx[F("seqskip")]);
|
||||
CJSON(DMXAddress, if_live_dmx[F("addr")]);
|
||||
CJSON(DMXMode, if_live_dmx[F("mode")]);
|
||||
CJSON(DMXMode, if_live_dmx["mode"]);
|
||||
|
||||
tdd = if_live[F("timeout")] | -1;
|
||||
if (tdd >= 0) realtimeTimeoutMs = tdd * 100;
|
||||
@@ -365,7 +388,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
CJSON(timerMinutes[it], timer["min"]);
|
||||
CJSON(timerMacro[it], timer["macro"]);
|
||||
|
||||
byte dowPrev = timerWeekday[it];
|
||||
byte dowPrev = timerWeekday[it];
|
||||
//note: act is currently only 0 or 1.
|
||||
//the reason we are not using bool is that the on-disk type in 0.11.0 was already int
|
||||
int actPrev = timerWeekday[it] & 0x01;
|
||||
@@ -375,7 +398,17 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
int act = timer["en"] | actPrev;
|
||||
if (act) timerWeekday[it]++;
|
||||
}
|
||||
|
||||
if (it<8) {
|
||||
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
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
@@ -539,6 +572,7 @@ void serializeConfig() {
|
||||
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();
|
||||
|
||||
JsonArray hw_led_ins = hw_led.createNestedArray("ins");
|
||||
@@ -548,7 +582,7 @@ void serializeConfig() {
|
||||
if (!bus || bus->getLength()==0) break;
|
||||
JsonObject ins = hw_led_ins.createNestedObject();
|
||||
ins["start"] = bus->getStart();
|
||||
ins[F("len")] = bus->getLength();
|
||||
ins["len"] = bus->getLength();
|
||||
JsonArray ins_pin = ins.createNestedArray("pin");
|
||||
uint8_t pins[5];
|
||||
uint8_t nPins = bus->getPins(pins);
|
||||
@@ -558,7 +592,19 @@ void serializeConfig() {
|
||||
ins[F("skip")] = bus->skippedLeds();
|
||||
ins["type"] = bus->getType() & 0x7F;
|
||||
ins["ref"] = bus->isOffRefreshRequired();
|
||||
ins[F("rgbw")] = bus->isRgbw();
|
||||
//ins[F("rgbw")] = bus->isRgbw();
|
||||
}
|
||||
|
||||
JsonArray hw_com = hw.createNestedArray(F("com"));
|
||||
const ColorOrderMap& com = busses.getColorOrderMap();
|
||||
for (uint8_t s = 0; s < com.count(); s++) {
|
||||
const ColorOrderMapEntry *entry = com.get(s);
|
||||
if (!entry) break;
|
||||
|
||||
JsonObject co = hw_com.createNestedObject();
|
||||
co["start"] = entry->start;
|
||||
co["len"] = entry->len;
|
||||
co[F("order")] = entry->colorOrder;
|
||||
}
|
||||
|
||||
// button(s)
|
||||
@@ -589,6 +635,8 @@ void serializeConfig() {
|
||||
hw_relay["pin"] = rlyPin;
|
||||
hw_relay["rev"] = !rlyMde;
|
||||
|
||||
hw[F("baud")] = serialBaud;
|
||||
|
||||
//JsonObject hw_status = hw.createNestedObject("status");
|
||||
//hw_status["pin"] = -1;
|
||||
|
||||
@@ -602,12 +650,12 @@ void serializeConfig() {
|
||||
light_gc["col"] = (strip.gammaCorrectCol) ? 2.8 : 1.0;
|
||||
|
||||
JsonObject light_tr = light.createNestedObject("tr");
|
||||
light_tr[F("mode")] = fadeTransition;
|
||||
light_tr["mode"] = fadeTransition;
|
||||
light_tr["dur"] = transitionDelayDefault / 100;
|
||||
light_tr["pal"] = strip.paletteFade;
|
||||
|
||||
JsonObject light_nl = light.createNestedObject("nl");
|
||||
light_nl[F("mode")] = nightlightMode;
|
||||
light_nl["mode"] = nightlightMode;
|
||||
light_nl["dur"] = nightlightDelayMinsDefault;
|
||||
light_nl[F("tbri")] = nightlightTargetBri;
|
||||
light_nl["macro"] = macroNl;
|
||||
@@ -626,8 +674,10 @@ void serializeConfig() {
|
||||
JsonObject if_sync_recv = if_sync.createNestedObject("recv");
|
||||
if_sync_recv["bri"] = receiveNotificationBrightness;
|
||||
if_sync_recv["col"] = receiveNotificationColor;
|
||||
if_sync_recv["fx"] = receiveNotificationEffects;
|
||||
if_sync_recv["fx"] = receiveNotificationEffects;
|
||||
if_sync_recv["grp"] = receiveGroups;
|
||||
if_sync_recv["seg"] = receiveSegmentOptions;
|
||||
if_sync_recv["sb"] = receiveSegmentBounds;
|
||||
|
||||
JsonObject if_sync_send = if_sync.createNestedObject("send");
|
||||
if_sync_send[F("dir")] = notifyDirect;
|
||||
@@ -651,7 +701,7 @@ void serializeConfig() {
|
||||
if_live_dmx[F("uni")] = e131Universe;
|
||||
if_live_dmx[F("seqskip")] = e131SkipOutOfSequence;
|
||||
if_live_dmx[F("addr")] = DMXAddress;
|
||||
if_live_dmx[F("mode")] = DMXMode;
|
||||
if_live_dmx["mode"] = DMXMode;
|
||||
|
||||
if_live[F("timeout")] = realtimeTimeoutMs / 100;
|
||||
if_live[F("maxbri")] = arlsForceMaxBri;
|
||||
@@ -740,6 +790,14 @@ void serializeConfig() {
|
||||
timers_ins0["min"] = timerMinutes[i];
|
||||
timers_ins0["macro"] = timerMacro[i];
|
||||
timers_ins0[F("dow")] = timerWeekday[i] >> 1;
|
||||
if (i<8) {
|
||||
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;
|
||||
end["day"] = timerDayEnd[i];
|
||||
}
|
||||
}
|
||||
|
||||
JsonObject ota = doc.createNestedObject("ota");
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
* Color conversion methods
|
||||
*/
|
||||
|
||||
void setRandomColor(byte* rgb)
|
||||
{
|
||||
lastRandomIndex = strip.get_random_wheel_index(lastRandomIndex);
|
||||
colorHStoRGB(lastRandomIndex*256,255,rgb);
|
||||
}
|
||||
|
||||
void colorFromUint32(uint32_t in, bool secondary)
|
||||
{
|
||||
byte *_col = secondary ? colSec : col;
|
||||
@@ -22,11 +28,6 @@ void colorFromUint24(uint32_t in, bool secondary)
|
||||
_col[2] = B(in);
|
||||
}
|
||||
|
||||
//store color components in uint32_t
|
||||
uint32_t colorFromRgbw(byte* rgbw) {
|
||||
return RGBW32(rgbw[0], rgbw[1], rgbw[2], rgbw[3]);
|
||||
}
|
||||
|
||||
//relatively change white brightness, minumum A=5
|
||||
void relativeChangeWhite(int8_t amount, byte lowerBoundary)
|
||||
{
|
||||
@@ -259,7 +260,7 @@ uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb)
|
||||
rgbw[1] = ((uint16_t) correctionRGB[1] * G(rgb)) /255; // correct G
|
||||
rgbw[2] = ((uint16_t) correctionRGB[2] * B(rgb)) /255; // correct B
|
||||
rgbw[3] = W(rgb);
|
||||
return colorFromRgbw(rgbw);
|
||||
return RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3]);
|
||||
}
|
||||
|
||||
//approximates a Kelvin color temperature from an RGB color.
|
||||
@@ -299,4 +300,4 @@ uint16_t approximateKelvinFromRGB(uint32_t rgb) {
|
||||
uint16_t k = 8080 + (225-r) *86;
|
||||
return (k > 10091) ? 10091 : k;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,12 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ESP8266
|
||||
#define WLED_MAX_COLOR_ORDER_MAPPINGS 5
|
||||
#else
|
||||
#define WLED_MAX_COLOR_ORDER_MAPPINGS 10
|
||||
#endif
|
||||
|
||||
//Usermod IDs
|
||||
#define USERMOD_ID_RESERVED 0 //Unused. Might indicate no usermod present
|
||||
#define USERMOD_ID_UNSPECIFIED 1 //Default value for a general user mod that does not specify a custom ID
|
||||
@@ -64,6 +70,7 @@
|
||||
#define USERMOD_ID_SEVEN_SEGMENT_DISPLAY 21 //Usermod "usermod_v2_seven_segment_display.h"
|
||||
#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"
|
||||
|
||||
//Access point behavior
|
||||
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
|
||||
@@ -74,7 +81,7 @@
|
||||
//Notifier callMode
|
||||
#define CALL_MODE_INIT 0 //no updates on init, can be used to disable updates
|
||||
#define CALL_MODE_DIRECT_CHANGE 1
|
||||
#define CALL_MODE_BUTTON 2
|
||||
#define CALL_MODE_BUTTON 2 //default button actions applied to selected segments
|
||||
#define CALL_MODE_NOTIFICATION 3
|
||||
#define CALL_MODE_NIGHTLIGHT 4
|
||||
#define CALL_MODE_NO_NOTIFY 5
|
||||
@@ -84,6 +91,7 @@
|
||||
#define CALL_MODE_BLYNK 9
|
||||
#define CALL_MODE_ALEXA 10
|
||||
#define CALL_MODE_WS_SEND 11 //special call mode, not for notifier, updates websocket only
|
||||
#define CALL_MODE_BUTTON_PRESET 12 //button/IR JSON preset/macro
|
||||
|
||||
//RGB to RGBW conversion mode
|
||||
#define RGBW_MODE_MANUAL_ONLY 0 //No automatic white channel calculation. Manual white channel slider
|
||||
@@ -168,6 +176,7 @@
|
||||
#define COL_ORDER_RBG 3
|
||||
#define COL_ORDER_BGR 4
|
||||
#define COL_ORDER_GBR 5
|
||||
#define COL_ORDER_MAX 5
|
||||
|
||||
|
||||
//Button type
|
||||
@@ -217,6 +226,7 @@
|
||||
#define SEG_DIFFERS_FX 0x08
|
||||
#define SEG_DIFFERS_BOUNDS 0x10
|
||||
#define SEG_DIFFERS_GSO 0x20
|
||||
#define SEG_DIFFERS_SEL 0x80
|
||||
|
||||
//Playlist option byte
|
||||
#define PL_OPTION_SHUFFLE 0x01
|
||||
@@ -296,11 +306,17 @@
|
||||
|
||||
// Size of buffer for API JSON object (increase for more segments)
|
||||
#ifdef ESP8266
|
||||
#define JSON_BUFFER_SIZE 9216
|
||||
#define JSON_BUFFER_SIZE 10240
|
||||
#else
|
||||
#define JSON_BUFFER_SIZE 20480
|
||||
#endif
|
||||
|
||||
#ifdef WLED_USE_DYNAMIC_JSON
|
||||
#define MIN_HEAP_SIZE JSON_BUFFER_SIZE+512
|
||||
#else
|
||||
#define MIN_HEAP_SIZE 4096
|
||||
#endif
|
||||
|
||||
// Maximum size of node map (list of other WLED instances)
|
||||
#ifdef ESP8266
|
||||
#define WLED_MAX_NODES 24
|
||||
@@ -329,4 +345,6 @@
|
||||
#define DEFAULT_LED_COUNT 30
|
||||
#endif
|
||||
|
||||
#define INTERFACE_UPDATE_COOLDOWN 2000 //time in ms to wait between websockets, alexa, and MQTT updates
|
||||
|
||||
#endif
|
||||
|
||||
@@ -381,6 +381,10 @@ button {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.modal button:hover {
|
||||
background-color: var(--c-4);
|
||||
}
|
||||
|
||||
#info {
|
||||
z-index: 3;
|
||||
}
|
||||
@@ -587,8 +591,8 @@ input[type=range]:active + .sliderbubble {
|
||||
border: 0px solid white;
|
||||
border-radius: 25px;
|
||||
transition-duration: 0.5s;
|
||||
-webkit-backface-visibility: hidden;
|
||||
-webkit-transform:translate3d(0,0,0);
|
||||
/*-webkit-backface-visibility: hidden;
|
||||
-webkit-transform:translate3d(0,0,0);*/
|
||||
}
|
||||
|
||||
.btn-s {
|
||||
@@ -609,11 +613,12 @@ input[type=range]:active + .sliderbubble {
|
||||
width: 216px;
|
||||
}
|
||||
.btn-xs {
|
||||
width: 39px;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
margin: 2px 0 0 0;
|
||||
}
|
||||
.btn-pl-add {
|
||||
margin-left: 9px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
|
||||
@@ -645,7 +650,7 @@ input[type=range]:active + .sliderbubble {
|
||||
.sel-pl {
|
||||
width: 192px;
|
||||
background-position: 168px 16px;
|
||||
margin: 8px 7px 0 0;
|
||||
margin: 8px 3px 0 0;
|
||||
}
|
||||
|
||||
.sel-ple {
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
<button class="xxs cl btn" onclick="selectSlot(2);">3</button>
|
||||
</div>
|
||||
<div id="hexw">
|
||||
<input id="hexc" type="text" class="noslide" onkeydown="hexEnter(this)" autocomplete="off" maxlength="8" />
|
||||
<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>
|
||||
</div>
|
||||
<p class="labels">
|
||||
@@ -141,7 +141,7 @@
|
||||
<div id="Effects" class="tabcontent">
|
||||
<p class="labels">Effect speed</p>
|
||||
<div class="staytop">
|
||||
<i class="icons slider-icon"></i>
|
||||
<i class="icons slider-icon" 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>
|
||||
|
||||
@@ -215,7 +215,7 @@ function onLoad() {
|
||||
//TODO: do some parsing first
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log("holidays.json does not contain array of holidays. Defaults loaded.");
|
||||
console.log("No array of holidays in holidays.json. Defaults loaded.");
|
||||
})
|
||||
.finally(function(){
|
||||
loadBg(cfg.theme.bg.url);
|
||||
@@ -280,6 +280,8 @@ function showToast(text, error = false) {
|
||||
}
|
||||
|
||||
function showErrorToast() {
|
||||
// if we received a timeout force WS reconnect
|
||||
reconnectWS();
|
||||
showToast('Connection to light failed!', true);
|
||||
}
|
||||
function clearErrorToast() {
|
||||
@@ -585,8 +587,8 @@ function populateSegments(s)
|
||||
<td class="segtd">Offset</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${i}s" type="number" min="0" max="${ledCount-1}" value="${inst.start}" oninput="updateLen(${i})"></td>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${i}e" type="number" min="0" max="${ledCount-(cfg.comp.seglen?inst.start:0)}" value="${inst.stop-(cfg.comp.seglen?inst.start:0)}" oninput="updateLen(${i})"></td>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${i}s" type="number" min="0" max="${ledCount-1}" value="${inst.start}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${i}e" type="number" min="0" max="${ledCount-(cfg.comp.seglen?inst.start:0)}" value="${inst.stop-(cfg.comp.seglen?inst.start:0)}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${i}of" type="number" value="${inst.of}" oninput="updateLen(${i})"></td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -597,13 +599,12 @@ function populateSegments(s)
|
||||
<td class="segtd">Apply</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${i}grp" type="number" min="1" max="255" value="${inst.grp}" oninput="updateLen(${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})"></td>
|
||||
<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>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="h" id="seg${i}len"></div>
|
||||
<button class="btn btn-i btn-xs del" id="segd${i}" onclick="delSeg(${i})"><i class="icons btn-icon"></i></button>
|
||||
<label class="check revchkl">
|
||||
Reverse direction
|
||||
<input type="checkbox" id="seg${i}rev" onchange="setRev(${i})" ${inst.rev ? "checked":""}>
|
||||
@@ -614,6 +615,10 @@ function populateSegments(s)
|
||||
<input type="checkbox" id="seg${i}mi" onchange="setMi(${i})" ${inst.mi ? "checked":""}>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div><br>`;
|
||||
}
|
||||
@@ -627,10 +632,13 @@ function populateSegments(s)
|
||||
noNewSegs = false;
|
||||
}
|
||||
for (var i = 0; i <= lSeg; i++) {
|
||||
updateLen(i);
|
||||
updateTrail(d.getElementById(`seg${i}bri`));
|
||||
if (segCount < 2) d.getElementById(`segd${lSeg}`).style.display = "none";
|
||||
updateLen(i);
|
||||
updateTrail(d.getElementById(`seg${i}bri`));
|
||||
let segr = d.getElementById(`segr${i}`);
|
||||
if (segr) segr.style.display = "none";
|
||||
}
|
||||
if (segCount < 2) d.getElementById(`segd${lSeg}`).style.display = "none";
|
||||
if (!noNewSegs && (cfg.comp.seglen?parseInt(d.getElementById(`seg${lSeg}s`).value):0)+parseInt(d.getElementById(`seg${lSeg}e`).value)<ledCount) d.getElementById(`segr${lSeg}`).style.display = "inline";
|
||||
d.getElementById('rsbtn').style.display = (segCount > 1) ? "inline":"none";
|
||||
}
|
||||
|
||||
@@ -872,7 +880,7 @@ function updateLen(s)
|
||||
{
|
||||
if (!d.getElementById(`seg${s}s`)) return;
|
||||
var start = parseInt(d.getElementById(`seg${s}s`).value);
|
||||
var stop = parseInt(d.getElementById(`seg${s}e`).value);
|
||||
var stop = parseInt(d.getElementById(`seg${s}e`).value);
|
||||
var len = stop - (cfg.comp.seglen?0:start);
|
||||
var out = "(delete)";
|
||||
if (len > 1) {
|
||||
@@ -947,12 +955,20 @@ function cmpP(a, b) {
|
||||
return a[1].n.localeCompare(b[1].n,undefined, {numeric: true});
|
||||
}
|
||||
|
||||
//forces a WebSockets reconnect if timeout (error toast), or successful HTTP response to JSON request
|
||||
function reconnectWS() {
|
||||
if (ws) ws.close();
|
||||
ws = null;
|
||||
if (lastinfo && lastinfo.ws > -1) setTimeout(makeWS,500);
|
||||
}
|
||||
|
||||
function makeWS() {
|
||||
if (ws) return;
|
||||
ws = new WebSocket('ws://'+(loc?locip:window.location.hostname)+'/ws');
|
||||
ws.binaryType = "arraybuffer";
|
||||
ws.onmessage = function(event) {
|
||||
if (event.data instanceof ArrayBuffer) return; //liveview packet
|
||||
var json = JSON.parse(event.data);
|
||||
if (json.leds) return; //liveview packet
|
||||
clearTimeout(jsonTimeout);
|
||||
jsonTimeout = null;
|
||||
clearErrorToast();
|
||||
@@ -967,9 +983,16 @@ function makeWS() {
|
||||
displayRover(info, s);
|
||||
readState(json.state);
|
||||
};
|
||||
ws.onclose = function(event) {
|
||||
d.getElementById('connind').style.backgroundColor = "#831";
|
||||
}
|
||||
ws.onclose = (e)=>{
|
||||
//if there is already a new web socket open, do not null ws
|
||||
if (ws && ws.readyState === WebSocket.OPEN) return;
|
||||
|
||||
d.getElementById('connind').style.backgroundColor = "#831";
|
||||
ws = null;
|
||||
}
|
||||
ws.onopen = (e)=>{
|
||||
reqsLegal = true;
|
||||
}
|
||||
}
|
||||
|
||||
function readState(s,command=false) {
|
||||
@@ -1121,6 +1144,7 @@ function requestJson(command, rinfo = true) {
|
||||
return;
|
||||
}
|
||||
var s = json;
|
||||
if (reqsLegal && !ws) reconnectWS();
|
||||
|
||||
if (!command || rinfo) { //we have info object
|
||||
if (!rinfo) { //entire JSON (on load)
|
||||
@@ -1136,7 +1160,7 @@ function requestJson(command, rinfo = true) {
|
||||
});
|
||||
},25);
|
||||
|
||||
reqsLegal = true;
|
||||
reqsLegal = true;
|
||||
}
|
||||
|
||||
var info = json.info;
|
||||
@@ -1169,7 +1193,7 @@ function requestJson(command, rinfo = true) {
|
||||
displayRover(info, s);
|
||||
}
|
||||
|
||||
readState(s,command);
|
||||
readState(s,command);
|
||||
})
|
||||
.catch(function (error) {
|
||||
showToast(error, true);
|
||||
@@ -1254,8 +1278,8 @@ function makeSeg() {
|
||||
<td class="segtd">${cfg.comp.seglen?"Length":"Stop LED"}</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})"></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})"></td>
|
||||
<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>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="h" id="seg${lowestUnused}len">${ledCount - ns} LED${ledCount - ns >1 ? "s":""}</div>
|
||||
@@ -1445,8 +1469,8 @@ function makePlEntry(p,i) {
|
||||
}
|
||||
|
||||
function makePlUtil() {
|
||||
if (pNum < 2) {
|
||||
showToast("You need at least 2 presets to make a playlist!"); 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">
|
||||
@@ -1491,11 +1515,34 @@ function selSeg(s){
|
||||
requestJson(obj, false);
|
||||
}
|
||||
|
||||
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;}
|
||||
var rev = d.getElementById(`seg${s}rev`).checked;
|
||||
var mi = d.getElementById(`seg${s}mi`).checked;
|
||||
var sel = d.getElementById(`seg${s}sel`).checked;
|
||||
var obj = {"seg": {"id": s, "n": name, "start": start, "stop": (cfg.comp.seglen?start:0)+stop, "rev": rev, "mi": mi, "on": powered[s], "bri": parseInt(d.getElementById(`seg${s}bri`).value), "sel": sel}};
|
||||
if (d.getElementById(`seg${s}grp`)) {
|
||||
var grp = parseInt(d.getElementById(`seg${s}grp`).value);
|
||||
var spc = parseInt(d.getElementById(`seg${s}spc`).value);
|
||||
var ofs = parseInt(d.getElementById(`seg${s}of` ).value);
|
||||
obj.seg.grp = grp;
|
||||
obj.seg.spc = spc;
|
||||
obj.seg.of = ofs;
|
||||
}
|
||||
obj.seg.rpt = true;
|
||||
expand(s);
|
||||
requestJson(obj);
|
||||
}
|
||||
|
||||
function setSeg(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 <= start) {delSeg(s); return;}
|
||||
var stop = parseInt(d.getElementById(`seg${s}e`).value);
|
||||
if ((cfg.comp.seglen && stop == 0) || (!cfg.comp.seglen && stop <= start)) {delSeg(s); return;}
|
||||
var obj = {"seg": {"id": s, "n": name, "start": start, "stop": (cfg.comp.seglen?start:0)+stop}};
|
||||
if (d.getElementById(`seg${s}grp`))
|
||||
{
|
||||
@@ -1542,6 +1589,13 @@ function setSegBri(s){
|
||||
requestJson(obj);
|
||||
}
|
||||
|
||||
function tglFreeze(s=null)
|
||||
{
|
||||
var obj = {"seg": {"frz": "t"}}; // toggle
|
||||
if (s!==null) obj.id = s;
|
||||
requestJson(obj);
|
||||
}
|
||||
|
||||
function setX(ind = null) {
|
||||
if (ind === null) {
|
||||
ind = parseInt(d.querySelector('#fxlist input[name="fx"]:checked').value);
|
||||
@@ -1764,6 +1818,10 @@ function hexEnter() {
|
||||
if(event.keyCode == 13) fromHex();
|
||||
}
|
||||
|
||||
function segEnter(s) {
|
||||
if(event.keyCode == 13) setSeg(s);
|
||||
}
|
||||
|
||||
function fromHex()
|
||||
{
|
||||
var str = d.getElementById('hexc').value;
|
||||
|
||||
@@ -24,40 +24,40 @@
|
||||
function updatePreview(leds) {
|
||||
var str = "linear-gradient(90deg,";
|
||||
var len = leds.length;
|
||||
for (i = 0; i < len; i++) {
|
||||
var leddata = leds[i];
|
||||
if (leddata.length > 6) leddata = leddata.substring(2);
|
||||
str += "#" + leddata;
|
||||
if (i < len -1) str += ","
|
||||
for (i = 2; i < len; i+=3) {
|
||||
str += `rgb(${leds[i]},${leds[i+1]},${leds[i+2]})`;
|
||||
if (i < len -3) str += ","
|
||||
}
|
||||
str += ")";
|
||||
document.getElementById("canv").style.background = str;
|
||||
}
|
||||
|
||||
function getLiveJson(event) {
|
||||
function getLiveJson(e) {
|
||||
try {
|
||||
var json = JSON.parse(event.data);
|
||||
if (json && json.leds) {
|
||||
requestAnimationFrame(function () {updatePreview(json.leds);});
|
||||
}
|
||||
if (toString.call(e.data) === '[object ArrayBuffer]') {
|
||||
let leds = new Uint8Array(event.data);
|
||||
if (leds[0] != 76) return; //'L'
|
||||
updatePreview(leds);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
console.error("Live-Preview ws error:",err);
|
||||
console.error("Peek WS error:",err);
|
||||
}
|
||||
}
|
||||
|
||||
var ws = top.window.ws;
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
console.info("Use top WS for peek");
|
||||
console.info("Peek uses top WS");
|
||||
ws.send("{'lv':true}");
|
||||
} else {
|
||||
console.info("Peek ws opening");
|
||||
console.info("Peek WS opening");
|
||||
ws = new WebSocket("ws://"+document.location.host+"/ws");
|
||||
ws.onopen = function () {
|
||||
console.info("Peek WS opened");
|
||||
console.info("Peek WS open");
|
||||
ws.send("{'lv':true}");
|
||||
}
|
||||
}
|
||||
ws.binaryType = "arraybuffer";
|
||||
ws.addEventListener('message',getLiveJson);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<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=[];
|
||||
var customStarts=false,startsDirty=[],maxCOOverrides=5;
|
||||
function H()
|
||||
{
|
||||
window.open("https://kno.wled.ge/features/settings/#led-settings");
|
||||
@@ -347,10 +347,57 @@ ${i+1}:
|
||||
|
||||
if (!init) UI();
|
||||
}
|
||||
|
||||
function addCOM(start=0,len=1,co=0) {
|
||||
var i = d.getElementsByClassName("com_entry").length;
|
||||
if (i >= 10) return;
|
||||
|
||||
var b = `<div class="com_entry">
|
||||
<hr style="width:260px">
|
||||
${i+1}: Start: <input type="number" name="XS${i}" id="xs${i}" class="l starts" min="0" max="65535" value="${start}" oninput="UI();" required="">
|
||||
Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65535" value="${len}" required="" oninput="UI()">
|
||||
<div style="display:inline">Color Order:
|
||||
<select id="xo${i}" name="XO${i}">
|
||||
<option value="0">GRB</option>
|
||||
<option value="1">RGB</option>
|
||||
<option value="2">BRG</option>
|
||||
<option value="3">RBG</option>
|
||||
<option value="4">BGR</option>
|
||||
<option value="5">GBR</option>
|
||||
</select>
|
||||
</div><br></div>`;
|
||||
gId("com_entries").insertAdjacentHTML("beforeend", b);
|
||||
gId("xo"+i).value = co;
|
||||
btnCOM(i+1);
|
||||
}
|
||||
|
||||
function remCOM() {
|
||||
var entries = d.getElementsByClassName("com_entry");
|
||||
var i = entries.length;
|
||||
if (i === 0) return;
|
||||
entries[i-1].remove();
|
||||
btnCOM(i-1);
|
||||
}
|
||||
|
||||
function resetCOM(_newMaxCOOverrides=undefined) {
|
||||
if (_newMaxCOOverrides) {
|
||||
maxCOOverrides = _newMaxCOOverrides;
|
||||
}
|
||||
for (let e of d.getElementsByClassName("com_entry")) {
|
||||
e.remove();
|
||||
}
|
||||
btnCOM(0);
|
||||
}
|
||||
|
||||
function btnCOM(i) {
|
||||
gId("com_add").style.display = (i<maxCOOverrides) ? "inline":"none";
|
||||
gId("com_rem").style.display = (i>0) ? "inline":"none";
|
||||
}
|
||||
|
||||
function addBtn(i,p,t) {
|
||||
var c = gId("btns").innerHTML;
|
||||
var bt = "BT" + i;
|
||||
var be = "BE" + 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>`;
|
||||
@@ -431,6 +478,12 @@ ${i+1}:
|
||||
d.getElementsByName("CV"+i)[0].checked = v.rev;
|
||||
});
|
||||
}
|
||||
if(c.hw.com) {
|
||||
resetCOM();
|
||||
c.hw.com.forEach(e => {
|
||||
addCOM(e.start, e.len, e.order);
|
||||
});
|
||||
}
|
||||
if (c.hw.btn) {
|
||||
var b = c.hw.btn;
|
||||
if (Array.isArray(b.ins)) gId("btns").innerHTML = "";
|
||||
@@ -510,6 +563,14 @@ ${i+1}:
|
||||
Make a segment for each output: <input type="checkbox" name="MS"> <br>
|
||||
Custom bus start indices: <input type="checkbox" onchange="tglSi(this.checked)" id="si"> <br>
|
||||
<hr style="width:260px">
|
||||
<div id="color_order_mapping">
|
||||
Color Order Override:
|
||||
<div id="com_entries"></div>
|
||||
<hr style="width:260px">
|
||||
<button type="button" id="com_add" onclick="addCOM();UI()" style="display:none;border-radius:20px;height:36px;">+</button>
|
||||
<button type="button" id="com_rem" onclick="remCOM();UI()" style="display:none;border-radius:20px;width:36px;height:36px;">-</button><br>
|
||||
</div>
|
||||
<hr style="width:260px">
|
||||
<div id="btns"></div>
|
||||
Touch threshold: <input type="number" class="s" min="0" max="100" name="TT" required><br>
|
||||
IR GPIO: <input type="number" min="-1" max="40" name="IR" onchange="UI()" class="xs"><select name="IT" onchange="UI()">
|
||||
@@ -571,6 +632,7 @@ ${i+1}:
|
||||
<option value="2">Linear (never wrap)</option>
|
||||
<option value="3">None (not recommended)</option>
|
||||
</select><br>
|
||||
Target refresh rate: <input type="number" class="s" min="1" max="120" name="FR" required> FPS
|
||||
<hr style="width:260px">
|
||||
<div id="cfg">Config template: <input type="file" name="data2" accept=".json"> <input type="button" value="Apply" onclick="loadCfg(d.Sf.data2);"><br></div>
|
||||
<hr>
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
<a href="https://github.com/Aircoookie/WLED/" target="_blank">WLED</a> version ##VERSION##<!-- Autoreplaced from package.json --><br><br>
|
||||
<a href="https://github.com/Aircoookie/WLED/wiki/Contributors-and-credits" target="_blank">Contributors, dependencies and special thanks</a><br>
|
||||
A huge thank you to everyone who helped me create WLED!<br><br>
|
||||
(c) 2016-2021 Christian Schwinne <br>
|
||||
(c) 2016-2022 Christian Schwinne <br>
|
||||
<i>Licensed under the <a href="https://github.com/Aircoookie/WLED/blob/master/LICENSE" target="_blank">MIT license</a></i><br><br>
|
||||
Server message: <span class="sip"> Response error! </span><hr>
|
||||
<div id="toast"></div>
|
||||
|
||||
@@ -5,7 +5,8 @@ function gId(s)
|
||||
{
|
||||
return d.getElementById(s);
|
||||
}
|
||||
function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#sync-settings");}function B(){window.open("/settings","_self");}
|
||||
function H(){window.open("https://kno.wled.ge/interfaces/udp-notifier/");}
|
||||
function B(){window.open("/settings","_self");}
|
||||
function adj(){if (d.Sf.DI.value == 6454) {if (d.Sf.DA.value == 1) d.Sf.DA.value = 0; if (d.Sf.EU.value == 1) d.Sf.EU.value = 0;}
|
||||
else if (d.Sf.DI.value == 5568) {if (d.Sf.DA.value == 0) d.Sf.DA.value = 1; if (d.Sf.EU.value == 0) d.Sf.EU.value = 1;} }
|
||||
function FC()
|
||||
@@ -81,7 +82,8 @@ UDP Port: <input name="UP" type="number" min="1" max="65535" class="d5" required
|
||||
<td><input type="checkbox" id="R8" name="R8"></td>
|
||||
</tr>
|
||||
</table><br>
|
||||
Receive: <input type="checkbox" name="RB">Brightness, <input type="checkbox" name="RC">Color, and <input type="checkbox" name="RX">Effects<br>
|
||||
Receive: <input type="checkbox" name="RB"> Brightness, <input type="checkbox" name="RC"> Color, and <input type="checkbox" name="RX"> Effects<br>
|
||||
<input type="checkbox" name="SO"> Segment options, <input type="checkbox" name="SG"> bounds<br>
|
||||
Send notifications on direct change: <input type="checkbox" name="SD"><br>
|
||||
Send notifications on button press or IR: <input type="checkbox" name="SB"><br>
|
||||
Send Alexa notifications: <input type="checkbox" name="SA"><br>
|
||||
@@ -117,48 +119,62 @@ DMX mode:
|
||||
<option value=5>Dimmer + Multi RGB</option>
|
||||
<option value=6>Multi RGBW</option>
|
||||
</select><br>
|
||||
<a href="https://github.com/Aircoookie/WLED/wiki/E1.31-DMX" target="_blank">E1.31 info</a><br>
|
||||
<a href="https://kno.wled.ge/interfaces/e1.31-dmx/" target="_blank">E1.31 info</a><br>
|
||||
Timeout: <input name="ET" type="number" min="1" max="65000" required> ms<br>
|
||||
Force max brightness: <input type="checkbox" name="FB"><br>
|
||||
Disable realtime gamma correction: <input type="checkbox" name="RG"><br>
|
||||
Realtime LED offset: <input name="WO" type="number" min="-255" max="255" required>
|
||||
<h3>Alexa Voice Assistant</h3>
|
||||
Emulate Alexa device: <input type="checkbox" name="AL"><br>
|
||||
Alexa invocation name: <input name="AI" maxlength="32">
|
||||
Alexa invocation name: <input type="text" name="AI" maxlength="32">
|
||||
<h3>Blynk</h3>
|
||||
<b>Blynk, MQTT and Hue sync all connect to external hosts!<br>
|
||||
This may impact the responsiveness of the ESP8266.</b><br>
|
||||
For best results, only use one of these services at a time.<br>
|
||||
(alternatively, connect a second ESP to them and use the UDP sync)<br><br>
|
||||
Host: <input name="BH" maxlength="32">
|
||||
Host: <input type="text" name="BH" maxlength="32">
|
||||
Port: <input name="BP" type="number" min="1" max="65535" value="80" class="d5"><br>
|
||||
Device Auth token: <input name="BK" maxlength="33"><br>
|
||||
<i>Clear the token field to disable. </i><a href="https://github.com/Aircoookie/WLED/wiki/Blynk" target="_blank">Setup info</a>
|
||||
<i>Clear the token field to disable. </i><a href="https://kno.wled.ge/interfaces/blynk/" target="_blank">Setup info</a>
|
||||
<h3>MQTT</h3>
|
||||
Enable MQTT: <input type="checkbox" name="MQ"><br>
|
||||
Broker: <input name="MS" maxlength="32">
|
||||
Broker: <input type="text" name="MS" maxlength="32">
|
||||
Port: <input name="MQPORT" type="number" min="1" max="65535" class="d5"><br>
|
||||
<b>The MQTT credentials are sent over an unsecured connection.<br>
|
||||
Never use the MQTT password for another service!</b><br>
|
||||
Username: <input name="MQUSER" maxlength="40"><br>
|
||||
Username: <input type="text" name="MQUSER" maxlength="40"><br>
|
||||
Password: <input type="password" name="MQPASS" maxlength="64"><br>
|
||||
Client ID: <input name="MQCID" maxlength="40"><br>
|
||||
Device Topic: <input name="MD" maxlength="32"><br>
|
||||
Group Topic: <input name="MG" maxlength="32"><br>
|
||||
Client ID: <input type="text" name="MQCID" maxlength="40"><br>
|
||||
Device Topic: <input type="text" name="MD" maxlength="32"><br>
|
||||
Group Topic: <input type="text" name="MG" maxlength="32"><br>
|
||||
Publish on button press: <input type="checkbox" name="BM"><br>
|
||||
<i>Reboot required to apply changes. </i><a href="https://github.com/Aircoookie/WLED/wiki/MQTT" target="_blank">MQTT info</a>
|
||||
<i>Reboot required to apply changes. </i><a href="https://kno.wled.ge/interfaces/mqtt/" target="_blank">MQTT info</a>
|
||||
<h3>Philips Hue</h3>
|
||||
<i>You can find the bridge IP and the light number in the 'About' section of the hue app.</i><br>
|
||||
Poll Hue light <input name="HL" type="number" min="1" max="99" > every <input name="HI" type="number" min="100" max="65000"> ms: <input type="checkbox" name="HP"><br>
|
||||
Then, receive <input type="checkbox" name="HO"> On/Off, <input type="checkbox" name="HB"> Brightness, and <input type="checkbox" name="HC"> Color<br>
|
||||
Hue Bridge IP:<br>
|
||||
<input name="H0" type="number" min="0" max="255" > .
|
||||
<input name="H1" type="number" min="0" max="255" > .
|
||||
<input name="H2" type="number" min="0" max="255" > .
|
||||
<input name="H3" type="number" min="0" max="255" ><br>
|
||||
<input name="H0" type="number" class="s" min="0" max="255" > .
|
||||
<input name="H1" type="number" class="s" min="0" max="255" > .
|
||||
<input name="H2" type="number" class="s" min="0" max="255" > .
|
||||
<input name="H3" type="number" class="s" min="0" max="255" ><br>
|
||||
<b>Press the pushlink button on the bridge, after that save this page!</b><br>
|
||||
(when first connecting)<br>
|
||||
Hue status: <span class="sip"> Disabled in this build </span><hr>
|
||||
Hue status: <span class="sip"> Disabled in this build </span>
|
||||
<h3>Serial</h3>
|
||||
Baud rate:
|
||||
<select name=BD>
|
||||
<option value=1152>115200</option>
|
||||
<option value=2304>230400</option>
|
||||
<option value=4608>460800</option>
|
||||
<option value=5000>500000</option>
|
||||
<option value=5760>576000</option>
|
||||
<option value=9216>921600</option>
|
||||
<option value=10000>1000000</option>
|
||||
<option value=15000>1500000</option>
|
||||
</select><br>
|
||||
<i>Keep at 115200 to use Improv. Some boards may not support high rates.</i>
|
||||
<hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
||||
</form>
|
||||
</body>
|
||||
|
||||
@@ -6,9 +6,11 @@
|
||||
<title>Time Settings</title>
|
||||
<script>
|
||||
var d=document;
|
||||
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()
|
||||
{
|
||||
window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings");
|
||||
window.open("https://kno.wled.ge/features/settings/#time-settings");
|
||||
}
|
||||
function B()
|
||||
{
|
||||
@@ -16,12 +18,21 @@
|
||||
}
|
||||
function S()
|
||||
{
|
||||
BTa();GetV();Cs();FC();
|
||||
BTa();GetV();updLoc();Cs();FC();
|
||||
}
|
||||
function gId(s)
|
||||
{
|
||||
return d.getElementById(s);
|
||||
}
|
||||
function gN(s) {
|
||||
return d.getElementsByName(s)[0];
|
||||
}
|
||||
function expand(o,i)
|
||||
{
|
||||
var t = gId("WD"+i);
|
||||
t.style.display = t.style.display!=="none" ? "none" : "";
|
||||
o.innerHTML = t.style.display==="none" ? "🗓" : "✕";
|
||||
}
|
||||
function Cs()
|
||||
{
|
||||
gId("cac").style.display="none";
|
||||
@@ -41,54 +52,87 @@
|
||||
gId("coc").style.display="none";
|
||||
}
|
||||
}
|
||||
function BTa()
|
||||
{
|
||||
var ih="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Preset</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";
|
||||
for (i=0;i<8;i++)
|
||||
{
|
||||
ih+="<tr><td><input name=\"W"+i+"\" id=\"W"+i+"\" type=\"number\" style=\"display:none\"><input id=\"W"+i+"0\" type=\"checkbox\"></td><td><input name=\"H"+i+"\" type=\"number\" min=\"0\" max=\"24\"></td><td><input name=\"N"+i+"\" type=\"number\" min=\"0\" max=\"59\"></td><td><input name=\"T"+i+"\" type=\"number\" min=\"0\" max=\"250\"></td>";
|
||||
for (j=1;j<8;j++) ih+="<td><input id=\"W"+i+j+"\" type=\"checkbox\"></td>";
|
||||
}
|
||||
ih+="<tr><td><input name=\"W8\" id=\"W8\" type=\"number\" style=\"display:none\"><input id=\"W80\" type=\"checkbox\"></td><td>Sunrise<input name=\"H8\" value=\"255\" type=\"hidden\"></td><td><input name=\"N8\" type=\"number\" min=\"-59\" max=\"59\"></td><td><input name=\"T8\" type=\"number\" min=\"0\" max=\"250\"></td>";
|
||||
for (j=1;j<8;j++) ih+="<td><input id=\"W8"+j+"\" type=\"checkbox\"></td>";
|
||||
ih+="<tr><td><input name=\"W9\" id=\"W9\" type=\"number\" style=\"display:none\"><input id=\"W90\" type=\"checkbox\"></td><td>Sunset<input name=\"H9\" value=\"255\" type=\"hidden\"></td><td><input name=\"N9\" type=\"number\" min=\"-59\" max=\"59\"><td><input name=\"T9\" type=\"number\" min=\"0\" max=\"250\"></td>";
|
||||
for (j=1;j<8;j++) ih+="<td><input id=\"W9"+j+"\" type=\"checkbox\"></td>";
|
||||
gId("TMT").innerHTML=ih;
|
||||
}
|
||||
function FC()
|
||||
{
|
||||
for(j=0;j<8;j++)
|
||||
{
|
||||
for(i=0;i<10;i++) gId("W"+i+j).checked=gId("W"+i).value>>j&1;
|
||||
}
|
||||
}
|
||||
function Wd()
|
||||
{
|
||||
a=[0,0,0,0,0,0,0,0,0,0];
|
||||
for(i=0;i<10;i++)
|
||||
{
|
||||
m=1;
|
||||
for(j=0;j<8;j++)
|
||||
{
|
||||
a[i]+=gId("W"+i+j).checked*m;m*=2;
|
||||
}
|
||||
gId("W"+i).value=a[i];
|
||||
}
|
||||
}
|
||||
function BTa()
|
||||
{
|
||||
var ih="<tr><th>En.</th><th>Hour</th><th>Minute</th><th>Preset</th><th></th></tr>";
|
||||
for (i=0;i<8;i++) {
|
||||
ih+=`<tr><td><input name="W${i}" id="W${i}" type="hidden"><input id="W${i}0" type="checkbox"></td>
|
||||
<td><input name="H${i}" class="xs" type="number" min="0" max="24"></td>
|
||||
<td><input name="N${i}" class="xs" type="number" min="0" max="59"></td>
|
||||
<td><input name="T${i}" class="s" type="number" min="0" max="250"></td>
|
||||
<td><div id="CB${i}" onclick="expand(this,${i})" ${cals}>🗓</div></td></tr>`;
|
||||
ih+=`<tr><td colspan=5><div id="WD${i}" style="display:none;">Run on weekdays`;
|
||||
ih+=`<table style="width:100%%;"><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>`
|
||||
for (j=1;j<8;j++) ih+=`<td><input id="W${i}${j}" type="checkbox"></td>`;
|
||||
ih+=`</tr></table>from
|
||||
<select name="M${i}">`;
|
||||
for (j=0;j<12;j++) ih+=`<option value="${j+1}">${ms[j]}</option>`;
|
||||
ih+=`</select><input name="D${i}" class="xs" type="number" min="1" max="31"></input> to
|
||||
<select name="P${i}">`;
|
||||
for (j=0;j<12;j++) ih+=`<option value="${j+1}">${ms[j]}</option>`;
|
||||
ih+=`</select><input name="E${i}" class="xs" type="number" min="1" max="31"></input>
|
||||
<hr></div></td></tr>`;
|
||||
}
|
||||
ih+=`<tr><td><input name="W8" id="W8" type="hidden"><input id="W80" type="checkbox"></td>
|
||||
<td>Sunrise<input name="H8" value="255" type="hidden"></td>
|
||||
<td><input name="N8" class="xs" type="number" min="-59" max="59"></td>
|
||||
<td><input name="T8" class="s" type="number" min="0" max="250"></td>
|
||||
<td><div id="CB8" onclick="expand(this,8)" ${cals}>🗓</div></td></tr><tr><td colspan=5>`;
|
||||
ih+=`<div id="WD8"style="display:none;"><table style="width:100%%;"><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>`;
|
||||
for (j=1;j<8;j++) ih+=`<td><input id="W8${j}" type="checkbox"></td>`;
|
||||
ih+="</tr></table><hr></div></td></tr>";
|
||||
ih+=`<tr><td><input name="W9" id="W9" type="hidden"><input id="W90" type="checkbox"></td>
|
||||
<td>Sunset<input name="H9" value="255" type="hidden"></td>
|
||||
<td><input name="N9" class="xs" type="number" min="-59" max="59"></td>
|
||||
<td><input name="T9" class="s" type="number" min="0" max="250"></td>
|
||||
<td><div id="CB9" onclick="expand(this,9)" ${cals}>🗓</div></td></tr><tr><td colspan=5>`;
|
||||
ih+=`<div id="WD9" style="display:none;"><table style="width:100%%;"><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>`;
|
||||
for (j=1;j<8;j++) ih+=`<td><input id="W9${j}" type="checkbox"></td>`;
|
||||
ih+="</tr></table><hr></div></td></tr>";
|
||||
gId("TMT").innerHTML=ih;
|
||||
}
|
||||
function FC()
|
||||
{
|
||||
for(i=0;i<10;i++)
|
||||
{
|
||||
let wd = gId("W"+i).value;
|
||||
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))) {
|
||||
expand(gId("CB"+i),i); //expand macros with custom DOW or date range set
|
||||
}
|
||||
}
|
||||
}
|
||||
function Wd()
|
||||
{
|
||||
a = [0,0,0,0,0,0,0,0,0,0];
|
||||
for (i=0; i<10; i++) {
|
||||
m=1;
|
||||
for(j=0;j<8;j++) { a[i]+=gId(("W"+i)+j).checked*m; m*=2;}
|
||||
gId("W"+i).value=a[i];
|
||||
}
|
||||
if (d.Sf.LTR.value==="S") { d.Sf.LT.value = -1*parseFloat(d.Sf.LT.value); }
|
||||
if (d.Sf.LNR.value==="W") { d.Sf.LN.value = -1*parseFloat(d.Sf.LN.value); }
|
||||
}
|
||||
function addRow(i,p,l,d) {
|
||||
var t = gId("macros"); // table
|
||||
var rCnt = t.rows.length; // get the number of rows.
|
||||
var tr = t.insertRow(rCnt); // table row.
|
||||
|
||||
var rCnt = t.rows.length; // get the number of rows.
|
||||
var tr = t.insertRow(rCnt); // table row.
|
||||
var b = String.fromCharCode((i<10?48:55)+i);
|
||||
var td = document.createElement('td'); // TABLE DEFINITION.
|
||||
td = tr.insertCell(0);
|
||||
td.innerHTML = `Button ${i}:`;
|
||||
td = tr.insertCell(1);
|
||||
td.innerHTML = `<input name="MP${i}" type="number" min="0" max="250" value="${p}" required>`;
|
||||
td.innerHTML = `<input name="MP${b}" type="number" class="s" min="0" max="250" value="${p}" required>`;
|
||||
td = tr.insertCell(2);
|
||||
td.innerHTML = `<input name="ML${i}" type="number" min="0" max="250" value="${l}" required>`;
|
||||
td.innerHTML = `<input name="ML${b}" type="number" class="s" min="0" max="250" value="${l}" required>`;
|
||||
td = tr.insertCell(3);
|
||||
td.innerHTML = `<input name="MD${i}" type="number" min="0" max="250" value="${d}" required>`;
|
||||
td.innerHTML = `<input name="MD${b}" type="number" class="s" min="0" max="250" value="${d}" required>`;
|
||||
}
|
||||
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";
|
||||
}
|
||||
function GetV()
|
||||
{
|
||||
@@ -105,7 +149,7 @@
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
|
||||
<h2>Time setup</h2>
|
||||
Get time from NTP server: <input type="checkbox" name="NT"><br>
|
||||
<input name="NS" maxlength="32"><br>
|
||||
<input type="text" name="NS" maxlength="32"><br>
|
||||
Use 24h format: <input type="checkbox" name="CF"><br>
|
||||
Time zone:
|
||||
<select name="TZ">
|
||||
@@ -134,8 +178,8 @@
|
||||
</select><br>
|
||||
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 (N): <input name="LT" type="number" min="-66.6" max="66.6" step="0.01">
|
||||
Longitude (E): <input name="LN" type="number" min="-180" max="180" step="0.01">
|
||||
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">
|
||||
<div id="sun" class="times"></div>
|
||||
<h3>Clock</h3>
|
||||
Clock Overlay:
|
||||
@@ -158,16 +202,16 @@
|
||||
</div>
|
||||
Countdown Mode: <input type="checkbox" name="CE"><br>
|
||||
Countdown Goal:<br>
|
||||
Year: 20 <input name="CY" type="number" min="0" max="99" required> Month: <input name="CI" type="number" min="1" max="12" required> Day: <input name="CD" type="number" min="1" max="31" required><br>
|
||||
Hour: <input name="CH" type="number" min="0" max="23" required> Minute: <input name="CM" type="number" min="0" max="59" required> Second: <input name="CS" type="number" min="0" max="59" required><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>
|
||||
Time: <nowrap><input name="CH" class="xs" type="number" min="0" max="23" required>:<input name="CM" class="xs" type="number" min="0" max="59" required>:<input name="CS" class="xs" type="number" min="0" max="59" required></nowrap><br>
|
||||
<h3>Macro presets</h3>
|
||||
<b>Macros have moved!</b><br>
|
||||
<i>Presets now also can be used as macros to save both JSON and HTTP API commands.<br>
|
||||
Just enter the preset id below!</i>
|
||||
Just enter the preset ID below!</i>
|
||||
<i>Use 0 for the default action instead of a preset</i><br>
|
||||
Alexa On/Off Preset: <input name="A0" type="number" min="0" max="250" required> <input name="A1" type="number" min="0" max="250" required><br>
|
||||
Countdown-Over Preset: <input name="MC" type="number" min="0" max="250" required><br>
|
||||
Timed-Light-Over Presets: <input name="MN" type="number" min="0" max="250" required><br>
|
||||
Alexa On/Off Preset: <input name="A0" class="m" type="number" min="0" max="250" required> <input name="A1" class="m" type="number" min="0" max="250" required><br>
|
||||
Countdown-Over Preset: <input name="MC" class="m" type="number" min="0" max="250" required><br>
|
||||
Timed-Light-Over Presets: <input name="MN" class="m" type="number" min="0" max="250" required><br>
|
||||
<h3>Button actions</h3>
|
||||
<table style="margin: 0 auto;" id="macros">
|
||||
<thead>
|
||||
@@ -181,11 +225,12 @@
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<a href="https://github.com/Aircoookie/WLED/wiki/Macros#analog-button" target="_blank">Analog Button setup</a>
|
||||
<a href="https://kno.wled.ge/features/macros/#analog-button" target="_blank">Analog Button setup</a>
|
||||
<h3>Time-controlled presets</h3>
|
||||
<div style="display: inline-block">
|
||||
<table id="TMT">
|
||||
</table></div><hr>
|
||||
<div style="display: inline-block">
|
||||
<table id="TMT" style="min-width:330px;"></table>
|
||||
</div>
|
||||
<hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
||||
</form>
|
||||
</body>
|
||||
|
||||
@@ -66,7 +66,6 @@ input[type="number"].xs {
|
||||
}
|
||||
input[type="checkbox"] {
|
||||
transform: scale(1.5);
|
||||
margin-right: 10px;
|
||||
}
|
||||
select {
|
||||
background: #333;
|
||||
@@ -80,7 +79,6 @@ td {
|
||||
.d5 {
|
||||
width: 4.5em !important;
|
||||
}
|
||||
|
||||
#toast {
|
||||
opacity: 0;
|
||||
background-color: #444;
|
||||
|
||||
@@ -15,12 +15,16 @@ void handleAlexa();
|
||||
void onAlexaChange(EspalexaDevice* dev);
|
||||
|
||||
//blynk.cpp
|
||||
#ifndef WLED_DISABLE_BLYNK
|
||||
void initBlynk(const char* auth, const char* host, uint16_t port);
|
||||
void handleBlynk();
|
||||
void updateBlynk();
|
||||
#endif
|
||||
|
||||
//button.cpp
|
||||
void shortPressAction(uint8_t b=0);
|
||||
void longPressAction(uint8_t b=0);
|
||||
void doublePressAction(uint8_t b=0);
|
||||
bool isButtonPressed(uint8_t b=0);
|
||||
void handleButton();
|
||||
void handleIO();
|
||||
@@ -56,7 +60,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);
|
||||
uint32_t colorFromRgbw(byte* rgbw);
|
||||
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);
|
||||
@@ -71,6 +75,8 @@ bool colorFromHexString(byte* rgb, const char* in);
|
||||
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
|
||||
uint16_t approximateKelvinFromRGB(uint32_t rgb);
|
||||
|
||||
void setRandomColor(byte* rgb);
|
||||
|
||||
//dmx.cpp
|
||||
void initDMX();
|
||||
void handleDMX();
|
||||
@@ -133,16 +139,19 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
|
||||
void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true);
|
||||
void serializeInfo(JsonObject root);
|
||||
void serveJson(AsyncWebServerRequest* request);
|
||||
#ifdef WLED_ENABLE_JSONLIVE
|
||||
bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient = 0);
|
||||
#endif
|
||||
|
||||
//led.cpp
|
||||
void setValuesFromMainSeg();
|
||||
void setValuesFromFirstSelectedSeg();
|
||||
void resetTimebase();
|
||||
void toggleOnOff();
|
||||
void setAllLeds();
|
||||
void setLedsStandard();
|
||||
bool colorChanged();
|
||||
void colorUpdated(int callMode);
|
||||
void applyBri();
|
||||
void applyFinalBri();
|
||||
void applyValuesToSelectedSegs();
|
||||
void colorUpdated(byte callMode);
|
||||
void stateUpdated(byte callMode);
|
||||
void updateInterfaces(uint8_t callMode);
|
||||
void handleTransitions();
|
||||
void handleNightlight();
|
||||
@@ -190,11 +199,12 @@ 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 deletePreset(byte index);
|
||||
|
||||
//set.cpp
|
||||
void _setRandomColor(bool _sec,bool fromButton=false);
|
||||
bool isAsterisksOnly(const char* str, byte maxLen);
|
||||
void handleSettingsSet(AsyncWebServerRequest *request, byte subPage);
|
||||
bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply=true);
|
||||
@@ -217,10 +227,10 @@ void sendSysInfoUDP();
|
||||
//void sappend(char stype, const char* key, int val);
|
||||
//void sappends(char stype, const char* key, char* val);
|
||||
//void prepareHostname(char* hostname);
|
||||
//void _setRandomColor(bool _sec, bool fromButton);
|
||||
//bool isAsterisksOnly(const char* str, byte maxLen);
|
||||
bool requestJSONBufferLock(uint8_t module=255);
|
||||
void releaseJSONBufferLock();
|
||||
uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen);
|
||||
|
||||
//um_manager.cpp
|
||||
class Usermod {
|
||||
@@ -279,6 +289,7 @@ void clearEEPROM();
|
||||
|
||||
//wled_serial.cpp
|
||||
void handleSerial();
|
||||
void updateBaudRate(uint32_t rate);
|
||||
|
||||
//wled_server.cpp
|
||||
bool isIp(String str);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32 //FS info bare IDF function until FS wrapper is available for ESP32
|
||||
#if WLED_FS != LITTLEFS
|
||||
#if WLED_FS != LITTLEFS && ESP_IDF_VERSION_MAJOR < 4
|
||||
#include "esp_spiffs.h"
|
||||
#endif
|
||||
#endif
|
||||
@@ -359,9 +359,9 @@ bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest)
|
||||
|
||||
void updateFSInfo() {
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#if WLED_FS == LITTLEFS
|
||||
fsBytesTotal = LITTLEFS.totalBytes();
|
||||
fsBytesUsed = LITTLEFS.usedBytes();
|
||||
#if WLED_FS == LITTLEFS || ESP_IDF_VERSION_MAJOR >= 4
|
||||
fsBytesTotal = WLED_FS.totalBytes();
|
||||
fsBytesUsed = WLED_FS.usedBytes();
|
||||
#else
|
||||
esp_spiffs_info(nullptr, &fsBytesTotal, &fsBytesUsed);
|
||||
#endif
|
||||
|
||||
@@ -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-b6<br>Download the latest binary: <a
|
||||
Installed version: 0.13.0-b7<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,",o=e.length;for(i=0;i<o;i++){var t=e[i];t.length>6&&(t=t.substring(2)),n+="#"+t,i<o-1&&(n+=",")}n+=")",document.getElementById("canv").style.background=n}function getLiveJson(e){try{var n=JSON.parse(e.data);n&&n.leds&&requestAnimationFrame((function(){updatePreview(n.leds)}))}catch(e){console.error("Live-Preview ws error:",e)}}var ws=top.window.ws;ws&&ws.readyState===WebSocket.OPEN?(console.info("Use top WS for peek"),ws.send("{'lv':true}")):(console.info("Peek ws opening"),(ws=new WebSocket("ws://"+document.location.host+"/ws")).onopen=function(){console.info("Peek WS opened"),ws.send("{'lv':true}")}),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=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)
|
||||
</script></body></html>)=====";
|
||||
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
4375
wled00/html_ui.h
4375
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-b6/%i"),VERSION);
|
||||
uint8_t vlen = sprintf_P(out+lengthSum,PSTR("0.13.0-b7/%i"),VERSION);
|
||||
out[16] = vlen; lengthSum += vlen;
|
||||
uint8_t hlen = 7;
|
||||
#ifdef ESP8266
|
||||
|
||||
134
wled00/ir.cpp
134
wled00/ir.cpp
@@ -72,7 +72,7 @@ void decBrightness()
|
||||
void presetFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID)
|
||||
{
|
||||
byte prevError = errorFlag;
|
||||
if (!applyPreset(presetID, CALL_MODE_BUTTON)) {
|
||||
if (!applyPreset(presetID, CALL_MODE_BUTTON_PRESET)) {
|
||||
effectCurrent = effectID;
|
||||
effectPalette = paletteID;
|
||||
errorFlag = prevError; //clear error 12 from non-existent preset
|
||||
@@ -87,7 +87,7 @@ bool decodeIRCustom(uint32_t code)
|
||||
{
|
||||
//just examples, feel free to modify or remove
|
||||
case IRCUSTOM_ONOFF : toggleOnOff(); break;
|
||||
case IRCUSTOM_MACRO1 : applyPreset(1, CALL_MODE_BUTTON); break;
|
||||
case IRCUSTOM_MACRO1 : applyPreset(1, CALL_MODE_BUTTON_PRESET); break;
|
||||
|
||||
default: return false;
|
||||
}
|
||||
@@ -162,6 +162,7 @@ void decodeIR(uint32_t code)
|
||||
return;
|
||||
}
|
||||
lastValidCode = 0; irTimesRepeated = 0;
|
||||
lastRepeatableAction = ACTION_NONE;
|
||||
if (decodeIRCustom(code)) return;
|
||||
if (irEnabled == 8) { // any remote configurable with ir.json file
|
||||
decodeIRJson(code);
|
||||
@@ -193,53 +194,31 @@ void decodeIR(uint32_t code)
|
||||
colorUpdated(CALL_MODE_BUTTON); //for notifier, IR is considered a button input
|
||||
}
|
||||
|
||||
void applyRepeatActions(){
|
||||
|
||||
if (lastRepeatableAction == ACTION_BRIGHT_UP)
|
||||
{
|
||||
incBrightness(); colorUpdated(CALL_MODE_BUTTON);
|
||||
}
|
||||
else if (lastRepeatableAction == ACTION_BRIGHT_DOWN )
|
||||
{
|
||||
decBrightness(); colorUpdated(CALL_MODE_BUTTON);
|
||||
}
|
||||
|
||||
if (lastRepeatableAction == ACTION_SPEED_UP)
|
||||
{
|
||||
changeEffectSpeed(lastRepeatableValue); colorUpdated(CALL_MODE_BUTTON);
|
||||
}
|
||||
else if (lastRepeatableAction == ACTION_SPEED_DOWN )
|
||||
{
|
||||
changeEffectSpeed(lastRepeatableValue); colorUpdated(CALL_MODE_BUTTON);
|
||||
}
|
||||
|
||||
if (lastRepeatableAction == ACTION_INTENSITY_UP)
|
||||
{
|
||||
changeEffectIntensity(lastRepeatableValue); colorUpdated(CALL_MODE_BUTTON);
|
||||
}
|
||||
else if (lastRepeatableAction == ACTION_INTENSITY_DOWN )
|
||||
{
|
||||
changeEffectIntensity(lastRepeatableValue); colorUpdated(CALL_MODE_BUTTON);
|
||||
}
|
||||
|
||||
if (lastValidCode == IR40_WPLUS)
|
||||
{
|
||||
relativeChangeWhite(10); colorUpdated(CALL_MODE_BUTTON);
|
||||
}
|
||||
else if (lastValidCode == IR40_WMINUS)
|
||||
{
|
||||
relativeChangeWhite(-10, 5); colorUpdated(CALL_MODE_BUTTON);
|
||||
}
|
||||
else if ((lastValidCode == IR24_ON || lastValidCode == IR40_ON) && irTimesRepeated > 7 )
|
||||
{
|
||||
nightlightActive = true;
|
||||
nightlightStartTime = millis();
|
||||
colorUpdated(CALL_MODE_BUTTON);
|
||||
}
|
||||
else if (irEnabled == 8)
|
||||
{
|
||||
decodeIRJson(lastValidCode);
|
||||
}
|
||||
void applyRepeatActions()
|
||||
{
|
||||
if (irEnabled == 8) {
|
||||
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;
|
||||
default: break;
|
||||
}
|
||||
if (lastValidCode == IR40_WPLUS) {
|
||||
relativeChangeWhite(10);
|
||||
colorUpdated(CALL_MODE_BUTTON);
|
||||
} else if (lastValidCode == IR40_WMINUS) {
|
||||
relativeChangeWhite(-10, 5);
|
||||
colorUpdated(CALL_MODE_BUTTON);
|
||||
} else if ((lastValidCode == IR24_ON || lastValidCode == IR40_ON) && irTimesRepeated > 7 ) {
|
||||
nightlightActive = true;
|
||||
nightlightStartTime = millis();
|
||||
colorUpdated(CALL_MODE_BUTTON);
|
||||
}
|
||||
}
|
||||
|
||||
void decodeIR24(uint32_t code)
|
||||
@@ -363,20 +342,20 @@ void decodeIR40(uint32_t code)
|
||||
case IR40_MAGENTA : colorFromUint24(COLOR_MAGENTA); break;
|
||||
case IR40_PINK : colorFromUint24(COLOR_PINK); break;
|
||||
case IR40_WARMWHITE2 : {
|
||||
if (strip.isRgbw) { colorFromUint32(COLOR2_WARMWHITE2); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_WARMWHITE2); } break;
|
||||
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_WARMWHITE2); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_WARMWHITE2); } break;
|
||||
case IR40_WARMWHITE : {
|
||||
if (strip.isRgbw) { colorFromUint32(COLOR2_WARMWHITE); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_WARMWHITE); } break;
|
||||
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_WARMWHITE); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_WARMWHITE); } break;
|
||||
case IR40_WHITE : {
|
||||
if (strip.isRgbw) { colorFromUint32(COLOR2_NEUTRALWHITE); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_NEUTRALWHITE); } break;
|
||||
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_NEUTRALWHITE); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_NEUTRALWHITE); } break;
|
||||
case IR40_COLDWHITE : {
|
||||
if (strip.isRgbw) { colorFromUint32(COLOR2_COLDWHITE); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_COLDWHITE); } break;
|
||||
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_COLDWHITE); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_COLDWHITE); } break;
|
||||
case IR40_COLDWHITE2 : {
|
||||
if (strip.isRgbw) { colorFromUint32(COLOR2_COLDWHITE2); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_COLDWHITE2); } break;
|
||||
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;
|
||||
@@ -420,22 +399,22 @@ void decodeIR44(uint32_t code)
|
||||
case IR44_MAGENTA : colorFromUint24(COLOR_MAGENTA); break;
|
||||
case IR44_PINK : colorFromUint24(COLOR_PINK); break;
|
||||
case IR44_WHITE : {
|
||||
if (strip.isRgbw) {
|
||||
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.isRgbw) { colorFromUint32(COLOR2_WARMWHITE2); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_WARMWHITE2); } break;
|
||||
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_WARMWHITE2); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_WARMWHITE2); } break;
|
||||
case IR44_WARMWHITE : {
|
||||
if (strip.isRgbw) { colorFromUint32(COLOR2_WARMWHITE); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_WARMWHITE); } break;
|
||||
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_WARMWHITE); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_WARMWHITE); } break;
|
||||
case IR44_COLDWHITE : {
|
||||
if (strip.isRgbw) { colorFromUint32(COLOR2_COLDWHITE); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_COLDWHITE); } break;
|
||||
if (strip.hasWhiteChannel()) {colorFromUint32(COLOR2_COLDWHITE); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_COLDWHITE); } break;
|
||||
case IR44_COLDWHITE2 : {
|
||||
if (strip.isRgbw) { colorFromUint32(COLOR2_COLDWHITE2); effectCurrent = 0; }
|
||||
else colorFromUint24(COLOR_COLDWHITE2); } break;
|
||||
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;
|
||||
@@ -596,7 +575,7 @@ void decodeIRJson(uint32_t code)
|
||||
cmdStr = fdo["cmd"].as<String>();
|
||||
jsonCmdObj = fdo["cmd"]; //object
|
||||
|
||||
if (!cmdStr.isEmpty())
|
||||
if (jsonCmdObj.isNull())
|
||||
{
|
||||
if (cmdStr.startsWith("!")) {
|
||||
// call limited set of C functions
|
||||
@@ -626,12 +605,19 @@ void decodeIRJson(uint32_t code)
|
||||
if (!cmdStr.startsWith("win&")) {
|
||||
cmdStr = "win&" + cmdStr;
|
||||
}
|
||||
handleSet(nullptr, cmdStr, false);
|
||||
fdo.clear(); //clear JSON buffer (it is no longer needed)
|
||||
handleSet(nullptr, cmdStr, false); // no colorUpdated() call here
|
||||
}
|
||||
colorUpdated(CALL_MODE_BUTTON);
|
||||
} else if (!jsonCmdObj.isNull()) {
|
||||
} else {
|
||||
// command is JSON object
|
||||
deserializeState(jsonCmdObj, CALL_MODE_BUTTON);
|
||||
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);
|
||||
}
|
||||
}
|
||||
releaseJSONBufferLock();
|
||||
}
|
||||
@@ -661,7 +647,7 @@ void handleIR()
|
||||
{
|
||||
if (results.value != 0) // only print results if anything is received ( != 0 )
|
||||
{
|
||||
if (!pinManager.isPinAllocated(1)) //GPIO 1 - Serial TX pin
|
||||
if (!pinManager.isPinAllocated(1) || pinManager.getPinOwner(1) == PinOwner::DebugOut) //GPIO 1 - Serial TX pin
|
||||
Serial.printf_P(PSTR("IR recv: 0x%lX\n"), (unsigned long)results.value);
|
||||
}
|
||||
decodeIR(results.value);
|
||||
|
||||
253
wled00/json.cpp
253
wled00/json.cpp
@@ -8,7 +8,7 @@
|
||||
|
||||
bool getVal(JsonVariant elem, byte* val, byte vmin=0, byte vmax=255) {
|
||||
if (elem.is<int>()) {
|
||||
if (elem < 0) return false; //ignore e.g. {"ps":-1}
|
||||
if (elem < 0) return false; //ignore e.g. {"ps":-1}
|
||||
*val = elem;
|
||||
return true;
|
||||
} else if (elem.is<const char*>()) {
|
||||
@@ -27,16 +27,33 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
if (id >= strip.getMaxSegments()) return;
|
||||
|
||||
WS2812FX::Segment& seg = strip.getSegment(id);
|
||||
//WS2812FX::Segment prev;
|
||||
//prev = seg; //make a backup so we can tell if something changed
|
||||
WS2812FX::Segment prev = seg; //make a backup so we can tell if something changed
|
||||
|
||||
uint16_t start = elem["start"] | seg.start;
|
||||
int stop = elem["stop"] | -1;
|
||||
if (stop < 0) {
|
||||
uint16_t len = elem[F("len")];
|
||||
uint16_t len = elem["len"];
|
||||
stop = (len > 0) ? start + len : seg.stop;
|
||||
}
|
||||
|
||||
//repeat, multiplies segment until all LEDs are used, or max segments reached
|
||||
bool repeat = elem["rpt"] | false;
|
||||
if (repeat && stop>0) {
|
||||
elem.remove("id"); // remove for recursive call
|
||||
elem.remove("rpt"); // remove for recursive call
|
||||
elem.remove("n"); // remove for recursive call
|
||||
uint16_t len = stop - start;
|
||||
for (byte i=id+1; i<strip.getMaxSegments(); i++) {
|
||||
start = start + len;
|
||||
if (start >= strip.getLengthTotal()) break;
|
||||
elem["start"] = start;
|
||||
elem["stop"] = start + len;
|
||||
elem["rev"] = !elem["rev"]; // alternate reverse on even/odd segments
|
||||
deserializeSegment(elem, i, presetId); // recursive call with new id
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (elem["n"]) {
|
||||
// name field exists
|
||||
if (seg.name) { //clear old name
|
||||
@@ -64,7 +81,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
|
||||
uint16_t grp = elem["grp"] | seg.grouping;
|
||||
uint16_t spc = elem[F("spc")] | seg.spacing;
|
||||
uint16_t of = seg.offset;
|
||||
uint16_t of = seg.offset;
|
||||
|
||||
uint16_t len = 1;
|
||||
if (stop > start) len = stop - start;
|
||||
@@ -76,7 +93,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
of = offsetAbs;
|
||||
}
|
||||
if (stop > start && of > len -1) of = len -1;
|
||||
strip.setSegment(id, start, stop, grp, spc, of);
|
||||
strip.setSegment(id, start, stop, grp, spc, of);
|
||||
|
||||
byte segbri = 0;
|
||||
if (getVal(elem["bri"], &segbri)) {
|
||||
@@ -87,10 +104,11 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
bool on = elem["on"] | seg.getOption(SEG_OPTION_ON);
|
||||
if (elem["on"].is<const char*>() && elem["on"].as<const char*>()[0] == 't') on = !on;
|
||||
seg.setOption(SEG_OPTION_ON, on, id);
|
||||
|
||||
uint8_t cctPrev = seg.cct;
|
||||
bool frz = elem["frz"] | seg.getOption(SEG_OPTION_FREEZE);
|
||||
if (elem["frz"].is<const char*>() && elem["frz"].as<const char*>()[0] == 't') frz = !seg.getOption(SEG_OPTION_FREEZE);
|
||||
seg.setOption(SEG_OPTION_FREEZE, frz, id);
|
||||
|
||||
seg.setCCT(elem["cct"] | seg.cct, id);
|
||||
if (seg.cct != cctPrev && id == strip.getMainSegmentId()) effectChanged = true; //send UDP
|
||||
|
||||
JsonArray colarr = elem["col"];
|
||||
if (!colarr.isNull())
|
||||
@@ -117,21 +135,14 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
byte sz = colX.size();
|
||||
if (sz == 0) continue; //do nothing on empty array
|
||||
|
||||
byte cp = copyArray(colX, rgbw, 4);
|
||||
if (cp == 1 && rgbw[0] == 0)
|
||||
seg.setColor(i, 0, id);
|
||||
copyArray(colX, rgbw, 4);
|
||||
colValid = true;
|
||||
}
|
||||
|
||||
if (!colValid) continue;
|
||||
if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment
|
||||
{
|
||||
if (i == 0) {col[0] = rgbw[0]; col[1] = rgbw[1]; col[2] = rgbw[2]; col[3] = rgbw[3];}
|
||||
if (i == 1) {colSec[0] = rgbw[0]; colSec[1] = rgbw[1]; colSec[2] = rgbw[2]; colSec[3] = rgbw[3];}
|
||||
} else { //normal case, apply directly to segment
|
||||
seg.setColor(i, ((rgbw[3] << 24) | ((rgbw[0]&0xFF) << 16) | ((rgbw[1]&0xFF) << 8) | ((rgbw[2]&0xFF))), id);
|
||||
if (seg.mode == FX_MODE_STATIC) strip.trigger(); //instant refresh
|
||||
}
|
||||
|
||||
seg.setColor(i, RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3]), id);
|
||||
if (seg.mode == FX_MODE_STATIC) strip.trigger(); //instant refresh
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,35 +158,24 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
}
|
||||
#endif
|
||||
|
||||
//if (pal != seg.palette && pal < strip.getPaletteCount()) strip.setPalette(pal);
|
||||
seg.setOption(SEG_OPTION_SELECTED, elem[F("sel")] | seg.getOption(SEG_OPTION_SELECTED));
|
||||
seg.setOption(SEG_OPTION_REVERSED, elem["rev"] | seg.getOption(SEG_OPTION_REVERSED));
|
||||
seg.setOption(SEG_OPTION_MIRROR , elem[F("mi")] | seg.getOption(SEG_OPTION_MIRROR ));
|
||||
|
||||
//temporary, strip object gets updated via colorUpdated()
|
||||
if (id == strip.getMainSegmentId()) {
|
||||
byte effectPrev = effectCurrent;
|
||||
if (getVal(elem["fx"], &effectCurrent, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 0-255 exact value)
|
||||
if (!presetId && effectCurrent != effectPrev) unloadPlaylist(); //stop playlist if active and FX changed manually
|
||||
}
|
||||
effectSpeed = elem[F("sx")] | effectSpeed;
|
||||
effectIntensity = elem[F("ix")] | effectIntensity;
|
||||
getVal(elem["pal"], &effectPalette, 1, strip.getPaletteCount());
|
||||
} else { //permanent
|
||||
byte fx = seg.mode;
|
||||
byte fxPrev = fx;
|
||||
if (getVal(elem["fx"], &fx, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 0-255 exact value)
|
||||
strip.setMode(id, fx);
|
||||
if (!presetId && seg.mode != fxPrev) unloadPlaylist(); //stop playlist if active and FX changed manually
|
||||
}
|
||||
seg.speed = elem[F("sx")] | seg.speed;
|
||||
seg.intensity = elem[F("ix")] | seg.intensity;
|
||||
getVal(elem["pal"], &seg.palette, 1, strip.getPaletteCount());
|
||||
byte fx = seg.mode;
|
||||
if (getVal(elem["fx"], &fx, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 1-255 exact value)
|
||||
if (!presetId && currentPlaylist>=0) unloadPlaylist();
|
||||
strip.setMode(id, fx);
|
||||
}
|
||||
|
||||
//getVal also supports inc/decrementing and random
|
||||
getVal(elem[F("sx")], &seg.speed, 0, 255);
|
||||
getVal(elem[F("ix")], &seg.intensity, 0, 255);
|
||||
getVal(elem["pal"], &seg.palette, 1, strip.getPaletteCount());
|
||||
|
||||
JsonArray iarr = elem[F("i")]; //set individual LEDs
|
||||
if (!iarr.isNull()) {
|
||||
strip.setPixelSegment(id);
|
||||
uint8_t oldSegId = strip.setPixelSegment(id);
|
||||
|
||||
//freeze and init to black
|
||||
if (!seg.getOption(SEG_OPTION_FREEZE)) {
|
||||
@@ -221,17 +221,21 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
set = 0;
|
||||
}
|
||||
}
|
||||
strip.setPixelSegment(255);
|
||||
strip.setPixelSegment(oldSegId);
|
||||
strip.trigger();
|
||||
} else { //return to regular effect
|
||||
} else if (!elem["frz"] && iarr.isNull()) { //return to regular effect
|
||||
seg.setOption(SEG_OPTION_FREEZE, false);
|
||||
}
|
||||
return; // seg.differs(prev);
|
||||
// send UDP if not in preset and something changed that is not just selection
|
||||
//if (!presetId && (seg.differs(prev) & 0x7F)) stateChanged = true;
|
||||
// send UDP if something changed that is not just selection
|
||||
if (seg.differs(prev) & 0x7F) stateChanged = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// deserializes WLED state (fileDoc points to doc object if called from web server)
|
||||
bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
{
|
||||
strip.applyToAllSelected = false;
|
||||
bool stateResponse = root[F("v")] | false;
|
||||
|
||||
getVal(root["bri"], &bri);
|
||||
@@ -264,16 +268,16 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
tr = root[F("tb")] | -1;
|
||||
if (tr >= 0) strip.timebase = ((uint32_t)tr) - millis();
|
||||
|
||||
JsonObject nl = root["nl"];
|
||||
JsonObject nl = root["nl"];
|
||||
nightlightActive = nl["on"] | nightlightActive;
|
||||
nightlightDelayMins = nl[F("dur")] | nightlightDelayMins;
|
||||
nightlightMode = nl[F("mode")] | nightlightMode;
|
||||
nightlightDelayMins = nl["dur"] | nightlightDelayMins;
|
||||
nightlightMode = nl["mode"] | nightlightMode;
|
||||
nightlightTargetBri = nl[F("tbri")] | nightlightTargetBri;
|
||||
|
||||
JsonObject udpn = root["udpn"];
|
||||
JsonObject udpn = root["udpn"];
|
||||
notifyDirect = udpn["send"] | notifyDirect;
|
||||
receiveNotifications = udpn["recv"] | receiveNotifications;
|
||||
bool noNotification = udpn[F("nn")]; //send no notification just for this request
|
||||
if ((bool)udpn[F("nn")]) callMode = CALL_MODE_NO_NOTIFY; //send no notification just for this request
|
||||
|
||||
unsigned long timein = root[F("time")] | UINT32_MAX; //backup time source if NTP not synced
|
||||
if (timein != UINT32_MAX) {
|
||||
@@ -292,34 +296,30 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
else realtimeTimeout = 0; //cancel realtime mode immediately
|
||||
}
|
||||
|
||||
byte prevMain = strip.getMainSegmentId();
|
||||
strip.mainSegment = root[F("mainseg")] | prevMain;
|
||||
if (strip.getMainSegmentId() != prevMain) setValuesFromMainSeg();
|
||||
strip.setMainSegmentId(root[F("mainseg")] | strip.getMainSegmentId());
|
||||
|
||||
int it = 0;
|
||||
JsonVariant segVar = root["seg"];
|
||||
if (segVar.is<JsonObject>())
|
||||
{
|
||||
int id = segVar["id"] | -1;
|
||||
|
||||
if (id < 0) { //set all selected segments
|
||||
//if "seg" is not an array and ID not specified, apply to all selected/checked segments
|
||||
if (id < 0) {
|
||||
//apply all selected segments
|
||||
bool didSet = false;
|
||||
byte lowestActive = 99;
|
||||
for (byte s = 0; s < strip.getMaxSegments(); s++)
|
||||
{
|
||||
WS2812FX::Segment sg = strip.getSegment(s);
|
||||
if (sg.isActive())
|
||||
{
|
||||
if (lowestActive == 99) lowestActive = s;
|
||||
for (byte s = 0; s < strip.getMaxSegments(); s++) {
|
||||
WS2812FX::Segment &sg = strip.getSegment(s);
|
||||
if (sg.isActive()) {
|
||||
if (sg.isSelected()) {
|
||||
deserializeSegment(segVar, s, presetId);
|
||||
didSet = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!didSet && lowestActive < strip.getMaxSegments()) deserializeSegment(segVar, lowestActive, presetId);
|
||||
} else { //set only the segment with the specified ID
|
||||
deserializeSegment(segVar, it, presetId);
|
||||
//if none selected, apply to the main segment
|
||||
if (!didSet) deserializeSegment(segVar, strip.getMainSegmentId(), presetId);
|
||||
} else {
|
||||
deserializeSegment(segVar, id, presetId); //apply only the segment with the specified ID
|
||||
}
|
||||
} else {
|
||||
JsonArray segs = segVar.as<JsonArray>();
|
||||
@@ -369,63 +369,60 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
JsonObject playlist = root[F("playlist")];
|
||||
if (!playlist.isNull() && loadPlaylist(playlist, presetId)) {
|
||||
//do not notify here, because the first playlist entry will do
|
||||
noNotification = true;
|
||||
if (root["on"].isNull()) callMode = CALL_MODE_NO_NOTIFY;
|
||||
else callMode = CALL_MODE_DIRECT_CHANGE; // possible bugfix for playlist only containing HTTP API preset FX=~
|
||||
} else {
|
||||
interfaceUpdateCallMode = CALL_MODE_WS_SEND;
|
||||
}
|
||||
|
||||
colorUpdated(noNotification ? CALL_MODE_NO_NOTIFY : callMode);
|
||||
stateUpdated(callMode);
|
||||
|
||||
return stateResponse;
|
||||
}
|
||||
|
||||
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset, bool segmentBounds)
|
||||
{
|
||||
root["id"] = id;
|
||||
root["id"] = id;
|
||||
if (segmentBounds) {
|
||||
root["start"] = seg.start;
|
||||
root["stop"] = seg.stop;
|
||||
}
|
||||
if (!forPreset) root[F("len")] = seg.stop - seg.start;
|
||||
if (!forPreset) root["len"] = seg.stop - seg.start;
|
||||
root["grp"] = seg.grouping;
|
||||
root[F("spc")] = seg.spacing;
|
||||
root[F("of")] = seg.offset;
|
||||
root["on"] = seg.getOption(SEG_OPTION_ON);
|
||||
root["frz"] = seg.getOption(SEG_OPTION_FREEZE);
|
||||
byte segbri = seg.opacity;
|
||||
root["bri"] = (segbri) ? segbri : 255;
|
||||
root["cct"] = seg.cct;
|
||||
|
||||
if (segmentBounds && seg.name != nullptr) root["n"] = reinterpret_cast<const char *>(seg.name); //not good practice, but decreases required JSON buffer
|
||||
|
||||
char colstr[70]; colstr[0] = '['; colstr[1] = '\0'; //max len 68 (5 chan, all 255)
|
||||
|
||||
for (uint8_t i = 0; i < 3; i++)
|
||||
{
|
||||
// to conserve RAM we will serialize the col array manually
|
||||
// this will reduce RAM footprint from ~300 bytes to 84 bytes per segment
|
||||
char colstr[70]; colstr[0] = '['; colstr[1] = '\0'; //max len 68 (5 chan, all 255)
|
||||
const char *format = strip.hasWhiteChannel() ? PSTR("[%u,%u,%u,%u]") : PSTR("[%u,%u,%u]");
|
||||
for (uint8_t i = 0; i < 3; i++)
|
||||
{
|
||||
byte segcol[4]; byte* c = segcol;
|
||||
|
||||
if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment
|
||||
{
|
||||
c = (i == 0)? col:colSec;
|
||||
} else {
|
||||
segcol[0] = (byte)(seg.colors[i] >> 16); segcol[1] = (byte)(seg.colors[i] >> 8);
|
||||
segcol[2] = (byte)(seg.colors[i]); segcol[3] = (byte)(seg.colors[i] >> 24);
|
||||
}
|
||||
|
||||
segcol[0] = R(seg.colors[i]);
|
||||
segcol[1] = G(seg.colors[i]);
|
||||
segcol[2] = B(seg.colors[i]);
|
||||
segcol[3] = W(seg.colors[i]);
|
||||
char tmpcol[22];
|
||||
if (strip.isRgbw) sprintf_P(tmpcol, PSTR("[%u,%u,%u,%u]"), c[0], c[1], c[2], c[3]);
|
||||
else sprintf_P(tmpcol, PSTR("[%u,%u,%u]"), c[0], c[1], c[2]);
|
||||
|
||||
strcat(colstr, i<2 ? strcat(tmpcol,",") : tmpcol);
|
||||
}
|
||||
strcat(colstr,"]");
|
||||
sprintf_P(tmpcol, format, (unsigned)c[0], (unsigned)c[1], (unsigned)c[2], (unsigned)c[3]);
|
||||
strcat(colstr, i<2 ? strcat(tmpcol, ",") : tmpcol);
|
||||
}
|
||||
strcat(colstr, "]");
|
||||
root["col"] = serialized(colstr);
|
||||
|
||||
root["fx"] = seg.mode;
|
||||
root[F("sx")] = seg.speed;
|
||||
root[F("ix")] = seg.intensity;
|
||||
root["pal"] = seg.palette;
|
||||
root[F("sel")] = seg.isSelected();
|
||||
root["rev"] = seg.getOption(SEG_OPTION_REVERSED);
|
||||
root["fx"] = seg.mode;
|
||||
root[F("sx")] = seg.speed;
|
||||
root[F("ix")] = seg.intensity;
|
||||
root["pal"] = seg.palette;
|
||||
root[F("sel")] = seg.isSelected();
|
||||
root["rev"] = seg.getOption(SEG_OPTION_REVERSED);
|
||||
root[F("mi")] = seg.getOption(SEG_OPTION_MIRROR);
|
||||
}
|
||||
|
||||
@@ -438,7 +435,7 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
|
||||
}
|
||||
|
||||
if (!forPreset) {
|
||||
if (errorFlag) root[F("error")] = errorFlag;
|
||||
if (errorFlag) {root[F("error")] = errorFlag; errorFlag = ERR_NONE;} //prevent error message to persist on screen
|
||||
|
||||
root["ps"] = (currentPreset > 0) ? currentPreset : -1;
|
||||
root[F("pl")] = currentPlaylist;
|
||||
@@ -447,8 +444,8 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
|
||||
|
||||
JsonObject nl = root.createNestedObject("nl");
|
||||
nl["on"] = nightlightActive;
|
||||
nl[F("dur")] = nightlightDelayMins;
|
||||
nl[F("mode")] = nightlightMode;
|
||||
nl["dur"] = nightlightDelayMins;
|
||||
nl["mode"] = nightlightMode;
|
||||
nl[F("tbri")] = nightlightTargetBri;
|
||||
if (nightlightActive) {
|
||||
nl[F("rem")] = (nightlightDelayMs - (millis() - nightlightStartTime)) / 1000; // seconds remaining
|
||||
@@ -466,11 +463,9 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
|
||||
root[F("mainseg")] = strip.getMainSegmentId();
|
||||
|
||||
JsonArray seg = root.createNestedArray("seg");
|
||||
for (byte s = 0; s < strip.getMaxSegments(); s++)
|
||||
{
|
||||
WS2812FX::Segment sg = strip.getSegment(s);
|
||||
if (sg.isActive())
|
||||
{
|
||||
for (byte s = 0; s < strip.getMaxSegments(); s++) {
|
||||
WS2812FX::Segment &sg = strip.getSegment(s);
|
||||
if (sg.isActive()) {
|
||||
JsonObject seg0 = seg.createNestedObject();
|
||||
serializeSegment(seg0, sg, s, forPreset, segmentBounds);
|
||||
} else if (forPreset && segmentBounds) { //disable segments not part of preset
|
||||
@@ -508,21 +503,32 @@ void serializeInfo(JsonObject root)
|
||||
|
||||
JsonObject leds = root.createNestedObject("leds");
|
||||
leds[F("count")] = strip.getLengthTotal();
|
||||
leds[F("rgbw")] = strip.isRgbw;
|
||||
leds[F("wv")] = false;
|
||||
leds["cct"] = correctWB || strip.hasCCTBus();
|
||||
switch (Bus::getAutoWhiteMode()) {
|
||||
case RGBW_MODE_MANUAL_ONLY:
|
||||
case RGBW_MODE_DUAL:
|
||||
if (strip.isRgbw) leds[F("wv")] = true;
|
||||
break;
|
||||
}
|
||||
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[F("fps")] = strip.getFps();
|
||||
leds["fps"] = strip.getFps();
|
||||
leds[F("maxpwr")] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0;
|
||||
leds[F("maxseg")] = strip.getMaxSegments();
|
||||
//leds[F("seglock")] = false; //might be used in the future to prevent modifications to segment config
|
||||
|
||||
uint8_t totalLC = 0;
|
||||
JsonArray lcarr = leds.createNestedArray(F("seglc"));
|
||||
uint8_t nSegs = strip.getLastActiveSegmentId();
|
||||
for (byte s = 0; s <= nSegs; s++) {
|
||||
uint8_t lc = strip.getSegment(s).getLightCapabilities();
|
||||
totalLC |= lc;
|
||||
lcarr.add(lc);
|
||||
}
|
||||
|
||||
leds["lc"] = totalLC;
|
||||
|
||||
root[F("str")] = syncToggleReceive;
|
||||
|
||||
@@ -596,9 +602,11 @@ void serializeInfo(JsonObject root)
|
||||
#endif
|
||||
|
||||
root[F("freeheap")] = ESP.getFreeHeap();
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
|
||||
if (psramFound()) root[F("psram")] = ESP.getFreePsram();
|
||||
#endif
|
||||
root[F("uptime")] = millis()/1000 + rolloverMillis*4294967;
|
||||
|
||||
|
||||
usermods.addToJsonInfo(root);
|
||||
|
||||
byte os = 0;
|
||||
@@ -645,7 +653,7 @@ void setPaletteColors(JsonArray json, CRGBPalette16 palette)
|
||||
for (int i = 0; i < 16; i++) {
|
||||
JsonArray colors = json.createNestedArray();
|
||||
CRGB color = palette[i];
|
||||
colors.add((((float)i / (float)16) * 255));
|
||||
colors.add(i<<4);
|
||||
colors.add(color.red);
|
||||
colors.add(color.green);
|
||||
colors.add(color.blue);
|
||||
@@ -707,9 +715,6 @@ void serializePalettes(JsonObject root, AsyncWebServerRequest* request)
|
||||
|
||||
for (int i = start; i < end; i++) {
|
||||
JsonArray curPalette = palettes.createNestedArray(String(i));
|
||||
CRGB prim;
|
||||
CRGB sec;
|
||||
CRGB ter;
|
||||
switch (i) {
|
||||
case 0: //default palette
|
||||
setPaletteColors(curPalette, PartyColors_p);
|
||||
@@ -814,10 +819,12 @@ void serveJson(AsyncWebServerRequest* request)
|
||||
else if (url.indexOf("si") > 0) subJson = 3;
|
||||
else if (url.indexOf("nodes") > 0) subJson = 4;
|
||||
else if (url.indexOf("palx") > 0) subJson = 5;
|
||||
#ifdef WLED_ENABLE_JSONLIVE
|
||||
else if (url.indexOf("live") > 0) {
|
||||
serveLiveLeds(request);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
else if (url.indexOf(F("eff")) > 0) {
|
||||
request->send_P(200, "application/json", JSON_mode_names);
|
||||
return;
|
||||
@@ -873,17 +880,18 @@ void serveJson(AsyncWebServerRequest* request)
|
||||
releaseJSONBufferLock();
|
||||
}
|
||||
|
||||
#ifdef WLED_ENABLE_JSONLIVE
|
||||
#define MAX_LIVE_LEDS 180
|
||||
|
||||
bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
|
||||
{
|
||||
#ifdef WLED_ENABLE_WEBSOCKETS
|
||||
AsyncWebSocketClient * wsc = nullptr;
|
||||
if (!request) { //not HTTP, use Websockets
|
||||
#ifdef WLED_ENABLE_WEBSOCKETS
|
||||
wsc = ws.client(wsClient);
|
||||
if (!wsc || wsc->queueLength() > 0) return false; //only send if queue free
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
uint16_t used = strip.getLengthTotal();
|
||||
uint16_t n = (used -1) /MAX_LIVE_LEDS +1; //only serve every n'th LED if count over MAX_LIVE_LEDS
|
||||
@@ -894,7 +902,11 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
|
||||
|
||||
for (uint16_t i= 0; i < used; i += n)
|
||||
{
|
||||
olen += sprintf(obuf + olen, "\"%06X\",", strip.getPixelColor(i) & 0xFFFFFF);
|
||||
uint32_t c = strip.getPixelColor(i);
|
||||
uint8_t r = qadd8(W(c), R(c)); //add white channel to RGB channels as a simple RGBW -> RGB map
|
||||
uint8_t g = qadd8(W(c), G(c));
|
||||
uint8_t b = qadd8(W(c), B(c));
|
||||
olen += sprintf(obuf + olen, "\"%06X\",", RGBW32(r,g,b,0));
|
||||
}
|
||||
olen -= 1;
|
||||
oappend((const char*)F("],\"n\":"));
|
||||
@@ -910,3 +922,4 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
160
wled00/led.cpp
160
wled00/led.cpp
@@ -3,9 +3,9 @@
|
||||
/*
|
||||
* LED methods
|
||||
*/
|
||||
void setValuesFromMainSeg()
|
||||
void setValuesFromFirstSelectedSeg()
|
||||
{
|
||||
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
|
||||
WS2812FX::Segment& seg = strip.getFirstSelectedSeg();
|
||||
colorFromUint32(seg.colors[0]);
|
||||
colorFromUint32(seg.colors[1], true);
|
||||
effectCurrent = seg.mode;
|
||||
@@ -15,6 +15,35 @@ void setValuesFromMainSeg()
|
||||
}
|
||||
|
||||
|
||||
// applies global legacy values (col, colSec, effectCurrent...)
|
||||
// problem: if the first selected segment already has the value to be set, other selected segments are not updated
|
||||
void applyValuesToSelectedSegs()
|
||||
{
|
||||
// copy of first selected segment to tell if value was updated
|
||||
uint8_t firstSel = strip.getFirstSelectedSegId();
|
||||
WS2812FX::Segment selsegPrev = strip.getSegment(firstSel);
|
||||
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
|
||||
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]);
|
||||
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;}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void resetTimebase()
|
||||
{
|
||||
strip.timebase = 0 - millis();
|
||||
@@ -43,9 +72,8 @@ byte scaledBri(byte in)
|
||||
}
|
||||
|
||||
|
||||
void setAllLeds() {
|
||||
strip.setColor(0, col[0], col[1], col[2], col[3]);
|
||||
strip.setColor(1, colSec[0], colSec[1], colSec[2], colSec[3]);
|
||||
//applies global brightness
|
||||
void applyBri() {
|
||||
if (!realtimeMode || !arlsForceMaxBri)
|
||||
{
|
||||
strip.setBrightness(scaledBri(briT));
|
||||
@@ -53,113 +81,72 @@ void setAllLeds() {
|
||||
}
|
||||
|
||||
|
||||
void setLedsStandard()
|
||||
{
|
||||
//applies global brightness and sets it as the "current" brightness (no transition)
|
||||
void applyFinalBri() {
|
||||
briOld = bri;
|
||||
briT = bri;
|
||||
setAllLeds();
|
||||
applyBri();
|
||||
}
|
||||
|
||||
|
||||
bool colorChanged()
|
||||
{
|
||||
for (byte i=0; i<4; i++)
|
||||
{
|
||||
if (col[i] != colIT[i]) return true;
|
||||
if (colSec[i] != colSecIT[i]) return true;
|
||||
}
|
||||
if (bri != briIT) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void colorUpdated(int callMode)
|
||||
{
|
||||
//called after every state changes, schedules interface updates, handles brightness transition and nightlight activation
|
||||
//unlike colorUpdated(), does NOT apply any colors or FX to segments
|
||||
void stateUpdated(byte callMode) {
|
||||
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification)
|
||||
// 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa
|
||||
if (callMode != CALL_MODE_INIT &&
|
||||
callMode != CALL_MODE_DIRECT_CHANGE &&
|
||||
callMode != CALL_MODE_NO_NOTIFY) strip.applyToAllSelected = true; //if not from JSON api, which directly sets segments
|
||||
// 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa 11: ws send only 12: button preset
|
||||
setValuesFromFirstSelectedSeg();
|
||||
|
||||
bool someSel = false;
|
||||
|
||||
if (callMode == CALL_MODE_NOTIFICATION) {
|
||||
someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
|
||||
}
|
||||
|
||||
//Notifier: apply received FX to selected segments only if actually receiving FX
|
||||
if (someSel) strip.applyToAllSelected = receiveNotificationEffects;
|
||||
|
||||
bool fxChanged = strip.setEffectConfig(effectCurrent, effectSpeed, effectIntensity, effectPalette) || effectChanged;
|
||||
bool colChanged = colorChanged();
|
||||
|
||||
//Notifier: apply received color to selected segments only if actually receiving color
|
||||
if (someSel) strip.applyToAllSelected = receiveNotificationColor;
|
||||
|
||||
if (fxChanged || colChanged)
|
||||
{
|
||||
effectChanged = false;
|
||||
if (bri != briOld || stateChanged) {
|
||||
if (realtimeTimeout == UINT32_MAX) realtimeTimeout = 0;
|
||||
currentPreset = 0; //something changed, so we are no longer in the preset
|
||||
if (stateChanged) currentPreset = 0; //something changed, so we are no longer in the preset
|
||||
|
||||
notify(callMode);
|
||||
if (callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) notify(callMode);
|
||||
|
||||
//set flag to update blynk, ws and mqtt
|
||||
interfaceUpdateCallMode = callMode;
|
||||
stateChanged = false;
|
||||
} else {
|
||||
if (nightlightActive && !nightlightActiveOld &&
|
||||
callMode != CALL_MODE_NOTIFICATION &&
|
||||
callMode != CALL_MODE_NO_NOTIFY)
|
||||
{
|
||||
if (nightlightActive && !nightlightActiveOld && callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) {
|
||||
notify(CALL_MODE_NIGHTLIGHT);
|
||||
interfaceUpdateCallMode = CALL_MODE_NIGHTLIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
if (!colChanged) return; //following code is for e.g. initiating transitions
|
||||
|
||||
if (callMode != CALL_MODE_NO_NOTIFY && nightlightActive && (nightlightMode == NL_MODE_FADE || nightlightMode == NL_MODE_COLORFADE))
|
||||
{
|
||||
if (callMode != CALL_MODE_NO_NOTIFY && nightlightActive && (nightlightMode == NL_MODE_FADE || nightlightMode == NL_MODE_COLORFADE)) {
|
||||
briNlT = bri;
|
||||
nightlightDelayMs -= (millis() - nightlightStartTime);
|
||||
nightlightStartTime = millis();
|
||||
}
|
||||
for (byte i=0; i<4; i++)
|
||||
{
|
||||
colIT[i] = col[i];
|
||||
colSecIT[i] = colSec[i];
|
||||
}
|
||||
if (briT == 0)
|
||||
{
|
||||
if (briT == 0) {
|
||||
if (callMode != CALL_MODE_NOTIFICATION) resetTimebase(); //effect start from beginning
|
||||
}
|
||||
|
||||
briIT = bri;
|
||||
if (bri > 0) briLast = bri;
|
||||
|
||||
//deactivate nightlight if target brightness is reached
|
||||
if (bri == nightlightTargetBri && callMode != CALL_MODE_NO_NOTIFY && nightlightMode != NL_MODE_SUN) nightlightActive = false;
|
||||
|
||||
if (fadeTransition)
|
||||
{
|
||||
if (fadeTransition) {
|
||||
//set correct delay if not using notification delay
|
||||
if (callMode != CALL_MODE_NOTIFICATION && !jsonTransitionOnce) transitionDelayTemp = transitionDelay;
|
||||
jsonTransitionOnce = false;
|
||||
strip.setTransition(transitionDelayTemp);
|
||||
if (transitionDelayTemp == 0) {setLedsStandard(); strip.trigger(); return;}
|
||||
|
||||
if (transitionActive)
|
||||
{
|
||||
if (transitionDelayTemp == 0) {
|
||||
applyFinalBri();
|
||||
strip.trigger();
|
||||
return;
|
||||
}
|
||||
|
||||
if (transitionActive) {
|
||||
briOld = briT;
|
||||
tperLast = 0;
|
||||
}
|
||||
strip.setTransitionMode(true);
|
||||
transitionActive = true;
|
||||
transitionStartTime = millis();
|
||||
} else
|
||||
{
|
||||
} else {
|
||||
strip.setTransition(0);
|
||||
setLedsStandard();
|
||||
applyFinalBri();
|
||||
strip.trigger();
|
||||
}
|
||||
}
|
||||
@@ -168,10 +155,8 @@ void colorUpdated(int callMode)
|
||||
void updateInterfaces(uint8_t callMode)
|
||||
{
|
||||
sendDataWs();
|
||||
if (callMode == CALL_MODE_WS_SEND) {
|
||||
lastInterfaceUpdate = millis();
|
||||
return;
|
||||
}
|
||||
lastInterfaceUpdate = millis();
|
||||
if (callMode == CALL_MODE_WS_SEND) return;
|
||||
|
||||
#ifndef WLED_DISABLE_ALEXA
|
||||
if (espalexaDevice != nullptr && callMode != CALL_MODE_ALEXA) {
|
||||
@@ -184,14 +169,13 @@ void updateInterfaces(uint8_t callMode)
|
||||
callMode != CALL_MODE_NO_NOTIFY) updateBlynk();
|
||||
#endif
|
||||
doPublishMqtt = true;
|
||||
lastInterfaceUpdate = millis();
|
||||
}
|
||||
|
||||
|
||||
void handleTransitions()
|
||||
{
|
||||
//handle still pending interface update
|
||||
if (interfaceUpdateCallMode && millis() - lastInterfaceUpdate > 2000)
|
||||
if (interfaceUpdateCallMode && millis() - lastInterfaceUpdate > INTERFACE_UPDATE_COOLDOWN)
|
||||
{
|
||||
updateInterfaces(interfaceUpdateCallMode);
|
||||
interfaceUpdateCallMode = 0; //disable
|
||||
@@ -206,20 +190,34 @@ void handleTransitions()
|
||||
strip.setTransitionMode(false);
|
||||
transitionActive = false;
|
||||
tperLast = 0;
|
||||
setLedsStandard();
|
||||
applyFinalBri();
|
||||
return;
|
||||
}
|
||||
if (tper - tperLast < 0.004) return;
|
||||
tperLast = tper;
|
||||
briT = briOld +((bri - briOld )*tper);
|
||||
|
||||
setAllLeds();
|
||||
applyBri();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//legacy method, applies values from col, effectCurrent, ... to selected segments
|
||||
void colorUpdated(byte callMode){
|
||||
applyValuesToSelectedSegs();
|
||||
stateUpdated(callMode);
|
||||
}
|
||||
|
||||
|
||||
void handleNightlight()
|
||||
{
|
||||
/*
|
||||
static unsigned long lastNlUpdate;
|
||||
unsigned long now = millis();
|
||||
if (now < 100 && lastNlUpdate > 0) lastNlUpdate = 0; //take care of millis() rollover
|
||||
if (now - lastNlUpdate < 100) return; //allow only 10 NL updates per second
|
||||
lastNlUpdate = now;
|
||||
*/
|
||||
if (nightlightActive)
|
||||
{
|
||||
if (!nightlightActiveOld) //init
|
||||
@@ -236,7 +234,7 @@ void handleNightlight()
|
||||
colNlT[1] = effectSpeed;
|
||||
colNlT[2] = effectPalette;
|
||||
|
||||
strip.setMode(strip.getMainSegmentId(), FX_MODE_STATIC); //make sure seg runtime is reset if left in sunrise mode
|
||||
strip.setMode(strip.getFirstSelectedSegId(), FX_MODE_STATIC); // make sure seg runtime is reset if it was in sunrise mode
|
||||
effectCurrent = FX_MODE_SUNRISE;
|
||||
effectSpeed = nightlightDelayMins;
|
||||
effectPalette = 0;
|
||||
@@ -273,7 +271,7 @@ void handleNightlight()
|
||||
effectSpeed = colNlT[1];
|
||||
effectPalette = colNlT[2];
|
||||
toggleOnOff();
|
||||
setLedsStandard();
|
||||
applyFinalBri();
|
||||
}
|
||||
}
|
||||
#ifndef WLED_DISABLE_BLYNK
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
|
||||
void parseMQTTBriPayload(char* payload)
|
||||
{
|
||||
if (strstr(payload, "ON") || strstr(payload, "on") || strstr(payload, "true")) {bri = briLast; colorUpdated(1);}
|
||||
else if (strstr(payload, "T" ) || strstr(payload, "t" )) {toggleOnOff(); colorUpdated(1);}
|
||||
if (strstr(payload, "ON") || strstr(payload, "on") || strstr(payload, "true")) {bri = briLast; stateUpdated(1);}
|
||||
else if (strstr(payload, "T" ) || strstr(payload, "t" )) {toggleOnOff(); stateUpdated(1);}
|
||||
else {
|
||||
uint8_t in = strtoul(payload, NULL, 10);
|
||||
if (in == 0 && bri > 0) briLast = bri;
|
||||
bri = in;
|
||||
colorUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
stateUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
* DO NOT make changes to the "my_config_sample.h" file directly! Your changes will not be applied.
|
||||
*/
|
||||
|
||||
// force the compiler to show a warning to confirm that this file is included
|
||||
#warning **** my_config.h: Settings from this file are honored ****
|
||||
// uncomment to force the compiler to show a warning to confirm that this file is included
|
||||
//#warning **** my_config.h: Settings from this file are honored ****
|
||||
|
||||
/* Uncomment to use your WIFI settings as defaults
|
||||
//WARNING: this will hardcode these as the default even after a factory reset
|
||||
|
||||
@@ -318,6 +318,32 @@ byte weekdayMondayFirst()
|
||||
return wd;
|
||||
}
|
||||
|
||||
bool isTodayInDateRange(byte monthStart, byte dayStart, byte monthEnd, byte dayEnd)
|
||||
{
|
||||
if (monthStart == 0 || dayStart == 0) return true;
|
||||
if (monthEnd == 0) monthEnd = monthStart;
|
||||
if (dayEnd == 0) dayEnd = 31;
|
||||
byte d = day(localTime);
|
||||
byte m = month(localTime);
|
||||
|
||||
if (monthStart < monthEnd) {
|
||||
if (m > monthStart && m < monthEnd) return true;
|
||||
if (m == monthStart) return (d >= dayStart);
|
||||
if (m == monthEnd) return (d <= dayEnd);
|
||||
return false;
|
||||
}
|
||||
if (monthEnd < monthStart) { //range spans change of year
|
||||
if (m > monthStart || m < monthEnd) return true;
|
||||
if (m == monthStart) return (d >= dayStart);
|
||||
if (m == monthEnd) return (d <= dayEnd);
|
||||
return false;
|
||||
}
|
||||
|
||||
//start month and end month are the same
|
||||
if (dayEnd < dayStart) return (m != monthStart || (d <= dayEnd || d >= dayStart)); //all year, except the designated days in this month
|
||||
return (m == monthStart && d >= dayStart && d <= dayEnd); //just the designated days this month
|
||||
}
|
||||
|
||||
void checkTimers()
|
||||
{
|
||||
if (lastTimerMinute != minute(localTime)) //only check once a new minute begins
|
||||
@@ -331,11 +357,14 @@ void checkTimers()
|
||||
for (uint8_t i = 0; i < 8; i++)
|
||||
{
|
||||
if (timerMacro[i] != 0
|
||||
&& (timerWeekday[i] & 0x01) //timer is enabled
|
||||
&& (timerHours[i] == hour(localTime) || timerHours[i] == 24) //if hour is set to 24, activate every hour
|
||||
&& timerMinutes[i] == minute(localTime)
|
||||
&& (timerWeekday[i] & 0x01) //timer is enabled
|
||||
&& ((timerWeekday[i] >> weekdayMondayFirst()) & 0x01)) //timer should activate at current day of week
|
||||
&& ((timerWeekday[i] >> weekdayMondayFirst()) & 0x01) //timer should activate at current day of week
|
||||
&& isTodayInDateRange(((timerMonth[i] >> 4) & 0x0F), timerDay[i], timerMonth[i] & 0x0F, timerDayEnd[i])
|
||||
)
|
||||
{
|
||||
unloadPlaylist();
|
||||
applyPreset(timerMacro[i]);
|
||||
}
|
||||
}
|
||||
@@ -349,6 +378,7 @@ void checkTimers()
|
||||
&& (timerWeekday[8] & 0x01) //timer is enabled
|
||||
&& ((timerWeekday[8] >> weekdayMondayFirst()) & 0x01)) //timer should activate at current day of week
|
||||
{
|
||||
unloadPlaylist();
|
||||
applyPreset(timerMacro[8]);
|
||||
DEBUG_PRINTF("Sunrise macro %d triggered.",timerMacro[8]);
|
||||
}
|
||||
@@ -363,6 +393,7 @@ void checkTimers()
|
||||
&& (timerWeekday[9] & 0x01) //timer is enabled
|
||||
&& ((timerWeekday[9] >> weekdayMondayFirst()) & 0x01)) //timer should activate at current day of week
|
||||
{
|
||||
unloadPlaylist();
|
||||
applyPreset(timerMacro[9]);
|
||||
DEBUG_PRINTF("Sunset macro %d triggered.",timerMacro[9]);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "pin_manager.h"
|
||||
#include "wled.h"
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
static void DebugPrintOwnerTag(PinOwner tag)
|
||||
{
|
||||
uint32_t q = static_cast<uint8_t>(tag);
|
||||
@@ -10,6 +11,7 @@ static void DebugPrintOwnerTag(PinOwner tag)
|
||||
DEBUG_PRINT(F("(no owner)"));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Actual allocation/deallocation routines
|
||||
bool PinManagerClass::deallocatePin(byte gpio, PinOwner tag)
|
||||
@@ -19,12 +21,14 @@ bool PinManagerClass::deallocatePin(byte gpio, PinOwner tag)
|
||||
|
||||
// if a non-zero ownerTag, only allow de-allocation if the owner's tag is provided
|
||||
if ((ownerTag[gpio] != PinOwner::None) && (ownerTag[gpio] != tag)) {
|
||||
#ifdef WLED_DEBUG
|
||||
DEBUG_PRINT(F("PIN DEALLOC: IO "));
|
||||
DEBUG_PRINT(gpio);
|
||||
DEBUG_PRINT(F(" allocated by "));
|
||||
DebugPrintOwnerTag(ownerTag[gpio]);
|
||||
DEBUG_PRINT(F(", but attempted de-allocation by "));
|
||||
DebugPrintOwnerTag(tag);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -34,6 +38,49 @@ bool PinManagerClass::deallocatePin(byte gpio, PinOwner tag)
|
||||
ownerTag[gpio] = PinOwner::None;
|
||||
return true;
|
||||
}
|
||||
|
||||
// support function for deallocating multiple pins
|
||||
bool PinManagerClass::deallocateMultiplePins(const uint8_t *pinArray, byte arrayElementCount, PinOwner tag)
|
||||
{
|
||||
bool shouldFail = false;
|
||||
DEBUG_PRINTLN(F("MULTIPIN DEALLOC"));
|
||||
// first verify the pins are OK and allocated by selected owner
|
||||
for (int i = 0; i < arrayElementCount; i++) {
|
||||
byte gpio = pinArray[i];
|
||||
if (gpio == 0xFF) {
|
||||
// explicit support for io -1 as a no-op (no allocation of pin),
|
||||
// as this can greatly simplify configuration arrays
|
||||
continue;
|
||||
}
|
||||
if (isPinAllocated(gpio, tag)) {
|
||||
// if the current pin is allocated by selected owner it is possible to release it
|
||||
continue;
|
||||
}
|
||||
#ifdef WLED_DEBUG
|
||||
DEBUG_PRINT(F("PIN DEALLOC: IO "));
|
||||
DEBUG_PRINT(gpio);
|
||||
DEBUG_PRINT(F(" allocated by "));
|
||||
DebugPrintOwnerTag(ownerTag[gpio]);
|
||||
DEBUG_PRINT(F(", but attempted de-allocation by "));
|
||||
DebugPrintOwnerTag(tag);
|
||||
#endif
|
||||
shouldFail = true;
|
||||
}
|
||||
if (shouldFail) {
|
||||
return false; // no pins deallocated
|
||||
}
|
||||
if (tag==PinOwner::HW_I2C) {
|
||||
if (i2cAllocCount && --i2cAllocCount>0) {
|
||||
// no deallocation done until last owner releases pins
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < arrayElementCount; i++) {
|
||||
deallocatePin(pinArray[i], tag);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, byte arrayElementCount, PinOwner tag)
|
||||
{
|
||||
bool shouldFail = false;
|
||||
@@ -46,17 +93,24 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by
|
||||
continue;
|
||||
}
|
||||
if (!isPinOk(gpio, mptArray[i].isOutput)) {
|
||||
#ifdef WLED_DEBUG
|
||||
DEBUG_PRINT(F("PIN ALLOC: Invalid pin attempted to be allocated: "));
|
||||
DEBUG_PRINT(gpio);
|
||||
DEBUG_PRINTLN(F(""));
|
||||
#endif
|
||||
shouldFail = true;
|
||||
}
|
||||
if (isPinAllocated(gpio)) {
|
||||
if (tag==PinOwner::HW_I2C && isPinAllocated(gpio, tag)) {
|
||||
// allow multiple "allocations" of HW I2C bus pins
|
||||
continue;
|
||||
} else if (isPinAllocated(gpio)) {
|
||||
#ifdef WLED_DEBUG
|
||||
DEBUG_PRINT(F("PIN ALLOC: FAIL: IO "));
|
||||
DEBUG_PRINT(gpio);
|
||||
DEBUG_PRINT(F(" already allocated by "));
|
||||
DebugPrintOwnerTag(ownerTag[gpio]);
|
||||
DEBUG_PRINTLN(F(""));
|
||||
#endif
|
||||
shouldFail = true;
|
||||
}
|
||||
}
|
||||
@@ -64,6 +118,8 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tag==PinOwner::HW_I2C) i2cAllocCount++;
|
||||
|
||||
// all pins are available .. track each one
|
||||
for (int i = 0; i < arrayElementCount; i++) {
|
||||
byte gpio = mptArray[i].pin;
|
||||
@@ -76,18 +132,29 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by
|
||||
byte bi = gpio - 8*by;
|
||||
bitWrite(pinAlloc[by], bi, true);
|
||||
ownerTag[gpio] = tag;
|
||||
#ifdef WLED_DEBUG
|
||||
DEBUG_PRINT(F("PIN ALLOC: Pin "));
|
||||
DEBUG_PRINT(gpio);
|
||||
DEBUG_PRINT(F(" allocated by "));
|
||||
DebugPrintOwnerTag(tag);
|
||||
DEBUG_PRINTLN(F(""));
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PinManagerClass::allocatePin(byte gpio, bool output, PinOwner tag)
|
||||
{
|
||||
if (!isPinOk(gpio, output)) return false;
|
||||
// HW I2C pins have to be allocated using allocateMultiplePins variant since there is always SCL/SDA pair
|
||||
if (!isPinOk(gpio, output) || tag==PinOwner::HW_I2C) return false;
|
||||
if (isPinAllocated(gpio)) {
|
||||
#ifdef WLED_DEBUG
|
||||
DEBUG_PRINT(F("PIN ALLOC: Pin "));
|
||||
DEBUG_PRINT(gpio);
|
||||
DEBUG_PRINT(F(" already allocated by "));
|
||||
DebugPrintOwnerTag(ownerTag[gpio]);
|
||||
DEBUG_PRINTLN(F(""));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -95,7 +162,14 @@ bool PinManagerClass::allocatePin(byte gpio, bool output, PinOwner tag)
|
||||
byte bi = gpio - 8*by;
|
||||
bitWrite(pinAlloc[by], bi, true);
|
||||
ownerTag[gpio] = tag;
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
DEBUG_PRINT(F("PIN ALLOC: Pin "));
|
||||
DEBUG_PRINT(gpio);
|
||||
DEBUG_PRINT(F(" allocated by "));
|
||||
DebugPrintOwnerTag(tag);
|
||||
DEBUG_PRINTLN(F(""));
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -106,7 +180,7 @@ bool PinManagerClass::isPinAllocated(byte gpio, PinOwner tag)
|
||||
if (!isPinOk(gpio, false)) return true;
|
||||
if ((tag != PinOwner::None) && (ownerTag[gpio] != tag)) return false;
|
||||
byte by = gpio >> 3;
|
||||
byte bi = gpio - 8*by;
|
||||
byte bi = gpio - (by<<3);
|
||||
return bitRead(pinAlloc[by], bi);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
#include "const.h" // for USERMOD_* values
|
||||
|
||||
typedef struct PinManagerPinType {
|
||||
int8_t pin;
|
||||
uint8_t isOutput;
|
||||
int8_t pin;
|
||||
bool isOutput;
|
||||
} managed_pin_type;
|
||||
|
||||
/*
|
||||
@@ -21,9 +21,8 @@ typedef struct PinManagerPinType {
|
||||
* 40 bytes on ESP32
|
||||
*/
|
||||
enum struct PinOwner : uint8_t {
|
||||
None = 0, // default == legacy == unspecified owner
|
||||
None = 0, // default == legacy == unspecified owner
|
||||
// High bit is set for all built-in pin owners
|
||||
// StatusLED -- THIS SHOULD NEVER BE ALLOCATED -- see handleStatusLED()
|
||||
Ethernet = 0x81,
|
||||
BusDigital = 0x82,
|
||||
BusDigital2 = 0x83,
|
||||
@@ -34,26 +33,27 @@ enum struct PinOwner : uint8_t {
|
||||
SPI_RAM = 0x88, // 'SpiR' == SPI RAM
|
||||
DebugOut = 0x89, // 'Dbg' == debug output always IO1
|
||||
DMX = 0x8A, // 'DMX' == hard-coded to IO2
|
||||
HW_I2C = 0x8B, // 'I2C' == hardware I2C pins (4&5 on ESP8266, 21&22 on ESP32)
|
||||
// Use UserMod IDs from const.h here
|
||||
UM_Unspecified = USERMOD_ID_UNSPECIFIED, // 0x01
|
||||
UM_Example = USERMOD_ID_EXAMPLE, // 0x02 // Usermod "usermod_v2_example.h"
|
||||
UM_Temperature = USERMOD_ID_TEMPERATURE, // 0x03 // Usermod "usermod_temperature.h"
|
||||
// #define USERMOD_ID_FIXNETSERVICES // 0x04 // Usermod "usermod_Fix_unreachable_netservices.h" -- Does not allocate pins
|
||||
UM_PIR = USERMOD_ID_PIRSWITCH, // 0x05 // Usermod "usermod_PIR_sensor_switch.h"
|
||||
// #define USERMOD_ID_IMU // 0x06 // Usermod "usermod_mpu6050_imu.h" -- Uses "standard" I2C pins ... TODO -- enable shared I2C bus use
|
||||
UM_FourLineDisplay = USERMOD_ID_FOUR_LINE_DISP, // 0x07 // Usermod "usermod_v2_four_line_display.h
|
||||
// #define USERMOD_ID_IMU // 0x06 // Usermod "usermod_mpu6050_imu.h" -- Uses "standard" HW_I2C pins
|
||||
UM_FourLineDisplay = USERMOD_ID_FOUR_LINE_DISP, // 0x07 // Usermod "usermod_v2_four_line_display.h -- May use "standard" HW_I2C pins
|
||||
UM_RotaryEncoderUI = USERMOD_ID_ROTARY_ENC_UI, // 0x08 // Usermod "usermod_v2_rotary_encoder_ui.h"
|
||||
// #define USERMOD_ID_AUTO_SAVE // 0x09 // Usermod "usermod_v2_auto_save.h" -- Does not allocate pins
|
||||
// #define USERMOD_ID_DHT // 0x0A // Usermod "usermod_dht.h" -- Statically allocates pins, not compatible with pinManager?
|
||||
// #define USERMOD_ID_MODE_SORT // 0x0B // Usermod "usermod_v2_mode_sort.h" -- Does not allocate pins
|
||||
// #define USERMOD_ID_VL53L0X // 0x0C // Usermod "usermod_vl53l0x_gestures.h" -- Uses "standard" I2C pins ... TODO -- enable shared I2C bus use
|
||||
// #define USERMOD_ID_VL53L0X // 0x0C // Usermod "usermod_vl53l0x_gestures.h" -- Uses "standard" HW_I2C pins
|
||||
UM_MultiRelay = USERMOD_ID_MULTI_RELAY, // 0x0D // Usermod "usermod_multi_relay.h"
|
||||
UM_AnimatedStaircase = USERMOD_ID_ANIMATED_STAIRCASE, // 0x0E // Usermod "Animated_Staircase.h"
|
||||
// #define USERMOD_ID_RTC // 0x0F // Usermod "usermod_rtc.h" -- Uses "standard" I2C pins ... TODO -- enable shared I2C bus use
|
||||
// #define USERMOD_ID_RTC // 0x0F // Usermod "usermod_rtc.h" -- Uses "standard" HW_I2C pins
|
||||
// #define USERMOD_ID_ELEKSTUBE_IPS // 0x10 // Usermod "usermod_elekstube_ips.h" -- Uses quite a few pins ... see Hardware.h and User_Setup.h
|
||||
// #define USERMOD_ID_SN_PHOTORESISTOR // 0x11 // Usermod "usermod_sn_photoresistor.h" -- Uses hard-coded pin (PHOTORESISTOR_PIN == A0), but could be easily updated to use pinManager
|
||||
UM_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_RGBRotaryEncoder = USERMOD_RGB_ROTARY_ENCODER, // 0x16 // Usermod "rgb-rotary-encoder.h"
|
||||
UM_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA // 0x17 // Usermod "quinled-an-penta.h"
|
||||
};
|
||||
static_assert(0u == static_cast<uint8_t>(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected");
|
||||
|
||||
@@ -67,10 +67,13 @@ class PinManagerClass {
|
||||
uint8_t ledcAlloc[2] = {0x00, 0x00}; //16 LEDC channels
|
||||
PinOwner ownerTag[40] = { PinOwner::None };
|
||||
#endif
|
||||
uint8_t i2cAllocCount = 0; // allow multiple allocation of I2C bus pins but keep track of allocations
|
||||
|
||||
public:
|
||||
// De-allocates a single pin
|
||||
bool deallocatePin(byte gpio, PinOwner tag);
|
||||
// De-allocates multiple pins but only if all can be deallocated (PinOwner has to be specified)
|
||||
bool deallocateMultiplePins(const uint8_t *pinArray, byte arrayElementCount, PinOwner tag);
|
||||
// Allocates a single pin, with an owner tag.
|
||||
// De-allocation requires the same owner tag (or override)
|
||||
bool allocatePin(byte gpio, bool output, PinOwner tag);
|
||||
@@ -85,11 +88,13 @@ class PinManagerClass {
|
||||
#endif
|
||||
inline bool allocatePin(byte gpio, bool output = true) { return allocatePin(gpio, output, PinOwner::None); }
|
||||
#if !defined(ESP8266) // ESP8266 compiler doesn't understand deprecated attribute
|
||||
[[deprecated("Replaced by three-parameter deallocatePin(gpio, output, ownerTag), for improved debugging")]]
|
||||
[[deprecated("Replaced by two-parameter deallocatePin(gpio, ownerTag), for improved debugging")]]
|
||||
#endif
|
||||
inline void deallocatePin(byte gpio) { deallocatePin(gpio, PinOwner::None); }
|
||||
|
||||
// will return true for reserved pins
|
||||
bool isPinAllocated(byte gpio, PinOwner tag = PinOwner::None);
|
||||
// will return false for reserved pins
|
||||
bool isPinOk(byte gpio, bool output = true);
|
||||
|
||||
PinOwner getPinOwner(byte gpio);
|
||||
|
||||
@@ -107,7 +107,7 @@ int16_t loadPlaylist(JsonObject playlistObj, byte presetId) {
|
||||
|
||||
playlistRepeat = rep;
|
||||
if (playlistRepeat > 0) playlistRepeat++; //add one extra repetition immediately since it will be deducted on first start
|
||||
playlistEndPreset = playlistObj[F("end")] | 0;
|
||||
playlistEndPreset = playlistObj["end"] | 0;
|
||||
shuffle = shuffle || playlistObj["r"];
|
||||
if (shuffle) playlistOptions += PL_OPTION_SHUFFLE;
|
||||
|
||||
@@ -119,7 +119,8 @@ int16_t loadPlaylist(JsonObject playlistObj, byte presetId) {
|
||||
|
||||
void handlePlaylist() {
|
||||
static unsigned long presetCycledTime = 0;
|
||||
if (currentPlaylist < 0 || playlistEntries == nullptr) return;
|
||||
// if fileDoc is not null JSON buffer is in use so just quit
|
||||
if (currentPlaylist < 0 || playlistEntries == nullptr || fileDoc != nullptr) return;
|
||||
|
||||
if (millis() - presetCycledTime > (100*playlistEntryDur)) {
|
||||
presetCycledTime = millis();
|
||||
|
||||
@@ -10,7 +10,15 @@ bool applyPreset(byte index, byte callMode)
|
||||
|
||||
const char *filename = index < 255 ? "/presets.json" : "/tmp.json";
|
||||
|
||||
if (fileDoc) {
|
||||
uint8_t core = 1;
|
||||
//crude way to determine if this was called by a network request
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
core = xPortGetCoreID();
|
||||
#endif
|
||||
|
||||
//only allow use of fileDoc from the core responsible for network requests
|
||||
//do not use active network request doc from preset called by main loop (playlist, schedule, ...)
|
||||
if (fileDoc && core) {
|
||||
errorFlag = readObjectFromFileUsingId(filename, index, fileDoc) ? ERR_NONE : ERR_FS_PLOAD;
|
||||
JsonObject fdo = fileDoc->as<JsonObject>();
|
||||
if (fdo["ps"] == index) fdo.remove("ps"); //remove load request for same presets to prevent recursive crash
|
||||
@@ -45,6 +53,7 @@ bool applyPreset(byte index, byte callMode)
|
||||
void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj)
|
||||
{
|
||||
if (index == 0 || (index > 250 && persist) || (index<255 && !persist)) return;
|
||||
char tmp[12];
|
||||
JsonObject sObj = saveobj;
|
||||
|
||||
const char *filename = persist ? "/presets.json" : "/tmp.json";
|
||||
@@ -57,7 +66,11 @@ void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj)
|
||||
if (!requestJSONBufferLock(10)) return;
|
||||
#endif
|
||||
sObj = doc.to<JsonObject>();
|
||||
if (pname) sObj["n"] = pname;
|
||||
|
||||
if (sObj["n"].isNull() && pname == nullptr) {
|
||||
sprintf_P(tmp, PSTR("Preset %d"), index);
|
||||
sObj["n"] = tmp;
|
||||
} else if (pname) sObj["n"] = pname;
|
||||
|
||||
DEBUGFS_PRINTLN(F("Save current state"));
|
||||
serializeState(sObj, true);
|
||||
|
||||
282
wled00/set.cpp
282
wled00/set.cpp
@@ -3,19 +3,6 @@
|
||||
/*
|
||||
* Receives client input
|
||||
*/
|
||||
|
||||
void _setRandomColor(bool _sec,bool fromButton)
|
||||
{
|
||||
lastRandomIndex = strip.get_random_wheel_index(lastRandomIndex);
|
||||
if (_sec){
|
||||
colorHStoRGB(lastRandomIndex*256,255,colSec);
|
||||
} else {
|
||||
colorHStoRGB(lastRandomIndex*256,255,col);
|
||||
}
|
||||
if (fromButton) colorUpdated(2);
|
||||
}
|
||||
|
||||
|
||||
bool isAsterisksOnly(const char* str, byte maxLen)
|
||||
{
|
||||
for (byte i = 0; i < maxLen; i++) {
|
||||
@@ -100,6 +87,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
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());
|
||||
|
||||
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
|
||||
@@ -135,6 +123,20 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
doInitBusses = true;
|
||||
}
|
||||
|
||||
ColorOrderMap com = {};
|
||||
for (uint8_t s = 0; s < WLED_MAX_COLOR_ORDER_MAPPINGS; s++) {
|
||||
char xs[4] = "XS"; xs[2] = 48+s; xs[3] = 0; //start LED
|
||||
char xc[4] = "XC"; xc[2] = 48+s; xc[3] = 0; //strip length
|
||||
char xo[4] = "XO"; xo[2] = 48+s; xo[3] = 0; //color order
|
||||
if (request->hasArg(xs)) {
|
||||
start = request->arg(xs).toInt();
|
||||
length = request->arg(xc).toInt();
|
||||
colorOrder = request->arg(xo).toInt();
|
||||
com.add(start, length, colorOrder);
|
||||
}
|
||||
}
|
||||
busses.updateColorOrderMap(com);
|
||||
|
||||
// upate other pins
|
||||
int hw_ir_pin = request->arg(F("IR")).toInt();
|
||||
if (pinManager.allocatePin(hw_ir_pin,false, PinOwner::IR)) {
|
||||
@@ -153,8 +155,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
rlyMde = (bool)request->hasArg(F("RM"));
|
||||
|
||||
for (uint8_t i=0; i<WLED_MAX_BUTTONS; i++) {
|
||||
char bt[4] = "BT"; bt[2] = 48+i; bt[3] = 0; // button pin
|
||||
char be[4] = "BE"; be[2] = 48+i; be[3] = 0; // button type
|
||||
char bt[4] = "BT"; bt[2] = (i<10?48:55)+i; bt[3] = 0; // button pin (use A,B,C,... if WLED_MAX_BUTTONS>10)
|
||||
char be[4] = "BE"; be[2] = (i<10?48:55)+i; be[3] = 0; // button type (use A,B,C,... if WLED_MAX_BUTTONS>10)
|
||||
int hw_btn_pin = request->arg(bt).toInt();
|
||||
if (pinManager.allocatePin(hw_btn_pin,false,PinOwner::Button)) {
|
||||
btnPin[i] = hw_btn_pin;
|
||||
@@ -217,7 +219,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
receiveNotificationBrightness = request->hasArg(F("RB"));
|
||||
receiveNotificationColor = request->hasArg(F("RC"));
|
||||
receiveNotificationEffects = request->hasArg(F("RX"));
|
||||
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
|
||||
receiveSegmentOptions = request->hasArg(F("SO"));
|
||||
receiveSegmentBounds = request->hasArg(F("SG"));
|
||||
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects || receiveSegmentOptions);
|
||||
notifyDirectDefault = request->hasArg(F("SD"));
|
||||
notifyDirect = notifyDirectDefault;
|
||||
notifyButton = request->hasArg(F("SB"));
|
||||
@@ -293,6 +297,10 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
hueStoreAllowed = true;
|
||||
reconnectHue();
|
||||
#endif
|
||||
|
||||
t = request->arg(F("BD")).toInt();
|
||||
if (t >= 96 && t <= 15000) serialBaud = t;
|
||||
updateBaudRate(serialBaud *100);
|
||||
}
|
||||
|
||||
//TIME
|
||||
@@ -306,6 +314,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
|
||||
//start ntp if not already connected
|
||||
if (ntpEnabled && WLED_CONNECTED && !ntpConnected) ntpConnected = ntpUdp.begin(ntpLocalPort);
|
||||
ntpLastSyncTime = 0; // force new NTP query
|
||||
|
||||
longitude = request->arg(F("LN")).toFloat();
|
||||
latitude = request->arg(F("LT")).toFloat();
|
||||
@@ -341,9 +350,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
macroCountdown = request->arg(F("MC")).toInt();
|
||||
macroNl = request->arg(F("MN")).toInt();
|
||||
for (uint8_t i=0; i<WLED_MAX_BUTTONS; i++) {
|
||||
char mp[4] = "MP"; mp[2] = 48+i; mp[3] = 0; // short
|
||||
char ml[4] = "ML"; ml[2] = 48+i; ml[3] = 0; // long
|
||||
char md[4] = "MD"; md[2] = 48+i; md[3] = 0; // double
|
||||
char mp[4] = "MP"; mp[2] = (i<10?48:55)+i; mp[3] = 0; // short
|
||||
char ml[4] = "ML"; ml[2] = (i<10?48:55)+i; ml[3] = 0; // long
|
||||
char md[4] = "MD"; md[2] = (i<10?48:55)+i; md[3] = 0; // double
|
||||
//if (!request->hasArg(mp)) break;
|
||||
macroButton[i] = request->arg(mp).toInt(); // these will default to 0 if not present
|
||||
macroLongPress[i] = request->arg(ml).toInt();
|
||||
@@ -351,21 +360,27 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
}
|
||||
|
||||
char k[3]; k[2] = 0;
|
||||
for (int i = 0; i<10; i++)
|
||||
{
|
||||
k[1] = i+48;//ascii 0,1,2,3
|
||||
|
||||
for (int i = 0; i<10; i++) {
|
||||
k[1] = i+48;//ascii 0,1,2,3,...
|
||||
k[0] = 'H'; //timer hours
|
||||
timerHours[i] = request->arg(k).toInt();
|
||||
|
||||
k[0] = 'N'; //minutes
|
||||
timerMinutes[i] = request->arg(k).toInt();
|
||||
|
||||
k[0] = 'T'; //macros
|
||||
timerMacro[i] = request->arg(k).toInt();
|
||||
|
||||
k[0] = 'W'; //weekdays
|
||||
timerWeekday[i] = request->arg(k).toInt();
|
||||
if (i<8) {
|
||||
k[0] = 'M'; //start month
|
||||
timerMonth[i] = request->arg(k).toInt() & 0x0F;
|
||||
timerMonth[i] <<= 4;
|
||||
k[0] = 'P'; //end month
|
||||
timerMonth[i] += (request->arg(k).toInt() & 0x0F);
|
||||
k[0] = 'D'; //start day
|
||||
timerDay[i] = request->arg(k).toInt();
|
||||
k[0] = 'E'; //end day
|
||||
timerDayEnd[i] = request->arg(k).toInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,7 +457,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
JsonObject um = doc.createNestedObject("um");
|
||||
|
||||
size_t args = request->args();
|
||||
uint j=0;
|
||||
uint16_t j=0;
|
||||
for (size_t i=0; i<args; i++) {
|
||||
String name = request->argName(i);
|
||||
String value = request->arg(i);
|
||||
@@ -476,12 +491,15 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
// check if parameters represent array
|
||||
if (name.endsWith("[]")) {
|
||||
name.replace("[]","");
|
||||
value.replace(",","."); // just in case conversion
|
||||
if (!subObj[name].is<JsonArray>()) {
|
||||
JsonArray ar = subObj.createNestedArray(name);
|
||||
ar.add(value.toInt());
|
||||
if (value.indexOf(".") >= 0) ar.add(value.toFloat()); // we do have a float
|
||||
else ar.add(value.toInt()); // we may have an int
|
||||
j=0;
|
||||
} else {
|
||||
subObj[name].add(value.toInt());
|
||||
if (value.indexOf(".") >= 0) subObj[name].add(value.toFloat()); // we do have a float
|
||||
else subObj[name].add(value.toInt()); // we may have an int
|
||||
j++;
|
||||
}
|
||||
DEBUG_PRINT("[");
|
||||
@@ -511,9 +529,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
}
|
||||
}
|
||||
usermods.readFromConfig(um); // force change of usermod parameters
|
||||
}
|
||||
|
||||
releaseJSONBufferLock();
|
||||
releaseJSONBufferLock();
|
||||
}
|
||||
|
||||
if (subPage != 2 && (subPage != 6 || !doReboot)) serializeConfig(); //do not save if factory reset or LED settings (which are saved after LED re-init)
|
||||
if (subPage == 4) alexaInit();
|
||||
@@ -585,36 +603,43 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
DEBUG_PRINT(F("API req: "));
|
||||
DEBUG_PRINTLN(req);
|
||||
|
||||
strip.applyToAllSelected = false;
|
||||
|
||||
//segment select (sets main segment)
|
||||
byte prevMain = strip.getMainSegmentId();
|
||||
pos = req.indexOf(F("SM="));
|
||||
if (pos > 0) {
|
||||
strip.mainSegment = getNumVal(&req, pos);
|
||||
strip.setMainSegmentId(getNumVal(&req, pos));
|
||||
}
|
||||
byte selectedSeg = strip.getMainSegmentId();
|
||||
if (selectedSeg != prevMain) setValuesFromMainSeg();
|
||||
|
||||
byte selectedSeg = strip.getFirstSelectedSegId();
|
||||
|
||||
bool singleSegment = false;
|
||||
|
||||
pos = req.indexOf(F("SS="));
|
||||
if (pos > 0) {
|
||||
byte t = getNumVal(&req, pos);
|
||||
if (t < strip.getMaxSegments()) selectedSeg = t;
|
||||
if (t < strip.getMaxSegments()) {
|
||||
selectedSeg = t;
|
||||
singleSegment = true;
|
||||
}
|
||||
}
|
||||
|
||||
WS2812FX::Segment& selseg = strip.getSegment(selectedSeg);
|
||||
pos = req.indexOf(F("SV=")); //segment selected
|
||||
if (pos > 0) {
|
||||
byte t = getNumVal(&req, pos);
|
||||
if (t == 2) {
|
||||
for (uint8_t i = 0; i < strip.getMaxSegments(); i++)
|
||||
{
|
||||
strip.getSegment(i).setOption(SEG_OPTION_SELECTED, 0);
|
||||
}
|
||||
}
|
||||
if (t == 2) for (uint8_t i = 0; i < strip.getMaxSegments(); i++) strip.getSegment(i).setOption(SEG_OPTION_SELECTED, 0); // unselect other segments
|
||||
selseg.setOption(SEG_OPTION_SELECTED, t);
|
||||
}
|
||||
|
||||
// temporary values, write directly to segments, globals are updated by setValuesFromFirstSelectedSeg()
|
||||
uint32_t col0 = selseg.colors[0];
|
||||
uint32_t col1 = selseg.colors[1];
|
||||
byte colIn[4] = {R(col0), G(col0), B(col0), W(col0)};
|
||||
byte colInSec[4] = {R(col1), G(col1), B(col1), W(col1)};
|
||||
byte effectIn = selseg.mode;
|
||||
byte speedIn = selseg.speed;
|
||||
byte intensityIn = selseg.intensity;
|
||||
byte paletteIn = selseg.palette;
|
||||
|
||||
uint16_t startI = selseg.start;
|
||||
uint16_t stopI = selseg.stop;
|
||||
uint8_t grpI = selseg.grouping;
|
||||
@@ -677,43 +702,39 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
applyPreset(presetCycCurr);
|
||||
}
|
||||
|
||||
//snapshot to check if request changed values later, temporary.
|
||||
byte prevCol[4] = {col[0], col[1], col[2], col[3]};
|
||||
byte prevColSec[4] = {colSec[0], colSec[1], colSec[2], colSec[3]};
|
||||
byte prevEffect = effectCurrent;
|
||||
byte prevSpeed = effectSpeed;
|
||||
byte prevIntensity = effectIntensity;
|
||||
byte prevPalette = effectPalette;
|
||||
|
||||
//set brightness
|
||||
updateVal(&req, "&A=", &bri);
|
||||
|
||||
bool col0Changed = false, col1Changed = false;
|
||||
//set colors
|
||||
updateVal(&req, "&R=", &col[0]);
|
||||
updateVal(&req, "&G=", &col[1]);
|
||||
updateVal(&req, "&B=", &col[2]);
|
||||
updateVal(&req, "&W=", &col[3]);
|
||||
updateVal(&req, "R2=", &colSec[0]);
|
||||
updateVal(&req, "G2=", &colSec[1]);
|
||||
updateVal(&req, "B2=", &colSec[2]);
|
||||
updateVal(&req, "W2=", &colSec[3]);
|
||||
col0Changed |= updateVal(&req, "&R=", &colIn[0]);
|
||||
col0Changed |= updateVal(&req, "&G=", &colIn[1]);
|
||||
col0Changed |= updateVal(&req, "&B=", &colIn[2]);
|
||||
col0Changed |= updateVal(&req, "&W=", &colIn[3]);
|
||||
|
||||
col1Changed |= updateVal(&req, "R2=", &colInSec[0]);
|
||||
col1Changed |= updateVal(&req, "G2=", &colInSec[1]);
|
||||
col1Changed |= updateVal(&req, "B2=", &colInSec[2]);
|
||||
col1Changed |= updateVal(&req, "W2=", &colInSec[3]);
|
||||
|
||||
#ifdef WLED_ENABLE_LOXONE
|
||||
//lox parser
|
||||
pos = req.indexOf(F("LX=")); // Lox primary color
|
||||
if (pos > 0) {
|
||||
int lxValue = getNumVal(&req, pos);
|
||||
if (parseLx(lxValue, col)) {
|
||||
if (parseLx(lxValue, colIn)) {
|
||||
bri = 255;
|
||||
nightlightActive = false; //always disable nightlight when toggling
|
||||
col0Changed = true;
|
||||
}
|
||||
}
|
||||
pos = req.indexOf(F("LY=")); // Lox secondary color
|
||||
if (pos > 0) {
|
||||
int lxValue = getNumVal(&req, pos);
|
||||
if(parseLx(lxValue, colSec)) {
|
||||
if(parseLx(lxValue, colInSec)) {
|
||||
bri = 255;
|
||||
nightlightActive = false; //always disable nightlight when toggling
|
||||
col1Changed = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -727,59 +748,95 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
if (pos > 0) {
|
||||
tempsat = getNumVal(&req, pos);
|
||||
}
|
||||
colorHStoRGB(temphue,tempsat,(req.indexOf(F("H2"))>0)? colSec:col);
|
||||
byte sec = req.indexOf(F("H2"));
|
||||
colorHStoRGB(temphue, tempsat, (sec>0) ? colInSec : colIn);
|
||||
col0Changed |= (!sec); col1Changed |= sec;
|
||||
}
|
||||
|
||||
//set white spectrum (kelvin)
|
||||
pos = req.indexOf(F("&K="));
|
||||
if (pos > 0) {
|
||||
colorKtoRGB(getNumVal(&req, pos),(req.indexOf(F("K2"))>0)? colSec:col);
|
||||
byte sec = req.indexOf(F("K2"));
|
||||
colorKtoRGB(getNumVal(&req, pos), (sec>0) ? colInSec : colIn);
|
||||
col0Changed |= (!sec); col1Changed |= sec;
|
||||
}
|
||||
|
||||
//set color from HEX or 32bit DEC
|
||||
byte tmpCol[4];
|
||||
pos = req.indexOf(F("CL="));
|
||||
if (pos > 0) {
|
||||
colorFromDecOrHexString(col, (char*)req.substring(pos + 3).c_str());
|
||||
colorFromDecOrHexString(colIn, (char*)req.substring(pos + 3).c_str());
|
||||
col0Changed = true;
|
||||
}
|
||||
pos = req.indexOf(F("C2="));
|
||||
if (pos > 0) {
|
||||
colorFromDecOrHexString(colSec, (char*)req.substring(pos + 3).c_str());
|
||||
colorFromDecOrHexString(colInSec, (char*)req.substring(pos + 3).c_str());
|
||||
col1Changed = true;
|
||||
}
|
||||
pos = req.indexOf(F("C3="));
|
||||
if (pos > 0) {
|
||||
byte t[4];
|
||||
colorFromDecOrHexString(t, (char*)req.substring(pos + 3).c_str());
|
||||
if (selectedSeg != strip.getMainSegmentId()) {
|
||||
strip.applyToAllSelected = true;
|
||||
strip.setColor(2, t[0], t[1], t[2], t[3]);
|
||||
} else {
|
||||
selseg.setColor(2,((t[0] << 16) + (t[1] << 8) + t[2] + (t[3] << 24)), selectedSeg); // defined above (SS=)
|
||||
}
|
||||
colorFromDecOrHexString(tmpCol, (char*)req.substring(pos + 3).c_str());
|
||||
uint32_t col2 = RGBW32(tmpCol[0], tmpCol[1], tmpCol[2], tmpCol[3]);
|
||||
selseg.setColor(2, col2, selectedSeg); // defined above (SS= or main)
|
||||
stateChanged = true;
|
||||
if (!singleSegment) strip.setColor(2, col2);
|
||||
}
|
||||
|
||||
//set to random hue SR=0->1st SR=1->2nd
|
||||
pos = req.indexOf(F("SR"));
|
||||
if (pos > 0) {
|
||||
_setRandomColor(getNumVal(&req, pos));
|
||||
byte sec = getNumVal(&req, pos);
|
||||
setRandomColor(sec? colInSec : colIn);
|
||||
col0Changed |= (!sec); col1Changed |= sec;
|
||||
}
|
||||
|
||||
//swap 2nd & 1st
|
||||
pos = req.indexOf(F("SC"));
|
||||
if (pos > 0) {
|
||||
byte temp;
|
||||
for (uint8_t i=0; i<4; i++)
|
||||
{
|
||||
temp = col[i];
|
||||
col[i] = colSec[i];
|
||||
colSec[i] = temp;
|
||||
for (uint8_t i=0; i<4; i++) {
|
||||
temp = colIn[i];
|
||||
colIn[i] = colInSec[i];
|
||||
colInSec[i] = temp;
|
||||
}
|
||||
col0Changed = col1Changed = true;
|
||||
}
|
||||
|
||||
//set effect parameters
|
||||
if (updateVal(&req, "FX=", &effectCurrent, 0, strip.getModeCount()-1) && request != nullptr) unloadPlaylist(); //unload playlist if changing FX using web request
|
||||
updateVal(&req, "SX=", &effectSpeed);
|
||||
updateVal(&req, "IX=", &effectIntensity);
|
||||
updateVal(&req, "FP=", &effectPalette, 0, strip.getPaletteCount()-1);
|
||||
// apply colors to selected segment, and all selected segments if applicable
|
||||
if (col0Changed) {
|
||||
stateChanged = true;
|
||||
uint32_t colIn0 = RGBW32(colIn[0], colIn[1], colIn[2], colIn[3]);
|
||||
selseg.setColor(0, colIn0, selectedSeg);
|
||||
if (!singleSegment) strip.setColor(0, colIn0);
|
||||
}
|
||||
|
||||
if (col1Changed) {
|
||||
stateChanged = true;
|
||||
uint32_t colIn1 = RGBW32(colInSec[0], colInSec[1], colInSec[2], colInSec[3]);
|
||||
selseg.setColor(1, colIn1, selectedSeg);
|
||||
if (!singleSegment) strip.setColor(1, colIn1);
|
||||
}
|
||||
|
||||
bool fxModeChanged = false, speedChanged = false, intensityChanged = false, paletteChanged = false;
|
||||
// set effect parameters
|
||||
if (updateVal(&req, "FX=", &effectIn, 0, strip.getModeCount()-1)) {
|
||||
if (request != nullptr) unloadPlaylist(); // unload playlist if changing FX using web request
|
||||
fxModeChanged = true;
|
||||
}
|
||||
speedChanged = updateVal(&req, "SX=", &speedIn);
|
||||
intensityChanged = updateVal(&req, "IX=", &intensityIn);
|
||||
paletteChanged = updateVal(&req, "FP=", &paletteIn, 0, strip.getPaletteCount()-1);
|
||||
|
||||
stateChanged |= (fxModeChanged || speedChanged || intensityChanged || paletteChanged);
|
||||
|
||||
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 (fxModeChanged) strip.setMode(i, effectIn);
|
||||
if (speedChanged) seg.speed = speedIn;
|
||||
if (intensityChanged) seg.intensity = intensityIn;
|
||||
if (paletteChanged) seg.palette = paletteIn;
|
||||
}
|
||||
|
||||
//set advanced overlay
|
||||
pos = req.indexOf(F("OL="));
|
||||
@@ -907,60 +964,17 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
if (pos > 0) {
|
||||
userVar1 = getNumVal(&req, pos);
|
||||
}
|
||||
//you can add more if you need
|
||||
// you can add more if you need
|
||||
|
||||
//apply to all selected manually to prevent #1618. Temporary
|
||||
bool col0Changed = false, col1Changed = false;
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
if (col[i] != prevCol[i]) col0Changed = true;
|
||||
if (colSec[i] != prevColSec[i]) col1Changed = true;
|
||||
}
|
||||
for (uint8_t i = 0; i < strip.getMaxSegments(); i++)
|
||||
{
|
||||
WS2812FX::Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isSelected()) continue;
|
||||
if (effectCurrent != prevEffect) {
|
||||
strip.setMode(i, effectCurrent);
|
||||
effectChanged = true;
|
||||
}
|
||||
if (effectSpeed != prevSpeed) {
|
||||
seg.speed = effectSpeed;
|
||||
effectChanged = true;
|
||||
}
|
||||
if (effectIntensity != prevIntensity) {
|
||||
seg.intensity = effectIntensity;
|
||||
effectChanged = true;
|
||||
}
|
||||
if (effectPalette != prevPalette) {
|
||||
seg.palette = effectPalette;
|
||||
effectChanged = true;
|
||||
}
|
||||
}
|
||||
// global col[], effectCurrent, ... are updated in stateChanged()
|
||||
if (!apply) return true; // when called by JSON API, do not call colorUpdated() here
|
||||
|
||||
if (col0Changed) {
|
||||
if (selectedSeg == strip.getMainSegmentId()) {
|
||||
strip.applyToAllSelected = true;
|
||||
strip.setColor(0, colorFromRgbw(col));
|
||||
}
|
||||
}
|
||||
if (col1Changed) {
|
||||
if (selectedSeg == strip.getMainSegmentId()) {
|
||||
strip.applyToAllSelected = true;
|
||||
strip.setColor(1, colorFromRgbw(colSec));
|
||||
}
|
||||
}
|
||||
//end of temporary fix code
|
||||
pos = req.indexOf(F("&NN")); //do not send UDP notifications this time
|
||||
stateUpdated((pos > 0) ? CALL_MODE_NO_NOTIFY : CALL_MODE_DIRECT_CHANGE);
|
||||
|
||||
if (!apply) return true; //when called by JSON API, do not call colorUpdated() here
|
||||
|
||||
//internal call, does not send XML response
|
||||
// internal call, does not send XML response
|
||||
pos = req.indexOf(F("IN"));
|
||||
if (pos < 1) XML_response(request);
|
||||
|
||||
strip.applyToAllSelected = false;
|
||||
|
||||
pos = req.indexOf(F("&NN")); //do not send UDP notifications this time
|
||||
colorUpdated((pos > 0) ? CALL_MODE_NO_NOTIFY : CALL_MODE_DIRECT_CHANGE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
204
wled00/udp.cpp
204
wled00/udp.cpp
@@ -4,7 +4,9 @@
|
||||
* UDP sync notifier / Realtime / Hyperion / TPM2.NET
|
||||
*/
|
||||
|
||||
#define WLEDPACKETSIZE 39
|
||||
#define UDP_SEG_SIZE 28
|
||||
#define SEG_OFFSET (41+(MAX_NUM_SEGMENTS*UDP_SEG_SIZE))
|
||||
#define WLEDPACKETSIZE (41+(MAX_NUM_SEGMENTS*UDP_SEG_SIZE)+0)
|
||||
#define UDP_IN_MAXSIZE 1472
|
||||
#define PRESUMED_NETWORK_DELAY 3 //how many ms could it take on avg to reach the receiver? This will be added to transmitted times
|
||||
|
||||
@@ -17,6 +19,7 @@ void notify(byte callMode, bool followUp)
|
||||
case CALL_MODE_INIT: return;
|
||||
case CALL_MODE_DIRECT_CHANGE: if (!notifyDirect) return; break;
|
||||
case CALL_MODE_BUTTON: if (!notifyButton) return; break;
|
||||
case CALL_MODE_BUTTON_PRESET: if (!notifyButton) return; break;
|
||||
case CALL_MODE_NIGHTLIGHT: if (!notifyDirect) return; break;
|
||||
case CALL_MODE_HUE: if (!notifyHue) return; break;
|
||||
case CALL_MODE_PRESET_CYCLE: if (!notifyDirect) return; break;
|
||||
@@ -25,37 +28,39 @@ void notify(byte callMode, bool followUp)
|
||||
default: return;
|
||||
}
|
||||
byte udpOut[WLEDPACKETSIZE];
|
||||
WS2812FX::Segment& mainseg = strip.getSegment(strip.getMainSegmentId());
|
||||
WS2812FX::Segment& mainseg = strip.getMainSegment();
|
||||
udpOut[0] = 0; //0: wled notifier protocol 1: WARLS protocol
|
||||
udpOut[1] = callMode;
|
||||
udpOut[2] = bri;
|
||||
udpOut[3] = col[0];
|
||||
udpOut[4] = col[1];
|
||||
udpOut[5] = col[2];
|
||||
uint32_t col = mainseg.colors[0];
|
||||
udpOut[3] = R(col);
|
||||
udpOut[4] = G(col);
|
||||
udpOut[5] = B(col);
|
||||
udpOut[6] = nightlightActive;
|
||||
udpOut[7] = nightlightDelayMins;
|
||||
udpOut[8] = effectCurrent;
|
||||
udpOut[9] = effectSpeed;
|
||||
udpOut[10] = col[3];
|
||||
udpOut[8] = mainseg.mode;
|
||||
udpOut[9] = mainseg.speed;
|
||||
udpOut[10] = W(col);
|
||||
//compatibilityVersionByte:
|
||||
//0: old 1: supports white 2: supports secondary color
|
||||
//3: supports FX intensity, 24 byte packet 4: supports transitionDelay 5: sup palette
|
||||
//6: supports timebase syncing, 29 byte packet 7: supports tertiary color 8: supports sys time sync, 36 byte packet
|
||||
//9: supports sync groups, 37 byte packet 10: supports CCT, 39 byte packet
|
||||
udpOut[11] = 10;
|
||||
udpOut[12] = colSec[0];
|
||||
udpOut[13] = colSec[1];
|
||||
udpOut[14] = colSec[2];
|
||||
udpOut[15] = colSec[3];
|
||||
udpOut[16] = effectIntensity;
|
||||
//9: supports sync groups, 37 byte packet 10: supports CCT, 39 byte packet 11: per segment options, variable packet length (40+MAX_NUM_SEGMENTS*3)
|
||||
udpOut[11] = 11;
|
||||
col = mainseg.colors[1];
|
||||
udpOut[12] = R(col);
|
||||
udpOut[13] = G(col);
|
||||
udpOut[14] = B(col);
|
||||
udpOut[15] = W(col);
|
||||
udpOut[16] = mainseg.intensity;
|
||||
udpOut[17] = (transitionDelay >> 0) & 0xFF;
|
||||
udpOut[18] = (transitionDelay >> 8) & 0xFF;
|
||||
udpOut[19] = effectPalette;
|
||||
uint32_t colTer = mainseg.colors[2];
|
||||
udpOut[20] = (colTer >> 16) & 0xFF;
|
||||
udpOut[21] = (colTer >> 8) & 0xFF;
|
||||
udpOut[22] = (colTer >> 0) & 0xFF;
|
||||
udpOut[23] = (colTer >> 24) & 0xFF;
|
||||
udpOut[19] = mainseg.palette;
|
||||
col = mainseg.colors[2];
|
||||
udpOut[20] = R(col);
|
||||
udpOut[21] = G(col);
|
||||
udpOut[22] = B(col);
|
||||
udpOut[23] = W(col);
|
||||
|
||||
udpOut[24] = followUp;
|
||||
uint32_t t = millis() + strip.timebase;
|
||||
@@ -78,12 +83,50 @@ void notify(byte callMode, bool followUp)
|
||||
|
||||
//sync groups
|
||||
udpOut[36] = syncGroups;
|
||||
|
||||
//Might be changed to Kelvin in the future, receiver code should handle that case
|
||||
//0: byte 38 contains 0-255 value, 255: no valid CCT, 1-254: Kelvin value MSB
|
||||
udpOut[37] = strip.hasCCTBus() ? 0 : 255; //check this is 0 for the next value to be significant
|
||||
udpOut[38] = mainseg.cct;
|
||||
|
||||
|
||||
//Might be changed to Kelvin in the future, receiver code should handle that case
|
||||
//0: byte 38 contains 0-255 value, 255: no valid CCT, 1-254: Kelvin value MSB
|
||||
udpOut[37] = strip.hasCCTBus() ? 0 : 255; //check this is 0 for the next value to be significant
|
||||
udpOut[38] = mainseg.cct;
|
||||
|
||||
udpOut[39] = strip.getMaxSegments();
|
||||
udpOut[40] = UDP_SEG_SIZE; //size of each loop iteration (one segment)
|
||||
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
|
||||
WS2812FX::Segment &selseg = strip.getSegment(i);
|
||||
uint16_t ofs = 41 + i*UDP_SEG_SIZE; //start of segment offset byte
|
||||
udpOut[0 +ofs] = i;
|
||||
udpOut[1 +ofs] = selseg.start >> 8;
|
||||
udpOut[2 +ofs] = selseg.start & 0xFF;
|
||||
udpOut[3 +ofs] = selseg.stop >> 8;
|
||||
udpOut[4 +ofs] = selseg.stop & 0xFF;
|
||||
udpOut[5 +ofs] = selseg.grouping;
|
||||
udpOut[6 +ofs] = selseg.spacing;
|
||||
udpOut[7 +ofs] = selseg.offset >> 8;
|
||||
udpOut[8 +ofs] = selseg.offset & 0xFF;
|
||||
udpOut[9 +ofs] = selseg.options & 0x0F; //only take into account mirrored, selected, on, reversed
|
||||
udpOut[10+ofs] = selseg.opacity;
|
||||
udpOut[11+ofs] = selseg.mode;
|
||||
udpOut[12+ofs] = selseg.speed;
|
||||
udpOut[13+ofs] = selseg.intensity;
|
||||
udpOut[14+ofs] = selseg.palette;
|
||||
udpOut[15+ofs] = R(selseg.colors[0]);
|
||||
udpOut[16+ofs] = G(selseg.colors[0]);
|
||||
udpOut[17+ofs] = B(selseg.colors[0]);
|
||||
udpOut[18+ofs] = W(selseg.colors[0]);
|
||||
udpOut[19+ofs] = R(selseg.colors[1]);
|
||||
udpOut[20+ofs] = G(selseg.colors[1]);
|
||||
udpOut[21+ofs] = B(selseg.colors[1]);
|
||||
udpOut[22+ofs] = W(selseg.colors[1]);
|
||||
udpOut[23+ofs] = R(selseg.colors[2]);
|
||||
udpOut[24+ofs] = G(selseg.colors[2]);
|
||||
udpOut[25+ofs] = B(selseg.colors[2]);
|
||||
udpOut[26+ofs] = W(selseg.colors[2]);
|
||||
udpOut[27+ofs] = selseg.cct;
|
||||
}
|
||||
|
||||
//uint16_t offs = SEG_OFFSET;
|
||||
//next value to be added has index: udpOut[offs + 0]
|
||||
|
||||
IPAddress broadcastIp;
|
||||
broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP());
|
||||
|
||||
@@ -246,47 +289,84 @@ void handleNotifications()
|
||||
} else if (!(receiveGroups & udpIn[36])) return;
|
||||
|
||||
bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
|
||||
//apply colors from notification
|
||||
if (receiveNotificationColor || !someSel)
|
||||
{
|
||||
col[0] = udpIn[3];
|
||||
col[1] = udpIn[4];
|
||||
col[2] = udpIn[5];
|
||||
if (version > 0) //sending module's white val is intended
|
||||
{
|
||||
col[3] = udpIn[10];
|
||||
if (version > 1)
|
||||
{
|
||||
colSec[0] = udpIn[12];
|
||||
colSec[1] = udpIn[13];
|
||||
colSec[2] = udpIn[14];
|
||||
colSec[3] = udpIn[15];
|
||||
|
||||
//apply colors from notification to main segment, only if not syncing full segments
|
||||
if ((receiveNotificationColor || !someSel) && (version < 11 || !receiveSegmentOptions)) {
|
||||
// primary color, only apply white if intented (version > 0)
|
||||
strip.setColor(0, RGBW32(udpIn[3], udpIn[4], udpIn[5], (version > 0) ? udpIn[10] : 0));
|
||||
if (version > 1) {
|
||||
strip.setColor(1, RGBW32(udpIn[12], udpIn[13], udpIn[14], udpIn[15])); // secondary color
|
||||
}
|
||||
if (version > 6) {
|
||||
strip.setColor(2, RGBW32(udpIn[20], udpIn[21], udpIn[22], udpIn[23])); // tertiary color
|
||||
if (version > 9 && version < 200 && udpIn[37] < 255) { // valid CCT/Kelvin value
|
||||
uint8_t cct = udpIn[38];
|
||||
if (udpIn[37] > 0) { //Kelvin
|
||||
cct = (((udpIn[37] << 8) + udpIn[38]) - 1900) >> 5;
|
||||
}
|
||||
strip.setCCT(cct);
|
||||
}
|
||||
if (version > 6)
|
||||
{
|
||||
strip.setColor(2, udpIn[20], udpIn[21], udpIn[22], udpIn[23]); //tertiary color
|
||||
}
|
||||
if (version > 9 && version < 200 && udpIn[37] < 255) { //valid CCT/Kelvin value
|
||||
uint8_t cct = udpIn[38];
|
||||
if (udpIn[37] > 0) { //Kelvin
|
||||
cct = (((udpIn[37] << 8) + udpIn[38]) - 1900) >> 5;
|
||||
}
|
||||
uint8_t segid = strip.getMainSegmentId();
|
||||
strip.getSegment(segid).setCCT(cct, segid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool timebaseUpdated = false;
|
||||
//apply effects from notification
|
||||
if (version < 200 && (receiveNotificationEffects || !someSel))
|
||||
bool applyEffects = (receiveNotificationEffects || !someSel);
|
||||
if (version < 200)
|
||||
{
|
||||
if (udpIn[8] < strip.getModeCount()) effectCurrent = udpIn[8];
|
||||
effectSpeed = udpIn[9];
|
||||
if (version > 2) effectIntensity = udpIn[16];
|
||||
if (version > 4 && udpIn[19] < strip.getPaletteCount()) effectPalette = udpIn[19];
|
||||
if (version > 5)
|
||||
{
|
||||
if (applyEffects && currentPlaylist >= 0) unloadPlaylist();
|
||||
if (version > 10 && (receiveSegmentOptions || receiveSegmentBounds)) {
|
||||
uint8_t numSrcSegs = udpIn[39];
|
||||
for (uint8_t i = 0; i < numSrcSegs; i++) {
|
||||
uint16_t ofs = 41 + i*udpIn[40]; //start of segment offset byte
|
||||
uint8_t id = udpIn[0 +ofs];
|
||||
if (id > strip.getMaxSegments()) continue;
|
||||
WS2812FX::Segment& selseg = strip.getSegment(id);
|
||||
uint16_t start = (udpIn[1+ofs] << 8 | udpIn[2+ofs]);
|
||||
uint16_t stop = (udpIn[3+ofs] << 8 | udpIn[4+ofs]);
|
||||
uint16_t offset = (udpIn[7+ofs] << 8 | udpIn[8+ofs]);
|
||||
if (!receiveSegmentOptions) {
|
||||
strip.setSegment(id, start, stop, selseg.grouping, selseg.spacing, offset);
|
||||
continue;
|
||||
}
|
||||
for (uint8_t j = 0; j<4; j++) selseg.setOption(j, (udpIn[9 +ofs] >> j) & 0x01); //only take into account mirrored, selected, on, reversed
|
||||
selseg.setOpacity(udpIn[10+ofs], id);
|
||||
if (applyEffects) {
|
||||
strip.setMode(id, udpIn[11+ofs]);
|
||||
selseg.speed = udpIn[12+ofs];
|
||||
selseg.intensity = udpIn[13+ofs];
|
||||
selseg.palette = udpIn[14+ofs];
|
||||
}
|
||||
if (receiveNotificationColor || !someSel) {
|
||||
selseg.setColor(0, RGBW32(udpIn[15+ofs],udpIn[16+ofs],udpIn[17+ofs],udpIn[18+ofs]), id);
|
||||
selseg.setColor(1, RGBW32(udpIn[19+ofs],udpIn[20+ofs],udpIn[21+ofs],udpIn[22+ofs]), id);
|
||||
selseg.setColor(2, RGBW32(udpIn[23+ofs],udpIn[24+ofs],udpIn[25+ofs],udpIn[26+ofs]), id);
|
||||
selseg.setCCT(udpIn[27+ofs], id);
|
||||
}
|
||||
//setSegment() also properly resets segments
|
||||
if (receiveSegmentBounds) {
|
||||
strip.setSegment(id, start, stop, udpIn[5+ofs], udpIn[6+ofs], offset);
|
||||
} else {
|
||||
strip.setSegment(id, selseg.start, selseg.stop, udpIn[5+ofs], udpIn[6+ofs], selseg.offset);
|
||||
}
|
||||
}
|
||||
stateChanged = true;
|
||||
}
|
||||
|
||||
// simple effect sync, applies to all selected segments
|
||||
if (applyEffects && (version < 11 || !receiveSegmentOptions)) {
|
||||
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
|
||||
WS2812FX::Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
if (udpIn[8] < strip.getModeCount()) strip.setMode(i, udpIn[8]);
|
||||
seg.speed = udpIn[9];
|
||||
if (version > 2) seg.intensity = udpIn[16];
|
||||
if (version > 4 && udpIn[19] < strip.getPaletteCount()) seg.palette = udpIn[19];
|
||||
}
|
||||
stateChanged = true;
|
||||
}
|
||||
|
||||
if (applyEffects && version > 5) {
|
||||
uint32_t t = (udpIn[25] << 24) | (udpIn[26] << 16) | (udpIn[27] << 8) | (udpIn[28]);
|
||||
t += PRESUMED_NETWORK_DELAY; //adjust trivially for network delay
|
||||
t -= millis();
|
||||
@@ -328,7 +408,7 @@ void handleNotifications()
|
||||
if (nightlightActive) nightlightDelayMins = udpIn[7];
|
||||
|
||||
if (receiveNotificationBrightness || !someSel) bri = udpIn[2];
|
||||
colorUpdated(CALL_MODE_NOTIFICATION);
|
||||
stateUpdated(CALL_MODE_NOTIFICATION);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -100,6 +100,10 @@
|
||||
#include "../usermods/seven_segment_display/usermod_v2_seven_segment_display.h"
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_SSDR
|
||||
#include "../usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h"
|
||||
#endif
|
||||
|
||||
#ifdef QUINLED_AN_PENTA
|
||||
#include "../usermods/quinled-an-penta/quinled-an-penta.h"
|
||||
#endif
|
||||
@@ -192,6 +196,10 @@ void registerUsermods()
|
||||
usermods.add(new SevenSegmentDisplay());
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_SSDR
|
||||
usermods.add(new UsermodSSDR());
|
||||
#endif
|
||||
|
||||
#ifdef QUINLED_AN_PENTA
|
||||
usermods.add(new QuinLEDAnPentaUsermod());
|
||||
#endif
|
||||
|
||||
@@ -31,3 +31,38 @@ void releaseJSONBufferLock()
|
||||
fileDoc = nullptr;
|
||||
jsonBufferLock = 0;
|
||||
}
|
||||
|
||||
|
||||
// extracts effect mode (or palette) name from names serialized string
|
||||
// caller must provide large enough buffer for name (incluing SR extensions)!
|
||||
uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen)
|
||||
{
|
||||
uint8_t qComma = 0;
|
||||
bool insideQuotes = false;
|
||||
uint8_t printedChars = 0;
|
||||
char singleJsonSymbol;
|
||||
size_t len = strlen_P(src);
|
||||
|
||||
// Find the mode name in JSON
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
singleJsonSymbol = pgm_read_byte_near(src + i);
|
||||
if (singleJsonSymbol == '\0') break;
|
||||
if (singleJsonSymbol == '@' && insideQuotes && qComma == mode) break; //stop when SR extension encountered
|
||||
switch (singleJsonSymbol) {
|
||||
case '"':
|
||||
insideQuotes = !insideQuotes;
|
||||
break;
|
||||
case '[':
|
||||
case ']':
|
||||
break;
|
||||
case ',':
|
||||
if (!insideQuotes) qComma++;
|
||||
default:
|
||||
if (!insideQuotes || (qComma != mode)) break;
|
||||
dest[printedChars++] = singleJsonSymbol;
|
||||
}
|
||||
if ((qComma > mode) || (printedChars >= maxLen)) break;
|
||||
}
|
||||
dest[printedChars] = '\0';
|
||||
return strlen(dest);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ void WLED::reset()
|
||||
while (millis() - dly < 450) {
|
||||
yield(); // enough time to send response to client
|
||||
}
|
||||
setAllLeds();
|
||||
applyBri();
|
||||
DEBUG_PRINTLN(F("MODULE RESET"));
|
||||
ESP.restart();
|
||||
}
|
||||
@@ -177,7 +177,7 @@ void WLED::loop()
|
||||
|
||||
yield();
|
||||
|
||||
if (!offMode || strip.isOffRefreshRequred)
|
||||
if (!offMode || strip.isOffRefreshRequired())
|
||||
strip.service();
|
||||
#ifdef ESP8266
|
||||
else if (!noWifiSleep)
|
||||
@@ -193,6 +193,8 @@ void WLED::loop()
|
||||
if (lastMqttReconnectAttempt > millis()) {
|
||||
rolloverMillis++;
|
||||
lastMqttReconnectAttempt = 0;
|
||||
ntpLastSyncTime = 0;
|
||||
strip.restartRuntime();
|
||||
}
|
||||
if (millis() - lastMqttReconnectAttempt > 30000) {
|
||||
lastMqttReconnectAttempt = millis();
|
||||
@@ -571,7 +573,7 @@ void WLED::initConnection()
|
||||
if (staticIP[0] != 0 && staticGateway[0] != 0) {
|
||||
WiFi.config(staticIP, staticGateway, staticSubnet, IPAddress(1, 1, 1, 1));
|
||||
} else {
|
||||
WiFi.config(0U, 0U, 0U);
|
||||
WiFi.config(IPAddress((uint32_t)0), IPAddress((uint32_t)0), IPAddress((uint32_t)0));
|
||||
}
|
||||
|
||||
lastReconnectAttempt = millis();
|
||||
@@ -682,13 +684,15 @@ void WLED::handleConnection()
|
||||
|
||||
if (now < 2000 && (!WLED_WIFI_CONFIGURED || apBehavior == AP_BEHAVIOR_ALWAYS))
|
||||
return;
|
||||
if (lastReconnectAttempt == 0)
|
||||
if (lastReconnectAttempt == 0) {
|
||||
initConnection();
|
||||
return;
|
||||
}
|
||||
|
||||
// reconnect WiFi to clear stale allocations if heap gets too low
|
||||
if (now - heapTime > 5000) {
|
||||
uint32_t heap = ESP.getFreeHeap();
|
||||
if (heap < JSON_BUFFER_SIZE+512 && lastHeap < JSON_BUFFER_SIZE+512) {
|
||||
if (heap < MIN_HEAP_SIZE && lastHeap < MIN_HEAP_SIZE) {
|
||||
DEBUG_PRINT(F("Heap too low! "));
|
||||
DEBUG_PRINTLN(heap);
|
||||
forceReconnect = true;
|
||||
@@ -800,4 +804,4 @@ void WLED::handleStatusLED()
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
/*
|
||||
Main sketch, global variable declarations
|
||||
@title WLED project sketch
|
||||
@version 0.13.0-b6
|
||||
@version 0.13.0-b7
|
||||
@author Christian Schwinne
|
||||
*/
|
||||
|
||||
// version code in format yymmddb (b = daily build)
|
||||
#define VERSION 2112080
|
||||
#define VERSION 2202222
|
||||
|
||||
//uncomment this if you have a "my_config.h" file you'd like to use
|
||||
//#define WLED_USE_MY_CONFIG
|
||||
@@ -16,6 +16,7 @@
|
||||
// ESP8266-01 (blue) got too little storage space to work with WLED. 0.10.2 is the last release supporting this unit.
|
||||
|
||||
// ESP8266-01 (black) has 1MB flash and can thus fit the whole program, although OTA update is not possible. Use 1M(128K SPIFFS).
|
||||
// 2-step OTA may still be possible: https://github.com/Aircoookie/WLED/issues/2040#issuecomment-981111096
|
||||
// Uncomment some of the following lines to disable features:
|
||||
// Alternatively, with platformio pass your chosen flags to your custom build target in platformio_override.ini
|
||||
|
||||
@@ -33,6 +34,7 @@
|
||||
#endif
|
||||
#define WLED_ENABLE_ADALIGHT // saves 500b only (uses GPIO3 (RX) for serial)
|
||||
//#define WLED_ENABLE_DMX // uses 3.5kb (use LEDPIN other than 2)
|
||||
//#define WLED_ENABLE_JSONLIVE // peek LED output via /json/live (WS binary peek is always enabled)
|
||||
#ifndef WLED_DISABLE_LOXONE
|
||||
#define WLED_ENABLE_LOXONE // uses 1.2kb
|
||||
#endif
|
||||
@@ -69,11 +71,14 @@
|
||||
#include "esp_wifi.h"
|
||||
#include <ESPmDNS.h>
|
||||
#include <AsyncTCP.h>
|
||||
//#include "SPIFFS.h"
|
||||
#ifndef CONFIG_LITTLEFS_FOR_IDF_3_2
|
||||
#define CONFIG_LITTLEFS_FOR_IDF_3_2
|
||||
#if LOROL_LITTLEFS
|
||||
#ifndef CONFIG_LITTLEFS_FOR_IDF_3_2
|
||||
#define CONFIG_LITTLEFS_FOR_IDF_3_2
|
||||
#endif
|
||||
#include <LITTLEFS.h>
|
||||
#else
|
||||
#include <LittleFS.h>
|
||||
#endif
|
||||
#include <LITTLEFS.h>
|
||||
#endif
|
||||
|
||||
#include "src/dependencies/network/Network.h"
|
||||
@@ -172,7 +177,11 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument<PSRAM_Allocator>;
|
||||
#ifdef ESP8266
|
||||
#define WLED_FS LittleFS
|
||||
#else
|
||||
#define WLED_FS LITTLEFS
|
||||
#if LOROL_LITTLEFS
|
||||
#define WLED_FS LITTLEFS
|
||||
#else
|
||||
#define WLED_FS LittleFS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// GLOBAL VARIABLES
|
||||
@@ -216,7 +225,7 @@ WLED_GLOBAL int8_t btnPin[WLED_MAX_BUTTONS] _INIT({0});
|
||||
WLED_GLOBAL int8_t btnPin[WLED_MAX_BUTTONS] _INIT({BTNPIN});
|
||||
#endif
|
||||
#ifndef RLYPIN
|
||||
WLED_GLOBAL int8_t rlyPin _INIT(12);
|
||||
WLED_GLOBAL int8_t rlyPin _INIT(-1);
|
||||
#else
|
||||
WLED_GLOBAL int8_t rlyPin _INIT(RLYPIN);
|
||||
#endif
|
||||
@@ -305,6 +314,8 @@ WLED_GLOBAL uint8_t receiveGroups _INIT(0x01); // sync receiv
|
||||
WLED_GLOBAL bool receiveNotificationBrightness _INIT(true); // apply brightness from incoming notifications
|
||||
WLED_GLOBAL bool receiveNotificationColor _INIT(true); // apply color
|
||||
WLED_GLOBAL bool receiveNotificationEffects _INIT(true); // apply effects setup
|
||||
WLED_GLOBAL bool receiveSegmentOptions _INIT(false); // apply segment options
|
||||
WLED_GLOBAL bool receiveSegmentBounds _INIT(false); // apply segment bounds (start, stop, offset)
|
||||
WLED_GLOBAL bool notifyDirect _INIT(false); // send notification if change via UI or HTTP API
|
||||
WLED_GLOBAL bool notifyButton _INIT(false); // send if updated by button or infrared remote
|
||||
WLED_GLOBAL bool notifyAlexa _INIT(false); // send notification if updated via Alexa
|
||||
@@ -360,11 +371,13 @@ WLED_GLOBAL bool hueApplyBri _INIT(true);
|
||||
WLED_GLOBAL bool hueApplyColor _INIT(true);
|
||||
#endif
|
||||
|
||||
WLED_GLOBAL uint16_t serialBaud _INIT(1152); // serial baud rate, multiply by 100
|
||||
|
||||
// Time CONFIG
|
||||
WLED_GLOBAL bool ntpEnabled _INIT(false); // get internet time. Only required if you use clock overlays or time-activated macros
|
||||
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 bool ntpEnabled _INIT(false); // get internet time. Only required if you use clock overlays or time-activated macros
|
||||
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 overlayMin _INIT(0), overlayMax _INIT(DEFAULT_LED_COUNT - 1); // boundaries of overlay mode
|
||||
@@ -416,9 +429,6 @@ WLED_GLOBAL bool interfacesInited _INIT(false);
|
||||
WLED_GLOBAL bool wasConnected _INIT(false);
|
||||
|
||||
// color
|
||||
WLED_GLOBAL byte colIT[] _INIT_N(({ 0, 0, 0, 0 })); // color that was last sent to LEDs
|
||||
WLED_GLOBAL byte colSecIT[] _INIT_N(({ 0, 0, 0, 0 }));
|
||||
|
||||
WLED_GLOBAL byte lastRandomIndex _INIT(0); // used to save last random color so the new one is not the same
|
||||
|
||||
// transitions
|
||||
@@ -468,7 +478,7 @@ WLED_GLOBAL byte effectCurrent _INIT(0);
|
||||
WLED_GLOBAL byte effectSpeed _INIT(128);
|
||||
WLED_GLOBAL byte effectIntensity _INIT(128);
|
||||
WLED_GLOBAL byte effectPalette _INIT(0);
|
||||
WLED_GLOBAL bool effectChanged _INIT(false);
|
||||
WLED_GLOBAL bool stateChanged _INIT(false);
|
||||
|
||||
// network
|
||||
WLED_GLOBAL bool udpConnected _INIT(false), udp2Connected _INIT(false), udpRgbConnected _INIT(false);
|
||||
@@ -497,13 +507,17 @@ WLED_GLOBAL byte dP[] _INIT_N(({ 255, 255, 255, 255, 255, 255 }));
|
||||
WLED_GLOBAL unsigned long countdownTime _INIT(1514764800L);
|
||||
WLED_GLOBAL bool countdownOverTriggered _INIT(true);
|
||||
|
||||
// timer
|
||||
WLED_GLOBAL byte lastTimerMinute _INIT(0);
|
||||
WLED_GLOBAL byte timerHours[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
|
||||
//timer
|
||||
WLED_GLOBAL byte lastTimerMinute _INIT(0);
|
||||
WLED_GLOBAL byte timerHours[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
|
||||
WLED_GLOBAL int8_t timerMinutes[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
|
||||
WLED_GLOBAL byte timerMacro[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
|
||||
WLED_GLOBAL byte timerWeekday[] _INIT_N(({ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 })); // weekdays to activate on
|
||||
// bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity
|
||||
WLED_GLOBAL byte timerMacro[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
|
||||
//weekdays to activate on, bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity
|
||||
WLED_GLOBAL byte timerWeekday[] _INIT_N(({ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }));
|
||||
//upper 4 bits start, lower 4 bits end month (default 28: start month 1 and end month 12)
|
||||
WLED_GLOBAL byte timerMonth[] _INIT_N(({28,28,28,28,28,28,28,28}));
|
||||
WLED_GLOBAL byte timerDay[] _INIT_N(({1,1,1,1,1,1,1,1}));
|
||||
WLED_GLOBAL byte timerDayEnd[] _INIT_N(({31,31,31,31,31,31,31,31}));
|
||||
|
||||
// blynk
|
||||
WLED_GLOBAL bool blynkEnabled _INIT(false);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user