Merge pull request #1355 from Aircoookie/filesystem

The wait is almost over
This commit is contained in:
Aircoookie 2020-11-20 01:06:54 +01:00 committed by GitHub
commit 7875a96137
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 4902 additions and 2791 deletions

3
.gitignore vendored
View File

@ -1,4 +1,5 @@
.pio
.cache
.pioenvs
.piolibdeps
.vscode
@ -6,6 +7,8 @@
/wled00/Release
/wled00/extLibs
/platformio_override.ini
/wled00/my_config.h
/build_output
.DS_Store
.gitignore
.clang-format

5
.gitpod.Dockerfile vendored Normal file
View File

@ -0,0 +1,5 @@
FROM gitpod/workspace-full
USER gitpod
RUN pip3 install -U platformio

12
.gitpod.yml Normal file
View File

@ -0,0 +1,12 @@
tasks:
- command: platformio run
image:
file: .gitpod.Dockerfile
vscode:
extensions:
- ms-vscode.cpptools@0.26.3:u3GsZ5PK12Ddr79vh4TWgQ==
- eamodio.gitlens@10.2.1:e0IYyp0efFqVsrZwsIe8CA==
- Atishay-Jain.All-Autocomplete@0.0.23:fbZNfSpnd8XkAHGfAPS2rA==
- 2gua.rainbow-brackets@0.0.6:Tbu8dTz0i+/bgcKQTQ5b8g==

View File

@ -2,6 +2,61 @@
### Development versions after the 0.10.2 release
#### Build 2011200
- Added HEX color receiving to JSON API with `"col":["RRGGBBWW"]` format
- Moved Kelvin color receiving in JSON API from `"col":[[val]]` to `"col":[val]` format
_Notice:_ This is technically a breaking change. Since no release was made since the introduction and the Kelvin property was not previously documented in the wiki,
impact should be minimal.
- BTNPIN can now be disabled by setting to -1 (fixes #1237)
#### Build 2011180
- Platformio.ini updates and streamlining (PR #1266)
- my_config.h custom compile settings system (not yet used for much, adapted from PR #1266)
- Added Hawaii timezone (HST)
- Linebreak after 5 quick select buttons
#### Build 2011154
- Fixed RGBW saved incorrectly
- Fixed pmt caching requesting /presets.json too often
- Fixed deEEP not copying the first segment of EEPROM preset 16
#### Build 2011153
- Fixed an ESP32 end-of-file issue
- Fixed useRGBW not read from cfg.json
#### Build 2011152
- Version bump to 0.11.0p "Mirai"
- Increased max. num of segments to 12 (ESP8266) / 16 (ESP32)
- Up to 250 presets stored in the `presets.json` file in filesystem
- Complete overhaul of the Presets UI tab
- Updated iro.js to v5 (fixes black color wheel)
- Added white temperature slider to color wheel
- Add JSON settings serialization/deserialization to cfg.json and wsec.json
- Added deEEP to convert the EEPROM settings and presets to files
- Playlist support - JSON only for now
- New v2 usermod methods `addToConfig()` and `readFromConfig()` (see EXAMPLE_v2 for doc)
- Added Ethernet support for ESP32 (PR #1316)
- IP addresses are now handled by the `Network` class
- New `esp32_poe` PIO environment
- Use EspAsyncWebserver Aircoookie fork v.2.0.0 (hiding wsec.json)
- Removed `WLED_DISABLE_FILESYSTEM` and `WLED_ENABLE_FS_SERVING` defines as they are now required
- Added pin manager
- UI performance improvements (no drop shadows)
- More explanatory error messages in UI
- Improved candle brightness
- Return remaining nightlight time `nl.rem` in JSON API (PR #1302)
- UI sends timestamp with every command, allowing for timed presets without using NTP
- Added gamma calculation (yet unused)
- Added LED type definitions to const.h (yet unused)
- Added nicer 404 page
- Removed `NP` and `MS=` macro HTTP API commands
- Removed macros from Time settings
#### Build 2011120
- Added the ability for the /api MQTT topic to receive JSON API payloads
@ -43,6 +98,7 @@
- Added Loxone parser (PR #1185)
- Added support for kelvin input via `K=` HTTP and `"col":[[val]]` JSON API calls
_Notice:_ `"col":[[val]]` removed in build 2011200, use `"col":[val]`
- Added supplementary UDP socket (#1205)
- TMP2.net receivable by default
- UDP sockets accept HTTP and JSON API commands

View File

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

23
pio/gzip-firmware.py Normal file
View File

@ -0,0 +1,23 @@
Import('env')
import os
import shutil
import gzip
OUTPUT_DIR = "build_output{}".format(os.path.sep)
def bin_gzip(source, target, env):
variant = str(target[0]).split(os.path.sep)[2]
# create string with location and file names based on variant
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
gzip_file = "{}firmware{}{}.bin.gz".format(OUTPUT_DIR, os.path.sep, variant)
# check if new target files exist and remove if necessary
if os.path.isfile(gzip_file): os.remove(gzip_file)
# write gzip firmware file
with open(bin_file,"rb") as fp:
with gzip.open(gzip_file, "wb", compresslevel = 9) as f:
shutil.copyfileobj(fp, f)
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_gzip])

34
pio/name-firmware.py Normal file
View File

@ -0,0 +1,34 @@
Import('env')
import os
import shutil
OUTPUT_DIR = "build_output{}".format(os.path.sep)
def bin_rename_copy(source, target, env):
variant = str(target[0]).split(os.path.sep)[2]
# check if output directories exist and create if necessary
if not os.path.isdir(OUTPUT_DIR):
os.mkdir(OUTPUT_DIR)
for d in ['firmware', 'map']:
if not os.path.isdir("{}{}".format(OUTPUT_DIR, d)):
os.mkdir("{}{}".format(OUTPUT_DIR, d))
# create string with location and file names based on variant
map_file = "{}map{}{}.map".format(OUTPUT_DIR, os.path.sep, variant)
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
# check if new target files exist and remove if necessary
for f in [map_file, bin_file]:
if os.path.isfile(f):
os.remove(f)
# copy firmware.bin to firmware/<variant>.bin
shutil.copy(str(target[0]), bin_file)
# copy firmware.map to map/<variant>.map
if os.path.isfile("firmware.map"):
shutil.move("firmware.map", map_file)
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_rename_copy])

9
pio/obj-dump.py Normal file
View File

@ -0,0 +1,9 @@
# Little convenience script to get an object dump
Import('env')
def obj_dump_after_elf(source, target, env):
print("Create firmware.asm")
env.Execute("xtensa-lx106-elf-objdump "+ "-D " + str(target[0]) + " > "+ "${PROGNAME}.asm")
env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", [obj_dump_after_elf])

15
pio/strip-floats.py Normal file
View File

@ -0,0 +1,15 @@
Import('env')
#
# Dump build environment (for debug)
#print env.Dump()
#
flags = " ".join(env['LINKFLAGS'])
flags = flags.replace("-u _printf_float", "")
flags = flags.replace("-u _scanf_float", "")
newflags = flags.split()
env.Replace(
LINKFLAGS=newflags
)

9
pio/user_config_copy.py Normal file
View File

@ -0,0 +1,9 @@
Import('env')
import os
import shutil
# copy WLED00/my_config_sample.h to WLED00/my_config.h
if os.path.isfile("wled00/my_config.h"):
print ("*** use existing my_config.h ***")
else:
shutil.copy("wled00/my_config_sample.h", "wled00/my_config.h")

View File

@ -2,20 +2,13 @@
; Please visit documentation: https://docs.platformio.org/page/projectconf.html
[platformio]
src_dir = ./wled00
data_dir = ./wled00/data
lib_dir = ./wled00/src
build_cache_dir = ~/.buildcache
extra_configs =
platformio_override.ini
# ------------------------------------------------------------------------------
# ENVIRONMENTS
#
# Please uncomment one of the lines below to select your board(s)
# ------------------------------------------------------------------------------
# Travis CI binaries
# Travis CI binaries (comment this out with a ';' when building for your own board)
default_envs = travis_esp8266, travis_esp32
# Release binaries
@ -41,36 +34,21 @@ default_envs = travis_esp8266, travis_esp32
; default_envs = m5atom
; default_envs = esp32_poe
src_dir = ./wled00
data_dir = ./wled00/data
build_cache_dir = ~/.buildcache
extra_configs =
platformio_override.ini
[common]
# ------------------------------------------------------------------------------
# PLATFORM:
# !! DO NOT confuse platformio's ESP8266 development platform with Arduino core for ESP8266
#
# arduino core 2.3.0 = platformIO 1.5.0
# arduino core 2.4.0 = platformIO 1.6.0
# arduino core 2.4.1 = platformIO 1.7.3
# arduino core 2.4.2 = platformIO 1.8.0
# arduino core 2.5.0 = platformIO 2.0.4
# arduino core 2.5.1 = platformIO 2.1.1
# arduino core 2.5.2 = platformIO 2.2.3
# arduino core 2.6.1 = platformIO 2.3.0
# arduino core 2.6.2 = platformIO 2.3.1
# arduino core 2.6.3 = platformIO 2.3.2
# arduino core 2.7.0 = platformIO 2.5.0
# ------------------------------------------------------------------------------
arduino_core_2_3_0 = espressif8266@1.5.0
arduino_core_2_4_0 = espressif8266@1.6.0
arduino_core_2_4_1 = espressif8266@1.7.3
arduino_core_2_4_2 = espressif8266@1.8.0
arduino_core_2_5_0 = espressif8266@2.0.4
arduino_core_2_5_1 = espressif8266@2.1.1
arduino_core_2_5_2 = espressif8266@2.2.3
arduino_core_2_6_1 = espressif8266@2.3.0
arduino_core_2_6_2 = espressif8266@2.3.1
arduino_core_2_6_3 = espressif8266@2.3.3
arduino_core_2_7_1 = espressif8266@2.5.1
arduino_core_2_7_2 = espressif8266@2.6.0
arduino_core_2_7_3 = espressif8266@2.6.1
arduino_core_2_7_4 = espressif8266@2.6.2
# Development platforms
@ -79,8 +57,8 @@ arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/
# Platform to use for ESP8266
platform_wled_default = ${common.arduino_core_2_7_4}
# We use 2.7.0+ on analog boards because of PWM flicker fix
platform_latest = ${common.arduino_core_2_7_4}
# We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization
platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
# ------------------------------------------------------------------------------
# FLAGS: DEBUG
@ -91,7 +69,7 @@ debug_flags = -D DEBUG=1 -D WLED_DEBUG -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT
#-DDEBUG_ESP_CORE is not working right now
# ------------------------------------------------------------------------------
# FLAGS: ldscript
# FLAGS: ldscript (available ldscripts at https://github.com/esp8266/Arduino/tree/master/tools/sdk/ld)
# ldscript_512k ( 512 KB) = 487 KB sketch, 4 KB eeprom, no spiffs, 16 KB reserved
# ldscript_1m0m (1024 KB) = 999 KB sketch, 4 KB eeprom, no spiffs, 16 KB reserved
# ldscript_2m1m (2048 KB) = 1019 KB sketch, 4 KB eeprom, 1004 KB spiffs, 16 KB reserved
@ -102,6 +80,7 @@ debug_flags = -D DEBUG=1 -D WLED_DEBUG -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT
# -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH = v1.4 Higher Bandwidth (default)
# -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY = v2 Lower Memory
# -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH = v2 Higher Bandwidth
# -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
#
# BearSSL performance:
# When building with -DSECURE_CLIENT=SECURE_CLIENT_BEARSSL, please add `board_build.f_cpu = 160000000` to the environment configuration
@ -114,18 +93,35 @@ debug_flags = -D DEBUG=1 -D WLED_DEBUG -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT
# TLS_RSA_WITH_AES_256_CBC_SHA / AES256-SHA
# This reduces the OTA size with ~45KB, so it's especially useful on low memory boards (512k/1m).
# ------------------------------------------------------------------------------
build_flags = -g -w -DMQTT_MAX_PACKET_SIZE=1024 -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
-DSECURE_CLIENT=SECURE_CLIENT_BEARSSL -DBEARSSL_SSL_BASIC
#build_flags for the IRremoteESP8266 library (enabled decoders have to appear here)
build_flags =
-Wno-switch
-Wno-deprecated-declarations
-Wno-write-strings
-Wno-unused-variable
-Wno-unused-value
-Wno-sign-compare
-Wno-unused-but-set-variable
-Wno-return-type
-Wno-sequence-point
-Wno-narrowing
-Wno-reorder
-DMQTT_MAX_PACKET_SIZE=1024
-DSECURE_CLIENT=SECURE_CLIENT_BEARSSL
-DBEARSSL_SSL_BASIC
-D CORE_DEBUG_LEVEL=0
-D NDEBUG
#build_flags for the IRremoteESP8266 library (enabled decoders have to appear here)
-D _IR_ENABLE_DEFAULT_=false
-D DECODE_HASH=true
-D DECODE_NEC=true
-D DECODE_SONY=true
-D DECODE_SAMSUNG=true
-D DECODE_LG=true
build_flags_esp8266 = ${common.build_flags} -DESP8266
build_flags_esp32 = ${common.build_flags} -DARDUINO_ARCH_ESP32
-DWLED_USE_MY_CONFIG
build_unflags =
-Wall
-Wdeprecated-declarations
# enables all features for travis CI
build_flags_all_features =
@ -137,13 +133,39 @@ build_flags_all_features =
-D WLED_ENABLE_MQTT
-D WLED_ENABLE_WEBSOCKETS
build_flags_esp8266 = ${common.build_flags} ${esp8266.build_flags}
build_flags_esp32 = ${common.build_flags} ${esp32.build_flags}
ldscript_512k = eagle.flash.512k.ld ;for older versions change this to eagle.flash.512k0.ld
ldscript_1m0m = eagle.flash.1m.ld ;for older versions change this to eagle.flash.1m0.ld
ldscript_1m128k = eagle.flash.1m128.ld
ldscript_2m512k = eagle.flash.2m512.ld
ldscript_2m1m = eagle.flash.2m1m.ld
ldscript_4m1m = eagle.flash.4m1m.ld
ldscript_4m3m = eagle.flash.4m3m.ld
shared_libdeps_dir = ./wled00/src
[esp8266]
build_flags =
-DESP8266
-DFP_IN_IROM
; NONOSDK22x_190703 = 2.2.2-dev(38a443e)
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
; lwIP 2 - Higher Bandwidth no Features
-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
; VTABLES in Flash
-DVTABLES_IN_FLASH
; restrict to minimal mime-types
-DMIMETYPE_MINIMAL
[esp32]
build_flags = -w -g
-DARDUINO_ARCH_ESP32
-DCONFIG_LITTLEFS_FOR_IDF_3_2
[scripts_defaults]
extra_scripts = pio/name-firmware.py
pio/gzip-firmware.py
pio/strip-floats.py
pio/user_config_copy.py
# ------------------------------------------------------------------------------
# COMMON SETTINGS:
@ -153,8 +175,6 @@ framework = arduino
board_build.flash_mode = dout
monitor_speed = 115200
upload_speed = 115200
lib_extra_dirs =
${common.shared_libdeps_dir}
# ------------------------------------------------------------------------------
# LIBRARIES: required dependencies
@ -166,24 +186,27 @@ lib_extra_dirs =
lib_compat_mode = strict
lib_deps =
FastLED@3.3.2
#NeoPixelBus@2.5.7
https://github.com/Makuna/NeoPixelBus
NeoPixelBus@2.6.0
ESPAsyncTCP@1.2.0
ESPAsyncUDP
AsyncTCP@1.0.3
https://github.com/Aircoookie/ESPAsyncWebServer
IRremoteESP8266@2.7.3
https://github.com/lorol/LITTLEFS.git
https://github.com/Aircoookie/ESPAsyncWebServer.git@~2.0.0
#For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line
#TFT_eSPI
#For use SSD1306 OLED display uncomment following
#U8g2@~2.27.2
#For Dallas sensor uncomment following 2 lines
#OneWire@~2.3.5
#milesburton/DallasTemperature@^3.9.0
#For BME280 sensor uncomment following
#BME280@~3.0.0
lib_ignore =
AsyncTCP
extra_scripts = ${scripts_defaults.extra_scripts}
# ------------------------------------------------------------------------------
# WLED BUILDS
# ------------------------------------------------------------------------------
@ -191,14 +214,18 @@ lib_ignore =
[env:nodemcuv2]
board = nodemcuv2
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
# Unsupported environment due to insufficient flash
[env:esp01]
board = esp01
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK
-D WLED_DISABLE_CRONIXIE -D WLED_DISABLE_HUESYNC -D WLED_DISABLE_INFRARED -D WLED_DISABLE_MQTT -D WLED_DISABLE_WEBSOCKETS
@ -206,44 +233,58 @@ build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA -D WLED_DISABLE_
[env:esp01_1m_ota]
board = esp01_1m
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m0m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK -D WLED_DISABLE_CRONIXIE
-D WLED_DISABLE_HUESYNC -D WLED_DISABLE_INFRARED -D WLED_DISABLE_MQTT -D WLED_DISABLE_WEBSOCKETS
[env:esp01_1m_full]
board = esp01_1m
platform = ${common.platform_wled_default}
board_build.ldscript = ${common.ldscript_1m0m}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
[env:esp07]
board = esp07
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
[env:d1_mini]
board = d1_mini
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
upload_speed = 921600
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
monitor_filters = esp8266_exception_decoder
[env:heltec_wifi_kit_8]
board = d1_mini
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
[env:h803wf]
board = d1_mini
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D LEDPIN=1 -D WLED_DISABLE_INFRARED
[env:esp32dev]
board = esp32dev
platform = espressif32@1.12.4
platform = espressif32@2.0
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32}
lib_ignore =
ESPAsyncTCP
@ -251,35 +292,44 @@ lib_ignore =
[env:esp32_poe]
board = esp32-poe
platform = espressif32@1.12.4
platform = espressif32@2.0
upload_speed = 921600
build_flags = ${common.build_flags_esp32} ${common.debug_flags} -D RLYPIN=-1 -D WLED_USE_ETHERNET
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D RLYPIN=-1 -D WLED_USE_ETHERNET
lib_ignore =
ESPAsyncTCP
ESPAsyncUDP
[env:esp8285_4CH_MagicHome]
board = esp8285
platform = ${common.platform_latest}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m0m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_HUESYNC -D WLED_USE_ANALOG_LEDS
[env:esp8285_4CH_H801]
board = esp8285
platform = ${common.platform_latest}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m0m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_HUESYNC -D WLED_USE_ANALOG_LEDS -D WLED_USE_H801
[env:esp8285_5CH_H801]
board = esp8285
platform = ${common.platform_latest}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m0m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_HUESYNC -D WLED_USE_ANALOG_LEDS -D WLED_USE_H801 -D WLED_ENABLE_5CH_LEDS
[env:d1_mini_5CH_Shojo_PCB]
board = d1_mini
platform = ${common.platform_latest}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_USE_ANALOG_LEDS -D WLED_USE_SHOJO_PCB -D WLED_ENABLE_5CH_LEDS
# ------------------------------------------------------------------------------
@ -290,7 +340,9 @@ build_flags = ${common.build_flags_esp8266} -D WLED_USE_ANALOG_LEDS -D WLED_USE_
board = d1_mini
build_type = debug
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} ${common.debug_flags}
[env:d1_mini_ota]
@ -299,7 +351,9 @@ upload_protocol = espota
# exchange for your WLED IP
upload_port = "10.10.1.27"
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
# ------------------------------------------------------------------------------
@ -308,38 +362,49 @@ build_flags = ${common.build_flags_esp8266}
[env:custom_LEDPIN_4]
board = d1_mini
platform = ${common.platform_latest}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D LEDPIN=4 -D IRPIN=5
[env:custom_LEDPIN_16]
board = d1_mini
platform = ${common.platform_latest}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D LEDPIN=16
[env:custom_LEDPIN_3]
board = d1_mini
platform = ${common.platform_latest}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D LEDPIN=3
[env:custom_APA102]
board = d1_mini
platform = ${common.platform_latest}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D USE_APA102
[env:custom_WS2801]
board = d1_mini
platform = ${common.platform_latest}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D USE_WS2801
[env:custom32_LEDPIN_16]
board = esp32dev
platform = espressif32@1.12.4
platform = espressif32@2.0
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D LEDPIN=16
lib_ignore =
ESPAsyncTCP
@ -347,7 +412,8 @@ lib_ignore =
[env:custom32_TOUCHPIN_T0]
board = esp32dev
platform = espressif32@1.12.4
platform = espressif32@2.0
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D TOUCHPIN=T0
lib_ignore =
ESPAsyncTCP
@ -355,10 +421,11 @@ lib_ignore =
[env:wemos_shield_esp32]
board = esp32dev
platform = espressif32@1.12.4
platform = espressif32@2.0
upload_port = /dev/cu.SLAB_USBtoUART
monitor_port = /dev/cu.SLAB_USBtoUART
upload_speed = 460800
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D LEDPIN=16 -D RLYPIN=19 -D BTNPIN=17
lib_ignore =
ESPAsyncTCP
@ -366,11 +433,12 @@ lib_ignore =
[env:m5atom]
board = esp32dev
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D LEDPIN=27 -D BTNPIN=39
lib_ignore =
ESPAsyncTCP
ESPAsyncUDP
platform = espressif32@1.12.4
platform = espressif32@2.0
[env:sp501e]
board = esp_wroom_02
@ -385,11 +453,13 @@ build_flags = ${common.build_flags_esp8266} -D LEDPIN=3 -D BTNPIN=1
[env:travis_esp8266]
extends = env:d1_mini
build_type = debug
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} ${common.debug_flags} ${common.build_flags_all_features}
[env:travis_esp32]
extends = env:esp32dev
build_type = debug
; build_type = debug
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} ${common.debug_flags} ${common.build_flags_all_features}
# ------------------------------------------------------------------------------
@ -399,35 +469,47 @@ build_flags = ${common.build_flags_esp32} ${common.debug_flags} ${common.build_f
[env:codm-controller-0.4]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D LEDPIN=3
[env:codm-controller-0.4-WS2801]
board = esp_wroom_02
platform = ${common.platform_latest}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D USE_WS2801 -D CLKPIN=13 -D DATAPIN=3
[env:codm-controller-0.4-APA102]
board = esp_wroom_02
platform = ${common.platform_latest}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D USE_APA102 -D CLKPIN=13 -D DATAPIN=3
[env:codm-controller-0.5]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
[env:codm-controller-0.5-WS2801]
board = esp_wroom_02
platform = ${common.platform_latest}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D USE_WS2801 #-D CLKPIN=0 -D DATAPIN=2
[env:codm-controller-0.5-APA102]
board = esp_wroom_02
platform = ${common.platform_latest}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D USE_APA102 #-D CLKPIN=0 -D DATAPIN=2

View File

@ -5,19 +5,26 @@
# Please visit documentation: https://docs.platformio.org/page/projectconf.html
[platformio]
default_envs = esp8266_1m_custom
default_envs = WLED_tasmota_1M
[env:esp8266_1m_custom]
[env:WLED_tasmota_1M]
board = esp01_1m
platform = ${common.arduino_core_2_4_2}
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m0m}
build_flags = ${common.build_flags_esp8266}
-D WLED_DISABLE_OTA
-D WLED_DISABLE_ALEXA
-D WLED_DISABLE_BLYNK
-D WLED_DISABLE_CRONIXIE
-D WLED_DISABLE_HUESYNC
-D WLED_DISABLE_INFRARED
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
; *********************************************************************
; *** Use custom settings from file my_config.h
-DWLED_USE_MY_CONFIG
; *********************************************************************
; -D WLED_DISABLE_OTA
; -D WLED_DISABLE_ALEXA
; -D WLED_DISABLE_BLYNK
; -D WLED_DISABLE_CRONIXIE
; -D WLED_DISABLE_HUESYNC
; -D WLED_DISABLE_INFRARED
; -D WLED_DISABLE_WEBSOCKETS
; PIN defines - uncomment and change, if needed:
; -D LEDPIN=2
; -D BTNPIN=0
@ -30,11 +37,11 @@ build_flags = ${common.build_flags_esp8266}
; -D USE_WS2801
; -D USE_LPD8806
; PIN defines for 2 wire LEDs
; -D CLKPIN=0
; -D DATAPIN=2
-D CLKPIN=0
-D DATAPIN=2
; to drive analog LED strips (aka 5050), uncomment the following
; PWM pins 5,12,13,15 are used with Magic Home LED Controller (default)
; -D WLED_USE_ANALOG_LEDS
-D WLED_USE_ANALOG_LEDS
; for the H801 controller (PINs 15,13,12,14 (W2 = 04)) uncomment this
; -D WLED_USE_H801
; for the BW-LT11 controller (PINs 12,4,14,5 ) uncomment this

