Merge remote-tracking branch 'origin/main' into usermod-battery-update2023

This commit is contained in:
Maximilian Mewes 2023-09-08 23:17:23 +02:00
commit b77e1eb94b
161 changed files with 22388 additions and 19582 deletions

View File

@ -8,21 +8,23 @@ jobs:
name: Gather Environments name: Gather Environments
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Cache pip - name: Cache pip
uses: actions/cache@v2 uses: actions/cache@v3
with: with:
path: ~/.cache/pip path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: | restore-keys: |
${{ runner.os }}-pip- ${{ runner.os }}-pip-
- uses: actions/setup-python@v2 - uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install PlatformIO - name: Install PlatformIO
run: pip install -r requirements.txt run: pip install -r requirements.txt
- name: Get default environments - name: Get default environments
id: envs id: envs
run: | 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: outputs:
environments: ${{ steps.envs.outputs.environments }} environments: ${{ steps.envs.outputs.environments }}
@ -32,24 +34,27 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: get_default_envs needs: get_default_envs
strategy: strategy:
fail-fast: false
matrix: matrix:
environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }} environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Cache pip - name: Cache pip
uses: actions/cache@v2 uses: actions/cache@v3
with: with:
path: ~/.cache/pip path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: | restore-keys: |
${{ runner.os }}-pip- ${{ runner.os }}-pip-
- name: Cache PlatformIO - name: Cache PlatformIO
uses: actions/cache@v2 uses: actions/cache@v3
with: with:
path: ~/.platformio path: ~/.platformio
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v2 uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install PlatformIO - name: Install PlatformIO
run: pip install -r requirements.txt run: pip install -r requirements.txt
- name: Build firmware - name: Build firmware

1
.gitignore vendored
View File

@ -18,3 +18,4 @@ wled-update.sh
esp01-update.sh esp01-update.sh
/wled00/LittleFS /wled00/LittleFS
replace_fs.py replace_fs.py
wled00/wled00.ino.cpp

View File

@ -1,11 +1,148 @@
## WLED changelog ## WLED changelog
#### 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
- Added ability to disable ADAlight (-D WLED_DISABLE_ADALIGHT)
- Fixed APA102 output on Ethernet enabled controllers
- Added ArtNet virtual/network output (#3121)
- Klipper usermod (#3106)
- Remove DST from CST timezone
- various fixes and enhancements
#### Build 2302180
- Removed Blynk support (servers shut down on 31st Dec 2022)
- Added `ledgap.json` to complement ledmaps for 2D matrices
- Added support for white addressable strips (#3073)
- Ability to use SHT temperature usermod with PWM fan usermod
- Added `onStateChange()` callback to usermods (#3081)
- Refactored `bus_manager` [internal]
- Dual 1D & 2D mode (add 1D strip after the matrix)
- Removed 1D -> 2D mapping for individual pixel control
- effect tweak: Fireworks 1D
- various bugfixes
#### Build 2301240
- Version bump to v0.14.0-b2 "Hoshi"
- PixelArt converter (convert any image to pixel art and display it on a matrix) (PR #3042)
- various effect updates and optimisations
- added Overlay option to some effects (allows overlapping segments)
- added gradient text on Scrolling Text
- added #DDMM, #MMDD & #HHMM date and time options for Scrolling Text effect (PR #2990)
- deprecated: Dynamic Smooth, Dissolve Rnd, Solid Glitter
- optimised & enhanced loading of default values
- new effect: Distortion Waves (2D)
- 2D support for Ripple effect
- slower minimum speed for Railway effect
- DMX effect mode & segment controls (PR #2891)
- Optimisations for conditional compiles (further reduction of code size)
- better UX with effect sliders (PR #3012)
- enhanced support for ESP32 variants: C3, S2 & S3
- usermod enhancements (PIR, Temperature, Battery (PR #2975), Analog Clock (PR #2993))
- new usermod SHT (PR #2963)
- 2D matrix set up with gaps or irregular panels (breaking change!) (PR #2892)
- palette blending/transitions
- random palette smooth changes
- hex color notations in custom palettes
- allow more virtual buses
- plethora of bugfixes
### WLED release 0.14.0-b1 ### WLED release 0.14.0-b1
#### Build 2212222 #### Build 2212222
- Version bump to v0.14.0-b1 "Hoshi" - Version bump to v0.14.0-b1 "Hoshi"
- Full changelog TBD - 2D matrix support (including mapping 1D effects to 2D and 2D peek)
- [internal] completely rewritten Segment & WS2812FX handling code
- [internal] ability to add custom effects via usermods
- [internal] set of 2D drawing functions
- transitions on every segment (including ESP8266)
- enhanced old and new 2D effects (metadata: default values)
- custom palettes (up to 10; upload palette0.json, palette1.json, ...)
- custom effect sliders and options, quick filters
- global I2C and SPI GPIO allocation (for usermods)
- usermod settings page enhancements (dropdown & info)
- asynchronous preset loading (and added "pd" JSON API call for direct preset apply)
- new usermod Boblight (PR #2917)
- new usermod PWM Outputs (PR #2912)
- new usermod Audioreactive
- new usermod Word Clock Matrix (PR #2743)
- new usermod Ping Pong Clock (PR #2746)
- new usermod ADS1115 (PR #2752)
- new usermod Analog Clock (PR #2736)
- various usermod enhancements and updates
- allow disabling pull-up resistors on buttons
- SD card support (PR #2877)
- enhanced HTTP API to support custom effect sliders & options (X1, X2, X3, M1, M2, M3)
- multiple UDP sync message retries (PR #2830)
- network debug printer (PR #2870)
- automatic UI PC mode on large displays
- removed support for upgrading from pre-0.10 (EEPROM)
- support for setting GPIO level when LEDs are off (RMT idle level, ESP32 only) (PR #2478)
- Pakistan time-zone (PKT)
- ArtPoll support
- TM1829 LED support
- experimental support for ESP32 S2, S3 and C3
- general improvements and bugfixes
### WLED release 0.13.3 ### WLED release 0.13.3

723
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "wled", "name": "wled",
"version": "0.14.0-b1", "version": "0.14.0-b3",
"description": "Tools for WLED project", "description": "Tools for WLED project",
"main": "tools/cdata.js", "main": "tools/cdata.js",
"directories": { "directories": {
@ -25,7 +25,7 @@
"clean-css": "^4.2.3", "clean-css": "^4.2.3",
"html-minifier-terser": "^5.1.1", "html-minifier-terser": "^5.1.1",
"inliner": "^1.13.1", "inliner": "^1.13.1",
"nodemon": "^2.0.4", "nodemon": "^2.0.20",
"zlib": "^1.0.5" "zlib": "^1.0.5"
} }
} }

View File

@ -9,11 +9,15 @@
# (use `platformio_override.ini` when building for your own board; see `platformio_override.ini.sample` for an example) # (use `platformio_override.ini` when building for your own board; see `platformio_override.ini.sample` for an example)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Release / CI binaries # CI binaries
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, esp32s2_saola, esp32c3, esp32s3dev_8MB ; 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 # 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) # Single binaries (uncomment your board)
; default_envs = elekstube_ips ; 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 = esp32dev_qio80
; default_envs = esp32_eth_ota1mapp ; default_envs = esp32_eth_ota1mapp
; default_envs = esp32s2_saola ; default_envs = esp32s2_saola
; default_envs = esp32c3dev
; default_envs = lolin_s2_mini
src_dir = ./wled00 src_dir = ./wled00
data_dir = ./wled00/data 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_2_7_4 = espressif8266@2.6.2
arduino_core_3_0_0 = espressif8266@3.0.0 arduino_core_3_0_0 = espressif8266@3.0.0
arduino_core_3_2_0 = espressif8266@3.2.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 # Development platforms
arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop
arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/stage arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/stage
# Platform to use for ESP8266 # 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 # We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization
platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7 #platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
platformio/toolchain-xtensa @ ~2.40802.200502 platform_packages = platformio/framework-arduinoespressif8266
platformio/tool-esptool @ ~1.413.0 platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502
platformio/tool-esptoolpy @ ~1.30000.0 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 # 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). # This reduces the OTA size with ~45KB, so it's especially useful on low memory boards (512k/1m).
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
build_flags = build_flags =
-Wno-attributes
-DMQTT_MAX_PACKET_SIZE=1024 -DMQTT_MAX_PACKET_SIZE=1024
-DSECURE_CLIENT=SECURE_CLIENT_BEARSSL -DSECURE_CLIENT=SECURE_CLIENT_BEARSSL
-DBEARSSL_SSL_BASIC -DBEARSSL_SSL_BASIC
-D CORE_DEBUG_LEVEL=0 -D CORE_DEBUG_LEVEL=0
-D NDEBUG -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) #build_flags for the IRremoteESP8266 library (enabled decoders have to appear here)
-D _IR_ENABLE_DEFAULT_=false -D _IR_ENABLE_DEFAULT_=false
-D DECODE_HASH=true -D DECODE_HASH=true
@ -111,7 +130,7 @@ build_flags =
-D DECODE_SONY=true -D DECODE_SONY=true
-D DECODE_SAMSUNG=true -D DECODE_SAMSUNG=true
-D DECODE_LG=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 -DWLED_USE_MY_CONFIG
; -D USERMOD_SENSORSTOMQTT ; -D USERMOD_SENSORSTOMQTT
#For ADS1115 sensor uncomment following #For ADS1115 sensor uncomment following
@ -121,6 +140,7 @@ build_unflags =
build_flags_esp8266 = ${common.build_flags} ${esp8266.build_flags} build_flags_esp8266 = ${common.build_flags} ${esp8266.build_flags}
build_flags_esp32 = ${common.build_flags} ${esp32.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_1m128k = eagle.flash.1m128.ld
ldscript_2m512k = eagle.flash.2m512.ld ldscript_2m512k = eagle.flash.2m512.ld
@ -150,22 +170,21 @@ upload_speed = 115200
# LIBRARIES: required dependencies # LIBRARIES: required dependencies
# Please note that we don't always use the latest version of a library. # 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, Blynk@0.5.4(changed), E131@1.0.0(changed), Time@1.5, Timezone@1.2.1 # ArduinoJson@5.13.5, E131@1.0.0(changed), Time@1.5, Timezone@1.2.1
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
lib_compat_mode = strict lib_compat_mode = strict
lib_deps = lib_deps =
fastled/FastLED @ 3.5.0 fastled/FastLED @ 3.5.0
IRremoteESP8266 @ 2.8.2 IRremoteESP8266 @ 2.8.2
makuna/NeoPixelBus @ 2.7.5
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.7 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 #For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line
#TFT_eSPI #TFT_eSPI
#For use SSD1306 OLED display uncomment following #For compatible OLED display uncomment following
#U8g2@~2.28.8 #U8g2 #@ ~2.33.15
#U8g2@~2.32.10 #For Dallas sensor uncomment following
#For Dallas sensor uncomment following 2 lines #OneWire @ ~2.3.7
#OneWire@~2.3.5
#milesburton/DallasTemperature@^3.9.0
#For BME280 sensor uncomment following #For BME280 sensor uncomment following
#BME280 @ ~3.0.0 #BME280 @ ~3.0.0
; adafruit/Adafruit BMP280 Library @ 2.1.0 ; adafruit/Adafruit BMP280 Library @ 2.1.0
@ -182,8 +201,9 @@ build_flags =
-DESP8266 -DESP8266
-DFP_IN_IROM -DFP_IN_IROM
;-Wno-deprecated-declarations ;-Wno-deprecated-declarations
;-Wno-register -Wno-register ;; leaves some warnings when compiling C files: command-line option '-Wno-register' is valid for C++/ObjC++ but not for C
;-Wno-misleading-indentation ;-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) ; NONOSDK22x_190703 = 2.2.2-dev(38a443e)
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703 -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
; lwIP 2 - Higher Bandwidth no Features ; lwIP 2 - Higher Bandwidth no Features
@ -196,11 +216,10 @@ build_flags =
-DMIMETYPE_MINIMAL -DMIMETYPE_MINIMAL
lib_deps = lib_deps =
${env.lib_deps}
#https://github.com/lorol/LITTLEFS.git #https://github.com/lorol/LITTLEFS.git
ESPAsyncTCP @ 1.2.2 ESPAsyncTCP @ 1.2.2
ESPAsyncUDP ESPAsyncUDP
makuna/NeoPixelBus @ 2.6.9 ${env.lib_deps}
[esp32] [esp32]
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip #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 -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 #use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x
-D LOROL_LITTLEFS -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 default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
lib_deps = lib_deps =
${env.lib_deps}
https://github.com/lorol/LITTLEFS.git https://github.com/lorol/LITTLEFS.git
makuna/NeoPixelBus @ 2.6.9
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 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] [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 build_flags = -g
-DARDUINO_ARCH_ESP32 -DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32S2 -DARDUINO_ARCH_ESP32S2
-DCONFIG_IDF_TARGET_ESP32S2 -DCONFIG_IDF_TARGET_ESP32S2=1
-D CONFIG_ASYNC_TCP_USE_WDT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0
-DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0
-DCO -DCO
-DARDUINO_USB_MODE=0 ;; this flag is mandatory for ESP32-S2 ! -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: ;; 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 = lib_deps =
${env.lib_deps}
makuna/NeoPixelBus @ 2.6.9
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps}
[esp32c3] [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 build_flags = -g
-DARDUINO_ARCH_ESP32 -DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32C3 -DARDUINO_ARCH_ESP32C3
-DCONFIG_IDF_TARGET_ESP32C3 -DCONFIG_IDF_TARGET_ESP32C3=1
-D CONFIG_ASYNC_TCP_USE_WDT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0
-DCO -DCO
-DARDUINO_USB_MODE=1 ;; this flag is mandatory for ESP32-C3 -DARDUINO_USB_MODE=1 ;; this flag is mandatory for ESP32-C3
@ -252,27 +299,28 @@ build_flags = -g
;; ARDUINO_USB_CDC_ON_BOOT ;; ARDUINO_USB_CDC_ON_BOOT
lib_deps = lib_deps =
${env.lib_deps}
makuna/NeoPixelBus @ 2.6.9
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps}
[esp32s3] [esp32s3]
;; generic definitions for all ESP32-S3 boards ;; 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 build_flags = -g
-DESP32 -DESP32
-DARDUINO_ARCH_ESP32 -DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32S3 -DARDUINO_ARCH_ESP32S3
-DCONFIG_IDF_TARGET_ESP32S3 -DCONFIG_IDF_TARGET_ESP32S3=1
-D CONFIG_ASYNC_TCP_USE_WDT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0
-DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_DFU_ON_BOOT=0
-DCO -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: ;; 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 = 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 https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -285,7 +333,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m} board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D WLED_DISABLE_BLYNK #-DWLED_DISABLE_2D build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 #-DWLED_DISABLE_2D
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
monitor_filters = esp8266_exception_decoder monitor_filters = esp8266_exception_decoder
@ -295,7 +343,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k} board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02 -D WLED_DISABLE_BLYNK build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:esp01_1m_full] [env:esp01_1m_full]
@ -304,7 +352,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k} board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA -D WLED_DISABLE_BLYNK build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:esp07] [env:esp07]
@ -350,7 +398,7 @@ board = esp32dev
platform = ${esp32.platform} platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages} platform_packages = ${esp32.platform_packages}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 -D WLED_DISABLE_BLYNK #-D WLED_DISABLE_BROWNOUT_DET build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 #-D WLED_DISABLE_BROWNOUT_DET
lib_deps = ${esp32.lib_deps} lib_deps = ${esp32.lib_deps}
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions} board_build.partitions = ${esp32.default_partitions}
@ -360,20 +408,35 @@ board = esp32dev
platform = ${esp32.platform} platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages} platform_packages = ${esp32.platform_packages}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_qio80 -D WLED_DISABLE_BLYNK #-D WLED_DISABLE_BROWNOUT_DET build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_qio80 #-D WLED_DISABLE_BROWNOUT_DET
lib_deps = ${esp32.lib_deps} lib_deps = ${esp32.lib_deps}
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions} board_build.partitions = ${esp32.default_partitions}
board_build.f_flash = 80000000L board_build.f_flash = 80000000L
board_build.flash_mode = qio 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] [env:esp32_eth]
board = esp32-poe board = esp32-poe
platform = ${esp32.platform} platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages} platform_packages = ${esp32.platform_packages}
upload_speed = 921600 upload_speed = 921600
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1 -D WLED_DISABLE_BLYNK build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1
lib_deps = ${esp32.lib_deps} lib_deps = ${esp32.lib_deps}
board_build.partitions = ${esp32.default_partitions} board_build.partitions = ${esp32.default_partitions}
@ -387,17 +450,20 @@ board_build.flash_mode = qio
upload_speed = 460800 upload_speed = 460800
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=S2_saola build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=S2_saola
-DARDUINO_USB_CDC_ON_BOOT=1
lib_deps = ${esp32s2.lib_deps} lib_deps = ${esp32s2.lib_deps}
[env:esp32c3] [env:esp32c3dev]
platform = espressif32@5.1.1 ;; well-tested on -C3, good compatibility with WLED extends = esp32c3
; platform = espressif32@~5.2.0 ;; might help in case you experience bootloops due to corrupted flash filesystem platform = ${esp32c3.platform}
platform_packages = ${esp32c3.platform_packages}
framework = arduino framework = arduino
board = esp32-c3-devkitm-1 board = esp32-c3-devkitm-1
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3 build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3
-D WLED_WATCHDOG_TIMEOUT=0 -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 upload_speed = 460800
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
lib_deps = ${esp32c3.lib_deps} lib_deps = ${esp32c3.lib_deps}
@ -405,14 +471,14 @@ lib_deps = ${esp32c3.lib_deps}
[env:esp32s3dev_8MB] [env:esp32s3dev_8MB]
;; ESP32-S3-DevKitC-1 development board, with 8MB FLASH, no PSRAM (flash_mode: qio) ;; ESP32-S3-DevKitC-1 development board, with 8MB FLASH, no PSRAM (flash_mode: qio)
board = esp32-s3-devkitc-1 board = esp32-s3-devkitc-1
platform = espressif32@5.1.1 platform = ${esp32s3.platform}
platform_packages = platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600 ; or 460800 upload_speed = 921600 ; or 460800
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} build_flags = ${common.build_flags} ${esp32s3.build_flags}
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 -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=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=1 ;; -D ARDUINO_USB_MODE=0 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
;-D WLED_DEBUG ;-D WLED_DEBUG
lib_deps = ${esp32s3.lib_deps} lib_deps = ${esp32s3.lib_deps}
board_build.partitions = tools/WLED_ESP32_8MB.csv 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 = 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 = esp32s3box ; -> error: 'esp32_adc2gpio' was not declared in this scope
board = esp32-s3-devkitc-1 ; -> compiles, but does not support PSRAM board = esp32-s3-devkitc-1 ; -> compiles, but does not support PSRAM
platform = espressif32 @ ~5.2.0 platform = ${esp32s3.platform}
platform_packages = platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600 upload_speed = 921600
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} build_flags = ${common.build_flags} ${esp32s3.build_flags}
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 -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_RELEASE_NAME=ESP32-S3_PSRAM
-D WLED_USE_PSRAM -DBOARD_HAS_PSRAM ; tells WLED that PSRAM shall be used -D WLED_USE_PSRAM -DBOARD_HAS_PSRAM ; tells WLED that PSRAM shall be used
lib_deps = ${esp32s3.lib_deps} 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} lib_deps = ${esp8266.lib_deps}
[env:lolin_s2_mini] [env:lolin_s2_mini]
platform = espressif32@5.1.1 platform = ${esp32s2.platform}
platform_packages = ${esp32s2.platform_packages}
board = lolin_s2_mini board = lolin_s2_mini
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv 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 build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=LolinS2
-DBOARD_HAS_PSRAM -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_USE_PSRAM
-D WLED_WATCHDOG_TIMEOUT=0 -D WLED_WATCHDOG_TIMEOUT=0
-D CONFIG_ASYNC_TCP_USE_WDT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0
@ -530,9 +601,28 @@ lib_deps = ${esp32s2.lib_deps}
# custom board configurations # 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] [env:wemos_shield_esp32]
board = esp32dev board = esp32dev
platform = espressif32@3.2 platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
upload_speed = 460800 upload_speed = 460800
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} build_flags = ${common.build_flags_esp32}
@ -557,7 +647,8 @@ board = esp32dev
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D LEDPIN=27 -D BTNPIN=39 build_flags = ${common.build_flags_esp32} -D LEDPIN=27 -D BTNPIN=39
lib_deps = ${esp32.lib_deps} lib_deps = ${esp32.lib_deps}
platform = espressif32@3.2 platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
board_build.partitions = ${esp32.default_partitions} board_build.partitions = ${esp32.default_partitions}
[env:sp501e] [env:sp501e]
@ -574,20 +665,58 @@ board_build.ldscript = ${common.ldscript_2m512k}
build_flags = ${common.build_flags_esp8266} -D LEDPIN=3 -D BTNPIN=2 -D IRPIN=5 -D WLED_MAX_BUTTONS=3 build_flags = ${common.build_flags_esp8266} -D LEDPIN=3 -D BTNPIN=2 -D IRPIN=5 -D WLED_MAX_BUTTONS=3
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:athom7w] [env:Athom_RGBCW] ;7w and 5w(GU10) bulbs
board = esp_wroom_02 board = esp8285
platform = ${common.platform_wled_default} platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k} board_build.ldscript = ${common.ldscript_2m512k}
build_flags = ${common.build_flags_esp8266} -D WLED_MAX_CCT_BLEND=0 -D BTNPIN=-1 -D IRPIN=-1 -D WLED_DISABLE_INFRARED build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,13,5
-D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:athom15w]
board = esp_wroom_02 [env:Athom_15w_RGBCW] ;15w bulb
board = esp8285
platform = ${common.platform_wled_default} platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k} board_build.ldscript = ${common.ldscript_2m512k}
build_flags = ${common.build_flags_esp8266} -D WLED_USE_IC_CCT -D BTNPIN=-1 -D IRPIN=-1 -D WLED_DISABLE_INFRARED build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,5,13
-D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 -D WLED_USE_IC_CCT
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:Athom_3Pin_Controller] ;small controller with only data
board = esp8285
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=0 -D RLYPIN=-1 -D LEDPIN=1 -D WLED_DISABLE_INFRARED
lib_deps = ${esp8266.lib_deps}
[env:Athom_4Pin_Controller] ; With clock and data interface
board = esp8285
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=0 -D RLYPIN=12 -D LEDPIN=1 -D WLED_DISABLE_INFRARED
lib_deps = ${esp8266.lib_deps}
[env:Athom_5Pin_Controller] ;Analog light strip controller
board = esp8285
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=0 -D RLYPIN=-1 DATA_PINS=4,12,14,13 -D WLED_DISABLE_INFRARED
lib_deps = ${esp8266.lib_deps}
[env:MY9291] [env:MY9291]
board = esp01_1m board = esp01_1m
platform = ${common.platform_wled_default} platform = ${common.platform_wled_default}
@ -599,10 +728,10 @@ lib_deps = ${esp8266.lib_deps}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# codm pixel controller board configurations # 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 board = esp_wroom_02
platform = ${common.platform_wled_default} platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
@ -611,7 +740,7 @@ build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:codm-controller-0.6-rev2] [env:codm-controller-0_6-rev2]
board = esp_wroom_02 board = esp_wroom_02
platform = ${common.platform_wled_default} platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
@ -625,7 +754,8 @@ lib_deps = ${esp8266.lib_deps}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
[env:elekstube_ips] [env:elekstube_ips]
board = esp32dev board = esp32dev
platform = espressif32@3.2 platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
upload_speed = 921600 upload_speed = 921600
build_flags = ${common.build_flags_esp32} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED build_flags = ${common.build_flags_esp32} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED
-D USERMOD_RTC -D USERMOD_RTC
@ -633,7 +763,6 @@ build_flags = ${common.build_flags_esp32} -D WLED_DISABLE_BROWNOUT_DET -D WLED_D
-D LEDPIN=12 -D LEDPIN=12
-D RLYPIN=27 -D RLYPIN=27
-D BTNPIN=34 -D BTNPIN=34
-D WLED_DISABLE_BLYNK
-D DEFAULT_LED_COUNT=6 -D DEFAULT_LED_COUNT=6
# Display config # Display config
-D ST7789_DRIVER -D ST7789_DRIVER

View File

@ -26,7 +26,6 @@ build_flags = ${common.build_flags_esp8266}
; disable specific features ; disable specific features
; -D WLED_DISABLE_OTA ; -D WLED_DISABLE_OTA
; -D WLED_DISABLE_ALEXA ; -D WLED_DISABLE_ALEXA
; -D WLED_DISABLE_BLYNK
; -D WLED_DISABLE_HUESYNC ; -D WLED_DISABLE_HUESYNC
; -D WLED_DISABLE_INFRARED ; -D WLED_DISABLE_INFRARED
; -D WLED_DISABLE_WEBSOCKETS ; -D WLED_DISABLE_WEBSOCKETS

View File

@ -35,7 +35,6 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control
- WLED app for [Android](https://play.google.com/store/apps/details?id=com.aircoookie.WLED) and [iOS](https://apps.apple.com/us/app/wled/id1475695033) - WLED app for [Android](https://play.google.com/store/apps/details?id=com.aircoookie.WLED) and [iOS](https://apps.apple.com/us/app/wled/id1475695033)
- JSON and HTTP request APIs - JSON and HTTP request APIs
- MQTT - MQTT
- Blynk IoT
- E1.31, Art-Net, DDP and TPM2.net - E1.31, Art-Net, DDP and TPM2.net
- [diyHue](https://github.com/diyhue/diyHue) (Wled is supported by diyHue, including Hue Sync Entertainment under udp. Thanks to [Gregory Mallios](https://github.com/gmallios)) - [diyHue](https://github.com/diyhue/diyHue) (Wled is supported by diyHue, including Hue Sync Entertainment under udp. Thanks to [Gregory Mallios](https://github.com/gmallios))
- [Hyperion](https://github.com/hyperion-project/hyperion.ng) - [Hyperion](https://github.com/hyperion-project/hyperion.ng)

View File

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

@ -220,6 +220,9 @@ function writeChunks(srcDir, specs, resultFile) {
writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h", 'index'); writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h", 'index');
writeHtmlGzipped("wled00/data/simple.htm", "wled00/html_simple.h", 'simple'); 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( writeChunks(
"wled00/data", "wled00/data",
@ -387,12 +390,6 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()=====";
method: "gzip", method: "gzip",
filter: "html-minify", filter: "html-minify",
}, },
{
file: "liveviewws.htm",
name: "PAGE_liveviewws",
method: "gzip",
filter: "html-minify",
},
{ {
file: "liveviewws2D.htm", file: "liveviewws2D.htm",
name: "PAGE_liveviewws2D", name: "PAGE_liveviewws2D",

View File

@ -8,7 +8,6 @@
#pragma once #pragma once
#include "wled.h" #include "wled.h"
#include <Wire.h>
#include <BH1750.h> #include <BH1750.h>
// the max frequency to check photoresistor, 10 seconds // the max frequency to check photoresistor, 10 seconds
@ -56,15 +55,6 @@ private:
static const char _offset[]; static const char _offset[];
static const char _HomeAssistantDiscovery[]; 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 initDone = false;
bool sensorFound = false; bool sensorFound = false;
@ -123,14 +113,7 @@ private:
public: public:
void setup() 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 if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
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]);
sensorFound = lightMeter.begin(); sensorFound = lightMeter.begin();
initDone = true; initDone = true;
} }
@ -190,7 +173,9 @@ public:
user = root.createNestedObject(F("u")); user = root.createNestedObject(F("u"));
JsonArray lux_json = user.createNestedArray(F("Luminance")); JsonArray lux_json = user.createNestedArray(F("Luminance"));
if (!sensorFound) { if (!enabled) {
lux_json.add(F("disabled"));
} else if (!sensorFound) {
// if no sensor // if no sensor
lux_json.add(F("BH1750 ")); lux_json.add(F("BH1750 "));
lux_json.add(F("Not Found")); lux_json.add(F("Not Found"));
@ -216,9 +201,6 @@ public:
top[FPSTR(_minReadInterval)] = minReadingInterval; top[FPSTR(_minReadInterval)] = minReadingInterval;
top[FPSTR(_HomeAssistantDiscovery)] = HomeAssistantDiscovery; top[FPSTR(_HomeAssistantDiscovery)] = HomeAssistantDiscovery;
top[FPSTR(_offset)] = offset; 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.")); DEBUG_PRINTLN(F("BH1750 config saved."));
} }
@ -226,8 +208,6 @@ public:
// called before setup() to populate properties from values stored in cfg.json // called before setup() to populate properties from values stored in cfg.json
bool readFromConfig(JsonObject &root) 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. // we look for JSON object.
JsonObject top = root[FPSTR(_name)]; JsonObject top = root[FPSTR(_name)];
if (top.isNull()) if (top.isNull())
@ -244,27 +224,12 @@ public:
configComplete &= getJsonValue(top[FPSTR(_minReadInterval)], minReadingInterval, 500); //ms configComplete &= getJsonValue(top[FPSTR(_minReadInterval)], minReadingInterval, 500); //ms
configComplete &= getJsonValue(top[FPSTR(_HomeAssistantDiscovery)], HomeAssistantDiscovery, false); configComplete &= getJsonValue(top[FPSTR(_HomeAssistantDiscovery)], HomeAssistantDiscovery, false);
configComplete &= getJsonValue(top[FPSTR(_offset)], offset, 1); 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)); DEBUG_PRINT(FPSTR(_name));
if (!initDone) { if (!initDone) {
// first run: reading from cfg.json
for (byte i=0; i<2; i++) ioPin[i] = newPin[i];
DEBUG_PRINTLN(F(" config loaded.")); DEBUG_PRINTLN(F(" config loaded."));
} else { } else {
DEBUG_PRINTLN(F(" config (re)loaded.")); 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; return configComplete;

View File

@ -9,7 +9,6 @@
#include "wled.h" #include "wled.h"
#include <Arduino.h> #include <Arduino.h>
#include <Wire.h>
#include <BME280I2C.h> // BME280 sensor #include <BME280I2C.h> // BME280 sensor
#include <EnvironmentCalculations.h> // BME280 extended measurements #include <EnvironmentCalculations.h> // BME280 extended measurements
@ -34,7 +33,6 @@ private:
#ifdef ESP8266 #ifdef ESP8266
//uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8 //uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8
#endif #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; bool initDone = false;
// BME280 sensor settings // BME280 sensor settings
@ -186,13 +184,7 @@ private:
public: public:
void setup() void setup()
{ {
bool HW_Pins_Used = (ioPin[0]==i2c_scl && ioPin[1]==i2c_sda); // note whether architecture-based hardware SCL/SDA pins used if (i2c_scl<0 || i2c_sda<0) { enabled = false; sensorType = 0; return; }
PinOwner po = PinOwner::UM_BME280; // defaults to being pinowner for SCL/SDA pins
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } }; // allocate pins
if (HW_Pins_Used) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
if (!pinManager.allocateMultiplePins(pins, 2, po)) { sensorType=0; return; }
Wire.begin(ioPin[1], ioPin[0]);
if (!bme.begin()) if (!bme.begin())
{ {
@ -415,9 +407,6 @@ public:
top[F("PublishAlways")] = PublishAlways; top[F("PublishAlways")] = PublishAlways;
top[F("UseCelsius")] = UseCelsius; top[F("UseCelsius")] = UseCelsius;
top[F("HomeAssistantDiscovery")] = HomeAssistantDiscovery; 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.")); 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 // 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) // 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)]; JsonObject top = root[FPSTR(_name)];
if (top.isNull()) { if (top.isNull()) {
DEBUG_PRINT(F(_name)); DEBUG_PRINT(F(_name));
@ -447,27 +434,14 @@ public:
configComplete &= getJsonValue(top[F("PublishAlways")], PublishAlways, false); configComplete &= getJsonValue(top[F("PublishAlways")], PublishAlways, false);
configComplete &= getJsonValue(top[F("UseCelsius")], UseCelsius, true); configComplete &= getJsonValue(top[F("UseCelsius")], UseCelsius, true);
configComplete &= getJsonValue(top[F("HomeAssistantDiscovery")], HomeAssistantDiscovery, false); 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)); DEBUG_PRINT(FPSTR(_name));
if (!initDone) { if (!initDone) {
// first run: reading from cfg.json // first run: reading from cfg.json
for (byte i=0; i<2; i++) ioPin[i] = newPin[i];
DEBUG_PRINTLN(F(" config loaded.")); DEBUG_PRINTLN(F(" config loaded."));
} else { } else {
DEBUG_PRINTLN(F(" config (re)loaded.")); DEBUG_PRINTLN(F(" config (re)loaded."));
// changing parameters from settings page // 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; return configComplete;

View File

@ -86,6 +86,33 @@
#define USERMOD_BATTERY_LION_CALIBRATION 0 #define USERMOD_BATTERY_LION_CALIBRATION 0
#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
// a common capacity for single 18650 battery cells is between 2500 and 3600 mAh
#ifndef USERMOD_BATTERY_TOTAL_CAPACITY
#define USERMOD_BATTERY_TOTAL_CAPACITY 3100
#endif
// offset or calibration value to fine tune the calculated voltage
#ifndef USERMOD_BATTERY_CALIBRATION
#define USERMOD_BATTERY_CALIBRATION 0
#endif
// calculate remaining time / the time that is left before the battery runs out of power
// #ifndef USERMOD_BATTERY_CALCULATE_TIME_LEFT_ENABLED
// #define USERMOD_BATTERY_CALCULATE_TIME_LEFT_ENABLED false
// #endif
// auto-off feature // auto-off feature
#ifndef USERMOD_BATTERY_AUTO_OFF_ENABLED #ifndef USERMOD_BATTERY_AUTO_OFF_ENABLED

View File

@ -27,6 +27,28 @@ class UsermodBattery : public Usermod
unsigned long readingInterval = USERMOD_BATTERY_MEASUREMENT_INTERVAL; unsigned long readingInterval = USERMOD_BATTERY_MEASUREMENT_INTERVAL;
unsigned long nextReadTime = 0; unsigned long nextReadTime = 0;
unsigned long lastReadTime = 0; unsigned long lastReadTime = 0;
// battery min. voltage
float minBatteryVoltage = USERMOD_BATTERY_MIN_VOLTAGE;
// battery max. voltage
float maxBatteryVoltage = USERMOD_BATTERY_MAX_VOLTAGE;
// all battery cells summed up
unsigned int totalBatteryCapacity = USERMOD_BATTERY_TOTAL_CAPACITY;
// raw analog reading
float rawValue = 0.0f;
// calculated voltage
float voltage = maxBatteryVoltage;
// between 0 and 1, to control strength of voltage smoothing filter
float alpha = 0.05f;
// multiplier for the voltage divider that is in place between ADC pin and battery, default will be 2 but might be adapted to readout voltages over ~5v ESP32 or ~6.6v ESP8266
float voltageMultiplier = USERMOD_BATTERY_VOLTAGE_MULTIPLIER;
// mapped battery level based on voltage
int8_t batteryLevel = 100;
// offset or calibration value to fine tune the calculated voltage
float calibration = USERMOD_BATTERY_CALIBRATION;
// time left estimation feature
// bool calculateTimeLeftEnabled = USERMOD_BATTERY_CALCULATE_TIME_LEFT_ENABLED;
// float estimatedTimeLeft = 0.0;
// auto shutdown/shutoff/master off feature // auto shutdown/shutoff/master off feature
bool autoOffEnabled = USERMOD_BATTERY_AUTO_OFF_ENABLED; bool autoOffEnabled = USERMOD_BATTERY_AUTO_OFF_ENABLED;
@ -92,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: public:
//Functions called by WLED //Functions called by WLED
@ -108,6 +141,7 @@ class UsermodBattery : public Usermod
if (pinManager.allocatePin(batteryPin, false, PinOwner::UM_Battery)) { if (pinManager.allocatePin(batteryPin, false, PinOwner::UM_Battery)) {
DEBUG_PRINTLN(F("Battery pin allocation succeeded.")); DEBUG_PRINTLN(F("Battery pin allocation succeeded."));
success = true; success = true;
voltage = readVoltage();
} }
if (!success) { if (!success) {
@ -117,8 +151,8 @@ class UsermodBattery : public Usermod
pinMode(batteryPin, INPUT); pinMode(batteryPin, INPUT);
} }
#else //ESP8266 boards have only one analog input pin A0 #else //ESP8266 boards have only one analog input pin A0
pinMode(batteryPin, INPUT); pinMode(batteryPin, INPUT);
voltage = readVoltage();
#endif #endif
//this could also be handled with a factory class but for only 2 types it should be sufficient for now //this could also be handled with a factory class but for only 2 types it should be sufficient for now
@ -184,6 +218,14 @@ class UsermodBattery : public Usermod
// calculate the voltage // calculate the voltage
voltage = ((rawValue / getAdcPrecision()) * bat->getMaxVoltage()) + bat->getCalibration(); voltage = ((rawValue / getAdcPrecision()) * bat->getMaxVoltage()) + bat->getCalibration();
#endif #endif
// initializing = false;
// rawValue = readVoltage();
// // filter with exponential smoothing because ADC in esp32 is fluctuating too much for a good single readout
// voltage = voltage + alpha * (rawValue - voltage);
// check if voltage is within specified voltage range, allow 10% over/under voltage - removed cause this just makes it hard for people to troubleshoot as the voltage in the web gui will say invalid instead of displaying a voltage
//voltage = ((voltage < minBatteryVoltage * 0.85f) || (voltage > maxBatteryVoltage * 1.1f)) ? -1.0f : voltage;
bat->setVoltage(voltage); bat->setVoltage(voltage);
// translate battery voltage into percentage // translate battery voltage into percentage
@ -380,8 +422,19 @@ class UsermodBattery : public Usermod
battery[F("pin")] = batteryPin; battery[F("pin")] = batteryPin;
#endif #endif
// battery[F("time-left")] = calculateTimeLeftEnabled;
battery[F("min-voltage")] = minBatteryVoltage;
battery[F("max-voltage")] = maxBatteryVoltage;
battery[F("capacity")] = totalBatteryCapacity;
battery[F("calibration")] = calibration;
battery[F("voltage-multiplier")] = voltageMultiplier;
battery[FPSTR(_readInterval)] = readingInterval;
addBatteryToJsonObject(battery, false); addBatteryToJsonObject(battery, false);
// read voltage in case calibration or voltage multiplier changed to see immediate effect
voltage = readVoltage();
DEBUG_PRINTLN(F("Battery config saved.")); DEBUG_PRINTLN(F("Battery config saved."));
} }
@ -447,6 +500,13 @@ class UsermodBattery : public Usermod
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
newBatteryPin = battery[F("pin")] | newBatteryPin; newBatteryPin = battery[F("pin")] | newBatteryPin;
#endif #endif
// calculateTimeLeftEnabled = battery[F("time-left")] | calculateTimeLeftEnabled;
setMinBatteryVoltage(battery[F("min-voltage")] | minBatteryVoltage);
setMaxBatteryVoltage(battery[F("max-voltage")] | maxBatteryVoltage);
setTotalBatteryCapacity(battery[F("capacity")] | totalBatteryCapacity);
setCalibration(battery[F("calibration")] | calibration);
setVoltageMultiplier(battery[F("voltage-multiplier")] | voltageMultiplier);
setReadingInterval(battery[FPSTR(_readInterval)] | readingInterval);
getUsermodConfigFromJsonObject(battery); getUsermodConfigFromJsonObject(battery);
@ -554,6 +614,117 @@ class UsermodBattery : public Usermod
#endif #endif
} }
/*
/*
* Get lowest configured battery voltage
*/
float getMinBatteryVoltage()
{
return minBatteryVoltage;
}
/*
* Set lowest battery voltage
* can't be below 0 volt
*/
void setMinBatteryVoltage(float voltage)
{
minBatteryVoltage = max(0.0f, voltage);
}
/*
* Get highest configured battery voltage
*/
float getMaxBatteryVoltage()
{
return maxBatteryVoltage;
}
/*
* Set highest battery voltage
* can't be below minBatteryVoltage
*/
void setMaxBatteryVoltage(float voltage)
{
#ifdef USERMOD_BATTERY_USE_LIPO
maxBatteryVoltage = max(getMinBatteryVoltage()+0.7f, voltage);
#else
maxBatteryVoltage = max(getMinBatteryVoltage()+1.0f, voltage);
#endif
}
/*
* Get the capacity of all cells in parralel sumed up
* unit: mAh
*/
unsigned int getTotalBatteryCapacity()
{
return totalBatteryCapacity;
}
void setTotalBatteryCapacity(unsigned int capacity)
{
totalBatteryCapacity = capacity;
}
/*
* Get the calculated voltage
* formula: (adc pin value / adc precision * max voltage) + calibration
*/
float getVoltage()
{
return voltage;
}
/*
* Get the mapped battery level (0 - 100) based on voltage
* important: voltage can drop when a load is applied, so its only an estimate
*/
int8_t getBatteryLevel()
{
return batteryLevel;
}
/*
* Get the configured calibration value
* a offset value to fine-tune the calculated voltage.
*/
float getCalibration()
{
return calibration;
}
/*
* Set the voltage calibration offset value
* a offset value to fine-tune the calculated voltage.
*/
void setCalibration(float offset)
{
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 * Get auto-off feature enabled status
* is auto-off enabled, true/false * is auto-off enabled, true/false

View File

@ -22,8 +22,12 @@
//class name. Use something descriptive and leave the ": public Usermod" part :) //class name. Use something descriptive and leave the ": public Usermod" part :)
class MyExampleUsermod : public Usermod { class MyExampleUsermod : public Usermod {
private: private:
// Private class members. You can declare variables and functions only accessible to your usermod here // Private class members. You can declare variables and functions only accessible to your usermod here
bool enabled = false;
bool initDone = false;
unsigned long lastTime = 0; unsigned long lastTime = 0;
// set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer) // set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer)
@ -37,15 +41,56 @@ class MyExampleUsermod : public Usermod {
long testLong; long testLong;
int8_t testPins[2]; int8_t testPins[2];
// string that are used multiple time (this will save some flash memory)
static const char _name[];
static const char _enabled[];
// any private methods should go here (non-inline methosd should be defined out of class)
void publishMqtt(const char* state, bool retain = false); // example for publishing MQTT message
public: public:
//Functions called by WLED
// non WLED related methods, may be used for data exchange between usermods (non-inline methods should be defined out of class)
/**
* Enable/Disable the usermod
*/
inline void enable(bool enable) { enabled = enable; }
/**
* Get usermod enabled/disabled state
*/
inline bool isEnabled() { return enabled; }
// in such case add the following to another usermod:
// in private vars:
// #ifdef USERMOD_EXAMPLE
// MyExampleUsermod* UM;
// #endif
// in setup()
// #ifdef USERMOD_EXAMPLE
// UM = (MyExampleUsermod*) usermods.lookup(USERMOD_ID_EXAMPLE);
// #endif
// somewhere in loop() or other member method
// #ifdef USERMOD_EXAMPLE
// if (UM != nullptr) isExampleEnabled = UM->isEnabled();
// if (!isExampleEnabled) UM->enable(true);
// #endif
// methods called by WLED (can be inlined as they are called only once but if you call them explicitly define them out of class)
/* /*
* setup() is called once at boot. WiFi is not yet connected at this point. * setup() is called once at boot. WiFi is not yet connected at this point.
* readFromConfig() is called prior to setup()
* You can use it to initialize variables, sensors or similar. * You can use it to initialize variables, sensors or similar.
*/ */
void setup() { void setup() {
// do your set-up here
//Serial.println("Hello from my usermod!"); //Serial.println("Hello from my usermod!");
initDone = true;
} }
@ -69,6 +114,11 @@ class MyExampleUsermod : public Usermod {
* Instead, use a timer check as shown here. * Instead, use a timer check as shown here.
*/ */
void loop() { void loop() {
// if usermod is disabled or called during strip updating just exit
// NOTE: on very long strips strip.isUpdating() may always return true so update accordingly
if (!enabled || strip.isUpdating()) return;
// do your magic here
if (millis() - lastTime > 1000) { if (millis() - lastTime > 1000) {
//Serial.println("I'm alive!"); //Serial.println("I'm alive!");
lastTime = millis(); lastTime = millis();
@ -81,19 +131,25 @@ class MyExampleUsermod : public Usermod {
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. * 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 * Below it is shown how this could be used for e.g. a light sensor
*/ */
/*
void addToJsonInfo(JsonObject& root) void addToJsonInfo(JsonObject& root)
{ {
int reading = 20; // if "u" object does not exist yet wee need to create it
//this code adds "u":{"Light":[20," lux"]} to the info object
JsonObject user = root["u"]; JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u"); if (user.isNull()) user = root.createNestedObject("u");
JsonArray lightArr = user.createNestedArray("Light"); //name //this code adds "u":{"ExampleUsermod":[20," lux"]} to the info object
lightArr.add(reading); //value //int reading = 20;
lightArr.add(" lux"); //unit //JsonArray lightArr = user.createNestedArray(FPSTR(_name))); //name
//lightArr.add(reading); //value
//lightArr.add(F(" lux")); //unit
// if you are implementing a sensor usermod, you may publish sensor data
//JsonObject sensor = root[F("sensor")];
//if (sensor.isNull()) sensor = root.createNestedObject(F("sensor"));
//temp = sensor.createNestedArray(F("light"));
//temp.add(reading);
//temp.add(F("lux"));
} }
*/
/* /*
@ -102,7 +158,12 @@ class MyExampleUsermod : public Usermod {
*/ */
void addToJsonState(JsonObject& root) void addToJsonState(JsonObject& root)
{ {
//root["user0"] = userVar0; if (!initDone || !enabled) return; // prevent crash on boot applyPreset()
JsonObject usermod = root[FPSTR(_name)];
if (usermod.isNull()) usermod = root.createNestedObject(FPSTR(_name));
//usermod["user0"] = userVar0;
} }
@ -112,7 +173,14 @@ class MyExampleUsermod : public Usermod {
*/ */
void readFromJsonState(JsonObject& root) void readFromJsonState(JsonObject& root)
{ {
userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value if (!initDone) return; // prevent crash on boot applyPreset()
JsonObject usermod = root[FPSTR(_name)];
if (!usermod.isNull()) {
// expect JSON usermod data in usermod name object: {"ExampleUsermod:{"user0":10}"}
userVar0 = usermod["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
}
// you can as well check WLED state JSON keys
//if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!")); //if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
} }
@ -154,8 +222,10 @@ class MyExampleUsermod : public Usermod {
*/ */
void addToConfig(JsonObject& root) void addToConfig(JsonObject& root)
{ {
JsonObject top = root.createNestedObject("exampleUsermod"); JsonObject top = root.createNestedObject(FPSTR(_name));
top["great"] = userVar0; //save these vars persistently whenever settings are saved top[FPSTR(_enabled)] = enabled;
//save these vars persistently whenever settings are saved
top["great"] = userVar0;
top["testBool"] = testBool; top["testBool"] = testBool;
top["testInt"] = testInt; top["testInt"] = testInt;
top["testLong"] = testLong; top["testLong"] = testLong;
@ -188,7 +258,7 @@ class MyExampleUsermod : public Usermod {
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor // 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) // 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["exampleUsermod"]; JsonObject top = root[FPSTR(_name)];
bool configComplete = !top.isNull(); bool configComplete = !top.isNull();
@ -201,6 +271,8 @@ class MyExampleUsermod : public Usermod {
// A 3-argument getJsonValue() assigns the 3rd argument as a default value if the Json value is missing // A 3-argument getJsonValue() assigns the 3rd argument as a default value if the Json value is missing
configComplete &= getJsonValue(top["testInt"], testInt, 42); configComplete &= getJsonValue(top["testInt"], testInt, 42);
configComplete &= getJsonValue(top["testLong"], testLong, -42424242); configComplete &= getJsonValue(top["testLong"], testLong, -42424242);
// "pin" fields have special handling in settings page (or some_pin as well)
configComplete &= getJsonValue(top["pin"][0], testPins[0], -1); configComplete &= getJsonValue(top["pin"][0], testPins[0], -1);
configComplete &= getJsonValue(top["pin"][1], testPins[1], -1); configComplete &= getJsonValue(top["pin"][1], testPins[1], -1);
@ -208,6 +280,21 @@ class MyExampleUsermod : public Usermod {
} }
/*
* appendConfigData() is called when user enters usermod settings page
* it may add additional metadata for certain entry fields (adding drop down is possible)
* be careful not to add too much as oappend() buffer is limited to 3k
*/
void appendConfigData()
{
oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":great")); oappend(SET_F("',1,'<i>(this is a great config value)</i>');"));
oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":testString")); oappend(SET_F("',1,'enter any string you want');"));
oappend(SET_F("dd=addDropdown('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F("','testInt');"));
oappend(SET_F("addOption(dd,'Nothing',0);"));
oappend(SET_F("addOption(dd,'Everything',42);"));
}
/* /*
* handleOverlayDraw() is called just before every show() (LED strip update frame) after effects have set the colors. * handleOverlayDraw() is called just before every show() (LED strip update frame) after effects have set the colors.
* Use this to blank out some LEDs or set them to a different color regardless of the set effect mode. * Use this to blank out some LEDs or set them to a different color regardless of the set effect mode.
@ -219,6 +306,71 @@ class MyExampleUsermod : public Usermod {
} }
/**
* 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) {
yield();
// ignore certain button types as they may have other consequences
if (!enabled
|| buttonType[b] == BTN_TYPE_NONE
|| buttonType[b] == BTN_TYPE_RESERVED
|| buttonType[b] == BTN_TYPE_PIR_SENSOR
|| buttonType[b] == BTN_TYPE_ANALOG
|| buttonType[b] == BTN_TYPE_ANALOG_INVERTED) {
return false;
}
bool handled = false;
// do your button handling here
return handled;
}
#ifndef WLED_DISABLE_MQTT
/**
* handling of MQTT message
* topic only contains stripped topic (part after /wled/MAC)
*/
bool onMqttMessage(char* topic, char* payload) {
// check if we received a command
//if (strlen(topic) == 8 && strncmp_P(topic, PSTR("/command"), 8) == 0) {
// String action = payload;
// if (action == "on") {
// enabled = true;
// return true;
// } else if (action == "off") {
// enabled = false;
// return true;
// } else if (action == "toggle") {
// enabled = !enabled;
// return true;
// }
//}
return false;
}
/**
* onMqttConnect() is called when MQTT connection is established
*/
void onMqttConnect(bool sessionPresent) {
// do any MQTT related initialisation here
//publishMqtt("I am alive!");
}
#endif
/**
* onStateChanged() is used to detect WLED state change
* @mode parameter is CALL_MODE_... parameter used for notifications
*/
void onStateChange(uint8_t mode) {
// do something if WLED state changed (color, brightness, effect, preset, etc)
}
/* /*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!). * 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. * This could be used in the future for the system to determine whether your usermod is installed.
@ -231,3 +383,24 @@ class MyExampleUsermod : public Usermod {
//More methods can be added in the future, this example will then be extended. //More methods can be added in the future, this example will then be extended.
//Your usermod will remain compatible as it does not need to implement all methods from the Usermod base class! //Your usermod will remain compatible as it does not need to implement all methods from the Usermod base class!
}; };
// add more strings here to reduce flash memory usage
const char MyExampleUsermod::_name[] PROGMEM = "ExampleUsermod";
const char MyExampleUsermod::_enabled[] PROGMEM = "enabled";
// implementation of non-inline member methods
void MyExampleUsermod::publishMqtt(const char* state, bool retain)
{
#ifndef WLED_DISABLE_MQTT
//Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED) {
char subuf[64];
strcpy(subuf, mqttDeviceTopic);
strcat_P(subuf, PSTR("/example"));
mqtt->publish(subuf, 0, retain, state);
}
#endif
}

View File

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

View File

@ -20,7 +20,7 @@
* This usermod handles PIR sensor states. * This usermod handles PIR sensor states.
* The strip will be switched on and the off timer will be resetted when the sensor goes HIGH. * 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. * 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 * Usermods allow you to add own functionality to WLED more easily
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
@ -38,21 +38,21 @@ public:
~PIRsensorSwitch() {} ~PIRsensorSwitch() {}
//Enable/Disable the PIR sensor //Enable/Disable the PIR sensor
void EnablePIRsensor(bool en) { enabled = en; } inline void EnablePIRsensor(bool en) { enabled = en; }
// Get PIR sensor enabled/disabled state // Get PIR sensor enabled/disabled state
bool PIRsensorEnabled() { return enabled; } inline bool PIRsensorEnabled() { return enabled; }
private: private:
byte prevPreset = 0; byte prevPreset = 0;
byte prevPlaylist = 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 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 byte sensorPinState = LOW; // current PIR sensor pin state
bool initDone = false; // status of initialization bool initDone = false; // status of initialization
bool PIRtriggered = false;
unsigned long lastLoop = 0; unsigned long lastLoop = 0;
// configurable parameters // 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) // 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_offOnly = false;
bool m_offMode = offMode; bool m_offMode = offMode;
bool m_override = false;
// Home Assistant // Home Assistant
bool HomeAssistantDiscovery = false; // is HA discovery turned on bool HomeAssistantDiscovery = false; // is HA discovery turned on
@ -81,12 +82,122 @@ private:
static const char _offOnly[]; static const char _offOnly[];
static const char _haDiscovery[]; static const char _haDiscovery[];
static const char _notify[]; static const char _notify[];
static const char _override[];
/** /**
* check if it is daytime * check if it is daytime
* if sunrise/sunset is not defined (no NTP or lat/lon) default to nighttime * 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(); updateLocalTime();
uint8_t hr = hour(localTime); uint8_t hr = hour(localTime);
uint8_t mi = minute(localTime); uint8_t mi = minute(localTime);
@ -106,14 +217,12 @@ private:
return false; return false;
} }
/** void PIRsensorSwitch::switchStrip(bool switchOn)
* switch strip on/off
*/
void switchStrip(bool switchOn)
{ {
if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return; //if lights on and off only, do nothing 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 if (PIRtriggered && switchOn) return; //if already on and triggered before, do nothing
PIRtriggered = switchOn; PIRtriggered = switchOn;
DEBUG_PRINT(F("PIR: strip=")); DEBUG_PRINTLN(switchOn?"on":"off");
if (switchOn) { if (switchOn) {
if (m_onPreset) { if (m_onPreset) {
if (currentPlaylist>0 && !offMode) { if (currentPlaylist>0 && !offMode) {
@ -157,21 +266,19 @@ private:
} }
} }
void publishMqtt(const char* state) void PIRsensorSwitch::publishMqtt(const char* state)
{ {
#ifndef WLED_DISABLE_MQTT #ifndef WLED_DISABLE_MQTT
//Check if MQTT Connected, otherwise it will crash the 8266 //Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED) { if (WLED_MQTT_CONNECTED) {
char subuf[64]; char buf[64];
strcpy(subuf, mqttDeviceTopic); sprintf_P(buf, PSTR("%s/motion"), mqttDeviceTopic); //max length: 33 + 7 = 40
strcat_P(subuf, PSTR("/motion")); mqtt->publish(buf, 0, false, state);
mqtt->publish(subuf, 0, false, state);
} }
#endif #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 PIRsensorSwitch::publishHomeAssistantAutodiscovery()
void publishHomeAssistantAutodiscovery()
{ {
#ifndef WLED_DISABLE_MQTT #ifndef WLED_DISABLE_MQTT
if (WLED_MQTT_CONNECTED) { if (WLED_MQTT_CONNECTED) {
@ -206,11 +313,7 @@ private:
#endif #endif
} }
/** bool PIRsensorSwitch::updatePIRsensorState()
* Read and update PIR sensor state.
* Initilize/reset switch off timer
*/
bool updatePIRsensorState()
{ {
bool pinState = digitalRead(PIRsensorPin); bool pinState = digitalRead(PIRsensorPin);
if (pinState != sensorPinState) { if (pinState != sensorPinState) {
@ -231,10 +334,7 @@ private:
return false; return false;
} }
/** bool PIRsensorSwitch::handleOffTimer()
* switch off the strip if the delay has elapsed
*/
bool handleOffTimer()
{ {
if (offTimerStart > 0 && millis() - offTimerStart > m_switchOffDelay) { if (offTimerStart > 0 && millis() - offTimerStart > m_switchOffDelay) {
offTimerStart = 0; offTimerStart = 0;
@ -248,14 +348,9 @@ private:
return false; return false;
} }
public:
//Functions called by WLED //Functions called by WLED
/** void PIRsensorSwitch::setup()
* 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()
{ {
if (enabled) { if (enabled) {
// pin retrieved from cfg.json (readFromConfig()) prior to running setup() // pin retrieved from cfg.json (readFromConfig()) prior to running setup()
@ -274,27 +369,14 @@ public:
initDone = true; initDone = true;
} }
/** void PIRsensorSwitch::onMqttConnect(bool sessionPresent)
* 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) {
if (HomeAssistantDiscovery) { if (HomeAssistantDiscovery) {
publishHomeAssistantAutodiscovery(); publishHomeAssistantAutodiscovery();
} }
} }
/** void PIRsensorSwitch::loop()
* loop() is called continuously. Here you can check for events, read sensors, etc.
*/
void loop()
{ {
// only check sensors 4x/s // only check sensors 4x/s
if (!enabled || millis() - lastLoop < 250 || strip.isUpdating()) return; if (!enabled || millis() - lastLoop < 250 || strip.isUpdating()) return;
@ -305,12 +387,7 @@ public:
} }
} }
/** void PIRsensorSwitch::addToJsonInfo(JsonObject &root)
* 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)
{ {
JsonObject user = root["u"]; JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u"); if (user.isNull()) user = root.createNestedObject("u");
@ -370,22 +447,18 @@ public:
sensor[F("motion")] = sensorPinState || offTimerStart>0 ? true : false; sensor[F("motion")] = sensorPinState || offTimerStart>0 ? true : false;
} }
/** void PIRsensorSwitch::onStateChange(uint8_t mode) {
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). if (!initDone) return;
* Values in the state object may be modified by connected clients DEBUG_PRINT(F("PIR: offTimerStart=")); DEBUG_PRINTLN(offTimerStart);
*/ if (m_override && PIRtriggered && offTimerStart) { // debounce
/* // checking PIRtriggered and offTimerStart will prevent cancellation upon On trigger
void addToJsonState(JsonObject &root) DEBUG_PRINTLN(F("PIR: Canceled."));
{ offTimerStart = 0;
PIRtriggered = false;
}
} }
*/
/** void PIRsensorSwitch::readFromJsonState(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)
{ {
if (!initDone) return; // prevent crash on boot applyPreset() if (!initDone) return; // prevent crash on boot applyPreset()
JsonObject usermod = root[FPSTR(_name)]; JsonObject usermod = root[FPSTR(_name)];
@ -396,11 +469,7 @@ public:
} }
} }
void PIRsensorSwitch::addToConfig(JsonObject &root)
/**
* provide the changeable values
*/
void addToConfig(JsonObject &root)
{ {
JsonObject top = root.createNestedObject(FPSTR(_name)); JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled; top[FPSTR(_enabled)] = enabled;
@ -411,24 +480,20 @@ public:
top[FPSTR(_nightTime)] = m_nightTimeOnly; top[FPSTR(_nightTime)] = m_nightTimeOnly;
top[FPSTR(_mqttOnly)] = m_mqttOnly; top[FPSTR(_mqttOnly)] = m_mqttOnly;
top[FPSTR(_offOnly)] = m_offOnly; top[FPSTR(_offOnly)] = m_offOnly;
top[FPSTR(_override)] = m_override;
top[FPSTR(_haDiscovery)] = HomeAssistantDiscovery; top[FPSTR(_haDiscovery)] = HomeAssistantDiscovery;
top[FPSTR(_notify)] = (NotifyUpdateMode != CALL_MODE_NO_NOTIFY); top[FPSTR(_notify)] = (NotifyUpdateMode != CALL_MODE_NO_NOTIFY);
DEBUG_PRINTLN(F("PIR config saved.")); 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: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: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
} }
/** bool PIRsensorSwitch::readFromConfig(JsonObject &root)
* 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 oldEnabled = enabled; bool oldEnabled = enabled;
int8_t oldPin = PIRsensorPin; int8_t oldPin = PIRsensorPin;
@ -454,6 +519,7 @@ public:
m_nightTimeOnly = top[FPSTR(_nightTime)] | m_nightTimeOnly; m_nightTimeOnly = top[FPSTR(_nightTime)] | m_nightTimeOnly;
m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly; m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly;
m_offOnly = top[FPSTR(_offOnly)] | m_offOnly; m_offOnly = top[FPSTR(_offOnly)] | m_offOnly;
m_override = top[FPSTR(_override)] | m_override;
HomeAssistantDiscovery = top[FPSTR(_haDiscovery)] | HomeAssistantDiscovery; HomeAssistantDiscovery = top[FPSTR(_haDiscovery)] | HomeAssistantDiscovery;
NotifyUpdateMode = top[FPSTR(_notify)] ? CALL_MODE_DIRECT_CHANGE : CALL_MODE_NO_NOTIFY; NotifyUpdateMode = top[FPSTR(_notify)] ? CALL_MODE_DIRECT_CHANGE : CALL_MODE_NO_NOTIFY;
@ -483,27 +549,5 @@ public:
DEBUG_PRINTLN(F(" config (re)loaded.")); DEBUG_PRINTLN(F(" config (re)loaded."));
} }
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features // 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

@ -1,7 +1,7 @@
#pragma once #pragma once
#ifndef USERMOD_DALLASTEMPERATURE #if !defined(USERMOD_DALLASTEMPERATURE) && !defined(USERMOD_SHT)
#error The "PWM fan" usermod requires "Dallas Temeprature" usermod to function properly. #error The "PWM fan" usermod requires "Dallas Temeprature" or "SHT" usermod to function properly.
#endif #endif
#include "wled.h" #include "wled.h"
@ -42,6 +42,8 @@ class PWMFanUsermod : public Usermod {
#ifdef USERMOD_DALLASTEMPERATURE #ifdef USERMOD_DALLASTEMPERATURE
UsermodTemperature* tempUM; UsermodTemperature* tempUM;
#elif defined(USERMOD_SHT)
ShtUsermod* tempUM;
#endif #endif
// configurable parameters // configurable parameters
@ -145,7 +147,7 @@ class PWMFanUsermod : public Usermod {
} }
float getActualTemperature(void) { float getActualTemperature(void) {
#ifdef USERMOD_DALLASTEMPERATURE #if defined(USERMOD_DALLASTEMPERATURE) || defined(USERMOD_SHT)
if (tempUM != nullptr) if (tempUM != nullptr)
return tempUM->getTemperatureC(); return tempUM->getTemperatureC();
#endif #endif
@ -189,6 +191,8 @@ class PWMFanUsermod : public Usermod {
#ifdef USERMOD_DALLASTEMPERATURE #ifdef USERMOD_DALLASTEMPERATURE
// This Usermod requires Temperature usermod // This Usermod requires Temperature usermod
tempUM = (UsermodTemperature*) usermods.lookup(USERMOD_ID_TEMPERATURE); tempUM = (UsermodTemperature*) usermods.lookup(USERMOD_ID_TEMPERATURE);
#elif defined(USERMOD_SHT)
tempUM = (ShtUsermod*) usermods.lookup(USERMOD_ID_SHT);
#endif #endif
initTacho(); initTacho();
initPWMfan(); initPWMfan();

View File

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

View File

@ -17,12 +17,6 @@
#ifndef TFT_HEIGHT #ifndef TFT_HEIGHT
#error Please define TFT_HEIGHT #error Please define TFT_HEIGHT
#endif #endif
#ifndef TFT_MOSI
#error Please define TFT_MOSI
#endif
#ifndef TFT_SCLK
#error Please define TFT_SCLK
#endif
#ifndef TFT_DC #ifndef TFT_DC
#error Please define TFT_DC #error Please define TFT_DC
#endif #endif
@ -140,8 +134,14 @@ class St7789DisplayUsermod : public Usermod {
*/ */
void setup() 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 } }; PinManagerPinType spiPins[] = { { spi_mosi, true }, { spi_miso, false}, { spi_sclk, true } };
if (!pinManager.allocateMultiplePins(pins, 7, PinOwner::UM_FourLineDisplay)) { enabled = false; return; } 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.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. 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"); JsonObject top = root.createNestedObject("ST7789");
JsonArray pins = top.createNestedArray("pin"); JsonArray pins = top.createNestedArray("pin");
pins.add(TFT_MOSI);
pins.add(TFT_MISO);
pins.add(TFT_SCLK);
pins.add(TFT_CS); pins.add(TFT_CS);
pins.add(TFT_DC); pins.add(TFT_DC);
pins.add(TFT_RST); 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(). * 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) * 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; 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 class Si7021_MQTT_HA : public Usermod
{ {
private: private:
@ -184,7 +176,6 @@ class Si7021_MQTT_HA : public Usermod
{ {
if (enabled) { if (enabled) {
Serial.println("Si7021_MQTT_HA: Starting!"); Serial.println("Si7021_MQTT_HA: Starting!");
Wire.begin(SDA_PIN, SCL_PIN);
Serial.println("Si7021_MQTT_HA: Initializing sensors.. "); Serial.println("Si7021_MQTT_HA: Initializing sensors.. ");
_initializeSensor(); _initializeSensor();
} }

View File

@ -1,13 +1,12 @@
; Options ; Options
; ------- ; -------
; USERMOD_DALLASTEMPERATURE - define this to have this user mod included wled00\usermods_list.cpp ; 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_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] [env:d1_mini_usermod_dallas_temperature_C]
extends = env:d1_mini 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} lib_deps = ${env.lib_deps}
milesburton/DallasTemperature@^3.9.0 paulstoffregen/OneWire@~2.3.7
OneWire@~2.3.5 # 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. If temperature sensor is not detected during boot, this usermod will be disabled.
Maintained by @blazoncek
## Installation ## Installation
Copy the example `platformio_override.ini` to the root directory. This file should be placed in the same directory as `platformio.ini`. 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 ### Define Your Options
* `USERMOD_DALLASTEMPERATURE` - enables this user mod wled00/usermods_list.cpp * `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. 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 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`: If you are not using `platformio_override.ini`, you might have to uncomment `OneWire@~2.3.5 under` `[common]` section in `platformio.ini`:
```ini ```ini
@ -43,8 +44,9 @@ default_envs = d1_mini
lib_deps = lib_deps =
... ...
#For Dallas sensor uncomment following line #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 ## 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 * Report the number of seconds until the first read in the info screen instead of sensor error
2021-04 2021-04
* Adaptation for runtime configuration. * 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[]; static const char _parasitePin[];
//Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013 //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]; byte data[9];
int16_t result; // raw data from sensor int16_t result; // raw data from sensor
float retVal = -127.0f; float retVal = -127.0f;
@ -92,7 +132,7 @@ class UsermodTemperature : public Usermod {
return data[0]==0xFF ? -127.0f : retVal; return data[0]==0xFF ? -127.0f : retVal;
} }
void requestTemperatures() { void UsermodTemperature::requestTemperatures() {
DEBUG_PRINTLN(F("Requesting temperature.")); DEBUG_PRINTLN(F("Requesting temperature."));
oneWire->reset(); oneWire->reset();
oneWire->skip(); // skip ROM oneWire->skip(); // skip ROM
@ -102,7 +142,7 @@ class UsermodTemperature : public Usermod {
waitingForConversion = true; waitingForConversion = true;
} }
void readTemperature() { void UsermodTemperature::readTemperature() {
if (parasite && parasitePin >=0 ) digitalWrite(parasitePin, LOW); // deactivate power (close MOSFET) if (parasite && parasitePin >=0 ) digitalWrite(parasitePin, LOW); // deactivate power (close MOSFET)
temperature = readDallas(); temperature = readDallas();
lastMeasurement = millis(); lastMeasurement = millis();
@ -112,7 +152,7 @@ class UsermodTemperature : public Usermod {
DEBUG_PRINTLN(temperature); DEBUG_PRINTLN(temperature);
} }
bool findSensor() { bool UsermodTemperature::findSensor() {
DEBUG_PRINTLN(F("Searching for sensor...")); DEBUG_PRINTLN(F("Searching for sensor..."));
uint8_t deviceAddress[8] = {0,0,0,0,0,0,0,0}; uint8_t deviceAddress[8] = {0,0,0,0,0,0,0,0};
// find out if we have DS18xxx sensor attached // find out if we have DS18xxx sensor attached
@ -139,7 +179,7 @@ class UsermodTemperature : public Usermod {
} }
#ifndef WLED_DISABLE_MQTT #ifndef WLED_DISABLE_MQTT
void publishHomeAssistantAutodiscovery() { void UsermodTemperature::publishHomeAssistantAutodiscovery() {
if (!WLED_MQTT_CONNECTED) return; if (!WLED_MQTT_CONNECTED) return;
char json_str[1024], buf[128]; char json_str[1024], buf[128];
@ -162,9 +202,7 @@ class UsermodTemperature : public Usermod {
} }
#endif #endif
public: void UsermodTemperature::setup() {
void setup() {
int retries = 10; int retries = 10;
sensorFound = 0; sensorFound = 0;
temperature = -127.0f; // default to -127, DS18B20 only goes down to -50C temperature = -127.0f; // default to -127, DS18B20 only goes down to -50C
@ -196,7 +234,7 @@ class UsermodTemperature : public Usermod {
initDone = true; initDone = true;
} }
void loop() { void UsermodTemperature::loop() {
if (!enabled || !sensorFound || strip.isUpdating()) return; if (!enabled || !sensorFound || strip.isUpdating()) return;
static uint8_t errorCount = 0; static uint8_t errorCount = 0;
@ -248,13 +286,13 @@ class UsermodTemperature : public Usermod {
* connected() is called every time the WiFi is (re)connected * connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces * Use it to initialize network interfaces
*/ */
//void connected() {} //void UsermodTemperature::connected() {}
#ifndef WLED_DISABLE_MQTT #ifndef WLED_DISABLE_MQTT
/** /**
* subscribe to MQTT topic if needed * subscribe to MQTT topic if needed
*/ */
void onMqttConnect(bool sessionPresent) { void UsermodTemperature::onMqttConnect(bool sessionPresent) {
//(re)subscribe to required topics //(re)subscribe to required topics
//char subuf[64]; //char subuf[64];
if (mqttDeviceTopic[0] != 0) { if (mqttDeviceTopic[0] != 0) {
@ -263,22 +301,12 @@ class UsermodTemperature : public Usermod {
} }
#endif #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. * 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. * 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 * 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 // dont add temperature to info if we are disabled
if (!enabled) return; if (!enabled) return;
@ -293,21 +321,21 @@ class UsermodTemperature : public Usermod {
return; return;
} }
temp.add(degC ? getTemperatureC() : getTemperatureF()); temp.add(getTemperature());
temp.add(degC ? F("°C") : F("°F")); temp.add(getTemperatureUnit());
JsonObject sensor = root[F("sensor")]; JsonObject sensor = root[F("sensor")];
if (sensor.isNull()) sensor = root.createNestedObject(F("sensor")); if (sensor.isNull()) sensor = root.createNestedObject(F("sensor"));
temp = sensor.createNestedArray(F("temp")); temp = sensor.createNestedArray(F("temperature"));
temp.add(degC ? temperature : (float)temperature * 1.8f + 32); temp.add(getTemperature());
temp.add(degC ? F("°C") : F("°F")); temp.add(getTemperatureUnit());
} }
/** /**
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). * 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 * 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 * 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. * 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() // if (!initDone) return; // prevent crash on boot applyPreset()
//} //}
/** /**
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json * 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}} // we add JSON object: {"Temperature": {"pin": 0, "degC": true}}
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
top[FPSTR(_enabled)] = enabled; 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. * 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}} // we look for JSON object: {"Temperature": {"pin": 0, "degC": true}}
int8_t newTemperaturePin = temperaturePin; int8_t newTemperaturePin = temperaturePin;
DEBUG_PRINT(FPSTR(_name)); DEBUG_PRINT(FPSTR(_name));
@ -381,19 +409,20 @@ class UsermodTemperature : public Usermod {
return !top[FPSTR(_parasitePin)].isNull(); 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("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("',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("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 oappend(SET_F("',1,'<i>(for external MOSFET)</i>');")); // 0 is field type, 1 is actual field
} }
uint16_t getId() float UsermodTemperature::getTemperature() {
{ return degC ? getTemperatureC() : getTemperatureF();
return USERMOD_ID_TEMPERATURE; }
const char *UsermodTemperature::getTemperatureUnit() {
return degC ? "°C" : "°F";
} }
};
// strings to reduce flash memory usage (used more than twice) // strings to reduce flash memory usage (used more than twice)
const char UsermodTemperature::_name[] PROGMEM = "Temperature"; 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: public:
void setup() { void setup() {
PinManagerPinType pins[2] = { { i2c_scl, true }, { i2c_sda, true } }; if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; }
Wire.begin();
sensor.setTimeout(150); sensor.setTimeout(150);
if (!sensor.init()) if (!sensor.init())

View File

@ -569,16 +569,6 @@ class AudioReactive : public Usermod {
#else #else
int8_t i2sckPin = I2S_CKPIN; int8_t i2sckPin = I2S_CKPIN;
#endif #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 #ifndef MCLK_PIN
int8_t mclkPin = I2S_PIN_NO_CHANGE; /* ESP32: only -1, 0, 1, 3 allowed*/ int8_t mclkPin = I2S_PIN_NO_CHANGE; /* ESP32: only -1, 0, 1, 3 allowed*/
#else #else
@ -1136,7 +1126,7 @@ class AudioReactive : public Usermod {
DEBUGSR_PRINTLN(F("AR: ES7243 Microphone (right channel only).")); DEBUGSR_PRINTLN(F("AR: ES7243 Microphone (right channel only)."));
audioSource = new ES7243(SAMPLE_RATE, BLOCK_SIZE); audioSource = new ES7243(SAMPLE_RATE, BLOCK_SIZE);
delay(100); delay(100);
if (audioSource) audioSource->initialize(sdaPin, sclPin, i2swsPin, i2ssdPin, i2sckPin, mclkPin); if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
break; break;
case 3: case 3:
DEBUGSR_PRINT(F("AR: SPH0645 Microphone - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT)); 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(i2swsPin);
pinArray.add(i2sckPin); pinArray.add(i2sckPin);
pinArray.add(mclkPin); pinArray.add(mclkPin);
pinArray.add(sdaPin);
pinArray.add(sclPin);
JsonObject cfg = top.createNestedObject("config"); JsonObject cfg = top.createNestedObject("config");
cfg[F("squelch")] = soundSquelch; 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"][1], i2swsPin);
configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][2], i2sckPin); configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][2], i2sckPin);
configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][3], mclkPin); 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("squelch")], soundSquelch);
configComplete &= getJsonValue(top["config"][F("gain")], sampleGain); configComplete &= getJsonValue(top["config"][F("gain")], sampleGain);
@ -1784,8 +1770,6 @@ class AudioReactive : public Usermod {
#else #else
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',3,'<i>master clock</i>','I2S MCLK');")); oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',3,'<i>master clock</i>','I2S MCLK');"));
#endif #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 #pragma once
#include <Wire.h>
#include "wled.h" #include "wled.h"
#include <driver/i2s.h> #include <driver/i2s.h>
#include <driver/adc.h> #include <driver/adc.h>
@ -383,21 +382,12 @@ class I2SSource : public AudioSource {
*/ */
class ES7243 : public I2SSource { class ES7243 : public I2SSource {
private: 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) { void _es7243I2cWrite(uint8_t reg, uint8_t val) {
#ifndef ES7243_ADDR #ifndef ES7243_ADDR
Wire.beginTransmission(0x13);
#define ES7243_ADDR 0x13 // default address #define ES7243_ADDR 0x13 // default address
#else
Wire.beginTransmission(ES7243_ADDR);
#endif #endif
Wire.beginTransmission(ES7243_ADDR);
Wire.write((uint8_t)reg); Wire.write((uint8_t)reg);
Wire.write((uint8_t)val); Wire.write((uint8_t)val);
uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK
@ -407,7 +397,6 @@ class ES7243 : public I2SSource {
} }
void _es7243InitAdc() { void _es7243InitAdc() {
_es7243I2cBegin();
_es7243I2cWrite(0x00, 0x01); _es7243I2cWrite(0x00, 0x01);
_es7243I2cWrite(0x06, 0x00); _es7243I2cWrite(0x06, 0x00);
_es7243I2cWrite(0x05, 0x1B); _es7243I2cWrite(0x05, 0x1B);
@ -422,44 +411,20 @@ public:
_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT; _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) { void initialize(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;
}
if ((i2sckPin < 0) || (mclkPin < 0)) { if ((i2sckPin < 0) || (mclkPin < 0)) {
DEBUGSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin); DEBUGSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
return; 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 // First route mclk, then configure ADC over I2C, then configure I2S
_es7243InitAdc(); _es7243InitAdc();
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin); I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
} }
void deinitialize() { 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(); I2SSource::deinitialize();
} }
private:
int8_t pin_ES7243_SDA;
int8_t pin_ES7243_SCL;
}; };

View File

@ -1,28 +0,0 @@
# Blynk controllable relay
Enables controlling a relay state via user variables. Allows the user variables to be set via Blynk.
Optionally, the servo can have a reset timer to return to its default state after a user definable interval. The interval is set via userVar1.
## Instalation
Replace the WLED06_usermod.ino file in Aircoookies WLED folder, with the one here.
## Customizations
Update the following parameters in WLED06_usermod.ino to configure the mod's behavior:
```cpp
//Which pin is the relay connected to
#define RELAY_PIN 5
//Which pin state should the relay default to
#define RELAY_PIN_DEFAULT LOW
//If >0 The controller returns to RELAY_PIN_DEFAULT after this time, in milliseconds
#define RELAY_PIN_TIMER_DEFAULT 3000
//Blynk virtual pin for controlling relay
#define BLYNK_USER_VAR0_PIN V9
//Blynk virtual pin for controlling relay timer
#define BLYNK_USER_VAR1_PIN V10
//Number of milliseconds between Blynk updates
#define BLYNK_RELAY_UPDATE_INTERVAL 5000
```

View File

@ -1,96 +0,0 @@
/*
* This file allows you to add own functionality to WLED more easily
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
* EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in wled_eeprom.h)
* bytes 2400+ are currently ununsed, but might be used for future wled features
*/
//Use userVar0 (API calls &U0=, uint16_t) to set relay state
#define relayPinState userVar0
//Use userVar1 (API calls &U1=, uint16_t) to set relay timer duration
//Ignored if 0, otherwise number of milliseconds to allow relay to stay in
//non default state.
#define relayTimerInterval userVar1
//Which pin is the relay connected to
#define RELAY_PIN 5
//Which pin state should the relay default to
#define RELAY_PIN_DEFAULT LOW
//If >0 The controller returns to RELAY_PIN_DEFAULT after this time in milliseconds
#define RELAY_PIN_TIMER_DEFAULT 3000
//Blynk virtual pin for controlling relay
#define BLYNK_USER_VAR0_PIN V9
//Blynk virtual pin for controlling relay timer
#define BLYNK_USER_VAR1_PIN V10
//Number of milliseconds between updating blynk
#define BLYNK_RELAY_UPDATE_INTERVAL 5000
//Is the timer for resetting the relay active
bool relayTimerStarted = false;
//millis() time after which relay will be reset
unsigned long relayTimeToDefault = 0;
//millis() time after which relay vars in Blynk will be sent
unsigned long relayBlynkUpdateTime = 0;
//gets called once at boot. Do all initialization that doesn't depend on network here
void userSetup()
{
relayPinState = RELAY_PIN_DEFAULT;
relayTimerInterval = RELAY_PIN_TIMER_DEFAULT;
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, relayPinState);
}
//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
void userConnected()
{
}
//loop. You can use "if (WLED_CONNECTED)" to check for successful connection
void userLoop()
{
//Normalize relayPinState to an accepted value
if (relayPinState != HIGH && relayPinState != LOW) {
relayPinState = RELAY_PIN_DEFAULT;
}
//If relay changes and relayTimerInterval is set, start a timer to change back
if (relayTimerInterval != 0 &&
relayPinState != RELAY_PIN_DEFAULT &&
!relayTimerStarted ) {
relayTimerStarted = true;
relayTimeToDefault = millis() + relayTimerInterval;
}
//If manually changed back to default, cancel timer
if (relayTimerStarted && relayPinState == RELAY_PIN_DEFAULT ) {
relayTimerStarted = false;
}
//If timer completes, set relay back to default
if (relayTimerStarted && millis() > relayTimeToDefault) {
relayPinState = RELAY_PIN_DEFAULT;
relayTimerStarted = false;
}
digitalWrite(RELAY_PIN, relayPinState);
updateRelayBlynk();
}
//Update Blynk with state of userVars at BLYNK_RELAY_UPDATE_INTERVAL
void updateRelayBlynk()
{
if (!WLED_CONNECTED) return;
if (relayBlynkUpdateTime > millis()) return;
Blynk.virtualWrite(BLYNK_USER_VAR0_PIN, userVar0);
Blynk.virtualWrite(BLYNK_USER_VAR1_PIN, userVar1);
relayBlynkUpdateTime = millis() + BLYNK_RELAY_UPDATE_INTERVAL;
}
//Add Blynk callback for setting userVar0
BLYNK_WRITE(BLYNK_USER_VAR0_PIN)
{
userVar0 = param.asInt();
}
//Add Blynk callback for setting userVar1
BLYNK_WRITE(BLYNK_USER_VAR1_PIN)
{
userVar1 = param.asInt();
}

View File

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

View File

@ -1,6 +1,9 @@
# Multi Relay # Multi Relay
This usermod-v2 modification allows the connection of multiple relays, each with individual delay and on/off mode. 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 ## HTTP API
All responses are returned in JSON format. All responses are returned in JSON format.
@ -81,13 +84,15 @@ void registerUsermods()
Usermod can be configured via the Usermods settings page. Usermod can be configured via the Usermods settings page.
* `enabled` - enable/disable usermod * `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,...`) * `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 * `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) * `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) * `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 * `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. 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 2021-11
* Added information about dynamic configuration options * Added information about dynamic configuration options
* Added button support. * Added button support.
2023-05
* Added support for PCF8574 I2C port expander (multiple)

View File

@ -4,10 +4,19 @@
#ifndef MULTI_RELAY_MAX_RELAYS #ifndef MULTI_RELAY_MAX_RELAYS
#define MULTI_RELAY_MAX_RELAYS 4 #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 #endif
#ifndef MULTI_RELAY_PINS #ifndef MULTI_RELAY_PINS
#define MULTI_RELAY_PINS -1 #define MULTI_RELAY_PINS -1
#define MULTI_RELAY_ENABLED false
#else
#define MULTI_RELAY_ENABLED true
#endif #endif
#define WLED_DEBOUNCE_THRESHOLD 50 //only consider button input of at least 50ms as valid (debouncing) #define WLED_DEBOUNCE_THRESHOLD 50 //only consider button input of at least 50ms as valid (debouncing)
@ -15,21 +24,37 @@
#define ON true #define ON true
#define OFF false #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. * This usermod handles multiple relay outputs.
* These outputs complement built-in relay output in a way that the activation can be delayed. * 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. * They can also activate/deactivate in reverse logic independently.
*
* Written and maintained by @blazoncek
*/ */
typedef struct relay_t { typedef struct relay_t {
int8_t pin; int8_t pin;
bool active; struct { // reduces memory footprint
bool mode; bool active : 1; // is the relay waiting to be switched
bool state; bool invert : 1; // does On mean 1 or 0
bool external; bool state : 1; // 1 relay is On, 0 relay is Off
uint16_t delay; bool external : 1; // is the relay externally controlled
int8_t button; int8_t button : 4; // which button triggers relay
};
uint16_t delay; // amount of ms to wait after it is activated
} Relay; } Relay;
@ -39,20 +64,15 @@ class MultiRelay : public Usermod {
// array of relays // array of relays
Relay _relay[MULTI_RELAY_MAX_RELAYS]; Relay _relay[MULTI_RELAY_MAX_RELAYS];
// switch timer start time uint32_t _switchTimerStart; // switch timer start time
uint32_t _switchTimerStart = 0; bool _oldMode; // old brightness
// old brightness bool enabled; // usermod enabled
bool _oldMode; bool initDone; // status of initialisation
bool usePcf8574;
// usermod enabled uint8_t addrPcf8574;
bool enabled = false; // needs to be configured (no default config) bool HAautodiscovery;
// status of initialisation uint16_t periodicBroadcastSec;
bool initDone = false; unsigned long lastBroadcast;
bool HAautodiscovery = false;
uint16_t periodicBroadcastSec = 60;
unsigned long lastBroadcast = 0;
// strings to reduce flash memory usage (used more than twice) // strings to reduce flash memory usage (used more than twice)
static const char _name[]; static const char _name[];
@ -64,8 +84,127 @@ class MultiRelay : public Usermod {
static const char _button[]; static const char _button[];
static const char _broadcast[]; static const char _broadcast[];
static const char _HAautodiscovery[]; 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 #ifndef WLED_DISABLE_MQTT
//Check if MQTT Connected, otherwise it will crash the 8266 //Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED){ if (WLED_MQTT_CONNECTED){
@ -79,12 +218,12 @@ class MultiRelay : public Usermod {
/** /**
* switch off the strip if the delay has elapsed * switch off the strip if the delay has elapsed
*/ */
void handleOffTimer() { void MultiRelay::handleOffTimer() {
unsigned long now = millis(); unsigned long now = millis();
bool activeRelays = false; bool activeRelays = false;
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { 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].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; _relay[i].active = false;
} else if (periodicBroadcastSec && now - lastBroadcast > (periodicBroadcastSec*1000)) { } else if (periodicBroadcastSec && now - lastBroadcast > (periodicBroadcastSec*1000)) {
if (_relay[i].pin>=0) publishMqtt(i); 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 * https://github.com/gsieben/WLED/blob/master/usermods/GeoGab-Relays/usermod_GeoGab.h
*/ */
#define GEOGABVERSION "0.1.3" #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")); DEBUG_PRINTLN(F("Relays: Initialize HTML API"));
server.on("/relays", HTTP_GET, [this](AsyncWebServerRequest *request) { 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 found = 0;
int strIndex[] = {0, -1}; int strIndex[] = {0, -1};
int maxIndex = data.length()-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; return found>index ? data.substring(strIndex[0], strIndex[1]).toInt() : -1;
} }
public: //Write a byte to the IO expander
/** byte MultiRelay::IOexpanderWrite(byte address, byte _data ) {
* constructor Wire.beginTransmission(address);
*/ Wire.write(_data);
MultiRelay() { 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}; const int8_t defPins[] = {MULTI_RELAY_PINS};
for (size_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { for (size_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
_relay[i].pin = i<sizeof(defPins) ? defPins[i] : -1; _relay[i].pin = i<sizeof(defPins) ? defPins[i] : -1;
_relay[i].delay = 0; _relay[i].delay = 0;
_relay[i].mode = false; _relay[i].invert = false;
_relay[i].active = false; _relay[i].active = false;
_relay[i].state = false; _relay[i].state = false;
_relay[i].external = false; _relay[i].external = false;
_relay[i].button = -1; _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 * 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; if (relay>=MULTI_RELAY_MAX_RELAYS || _relay[relay].pin<0) return;
_relay[relay].state = mode; _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); 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); publishMqtt(relay);
} }
/** uint8_t MultiRelay::getActiveRelayCount() {
* toggle relay
*/
inline void toggleRelay(uint8_t relay) {
switchRelay(relay, !_relay[relay].state);
}
uint8_t getActiveRelayCount() {
uint8_t count = 0; uint8_t count = 0;
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) if (_relay[i].pin>=0) count++; for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) if (_relay[i].pin>=0) count++;
return count; return count;
} }
//Functions called by WLED //Functions called by WLED
#ifndef WLED_DISABLE_MQTT #ifndef WLED_DISABLE_MQTT
@ -240,7 +397,7 @@ class MultiRelay : public Usermod {
* topic only contains stripped topic (part after /wled/MAC) * topic only contains stripped topic (part after /wled/MAC)
* topic should look like: /relay/X/command; where X is relay number, 0 based * 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) { 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); uint8_t relay = strtoul(topic+7, NULL, 10);
if (relay<MULTI_RELAY_MAX_RELAYS) { if (relay<MULTI_RELAY_MAX_RELAYS) {
@ -263,7 +420,7 @@ class MultiRelay : public Usermod {
/** /**
* subscribe to MQTT topic for controlling relays * subscribe to MQTT topic for controlling relays
*/ */
void onMqttConnect(bool sessionPresent) { void MultiRelay::onMqttConnect(bool sessionPresent) {
//(re)subscribe to required topics //(re)subscribe to required topics
char subuf[64]; char subuf[64];
if (mqttDeviceTopic[0] != 0) { 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++) { for (int i = 0; i < MULTI_RELAY_MAX_RELAYS; i++) {
char uid[24], json_str[1024], buf[128]; char uid[24], json_str[1024], buf[128];
size_t payload_size; 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. * setup() is called once at boot. WiFi is not yet connected at this point.
* You can use it to initialize variables, sensors or similar. * 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() // 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++) { for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].pin<0) continue; if (usePcf8574 && _relay[i].pin >= 100) {
if (!pinManager.allocatePin(_relay[i].pin,true, PinOwner::UM_MultiRelay)) { uint8_t pin = _relay[i].pin - 100;
_relay[i].pin = -1; // allocation failed if (!_relay[i].external) _relay[i].state = !offMode;
} else { 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; if (!_relay[i].external) _relay[i].state = !offMode;
switchRelay(i, _relay[i].state); switchRelay(i, _relay[i].state);
_relay[i].active = false; _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; _oldMode = offMode;
initDone = true; 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. * loop() is called continuously. Here you can check for events, read sensors, etc.
*/ */
void loop() { void MultiRelay::loop() {
yield(); yield();
if (!enabled || strip.isUpdating()) return; if (!enabled || strip.isUpdating()) return;
@ -362,7 +524,7 @@ class MultiRelay : public Usermod {
_oldMode = offMode; _oldMode = offMode;
_switchTimerStart = millis(); _switchTimerStart = millis();
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { 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. * will prevent button working in a default way.
* Replicating button.cpp * Replicating button.cpp
*/ */
bool handleButton(uint8_t b) { bool MultiRelay::handleButton(uint8_t b) {
yield(); yield();
if (!enabled if (!enabled
|| buttonType[b] == BTN_TYPE_NONE || 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. * 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) { if (enabled) {
JsonObject user = root["u"]; JsonObject user = root["u"];
if (user.isNull()) 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). * 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 * 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() if (!initDone || !enabled) return; // prevent crash on boot applyPreset()
JsonObject multiRelay = root[FPSTR(_name)]; JsonObject multiRelay = root[FPSTR(_name)];
if (multiRelay.isNull()) { 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). * 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 * 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() if (!initDone || !enabled) return; // prevent crash on boot applyPreset()
JsonObject usermod = root[FPSTR(_name)]; JsonObject usermod = root[FPSTR(_name)];
if (!usermod.isNull()) { if (!usermod.isNull()) {
@ -557,31 +719,40 @@ class MultiRelay : public Usermod {
/** /**
* provide the changeable values * provide the changeable values
*/ */
void addToConfig(JsonObject &root) { void MultiRelay::addToConfig(JsonObject &root) {
JsonObject top = root.createNestedObject(FPSTR(_name)); JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled; top[FPSTR(_enabled)] = enabled;
top[FPSTR(_pcf8574)] = usePcf8574;
top[FPSTR(_pcfAddress)] = addrPcf8574;
top[FPSTR(_broadcast)] = periodicBroadcastSec; top[FPSTR(_broadcast)] = periodicBroadcastSec;
top[FPSTR(_HAautodiscovery)] = HAautodiscovery;
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
String parName = FPSTR(_relay_str); parName += '-'; parName += i; String parName = FPSTR(_relay_str); parName += '-'; parName += i;
JsonObject relay = top.createNestedObject(parName); JsonObject relay = top.createNestedObject(parName);
relay["pin"] = _relay[i].pin; 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(_delay_str)] = _relay[i].delay;
relay[FPSTR(_external)] = _relay[i].external; relay[FPSTR(_external)] = _relay[i].external;
relay[FPSTR(_button)] = _relay[i].button; relay[FPSTR(_button)] = _relay[i].button;
} }
top[FPSTR(_HAautodiscovery)] = HAautodiscovery;
DEBUG_PRINTLN(F("MultiRelay config saved.")); 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 * restore the changeable values
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json * 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. * 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]; int8_t oldPin[MULTI_RELAY_MAX_RELAYS];
JsonObject top = root[FPSTR(_name)]; JsonObject top = root[FPSTR(_name)];
@ -591,7 +762,13 @@ class MultiRelay : public Usermod {
return false; return false;
} }
//bool configComplete = !top.isNull();
//configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled);
enabled = 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 = top[FPSTR(_broadcast)] | periodicBroadcastSec;
periodicBroadcastSec = min(900,max(0,(int)periodicBroadcastSec)); periodicBroadcastSec = min(900,max(0,(int)periodicBroadcastSec));
HAautodiscovery = top[FPSTR(_HAautodiscovery)] | HAautodiscovery; HAautodiscovery = top[FPSTR(_HAautodiscovery)] | HAautodiscovery;
@ -600,14 +777,14 @@ class MultiRelay : public Usermod {
String parName = FPSTR(_relay_str); parName += '-'; parName += i; String parName = FPSTR(_relay_str); parName += '-'; parName += i;
oldPin[i] = _relay[i].pin; oldPin[i] = _relay[i].pin;
_relay[i].pin = top[parName]["pin"] | _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].external = top[parName][FPSTR(_external)] | _relay[i].external;
_relay[i].delay = top[parName][FPSTR(_delay_str)] | _relay[i].delay; _relay[i].delay = top[parName][FPSTR(_delay_str)] | _relay[i].delay;
_relay[i].button = top[parName][FPSTR(_button)] | _relay[i].button; _relay[i].button = top[parName][FPSTR(_button)] | _relay[i].button;
// begin backwards compatibility (beta) remove when 0.13 is released // begin backwards compatibility (beta) remove when 0.13 is released
parName += '-'; parName += '-';
_relay[i].pin = top[parName+"pin"] | _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].external = top[parName+FPSTR(_external)] | _relay[i].external;
_relay[i].delay = top[parName+FPSTR(_delay_str)] | _relay[i].delay; _relay[i].delay = top[parName+FPSTR(_delay_str)] | _relay[i].delay;
// end compatibility // end compatibility
@ -621,38 +798,17 @@ class MultiRelay : public Usermod {
} else { } else {
// deallocate all pins 1st // deallocate all pins 1st
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) 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); pinManager.deallocatePin(oldPin[i], PinOwner::UM_MultiRelay);
} }
// allocate new pins // allocate new pins
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { setup();
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;
}
DEBUG_PRINTLN(F(" config (re)loaded.")); DEBUG_PRINTLN(F(" config (re)loaded."));
} }
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features // 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) // strings to reduce flash memory usage (used more than twice)
const char MultiRelay::_name[] PROGMEM = "MultiRelay"; const char MultiRelay::_name[] PROGMEM = "MultiRelay";
const char MultiRelay::_enabled[] PROGMEM = "enabled"; 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::_button[] PROGMEM = "button";
const char MultiRelay::_broadcast[] PROGMEM = "broadcast-sec"; const char MultiRelay::_broadcast[] PROGMEM = "broadcast-sec";
const char MultiRelay::_HAautodiscovery[] PROGMEM = "HA-autodiscovery"; 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 "wled.h"
#include <Arduino.h> #include <Arduino.h>
#include <Wire.h>
#include <Adafruit_Sensor.h> #include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h> #include <Adafruit_BMP280.h>
#include <Adafruit_CCS811.h> #include <Adafruit_CCS811.h>
@ -16,14 +15,6 @@ Adafruit_BMP280 bmp;
Adafruit_Si7021 si7021; Adafruit_Si7021 si7021;
Adafruit_CCS811 ccs811; 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 class UserMod_SensorsToMQTT : public Usermod
{ {
private: private:
@ -231,7 +222,6 @@ public:
void setup() void setup()
{ {
Serial.println("Starting!"); Serial.println("Starting!");
Wire.begin(SDA_PIN, SCL_PIN);
Serial.println("Initializing sensors.. "); Serial.println("Initializing sensors.. ");
_initialize(); _initialize();
} }

View File

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

@ -0,0 +1,40 @@
# Klipper Percentage Usermod
This usermod polls the Klipper API every 10s for the progressvalue.
The leds are then filled with a solid color according to that progress percentage.
the solid color is the secondary color of the segment.
A corresponding curl command would be:
```
curl --location --request GET 'http://[]/printer/objects/query?virtual_sdcard=progress'
```
## Usage
Compile the source with the buildflag `-D USERMOD_KLIPPER_PERCENTAGE` added.
You can also use the WLBD bot in the Discord by simply extending an exsisting build enviroment:
```
[env:esp32klipper]
extends = env:esp32dev
build_flags = ${common.build_flags_esp32} -D USERMOD_KLIPPER_PERCENTAGE
```
## Settings
### Enabled:
Checkbox to enable or disable the overlay
### Klipper IP:
IP adress of your Klipper instance you want to poll. ESP has to be restarted after change
### Direction :
0 = normal
1 = reversed
2 = center
-----
Author:
Sören Willrodt
Discord: Sören#5281

View File

@ -0,0 +1,222 @@
#pragma once
#include "wled.h"
class klipper_percentage : public Usermod
{
private:
unsigned long lastTime = 0;
String ip = "192.168.25.207";
WiFiClient wifiClient;
char errorMessage[100] = "";
int printPercent = 0;
int direction = 0; // 0 for along the strip, 1 for reversed direction
static const char _name[];
static const char _enabled[];
bool enabled = false;
void httpGet(WiFiClient &client, char *errorMessage)
{
// https://arduinojson.org/v6/example/http-client/
// is this the most compact way to do http get and put it in arduinojson object???
// would like async response ... ???
client.setTimeout(10000);
if (!client.connect(ip.c_str(), 80))
{
strcat(errorMessage, PSTR("Connection failed"));
}
else
{
// Send HTTP request
client.println(F("GET /printer/objects/query?virtual_sdcard=progress HTTP/1.0"));
client.println("Host: " + ip);
client.println(F("Connection: close"));
if (client.println() == 0)
{
strcat(errorMessage, PSTR("Failed to send request"));
}
else
{
// Check HTTP status
char status[32] = {0};
client.readBytesUntil('\r', status, sizeof(status));
if (strcmp(status, "HTTP/1.1 200 OK") != 0)
{
strcat(errorMessage, PSTR("Unexpected response: "));
strcat(errorMessage, status);
}
else
{
// Skip HTTP headers
char endOfHeaders[] = "\r\n\r\n";
if (!client.find(endOfHeaders))
{
strcat(errorMessage, PSTR("Invalid response"));
}
}
}
}
}
public:
void setup()
{
}
void connected()
{
}
void loop()
{
if (enabled)
{
if (WLED_CONNECTED)
{
if (millis() - lastTime > 10000)
{
httpGet(wifiClient, errorMessage);
if (strcmp(errorMessage, "") == 0)
{
PSRAMDynamicJsonDocument klipperDoc(4096); // in practive about 2673
DeserializationError error = deserializeJson(klipperDoc, wifiClient);
if (error)
{
strcat(errorMessage, PSTR("deserializeJson() failed: "));
strcat(errorMessage, error.c_str());
}
printPercent = (int)(klipperDoc["result"]["status"]["virtual_sdcard"]["progress"].as<float>() * 100);
DEBUG_PRINT("Percent: ");
DEBUG_PRINTLN((int)(klipperDoc["result"]["status"]["virtual_sdcard"]["progress"].as<float>() * 100));
DEBUG_PRINT("LEDs: ");
DEBUG_PRINTLN(direction == 2 ? (strip.getLengthTotal() / 2) * printPercent / 100 : strip.getLengthTotal() * printPercent / 100);
}
else
{
DEBUG_PRINTLN(errorMessage);
DEBUG_PRINTLN(ip);
}
lastTime = millis();
}
}
}
}
void addToConfig(JsonObject &root)
{
JsonObject top = root.createNestedObject("Klipper Printing Percentage");
top["Enabled"] = enabled;
top["Klipper IP"] = ip;
top["Direction"] = direction;
}
bool readFromConfig(JsonObject &root)
{
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
JsonObject top = root["Klipper Printing Percentage"];
bool configComplete = !top.isNull();
configComplete &= getJsonValue(top["Klipper IP"], ip);
configComplete &= getJsonValue(top["Enabled"], enabled);
configComplete &= getJsonValue(top["Direction"], direction);
return configComplete;
}
/*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
* Below it is shown how this could be used for e.g. a light sensor
*/
void addToJsonInfo(JsonObject &root)
{
JsonObject user = root["u"];
if (user.isNull())
user = root.createNestedObject("u");
JsonArray infoArr = user.createNestedArray(FPSTR(_name));
String uiDomString = F("<button class=\"btn btn-xs\" onclick=\"requestJson({");
uiDomString += FPSTR(_name);
uiDomString += F(":{");
uiDomString += FPSTR(_enabled);
uiDomString += enabled ? F(":false}});\">") : F(":true}});\">");
uiDomString += F("<i class=\"icons");
uiDomString += enabled ? F(" on") : F(" off");
uiDomString += F("\">&#xe08f;</i>");
uiDomString += F("</button>");
infoArr.add(uiDomString);
}
void addToJsonState(JsonObject &root)
{
JsonObject usermod = root[FPSTR(_name)];
if (usermod.isNull())
{
usermod = root.createNestedObject(FPSTR(_name));
}
usermod["on"] = enabled;
}
void readFromJsonState(JsonObject &root)
{
JsonObject usermod = root[FPSTR(_name)];
if (!usermod.isNull())
{
if (usermod[FPSTR(_enabled)].is<bool>())
{
enabled = usermod[FPSTR(_enabled)].as<bool>();
}
}
}
/*
* handleOverlayDraw() is called just before every show() (LED strip update frame) after effects have set the colors.
* Use this to blank out some LEDs or set them to a different color regardless of the set effect mode.
* Commonly used for custom clocks (Cronixie, 7 segment)
*/
void handleOverlayDraw()
{
if (enabled)
{
if (direction == 0) // normal
{
for (int i = 0; i < strip.getLengthTotal() * printPercent / 100; i++)
{
strip.setPixelColor(i, strip.getSegment(0).colors[1]);
}
}
else if (direction == 1) // reversed
{
for (int i = 0; i < strip.getLengthTotal() * printPercent / 100; i++)
{
strip.setPixelColor(strip.getLengthTotal() - i, strip.getSegment(0).colors[1]);
}
}
else if (direction == 2) // center
{
for (int i = 0; i < (strip.getLengthTotal() / 2) * printPercent / 100; i++)
{
strip.setPixelColor((strip.getLengthTotal() / 2) + i, strip.getSegment(0).colors[1]);
strip.setPixelColor((strip.getLengthTotal() / 2) - i, strip.getSegment(0).colors[1]);
}
}
else
{
direction = 0;
}
}
}
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
* This could be used in the future for the system to determine whether your usermod is installed.
*/
uint16_t getId()
{
return USERMOD_ID_KLIPPER;
}
};
const char klipper_percentage::_name[] PROGMEM = "Klipper_Percentage";
const char klipper_percentage::_enabled[] PROGMEM = "enabled";

View File

@ -45,7 +45,23 @@
#define ENCODER_SW_PIN 19 #define ENCODER_SW_PIN 19
#endif #endif
// The last UI state, remove color and saturation option if diplay not active(too many options) #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 #ifdef USERMOD_FOUR_LINE_DISPLAY
#define LAST_UI_STATE 11 #define LAST_UI_STATE 11
#else #else
@ -113,62 +129,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 { class RotaryEncoderUIUsermod : public Usermod {
private: 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 loopTime;
unsigned long buttonPressedTime = 0; unsigned long buttonPressedTime;
unsigned long buttonWaitTime = 0; unsigned long buttonWaitTime;
bool buttonPressedBefore = false; bool buttonPressedBefore;
bool buttonLongPressed = false; bool buttonLongPressed;
int8_t pinA = ENCODER_DT_PIN; // DT from encoder int8_t pinA; // DT from encoder
int8_t pinB = ENCODER_CLK_PIN; // CLK from encoder int8_t pinB; // CLK from encoder
int8_t pinC = ENCODER_SW_PIN; // SW 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 uint16_t currentHue1; // default boot color
byte currentSat1 = 255; byte currentSat1;
uint8_t currentCCT;
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
FourLineDisplayUsermod *display; FourLineDisplayUsermod *display;
#else #else
void* display = nullptr; void* display;
#endif #endif
// Pointers the start of the mode names within JSON_mode_names // 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. // 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 // 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. // Array of palette indexes in alphabetical order.
byte *palettes_alpha_indexes = nullptr; byte *palettes_alpha_indexes;
unsigned char Enc_A; struct { // reduce memory footprint
unsigned char Enc_B; bool Enc_A : 1;
unsigned char Enc_A_prev = 0; bool Enc_B : 1;
bool Enc_A_prev : 1;
};
bool currentEffectAndPaletteInitialized = false; bool currentEffectAndPaletteInitialized;
uint8_t effectCurrentIndex = 0; uint8_t effectCurrentIndex;
uint8_t effectPaletteIndex = 0; uint8_t effectPaletteIndex;
uint8_t knownMode = 0; uint8_t knownMode;
uint8_t knownPalette = 0; uint8_t knownPalette;
uint8_t currentCCT = 128; byte presetHigh;
byte presetLow;
byte presetHigh = 0; bool applyToAll;
byte presetLow = 0;
bool applyToAll = true; bool initDone;
bool enabled;
bool initDone = false; bool usePcf8574;
bool enabled = true; int8_t pinIRQ;
// strings to reduce flash memory usage (used more than twice) // strings to reduce flash memory usage (used more than twice)
static const char _name[]; static const char _name[];
@ -179,12 +216,179 @@ private:
static const char _presetHigh[]; static const char _presetHigh[];
static const char _presetLow[]; static const char _presetLow[];
static const char _applyToAll[]; 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 * Sort the modes and palettes to the index arrays
* modes_alpha_indexes and palettes_alpha_indexes. * 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 = re_findModeStrings(JSON_mode_names, strip.getModeCount());
modes_qstrings = strip.getModeDataSrc(); modes_qstrings = strip.getModeDataSrc();
modes_alpha_indexes = re_initIndexArray(strip.getModeCount()); modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
@ -200,7 +404,7 @@ private:
re_sortModes(palettes_qstrings, palettes_alpha_indexes, strip.getPaletteCount(), skipPaletteCount); 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); byte *indexes = (byte *)malloc(sizeof(byte) * numModes);
for (byte i = 0; i < numModes; i++) { for (byte i = 0; i < numModes; i++) {
indexes[i] = i; indexes[i] = i;
@ -212,7 +416,7 @@ private:
* Return an array of mode or palette names from the JSON string. * Return an array of mode or palette names from the JSON string.
* They don't end in '\0', they end in '"'. * 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); const char **modeStrings = (const char **)malloc(sizeof(const char *) * numModes);
uint8_t modeIndex = 0; uint8_t modeIndex = 0;
bool insideQuotes = false; bool insideQuotes = false;
@ -250,7 +454,7 @@ private:
/** /**
* Sort either the modes or the palettes using quicksort. * 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; if (!modeNames) return;
listBeingSorted = modeNames; listBeingSorted = modeNames;
qsort(indexes + numSkip, count - numSkip, sizeof(byte), re_qstringCmp); qsort(indexes + numSkip, count - numSkip, sizeof(byte), re_qstringCmp);
@ -258,21 +462,37 @@ private:
} }
public: // public methods
/* /*
* setup() is called once at boot. WiFi is not yet connected at this point. * setup() is called once at boot. WiFi is not yet connected at this point.
* You can use it to initialize variables, sensors or similar. * You can use it to initialize variables, sensors or similar.
*/ */
void setup() void RotaryEncoderUIUsermod::setup()
{ {
DEBUG_PRINTLN(F("Usermod Rotary Encoder init.")); 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 } }; PinManagerPinType pins[3] = { { pinA, false }, { pinB, false }, { pinC, false } };
if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_RotaryEncoderUI)) { 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; pinA = pinB = pinC = -1;
enabled = false; enabled = false;
return; return;
@ -284,6 +504,7 @@ public:
pinMode(pinA, USERMOD_ROTARY_ENCODER_GPIO); pinMode(pinA, USERMOD_ROTARY_ENCODER_GPIO);
pinMode(pinB, USERMOD_ROTARY_ENCODER_GPIO); pinMode(pinB, USERMOD_ROTARY_ENCODER_GPIO);
pinMode(pinC, USERMOD_ROTARY_ENCODER_GPIO); pinMode(pinC, USERMOD_ROTARY_ENCODER_GPIO);
}
loopTime = millis(); loopTime = millis();
@ -301,20 +522,11 @@ public:
#endif #endif
initDone = true; initDone = true;
Enc_A = digitalRead(pinA); // Read encoder pins Enc_A = readPin(pinA); // Read encoder pins
Enc_B = digitalRead(pinB); Enc_B = readPin(pinB);
Enc_A_prev = Enc_A; 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. * loop() is called continuously. Here you can check for events, read sensors, etc.
* *
@ -325,7 +537,7 @@ public:
* 2. Try to avoid using the delay() function. NEVER use delays longer than 10 milliseconds. * 2. Try to avoid using the delay() function. NEVER use delays longer than 10 milliseconds.
* Instead, use a timer check as shown here. * Instead, use a timer check as shown here.
*/ */
void loop() void RotaryEncoderUIUsermod::loop()
{ {
if (!enabled || strip.isUpdating()) return; if (!enabled || strip.isUpdating()) return;
unsigned long currentTime = millis(); // get the current elapsed time unsigned long currentTime = millis(); // get the current elapsed time
@ -339,14 +551,13 @@ public:
} }
if (modes_alpha_indexes[effectCurrentIndex] != effectCurrent || palettes_alpha_indexes[effectPaletteIndex] != effectPalette) { if (modes_alpha_indexes[effectCurrentIndex] != effectCurrent || palettes_alpha_indexes[effectPaletteIndex] != effectPalette) {
DEBUG_PRINTLN(F("Current mode or palette changed."));
currentEffectAndPaletteInitialized = false; currentEffectAndPaletteInitialized = false;
} }
if (currentTime >= (loopTime + 2)) // 2ms since last check of encoder = 500Hz if (currentTime >= (loopTime + 2)) // 2ms since last check of encoder = 500Hz
{ {
loopTime = currentTime; // Updates loopTime bool buttonPressed = !readPin(pinC); //0=pressed, 1=released
bool buttonPressed = !digitalRead(pinC); //0=pressed, 1=released
if (buttonPressed) { if (buttonPressed) {
if (!buttonPressedBefore) buttonPressedTime = currentTime; if (!buttonPressedBefore) buttonPressedTime = currentTime;
buttonPressedBefore = true; buttonPressedBefore = true;
@ -413,8 +624,8 @@ public:
if (changedState) select_state = newState; if (changedState) select_state = newState;
} }
Enc_A = digitalRead(pinA); // Read encoder pins Enc_A = readPin(pinA); // Read encoder pins
Enc_B = digitalRead(pinB); Enc_B = readPin(pinB);
if ((Enc_A) && (!Enc_A_prev)) if ((Enc_A) && (!Enc_A_prev))
{ // A has gone from high to low { // 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 if (Enc_B == LOW) //changes to LOW so that then encoder registers a change at the very end of a pulse
@ -453,16 +664,18 @@ public:
} }
} }
Enc_A_prev = Enc_A; // Store value of A for next time 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 #ifdef USERMOD_FOUR_LINE_DISPLAY
display->networkOverlay(PSTR("NETWORK INFO"), 10000); display->networkOverlay(PSTR("NETWORK INFO"), 10000);
#endif #endif
} }
void findCurrentEffectAndPalette() { void RotaryEncoderUIUsermod::findCurrentEffectAndPalette() {
DEBUG_PRINTLN(F("Finding current mode and palette."));
currentEffectAndPaletteInitialized = true; currentEffectAndPaletteInitialized = true;
for (uint8_t i = 0; i < strip.getModeCount(); i++) { for (uint8_t i = 0; i < strip.getModeCount(); i++) {
if (modes_alpha_indexes[i] == effectCurrent) { if (modes_alpha_indexes[i] == effectCurrent) {
@ -470,6 +683,7 @@ public:
break; break;
} }
} }
DEBUG_PRINTLN(F("Found current mode."));
for (uint8_t i = 0; i < strip.getPaletteCount(); i++) { for (uint8_t i = 0; i < strip.getPaletteCount(); i++) {
if (palettes_alpha_indexes[i] == effectPalette) { if (palettes_alpha_indexes[i] == effectPalette) {
@ -477,9 +691,10 @@ public:
break; 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 #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display != nullptr) { if (display != nullptr) {
if (display->wakeDisplay()) { if (display->wakeDisplay()) {
@ -494,7 +709,7 @@ public:
return true; 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) //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 // 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) //setValuesFromFirstSelectedSeg(); //to make transition work on main segment (should no longer be required)
@ -502,7 +717,7 @@ public:
updateInterfaces(CALL_MODE_BUTTON); updateInterfaces(CALL_MODE_BUTTON);
} }
void changeBrightness(bool increase) { void RotaryEncoderUIUsermod::changeBrightness(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) { if (display && display->wakeDisplay()) {
display->redraw(true); display->redraw(true);
@ -519,7 +734,7 @@ public:
} }
void changeEffect(bool increase) { void RotaryEncoderUIUsermod::changeEffect(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) { if (display && display->wakeDisplay()) {
display->redraw(true); display->redraw(true);
@ -548,7 +763,7 @@ public:
} }
void changeEffectSpeed(bool increase) { void RotaryEncoderUIUsermod::changeEffectSpeed(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) { if (display && display->wakeDisplay()) {
display->redraw(true); display->redraw(true);
@ -576,7 +791,7 @@ public:
} }
void changeEffectIntensity(bool increase) { void RotaryEncoderUIUsermod::changeEffectIntensity(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) { if (display && display->wakeDisplay()) {
display->redraw(true); display->redraw(true);
@ -604,7 +819,7 @@ public:
} }
void changeCustom(uint8_t par, bool increase) { void RotaryEncoderUIUsermod::changeCustom(uint8_t par, bool increase) {
uint8_t val = 0; uint8_t val = 0;
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) { if (display && display->wakeDisplay()) {
@ -649,7 +864,7 @@ public:
} }
void changePalette(bool increase) { void RotaryEncoderUIUsermod::changePalette(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) { if (display && display->wakeDisplay()) {
display->redraw(true); display->redraw(true);
@ -678,7 +893,7 @@ public:
} }
void changeHue(bool increase){ void RotaryEncoderUIUsermod::changeHue(bool increase){
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) { if (display && display->wakeDisplay()) {
display->redraw(true); display->redraw(true);
@ -708,7 +923,7 @@ public:
#endif #endif
} }
void changeSat(bool increase){ void RotaryEncoderUIUsermod::changeSat(bool increase){
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) { if (display && display->wakeDisplay()) {
display->redraw(true); display->redraw(true);
@ -737,7 +952,7 @@ public:
#endif #endif
} }
void changePreset(bool increase) { void RotaryEncoderUIUsermod::changePreset(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) { if (display && display->wakeDisplay()) {
display->redraw(true); display->redraw(true);
@ -769,7 +984,7 @@ public:
} }
} }
void changeCCT(bool increase){ void RotaryEncoderUIUsermod::changeCCT(bool increase){
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) { if (display && display->wakeDisplay()) {
display->redraw(true); display->redraw(true);
@ -803,7 +1018,7 @@ public:
* Below it is shown how this could be used for e.g. a light sensor * 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; int reading = 20;
//this code adds "u":{"Light":[20," lux"]} to the info object //this code adds "u":{"Light":[20," lux"]} to the info object
@ -820,7 +1035,7 @@ public:
* Values in the state object may be modified by connected clients * Values in the state object may be modified by connected clients
*/ */
/* /*
void addToJsonState(JsonObject &root) void RotaryEncoderUIUsermod::addToJsonState(JsonObject &root)
{ {
//root["user0"] = userVar0; //root["user0"] = userVar0;
} }
@ -831,7 +1046,7 @@ public:
* Values in the state object may be modified by connected clients * 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 //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!")); //if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
@ -841,7 +1056,7 @@ public:
/** /**
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json * 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}} // we add JSON object: {"Rotary-Encoder":{"DT-pin":12,"CLK-pin":14,"SW-pin":13}}
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
top[FPSTR(_enabled)] = enabled; top[FPSTR(_enabled)] = enabled;
@ -851,15 +1066,23 @@ public:
top[FPSTR(_presetLow)] = presetLow; top[FPSTR(_presetLow)] = presetLow;
top[FPSTR(_presetHigh)] = presetHigh; top[FPSTR(_presetHigh)] = presetHigh;
top[FPSTR(_applyToAll)] = applyToAll; top[FPSTR(_applyToAll)] = applyToAll;
top[FPSTR(_pcf8574)] = usePcf8574;
top[FPSTR(_pcfAddress)] = addrPcf8574;
top[FPSTR(_pcfINTpin)] = pinIRQ;
DEBUG_PRINTLN(F("Rotary Encoder config saved.")); 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 * 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. * 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}} // we look for JSON object: {"Rotary-Encoder":{"DT-pin":12,"CLK-pin":14,"SW-pin":13}}
JsonObject top = root[FPSTR(_name)]; JsonObject top = root[FPSTR(_name)];
if (top.isNull()) { if (top.isNull()) {
@ -870,6 +1093,8 @@ public:
int8_t newDTpin = top[FPSTR(_DT_pin)] | pinA; int8_t newDTpin = top[FPSTR(_DT_pin)] | pinA;
int8_t newCLKpin = top[FPSTR(_CLK_pin)] | pinB; int8_t newCLKpin = top[FPSTR(_CLK_pin)] | pinB;
int8_t newSWpin = top[FPSTR(_SW_pin)] | pinC; int8_t newSWpin = top[FPSTR(_SW_pin)] | pinC;
int8_t newIRQpin = top[FPSTR(_pcfINTpin)] | pinIRQ;
bool oldPcf8574 = usePcf8574;
presetHigh = top[FPSTR(_presetHigh)] | presetHigh; presetHigh = top[FPSTR(_presetHigh)] | presetHigh;
presetLow = top[FPSTR(_presetLow)] | presetLow; presetLow = top[FPSTR(_presetLow)] | presetLow;
@ -879,6 +1104,9 @@ public:
enabled = top[FPSTR(_enabled)] | enabled; enabled = top[FPSTR(_enabled)] | enabled;
applyToAll = top[FPSTR(_applyToAll)] | applyToAll; applyToAll = top[FPSTR(_applyToAll)] | applyToAll;
usePcf8574 = top[FPSTR(_pcf8574)] | usePcf8574;
addrPcf8574 = top[FPSTR(_pcfAddress)] | addrPcf8574;
DEBUG_PRINT(FPSTR(_name)); DEBUG_PRINT(FPSTR(_name));
if (!initDone) { if (!initDone) {
// first run: reading from cfg.json // first run: reading from cfg.json
@ -889,10 +1117,20 @@ public:
} else { } else {
DEBUG_PRINTLN(F(" config (re)loaded.")); DEBUG_PRINTLN(F(" config (re)loaded."));
// changing parameters from settings page // 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(pinA, PinOwner::UM_RotaryEncoderUI);
pinManager.deallocatePin(pinB, PinOwner::UM_RotaryEncoderUI); pinManager.deallocatePin(pinB, PinOwner::UM_RotaryEncoderUI);
pinManager.deallocatePin(pinC, PinOwner::UM_RotaryEncoderUI); pinManager.deallocatePin(pinC, PinOwner::UM_RotaryEncoderUI);
DEBUG_PRINTLN(F("Deallocated old pins."));
}
pinA = newDTpin; pinA = newDTpin;
pinB = newCLKpin; pinB = newCLKpin;
pinC = newSWpin; pinC = newSWpin;
@ -904,18 +1142,9 @@ public:
} }
} }
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features // 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) // strings to reduce flash memory usage (used more than twice)
const char RotaryEncoderUIUsermod::_name[] PROGMEM = "Rotary-Encoder"; const char RotaryEncoderUIUsermod::_name[] PROGMEM = "Rotary-Encoder";
@ -926,3 +1155,6 @@ const char RotaryEncoderUIUsermod::_SW_pin[] PROGMEM = "SW-pin";
const char RotaryEncoderUIUsermod::_presetHigh[] PROGMEM = "preset-high"; const char RotaryEncoderUIUsermod::_presetHigh[] PROGMEM = "preset-high";
const char RotaryEncoderUIUsermod::_presetLow[] PROGMEM = "preset-low"; const char RotaryEncoderUIUsermod::_presetLow[] PROGMEM = "preset-low";
const char RotaryEncoderUIUsermod::_applyToAll[] PROGMEM = "apply-2-all-seg"; 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

@ -25,6 +25,7 @@ class WordClockUsermod : public Usermod
bool displayItIs = false; bool displayItIs = false;
int ledOffset = 100; int ledOffset = 100;
bool meander = false; bool meander = false;
bool nord = false;
// defines for mask sizes // defines for mask sizes
#define maskSizeLeds 114 #define maskSizeLeds 114
@ -37,39 +38,44 @@ class WordClockUsermod : public Usermod
// "minute" masks // "minute" masks
// Normal wiring // Normal wiring
const int maskMinutes[12][maskSizeMinutes] = const int maskMinutes[14][maskSizeMinutes] =
{ {
{107, 108, 109, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // :00 {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}, // :05 fünf nach { 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}, // :10 zehn 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}, // :15 viertel { 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}, // :20 zwanzig nach { 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}, // :25 fünf vor halb { 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}, // :30 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}, // :35 fünf nach 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}, // :40 zwanzig vor { 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}, // :45 dreiviertel { 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}, // :50 zehn vor { 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} // :55 fünf 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 // Meander wiring
const int maskMinutesMea[12][maskSizeMinutesMea] = const int maskMinutesMea[14][maskSizeMinutesMea] =
{ {
{ 99, 100, 101, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // :00 { 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}, // :05 fünf nach { 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}, // :10 zehn 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}, // :15 viertel { 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}, // :20 zwanzig nach { 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}, // :25 fünf vor halb { 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}, // :30 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}, // :35 fünf nach 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}, // :40 zwanzig vor { 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}, // :45 dreiviertel { 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}, // :50 zehn vor { 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} // :55 fünf 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
}; };
// hour masks // hour masks
// Normal wiring // Normal wiring
const int maskHours[13][maskSizeHours] = const int maskHours[13][maskSizeHours] =
@ -242,9 +248,15 @@ class WordClockUsermod : public Usermod
setHours(hours, false); setHours(hours, false);
break; break;
case 3: case 3:
if (nord) {
// viertel nach
setMinutes(12);
setHours(hours, false);
} else {
// viertel // viertel
setMinutes(3); setMinutes(3);
setHours(hours + 1, false); setHours(hours + 1, false);
};
break; break;
case 4: case 4:
// 20 nach // 20 nach
@ -273,7 +285,13 @@ class WordClockUsermod : public Usermod
break; break;
case 9: case 9:
// viertel vor // viertel vor
if (nord) {
setMinutes(13);
}
// dreiviertel
else {
setMinutes(9); setMinutes(9);
}
setHours(hours + 1, false); setHours(hours + 1, false);
break; break;
case 10: case 10:
@ -405,11 +423,18 @@ class WordClockUsermod : public Usermod
*/ */
void addToConfig(JsonObject& root) void addToConfig(JsonObject& root)
{ {
JsonObject top = root.createNestedObject("WordClockUsermod"); JsonObject top = root.createNestedObject(F("WordClockUsermod"));
top["active"] = usermodActive; top[F("active")] = usermodActive;
top["displayItIs"] = displayItIs; top[F("displayItIs")] = displayItIs;
top["ledOffset"] = ledOffset; top[F("ledOffset")] = ledOffset;
top["Meander wiring?"] = meander; 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');"));
} }
/* /*
@ -432,14 +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 // 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) // 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(); bool configComplete = !top.isNull();
configComplete &= getJsonValue(top["active"], usermodActive); configComplete &= getJsonValue(top[F("active")], usermodActive);
configComplete &= getJsonValue(top["displayItIs"], displayItIs); configComplete &= getJsonValue(top[F("displayItIs")], displayItIs);
configComplete &= getJsonValue(top["ledOffset"], ledOffset); configComplete &= getJsonValue(top[F("ledOffset")], ledOffset);
configComplete &= getJsonValue(top["Meander wiring?"], meander); configComplete &= getJsonValue(top[F("Meander wiring?")], meander);
configComplete &= getJsonValue(top[F("Norddeutsch")], nord);
return configComplete; 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

File diff suppressed because it is too large Load Diff

View File

@ -72,8 +72,12 @@
#ifndef MAX_NUM_SEGMENTS #ifndef MAX_NUM_SEGMENTS
#define MAX_NUM_SEGMENTS 32 #define MAX_NUM_SEGMENTS 32
#endif #endif
#if defined(ARDUINO_ARCH_ESP32S2)
#define MAX_SEGMENT_DATA 24576
#else
#define MAX_SEGMENT_DATA 32767 #define MAX_SEGMENT_DATA 32767
#endif #endif
#endif
/* How much data bytes each segment should max allocate to leave enough space for other segments, /* How much data bytes each segment should max allocate to leave enough space for other segments,
assuming each segment uses the same amount of data. 256 for ESP8266, 640 for ESP32. */ assuming each segment uses the same amount of data. 256 for ESP8266, 640 for ESP32. */
@ -143,7 +147,7 @@
#define FX_MODE_SAW 16 #define FX_MODE_SAW 16
#define FX_MODE_TWINKLE 17 #define FX_MODE_TWINKLE 17
#define FX_MODE_DISSOLVE 18 #define FX_MODE_DISSOLVE 18
#define FX_MODE_DISSOLVE_RANDOM 19 #define FX_MODE_DISSOLVE_RANDOM 19 // candidate for removal (use Dissolve with with check 3)
#define FX_MODE_SPARKLE 20 #define FX_MODE_SPARKLE 20
#define FX_MODE_FLASH_SPARKLE 21 #define FX_MODE_FLASH_SPARKLE 21
#define FX_MODE_HYPER_SPARKLE 22 #define FX_MODE_HYPER_SPARKLE 22
@ -227,7 +231,7 @@
#define FX_MODE_HEARTBEAT 100 #define FX_MODE_HEARTBEAT 100
#define FX_MODE_PACIFICA 101 #define FX_MODE_PACIFICA 101
#define FX_MODE_CANDLE_MULTI 102 #define FX_MODE_CANDLE_MULTI 102
#define FX_MODE_SOLID_GLITTER 103 #define FX_MODE_SOLID_GLITTER 103 // candidate for removal (use glitter)
#define FX_MODE_SUNRISE 104 #define FX_MODE_SUNRISE 104
#define FX_MODE_PHASED 105 #define FX_MODE_PHASED 105
#define FX_MODE_TWINKLEUP 106 #define FX_MODE_TWINKLEUP 106
@ -241,7 +245,7 @@
// #define FX_MODE_CANDY_CANE 114 // removed in 0.14! // #define FX_MODE_CANDY_CANE 114 // removed in 0.14!
#define FX_MODE_BLENDS 115 #define FX_MODE_BLENDS 115
#define FX_MODE_TV_SIMULATOR 116 #define FX_MODE_TV_SIMULATOR 116
#define FX_MODE_DYNAMIC_SMOOTH 117 #define FX_MODE_DYNAMIC_SMOOTH 117 // candidate for removal (check3 in dynamic)
// new 0.14 2D effects // new 0.14 2D effects
#define FX_MODE_2DSPACESHIPS 118 //gap fill #define FX_MODE_2DSPACESHIPS 118 //gap fill
@ -250,7 +254,10 @@
#define FX_MODE_2DBLOBS 121 //gap fill #define FX_MODE_2DBLOBS 121 //gap fill
#define FX_MODE_2DSCROLLTEXT 122 //gap fill #define FX_MODE_2DSCROLLTEXT 122 //gap fill
#define FX_MODE_2DDRIFTROSE 123 //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 !!!) // WLED-SR effects (SR compatible IDs !!!)
#define FX_MODE_PIXELS 128 #define FX_MODE_PIXELS 128
@ -346,7 +353,8 @@ typedef struct Segment {
bool mirror_y : 1; // 8 : mirrored Y (2D) bool mirror_y : 1; // 8 : mirrored Y (2D)
bool transpose : 1; // 9 : transposed (2D, swapped X & Y) 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 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; uint8_t grouping, spacing;
@ -460,7 +468,7 @@ typedef struct Segment {
_dataLen(0), _dataLen(0),
_t(nullptr) _t(nullptr)
{ {
refreshLightCapabilities(); //refreshLightCapabilities();
} }
Segment(uint16_t sStartX, uint16_t sStopX, uint16_t sStartY, uint16_t sStopY) : Segment(sStartX, sStopX) { Segment(uint16_t sStartX, uint16_t sStopX, uint16_t sStartY, uint16_t sStopY) : Segment(sStartX, sStopX) {
@ -496,6 +504,9 @@ typedef struct Segment {
inline bool isSelected(void) const { return selected; } inline bool isSelected(void) const { return selected; }
inline bool isActive(void) const { return stop > start; } inline bool isActive(void) const { return stop > start; }
inline bool is2D(void) const { return (width()>1 && height()>1); } inline bool is2D(void) const { return (width()>1 && height()>1); }
inline bool hasRGB(void) const { return _isRGB; }
inline bool hasWhite(void) const { return _hasW; }
inline bool isCCT(void) const { return _isCCT; }
inline uint16_t width(void) const { return stop - start; } // segment width in physical pixels (length if 1D) inline uint16_t 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 height(void) const { return stopY - startY; } // segment height (if 2D) in physical pixels
inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels
@ -505,7 +516,7 @@ typedef struct Segment {
static uint16_t getUsedSegmentData(void) { return _usedSegmentData; } static uint16_t getUsedSegmentData(void) { return _usedSegmentData; }
static void addUsedSegmentData(int len) { _usedSegmentData += len; } 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 bool setColor(uint8_t slot, uint32_t c); //returns true if changed
void setCCT(uint16_t k); void setCCT(uint16_t k);
void setOpacity(uint8_t o); void setOpacity(uint8_t o);
@ -555,9 +566,9 @@ typedef struct Segment {
void fadeToBlackBy(uint8_t fadeBy); void fadeToBlackBy(uint8_t fadeBy);
void blendPixelColor(int n, uint32_t color, uint8_t blend); 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 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, uint32_t color, bool fast = false);
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, 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) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } // 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); void fadePixelColor(uint16_t n, uint8_t fade);
uint8_t get_random_wheel_index(uint8_t pos); uint8_t get_random_wheel_index(uint8_t pos);
uint32_t color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255); uint32_t color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255);
@ -579,16 +590,16 @@ typedef struct Segment {
// 2D support functions // 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, 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 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, uint32_t color, bool fast = false);
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, 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) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } 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 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 box_blur(uint16_t i, bool vertical, fract8 blur_amount); // 1D box blur (with weight)
void blurRow(uint16_t row, fract8 blur_amount); void blurRow(uint16_t row, fract8 blur_amount);
void blurCol(uint16_t col, fract8 blur_amount); void blurCol(uint16_t col, fract8 blur_amount);
void moveX(int8_t delta); void moveX(int8_t delta, bool wrap = false);
void moveY(int8_t delta); void moveY(int8_t delta, bool wrap = false);
void move(uint8_t dir, uint8_t delta); 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 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 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, uint32_t c);
@ -612,16 +623,16 @@ typedef struct Segment {
uint32_t getPixelColorXY(uint16_t x, uint16_t y) { return getPixelColor(x); } 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, 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 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, 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) { addPixelColor(x, RGBW32(r,g,b,w)); } 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) { addPixelColor(x, RGBW32(c.r,c.g,c.b,0)); } 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 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 box_blur(uint16_t i, bool vertical, fract8 blur_amount) {}
void blurRow(uint16_t row, fract8 blur_amount) {} void blurRow(uint16_t row, fract8 blur_amount) {}
void blurCol(uint16_t col, fract8 blur_amount) {} void blurCol(uint16_t col, fract8 blur_amount) {}
void moveX(int8_t delta) {} void moveX(int8_t delta, bool wrap = false) {}
void moveY(int8_t delta) {} void moveY(int8_t delta, bool wrap = false) {}
void move(uint8_t dir, uint8_t delta) {} 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 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, uint32_t c) {}
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) {} void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) {}
@ -659,7 +670,6 @@ class WS2812FX { // 96 bytes
isMatrix(false), isMatrix(false),
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
panels(1), panels(1),
matrix{0,0,0,0},
#endif #endif
// semi-private (just obscured) used in effect functions through macros // semi-private (just obscured) used in effect functions through macros
_currentPalette(CRGBPalette16(CRGB::Black)), _currentPalette(CRGBPalette16(CRGB::Black)),
@ -712,7 +722,6 @@ class WS2812FX { // 96 bytes
finalizeInit(), finalizeInit(),
service(void), service(void),
setMode(uint8_t segid, uint8_t m), setMode(uint8_t segid, uint8_t m),
setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
setColor(uint8_t slot, uint32_t c), setColor(uint8_t slot, uint32_t c),
setCCT(uint16_t k), setCCT(uint16_t k),
setBrightness(uint8_t b, bool direct = false), setBrightness(uint8_t b, bool direct = false),
@ -727,10 +736,10 @@ class WS2812FX { // 96 bytes
fixInvalidSegments(), fixInvalidSegments(),
setPixelColor(int n, uint32_t c), setPixelColor(int n, uint32_t c),
show(void), show(void),
setTargetFps(uint8_t fps), setTargetFps(uint8_t fps);
deserializeMap(uint8_t n=0);
void fill(uint32_t c) { for (int i = 0; i < _length; i++) setPixelColor(i, c); } // fill whole strip with color (inline) void setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setColor(slot, RGBW32(r,g,b,w)); }
void fill(uint32_t c) { for (int i = 0; i < getLengthTotal(); i++) setPixelColor(i, c); } // fill whole strip with color (inline)
void addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name); // add effect to the list; defined in FX.cpp void addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name); // add effect to the list; defined in FX.cpp
void setupEffectData(void); // add default effects to the list; defined in FX.cpp void setupEffectData(void); // add default effects to the list; defined in FX.cpp
@ -748,6 +757,7 @@ class WS2812FX { // 96 bytes
hasCCTBus(void), hasCCTBus(void),
// return true if the strip is being sent pixel updates // return true if the strip is being sent pixel updates
isUpdating(void), isUpdating(void),
deserializeMap(uint8_t n=0),
useLedsArray = false; useLedsArray = false;
inline bool isServicing(void) { return _isServicing; } inline bool isServicing(void) { return _isServicing; }
@ -762,6 +772,7 @@ class WS2812FX { // 96 bytes
getActiveSegmentsNum(void), getActiveSegmentsNum(void),
getFirstSelectedSegId(void), getFirstSelectedSegId(void),
getLastActiveSegmentId(void), getLastActiveSegmentId(void),
getActiveSegsLightCapabilities(bool selectedOnly = false),
setPixelSegment(uint8_t n); setPixelSegment(uint8_t n);
inline uint8_t getBrightness(void) { return _brightness; } inline uint8_t getBrightness(void) { return _brightness; }
@ -777,17 +788,17 @@ class WS2812FX { // 96 bytes
ablMilliampsMax, ablMilliampsMax,
currentMilliamps, currentMilliamps,
getLengthPhysical(void), getLengthPhysical(void),
getLengthTotal(void), // will include virtual/nonexistent pixels in matrix
getFps(); getFps();
inline uint16_t getFrameTime(void) { return _frametime; } inline uint16_t getFrameTime(void) { return _frametime; }
inline uint16_t getMinShowDelay(void) { return MIN_SHOW_DELAY; } inline uint16_t getMinShowDelay(void) { return MIN_SHOW_DELAY; }
inline uint16_t getLengthTotal(void) { return _length; } inline uint16_t getLength(void) { return _length; } // 2D matrix may have less pixels than W*H
inline uint16_t getTransition(void) { return _transitionDur; } inline uint16_t getTransition(void) { return _transitionDur; }
uint32_t uint32_t
now, now,
timebase, timebase,
currentColor(uint32_t colorNew, uint8_t tNr),
getPixelColor(uint16_t); getPixelColor(uint16_t);
inline uint32_t getLastShow(void) { return _lastShow; } inline uint32_t getLastShow(void) { return _lastShow; }
@ -813,13 +824,6 @@ class WS2812FX { // 96 bytes
uint8_t uint8_t
panels; panels;
struct {
bool bottomStart : 1;
bool rightStart : 1;
bool vertical : 1;
bool serpentine : 1;
} matrix;
typedef struct panel_t { typedef struct panel_t {
uint16_t xOffset; // x offset relative to the top left of matrix in LEDs uint16_t xOffset; // x offset relative to the top left of matrix in LEDs
uint16_t yOffset; // y offset relative to the top left of matrix in LEDs uint16_t yOffset; // y offset relative to the top left of matrix in LEDs
@ -834,6 +838,13 @@ class WS2812FX { // 96 bytes
bool serpentine : 1; // is serpentine? bool serpentine : 1; // is serpentine?
}; };
}; };
panel_t()
: xOffset(0)
, yOffset(0)
, width(8)
, height(8)
, options(0)
{}
} Panel; } Panel;
std::vector<Panel> panel; std::vector<Panel> panel;
#endif #endif

View File

@ -64,6 +64,7 @@ void WS2812FX::setUpMatrix() {
Segment::maxHeight = 1; Segment::maxHeight = 1;
panels = 0; panels = 0;
panel.clear(); // release memory allocated by panels panel.clear(); // release memory allocated by panels
resetSegments();
return; return;
} }
@ -77,21 +78,60 @@ void WS2812FX::setUpMatrix() {
customMappingTable[i] = (uint16_t)-1; customMappingTable[i] = (uint16_t)-1;
} }
// we will try to load a "gap" array (a JSON file)
// the array has to have the same amount of values as mapping array (or larger)
// "gap" array is used while building ledmap (mapping array)
// and discarded afterwards as it has no meaning after the process
// content of the file is just raw JSON array in the form of [val1,val2,val3,...]
// there are no other "key":"value" pairs in it
// allowed values are: -1 (missing pixel/no LED attached), 0 (inactive/unused pixel), 1 (active/used pixel)
char fileName[32]; strcpy_P(fileName, PSTR("/2d-gaps.json")); // reduce flash footprint
bool isFile = WLED_FS.exists(fileName);
size_t gapSize = 0;
int8_t *gapTable = nullptr;
if (isFile && requestJSONBufferLock(20)) {
DEBUG_PRINT(F("Reading LED gap from "));
DEBUG_PRINTLN(fileName);
// read the array into global JSON buffer
if (readObjectFromFile(fileName, nullptr, &doc)) {
// the array is similar to ledmap, except it has only 3 values:
// -1 ... missing pixel (do not increase pixel count)
// 0 ... inactive pixel (it does count, but should be mapped out (-1))
// 1 ... active pixel (it will count and will be mapped)
JsonArray map = doc.as<JsonArray>();
gapSize = map.size();
if (!map.isNull() && gapSize >= customMappingSize) { // not an empty map
gapTable = new int8_t[gapSize];
if (gapTable) for (size_t i = 0; i < gapSize; i++) {
gapTable[i] = constrain(map[i], -1, 1);
}
}
}
DEBUG_PRINTLN(F("Gaps loaded."));
releaseJSONBufferLock();
}
uint16_t x, y, pix=0; //pixel uint16_t x, y, pix=0; //pixel
for (size_t pan = 0; pan < panel.size(); pan++) { for (size_t pan = 0; pan < panel.size(); pan++) {
Panel &p = panel[pan]; Panel &p = panel[pan];
uint16_t h = p.vertical ? p.height : p.width; uint16_t h = p.vertical ? p.height : p.width;
uint16_t v = p.vertical ? p.width : p.height; uint16_t v = p.vertical ? p.width : p.height;
for (size_t j = 0; j < v; j++){ for (size_t j = 0; j < v; j++){
for(size_t i = 0; i < h; i++, pix++) { for(size_t i = 0; i < h; i++) {
y = (p.vertical?p.rightStart:p.bottomStart) ? v-j-1 : j; y = (p.vertical?p.rightStart:p.bottomStart) ? v-j-1 : j;
x = (p.vertical?p.bottomStart:p.rightStart) ? h-i-1 : i; x = (p.vertical?p.bottomStart:p.rightStart) ? h-i-1 : i;
x = p.serpentine && j%2 ? h-x-1 : x; x = p.serpentine && j%2 ? h-x-1 : x;
customMappingTable[(p.yOffset + (p.vertical?x:y)) * Segment::maxWidth + p.xOffset + (p.vertical?y:x)] = pix; size_t index = (p.yOffset + (p.vertical?x:y)) * Segment::maxWidth + p.xOffset + (p.vertical?y:x);
if (!gapTable || (gapTable && gapTable[index] > 0)) customMappingTable[index] = pix; // a useful pixel (otherwise -1 is retained)
if (!gapTable || (gapTable && gapTable[index] >= 0)) pix++; // not a missing pixel
} }
} }
} }
// delete gap array as we no longer need it
if (gapTable) delete[] gapTable;
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
DEBUG_PRINT(F("Matrix ledmap:")); DEBUG_PRINT(F("Matrix ledmap:"));
for (uint16_t i=0; i<customMappingSize; i++) { for (uint16_t i=0; i<customMappingSize; i++) {
@ -107,7 +147,7 @@ void WS2812FX::setUpMatrix() {
panel.clear(); panel.clear();
Segment::maxWidth = _length; Segment::maxWidth = _length;
Segment::maxHeight = 1; Segment::maxHeight = 1;
return; resetSegments();
} }
} }
#else #else
@ -116,17 +156,16 @@ void WS2812FX::setUpMatrix() {
} }
// absolute matrix version of setPixelColor() // absolute matrix version of setPixelColor()
void IRAM_ATTR WS2812FX::setPixelColorXY(int x, int y, uint32_t col) void /*IRAM_ATTR*/ WS2812FX::setPixelColorXY(int x, int y, uint32_t col)
{ {
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
if (!isMatrix) return; // not a matrix set-up if (!isMatrix) return; // not a matrix set-up
uint16_t index = y * Segment::maxWidth + x; uint16_t index = y * Segment::maxWidth + x;
if (index >= customMappingSize) return;
#else #else
uint16_t index = x; uint16_t index = x;
if (index >= _length) return;
#endif #endif
if (index < customMappingSize) index = customMappingTable[index]; if (index < customMappingSize) index = customMappingTable[index];
if (index >= _length) return;
busses.setPixelColor(index, col); busses.setPixelColor(index, col);
} }
@ -134,12 +173,11 @@ void IRAM_ATTR WS2812FX::setPixelColorXY(int x, int y, uint32_t col)
uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) { uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) {
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
uint16_t index = (y * Segment::maxWidth + x); uint16_t index = (y * Segment::maxWidth + x);
if (index >= customMappingSize) return 0; // customMappingSize is always W * H of matrix in 2D setup
#else #else
uint16_t index = x; uint16_t index = x;
if (index >= _length) return 0;
#endif #endif
if (index < customMappingSize) index = customMappingTable[index]; if (index < customMappingSize) index = customMappingTable[index];
if (index >= _length) return 0;
return busses.getPixelColor(index); return busses.getPixelColor(index);
} }
@ -150,13 +188,13 @@ uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) {
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
// XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element) // XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element)
uint16_t IRAM_ATTR Segment::XY(uint16_t x, uint16_t y) { uint16_t /*IRAM_ATTR*/ Segment::XY(uint16_t x, uint16_t y) {
uint16_t width = virtualWidth(); // segment width in logical pixels uint16_t width = virtualWidth(); // segment width in logical pixels
uint16_t height = virtualHeight(); // segment height in logical pixels uint16_t height = virtualHeight(); // segment height in logical pixels
return (x%width) + (y%height) * width; return (x%width) + (y%height) * width;
} }
void IRAM_ATTR Segment::setPixelColorXY(int x, int y, uint32_t col) void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col)
{ {
if (Segment::maxHeight==1) return; // not a matrix set-up if (Segment::maxHeight==1) return; // not a matrix set-up
if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit
@ -164,7 +202,6 @@ void IRAM_ATTR Segment::setPixelColorXY(int x, int y, uint32_t col)
if (leds) leds[XY(x,y)] = col; if (leds) leds[XY(x,y)] = col;
uint8_t _bri_t = currentBri(on ? opacity : 0); uint8_t _bri_t = currentBri(on ? opacity : 0);
if (!_bri_t && !transitional) return;
if (_bri_t < 255) { if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t); byte r = scale8(R(col), _bri_t);
byte g = scale8(G(col), _bri_t); byte g = scale8(G(col), _bri_t);
@ -266,8 +303,22 @@ 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. // Adds the specified color with the existing pixel color perserving color balance.
void Segment::addPixelColorXY(int x, int y, uint32_t color) { void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) {
setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color)); 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) { void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) {
@ -378,54 +429,55 @@ void Segment::blur1d(fract8 blur_amount) {
for (uint16_t y = 0; y < rows; y++) blurRow(y, 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 cols = virtualWidth();
const uint16_t rows = virtualHeight(); 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) { if (delta > 0) {
for (uint8_t y = 0; y < rows; y++) for (uint8_t x = 0; x < cols-1; x++) { for (int x = 0; x < cols-delta; x++) newPxCol[x] = getPixelColorXY((x + delta), y);
if (x + delta >= cols) break; for (int x = cols-delta; x < cols; x++) newPxCol[x] = getPixelColorXY(wrap ? (x + delta) - cols : x, y);
setPixelColorXY(x, y, getPixelColorXY((x + delta)%cols, y));
}
} else { } else {
for (uint8_t y = 0; y < rows; y++) for (int16_t x = cols-1; x >= 0; x--) { for (int x = cols-1; x >= -delta; x--) newPxCol[x] = getPixelColorXY((x + delta), y);
if (x + delta < 0) break; for (int x = -delta-1; x >= 0; x--) newPxCol[x] = getPixelColorXY(wrap ? (x + delta) + cols : x, y);
setPixelColorXY(x, y, getPixelColorXY(x + delta, 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 cols = virtualWidth();
const uint16_t rows = virtualHeight(); 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) { if (delta > 0) {
for (uint8_t x = 0; x < cols; x++) for (uint8_t y = 0; y < rows-1; y++) { for (int y = 0; y < rows-delta; y++) newPxCol[y] = getPixelColorXY(x, (y + delta));
if (y + delta >= rows) break; for (int y = rows-delta; y < rows; y++) newPxCol[y] = getPixelColorXY(x, wrap ? (y + delta) - rows : y);
setPixelColorXY(x, y, getPixelColorXY(x, (y + delta)));
}
} else { } else {
for (uint8_t x = 0; x < cols; x++) for (int16_t y = rows-1; y >= 0; y--) { for (int y = rows-1; y >= -delta; y--) newPxCol[y] = getPixelColorXY(x, (y + delta));
if (y + delta < 0) break; for (int y = -delta-1; y >= 0; y--) newPxCol[y] = getPixelColorXY(x, wrap ? (y + delta) + rows : y);
setPixelColorXY(x, y, getPixelColorXY(x, y + delta));
} }
for (int y = 0; y < rows; y++) setPixelColorXY(x, y, newPxCol[y]);
} }
} }
// move() - move all pixels in desired direction delta number of pixels // 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 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 // @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; if (delta==0) return;
switch (dir) { switch (dir) {
case 0: moveX( delta); break; case 0: moveX( delta, wrap); break;
case 1: moveX( delta); moveY( delta); break; case 1: moveX( delta, wrap); moveY( delta, wrap); break;
case 2: moveY( delta); break; case 2: moveY( delta, wrap); break;
case 3: moveX(-delta); moveY( delta); break; case 3: moveX(-delta, wrap); moveY( delta, wrap); break;
case 4: moveX(-delta); break; case 4: moveX(-delta, wrap); break;
case 5: moveX(-delta); moveY(-delta); break; case 5: moveX(-delta, wrap); moveY(-delta, wrap); break;
case 6: moveY(-delta); break; case 6: moveY(-delta, wrap); break;
case 7: moveX( delta); moveY(-delta); break; case 7: moveX( delta, wrap); moveY(-delta, wrap); break;
} }
} }
@ -527,7 +579,7 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
for (int j = 0; j<w; j++) { // character width for (int j = 0; j<w; j++) { // character width
int16_t x0 = x + (w-1) - j; int16_t x0 = x + (w-1) - j;
if ((x0 >= 0 || x0 < cols) && ((bits>>(j+(8-w))) & 0x01)) { // bit set & drawing on-screen if ((x0 >= 0 || x0 < cols) && ((bits>>(j+(8-w))) & 0x01)) { // bit set & drawing on-screen
addPixelColorXY(x0, y0, col); setPixelColorXY(x0, y0, col);
} }
} }
} }

View File

@ -81,7 +81,7 @@ uint16_t Segment::maxHeight = 1;
// copy constructor // copy constructor
Segment::Segment(const Segment &orig) { Segment::Segment(const Segment &orig) {
//DEBUG_PRINTLN(F("-- Copy segment constructor --")); //DEBUG_PRINTLN(F("-- Copy segment constructor --"));
memcpy(this, &orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
name = nullptr; name = nullptr;
data = nullptr; data = nullptr;
_dataLen = 0; _dataLen = 0;
@ -96,7 +96,7 @@ Segment::Segment(const Segment &orig) {
// move constructor // move constructor
Segment::Segment(Segment &&orig) noexcept { Segment::Segment(Segment &&orig) noexcept {
//DEBUG_PRINTLN(F("-- Move segment constructor --")); //DEBUG_PRINTLN(F("-- Move segment constructor --"));
memcpy(this, &orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.name = nullptr; orig.name = nullptr;
orig.data = nullptr; orig.data = nullptr;
orig._dataLen = 0; orig._dataLen = 0;
@ -114,7 +114,7 @@ Segment& Segment::operator= (const Segment &orig) {
if (leds && !Segment::_globalLeds) free(leds); if (leds && !Segment::_globalLeds) free(leds);
deallocateData(); deallocateData();
// copy source // copy source
memcpy(this, &orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
// erase pointers to allocated data // erase pointers to allocated data
name = nullptr; name = nullptr;
data = nullptr; data = nullptr;
@ -138,7 +138,7 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
deallocateData(); // free old runtime data deallocateData(); // free old runtime data
if (_t) delete _t; if (_t) delete _t;
if (leds && !Segment::_globalLeds) free(leds); if (leds && !Segment::_globalLeds) free(leds);
memcpy(this, &orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.name = nullptr; orig.name = nullptr;
orig.data = nullptr; orig.data = nullptr;
orig._dataLen = 0; orig._dataLen = 0;
@ -152,12 +152,12 @@ bool Segment::allocateData(size_t len) {
if (data && _dataLen == len) return true; //already allocated if (data && _dataLen == len) return true; //already allocated
deallocateData(); deallocateData();
if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) return false; //not enough memory if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) return false; //not enough memory
// if possible use SPI RAM on ESP32 // do not use SPI RAM on ESP32 since it is slow
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM) //#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM)
if (psramFound()) //if (psramFound())
data = (byte*) ps_malloc(len); // data = (byte*) ps_malloc(len);
else //else
#endif //#endif
data = (byte*) malloc(len); data = (byte*) malloc(len);
if (!data) return false; //allocation failed if (!data) return false; //allocation failed
Segment::addUsedSegmentData(len); Segment::addUsedSegmentData(len);
@ -184,7 +184,8 @@ void Segment::deallocateData() {
void Segment::resetIfRequired() { void Segment::resetIfRequired() {
if (reset) { if (reset) {
if (leds && !Segment::_globalLeds) { free(leds); leds = nullptr; } if (leds && !Segment::_globalLeds) { free(leds); leds = nullptr; }
//if (_t) { delete _t; _t = nullptr; transitional = false; } //if (transitional && _t) { transitional = false; delete _t; _t = nullptr; }
deallocateData();
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0; next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
reset = false; // setOption(SEG_OPTION_RESET, false); reset = false; // setOption(SEG_OPTION_RESET, false);
} }
@ -198,12 +199,12 @@ void Segment::setUpLeds() {
#else #else
leds = &Segment::_globalLeds[start]; leds = &Segment::_globalLeds[start];
#endif #endif
else if (!leds) { 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 defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
if (psramFound()) //if (psramFound())
leds = (CRGB*)ps_malloc(sizeof(CRGB)*length()); // leds = (CRGB*)ps_malloc(sizeof(CRGB)*length()); // softhack007 disabled; putting leds into psram leads to horrible slowdown on WROVER boards
else //else
#endif //#endif
leds = (CRGB*)malloc(sizeof(CRGB)*length()); leds = (CRGB*)malloc(sizeof(CRGB)*length());
} }
} }
@ -226,14 +227,15 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
case FX_MODE_NOISE16_4 : pal = 26; break; // landscape 33 case FX_MODE_NOISE16_4 : pal = 26; break; // landscape 33
case FX_MODE_GLITTER : pal = 11; break; // rainbow colors case FX_MODE_GLITTER : pal = 11; break; // rainbow colors
case FX_MODE_SUNRISE : pal = 35; break; // heat palette case FX_MODE_SUNRISE : pal = 35; break; // heat palette
case FX_MODE_FLOW : pal = 6; break; // party case FX_MODE_RAILWAY : pal = 3; break; // prim + sec
case FX_MODE_2DSOAP : pal = 11; break; // rainbow colors
} }
switch (pal) { switch (pal) {
case 0: //default palette. Exceptions for specific effects above case 0: //default palette. Exceptions for specific effects above
targetPalette = PartyColors_p; break; targetPalette = PartyColors_p; break;
case 1: {//periodically replace palette with a random one. Transition palette change in 500ms case 1: {//periodically replace palette with a random one. Transition palette change in 500ms
uint32_t timeSinceLastChange = millis() - _lastPaletteChange; uint32_t timeSinceLastChange = millis() - _lastPaletteChange;
if (timeSinceLastChange > 5000 /*+ ((uint32_t)(255-intensity))*100*/) { if (timeSinceLastChange > randomPaletteChangeTime * 1000U) {
prevRandomPalette = randomPalette; prevRandomPalette = randomPalette;
randomPalette = CRGBPalette16( randomPalette = CRGBPalette16(
CHSV(random8(), random8(160, 255), random8(128, 255)), CHSV(random8(), random8(160, 255), random8(128, 255)),
@ -303,23 +305,26 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
} }
void Segment::startTransition(uint16_t dur) { 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 // starting a transition has to occur before change so we get current values 1st
uint8_t _briT = currentBri(on ? opacity : 0); _t = new Transition(dur); // no previous transition running
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
if (!_t) return; // failed to allocate data 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->_palT = _palT;
_t->_modeP = _modeP; _t->_modeP = mode;
for (size_t i=0; i<NUM_COLORS; i++) _t->_colorT[i] = _colorT[i]; for (size_t i=0; i<NUM_COLORS; i++) _t->_colorT[i] = colors[i];
transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true); transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true);
} }
@ -332,10 +337,10 @@ uint16_t Segment::progress() {
} }
uint8_t Segment::currentBri(uint8_t briNew, bool useCct) { uint8_t Segment::currentBri(uint8_t briNew, bool useCct) {
if (transitional && _t) { uint32_t prog = progress();
uint32_t prog = progress() + 1; if (transitional && _t && prog < 0xFFFFU) {
if (useCct) return ((briNew * prog) + _t->_cctT * (0x10000 - prog)) >> 16; if (useCct) return ((briNew * prog) + _t->_cctT * (0xFFFFU - prog)) >> 16;
else return ((briNew * prog) + _t->_briT * (0x10000 - prog)) >> 16; else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16;
} else { } else {
return briNew; return briNew;
} }
@ -365,19 +370,18 @@ CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal
void Segment::handleTransition() { void Segment::handleTransition() {
if (!transitional) return; if (!transitional) return;
unsigned long maxWait = millis() + 20; uint16_t _progress = progress();
if (mode == FX_MODE_STATIC && next_time > maxWait) next_time = maxWait; if (_progress == 0xFFFFU) transitional = false; // finish transitioning segment
if (progress() == 0xFFFFU) { if (_t) { // thanks to @nXm AKA https://github.com/NMeirer
if (_t) { if (_progress >= 32767U && _t->_modeP != mode) markForReset();
if (_t->_modeP != mode) markForReset(); if (_progress == 0xFFFFU) {
delete _t; delete _t;
_t = nullptr; _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 //return if neither bounds nor grouping have changed
bool boundsUnchanged = (start == i1 && stop == i2); bool boundsUnchanged = (start == i1 && stop == i2);
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
@ -393,8 +397,8 @@ void Segment::set(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t o
markForReset(); markForReset();
return; return;
} }
if (i1 < Segment::maxWidth) start = i1; // Segment::maxWidth equals strip.getLengthTotal() for 1D if (i1 < Segment::maxWidth || (i1 >= Segment::maxWidth*Segment::maxHeight && i1 < strip.getLengthTotal())) start = i1; // Segment::maxWidth equals strip.getLengthTotal() for 1D
stop = i2 > Segment::maxWidth ? Segment::maxWidth : MAX(1,i2); stop = i2 > Segment::maxWidth*Segment::maxHeight ? MIN(i2,strip.getLengthTotal()) : (i2 > Segment::maxWidth ? Segment::maxWidth : MAX(1,i2));
startY = 0; startY = 0;
stopY = 1; stopY = 1;
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
@ -415,6 +419,10 @@ void Segment::set(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t o
bool Segment::setColor(uint8_t slot, uint32_t c) { //returns true if changed bool Segment::setColor(uint8_t slot, uint32_t c) { //returns true if changed
if (slot >= NUM_COLORS || c == colors[slot]) return false; if (slot >= NUM_COLORS || c == colors[slot]) return false;
if (!_isRGB && !_hasW) {
if (slot == 0 && c == BLACK) return false; // on/off segment cannot have primary color black
if (slot == 1 && c != BLACK) return false; // on/off segment cannot have secondary color non black
}
if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change
colors[slot] = c; colors[slot] = c;
stateChanged = true; // send UDP/WS broadcast stateChanged = true; // send UDP/WS broadcast
@ -468,7 +476,7 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
sOpt = extractModeDefaults(fx, "o2"); check2 = (sOpt >= 0) ? (bool)sOpt : false; sOpt = extractModeDefaults(fx, "o2"); check2 = (sOpt >= 0) ? (bool)sOpt : false;
sOpt = extractModeDefaults(fx, "o3"); check3 = (sOpt >= 0) ? (bool)sOpt : false; sOpt = extractModeDefaults(fx, "o3"); check3 = (sOpt >= 0) ? (bool)sOpt : false;
sOpt = extractModeDefaults(fx, "m12"); if (sOpt >= 0) map1D2D = constrain(sOpt, 0, 7); sOpt = extractModeDefaults(fx, "m12"); if (sOpt >= 0) map1D2D = constrain(sOpt, 0, 7);
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, "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, "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; sOpt = extractModeDefaults(fx, "rY"); if (sOpt >= 0) reverse_y = (bool)sOpt;
@ -546,7 +554,9 @@ uint16_t Segment::virtualLength() const {
void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col) void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
{ {
#ifndef WLED_DISABLE_2D
int vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows) int vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows)
#endif
i &= 0xFFFF; i &= 0xFFFF;
if (i >= virtualLength() || i<0) return; // if pixel would fall out of segment just exit if (i >= virtualLength() || i<0) return; // if pixel would fall out of segment just exit
@ -600,6 +610,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
} }
return; return;
} else if (Segment::maxHeight!=1 && (width()==1 || height()==1)) { } else if (Segment::maxHeight!=1 && (width()==1 || height()==1)) {
if (start < Segment::maxWidth*Segment::maxHeight) {
// we have a vertical or horizontal 1D segment (WARNING: virtual...() may be transposed) // we have a vertical or horizontal 1D segment (WARNING: virtual...() may be transposed)
int x = 0, y = 0; int x = 0, y = 0;
if (virtualHeight()>1) y = i; if (virtualHeight()>1) y = i;
@ -607,13 +618,13 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
setPixelColorXY(x, y, col); setPixelColorXY(x, y, col);
return; return;
} }
}
#endif #endif
if (leds) leds[i] = col; if (leds) leds[i] = col;
uint16_t len = length(); uint16_t len = length();
uint8_t _bri_t = currentBri(on ? opacity : 0); uint8_t _bri_t = currentBri(on ? opacity : 0);
if (!_bri_t && !transitional) return;
if (_bri_t < 255) { if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t); byte r = scale8(R(col), _bri_t);
byte g = scale8(G(col), _bri_t); byte g = scale8(G(col), _bri_t);
@ -684,7 +695,9 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa)
uint32_t Segment::getPixelColor(int i) uint32_t Segment::getPixelColor(int i)
{ {
#ifndef WLED_DISABLE_2D
int vStrip = i>>16; int vStrip = i>>16;
#endif
i &= 0xFFFF; i &= 0xFFFF;
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
@ -738,7 +751,7 @@ uint8_t Segment::differs(Segment& b) const {
if (startY != b.startY) d |= SEG_DIFFERS_BOUNDS; if (startY != b.startY) d |= SEG_DIFFERS_BOUNDS;
if (stopY != b.stopY) 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 & 0b1111111110011110U) != (b.options & 0b1111111110011110U)) d |= SEG_DIFFERS_OPT;
if ((options & 0x0001U) != (b.options & 0x0001U)) d |= SEG_DIFFERS_SEL; if ((options & 0x0001U) != (b.options & 0x0001U)) d |= SEG_DIFFERS_SEL;
for (uint8_t i = 0; i < NUM_COLORS; i++) if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL; for (uint8_t i = 0; i < NUM_COLORS; i++) if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL;
@ -747,29 +760,46 @@ uint8_t Segment::differs(Segment& b) const {
} }
void Segment::refreshLightCapabilities() { void Segment::refreshLightCapabilities() {
uint8_t capabilities = 0x01; uint8_t capabilities = 0;
uint16_t segStartIdx = 0xFFFFU;
uint16_t segStopIdx = 0;
if (start < Segment::maxWidth * Segment::maxHeight) {
// we are withing 2D matrix (includes 1D segments)
for (int y = startY; y < stopY; y++) for (int x = start; x < stop; x++) {
uint16_t index = x + Segment::maxWidth * y;
if (index < strip.customMappingSize) index = strip.customMappingTable[index]; // convert logical address to physical
if (index < 0xFFFFU) {
if (segStartIdx > index) segStartIdx = index;
if (segStopIdx < index) segStopIdx = index;
}
if (segStartIdx == segStopIdx) segStopIdx++; // we only have 1 pixel segment
}
} else {
// we are on the strip located after the matrix
segStartIdx = start;
segStopIdx = stop;
}
for (uint8_t b = 0; b < busses.getNumBusses(); b++) { for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
Bus *bus = busses.getBus(b); Bus *bus = busses.getBus(b);
if (bus == nullptr || bus->getLength()==0) break; if (bus == nullptr || bus->getLength()==0) break;
if (!bus->isOk()) continue; if (!bus->isOk()) continue;
if (bus->getStart() >= stop) continue; if (bus->getStart() >= segStopIdx) continue;
if (bus->getStart() + bus->getLength() <= start) continue; if (bus->getStart() + bus->getLength() <= segStartIdx) continue;
uint8_t type = bus->getType(); //uint8_t type = bus->getType();
if (type == TYPE_ONOFF || type == TYPE_ANALOG_1CH || (!cctFromRgb && type == TYPE_ANALOG_2CH)) capabilities &= 0xFE; // does not support RGB if (bus->hasRGB() || (cctFromRgb && bus->hasCCT())) capabilities |= SEG_CAPABILITY_RGB;
if (bus->isRgbw()) capabilities |= 0x02; // segment supports white channel if (!cctFromRgb && bus->hasCCT()) capabilities |= SEG_CAPABILITY_CCT;
if (!cctFromRgb) { if (correctWB && (bus->hasRGB() || bus->hasCCT())) capabilities |= SEG_CAPABILITY_CCT; //white balance correction (CCT slider)
switch (type) { if (bus->hasWhite()) {
case TYPE_ANALOG_5CH: uint8_t aWM = Bus::getGlobalAWMode() == AW_GLOBAL_DISABLED ? bus->getAutoWhiteMode() : Bus::getGlobalAWMode();
case TYPE_ANALOG_2CH:
capabilities |= 0x04; //segment supports white CCT
}
}
if (correctWB && !(type == TYPE_ANALOG_1CH || type == TYPE_ONOFF)) capabilities |= 0x04; //white balance correction (uses CCT slider)
uint8_t aWM = Bus::getAutoWhiteMode()<255 ? Bus::getAutoWhiteMode() : bus->getAWMode();
bool whiteSlider = (aWM == RGBW_MODE_DUAL || aWM == RGBW_MODE_MANUAL_ONLY); // white slider allowed bool whiteSlider = (aWM == RGBW_MODE_DUAL || aWM == RGBW_MODE_MANUAL_ONLY); // white slider allowed
if (bus->isRgbw() && (whiteSlider || !(capabilities & 0x01))) capabilities |= 0x08; // allow white channel adjustments (AWM allows or is not RGB) // if auto white calculation from RGB is active (Accurate/Brighter), force RGB controls even if there are no RGB busses
if (!whiteSlider) capabilities |= SEG_CAPABILITY_RGB;
// if auto white calculation from RGB is disabled/optional (None/Dual), allow white channel adjustments
if ( whiteSlider) capabilities |= SEG_CAPABILITY_W;
}
} }
_capabilities = capabilities; _capabilities = capabilities;
} }
@ -792,8 +822,22 @@ void Segment::blendPixelColor(int n, uint32_t color, uint8_t blend) {
} }
// Adds the specified color with the existing pixel color perserving color balance. // Adds the specified color with the existing pixel color perserving color balance.
void Segment::addPixelColor(int n, uint32_t color) { void Segment::addPixelColor(int n, uint32_t color, bool fast) {
setPixelColor(n, color_add(getPixelColor(n), color)); 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) { void Segment::fadePixelColor(uint16_t n, uint8_t fade) {
@ -893,7 +937,7 @@ void Segment::blur(uint8_t blur_amount)
* The colours are a transition r -> g -> b -> back to r * The colours are a transition r -> g -> b -> back to r
* Inspired by the Adafruit examples. * Inspired by the Adafruit examples.
*/ */
uint32_t Segment::color_wheel(uint8_t pos) { // TODO uint32_t Segment::color_wheel(uint8_t pos) {
if (palette) return color_from_palette(pos, false, true, 0); if (palette) return color_from_palette(pos, false, true, 0);
pos = 255 - pos; pos = 255 - pos;
if(pos < 85) { if(pos < 85) {
@ -998,7 +1042,7 @@ void WS2812FX::finalizeInit(void)
if (bus == nullptr) continue; if (bus == nullptr) continue;
if (bus->getStart() + bus->getLength() > MAX_LEDS) break; if (bus->getStart() + bus->getLength() > MAX_LEDS) break;
//RGBW mode is enabled if at least one of the strips is RGBW //RGBW mode is enabled if at least one of the strips is RGBW
_hasWhiteChannel |= bus->isRgbw(); _hasWhiteChannel |= bus->hasWhite();
//refresh is required to remain off if at least one of the strips requires the refresh. //refresh is required to remain off if at least one of the strips requires the refresh.
_isOffRefreshRequired |= bus->isOffRefreshRequired(); _isOffRefreshRequired |= bus->isOffRefreshRequired();
uint16_t busEnd = bus->getStart() + bus->getLength(); uint16_t busEnd = bus->getStart() + bus->getLength();
@ -1012,7 +1056,8 @@ void WS2812FX::finalizeInit(void)
#endif #endif
} }
if (!isMatrix) { // if 2D then max values defined in setUpMatrix() using panel set-up if (isMatrix) setUpMatrix();
else {
Segment::maxWidth = _length; Segment::maxWidth = _length;
Segment::maxHeight = 1; Segment::maxHeight = 1;
} }
@ -1024,17 +1069,21 @@ void WS2812FX::finalizeInit(void)
Segment::_globalLeds = nullptr; Segment::_globalLeds = nullptr;
} }
if (useLedsArray) { if (useLedsArray) {
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM) size_t arrSize = sizeof(CRGB) * getLengthTotal();
if (psramFound()) // softhack007 disabled; putting leds into psram leads to horrible slowdown on WROVER boards (see setUpLeds())
Segment::_globalLeds = (CRGB*) ps_malloc(sizeof(CRGB) * _length); //#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
else //if (psramFound())
#endif // Segment::_globalLeds = (CRGB*) ps_malloc(arrSize);
Segment::_globalLeds = (CRGB*) malloc(sizeof(CRGB) * _length); //else
memset(Segment::_globalLeds, 0, sizeof(CRGB) * _length); //#endif
Segment::_globalLeds = (CRGB*) malloc(arrSize);
memset(Segment::_globalLeds, 0, arrSize);
} }
//segments are created in makeAutoSegments(); //segments are created in makeAutoSegments();
DEBUG_PRINTLN(F("Loading custom palettes"));
loadCustomPalettes(); // (re)load all custom palettes loadCustomPalettes(); // (re)load all custom palettes
DEBUG_PRINTLN(F("Loading custom ledmaps"));
deserializeMap(); // (re)load default ledmap deserializeMap(); // (re)load default ledmap
} }
@ -1047,6 +1096,8 @@ void WS2812FX::service() {
_isServicing = true; _isServicing = true;
_segment_index = 0; _segment_index = 0;
for (segment &seg : _segments) { for (segment &seg : _segments) {
// process transition (mode changes in the middle of transition)
seg.handleTransition();
// reset the segment runtime data if needed // reset the segment runtime data if needed
seg.resetIfRequired(); seg.resetIfRequired();
@ -1075,8 +1126,6 @@ void WS2812FX::service() {
delay = (*_mode[seg.currentMode(seg.mode)])(); delay = (*_mode[seg.currentMode(seg.mode)])();
if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++; if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++;
if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
seg.handleTransition();
} }
seg.next_time = nowUp + delay; seg.next_time = nowUp + delay;
@ -1095,15 +1144,15 @@ void WS2812FX::service() {
void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col) void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col)
{ {
if (i >= _length) return;
if (i < customMappingSize) i = customMappingTable[i]; if (i < customMappingSize) i = customMappingTable[i];
if (i >= _length) return;
busses.setPixelColor(i, col); busses.setPixelColor(i, col);
} }
uint32_t WS2812FX::getPixelColor(uint16_t i) uint32_t WS2812FX::getPixelColor(uint16_t i)
{ {
if (i >= _length) return 0;
if (i < customMappingSize) i = customMappingTable[i]; if (i < customMappingSize) i = customMappingTable[i];
if (i >= _length) return 0;
return busses.getPixelColor(i); return busses.getPixelColor(i);
} }
@ -1149,12 +1198,12 @@ void WS2812FX::estimateCurrentAndLimitBri() {
uint32_t powerSum = 0; uint32_t powerSum = 0;
for (uint8_t b = 0; b < busses.getNumBusses(); b++) { for (uint_fast8_t bNum = 0; bNum < busses.getNumBusses(); bNum++) {
Bus *bus = busses.getBus(b); Bus *bus = busses.getBus(bNum);
if (bus->getType() >= TYPE_NET_DDP_RGB) continue; //exclude non-physical network busses if (bus->getType() >= TYPE_NET_DDP_RGB) continue; //exclude non-physical network busses
uint16_t len = bus->getLength(); uint16_t len = bus->getLength();
uint32_t busPowerSum = 0; 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); uint32_t c = bus->getPixelColor(i);
byte r = R(c), g = G(c), b = B(c), w = W(c); byte r = R(c), g = G(c), b = B(c), w = W(c);
@ -1165,7 +1214,7 @@ void WS2812FX::estimateCurrentAndLimitBri() {
} }
} }
if (bus->isRgbw()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less if (bus->hasWhite()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
busPowerSum *= 3; busPowerSum *= 3;
busPowerSum = busPowerSum >> 2; //same as /= 4 busPowerSum = busPowerSum >> 2; //same as /= 4
} }
@ -1282,6 +1331,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) uint8_t WS2812FX::getFirstSelectedSegId(void)
{ {
size_t i = 0; size_t i = 0;
@ -1316,6 +1373,12 @@ uint8_t WS2812FX::getActiveSegmentsNum(void) {
return c; return c;
} }
uint16_t WS2812FX::getLengthTotal(void) {
uint16_t len = Segment::maxWidth * Segment::maxHeight; // will be _length for 1D (see finalizeInit()) but should cover whole matrix for 2D
if (isMatrix && _length > len) len = _length; // for 2D with trailing strip
return len;
}
uint16_t WS2812FX::getLengthPhysical(void) { uint16_t WS2812FX::getLengthPhysical(void) {
uint16_t len = 0; uint16_t len = 0;
for (size_t b = 0; b < busses.getNumBusses(); b++) { for (size_t b = 0; b < busses.getNumBusses(); b++) {
@ -1333,12 +1396,7 @@ bool WS2812FX::hasRGBWBus(void) {
for (size_t b = 0; b < busses.getNumBusses(); b++) { for (size_t b = 0; b < busses.getNumBusses(); b++) {
Bus *bus = busses.getBus(b); Bus *bus = busses.getBus(b);
if (bus == nullptr || bus->getLength()==0) break; if (bus == nullptr || bus->getLength()==0) break;
switch (bus->getType()) { if (bus->hasRGB() && bus->hasWhite()) return true;
case TYPE_SK6812_RGBW:
case TYPE_TM1814:
case TYPE_ANALOG_4CH:
return true;
}
} }
return false; return false;
} }
@ -1363,7 +1421,6 @@ void WS2812FX::purgeSegments(bool force) {
if (_segments.size() <= 1) return; if (_segments.size() <= 1) return;
for (size_t i = _segments.size()-1; i > 0; i--) for (size_t i = _segments.size()-1; i > 0; i--)
if (_segments[i].stop == 0 || force) { if (_segments[i].stop == 0 || force) {
DEBUG_PRINT(F("Purging segment segment: ")); DEBUG_PRINTLN(i);
deleted++; deleted++;
_segments.erase(_segments.begin() + i); _segments.erase(_segments.begin() + i);
} }
@ -1379,7 +1436,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) { 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; 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() { void WS2812FX::restartRuntime() {
@ -1398,31 +1455,31 @@ void WS2812FX::resetSegments() {
} }
void WS2812FX::makeAutoSegments(bool forceReset) { void WS2812FX::makeAutoSegments(bool forceReset) {
if (isMatrix) { if (autoSegments) { //make one segment per bus
#ifndef WLED_DISABLE_2D
// only create 1 2D segment
if (forceReset || getSegmentsNum() == 0) resetSegments(); // initialises 1 segment
else if (getActiveSegmentsNum() == 1) {
size_t i = getLastActiveSegmentId();
_segments[i].start = 0;
_segments[i].stop = Segment::maxWidth;
_segments[i].startY = 0;
_segments[i].stopY = Segment::maxHeight;
_segments[i].grouping = 1;
_segments[i].spacing = 0;
_mainSegment = i;
}
#endif
} else if (autoSegments) { //make one segment per bus
uint16_t segStarts[MAX_NUM_SEGMENTS] = {0}; uint16_t segStarts[MAX_NUM_SEGMENTS] = {0};
uint16_t segStops [MAX_NUM_SEGMENTS] = {0}; uint16_t segStops [MAX_NUM_SEGMENTS] = {0};
uint8_t s = 0; size_t s = 0;
for (uint8_t i = 0; i < busses.getNumBusses(); i++) {
#ifndef WLED_DISABLE_2D
// 2D segment is the 1st one using entire matrix
if (isMatrix) {
segStarts[0] = 0;
segStops[0] = Segment::maxWidth*Segment::maxHeight;
s++;
}
#endif
for (size_t i = s; i < busses.getNumBusses(); i++) {
Bus* b = busses.getBus(i); Bus* b = busses.getBus(i);
segStarts[s] = b->getStart(); segStarts[s] = b->getStart();
segStops[s] = segStarts[s] + b->getLength(); segStops[s] = segStarts[s] + b->getLength();
#ifndef WLED_DISABLE_2D
if (isMatrix && segStops[s] < Segment::maxWidth*Segment::maxHeight) continue; // ignore buses comprising matrix
if (isMatrix && segStarts[s] < Segment::maxWidth*Segment::maxHeight) segStarts[s] = Segment::maxWidth*Segment::maxHeight;
#endif
//check for overlap with previous segments //check for overlap with previous segments
for (size_t j = 0; j < s; j++) { for (size_t j = 0; j < s; j++) {
if (segStops[j] > segStarts[s] && segStarts[j] < segStops[s]) { if (segStops[j] > segStarts[s] && segStarts[j] < segStops[s]) {
@ -1434,23 +1491,40 @@ void WS2812FX::makeAutoSegments(bool forceReset) {
} }
s++; s++;
} }
_segments.clear(); _segments.clear();
for (size_t i = 0; i < s; i++) { _segments.reserve(s); // prevent reallocations
Segment seg = Segment(segStarts[i], segStops[i]); // there is always at least one segment (but we need to differentiate between 1D and 2D)
seg.selected = true; #ifndef WLED_DISABLE_2D
_segments.push_back(seg); if (isMatrix)
_segments.push_back(Segment(0, Segment::maxWidth, 0, Segment::maxHeight));
else
#endif
_segments.push_back(Segment(segStarts[0], segStops[0]));
for (size_t i = 1; i < s; i++) {
_segments.push_back(Segment(segStarts[i], segStops[i]));
} }
_mainSegment = 0;
} else { } else {
if (forceReset || getSegmentsNum() == 0) resetSegments(); if (forceReset || getSegmentsNum() == 0) resetSegments();
//expand the main seg to the entire length, but only if there are no other segments, or reset is forced //expand the main seg to the entire length, but only if there are no other segments, or reset is forced
else if (getActiveSegmentsNum() == 1) { else if (getActiveSegmentsNum() == 1) {
size_t i = getLastActiveSegmentId(); size_t i = getLastActiveSegmentId();
#ifndef WLED_DISABLE_2D
_segments[i].start = 0;
_segments[i].stop = Segment::maxWidth;
_segments[i].startY = 0;
_segments[i].stopY = Segment::maxHeight;
_segments[i].grouping = 1;
_segments[i].spacing = 0;
#else
_segments[i].start = 0; _segments[i].start = 0;
_segments[i].stop = _length; _segments[i].stop = _length;
#endif
}
}
_mainSegment = 0; _mainSegment = 0;
}
}
fixInvalidSegments(); fixInvalidSegments();
} }
@ -1458,14 +1532,30 @@ void WS2812FX::makeAutoSegments(bool forceReset) {
void WS2812FX::fixInvalidSegments() { void WS2812FX::fixInvalidSegments() {
//make sure no segment is longer than total (sanity check) //make sure no segment is longer than total (sanity check)
for (size_t i = getSegmentsNum()-1; i > 0; i--) { for (size_t i = getSegmentsNum()-1; i > 0; i--) {
if (isMatrix) {
#ifndef WLED_DISABLE_2D
if (_segments[i].start >= Segment::maxWidth * Segment::maxHeight) {
// 1D segment at the end of matrix
if (_segments[i].start >= _length || _segments[i].startY > 0 || _segments[i].stopY > 1) { _segments.erase(_segments.begin()+i); continue; }
if (_segments[i].stop > _length) _segments[i].stop = _length;
continue;
}
if (_segments[i].start >= Segment::maxWidth || _segments[i].startY >= Segment::maxHeight) { _segments.erase(_segments.begin()+i); continue; }
if (_segments[i].stop > Segment::maxWidth) _segments[i].stop = Segment::maxWidth;
if (_segments[i].stopY > Segment::maxHeight) _segments[i].stopY = Segment::maxHeight;
#endif
} else {
if (_segments[i].start >= _length) { _segments.erase(_segments.begin()+i); continue; } if (_segments[i].start >= _length) { _segments.erase(_segments.begin()+i); continue; }
if (_segments[i].stop > _length) _segments[i].stop = _length; if (_segments[i].stop > _length) _segments[i].stop = _length;
// this is always called as the last step after finalizeInit(), update covered bus types
_segments[i].refreshLightCapabilities();
} }
} }
// this is always called as the last step after finalizeInit(), update covered bus types
for (segment &seg : _segments)
seg.refreshLightCapabilities();
}
//true if all segments align with a bus, or if a segment covers the total length //true if all segments align with a bus, or if a segment covers the total length
//irrelevant in 2D set-up
bool WS2812FX::checkSegmentAlignment() { bool WS2812FX::checkSegmentAlignment() {
bool aligned = false; bool aligned = false;
for (segment &seg : _segments) { for (segment &seg : _segments) {
@ -1482,8 +1572,7 @@ bool WS2812FX::checkSegmentAlignment() {
//After this function is called, setPixelColor() will use that segment (offsets, grouping, ... will apply) //After this function is called, setPixelColor() will use that segment (offsets, grouping, ... will apply)
//Note: If called in an interrupt (e.g. JSON API), original segment must be restored, //Note: If called in an interrupt (e.g. JSON API), original segment must be restored,
//otherwise it can lead to a crash on ESP32 because _segment_index is modified while in use by the main thread //otherwise it can lead to a crash on ESP32 because _segment_index is modified while in use by the main thread
uint8_t WS2812FX::setPixelSegment(uint8_t n) uint8_t WS2812FX::setPixelSegment(uint8_t n) {
{
uint8_t prevSegId = _segment_index; uint8_t prevSegId = _segment_index;
if (n < _segments.size()) { if (n < _segments.size()) {
_segment_index = n; _segment_index = n;
@ -1492,8 +1581,7 @@ uint8_t WS2812FX::setPixelSegment(uint8_t n)
return prevSegId; return prevSegId;
} }
void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) {
{
if (i2 >= i) if (i2 >= i)
{ {
for (uint16_t x = i; x <= i2; x++) setPixelColor(x, col); for (uint16_t x = i; x <= i2; x++) setPixelColor(x, col);
@ -1503,26 +1591,24 @@ void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col)
} }
} }
void WS2812FX::setTransitionMode(bool t) void WS2812FX::setTransitionMode(bool t) {
{ for (segment &seg : _segments) seg.startTransition(t ? _transitionDur : 0);
for (segment &seg : _segments) if (!seg.transitional) seg.startTransition(t ? _transitionDur : 0);
} }
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
void WS2812FX::printSize() void WS2812FX::printSize() {
{
size_t size = 0; size_t size = 0;
for (const Segment &seg : _segments) size += seg.getSize(); for (const Segment &seg : _segments) size += seg.getSize();
DEBUG_PRINTF("Segments: %d -> %uB\n", _segments.size(), size); DEBUG_PRINTF("Segments: %d -> %uB\n", _segments.size(), size);
DEBUG_PRINTF("Modes: %d*%d=%uB\n", sizeof(mode_ptr), _mode.size(), (_mode.capacity()*sizeof(mode_ptr))); DEBUG_PRINTF("Modes: %d*%d=%uB\n", sizeof(mode_ptr), _mode.size(), (_mode.capacity()*sizeof(mode_ptr)));
DEBUG_PRINTF("Data: %d*%d=%uB\n", sizeof(const char *), _modeData.size(), (_modeData.capacity()*sizeof(const char *))); DEBUG_PRINTF("Data: %d*%d=%uB\n", sizeof(const char *), _modeData.size(), (_modeData.capacity()*sizeof(const char *)));
DEBUG_PRINTF("Map: %d*%d=%uB\n", sizeof(uint16_t), (int)customMappingSize, customMappingSize*sizeof(uint16_t)); DEBUG_PRINTF("Map: %d*%d=%uB\n", sizeof(uint16_t), (int)customMappingSize, customMappingSize*sizeof(uint16_t));
if (useLedsArray) DEBUG_PRINTF("Buffer: %d*%d=%uB\n", sizeof(CRGB), (int)_length, _length*sizeof(CRGB)); size = getLengthTotal();
if (useLedsArray) DEBUG_PRINTF("Buffer: %d*%u=%uB\n", sizeof(CRGB), size, size*sizeof(CRGB));
} }
#endif #endif
void WS2812FX::loadCustomPalettes() void WS2812FX::loadCustomPalettes() {
{
byte tcp[72]; //support gradient palettes with up to 18 entries byte tcp[72]; //support gradient palettes with up to 18 entries
CRGBPalette16 targetPalette; CRGBPalette16 targetPalette;
customPalettes.clear(); // start fresh customPalettes.clear(); // start fresh
@ -1570,8 +1656,8 @@ void WS2812FX::loadCustomPalettes()
} }
//load custom mapping table from JSON file (called from finalizeInit() or deserializeState()) //load custom mapping table from JSON file (called from finalizeInit() or deserializeState())
void WS2812FX::deserializeMap(uint8_t n) { bool WS2812FX::deserializeMap(uint8_t n) {
if (isMatrix) return; // 2D support creates its own ledmap // 2D support creates its own ledmap (on the fly) if a ledmap.json exists it will overwrite built one.
char fileName[32]; char fileName[32];
strcpy_P(fileName, PSTR("/ledmap")); strcpy_P(fileName, PSTR("/ledmap"));
@ -1581,24 +1667,24 @@ void WS2812FX::deserializeMap(uint8_t n) {
if (!isFile) { if (!isFile) {
// erase custom mapping if selecting nonexistent ledmap.json (n==0) // erase custom mapping if selecting nonexistent ledmap.json (n==0)
if (!n && customMappingTable != nullptr) { if (!isMatrix && !n && customMappingTable != nullptr) {
customMappingSize = 0; customMappingSize = 0;
delete[] customMappingTable; delete[] customMappingTable;
customMappingTable = nullptr; customMappingTable = nullptr;
} }
return; return false;
} }
if (!requestJSONBufferLock(7)) return; if (!requestJSONBufferLock(7)) return false;
DEBUG_PRINT(F("Reading LED map from "));
DEBUG_PRINTLN(fileName);
if (!readObjectFromFile(fileName, nullptr, &doc)) { if (!readObjectFromFile(fileName, nullptr, &doc)) {
releaseJSONBufferLock(); releaseJSONBufferLock();
return; //if file does not exist just exit return false; //if file does not exist just exit
} }
DEBUG_PRINT(F("Reading LED map from "));
DEBUG_PRINTLN(fileName);
// erase old custom ledmap // erase old custom ledmap
if (customMappingTable != nullptr) { if (customMappingTable != nullptr) {
customMappingSize = 0; customMappingSize = 0;
@ -1611,21 +1697,17 @@ void WS2812FX::deserializeMap(uint8_t n) {
customMappingSize = map.size(); customMappingSize = map.size();
customMappingTable = new uint16_t[customMappingSize]; customMappingTable = new uint16_t[customMappingSize];
for (uint16_t i=0; i<customMappingSize; i++) { for (uint16_t i=0; i<customMappingSize; i++) {
customMappingTable[i] = (uint16_t) map[i]; customMappingTable[i] = (uint16_t) (map[i]<0 ? 0xFFFFU : map[i]);
} }
} }
releaseJSONBufferLock(); releaseJSONBufferLock();
return true;
} }
WS2812FX* WS2812FX::instance = nullptr; WS2812FX* WS2812FX::instance = nullptr;
//Bus static member definition, would belong in bus_manager.cpp
int16_t Bus::_cct = -1;
uint8_t Bus::_cctBlend = 0;
uint8_t Bus::_gAWM = 255;
const char JSON_mode_names[] PROGMEM = R"=====(["FX names moved"])====="; const char JSON_mode_names[] PROGMEM = R"=====(["FX names moved"])=====";
const char JSON_palette_names[] PROGMEM = R"=====([ const char JSON_palette_names[] PROGMEM = R"=====([
"Default","* Random Cycle","* Color 1","* Colors 1&2","* Color Gradient","* Colors Only","Party","Cloud","Lava","Ocean", "Default","* Random Cycle","* Color 1","* Colors 1&2","* Color Gradient","* Colors Only","Party","Cloud","Lava","Ocean",

View File

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

View File

@ -1,99 +0,0 @@
#include "wled.h"
#ifndef WLED_DISABLE_BLYNK
#include "src/dependencies/blynk/Blynk/BlynkHandlers.h"
#endif
/*
* Remote light control with the free Blynk app
*/
uint16_t blHue = 0;
byte blSat = 255;
void initBlynk(const char *auth, const char *host, uint16_t port)
{
#ifndef WLED_DISABLE_BLYNK
if (!WLED_CONNECTED) return;
blynkEnabled = (auth[0] != 0);
if (blynkEnabled) Blynk.config(auth, host, port);
#endif
}
void handleBlynk()
{
#ifndef WLED_DISABLE_BLYNK
if (WLED_CONNECTED && blynkEnabled)
Blynk.run();
#endif
}
void updateBlynk()
{
#ifndef WLED_DISABLE_BLYNK
if (!WLED_CONNECTED) return;
Blynk.virtualWrite(V0, bri);
//we need a RGB -> HSB convert here
Blynk.virtualWrite(V3, bri? 1:0);
Blynk.virtualWrite(V4, effectCurrent);
Blynk.virtualWrite(V5, effectSpeed);
Blynk.virtualWrite(V6, effectIntensity);
Blynk.virtualWrite(V7, nightlightActive);
Blynk.virtualWrite(V8, notifyDirect);
#endif
}
#ifndef WLED_DISABLE_BLYNK
BLYNK_WRITE(V0)
{
bri = param.asInt();//bri
stateUpdated(CALL_MODE_BLYNK);
}
BLYNK_WRITE(V1)
{
blHue = param.asInt();//hue
colorHStoRGB(blHue*10,blSat,col);
colorUpdated(CALL_MODE_BLYNK);
}
BLYNK_WRITE(V2)
{
blSat = param.asInt();//sat
colorHStoRGB(blHue*10,blSat,col);
colorUpdated(CALL_MODE_BLYNK);
}
BLYNK_WRITE(V3)
{
bool on = (param.asInt()>0);
if (!on != !bri) {toggleOnOff(); stateUpdated(CALL_MODE_BLYNK);}
}
BLYNK_WRITE(V4)
{
effectCurrent = param.asInt()-1;//fx
colorUpdated(CALL_MODE_BLYNK);
}
BLYNK_WRITE(V5)
{
effectSpeed = param.asInt();//sx
colorUpdated(CALL_MODE_BLYNK);
}
BLYNK_WRITE(V6)
{
effectIntensity = param.asInt();//ix
colorUpdated(CALL_MODE_BLYNK);
}
BLYNK_WRITE(V7)
{
nightlightActive = (param.asInt()>0);
}
BLYNK_WRITE(V8)
{
notifyDirect = (param.asInt()>0); //send notifications
}
#endif

565
wled00/bus_manager.cpp Normal file
View File

@ -0,0 +1,565 @@
/*
* Class implementation for addressing various light types
*/
#include <Arduino.h>
#include <IPAddress.h>
#include "const.h"
#include "pin_manager.h"
#include "bus_wrapper.h"
#include "bus_manager.h"
//colors.cpp
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
uint16_t approximateKelvinFromRGB(uint32_t rgb);
void colorRGBtoRGBW(byte* rgb);
//udp.cpp
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte *buffer, uint8_t bri=255, bool isRGBW=false);
// enable additional debug output
#if defined(WLED_DEBUG_HOST)
#include "net_debug.h"
#define DEBUGOUT NetDebug
#else
#define DEBUGOUT Serial
#endif
#ifdef WLED_DEBUG
#ifndef ESP8266
#include <rom/rtc.h>
#endif
#define DEBUG_PRINT(x) DEBUGOUT.print(x)
#define DEBUG_PRINTLN(x) DEBUGOUT.println(x)
#define DEBUG_PRINTF(x...) DEBUGOUT.printf(x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#define DEBUG_PRINTF(x...)
#endif
//color mangling macros
#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b))))
#define R(c) (byte((c) >> 16))
#define G(c) (byte((c) >> 8))
#define B(c) (byte(c))
#define W(c) (byte((c) >> 24))
void ColorOrderMap::add(uint16_t start, uint16_t len, uint8_t colorOrder) {
if (_count >= WLED_MAX_COLOR_ORDER_MAPPINGS) {
return;
}
if (len == 0) {
return;
}
if (colorOrder > COL_ORDER_MAX) {
return;
}
_mappings[_count].start = start;
_mappings[_count].len = len;
_mappings[_count].colorOrder = colorOrder;
_count++;
}
uint8_t IRAM_ATTR ColorOrderMap::getPixelColorOrder(uint16_t pix, uint8_t defaultColorOrder) const {
if (_count == 0) return defaultColorOrder;
// upper nibble containd W swap information
uint8_t swapW = defaultColorOrder >> 4;
for (uint8_t i = 0; i < _count; i++) {
if (pix >= _mappings[i].start && pix < (_mappings[i].start + _mappings[i].len)) {
return _mappings[i].colorOrder | (swapW << 4);
}
}
return defaultColorOrder;
}
uint32_t Bus::autoWhiteCalc(uint32_t c) {
uint8_t aWM = _autoWhiteMode;
if (_gAWM < 255) aWM = _gAWM;
if (aWM == RGBW_MODE_MANUAL_ONLY) return c;
uint8_t w = W(c);
//ignore auto-white calculation if w>0 and mode DUAL (DUAL behaves as BRIGHTER if w==0)
if (w > 0 && aWM == RGBW_MODE_DUAL) return c;
uint8_t r = R(c);
uint8_t g = G(c);
uint8_t b = B(c);
if (aWM == RGBW_MODE_MAX) return RGBW32(r, g, b, r > g ? (r > b ? r : b) : (g > b ? g : b)); // brightest RGB channel
w = r < g ? (r < b ? r : b) : (g < b ? g : b);
if (aWM == RGBW_MODE_AUTO_ACCURATE) { r -= w; g -= w; b -= w; } //subtract w in ACCURATE mode
return RGBW32(r, g, b, w);
}
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;
_skip = bc.skipAmount; //sacrificial pixels
_len = bc.count + _skip;
_iType = PolyBus::getI(bc.type, _pins, nr);
if (_iType == I_NONE) return;
uint16_t lenToCreate = _len;
if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(_len); // only needs a third of "RGB" LEDs for NeoPixelBus
_busPtr = PolyBus::create(_iType, _pins, lenToCreate, nr, _frequencykHz);
_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);
}
void BusDigital::show() {
PolyBus::show(_busPtr, _iType);
}
bool BusDigital::canShow() {
return PolyBus::canShow(_busPtr, _iType);
}
void BusDigital::setBrightness(uint8_t b) {
//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);
}
//If LEDs are skipped, it is possible to use the first as a status LED.
//TODO only show if no new show due in the next 50ms
void BusDigital::setStatusPixel(uint32_t c) {
if (_skip && canShow()) {
PolyBus::setPixelColor(_busPtr, _iType, 0, c, _colorOrderMap.getPixelColorOrder(_start, _colorOrder));
PolyBus::show(_busPtr, _iType);
}
}
void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
if (_type == TYPE_SK6812_RGBW || _type == TYPE_TM1814 || _type == TYPE_WS2812_1CH_X3) c = autoWhiteCalc(c);
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
if (reversed) pix = _len - pix -1;
else pix += _skip;
uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs
uint16_t pOld = pix;
pix = IC_INDEX_WS2812_1CH_3X(pix);
uint32_t cOld = PolyBus::getPixelColor(_busPtr, _iType, pix, co);
switch (pOld % 3) { // change only the single channel (TODO: this can cause loss because of get/set)
case 0: c = RGBW32(R(cOld), W(c) , B(cOld), 0); break;
case 1: c = RGBW32(W(c) , G(cOld), B(cOld), 0); break;
case 2: c = RGBW32(R(cOld), G(cOld), W(c) , 0); break;
}
}
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co);
}
uint32_t BusDigital::getPixelColor(uint16_t pix) {
if (reversed) pix = _len - pix -1;
else pix += _skip;
uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs
uint16_t pOld = pix;
pix = IC_INDEX_WS2812_1CH_3X(pix);
uint32_t c = PolyBus::getPixelColor(_busPtr, _iType, pix, co);
switch (pOld % 3) { // get only the single channel
case 0: c = RGBW32(G(c), G(c), G(c), G(c)); break;
case 1: c = RGBW32(R(c), R(c), R(c), R(c)); break;
case 2: c = RGBW32(B(c), B(c), B(c), B(c)); break;
}
return c;
}
return PolyBus::getPixelColor(_busPtr, _iType, pix, co);
}
uint8_t BusDigital::getPins(uint8_t* pinArray) {
uint8_t numPins = IS_2PIN(_type) ? 2 : 1;
for (uint8_t i = 0; i < numPins; i++) pinArray[i] = _pins[i];
return numPins;
}
void BusDigital::setColorOrder(uint8_t colorOrder) {
// upper nibble contains W swap information
if ((colorOrder & 0x0F) > 5) return;
_colorOrder = colorOrder;
}
void BusDigital::reinit() {
PolyBus::begin(_busPtr, _iType, _pins);
}
void BusDigital::cleanup() {
DEBUG_PRINTLN(F("Digital Cleanup."));
PolyBus::cleanup(_busPtr, _iType);
_iType = I_NONE;
_valid = false;
_busPtr = nullptr;
pinManager.deallocatePin(_pins[1], PinOwner::BusDigital);
pinManager.deallocatePin(_pins[0], PinOwner::BusDigital);
}
BusPwm::BusPwm(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
_valid = false;
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(_frequency);
#else
_ledcStart = pinManager.allocateLedc(numPins);
if (_ledcStart == 255) { //no more free LEDC channels
deallocatePins(); return;
}
#endif
for (uint8_t i = 0; i < numPins; i++) {
uint8_t currentPin = bc.pins[i];
if (!pinManager.allocatePin(currentPin, true, PinOwner::BusPwm)) {
deallocatePins(); return;
}
_pins[i] = currentPin; //store only after allocatePin() succeeds
#ifdef ESP8266
pinMode(_pins[i], OUTPUT);
#else
ledcSetup(_ledcStart + i, _frequency, 8);
ledcAttachPin(_pins[i], _ledcStart + i);
#endif
}
reversed = bc.reversed;
_valid = true;
}
void BusPwm::setPixelColor(uint16_t pix, uint32_t c) {
if (pix != 0 || !_valid) return; //only react to first pixel
if (_type != TYPE_ANALOG_3CH) c = autoWhiteCalc(c);
if (_cct >= 1900 && (_type == TYPE_ANALOG_3CH || _type == TYPE_ANALOG_4CH)) {
c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
}
uint8_t r = R(c);
uint8_t g = G(c);
uint8_t b = B(c);
uint8_t w = W(c);
uint8_t cct = 0; //0 - full warm white, 255 - full cold white
if (_cct > -1) {
if (_cct >= 1900) cct = (_cct - 1900) >> 5;
else if (_cct < 256) cct = _cct;
} else {
cct = (approximateKelvinFromRGB(c) - 1900) >> 5;
}
uint8_t ww, cw;
#ifdef WLED_USE_IC_CCT
ww = w;
cw = cct;
#else
//0 - linear (CCT 127 = 50% warm, 50% cold), 127 - additive CCT blending (CCT 127 = 100% warm, 100% cold)
if (cct < _cctBlend) ww = 255;
else ww = ((255-cct) * 255) / (255 - _cctBlend);
if ((255-cct) < _cctBlend) cw = 255;
else cw = (cct * 255) / (255 - _cctBlend);
ww = (w * ww) / 255; //brightness scaling
cw = (w * cw) / 255;
#endif
switch (_type) {
case TYPE_ANALOG_1CH: //one channel (white), relies on auto white calculation
_data[0] = w;
break;
case TYPE_ANALOG_2CH: //warm white + cold white
_data[1] = cw;
_data[0] = ww;
break;
case TYPE_ANALOG_5CH: //RGB + warm white + cold white
_data[4] = cw;
w = ww;
case TYPE_ANALOG_4CH: //RGBW
_data[3] = w;
case TYPE_ANALOG_3CH: //standard dumb RGB
_data[0] = r; _data[1] = g; _data[2] = b;
break;
}
}
//does no index check
uint32_t BusPwm::getPixelColor(uint16_t pix) {
if (!_valid) return 0;
return RGBW32(_data[0], _data[1], _data[2], _data[3]);
}
void BusPwm::show() {
if (!_valid) return;
uint8_t numPins = NUM_PWM_PINS(_type);
for (uint8_t i = 0; i < numPins; i++) {
uint8_t scaled = (_data[i] * _bri) / 255;
if (reversed) scaled = 255 - scaled;
#ifdef ESP8266
analogWrite(_pins[i], scaled);
#else
ledcWrite(_ledcStart + i, scaled);
#endif
}
}
uint8_t BusPwm::getPins(uint8_t* pinArray) {
if (!_valid) return 0;
uint8_t numPins = NUM_PWM_PINS(_type);
for (uint8_t i = 0; i < numPins; i++) {
pinArray[i] = _pins[i];
}
return numPins;
}
void BusPwm::deallocatePins() {
uint8_t numPins = NUM_PWM_PINS(_type);
for (uint8_t i = 0; i < numPins; i++) {
pinManager.deallocatePin(_pins[i], PinOwner::BusPwm);
if (!pinManager.isPinOk(_pins[i])) continue;
#ifdef ESP8266
digitalWrite(_pins[i], LOW); //turn off PWM interrupt
#else
if (_ledcStart < 16) ledcDetachPin(_pins[i]);
#endif
}
#ifdef ARDUINO_ARCH_ESP32
pinManager.deallocateLedc(_ledcStart, numPins);
#endif
}
BusOnOff::BusOnOff(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
_valid = false;
if (bc.type != TYPE_ONOFF) return;
uint8_t currentPin = bc.pins[0];
if (!pinManager.allocatePin(currentPin, true, PinOwner::BusOnOff)) {
return;
}
_pin = currentPin; //store only after allocatePin() succeeds
pinMode(_pin, OUTPUT);
reversed = bc.reversed;
_valid = true;
}
void BusOnOff::setPixelColor(uint16_t pix, uint32_t c) {
if (pix != 0 || !_valid) return; //only react to first pixel
c = autoWhiteCalc(c);
uint8_t r = R(c);
uint8_t g = G(c);
uint8_t b = B(c);
uint8_t w = W(c);
_data = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0;
}
uint32_t BusOnOff::getPixelColor(uint16_t pix) {
if (!_valid) return 0;
return RGBW32(_data, _data, _data, _data);
}
void BusOnOff::show() {
if (!_valid) return;
digitalWrite(_pin, reversed ? !(bool)_data : (bool)_data);
}
uint8_t BusOnOff::getPins(uint8_t* pinArray) {
if (!_valid) return 0;
pinArray[0] = _pin;
return 1;
}
BusNetwork::BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
_valid = false;
switch (bc.type) {
case TYPE_NET_ARTNET_RGB:
_rgbw = false;
_UDPtype = 2;
break;
case TYPE_NET_E131_RGB:
_rgbw = false;
_UDPtype = 1;
break;
default: // TYPE_NET_DDP_RGB / TYPE_NET_DDP_RGBW
_rgbw = bc.type == TYPE_NET_DDP_RGBW;
_UDPtype = 0;
break;
}
_UDPchannels = _rgbw ? 4 : 3;
_data = (byte *)malloc(bc.count * _UDPchannels);
if (_data == nullptr) return;
memset(_data, 0, bc.count * _UDPchannels);
_len = bc.count;
_client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]);
_broadcastLock = false;
_valid = true;
}
void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) {
if (!_valid || pix >= _len) return;
if (hasWhite()) c = autoWhiteCalc(c);
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
uint16_t offset = pix * _UDPchannels;
_data[offset] = R(c);
_data[offset+1] = G(c);
_data[offset+2] = B(c);
if (_rgbw) _data[offset+3] = W(c);
}
uint32_t BusNetwork::getPixelColor(uint16_t pix) {
if (!_valid || pix >= _len) return 0;
uint16_t offset = pix * _UDPchannels;
return RGBW32(_data[offset], _data[offset+1], _data[offset+2], _rgbw ? (_data[offset+3] << 24) : 0);
}
void BusNetwork::show() {
if (!_valid || !canShow()) return;
_broadcastLock = true;
realtimeBroadcast(_UDPtype, _client, _len, _data, _bri, _rgbw);
_broadcastLock = false;
}
uint8_t BusNetwork::getPins(uint8_t* pinArray) {
for (uint8_t i = 0; i < 4; i++) {
pinArray[i] = _client[i];
}
return 4;
}
void BusNetwork::cleanup() {
_type = I_NONE;
_valid = false;
if (_data != nullptr) free(_data);
_data = nullptr;
}
//utility to get the approx. memory usage of a given BusConfig
uint32_t BusManager::memUsage(BusConfig &bc) {
uint8_t type = bc.type;
uint16_t len = bc.count + bc.skipAmount;
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 > 28) return len*20; //RGBW
return len*15;
}
if (type > 28) return len*4; //RGBW
return len*3;
#else //ESP32 RMT uses double buffer?
if (type > 28) return len*8; //RGBW
return len*6;
#endif
}
if (type > 31 && type < 48) return 5;
return len*3; //RGB
}
int BusManager::add(BusConfig &bc) {
if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1;
if (bc.type >= TYPE_NET_DDP_RGB && bc.type < 96) {
busses[numBusses] = new BusNetwork(bc);
} else if (IS_DIGITAL(bc.type)) {
busses[numBusses] = new BusDigital(bc, numBusses, colorOrderMap);
} else if (bc.type == TYPE_ONOFF) {
busses[numBusses] = new BusOnOff(bc);
} else {
busses[numBusses] = new BusPwm(bc);
}
return numBusses++;
}
//do not call this method from system context (network callback)
void BusManager::removeAll() {
DEBUG_PRINTLN(F("Removing all."));
//prevents crashes due to deleting busses while in use.
while (!canAllShow()) yield();
for (uint8_t i = 0; i < numBusses; i++) delete busses[i];
numBusses = 0;
}
void BusManager::show() {
for (uint8_t i = 0; i < numBusses; i++) {
busses[i]->show();
}
}
void BusManager::setStatusPixel(uint32_t c) {
for (uint8_t i = 0; i < numBusses; i++) {
busses[i]->setStatusPixel(c);
}
}
void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c, int16_t cct) {
for (uint8_t i = 0; i < numBusses; i++) {
Bus* b = busses[i];
uint16_t bstart = b->getStart();
if (pix < bstart || pix >= bstart + b->getLength()) continue;
busses[i]->setPixelColor(pix - bstart, c);
}
}
void BusManager::setBrightness(uint8_t b) {
for (uint8_t i = 0; i < numBusses; i++) {
busses[i]->setBrightness(b);
}
}
void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) {
if (cct > 255) cct = 255;
if (cct >= 0) {
//if white balance correction allowed, save as kelvin value instead of 0-255
if (allowWBCorrection) cct = 1900 + (cct << 5);
} else cct = -1;
Bus::setCCT(cct);
}
uint32_t BusManager::getPixelColor(uint16_t pix) {
for (uint8_t i = 0; i < numBusses; i++) {
Bus* b = busses[i];
uint16_t bstart = b->getStart();
if (pix < bstart || pix >= bstart + b->getLength()) continue;
return b->getPixelColor(pix - bstart);
}
return 0;
}
bool BusManager::canAllShow() {
for (uint8_t i = 0; i < numBusses; i++) {
if (!busses[i]->canShow()) return false;
}
return true;
}
Bus* BusManager::getBus(uint8_t busNr) {
if (busNr >= numBusses) return nullptr;
return busses[busNr];
}
//semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
uint16_t BusManager::getTotalLength() {
uint16_t len = 0;
for (uint8_t i=0; i<numBusses; i++) len += busses[i]->getLength();
return len;
}
// Bus static member definition
int16_t Bus::_cct = -1;
uint8_t Bus::_cctBlend = 0;
uint8_t Bus::_gAWM = 255;

View File

@ -6,44 +6,17 @@
*/ */
#include "const.h" #include "const.h"
#include "pin_manager.h"
#include "bus_wrapper.h"
#include <Arduino.h>
//colors.cpp
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
void colorRGBtoRGBW(byte* rgb);
// enable additional debug output
#if defined(WLED_DEBUG_HOST)
#define DEBUGOUT NetDebug
#else
#define DEBUGOUT Serial
#endif
#ifdef WLED_DEBUG
#ifndef ESP8266
#include <rom/rtc.h>
#endif
#define DEBUG_PRINT(x) DEBUGOUT.print(x)
#define DEBUG_PRINTLN(x) DEBUGOUT.println(x)
#define DEBUG_PRINTF(x...) DEBUGOUT.printf(x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#define DEBUG_PRINTF(x...)
#endif
#define GET_BIT(var,bit) (((var)>>(bit))&0x01) #define GET_BIT(var,bit) (((var)>>(bit))&0x01)
#define SET_BIT(var,bit) ((var)|=(uint16_t)(0x0001<<(bit))) #define SET_BIT(var,bit) ((var)|=(uint16_t)(0x0001<<(bit)))
#define UNSET_BIT(var,bit) ((var)&=(~(uint16_t)(0x0001<<(bit)))) #define UNSET_BIT(var,bit) ((var)&=(~(uint16_t)(0x0001<<(bit))))
//color mangling macros #define NUM_ICS_WS2812_1CH_3X(len) (((len)+2)/3) // 1 WS2811 IC controls 3 zones (each zone has 1 LED, W)
#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b)))) #define IC_INDEX_WS2812_1CH_3X(i) ((i)/3)
#define R(c) (byte((c) >> 16))
#define G(c) (byte((c) >> 8)) #define NUM_ICS_WS2812_2CH_3X(len) (((len)+1)*2/3) // 2 WS2811 ICs control 3 zones (each zone has 2 LEDs, CW and WW)
#define B(c) (byte(c)) #define IC_INDEX_WS2812_2CH_3X(i) ((i)*2/3)
#define W(c) (byte((c) >> 24)) #define WS2812_2CH_3X_SPANS_2_ICS(i) ((i)&0x01) // every other LED zone is on two different ICs
//temporary struct for passing bus configuration to bus //temporary struct for passing bus configuration to bus
struct BusConfig { struct BusConfig {
@ -56,10 +29,11 @@ struct BusConfig {
bool refreshReq; bool refreshReq;
uint8_t autoWhite; uint8_t autoWhite;
uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255}; 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); 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) 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; uint8_t nPins = 1;
if (type >= TYPE_NET_DDP_RGB && type < 96) nPins = 4; //virtual network bus. 4 "pins" store IP address if (type >= TYPE_NET_DDP_RGB && type < 96) nPins = 4; //virtual network bus. 4 "pins" store IP address
else if (type > 47) nPins = 2; else if (type > 47) nPins = 2;
@ -88,21 +62,7 @@ struct ColorOrderMapEntry {
}; };
struct ColorOrderMap { struct ColorOrderMap {
void add(uint16_t start, uint16_t len, uint8_t colorOrder) { void add(uint16_t start, uint16_t len, uint8_t colorOrder);
if (_count >= WLED_MAX_COLOR_ORDER_MAPPINGS) {
return;
}
if (len == 0) {
return;
}
if (colorOrder > COL_ORDER_MAX) {
return;
}
_mappings[_count].start = start;
_mappings[_count].len = len;
_mappings[_count].colorOrder = colorOrder;
_count++;
}
uint8_t count() const { uint8_t count() const {
return _count; return _count;
@ -120,17 +80,7 @@ struct ColorOrderMap {
return &(_mappings[n]); return &(_mappings[n]);
} }
inline uint8_t IRAM_ATTR getPixelColorOrder(uint16_t pix, uint8_t defaultColorOrder) const { uint8_t getPixelColorOrder(uint16_t pix, uint8_t defaultColorOrder) const;
if (_count == 0) return defaultColorOrder;
// upper nibble containd W swap information
uint8_t swapW = defaultColorOrder >> 4;
for (uint8_t i = 0; i < _count; i++) {
if (pix >= _mappings[i].start && pix < (_mappings[i].start + _mappings[i].len)) {
return _mappings[i].colorOrder | (swapW << 4);
}
}
return defaultColorOrder;
}
private: private:
uint8_t _count; uint8_t _count;
@ -148,7 +98,7 @@ class Bus {
{ {
_type = type; _type = type;
_start = start; _start = start;
_autoWhiteMode = Bus::isRgbw(_type) ? aw : RGBW_MODE_MANUAL_ONLY; _autoWhiteMode = Bus::hasWhite(_type) ? aw : RGBW_MODE_MANUAL_ONLY;
}; };
virtual ~Bus() {} //throw the bus under the bus virtual ~Bus() {} //throw the bus under the bus
@ -165,6 +115,7 @@ class Bus {
virtual void setColorOrder() {} virtual void setColorOrder() {}
virtual uint8_t getColorOrder() { return COL_ORDER_RGB; } virtual uint8_t getColorOrder() { return COL_ORDER_RGB; }
virtual uint8_t skippedLeds() { return 0; } virtual uint8_t skippedLeds() { return 0; }
virtual uint16_t getFrequency() { return 0U; }
inline uint16_t getStart() { return _start; } inline uint16_t getStart() { return _start; }
inline void setStart(uint16_t start) { _start = start; } inline void setStart(uint16_t start) { _start = start; }
inline uint8_t getType() { return _type; } inline uint8_t getType() { return _type; }
@ -172,20 +123,20 @@ class Bus {
inline bool isOffRefreshRequired() { return _needsRefresh; } inline bool isOffRefreshRequired() { return _needsRefresh; }
bool containsPixel(uint16_t pix) { return pix >= _start && pix < _start+_len; } bool containsPixel(uint16_t pix) { return pix >= _start && pix < _start+_len; }
virtual bool isRgbw() { return Bus::isRgbw(_type); }
static bool isRgbw(uint8_t type) {
if (type == TYPE_SK6812_RGBW || type == TYPE_TM1814) return true;
if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true;
if (type == TYPE_NET_DDP_RGBW) return true;
return false;
}
virtual bool hasRGB() { virtual bool hasRGB() {
if (_type == TYPE_WS2812_1CH || _type == TYPE_WS2812_WWA || _type == TYPE_ANALOG_1CH || _type == TYPE_ANALOG_2CH || _type == TYPE_ONOFF) return false; if ((_type >= TYPE_WS2812_1CH && _type <= TYPE_WS2812_WWA) || _type == TYPE_ANALOG_1CH || _type == TYPE_ANALOG_2CH || _type == TYPE_ONOFF) return false;
return true; return true;
} }
virtual bool hasWhite() { virtual bool hasWhite() { return Bus::hasWhite(_type); }
if (_type == TYPE_SK6812_RGBW || _type == TYPE_TM1814 || _type == TYPE_WS2812_1CH || _type == TYPE_WS2812_WWA || static bool hasWhite(uint8_t type) {
_type == TYPE_ANALOG_1CH || _type == TYPE_ANALOG_2CH || _type == TYPE_ANALOG_4CH || _type == TYPE_ANALOG_5CH || _type == TYPE_NET_DDP_RGBW) return true; if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_SK6812_RGBW || type == TYPE_TM1814) return true; // digital types with white channel
if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true; // analog types with white channel
if (type == TYPE_NET_DDP_RGBW) return true; // network types with white channel
return false;
}
virtual bool hasCCT() {
if (_type == TYPE_WS2812_2CH_X3 || _type == TYPE_WS2812_WWA ||
_type == TYPE_ANALOG_2CH || _type == TYPE_ANALOG_5CH) return true;
return false; return false;
} }
static void setCCT(uint16_t cct) { static void setCCT(uint16_t cct) {
@ -199,10 +150,10 @@ class Bus {
if (_cctBlend > WLED_MAX_CCT_BLEND) _cctBlend = WLED_MAX_CCT_BLEND; if (_cctBlend > WLED_MAX_CCT_BLEND) _cctBlend = WLED_MAX_CCT_BLEND;
#endif #endif
} }
inline void setAWMode(uint8_t m) { if (m < 4) _autoWhiteMode = m; } inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; }
inline uint8_t getAWMode() { return _autoWhiteMode; } inline uint8_t getAutoWhiteMode() { return _autoWhiteMode; }
inline static void setAutoWhiteMode(uint8_t m) { if (m < 4) _gAWM = m; else _gAWM = 255; } inline static void setGlobalAWMode(uint8_t m) { if (m < 5) _gAWM = m; else _gAWM = AW_GLOBAL_DISABLED; }
inline static uint8_t getAutoWhiteMode() { return _gAWM; } inline static uint8_t getGlobalAWMode() { return _gAWM; }
bool reversed = false; bool reversed = false;
@ -214,94 +165,31 @@ class Bus {
bool _valid; bool _valid;
bool _needsRefresh; bool _needsRefresh;
uint8_t _autoWhiteMode; uint8_t _autoWhiteMode;
static uint8_t _gAWM; // definition in FX_fcn.cpp static uint8_t _gAWM;
static int16_t _cct; // definition in FX_fcn.cpp static int16_t _cct;
static uint8_t _cctBlend; // definition in FX_fcn.cpp static uint8_t _cctBlend;
uint32_t autoWhiteCalc(uint32_t c) { uint32_t autoWhiteCalc(uint32_t c);
uint8_t aWM = _autoWhiteMode;
if (_gAWM < 255) aWM = _gAWM;
if (aWM == RGBW_MODE_MANUAL_ONLY) return c;
uint8_t w = W(c);
//ignore auto-white calculation if w>0 and mode DUAL (DUAL behaves as BRIGHTER if w==0)
if (w > 0 && aWM == RGBW_MODE_DUAL) return c;
uint8_t r = R(c);
uint8_t g = G(c);
uint8_t b = B(c);
w = r < g ? (r < b ? r : b) : (g < b ? g : b);
if (aWM == RGBW_MODE_AUTO_ACCURATE) { r -= w; g -= w; b -= w; } //subtract w in ACCURATE mode
return RGBW32(r, g, b, w);
}
}; };
class BusDigital : public Bus { class BusDigital : public Bus {
public: public:
BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) : Bus(bc.type, bc.start, bc.autoWhite), _colorOrderMap(com) { BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com);
if (!IS_DIGITAL(bc.type) || !bc.count) return;
if (!pinManager.allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return;
_pins[0] = bc.pins[0];
if (IS_2PIN(bc.type)) {
if (!pinManager.allocatePin(bc.pins[1], true, PinOwner::BusDigital)) {
cleanup(); return;
}
_pins[1] = bc.pins[1];
}
reversed = bc.reversed;
_needsRefresh = bc.refreshReq || bc.type == TYPE_TM1814;
_skip = bc.skipAmount; //sacrificial pixels
_len = bc.count + _skip;
_iType = PolyBus::getI(bc.type, _pins, nr);
if (_iType == I_NONE) return;
_busPtr = PolyBus::create(_iType, _pins, _len, nr);
_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);
};
inline void show() { inline void show();
PolyBus::show(_busPtr, _iType);
}
inline bool canShow() { bool canShow();
return PolyBus::canShow(_busPtr, _iType);
}
void setBrightness(uint8_t b) { void setBrightness(uint8_t b);
//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);
}
//If LEDs are skipped, it is possible to use the first as a status LED. void setStatusPixel(uint32_t c);
//TODO only show if no new show due in the next 50ms
void setStatusPixel(uint32_t c) {
if (_skip && canShow()) {
PolyBus::setPixelColor(_busPtr, _iType, 0, c, _colorOrderMap.getPixelColorOrder(_start, _colorOrder));
PolyBus::show(_busPtr, _iType);
}
}
void setPixelColor(uint16_t pix, uint32_t c) { void setPixelColor(uint16_t pix, uint32_t c);
if (_type == TYPE_SK6812_RGBW || _type == TYPE_TM1814) c = autoWhiteCalc(c);
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
if (reversed) pix = _len - pix -1;
else pix += _skip;
PolyBus::setPixelColor(_busPtr, _iType, pix, c, _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder));
}
uint32_t getPixelColor(uint16_t pix) { uint32_t getPixelColor(uint16_t pix);
if (reversed) pix = _len - pix -1;
else pix += _skip;
return PolyBus::getPixelColor(_busPtr, _iType, pix, _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder));
}
inline uint8_t getColorOrder() { uint8_t getColorOrder() {
return _colorOrder; return _colorOrder;
} }
@ -309,35 +197,19 @@ class BusDigital : public Bus {
return _len - _skip; return _len - _skip;
} }
uint8_t getPins(uint8_t* pinArray) { uint8_t getPins(uint8_t* pinArray);
uint8_t numPins = IS_2PIN(_type) ? 2 : 1;
for (uint8_t i = 0; i < numPins; i++) pinArray[i] = _pins[i];
return numPins;
}
void setColorOrder(uint8_t colorOrder) { void setColorOrder(uint8_t colorOrder);
// upper nibble contains W swap information
if ((colorOrder & 0x0F) > 5) return;
_colorOrder = colorOrder;
}
inline uint8_t skippedLeds() { uint8_t skippedLeds() {
return _skip; return _skip;
} }
inline void reinit() { uint16_t getFrequency() { return _frequencykHz; }
PolyBus::begin(_busPtr, _iType, _pins);
}
void cleanup() { void reinit();
DEBUG_PRINTLN(F("Digital Cleanup."));
PolyBus::cleanup(_busPtr, _iType); void cleanup();
_iType = I_NONE;
_valid = false;
_busPtr = nullptr;
pinManager.deallocatePin(_pins[1], PinOwner::BusDigital);
pinManager.deallocatePin(_pins[0], PinOwner::BusDigital);
}
~BusDigital() { ~BusDigital() {
cleanup(); cleanup();
@ -346,8 +218,9 @@ class BusDigital : public Bus {
private: private:
uint8_t _colorOrder = COL_ORDER_GRB; uint8_t _colorOrder = COL_ORDER_GRB;
uint8_t _pins[2] = {255, 255}; uint8_t _pins[2] = {255, 255};
uint8_t _iType = I_NONE; uint8_t _iType = 0; //I_NONE;
uint8_t _skip = 0; uint8_t _skip = 0;
uint16_t _frequencykHz = 0U;
void * _busPtr = nullptr; void * _busPtr = nullptr;
const ColorOrderMap &_colorOrderMap; const ColorOrderMap &_colorOrderMap;
}; };
@ -355,119 +228,18 @@ class BusDigital : public Bus {
class BusPwm : public Bus { class BusPwm : public Bus {
public: public:
BusPwm(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { BusPwm(BusConfig &bc);
_valid = false;
if (!IS_PWM(bc.type)) return;
uint8_t numPins = NUM_PWM_PINS(bc.type);
#ifdef ESP8266 void setPixelColor(uint16_t pix, uint32_t c);
analogWriteRange(255); //same range as one RGB channel
analogWriteFreq(WLED_PWM_FREQ);
#else
_ledcStart = pinManager.allocateLedc(numPins);
if (_ledcStart == 255) { //no more free LEDC channels
deallocatePins(); return;
}
#endif
for (uint8_t i = 0; i < numPins; i++) {
uint8_t currentPin = bc.pins[i];
if (!pinManager.allocatePin(currentPin, true, PinOwner::BusPwm)) {
deallocatePins(); return;
}
_pins[i] = currentPin; //store only after allocatePin() succeeds
#ifdef ESP8266
pinMode(_pins[i], OUTPUT);
#else
ledcSetup(_ledcStart + i, WLED_PWM_FREQ, 8);
ledcAttachPin(_pins[i], _ledcStart + i);
#endif
}
reversed = bc.reversed;
_valid = true;
};
void setPixelColor(uint16_t pix, uint32_t c) {
if (pix != 0 || !_valid) return; //only react to first pixel
if (_type != TYPE_ANALOG_3CH) c = autoWhiteCalc(c);
if (_cct >= 1900 && (_type == TYPE_ANALOG_3CH || _type == TYPE_ANALOG_4CH)) {
c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
}
uint8_t r = R(c);
uint8_t g = G(c);
uint8_t b = B(c);
uint8_t w = W(c);
uint8_t cct = 0; //0 - full warm white, 255 - full cold white
if (_cct > -1) {
if (_cct >= 1900) cct = (_cct - 1900) >> 5;
else if (_cct < 256) cct = _cct;
} else {
cct = (approximateKelvinFromRGB(c) - 1900) >> 5;
}
uint8_t ww, cw;
#ifdef WLED_USE_IC_CCT
ww = w;
cw = cct;
#else
//0 - linear (CCT 127 = 50% warm, 50% cold), 127 - additive CCT blending (CCT 127 = 100% warm, 100% cold)
if (cct < _cctBlend) ww = 255;
else ww = ((255-cct) * 255) / (255 - _cctBlend);
if ((255-cct) < _cctBlend) cw = 255;
else cw = (cct * 255) / (255 - _cctBlend);
ww = (w * ww) / 255; //brightness scaling
cw = (w * cw) / 255;
#endif
switch (_type) {
case TYPE_ANALOG_1CH: //one channel (white), relies on auto white calculation
_data[0] = w;
break;
case TYPE_ANALOG_2CH: //warm white + cold white
_data[1] = cw;
_data[0] = ww;
break;
case TYPE_ANALOG_5CH: //RGB + warm white + cold white
_data[4] = cw;
w = ww;
case TYPE_ANALOG_4CH: //RGBW
_data[3] = w;
case TYPE_ANALOG_3CH: //standard dumb RGB
_data[0] = r; _data[1] = g; _data[2] = b;
break;
}
}
//does no index check //does no index check
uint32_t getPixelColor(uint16_t pix) { uint32_t getPixelColor(uint16_t pix);
if (!_valid) return 0;
return RGBW32(_data[0], _data[1], _data[2], _data[3]);
}
void show() { void show();
if (!_valid) return;
uint8_t numPins = NUM_PWM_PINS(_type);
for (uint8_t i = 0; i < numPins; i++) {
uint8_t scaled = (_data[i] * _bri) / 255;
if (reversed) scaled = 255 - scaled;
#ifdef ESP8266
analogWrite(_pins[i], scaled);
#else
ledcWrite(_ledcStart + i, scaled);
#endif
}
}
uint8_t getPins(uint8_t* pinArray) { uint8_t getPins(uint8_t* pinArray);
if (!_valid) return 0;
uint8_t numPins = NUM_PWM_PINS(_type); uint16_t getFrequency() { return _frequency; }
for (uint8_t i = 0; i < numPins; i++) {
pinArray[i] = _pins[i];
}
return numPins;
}
void cleanup() { void cleanup() {
deallocatePins(); deallocatePins();
@ -483,67 +255,23 @@ class BusPwm : public Bus {
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
uint8_t _ledcStart = 255; uint8_t _ledcStart = 255;
#endif #endif
uint16_t _frequency = 0U;
void deallocatePins() { void deallocatePins();
uint8_t numPins = NUM_PWM_PINS(_type);
for (uint8_t i = 0; i < numPins; i++) {
pinManager.deallocatePin(_pins[i], PinOwner::BusPwm);
if (!pinManager.isPinOk(_pins[i])) continue;
#ifdef ESP8266
digitalWrite(_pins[i], LOW); //turn off PWM interrupt
#else
if (_ledcStart < 16) ledcDetachPin(_pins[i]);
#endif
}
#ifdef ARDUINO_ARCH_ESP32
pinManager.deallocateLedc(_ledcStart, numPins);
#endif
}
}; };
class BusOnOff : public Bus { class BusOnOff : public Bus {
public: public:
BusOnOff(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { BusOnOff(BusConfig &bc);
_valid = false;
if (bc.type != TYPE_ONOFF) return;
uint8_t currentPin = bc.pins[0]; void setPixelColor(uint16_t pix, uint32_t c);
if (!pinManager.allocatePin(currentPin, true, PinOwner::BusOnOff)) {
return;
}
_pin = currentPin; //store only after allocatePin() succeeds
pinMode(_pin, OUTPUT);
reversed = bc.reversed;
_valid = true;
};
void setPixelColor(uint16_t pix, uint32_t c) { uint32_t getPixelColor(uint16_t pix);
if (pix != 0 || !_valid) return; //only react to first pixel
c = autoWhiteCalc(c);
uint8_t r = R(c);
uint8_t g = G(c);
uint8_t b = B(c);
uint8_t w = W(c);
_data = bool((r+g+b+w) && _bri) ? 0xFF : 0; void show();
}
uint32_t getPixelColor(uint16_t pix) { uint8_t getPins(uint8_t* pinArray);
if (!_valid) return 0;
return RGBW32(_data, _data, _data, _data);
}
void show() {
if (!_valid) return;
digitalWrite(_pin, reversed ? !(bool)_data : (bool)_data);
}
uint8_t getPins(uint8_t* pinArray) {
if (!_valid) return 0;
pinArray[0] = _pin;
return 1;
}
void cleanup() { void cleanup() {
pinManager.deallocatePin(_pin, PinOwner::BusOnOff); pinManager.deallocatePin(_pin, PinOwner::BusOnOff);
@ -561,89 +289,29 @@ class BusOnOff : public Bus {
class BusNetwork : public Bus { class BusNetwork : public Bus {
public: public:
BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { BusNetwork(BusConfig &bc);
_valid = false;
// switch (bc.type) {
// case TYPE_NET_ARTNET_RGB:
// _rgbw = false;
// _UDPtype = 2;
// break;
// case TYPE_NET_E131_RGB:
// _rgbw = false;
// _UDPtype = 1;
// break;
// case TYPE_NET_DDP_RGB:
// _rgbw = false;
// _UDPtype = 0;
// break;
// default: // TYPE_NET_DDP_RGB / TYPE_NET_DDP_RGBW
_rgbw = bc.type == TYPE_NET_DDP_RGBW;
_UDPtype = 0;
// break;
// }
_UDPchannels = _rgbw ? 4 : 3;
_data = (byte *)malloc(bc.count * _UDPchannels);
if (_data == nullptr) return;
memset(_data, 0, bc.count * _UDPchannels);
_len = bc.count;
_client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]);
_broadcastLock = false;
_valid = true;
};
bool hasRGB() { return true; } bool hasRGB() { return true; }
bool hasWhite() { return _rgbw; } bool hasWhite() { return _rgbw; }
void setPixelColor(uint16_t pix, uint32_t c) { void setPixelColor(uint16_t pix, uint32_t c);
if (!_valid || pix >= _len) return;
if (isRgbw()) c = autoWhiteCalc(c);
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
uint16_t offset = pix * _UDPchannels;
_data[offset] = R(c);
_data[offset+1] = G(c);
_data[offset+2] = B(c);
if (_rgbw) _data[offset+3] = W(c);
}
uint32_t getPixelColor(uint16_t pix) { uint32_t getPixelColor(uint16_t pix);
if (!_valid || pix >= _len) return 0;
uint16_t offset = pix * _UDPchannels;
return RGBW32(_data[offset], _data[offset+1], _data[offset+2], _rgbw ? (_data[offset+3] << 24) : 0);
}
void show() { void show();
if (!_valid || !canShow()) return;
_broadcastLock = true;
realtimeBroadcast(_UDPtype, _client, _len, _data, _bri, _rgbw);
_broadcastLock = false;
}
inline bool canShow() { bool canShow() {
// this should be a return value from UDP routine if it is still sending data out // this should be a return value from UDP routine if it is still sending data out
return !_broadcastLock; return !_broadcastLock;
} }
uint8_t getPins(uint8_t* pinArray) { uint8_t getPins(uint8_t* pinArray);
for (uint8_t i = 0; i < 4; i++) {
pinArray[i] = _client[i];
}
return 4;
}
inline bool isRgbw() { uint16_t getLength() {
return _rgbw;
}
inline uint16_t getLength() {
return _len; return _len;
} }
void cleanup() { void cleanup();
_type = I_NONE;
_valid = false;
if (_data != nullptr) free(_data);
_data = nullptr;
}
~BusNetwork() { ~BusNetwork() {
cleanup(); cleanup();
@ -664,129 +332,31 @@ class BusManager {
BusManager() {}; BusManager() {};
//utility to get the approx. memory usage of a given BusConfig //utility to get the approx. memory usage of a given BusConfig
static uint32_t memUsage(BusConfig &bc) { static uint32_t memUsage(BusConfig &bc);
uint8_t type = bc.type;
uint16_t len = bc.count + bc.skipAmount;
if (type > 15 && type < 32) {
#ifdef ESP8266
if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem
if (type > 29) return len*20; //RGBW
return len*15;
}
if (type > 29) return len*4; //RGBW
return len*3;
#else //ESP32 RMT uses double buffer?
if (type > 29) 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
}
/*
int add(BusConfig &bc); int add(BusConfig &bc);
void removeAll(); //do not call this method from system context (network callback)
void setStatusPixel(uint32_t c);
void setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1);
void setBrightness(uint8_t b);
void setSegmentCCT(int16_t cct, bool allowWBCorrection = false);
uint32_t getPixelColor(uint16_t pix);
bool canAllShow();
Bus* getBus(uint8_t busNr);
void show();
uint16_t getTotalLength(); //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
*/
// the following functions are inlined by compiler since they are defined within class definition
// they should be placed in cpp file instead
int add(BusConfig &bc) {
if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1;
if (bc.type >= TYPE_NET_DDP_RGB && bc.type < 96) {
busses[numBusses] = new BusNetwork(bc);
} else if (IS_DIGITAL(bc.type)) {
busses[numBusses] = new BusDigital(bc, numBusses, colorOrderMap);
} else if (bc.type == TYPE_ONOFF) {
busses[numBusses] = new BusOnOff(bc);
} else {
busses[numBusses] = new BusPwm(bc);
}
return numBusses++;
}
//do not call this method from system context (network callback) //do not call this method from system context (network callback)
void removeAll() { void removeAll();
DEBUG_PRINTLN(F("Removing all."));
//prevents crashes due to deleting busses while in use.
while (!canAllShow()) yield();
for (uint8_t i = 0; i < numBusses; i++) delete busses[i];
numBusses = 0;
}
void show() { void show();
for (uint8_t i = 0; i < numBusses; i++) {
busses[i]->show();
}
}
void setStatusPixel(uint32_t c) { void setStatusPixel(uint32_t c);
for (uint8_t i = 0; i < numBusses; i++) {
busses[i]->setStatusPixel(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);
for (uint8_t i = 0; i < numBusses; i++) {
Bus* b = busses[i];
uint16_t bstart = b->getStart();
if (pix < bstart || pix >= bstart + b->getLength()) continue;
busses[i]->setPixelColor(pix - bstart, c);
}
}
void setBrightness(uint8_t b) { void setBrightness(uint8_t b);
for (uint8_t i = 0; i < numBusses; i++) {
busses[i]->setBrightness(b);
}
}
void setSegmentCCT(int16_t cct, bool allowWBCorrection = false) { void setSegmentCCT(int16_t cct, bool allowWBCorrection = false);
if (cct > 255) cct = 255;
if (cct >= 0) {
//if white balance correction allowed, save as kelvin value instead of 0-255
if (allowWBCorrection) cct = 1900 + (cct << 5);
} else cct = -1;
Bus::setCCT(cct);
}
uint32_t getPixelColor(uint16_t pix) { uint32_t getPixelColor(uint16_t pix);
for (uint8_t i = 0; i < numBusses; i++) {
Bus* b = busses[i];
uint16_t bstart = b->getStart();
if (pix < bstart || pix >= bstart + b->getLength()) continue;
return b->getPixelColor(pix - bstart);
}
return 0;
}
bool canAllShow() { bool canAllShow();
for (uint8_t i = 0; i < numBusses; i++) {
if (!busses[i]->canShow()) return false;
}
return true;
}
Bus* getBus(uint8_t busNr) { Bus* getBus(uint8_t busNr);
if (busNr >= numBusses) return nullptr;
return busses[busNr];
}
//semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit()) //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
uint16_t getTotalLength() { uint16_t getTotalLength();
uint16_t len = 0;
for (uint8_t i=0; i<numBusses; i++) len += busses[i]->getLength();
return len;
}
inline void updateColorOrderMap(const ColorOrderMap &com) { inline void updateColorOrderMap(const ColorOrderMap &com) {
memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap)); memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap));

View File

@ -1,7 +1,7 @@
#ifndef BusWrapper_h #ifndef BusWrapper_h
#define BusWrapper_h #define BusWrapper_h
#include "NeoPixelBrightnessBus.h" #include "NeoPixelBusLg.h"
// temporary - these defines should actually be set in platformio.ini // temporary - these defines should actually be set in platformio.ini
// C3: I2S0 and I2S1 methods not supported (has one I2S bus) // 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_U1_TM2_3 18
#define I_8266_DM_TM2_3 19 #define I_8266_DM_TM2_3 19
#define I_8266_BB_TM2_3 20 #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 ***/ /*** ESP32 Neopixel methods ***/
//RGB //RGB
@ -80,6 +90,16 @@
#define I_32_I0_TM2_3 37 #define I_32_I0_TM2_3 37
#define I_32_I1_TM2_3 38 #define I_32_I1_TM2_3 38
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S) //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 //APA102
#define I_HS_DOT_3 39 //hardware SPI #define I_HS_DOT_3 39 //hardware SPI
@ -102,122 +122,164 @@
#define I_SS_LPO_3 48 #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 ***/ /*** ESP8266 Neopixel methods ***/
#ifdef ESP8266 #ifdef ESP8266
//RGB //RGB
#define B_8266_U0_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Uart0Ws2813Method> //3 chan, esp8266, gpio1 #define B_8266_U0_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod> //3 chan, esp8266, gpio1
#define B_8266_U1_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Uart1Ws2813Method> //3 chan, esp8266, gpio2 #define B_8266_U1_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod> //3 chan, esp8266, gpio2
#define B_8266_DM_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Dma800KbpsMethod> //3 chan, esp8266, gpio3 #define B_8266_DM_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Dma800KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, gpio3
#define B_8266_BB_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266BitBang800KbpsMethod> //3 chan, esp8266, bb (any pin but 16) #define B_8266_BB_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266BitBang800KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, bb (any pin but 16)
//RGBW //RGBW
#define B_8266_U0_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp8266Uart0Ws2813Method> //4 chan, esp8266, gpio1 #define B_8266_U0_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod> //4 chan, esp8266, gpio1
#define B_8266_U1_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp8266Uart1Ws2813Method> //4 chan, esp8266, gpio2 #define B_8266_U1_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod> //4 chan, esp8266, gpio2
#define B_8266_DM_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp8266Dma800KbpsMethod> //4 chan, esp8266, gpio3 #define B_8266_DM_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp8266Dma800KbpsMethod, NeoGammaNullMethod> //4 chan, esp8266, gpio3
#define B_8266_BB_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp8266BitBang800KbpsMethod> //4 chan, esp8266, bb (any pin) #define B_8266_BB_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp8266BitBang800KbpsMethod, NeoGammaNullMethod> //4 chan, esp8266, bb (any pin)
//400Kbps //400Kbps
#define B_8266_U0_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Uart0400KbpsMethod> //3 chan, esp8266, gpio1 #define B_8266_U0_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Uart0400KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, gpio1
#define B_8266_U1_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Uart1400KbpsMethod> //3 chan, esp8266, gpio2 #define B_8266_U1_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Uart1400KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, gpio2
#define B_8266_DM_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Dma400KbpsMethod> //3 chan, esp8266, gpio3 #define B_8266_DM_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266Dma400KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, gpio3
#define B_8266_BB_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266BitBang400KbpsMethod> //3 chan, esp8266, bb (any pin) #define B_8266_BB_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266BitBang400KbpsMethod, NeoGammaNullMethod> //3 chan, esp8266, bb (any pin)
//TM1814 (RGBW) //TM1814 (RGBW)
#define B_8266_U0_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266Uart0Tm1814Method> #define B_8266_U0_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266Uart0Tm1814Method, NeoGammaNullMethod>
#define B_8266_U1_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266Uart1Tm1814Method> #define B_8266_U1_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266Uart1Tm1814Method, NeoGammaNullMethod>
#define B_8266_DM_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266DmaTm1814Method> #define B_8266_DM_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266DmaTm1814Method, NeoGammaNullMethod>
#define B_8266_BB_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266BitBangTm1814Method> #define B_8266_BB_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266BitBangTm1814Method, NeoGammaNullMethod>
//TM1829 (RGB) //TM1829 (RGB)
#define B_8266_U0_TM2_4 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp8266Uart0Tm1829Method> #define B_8266_U0_TM2_4 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266Uart0Tm1829Method, NeoGammaNullMethod>
#define B_8266_U1_TM2_4 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp8266Uart1Tm1829Method> #define B_8266_U1_TM2_4 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266Uart1Tm1829Method, NeoGammaNullMethod>
#define B_8266_DM_TM2_4 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp8266DmaTm1829Method> #define B_8266_DM_TM2_4 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266DmaTm1829Method, NeoGammaNullMethod>
#define B_8266_BB_TM2_4 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp8266BitBangTm1829Method> #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 #endif
/*** ESP32 Neopixel methods ***/ /*** ESP32 Neopixel methods ***/
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
//RGB //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 #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 #endif
#ifndef WLED_NO_I2S1_PIXELBUS #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 #endif
//#define B_32_BB_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32BitBang800KbpsMethod> // NeoEsp8266BitBang800KbpsMethod //#define B_32_BB_NEO_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32BitBang800KbpsMethod, NeoGammaNullMethod> // NeoEsp8266BitBang800KbpsMethod
//RGBW //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 #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 #endif
#ifndef WLED_NO_I2S1_PIXELBUS #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 #endif
//#define B_32_BB_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32BitBang800KbpsMethod> // NeoEsp8266BitBang800KbpsMethod //#define B_32_BB_NEO_4 NeoPixelBusLg<NeoGrbwFeature, NeoEsp32BitBang800KbpsMethod, NeoGammaNullMethod> // NeoEsp8266BitBang800KbpsMethod
//400Kbps //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 #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 #endif
#ifndef WLED_NO_I2S1_PIXELBUS #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 #endif
//#define B_32_BB_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32BitBang400KbpsMethod> // NeoEsp8266BitBang400KbpsMethod //#define B_32_BB_400_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32BitBang400KbpsMethod, NeoGammaNullMethod> // NeoEsp8266BitBang400KbpsMethod
//TM1814 (RGBW) //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 #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 #endif
#ifndef WLED_NO_I2S1_PIXELBUS #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 #endif
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S) //Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
//TM1829 (RGB) //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 #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 #endif
#ifndef WLED_NO_I2S1_PIXELBUS #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 #endif
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S) //Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
#endif #endif
//APA102 //APA102
#define B_HS_DOT_3 NeoPixelBrightnessBus<DotStarBgrFeature, DotStarSpi5MhzMethod> //hardware SPI #ifdef WLED_USE_ETHERNET
#define B_SS_DOT_3 NeoPixelBrightnessBus<DotStarBgrFeature, DotStarMethod> //soft SPI // fix for #2542 (by @BlackBird77)
#define B_HS_DOT_3 NeoPixelBusLg<DotStarBgrFeature, DotStarEsp32HspiHzMethod, NeoGammaNullMethod> //hardware HSPI (was DotStarEsp32DmaHspi5MhzMethod in NPB @ 2.6.9)
#else
#define B_HS_DOT_3 NeoPixelBusLg<DotStarBgrFeature, DotStarSpiHzMethod, NeoGammaNullMethod> //hardware VSPI
#endif
#define B_SS_DOT_3 NeoPixelBusLg<DotStarBgrFeature, DotStarMethod, NeoGammaNullMethod> //soft SPI
//LPD8806 //LPD8806
#define B_HS_LPD_3 NeoPixelBrightnessBus<Lpd8806GrbFeature, Lpd8806SpiMethod> #define B_HS_LPD_3 NeoPixelBusLg<Lpd8806GrbFeature, Lpd8806SpiHzMethod, NeoGammaNullMethod>
#define B_SS_LPD_3 NeoPixelBrightnessBus<Lpd8806GrbFeature, Lpd8806Method> #define B_SS_LPD_3 NeoPixelBusLg<Lpd8806GrbFeature, Lpd8806Method, NeoGammaNullMethod>
//LPD6803 //LPD6803
#define B_HS_LPO_3 NeoPixelBrightnessBus<Lpd6803GrbFeature, Lpd6803SpiMethod> #define B_HS_LPO_3 NeoPixelBusLg<Lpd6803GrbFeature, Lpd6803SpiHzMethod, NeoGammaNullMethod>
#define B_SS_LPO_3 NeoPixelBrightnessBus<Lpd6803GrbFeature, Lpd6803Method> #define B_SS_LPO_3 NeoPixelBusLg<Lpd6803GrbFeature, Lpd6803Method, NeoGammaNullMethod>
//WS2801 //WS2801
#if defined(WLED_WS2801_SPEED_KHZ) && WLED_WS2801_SPEED_KHZ==40000 #ifdef WLED_USE_ETHERNET
#define B_HS_WS1_3 NeoPixelBrightnessBus<NeoRbgFeature, NeoWs2801Spi40MhzMethod> // fastest bus speed (not existing method?) #define B_HS_WS1_3 NeoPixelBusLg<NeoRbgFeature, Ws2801MethodBase<TwoWireHspiImple<SpiSpeedHz>>, NeoGammaNullMethod>
#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
#else #else
#define B_HS_WS1_3 NeoPixelBrightnessBus<NeoRbgFeature, NeoWs2801Spi2MhzMethod> // 2MHz; slower, more compatible #define B_HS_WS1_3 NeoPixelBusLg<NeoRbgFeature, Ws2801SpiHzMethod, NeoGammaNullMethod>
#endif #endif
#define B_SS_WS1_3 NeoPixelBrightnessBus<NeoRbgFeature, NeoWs2801Method> #define B_SS_WS1_3 NeoPixelBusLg<NeoRbgFeature, Ws2801Method, NeoGammaNullMethod>
//P9813 //P9813
#define B_HS_P98_3 NeoPixelBrightnessBus<P9813BgrFeature, P9813SpiMethod> #define B_HS_P98_3 NeoPixelBusLg<P9813BgrFeature, P9813SpiHzMethod, NeoGammaNullMethod>
#define B_SS_P98_3 NeoPixelBrightnessBus<P9813BgrFeature, P9813Method> #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 //handles pointer type conversion for all possible bus types
class PolyBus { class PolyBus {
public: 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. // Begin & initialize the PixelSettings for TM1814 strips.
template <class T> template <class T>
static void beginTM1814(void* busPtr) { static void beginTM1814(void* busPtr) {
@ -226,7 +288,7 @@ class PolyBus {
// Max current for each LED (22.5 mA). // Max current for each LED (22.5 mA).
tm1814_strip->SetPixelSettings(NeoTm1814Settings(/*R*/225, /*G*/225, /*B*/225, /*W*/225)); 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) { switch (busType) {
case I_NONE: break; case I_NONE: break;
#ifdef ESP8266 #ifdef ESP8266
@ -250,11 +312,19 @@ class PolyBus {
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->Begin(); break; case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->Begin(); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->Begin(); break; case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->Begin(); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->Begin(); break; case I_8266_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_DOT_3: beginDotStar<B_HS_DOT_3*>(busPtr, -1, -1, -1, -1, clock_kHz); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->Begin(); break; case I_HS_LPD_3: beginDotStar<B_HS_LPD_3*>(busPtr, -1, -1, -1, -1, clock_kHz); break;
case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->Begin(); break; case I_HS_LPO_3: beginDotStar<B_HS_LPO_3*>(busPtr, -1, -1, -1, -1, clock_kHz); break;
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->Begin(); break; case I_HS_WS1_3: beginDotStar<B_HS_WS1_3*>(busPtr, -1, -1, -1, -1, clock_kHz); break;
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->Begin(); 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 #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Begin(); break; case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Begin(); break;
@ -291,12 +361,28 @@ class PolyBus {
case I_32_I1_TM1_4: beginTM1814<B_32_I1_TM1_4*>(busPtr); break; 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; case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->Begin(); break;
#endif #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() // 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_DOT_3: beginDotStar<B_HS_DOT_3*>(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->Begin(pins[1], -1, pins[0], -1); 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: (static_cast<B_HS_LPO_3*>(busPtr))->Begin(pins[1], -1, pins[0], -1); 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: (static_cast<B_HS_WS1_3*>(busPtr))->Begin(pins[1], -1, pins[0], -1); 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: (static_cast<B_HS_P98_3*>(busPtr))->Begin(pins[1], -1, pins[0], -1); break; case I_HS_P98_3: beginDotStar<B_HS_P98_3*>(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break;
#endif #endif
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->Begin(); break; case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->Begin(); break;
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->Begin(); break; case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->Begin(); break;
@ -305,7 +391,7 @@ class PolyBus {
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->Begin(); break; 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; void* busPtr = nullptr;
switch (busType) { switch (busType) {
case I_NONE: break; case I_NONE: break;
@ -330,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_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_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_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 #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); break; case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); break;
@ -366,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_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; case I_32_I1_TM2_3: busPtr = new B_32_I1_TM2_3(len, pins[0]); break;
#endif #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 #endif
// for 2-wire: pins[1] is clk, pins[0] is dat. begin expects (len, clk, dat) // 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; case I_HS_DOT_3: busPtr = new B_HS_DOT_3(len, pins[1], pins[0]); break;
@ -379,7 +489,7 @@ class PolyBus {
case I_HS_P98_3: busPtr = new B_HS_P98_3(len, pins[1], pins[0]); break; 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; 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; return busPtr;
}; };
static void show(void* busPtr, uint8_t busType) { static void show(void* busPtr, uint8_t busType) {
@ -406,6 +516,14 @@ class PolyBus {
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->Show(); break; 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_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_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 #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Show(); break; case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Show(); break;
@ -442,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_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; case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->Show(); break;
#endif #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 #endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Show(); break; 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; case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->Show(); break;
@ -479,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_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_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_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 #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: return (static_cast<B_32_RN_NEO_3*>(busPtr))->CanShow(); break; case I_32_RN_NEO_3: return (static_cast<B_32_RN_NEO_3*>(busPtr))->CanShow(); break;
@ -515,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_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; case I_32_I1_TM2_3: return (static_cast<B_32_I1_TM2_3*>(busPtr))->CanShow(); break;
#endif #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 #endif
case I_HS_DOT_3: return (static_cast<B_HS_DOT_3*>(busPtr))->CanShow(); break; 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; case I_SS_DOT_3: return (static_cast<B_SS_DOT_3*>(busPtr))->CanShow(); break;
@ -556,36 +713,44 @@ class PolyBus {
switch (busType) { switch (busType) {
case I_NONE: break; case I_NONE: break;
#ifdef ESP8266 #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_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.R,col.G,col.B)); 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.R,col.G,col.B)); 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.R,col.G,col.B)); 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_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_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_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_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_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.R,col.G,col.B)); 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.R,col.G,col.B)); 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.R,col.G,col.B)); 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_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_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_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_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_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.R,col.G,col.B)); 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.R,col.G,col.B)); 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.R,col.G,col.B)); 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 #endif
#ifdef ARDUINO_ARCH_ESP32 #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 #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 #endif
#ifndef WLED_NO_I2S1_PIXELBUS #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 #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; case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetPixelColor(pix, col); break; case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
@ -594,108 +759,148 @@ class PolyBus {
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetPixelColor(pix, col); break; case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
#endif #endif
// case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->SetPixelColor(pix, col); break; // 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 #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 #endif
#ifndef WLED_NO_I2S1_PIXELBUS #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 #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_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 #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_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 #endif
#ifndef WLED_NO_I2S1_PIXELBUS #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_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 #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 #endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; #ifndef WLED_NO_I2S1_PIXELBUS
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; #endif
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; // case I_32_BB_UCS_3: (static_cast<B_32_BB_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); 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_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; #ifndef WLED_NO_I2S0_PIXELBUS
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; #endif
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; #ifndef WLED_NO_I2S1_PIXELBUS
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; 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) {
switch (busType) { switch (busType) {
case I_NONE: break; case I_NONE: break;
#ifdef ESP8266 #ifdef ESP8266
case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->SetBrightness(b); break; case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->SetLuminance(b); (static_cast<B_8266_U0_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->SetBrightness(b); break; case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->SetLuminance(b); (static_cast<B_8266_U1_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->SetBrightness(b); break; case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->SetLuminance(b); (static_cast<B_8266_DM_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->SetBrightness(b); break; case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->SetLuminance(b); (static_cast<B_8266_BB_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U0_NEO_4: (static_cast<B_8266_U0_NEO_4*>(busPtr))->SetBrightness(b); break; case I_8266_U0_NEO_4: (static_cast<B_8266_U0_NEO_4*>(busPtr))->SetLuminance(b); (static_cast<B_8266_U0_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U1_NEO_4: (static_cast<B_8266_U1_NEO_4*>(busPtr))->SetBrightness(b); break; case I_8266_U1_NEO_4: (static_cast<B_8266_U1_NEO_4*>(busPtr))->SetLuminance(b); (static_cast<B_8266_U1_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_DM_NEO_4: (static_cast<B_8266_DM_NEO_4*>(busPtr))->SetBrightness(b); break; case I_8266_DM_NEO_4: (static_cast<B_8266_DM_NEO_4*>(busPtr))->SetLuminance(b); (static_cast<B_8266_DM_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_BB_NEO_4: (static_cast<B_8266_BB_NEO_4*>(busPtr))->SetBrightness(b); break; case I_8266_BB_NEO_4: (static_cast<B_8266_BB_NEO_4*>(busPtr))->SetLuminance(b); (static_cast<B_8266_BB_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->SetBrightness(b); break; case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->SetLuminance(b); (static_cast<B_8266_U0_400_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->SetBrightness(b); break; case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->SetLuminance(b); (static_cast<B_8266_U1_400_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->SetBrightness(b); break; case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->SetLuminance(b); (static_cast<B_8266_DM_400_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->SetBrightness(b); break; case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->SetLuminance(b); (static_cast<B_8266_BB_400_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U0_TM1_4: (static_cast<B_8266_U0_TM1_4*>(busPtr))->SetBrightness(b); break; case I_8266_U0_TM1_4: (static_cast<B_8266_U0_TM1_4*>(busPtr))->SetLuminance(b); (static_cast<B_8266_U0_TM1_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetBrightness(b); break; case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetLuminance(b); (static_cast<B_8266_U1_TM1_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetBrightness(b); break; case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetLuminance(b); (static_cast<B_8266_DM_TM1_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetBrightness(b); break; case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetLuminance(b); (static_cast<B_8266_BB_TM1_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->SetBrightness(b); break; case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->SetLuminance(b); (static_cast<B_8266_U0_TM2_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->SetBrightness(b); break; case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->SetLuminance(b); (static_cast<B_8266_U1_TM2_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->SetBrightness(b); break; case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->SetLuminance(b); (static_cast<B_8266_DM_TM2_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->SetBrightness(b); break; case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->SetLuminance(b); (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); (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); (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); (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); (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); (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); (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); (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); (static_cast<B_8266_BB_UCS_4*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #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); (static_cast<B_32_RN_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
#ifndef WLED_NO_I2S0_PIXELBUS #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); (static_cast<B_32_I0_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #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); (static_cast<B_32_I1_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
// case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->SetBrightness(b); break; // case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->SetLuminance(b); (static_cast<B_32_BB_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetBrightness(b); break; case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetLuminance(b); (static_cast<B_32_RN_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
#ifndef WLED_NO_I2S0_PIXELBUS #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); (static_cast<B_32_I0_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #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); (static_cast<B_32_I1_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
// case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->SetBrightness(b); break; // case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->SetLuminance(b); (static_cast<B_32_BB_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetBrightness(b); break; case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetLuminance(b); (static_cast<B_32_RN_400_3*>(busPtr))->ApplyPostAdjustments(); break;
#ifndef WLED_NO_I2S0_PIXELBUS #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); (static_cast<B_32_I0_400_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #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); (static_cast<B_32_I1_400_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
// case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->SetBrightness(b); break; // case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->SetLuminance(b); (static_cast<B_32_BB_400_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetBrightness(b); break; case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetLuminance(b); (static_cast<B_32_RN_TM1_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetBrightness(b); break; case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetLuminance(b); (static_cast<B_32_RN_TM2_3*>(busPtr))->ApplyPostAdjustments(); break;
#ifndef WLED_NO_I2S0_PIXELBUS #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_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetLuminance(b); (static_cast<B_32_I0_TM1_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->SetBrightness(b); break; case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->SetLuminance(b); (static_cast<B_32_I0_TM2_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #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_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetLuminance(b); (static_cast<B_32_I1_TM1_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->SetBrightness(b); break; case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->SetLuminance(b); (static_cast<B_32_I1_TM2_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
case I_32_RN_UCS_3: (static_cast<B_32_RN_UCS_3*>(busPtr))->SetLuminance(b); (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); (static_cast<B_32_I0_UCS_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetBrightness(b); break; #ifndef WLED_NO_I2S1_PIXELBUS
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetBrightness(b); break; case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->SetLuminance(b); (static_cast<B_32_I1_UCS_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->SetBrightness(b); break; #endif
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->SetBrightness(b); break; // case I_32_BB_UCS_3: (static_cast<B_32_BB_UCS_3*>(busPtr))->SetLuminance(b); (static_cast<B_32_BB_UCS_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->SetBrightness(b); break; case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->SetLuminance(b); (static_cast<B_32_RN_UCS_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->SetBrightness(b); break; #ifndef WLED_NO_I2S0_PIXELBUS
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->SetBrightness(b); break; case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->SetLuminance(b); (static_cast<B_32_I0_UCS_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->SetBrightness(b); break; #endif
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetBrightness(b); break; #ifndef WLED_NO_I2S1_PIXELBUS
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetBrightness(b); break; case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->SetLuminance(b); (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); (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); (static_cast<B_HS_DOT_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetLuminance(b); (static_cast<B_SS_DOT_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->SetLuminance(b); (static_cast<B_HS_LPD_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->SetLuminance(b); (static_cast<B_SS_LPD_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->SetLuminance(b); (static_cast<B_HS_LPO_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->SetLuminance(b); (static_cast<B_SS_LPO_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->SetLuminance(b); (static_cast<B_HS_WS1_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->SetLuminance(b); (static_cast<B_SS_WS1_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetLuminance(b); (static_cast<B_HS_P98_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetLuminance(b); (static_cast<B_SS_P98_3*>(busPtr))->ApplyPostAdjustments(); break;
} }
}; };
static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) { static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) {
@ -723,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_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_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_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 #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: col = (static_cast<B_32_RN_NEO_3*>(busPtr))->GetPixelColor(pix); break; case I_32_RN_NEO_3: col = (static_cast<B_32_RN_NEO_3*>(busPtr))->GetPixelColor(pix); break;
@ -759,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_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; case I_32_I1_TM2_3: col = (static_cast<B_32_I1_TM2_3*>(busPtr))->GetPixelColor(pix); break;
#endif #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 #endif
case I_HS_DOT_3: col = (static_cast<B_HS_DOT_3*>(busPtr))->GetPixelColor(pix); break; 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; case I_SS_DOT_3: col = (static_cast<B_SS_DOT_3*>(busPtr))->GetPixelColor(pix); break;
@ -816,6 +1045,14 @@ class PolyBus {
case I_8266_U1_TM2_3: delete (static_cast<B_8266_U1_TM2_4*>(busPtr)); break; case I_8266_U1_TM2_3: delete (static_cast<B_8266_U1_TM2_4*>(busPtr)); break;
case I_8266_DM_TM2_3: delete (static_cast<B_8266_DM_TM2_4*>(busPtr)); break; case I_8266_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_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 #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: delete (static_cast<B_32_RN_NEO_3*>(busPtr)); break; case I_32_RN_NEO_3: delete (static_cast<B_32_RN_NEO_3*>(busPtr)); break;
@ -852,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_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; case I_32_I1_TM2_3: delete (static_cast<B_32_I1_TM2_3*>(busPtr)); break;
#endif #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 #endif
case I_HS_DOT_3: delete (static_cast<B_HS_DOT_3*>(busPtr)); break; 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; case I_SS_DOT_3: delete (static_cast<B_SS_DOT_3*>(busPtr)); break;
@ -894,6 +1147,8 @@ class PolyBus {
uint8_t offset = pins[0] -1; //for driver: 0 = uart0, 1 = uart1, 2 = dma, 3 = bitbang uint8_t offset = pins[0] -1; //for driver: 0 = uart0, 1 = uart1, 2 = dma, 3 = bitbang
if (offset > 3) offset = 3; if (offset > 3) offset = 3;
switch (busType) { switch (busType) {
case TYPE_WS2812_1CH_X3:
case TYPE_WS2812_2CH_X3:
case TYPE_WS2812_RGB: case TYPE_WS2812_RGB:
case TYPE_WS2812_WWA: case TYPE_WS2812_WWA:
return I_8266_U0_NEO_3 + offset; return I_8266_U0_NEO_3 + offset;
@ -905,6 +1160,10 @@ class PolyBus {
return I_8266_U0_TM1_4 + offset; return I_8266_U0_TM1_4 + offset;
case TYPE_TM1829: case TYPE_TM1829:
return I_8266_U0_TM2_3 + offset; 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 #else //ESP32
uint8_t offset = 0; //0 = RMT (num 0-7) 8 = I2S0 9 = I2S1 uint8_t offset = 0; //0 = RMT (num 0-7) 8 = I2S0 9 = I2S1
@ -926,6 +1185,8 @@ class PolyBus {
if (num > 7) offset = num -7; if (num > 7) offset = num -7;
#endif #endif
switch (busType) { switch (busType) {
case TYPE_WS2812_1CH_X3:
case TYPE_WS2812_2CH_X3:
case TYPE_WS2812_RGB: case TYPE_WS2812_RGB:
case TYPE_WS2812_WWA: case TYPE_WS2812_WWA:
return I_32_RN_NEO_3 + offset; return I_32_RN_NEO_3 + offset;
@ -937,6 +1198,10 @@ class PolyBus {
return I_32_RN_TM1_4 + offset; return I_32_RN_TM1_4 + offset;
case TYPE_TM1829: case TYPE_TM1829:
return I_32_RN_TM2_3 + offset; 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 #endif
} }

View File

@ -225,12 +225,10 @@ void handleButton()
{ {
static unsigned long lastRead = 0UL; static unsigned long lastRead = 0UL;
static unsigned long lastRun = 0UL; static unsigned long lastRun = 0UL;
bool analog = false;
unsigned long now = millis(); 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() && (now - lastRun < 400)) return; // don't interfere with strip update (unless strip is updating continuously, e.g. very long strips)
if (strip.isUpdating() && (millis() - lastRun < 400)) return; // be niced, but avoid button starvation lastRun = now;
lastRun = millis();
for (uint8_t b=0; b<WLED_MAX_BUTTONS; b++) { for (uint8_t b=0; b<WLED_MAX_BUTTONS; b++) {
#ifdef ESP8266 #ifdef ESP8266
@ -241,19 +239,32 @@ void handleButton()
if (usermods.handleButton(b)) continue; // did usermod handle buttons 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 if (buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) { // button is not a button but a potentiometer
analog = true; if (now - lastRead > ANALOG_BTN_READ_CYCLE) {
handleAnalog(b); continue; 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) // 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) { if (buttonType[b] == BTN_TYPE_SWITCH || buttonType[b] == BTN_TYPE_PIR_SENSOR) {
handleSwitch(b); continue; handleSwitch(b);
continue;
} }
// momentary button logic // momentary button logic
if (isButtonPressed(b)) { // pressed 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; if (!buttonPressedBefore[b]) buttonPressedTime[b] = now;
buttonPressedBefore[b] = true; buttonPressedBefore[b] = true;
@ -267,8 +278,14 @@ void handleButton()
} }
} else if (!isButtonPressed(b) && buttonPressedBefore[b]) { //released } else if (!isButtonPressed(b) && buttonPressedBefore[b]) { //released
long dur = now - buttonPressedTime[b]; 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 if (dur < WLED_DEBOUNCE_THRESHOLD) {buttonPressedBefore[b] = false; continue;} // too short "press", debounce
bool doublePress = buttonWaitTime[b]; //did we have a short press before? bool doublePress = buttonWaitTime[b]; //did we have a short press before?
buttonWaitTime[b] = 0; buttonWaitTime[b] = 0;
@ -305,7 +322,6 @@ void handleButton()
shortPressAction(b); shortPressAction(b);
} }
} }
if (analog) lastRead = now;
} }
// If enabled, RMT idle level is set to HIGH when off // If enabled, RMT idle level is set to HIGH when off

View File

@ -84,7 +84,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
uint8_t autoWhiteMode = RGBW_MODE_MANUAL_ONLY; uint8_t autoWhiteMode = RGBW_MODE_MANUAL_ONLY;
CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]); CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]);
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]); CJSON(strip.milliampsPerLed, hw_led[F("ledma")]);
Bus::setAutoWhiteMode(hw_led[F("rgbwm")] | 255); Bus::setGlobalAWMode(hw_led[F("rgbwm")] | 255);
CJSON(correctWB, hw_led["cct"]); CJSON(correctWB, hw_led["cct"]);
CJSON(cctFromRgb, hw_led[F("cr")]); CJSON(cctFromRgb, hw_led[F("cr")]);
CJSON(strip.cctBlending, hw_led[F("cb")]); CJSON(strip.cctBlending, hw_led[F("cb")]);
@ -98,11 +98,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (!matrix.isNull()) { if (!matrix.isNull()) {
strip.isMatrix = true; strip.isMatrix = true;
CJSON(strip.panels, matrix[F("mpc")]); CJSON(strip.panels, matrix[F("mpc")]);
CJSON(strip.matrix.bottomStart, matrix[F("pb")]);
CJSON(strip.matrix.rightStart, matrix[F("pr")]);
CJSON(strip.matrix.vertical, matrix[F("pv")]);
CJSON(strip.matrix.serpentine, matrix["ps"]);
strip.panel.clear(); strip.panel.clear();
JsonArray panels = matrix[F("panels")]; JsonArray panels = matrix[F("panels")];
uint8_t s = 0; uint8_t s = 0;
@ -130,8 +125,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
p.options = 0; p.options = 0;
strip.panel.push_back(p); strip.panel.push_back(p);
} }
// cannot call strip.setUpMatrix() here due to already locked JSON buffer
strip.setUpMatrix();
} }
#endif #endif
@ -162,10 +156,11 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
uint8_t ledType = elm["type"] | TYPE_WS2812_RGB; uint8_t ledType = elm["type"] | TYPE_WS2812_RGB;
bool reversed = elm["rev"]; bool reversed = elm["rev"];
bool refresh = elm["ref"] | false; 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 ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh
uint8_t AWmode = elm[F("rgbwm")] | autoWhiteMode; uint8_t AWmode = elm[F("rgbwm")] | autoWhiteMode;
if (fromFS) { 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); mem += BusManager::memUsage(bc);
if (mem <= MAX_LED_MEMORY) if (busses.add(bc) == -1) break; // finalization will be done in WLED::beginStrip() if (mem <= MAX_LED_MEMORY) if (busses.add(bc) == -1) break; // finalization will be done in WLED::beginStrip()
} else { } else {
@ -302,9 +297,11 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
PinManagerPinType i2c[2] = { { i2c_sda, true }, { i2c_scl, true } }; PinManagerPinType i2c[2] = { { i2c_sda, true }, { i2c_scl, true } };
if (i2c_scl >= 0 && i2c_sda >= 0 && pinManager.allocateMultiplePins(i2c, 2, PinOwner::HW_I2C)) { if (i2c_scl >= 0 && i2c_sda >= 0 && pinManager.allocateMultiplePins(i2c, 2, PinOwner::HW_I2C)) {
#ifdef ESP32 #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 #endif
Wire.begin();
} else { } else {
i2c_sda = -1; i2c_sda = -1;
i2c_scl = -1; i2c_scl = -1;
@ -333,18 +330,27 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(strip.paletteBlend, light[F("pal-mode")]); CJSON(strip.paletteBlend, light[F("pal-mode")]);
CJSON(autoSegments, light[F("aseg")]); CJSON(autoSegments, light[F("aseg")]);
CJSON(gammaCorrectVal, light["gc"]["val"]); // default 2.8
float light_gc_bri = light["gc"]["bri"]; float light_gc_bri = light["gc"]["bri"];
float light_gc_col = light["gc"]["col"]; // 2.8 float light_gc_col = light["gc"]["col"];
if (light_gc_bri > 1.5) gammaCorrectBri = true; if (light_gc_bri > 1.0f) gammaCorrectBri = true;
else if (light_gc_bri > 0.5) gammaCorrectBri = false; else gammaCorrectBri = false;
if (light_gc_col > 1.5) gammaCorrectCol = true; if (light_gc_col > 1.0f) gammaCorrectCol = true;
else if (light_gc_col > 0.5) gammaCorrectCol = false; else gammaCorrectCol = false;
if (gammaCorrectVal > 1.0f && gammaCorrectVal <= 3) {
if (gammaCorrectVal != 2.8f) NeoGammaWLEDMethod::calcGammaTable(gammaCorrectVal);
} else {
gammaCorrectVal = 1.0f; // no gamma correction
gammaCorrectBri = false;
gammaCorrectCol = false;
}
JsonObject light_tr = light["tr"]; JsonObject light_tr = light["tr"];
CJSON(fadeTransition, light_tr["mode"]); CJSON(fadeTransition, light_tr["mode"]);
int tdd = light_tr["dur"] | -1; int tdd = light_tr["dur"] | -1;
if (tdd >= 0) transitionDelay = transitionDelayDefault = tdd * 100; if (tdd >= 0) transitionDelay = transitionDelayDefault = tdd * 100;
CJSON(strip.paletteFade, light_tr["pal"]); CJSON(strip.paletteFade, light_tr["pal"]);
CJSON(randomPaletteChangeTime, light_tr[F("rpc")]);
JsonObject light_nl = light["nl"]; JsonObject light_nl = light["nl"];
CJSON(nightlightMode, light_nl["mode"]); CJSON(nightlightMode, light_nl["mode"]);
@ -406,6 +412,8 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (!DMXAddress || DMXAddress > 510) DMXAddress = 1; if (!DMXAddress || DMXAddress > 510) DMXAddress = 1;
CJSON(DMXSegmentSpacing, if_live_dmx[F("dss")]); CJSON(DMXSegmentSpacing, if_live_dmx[F("dss")]);
if (DMXSegmentSpacing > 150) DMXSegmentSpacing = 0; if (DMXSegmentSpacing > 150) DMXSegmentSpacing = 0;
CJSON(e131Priority, if_live_dmx[F("e131prio")]);
if (e131Priority > 200) e131Priority = 200;
CJSON(DMXMode, if_live_dmx["mode"]); CJSON(DMXMode, if_live_dmx["mode"]);
tdd = if_live[F("timeout")] | -1; tdd = if_live[F("timeout")] | -1;
@ -421,17 +429,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(alexaNumPresets, interfaces["va"]["p"]); CJSON(alexaNumPresets, interfaces["va"]["p"]);
#ifndef WLED_DISABLE_BLYNK
const char* apikey = interfaces["blynk"][F("token")] | "Hidden";
tdd = strnlen(apikey, 36);
if (tdd > 20 || tdd == 0)
getStringFromJson(blynkApiKey, apikey, 36); //normally not present due to security
JsonObject if_blynk = interfaces["blynk"];
getStringFromJson(blynkHost, if_blynk[F("host")], 33);
CJSON(blynkPort, if_blynk["port"]);
#endif
#ifdef WLED_ENABLE_MQTT #ifdef WLED_ENABLE_MQTT
JsonObject if_mqtt = interfaces["mqtt"]; JsonObject if_mqtt = interfaces["mqtt"];
CJSON(mqttEnabled, if_mqtt["en"]); CJSON(mqttEnabled, if_mqtt["en"]);
@ -443,8 +440,16 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
getStringFromJson(mqttDeviceTopic, if_mqtt[F("topics")][F("device")], 33); // "wled/test" getStringFromJson(mqttDeviceTopic, if_mqtt[F("topics")][F("device")], 33); // "wled/test"
getStringFromJson(mqttGroupTopic, if_mqtt[F("topics")][F("group")], 33); // "" getStringFromJson(mqttGroupTopic, if_mqtt[F("topics")][F("group")], 33); // ""
CJSON(retainMqttMsg, if_mqtt[F("rtn")]);
#endif #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 #ifndef WLED_DISABLE_HUESYNC
JsonObject if_hue = interfaces["hue"]; JsonObject if_hue = interfaces["hue"];
CJSON(huePollingEnabled, if_hue["en"]); CJSON(huePollingEnabled, if_hue["en"]);
@ -700,7 +705,7 @@ void serializeConfig() {
hw_led[F("cr")] = cctFromRgb; hw_led[F("cr")] = cctFromRgb;
hw_led[F("cb")] = strip.cctBlending; hw_led[F("cb")] = strip.cctBlending;
hw_led["fps"] = strip.getTargetFps(); hw_led["fps"] = strip.getTargetFps();
hw_led[F("rgbwm")] = Bus::getAutoWhiteMode(); // global override hw_led[F("rgbwm")] = Bus::getGlobalAWMode(); // global auto white mode override
hw_led[F("ld")] = strip.useLedsArray; hw_led[F("ld")] = strip.useLedsArray;
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
@ -708,11 +713,6 @@ void serializeConfig() {
if (strip.isMatrix) { if (strip.isMatrix) {
JsonObject matrix = hw_led.createNestedObject(F("matrix")); JsonObject matrix = hw_led.createNestedObject(F("matrix"));
matrix[F("mpc")] = strip.panels; matrix[F("mpc")] = strip.panels;
matrix[F("pb")] = strip.matrix.bottomStart;
matrix[F("pr")] = strip.matrix.rightStart;
matrix[F("pv")] = strip.matrix.vertical;
matrix["ps"] = strip.matrix.serpentine;
JsonArray panels = matrix.createNestedArray(F("panels")); JsonArray panels = matrix.createNestedArray(F("panels"));
for (uint8_t i=0; i<strip.panel.size(); i++) { for (uint8_t i=0; i<strip.panel.size(); i++) {
JsonObject pnl = panels.createNestedObject(); JsonObject pnl = panels.createNestedObject();
@ -745,7 +745,8 @@ void serializeConfig() {
ins[F("skip")] = bus->skippedLeds(); ins[F("skip")] = bus->skippedLeds();
ins["type"] = bus->getType() & 0x7F; ins["type"] = bus->getType() & 0x7F;
ins["ref"] = bus->isOffRefreshRequired(); ins["ref"] = bus->isOffRefreshRequired();
ins[F("rgbwm")] = bus->getAWMode(); ins[F("rgbwm")] = bus->getAutoWhiteMode();
ins[F("freq")] = bus->getFrequency();
} }
JsonArray hw_com = hw.createNestedArray(F("com")); JsonArray hw_com = hw.createNestedArray(F("com"));
@ -810,13 +811,15 @@ void serializeConfig() {
light[F("aseg")] = autoSegments; light[F("aseg")] = autoSegments;
JsonObject light_gc = light.createNestedObject("gc"); JsonObject light_gc = light.createNestedObject("gc");
light_gc["bri"] = (gammaCorrectBri) ? 2.8 : 1.0; light_gc["bri"] = (gammaCorrectBri) ? gammaCorrectVal : 1.0f; // keep compatibility
light_gc["col"] = (gammaCorrectCol) ? 2.8 : 1.0; light_gc["col"] = (gammaCorrectCol) ? gammaCorrectVal : 1.0f; // keep compatibility
light_gc["val"] = gammaCorrectVal;
JsonObject light_tr = light.createNestedObject("tr"); JsonObject light_tr = light.createNestedObject("tr");
light_tr["mode"] = fadeTransition; light_tr["mode"] = fadeTransition;
light_tr["dur"] = transitionDelayDefault / 100; light_tr["dur"] = transitionDelayDefault / 100;
light_tr["pal"] = strip.paletteFade; light_tr["pal"] = strip.paletteFade;
light_tr[F("rpc")] = randomPaletteChangeTime;
JsonObject light_nl = light.createNestedObject("nl"); JsonObject light_nl = light.createNestedObject("nl");
light_nl["mode"] = nightlightMode; light_nl["mode"] = nightlightMode;
@ -865,6 +868,7 @@ void serializeConfig() {
JsonObject if_live_dmx = if_live.createNestedObject("dmx"); JsonObject if_live_dmx = if_live.createNestedObject("dmx");
if_live_dmx[F("uni")] = e131Universe; if_live_dmx[F("uni")] = e131Universe;
if_live_dmx[F("seqskip")] = e131SkipOutOfSequence; if_live_dmx[F("seqskip")] = e131SkipOutOfSequence;
if_live_dmx[F("e131prio")] = e131Priority;
if_live_dmx[F("addr")] = DMXAddress; if_live_dmx[F("addr")] = DMXAddress;
if_live_dmx[F("dss")] = DMXSegmentSpacing; if_live_dmx[F("dss")] = DMXSegmentSpacing;
if_live_dmx["mode"] = DMXMode; if_live_dmx["mode"] = DMXMode;
@ -883,13 +887,6 @@ void serializeConfig() {
if_va["p"] = alexaNumPresets; if_va["p"] = alexaNumPresets;
#ifndef WLED_DISABLE_BLYNK
JsonObject if_blynk = interfaces.createNestedObject("blynk");
if_blynk[F("token")] = strlen(blynkApiKey) ? "Hidden":"";
if_blynk[F("host")] = blynkHost;
if_blynk["port"] = blynkPort;
#endif
#ifdef WLED_ENABLE_MQTT #ifdef WLED_ENABLE_MQTT
JsonObject if_mqtt = interfaces.createNestedObject("mqtt"); JsonObject if_mqtt = interfaces.createNestedObject("mqtt");
if_mqtt["en"] = mqttEnabled; if_mqtt["en"] = mqttEnabled;
@ -898,12 +895,20 @@ void serializeConfig() {
if_mqtt[F("user")] = mqttUser; if_mqtt[F("user")] = mqttUser;
if_mqtt[F("pskl")] = strlen(mqttPass); if_mqtt[F("pskl")] = strlen(mqttPass);
if_mqtt[F("cid")] = mqttClientID; if_mqtt[F("cid")] = mqttClientID;
if_mqtt[F("rtn")] = retainMqttMsg;
JsonObject if_mqtt_topics = if_mqtt.createNestedObject(F("topics")); JsonObject if_mqtt_topics = if_mqtt.createNestedObject(F("topics"));
if_mqtt_topics[F("device")] = mqttDeviceTopic; if_mqtt_topics[F("device")] = mqttDeviceTopic;
if_mqtt_topics[F("group")] = mqttGroupTopic; if_mqtt_topics[F("group")] = mqttGroupTopic;
#endif #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 #ifndef WLED_DISABLE_HUESYNC
JsonObject if_hue = interfaces.createNestedObject("hue"); JsonObject if_hue = interfaces.createNestedObject("hue");
if_hue["en"] = huePollingEnabled; if_hue["en"] = huePollingEnabled;
@ -1020,13 +1025,6 @@ bool deserializeConfigSec() {
JsonObject interfaces = doc["if"]; JsonObject interfaces = doc["if"];
#ifndef WLED_DISABLE_BLYNK
const char* apikey = interfaces["blynk"][F("token")] | "Hidden";
int tdd = strnlen(apikey, 36);
if (tdd > 20 || tdd == 0)
getStringFromJson(blynkApiKey, apikey, 36);
#endif
#ifdef WLED_ENABLE_MQTT #ifdef WLED_ENABLE_MQTT
JsonObject if_mqtt = interfaces["mqtt"]; JsonObject if_mqtt = interfaces["mqtt"];
getStringFromJson(mqttPass, if_mqtt["psk"], 65); getStringFromJson(mqttPass, if_mqtt["psk"], 65);
@ -1065,10 +1063,6 @@ void serializeConfigSec() {
ap["psk"] = apPass; ap["psk"] = apPass;
JsonObject interfaces = doc.createNestedObject("if"); JsonObject interfaces = doc.createNestedObject("if");
#ifndef WLED_DISABLE_BLYNK
JsonObject if_blynk = interfaces.createNestedObject("blynk");
if_blynk[F("token")] = blynkApiKey;
#endif
#ifdef WLED_ENABLE_MQTT #ifdef WLED_ENABLE_MQTT
JsonObject if_mqtt = interfaces.createNestedObject("mqtt"); JsonObject if_mqtt = interfaces.createNestedObject("mqtt");
if_mqtt["psk"] = mqttPass; if_mqtt["psk"] = mqttPass;

View File

@ -57,39 +57,42 @@ void setRandomColor(byte* rgb)
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) //hue, sat to rgb void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) //hue, sat to rgb
{ {
float h = ((float)hue)/65535.0; float h = ((float)hue)/65535.0f;
float s = ((float)sat)/255.0; float s = ((float)sat)/255.0f;
byte i = floor(h*6); int i = floorf(h*6);
float f = h * 6-i; float f = h * 6.0f - i;
float p = 255 * (1-s); int p = int(255.0f * (1.0f-s));
float q = 255 * (1-f*s); int q = int(255.0f * (1.0f-f*s));
float t = 255 * (1-(1-f)*s); int t = int(255.0f * (1.0f-(1.0f-f)*s));
p = constrain(p, 0, 255);
q = constrain(q, 0, 255);
t = constrain(t, 0, 255);
switch (i%6) { switch (i%6) {
case 0: rgb[0]=255,rgb[1]=t, rgb[2]=p; break; case 0: rgb[0]=255,rgb[1]=t, rgb[2]=p; break;
case 1: rgb[0]=q, rgb[1]=255,rgb[2]=p; break; case 1: rgb[0]=q, rgb[1]=255,rgb[2]=p; break;
case 2: rgb[0]=p, rgb[1]=255,rgb[2]=t; break; case 2: rgb[0]=p, rgb[1]=255,rgb[2]=t; break;
case 3: rgb[0]=p, rgb[1]=q, rgb[2]=255;break; case 3: rgb[0]=p, rgb[1]=q, rgb[2]=255;break;
case 4: rgb[0]=t, rgb[1]=p, rgb[2]=255;break; case 4: rgb[0]=t, rgb[1]=p, rgb[2]=255;break;
case 5: rgb[0]=255,rgb[1]=p,rgb[2]=q; case 5: rgb[0]=255,rgb[1]=p, rgb[2]=q; break;
} }
} }
//get RGB values from color temperature in K (https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html) //get RGB values from color temperature in K (https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html)
void colorKtoRGB(uint16_t kelvin, byte* rgb) //white spectrum to rgb, calc void colorKtoRGB(uint16_t kelvin, byte* rgb) //white spectrum to rgb, calc
{ {
float r = 0, g = 0, b = 0; int r = 0, g = 0, b = 0;
float temp = kelvin / 100; float temp = kelvin / 100.0f;
if (temp <= 66) { if (temp <= 66.0f) {
r = 255; r = 255;
g = round(99.4708025861 * log(temp) - 161.1195681661); g = roundf(99.4708025861f * logf(temp) - 161.1195681661f);
if (temp <= 19) { if (temp <= 19.0f) {
b = 0; b = 0;
} else { } else {
b = round(138.5177312231 * log((temp - 10)) - 305.0447927307); b = roundf(138.5177312231f * logf((temp - 10.0f)) - 305.0447927307f);
} }
} else { } else {
r = round(329.698727446 * pow((temp - 60), -0.1332047592)); r = roundf(329.698727446f * powf((temp - 60.0f), -0.1332047592f));
g = round(288.1221695283 * pow((temp - 60), -0.0755148492)); g = roundf(288.1221695283f * powf((temp - 60.0f), -0.0755148492f));
b = 255; b = 255;
} }
//g += 12; //mod by Aircoookie, a bit less accurate but visibly less pinkish //g += 12; //mod by Aircoookie, a bit less accurate but visibly less pinkish
@ -147,9 +150,9 @@ void colorXYtoRGB(float x, float y, byte* rgb) //coordinates to rgb (https://www
b = 1.0f; b = 1.0f;
} }
// Apply gamma correction // Apply gamma correction
r = r <= 0.0031308f ? 12.92f * r : (1.0f + 0.055f) * pow(r, (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) * pow(g, (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) * pow(b, (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) { if (r > b && r > g) {
// red is biggest // red is biggest
@ -173,9 +176,9 @@ void colorXYtoRGB(float x, float y, byte* rgb) //coordinates to rgb (https://www
b = 1.0f; b = 1.0f;
} }
} }
rgb[0] = 255.0*r; rgb[0] = byte(255.0f*r);
rgb[1] = 255.0*g; rgb[1] = byte(255.0f*g);
rgb[2] = 255.0*b; rgb[2] = byte(255.0f*b);
} }
void colorRGBtoXY(byte* rgb, float* xy) //rgb to coordinates (https://www.developers.meethue.com/documentation/color-conversions-rgb-xy) void colorRGBtoXY(byte* rgb, float* xy) //rgb to coordinates (https://www.developers.meethue.com/documentation/color-conversions-rgb-xy)
@ -242,35 +245,13 @@ float maxf (float v, float w)
return v; return v;
} }
/*
uint32_t colorRGBtoRGBW(uint32_t c)
{
byte rgb[4];
rgb[0] = R(c);
rgb[1] = G(c);
rgb[2] = B(c);
rgb[3] = W(c);
colorRGBtoRGBW(rgb);
return RGBW32(rgb[0], rgb[1], rgb[2], rgb[3]);
}
void colorRGBtoRGBW(byte* rgb) //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_MODE_LEGACY)
{
float low = minf(rgb[0],minf(rgb[1],rgb[2]));
float high = maxf(rgb[0],maxf(rgb[1],rgb[2]));
if (high < 0.1f) return;
float sat = 100.0f * ((high - low) / high); // maximum saturation is 100 (corrected from 255)
rgb[3] = (byte)((255.0f - sat) / 255.0f * (rgb[0] + rgb[1] + rgb[2]) / 3);
}
*/
byte correctionRGB[4] = {0,0,0,0};
uint16_t lastKelvin = 0;
// adjust RGB values based on color temperature in K (range [2800-10200]) (https://en.wikipedia.org/wiki/Color_balance) // adjust RGB values based on color temperature in K (range [2800-10200]) (https://en.wikipedia.org/wiki/Color_balance)
// called from bus manager when color correction is enabled!
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb) uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb)
{ {
//remember so that slow colorKtoRGB() doesn't have to run for every setPixelColor() //remember so that slow colorKtoRGB() doesn't have to run for every setPixelColor()
static byte correctionRGB[4] = {0,0,0,0};
static uint16_t lastKelvin = 0;
if (lastKelvin != kelvin) colorKtoRGB(kelvin, correctionRGB); // convert Kelvin to RGB if (lastKelvin != kelvin) colorKtoRGB(kelvin, correctionRGB); // convert Kelvin to RGB
lastKelvin = kelvin; lastKelvin = kelvin;
byte rgbw[4]; byte rgbw[4];
@ -321,7 +302,7 @@ uint16_t approximateKelvinFromRGB(uint32_t rgb) {
} }
//gamma 2.8 lookup table used for color correction //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, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
@ -339,27 +320,22 @@ static byte gammaT[] = {
177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213, 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 }; 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 // re-calculates & fills gamma table
void calcGammaTable(float gamma) void NeoGammaWLEDMethod::calcGammaTable(float gamma)
{ {
for (uint16_t i = 0; i < 256; i++) { for (size_t i = 0; i < 256; i++) {
gammaT[i] = gamma8_cal(i, gamma); gammaT[i] = (int)(powf((float)i / 255.0f, gamma) * 255.0f + 0.5f);
} }
} }
// used for individual channel or brightness gamma correction uint8_t NeoGammaWLEDMethod::Correct(uint8_t value)
uint8_t gamma8(uint8_t b)
{ {
return gammaT[b]; if (!gammaCorrectCol) return value;
return gammaT[value];
} }
// used for color gamma correction // used for color gamma correction
uint32_t gamma32(uint32_t color) uint32_t NeoGammaWLEDMethod::Correct32(uint32_t color)
{ {
if (!gammaCorrectCol) return color; if (!gammaCorrectCol) return color;
uint8_t w = W(color); uint8_t w = W(color);

View File

@ -12,6 +12,7 @@
#define DEFAULT_AP_SSID "WLED-AP" #define DEFAULT_AP_SSID "WLED-AP"
#define DEFAULT_AP_PASS "wled1234" #define DEFAULT_AP_PASS "wled1234"
#define DEFAULT_OTA_PASS "wledota" #define DEFAULT_OTA_PASS "wledota"
#define DEFAULT_MDNS_NAME "x"
//increase if you need more //increase if you need more
#ifndef WLED_MAX_USERMODS #ifndef WLED_MAX_USERMODS
@ -79,6 +80,32 @@
#define WLED_MAX_COLOR_ORDER_MAPPINGS 10 #define WLED_MAX_COLOR_ORDER_MAPPINGS 10
#endif #endif
#if defined(WLED_MAX_LEDMAPS) && (WLED_MAX_LEDMAPS > 32 || WLED_MAX_LEDMAPS < 10)
#undef WLED_MAX_LEDMAPS
#endif
#ifndef WLED_MAX_LEDMAPS
#ifdef ESP8266
#define WLED_MAX_LEDMAPS 10
#else
#define WLED_MAX_LEDMAPS 16
#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 //Usermod IDs
#define USERMOD_ID_RESERVED 0 //Unused. Might indicate no usermod present #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 #define USERMOD_ID_UNSPECIFIED 1 //Default value for a general user mod that does not specify a custom ID
@ -120,6 +147,7 @@
#define USERMOD_ID_SD_CARD 37 //Usermod "usermod_sd_card.h" #define USERMOD_ID_SD_CARD 37 //Usermod "usermod_sd_card.h"
#define USERMOD_ID_PWM_OUTPUTS 38 //Usermod "usermod_pwm_outputs.h #define USERMOD_ID_PWM_OUTPUTS 38 //Usermod "usermod_pwm_outputs.h
#define USERMOD_ID_SHT 39 //Usermod "usermod_sht.h #define USERMOD_ID_SHT 39 //Usermod "usermod_sht.h
#define USERMOD_ID_KLIPPER 40 // Usermod Klipper percentage
//Access point behavior //Access point behavior
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot #define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
@ -137,7 +165,7 @@
#define CALL_MODE_FX_CHANGED 6 //no longer used #define CALL_MODE_FX_CHANGED 6 //no longer used
#define CALL_MODE_HUE 7 #define CALL_MODE_HUE 7
#define CALL_MODE_PRESET_CYCLE 8 #define CALL_MODE_PRESET_CYCLE 8
#define CALL_MODE_BLYNK 9 #define CALL_MODE_BLYNK 9 //no longer used
#define CALL_MODE_ALEXA 10 #define CALL_MODE_ALEXA 10
#define CALL_MODE_WS_SEND 11 //special call mode, not for notifier, updates websocket only #define CALL_MODE_WS_SEND 11 //special call mode, not for notifier, updates websocket only
#define CALL_MODE_BUTTON_PRESET 12 //button/IR JSON preset/macro #define CALL_MODE_BUTTON_PRESET 12 //button/IR JSON preset/macro
@ -147,7 +175,9 @@
#define RGBW_MODE_AUTO_BRIGHTER 1 // New algorithm. Adds as much white as the darkest RGBW channel #define RGBW_MODE_AUTO_BRIGHTER 1 // New algorithm. Adds as much white as the darkest RGBW channel
#define RGBW_MODE_AUTO_ACCURATE 2 // New algorithm. Adds as much white as the darkest RGBW channel and subtracts this amount from each RGB channel #define RGBW_MODE_AUTO_ACCURATE 2 // New algorithm. Adds as much white as the darkest RGBW channel and subtracts this amount from each RGB channel
#define RGBW_MODE_DUAL 3 // Manual slider + auto calculation. Automatically calculates only if manual slider is set to off (0) #define RGBW_MODE_DUAL 3 // Manual slider + auto calculation. Automatically calculates only if manual slider is set to off (0)
#define RGBW_MODE_LEGACY 4 //Old floating algorithm. Too slow for realtime and palette support #define RGBW_MODE_MAX 4 // Sets white to the value of the brightest RGB channel (good for white-only LEDs without any RGB)
//#define RGBW_MODE_LEGACY 4 // Old floating algorithm. Too slow for realtime and palette support (unused)
#define AW_GLOBAL_DISABLED 255 // Global auto white mode override disabled. Per-bus setting is used
//realtime modes //realtime modes
#define REALTIME_MODE_INACTIVE 0 #define REALTIME_MODE_INACTIVE 0
@ -193,12 +223,16 @@
#define TYPE_NONE 0 //light is not configured #define TYPE_NONE 0 //light is not configured
#define TYPE_RESERVED 1 //unused. Might indicate a "virtual" light #define TYPE_RESERVED 1 //unused. Might indicate a "virtual" light
//Digital types (data pin only) (16-31) //Digital types (data pin only) (16-31)
#define TYPE_WS2812_1CH 20 //white-only chips #define TYPE_WS2812_1CH 18 //white-only chips (1 channel per IC) (unused)
#define TYPE_WS2812_1CH_X3 19 //white-only chips (3 channels per IC)
#define TYPE_WS2812_2CH_X3 20 //CCT chips (1st IC controls WW + CW of 1st zone and CW of 2nd zone, 2nd IC controls WW of 2nd zone and WW + CW of 3rd zone)
#define TYPE_WS2812_WWA 21 //amber + warm + cold white #define TYPE_WS2812_WWA 21 //amber + warm + cold white
#define TYPE_WS2812_RGB 22 #define TYPE_WS2812_RGB 22
#define TYPE_GS8608 23 //same driver as WS2812, but will require signal 2x per second (else displays test pattern) #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_WS2811_400KHZ 24 //half-speed WS2812 protocol, used by very old WS2811 units
#define TYPE_TM1829 25 #define TYPE_TM1829 25
#define TYPE_UCS8903 26
#define TYPE_UCS8904 29
#define TYPE_SK6812_RGBW 30 #define TYPE_SK6812_RGBW 30
#define TYPE_TM1814 31 #define TYPE_TM1814 31
//"Analog" types (PWM) (32-47) //"Analog" types (PWM) (32-47)
@ -247,7 +281,7 @@
#define BTN_TYPE_ANALOG_INVERTED 8 #define BTN_TYPE_ANALOG_INVERTED 8
//Ethernet board types //Ethernet board types
#define WLED_NUM_ETH_TYPES 9 #define WLED_NUM_ETH_TYPES 11
#define WLED_ETH_NONE 0 #define WLED_ETH_NONE 0
#define WLED_ETH_WT32_ETH01 1 #define WLED_ETH_WT32_ETH01 1
@ -256,6 +290,10 @@
#define WLED_ETH_QUINLED 4 #define WLED_ETH_QUINLED 4
#define WLED_ETH_TWILIGHTLORD 5 #define WLED_ETH_TWILIGHTLORD 5
#define WLED_ETH_ESP32DEUX 6 #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 //Hue error codes
#define HUE_ERROR_INACTIVE 0 #define HUE_ERROR_INACTIVE 0
@ -290,9 +328,15 @@
//Playlist option byte //Playlist option byte
#define PL_OPTION_SHUFFLE 0x01 #define PL_OPTION_SHUFFLE 0x01
// Segment capability byte
#define SEG_CAPABILITY_RGB 0x01
#define SEG_CAPABILITY_W 0x02
#define SEG_CAPABILITY_CCT 0x04
// WLED Error modes // WLED Error modes
#define ERR_NONE 0 // All good :) #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_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_JSON 9 // JSON parsing failed (input too large?)
#define ERR_FS_BEGIN 10 // Could not init filesystem (no partition?) #define ERR_FS_BEGIN 10 // Could not init filesystem (no partition?)
@ -310,6 +354,23 @@
#define NL_MODE_COLORFADE 2 //Fade to target brightness and secondary color gradually #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. #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 #define NTP_PACKET_SIZE 48
@ -342,7 +403,7 @@
#ifdef ESP8266 #ifdef ESP8266
#define SETTINGS_STACK_BUF_SIZE 2048 #define SETTINGS_STACK_BUF_SIZE 2048
#else #else
#define SETTINGS_STACK_BUF_SIZE 3096 #define SETTINGS_STACK_BUF_SIZE 3608 // warning: quite a large value for stack
#endif #endif
#ifdef WLED_USE_ETHERNET #ifdef WLED_USE_ETHERNET
@ -383,8 +444,8 @@
#define JSON_BUFFER_SIZE 24576 #define JSON_BUFFER_SIZE 24576
#endif #endif
//#define MIN_HEAP_SIZE (MAX_LED_MEMORY+2048) //#define MIN_HEAP_SIZE (8k for AsyncWebServer)
#define MIN_HEAP_SIZE (8192) #define MIN_HEAP_SIZE 8192
// Maximum size of node map (list of other WLED instances) // Maximum size of node map (list of other WLED instances)
#ifdef ESP8266 #ifdef ESP8266
@ -414,7 +475,10 @@
#define DEFAULT_LED_COUNT 30 #define DEFAULT_LED_COUNT 30
#endif #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 // 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) // 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"> <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> <h1>404 Not Found</h1>
<b>Akemi does not know where you are headed...</b><br><br> <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> </body>
</html> </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

@ -151,7 +151,7 @@ button {
} }
.segt TD { .segt TD {
padding: 2px !important; padding: 2px 0 !important;
text-align: center; text-align: center;
/*text-transform: uppercase;*/ /*text-transform: uppercase;*/
} }
@ -174,34 +174,62 @@ button {
} }
.slider-icon { .slider-icon {
transform: translate(3px,3px); position: absolute;
} left: 8px;
bottom: 5px;
.e-icon {
transform: translateY(3px);
} }
.sel-icon { .sel-icon {
transform: translateX(3px); transform: translateX(3px);
} }
.e-icon, .sel-icon, .slider-icon { .e-icon, .g-icon, .sel-icon, .slider-icon {
cursor: pointer; cursor: pointer;
color: var(--c-d); 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 { .search-icon {
position: absolute; position: absolute;
top: 8px; top: 8px;
left: 12px; left: 12px;
/*pointer-events: none;*/
width: 24px; width: 24px;
height: 24px; height: 24px;
} }
.clear-icon { .clear-icon {
position: absolute; position: absolute;
display: none;
top: 8px; top: 8px;
right: 9px; right: 9px;
cursor: pointer; cursor: pointer;
@ -229,14 +257,12 @@ button {
#liveview { #liveview {
height: 4px; height: 4px;
display: none;
width: 100%; width: 100%;
border: 0px; border: 0px;
} }
#liveview2D { #liveview2D {
height: 90%; height: 90%;
display: none;
width: 90%; width: 90%;
border: 0px; border: 0px;
position: absolute; position: absolute;
@ -314,14 +340,14 @@ button {
overflow: auto; overflow: auto;
height: 100%; height: 100%;
overscroll-behavior: none; overscroll-behavior: none;
} padding: 0 4px;
#Effects {
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
} }
#segutil, #segutil2, #segcont, #putil, #pcont, #pql { #segutil, #segutil2, #segcont, #putil, #pcont, #pql, #fx, #palw,
width: 280px; .fnd {
max-width: 280px;
font-size: 19px;
} }
#putil, #segutil, #segutil2 { #putil, #segutil, #segutil2 {
@ -333,7 +359,8 @@ button {
padding-top: 12px; 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; margin: 0 auto;
} }
@ -380,10 +407,10 @@ button {
} }
#sliders { #sliders {
width: 300px; position: -webkit-sticky;
margin: 0 auto;
position: sticky; position: sticky;
bottom: 0; bottom: 0;
max-width: 300px;
} }
#sliders .labels { #sliders .labels {
@ -392,29 +419,49 @@ button {
} }
.slider { .slider {
background-color: var(--c-2); /*max-width: 300px;*/
max-width: 300px; /* margin: 5px auto; add 5px; if you want some vertical space but looks ugly */
min-width: 280px;
margin: 0 auto; /* add 5px; if you want some vertical space but looks ugly */
border-radius: 24px; border-radius: 24px;
position: relative; 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 { .filter, .option {
background-color: var(--c-4); background-color: var(--c-4);
border-radius: 26px; border-radius: 26px;
height: 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; padding: 4px 2px;
position: relative; position: relative;
z-index: 1;
opacity: 1; opacity: 1;
transition: opacity 0.5s linear, height 0.5s, transform 0.5s; transition: opacity 0.5s linear, height 0.5s, transform 0.5s;
transform: scaleY(1);
} }
.option { .filter {
z-index: unset; z-index: 1;
overflow: hidden;
} }
/* Tooltip text */ /* Tooltip text */
@ -425,14 +472,14 @@ button {
box-shadow: 4px 4px 10px 4px var(--c-1); box-shadow: 4px 4px 10px 4px var(--c-1);
color: var(--c-f); color: var(--c-f);
text-align: center; text-align: center;
padding: 5px 10px; padding: 4px 8px;
border-radius: 6px; border-radius: 6px;
/* Position the tooltip text */ /* Position the tooltip text */
width: 160px; width: 160px;
position: absolute; position: absolute;
z-index: 1; z-index: 1;
bottom: 100%; bottom: 80%;
left: 50%; left: 50%;
margin-left: -92px; margin-left: -92px;
@ -463,13 +510,6 @@ button {
opacity: 1; opacity: 1;
} }
#pql, .edit-icon {
display: none;
}
.hide {
display: none !important;
}
.fade { .fade {
visibility: hidden; /* hide it */ visibility: hidden; /* hide it */
opacity: 0; /* make it transparent */ opacity: 0; /* make it transparent */
@ -526,6 +566,7 @@ button {
} }
.close { .close {
position: -webkit-sticky;
position: sticky; position: sticky;
top: 0; top: 0;
float: right; float: right;
@ -547,7 +588,6 @@ button {
position: fixed; position: fixed;
top: calc(var(--th) + 5px); top: calc(var(--th) + 5px);
left: 1px; left: 1px;
display: none;
cursor: pointer; cursor: pointer;
} }
@ -592,14 +632,9 @@ button {
} }
#info #imgw { #info #imgw {
/*display: inline-block;*/
margin: 8px auto; margin: 8px auto;
} }
/*
#kv, #kn {
display: inline-block;
}
*/
#lv { #lv {
max-width: 600px; max-width: 600px;
display: inline-block; display: inline-block;
@ -647,23 +682,27 @@ img {
#wbal .sliderdisplay { background: linear-gradient(90deg, #ff8f1f 0%, #fff 50%, #cbdbff); } #wbal .sliderdisplay { background: linear-gradient(90deg, #ff8f1f 0%, #fff 50%, #cbdbff); }
/* wrapper divs hidden by default */ /* wrapper divs hidden by default */
#rgbwrap, #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; display: none;
} }
.sliderbubble { .sliderbubble {
width: 24px; width: 24px;
position: relative; position: absolute;
display: inline-block; display: inline-block;
border-radius: 10px; border-radius: 16px;
background: var(--c-3); background: var(--c-3);
color: var(--c-f); color: var(--c-f);
padding: 2px 4px; padding: 4px;
font-size: 14px; font-size: 14px;
right: 3px; right: 6px;
transition: visibility 0.25s ease, opacity 0.25s ease; transition: visibility .25s ease,opacity .25s ease;
opacity: 0; opacity: 0;
visibility: hidden; visibility: hidden;
/* left: 8px; */
top: 4px;
} }
output.sliderbubbleshow { output.sliderbubbleshow {
@ -721,17 +760,8 @@ input[type=range]::-moz-range-thumb {
border: 2px solid var(--c-1); border: 2px solid var(--c-1);
} }
/* Slider wrapper div */
.sliderwrap {
height: 30px;
width: 230px;
position: relative;
z-index: 0;
}
#Colors .sliderwrap { #Colors .sliderwrap {
width: 260px; margin: 4px 0 0;
margin: 10px 0 0;
} }
/* Dynamically hide brightness slider label */ /* Dynamically hide brightness slider label */
@ -740,17 +770,14 @@ input[type=range]::-moz-range-thumb {
} }
#briwrap { #briwrap {
min-width: 267px;
float: right; float: right;
margin-top: var(--bmt); margin-top: var(--bmt);
} }
#picker, #rgbwrap, #kwrap, #wwrap, #wbal, #vwrap, #qcs-w, #hexw, #pall, #ledmap {
margin: 0 auto;
width: 260px;
}
#picker { #picker {
margin-top: 10px; margin-top: 8px !important;
max-width: 260px;
} }
/* buttons */ /* buttons */
@ -767,8 +794,8 @@ input[type=range]::-moz-range-thumb {
-webkit-transform:translate3d(0,0,0); -webkit-transform:translate3d(0,0,0);
backface-visibility: hidden; backface-visibility: hidden;
transform:translate3d(0,0,0); transform:translate3d(0,0,0);
overflow: clip; overflow: hidden;
text-overflow: clip; text-overflow: ellipsis;
border: 1px solid var(--c-3); border: 1px solid var(--c-3);
background-color: var(--c-3); background-color: var(--c-3);
} }
@ -799,6 +826,7 @@ input[type=range]::-moz-range-thumb {
.btn-xs, .btn-pl-del, .btn-pl-add { .btn-xs, .btn-pl-del, .btn-pl-add {
width: 42px !important; width: 42px !important;
height: 42px !important; height: 42px !important;
text-overflow: clip;
} }
.btn-xs { .btn-xs {
margin: 2px 0 0 0; margin: 2px 0 0 0;
@ -976,7 +1004,6 @@ textarea {
.ptxt { .ptxt {
margin: -1px 4px 8px !important; margin: -1px 4px 8px !important;
display: none;
} }
.stxt { .stxt {
@ -985,14 +1012,12 @@ textarea {
.segname, .pname { .segname, .pname {
white-space: nowrap; white-space: nowrap;
/*cursor: pointer;*/
text-align: center; text-align: center;
overflow: clip; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
line-height: 24px; line-height: 24px;
padding: 8px 24px; padding: 8px 24px;
max-width: 170px; max-width: 170px;
margin: 0 auto;
position: relative; position: relative;
} }
@ -1003,8 +1028,9 @@ textarea {
/* segment power wrapper */ /* segment power wrapper */
.sbs { .sbs {
padding: 4px 0 4px 8px; /*padding: 1px 0 1px 20px;*/
display: var(--sgp); display: var(--sgp);
width: 100%;
} }
.pname { .pname {
@ -1046,11 +1072,9 @@ textarea {
#csl .xxs { #csl .xxs {
border: 2px solid var(--c-d) !important; border: 2px solid var(--c-d) !important;
/*box-shadow: 0 0 0 2px var(--c-d);*/
} }
#csl .xxs-w { #csl .xxs-w {
border-width: 5px !important; 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 */ .qcs, #namelabel { /* text shadow for name to be legible on grey backround */
@ -1064,7 +1088,6 @@ textarea {
.pwr { .pwr {
color: var(--c-6); color: var(--c-6);
transform: translate(1px, 1px);
cursor: pointer; cursor: pointer;
} }
@ -1079,18 +1102,13 @@ textarea {
} }
.frz { .frz {
left: 32px; left: 10px;
position: absolute; position: absolute;
top: -3px; top: 8px;
cursor: pointer; cursor: pointer;
padding: 8px;
z-index: 1; z-index: 1;
} }
.expanded .frz {
display: none;
}
/* radiobuttons and checkmarks */ /* radiobuttons and checkmarks */
.check, .radio { .check, .radio {
display: block; display: block;
@ -1189,7 +1207,7 @@ TD .checkmark, TD .radiomark {
} }
.bp { .bp {
margin-bottom: 5px; margin-bottom: 8px;
} }
/* segment & preset wrapper */ /* segment & preset wrapper */
@ -1199,10 +1217,7 @@ TD .checkmark, TD .radiomark {
border: 0px solid var(--c-f); border: 0px solid var(--c-f);
text-align: left; text-align: left;
transition: background-color 0.5s; transition: background-color 0.5s;
/*filter: brightness(1);*/
font-size: 19px;
border-radius: 21px; border-radius: 21px;
/*min-width: 264px;*/
} }
.seg { .seg {
@ -1235,7 +1250,6 @@ TD .checkmark, TD .radiomark {
.lbl-s { .lbl-s {
display: inline-block; display: inline-block;
/* margin: 10px 4px 0 0; */
margin-top: 6px; margin-top: 6px;
font-size: 13px; font-size: 13px;
width: 48%; width: 48%;
@ -1245,10 +1259,8 @@ TD .checkmark, TD .radiomark {
/* list wrapper */ /* list wrapper */
.list { .list {
position: relative; position: relative;
width: 280px;
transition: background-color 0.5s; transition: background-color 0.5s;
margin: auto auto 20px; margin: auto auto 10px;
font-size: 19px;
line-height: 24px; line-height: 24px;
} }
@ -1258,6 +1270,7 @@ TD .checkmark, TD .radiomark {
cursor: pointer; cursor: pointer;
background-color: var(--c-2); background-color: var(--c-2);
overflow: hidden; overflow: hidden;
position: -webkit-sticky;
position: sticky; position: sticky;
border-radius: 21px; border-radius: 21px;
margin: 13px auto 0; margin: 13px auto 0;
@ -1286,14 +1299,11 @@ TD .checkmark, TD .radiomark {
background-color: var(--c-3); background-color: var(--c-3);
} }
/*.selected .radiomark {
border: 1px solid var(--c-3);
}*/
/* selected list item */ /* selected list item */
.lstI.selected { .lstI.selected {
top: 0; top: 0;
bottom: 0; bottom: 0;
border: 1px solid var(--c-4);
} }
.lstI.sticky, .lstI.sticky,
@ -1349,8 +1359,6 @@ TD .checkmark, TD .radiomark {
/* find/search element */ /* find/search element */
.fnd { .fnd {
width: 280px;
margin: 0 auto;
position: relative; position: relative;
} }
@ -1382,7 +1390,6 @@ TD .checkmark, TD .radiomark {
.presin { .presin {
padding: 8px; padding: 8px;
position: relative; position: relative;
width: 264px;
} }
.btn-s, .btn-s,
@ -1409,6 +1416,9 @@ TD .checkmark, TD .radiomark {
.expanded { .expanded {
display: inline-block !important; display: inline-block !important;
} }
.hide, .expanded .segin.hide, .expanded .presin.hide, .expanded .sbs.hide, .expanded .frz, .expanded .g-icon {
display: none !important;
}
.m6 { .m6 {
margin: 6px 0; margin: 6px 0;
@ -1457,6 +1467,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) { @media all and (max-width: 335px) {
.sliderbubble { .sliderbubble {
display: none; display: none;
@ -1475,38 +1495,83 @@ TD .checkmark, TD .radiomark {
} }
} }
@media all and (max-width: 540px) { @media all and (max-width: 420px) {
.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) {
#buttonNodes { #buttonNodes {
display: none; display: none;
} }
} }
@media all and (max-width: 1249px) { @media all and (max-width: 639px) {
#buttonPcm { .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; 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="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="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 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> <button id="buttonPcm" onclick="togglePcMode(true)"><i class="icons">&#xe23d;</i><p class="tab-label">PC Mode</p></button>
</div> </div>
<div id="briwrap"> <div id="briwrap">
<p class="hd">Brightness</p> <p class="hd">Brightness</p>
<div class="il"> <div class="slider" style="padding-right:32px;">
<i class="icons slider-icon" onclick="tglTheme()">&#xe2a6;</i> <i class="icons slider-icon" onclick="tglTheme()" style="transform: translate(-32px,5px);">&#xe2a6;</i>
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderBri" onchange="setBri()" oninput="updateTrail(this)" max="255" min="1" type="range" value="128" /> <input id="sliderBri" onchange="setBri()" oninput="updateTrail(this)" max="255" min="1" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
@ -88,68 +88,73 @@
<div class ="container"> <div class ="container">
<div id="Colors" class="tabcontent"> <div id="Colors" class="tabcontent">
<div id="picker" class="noslide"></div> <div id="picker" class="noslide"></div>
<div id="hwrap"> <div id="hwrap" class="slider" style="margin-top: 20px;">
<!--p class="labels hd">Hue</p-->
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderH" class="noslide" oninput="fromH()" onchange="setColor(0)" max="359" min="0" type="range" value="0" step="any"> <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> <div class="sliderdisplay" style="background: linear-gradient(90deg, #f00 2%, #ff0 19%, #0f0 35%, #0ff 52%, #00f 68%, #f0f 85%, #f00)"></div>
</div><br>
</div> </div>
<div id="swrap"> <span class="tooltiptext">Hue</span>
<!--p class="labels hd">Saturation</p--> </div>
<div id="swrap" class="slider">
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderS" class="noslide" oninput="fromS()" onchange="setColor(0)" max="100" min="0" type="range" value="100" step="any"> <input id="sliderS" class="noslide" oninput="fromS()" onchange="setColor(0)" max="100" min="0" type="range" value="100" step="any">
<div class="sliderdisplay" style="background: linear-gradient(90deg, #aaa 0%, #f00)"></div> <div class="sliderdisplay" style="background: linear-gradient(90deg, #aaa 0%, #f00)"></div>
</div><br>
</div> </div>
<div id="vwrap"> <span class="tooltiptext">Saturation</span>
<!--p class="labels hd">Value</p--> </div>
<div id="vwrap" class="slider">
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderV" class="noslide" oninput="fromV()" onchange="setColor(0)" max="100" min="0" type="range" value="100" step="any" /> <input id="sliderV" class="noslide" oninput="fromV()" onchange="setColor(0)" max="100" min="0" type="range" value="100" step="any" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div><br>
</div> </div>
<div id="kwrap"> <span class="tooltiptext">Value/Brightness</span>
<!--p class="labels hd">Temperature</p--> </div>
<div id="kwrap" class="slider">
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderK" class="noslide" oninput="fromK()" onchange="setColor(0)" max="10091" min="1900" type="range" value="6550" /> <input id="sliderK" class="noslide" oninput="fromK()" onchange="setColor(0)" max="10091" min="1900" type="range" value="6550" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
<span class="tooltiptext">Kelvin/Temperature</span>
</div> </div>
<div id="rgbwrap"> <div id="rgbwrap">
<p class="labels hd">RGB color</p> <!--p class="labels hd">RGB color</p-->
<div id="rwrap" class="il"> <div id="rwrap" class="slider">
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderR" class="noslide" oninput="fromRgb()" onchange="setColor(0)" max="255" min="0" type="range" value="128" /> <input id="sliderR" class="noslide" oninput="fromRgb()" onchange="setColor(0)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
<span class="tooltiptext">Red channel</span>
</div> </div>
<div id="gwrap" class="il"> <div id="gwrap" class="slider">
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderG" class="noslide" oninput="fromRgb()" onchange="setColor(0)" max="255" min="0" type="range" value="128" /> <input id="sliderG" class="noslide" oninput="fromRgb()" onchange="setColor(0)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
<span class="tooltiptext">Green channel</span>
</div> </div>
<div id="bwrap" class="il"> <div id="bwrap" class="slider">
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderB" class="noslide" oninput="fromRgb()" onchange="setColor(0)" max="255" min="0" type="range" value="128" /> <input id="sliderB" class="noslide" oninput="fromRgb()" onchange="setColor(0)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
<span class="tooltiptext">Blue channel</span>
</div> </div>
</div> </div>
<div id="wwrap"> <div id="wwrap" class="slider">
<p class="labels hd">White channel</p> <!--p class="labels hd">White channel</p-->
<div id="whibri" class="sliderwrap il"> <div id="whibri" class="sliderwrap il">
<input id="sliderW" class="noslide" oninput="fromW()" onchange="setColor(0)" max="255" min="0" type="range" value="128" /> <input id="sliderW" class="noslide" oninput="fromW()" onchange="setColor(0)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
<span class="tooltiptext">White channel</span>
</div> </div>
<div id="wbal"> <div id="wbal" class="slider">
<p class="labels hd">White balance</p> <!--p class="labels hd">White balance</p-->
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderA" class="noslide" onchange="setBalance(this.value)" max="255" min="0" type="range" value="128" /> <input id="sliderA" class="noslide" onchange="setBalance(this.value)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
<span class="tooltiptext">White balance</span>
</div> </div>
<div id="qcs-w"> <div id="qcs-w">
<div class="qcs" onclick="pC('#ff0000');" title="Red" style="background-color:#ff0000;"></div> <div class="qcs" onclick="pC('#ff0000');" title="Red" style="background-color:#ff0000;"></div>
@ -193,6 +198,10 @@
</label> </label>
</div> </div>
</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>
</div> </div>
@ -222,6 +231,10 @@
<input type="checkbox" data-flt="&#x1F3A8;" onchange="filterFx(this)"> <input type="checkbox" data-flt="&#x1F3A8;" onchange="filterFx(this)">
<span class="checkmark"></span> <span class="checkmark"></span>
</label> </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; <label id="filter1D" class="check fchkl">&#8942;
<input type="checkbox" data-flt="&#8942;" onchange="filterFx(this)"> <input type="checkbox" data-flt="&#8942;" onchange="filterFx(this)">
<span class="checkmark"></span> <span class="checkmark"></span>
@ -240,7 +253,7 @@
</label> </label>
</div> </div>
<div id="slider0" class="slider"> <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"> <div class="sliderwrap il">
<input id="sliderSpeed" class="noslide" onchange="setSpeed()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" /> <input id="sliderSpeed" class="noslide" onchange="setSpeed()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
@ -249,7 +262,7 @@
<span id="sliderLabel0" class="tooltiptext">Effect speed</span> <span id="sliderLabel0" class="tooltiptext">Effect speed</span>
</div> </div>
<div id="slider1" class="slider"> <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"> <div class="sliderwrap il">
<input id="sliderIntensity" class="noslide" onchange="setIntensity()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" /> <input id="sliderIntensity" class="noslide" onchange="setIntensity()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
@ -310,7 +323,7 @@
<div id="segutil2"> <div id="segutil2">
<button class="btn btn-s" id="rsbtn" onclick="rSegs()">Reset segments</button> <button class="btn btn-s" id="rsbtn" onclick="rSegs()">Reset segments</button>
</div> </div>
<p>Transition: <input id="tt" class="noslide" type="number" min="0" max="65.5" step="0.1" value="0.7">&nbsp;s</p> <p>Transition: <input id="tt" type="number" min="0" max="65.5" step="0.1" value="0.7">&nbsp;s</p>
<p id="ledmap" class="hide"></p> <p id="ledmap" class="hide"></p>
</div> </div>
@ -350,7 +363,7 @@
<div> <div>
<button class="btn infobtn" onclick="requestJson()">Refresh</button> <button class="btn infobtn" onclick="requestJson()">Refresh</button>
<button class="btn infobtn" onclick="toggleNodes()">Instance List</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> <button class="btn infobtn" id="resetbtn" onclick="cnfReset()">Reboot WLED</button>
</div> </div>
<br> <br>
@ -366,8 +379,8 @@
</div> </div>
</div> </div>
<div id="mliveview2D" class="modal"> <div id="mlv2D" class="modal">
<div id="kliveview2D" style="width:100%; height:100%">Loading...</div><br> <div id="klv2D" style="width:100%; height:100%">Loading...</div>
</div> </div>
<div id="rover" class="modal"> <div id="rover" class="modal">

File diff suppressed because it is too large Load Diff

View File

@ -17,21 +17,17 @@
position: absolute; position: absolute;
} }
</style> </style>
</head>
<body>
<div id="canv" />
<script> <script>
update(); var ws;
var tmout = null; var tmout = null;
function update() function update() // via HTTP (/json/live)
{ {
if (document.hidden) { if (document.hidden) {
clearTimeout(tmout); clearTimeout(tmout);
tmout = setTimeout(update, 250); tmout = setTimeout(update, 250);
return; return;
} }
fetch('/json/live') fetch('./json/live')
.then(res => { .then(res => {
if (!res.ok) { if (!res.ok) {
clearTimeout(tmout); clearTimeout(tmout);
@ -57,8 +53,57 @@
clearTimeout(tmout); clearTimeout(tmout);
tmout = setTimeout(update, 2500); 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> </script>
</head>
<body onload="S()">
<div id="canv"></div>
</body> </body>
</html> </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) { if (ws && ws.readyState === WebSocket.OPEN) {
ws.send("{'lv':true}"); ws.send("{'lv':true}");
} else { } 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.onopen = ()=>{
ws.send("{'lv':true}"); ws.send("{'lv':true}");
} }

View File

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

View File

@ -0,0 +1,62 @@
function drawBoxes(inputPixelArray, widthPixels, heightPixels) {
var w = window;
// Get the canvas context
var ctx = canvas.getContext('2d', { willReadFrequently: true });
// Set the width and height of the canvas
if (w.innerHeight < w.innerWidth) {
canvas.width = Math.floor(w.innerHeight * 0.98);
}
else{
canvas.width = Math.floor(w.innerWidth * 0.98);
}
//canvas.height = w.innerWidth;
let pixelSize = Math.floor(canvas.width/widthPixels);
let xOffset = (w.innerWidth - (widthPixels * pixelSize))/2
//Set the canvas height to fit the right number of pixelrows
canvas.height = (pixelSize * heightPixels) + 10
//Iterate through the matrix
for (let y = 0; y < heightPixels; y++) {
for (let x = 0; x < widthPixels; x++) {
// Calculate the index of the current pixel
let i = (y*widthPixels) + x;
//Gets the RGB of the current pixel
let pixel = inputPixelArray[i];
let pixelColor = 'rgb(' + pixel[0] + ', ' + pixel[1] + ', ' + pixel[2] + ')';
let textColor = 'rgb(128,128,128)';
// Set the fill style to the pixel color
ctx.fillStyle = pixelColor;
//Draw the rectangle
ctx.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
// Draw a border on the box
ctx.strokeStyle = '#888888';
ctx.lineWidth = 1;
ctx.strokeRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
//Write text to box
ctx.font = "10px Arial";
ctx.fillStyle = textColor;
ctx.textAlign = "center";
ctx.textBaseline = 'middle';
ctx.fillText((pixel[4] + 1), (x * pixelSize) + (pixelSize /2), (y * pixelSize) + (pixelSize /2));
}
}
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
canvas.width = w.innerWidth;
ctx.putImageData(imageData, xOffset, 0);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 649 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,320 @@
function getPixelRGBValues(base64Image) {
httpArray = [];
fileJSON = `{"on":true,"bri":${brgh.value},"seg":{"id":${tSg.value},"i":[`;
//Which object holds the secret to the segment ID
let segID = 0;
if(tSg.style.display == "flex"){
segID = tSg.value
} else {
segID = sID.value;
}
//const copyJSONledbutton = gId('copyJSONledbutton');
const maxNoOfColorsInCommandSting = parseInt(cLN.value);
let hybridAddressing = false;
let selectedIndex = -1;
selectedIndex = frm.selectedIndex;
const formatSelection = frm.options[selectedIndex].value;
selectedIndex = lSS.selectedIndex;
const ledSetupSelection = lSS.options[selectedIndex].value;
selectedIndex = cFS.selectedIndex;
let hexValueCheck = true;
if (cFS.options[selectedIndex].value == 'dec'){
hexValueCheck = false
}
selectedIndex = aS.selectedIndex;
let segmentValueCheck = true; //If Range or Hybrid
if (aS.options[selectedIndex].value == 'single'){
segmentValueCheck = false
} else if (aS.options[selectedIndex].value == 'hybrid'){
hybridAddressing = true;
}
let curlString = ''
let haString = ''
let colorSeparatorStart = '"';
let colorSeparatorEnd = '"';
if (!hexValueCheck){
colorSeparatorStart = '[';
colorSeparatorEnd = ']';
}
// Warnings
let hasTransparency = false; //If alpha < 255 is detected on any pixel, this is set to true in code below
let imageInfo = '';
// Create an off-screen canvas
var canvas = cE('canvas');
var context = canvas.getContext('2d', { willReadFrequently: true });
// Create an image element and set its src to the base64 image
var image = new Image();
image.src = base64Image;
// Wait for the image to load before drawing it onto the canvas
image.onload = function() {
let scalePath = scDiv.children[0].children[0];
let color = scalePath.getAttribute("fill");
let sizeX = szX.value;
let sizeY = szY.value;
if (color != accentColor || sizeX < 1 || sizeY < 1){
//image will not be rezised Set desitred size to original size
sizeX = image.width;
sizeY = image.height;
//failsafe for not generating huge images automatically
if (image.width > 512 || image.height > 512)
{
sizeX = 16;
sizeY = 16;
}
}
// Set the canvas size to the same as the desired image size
canvas.width = sizeX;
canvas.height = sizeY;
imageInfo = '<p>Width: ' + sizeX + ', Height: ' + sizeY + ' (make sure this matches your led matrix setup)</p>'
// Draw the image onto the canvas
context.drawImage(image, 0, 0, sizeX, sizeY);
// Get the pixel data from the canvas
var pixelData = context.getImageData(0, 0, sizeX, sizeY).data;
// Create an array to hold the RGB values of each pixel
var pixelRGBValues = [];
// If the first row of the led matrix is right -> left
let right2leftAdjust = 1;
if (ledSetupSelection == 'l2r'){
right2leftAdjust = 0;
}
// Loop through the pixel data and get the RGB values of each pixel
for (var i = 0; i < pixelData.length; i += 4) {
var r = pixelData[i];
var g = pixelData[i + 1];
var b = pixelData[i + 2];
var a = pixelData[i + 3];
let pixel = i/4
let row = Math.floor(pixel/sizeX);
let led = pixel;
if (ledSetupSelection == 'matrix'){
//Do nothing, the matrix is set upp like the index in the image
//Every row starts from the left, i.e. no zigzagging
}
else if ((row + right2leftAdjust) % 2 === 0) {
//Setup is traditional zigzag
//right2leftAdjust basically flips the row order if = 1
//Row is left to right
//Leave led index as pixel index
} else {
//Setup is traditional zigzag
//Row is right to left
//Invert index of row for led
let indexOnRow = led - (row * sizeX);
let maxIndexOnRow = sizeX - 1;
let reversedIndexOnRow = maxIndexOnRow - indexOnRow;
led = (row * sizeX) + reversedIndexOnRow;
}
// Add the RGB values to the pixel RGB values array
pixelRGBValues.push([r, g, b, a, led, pixel, row]);
}
pixelRGBValues.sort((a, b) => a[5] - b[5]);
//Copy the values to a new array for resorting
let ledRGBValues = [... pixelRGBValues];
//Sort the array based on led index
ledRGBValues.sort((a, b) => a[4] - b[4]);
//Generate JSON in WLED format
let JSONledString = '';
//Set starting values for the segment check to something that is no color
let segmentStart = -1;
let maxi = ledRGBValues.length;
let curentColorIndex = 0
let commandArray = [];
//For evry pixel in the LED array
for (let i = 0; i < maxi; i++) {
let pixel = ledRGBValues[i];
let r = pixel[0];
let g = pixel[1];
let b = pixel[2];
let a = pixel[3];
let segmentString = '';
let segmentEnd = -1;
if(segmentValueCheck){
if (segmentStart < 0){
//This is the first led of a new segment
segmentStart = i;
} //Else we allready have a start index
if (i < maxi - 1){
let iNext = i + 1;
let nextPixel = ledRGBValues[iNext];
if (nextPixel[0] != r || nextPixel[1] != g || nextPixel[2] != b ){
//Next pixel has new color
//The current segment ends with this pixel
segmentEnd = i + 1 //WLED wants the NEXT LED as the stop led...
if (segmentStart == i && hybridAddressing){
//If only one led/pixel, no segment info needed
if (JSONledString == ''){
//If addressing is single, we need to start every command with a starting possition
segmentString = '' + i + ',';
//Fixed to b2
} else{
segmentString = ''
}
}
else {
segmentString = segmentStart + ',' + segmentEnd + ',';
}
}
} else {
//This is the last pixel, so the segment must end
segmentEnd = i + 1;
if (segmentStart + 1 == segmentEnd && hybridAddressing){
//If only one led/pixel, no segment info needed
if (JSONledString == ''){
//If addressing is single, we need to start every command with a starting possition
segmentString = '' + i + ',';
//Fixed to b2
} else{
segmentString = ''
}
}
else {
segmentString = segmentStart + ',' + segmentEnd + ',';
}
}
} else{
//Write every pixel
if (JSONledString == ''){
//If addressing is single, we need to start every command with a starting possition
JSONledString = i
//Fixed to b2
}
segmentStart = i
segmentEnd = i
//Segment string should be empty for when addressing single. So no need to set it again.
}
if (a < 255){
hasTransparency = true; //If ANY pixel has alpha < 255 then this is set to true to warn the user
}
if (segmentEnd > -1){
//This is the last pixel in the segment, write to the JSONledString
//Return color value in selected format
let colorValueString = r + ',' + g + ',' + b ;
if (hexValueCheck){
const [red, green, blue] = [r, g, b];
colorValueString = `${[red, green, blue].map(x => x.toString(16).padStart(2, '0')).join('')}`;
} else{
//do nothing, allready set
}
// Check if start and end is the same, in which case remove
JSONledString += segmentString + colorSeparatorStart + colorValueString + colorSeparatorEnd;
fileJSON = JSONledString + segmentString + colorSeparatorStart + colorValueString + colorSeparatorEnd;
curentColorIndex = curentColorIndex + 1; // We've just added a new color to the string so up the count with one
if (curentColorIndex % maxNoOfColorsInCommandSting === 0 || i == maxi - 1) {
//If we have accumulated the max number of colors to send in a single command or if this is the last pixel, we should write the current colorstring to the array
commandArray.push(JSONledString);
JSONledString = ''; //Start on an new command string
} else
{
//Add a comma to continue the command string
JSONledString = JSONledString + ','
}
//Reset segment values
segmentStart = - 1;
}
}
JSONledString = ''
//For every commandString in the array
for (let i = 0; i < commandArray.length; i++) {
let thisJSONledString = `{"on":true,"bri":${brgh.value},"seg":{"id":${segID},"i":[${commandArray[i]}]}}`;
httpArray.push(thisJSONledString);
let thiscurlString = `curl -X POST "http://${gurl.value}/json/state" -d \'${thisJSONledString}\' -H "Content-Type: application/json"`;
//Aggregated Strings That should be returned to the user
if (i > 0){
JSONledString = JSONledString + '\n<NEXT COMMAND (multiple commands not supported in API/preset setup)>\n';
curlString = curlString + ' && ';
}
JSONledString += thisJSONledString;
curlString += thiscurlString;
}
haString = `#Uncomment if you don\'t allready have these defined in your switch section of your configuration.yaml
#- platform: command_line
#switches:
${haIDe.value}
friendly_name: ${haNe.value}
unique_id: ${haUe.value}
command_on: >
${curlString}
command_off: >
curl -X POST "http://${gurl.value}/json/state" -d \'{"on":false}\' -H "Content-Type: application/json"`;
if (formatSelection == 'wled'){
JLD.value = JSONledString;
} else if (formatSelection == 'curl'){
JLD.value = curlString;
} else if (formatSelection == 'ha'){
JLD.value = haString;
} else {
JLD.value = 'ERROR!/n' + formatSelection + ' is an unknown format.'
}
fileJSON += ']}}';
let infoDiv = imin;
let canvasDiv = imin;
if (hasTransparency){
imageInfo = imageInfo + '<p><b>WARNING!</b> Transparency info detected in image. Transparency (alpha) has been ignored. To ensure you get the result you desire, use only solid colors in your image.</p>'
}
infoDiv.innerHTML = imageInfo;
canvasDiv.style.display = "block"
//Drawing the image
drawBoxes(pixelRGBValues, sizeX, sizeY);
}
}

View File

@ -0,0 +1,324 @@
.box {
border: 2px solid #fff;
}
body {
font-family: Arial, sans-serif;
background-color: #111;
}
.top-part {
width: 600px;
margin: 0 auto;
}
.container {
max-width: 100% -40px;
border-radius: 0px;
padding: 20px;
text-align: center;
}
h1 {
font-size: 2.3em;
color: #ddd;
margin: 1px 0;
font-family: Arial, sans-serif;
line-height: 0.5;
/*text-align: center;*/
}
h2 {
font-size: 1.1em;
color: rgba(221, 221, 221, 0.61);
margin: 1px 0;
font-family: Arial, sans-serif;
line-height: 0.5;
text-align: center;
}
h3 {
font-size: 0.7em;
color: rgba(221, 221, 221, 0.61);
margin: 1px 0;
font-family: Arial, sans-serif;
line-height: 1.4;
text-align: center;
align-items: center;
justify-content: center;
display: flex;
}
p {
font-size: 1em;
color: #777;
line-height: 1.5;
font-family: Arial, sans-serif;
}
#fieldTable {
font-size: 1 em;
color: #777;
line-height: 1;
font-family: Arial, sans-serif;
}
#scaleTable {
font-size: 1 em;
color: #777;
line-height: 1;
font-family: Arial, sans-serif;
}
#drop-zone {
display: block;
width: 100%-40px;
border: 3px dashed #ddd;
border-radius: 0px;
text-align: center;
padding: 20px;
margin: 0px;
cursor: pointer;
font-family: Arial, sans-serif;
font-size: 15px;
color: #777;
}
#file-picker {
display: none;
}
.adaptiveTD{
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
}
.mainSelector {
background-color: #222;
color: #ddd;
border: 1px solid #333;
margin-top: 4px;
margin-bottom: 4px;
padding: 0 8px;
height: 28px;
font-size: 15px;
border-radius: 7px;
flex-grow: 1;
display: flex;
align-items: center;
justify-content: center;
}
.adaptiveSelector {
background-color: #222;
color: #ddd;
border: 1px solid #333;
margin-top: 4px;
margin-bottom: 4px;
padding: 0 8px;
height: 28px;
font-size: 15px;
border-radius: 7px;
flex-grow: 1;
display: none;
}
.segmentsDiv{
width: 36px;
padding-left: 5px;
}
* input[type=range] {
appearance: none;
-moz-appearance: none;
-webkit-appearance: none;
flex-grow: 1;
padding: 0;
margin: 4px 8px 4px 0;
background-color: transparent;
cursor: pointer;
background: linear-gradient(to right, #bbb 50%, #333 50%);
border-radius: 7px;
}
input[type=range]:focus {
outline: none;
}
input[type=range]::-webkit-slider-runnable-track {
height: 28px;
cursor: pointer;
background: transparent;
border-radius: 7px;
}
input[type=range]::-webkit-slider-thumb {
height: 16px;
width: 16px;
border-radius: 50%;
background: #fff;
cursor: pointer;
-webkit-appearance: none;
margin-top: 4px;
border-radius: 7px;
}
input[type=range]::-moz-range-track {
height: 28px;
background-color: rgba(0, 0, 0, 0);
border-radius: 7px;
}
input[type=range]::-moz-range-thumb {
border: 0px solid rgba(0, 0, 0, 0);
height: 16px;
width: 16px;
border-radius: 7px;
background: #fff;
}
.rangeNumber{
width: 20px;
vertical-align: middle;
}
.fullTextField[type=text] {
background-color: #222;
border: 1px solid #333;
padding-inline-start: 5px;
margin-top: 4px;
margin-bottom: 4px;
height: 24px;
border-radius: 0px;
font-family: Arial, sans-serif;
font-size: 15px;
color: #ddd;
border-radius: 7px;
flex-grow: 1;
display: flex;
align-items: center;
justify-content: center;
}
.flxTFld{
background-color: #222;
border: 1px solid #333;
padding-inline-start: 5px;
height: 24px;
border-radius: 0px;
font-family: Arial, sans-serif;
font-size: 15px;
color: #ddd;
border-radius: 7px;
flex-grow: 1;
display: flex;
align-items: center;
justify-content: center;
}
* input[type=submit] {
background-color: #222;
border: 1px solid #333;
padding: 0.5em;
width: 100%;
border-radius: 24px;
font-family: Arial, sans-serif;
font-size: 1.3em;
color: #ddd;
}
* button {
background-color: #222;
border: 1px solid #333;
padding-inline: 5px;
width: 100%;
border-radius: 24px;
font-family: Arial, sans-serif;
font-size: 1em;
color: #ddd;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
#scaleDiv {
display: flex;
align-items: center;
vertical-align: middle;
}
textarea {
grid-row: 1 / 2;
width: 100%;
height: 200px;
background-color: #222;
border: 1px solid #333;
color: #ddd;
}
.hide {
display: none;
}
.svg-icon {
vertical-align: middle;
}
#image-container {
display: grid;
grid-template-rows: 1fr 1fr;
}
#button-container {
display: flex;
padding-bottom: 10px;
padding-top: 10px;
}
.buttonclass {
flex: 1;
padding-top: 5px;
padding-bottom: 5px;
}
.gap {
width: 10px;
}
#submitConvert::before {
content: "";
display: inline-block;
background-image: url('data:image/svg+xml;utf8, <svg style="width:24px;height:24px" viewBox="0 0 24 24" <path fill="currentColor" d="M12,6V9L16,5L12,1V4A8,8 0 0,0 4,12C4,13.57 4.46,15.03 5.24,16.26L6.7,14.8C6.25,13.97 6,13 6,12A6,6 0 0,1 12,6M18.76,7.74L17.3,9.2C17.74,10.04 18,11 18,12A6,6 0 0,1 12,18V15L8,19L12,23V20A8,8 0 0,0 20,12C20,10.43 19.54,8.97 18.76,7.74Z" /></svg>');
width: 36px;
height: 36px;
}
#sizeDiv * {
display: inline-block;
}
.sizeInputFields{
width: 50px;
background-color: #222;
border: 1px solid #333;
padding-inline-start: 5px;
margin-top: -5px;
height: 24px;
border-radius: 7px;
font-family: Arial, sans-serif;
font-size: 15px;
color: #ddd;
}
a:link {
color: rgba(221, 221, 221, 0.61);
background-color: transparent;
text-decoration: none;
}
a:visited {
color: rgba(221, 221, 221, 0.61);
background-color: transparent;
text-decoration: none;
}
a:hover {
color: #ddd;
background-color: transparent;
text-decoration: none;
}
a:active {
color: rgba(221, 221, 221, 0.61);
background-color: transparent;
text-decoration: none;
}

View File

@ -0,0 +1,210 @@
<!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 Pixel Art Converter</title>
<link rel="stylesheet" href="pixart.css">
<link rel="shortcut icon" href="favicon-16x16.png">
<script type="text/javascript">
var d = document;
function gId(e) {return d.getElementById(e);}
function cE(e) {return d.createElement(e);}
</script>
</head>
<body>
<body>
<div class="top-part" >
<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>
WLED Pixel Art Converter
</h1>
</div>
<h2>Convert image to WLED JSON (pixel art on WLED matrix)</h2>
<p>
<table id="fieldTable" style="width: 100%; table-layout: fixed; align-content: center;">
<tr>
<td style="vertical-align: middle;">
<label for="ledSetupSelector">Led setup:</label>
</td>
<td class="adaptiveTD">
<select id="ledSetupSelector" class="mainSelector">
<option value="matrix" selected>2D Matrix</option>
<option value="r2l">Serpentine, first row right to left &lt;-</option>
<option value="l2r">Serpentine, first row left to right -&gt;</option>
</select>
</td>
</tr>
<tr>
<td style="vertical-align: middle;">
<label for="formatSelector">Output format:</label>
</td>
<td class="adaptiveTD">
<select id="formatSelector" class="mainSelector">
<option value="wled" selected>WLED JSON</option>
<option value="curl">CURL</option>
<option value="ha">Home Assistant YAML</option>
</select>
</td>
</tr>
<tr>
<td style="vertical-align: middle;">
<label for="colorFormatSelector">Color code format:</label>
</td>
<td class="adaptiveTD">
<select id="colorFormatSelector" class="mainSelector">
<option value="hex" selected>HEX (&quot;f4f4f4&quot;)</option>
<option value="dec">DEC (244,244,244)</option>
</select>
</td>
</tr>
<tr>
<td style="vertical-align: middle;">
<label for="addressingSelector">Addressing:</label>
</td>
<td class="adaptiveTD">
<select id="addressingSelector" class="mainSelector">
<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>
<tr>
<td style="vertical-align: middle;">
<label for="brightnessNumber">Brightness:</label>
</td>
<td style="vertical-align: middle; display: flex; align-items: center;">
<input type="range" id="brightnessNumber" min="1" max="255" value="128">
<span id="brightnessValue">128</span>
</td>
</tr>
<tr>
<td style="vertical-align: middle;">
<label for="colorLimitNumber">Max no of colors/JSON:</label>
</td>
<td style="vertical-align: middle; display: flex; align-items: center;">
<input type="range" id="colorLimitNumber" min="1" max="512" value="256">
<span id="colorLimitValue" >256</span>
</td>
</tr>
<tr class="ha-hide">
<td style="vertical-align: middle;">
<label for="haID">HA Device ID:</label>
</td>
<td class="adaptiveTD">
<input class="fullTextField" type="text" id="haID" value="pixel_art_controller_001">
</td>
</tr>
<tr class="ha-hide">
<td style="vertical-align: middle;">
<label for="haUID">HA Device Unique ID:</label>
</td>
<td class="adaptiveTD">
<input class="fullTextField" type="text" id="haUID" value="pixel_art_controller_001a">
</td>
</tr>
<tr class="ha-hide">
<td style="vertical-align: middle;">
<label for="haName">HA Device Name:</label>
</td>
<td class="adaptiveTD">
<input class="fullTextField" type="text" id="haName" value="Pixel Art Kitchen">
</td>
</tr>
<tr>
<td style="vertical-align: middle;">
<label for="curlUrl">Device IP/host name:</label>
</td>
<td class="adaptiveTD">
<input class="fullTextField" type="text" id="curlUrl" value="">
</td>
</tr>
<tr>
<td style="vertical-align: middle;">
<label for="targetSegment">Target segment id:</label>
</td>
<td class="adaptiveTD">
<input class="flxTFld" type="number" id="segID" value="0" min="0" max="63">
<select id="targetSegment" class="adaptiveSelector">
</select>
<div id="getSegmentsDiv" class="segmentsDiv"></div>
</td>
</tr>
</table>
<table class= "scaleTableClass" id="scaleTable" style="width: 100%; table-layout: fixed; align-content: center;">
<tr>
<td style="vertical-align: middle;">
<div id="scaleDiv">
<svg id="scaleToggle" style="width:36px;height:36px; cursor: pointer;" viewBox="0 0 24 24" onclick="switchScale()">
<path id="scaleTogglePath" fill="currentColor" d="M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M7,15A3,3 0 0,1 4,12A3,3 0 0,1 7,9A3,3 0 0,1 10,12A3,3 0 0,1 7,15Z" />
</svg>
&nbsp;Scale image
</div>
</td>
<td style="vertical-align: middle;">
<div id="sizeDiv" style="display: none;">
<label for="sizeX">W : </label> &nbsp;<input class="sizeInputFields" type="number" id="sizeX" min="1" value="16">
&nbsp;&nbsp;&nbsp;
<label for="sizeY">H : </label> &nbsp;<input class="sizeInputFields" type="number" id="sizeY" min="1" value="16">
</div>
</td>
</tr>
</table>
</p>
<p>
<label for="file-picker">
<div id="drop-zone">
Drop image here <br>or <br>
Click to select a file
</div>
</label>
</p>
<p>
<input type="file" id="file-picker" style="display: none;">
<div style="width: 100%; text-align: center;">
<img id="preview" style="display: none; margin: 0 auto;">
</div>
<!--
<div id="submitConvertDiv" style="display: none;">
<button id="convertbutton" class="buttonclass"></button>
</div>
-->
<div id="raw-image-container" style="display: none">
<img id="image" src="" alt="RawImage image">
</div>
</p>
<div id="image-container" style="display: none;">
<div id="image-info" style="display: none"></div>
<textarea id="JSONled" readonly></textarea>
</div>
<div id="button-container" style="display: none;">
<button id="copyJSONledbutton" class="buttonclass"></button>
<div id="gap1" class="gap"></div>
<button id="sendJSONledbutton" class="buttonclass"></button>
</div>
<div>
<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>
<canvas id="pixelCanvas"></canvas>
</div>
<script src="statics.js" type="text/javascript"></script>
<script src="getPixelValues.js" type="text/javascript"></script>
<script src="boxdraw.js" type="text/javascript"></script>
<script src="pixart.js" type="text/javascript"></script>
</body>
</html>

View File

@ -0,0 +1,364 @@
//Start up code
//if (window.location.protocol == "file:") {
// let locip = prompt("File Mode. Please enter WLED IP!");
// gId('curlUrl').value = locip;
//} else
//
//Start up code
let devMode = false; //Remove
gurl.value = location.host;
const urlParams = new URLSearchParams(window.location.search);
if (gurl.value.length < 1){
gurl.value = "Missing_Host";
}
function gen(){
//Generate image if enough info is in place
//Is host non empty
//Is image loaded
//is scale > 0
if (((szX.value > 0 && szY.value > 0) || szDiv.style.display == 'none') && gurl.value.length > 0 && prw.style.display != 'none'){
//regenerate
let base64Image = prw.src;
if (isValidBase64Gif(base64Image)) {
im.src = base64Image;
getPixelRGBValues(base64Image);
imcn.style.display = "block";
bcn.style.display = "";
} else {
let imageInfo = '<p><b>WARNING!</b> File does not appear to be a valid image</p>';
imin.innerHTML = imageInfo;
imin.style.display = "block";
imcn.style.display = "none";
JLD.value = '';
if (devMode) console.log("The string '" + base64Image + "' is not a valid base64 image.");
}
}
if(gurl.value.length > 0){
gId("sSg").setAttribute("fill", accentColor);
} else{
gId("sSg").setAttribute("fill", accentTextColor);
let ts = tSg;
ts.style.display = "none";
ts.innerHTML = "";
sID.style.display = "flex";
}
}
// Code for copying the generated string to clipboard
cjb.addEventListener('click', async () => {
let JSONled = JLD;
JSONled.select();
try {
await navigator.clipboard.writeText(JSONled.value);
} catch (err) {
try {
await d.execCommand("copy");
} catch (err) {
console.error('Failed to copy text: ', err);
}
}
});
// Event listeners =======================
lSS.addEventListener("change", gen);
szY.addEventListener("change", gen);
szX.addEventListener("change", gen);
cFS.addEventListener("change", gen);
aS.addEventListener("change", gen);
brgh.addEventListener("change", gen);
cLN.addEventListener("change", gen);
haIDe.addEventListener("change", gen);
haUe.addEventListener("change", gen);
haNe.addEventListener("change", gen);
gurl.addEventListener("change", gen);
sID.addEventListener("change", gen);
prw.addEventListener("load", gen);
//gId("convertbutton").addEventListener("click", gen);
tSg.addEventListener("change", () => {
sop = tSg.options[tSg.selectedIndex];
szX.value = sop.dataset.x;
szY.value = sop.dataset.y;
gen();
});
gId("sendJSONledbutton").addEventListener('click', async () => {
if (window.location.protocol === "https:") {
alert('Will only be available when served over http (or WLED is run over https)');
} else {
postPixels();
}
});
brgh.oninput = () => {
brgV.textContent = brgh.value;
let perc = parseInt(brgh.value)*100/255;
var val = `linear-gradient(90deg, #bbb ${perc}%, #333 ${perc}%)`;
brgh.style.backgroundImage = val;
}
cLN.oninput = () => {
let cln = cLN;
cLV.textContent = cln.value;
let perc = parseInt(cln.value)*100/512;
var val = `linear-gradient(90deg, #bbb ${perc}%, #333 ${perc}%)`;
cln.style.backgroundImage = val;
}
frm.addEventListener("change", () => {
for (var i = 0; i < hideableRows.length; i++) {
hideableRows[i].classList.toggle("hide", frm.value !== "ha");
gen();
}
});
async function postPixels() {
let ss = gId("sendSvgP");
ss.setAttribute("fill", prsCol);
let er = false;
for (let i of httpArray) {
try {
if (devMode) console.log(i);
if (devMode) console.log(i.length);
const response = await fetch('http://'+gId('curlUrl').value+'/json/state', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
//'Content-Type': 'text/html; charset=UTF-8'
},
body: i
});
const data = await response.json();
if (devMode) console.log(data);
} catch (error) {
console.error(error);
er = true;
}
}
if(er){
//Something went wrong
ss.setAttribute("fill", redColor);
setTimeout(function(){
ss.setAttribute("fill", accentTextColor);
}, 1000);
} else {
// A, OK
ss.setAttribute("fill", greenColor);
setTimeout(function(){
ss.setAttribute("fill", accentColor);
}, 1000);
}
}
//File uploader code
const dropZone = gId('drop-zone');
const filePicker = gId('file-picker');
const preview = prw;
// Listen for dragenter, dragover, and drop events
dropZone.addEventListener('dragenter', dragEnter);
dropZone.addEventListener('dragover', dragOver);
dropZone.addEventListener('drop', dropped);
dropZone.addEventListener('click', zoneClicked);
// Listen for change event on file picker
filePicker.addEventListener('change', filePicked);
// Handle zone click
function zoneClicked(e) {
e.preventDefault();
//this.classList.add('drag-over');
//alert('Hej');
filePicker.click();
}
// Handle dragenter
function dragEnter(e) {
e.preventDefault();
this.classList.add('drag-over');
}
// Handle dragover
function dragOver(e) {
e.preventDefault();
}
// Handle drop
function dropped(e) {
e.preventDefault();
this.classList.remove('drag-over');
// Get the dropped file
const file = e.dataTransfer.files[0];
updatePreview(file)
}
// Handle file picked
function filePicked(e) {
// Get the picked file
const file = e.target.files[0];
updatePreview(file)
}
// Update the preview image
function updatePreview(file) {
// Use FileReader to read the file
const reader = new FileReader();
reader.onload = () => {
// Update the preview image
preview.src = reader.result;
//gId("submitConvertDiv").style.display = "";
prw.style.display = "";
};
reader.readAsDataURL(file);
}
function isValidBase64Gif(string) {
// Use a regular expression to check that the string is a valid base64 string
/*
const base64gifPattern = /^data:image\/gif;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/;
const base64pngPattern = /^data:image\/png;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/;
const base64jpgPattern = /^data:image\/jpg;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/;
const base64webpPattern = /^data:image\/webp;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/;
*/
//REMOVED, Any image appear to work as long as it can be drawn to the canvas. Leaving code in for future use, possibly
if (1==1 || base64gifPattern.test(string) || base64pngPattern.test(string) || base64jpgPattern.test(string) || base64webpPattern.test(string)) {
return true;
} else {
//Not OK
return false;
}
}
var hideableRows = d.querySelectorAll(".ha-hide");
for (var i = 0; i < hideableRows.length; i++) {
hideableRows[i].classList.add("hide");
}
frm.addEventListener("change", () => {
for (var i = 0; i < hideableRows.length; i++) {
hideableRows[i].classList.toggle("hide", frm.value !== "ha");
}
});
function switchScale() {
//let scalePath = gId("scaleDiv").children[1].children[0]
let scaleTogglePath = scDiv.children[0].children[0]
let color = scaleTogglePath.getAttribute("fill");
let d = '';
if (color === accentColor) {
color = accentTextColor;
d = scaleToggleOffd;
szDiv.style.display = "none";
// Set values to actual XY of image, if possible
} else {
color = accentColor;
d = scaleToggleOnd;
szDiv.style.display = "";
}
//scalePath.setAttribute("fill", color);
scaleTogglePath.setAttribute("fill", color);
scaleTogglePath.setAttribute("d", d);
gen();
}
function generateSegmentOptions(array) {
//This function is prepared for a name property on each segment for easier selection
//Currently the name is generated generically based on index
tSg.innerHTML = "";
for (var i = 0; i < array.length; i++) {
var option = cE("option");
option.value = array[i].value;
option.text = array[i].text;
option.dataset.x = array[i].x;
option.dataset.y = array[i].y;
tSg.appendChild(option);
if(i === 0) {
option.selected = true;
szX.value = option.dataset.x;
szY.value = option.dataset.y;
}
}
}
// Get segments from device
async function getSegments() {
cv = gurl.value;
if (cv.length > 0 ){
try {
var arr = [];
const response = await fetch('http://'+cv+'/json/state');
const json = await response.json();
let ids = json.seg.map(sg => ({id: sg.id, n: sg.n, xs: sg.start, xe: sg.stop, ys: sg.startY, ye: sg.stopY}));
for (var i = 0; i < ids.length; i++) {
arr.push({
value: ids[i]["id"],
text: ids[i]["n"] + ' (index: ' + ids[i]["id"] + ')',
x: ids[i]["xe"] - ids[i]["xs"],
y: ids[i]["ye"] - ids[i]["ys"]
});
}
generateSegmentOptions(arr);
tSg.style.display = "flex";
sID.style.display = "none";
gId("sSg").setAttribute("fill", greenColor);
setTimeout(function(){
gId("sSg").setAttribute("fill", accentColor);
}, 1000);
} catch (error) {
console.error(error);
gId("sSg").setAttribute("fill", redColor);
setTimeout(function(){
gId("sSg").setAttribute("fill", accentColor);
}, 1000);
tSg.style.display = "none";
sID.style.display = "flex";
}
} else{
gId("sSg").setAttribute("fill", redColor);
setTimeout(function(){
gId("sSg").setAttribute("fill", accentTextColor);
}, 1000);
tSg.style.display = "none";
sID.style.display = "flex";
}
}
//Initial population of segment selection
function generateSegmentArray(noOfSegments) {
var arr = [];
for (var i = 0; i < noOfSegments; i++) {
arr.push({
value: i,
text: "Segment index " + i
});
}
return arr;
}
var segmentData = generateSegmentArray(10);
generateSegmentOptions(segmentData);
seDiv.innerHTML =
'<svg id=getSegmentsSVG style="width:36px;height:36px;cursor:pointer" viewBox="0 0 24 24" onclick="getSegments()"><path id=sSg fill="currentColor" d="M6.5 20Q4.22 20 2.61 18.43 1 16.85 1 14.58 1 12.63 2.17 11.1 3.35 9.57 5.25 9.15 5.68 7.35 7.38 5.73 9.07 4.1 11 4.1 11.83 4.1 12.41 4.69 13 5.28 13 6.1V12.15L14.6 10.6L16 12L12 16L8 12L9.4 10.6L11 12.15V6.1Q9.1 6.45 8.05 7.94 7 9.43 7 11H6.5Q5.05 11 4.03 12.03 3 13.05 3 14.5 3 15.95 4.03 17 5.05 18 6.5 18H18.5Q19.55 18 20.27 17.27 21 16.55 21 15.5 21 14.45 20.27 13.73 19.55 13 18.5 13H17V11Q17 9.8 16.45 8.76 15.9 7.73 15 7V4.68Q16.85 5.55 17.93 7.26 19 9 19 11 20.73 11.2 21.86 12.5 23 13.78 23 15.5 23 17.38 21.69 18.69 20.38 20 18.5 20M12 11.05Z" /></svg>'
/*gId("convertbutton").innerHTML =
'<svg style="width:36px;height:36px" viewBox="0 0 24 24"><path fill="currentColor" d="M12,6V9L16,5L12,1V4A8,8 0 0,0 4,12C4,13.57 4.46,15.03 5.24,16.26L6.7,14.8C6.25,13.97 6,13 6,12A6,6 0 0,1 12,6M18.76,7.74L17.3,9.2C17.74,10.04 18,11 18,12A6,6 0 0,1 12,18V15L8,19L12,23V20A8,8 0 0,0 20,12C20,10.43 19.54,8.97 18.76,7.74Z" /> </svg>&nbsp; Convert to WLED JSON ';
*/
cjb.innerHTML =
'<svg class="svg-icon" style="width:36px;height:36px" viewBox="0 0 24 24"> <path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z" /> </svg>&nbsp; Copy to clipboard';
gId("sendJSONledbutton").innerHTML =
'<svg class="svg-icon" style="width:36px;height:36px" viewBox="0 0 24 24"> <path id=sendSvgP fill="currentColor" d="M6.5 20Q4.22 20 2.61 18.43 1 16.85 1 14.58 1 12.63 2.17 11.1 3.35 9.57 5.25 9.15 5.88 6.85 7.75 5.43 9.63 4 12 4 14.93 4 16.96 6.04 19 8.07 19 11 20.73 11.2 21.86 12.5 23 13.78 23 15.5 23 17.38 21.69 18.69 20.38 20 18.5 20H13Q12.18 20 11.59 19.41 11 18.83 11 18V12.85L9.4 14.4L8 13L12 9L16 13L14.6 14.4L13 12.85V18H18.5Q19.55 18 20.27 17.27 21 16.55 21 15.5 21 14.45 20.27 13.73 19.55 13 18.5 13H17V11Q17 8.93 15.54 7.46 14.08 6 12 6 9.93 6 8.46 7.46 7 8.93 7 11H6.5Q5.05 11 4.03 12.03 3 13.05 3 14.5 3 15.95 4.03 17 5.05 18 6.5 18H9V20M12 13Z" /> </svg>&nbsp; Send to device';
//After everything is loaded, check if we have a possible IP/host
if(gurl.value.length > 0){
// Needs to be addressed directly here so the object actually exists
gId("sSg").setAttribute("fill", accentColor);
}

View File

@ -0,0 +1,19 @@
{
"name": "WLED Pixel Art Convertor",
"short_name": "ledconv",
"icons": [
{
"src": "/favicon-32x32.png",
"sizes": "32x322",
"type": "image/png"
},
{
"src": "/favicon-32x32.png",
"sizes": "32x32",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

View File

@ -0,0 +1,51 @@
//elements
var gurl = gId('curlUrl');
var szX = gId("sizeX");
var szY = gId("sizeY");
var szDiv = gId("sizeDiv");
var prw = gId("preview");
var sID = gId('segID');
var JLD = gId('JSONled');
var tSg = gId('targetSegment');
var brgh = gId("brightnessNumber");
var seDiv = gId("getSegmentsDiv")
var cjb = gId("copyJSONledbutton");
var frm = gId("formatSelector");
var cLN = gId("colorLimitNumber");
var haIDe = gId("haID");
var haUe = gId("haUID");
var haNe = gId("haName");
var aS = gId("addressingSelector");
var cFS = gId("colorFormatSelector");
var lSS = gId("ledSetupSelector");
var imin = gId('image-info');
var imcn = gId('image-container');
var bcn = gId("button-container");
var im = gId('image');
//var ss = gId("sendSvgP");
var scDiv = gId("scaleDiv");
var w = window;
var canvas = gId('pixelCanvas');
var brgV = gId("brightnessValue");
var cLV = gId("colorLimitValue")
//vars
var httpArray = [];
var fileJSON = '';
var hideableRows = d.querySelectorAll(".ha-hide");
for (var i = 0; i < hideableRows.length; i++) {
hideableRows[i].classList.add("hide");
}
var accentColor = '#eee';
var accentTextColor = '#777';
var prsCol = '#ccc';
var greenColor = '#056b0a';
var redColor = '#6b050c';
var scaleToggleOffd = "M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M7,15A3,3 0 0,1 4,12A3,3 0 0,1 7,9A3,3 0 0,1 10,12A3,3 0 0,1 7,15Z";
var scaleToggleOnd = "M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M17,15A3,3 0 0,1 14,12A3,3 0 0,1 17,9A3,3 0 0,1 20,12A3,3 0 0,1 17,15Z";
var sSg = gId("getSegmentsSVGpath");

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@
<title>WLED Settings</title> <title>WLED Settings</title>
<script> <script>
var d=document; var d=document;
var loc = false, locip; var loc = false, locip, locproto = "http:";
function gId(n){return d.getElementById(n);} function gId(n){return d.getElementById(n);}
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript // https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) { function loadJS(FILE_URL, async = true) {
@ -27,16 +27,28 @@
}); });
} }
function S() { function S() {
if (window.location.protocol == "file:") { let l = window.location;
if (l.protocol == "file:") {
loc = true; loc = true;
locip = localStorage.getItem('locIp'); locip = localStorage.getItem('locIp');
if (!locip) { if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!"); 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 > 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> </script>
<style> <style>
@ -65,15 +77,15 @@
</style> </style>
</head> </head>
<body onload="S()"> <body onload="S()">
<button type=submit id="b" onclick="window.location='/'">Back</button> <button type=submit id="b" onclick="window.location=getURL('/')">Back</button>
<button type="submit" onclick="window.location='./settings/wifi'">WiFi Setup</button> <button type="submit" onclick="window.location=getURL('/settings/wifi')">WiFi Setup</button>
<button type="submit" onclick="window.location='./settings/leds'">LED Preferences</button> <button type="submit" onclick="window.location=getURL('/settings/leds')">LED Preferences</button>
<button id="2dbtn" style="display:none;" type="submit" onclick="window.location='./settings/2D'">2D Configuration</button> <button id="2dbtn" style="display:none;" type="submit" onclick="window.location=getURL('/settings/2D')">2D Configuration</button>
<button type="submit" onclick="window.location='./settings/ui'">User Interface</button> <button type="submit" onclick="window.location=getURL('/settings/ui')">User Interface</button>
<button id="dmxbtn" style="display:none;" type="submit" onclick="window.location='./settings/dmx'">DMX Output</button> <button id="dmxbtn" style="display:none;" type="submit" onclick="window.location=getURL('/settings/dmx')">DMX Output</button>
<button type="submit" onclick="window.location='./settings/sync'">Sync Interfaces</button> <button type="submit" onclick="window.location=getURL('/settings/sync')">Sync Interfaces</button>
<button type="submit" onclick="window.location='./settings/time'">Time & Macros</button> <button type="submit" onclick="window.location=getURL('/settings/time')">Time & Macros</button>
<button type="submit" onclick="window.location='./settings/um'">Usermods</button> <button type="submit" onclick="window.location=getURL('/settings/um')">Usermods</button>
<button type="submit" onclick="window.location='./settings/sec'">Security & Updates</button> <button type="submit" onclick="window.location=getURL('/settings/sec')">Security & Updates</button>
</body> </body>
</html> </html>

View File

@ -7,11 +7,13 @@
<title>2D Set-up</title> <title>2D Set-up</title>
<script> <script>
var d=document; var d=document;
var loc = false, locip; var loc = false, locip, locproto = "http:";
var maxPanels=64; var maxPanels=64;
var ctx = null; // WLEDMM
function H(){window.open("https://kno.wled.ge/features/2D");} 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 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 // https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) { function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script"); let scE = d.createElement("script");
@ -33,16 +35,29 @@
}); });
} }
function S() { function S() {
if (window.location.protocol == "file:") { let l = window.location;
if (l.protocol == "file:") {
loc = true; loc = true;
locip = localStorage.getItem('locIp'); locip = localStorage.getItem('locIp');
if (!locip) { if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!"); 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];
} }
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() { function UI() {
@ -52,6 +67,30 @@
return; return;
} }
gId("mpdiv").style.display = "block"; gId("mpdiv").style.display = "block";
draw();
}
var timeout;
function showToast(text, error = false)
{
var x = gId("toast");
x.innerHTML = text;
x.className = error ? "error":"show";
clearTimeout(timeout);
x.style.animation = 'none';
timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900);
}
function uploadFile(name) {
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");
var formData = new FormData();
formData.append("data", d.Sf.data.files[0], name);
req.send(formData);
d.Sf.data.value = '';
return false;
} }
function addPanels() { function addPanels() {
@ -67,21 +106,21 @@
var pw = parseInt(d.Sf.PW.value); var pw = parseInt(d.Sf.PW.value);
var ph = parseInt(d.Sf.PH.value); var ph = parseInt(d.Sf.PH.value);
let b = `<div id="pnl${i}"><hr class="sml">Panel ${i}<br> let b = `<div id="pnl${i}"><hr class="sml">Panel ${i}<br>
1<sup>st</sup> LED: <select name="P${i}B"> 1<sup>st</sup> LED: <select name="P${i}B" oninput="UI()">
<option value="0">Top</option> <option value="0">Top</option>
<option value="1">Bottom</option> <option value="1">Bottom</option>
</select><select name="P${i}R"> </select><select name="P${i}R" oninput="UI()">
<option value="0">Left</option> <option value="0">Left</option>
<option value="1">Right</option> <option value="1">Right</option>
</select><br> </select><br>
Orientation: <select name="P${i}V"> Orientation: <select name="P${i}V" oninput="UI()">
<option value="0">Horizontal</option> <option value="0">Horizontal</option>
<option value="1">Vertical</option> <option value="1">Vertical</option>
</select><br> </select><br>
Serpentine: <input type="checkbox" name="P${i}S"><br> Serpentine: <input type="checkbox" name="P${i}S" oninput="UI()"><br>
Dimensions (WxH): <input id="P${i}W" name="P${i}W" type="number" min="1" max="128" value="${pw}"> x <input id="P${i}H" name="P${i}H" type="number" min="1" max="128" value="${ph}"><br> Dimensions (WxH): <input name="P${i}W" type="number" min="1" max="255" value="${pw}" oninput="UI()"> x <input name="P${i}H" type="number" min="1" max="255" value="${ph}" oninput="UI()"><br>
Offset X:<input id="P${i}X" name="P${i}X" type="number" min="0" max="256" value="0"> Offset X:<input name="P${i}X" type="number" min="0" max="255" value="0" oninput="UI()">
Y:<input id="P${i}Y" name="P${i}Y" type="number" min="0" max="256" value="0"><br><i>(offset from top-left corner in # LEDs)</i> Y:<input name="P${i}Y" type="number" min="0" max="255" value="0" oninput="UI()"><br><i>(offset from top-left corner in # LEDs)</i>
</div>`; </div>`;
p.insertAdjacentHTML("beforeend", b); p.insertAdjacentHTML("beforeend", b);
} }
@ -107,17 +146,17 @@ Y:<input id="P${i}Y" name="P${i}Y" type="number" min="0" max="256" value="0"><br
function gen() { function gen() {
resetPanels(); resetPanels();
var pansH = parseInt(d.Sf.MPH.value); var pansH = parseInt(Sf.MPH.value);
var pansV = parseInt(d.Sf.MPV.value); var pansV = parseInt(Sf.MPV.value);
var c = pansH*pansV; var c = pansH*pansV;
d.Sf.MPC.value = c; // number of panels Sf.MPC.value = c; // number of panels
var ps = d.Sf.PS.checked; var ps = Sf.PS.checked;
var pv = d.Sf.PV.value==="1"; var pv = Sf.PV.value==="1";
var pb = d.Sf.PB.value==="1"; var pb = Sf.PB.value==="1";
var pr = d.Sf.PR.value==="1"; var pr = Sf.PR.value==="1";
var pw = parseInt(d.Sf.PW.value); var pw = parseInt(Sf.PW.value);
var ph = parseInt(d.Sf.PH.value); var ph = parseInt(Sf.PH.value);
var h = pv ? pansV : pansH; var h = pv ? pansV : pansH;
var v = pv ? pansH : pansV; var v = pv ? pansH : pansV;
@ -127,13 +166,141 @@ Y:<input id="P${i}Y" name="P${i}Y" type="number" min="0" max="256" value="0"><br
var y = (pv?pr:pb) ? v-j-1: j; var y = (pv?pr:pb) ? v-j-1: j;
var x = (pv?pb:pr) ? h-i-1 : i; var x = (pv?pb:pr) ? h-i-1 : i;
x = ps && j%2 ? h-x-1 : x; x = ps && j%2 ? h-x-1 : x;
gId("P"+p+"X").value = (pv?y:x) * pw; Sf[`P${p}X`].value = (pv?y:x) * pw;
gId("P"+p+"Y").value = (pv?x:y) * ph Sf[`P${p}Y`].value = (pv?x:y) * ph
gId("P"+p+"W").value = pw; Sf[`P${p}W`].value = pw;
gId("P"+p+"H").value = ph; Sf[`P${p}H`].value = ph;
} }
} }
} }
function expand(o,i)
{
i.style.display = i.style.display!=="none" ? "none" : "";
o.style.rotate = i.style.display==="none" ? "none" : "90deg";
}
function draw() {
if (!ctx) {
//WLEDMM: add canvas, initialize and set UI
var canvas = gId("canvas");
canvas.width = window.innerWidth > 640?640:400; //Mobile gets 400, pc 640
canvas.height = canvas.width;
ctx = canvas.getContext('2d');
// window.requestAnimationFrame(animate);
}
//calc max height and width
var maxWidth = 0;
var maxHeight = 0;
for (let p=0; p<gId("panels").children.length; p++) {
var px = parseInt(Sf[`P${p}X`].value); //first led x
var py = parseInt(Sf[`P${p}Y`].value); //first led y
var pw = parseInt(Sf[`P${p}W`].value); //width
var ph = parseInt(Sf[`P${p}H`].value); //height
maxWidth = Math.max(maxWidth, px + pw);
maxHeight = Math.max(maxHeight, py + ph);
}
ctx.canvas.height = ctx.canvas.width / maxWidth * maxHeight;
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
var space=0; // space between panels + margin
var ppL = (ctx.canvas.width - space * 2) / maxWidth; //pixels per led
ctx.lineWidth = 1;
ctx.strokeStyle="yellow";
ctx.strokeRect(0, 0, ctx.canvas.width, ctx.canvas.height); // add space between panels
for (let p=0; p<gId("panels").children.length; p++) {
var px = parseInt(Sf[`P${p}X`].value); //first led x
var py = parseInt(Sf[`P${p}Y`].value); //first led y
var pw = parseInt(Sf[`P${p}W`].value); //width
var ph = parseInt(Sf[`P${p}H`].value); //height
var pb = Sf[`P${p}B`].value == "1"; //bottom
var pr = Sf[`P${p}R`].value == "1"; //right
var pv = Sf[`P${p}V`].value == "1"; //vertical
var ps = Sf[`P${p}S`].checked; //serpentine
var topLeftX = px*ppL + space; //left margin
var topLeftY = py*ppL + space; //top margin
ctx.lineWidth = 3;
ctx.strokeStyle="white";
ctx.strokeRect(topLeftX, topLeftY, pw*ppL, ph*ppL); // add space between panels
var lnX;
var lnY;
//find start led
if (pb) //bottom
lnY = topLeftY + ph*ppL - ppL/2;
else //top
lnY = topLeftY + ppL/2;
if (pr) //right
lnX = topLeftX + pw*ppL - ppL/2;
else //left
lnX = topLeftX + ppL/2;
//first led
ctx.fillStyle = "green";
ctx.beginPath();
ctx.arc(lnX, lnY, ppL*0.5, 0, 2 * Math.PI);
ctx.fill();
//start line
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(lnX, lnY);
var longLineLength = (pv?ph:pw)*ppL - ppL;
for (let ln=0; ln<(pv?pw:ph); ln++) { //loop over panelwidth (or height of vertical?)
var serpLine = ps && ln%2!=0; //serp: turn around if even line
if (pv) //if vertical
lnY += (pb?-1:1) * longLineLength * (serpLine?-1:1); //if vertical change the Y
else
lnX += (pr?-1:1) * longLineLength * (serpLine?-1:1); //if horizontal change the X
ctx.lineTo(lnX, lnY); //draw the long line
if (ln<(pv?pw:ph)-1) { //not the last
//find the small line end point
if (pv) //vertical
lnX += (pr?-1:1) * ppL;
else //horizontal
lnY += (pb?-1:1) * ppL;
//if serpentine go next else go down
if (ps) { //serpentine
ctx.lineTo(lnX, lnY); //draw the serpentine line
} else {
//find the other end of the long line
if (pv) //vertical
lnY += (pb?1:-1) * longLineLength * (serpLine?-1:1); //min as we go back
else //horizontal
lnX += (pr?1:-1) * longLineLength * (serpLine?-1:1);
ctx.moveTo(lnX, lnY); //move to the start point of the next long line
}
}
}
ctx.stroke();
//last led
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(lnX, lnY, ppL*0.5, 0, 2 * Math.PI);
ctx.fill();
ctx.font = '40px Arial';
ctx.fillStyle = "orange";
ctx.fillText(p, topLeftX + pw/2*ppL - 10, topLeftY + ph/2*ppL + 10);
}
gId("MD").innerHTML = "Matrix Dimensions (W*H=LC): " + maxWidth + " x " + maxHeight + " = " + maxWidth * maxHeight;
}
</script> </script>
<style>@import url("style.css");</style> <style>@import url("style.css");</style>
</head> </head>
@ -141,7 +308,7 @@ Y:<input id="P${i}Y" name="P${i}Y" type="number" min="0" max="256" value="0"><br
<form id="form_s" name="Sf" method="post"> <form id="form_s" name="Sf" method="post">
<div class="toprow"> <div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div> <div class="helpB"><button type="button" onclick="H()">?</button></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr> <button type="button" onclick="B()">Back</button><button type="button" onclick="fS()">Save</button><hr>
</div> </div>
<h2>2D setup</h2> <h2>2D setup</h2>
Strip or panel: Strip or panel:
@ -151,7 +318,8 @@ Y:<input id="P${i}Y" name="P${i}Y" type="number" min="0" max="256" value="0"><br
</select><br> </select><br>
<div id="mpdiv" style="display:none;"> <div id="mpdiv" style="display:none;">
<hr class="sml"> <hr class="sml">
<h3>Matrix Generator</h3> <h3>Matrix Generator <button type="button" id="expGen" onclick="expand(this,gId('mxGen'));">&gt;</button></h3>
<div id="mxGen" style="display:none;">
Panel dimensions (WxH): <input name="PW" type="number" min="1" max="128" value="8"> x <input name="PH" type="number" min="1" max="128" value="8"><br> Panel dimensions (WxH): <input name="PW" type="number" min="1" max="128" value="8"> x <input name="PH" type="number" min="1" max="128" value="8"><br>
Horizontal panels: <input name="MPH" type="number" min="1" max="8" value="1"> Horizontal panels: <input name="MPH" type="number" min="1" max="8" value="1">
Vertical panels: <input name="MPV" type="number" min="1" max="8" value="1"><br> Vertical panels: <input name="MPV" type="number" min="1" max="8" value="1"><br>
@ -167,20 +335,28 @@ Y:<input id="P${i}Y" name="P${i}Y" type="number" min="0" max="256" value="0"><br
<option value="1">Vertical</option> <option value="1">Vertical</option>
</select><br> </select><br>
Serpentine: <input type="checkbox" name="PS"><br> Serpentine: <input type="checkbox" name="PS"><br>
<i style="color:#fa0;">Can populate LED panel layout with pre-arranged matrix.<br>These values do not affect final layout.</i><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>
<button type="button" onclick="gen()">Populate</button> 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>
<hr class="sml"> <hr class="sml">
<h3>Panel set-up</h3> <h3>Panel set-up</h3>
Number of panels: <input name="MPC" type="number" min="1" max="64" value="1" oninput="addPanels()"><br> Number of panels: <input name="MPC" type="number" min="1" max="64" value="1" oninput="addPanels();UI();"><br>
<i>A matrix is made of 1 or more physical LED panels.<br> <i>A matrix is made of 1 or more physical LED panels.<br>
<!--Panels should be arranged from top-left to bottom-right order, starting with lower panel number on the left (or top if transposed).<br>-->
Each panel can be of different size and/or have different LED orientation and/or starting point and/or layout.</i><br> Each panel can be of different size and/or have different LED orientation and/or starting point and/or layout.</i><br>
<h3>LED panel layout</h3> <h3>LED panel layout</h3>
<div id="panels"> <div id="panels">
</div> </div>
<hr class="sml">
<div id="MD"></div>
<canvas id="canvas"></canvas>
<div id="json" >Gap file: <input type="file" name="data" accept=".json"><button type="button" class="sml" onclick="uploadFile('/2d-gaps.json')">Upload</button></div>
<i>Note: Gap file is a <b>.json</b> file containing an array with number of elements equal to the matrix size.<br>
A value of -1 means that pixel at that position is missing, a value of 0 means never paint that pixel, and 1 means regular pixel.</i>
</div> </div>
<hr> <hr>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button> <button type="button" onclick="B()">Back</button><button type="button" onclick="fS()">Save</button>
</form> </form>
<div id="toast"></div>
</body> </body>
</html> </html>

View File

@ -7,9 +7,9 @@
<title>DMX Settings</title> <title>DMX Settings</title>
<script> <script>
var d=document; 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 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) { function GCH(num) {
d.getElementById('dmxchannels').innerHTML += ""; d.getElementById('dmxchannels').innerHTML += "";
for (i=0;i<num;i++) { for (i=0;i<num;i++) {
@ -54,16 +54,29 @@
}); });
} }
function S(){ function S(){
if (window.location.protocol == "file:") { let l = window.location;
if (l.protocol == "file:") {
loc = true; loc = true;
locip = localStorage.getItem('locIp'); locip = localStorage.getItem('locIp');
if (!locip) { if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!"); 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];
} }
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> </script>
<style>@import url("style.css");</style> <style>@import url("style.css");</style>

View File

@ -7,14 +7,14 @@
<title>LED Settings</title> <title>LED Settings</title>
<script> <script>
var d=document,laprev=55,maxB=1,maxV=0,maxM=4000,maxPB=4096,maxL=1333,maxLbquot=0; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32 var d=document,laprev=55,maxB=1,maxV=0,maxM=4000,maxPB=4096,maxL=1333,maxLbquot=0; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32
var customStarts=false,startsDirty=[],maxCOOverrides=5;
var loc = false, locip;
d.um_p = []; d.um_p = [];
d.rsvd = []; d.rsvd = [];
d.ro_gpio = []; d.ro_gpio = [];
d.max_gpio = 39; d.max_gpio = 50;
var customStarts=false,startsDirty=[],maxCOOverrides=5;
var loc = false, locip, locproto = "http:";
function H(){window.open("https://kno.wled.ge/features/settings/#led-settings");} 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 gId(n){return d.getElementById(n);}
function off(n){d.getElementsByName(n)[0].value = -1;} function off(n){d.getElementsByName(n)[0].value = -1;}
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript // https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
@ -26,9 +26,12 @@
d.body.appendChild(scE); d.body.appendChild(scE);
// success event // success event
scE.addEventListener("load", () => { scE.addEventListener("load", () => {
//console.log("File loaded"); GetV();
GetV();checkSi();setABL(); checkSi();
setABL();
d.Sf.addEventListener("submit", trySubmit);
if (d.um_p[0]==-1) d.um_p.shift(); if (d.um_p[0]==-1) d.um_p.shift();
pinDropdowns();
}); });
// error event // error event
scE.addEventListener("error", (ev) => { scE.addEventListener("error", (ev) => {
@ -50,7 +53,7 @@
maxB = b; maxV = v; maxM = m; maxPB = p; maxL = l; maxB = b; maxV = v; maxM = m; maxPB = p; maxL = l;
} }
function pinsOK() { 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++) { for (i=0; i<LCs.length; i++) {
var nm = LCs[i].name.substring(0,2); var nm = LCs[i].name.substring(0,2);
// ignore IP address // ignore IP address
@ -60,23 +63,36 @@
if (t>=80) continue; if (t>=80) continue;
} }
//check for pin conflicts //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") { if (LCs[i].value!="" && LCs[i].value!="-1") {
var p = []; // used pin array var p = d.rsvd.concat(d.um_p); // used pin array
for (k=0;k<d.rsvd.length;k++) p.push(d.rsvd[k]); // fill with reservations d.Sf.querySelectorAll("select.pin").forEach((e)=>{if(e.value>-1)p.push(parseInt(e.value));}) // buttons, IR & relay
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))) {
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;} alert(`Sorry, pins ${JSON.stringify(p)} can't be used.`);
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;} LCs[i].value="";
for (j=i+1; j<LCs.length; j++) 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); 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") { if (n2.substring(0,1)==="L") {
var m = LCs[j].name.substring(2); var m = LCs[j].name.substring(2);
var t2 = parseInt(d.getElementsByName("LT"+m)[0].value, 10); var t2 = parseInt(d.getElementsByName("LT"+m)[0].value, 10);
if (t2>=80) continue; 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;
}
} }
} }
} }
@ -97,6 +113,7 @@
gId('abl').style.display = (en) ? 'inline':'none'; gId('abl').style.display = (en) ? 'inline':'none';
gId('psu2').style.display = (en) ? 'inline':'none'; gId('psu2').style.display = (en) ? 'inline':'none';
if (d.Sf.LA.value > 0) setABL(); if (d.Sf.LA.value > 0) setABL();
UI();
} }
function enLA() function enLA()
{ {
@ -118,32 +135,33 @@
default: gId('LAdis').style.display = 'inline'; default: gId('LAdis').style.display = 'inline';
} }
gId('m1').innerHTML = maxM; gId('m1').innerHTML = maxM;
d.getElementsByName("Sf")[0].addEventListener("submit", trySubmit);
UI();
} }
//returns mem usage //returns mem usage
function getMem(t, n) { function getMem(t, n) {
let len = parseInt(d.getElementsByName("LC"+n)[0].value); let len = parseInt(d.getElementsByName("LC"+n)[0].value);
len += parseInt(d.getElementsByName("SL"+n)[0].value); // skipped LEDs are allocated too 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 < 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 (maxM < 10000 && d.getElementsByName("L0"+n)[0].value == 3) { //8266 DMA uses 5x the mem
if (t > 29) return len*20; //RGBW if (t > 28) return len*20 + dbl; //RGBW
return len*15; return len*15 + dbl;
} else if (maxM >= 10000) //ESP32 RMT uses double buffer? } else if (maxM >= 10000) //ESP32 RMT uses double buffer?
{ {
if (t > 29) return len*8; //RGBW if (t > 28) return len*8 + dbl; //RGBW
return len*6; return len*6 + dbl;
} }
if (t > 29) return len*4; //RGBW if (t > 28) return len*4 + dbl; //RGBW
return len*3; return len*3 + dbl;
} }
if (t > 31 && t < 48) return 5; if (t > 31 && t < 48) return 5; // analog
if (t == 44 || t == 45) return len*4; //RGBW return len*3 + dbl;
return len*3;
} }
function UI(change=false) function UI(change=false)
{ {
var isRGBW = false, memu = 0; let isRGBW = false, gRGBW = false, memu = 0;
gId('ampwarning').style.display = (d.Sf.MA.value > 7200) ? 'inline':'none'; gId('ampwarning').style.display = (d.Sf.MA.value > 7200) ? 'inline':'none';
@ -151,15 +169,13 @@
else if (d.Sf.LA.value > 0) laprev = d.Sf.LA.value; else if (d.Sf.LA.value > 0) laprev = d.Sf.LA.value;
// enable/disable LED fields // enable/disable LED fields
var s = d.getElementsByTagName("select"); d.Sf.querySelectorAll("#mLC select[name^=LT]").forEach((s)=>{
for (i=0; i<s.length; i++) {
// is the field a LED type? // is the field a LED type?
if (s[i].name.substring(0,2)=="LT") { var n = s.name.substring(2);
var n = s[i].name.substring(2); var t = parseInt(s.value);
var t = parseInt(s[i].value,10);
gId("p0d"+n).innerHTML = (t>=80 && t<96) ? "IP address:" : (t > 49) ? "Data GPIO:" : (t > 41) ? "GPIOs:" : "GPIO:"; 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:" : ""; 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 memu += getMem(t, n); // calc memory
@ -184,27 +200,27 @@
if (t > 31 && t < 48) d.getElementsByName("LC"+n)[0].value = 1; // for sanity change analog count just to 1 LED 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 gId("rf"+n).onclick = (t == 31) ? (()=>{return false}) : (()=>{}); // prevent change for TM1814
isRGBW = (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("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 gId("dig"+n+"w").style.display = (t > 28 && t < 32) ? "inline":"none"; // show swap channels dropdown
if (!(t == 30 || t == 31)) d.getElementsByName("WO"+n)[0].value = 0; // reset swapping 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+"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+"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+"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+"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+"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("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 gId("psd"+n).innerHTML = (t >= 40 && t < 48) ? "Index:":"Start:"; // change analog start description
} });
} // display global white channel overrides
// display white channel calculation method gId("wc").style.display = (gRGBW) ? 'inline':'none';
var myC = d.querySelectorAll('.wc'), if (!gRGBW) {
l = myC.length; d.Sf.AW.selectedIndex = 0;
for (i = 0; i < l; i++) { d.Sf.CR.checked = false;
myC[i].style.display = (isRGBW) ? 'inline':'none';
} }
// check for pin conflicts // 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; var sLC = 0, sPC = 0, maxLC = 0;
for (i=0; i<LCs.length; i++) { for (i=0; i<LCs.length; i++) {
var nm = LCs[i].name.substring(0,2); // field name var nm = LCs[i].name.substring(0,2); // field name
@ -242,15 +258,14 @@
} }
} }
// check for pin conflicts // 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") { if (LCs[i].value!="" && LCs[i].value!="-1") {
var p = []; // used pin array var p = d.rsvd.concat(d.um_p); // used pin array
for (k=0;k<d.rsvd.length;k++) p.push(d.rsvd[k]); // fill with reservations d.Sf.querySelectorAll("select.pin").forEach((e)=>{if(e.value>-1)p.push(parseInt(e.value));}) // buttons, IR & relay
for (k=0;k<d.um_p.length;k++) p.push(d.um_p[k]); // fill with usermod pins
for (j=0; j<LCs.length; j++) { for (j=0; j<LCs.length; j++) {
if (i==j) continue; if (i==j) continue;
var n2 = LCs[j].name.substring(0,2); 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") { if (n2.substring(0,1)==="L") {
var m = LCs[j].name.substring(2); var m = LCs[j].name.substring(2);
var t2 = parseInt(d.getElementsByName("LT"+m)[0].value, 10); var t2 = parseInt(d.getElementsByName("LT"+m)[0].value, 10);
@ -260,13 +275,13 @@
} }
} }
// now check for conflicts // 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 // check buttons, IR & relay
if (nm=="IR" || nm=="BT" || nm=="RL") { //if (nm=="IR" || nm=="BT" || nm=="RL") {
LCs[i].max = d.max_gpio; // LCs[i].max = d.max_gpio;
LCs[i].min = -1; // LCs[i].min = -1;
} //}
} }
// update total led count // update total led count
gId("lc").textContent = sLC; gId("lc").textContent = sLC;
@ -330,11 +345,14 @@ ${i+1}:
<option value="31">TM1814</option>\ <option value="31">TM1814</option>\
<option value="24">400kHz</option>\ <option value="24">400kHz</option>\
<option value="25">TM1829</option>\ <option value="25">TM1829</option>\
<option value="26">UCS8903</option>\
<option value="29">UCS8904 RGBW</option>\
<option value="50">WS2801</option>\ <option value="50">WS2801</option>\
<option value="51">APA102</option>\ <option value="51">APA102</option>\
<option value="52">LPD8806</option>\ <option value="52">LPD8806</option>\
<option value="54">LPD6803</option>\ <option value="54">LPD6803</option>\
<option value="53">P9813</option>\ <option value="53">P9813</option>\
<option value="19">WS2811 White</option>\
<option value="40">On/Off</option>\ <option value="40">On/Off</option>\
<option value="41">PWM White</option>\ <option value="41">PWM White</option>\
<option value="42">PWM CCT</option>\ <option value="42">PWM CCT</option>\
@ -344,7 +362,7 @@ ${i+1}:
<!--option value="46">PWM RGB+DCCT</option-->'} <!--option value="46">PWM RGB+DCCT</option-->'}
<option value="80">DDP RGB (network)</option> <option value="80">DDP RGB (network)</option>
<!--option value="81">E1.31 RGB (network)</option--> <!--option value="81">E1.31 RGB (network)</option-->
<!--option value="82">ArtNet RGB (network)</option--> <option value="82">Art-Net RGB (network)</option>
<option value="88">DDP RGBW (network)</option> <option value="88">DDP RGBW (network)</option>
</select><br> </select><br>
<div id="co${i}" style="display:inline">Color Order: <div id="co${i}" style="display:inline">Color Order:
@ -357,19 +375,20 @@ ${i+1}:
<option value="5">GBR</option> <option value="5">GBR</option>
</select></div> </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}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> <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; <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 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> </div>
<span id="p0d${i}">GPIO:</span> <input type="number" name="L0${i}" min="0" max="48" required 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}" min="0" max="48" class="s" onchange="UI()"/> <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}" min="0" max="48" class="s" onchange="UI()"/> <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}" min="0" max="48" class="s" onchange="UI()"/> <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}" min="0" max="48" class="s" onchange="UI()"/> <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}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}s" style="display:inline"><br>Skip first LEDs: <input type="number" name="SL${i}" min="0" max="255" value="0" oninput="UI()"></div>
<div id="dig${i}f" style="display:inline"><br>Off Refresh: <input id="rf${i}" type="checkbox" name="RF${i}"></div> <div id="dig${i}f" style="display:inline"><br>Off Refresh: <input id="rf${i}" type="checkbox" name="RF${i}"></div>
<div id="dig${i}a" style="display:inline"><br>Auto-calculate white channel from RGB:<br><select name="AW${i}"><option value=0>None</option><option value=1>Brighter</option><option value=2>Accurate</option><option value=3>Dual</option></select>&nbsp;</div> <div id="dig${i}a" style="display:inline"><br>Auto-calculate white channel from RGB:<br><select name="AW${i}"><option value=0>None</option><option value=1>Brighter</option><option value=2>Accurate</option><option value=3>Dual</option><option value=4>Max</option></select>&nbsp;</div>
</div>`; </div>`;
f.insertAdjacentHTML("beforeend", cn); f.insertAdjacentHTML("beforeend", cn);
} }
@ -435,7 +454,7 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
var c = gId("btns").innerHTML; var c = gId("btns").innerHTML;
var bt = "BT" + String.fromCharCode((i<10?48:55)+i); var bt = "BT" + String.fromCharCode((i<10?48:55)+i);
var be = "BE" + String.fromCharCode((i<10?48:55)+i); var be = "BE" + String.fromCharCode((i<10?48:55)+i);
c += `Button ${i} GPIO: <input type="number" min="-1" max="48" name="${bt}" onchange="UI()" class="xs" value="${p}">`; c += `Button ${i} GPIO: <input type="number" name="${bt}" onchange="UI()" class="xs" value="${p}">`;
c += `&nbsp;<select name="${be}">` c += `&nbsp;<select name="${be}">`
c += `<option value="0" ${t==0?"selected":""}>Disabled</option>`; c += `<option value="0" ${t==0?"selected":""}>Disabled</option>`;
c += `<option value="2" ${t==2?"selected":""}>Pushbutton</option>`; c += `<option value="2" ${t==2?"selected":""}>Pushbutton</option>`;
@ -541,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() { function S() {
if (window.location.protocol == "file:") { let l = window.location;
if (l.protocol == "file:") {
loc = true; loc = true;
locip = localStorage.getItem('locIp'); locip = localStorage.getItem('locIp');
if (!locip) { if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!"); 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];
} }
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> </script>
<style>@import url("style.css");</style> <style>@import url("style.css");</style>
@ -571,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> Enable automatic brightness limiter: <input type="checkbox" name="ABen" onchange="enABL()" id="able"><br>
<div id="abl"> <div id="abl">
Maximum Current: <input name="MA" type="number" class="l" min="250" max="65000" oninput="UI()" required> mA<br> 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> &#9888; Your power supply provides high current.<br>
To improve the safety of your setup,<br> To improve the safety of your setup,<br>
please use thick cables,<br> please use thick cables,<br>
@ -599,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> <button type="button" id="-" onclick="addLEDs(-1,false)">-</button><br>
LED Memory Usage: <span id="m0">0</span> / <span id="m1">?</span> B<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="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> &#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> Use less than <span id="wreason">800 LEDs per output</span> for the best experience!<br>
</div> </div>
<hr class="sml"> <hr class="sml">
Make a segment for each output: <input type="checkbox" name="MS"><br> 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> 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"> <hr class="sml">
<div id="color_order_mapping"> <div id="color_order_mapping">
Color Order Override: Color Order Override:
@ -641,12 +769,14 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
Apply preset <input name="BP" type="number" class="m" min="0" max="250" required> at boot (0 uses defaults) Apply preset <input name="BP" type="number" class="m" min="0" max="250" required> at boot (0 uses defaults)
<br><br> <br><br>
Use Gamma correction for color: <input type="checkbox" name="GC"> (strongly recommended)<br> Use Gamma correction for color: <input type="checkbox" name="GC"> (strongly recommended)<br>
Use Gamma correction for brightness: <input type="checkbox" name="GB"> (not recommended)<br><br> Use Gamma correction for brightness: <input type="checkbox" name="GB"> (not recommended)<br>
Use Gamma value: <input name="GV" type="number" class="m" placeholder="2.8" min="1" max="3" step="0.1" required><br><br>
Brightness factor: <input name="BF" type="number" class="m" min="1" max="255" required> % Brightness factor: <input name="BF" type="number" class="m" min="1" max="255" required> %
<h3>Transitions</h3> <h3>Transitions</h3>
Crossfade: <input type="checkbox" name="TF"><br> Crossfade: <input type="checkbox" name="TF"><br>
Transition Time: <input name="TD" type="number" class="xl" min="0" max="65500"> ms<br> Transition Time: <input name="TD" type="number" class="xl" min="0" max="65500"> ms<br>
Enable Palette transitions: <input type="checkbox" name="PF"> Enable Palette transitions: <input type="checkbox" name="PF"><br>
<i>Random Cycle</i> Palette Time: <input name="TP" type="number" class="m" min="1" max="255"> s<br>
<h3>Timed light</h3> <h3>Timed light</h3>
Default Duration: <input name="TL" type="number" class="m" min="1" max="255" required> min<br> Default Duration: <input name="TL" type="number" class="m" min="1" max="255" required> min<br>
Default Target brightness: <input name="TB" type="number" class="m" min="0" max="255" required><br> Default Target brightness: <input name="TB" type="number" class="m" min="0" max="255" required><br>
@ -659,7 +789,7 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
</select> </select>
<h3>White management</h3> <h3>White management</h3>
White Balance correction: <input type="checkbox" name="CCT"><br> White Balance correction: <input type="checkbox" name="CCT"><br>
<span class="wc"> <div id="wc">
Global override for Auto-calculate white:<br> Global override for Auto-calculate white:<br>
<select name="AW"> <select name="AW">
<option value=255>Disabled</option> <option value=255>Disabled</option>
@ -667,10 +797,12 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
<option value=1>Brighter</option> <option value=1>Brighter</option>
<option value=2>Accurate</option> <option value=2>Accurate</option>
<option value=3>Dual</option> <option value=3>Dual</option>
<option value=4>Max</option>
</select> </select>
<br> <br>
Calculate CCT from RGB: <input type="checkbox" name="CR"><br> Calculate CCT from RGB: <input type="checkbox" name="CR"><br>
CCT additive blending: <input type="number" class="s" min="0" max="100" name="CB" required> %</span> CCT additive blending: <input type="number" class="s" min="0" max="100" name="CB" required> %
</div>
<h3>Advanced</h3> <h3>Advanced</h3>
Palette blending: Palette blending:
<select name="PB"> <select name="PB">

View File

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

View File

@ -7,10 +7,10 @@
<title>Misc Settings</title> <title>Misc Settings</title>
<script> <script>
var d = document; 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 H() { window.open("https://kno.wled.ge/features/settings/#security-settings"); }
function B() { window.open("/settings","_self"); } function B() { window.open(getURL("/settings"),"_self"); }
function U() { window.open("/update","_self"); } function U() { window.open(getURL("/update"),"_self"); }
function gId(s) { return d.getElementById(s); } function gId(s) { return d.getElementById(s); }
function isObj(o) { return (o && typeof o === 'object' && !Array.isArray(o)); } 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 // https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
@ -47,7 +47,7 @@
var req = new XMLHttpRequest(); var req = new XMLHttpRequest();
req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400)}); req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400)});
req.addEventListener('error', function(e){showToast(e.stack,true);}); req.addEventListener('error', function(e){showToast(e.stack,true);});
req.open("POST", "/upload"); req.open("POST", getURL("/upload"));
var formData = new FormData(); var formData = new FormData();
formData.append("data", fO.files[0], name); formData.append("data", fO.files[0], name);
req.send(formData); req.send(formData);
@ -65,16 +65,33 @@
x.setAttribute("download","wled_" + x.getAttribute("download") + (sd=="WLED"?"":("_" +sd))); x.setAttribute("download","wled_" + x.getAttribute("download") + (sd=="WLED"?"":("_" +sd)));
} }
function S() { function S() {
if (window.location.protocol == "file:") { let l = window.location;
if (l.protocol == "file:") {
loc = true; loc = true;
locip = localStorage.getItem('locIp'); locip = localStorage.getItem('locIp');
if (!locip) { if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!"); 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];
} }
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> </script>
<style> <style>
@ -89,7 +106,7 @@
</div> </div>
<h2>Security & Update setup</h2> <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> 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> Lock wireless (OTA) software update: <input type="checkbox" name="NO"><br>
Passphrase: <input type="password" name="OP" maxlength="32"><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> 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> Deny access to WiFi settings if locked: <input type="checkbox" name="OW"><br><br>
Factory reset: <input type="checkbox" name="RS"><br> Factory reset: <input type="checkbox" name="RS"><br>
All settings and presets will be erased.<br><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> <hr>
<h3>Software Update</h3> <h3>Software Update</h3>
<button type="button" onclick="U()">Manual OTA Update</button><br> <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> <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> <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>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> Incorrect configuration may require a factory reset or re-flashing of your ESP.</div>
For security reasons, passwords are not backed up. For security reasons, passwords are not backed up.
<hr> <hr>

View File

@ -6,11 +6,11 @@
<meta charset="utf-8"> <meta charset="utf-8">
<title>Sync Settings</title> <title>Sync Settings</title>
<script>var d=document; <script>var d=document;
var loc = false, locip; var loc = false, locip, locproto = "http:";
function gId(s){return d.getElementById(s);} function gId(s){return d.getElementById(s);}
function toggle(el){gId(el).classList.toggle("hide"); gId('No'+el).classList.toggle("hide");} 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 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;} 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;} } 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 // 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 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 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(){ function S(){
if (window.location.protocol == "file:") { let l = window.location;
if (l.protocol == "file:") {
loc = true; loc = true;
locip = localStorage.getItem('locIp'); locip = localStorage.getItem('locIp');
if (!locip) { if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!"); locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip); 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> </script>
<style>@import url("style.css");</style> <style>@import url("style.css");</style>
@ -149,6 +161,7 @@ Start universe: <input name="EU" type="number" min="0" max="63999" required><br>
Skip out-of-sequence packets: <input type="checkbox" name="ES"><br> Skip out-of-sequence packets: <input type="checkbox" name="ES"><br>
DMX start address: <input name="DA" type="number" min="1" max="510" required><br> DMX start address: <input name="DA" type="number" min="1" max="510" required><br>
DMX segment spacing: <input name="XX" type="number" min="0" max="150" required><br> DMX segment spacing: <input name="XX" type="number" min="0" max="150" required><br>
E1.31 port priority: <input name="PY" type="number" min="0" max="200" required><br>
DMX mode: DMX mode:
<select name=DM> <select name=DM>
<option value=0>Disabled</option> <option value=0>Disabled</option>
@ -171,7 +184,7 @@ Realtime LED offset: <input name="WO" type="number" min="-255" max="255" require
<hr class="sml"> <hr class="sml">
<h3>Alexa Voice Assistant</h3> <h3>Alexa Voice Assistant</h3>
<div id="NoAlexa" class="hide"> <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>
<div id="Alexa"> <div id="Alexa">
Emulate Alexa device: <input type="checkbox" name="AL"><br> Emulate Alexa device: <input type="checkbox" name="AL"><br>
@ -179,25 +192,15 @@ 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> Also emulate devices to call the first <input name="AP" type="number" class="s" min="0" max="9" required> presets<br><br>
</div> </div>
<hr class="sml"> <hr class="sml">
<div style="color: #fa0;">&#9888; <b>Blynk, 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> This may impact the responsiveness of WLED.</b><br>
</div> </div>
For best results, only use one of these services at a time.<br> For best results, only use one of these services at a time.<br>
(alternatively, connect a second ESP to them and use the UDP sync) (alternatively, connect a second ESP to them and use the UDP sync)
<hr class="sml"> <hr class="sml">
<h3>Blynk</h3>
<div id="NoBlynk" class="hide">
<em style="color:#fa0;">This firmware build does not include Blynk support.<br></em>
</div>
<div id="Blynk">
Host: <input type="text" name="BH" maxlength="32">
Port: <input name="BP" type="number" min="1" max="65535" value="80" class="d5"><br>
Device Auth token: <input name="BK" maxlength="33"><br>
<i>Clear the token field to disable. </i><a href="https://kno.wled.ge/interfaces/blynk/" target="_blank">Setup info</a>
</div>
<h3>MQTT</h3> <h3>MQTT</h3>
<div id="NoMQTT" class="hide"> <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>
<div id="MQTT"> <div id="MQTT">
Enable MQTT: <input type="checkbox" name="MQ"><br> Enable MQTT: <input type="checkbox" name="MQ"><br>
@ -211,11 +214,12 @@ Client ID: <input type="text" name="MQCID" maxlength="40"><br>
Device Topic: <input type="text" name="MD" maxlength="32"><br> Device Topic: <input type="text" name="MD" maxlength="32"><br>
Group Topic: <input type="text" name="MG" maxlength="32"><br> Group Topic: <input type="text" name="MG" maxlength="32"><br>
Publish on button press: <input type="checkbox" name="BM"><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> <i>Reboot required to apply changes. </i><a href="https://kno.wled.ge/interfaces/mqtt/" target="_blank">MQTT info</a>
</div> </div>
<h3>Philips Hue</h3> <h3>Philips Hue</h3>
<div id="NoHue" class="hide"> <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>
<div id="Hue"> <div id="Hue">
<i>You can find the bridge IP and the light number in the 'About' section of the hue app.</i><br> <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> <title>Time Settings</title>
<script> <script>
var d=document; var d=document;
var loc = false, locip; var loc = false, locip, locproto = "http:";
var el=false; var el=false;
var ms=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; 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 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 gId(s) { return d.getElementById(s); }
function gN(s) { return d.getElementsByName(s)[0]; } function gN(s) { return d.getElementsByName(s)[0]; }
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript // https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
@ -33,16 +33,29 @@
}); });
} }
function S() { function S() {
if (window.location.protocol == "file:") { let l = window.location;
if (l.protocol == "file:") {
loc = true; loc = true;
locip = localStorage.getItem('locIp'); locip = localStorage.getItem('locIp');
if (!locip) { if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!"); 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];
} }
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) function expand(o,i)
{ {
@ -181,7 +194,7 @@
<option value="18">HST (Hawaii)</option> <option value="18">HST (Hawaii)</option>
<option value="19">NOVT (Novosibirsk)</option> <option value="19">NOVT (Novosibirsk)</option>
<option value="20">AKST/AKDT (Anchorage)</option> <option value="20">AKST/AKDT (Anchorage)</option>
<option value="21">MX-CST/CDT</option> <option value="21">MX-CST</option>
<option value="22">PKT (Pakistan)</option> <option value="22">PKT (Pakistan)</option>
</select><br> </select><br>
UTC offset: <input name="UO" type="number" min="-65500" max="65500" required> seconds (max. 18 hours)<br> UTC offset: <input name="UO" type="number" min="-65500" max="65500" required> seconds (max. 18 hours)<br>

View File

@ -7,7 +7,7 @@
<title>UI Settings</title> <title>UI Settings</title>
<script> <script>
var d = document; var d = document;
var loc = false, locip; var loc = false, locip, locproto = "http:";
var initial_ds, initial_st, initial_su; var initial_ds, initial_st, initial_su;
var sett = null; var sett = null;
var l = { var l = {
@ -26,7 +26,8 @@
"segpwr": "Hide segment power &amp; brightness", "segpwr": "Hide segment power &amp; brightness",
"segexp" : "Always expand first segment", "segexp" : "Always expand first segment",
"css": "Enable custom CSS", "css": "Enable custom CSS",
"hdays": "Enable custom Holidays list" "hdays": "Enable custom Holidays list",
"fxdef": "Use effect default parameters"
}, },
"theme":{ "theme":{
"alpha": { "alpha": {
@ -185,21 +186,33 @@
alert("Loading of configuration script failed.\nIncomplete page data!"); alert("Loading of configuration script failed.\nIncomplete page data!");
}); });
} }
function S() function S() {
{ let l = window.location;
if (window.location.protocol == "file:") { if (l.protocol == "file:") {
loc = true; loc = true;
locip = localStorage.getItem('locIp'); locip = localStorage.getItem('locIp');
if (!locip) { if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!"); 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];
} }
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 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() function UI()
{ {
gId('idonthateyou').style.display = (gId('dm').checked) ? 'inline':'none'; gId('idonthateyou').style.display = (gId('dm').checked) ? 'inline':'none';
@ -249,7 +262,7 @@
Server description: <input type="text" name="DS" maxlength="32"><br> Server description: <input type="text" name="DS" maxlength="32"><br>
Sync button toggles both send and receive: <input type="checkbox" name="ST"><br> Sync button toggles both send and receive: <input type="checkbox" name="ST"><br>
<div id="NoSimple" class="hide"> <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>
<div id="Simple">Enable simplified UI: <input type="checkbox" name="SU"><br></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> <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> <title>Usermod Settings</title>
<script> <script>
var d = document; var d = document;
d.max_gpio = 39; d.max_gpio = 50;
d.um_p = []; d.um_p = [];
d.rsvd = []; d.rsvd = [];
d.ro_gpio = []; d.ro_gpio = [];
d.extra = [];
var umCfg = {}; var umCfg = {};
var pins = [], pinO = [], owner; var pins = [], pinO = [], owner;
var loc = false, locip; var loc = false, locip, locproto = "http:";
var urows; var urows;
var numM = 0; var numM = 0;
function gId(s) { return d.getElementById(s); } function gId(s) { return d.getElementById(s); }
function isO(i) { return (i && typeof i === 'object' && !Array.isArray(i)); } 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 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 // https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) { function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script"); let scE = d.createElement("script");
@ -30,15 +31,11 @@
// success event // success event
scE.addEventListener("load", () => { scE.addEventListener("load", () => {
GetV(); GetV();
for (let k=0; k<d.rsvd.length; k++) { pins.push(d.rsvd[k]); pinO.push("rsvd"); } for (let r of d.rsvd) { pins.push(r); pinO.push("rsvd"); } // reserved pins
if (d.um_p[0]==-1) d.um_p.shift(); if (d.um_p[0]==-1) d.um_p.shift(); // remove filler
d.Sf.SDA.max = d.max_gpio; d.Sf.SDA.max = d.Sf.SCL.max = d.Sf.MOSI.max = d.Sf.SCLK.max = d.Sf.MISO.max = d.max_gpio;
d.Sf.SCL.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;
d.Sf.MOSI.max = d.max_gpio; pinDropdowns(); // convert INPUT to SELECT for pins
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;
}); });
// error event // error event
scE.addEventListener("error", (ev) => { scE.addEventListener("error", (ev) => {
@ -47,21 +44,36 @@
}); });
} }
function S() { function S() {
if (window.location.protocol == "file:") { let l = window.location;
if (l.protocol == "file:") {
loc = true; loc = true;
locip = localStorage.getItem('locIp'); locip = localStorage.getItem('locIp');
if (!locip) { if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!"); 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];
}
} }
ldS(); ldS();
if (!numM) gId("um").innerHTML = "No Usermods installed."; 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 // 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 isF(n) { return n === +n && n !== (n|0); }
function isI(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 function check(o,k) { // input object, pin owner key
/* no longer necessary with pin dropdown fields
var n = o.name.replace("[]","").substr(-3); var n = o.name.replace("[]","").substr(-3);
if (o.type=="number" && n.substr(0,3)=="pin") { if (o.type=="number" && n.substr(0,3)=="pin") {
for (var i=0; i<pins.length; i++) { 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"; 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) { function getPins(o) {
if (isO(o)) { if (isO(o)) {
for (const [k,v] of Object.entries(o)) { for (const [k,v] of Object.entries(o)) {
if (isO(v)) { if (isO(v)) {
let oldO = owner; // keep parent name
owner = k; owner = k;
getPins(v); getPins(v);
owner = oldO;
continue; continue;
} }
if (k.replace("[]","").substr(-3)=="pin") { 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>`; 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 // https://stackoverflow.com/questions/39729741/javascript-change-input-text-to-select-option
function addDropdown(um,fld) { function addDropdown(um,fld) {
let sel = d.createElement('select'); let sel = d.createElement('select');
let arr = d.getElementsByName(um+":"+fld); if (typeof(fld) === "string") { // parameter from usermod (field name)
let inp = arr[1]; // assume 1st field to be hidden (type) 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 if (inp && inp.tagName === "INPUT" && (inp.type === "text" || inp.type === "number")) { // may also use nodeName
let v = inp.value; let v = inp.value;
let n = inp.name; let n = inp.name;
@ -162,11 +242,12 @@
var att = inp.attributes[i]; var att = inp.attributes[i];
// type and value don't apply, so skip them // type and value don't apply, so skip them
// ** you might also want to skip style, or others -- modify as needed ** // ** 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(att.name, att.value);
} }
} }
sel.setAttribute("data-val", v); sel.setAttribute("data-val", v);
sel.setAttribute("onchange", "UI(this)");
// finally, replace the old input element with the new select element // finally, replace the old input element with the new select element
inp.parentElement.replaceChild(sel, inp); inp.parentElement.replaceChild(sel, inp);
return sel; return sel;
@ -183,6 +264,7 @@
let c = sel.childNodes[i]; let c = sel.childNodes[i];
if (c.value == sel.dataset.val) sel.selectedIndex = 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 // https://stackoverflow.com/questions/26440494/insert-text-after-this-input-element-with-javascript
function addInfo(name,el,txt, txt2="") { function addInfo(name,el,txt, txt2="") {
@ -194,10 +276,13 @@
if (txt2!="") obj[el].insertAdjacentHTML('beforebegin', txt2 + '&nbsp;'); //add pre texts 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 // load settings and insert values into DOM
function ldS() { function ldS() {
var url = (loc?`http://${locip}`:'') + '/cfg.json'; fetch(getURL('/cfg.json'), {
fetch(url, {
method: 'get' method: 'get'
}) })
.then(res => { .then(res => {
@ -216,8 +301,7 @@
} }
if (urows==="") urows = "Usermods configuration not found.<br>Press <i>Save</i> to initialize defaults."; if (urows==="") urows = "Usermods configuration not found.<br>Press <i>Save</i> to initialize defaults.";
gId("um").innerHTML = urows; gId("um").innerHTML = urows;
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=8'; loadJS(getURL('/settings/s.js?p=8'), false); // If we set async false, file is loaded and executed, then next statement is processed
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
}) })
.catch((error)=>{ .catch((error)=>{
gId('lserr').style.display = "inline"; gId('lserr').style.display = "inline";
@ -242,15 +326,17 @@
</div> </div>
<h2>Usermod Setup</h2> <h2>Usermod Setup</h2>
Global I<sup>2</sup>C GPIOs (HW)<br> 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"> 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"> SCL:<input type="number" min="-1" max="48" name="SCL" onchange="check(this,'if')" class="s" placeholder="SCL">
<hr class="sml"> <hr class="sml">
Global SPI GPIOs (HW)<br> 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"> 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"> 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"> 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> <div id="um">Loading settings...</div>
<hr><button type="button" onclick="B()">Back</button><button type="submit">Save</button> <hr><button type="button" onclick="B()">Back</button><button type="submit">Save</button>
</form> </form>

View File

@ -7,21 +7,19 @@
<title>WiFi Settings</title> <title>WiFi Settings</title>
<script> <script>
var d = document; var d = document;
var loc = false, locip; var loc = false, locip, locproto = "http:";
var scanLoops = 0, preScanSSID = ""; var scanLoops = 0, preScanSSID = "";
function gId(e) { return d.getElementById(e); } function gId(e) { return d.getElementById(e); }
function cE(e) { return d.createElement(e); } function cE(e) { return d.createElement(e); }
function H(){window.open("https://kno.wled.ge/features/settings/#wifi-settings");} 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() { function N() {
const url = (loc?`http://${locip}`:"") + "/json/net";
const button = gId("scan"); const button = gId("scan");
button.disabled = true; button.disabled = true;
button.innerHTML = "Scanning..."; button.textContent = "Scanning...";
fetch(url).then((response) => { fetch(getURL("/json/net")).then((response) => {
return response.json(); return response.json();
}).then((json) => { }).then((json) => {
// Get the list of networks only, defaulting to an empty array. // Get the list of networks only, defaulting to an empty array.
@ -70,7 +68,7 @@
const option = cE("option"); const option = cE("option");
option.setAttribute("value", networks[i].ssid); 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) { if (networks[i].ssid === cs.value) {
option.setAttribute("selected", "selected"); option.setAttribute("selected", "selected");
@ -81,14 +79,14 @@
const option = cE("option"); const option = cE("option");
option.setAttribute("value", "!Cs"); option.setAttribute("value", "!Cs");
option.innerHTML = `Other network...`; option.textContent = `Other network...`;
select.appendChild(option); select.appendChild(option);
cs.replaceWith(select); cs.replaceWith(select);
} }
button.disabled = false; button.disabled = false;
button.innerHTML = "Scan"; button.textContent = "Scan";
}); });
} }
// replace WiFi select with custom SSID input field again // replace WiFi select with custom SSID input field again
@ -122,16 +120,29 @@
}); });
} }
function S() { function S() {
if (window.location.protocol == "file:") { let l = window.location;
if (l.protocol == "file:") {
loc = true; loc = true;
locip = localStorage.getItem("locIp"); locip = localStorage.getItem('locIp');
if (!locip) { if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!"); 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(getURL('/settings/s.js?p=1'), false); // If we set async false, file is loaded and executed, then next statement is processed
loadJS(url, 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> </script>
<style>@import url("style.css");</style> <style>@import url("style.css");</style>
@ -182,15 +193,28 @@
Disable WiFi sleep: <input type="checkbox" name="WS"><br> Disable WiFi sleep: <input type="checkbox" name="WS"><br>
<i>Can help with connectivity issues.<br> <i>Can help with connectivity issues.<br>
Do not enable if WiFi is working correctly, increases power consumption.</i> 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"> <div id="ethd">
<h3>Ethernet Type</h3> <h3>Ethernet Type</h3>
<select name="ETH"> <select name="ETH">
<option value="0">None</option> <option value="0">None</option>
<option value="9">ABC! WLED V43 & compatible</option>
<option value="2">ESP32-POE</option> <option value="2">ESP32-POE</option>
<option value="6">ESP32Deux</option> <option value="6">ESP32Deux</option>
<option value="7">KIT-VE</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="4">QuinLED-ESP32</option>
<option value="10">Serg74-ETH32</option>
<option value="5">TwilightLord-ESP32</option> <option value="5">TwilightLord-ESP32</option>
<option value="3">WESP32</option> <option value="3">WESP32</option>
<option value="1">WT32-ETH01</option> <option value="1">WT32-ETH01</option>

View File

@ -127,19 +127,19 @@
<p class="label h">RGB channels</p> <p class="label h">RGB channels</p>
<div id="rwrap" class="il"> <div id="rwrap" class="il">
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderR" onchange="fromRgb()" max="255" min="0" type="range" value="128" /> <input id="sliderR" class="noslide" onchange="fromRgb()" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
</div><br> </div><br>
<div id="gwrap" class="il"> <div id="gwrap" class="il">
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderG" onchange="fromRgb()" max="255" min="0" type="range" value="128" /> <input id="sliderG" class="noslide" onchange="fromRgb()" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
</div><br> </div><br>
<div id="bwrap" class="il"> <div id="bwrap" class="il">
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderB" onchange="fromRgb()" max="255" min="0" type="range" value="128" /> <input id="sliderB" class="noslide" onchange="fromRgb()" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
</div><br> </div><br>
@ -147,14 +147,14 @@
<div id="wwrap" class="center"> <div id="wwrap" class="center">
<p class="label h">White channel</p> <p class="label h">White channel</p>
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderW" onchange="setColor(0)" max="255" min="0" type="range" value="128" /> <input id="sliderW" class="noslide" onchange="setColor(0)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
</div> </div>
<div id="wbal"> <div id="wbal">
<p class="label h">White balance</p> <p class="label h">White balance</p>
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderA" onchange="setBalance(this.value)" max="255" min="0" type="range" value="128" /> <input id="sliderA" class="noslide" onchange="setBalance(this.value)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
</div> </div>

View File

@ -1,5 +1,5 @@
//page js //page js
var loc = false, locip; var loc = false, locip, locproto = "http:";
var noNewSegs = false; var noNewSegs = false;
var isOn = false, isInfo = false, isNodes = false, isRgbw = false, cct = false; var isOn = false, isInfo = false, isNodes = false, isRgbw = false, cct = false;
var whites = [0,0,0]; var whites = [0,0,0];
@ -148,22 +148,39 @@ function loadSkinCSS(cId)
l.id = cId; l.id = cId;
l.rel = 'stylesheet'; l.rel = 'stylesheet';
l.type = 'text/css'; l.type = 'text/css';
l.href = (loc?`http://${locip}`:'.') + '/skin.css'; l.href = getURL('/skin.css');
l.media = 'all'; l.media = 'all';
h.appendChild(l); h.appendChild(l);
} }
} }
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
async function onLoad() async function onLoad()
{ {
if (window.location.protocol == "file:") { let l = window.location;
if (l.protocol == "file:") {
loc = true; loc = true;
locip = localStorage.getItem('locIp'); locip = localStorage.getItem('locIp');
if (!locip) if (!locip) {
{
locip = prompt("File Mode. Please enter WLED IP!"); locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip); 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'); var sett = localStorage.getItem('wledUiCfg');
if (sett) cfg = mergeDeep(cfg, JSON.parse(sett)); if (sett) cfg = mergeDeep(cfg, JSON.parse(sett));
@ -173,7 +190,7 @@ async function onLoad()
applyCfg(); applyCfg();
if (cfg.theme.bg.url=="" || cfg.theme.bg.url === "https://picsum.photos/1920/1080") { if (cfg.theme.bg.url=="" || cfg.theme.bg.url === "https://picsum.photos/1920/1080") {
var iUrl = cfg.theme.bg.url; var iUrl = cfg.theme.bg.url;
fetch((loc?`http://${locip}`:'.') + "/holidays.json", { fetch(getURL("/holidays.json"), {
method: 'get' method: 'get'
}) })
.then((res)=>{ .then((res)=>{
@ -330,9 +347,7 @@ function loadPresets(callback = null)
pmtLast = pmt; pmtLast = pmt;
var url = (loc?`http://${locip}`:'') + '/presets.json'; fetch(getURL('/presets.json'), {
fetch(url, {
method: 'get' method: 'get'
}) })
.then(res => { .then(res => {
@ -355,9 +370,7 @@ function loadPresets(callback = null)
function loadPalettes(callback = null) function loadPalettes(callback = null)
{ {
var url = (loc?`http://${locip}`:'') + '/json/palettes'; fetch(getURL('/json/palettes'), {
fetch(url, {
method: 'get' method: 'get'
}) })
.then(res => { .then(res => {
@ -379,9 +392,7 @@ function loadPalettes(callback = null)
function loadFX(callback = null) function loadFX(callback = null)
{ {
var url = (loc?`http://${locip}`:'') + '/json/effects'; fetch(getURL('/json/effects'), {
fetch(url, {
method: 'get' method: 'get'
}) })
.then(res => { .then(res => {
@ -403,9 +414,7 @@ function loadFX(callback = null)
function loadFXData(callback = null) function loadFXData(callback = null)
{ {
var url = (loc?`http://${locip}`:'') + '/json/fxdata'; fetch(getURL('/json/fxdata'), {
fetch(url, {
method: 'get' method: 'get'
}) })
.then(res => { .then(res => {
@ -551,7 +560,7 @@ function populateSegments(s)
<div class="il"> <div class="il">
<i class="icons slider-icon pwr ${powered[i] ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})" title="${inst.n}">&#xe08f;</i> <i class="icons slider-icon pwr ${powered[i] ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})" title="${inst.n}">&#xe08f;</i>
<div id="sliderSeg${i}Bri" class="sliderwrap il"> <div id="sliderSeg${i}Bri" 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}" /> <input id="seg${i}bri" onchange="setSegBri(${i})" oninput="updateTrail(this)" max="255" min="1" type="range" value="${inst.bri}" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
<output class="sliderbubble"></output> <output class="sliderbubble"></output>
@ -611,8 +620,7 @@ function populateNodes(i,n)
function loadNodes() function loadNodes()
{ {
var url = (loc?`http://${locip}`:'') + '/json/nodes'; fetch(getURL('/json/nodes'), {
fetch(url, {
method: 'get' method: 'get'
}) })
.then(res => { .then(res => {
@ -855,7 +863,8 @@ function cmpP(a, b)
function makeWS() { function makeWS() {
if (ws) return; 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)=>{ ws.onmessage = (e)=>{
var json = JSON.parse(e.data); var json = JSON.parse(e.data);
if (json.leds) return; //liveview packet 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 (command && !reqsLegal) return; //stop post requests from chrome onchange event on page restore
if (!jsonTimeout) jsonTimeout = setTimeout(showErrorToast, 3000); if (!jsonTimeout) jsonTimeout = setTimeout(showErrorToast, 3000);
var req = null; var req = null;
var url = (loc?`http://${locip}`:'') + '/json/si';
var useWs = (ws && ws.readyState === WebSocket.OPEN); var useWs = (ws && ws.readyState === WebSocket.OPEN);
var type = command ? 'post':'get'; var type = command ? 'post':'get';
if (command) { if (command) {
@ -991,7 +999,7 @@ function requestJson(command=null)
setTimeout(requestJson,200); setTimeout(requestJson,200);
} }
fetch(url, { fetch(getURL('/json/si'), {
method: type, method: type,
headers: { headers: {
"Content-type": "application/json; charset=UTF-8" "Content-type": "application/json; charset=UTF-8"
@ -1314,9 +1322,7 @@ function loadPalettesData(callback = null)
function getPalettesData(page, callback) function getPalettesData(page, callback)
{ {
var url = (loc?`http://${locip}`:'') + `/json/palx?page=${page}`; fetch(getURL(`/json/palx?page=${page}`), {
fetch(url, {
method: 'get', method: 'get',
headers: { headers: {
"Content-type": "application/json; charset=UTF-8" "Content-type": "application/json; charset=UTF-8"
@ -1379,7 +1385,7 @@ function hasIroClass(classList)
} }
return false; return false;
} }
//required by rangetouch.js
function lock(e) function lock(e)
{ {
var l = e.target.classList; var l = e.target.classList;
@ -1392,7 +1398,7 @@ function lock(e)
_C.classList.toggle('smooth', !(locked = true)); _C.classList.toggle('smooth', !(locked = true));
} }
//required by rangetouch.js
function move(e) function move(e)
{ {
if(!locked) return; if(!locked) return;

View File

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

View File

@ -15,7 +15,7 @@
<body onload="GetV()"> <body onload="GetV()">
<h2>WLED Software Update</h2> <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> Installed version: <span class="sip">##VERSION##</span><br>
Download the latest binary: <a href="https://github.com/Aircoookie/WLED/releases" target="_blank"> 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> <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> <h3>Thank you for installing my application!</h3>
<b>Next steps:</b><br><br> <b>Next steps:</b><br><br>
Connect the module to your local WiFi here!<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> <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> </div>
</body> </body>
</html> </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; uint32_t start = htonl(p->channelOffset) / ddpChannelsPerLed;
start += DMXAddress / ddpChannelsPerLed; start += DMXAddress / ddpChannelsPerLed;
@ -70,10 +70,20 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
seq = p->art_sequence_number; seq = p->art_sequence_number;
mde = REALTIME_MODE_ARTNET; mde = REALTIME_MODE_ARTNET;
} else if (protocol == P_E131) { } else if (protocol == P_E131) {
uni = htons(p->universe); // Ignore PREVIEW data (E1.31: 6.2.6)
if ((p->options & 0x80) != 0) return;
dmxChannels = htons(p->property_value_count) - 1; dmxChannels = htons(p->property_value_count) - 1;
// DMX level data is zero start code. Ignore everything else. (E1.11: 8.5)
if (dmxChannels == 0 || p->property_values[0] != 0) return;
uni = htons(p->universe);
e131_data = p->property_values; e131_data = p->property_values;
seq = p->sequence_number; seq = p->sequence_number;
if (e131Priority != 0) {
if (p->priority < e131Priority ) return;
// track highest priority & skip all lower priorities
if (p->priority >= highPriority.get()) highPriority.set(p->priority);
if (p->priority < highPriority.get()) return;
}
} else { //DDP } else { //DDP
realtimeIP = clientIP; realtimeIP = clientIP;
handleDDPPacket(p); handleDDPPacket(p);
@ -163,14 +173,30 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
break; break;
case DMX_MODE_PRESET: // 2 channel: [Dimmer,Preset] case DMX_MODE_PRESET: // 2 channel: [Dimmer,Preset]
{
if (uni != e131Universe || availDMXLen < 2) return; 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]) { if (bri != e131_data[dataOffset]) {
bri = e131_data[dataOffset]; bri = e131_data[dataOffset];
strip.setBrightness(bri, true); strip.setBrightness(scaledBri(bri), false);
stateUpdated(CALL_MODE_WS_SEND);
} }
return; return;
break; 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: // 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] case DMX_MODE_EFFECT_W: // 18 channels, same as above but with extra +3 white channels [..,W,W2,W3]

View File

@ -13,13 +13,6 @@ void handleAlexa();
void onAlexaChange(EspalexaDevice* dev); void onAlexaChange(EspalexaDevice* dev);
#endif #endif
//blynk.cpp
#ifndef WLED_DISABLE_BLYNK
void initBlynk(const char* auth, const char* host, uint16_t port);
void handleBlynk();
void updateBlynk();
#endif
//button.cpp //button.cpp
void shortPressAction(uint8_t b=0); void shortPressAction(uint8_t b=0);
void longPressAction(uint8_t b=0); void longPressAction(uint8_t b=0);
@ -57,6 +50,18 @@ bool getJsonValue(const JsonVariant& element, DestType& destination, const Defau
//colors.cpp //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_blend(uint32_t,uint32_t,uint16_t,bool b16=false);
uint32_t color_add(uint32_t,uint32_t); 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]))); } inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); }
@ -70,10 +75,6 @@ bool colorFromHexString(byte* rgb, const char* in);
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb); uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
uint16_t approximateKelvinFromRGB(uint32_t rgb); uint16_t approximateKelvinFromRGB(uint32_t rgb);
void setRandomColor(byte* 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 //dmx.cpp
void initDMX(); void initDMX();
@ -103,10 +104,20 @@ void sendHuePoll();
void onHueData(void* arg, AsyncClient* client, void *data, size_t len); void onHueData(void* arg, AsyncClient* client, void *data, size_t len);
//improv.cpp //improv.cpp
enum ImprovRPCType {
Command_Wifi = 0x01,
Request_State = 0x02,
Request_Info = 0x03,
Request_Scan = 0x04
};
void handleImprovPacket(); void handleImprovPacket();
void sendImprovRPCResult(ImprovRPCType type, uint8_t n_strings = 0, const char **strings = nullptr);
void sendImprovStateResponse(uint8_t state, bool error = false); void sendImprovStateResponse(uint8_t state, bool error = false);
void sendImprovInfoResponse(); void sendImprovInfoResponse();
void sendImprovRPCResponse(uint8_t commandId); void startImprovWifiScan();
void handleImprovWifiScan();
void sendImprovIPRPCResult(ImprovRPCType type);
//ir.cpp //ir.cpp
void applyRepeatActions(); void applyRepeatActions();
@ -131,13 +142,13 @@ void handleIR();
#include "src/dependencies/json/AsyncJson-v6.h" #include "src/dependencies/json/AsyncJson-v6.h"
#include "FX.h" #include "FX.h"
void deserializeSegment(JsonObject elem, byte it, byte presetId = 0); bool deserializeSegment(JsonObject elem, byte it, byte presetId = 0);
bool deserializeState(JsonObject root, byte callMode = CALL_MODE_DIRECT_CHANGE, byte presetId = 0); bool deserializeState(JsonObject root, byte callMode = CALL_MODE_DIRECT_CHANGE, byte presetId = 0);
void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true); 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 serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true, bool selectedSegmentsOnly = false);
void serializeInfo(JsonObject root); void serializeInfo(JsonObject root);
void serializeModeNames(JsonArray arr, const char *qstring); void serializeModeNames(JsonArray root);
void serializeModeData(JsonObject root); void serializeModeData(JsonArray root);
void serveJson(AsyncWebServerRequest* request); void serveJson(AsyncWebServerRequest* request);
#ifdef WLED_ENABLE_JSONLIVE #ifdef WLED_ENABLE_JSONLIVE
bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient = 0); bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient = 0);
@ -159,9 +170,11 @@ void handleTransitions();
void handleNightlight(); void handleNightlight();
byte scaledBri(byte in); byte scaledBri(byte in);
#ifdef WLED_ENABLE_LOXONE
//lx_parser.cpp //lx_parser.cpp
bool parseLx(int lxValue, byte* rgbw); bool parseLx(int lxValue, byte* rgbw);
void parseLxJson(int lxValue, byte segId, bool secondary); void parseLxJson(int lxValue, byte segId, bool secondary);
#endif
//mqtt.cpp //mqtt.cpp
bool initMqtt(); bool initMqtt();
@ -197,12 +210,16 @@ void serializePlaylist(JsonObject obj);
void initPresetsFile(); void initPresetsFile();
void handlePresets(); void handlePresets();
bool applyPreset(byte index, byte callMode = CALL_MODE_DIRECT_CHANGE); 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);}; inline bool applyTemporaryPreset() {return applyPreset(255);};
void savePreset(byte index, const char* pname = nullptr, JsonObject saveobj = JsonObject()); void savePreset(byte index, const char* pname = nullptr, JsonObject saveobj = JsonObject());
inline void saveTemporaryPreset() {savePreset(255);}; inline void saveTemporaryPreset() {savePreset(255);};
void deletePreset(byte index); void deletePreset(byte index);
bool getPresetName(byte index, String& name); bool getPresetName(byte index, String& name);
//remote.cpp
void handleRemote();
//set.cpp //set.cpp
bool isAsterisksOnly(const char* str, byte maxLen); bool isAsterisksOnly(const char* str, byte maxLen);
void handleSettingsSet(AsyncWebServerRequest *request, byte subPage); void handleSettingsSet(AsyncWebServerRequest *request, byte subPage);
@ -210,7 +227,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply=tru
//udp.cpp //udp.cpp
void notify(byte callMode, bool followUp=false); 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 realtimeLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC);
void exitRealtime(); void exitRealtime();
void handleNotifications(); void handleNotifications();
@ -264,19 +281,20 @@ class Usermod {
virtual ~Usermod() { if (um_data) delete um_data; } virtual ~Usermod() { if (um_data) delete um_data; }
virtual void setup() = 0; // pure virtual, has to be overriden virtual void setup() = 0; // pure virtual, has to be overriden
virtual void loop() = 0; // pure virtual, has to be overriden virtual void loop() = 0; // pure virtual, has to be overriden
virtual void handleOverlayDraw() {} virtual void handleOverlayDraw() {} // called after all effects have been processed, just before strip.show()
virtual bool handleButton(uint8_t b) { return false; } virtual bool handleButton(uint8_t b) { return false; } // button overrides are possible here
virtual bool getUMData(um_data_t **data) { if (data) *data = nullptr; return false; }; virtual bool getUMData(um_data_t **data) { if (data) *data = nullptr; return false; }; // usermod data exchange [see examples for audio effects]
virtual void connected() {} virtual void connected() {} // called when WiFi is (re)connected
virtual void appendConfigData() {} virtual void appendConfigData() {} // helper function called from usermod settings page to add metadata for entry fields
virtual void addToJsonState(JsonObject& obj) {} virtual void addToJsonState(JsonObject& obj) {} // add JSON objects for WLED state
virtual void addToJsonInfo(JsonObject& obj) {} virtual void addToJsonInfo(JsonObject& obj) {} // add JSON objects for UI Info page
virtual void readFromJsonState(JsonObject& obj) {} virtual void readFromJsonState(JsonObject& obj) {} // process JSON messages received from web server
virtual void addToConfig(JsonObject& obj) {} virtual void addToConfig(JsonObject& obj) {} // add JSON entries that go to cfg.json
virtual bool readFromConfig(JsonObject& obj) { return true; } // Note as of 2021-06 readFromConfig() now needs to return a bool, see usermod_v2_example.h virtual bool readFromConfig(JsonObject& obj) { return true; } // Note as of 2021-06 readFromConfig() now needs to return a bool, see usermod_v2_example.h
virtual void onMqttConnect(bool sessionPresent) {} virtual void onMqttConnect(bool sessionPresent) {} // fired when MQTT connection is established (so usermod can subscribe)
virtual bool onMqttMessage(char* topic, char* payload) { return false; } virtual bool onMqttMessage(char* topic, char* payload) { return false; } // fired upon MQTT message received (wled topic)
virtual void onUpdateBegin(bool) {} virtual void onUpdateBegin(bool) {} // fired prior to and after unsuccessful firmware update
virtual void onStateChange(uint8_t mode) {} // fired upon WLED state change
virtual uint16_t getId() {return USERMOD_ID_UNSPECIFIED;} virtual uint16_t getId() {return USERMOD_ID_UNSPECIFIED;}
}; };
@ -301,6 +319,7 @@ class UsermodManager {
void onMqttConnect(bool sessionPresent); void onMqttConnect(bool sessionPresent);
bool onMqttMessage(char* topic, char* payload); bool onMqttMessage(char* topic, char* payload);
void onUpdateBegin(bool); void onUpdateBegin(bool);
void onStateChange(uint8_t);
bool add(Usermod* um); bool add(Usermod* um);
Usermod* lookup(uint16_t mod_id); Usermod* lookup(uint16_t mod_id);
byte getModCount() {return numMods;}; byte getModCount() {return numMods;};
@ -330,6 +349,7 @@ void releaseJSONBufferLock();
uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen); 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); 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); 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); uint16_t crc16(const unsigned char* data_p, size_t length);
um_data_t* simulateSound(uint8_t simulationId); um_data_t* simulateSound(uint8_t simulationId);
void enumerateLedmaps(); void enumerateLedmaps();
@ -377,7 +397,6 @@ void serveIndexOrWelcome(AsyncWebServerRequest *request);
void serveIndex(AsyncWebServerRequest* request); void serveIndex(AsyncWebServerRequest* request);
String msgProcessor(const String& var); String msgProcessor(const String& var);
void serveMessage(AsyncWebServerRequest* request, uint16_t code, const String& headl, const String& subl="", byte optionT=255); 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); String dmxProcessor(const String& var);
void serveSettings(AsyncWebServerRequest* request, bool post = false); void serveSettings(AsyncWebServerRequest* request, bool post = false);
void serveSettingsJS(AsyncWebServerRequest* request); 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 // 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 // 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 //wrapper to find out how long closing takes
void closeFile() { void closeFile() {
@ -44,7 +45,7 @@ void closeFile() {
//find() that reads and buffers data from file stream in 256-byte blocks. //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 //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 #ifdef WLED_DEBUG_FS
DEBUGFS_PRINT("Find "); DEBUGFS_PRINT("Find ");
DEBUGFS_PRINTLN(target); DEBUGFS_PRINTLN(target);
@ -59,8 +60,8 @@ bool bufferedFind(const char *target, bool fromStart = true) {
if (fromStart) f.seek(0); if (fromStart) f.seek(0);
while (f.position() < f.size() -1) { while (f.position() < f.size() -1) {
uint16_t bufsize = f.read(buf, FS_BUFSIZE); size_t bufsize = f.read(buf, FS_BUFSIZE); // better to use size_t instead if uint16_t
uint16_t count = 0; size_t count = 0;
while (count < bufsize) { while (count < bufsize) {
if(buf[count] != target[index]) if(buf[count] != target[index])
index = 0; // reset index if any char does not match 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. //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 #ifdef WLED_DEBUG_FS
DEBUGFS_PRINTF("Find %d spaces\n", targetLen); 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; 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]; byte buf[FS_BUFSIZE];
if (fromStart) f.seek(0); if (fromStart) f.seek(0);
while (f.position() < f.size() -1) { while (f.position() < f.size() -1) {
uint16_t bufsize = f.read(buf, FS_BUFSIZE); size_t bufsize = f.read(buf, FS_BUFSIZE);
uint16_t count = 0; size_t count = 0;
while (count < bufsize) { while (count < bufsize) {
if(buf[count] == ' ') { if(buf[count] == ' ') {
if(++index >= targetLen) { // return true if space long enough if(++index >= targetLen) { // return true if space long enough
if (fromStart) { if (fromStart) {
f.seek((f.position() - bufsize) + count +1 - targetLen); 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); DEBUGFS_PRINTF("Found at pos %d, took %d ms", f.position(), millis() - s);
return true; return true;
@ -116,7 +117,7 @@ bool bufferedFindSpace(uint16_t targetLen, bool fromStart = true) {
} else { } else {
if (!fromStart) return false; if (!fromStart) return false;
if (index) { 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 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 //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 #ifdef WLED_DEBUG_FS
DEBUGFS_PRINTLN(F("Find obj end")); DEBUGFS_PRINTLN(F("Find obj end"));
uint32_t s = millis(); uint32_t s = millis();
@ -142,8 +143,8 @@ bool bufferedFindObjectEnd() {
byte buf[FS_BUFSIZE]; byte buf[FS_BUFSIZE];
while (f.position() < f.size() -1) { while (f.position() < f.size() -1) {
uint16_t bufsize = f.read(buf, FS_BUFSIZE); size_t bufsize = f.read(buf, FS_BUFSIZE); // better to use size_t instead of uint16_t
uint16_t count = 0; size_t count = 0;
while (count < bufsize) { while (count < bufsize) {
if (buf[count] == '{') objDepth++; if (buf[count] == '{') objDepth++;
@ -161,13 +162,13 @@ bool bufferedFindObjectEnd() {
} }
//fills n bytes from current file pos with ' ' characters //fills n bytes from current file pos with ' ' characters
void writeSpace(uint16_t l) static void writeSpace(size_t l)
{ {
byte buf[FS_BUFSIZE]; byte buf[FS_BUFSIZE];
memset(buf, ' ', FS_BUFSIZE); memset(buf, ' ', FS_BUFSIZE);
while (l > 0) { 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); f.write(buf, block);
l -= block; l -= block;
} }
@ -270,7 +271,7 @@ bool writeObjectToFile(const char* file, const char* key, JsonDocument* content)
s = millis(); s = millis();
#endif #endif
uint32_t pos = 0; size_t pos = 0;
f = WLED_FS.open(file, "r+"); f = WLED_FS.open(file, "r+");
if (!f && !WLED_FS.exists(file)) f = WLED_FS.open(file, "w+"); if (!f && !WLED_FS.exists(file)) f = WLED_FS.open(file, "w+");
if (!f) { if (!f) {
@ -287,7 +288,7 @@ bool writeObjectToFile(const char* file, const char* key, JsonDocument* content)
pos = f.position(); pos = f.position();
//measure out end of old object //measure out end of old object
bufferedFindObjectEnd(); bufferedFindObjectEnd();
uint32_t pos2 = f.position(); size_t pos2 = f.position();
uint32_t oldLen = pos2 - pos; uint32_t oldLen = pos2 - pos;
DEBUGFS_PRINTF("Old obj len %d\n", oldLen); 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 //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 //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 (!content->isNull()) contentLen = measureJson(*content);
if (contentLen && contentLen <= oldLen) { //replace and fill diff with spaces if (contentLen && contentLen <= oldLen) { //replace and fill diff with spaces
@ -375,7 +376,7 @@ void updateFSInfo() {
//Un-comment any file types you need //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"; if(request->hasArg("download")) return "application/octet-stream";
else if(filename.endsWith(".htm")) return "text/html"; else if(filename.endsWith(".htm")) return "text/html";
else if(filename.endsWith(".html")) 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){ bool handleFileRead(AsyncWebServerRequest* request, String path){
DEBUG_PRINTLN("FileRead: " + path); DEBUG_PRINTLN("WS FileRead: " + path);
if(path.endsWith("/")) path += "index.htm"; if(path.endsWith("/")) path += "index.htm";
if(path.indexOf("sec") > -1) return false; if(path.indexOf("sec") > -1) return false;
String contentType = getContentType(request, path); 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
};

File diff suppressed because it is too large Load Diff

535
wled00/html_pixart.h Normal file
View File

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

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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