Merge branch 'main' into power-ap

This commit is contained in:
Blaz Kristan 2023-07-15 22:01:48 +02:00
commit 75c048202e
110 changed files with 16798 additions and 11490 deletions

View File

@ -8,21 +8,23 @@ jobs:
name: Gather Environments
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Cache pip
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- uses: actions/setup-python@v2
- uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install PlatformIO
run: pip install -r requirements.txt
- name: Get default environments
id: envs
run: |
echo "::set-output name=environments::$(pio project config --json-output | jq -cr '.[0][1][0][1]')"
echo "environments=$(pio project config --json-output | jq -cr '.[0][1][0][1]')" >> $GITHUB_OUTPUT
outputs:
environments: ${{ steps.envs.outputs.environments }}
@ -32,24 +34,27 @@ jobs:
runs-on: ubuntu-latest
needs: get_default_envs
strategy:
fail-fast: false
matrix:
environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Cache pip
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Cache PlatformIO
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: ~/.platformio
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install PlatformIO
run: pip install -r requirements.txt
- name: Build firmware

View File

@ -1,5 +1,82 @@
## WLED changelog
#### Build 2307130
- larger `oappend()` stack buffer (3.5k) for ESP32
- Preset cycle bugfix (#3262)
- Rotary encoder ALT fix for large LED count (#3276)
- effect updates (2D Plasmaball), `blur()` speedup
- On/Off toggle from nodes view (may show unknow device type on older versions) (#3291)
- various fixes and improvements (ABL, crashes when changing presets with different segments)
#### Build 2306270
- ESP-NOW remote support (#3237)
- Pixel Magic tool (display pixel art) (#3249)
- Websocket (peek) fallback when connection cannot be established, WS retries (#3267)
- Add WiFi network scan RPC command to Improv Serial (#3271)
- Longer (custom option available) segment name for ESP32
- various fixes and improvements
#### Build 2306210
- 0.14.0-b3 release
- respect global I2C in all usermods (no local initilaisation of I2C bus)
- Multi relay usermod compile-time enabled option (-D MULTI_RELAY_ENABLED=true|false)
#### Build 2306180
- Added client-side option for applying effect defaults from metadata
- Improved ESP8266 stability by reducing WebSocket response resends
- Updated ESP8266 core to 3.1.2
#### Build 2306141
- Lissajous improvements
- Scrolling Text improvements (leading 0)
#### Build 2306140
- Add settings PIN (un)locking to JSON post API
#### Build 2306130
- Bumped version to 0.14-b3 (beta 3)
- added pin dropdowns in LED preferences (not for LED pins) and usermods
- introduced (unused ATM) NeoGammaWLEDMethod class
- Reverse proxy support
- PCF8754 support for Rotary encoder (requires wiring INT pin to ESP GPIO)
- Rely on global I2C pins for usermods (breaking change)
- various fixes and enhancements
#### Build 2306020
- Support for segment sets (PR #3171)
- Reduce sound simulation modes to 2 to facilitiate segment sets
- Trigger button immediately on press if all configured presets are the same (PR #3226)
- Changes for allowing Alexa to change light color to White when auto-calculating from RGB (PR #3211)
#### Build 2305280
- DDP protocol update (#3193)
- added PCF8574 I2C port expander support for Multi relay usermod
- MQTT multipacket (fragmented) message fix
- added option to retain MQTT brightness and color messages
- new ethernet board: @srg74 Ethernet Shield
- new 2D effects: Soap (#3184) & Octopus & Waving cell (credit @St3P40 https://github.com/80Stepko08)
- various fixes and enhancements
#### Build 2305090
- new ethernet board: @Wladi ABC! WLED Eth
- Battery usermod voltage calculation (#3116)
- custom palette editor (#3164)
- improvements in Dancing Shadows and Tartan effects
- UCS389x support
- switched to NeoPixelBus 2.7.5 (replaced NeoPixelBrightnessBus with NeoPixelBusLg)
- SPI bus clock selection (for LEDs) (#3173)
- DMX mode preset fix (#3134)
- iOS fix for scroll (#3182)
- Wordclock "Norddeutsch" fix (#3161)
- various fixes and enhancements
#### Build 2304090
- updated Arduino ESP8266 core to 4.1.0 (newer compiler)
- updated NeoPixelBus to 2.7.3 (with support for UCS890x chipset)
- better support for ESP32-C3, ESP32-S2 and ESP32-S3 (Arduino ESP32 core 5.2.0)
- iPad/tablet with 1024 pixels width in landscape orientation PC mode support (#3153)
- fix for Pixel Art Converter (#3155)
#### Build 2303240
- Peek scaling of large 2D matrices
- Added 0D (1 pixel) metadata for effects & enhance 0D (analog strip) UI handling

8
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "wled",
"version": "0.14.0-b2",
"version": "0.14.0-b3",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -1409,9 +1409,9 @@
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="
},
"semver-diff": {
"version": "2.1.0",

View File

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

View File

@ -9,11 +9,15 @@
# (use `platformio_override.ini` when building for your own board; see `platformio_override.ini.sample` for an example)
# ------------------------------------------------------------------------------
# Release / CI binaries
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, esp32s2_saola, esp32c3, esp32s3dev_8MB
# CI binaries
; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth # ESP32 variant builds are temporarily excluded from CI due to toolchain issues on the GitHub Actions Linux environment
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB
# Release binaries
; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB
# Build everything
; default_envs = esp32dev, esp8285_4CH_MagicHome, codm-controller-0.6-rev2, codm-controller-0.6, esp32s2_saola, d1_mini_5CH_Shojo_PCB, d1_mini, sp501e, 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
; 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, 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
@ -36,6 +40,8 @@ default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, esp32s
; default_envs = esp32dev_qio80
; default_envs = esp32_eth_ota1mapp
; default_envs = esp32s2_saola
; default_envs = esp32c3dev
; default_envs = lolin_s2_mini
src_dir = ./wled00
data_dir = ./wled00/data
@ -55,18 +61,29 @@ 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_2_0 = espressif8266@3.2.0
arduino_core_4_1_0 = espressif8266@4.1.0
arduino_core_3_1_2 = espressif8266@4.2.0
# Development platforms
arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop
arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/stage
# Platform to use for ESP8266
platform_wled_default = ${common.arduino_core_3_2_0}
platform_wled_default = ${common.arduino_core_3_1_2}
# We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization
platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
platformio/toolchain-xtensa @ ~2.40802.200502
platformio/tool-esptool @ ~1.413.0
platformio/tool-esptoolpy @ ~1.30000.0
#platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
platform_packages = platformio/framework-arduinoespressif8266
platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502
platformio/tool-esptool #@ ~1.413.0
platformio/tool-esptoolpy #@ ~1.30000.0
## previous platform for 8266, in case of problems with the new one
## you'll need makuna/NeoPixelBus@ 2.6.9 for arduino_core_3_2_0, which does not support Ucs890x
;; platform_wled_default = ${common.arduino_core_3_2_0}
;; platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
;; platformio/toolchain-xtensa @ ~2.40802.200502
;; platformio/tool-esptool @ ~1.413.0
;; platformio/tool-esptoolpy @ ~1.30000.0
# ------------------------------------------------------------------------------
# FLAGS: DEBUG
@ -99,11 +116,13 @@ debug_flags = -D DEBUG=1 -D WLED_DEBUG -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT
# This reduces the OTA size with ~45KB, so it's especially useful on low memory boards (512k/1m).
# ------------------------------------------------------------------------------
build_flags =
-Wno-attributes
-DMQTT_MAX_PACKET_SIZE=1024
-DSECURE_CLIENT=SECURE_CLIENT_BEARSSL
-DBEARSSL_SSL_BASIC
-D CORE_DEBUG_LEVEL=0
-D NDEBUG
-Wno-attributes ;; silence warnings about unknown attribute 'maybe_unused' in NeoPixelBus
#build_flags for the IRremoteESP8266 library (enabled decoders have to appear here)
-D _IR_ENABLE_DEFAULT_=false
-D DECODE_HASH=true
@ -111,7 +130,7 @@ build_flags =
-D DECODE_SONY=true
-D DECODE_SAMSUNG=true
-D DECODE_LG=true
; -Dregister= # remove warnings in C++17 due to use of deprecated register keyword by the FastLED library
;-Dregister= # remove warnings in C++17 due to use of deprecated register keyword by the FastLED library ;; warning: this breaks framework code on ESP32-C3 and ESP32-S2
-DWLED_USE_MY_CONFIG
; -D USERMOD_SENSORSTOMQTT
#For ADS1115 sensor uncomment following
@ -121,6 +140,7 @@ build_unflags =
build_flags_esp8266 = ${common.build_flags} ${esp8266.build_flags}
build_flags_esp32 = ${common.build_flags} ${esp32.build_flags}
build_flags_esp32_V4= ${common.build_flags} ${esp32_idf_V4.build_flags}
ldscript_1m128k = eagle.flash.1m128.ld
ldscript_2m512k = eagle.flash.2m512.ld
@ -150,22 +170,21 @@ upload_speed = 115200
# LIBRARIES: required dependencies
# Please note that we don't always use the latest version of a library.
#
# The following libraries have been included (and some of them changd) in the source:
# The following libraries have been included (and some of them changed) in the source:
# ArduinoJson@5.13.5, E131@1.0.0(changed), Time@1.5, Timezone@1.2.1
# ------------------------------------------------------------------------------
lib_compat_mode = strict
lib_deps =
fastled/FastLED @ 3.5.0
IRremoteESP8266 @ 2.8.2
makuna/NeoPixelBus @ 2.7.5
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.7
#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
#U8g2@~2.28.8
#U8g2@~2.32.10
#For Dallas sensor uncomment following 2 lines
#OneWire@~2.3.5
#milesburton/DallasTemperature@^3.9.0
#For compatible OLED display uncomment following
#U8g2 #@ ~2.33.15
#For Dallas sensor uncomment following
#OneWire @ ~2.3.7
#For BME280 sensor uncomment following
#BME280 @ ~3.0.0
; adafruit/Adafruit BMP280 Library @ 2.1.0
@ -182,8 +201,9 @@ build_flags =
-DESP8266
-DFP_IN_IROM
;-Wno-deprecated-declarations
;-Wno-register
;-Wno-misleading-indentation
-Wno-register ;; leaves some warnings when compiling C files: command-line option '-Wno-register' is valid for C++/ObjC++ but not for C
;-Dregister= # remove warnings in C++17 due to use of deprecated register keyword by the FastLED library ;; warning: this can be dangerous
-Wno-misleading-indentation
; NONOSDK22x_190703 = 2.2.2-dev(38a443e)
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
; lwIP 2 - Higher Bandwidth no Features
@ -196,11 +216,10 @@ build_flags =
-DMIMETYPE_MINIMAL
lib_deps =
${env.lib_deps}
#https://github.com/lorol/LITTLEFS.git
ESPAsyncTCP @ 1.2.2
ESPAsyncUDP
makuna/NeoPixelBus @ 2.6.9
${env.lib_deps}
[esp32]
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip
@ -214,37 +233,65 @@ build_flags = -g
-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
; -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when builing with arduino-esp32 >=2.0.3
; -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
lib_deps =
${env.lib_deps}
https://github.com/lorol/LITTLEFS.git
makuna/NeoPixelBus @ 2.6.9
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps}
[esp32_idf_V4]
;; experimental build environment for ESP32 using ESP-IDF 4.4.x / arduino-esp32 v2.0.5
;; very similar to the normal ESP32 flags, but omitting Lorol LittleFS, as littlefs is included in the new framework already.
;;
;; please note that you can NOT update existing ESP32 installs with a "V4" build. Also updating by OTA will not work properly.
;; You need to completely erase your device (esptool erase_flash) first, then install the "V4" build from VSCode+platformio.
platform = espressif32@5.2.0
platform_packages =
toolchain-riscv32-esp @ 8.4.0+2021r2-patch5 ; required for platform version < 5.3.0, remove this line when upgrading to platform >=5.3.0
build_flags = -g
-Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one
-DARDUINO_ARCH_ESP32 -DESP32
#-DCONFIG_LITTLEFS_FOR_IDF_3_2
-D CONFIG_ASYNC_TCP_USE_WDT=0
-DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
lib_deps =
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps}
[esp32s2]
;; generic definitions for all ESP32-S2 boards
platform = espressif32@5.2.0
platform_packages =
toolchain-riscv32-esp @ 8.4.0+2021r2-patch5 ; required for platform version < 5.3.0, remove this line when upgrading to platform >=5.3.0
build_flags = -g
-DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32S2
-DCONFIG_IDF_TARGET_ESP32S2
-DCONFIG_IDF_TARGET_ESP32S2=1
-D CONFIG_ASYNC_TCP_USE_WDT=0
-DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0
-DCO
-DARDUINO_USB_MODE=0 ;; this flag is mandatory for ESP32-S2 !
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_CDC_ON_BOOT, ARDUINO_USB_MSC_ON_BOOT, ARDUINO_USB_DFU_ON_BOOT
;; ARDUINO_USB_CDC_ON_BOOT
lib_deps =
${env.lib_deps}
makuna/NeoPixelBus @ 2.6.9
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps}
[esp32c3]
;; generic definitions for all ESP32-C3 boards
platform = espressif32@5.2.0
platform_packages =
toolchain-riscv32-esp @ 8.4.0+2021r2-patch5 ; required for platform version < 5.3.0, remove this line when upgrading to platform >=5.3.0
build_flags = -g
-DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32C3
-DCONFIG_IDF_TARGET_ESP32C3
-DCONFIG_IDF_TARGET_ESP32C3=1
-D CONFIG_ASYNC_TCP_USE_WDT=0
-DCO
-DARDUINO_USB_MODE=1 ;; this flag is mandatory for ESP32-C3
@ -252,27 +299,28 @@ build_flags = -g
;; ARDUINO_USB_CDC_ON_BOOT
lib_deps =
${env.lib_deps}
makuna/NeoPixelBus @ 2.6.9
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps}
[esp32s3]
;; generic definitions for all ESP32-S3 boards
platform = espressif32@5.2.0
platform_packages =
toolchain-riscv32-esp @ 8.4.0+2021r2-patch5 ; required for platform version < 5.3.0, remove this line when upgrading to platform >=5.3.0
build_flags = -g
-DESP32
-DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32S3
-DCONFIG_IDF_TARGET_ESP32S3
-DCONFIG_IDF_TARGET_ESP32S3=1
-D CONFIG_ASYNC_TCP_USE_WDT=0
-DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_DFU_ON_BOOT=0
-DCO
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_MODE, ARDUINO_USB_CDC_ON_BOOT, ARDUINO_USB_MSC_ON_BOOT, ARDUINO_USB_DFU_ON_BOOT
;; ARDUINO_USB_MODE, ARDUINO_USB_CDC_ON_BOOT
lib_deps =
${env.lib_deps}
;; NeoPixelBus 2.7.1 is the first that has official support for ESP32-S3
makuna/NeoPixelBus @ ~2.7.1
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps}
# ------------------------------------------------------------------------------
@ -367,6 +415,21 @@ board_build.partitions = ${esp32.default_partitions}
board_build.f_flash = 80000000L
board_build.flash_mode = qio
[env:esp32dev_V4_dio80]
;; experimental ESP32 env using ESP-IDF V4.4.x
;; Warning: this build environment is not stable!!
;; please erase your device before installing.
board = esp32dev
platform = ${esp32_idf_V4.platform}
platform_packages = ${esp32_idf_V4.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_V4_qio80 #-D WLED_DISABLE_BROWNOUT_DET
lib_deps = ${esp32_idf_V4.lib_deps}
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32_idf_V4.default_partitions}
board_build.f_flash = 80000000L
board_build.flash_mode = dio
[env:esp32_eth]
board = esp32-poe
platform = ${esp32.platform}
@ -387,17 +450,20 @@ board_build.flash_mode = qio
upload_speed = 460800
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=S2_saola
-DARDUINO_USB_CDC_ON_BOOT=1
lib_deps = ${esp32s2.lib_deps}
[env:esp32c3]
platform = espressif32@5.1.1 ;; well-tested on -C3, good compatibility with WLED
; platform = espressif32@~5.2.0 ;; might help in case you experience bootloops due to corrupted flash filesystem
[env:esp32c3dev]
extends = esp32c3
platform = ${esp32c3.platform}
platform_packages = ${esp32c3.platform_packages}
framework = arduino
board = esp32-c3-devkitm-1
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3
-D WLED_WATCHDOG_TIMEOUT=0
; -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual USB
-DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB
;-DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip
upload_speed = 460800
build_unflags = ${common.build_unflags}
lib_deps = ${esp32c3.lib_deps}
@ -405,14 +471,14 @@ lib_deps = ${esp32c3.lib_deps}
[env:esp32s3dev_8MB]
;; ESP32-S3-DevKitC-1 development board, with 8MB FLASH, no PSRAM (flash_mode: qio)
board = esp32-s3-devkitc-1
platform = espressif32@5.1.1
platform_packages =
platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600 ; or 460800
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags}
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
-D ARDUINO_USB_CDC_ON_BOOT=0 -D ARDUINO_USB_MSC_ON_BOOT=0 -D ARDUINO_DFU_ON_BOOT=0 -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
;-D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MSC_ON_BOOT=0 -D ARDUINO_DFU_ON_BOOT=0 ; -D ARDUINO_USB_MODE=0 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
;-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=0 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
;-D WLED_DEBUG
lib_deps = ${esp32s3.lib_deps}
board_build.partitions = tools/WLED_ESP32_8MB.csv
@ -426,13 +492,14 @@ monitor_filters = esp32_exception_decoder
;board = um_tinys3 ; -> needs workaround from https://github.com/Aircoookie/WLED/pull/2905#issuecomment-1328049860
;board = esp32s3box ; -> error: 'esp32_adc2gpio' was not declared in this scope
board = esp32-s3-devkitc-1 ; -> compiles, but does not support PSRAM
platform = espressif32 @ ~5.2.0
platform_packages =
platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags}
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
-D ARDUINO_USB_MODE=1 -D ARDUINO_USB_MSC_ON_BOOT=0 ; -D ARDUINO_USB_CDC_ON_BOOT=0
;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=0 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
; -D WLED_RELEASE_NAME=ESP32-S3_PSRAM
-D WLED_USE_PSRAM -DBOARD_HAS_PSRAM ; tells WLED that PSRAM shall be used
lib_deps = ${esp32s3.lib_deps}
@ -504,13 +571,17 @@ build_flags = ${common.build_flags_esp8266} -D LEDPIN=12 -D IRPIN=-1 -D RLYPIN=2
lib_deps = ${esp8266.lib_deps}
[env:lolin_s2_mini]
platform = espressif32@5.1.1
platform = ${esp32s2.platform}
platform_packages = ${esp32s2.platform_packages}
board = lolin_s2_mini
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
build_unflags = ${common.build_unflags}
build_unflags = ${common.build_unflags} #-DARDUINO_USB_CDC_ON_BOOT=1
build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=LolinS2
-DBOARD_HAS_PSRAM
-D ARDUINO_USB_CDC_ON_BOOT
-DARDUINO_USB_CDC_ON_BOOT=1 # try disabling and enabling unflag above in case of board-specific issues, will disable Serial
-DARDUINO_USB_MSC_ON_BOOT=0
-DARDUINO_USB_DFU_ON_BOOT=0
-DLOLIN_WIFI_FIX ; seems to work much better with this
-D WLED_USE_PSRAM
-D WLED_WATCHDOG_TIMEOUT=0
-D CONFIG_ASYNC_TCP_USE_WDT=0
@ -530,9 +601,28 @@ lib_deps = ${esp32s2.lib_deps}
# custom board configurations
# ------------------------------------------------------------------------------
[env:esp32c3dev_2MB]
;; for ESP32-C3 boards with 2MB flash (instead of 4MB).
;; this board need a specific partition file. OTA not possible.
extends = esp32c3
platform = ${esp32c3.platform}
platform_packages = ${esp32c3.platform_packages}
board = esp32-c3-devkitm-1
build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3
-D WLED_WATCHDOG_TIMEOUT=0
-D WLED_DISABLE_OTA
; -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB
-DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip
build_unflags = ${common.build_unflags}
upload_speed = 115200
lib_deps = ${esp32c3.lib_deps}
board_build.partitions = tools/WLED_ESP32_2MB_noOTA.csv
board_build.flash_mode = dio
[env:wemos_shield_esp32]
board = esp32dev
platform = espressif32@3.2
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
upload_speed = 460800
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32}
@ -557,7 +647,8 @@ board = esp32dev
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D LEDPIN=27 -D BTNPIN=39
lib_deps = ${esp32.lib_deps}
platform = espressif32@3.2
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
board_build.partitions = ${esp32.default_partitions}
[env:sp501e]
@ -637,10 +728,10 @@ lib_deps = ${esp8266.lib_deps}
# ------------------------------------------------------------------------------
# codm pixel controller board configurations
# codm-controller-0.6 can also be used for the TYWE3S controller
# codm-controller-0_6 can also be used for the TYWE3S controller
# ------------------------------------------------------------------------------
[env:codm-controller-0.6]
[env:codm-controller-0_6]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
@ -649,7 +740,7 @@ build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps}
[env:codm-controller-0.6-rev2]
[env:codm-controller-0_6-rev2]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
@ -663,7 +754,8 @@ lib_deps = ${esp8266.lib_deps}
# ------------------------------------------------------------------------------
[env:elekstube_ips]
board = esp32dev
platform = espressif32@3.2
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
upload_speed = 921600
build_flags = ${common.build_flags_esp32} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED
-D USERMOD_RTC

View File

@ -4,63 +4,55 @@
#
# pip-compile
#
aiofiles==0.8.0
aiofiles==22.1.0
# via platformio
ajsonrpc==1.2.0
# via platformio
anyio==3.6.1
anyio==3.6.2
# via starlette
async-timeout==4.0.2
# via zeroconf
bottle==0.12.23
bottle==0.12.25
# via platformio
certifi==2022.12.7
# via requests
charset-normalizer==2.1.1
charset-normalizer==3.1.0
# via requests
click==8.1.3
# via
# platformio
# uvicorn
colorama==0.4.5
colorama==0.4.6
# via platformio
h11==0.13.0
h11==0.14.0
# via
# uvicorn
# wsproto
idna==3.3
idna==3.4
# via
# anyio
# requests
ifaddr==0.2.0
# via zeroconf
marshmallow==3.17.0
marshmallow==3.19.0
# via platformio
packaging==21.3
packaging==23.1
# via marshmallow
platformio==6.1.4
platformio==6.1.6
# via -r requirements.in
pyelftools==0.29
# via platformio
pyparsing==3.0.9
# via packaging
pyserial==3.5
# via platformio
requests==2.28.1
requests==2.31.0
# via platformio
semantic-version==2.10.0
# via platformio
sniffio==1.2.0
sniffio==1.3.0
# via anyio
starlette==0.20.4
starlette==0.23.1
# via platformio
tabulate==0.8.10
tabulate==0.9.0
# via platformio
urllib3==1.26.11
urllib3==1.26.15
# via requests
uvicorn==0.18.2
uvicorn==0.20.0
# via platformio
wsproto==1.1.0
# via platformio
zeroconf==0.39.0
wsproto==1.2.0
# via platformio

View File

@ -0,0 +1,5 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 20K,
otadata, data, ota, 0xe000, 8K,
app0, app, ota_0, 0x10000, 1536K,
spiffs, data, spiffs, 0x190000, 384K,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 20K
3 otadata data ota 0xe000 8K
4 app0 app ota_0 0x10000 1536K
5 spiffs data spiffs 0x190000 384K

View File

@ -221,6 +221,8 @@ function writeChunks(srcDir, specs, resultFile) {
writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h", 'index');
writeHtmlGzipped("wled00/data/simple.htm", "wled00/html_simple.h", 'simple');
writeHtmlGzipped("wled00/data/pixart/pixart.htm", "wled00/html_pixart.h", 'pixart');
writeHtmlGzipped("wled00/data/cpal/cpal.htm", "wled00/html_cpal.h", 'cpal');
writeHtmlGzipped("wled00/data/pxmagic/pxmagic.htm", "wled00/html_pxmagic.h", 'pxmagic');
/*
writeChunks(
"wled00/data",
@ -388,12 +390,6 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()=====";
method: "gzip",
filter: "html-minify",
},
{
file: "liveviewws.htm",
name: "PAGE_liveviewws",
method: "gzip",
filter: "html-minify",
},
{
file: "liveviewws2D.htm",
name: "PAGE_liveviewws2D",

View File

@ -8,7 +8,6 @@
#pragma once
#include "wled.h"
#include <Wire.h>
#include <BH1750.h>
// the max frequency to check photoresistor, 10 seconds
@ -56,15 +55,6 @@ private:
static const char _offset[];
static const char _HomeAssistantDiscovery[];
// set the default pins based on the architecture, these get overridden by Usermod menu settings
#ifdef ARDUINO_ARCH_ESP32 // ESP32 boards
#define HW_PIN_SCL 22
#define HW_PIN_SDA 21
#else // ESP8266 boards
#define HW_PIN_SCL 5
#define HW_PIN_SDA 4
#endif
int8_t ioPin[2] = {HW_PIN_SCL, HW_PIN_SDA}; // I2C pins: SCL, SDA...defaults to Arch hardware pins but overridden at setup()
bool initDone = false;
bool sensorFound = false;
@ -123,14 +113,7 @@ private:
public:
void setup()
{
bool HW_Pins_Used = (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA); // note whether architecture-based hardware SCL/SDA pins used
PinOwner po = PinOwner::UM_BH1750; // defaults to being pinowner for SCL/SDA pins
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } }; // allocate pins
if (HW_Pins_Used) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
if (!pinManager.allocateMultiplePins(pins, 2, po)) return;
Wire.begin(ioPin[1], ioPin[0]);
if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
sensorFound = lightMeter.begin();
initDone = true;
}
@ -190,7 +173,9 @@ public:
user = root.createNestedObject(F("u"));
JsonArray lux_json = user.createNestedArray(F("Luminance"));
if (!sensorFound) {
if (!enabled) {
lux_json.add(F("disabled"));
} else if (!sensorFound) {
// if no sensor
lux_json.add(F("BH1750 "));
lux_json.add(F("Not Found"));
@ -216,9 +201,6 @@ public:
top[FPSTR(_minReadInterval)] = minReadingInterval;
top[FPSTR(_HomeAssistantDiscovery)] = HomeAssistantDiscovery;
top[FPSTR(_offset)] = offset;
JsonArray io_pin = top.createNestedArray(F("pin"));
for (byte i=0; i<2; i++) io_pin.add(ioPin[i]);
top[F("help4Pins")] = F("SCL,SDA"); // help for Settings page
DEBUG_PRINTLN(F("BH1750 config saved."));
}
@ -226,8 +208,6 @@ public:
// called before setup() to populate properties from values stored in cfg.json
bool readFromConfig(JsonObject &root)
{
int8_t newPin[2]; for (byte i=0; i<2; i++) newPin[i] = ioPin[i]; // prepare to note changed pins
// we look for JSON object.
JsonObject top = root[FPSTR(_name)];
if (top.isNull())
@ -244,27 +224,12 @@ public:
configComplete &= getJsonValue(top[FPSTR(_minReadInterval)], minReadingInterval, 500); //ms
configComplete &= getJsonValue(top[FPSTR(_HomeAssistantDiscovery)], HomeAssistantDiscovery, false);
configComplete &= getJsonValue(top[FPSTR(_offset)], offset, 1);
for (byte i=0; i<2; i++) configComplete &= getJsonValue(top[F("pin")][i], newPin[i], ioPin[i]);
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
// first run: reading from cfg.json
for (byte i=0; i<2; i++) ioPin[i] = newPin[i];
DEBUG_PRINTLN(F(" config loaded."));
} else {
DEBUG_PRINTLN(F(" config (re)loaded."));
// changing parameters from settings page
bool pinsChanged = false;
for (byte i=0; i<2; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; } // check if any pins changed
if (pinsChanged) { //if pins changed, deallocate old pins and allocate new ones
PinOwner po = PinOwner::UM_BH1750;
if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
pinManager.deallocateMultiplePins((const uint8_t *)ioPin, 2, po); // deallocate pins
for (byte i=0; i<2; i++) ioPin[i] = newPin[i];
setup();
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[F("pin")].isNull();
}
return configComplete;

View File

@ -9,7 +9,6 @@
#include "wled.h"
#include <Arduino.h>
#include <Wire.h>
#include <BME280I2C.h> // BME280 sensor
#include <EnvironmentCalculations.h> // BME280 extended measurements
@ -34,7 +33,6 @@ private:
#ifdef ESP8266
//uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8
#endif
int8_t ioPin[2] = {i2c_scl, i2c_sda}; // I2C pins: SCL, SDA...defaults to Arch hardware pins but overridden at setup()
bool initDone = false;
// BME280 sensor settings
@ -186,13 +184,7 @@ private:
public:
void setup()
{
bool HW_Pins_Used = (ioPin[0]==i2c_scl && ioPin[1]==i2c_sda); // note whether architecture-based hardware SCL/SDA pins used
PinOwner po = PinOwner::UM_BME280; // defaults to being pinowner for SCL/SDA pins
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } }; // allocate pins
if (HW_Pins_Used) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
if (!pinManager.allocateMultiplePins(pins, 2, po)) { sensorType=0; return; }
Wire.begin(ioPin[1], ioPin[0]);
if (i2c_scl<0 || i2c_sda<0) { enabled = false; sensorType = 0; return; }
if (!bme.begin())
{
@ -415,9 +407,6 @@ public:
top[F("PublishAlways")] = PublishAlways;
top[F("UseCelsius")] = UseCelsius;
top[F("HomeAssistantDiscovery")] = HomeAssistantDiscovery;
JsonArray io_pin = top.createNestedArray(F("pin"));
for (byte i=0; i<2; i++) io_pin.add(ioPin[i]);
top[F("help4Pins")] = F("SCL,SDA"); // help for Settings page
DEBUG_PRINTLN(F("BME280 config saved."));
}
@ -427,8 +416,6 @@ public:
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
int8_t newPin[2]; for (byte i=0; i<2; i++) newPin[i] = ioPin[i]; // prepare to note changed pins
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
DEBUG_PRINT(F(_name));
@ -447,27 +434,14 @@ public:
configComplete &= getJsonValue(top[F("PublishAlways")], PublishAlways, false);
configComplete &= getJsonValue(top[F("UseCelsius")], UseCelsius, true);
configComplete &= getJsonValue(top[F("HomeAssistantDiscovery")], HomeAssistantDiscovery, false);
for (byte i=0; i<2; i++) configComplete &= getJsonValue(top[F("pin")][i], newPin[i], ioPin[i]);
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
// first run: reading from cfg.json
for (byte i=0; i<2; i++) ioPin[i] = newPin[i];
DEBUG_PRINTLN(F(" config loaded."));
} else {
DEBUG_PRINTLN(F(" config (re)loaded."));
// changing parameters from settings page
bool pinsChanged = false;
for (byte i=0; i<2; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; } // check if any pins changed
if (pinsChanged) { //if pins changed, deallocate old pins and allocate new ones
PinOwner po = PinOwner::UM_BME280;
if (ioPin[0]==i2c_scl && ioPin[1]==i2c_sda) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
pinManager.deallocateMultiplePins((const uint8_t *)ioPin, 2, po); // deallocate pins
for (byte i=0; i<2; i++) ioPin[i] = newPin[i];
setup();
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[F("pin")].isNull();
}
return configComplete;

View File

@ -26,6 +26,15 @@
#endif
#endif
//the default ratio for the voltage divider
#ifndef USERMOD_BATTERY_VOLTAGE_MULTIPLIER
#ifdef ARDUINO_ARCH_ESP32
#define USERMOD_BATTERY_VOLTAGE_MULTIPLIER 2.0f
#else //ESP8266 boards
#define USERMOD_BATTERY_VOLTAGE_MULTIPLIER 4.2f
#endif
#endif
#ifndef USERMOD_BATTERY_MAX_VOLTAGE
#define USERMOD_BATTERY_MAX_VOLTAGE 4.2f
#endif

View File

@ -29,6 +29,10 @@ class UsermodBattery : public Usermod
float rawValue = 0.0f;
// calculated voltage
float voltage = maxBatteryVoltage;
// between 0 and 1, to control strength of voltage smoothing filter
float alpha = 0.05f;
// multiplier for the voltage divider that is in place between ADC pin and battery, default will be 2 but might be adapted to readout voltages over ~5v ESP32 or ~6.6v ESP8266
float voltageMultiplier = USERMOD_BATTERY_VOLTAGE_MULTIPLIER;
// mapped battery level based on voltage
int8_t batteryLevel = 100;
// offset or calibration value to fine tune the calculated voltage
@ -110,6 +114,17 @@ class UsermodBattery : public Usermod
}
}
float readVoltage()
{
#ifdef ARDUINO_ARCH_ESP32
// use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV default attentuation) and divide by 1000 to get from milivolts to volts and multiply by voltage multiplier and apply calibration value
return (analogReadMilliVolts(batteryPin) / 1000.0f) * voltageMultiplier + calibration;
#else
// use analog read on esp8266 ( 0V ~ 1V no attenuation options) and divide by ADC precision 1023 and multiply by voltage multiplier and apply calibration value
return (analogRead(batteryPin) / 1023.0f) * voltageMultiplier + calibration;
#endif
}
public:
//Functions called by WLED
@ -126,6 +141,7 @@ class UsermodBattery : public Usermod
if (pinManager.allocatePin(batteryPin, false, PinOwner::UM_Battery)) {
DEBUG_PRINTLN(F("Battery pin allocation succeeded."));
success = true;
voltage = readVoltage();
}
if (!success) {
@ -135,8 +151,8 @@ class UsermodBattery : public Usermod
pinMode(batteryPin, INPUT);
}
#else //ESP8266 boards have only one analog input pin A0
pinMode(batteryPin, INPUT);
voltage = readVoltage();
#endif
nextReadTime = millis() + readingInterval;
@ -176,22 +192,12 @@ class UsermodBattery : public Usermod
initializing = false;
#ifdef ARDUINO_ARCH_ESP32
// use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV)
rawValue = analogReadMilliVolts(batteryPin);
// calculate the voltage
voltage = (rawValue / 1000.0f) + calibration;
// usually a voltage divider (50%) is used on ESP32, so we need to multiply by 2
voltage *= 2.0f;
#else
// read battery raw input
rawValue = analogRead(batteryPin);
rawValue = readVoltage();
// filter with exponential smoothing because ADC in esp32 is fluctuating too much for a good single readout
voltage = voltage + alpha * (rawValue - voltage);
// calculate the voltage
voltage = ((rawValue / getAdcPrecision()) * maxBatteryVoltage) + calibration;
#endif
// check if voltage is within specified voltage range, allow 10% over/under voltage
voltage = ((voltage < minBatteryVoltage * 0.85f) || (voltage > maxBatteryVoltage * 1.1f)) ? -1.0f : voltage;
// check if voltage is within specified voltage range, allow 10% over/under voltage - removed cause this just makes it hard for people to troubleshoot as the voltage in the web gui will say invalid instead of displaying a voltage
//voltage = ((voltage < minBatteryVoltage * 0.85f) || (voltage > maxBatteryVoltage * 1.1f)) ? -1.0f : voltage;
// translate battery voltage into percentage
/*
@ -363,6 +369,7 @@ class UsermodBattery : public Usermod
battery[F("max-voltage")] = maxBatteryVoltage;
battery[F("capacity")] = totalBatteryCapacity;
battery[F("calibration")] = calibration;
battery[F("voltage-multiplier")] = voltageMultiplier;
battery[FPSTR(_readInterval)] = readingInterval;
JsonObject ao = battery.createNestedObject(F("auto-off")); // auto off section
@ -375,6 +382,9 @@ class UsermodBattery : public Usermod
lp[FPSTR(_threshold)] = lowPowerIndicatorThreshold;
lp[FPSTR(_duration)] = lowPowerIndicatorDuration;
// read voltage in case calibration or voltage multiplier changed to see immediate effect
voltage = readVoltage();
DEBUG_PRINTLN(F("Battery config saved."));
}
@ -439,6 +449,7 @@ class UsermodBattery : public Usermod
setMaxBatteryVoltage(battery[F("max-voltage")] | maxBatteryVoltage);
setTotalBatteryCapacity(battery[F("capacity")] | totalBatteryCapacity);
setCalibration(battery[F("calibration")] | calibration);
setVoltageMultiplier(battery[F("voltage-multiplier")] | voltageMultiplier);
setReadingInterval(battery[FPSTR(_readInterval)] | readingInterval);
JsonObject ao = battery[F("auto-off")];
@ -595,21 +606,7 @@ class UsermodBattery : public Usermod
totalBatteryCapacity = capacity;
}
/*
* Get the choosen adc precision
* esp8266 = 10bit resolution = 1024.0f
* esp32 = 12bit resolution = 4095.0f
*/
float getAdcPrecision()
{
#ifdef ARDUINO_ARCH_ESP32
// esp32
return 4096.0f;
#else
// esp8266
return 1024.0f;
#endif
}
/*
* Get the calculated voltage
@ -647,6 +644,23 @@ class UsermodBattery : public Usermod
calibration = offset;
}
/*
* Set the voltage multiplier value
* A multiplier that may need adjusting for different voltage divider setups
*/
void setVoltageMultiplier(float multiplier)
{
voltageMultiplier = multiplier;
}
/*
* Get the voltage multiplier value
* A multiplier that may need adjusting for different voltage divider setups
*/
float getVoltageMultiplier()
{
return voltageMultiplier;
}
/*
* Get auto-off feature enabled status

View File

@ -133,13 +133,13 @@ private:
return false;
}
read32(bmpFS); // filesize in bytes
read32(bmpFS); // reserved
(void) read32(bmpFS); // filesize in bytes
(void) read32(bmpFS); // reserved
seekOffset = read32(bmpFS); // start of bitmap
headerSize = read32(bmpFS); // header size
w = read32(bmpFS); // width
h = read32(bmpFS); // height
read16(bmpFS); // color planes (must be 1)
(void) read16(bmpFS); // color planes (must be 1)
bitDepth = read16(bmpFS);
if (read32(bmpFS) != 0 || (bitDepth != 24 && bitDepth != 1 && bitDepth != 4 && bitDepth != 8)) {
@ -151,9 +151,9 @@ private:
uint32_t palette[256];
if (bitDepth <= 8) // 1,4,8 bit bitmap: read color palette
{
read32(bmpFS); read32(bmpFS); read32(bmpFS); // size, w resolution, h resolution
(void) read32(bmpFS); (void) read32(bmpFS); (void) read32(bmpFS); // size, w resolution, h resolution
paletteSize = read32(bmpFS);
if (paletteSize == 0) paletteSize = bitDepth * bitDepth; //if 0, size is 2^bitDepth
if (paletteSize == 0) paletteSize = 1 << bitDepth; //if 0, size is 2^bitDepth
bmpFS.seek(14 + headerSize); // start of color palette
for (uint16_t i = 0; i < paletteSize; i++) {
palette[i] = read32(bmpFS);
@ -198,7 +198,7 @@ private:
}
b = c; g = c >> 8; r = c >> 16;
}
if (dimming != 255) { // only dimm when needed
if (dimming != 255) { // only dim when needed
r *= dimming; g *= dimming; b *= dimming;
r = r >> 8; g = g >> 8; b = b >> 8;
}

View File

@ -20,7 +20,7 @@
* This usermod handles PIR sensor states.
* The strip will be switched on and the off timer will be resetted when the sensor goes HIGH.
* When the sensor state goes LOW, the off timer is started and when it expires, the strip is switched off.
*
* Maintained by: @blazoncek
*
* Usermods allow you to add own functionality to WLED more easily
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
@ -38,21 +38,21 @@ public:
~PIRsensorSwitch() {}
//Enable/Disable the PIR sensor
void EnablePIRsensor(bool en) { enabled = en; }
inline void EnablePIRsensor(bool en) { enabled = en; }
// Get PIR sensor enabled/disabled state
bool PIRsensorEnabled() { return enabled; }
inline bool PIRsensorEnabled() { return enabled; }
private:
byte prevPreset = 0;
byte prevPlaylist = 0;
uint32_t offTimerStart = 0; // off timer start time
volatile unsigned long offTimerStart = 0; // off timer start time
volatile bool PIRtriggered = false; // did PIR trigger?
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
@ -66,6 +66,7 @@ private:
// 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 m_offMode = offMode;
bool m_override = false;
// Home Assistant
bool HomeAssistantDiscovery = false; // is HA discovery turned on
@ -81,12 +82,122 @@ private:
static const char _offOnly[];
static const char _haDiscovery[];
static const char _notify[];
static const char _override[];
/**
* check if it is daytime
* if sunrise/sunset is not defined (no NTP or lat/lon) default to nighttime
*/
bool isDayTime() {
static bool isDayTime();
/**
* switch strip on/off
*/
void switchStrip(bool switchOn);
void publishMqtt(const char* state);
// Create an MQTT Binary Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop.
void publishHomeAssistantAutodiscovery();
/**
* Read and update PIR sensor state.
* Initilize/reset switch off timer
*/
bool updatePIRsensorState();
/**
* switch off the strip if the delay has elapsed
*/
bool handleOffTimer();
public:
//Functions called by WLED
/**
* setup() is called once at boot. WiFi is not yet connected at this point.
* You can use it to initialize variables, sensors or similar.
*/
void setup();
/**
* connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces
*/
//void connected();
/**
* onMqttConnect() is called when MQTT connection is established
*/
void onMqttConnect(bool sessionPresent);
/**
* loop() is called continuously. Here you can check for events, read sensors, etc.
*/
void loop();
/**
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
*
* Add PIR sensor state and switch off timer duration to jsoninfo
*/
void addToJsonInfo(JsonObject &root);
/**
* onStateChanged() is used to detect WLED state change
*/
void onStateChange(uint8_t mode);
/**
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
//void addToJsonState(JsonObject &root);
/**
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
void readFromJsonState(JsonObject &root);
/**
* provide the changeable values
*/
void addToConfig(JsonObject &root);
/**
* provide UI information and allow extending UI options
*/
void appendConfigData();
/**
* restore the changeable values
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
*
* The function should return true if configuration was successfully loaded or false if there was no configuration.
*/
bool readFromConfig(JsonObject &root);
/**
* 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_PIRSWITCH; }
};
// strings to reduce flash memory usage (used more than twice)
const char PIRsensorSwitch::_name[] PROGMEM = "PIRsensorSwitch";
const char PIRsensorSwitch::_enabled[] PROGMEM = "PIRenabled";
const char PIRsensorSwitch::_switchOffDelay[] PROGMEM = "PIRoffSec";
const char PIRsensorSwitch::_onPreset[] PROGMEM = "on-preset";
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::_haDiscovery[] PROGMEM = "HA-discovery";
const char PIRsensorSwitch::_notify[] PROGMEM = "notifications";
const char PIRsensorSwitch::_override[] PROGMEM = "override";
bool PIRsensorSwitch::isDayTime() {
updateLocalTime();
uint8_t hr = hour(localTime);
uint8_t mi = minute(localTime);
@ -106,10 +217,7 @@ private:
return false;
}
/**
* switch strip on/off
*/
void switchStrip(bool switchOn)
void PIRsensorSwitch::switchStrip(bool switchOn)
{
if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return; //if lights on and off only, do nothing
if (PIRtriggered && switchOn) return; //if already on and triggered before, do nothing
@ -158,21 +266,19 @@ private:
}
}
void publishMqtt(const char* state)
void PIRsensorSwitch::publishMqtt(const char* state)
{
#ifndef WLED_DISABLE_MQTT
//Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED) {
char subuf[64];
strcpy(subuf, mqttDeviceTopic);
strcat_P(subuf, PSTR("/motion"));
mqtt->publish(subuf, 0, false, state);
char buf[64];
sprintf_P(buf, PSTR("%s/motion"), mqttDeviceTopic); //max length: 33 + 7 = 40
mqtt->publish(buf, 0, false, state);
}
#endif
}
// Create an MQTT Binary Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop.
void publishHomeAssistantAutodiscovery()
void PIRsensorSwitch::publishHomeAssistantAutodiscovery()
{
#ifndef WLED_DISABLE_MQTT
if (WLED_MQTT_CONNECTED) {
@ -207,11 +313,7 @@ private:
#endif
}
/**
* Read and update PIR sensor state.
* Initilize/reset switch off timer
*/
bool updatePIRsensorState()
bool PIRsensorSwitch::updatePIRsensorState()
{
bool pinState = digitalRead(PIRsensorPin);
if (pinState != sensorPinState) {
@ -232,10 +334,7 @@ private:
return false;
}
/**
* switch off the strip if the delay has elapsed
*/
bool handleOffTimer()
bool PIRsensorSwitch::handleOffTimer()
{
if (offTimerStart > 0 && millis() - offTimerStart > m_switchOffDelay) {
offTimerStart = 0;
@ -249,14 +348,9 @@ private:
return false;
}
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()
void PIRsensorSwitch::setup()
{
if (enabled) {
// pin retrieved from cfg.json (readFromConfig()) prior to running setup()
@ -275,27 +369,14 @@ public:
initDone = true;
}
/**
* connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces
*/
void connected()
void PIRsensorSwitch::onMqttConnect(bool sessionPresent)
{
}
/**
* onMqttConnect() is called when MQTT connection is established
*/
void onMqttConnect(bool sessionPresent) {
if (HomeAssistantDiscovery) {
publishHomeAssistantAutodiscovery();
}
}
/**
* loop() is called continuously. Here you can check for events, read sensors, etc.
*/
void loop()
void PIRsensorSwitch::loop()
{
// only check sensors 4x/s
if (!enabled || millis() - lastLoop < 250 || strip.isUpdating()) return;
@ -306,12 +387,7 @@ public:
}
}
/**
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
*
* Add PIR sensor state and switch off timer duration to jsoninfo
*/
void addToJsonInfo(JsonObject &root)
void PIRsensorSwitch::addToJsonInfo(JsonObject &root)
{
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
@ -371,13 +447,10 @@ public:
sensor[F("motion")] = sensorPinState || offTimerStart>0 ? true : false;
}
/**
* onStateChanged() is used to detect WLED state change
*/
void onStateChange(uint8_t mode) {
void PIRsensorSwitch::onStateChange(uint8_t mode) {
if (!initDone) return;
DEBUG_PRINT(F("PIR: offTimerStart=")); DEBUG_PRINTLN(offTimerStart);
if (PIRtriggered && offTimerStart) {
if (m_override && PIRtriggered && offTimerStart) { // debounce
// checking PIRtriggered and offTimerStart will prevent cancellation upon On trigger
DEBUG_PRINTLN(F("PIR: Canceled."));
offTimerStart = 0;
@ -385,22 +458,7 @@ public:
}
}
/**
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
/*
void addToJsonState(JsonObject &root)
{
}
*/
/**
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
void readFromJsonState(JsonObject &root)
void PIRsensorSwitch::readFromJsonState(JsonObject &root)
{
if (!initDone) return; // prevent crash on boot applyPreset()
JsonObject usermod = root[FPSTR(_name)];
@ -411,11 +469,7 @@ public:
}
}
/**
* provide the changeable values
*/
void addToConfig(JsonObject &root)
void PIRsensorSwitch::addToConfig(JsonObject &root)
{
JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled;
@ -426,24 +480,20 @@ public:
top[FPSTR(_nightTime)] = m_nightTimeOnly;
top[FPSTR(_mqttOnly)] = m_mqttOnly;
top[FPSTR(_offOnly)] = m_offOnly;
top[FPSTR(_override)] = m_override;
top[FPSTR(_haDiscovery)] = HomeAssistantDiscovery;
top[FPSTR(_notify)] = (NotifyUpdateMode != CALL_MODE_NO_NOTIFY);
DEBUG_PRINTLN(F("PIR config saved."));
}
void appendConfigData()
void PIRsensorSwitch::appendConfigData()
{
oappend(SET_F("addInfo('PIRsensorSwitch:HA-discovery',1,'HA=Home Assistant');")); // 0 is field type, 1 is actual field
oappend(SET_F("addInfo('PIRsensorSwitch:notifications',1,'Periodic WS updates');")); // 0 is field type, 1 is actual field
oappend(SET_F("addInfo('PIRsensorSwitch:override',1,'Cancel timer on change');")); // 0 is field type, 1 is actual field
}
/**
* restore the changeable values
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
*
* The function should return true if configuration was successfully loaded or false if there was no configuration.
*/
bool readFromConfig(JsonObject &root)
bool PIRsensorSwitch::readFromConfig(JsonObject &root)
{
bool oldEnabled = enabled;
int8_t oldPin = PIRsensorPin;
@ -469,6 +519,7 @@ public:
m_nightTimeOnly = top[FPSTR(_nightTime)] | m_nightTimeOnly;
m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly;
m_offOnly = top[FPSTR(_offOnly)] | m_offOnly;
m_override = top[FPSTR(_override)] | m_override;
HomeAssistantDiscovery = top[FPSTR(_haDiscovery)] | HomeAssistantDiscovery;
NotifyUpdateMode = top[FPSTR(_notify)] ? CALL_MODE_DIRECT_CHANGE : CALL_MODE_NO_NOTIFY;
@ -498,27 +549,5 @@ public:
DEBUG_PRINTLN(F(" config (re)loaded."));
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_haDiscovery)].isNull();
return !top[FPSTR(_override)].isNull();
}
/**
* 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_PIRSWITCH;
}
};
// strings to reduce flash memory usage (used more than twice)
const char PIRsensorSwitch::_name[] PROGMEM = "PIRsensorSwitch";
const char PIRsensorSwitch::_enabled[] PROGMEM = "PIRenabled";
const char PIRsensorSwitch::_switchOffDelay[] PROGMEM = "PIRoffSec";
const char PIRsensorSwitch::_onPreset[] PROGMEM = "on-preset";
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::_haDiscovery[] PROGMEM = "HA-discovery";
const char PIRsensorSwitch::_notify[] PROGMEM = "notifications";

View File

@ -12,8 +12,7 @@ class RTCUsermod : public Usermod {
public:
void setup() {
PinManagerPinType pins[2] = { { i2c_scl, true }, { i2c_sda, true } };
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { disabled = true; return; }
if (i2c_scl<0 || i2c_sda<0) { disabled = true; return; }
RTC.begin();
time_t rtcTime = RTC.get();
if (rtcTime) {
@ -25,8 +24,8 @@ class RTCUsermod : public Usermod {
}
void loop() {
if (strip.isUpdating()) return;
if (!disabled && toki.isTick()) {
if (disabled || strip.isUpdating()) return;
if (toki.isTick()) {
time_t t = toki.second();
if (t != RTC.get()) RTC.set(t); //set RTC to NTP/UI-provided value
}

View File

@ -17,12 +17,6 @@
#ifndef TFT_HEIGHT
#error Please define TFT_HEIGHT
#endif
#ifndef TFT_MOSI
#error Please define TFT_MOSI
#endif
#ifndef TFT_SCLK
#error Please define TFT_SCLK
#endif
#ifndef TFT_DC
#error Please define TFT_DC
#endif
@ -140,8 +134,14 @@ class St7789DisplayUsermod : public Usermod {
*/
void setup()
{
PinManagerPinType pins[] = { { TFT_MOSI, true }, { TFT_MISO, false}, { TFT_SCLK, true }, { TFT_CS, true}, { TFT_DC, true}, { TFT_RST, true }, { TFT_BL, true } };
if (!pinManager.allocateMultiplePins(pins, 7, PinOwner::UM_FourLineDisplay)) { enabled = false; return; }
PinManagerPinType spiPins[] = { { spi_mosi, true }, { spi_miso, false}, { spi_sclk, true } };
if (!pinManager.allocateMultiplePins(spiPins, 3, PinOwner::HW_SPI)) { enabled = false; return; }
PinManagerPinType displayPins[] = { { TFT_CS, true}, { TFT_DC, true}, { TFT_RST, true }, { TFT_BL, true } };
if (!pinManager.allocateMultiplePins(displayPins, sizeof(displayPins)/sizeof(PinManagerPinType), PinOwner::UM_FourLineDisplay)) {
pinManager.deallocateMultiplePins(spiPins, 3, PinOwner::HW_SPI);
enabled = false;
return;
}
tft.init();
tft.setRotation(0); //Rotation here is set up for the text to be readable with the port on the left. Use 1 to flip.
@ -365,9 +365,6 @@ class St7789DisplayUsermod : public Usermod {
{
JsonObject top = root.createNestedObject("ST7789");
JsonArray pins = top.createNestedArray("pin");
pins.add(TFT_MOSI);
pins.add(TFT_MISO);
pins.add(TFT_SCLK);
pins.add(TFT_CS);
pins.add(TFT_DC);
pins.add(TFT_RST);
@ -376,6 +373,13 @@ class St7789DisplayUsermod : public Usermod {
}
void appendConfigData() {
oappend(SET_F("addInfo('ST7789:pin[]',0,'','SPI CS');"));
oappend(SET_F("addInfo('ST7789:pin[]',1,'','SPI DC');"));
oappend(SET_F("addInfo('ST7789:pin[]',2,'','SPI RST');"));
oappend(SET_F("addInfo('ST7789:pin[]',2,'','SPI BL');"));
}
/*
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
* This is called by WLED when settings are loaded (currently this only happens once immediately after boot)

View File

@ -13,14 +13,6 @@
Adafruit_Si7021 si7021;
#ifdef ARDUINO_ARCH_ESP32 //ESP32 boards
uint8_t SCL_PIN = 22;
uint8_t SDA_PIN = 21;
#else //ESP8266 boards
uint8_t SCL_PIN = 5;
uint8_t SDA_PIN = 4;
#endif
class Si7021_MQTT_HA : public Usermod
{
private:
@ -184,7 +176,6 @@ class Si7021_MQTT_HA : public Usermod
{
if (enabled) {
Serial.println("Si7021_MQTT_HA: Starting!");
Wire.begin(SDA_PIN, SCL_PIN);
Serial.println("Si7021_MQTT_HA: Initializing sensors.. ");
_initializeSensor();
}

View File

@ -1,13 +1,12 @@
; Options
; -------
; USERMOD_DALLASTEMPERATURE - define this to have this user mod included wled00\usermods_list.cpp
; USERMOD_DALLASTEMPERATURE_CELSIUS - define this to report temperatures in degrees celsius, otherwise fahrenheit will be reported
; USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL - the number of milliseconds between measurements, defaults to 60 seconds
; USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT - the number of milliseconds after boot to take first measurement, defaults to 20 seconds
;
[env:d1_mini_usermod_dallas_temperature_C]
extends = env:d1_mini
build_flags = ${common.build_flags_esp8266} -D USERMOD_DALLASTEMPERATURE -D USERMOD_DALLASTEMPERATURE_CELSIUS
build_flags = ${common.build_flags_esp8266} -D USERMOD_DALLASTEMPERATURE
lib_deps = ${env.lib_deps}
milesburton/DallasTemperature@^3.9.0
OneWire@~2.3.5
paulstoffregen/OneWire@~2.3.7
# you may want to use following with ESP32
; https://github.com/blazoncek/OneWire.git # fixes Sensor error on ESP32

View File

@ -7,6 +7,8 @@ May be expanded with support for different sensor types in the future.
If temperature sensor is not detected during boot, this usermod will be disabled.
Maintained by @blazoncek
## Installation
Copy the example `platformio_override.ini` to the root directory. This file should be placed in the same directory as `platformio.ini`.
@ -14,7 +16,7 @@ Copy the example `platformio_override.ini` to the root directory. This file sho
### Define Your Options
* `USERMOD_DALLASTEMPERATURE` - enables this user mod wled00/usermods_list.cpp
* `USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT` - number of milliseconds after boot to take first measurement, defaults to 20000 ms
* `USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL` - number of milliseconds between measurements, defaults to 60000 ms (60s)
All parameters can be configured at runtime via the Usermods settings page, including pin, temperature in degrees Celsius or Farenheit and measurement interval.
@ -27,7 +29,6 @@ All parameters can be configured at runtime via the Usermods settings page, incl
If you are using `platformio_override.ini`, you should be able to refresh the task list and see your custom task, for example `env:d1_mini_usermod_dallas_temperature_C`.
If you are not using `platformio_override.ini`, you might have to uncomment `OneWire@~2.3.5 under` `[common]` section in `platformio.ini`:
```ini
@ -43,8 +44,9 @@ default_envs = d1_mini
lib_deps =
...
#For Dallas sensor uncomment following line
OneWire@~2.3.5
...
OneWire@~2.3.7
# ... or you may want to use following with ESP32
; https://github.com/blazoncek/OneWire.git # fixes Sensor error on ESP32...
```
## Change Log
@ -56,3 +58,6 @@ lib_deps =
* Report the number of seconds until the first read in the info screen instead of sensor error
2021-04
* Adaptation for runtime configuration.
2023-05
* Rewrite to conform to newer recommendations.
* Recommended @blazoncek fork of OneWire for ESP32 to avoid Sensor error

View File

@ -57,7 +57,47 @@ class UsermodTemperature : public Usermod {
static const char _parasitePin[];
//Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013
float readDallas() {
float readDallas();
void requestTemperatures();
void readTemperature();
bool findSensor();
#ifndef WLED_DISABLE_MQTT
void publishHomeAssistantAutodiscovery();
#endif
public:
/*
* API calls te enable data exchange between WLED modules
*/
inline float getTemperatureC() { return temperature; }
inline float getTemperatureF() { return temperature * 1.8f + 32.0f; }
float getTemperature();
const char *getTemperatureUnit();
uint16_t getId() { return USERMOD_ID_TEMPERATURE; }
void setup();
void loop();
//void connected();
#ifndef WLED_DISABLE_MQTT
void onMqttConnect(bool sessionPresent);
#endif
//void onUpdateBegin(bool init);
//bool handleButton(uint8_t b);
//void handleOverlayDraw();
void addToJsonInfo(JsonObject& root);
//void addToJsonState(JsonObject &root);
//void readFromJsonState(JsonObject &root);
void addToConfig(JsonObject &root);
bool readFromConfig(JsonObject &root);
void appendConfigData();
};
//Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013
float UsermodTemperature::readDallas() {
byte data[9];
int16_t result; // raw data from sensor
float retVal = -127.0f;
@ -92,7 +132,7 @@ class UsermodTemperature : public Usermod {
return data[0]==0xFF ? -127.0f : retVal;
}
void requestTemperatures() {
void UsermodTemperature::requestTemperatures() {
DEBUG_PRINTLN(F("Requesting temperature."));
oneWire->reset();
oneWire->skip(); // skip ROM
@ -102,7 +142,7 @@ class UsermodTemperature : public Usermod {
waitingForConversion = true;
}
void readTemperature() {
void UsermodTemperature::readTemperature() {
if (parasite && parasitePin >=0 ) digitalWrite(parasitePin, LOW); // deactivate power (close MOSFET)
temperature = readDallas();
lastMeasurement = millis();
@ -112,7 +152,7 @@ class UsermodTemperature : public Usermod {
DEBUG_PRINTLN(temperature);
}
bool findSensor() {
bool UsermodTemperature::findSensor() {
DEBUG_PRINTLN(F("Searching for sensor..."));
uint8_t deviceAddress[8] = {0,0,0,0,0,0,0,0};
// find out if we have DS18xxx sensor attached
@ -139,7 +179,7 @@ class UsermodTemperature : public Usermod {
}
#ifndef WLED_DISABLE_MQTT
void publishHomeAssistantAutodiscovery() {
void UsermodTemperature::publishHomeAssistantAutodiscovery() {
if (!WLED_MQTT_CONNECTED) return;
char json_str[1024], buf[128];
@ -162,9 +202,7 @@ class UsermodTemperature : public Usermod {
}
#endif
public:
void setup() {
void UsermodTemperature::setup() {
int retries = 10;
sensorFound = 0;
temperature = -127.0f; // default to -127, DS18B20 only goes down to -50C
@ -196,7 +234,7 @@ class UsermodTemperature : public Usermod {
initDone = true;
}
void loop() {
void UsermodTemperature::loop() {
if (!enabled || !sensorFound || strip.isUpdating()) return;
static uint8_t errorCount = 0;
@ -248,13 +286,13 @@ class UsermodTemperature : public Usermod {
* connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces
*/
//void connected() {}
//void UsermodTemperature::connected() {}
#ifndef WLED_DISABLE_MQTT
/**
* subscribe to MQTT topic if needed
*/
void onMqttConnect(bool sessionPresent) {
void UsermodTemperature::onMqttConnect(bool sessionPresent) {
//(re)subscribe to required topics
//char subuf[64];
if (mqttDeviceTopic[0] != 0) {
@ -263,22 +301,12 @@ class UsermodTemperature : public Usermod {
}
#endif
/*
* API calls te enable data exchange between WLED modules
*/
inline float getTemperatureC() {
return (float)temperature;
}
inline float getTemperatureF() {
return (float)temperature * 1.8f + 32;
}
/*
* 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) {
void UsermodTemperature::addToJsonInfo(JsonObject& root) {
// dont add temperature to info if we are disabled
if (!enabled) return;
@ -293,21 +321,21 @@ class UsermodTemperature : public Usermod {
return;
}
temp.add(degC ? getTemperatureC() : getTemperatureF());
temp.add(degC ? F("°C") : F("°F"));
temp.add(getTemperature());
temp.add(getTemperatureUnit());
JsonObject sensor = root[F("sensor")];
if (sensor.isNull()) sensor = root.createNestedObject(F("sensor"));
temp = sensor.createNestedArray(F("temp"));
temp.add(degC ? temperature : (float)temperature * 1.8f + 32);
temp.add(degC ? F("°C") : F("°F"));
temp = sensor.createNestedArray(F("temperature"));
temp.add(getTemperature());
temp.add(getTemperatureUnit());
}
/**
* 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)
//void UsermodTemperature::addToJsonState(JsonObject &root)
//{
//}
@ -316,14 +344,14 @@ class UsermodTemperature : public Usermod {
* Values in the state object may be modified by connected clients
* Read "<usermodname>_<usermodparam>" from json state and and change settings (i.e. GPIO pin) used.
*/
//void readFromJsonState(JsonObject &root) {
//void UsermodTemperature::readFromJsonState(JsonObject &root) {
// if (!initDone) return; // prevent crash on boot applyPreset()
//}
/**
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
*/
void addToConfig(JsonObject &root) {
void UsermodTemperature::addToConfig(JsonObject &root) {
// we add JSON object: {"Temperature": {"pin": 0, "degC": true}}
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
top[FPSTR(_enabled)] = enabled;
@ -340,7 +368,7 @@ class UsermodTemperature : public Usermod {
*
* The function should return true if configuration was successfully loaded or false if there was no configuration.
*/
bool readFromConfig(JsonObject &root) {
bool UsermodTemperature::readFromConfig(JsonObject &root) {
// we look for JSON object: {"Temperature": {"pin": 0, "degC": true}}
int8_t newTemperaturePin = temperaturePin;
DEBUG_PRINT(FPSTR(_name));
@ -381,19 +409,20 @@ class UsermodTemperature : public Usermod {
return !top[FPSTR(_parasitePin)].isNull();
}
void appendConfigData()
{
void UsermodTemperature::appendConfigData() {
oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":")); oappend(String(FPSTR(_parasite)).c_str());
oappend(SET_F("',1,'<i>(if no Vcc connected)</i>');")); // 0 is field type, 1 is actual field
oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":")); oappend(String(FPSTR(_parasitePin)).c_str());
oappend(SET_F("',1,'<i>(for external MOSFET)</i>');")); // 0 is field type, 1 is actual field
}
uint16_t getId()
{
return USERMOD_ID_TEMPERATURE;
float UsermodTemperature::getTemperature() {
return degC ? getTemperatureC() : getTemperatureF();
}
const char *UsermodTemperature::getTemperatureUnit() {
return degC ? "°C" : "°F";
}
};
// strings to reduce flash memory usage (used more than twice)
const char UsermodTemperature::_name[] PROGMEM = "Temperature";

View File

@ -1,31 +0,0 @@
#include "wled.h"
/*
* Register your v2 usermods here!
*/
/*
* Add/uncomment your usermod filename here (and once more below)
* || || ||
* \/ \/ \/
*/
//#include "usermod_v2_example.h"
#ifdef USERMOD_DALLASTEMPERATURE
#include "../usermods/Temperature/usermod_temperature.h"
#endif
//#include "usermod_v2_empty.h"
void registerUsermods()
{
/*
* Add your usermod class name here
* || || ||
* \/ \/ \/
*/
//usermods.add(new MyExampleUsermod());
#ifdef USERMOD_DALLASTEMPERATURE
usermods.add(new UsermodTemperature());
#endif
//usermods.add(new UsermodRenameMe());
}

View File

@ -50,9 +50,7 @@ class UsermodVL53L0XGestures : public Usermod {
public:
void setup() {
PinManagerPinType pins[2] = { { i2c_scl, true }, { i2c_sda, true } };
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; }
Wire.begin();
if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
sensor.setTimeout(150);
if (!sensor.init())

View File

@ -569,16 +569,6 @@ class AudioReactive : public Usermod {
#else
int8_t i2sckPin = I2S_CKPIN;
#endif
#ifndef ES7243_SDAPIN
int8_t sdaPin = -1;
#else
int8_t sdaPin = ES7243_SDAPIN;
#endif
#ifndef ES7243_SCLPIN
int8_t sclPin = -1;
#else
int8_t sclPin = ES7243_SCLPIN;
#endif
#ifndef MCLK_PIN
int8_t mclkPin = I2S_PIN_NO_CHANGE; /* ESP32: only -1, 0, 1, 3 allowed*/
#else
@ -1136,7 +1126,7 @@ class AudioReactive : public Usermod {
DEBUGSR_PRINTLN(F("AR: ES7243 Microphone (right channel only)."));
audioSource = new ES7243(SAMPLE_RATE, BLOCK_SIZE);
delay(100);
if (audioSource) audioSource->initialize(sdaPin, sclPin, i2swsPin, i2ssdPin, i2sckPin, mclkPin);
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
break;
case 3:
DEBUGSR_PRINT(F("AR: SPH0645 Microphone - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT));
@ -1657,8 +1647,6 @@ class AudioReactive : public Usermod {
pinArray.add(i2swsPin);
pinArray.add(i2sckPin);
pinArray.add(mclkPin);
pinArray.add(sdaPin);
pinArray.add(sclPin);
JsonObject cfg = top.createNestedObject("config");
cfg[F("squelch")] = soundSquelch;
@ -1719,8 +1707,6 @@ class AudioReactive : public Usermod {
configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][1], i2swsPin);
configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][2], i2sckPin);
configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][3], mclkPin);
configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][4], sdaPin);
configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][5], sclPin);
configComplete &= getJsonValue(top["config"][F("squelch")], soundSquelch);
configComplete &= getJsonValue(top["config"][F("gain")], sampleGain);
@ -1784,8 +1770,6 @@ class AudioReactive : public Usermod {
#else
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',3,'<i>master clock</i>','I2S MCLK');"));
#endif
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',4,'','I2C SDA');"));
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',5,'','I2C SCL');"));
}

View File

@ -1,6 +1,5 @@
#pragma once
#include <Wire.h>
#include "wled.h"
#include <driver/i2s.h>
#include <driver/adc.h>
@ -383,21 +382,12 @@ class I2SSource : public AudioSource {
*/
class ES7243 : public I2SSource {
private:
// I2C initialization functions for ES7243
void _es7243I2cBegin() {
bool i2c_initialized = Wire.begin(pin_ES7243_SDA, pin_ES7243_SCL, 100000U);
if (i2c_initialized == false) {
DEBUGSR_PRINTLN(F("AR: ES7243 failed to initialize I2C bus driver."));
}
}
void _es7243I2cWrite(uint8_t reg, uint8_t val) {
#ifndef ES7243_ADDR
Wire.beginTransmission(0x13);
#define ES7243_ADDR 0x13 // default address
#else
Wire.beginTransmission(ES7243_ADDR);
#endif
Wire.beginTransmission(ES7243_ADDR);
Wire.write((uint8_t)reg);
Wire.write((uint8_t)val);
uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK
@ -407,7 +397,6 @@ class ES7243 : public I2SSource {
}
void _es7243InitAdc() {
_es7243I2cBegin();
_es7243I2cWrite(0x00, 0x01);
_es7243I2cWrite(0x06, 0x00);
_es7243I2cWrite(0x05, 0x1B);
@ -422,44 +411,20 @@ public:
_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT;
};
void initialize(int8_t sdaPin, int8_t sclPin, int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
// check that pins are valid
if ((sdaPin < 0) || (sclPin < 0)) {
DEBUGSR_PRINTF("\nAR: invalid ES7243 I2C pins: SDA=%d, SCL=%d\n", sdaPin, sclPin);
return;
}
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
if ((i2sckPin < 0) || (mclkPin < 0)) {
DEBUGSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
return;
}
// Reserve SDA and SCL pins of the I2C interface
PinManagerPinType es7243Pins[2] = { { sdaPin, true }, { sclPin, true } };
if (!pinManager.allocateMultiplePins(es7243Pins, 2, PinOwner::HW_I2C)) {
pinManager.deallocateMultiplePins(es7243Pins, 2, PinOwner::HW_I2C);
DEBUGSR_PRINTF("\nAR: Failed to allocate ES7243 I2C pins: SDA=%d, SCL=%d\n", sdaPin, sclPin);
return;
}
pin_ES7243_SDA = sdaPin;
pin_ES7243_SCL = sclPin;
// First route mclk, then configure ADC over I2C, then configure I2S
_es7243InitAdc();
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
}
void deinitialize() {
// Release SDA and SCL pins of the I2C interface
PinManagerPinType es7243Pins[2] = { { pin_ES7243_SDA, true }, { pin_ES7243_SCL, true } };
pinManager.deallocateMultiplePins(es7243Pins, 2, PinOwner::HW_I2C);
I2SSource::deinitialize();
}
private:
int8_t pin_ES7243_SDA;
int8_t pin_ES7243_SCL;
};

View File

@ -85,12 +85,9 @@ class MPU6050Driver : public Usermod {
* setup() is called once at boot. WiFi is not yet connected at this point.
*/
void setup() {
PinManagerPinType pins[2] = { { i2c_scl, true }, { i2c_sda, true } };
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; }
// join I2C bus (I2Cdev library doesn't do this automatically)
if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
Wire.setClock(400000U); // 400kHz I2C clock. Comment this line if having compilation difficulties
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif

View File

@ -1,6 +1,9 @@
# Multi Relay
This usermod-v2 modification allows the connection of multiple relays, each with individual delay and on/off mode.
Usermod supports PCF8574 I2C port expander to reduce GPIO use.
PCF8574 supports 8 outputs and each output corresponds to a relay in WLED (relay 0 = port 0, etc). I you are using more than 8 relays with multiple PCF8574 make sure their addresses are set conscutively (e.g. 0x20 and 0x21). You can set address of first expander in settings.
(**NOTE:** Will require Wire library and global I2C pins defined.)
## HTTP API
All responses are returned in JSON format.
@ -81,13 +84,15 @@ void registerUsermods()
Usermod can be configured via the Usermods settings page.
* `enabled` - enable/disable usermod
* `use-PCF8574` - use PCF8574 port expander instead of GPIO pins
* `first-PCF8574` - I2C address of first expander (WARNING: enter *decimal* value)
* `broadcast`- time in seconds between MQTT relay-state broadcasts
* `HA-discovery`- enable Home Assistant auto discovery
* `pin` - ESP GPIO pin the relay is connected to (can be configured at compile time `-D MULTI_RELAY_PINS=xx,xx,...`)
* `delay-s` - delay in seconds after on/off command is received
* `active-high` - assign 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 an external command (MQTT, HTTP, JSON or button)
* `button` - button (from LED Settings) that controls this relay
* `broadcast`- time in seconds between MQTT relay-state broadcasts
* `HA-discovery`- enable Home Assistant auto discovery
If there is no MultiRelay section, just save current configuration and re-open Usermods settings page.
@ -100,3 +105,6 @@ Have fun - @blazoncek
2021-11
* Added information about dynamic configuration options
* Added button support.
2023-05
* Added support for PCF8574 I2C port expander (multiple)

View File

@ -4,10 +4,19 @@
#ifndef MULTI_RELAY_MAX_RELAYS
#define MULTI_RELAY_MAX_RELAYS 4
#else
#if MULTI_RELAY_MAX_RELAYS>8
#undef MULTI_RELAY_MAX_RELAYS
#define MULTI_RELAY_MAX_RELAYS 8
#warning Maximum relays set to 8
#endif
#endif
#ifndef MULTI_RELAY_PINS
#define MULTI_RELAY_PINS -1
#define MULTI_RELAY_ENABLED false
#else
#define MULTI_RELAY_ENABLED true
#endif
#define WLED_DEBOUNCE_THRESHOLD 50 //only consider button input of at least 50ms as valid (debouncing)
@ -15,21 +24,37 @@
#define ON true
#define OFF false
#ifndef USERMOD_USE_PCF8574
#undef USE_PCF8574
#define USE_PCF8574 false
#else
#undef USE_PCF8574
#define USE_PCF8574 true
#endif
#ifndef PCF8574_ADDRESS
#define PCF8574_ADDRESS 0x20 // some may start at 0x38
#endif
/*
* This usermod handles multiple relay outputs.
* These outputs complement built-in relay output in a way that the activation can be delayed.
* They can also activate/deactivate in reverse logic independently.
*
* Written and maintained by @blazoncek
*/
typedef struct relay_t {
int8_t pin;
bool active;
bool mode;
bool state;
bool external;
uint16_t delay;
int8_t button;
struct { // reduces memory footprint
bool active : 1; // is the relay waiting to be switched
bool invert : 1; // does On mean 1 or 0
bool state : 1; // 1 relay is On, 0 relay is Off
bool external : 1; // is the relay externally controlled
int8_t button : 4; // which button triggers relay
};
uint16_t delay; // amount of ms to wait after it is activated
} Relay;
@ -39,20 +64,15 @@ class MultiRelay : public Usermod {
// array of relays
Relay _relay[MULTI_RELAY_MAX_RELAYS];
// switch timer start time
uint32_t _switchTimerStart = 0;
// old brightness
bool _oldMode;
// usermod enabled
bool enabled = false; // needs to be configured (no default config)
// status of initialisation
bool initDone = false;
bool HAautodiscovery = false;
uint16_t periodicBroadcastSec = 60;
unsigned long lastBroadcast = 0;
uint32_t _switchTimerStart; // switch timer start time
bool _oldMode; // old brightness
bool enabled; // usermod enabled
bool initDone; // status of initialisation
bool usePcf8574;
uint8_t addrPcf8574;
bool HAautodiscovery;
uint16_t periodicBroadcastSec;
unsigned long lastBroadcast;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
@ -64,8 +84,127 @@ class MultiRelay : public Usermod {
static const char _button[];
static const char _broadcast[];
static const char _HAautodiscovery[];
static const char _pcf8574[];
static const char _pcfAddress[];
void publishMqtt(int relay) {
void handleOffTimer();
void InitHtmlAPIHandle();
int getValue(String data, char separator, int index);
uint8_t getActiveRelayCount();
byte IOexpanderWrite(byte address, byte _data);
byte IOexpanderRead(int address);
void publishMqtt(int relay);
#ifndef WLED_DISABLE_MQTT
void publishHomeAssistantAutodiscovery();
#endif
public:
/**
* constructor
*/
MultiRelay();
/**
* desctructor
*/
//~MultiRelay() {}
/**
* Enable/Disable the usermod
*/
inline void enable(bool enable) { enabled = enable; }
/**
* Get usermod enabled/disabled state
*/
inline bool isEnabled() { return enabled; }
/**
* 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.
*/
inline uint16_t getId() { return USERMOD_ID_MULTI_RELAY; }
/**
* switch relay on/off
*/
void switchRelay(uint8_t relay, bool mode);
/**
* toggle relay
*/
inline void toggleRelay(uint8_t relay) {
switchRelay(relay, !_relay[relay].state);
}
/**
* setup() is called once at boot. WiFi is not yet connected at this point.
* You can use it to initialize variables, sensors or similar.
*/
void setup();
/**
* connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces
*/
inline void connected() { InitHtmlAPIHandle(); }
/**
* loop() is called continuously. Here you can check for events, read sensors, etc.
*/
void loop();
#ifndef WLED_DISABLE_MQTT
bool onMqttMessage(char* topic, char* payload);
void onMqttConnect(bool sessionPresent);
#endif
/**
* handleButton() can be used to override default button behaviour. Returning true
* will prevent button working in a default way.
* Replicating button.cpp
*/
bool handleButton(uint8_t b);
/**
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
*/
void addToJsonInfo(JsonObject &root);
/**
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
void addToJsonState(JsonObject &root);
/**
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
void readFromJsonState(JsonObject &root);
/**
* provide the changeable values
*/
void addToConfig(JsonObject &root);
void appendConfigData();
/**
* restore the changeable values
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
*
* The function should return true if configuration was successfully loaded or false if there was no configuration.
*/
bool readFromConfig(JsonObject &root);
};
// class implementetion
void MultiRelay::publishMqtt(int relay) {
#ifndef WLED_DISABLE_MQTT
//Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED){
@ -79,12 +218,12 @@ class MultiRelay : public Usermod {
/**
* switch off the strip if the delay has elapsed
*/
void handleOffTimer() {
void MultiRelay::handleOffTimer() {
unsigned long now = millis();
bool activeRelays = false;
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].active && _switchTimerStart > 0 && now - _switchTimerStart > (_relay[i].delay*1000)) {
if (!_relay[i].external) toggleRelay(i);
if (!_relay[i].external) switchRelay(i, !offMode);
_relay[i].active = false;
} else if (periodicBroadcastSec && now - lastBroadcast > (periodicBroadcastSec*1000)) {
if (_relay[i].pin>=0) publishMqtt(i);
@ -101,7 +240,7 @@ class MultiRelay : public Usermod {
* https://github.com/gsieben/WLED/blob/master/usermods/GeoGab-Relays/usermod_GeoGab.h
*/
#define GEOGABVERSION "0.1.3"
void InitHtmlAPIHandle() { // https://github.com/me-no-dev/ESPAsyncWebServer
void MultiRelay::InitHtmlAPIHandle() { // https://github.com/me-no-dev/ESPAsyncWebServer
DEBUG_PRINTLN(F("Relays: Initialize HTML API"));
server.on("/relays", HTTP_GET, [this](AsyncWebServerRequest *request) {
@ -163,7 +302,7 @@ class MultiRelay : public Usermod {
});
}
int getValue(String data, char separator, int index) {
int MultiRelay::getValue(String data, char separator, int index) {
int found = 0;
int strIndex[] = {0, -1};
int maxIndex = data.length()-1;
@ -178,60 +317,78 @@ class MultiRelay : public Usermod {
return found>index ? data.substring(strIndex[0], strIndex[1]).toInt() : -1;
}
public:
/**
* constructor
*/
MultiRelay() {
//Write a byte to the IO expander
byte MultiRelay::IOexpanderWrite(byte address, byte _data ) {
Wire.beginTransmission(address);
Wire.write(_data);
return Wire.endTransmission();
}
//Read a byte from the IO expander
byte MultiRelay::IOexpanderRead(int address) {
byte _data = 0;
Wire.requestFrom(address, 1);
if (Wire.available()) {
_data = Wire.read();
}
return _data;
}
// public methods
MultiRelay::MultiRelay()
: _switchTimerStart(0)
, enabled(MULTI_RELAY_ENABLED)
, initDone(false)
, usePcf8574(USE_PCF8574)
, addrPcf8574(PCF8574_ADDRESS)
, HAautodiscovery(false)
, periodicBroadcastSec(60)
, lastBroadcast(0)
{
const int8_t defPins[] = {MULTI_RELAY_PINS};
for (size_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
_relay[i].pin = i<sizeof(defPins) ? defPins[i] : -1;
_relay[i].delay = 0;
_relay[i].mode = false;
_relay[i].invert = false;
_relay[i].active = false;
_relay[i].state = false;
_relay[i].external = false;
_relay[i].button = -1;
}
}
/**
* desctructor
*/
~MultiRelay() {}
/**
* Enable/Disable the usermod
*/
inline void enable(bool enable) { enabled = enable; }
/**
* Get usermod enabled/disabled state
*/
inline bool isEnabled() { return enabled; }
/**
* switch relay on/off
*/
void switchRelay(uint8_t relay, bool mode) {
void MultiRelay::switchRelay(uint8_t relay, bool mode) {
if (relay>=MULTI_RELAY_MAX_RELAYS || _relay[relay].pin<0) return;
_relay[relay].state = mode;
if (usePcf8574 && _relay[relay].pin >= 100) {
// we need to send all ouputs at the same time
uint8_t state = 0;
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].pin < 100) continue;
uint8_t pin = _relay[i].pin - 100;
state |= (_relay[i].invert ? !_relay[i].state : _relay[i].state) << pin; // fill relay states for all pins
}
IOexpanderWrite(addrPcf8574, state);
DEBUG_PRINT(F("Writing to PCF8574: ")); DEBUG_PRINTLN(state);
} else if (_relay[relay].pin < 100) {
pinMode(_relay[relay].pin, OUTPUT);
digitalWrite(_relay[relay].pin, mode ? !_relay[relay].mode : _relay[relay].mode);
digitalWrite(_relay[relay].pin, _relay[relay].invert ? !_relay[relay].state : _relay[relay].state);
} else return;
publishMqtt(relay);
}
/**
* toggle relay
*/
inline void toggleRelay(uint8_t relay) {
switchRelay(relay, !_relay[relay].state);
}
uint8_t getActiveRelayCount() {
uint8_t MultiRelay::getActiveRelayCount() {
uint8_t count = 0;
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) if (_relay[i].pin>=0) count++;
return count;
}
//Functions called by WLED
#ifndef WLED_DISABLE_MQTT
@ -240,7 +397,7 @@ class MultiRelay : public Usermod {
* topic only contains stripped topic (part after /wled/MAC)
* topic should look like: /relay/X/command; where X is relay number, 0 based
*/
bool onMqttMessage(char* topic, char* payload) {
bool MultiRelay::onMqttMessage(char* topic, char* payload) {
if (strlen(topic) > 8 && strncmp_P(topic, PSTR("/relay/"), 7) == 0 && strncmp_P(topic+8, PSTR("/command"), 8) == 0) {
uint8_t relay = strtoul(topic+7, NULL, 10);
if (relay<MULTI_RELAY_MAX_RELAYS) {
@ -263,7 +420,7 @@ class MultiRelay : public Usermod {
/**
* subscribe to MQTT topic for controlling relays
*/
void onMqttConnect(bool sessionPresent) {
void MultiRelay::onMqttConnect(bool sessionPresent) {
//(re)subscribe to required topics
char subuf[64];
if (mqttDeviceTopic[0] != 0) {
@ -278,7 +435,7 @@ class MultiRelay : public Usermod {
}
}
void publishHomeAssistantAutodiscovery() {
void MultiRelay::publishHomeAssistantAutodiscovery() {
for (int i = 0; i < MULTI_RELAY_MAX_RELAYS; i++) {
char uid[24], json_str[1024], buf[128];
size_t payload_size;
@ -322,34 +479,39 @@ class MultiRelay : public Usermod {
* 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() {
void MultiRelay::setup() {
// pins retrieved from cfg.json (readFromConfig()) prior to running setup()
// if we want PCF8574 expander I2C pins need to be valid
if (i2c_sda<0 || i2c_scl<0) usePcf8574 = false;
uint8_t state = 0;
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].pin<0) continue;
if (!pinManager.allocatePin(_relay[i].pin,true, PinOwner::UM_MultiRelay)) {
_relay[i].pin = -1; // allocation failed
} else {
if (usePcf8574 && _relay[i].pin >= 100) {
uint8_t pin = _relay[i].pin - 100;
if (!_relay[i].external) _relay[i].state = !offMode;
state |= (uint8_t)(_relay[i].invert ? !_relay[i].state : _relay[i].state) << pin;
} else if (_relay[i].pin<100 && _relay[i].pin>=0) {
if (pinManager.allocatePin(_relay[i].pin,true, PinOwner::UM_MultiRelay)) {
if (!_relay[i].external) _relay[i].state = !offMode;
switchRelay(i, _relay[i].state);
_relay[i].active = false;
} else {
_relay[i].pin = -1; // allocation failed
}
}
}
if (usePcf8574) {
IOexpanderWrite(addrPcf8574, state); // init expander (set all outputs)
DEBUG_PRINTLN(F("PCF8574(s) inited."));
}
_oldMode = offMode;
initDone = true;
}
/**
* connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces
*/
void connected() {
InitHtmlAPIHandle();
}
/**
* loop() is called continuously. Here you can check for events, read sensors, etc.
*/
void loop() {
void MultiRelay::loop() {
yield();
if (!enabled || strip.isUpdating()) return;
@ -362,7 +524,7 @@ class MultiRelay : public Usermod {
_oldMode = offMode;
_switchTimerStart = millis();
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].pin>=0 && !_relay[i].external) _relay[i].active = true;
if ((_relay[i].pin>=0) && !_relay[i].external) _relay[i].active = true;
}
}
@ -374,7 +536,7 @@ class MultiRelay : public Usermod {
* will prevent button working in a default way.
* Replicating button.cpp
*/
bool handleButton(uint8_t b) {
bool MultiRelay::handleButton(uint8_t b) {
yield();
if (!enabled
|| buttonType[b] == BTN_TYPE_NONE
@ -466,7 +628,7 @@ class MultiRelay : public Usermod {
/**
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
*/
void addToJsonInfo(JsonObject &root) {
void MultiRelay::addToJsonInfo(JsonObject &root) {
if (enabled) {
JsonObject user = root["u"];
if (user.isNull())
@ -503,7 +665,7 @@ class MultiRelay : public Usermod {
* 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) {
void MultiRelay::addToJsonState(JsonObject &root) {
if (!initDone || !enabled) return; // prevent crash on boot applyPreset()
JsonObject multiRelay = root[FPSTR(_name)];
if (multiRelay.isNull()) {
@ -527,7 +689,7 @@ class MultiRelay : public Usermod {
* 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) {
void MultiRelay::readFromJsonState(JsonObject &root) {
if (!initDone || !enabled) return; // prevent crash on boot applyPreset()
JsonObject usermod = root[FPSTR(_name)];
if (!usermod.isNull()) {
@ -557,31 +719,40 @@ class MultiRelay : public Usermod {
/**
* provide the changeable values
*/
void addToConfig(JsonObject &root) {
void MultiRelay::addToConfig(JsonObject &root) {
JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled;
top[FPSTR(_pcf8574)] = usePcf8574;
top[FPSTR(_pcfAddress)] = addrPcf8574;
top[FPSTR(_broadcast)] = periodicBroadcastSec;
top[FPSTR(_HAautodiscovery)] = HAautodiscovery;
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
String parName = FPSTR(_relay_str); parName += '-'; parName += i;
JsonObject relay = top.createNestedObject(parName);
relay["pin"] = _relay[i].pin;
relay[FPSTR(_activeHigh)] = _relay[i].mode;
relay[FPSTR(_activeHigh)] = _relay[i].invert;
relay[FPSTR(_delay_str)] = _relay[i].delay;
relay[FPSTR(_external)] = _relay[i].external;
relay[FPSTR(_button)] = _relay[i].button;
}
top[FPSTR(_HAautodiscovery)] = HAautodiscovery;
DEBUG_PRINTLN(F("MultiRelay config saved."));
}
void MultiRelay::appendConfigData() {
oappend(SET_F("addInfo('MultiRelay:PCF8574-address',1,'<i>(not hex!)</i>');"));
oappend(SET_F("addInfo('MultiRelay:broadcast-sec',1,'(MQTT message)');"));
//oappend(SET_F("addInfo('MultiRelay:relay-0:pin',1,'(use -1 for PCF8574)');"));
oappend(SET_F("d.extra.push({'MultiRelay':{pin:[['P0',100],['P1',101],['P2',102],['P3',103],['P4',104],['P5',105],['P6',106],['P7',107]]}});"));
}
/**
* restore the changeable values
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
*
* The function should return true if configuration was successfully loaded or false if there was no configuration.
*/
bool readFromConfig(JsonObject &root) {
bool MultiRelay::readFromConfig(JsonObject &root) {
int8_t oldPin[MULTI_RELAY_MAX_RELAYS];
JsonObject top = root[FPSTR(_name)];
@ -591,7 +762,13 @@ class MultiRelay : public Usermod {
return false;
}
//bool configComplete = !top.isNull();
//configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled);
enabled = top[FPSTR(_enabled)] | enabled;
usePcf8574 = top[FPSTR(_pcf8574)] | usePcf8574;
addrPcf8574 = top[FPSTR(_pcfAddress)] | addrPcf8574;
// if I2C is not globally initialised just ignore
if (i2c_sda<0 || i2c_scl<0) usePcf8574 = false;
periodicBroadcastSec = top[FPSTR(_broadcast)] | periodicBroadcastSec;
periodicBroadcastSec = min(900,max(0,(int)periodicBroadcastSec));
HAautodiscovery = top[FPSTR(_HAautodiscovery)] | HAautodiscovery;
@ -600,14 +777,14 @@ class MultiRelay : public Usermod {
String parName = FPSTR(_relay_str); parName += '-'; parName += i;
oldPin[i] = _relay[i].pin;
_relay[i].pin = top[parName]["pin"] | _relay[i].pin;
_relay[i].mode = top[parName][FPSTR(_activeHigh)] | _relay[i].mode;
_relay[i].invert = top[parName][FPSTR(_activeHigh)] | _relay[i].invert;
_relay[i].external = top[parName][FPSTR(_external)] | _relay[i].external;
_relay[i].delay = top[parName][FPSTR(_delay_str)] | _relay[i].delay;
_relay[i].button = top[parName][FPSTR(_button)] | _relay[i].button;
// begin backwards compatibility (beta) remove when 0.13 is released
parName += '-';
_relay[i].pin = top[parName+"pin"] | _relay[i].pin;
_relay[i].mode = top[parName+FPSTR(_activeHigh)] | _relay[i].mode;
_relay[i].invert = top[parName+FPSTR(_activeHigh)] | _relay[i].invert;
_relay[i].external = top[parName+FPSTR(_external)] | _relay[i].external;
_relay[i].delay = top[parName+FPSTR(_delay_str)] | _relay[i].delay;
// end compatibility
@ -621,38 +798,17 @@ class MultiRelay : public Usermod {
} else {
// deallocate all pins 1st
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++)
if (oldPin[i]>=0) {
if (oldPin[i]>=0 && oldPin[i]<100) {
pinManager.deallocatePin(oldPin[i], PinOwner::UM_MultiRelay);
}
// allocate new pins
for (int 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) {
_relay[i].state = !offMode;
switchRelay(i, _relay[i].state);
_oldMode = offMode;
}
} else {
_relay[i].pin = -1;
}
_relay[i].active = false;
}
setup();
DEBUG_PRINTLN(F(" config (re)loaded."));
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_HAautodiscovery)].isNull();
return !top[FPSTR(_pcf8574)].isNull();
}
/**
* 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_MULTI_RELAY;
}
};
// strings to reduce flash memory usage (used more than twice)
const char MultiRelay::_name[] PROGMEM = "MultiRelay";
const char MultiRelay::_enabled[] PROGMEM = "enabled";
@ -663,3 +819,5 @@ 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";
const char MultiRelay::_pcf8574[] PROGMEM = "use-PCF8574";
const char MultiRelay::_pcfAddress[] PROGMEM = "PCF8574-address";

View File

@ -6,7 +6,6 @@
#include "wled.h"
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h>
#include <Adafruit_CCS811.h>
@ -16,14 +15,6 @@ Adafruit_BMP280 bmp;
Adafruit_Si7021 si7021;
Adafruit_CCS811 ccs811;
#ifdef ARDUINO_ARCH_ESP32 //ESP32 boards
uint8_t SCL_PIN = 22;
uint8_t SDA_PIN = 21;
#else //ESP8266 boards
uint8_t SCL_PIN = 5;
uint8_t SDA_PIN = 4;
#endif
class UserMod_SensorsToMQTT : public Usermod
{
private:
@ -231,7 +222,6 @@ public:
void setup()
{
Serial.println("Starting!");
Wire.begin(SDA_PIN, SCL_PIN);
Serial.println("Initializing sensors.. ");
_initialize();
}

View File

@ -16,13 +16,12 @@ class ShtUsermod : public Usermod
private:
bool enabled = false; // Is usermod enabled or not
bool firstRunDone = false; // Remembers if the first config load run had been done
bool pinAllocDone = true; // Remembers if we have allocated pins
bool initDone = false; // Remembers if the mod has been completely initialised
bool haMqttDiscovery = false; // Is MQTT discovery enabled or not
bool haMqttDiscoveryDone = false; // Remembers if we already published the HA discovery topics
// SHT vars
SHT *shtTempHumidSensor; // Instance of SHT lib
SHT *shtTempHumidSensor = nullptr; // Instance of SHT lib
byte shtType = 0; // SHT sensor type to be used. Default: SHT30
byte unitOfTemp = 0; // Temperature unit to be used. Default: Celsius (0 = Celsius, 1 = Fahrenheit)
bool shtInitDone = false; // Remembers if SHT sensor has been initialised
@ -37,7 +36,7 @@ class ShtUsermod : public Usermod
void initShtTempHumiditySensor();
void cleanupShtTempHumiditySensor();
void cleanup();
bool isShtReady();
inline bool isShtReady() { return shtInitDone; } // Checks if the SHT sensor has been initialised.
void publishTemperatureAndHumidityViaMqtt();
void publishHomeAssistantAutodiscovery();
@ -94,7 +93,7 @@ void ShtUsermod::initShtTempHumiditySensor()
case USERMOD_SHT_TYPE_SHT85: shtTempHumidSensor = (SHT *) new SHT85(); break;
}
shtTempHumidSensor->begin(shtI2cAddress, i2c_sda, i2c_scl);
shtTempHumidSensor->begin(shtI2cAddress); // uses &Wire
if (shtTempHumidSensor->readStatus() == 0xFFFF) {
DEBUG_PRINTF("[%s] SHT init failed!\n", _name);
cleanup();
@ -113,8 +112,11 @@ void ShtUsermod::initShtTempHumiditySensor()
*/
void ShtUsermod::cleanupShtTempHumiditySensor()
{
if (isShtReady()) shtTempHumidSensor->reset();
if (isShtReady()) {
shtTempHumidSensor->reset();
delete shtTempHumidSensor;
shtTempHumidSensor = nullptr;
}
shtInitDone = false;
}
@ -129,26 +131,9 @@ void ShtUsermod::cleanupShtTempHumiditySensor()
void ShtUsermod::cleanup()
{
cleanupShtTempHumiditySensor();
if (pinAllocDone) {
PinManagerPinType pins[2] = { { i2c_sda, true }, { i2c_scl, true } };
pinManager.deallocateMultiplePins(pins, 2, PinOwner::HW_I2C);
pinAllocDone = false;
}
enabled = false;
}
/**
* Checks if the SHT sensor has been initialised.
*
* @return bool
*/
bool ShtUsermod::isShtReady()
{
return shtInitDone;
}
/**
* Publish temperature and humidity to WLED device topic.
*
@ -244,14 +229,12 @@ void ShtUsermod::appendDeviceToMqttDiscoveryMessage(JsonDocument& root) {
void ShtUsermod::setup()
{
if (enabled) {
PinManagerPinType pins[2] = { { i2c_sda, true }, { i2c_scl, true } };
// GPIOs can be set to -1 and allocateMultiplePins() will return true, so check they're gt zero
if (i2c_sda < 0 || i2c_scl < 0 || !pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) {
DEBUG_PRINTF("[%s] SHT pin allocation failed!\n", _name);
// GPIOs can be set to -1 , so check they're gt zero
if (i2c_sda < 0 || i2c_scl < 0) {
DEBUG_PRINTF("[%s] I2C bus not initialised!\n", _name);
cleanup();
return;
}
pinAllocDone = true;
initShtTempHumiditySensor();
@ -463,7 +446,19 @@ void ShtUsermod::addToJsonInfo(JsonObject& root)
jsonHumidity.add(F(" RH"));
jsonTemp.add(getTemperature());
jsonTemp.add(unitOfTemp ? "°F" : "°C");
jsonTemp.add(getUnitString());
// sensor object
JsonObject sensor = root[F("sensor")];
if (sensor.isNull()) sensor = root.createNestedObject(F("sensor"));
jsonTemp = sensor.createNestedArray(F("temp"));
jsonTemp.add(getTemperature());
jsonTemp.add(getUnitString());
jsonHumidity = sensor.createNestedArray(F("humidity"));
jsonHumidity.add(getHumidity());
jsonHumidity.add(F(" RH"));
}
/**

View File

@ -1,20 +1,24 @@
#pragma once
#include "wled.h"
#undef U8X8_NO_HW_I2C // borrowed from WLEDMM: we do want I2C hardware drivers - if possible
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
#include "4LD_wled_fonts.c"
#ifndef FLD_ESP32_NO_THREADS
#define FLD_ESP32_USE_THREADS // comment out to use 0.13.x behviour without parallel update task - slower, but more robust. May delay other tasks like LEDs or audioreactive!!
#endif
//
// Insired by the usermod_v2_four_line_display
// Inspired by the usermod_v2_four_line_display
//
// v2 usermod for using 128x32 or 128x64 i2c
// OLED displays to provide a four line display
// for WLED.
//
// Dependencies
// * This usermod REQURES the ModeSortUsermod
// * This Usermod works best, by far, when coupled
// with RotaryEncoderUIUsermod.
// with RotaryEncoderUI ALT Usermod.
//
// Make sure to enable NTP and set your time zone in WLED Config | Time.
//
@ -23,22 +27,14 @@
// REQUIREMENT: * U8g2 (the version already in platformio.ini is fine)
// REQUIREMENT: * Wire
//
// 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
//The SCL and SDA pins are defined here.
#ifndef FLD_PIN_SCL
#define FLD_PIN_SCL i2c_scl
#endif
#ifndef FLD_PIN_SDA
#define FLD_PIN_SDA i2c_sda
#endif
#ifndef FLD_PIN_CLOCKSPI
#define FLD_PIN_CLOCKSPI spi_sclk
#endif
#ifndef FLD_PIN_DATASPI
#define FLD_PIN_DATASPI spi_mosi
#endif
#ifndef FLD_PIN_CS
#define FLD_PIN_CS spi_cs
#define FLD_PIN_CS 15
#endif
#ifdef ARDUINO_ARCH_ESP32
@ -92,14 +88,17 @@ typedef enum {
SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C
SSD1305_64, // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C
SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI
SSD1306_SPI64 // U8X8_SSD1306_128X64_NONAME_HW_SPI
SSD1306_SPI64, // U8X8_SSD1306_128X64_NONAME_HW_SPI
SSD1309_SPI64 // U8X8_SSD1309_128X64_NONAME0_4W_HW_SPI
} DisplayType;
class FourLineDisplayUsermod : public Usermod {
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
public:
FourLineDisplayUsermod() { if (!instance) instance = this; }
static FourLineDisplayUsermod* getInstance(void) { return instance; }
#endif
private:
@ -112,10 +111,10 @@ class FourLineDisplayUsermod : public Usermod {
U8X8 *u8x8 = nullptr; // pointer to U8X8 display object
#ifndef FLD_SPI_DEFAULT
int8_t ioPin[5] = {FLD_PIN_SCL, FLD_PIN_SDA, -1, -1, -1}; // I2C pins: SCL, SDA
int8_t ioPin[3] = {-1, -1, -1}; // I2C pins: SCL, SDA
uint32_t ioFrequency = 400000; // in Hz (minimum is 100000, baseline is 400000 and maximum should be 3400000)
#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
int8_t ioPin[3] = {FLD_PIN_CS, FLD_PIN_DC, FLD_PIN_RESET}; // custom SPI pins: CS, DC, RST
uint32_t ioFrequency = 1000000; // in Hz (minimum is 500kHz, baseline is 1MHz and maximum should be 20MHz)
#endif
@ -178,7 +177,194 @@ class FourLineDisplayUsermod : public Usermod {
// https://github.com/olikraus/u8g2/wiki/gallery
// some displays need this to properly apply contrast
void setVcomh(bool highContrast) {
void setVcomh(bool highContrast);
void startDisplay();
/**
* Wrappers for screen drawing
*/
void setFlipMode(uint8_t mode);
void setContrast(uint8_t contrast);
void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false);
void draw2x2String(uint8_t col, uint8_t row, const char *string);
void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH=false);
void draw2x2Glyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font);
void draw2x2GlyphIcons();
uint8_t getCols();
void clear();
void setPowerSave(uint8_t save);
void center(String &line, uint8_t width);
/**
* Display the current date and time in large characters
* on the middle rows. Based 24 or 12 hour depending on
* the useAMPM configuration.
*/
void showTime();
/**
* Enable sleep (turn the display off) or clock mode.
*/
void sleepOrClock(bool enabled);
public:
// gets called once at boot. Do all initialization that doesn't depend on
// network here
void setup();
// gets called every time WiFi is (re-)connected. Initialize own network
// interfaces here
void connected();
/**
* Da loop.
*/
void loop();
//function to update lastredraw
inline void updateRedrawTime() { lastRedraw = millis(); }
/**
* Redraw the screen (but only if things have changed
* or if forceRedraw).
*/
void redraw(bool forceRedraw);
void updateBrightness();
void updateSpeed();
void updateIntensity();
void drawStatusIcons();
/**
* marks the position of the arrow showing
* the current setting being changed
* pass line and colum info
*/
void setMarkLine(byte newMarkLineNum, byte newMarkColNum);
//Draw the arrow for the current setting beiong changed
void drawArrow();
//Display the current effect or palette (desiredEntry)
// on the appropriate line (row).
void showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row);
/**
* If there screen is off or in clock is displayed,
* this will return true. This allows us to throw away
* the first input from the rotary encoder but
* to wake up the screen.
*/
bool wakeDisplay();
/**
* Allows you to show one line and a glyph as overlay for a period of time.
* Clears the screen and prints.
* Used in Rotary Encoder usermod.
*/
void overlay(const char* line1, long showHowLong, byte glyphType);
/**
* Allows you to show Akemi WLED logo overlay for a period of time.
* Clears the screen and prints.
*/
void overlayLogo(long showHowLong);
/**
* Allows you to show two lines as overlay for a period of time.
* Clears the screen and prints.
* Used in Auto Save usermod
*/
void overlay(const char* line1, const char* line2, long showHowLong);
void networkOverlay(const char* line1, long showHowLong);
/**
* handleButton() can be used to override default button behaviour. Returning true
* will prevent button working in a default way.
* Replicating button.cpp
*/
bool handleButton(uint8_t b);
void onUpdateBegin(bool init);
/*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
* Below it is shown how this could be used for e.g. a light sensor
*/
//void addToJsonInfo(JsonObject& root);
/*
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
//void addToJsonState(JsonObject& root);
/*
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
//void readFromJsonState(JsonObject& root);
void appendConfigData();
/*
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
* If you want to force saving the current state, use serializeConfig() in your loop().
*
* CAUTION: serializeConfig() will initiate a filesystem write operation.
* It might cause the LEDs to stutter and will cause flash wear if called too often.
* Use it sparingly and always in the loop, never in network callbacks!
*
* addToConfig() will also not yet add your setting to one of the settings pages automatically.
* To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually.
*
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
*/
void addToConfig(JsonObject& root);
/*
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
* This is called by WLED when settings are loaded (currently this only happens once immediately after boot)
*
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
*/
bool readFromConfig(JsonObject& root);
/*
* 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_FOUR_LINE_DISP;
}
};
// 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 = "refreshRate-ms";
const char FourLineDisplayUsermod::_screenTimeOut[] PROGMEM = "screenTimeOutSec";
const char FourLineDisplayUsermod::_flip[] PROGMEM = "flip";
const char FourLineDisplayUsermod::_sleepMode[] PROGMEM = "sleepMode";
const char FourLineDisplayUsermod::_clockMode[] PROGMEM = "clockMode";
const char FourLineDisplayUsermod::_showSeconds[] PROGMEM = "showSeconds";
const char FourLineDisplayUsermod::_busClkFrequency[] PROGMEM = "i2c-freq-kHz";
const char FourLineDisplayUsermod::_contrastFix[] PROGMEM = "contrastFix";
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
FourLineDisplayUsermod *FourLineDisplayUsermod::instance = nullptr;
#endif
// some displays need this to properly apply contrast
void FourLineDisplayUsermod::setVcomh(bool highContrast) {
if (type == NONE || !enabled) return;
u8x8_t *u8x8_struct = u8x8->getU8x8();
u8x8_cad_StartTransfer(u8x8_struct);
u8x8_cad_SendCmd(u8x8_struct, 0x0db); //address of value
@ -186,18 +372,32 @@ class FourLineDisplayUsermod : public Usermod {
u8x8_cad_EndTransfer(u8x8_struct);
}
void FourLineDisplayUsermod::startDisplay() {
if (type == NONE || !enabled) return;
lineHeight = u8x8->getRows() > 4 ? 2 : 1;
DEBUG_PRINTLN(F("Starting display."));
u8x8->setBusClock(ioFrequency); // can be used for SPI too
u8x8->begin();
setFlipMode(flip);
setVcomh(contrastFix);
setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
setPowerSave(0);
//drawString(0, 0, "Loading...");
overlayLogo(3500);
}
/**
* Wrappers for screen drawing
*/
void setFlipMode(uint8_t mode) {
void FourLineDisplayUsermod::setFlipMode(uint8_t mode) {
if (type == NONE || !enabled) return;
u8x8->setFlipMode(mode);
}
void setContrast(uint8_t contrast) {
void FourLineDisplayUsermod::setContrast(uint8_t contrast) {
if (type == NONE || !enabled) return;
u8x8->setContrast(contrast);
}
void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false) {
void FourLineDisplayUsermod::drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH) {
if (type == NONE || !enabled) return;
drawing = true;
u8x8->setFont(u8x8_font_chroma48medium8_r);
@ -205,14 +405,14 @@ class FourLineDisplayUsermod : public Usermod {
else u8x8->drawString(col, row, string);
drawing = false;
}
void draw2x2String(uint8_t col, uint8_t row, const char *string) {
void FourLineDisplayUsermod::draw2x2String(uint8_t col, uint8_t row, const char *string) {
if (type == NONE || !enabled) return;
drawing = true;
u8x8->setFont(u8x8_font_chroma48medium8_r);
u8x8->draw2x2String(col, row, string);
drawing = false;
}
void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH=false) {
void FourLineDisplayUsermod::drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH) {
if (type == NONE || !enabled) return;
drawing = true;
u8x8->setFont(font);
@ -220,35 +420,35 @@ class FourLineDisplayUsermod : public Usermod {
else u8x8->drawGlyph(col, row, glyph);
drawing = false;
}
void draw2x2Glyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font) {
void FourLineDisplayUsermod::draw2x2Glyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font) {
if (type == NONE || !enabled) return;
drawing = true;
u8x8->setFont(font);
u8x8->draw2x2Glyph(col, row, glyph);
drawing = false;
}
uint8_t getCols() {
uint8_t FourLineDisplayUsermod::getCols() {
if (type==NONE || !enabled) return 0;
return u8x8->getCols();
}
void clear() {
void FourLineDisplayUsermod::clear() {
if (type == NONE || !enabled) return;
drawing = true;
u8x8->clear();
drawing = false;
}
void setPowerSave(uint8_t save) {
void FourLineDisplayUsermod::setPowerSave(uint8_t save) {
if (type == NONE || !enabled) return;
u8x8->setPowerSave(save);
}
void center(String &line, uint8_t width) {
void FourLineDisplayUsermod::center(String &line, uint8_t width) {
int len = line.length();
if (len<width) for (byte i=(width-len)/2; i>0; i--) line = ' ' + line;
for (byte i=line.length(); i<width; i++) line += ' ';
}
void draw2x2GlyphIcons() {
void FourLineDisplayUsermod::draw2x2GlyphIcons() {
drawing = true;
if (lineHeight == 2) {
drawGlyph( 1, 0, 1, u8x8_4LineDisplay_WLED_icons_2x2, true); //brightness icon
@ -271,7 +471,7 @@ class FourLineDisplayUsermod : public Usermod {
* on the middle rows. Based 24 or 12 hour depending on
* the useAMPM configuration.
*/
void showTime() {
void FourLineDisplayUsermod::showTime() {
if (type == NONE || !enabled || !displayTurnedOff) return;
unsigned long now = millis();
@ -317,7 +517,7 @@ class FourLineDisplayUsermod : public Usermod {
/**
* Enable sleep (turn the display off) or clock mode.
*/
void sleepOrClock(bool enabled) {
void FourLineDisplayUsermod::sleepOrClock(bool enabled) {
if (enabled) {
displayTurnedOff = true;
if (clockMode && ntpEnabled) {
@ -331,143 +531,56 @@ class FourLineDisplayUsermod : public Usermod {
}
}
public:
// gets called once at boot. Do all initialization that doesn't depend on
// network here
void setup() {
if (type == NONE || !enabled) return;
void FourLineDisplayUsermod::setup() {
bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64 || type == SSD1309_SPI64);
bool isHW, isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64);
PinOwner po = PinOwner::UM_FourLineDisplay;
// check if pins are -1 and disable usermod as PinManager::allocateMultiplePins() will accept -1 as a valid pin
if (isSPI) {
uint8_t hw_sclk = spi_sclk<0 ? HW_PIN_CLOCKSPI : spi_sclk;
uint8_t hw_mosi = spi_mosi<0 ? HW_PIN_DATASPI : spi_mosi;
if (ioPin[0] < 0 || ioPin[1] < 0) {
ioPin[0] = hw_sclk;
ioPin[1] = hw_mosi;
}
isHW = (ioPin[0]==hw_sclk && ioPin[1]==hw_mosi);
PinManagerPinType cspins[3] = { { ioPin[2], true }, { ioPin[3], true }, { ioPin[4], true } };
if (!pinManager.allocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay)) { type=NONE; return; }
if (isHW) po = PinOwner::HW_SPI; // allow multiple allocations of HW I2C bus pins
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } };
if (!pinManager.allocateMultiplePins(pins, 2, po)) {
pinManager.deallocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay);
if (spi_sclk<0 || spi_mosi<0 || ioPin[0]<0 || ioPin[1]<0 || ioPin[1]<0) {
type = NONE;
return;
} else {
PinManagerPinType cspins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } };
if (!pinManager.allocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay)) { type = NONE; }
}
} else {
uint8_t hw_scl = i2c_scl<0 ? HW_PIN_SCL : i2c_scl;
uint8_t hw_sda = i2c_sda<0 ? HW_PIN_SDA : i2c_sda;
if (ioPin[0] < 0 || ioPin[1] < 0) {
ioPin[0] = hw_scl;
ioPin[1] = hw_sda;
}
isHW = (ioPin[0]==hw_scl && ioPin[1]==hw_sda);
if (isHW) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
PinManagerPinType pins[2] = { {ioPin[0], true }, { ioPin[1], true } };
if (!pinManager.allocateMultiplePins(pins, 2, po)) { type=NONE; return; }
if (i2c_scl<0 || i2c_sda<0) { type=NONE; }
}
DEBUG_PRINTLN(F("Allocating display."));
/*
// At some point it may be good to not new/delete U8X8 object but use this instead
// (does not currently work)
//-------------------------------------------------------------------------------
switch (type) {
case SSD1306:
u8x8_Setup(u8x8.getU8x8(), u8x8_d_ssd1306_128x32_univision, u8x8_cad_ssd13xx_fast_i2c, u8x8_byte_arduino_sw_i2c, u8x8_gpio_and_delay_arduino);
break;
case SH1106:
u8x8_Setup(u8x8.getU8x8(), u8x8_d_sh1106_128x64_winstar, u8x8_cad_ssd13xx_i2c, u8x8_byte_arduino_sw_i2c, u8x8_gpio_and_delay_arduino);
break;
case SSD1306_64:
u8x8_Setup(u8x8.getU8x8(), u8x8_d_ssd1306_128x64_noname, u8x8_cad_ssd13xx_fast_i2c, u8x8_byte_arduino_sw_i2c, u8x8_gpio_and_delay_arduino);
break;
case SSD1305:
u8x8_Setup(u8x8.getU8x8(), u8x8_d_ssd1305_128x32_adafruit, u8x8_cad_ssd13xx_i2c, u8x8_byte_arduino_hw_i2c, u8x8_gpio_and_delay_arduino);
break;
case SSD1305_64:
u8x8_Setup(u8x8.getU8x8(), u8x8_d_ssd1305_128x64_adafruit, u8x8_cad_ssd13xx_i2c, u8x8_byte_arduino_sw_i2c, u8x8_gpio_and_delay_arduino);
break;
case SSD1306_SPI:
u8x8_Setup(u8x8.getU8x8(), u8x8_d_ssd1306_128x32_univision, u8x8_cad_001, u8x8_byte_arduino_4wire_sw_spi, u8x8_gpio_and_delay_arduino);
break;
case SSD1306_SPI64:
u8x8_Setup(u8x8.getU8x8(), u8x8_d_ssd1306_128x64_noname, u8x8_cad_001, u8x8_byte_arduino_4wire_sw_spi, u8x8_gpio_and_delay_arduino);
break;
default:
type = NONE;
return;
}
if (isSPI) {
if (!isHW) u8x8_SetPin_4Wire_SW_SPI(u8x8.getU8x8(), ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
else u8x8_SetPin_4Wire_HW_SPI(u8x8.getU8x8(), ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
} else {
if (!isHW) u8x8_SetPin_SW_I2C(u8x8.getU8x8(), ioPin[0], ioPin[1], U8X8_PIN_NONE); // SCL, SDA, reset
else u8x8_SetPin_HW_I2C(u8x8.getU8x8(), U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
}
*/
switch (type) {
case SSD1306:
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
break;
case SH1106:
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
break;
case SSD1306_64:
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
break;
case SSD1305:
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
break;
case SSD1305_64:
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
break;
case SSD1306_SPI:
// u8x8 uses global SPI variable that is attached to VSPI bus on ESP32
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
break;
case SSD1306_SPI64:
// u8x8 uses global SPI variable that is attached to VSPI bus on ESP32
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
break;
default:
u8x8 = nullptr;
// U8X8 uses Wire (or Wire1 with 2ND constructor) and will use existing Wire properties (calls Wire.begin() though)
case SSD1306: u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(); break;
case SH1106: u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(); break;
case SSD1306_64: u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(); break;
case SSD1305: u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(); break;
case SSD1305_64: u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(); break;
// U8X8 uses global SPI variable that is attached to VSPI bus on ESP32
case SSD1306_SPI: u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset
case SSD1306_SPI64: u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset
case SSD1309_SPI64: u8x8 = (U8X8 *) new U8X8_SSD1309_128X64_NONAME0_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset
// catchall
default: u8x8 = (U8X8 *) new U8X8_NULL(); enabled = false; break; // catchall to create U8x8 instance
}
if (nullptr == u8x8) {
DEBUG_PRINTLN(F("Display init failed."));
pinManager.deallocateMultiplePins((const uint8_t*)ioPin, isSPI ? 5 : 2, po);
if (isSPI) {
pinManager.deallocateMultiplePins((const uint8_t*)ioPin, 3, PinOwner::UM_FourLineDisplay);
}
type = NONE;
return;
}
lineHeight = u8x8->getRows() > 4 ? 2 : 1;
DEBUG_PRINTLN(F("Starting display."));
u8x8->setBusClock(ioFrequency); // can be used for SPI too
u8x8->begin();
setFlipMode(flip);
setVcomh(contrastFix);
setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
setPowerSave(0);
//drawString(0, 0, "Loading...");
overlayLogo(3500);
startDisplay();
onUpdateBegin(false); // create Display task
initDone = true;
}
// gets called every time WiFi is (re-)connected. Initialize own network
// interfaces here
void connected() {
void FourLineDisplayUsermod::connected() {
knownSsid = WiFi.SSID(); //apActive ? apSSID : WiFi.SSID(); //apActive ? WiFi.softAPSSID() :
knownIp = Network.localIP(); //apActive ? IPAddress(4, 3, 2, 1) : Network.localIP();
networkOverlay(PSTR("NETWORK INFO"),7000);
@ -476,8 +589,8 @@ class FourLineDisplayUsermod : public Usermod {
/**
* Da loop.
*/
void loop() {
#ifndef ARDUINO_ARCH_ESP32
void FourLineDisplayUsermod::loop() {
#if !(defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS))
if (!enabled || strip.isUpdating()) return;
unsigned long now = millis();
if (now < nextUpdate) return;
@ -486,16 +599,11 @@ class FourLineDisplayUsermod : public Usermod {
#endif
}
//function to update lastredraw
void updateRedrawTime() {
lastRedraw = millis();
}
/**
* Redraw the screen (but only if things have changed
* or if forceRedraw).
*/
void redraw(bool forceRedraw) {
void FourLineDisplayUsermod::redraw(bool forceRedraw) {
bool needRedraw = false;
unsigned long now = millis();
@ -607,7 +715,12 @@ class FourLineDisplayUsermod : public Usermod {
showCurrentEffectOrPalette(knownMode, JSON_mode_names, 3); //Effect Mode info
}
void updateBrightness() {
void FourLineDisplayUsermod::updateBrightness() {
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
unsigned long now = millis();
while (drawing && millis()-now < 125) delay(1); // wait if someone else is drawing
if (drawing || lockRedraw) return;
#endif
knownBrightness = bri;
if (overlayUntil == 0) {
lockRedraw = true;
@ -619,7 +732,12 @@ class FourLineDisplayUsermod : public Usermod {
}
}
void updateSpeed() {
void FourLineDisplayUsermod::updateSpeed() {
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
unsigned long now = millis();
while (drawing && millis()-now < 125) delay(1); // wait if someone else is drawing
if (drawing || lockRedraw) return;
#endif
knownEffectSpeed = effectSpeed;
if (overlayUntil == 0) {
lockRedraw = true;
@ -631,7 +749,12 @@ class FourLineDisplayUsermod : public Usermod {
}
}
void updateIntensity() {
void FourLineDisplayUsermod::updateIntensity() {
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
unsigned long now = millis();
while (drawing && millis()-now < 125) delay(1); // wait if someone else is drawing
if (drawing || lockRedraw) return;
#endif
knownEffectIntensity = effectIntensity;
if (overlayUntil == 0) {
lockRedraw = true;
@ -643,7 +766,12 @@ class FourLineDisplayUsermod : public Usermod {
}
}
void drawStatusIcons() {
void FourLineDisplayUsermod::drawStatusIcons() {
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
unsigned long now = millis();
while (drawing && millis()-now < 125) delay(1); // wait if someone else is drawing
if (drawing || lockRedraw) return;
#endif
uint8_t col = 15;
uint8_t row = 0;
lockRedraw = true;
@ -660,13 +788,18 @@ class FourLineDisplayUsermod : public Usermod {
* the current setting being changed
* pass line and colum info
*/
void setMarkLine(byte newMarkLineNum, byte newMarkColNum) {
void FourLineDisplayUsermod::setMarkLine(byte newMarkLineNum, byte newMarkColNum) {
markLineNum = newMarkLineNum;
markColNum = newMarkColNum;
}
//Draw the arrow for the current setting beiong changed
void drawArrow() {
void FourLineDisplayUsermod::drawArrow() {
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
unsigned long now = millis();
while (drawing && millis()-now < 125) delay(1); // wait if someone else is drawing
if (drawing || lockRedraw) return;
#endif
lockRedraw = true;
if (markColNum != 255 && markLineNum !=255) drawGlyph(markColNum, markLineNum*lineHeight, 21, u8x8_4LineDisplay_WLED_icons_1x1);
lockRedraw = false;
@ -674,7 +807,12 @@ class FourLineDisplayUsermod : public Usermod {
//Display the current effect or palette (desiredEntry)
// on the appropriate line (row).
void showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row) {
void FourLineDisplayUsermod::showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row) {
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
unsigned long now = millis();
while (drawing && millis()-now < 125) delay(1); // wait if someone else is drawing
if (drawing || lockRedraw) return;
#endif
char lineBuffer[MAX_JSON_CHARS];
if (overlayUntil == 0) {
lockRedraw = true;
@ -740,12 +878,14 @@ class FourLineDisplayUsermod : public Usermod {
* the first input from the rotary encoder but
* to wake up the screen.
*/
bool wakeDisplay() {
bool FourLineDisplayUsermod::wakeDisplay() {
if (type == NONE || !enabled) return false;
if (displayTurnedOff) {
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
unsigned long now = millis();
while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing
if (drawing) return false;
while (drawing && millis()-now < 125) delay(1); // wait if someone else is drawing
if (drawing || lockRedraw) return false;
#endif
lockRedraw = true;
clear();
// Turn the display back on
@ -761,10 +901,12 @@ class FourLineDisplayUsermod : public Usermod {
* Clears the screen and prints.
* Used in Rotary Encoder usermod.
*/
void overlay(const char* line1, long showHowLong, byte glyphType) {
void FourLineDisplayUsermod::overlay(const char* line1, long showHowLong, byte glyphType) {
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
unsigned long now = millis();
while (drawing && millis()-now < 125) delay(1); // wait if someone else is drawing
if (drawing) return;
if (drawing || lockRedraw) return;
#endif
lockRedraw = true;
// Turn the display back on
if (!wakeDisplay()) clear();
@ -786,10 +928,12 @@ class FourLineDisplayUsermod : public Usermod {
* Allows you to show Akemi WLED logo overlay for a period of time.
* Clears the screen and prints.
*/
void overlayLogo(long showHowLong) {
void FourLineDisplayUsermod::overlayLogo(long showHowLong) {
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
unsigned long now = millis();
while (drawing && millis()-now < 125) delay(1); // wait if someone else is drawing
if (drawing) return;
if (drawing || lockRedraw) return;
#endif
lockRedraw = true;
// Turn the display back on
if (!wakeDisplay()) clear();
@ -848,10 +992,12 @@ class FourLineDisplayUsermod : public Usermod {
* Clears the screen and prints.
* Used in Auto Save usermod
*/
void overlay(const char* line1, const char* line2, long showHowLong) {
void FourLineDisplayUsermod::overlay(const char* line1, const char* line2, long showHowLong) {
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
unsigned long now = millis();
while (drawing && millis()-now < 125) delay(1); // wait if someone else is drawing
if (drawing) return;
if (drawing || lockRedraw) return;
#endif
lockRedraw = true;
// Turn the display back on
if (!wakeDisplay()) clear();
@ -870,10 +1016,12 @@ class FourLineDisplayUsermod : public Usermod {
lockRedraw = false;
}
void networkOverlay(const char* line1, long showHowLong) {
void FourLineDisplayUsermod::networkOverlay(const char* line1, long showHowLong) {
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
unsigned long now = millis();
while (drawing && millis()-now < 125) delay(1); // wait if someone else is drawing
if (drawing) return;
if (drawing || lockRedraw) return;
#endif
lockRedraw = true;
String line;
@ -915,7 +1063,7 @@ class FourLineDisplayUsermod : public Usermod {
* will prevent button working in a default way.
* Replicating button.cpp
*/
bool handleButton(uint8_t b) {
bool FourLineDisplayUsermod::handleButton(uint8_t b) {
yield();
if (!enabled
|| b // butto 0 only
@ -989,8 +1137,8 @@ class FourLineDisplayUsermod : public Usermod {
#else
#define ARDUINO_RUNNING_CORE 1
#endif
void onUpdateBegin(bool init) {
#ifdef ARDUINO_ARCH_ESP32
void FourLineDisplayUsermod::onUpdateBegin(bool init) {
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
if (init && Display_Task) {
vTaskSuspend(Display_Task); // update is about to begin, disable task to prevent crash
} else {
@ -1026,7 +1174,7 @@ class FourLineDisplayUsermod : public Usermod {
* 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) {
//void FourLineDisplayUsermod::addToJsonInfo(JsonObject& root) {
//JsonObject user = root["u"];
//if (user.isNull()) user = root.createNestedObject("u");
//JsonArray data = user.createNestedArray(F("4LineDisplay"));
@ -1037,18 +1185,18 @@ class FourLineDisplayUsermod : public Usermod {
* 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) {
//void FourLineDisplayUsermod::addToJsonState(JsonObject& root) {
//}
/*
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
//void readFromJsonState(JsonObject& root) {
//void FourLineDisplayUsermod::readFromJsonState(JsonObject& root) {
// if (!initDone) return; // prevent crash on boot applyPreset()
//}
void appendConfigData() {
void FourLineDisplayUsermod::appendConfigData() {
oappend(SET_F("dd=addDropdown('4LineDisplay','type');"));
oappend(SET_F("addOption(dd,'None',0);"));
oappend(SET_F("addOption(dd,'SSD1306',1);"));
@ -1058,11 +1206,11 @@ class FourLineDisplayUsermod : public Usermod {
oappend(SET_F("addOption(dd,'SSD1305 128x64',5);"));
oappend(SET_F("addOption(dd,'SSD1306 SPI',6);"));
oappend(SET_F("addOption(dd,'SSD1306 SPI 128x64',7);"));
oappend(SET_F("addInfo('4LineDisplay:pin[]',0,'<i>-1 use global</i>','I2C/SPI CLK');"));
oappend(SET_F("addInfo('4LineDisplay:pin[]',1,'<i>-1 use global</i>','I2C/SPI DTA');"));
oappend(SET_F("addInfo('4LineDisplay:pin[]',2,'','SPI CS');"));
oappend(SET_F("addInfo('4LineDisplay:pin[]',3,'','SPI DC');"));
oappend(SET_F("addInfo('4LineDisplay:pin[]',4,'','SPI RST');"));
oappend(SET_F("addOption(dd,'SSD1309 SPI 128x64',8);"));
oappend(SET_F("addInfo('4LineDisplay:type',1,'<br><i class=\"warn\">Change may require reboot</i>','');"));
oappend(SET_F("addInfo('4LineDisplay:pin[]',0,'','SPI CS');"));
oappend(SET_F("addInfo('4LineDisplay:pin[]',1,'','SPI DC');"));
oappend(SET_F("addInfo('4LineDisplay:pin[]',2,'','SPI RST');"));
}
/*
@ -1079,27 +1227,13 @@ class FourLineDisplayUsermod : public Usermod {
*
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
*/
void addToConfig(JsonObject& root) {
// determine if we are using global HW pins (data & clock)
int8_t hw_dta, hw_clk;
if ((type == SSD1306_SPI || type == SSD1306_SPI64)) {
hw_clk = spi_sclk<0 ? HW_PIN_CLOCKSPI : spi_sclk;
hw_dta = spi_mosi<0 ? HW_PIN_DATASPI : spi_mosi;
} else {
hw_clk = i2c_scl<0 ? HW_PIN_SCL : i2c_scl;
hw_dta = i2c_sda<0 ? HW_PIN_SDA : i2c_sda;
}
void FourLineDisplayUsermod::addToConfig(JsonObject& root) {
JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled;
JsonArray io_pin = top.createNestedArray("pin");
for (int i=0; i<5; i++) {
if (i==0 && ioPin[i]==hw_clk) io_pin.add(-1); // do not store global HW pin
else if (i==1 && ioPin[i]==hw_dta) io_pin.add(-1); // do not store global HW pin
else io_pin.add(ioPin[i]);
}
top["type"] = type;
JsonArray io_pin = top.createNestedArray("pin");
for (int i=0; i<3; i++) io_pin.add(ioPin[i]);
top[FPSTR(_flip)] = (bool) flip;
top[FPSTR(_contrast)] = contrast;
top[FPSTR(_contrastFix)] = (bool) contrastFix;
@ -1122,10 +1256,10 @@ class FourLineDisplayUsermod : public Usermod {
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
*/
bool readFromConfig(JsonObject& root) {
bool FourLineDisplayUsermod::readFromConfig(JsonObject& root) {
bool needsRedraw = false;
DisplayType newType = type;
int8_t oldPin[5]; for (byte i=0; i<5; i++) oldPin[i] = ioPin[i];
int8_t oldPin[3]; for (byte i=0; i<3; i++) oldPin[i] = ioPin[i];
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
@ -1136,7 +1270,7 @@ class FourLineDisplayUsermod : public Usermod {
enabled = top[FPSTR(_enabled)] | enabled;
newType = top["type"] | newType;
for (byte i=0; i<5; i++) ioPin[i] = top["pin"][i] | ioPin[i];
for (byte i=0; i<3; i++) ioPin[i] = top["pin"][i] | ioPin[i];
flip = top[FPSTR(_flip)] | flip;
contrast = top[FPSTR(_contrast)] | contrast;
#ifndef ARDUINO_ARCH_ESP32
@ -1162,26 +1296,72 @@ class FourLineDisplayUsermod : public Usermod {
DEBUG_PRINTLN(F(" config (re)loaded."));
// changing parameters from settings page
bool pinsChanged = false;
for (byte i=0; i<5; i++) if (ioPin[i] != oldPin[i]) { pinsChanged = true; break; }
for (byte i=0; i<3; i++) if (ioPin[i] != oldPin[i]) { pinsChanged = true; break; }
if (pinsChanged || type!=newType) {
if (type != NONE) delete u8x8;
PinOwner po = PinOwner::UM_FourLineDisplay;
bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64);
bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64 || type == SSD1309_SPI64);
bool newSPI = (newType == SSD1306_SPI || newType == SSD1306_SPI64 || newType == SSD1309_SPI64);
if (isSPI) {
pinManager.deallocateMultiplePins((const uint8_t *)(&oldPin[2]), 3, po);
uint8_t hw_sclk = spi_sclk<0 ? HW_PIN_CLOCKSPI : spi_sclk;
uint8_t hw_mosi = spi_mosi<0 ? HW_PIN_DATASPI : spi_mosi;
bool isHW = (oldPin[0]==hw_sclk && oldPin[1]==hw_mosi);
if (isHW) po = PinOwner::HW_SPI;
if (pinsChanged || !newSPI) pinManager.deallocateMultiplePins((const uint8_t*)oldPin, 3, PinOwner::UM_FourLineDisplay);
if (!newSPI) {
// was SPI but is no longer SPI
if (i2c_scl<0 || i2c_sda<0) { newType=NONE; }
} else {
uint8_t hw_scl = i2c_scl<0 ? HW_PIN_SCL : i2c_scl;
uint8_t hw_sda = i2c_sda<0 ? HW_PIN_SDA : i2c_sda;
bool isHW = (oldPin[0]==hw_scl && oldPin[1]==hw_sda);
if (isHW) po = PinOwner::HW_I2C;
// still SPI but pins changed
PinManagerPinType cspins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } };
if (ioPin[0]<0 || ioPin[1]<0 || ioPin[1]<0) { newType=NONE; }
else if (!pinManager.allocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay)) { newType=NONE; }
}
} else if (newSPI) {
// was I2C but is now SPI
if (spi_sclk<0 || spi_mosi<0) {
newType=NONE;
} else {
PinManagerPinType pins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } };
if (ioPin[0]<0 || ioPin[1]<0 || ioPin[1]<0) { newType=NONE; }
else if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_FourLineDisplay)) { newType=NONE; }
}
} else {
// just I2C type changed
}
pinManager.deallocateMultiplePins((const uint8_t *)oldPin, 2, po);
type = newType;
setup();
switch (type) {
case SSD1306:
u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1306_128x32_univision, u8x8_cad_ssd13xx_fast_i2c, u8x8_byte_arduino_hw_i2c, u8x8_gpio_and_delay_arduino);
u8x8_SetPin_HW_I2C(u8x8->getU8x8(), U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE);
break;
case SH1106:
u8x8_Setup(u8x8->getU8x8(), u8x8_d_sh1106_128x64_winstar, u8x8_cad_ssd13xx_fast_i2c, u8x8_byte_arduino_hw_i2c, u8x8_gpio_and_delay_arduino);
u8x8_SetPin_HW_I2C(u8x8->getU8x8(), U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE);
break;
case SSD1306_64:
u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1306_128x64_noname, u8x8_cad_ssd13xx_fast_i2c, u8x8_byte_arduino_hw_i2c, u8x8_gpio_and_delay_arduino);
u8x8_SetPin_HW_I2C(u8x8->getU8x8(), U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE);
break;
case SSD1305:
u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1305_128x32_adafruit, u8x8_cad_ssd13xx_fast_i2c, u8x8_byte_arduino_hw_i2c, u8x8_gpio_and_delay_arduino);
u8x8_SetPin_HW_I2C(u8x8->getU8x8(), U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE);
break;
case SSD1305_64:
u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1305_128x64_adafruit, u8x8_cad_ssd13xx_fast_i2c, u8x8_byte_arduino_hw_i2c, u8x8_gpio_and_delay_arduino);
u8x8_SetPin_HW_I2C(u8x8->getU8x8(), U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE);
break;
case SSD1306_SPI:
u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1306_128x32_univision, u8x8_cad_001, u8x8_byte_arduino_hw_spi, u8x8_gpio_and_delay_arduino);
u8x8_SetPin_4Wire_HW_SPI(u8x8->getU8x8(), ioPin[0], ioPin[1], ioPin[2]); // Pins are cs, dc, reset
break;
case SSD1306_SPI64:
u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1306_128x64_noname, u8x8_cad_001, u8x8_byte_arduino_hw_spi, u8x8_gpio_and_delay_arduino);
u8x8_SetPin_4Wire_HW_SPI(u8x8->getU8x8(), ioPin[0], ioPin[1], ioPin[2]); // Pins are cs, dc, reset
break;
case SSD1309_SPI64:
u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1309_128x64_noname0, u8x8_cad_001, u8x8_byte_arduino_hw_spi, u8x8_gpio_and_delay_arduino);
u8x8_SetPin_4Wire_HW_SPI(u8x8->getU8x8(), ioPin[0], ioPin[1], ioPin[2]); // Pins are cs, dc, reset
default:
u8x8_Setup(u8x8->getU8x8(), u8x8_d_null_cb, u8x8_cad_empty, u8x8_byte_empty, u8x8_dummy_cb);
enabled = false;
break;
}
startDisplay();
needsRedraw |= true;
} else {
u8x8->setBusClock(ioFrequency); // can be used for SPI too
@ -1196,27 +1376,3 @@ class FourLineDisplayUsermod : public Usermod {
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_contrastFix)].isNull();
}
/*
* 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_FOUR_LINE_DISP;
}
};
// 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 = "refreshRate-ms";
const char FourLineDisplayUsermod::_screenTimeOut[] PROGMEM = "screenTimeOutSec";
const char FourLineDisplayUsermod::_flip[] PROGMEM = "flip";
const char FourLineDisplayUsermod::_sleepMode[] PROGMEM = "sleepMode";
const char FourLineDisplayUsermod::_clockMode[] PROGMEM = "clockMode";
const char FourLineDisplayUsermod::_showSeconds[] PROGMEM = "showSeconds";
const char FourLineDisplayUsermod::_busClkFrequency[] PROGMEM = "i2c-freq-kHz";
const char FourLineDisplayUsermod::_contrastFix[] PROGMEM = "contrastFix";
FourLineDisplayUsermod *FourLineDisplayUsermod::instance = nullptr;

View File

@ -45,7 +45,27 @@
#define ENCODER_SW_PIN 19
#endif
// The last UI state, remove color and saturation option if diplay not active(too many options)
#ifndef ENCODER_MAX_DELAY_MS // max delay between polling encoder pins
#define ENCODER_MAX_DELAY_MS 8 // 8 milliseconds => max 120 change impulses in 1 second, for full turn of a 30/30 encoder (4 changes per segment, 30 segments for one turn)
#endif
#ifndef USERMOD_USE_PCF8574
#undef USE_PCF8574
#define USE_PCF8574 false
#else
#undef USE_PCF8574
#define USE_PCF8574 true
#endif
#ifndef PCF8574_ADDRESS
#define PCF8574_ADDRESS 0x20 // some may start at 0x38
#endif
#ifndef PCF8574_INT_PIN
#define PCF8574_INT_PIN -1 // GPIO connected to INT pin on PCF8574
#endif
// The last UI state, remove color and saturation option if display not active (too many options)
#ifdef USERMOD_FOUR_LINE_DISPLAY
#define LAST_UI_STATE 11
#else
@ -113,62 +133,83 @@ static int re_qstringCmp(const void *ap, const void *bp) {
}
static volatile uint8_t pcfPortData = 0; // port expander port state
static volatile uint8_t addrPcf8574 = PCF8574_ADDRESS; // has to be accessible in ISR
// Interrupt routine to read I2C rotary state
// if we are to use PCF8574 port expander we will need to rely on interrupts as polling I2C every 2ms
// is a waste of resources and causes 4LD to fail.
// in such case rely on ISR to read pin values and store them into static variable
static void IRAM_ATTR i2cReadingISR() {
Wire.requestFrom(addrPcf8574, 1U);
if (Wire.available()) {
pcfPortData = Wire.read();
}
}
class RotaryEncoderUIUsermod : public Usermod {
private:
int8_t fadeAmount = 5; // Amount to change every step (brightness)
const int8_t fadeAmount; // Amount to change every step (brightness)
unsigned long loopTime;
unsigned long buttonPressedTime = 0;
unsigned long buttonWaitTime = 0;
bool buttonPressedBefore = false;
bool buttonLongPressed = false;
unsigned long buttonPressedTime;
unsigned long buttonWaitTime;
bool buttonPressedBefore;
bool buttonLongPressed;
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
int8_t pinA; // DT from encoder
int8_t pinB; // CLK from encoder
int8_t pinC; // SW from encoder
unsigned char select_state = 0; // 0: brightness, 1: effect, 2: effect speed, ...
unsigned char select_state; // 0: brightness, 1: effect, 2: effect speed, ...
uint16_t currentHue1 = 16; // default boot color
byte currentSat1 = 255;
uint16_t currentHue1; // default boot color
byte currentSat1;
uint8_t currentCCT;
#ifdef USERMOD_FOUR_LINE_DISPLAY
FourLineDisplayUsermod *display;
#else
void* display = nullptr;
void* display;
#endif
// Pointers the start of the mode names within JSON_mode_names
const char **modes_qstrings = nullptr;
const char **modes_qstrings;
// Array of mode indexes in alphabetical order.
byte *modes_alpha_indexes = nullptr;
byte *modes_alpha_indexes;
// Pointers the start of the palette names within JSON_palette_names
const char **palettes_qstrings = nullptr;
const char **palettes_qstrings;
// Array of palette indexes in alphabetical order.
byte *palettes_alpha_indexes = nullptr;
byte *palettes_alpha_indexes;
unsigned char Enc_A;
unsigned char Enc_B;
unsigned char Enc_A_prev = 0;
struct { // reduce memory footprint
bool Enc_A : 1;
bool Enc_B : 1;
bool Enc_A_prev : 1;
};
bool currentEffectAndPaletteInitialized = false;
uint8_t effectCurrentIndex = 0;
uint8_t effectPaletteIndex = 0;
uint8_t knownMode = 0;
uint8_t knownPalette = 0;
bool currentEffectAndPaletteInitialized;
uint8_t effectCurrentIndex;
uint8_t effectPaletteIndex;
uint8_t knownMode;
uint8_t knownPalette;
uint8_t currentCCT = 128;
byte presetHigh;
byte presetLow;
byte presetHigh = 0;
byte presetLow = 0;
bool applyToAll;
bool applyToAll = true;
bool initDone;
bool enabled;
bool initDone = false;
bool enabled = true;
bool usePcf8574;
int8_t pinIRQ;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
@ -179,12 +220,179 @@ private:
static const char _presetHigh[];
static const char _presetLow[];
static const char _applyToAll[];
static const char _pcf8574[];
static const char _pcfAddress[];
static const char _pcfINTpin[];
/**
* readPin() - read rotary encoder pin value
*/
byte readPin(uint8_t pin);
/**
* Sort the modes and palettes to the index arrays
* modes_alpha_indexes and palettes_alpha_indexes.
*/
void sortModesAndPalettes() {
void sortModesAndPalettes();
byte *re_initIndexArray(int numModes);
/**
* Return an array of mode or palette names from the JSON string.
* They don't end in '\0', they end in '"'.
*/
const char **re_findModeStrings(const char json[], int numModes);
/**
* Sort either the modes or the palettes using quicksort.
*/
void re_sortModes(const char **modeNames, byte *indexes, int count, int numSkip);
public:
RotaryEncoderUIUsermod()
: fadeAmount(5)
, buttonPressedTime(0)
, buttonWaitTime(0)
, buttonPressedBefore(false)
, buttonLongPressed(false)
, pinA(ENCODER_DT_PIN)
, pinB(ENCODER_CLK_PIN)
, pinC(ENCODER_SW_PIN)
, select_state(0)
, currentHue1(16)
, currentSat1(255)
, currentCCT(128)
, display(nullptr)
, modes_qstrings(nullptr)
, modes_alpha_indexes(nullptr)
, palettes_qstrings(nullptr)
, palettes_alpha_indexes(nullptr)
, currentEffectAndPaletteInitialized(false)
, effectCurrentIndex(0)
, effectPaletteIndex(0)
, knownMode(0)
, knownPalette(0)
, presetHigh(0)
, presetLow(0)
, applyToAll(true)
, initDone(false)
, enabled(true)
, usePcf8574(USE_PCF8574)
, pinIRQ(PCF8574_INT_PIN)
{}
/*
* 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_ROTARY_ENC_UI; }
/**
* Enable/Disable the usermod
*/
inline void enable(bool enable) { if (!(pinA<0 || pinB<0 || pinC<0)) enabled = enable; }
/**
* Get usermod enabled/disabled state
*/
inline bool isEnabled() { return enabled; }
/**
* setup() is called once at boot. WiFi is not yet connected at this point.
* You can use it to initialize variables, sensors or similar.
*/
void setup();
/**
* connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces
*/
//void connected();
/**
* loop() is called continuously. Here you can check for events, read sensors, etc.
*/
void loop();
#ifndef WLED_DISABLE_MQTT
//bool onMqttMessage(char* topic, char* payload);
//void onMqttConnect(bool sessionPresent);
#endif
/**
* handleButton() can be used to override default button behaviour. Returning true
* will prevent button working in a default way.
* Replicating button.cpp
*/
//bool handleButton(uint8_t b);
/**
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
*/
//void addToJsonInfo(JsonObject &root);
/**
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
//void addToJsonState(JsonObject &root);
/**
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
//void readFromJsonState(JsonObject &root);
/**
* provide the changeable values
*/
void addToConfig(JsonObject &root);
void appendConfigData();
/**
* restore the changeable values
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
*
* The function should return true if configuration was successfully loaded or false if there was no configuration.
*/
bool readFromConfig(JsonObject &root);
// custom methods
void displayNetworkInfo();
void findCurrentEffectAndPalette();
bool changeState(const char *stateName, byte markedLine, byte markedCol, byte glyph);
void lampUdated();
void changeBrightness(bool increase);
void changeEffect(bool increase);
void changeEffectSpeed(bool increase);
void changeEffectIntensity(bool increase);
void changeCustom(uint8_t par, bool increase);
void changePalette(bool increase);
void changeHue(bool increase);
void changeSat(bool increase);
void changePreset(bool increase);
void changeCCT(bool increase);
};
/**
* readPin() - read rotary encoder pin value
*/
byte RotaryEncoderUIUsermod::readPin(uint8_t pin) {
if (usePcf8574) {
if (pin >= 100) pin -= 100; // PCF I/O ports
return (pcfPortData>>pin) & 1;
} else {
return digitalRead(pin);
}
}
/**
* Sort the modes and palettes to the index arrays
* modes_alpha_indexes and palettes_alpha_indexes.
*/
void RotaryEncoderUIUsermod::sortModesAndPalettes() {
DEBUG_PRINTLN(F("Sorting modes and palettes."));
//modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount());
modes_qstrings = strip.getModeDataSrc();
modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
@ -200,7 +408,7 @@ private:
re_sortModes(palettes_qstrings, palettes_alpha_indexes, strip.getPaletteCount(), skipPaletteCount);
}
byte *re_initIndexArray(int numModes) {
byte *RotaryEncoderUIUsermod::re_initIndexArray(int numModes) {
byte *indexes = (byte *)malloc(sizeof(byte) * numModes);
for (byte i = 0; i < numModes; i++) {
indexes[i] = i;
@ -212,7 +420,7 @@ private:
* Return an array of mode or palette names from the JSON string.
* They don't end in '\0', they end in '"'.
*/
const char **re_findModeStrings(const char json[], int numModes) {
const char **RotaryEncoderUIUsermod::re_findModeStrings(const char json[], int numModes) {
const char **modeStrings = (const char **)malloc(sizeof(const char *) * numModes);
uint8_t modeIndex = 0;
bool insideQuotes = false;
@ -250,7 +458,7 @@ private:
/**
* Sort either the modes or the palettes using quicksort.
*/
void re_sortModes(const char **modeNames, byte *indexes, int count, int numSkip) {
void RotaryEncoderUIUsermod::re_sortModes(const char **modeNames, byte *indexes, int count, int numSkip) {
if (!modeNames) return;
listBeingSorted = modeNames;
qsort(indexes + numSkip, count - numSkip, sizeof(byte), re_qstringCmp);
@ -258,21 +466,37 @@ private:
}
public:
// public methods
/*
* 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()
void RotaryEncoderUIUsermod::setup()
{
DEBUG_PRINTLN(F("Usermod Rotary Encoder init."));
if (usePcf8574) {
if (i2c_sda < 0 || i2c_scl < 0 || pinA < 0 || pinB < 0 || pinC < 0) {
DEBUG_PRINTLN(F("I2C and/or PCF8574 pins unused, disabling."));
enabled = false;
return;
} else {
if (pinIRQ >= 0 && pinManager.allocatePin(pinIRQ, false, PinOwner::UM_RotaryEncoderUI)) {
pinMode(pinIRQ, INPUT_PULLUP);
attachInterrupt(pinIRQ, i2cReadingISR, FALLING); // RISING, FALLING, CHANGE, ONLOW, ONHIGH
DEBUG_PRINTLN(F("Interrupt attached."));
} else {
DEBUG_PRINTLN(F("Unable to allocate interrupt pin, disabling."));
pinIRQ = -1;
enabled = false;
return;
}
}
} else {
PinManagerPinType pins[3] = { { pinA, false }, { pinB, false }, { pinC, false } };
if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_RotaryEncoderUI)) {
// BUG: configuring this usermod with conflicting pins
// will cause it to de-allocate pins it does not own
// (at second config)
// This is the exact type of bug solved by pinManager
// tracking the owner tags....
pinA = pinB = pinC = -1;
enabled = false;
return;
@ -284,6 +508,7 @@ public:
pinMode(pinA, USERMOD_ROTARY_ENCODER_GPIO);
pinMode(pinB, USERMOD_ROTARY_ENCODER_GPIO);
pinMode(pinC, USERMOD_ROTARY_ENCODER_GPIO);
}
loopTime = millis();
@ -301,20 +526,11 @@ public:
#endif
initDone = true;
Enc_A = digitalRead(pinA); // Read encoder pins
Enc_B = digitalRead(pinB);
Enc_A = readPin(pinA); // Read encoder pins
Enc_B = readPin(pinB);
Enc_A_prev = Enc_A;
}
/*
* 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.
*
@ -325,10 +541,11 @@ public:
* 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()
void RotaryEncoderUIUsermod::loop()
{
if (!enabled || strip.isUpdating()) return;
if (!enabled) return;
unsigned long currentTime = millis(); // get the current elapsed time
if (strip.isUpdating() && ((currentTime - loopTime) < ENCODER_MAX_DELAY_MS)) return; // be nice, but not too nice
// Initialize effectCurrentIndex and effectPaletteIndex to
// current state. We do it here as (at least) effectCurrent
@ -339,14 +556,13 @@ public:
}
if (modes_alpha_indexes[effectCurrentIndex] != effectCurrent || palettes_alpha_indexes[effectPaletteIndex] != effectPalette) {
DEBUG_PRINTLN(F("Current mode or palette changed."));
currentEffectAndPaletteInitialized = false;
}
if (currentTime >= (loopTime + 2)) // 2ms since last check of encoder = 500Hz
{
loopTime = currentTime; // Updates loopTime
bool buttonPressed = !digitalRead(pinC); //0=pressed, 1=released
bool buttonPressed = !readPin(pinC); //0=pressed, 1=released
if (buttonPressed) {
if (!buttonPressedBefore) buttonPressedTime = currentTime;
buttonPressedBefore = true;
@ -413,8 +629,8 @@ public:
if (changedState) select_state = newState;
}
Enc_A = digitalRead(pinA); // Read encoder pins
Enc_B = digitalRead(pinB);
Enc_A = readPin(pinA); // Read encoder pins
Enc_B = readPin(pinB);
if ((Enc_A) && (!Enc_A_prev))
{ // A has gone from high to low
if (Enc_B == LOW) //changes to LOW so that then encoder registers a change at the very end of a pulse
@ -453,16 +669,18 @@ public:
}
}
Enc_A_prev = Enc_A; // Store value of A for next time
loopTime = currentTime; // Updates loopTime
}
}
void displayNetworkInfo() {
void RotaryEncoderUIUsermod::displayNetworkInfo() {
#ifdef USERMOD_FOUR_LINE_DISPLAY
display->networkOverlay(PSTR("NETWORK INFO"), 10000);
#endif
}
void findCurrentEffectAndPalette() {
void RotaryEncoderUIUsermod::findCurrentEffectAndPalette() {
DEBUG_PRINTLN(F("Finding current mode and palette."));
currentEffectAndPaletteInitialized = true;
for (uint8_t i = 0; i < strip.getModeCount(); i++) {
if (modes_alpha_indexes[i] == effectCurrent) {
@ -470,6 +688,7 @@ public:
break;
}
}
DEBUG_PRINTLN(F("Found current mode."));
for (uint8_t i = 0; i < strip.getPaletteCount(); i++) {
if (palettes_alpha_indexes[i] == effectPalette) {
@ -477,9 +696,10 @@ public:
break;
}
}
DEBUG_PRINTLN(F("Found palette."));
}
boolean changeState(const char *stateName, byte markedLine, byte markedCol, byte glyph) {
bool RotaryEncoderUIUsermod::changeState(const char *stateName, byte markedLine, byte markedCol, byte glyph) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display != nullptr) {
if (display->wakeDisplay()) {
@ -494,7 +714,7 @@ public:
return true;
}
void lampUdated() {
void RotaryEncoderUIUsermod::lampUdated() {
//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
//setValuesFromFirstSelectedSeg(); //to make transition work on main segment (should no longer be required)
@ -502,7 +722,7 @@ public:
updateInterfaces(CALL_MODE_BUTTON);
}
void changeBrightness(bool increase) {
void RotaryEncoderUIUsermod::changeBrightness(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
@ -519,7 +739,7 @@ public:
}
void changeEffect(bool increase) {
void RotaryEncoderUIUsermod::changeEffect(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
@ -548,7 +768,7 @@ public:
}
void changeEffectSpeed(bool increase) {
void RotaryEncoderUIUsermod::changeEffectSpeed(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
@ -576,7 +796,7 @@ public:
}
void changeEffectIntensity(bool increase) {
void RotaryEncoderUIUsermod::changeEffectIntensity(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
@ -604,7 +824,7 @@ public:
}
void changeCustom(uint8_t par, bool increase) {
void RotaryEncoderUIUsermod::changeCustom(uint8_t par, bool increase) {
uint8_t val = 0;
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
@ -649,7 +869,7 @@ public:
}
void changePalette(bool increase) {
void RotaryEncoderUIUsermod::changePalette(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
@ -678,7 +898,7 @@ public:
}
void changeHue(bool increase){
void RotaryEncoderUIUsermod::changeHue(bool increase){
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
@ -708,7 +928,7 @@ public:
#endif
}
void changeSat(bool increase){
void RotaryEncoderUIUsermod::changeSat(bool increase){
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
@ -737,7 +957,7 @@ public:
#endif
}
void changePreset(bool increase) {
void RotaryEncoderUIUsermod::changePreset(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
@ -769,7 +989,7 @@ public:
}
}
void changeCCT(bool increase){
void RotaryEncoderUIUsermod::changeCCT(bool increase){
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
@ -803,7 +1023,7 @@ public:
* Below it is shown how this could be used for e.g. a light sensor
*/
/*
void addToJsonInfo(JsonObject& root)
void RotaryEncoderUIUsermod::addToJsonInfo(JsonObject& root)
{
int reading = 20;
//this code adds "u":{"Light":[20," lux"]} to the info object
@ -820,7 +1040,7 @@ public:
* Values in the state object may be modified by connected clients
*/
/*
void addToJsonState(JsonObject &root)
void RotaryEncoderUIUsermod::addToJsonState(JsonObject &root)
{
//root["user0"] = userVar0;
}
@ -831,7 +1051,7 @@ public:
* Values in the state object may be modified by connected clients
*/
/*
void readFromJsonState(JsonObject &root)
void RotaryEncoderUIUsermod::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!"));
@ -841,7 +1061,7 @@ public:
/**
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
*/
void addToConfig(JsonObject &root) {
void RotaryEncoderUIUsermod::addToConfig(JsonObject &root) {
// we add JSON object: {"Rotary-Encoder":{"DT-pin":12,"CLK-pin":14,"SW-pin":13}}
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
top[FPSTR(_enabled)] = enabled;
@ -851,15 +1071,23 @@ public:
top[FPSTR(_presetLow)] = presetLow;
top[FPSTR(_presetHigh)] = presetHigh;
top[FPSTR(_applyToAll)] = applyToAll;
top[FPSTR(_pcf8574)] = usePcf8574;
top[FPSTR(_pcfAddress)] = addrPcf8574;
top[FPSTR(_pcfINTpin)] = pinIRQ;
DEBUG_PRINTLN(F("Rotary Encoder config saved."));
}
void RotaryEncoderUIUsermod::appendConfigData() {
oappend(SET_F("addInfo('Rotary-Encoder:PCF8574-address',1,'<i>(not hex!)</i>');"));
oappend(SET_F("d.extra.push({'Rotary-Encoder':{pin:[['P0',100],['P1',101],['P2',102],['P3',103],['P4',104],['P5',105],['P6',106],['P7',107]]}});"));
}
/**
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
*
* The function should return true if configuration was successfully loaded or false if there was no configuration.
*/
bool readFromConfig(JsonObject &root) {
bool RotaryEncoderUIUsermod::readFromConfig(JsonObject &root) {
// we look for JSON object: {"Rotary-Encoder":{"DT-pin":12,"CLK-pin":14,"SW-pin":13}}
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
@ -870,6 +1098,8 @@ public:
int8_t newDTpin = top[FPSTR(_DT_pin)] | pinA;
int8_t newCLKpin = top[FPSTR(_CLK_pin)] | pinB;
int8_t newSWpin = top[FPSTR(_SW_pin)] | pinC;
int8_t newIRQpin = top[FPSTR(_pcfINTpin)] | pinIRQ;
bool oldPcf8574 = usePcf8574;
presetHigh = top[FPSTR(_presetHigh)] | presetHigh;
presetLow = top[FPSTR(_presetLow)] | presetLow;
@ -879,6 +1109,9 @@ public:
enabled = top[FPSTR(_enabled)] | enabled;
applyToAll = top[FPSTR(_applyToAll)] | applyToAll;
usePcf8574 = top[FPSTR(_pcf8574)] | usePcf8574;
addrPcf8574 = top[FPSTR(_pcfAddress)] | addrPcf8574;
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
// first run: reading from cfg.json
@ -889,10 +1122,20 @@ public:
} else {
DEBUG_PRINTLN(F(" config (re)loaded."));
// changing parameters from settings page
if (pinA!=newDTpin || pinB!=newCLKpin || pinC!=newSWpin) {
if (pinA!=newDTpin || pinB!=newCLKpin || pinC!=newSWpin || pinIRQ!=newIRQpin) {
if (oldPcf8574) {
if (pinIRQ >= 0) {
detachInterrupt(pinIRQ);
pinManager.deallocatePin(pinIRQ, PinOwner::UM_RotaryEncoderUI);
DEBUG_PRINTLN(F("Deallocated old IRQ pin."));
}
pinIRQ = newIRQpin<100 ? newIRQpin : -1; // ignore PCF8574 pins
} else {
pinManager.deallocatePin(pinA, PinOwner::UM_RotaryEncoderUI);
pinManager.deallocatePin(pinB, PinOwner::UM_RotaryEncoderUI);
pinManager.deallocatePin(pinC, PinOwner::UM_RotaryEncoderUI);
DEBUG_PRINTLN(F("Deallocated old pins."));
}
pinA = newDTpin;
pinB = newCLKpin;
pinC = newSWpin;
@ -904,18 +1147,9 @@ public:
}
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_applyToAll)].isNull();
return !top[FPSTR(_pcfINTpin)].isNull();
}
/*
* 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_ROTARY_ENC_UI;
}
};
// strings to reduce flash memory usage (used more than twice)
const char RotaryEncoderUIUsermod::_name[] PROGMEM = "Rotary-Encoder";
@ -926,3 +1160,6 @@ 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";
const char RotaryEncoderUIUsermod::_pcf8574[] PROGMEM = "use-PCF8574";
const char RotaryEncoderUIUsermod::_pcfAddress[] PROGMEM = "PCF8574-address";
const char RotaryEncoderUIUsermod::_pcfINTpin[] PROGMEM = "PCF8574-INT-pin";

View File

@ -40,39 +40,39 @@ class WordClockUsermod : public Usermod
// Normal wiring
const int maskMinutes[14][maskSizeMinutes] =
{
{107, 108, 109, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // :00
{ 7, 8, 9, 10, 40, 41, 42, 43, -1, -1, -1, -1}, // :05 fünf nach
{ 11, 12, 13, 14, 40, 41, 42, 43, -1, -1, -1, -1}, // :10 zehn nach
{ 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1}, // :15 viertel
{ 15, 16, 17, 18, 19, 20, 21, 40, 41, 42, 43, -1}, // :20 zwanzig nach
{ 7, 8, 9, 10, 33, 34, 35, 44, 45, 46, 47, -1}, // :25 fünf vor halb
{ 44, 45, 46, 47, -1, -1, -1, -1, -1, -1, -1, -1}, // :30 halb
{ 7, 8, 9, 10, 40, 41, 42, 43, 44, 45, 46, 47}, // :35 fünf nach halb
{ 15, 16, 17, 18, 19, 20, 21, 33, 34, 35, -1, -1}, // :40 zwanzig vor
{ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1}, // :45 dreiviertel
{ 11, 12, 13, 14, 33, 34, 35, -1, -1, -1, -1, -1}, // :50 zehn vor
{ 7, 8, 9, 10, 33, 34, 35, -1, -1, -1, -1, -1}, // :55 fünf vor
{ 26, 27, 28, 29, 30, 31, 32, 40, 41, 42, 43, -1}, // :15 alternative viertel nach
{ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1} // :45 alternative viertel vor
{107, 108, 109, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 0 - 00
{ 7, 8, 9, 10, 40, 41, 42, 43, -1, -1, -1, -1}, // 1 - 05 fünf nach
{ 11, 12, 13, 14, 40, 41, 42, 43, -1, -1, -1, -1}, // 2 - 10 zehn nach
{ 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1}, // 3 - 15 viertel
{ 15, 16, 17, 18, 19, 20, 21, 40, 41, 42, 43, -1}, // 4 - 20 zwanzig nach
{ 7, 8, 9, 10, 33, 34, 35, 44, 45, 46, 47, -1}, // 5 - 25 fünf vor halb
{ 44, 45, 46, 47, -1, -1, -1, -1, -1, -1, -1, -1}, // 6 - 30 halb
{ 7, 8, 9, 10, 40, 41, 42, 43, 44, 45, 46, 47}, // 7 - 35 fünf nach halb
{ 15, 16, 17, 18, 19, 20, 21, 33, 34, 35, -1, -1}, // 8 - 40 zwanzig vor
{ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1}, // 9 - 45 dreiviertel
{ 11, 12, 13, 14, 33, 34, 35, -1, -1, -1, -1, -1}, // 10 - 50 zehn vor
{ 7, 8, 9, 10, 33, 34, 35, -1, -1, -1, -1, -1}, // 11 - 55 fünf vor
{ 26, 27, 28, 29, 30, 31, 32, 40, 41, 42, 43, -1}, // 12 - 15 alternative viertel nach
{ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1} // 13 - 45 alternative viertel vor
};
// Meander wiring
const int maskMinutesMea[14][maskSizeMinutesMea] =
{
{ 99, 100, 101, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // :00
{ 7, 8, 9, 10, 33, 34, 35, 36, -1, -1, -1, -1}, // :05 fünf nach
{ 18, 19, 20, 21, 33, 34, 35, 36, -1, -1, -1, -1}, // :10 zehn nach
{ 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1}, // :15 viertel
{ 11, 12, 13, 14, 15, 16, 17, 33, 34, 35, 36, -1}, // :20 zwanzig nach
{ 7, 8, 9, 10, 41, 42, 43, 44, 45, 46, 47, -1}, // :25 fünf vor halb
{ 44, 45, 46, 47, -1, -1, -1, -1, -1, -1, -1, -1}, // :30 halb
{ 7, 8, 9, 10, 33, 34, 35, 36, 44, 45, 46, 47}, // :35 fünf nach halb
{ 11, 12, 13, 14, 15, 16, 17, 41, 42, 43, -1, -1}, // :40 zwanzig vor
{ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1}, // :45 dreiviertel
{ 18, 19, 20, 21, 41, 42, 43, -1, -1, -1, -1, -1}, // :50 zehn vor
{ 7, 8, 9, 10, 41, 42, 43, -1, -1, -1, -1, -1}, // :55 fünf vor
{ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1}, // :15 alternative viertel nach
{ 26, 27, 28, 29, 30, 31, 32, 41, 42, 43, -1, -1} // :45 alternative viertel vor
{ 99, 100, 101, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 0 - 00
{ 7, 8, 9, 10, 33, 34, 35, 36, -1, -1, -1, -1}, // 1 - 05 fünf nach
{ 18, 19, 20, 21, 33, 34, 35, 36, -1, -1, -1, -1}, // 2 - 10 zehn nach
{ 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1}, // 3 - 15 viertel
{ 11, 12, 13, 14, 15, 16, 17, 33, 34, 35, 36, -1}, // 4 - 20 zwanzig nach
{ 7, 8, 9, 10, 41, 42, 43, 44, 45, 46, 47, -1}, // 5 - 25 fünf vor halb
{ 44, 45, 46, 47, -1, -1, -1, -1, -1, -1, -1, -1}, // 6 - 30 halb
{ 7, 8, 9, 10, 33, 34, 35, 36, 44, 45, 46, 47}, // 7 - 35 fünf nach halb
{ 11, 12, 13, 14, 15, 16, 17, 41, 42, 43, -1, -1}, // 8 - 40 zwanzig vor
{ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1}, // 9 - 45 dreiviertel
{ 18, 19, 20, 21, 41, 42, 43, -1, -1, -1, -1, -1}, // 10 - 50 zehn vor
{ 7, 8, 9, 10, 41, 42, 43, -1, -1, -1, -1, -1}, // 11 - 55 fünf vor
{ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1}, // 12 - 15 alternative viertel nach
{ 26, 27, 28, 29, 30, 31, 32, 41, 42, 43, -1, -1} // 13 - 45 alternative viertel vor
};
@ -284,12 +284,13 @@ class WordClockUsermod : public Usermod
setHours(hours + 1, false);
break;
case 9:
// viertel vor bzw dreiviertel
// viertel vor
if (nord) {
setMinutes(9);
setMinutes(13);
}
// dreiviertel
else {
setMinutes(12);
setMinutes(9);
}
setHours(hours + 1, false);
break;
@ -422,12 +423,18 @@ class WordClockUsermod : public Usermod
*/
void addToConfig(JsonObject& root)
{
JsonObject top = root.createNestedObject("WordClockUsermod");
top["active"] = usermodActive;
top["displayItIs"] = displayItIs;
top["ledOffset"] = ledOffset;
top["Meander wiring?"] = meander;
top["Norddeutsch"] = nord;
JsonObject top = root.createNestedObject(F("WordClockUsermod"));
top[F("active")] = usermodActive;
top[F("displayItIs")] = displayItIs;
top[F("ledOffset")] = ledOffset;
top[F("Meander wiring?")] = meander;
top[F("Norddeutsch")] = nord;
}
void appendConfigData()
{
oappend(SET_F("addInfo('WordClockUsermod:ledOffset', 1, 'Number of LEDs before the letters');"));
oappend(SET_F("addInfo('WordClockUsermod:Norddeutsch', 1, 'Viertel vor instead of Dreiviertel');"));
}
/*
@ -450,15 +457,15 @@ class WordClockUsermod : public Usermod
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
JsonObject top = root["WordClockUsermod"];
JsonObject top = root[F("WordClockUsermod")];
bool configComplete = !top.isNull();
configComplete &= getJsonValue(top["active"], usermodActive);
configComplete &= getJsonValue(top["displayItIs"], displayItIs);
configComplete &= getJsonValue(top["ledOffset"], ledOffset);
configComplete &= getJsonValue(top["Meander wiring?"], meander);
configComplete &= getJsonValue(top["Norddeutsch"], nord);
configComplete &= getJsonValue(top[F("active")], usermodActive);
configComplete &= getJsonValue(top[F("displayItIs")], displayItIs);
configComplete &= getJsonValue(top[F("ledOffset")], ledOffset);
configComplete &= getJsonValue(top[F("Meander wiring?")], meander);
configComplete &= getJsonValue(top[F("Norddeutsch")], nord);
return configComplete;
}

View File

@ -1,25 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28010.2046
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wled00", "wled00\wled00.vcxproj", "{C5F80730-F44F-4478-BDAE-6634EFC2CA88}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.ActiveCfg = Debug|Win32
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.Build.0 = Debug|Win32
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.ActiveCfg = Release|Win32
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9A679C2B-61D3-400B-B96F-06E604E9CED2}
EndGlobalSection
EndGlobal

View File

@ -278,7 +278,7 @@ uint16_t mode_random_color(void) {
SEGMENT.fill(color_blend(SEGMENT.color_wheel(SEGENV.aux1), SEGMENT.color_wheel(SEGENV.aux0), fade));
return FRAMETIME;
}
static const char _data_FX_MODE_RANDOM_COLOR[] PROGMEM = "Random Colors@!,Fade time;;!";
static const char _data_FX_MODE_RANDOM_COLOR[] PROGMEM = "Random Colors@!,Fade time;;!;01";
/*
@ -433,7 +433,7 @@ uint16_t mode_rainbow(void) {
return FRAMETIME;
}
static const char _data_FX_MODE_RAINBOW[] PROGMEM = "Colorloop@!,Saturation;;!";
static const char _data_FX_MODE_RAINBOW[] PROGMEM = "Colorloop@!,Saturation;;!;01";
/*
@ -1247,10 +1247,10 @@ uint16_t mode_rain() {
if (SEGENV.call && SEGENV.step > SPEED_FORMULA_L) {
SEGENV.step = 1;
if (strip.isMatrix) {
uint32_t ctemp[width];
for (int i = 0; i<width; i++) ctemp[i] = SEGMENT.getPixelColorXY(i, height-1);
SEGMENT.move(6,1); // move all pixels down
for (int i = 0; i<width; i++) SEGMENT.setPixelColorXY(i, 0, ctemp[i]); // wrap around
//uint32_t ctemp[width];
//for (int i = 0; i<width; i++) ctemp[i] = SEGMENT.getPixelColorXY(i, height-1);
SEGMENT.move(6, 1, true); // move all pixels down
//for (int i = 0; i<width; i++) SEGMENT.setPixelColorXY(i, 0, ctemp[i]); // wrap around
SEGENV.aux0 = (SEGENV.aux0 % width) + (SEGENV.aux0 / width + 1) * width;
SEGENV.aux1 = (SEGENV.aux1 % width) + (SEGENV.aux1 / width + 1) * width;
} else {
@ -1978,7 +1978,7 @@ uint16_t mode_fire_2012() {
if (!SEGENV.allocateData(strips * SEGLEN)) return mode_static(); //allocation failed
byte* heat = SEGENV.data;
const uint32_t it = strip.now >> 6; //div 64
const uint32_t it = strip.now >> 5; //div 32
struct virtualStrip {
static void runStrip(uint16_t stripNr, byte* heat, uint32_t it) {
@ -1987,28 +1987,24 @@ uint16_t mode_fire_2012() {
// Step 1. Cool down every cell a little
for (int i = 0; i < SEGLEN; i++) {
uint8_t cool = (it != SEGENV.step) ? random8((((20 + SEGMENT.speed/3) * 16) / SEGLEN)+2) : random(8);
uint8_t minTemp = 0;
if (i<ignition) {
minTemp = (ignition-i)/4 + 16; // and should not become black
}
uint8_t cool = (it != SEGENV.step) ? random8((((20 + SEGMENT.speed/3) * 16) / SEGLEN)+2) : random(4);
uint8_t minTemp = (i<ignition) ? (ignition-i)/4 + 16 : 0; // should not become black in ignition area
uint8_t temp = qsub8(heat[i], cool);
heat[i] = temp<minTemp ? minTemp : temp;
}
if (it != SEGENV.step)
{
if (it != SEGENV.step) {
// Step 2. Heat from each cell drifts 'up' and diffuses a little
for (int k = SEGLEN -1; k > 1; k--) {
heat[k] = (heat[k - 1] + (heat[k - 2]<<1) ) / 3; // heat[k-2] multiplied by 2
}
}
// Step 3. Randomly ignite new 'sparks' of heat near the bottom
if (random8() <= SEGMENT.intensity) {
uint8_t y = random8(ignition);
uint8_t boost = (32+SEGMENT.custom3*2) * (2*ignition-y) / (2*ignition);
heat[y] = qadd8(heat[y], random8(64+boost,128+boost));
uint8_t boost = (17+SEGMENT.custom3) * (ignition - y/2) / ignition; // integer math!
heat[y] = qadd8(heat[y], random8(96+2*boost,207+boost));
}
}
// Step 4. Map from heat cells to LED colors
@ -2028,7 +2024,7 @@ uint16_t mode_fire_2012() {
return FRAMETIME;
}
static const char _data_FX_MODE_FIRE_2012[] PROGMEM = "Fire 2012@Cooling,Spark rate,,,Boost;;!;1;sx=120,ix=64,m12=1"; // bars
static const char _data_FX_MODE_FIRE_2012[] PROGMEM = "Fire 2012@Cooling,Spark rate,,,Boost;;!;1;sx=64,ix=160,m12=1"; // bars
// ColorWavesWithPalettes by Mark Kriegsman: https://gist.github.com/kriegsman/8281905786e8b2632aeb
@ -2765,7 +2761,7 @@ uint16_t mode_spots()
{
return spots_base((255 - SEGMENT.speed) << 8);
}
static const char _data_FX_MODE_SPOTS[] PROGMEM = "Spots@,Width,,,,,Overlay;!,!;!";
static const char _data_FX_MODE_SPOTS[] PROGMEM = "Spots@Spread,Width,,,,,Overlay;!,!;!";
//Intensity slider sets number of "lights", LEDs per light fade in and out
@ -2809,7 +2805,7 @@ uint16_t mode_bouncing_balls(void) {
// number of balls based on intensity setting to max of 7 (cycles colors)
// non-chosen color is a random color
uint16_t numBalls = (SEGMENT.intensity * (maxNumBalls - 1)) / 255 + 1; // minimum 1 ball
const float gravity = -9.81; // standard value of gravity
const float gravity = -9.81f; // standard value of gravity
const bool hasCol2 = SEGCOLOR(2);
const unsigned long time = millis();
@ -4109,7 +4105,7 @@ uint16_t mode_dancing_shadows(void)
spotlights[i].type = random8(SPOT_TYPES_COUNT);
}
uint32_t color = SEGMENT.color_from_palette(spotlights[i].colorIdx, false, false, 0);
uint32_t color = SEGMENT.color_from_palette(spotlights[i].colorIdx, false, false, 255);
int start = spotlights[i].position;
if (spotlights[i].width <= 1) {
@ -4179,11 +4175,9 @@ static const char _data_FX_MODE_DANCING_SHADOWS[] PROGMEM = "Dancing Shadows@!,#
By Stefan Seegel
*/
uint16_t mode_washing_machine(void) {
float speed = tristate_square8(strip.now >> 7, 90, 15);
float quot = 32.0f - ((float)SEGMENT.speed / 16.0f);
speed /= quot;
int speed = tristate_square8(strip.now >> 7, 90, 15);
SEGENV.step += (speed * 128.0f);
SEGENV.step += (speed * 2048) / (512 - SEGMENT.speed);
for (int i = 0; i < SEGLEN; i++) {
uint8_t col = sin8(((SEGMENT.intensity / 25 + 1) * 255 * i / SEGLEN) + (SEGENV.step >> 7));
@ -4597,7 +4591,7 @@ uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulma
}
SEGMENT.fadeToBlackBy(16 + (SEGMENT.speed>>3)); // create fading trails
float t = (float)(millis())/128; // timebase
unsigned long t = millis()/128; // timebase
// outer stars
for (size_t i = 0; i < 8; i++) {
x = beatsin8(SEGMENT.custom1>>3, 0, cols - 1, 0, ((i % 2) ? 128 : 0) + t * i);
@ -4653,8 +4647,8 @@ uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.so
byte ysteps = abs8(x2 - y2) + 1;
byte steps = xsteps >= ysteps ? xsteps : ysteps;
//Draw gradient line
for (size_t i = 1; i <= steps; i++) {
uint8_t rate = i * 255 / steps;
for (size_t j = 1; j <= steps; j++) {
uint8_t rate = j * 255 / steps;
byte dx = lerp8by8(x1, y1, rate);
byte dy = lerp8by8(x2, y2, rate);
//SEGMENT.setPixelColorXY(dx, dy, grad ? color.nscale8_video(255-rate) : color); // use addPixelColorXY for different look
@ -5101,21 +5095,22 @@ uint16_t mode_2DLissajous(void) { // By: Andrew Tuline
const uint16_t rows = SEGMENT.virtualHeight();
SEGMENT.fadeToBlackBy(SEGMENT.intensity);
uint_fast16_t phase = (millis() * (1 + SEGENV.custom3)) /32; // allow user to control rotation speed
//for (int i=0; i < 4*(cols+rows); i ++) {
for (int i=0; i < 256; i ++) {
//float xlocn = float(sin8(now/4+i*(SEGMENT.speed>>5))) / 255.0f;
//float ylocn = float(cos8(now/4+i*2)) / 255.0f;
uint8_t xlocn = sin8(millis()/4+i*(SEGMENT.speed>>5));
uint8_t ylocn = cos8(millis()/4+i*2);
xlocn = map(xlocn,0,255,0,cols-1);
ylocn = map(ylocn,0,255,0,rows-1);
SEGMENT.setPixelColorXY(xlocn, ylocn, SEGMENT.color_from_palette(millis()/100+i, false, PALETTE_SOLID_WRAP, 0));
uint_fast8_t xlocn = sin8(phase/2 + (i*SEGMENT.speed)/32);
uint_fast8_t ylocn = cos8(phase/2 + i*2);
xlocn = (cols < 2) ? 1 : (map(2*xlocn, 0,511, 0,2*(cols-1)) +1) /2; // softhack007: "(2* ..... +1) /2" for proper rounding
ylocn = (rows < 2) ? 1 : (map(2*ylocn, 0,511, 0,2*(rows-1)) +1) /2; // "rows > 1" is needed to avoid div/0 in map()
SEGMENT.setPixelColorXY((uint8_t)xlocn, (uint8_t)ylocn, SEGMENT.color_from_palette(millis()/100+i, false, PALETTE_SOLID_WRAP, 0));
}
return FRAMETIME;
} // mode_2DLissajous()
static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,Fade rate;!;!;2";
static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,Fade rate,,,Speed;!;!;2;;c3=15";
///////////////////////
@ -5279,7 +5274,7 @@ uint16_t mode_2DPlasmaball(void) { // By: Stepko https://edito
SEGMENT.fadeToBlackBy(SEGMENT.custom1>>2);
float t = millis() / (33 - SEGMENT.speed/8);
uint_fast32_t t = (millis() * 8) / (256 - SEGMENT.speed); // optimized to avoid float
for (int i = 0; i < cols; i++) {
uint16_t thisVal = inoise8(i * 30, t, t);
uint16_t thisMax = map(thisVal, 0, 255, 0, cols-1);
@ -5526,22 +5521,30 @@ uint16_t mode_2Dtartan(void) { // By: Elliott Kember https://editor.so
SEGMENT.fill(BLACK);
}
uint8_t hue;
uint8_t hue, bri;
size_t intensity;
int offsetX = beatsin16(3, -360, 360);
int offsetY = beatsin16(2, -360, 360);
int sharpness = SEGMENT.custom3 / 8; // 0-3
for (int x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) {
hue = x * beatsin16(10, 1, 10) + offsetY;
SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, hue, sin8(x * SEGMENT.speed + offsetX) * sin8(x * SEGMENT.speed + offsetX) / 255, LINEARBLEND));
intensity = bri = sin8(x * SEGMENT.speed/2 + offsetX);
for (int i=0; i<sharpness; i++) intensity *= bri;
intensity >>= 8*sharpness;
SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, hue, intensity, LINEARBLEND));
hue = y * 3 + offsetX;
SEGMENT.addPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, hue, sin8(y * SEGMENT.intensity + offsetY) * sin8(y * SEGMENT.intensity + offsetY) / 255, LINEARBLEND));
intensity = bri = sin8(y * SEGMENT.intensity/2 + offsetY);
for (int i=0; i<sharpness; i++) intensity *= bri;
intensity >>= 8*sharpness;
SEGMENT.addPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, hue, intensity, LINEARBLEND));
}
}
return FRAMETIME;
} // mode_2DTartan()
static const char _data_FX_MODE_2DTARTAN[] PROGMEM = "Tartan@X scale,Y scale;;!;2";
static const char _data_FX_MODE_2DTARTAN[] PROGMEM = "Tartan@X scale,Y scale,,,Sharpness;;!;2";
/////////////////////////
@ -5874,11 +5877,17 @@ uint16_t mode_2Dscrollingtext(void) {
case 4: letterWidth = 7; letterHeight = 9; break;
case 5: letterWidth = 5; letterHeight = 12; break;
}
const bool zero = SEGMENT.check3;
const int yoffset = map(SEGMENT.intensity, 0, 255, -rows/2, rows/2) + (rows-letterHeight)/2;
char text[33] = {'\0'};
char text[WLED_MAX_SEGNAME_LEN+1] = {'\0'};
if (SEGMENT.name) for (size_t i=0,j=0; i<strlen(SEGMENT.name); i++) if (SEGMENT.name[i]>31 && SEGMENT.name[i]<128) text[j++] = SEGMENT.name[i];
if (!strlen(text) || !strncmp_P(text,PSTR("#DATE"),5) || !strncmp_P(text,PSTR("#DDMM"),5) || !strncmp_P(text,PSTR("#MMDD"),5) || !strncmp_P(text,PSTR("#TIME"),5) || !strncmp_P(text,PSTR("#HHMM"),5)) { // fallback if empty segment name: display date and time
if (!strlen(text)
|| !strncmp_P(text,PSTR("#DATE"),5)
|| !strncmp_P(text,PSTR("#DDMM"),5)
|| !strncmp_P(text,PSTR("#MMDD"),5)
|| !strncmp_P(text,PSTR("#TIME"),5)
|| !strncmp_P(text,PSTR("#HHMM"),5)) { // fallback if empty segment name: display date and time
char sec[5];
byte AmPmHour = hour(localTime);
boolean isitAM = true;
@ -5888,12 +5897,12 @@ uint16_t mode_2Dscrollingtext(void) {
}
if (useAMPM) sprintf_P(sec, PSTR(" %2s"), (isitAM ? "AM" : "PM"));
else sprintf_P(sec, PSTR(":%02d"), second(localTime));
if (!strncmp_P(text,PSTR("#DATE"),5)) sprintf_P(text, PSTR("%d.%d.%d"), day(localTime), month(localTime), year(localTime));
else if (!strncmp_P(text,PSTR("#DDMM"),5)) sprintf_P(text, PSTR("%d.%d"), day(localTime), month(localTime));
else if (!strncmp_P(text,PSTR("#MMDD"),5)) sprintf_P(text, PSTR("%d/%d"), month(localTime), day(localTime));
else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec);
else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, PSTR("%2d:%02d"), AmPmHour, minute(localTime));
else sprintf_P(text, PSTR("%s %d, %d %2d:%02d%s"), monthShortStr(month(localTime)), day(localTime), year(localTime), AmPmHour, minute(localTime), sec);
if (!strncmp_P(text,PSTR("#DATE"),5)) sprintf_P(text, zero?PSTR("%02d.%02d.%04d"):PSTR("%d.%d.%d"), day(localTime), month(localTime), year(localTime));
else if (!strncmp_P(text,PSTR("#DDMM"),5)) sprintf_P(text, zero?PSTR("%02d.%02d"):PSTR("%d.%d"), day(localTime), month(localTime));
else if (!strncmp_P(text,PSTR("#MMDD"),5)) sprintf_P(text, zero?PSTR("%02d/%02d"):PSTR("%d/%d"), month(localTime), day(localTime));
else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, zero?PSTR("%02d:%02d%s"):PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec);
else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, zero?PSTR("%02d:%02d"):PSTR("%d:%02d"), AmPmHour, minute(localTime));
else sprintf_P(text, zero?PSTR("%s %02d, %04d %02d:%02d%s"):PSTR("%s %d, %d %d:%02d%s"), monthShortStr(month(localTime)), day(localTime), year(localTime), AmPmHour, minute(localTime), sec);
}
const int numberOfLetters = strlen(text);
@ -5920,7 +5929,7 @@ uint16_t mode_2Dscrollingtext(void) {
return FRAMETIME;
}
static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,,Gradient,Overlay;!,!,Gradient;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0";
static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,,Gradient,Overlay,0;!,!,Gradient;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0";
////////////////////////////
@ -7333,7 +7342,7 @@ static const char _data_FX_MODE_2DAKEMI[] PROGMEM = "Akemi@Color speed,Dance;Hea
// Distortion waves - ldirko
// https://editor.soulmatelights.com/gallery/1089-distorsion-waves
// apated for WLD by @blazoncek
// adapted for WLED by @blazoncek
uint16_t mode_2Ddistortionwaves() {
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
@ -7382,7 +7391,198 @@ uint16_t mode_2Ddistortionwaves() {
return FRAMETIME;
}
static const char _data_FX_MODE_2DDISTORTIONWAVES[] PROGMEM = "Distortion Waves@!,Scale;;;2;";
static const char _data_FX_MODE_2DDISTORTIONWAVES[] PROGMEM = "Distortion Waves@!,Scale;;;2";
//Soap
//@Stepko
//Idea from https://www.youtube.com/watch?v=DiHBgITrZck&ab_channel=StefanPetrick
// adapted for WLED by @blazoncek
uint16_t mode_2Dsoap() {
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
const size_t dataSize = SEGMENT.width() * SEGMENT.height() * sizeof(uint8_t); // prevent reallocation if mirrored or grouped
if (!SEGENV.allocateData(dataSize + sizeof(uint32_t)*3)) return mode_static(); //allocation failed
uint8_t *noise3d = reinterpret_cast<uint8_t*>(SEGENV.data);
uint32_t *noise32_x = reinterpret_cast<uint32_t*>(SEGENV.data + dataSize);
uint32_t *noise32_y = reinterpret_cast<uint32_t*>(SEGENV.data + dataSize + sizeof(uint32_t));
uint32_t *noise32_z = reinterpret_cast<uint32_t*>(SEGENV.data + dataSize + sizeof(uint32_t)*2);
const uint32_t scale32_x = 160000U/cols;
const uint32_t scale32_y = 160000U/rows;
const uint32_t mov = MIN(cols,rows)*(SEGMENT.speed+2)/2;
const uint8_t smoothness = MIN(250,SEGMENT.intensity); // limit as >250 produces very little changes
// init
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
*noise32_x = random16();
*noise32_y = random16();
*noise32_z = random16();
} else {
*noise32_x += mov;
*noise32_y += mov;
*noise32_z += mov;
}
for (int i = 0; i < cols; i++) {
int32_t ioffset = scale32_x * (i - cols / 2);
for (int j = 0; j < rows; j++) {
int32_t joffset = scale32_y * (j - rows / 2);
uint8_t data = inoise16(*noise32_x + ioffset, *noise32_y + joffset, *noise32_z) >> 8;
noise3d[XY(i,j)] = scale8(noise3d[XY(i,j)], smoothness) + scale8(data, 255 - smoothness);
}
}
// init also if dimensions changed
if (SEGENV.call == 0 || SEGMENT.aux0 != cols || SEGMENT.aux1 != rows) {
SEGMENT.aux0 = cols;
SEGMENT.aux1 = rows;
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
SEGMENT.setPixelColorXY(i, j, ColorFromPalette(SEGPALETTE,~noise3d[XY(i,j)]*3));
}
}
}
int zD;
int zF;
int amplitude;
int8_t shiftX = 0; //(SEGMENT.custom1 - 128) / 4;
int8_t shiftY = 0; //(SEGMENT.custom2 - 128) / 4;
amplitude = (cols >= 16) ? (cols-8)/8 : 1;
for (int y = 0; y < rows; y++) {
int amount = ((int)noise3d[XY(0,y)] - 128) * 2 * amplitude + 256*shiftX;
int delta = abs(amount) >> 8;
int fraction = abs(amount) & 255;
for (int x = 0; x < cols; x++) {
if (amount < 0) {
zD = x - delta;
zF = zD - 1;
} else {
zD = x + delta;
zF = zD + 1;
}
CRGB PixelA = CRGB::Black;
if ((zD >= 0) && (zD < cols)) PixelA = SEGMENT.getPixelColorXY(zD, y);
else PixelA = ColorFromPalette(SEGPALETTE, ~noise3d[XY(abs(zD),y)]*3);
CRGB PixelB = CRGB::Black;
if ((zF >= 0) && (zF < cols)) PixelB = SEGMENT.getPixelColorXY(zF, y);
else PixelB = ColorFromPalette(SEGPALETTE, ~noise3d[XY(abs(zF),y)]*3);
CRGB pix = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction)));
SEGMENT.setPixelColorXY(x, y, pix);
}
}
amplitude = (rows >= 16) ? (rows-8)/8 : 1;
for (int x = 0; x < cols; x++) {
int amount = ((int)noise3d[XY(x,0)] - 128) * 2 * amplitude + 256*shiftY;
int delta = abs(amount) >> 8;
int fraction = abs(amount) & 255;
for (int y = 0; y < rows; y++) {
if (amount < 0) {
zD = y - delta;
zF = zD - 1;
} else {
zD = y + delta;
zF = zD + 1;
}
CRGB PixelA = CRGB::Black;
if ((zD >= 0) && (zD < rows)) PixelA = SEGMENT.getPixelColorXY(x, zD);
else PixelA = ColorFromPalette(SEGPALETTE, ~noise3d[XY(x,abs(zD))]*3);
CRGB PixelB = CRGB::Black;
if ((zF >= 0) && (zF < rows)) PixelB = SEGMENT.getPixelColorXY(x, zF);
else PixelB = ColorFromPalette(SEGPALETTE, ~noise3d[XY(x,abs(zF))]*3);
CRGB pix = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction)));
SEGMENT.setPixelColorXY(x, y, pix);
}
}
return FRAMETIME;
}
static const char _data_FX_MODE_2DSOAP[] PROGMEM = "Soap@!,Smoothness;;!;2";
//Idea from https://www.youtube.com/watch?v=HsA-6KIbgto&ab_channel=GreatScott%21
//Octopus (https://editor.soulmatelights.com/gallery/671-octopus)
//Stepko and Sutaburosu
// adapted for WLED by @blazoncek
uint16_t mode_2Doctopus() {
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
const uint8_t mapp = 180 / MAX(cols,rows);
typedef struct {
uint8_t angle;
uint8_t radius;
} map_t;
const size_t dataSize = SEGMENT.width() * SEGMENT.height() * sizeof(map_t); // prevent reallocation if mirrored or grouped
if (!SEGENV.allocateData(dataSize + 2)) return mode_static(); //allocation failed
map_t *rMap = reinterpret_cast<map_t*>(SEGENV.data);
uint8_t *offsX = reinterpret_cast<uint8_t*>(SEGENV.data + dataSize);
uint8_t *offsY = reinterpret_cast<uint8_t*>(SEGENV.data + dataSize + 1);
// re-init if SEGMENT dimensions or offset changed
if (SEGENV.call == 0 || SEGENV.aux0 != cols || SEGENV.aux1 != rows || SEGMENT.custom1 != *offsX || SEGMENT.custom2 != *offsY) {
SEGENV.step = 0; // t
SEGENV.aux0 = cols;
SEGENV.aux1 = rows;
*offsX = SEGMENT.custom1;
*offsY = SEGMENT.custom2;
const uint8_t C_X = cols / 2 + (SEGMENT.custom1 - 128)*cols/255;
const uint8_t C_Y = rows / 2 + (SEGMENT.custom2 - 128)*rows/255;
for (int x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) {
rMap[XY(x, y)].angle = 40.7436f * atan2f(y - C_Y, x - C_X); // avoid 128*atan2()/PI
rMap[XY(x, y)].radius = hypotf(x - C_X, y - C_Y) * mapp; //thanks Sutaburosu
}
}
}
SEGENV.step += SEGMENT.speed / 32 + 1; // 1-4 range
for (int x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) {
byte angle = rMap[XY(x,y)].angle;
byte radius = rMap[XY(x,y)].radius;
//CRGB c = CHSV(SEGENV.step / 2 - radius, 255, sin8(sin8((angle * 4 - radius) / 4 + SEGENV.step) + radius - SEGENV.step * 2 + angle * (SEGMENT.custom3/3+1)));
uint16_t intensity = sin8(sin8((angle * 4 - radius) / 4 + SEGENV.step/2) + radius - SEGENV.step + angle * (SEGMENT.custom3/4+1));
intensity = map(intensity*intensity, 0, 65535, 0, 255); // add a bit of non-linearity for cleaner display
CRGB c = ColorFromPalette(SEGPALETTE, SEGENV.step / 2 - radius, intensity);
SEGMENT.setPixelColorXY(x, y, c);
}
}
return FRAMETIME;
}
static const char _data_FX_MODE_2DOCTOPUS[] PROGMEM = "Octopus@!,,Offset X,Offset Y,Legs;;!;2;";
//Waving Cell
//@Stepko (https://editor.soulmatelights.com/gallery/1704-wavingcells)
// adapted for WLED by @blazoncek
uint16_t mode_2Dwavingcell() {
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
uint32_t t = millis()/(257-SEGMENT.speed);
uint8_t aX = SEGMENT.custom1/16 + 9;
uint8_t aY = SEGMENT.custom2/16 + 1;
uint8_t aZ = SEGMENT.custom3 + 1;
for (int x = 0; x < cols; x++) for (int y = 0; y <rows; y++)
SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, ((sin8((x*aX)+sin8((y+t)*aY))+cos8(y*aZ))+1)+t));
return FRAMETIME;
}
static const char _data_FX_MODE_2DWAVINGCELL[] PROGMEM = "Waving Cell@!,,Amplitude 1,Amplitude 2,Amplitude 3;;!;2";
#endif // WLED_DISABLE_2D
@ -7617,6 +7817,9 @@ void WS2812FX::setupEffectData() {
addEffect(FX_MODE_2DSINDOTS, &mode_2DSindots, _data_FX_MODE_2DSINDOTS);
addEffect(FX_MODE_2DDNASPIRAL, &mode_2DDNASpiral, _data_FX_MODE_2DDNASPIRAL);
addEffect(FX_MODE_2DBLACKHOLE, &mode_2DBlackHole, _data_FX_MODE_2DBLACKHOLE);
addEffect(FX_MODE_2DSOAP, &mode_2Dsoap, _data_FX_MODE_2DSOAP);
addEffect(FX_MODE_2DOCTOPUS, &mode_2Doctopus, _data_FX_MODE_2DOCTOPUS);
addEffect(FX_MODE_2DWAVINGCELL, &mode_2Dwavingcell, _data_FX_MODE_2DWAVINGCELL);
addEffect(FX_MODE_2DAKEMI, &mode_2DAkemi, _data_FX_MODE_2DAKEMI); // audio
#endif // WLED_DISABLE_2D

View File

@ -254,7 +254,10 @@
#define FX_MODE_2DBLOBS 121 //gap fill
#define FX_MODE_2DSCROLLTEXT 122 //gap fill
#define FX_MODE_2DDRIFTROSE 123 //gap fill
#define FX_MODE_2DDISTORTIONWAVES 124
#define FX_MODE_2DDISTORTIONWAVES 124 //gap fill
#define FX_MODE_2DSOAP 125 //gap fill
#define FX_MODE_2DOCTOPUS 126 //gap fill
#define FX_MODE_2DWAVINGCELL 127 //gap fill
// WLED-SR effects (SR compatible IDs !!!)
#define FX_MODE_PIXELS 128
@ -350,7 +353,8 @@ typedef struct Segment {
bool mirror_y : 1; // 8 : mirrored Y (2D)
bool transpose : 1; // 9 : transposed (2D, swapped X & Y)
uint8_t map1D2D : 3; // 10-12 : mapping for 1D effect on 2D (0-use as strip, 1-expand vertically, 2-circular/arc, 3-rectangular/corner, ...)
uint8_t soundSim : 3; // 13-15 : 0-7 sound simulation types
uint8_t soundSim : 1; // 13 : 0-1 sound simulation types ("soft" & "hard" or "on"/"off")
uint8_t set : 2; // 14-15 : 0-3 UI segment sets/groups
};
};
uint8_t grouping, spacing;
@ -483,7 +487,7 @@ typedef struct Segment {
//if (leds) Serial.printf(" [%u]", length()*sizeof(CRGB));
//Serial.println();
//#endif
if (!Segment::_globalLeds && leds) free(leds);
if (!Segment::_globalLeds && leds) { free(leds); leds = nullptr;} // reset to nullptr, to avoid race conditions
if (name) delete[] name;
if (_t) delete _t;
deallocateData();
@ -503,8 +507,8 @@ typedef struct Segment {
inline bool hasRGB(void) const { return _isRGB; }
inline bool hasWhite(void) const { return _hasW; }
inline bool isCCT(void) const { return _isCCT; }
inline uint16_t width(void) const { return stop - start; } // segment width in physical pixels (length if 1D)
inline uint16_t height(void) const { return stopY - startY; } // segment height (if 2D) in physical pixels
inline uint16_t width(void) const { return (stop > start) ? (stop - start) : 0; } // segment width in physical pixels (length if 1D)
inline uint16_t height(void) const { return (stopY > startY) ? (stopY - startY) : 0; } // segment height (if 2D) in physical pixels // softhack007: make sure its always > 0
inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels
inline uint16_t groupLength(void) const { return grouping + spacing; }
inline uint8_t getLightCapabilities(void) const { return _capabilities; }
@ -512,7 +516,7 @@ typedef struct Segment {
static uint16_t getUsedSegmentData(void) { return _usedSegmentData; }
static void addUsedSegmentData(int len) { _usedSegmentData += len; }
void set(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1);
void setUp(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1);
bool setColor(uint8_t slot, uint32_t c); //returns true if changed
void setCCT(uint16_t k);
void setOpacity(uint8_t o);
@ -562,9 +566,9 @@ typedef struct Segment {
void fadeToBlackBy(uint8_t fadeBy);
void blendPixelColor(int n, uint32_t color, uint8_t blend);
void blendPixelColor(int n, CRGB c, uint8_t blend) { blendPixelColor(n, RGBW32(c.r,c.g,c.b,0), blend); }
void addPixelColor(int n, uint32_t color);
void addPixelColor(int n, byte r, byte g, byte b, byte w = 0) { addPixelColor(n, RGBW32(r,g,b,w)); } // automatically inline
void addPixelColor(int n, CRGB c) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } // automatically inline
void addPixelColor(int n, uint32_t color, bool fast = false);
void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(n, RGBW32(r,g,b,w), fast); } // automatically inline
void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); } // automatically inline
void fadePixelColor(uint16_t n, uint8_t fade);
uint8_t get_random_wheel_index(uint8_t pos);
uint32_t color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255);
@ -586,16 +590,16 @@ typedef struct Segment {
// 2D support functions
void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend);
void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), blend); }
void addPixelColorXY(int x, int y, uint32_t color);
void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { addPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline
void addPixelColorXY(int x, int y, CRGB c) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); }
void addPixelColorXY(int x, int y, uint32_t color, bool fast = false);
void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColorXY(x, y, RGBW32(r,g,b,w), fast); } // automatically inline
void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), fast); }
void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade);
void box_blur(uint16_t i, bool vertical, fract8 blur_amount); // 1D box blur (with weight)
void blurRow(uint16_t row, fract8 blur_amount);
void blurCol(uint16_t col, fract8 blur_amount);
void moveX(int8_t delta);
void moveY(int8_t delta);
void move(uint8_t dir, uint8_t delta);
void moveX(int8_t delta, bool wrap = false);
void moveY(int8_t delta, bool wrap = false);
void move(uint8_t dir, uint8_t delta, bool wrap = false);
void draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c);
void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c);
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c);
@ -619,16 +623,16 @@ typedef struct Segment {
uint32_t getPixelColorXY(uint16_t x, uint16_t y) { return getPixelColor(x); }
void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t c, uint8_t blend) { blendPixelColor(x, c, blend); }
void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColor(x, RGBW32(c.r,c.g,c.b,0), blend); }
void addPixelColorXY(int x, int y, uint32_t color) { addPixelColor(x, color); }
void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { addPixelColor(x, RGBW32(r,g,b,w)); }
void addPixelColorXY(int x, int y, CRGB c) { addPixelColor(x, RGBW32(c.r,c.g,c.b,0)); }
void addPixelColorXY(int x, int y, uint32_t color, bool fast = false) { addPixelColor(x, color, fast); }
void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(x, RGBW32(r,g,b,w), fast); }
void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColor(x, RGBW32(c.r,c.g,c.b,0), fast); }
void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { fadePixelColor(x, fade); }
void box_blur(uint16_t i, bool vertical, fract8 blur_amount) {}
void blurRow(uint16_t row, fract8 blur_amount) {}
void blurCol(uint16_t col, fract8 blur_amount) {}
void moveX(int8_t delta) {}
void moveY(int8_t delta) {}
void move(uint8_t dir, uint8_t delta) {}
void moveX(int8_t delta, bool wrap = false) {}
void moveY(int8_t delta, bool wrap = false) {}
void move(uint8_t dir, uint8_t delta, bool wrap = false) {}
void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c) {}
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) {}
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) {}
@ -706,7 +710,7 @@ class WS2812FX { // 96 bytes
panel.clear();
#endif
customPalettes.clear();
if (useLedsArray && Segment::_globalLeds) free(Segment::_globalLeds);
if (useLedsArray && Segment::_globalLeds) {free(Segment::_globalLeds); Segment::_globalLeds = nullptr;} // reset to nullptr, to avoid race conditions
}
static WS2812FX* getInstance(void) { return instance; }
@ -768,6 +772,7 @@ class WS2812FX { // 96 bytes
getActiveSegmentsNum(void),
getFirstSelectedSegId(void),
getLastActiveSegmentId(void),
getActiveSegsLightCapabilities(bool selectedOnly = false),
setPixelSegment(uint8_t n);
inline uint8_t getBrightness(void) { return _brightness; }

View File

@ -191,6 +191,8 @@ uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) {
uint16_t /*IRAM_ATTR*/ Segment::XY(uint16_t x, uint16_t y) {
uint16_t width = virtualWidth(); // segment width in logical pixels
uint16_t height = virtualHeight(); // segment height in logical pixels
if (width == 0) return 0; // softhack007 avoid div/0
if (height == 0) return (x%width); // softhack007 avoid div/0
return (x%width) + (y%height) * width;
}
@ -202,7 +204,6 @@ void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col)
if (leds) leds[XY(x,y)] = col;
uint8_t _bri_t = currentBri(on ? opacity : 0);
if (!_bri_t && !transitional) return;
if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t);
byte g = scale8(G(col), _bri_t);
@ -304,8 +305,23 @@ void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t
}
// Adds the specified color with the existing pixel color perserving color balance.
void Segment::addPixelColorXY(int x, int y, uint32_t color) {
setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color));
void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) {
if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit
uint32_t col = getPixelColorXY(x,y);
uint8_t r = R(col);
uint8_t g = G(col);
uint8_t b = B(col);
uint8_t w = W(col);
if (fast) {
r = qadd8(r, R(color));
g = qadd8(g, G(color));
b = qadd8(b, B(color));
w = qadd8(w, W(color));
col = RGBW32(r,g,b,w);
} else {
col = color_add(col, color);
}
setPixelColorXY(x, y, col);
}
void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) {
@ -315,24 +331,26 @@ void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) {
// blurRow: perform a blur on a row of a rectangular matrix
void Segment::blurRow(uint16_t row, fract8 blur_amount) {
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
const uint_fast16_t cols = virtualWidth();
const uint_fast16_t rows = virtualHeight();
if (row >= rows) return;
// blur one row
uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black;
for (uint16_t x = 0; x < cols; x++) {
for (uint_fast16_t x = 0; x < cols; x++) {
CRGB cur = getPixelColorXY(x, row);
uint32_t before = uint32_t(cur); // remember color before blur
CRGB part = cur;
part.nscale8(seep);
cur.nscale8(keep);
cur += carryover;
if (x) {
if (x>0) {
CRGB prev = CRGB(getPixelColorXY(x-1, row)) + part;
setPixelColorXY(x-1, row, prev);
}
if (before != uint32_t(cur)) // optimization: only set pixel if color has changed
setPixelColorXY(x, row, cur);
carryover = part;
}
@ -340,25 +358,27 @@ void Segment::blurRow(uint16_t row, fract8 blur_amount) {
// blurCol: perform a blur on a column of a rectangular matrix
void Segment::blurCol(uint16_t col, fract8 blur_amount) {
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
const uint_fast16_t cols = virtualWidth();
const uint_fast16_t rows = virtualHeight();
if (col >= cols) return;
// blur one column
uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black;
for (uint16_t i = 0; i < rows; i++) {
CRGB cur = getPixelColorXY(col, i);
for (uint_fast16_t y = 0; y < rows; y++) {
CRGB cur = getPixelColorXY(col, y);
CRGB part = cur;
uint32_t before = uint32_t(cur); // remember color before blur
part.nscale8(seep);
cur.nscale8(keep);
cur += carryover;
if (i) {
CRGB prev = CRGB(getPixelColorXY(col, i-1)) + part;
setPixelColorXY(col, i-1, prev);
if (y>0) {
CRGB prev = CRGB(getPixelColorXY(col, y-1)) + part;
setPixelColorXY(col, y-1, prev);
}
setPixelColorXY(col, i, cur);
if (before != uint32_t(cur)) // optimization: only set pixel if color has changed
setPixelColorXY(col, y, cur);
carryover = part;
}
}
@ -377,8 +397,8 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
for (uint16_t j = 0; j < dim1; j++) {
uint16_t x = vertical ? i : j;
uint16_t y = vertical ? j : i;
uint16_t xp = vertical ? x : x-1;
uint16_t yp = vertical ? y-1 : y;
int16_t xp = vertical ? x : x-1; // "signed" to prevent underflow
int16_t yp = vertical ? y-1 : y; // "signed" to prevent underflow
uint16_t xn = vertical ? x : x+1;
uint16_t yn = vertical ? y+1 : y;
CRGB curr = getPixelColorXY(x,y);
@ -416,54 +436,55 @@ void Segment::blur1d(fract8 blur_amount) {
for (uint16_t y = 0; y < rows; y++) blurRow(y, blur_amount);
}
void Segment::moveX(int8_t delta) {
void Segment::moveX(int8_t delta, bool wrap) {
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
if (!delta) return;
if (!delta || abs(delta) >= cols) return;
uint32_t newPxCol[cols];
for (int y = 0; y < rows; y++) {
if (delta > 0) {
for (uint8_t y = 0; y < rows; y++) for (uint8_t x = 0; x < cols-1; x++) {
if (x + delta >= cols) break;
setPixelColorXY(x, y, getPixelColorXY((x + delta)%cols, y));
}
for (int x = 0; x < cols-delta; x++) newPxCol[x] = getPixelColorXY((x + delta), y);
for (int x = cols-delta; x < cols; x++) newPxCol[x] = getPixelColorXY(wrap ? (x + delta) - cols : x, y);
} else {
for (uint8_t y = 0; y < rows; y++) for (int16_t x = cols-1; x >= 0; x--) {
if (x + delta < 0) break;
setPixelColorXY(x, y, getPixelColorXY(x + delta, y));
for (int x = cols-1; x >= -delta; x--) newPxCol[x] = getPixelColorXY((x + delta), y);
for (int x = -delta-1; x >= 0; x--) newPxCol[x] = getPixelColorXY(wrap ? (x + delta) + cols : x, y);
}
for (int x = 0; x < cols; x++) setPixelColorXY(x, y, newPxCol[x]);
}
}
void Segment::moveY(int8_t delta) {
void Segment::moveY(int8_t delta, bool wrap) {
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
if (!delta) return;
if (!delta || abs(delta) >= rows) return;
uint32_t newPxCol[rows];
for (int x = 0; x < cols; x++) {
if (delta > 0) {
for (uint8_t x = 0; x < cols; x++) for (uint8_t y = 0; y < rows-1; y++) {
if (y + delta >= rows) break;
setPixelColorXY(x, y, getPixelColorXY(x, (y + delta)));
}
for (int y = 0; y < rows-delta; y++) newPxCol[y] = getPixelColorXY(x, (y + delta));
for (int y = rows-delta; y < rows; y++) newPxCol[y] = getPixelColorXY(x, wrap ? (y + delta) - rows : y);
} else {
for (uint8_t x = 0; x < cols; x++) for (int16_t y = rows-1; y >= 0; y--) {
if (y + delta < 0) break;
setPixelColorXY(x, y, getPixelColorXY(x, y + delta));
for (int y = rows-1; y >= -delta; y--) newPxCol[y] = getPixelColorXY(x, (y + delta));
for (int y = -delta-1; y >= 0; y--) newPxCol[y] = getPixelColorXY(x, wrap ? (y + delta) + rows : y);
}
for (int y = 0; y < rows; y++) setPixelColorXY(x, y, newPxCol[y]);
}
}
// move() - move all pixels in desired direction delta number of pixels
// @param dir direction: 0=left, 1=left-up, 2=up, 3=right-up, 4=right, 5=right-down, 6=down, 7=left-down
// @param delta number of pixels to move
void Segment::move(uint8_t dir, uint8_t delta) {
// @param wrap around
void Segment::move(uint8_t dir, uint8_t delta, bool wrap) {
if (delta==0) return;
switch (dir) {
case 0: moveX( delta); break;
case 1: moveX( delta); moveY( delta); break;
case 2: moveY( delta); break;
case 3: moveX(-delta); moveY( delta); break;
case 4: moveX(-delta); break;
case 5: moveX(-delta); moveY(-delta); break;
case 6: moveY(-delta); break;
case 7: moveX( delta); moveY(-delta); break;
case 0: moveX( delta, wrap); break;
case 1: moveX( delta, wrap); moveY( delta, wrap); break;
case 2: moveY( delta, wrap); break;
case 3: moveX(-delta, wrap); moveY( delta, wrap); break;
case 4: moveX(-delta, wrap); break;
case 5: moveX(-delta, wrap); moveY(-delta, wrap); break;
case 6: moveY(-delta, wrap); break;
case 7: moveX( delta, wrap); moveY(-delta, wrap); break;
}
}

View File

@ -90,18 +90,18 @@ Segment::Segment(const Segment &orig) {
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
if (orig.leds && !Segment::_globalLeds) { leds = (CRGB*)malloc(sizeof(CRGB)*length()); if (leds) memcpy(leds, orig.leds, sizeof(CRGB)*length()); }
if (orig.leds && !Segment::_globalLeds && length() > 0) { leds = (CRGB*)malloc(sizeof(CRGB)*length()); if (leds) memcpy(leds, orig.leds, sizeof(CRGB)*length()); }
}
// move constructor
Segment::Segment(Segment &&orig) noexcept {
//DEBUG_PRINTLN(F("-- Move segment constructor --"));
memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.leds = nullptr;
orig.name = nullptr;
orig.data = nullptr;
orig._dataLen = 0;
orig._t = nullptr;
orig.leds = nullptr;
}
// copy assignment
@ -111,7 +111,7 @@ Segment& Segment::operator= (const Segment &orig) {
// clean destination
if (name) delete[] name;
if (_t) delete _t;
if (leds && !Segment::_globalLeds) free(leds);
if (leds && !Segment::_globalLeds) {free(leds); leds=nullptr;}
deallocateData();
// copy source
memcpy((void*)this, (void*)&orig, sizeof(Segment));
@ -125,7 +125,7 @@ Segment& Segment::operator= (const Segment &orig) {
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
if (orig.leds && !Segment::_globalLeds) { leds = (CRGB*)malloc(sizeof(CRGB)*length()); if (leds) memcpy(leds, orig.leds, sizeof(CRGB)*length()); }
if (orig.leds && !Segment::_globalLeds && length() > 0) { leds = (CRGB*)malloc(sizeof(CRGB)*length()); if (leds) memcpy(leds, orig.leds, sizeof(CRGB)*length()); }
}
return *this;
}
@ -137,7 +137,7 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
if (name) delete[] name; // free old name
deallocateData(); // free old runtime data
if (_t) delete _t;
if (leds && !Segment::_globalLeds) free(leds);
if (leds && !Segment::_globalLeds) {free(leds); leds=nullptr;}
memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.name = nullptr;
orig.data = nullptr;
@ -152,12 +152,12 @@ bool Segment::allocateData(size_t len) {
if (data && _dataLen == len) return true; //already allocated
deallocateData();
if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) return false; //not enough memory
// if possible use SPI RAM on ESP32
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
if (psramFound())
data = (byte*) ps_malloc(len);
else
#endif
// do not use SPI RAM on ESP32 since it is slow
//#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM)
//if (psramFound())
// data = (byte*) ps_malloc(len);
//else
//#endif
data = (byte*) malloc(len);
if (!data) return false; //allocation failed
Segment::addUsedSegmentData(len);
@ -184,7 +184,7 @@ void Segment::deallocateData() {
void Segment::resetIfRequired() {
if (reset) {
if (leds && !Segment::_globalLeds) { free(leds); leds = nullptr; }
if (transitional && _t) { transitional = false; delete _t; _t = nullptr; }
//if (transitional && _t) { transitional = false; delete _t; _t = nullptr; }
deallocateData();
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
reset = false; // setOption(SEG_OPTION_RESET, false);
@ -199,12 +199,12 @@ void Segment::setUpLeds() {
#else
leds = &Segment::_globalLeds[start];
#endif
else if (!leds) {
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
if (psramFound())
leds = (CRGB*)ps_malloc(sizeof(CRGB)*length());
else
#endif
else if (leds == nullptr && length() > 0) { //softhack007 quickfix - avoid malloc(0) which is undefined behaviour (should not happen, but i've seen it)
//#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
//if (psramFound())
// leds = (CRGB*)ps_malloc(sizeof(CRGB)*length()); // softhack007 disabled; putting leds into psram leads to horrible slowdown on WROVER boards
//else
//#endif
leds = (CRGB*)malloc(sizeof(CRGB)*length());
}
}
@ -228,6 +228,7 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
case FX_MODE_GLITTER : pal = 11; break; // rainbow colors
case FX_MODE_SUNRISE : pal = 35; break; // heat palette
case FX_MODE_RAILWAY : pal = 3; break; // prim + sec
case FX_MODE_2DSOAP : pal = 11; break; // rainbow colors
}
switch (pal) {
case 0: //default palette. Exceptions for specific effects above
@ -304,23 +305,26 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
}
void Segment::startTransition(uint16_t dur) {
if (transitional || _t) return; // already in transition no need to store anything
if (!dur) {
transitional = false;
if (_t) {
delete _t;
_t = nullptr;
}
return;
}
if (transitional && _t) return; // already in transition no need to store anything
// starting a transition has to occur before change so we get current values 1st
uint8_t _briT = currentBri(on ? opacity : 0);
uint8_t _cctT = currentBri(cct, true);
CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette);
uint8_t _modeP = mode;
uint32_t _colorT[NUM_COLORS];
for (size_t i=0; i<NUM_COLORS; i++) _colorT[i] = currentColor(i, colors[i]);
if (!_t) _t = new Transition(dur); // no previous transition running
_t = new Transition(dur); // no previous transition running
if (!_t) return; // failed to allocate data
_t->_briT = _briT;
_t->_cctT = _cctT;
CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette);
_t->_briT = on ? opacity : 0;
_t->_cctT = cct;
_t->_palT = _palT;
_t->_modeP = _modeP;
for (size_t i=0; i<NUM_COLORS; i++) _t->_colorT[i] = _colorT[i];
_t->_modeP = mode;
for (size_t i=0; i<NUM_COLORS; i++) _t->_colorT[i] = colors[i];
transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true);
}
@ -333,10 +337,10 @@ uint16_t Segment::progress() {
}
uint8_t Segment::currentBri(uint8_t briNew, bool useCct) {
if (transitional && _t) {
uint32_t prog = progress() + 1;
if (useCct) return ((briNew * prog) + _t->_cctT * (0x10000 - prog)) >> 16;
else return ((briNew * prog) + _t->_briT * (0x10000 - prog)) >> 16;
uint32_t prog = progress();
if (transitional && _t && prog < 0xFFFFU) {
if (useCct) return ((briNew * prog) + _t->_cctT * (0xFFFFU - prog)) >> 16;
else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16;
} else {
return briNew;
}
@ -366,19 +370,18 @@ CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal
void Segment::handleTransition() {
if (!transitional) return;
unsigned long maxWait = millis() + 20;
if (mode == FX_MODE_STATIC && next_time > maxWait) next_time = maxWait;
if (progress() == 0xFFFFU) {
if (_t) {
if (_t->_modeP != mode) markForReset();
uint16_t _progress = progress();
if (_progress == 0xFFFFU) transitional = false; // finish transitioning segment
if (_t) { // thanks to @nXm AKA https://github.com/NMeirer
if (_progress >= 32767U && _t->_modeP != mode) markForReset();
if (_progress == 0xFFFFU) {
delete _t;
_t = nullptr;
}
transitional = false; // finish transitioning segment
}
}
void Segment::set(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y) {
void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y) {
//return if neither bounds nor grouping have changed
bool boundsUnchanged = (start == i1 && stop == i2);
#ifndef WLED_DISABLE_2D
@ -473,7 +476,7 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
sOpt = extractModeDefaults(fx, "o2"); check2 = (sOpt >= 0) ? (bool)sOpt : false;
sOpt = extractModeDefaults(fx, "o3"); check3 = (sOpt >= 0) ? (bool)sOpt : false;
sOpt = extractModeDefaults(fx, "m12"); if (sOpt >= 0) map1D2D = constrain(sOpt, 0, 7);
sOpt = extractModeDefaults(fx, "si"); if (sOpt >= 0) soundSim = constrain(sOpt, 0, 7);
sOpt = extractModeDefaults(fx, "si"); if (sOpt >= 0) soundSim = constrain(sOpt, 0, 1);
sOpt = extractModeDefaults(fx, "rev"); if (sOpt >= 0) reverse = (bool)sOpt;
sOpt = extractModeDefaults(fx, "mi"); if (sOpt >= 0) mirror = (bool)sOpt; // NOTE: setting this option is a risky business
sOpt = extractModeDefaults(fx, "rY"); if (sOpt >= 0) reverse_y = (bool)sOpt;
@ -544,6 +547,7 @@ uint16_t Segment::virtualLength() const {
}
#endif
uint16_t groupLen = groupLength();
if (groupLen < 1) groupLen = 1; // prevent division by zero - better safe than sorry ...
uint16_t vLength = (length() + groupLen - 1) / groupLen;
if (mirror) vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED
return vLength;
@ -622,7 +626,6 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
uint16_t len = length();
uint8_t _bri_t = currentBri(on ? opacity : 0);
if (!_bri_t && !transitional) return;
if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t);
byte g = scale8(G(col), _bri_t);
@ -727,7 +730,7 @@ uint32_t Segment::getPixelColor(int i)
i += start;
/* offset/phase */
i += offset;
if (i >= stop) i -= length();
if ((i >= stop) && (stop>0)) i -= length(); // avoids negative pixel index (stop = 0 is a possible value)
return strip.getPixelColor(i);
}
@ -749,7 +752,7 @@ uint8_t Segment::differs(Segment& b) const {
if (startY != b.startY) d |= SEG_DIFFERS_BOUNDS;
if (stopY != b.stopY) d |= SEG_DIFFERS_BOUNDS;
//bit pattern: (msb first) sound:3, mapping:3, transposed, mirrorY, reverseY, [transitional, reset,] paused, mirrored, on, reverse, [selected]
//bit pattern: (msb first) set:2, sound:1, mapping:3, transposed, mirrorY, reverseY, [transitional, reset,] paused, mirrored, on, reverse, [selected]
if ((options & 0b1111111110011110U) != (b.options & 0b1111111110011110U)) d |= SEG_DIFFERS_OPT;
if ((options & 0x0001U) != (b.options & 0x0001U)) d |= SEG_DIFFERS_SEL;
for (uint8_t i = 0; i < NUM_COLORS; i++) if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL;
@ -820,8 +823,22 @@ void Segment::blendPixelColor(int n, uint32_t color, uint8_t blend) {
}
// Adds the specified color with the existing pixel color perserving color balance.
void Segment::addPixelColor(int n, uint32_t color) {
setPixelColor(n, color_add(getPixelColor(n), color));
void Segment::addPixelColor(int n, uint32_t color, bool fast) {
uint32_t col = getPixelColor(n);
uint8_t r = R(col);
uint8_t g = G(col);
uint8_t b = B(col);
uint8_t w = W(col);
if (fast) {
r = qadd8(r, R(color));
g = qadd8(g, G(color));
b = qadd8(b, B(color));
w = qadd8(w, W(color));
col = RGBW32(r,g,b,w);
} else {
col = color_add(col, color);
}
setPixelColor(n, col);
}
void Segment::fadePixelColor(uint16_t n, uint8_t fade) {
@ -870,6 +887,7 @@ void Segment::fade_out(uint8_t rate) {
// fades all pixels to black using nscale8()
void Segment::fadeToBlackBy(uint8_t fadeBy) {
if (fadeBy == 0) return; // optimization - no scaling to apply
const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
const uint16_t rows = virtualHeight(); // will be 1 for 1D
@ -884,23 +902,26 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) {
*/
void Segment::blur(uint8_t blur_amount)
{
if (blur_amount == 0) return; // optimization: 0 means "don't blur"
#ifndef WLED_DISABLE_2D
if (is2D()) {
// compatibility with 2D
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
for (uint16_t i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows
for (uint16_t k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns
const uint_fast16_t cols = virtualWidth();
const uint_fast16_t rows = virtualHeight();
for (uint_fast16_t i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows
for (uint_fast16_t k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns
return;
}
#endif
uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black;
for(uint16_t i = 0; i < virtualLength(); i++)
uint_fast16_t vlength = virtualLength();
for(uint_fast16_t i = 0; i < vlength; i++)
{
CRGB cur = CRGB(getPixelColor(i));
CRGB part = cur;
uint32_t before = uint32_t(cur); // remember color before blur
part.nscale8(seep);
cur.nscale8(keep);
cur += carryover;
@ -909,9 +930,10 @@ void Segment::blur(uint8_t blur_amount)
uint8_t r = R(c);
uint8_t g = G(c);
uint8_t b = B(c);
setPixelColor(i-1, qadd8(r, part.red), qadd8(g, part.green), qadd8(b, part.blue));
setPixelColor((uint16_t)(i-1), qadd8(r, part.red), qadd8(g, part.green), qadd8(b, part.blue));
}
setPixelColor(i,cur.red, cur.green, cur.blue);
if (before != uint32_t(cur)) // optimization: only set pixel if color has changed
setPixelColor((uint16_t)i,cur.red, cur.green, cur.blue);
carryover = part;
}
}
@ -1054,13 +1076,14 @@ void WS2812FX::finalizeInit(void)
}
if (useLedsArray) {
size_t arrSize = sizeof(CRGB) * getLengthTotal();
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
if (psramFound())
Segment::_globalLeds = (CRGB*) ps_malloc(arrSize);
else
#endif
// softhack007 disabled; putting leds into psram leads to horrible slowdown on WROVER boards (see setUpLeds())
//#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
//if (psramFound())
// Segment::_globalLeds = (CRGB*) ps_malloc(arrSize);
//else
//#endif
Segment::_globalLeds = (CRGB*) malloc(arrSize);
memset(Segment::_globalLeds, 0, arrSize);
if (Segment::_globalLeds && (arrSize > 0)) memset(Segment::_globalLeds, 0, arrSize);
}
//segments are created in makeAutoSegments();
@ -1079,6 +1102,8 @@ void WS2812FX::service() {
_isServicing = true;
_segment_index = 0;
for (segment &seg : _segments) {
// process transition (mode changes in the middle of transition)
seg.handleTransition();
// reset the segment runtime data if needed
seg.resetIfRequired();
@ -1107,8 +1132,6 @@ void WS2812FX::service() {
delay = (*_mode[seg.currentMode(seg.mode)])();
if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++;
if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
seg.handleTransition();
}
seg.next_time = nowUp + delay;
@ -1181,12 +1204,12 @@ void WS2812FX::estimateCurrentAndLimitBri() {
uint32_t powerSum = 0;
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
Bus *bus = busses.getBus(b);
for (uint_fast8_t bNum = 0; bNum < busses.getNumBusses(); bNum++) {
Bus *bus = busses.getBus(bNum);
if (bus->getType() >= TYPE_NET_DDP_RGB) continue; //exclude non-physical network busses
uint16_t len = bus->getLength();
uint32_t busPowerSum = 0;
for (uint16_t i = 0; i < len; i++) { //sum up the usage of each LED
for (uint_fast16_t i = 0; i < len; i++) { //sum up the usage of each LED
uint32_t c = bus->getPixelColor(i);
byte r = R(c), g = G(c), b = B(c), w = W(c);
@ -1205,7 +1228,8 @@ void WS2812FX::estimateCurrentAndLimitBri() {
}
uint32_t powerSum0 = powerSum;
powerSum *= _brightness;
//powerSum *= _brightness; // for NPBrightnessBus
powerSum *= 255; // no need to scale down powerSum - NPB-LG getPixelColor returns colors scaled down by brightness
if (powerSum > powerBudget) //scale brightness down to stay in current limit
{
@ -1213,11 +1237,14 @@ void WS2812FX::estimateCurrentAndLimitBri() {
uint16_t scaleI = scale * 255;
uint8_t scaleB = (scaleI > 255) ? 255 : scaleI;
uint8_t newBri = scale8(_brightness, scaleB);
busses.setBrightness(newBri); //to keep brightness uniform, sets virtual busses too
currentMilliamps = (powerSum0 * newBri) / puPerMilliamp;
// to keep brightness uniform, sets virtual busses too - softhack007: apply reductions immediately
if (scaleB < 255) busses.setBrightness(scaleB, true); // NPB-LG has already applied brightness, so its suffifient to post-apply scaling ==> use scaleB instead of newBri
busses.setBrightness(newBri, false); // set new brightness for next frame
//currentMilliamps = (powerSum0 * newBri) / puPerMilliamp; // for NPBrightnessBus
currentMilliamps = (powerSum0 * scaleB) / puPerMilliamp; // for NPBus-LG
} else {
currentMilliamps = powerSum / puPerMilliamp;
busses.setBrightness(_brightness);
busses.setBrightness(_brightness, false); // set new brightness for next frame
}
currentMilliamps += MA_FOR_ESP; //add power of ESP back to estimate
currentMilliamps += pLen; //add standby power back to estimate
@ -1314,6 +1341,14 @@ void WS2812FX::setBrightness(uint8_t b, bool direct) {
}
}
uint8_t WS2812FX::getActiveSegsLightCapabilities(bool selectedOnly) {
uint8_t totalLC = 0;
for (segment &seg : _segments) {
if (seg.isActive() && (!selectedOnly || seg.isSelected())) totalLC |= seg.getLightCapabilities();
}
return totalLC;
}
uint8_t WS2812FX::getFirstSelectedSegId(void)
{
size_t i = 0;
@ -1411,7 +1446,7 @@ Segment& WS2812FX::getSegment(uint8_t id) {
void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing, uint16_t offset, uint16_t startY, uint16_t stopY) {
if (n >= _segments.size()) return;
_segments[n].set(i1, i2, grouping, spacing, offset, startY, stopY);
_segments[n].setUp(i1, i2, grouping, spacing, offset, startY, stopY);
}
void WS2812FX::restartRuntime() {
@ -1567,7 +1602,7 @@ void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) {
}
void WS2812FX::setTransitionMode(bool t) {
for (segment &seg : _segments) if (!seg.transitional) seg.startTransition(t ? _transitionDur : 0);
for (segment &seg : _segments) seg.startTransition(t ? _transitionDur : 0);
}
#ifdef WLED_DEBUG

View File

@ -9,9 +9,9 @@
#include <IPAddress.h>
#define NODE_TYPE_ID_UNDEFINED 0
#define NODE_TYPE_ID_ESP8266 82
#define NODE_TYPE_ID_ESP32 32
#define NODE_TYPE_ID_ESP32S2 33
#define NODE_TYPE_ID_ESP8266 82 // should be 1
#define NODE_TYPE_ID_ESP32 32 // should be 2
#define NODE_TYPE_ID_ESP32S2 33 // etc
#define NODE_TYPE_ID_ESP32S3 34
#define NODE_TYPE_ID_ESP32C3 35
@ -23,7 +23,13 @@ struct NodeStruct
String nodeName;
IPAddress ip;
uint8_t age;
uint8_t nodeType;
union {
uint8_t nodeType; // a waste of space as we only have 5 types
struct {
uint8_t type : 7; // still a waste of space (4 bits would be enough and future-proof)
bool on : 1;
};
};
uint32_t build;
NodeStruct() : age(0), nodeType(0), build(0)

View File

@ -105,8 +105,15 @@ void onAlexaChange(EspalexaDevice* dev)
uint16_t k = 1000000 / ct; //mireds to kelvin
if (strip.hasCCTBus()) {
bool hasManualWhite = strip.getActiveSegsLightCapabilities(true) & SEG_CAPABILITY_W;
strip.setCCT(k);
if (hasManualWhite) {
rgbw[0] = 0; rgbw[1] = 0; rgbw[2] = 0; rgbw[3] = 255;
} else {
rgbw[0] = 255; rgbw[1] = 255; rgbw[2] = 255; rgbw[3] = 0;
dev->setValue(255);
}
} else if (strip.hasWhiteChannel()) {
switch (ct) { //these values empirically look good on RGBW
case 199: rgbw[0]=255; rgbw[1]=255; rgbw[2]=255; rgbw[3]=255; break;

View File

@ -95,12 +95,14 @@ uint32_t Bus::autoWhiteCalc(uint32_t c) {
BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) : Bus(bc.type, bc.start, bc.autoWhite), _colorOrderMap(com) {
if (!IS_DIGITAL(bc.type) || !bc.count) return;
if (!pinManager.allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return;
_frequencykHz = 0U;
_pins[0] = bc.pins[0];
if (IS_2PIN(bc.type)) {
if (!pinManager.allocatePin(bc.pins[1], true, PinOwner::BusDigital)) {
cleanup(); return;
}
_pins[1] = bc.pins[1];
_frequencykHz = bc.frequency ? bc.frequency : 2000U; // 2MHz clock if undefined
}
reversed = bc.reversed;
_needsRefresh = bc.refreshReq || bc.type == TYPE_TM1814;
@ -110,7 +112,7 @@ BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) : Bu
if (_iType == I_NONE) return;
uint16_t lenToCreate = _len;
if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(_len); // only needs a third of "RGB" LEDs for NeoPixelBus
_busPtr = PolyBus::create(_iType, _pins, lenToCreate, nr);
_busPtr = PolyBus::create(_iType, _pins, lenToCreate, nr, _frequencykHz);
_valid = (_busPtr != nullptr);
_colorOrder = bc.colorOrder;
DEBUG_PRINTF("%successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u)\n", _valid?"S":"Uns", nr, _len, bc.type, _pins[0],_pins[1],_iType);
@ -124,15 +126,15 @@ bool BusDigital::canShow() {
return PolyBus::canShow(_busPtr, _iType);
}
void BusDigital::setBrightness(uint8_t b) {
void BusDigital::setBrightness(uint8_t b, bool immediate) {
//Fix for turning off onboard LED breaking bus
#ifdef LED_BUILTIN
if (_bri == 0 && b > 0) {
if (_pins[0] == LED_BUILTIN || _pins[1] == LED_BUILTIN) PolyBus::begin(_busPtr, _iType, _pins);
}
#endif
Bus::setBrightness(b);
PolyBus::setBrightness(_busPtr, _iType, b);
Bus::setBrightness(b, immediate);
PolyBus::setBrightness(_busPtr, _iType, b, immediate);
}
//If LEDs are skipped, it is possible to use the first as a status LED.
@ -212,10 +214,11 @@ BusPwm::BusPwm(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
_valid = false;
if (!IS_PWM(bc.type)) return;
uint8_t numPins = NUM_PWM_PINS(bc.type);
_frequency = bc.frequency ? bc.frequency : WLED_PWM_FREQ;
#ifdef ESP8266
analogWriteRange(255); //same range as one RGB channel
analogWriteFreq(WLED_PWM_FREQ);
analogWriteFreq(_frequency);
#else
_ledcStart = pinManager.allocateLedc(numPins);
if (_ledcStart == 255) { //no more free LEDC channels
@ -232,7 +235,7 @@ BusPwm::BusPwm(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
#ifdef ESP8266
pinMode(_pins[i], OUTPUT);
#else
ledcSetup(_ledcStart + i, WLED_PWM_FREQ, 8);
ledcSetup(_ledcStart + i, _frequency, 8);
ledcAttachPin(_pins[i], _ledcStart + i);
#endif
}
@ -450,21 +453,21 @@ void BusNetwork::cleanup() {
uint32_t BusManager::memUsage(BusConfig &bc) {
uint8_t type = bc.type;
uint16_t len = bc.count + bc.skipAmount;
if (type > 15 && type < 32) {
if (type > 15 && type < 32) { // digital types
if (type == TYPE_UCS8903 || type == TYPE_UCS8904) len *= 2; // 16-bit LEDs
#ifdef ESP8266
if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem
if (type > 29) return len*20; //RGBW
if (type > 28) return len*20; //RGBW
return len*15;
}
if (type > 29) return len*4; //RGBW
if (type > 28) return len*4; //RGBW
return len*3;
#else //ESP32 RMT uses double buffer?
if (type > 29) return len*8; //RGBW
if (type > 28) return len*8; //RGBW
return len*6;
#endif
}
if (type > 31 && type < 48) return 5;
if (type == 44 || type == 45) return len*4; //RGBW
return len*3; //RGB
}
@ -512,9 +515,9 @@ void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c, int16_t cct)
}
}
void BusManager::setBrightness(uint8_t b) {
void BusManager::setBrightness(uint8_t b, bool immediate) {
for (uint8_t i = 0; i < numBusses; i++) {
busses[i]->setBrightness(b);
busses[i]->setBrightness(b, immediate);
}
}

View File

@ -29,10 +29,11 @@ struct BusConfig {
bool refreshReq;
uint8_t autoWhite;
uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255};
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY) {
uint16_t frequency;
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U) {
refreshReq = (bool) GET_BIT(busType,7);
type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh)
count = len; start = pstart; colorOrder = pcolorOrder; reversed = rev; skipAmount = skip; autoWhite = aw;
count = len; start = pstart; colorOrder = pcolorOrder; reversed = rev; skipAmount = skip; autoWhite = aw; frequency = clock_kHz;
uint8_t nPins = 1;
if (type >= TYPE_NET_DDP_RGB && type < 96) nPins = 4; //virtual network bus. 4 "pins" store IP address
else if (type > 47) nPins = 2;
@ -107,13 +108,14 @@ class Bus {
virtual void setStatusPixel(uint32_t c) {}
virtual void setPixelColor(uint16_t pix, uint32_t c) = 0;
virtual uint32_t getPixelColor(uint16_t pix) { return 0; }
virtual void setBrightness(uint8_t b) { _bri = b; };
virtual void setBrightness(uint8_t b, bool immediate=false) { _bri = b; };
virtual void cleanup() = 0;
virtual uint8_t getPins(uint8_t* pinArray) { return 0; }
virtual uint16_t getLength() { return _len; }
virtual void setColorOrder() {}
virtual uint8_t getColorOrder() { return COL_ORDER_RGB; }
virtual uint8_t skippedLeds() { return 0; }
virtual uint16_t getFrequency() { return 0U; }
inline uint16_t getStart() { return _start; }
inline void setStart(uint16_t start) { _start = start; }
inline uint8_t getType() { return _type; }
@ -179,7 +181,7 @@ class BusDigital : public Bus {
bool canShow();
void setBrightness(uint8_t b);
void setBrightness(uint8_t b, bool immediate);
void setStatusPixel(uint32_t c);
@ -203,6 +205,8 @@ class BusDigital : public Bus {
return _skip;
}
uint16_t getFrequency() { return _frequencykHz; }
void reinit();
void cleanup();
@ -216,6 +220,7 @@ class BusDigital : public Bus {
uint8_t _pins[2] = {255, 255};
uint8_t _iType = 0; //I_NONE;
uint8_t _skip = 0;
uint16_t _frequencykHz = 0U;
void * _busPtr = nullptr;
const ColorOrderMap &_colorOrderMap;
};
@ -234,6 +239,8 @@ class BusPwm : public Bus {
uint8_t getPins(uint8_t* pinArray);
uint16_t getFrequency() { return _frequency; }
void cleanup() {
deallocatePins();
}
@ -248,6 +255,7 @@ class BusPwm : public Bus {
#ifdef ARDUINO_ARCH_ESP32
uint8_t _ledcStart = 255;
#endif
uint16_t _frequency = 0U;
void deallocatePins();
};
@ -335,9 +343,9 @@ class BusManager {
void setStatusPixel(uint32_t c);
void IRAM_ATTR setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1);
void setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1);
void setBrightness(uint8_t b);
void setBrightness(uint8_t b, bool immediate=false); // immediate=true is for use in ABL, it applies brightness immediately (warning: inefficient)
void setSegmentCCT(int16_t cct, bool allowWBCorrection = false);

View File

@ -1,7 +1,7 @@
#ifndef BusWrapper_h
#define BusWrapper_h
#include "NeoPixelBrightnessBus.h"
#include "NeoPixelBusLg.h"
// temporary - these defines should actually be set in platformio.ini
// C3: I2S0 and I2S1 methods not supported (has one I2S bus)
@ -53,6 +53,16 @@
#define I_8266_U1_TM2_3 18
#define I_8266_DM_TM2_3 19
#define I_8266_BB_TM2_3 20
//UCS8903 (RGB)
#define I_8266_U0_UCS_3 49
#define I_8266_U1_UCS_3 50
#define I_8266_DM_UCS_3 51
#define I_8266_BB_UCS_3 52
//UCS8904 (RGBW)
#define I_8266_U0_UCS_4 53
#define I_8266_U1_UCS_4 54
#define I_8266_DM_UCS_4 55
#define I_8266_BB_UCS_4 56
/*** ESP32 Neopixel methods ***/
//RGB
@ -80,6 +90,16 @@
#define I_32_I0_TM2_3 37
#define I_32_I1_TM2_3 38
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
//UCS8903 (RGB)
#define I_32_RN_UCS_3 57
#define I_32_I0_UCS_3 58
#define I_32_I1_UCS_3 59
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
//UCS8904 (RGBW)
#define I_32_RN_UCS_4 60
#define I_32_I0_UCS_4 61
#define I_32_I1_UCS_4 62
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
//APA102
#define I_HS_DOT_3 39 //hardware SPI
@ -102,80 +122,111 @@
#define I_SS_LPO_3 48
// In the following NeoGammaNullMethod can be replaced with NeoGammaWLEDMethod to perform Gamma correction implicitly
// unfortunately that may apply Gamma correction to pre-calculated palettes which is undesired
/*** ESP8266 Neopixel methods ***/
#ifdef ESP8266
//RGB
#define B_8266_U0_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Uart0Ws2813Method> //3 chan, esp8266, gpio1
#define B_8266_U1_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Uart1Ws2813Method> //3 chan, esp8266, gpio2
#define B_8266_DM_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Dma800KbpsMethod> //3 chan, esp8266, gpio3
#define B_8266_BB_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266BitBang800KbpsMethod> //3 chan, esp8266, bb (any pin but 16)
#define B_8266_U0_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod> //3 chan, esp8266, gpio1
#define B_8266_U1_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod> //3 chan, esp8266, gpio2
#define B_8266_DM_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Dma800KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, gpio3
#define B_8266_BB_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266BitBang800KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, bb (any pin but 16)
//RGBW
#define B_8266_U0_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp8266Uart0Ws2813Method> //4 chan, esp8266, gpio1
#define B_8266_U1_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp8266Uart1Ws2813Method> //4 chan, esp8266, gpio2
#define B_8266_DM_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp8266Dma800KbpsMethod> //4 chan, esp8266, gpio3
#define B_8266_BB_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp8266BitBang800KbpsMethod> //4 chan, esp8266, bb (any pin)
#define B_8266_U0_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod> //4 chan, esp8266, gpio1
#define B_8266_U1_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod> //4 chan, esp8266, gpio2
#define B_8266_DM_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp8266Dma800KbpsMethod, NeoGammaNullMethod> //4 chan, esp8266, gpio3
#define B_8266_BB_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp8266BitBang800KbpsMethod, NeoGammaNullMethod> //4 chan, esp8266, bb (any pin)
//400Kbps
#define B_8266_U0_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Uart0400KbpsMethod> //3 chan, esp8266, gpio1
#define B_8266_U1_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Uart1400KbpsMethod> //3 chan, esp8266, gpio2
#define B_8266_DM_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Dma400KbpsMethod> //3 chan, esp8266, gpio3
#define B_8266_BB_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266BitBang400KbpsMethod> //3 chan, esp8266, bb (any pin)
#define B_8266_U0_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Uart0400KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, gpio1
#define B_8266_U1_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Uart1400KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, gpio2
#define B_8266_DM_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Dma400KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, gpio3
#define B_8266_BB_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266BitBang400KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, bb (any pin)
//TM1814 (RGBW)
#define B_8266_U0_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266Uart0Tm1814Method>
#define B_8266_U1_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266Uart1Tm1814Method>
#define B_8266_DM_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266DmaTm1814Method>
#define B_8266_BB_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266BitBangTm1814Method>
#define B_8266_U0_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266Uart0Tm1814Method, NeoGammaNullMethod>
#define B_8266_U1_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266Uart1Tm1814Method, NeoGammaNullMethod>
#define B_8266_DM_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266DmaTm1814Method, NeoGammaNullMethod>
#define B_8266_BB_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266BitBangTm1814Method, NeoGammaNullMethod>
//TM1829 (RGB)
#define B_8266_U0_TM2_4 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp8266Uart0Tm1829Method>
#define B_8266_U1_TM2_4 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp8266Uart1Tm1829Method>
#define B_8266_DM_TM2_4 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp8266DmaTm1829Method>
#define B_8266_BB_TM2_4 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp8266BitBangTm1829Method>
#define B_8266_U0_TM2_4 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266Uart0Tm1829Method, NeoGammaNullMethod>
#define B_8266_U1_TM2_4 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266Uart1Tm1829Method, NeoGammaNullMethod>
#define B_8266_DM_TM2_4 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266DmaTm1829Method, NeoGammaNullMethod>
#define B_8266_BB_TM2_4 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266BitBangTm1829Method, NeoGammaNullMethod>
//UCS8903
#define B_8266_U0_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod> //3 chan, esp8266, gpio1
#define B_8266_U1_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod> //3 chan, esp8266, gpio2
#define B_8266_DM_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp8266Dma800KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, gpio3
#define B_8266_BB_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp8266BitBang800KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, bb (any pin but 16)
//UCS8904 RGBW
#define B_8266_U0_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod> //4 chan, esp8266, gpio1
#define B_8266_U1_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod> //4 chan, esp8266, gpio2
#define B_8266_DM_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp8266Dma800KbpsMethod, NeoGammaNullMethod> //4 chan, esp8266, gpio3
#define B_8266_BB_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp8266BitBang800KbpsMethod, NeoGammaNullMethod> //4 chan, esp8266, bb (any pin)
#endif
/*** ESP32 Neopixel methods ***/
#ifdef ARDUINO_ARCH_ESP32
//RGB
#define B_32_RN_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32RmtNWs2812xMethod>
#define B_32_RN_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod>
#ifndef WLED_NO_I2S0_PIXELBUS
#define B_32_I0_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s0800KbpsMethod>
#define B_32_I0_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s0800KbpsMethod, NeoGammaNullMethod>
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
#define B_32_I1_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s1800KbpsMethod>
#define B_32_I1_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1800KbpsMethod, NeoGammaNullMethod>
#endif
//#define B_32_BB_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32BitBang800KbpsMethod> // NeoEsp8266BitBang800KbpsMethod
//#define B_32_BB_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32BitBang800KbpsMethod, NeoGammaNullMethod> // NeoEsp8266BitBang800KbpsMethod
//RGBW
#define B_32_RN_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32RmtNWs2812xMethod>
#define B_32_RN_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod>
#ifndef WLED_NO_I2S0_PIXELBUS
#define B_32_I0_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32I2s0800KbpsMethod>
#define B_32_I0_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32I2s0800KbpsMethod, NeoGammaNullMethod>
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
#define B_32_I1_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32I2s1800KbpsMethod>
#define B_32_I1_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32I2s1800KbpsMethod, NeoGammaNullMethod>
#endif
//#define B_32_BB_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32BitBang800KbpsMethod> // NeoEsp8266BitBang800KbpsMethod
//#define B_32_BB_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32BitBang800KbpsMethod, NeoGammaNullMethod> // NeoEsp8266BitBang800KbpsMethod
//400Kbps
#define B_32_RN_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32RmtN400KbpsMethod>
#define B_32_RN_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32RmtN400KbpsMethod, NeoGammaNullMethod>
#ifndef WLED_NO_I2S0_PIXELBUS
#define B_32_I0_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s0400KbpsMethod>
#define B_32_I0_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s0400KbpsMethod, NeoGammaNullMethod>
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
#define B_32_I1_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s1400KbpsMethod>
#define B_32_I1_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1400KbpsMethod, NeoGammaNullMethod>
#endif
//#define B_32_BB_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32BitBang400KbpsMethod> // NeoEsp8266BitBang400KbpsMethod
//#define B_32_BB_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32BitBang400KbpsMethod, NeoGammaNullMethod> // NeoEsp8266BitBang400KbpsMethod
//TM1814 (RGBW)
#define B_32_RN_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32RmtNTm1814Method>
#define B_32_RN_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32RmtNTm1814Method, NeoGammaNullMethod>
#ifndef WLED_NO_I2S0_PIXELBUS
#define B_32_I0_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32I2s0Tm1814Method>
#define B_32_I0_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32I2s0Tm1814Method, NeoGammaNullMethod>
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
#define B_32_I1_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32I2s1Tm1814Method>
#define B_32_I1_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp32I2s1Tm1814Method, NeoGammaNullMethod>
#endif
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
//TM1829 (RGB)
#define B_32_RN_TM2_3 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp32RmtNTm1829Method>
#define B_32_RN_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp32RmtNTm1829Method, NeoGammaNullMethod>
#ifndef WLED_NO_I2S0_PIXELBUS
#define B_32_I0_TM2_3 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp32I2s0Tm1829Method>
#define B_32_I0_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp32I2s0Tm1829Method, NeoGammaNullMethod>
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
#define B_32_I1_TM2_3 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp32I2s1Tm1829Method>
#define B_32_I1_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp32I2s1Tm1829Method, NeoGammaNullMethod>
#endif
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
//UCS8903
#define B_32_RN_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod>
#ifndef WLED_NO_I2S0_PIXELBUS
#define B_32_I0_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32I2s0800KbpsMethod, NeoGammaNullMethod>
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
#define B_32_I1_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp32I2s1800KbpsMethod, NeoGammaNullMethod>
#endif
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
//UCS8904
#define B_32_RN_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32RmtNWs2812xMethod, NeoGammaNullMethod>
#ifndef WLED_NO_I2S0_PIXELBUS
#define B_32_I0_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32I2s0800KbpsMethod, NeoGammaNullMethod>
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
#define B_32_I1_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32I2s1800KbpsMethod, NeoGammaNullMethod>
#endif
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
@ -184,45 +235,51 @@
//APA102
#ifdef WLED_USE_ETHERNET
// fix for #2542 (by @BlackBird77)
#define B_HS_DOT_3 NeoPixelBrightnessBus<DotStarBgrFeature, DotStarEsp32DmaHspi5MhzMethod> //hardware HSPI with DMA (ESP32 only)
#define B_HS_DOT_3 NeoPixelBusLg<DotStarBgrFeature, DotStarEsp32HspiHzMethod, NeoGammaNullMethod> //hardware HSPI (was DotStarEsp32DmaHspi5MhzMethod in NPB @ 2.6.9)
#else
#define B_HS_DOT_3 NeoPixelBrightnessBus<DotStarBgrFeature, DotStarSpi5MhzMethod> //hardware HSPI
#define B_HS_DOT_3 NeoPixelBusLg<DotStarBgrFeature, DotStarSpiHzMethod, NeoGammaNullMethod> //hardware VSPI
#endif
#define B_SS_DOT_3 NeoPixelBrightnessBus<DotStarBgrFeature, DotStarMethod> //soft SPI
#define B_SS_DOT_3 NeoPixelBusLg<DotStarBgrFeature, DotStarMethod, NeoGammaNullMethod> //soft SPI
//LPD8806
#define B_HS_LPD_3 NeoPixelBrightnessBus<Lpd8806GrbFeature, Lpd8806SpiMethod>
#define B_SS_LPD_3 NeoPixelBrightnessBus<Lpd8806GrbFeature, Lpd8806Method>
#define B_HS_LPD_3 NeoPixelBusLg<Lpd8806GrbFeature, Lpd8806SpiHzMethod, NeoGammaNullMethod>
#define B_SS_LPD_3 NeoPixelBusLg<Lpd8806GrbFeature, Lpd8806Method, NeoGammaNullMethod>
//LPD6803
#define B_HS_LPO_3 NeoPixelBrightnessBus<Lpd6803GrbFeature, Lpd6803SpiMethod>
#define B_SS_LPO_3 NeoPixelBrightnessBus<Lpd6803GrbFeature, Lpd6803Method>
#define B_HS_LPO_3 NeoPixelBusLg<Lpd6803GrbFeature, Lpd6803SpiHzMethod, NeoGammaNullMethod>
#define B_SS_LPO_3 NeoPixelBusLg<Lpd6803GrbFeature, Lpd6803Method, NeoGammaNullMethod>
//WS2801
#if defined(WLED_WS2801_SPEED_KHZ) && WLED_WS2801_SPEED_KHZ==40000
#define B_HS_WS1_3 NeoPixelBrightnessBus<NeoRbgFeature, NeoWs2801Spi40MhzMethod> // fastest bus speed (not existing method?)
#elif defined(WLED_WS2801_SPEED_KHZ) && WLED_WS2801_SPEED_KHZ==20000
#define B_HS_WS1_3 NeoPixelBrightnessBus<NeoRbgFeature, NeoWs2801Spi20MhzMethod> // 20MHz
#elif defined(WLED_WS2801_SPEED_KHZ) && WLED_WS2801_SPEED_KHZ==10000
#define B_HS_WS1_3 NeoPixelBrightnessBus<NeoRbgFeature, NeoWs2801SpiMethod> // 10MHz
#elif defined(WLED_WS2801_SPEED_KHZ) && WLED_WS2801_SPEED_KHZ==2000
#define B_HS_WS1_3 NeoPixelBrightnessBus<NeoRbgFeature, NeoWs2801Spi2MhzMethod> //slower, more compatible
#elif defined(WLED_WS2801_SPEED_KHZ) && WLED_WS2801_SPEED_KHZ==1000
#define B_HS_WS1_3 NeoPixelBrightnessBus<NeoRbgFeature, NeoWs2801Spi1MhzMethod> //slower, more compatible
#elif defined(WLED_WS2801_SPEED_KHZ) && WLED_WS2801_SPEED_KHZ==500
#define B_HS_WS1_3 NeoPixelBrightnessBus<NeoRbgFeature, NeoWs2801Spi500KhzMethod> //slower, more compatible
#ifdef WLED_USE_ETHERNET
#define B_HS_WS1_3 NeoPixelBusLg<NeoRbgFeature, Ws2801MethodBase<TwoWireHspiImple<SpiSpeedHz>>, NeoGammaNullMethod>
#else
#define B_HS_WS1_3 NeoPixelBrightnessBus<NeoRbgFeature, NeoWs2801Spi2MhzMethod> // 2MHz; slower, more compatible
#define B_HS_WS1_3 NeoPixelBusLg<NeoRbgFeature, Ws2801SpiHzMethod, NeoGammaNullMethod>
#endif
#define B_SS_WS1_3 NeoPixelBrightnessBus<NeoRbgFeature, NeoWs2801Method>
#define B_SS_WS1_3 NeoPixelBusLg<NeoRbgFeature, Ws2801Method, NeoGammaNullMethod>
//P9813
#define B_HS_P98_3 NeoPixelBrightnessBus<P9813BgrFeature, P9813SpiMethod>
#define B_SS_P98_3 NeoPixelBrightnessBus<P9813BgrFeature, P9813Method>
#define B_HS_P98_3 NeoPixelBusLg<P9813BgrFeature, P9813SpiHzMethod, NeoGammaNullMethod>
#define B_SS_P98_3 NeoPixelBusLg<P9813BgrFeature, P9813Method, NeoGammaNullMethod>
// 48bit & 64bit to 24bit & 32bit RGB(W) conversion
#define toRGBW32(c) (RGBW32((c>>40)&0xFF, (c>>24)&0xFF, (c>>8)&0xFF, (c>>56)&0xFF))
#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b))))
//handles pointer type conversion for all possible bus types
class PolyBus {
public:
// initialize SPI bus speed for DotStar methods
template <class T>
static void beginDotStar(void* busPtr, int8_t sck, int8_t miso, int8_t mosi, int8_t ss, uint16_t clock_kHz = 0U) {
T dotStar_strip = static_cast<T>(busPtr);
#ifdef ESP8266
dotStar_strip->Begin();
#else
if (sck == -1 && mosi == -1) dotStar_strip->Begin();
else dotStar_strip->Begin(sck, miso, mosi, ss);
#endif
if (clock_kHz) dotStar_strip->SetMethodSettings(NeoSpiSettings((uint32_t)clock_kHz*1000));
}
// Begin & initialize the PixelSettings for TM1814 strips.
template <class T>
static void beginTM1814(void* busPtr) {
@ -231,7 +288,7 @@ class PolyBus {
// Max current for each LED (22.5 mA).
tm1814_strip->SetPixelSettings(NeoTm1814Settings(/*R*/225, /*G*/225, /*B*/225, /*W*/225));
}
static void begin(void* busPtr, uint8_t busType, uint8_t* pins) {
static void begin(void* busPtr, uint8_t busType, uint8_t* pins, uint16_t clock_kHz = 0U) {
switch (busType) {
case I_NONE: break;
#ifdef ESP8266
@ -255,11 +312,19 @@ class PolyBus {
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->Begin(); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->Begin(); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->Begin(); break;
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Begin(); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->Begin(); break;
case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->Begin(); break;
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->Begin(); break;
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->Begin(); break;
case I_HS_DOT_3: beginDotStar<B_HS_DOT_3*>(busPtr, -1, -1, -1, -1, clock_kHz); break;
case I_HS_LPD_3: beginDotStar<B_HS_LPD_3*>(busPtr, -1, -1, -1, -1, clock_kHz); break;
case I_HS_LPO_3: beginDotStar<B_HS_LPO_3*>(busPtr, -1, -1, -1, -1, clock_kHz); break;
case I_HS_WS1_3: beginDotStar<B_HS_WS1_3*>(busPtr, -1, -1, -1, -1, clock_kHz); break;
case I_HS_P98_3: beginDotStar<B_HS_P98_3*>(busPtr, -1, -1, -1, -1, clock_kHz); break;
case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->Begin(); break;
case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->Begin(); break;
case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->Begin(); break;
case I_8266_BB_UCS_3: (static_cast<B_8266_BB_UCS_3*>(busPtr))->Begin(); break;
case I_8266_U0_UCS_4: (static_cast<B_8266_U0_UCS_4*>(busPtr))->Begin(); break;
case I_8266_U1_UCS_4: (static_cast<B_8266_U1_UCS_4*>(busPtr))->Begin(); break;
case I_8266_DM_UCS_4: (static_cast<B_8266_DM_UCS_4*>(busPtr))->Begin(); break;
case I_8266_BB_UCS_4: (static_cast<B_8266_BB_UCS_4*>(busPtr))->Begin(); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Begin(); break;
@ -296,12 +361,28 @@ class PolyBus {
case I_32_I1_TM1_4: beginTM1814<B_32_I1_TM1_4*>(busPtr); break;
case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->Begin(); break;
#endif
case I_32_RN_UCS_3: (static_cast<B_32_RN_UCS_3*>(busPtr))->Begin(); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_3: (static_cast<B_32_I0_UCS_3*>(busPtr))->Begin(); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->Begin(); break;
#endif
// case I_32_BB_UCS_3: (static_cast<B_32_BB_UCS_3*>(busPtr))->Begin(); break;
case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->Begin(); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->Begin(); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->Begin(); break;
#endif
// case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->Begin(); break;
// ESP32 can (and should, to avoid inadvertantly driving the chip select signal) specify the pins used for SPI, but only in begin()
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Begin(pins[1], -1, pins[0], -1); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->Begin(pins[1], -1, pins[0], -1); break;
case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->Begin(pins[1], -1, pins[0], -1); break;
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->Begin(pins[1], -1, pins[0], -1); break;
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->Begin(pins[1], -1, pins[0], -1); break;
case I_HS_DOT_3: beginDotStar<B_HS_DOT_3*>(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break;
case I_HS_LPD_3: beginDotStar<B_HS_LPD_3*>(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break;
case I_HS_LPO_3: beginDotStar<B_HS_LPO_3*>(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break;
case I_HS_WS1_3: beginDotStar<B_HS_WS1_3*>(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break;
case I_HS_P98_3: beginDotStar<B_HS_P98_3*>(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break;
#endif
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->Begin(); break;
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->Begin(); break;
@ -310,7 +391,7 @@ class PolyBus {
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->Begin(); break;
}
};
static void* create(uint8_t busType, uint8_t* pins, uint16_t len, uint8_t channel) {
static void* create(uint8_t busType, uint8_t* pins, uint16_t len, uint8_t channel, uint16_t clock_kHz = 0U) {
void* busPtr = nullptr;
switch (busType) {
case I_NONE: break;
@ -335,6 +416,14 @@ class PolyBus {
case I_8266_U1_TM2_3: busPtr = new B_8266_U1_TM2_4(len, pins[0]); break;
case I_8266_DM_TM2_3: busPtr = new B_8266_DM_TM2_4(len, pins[0]); break;
case I_8266_BB_TM2_3: busPtr = new B_8266_BB_TM2_4(len, pins[0]); break;
case I_8266_U0_UCS_3: busPtr = new B_8266_U0_UCS_3(len, pins[0]); break;
case I_8266_U1_UCS_3: busPtr = new B_8266_U1_UCS_3(len, pins[0]); break;
case I_8266_DM_UCS_3: busPtr = new B_8266_DM_UCS_3(len, pins[0]); break;
case I_8266_BB_UCS_3: busPtr = new B_8266_BB_UCS_3(len, pins[0]); break;
case I_8266_U0_UCS_4: busPtr = new B_8266_U0_UCS_4(len, pins[0]); break;
case I_8266_U1_UCS_4: busPtr = new B_8266_U1_UCS_4(len, pins[0]); break;
case I_8266_DM_UCS_4: busPtr = new B_8266_DM_UCS_4(len, pins[0]); break;
case I_8266_BB_UCS_4: busPtr = new B_8266_BB_UCS_4(len, pins[0]); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); break;
@ -371,6 +460,22 @@ class PolyBus {
case I_32_I1_TM1_4: busPtr = new B_32_I1_TM1_4(len, pins[0]); break;
case I_32_I1_TM2_3: busPtr = new B_32_I1_TM2_3(len, pins[0]); break;
#endif
case I_32_RN_UCS_3: busPtr = new B_32_RN_UCS_3(len, pins[0], (NeoBusChannel)channel); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_3: busPtr = new B_32_I0_UCS_3(len, pins[0]); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_3: busPtr = new B_32_I1_UCS_3(len, pins[0]); break;
#endif
// case I_32_BB_UCS_3: busPtr = new B_32_BB_UCS_3(len, pins[0], (NeoBusChannel)channel); break;
case I_32_RN_UCS_4: busPtr = new B_32_RN_UCS_4(len, pins[0], (NeoBusChannel)channel); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_4: busPtr = new B_32_I0_UCS_4(len, pins[0]); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: busPtr = new B_32_I1_UCS_4(len, pins[0]); break;
#endif
// case I_32_BB_UCS_4: busPtr = new B_32_BB_UCS_4(len, pins[0], (NeoBusChannel)channel); break;
#endif
// for 2-wire: pins[1] is clk, pins[0] is dat. begin expects (len, clk, dat)
case I_HS_DOT_3: busPtr = new B_HS_DOT_3(len, pins[1], pins[0]); break;
@ -384,7 +489,7 @@ class PolyBus {
case I_HS_P98_3: busPtr = new B_HS_P98_3(len, pins[1], pins[0]); break;
case I_SS_P98_3: busPtr = new B_SS_P98_3(len, pins[1], pins[0]); break;
}
begin(busPtr, busType, pins);
begin(busPtr, busType, pins, clock_kHz);
return busPtr;
};
static void show(void* busPtr, uint8_t busType) {
@ -411,6 +516,14 @@ class PolyBus {
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->Show(); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->Show(); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->Show(); break;
case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->Show(); break;
case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->Show(); break;
case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->Show(); break;
case I_8266_BB_UCS_3: (static_cast<B_8266_BB_UCS_3*>(busPtr))->Show(); break;
case I_8266_U0_UCS_4: (static_cast<B_8266_U0_UCS_4*>(busPtr))->Show(); break;
case I_8266_U1_UCS_4: (static_cast<B_8266_U1_UCS_4*>(busPtr))->Show(); break;
case I_8266_DM_UCS_4: (static_cast<B_8266_DM_UCS_4*>(busPtr))->Show(); break;
case I_8266_BB_UCS_4: (static_cast<B_8266_BB_UCS_4*>(busPtr))->Show(); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Show(); break;
@ -447,6 +560,22 @@ class PolyBus {
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->Show(); break;
case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->Show(); break;
#endif
case I_32_RN_UCS_3: (static_cast<B_32_RN_UCS_3*>(busPtr))->Show(); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_3: (static_cast<B_32_I0_UCS_3*>(busPtr))->Show(); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->Show(); break;
#endif
// case I_32_BB_UCS_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->Show(); break;
case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->Show(); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->Show(); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->Show(); break;
#endif
// case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->Show(); break;
#endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Show(); break;
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->Show(); break;
@ -484,6 +613,13 @@ class PolyBus {
case I_8266_U1_TM2_3: return (static_cast<B_8266_U1_TM2_4*>(busPtr))->CanShow(); break;
case I_8266_DM_TM2_3: return (static_cast<B_8266_DM_TM2_4*>(busPtr))->CanShow(); break;
case I_8266_BB_TM2_3: return (static_cast<B_8266_BB_TM2_4*>(busPtr))->CanShow(); break;
case I_8266_U0_UCS_3: return (static_cast<B_8266_U0_UCS_3*>(busPtr))->CanShow(); break;
case I_8266_U1_UCS_3: return (static_cast<B_8266_U1_UCS_3*>(busPtr))->CanShow(); break;
case I_8266_DM_UCS_3: return (static_cast<B_8266_DM_UCS_3*>(busPtr))->CanShow(); break;
case I_8266_BB_UCS_3: return (static_cast<B_8266_BB_UCS_3*>(busPtr))->CanShow(); break;
case I_8266_U0_UCS_4: return (static_cast<B_8266_U0_UCS_4*>(busPtr))->CanShow(); break;
case I_8266_U1_UCS_4: return (static_cast<B_8266_U1_UCS_4*>(busPtr))->CanShow(); break;
case I_8266_DM_UCS_4: return (static_cast<B_8266_DM_UCS_4*>(busPtr))->CanShow(); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: return (static_cast<B_32_RN_NEO_3*>(busPtr))->CanShow(); break;
@ -520,6 +656,22 @@ class PolyBus {
case I_32_I1_TM1_4: return (static_cast<B_32_I1_TM1_4*>(busPtr))->CanShow(); break;
case I_32_I1_TM2_3: return (static_cast<B_32_I1_TM2_3*>(busPtr))->CanShow(); break;
#endif
case I_32_RN_UCS_3: return (static_cast<B_32_RN_UCS_3*>(busPtr))->CanShow(); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_3: return (static_cast<B_32_I0_UCS_3*>(busPtr))->CanShow(); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_3: return (static_cast<B_32_I1_UCS_3*>(busPtr))->CanShow(); break;
#endif
// case I_32_BB_UCS_3: return (static_cast<B_32_BB_UCS_3*>(busPtr))->CanShow(); break;
case I_32_RN_UCS_4: return (static_cast<B_32_RN_UCS_4*>(busPtr))->CanShow(); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_4: return (static_cast<B_32_I0_UCS_4*>(busPtr))->CanShow(); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: return (static_cast<B_32_I1_UCS_4*>(busPtr))->CanShow(); break;
#endif
// case I_32_BB_UCS_4: return (static_cast<B_32_BB_UCS_4*>(busPtr))->CanShow(); break;
#endif
case I_HS_DOT_3: return (static_cast<B_HS_DOT_3*>(busPtr))->CanShow(); break;
case I_SS_DOT_3: return (static_cast<B_SS_DOT_3*>(busPtr))->CanShow(); break;
@ -561,36 +713,44 @@ class PolyBus {
switch (busType) {
case I_NONE: break;
#ifdef ESP8266
case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_U0_NEO_4: (static_cast<B_8266_U0_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_8266_U1_NEO_4: (static_cast<B_8266_U1_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_8266_DM_NEO_4: (static_cast<B_8266_DM_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_8266_BB_NEO_4: (static_cast<B_8266_BB_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_U0_TM1_4: (static_cast<B_8266_U0_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break;
case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break;
case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break;
case I_8266_BB_UCS_3: (static_cast<B_8266_BB_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break;
case I_8266_U0_UCS_4: (static_cast<B_8266_U0_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
case I_8266_U1_UCS_4: (static_cast<B_8266_U1_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
case I_8266_DM_UCS_4: (static_cast<B_8266_DM_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
case I_8266_BB_UCS_4: (static_cast<B_8266_BB_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
#endif
// case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
// case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
@ -599,108 +759,148 @@ class PolyBus {
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
#endif
// case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
#endif
// case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
// case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(colB)); break;
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
#endif
case I_32_RN_UCS_3: (static_cast<B_32_RN_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_3: (static_cast<B_32_I0_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break;
#endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break;
#endif
// case I_32_BB_UCS_3: (static_cast<B_32_BB_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break;
case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
#endif
// case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
#endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
}
};
static void setBrightness(void* busPtr, uint8_t busType, uint8_t b) {
static void setBrightness(void* busPtr, uint8_t busType, uint8_t b, bool immediate) { // immediate=true is for use in ABL, it applies brightness immediately (warning: inefficient)
switch (busType) {
case I_NONE: break;
#ifdef ESP8266
case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_8266_U0_NEO_4: (static_cast<B_8266_U0_NEO_4*>(busPtr))->SetBrightness(b); break;
case I_8266_U1_NEO_4: (static_cast<B_8266_U1_NEO_4*>(busPtr))->SetBrightness(b); break;
case I_8266_DM_NEO_4: (static_cast<B_8266_DM_NEO_4*>(busPtr))->SetBrightness(b); break;
case I_8266_BB_NEO_4: (static_cast<B_8266_BB_NEO_4*>(busPtr))->SetBrightness(b); break;
case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->SetBrightness(b); break;
case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->SetBrightness(b); break;
case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->SetBrightness(b); break;
case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->SetBrightness(b); break;
case I_8266_U0_TM1_4: (static_cast<B_8266_U0_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->SetBrightness(b); break;
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->SetBrightness(b); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->SetBrightness(b); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->SetBrightness(b); break;
case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U0_NEO_4: (static_cast<B_8266_U0_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U1_NEO_4: (static_cast<B_8266_U1_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_DM_NEO_4: (static_cast<B_8266_DM_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_BB_NEO_4: (static_cast<B_8266_BB_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_400_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_400_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_400_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_400_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U0_TM1_4: (static_cast<B_8266_U0_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_TM1_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_TM1_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_TM1_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_TM1_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_TM2_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_TM2_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_TM2_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_TM2_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_UCS_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_UCS_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_UCS_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_BB_UCS_3: (static_cast<B_8266_BB_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_UCS_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U0_UCS_4: (static_cast<B_8266_U0_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_UCS_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U1_UCS_4: (static_cast<B_8266_U1_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_UCS_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_DM_UCS_4: (static_cast<B_8266_DM_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_UCS_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_BB_UCS_4: (static_cast<B_8266_BB_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_UCS_4*>(busPtr))->ApplyPostAdjustments(); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif
// case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->SetBrightness(b); break;
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetBrightness(b); break;
// case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_BB_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetBrightness(b); break;
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetBrightness(b); break;
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
#endif
// case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->SetBrightness(b); break;
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetBrightness(b); break;
// case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_BB_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_400_3*>(busPtr))->ApplyPostAdjustments(); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetBrightness(b); break;
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_400_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetBrightness(b); break;
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_400_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif
// case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->SetBrightness(b); break;
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetBrightness(b); break;
// case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_BB_400_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_TM1_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_TM2_3*>(busPtr))->ApplyPostAdjustments(); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->SetBrightness(b); break;
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_TM1_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_TM2_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->SetBrightness(b); break;
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_TM1_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_TM2_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif
case I_32_RN_UCS_3: (static_cast<B_32_RN_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_UCS_3*>(busPtr))->ApplyPostAdjustments(); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_3: (static_cast<B_32_I0_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_UCS_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetBrightness(b); break;
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetBrightness(b); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->SetBrightness(b); break;
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->SetBrightness(b); break;
case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->SetBrightness(b); break;
case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->SetBrightness(b); break;
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->SetBrightness(b); break;
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->SetBrightness(b); break;
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetBrightness(b); break;
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetBrightness(b); break;
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_UCS_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif
// case I_32_BB_UCS_3: (static_cast<B_32_BB_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_BB_UCS_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_UCS_4*>(busPtr))->ApplyPostAdjustments(); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_UCS_4*>(busPtr))->ApplyPostAdjustments(); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_UCS_4*>(busPtr))->ApplyPostAdjustments(); break;
#endif
// case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_BB_UCS_4*>(busPtr))->ApplyPostAdjustments(); break;
#endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_HS_DOT_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_SS_DOT_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_HS_LPD_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_SS_LPD_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_HS_LPO_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_SS_LPO_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_HS_WS1_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_SS_WS1_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_HS_P98_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_SS_P98_3*>(busPtr))->ApplyPostAdjustments(); break;
}
};
static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) {
@ -728,6 +928,14 @@ class PolyBus {
case I_8266_U1_TM2_3: col = (static_cast<B_8266_U1_TM2_4*>(busPtr))->GetPixelColor(pix); break;
case I_8266_DM_TM2_3: col = (static_cast<B_8266_DM_TM2_4*>(busPtr))->GetPixelColor(pix); break;
case I_8266_BB_TM2_3: col = (static_cast<B_8266_BB_TM2_4*>(busPtr))->GetPixelColor(pix); break;
case I_8266_U0_UCS_3: { Rgb48Color c = (static_cast<B_8266_U0_UCS_3*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,0); } break;
case I_8266_U1_UCS_3: { Rgb48Color c = (static_cast<B_8266_U1_UCS_3*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,0); } break;
case I_8266_DM_UCS_3: { Rgb48Color c = (static_cast<B_8266_DM_UCS_3*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,0); } break;
case I_8266_BB_UCS_3: { Rgb48Color c = (static_cast<B_8266_BB_UCS_3*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,0); } break;
case I_8266_U0_UCS_4: { Rgbw64Color c = (static_cast<B_8266_U0_UCS_4*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.W>>8); } break;
case I_8266_U1_UCS_4: { Rgbw64Color c = (static_cast<B_8266_U1_UCS_4*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.W>>8); } break;
case I_8266_DM_UCS_4: { Rgbw64Color c = (static_cast<B_8266_DM_UCS_4*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.W>>8); } break;
case I_8266_BB_UCS_4: { Rgbw64Color c = (static_cast<B_8266_BB_UCS_4*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.W>>8); } break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: col = (static_cast<B_32_RN_NEO_3*>(busPtr))->GetPixelColor(pix); break;
@ -764,6 +972,22 @@ class PolyBus {
case I_32_I1_TM1_4: col = (static_cast<B_32_I1_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_I1_TM2_3: col = (static_cast<B_32_I1_TM2_3*>(busPtr))->GetPixelColor(pix); break;
#endif
case I_32_RN_UCS_3: { Rgb48Color c = (static_cast<B_32_RN_UCS_3*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,0); } break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_3: { Rgb48Color c = (static_cast<B_32_I0_UCS_3*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,0); } break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_3: { Rgb48Color c = (static_cast<B_32_I1_UCS_3*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,0); } break;
#endif
// case I_32_BB_UCS_3: col = (static_cast<B_32_BB_UCS_3*>(busPtr))->GetPixelColor(pix); break;
case I_32_RN_UCS_4: { Rgbw64Color c = (static_cast<B_32_RN_UCS_4*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.W>>8); } break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_4: { Rgbw64Color c = (static_cast<B_32_I0_UCS_4*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.W>>8); } break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: { Rgbw64Color c = (static_cast<B_32_I1_UCS_4*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.W>>8); } break;
#endif
// case I_32_BB_UCS_4: col = (static_cast<B_32_BB_UCS_4*>(busPtr))->GetPixelColor(pix); break;
#endif
case I_HS_DOT_3: col = (static_cast<B_HS_DOT_3*>(busPtr))->GetPixelColor(pix); break;
case I_SS_DOT_3: col = (static_cast<B_SS_DOT_3*>(busPtr))->GetPixelColor(pix); break;
@ -821,6 +1045,14 @@ class PolyBus {
case I_8266_U1_TM2_3: delete (static_cast<B_8266_U1_TM2_4*>(busPtr)); break;
case I_8266_DM_TM2_3: delete (static_cast<B_8266_DM_TM2_4*>(busPtr)); break;
case I_8266_BB_TM2_3: delete (static_cast<B_8266_BB_TM2_4*>(busPtr)); break;
case I_8266_U0_UCS_3: delete (static_cast<B_8266_U0_UCS_3*>(busPtr)); break;
case I_8266_U1_UCS_3: delete (static_cast<B_8266_U1_UCS_3*>(busPtr)); break;
case I_8266_DM_UCS_3: delete (static_cast<B_8266_DM_UCS_3*>(busPtr)); break;
case I_8266_BB_UCS_3: delete (static_cast<B_8266_BB_UCS_3*>(busPtr)); break;
case I_8266_U0_UCS_4: delete (static_cast<B_8266_U0_UCS_4*>(busPtr)); break;
case I_8266_U1_UCS_4: delete (static_cast<B_8266_U1_UCS_4*>(busPtr)); break;
case I_8266_DM_UCS_4: delete (static_cast<B_8266_DM_UCS_4*>(busPtr)); break;
case I_8266_BB_UCS_4: delete (static_cast<B_8266_BB_UCS_4*>(busPtr)); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: delete (static_cast<B_32_RN_NEO_3*>(busPtr)); break;
@ -857,6 +1089,22 @@ class PolyBus {
case I_32_I1_TM1_4: delete (static_cast<B_32_I1_TM1_4*>(busPtr)); break;
case I_32_I1_TM2_3: delete (static_cast<B_32_I1_TM2_3*>(busPtr)); break;
#endif
case I_32_RN_UCS_3: delete (static_cast<B_32_RN_UCS_3*>(busPtr)); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_3: delete (static_cast<B_32_I0_UCS_3*>(busPtr)); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_3: delete (static_cast<B_32_I1_UCS_3*>(busPtr)); break;
#endif
// case I_32_BB_UCS_3: delete (static_cast<B_32_BB_UCS_3*>(busPtr)); break;
case I_32_RN_UCS_4: delete (static_cast<B_32_RN_UCS_4*>(busPtr)); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_4: delete (static_cast<B_32_I0_UCS_4*>(busPtr)); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: delete (static_cast<B_32_I1_UCS_4*>(busPtr)); break;
#endif
// case I_32_BB_UCS_4: delete (static_cast<B_32_BB_UCS_4*>(busPtr)); break;
#endif
case I_HS_DOT_3: delete (static_cast<B_HS_DOT_3*>(busPtr)); break;
case I_SS_DOT_3: delete (static_cast<B_SS_DOT_3*>(busPtr)); break;
@ -912,6 +1160,10 @@ class PolyBus {
return I_8266_U0_TM1_4 + offset;
case TYPE_TM1829:
return I_8266_U0_TM2_3 + offset;
case TYPE_UCS8903:
return I_8266_U0_UCS_3 + offset;
case TYPE_UCS8904:
return I_8266_U0_UCS_4 + offset;
}
#else //ESP32
uint8_t offset = 0; //0 = RMT (num 0-7) 8 = I2S0 9 = I2S1
@ -946,6 +1198,10 @@ class PolyBus {
return I_32_RN_TM1_4 + offset;
case TYPE_TM1829:
return I_32_RN_TM2_3 + offset;
case TYPE_UCS8903:
return I_32_RN_UCS_3 + offset;
case TYPE_UCS8904:
return I_32_RN_UCS_4 + offset;
}
#endif
}

View File

@ -225,12 +225,10 @@ void handleButton()
{
static unsigned long lastRead = 0UL;
static unsigned long lastRun = 0UL;
bool analog = false;
unsigned long now = millis();
//if (strip.isUpdating()) return; // don't interfere with strip updates. Our button will still be there in 1ms (next cycle)
if (strip.isUpdating() && (millis() - lastRun < 400)) return; // be niced, but avoid button starvation
lastRun = millis();
if (strip.isUpdating() && (now - lastRun < 400)) return; // don't interfere with strip update (unless strip is updating continuously, e.g. very long strips)
lastRun = now;
for (uint8_t b=0; b<WLED_MAX_BUTTONS; b++) {
#ifdef ESP8266
@ -241,19 +239,32 @@ void handleButton()
if (usermods.handleButton(b)) continue; // did usermod handle buttons
if ((buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) && now - lastRead > ANALOG_BTN_READ_CYCLE) { // button is not a button but a potentiometer
analog = true;
handleAnalog(b); continue;
if (buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) { // button is not a button but a potentiometer
if (now - lastRead > ANALOG_BTN_READ_CYCLE) {
handleAnalog(b);
lastRead = now;
}
continue;
}
// button is not momentary, but switch. This is only suitable on pins whose on-boot state does not matter (NOT gpio0)
if (buttonType[b] == BTN_TYPE_SWITCH || buttonType[b] == BTN_TYPE_PIR_SENSOR) {
handleSwitch(b); continue;
handleSwitch(b);
continue;
}
// momentary button logic
if (isButtonPressed(b)) { // pressed
// if all macros are the same, fire action immediately on rising edge
if (macroButton[b] && macroButton[b] == macroLongPress[b] && macroButton[b] == macroDoublePress[b]) {
if (!buttonPressedBefore[b])
shortPressAction(b);
buttonPressedBefore[b] = true;
buttonPressedTime[b] = now; // continually update (for debouncing to work in release handler)
return;
}
if (!buttonPressedBefore[b]) buttonPressedTime[b] = now;
buttonPressedBefore[b] = true;
@ -267,8 +278,14 @@ void handleButton()
}
} else if (!isButtonPressed(b) && buttonPressedBefore[b]) { //released
long dur = now - buttonPressedTime[b];
// released after rising-edge short press action
if (macroButton[b] && macroButton[b] == macroLongPress[b] && macroButton[b] == macroDoublePress[b]) {
if (dur > WLED_DEBOUNCE_THRESHOLD) buttonPressedBefore[b] = false; // debounce, blocks button for 50 ms once it has been released
return;
}
if (dur < WLED_DEBOUNCE_THRESHOLD) {buttonPressedBefore[b] = false; continue;} // too short "press", debounce
bool doublePress = buttonWaitTime[b]; //did we have a short press before?
buttonWaitTime[b] = 0;
@ -305,7 +322,6 @@ void handleButton()
shortPressAction(b);
}
}
if (analog) lastRead = now;
}
// If enabled, RMT idle level is set to HIGH when off

View File

@ -156,10 +156,11 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
uint8_t ledType = elm["type"] | TYPE_WS2812_RGB;
bool reversed = elm["rev"];
bool refresh = elm["ref"] | false;
uint16_t freqkHz = elm[F("freq")] | 0; // will be in kHz for DotStar and Hz for PWM (not yet implemented fully)
ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh
uint8_t AWmode = elm[F("rgbwm")] | autoWhiteMode;
if (fromFS) {
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode);
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz);
mem += BusManager::memUsage(bc);
if (mem <= MAX_LED_MEMORY) if (busses.add(bc) == -1) break; // finalization will be done in WLED::beginStrip()
} else {
@ -296,9 +297,11 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
PinManagerPinType i2c[2] = { { i2c_sda, true }, { i2c_scl, true } };
if (i2c_scl >= 0 && i2c_sda >= 0 && pinManager.allocateMultiplePins(i2c, 2, PinOwner::HW_I2C)) {
#ifdef ESP32
Wire.setPins(i2c_sda, i2c_scl); // this will fail if Wire is initilised (Wire.begin() called prior)
if (!Wire.setPins(i2c_sda, i2c_scl)) { i2c_scl = i2c_sda = -1; } // this will fail if Wire is initilised (Wire.begin() called prior)
else Wire.begin();
#else
Wire.begin(i2c_sda, i2c_scl);
#endif
Wire.begin();
} else {
i2c_sda = -1;
i2c_scl = -1;
@ -335,7 +338,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (light_gc_col > 1.0f) gammaCorrectCol = true;
else gammaCorrectCol = false;
if (gammaCorrectVal > 1.0f && gammaCorrectVal <= 3) {
if (gammaCorrectVal != 2.8f) calcGammaTable(gammaCorrectVal);
if (gammaCorrectVal != 2.8f) NeoGammaWLEDMethod::calcGammaTable(gammaCorrectVal);
} else {
gammaCorrectVal = 1.0f; // no gamma correction
gammaCorrectBri = false;
@ -437,8 +440,16 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
getStringFromJson(mqttDeviceTopic, if_mqtt[F("topics")][F("device")], 33); // "wled/test"
getStringFromJson(mqttGroupTopic, if_mqtt[F("topics")][F("group")], 33); // ""
CJSON(retainMqttMsg, if_mqtt[F("rtn")]);
#endif
#ifndef WLED_DISABLE_ESPNOW
JsonObject remote = doc["remote"];
CJSON(enable_espnow_remote, remote[F("remote_enabled")]);
getStringFromJson(linked_remote, remote[F("linked_remote")], 13);
#endif
#ifndef WLED_DISABLE_HUESYNC
JsonObject if_hue = interfaces["hue"];
CJSON(huePollingEnabled, if_hue["en"]);
@ -735,6 +746,7 @@ void serializeConfig() {
ins["type"] = bus->getType() & 0x7F;
ins["ref"] = bus->isOffRefreshRequired();
ins[F("rgbwm")] = bus->getAutoWhiteMode();
ins[F("freq")] = bus->getFrequency();
}
JsonArray hw_com = hw.createNestedArray(F("com"));
@ -883,12 +895,20 @@ void serializeConfig() {
if_mqtt[F("user")] = mqttUser;
if_mqtt[F("pskl")] = strlen(mqttPass);
if_mqtt[F("cid")] = mqttClientID;
if_mqtt[F("rtn")] = retainMqttMsg;
JsonObject if_mqtt_topics = if_mqtt.createNestedObject(F("topics"));
if_mqtt_topics[F("device")] = mqttDeviceTopic;
if_mqtt_topics[F("group")] = mqttGroupTopic;
#endif
#ifndef WLED_DISABLE_ESPNOW
JsonObject remote = doc.createNestedObject(F("remote"));
remote[F("remote_enabled")] = enable_espnow_remote;
remote[F("linked_remote")] = linked_remote;
#endif
#ifndef WLED_DISABLE_HUESYNC
JsonObject if_hue = interfaces.createNestedObject("hue");
if_hue["en"] = huePollingEnabled;

View File

@ -302,7 +302,7 @@ uint16_t approximateKelvinFromRGB(uint32_t rgb) {
}
//gamma 2.8 lookup table used for color correction
static byte gammaT[] = {
uint8_t NeoGammaWLEDMethod::gammaT[256] = {
0, 0, 0, 0, 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, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
@ -320,27 +320,22 @@ static byte gammaT[] = {
177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };
uint8_t gamma8_cal(uint8_t b, float gamma)
{
return (int)(powf((float)b / 255.0f, gamma) * 255.0f + 0.5f);
}
// re-calculates & fills gamma table
void calcGammaTable(float gamma)
void NeoGammaWLEDMethod::calcGammaTable(float gamma)
{
for (uint16_t i = 0; i < 256; i++) {
gammaT[i] = gamma8_cal(i, gamma);
for (size_t i = 0; i < 256; i++) {
gammaT[i] = (int)(powf((float)i / 255.0f, gamma) * 255.0f + 0.5f);
}
}
// used for individual channel or brightness gamma correction
uint8_t gamma8(uint8_t b)
uint8_t NeoGammaWLEDMethod::Correct(uint8_t value)
{
return gammaT[b];
if (!gammaCorrectCol) return value;
return gammaT[value];
}
// used for color gamma correction
uint32_t gamma32(uint32_t color)
uint32_t NeoGammaWLEDMethod::Correct32(uint32_t color)
{
if (!gammaCorrectCol) return color;
uint8_t w = W(color);

View File

@ -12,6 +12,7 @@
#define DEFAULT_AP_SSID "WLED-AP"
#define DEFAULT_AP_PASS "wled1234"
#define DEFAULT_OTA_PASS "wledota"
#define DEFAULT_MDNS_NAME "x"
//increase if you need more
#ifndef WLED_MAX_USERMODS
@ -90,6 +91,21 @@
#endif
#endif
#ifndef WLED_MAX_SEGNAME_LEN
#ifdef ESP8266
#define WLED_MAX_SEGNAME_LEN 32
#else
#define WLED_MAX_SEGNAME_LEN 64
#endif
#else
#if WLED_MAX_SEGNAME_LEN<32
#undef WLED_MAX_SEGNAME_LEN
#define WLED_MAX_SEGNAME_LEN 32
#else
#warning WLED UI does not support modified maximum segment name length!
#endif
#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
@ -215,6 +231,8 @@
#define TYPE_GS8608 23 //same driver as WS2812, but will require signal 2x per second (else displays test pattern)
#define TYPE_WS2811_400KHZ 24 //half-speed WS2812 protocol, used by very old WS2811 units
#define TYPE_TM1829 25
#define TYPE_UCS8903 26
#define TYPE_UCS8904 29
#define TYPE_SK6812_RGBW 30
#define TYPE_TM1814 31
//"Analog" types (PWM) (32-47)
@ -263,7 +281,7 @@
#define BTN_TYPE_ANALOG_INVERTED 8
//Ethernet board types
#define WLED_NUM_ETH_TYPES 9
#define WLED_NUM_ETH_TYPES 11
#define WLED_ETH_NONE 0
#define WLED_ETH_WT32_ETH01 1
@ -272,6 +290,10 @@
#define WLED_ETH_QUINLED 4
#define WLED_ETH_TWILIGHTLORD 5
#define WLED_ETH_ESP32DEUX 6
#define WLED_ETH_ESP32ETHKITVE 7
#define WLED_ETH_QUINLED_OCTA 8
#define WLED_ETH_ABCWLEDV43ETH 9
#define WLED_ETH_SERG74 10
//Hue error codes
#define HUE_ERROR_INACTIVE 0
@ -313,7 +335,8 @@
// WLED Error modes
#define ERR_NONE 0 // All good :)
#define ERR_EEP_COMMIT 2 // Could not commit to EEPROM (wrong flash layout?)
#define ERR_DENIED 1 // Permission denied
#define ERR_EEP_COMMIT 2 // Could not commit to EEPROM (wrong flash layout?) OBSOLETE
#define ERR_NOBUF 3 // JSON buffer was not released in time, request cannot be handled at this time
#define ERR_JSON 9 // JSON parsing failed (input too large?)
#define ERR_FS_BEGIN 10 // Could not init filesystem (no partition?)
@ -331,6 +354,23 @@
#define NL_MODE_COLORFADE 2 //Fade to target brightness and secondary color gradually
#define NL_MODE_SUN 3 //Sunrise/sunset. Target brightness is set immediately, then Sunrise effect is started. Max 60 min.
// Settings sub page IDs
#define SUBPAGE_MENU 0
#define SUBPAGE_WIFI 1
#define SUBPAGE_LEDS 2
#define SUBPAGE_UI 3
#define SUBPAGE_SYNC 4
#define SUBPAGE_TIME 5
#define SUBPAGE_SEC 6
#define SUBPAGE_DMX 7
#define SUBPAGE_UM 8
#define SUBPAGE_UPDATE 9
#define SUBPAGE_2D 10
#define SUBPAGE_LOCK 251
#define SUBPAGE_PINREQ 252
#define SUBPAGE_CSS 253
#define SUBPAGE_JS 254
#define SUBPAGE_WELCOME 255
#define NTP_PACKET_SIZE 48
@ -363,7 +403,7 @@
#ifdef ESP8266
#define SETTINGS_STACK_BUF_SIZE 2048
#else
#define SETTINGS_STACK_BUF_SIZE 3096
#define SETTINGS_STACK_BUF_SIZE 3608 // warning: quite a large value for stack
#endif
#ifdef WLED_USE_ETHERNET
@ -435,7 +475,10 @@
#define DEFAULT_LED_COUNT 30
#endif
#define INTERFACE_UPDATE_COOLDOWN 2000 //time in ms to wait between websockets, alexa, and MQTT updates
#define INTERFACE_UPDATE_COOLDOWN 1000 // time in ms to wait between websockets, alexa, and MQTT updates
#define PIN_RETRY_COOLDOWN 3000 // time in ms after an incorrect attempt PIN and OTA pass will be rejected even if correct
#define PIN_TIMEOUT 900000 // time in ms after which the PIN will be required again, 15 minutes
// HW_PIN_SCL & HW_PIN_SDA are used for information in usermods settings page and usermods themselves
// which GPIO pins are actually used in a hardwarea layout (controller board)

View File

@ -42,6 +42,6 @@
<img alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAbUExURQAAAAB81gCU/zKq/////9bW1oCAgGhoaAAAAGPLX8AAAAAJdFJOU///////////AFNPeBIAAAAJcEhZcwAADsAAAA7AAWrWiQkAAACdSURBVDhPxc9bDoUgEANQebP/FUuHMjBGY/B+3EYR7RH0qC/ZBc6HwCljgHO+xZIVSI2sYgHaG7EBWh8jWoxTrCBFdDJ+BD4lbIHxAcz8APAVLTsrZE4eQD5qzt3cAFTYokC4YCN9Gybgu4yAQtBFLQXHuHABA7JMeOEC/E0W5uy9gv4vo5QHK2i7yq2C8UABM4HmL+CSTXCTF1DrCX6+Gp9zB5dsAAAAAElFTkSuQmCC">
<h1>404 Not Found</h1>
<b>Akemi does not know where you are headed...</b><br><br>
<button onclick="window.location.href='/sliders'">Back to controls</button>
<button onclick="window.location.href='../?sliders'">Back to controls</button>
</body>
</html>

689
wled00/data/cpal/cpal.htm Normal file
View File

@ -0,0 +1,689 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>WLED Custom Palette Editor</title>
<script type="text/javascript">
var d = document;
function gId(e) {return d.getElementById(e);}
function cE(e) {return d.createElement(e);}
</script>
<style>
body {
font-family: Arial, sans-serif;
background-color: #111;
font-size: 16px;
color: #ddd;
margin: 0 10px;
line-height: 0.5;
}
#parent-container {
position: relative;
width: 100%;
height: 20px;
}
#bottomContainer {
position: absolute;
margin-top: 50px;
}
#gradient-box {
width: 100%;
height: 100%;
}
.color-marker, .color-picker-marker {
position: absolute;
border-radius: 3px;
background-color: rgb(192, 192, 192);
border: 2px solid rgba(68, 68, 68, 0.5);
z-index: 2;
}
.color-marker {
height: 30px;
width: 7px;
top: 50%;
transform: translateY(-50%);
}
.color-picker-marker {
height: 7px;
width: 7px;
top: 150%;
}
.delete-marker {
position: absolute;
height: 5px;
width: 5px;
border-radius: 3px;
background-color: rgb(255, 255, 255);
border: 3px solid rgb(155, 40, 40);
top: 220%;
z-index: 2;
}
.color-picker {
position: absolute;
height: 1px;
width: 1px;
border: 1px;
top: 150%;
z-index: 1;
border-color: #111;
background-color: #111;
}
.buttonclass {
padding: 0;
margin: 0;
vertical-align: bottom;
background-color: #111;
}
#bottomContainer span {
display: inline-flex;
align-items: center;
color: #fff;
font-size: 12px;
vertical-align: middle;
}
#info {
display: "";
text-align: center;
color: #fff;
font-size: 12px;
position: relative;
margin-top: 10px;
line-height: 1;
}
.wrap {
width: 800px;
margin: 0 auto;
}
.palette {
height: 20px;
}
.paletteGradients {
flex: 1;
height: 20px;
border-radius: 3px;
}
.palettesMain {
margin-top: 50px;
width: 100%;
}
.palTop {
height: fit-content;
text-align: center;
color: #fff;
font-size: 12px;
position: relative;
line-height: 1;
}
.palGradientParent {
display: flex;
align-items: center;
height: fit-content;
margin-top: 10px;
text-align: center;
color: #fff;
font-size: 12px;
position: relative;
line-height: 1;
}
.buttonsDiv {
display: inline-flex;
margin-left: 5px;
width: 50px;
}
.sendSpan, .editSpan{
cursor: pointer;
}
</style>
</head>
<body>
<div id="wrap" class="wrap">
<div style="display: flex; justify-content: center;">
<h1 style="display: flex; align-items: center;">
<svg style="width:36px;height:36px;margin-right:6px;" viewBox="0 0 32 32">
<rect style="fill:#003FFF" x="6" y="22" width="8" height="4"/>
<rect style="fill:#003FFF" x="14" y="14" width="4" height="8"/>
<rect style="fill:#003FFF" x="18" y="10" width="4" height="8"/>
<rect style="fill:#003FFF" x="22" y="6" width="8" height="4"/>
</svg>
<span id="head">WLED Custom Palette Editor</span>
</h1>
</div>
<div id="parent-container">
<div id="gradient-box"></div>
</div>
<div style="display: flex; justify-content: center;">
<div id="palettes" class="palettesMain">
<div id="palTop" class="palTop">
Currently in use custom palettes
</div>
</div>
</div>
<div style="display: flex; justify-content: center;">
<div id="info">
Click on the gradient editor to add new color slider, then the colored box below the slider to change its color.
Click the red box below indicator (and confirm) to delete.
Once finished, click the arrow icon to upload into the desired slot.
To edit existing palette, click the pencil icon.
</div>
</div>
<div style="display: flex; justify-content: center;">
<div id="staticPalettes" class="palettesMain">
<div id="statpalTop" class="palTop">
Available static palettes
</div>
</div>
</div>
</body>
<script type="text/javascript">
//global variables
var gradientBox = gId('gradient-box');
var cpalc = -1;
var pxCol = {};
var tCol = {};
var rect = gradientBox.getBoundingClientRect();
var gradientLength = rect.width;
var mOffs = Math.round((gradientLength / 256) / 2) - 5;
var paletteArray = []; //Holds the palettes after load.
var svgSave = '<svg style="width:25px;height:25px" viewBox="0 0 24 24"><path fill=#fff d="M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,12M7,12L12,17V14H16V10H12V7L7,12Z"/></svg>'
var svgEdit = '<svg style="width:25px;height:25px" viewBox="0 0 24 24"><path fill=#fff d="M12,2C6.47,2 2,6.47 2,12C2,17.53 6.47,22 12,22C17.53,22 22,17.53 22,12C22,6.47 17.53,2 12,2M15.1,7.07C15.24,7.07 15.38,7.12 15.5,7.23L16.77,8.5C17,8.72 17,9.07 16.77,9.28L15.77,10.28L13.72,8.23L14.72,7.23C14.82,7.12 14.96,7.07 15.1,7.07M13.13,8.81L15.19,10.87L9.13,16.93H7.07V14.87L13.13,8.81Z"/></svg>'
function recOf() {
rect = gradientBox.getBoundingClientRect();
gradientLength = rect.width;
mOffs = Math.round((gradientLength / 256) / 2) - 5;
}
//Initiation
getInfo();
window.addEventListener('load', chkW);
window.addEventListener('resize', chkW);
gradientBox.addEventListener("click", clikOnGradient);
//Sets start and stop, mandatory
addC(0);
addC(255);
updateGradient(); //Sets the gradient at startup
function clikOnGradient(e) {
removeTrashcan(e);
addC(Math.round((e.offsetX/gradientLength)*256));
}
///////// Add a new colorMarker
function addC(truePos, thisColor = '') {
let position = -1;
let iExist = false;
const colorMarkers = gradientBox.querySelectorAll('.color-marker');
colorMarkers.forEach((colorMarker, i) => {
if (colorMarker.getAttribute("data-truepos") == truePos) {
iExist = true;
}
});
if (colorMarkers.length > 17) iExist = true;
if (iExist) return; // Exit the function early if iExist is true
if (truePos > 0 && truePos < 255) {
//calculate first available > 0
for (var i = 1; i <= 16 && position < 1; i++) {
if (!gId("colorMarker"+i)) {
position = i;
}
}
} else{
position = truePos;
}
if (thisColor == ''){
thisColor = `#${(Math.random() * 0xFFFFFF << 0).toString(16).padStart(6, '0')}`;// set random color as default
}
const colorMarker = cE('span'); // create a marker for the color position
colorMarker.className = 'color-marker';
colorMarker.id = 'colorMarker' + position.toString();
colorMarker.setAttribute("data-truepos", truePos); //Used to always have a true position no matter what screen or percentage we use
colorMarker.setAttribute("data-truecol", thisColor); //Used to hold the color of the position in the gradient connected to a true position
colorMarker.setAttribute("data-offset", mOffs);
colorMarker.addEventListener('click', stopFurtherProp); //Added to prevent the gradient click to fire when covered by a marker
colorMarker.style.left = `${Math.round((gradientLength / 256) * truePos)+mOffs}px`;
const colorPicker = cE('input');
colorPicker.type = 'color';
colorPicker.value = thisColor;
colorPicker.className = 'color-picker';
colorPicker.id = 'colorPicker' + position.toString();
colorPicker.addEventListener('input', updateGradient);
colorPicker.addEventListener('click',cpClk)
const colorPickerMarker = cE('span'); // create a marker for the color position
colorPickerMarker.className = 'color-picker-marker';
colorPickerMarker.id = 'colorPickerMarker' + position.toString();
colorPickerMarker.addEventListener('click', colClk);
colorPickerMarker.style.left = colorMarker.style.left;
colorPicker.style.left = colorMarker.style.left;
const deleteMarker = cE('span'); // create a delete marker for the color position
if (position > 0 && position < 255) {
deleteMarker.className = 'delete-marker';
deleteMarker.id = 'deleteMarker' + position.toString();
deleteMarker.addEventListener('click', (e) => {
deleteColor(e);
});
deleteMarker.style.left = colorMarker.style.left
}
colorMarker.style.backgroundColor = colorPicker.value; // set marker color to match color picker
colorPickerMarker.style.backgroundColor = colorPicker.value;
gradientBox.appendChild(colorPicker);
gradientBox.appendChild(colorMarker);
gradientBox.appendChild(colorPickerMarker);
if (position != 0 && position != 255) gradientBox.appendChild(deleteMarker); // append the marker if not start or end
//make markers slidable IF they are not the first or last slider
if (position > 0 && position < 255) makeMeDrag(gId(colorMarker.id));
setTooltipMarker(gId(colorMarker.id));
updateGradient();
}
///////// Update Gradient
function updateGradient() {
const colorMarkers = gradientBox.querySelectorAll('.color-marker');
pxCol = {};
tCol = {}
colorMarkers.forEach((colorMarker, index) => {
const thisColorPicker = gId(colorMarkers[index].id.replace('colorMarker', 'colorPicker'));
const colorToSet = thisColorPicker.value;
gId(colorMarkers[index].id.replace('colorMarker', 'colorPickerMarker')).style.backgroundColor = colorToSet;
colorMarkers[index].style.backgroundColor = colorToSet;
colorMarkers[index].setAttribute("data-truecol", colorToSet);
const tPos = colorMarkers[index].getAttribute("data-truepos");
const gradientPos = Math.round((gradientLength / 256)*tPos);
pxCol[gradientPos] = colorToSet;
tCol[tPos] = colorToSet;
});
gradientString = 'linear-gradient(to right';
Object.entries(pxCol).forEach(([p, c]) => {
gradientString += `, ${c} ${p}px`;
});
gradientString += ')';
gradientBox.style.background = gradientString;
//gId("jsonstring").innerHTML = calcJSON();
}
function stopFurtherProp(e) {
e.stopPropagation();
}
function colClk(e){
removeTrashcan(e)
e.stopPropagation();
let cp = gId(e.srcElement.id.replace("Marker",""));
cp.click();
}
function cpClk(e) {
removeTrashcan(event)
e.stopPropagation();
}
//This neat little function makes any element draggable on the X-axis.
//Just call: makeMeDrag(myElement); And you are good to go.
function makeMeDrag(elmnt) {
var posNew = 0, mousePos = 0, mouseOffset = 0
//Set these to whatever you want to limit your movement to
var rect = gradientBox.getBoundingClientRect();
var maxX = rect.right; // maximum X coordinate
var minX = rect.left; // minimum X coordinate i.e. also offset from left of screen
var gradientLength = maxX - minX + 1;
elmnt.onmousedown = dragMouseDown;
function dragMouseDown(e) {
removeTrashcan(event)
e = e || window.event;
e.preventDefault();
// get the mouse cursor position at startup:
mousePos = e.clientX;
d.onmouseup = closeDragElement;
// call a function whenever the cursor moves:
d.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
// calculate the new cursor position:
posNew = mousePos - e.clientX;
mousePos = e.clientX;
mousePosInGradient = mousePos - (minX + 1)
truePos = Math.round((mousePosInGradient/gradientLength)*256);
oldTruePos = elmnt.getAttribute("data-truepos");
// set the element's new position if new position within min/max limits:
if (truePos > 0 && truePos < 255 && oldTruePos != truePos) {
if (truePos < 64) {
thisOffset = 0;
} else if (truePos > 192) {
thisOffset = 7;
} else {
thisOffset=3;
}
elmnt.style.left = (Math.round((gradientLength/256)*truePos)+mOffs) + "px";
gId(elmnt.id.replace('colorMarker', 'colorPickerMarker')).style.left = elmnt.style.left;
gId(elmnt.id.replace('colorMarker', 'deleteMarker')).style.left = elmnt.style.left;
gId(elmnt.id.replace('colorMarker', 'colorPicker')).style.left = elmnt.style.left;
elmnt.setAttribute("data-truepos", truePos);
setTooltipMarker(elmnt);
updateGradient();
}
}
function closeDragElement() {
/* stop moving when mouse button is released:*/
d.onmouseup = null;
d.onmousemove = null;
}
}
function setTooltipMarker(elmnt) {
elmnt.setAttribute('title', `${elmnt.getAttribute("data-truepos")} : ${elmnt.getAttribute("data-truecol")}`)
}
function deleteColor(e) {
var trash = cE("div");
thisDeleteMarker = e.srcElement;
thisColorMarker = gId(thisDeleteMarker.id.replace("delete", "color"));
thisColorPickerMarker = gId(thisDeleteMarker.id.replace("delete", "colorPicker"));
thisColorPicker = gId(thisDeleteMarker.id.replace("deleteMarker", "colorPicker"));
renderOffsetX = 15 - 5;
renderX = e.srcElement.getBoundingClientRect().x - renderOffsetX;
renderY = e.srcElement.getBoundingClientRect().y + 13;
trash.id = "trash";
trash.innerHTML = '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="30px" height="30px"><path style="fill:#880000; stroke: #888888; stroke-width: -2px;stroke-dasharray: 0.1, 8;" d="M9,3V4H4V6H5V19A2,2 0 0,0 7,21H17A2,2 0 0,0 19,19V6H20V4H15V3H9M7,6H17V19H7V6M9,8V17H11V8H9M13,8V17H15V8H13Z"/></svg>';
trash.style.position = "absolute";
trash.style.left = (renderX) + "px";
trash.style.top = (renderY) + "px";
d.body.appendChild(trash);
trash.addEventListener("click", (e)=>{
trash.parentNode.removeChild(trash);
thisDeleteMarker.parentNode.removeChild(thisDeleteMarker);
thisColorPickerMarker.parentNode.removeChild(thisColorPickerMarker);
thisColorMarker.parentNode.removeChild(thisColorMarker);
thisColorPicker.parentNode.removeChild(thisColorPicker);
updateGradient();
});
e.stopPropagation();
// Add event listener to close the trashcan on outside click
d.addEventListener("click", removeTrashcan);
e.stopPropagation();
}
function removeTrashcan(event) {
trash = gId("trash");
if (event.target != trash && trash) {
trash.parentNode.removeChild(trash);
d.removeEventListener("click", removeTrashcan);
}
}
function chkW() {
//Possibly add more code that recalculates the gradient... Massive job ;)
const wrap = gId('wrap');
const head = gId('head');
if (wrap.offsetWidth < 600) {
head.style.display = 'none';
} else {
head.style.display = 'inline';
}
}
function calcJSON() {
let rStr = '{"palette":['
Object.entries(tCol).forEach(([p, c]) => {
if (p > 0) rStr += ',';
rStr += `${p},"${c.slice(1)}"`; // store in hex notation
//rStr += `${p},${parseInt(c.slice(1, 3), 16)},${parseInt(c.slice(3, 5), 16)},${parseInt(c.slice(5, 7), 16)}`;
});
rStr += ']}';
return rStr;
}
function initiateUpload(idx) {
const data = calcJSON();
const fileName = `/palette${idx}.json`;
uploadJSON(data, fileName);
}
function uploadJSON(jsonString, fileName) {
//Some indication on "I'm working"
var req = new XMLHttpRequest();
var blob = new Blob([jsonString], {type: "application/json"});
req.addEventListener('load', ()=>{
console.log(this.responseText, ' - ', this.status)
localStorage.removeItem('wledPalx');
//setTimeout(()=>{
// ss.setAttribute('fill', '#fff');
//}, 1000);
//setTimeout(()=>{window.location.href='/';},2000);
window.location.href = '/'; //Guessing we want to return ASAP when we get confirmation save is done
});
req.addEventListener('error', (e)=>{
console.log('Error: ', e); console.log(' Status: ', this.status);
//Show some error notification for some time
setTimeout(()=>{
//Remove it when time has pased
}, 1000);
});
req.open("POST", "/upload");
var formData = new FormData();
formData.append("data", blob, fileName);
req.send(formData);
return false;
}
async function getInfo() {
hst = location.host;
if (hst.length > 0 ) {
try {
var arr = [];
const response = await fetch('http://'+hst+'/json/info');
const json = await response.json();
cpalc = json.cpalcount;
fetchPalettes(cpalc-1);
} catch (error) {
console.error(error);
}
} else {
console.error('cannot identify host');
}
}
async function fetchPalettes(lastPal) {
paletteArray.length = 0;
for (let i = 0; i <= lastPal; i++) {
const url = `http://${hst}/palette${i}.json`;
try {
const response = await fetch(url);
const json = await response.json();
paletteArray.push(json);
} catch (error) {
console.error(`Error fetching JSON from ${url}: `, error);
}
}
//If there is room for more custom palettes, add an empty, gray slot
if (paletteArray.length < 10) {
//Room for one more :)
paletteArray.push({"palette":[0,70,70,70,255,70,70,70]});
}
//Get static palettes from localStorage and do some magic to reformat them into the same format as the pallete JSONs
//This code excludes any objects with "non valid integer colors", i.e. r, c1, c2, c3 and such
//This code also fixes potentially broken palettes which doesn't end on 255
//The code finally also removes any representations of the custom palettes, since we read them from file
const wledPalx = JSON.parse(localStorage.getItem('wledPalx'));
if (!wledPalx) {
alert("The cache of palettes are missig from your browser. You should probably return to the main page and let it load properly for the palettes cache to regenerate before returning here.","Missing cached palettes!")
} else {
for (const key in wledPalx.p) {
if (key > 245) {
delete wledPalx.p[key];
continue;
}
const arr = wledPalx.p[key];
let valid = true;
for (const subArr of arr) {
if (!Array.isArray(subArr) || subArr.length !== 4) {
valid = false;
break;
}
for (const val of subArr) {
if (typeof val !== 'number' || val < 0 || val > 255 || !Number.isInteger(val)) {
valid = false;
break;
}
}
}
if (!valid) {
delete wledPalx.p[key];
continue;
}
const lastArr = arr[arr.length - 1];
if (lastArr[0] !== 255) {
const copyArr = [...lastArr];
copyArr[0] = 255;
arr.push(copyArr);
}
}
const pArray = Object.entries(wledPalx.p).map(([key, value]) => ({
[key]: value.flat()
}));
paletteArray.push( ...pArray);
}
generatePaletteDivs();
}
function generatePaletteDivs() {
const palettesDiv = d.getElementById("palettes");
const staticPalettesDiv = d.getElementById("staticPalettes");
const paletteDivs = Array.from(palettesDiv.children).filter((child) => {
return child.id.match(/^palette\d$/); // match only elements with id starting with "palette" followed by a single digit
});
for (const div of paletteDivs) {
palettesDiv.removeChild(div); // remove each div that matches the above selector
}
for (let i = 0; i < paletteArray.length; i++) {
const palette = paletteArray[i];
const paletteDiv = d.createElement("div");
paletteDiv.id = `palette${i}`;
paletteDiv.classList.add("palette");
const thisKey = Object.keys(palette)[0];
paletteDiv.dataset.colarray = JSON.stringify(palette[thisKey]);
const gradientDiv = d.createElement("div");
gradientDiv.id = `paletteGradient${i}`
const buttonsDiv = d.createElement("div");
buttonsDiv.id = `buttonsDiv${i}`;
buttonsDiv.classList.add("buttonsDiv")
const sendSpan = d.createElement("span");
sendSpan.id = `sendSpan${i}`;
sendSpan.onclick = function() {initiateUpload(i)};
sendSpan.setAttribute('title', `Send current editor to slot ${i}`); // perhaps Save instead of Send?
sendSpan.innerHTML = svgSave;
sendSpan.classList.add("sendSpan")
const editSpan = d.createElement("span");
editSpan.id = `editSpan${i}`;
editSpan.onclick = function() {loadForEdit(i)};
editSpan.setAttribute('title', `Copy slot ${i} palette to editor`);
editSpan.innerHTML = svgEdit;
editSpan.classList.add("editSpan")
gradientDiv.classList.add("paletteGradients");
let gradientColors = "";
for (let j = 0; j < palette[thisKey].length; j += 2) {
const position = palette[thisKey][j];
if (typeof(palette[thisKey][j+1]) === "string") {
gradientColors += `#${palette[thisKey][j+1]} ${position/255*100}%, `;
} else {
const red = palette[thisKey][j + 1];
const green = palette[thisKey][j + 2];
const blue = palette[thisKey][j + 3];
gradientColors += `rgba(${red}, ${green}, ${blue}, 1) ${position/255*100}%, `;
j += 2;
}
}
gradientColors = gradientColors.slice(0, -2); // remove the last comma and space
gradientDiv.style.backgroundImage = `linear-gradient(to right, ${gradientColors})`;
paletteDiv.className = "palGradientParent";
if (thisKey == "palette") {
buttonsDiv.appendChild(sendSpan); //Only offer to send to custom palettes
} else{
editSpan.style.marginLeft = "25px";
}
if (i!=cpalc) {
buttonsDiv.appendChild(editSpan); //Dont offer to edit the empty spot
}
paletteDiv.appendChild(gradientDiv);
paletteDiv.appendChild(buttonsDiv);
if (thisKey == "palette") {
palettesDiv.appendChild(paletteDiv);
} else {
staticPalettesDiv.appendChild(paletteDiv);
}
}
}
function loadForEdit(i) {
d.querySelectorAll('input[id^="colorPicker"]').forEach((input) => {
input.parentNode.removeChild(input);
});
d.querySelectorAll('span[id^="colorMarker"], span[id^="colorPickerMarker"], span[id^="deleteMarker"]').forEach((span) => {
span.parentNode.removeChild(span);
});
let colArray = JSON.parse(gId(`palette${i}`).getAttribute("data-colarray"));
for (let j = 0; j < colArray.length; j += 2) {
const position = colArray[j];
let hex;
if (typeof(colArray[j+1]) === "string") {
hex = `#${colArray[j+1]}`;
} else {
const red = colArray[j + 1];
const green = colArray[j + 2];
const blue = colArray[j + 3];
hex = rgbToHex(red, green, blue);
j += 2;
}
addC(position, hex);
window.scroll(0, 0);
}
}
function rgbToHex(r, g, b) {
const hex = ((r << 16) | (g << 8) | b).toString(16);
return "#" + "0".repeat(6 - hex.length) + hex;
}
</script>
</html>

View File

@ -134,7 +134,7 @@ button {
.off {
color: var(--c-6) !important;
cursor: default !important;
/* cursor: default !important; */
}
.top .icons, .bot .icons {
@ -151,7 +151,7 @@ button {
}
.segt TD {
padding: 2px !important;
padding: 2px 0 !important;
text-align: center;
/*text-transform: uppercase;*/
}
@ -174,34 +174,62 @@ button {
}
.slider-icon {
transform: translate(3px,3px);
}
.e-icon {
transform: translateY(3px);
position: absolute;
left: 8px;
bottom: 5px;
}
.sel-icon {
transform: translateX(3px);
}
.e-icon, .sel-icon, .slider-icon {
.e-icon, .g-icon, .sel-icon, .slider-icon {
cursor: pointer;
color: var(--c-d);
}
.g-icon {
font-style: normal;
position: absolute;
top: 8px;
right: 8px;
}
/* pop-up container */
.pop {
position: absolute;
display: inline-block;
top: 0;
right: 0;
}
/* pop-up content (segment sets) */
.pop-c {
position: absolute;
background-color: var(--c-2);
border: 1px solid var(--c-8);
border-radius: 20px;
z-index: 1;
top: 3px;
right: 35px;
padding: 3px 8px 1px;
font-size: 24px;
line-height: 24px;
}
.pop-c span {
padding: 2px 6px;
}
.search-icon {
position: absolute;
top: 8px;
left: 12px;
/*pointer-events: none;*/
width: 24px;
height: 24px;
}
.clear-icon {
position: absolute;
display: none;
top: 8px;
right: 9px;
cursor: pointer;
@ -229,14 +257,12 @@ button {
#liveview {
height: 4px;
display: none;
width: 100%;
border: 0px;
}
#liveview2D {
height: 90%;
display: none;
width: 90%;
border: 0px;
position: absolute;
@ -314,14 +340,14 @@ button {
overflow: auto;
height: 100%;
overscroll-behavior: none;
}
#Effects {
padding: 0 4px;
-webkit-overflow-scrolling: touch;
}
#segutil, #segutil2, #segcont, #putil, #pcont, #pql {
width: 280px;
#segutil, #segutil2, #segcont, #putil, #pcont, #pql, #fx, #palw,
.fnd {
max-width: 280px;
font-size: 19px;
}
#putil, #segutil, #segutil2 {
@ -333,7 +359,8 @@ button {
padding-top: 12px;
}
#pql, #segcont, #pcont {
#fx, #pql, #segcont, #pcont, #sliders, #picker, #qcs-w, #hexw, #pall, #ledmap,
.slider, .filter, .option, .segname, .pname, .fnd {
margin: 0 auto;
}
@ -380,10 +407,10 @@ button {
}
#sliders {
width: 300px;
margin: 0 auto;
position: -webkit-sticky;
position: sticky;
bottom: 0;
max-width: 300px;
}
#sliders .labels {
@ -392,33 +419,49 @@ button {
}
.slider {
max-width: 300px;
min-width: 260px;
margin: 0 auto; /* add 5px; if you want some vertical space but looks ugly */
/*max-width: 300px;*/
/* margin: 5px auto; add 5px; if you want some vertical space but looks ugly */
border-radius: 24px;
position: relative;
padding-bottom: 2px;
}
/* Slider wrapper div */
.sliderwrap {
height: 30px;
width: 230px;
max-width: 230px;
position: relative;
z-index: 0;
}
#sliders .slider {
padding-right: 64px; /* offset for bubble */
}
#sliders .slider, #info .slider {
background-color: var(--c-2);
}
#sliders .sliderwrap, .sbs .sliderwrap {
left: 32px; /* offset for icon */
}
.filter, .option {
background-color: var(--c-4);
border-radius: 26px;
height: 26px;
margin: 0 auto; /* add 4-8px if you want space at the bottom */
max-width: 300px;
/* margin: 0 auto 4px; add 4-8px if you want space at the bottom */
padding: 4px 2px;
position: relative;
z-index: 1;
opacity: 1;
transition: opacity 0.5s linear, height 0.5s, transform 0.5s;
transform: scaleY(1);
}
.option {
z-index: unset;
.filter {
z-index: 1;
overflow: hidden;
}
/* Tooltip text */
@ -467,13 +510,6 @@ button {
opacity: 1;
}
#pql, .edit-icon {
display: none;
}
.hide {
display: none !important;
}
.fade {
visibility: hidden; /* hide it */
opacity: 0; /* make it transparent */
@ -530,6 +566,7 @@ button {
}
.close {
position: -webkit-sticky;
position: sticky;
top: 0;
float: right;
@ -551,7 +588,6 @@ button {
position: fixed;
top: calc(var(--th) + 5px);
left: 1px;
display: none;
cursor: pointer;
}
@ -596,14 +632,9 @@ button {
}
#info #imgw {
/*display: inline-block;*/
margin: 8px auto;
}
/*
#kv, #kn {
display: inline-block;
}
*/
#lv {
max-width: 600px;
display: inline-block;
@ -651,23 +682,27 @@ img {
#wbal .sliderdisplay { background: linear-gradient(90deg, #ff8f1f 0%, #fff 50%, #cbdbff); }
/* wrapper divs hidden by default */
#rgbwrap, #swrap, #hwrap, #kwrap, #wwrap, #wbal, #qcs-w, #hexw {
#liveview, #liveview2D, #roverstar, #pql
#rgbwrap, #swrap, #hwrap, #kwrap, #wwrap, #wbal, #qcs-w, #hexw,
.clear-icon, .edit-icon, .ptxt {
display: none;
}
.sliderbubble {
width: 24px;
position: relative;
position: absolute;
display: inline-block;
border-radius: 10px;
border-radius: 16px;
background: var(--c-3);
color: var(--c-f);
padding: 2px 4px;
padding: 4px;
font-size: 14px;
right: 3px;
transition: visibility 0.25s ease, opacity 0.25s ease;
right: 6px;
transition: visibility .25s ease,opacity .25s ease;
opacity: 0;
visibility: hidden;
/* left: 8px; */
top: 4px;
}
output.sliderbubbleshow {
@ -725,41 +760,24 @@ input[type=range]::-moz-range-thumb {
border: 2px solid var(--c-1);
}
/* Slider wrapper div */
.sliderwrap {
height: 30px;
width: 230px;
position: relative;
z-index: 0;
}
#Colors .sliderwrap {
width: 260px;
margin: 4px 0 0;
}
#Colors {
padding-top: 18px;
}
/* Dynamically hide brightness slider label */
.hd {
display: var(--bhd);
}
#briwrap {
min-width: 267px;
float: right;
margin-top: var(--bmt);
}
#picker, #qcs-w, #hexw, #pall, #ledmap {
margin: 0 auto;
width: 260px;
/*background-color: unset;*/
}
#picker {
margin-top: -10px !important;
margin-top: 8px !important;
max-width: 260px;
}
/* buttons */
@ -776,8 +794,8 @@ input[type=range]::-moz-range-thumb {
-webkit-transform:translate3d(0,0,0);
backface-visibility: hidden;
transform:translate3d(0,0,0);
overflow: clip;
text-overflow: clip;
overflow: hidden;
text-overflow: ellipsis;
border: 1px solid var(--c-3);
background-color: var(--c-3);
}
@ -808,6 +826,7 @@ input[type=range]::-moz-range-thumb {
.btn-xs, .btn-pl-del, .btn-pl-add {
width: 42px !important;
height: 42px !important;
text-overflow: clip;
}
.btn-xs {
margin: 2px 0 0 0;
@ -985,25 +1004,25 @@ textarea {
.ptxt {
margin: -1px 4px 8px !important;
display: none;
}
.stxt {
width: 50px !important;
}
.segname, .pname {
.segname, .pname, .bname {
white-space: nowrap;
/*cursor: pointer;*/
text-align: center;
overflow: clip;
overflow: hidden;
text-overflow: ellipsis;
line-height: 24px;
padding: 8px 24px;
max-width: 170px;
margin: 0 auto;
position: relative;
}
.bname {
padding: 0 24px;
}
.segname .flr, .pname .flr {
transform: rotate(0deg);
@ -1012,8 +1031,9 @@ textarea {
/* segment power wrapper */
.sbs {
padding: 4px 0 4px 8px;
/*padding: 1px 0 1px 20px;*/
display: var(--sgp);
width: 100%;
}
.pname {
@ -1055,11 +1075,9 @@ textarea {
#csl .xxs {
border: 2px solid var(--c-d) !important;
/*box-shadow: 0 0 0 2px var(--c-d);*/
}
#csl .xxs-w {
border-width: 5px !important;
/*box-shadow: 0 0 0 5px var(--c-d);*/
}
.qcs, #namelabel { /* text shadow for name to be legible on grey backround */
@ -1073,7 +1091,6 @@ textarea {
.pwr {
color: var(--c-6);
transform: translate(1px, 1px);
cursor: pointer;
}
@ -1088,18 +1105,13 @@ textarea {
}
.frz {
left: 32px;
left: 10px;
position: absolute;
top: -3px;
top: 8px;
cursor: pointer;
padding: 8px;
z-index: 1;
}
.expanded .frz {
display: none;
}
/* radiobuttons and checkmarks */
.check, .radio {
display: block;
@ -1208,10 +1220,7 @@ TD .checkmark, TD .radiomark {
border: 0px solid var(--c-f);
text-align: left;
transition: background-color 0.5s;
/*filter: brightness(1);*/
font-size: 19px;
border-radius: 21px;
/*min-width: 264px;*/
}
.seg {
@ -1244,7 +1253,6 @@ TD .checkmark, TD .radiomark {
.lbl-s {
display: inline-block;
/* margin: 10px 4px 0 0; */
margin-top: 6px;
font-size: 13px;
width: 48%;
@ -1254,10 +1262,8 @@ TD .checkmark, TD .radiomark {
/* list wrapper */
.list {
position: relative;
width: 280px;
transition: background-color 0.5s;
margin: auto auto 20px;
font-size: 19px;
margin: auto auto 10px;
line-height: 24px;
}
@ -1267,6 +1273,7 @@ TD .checkmark, TD .radiomark {
cursor: pointer;
background-color: var(--c-2);
overflow: hidden;
position: -webkit-sticky;
position: sticky;
border-radius: 21px;
margin: 13px auto 0;
@ -1295,14 +1302,11 @@ TD .checkmark, TD .radiomark {
background-color: var(--c-3);
}
/*.selected .radiomark {
border: 1px solid var(--c-3);
}*/
/* selected list item */
.lstI.selected {
top: 0;
bottom: 0;
border: 1px solid var(--c-4);
}
.lstI.sticky,
@ -1358,8 +1362,6 @@ TD .checkmark, TD .radiomark {
/* find/search element */
.fnd {
width: 280px;
margin: 0 auto;
position: relative;
}
@ -1391,7 +1393,6 @@ TD .checkmark, TD .radiomark {
.presin {
padding: 8px;
position: relative;
width: 264px;
}
.btn-s,
@ -1418,7 +1419,7 @@ TD .checkmark, TD .radiomark {
.expanded {
display: inline-block !important;
}
.expanded .segin.hide, .expanded .presin.hide, .expanded .sbs.hide {
.hide, .expanded .segin.hide, .expanded .presin.hide, .expanded .sbs.hide, .expanded .frz, .expanded .g-icon {
display: none !important;
}
@ -1469,6 +1470,16 @@ TD .checkmark, TD .radiomark {
}
}
@media all and (max-width: 1023px) {
.top button {
width: 8%;
padding: 10px 0 8px 0;
}
#buttonPcm {
display: none;
}
}
@media all and (max-width: 335px) {
.sliderbubble {
display: none;
@ -1487,38 +1498,83 @@ TD .checkmark, TD .radiomark {
}
}
@media all and (max-width: 540px) {
.top button {
width: 16.6%;
padding: 8px 0 4px 0;
}
}
@media all and (min-width: 541px) and (max-width: 719px) {
.top button {
width: 14.2%;
padding: 8px 0 4px 0;
}
}
@media all and (max-width: 719px) {
.hd {
display: none !important;
}
#briwrap {
margin-top: 0px !important;
float: none;
}
}
@media all and (max-width: 798px) {
@media all and (max-width: 420px) {
#buttonNodes {
display: none;
}
}
@media all and (max-width: 1249px) {
#buttonPcm {
@media all and (max-width: 639px) {
.top button {
width: 16.6%;
padding: 8px 0 4px 0;
}
#briwrap {
margin: 0 auto !important;
float: none;
display: inline-block;
}
.hd {
display: none !important;
}
}
@media all and (min-width: 420px) and (max-width: 639px) {
.top button {
width: 14.28%;
padding: 8px 0 4px 0;
}
}
@media all and (min-width: 640px) and (max-width: 767px) {
#buttonNodes {
display: none;
}
}
/* small screen & tablet "PC mode" support */
@media all and (min-width: 1024px) and (max-width: 1249px) {
#segutil, #segutil2, #segcont, #putil, #pcont, #pql, #fx, #palw, #psFind, #sliders {
width: 100%;
max-width: 280px;
font-size: 18px;
}
#picker {
width: 230px;
}
#putil .btn-s {
width: 114px;
}
#sliders .sliderbubble {
display: none;
}
#sliders .sliderwrap, .sbs .sliderwrap {
width: calc(100% - 42px);
}
#sliders .slider {
padding-right: 0;
}
#sliders .sliderwrap {
left: 12px;
}
.segname {
max-width: calc(100% - 110px);
}
.segt TD {
padding: 0 !important;
}
input[type="number"], input[type=text], select, textarea {
font-size: 18px;
}
input[type="number"] {
width: 32px;
}
.lstIcontent {
padding-left: 8px;
}
.revchkl {
max-width: 183px;
text-overflow: ellipsis;
overflow-x: clip;
}
}

View File

@ -67,13 +67,13 @@
<button id="buttonSr" onclick="toggleLiveview()"><i class="icons">&#xe410;</i><p class="tab-label">Peek</p></button>
<button id="buttonI" onclick="toggleInfo()"><i class="icons">&#xe066;</i><p class="tab-label">Info</p></button>
<button id="buttonNodes" onclick="toggleNodes()"><i class="icons">&#xe22d;</i><p class="tab-label">Nodes</p></button>
<button onclick="window.location.href='/settings';"><i class="icons">&#xe0a2;</i><p class="tab-label">Config</p></button>
<button onclick="window.location.href=getURL('/settings');"><i class="icons">&#xe0a2;</i><p class="tab-label">Config</p></button>
<button id="buttonPcm" onclick="togglePcMode(true)"><i class="icons">&#xe23d;</i><p class="tab-label">PC Mode</p></button>
</div>
<div id="briwrap">
<p class="hd">Brightness</p>
<div class="il">
<i class="icons slider-icon" onclick="tglTheme()">&#xe2a6;</i>
<div class="slider" style="padding-right:32px;">
<i class="icons slider-icon" onclick="tglTheme()" style="transform: translate(-32px,5px);">&#xe2a6;</i>
<div class="sliderwrap il">
<input id="sliderBri" onchange="setBri()" oninput="updateTrail(this)" max="255" min="1" type="range" value="128" />
<div class="sliderdisplay"></div>
@ -88,7 +88,7 @@
<div class ="container">
<div id="Colors" class="tabcontent">
<div id="picker" class="noslide"></div>
<div id="hwrap" class="slider">
<div id="hwrap" class="slider" style="margin-top: 20px;">
<div class="sliderwrap il">
<input id="sliderH" class="noslide" oninput="fromH()" onchange="setColor(0)" max="359" min="0" type="range" value="0" step="any">
<div class="sliderdisplay" style="background: linear-gradient(90deg, #f00 2%, #ff0 19%, #0f0 35%, #0ff 52%, #00f 68%, #f0f 85%, #f00)"></div>
@ -198,6 +198,10 @@
</label>
</div>
</div>
<div style="padding-bottom: 10px;">
<button class="btn btn-xs" type="button" onclick="window.location.href=getURL('/cpal.htm')"><i class="icons btn-icon">&#xe18a;</i></button>
<button class="btn btn-xs" type="button" onclick="palettesData=null;localStorage.removeItem('wledPalx');requestJson({rmcpal:true});setTimeout(loadPalettes,250,loadPalettesData);"><i class="icons btn-icon">&#xe037;</i></button>
</div>
</div>
</div>
@ -227,6 +231,10 @@
<input type="checkbox" data-flt="&#x1F3A8;" onchange="filterFx(this)">
<span class="checkmark"></span>
</label>
<label id="filter0D" class="check fchkl hide">&#8226;
<input type="checkbox" data-flt="&#8226;" onchange="filterFx(this)">
<span class="checkmark"></span>
</label>
<label id="filter1D" class="check fchkl">&#8942;
<input type="checkbox" data-flt="&#8942;" onchange="filterFx(this)">
<span class="checkmark"></span>
@ -245,7 +253,7 @@
</label>
</div>
<div id="slider0" class="slider">
<i class="icons slider-icon" style="cursor: pointer;" onclick="tglFreeze()">&#xe325;</i>
<i class="icons slider-icon" onclick="tglFreeze()">&#xe325;</i>
<div class="sliderwrap il">
<input id="sliderSpeed" class="noslide" onchange="setSpeed()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
@ -254,7 +262,7 @@
<span id="sliderLabel0" class="tooltiptext">Effect speed</span>
</div>
<div id="slider1" class="slider">
<i class="icons slider-icon" style="cursor: pointer;" onclick="tglLabels()">&#xe409;</i>
<i class="icons slider-icon" onclick="tglLabels()">&#xe409;</i>
<div class="sliderwrap il">
<input id="sliderIntensity" class="noslide" onchange="setIntensity()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
@ -355,7 +363,7 @@
<div>
<button class="btn infobtn" onclick="requestJson()">Refresh</button>
<button class="btn infobtn" onclick="toggleNodes()">Instance List</button>
<button class="btn infobtn" onclick="window.open('/update','_self');">Update WLED</button>
<button class="btn infobtn" onclick="window.open(getURL('/update'),'_self');">Update WLED</button>
<button class="btn infobtn" id="resetbtn" onclick="cnfReset()">Reboot WLED</button>
</div>
<br>
@ -371,8 +379,8 @@
</div>
</div>
<div id="mliveview2D" class="modal">
<div id="kliveview2D" style="width:100%; height:100%">Loading...</div><br>
<div id="mlv2D" class="modal">
<div id="klv2D" style="width:100%; height:100%">Loading...</div>
</div>
<div id="rover" class="modal">

View File

@ -1,5 +1,5 @@
//page js
var loc = false, locip;
var loc = false, locip, locproto = "http:";
var isOn = false, nlA = false, isLv = false, isInfo = false, isNodes = false, syncSend = false, syncTglRecv = true;
var hasWhite = false, hasRGB = false, hasCCT = false;
var nlDur = 60, nlTar = 0;
@ -22,11 +22,12 @@ var pN = "", pI = 0, pNum = 0;
var pmt = 1, pmtLS = 0, pmtLast = 0;
var lastinfo = {};
var isM = false, mw = 0, mh=0;
var ws, cpick, ranges;
var ws, cpick, ranges, wsRpt=0;
var cfg = {
theme:{base:"dark", bg:{url:""}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}},
comp :{colors:{picker: true, rgb: false, quick: true, hex: false},
labels:true, pcmbot:false, pid:true, seglen:false, segpwr:false, segexp:false, css:true, hdays:false}
labels:true, pcmbot:false, pid:true, seglen:false, segpwr:false, segexp:false,
css:true, hdays:false, fxdef:true}
};
var hol = [
[0,11,24,4,"https://aircoookie.github.io/xmas.png"], // christmas
@ -193,30 +194,49 @@ function loadSkinCSS(cId)
l.id = cId;
l.rel = 'stylesheet';
l.type = 'text/css';
l.href = (loc?`http://${locip}`:'.') + '/skin.css';
l.href = getURL('/skin.css');
l.media = 'all';
h.appendChild(l);
}
}
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
function onLoad()
{
if (window.location.protocol == "file:") {
let l = window.location;
if (l.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy and/or HTTPS
let pathn = l.pathname;
let paths = pathn.slice(1,pathn.endsWith('/')?-1:undefined).split("/");
//if (paths[0]==="sliders") paths.shift();
//while (paths[0]==="") paths.shift();
locproto = l.protocol;
locip = l.hostname + (l.port ? ":" + l.port : "");
if (paths.length > 0 && paths[0]!=="") {
loc = true;
locip += "/" + paths[0];
} else if (locproto==="https:") {
loc = true;
}
}
var sett = localStorage.getItem('wledUiCfg');
if (sett) cfg = mergeDeep(cfg, JSON.parse(sett));
resetPUtil();
if (localStorage.getItem('pcm') == "true" || (!/Mobi/.test(navigator.userAgent) && localStorage.getItem('pcm') == null)) togglePcMode(true);
applyCfg();
if (cfg.comp.hdays) { //load custom holiday list
fetch((loc?`http://${locip}`:'.') + "/holidays.json", { // may be loaded from external source
fetch(getURL("/holidays.json"), { // may be loaded from external source
method: 'get'
})
.then((res)=>{
@ -258,9 +278,8 @@ function onLoad()
resetUtil();
d.addEventListener("visibilitychange", handleVisibilityChange, false);
size();
//size();
gId("cv").style.opacity=0;
if (localStorage.getItem('pcm') == "true" || (!/Mobi/.test(navigator.userAgent) && localStorage.getItem('pcm') == null)) togglePcMode(true);
var sls = d.querySelectorAll('input[type="range"]');
for (var sl of sls) {
sl.addEventListener('touchstart', toggleBubble);
@ -433,9 +452,7 @@ function loadPresets(callback = null)
// afterwards
if (!callback && pmt == pmtLast) return;
var url = (loc?`http://${locip}`:'') + '/presets.json';
fetch(url, {
fetch(getURL('/presets.json'), {
method: 'get'
})
.then(res => {
@ -459,9 +476,7 @@ function loadPresets(callback = null)
function loadPalettes(callback = null)
{
var url = (loc?`http://${locip}`:'') + '/json/palettes';
fetch(url, {
fetch(getURL('/json/palettes'), {
method: 'get'
})
.then((res)=>{
@ -483,9 +498,7 @@ function loadPalettes(callback = null)
function loadFX(callback = null)
{
var url = (loc?`http://${locip}`:'') + '/json/effects';
fetch(url, {
fetch(getURL('/json/effects'), {
method: 'get'
})
.then((res)=>{
@ -497,6 +510,7 @@ function loadFX(callback = null)
populateEffects();
})
.catch((e)=>{
//setTimeout(loadFX, 250); // retry
showToast(e, true);
})
.finally(()=>{
@ -507,9 +521,7 @@ function loadFX(callback = null)
function loadFXData(callback = null)
{
var url = (loc?`http://${locip}`:'') + '/json/fxdata';
fetch(url, {
fetch(getURL('/json/fxdata'), {
method: 'get'
})
.then((res)=>{
@ -524,6 +536,7 @@ function loadFXData(callback = null)
})
.catch((e)=>{
fxdata = [];
//setTimeout(loadFXData, 250); // retry
showToast(e, true);
})
.finally(()=>{
@ -603,14 +616,19 @@ function parseInfo(i) {
syncTglRecv = i.str;
maxSeg = i.leds.maxseg;
pmt = i.fs.pmt;
gId('buttonNodes').style.display = lastinfo.ndc > 0 ? null:"none";
// do we have a matrix set-up
mw = i.leds.matrix ? i.leds.matrix.w : 0;
mh = i.leds.matrix ? i.leds.matrix.h : 0;
isM = mw>0 && mh>0;
if (!isM) {
gId("filter0D").classList.remove('hide');
gId("filter1D").classList.add('hide');
//gId("filter2D").classList.add('hide');
hideModes("2D");
gId("filter2D").classList.add('hide');
} else {
gId("filter0D").classList.add('hide');
gId("filter1D").classList.remove('hide');
gId("filter2D").classList.remove('hide');
}
// if (i.noaudio) {
// gId("filterVol").classList.add("hide");
@ -638,7 +656,7 @@ function parseInfo(i) {
function populateInfo(i)
{
var cn="";
var heap = i.freeheap/1000;
var heap = i.freeheap/1024;
heap = heap.toFixed(1);
var pwr = i.leds.pwr;
var pwru = "Not calculated";
@ -697,99 +715,110 @@ function populateSegments(s)
let sg = gId(`seg${i}`);
let exp = sg ? (sg.classList.contains('expanded') || (i===0 && cfg.comp.segexp)) : false;
let segp = `<div id="segp${i}" class="sbs">
<i class="icons e-icon pwr ${inst.on ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})">&#xe08f;</i>
<div class="sliderwrap il">
<input id="seg${i}bri" class="noslide" onchange="setSegBri(${i})" oninput="updateTrail(this)" max="255" min="1" type="range" value="${inst.bri}" />
<div class="sliderdisplay"></div>
</div>
</div>`;
// segment set icon color
let cG = "var(--c-b)";
switch (inst.set) {
case 1: cG = "var(--c-r)"; break;
case 2: cG = "var(--c-g)"; break;
case 3: cG = "var(--c-l)"; break;
}
let segp = `<div id="segp${i}" class="sbs">`+
`<i class="icons slider-icon pwr ${inst.on ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})">&#xe08f;</i>`+
`<div class="sliderwrap il">`+
`<input id="seg${i}bri" class="noslide" onchange="setSegBri(${i})" oninput="updateTrail(this)" max="255" min="1" type="range" value="${inst.bri}" />`+
`<div class="sliderdisplay"></div>`+
`</div>`+
`</div>`;
let staX = inst.start;
let stoX = inst.stop;
let staY = inst.startY;
let stoY = inst.stopY;
let isMSeg = isM && staX<mw*mh; // 2D matrix segment
let rvXck = `<label class="check revchkl">Reverse ${isM?'':'direction'}<input type="checkbox" id="seg${i}rev" onchange="setRev(${i})" ${inst.rev?"checked":""}><span class="checkmark"></span></label>`;
let miXck = `<label class="check revchkl">Mirror<input type="checkbox" id="seg${i}mi" onchange="setMi(${i})" ${inst.mi?"checked":""}><span class="checkmark"></span></label>`;
let rvYck = "", miYck ="";
if (isM && staX<mw*mh) {
if (isMSeg) {
rvYck = `<label class="check revchkl">Reverse<input type="checkbox" id="seg${i}rY" onchange="setRevY(${i})" ${inst.rY?"checked":""}><span class="checkmark"></span></label>`;
miYck = `<label class="check revchkl">Mirror<input type="checkbox" id="seg${i}mY" onchange="setMiY(${i})" ${inst.mY?"checked":""}><span class="checkmark"></span></label>`;
}
let map2D = `<div id="seg${i}map2D" data-map="map2D" class="lbl-s hide">Expand 1D FX<br>
<div class="sel-p"><select class="sel-p" id="seg${i}m12" onchange="setM12(${i})">
<option value="0" ${inst.m12==0?' selected':''}>Pixels</option>
<option value="1" ${inst.m12==1?' selected':''}>Bar</option>
<option value="2" ${inst.m12==2?' selected':''}>Arc</option>
<option value="3" ${inst.m12==3?' selected':''}>Corner</option>
</select></div>
</div>`;
let sndSim = `<div data-snd="si" class="lbl-s hide">Sound sim<br>
<div class="sel-p"><select class="sel-p" id="seg${i}si" onchange="setSi(${i})">
<option value="0" ${inst.si==0?' selected':''}>BeatSin</option>
<option value="1" ${inst.si==1?' selected':''}>WeWillRockYou</option>
<option value="2" ${inst.si==2?' selected':''}>U10_3</option>
<option value="3" ${inst.si==3?' selected':''}>U14_3</option>
</select></div>
</div>`;
cn += `<div class="seg lstI ${i==s.mainseg ? 'selected' : ''} ${exp ? "expanded":""}" id="seg${i}">
<label class="check schkl">
<input type="checkbox" id="seg${i}sel" onchange="selSeg(${i})" ${inst.sel ? "checked":""}>
<span class="checkmark"></span>
</label>
<i class="icons e-icon frz" id="seg${i}frz" onclick="event.preventDefault();tglFreeze(${i});">&#x${inst.frz ? (li.live && li.liveseg==i?'e410':'e0e8') : 'e325'};</i>
<div class="segname" onclick="selSegEx(${i})">
${inst.n ? inst.n : "Segment "+i}
<i class="icons edit-icon flr" id="seg${i}nedit" onclick="tglSegn(${i})">&#xe2c6;</i>
</div>
<i class="icons e-icon flr" id="sege${i}" onclick="expand(${i})">&#xe395;</i>
${cfg.comp.segpwr?segp:''}
<div class="segin" id="seg${i}in">
<input type="text" class="ptxt" id="seg${i}t" autocomplete="off" maxlength=32 value="${inst.n?inst.n:""}" placeholder="Enter name..."/>
<table class="infot segt">
<tr>
<td>${isM&&staX<mw*mh?'Start X':'Start LED'}</td>
<td>${isM&&staX<mw*mh?(cfg.comp.seglen?"Width":"Stop X"):(cfg.comp.seglen?"LED count":"Stop LED")}</td>
<td>${isM&&staX<mw*mh?'':'Offset'}</td>
</tr>
<tr>
<td><input class="segn" id="seg${i}s" type="number" min="0" max="${(isM&&staX<mw*mh?mw:ledCount)-1}" value="${staX}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td><input class="segn" id="seg${i}e" type="number" min="0" max="${(isM&&staX<mw*mh?mw:ledCount)}" value="${stoX-(cfg.comp.seglen?staX:0)}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td style="text-align:revert;">${isM&&staX<mw*mh?miXck+'<br>'+rvXck:''}<input class="segn ${isM&&staX<mw*mh?'hide':''}" id="seg${i}of" type="number" value="${inst.of}" oninput="updateLen(${i})"></td>
</tr>
${isM&&staX<mw*mh ? '<tr><td>Start Y</td><td>'+(cfg.comp.seglen?'Height':'Stop Y')+'</td><td></td></tr>'+
let map2D = `<div id="seg${i}map2D" data-map="map2D" class="lbl-s hide">Expand 1D FX<br>`+
`<div class="sel-p"><select class="sel-p" id="seg${i}m12" onchange="setM12(${i})">`+
`<option value="0" ${inst.m12==0?' selected':''}>Pixels</option>`+
`<option value="1" ${inst.m12==1?' selected':''}>Bar</option>`+
`<option value="2" ${inst.m12==2?' selected':''}>Arc</option>`+
`<option value="3" ${inst.m12==3?' selected':''}>Corner</option>`+
`</select></div>`+
`</div>`;
let sndSim = `<div data-snd="si" class="lbl-s hide">Sound sim<br>`+
`<div class="sel-p"><select class="sel-p" id="seg${i}si" onchange="setSi(${i})">`+
`<option value="0" ${inst.si==0?' selected':''}>BeatSin</option>`+
`<option value="1" ${inst.si==1?' selected':''}>WeWillRockYou</option>`+
`</select></div>`+
`</div>`;
cn += `<div class="seg lstI ${i==s.mainseg ? 'selected' : ''} ${exp ? "expanded":""}" id="seg${i}" data-set="${inst.set}">`+
`<label class="check schkl">`+
`<input type="checkbox" id="seg${i}sel" onchange="selSeg(${i})" ${inst.sel ? "checked":""}>`+
`<span class="checkmark"></span>`+
`</label>`+
`<div class="segname" onclick="selSegEx(${i})">`+
`<i class="icons e-icon frz" id="seg${i}frz" onclick="event.preventDefault();tglFreeze(${i});">&#x${inst.frz ? (li.live && li.liveseg==i?'e410':'e0e8') : 'e325'};</i>`+
(inst.n ? inst.n : "Segment "+i) +
`<div class="pop hide" onclick="event.preventDefault();event.stopPropagation();">`+
`<i class="icons g-icon" style="color:${cG};" onclick="this.nextElementSibling.classList.toggle('hide');">&#x278${String.fromCharCode(inst.set+"A".charCodeAt(0))};</i>`+
`<div class="pop-c hide"><span style="color:var(--c-f);" onclick="setGrp(${i},0);">&#x278A;</span><span style="color:var(--c-r);" onclick="setGrp(${i},1);">&#x278B;</span><span style="color:var(--c-g);" onclick="setGrp(${i},2);">&#x278C;</span><span style="color:var(--c-l);" onclick="setGrp(${i},3);">&#x278D;</span></div>`+
`</div> `+
`<i class="icons edit-icon flr" id="seg${i}nedit" onclick="tglSegn(${i})">&#xe2c6;</i>`+
`</div>`+
`<i class="icons e-icon flr" id="sege${i}" onclick="expand(${i})">&#xe395;</i>`+
(cfg.comp.segpwr ? segp : '') +
`<div class="segin" id="seg${i}in">`+
`<input type="text" class="ptxt" id="seg${i}t" autocomplete="off" maxlength=${li.arch=="esp8266"?32:64} value="${inst.n?inst.n:""}" placeholder="Enter name..."/>`+
`<table class="infot segt">`+
`<tr>`+
`<td>${isMSeg?'Start X':'Start LED'}</td>`+
`<td>${isMSeg?(cfg.comp.seglen?"Width":"Stop X"):(cfg.comp.seglen?"LED count":"Stop LED")}</td>`+
`<td>${isMSeg?'':'Offset'}</td>`+
`</tr>`+
`<tr>`+
`<td><input class="segn" id="seg${i}s" type="number" min="0" max="${(isMSeg?mw:ledCount)-1}" value="${staX}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>`+
`<td><input class="segn" id="seg${i}e" type="number" min="0" max="${(isMSeg?mw:ledCount)}" value="${stoX-(cfg.comp.seglen?staX:0)}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>`+
`<td ${isMSeg?'style="text-align:revert;"':''}>${isMSeg?miXck+'<br>'+rvXck:''}<input class="segn ${isMSeg?'hide':''}" id="seg${i}of" type="number" value="${inst.of}" oninput="updateLen(${i})"></td>`+
`</tr>`+
(isMSeg ? '<tr><td>Start Y</td><td>'+(cfg.comp.seglen?'Height':'Stop Y')+'</td><td></td></tr>'+
'<tr>'+
'<td><input class="segn" id="seg'+i+'sY" type="number" min="0" max="'+(mh-1)+'" value="'+staY+'" oninput="updateLen('+i+')" onkeydown="segEnter('+i+')"></td>'+
'<td><input class="segn" id="seg'+i+'eY" type="number" min="0" max="'+mh+'" value="'+(stoY-(cfg.comp.seglen?staY:0))+'" oninput="updateLen('+i+')" onkeydown="segEnter('+i+')"></td>'+
'<td style="text-align:revert;">'+miYck+'<br>'+rvYck+'</td>'+
'</tr>':''}
<tr>
<td>Grouping</td>
<td>Spacing</td>
<td></td>
</tr>
<tr>
<td><input class="segn" id="seg${i}grp" type="number" min="1" max="255" value="${inst.grp}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td><input class="segn" id="seg${i}spc" type="number" min="0" max="255" value="${inst.spc}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td style="text-align:revert;"><button class="btn btn-xs" onclick="setSeg(${i})"><i class="icons btn-icon" id="segc${i}">&#xe390;</i></button></td>
</tr>
</table>
<div class="h bp" id="seg${i}len"></div>
${!(isM&&staX<mw*mh)?rvXck:''}
${isM&&staX<mw*mh&&stoY-staY>1&&stoX-staX>1?map2D:''}
${s.AudioReactive && s.AudioReactive.on ? "" : sndSim}
<label class="check revchkl" id="seg${i}lbtm">
${isM&&staX<mw*mh?'Transpose':'Mirror effect'}${isM&&staX<mw*mh?
'</tr>' : '') +
`<tr>`+
`<td>Grouping</td>`+
`<td>Spacing</td>`+
`<td></td>`+
`</tr>`+
`<tr>`+
`<td><input class="segn" id="seg${i}grp" type="number" min="1" max="255" value="${inst.grp}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>`+
`<td><input class="segn" id="seg${i}spc" type="number" min="0" max="255" value="${inst.spc}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>`+
`<td><button class="btn btn-xs" onclick="setSeg(${i})"><i class="icons btn-icon" id="segc${i}">&#xe390;</i></button></td>`+
`</tr>`+
`</table>`+
`<div class="h bp" id="seg${i}len"></div>`+
(!isMSeg ? rvXck : '') +
(isMSeg&&stoY-staY>1&&stoX-staX>1 ? map2D : '') +
(s.AudioReactive && s.AudioReactive.on ? "" : sndSim) +
`<label class="check revchkl" id="seg${i}lbtm">`+
(isMSeg?'Transpose':'Mirror effect') + (isMSeg ?
'<input type="checkbox" id="seg'+i+'tp" onchange="setTp('+i+')" '+(inst.tp?"checked":"")+'>':
'<input type="checkbox" id="seg'+i+'mi" onchange="setMi('+i+')" '+(inst.mi?"checked":"")+'>'}
<span class="checkmark"></span>
</label>
<div class="del">
<button class="btn btn-xs" id="segr${i}" title="Repeat until end" onclick="rptSeg(${i})"><i class="icons btn-icon">&#xe22d;</i></button>
<button class="btn btn-xs" id="segd${i}" title="Delete" onclick="delSeg(${i})"><i class="icons btn-icon">&#xe037;</i></button>
</div>
</div>
${cfg.comp.segpwr?'':segp}
</div>`;
'<input type="checkbox" id="seg'+i+'mi" onchange="setMi('+i+')" '+(inst.mi?"checked":"")+'>') +
`<span class="checkmark"></span>`+
`</label>`+
`<div class="del">`+
`<button class="btn btn-xs" id="segr${i}" title="Repeat until end" onclick="rptSeg(${i})"><i class="icons btn-icon">&#xe22d;</i></button>`+
`<button class="btn btn-xs" id="segd${i}" title="Delete" onclick="delSeg(${i})"><i class="icons btn-icon">&#xe037;</i></button>`+
`</div>`+
`</div>`+
(cfg.comp.segpwr ? '' : segp) +
`</div>`;
}
gId('segcont').innerHTML = cn;
@ -797,6 +826,7 @@ function populateSegments(s)
resetUtil(noNewSegs);
if (gId('selall')) gId('selall').checked = true;
for (var i = 0; i <= lSeg; i++) {
if (!gId(`seg${i}`)) continue;
updateLen(i);
updateTrail(gId(`seg${i}bri`));
gId(`segr${i}`).classList.add("hide");
@ -806,7 +836,7 @@ function populateSegments(s)
gId(`segd${lSeg}`).classList.add("hide");
gId(`segp0`).classList.add("hide");
}
if (!isM && !noNewSegs && (cfg.comp.seglen?parseInt(gId(`seg${lSeg}s`).value):0)+parseInt(gId(`seg${lSeg}e`).value)<ledCount) gId(`segr${lSeg}`).style.display = "inline";
if (!isM && !noNewSegs && (cfg.comp.seglen?parseInt(gId(`seg${lSeg}s`).value):0)+parseInt(gId(`seg${lSeg}e`).value)<ledCount) gId(`segr${lSeg}`).classList.remove("hide");
gId('segutil2').style.display = (segCount > 1) ? "block":"none"; // rsbtn parent
if (Array.isArray(li.maps) && li.maps.length>1) {
@ -883,8 +913,21 @@ function populatePalettes()
`<div class="lstIprev" style="${genPalPrevCss(pa[0])}"></div>`
);
}
gId('pallist').innerHTML=html;
// append custom palettes (when loading for the 1st time)
if (!isEmpty(lastinfo) && lastinfo.cpalcount) {
for (let j = 0; j<lastinfo.cpalcount; j++) {
let div = d.createElement("div");
gId('pallist').appendChild(div);
div.outerHTML = generateListItemHtml(
'palette',
255-j,
'~ Custom '+j+' ~',
'setPalette',
`<div class="lstIprev" style="${genPalPrevCss(255-j)}"></div>`
);
}
}
}
function redrawPalPrev()
@ -947,27 +990,30 @@ function genPalPrevCss(id)
function generateListItemHtml(listName, id, name, clickAction, extraHtml = '', effectPar = '')
{
return `<div class="lstI${id==0?' sticky':''}" data-id="${id}" ${effectPar===''?'':'data-opt="'+effectPar+'"'}onClick="${clickAction}(${id})">
<label class="radio schkl" onclick="event.preventDefault()">
<input type="radio" value="${id}" name="${listName}">
<span class="radiomark"></span>
<div class="lstIcontent">
<span class="lstIname">
${name}
</span>
</div>
</label>
${extraHtml}
</div>`;
return `<div class="lstI${id==0?' sticky':''}" data-id="${id}" ${effectPar===''?'':'data-opt="'+effectPar+'" '}onClick="${clickAction}(${id})">`+
`<label title="(${id})" class="radio schkl" onclick="event.preventDefault()">`+ // (#1984)
`<input type="radio" value="${id}" name="${listName}">`+
`<span class="radiomark"></span>`+
`<div class="lstIcontent">`+
`<span class="lstIname">${name}</span>`+
`</div>`+
`</label>`+
extraHtml +
`</div>`;
}
function btype(b)
{
switch (b) {
case 2:
case 32: return "ESP32";
case 3:
case 33: return "ESP32-S2";
case 4:
case 34: return "ESP32-S3";
case 5:
case 35: return "ESP32-C3";
case 1:
case 82: return "ESP8266";
}
return "?";
@ -988,8 +1034,9 @@ function populateNodes(i,n)
n.nodes.sort((a,b) => (a.name).localeCompare(b.name));
for (var o of n.nodes) {
if (o.name) {
var url = `<button class="btn" title="${o.ip}" onclick="location.assign('http://${o.ip}');">${bname(o)}</button>`;
urows += inforow(url,`${btype(o.type)}<br><i>${o.vid==0?"N/A":o.vid}</i>`);
let onoff = `<i class="icons e-icon flr ${o.type&0x80?'':'off'}" onclick="rmtTgl('${o.ip}',this);"">&#xe08f;</i>`;
var url = `<button class="btn" title="${o.ip}" onclick="location.assign('http://${o.ip}');"><div class="bname">${bname(o)}</div>${o.vid<2307130?'':onoff}</button>`;
urows += inforow(url,`${btype(o.type&0x7F)}<br><i>${o.vid==0?"N/A":o.vid}</i>`);
nnodes++;
}
}
@ -1005,8 +1052,7 @@ function populateNodes(i,n)
function loadNodes()
{
var url = (loc?`http://${locip}`:'') + '/json/nodes';
fetch(url, {
fetch(getURL('/json/nodes'), {
method: 'get'
})
.then((res)=>{
@ -1147,7 +1193,6 @@ function updateUI()
gId('buttonPower').className = (isOn) ? 'active':'';
gId('buttonNl').className = (nlA) ? 'active':'';
gId('buttonSync').className = (syncSend) ? 'active':'';
showNodes();
updateSelectedFx();
updateSelectedPalette(selectedPal); // must be after updateSelectedFx() to un-hide color slots for * palettes
@ -1227,11 +1272,12 @@ function updateSelectedFx()
// hide non-0D effects if segment only has 1 pixel (0D)
var fxs = parent.querySelectorAll('.lstI');
for (const fx of fxs) {
if (!fx.dataset.opt) continue;
let opts = fx.dataset.opt.split(";");
if (fx.dataset.id>0) {
if (segLmax==0) fx.classList.add('hide'); // none of the segments selected (hide all effects)
else {
if (segLmax==1 && (!opts[3] || opts[3].indexOf("0")<0)) fx.classList.add('hide');
if ((segLmax==1 && (!opts[3] || opts[3].indexOf("0")<0)) || (!isM && opts[3] && ((opts[3].indexOf("2")>=0 && opts[3].indexOf("1")<0)))) fx.classList.add('hide');
else fx.classList.remove('hide');
}
}
@ -1241,7 +1287,7 @@ function updateSelectedFx()
var segs = gId("segcont").querySelectorAll(`div[data-map="map2D"]`);
for (const seg of segs) if (selectedName.indexOf("\u25A6")<0) seg.classList.remove('hide'); else seg.classList.add('hide');
var segs = gId("segcont").querySelectorAll(`div[data-snd="si"]`);
for (const seg of segs) if (selectedName.indexOf("\u266A")<0 && selectedName.indexOf("\266B")<0) seg.classList.add('hide'); else seg.classList.remove('hide'); // also "♫ "?
for (const seg of segs) if (selectedName.indexOf("\u266A")<0 && selectedName.indexOf("\u266B")<0) seg.classList.add('hide'); else seg.classList.remove('hide'); // also "♫ "?
}
}
@ -1267,7 +1313,8 @@ function cmpP(a, b)
function makeWS() {
if (ws || lastinfo.ws < 0) return;
ws = new WebSocket((window.location.protocol == "https:"?"wss":"ws")+'://'+(loc?locip:window.location.hostname)+'/ws');
let url = loc ? getURL('/ws').replace("http","ws") : "ws://"+window.location.hostname+"/ws";
ws = new WebSocket(url);
ws.binaryType = "arraybuffer";
ws.onmessage = (e)=>{
if (e.data instanceof ArrayBuffer) return; // liveview packet
@ -1282,7 +1329,6 @@ function makeWS() {
var i = json.info;
if (i) {
parseInfo(i);
showNodes();
if (isInfo) populateInfo(i);
} else
i = lastinfo;
@ -1292,11 +1338,12 @@ function makeWS() {
};
ws.onclose = (e)=>{
gId('connind').style.backgroundColor = "var(--c-r)";
setTimeout(makeWS,1500); // retry WS connection
if (wsRpt++ < 5) setTimeout(makeWS,1500); // retry WS connection
ws = null;
}
ws.onopen = (e)=>{
//ws.send("{'v':true}"); // unnecessary (https://github.com/Aircoookie/WLED/blob/master/wled00/ws.cpp#L18)
wsRpt = 0;
reqsLegal = true;
}
}
@ -1354,6 +1401,8 @@ function readState(s,command=false)
return true;
}
if (s.seg.length>2) d.querySelectorAll(".pop").forEach((e)=>{e.classList.remove("hide");});
var cd = gId('csl').children;
for (let e = cd.length-1; e >= 0; e--) {
cd[e].dataset.r = i.col[e][0];
@ -1545,7 +1594,6 @@ function requestJson(command=null)
if (command && !reqsLegal) return; // stop post requests from chrome onchange event on page restore
if (!jsonTimeout) jsonTimeout = setTimeout(()=>{if (ws) ws.close(); ws=null; showErrorToast()}, 3000);
var req = null;
var url = (loc?`http://${locip}`:'') + '/json/si';
var useWs = (ws && ws.readyState === WebSocket.OPEN);
var type = command ? 'post':'get';
if (command) {
@ -1566,7 +1614,7 @@ function requestJson(command=null)
return;
}
fetch(url, {
fetch(getURL('/json/si'), {
method: type,
headers: {
"Content-type": "application/json; charset=UTF-8"
@ -1587,21 +1635,8 @@ function requestJson(command=null)
if (json.success) return;
if (json.info) {
let i = json.info;
// append custom palettes (when loading for the 1st time)
if (!command && isEmpty(lastinfo) && i.cpalcount) {
for (let j = 0; j<i.cpalcount; j++) {
let div = d.createElement("div");
gId('pallist').appendChild(div);
div.outerHTML = generateListItemHtml(
'palette',
255-j,
'~ Custom '+j+' ~',
'setPalette',
`<div class="lstIprev" style="${genPalPrevCss(255-j)}"></div>`
);
}
}
parseInfo(i);
populatePalettes(i);
if (isInfo) populateInfo(i);
}
var s = json.state ? json.state : json;
@ -1610,6 +1645,7 @@ function requestJson(command=null)
//load presets and open websocket sequentially
if (!pJson || isEmpty(pJson)) setTimeout(()=>{
loadPresets(()=>{
wsRpt = 0;
if (!(ws && ws.readyState === WebSocket.OPEN)) makeWS();
});
},25);
@ -1657,27 +1693,22 @@ function toggleSync()
function toggleLiveview()
{
//WLEDSR adding liveview2D support
if (isInfo && isM) toggleInfo();
if (isNodes && isM) toggleNodes();
isLv = !isLv;
let wsOn = ws && ws.readyState === WebSocket.OPEN;
var lvID = "liveview";
if (isM) {
lvID = "liveview2D"
if (isLv) {
var cn = '<iframe id="liveview2D" src="about:blank"></iframe>';
d.getElementById('kliveview2D').innerHTML = cn;
}
gId('mliveview2D').style.transform = (isLv) ? "translateY(0px)":"translateY(100%)";
if (isM && wsOn) {
lvID += "2D";
if (isLv) gId('klv2D').innerHTML = `<iframe id="${lvID}" src="about:blank"></iframe>`;
gId('mlv2D').style.transform = (isLv) ? "translateY(0px)":"translateY(100%)";
}
gId(lvID).style.display = (isLv) ? "block":"none";
var url = (loc?`http://${locip}`:'') + "/" + lvID;
gId(lvID).src = (isLv) ? url:"about:blank";
gId('buttonSr').className = (isLv) ? "active":"";
if (!isLv && ws && ws.readyState === WebSocket.OPEN) ws.send('{"lv":false}');
gId(lvID).src = (isLv) ? getURL("/" + lvID + ((wsOn) ? "?ws":"")):"about:blank";
gId('buttonSr').classList.toggle("active");
if (!isLv && wsOn) ws.send('{"lv":false}');
size();
}
@ -1720,37 +1751,41 @@ function makeSeg()
behavior: 'smooth',
block: 'start',
});
var cn = `<div class="seg lstI expanded">
<div class="segin">
<input type="text" id="seg${lu}t" autocomplete="off" maxlength=32 value="" placeholder="New segment ${lu}"/>
<table class="segt">
<tr>
<td width="38%">${isM?'Start X':'Start LED'}</td>
<td width="38%">${isM?(cfg.comp.seglen?"Width":"Stop X"):(cfg.comp.seglen?"LED count":"Stop LED")}</td>
</tr>
<tr>
<td><input class="segn" id="seg${lu}s" type="number" min="0" max="${isM?mw-1:ledCount-1}" value="${ns}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
<td><input class="segn" id="seg${lu}e" type="number" min="0" max="${ct}" value="${ct}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
<td><button class="btn btn-xs" onclick="setSeg(${lu});"><i class="icons bth-icon" id="segc${lu}">&#xe390;</i></button></td>
</tr>
<tr id="mkSYH" class="${isM?"":"hide"}"><td>Start Y</td><td>${cfg.comp.seglen?'Height':'Stop Y'}</td></tr>
<tr id="mkSYD" class="${isM?"":"hide"}">
<td><input class="segn" id="seg${lu}sY" type="number" min="0" max="${mh-1}" value="0" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
<td><input class="segn" id="seg${lu}eY" type="number" min="0" max="${mh}" value="${isM?mh:1}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
</tr>
</table>
<div class="h" id="seg${lu}len">${ledCount - ns} LEDs</div>
<div class="c"><button class="btn btn-p" onclick="resetUtil()">Cancel</button></div>
</div>
</div>`;
var cn = `<div class="seg lstI expanded">`+
`<div class="segin">`+
`<input type="text" id="seg${lu}t" autocomplete="off" maxlength=32 value="" placeholder="New segment ${lu}"/>`+
`<table class="segt">`+
`<tr>`+
`<td width="38%">${isM?'Start X':'Start LED'}</td>`+
`<td width="38%">${isM?(cfg.comp.seglen?"Width":"Stop X"):(cfg.comp.seglen?"LED count":"Stop LED")}</td>`+
`</tr>`+
`<tr>`+
`<td><input class="segn" id="seg${lu}s" type="number" min="0" max="${isM?mw-1:ledCount-1}" value="${ns}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>`+
`<td><input class="segn" id="seg${lu}e" type="number" min="0" max="${ct}" value="${ct}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>`+
`<td><button class="btn btn-xs" onclick="setSeg(${lu});"><i class="icons bth-icon" id="segc${lu}">&#xe390;</i></button></td>`+
`</tr>`+
`<tr id="mkSYH" class="${isM?"":"hide"}"><td>Start Y</td><td>${cfg.comp.seglen?'Height':'Stop Y'}</td></tr>`+
`<tr id="mkSYD" class="${isM?"":"hide"}">`+
`<td><input class="segn" id="seg${lu}sY" type="number" min="0" max="${mh-1}" value="0" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>`+
`<td><input class="segn" id="seg${lu}eY" type="number" min="0" max="${mh}" value="${isM?mh:1}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>`+
`</tr>`+
`</table>`+
`<div class="h" id="seg${lu}len">${ledCount - ns} LEDs</div>`+
`<div class="c"><button class="btn btn-p" onclick="resetUtil()">Cancel</button></div>`+
`</div>`+
`</div>`;
gId('segutil').innerHTML = cn;
}
function resetUtil(off=false)
{
gId('segutil').innerHTML = `<div class="seg btn btn-s ${off?'off':''}" style="border-radius:24px;padding:0;">`
gId('segutil').innerHTML = `<div class="seg btn btn-s${off?' off':''}" style="padding:0;">`
+ '<label class="check schkl"><input type="checkbox" id="selall" onchange="selSegAll(this)"><span class="checkmark"></span></label>'
+ `<div class="segname" ${off?'':'onclick="makeSeg()"'}><i class="icons btn-icon">&#xe18a;</i>Add segment</div></div>`;
+ `<div class="segname" ${off?'':'onclick="makeSeg()"'}><i class="icons btn-icon">&#xe18a;</i>Add segment</div>`
+ '<div class="pop hide" onclick="event.stopPropagation();">'
+ `<i class="icons g-icon" onclick="this.nextElementSibling.classList.toggle('hide');">&#xE34B;</i>`
+ '<div class="pop-c hide"><span style="color:var(--c-f);" onclick="selGrp(0);">&#x278A;</span><span style="color:var(--c-r);" onclick="selGrp(1);">&#x278B;</span><span style="color:var(--c-g);" onclick="selGrp(2);">&#x278C;</span><span style="color:var(--c-l);" onclick="selGrp(3);">&#x278D;</span></div>'
+ '</div></div>';
}
function makePlSel(el, incPl=false)
@ -2017,14 +2052,14 @@ function tglSegn(s)
function selSegAll(o)
{
var obj = {"seg":[]};
for (let i=0; i<=lSeg; i++) obj.seg.push({"id":i,"sel":o.checked});
for (let i=0; i<=lSeg; i++) if (gId(`seg${i}`)) obj.seg.push({"id":i,"sel":o.checked});
requestJson(obj);
}
function selSegEx(s)
{
var obj = {"seg":[]};
for (let i=0; i<=lSeg; i++) obj.seg.push({"id":i,"sel":(i==s)});
for (let i=0; i<=lSeg; i++) if (gId(`seg${i}`)) obj.seg.push({"id":i,"sel":(i==s)});
obj.mainseg = s;
requestJson(obj);
}
@ -2036,6 +2071,20 @@ function selSeg(s)
requestJson(obj);
}
function selGrp(g)
{
event.preventDefault();
event.stopPropagation();
var sel = gId(`segcont`).querySelectorAll(`div[data-set="${g}"]`);
var obj = {"seg":[]};
for (let i=0; i<=lSeg; i++) if (gId(`seg${i}`)) obj.seg.push({"id":i,"sel":false});
if (sel) for (let s of sel||[]) {
let i = parseInt(s.id.substring(3));
obj.seg[i] = {"id":i,"sel":true};
}
if (obj.seg.length) requestJson(obj);
}
function rptSeg(s)
{
//TODO: 2D support
@ -2156,6 +2205,14 @@ function setTp(s)
requestJson(obj);
}
function setGrp(s, g)
{
event.preventDefault();
event.stopPropagation();
var obj = {"seg": {"id": s, "set": g}};
requestJson(obj);
}
function setSegPwr(s)
{
var pwr = gId(`seg${s}pwr`).classList.contains('act');
@ -2187,8 +2244,7 @@ function setFX(ind = null)
} else {
d.querySelector(`#fxlist input[name="fx"][value="${ind}"]`).checked = true;
}
var obj = {"seg": {"fx": parseInt(ind),"fxdef":1}}; // fxdef sets effect parameters to default values, TODO add client setting
var obj = {"seg": {"fx": parseInt(ind), "fxdef": cfg.comp.fxdef}}; // fxdef sets effect parameters to default values
requestJson(obj);
}
@ -2522,6 +2578,24 @@ function setBalance(b)
requestJson(obj);
}
function rmtTgl(ip,i) {
event.preventDefault();
event.stopPropagation();
fetch(`http://${ip}/win&T=2`, {method: 'get'})
.then((r)=>{
return r.text();
})
.then((t)=>{
let c = (new window.DOMParser()).parseFromString(t, "text/xml");
// perhaps just i.classList.toggle("off"); would be enough
if (c.getElementsByTagName('ac')[0].textContent === "0") {
i.classList.add("off");
} else {
i.classList.remove("off");
}
});
}
var hc = 0;
setInterval(()=>{
if (!isInfo) return;
@ -2544,7 +2618,7 @@ function cnfReset()
bt.innerHTML = "Confirm Reboot";
cnfr = true; return;
}
window.location.href = "/reset";
window.location.href = getURL("/reset");
}
var cnfrS = false;
@ -2598,9 +2672,7 @@ function loadPalettesData(callback = null)
function getPalettesData(page, callback)
{
var url = (loc?`http://${locip}`:'') + `/json/palx?page=${page}`;
fetch(url, {
fetch(getURL(`/json/palx?page=${page}`), {
method: 'get',
headers: {
"Content-type": "application/json; charset=UTF-8"
@ -2619,7 +2691,7 @@ function getPalettesData(page, callback)
showToast(error, true);
});
}
/*
function hideModes(txt)
{
for (let e of (gId('fxlist').querySelectorAll('.lstI')||[])) {
@ -2630,7 +2702,7 @@ function hideModes(txt)
if (f) e.classList.add('hide'); //else e.classList.remove('hide');
}
}
*/
function search(f,l=null)
{
f.nextElementSibling.style.display=(f.value!=='')?'block':'none';
@ -2795,20 +2867,16 @@ function move(e)
x0 = null;
}
function showNodes() {
gId('buttonNodes').style.display = (lastinfo.ndc > 0 && (wW > 797 || (wW > 539 && wW < 720))) ? "block":"none";
}
function size()
{
wW = window.innerWidth;
showNodes();
var h = gId('top').clientHeight;
sCol('--th', h + "px");
sCol('--bh', gId('bot').clientHeight + "px");
if (isLv) h -= 4;
sCol('--tp', h + "px");
togglePcMode();
lastw = wW;
}
function togglePcMode(fromB = false)
@ -2816,19 +2884,16 @@ function togglePcMode(fromB = false)
if (fromB) {
pcModeA = !pcModeA;
localStorage.setItem('pcm', pcModeA);
pcMode = pcModeA;
}
if (wW < 1250 && !pcMode) return;
if (!fromB && ((wW < 1250 && lastw < 1250) || (wW >= 1250 && lastw >= 1250))) return;
pcMode = (wW >= 1024) && pcModeA;
if (cpick) cpick.resize(pcMode && wW>1023 && wW<1250 ? 230 : 260); // for tablet in landscape
if (!fromB && ((wW < 1024 && lastw < 1024) || (wW >= 1024 && lastw >= 1024))) return; // no change in size and called from size()
openTab(0, true);
if (wW < 1250) {pcMode = false;}
else if (pcModeA && !fromB) pcMode = pcModeA;
updateTablinks(0);
gId('buttonPcm').className = (pcMode) ? "active":"";
gId('bot').style.height = (pcMode && !cfg.comp.pcmbot) ? "0":"auto";
sCol('--bh', gId('bot').clientHeight + "px");
_C.style.width = (pcMode)?'100%':'400%';
lastw = wW;
}
function mergeDeep(target, ...sources)
@ -2852,7 +2917,7 @@ function mergeDeep(target, ...sources)
size();
_C.style.setProperty('--n', N);
window.addEventListener('resize', size, false);
window.addEventListener('resize', size, true);
_C.addEventListener('mousedown', lock, false);
_C.addEventListener('touchstart', lock, false);

View File

@ -17,21 +17,17 @@
position: absolute;
}
</style>
</head>
<body>
<div id="canv" />
<script>
update();
var ws;
var tmout = null;
function update()
function update() // via HTTP (/json/live)
{
if (document.hidden) {
clearTimeout(tmout);
tmout = setTimeout(update, 250);
return;
}
fetch('/json/live')
fetch('./json/live')
.then(res => {
if (!res.ok) {
clearTimeout(tmout);
@ -57,8 +53,57 @@
clearTimeout(tmout);
tmout = setTimeout(update, 2500);
})
}
function S() { // Startup function (onload)
let wsOn = (window.location.href.indexOf("?ws") > 0);
if (!wsOn) {update(); return;}
// Initialize WebSocket connection
try {
ws = top.window.ws;
} catch (e) {}
if (ws && ws.readyState === WebSocket.OPEN) {
//console.info("Peek uses top WS");
ws.send("{'lv':true}");
} else {
//console.info("Peek WS opening");
let l = window.location;
let pathn = l.pathname;
let paths = pathn.slice(1,pathn.endsWith('/')?-1:undefined).split("/");
let url = l.origin.replace("http","ws");
if (paths.length > 1) {
url += "/" + paths[0];
}
ws = new WebSocket(url+"/ws");
ws.onopen = function () {
//console.info("Peek WS open");
ws.send("{'lv':true}");
}
}
ws.binaryType = "arraybuffer";
ws.addEventListener('message', (e) => {
try {
if (toString.call(e.data) === '[object ArrayBuffer]') {
let leds = new Uint8Array(event.data);
if (leds[0] != 76) return; //'L'
let str = "linear-gradient(90deg,";
let len = leds.length;
let start = leds[1]==2 ? 4 : 2; // 1 = 1D, 2 = 1D/2D (leds[2]=w, leds[3]=h)
for (i = start; 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;
}
} catch (err) {
console.error("Peek WS error:",err);
}
});
}
</script>
</head>
<body onload="S()">
<div id="canv"></div>
</body>
</html>

View File

@ -1,61 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<meta charset="utf-8">
<meta name="theme-color" content="#222222">
<title>WLED Live Preview</title>
<style>
body {
margin: 0;
}
#canv {
background: black;
filter: brightness(175%);
width: 100%;
height: 100%;
position: absolute;
}
</style>
</head>
<body>
<div id="canv"></div>
<script>
var ws;
try {
ws = top.window.ws;
} catch (e) {}
if (ws && ws.readyState === WebSocket.OPEN) {
//console.info("Peek uses top WS");
ws.send("{'lv':true}");
} else {
console.info("Peek WS opening");
ws = new WebSocket((window.location.protocol == "https:"?"wss":"ws")+"://"+document.location.host+"/ws");
ws.onopen = function () {
//console.info("Peek WS open");
ws.send("{'lv':true}");
}
}
ws.binaryType = "arraybuffer";
ws.addEventListener('message', (e) => {
try {
if (toString.call(e.data) === '[object ArrayBuffer]') {
let leds = new Uint8Array(event.data);
if (leds[0] != 76) return; //'L'
let str = "linear-gradient(90deg,";
let len = leds.length;
let start = leds[1]==2 ? 4 : 2; // 1 = 1D, 2 = 1D/2D (leds[2]=w, leds[3]=h)
for (i = start; 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;
}
} catch (err) {
console.error("Peek WS error:",err);
}
});
</script>
</body>
</html>

View File

@ -33,7 +33,14 @@
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send("{'lv':true}");
} else {
ws = new WebSocket((window.location.protocol == "https:"?"wss":"ws")+"://"+document.location.host+"/ws");
let l = window.location;
let pathn = l.pathname;
let paths = pathn.slice(1,pathn.endsWith('/')?-1:undefined).split("/");
let url = l.origin.replace("http","ws");
if (paths.length > 1) {
url += "/" + paths[0];
}
ws = new WebSocket(url+"/ws");
ws.onopen = ()=>{
ws.send("{'lv':true}");
}

View File

@ -6,8 +6,8 @@
<title>WLED Message</title>
<script>
function B() { window.history.back() };
function RS() { window.location = "/settings"; }
function RP() { top.location.href = "/"; }
function RS() { window.location = "../settings"; }
function RP() { top.location.href = "../"; }
</script>
<style>
@import url("style.css");

View File

@ -273,7 +273,7 @@ function getPixelRGBValues(base64Image) {
//Aggregated Strings That should be returned to the user
if (i > 0){
JSONledString = JSONledString + '\n';
JSONledString = JSONledString + '\n<NEXT COMMAND (multiple commands not supported in API/preset setup)>\n';
curlString = curlString + ' && ';
}
JSONledString += thisJSONledString;

View File

@ -60,7 +60,7 @@
</td>
<td class="adaptiveTD">
<select id="colorFormatSelector" class="mainSelector">
<option value="hex" selected>HEX (#f4f4f4)</option>
<option value="hex" selected>HEX (&quot;f4f4f4&quot;)</option>
<option value="dec">DEC (244,244,244)</option>
</select>
</td>
@ -71,9 +71,9 @@
</td>
<td class="adaptiveTD">
<select id="addressingSelector" class="mainSelector">
<option value="hybrid" selected>Hybrid (#f0f0f0,10, 17, #f4f4f4)</option>
<option value="range">Range (10, 17, #f4f4f4)</option>
<option value="single">Single (#f4f4f4)</option>
<option value="hybrid" selected>Hybrid (&quot;f0f0f0&quot;,10, 17, &quot;f4f4f4&quot;)</option>
<option value="range">Range (10, 17, &quot;f4f4f4&quot;)</option>
<option value="single">Single (&quot;f4f4f4&quot;)</option>
</select>
</td>
</tr>
@ -196,7 +196,7 @@
<button id="sendJSONledbutton" class="buttonclass"></button>
</div>
<div>
<h3><div id="version">Version 1.0.7</div>&nbsp;-&nbsp; <a href="https://github.com/werkstrom/WLED-PixelArtConverter/blob/main/README.md" target="_blank">Help/About</a></h3>
<h3><div id="version">Version 1.0.8</div>&nbsp;-&nbsp; <a href="https://github.com/werkstrom/WLED-PixelArtConverter/blob/main/README.md" target="_blank">Help/About</a></h3>
</div>
</div>
<div id=bottom-part style="display: none" class=bottom-part></div>

View File

@ -69,7 +69,6 @@ cjb.addEventListener('click', async () => {
lSS.addEventListener("change", gen);
szY.addEventListener("change", gen);
szX.addEventListener("change", gen);
//frm.addEventListener("change", gen);
cFS.addEventListener("change", gen);
aS.addEventListener("change", gen);
brgh.addEventListener("change", gen);
@ -115,6 +114,7 @@ cLN.oninput = () => {
frm.addEventListener("change", () => {
for (var i = 0; i < hideableRows.length; i++) {
hideableRows[i].classList.toggle("hide", frm.value !== "ha");
gen();
}
});

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@
<title>WLED Settings</title>
<script>
var d=document;
var loc = false, locip;
var loc = false, locip, locproto = "http:";
function gId(n){return d.getElementById(n);}
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) {
@ -27,16 +27,28 @@
});
}
function S() {
if (window.location.protocol == "file:") {
let l = window.location;
if (l.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy
let path = l.pathname;
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
if (paths.length > 1) {
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths[0];
}
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=0';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
}
loadJS(getURL('/settings/s.js?p=0'), false); // If we set async false, file is loaded and executed, then next statement is processed
}
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
</script>
<style>
@ -65,15 +77,15 @@
</style>
</head>
<body onload="S()">
<button type=submit id="b" onclick="window.location='/'">Back</button>
<button type="submit" onclick="window.location='./settings/wifi'">WiFi Setup</button>
<button type="submit" onclick="window.location='./settings/leds'">LED Preferences</button>
<button id="2dbtn" style="display:none;" type="submit" onclick="window.location='./settings/2D'">2D Configuration</button>
<button type="submit" onclick="window.location='./settings/ui'">User Interface</button>
<button id="dmxbtn" style="display:none;" type="submit" onclick="window.location='./settings/dmx'">DMX Output</button>
<button type="submit" onclick="window.location='./settings/sync'">Sync Interfaces</button>
<button type="submit" onclick="window.location='./settings/time'">Time & Macros</button>
<button type="submit" onclick="window.location='./settings/um'">Usermods</button>
<button type="submit" onclick="window.location='./settings/sec'">Security & Updates</button>
<button type=submit id="b" onclick="window.location=getURL('/')">Back</button>
<button type="submit" onclick="window.location=getURL('/settings/wifi')">WiFi Setup</button>
<button type="submit" onclick="window.location=getURL('/settings/leds')">LED Preferences</button>
<button id="2dbtn" style="display:none;" type="submit" onclick="window.location=getURL('/settings/2D')">2D Configuration</button>
<button type="submit" onclick="window.location=getURL('/settings/ui')">User Interface</button>
<button id="dmxbtn" style="display:none;" type="submit" onclick="window.location=getURL('/settings/dmx')">DMX Output</button>
<button type="submit" onclick="window.location=getURL('/settings/sync')">Sync Interfaces</button>
<button type="submit" onclick="window.location=getURL('/settings/time')">Time & Macros</button>
<button type="submit" onclick="window.location=getURL('/settings/um')">Usermods</button>
<button type="submit" onclick="window.location=getURL('/settings/sec')">Security & Updates</button>
</body>
</html>

View File

@ -7,11 +7,11 @@
<title>2D Set-up</title>
<script>
var d=document;
var loc = false, locip;
var loc = false, locip, locproto = "http:";
var maxPanels=64;
var ctx = null; // WLEDMM
function H(){window.open("https://kno.wled.ge/features/2D");}
function B(){window.open("/settings","_self");}
function B(){window.open(getURL("/settings"),"_self");}
function gId(n){return d.getElementById(n);}
function fS(){d.Sf.submit();} // <button type=submit> sometimes didn't work
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
@ -35,16 +35,29 @@
});
}
function S() {
if (window.location.protocol == "file:") {
let l = window.location;
if (l.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy
let path = l.pathname;
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
if (paths.length > 2) {
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths[0];
}
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=10';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
}
loadJS(getURL('/settings/s.js?p=10'), false); // If we set async false, file is loaded and executed, then next statement is processed
if (loc) d.Sf.action = getURL('/settings/2D');
}
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
function UI() {
@ -322,7 +335,7 @@ Y:<input name="P${i}Y" type="number" min="0" max="255" value="0" oninput="UI()">
<option value="1">Vertical</option>
</select><br>
Serpentine: <input type="checkbox" name="PS"><br>
<i style="color:#fa0;">Pressing Populate will create LED panel layout with pre-arranged matrix.<br>Values above <i>will not</i> affect final layout.<br>
<i class="warn">Pressing Populate will create LED panel layout with pre-arranged matrix.<br>Values above <i>will not</i> affect final layout.<br>
WARNING: You may need to update each panel parameters after they are generated.</i><br>
<button type="button" onclick="gen();expand(gId('expGen'),gId('mxGen'));">Populate</button>
</div>

View File

@ -7,9 +7,9 @@
<title>DMX Settings</title>
<script>
var d=document;
var loc = false, locip;
var loc = false, locip, locproto = "http:";
function H(){window.open("https://github.com/Aircoookie/WLED/wiki/DMX");}
function B(){window.history.back();}
function B(){window.open(getURL("/settings"),"_self");}
function GCH(num) {
d.getElementById('dmxchannels').innerHTML += "";
for (i=0;i<num;i++) {
@ -54,16 +54,29 @@
});
}
function S(){
if (window.location.protocol == "file:") {
let l = window.location;
if (l.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy
let path = l.pathname;
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
if (paths.length > 2) {
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths[0];
}
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=7';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
}
loadJS(getURL('/settings/s.js?p=7'), false); // If we set async false, file is loaded and executed, then next statement is processed
if (loc) d.Sf.action = getURL('/settings/dmx');
}
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
</script>
<style>@import url("style.css");</style>

View File

@ -10,11 +10,11 @@
d.um_p = [];
d.rsvd = [];
d.ro_gpio = [];
d.max_gpio = 39;
d.max_gpio = 50;
var customStarts=false,startsDirty=[],maxCOOverrides=5;
var loc = false, locip;
var loc = false, locip, locproto = "http:";
function H(){window.open("https://kno.wled.ge/features/settings/#led-settings");}
function B(){window.open("/settings","_self");}
function B(){window.open(getURL("/settings"),"_self");}
function gId(n){return d.getElementById(n);}
function off(n){d.getElementsByName(n)[0].value = -1;}
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
@ -26,8 +26,12 @@
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
GetV();checkSi();setABL();
GetV();
checkSi();
setABL();
d.Sf.addEventListener("submit", trySubmit);
if (d.um_p[0]==-1) d.um_p.shift();
pinDropdowns();
});
// error event
scE.addEventListener("error", (ev) => {
@ -49,7 +53,7 @@
maxB = b; maxV = v; maxM = m; maxPB = p; maxL = l;
}
function pinsOK() {
var LCs = d.getElementsByTagName("input");
var LCs = d.Sf.querySelectorAll("#mLC input[name^=L]"); // input fields
for (i=0; i<LCs.length; i++) {
var nm = LCs[i].name.substring(0,2);
// ignore IP address
@ -59,23 +63,36 @@
if (t>=80) continue;
}
//check for pin conflicts
if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3" || nm=="L4" || nm=="RL" || nm=="BT" || nm=="IR")
if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3" || nm=="L4"/* || nm=="RL" || nm=="BT" || nm=="IR"*/)
if (LCs[i].value!="" && LCs[i].value!="-1") {
var p = []; // used pin array
for (k=0;k<d.rsvd.length;k++) p.push(d.rsvd[k]); // fill with reservations
for (k=0;k<d.um_p.length;k++) p.push(d.um_p[k]); // fill with usermod pins
if (p.some((e)=>e==parseInt(LCs[i].value,10))) {alert(`Sorry, pins ${JSON.stringify(p)} can't be used.`);LCs[i].value="";LCs[i].focus();return false;}
else if (!(nm == "IR" || nm=="BT") && d.ro_gpio.some((e)=>e==parseInt(LCs[i].value,10))) {alert(`Sorry, pins ${JSON.stringify(d.ro_gpio)} are input only.`);LCs[i].value="";LCs[i].focus();return false;}
for (j=i+1; j<LCs.length; j++)
{
var p = d.rsvd.concat(d.um_p); // used pin array
d.Sf.querySelectorAll("select.pin").forEach((e)=>{if(e.value>-1)p.push(parseInt(e.value));}) // buttons, IR & relay
if (p.some((e)=>e==parseInt(LCs[i].value))) {
alert(`Sorry, pins ${JSON.stringify(p)} can't be used.`);
LCs[i].value="";
LCs[i].focus();
return false;
}
else if (/*!(nm == "IR" || nm=="BT") &&*/ d.ro_gpio.some((e)=>e==parseInt(LCs[i].value))) {
alert(`Sorry, pins ${JSON.stringify(d.ro_gpio)} are input only.`);
LCs[i].value="";
LCs[i].focus();
return false;
}
for (j=i+1; j<LCs.length; j++) {
var n2 = LCs[j].name.substring(0,2);
if (n2=="L0" || n2=="L1" || n2=="L2" || n2=="L3" || n2=="L4" || n2=="RL" || n2=="BT" || n2=="IR") {
if (n2=="L0" || n2=="L1" || n2=="L2" || n2=="L3" || n2=="L4"/* || n2=="RL" || n2=="BT" || n2=="IR"*/) {
if (n2.substring(0,1)==="L") {
var m = LCs[j].name.substring(2);
var t2 = parseInt(d.getElementsByName("LT"+m)[0].value, 10);
if (t2>=80) continue;
}
if (LCs[j].value!="" && LCs[i].value==LCs[j].value) {alert(`Pin conflict between ${LCs[i].name}/${LCs[j].name}!`);LCs[j].value="";LCs[j].focus();return false;}
if (LCs[j].value!="" && LCs[i].value==LCs[j].value) {
alert(`Pin conflict between ${LCs[i].name}/${LCs[j].name}!`);
LCs[j].value="";
LCs[j].focus();
return false;
}
}
}
}
@ -96,6 +113,7 @@
gId('abl').style.display = (en) ? 'inline':'none';
gId('psu2').style.display = (en) ? 'inline':'none';
if (d.Sf.LA.value > 0) setABL();
UI();
}
function enLA()
{
@ -117,28 +135,28 @@
default: gId('LAdis').style.display = 'inline';
}
gId('m1').innerHTML = maxM;
d.getElementsByName("Sf")[0].addEventListener("submit", trySubmit);
UI();
}
//returns mem usage
function getMem(t, n) {
let len = parseInt(d.getElementsByName("LC"+n)[0].value);
len += parseInt(d.getElementsByName("SL"+n)[0].value); // skipped LEDs are allocated too
let dbl = 0;
if (d.Sf.LD.checked) dbl = len * 3; // double buffering
if (t < 32) {
if (t==26 || t==29) len *= 2; // 16 bit LEDs
if (maxM < 10000 && d.getElementsByName("L0"+n)[0].value == 3) { //8266 DMA uses 5x the mem
if (t > 29) return len*20; //RGBW
return len*15;
if (t > 28) return len*20 + dbl; //RGBW
return len*15 + dbl;
} else if (maxM >= 10000) //ESP32 RMT uses double buffer?
{
if (t > 29) return len*8; //RGBW
return len*6;
if (t > 28) return len*8 + dbl; //RGBW
return len*6 + dbl;
}
if (t > 29) return len*4; //RGBW
return len*3;
if (t > 28) return len*4 + dbl; //RGBW
return len*3 + dbl;
}
if (t > 31 && t < 48) return 5;
if (t == 44 || t == 45) return len*4; //RGBW
return len*3;
if (t > 31 && t < 48) return 5; // analog
return len*3 + dbl;
}
function UI(change=false)
@ -151,15 +169,13 @@
else if (d.Sf.LA.value > 0) laprev = d.Sf.LA.value;
// enable/disable LED fields
var s = d.getElementsByTagName("select");
for (i=0; i<s.length; i++) {
d.Sf.querySelectorAll("#mLC select[name^=LT]").forEach((s)=>{
// is the field a LED type?
if (s[i].name.substring(0,2)=="LT") {
var n = s[i].name.substring(2);
var t = parseInt(s[i].value,10);
var n = s.name.substring(2);
var t = parseInt(s.value);
gId("p0d"+n).innerHTML = (t>=80 && t<96) ? "IP address:" : (t > 49) ? "Data GPIO:" : (t > 41) ? "GPIOs:" : "GPIO:";
gId("p1d"+n).innerHTML = (t> 49 && t<64) ? "Clk GPIO:" : "";
var LK = d.getElementsByName("L1"+n)[0]; // clock pin
//var LK = d.getElementsByName("L1"+n)[0]; // clock pin
memu += getMem(t, n); // calc memory
@ -184,19 +200,19 @@
if (t > 31 && t < 48) d.getElementsByName("LC"+n)[0].value = 1; // for sanity change analog count just to 1 LED
}
gId("rf"+n).onclick = (t == 31) ? (()=>{return false}) : (()=>{}); // prevent change for TM1814
gRGBW |= isRGBW = ((t > 17 && t < 22) || t == 30 || t == 31 || (t > 40 && t < 46 && t != 43) || t == 88); // RGBW checkbox, TYPE_xxxx values from const.h
gRGBW |= isRGBW = ((t > 17 && t < 22) || (t > 28 && t < 32) || (t > 40 && t < 46 && t != 43) || t == 88); // RGBW checkbox, TYPE_xxxx values from const.h
gId("co"+n).style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? "none":"inline"; // hide color order for PWM
gId("dig"+n+"w").style.display = (t == 30 || t == 31) ? "inline":"none"; // show swap channels dropdown
if (!(t == 30 || t == 31)) d.getElementsByName("WO"+n)[0].value = 0; // reset swapping
gId("dig"+n+"w").style.display = (t > 28 && t < 32) ? "inline":"none"; // show swap channels dropdown
if (!(t > 28 && t < 32)) d.getElementsByName("WO"+n)[0].value = 0; // reset swapping
gId("dig"+n+"c").style.display = (t >= 40 && t < 48) ? "none":"inline"; // hide count for analog
gId("dig"+n+"r").style.display = (t >= 80 && t < 96) ? "none":"inline"; // hide reversed for virtual
gId("dig"+n+"s").style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? "none":"inline"; // hide skip 1st for virtual & analog
gId("dig"+n+"f").style.display = ((t >= 16 && t < 32) || (t >= 50 && t < 64)) ? "inline":"none"; // hide refresh
gId("dig"+n+"a").style.display = (isRGBW && t != 40) ? "inline":"none"; // auto calculate white
gId("dig"+n+"l").style.display = (t > 48 && t < 64) ? "inline":"none"; // bus clock speed
gId("rev"+n).innerHTML = (t >= 40 && t < 48) ? "Inverted output":"Reversed (rotated 180°)"; // change reverse text for analog
gId("psd"+n).innerHTML = (t >= 40 && t < 48) ? "Index:":"Start:"; // change analog start description
}
}
});
// display global white channel overrides
gId("wc").style.display = (gRGBW) ? 'inline':'none';
if (!gRGBW) {
@ -204,7 +220,7 @@
d.Sf.CR.checked = false;
}
// check for pin conflicts
var LCs = d.getElementsByTagName("input");
var LCs = d.Sf.querySelectorAll("#mLC input[name^=L]"); // input fields
var sLC = 0, sPC = 0, maxLC = 0;
for (i=0; i<LCs.length; i++) {
var nm = LCs[i].name.substring(0,2); // field name
@ -242,15 +258,14 @@
}
}
// check for pin conflicts
if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3" || nm=="L4" || nm=="RL" || nm=="BT" || nm=="IR")
if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3" || nm=="L4"/* || nm=="RL" || nm=="BT" || nm=="IR"*/)
if (LCs[i].value!="" && LCs[i].value!="-1") {
var p = []; // used pin array
for (k=0;k<d.rsvd.length;k++) p.push(d.rsvd[k]); // fill with reservations
for (k=0;k<d.um_p.length;k++) p.push(d.um_p[k]); // fill with usermod pins
var p = d.rsvd.concat(d.um_p); // used pin array
d.Sf.querySelectorAll("select.pin").forEach((e)=>{if(e.value>-1)p.push(parseInt(e.value));}) // buttons, IR & relay
for (j=0; j<LCs.length; j++) {
if (i==j) continue;
var n2 = LCs[j].name.substring(0,2);
if (n2=="L0" || n2=="L1" || n2=="L2" || n2=="L3" || n2=="L4" || n2=="RL" || n2=="BT" || n2=="IR") {
if (n2=="L0" || n2=="L1" || n2=="L2" || n2=="L3" || n2=="L4"/* || n2=="RL" || n2=="BT" || n2=="IR"*/) {
if (n2.substring(0,1)==="L") {
var m = LCs[j].name.substring(2);
var t2 = parseInt(d.getElementsByName("LT"+m)[0].value, 10);
@ -260,13 +275,13 @@
}
}
// now check for conflicts
if (p.some((e)=>e==parseInt(LCs[i].value,10))) LCs[i].style.color="red"; else LCs[i].style.color=d.ro_gpio.some((e)=>e==parseInt(LCs[i].value,10))?"orange":"#fff";
if (p.some((e)=>e==parseInt(LCs[i].value))) LCs[i].style.color="red"; else LCs[i].style.color=d.ro_gpio.some((e)=>e==parseInt(LCs[i].value))?"orange":"#fff";
}
// check buttons, IR & relay
if (nm=="IR" || nm=="BT" || nm=="RL") {
LCs[i].max = d.max_gpio;
LCs[i].min = -1;
}
//if (nm=="IR" || nm=="BT" || nm=="RL") {
// LCs[i].max = d.max_gpio;
// LCs[i].min = -1;
//}
}
// update total led count
gId("lc").textContent = sLC;
@ -330,6 +345,8 @@ ${i+1}:
<option value="31">TM1814</option>\
<option value="24">400kHz</option>\
<option value="25">TM1829</option>\
<option value="26">UCS8903</option>\
<option value="29">UCS8904 RGBW</option>\
<option value="50">WS2801</option>\
<option value="51">APA102</option>\
<option value="52">LPD8806</option>\
@ -358,15 +375,16 @@ ${i+1}:
<option value="5">GBR</option>
</select></div>
<div id="dig${i}w" style="display:none">Swap: <select name="WO${i}"><option value="0">None</option><option value="1">W & B</option><option value="2">W & G</option><option value="3">W & R</option></select></div>
<div id="dig${i}l" style="display:none">Clock: <select name="SP${i}"><option value="0">Slowest</option><option value="1">Slow</option><option value="2">Normal</option><option value="3">Fast</option><option value="4">Fastest</option></select></div>
<div>
<span id="psd${i}">Start:</span> <input type="number" name="LS${i}" id="ls${i}" class="l starts" min="0" max="8191" value="${lastEnd(i)}" oninput="startsDirty[${i}]=true;UI();" required />&nbsp;
<div id="dig${i}c" style="display:inline">Length: <input type="number" name="LC${i}" class="l" min="1" max="${maxPB}" value="1" required oninput="UI()" /></div><br>
</div>
<span id="p0d${i}">GPIO:</span> <input type="number" name="L0${i}" required class="s" onchange="UI()"/>
<span id="p1d${i}"></span><input type="number" name="L1${i}" class="s" onchange="UI()"/>
<span id="p2d${i}"></span><input type="number" name="L2${i}" class="s" onchange="UI()"/>
<span id="p3d${i}"></span><input type="number" name="L3${i}" class="s" onchange="UI()"/>
<span id="p4d${i}"></span><input type="number" name="L4${i}" class="s" onchange="UI()"/>
<span id="p0d${i}">GPIO:</span><input type="number" name="L0${i}" required class="s" onchange="UI();pinUpd(this);"/>
<span id="p1d${i}"></span><input type="number" name="L1${i}" class="s" onchange="UI();pinUpd(this);"/>
<span id="p2d${i}"></span><input type="number" name="L2${i}" class="s" onchange="UI();pinUpd(this);"/>
<span id="p3d${i}"></span><input type="number" name="L3${i}" class="s" onchange="UI();pinUpd(this);"/>
<span id="p4d${i}"></span><input type="number" name="L4${i}" class="s" onchange="UI();pinUpd(this);"/>
<div id="dig${i}r" style="display:inline"><br><span id="rev${i}">Reversed</span>: <input type="checkbox" name="CV${i}"></div>
<div id="dig${i}s" style="display:inline"><br>Skip first LEDs: <input type="number" name="SL${i}" min="0" max="255" value="0" oninput="UI()"></div>
<div id="dig${i}f" style="display:inline"><br>Off Refresh: <input id="rf${i}" type="checkbox" name="RF${i}"></div>
@ -542,17 +560,126 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
}
}
}
function pinDropdowns() {
let fields = ["IR","RL"]; // IR & relay
gId("btns").querySelectorAll('input[type="number"]').forEach((e)=>{fields.push(e.name);}) // buttons
for (let i of d.Sf.elements) {
if (i.type === "number" && fields.includes(i.name)) { //select all pin select elements
let v = parseInt(i.value);
let sel = addDropdown(i.name,0);
for (var j = -1; j <= d.max_gpio; j++) {
if (d.rsvd.includes(j)) continue;
let foundPin = d.um_p.indexOf(j);
let txt = (j === -1) ? "unused" : `${j}`;
if (foundPin >= 0 && j !== v) txt += ` used`; // already reserved pin
if (d.ro_gpio.includes(j)) txt += " (R/O)";
let opt = addOption(sel, txt, j);
if (j === v) opt.selected = true; // this is "our" pin
else if (d.um_p.includes(j)) opt.disabled = true; // someone else's pin
}
}
}
// update select options
d.Sf.querySelectorAll("select.pin").forEach((e)=>{pinUpd(e);});
// add dataset values for LED GPIO pins
d.Sf.querySelectorAll(".iST input.s[name^=L]").forEach((i)=>{
if (i.value!=="" && i.value>=0)
i.dataset.val = i.value;
});
}
function pinUpd(e) {
// update changed select options across all usermods
let oldV = parseInt(e.dataset.val);
e.dataset.val = e.value;
let txt = e.name;
let pins = [];
d.Sf.querySelectorAll(".iST input.s[name^=L]").forEach((i)=>{
if (i.value!=="" && i.value>=0 && i.max<255)
pins.push(i.value);
});
let selects = d.Sf.querySelectorAll("select.pin");
for (let sel of selects) {
if (sel == e) continue
Array.from(sel.options).forEach((i)=>{
let led = pins.includes(i.value);
if (!(i.value==oldV || i.value==e.value || led)) return;
if (i.value == -1) {
i.text = "unused";
return
}
i.text = i.value;
if (i.value==oldV) {
i.disabled = false;
}
if (i.value==e.value || led) {
i.disabled = true;
i.text += ` ${led?'LED':txt}`;
}
if (d.ro_gpio.includes(parseInt(i.value))) i.text += " (R/O)";
});
}
}
// https://stackoverflow.com/questions/39729741/javascript-change-input-text-to-select-option
function addDropdown(field) {
let sel = d.createElement('select');
sel.classList.add("pin");
let inp = d.getElementsByName(field)[0];
if (inp && inp.tagName === "INPUT" && (inp.type === "text" || inp.type === "number")) { // may also use nodeName
let v = inp.value;
let n = inp.name;
// copy the existing input element's attributes to the new select element
for (var i = 0; i < inp.attributes.length; ++ i) {
var att = inp.attributes[i];
// type and value don't apply, so skip them
// ** you might also want to skip style, or others -- modify as needed **
if (att.name != 'type' && att.name != 'value' && att.name != 'class' && att.name != 'style') {
sel.setAttribute(att.name, att.value);
}
}
sel.setAttribute("data-val", v);
sel.setAttribute("onchange", "pinUpd(this)");
// finally, replace the old input element with the new select element
inp.parentElement.replaceChild(sel, inp);
return sel;
}
return null;
}
function addOption(sel,txt,val) {
if (sel===null) return; // select object missing
let opt = d.createElement("option");
opt.value = val;
opt.text = txt;
sel.appendChild(opt);
for (let i=0; i<sel.childNodes.length; i++) {
let c = sel.childNodes[i];
if (c.value == sel.dataset.val) sel.selectedIndex = i;
}
return opt;
}
function S() {
if (window.location.protocol == "file:") {
let l = window.location;
if (l.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy
let path = l.pathname;
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
if (paths.length > 2) {
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths[0];
}
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=2';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
}
loadJS(getURL('/settings/s.js?p=2'), false); // If we set async false, file is loaded and executed, then next statement is processed
if (loc) d.Sf.action = getURL('/settings/leds');
}
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
</script>
<style>@import url("style.css");</style>
@ -572,7 +699,7 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
Enable automatic brightness limiter: <input type="checkbox" name="ABen" onchange="enABL()" id="able"><br>
<div id="abl">
Maximum Current: <input name="MA" type="number" class="l" min="250" max="65000" oninput="UI()" required> mA<br>
<div id="ampwarning" style="color: orange; display: none;">
<div id="ampwarning" class="warn" style="display: none;">
&#9888; Your power supply provides high current.<br>
To improve the safety of your setup,<br>
please use thick cables,<br>
@ -600,14 +727,14 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
<button type="button" id="-" onclick="addLEDs(-1,false)">-</button><br>
LED Memory Usage: <span id="m0">0</span> / <span id="m1">?</span> B<br>
<div id="dbar" style="display:inline-block; width: 100px; height: 10px; border-radius: 20px;"></div><br>
<div id="ledwarning" style="color: orange; display: none;">
<div id="ledwarning" class="warn" style="display: none;">
&#9888; You might run into stability or lag issues.<br>
Use less than <span id="wreason">800 LEDs per output</span> for the best experience!<br>
</div>
<hr class="sml">
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>
Use global LED buffer: <input type="checkbox" name="LD"><br>
Use global LED buffer: <input type="checkbox" name="LD" onchange="UI()"><br>
<hr class="sml">
<div id="color_order_mapping">
Color Order Override:

View File

@ -7,7 +7,7 @@
<title>PIN required</title>
<script>
var d = document;
function B() { window.open("/settings","_self"); }
function B() { window.open("../settings","_self"); }
</script>
<style>
@import url("style.css");

View File

@ -7,10 +7,10 @@
<title>Misc Settings</title>
<script>
var d = document;
var loc = false, locip;
var loc = false, locip, locproto = "http:";
function H() { window.open("https://kno.wled.ge/features/settings/#security-settings"); }
function B() { window.open("/settings","_self"); }
function U() { window.open("/update","_self"); }
function B() { window.open(getURL("/settings"),"_self"); }
function U() { window.open(getURL("/update"),"_self"); }
function gId(s) { return d.getElementById(s); }
function isObj(o) { return (o && typeof o === 'object' && !Array.isArray(o)); }
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
@ -47,7 +47,7 @@
var req = new XMLHttpRequest();
req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400)});
req.addEventListener('error', function(e){showToast(e.stack,true);});
req.open("POST", "/upload");
req.open("POST", getURL("/upload"));
var formData = new FormData();
formData.append("data", fO.files[0], name);
req.send(formData);
@ -65,16 +65,33 @@
x.setAttribute("download","wled_" + x.getAttribute("download") + (sd=="WLED"?"":("_" +sd)));
}
function S() {
if (window.location.protocol == "file:") {
let l = window.location;
if (l.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy
let path = l.pathname;
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
if (paths.length > 2) {
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths[0];
}
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=6';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
}
if (loc) {
gId("bckcfg").setAttribute('href',getURL(gId("bckcfg").pathname));
gId("bckpresets").setAttribute('href',getURL(gId("bckpresets").pathname));
}
loadJS(getURL('/settings/s.js?p=6'), false); // If we set async false, file is loaded and executed, then next statement is processed
if (loc) d.Sf.action = getURL('/settings/sec');
}
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
</script>
<style>
@ -89,7 +106,7 @@
</div>
<h2>Security & Update setup</h2>
Settings PIN: <input type="password" id="PIN" name="PIN" size="4" maxlength="4" minlength="4" onkeydown="checkNum(this)" pattern="[0-9]*" inputmode="numeric" title="Please enter a 4 digit number"><br>
<div style="color: #fa0;">&#9888; Unencrypted transmission. Be prudent when selecting PIN, do NOT use your banking, door, SIM, etc. pin!</div><br>
<div class="warn">&#9888; Unencrypted transmission. Be prudent when selecting PIN, do NOT use your banking, door, SIM, etc. pin!</div><br>
Lock wireless (OTA) software update: <input type="checkbox" name="NO"><br>
Passphrase: <input type="password" name="OP" maxlength="32"><br>
To enable OTA, for security reasons you need to also enter the correct password!<br>
@ -99,7 +116,7 @@
Deny access to WiFi settings if locked: <input type="checkbox" name="OW"><br><br>
Factory reset: <input type="checkbox" name="RS"><br>
All settings and presets will be erased.<br><br>
<div style="color: #fa0;">&#9888; Unencrypted transmission. An attacker on the same network can intercept form data!</div>
<div class="warn">&#9888; Unencrypted transmission. An attacker on the same network can intercept form data!</div>
<hr>
<h3>Software Update</h3>
<button type="button" onclick="U()">Manual OTA Update</button><br>
@ -110,7 +127,7 @@
<div>Restore presets<br><input type="file" name="data" accept=".json"> <button type="button" onclick="uploadFile(d.Sf.data,'/presets.json');">Upload</button><br></div><br>
<a class="btn lnk" id="bckpresets" href="/cfg.json" download="cfg">Backup configuration</a><br>
<div>Restore configuration<br><input type="file" name="data2" accept=".json"> <button type="button" onclick="uploadFile(d.Sf.data2,'/cfg.json');">Upload</button><br></div>
<div style="color: #fa0;">&#9888; Restoring presets/configuration will OVERWRITE your current presets/configuration.<br>
<div class="warn">&#9888; Restoring presets/configuration will OVERWRITE your current presets/configuration.<br>
Incorrect configuration may require a factory reset or re-flashing of your ESP.</div>
For security reasons, passwords are not backed up.
<hr>

View File

@ -6,11 +6,11 @@
<meta charset="utf-8">
<title>Sync Settings</title>
<script>var d=document;
var loc = false, locip;
var loc = false, locip, locproto = "http:";
function gId(s){return d.getElementById(s);}
function toggle(el){gId(el).classList.toggle("hide"); gId('No'+el).classList.toggle("hide");}
function H(){window.open("https://kno.wled.ge/interfaces/udp-notifier/");}
function B(){window.open("/settings","_self");}
function B(){window.open(getURL("/settings"),"_self");}
function adj(){if (d.Sf.DI.value == 6454) {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;} }
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
@ -56,16 +56,28 @@
function SP(){var p = d.Sf.DI.value; gId("xp").style.display = (p > 0)?"none":"block"; if (p > 0) d.Sf.EP.value = p;}
function SetVal(){switch(parseInt(d.Sf.EP.value)){case 5568: d.Sf.DI.value = 5568; break; case 6454: d.Sf.DI.value = 6454; break; case 4048: d.Sf.DI.value = 4048; break; }; SP();FC();}
function S(){
if (window.location.protocol == "file:") {
let l = window.location;
if (l.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy
let paths = l.pathname.slice(1,l.pathname.endsWith('/')?-1:undefined).split("/");
if (paths.length > 2) {
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths[0];
}
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=4';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
}
loadJS(getURL('/settings/s.js?p=4'), false); // If we set async false, file is loaded and executed, then next statement is processed
if (loc) d.Sf.action = getURL('/settings/sync');
}
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
</script>
<style>@import url("style.css");</style>
@ -172,7 +184,7 @@ Realtime LED offset: <input name="WO" type="number" min="-255" max="255" require
<hr class="sml">
<h3>Alexa Voice Assistant</h3>
<div id="NoAlexa" class="hide">
<em style="color:#fa0;">This firmware build does not include Alexa support.<br></em><br>
<i class="warn">This firmware build does not include Alexa support.<br></i><br>
</div>
<div id="Alexa">
Emulate Alexa device: <input type="checkbox" name="AL"><br>
@ -180,7 +192,7 @@ Alexa invocation name: <input type="text" name="AI" maxlength="32"><br>
Also emulate devices to call the first <input name="AP" type="number" class="s" min="0" max="9" required> presets<br><br>
</div>
<hr class="sml">
<div style="color: #fa0;">&#9888; <b>MQTT and Hue sync all connect to external hosts!<br>
<div class="warn">&#9888; <b>MQTT and Hue sync all connect to external hosts!<br>
This may impact the responsiveness of WLED.</b><br>
</div>
For best results, only use one of these services at a time.<br>
@ -188,7 +200,7 @@ For best results, only use one of these services at a time.<br>
<hr class="sml">
<h3>MQTT</h3>
<div id="NoMQTT" class="hide">
<em style="color:#fa0;">This firmware build does not include MQTT support.<br></em>
<i class="warn">This firmware build does not include MQTT support.<br></i>
</div>
<div id="MQTT">
Enable MQTT: <input type="checkbox" name="MQ"><br>
@ -202,11 +214,12 @@ 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>
Retain brightness & color messages: <input type="checkbox" name="RT"><br>
<i>Reboot required to apply changes. </i><a href="https://kno.wled.ge/interfaces/mqtt/" target="_blank">MQTT info</a>
</div>
<h3>Philips Hue</h3>
<div id="NoHue" class="hide">
<em style="color:#fa0;">This firmware build does not include Philips Hue support.<br></em>
<em class="warn">This firmware build does not include Philips Hue support.<br></em>
</div>
<div id="Hue">
<i>You can find the bridge IP and the light number in the 'About' section of the hue app.</i><br>

View File

@ -7,11 +7,11 @@
<title>Time Settings</title>
<script>
var d=document;
var loc = false, locip;
var loc = false, locip, locproto = "http:";
var el=false;
var ms=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
function H() { window.open("https://kno.wled.ge/features/settings/#time-settings"); }
function B() { window.open("/settings","_self"); }
function B() { window.open(getURL("/settings"),"_self"); }
function gId(s) { return d.getElementById(s); }
function gN(s) { return d.getElementsByName(s)[0]; }
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
@ -33,16 +33,29 @@
});
}
function S() {
if (window.location.protocol == "file:") {
let l = window.location;
if (l.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy
let path = l.pathname;
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
if (paths.length > 2) {
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths[0];
}
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=5';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
}
loadJS(getURL('/settings/s.js?p=5'), false); // If we set async false, file is loaded and executed, then next statement is processed
if (loc) d.Sf.action = getURL('/settings/time');
}
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
function expand(o,i)
{

View File

@ -7,7 +7,7 @@
<title>UI Settings</title>
<script>
var d = document;
var loc = false, locip;
var loc = false, locip, locproto = "http:";
var initial_ds, initial_st, initial_su;
var sett = null;
var l = {
@ -26,7 +26,8 @@
"segpwr": "Hide segment power &amp; brightness",
"segexp" : "Always expand first segment",
"css": "Enable custom CSS",
"hdays": "Enable custom Holidays list"
"hdays": "Enable custom Holidays list",
"fxdef": "Use effect default parameters"
},
"theme":{
"alpha": {
@ -185,21 +186,33 @@
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
function S()
{
if (window.location.protocol == "file:") {
function S() {
let l = window.location;
if (l.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy
let path = l.pathname;
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
if (paths.length > 2) {
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths[0];
}
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=3';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
}
loadJS(getURL('/settings/s.js?p=3'), false); // If we set async false, file is loaded and executed, then next statement is processed
if (loc) d.Sf.action = getURL('/settings/ui');
}
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
function H() { window.open("https://kno.wled.ge/features/settings/#user-interface-settings"); }
function B() { window.open("/settings","_self"); }
function B() { window.open(getURL("/settings"),"_self"); }
function UI()
{
gId('idonthateyou').style.display = (gId('dm').checked) ? 'inline':'none';
@ -249,7 +262,7 @@
Server description: <input type="text" name="DS" maxlength="32"><br>
Sync button toggles both send and receive: <input type="checkbox" name="ST"><br>
<div id="NoSimple" class="hide">
<em style="color:#fa0;">This firmware build does not include simplified UI support.<br></em>
<i class="warn">This firmware build does not include simplified UI support.<br></i>
</div>
<div id="Simple">Enable simplified UI: <input type="checkbox" name="SU"><br></div>
<i>The following UI customization settings are unique both to the WLED device and this browser.<br>

View File

@ -7,19 +7,20 @@
<title>Usermod Settings</title>
<script>
var d = document;
d.max_gpio = 39;
d.max_gpio = 50;
d.um_p = [];
d.rsvd = [];
d.ro_gpio = [];
d.extra = [];
var umCfg = {};
var pins = [], pinO = [], owner;
var loc = false, locip;
var loc = false, locip, locproto = "http:";
var urows;
var numM = 0;
function gId(s) { return d.getElementById(s); }
function isO(i) { return (i && typeof i === 'object' && !Array.isArray(i)); }
function H() { window.open("https://github.com/Aircoookie/WLED/wiki/Settings#usermod-settings"); }
function B() { window.open("/settings","_self"); }
function B() { window.open(getURL("/settings"),"_self"); }
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script");
@ -30,15 +31,11 @@
// success event
scE.addEventListener("load", () => {
GetV();
for (let k=0; k<d.rsvd.length; k++) { pins.push(d.rsvd[k]); pinO.push("rsvd"); }
if (d.um_p[0]==-1) d.um_p.shift();
d.Sf.SDA.max = d.max_gpio;
d.Sf.SCL.max = d.max_gpio;
d.Sf.MOSI.max = d.max_gpio;
d.Sf.SCLK.max = d.max_gpio;
d.Sf.MISO.max = d.max_gpio;
let inp = d.getElementsByTagName("input");
for (let i of inp) if (i.type === "number" && i.name.replace("[]","").substr(-3) === "pin") i.max = d.max_gpio;
for (let r of d.rsvd) { pins.push(r); pinO.push("rsvd"); } // reserved pins
if (d.um_p[0]==-1) d.um_p.shift(); // remove filler
d.Sf.SDA.max = d.Sf.SCL.max = d.Sf.MOSI.max = d.Sf.SCLK.max = d.Sf.MISO.max = d.max_gpio;
//for (let i of d.getElementsByTagName("input")) if (i.type === "number" && i.name.replace("[]","").substr(-3) === "pin") i.max = d.max_gpio;
pinDropdowns(); // convert INPUT to SELECT for pins
});
// error event
scE.addEventListener("error", (ev) => {
@ -47,21 +44,36 @@
});
}
function S() {
if (window.location.protocol == "file:") {
let l = window.location;
if (l.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy
let path = l.pathname;
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
if (paths.length > 2) {
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths[0];
}
}
ldS();
if (!numM) gId("um").innerHTML = "No Usermods installed.";
if (loc) d.Sf.action = getURL('/settings/um');
}
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
// https://stackoverflow.com/questions/3885817/how-do-i-check-that-a-number-is-float-or-integer
function isF(n) { return n === +n && n !== (n|0); }
function isI(n) { return n === +n && n === (n|0); }
function check(o,k) { // input object, pin owner key
/* no longer necessary with pin dropdown fields
var n = o.name.replace("[]","").substr(-3);
if (o.type=="number" && n.substr(0,3)=="pin") {
for (var i=0; i<pins.length; i++) {
@ -84,13 +96,16 @@
if (o.value==pins[i] || o.value<-1 || o.value>d.max_gpio) { o.style.color="red"; break; } else o.style.color=d.ro_gpio.some((e)=>e==parseInt(o.value,10))?"orange":"#fff";
}
}
*/
}
function getPins(o) {
if (isO(o)) {
for (const [k,v] of Object.entries(o)) {
if (isO(v)) {
let oldO = owner; // keep parent name
owner = k;
getPins(v);
owner = oldO;
continue;
}
if (k.replace("[]","").substr(-3)=="pin") {
@ -149,11 +164,76 @@
urows += `<input type="${t==="int"?"number":t}" name="${k}:${f}${a?"[]":""}" ${c} oninput="check(this,'${k.substr(k.indexOf(":")+1)}')"><br>`;
}
}
function pinDropdowns() {
for (let i of d.Sf.elements) {
if (i.type === "number" && (i.name.includes("pin") || ["SDA","SCL","MOSI","MISO","SCLK"].includes(i.name))) { //select all pin select elements
let v = parseInt(i.value);
let sel = addDropdown(i.name,0);
for (var j = -1; j <= d.max_gpio; j++) {
if (d.rsvd.includes(j)) continue;
let foundPin = pins.indexOf(j);
let txt = (j === -1) ? "unused" : `${j}`;
if (foundPin >= 0 && j !== v) txt += ` ${pinO[foundPin]=="if"?"global":pinO[foundPin]}`; // already reserved pin
if (d.ro_gpio.includes(j)) txt += " (R/O)";
let opt = addOption(sel, txt, j);
if (j === v) opt.selected = true; // this is "our" pin
else if (pins.includes(j)) opt.disabled = true; // someone else's pin
}
let um = i.name.split(":")[0];
d.extra.forEach((o)=>{
if (o[um] && o[um].pin) o[um].pin.forEach((e)=>{
let opt = addOption(sel,e[0],e[1]);
if (e[1]==v) opt.selected = true;
});
});
}
}
}
function UI(e) {
// update changed select options across all usermods
let oldV = parseInt(e.dataset.val);
e.dataset.val = e.value;
let txt = e.name.split(":")[e.name.split(":").length-2];
let selects = d.Sf.querySelectorAll("select[class='pin']");
for (let sel of selects) {
if (sel == e) continue
Array.from(sel.options).forEach((i)=>{
if (!(i.value==oldV || i.value==e.value)) return;
if (i.value == -1) {
i.text = "unused";
return
}
if (i.value<100) { // TODO remove this hack and use d.extra
i.text = i.value;
if (i.value==oldV) {
i.disabled = false;
}
if (i.value==e.value) {
i.disabled = true;
i.text += ` ${txt}`;
}
if (d.ro_gpio.includes(parseInt(i.value))) i.text += " (R/O)";
}
});
}
}
// https://stackoverflow.com/questions/39729741/javascript-change-input-text-to-select-option
function addDropdown(um,fld) {
let sel = d.createElement('select');
let arr = d.getElementsByName(um+":"+fld);
let inp = arr[1]; // assume 1st field to be hidden (type)
if (typeof(fld) === "string") { // parameter from usermod (field name)
if (fld.includes("pin")) sel.classList.add("pin");
um += ":"+fld;
} else if (typeof(fld) === "number") sel.classList.add("pin"); // a hack to add a class
let arr = d.getElementsByName(um);
let idx = arr[0].type==="hidden"?1:0; // ignore hidden field
if (arr.length > 2) {
// we have array of values (usually pins)
for (let i of arr) {
if (i.type === "number") break;
idx++;
}
}
let inp = arr[idx];
if (inp && inp.tagName === "INPUT" && (inp.type === "text" || inp.type === "number")) { // may also use nodeName
let v = inp.value;
let n = inp.name;
@ -162,11 +242,12 @@
var att = inp.attributes[i];
// type and value don't apply, so skip them
// ** you might also want to skip style, or others -- modify as needed **
if (att.name != 'type' && att.name != 'value' && att.name != 'class' && att.name != 'style') {
if (att.name != 'type' && att.name != 'value' && att.name != 'class' && att.name != 'style' && att.name != 'oninput' && att.name != 'max' && att.name != 'min') {
sel.setAttribute(att.name, att.value);
}
}
sel.setAttribute("data-val", v);
sel.setAttribute("onchange", "UI(this)");
// finally, replace the old input element with the new select element
inp.parentElement.replaceChild(sel, inp);
return sel;
@ -183,6 +264,7 @@
let c = sel.childNodes[i];
if (c.value == sel.dataset.val) sel.selectedIndex = i;
}
return opt;
}
// https://stackoverflow.com/questions/26440494/insert-text-after-this-input-element-with-javascript
function addInfo(name,el,txt, txt2="") {
@ -194,10 +276,13 @@
if (txt2!="") obj[el].insertAdjacentHTML('beforebegin', txt2 + '&nbsp;'); //add pre texts
}
}
// add Help Button
function addHB(um) {
addInfo(um + ':help',0,`<button onclick="location.href='https://kno.wled.ge/usermods/${um}'" type="button">?</button>`);
}
// load settings and insert values into DOM
function ldS() {
var url = (loc?`http://${locip}`:'') + '/cfg.json';
fetch(url, {
fetch(getURL('/cfg.json'), {
method: 'get'
})
.then(res => {
@ -216,8 +301,7 @@
}
if (urows==="") urows = "Usermods configuration not found.<br>Press <i>Save</i> to initialize defaults.";
gId("um").innerHTML = urows;
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=8';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
loadJS(getURL('/settings/s.js?p=8'), false); // If we set async false, file is loaded and executed, then next statement is processed
})
.catch((error)=>{
gId('lserr').style.display = "inline";
@ -242,15 +326,17 @@
</div>
<h2>Usermod Setup</h2>
Global I<sup>2</sup>C GPIOs (HW)<br>
<i style="color: orange;">(only changable on ESP32, change requires reboot!)</i><br>
<i class="warn">(change requires reboot!)</i><br>
SDA:<input type="number" min="-1" max="48" name="SDA" onchange="check(this,'if')" class="s" placeholder="SDA">
SCL:<input type="number" min="-1" max="48" name="SCL" onchange="check(this,'if')" class="s" placeholder="SCL">
<hr class="sml">
Global SPI GPIOs (HW)<br>
<i style="color: orange;">(only changable on ESP32, change requires reboot!)</i><br>
<i class="warn">(only changable on ESP32, change requires reboot!)</i><br>
MOSI:<input type="number" min="-1" max="48" name="MOSI" onchange="check(this,'if')" class="s" placeholder="MOSI">
MISO:<input type="number" min="-1" max="48" name="MISO" onchange="check(this,'if')" class="s" placeholder="MISO">
SCLK:<input type="number" min="-1" max="48" name="SCLK" onchange="check(this,'if')" class="s" placeholder="SCLK">
<hr class="sml">
Reboot after save? <input type="checkbox" name="RBT"><br>
<div id="um">Loading settings...</div>
<hr><button type="button" onclick="B()">Back</button><button type="submit">Save</button>
</form>

View File

@ -7,21 +7,19 @@
<title>WiFi Settings</title>
<script>
var d = document;
var loc = false, locip;
var loc = false, locip, locproto = "http:";
var scanLoops = 0, preScanSSID = "";
function gId(e) { return d.getElementById(e); }
function cE(e) { return d.createElement(e); }
function H(){window.open("https://kno.wled.ge/features/settings/#wifi-settings");}
function B(){window.open("/settings","_self");}
function B(){window.open(getURL("/settings"),"_self");}
function N() {
const url = (loc?`http://${locip}`:"") + "/json/net";
const button = gId("scan");
button.disabled = true;
button.innerHTML = "Scanning...";
button.textContent = "Scanning...";
fetch(url).then((response) => {
fetch(getURL("/json/net")).then((response) => {
return response.json();
}).then((json) => {
// Get the list of networks only, defaulting to an empty array.
@ -70,7 +68,7 @@
const option = cE("option");
option.setAttribute("value", networks[i].ssid);
option.innerHTML = `${networks[i].ssid} (${networks[i].rssi} dBm)`;
option.textContent = `${networks[i].ssid} (${networks[i].rssi} dBm)`;
if (networks[i].ssid === cs.value) {
option.setAttribute("selected", "selected");
@ -81,14 +79,14 @@
const option = cE("option");
option.setAttribute("value", "!Cs");
option.innerHTML = `Other network...`;
option.textContent = `Other network...`;
select.appendChild(option);
cs.replaceWith(select);
}
button.disabled = false;
button.innerHTML = "Scan";
button.textContent = "Scan";
});
}
// replace WiFi select with custom SSID input field again
@ -122,16 +120,29 @@
});
}
function S() {
if (window.location.protocol == "file:") {
let l = window.location;
if (l.protocol == "file:") {
loc = true;
locip = localStorage.getItem("locIp");
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem("locIp", locip);
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy
let path = l.pathname;
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
if (paths.length > 2) {
locproto = l.protocol;
loc = true;
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths[0];
}
}
let url = (loc?`http://${locip}`:'') + '/settings/s.js?p=1';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
loadJS(getURL('/settings/s.js?p=1'), false); // If we set async false, file is loaded and executed, then next statement is processed
if (loc) d.Sf.action = getURL('/settings/wifi');
}
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
</script>
<style>@import url("style.css");</style>
@ -182,15 +193,28 @@
Disable WiFi sleep: <input type="checkbox" name="WS"><br>
<i>Can help with connectivity issues.<br>
Do not enable if WiFi is working correctly, increases power consumption.</i>
<div id="remd">
<h3>Wireless Remote</h3>
<i>Listen for events over ESP-NOW<br>
Keep disabled if not using a remote, increases power consumption.<br></i>
Enable Remote: <input type="checkbox" name="RE"><br>
Hardware MAC: <input type="text" name="RMAC"><br>
Last Seen: <span class="rlid">None</span> <br>
</div>
<div id="ethd">
<h3>Ethernet Type</h3>
<select name="ETH">
<option value="0">None</option>
<option value="9">ABC! WLED V43 & compatible</option>
<option value="2">ESP32-POE</option>
<option value="6">ESP32Deux</option>
<option value="7">KIT-VE</option>
<option value="8">QuinLED-Dig-Octa</option>
<option value="8">QuinLED-Dig-Octa & T-ETH-POE</option>
<option value="4">QuinLED-ESP32</option>
<option value="10">Serg74-ETH32</option>
<option value="5">TwilightLord-ESP32</option>
<option value="3">WESP32</option>
<option value="1">WT32-ETH01</option>

View File

@ -1,5 +1,5 @@
//page js
var loc = false, locip;
var loc = false, locip, locproto = "http:";
var noNewSegs = false;
var isOn = false, isInfo = false, isNodes = false, isRgbw = false, cct = false;
var whites = [0,0,0];
@ -148,22 +148,39 @@ function loadSkinCSS(cId)
l.id = cId;
l.rel = 'stylesheet';
l.type = 'text/css';
l.href = (loc?`http://${locip}`:'.') + '/skin.css';
l.href = getURL('/skin.css');
l.media = 'all';
h.appendChild(l);
}
}
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
async function onLoad()
{
if (window.location.protocol == "file:") {
let l = window.location;
if (l.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip)
{
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
} else {
// detect reverse proxy and/or HTTPS
let pathn = l.pathname;
let paths = pathn.slice(1,pathn.endsWith('/')?-1:undefined).split("/");
if (paths[0]==="sliders") paths.shift();
//while (paths[0]==="") paths.shift();
locproto = l.protocol;
locip = l.hostname + (l.port ? ":" + l.port : "");
if (paths.length > 0 && paths[0]!=="") {
loc = true;
locip += "/" + paths[0];
} else if (locproto==="https:") {
loc = true;
}
}
var sett = localStorage.getItem('wledUiCfg');
if (sett) cfg = mergeDeep(cfg, JSON.parse(sett));
@ -173,7 +190,7 @@ async function onLoad()
applyCfg();
if (cfg.theme.bg.url=="" || cfg.theme.bg.url === "https://picsum.photos/1920/1080") {
var iUrl = cfg.theme.bg.url;
fetch((loc?`http://${locip}`:'.') + "/holidays.json", {
fetch(getURL("/holidays.json"), {
method: 'get'
})
.then((res)=>{
@ -330,9 +347,7 @@ function loadPresets(callback = null)
pmtLast = pmt;
var url = (loc?`http://${locip}`:'') + '/presets.json';
fetch(url, {
fetch(getURL('/presets.json'), {
method: 'get'
})
.then(res => {
@ -355,9 +370,7 @@ function loadPresets(callback = null)
function loadPalettes(callback = null)
{
var url = (loc?`http://${locip}`:'') + '/json/palettes';
fetch(url, {
fetch(getURL('/json/palettes'), {
method: 'get'
})
.then(res => {
@ -379,9 +392,7 @@ function loadPalettes(callback = null)
function loadFX(callback = null)
{
var url = (loc?`http://${locip}`:'') + '/json/effects';
fetch(url, {
fetch(getURL('/json/effects'), {
method: 'get'
})
.then(res => {
@ -403,9 +414,7 @@ function loadFX(callback = null)
function loadFXData(callback = null)
{
var url = (loc?`http://${locip}`:'') + '/json/fxdata';
fetch(url, {
fetch(getURL('/json/fxdata'), {
method: 'get'
})
.then(res => {
@ -611,8 +620,7 @@ function populateNodes(i,n)
function loadNodes()
{
var url = (loc?`http://${locip}`:'') + '/json/nodes';
fetch(url, {
fetch(getURL('/json/nodes'), {
method: 'get'
})
.then(res => {
@ -855,7 +863,8 @@ function cmpP(a, b)
function makeWS() {
if (ws) return;
ws = new WebSocket('ws://'+(loc?locip:window.location.hostname)+'/ws');
let url = loc ? getURL('/ws').replace("http","ws") : "ws://"+window.location.hostname+"/ws";
ws = new WebSocket(url);
ws.onmessage = (e)=>{
var json = JSON.parse(e.data);
if (json.leds) return; //liveview packet
@ -974,7 +983,6 @@ function requestJson(command=null)
if (command && !reqsLegal) return; //stop post requests from chrome onchange event on page restore
if (!jsonTimeout) jsonTimeout = setTimeout(showErrorToast, 3000);
var req = null;
var url = (loc?`http://${locip}`:'') + '/json/si';
var useWs = (ws && ws.readyState === WebSocket.OPEN);
var type = command ? 'post':'get';
if (command) {
@ -991,7 +999,7 @@ function requestJson(command=null)
setTimeout(requestJson,200);
}
fetch(url, {
fetch(getURL('/json/si'), {
method: type,
headers: {
"Content-type": "application/json; charset=UTF-8"
@ -1314,9 +1322,7 @@ function loadPalettesData(callback = null)
function getPalettesData(page, callback)
{
var url = (loc?`http://${locip}`:'') + `/json/palx?page=${page}`;
fetch(url, {
fetch(getURL(`/json/palx?page=${page}`), {
method: 'get',
headers: {
"Content-type": "application/json; charset=UTF-8"

View File

@ -61,6 +61,12 @@ button.sml {
.hide {
display: none;
}
.err {
color: #f00;
}
.warn {
color: #fa0;
}
input {
background: #333;
color: #fff;
@ -114,6 +120,10 @@ select {
font-family: Verdana, sans-serif;
border: 0.5ch solid #333;
}
select.pin {
max-width: 120px;
text-overflow: ellipsis;
}
tr {
line-height: 100%;
}

View File

@ -15,7 +15,7 @@
<body onload="GetV()">
<h2>WLED Software Update</h2>
<form method='POST' action='/update' id='uf' enctype='multipart/form-data' onsubmit="U()">
<form method='POST' action='./update' id='uf' enctype='multipart/form-data' onsubmit="U()">
Installed version: <span class="sip">##VERSION##</span><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>

View File

@ -55,9 +55,9 @@
<h3>Thank you for installing my application!</h3>
<b>Next steps:</b><br><br>
Connect the module to your local WiFi here!<br>
<button onclick="window.location.href='/settings/wifi'">WiFi settings</button><br>
<button onclick="window.location.href='./settings/wifi'">WiFi settings</button><br>
<i>Just trying this out in AP mode?</i><br>
<button onclick="window.location.href='/sliders'">To the controls!</button><br>
<button onclick="window.location.href='./?sliders'">To the controls!</button><br>
</div>
</body>
</html>

View File

@ -25,7 +25,7 @@ void handleDDPPacket(e131_packet_t* p) {
}
}
uint8_t ddpChannelsPerLed = (p->dataType == DDP_TYPE_RGBW32) ? 4 : 3; // data type 0x1A is RGBW (type 3, 8 bit/channel)
uint8_t ddpChannelsPerLed = ((p->dataType & 0b00111000)>>3 == 0b011) ? 4 : 3; // data type 0x1B (formerly 0x1A) is RGBW (type 3, 8 bit/channel)
uint32_t start = htonl(p->channelOffset) / ddpChannelsPerLed;
start += DMXAddress / ddpChannelsPerLed;
@ -173,14 +173,30 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
break;
case DMX_MODE_PRESET: // 2 channel: [Dimmer,Preset]
{
if (uni != e131Universe || availDMXLen < 2) return;
applyPreset(e131_data[dataOffset+1], CALL_MODE_NOTIFICATION);
// limit max. selectable preset to 250, even though DMX max. val is 255
uint8_t dmxValPreset = (e131_data[dataOffset+1] > 250 ? 250 : e131_data[dataOffset+1]);
// only apply preset if value changed
if (dmxValPreset != 0 && dmxValPreset != currentPreset &&
// only apply preset if not in playlist, or playlist changed
(currentPlaylist < 0 || dmxValPreset != currentPlaylist)) {
presetCycCurr = dmxValPreset;
unloadPlaylist(); // applying a preset unloads the playlist
applyPreset(dmxValPreset, CALL_MODE_NOTIFICATION);
}
// only change brightness if value changed
if (bri != e131_data[dataOffset]) {
bri = e131_data[dataOffset];
strip.setBrightness(bri, true);
strip.setBrightness(scaledBri(bri), false);
stateUpdated(CALL_MODE_WS_SEND);
}
return;
break;
}
case DMX_MODE_EFFECT: // 15 channels [bri,effectCurrent,effectSpeed,effectIntensity,effectPalette,effectOption,R,G,B,R2,G2,B2,R3,G3,B3]
case DMX_MODE_EFFECT_W: // 18 channels, same as above but with extra +3 white channels [..,W,W2,W3]

View File

@ -50,6 +50,18 @@ bool getJsonValue(const JsonVariant& element, DestType& destination, const Defau
//colors.cpp
// similar to NeoPixelBus NeoGammaTableMethod but allows dynamic changes (superseded by NPB::NeoGammaDynamicTableMethod)
class NeoGammaWLEDMethod {
public:
static uint8_t Correct(uint8_t value); // apply Gamma to single channel
static uint32_t Correct32(uint32_t color); // apply Gamma to RGBW32 color (WLED specific, not used by NPB)
static void calcGammaTable(float gamma); // re-calculates & fills gamma table
static inline uint8_t rawGamma8(uint8_t val) { return gammaT[val]; } // get value from Gamma table (WLED specific, not used by NPB)
private:
static uint8_t gammaT[];
};
#define gamma32(c) NeoGammaWLEDMethod::Correct32(c)
#define gamma8(c) NeoGammaWLEDMethod::rawGamma8(c)
uint32_t color_blend(uint32_t,uint32_t,uint16_t,bool b16=false);
uint32_t color_add(uint32_t,uint32_t);
inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); }
@ -63,10 +75,6 @@ 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);
uint8_t gamma8_cal(uint8_t b, float gamma);
void calcGammaTable(float gamma);
uint8_t gamma8(uint8_t b);
uint32_t gamma32(uint32_t);
//dmx.cpp
void initDMX();
@ -96,10 +104,20 @@ void sendHuePoll();
void onHueData(void* arg, AsyncClient* client, void *data, size_t len);
//improv.cpp
enum ImprovRPCType {
Command_Wifi = 0x01,
Request_State = 0x02,
Request_Info = 0x03,
Request_Scan = 0x04
};
void handleImprovPacket();
void sendImprovRPCResult(ImprovRPCType type, uint8_t n_strings = 0, const char **strings = nullptr);
void sendImprovStateResponse(uint8_t state, bool error = false);
void sendImprovInfoResponse();
void sendImprovRPCResponse(uint8_t commandId);
void startImprovWifiScan();
void handleImprovWifiScan();
void sendImprovIPRPCResult(ImprovRPCType type);
//ir.cpp
void applyRepeatActions();
@ -129,8 +147,8 @@ bool deserializeState(JsonObject root, byte callMode = CALL_MODE_DIRECT_CHANGE,
void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true);
void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true, bool selectedSegmentsOnly = false);
void serializeInfo(JsonObject root);
void serializeModeNames(JsonArray arr, const char *qstring);
void serializeModeData(JsonObject root);
void serializeModeNames(JsonArray root);
void serializeModeData(JsonArray root);
void serveJson(AsyncWebServerRequest* request);
#ifdef WLED_ENABLE_JSONLIVE
bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient = 0);
@ -152,9 +170,11 @@ void handleTransitions();
void handleNightlight();
byte scaledBri(byte in);
#ifdef WLED_ENABLE_LOXONE
//lx_parser.cpp
bool parseLx(int lxValue, byte* rgbw);
void parseLxJson(int lxValue, byte segId, bool secondary);
#endif
//mqtt.cpp
bool initMqtt();
@ -190,12 +210,16 @@ void serializePlaylist(JsonObject obj);
void initPresetsFile();
void handlePresets();
bool applyPreset(byte index, byte callMode = CALL_MODE_DIRECT_CHANGE);
void applyPresetWithFallback(uint8_t presetID, uint8_t callMode, uint8_t effectID = 0, uint8_t paletteID = 0);
inline bool applyTemporaryPreset() {return applyPreset(255);};
void savePreset(byte index, const char* pname = nullptr, JsonObject saveobj = JsonObject());
inline void saveTemporaryPreset() {savePreset(255);};
void deletePreset(byte index);
bool getPresetName(byte index, String& name);
//remote.cpp
void handleRemote();
//set.cpp
bool isAsterisksOnly(const char* str, byte maxLen);
void handleSettingsSet(AsyncWebServerRequest *request, byte subPage);
@ -203,7 +227,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply=tru
//udp.cpp
void notify(byte callMode, bool followUp=false);
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte *buffer, uint8_t bri=255, bool isRGBW=false);
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8_t *buffer, uint8_t bri=255, bool isRGBW=false);
void realtimeLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC);
void exitRealtime();
void handleNotifications();
@ -325,6 +349,7 @@ void releaseJSONBufferLock();
uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen);
uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxLen, uint8_t *var = nullptr);
int16_t extractModeDefaults(uint8_t mode, const char *segVar);
void checkSettingsPIN(const char *pin);
uint16_t crc16(const unsigned char* data_p, size_t length);
um_data_t* simulateSound(uint8_t simulationId);
void enumerateLedmaps();
@ -372,7 +397,6 @@ void serveIndexOrWelcome(AsyncWebServerRequest *request);
void serveIndex(AsyncWebServerRequest* request);
String msgProcessor(const String& var);
void serveMessage(AsyncWebServerRequest* request, uint16_t code, const String& headl, const String& subl="", byte optionT=255);
String settingsProcessor(const String& var);
String dmxProcessor(const String& var);
void serveSettings(AsyncWebServerRequest* request, bool post = false);
void serveSettingsJS(AsyncWebServerRequest* request);

View File

@ -27,9 +27,10 @@
// There are no consecutive spaces longer than this in the file, so if more space is required, findSpace() can return false immediately
// Actual space may be lower
uint16_t knownLargestSpace = UINT16_MAX;
constexpr size_t MAX_SPACE = UINT16_MAX * 2U; // smallest supported config has 128Kb flash size
static volatile size_t knownLargestSpace = MAX_SPACE;
File f;
static File f; // don't export to other cpp files
//wrapper to find out how long closing takes
void closeFile() {
@ -44,7 +45,7 @@ void closeFile() {
//find() that reads and buffers data from file stream in 256-byte blocks.
//Significantly faster, f.find(key) can take SECONDS for multi-kB files
bool bufferedFind(const char *target, bool fromStart = true) {
static bool bufferedFind(const char *target, bool fromStart = true) {
#ifdef WLED_DEBUG_FS
DEBUGFS_PRINT("Find ");
DEBUGFS_PRINTLN(target);
@ -59,8 +60,8 @@ bool bufferedFind(const char *target, bool fromStart = true) {
if (fromStart) f.seek(0);
while (f.position() < f.size() -1) {
uint16_t bufsize = f.read(buf, FS_BUFSIZE);
uint16_t count = 0;
size_t bufsize = f.read(buf, FS_BUFSIZE); // better to use size_t instead if uint16_t
size_t count = 0;
while (count < bufsize) {
if(buf[count] != target[index])
index = 0; // reset index if any char does not match
@ -80,7 +81,7 @@ bool bufferedFind(const char *target, bool fromStart = true) {
}
//find empty spots in file stream in 256-byte blocks.
bool bufferedFindSpace(uint16_t targetLen, bool fromStart = true) {
static bool bufferedFindSpace(size_t targetLen, bool fromStart = true) {
#ifdef WLED_DEBUG_FS
DEBUGFS_PRINTF("Find %d spaces\n", targetLen);
@ -95,20 +96,20 @@ bool bufferedFindSpace(uint16_t targetLen, bool fromStart = true) {
if (!f || !f.size()) return false;
uint16_t index = 0;
size_t index = 0; // better to use size_t instead if uint16_t
byte buf[FS_BUFSIZE];
if (fromStart) f.seek(0);
while (f.position() < f.size() -1) {
uint16_t bufsize = f.read(buf, FS_BUFSIZE);
uint16_t count = 0;
size_t bufsize = f.read(buf, FS_BUFSIZE);
size_t count = 0;
while (count < bufsize) {
if(buf[count] == ' ') {
if(++index >= targetLen) { // return true if space long enough
if (fromStart) {
f.seek((f.position() - bufsize) + count +1 - targetLen);
knownLargestSpace = UINT16_MAX; //there may be larger spaces after, so we don't know
knownLargestSpace = MAX_SPACE; //there may be larger spaces after, so we don't know
}
DEBUGFS_PRINTF("Found at pos %d, took %d ms", f.position(), millis() - s);
return true;
@ -116,7 +117,7 @@ bool bufferedFindSpace(uint16_t targetLen, bool fromStart = true) {
} else {
if (!fromStart) return false;
if (index) {
if (knownLargestSpace < index || knownLargestSpace == UINT16_MAX) knownLargestSpace = index;
if (knownLargestSpace < index || (knownLargestSpace == MAX_SPACE)) knownLargestSpace = index;
index = 0; // reset index if not space
}
}
@ -129,7 +130,7 @@ bool bufferedFindSpace(uint16_t targetLen, bool fromStart = true) {
}
//find the closing bracket corresponding to the opening bracket at the file pos when calling this function
bool bufferedFindObjectEnd() {
static bool bufferedFindObjectEnd() {
#ifdef WLED_DEBUG_FS
DEBUGFS_PRINTLN(F("Find obj end"));
uint32_t s = millis();
@ -142,8 +143,8 @@ bool bufferedFindObjectEnd() {
byte buf[FS_BUFSIZE];
while (f.position() < f.size() -1) {
uint16_t bufsize = f.read(buf, FS_BUFSIZE);
uint16_t count = 0;
size_t bufsize = f.read(buf, FS_BUFSIZE); // better to use size_t instead of uint16_t
size_t count = 0;
while (count < bufsize) {
if (buf[count] == '{') objDepth++;
@ -161,13 +162,13 @@ bool bufferedFindObjectEnd() {
}
//fills n bytes from current file pos with ' ' characters
void writeSpace(uint16_t l)
static void writeSpace(size_t l)
{
byte buf[FS_BUFSIZE];
memset(buf, ' ', FS_BUFSIZE);
while (l > 0) {
uint16_t block = (l>FS_BUFSIZE) ? FS_BUFSIZE : l;
size_t block = (l>FS_BUFSIZE) ? FS_BUFSIZE : l;
f.write(buf, block);
l -= block;
}
@ -270,7 +271,7 @@ bool writeObjectToFile(const char* file, const char* key, JsonDocument* content)
s = millis();
#endif
uint32_t pos = 0;
size_t pos = 0;
f = WLED_FS.open(file, "r+");
if (!f && !WLED_FS.exists(file)) f = WLED_FS.open(file, "w+");
if (!f) {
@ -287,7 +288,7 @@ bool writeObjectToFile(const char* file, const char* key, JsonDocument* content)
pos = f.position();
//measure out end of old object
bufferedFindObjectEnd();
uint32_t pos2 = f.position();
size_t pos2 = f.position();
uint32_t oldLen = pos2 - pos;
DEBUGFS_PRINTF("Old obj len %d\n", oldLen);
@ -298,7 +299,7 @@ bool writeObjectToFile(const char* file, const char* key, JsonDocument* content)
//3. The new content is larger than the old, but smaller than old + trailing spaces, overwrite with new
//4. The new content is larger than old + trailing spaces, delete old and append
uint32_t contentLen = 0;
size_t contentLen = 0;
if (!content->isNull()) contentLen = measureJson(*content);
if (contentLen && contentLen <= oldLen) { //replace and fill diff with spaces
@ -375,7 +376,7 @@ void updateFSInfo() {
//Un-comment any file types you need
String getContentType(AsyncWebServerRequest* request, String filename){
static String getContentType(AsyncWebServerRequest* request, String filename){
if(request->hasArg("download")) return "application/octet-stream";
else if(filename.endsWith(".htm")) return "text/html";
else if(filename.endsWith(".html")) return "text/html";
@ -394,7 +395,7 @@ String getContentType(AsyncWebServerRequest* request, String filename){
}
bool handleFileRead(AsyncWebServerRequest* request, String path){
DEBUG_PRINTLN("FileRead: " + path);
DEBUG_PRINTLN("WS FileRead: " + path);
if(path.endsWith("/")) path += "index.htm";
if(path.indexOf("sec") > -1) return false;
String contentType = getContentType(request, path);

308
wled00/html_cpal.h Normal file
View File

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

View File

@ -20,7 +20,7 @@ const uint8_t PAGE_usermod[] PROGMEM = {
// Autogenerated from wled00/data/msg.htm, do not edit!!
const char PAGE_msg[] PROGMEM = R"=====(<!DOCTYPE html><html><head><meta content="width=device-width" name="viewport">
<title>WLED Message</title><script>
function B(){window.history.back()}function RS(){window.location="/settings"}function RP(){top.location.href="/"}
function B(){window.history.back()}function RS(){window.location="../settings"}function RP(){top.location.href="../"}
</script><style>@import url("style.css");</style></head><body><h2>%MSG%</body></html>)=====";
@ -41,57 +41,57 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()=====";
#endif
// Autogenerated from wled00/data/update.htm, do not edit!!
const uint16_t PAGE_update_length = 615;
const uint16_t PAGE_update_length = 616;
const uint8_t PAGE_update[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x75, 0x53, 0x5d, 0x6f, 0xd4, 0x30,
0x10, 0x7c, 0xcf, 0xaf, 0x70, 0xfd, 0x74, 0x27, 0x71, 0x4e, 0xa9, 0x78, 0xa1, 0x24, 0x29, 0x1c,
0xad, 0x50, 0x25, 0xa4, 0x56, 0x6a, 0x0b, 0xe2, 0x09, 0x39, 0xf6, 0xe6, 0x62, 0xce, 0xb1, 0x53,
0x7b, 0x73, 0xa7, 0xa8, 0xea, 0x7f, 0x67, 0xe3, 0xdc, 0x15, 0xc4, 0xc7, 0x4b, 0x14, 0x67, 0x67,
0xc7, 0xbb, 0x33, 0x93, 0xe2, 0xe4, 0xf2, 0xe6, 0xe3, 0xfd, 0xb7, 0xdb, 0x2b, 0xd6, 0x62, 0x67,
0xab, 0xe2, 0xf0, 0x04, 0xa9, 0xab, 0xa2, 0x03, 0x94, 0x4c, 0x79, 0x87, 0xe0, 0xb0, 0xe4, 0x7b,
0xa3, 0xb1, 0x2d, 0x35, 0xec, 0x8c, 0x82, 0x55, 0x3a, 0x70, 0xe6, 0x64, 0x07, 0x25, 0xdf, 0x19,
0xd8, 0xf7, 0x3e, 0x20, 0xaf, 0xb2, 0x02, 0x0d, 0x5a, 0xa8, 0xbe, 0x7e, 0xbe, 0xba, 0x64, 0x0f,
0xbd, 0x96, 0x08, 0x45, 0x3e, 0x7f, 0x2a, 0xa2, 0x0a, 0xa6, 0xc7, 0x2a, 0x6b, 0x06, 0xa7, 0xd0,
0x78, 0xc7, 0xd6, 0x8b, 0xe5, 0xd3, 0xde, 0x38, 0xed, 0xf7, 0xa2, 0x35, 0x11, 0x7d, 0x18, 0x45,
0x2d, 0xd5, 0x76, 0xb1, 0x7c, 0x7e, 0x81, 0x3c, 0x10, 0x44, 0x7b, 0x35, 0x74, 0x34, 0x81, 0xd8,
0x00, 0x5e, 0x59, 0x98, 0x5e, 0xd7, 0xe3, 0xb5, 0x5e, 0xf0, 0xa1, 0xe1, 0x4b, 0x11, 0x71, 0xb4,
0x20, 0xb4, 0x89, 0xbd, 0x95, 0x63, 0xc9, 0x9d, 0x77, 0xc0, 0x5f, 0xfd, 0xb7, 0xa5, 0x8b, 0x9b,
0xbf, 0x7b, 0x6a, 0xeb, 0xd5, 0x96, 0x3f, 0x67, 0x45, 0x7e, 0x18, 0xf1, 0x30, 0x2a, 0x8b, 0x41,
0x95, 0x3c, 0x8f, 0x80, 0x68, 0xdc, 0x26, 0xe6, 0x51, 0xfc, 0x88, 0x17, 0x7d, 0xf9, 0x96, 0x57,
0xbf, 0x21, 0x27, 0xaa, 0x2a, 0x7b, 0x6f, 0xba, 0x49, 0x00, 0x36, 0x04, 0xbb, 0xe0, 0x33, 0xbd,
0x8a, 0x91, 0x2f, 0xdf, 0x11, 0x32, 0x21, 0x8a, 0x7c, 0x96, 0xb4, 0xf6, 0x7a, 0x64, 0xde, 0x59,
0x2f, 0x75, 0xc9, 0x3f, 0x01, 0x7e, 0x59, 0x2c, 0x89, 0xae, 0x3d, 0xab, 0xb2, 0x24, 0xd9, 0x9d,
0x6f, 0x70, 0x2f, 0x03, 0xbc, 0x68, 0x47, 0x95, 0xa2, 0xf1, 0xa1, 0x63, 0xe4, 0x45, 0xeb, 0xa9,
0xe7, 0xf6, 0xe6, 0xee, 0x9e, 0x33, 0x99, 0xe4, 0xa1, 0xe1, 0x86, 0x84, 0xe3, 0xcc, 0x50, 0x89,
0xf4, 0x60, 0x19, 0x90, 0x72, 0x63, 0x4f, 0xa6, 0x74, 0x83, 0x45, 0xd3, 0xcb, 0x80, 0xf9, 0xd4,
0xbf, 0x22, 0x98, 0xe4, 0x74, 0x73, 0x1c, 0xea, 0xce, 0x90, 0x9b, 0x0f, 0xd3, 0xc5, 0xd7, 0x2e,
0xa2, 0xb4, 0x16, 0x34, 0xdb, 0x41, 0x88, 0xc4, 0x78, 0xce, 0x8a, 0xd8, 0x4b, 0xc7, 0x32, 0x65,
0x65, 0x8c, 0x25, 0x8f, 0xa6, 0xe7, 0xd5, 0xa9, 0x78, 0xfd, 0x46, 0x9c, 0xae, 0xea, 0x33, 0x5a,
0x86, 0x8a, 0xb4, 0x44, 0xa8, 0x2e, 0xfd, 0x3e, 0x2d, 0xc1, 0xb0, 0x05, 0x66, 0x69, 0x84, 0x88,
0xac, 0x36, 0x4e, 0x86, 0x91, 0x28, 0x24, 0xcb, 0xda, 0x00, 0x4d, 0xc9, 0x5b, 0xc4, 0x3e, 0x9e,
0xe7, 0xf9, 0xc6, 0x60, 0x3b, 0xd4, 0x42, 0xf9, 0x2e, 0xff, 0x60, 0x82, 0xf2, 0xde, 0x6f, 0x0d,
0xe4, 0xd3, 0xc6, 0x79, 0x00, 0x0b, 0x32, 0x42, 0xe4, 0x0c, 0x65, 0x20, 0xbb, 0x4a, 0xfe, 0xbd,
0xb6, 0xd2, 0x6d, 0x49, 0x15, 0xd3, 0x6d, 0x58, 0x96, 0x3c, 0x38, 0xf2, 0xd0, 0x17, 0x11, 0x5b,
0x03, 0x56, 0x47, 0x61, 0xfc, 0x81, 0xf6, 0x48, 0xf1, 0x27, 0xb5, 0x88, 0xbb, 0xcd, 0x45, 0x52,
0xbf, 0x6c, 0x68, 0xc2, 0x55, 0x7c, 0x1c, 0x48, 0xd9, 0x29, 0xa3, 0xb9, 0x4c, 0x3b, 0x14, 0xc6,
0xf5, 0x03, 0xb2, 0x59, 0xae, 0xc6, 0x58, 0x38, 0xe6, 0xf9, 0x28, 0x6a, 0x80, 0xc7, 0xc1, 0x04,
0xd0, 0x33, 0xba, 0x1e, 0x10, 0x29, 0x92, 0x33, 0x7c, 0x96, 0x91, 0xc8, 0x66, 0xa3, 0x4e, 0x8a,
0x7c, 0x2e, 0xff, 0x03, 0x3a, 0x1f, 0x26, 0xed, 0x95, 0x35, 0x6a, 0x5b, 0xf2, 0xf5, 0x24, 0xfd,
0x9a, 0x92, 0xfe, 0xab, 0x29, 0x79, 0x54, 0x15, 0xda, 0xec, 0xb2, 0x64, 0xe5, 0x94, 0x53, 0xa2,
0xa9, 0x12, 0x3b, 0x85, 0x4f, 0x08, 0x41, 0xe0, 0x44, 0x7e, 0x9b, 0x96, 0x65, 0xda, 0x33, 0xe7,
0x91, 0x29, 0xeb, 0xe9, 0xe0, 0x03, 0xcd, 0xda, 0x04, 0x88, 0x6d, 0xf2, 0xa3, 0x97, 0x1b, 0x60,
0xe7, 0xcb, 0x22, 0x27, 0xbe, 0x69, 0xdd, 0x29, 0x74, 0x53, 0x02, 0xa7, 0x5f, 0xfb, 0x27, 0x3b,
0x01, 0xc5, 0x54, 0xf0, 0x03, 0x00, 0x00
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x75, 0x53, 0x4d, 0x6f, 0xd4, 0x30,
0x10, 0xbd, 0xe7, 0x57, 0x18, 0x9f, 0x76, 0x25, 0xd6, 0x2e, 0x1f, 0x17, 0x4a, 0x92, 0x42, 0x69,
0x85, 0x2a, 0x21, 0xb5, 0x52, 0x5b, 0x10, 0x27, 0xe4, 0xd8, 0x93, 0x8d, 0x59, 0xc7, 0x4e, 0xed,
0xc9, 0xae, 0x56, 0xa8, 0xff, 0x9d, 0x89, 0xb3, 0x5b, 0x10, 0x1f, 0x97, 0x28, 0xce, 0xbc, 0x79,
0x9e, 0x79, 0xef, 0xa5, 0x7c, 0x76, 0x71, 0xfd, 0xe1, 0xee, 0xeb, 0xcd, 0x25, 0xeb, 0xb0, 0x77,
0x75, 0x79, 0x78, 0x82, 0x32, 0x75, 0xd9, 0x03, 0x2a, 0xa6, 0x83, 0x47, 0xf0, 0x58, 0xf1, 0x9d,
0x35, 0xd8, 0x55, 0x06, 0xb6, 0x56, 0xc3, 0x2a, 0x1f, 0x38, 0xf3, 0xaa, 0x87, 0x8a, 0x6f, 0x2d,
0xec, 0x86, 0x10, 0x91, 0xd7, 0x45, 0x89, 0x16, 0x1d, 0xd4, 0x5f, 0x3e, 0x5d, 0x5e, 0xb0, 0xfb,
0xc1, 0x28, 0x84, 0x52, 0xce, 0x9f, 0xca, 0xa4, 0xa3, 0x1d, 0xb0, 0x2e, 0xda, 0xd1, 0x6b, 0xb4,
0xc1, 0xb3, 0xf3, 0xc5, 0xf2, 0xc7, 0xce, 0x7a, 0x13, 0x76, 0xa2, 0xb3, 0x09, 0x43, 0xdc, 0x8b,
0x46, 0xe9, 0xcd, 0x62, 0xf9, 0xf8, 0x04, 0xb9, 0x27, 0x88, 0x09, 0x7a, 0xec, 0x69, 0x02, 0xb1,
0x06, 0xbc, 0x74, 0x30, 0xbd, 0x9e, 0xef, 0xaf, 0xcc, 0x82, 0x8f, 0x2d, 0x5f, 0x8a, 0x84, 0x7b,
0x07, 0xc2, 0xd8, 0x34, 0x38, 0xb5, 0xaf, 0xb8, 0x0f, 0x1e, 0xf8, 0xf3, 0xff, 0xb6, 0xf4, 0x69,
0xfd, 0x77, 0x4f, 0xe3, 0x82, 0xde, 0xf0, 0xc7, 0xa2, 0x94, 0x87, 0x11, 0x0f, 0xa3, 0xb2, 0x14,
0x75, 0xc5, 0x65, 0x02, 0x44, 0xeb, 0xd7, 0x49, 0x26, 0xf1, 0x3d, 0x9d, 0x0d, 0xd5, 0x1b, 0x5e,
0xff, 0x86, 0x9c, 0xa8, 0xea, 0xe2, 0x9d, 0xed, 0x27, 0x01, 0xd8, 0x18, 0xdd, 0x82, 0xcf, 0xf4,
0x3a, 0x25, 0xbe, 0x7c, 0x4b, 0xc8, 0x8c, 0x28, 0xe5, 0x2c, 0x69, 0x13, 0xcc, 0x9e, 0x05, 0xef,
0x82, 0x32, 0x15, 0xff, 0x08, 0xf8, 0x79, 0xb1, 0x24, 0xba, 0xee, 0x65, 0x5d, 0x64, 0xc9, 0x6e,
0x43, 0x8b, 0x3b, 0x15, 0xe1, 0x49, 0x3b, 0xaa, 0x94, 0x6d, 0x88, 0x3d, 0x23, 0x2f, 0xba, 0x40,
0x3d, 0x37, 0xd7, 0xb7, 0x77, 0x9c, 0xa9, 0x2c, 0x4f, 0xc5, 0x85, 0x1c, 0x33, 0x90, 0x33, 0x4b,
0x35, 0x12, 0x84, 0x15, 0x40, 0xd2, 0xed, 0x07, 0x72, 0xa5, 0x1f, 0x1d, 0xda, 0x41, 0x45, 0x94,
0x13, 0xc1, 0x8a, 0x60, 0x8a, 0xd3, 0xd5, 0x69, 0x6c, 0x7a, 0x4b, 0x76, 0xde, 0x4f, 0x37, 0x5f,
0xf9, 0x84, 0xca, 0x39, 0x30, 0x6c, 0x0b, 0x31, 0x11, 0xe5, 0x29, 0x2b, 0xd3, 0xa0, 0x3c, 0x2b,
0xb4, 0x53, 0x29, 0x55, 0x3c, 0xd9, 0x81, 0xd7, 0x27, 0xe2, 0xc5, 0x6b, 0x71, 0xb2, 0x6a, 0x5e,
0xd1, 0x36, 0x54, 0xa4, 0x2d, 0x62, 0x7d, 0x11, 0x76, 0x79, 0x0b, 0x86, 0x1d, 0x30, 0x47, 0x23,
0x24, 0x64, 0x8d, 0xf5, 0x2a, 0xee, 0x89, 0x42, 0xb1, 0xa2, 0x8b, 0xd0, 0x56, 0xbc, 0x43, 0x1c,
0xd2, 0xa9, 0x94, 0x6b, 0x8b, 0xdd, 0xd8, 0x08, 0x1d, 0x7a, 0xf9, 0xde, 0x46, 0x1d, 0x42, 0xd8,
0x58, 0x90, 0xd3, 0xca, 0x32, 0x82, 0x03, 0x95, 0x20, 0x71, 0x86, 0x2a, 0x92, 0x5f, 0x15, 0xff,
0xd6, 0x38, 0xe5, 0x37, 0x24, 0x8b, 0xed, 0xd7, 0xac, 0xc8, 0x26, 0x1c, 0x79, 0xe8, 0x8b, 0x48,
0x9d, 0x05, 0x67, 0x92, 0xb0, 0xe1, 0x40, 0x7b, 0xa4, 0xf8, 0x93, 0x5a, 0xa4, 0xed, 0xfa, 0x2c,
0xcb, 0x5f, 0xb5, 0x34, 0xe1, 0x2a, 0x3d, 0x8c, 0x24, 0xed, 0x14, 0x52, 0xa9, 0xf2, 0x0e, 0xa5,
0xf5, 0xc3, 0x88, 0x6c, 0x96, 0xab, 0xb5, 0x0e, 0x8e, 0x81, 0x3e, 0x8a, 0x1a, 0xe1, 0x61, 0xb4,
0x11, 0xcc, 0x8c, 0x6e, 0x46, 0x44, 0xca, 0xe4, 0x0c, 0x9f, 0x65, 0x24, 0xb2, 0xd9, 0xa9, 0x67,
0xa5, 0x9c, 0xcb, 0xff, 0x80, 0xce, 0x87, 0x49, 0x7b, 0xed, 0xac, 0xde, 0x54, 0xfc, 0x7c, 0x92,
0xfe, 0x9c, 0xa2, 0xfe, 0xab, 0x29, 0x7b, 0x54, 0x97, 0xc6, 0x6e, 0x8b, 0x6c, 0xe5, 0x14, 0x54,
0xa2, 0xa9, 0x33, 0x3b, 0xa5, 0x4f, 0x08, 0x41, 0xe0, 0x4c, 0x7e, 0x93, 0x97, 0x65, 0x26, 0x30,
0x1f, 0x90, 0x69, 0x17, 0xe8, 0x10, 0x22, 0xcd, 0xda, 0x46, 0x48, 0x5d, 0xf6, 0x63, 0x50, 0x6b,
0x60, 0xa7, 0xcb, 0x52, 0x12, 0xdf, 0xb4, 0xee, 0x94, 0xba, 0x29, 0x82, 0xd3, 0xbf, 0xfd, 0x13,
0x46, 0x22, 0xf9, 0xe1, 0xf1, 0x03, 0x00, 0x00
};
// Autogenerated from wled00/data/welcome.htm, do not edit!!
const uint16_t PAGE_welcome_length = 1528;
const uint16_t PAGE_welcome_length = 1531;
const uint8_t PAGE_welcome[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x95, 0x56, 0x5b, 0x93, 0xaa, 0x3a,
0x16, 0x7e, 0xef, 0x5f, 0xc1, 0x76, 0xea, 0xd4, 0x79, 0x70, 0x77, 0x73, 0x13, 0x51, 0xdb, 0xee,
0x16, 0x7e, 0xef, 0x5f, 0xc1, 0x76, 0x6a, 0xea, 0x3c, 0xb8, 0xbb, 0xb9, 0x89, 0xa8, 0x6d, 0xf7,
0x19, 0xc5, 0x4b, 0x7b, 0x03, 0x6f, 0x78, 0x7b, 0x0b, 0x10, 0x20, 0x08, 0x04, 0x93, 0x80, 0x97,
0xae, 0xfe, 0xef, 0x13, 0x74, 0xf7, 0xd4, 0x3e, 0x75, 0x1e, 0xa6, 0x4e, 0x2c, 0x21, 0xf9, 0x92,
0xae, 0xfe, 0xef, 0x27, 0xe8, 0xee, 0xa9, 0x7d, 0xea, 0x3c, 0x4c, 0x4d, 0x2c, 0x21, 0xf9, 0x92,
0xb5, 0xd6, 0xb7, 0x92, 0xb8, 0x3e, 0xdb, 0x3f, 0x7a, 0x96, 0xb1, 0xde, 0xcf, 0xfb, 0x42, 0xc8,
0x92, 0xf8, 0xbd, 0xfd, 0xeb, 0x09, 0x81, 0xf7, 0xde, 0x4e, 0x20, 0x03, 0x82, 0x1b, 0x02, 0x42,
0x21, 0x7b, 0xab, 0xe4, 0xcc, 0x7f, 0x6e, 0x54, 0x7e, 0xa1, 0x4f, 0x2e, 0x4e, 0x19, 0x4c, 0x39,
@ -107,294 +107,272 @@ const uint8_t PAGE_welcome[] PROGMEM = {
0xb0, 0x9c, 0xcb, 0x09, 0xe5, 0x93, 0x19, 0x46, 0x77, 0x4f, 0x19, 0xf0, 0x3c, 0x94, 0x06, 0xad,
0x46, 0x76, 0xf9, 0xb6, 0x97, 0x25, 0xde, 0xbf, 0xef, 0x41, 0x4b, 0x51, 0xcb, 0xfe, 0x3d, 0x3e,
0x23, 0x9c, 0x90, 0x8f, 0x49, 0xd2, 0xca, 0xb3, 0x0c, 0x12, 0x17, 0x50, 0xf8, 0xfa, 0x7b, 0x06,
0xe1, 0x37, 0xf3, 0x07, 0x4a, 0xd1, 0x0d, 0xb6, 0xe4, 0x26, 0xb7, 0xfe, 0x3b, 0x57, 0x55, 0x55,
0xe1, 0x37, 0xf3, 0x07, 0x4a, 0xd1, 0x0d, 0xb6, 0xe4, 0x26, 0xb7, 0xfe, 0x27, 0x57, 0x55, 0x55,
0x7f, 0xa3, 0xf8, 0xea, 0x60, 0xe2, 0x41, 0xd2, 0x92, 0x04, 0x8a, 0x63, 0xe4, 0x09, 0xbf, 0x61,
0xcf, 0x04, 0x78, 0x28, 0xa7, 0x2d, 0x45, 0xcb, 0x2e, 0x5f, 0x28, 0x09, 0x3e, 0x1f, 0xac, 0x9a,
0x9a, 0x74, 0x67, 0x7b, 0x79, 0x9c, 0x54, 0xab, 0xa1, 0xfc, 0xf1, 0x8a, 0x12, 0x10, 0xc0, 0x67,
0x02, 0x53, 0x6e, 0x56, 0xe6, 0x93, 0xa1, 0x0b, 0x8c, 0x01, 0x83, 0xde, 0xdf, 0x66, 0x5c, 0x82,
0x68, 0xf6, 0x0c, 0xbd, 0x00, 0xd2, 0xef, 0x8c, 0x6b, 0x45, 0x28, 0x48, 0xe5, 0xe7, 0x15, 0xa4,
0x7c, 0x39, 0x43, 0x38, 0x6d, 0xf9, 0x48, 0x90, 0xe9, 0xd7, 0x7f, 0x8e, 0xf0, 0xea, 0x13, 0x7e,
0xe4, 0x54, 0xf0, 0xd1, 0xa7, 0x4f, 0x70, 0xf2, 0x89, 0x33, 0xe0, 0x22, 0x76, 0x6d, 0x49, 0x5f,
0x0c, 0xff, 0x6f, 0x20, 0x7f, 0x7d, 0xbd, 0x24, 0x00, 0xa5, 0x9f, 0x7f, 0x75, 0xf0, 0xa2, 0x51,
0xe1, 0x45, 0xa7, 0x82, 0x83, 0x59, 0xf8, 0xf5, 0xd4, 0x16, 0x1f, 0xc7, 0xdf, 0x16, 0x1f, 0x37,
0xb3, 0xbc, 0x05, 0xef, 0x6d, 0x9e, 0x97, 0x00, 0x62, 0x7e, 0x79, 0xf8, 0x45, 0xa2, 0xc4, 0x7d,
0xab, 0x78, 0x80, 0x81, 0xd6, 0x9d, 0xb5, 0x98, 0xa5, 0x01, 0xdf, 0x3f, 0x0a, 0xeb, 0xb5, 0x9f,
0x68, 0xd3, 0xb5, 0x96, 0x67, 0x69, 0x32, 0x0c, 0x70, 0x87, 0x37, 0x73, 0x65, 0x87, 0x7d, 0x3b,
0xe0, 0xbd, 0xa1, 0x54, 0x8e, 0x7d, 0xa3, 0x33, 0xe3, 0xaf, 0x1e, 0xb8, 0x4d, 0xad, 0xbc, 0x04,
0x3a, 0x3b, 0x73, 0xb5, 0x94, 0x46, 0x1d, 0x42, 0x6b, 0x6e, 0x7d, 0x51, 0x02, 0xcb, 0x74, 0x61,
0xcb, 0xdd, 0x4e, 0xc7, 0xb8, 0x44, 0xe7, 0xa2, 0xb1, 0x5f, 0xd8, 0x1c, 0xeb, 0x4e, 0xed, 0xfe,
0xc5, 0x5e, 0xde, 0xe7, 0xbb, 0x0d, 0x39, 0x30, 0x6c, 0xf1, 0x36, 0x39, 0x89, 0xa2, 0x98, 0x60,
0x9d, 0x6e, 0x67, 0x66, 0xc3, 0xb1, 0xaa, 0x87, 0xd1, 0x9e, 0x1d, 0x40, 0x67, 0xde, 0x24, 0x9d,
0x79, 0xf5, 0x63, 0x46, 0x0d, 0x34, 0xac, 0xae, 0x3b, 0x23, 0x2b, 0x9d, 0xad, 0xa4, 0xc9, 0xc9,
0xb4, 0xf5, 0xc9, 0x3c, 0x9d, 0xda, 0x07, 0x8b, 0x9c, 0xea, 0x05, 0xb7, 0xac, 0x19, 0x9d, 0x60,
0x18, 0x62, 0x30, 0xad, 0x8a, 0x45, 0xdd, 0x08, 0x70, 0xff, 0x32, 0x5b, 0xdf, 0x09, 0xc5, 0x49,
0xcd, 0x6a, 0x94, 0x9d, 0x83, 0x37, 0x18, 0x5b, 0xb6, 0xf8, 0x7f, 0xda, 0xb9, 0xd3, 0x35, 0x3b,
0x27, 0xb5, 0x34, 0x30, 0x76, 0xdd, 0xd1, 0x76, 0x57, 0xe6, 0xa7, 0xf7, 0xf8, 0xc3, 0x3a, 0x9f,
0x3f, 0x3e, 0x9c, 0x7a, 0x78, 0x2c, 0xa7, 0x4c, 0x29, 0xee, 0x2f, 0x36, 0xcb, 0xd1, 0x4a, 0x57,
0x37, 0xd1, 0x66, 0x6a, 0xcc, 0xba, 0x9d, 0xfe, 0x7e, 0x44, 0x1a, 0xc6, 0x7e, 0x72, 0x24, 0x5e,
0xa4, 0xfa, 0xf2, 0x58, 0xd5, 0x6f, 0x60, 0x37, 0x30, 0xb2, 0xb5, 0x55, 0xcd, 0x10, 0xe8, 0x05,
0xce, 0xfc, 0xc4, 0x2f, 0x66, 0x73, 0xb3, 0x90, 0x4e, 0x57, 0x52, 0x4c, 0xa2, 0xda, 0xa9, 0x9e,
0x48, 0x07, 0x22, 0x87, 0xd5, 0x99, 0x7e, 0x19, 0xc8, 0xb7, 0x65, 0x92, 0x6e, 0x6f, 0xa7, 0x4d,
0x53, 0x94, 0x3c, 0x25, 0x62, 0x6c, 0x88, 0x99, 0x25, 0xe7, 0x45, 0xd3, 0xb3, 0x2d, 0xe7, 0x0c,
0x23, 0x0d, 0x9f, 0x32, 0xdd, 0xbf, 0x6d, 0x37, 0xf3, 0xb8, 0x91, 0xd6, 0x9b, 0x20, 0x23, 0x37,
0x6c, 0xd9, 0xb6, 0xe3, 0x14, 0xde, 0xc8, 0xd9, 0xa8, 0xd6, 0xf4, 0x8c, 0xd8, 0xce, 0xad, 0x17,
0xab, 0xa1, 0xec, 0xe9, 0x49, 0x63, 0xa4, 0xfa, 0x70, 0xd5, 0x37, 0xa5, 0x48, 0x31, 0xa0, 0xe9,
0x58, 0xfb, 0xda, 0xec, 0x82, 0x82, 0xc8, 0x9a, 0x1a, 0x6e, 0x8d, 0x1e, 0xe8, 0x7a, 0xa3, 0xc4,
0xb2, 0x6b, 0x5c, 0xaf, 0xb5, 0xf3, 0x68, 0x34, 0x9d, 0x4e, 0xa3, 0xce, 0x85, 0x5d, 0x8f, 0x31,
0x3b, 0x29, 0xc4, 0x59, 0xdb, 0xd5, 0x13, 0x92, 0x64, 0x53, 0x23, 0x3b, 0xd3, 0x52, 0x62, 0x08,
0x06, 0xd6, 0x12, 0xa3, 0x08, 0x28, 0xb1, 0x36, 0x9b, 0x69, 0x40, 0x52, 0x80, 0xdb, 0xdc, 0x03,
0xb9, 0xbe, 0x3a, 0x6a, 0x2c, 0x00, 0x73, 0x62, 0x67, 0xd1, 0x21, 0x77, 0xa4, 0xee, 0xb4, 0xbe,
0x3f, 0xad, 0x2e, 0x93, 0xb3, 0xf3, 0xa1, 0xeb, 0x3b, 0xdb, 0x4e, 0x56, 0xc7, 0xf1, 0x6e, 0x15,
0x37, 0x16, 0x0c, 0xcc, 0xf2, 0xeb, 0x38, 0x3c, 0x69, 0x09, 0x98, 0x6a, 0xe9, 0x7a, 0xb2, 0xc9,
0x0e, 0x86, 0xac, 0x6e, 0x12, 0x36, 0xcb, 0xd6, 0x83, 0xb5, 0x12, 0xd4, 0x8a, 0x71, 0xb4, 0xce,
0x87, 0xfe, 0xec, 0x76, 0xdb, 0xf9, 0x0c, 0xd9, 0x87, 0x34, 0xf4, 0x58, 0xe0, 0xc8, 0x17, 0xec,
0x17, 0xd7, 0x6c, 0x89, 0x53, 0x6d, 0x1d, 0x99, 0xe9, 0x65, 0x6f, 0x36, 0x6f, 0x63, 0x5c, 0x9f,
0x68, 0x24, 0x5f, 0x8d, 0x6e, 0x0b, 0x36, 0xcc, 0x37, 0x87, 0x54, 0xba, 0x34, 0x65, 0x32, 0x29,
0xbc, 0x8f, 0x6e, 0x91, 0xa8, 0xcd, 0xbe, 0xbe, 0xba, 0x1e, 0x6a, 0x57, 0xa9, 0x3e, 0xbc, 0x35,
0xba, 0xbd, 0xee, 0x60, 0x72, 0xa3, 0xbb, 0x24, 0x74, 0xcf, 0x57, 0x9f, 0x0e, 0x0f, 0xcd, 0x4d,
0xe6, 0x84, 0x18, 0x19, 0x28, 0x05, 0x97, 0xb9, 0x99, 0x0c, 0xb7, 0xc9, 0x76, 0x4b, 0x4c, 0x5b,
0x89, 0xba, 0xd2, 0xa9, 0xfe, 0x51, 0x58, 0xa1, 0x29, 0x8f, 0x6d, 0x66, 0xa0, 0xcb, 0x82, 0x65,
0x4a, 0xa0, 0xc9, 0x87, 0x8d, 0xbd, 0x1b, 0xcd, 0x57, 0xca, 0x72, 0xd6, 0xe9, 0x55, 0xab, 0x6b,
0x25, 0xdd, 0x8b, 0x3d, 0xd2, 0x8b, 0xac, 0x59, 0xcf, 0xaa, 0x9d, 0x0d, 0x3d, 0x4a, 0xcc, 0x7d,
0xa4, 0x7a, 0xba, 0x9a, 0x92, 0x5d, 0x1e, 0x34, 0xf6, 0xac, 0x99, 0x9b, 0xdd, 0xc6, 0xc5, 0xb4,
0x65, 0x77, 0x62, 0xee, 0xb6, 0xb1, 0x3d, 0x30, 0x77, 0x46, 0xb4, 0x91, 0xb3, 0x43, 0x38, 0x5c,
0xf7, 0x1b, 0x6a, 0xa2, 0x14, 0x5b, 0x7f, 0xef, 0x8b, 0xe6, 0x30, 0xaa, 0x75, 0x03, 0xf9, 0x96,
0x6b, 0xe3, 0x9e, 0x2a, 0xce, 0xd2, 0x0f, 0xed, 0xb0, 0xf5, 0xa7, 0xd6, 0x91, 0x3a, 0x9c, 0xd2,
0x30, 0x0a, 0x76, 0x33, 0x53, 0x13, 0x0d, 0x65, 0xb8, 0xdb, 0x0f, 0x07, 0x83, 0x6d, 0xd3, 0x4c,
0x78, 0xfd, 0xae, 0xef, 0x72, 0x26, 0x0d, 0x93, 0xea, 0x98, 0x82, 0xab, 0x11, 0x35, 0x6e, 0x52,
0x1a, 0x46, 0xc3, 0x62, 0x3f, 0xbe, 0x6d, 0xfc, 0x1a, 0x58, 0xdd, 0x38, 0x13, 0x4a, 0xaa, 0xb9,
0xbe, 0x50, 0xc6, 0x23, 0x0f, 0xef, 0xd4, 0xbd, 0xb5, 0x88, 0x2c, 0x1a, 0x27, 0x74, 0x2b, 0x47,
0x63, 0x55, 0x56, 0xa4, 0xb4, 0xdf, 0x0c, 0x7c, 0xac, 0x37, 0xc3, 0x35, 0x30, 0x3d, 0x97, 0x9e,
0x36, 0xcb, 0xba, 0x2c, 0xc5, 0x85, 0x55, 0xd3, 0xb3, 0x38, 0x06, 0xb0, 0xb9, 0x84, 0x87, 0x86,
0xa4, 0xdd, 0x26, 0x58, 0x01, 0x2a, 0x02, 0x7b, 0xcd, 0xd5, 0xb5, 0x6c, 0x9d, 0x6c, 0x8c, 0x4e,
0xdd, 0x1b, 0x6b, 0x1f, 0xba, 0x29, 0x51, 0x22, 0x02, 0x3a, 0x3f, 0x77, 0x61, 0xe8, 0xe8, 0xc8,
0xef, 0x87, 0x39, 0x5d, 0xde, 0x7f, 0x5a, 0xfd, 0x78, 0xb0, 0x3e, 0xae, 0xf2, 0x45, 0x62, 0x18,
0x95, 0xf7, 0xa7, 0xb6, 0x87, 0x0a, 0xc1, 0x8d, 0x01, 0xa5, 0x6f, 0x95, 0xb2, 0x28, 0x71, 0x39,
0x0a, 0xe5, 0x6f, 0x2d, 0x12, 0x18, 0x16, 0xb6, 0xd3, 0x7e, 0x8f, 0x6b, 0x12, 0x07, 0xdb, 0xa1,
0xfa, 0xfe, 0xb4, 0x0e, 0x41, 0x7a, 0x14, 0xae, 0x38, 0x17, 0x78, 0x7d, 0x17, 0x50, 0x4a, 0x19,
0x88, 0xb9, 0x5a, 0x04, 0x42, 0x72, 0x15, 0x40, 0x96, 0xc5, 0xbc, 0xa8, 0x97, 0x15, 0xad, 0xb4,
0x50, 0x79, 0xd5, 0x7a, 0x37, 0xb9, 0x1e, 0x08, 0x94, 0xc1, 0x8c, 0xb6, 0xda, 0xa2, 0xc3, 0x11,
0x72, 0xff, 0x3e, 0x19, 0x38, 0x4d, 0xa1, 0xcb, 0x04, 0x2e, 0x8e, 0x42, 0x82, 0xbd, 0x3c, 0xbe,
0x07, 0xe3, 0x7e, 0x89, 0x10, 0x63, 0x17, 0xc4, 0xc2, 0x16, 0x0d, 0x90, 0x10, 0x42, 0xc2, 0x05,
0xf1, 0x6e, 0x73, 0xd7, 0x26, 0xe1, 0x09, 0xa7, 0x2e, 0x8f, 0x71, 0x7c, 0xfb, 0xf3, 0x8c, 0x52,
0x0f, 0x9f, 0x5f, 0xca, 0xc5, 0x65, 0xc0, 0x97, 0x90, 0x40, 0xff, 0xad, 0x22, 0x72, 0xe1, 0x66,
0x9c, 0x0e, 0x15, 0xcf, 0xc8, 0x47, 0x95, 0x3f, 0xdf, 0xef, 0x6e, 0xbe, 0x41, 0xce, 0xe0, 0xee,
0xe6, 0x41, 0x03, 0xbd, 0x3f, 0x8d, 0x73, 0xca, 0x29, 0x90, 0x6b, 0x99, 0x00, 0x0b, 0x11, 0x15,
0xb8, 0xf6, 0xf1, 0xa4, 0x84, 0xce, 0xbc, 0x24, 0x05, 0xff, 0xdd, 0x16, 0xd1, 0xfb, 0x3f, 0x0b,
0xcf, 0x55, 0x09, 0x12, 0xca, 0x03, 0xaf, 0xf1, 0x3d, 0xb7, 0x52, 0xee, 0x09, 0x8e, 0xe9, 0x8f,
0xbf, 0xc6, 0x16, 0xf9, 0xb6, 0xf3, 0xcd, 0x17, 0x1f, 0x65, 0x5d, 0xbc, 0xff, 0x07, 0xf9, 0x2f,
0xec, 0xfa, 0x82, 0xd2, 0x99, 0x08, 0x00, 0x00
0x9a, 0x74, 0x67, 0x7b, 0x79, 0x9c, 0x54, 0xab, 0xa1, 0xfc, 0xfb, 0x15, 0x25, 0x20, 0x80, 0xcf,
0x04, 0xa6, 0xdc, 0xac, 0xcc, 0x27, 0x43, 0x17, 0x18, 0x03, 0x06, 0xbd, 0x7f, 0xcc, 0xb8, 0x04,
0xd1, 0xec, 0x19, 0x7a, 0x01, 0xa4, 0xdf, 0x19, 0xd7, 0x8a, 0x50, 0x90, 0xca, 0xcf, 0x2b, 0x48,
0xf9, 0x72, 0x86, 0x70, 0xda, 0xf2, 0x91, 0x20, 0xd3, 0xaf, 0xff, 0x1c, 0xe1, 0xd5, 0x27, 0xfc,
0xc8, 0xa9, 0xe0, 0xa3, 0x4f, 0x9f, 0xe0, 0xe4, 0x13, 0x67, 0xc0, 0x45, 0xec, 0xda, 0x92, 0xbe,
0x18, 0xfe, 0xef, 0x40, 0xfe, 0xfa, 0x7a, 0x49, 0x00, 0x4a, 0x3f, 0xff, 0xee, 0xe0, 0x45, 0xa3,
0xc2, 0x8b, 0x4e, 0x05, 0x07, 0xb3, 0xf0, 0xeb, 0xa9, 0x2d, 0x3e, 0x8e, 0xbf, 0x2d, 0x3e, 0x6e,
0x66, 0x79, 0x0b, 0xde, 0xdb, 0x3c, 0x2f, 0x01, 0xc4, 0xfc, 0xf2, 0xf0, 0x8b, 0x44, 0x89, 0xfb,
0x56, 0xf1, 0x00, 0x03, 0xad, 0x3b, 0x6b, 0x31, 0x4b, 0x03, 0xbe, 0x7f, 0x14, 0xd6, 0x6b, 0x3f,
0xd1, 0xa6, 0x6b, 0x2d, 0xcf, 0xd2, 0x64, 0x18, 0xe0, 0x0e, 0x6f, 0xe6, 0xca, 0x0e, 0xfb, 0x76,
0xc0, 0x7b, 0x43, 0xa9, 0x1c, 0xfb, 0x46, 0x67, 0xc6, 0x5f, 0x3d, 0x70, 0x9b, 0x5a, 0x79, 0x09,
0x74, 0x76, 0xe6, 0x6a, 0x29, 0x8d, 0x3a, 0x84, 0xd6, 0xdc, 0xfa, 0xa2, 0x04, 0x96, 0xe9, 0xc2,
0x96, 0xbb, 0x9d, 0x8e, 0x71, 0x89, 0xce, 0x45, 0x63, 0xbf, 0xb0, 0x39, 0xd6, 0x9d, 0xda, 0xfd,
0x8b, 0xbd, 0xbc, 0xcf, 0x77, 0x1b, 0x72, 0x60, 0xd8, 0xe2, 0x6d, 0x72, 0x12, 0x45, 0x31, 0xc1,
0x3a, 0xdd, 0xce, 0xcc, 0x86, 0x63, 0x55, 0x0f, 0xa3, 0x3d, 0x3b, 0x80, 0xce, 0xbc, 0x49, 0x3a,
0xf3, 0xea, 0xc7, 0x8c, 0x1a, 0x68, 0x58, 0x5d, 0x77, 0x46, 0x56, 0x3a, 0x5b, 0x49, 0x93, 0x93,
0x69, 0xeb, 0x93, 0x79, 0x3a, 0xb5, 0x0f, 0x16, 0x39, 0xd5, 0x0b, 0x6e, 0x59, 0x33, 0x3a, 0xc1,
0x30, 0xc4, 0x60, 0x5a, 0x15, 0x8b, 0xba, 0x11, 0xe0, 0xfe, 0x65, 0xb6, 0xbe, 0x13, 0x8a, 0x93,
0x9a, 0xd5, 0x28, 0x3b, 0x07, 0x6f, 0x30, 0xb6, 0x6c, 0xf1, 0x7f, 0xb4, 0x73, 0xa7, 0x6b, 0x76,
0x4e, 0x6a, 0x69, 0x60, 0xec, 0xba, 0xa3, 0xed, 0xae, 0xcc, 0x4f, 0xef, 0xf1, 0x87, 0x75, 0x3e,
0x7f, 0x7c, 0x38, 0xf5, 0xf0, 0x58, 0x4e, 0x99, 0x52, 0xdc, 0x5f, 0x6c, 0x96, 0xa3, 0x95, 0xae,
0x6e, 0xa2, 0xcd, 0xd4, 0x98, 0x75, 0x3b, 0xfd, 0xfd, 0x88, 0x34, 0x8c, 0xfd, 0xe4, 0x48, 0xbc,
0x48, 0xf5, 0xe5, 0xb1, 0xaa, 0xdf, 0xc0, 0x6e, 0x60, 0x64, 0x6b, 0xab, 0x9a, 0x21, 0xd0, 0x0b,
0x9c, 0xf9, 0x89, 0x5f, 0xcc, 0xe6, 0x66, 0x21, 0x9d, 0xae, 0xa4, 0x98, 0x44, 0xb5, 0x53, 0x3d,
0x91, 0x0e, 0x44, 0x0e, 0xab, 0x33, 0xfd, 0x32, 0x90, 0x6f, 0xcb, 0x24, 0xdd, 0xde, 0x4e, 0x9b,
0xa6, 0x28, 0x79, 0x4a, 0xc4, 0xd8, 0x10, 0x33, 0x4b, 0xce, 0x8b, 0xa6, 0x67, 0x5b, 0xce, 0x19,
0x46, 0x1a, 0x3e, 0x65, 0xba, 0x7f, 0xdb, 0x6e, 0xe6, 0x71, 0x23, 0xad, 0x37, 0x41, 0x46, 0x6e,
0xd8, 0xb2, 0x6d, 0xc7, 0x29, 0xbc, 0x91, 0xb3, 0x51, 0xad, 0xe9, 0x19, 0xb1, 0x9d, 0x5b, 0x2f,
0x56, 0x43, 0xd9, 0xd3, 0x93, 0xc6, 0x48, 0xf5, 0xe1, 0xaa, 0x6f, 0x4a, 0x91, 0x62, 0x40, 0xd3,
0xb1, 0xf6, 0xb5, 0xd9, 0x05, 0x05, 0x91, 0x35, 0x35, 0xdc, 0x1a, 0x3d, 0xd0, 0xf5, 0x46, 0x89,
0x65, 0xd7, 0xb8, 0x5e, 0x6b, 0xe7, 0xd1, 0x68, 0x3a, 0x9d, 0x46, 0x9d, 0x0b, 0xbb, 0x1e, 0x63,
0x76, 0x52, 0x88, 0xb3, 0xb6, 0xab, 0x27, 0x24, 0xc9, 0xa6, 0x46, 0x76, 0xa6, 0xa5, 0xc4, 0x10,
0x0c, 0xac, 0x25, 0x46, 0x11, 0x50, 0x62, 0x6d, 0x36, 0xd3, 0x80, 0xa4, 0x00, 0xb7, 0xb9, 0x07,
0x72, 0x7d, 0x75, 0xd4, 0x58, 0x00, 0xe6, 0xc4, 0xce, 0xa2, 0x43, 0xee, 0x48, 0xdd, 0x69, 0x7d,
0x7f, 0x5a, 0x5d, 0x26, 0x67, 0xe7, 0x43, 0xd7, 0x77, 0xb6, 0x9d, 0xac, 0x8e, 0xe3, 0xdd, 0x2a,
0x6e, 0x2c, 0x18, 0x98, 0xe5, 0xd7, 0x71, 0x78, 0xd2, 0x12, 0x30, 0xd5, 0xd2, 0xf5, 0x64, 0x93,
0x1d, 0x0c, 0x59, 0xdd, 0x24, 0x6c, 0x96, 0xad, 0x07, 0x6b, 0x25, 0xa8, 0x15, 0xe3, 0x68, 0x9d,
0x0f, 0xfd, 0xd9, 0xed, 0xb6, 0xf3, 0x19, 0xb2, 0x0f, 0x69, 0xe8, 0xb1, 0xc0, 0x91, 0x2f, 0xd8,
0x2f, 0xae, 0xd9, 0x12, 0xa7, 0xda, 0x3a, 0x32, 0xd3, 0xcb, 0xde, 0x6c, 0xde, 0xc6, 0xb8, 0x3e,
0xd1, 0x48, 0xbe, 0x1a, 0xdd, 0x16, 0x6c, 0x98, 0x6f, 0x0e, 0xa9, 0x74, 0x69, 0xca, 0x64, 0x52,
0x78, 0x1f, 0xdd, 0x22, 0x51, 0x9b, 0x7d, 0x7d, 0x75, 0x3d, 0xd4, 0xae, 0x52, 0x7d, 0x78, 0x6b,
0x74, 0x7b, 0xdd, 0xc1, 0xe4, 0x46, 0x77, 0x49, 0xe8, 0x9e, 0xaf, 0x3e, 0x1d, 0x1e, 0x9a, 0x9b,
0xcc, 0x09, 0x31, 0x32, 0x50, 0x0a, 0x2e, 0x73, 0x33, 0x19, 0x6e, 0x93, 0xed, 0x96, 0x98, 0xb6,
0x12, 0x75, 0xa5, 0x53, 0xfd, 0xa3, 0xb0, 0x42, 0x53, 0x1e, 0xdb, 0xcc, 0x40, 0x97, 0x05, 0xcb,
0x94, 0x40, 0x93, 0x0f, 0x1b, 0x7b, 0x37, 0x9a, 0xaf, 0x94, 0xe5, 0xac, 0xd3, 0xab, 0x56, 0xd7,
0x4a, 0xba, 0x17, 0x7b, 0xa4, 0x17, 0x59, 0xb3, 0x9e, 0x55, 0x3b, 0x1b, 0x7a, 0x94, 0x98, 0xfb,
0x48, 0xf5, 0x74, 0x35, 0x25, 0xbb, 0x3c, 0x68, 0xec, 0x59, 0x33, 0x37, 0xbb, 0x8d, 0x8b, 0x69,
0xcb, 0xee, 0xc4, 0xdc, 0x6d, 0x63, 0x7b, 0x60, 0xee, 0x8c, 0x68, 0x23, 0x67, 0x87, 0x70, 0xb8,
0xee, 0x37, 0xd4, 0x44, 0x29, 0xb6, 0xfe, 0xde, 0x17, 0xcd, 0x61, 0x54, 0xeb, 0x06, 0xf2, 0x2d,
0xd7, 0xc6, 0x3d, 0x55, 0x9c, 0xa5, 0x1f, 0xda, 0x61, 0xeb, 0x4f, 0xad, 0x23, 0x75, 0x38, 0xa5,
0x61, 0x14, 0xec, 0x66, 0xa6, 0x26, 0x1a, 0xca, 0x70, 0xb7, 0x1f, 0x0e, 0x06, 0xdb, 0xa6, 0x99,
0xf0, 0xfa, 0x5d, 0xdf, 0xe5, 0x4c, 0x1a, 0x26, 0xd5, 0x31, 0x05, 0x57, 0x23, 0x6a, 0xdc, 0xa4,
0x34, 0x8c, 0x86, 0xc5, 0x7e, 0x7c, 0xdb, 0xf8, 0x35, 0xb0, 0xba, 0x71, 0x26, 0x94, 0x54, 0x73,
0x7d, 0xa1, 0x8c, 0x47, 0x1e, 0xde, 0xa9, 0x7b, 0x6b, 0x11, 0x59, 0x34, 0x4e, 0xe8, 0x56, 0x8e,
0xc6, 0xaa, 0xac, 0x48, 0x69, 0xbf, 0x19, 0xf8, 0x58, 0x6f, 0x86, 0x6b, 0x60, 0x7a, 0x2e, 0x3d,
0x6d, 0x96, 0x75, 0x59, 0x8a, 0x0b, 0xab, 0xa6, 0x67, 0x71, 0x0c, 0x60, 0x73, 0x09, 0x0f, 0x0d,
0x49, 0xbb, 0x4d, 0xb0, 0x02, 0x54, 0x04, 0xf6, 0x9a, 0xab, 0x6b, 0xd9, 0x3a, 0xd9, 0x18, 0x9d,
0xba, 0x37, 0xd6, 0x3e, 0x74, 0x53, 0xa2, 0x44, 0x04, 0x74, 0x7e, 0xee, 0xc2, 0xd0, 0xd1, 0x91,
0xdf, 0x0f, 0x73, 0xba, 0xbc, 0xff, 0xb4, 0xfa, 0xf1, 0x60, 0x7d, 0x5c, 0xe5, 0x8b, 0xc4, 0x30,
0x2a, 0xef, 0x4f, 0x6d, 0x0f, 0x15, 0x82, 0x1b, 0x03, 0x4a, 0xdf, 0x2a, 0x65, 0x51, 0xe2, 0x72,
0x14, 0xca, 0xdf, 0x5a, 0x24, 0x30, 0x2c, 0x6c, 0xa7, 0xfd, 0x1e, 0xd7, 0x24, 0x0e, 0xb6, 0x43,
0xf5, 0xfd, 0x69, 0x1d, 0x82, 0xf4, 0x28, 0x5c, 0x71, 0x2e, 0xf0, 0xfa, 0x2e, 0xa0, 0x94, 0x32,
0x10, 0x73, 0xb5, 0x08, 0x84, 0xe4, 0x2a, 0x80, 0x2c, 0x8b, 0x79, 0x51, 0x2f, 0x2b, 0x5a, 0x69,
0xa1, 0xf2, 0xaa, 0xf5, 0x6e, 0x72, 0x3d, 0x10, 0x28, 0x83, 0x19, 0x6d, 0xb5, 0x45, 0x87, 0x23,
0xe4, 0xfe, 0x7d, 0x32, 0x70, 0x9a, 0x42, 0x97, 0x09, 0x5c, 0x1c, 0x85, 0x04, 0x7b, 0x79, 0x7c,
0x0f, 0xc6, 0xfd, 0x12, 0x21, 0xc6, 0x2e, 0x88, 0x85, 0x2d, 0x1a, 0x20, 0x21, 0x84, 0x84, 0x0b,
0xe2, 0xdd, 0xe6, 0xae, 0x4d, 0xc2, 0x13, 0x4e, 0x5d, 0x1e, 0xe3, 0xf8, 0xf6, 0xc7, 0x19, 0xa5,
0x1e, 0x3e, 0xbf, 0x94, 0x8b, 0xcb, 0x80, 0x2f, 0x21, 0x81, 0xfe, 0x5b, 0xe5, 0x45, 0xe4, 0xca,
0xcd, 0x38, 0x1f, 0x2a, 0x9e, 0x91, 0x8f, 0x2a, 0x7f, 0xbc, 0xdf, 0xfd, 0x7c, 0x83, 0x9c, 0xc2,
0xdd, 0xcf, 0x83, 0x07, 0x7a, 0x7f, 0x1a, 0xe7, 0x94, 0x73, 0x20, 0xd7, 0x32, 0x03, 0x16, 0x22,
0x2a, 0x70, 0xf1, 0xe3, 0x59, 0x09, 0x9d, 0x79, 0xc9, 0x0a, 0xfe, 0xd9, 0x16, 0xd1, 0xfb, 0xff,
0x17, 0xff, 0x4f, 0xca, 0x85, 0x09, 0x12, 0xca, 0x43, 0xaf, 0xf1, 0x3d, 0xbd, 0x52, 0xf1, 0x09,
0x8e, 0xe9, 0x8f, 0xbf, 0x47, 0x17, 0xf9, 0xce, 0xf3, 0xfd, 0x17, 0x1f, 0x95, 0x5d, 0xbc, 0xff,
0x0d, 0xf9, 0x0b, 0x5c, 0xd5, 0x96, 0x6f, 0x9c, 0x08, 0x00, 0x00
};
// Autogenerated from wled00/data/liveview.htm, do not edit!!
const uint16_t PAGE_liveview_length = 547;
const uint16_t PAGE_liveview_length = 962;
const uint8_t PAGE_liveview[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x95, 0x53, 0x4d, 0x6f, 0xdb, 0x30,
0x0c, 0xbd, 0xe7, 0x57, 0x78, 0x2a, 0x5a, 0x48, 0x88, 0x63, 0x3b, 0xc5, 0xba, 0x8f, 0xf8, 0xe3,
0xb0, 0xb5, 0x87, 0x02, 0x05, 0xd6, 0x43, 0x81, 0x61, 0x18, 0x76, 0x50, 0x24, 0xc6, 0xd6, 0x2a,
0x4b, 0x81, 0x4c, 0xb9, 0x08, 0xd2, 0xfc, 0xf7, 0xc9, 0x76, 0xd2, 0x62, 0xc0, 0x30, 0xa0, 0x3e,
0xc8, 0x94, 0xf8, 0xf4, 0x48, 0x3e, 0x52, 0xc5, 0xbb, 0xeb, 0x6f, 0x5f, 0x1f, 0x7e, 0xdc, 0xdf,
0x44, 0x0d, 0xb6, 0xba, 0x2a, 0x8e, 0x2b, 0x70, 0x59, 0x15, 0x2d, 0x20, 0x8f, 0x0c, 0x6f, 0xa1,
0x24, 0xbd, 0x82, 0xa7, 0xad, 0x75, 0x48, 0xa2, 0x99, 0xb0, 0x06, 0xc1, 0x60, 0x49, 0x9e, 0x94,
0xc4, 0xa6, 0x94, 0xd0, 0x2b, 0x01, 0x8b, 0x71, 0x13, 0x2b, 0xa3, 0x50, 0x71, 0xbd, 0xe8, 0x04,
0xd7, 0x50, 0x2e, 0xe3, 0x36, 0x1c, 0xb4, 0xbe, 0x3d, 0xed, 0xc9, 0x91, 0x73, 0x26, 0x1a, 0xee,
0x3a, 0x08, 0x1c, 0x1e, 0x37, 0x8b, 0x4f, 0xe4, 0xaf, 0x50, 0xd8, 0x40, 0x0b, 0x0b, 0x61, 0xb5,
0x75, 0x24, 0x7a, 0x09, 0x76, 0x76, 0x39, 0x7e, 0x01, 0x8a, 0x0a, 0x35, 0x54, 0xb3, 0xef, 0x77,
0x37, 0xd7, 0xd1, 0x9d, 0xea, 0x21, 0xba, 0x77, 0x30, 0xa4, 0x57, 0xa4, 0x93, 0xa7, 0xe8, 0x70,
0x37, 0x00, 0xd6, 0x56, 0xee, 0xf6, 0x2d, 0x77, 0xb5, 0x32, 0xab, 0xec, 0x70, 0x26, 0xb8, 0xe9,
0xf7, 0x6b, 0x2e, 0x1e, 0x6b, 0x67, 0xbd, 0x91, 0xab, 0xb3, 0x2c, 0xcb, 0xf2, 0x8d, 0xd2, 0x08,
0x6e, 0xb5, 0x76, 0xaa, 0x6e, 0xd0, 0x40, 0xd7, 0xd1, 0xe5, 0xc7, 0xab, 0x73, 0x96, 0x8f, 0xd5,
0xac, 0x96, 0x59, 0x76, 0x9e, 0x37, 0x30, 0xf8, 0x26, 0x7b, 0x6b, 0xbb, 0x50, 0x9f, 0x35, 0x2b,
0xbe, 0xee, 0xac, 0xf6, 0x08, 0x87, 0x59, 0x91, 0x4e, 0xe1, 0x8a, 0x74, 0xd2, 0x6c, 0x88, 0x5a,
0x15, 0x52, 0xf5, 0x91, 0x92, 0x25, 0x19, 0x82, 0x86, 0x94, 0x3b, 0xe1, 0xd4, 0x16, 0xab, 0x99,
0xdf, 0x4a, 0x8e, 0x40, 0x59, 0xde, 0x73, 0x17, 0x61, 0x6b, 0x3d, 0x96, 0xc6, 0x6b, 0x9d, 0x6f,
0xbc, 0x11, 0x03, 0x6f, 0x74, 0x02, 0xec, 0xd5, 0x86, 0x4a, 0x2b, 0x7c, 0x1b, 0x6a, 0x4f, 0x1a,
0x25, 0x25, 0x18, 0xe6, 0x00, 0xbd, 0x33, 0x91, 0xd0, 0xc0, 0xdd, 0x83, 0x6a, 0x21, 0xdc, 0xa6,
0x23, 0x07, 0x8b, 0x7b, 0xab, 0xe4, 0x64, 0x97, 0x41, 0xd5, 0x93, 0x73, 0x22, 0x8b, 0x2f, 0xaf,
0x32, 0xc6, 0xf2, 0x0d, 0xa0, 0x68, 0x28, 0x49, 0x7f, 0x77, 0xd6, 0xa4, 0x3a, 0xc8, 0x46, 0x58,
0x12, 0x94, 0x36, 0x14, 0xcb, 0x8a, 0x62, 0x62, 0x1f, 0x9f, 0x9f, 0xe9, 0xbf, 0xa8, 0xff, 0xc3,
0x1a, 0x68, 0x63, 0x4c, 0x06, 0x42, 0xca, 0xd8, 0x2b, 0xdb, 0x7e, 0x28, 0x2e, 0xf4, 0x51, 0x2b,
0x13, 0xe8, 0x16, 0xb5, 0xe3, 0x52, 0x85, 0x32, 0xe8, 0xe7, 0x4c, 0x42, 0x1d, 0x93, 0xd8, 0x97,
0x98, 0x68, 0x90, 0x5d, 0x58, 0x4c, 0x8d, 0x4d, 0xbe, 0xb1, 0x8e, 0xaa, 0x32, 0xcb, 0x55, 0xe1,
0x73, 0x35, 0x9f, 0xb3, 0xf1, 0xbe, 0x3d, 0x82, 0x7e, 0xaa, 0x5f, 0xb9, 0x3d, 0x22, 0xab, 0x0f,
0x17, 0x17, 0xd4, 0x96, 0x36, 0xe9, 0xfc, 0xba, 0x43, 0xa7, 0x4c, 0x4d, 0x2f, 0x43, 0x0a, 0x30,
0x0f, 0xd3, 0x41, 0xe6, 0x36, 0x0e, 0x04, 0x8b, 0x65, 0x80, 0x0c, 0x07, 0x31, 0x61, 0x87, 0xe1,
0xcf, 0x48, 0xfc, 0xa2, 0x63, 0x0d, 0x78, 0xa3, 0x61, 0x30, 0xbf, 0xec, 0x6e, 0x25, 0x9d, 0x9a,
0xc3, 0x92, 0xb1, 0x7f, 0xc9, 0xeb, 0x64, 0x94, 0x10, 0xbf, 0x45, 0x88, 0xf7, 0x19, 0x3b, 0xb0,
0x44, 0xf0, 0x41, 0x5e, 0x7a, 0x6a, 0x24, 0x45, 0xb6, 0x7f, 0xb3, 0x9a, 0x07, 0xc6, 0xc6, 0x79,
0x9a, 0x86, 0xa5, 0x48, 0xa7, 0x51, 0x4a, 0xc7, 0x17, 0xf9, 0x07, 0xe3, 0xed, 0x60, 0x85, 0xa7,
0x03, 0x00, 0x00
};
// Autogenerated from wled00/data/liveviewws.htm, do not edit!!
const uint16_t PAGE_liveviewws_length = 711;
const uint8_t PAGE_liveviewws[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x6d, 0x54, 0x5d, 0x8f, 0x9b, 0x3a,
0x10, 0x7d, 0xcf, 0xaf, 0xa0, 0xde, 0xdb, 0x2d, 0x56, 0x08, 0x24, 0xdb, 0xdb, 0x2f, 0xc0, 0x59,
0xb5, 0xb7, 0x79, 0xa8, 0xb4, 0x6a, 0x57, 0xda, 0x5e, 0xad, 0xaa, 0x55, 0xa4, 0x1a, 0x98, 0x80,
0xef, 0x82, 0x1d, 0xd9, 0x43, 0x50, 0x84, 0xf8, 0xef, 0x77, 0x20, 0xdb, 0xac, 0x2a, 0x95, 0x07,
0xdb, 0xc3, 0x9c, 0x99, 0x33, 0x33, 0x3e, 0x90, 0xbe, 0xf8, 0xfc, 0xed, 0x9f, 0xef, 0x3f, 0x6e,
0x37, 0x5e, 0x85, 0x4d, 0xbd, 0x4e, 0x9f, 0x56, 0x90, 0xc5, 0x3a, 0x6d, 0x00, 0xa5, 0xa7, 0x65,
0x03, 0x82, 0x1d, 0x14, 0x74, 0x7b, 0x63, 0x91, 0x79, 0xb3, 0xdc, 0x68, 0x04, 0x8d, 0x82, 0x75,
0xaa, 0xc0, 0x4a, 0x14, 0x70, 0x50, 0x39, 0x2c, 0x26, 0x23, 0x50, 0x5a, 0xa1, 0x92, 0xf5, 0xc2,
0xe5, 0xb2, 0x06, 0xb1, 0x0a, 0x1a, 0x7a, 0xd1, 0xb4, 0xcd, 0x2f, 0x9b, 0x3d, 0xe5, 0x9c, 0xe5,
0x95, 0xb4, 0x0e, 0x28, 0x47, 0x8b, 0xbb, 0xc5, 0x7b, 0xf6, 0x1b, 0x15, 0x56, 0xd0, 0xc0, 0x22,
0x37, 0xb5, 0xb1, 0xcc, 0x3b, 0x93, 0x5d, 0x5c, 0x4d, 0x0f, 0x41, 0x51, 0x61, 0x0d, 0xeb, 0xd9,
0xfd, 0xcd, 0xe6, 0xb3, 0x77, 0xa3, 0x0e, 0xe0, 0xdd, 0x5a, 0x18, 0xcb, 0x4b, 0xa3, 0x93, 0x27,
0x75, 0x78, 0x1c, 0x01, 0x99, 0x29, 0x8e, 0x7d, 0x23, 0x6d, 0xa9, 0x74, 0xbc, 0x1c, 0x2e, 0x72,
0xa9, 0x0f, 0x7d, 0x26, 0xf3, 0xc7, 0xd2, 0x9a, 0x56, 0x17, 0xf1, 0xc5, 0x72, 0xb9, 0x4c, 0x76,
0xaa, 0x46, 0xb0, 0x71, 0x66, 0x55, 0x59, 0xa1, 0x06, 0xe7, 0xfc, 0xd5, 0xbb, 0x37, 0x2f, 0x79,
0x32, 0x75, 0x13, 0xaf, 0x96, 0xcb, 0x97, 0x49, 0x05, 0xa3, 0xef, 0x74, 0xde, 0x1b, 0x47, 0xfd,
0x19, 0x1d, 0xcb, 0xcc, 0x99, 0xba, 0x45, 0x18, 0x66, 0x69, 0x74, 0xa2, 0x4b, 0xa3, 0xd3, 0xcc,
0x46, 0xd6, 0x75, 0x5a, 0xa8, 0x83, 0xa7, 0x0a, 0xc1, 0x46, 0x52, 0x2a, 0x39, 0x22, 0x9b, 0xea,
0xca, 0xad, 0xda, 0xe3, 0x7a, 0x76, 0x90, 0xd6, 0xeb, 0x5c, 0x82, 0xf6, 0xd8, 0x77, 0x4e, 0xa0,
0xd9, 0x87, 0x9d, 0xd2, 0x85, 0xe9, 0xc2, 0xce, 0x0d, 0xb9, 0xc4, 0xbc, 0xf2, 0x81, 0xf7, 0x43,
0xe7, 0x2e, 0x2f, 0x3b, 0x17, 0x5a, 0xca, 0x7a, 0xbc, 0x43, 0x89, 0x20, 0x84, 0xb8, 0x87, 0xec,
0xce, 0xe4, 0x8f, 0x80, 0xe1, 0xb7, 0xdb, 0xcd, 0xd7, 0x6b, 0x72, 0x3b, 0xd0, 0x85, 0xcf, 0xfa,
0x57, 0xf5, 0xe1, 0x55, 0x8c, 0xb6, 0x85, 0x81, 0xf1, 0xd8, 0xa7, 0xa1, 0x51, 0x79, 0x10, 0x2a,
0xbd, 0x33, 0x3e, 0xbb, 0x05, 0x78, 0xf4, 0xee, 0xef, 0x3c, 0xb3, 0x07, 0xad, 0x74, 0xc9, 0x78,
0xe0, 0x13, 0xad, 0x86, 0xce, 0x3b, 0xa7, 0xf3, 0x7d, 0x56, 0x21, 0xee, 0x5d, 0xcc, 0x84, 0x78,
0xaa, 0xa5, 0x36, 0x54, 0x0a, 0xb5, 0x1a, 0xee, 0xad, 0x41, 0x43, 0xb7, 0x71, 0xcd, 0x3a, 0xe7,
0x58, 0x4c, 0x2b, 0xe3, 0x73, 0x16, 0x47, 0x11, 0x9b, 0x17, 0x26, 0x6f, 0x1b, 0xba, 0x9d, 0x67,
0x70, 0x65, 0x1c, 0xce, 0x59, 0x34, 0x62, 0x78, 0x68, 0xf4, 0x48, 0x29, 0x76, 0xad, 0xce, 0x47,
0xa7, 0xcf, 0xfb, 0x3f, 0x17, 0x3c, 0xf0, 0x80, 0x1c, 0x99, 0xd2, 0xd2, 0x1e, 0xbf, 0x1f, 0xf7,
0x24, 0x01, 0x69, 0xad, 0x3c, 0x66, 0xed, 0x6e, 0x07, 0x96, 0x8d, 0x3e, 0x59, 0x14, 0x9b, 0x03,
0x11, 0xdd, 0x28, 0x47, 0x6a, 0x00, 0xeb, 0xb3, 0x86, 0xee, 0x4a, 0x96, 0xc0, 0x02, 0x10, 0xeb,
0x7e, 0x9c, 0xa4, 0xda, 0xf9, 0xec, 0xc1, 0x64, 0xff, 0x41, 0x8e, 0xde, 0xc7, 0x31, 0xfc, 0xd3,
0x14, 0xbe, 0xa5, 0x8e, 0x68, 0xc4, 0x77, 0x68, 0xa9, 0xf5, 0x90, 0x54, 0x58, 0xfb, 0x10, 0x16,
0x12, 0x25, 0xe7, 0x7d, 0x0d, 0xe8, 0xc1, 0x34, 0x88, 0x7f, 0x95, 0xc6, 0xf7, 0x53, 0x94, 0x0f,
0x23, 0xcf, 0x09, 0x91, 0x50, 0xce, 0x77, 0x6f, 0x5f, 0x08, 0x78, 0x58, 0x6e, 0xb9, 0x05, 0x6c,
0xad, 0x4e, 0xc6, 0x18, 0x52, 0x63, 0xad, 0x34, 0x48, 0xbb, 0x28, 0xad, 0x2c, 0x14, 0xe1, 0xfd,
0x0f, 0xcb, 0x02, 0xca, 0x80, 0x05, 0x5a, 0x40, 0x58, 0x83, 0x2e, 0xe9, 0x53, 0xb0, 0xe2, 0x4a,
0x50, 0xe8, 0x6a, 0x7b, 0xfd, 0x77, 0x7c, 0x95, 0xec, 0x8c, 0xf5, 0x95, 0xb0, 0x89, 0x4a, 0x75,
0xa2, 0xe6, 0xe2, 0x35, 0xc7, 0xb9, 0xf8, 0x69, 0xcb, 0xcc, 0xff, 0xab, 0x87, 0x07, 0xb5, 0x1d,
0x82, 0x69, 0x9f, 0xaf, 0xce, 0xa7, 0xab, 0xed, 0xc0, 0x7f, 0x06, 0x04, 0x5f, 0xbc, 0xbe, 0xbc,
0xf4, 0x09, 0xcd, 0x02, 0xc6, 0x93, 0x71, 0xe7, 0x2c, 0x38, 0x0f, 0xbe, 0x04, 0xdc, 0xd4, 0x30,
0x1e, 0x3f, 0x1d, 0xbf, 0xd0, 0x5c, 0x27, 0xbd, 0xf1, 0x70, 0x92, 0x64, 0xf8, 0x2c, 0x76, 0x81,
0xc3, 0xb3, 0xb2, 0x7e, 0xa9, 0x03, 0xac, 0xa5, 0xa2, 0xce, 0xf2, 0x98, 0xcc, 0x98, 0x06, 0xca,
0x87, 0x81, 0x8f, 0xb2, 0x3e, 0xa9, 0x35, 0x8d, 0x4e, 0x8a, 0x8e, 0xa6, 0x1f, 0xc3, 0xff, 0x16,
0x7e, 0x9e, 0x8e, 0x2e, 0x04, 0x00, 0x00
};
// Autogenerated from wled00/data/liveviewws2D.htm, do not edit!!
const uint16_t PAGE_liveviewws2D_length = 818;
const uint8_t PAGE_liveviewws2D[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x6d, 0x54, 0x6d, 0x6f, 0xdb, 0x36,
0x10, 0xfe, 0xee, 0x5f, 0xa1, 0x70, 0x43, 0x2b, 0xda, 0xb2, 0xe4, 0xb8, 0xed, 0x96, 0xd9, 0xa2,
0x8b, 0x36, 0x35, 0xb0, 0x02, 0xd9, 0x6a, 0xc0, 0x19, 0x82, 0x21, 0x30, 0x50, 0x5a, 0x3a, 0x5b,
0x5c, 0x25, 0xd2, 0xa0, 0xce, 0x96, 0x35, 0x47, 0xff, 0x7d, 0x47, 0xc9, 0xf1, 0x32, 0x74, 0xfa,
0x40, 0x91, 0xf7, 0xf2, 0xdc, 0xf1, 0xb9, 0x3b, 0xc6, 0x57, 0x9f, 0xbe, 0xdc, 0xde, 0xff, 0xb9,
0x98, 0x7b, 0x19, 0x16, 0xf9, 0x2c, 0x3e, 0xaf, 0x20, 0xd3, 0x59, 0x5c, 0x00, 0x4a, 0x4f, 0xcb,
0x02, 0x04, 0x3b, 0x28, 0xa8, 0x76, 0xc6, 0x22, 0xf3, 0x7a, 0x89, 0xd1, 0x08, 0x1a, 0x05, 0xab,
0x54, 0x8a, 0x99, 0x48, 0xe1, 0xa0, 0x12, 0x18, 0xb6, 0x87, 0x40, 0x69, 0x85, 0x4a, 0xe6, 0xc3,
0x32, 0x91, 0x39, 0x88, 0xeb, 0xa0, 0x20, 0x41, 0xb1, 0x2f, 0x9e, 0xcf, 0xec, 0x8c, 0xd9, 0x4b,
0x32, 0x69, 0x4b, 0x20, 0x8c, 0x3d, 0x6e, 0x86, 0x37, 0xec, 0x3f, 0xa1, 0x30, 0x83, 0x02, 0x86,
0x89, 0xc9, 0x8d, 0x65, 0xde, 0x25, 0xd8, 0x0f, 0xe3, 0xf6, 0x23, 0x53, 0x54, 0x98, 0xc3, 0xac,
0xf7, 0x70, 0x37, 0xff, 0xe4, 0xdd, 0xa9, 0x03, 0x78, 0x0b, 0x0b, 0x2e, 0xbd, 0x38, 0xea, 0x34,
0x71, 0x89, 0x35, 0xfd, 0xd6, 0x26, 0xad, 0x4f, 0x85, 0xb4, 0x5b, 0xa5, 0x27, 0xa3, 0x26, 0x8e,
0x3a, 0x69, 0x1c, 0x75, 0x57, 0x73, 0xda, 0x59, 0x9c, 0x48, 0x7d, 0x90, 0xa5, 0xd7, 0x53, 0xa9,
0x60, 0x6e, 0x4f, 0xe8, 0x51, 0x27, 0x23, 0x94, 0xc4, 0xaa, 0x1d, 0xce, 0x7a, 0x07, 0x69, 0xbd,
0x44, 0xa4, 0x26, 0xd9, 0x17, 0x94, 0x48, 0xb8, 0x05, 0x9c, 0xe7, 0xe0, 0xb6, 0x1f, 0xeb, 0xcf,
0xa9, 0xdf, 0xb9, 0xf1, 0x20, 0x87, 0xb4, 0x14, 0x8c, 0x05, 0x98, 0x59, 0x83, 0x94, 0x45, 0x2a,
0xae, 0xae, 0xa7, 0x9b, 0xbd, 0x4e, 0x50, 0x19, 0xed, 0xd1, 0x55, 0x6f, 0x5b, 0x58, 0x9f, 0x9f,
0x92, 0xb0, 0xe3, 0x2d, 0xfc, 0xe5, 0xa6, 0x5f, 0x29, 0x9d, 0x9a, 0x2a, 0x54, 0x5a, 0x83, 0x7d,
0x68, 0x09, 0x4c, 0xc2, 0x0c, 0xd4, 0x36, 0xc3, 0xef, 0xd4, 0xbf, 0xb6, 0xe2, 0xe6, 0x05, 0xd2,
0xb4, 0xcd, 0x0c, 0x8f, 0x22, 0x71, 0x49, 0xdd, 0x3a, 0xa2, 0x8e, 0xe8, 0xb3, 0x71, 0xca, 0xf8,
0x54, 0x6d, 0x7c, 0xd2, 0xf0, 0x93, 0x33, 0xa9, 0xca, 0x29, 0xda, 0xfa, 0x54, 0x95, 0x02, 0xcd,
0x2e, 0x3c, 0x63, 0x56, 0x65, 0x93, 0x48, 0x4c, 0x32, 0x1f, 0xf9, 0xa9, 0xa9, 0xca, 0x57, 0xaf,
0xaa, 0x32, 0xb4, 0xc4, 0x4c, 0xbd, 0x44, 0x89, 0x20, 0x84, 0x78, 0x80, 0xf5, 0xd2, 0x24, 0xdf,
0x00, 0xc3, 0x2f, 0x8b, 0xf9, 0xef, 0xef, 0x49, 0x5d, 0x82, 0xa6, 0xfb, 0x9e, 0x5e, 0xe7, 0x87,
0xd7, 0x13, 0xb4, 0x7b, 0x68, 0x18, 0x9f, 0xf8, 0x84, 0xaa, 0xa1, 0xf2, 0x2e, 0xd6, 0xbe, 0xcf,
0x32, 0xc4, 0x5d, 0x39, 0x61, 0x42, 0x9c, 0x43, 0xe5, 0x86, 0x22, 0x11, 0x0b, 0xe1, 0x8e, 0xa8,
0x31, 0x54, 0xd7, 0xf7, 0xac, 0x2a, 0x4b, 0x36, 0xa1, 0x95, 0xf1, 0x01, 0x9b, 0x44, 0x11, 0x1b,
0x5c, 0xe8, 0xbd, 0x18, 0x67, 0xa6, 0xc4, 0x01, 0x8b, 0x9c, 0x0d, 0x0f, 0x8d, 0x36, 0x3b, 0xd0,
0xc2, 0xe7, 0x62, 0x76, 0xfa, 0xff, 0x4c, 0x9a, 0x80, 0xe4, 0x6b, 0xa5, 0xa5, 0xad, 0xef, 0xeb,
0x1d, 0x75, 0x91, 0xb4, 0x56, 0xd6, 0xeb, 0xfd, 0x66, 0x03, 0x96, 0x39, 0x9d, 0x4c, 0xd3, 0xf9,
0x81, 0x22, 0xdc, 0xa9, 0x92, 0x1a, 0x0a, 0xac, 0xcf, 0x0a, 0x28, 0x4b, 0xb9, 0x05, 0xaa, 0x19,
0xa1, 0x3a, 0x86, 0x88, 0x34, 0xf6, 0x68, 0xd6, 0x7f, 0x41, 0x82, 0xde, 0x07, 0xe7, 0xfe, 0xb1,
0x75, 0x5f, 0xd1, 0x55, 0x88, 0xba, 0x25, 0x5a, 0xa5, 0xb7, 0x21, 0x35, 0x72, 0xee, 0x63, 0x98,
0x4a, 0x94, 0x9c, 0x9f, 0x72, 0x40, 0x0f, 0x5b, 0x06, 0xfe, 0x50, 0x1a, 0x6f, 0x5a, 0x2f, 0x1f,
0x5c, 0x9c, 0xce, 0xc2, 0x15, 0xe2, 0xe7, 0x9f, 0xae, 0x04, 0x3e, 0x8e, 0x56, 0x4f, 0x4f, 0x63,
0xb7, 0xb9, 0xa6, 0xcd, 0x95, 0x2b, 0x8e, 0x05, 0xdc, 0x5b, 0x3d, 0x75, 0x10, 0x96, 0xe4, 0xe3,
0x55, 0xa0, 0xe9, 0xf7, 0x66, 0x15, 0x18, 0xf1, 0x9b, 0xc4, 0x2c, 0xa4, 0xd9, 0xf1, 0xcf, 0xcd,
0x12, 0xd9, 0x4b, 0x63, 0x44, 0x9a, 0x07, 0xb2, 0x33, 0xd8, 0xe4, 0xc6, 0x58, 0xff, 0xd9, 0x66,
0x68, 0xfa, 0x96, 0x47, 0xe3, 0xae, 0x2d, 0x40, 0xbc, 0x9d, 0x6e, 0x48, 0x59, 0x8b, 0xf0, 0xdd,
0xb4, 0x8e, 0xf5, 0xb4, 0x1e, 0x0c, 0xb8, 0x13, 0x1c, 0x9d, 0xe0, 0x18, 0xdb, 0xe9, 0x91, 0x04,
0x94, 0x45, 0xb8, 0x51, 0x79, 0xbe, 0x74, 0xa3, 0x21, 0xbe, 0xda, 0xed, 0xda, 0xff, 0xf1, 0x84,
0x8f, 0xb0, 0x6a, 0x82, 0xf6, 0x3f, 0xb8, 0xbe, 0xec, 0xc6, 0xab, 0x86, 0x7f, 0x0d, 0x9c, 0xc3,
0x1a, 0x68, 0xa2, 0x16, 0x14, 0xdf, 0xe7, 0xed, 0x59, 0xda, 0xc4, 0x3f, 0xf6, 0xcd, 0x40, 0x06,
0x75, 0xdf, 0x04, 0xe1, 0x5b, 0x5a, 0x46, 0xc1, 0xb8, 0xdf, 0x66, 0xb8, 0xf8, 0xdc, 0xd9, 0xb8,
0x20, 0x64, 0x0e, 0x03, 0xf1, 0xa6, 0xf9, 0xb7, 0xef, 0x68, 0xb6, 0x4b, 0x93, 0x43, 0x08, 0xd6,
0x52, 0x66, 0x6c, 0x01, 0xf0, 0xcd, 0x7b, 0x58, 0x7a, 0xed, 0x71, 0x42, 0x65, 0xe1, 0x4d, 0xc3,
0x9b, 0x73, 0x13, 0x7d, 0x5f, 0x3e, 0x0b, 0xa5, 0xfa, 0xfb, 0xb9, 0x7a, 0xcf, 0x53, 0xf7, 0xf4,
0xe4, 0xbf, 0x98, 0x90, 0x97, 0xd3, 0x38, 0x0a, 0x48, 0x71, 0xaf, 0x0a, 0x30, 0x7b, 0x6a, 0x52,
0xfe, 0xd2, 0x89, 0x46, 0xb5, 0x09, 0xc6, 0xef, 0x46, 0x9c, 0x37, 0xbc, 0x47, 0x0f, 0x45, 0x37,
0xf8, 0x71, 0xd4, 0xbd, 0x11, 0x51, 0xfb, 0x22, 0xfe, 0x03, 0x69, 0xb3, 0xce, 0x95, 0x27, 0x05,
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x9d, 0x55, 0x6d, 0x6f, 0xdb, 0x36,
0x10, 0xfe, 0x9e, 0x5f, 0xa1, 0x32, 0x6b, 0x4a, 0xc2, 0xb2, 0x2c, 0xbb, 0xaf, 0xb3, 0x4c, 0x07,
0xed, 0x9a, 0x0f, 0x03, 0x82, 0x25, 0x40, 0x32, 0x04, 0x43, 0x10, 0xa0, 0xb4, 0x74, 0xb6, 0xd8,
0x50, 0xa4, 0x40, 0x52, 0xf6, 0x0c, 0x55, 0xff, 0x7d, 0x47, 0xc9, 0x71, 0xb0, 0xa1, 0xd8, 0xd0,
0xf9, 0x83, 0x2c, 0x92, 0x77, 0xc7, 0xbb, 0xe7, 0xb9, 0xe7, 0xb4, 0x78, 0xf1, 0xf9, 0xea, 0x97,
0xdb, 0x3f, 0xae, 0x2f, 0xa2, 0xd2, 0x57, 0x6a, 0xb9, 0x38, 0x3c, 0x41, 0x14, 0xcb, 0x45, 0x05,
0x5e, 0x44, 0x5a, 0x54, 0xc0, 0xc9, 0x56, 0xc2, 0xae, 0x36, 0xd6, 0x93, 0xe8, 0x24, 0x37, 0xda,
0x83, 0xf6, 0x9c, 0xec, 0x64, 0xe1, 0x4b, 0x5e, 0xc0, 0x56, 0xe6, 0x30, 0xee, 0x17, 0xb1, 0xd4,
0xd2, 0x4b, 0xa1, 0xc6, 0x2e, 0x17, 0x0a, 0xf8, 0x34, 0xae, 0x70, 0xa3, 0x6a, 0xaa, 0xa7, 0x35,
0x39, 0xc4, 0x3c, 0xc9, 0x4b, 0x61, 0x1d, 0x60, 0x8c, 0xc6, 0xaf, 0xc7, 0x1f, 0xc8, 0xdf, 0xae,
0xf2, 0x25, 0x54, 0x30, 0xce, 0x8d, 0x32, 0x96, 0x44, 0xc7, 0xcb, 0x4e, 0x67, 0xfd, 0x0f, 0x4d,
0xbd, 0xf4, 0x0a, 0x96, 0x27, 0x77, 0x97, 0x17, 0x9f, 0xa3, 0x4b, 0xb9, 0x85, 0xe8, 0xda, 0x42,
0x48, 0x6f, 0x31, 0x19, 0x4e, 0x16, 0xce, 0xef, 0x83, 0xc1, 0xca, 0x14, 0xfb, 0xb6, 0x12, 0x76,
0x23, 0xf5, 0x3c, 0xed, 0x4e, 0x73, 0xa1, 0xb7, 0xed, 0x4a, 0xe4, 0x8f, 0x1b, 0x6b, 0x1a, 0x5d,
0xcc, 0x4f, 0xd3, 0x34, 0xcd, 0xd6, 0x52, 0x79, 0xb0, 0xf3, 0x95, 0x95, 0x9b, 0xd2, 0x6b, 0x70,
0x8e, 0x4e, 0xdf, 0xbf, 0x7d, 0xc9, 0xb2, 0xbe, 0x9a, 0xf9, 0x34, 0x4d, 0x5f, 0x66, 0x25, 0x84,
0xb3, 0xe1, 0xbd, 0x36, 0x0e, 0xeb, 0x33, 0x7a, 0x2e, 0x56, 0xce, 0xa8, 0xc6, 0x43, 0x77, 0xb2,
0x98, 0x0c, 0xd7, 0x2d, 0x5c, 0x6e, 0x65, 0xed, 0x97, 0x27, 0x5b, 0x61, 0xa3, 0x9d, 0x8b, 0x7d,
0x65, 0x1a, 0xcf, 0x75, 0xa3, 0x54, 0xb6, 0x6e, 0x74, 0x1e, 0xbc, 0xa2, 0xa6, 0x2e, 0x84, 0x07,
0xca, 0x5a, 0xb9, 0xa6, 0x85, 0xc9, 0x9b, 0x0a, 0x2b, 0x4b, 0x4a, 0x59, 0x14, 0xa0, 0x99, 0x05,
0xdf, 0x58, 0x1d, 0xe5, 0x0a, 0x84, 0xbd, 0x95, 0x15, 0xa0, 0x37, 0xed, 0x63, 0xb0, 0x78, 0x6b,
0x64, 0x31, 0xbc, 0x73, 0xc4, 0xec, 0xe9, 0x70, 0x08, 0x16, 0xcf, 0xde, 0xa6, 0x8c, 0x65, 0x6b,
0xf0, 0x79, 0x49, 0x49, 0x32, 0xf9, 0xea, 0x8c, 0x9e, 0x28, 0x44, 0x85, 0xb0, 0x04, 0x81, 0xd4,
0x14, 0xf8, 0x92, 0x42, 0x62, 0x1e, 0xbf, 0x7d, 0xa3, 0xdf, 0x8b, 0xfd, 0x2f, 0x61, 0x31, 0x6e,
0x0c, 0x49, 0x08, 0x48, 0x19, 0x7b, 0x8e, 0xd6, 0x86, 0x02, 0x91, 0x10, 0x25, 0x35, 0x86, 0x1b,
0x6f, 0xac, 0x28, 0x24, 0xd6, 0x41, 0x7f, 0x4e, 0x0b, 0xd8, 0xc4, 0x24, 0xd6, 0x1c, 0x12, 0x05,
0x85, 0xc3, 0x87, 0xde, 0xf8, 0x32, 0x5b, 0x1b, 0x4b, 0x25, 0x4f, 0x33, 0xb9, 0xd0, 0x99, 0x1c,
0x8d, 0x58, 0xef, 0x6f, 0x0e, 0x46, 0xf7, 0xf2, 0x21, 0x33, 0x07, 0xcb, 0xe5, 0xbb, 0xb3, 0x33,
0x6a, 0xb8, 0x49, 0x5c, 0xb3, 0x72, 0xde, 0x4a, 0xbd, 0xa1, 0x33, 0x4c, 0xc1, 0x8f, 0x90, 0x7c,
0x32, 0x32, 0x31, 0x06, 0x18, 0x4f, 0xd1, 0x24, 0x6c, 0xc4, 0x84, 0x75, 0xe1, 0x9f, 0x91, 0xf8,
0x08, 0xe4, 0x06, 0xfc, 0x85, 0x82, 0xf0, 0xfa, 0x69, 0xff, 0x6b, 0x41, 0x49, 0x20, 0x1c, 0x51,
0xe8, 0xe9, 0x49, 0x9e, 0x89, 0xe7, 0x3e, 0xfe, 0x11, 0x20, 0xde, 0xa4, 0xac, 0x63, 0x49, 0x2e,
0x02, 0xbe, 0xf4, 0x89, 0x49, 0x0a, 0xac, 0xfd, 0x61, 0x34, 0x3b, 0xc6, 0xba, 0x63, 0x2b, 0xdc,
0x0c, 0x5d, 0xb0, 0x93, 0xba, 0x30, 0xbb, 0x44, 0x19, 0xbc, 0x00, 0xb7, 0x93, 0xd2, 0xc2, 0x3a,
0xc1, 0x3d, 0xf8, 0xf3, 0x6a, 0x4d, 0xc9, 0xf9, 0xce, 0x11, 0xb6, 0x4c, 0x59, 0xeb, 0xed, 0xbe,
0xdd, 0x39, 0xee, 0x4d, 0x9d, 0x1c, 0x3c, 0x76, 0xae, 0x1b, 0x72, 0xc2, 0x4c, 0xba, 0x10, 0xc8,
0x9d, 0x9d, 0xed, 0x5c, 0x62, 0x51, 0xb7, 0xfb, 0x1b, 0x8f, 0x77, 0x72, 0xce, 0xef, 0x60, 0x75,
0x63, 0xf2, 0x47, 0xf0, 0xc9, 0xd5, 0xf5, 0xc5, 0x6f, 0x0c, 0x8f, 0x1d, 0x68, 0x04, 0xa6, 0x7d,
0xa5, 0xb6, 0xaf, 0xe6, 0xde, 0x36, 0xd0, 0x11, 0x96, 0x81, 0x72, 0xd0, 0x2a, 0xf0, 0x11, 0xf0,
0x7f, 0x64, 0x13, 0x7b, 0x64, 0xa9, 0x16, 0xbe, 0x0c, 0xba, 0x44, 0x5e, 0x7d, 0xe2, 0x14, 0x4a,
0x9d, 0x4e, 0x63, 0x9f, 0x60, 0x20, 0x77, 0x27, 0x3d, 0xf6, 0xdc, 0x84, 0xb0, 0xf3, 0xf1, 0x74,
0x1e, 0x9a, 0x34, 0x4a, 0x11, 0xee, 0x5a, 0x49, 0xdf, 0xef, 0xc6, 0x12, 0xdd, 0x0d, 0x2a, 0x4b,
0x6a, 0xcc, 0xab, 0x56, 0x02, 0x5d, 0x49, 0xe9, 0x7d, 0x8d, 0xec, 0x85, 0xc2, 0x32, 0xfd, 0x44,
0x7c, 0x60, 0x55, 0x22, 0x9b, 0x13, 0x32, 0xd2, 0xf7, 0xe9, 0x03, 0x8b, 0xb1, 0x1c, 0xae, 0x61,
0x17, 0x1d, 0x2b, 0xc0, 0x63, 0x32, 0x09, 0x4e, 0x2c, 0x31, 0xda, 0xd4, 0xa0, 0xf9, 0x91, 0x0a,
0xd6, 0x7e, 0xbf, 0xb0, 0xae, 0xc3, 0xfd, 0x95, 0xd4, 0xc2, 0xee, 0x6f, 0xf7, 0x35, 0xce, 0x15,
0x61, 0xad, 0xd8, 0xaf, 0x9a, 0xf5, 0x1a, 0x2c, 0x89, 0xf1, 0x4c, 0x14, 0xc5, 0xc5, 0x16, 0xbb,
0xe5, 0x52, 0x3a, 0x1c, 0x31, 0x60, 0x29, 0xa9, 0x70, 0x00, 0x88, 0x0d, 0x90, 0x38, 0xb4, 0x79,
0xc0, 0x1c, 0x81, 0x25, 0xf7, 0x66, 0xf5, 0x15, 0x72, 0x1f, 0x7d, 0x0c, 0xee, 0x9f, 0x7a, 0xf7,
0x07, 0x82, 0xe8, 0x7a, 0x73, 0xd3, 0x37, 0x29, 0xf6, 0x86, 0x52, 0x28, 0x31, 0x24, 0x5a, 0x30,
0x76, 0x40, 0x32, 0x24, 0xff, 0xbb, 0xd4, 0xfe, 0x43, 0xef, 0x45, 0x21, 0xdc, 0x33, 0x58, 0x64,
0x18, 0xf3, 0xfd, 0xbb, 0x17, 0x1c, 0x42, 0xa1, 0x83, 0xe8, 0xb3, 0xe0, 0xf3, 0x9f, 0x8a, 0x0a,
0x48, 0xc5, 0x86, 0xcf, 0x38, 0xba, 0x4e, 0x1f, 0xce, 0xdf, 0xcc, 0x67, 0x07, 0x69, 0x99, 0x83,
0xb4, 0xf8, 0x6b, 0x86, 0x92, 0xf8, 0x62, 0x37, 0x2b, 0xfa, 0x53, 0x0b, 0xa8, 0xac, 0x2e, 0xee,
0xff, 0x47, 0xd3, 0xe3, 0xdb, 0xec, 0xa1, 0x63, 0x5f, 0x7a, 0x21, 0xbd, 0x7e, 0x16, 0x52, 0xf6,
0xbf, 0x85, 0xd4, 0x3d, 0xf7, 0x20, 0xce, 0x69, 0x9c, 0x88, 0x90, 0x80, 0xb5, 0x98, 0x14, 0xb9,
0x06, 0x78, 0x8c, 0xee, 0x6e, 0xa2, 0x7e, 0x39, 0x47, 0x40, 0x91, 0x0e, 0xd6, 0x85, 0x56, 0x3b,
0x0e, 0xc1, 0x7e, 0x74, 0x0e, 0x33, 0x73, 0x31, 0x19, 0xbe, 0x37, 0x61, 0x62, 0x47, 0x46, 0x2b,
0x23, 0x0a, 0x4e, 0x50, 0x21, 0x38, 0xec, 0x0b, 0xb9, 0x8d, 0x24, 0xae, 0xfa, 0x1c, 0xd0, 0x10,
0xd7, 0xf8, 0x0c, 0x86, 0xc1, 0x2b, 0x7c, 0xab, 0xfe, 0x02, 0x51, 0x65, 0x4f, 0x41, 0xc1, 0x06,
0x00, 0x00
};
// Autogenerated from wled00/data/liveviewws2D.htm, do not edit!!
const uint16_t PAGE_liveviewws2D_length = 870;
const uint8_t PAGE_liveviewws2D[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x6d, 0x54, 0x6d, 0x6f, 0xdb, 0x36,
0x10, 0xfe, 0xee, 0x5f, 0xa1, 0x70, 0x43, 0x2a, 0xc6, 0xb2, 0x64, 0xbb, 0xed, 0x96, 0xc5, 0xa2,
0x87, 0x35, 0x0d, 0xb0, 0x02, 0xd9, 0x6a, 0x20, 0x19, 0x82, 0x21, 0x30, 0x50, 0x5a, 0x3a, 0x5b,
0x5c, 0x25, 0xd2, 0x20, 0xcf, 0x96, 0x35, 0x47, 0xff, 0x7d, 0x47, 0xc9, 0xc9, 0x32, 0x74, 0xfe,
0x20, 0x93, 0xf7, 0xf2, 0xdc, 0xdb, 0x73, 0x4c, 0xcf, 0x3e, 0x7e, 0xbe, 0xbe, 0xff, 0x73, 0x71,
0x13, 0x14, 0x58, 0x95, 0xf3, 0xf4, 0xf4, 0x05, 0x99, 0xcf, 0xd3, 0x0a, 0x50, 0x06, 0x5a, 0x56,
0x20, 0xd8, 0x5e, 0x41, 0xbd, 0x35, 0x16, 0x59, 0x30, 0xc8, 0x8c, 0x46, 0xd0, 0x28, 0x58, 0xad,
0x72, 0x2c, 0x44, 0x0e, 0x7b, 0x95, 0xc1, 0xa8, 0xbb, 0x44, 0x4a, 0x2b, 0x54, 0xb2, 0x1c, 0xb9,
0x4c, 0x96, 0x20, 0x26, 0x51, 0x45, 0x82, 0x6a, 0x57, 0x3d, 0xdf, 0xd9, 0x09, 0x73, 0x90, 0x15,
0xd2, 0x3a, 0x20, 0x8c, 0x1d, 0xae, 0x47, 0x97, 0xec, 0x3f, 0xa1, 0xb0, 0x80, 0x0a, 0x46, 0x99,
0x29, 0x8d, 0x65, 0xc1, 0x4b, 0xb0, 0xef, 0xa6, 0xdd, 0x8f, 0x4c, 0x51, 0x61, 0x09, 0xf3, 0xc1,
0xc3, 0xed, 0xcd, 0xc7, 0xe0, 0x56, 0xed, 0x21, 0x58, 0x58, 0xf0, 0xe9, 0xa5, 0x49, 0xaf, 0x49,
0x1d, 0x36, 0xf4, 0xb7, 0x32, 0x79, 0x73, 0xac, 0xa4, 0xdd, 0x28, 0x7d, 0x35, 0x6e, 0xd3, 0xa4,
0x97, 0xa6, 0x49, 0x5f, 0x9a, 0xd7, 0xce, 0xd3, 0x4c, 0xea, 0xbd, 0x74, 0xc1, 0x40, 0xe5, 0x82,
0xf9, 0x33, 0xa1, 0x27, 0xbd, 0x8c, 0x50, 0x32, 0xab, 0xb6, 0x38, 0x1f, 0xec, 0xa5, 0x0d, 0x32,
0x91, 0x9b, 0x6c, 0x57, 0x51, 0x22, 0xf1, 0x06, 0xf0, 0xa6, 0x04, 0x7f, 0xfc, 0xd0, 0x7c, 0xca,
0xc3, 0xde, 0x8d, 0x47, 0x25, 0xe4, 0x4e, 0x30, 0x16, 0x61, 0x61, 0x0d, 0x52, 0x16, 0xb9, 0x38,
0x9b, 0xcc, 0xd6, 0x3b, 0x9d, 0xa1, 0x32, 0x3a, 0xa0, 0x52, 0xaf, 0x3b, 0xd8, 0x90, 0x1f, 0xb3,
0xb8, 0xef, 0x5b, 0xfc, 0xd3, 0xe5, 0x45, 0xad, 0x74, 0x6e, 0xea, 0x58, 0x69, 0x0d, 0xf6, 0xa1,
0x6b, 0x60, 0x16, 0x17, 0xa0, 0x36, 0x05, 0x7e, 0xa3, 0xfe, 0xb5, 0x13, 0xb7, 0xaf, 0x90, 0x66,
0x5d, 0x66, 0x78, 0x10, 0x99, 0x4f, 0xea, 0xda, 0x37, 0xea, 0x80, 0x21, 0x9b, 0xe6, 0x8c, 0xcf,
0xd4, 0x3a, 0x24, 0x0d, 0x3f, 0x7a, 0x93, 0xda, 0xcd, 0xd0, 0x36, 0xc7, 0xda, 0x09, 0x34, 0xdb,
0xf8, 0x84, 0x59, 0xbb, 0x36, 0x93, 0x98, 0x15, 0x21, 0xf2, 0x63, 0x4b, 0xd6, 0xb5, 0x3b, 0x3f,
0xaf, 0x5d, 0x6c, 0xa9, 0x39, 0xcd, 0x1d, 0x4a, 0x04, 0x21, 0xc4, 0x03, 0xac, 0xee, 0x4c, 0xf6,
0x15, 0x30, 0xfe, 0xbc, 0xb8, 0xf9, 0x9d, 0x93, 0xda, 0x81, 0xa6, 0x92, 0x8f, 0x6f, 0xca, 0xfd,
0x9b, 0x2b, 0xb4, 0x3b, 0x68, 0x29, 0x14, 0x94, 0x0e, 0x8e, 0x25, 0x60, 0x80, 0xe2, 0x84, 0x5d,
0x1a, 0x82, 0xa6, 0xb2, 0x23, 0x10, 0x18, 0x6f, 0x25, 0x16, 0x7e, 0xae, 0x91, 0x15, 0x10, 0xbb,
0x92, 0xa8, 0x12, 0x4e, 0x22, 0x88, 0x09, 0xc8, 0x3d, 0x28, 0x2c, 0x42, 0x96, 0x30, 0xfe, 0xf3,
0x68, 0x72, 0xb5, 0x37, 0x2a, 0x0f, 0xc6, 0x3c, 0x76, 0xdb, 0x52, 0x61, 0x27, 0x8d, 0x34, 0xb9,
0x1b, 0xab, 0x68, 0x80, 0x94, 0xd7, 0xb6, 0x94, 0xe4, 0xca, 0x0a, 0xc4, 0x2d, 0x8b, 0x58, 0xed,
0x28, 0xb2, 0x8d, 0x4b, 0xd0, 0x1b, 0x2c, 0xe6, 0x93, 0xf3, 0xf3, 0x50, 0x0f, 0x05, 0x39, 0x0d,
0xed, 0xe3, 0x78, 0xc9, 0x23, 0x2a, 0x47, 0x68, 0xa8, 0x83, 0x97, 0x0a, 0x48, 0xcd, 0x12, 0xef,
0xc4, 0x63, 0xa3, 0xcd, 0x16, 0xb4, 0x08, 0xb9, 0x98, 0x1f, 0xff, 0xbf, 0xa4, 0xb6, 0x25, 0xf9,
0x4a, 0x69, 0x69, 0x9b, 0xfb, 0x66, 0x4b, 0x8c, 0x94, 0xd6, 0xca, 0x66, 0xb5, 0x5b, 0xaf, 0xc1,
0xb2, 0x88, 0x74, 0x32, 0xcf, 0x6f, 0xf6, 0xc4, 0x80, 0x5b, 0xe5, 0x88, 0x9c, 0x60, 0x43, 0x56,
0x81, 0x73, 0x72, 0x03, 0x34, 0x7f, 0x42, 0xf5, 0xdd, 0xa6, 0x96, 0xb2, 0x47, 0xb3, 0xfa, 0x0b,
0x32, 0x0c, 0x7e, 0xf1, 0xee, 0x1f, 0x3a, 0xf7, 0x25, 0xa3, 0xbe, 0xa2, 0xb9, 0x43, 0xab, 0xf4,
0x26, 0xa6, 0xa5, 0x28, 0x43, 0x8c, 0x73, 0x89, 0x92, 0xf3, 0x53, 0x0f, 0x7d, 0xda, 0x7f, 0x28,
0x8d, 0x97, 0x9d, 0x57, 0x08, 0x3e, 0x4e, 0x6f, 0xe1, 0x87, 0xfa, 0xe3, 0x0f, 0x67, 0x02, 0xa9,
0xc4, 0xa7, 0xa7, 0xa9, 0x3f, 0x4c, 0xe8, 0x70, 0xe6, 0x07, 0x6d, 0x01, 0x77, 0x56, 0xcf, 0x3c,
0x84, 0x25, 0xf9, 0x74, 0xe9, 0x9b, 0xf7, 0xf8, 0x76, 0x19, 0x49, 0xf1, 0x1b, 0x0d, 0x20, 0xa6,
0x3d, 0x0c, 0x4f, 0xc4, 0x4b, 0xec, 0x0b, 0xc9, 0x12, 0xcd, 0x23, 0xd5, 0x1b, 0xac, 0x4b, 0x63,
0x6c, 0xf8, 0x6c, 0x33, 0x92, 0x17, 0x96, 0x27, 0xd3, 0x9e, 0x62, 0x20, 0xde, 0xcd, 0xd6, 0xa4,
0x6c, 0x44, 0xfc, 0x7e, 0xd6, 0xa4, 0x7a, 0xd6, 0x0c, 0x87, 0xdc, 0x0b, 0x0e, 0x5e, 0x70, 0x48,
0xed, 0xec, 0x40, 0x02, 0xca, 0x22, 0x5e, 0xab, 0xb2, 0xbc, 0xf3, 0x6b, 0x26, 0xbe, 0xd8, 0xcd,
0x2a, 0xfc, 0xfe, 0x88, 0x8f, 0xb0, 0x6c, 0xa3, 0xee, 0x7f, 0x38, 0x79, 0x39, 0x4d, 0x97, 0x2d,
0xff, 0x12, 0x79, 0x87, 0x15, 0xd0, 0x70, 0x17, 0x14, 0x3f, 0xe4, 0xdd, 0x5d, 0xda, 0x2c, 0x3c,
0x5c, 0xc8, 0xa1, 0x8a, 0x9a, 0x0b, 0x19, 0xc5, 0xef, 0xe8, 0x33, 0x8e, 0xa6, 0x17, 0x5d, 0x86,
0x8b, 0x4f, 0xbd, 0x8d, 0x0f, 0x42, 0xe6, 0x30, 0x14, 0x6f, 0xdb, 0x7f, 0x39, 0x4c, 0xef, 0x84,
0x33, 0x25, 0xf1, 0xca, 0x5a, 0xca, 0x8c, 0x2d, 0x00, 0xbe, 0x06, 0x0f, 0x77, 0x41, 0x77, 0xbd,
0xa2, 0xb1, 0xd0, 0x50, 0x79, 0x7b, 0xe2, 0xe7, 0xb7, 0xe3, 0xb3, 0xe0, 0xd4, 0xdf, 0xcf, 0xd3,
0x7b, 0xde, 0xe0, 0xa7, 0xa7, 0xf0, 0xd5, 0xb6, 0xbd, 0xde, 0xec, 0x71, 0x44, 0x8a, 0x7b, 0x55,
0x81, 0xd9, 0x61, 0xd8, 0x11, 0xe9, 0xf5, 0xda, 0xb7, 0xd1, 0xf4, 0xfd, 0x98, 0xf3, 0x96, 0x0f,
0xe8, 0xd1, 0xe9, 0x1f, 0x91, 0x34, 0xe9, 0xdf, 0x9b, 0xa4, 0x7b, 0x5d, 0xff, 0x01, 0x47, 0xe8,
0xd7, 0x03, 0x73, 0x05, 0x00, 0x00
};
// Autogenerated from wled00/data/404.htm, do not edit!!
const uint16_t PAGE_404_length = 868;
const uint16_t PAGE_404_length = 870;
const uint8_t PAGE_404[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x65, 0x54, 0x5b, 0x73, 0xaa, 0x3a,
0x14, 0x7e, 0xef, 0xaf, 0xe0, 0x78, 0xe6, 0xcc, 0x7e, 0x68, 0x2d, 0xa8, 0xd8, 0x2a, 0xa2, 0x33,
0x01, 0x51, 0xec, 0xc5, 0x7a, 0xa3, 0xd6, 0xbe, 0x05, 0x12, 0x21, 0x15, 0x08, 0x4d, 0x82, 0x62,
0x3b, 0xfd, 0xef, 0x3b, 0x40, 0xf7, 0x9c, 0xce, 0xec, 0x35, 0x03, 0x2b, 0xf9, 0x56, 0xd6, 0x7d,
0x25, 0xe6, 0x3f, 0xe3, 0x27, 0x7b, 0xb3, 0x5b, 0x38, 0x4a, 0x24, 0x92, 0x78, 0x64, 0x7e, 0xff,
0x31, 0x44, 0x23, 0x33, 0xc1, 0x02, 0x2a, 0x41, 0x04, 0x19, 0xc7, 0x62, 0xd8, 0xc8, 0xc5, 0xbe,
0xd9, 0x6b, 0x7c, 0xa3, 0x17, 0x01, 0x4d, 0x05, 0x4e, 0x25, 0x7c, 0x22, 0x48, 0x44, 0x43, 0x84,
0x8f, 0x24, 0xc0, 0xcd, 0x6a, 0xd3, 0x50, 0x52, 0x98, 0xe0, 0x61, 0xe3, 0x48, 0xf0, 0x29, 0xa3,
0x4c, 0xfc, 0xd1, 0xa9, 0x51, 0x11, 0xe1, 0x04, 0x37, 0x03, 0x1a, 0x53, 0xd6, 0xf8, 0x61, 0xe6,
0xdf, 0x76, 0x45, 0xf2, 0xac, 0x20, 0x22, 0xc6, 0xa3, 0x39, 0x15, 0xca, 0x9e, 0xe6, 0x29, 0x32,
0xd5, 0x1a, 0x30, 0xb9, 0x38, 0x4b, 0x76, 0xe1, 0x53, 0x74, 0xfe, 0xdc, 0x4b, 0xb5, 0xe6, 0x1e,
0x26, 0x24, 0x3e, 0x1b, 0xcf, 0x98, 0x21, 0x98, 0xc2, 0x2b, 0x17, 0xc7, 0x47, 0x2c, 0x48, 0x00,
0xaf, 0x38, 0x4c, 0x79, 0x93, 0x63, 0x46, 0xf6, 0x03, 0x81, 0x0b, 0xd1, 0x84, 0x31, 0x09, 0x53,
0x23, 0x90, 0x7e, 0x30, 0x1b, 0xf8, 0x30, 0x38, 0x84, 0xac, 0xb4, 0x5c, 0x07, 0x61, 0x94, 0x9e,
0x07, 0x09, 0x64, 0x21, 0x49, 0x0d, 0x6d, 0xf0, 0x8d, 0xed, 0xf7, 0xfb, 0x2f, 0x92, 0x84, 0x9f,
0x55, 0x42, 0x86, 0xae, 0x69, 0x59, 0x21, 0xcf, 0x14, 0x75, 0x82, 0x46, 0x57, 0xfb, 0x6f, 0x40,
0x12, 0x18, 0xe2, 0x26, 0xc3, 0x29, 0x92, 0x8e, 0xd2, 0xd0, 0xc8, 0x48, 0x81, 0x63, 0x28, 0x30,
0xfa, 0x4b, 0x12, 0x30, 0xc2, 0xb3, 0x26, 0x46, 0x21, 0xe6, 0x7f, 0xfc, 0xb4, 0xbb, 0x59, 0xa1,
0x68, 0x4a, 0xb3, 0xa5, 0x95, 0xfc, 0xcb, 0xcf, 0x85, 0xa0, 0xe9, 0x27, 0xcd, 0x45, 0x4c, 0x52,
0x5c, 0x46, 0x91, 0x33, 0x2e, 0xc3, 0xc8, 0x28, 0xa9, 0x62, 0xce, 0x20, 0x42, 0xa5, 0xa5, 0x5e,
0x15, 0x45, 0x65, 0xa1, 0xd4, 0x1c, 0xd4, 0xd1, 0xb4, 0x3b, 0xe5, 0xba, 0xca, 0x54, 0x30, 0x99,
0xfa, 0x9e, 0xb2, 0xc4, 0xc8, 0xb3, 0x0c, 0xb3, 0x00, 0x72, 0x3c, 0xf8, 0x59, 0xab, 0xe8, 0x4f,
0x8d, 0x6a, 0x94, 0x93, 0x0f, 0x6c, 0xb4, 0xfa, 0x52, 0xfb, 0xef, 0xaa, 0x74, 0x3a, 0x9d, 0x1f,
0xc5, 0x18, 0xf8, 0x94, 0xc9, 0x74, 0x0c, 0x4d, 0xe1, 0x34, 0x26, 0x48, 0xf9, 0x81, 0x35, 0x19,
0x44, 0x24, 0xe7, 0x55, 0x4e, 0x5f, 0x17, 0xa6, 0x5a, 0xf7, 0xc9, 0x54, 0xeb, 0x19, 0x2a, 0xdb,
0x35, 0x32, 0x65, 0x29, 0x15, 0x18, 0xcb, 0x36, 0xcb, 0x96, 0x73, 0x16, 0x0c, 0x1b, 0x08, 0x0a,
0x68, 0x54, 0x85, 0x52, 0xb3, 0x34, 0x94, 0xee, 0x39, 0xbe, 0xd1, 0xaf, 0xc8, 0xb3, 0xf5, 0xb4,
0x3a, 0x69, 0xf7, 0xd3, 0x90, 0x02, 0x49, 0xf3, 0xb5, 0x17, 0x39, 0x5e, 0x28, 0x57, 0x76, 0xb9,
0x05, 0xa1, 0x0d, 0x1e, 0x25, 0xb3, 0x9c, 0x6c, 0xc6, 0xa6, 0x15, 0xf2, 0x32, 0x5f, 0xaf, 0xb4,
0x19, 0x60, 0x5c, 0x0f, 0x6e, 0x96, 0x25, 0xb0, 0x4a, 0x97, 0x5e, 0xcb, 0x92, 0x0a, 0xc5, 0xdb,
0xe9, 0xd8, 0xdb, 0x2d, 0xbd, 0x12, 0xf4, 0x3d, 0xa7, 0xf0, 0x56, 0x95, 0xdc, 0xea, 0xb5, 0x42,
0xdb, 0x53, 0x3f, 0xee, 0xdf, 0xd5, 0x92, 0xfa, 0xfe, 0xb6, 0x45, 0x6d, 0x10, 0x4e, 0x23, 0x0a,
0x4b, 0xf1, 0x74, 0xf1, 0xf0, 0xd2, 0xab, 0x2c, 0xdf, 0xa1, 0xc9, 0xdd, 0x93, 0xa7, 0xfe, 0x4f,
0x60, 0x32, 0x5f, 0x60, 0x6b, 0x56, 0xc9, 0x02, 0x27, 0x7a, 0x0d, 0x4e, 0x00, 0x8c, 0x79, 0xb9,
0xbd, 0x05, 0x60, 0xcb, 0xb6, 0x64, 0x79, 0x28, 0x03, 0x45, 0x6b, 0x6f, 0x65, 0x3d, 0x8f, 0xa3,
0x45, 0x11, 0xf4, 0xfd, 0x31, 0xf5, 0x42, 0x07, 0xcc, 0x97, 0xd8, 0x5f, 0xa8, 0x13, 0x2f, 0x77,
0x1f, 0xdf, 0xac, 0xe9, 0x4e, 0xb5, 0x2e, 0x3b, 0xce, 0x6e, 0x75, 0xbb, 0x72, 0xb5, 0x77, 0x5b,
0x7d, 0xb5, 0x82, 0x1b, 0xf7, 0x64, 0xc7, 0x6f, 0xa1, 0xfb, 0x74, 0x59, 0xbc, 0xce, 0x9e, 0xd7,
0xb3, 0x36, 0xdf, 0x85, 0x2e, 0x9c, 0xde, 0x3a, 0xd6, 0x36, 0xea, 0xbd, 0x6d, 0x69, 0xb1, 0x61,
0xb6, 0x35, 0x41, 0xe3, 0xbb, 0x4b, 0x6b, 0xac, 0xc7, 0xfe, 0xcc, 0x2d, 0x40, 0xf0, 0xd1, 0x03,
0x0b, 0xf0, 0xfc, 0xb0, 0xe1, 0xec, 0xd5, 0xd1, 0xf1, 0x72, 0xdc, 0x7d, 0xff, 0x10, 0x9d, 0x00,
0x4c, 0x36, 0x3b, 0x7a, 0xb0, 0xf5, 0x9d, 0x3d, 0xef, 0x4f, 0xcf, 0x7e, 0x98, 0xeb, 0x67, 0xb0,
0x14, 0xd6, 0xe4, 0x61, 0xf9, 0xe2, 0xe6, 0x2e, 0xb0, 0xc0, 0xed, 0xdd, 0x23, 0x7e, 0x72, 0x6c,
0xd5, 0xd1, 0xb6, 0xdd, 0xfc, 0xdc, 0x0f, 0x8f, 0xfa, 0x91, 0x76, 0x97, 0xee, 0x7d, 0x9b, 0xdc,
0x9e, 0xdf, 0xdb, 0x76, 0xcf, 0x03, 0xd6, 0xa3, 0xee, 0x26, 0x0f, 0x97, 0xf6, 0x7a, 0xf3, 0x62,
0x6f, 0x26, 0xad, 0x31, 0xb3, 0x5f, 0x6e, 0x2e, 0xa7, 0x59, 0xff, 0xc3, 0xea, 0xa2, 0x2a, 0x5b,
0xe0, 0xc4, 0x93, 0xcd, 0x61, 0x9d, 0x2f, 0x13, 0xdb, 0x6e, 0x8c, 0x2e, 0xcc, 0xa8, 0x35, 0xd2,
0x35, 0x5d, 0x29, 0xef, 0xeb, 0xa4, 0xbe, 0xaf, 0x12, 0x31, 0xfd, 0x11, 0x38, 0xe0, 0x84, 0x28,
0x88, 0x62, 0xae, 0xa4, 0x52, 0x76, 0x48, 0xe9, 0x49, 0x39, 0x45, 0x98, 0x61, 0xe5, 0x4c, 0x73,
0x05, 0x4a, 0x5e, 0x0e, 0x08, 0x46, 0xd7, 0xd7, 0xd7, 0xa6, 0xea, 0x4b, 0x0d, 0x56, 0x7d, 0x17,
0x66, 0x7d, 0x19, 0x14, 0x9a, 0x06, 0x31, 0x09, 0x0e, 0xc3, 0x5f, 0x27, 0x92, 0x22, 0x7a, 0xba,
0x8e, 0x69, 0x00, 0x05, 0xa1, 0xe9, 0x75, 0xc4, 0xf0, 0x7e, 0xd8, 0x50, 0xb9, 0x1c, 0x43, 0xcc,
0x78, 0xe3, 0xd7, 0xc8, 0x92, 0xb3, 0xab, 0x08, 0xaa, 0x94, 0x6f, 0x09, 0xa3, 0x31, 0x97, 0xe6,
0x2a, 0x13, 0xd2, 0x96, 0x5a, 0x8f, 0x9f, 0x5a, 0xbd, 0x6a, 0xbf, 0x01, 0x22, 0xc8, 0xb7, 0x64,
0xeb, 0x04, 0x00, 0x00
0x14, 0x7e, 0xef, 0xaf, 0xe0, 0xb8, 0x67, 0xcf, 0x7e, 0x68, 0x15, 0x54, 0xac, 0x8a, 0xe8, 0x19,
0x40, 0x14, 0x7b, 0xf1, 0x4e, 0xad, 0x7d, 0x0b, 0x24, 0x42, 0x2a, 0x10, 0x9a, 0x04, 0xc5, 0x76,
0xfa, 0xdf, 0x4f, 0x80, 0x76, 0x4e, 0x67, 0xf6, 0x9a, 0x81, 0x95, 0x7c, 0x2b, 0xeb, 0xbe, 0x12,
0xfd, 0x9f, 0xf1, 0xc2, 0xda, 0xee, 0x97, 0xb6, 0x14, 0xf2, 0x38, 0x1a, 0xe9, 0x5f, 0x7f, 0x04,
0xe0, 0x48, 0x8f, 0x11, 0x07, 0x92, 0x1f, 0x02, 0xca, 0x10, 0x1f, 0xd6, 0x32, 0x7e, 0xa8, 0xf7,
0x6a, 0x5f, 0xe8, 0x95, 0x4f, 0x12, 0x8e, 0x12, 0x01, 0x9f, 0x31, 0xe4, 0xe1, 0x10, 0xa2, 0x13,
0xf6, 0x51, 0xbd, 0xdc, 0xd4, 0xa4, 0x04, 0xc4, 0x68, 0x58, 0x3b, 0x61, 0x74, 0x4e, 0x09, 0xe5,
0xdf, 0x3a, 0x15, 0xca, 0x43, 0x14, 0xa3, 0xba, 0x4f, 0x22, 0x42, 0x6b, 0x3f, 0xcc, 0xfc, 0x6a,
0x95, 0x24, 0xce, 0x72, 0xcc, 0x23, 0x34, 0x9a, 0x13, 0x2e, 0x1d, 0x48, 0x96, 0x40, 0x5d, 0xae,
0x00, 0x9d, 0xf1, 0x8b, 0x60, 0x57, 0x1e, 0x81, 0x97, 0x8f, 0x83, 0x50, 0xab, 0x1f, 0x40, 0x8c,
0xa3, 0x8b, 0xf6, 0x84, 0x28, 0x04, 0x09, 0xb8, 0x71, 0x50, 0x74, 0x42, 0x1c, 0xfb, 0xe0, 0x86,
0x81, 0x84, 0xd5, 0x19, 0xa2, 0xf8, 0x30, 0xe0, 0x28, 0xe7, 0x75, 0x10, 0xe1, 0x20, 0xd1, 0x7c,
0xe1, 0x07, 0xd1, 0x81, 0x07, 0xfc, 0x63, 0x40, 0x0b, 0xcb, 0x55, 0x10, 0x5a, 0xe1, 0x79, 0x10,
0x03, 0x1a, 0xe0, 0x44, 0x53, 0x06, 0x5f, 0xd8, 0xe1, 0x70, 0xf8, 0xc4, 0x71, 0xf0, 0x51, 0x26,
0xa4, 0xa9, 0x8a, 0x92, 0xe6, 0xe2, 0x4c, 0x5e, 0x25, 0xa8, 0x75, 0x94, 0xdf, 0x03, 0x1c, 0x83,
0x00, 0xd5, 0x29, 0x4a, 0xa0, 0x70, 0x94, 0x04, 0x5a, 0x8a, 0x73, 0x14, 0x01, 0x8e, 0xe0, 0x5f,
0x12, 0x9f, 0x62, 0x96, 0xd6, 0x11, 0x0c, 0x10, 0xfb, 0xf6, 0xd3, 0xea, 0xa4, 0xb9, 0xa4, 0x48,
0xf5, 0xa6, 0x52, 0xf0, 0x4f, 0x2f, 0xe3, 0x9c, 0x24, 0x1f, 0x24, 0xe3, 0x11, 0x4e, 0x50, 0x11,
0x45, 0x46, 0x99, 0x08, 0x23, 0x25, 0xb8, 0x8c, 0x39, 0x05, 0x10, 0x16, 0x96, 0x7a, 0x65, 0x14,
0xa5, 0x85, 0x42, 0x73, 0x50, 0x45, 0xd3, 0x6a, 0x17, 0xeb, 0x32, 0x53, 0x4e, 0x45, 0xea, 0x07,
0x42, 0x63, 0x2d, 0x4b, 0x53, 0x44, 0x7d, 0xc0, 0xd0, 0xe0, 0x67, 0xad, 0xc2, 0xef, 0x1a, 0x55,
0x28, 0xc3, 0xef, 0x48, 0x6b, 0xf6, 0x85, 0xf6, 0xdf, 0x55, 0x69, 0xb7, 0xdb, 0x3f, 0x8a, 0x31,
0xf0, 0x08, 0x15, 0xe9, 0x68, 0x8a, 0xc4, 0x48, 0x84, 0xa1, 0xf4, 0x03, 0xab, 0x53, 0x00, 0x71,
0xc6, 0xca, 0x9c, 0x3e, 0xaf, 0x74, 0xb9, 0xea, 0x93, 0x2e, 0x57, 0x33, 0x54, 0xb4, 0x6b, 0xa4,
0x8b, 0x52, 0x4a, 0x20, 0x12, 0x6d, 0x16, 0x2d, 0x67, 0xd4, 0x1f, 0xd6, 0x20, 0xe0, 0x40, 0x2b,
0x0b, 0x25, 0xa7, 0x49, 0x20, 0xdc, 0x33, 0x74, 0xab, 0xde, 0xe0, 0x27, 0x73, 0xb1, 0x3e, 0x2b,
0xf7, 0xd3, 0x80, 0x18, 0x82, 0xe6, 0x1b, 0x37, 0xb4, 0xdd, 0x40, 0xac, 0xac, 0x62, 0x6b, 0x04,
0x96, 0xf1, 0x28, 0x98, 0x69, 0xa7, 0x33, 0x3a, 0x2d, 0x91, 0xe7, 0xf9, 0x66, 0xad, 0xcc, 0x0c,
0xca, 0x54, 0xff, 0x76, 0x55, 0x00, 0xeb, 0x64, 0xe5, 0x36, 0x4d, 0xa1, 0x90, 0xbf, 0x9e, 0x4f,
0xbd, 0xfd, 0xca, 0x2d, 0x40, 0xcf, 0xb5, 0x73, 0x77, 0x5d, 0xca, 0xcd, 0x5e, 0x33, 0xb0, 0x5c,
0xf9, 0xfd, 0xfe, 0x4d, 0x2e, 0xa8, 0xef, 0xed, 0x9a, 0xc4, 0x32, 0x82, 0x69, 0x48, 0x40, 0x21,
0x9e, 0x2e, 0x1f, 0x9e, 0x7b, 0xa5, 0xe5, 0x3b, 0x38, 0xb9, 0x5b, 0xb8, 0xf2, 0xff, 0x64, 0x4c,
0xe6, 0x4b, 0x64, 0xce, 0x4a, 0x99, 0x6f, 0x87, 0x2f, 0xfe, 0xd9, 0x30, 0xc6, 0xac, 0xd8, 0x76,
0x0d, 0x63, 0x47, 0x77, 0x78, 0x75, 0x2c, 0x02, 0x85, 0x1b, 0x77, 0x6d, 0x3e, 0x8d, 0xc3, 0x65,
0xee, 0xf7, 0xbd, 0x31, 0x71, 0x03, 0xdb, 0x98, 0xaf, 0x90, 0xb7, 0x94, 0x27, 0x6e, 0xe6, 0x3c,
0xbe, 0x9a, 0xd3, 0xbd, 0x6c, 0x5e, 0xb7, 0xed, 0xfd, 0xba, 0xbb, 0x76, 0x94, 0x37, 0x4b, 0x7e,
0x31, 0xfd, 0x5b, 0xe7, 0x6c, 0x45, 0xaf, 0x81, 0xb3, 0xb8, 0xce, 0x5f, 0x66, 0x4f, 0x9b, 0x59,
0x8b, 0xed, 0x03, 0x07, 0x4c, 0xbb, 0xb6, 0xb9, 0x0b, 0x7b, 0xaf, 0x3b, 0x92, 0x6f, 0xa9, 0x65,
0x4e, 0xe0, 0xf8, 0xee, 0xda, 0x1c, 0xab, 0x91, 0x37, 0x73, 0x72, 0xc3, 0x7f, 0xef, 0x19, 0x4b,
0xe3, 0xe9, 0x61, 0xcb, 0xe8, 0x8b, 0xad, 0xa2, 0xd5, 0xb8, 0xf3, 0xf6, 0xce, 0xdb, 0xbe, 0x31,
0xd9, 0xee, 0xc9, 0xd1, 0x52, 0xf7, 0xd6, 0xbc, 0x3f, 0xbd, 0x78, 0x41, 0xa6, 0x5e, 0x8c, 0x15,
0x37, 0x27, 0x0f, 0xab, 0x67, 0x27, 0x73, 0x0c, 0xd3, 0xe8, 0xde, 0x3d, 0xa2, 0x85, 0x6d, 0xc9,
0xb6, 0xb2, 0xeb, 0x64, 0x97, 0x7e, 0x70, 0x52, 0x4f, 0xa4, 0xb3, 0x72, 0xee, 0x5b, 0xb8, 0x7b,
0x79, 0x6b, 0x59, 0x3d, 0xd7, 0x30, 0x1f, 0x55, 0x27, 0x7e, 0xb8, 0xb6, 0x36, 0xdb, 0x67, 0x6b,
0x3b, 0x69, 0x8e, 0xa9, 0xf5, 0x7c, 0x7b, 0x3d, 0x4d, 0xfb, 0xef, 0x66, 0x07, 0x96, 0xd9, 0x1a,
0x76, 0x34, 0xd9, 0x1e, 0x37, 0xd9, 0x2a, 0xb6, 0xac, 0xda, 0xe8, 0x4a, 0x0f, 0x9b, 0x23, 0x55,
0x51, 0xa5, 0xe2, 0xbe, 0x4e, 0xaa, 0xfb, 0x2a, 0x10, 0xdd, 0x1b, 0x19, 0x47, 0x14, 0x63, 0x09,
0x12, 0xc4, 0xa4, 0x44, 0xc8, 0x8e, 0x09, 0x39, 0x4b, 0xe7, 0x10, 0x51, 0x24, 0x5d, 0x48, 0x26,
0x01, 0xc1, 0x8b, 0x01, 0x41, 0xb0, 0xd1, 0x68, 0xe8, 0xb2, 0x27, 0x34, 0x68, 0xf9, 0x5d, 0xe9,
0xd5, 0x65, 0x90, 0x48, 0xe2, 0x47, 0xd8, 0x3f, 0x0e, 0xff, 0x9c, 0x71, 0x02, 0xc9, 0xb9, 0x11,
0x11, 0x1f, 0x70, 0x4c, 0x92, 0x46, 0x48, 0xd1, 0x61, 0x58, 0x6b, 0x34, 0xe4, 0x7f, 0x99, 0x98,
0x44, 0x44, 0x59, 0xed, 0xcf, 0xc8, 0x14, 0xe3, 0x2b, 0x71, 0x22, 0x15, 0xcf, 0x09, 0x25, 0x11,
0x13, 0x16, 0x4b, 0x2b, 0xc2, 0x9c, 0x5c, 0x4d, 0xa0, 0x5c, 0x3e, 0x6c, 0xff, 0x01, 0xd9, 0xa5,
0x2c, 0x90, 0xee, 0x04, 0x00, 0x00
};

File diff suppressed because it is too large Load Diff

549
wled00/html_pxmagic.h Normal file
View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,11 @@
#define DIMPROV_PRINTF(x...)
#endif
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
#undef WLED_DISABLE_IMPROV_WIFISCAN
#define WLED_DISABLE_IMPROV_WIFISCAN
#endif
#define IMPROV_VERSION 1
void parseWiFiCommand(char *rpcData);
@ -28,20 +33,14 @@ enum ImprovPacketByte {
RPC_CommandType = 9
};
enum ImprovRPCType {
Command_Wifi = 0x01,
Request_State = 0x02,
Request_Info = 0x03
};
//File dbgf;
#ifndef WLED_DISABLE_IMPROV_WIFISCAN
static bool improvWifiScanRunning = false;
#endif
//blocking function to parse an Improv Serial packet
void handleImprovPacket() {
uint8_t header[6] = {'I','M','P','R','O','V'};
//dbgf = WLED_FS.open("/improv.log","a");
bool timeout = false;
uint8_t waitTime = 25;
uint16_t packetByte = 0;
@ -62,12 +61,11 @@ void handleImprovPacket() {
byte next = Serial.read();
DIMPROV_PRINT("Received improv byte: "); DIMPROV_PRINTF("%x\r\n",next);
//f.write(next);
switch (packetByte) {
case ImprovPacketByte::Version: {
if (next != IMPROV_VERSION) {
DIMPROV_PRINTLN(F("Invalid version"));
//dbgf.close();
return;
}
break;
@ -75,7 +73,6 @@ void handleImprovPacket() {
case ImprovPacketByte::PacketType: {
if (next != ImprovPacketType::RPC_Command) {
DIMPROV_PRINTF("Non RPC-command improv packet type %i\n",next);
//dbgf.close();
return;
}
if (!improvActive) improvActive = 1;
@ -89,7 +86,6 @@ void handleImprovPacket() {
if (checksum != next) {
DIMPROV_PRINTF("Got RPC checksum %i, expected %i",next,checksum);
sendImprovStateResponse(0x01, true);
//dbgf.close();
return;
}
@ -100,22 +96,23 @@ void handleImprovPacket() {
if (WLED_WIFI_CONFIGURED) improvState = 0x03; //provisioning
if (Network.isConnected()) improvState = 0x04; //provisioned
sendImprovStateResponse(improvState, false);
if (improvState == 0x04) sendImprovRPCResponse(ImprovRPCType::Request_State);
if (improvState == 0x04) sendImprovIPRPCResult(ImprovRPCType::Request_State);
break;
}
case ImprovRPCType::Request_Info: sendImprovInfoResponse(); break;
#ifndef WLED_DISABLE_IMPROV_WIFISCAN
case ImprovRPCType::Request_Scan: startImprovWifiScan(); break;
#endif
default: {
DIMPROV_PRINTF("Unknown RPC command %i\n",next);
sendImprovStateResponse(0x02, true);
}
}
//dbgf.close();
return;
}
if (packetByte < 6) { //check header
if (next != header[packetByte]) {
DIMPROV_PRINTLN(F("Invalid improv header"));
//dbgf.close();
return;
}
} else if (packetByte > 9) { //RPC data
@ -128,7 +125,6 @@ void handleImprovPacket() {
checksum += next;
packetByte++;
}
//dbgf.close();
}
void sendImprovStateResponse(uint8_t state, bool error) {
@ -147,79 +143,116 @@ void sendImprovStateResponse(uint8_t state, bool error) {
Serial.write('\n');
}
void sendImprovRPCResponse(byte commandId) {
// used by sendImprovIPRPCResult(), sendImprovInfoResponse(), and handleImprovWifiScan()
void sendImprovRPCResult(ImprovRPCType type, uint8_t n_strings, const char **strings) {
if (improvError > 0 && improvError < 3) sendImprovStateResponse(0x00, true);
uint8_t packetLen = 12;
char out[64] = {'I','M','P','R','O','V'};
char out[256] = {'I','M','P','R','O','V'};
out[6] = IMPROV_VERSION;
out[7] = ImprovPacketType::RPC_Response;
out[8] = 2; //Length (set below)
out[9] = commandId;
out[10] = 0; //Data len (set below)
out[11] = '\0'; //URL len (set below)
//out[8] = 2; //Length (set below)
out[9] = type;
//out[10] = 0; //Data len (set below)
uint16_t pos = 11;
if (Network.isConnected())
{
IPAddress localIP = Network.localIP();
uint8_t len = sprintf(out+12, "http://%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]);
if (len > 24) return; //sprintf fail?
out[11] = len;
out[10] = 1 + len;
out[8] = 3 + len; //RPC command type + data len + url len + url
packetLen = 13 + len;
for (uint8_t s = 0; s < n_strings; s++) {
size_t len = strlen(strings[s]);
if (pos + len > 254) continue; // simple buffer overflow guard
out[pos++] = len;
strcpy(out + pos, strings[s]);
pos += len;
}
packetLen = pos +1;
out[8] = pos -9; // Length of packet (excluding first 9 header bytes and final checksum byte)
out[10] = pos -11; // Data len
uint8_t checksum = 0;
for (uint8_t i = 0; i < packetLen -1; i++) checksum += out[i];
out[packetLen -1] = checksum;
Serial.write((uint8_t*)out, packetLen);
Serial.write('\n');
DIMPROV_PRINT("RPC result checksum");
DIMPROV_PRINTLN(checksum);
}
void sendImprovIPRPCResult(ImprovRPCType type) {
if (Network.isConnected())
{
char urlStr[64];
IPAddress localIP = Network.localIP();
uint8_t len = sprintf(urlStr, "http://%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]);
if (len > 24) return; //sprintf fail?
const char *str[1] = {urlStr};
sendImprovRPCResult(type, 1, str);
} else {
sendImprovRPCResult(type, 0);
}
improvActive = 1; //no longer provisioning
}
void sendImprovInfoResponse() {
if (improvError > 0 && improvError < 3) sendImprovStateResponse(0x00, true);
uint8_t packetLen = 12;
char out[128] = {'I','M','P','R','O','V'};
out[6] = IMPROV_VERSION;
out[7] = ImprovPacketType::RPC_Response;
//out[8] = 2; //Length (set below)
out[9] = ImprovRPCType::Request_Info;
//out[10] = 0; //Data len (set below)
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.14.0-b2/%i"),VERSION);
out[16] = vlen; lengthSum += vlen;
uint8_t hlen = 7;
const char* bString =
#ifdef ESP8266
strcpy(out+lengthSum+1,"esp8266");
#else
hlen = 5;
strcpy(out+lengthSum+1,"esp32");
"esp8266"
#elif CONFIG_IDF_TARGET_ESP32C3
"esp32-c3"
#elif CONFIG_IDF_TARGET_ESP32S2
"esp32-s2"
#elif CONFIG_IDF_TARGET_ESP32S3
"esp32-s3";
#else // ESP32
"esp32";
#endif
out[lengthSum] = hlen;
lengthSum += hlen + 1;
;
//Use serverDescription if it has been changed from the default "WLED", else mDNS name
bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0);
strcpy(out+lengthSum+1,useMdnsName ? cmDNS : serverDescription);
uint8_t nlen = strlen(useMdnsName ? cmDNS : serverDescription);
out[lengthSum] = nlen;
lengthSum += nlen + 1;
char vString[20];
sprintf_P(vString, PSTR("0.14.0-b3/%i"), VERSION);
const char *str[4] = {"WLED", vString, bString, useMdnsName ? cmDNS : serverDescription};
packetLen = lengthSum +1;
out[8] = lengthSum -9;
out[10] = lengthSum -11;
uint8_t checksum = 0;
for (uint8_t i = 0; i < packetLen -1; i++) checksum += out[i];
out[packetLen -1] = checksum;
Serial.write((uint8_t*)out, packetLen);
Serial.write('\n');
DIMPROV_PRINT("Info checksum");
DIMPROV_PRINTLN(checksum);
sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str);
}
#ifndef WLED_DISABLE_IMPROV_WIFISCAN
void startImprovWifiScan() {
if (improvWifiScanRunning) return;
WiFi.scanNetworks(true);
improvWifiScanRunning = true;
}
void handleImprovWifiScan() {
if (!improvWifiScanRunning) return;
int16_t status = WiFi.scanComplete();
if (status == WIFI_SCAN_RUNNING) return;
// here scan completed or failed (-2)
improvWifiScanRunning = false;
for (int i = 0; i < status; i++) {
char rssiStr[8];
sprintf(rssiStr, "%d", WiFi.RSSI(i));
#ifdef ESP8266
bool isOpen = WiFi.encryptionType(i) == ENC_TYPE_NONE;
#else
bool isOpen = WiFi.encryptionType(i) == WIFI_AUTH_OPEN;
#endif
char ssidStr[33];
strcpy(ssidStr, WiFi.SSID(i).c_str());
const char *str[3] = {ssidStr, rssiStr, isOpen ? "NO":"YES"};
sendImprovRPCResult(ImprovRPCType::Request_Scan, 3, str);
}
sendImprovRPCResult(ImprovRPCType::Request_Scan, 0);
WiFi.scanDelete();
}
#else
void startImprovWifiScan() {}
void handleImprovWifiScan() {}
#endif
void parseWiFiCommand(char* rpcData) {
uint8_t len = rpcData[0];
if (!len || len > 126) return;

View File

@ -70,13 +70,9 @@ void decBrightness()
}
}
// apply preset or fallback to a effect and palette if it doesn't exist
void presetFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID)
{
applyPreset(presetID, CALL_MODE_BUTTON_PRESET);
//these two will be overwritten if preset exists in handlePresets()
effectCurrent = effectID;
effectPalette = paletteID;
applyPresetWithFallback(presetID, CALL_MODE_BUTTON_PRESET, effectID, paletteID);
}
byte relativeChange(byte property, int8_t amount, byte lowerBoundary, byte higherBoundary)

View File

@ -9,6 +9,7 @@
#define JSON_PATH_PALETTES 5
#define JSON_PATH_FXDATA 6
#define JSON_PATH_NETWORKS 7
#define JSON_PATH_EFFECTS 8
/*
* JSON API (De)serialization
@ -33,7 +34,7 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
uint16_t start = elem["start"] | seg.start;
if (stop < 0) {
uint16_t len = elem["len"];
int len = elem["len"];
stop = (len > 0) ? start + len : seg.stop;
}
// 2D segments
@ -69,9 +70,10 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
const char * name = elem["n"].as<const char*>();
size_t len = 0;
if (name != nullptr) len = strlen(name);
if (len > 0 && len < 33) {
if (len > 0) {
if (len > WLED_MAX_SEGNAME_LEN) len = WLED_MAX_SEGNAME_LEN;
seg.name = new char[len+1];
if (seg.name) strlcpy(seg.name, name, 33);
if (seg.name) strlcpy(seg.name, name, WLED_MAX_SEGNAME_LEN+1);
} else {
// but is empty (already deleted above)
elem.remove("n");
@ -93,7 +95,10 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
if ((spc>0 && spc!=seg.spacing) || seg.map1D2D!=map1D2D) seg.fill(BLACK); // clear spacing gaps
seg.map1D2D = constrain(map1D2D, 0, 7);
seg.soundSim = constrain(soundSim, 0, 7);
seg.soundSim = constrain(soundSim, 0, 1);
uint8_t set = elem[F("set")] | seg.set;
seg.set = constrain(set, 0, 3);
uint16_t len = 1;
if (stop > start) len = stop - start;
@ -105,7 +110,7 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
of = offsetAbs;
}
if (stop > start && of > len -1) of = len -1;
seg.set(start, stop, grp, spc, of, startY, stopY);
seg.setUp(start, stop, grp, spc, of, startY, stopY);
if (seg.reset && seg.stop == 0) return true; // segment was deleted & is marked for reset, no need to change anything else
@ -127,6 +132,8 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
JsonArray colarr = elem["col"];
if (!colarr.isNull())
{
if (seg.getLightCapabilities() & 3) {
// segment has RGB or White
for (size_t i = 0; i < 3; i++)
{
int rgbw[] = {0,0,0,0};
@ -158,6 +165,11 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
seg.setColor(i, RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3]));
if (seg.mode == FX_MODE_STATIC) strip.trigger(); //instant refresh
}
} else {
// non RGB & non White segment (usually On/Off bus)
seg.setColor(0, ULTRAWHITE);
seg.setColor(1, BLACK);
}
}
// lx parser
@ -199,7 +211,9 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
getVal(elem["ix"], &seg.intensity);
uint8_t pal = seg.palette;
if (seg.getLightCapabilities() & 1) { // ignore palette for White and On/Off segments
if (getVal(elem["pal"], &pal)) seg.setPalette(pal);
}
getVal(elem["c1"], &seg.custom1);
getVal(elem["c2"], &seg.custom2);
@ -338,7 +352,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
if (presetsModifiedTime == 0) presetsModifiedTime = timein;
}
doReboot = root[F("rb")] | doReboot;
if (root[F("psave")].isNull()) doReboot = root[F("rb")] | doReboot;
// do not allow changing main segment while in realtime mode (may get odd results else)
if (!realtimeMode) strip.setMainSegmentId(root[F("mainseg")] | strip.getMainSegmentId()); // must be before realtimeLock() if "live"
@ -412,7 +426,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
// a) already applied preset content (requires "seg" or "win" but will ignore the rest)
if (!root["pd"].isNull() && stateChanged) {
currentPreset = root[F("pd")] | currentPreset;
if (root["win"].isNull()) presetCycCurr = currentPreset;
if (root["win"].isNull()) presetCycCurr = currentPreset; // otherwise it was set in handleSet() [set.cpp]
presetToRestore = currentPreset; // stateUpdated() will clear the preset, so we need to restore it after
//unloadPlaylist(); // applying a preset unloads the playlist, may be needed here too?
} else if (!root["ps"].isNull()) {
@ -433,6 +447,15 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
else callMode = CALL_MODE_DIRECT_CHANGE; // possible bugfix for playlist only containing HTTP API preset FX=~
}
if (root.containsKey(F("rmcpal")) && root[F("rmcpal")].as<bool>()) {
if (strip.customPalettes.size()) {
char fileName[32];
sprintf_P(fileName, PSTR("/palette%d.json"), strip.customPalettes.size()-1);
if (WLED_FS.exists(fileName)) WLED_FS.remove(fileName);
strip.loadCustomPalettes();
}
}
stateUpdated(callMode);
if (presetToRestore) currentPreset = presetToRestore;
@ -450,7 +473,7 @@ void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset, b
root[F("stopY")] = seg.stopY;
}
}
if (!forPreset) root["len"] = seg.stop - seg.start;
if (!forPreset) root["len"] = (seg.stop >= seg.start) ? (seg.stop - seg.start) : 0;
root["grp"] = seg.grouping;
root[F("spc")] = seg.spacing;
root[F("of")] = seg.offset;
@ -459,6 +482,7 @@ void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset, b
byte segbri = seg.opacity;
root["bri"] = (segbri) ? segbri : 255;
root["cct"] = seg.cct;
root[F("set")] = seg.set;
if (segmentBounds && seg.name != nullptr) root["n"] = reinterpret_cast<const char *>(seg.name); //not good practice, but decreases required JSON buffer
@ -700,7 +724,7 @@ void serializeInfo(JsonObject root)
#endif
root[F("freeheap")] = ESP.getFreeHeap();
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM)
if (psramFound()) root[F("psram")] = ESP.getFreePsram();
#endif
root[F("uptime")] = millis()/1000 + rolloverMillis*4294967;
@ -790,7 +814,7 @@ void setPaletteColors(JsonArray json, byte* tcp)
}
}
void serializePalettes(JsonObject root, AsyncWebServerRequest* request)
void serializePalettes(JsonObject root, int page)
{
byte tcp[72];
#ifdef ESP8266
@ -799,11 +823,6 @@ void serializePalettes(JsonObject root, AsyncWebServerRequest* request)
int itemPerPage = 8;
#endif
int page = 0;
if (request->hasParam("page")) {
page = request->getParam("page")->value().toInt();
}
int palettesCount = strip.getPaletteCount();
int customPalettes = strip.customPalettes.size();
@ -979,6 +998,7 @@ void serveJson(AsyncWebServerRequest* request)
else if (url.indexOf("info") > 0) subJson = JSON_PATH_INFO;
else if (url.indexOf("si") > 0) subJson = JSON_PATH_STATE_INFO;
else if (url.indexOf("nodes") > 0) subJson = JSON_PATH_NODES;
else if (url.indexOf("eff") > 0) subJson = JSON_PATH_EFFECTS;
else if (url.indexOf("palx") > 0) subJson = JSON_PATH_PALETTES;
else if (url.indexOf("fxda") > 0) subJson = JSON_PATH_FXDATA;
else if (url.indexOf("net") > 0) subJson = JSON_PATH_NETWORKS;
@ -988,20 +1008,6 @@ void serveJson(AsyncWebServerRequest* request)
return;
}
#endif
else if (url.indexOf(F("eff")) > 0) {
// this serves just effect names without FX data extensions in names
if (requestJSONBufferLock(19)) {
AsyncJsonResponse* response = new AsyncJsonResponse(&doc, true); // array document
JsonArray lDoc = response->getRoot();
serializeModeNames(lDoc); // remove WLED-SR extensions from effect names
response->setLength();
request->send(response);
releaseJSONBufferLock();
} else {
request->send(503, "application/json", F("{\"error\":3}"));
}
return;
}
else if (url.indexOf("pal") > 0) {
request->send_P(200, "application/json", JSON_palette_names);
return;
@ -1018,7 +1024,7 @@ void serveJson(AsyncWebServerRequest* request)
request->send(503, "application/json", F("{\"error\":3}"));
return;
}
AsyncJsonResponse *response = new AsyncJsonResponse(&doc, subJson==6);
AsyncJsonResponse *response = new AsyncJsonResponse(&doc, subJson==JSON_PATH_FXDATA || subJson==JSON_PATH_EFFECTS); // will clear and convert JsonDocument into JsonArray if necessary
JsonVariant lDoc = response->getRoot();
@ -1031,9 +1037,11 @@ void serveJson(AsyncWebServerRequest* request)
case JSON_PATH_NODES:
serializeNodes(lDoc); break;
case JSON_PATH_PALETTES:
serializePalettes(lDoc, request); break;
serializePalettes(lDoc, request->hasParam("page") ? request->getParam("page")->value().toInt() : 0); break;
case JSON_PATH_EFFECTS:
serializeModeNames(lDoc); break;
case JSON_PATH_FXDATA:
serializeModeData(lDoc.as<JsonArray>()); break;
serializeModeData(lDoc); break;
case JSON_PATH_NETWORKS:
serializeNetworks(lDoc); break;
default: //all
@ -1052,7 +1060,9 @@ void serveJson(AsyncWebServerRequest* request)
DEBUG_PRINTF("JSON buffer size: %u for request: %d\n", lDoc.memoryUsage(), subJson);
response->setLength();
size_t len = response->setLength();
DEBUG_PRINT(F("JSON content length: ")); DEBUG_PRINTLN(len);
request->send(response);
releaseJSONBufferLock();
}

View File

@ -37,8 +37,8 @@ void applyValuesToSelectedSegs()
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;}
if (effectPalette != selsegPrev.palette) {seg.setPalette(effectPalette); stateChanged = true;}
if (effectCurrent != selsegPrev.mode) {seg.setMode(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); stateChanged = true;}
@ -104,6 +104,7 @@ void stateUpdated(byte callMode) {
if (stateChanged) currentPreset = 0; //something changed, so we are no longer in the preset
if (callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) notify(callMode);
if (bri != briOld && nodeBroadcastEnabled) sendSysInfoUDP(); // update on state
//set flag to update ws and mqtt
interfaceUpdateCallMode = callMode;
@ -146,7 +147,7 @@ void stateUpdated(byte callMode) {
if (transitionActive) {
briOld = briT;
tperLast = 0;
}
} else
strip.setTransitionMode(true); // force all segments to transition mode
transitionActive = true;
transitionStartTime = millis();

View File

@ -5,7 +5,7 @@
/*
* Parser for Loxone formats
*/
bool parseLx(int lxValue, byte rgbw[4])
bool parseLx(int lxValue, byte* rgbw)
{
DEBUG_PRINT(F("LX: Lox = "));
DEBUG_PRINTLN(lxValue);

View File

@ -9,8 +9,8 @@
void parseMQTTBriPayload(char* payload)
{
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);}
if (strstr(payload, "ON") || strstr(payload, "on") || strstr(payload, "true")) {bri = briLast; stateUpdated(CALL_MODE_DIRECT_CHANGE);}
else if (strstr(payload, "T" ) || strstr(payload, "t" )) {toggleOnOff(); stateUpdated(CALL_MODE_DIRECT_CHANGE);}
else {
uint8_t in = strtoul(payload, NULL, 10);
if (in == 0 && bri > 0) briLast = bri;
@ -53,6 +53,7 @@ void onMqttConnect(bool sessionPresent)
void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
static char *payloadStr;
DEBUG_PRINT(F("MQTT msg: "));
DEBUG_PRINTLN(topic);
@ -62,11 +63,22 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties
DEBUG_PRINTLN(F("no payload -> leave"));
return;
}
//make a copy of the payload to 0-terminate it
char* payloadStr = new char[len+1];
if (payloadStr == nullptr) return; //no mem
strncpy(payloadStr, payload, len);
payloadStr[len] = '\0';
if (index == 0) { // start (1st partial packet or the only packet)
if (payloadStr) delete[] payloadStr; // fail-safe: release buffer
payloadStr = new char[total+1]; // allocate new buffer
}
if (payloadStr == nullptr) return; // buffer not allocated
// copy (partial) packet to buffer and 0-terminate it if it is last packet
char* buff = payloadStr + index;
memcpy(buff, payload, len);
if (index + len >= total) { // at end
payloadStr[total] = '\0'; // terminate c style string
} else {
DEBUG_PRINTLN(F("Partial packet received."));
return; // process next packet
}
DEBUG_PRINTLN(payloadStr);
size_t topicPrefixLen = strlen(mqttDeviceTopic);
@ -80,6 +92,7 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties
// Non-Wled Topic used here. Probably a usermod subscribed to this topic.
usermods.onMqttMessage(topic, payloadStr);
delete[] payloadStr;
payloadStr = nullptr;
return;
}
}
@ -87,16 +100,20 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties
//Prefix is stripped from the topic at this point
if (strcmp_P(topic, PSTR("/col")) == 0) {
colorFromDecOrHexString(col, (char*)payloadStr);
colorFromDecOrHexString(col, payloadStr);
colorUpdated(CALL_MODE_DIRECT_CHANGE);
} else if (strcmp_P(topic, PSTR("/api")) == 0) {
if (!requestJSONBufferLock(15)) { delete[] payloadStr; return; }
if (payload[0] == '{') { //JSON API
if (!requestJSONBufferLock(15)) {
delete[] payloadStr;
payloadStr = nullptr;
return;
}
if (payloadStr[0] == '{') { //JSON API
deserializeJson(doc, payloadStr);
deserializeState(doc.as<JsonObject>());
} else { //HTTP API
String apireq = "win"; apireq += '&'; // reduce flash string usage
apireq += (char*)payloadStr;
apireq += payloadStr;
handleSet(nullptr, apireq);
}
releaseJSONBufferLock();
@ -108,6 +125,7 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties
parseMQTTBriPayload(payloadStr);
}
delete[] payloadStr;
payloadStr = nullptr;
}
@ -124,12 +142,12 @@ void publishMqtt()
sprintf_P(s, PSTR("%u"), bri);
strlcpy(subuf, mqttDeviceTopic, 33);
strcat_P(subuf, PSTR("/g"));
mqtt->publish(subuf, 0, true, s); // retain message
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263)
sprintf_P(s, PSTR("#%06X"), (col[3] << 24) | (col[0] << 16) | (col[1] << 8) | (col[2]));
strlcpy(subuf, mqttDeviceTopic, 33);
strcat_P(subuf, PSTR("/c"));
mqtt->publish(subuf, 0, true, s); // retain message
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263)
strlcpy(subuf, mqttDeviceTopic, 33);
strcat_P(subuf, PSTR("/status"));
@ -139,7 +157,7 @@ void publishMqtt()
XML_response(nullptr, apires);
strlcpy(subuf, mqttDeviceTopic, 33);
strcat_P(subuf, PSTR("/v"));
mqtt->publish(subuf, 0, false, apires); // do not retain message
mqtt->publish(subuf, 0, retainMqttMsg, apires); // optionally retain message (#2263)
#endif
}

View File

@ -23,3 +23,4 @@
*/
//#define MAX_LEDS 1500 // Maximum total LEDs. More than 1500 might create a low memory situation on ESP8266.
//#define MDNS_NAME "wled" // mDNS hostname, ie: *.local

View File

@ -104,6 +104,26 @@ const ethernet_settings ethernetBoards[] = {
ETH_PHY_LAN8720, // eth_type,
ETH_CLOCK_GPIO17_OUT // eth_clk_mode
},
// ABC! WLED Controller V43 + Ethernet Shield & compatible
{
1, // eth_address,
5, // eth_power,
23, // eth_mdc,
33, // eth_mdio,
ETH_PHY_LAN8720, // eth_type,
ETH_CLOCK_GPIO17_OUT // eth_clk_mode
},
// Serg74-ESP32 Ethernet Shield
{
1, // eth_address,
5, // eth_power,
23, // eth_mdc,
18, // eth_mdio,
ETH_PHY_LAN8720, // eth_type,
ETH_CLOCK_GPIO17_OUT // eth_clk_mode
}
};
#endif
@ -132,16 +152,13 @@ int getSignalQuality(int rssi)
//handle Ethernet connection event
void WiFiEvent(WiFiEvent_t event)
{
#ifdef WLED_USE_ETHERNET
char hostname[25];
#endif
switch (event) {
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
case SYSTEM_EVENT_ETH_START:
DEBUG_PRINTLN(F("ETH Started"));
break;
case SYSTEM_EVENT_ETH_CONNECTED:
{
DEBUG_PRINTLN(F("ETH Connected"));
if (!apActive) {
WiFi.disconnect(true);
@ -152,10 +169,12 @@ void WiFiEvent(WiFiEvent_t event)
ETH.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);
}
// convert the "serverDescription" into a valid DNS hostname (alphanumeric)
char hostname[64];
prepareHostname(hostname);
ETH.setHostname(hostname);
showWelcomePage = false;
break;
}
case SYSTEM_EVENT_ETH_DISCONNECTED:
DEBUG_PRINTLN(F("ETH Disconnected"));
// This doesn't really affect ethernet per se,

View File

@ -3,7 +3,7 @@
*/
// From ColorWavesWithPalettes by Mark Kriegsman: https://gist.github.com/kriegsman/8281905786e8b2632aeb
// Unfortunaltely, these are stored in RAM!
// Unfortunately, these are stored in RAM!
// Gradient palette "ib_jul01_gp", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/ing/xmas/tn/ib_jul01.png.index.html

View File

@ -300,10 +300,10 @@ byte PinManagerClass::allocateLedc(byte channels)
if (ca >= channels) { //enough free channels
byte in = (i + 1) - ca;
for (byte j = 0; j < ca; j++) {
byte b = in + j;
byte by = b >> 3;
byte bi = b - 8*by;
bitWrite(ledcAlloc[by], bi, true);
byte bChan = in + j;
byte byChan = bChan >> 3;
byte biChan = bChan - 8*byChan;
bitWrite(ledcAlloc[byChan], biChan, true);
}
return in;
}

View File

@ -52,7 +52,7 @@ static void doSaveState() {
size_t len = measureJson(*fileDoc) + 1;
DEBUG_PRINTLN(len);
// if possible use SPI RAM on ESP32
#ifdef WLED_USE_PSRAM
#if defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM)
if (psramFound())
tmpRAMbuffer = (char*) ps_malloc(len);
else
@ -120,6 +120,15 @@ bool applyPreset(byte index, byte callMode)
return true;
}
// apply preset or fallback to a effect and palette if it doesn't exist
void applyPresetWithFallback(uint8_t index, uint8_t callMode, uint8_t effectID, uint8_t paletteID)
{
applyPreset(index, callMode);
//these two will be overwritten if preset exists in handlePresets()
effectCurrent = effectID;
effectPalette = paletteID;
}
void handlePresets()
{
if (presetToSave) {
@ -167,10 +176,11 @@ void handlePresets()
changePreset = true;
} else {
if (!fdo["seg"].isNull() || !fdo["on"].isNull() || !fdo["bri"].isNull() || !fdo["nl"].isNull() || !fdo["ps"].isNull() || !fdo[F("playlist")].isNull()) changePreset = true;
fdo.remove("ps"); //remove load request for presets to prevent recursive crash
if (!(tmpMode == CALL_MODE_BUTTON_PRESET && fdo["ps"].is<const char *>() && strchr(fdo["ps"].as<const char *>(),'~') != strrchr(fdo["ps"].as<const char *>(),'~')))
fdo.remove("ps"); // remove load request for presets to prevent recursive crash (if not called by button and contains preset cycling string "1~5~")
deserializeState(fdo, CALL_MODE_NO_NOTIFY, tmpPreset); // may change presetToApply by calling applyPreset()
}
if (!errorFlag && tmpPreset < 255 && changePreset) presetCycCurr = currentPreset = tmpPreset;
if (!errorFlag && tmpPreset < 255 && changePreset) currentPreset = tmpPreset;
#if defined(ARDUINO_ARCH_ESP32)
//Aircoookie recommended not to delete buffer

200
wled00/remote.cpp Normal file
View File

@ -0,0 +1,200 @@
#include "wled.h"
#define ESP_NOW_STATE_UNINIT 0
#define ESP_NOW_STATE_ON 1
#define ESP_NOW_STATE_ERROR 2
#define NIGHT_MODE_DEACTIVATED -1
#define NIGHT_MODE_BRIGHTNESS 5
#define WIZMOTE_BUTTON_ON 1
#define WIZMOTE_BUTTON_OFF 2
#define WIZMOTE_BUTTON_NIGHT 3
#define WIZMOTE_BUTTON_ONE 16
#define WIZMOTE_BUTTON_TWO 17
#define WIZMOTE_BUTTON_THREE 18
#define WIZMOTE_BUTTON_FOUR 19
#define WIZMOTE_BUTTON_BRIGHT_UP 9
#define WIZMOTE_BUTTON_BRIGHT_DOWN 8
#ifdef WLED_DISABLE_ESPNOW
void handleRemote(){}
#else
// This is kind of an esoteric strucure because it's pulled from the "Wizmote"
// product spec. That remote is used as the baseline for behavior and availability
// since it's broadly commercially available and works out of the box as a drop-in
typedef struct message_structure {
uint8_t program; // 0x91 for ON button, 0x81 for all others
uint8_t seq[4]; // Incremetal sequence number 32 bit unsigned integer LSB first
uint8_t byte5 = 32; // Unknown
uint8_t button; // Identifies which button is being pressed
uint8_t byte8 = 1; // Unknown, but always 0x01
uint8_t byte9 = 100; // Unnkown, but always 0x64
uint8_t byte10; // Unknown, maybe checksum
uint8_t byte11; // Unknown, maybe checksum
uint8_t byte12; // Unknown, maybe checksum
uint8_t byte13; // Unknown, maybe checksum
} message_structure;
static int esp_now_state = ESP_NOW_STATE_UNINIT;
static uint32_t last_seq = -1;
static int brightnessBeforeNightMode = NIGHT_MODE_DEACTIVATED;
static message_structure incoming;
// Pulled from the IR Remote logic but reduced to 10 steps with a constant of 3
const byte brightnessSteps[] = {
6, 9, 14, 22, 33, 50, 75, 113, 170, 255
};
const size_t numBrightnessSteps = sizeof(brightnessSteps) / sizeof(uint8_t);
bool nightModeActive() {
return brightnessBeforeNightMode != NIGHT_MODE_DEACTIVATED;
}
void activateNightMode() {
brightnessBeforeNightMode = bri;
bri = NIGHT_MODE_BRIGHTNESS;
}
bool resetNightMode() {
if (!nightModeActive()) {
return false;
}
bri = brightnessBeforeNightMode;
brightnessBeforeNightMode = NIGHT_MODE_DEACTIVATED;
return true;
}
// increment `bri` to the next `brightnessSteps` value
void brightnessUp() {
if (nightModeActive()) { return; }
// dumb incremental search is efficient enough for so few items
for (uint8_t index = 0; index < numBrightnessSteps; ++index) {
if (brightnessSteps[index] > bri) {
bri = brightnessSteps[index];
break;
}
}
}
// decrement `bri` to the next `brightnessSteps` value
void brightnessDown() {
if (nightModeActive()) { return; }
// dumb incremental search is efficient enough for so few items
for (int index = numBrightnessSteps - 1; index >= 0; --index) {
if (brightnessSteps[index] < bri) {
bri = brightnessSteps[index];
break;
}
}
}
void setOn() {
if (resetNightMode()) {
stateUpdated(CALL_MODE_BUTTON);
}
if (!bri) {
toggleOnOff();
}
}
void setOff() {
if (resetNightMode()) {
stateUpdated(CALL_MODE_BUTTON);
}
if (bri) {
toggleOnOff();
}
}
void presetWithFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID) {
applyPresetWithFallback(presetID, CALL_MODE_BUTTON_PRESET, effectID, paletteID);
}
// Callback function that will be executed when data is received
#ifdef ESP8266
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
#else
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
#endif
sprintf (last_signal_src, "%02x%02x%02x%02x%02x%02x",
mac [0], mac [1], mac [2], mac [3], mac [4], mac [5]);
if (strcmp(last_signal_src, linked_remote) != 0) {
DEBUG_PRINT(F("ESP Now Message Received from Unlinked Sender: "));
DEBUG_PRINTLN(last_signal_src);
return;
}
if (len != sizeof(incoming)) {
DEBUG_PRINT(F("Unknown incoming ESP Now message received of length "));
DEBUG_PRINTLN(len);
return;
}
memcpy(&(incoming.program), incomingData, sizeof(incoming));
uint32_t cur_seq = incoming.seq[0] | (incoming.seq[1] << 8) | (incoming.seq[2] << 16) | (incoming.seq[3] << 24);
if (cur_seq == last_seq) {
return;
}
DEBUG_PRINT(F("Incoming ESP Now Packet["));
DEBUG_PRINT(cur_seq);
DEBUG_PRINT(F("] from sender["));
DEBUG_PRINT(last_signal_src);
DEBUG_PRINT(F("] button: "));
DEBUG_PRINTLN(incoming.button);
switch (incoming.button) {
case WIZMOTE_BUTTON_ON : setOn(); stateUpdated(CALL_MODE_BUTTON); break;
case WIZMOTE_BUTTON_OFF : setOff(); stateUpdated(CALL_MODE_BUTTON); break;
case WIZMOTE_BUTTON_ONE : presetWithFallback(1, FX_MODE_STATIC, 0); resetNightMode(); break;
case WIZMOTE_BUTTON_TWO : presetWithFallback(2, FX_MODE_BREATH, 0); resetNightMode(); break;
case WIZMOTE_BUTTON_THREE : presetWithFallback(3, FX_MODE_FIRE_FLICKER, 0); resetNightMode(); break;
case WIZMOTE_BUTTON_FOUR : presetWithFallback(4, FX_MODE_RAINBOW, 0); resetNightMode(); break;
case WIZMOTE_BUTTON_NIGHT : activateNightMode(); stateUpdated(CALL_MODE_BUTTON); break;
case WIZMOTE_BUTTON_BRIGHT_UP : brightnessUp(); stateUpdated(CALL_MODE_BUTTON); break;
case WIZMOTE_BUTTON_BRIGHT_DOWN : brightnessDown(); stateUpdated(CALL_MODE_BUTTON); break;
default: break;
}
last_seq = cur_seq;
}
void handleRemote() {
if (enable_espnow_remote) {
if (esp_now_state == ESP_NOW_STATE_UNINIT) {
DEBUG_PRINTLN(F("Initializing ESP_NOW listener"));
// Init ESP-NOW
if (esp_now_init() != 0) {
DEBUG_PRINTLN(F("Error initializing ESP-NOW"));
esp_now_state = ESP_NOW_STATE_ERROR;
}
#ifdef ESP8266
esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
#endif
esp_now_register_recv_cb(OnDataRecv);
esp_now_state = ESP_NOW_STATE_ON;
}
} else {
if (esp_now_state == ESP_NOW_STATE_ON) {
DEBUG_PRINTLN(F("Disabling ESP-NOW Remote Listener"));
if (esp_now_deinit() != 0) {
DEBUG_PRINTLN(F("Error de-initializing ESP-NOW"));
}
esp_now_state = ESP_NOW_STATE_UNINIT;
} else if (esp_now_state == ESP_NOW_STATE_ERROR) {
//Clear any error states (allows retrying by cycling)
esp_now_state = ESP_NOW_STATE_UNINIT;
}
}
}
#endif

View File

@ -7,11 +7,9 @@
//called upon POST settings form submit
void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
{
// PIN code request
if (subPage == 252)
if (subPage == SUBPAGE_PINREQ)
{
correctPIN = (strlen(settingsPIN)==0 || strncmp(settingsPIN, request->arg(F("PIN")).c_str(), 4)==0);
lastEditTime = millis();
checkSettingsPIN(request->arg(F("PIN")).c_str());
return;
}
@ -19,7 +17,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
if (subPage < 1 || subPage > 10 || !correctPIN) return;
//WIFI SETTINGS
if (subPage == 1)
if (subPage == SUBPAGE_WIFI)
{
strlcpy(clientSSID,request->arg(F("CS")).c_str(), 33);
@ -36,6 +34,14 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
noWifiSleep = request->hasArg(F("WS"));
#ifndef WLED_DISABLE_ESPNOW
enable_espnow_remote = request->hasArg(F("RE"));
strlcpy(linked_remote,request->arg(F("RMAC")).c_str(), 13);
//Normalize MAC format to lowercase
strlcpy(linked_remote,strlwr(linked_remote), 13);
#endif
#ifdef WLED_USE_ETHERNET
ethernetType = request->arg(F("ETH")).toInt();
WLED::instance().initEthernet();
@ -58,7 +64,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
}
//LED SETTINGS
if (subPage == 2)
if (subPage == SUBPAGE_LEDS)
{
int t = 0;
@ -99,6 +105,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
char rf[4] = "RF"; rf[2] = 48+s; rf[3] = 0; //refresh required
char aw[4] = "AW"; aw[2] = 48+s; aw[3] = 0; //auto white mode
char wo[4] = "WO"; wo[2] = 48+s; wo[3] = 0; //channel swap
char sp[4] = "SP"; sp[2] = 48+s; sp[3] = 0; //bus clock speed (DotStar & PWM)
if (!request->hasArg(lp)) {
DEBUG_PRINT(F("No data for "));
DEBUG_PRINTLN(s);
@ -120,11 +127,33 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
break; // no parameter
}
awmode = request->arg(aw).toInt();
uint16_t freqHz = request->arg(sp).toInt();
if (type > TYPE_ONOFF && type < 49) {
switch (freqHz) {
case 0 : freqHz = WLED_PWM_FREQ/3; break;
case 1 : freqHz = WLED_PWM_FREQ/2; break;
default:
case 2 : freqHz = WLED_PWM_FREQ; break;
case 3 : freqHz = WLED_PWM_FREQ*2; break;
case 4 : freqHz = WLED_PWM_FREQ*3; break;
}
} else if (type > 48 && type < 64) {
switch (freqHz) {
default:
case 0 : freqHz = 1000; break;
case 1 : freqHz = 2000; break;
case 2 : freqHz = 5000; break;
case 3 : freqHz = 10000; break;
case 4 : freqHz = 20000; break;
}
} else {
freqHz = 0;
}
channelSwap = (type == TYPE_SK6812_RGBW || type == TYPE_TM1814) ? request->arg(wo).toInt() : 0;
// actual finalization is done in WLED::loop() (removing old busses and adding new)
// this may happen even before this loop is finished so we do "doInitBusses" after the loop
if (busConfigs[s] != nullptr) delete busConfigs[s];
busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode);
busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freqHz);
busesChanged = true;
}
//doInitBusses = busesChanged; // we will do that below to ensure all input data is processed
@ -210,7 +239,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
gammaCorrectCol = request->hasArg(F("GC"));
gammaCorrectVal = request->arg(F("GV")).toFloat();
if (gammaCorrectVal > 1.0f && gammaCorrectVal <= 3)
calcGammaTable(gammaCorrectVal);
NeoGammaWLEDMethod::calcGammaTable(gammaCorrectVal);
else {
gammaCorrectVal = 1.0f; // no gamma correction
gammaCorrectBri = false;
@ -239,7 +268,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
}
//UI
if (subPage == 3)
if (subPage == SUBPAGE_UI)
{
strlcpy(serverDescription, request->arg(F("DS")).c_str(), 33);
syncToggleReceive = request->hasArg(F("ST"));
@ -257,7 +286,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
}
//SYNC
if (subPage == 4)
if (subPage == SUBPAGE_SYNC)
{
int t = request->arg(F("UP")).toInt();
if (t > 0) udpPort = t;
@ -327,6 +356,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
strlcpy(mqttDeviceTopic, request->arg(F("MD")).c_str(), 33);
strlcpy(mqttGroupTopic, request->arg(F("MG")).c_str(), 33);
buttonPublishMqtt = request->hasArg(F("BM"));
retainMqttMsg = request->hasArg(F("RT"));
#endif
#ifndef WLED_DISABLE_HUESYNC
@ -355,7 +385,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
}
//TIME
if (subPage == 5)
if (subPage == SUBPAGE_TIME)
{
ntpEnabled = request->hasArg(F("NT"));
strlcpy(ntpServerName, request->arg(F("NS")).c_str(), 33);
@ -429,7 +459,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
}
//SECURITY
if (subPage == 6)
if (subPage == SUBPAGE_SEC)
{
if (request->hasArg(F("RS"))) //complete factory reset
{
@ -438,7 +468,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
clearEEPROM();
#endif
serveMessage(request, 200, F("All Settings erased."), F("Connect to WLED-AP to setup again"),255);
doReboot = true;
doReboot = true; // may reboot immediately on dual-core system (race condition) which is desireable in this case
}
if (request->hasArg(F("PIN"))) {
@ -460,7 +490,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
if (otaLock && strcmp(otaPass,request->arg(F("OP")).c_str()) == 0)
{
// brute force protection: do not unlock even if correct if last save was less than 3 seconds ago
if (millis() - lastEditTime > 3000) pwdCorrect = true;
if (millis() - lastEditTime > PIN_RETRY_COOLDOWN) pwdCorrect = true;
}
if (!otaLock && request->arg(F("OP")).length() > 0)
{
@ -478,7 +508,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
}
#ifdef WLED_ENABLE_DMX // include only if DMX is enabled
if (subPage == 7)
if (subPage == SUBPAGE_DMX)
{
int t = request->arg(F("PU")).toInt();
if (t >= 0 && t <= 63999) e131ProxyUniverse = t;
@ -508,34 +538,31 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
#endif
//USERMODS
if (subPage == 8)
if (subPage == SUBPAGE_UM)
{
if (!requestJSONBufferLock(5)) return;
// global I2C & SPI pins
int8_t hw_sda_pin = !request->arg(F("SDA")).length() ? -1 : (int)request->arg(F("SDA")).toInt();
int8_t hw_scl_pin = !request->arg(F("SCL")).length() ? -1 : (int)request->arg(F("SCL")).toInt();
#ifdef ESP8266
// cannot change pins on ESP8266
if (hw_sda_pin >= 0 && hw_sda_pin != HW_PIN_SDA) hw_sda_pin = HW_PIN_SDA;
if (hw_scl_pin >= 0 && hw_scl_pin != HW_PIN_SCL) hw_scl_pin = HW_PIN_SCL;
#endif
if (i2c_sda != hw_sda_pin || i2c_scl != hw_scl_pin) {
// only if pins changed
uint8_t old_i2c[2] = { static_cast<uint8_t>(i2c_scl), static_cast<uint8_t>(i2c_sda) };
pinManager.deallocateMultiplePins(old_i2c, 2, PinOwner::HW_I2C); // just in case deallocation of old pins
PinManagerPinType i2c[2] = { { hw_sda_pin, true }, { hw_scl_pin, true } };
if (hw_sda_pin >= 0 && hw_scl_pin >= 0 && pinManager.allocateMultiplePins(i2c, 2, PinOwner::HW_I2C)) {
i2c_sda = hw_sda_pin;
i2c_scl = hw_scl_pin;
#ifdef ESP32
Wire.setPins(i2c_sda, i2c_scl); // this will fail if Wire is initilised (Wire.begin() called)
#endif
Wire.begin();
// no bus re-initialisation as usermods do not get any notification
//Wire.begin(i2c_sda, i2c_scl);
} else {
// there is no Wire.end()
DEBUG_PRINTLN(F("Could not allocate I2C pins."));
uint8_t i2c[2] = { static_cast<uint8_t>(i2c_scl), static_cast<uint8_t>(i2c_sda) };
pinManager.deallocateMultiplePins(i2c, 2, PinOwner::HW_I2C); // just in case deallocation of old pins
i2c_sda = -1;
i2c_scl = -1;
}
}
int8_t hw_mosi_pin = !request->arg(F("MOSI")).length() ? -1 : (int)request->arg(F("MOSI")).toInt();
int8_t hw_miso_pin = !request->arg(F("MISO")).length() ? -1 : (int)request->arg(F("MISO")).toInt();
int8_t hw_sclk_pin = !request->arg(F("SCLK")).length() ? -1 : (int)request->arg(F("SCLK")).toInt();
@ -545,6 +572,10 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
if (hw_miso_pin >= 0 && hw_miso_pin != HW_PIN_MISOSPI) hw_mosi_pin = HW_PIN_MISOSPI;
if (hw_sclk_pin >= 0 && hw_sclk_pin != HW_PIN_CLOCKSPI) hw_sclk_pin = HW_PIN_CLOCKSPI;
#endif
if (spi_mosi != hw_mosi_pin || spi_miso != hw_miso_pin || spi_sclk != hw_sclk_pin) {
// only if pins changed
uint8_t old_spi[3] = { static_cast<uint8_t>(spi_mosi), static_cast<uint8_t>(spi_miso), static_cast<uint8_t>(spi_sclk) };
pinManager.deallocateMultiplePins(old_spi, 3, PinOwner::HW_SPI); // just in case deallocation of old pins
PinManagerPinType spi[3] = { { hw_mosi_pin, true }, { hw_miso_pin, true }, { hw_sclk_pin, true } };
if (hw_mosi_pin >= 0 && hw_sclk_pin >= 0 && pinManager.allocateMultiplePins(spi, 3, PinOwner::HW_SPI)) {
spi_mosi = hw_mosi_pin;
@ -560,12 +591,11 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
} else {
//SPI.end();
DEBUG_PRINTLN(F("Could not allocate SPI pins."));
uint8_t spi[3] = { static_cast<uint8_t>(spi_mosi), static_cast<uint8_t>(spi_miso), static_cast<uint8_t>(spi_sclk) };
pinManager.deallocateMultiplePins(spi, 3, PinOwner::HW_SPI); // just in case deallocation of old pins
spi_mosi = -1;
spi_miso = -1;
spi_sclk = -1;
}
}
JsonObject um = doc.createNestedObject("um");
@ -648,7 +678,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
#ifndef WLED_DISABLE_2D
//2D panels
if (subPage == 10)
if (subPage == SUBPAGE_2D)
{
strip.isMatrix = request->arg(F("SOMP")).toInt();
strip.panel.clear(); // release memory if allocated
@ -684,9 +714,11 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
#endif
lastEditTime = millis();
if (subPage != 2 && !doReboot) doSerializeConfig = true; //serializeConfig(); //do not save if factory reset or LED settings (which are saved after LED re-init)
// do not save if factory reset or LED settings (which are saved after LED re-init)
doSerializeConfig = subPage != SUBPAGE_LEDS && !(subPage == SUBPAGE_SEC && doReboot);
if (subPage == SUBPAGE_UM) doReboot = request->hasArg(F("RBT")); // prevent race condition on dual core system (set reboot here, after doSerializeConfig has been set)
#ifndef WLED_DISABLE_ALEXA
if (subPage == 4) alexaInit();
if (subPage == SUBPAGE_SYNC) alexaInit();
#endif
}
@ -765,7 +797,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
if (pos > 0) {
spcI = getNumVal(&req, pos);
}
selseg.set(startI, stopI, grpI, spcI, UINT16_MAX, startY, stopY);
selseg.setUp(startI, stopI, grpI, spcI, UINT16_MAX, startY, stopY);
pos = req.indexOf(F("RV=")); //Segment reverse
if (pos > 0) selseg.reverse = req.charAt(pos+3) != '0';

View File

@ -53,8 +53,8 @@ typedef struct ip_addr ip4_addr_t;
#define DDP_PUSH_FLAG 0x01
#define DDP_TIMECODE_FLAG 0x10
#define DDP_TYPE_RGB24 0x0A
#define DDP_TYPE_RGBW32 0x1A
#define DDP_TYPE_RGB24 0x0B // 00 001 011 (RGB , 8 bits per channel, 3 channels)
#define DDP_TYPE_RGBW32 0x1B // 00 011 011 (RGBW, 8 bits per channel, 4 channels)
#define ARTNET_OPCODE_OPDMX 0x5000
#define ARTNET_OPCODE_OPPOLL 0x2000

View File

@ -10,7 +10,7 @@
*/
/*
* @title Espalexa library
* @version 2.7.0
* @version 2.7.1
* @author Christian Schwinne
* @license MIT
* @contributors d-999
@ -50,7 +50,7 @@
#include "../network/Network.h"
#ifdef ESPALEXA_DEBUG
#pragma message "Espalexa 2.7.0 debug mode"
#pragma message "Espalexa 2.7.1 debug mode"
#define EA_DEBUG(x) Serial.print (x)
#define EA_DEBUGLN(x) Serial.println (x)
#else
@ -142,7 +142,7 @@ private:
}
//device JSON string: color+temperature device emulates LCT015, dimmable device LWB010, (TODO: on/off Plug 01, color temperature device LWT010, color device LST001)
void deviceJsonString(EspalexaDevice* dev, char* buf)
void deviceJsonString(EspalexaDevice* dev, char* buf, size_t maxBuf) // softhack007 "size" parameter added, to avoid buffer overrun
{
char buf_lightid[27];
encodeLightId(dev->getId() + 1, buf_lightid);
@ -153,19 +153,19 @@ private:
//TODO: %f is not working for some reason on ESP8266 in v0.11.0 (was fine in 0.10.2). Need to investigate
//sprintf_P(buf_col,PSTR(",\"hue\":%u,\"sat\":%u,\"effect\":\"none\",\"xy\":[%f,%f]")
// ,dev->getHue(), dev->getSat(), dev->getX(), dev->getY());
sprintf_P(buf_col,PSTR(",\"hue\":%u,\"sat\":%u,\"effect\":\"none\",\"xy\":[%s,%s]"),dev->getHue(), dev->getSat(),
snprintf_P(buf_col, sizeof(buf_col), PSTR(",\"hue\":%u,\"sat\":%u,\"effect\":\"none\",\"xy\":[%s,%s]"),dev->getHue(), dev->getSat(),
((String)dev->getX()).c_str(), ((String)dev->getY()).c_str());
char buf_ct[16] = "";
//white spectrum support
if (static_cast<uint8_t>(dev->getType()) > 1 && dev->getType() != EspalexaDeviceType::color)
sprintf(buf_ct, ",\"ct\":%u", dev->getCt());
snprintf(buf_ct, sizeof(buf_ct), ",\"ct\":%u", dev->getCt());
char buf_cm[20] = "";
if (static_cast<uint8_t>(dev->getType()) > 1)
sprintf(buf_cm,PSTR("\",\"colormode\":\"%s"), modeString(dev->getColorMode()));
snprintf(buf_cm, sizeof(buf_cm), PSTR("\",\"colormode\":\"%s"), modeString(dev->getColorMode()));
sprintf_P(buf, PSTR("{\"state\":{\"on\":%s,\"bri\":%u%s%s,\"alert\":\"none%s\",\"mode\":\"homeautomation\",\"reachable\":true},"
snprintf_P(buf, maxBuf, PSTR("{\"state\":{\"on\":%s,\"bri\":%u%s%s,\"alert\":\"none%s\",\"mode\":\"homeautomation\",\"reachable\":true},"
"\"type\":\"%s\",\"name\":\"%s\",\"modelid\":\"%s\",\"manufacturername\":\"Philips\",\"productname\":\"E%u"
"\",\"uniqueid\":\"%s\",\"swversion\":\"espalexa-2.7.0\"}")
@ -219,10 +219,10 @@ private:
EA_DEBUGLN("# Responding to description.xml ... #\n");
IPAddress localIP = Network.localIP();
char s[16];
sprintf(s, "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]);
snprintf(s, sizeof(s), "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]);
char buf[1024];
sprintf_P(buf,PSTR("<?xml version=\"1.0\" ?>"
snprintf_P(buf, sizeof(buf), PSTR("<?xml version=\"1.0\" ?>"
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">"
"<specVersion><major>1</major><minor>0</minor></specVersion>"
"<URLBase>http://%s:80/</URLBase>"
@ -297,7 +297,7 @@ private:
char buf[1024];
sprintf_P(buf,PSTR("HTTP/1.1 200 OK\r\n"
snprintf_P(buf, sizeof(buf), PSTR("HTTP/1.1 200 OK\r\n"
"EXT:\r\n"
"CACHE-CONTROL: max-age=100\r\n" // SSDP_INTERVAL
"LOCATION: http://%s:80/description.xml\r\n"
@ -493,7 +493,7 @@ public:
unsigned idx = decodeLightKey(devId);
EA_DEBUGLN(idx);
char buf[50];
sprintf_P(buf,PSTR("[{\"success\":{\"/lights/%u/state/\": true}}]"),devId);
snprintf_P(buf,sizeof(buf),PSTR("[{\"success\":{\"/lights/%u/state/\": true}}]"),devId);
server->send(200, "application/json", buf);
if (idx >= currentDeviceCount) return true; //return if invalid ID
EspalexaDevice* dev = devices[idx];
@ -571,7 +571,7 @@ public:
jsonTemp += ':';
char buf[512];
deviceJsonString(devices[i], buf);
deviceJsonString(devices[i], buf, sizeof(buf)-1);
jsonTemp += buf;
if (i < currentDeviceCount-1) jsonTemp += ',';
}
@ -588,7 +588,7 @@ public:
return true;
}
char buf[512];
deviceJsonString(devices[idx], buf);
deviceJsonString(devices[idx], buf, sizeof(buf)-1);
server->send(200, "application/json", buf);
}

View File

@ -2,6 +2,15 @@
#include "EspalexaDevice.h"
// debug macros
#ifdef ESPALEXA_DEBUG
#define EA_DEBUG(x) Serial.print (x)
#define EA_DEBUGLN(x) Serial.println (x)
#else
#define EA_DEBUG(x)
#define EA_DEBUGLN(x)
#endif
EspalexaDevice::EspalexaDevice(){}
EspalexaDevice::EspalexaDevice(String deviceName, BrightnessCallbackFunction gnCallback, uint8_t initialValue) { //constructor for dimmable device
@ -124,18 +133,21 @@ uint32_t EspalexaDevice::getRGB()
{
//TODO tweak a bit to match hue lamp characteristics
//based on https://gist.github.com/paulkaplan/5184275
float temp = 10000/ _ct; //kelvins = 1,000,000/mired (and that /100)
float temp = (_ct != 0) ? (10000/ _ct) : 2; //kelvins = 1,000,000/mired (and that /100) softhack007: avoid division by zero - using "2" as substitute
float r, g, b;
#ifdef ESPALEXA_DEBUG
if (_ct == 0) {EA_DEBUGLN(F("EspalexaDevice::getRGB() Warning: ct = 0!"));}
#endif
if (temp <= 66) {
r = 255;
g = temp;
g = 99.470802 * log(g) - 161.119568;
g = 99.470802 * logf(g) - 161.119568;
if (temp <= 19) {
b = 0;
} else {
b = temp-10;
b = 138.517731 * log(b) - 305.044793;
b = 138.517731 * logf(b) - 305.044793;
}
} else {
r = temp - 60;
@ -192,9 +204,9 @@ uint32_t EspalexaDevice::getRGB()
b = 1.0f;
}
// Apply gamma correction
r = r <= 0.0031308f ? 12.92f * r : (1.0f + 0.055f) * pow(r, (1.0f / 2.4f)) - 0.055f;
g = g <= 0.0031308f ? 12.92f * g : (1.0f + 0.055f) * pow(g, (1.0f / 2.4f)) - 0.055f;
b = b <= 0.0031308f ? 12.92f * b : (1.0f + 0.055f) * pow(b, (1.0f / 2.4f)) - 0.055f;
r = r <= 0.0031308f ? 12.92f * r : (1.0f + 0.055f) * powf(r, (1.0f / 2.4f)) - 0.055f;
g = g <= 0.0031308f ? 12.92f * g : (1.0f + 0.055f) * powf(g, (1.0f / 2.4f)) - 0.055f;
b = b <= 0.0031308f ? 12.92f * b : (1.0f + 0.055f) * powf(b, (1.0f / 2.4f)) - 0.055f;
if (r > b && r > g) {
// red is biggest
@ -328,8 +340,10 @@ void EspalexaDevice::setColor(uint8_t r, uint8_t g, uint8_t b)
float X = r * 0.664511f + g * 0.154324f + b * 0.162028f;
float Y = r * 0.283881f + g * 0.668433f + b * 0.047685f;
float Z = r * 0.000088f + g * 0.072310f + b * 0.986039f;
if ((r+g+b) > 0) { // softhack007: avoid division by zero
_x = X / (X + Y + Z);
_y = Y / (X + Y + Z);
} else { _x = _y = 0.5f;} // softhack007: use default values in case of "black"
_rgb = ((r << 16) | (g << 8) | b);
_mode = EspalexaColorMode::xy;
}

Some files were not shown because too many files have changed in this diff Show More