View File

@ -6,6 +6,7 @@
<a href="https://discord.gg/KuqP7NE"><img src="https://img.shields.io/discord/473448917040758787.svg?colorB=blue&label=discord&style=flat-square"></a>
<a href="https://github.com/Aircoookie/WLED/wiki"><img src="https://img.shields.io/badge/quick_start-wiki-blue.svg?style=flat-square"></a>
<a href="https://github.com/Aircoookie/WLED-App"><img src="https://img.shields.io/badge/app-wled-blue.svg?style=flat-square"></a>
<a href="https://gitpod.io/#https://github.com/Aircoookie/WLED"><img src="https://img.shields.io/badge/Gitpod-ready--to--code-blue?style=flat-square&logo=gitpod"></a>
</p>
@ -21,19 +22,20 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control
- Settings page - configuration over network
- Access Point and station mode - automatic failsafe AP
- Support for RGBW strips
- 16 user presets to save and load colors/effects easily, supports cycling through them.
- Macro functions to automatically execute API calls
- Up to 250 user presets to save and load colors/effects easily, supports cycling through them.
- Presets can be used to automatically execute API calls
- Nightlight function (gradually dims down)
- Full OTA software updatability (HTTP + ArduinoOTA), password protectable
- Configurable analog clock + support for the Cronixie kit by Diamex
- Configurable Auto Brightness limit for safer operation
- Filesystem-based config for easier backup of presets and settings
## 💡 Supported light control interfaces
- 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
- MQTT
- Blynk IoT
- E1.31, Art-Net and TPM2.net
- E1.31, Art-Net, DDP and TPM2.net
- [Hyperion](https://github.com/hyperion-project/hyperion.ng)
- UDP realtime
- Alexa voice control (including dimming and color)

View File

@ -386,6 +386,14 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()=====";
method: "plaintext",
filter: "html-minify",
},
{
file: "404.htm",
name: "PAGE_404",
prepend: "=====(",
append: ")=====",
method: "plaintext",
filter: "html-minify",
},
{
file: "favicon.ico",
name: "favicon",

View File

@ -103,7 +103,43 @@ class MyExampleUsermod : public Usermod {
userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
//if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
}
/*
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
* If you want to force saving the current state, use serializeConfig() in your loop().
*
* CAUTION: serializeConfig() will initiate a filesystem write operation.
* It might cause the LEDs to stutter and will cause flash wear if called too often.
* Use it sparingly and always in the loop, never in network callbacks!
*
* addToConfig() will also not yet add your setting to one of the settings pages automatically.
* To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually.
*
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
*/
void addToConfig(JsonObject& root)
{
JsonObject top = root.createNestedObject("exampleUsermod");
top["great"] = userVar0; //save this var persistently whenever settings are saved
}
/*
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
* This is called by WLED when settings are loaded (currently this only happens once immediately after boot)
*
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
*/
void readFromConfig(JsonObject& root)
{
JsonObject top = root["top"];
userVar0 = top["great"] | 42; //The value right of the pipe "|" is the default value in case your setting was not present in cfg.json (e.g. first boot)
}
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).

View File

@ -2726,7 +2726,7 @@ uint16_t WS2812FX::candle(bool multi)
//max. flicker range controlled by intensity
uint8_t valrange = SEGMENT.intensity;
uint8_t rndval = valrange >> 1;
uint8_t rndval = valrange >> 1; //max 127
//step (how much to move closer to target per frame) coarsely set by speed
uint8_t speedFactor = 4;
@ -2763,9 +2763,9 @@ uint16_t WS2812FX::candle(bool multi)
}
if (newTarget) {
s_target = random8(rndval) + random8(rndval);
s_target = random8(rndval) + random8(rndval); //between 0 and rndval*2 -2 = 252
if (s_target < (rndval >> 1)) s_target = (rndval >> 1) + random8(rndval);
uint8_t offset = (255 - valrange) >> 1;
uint8_t offset = (255 - valrange);
s_target += offset;
uint8_t dif = (s_target > s) ? s_target - s : s - s_target;

View File

@ -40,8 +40,12 @@
#define DEFAULT_INTENSITY (uint8_t)128
#define DEFAULT_COLOR (uint32_t)0xFFAA00
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
/* Not used in all effects yet */
#define WLED_FPS 42
@ -50,9 +54,9 @@
/* each segment uses 52 bytes of SRAM memory, so if you're application fails because of
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
#ifdef ESP8266
#define MAX_NUM_SEGMENTS 10
#define MAX_NUM_SEGMENTS 12
#else
#define MAX_NUM_SEGMENTS 10
#define MAX_NUM_SEGMENTS 16
#endif
/* How much data bytes all segments combined may allocate */
@ -452,6 +456,7 @@ class WS2812FX {
setRange(uint16_t i, uint16_t i2, uint32_t col),
setShowCallback(show_callback cb),
setTransitionMode(bool t),
calcGammaTable(float),
trigger(void),
setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 0, uint8_t spacing = 0),
resetSegments(),
@ -485,6 +490,7 @@ class WS2812FX {
//getFirstSelectedSegment(void),
getMainSegmentId(void),
gamma8(uint8_t),
gamma8_cal(uint8_t, float),
get_random_wheel_index(uint8_t);
int8_t

View File

@ -965,8 +965,8 @@ void WS2812FX::setRgbwPwm(void) {
void WS2812FX::setRgbwPwm() {}
#endif
//gamma 2.4 lookup table used for color correction
const byte gammaT[] = {
//gamma 2.8 lookup table used for color correction
byte gammaT[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
@ -984,6 +984,17 @@ const byte gammaT[] = {
177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };
uint8_t WS2812FX::gamma8_cal(uint8_t b, float gamma) {
return (int)(pow((float)b / 255.0, gamma) * 255 + 0.5);
}
void WS2812FX::calcGammaTable(float gamma)
{
for (uint16_t i = 0; i < 256; i++) {
gammaT[i] = gamma8_cal(i, gamma);
}
}
uint8_t WS2812FX::gamma8(uint8_t b)
{
return gammaT[b];

View File

@ -46,7 +46,7 @@ void onAlexaChange(EspalexaDevice* dev)
bri = briLast;
colorUpdated(NOTIFIER_CALL_MODE_ALEXA);
}
} else applyMacro(macroAlexaOn);
} else applyPreset(macroAlexaOn);
} else if (m == EspalexaDeviceProperty::off)
{
if (!macroAlexaOff)
@ -57,7 +57,7 @@ void onAlexaChange(EspalexaDevice* dev)
bri = 0;
colorUpdated(NOTIFIER_CALL_MODE_ALEXA);
}
} else applyMacro(macroAlexaOff);
} else applyPreset(macroAlexaOff);
} else if (m == EspalexaDeviceProperty::bri)
{
bri = espalexaDevice->getValue();

View File

@ -11,13 +11,13 @@ void shortPressAction()
toggleOnOff();
colorUpdated(NOTIFIER_CALL_MODE_BUTTON);
} else {
applyMacro(macroButton);
applyPreset(macroButton);
}
}
bool isButtonPressed()
{
#ifdef BTNPIN
#if defined(BTNPIN) && BTNPIN > -1
if (digitalRead(BTNPIN) == LOW) return true;
#endif
#ifdef TOUCHPIN
@ -29,7 +29,7 @@ bool isButtonPressed()
void handleButton()
{
#if defined(BTNPIN) || defined(TOUCHPIN)
#if (defined(BTNPIN) && BTNPIN > -1) || defined(TOUCHPIN)
if (!buttonEnabled) return;
if (isButtonPressed()) //pressed
@ -41,7 +41,7 @@ void handleButton()
{
if (!buttonLongPressed)
{
if (macroLongPress) {applyMacro(macroLongPress);}
if (macroLongPress) {applyPreset(macroLongPress);}
else _setRandomColor(false,true);
buttonLongPressed = true;
@ -62,7 +62,7 @@ void handleButton()
else if (!buttonLongPressed) { //short press
if (macroDoublePress)
{
if (doublePress) applyMacro(macroDoublePress);
if (doublePress) applyPreset(macroDoublePress);
else buttonWaitTime = millis();
} else shortPressAction();
}

680
wled00/cfg.cpp Normal file
View File

@ -0,0 +1,680 @@
#include "wled.h"
/*
* Serializes and parses the cfg.json and wsec.json settings files, stored in internal FS.
* The structure of the JSON is not to be considered an official API and may change without notice.
*/
//simple macro for ArduinoJSON's or syntax
#define CJSON(a,b) a = b | a
void getStringFromJson(char* dest, const char* src, size_t len) {
if (src != nullptr) strlcpy(dest, src, len);
}
void deserializeConfig() {
bool fromeep = false;
bool success = deserializeConfigSec();
if (!success) { //if file does not exist, try reading from EEPROM
deEEPSettings();
fromeep = true;
}
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
DEBUG_PRINTLN(F("Reading settings from /cfg.json..."));
success = readObjectFromFile("/cfg.json", nullptr, &doc);
if (!success) { //if file does not exist, try reading from EEPROM
if (!fromeep) deEEPSettings();
return;
}
//deserializeJson(doc, json);
//int rev_major = doc[F("rev")][0]; // 1
//int rev_minor = doc[F("rev")][1]; // 0
//long vid = doc[F("vid")]; // 2010020
JsonObject id = doc[F("id")];
getStringFromJson(cmDNS, id[F("mdns")], 33);
getStringFromJson(serverDescription, id[F("name")], 33);
getStringFromJson(alexaInvocationName, id[F("inv")], 33);
JsonObject nw_ins_0 = doc["nw"][F("ins")][0];
getStringFromJson(clientSSID, nw_ins_0[F("ssid")], 33);
//int nw_ins_0_pskl = nw_ins_0[F("pskl")];
//The WiFi PSK is normally not contained in the regular file for security reasons.
//If it is present however, we will use it
getStringFromJson(clientPass, nw_ins_0["psk"], 65);
JsonArray nw_ins_0_ip = nw_ins_0[F("ip")];
JsonArray nw_ins_0_gw = nw_ins_0[F("gw")];
JsonArray nw_ins_0_sn = nw_ins_0[F("sn")];
for (byte i = 0; i < 4; i++) {
CJSON(staticIP[i], nw_ins_0_ip[i]);
CJSON(staticGateway[i], nw_ins_0_gw[i]);
CJSON(staticSubnet[i], nw_ins_0_sn[i]);
}
JsonObject ap = doc[F("ap")];
getStringFromJson(apSSID, ap[F("ssid")], 33);
getStringFromJson(apPass, ap["psk"] , 65); //normally not present due to security
//int ap_pskl = ap[F("pskl")];
CJSON(apChannel, ap[F("chan")]);
if (apChannel > 13 || apChannel < 1) apChannel = 1;
CJSON(apHide, ap[F("hide")]);
if (apHide > 1) apHide = 1;
CJSON(apBehavior, ap[F("behav")]);
/*
JsonArray ap_ip = ap[F("ip")];
for (byte i = 0; i < 4; i++) {
apIP[i] = ap_ip;
}*/
noWifiSleep = doc[F("wifi")][F("sleep")] | !noWifiSleep; // inverted
noWifiSleep = !noWifiSleep;
//int wifi_phy = doc[F("wifi")][F("phy")]; //force phy mode n?
JsonObject hw = doc[F("hw")];
JsonObject hw_led = hw[F("led")];
CJSON(ledCount, hw_led[F("total")]);
if (ledCount > MAX_LEDS) ledCount = MAX_LEDS;
CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]);
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]);
CJSON(strip.reverseMode, hw_led[F("rev")]);
JsonObject hw_led_ins_0 = hw_led[F("ins")][0];
//bool hw_led_ins_0_en = hw_led_ins_0[F("en")]; // true
//int hw_led_ins_0_start = hw_led_ins_0[F("start")]; // 0
//int hw_led_ins_0_len = hw_led_ins_0[F("len")]; // 1200
//int hw_led_ins_0_pin_0 = hw_led_ins_0[F("pin")][0]; // 2
strip.colorOrder = hw_led_ins_0[F("order")];
//bool hw_led_ins_0_rev = hw_led_ins_0[F("rev")]; // false
skipFirstLed = hw_led_ins_0[F("skip")]; // 0
useRGBW = (hw_led_ins_0[F("type")] == TYPE_SK6812_RGBW);
JsonObject hw_btn_ins_0 = hw[F("btn")][F("ins")][0];
buttonEnabled = hw_btn_ins_0[F("en")] | buttonEnabled;
//int hw_btn_ins_0_pin_0 = hw_btn_ins_0[F("pin")][0]; // 0
JsonArray hw_btn_ins_0_macros = hw_btn_ins_0[F("macros")];
CJSON(macroButton, hw_btn_ins_0_macros[0]);
CJSON(macroLongPress,hw_btn_ins_0_macros[1]);
CJSON(macroDoublePress, hw_btn_ins_0_macros[2]);
//int hw_btn_ins_0_type = hw_btn_ins_0[F("type")]; // 0
//int hw_ir_pin = hw[F("ir")][F("pin")]; // 4
CJSON(irEnabled, hw[F("ir")][F("type")]); // 0
//int hw_relay_pin = hw[F("relay")][F("pin")]; // 12
//bool hw_relay_rev = hw[F("relay")][F("rev")]; // false
//int hw_status_pin = hw[F("status")][F("pin")]; // -1
JsonObject light = doc[F("light")];
CJSON(briMultiplier, light[F("scale-bri")]);
CJSON(strip.paletteBlend, light[F("pal-mode")]);
float light_gc_bri = light[F("gc")]["bri"];
float light_gc_col = light[F("gc")][F("col")]; // 2.8
if (light_gc_bri > 1.5) strip.gammaCorrectBri = true;
else if (light_gc_bri > 0.5) strip.gammaCorrectBri = false;
if (light_gc_col > 1.5) strip.gammaCorrectCol = true;
else if (light_gc_col > 0.5) strip.gammaCorrectCol = false;
JsonObject light_tr = light[F("tr")];
CJSON(fadeTransition, light_tr[F("mode")]);
int tdd = light_tr[F("dur")] | -1;
if (tdd >= 0) transitionDelayDefault = tdd * 100;
CJSON(strip.paletteFade, light_tr[F("pal")]);
JsonObject light_nl = light["nl"];
CJSON(nightlightMode, light_nl[F("mode")]);
CJSON(nightlightDelayMinsDefault, light_nl[F("dur")]);
nightlightDelayMins = nightlightDelayMinsDefault;
CJSON(nightlightTargetBri, light_nl[F("tbri")]);
CJSON(macroNl, light_nl[F("macro")]);
JsonObject def = doc[F("def")];
CJSON(bootPreset, def[F("ps")]);
CJSON(turnOnAtBoot, def["on"]); // true
CJSON(briS, def["bri"]); // 128
if (briS == 0) briS = 255;
JsonObject def_cy = def[F("cy")];
CJSON(presetCyclingEnabled, def_cy["on"]);
CJSON(presetCycleMin, def_cy[F("range")][0]);
CJSON(presetCycleMax, def_cy[F("range")][1]);
tdd = def_cy[F("dur")] | -1;
if (tdd >= 0) presetCycleTime = tdd * 100;
JsonObject interfaces = doc["if"];
JsonObject if_sync = interfaces[F("sync")];
CJSON(udpPort, if_sync[F("port0")]); // 21324
CJSON(udpPort2, if_sync[F("port1")]); // 65506
JsonObject if_sync_recv = if_sync[F("recv")];
CJSON(receiveNotificationBrightness, if_sync_recv["bri"]);
CJSON(receiveNotificationColor, if_sync_recv[F("col")]);
CJSON(receiveNotificationEffects, if_sync_recv[F("fx")]);
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
JsonObject if_sync_send = if_sync[F("send")];
CJSON(notifyDirectDefault, if_sync_send[F("dir")]);
notifyDirect = notifyDirectDefault;
CJSON(notifyButton, if_sync_send[F("btn")]);
CJSON(notifyAlexa, if_sync_send[F("va")]);
CJSON(notifyHue, if_sync_send[F("hue")]);
CJSON(notifyMacro, if_sync_send[F("macro")]);
CJSON(notifyTwice, if_sync_send[F("twice")]);
JsonObject if_live = interfaces[F("live")];
CJSON(receiveDirect, if_live[F("en")]);
CJSON(e131Port, if_live[F("port")]); // 5568
CJSON(e131Multicast, if_live[F("mc")]);
JsonObject if_live_dmx = if_live[F("dmx")];
CJSON(e131Universe, if_live_dmx[F("uni")]);
CJSON(e131SkipOutOfSequence, if_live_dmx[F("seqskip")]);
CJSON(DMXAddress, if_live_dmx[F("addr")]);
CJSON(DMXMode, if_live_dmx[F("mode")]);
tdd = if_live[F("timeout")] | -1;
if (tdd >= 0) realtimeTimeoutMs = tdd * 100;
CJSON(arlsForceMaxBri, if_live[F("maxbri")]);
CJSON(arlsDisableGammaCorrection, if_live[F("no-gc")]); // false
CJSON(arlsOffset, if_live[F("offset")]); // 0
CJSON(alexaEnabled, interfaces[F("va")][F("alexa")]); // false
CJSON(macroAlexaOn, interfaces[F("va")][F("macros")][0]);
CJSON(macroAlexaOff, interfaces[F("va")][F("macros")][1]);
const char* apikey = interfaces[F("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_mqtt = interfaces[F("mqtt")];
CJSON(mqttEnabled, if_mqtt[F("en")]);
getStringFromJson(mqttServer, if_mqtt[F("broker")], 33);
CJSON(mqttPort, if_mqtt[F("port")]); // 1883
getStringFromJson(mqttUser, if_mqtt[F("user")], 41);
getStringFromJson(mqttPass, if_mqtt["psk"], 41); //normally not present due to security
getStringFromJson(mqttClientID, if_mqtt[F("cid")], 41);
getStringFromJson(mqttDeviceTopic, if_mqtt[F("topics")][F("device")], 33); // "wled/test"
getStringFromJson(mqttGroupTopic, if_mqtt[F("topics")][F("group")], 33); // ""
JsonObject if_hue = interfaces[F("hue")];
CJSON(huePollingEnabled, if_hue[F("en")]);
CJSON(huePollLightId, if_hue[F("id")]);
tdd = if_hue[F("iv")] | -1;
if (tdd >= 2) huePollIntervalMs = tdd * 100;
JsonObject if_hue_recv = if_hue[F("recv")];
CJSON(hueApplyOnOff, if_hue_recv["on"]);
CJSON(hueApplyBri, if_hue_recv["bri"]);
CJSON(hueApplyColor, if_hue_recv[F("col")]);
JsonArray if_hue_ip = if_hue[F("ip")];
for (byte i = 0; i < 4; i++)
CJSON(hueIP[i], if_hue_ip[i]);
JsonObject if_ntp = interfaces[F("ntp")];
CJSON(ntpEnabled, if_ntp[F("en")]);
getStringFromJson(ntpServerName, if_ntp[F("host")], 33); // "1.wled.pool.ntp.org"
CJSON(currentTimezone, if_ntp[F("tz")]);
CJSON(utcOffsetSecs, if_ntp[F("offset")]);
CJSON(useAMPM, if_ntp[F("ampm")]);
JsonObject ol = doc[F("ol")];
CJSON(overlayDefault ,ol[F("clock")]); // 0
CJSON(countdownMode, ol[F("cntdwn")]);
overlayCurrent = overlayDefault;
JsonArray ol_cntdwn = ol[F("cntdwn")]; //[20,12,31,23,59,59]
//timed macro rules
JsonObject tm = doc[F("timers")];
JsonObject cntdwn = tm[F("cntdwn")];
JsonArray cntdwn_goal = cntdwn[F("goal")];
CJSON(countdownYear, cntdwn_goal[0]);
CJSON(countdownMonth, cntdwn_goal[1]);
CJSON(countdownDay, cntdwn_goal[2]);
CJSON(countdownHour, cntdwn_goal[3]);
CJSON(countdownMin, cntdwn_goal[4]);
CJSON(countdownSec, cntdwn_goal[5]);
CJSON(macroCountdown, cntdwn[F("macro")]);
JsonArray timers = tm[F("ins")];
uint8_t it = 0;
for (JsonObject timer : timers) {
if (it > 7) break;
CJSON(timerHours[it], timer[F("hour")]);
CJSON(timerMinutes[it], timer[F("min")]);
CJSON(timerMacro[it], timer[F("macro")]);
byte dowPrev = timerWeekday[it];
bool actPrev = timerWeekday[it] & 0x01;
CJSON(timerWeekday[it], timer[F("dow")]);
if (timerWeekday[it] != dowPrev) { //present in JSON
timerWeekday[it] <<= 1; //add active bit
bool act = timer[F("en")] | actPrev;
if (act) timerWeekday[it]++;
}
it++;
}
JsonObject ota = doc["ota"];
const char* pwd = ota["psk"]; //normally not present due to security
bool pwdCorrect = !otaLock; //always allow access if ota not locked
if (pwd != nullptr && strncmp(otaPass, pwd, 33) == 0) pwdCorrect = true;
if (pwdCorrect) { //only accept these values from cfg.json if ota is unlocked (else from wsec.json)
CJSON(otaLock, ota[F("lock")]);
CJSON(wifiLock, ota[F("lock-wifi")]);
CJSON(aOtaEnabled, ota[F("aota")]);
getStringFromJson(otaPass, pwd, 33); //normally not present due to security
}
#ifdef WLED_ENABLE_DMX
JsonObject dmx = doc["dmx"];
CJSON(DMXChannels, dmx[F("chan")]);
CJSON(DMXGap,dmx[F("gap")]);
CJSON(DMXStart, dmx[F("start")]);
CJSON(DMXStartLED,dmx[F("start-led")]);
JsonArray dmx_fixmap = dmx.createNestedArray("fixmap");
it = 0;
for (int i : dmx_fixmap) {
if (it > 14) break;
DMXFixtureMap[i] = i;
it++;
}
#endif
JsonObject usermods_settings = doc["um"];
usermods.readFromConfig(usermods_settings);
}
void serializeConfig() {
serializeConfigSec();
DEBUG_PRINTLN(F("Writing settings to /cfg.json..."));
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
//{ //scope this to reduce stack size
JsonArray rev = doc.createNestedArray("rev");
rev.add(1); //major settings revision
rev.add(0); //minor settings revision
doc[F("vid")] = VERSION;
JsonObject id = doc.createNestedObject("id");
id[F("mdns")] = cmDNS;
id[F("name")] = serverDescription;
id[F("inv")] = alexaInvocationName;
JsonObject nw = doc.createNestedObject("nw");
JsonArray nw_ins = nw.createNestedArray("ins");
JsonObject nw_ins_0 = nw_ins.createNestedObject();
nw_ins_0[F("ssid")] = clientSSID;
nw_ins_0[F("pskl")] = strlen(clientPass);
JsonArray nw_ins_0_ip = nw_ins_0.createNestedArray("ip");
JsonArray nw_ins_0_gw = nw_ins_0.createNestedArray("gw");
JsonArray nw_ins_0_sn = nw_ins_0.createNestedArray("sn");
for (byte i = 0; i < 4; i++) {
nw_ins_0_ip.add(staticIP[i]);
nw_ins_0_gw.add(staticGateway[i]);
nw_ins_0_sn.add(staticSubnet[i]);
}
JsonObject ap = doc.createNestedObject("ap");
ap[F("ssid")] = apSSID;
ap[F("pskl")] = strlen(apPass);
ap[F("chan")] = apChannel;
ap[F("behav")] = apBehavior;
JsonArray ap_ip = ap.createNestedArray("ip");
ap_ip.add(4);
ap_ip.add(3);
ap_ip.add(2);
ap_ip.add(1);
JsonObject wifi = doc.createNestedObject("wifi");
wifi[F("sleep")] = !noWifiSleep;
wifi[F("phy")] = 1;
JsonObject hw = doc.createNestedObject("hw");
JsonObject hw_led = hw.createNestedObject("led");
hw_led[F("total")] = ledCount;
hw_led[F("maxpwr")] = strip.ablMilliampsMax;
hw_led[F("ledma")] = strip.milliampsPerLed;
hw_led[F("rev")] = strip.reverseMode;
JsonArray hw_led_ins = hw_led.createNestedArray("ins");
JsonObject hw_led_ins_0 = hw_led_ins.createNestedObject();
hw_led_ins_0[F("en")] = true;
hw_led_ins_0[F("start")] = 0;
hw_led_ins_0[F("len")] = ledCount;
JsonArray hw_led_ins_0_pin = hw_led_ins_0.createNestedArray("pin");
hw_led_ins_0_pin.add(LEDPIN);
#ifdef DATAPIN
hw_led_ins_0_pin.add(DATAPIN);
#endif
hw_led_ins_0[F("order")] = strip.colorOrder; //color order
hw_led_ins_0[F("rev")] = false;
hw_led_ins_0[F("skip")] = skipFirstLed ? 1 : 0;
//this is very crude and temporary
byte ledType = TYPE_WS2812_RGB;
if (useRGBW) ledType = TYPE_SK6812_RGBW;
#ifdef USE_WS2801
ledType = TYPE_WS2801;
#endif
#ifdef USE_APA102
ledType = TYPE_APA102;
#endif
#ifdef USE_LPD8806
ledType = TYPE_LPD8806;
#endif
#ifdef USE_P9813
ledType = TYPE_P9813;
#endif
#ifdef USE_TM1814
ledType = TYPE_TM1814;
#endif
hw_led_ins_0[F("type")] = ledType;
JsonObject hw_btn = hw.createNestedObject("btn");
JsonArray hw_btn_ins = hw_btn.createNestedArray("ins");
#if defined(BTNPIN) && BTNPIN > -1
JsonObject hw_btn_ins_0 = hw_btn_ins.createNestedObject();
hw_btn_ins_0[F("type")] = (buttonEnabled) ? BTN_TYPE_PUSH : BTN_TYPE_NONE;
JsonArray hw_btn_ins_0_pin = hw_btn_ins_0.createNestedArray("pin");
hw_btn_ins_0_pin.add(BTNPIN);
JsonArray hw_btn_ins_0_macros = hw_btn_ins_0.createNestedArray("macros");
hw_btn_ins_0_macros.add(macroButton);
hw_btn_ins_0_macros.add(macroLongPress);
hw_btn_ins_0_macros.add(macroDoublePress);
#endif
#if defined(IRPIN) && IRPIN > -1
JsonObject hw_ir = hw.createNestedObject("ir");
hw_ir[F("pin")] = IR_PIN;
hw_ir[F("type")] = 0;
#endif
#if defined(RLYPIN) && RLYPIN > -1
JsonObject hw_relay = hw.createNestedObject("relay");
hw_relay[F("pin")] = RLYPIN;
hw_relay[F("rev")] = (RLYMDE) ? false : true;
JsonObject hw_status = hw.createNestedObject("status");
hw_status[F("pin")] = -1;
#endif
JsonObject light = doc.createNestedObject("light");
light[F("scale-bri")] = briMultiplier;
light[F("pal-mode")] = strip.paletteBlend;
JsonObject light_gc = light.createNestedObject("gc");
light_gc["bri"] = (strip.gammaCorrectBri) ? 2.8 : 1.0;
light_gc[F("col")] = (strip.gammaCorrectCol) ? 2.8 : 1.0;
JsonObject light_tr = light.createNestedObject("tr");
light_tr[F("mode")] = fadeTransition;
light_tr[F("dur")] = transitionDelayDefault / 100;
light_tr[F("pal")] = strip.paletteFade;
JsonObject light_nl = light.createNestedObject("nl");
light_nl[F("mode")] = nightlightMode;
light_nl[F("dur")] = nightlightDelayMinsDefault;
light_nl[F("tbri")] = nightlightTargetBri;
light_nl[F("macro")] = macroNl;
JsonObject def = doc.createNestedObject("def");
def[F("ps")] = bootPreset;
def["on"] = turnOnAtBoot;
def["bri"] = briS;
//to be removed once preset cycles are presets
if (saveCurrPresetCycConf) {
JsonObject def_cy = def.createNestedObject("cy");
def_cy["on"] = presetCyclingEnabled;
JsonArray def_cy_range = def_cy.createNestedArray("range");
def_cy_range.add(presetCycleMin);
def_cy_range.add(presetCycleMax);
def_cy[F("dur")] = presetCycleTime / 100;
}
JsonObject interfaces = doc.createNestedObject("if");
JsonObject if_sync = interfaces.createNestedObject("sync");
if_sync[F("port0")] = udpPort;
if_sync[F("port1")] = udpPort2;
JsonObject if_sync_recv = if_sync.createNestedObject("recv");
if_sync_recv["bri"] = receiveNotificationBrightness;
if_sync_recv[F("col")] = receiveNotificationColor;
if_sync_recv[F("fx")] = receiveNotificationEffects;
JsonObject if_sync_send = if_sync.createNestedObject("send");
if_sync_send[F("dir")] = notifyDirect;
if_sync_send[F("btn")] = notifyButton;
if_sync_send[F("va")] = notifyAlexa;
if_sync_send[F("hue")] = notifyHue;
if_sync_send[F("macro")] = notifyMacro;
if_sync_send[F("twice")] = notifyTwice;
JsonObject if_live = interfaces.createNestedObject("live");
if_live[F("en")] = receiveDirect;
if_live[F("port")] = e131Port;
if_live[F("mc")] = e131Multicast;
JsonObject if_live_dmx = if_live.createNestedObject("dmx");
if_live_dmx[F("uni")] = e131Universe;
if_live_dmx[F("seqskip")] = e131SkipOutOfSequence;
if_live_dmx[F("addr")] = DMXAddress;
if_live_dmx[F("mode")] = DMXMode;
if_live[F("timeout")] = realtimeTimeoutMs / 100;
if_live[F("maxbri")] = arlsForceMaxBri;
if_live[F("no-gc")] = arlsDisableGammaCorrection;
if_live[F("offset")] = arlsOffset;
JsonObject if_va = interfaces.createNestedObject("va");
if_va[F("alexa")] = alexaEnabled;
JsonArray if_va_macros = if_va.createNestedArray("macros");
if_va_macros.add(macroAlexaOn);
if_va_macros.add(macroAlexaOff);
JsonObject if_blynk = interfaces.createNestedObject("blynk");
if_blynk[F("token")] = strlen(blynkApiKey) ? "Hidden":"";
JsonObject if_mqtt = interfaces.createNestedObject("mqtt");
if_mqtt[F("en")] = mqttEnabled;
if_mqtt[F("broker")] = mqttServer;
if_mqtt[F("port")] = mqttPort;
if_mqtt[F("user")] = mqttUser;
if_mqtt[F("pskl")] = strlen(mqttPass);
if_mqtt[F("cid")] = mqttClientID;
JsonObject if_mqtt_topics = if_mqtt.createNestedObject("topics");
if_mqtt_topics[F("device")] = mqttDeviceTopic;
if_mqtt_topics[F("group")] = mqttGroupTopic;
JsonObject if_hue = interfaces.createNestedObject("hue");
if_hue[F("en")] = huePollingEnabled;
if_hue[F("id")] = huePollLightId;
if_hue[F("iv")] = huePollIntervalMs / 100;
JsonObject if_hue_recv = if_hue.createNestedObject("recv");
if_hue_recv["on"] = hueApplyOnOff;
if_hue_recv["bri"] = hueApplyBri;
if_hue_recv[F("col")] = hueApplyColor;
JsonArray if_hue_ip = if_hue.createNestedArray("ip");
for (byte i = 0; i < 4; i++) {
if_hue_ip.add(hueIP[i]);
}
JsonObject if_ntp = interfaces.createNestedObject("ntp");
if_ntp[F("en")] = ntpEnabled;
if_ntp[F("host")] = ntpServerName;
if_ntp[F("tz")] = currentTimezone;
if_ntp[F("offset")] = utcOffsetSecs;
if_ntp[F("ampm")] = useAMPM;
JsonObject ol = doc.createNestedObject("ol");
ol[F("clock")] = overlayDefault;
ol[F("cntdwn")] = countdownMode;
JsonObject timers = doc.createNestedObject("timers");
JsonObject cntdwn = timers.createNestedObject("cntdwn");
JsonArray goal = cntdwn.createNestedArray("goal");
goal.add(countdownYear); goal.add(countdownMonth); goal.add(countdownDay);
goal.add(countdownHour); goal.add(countdownMin); goal.add(countdownSec);
cntdwn[F("macro")] = macroCountdown;
JsonArray timers_ins = timers.createNestedArray("ins");
for (byte i = 0; i < 8; i++) {
if (timerMacro[i] == 0 && timerHours[i] == 0 && timerMinutes[i] == 0) continue;
JsonObject timers_ins0 = timers_ins.createNestedObject();
timers_ins0[F("en")] = (timerWeekday[i] & 0x01);
timers_ins0[F("hour")] = timerHours[i];
timers_ins0[F("min")] = timerMinutes[i];
timers_ins0[F("macro")] = timerMacro[i];
timers_ins0[F("dow")] = timerWeekday[i] >> 1;
}
JsonObject ota = doc.createNestedObject("ota");
ota[F("lock")] = otaLock;
ota[F("lock-wifi")] = wifiLock;
ota[F("pskl")] = strlen(otaPass);
ota[F("aota")] = aOtaEnabled;
#ifdef WLED_ENABLE_DMX
JsonObject dmx = doc.createNestedObject("dmx");
dmx[F("chan")] = DMXChannels;
dmx[F("gap")] = DMXGap;
dmx[F("start")] = DMXStart;
dmx[F("start-led")] = DMXStartLED;
JsonArray dmx_fixmap = dmx.createNestedArray("fixmap");
for (byte i = 0; i < 15; i++)
dmx_fixmap.add(DMXFixtureMap[i]);
#endif
//}
JsonObject usermods_settings = doc.createNestedObject("um");
usermods.addToConfig(usermods_settings);
File f = WLED_FS.open("/cfg.json", "w");
if (f) serializeJson(doc, f);
f.close();
}
//settings in /wsec.json, not accessible via webserver, for passwords and tokens
bool deserializeConfigSec() {
DEBUG_PRINTLN(F("Reading settings from /wsec.json..."));
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
bool success = readObjectFromFile("/wsec.json", nullptr, &doc);
if (!success) return false;
JsonObject nw_ins_0 = doc["nw"][F("ins")][0];
getStringFromJson(clientPass, nw_ins_0["psk"], 65);
JsonObject ap = doc[F("ap")];
getStringFromJson(apPass, ap["psk"] , 65);
JsonObject interfaces = doc["if"];
const char* apikey = interfaces["blynk"][F("token")] | "Hidden";
int tdd = strnlen(apikey, 36);
if (tdd > 20 || tdd == 0)
getStringFromJson(blynkApiKey, apikey, 36);
JsonObject if_mqtt = interfaces[F("mqtt")];
getStringFromJson(mqttPass, if_mqtt["psk"], 41);
getStringFromJson(hueApiKey, interfaces[F("hue")][F("key")], 47);
JsonObject ota = doc["ota"];
getStringFromJson(otaPass, ota[F("pwd")], 33);
CJSON(otaLock, ota[F("lock")]);
CJSON(wifiLock, ota[F("lock-wifi")]);
CJSON(aOtaEnabled, ota[F("aota")]);
return true;
}
void serializeConfigSec() {
DEBUG_PRINTLN(F("Writing settings to /wsec.json..."));
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
JsonObject nw = doc.createNestedObject("nw");
JsonArray nw_ins = nw.createNestedArray("ins");
JsonObject nw_ins_0 = nw_ins.createNestedObject();
nw_ins_0["psk"] = clientPass;
JsonObject ap = doc.createNestedObject("ap");
ap["psk"] = apPass;
JsonObject interfaces = doc.createNestedObject("if");
JsonObject if_blynk = interfaces.createNestedObject("blynk");
if_blynk[F("token")] = blynkApiKey;
JsonObject if_mqtt = interfaces.createNestedObject("mqtt");
if_mqtt["psk"] = mqttPass;
JsonObject if_hue = interfaces.createNestedObject("hue");
if_hue[F("key")] = hueApiKey;
JsonObject ota = doc.createNestedObject("ota");
ota[F("pwd")] = otaPass;
ota[F("lock")] = otaLock;
ota[F("lock-wifi")] = wifiLock;
ota[F("aota")] = aOtaEnabled;
File f = WLED_FS.open("/wsec.json", "w");
if (f) serializeJson(doc, f);
f.close();
}

View File

@ -177,7 +177,7 @@ void colorRGBtoXY(byte* rgb, float* xy) //rgb to coordinates (https://www.develo
}
#endif // WLED_DISABLE_HUESYNC
//RRGGBB / WWRRGGBB order for hex
void colorFromDecOrHexString(byte* rgb, char* in)
{
if (in[0] == 0) return;
@ -198,6 +198,27 @@ void colorFromDecOrHexString(byte* rgb, char* in)
rgb[2] = c & 0xFF;
}
//contrary to the colorFromDecOrHexString() function, this uses the more standard RRGGBB / RRGGBBWW order
bool colorFromHexString(byte* rgb, const char* in) {
if (in == nullptr) return false;
size_t inputSize = strnlen(in, 9);
if (inputSize != 6 && inputSize != 8) return false;
uint32_t c = strtoul(in, NULL, 16);
if (inputSize == 6) {
rgb[0] = (c >> 16) & 0xFF;
rgb[1] = (c >> 8) & 0xFF;
rgb[2] = c & 0xFF;
} else {
rgb[0] = (c >> 24) & 0xFF;
rgb[1] = (c >> 16) & 0xFF;
rgb[2] = (c >> 8) & 0xFF;
rgb[3] = c & 0xFF;
}
return true;
}
float minf (float v, float w)
{
if (w > v) return v;

View File

@ -72,20 +72,46 @@
#define DMX_MODE_MULTIPLE_RGB 4 //every LED is addressed with its own RGB (ledCount * 3 channels)
#define DMX_MODE_MULTIPLE_DRGB 5 //every LED is addressed with its own RGB and share a master dimmer (ledCount * 3 + 1 channels)
//Light capability byte (unused)
//Light capability byte (unused) 0bRRCCTTTT
//bits 0/1/2/3: specifies a type of LED driver. A single "driver" may have different chip models but must have the same protocol/behavior
//bits 4/5: specifies the class of LED driver - 0b00 (dec. 0-15) unconfigured/reserved
// - 0b01 (dec. 16-31) digital (data pin only)
// - 0b10 (dec. 32-47) analog (PWM)
// - 0b11 (dec. 48-63) digital (data + clock / SPI)
//bits 6/7 are reserved and set to 0b00
#define TYPE_NONE 0 //light is not configured
#define TYPE_RESERVED 1 //unused. Might indicate a "virtual" light
#define TYPE_WS2812_RGB 2
#define TYPE_SK6812_RGBW 3
#define TYPE_WS2812_WWA 4 //amber + warm + cold white
#define TYPE_WS2801 5
#define TYPE_ANALOG_1CH 6 //single channel PWM. Uses value of brightest RGBW channel
#define TYPE_ANALOG_2CH 7 //analog WW + CW
#define TYPE_ANALOG_3CH 8 //analog RGB
#define TYPE_ANALOG_4CH 9 //analog RGBW
#define TYPE_ANALOG_5CH 10 //analog RGB + WW + CW
#define TYPE_APA102 11
#define TYPE_LPD8806 12
//Digital types (data pin only) (16-31)
#define TYPE_WS2812_1CH 20 //white-only chips
#define TYPE_WS2812_WWA 21 //amber + warm + cold white
#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_WS2811_400KHZ 24 //half-speed WS2812 protocol, used by very old WS2811 units
#define TYPE_SK6812_RGBW 30
//"Analog" types (PWM) (32-47)
#define TYPE_ONOFF 40 //binary output (relays etc.)
#define TYPE_ANALOG_1CH 41 //single channel PWM. Uses value of brightest RGBW channel
#define TYPE_ANALOG_2CH 42 //analog WW + CW
#define TYPE_ANALOG_3CH 43 //analog RGB
#define TYPE_ANALOG_4CH 44 //analog RGBW
#define TYPE_ANALOG_5CH 45 //analog RGB + WW + CW
//Digital types (data + clock / SPI) (48-63)
#define TYPE_WS2801 50
#define TYPE_APA102 51
#define TYPE_LPD8806 52
#define TYPE_P9813 53
#define TYPE_TM1814 54
//Button type
#define BTN_TYPE_NONE 0
#define BTN_TYPE_RESERVED 1
#define BTN_TYPE_PUSH 2
#define BTN_TYPE_PUSH_ACT_HIGH 3 //not implemented
#define BTN_TYPE_SWITCH 4 //not implemented
#define BTN_TYPE_SWITCH_ACT_HIGH 5 //not implemented
//Hue error codes
#define HUE_ERROR_INACTIVE 0
@ -105,19 +131,29 @@
#define SEG_OPTION_FREEZE 5 //Segment contents will not be refreshed
#define SEG_OPTION_TRANSITIONAL 7
// WLED Error modes
#define ERR_NONE 0 // All good :)
#define ERR_EEP_COMMIT 2 // Could not commit to EEPROM (wrong flash layout?)
#define ERR_JSON 9 // JSON parsing failed (input too large?)
#define ERR_FS_BEGIN 10 // Could not init filesystem (no partition?)
#define ERR_FS_QUOTA 11 // The FS is full or the maximum file size is reached
#define ERR_FS_PLOAD 12 // It was attempted to load a preset that does not exist
#define ERR_FS_GENERAL 19 // A general unspecified filesystem error occured
//Timer mode types
#define NL_MODE_SET 0 //After nightlight time elapsed, set to target brightness
#define NL_MODE_FADE 1 //Fade to target brightness 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.
//EEPROM size
#define EEPSIZE 2560 //Maximum is 4096
#define NTP_PACKET_SIZE 48
// maximum number of LEDs - MAX_LEDS is coming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue
// maximum number of LEDs - more than 1500 LEDs (or 500 DMA "LEDPIN 3" driven ones) will cause a low memory condition on ESP8266
#ifndef MAX_LEDS
#define MAX_LEDS 1500
#endif
#define MAX_LEDS_DMA 500
// string temp buffer (now stored in stack locally)
@ -127,6 +163,7 @@
#define ABL_MILLIAMPS_DEFAULT 850; // auto lower brightness to stay close to milliampere limit
#define TOUCH_THRESHOLD 32 // limit to recognize a touch, higher value means more sensitive
// Size of buffer for API JSON object (increase for more segments)

View File

@ -35,7 +35,6 @@
color: white;
border: 0px solid white;
border-radius: 25px;
filter: drop-shadow(0px 0px 1px #000);
}
</style>
</head>

File diff suppressed because one or more lines are too long

View File

@ -151,7 +151,7 @@
<h3>Defaults</h3>
Turn LEDs on after power up/reset: <input type="checkbox" name="BO"><br>
Default brightness: <input name="CA" type="number" min="0" max="255" required> (0-255)<br><br>
Apply preset <input name="BP" type="number" min="0" max="16" required> at boot (0 uses defaults)
Apply preset <input name="BP" type="number" min="0" max="250" required> at boot (0 uses defaults)
<br>- <i>or</i> -<br>
Set current preset cycle setting as boot default: <input type="checkbox" name="PC"><br><br>
Use Gamma correction for color: <input type="checkbox" name="GC"> (strongly recommended)<br>

View File

@ -43,10 +43,10 @@
}
function BTa()
{
var ih="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Macro</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";
var ih="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Preset</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";
for (i=0;i<8;i++)
{
ih+="<tr><td><input name=\"W"+i+"\" id=\"W"+i+"\" type=\"number\" style=\"display:none\"><input id=\"W"+i+"0\" type=\"checkbox\"></td><td><input name=\"H"+i+"\" type=\"number\" min=\"0\" max=\"24\"></td><td><input name=\"N"+i+"\" type=\"number\" min=\"0\" max=\"59\"></td><td><input name=\"T"+i+"\" type=\"number\" min=\"0\" max=\"16\"></td>";
ih+="<tr><td><input name=\"W"+i+"\" id=\"W"+i+"\" type=\"number\" style=\"display:none\"><input id=\"W"+i+"0\" type=\"checkbox\"></td><td><input name=\"H"+i+"\" type=\"number\" min=\"0\" max=\"24\"></td><td><input name=\"N"+i+"\" type=\"number\" min=\"0\" max=\"59\"></td><td><input name=\"T"+i+"\" type=\"number\" min=\"0\" max=\"250\"></td>";
for (j=1;j<8;j++) ih+="<td><input id=\"W"+i+j+"\" type=\"checkbox\"></td>";
}
gId("TMT").innerHTML=ih;
@ -108,6 +108,7 @@
<option value="15">CA-Saskatchewan</option>
<option value="16">ACST</option>
<option value="17">ACST/ACDT</option>
<option value="18">HST (Hawaii)</option>
</select><br>
UTC offset: <input name="UO" type="number" min="-65500" max="65500" required> seconds (max. 18 hours)<br>
Current local time is <span class="times">unknown</span>.
@ -134,32 +135,17 @@
Countdown Goal:<br>
Year: 20 <input name="CY" type="number" min="0" max="99" required> Month: <input name="CI" type="number" min="1" max="12" required> Day: <input name="CD" type="number" min="1" max="31" required><br>
Hour: <input name="CH" type="number" min="0" max="23" required> Minute: <input name="CM" type="number" min="0" max="59" required> Second: <input name="CS" type="number" min="0" max="59" required><br>
<h3>Advanced Macros</h3>
Define API macros here:<br>
1: <input name="M1" maxlength="64"><br>
2: <input name="M2" maxlength="64"><br>
3: <input name="M3" maxlength="64"><br>
4: <input name="M4" maxlength="64"><br>
5: <input name="M5" maxlength="64"><br>
6: <input name="M6" maxlength="64"><br>
7: <input name="M7" maxlength="64"><br>
8: <input name="M8" maxlength="64"><br>
9: <input name="M9" maxlength="64"><br>
10: <input name="M10" maxlength="64"><br>
11: <input name="M11" maxlength="64"><br>
12: <input name="M12" maxlength="64"><br>
13: <input name="M13" maxlength="64"><br>
14: <input name="M14" maxlength="64"><br>
15: <input name="M15" maxlength="64"><br>
16: <input name="M16" maxlength="64"><br><br>
<i>Use 0 for the default action instead of a macro</i><br>
Boot Macro: <input name="MB" type="number" min="0" max="16" required><br>
Alexa On/Off Macros: <input name="A0" type="number" min="0" max="16" required> <input name="A1" type="number" min="0" max="16" required><br>
Button short press macro: Macro: <input name="MP" type="number" min="0" max="16" required><br>
Long Press: <input name="ML" type="number" min="0" max="16" required> Double press: <input name="MD" type="number" min="0" max="16" required><br>
Countdown-Over Macro: <input name="MC" type="number" min="0" max="16" required><br>
Timed-Light-Over Macro: <input name="MN" type="number" min="0" max="16" required><br>
Time-Controlled Macros:<br>
<h3>Macro presets</h3>
<b>Macros have moved!</b><br>
<i>Presets now also can be used as macros to save both JSON and HTTP API commands.<br>
Just enter the preset id below!</i>
<i>Use 0 for the default action instead of a preset</i><br>
Alexa On/Off Preset: <input name="A0" type="number" min="0" max="250" required> <input name="A1" type="number" min="0" max="250" required><br>
Button short press Preset: <input name="MP" type="number" min="0" max="250" required><br>
Long Press: <input name="ML" type="number" min="0" max="250" required> Double press: <input name="MD" type="number" min="0" max="250" required><br>
Countdown-Over Preset: <input name="MC" type="number" min="0" max="250" required><br>
Timed-Light-Over Presets: <input name="MN" type="number" min="0" max="250" required><br>
<h3>Time-controlled presets</h3>
<div style="display: inline-block">
<table id="TMT">
</table></div><hr>

View File

@ -24,15 +24,16 @@ void handleDDPPacket(e131_packet_t* p) {
}
}
uint32_t offsetLeds = htonl(p->channelOffset) /3;
uint16_t packetLeds = htons(p->dataLen) /3;
uint32_t start = htonl(p->channelOffset) /3;
start += DMXAddress /3;
uint16_t stop = start + htons(p->dataLen) /3;
uint8_t* data = p->data;
uint16_t c = 0;
if (p->flags & DDP_TIMECODE_FLAG) c = 4; //packet has timecode flag, we do not support it, but data starts 4 bytes later
realtimeLock(realtimeTimeoutMs, REALTIME_MODE_DDP);
for (uint16_t i = offsetLeds; i < offsetLeds + packetLeds; i++) {
for (uint16_t i = start; i < stop; i++) {
setRealtimePixel(i, data[c++], data[c++], data[c++], 0);
}

View File

@ -25,6 +25,12 @@ bool isButtonPressed();
void handleButton();
void handleIO();
//cfg.cpp
void deserializeConfig();
bool deserializeConfigSec();
void serializeConfig();
void serializeConfigSec();
//colors.cpp
void colorFromUint32(uint32_t in, bool secondary = false);
void colorFromUint24(uint32_t in, bool secondary = false);
@ -37,6 +43,7 @@ void colorXYtoRGB(float x, float y, byte* rgb); // only defined if huesync disab
void colorRGBtoXY(byte* rgb, float* xy); // only defined if huesync disabled TODO
void colorFromDecOrHexString(byte* rgb, char* in);
bool colorFromHexString(byte* rgb, const char* in);
void colorRGBtoRGBW(byte* rgb); //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_MODE_LEGACY)
//dmx.cpp
@ -48,6 +55,12 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol);
//file.cpp
bool handleFileRead(AsyncWebServerRequest*, String path);
bool writeObjectToFileUsingId(const char* file, uint16_t id, JsonDocument* content);
bool writeObjectToFile(const char* file, const char* key, JsonDocument* content);
bool readObjectFromFileUsingId(const char* file, uint16_t id, JsonDocument* dest);
bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest);
void updateFSInfo();
void closeFile();
//hue.cpp
void handleHue();
@ -85,8 +98,8 @@ void handleIR();
void deserializeSegment(JsonObject elem, byte it);
bool deserializeState(JsonObject root);
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id);
void serializeState(JsonObject root);
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true);
void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true);
void serializeInfo(JsonObject root);
void serveJson(AsyncWebServerRequest* request);
bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient = 0);
@ -135,11 +148,36 @@ void setCronixie();
void _overlayCronixie();
void _drawOverlayCronixie();
//pin_manager.cpp
class PinManagerClass {
private:
#ifdef ESP8266
uint8_t pinAlloc[3] = {0x00, 0x00, 0x00}; //24bit, 1 bit per pin, we use first 17bits
#else
uint8_t pinAlloc[5] = {0x00, 0x00, 0x00, 0x00, 0x00}; //40bit, 1 bit per pin, we use all bits
#endif
public:
void deallocatePin(byte gpio);
bool allocatePin(byte gpio, bool output = true);
bool isPinAllocated(byte gpio);
bool isPinOk(byte gpio, bool output = true);
};
//playlist.cpp
void loadPlaylist(JsonObject playlistObject);
void handlePlaylist();
//presets.cpp
bool applyPreset(byte index);
void savePreset(byte index, bool persist = true, const char* pname = nullptr, JsonObject saveobj = JsonObject());
void deletePreset(byte index);
//set.cpp
void _setRandomColor(bool _sec,bool fromButton=false);
bool isAsterisksOnly(const char* str, byte maxLen);
void handleSettingsSet(AsyncWebServerRequest *request, byte subPage);
bool handleSet(AsyncWebServerRequest *request, const String& req);
bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply=true);
int getNumVal(const String* req, uint16_t pos);
bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte maxv=255);
@ -158,6 +196,8 @@ class Usermod {
virtual void addToJsonState(JsonObject& obj) {}
virtual void addToJsonInfo(JsonObject& obj) {}
virtual void readFromJsonState(JsonObject& obj) {}
virtual void addToConfig(JsonObject& obj) {}
virtual void readFromConfig(JsonObject& obj) {}
virtual uint16_t getId() {return USERMOD_ID_UNSPECIFIED;}
};
@ -176,6 +216,9 @@ class UsermodManager {
void addToJsonInfo(JsonObject& obj);
void readFromJsonState(JsonObject& obj);
void addToConfig(JsonObject& obj);
void readFromConfig(JsonObject& obj);
bool add(Usermod* um);
byte getModCount();
};
@ -189,18 +232,9 @@ void userConnected();
void userLoop();
//wled_eeprom.cpp
void commit();
void clearEEPROM();
void writeStringToEEPROM(uint16_t pos, char* str, uint16_t len);
void readStringFromEEPROM(uint16_t pos, char* str, uint16_t len);
void saveSettingsToEEPROM();
void loadSettingsFromEEPROM(bool first);
void savedToPresets();
bool applyPreset(byte index, bool loadBri = true);
void savePreset(byte index, bool persist = true);
void loadMacro(byte index, char* m);
void applyMacro(byte index);
void saveMacro(byte index, const String& mc, bool persist = true); //only commit on single save, not in settings
void deEEP();
void deEEPSettings();
//wled_serial.cpp
void handleSerial();

View File

@ -4,17 +4,378 @@
* Utility for SPIFFS filesystem
*/
//filesystem
#ifndef WLED_DISABLE_FILESYSTEM
#include <FS.h>
#ifdef ARDUINO_ARCH_ESP32
#include "SPIFFS.h"
#ifdef ARDUINO_ARCH_ESP32 //FS info bare IDF function until FS wrapper is available for ESP32
#if WLED_FS != LITTLEFS
#include "esp_spiffs.h"
#endif
#include "SPIFFSEditor.h"
#endif
#define FS_BUFSIZE 256
/*
* Structural requirements for files managed by writeObjectToFile() and readObjectFromFile() utilities:
* 1. File must be a string representation of a valid JSON object
* 2. File must have '{' as first character
* 3. There must not be any additional characters between a root-level key and its value object (e.g. space, tab, newline)
* 4. There must not be any characters between an root object-separating ',' and the next object key string
* 5. There may be any number of spaces, tabs, and/or newlines before such object-separating ','
* 6. There must not be more than 5 consecutive spaces at any point except for those permitted in condition 5
* 7. If it is desired to delete the first usable object (e.g. preset file), a dummy object '"0":{}' is inserted at the beginning.
* It shall be disregarded by receiving software.
* The reason for it is that deleting the first preset would require special code to handle commas between it and the 2nd preset
*/
// There are no consecutive spaces longer than this in the file, so if more space is required, findSpace() can return false immediately
// Actual space may be lower
uint16_t knownLargestSpace = UINT16_MAX;
File f;
//wrapper to find out how long closing takes
void closeFile() {
DEBUGFS_PRINT(F("Close -> "));
uint32_t s = millis();
f.close();
DEBUGFS_PRINTF("took %d ms\n", millis() - s);
doCloseFile = false;
}
//find() that reads and buffers data from file stream in 256-byte blocks.
//Significantly faster, f.find(key) can take SECONDS for multi-kB files
bool bufferedFind(const char *target, bool fromStart = true) {
#ifdef WLED_DEBUG_FS
DEBUGFS_PRINT("Find ");
DEBUGFS_PRINTLN(target);
uint32_t s = millis();
#endif
if (!f || !f.size()) return false;
size_t targetLen = strlen(target);
size_t index = 0;
byte c;
uint16_t bufsize = 0, count = 0;
byte buf[FS_BUFSIZE];
if (fromStart) f.seek(0);
while (f.position() < f.size() -1) {
bufsize = f.read(buf, FS_BUFSIZE);
count = 0;
while (count < bufsize) {
if(buf[count] != target[index])
index = 0; // reset index if any char does not match
if(buf[count] == target[index]) {
if(++index >= targetLen) { // return true if all chars in the target match
f.seek((f.position() - bufsize) + count +1);
DEBUGFS_PRINTF("Found at pos %d, took %d ms", f.position(), millis() - s);
return true;
}
}
count++;
}
}
DEBUGFS_PRINTF("No match, took %d ms\n", millis() - s);
return false;
}
//find empty spots in file stream in 256-byte blocks.
bool bufferedFindSpace(uint16_t targetLen, bool fromStart = true) {
#ifdef WLED_DEBUG_FS
DEBUGFS_PRINTF("Find %d spaces\n", targetLen);
uint32_t s = millis();
#endif
if (knownLargestSpace < targetLen) {
DEBUGFS_PRINT(F("No match, KLS "));
DEBUGFS_PRINTLN(knownLargestSpace);
return false;
}
if (!f || !f.size()) return false;
uint16_t index = 0;
uint16_t bufsize = 0, count = 0;
byte buf[FS_BUFSIZE];
if (fromStart) f.seek(0);
while (f.position() < f.size() -1) {
bufsize = f.read(buf, FS_BUFSIZE);
count = 0;
while (count < bufsize) {
if(buf[count] == ' ') {
if(++index >= targetLen) { // return true if space long enough
if (fromStart) {
f.seek((f.position() - bufsize) + count +1 - targetLen);
knownLargestSpace = UINT16_MAX; //there may be larger spaces after, so we don't know
}
DEBUGFS_PRINTF("Found at pos %d, took %d ms", f.position(), millis() - s);
return true;
}
} else {
if (!fromStart) return false;
if (index) {
if (knownLargestSpace < index || knownLargestSpace == UINT16_MAX) knownLargestSpace = index;
index = 0; // reset index if not space
}
}
count++;
}
}
DEBUGFS_PRINTF("No match, took %d ms\n", millis() - s);
return false;
}
//find the closing bracket corresponding to the opening bracket at the file pos when calling this function
bool bufferedFindObjectEnd() {
#ifdef WLED_DEBUG_FS
DEBUGFS_PRINTLN(F("Find obj end"));
uint32_t s = millis();
#endif
if (!f || !f.size()) return false;
uint16_t objDepth = 0; //num of '{' minus num of '}'. return once 0
uint16_t bufsize = 0, count = 0;
//size_t start = f.position();
byte buf[FS_BUFSIZE];
while (f.position() < f.size() -1) {
bufsize = f.read(buf, FS_BUFSIZE);
count = 0;
while (count < bufsize) {
if (buf[count] == '{') objDepth++;
if (buf[count] == '}') objDepth--;
if (objDepth == 0) {
f.seek((f.position() - bufsize) + count +1);
DEBUGFS_PRINTF("} at pos %d, took %d ms", f.position(), millis() - s);
return true;
}
count++;
}
}
DEBUGFS_PRINTF("No match, took %d ms\n", millis() - s);
return false;
}
//fills n bytes from current file pos with ' ' characters
void writeSpace(uint16_t l)
{
byte buf[FS_BUFSIZE];
memset(buf, ' ', FS_BUFSIZE);
while (l > 0) {
uint16_t block = (l>FS_BUFSIZE) ? FS_BUFSIZE : l;
f.write(buf, block);
l -= block;
}
if (knownLargestSpace < l) knownLargestSpace = l;
}
bool appendObjectToFile(const char* key, JsonDocument* content, uint32_t s, uint32_t contentLen = 0)
{
#ifdef WLED_DEBUG_FS
DEBUGFS_PRINTLN(F("Append"));
uint32_t s1 = millis();
#endif
uint32_t pos = 0;
if (!f) return false;
if (f.size() < 3) {
char init[10];
strcpy_P(init, PSTR("{\"0\":{}}"));
f.print(init);
}
if (content->isNull()) {
doCloseFile = true;
return true; //nothing to append
}
//if there is enough empty space in file, insert there instead of appending
if (!contentLen) contentLen = measureJson(*content);
DEBUGFS_PRINTF("CLen %d\n", contentLen);
if (bufferedFindSpace(contentLen + strlen(key) + 1)) {
if (f.position() > 2) f.write(','); //add comma if not first object
f.print(key);
serializeJson(*content, f);
DEBUGFS_PRINTF("Inserted, took %d ms (total %d)", millis() - s1, millis() - s);
doCloseFile = true;
return true;
}
//not enough space, append at end
//permitted space for presets exceeded
updateFSInfo();
if (f.size() + 9000 > (fsBytesTotal - fsBytesUsed)) { //make sure there is enough space to at least copy the file once
errorFlag = ERR_FS_QUOTA;
doCloseFile = true;
return false;
}
//check if last character in file is '}' (typical)
uint32_t eof = f.size() -1;
f.seek(eof, SeekSet);
if (f.read() == '}') pos = eof;
if (pos == 0) //not found
{
DEBUGFS_PRINTLN("not }");
f.seek(0);
while (bufferedFind("}",false)) //find last closing bracket in JSON if not last char
{
pos = f.position();
}
if (pos > 0) pos--;
}
DEBUGFS_PRINT("pos "); DEBUGFS_PRINTLN(pos);
if (pos > 2)
{
f.seek(pos, SeekSet);
f.write(',');
} else { //file content is not valid JSON object
f.seek(0, SeekSet);
f.print('{'); //start JSON
}
f.print(key);
//Append object
serializeJson(*content, f);
f.write('}');
doCloseFile = true;
DEBUGFS_PRINTF("Appended, took %d ms (total %d)", millis() - s1, millis() - s);
return true;
}
bool writeObjectToFileUsingId(const char* file, uint16_t id, JsonDocument* content)
{
char objKey[10];
sprintf(objKey, "\"%d\":", id);
return writeObjectToFile(file, objKey, content);
}
bool writeObjectToFile(const char* file, const char* key, JsonDocument* content)
{
uint32_t s = 0; //timing
#ifdef WLED_DEBUG_FS
DEBUGFS_PRINTF("Write to %s with key %s >>>\n", file, (key==nullptr)?"nullptr":key);
serializeJson(*content, Serial); DEBUGFS_PRINTLN();
s = millis();
#endif
uint32_t pos = 0;
f = WLED_FS.open(file, "r+");
if (!f && !WLED_FS.exists(file)) f = WLED_FS.open(file, "w+");
if (!f) {
DEBUGFS_PRINTLN(F("Failed to open!"));
return false;
}
if (!bufferedFind(key)) //key does not exist in file
{
return appendObjectToFile(key, content, s);
}
//an object with this key already exists, replace or delete it
pos = f.position();
//measure out end of old object
bufferedFindObjectEnd();
uint32_t pos2 = f.position();
uint32_t oldLen = pos2 - pos;
DEBUGFS_PRINTF("Old obj len %d\n", oldLen);
//Three cases:
//1. The new content is null, overwrite old obj with spaces
//2. The new content is smaller than the old, overwrite and fill diff with spaces
//3. The new content is larger than the old, but smaller than old + trailing spaces, overwrite with new
//4. The new content is larger than old + trailing spaces, delete old and append
uint32_t contentLen = 0;
if (!content->isNull()) contentLen = measureJson(*content);
if (contentLen && contentLen <= oldLen) { //replace and fill diff with spaces
DEBUGFS_PRINTLN(F("replace"));
f.seek(pos);
serializeJson(*content, f);
writeSpace(pos2 - f.position());
} else if (contentLen && bufferedFindSpace(contentLen - oldLen, false)) { //enough leading spaces to replace
DEBUGFS_PRINTLN(F("replace (trailing)"));
f.seek(pos);
serializeJson(*content, f);
} else {
DEBUGFS_PRINTLN(F("delete"));
pos -= strlen(key);
if (pos > 3) pos--; //also delete leading comma if not first object
f.seek(pos);
writeSpace(pos2 - pos);
if (contentLen) return appendObjectToFile(key, content, s, contentLen);
}
doCloseFile = true;
DEBUGFS_PRINTF("Replaced/deleted, took %d ms\n", millis() - s);
return true;
}
bool readObjectFromFileUsingId(const char* file, uint16_t id, JsonDocument* dest)
{
char objKey[10];
sprintf(objKey, "\"%d\":", id);
return readObjectFromFile(file, objKey, dest);
}
//if the key is a nullptr, deserialize entire object
bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest)
{
if (doCloseFile) closeFile();
#ifdef WLED_DEBUG_FS
DEBUGFS_PRINTF("Read from %s with key %s >>>\n", file, (key==nullptr)?"nullptr":key);
uint32_t s = millis();
#endif
f = WLED_FS.open(file, "r");
if (!f) return false;
if (key != nullptr && !bufferedFind(key)) //key does not exist in file
{
f.close();
dest->clear();
DEBUGFS_PRINTLN(F("Obj not found."));
return false;
}
deserializeJson(*dest, f);
f.close();
DEBUGFS_PRINTF("Read, took %d ms\n", millis() - s);
return true;
}
void updateFSInfo() {
#ifdef ARDUINO_ARCH_ESP32
#if WLED_FS == LITTLEFS
fsBytesTotal = LITTLEFS.totalBytes();
fsBytesUsed = LITTLEFS.usedBytes();
#else
esp_spiffs_info(nullptr, &fsBytesTotal, &fsBytesUsed);
#endif
#else
FSInfo fsi;
WLED_FS.info(fsi);
fsBytesUsed = fsi.usedBytes;
fsBytesTotal = fsi.totalBytes;
#endif
}
#if !defined WLED_DISABLE_FILESYSTEM && defined WLED_ENABLE_FS_SERVING
//Un-comment any file types you need
String getContentType(AsyncWebServerRequest* request, String filename){
if(request->hasArg("download")) return "application/octet-stream";
@ -37,19 +398,16 @@ String getContentType(AsyncWebServerRequest* request, String filename){
bool handleFileRead(AsyncWebServerRequest* request, String path){
DEBUG_PRINTLN("FileRead: " + path);
if(path.endsWith("/")) path += "index.htm";
if(path.indexOf("sec") > -1) return false;
String contentType = getContentType(request, path);
String pathWithGz = path + ".gz";
if(SPIFFS.exists(pathWithGz)){
request->send(SPIFFS, pathWithGz, contentType);
/*String pathWithGz = path + ".gz";
if(WLED_FS.exists(pathWithGz)){
request->send(WLED_FS, pathWithGz, contentType);
return true;
}
if(SPIFFS.exists(path)) {
request->send(SPIFFS, path, contentType);
}*/
if(WLED_FS.exists(path)) {
request->send(WLED_FS, path, contentType);
return true;
}
return false;
}
#else
bool handleFileRead(AsyncWebServerRequest*, String path){return false;}
#endif

View File

@ -39,7 +39,7 @@ const char PAGE_update[] PROGMEM = R"=====(<!DOCTYPE html><html><head><meta cont
<title>WLED Update</title><script>function B(){window.history.back()}</script>
<style>
.bt{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;display:inline-block;font-size:20px;margin:8px;margin-top:12px}input[type=file]{font-size:16px}body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;line-height:200%}
</style></head><body><h2>WLED Software Update</h2>Installed version: 0.10.2<br>
</style></head><body><h2>WLED Software Update</h2>Installed version: 0.11.0p<br>
Download the latest binary: <a
href="https://github.com/Aircoookie/WLED/releases" target="_blank"><img
src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square">
@ -76,6 +76,18 @@ update();var tmout=null;function update(){if(document.hidden)return clearTimeout
</script></body></html>)=====";
// Autogenerated from wled00/data/404.htm, do not edit!!
const char PAGE_404[] PROGMEM = R"=====(<!DOCTYPE html><html><head><meta charset="utf-8"><meta
content="width=device-width" name="viewport"><meta name="theme-color"
content="#222222"><title>Not found</title><style>
body{font-family:Verdana,Helvetica,sans-serif;text-align:center;background-color:#222;margin:0;color:#fff}img{width:400px;max-width:50%;image-rendering:pixelated;image-rendering:crisp-edges;margin:25px 0 -10px 0}button{outline:0;cursor:pointer;padding:8px;margin:10px;width:230px;text-transform:uppercase;font-family:helvetica;font-size:19px;background-color:#333;color:#fff;border:0 solid #fff;border-radius:25px}
</style></head><body><img alt=""
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsEAAA7BAbiRa+0AAAFMSURBVFhH7ZTfbYNADMaPKDNUrJA85CEjdIjOgNQV+sASlZgmI/AIK6AuQfngnDrmjtpHItQ/P+l0juHsz2cH9+fJ/G7nreldfnDnp+ln/ZIlxbIfQmIwJOekCrEJ8FUvASEWEXoBiuSERcTO75uhuwFWff86bi57n3ZC+rW3YLqB5rn11ldCEPNr2LwFJgHHy8G1bTsu3oKYX4N5BrQ8ZAYewSoBGDjr0ElWCUC/rT2X7MqynL7tG4Dc45BwEYM9H5w7DqHMdfNCURR9nue3Iobk55MtOYeLoOQ8vmoG6o+0FaLrOm9FwC3wayLgx5I2WHpGIGYorulfgPYQ3AZLz4hQ9TMBVVVleJGrRUWz2YgQOg8bPjzzrit7vwcRQb5NTiARRPPzMYItoCpoWZITMkao+mRkddpqQ6z6FN+DfwFJrOm55GfewC/CuU/E4tQYg7BPYQAAAABJRU5ErkJggg==">
<h1>404 Not Found</h1><b>Akemi does not know where you are headed...</b><br><br>
<button onclick='window.location.href="/sliders"'>Back to controls</button>
</body></html>)=====";
// Autogenerated from wled00/data/favicon.ico, do not edit!!
const uint16_t favicon_length = 954;
const uint8_t favicon[] PROGMEM = {

View File

@ -109,8 +109,8 @@ value="3">RBG</option><option value="4">BGR</option><option value="5">GBR
</option></select><h3>Defaults</h3>Turn LEDs on after power up/reset: <input
type="checkbox" name="BO"><br>Default brightness: <input name="CA"
type="number" min="0" max="255" required> (0-255)<br><br>Apply preset <input
name="BP" type="number" min="0" max="16" required> at boot (0 uses defaults)<br>
- <i>or</i> -<br>Set current preset cycle setting as boot default: <input
name="BP" type="number" min="0" max="250" required> at boot (0 uses defaults)
<br>- <i>or</i> -<br>Set current preset cycle setting as boot default: <input
type="checkbox" name="PC"><br><br>Use Gamma correction for color: <input
type="checkbox" name="GC"> (strongly recommended)<br>
Use Gamma correction for brightness: <input type="checkbox" name="GB">
@ -283,7 +283,7 @@ type="submit">Save</button></form></body></html>)=====";
// Autogenerated from wled00/data/settings_time.htm, do not edit!!
const char PAGE_settings_time[] PROGMEM = R"=====(<!DOCTYPE html><html><head><meta name="viewport" content="width=500"><meta
charset="utf-8"><title>Time Settings</title><script>
var d=document;function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings")}function B(){window.open("/settings","_self")}function S(){BTa(),GetV(),Cs(),FC()}function gId(t){return d.getElementById(t)}function Cs(){gId("cac").style.display="none",gId("coc").style.display="block",gId("ccc").style.display="none",gId("ca").selected&&(gId("cac").style.display="block"),gId("cc").selected&&(gId("coc").style.display="none",gId("ccc").style.display="block"),gId("cn").selected&&(gId("coc").style.display="none")}function BTa(){var t="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Macro</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";for(i=0;i<8;i++)for(t+='<tr><td><input name="W'+i+'" id="W'+i+'" type="number" style="display:none"><input id="W'+i+'0" type="checkbox"></td><td><input name="H'+i+'" type="number" min="0" max="24"></td><td><input name="N'+i+'" type="number" min="0" max="59"></td><td><input name="T'+i+'" type="number" min="0" max="16"></td>',j=1;j<8;j++)t+='<td><input id="W'+i+j+'" type="checkbox"></td>';gId("TMT").innerHTML=t}function FC(){for(j=0;j<8;j++)for(i=0;i<8;i++)gId("W"+i+j).checked=gId("W"+i).value>>j&1}function Wd(){for(a=[0,0,0,0,0,0,0,0],i=0;i<8;i++){for(m=1,j=0;j<8;j++)a[i]+=gId("W"+i+j).checked*m,m*=2;gId("W"+i).value=a[i]}}function GetV() {
var d=document;function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings")}function B(){window.open("/settings","_self")}function S(){BTa(),GetV(),Cs(),FC()}function gId(t){return d.getElementById(t)}function Cs(){gId("cac").style.display="none",gId("coc").style.display="block",gId("ccc").style.display="none",gId("ca").selected&&(gId("cac").style.display="block"),gId("cc").selected&&(gId("coc").style.display="none",gId("ccc").style.display="block"),gId("cn").selected&&(gId("coc").style.display="none")}function BTa(){var t="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Preset</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";for(i=0;i<8;i++)for(t+='<tr><td><input name="W'+i+'" id="W'+i+'" type="number" style="display:none"><input id="W'+i+'0" type="checkbox"></td><td><input name="H'+i+'" type="number" min="0" max="24"></td><td><input name="N'+i+'" type="number" min="0" max="59"></td><td><input name="T'+i+'" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W'+i+j+'" type="checkbox"></td>';gId("TMT").innerHTML=t}function FC(){for(j=0;j<8;j++)for(i=0;i<8;i++)gId("W"+i+j).checked=gId("W"+i).value>>j&1}function Wd(){for(a=[0,0,0,0,0,0,0,0],i=0;i<8;i++){for(m=1,j=0;j<8;j++)a[i]+=gId("W"+i+j).checked*m,m*=2;gId("W"+i).value=a[i]}}function GetV() {
%CSS%%SCSS%</head><body onload="S()"><form
id="form_s" name="Sf" method="post" onsubmit="Wd()"><div class="helpB"><button
type="button" onclick="H()">?</button></div><button type="button" onclick="B()">
@ -299,47 +299,40 @@ CST(AWST)</option><option value="10">JST(KST)</option><option value="11">
AEST/AEDT</option><option value="12">NZST/NZDT</option><option value="13">
North Korea</option><option value="14">IST (India)</option><option value="15">
CA-Saskatchewan</option><option value="16">ACST</option><option value="17">
ACST/ACDT</option></select><br>UTC offset: <input name="UO" type="number"
min="-65500" max="65500" required> seconds (max. 18 hours)<br>
Current local time is <span class="times">unknown</span>.<h3>Clock</h3>
Clock Overlay: <select name="OL" onchange="Cs()"><option value="0" id="cn"
selected="selected">None</option><option value="1" id="ca">Analog Clock</option>
<option value="2">Single Digit Clock</option><option value="3" id="cc">
Cronixie Clock</option></select><br><div id="coc">First LED: <input name="O1"
type="number" min="0" max="255" required> Last LED: <input name="O2"
type="number" min="0" max="255" required><br><div id="cac">12h LED: <input
name="OM" type="number" min="0" max="255" required><br>Show 5min marks: <input
type="checkbox" name="O5"><br></div>Seconds (as trail): <input type="checkbox"
name="OS"><br></div><div id="ccc">Cronixie Display: <input name="CX"
maxlength="6"><br>Cronixie Backlight: <input type="checkbox" name="CB"><br>
</div>Countdown Mode: <input type="checkbox" name="CE"><br>Countdown Goal:<br>
Year: 20 <input name="CY" type="number" min="0" max="99" required> Month: <input
name="CI" type="number" min="1" max="12" required> Day: <input name="CD"
type="number" min="1" max="31" required><br>Hour: <input name="CH"
type="number" min="0" max="23" required> Minute: <input name="CM" type="number"
min="0" max="59" required> Second: <input name="CS" type="number" min="0"
max="59" required><br><h3>Advanced Macros</h3>Define API macros here:<br>1:
<input name="M1" maxlength="64"><br>2: <input name="M2" maxlength="64"><br>3:
<input name="M3" maxlength="64"><br>4: <input name="M4" maxlength="64"><br>5:
<input name="M5" maxlength="64"><br>6: <input name="M6" maxlength="64"><br>7:
<input name="M7" maxlength="64"><br>8: <input name="M8" maxlength="64"><br>9:
<input name="M9" maxlength="64"><br>10: <input name="M10" maxlength="64"><br>
11: <input name="M11" maxlength="64"><br>12: <input name="M12" maxlength="64">
<br>13: <input name="M13" maxlength="64"><br>14: <input name="M14"
maxlength="64"><br>15: <input name="M15" maxlength="64"><br>16: <input
name="M16" maxlength="64"><br><br><i>
Use 0 for the default action instead of a macro</i><br>Boot Macro: <input
name="MB" type="number" min="0" max="16" required><br>Alexa On/Off Macros:
<input name="A0" type="number" min="0" max="16" required> <input name="A1"
type="number" min="0" max="16" required><br>Button short press macro: Macro:
<input name="MP" type="number" min="0" max="16" required><br>Long Press: <input
name="ML" type="number" min="0" max="16" required> Double press: <input
name="MD" type="number" min="0" max="16" required><br>Countdown-Over Macro:
<input name="MC" type="number" min="0" max="16" required><br>
Timed-Light-Over Macro: <input name="MN" type="number" min="0" max="16"
required><br>Time-Controlled Macros:<br><div style="display:inline-block"><table
id="TMT"></table></div><hr><button type="button" onclick="B()">Back</button>
<button type="submit">Save</button></form></body></html>)=====";
ACST/ACDT</option><option value="18">HST (Hawaii)</option></select><br>
UTC offset: <input name="UO" type="number" min="-65500" max="65500" required>
seconds (max. 18 hours)<br>Current local time is <span class="times">unknown
</span>.<h3>Clock</h3>Clock Overlay: <select name="OL" onchange="Cs()"><option
value="0" id="cn" selected="selected">None</option><option value="1" id="ca">
Analog Clock</option><option value="2">Single Digit Clock</option><option
value="3" id="cc">Cronixie Clock</option></select><br><div id="coc">First LED:
<input name="O1" type="number" min="0" max="255" required> Last LED: <input
name="O2" type="number" min="0" max="255" required><br><div id="cac">12h LED:
<input name="OM" type="number" min="0" max="255" required><br>Show 5min marks:
<input type="checkbox" name="O5"><br></div>Seconds (as trail): <input
type="checkbox" name="OS"><br></div><div id="ccc">Cronixie Display: <input
name="CX" maxlength="6"><br>Cronixie Backlight: <input type="checkbox"
name="CB"><br></div>Countdown Mode: <input type="checkbox" name="CE"><br>
Countdown Goal:<br>Year: 20 <input name="CY" type="number" min="0" max="99"
required> Month: <input name="CI" type="number" min="1" max="12" required> Day:
<input name="CD" type="number" min="1" max="31" required><br>Hour: <input
name="CH" type="number" min="0" max="23" required> Minute: <input name="CM"
type="number" min="0" max="59" required> Second: <input name="CS" type="number"
min="0" max="59" required><br><h3>Macro presets</h3><b>Macros have moved!</b>
<br><i>
Presets now also can be used as macros to save both JSON and HTTP API commands.
<br>Just enter the preset id below!</i> <i>
Use 0 for the default action instead of a preset</i><br>Alexa On/Off Preset:
<input name="A0" type="number" min="0" max="250" required> <input name="A1"
type="number" min="0" max="250" required><br>Button short press Preset: <input
name="MP" type="number" min="0" max="250" required><br>Long Press: <input
name="ML" type="number" min="0" max="250" required> Double press: <input
name="MD" type="number" min="0" max="250" required><br>Countdown-Over Preset:
<input name="MC" type="number" min="0" max="250" required><br>
Timed-Light-Over Presets: <input name="MN" type="number" min="0" max="250"
required><br><h3>Time-controlled presets</h3><div style="display:inline-block">
<table id="TMT"></table></div><hr><button type="button" onclick="B()">Back
</button><button type="submit">Save</button></form></body></html>)=====";
// Autogenerated from wled00/data/settings_sec.htm, do not edit!!
@ -364,12 +357,13 @@ HTTP traffic is unencrypted. An attacker in the same network can intercept form
<h3>Software Update</h3><button type="button" onclick="U()">Manual OTA Update
</button><br>Enable ArduinoOTA: <input type="checkbox" name="AO"><br><h3>About
</h3><a href="https://github.com/Aircoookie/WLED/" target="_blank">WLED</a>
version 0.10.2<br><br><a
version 0.11.0p<br><br><a
href="https://github.com/Aircoookie/WLED/wiki/Contributors-&-About"
target="_blank">Contributors, dependencies and special thanks</a><br>
A huge thank you to everyone who helped me create WLED!<br><br>
(c) 2016-2020 Christian Schwinne<br><i>Licensed under the <a href="https://github.com/Aircoookie/WLED/blob/master/LICENSE" target="_blank">MIT license</a></i><br>
<br>Server message: <span class="sip">Response error!</span><hr><button
type="button" onclick="B()">Back</button><button type="submit">Save & Reboot
</button></form></body></html>)=====";
(c) 2016-2020 Christian Schwinne<br><i>Licensed under the <a
href="https://github.com/Aircoookie/WLED/blob/master/LICENSE" target="_blank">
MIT license</a></i><br><br>Server message: <span class="sip">Response error!
</span><hr><button type="button" onclick="B()">Back</button><button
type="submit">Save & Reboot</button></form></body></html>)=====";

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@ void handleHue()
colorUpdated(NOTIFIER_CALL_MODE_HUE); hueReceived = false;
if (hueStoreAllowed && hueNewKey)
{
saveSettingsToEEPROM(); //save api key
serializeConfigSec(); //save api key
hueStoreAllowed = false;
hueNewKey = false;
}

View File

@ -29,7 +29,7 @@ bool decodeIRCustom(uint32_t code)
{
//just examples, feel free to modify or remove
case IRCUSTOM_ONOFF : toggleOnOff(); break;
case IRCUSTOM_MACRO1 : applyMacro(1); break;
case IRCUSTOM_MACRO1 : applyPreset(1); break;
default: return false;
}

View File

@ -11,7 +11,7 @@ void deserializeSegment(JsonObject elem, byte it)
{
WS2812FX::Segment& seg = strip.getSegment(id);
uint16_t start = elem[F("start")] | seg.start;
int stop = elem[F("stop")] | -1;
int stop = elem["stop"] | -1;
if (stop < 0) {
uint16_t len = elem[F("len")];
@ -36,29 +36,38 @@ void deserializeSegment(JsonObject elem, byte it)
{
for (uint8_t i = 0; i < 3; i++)
{
int rgbw[] = {0,0,0,0};
bool colValid = false;
JsonArray colX = colarr[i];
if (colX.isNull()) break;
byte sz = colX.size();
if (sz > 0 && sz < 5)
{
int rgbw[] = {0,0,0,0};
byte cp = copyArray(colX, rgbw);
if (cp == 1) {
if (rgbw[0] == 0) seg.colors[i] = 0;
else {
byte ctrgb[] = {0,0,0,0};
colorKtoRGB(rgbw[0], ctrgb);
for (uint8_t c = 0; c < 3; c++) rgbw[c] = ctrgb[c];
}
}
if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment
{
if (i == 0) {col[0] = rgbw[0]; col[1] = rgbw[1]; col[2] = rgbw[2]; col[3] = rgbw[3];}
if (i == 1) {colSec[0] = rgbw[0]; colSec[1] = rgbw[1]; colSec[2] = rgbw[2]; colSec[3] = rgbw[3];}
} else {
seg.colors[i] = ((rgbw[3] << 24) | ((rgbw[0]&0xFF) << 16) | ((rgbw[1]&0xFF) << 8) | ((rgbw[2]&0xFF)));
if (colX.isNull()) {
byte brgbw[] = {0,0,0,0};
const char* hexCol = colarr[i];
if (hexCol == nullptr) { //Kelvin color temperature (or invalid), e.g 2400
int kelvin = colarr[i] | -1;
if (kelvin < 0) continue;
if (kelvin == 0) seg.colors[i] = 0;
if (kelvin > 0) colorKtoRGB(kelvin, brgbw);
colValid = true;
} else { //HEX string, e.g. "FFAA00"
colValid = colorFromHexString(brgbw, hexCol);
}
for (uint8_t c = 0; c < 4; c++) rgbw[c] = brgbw[c];
} else { //Array of ints (RGB or RGBW color), e.g. [255,160,0]
byte sz = colX.size();
if (sz == 0) continue; //do nothing on empty array
byte cp = copyArray(colX, rgbw, 4);
if (cp == 1 && rgbw[0] == 0) seg.colors[i] = 0;
colValid = true;
}
if (!colValid) continue;
if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment
{
if (i == 0) {col[0] = rgbw[0]; col[1] = rgbw[1]; col[2] = rgbw[2]; col[3] = rgbw[3];}
if (i == 1) {colSec[0] = rgbw[0]; colSec[1] = rgbw[1]; colSec[2] = rgbw[2]; colSec[3] = rgbw[3];}
} else { //normal case, apply directly to segment (=> no transition!)
seg.colors[i] = ((rgbw[3] << 24) | ((rgbw[0]&0xFF) << 16) | ((rgbw[1]&0xFF) << 8) | ((rgbw[2]&0xFF)));
}
}
}
@ -147,9 +156,6 @@ bool deserializeState(JsonObject root)
{
strip.applyToAllSelected = false;
bool stateResponse = root[F("v")] | false;
int ps = root[F("ps")] | -1;
if (ps >= 0) applyPreset(ps);
bri = root["bri"] | bri;
@ -173,26 +179,30 @@ bool deserializeState(JsonObject root)
int cy = root[F("pl")] | -2;
if (cy > -2) presetCyclingEnabled = (cy >= 0);
JsonObject ccnf = root[F("ccnf")];
JsonObject ccnf = root["ccnf"];
presetCycleMin = ccnf[F("min")] | presetCycleMin;
presetCycleMax = ccnf[F("max")] | presetCycleMax;
tr = ccnf[F("time")] | -1;
if (tr >= 2) presetCycleTime = tr;
JsonObject nl = root[F("nl")];
JsonObject nl = root["nl"];
nightlightActive = nl["on"] | nightlightActive;
nightlightDelayMins = nl[F("dur")] | nightlightDelayMins;
nightlightMode = nl[F("fade")] | nightlightMode; //deprecated
nightlightMode = nl[F("fade")] | nightlightMode; //deprecated, remove for v0.12.0
nightlightMode = nl[F("mode")] | nightlightMode;
nightlightTargetBri = nl[F("tbri")] | nightlightTargetBri;
JsonObject udpn = root[F("udpn")];
JsonObject udpn = root["udpn"];
notifyDirect = udpn[F("send")] | notifyDirect;
receiveNotifications = udpn[F("recv")] | receiveNotifications;
bool noNotification = udpn[F("nn")]; //send no notification just for this request
int timein = root[F("time")] | -1;
if (timein != -1) setTime(timein);
unsigned long timein = root[F("time")] | -1;
if (timein != -1) {
if (millis() - ntpLastSyncTime > 50000000L) setTime(timein);
if (presetsModifiedTime == 0) presetsModifiedTime = timein;
}
doReboot = root[F("rb")] | doReboot;
realtimeOverride = root[F("lor")] | realtimeOverride;
@ -203,7 +213,7 @@ bool deserializeState(JsonObject root)
if (strip.getMainSegmentId() != prevMain) setValuesFromMainSeg();
int it = 0;
JsonVariant segVar = root[F("seg")];
JsonVariant segVar = root["seg"];
if (segVar.is<JsonObject>())
{
int id = segVar[F("id")] | -1;
@ -238,23 +248,44 @@ bool deserializeState(JsonObject root)
usermods.readFromJsonState(root);
int ps = root[F("psave")] | -1;
if (ps > 0) {
savePreset(ps, true, nullptr, root);
} else {
ps = root[F("pdel")] | -1; //deletion
if (ps > 0) {
deletePreset(ps);
}
ps = root["ps"] | -1; //load preset (clears state request!)
if (ps >= 0) {applyPreset(ps); return stateResponse;}
//HTTP API commands
const char* httpwin = root["win"];
if (httpwin) {
String apireq = "win&";
apireq += httpwin;
handleSet(nullptr, apireq, false);
}
}
JsonObject playlist = root[F("playlist")];
if (!playlist.isNull()) {
loadPlaylist(playlist); return stateResponse;
}
colorUpdated(noNotification ? NOTIFIER_CALL_MODE_NO_NOTIFY : NOTIFIER_CALL_MODE_DIRECT_CHANGE);
//write presets to flash directly?
bool persistSaves = !(root[F("np")] | false);
ps = root[F("psave")] | -1;
if (ps >= 0) savePreset(ps, persistSaves);
return stateResponse;
}
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id)
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset, bool segmentBounds)
{
root[F("id")] = id;
root[F("start")] = seg.start;
root[F("stop")] = seg.stop;
root[F("len")] = seg.stop - seg.start;
if (segmentBounds) {
root[F("start")] = seg.start;
root["stop"] = seg.stop;
}
if (!forPreset) root[F("len")] = seg.stop - seg.start;
root[F("grp")] = seg.grouping;
root[F("spc")] = seg.spacing;
root["on"] = seg.getOption(SEG_OPTION_ON);
@ -291,44 +322,47 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id)
root[F("mi")] = seg.getOption(SEG_OPTION_MIRROR);
}
void serializeState(JsonObject root)
{
if (errorFlag) root[F("error")] = errorFlag;
root["on"] = (bri > 0);
root["bri"] = briLast;
root[F("transition")] = transitionDelay/100; //in 100ms
root[F("ps")] = currentPreset;
root[F("pss")] = savedPresets;
root[F("pl")] = (presetCyclingEnabled) ? 0: -1;
usermods.addToJsonState(root);
//temporary for preset cycle
JsonObject ccnf = root.createNestedObject("ccnf");
ccnf[F("min")] = presetCycleMin;
ccnf[F("max")] = presetCycleMax;
ccnf[F("time")] = presetCycleTime;
JsonObject nl = root.createNestedObject("nl");
nl["on"] = nightlightActive;
nl[F("dur")] = nightlightDelayMins;
nl[F("fade")] = (nightlightMode > NL_MODE_SET); //deprecated
nl[F("mode")] = nightlightMode;
nl[F("tbri")] = nightlightTargetBri;
if (nightlightActive) {
nl[F("rem")] = (nightlightDelayMs - (millis() - nightlightStartTime)) / 1000; // seconds remaining
} else {
nl[F("rem")] = -1;
void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segmentBounds)
{
if (includeBri) {
root["on"] = (bri > 0);
root["bri"] = briLast;
root[F("transition")] = transitionDelay/100; //in 100ms
}
JsonObject udpn = root.createNestedObject("udpn");
udpn[F("send")] = notifyDirect;
udpn[F("recv")] = receiveNotifications;
if (!forPreset) {
if (errorFlag) root[F("error")] = errorFlag;
root[F("ps")] = currentPreset;
root[F("pss")] = savedPresets;
root[F("pl")] = (presetCyclingEnabled) ? 0: -1;
usermods.addToJsonState(root);
root[F("lor")] = realtimeOverride;
//temporary for preset cycle
JsonObject ccnf = root.createNestedObject("ccnf");
ccnf[F("min")] = presetCycleMin;
ccnf[F("max")] = presetCycleMax;
ccnf[F("time")] = presetCycleTime;
JsonObject nl = root.createNestedObject("nl");
nl["on"] = nightlightActive;
nl[F("dur")] = nightlightDelayMins;
nl[F("fade")] = (nightlightMode > NL_MODE_SET); //deprecated
nl[F("mode")] = nightlightMode;
nl[F("tbri")] = nightlightTargetBri;
if (nightlightActive) {
nl[F("rem")] = (nightlightDelayMs - (millis() - nightlightStartTime)) / 1000; // seconds remaining
} else {
nl[F("rem")] = -1;
}
JsonObject udpn = root.createNestedObject("udpn");
udpn[F("send")] = notifyDirect;
udpn[F("recv")] = receiveNotifications;
root[F("lor")] = realtimeOverride;
}
root[F("mainseg")] = strip.getMainSegmentId();
@ -339,7 +373,10 @@ void serializeState(JsonObject root)
if (sg.isActive())
{
JsonObject seg0 = seg.createNestedObject();
serializeSegment(seg0, sg, s);
serializeSegment(seg0, sg, s, forPreset, segmentBounds);
} else if (forPreset && segmentBounds) { //disable segments not part of preset
JsonObject seg0 = seg.createNestedObject();
seg0["stop"] = 0;
}
}
}
@ -422,6 +459,11 @@ void serializeInfo(JsonObject root)
wifi_info[F("rssi")] = qrssi;
wifi_info[F("signal")] = getSignalQuality(qrssi);
wifi_info[F("channel")] = WiFi.channel();
JsonObject fs_info = root.createNestedObject("fs");
fs_info["u"] = fsBytesUsed / 1000;
fs_info["t"] = fsBytesTotal / 1000;
fs_info[F("pmt")] = presetsModifiedTime;
#ifdef ARDUINO_ARCH_ESP32
#ifdef WLED_DEBUG
@ -449,6 +491,7 @@ void serializeInfo(JsonObject root)
root[F("freeheap")] = ESP.getFreeHeap();
root[F("uptime")] = millis()/1000 + rolloverMillis*4294967;
usermods.addToJsonInfo(root);
byte os = 0;

View File

@ -282,7 +282,7 @@ void handleNightlight()
}
updateBlynk();
if (macroNl > 0)
applyMacro(macroNl);
applyPreset(macroNl);
nightlightActiveOld = false;
}
} else if (nightlightActiveOld) //early de-init
@ -303,7 +303,7 @@ void handleNightlight()
if (bri == 0 || nightlightActive) return;
if (presetCycCurr < presetCycleMin || presetCycCurr > presetCycleMax) presetCycCurr = presetCycleMin;
applyPreset(presetCycCurr,presetApplyBri);
applyPreset(presetCycCurr);
presetCycCurr++;
if (presetCycCurr > 16) presetCycCurr = 1;
colorUpdated(NOTIFIER_CALL_MODE_PRESET_CYCLE);

View File

@ -96,7 +96,7 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties
}
} else if (strcmp(topic, "") == 0)
{
parseMQTTBriPayload(payload);
parseMQTTBriPayload(payload);
}
}

25
wled00/my_config_sample.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
/*
* Welcome!
* You can use the file "my_config.h" to make changes to the way WLED is compiled!
* It is possible to enable and disable certain features as well as set defaults for some runtime changeable settings.
*
* How to use:
* PlatformIO: Just compile the unmodified code once! The file "my_config.h" will be generated automatically and now you can make your changes.
*
* ArduinoIDE: Make a copy of this file and name it "my_config.h". Go to wled.h and uncomment "#define WLED_USE_MY_CONFIG" in the top of the file.
*
* DO NOT make changes to the "my_config_sample.h" file directly! Your changes will not be applied.
*/
// force the compiler to show a warning to confirm that this file is included
#warning **** my_config.h: Settings from this file are honored ****
/* Uncomment to use your WIFI settings as defaults
//WARNING: this will hardcode these as the default even after a factory reset
#define CLIENT_SSID "Your_SSID"
#define CLIENT_PASS "Your_Password"
*/
//#define MAX_LEDS 1500 //Maximum total LEDs. More than 1500 might create a low memory situation on ESP8266.

View File

@ -24,6 +24,7 @@ Timezone* tz;
#define TZ_SASKACHEWAN 15
#define TZ_AUSTRALIA_NORTHERN 16
#define TZ_AUSTRALIA_SOUTHERN 17
#define TZ_HAWAII 18
#define TZ_INIT 255
byte tzCurrent = TZ_INIT; //uninitialized
@ -119,6 +120,11 @@ void updateTimezone() {
tcrStandard = {First, Sun, Apr, 3, 570}; //ACST = UTC + 9.5 hours
break;
}
case TZ_HAWAII : {
tcrDaylight = {Last, Sun, Mar, 1, -600}; //HST = UTC - 10 hours
tcrStandard = tcrDaylight;
break;
}
}
tzCurrent = currentTimezone;
@ -266,7 +272,7 @@ void checkTimers()
&& (timerWeekday[i] & 0x01) //timer is enabled
&& timerWeekday[i] >> weekdayMondayFirst() & 0x01) //timer should activate at current day of week
{
applyMacro(timerMacro[i]);
applyPreset(timerMacro[i]);
}
}
}

54
wled00/pin_manager.cpp Normal file
View File

@ -0,0 +1,54 @@
#include "wled.h"
/*
* Registers pins so there is no attempt for two interfaces to use the same pin
*/
void PinManagerClass::deallocatePin(byte gpio)
{
if (!isPinOk(gpio, false)) return;
byte by = gpio >> 3;
byte bi = gpio - 8*by;
bitWrite(pinAlloc[by], bi, false);
}
bool PinManagerClass::allocatePin(byte gpio, bool output)
{
if (!isPinOk(gpio, output)) return false;
if (isPinAllocated(gpio)) {
DEBUG_PRINT(F("Attempted duplicate allocation of pin "));
DEBUG_PRINTLN(gpio);
return false;
}
byte by = gpio >> 3;
byte bi = gpio - 8*by;
bitWrite(pinAlloc[by], bi, true);
return true;
}
bool PinManagerClass::isPinAllocated(byte gpio)
{
if (!isPinOk(gpio, false)) return true;
byte by = gpio >> 3;
byte bi = gpio - 8*by;
return bitRead(pinAlloc[by], bi);
}
bool PinManagerClass::isPinOk(byte gpio, bool output)
{
if (gpio < 6) return true;
if (gpio < 12) return false; //SPI flash pins
#ifdef ESP8266
if (gpio < 17) return true;
#else //ESP32
if (gpio < 34) return true;
if (gpio < 40 && !output) return true; //34-39 input only
#endif
return false;
}

106
wled00/playlist.cpp Normal file
View File

@ -0,0 +1,106 @@
#include "wled.h"
/*
* Handles playlists, timed sequences of presets
*/
typedef struct PlaylistEntry {
uint8_t preset;
uint16_t dur;
uint16_t tr;
} ple;
byte playlistRepeat = 1;
byte playlistEndPreset = 0;
uint8_t* playlistEntries;
byte playlistLen;
int8_t playlistIndex = -1;
uint16_t playlistEntryDur = 0;
void loadPlaylist(JsonObject playlistObj) {
delete playlistEntries;
playlistIndex = -1; playlistEntryDur = 0;
JsonArray presets = playlistObj["ps"];
playlistLen = presets.size();
if (playlistLen == 0) return;
if (playlistLen > 100) playlistLen = 100;
uint16_t dataSize = sizeof(ple) * playlistLen;
playlistEntries = new byte[dataSize];
PlaylistEntry* entries = reinterpret_cast<PlaylistEntry*>(playlistEntries);
byte it = 0;
for (int ps : presets) {
if (it >= playlistLen) break;
entries[it].preset = ps;
it++;
}
it = 0;
JsonArray durations = playlistObj["dur"];
if (durations.isNull()) {
entries[0].dur = playlistObj["dur"] | 100;
it = 1;
} else {
for (int dur : durations) {
if (it >= playlistLen) break;
entries[it].dur = dur;
it++;
}
}
for (int i = it; i < playlistLen; i++) entries[i].dur = entries[it -1].dur;
it = 0;
JsonArray tr = playlistObj["transition"];
if (tr.isNull()) {
entries[0].tr = playlistObj["transition"] | (transitionDelay / 100);
it = 1;
} else {
for (int transition : tr) {
if (it >= playlistLen) break;
entries[it].tr = transition;
it++;
}
}
for (int i = it; i < playlistLen; i++) entries[i].tr = entries[it -1].tr;
playlistRepeat = playlistObj[F("repeat")] | 0;
playlistEndPreset = playlistObj[F("end")] | 0;
currentPlaylist = 0; //TODO here we need the preset ID where the playlist is saved
}
void handlePlaylist()
{
if (currentPlaylist < 0 || playlistEntries == nullptr || presetCyclingEnabled) return;
if (millis() - presetCycledTime > (100*playlistEntryDur))
{
presetCycledTime = millis();
if (bri == 0 || nightlightActive) return;
playlistIndex++;
if (playlistIndex >= playlistLen) {
playlistIndex = 0;
if (playlistRepeat == 1) { //stop
currentPlaylist = -1;
delete playlistEntries;
playlistEntries = nullptr;
if (playlistEndPreset) applyPreset(playlistEndPreset);
return;
}
if (playlistRepeat > 1) playlistRepeat--;
}
PlaylistEntry* entries = reinterpret_cast<PlaylistEntry*>(playlistEntries);
jsonTransitionOnce = true;
transitionDelayTemp = entries[playlistIndex].tr * 100;
applyPreset(entries[playlistIndex].preset);
playlistEntryDur = entries[playlistIndex].dur;
if (playlistEntryDur == 0) playlistEntryDur = 10;
}
}

76
wled00/presets.cpp Normal file
View File

@ -0,0 +1,76 @@
#include "wled.h"
/*
* Methods to handle saving and loading presets to/from the filesystem
*/
bool applyPreset(byte index)
{
if (fileDoc) {
errorFlag = readObjectFromFileUsingId("/presets.json", index, fileDoc) ? ERR_NONE : ERR_FS_PLOAD;
JsonObject fdo = fileDoc->as<JsonObject>();
if (fdo["ps"] == index) fdo.remove("ps"); //remove load request for same presets to prevent recursive crash
#ifdef WLED_DEBUG_FS
serializeJson(*fileDoc, Serial);
#endif
deserializeState(fdo);
} else {
DEBUGFS_PRINTLN(F("Make read buf"));
DynamicJsonDocument fDoc(JSON_BUFFER_SIZE);
errorFlag = readObjectFromFileUsingId("/presets.json", index, &fDoc) ? ERR_NONE : ERR_FS_PLOAD;
JsonObject fdo = fDoc.as<JsonObject>();
if (fdo["ps"] == index) fdo.remove("ps");
#ifdef WLED_DEBUG_FS
serializeJson(fDoc, Serial);
#endif
deserializeState(fdo);
}
if (!errorFlag) {
currentPreset = index;
isPreset = true;
return true;
}
return false;
}
void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj)
{
if (index == 0 || index > 250) return;
bool docAlloc = fileDoc;
JsonObject sObj = saveobj;
if (!docAlloc) {
DEBUGFS_PRINTLN(F("Allocating saving buffer"));
fileDoc = new DynamicJsonDocument(JSON_BUFFER_SIZE);
sObj = fileDoc->to<JsonObject>();
if (pname) sObj["n"] = pname;
} else {
DEBUGFS_PRINTLN(F("Reuse recv buffer"));
sObj.remove(F("psave"));
sObj.remove(F("v"));
}
if (!sObj["o"]) {
DEBUGFS_PRINTLN(F("Save current state"));
serializeState(sObj, true, sObj["ib"], sObj["sb"]);
currentPreset = index;
}
sObj.remove("o");
sObj.remove("ib");
sObj.remove("sb");
sObj.remove(F("error"));
sObj.remove(F("time"));
writeObjectToFileUsingId("/presets.json", index, fileDoc);
if (!docAlloc) delete fileDoc;
presetsModifiedTime = now(); //unix time
updateFSInfo();
}
void deletePreset(byte index) {
StaticJsonDocument<24> empty;
writeObjectToFileUsingId("/presets.json", index, &empty);
presetsModifiedTime = now(); //unix time
updateFSInfo();
}

View File

@ -1,6 +1,5 @@
#include "wled.h"
/*
* Receives client input
*/
@ -234,13 +233,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
countdownMin = request->arg(F("CM")).toInt();
countdownSec = request->arg(F("CS")).toInt();
for (int i=1;i<17;i++)
{
String a = "M"+String(i);
if (request->hasArg(a.c_str())) saveMacro(i,request->arg(a),false);
}
macroBoot = request->arg(F("MB")).toInt();
macroAlexaOn = request->arg(F("A0")).toInt();
macroAlexaOff = request->arg(F("A1")).toInt();
macroButton = request->arg(F("MP")).toInt();
@ -273,7 +265,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
{
if (request->hasArg(F("RS"))) //complete factory reset
{
clearEEPROM();
WLED_FS.format();
serveMessage(request, 200, F("All Settings erased."), F("Connect to WLED-AP to setup again"),255);
doReboot = true;
}
@ -328,7 +320,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
}
#endif
if (subPage != 6 || !doReboot) saveSettingsToEEPROM(); //do not save if factory reset
if (subPage != 6 || !doReboot) serializeConfig(); //do not save if factory reset
if (subPage == 2) {
strip.init(useRGBW,ledCount,skipFirstLed);
}
@ -375,7 +367,7 @@ bool updateVal(const String* req, const char* key, byte* val, byte minv, byte ma
//HTTP API request parser
bool handleSet(AsyncWebServerRequest *request, const String& req)
bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
{
if (!(req.indexOf("win") >= 0)) return false;
@ -383,31 +375,6 @@ bool handleSet(AsyncWebServerRequest *request, const String& req)
DEBUG_PRINT(F("API req: "));
DEBUG_PRINTLN(req);
//write presets and macros saved to flash directly?
bool persistSaves = true;
pos = req.indexOf(F("NP"));
if (pos > 0) {
persistSaves = false;
}
//save macro, requires &MS=<slot>(<macro>) format
pos = req.indexOf(F("&MS="));
if (pos > 0) {
int i = req.substring(pos + 4).toInt();
pos = req.indexOf('(') +1;
if (pos > 0) {
int en = req.indexOf(')');
String mc = req.substring(pos);
if (en > 0) mc = req.substring(pos, en);
saveMacro(i, mc, persistSaves);
}
pos = req.indexOf(F("IN"));
if (pos < 1) XML_response(request);
return true;
//if you save a macro in one request, other commands in that request are ignored due to unwanted behavior otherwise
}
strip.applyToAllSelected = true;
//segment select (sets main segment)
@ -486,15 +453,12 @@ bool handleSet(AsyncWebServerRequest *request, const String& req)
if (v > 100) presetCycleTime = v/100;
}
pos = req.indexOf(F("PA=")); //apply brightness from preset
if (pos > 0) presetApplyBri = (req.charAt(pos+3) != '0');
pos = req.indexOf(F("PS=")); //saves current in preset
if (pos > 0) savePreset(getNumVal(&req, pos), persistSaves);
if (pos > 0) savePreset(getNumVal(&req, pos));
//apply preset
if (updateVal(&req, "PL=", &presetCycCurr, presetCycleMin, presetCycleMax)) {
applyPreset(presetCycCurr, presetApplyBri);
applyPreset(presetCycCurr);
}
//set brightness
@ -595,10 +559,10 @@ bool handleSet(AsyncWebServerRequest *request, const String& req)
overlayCurrent = getNumVal(&req, pos);
}
//apply macro
//apply macro (deprecated, added for compatibility with pre-0.11 automations)
pos = req.indexOf(F("&M="));
if (pos > 0) {
applyMacro(getNumVal(&req, pos));
applyPreset(getNumVal(&req, pos) + 16);
}
//toggle send UDP direct notifications
@ -746,6 +710,8 @@ bool handleSet(AsyncWebServerRequest *request, const String& req)
}
//you can add more if you need
if (!apply) return true; //when called by JSON API, do not call colorUpdated() here
//internal call, does not send XML response
pos = req.indexOf(F("IN"));
if (pos < 1) XML_response(request);

View File

@ -583,7 +583,7 @@ public:
return perc / 255;
}
~Espalexa(){delete devices;} //note: Espalexa is NOT meant to be destructed
~Espalexa(){} //note: Espalexa is NOT meant to be destructed
};
#endif

View File

@ -4,7 +4,7 @@
#include "Arduino.h"
#include <functional>
typedef class EspalexaDevice;
class EspalexaDevice;
typedef std::function<void(uint8_t b)> BrightnessCallbackFunction;
typedef std::function<void(EspalexaDevice* d)> DeviceCallbackFunction;

View File

@ -12,6 +12,8 @@ void UsermodManager::connected() { for (byte i = 0; i < numMods; i++) ums[i]->co
void UsermodManager::addToJsonState(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->addToJsonState(obj); }
void UsermodManager::addToJsonInfo(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->addToJsonInfo(obj); }
void UsermodManager::readFromJsonState(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->readFromJsonState(obj); }
void UsermodManager::addToConfig(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->addToConfig(obj); }
void UsermodManager::readFromConfig(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->readFromConfig(obj); }
bool UsermodManager::add(Usermod* um)
{

View File

@ -132,6 +132,10 @@ void WLED::loop()
if (doReboot)
reset();
if (doCloseFile) {
closeFile();
yield();
}
if (!realtimeMode || realtimeOverride) // block stuff if WARLS/Adalight is enabled
{
@ -142,10 +146,17 @@ void WLED::loop()
ArduinoOTA.handle();
#endif
handleNightlight();
handlePlaylist();
yield();
handleHue();
handleBlynk();
/*if (presetToApply) {
applyPreset(presetToApply);
presetToApply = 0;
}*/
yield();
if (!offMode)
@ -193,17 +204,6 @@ void WLED::loop()
void WLED::setup()
{
EEPROM.begin(EEPSIZE);
ledCount = EEPROM.read(229) + ((EEPROM.read(398) << 8) & 0xFF00);
if (ledCount > MAX_LEDS || ledCount == 0)
ledCount = 30;
#ifdef ESP8266
#if LEDPIN == 3
if (ledCount > MAX_LEDS_DMA)
ledCount = MAX_LEDS_DMA; // DMA method uses too much ram
#endif
#endif
Serial.begin(115200);
Serial.setTimeout(50);
DEBUG_PRINTLN();
@ -224,25 +224,33 @@ void WLED::setup()
DEBUG_PRINTLN(ESP.getFreeHeap());
registerUsermods();
strip.init(EEPROM.read(372), ledCount, EEPROM.read(2204)); // init LEDs quickly
strip.setBrightness(0);
//strip.init(EEPROM.read(372), ledCount, EEPROM.read(2204)); // init LEDs quickly
//strip.setBrightness(0);
DEBUG_PRINT(F("LEDs inited. heap usage ~"));
DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap());
//DEBUG_PRINT(F("LEDs inited. heap usage ~"));
//DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap());
#ifndef WLED_DISABLE_FILESYSTEM
#ifdef ARDUINO_ARCH_ESP32
SPIFFS.begin(true);
#endif
SPIFFS.begin();
bool fsinit = false;
DEBUGFS_PRINTLN(F("Mount FS"));
#ifdef ARDUINO_ARCH_ESP32
fsinit = WLED_FS.begin(true);
#else
fsinit = WLED_FS.begin();
#endif
if (!fsinit) {
DEBUGFS_PRINTLN(F("FS failed!"));
errorFlag = ERR_FS_BEGIN;
} else deEEP();
updateFSInfo();
deserializeConfig();
#if STATUSLED && STATUSLED != LEDPIN
pinMode(STATUSLED, OUTPUT);
#endif
DEBUG_PRINTLN(F("Load EEPROM"));
loadSettingsFromEEPROM(true);
//DEBUG_PRINTLN(F("Load EEPROM"));
//loadSettingsFromEEPROM();
beginStrip();
userSetup();
usermods.setup();
@ -251,8 +259,6 @@ void WLED::setup()
WiFi.persistent(false);
WiFi.onEvent(WiFiEvent);
if (macroBoot > 0)
applyMacro(macroBoot);
Serial.println(F("Ada"));
// generate module IDs
@ -297,18 +303,36 @@ void WLED::setup()
void WLED::beginStrip()
{
// Initialize NeoPixel Strip and button
#ifdef ESP8266
#if LEDPIN == 3
if (ledCount > MAX_LEDS_DMA)
ledCount = MAX_LEDS_DMA; // DMA method uses too much ram
#endif
#endif
if (ledCount > MAX_LEDS || ledCount == 0)
ledCount = 30;
strip.init(useRGBW, ledCount, skipFirstLed);
strip.setBrightness(0);
strip.setShowCallback(handleOverlayDraw);
#ifdef BTNPIN
#if defined(BTNPIN) && BTNPIN > -1
pinManager.allocatePin(BTNPIN, false);
pinMode(BTNPIN, INPUT_PULLUP);
#endif
if (bootPreset > 0)
applyPreset(bootPreset, turnOnAtBoot);
if (bootPreset > 0) applyPreset(bootPreset);
if (turnOnAtBoot) {
bri = (briS > 0) ? briS : 128;
} else {
briLast = briS; bri = 0;
}
colorUpdated(NOTIFIER_CALL_MODE_INIT);
// init relay pin
#if RLYPIN >= 0
pinManager.allocatePin(RLYPIN);
pinMode(RLYPIN, OUTPUT);
#if RLYMDE
digitalWrite(RLYPIN, bri);
@ -318,7 +342,7 @@ void WLED::beginStrip()
#endif
// disable button if it is "pressed" unintentionally
#if defined(BTNPIN) || defined(TOUCHPIN)
#if (defined(BTNPIN) && BTNPIN > -1) || defined(TOUCHPIN)
if (isButtonPressed())
buttonEnabled = false;
#else

View File

@ -3,16 +3,19 @@
/*
Main sketch, global variable declarations
@title WLED project sketch
@version 0.10.2
@version 0.11.0p
@author Christian Schwinne
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2011120
#define VERSION 2011200
// ESP8266-01 (blue) got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.4.2 and the setting 512K(No SPIFFS).
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG
// ESP8266-01 (black) has 1MB flash and can thus fit the whole program. Use 1M(64K SPIFFS).
// ESP8266-01 (blue) got too little storage space to work with WLED. 0.10.2 is the last release supporting this unit.
// ESP8266-01 (black) has 1MB flash and can thus fit the whole program, although OTA update is not possible. Use 1M(128K SPIFFS).
// Uncomment some of the following lines to disable features to compile for ESP8266-01 (max flash size 434kB):
// Alternatively, with platformio pass your chosen flags to your custom build target in platformio.ini.override
@ -35,19 +38,21 @@
#define WLED_ENABLE_WEBSOCKETS
#endif
#define WLED_DISABLE_FILESYSTEM // SPIFFS is not used by any WLED feature yet
//#define WLED_ENABLE_FS_SERVING // Enable sending html file from SPIFFS before serving progmem version
//#define WLED_ENABLE_FS_EDITOR // enable /edit page for editing SPIFFS content. Will also be disabled with OTA lock
#define WLED_ENABLE_FS_EDITOR // enable /edit page for editing FS content. Will also be disabled with OTA lock
// to toggle usb serial debug (un)comment the following line
//#define WLED_DEBUG
// filesystem specific debugging
//#define WLED_DEBUG_FS
// Library inclusions.
#include <Arduino.h>
#ifdef ESP8266
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <ESPAsyncTCP.h>
#include <LittleFS.h>
extern "C"
{
#include <user_interface.h>
@ -58,11 +63,17 @@
#include "esp_wifi.h"
#include <ESPmDNS.h>
#include <AsyncTCP.h>
#include "SPIFFS.h"
//#include "SPIFFS.h"
#define CONFIG_LITTLEFS_FOR_IDF_3_2
#include <LITTLEFS.h>
#endif
#include "src/dependencies/network/Network.h"
#ifdef WLED_USE_MY_CONFIG
#include "my_config.h"
#endif
#include <ESPAsyncWebServer.h>
#include <EEPROM.h>
#include <WiFiUdp.h>
@ -112,6 +123,12 @@
#define CLIENT_PASS ""
#endif
#ifndef SPIFFS_EDITOR_AIRCOOOKIE
#error You are not using the Aircoookie fork of the ESPAsyncWebserver library.\
Using upstream puts your WiFi password at risk of being served by the filesystem.\
Comment out this error message to build regardless.
#endif
#if IR_PIN < 0
#ifndef WLED_DISABLE_INFRARED
#define WLED_DISABLE_INFRARED
@ -124,25 +141,18 @@
#include <IRutils.h>
#endif
//Filesystem to use for preset and config files. SPIFFS or LittleFS on ESP8266, SPIFFS only on ESP32 (now using LITTLEFS port by lorol)
#ifdef ESP8266
#define WLED_FS LittleFS
#else
#define WLED_FS LITTLEFS
#endif
// remove flicker because PWM signal of RGB channels can become out of phase (part of core as of Arduino core v2.7.0)
//#if defined(WLED_USE_ANALOG_LEDS) && defined(ESP8266)
// #include "src/dependencies/arduino/core_esp8266_waveform.h"
//#endif
// enable additional debug output
#ifdef WLED_DEBUG
#ifndef ESP8266
#include <rom/rtc.h>
#endif
#define DEBUG_PRINT(x) Serial.print(x)
#define DEBUG_PRINTLN(x) Serial.println(x)
#define DEBUG_PRINTF(x) Serial.printf(x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#define DEBUG_PRINTF(x)
#endif
// GLOBAL VARIABLES
// both declared and defined in header (solution from http://www.keil.com/support/docs/1868.htm)
//
@ -163,8 +173,8 @@
#endif
// Global Variable definitions
WLED_GLOBAL char versionString[] _INIT("0.10.2");
#define WLED_CODENAME "Fumikiri"
WLED_GLOBAL char versionString[] _INIT("0.11.0p");
#define WLED_CODENAME "Mirai"
// AP and OTA default passwords (for maximum security change them!)
WLED_GLOBAL char apPass[65] _INIT(DEFAULT_AP_PASS);
@ -173,8 +183,12 @@ WLED_GLOBAL char otaPass[33] _INIT(DEFAULT_OTA_PASS);
// Hardware CONFIG (only changeble HERE, not at runtime)
// LED strip pin, button pin and IR pin changeable in NpbWrapper.h!
//WLED_GLOBAL byte presetToApply _INIT(0);
#if AUXPIN >= 0
WLED_GLOBAL byte auxDefaultState _INIT(0); // 0: input 1: high 2: low
WLED_GLOBAL byte auxTriggeredState _INIT(0); // 0: input 1: high 2: low
#endif
WLED_GLOBAL char ntpServerName[33] _INIT("0.wled.pool.ntp.org"); // NTP server to use
// WiFi CONFIG (all these can be changed via web UI, no need to set them here)
@ -294,7 +308,6 @@ WLED_GLOBAL byte countdownYear _INIT(20), countdownMonth _INIT(1); // Countdow
WLED_GLOBAL byte countdownDay _INIT(1) , countdownHour _INIT(0);
WLED_GLOBAL byte countdownMin _INIT(0) , countdownSec _INIT(0);
WLED_GLOBAL byte macroBoot _INIT(0); // macro loaded after startup
WLED_GLOBAL byte macroNl _INIT(0); // after nightlight delay over
WLED_GLOBAL byte macroCountdown _INIT(0);
WLED_GLOBAL byte macroAlexaOn _INIT(0), macroAlexaOff _INIT(0);
@ -429,9 +442,10 @@ WLED_GLOBAL byte presetCycleMin _INIT(1), presetCycleMax _INIT(5);
WLED_GLOBAL uint16_t presetCycleTime _INIT(12);
WLED_GLOBAL unsigned long presetCycledTime _INIT(0);
WLED_GLOBAL byte presetCycCurr _INIT(presetCycleMin);
WLED_GLOBAL bool presetApplyBri _INIT(true);
WLED_GLOBAL bool saveCurrPresetCycConf _INIT(false);
WLED_GLOBAL int16_t currentPlaylist _INIT(0);
// realtime
WLED_GLOBAL byte realtimeMode _INIT(REALTIME_MODE_INACTIVE);
WLED_GLOBAL byte realtimeOverride _INIT(REALTIME_OVERRIDE_NONE);
@ -476,9 +490,16 @@ WLED_GLOBAL uint16_t rolloverMillis _INIT(0);
WLED_GLOBAL char* obuf;
WLED_GLOBAL uint16_t olen _INIT(0);
// General filesystem
WLED_GLOBAL size_t fsBytesUsed _INIT(0);
WLED_GLOBAL size_t fsBytesTotal _INIT(0);
WLED_GLOBAL unsigned long presetsModifiedTime _INIT(0L);
WLED_GLOBAL JsonDocument* fileDoc;
WLED_GLOBAL bool doCloseFile _INIT(false);
// presets
WLED_GLOBAL uint16_t savedPresets _INIT(0);
WLED_GLOBAL int8_t currentPreset _INIT(-1);
WLED_GLOBAL int16_t currentPreset _INIT(-1);
WLED_GLOBAL bool isPreset _INIT(false);
WLED_GLOBAL byte errorFlag _INIT(0);
@ -509,6 +530,8 @@ WLED_GLOBAL WS2812FX strip _INIT(WS2812FX());
// Usermod manager
WLED_GLOBAL UsermodManager usermods _INIT(UsermodManager());
WLED_GLOBAL PinManagerClass pinManager _INIT(PinManagerClass());
// Status LED
#if STATUSLED && STATUSLED != LEDPIN
WLED_GLOBAL unsigned long ledStatusLastMillis _INIT(0);
@ -516,6 +539,30 @@ WLED_GLOBAL UsermodManager usermods _INIT(UsermodManager());
WLED_GLOBAL bool ledStatusState _INIT(0); // the current LED state
#endif
// enable additional debug output
#ifdef WLED_DEBUG
#ifndef ESP8266
#include <rom/rtc.h>
#endif
#define DEBUG_PRINT(x) Serial.print(x)
#define DEBUG_PRINTLN(x) Serial.println(x)
#define DEBUG_PRINTF(x...) Serial.printf(x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#define DEBUG_PRINTF(x)
#endif
#ifdef WLED_DEBUG_FS
#define DEBUGFS_PRINT(x) Serial.print(x)
#define DEBUGFS_PRINTLN(x) Serial.println(x)
#define DEBUGFS_PRINTF(x...) Serial.printf(x)
#else
#define DEBUGFS_PRINT(x)
#define DEBUGFS_PRINTLN(x)
#define DEBUGFS_PRINTF(x...)
#endif
// debug macro variable definitions
#ifdef WLED_DEBUG
WLED_GLOBAL unsigned long debugTime _INIT(0);

View File

@ -2,12 +2,16 @@
#include "wled.h"
/*
* DEPRECATED, do not use for new settings
* Only used to restore config from pre-0.11 installations using the deEEP() methods
*
* Methods to handle saving and loading to non-volatile memory
* EEPROM Map: https://github.com/Aircoookie/WLED/wiki/EEPROM-Map
*/
//eeprom Version code, enables default settings instead of 0 init on update
#define EEPVER 22
#define EEPSIZE 2560 //Maximum is 4096
//0 -> old version, default
//1 -> 0.4p 1711272 and up
//2 -> 0.4p 1711302 and up
@ -32,34 +36,6 @@
//21-> 0.10.1p
//22-> 2009260
void commit()
{
if (!EEPROM.commit()) errorFlag = 2;
}
/*
* Erase all configuration data
*/
void clearEEPROM()
{
for (int i = 0; i < EEPSIZE; i++)
{
EEPROM.write(i, 0);
}
commit();
}
void writeStringToEEPROM(uint16_t pos, char* str, uint16_t len)
{
for (int i = 0; i < len; ++i)
{
EEPROM.write(pos + i, str[i]);
if (str[i] == 0) return;
}
}
void readStringFromEEPROM(uint16_t pos, char* str, uint16_t len)
{
for (int i = 0; i < len; ++i)
@ -70,238 +46,14 @@ void readStringFromEEPROM(uint16_t pos, char* str, uint16_t len)
str[len] = 0; //make sure every string is properly terminated. str must be at least len +1 big.
}
/*
* Write configuration to flash
*/
void saveSettingsToEEPROM()
{
if (EEPROM.read(233) != 233) //set no first boot flag
{
clearEEPROM();
EEPROM.write(233, 233);
}
writeStringToEEPROM( 0, clientSSID, 32);
writeStringToEEPROM( 32, clientPass, 64);
writeStringToEEPROM( 96, cmDNS, 32);
writeStringToEEPROM(128, apSSID, 32);
writeStringToEEPROM(160, apPass, 64);
EEPROM.write(224, nightlightDelayMinsDefault);
EEPROM.write(225, nightlightMode);
EEPROM.write(226, notifyDirectDefault);
EEPROM.write(227, apChannel);
EEPROM.write(228, apHide);
EEPROM.write(229, ledCount & 0xFF);
EEPROM.write(230, notifyButton);
EEPROM.write(231, notifyTwice);
EEPROM.write(232, buttonEnabled);
//233 reserved for first boot flag
for (int i = 0; i<4; i++) //ip addresses
{
EEPROM.write(234+i, staticIP[i]);
EEPROM.write(238+i, staticGateway[i]);
EEPROM.write(242+i, staticSubnet[i]);
}
EEPROM.write(249, briS);
EEPROM.write(250, receiveNotificationBrightness);
EEPROM.write(251, fadeTransition);
EEPROM.write(252, strip.reverseMode);
EEPROM.write(253, transitionDelayDefault & 0xFF);
EEPROM.write(254, (transitionDelayDefault >> 8) & 0xFF);
EEPROM.write(255, briMultiplier);
//255,250,231,230,226 notifier bytes
writeStringToEEPROM(256, otaPass, 32);
EEPROM.write(288, nightlightTargetBri);
EEPROM.write(289, otaLock);
EEPROM.write(290, udpPort & 0xFF);
EEPROM.write(291, (udpPort >> 8) & 0xFF);
writeStringToEEPROM(292, serverDescription, 32);
EEPROM.write(327, ntpEnabled);
EEPROM.write(328, currentTimezone);
EEPROM.write(329, useAMPM);
EEPROM.write(330, strip.gammaCorrectBri);
EEPROM.write(331, strip.gammaCorrectCol);
EEPROM.write(332, overlayDefault);
EEPROM.write(333, alexaEnabled);
writeStringToEEPROM(334, alexaInvocationName, 32);
EEPROM.write(366, notifyAlexa);
EEPROM.write(367, (arlsOffset>=0));
EEPROM.write(368, abs(arlsOffset));
EEPROM.write(369, turnOnAtBoot);
EEPROM.write(370, noWifiSleep);
EEPROM.write(372, useRGBW);
EEPROM.write(374, strip.paletteFade);
EEPROM.write(375, strip.milliampsPerLed); //was apWaitTimeSecs up to 0.8.5
EEPROM.write(376, apBehavior);
EEPROM.write(377, EEPVER); //eeprom was updated to latest
EEPROM.write(378, udpPort2 & 0xFF);
EEPROM.write(379, (udpPort2 >> 8) & 0xFF);
EEPROM.write(382, strip.paletteBlend);
EEPROM.write(383, strip.colorOrder);
EEPROM.write(385, irEnabled);
EEPROM.write(387, strip.ablMilliampsMax & 0xFF);
EEPROM.write(388, (strip.ablMilliampsMax >> 8) & 0xFF);
EEPROM.write(389, bootPreset);
EEPROM.write(390, aOtaEnabled);
EEPROM.write(391, receiveNotificationColor);
EEPROM.write(392, receiveNotificationEffects);
EEPROM.write(393, wifiLock);
EEPROM.write(394, abs(utcOffsetSecs) & 0xFF);
EEPROM.write(395, (abs(utcOffsetSecs) >> 8) & 0xFF);
EEPROM.write(396, (utcOffsetSecs<0)); //is negative
EEPROM.write(397, syncToggleReceive);
EEPROM.write(398, (ledCount >> 8) & 0xFF);
//EEPROM.write(399, was !enableSecTransition);
//favorite setting (preset) memory (25 slots/ each 20byte)
//400 - 940 reserved
writeStringToEEPROM(990, ntpServerName, 32);
EEPROM.write(2048, huePollingEnabled);
//EEPROM.write(2049, hueUpdatingEnabled);
for (int i = 2050; i < 2054; ++i)
{
EEPROM.write(i, hueIP[i-2050]);
}
writeStringToEEPROM(2054, hueApiKey, 46);
EEPROM.write(2100, huePollIntervalMs & 0xFF);
EEPROM.write(2101, (huePollIntervalMs >> 8) & 0xFF);
EEPROM.write(2102, notifyHue);
EEPROM.write(2103, hueApplyOnOff);
EEPROM.write(2104, hueApplyBri);
EEPROM.write(2105, hueApplyColor);
EEPROM.write(2106, huePollLightId);
EEPROM.write(2150, overlayMin);
EEPROM.write(2151, overlayMax);
EEPROM.write(2152, analogClock12pixel);
EEPROM.write(2153, analogClock5MinuteMarks);
EEPROM.write(2154, analogClockSecondsTrail);
EEPROM.write(2155, countdownMode);
EEPROM.write(2156, countdownYear);
EEPROM.write(2157, countdownMonth);
EEPROM.write(2158, countdownDay);
EEPROM.write(2159, countdownHour);
EEPROM.write(2160, countdownMin);
EEPROM.write(2161, countdownSec);
setCountdown();
writeStringToEEPROM(2165, cronixieDisplay, 6);
EEPROM.write(2171, cronixieBacklight);
setCronixie();
EEPROM.write(2175, macroBoot);
EEPROM.write(2176, macroAlexaOn);
EEPROM.write(2177, macroAlexaOff);
EEPROM.write(2178, macroButton);
EEPROM.write(2179, macroLongPress);
EEPROM.write(2180, macroCountdown);
EEPROM.write(2181, macroNl);
EEPROM.write(2182, macroDoublePress);
#ifdef WLED_ENABLE_DMX
EEPROM.write(2185, e131ProxyUniverse & 0xFF);
EEPROM.write(2186, (e131ProxyUniverse >> 8) & 0xFF);
#endif
EEPROM.write(2187, e131Port & 0xFF);
EEPROM.write(2188, (e131Port >> 8) & 0xFF);
EEPROM.write(2189, e131SkipOutOfSequence);
EEPROM.write(2190, e131Universe & 0xFF);
EEPROM.write(2191, (e131Universe >> 8) & 0xFF);
EEPROM.write(2192, e131Multicast);
EEPROM.write(2193, realtimeTimeoutMs & 0xFF);
EEPROM.write(2194, (realtimeTimeoutMs >> 8) & 0xFF);
EEPROM.write(2195, arlsForceMaxBri);
EEPROM.write(2196, arlsDisableGammaCorrection);
EEPROM.write(2197, DMXAddress & 0xFF);
EEPROM.write(2198, (DMXAddress >> 8) & 0xFF);
EEPROM.write(2199, DMXMode);
EEPROM.write(2200, !receiveDirect);
EEPROM.write(2201, notifyMacro); //was enableRealtime
EEPROM.write(2203, strip.rgbwMode);
EEPROM.write(2204, skipFirstLed);
if (saveCurrPresetCycConf)
{
EEPROM.write(2205, presetCyclingEnabled);
EEPROM.write(2206, presetCycleTime & 0xFF);
EEPROM.write(2207, (presetCycleTime >> 8) & 0xFF);
EEPROM.write(2208, presetCycleMin);
EEPROM.write(2209, presetCycleMax);
EEPROM.write(2210, presetApplyBri);
// was EEPROM.write(2211, presetApplyCol);
// was EEPROM.write(2212, presetApplyFx);
saveCurrPresetCycConf = false;
}
writeStringToEEPROM(2220, blynkApiKey, 35);
for (int i = 0; i < 8; ++i)
{
EEPROM.write(2260 + i, timerHours[i] );
EEPROM.write(2270 + i, timerMinutes[i]);
EEPROM.write(2280 + i, timerWeekday[i]);
EEPROM.write(2290 + i, timerMacro[i] );
}
EEPROM.write(2299, mqttEnabled);
writeStringToEEPROM(2300, mqttServer, 32);
writeStringToEEPROM(2333, mqttDeviceTopic, 32);
writeStringToEEPROM(2366, mqttGroupTopic, 32);
writeStringToEEPROM(2399, mqttUser, 40);
writeStringToEEPROM(2440, mqttPass, 40);
writeStringToEEPROM(2481, mqttClientID, 40);
EEPROM.write(2522, mqttPort & 0xFF);
EEPROM.write(2523, (mqttPort >> 8) & 0xFF);
// DMX (2530 - 2549)
#ifdef WLED_ENABLE_DMX
EEPROM.write(2530, DMXChannels);
EEPROM.write(2531, DMXGap & 0xFF);
EEPROM.write(2532, (DMXGap >> 8) & 0xFF);
EEPROM.write(2533, DMXStart & 0xFF);
EEPROM.write(2534, (DMXStart >> 8) & 0xFF);
for (int i=0; i<15; i++) {
EEPROM.write(2535+i, DMXFixtureMap[i]);
} // last used: 2549. maybe leave a few bytes for future expansion and go on with 2600 kthxbye.
#endif
commit();
}
/*
* Read all configuration from flash
*/
void loadSettingsFromEEPROM(bool first)
void loadSettingsFromEEPROM()
{
if (EEPROM.read(233) != 233) //first boot/reset to default
{
DEBUG_PRINT("Settings invalid, restoring defaults...");
saveSettingsToEEPROM();
DEBUG_PRINTLN("done");
DEBUG_PRINTLN(F("EEPROM settings invalid, using defaults..."));
return;
}
int lastEEPROMversion = EEPROM.read(377); //last EEPROM version before update
@ -343,7 +95,7 @@ void loadSettingsFromEEPROM(bool first)
staticSubnet[3] = EEPROM.read(245);
briS = EEPROM.read(249); bri = briS;
if (!EEPROM.read(369) && first)
if (!EEPROM.read(369))
{
bri = 0; briLast = briS;
}
@ -426,7 +178,7 @@ void loadSettingsFromEEPROM(bool first)
readStringFromEEPROM(2165, cronixieDisplay, 6);
cronixieBacklight = EEPROM.read(2171);
macroBoot = EEPROM.read(2175);
//macroBoot = EEPROM.read(2175);
macroAlexaOn = EEPROM.read(2176);
macroAlexaOff = EEPROM.read(2177);
macroButton = EEPROM.read(2178);
@ -457,7 +209,9 @@ void loadSettingsFromEEPROM(bool first)
timerMinutes[i] = EEPROM.read(2270 + i);
timerWeekday[i] = EEPROM.read(2280 + i);
timerMacro[i] = EEPROM.read(2290 + i);
if (timerMacro[i] > 0) timerMacro[i] += 16; //add 16 to work with macro --> preset mapping
if (timerWeekday[i] == 0) timerWeekday[i] = 255;
if (timerMacro[i] == 0) timerWeekday[i] = timerWeekday[i] & 0b11111110;
}
}
@ -559,7 +313,7 @@ void loadSettingsFromEEPROM(bool first)
if (lastEEPROMversion < 21) presetCycleTime /= 100; //was stored in ms, now is in tenths of a second
presetCycleMin = EEPROM.read(2208);
presetCycleMax = EEPROM.read(2209);
presetApplyBri = EEPROM.read(2210);
//was presetApplyBri = EEPROM.read(2210);
//was presetApplyCol = EEPROM.read(2211);
//was presetApplyFx = EEPROM.read(2212);
}
@ -588,7 +342,7 @@ void loadSettingsFromEEPROM(bool first)
for (int i=0;i<15;i++) {
DMXFixtureMap[i] = EEPROM.read(2535+i);
} //last used: 2549
EEPROM.write(2550, DMXStartLED);
DMXStartLED = EEPROM.read(2550);
#endif
//Usermod memory
@ -597,159 +351,114 @@ void loadSettingsFromEEPROM(bool first)
//2944 - 3071 reserved for Usermods (need to increase EEPSIZE to 3072 in const.h)
overlayCurrent = overlayDefault;
savedToPresets();
}
//PRESET PROTOCOL 20 bytes
//0: preset purpose byte 0:invalid 1:valid preset 2:segment preset 2.0
//1:a 2:r 3:g 4:b 5:w 6:er 7:eg 8:eb 9:ew 10:fx 11:sx | custom chase 12:numP 13:numS 14:(0:fs 1:both 2:fe) 15:step 16:ix 17: fp 18-19:Zeros
//determines which presets already contain save data
void savedToPresets()
{
for (byte index = 1; index < 16; index++)
{
uint16_t i = 380 + index*20;
if (EEPROM.read(i) == 1) {
savedPresets |= 0x01 << (index-1);
} else
{
savedPresets &= ~(0x01 << (index-1));
}
}
if (EEPROM.read(700) == 2 || EEPROM.read(700) == 3) {
savedPresets |= 0x01 << 15;
} else
{
savedPresets &= ~(0x01 << 15);
}
//provided for increased compatibility with usermods written for v0.10
void applyMacro(byte index) {
applyPreset(index+16);
}
bool applyPreset(byte index, bool loadBri)
{
if (index == 255 || index == 0)
{
loadSettingsFromEEPROM(false);//load boot defaults
return true;
}
if (index > 16 || index < 1) return false;
uint16_t i = 380 + index*20;
byte ver = EEPROM.read(i);
if (index < 16) {
if (ver != 1) return false;
strip.applyToAllSelected = true;
if (loadBri) bri = EEPROM.read(i+1);
for (byte j=0; j<4; j++)
{
col[j] = EEPROM.read(i+j+2);
colSec[j] = EEPROM.read(i+j+6);
}
strip.setColor(2, EEPROM.read(i+12), EEPROM.read(i+13), EEPROM.read(i+14), EEPROM.read(i+15)); //tertiary color
// De-EEPROM routine, upgrade from previous versions to v0.11
void deEEP() {
if (WLED_FS.exists("/presets.json")) return;
DEBUG_PRINTLN(F("Preset file not found, attempting to load from EEPROM"));
DEBUGFS_PRINTLN(F("Allocating saving buffer for dEEP"));
DynamicJsonDocument dDoc(JSON_BUFFER_SIZE *2);
JsonObject sObj = dDoc.to<JsonObject>();
sObj.createNestedObject("0");
effectCurrent = EEPROM.read(i+10);
effectSpeed = EEPROM.read(i+11);
effectIntensity = EEPROM.read(i+16);
effectPalette = EEPROM.read(i+17);
} else {
if (ver != 2 && ver != 3) return false;
strip.applyToAllSelected = false;
if (loadBri) bri = EEPROM.read(i+1);
WS2812FX::Segment* seg = strip.getSegments();
memcpy(seg, EEPROM.getDataPtr() +i+2, 240);
if (ver == 2) { //versions before 2004230 did not have opacity
for (byte j = 0; j < strip.getMaxSegments(); j++)
{
strip.getSegment(j).opacity = 255;
strip.getSegment(j).setOption(SEG_OPTION_ON, 1);
EEPROM.begin(EEPSIZE);
if (EEPROM.read(233) == 233) { //valid EEPROM save
for (uint16_t index = 1; index <= 16; index++) { //copy presets to presets.json
uint16_t i = 380 + index*20;
byte ver = EEPROM.read(i);
if ((index < 16 && ver != 1) || (index == 16 && (ver < 2 || ver > 3))) continue;
char nbuf[16];
sprintf(nbuf, "%d", index);
JsonObject pObj = sObj.createNestedObject(nbuf);
sprintf_P(nbuf, (char*)F("Preset %d"), index);
pObj["n"] = nbuf;
pObj["bri"] = EEPROM.read(i+1);
if (index < 16) {
JsonObject segObj = pObj.createNestedObject("seg");
JsonArray colarr = segObj.createNestedArray("col");
byte numChannels = (useRGBW)? 4:3;
for (uint8_t k = 0; k < 3; k++) //k=0 primary (i+2) k=1 secondary (i+6) k=2 tertiary color (i+12)
{
JsonArray colX = colarr.createNestedArray();
uint16_t memloc = i + 6*k;
if (k == 0) memloc += 2;
for (byte j = 0; j < numChannels; j++) colX.add(EEPROM.read(memloc + j));
}
segObj[F("fx")] = EEPROM.read(i+10);
segObj[F("sx")] = EEPROM.read(i+11);
segObj[F("ix")] = EEPROM.read(i+16);
segObj[F("pal")] = EEPROM.read(i+17);
} else {
WS2812FX::Segment* seg = strip.getSegments();
memcpy(seg, EEPROM.getDataPtr() +i+2, 240);
if (ver == 2) { //versions before 2004230 did not have opacity
for (byte j = 0; j < strip.getMaxSegments(); j++)
{
strip.getSegment(j).opacity = 255;
strip.getSegment(j).setOption(SEG_OPTION_ON, 1);
}
}
setValuesFromMainSeg();
serializeState(pObj, true, false, true);
strip.resetSegments();
setValuesFromMainSeg();
}
}
setValuesFromMainSeg();
}
currentPreset = index;
isPreset = true;
return true;
}
void savePreset(byte index, bool persist)
{
if (index > 16) return;
if (index < 1) {saveSettingsToEEPROM();return;}
uint16_t i = 380 + index*20;//min400
if (index < 16) {
EEPROM.write(i, 1);
EEPROM.write(i+1, bri);
for (uint16_t j=0; j<4; j++)
{
EEPROM.write(i+j+2, col[j]);
EEPROM.write(i+j+6, colSec[j]);
for (uint16_t index = 1; index <= 16; index++) { //copy macros to presets.json
char m[65];
readStringFromEEPROM(1024+64*(index-1), m, 64);
if (m[0]) { //macro exists
char nbuf[16];
sprintf(nbuf, "%d", index + 16);
JsonObject pObj = sObj.createNestedObject(nbuf);
sprintf_P(nbuf, "Z Macro %d", index);
pObj["n"] = nbuf;
pObj["win"] = m;
}
}
EEPROM.write(i+10, effectCurrent);
EEPROM.write(i+11, effectSpeed);
uint32_t colTer = strip.getSegment(strip.getMainSegmentId()).colors[2];
EEPROM.write(i+12, (colTer >> 16) & 0xFF);
EEPROM.write(i+13, (colTer >> 8) & 0xFF);
EEPROM.write(i+14, (colTer >> 0) & 0xFF);
EEPROM.write(i+15, (colTer >> 24) & 0xFF);
EEPROM.write(i+16, effectIntensity);
EEPROM.write(i+17, effectPalette);
} else { //segment 16 can save segments
EEPROM.write(i, 3);
EEPROM.write(i+1, bri);
WS2812FX::Segment* seg = strip.getSegments();
memcpy(EEPROM.getDataPtr() +i+2, seg, 240);
}
if (persist) commit();
savedToPresets();
currentPreset = index;
isPreset = true;
}
EEPROM.end();
void loadMacro(byte index, char* m)
{
index-=1;
if (index > 15) return;
readStringFromEEPROM(1024+64*index, m, 64);
}
void applyMacro(byte index)
{
index-=1;
if (index > 15) return;
String mc="win&";
char m[65];
loadMacro(index+1, m);
mc += m;
mc += "&IN"; //internal, no XML response
if (!notifyMacro) mc += "&NN";
String forbidden = "&M="; //dont apply if called by the macro itself to prevent loop
/*
* NOTE: loop is still possible if you call a different macro from a macro, which then calls the first macro again.
* To prevent that, but also disable calling macros within macros, comment the next line out.
*/
forbidden = forbidden + index;
if (mc.indexOf(forbidden) >= 0) return;
handleSet(nullptr, mc);
}
void saveMacro(byte index, const String& mc, bool persist) //only commit on single save, not in settings
{
index-=1;
if (index > 15) return;
int s = 1024+index*64;
for (int i = s; i < s+64; i++)
{
EEPROM.write(i, mc.charAt(i-s));
File f = WLED_FS.open("/presets.json", "w");
if (!f) {
errorFlag = ERR_FS_GENERAL;
return;
}
if (persist) commit();
serializeJson(dDoc, f);
f.close();
DEBUG_PRINTLN(F("deEEP complete!"));
}
void deEEPSettings() {
DEBUG_PRINTLN(F("Restore settings from EEPROM"));
EEPROM.begin(EEPSIZE);
loadSettingsFromEEPROM();
EEPROM.end();
serializeConfig();
}

View File

@ -85,7 +85,9 @@ void initServer()
if (error || root.isNull()) {
request->send(400, "application/json", F("{\"error\":9}")); return;
}
fileDoc = &jsonBuffer;
verboseResponse = deserializeState(root);
fileDoc = nullptr;
}
if (verboseResponse) { //if JSON contains "v"
serveJson(request); return;
@ -94,7 +96,6 @@ void initServer()
});
server.addHandler(handler);
//*******DEPRECATED*******
server.on("/version", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/plain", (String)VERSION);
});
@ -106,7 +107,6 @@ void initServer()
server.on("/freeheap", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/plain", (String)ESP.getFreeHeap());
});
//*******END*******/
server.on("/u", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/html", PAGE_usermod);
@ -122,15 +122,15 @@ void initServer()
//if OTA is allowed
if (!otaLock){
#if !defined WLED_DISABLE_FILESYSTEM && defined WLED_ENABLE_FS_EDITOR
#ifdef WLED_ENABLE_FS_EDITOR
#ifdef ARDUINO_ARCH_ESP32
server.addHandler(new SPIFFSEditor(SPIFFS));//http_username,http_password));
server.addHandler(new SPIFFSEditor(WLED_FS));//http_username,http_password));
#else
server.addHandler(new SPIFFSEditor());//http_username,http_password));
server.addHandler(new SPIFFSEditor("","",WLED_FS));//http_username,http_password));
#endif
#else
server.on("/edit", HTTP_GET, [](AsyncWebServerRequest *request){
serveMessage(request, 501, "Not implemented", F("The SPIFFS editor is disabled in this build."), 254);
serveMessage(request, 501, "Not implemented", F("The FS editor is disabled in this build."), 254);
});
#endif
//init ota page
@ -214,10 +214,8 @@ void initServer()
#ifndef WLED_DISABLE_ALEXA
if(espalexa.handleAlexaApiCall(request)) return;
#endif
#ifdef WLED_ENABLE_FS_SERVING
if(handleFileRead(request, request->url())) return;
#endif
request->send(404, "text/plain", "Not Found");
request->send_P(404, "text/html", PAGE_404);
});
}
@ -234,9 +232,7 @@ void serveIndexOrWelcome(AsyncWebServerRequest *request)
void serveIndex(AsyncWebServerRequest* request)
{
#ifdef WLED_ENABLE_FS_SERVING
if (handleFileRead(request, "/index.htm")) return;
#endif
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_index, PAGE_index_L);

View File

@ -251,6 +251,8 @@ void getSettingsJS(byte subPage, char* dest)
#ifdef ESP8266
#if LEDPIN == 3
oappend(SET_F("d.Sf.LC.max=500;"));
#else
oappend(SET_F("d.Sf.LC.max=1500;"));
#endif
#endif
sappend('v',SET_F("LC"),ledCount);
@ -391,16 +393,7 @@ void getSettingsJS(byte subPage, char* dest)
sappend('v',SET_F("CH"),countdownHour);
sappend('v',SET_F("CM"),countdownMin);
sappend('v',SET_F("CS"),countdownSec);
char k[4]; k[0]= 'M';
for (int i=1;i<17;i++)
{
char m[65];
loadMacro(i, m);
sprintf(k+1,"%i",i);
sappends('s',k,m);
}
sappend('v',SET_F("MB"),macroBoot);
sappend('v',SET_F("A0"),macroAlexaOn);
sappend('v',SET_F("A1"),macroAlexaOff);
sappend('v',SET_F("MP"),macroButton);
@ -409,6 +402,7 @@ void getSettingsJS(byte subPage, char* dest)
sappend('v',SET_F("MN"),macroNl);
sappend('v',SET_F("MD"),macroDoublePress);
char k[4];
k[2] = 0; //Time macros
for (int i = 0; i<8; i++)
{