mirror of
https://github.com/wled/WLED.git
synced 2025-07-23 10:46:33 +00:00
Merge branch '0_15' into harmonic-random-palette-generator
This commit is contained in:
commit
66d9e8c038
76
.github/workflows/wled-ci.yml
vendored
76
.github/workflows/wled-ci.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: PlatformIO CI
|
||||
name: WLED CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
@ -8,17 +8,11 @@ jobs:
|
||||
name: Gather Environments
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.9'
|
||||
python-version: '3.12'
|
||||
cache: 'pip'
|
||||
- name: Install PlatformIO
|
||||
run: pip install -r requirements.txt
|
||||
- name: Get default environments
|
||||
@ -38,59 +32,63 @@ jobs:
|
||||
matrix:
|
||||
environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: 'npm'
|
||||
- run: npm install
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
- name: Cache PlatformIO
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
|
||||
path: |
|
||||
~/.platformio/.cache
|
||||
~/.buildcache
|
||||
build_output
|
||||
key: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-${{ hashFiles('wled00/**') }}
|
||||
restore-keys: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.9'
|
||||
python-version: '3.12'
|
||||
cache: 'pip'
|
||||
- name: Install PlatformIO
|
||||
run: pip install -r requirements.txt
|
||||
- name: Build firmware
|
||||
env:
|
||||
WLED_RELEASE: True
|
||||
run: pio run -e ${{ matrix.environment }}
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: firmware-${{ matrix.environment }}
|
||||
path: |
|
||||
build_output/firmware/*.bin
|
||||
build_output/firmware/*.gz
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
name: firmware-release
|
||||
path: build_output/release/*.bin
|
||||
build_output/release/*.bin
|
||||
build_output/release/*_ESP02.bin.gz
|
||||
release:
|
||||
name: Create Release
|
||||
runs-on: ubuntu-latest
|
||||
needs: [get_default_envs, build]
|
||||
needs: build
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
steps:
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: firmware-release
|
||||
merge-multiple: true
|
||||
- name: Create draft release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: True
|
||||
files: |
|
||||
*.bin
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
*.bin.gz
|
||||
|
||||
|
||||
testCdata:
|
||||
name: Test cdata.js
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '20.x'
|
||||
cache: 'npm'
|
||||
- run: npm ci
|
||||
- run: npm test
|
||||
|
@ -1,7 +1,14 @@
|
||||
## WLED changelog
|
||||
|
||||
#### Build 2309120 till build 2401270
|
||||
#### Build 2309120 till build 2402010
|
||||
- WLED version 0.15.0-a0
|
||||
- Multi-WiFi support. Add up to 3 (or more via cusom compile) WiFis to connect to
|
||||
- Temporary AP. Use your WLED in public with temporary AP.
|
||||
- Github CI build system enhancements (#3718 by @WoodyLetsCode)
|
||||
- Accessibility: Node list ( #3715 by @WoodyLetsCode)
|
||||
- Analog clock overlay enhancement (#3489 by @WoodyLetsCode)
|
||||
- ESP32-POE-WROVER from Olimex ethernet support (#3625 by @m-wachter)
|
||||
- APA106 support (#3580 by @itstefanjanos)
|
||||
- BREAKING: Effect: updated Palette effect to support 2D (#3683 by @TripleWhy)
|
||||
- "SuperSync" from WLED MM (by @MoonModules)
|
||||
- Effect: DNA Spiral Effect Speed Fix (#3723 by @Derek4aty1)
|
||||
|
2072
package-lock.json
generated
2072
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -9,6 +9,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node tools/cdata.js",
|
||||
"test": "node --test",
|
||||
"dev": "nodemon -e js,html,htm,css,png,jpg,gif,ico,js -w tools/ -w wled00/data/ -x node tools/cdata.js"
|
||||
},
|
||||
"repository": {
|
||||
@ -22,10 +23,10 @@
|
||||
},
|
||||
"homepage": "https://github.com/Aircoookie/WLED#readme",
|
||||
"dependencies": {
|
||||
"clean-css": "^4.2.3",
|
||||
"html-minifier-terser": "^5.1.1",
|
||||
"clean-css": "^5.3.3",
|
||||
"html-minifier-terser": "^7.2.0",
|
||||
"inliner": "^1.13.1",
|
||||
"nodemon": "^2.0.20",
|
||||
"nodemon": "^3.0.2",
|
||||
"zlib": "^1.0.5"
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,16 @@ def _create_dirs(dirs=["firmware", "map"]):
|
||||
if not os.path.isdir("{}{}".format(OUTPUT_DIR, d)):
|
||||
os.mkdir("{}{}".format(OUTPUT_DIR, d))
|
||||
|
||||
def create_release(source):
|
||||
release_name = _get_cpp_define_value(env, "WLED_RELEASE_NAME")
|
||||
if release_name:
|
||||
_create_dirs(["release"])
|
||||
version = _get_cpp_define_value(env, "WLED_VERSION")
|
||||
# get file extension of source file (.bin or .bin.gz)
|
||||
ext = source.split(".", 1)[1]
|
||||
release_file = "{}release{}WLED_{}_{}.{}".format(OUTPUT_DIR, os.path.sep, version, release_name, ext)
|
||||
shutil.copy(source, release_file)
|
||||
|
||||
def bin_rename_copy(source, target, env):
|
||||
_create_dirs()
|
||||
variant = env["PIOENV"]
|
||||
@ -30,14 +40,6 @@ def bin_rename_copy(source, target, env):
|
||||
map_file = "{}map{}{}.map".format(OUTPUT_DIR, os.path.sep, variant)
|
||||
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
|
||||
|
||||
release_name = _get_cpp_define_value(env, "WLED_RELEASE_NAME")
|
||||
|
||||
if release_name:
|
||||
_create_dirs(["release"])
|
||||
version = _get_cpp_define_value(env, "WLED_VERSION")
|
||||
release_file = "{}release{}WLED_{}_{}.bin".format(OUTPUT_DIR, os.path.sep, version, release_name)
|
||||
shutil.copy(str(target[0]), release_file)
|
||||
|
||||
# check if new target files exist and remove if necessary
|
||||
for f in [map_file, bin_file]:
|
||||
if os.path.isfile(f):
|
||||
@ -46,6 +48,8 @@ def bin_rename_copy(source, target, env):
|
||||
# copy firmware.bin to firmware/<variant>.bin
|
||||
shutil.copy(str(target[0]), bin_file)
|
||||
|
||||
create_release(bin_file)
|
||||
|
||||
# copy firmware.map to map/<variant>.map
|
||||
if os.path.isfile("firmware.map"):
|
||||
shutil.move("firmware.map", map_file)
|
||||
@ -66,4 +70,6 @@ def bin_gzip(source, target, env):
|
||||
with gzip.open(gzip_file, "wb", compresslevel = 9) as f:
|
||||
shutil.copyfileobj(fp, f)
|
||||
|
||||
create_release(gzip_file)
|
||||
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_rename_copy, bin_gzip])
|
||||
|
415
platformio.ini
415
platformio.ini
@ -9,39 +9,8 @@
|
||||
# (use `platformio_override.ini` when building for your own board; see `platformio_override.ini.sample` for an example)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# CI binaries
|
||||
; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth # ESP32 variant builds are temporarily excluded from CI due to toolchain issues on the GitHub Actions Linux environment
|
||||
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, esp32dev_audioreactive, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB, esp32s3dev_8MB_PSRAM_opi
|
||||
|
||||
# Release binaries
|
||||
; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB
|
||||
|
||||
# Build everything
|
||||
; default_envs = esp32dev, esp8285_4CH_MagicHome, codm-controller-0_6-rev2, codm-controller-0_6, esp32s2_saola, d1_mini_5CH_Shojo_PCB, d1_mini, sp501e, nodemcuv2, esp32_eth, anavi_miracle_controller, esp07, esp01_1m_full, m5atom, h803wf, d1_mini_ota, heltec_wifi_kit_8, esp8285_H801, d1_mini_debug, wemos_shield_esp32, elekstube_ips
|
||||
|
||||
# Single binaries (uncomment your board)
|
||||
; default_envs = elekstube_ips
|
||||
; default_envs = nodemcuv2
|
||||
; default_envs = esp8266_2m
|
||||
; default_envs = esp01_1m_full
|
||||
; default_envs = esp07
|
||||
; default_envs = d1_mini
|
||||
; default_envs = heltec_wifi_kit_8
|
||||
; default_envs = h803wf
|
||||
; default_envs = d1_mini_debug
|
||||
; default_envs = d1_mini_ota
|
||||
; default_envs = esp32dev
|
||||
; default_envs = esp8285_4CH_MagicHome
|
||||
; default_envs = esp8285_H801
|
||||
; default_envs = d1_mini_5CH_Shojo_PCB
|
||||
; default_envs = wemos_shield_esp32
|
||||
; default_envs = m5atom
|
||||
; default_envs = esp32_eth
|
||||
; default_envs = esp32dev_qio80
|
||||
; default_envs = esp32_eth_ota1mapp
|
||||
; default_envs = esp32s2_saola
|
||||
; default_envs = esp32c3dev
|
||||
; default_envs = lolin_s2_mini
|
||||
# CI/release binaries
|
||||
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, esp32dev_audioreactive, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB, esp32s3dev_8MB_PSRAM_opi, esp32_wrover
|
||||
|
||||
src_dir = ./wled00
|
||||
data_dir = ./wled00/data
|
||||
@ -60,9 +29,9 @@ extra_configs =
|
||||
arduino_core_2_6_3 = espressif8266@2.3.3
|
||||
arduino_core_2_7_4 = espressif8266@2.6.2
|
||||
arduino_core_3_0_0 = espressif8266@3.0.0
|
||||
arduino_core_3_2_0 = espressif8266@3.2.0
|
||||
arduino_core_4_1_0 = espressif8266@4.1.0
|
||||
arduino_core_3_1_2 = espressif8266@4.2.0
|
||||
arduino_core_3_0_2 = espressif8266@3.2.0
|
||||
arduino_core_3_1_0 = espressif8266@4.1.0
|
||||
arduino_core_3_1_2 = espressif8266@4.2.1
|
||||
|
||||
# Development platforms
|
||||
arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop
|
||||
@ -132,12 +101,7 @@ build_flags =
|
||||
-D DECODE_SONY=true
|
||||
-D DECODE_SAMSUNG=true
|
||||
-D DECODE_LG=true
|
||||
;-Dregister= # remove warnings in C++17 due to use of deprecated register keyword by the FastLED library ;; warning: this breaks framework code on ESP32-C3 and ESP32-S2
|
||||
-DWLED_USE_MY_CONFIG
|
||||
; -D USERMOD_SENSORSTOMQTT
|
||||
#For ADS1115 sensor uncomment following
|
||||
; -D USERMOD_ADS1115
|
||||
; -D USERMOD_ANIMARTRIX
|
||||
|
||||
build_unflags =
|
||||
|
||||
@ -165,10 +129,8 @@ extra_scripts =
|
||||
framework = arduino
|
||||
board_build.flash_mode = dout
|
||||
monitor_speed = 115200
|
||||
# slow upload speed (comment this out with a ';' when building for development use)
|
||||
# slow upload speed but most compatible (use platformio_override.ini to use faster speed)
|
||||
upload_speed = 115200
|
||||
# fast upload speed (remove ';' when building for development use)
|
||||
; upload_speed = 921600
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# LIBRARIES: required dependencies
|
||||
@ -183,15 +145,17 @@ lib_deps =
|
||||
IRremoteESP8266 @ 2.8.2
|
||||
makuna/NeoPixelBus @ 2.7.5
|
||||
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.7
|
||||
# for I2C interface
|
||||
;Wire
|
||||
# ESP-NOW library (includes mandatory QuickDebug library)
|
||||
;gmag11/QuickESPNow @ 0.6.2
|
||||
https://github.com/blazoncek/QuickESPNow.git#optional-debug
|
||||
#For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line
|
||||
#TFT_eSPI
|
||||
#For compatible OLED display uncomment following
|
||||
#U8g2 #@ ~2.33.15
|
||||
#olikraus/U8g2 #@ ~2.33.15
|
||||
#For Dallas sensor uncomment following
|
||||
#OneWire @ ~2.3.7
|
||||
#paulstoffregen/OneWire @ ~2.3.8
|
||||
#For BME280 sensor uncomment following
|
||||
#BME280 @ ~3.0.0
|
||||
;adafruit/Adafruit BMP280 Library @ 2.1.0
|
||||
@ -205,6 +169,10 @@ lib_deps =
|
||||
# For -D USERMOD_ANIMARTRIX
|
||||
# CC BY-NC 3.0 licensed effects by Stefan Petrick, include this usermod only if you accept the terms!
|
||||
;https://github.com/netmindz/animartrix.git#18bf17389e57c69f11bc8d04ebe1d215422c7fb7
|
||||
# SHT85
|
||||
;robtillaart/SHT85@~0.3.3
|
||||
# Audioreactive usermod
|
||||
;https://github.com/kosme/arduinoFFT#develop @ ^1.9.2
|
||||
|
||||
extra_scripts = ${scripts_defaults.extra_scripts}
|
||||
|
||||
@ -280,6 +248,7 @@ lib_deps =
|
||||
;; generic definitions for all ESP32-S2 boards
|
||||
platform = espressif32@5.3.0
|
||||
platform_packages =
|
||||
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||
build_flags = -g
|
||||
-DARDUINO_ARCH_ESP32
|
||||
-DARDUINO_ARCH_ESP32S2
|
||||
@ -290,7 +259,6 @@ build_flags = -g
|
||||
-DARDUINO_USB_MODE=0 ;; this flag is mandatory for ESP32-S2 !
|
||||
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
|
||||
;; ARDUINO_USB_CDC_ON_BOOT
|
||||
|
||||
lib_deps =
|
||||
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
|
||||
${env.lib_deps}
|
||||
@ -308,7 +276,6 @@ build_flags = -g
|
||||
-DARDUINO_USB_MODE=1 ;; this flag is mandatory for ESP32-C3
|
||||
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
|
||||
;; ARDUINO_USB_CDC_ON_BOOT
|
||||
|
||||
lib_deps =
|
||||
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
|
||||
${env.lib_deps}
|
||||
@ -366,44 +333,6 @@ build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01 -D WLED_D
|
||||
; -D WLED_USE_UNREAL_MATH ;; may cause wrong sunset/sunrise times, but saves 7064 bytes FLASH and 975 bytes RAM
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[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}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[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}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
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}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[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
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp32dev]
|
||||
board = esp32dev
|
||||
platform = ${esp32.platform}
|
||||
@ -428,33 +357,6 @@ board_build.partitions = ${esp32.default_partitions}
|
||||
; board_build.f_flash = 80000000L
|
||||
; board_build.flash_mode = dio
|
||||
|
||||
[env:esp32dev_qio80]
|
||||
board = esp32dev
|
||||
platform = ${esp32.platform}
|
||||
platform_packages = ${esp32.platform_packages}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_qio80 #-D WLED_DISABLE_BROWNOUT_DET
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
|
||||
[env:esp32dev_V4_dio80]
|
||||
;; experimental ESP32 env using ESP-IDF V4.4.x
|
||||
;; Warning: this build environment is not stable!!
|
||||
;; please erase your device before installing.
|
||||
board = esp32dev
|
||||
platform = ${esp32_idf_V4.platform}
|
||||
platform_packages = ${esp32_idf_V4.platform_packages}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_V4_qio80 #-D WLED_DISABLE_BROWNOUT_DET
|
||||
lib_deps = ${esp32_idf_V4.lib_deps}
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.partitions = ${esp32_idf_V4.default_partitions}
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = dio
|
||||
|
||||
[env:esp32_eth]
|
||||
board = esp32-poe
|
||||
platform = ${esp32.platform}
|
||||
@ -466,19 +368,18 @@ build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet -D
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
[env:esp32s2_saola]
|
||||
board = esp32-s2-saola-1
|
||||
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.2/platform-tasmota-espressif32-2.0.2.zip
|
||||
platform_packages =
|
||||
framework = arduino
|
||||
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||
[env:esp32_wrover]
|
||||
platform = ${esp32.platform}
|
||||
board = ttgo-t7-v14-mini32
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
upload_speed = 460800
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=S2_saola
|
||||
;-DLOLIN_WIFI_FIX ;; try this in case Wifi does not work
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
lib_deps = ${esp32s2.lib_deps}
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_WROVER
|
||||
-DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue
|
||||
-D WLED_USE_PSRAM
|
||||
-D LEDPIN=25
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
|
||||
[env:esp32c3dev]
|
||||
extends = esp32c3
|
||||
@ -523,7 +424,7 @@ platform = ${esp32s3.platform}
|
||||
platform_packages = ${esp32s3.platform_packages}
|
||||
upload_speed = 921600
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32s3.build_flags}
|
||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_8MB_PSRAM_opi
|
||||
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
|
||||
;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
|
||||
-D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
|
||||
@ -535,95 +436,24 @@ board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
monitor_filters = esp32_exception_decoder
|
||||
|
||||
[env:esp32s3dev_8MB_PSRAM_qspi]
|
||||
;; ESP32-TinyS3 development board, with 8MB FLASH and PSRAM (memory_type: qio_qspi)
|
||||
extends = env:esp32s3dev_8MB_PSRAM_opi
|
||||
;board = um_tinys3 ; -> needs workaround from https://github.com/Aircoookie/WLED/pull/2905#issuecomment-1328049860
|
||||
board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support
|
||||
board_build.arduino.memory_type = qio_qspi ;; use with PSRAM: 2MB or 4MB
|
||||
|
||||
[env:esp8285_4CH_MagicHome]
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp8285_H801]
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini_5CH_Shojo_PCB]
|
||||
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 WLED_USE_SHOJO_PCB
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# DEVELOPMENT BOARDS
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
[env:d1_mini_debug]
|
||||
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}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini_ota]
|
||||
board = d1_mini
|
||||
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}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:anavi_miracle_controller]
|
||||
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=12 -D IRPIN=-1 -D RLYPIN=2
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:lolin_s2_mini]
|
||||
platform = ${esp32s2.platform}
|
||||
platform_packages = ${esp32s2.platform_packages}
|
||||
board = lolin_s2_mini
|
||||
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||
build_unflags = ${common.build_unflags} #-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
;board_build.flash_mode = qio
|
||||
;board_build.f_flash = 80000000L
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=ESP32-S2
|
||||
-DBOARD_HAS_PSRAM
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1 # try disabling and enabling unflag above in case of board-specific issues, will disable Serial
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
-DARDUINO_USB_MSC_ON_BOOT=0
|
||||
-DARDUINO_USB_DFU_ON_BOOT=0
|
||||
-DLOLIN_WIFI_FIX ; seems to work much better with this
|
||||
-D WLED_USE_PSRAM
|
||||
; -D WLED_USE_UNREAL_MATH ;; may cause wrong sunset/sunrise times, but saves 6792 bytes FLASH
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
-D CONFIG_ASYNC_TCP_USE_WDT=0
|
||||
-D LEDPIN=16
|
||||
-D BTNPIN=18
|
||||
-D RLYPIN=9
|
||||
-D IRPIN=7
|
||||
-D HW_PIN_SCL=35
|
||||
-D HW_PIN_SDA=33
|
||||
-D HW_PIN_CLOCKSPI=7
|
||||
@ -631,188 +461,3 @@ build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=
|
||||
-D HW_PIN_MISOSPI=9
|
||||
; -D STATUSLED=15
|
||||
lib_deps = ${esp32s2.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# custom board configurations
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
[env:esp32c3dev_2MB]
|
||||
;; for ESP32-C3 boards with 2MB flash (instead of 4MB).
|
||||
;; this board need a specific partition file. OTA not possible.
|
||||
extends = esp32c3
|
||||
platform = ${esp32c3.platform}
|
||||
platform_packages = ${esp32c3.platform_packages}
|
||||
board = esp32-c3-devkitm-1
|
||||
build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
-D WLED_DISABLE_OTA
|
||||
; -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB
|
||||
-DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip
|
||||
build_unflags = ${common.build_unflags}
|
||||
upload_speed = 115200
|
||||
lib_deps = ${esp32c3.lib_deps}
|
||||
board_build.partitions = tools/WLED_ESP32_2MB_noOTA.csv
|
||||
board_build.flash_mode = dio
|
||||
|
||||
[env:wemos_shield_esp32]
|
||||
board = esp32dev
|
||||
platform = ${esp32.platform}
|
||||
platform_packages = ${esp32.platform_packages}
|
||||
upload_speed = 460800
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32}
|
||||
-D LEDPIN=16
|
||||
-D RLYPIN=19
|
||||
-D BTNPIN=17
|
||||
-D IRPIN=18
|
||||
-D UWLED_USE_MY_CONFIG
|
||||
-D USERMOD_DALLASTEMPERATURE
|
||||
-D USERMOD_FOUR_LINE_DISPLAY
|
||||
-D TEMPERATURE_PIN=23
|
||||
-D USE_ALT_DISPlAY ; new versions of USERMOD_FOUR_LINE_DISPLAY and USERMOD_ROTARY_ENCODER_UI
|
||||
-D USERMOD_AUDIOREACTIVE
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
OneWire@~2.3.5
|
||||
olikraus/U8g2 @ ^2.28.8
|
||||
https://github.com/blazoncek/arduinoFFT.git
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
[env:m5atom]
|
||||
board = esp32dev
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D LEDPIN=27 -D BTNPIN=39
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
platform = ${esp32.platform}
|
||||
platform_packages = ${esp32.platform_packages}
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
[env:sp501e]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags_esp8266} -D LEDPIN=3 -D BTNPIN=1
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:sp511e]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags_esp8266} -D LEDPIN=3 -D BTNPIN=2 -D IRPIN=5 -D WLED_MAX_BUTTONS=3
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:Athom_RGBCW] ;7w and 5w(GU10) bulbs
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,13,5
|
||||
-D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
|
||||
[env:Athom_15w_RGBCW] ;15w bulb
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,5,13
|
||||
-D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 -D WLED_USE_IC_CCT
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
|
||||
[env:Athom_3Pin_Controller] ;small controller with only data
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=0 -D RLYPIN=-1 -D LEDPIN=1 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
|
||||
[env:Athom_4Pin_Controller] ; With clock and data interface
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=0 -D RLYPIN=12 -D LEDPIN=1 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
|
||||
[env:Athom_5Pin_Controller] ;Analog light strip controller
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=0 -D RLYPIN=-1 DATA_PINS=4,12,14,13 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
|
||||
[env:MY9291]
|
||||
board = esp01_1m
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA -D USERMOD_MY9291
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# codm pixel controller board configurations
|
||||
# codm-controller-0_6 can also be used for the TYWE3S controller
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
[env:codm-controller-0_6]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:codm-controller-0_6-rev2]
|
||||
board = esp_wroom_02
|
||||
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}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# EleksTube-IPS
|
||||
# ------------------------------------------------------------------------------
|
||||
[env:elekstube_ips]
|
||||
board = esp32dev
|
||||
platform = ${esp32.platform}
|
||||
platform_packages = ${esp32.platform_packages}
|
||||
upload_speed = 921600
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED
|
||||
-D USERMOD_RTC
|
||||
-D USERMOD_ELEKSTUBE_IPS
|
||||
-D LEDPIN=12
|
||||
-D RLYPIN=27
|
||||
-D BTNPIN=34
|
||||
-D DEFAULT_LED_COUNT=6
|
||||
# Display config
|
||||
-D ST7789_DRIVER
|
||||
-D TFT_WIDTH=135
|
||||
-D TFT_HEIGHT=240
|
||||
-D CGRAM_OFFSET
|
||||
-D TFT_SDA_READ
|
||||
-D TFT_MOSI=23
|
||||
-D TFT_SCLK=18
|
||||
-D TFT_DC=25
|
||||
-D TFT_RST=26
|
||||
-D SPI_FREQUENCY=40000000
|
||||
-D USER_SETUP_LOADED
|
||||
monitor_filters = esp32_exception_decoder
|
||||
lib_deps =
|
||||
${esp32.lib_deps}
|
||||
TFT_eSPI @ ^2.3.70
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
@ -1,65 +0,0 @@
|
||||
# Example PlatformIO Project Configuration Override
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copy to platformio_override.ini to activate overrides
|
||||
# ------------------------------------------------------------------------------
|
||||
# Please visit documentation: https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
default_envs = WLED_tasmota_1M
|
||||
|
||||
[env:WLED_tasmota_1M]
|
||||
board = esp01_1m
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
; *********************************************************************
|
||||
; *** Use custom settings from file my_config.h
|
||||
-DWLED_USE_MY_CONFIG
|
||||
; *********************************************************************
|
||||
;
|
||||
;
|
||||
; *** To use the below defines/overrides, copy and paste each onto it's own line just below build_flags in the section above.
|
||||
;
|
||||
; disable specific features
|
||||
; -D WLED_DISABLE_OTA
|
||||
; -D WLED_DISABLE_ALEXA
|
||||
; -D WLED_DISABLE_HUESYNC
|
||||
; -D WLED_DISABLE_INFRARED
|
||||
; -D WLED_DISABLE_WEBSOCKETS
|
||||
; PIN defines - uncomment and change, if needed:
|
||||
; -D LEDPIN=2
|
||||
; -D BTNPIN=0
|
||||
; -D TOUCHPIN=T0
|
||||
; -D IRPIN=4
|
||||
; -D RLYPIN=12
|
||||
; -D RLYMDE=1
|
||||
; digital LED strip types - uncomment only one ! - this will disable WS281x / SK681x support
|
||||
; -D USE_APA102
|
||||
; -D USE_WS2801
|
||||
; -D USE_LPD8806
|
||||
; PIN defines for 2 wire LEDs
|
||||
-D CLKPIN=0
|
||||
-D DATAPIN=2
|
||||
; to drive analog LED strips (aka 5050) hardware configuration is no longer necessary
|
||||
; configure the settings in the UI as follows (hard):
|
||||
; for the Magic Home LED Controller use PWM pins 5,12,13,15
|
||||
; for the H801 controller use PINs 15,13,12,14 (W2 = 04)
|
||||
; for the BW-LT11 controller use PINs 12,4,14,5
|
||||
;
|
||||
; set the name of the module - make sure there is a quote-backslash-quote before the name and a backslash-quote-quote after the name
|
||||
; -D SERVERNAME="\"WLED\""
|
||||
;
|
||||
; set the number of LEDs
|
||||
; -D DEFAULT_LED_COUNT=30
|
||||
;
|
||||
; set milliampere limit when using ESP pin to power leds
|
||||
; -D ABL_MILLIAMPS_DEFAULT=850
|
||||
;
|
||||
; enable IR by setting remote type
|
||||
; -D IRTYPE=0 ;0 Remote disabled | 1 24-key RGB | 2 24-key with CT | 3 40-key blue | 4 40-key RGB | 5 21-key RGB | 6 6-key black | 7 9-key red | 8 JSON remote
|
||||
;
|
||||
; set default color order of your led strip
|
||||
; -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB
|
495
platformio_override.sample.ini
Normal file
495
platformio_override.sample.ini
Normal file
@ -0,0 +1,495 @@
|
||||
# Example PlatformIO Project Configuration Override
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copy to platformio_override.ini to activate overrides
|
||||
# ------------------------------------------------------------------------------
|
||||
# Please visit documentation: https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
default_envs = WLED_tasmota_1M # define as many as you need
|
||||
|
||||
#----------
|
||||
# SAMPLE
|
||||
#----------
|
||||
[env:WLED_tasmota_1M]
|
||||
extends = env:esp01_1m_full # when you want to extend the existing environment (define only updated options)
|
||||
; board = esp01_1m # uncomment when ou need different board
|
||||
; platform = ${common.platform_wled_default} # uncomment and change when you want particular platform
|
||||
; platform_packages = ${common.platform_packages}
|
||||
; board_build.ldscript = ${common.ldscript_1m128k}
|
||||
; upload_speed = 921600 # fast upload speed (remove ';' if your board supports fast upload speed)
|
||||
# Sample libraries used for various usermods. Uncomment when using particular usermod.
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
; olikraus/U8g2 # @~2.33.15
|
||||
; paulstoffregen/OneWire@~2.3.8
|
||||
; adafruit/Adafruit Unified Sensor@^1.1.4
|
||||
; adafruit/DHT sensor library@^1.4.1
|
||||
; adafruit/Adafruit BME280 Library@^2.2.2
|
||||
; Wire
|
||||
; robtillaart/SHT85@~0.3.3
|
||||
; gmag11/QuickESPNow ;@ 0.6.2
|
||||
; https://github.com/blazoncek/QuickESPNow.git#optional-debug ;; exludes debug library
|
||||
; https://github.com/kosme/arduinoFFT#develop @ 1.9.2+sha.419d7b0 ;; used for USERMOD_AUDIOREACTIVE - using "known working" hash
|
||||
; build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
;
|
||||
; *** To use the below defines/overrides, copy and paste each onto it's own line just below build_flags in the section above.
|
||||
;
|
||||
; disable specific features
|
||||
; -D WLED_DISABLE_OTA
|
||||
; -D WLED_DISABLE_ALEXA
|
||||
; -D WLED_DISABLE_HUESYNC
|
||||
; -D WLED_DISABLE_LOXONE
|
||||
; -D WLED_DISABLE_INFRARED
|
||||
; -D WLED_DISABLE_WEBSOCKETS
|
||||
; -D WLED_DISABLE_MQTT
|
||||
; -D WLED_DISABLE_ADALIGHT
|
||||
; -D WLED_DISABLE_2D
|
||||
; -D WLED_DISABLE_PXMAGIC
|
||||
; -D WLED_DISABLE_ESPNOW
|
||||
; -D WLED_DISABLE_BROWNOUT_DET
|
||||
;
|
||||
; PIN defines - uncomment and change, if needed:
|
||||
; -D LEDPIN=2
|
||||
; or use this for multiple outputs
|
||||
; -D DATA_PINS=1,3
|
||||
; -D BTNPIN=0
|
||||
; -D IRPIN=4
|
||||
; -D RLYPIN=12
|
||||
; -D RLYMDE=1
|
||||
; -D LED_BUILTIN=2 # GPIO of built-in LED
|
||||
;
|
||||
; Limit max buses
|
||||
; -D WLED_MAX_BUSSES=2
|
||||
;
|
||||
; Configure default WiFi
|
||||
; -D CLIENT_SSID='"MyNetwork"'
|
||||
; -D CLIENT_PASS='"Netw0rkPassw0rd"'
|
||||
;
|
||||
; Configure and use Ethernet
|
||||
; -D WLED_USE_ETHERNET
|
||||
; -D WLED_ETH_DEFAULT=5
|
||||
; do not use pins 5, (16,) 17, 18, 19, 21, 22, 23, 25, 26, 27 for anything but ethernet
|
||||
; -D PHY_ADDR=0 -D ETH_PHY_POWER=5 -D ETH_PHY_MDC=23 -D ETH_PHY_MDIO=18
|
||||
; -D ETH_CLK_MODE=ETH_CLOCK_GPIO17_OUT
|
||||
;
|
||||
; NTP time configuration
|
||||
; -D WLED_NTP_ENABLED=true
|
||||
; -D WLED_TIMEZONE=2
|
||||
; -D WLED_LAT=48.86
|
||||
; -D WLED_LON=2.33
|
||||
;
|
||||
; Use Watchdog timer with 10s guard
|
||||
; -D WLED_WATCHDOG_TIMEOUT=10
|
||||
;
|
||||
; Create debug build (with remote debug)
|
||||
; -D WLED_DEBUG
|
||||
; -D WLED_DEBUG_HOST='"192.168.0.100"'
|
||||
; -D WLED_DEBUG_PORT=7868
|
||||
;
|
||||
; Use Autosave usermod and set it to do save after 90s
|
||||
; -D USERMOD_AUTO_SAVE
|
||||
; -D AUTOSAVE_AFTER_SEC=90
|
||||
;
|
||||
; Use 4 Line Display usermod with SPI display
|
||||
; -D USERMOD_FOUR_LINE_DISPLAY
|
||||
; -D USE_ALT_DISPlAY # mandatory
|
||||
; -DFLD_SPI_DEFAULT
|
||||
; -D FLD_TYPE=SSD1306_SPI64
|
||||
; -D FLD_PIN_CLOCKSPI=14
|
||||
; -D FLD_PIN_DATASPI=13
|
||||
; -D FLD_PIN_DC=26
|
||||
; -D FLD_PIN_CS=15
|
||||
; -D FLD_PIN_RESET=27
|
||||
;
|
||||
; Use Rotary encoder usermod (in conjunction with 4LD)
|
||||
; -D USERMOD_ROTARY_ENCODER_UI
|
||||
; -D ENCODER_DT_PIN=5
|
||||
; -D ENCODER_CLK_PIN=18
|
||||
; -D ENCODER_SW_PIN=19
|
||||
;
|
||||
; Use Dallas DS18B20 temperature sensor usermod and configure it to use GPIO13
|
||||
; -D USERMOD_DALLASTEMPERATURE
|
||||
; -D TEMPERATURE_PIN=13
|
||||
;
|
||||
; Use Multi Relay usermod and configure it to use 6 relays and appropriate GPIO
|
||||
; -D USERMOD_MULTI_RELAY
|
||||
; -D MULTI_RELAY_MAX_RELAYS=6
|
||||
; -D MULTI_RELAY_PINS=12,23,22,21,24,25
|
||||
;
|
||||
; Use PIR sensor usermod and configure it to use GPIO4 and timer of 60s
|
||||
; -D USERMOD_PIRSWITCH
|
||||
; -D PIR_SENSOR_PIN=4
|
||||
; -D PIR_SENSOR_OFF_SEC=60
|
||||
;
|
||||
; Use Audioreactive usermod and configure I2S microphone
|
||||
; -D USERMOD_AUDIOREACTIVE
|
||||
; -D UM_AUDIOREACTIVE_USE_NEW_FFT
|
||||
; -D AUDIOPIN=-1
|
||||
; -D DMTYPE=1 # 0-analog/disabled, 1-I2S generic, 2-ES7243, 3-SPH0645, 4-I2S+mclk, 5-I2S PDM
|
||||
; -D I2S_SDPIN=36
|
||||
; -D I2S_WSPIN=23
|
||||
; -D I2S_CKPIN=19
|
||||
;
|
||||
; Use PWM fan usermod
|
||||
; -D USERMOD_PWM_FAN
|
||||
; -D TACHO_PIN=33
|
||||
; -D PWM_PIN=32
|
||||
;
|
||||
; Use built-in or custom LED as a status indicator (assumes LED is connected to GPIO16)
|
||||
; -D STATUSLED=16
|
||||
;
|
||||
; set the name of the module - make sure there is a quote-backslash-quote before the name and a backslash-quote-quote after the name
|
||||
; -D SERVERNAME="\"WLED\""
|
||||
;
|
||||
; set the number of LEDs
|
||||
; -D DEFAULT_LED_COUNT=30
|
||||
; or this for multiple outputs
|
||||
; -D PIXEL_COUNTS=30,30
|
||||
;
|
||||
; set milliampere limit when using ESP pin to power leds
|
||||
; -D ABL_MILLIAMPS_DEFAULT=850
|
||||
;
|
||||
; enable IR by setting remote type
|
||||
; -D IRTYPE=0 ;0 Remote disabled | 1 24-key RGB | 2 24-key with CT | 3 40-key blue | 4 40-key RGB | 5 21-key RGB | 6 6-key black | 7 9-key red | 8 JSON remote
|
||||
;
|
||||
; set default color order of your led strip
|
||||
; -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB
|
||||
;
|
||||
; use PSRAM if a device (ESP) has one
|
||||
; -DBOARD_HAS_PSRAM
|
||||
; -D WLED_USE_PSRAM
|
||||
;
|
||||
; configure I2C and SPI interface (for various hardware)
|
||||
; -D I2CSDAPIN=33 # initialise interface
|
||||
; -D I2CSCLPIN=35 # initialise interface
|
||||
; -D HW_PIN_SCL=35
|
||||
; -D HW_PIN_SDA=33
|
||||
; -D HW_PIN_CLOCKSPI=7
|
||||
; -D HW_PIN_DATASPI=11
|
||||
; -D HW_PIN_MISOSPI=9
|
||||
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# PRE-CONFIGURED DEVELOPMENT BOARDS AND CONTROLLERS
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
[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}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[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}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
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}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[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
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp32dev_qio80]
|
||||
board = esp32dev
|
||||
platform = ${esp32.platform}
|
||||
platform_packages = ${esp32.platform_packages}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_qio80 #-D WLED_DISABLE_BROWNOUT_DET
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
|
||||
[env:esp32dev_V4_dio80]
|
||||
;; experimental ESP32 env using ESP-IDF V4.4.x
|
||||
;; Warning: this build environment is not stable!!
|
||||
;; please erase your device before installing.
|
||||
board = esp32dev
|
||||
platform = ${esp32_idf_V4.platform}
|
||||
platform_packages = ${esp32_idf_V4.platform_packages}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_V4_qio80 #-D WLED_DISABLE_BROWNOUT_DET
|
||||
lib_deps = ${esp32_idf_V4.lib_deps}
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.partitions = ${esp32_idf_V4.default_partitions}
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = dio
|
||||
|
||||
[env:esp32s2_saola]
|
||||
board = esp32-s2-saola-1
|
||||
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.2/platform-tasmota-espressif32-2.0.2.zip
|
||||
platform_packages =
|
||||
framework = arduino
|
||||
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||
board_build.flash_mode = qio
|
||||
upload_speed = 460800
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=S2_saola
|
||||
;-DLOLIN_WIFI_FIX ;; try this in case Wifi does not work
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
lib_deps = ${esp32s2.lib_deps}
|
||||
|
||||
[env:esp32s3dev_8MB_PSRAM_qspi]
|
||||
;; ESP32-TinyS3 development board, with 8MB FLASH and PSRAM (memory_type: qio_qspi)
|
||||
extends = env:esp32s3dev_8MB_PSRAM_opi
|
||||
;board = um_tinys3 ; -> needs workaround from https://github.com/Aircoookie/WLED/pull/2905#issuecomment-1328049860
|
||||
board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support
|
||||
board_build.arduino.memory_type = qio_qspi ;; use with PSRAM: 2MB or 4MB
|
||||
|
||||
[env:esp8285_4CH_MagicHome]
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp8285_H801]
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini_5CH_Shojo_PCB]
|
||||
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 WLED_USE_SHOJO_PCB
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini_debug]
|
||||
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}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini_ota]
|
||||
board = d1_mini
|
||||
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}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:anavi_miracle_controller]
|
||||
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=12 -D IRPIN=-1 -D RLYPIN=2
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp32c3dev_2MB]
|
||||
;; for ESP32-C3 boards with 2MB flash (instead of 4MB).
|
||||
;; this board need a specific partition file. OTA not possible.
|
||||
extends = esp32c3
|
||||
platform = ${esp32c3.platform}
|
||||
platform_packages = ${esp32c3.platform_packages}
|
||||
board = esp32-c3-devkitm-1
|
||||
build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
-D WLED_DISABLE_OTA
|
||||
; -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB
|
||||
-DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip
|
||||
build_unflags = ${common.build_unflags}
|
||||
upload_speed = 115200
|
||||
lib_deps = ${esp32c3.lib_deps}
|
||||
board_build.partitions = tools/WLED_ESP32_2MB_noOTA.csv
|
||||
board_build.flash_mode = dio
|
||||
|
||||
[env:wemos_shield_esp32]
|
||||
board = esp32dev
|
||||
platform = ${esp32.platform}
|
||||
platform_packages = ${esp32.platform_packages}
|
||||
upload_speed = 460800
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32}
|
||||
-D LEDPIN=16
|
||||
-D RLYPIN=19
|
||||
-D BTNPIN=17
|
||||
-D IRPIN=18
|
||||
-D UWLED_USE_MY_CONFIG
|
||||
-D USERMOD_DALLASTEMPERATURE
|
||||
-D USERMOD_FOUR_LINE_DISPLAY
|
||||
-D TEMPERATURE_PIN=23
|
||||
-D USE_ALT_DISPlAY ; new versions of USERMOD_FOUR_LINE_DISPLAY and USERMOD_ROTARY_ENCODER_UI
|
||||
-D USERMOD_AUDIOREACTIVE
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
OneWire@~2.3.5
|
||||
olikraus/U8g2 @ ^2.28.8
|
||||
https://github.com/blazoncek/arduinoFFT.git
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
[env:m5atom]
|
||||
board = esp32dev
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D LEDPIN=27 -D BTNPIN=39
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
platform = ${esp32.platform}
|
||||
platform_packages = ${esp32.platform_packages}
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
[env:sp501e]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags_esp8266} -D LEDPIN=3 -D BTNPIN=1
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:sp511e]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags_esp8266} -D LEDPIN=3 -D BTNPIN=2 -D IRPIN=5 -D WLED_MAX_BUTTONS=3
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:Athom_RGBCW] ;7w and 5w(GU10) bulbs
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,13,5
|
||||
-D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:Athom_15w_RGBCW] ;15w bulb
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,5,13
|
||||
-D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 -D WLED_USE_IC_CCT
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:Athom_3Pin_Controller] ;small controller with only data
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=0 -D RLYPIN=-1 -D LEDPIN=1 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:Athom_4Pin_Controller] ; With clock and data interface
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=0 -D RLYPIN=12 -D LEDPIN=1 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:Athom_5Pin_Controller] ;Analog light strip controller
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 -D BTNPIN=0 -D RLYPIN=-1 DATA_PINS=4,12,14,13 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:MY9291]
|
||||
board = esp01_1m
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA -D USERMOD_MY9291
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# codm pixel controller board configurations
|
||||
# codm-controller-0_6 can also be used for the TYWE3S controller
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
[env:codm-controller-0_6]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:codm-controller-0_6-rev2]
|
||||
board = esp_wroom_02
|
||||
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}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# EleksTube-IPS
|
||||
# ------------------------------------------------------------------------------
|
||||
[env:elekstube_ips]
|
||||
board = esp32dev
|
||||
platform = ${esp32.platform}
|
||||
platform_packages = ${esp32.platform_packages}
|
||||
upload_speed = 921600
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED
|
||||
-D USERMOD_RTC
|
||||
-D USERMOD_ELEKSTUBE_IPS
|
||||
-D LEDPIN=12
|
||||
-D RLYPIN=27
|
||||
-D BTNPIN=34
|
||||
-D DEFAULT_LED_COUNT=6
|
||||
# Display config
|
||||
-D ST7789_DRIVER
|
||||
-D TFT_WIDTH=135
|
||||
-D TFT_HEIGHT=240
|
||||
-D CGRAM_OFFSET
|
||||
-D TFT_SDA_READ
|
||||
-D TFT_MOSI=23
|
||||
-D TFT_SCLK=18
|
||||
-D TFT_DC=25
|
||||
-D TFT_RST=26
|
||||
-D SPI_FREQUENCY=40000000
|
||||
-D USER_SETUP_LOADED
|
||||
monitor_filters = esp32_exception_decoder
|
||||
lib_deps =
|
||||
${esp32.lib_deps}
|
||||
TFT_eSPI @ ^2.3.70
|
||||
board_build.partitions = ${esp32.default_partitions}
|
@ -1,6 +1,6 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with python 3.8
|
||||
# To update, run:
|
||||
# This file is autogenerated by pip-compile with Python 3.12
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile
|
||||
#
|
||||
@ -21,7 +21,9 @@ click==8.1.3
|
||||
# platformio
|
||||
# uvicorn
|
||||
colorama==0.4.6
|
||||
# via platformio
|
||||
# via
|
||||
# click
|
||||
# platformio
|
||||
h11==0.14.0
|
||||
# via
|
||||
# uvicorn
|
||||
|
205
tools/cdata-test.js
Normal file
205
tools/cdata-test.js
Normal file
@ -0,0 +1,205 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('node:assert');
|
||||
const { describe, it, before, after } = require('node:test');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const child_process = require('child_process');
|
||||
const util = require('util');
|
||||
const execPromise = util.promisify(child_process.exec);
|
||||
|
||||
process.env.NODE_ENV = 'test'; // Set the environment to testing
|
||||
const cdata = require('./cdata.js');
|
||||
|
||||
describe('Function', () => {
|
||||
const testFolderPath = path.join(__dirname, 'testFolder');
|
||||
const oldFilePath = path.join(testFolderPath, 'oldFile.txt');
|
||||
const newFilePath = path.join(testFolderPath, 'newFile.txt');
|
||||
|
||||
// Create a temporary file before the test
|
||||
before(() => {
|
||||
// Create test folder
|
||||
if (!fs.existsSync(testFolderPath)) {
|
||||
fs.mkdirSync(testFolderPath);
|
||||
}
|
||||
|
||||
// Create an old file
|
||||
fs.writeFileSync(oldFilePath, 'This is an old file.');
|
||||
// Modify the 'mtime' to simulate an old file
|
||||
const oldTime = new Date();
|
||||
oldTime.setFullYear(oldTime.getFullYear() - 1);
|
||||
fs.utimesSync(oldFilePath, oldTime, oldTime);
|
||||
|
||||
// Create a new file
|
||||
fs.writeFileSync(newFilePath, 'This is a new file.');
|
||||
});
|
||||
|
||||
// delete the temporary files after the test
|
||||
after(() => {
|
||||
fs.rmSync(testFolderPath, { recursive: true });
|
||||
});
|
||||
|
||||
describe('isFileNewerThan', async () => {
|
||||
it('should return true if the file is newer than the provided time', async () => {
|
||||
const pastTime = Date.now() - 10000; // 10 seconds ago
|
||||
assert.strictEqual(cdata.isFileNewerThan(newFilePath, pastTime), true);
|
||||
});
|
||||
|
||||
it('should return false if the file is older than the provided time', async () => {
|
||||
assert.strictEqual(cdata.isFileNewerThan(oldFilePath, Date.now()), false);
|
||||
});
|
||||
|
||||
it('should throw an exception if the file does not exist', async () => {
|
||||
assert.throws(() => {
|
||||
cdata.isFileNewerThan('nonexistent.txt', Date.now());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAnyFileInFolderNewerThan', async () => {
|
||||
it('should return true if a file in the folder is newer than the given time', async () => {
|
||||
const time = fs.statSync(path.join(testFolderPath, 'oldFile.txt')).mtime;
|
||||
assert.strictEqual(cdata.isAnyFileInFolderNewerThan(testFolderPath, time), true);
|
||||
});
|
||||
|
||||
it('should return false if no files in the folder are newer than the given time', async () => {
|
||||
assert.strictEqual(cdata.isAnyFileInFolderNewerThan(testFolderPath, new Date()), false);
|
||||
});
|
||||
|
||||
it('should throw an exception if the folder does not exist', async () => {
|
||||
assert.throws(() => {
|
||||
cdata.isAnyFileInFolderNewerThan('nonexistent', new Date());
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Script', () => {
|
||||
const folderPath = 'wled00';
|
||||
const dataPath = path.join(folderPath, 'data');
|
||||
|
||||
before(() => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
// Backup files
|
||||
fs.cpSync("wled00/data", "wled00Backup", { recursive: true });
|
||||
fs.cpSync("tools/cdata.js", "cdata.bak.js");
|
||||
});
|
||||
after(() => {
|
||||
// Restore backup
|
||||
fs.rmSync("wled00/data", { recursive: true });
|
||||
fs.renameSync("wled00Backup", "wled00/data");
|
||||
fs.rmSync("tools/cdata.js");
|
||||
fs.renameSync("cdata.bak.js", "tools/cdata.js");
|
||||
});
|
||||
|
||||
// delete all html_*.h files
|
||||
async function deleteBuiltFiles() {
|
||||
const files = await fs.promises.readdir(folderPath);
|
||||
await Promise.all(files.map(file => {
|
||||
if (file.startsWith('html_') && path.extname(file) === '.h') {
|
||||
return fs.promises.unlink(path.join(folderPath, file));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// check if html_*.h files were created
|
||||
async function checkIfBuiltFilesExist() {
|
||||
const files = await fs.promises.readdir(folderPath);
|
||||
const htmlFiles = files.filter(file => file.startsWith('html_') && path.extname(file) === '.h');
|
||||
assert(htmlFiles.length > 0, 'html_*.h files were not created');
|
||||
}
|
||||
|
||||
async function runAndCheckIfBuiltFilesExist() {
|
||||
await execPromise('node tools/cdata.js');
|
||||
await checkIfBuiltFilesExist();
|
||||
}
|
||||
|
||||
async function checkIfFileWasNewlyCreated(file) {
|
||||
const modifiedTime = fs.statSync(file).mtimeMs;
|
||||
assert(Date.now() - modifiedTime < 500, file + ' was not modified');
|
||||
}
|
||||
|
||||
async function testFileModification(sourceFilePath, resultFile) {
|
||||
// run cdata.js to ensure html_*.h files are created
|
||||
await execPromise('node tools/cdata.js');
|
||||
|
||||
// modify file
|
||||
fs.appendFileSync(sourceFilePath, ' ');
|
||||
// delay for 1 second to ensure the modified time is different
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// run script cdata.js again and wait for it to finish
|
||||
await execPromise('node tools/cdata.js');
|
||||
|
||||
checkIfFileWasNewlyCreated(path.join(folderPath, resultFile));
|
||||
}
|
||||
|
||||
describe('should build if', () => {
|
||||
it('html_*.h files are missing', async () => {
|
||||
await deleteBuiltFiles();
|
||||
await runAndCheckIfBuiltFilesExist();
|
||||
});
|
||||
|
||||
it('only one html_*.h file is missing', async () => {
|
||||
// run script cdata.js and wait for it to finish
|
||||
await execPromise('node tools/cdata.js');
|
||||
|
||||
// delete a random html_*.h file
|
||||
let files = await fs.promises.readdir(folderPath);
|
||||
let htmlFiles = files.filter(file => file.startsWith('html_') && path.extname(file) === '.h');
|
||||
const randomFile = htmlFiles[Math.floor(Math.random() * htmlFiles.length)];
|
||||
await fs.promises.unlink(path.join(folderPath, randomFile));
|
||||
|
||||
await runAndCheckIfBuiltFilesExist();
|
||||
});
|
||||
|
||||
it('script was executed with -f or --force', async () => {
|
||||
await execPromise('node tools/cdata.js');
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
await execPromise('node tools/cdata.js --force');
|
||||
await checkIfFileWasNewlyCreated(path.join(folderPath, 'html_ui.h'));
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
await execPromise('node tools/cdata.js -f');
|
||||
await checkIfFileWasNewlyCreated(path.join(folderPath, 'html_ui.h'));
|
||||
});
|
||||
|
||||
it('a file changes', async () => {
|
||||
await testFileModification(path.join(dataPath, 'index.htm'), 'html_ui.h');
|
||||
});
|
||||
|
||||
it('a inlined file changes', async () => {
|
||||
await testFileModification(path.join(dataPath, 'index.js'), 'html_ui.h');
|
||||
});
|
||||
|
||||
it('a settings file changes', async () => {
|
||||
await testFileModification(path.join(dataPath, 'settings_leds.htm'), 'html_ui.h');
|
||||
});
|
||||
|
||||
it('the favicon changes', async () => {
|
||||
await testFileModification(path.join(dataPath, 'favicon.ico'), 'html_ui.h');
|
||||
});
|
||||
|
||||
it('cdata.js changes', async () => {
|
||||
await testFileModification('tools/cdata.js', 'html_ui.h');
|
||||
});
|
||||
});
|
||||
|
||||
describe('should not build if', () => {
|
||||
it('the files are already built', async () => {
|
||||
await deleteBuiltFiles();
|
||||
|
||||
// run script cdata.js and wait for it to finish
|
||||
let startTime = Date.now();
|
||||
await execPromise('node tools/cdata.js');
|
||||
const firstRunTime = Date.now() - startTime;
|
||||
|
||||
// run script cdata.js and wait for it to finish
|
||||
startTime = Date.now();
|
||||
await execPromise('node tools/cdata.js');
|
||||
const secondRunTime = Date.now() - startTime;
|
||||
|
||||
// check if second run was faster than the first (must be at least 2x faster)
|
||||
assert(secondRunTime < firstRunTime / 2, 'html_*.h files were rebuilt');
|
||||
});
|
||||
});
|
||||
});
|
245
tools/cdata.js
245
tools/cdata.js
@ -16,18 +16,49 @@
|
||||
*/
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require('path');
|
||||
const path = require("path");
|
||||
const inliner = require("inliner");
|
||||
const zlib = require("zlib");
|
||||
const CleanCSS = require("clean-css");
|
||||
const MinifyHTML = require("html-minifier-terser").minify;
|
||||
const minifyHtml = require("html-minifier-terser").minify;
|
||||
const packageJson = require("../package.json");
|
||||
|
||||
// Export functions for testing
|
||||
module.exports = { isFileNewerThan, isAnyFileInFolderNewerThan };
|
||||
|
||||
const output = ["wled00/html_ui.h", "wled00/html_pixart.h", "wled00/html_cpal.h", "wled00/html_pxmagic.h", "wled00/html_settings.h", "wled00/html_other.h"]
|
||||
|
||||
/**
|
||||
// \x1b[34m is blue, \x1b[36m is cyan, \x1b[0m is reset
|
||||
const wledBanner = `
|
||||
\t\x1b[34m## ## ## ######## ########
|
||||
\t\x1b[34m## ## ## ## ## ## ##
|
||||
\t\x1b[34m## ## ## ## ## ## ##
|
||||
\t\x1b[34m## ## ## ## ###### ## ##
|
||||
\t\x1b[34m## ## ## ## ## ## ##
|
||||
\t\x1b[34m## ## ## ## ## ## ##
|
||||
\t\x1b[34m ### ### ######## ######## ########
|
||||
\t\t\x1b[36mbuild script for web UI
|
||||
\x1b[0m`;
|
||||
|
||||
const singleHeader = `/*
|
||||
* Binary array for the Web UI.
|
||||
* gzip is used for smaller size and improved speeds.
|
||||
*
|
||||
* Please see https://kno.wled.ge/advanced/custom-features/#changing-web-ui
|
||||
* to find out how to easily modify the web UI source!
|
||||
*/
|
||||
|
||||
`;
|
||||
|
||||
const multiHeader = `/*
|
||||
* More web UI HTML source arrays.
|
||||
* This file is auto generated, please don't make any changes manually.
|
||||
*
|
||||
* Instead, see https://kno.wled.ge/advanced/custom-features/#changing-web-ui
|
||||
* to find out how to easily modify the web UI source!
|
||||
*/
|
||||
`;
|
||||
|
||||
function hexdump(buffer, isHex = false) {
|
||||
let lines = [];
|
||||
|
||||
@ -54,183 +85,112 @@ function hexdump(buffer,isHex=false) {
|
||||
return lines.join(",\n");
|
||||
}
|
||||
|
||||
function strReplace(str, search, replacement) {
|
||||
return str.split(search).join(replacement);
|
||||
}
|
||||
|
||||
function adoptVersionAndRepo(html) {
|
||||
let repoUrl = packageJson.repository ? packageJson.repository.url : undefined;
|
||||
if (repoUrl) {
|
||||
repoUrl = repoUrl.replace(/^git\+/, "");
|
||||
repoUrl = repoUrl.replace(/\.git$/, "");
|
||||
// Replace we
|
||||
html = strReplace(html, "https://github.com/atuline/WLED", repoUrl);
|
||||
html = strReplace(html, "https://github.com/Aircoookie/WLED", repoUrl);
|
||||
html = html.replaceAll("https://github.com/atuline/WLED", repoUrl);
|
||||
html = html.replaceAll("https://github.com/Aircoookie/WLED", repoUrl);
|
||||
}
|
||||
let version = packageJson.version;
|
||||
if (version) {
|
||||
html = strReplace(html, "##VERSION##", version);
|
||||
html = html.replaceAll("##VERSION##", version);
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
function filter(str, type) {
|
||||
str = adoptVersionAndRepo(str);
|
||||
if (type === undefined) {
|
||||
async function minify(str, type = "plain") {
|
||||
const options = {
|
||||
collapseWhitespace: true,
|
||||
collapseBooleanAttributes: true,
|
||||
collapseInlineTagWhitespace: true,
|
||||
minifyCSS: true,
|
||||
minifyJS: true,
|
||||
removeAttributeQuotes: true,
|
||||
removeComments: true,
|
||||
sortAttributes: true,
|
||||
sortClassName: true,
|
||||
};
|
||||
|
||||
if (type == "plain") {
|
||||
return str;
|
||||
} else if (type == "css-minify") {
|
||||
return new CleanCSS({}).minify(str).styles;
|
||||
} else if (type == "js-minify") {
|
||||
return MinifyHTML('<script>' + str + '</script>', {
|
||||
collapseWhitespace: true,
|
||||
minifyJS: true,
|
||||
continueOnParseError: false,
|
||||
removeComments: true,
|
||||
}).replace(/<[\/]*script>/g,'');
|
||||
return await minifyHtml('<script>' + str + '</script>', options).replace(/<[\/]*script>/g, '');
|
||||
} else if (type == "html-minify") {
|
||||
return MinifyHTML(str, {
|
||||
collapseWhitespace: true,
|
||||
maxLineLength: 80,
|
||||
minifyCSS: true,
|
||||
minifyJS: true,
|
||||
continueOnParseError: false,
|
||||
removeComments: true,
|
||||
});
|
||||
} else if (type == "html-minify-ui") {
|
||||
return MinifyHTML(str, {
|
||||
collapseWhitespace: true,
|
||||
conservativeCollapse: true,
|
||||
maxLineLength: 80,
|
||||
minifyCSS: true,
|
||||
minifyJS: true,
|
||||
continueOnParseError: false,
|
||||
removeComments: true,
|
||||
});
|
||||
} else {
|
||||
console.warn("Unknown filter: " + type);
|
||||
return str;
|
||||
}
|
||||
return await minifyHtml(str, options);
|
||||
}
|
||||
|
||||
function writeHtmlGzipped(sourceFile, resultFile, page) {
|
||||
throw new Error("Unknown filter: " + type);
|
||||
}
|
||||
|
||||
async function writeHtmlGzipped(sourceFile, resultFile, page) {
|
||||
console.info("Reading " + sourceFile);
|
||||
new inliner(sourceFile, function (error, html) {
|
||||
console.info("Inlined " + html.length + " characters");
|
||||
html = filter(html, "html-minify-ui");
|
||||
console.info("Minified to " + html.length + " characters");
|
||||
|
||||
if (error) {
|
||||
console.warn(error);
|
||||
throw error;
|
||||
}
|
||||
new inliner(sourceFile, async function (error, html) {
|
||||
if (error) throw error;
|
||||
|
||||
html = adoptVersionAndRepo(html);
|
||||
zlib.gzip(html, { level: zlib.constants.Z_BEST_COMPRESSION }, function (error, result) {
|
||||
if (error) {
|
||||
console.warn(error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
console.info("Compressed " + result.length + " bytes");
|
||||
const originalLength = html.length;
|
||||
html = await minify(html, "html-minify");
|
||||
const result = zlib.gzipSync(html, { level: zlib.constants.Z_BEST_COMPRESSION });
|
||||
console.info("Minified and compressed " + sourceFile + " from " + originalLength + " to " + result.length + " bytes");
|
||||
const array = hexdump(result);
|
||||
const src = `/*
|
||||
* Binary array for the Web UI.
|
||||
* gzip is used for smaller size and improved speeds.
|
||||
*
|
||||
* Please see https://kno.wled.ge/advanced/custom-features/#changing-web-ui
|
||||
* to find out how to easily modify the web UI source!
|
||||
*/
|
||||
|
||||
// Autogenerated from ${sourceFile}, do not edit!!
|
||||
const uint16_t PAGE_${page}_L = ${result.length};
|
||||
const uint8_t PAGE_${page}[] PROGMEM = {
|
||||
${array}
|
||||
};
|
||||
`;
|
||||
let src = singleHeader;
|
||||
src += `const uint16_t PAGE_${page}_L = ${result.length};\n`;
|
||||
src += `const uint8_t PAGE_${page}[] PROGMEM = {\n${array}\n};\n\n`;
|
||||
console.info("Writing " + resultFile);
|
||||
fs.writeFileSync(resultFile, src);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function specToChunk(srcDir, s) {
|
||||
if (s.method == "plaintext") {
|
||||
async function specToChunk(srcDir, s) {
|
||||
const buf = fs.readFileSync(srcDir + "/" + s.file);
|
||||
const str = buf.toString("utf-8");
|
||||
const chunk = `
|
||||
// Autogenerated from ${srcDir}/${s.file}, do not edit!!
|
||||
const char ${s.name}[] PROGMEM = R"${s.prepend || ""}${filter(str, s.filter)}${
|
||||
s.append || ""
|
||||
}";
|
||||
let chunk = `\n// Autogenerated from ${srcDir}/${s.file}, do not edit!!\n`
|
||||
|
||||
`;
|
||||
return s.mangle ? s.mangle(chunk) : chunk;
|
||||
} else if (s.method == "gzip") {
|
||||
const buf = fs.readFileSync(srcDir + "/" + s.file);
|
||||
var str = buf.toString('utf-8');
|
||||
if (s.method == "plaintext" || s.method == "gzip") {
|
||||
let str = buf.toString("utf-8");
|
||||
str = adoptVersionAndRepo(str);
|
||||
const originalLength = str.length;
|
||||
if (s.method == "gzip") {
|
||||
if (s.mangle) str = s.mangle(str);
|
||||
const zip = zlib.gzipSync(filter(str, s.filter), { level: zlib.constants.Z_BEST_COMPRESSION });
|
||||
const result = hexdump(zip.toString('hex'), true);
|
||||
const chunk = `
|
||||
// Autogenerated from ${srcDir}/${s.file}, do not edit!!
|
||||
const uint16_t ${s.name}_length = ${zip.length};
|
||||
const uint8_t ${s.name}[] PROGMEM = {
|
||||
${result}
|
||||
};
|
||||
|
||||
`;
|
||||
return chunk;
|
||||
} else if (s.method == "binary") {
|
||||
const buf = fs.readFileSync(srcDir + "/" + s.file);
|
||||
const result = hexdump(buf);
|
||||
const chunk = `
|
||||
// Autogenerated from ${srcDir}/${s.file}, do not edit!!
|
||||
const uint16_t ${s.name}_length = ${buf.length};
|
||||
const uint8_t ${s.name}[] PROGMEM = {
|
||||
${result}
|
||||
};
|
||||
|
||||
`;
|
||||
const zip = zlib.gzipSync(await minify(str, s.filter), { level: zlib.constants.Z_BEST_COMPRESSION });
|
||||
console.info("Minified and compressed " + s.file + " from " + originalLength + " to " + zip.length + " bytes");
|
||||
const result = hexdump(zip);
|
||||
chunk += `const uint16_t ${s.name}_length = ${zip.length};\n`;
|
||||
chunk += `const uint8_t ${s.name}[] PROGMEM = {\n${result}\n};\n\n`;
|
||||
return chunk;
|
||||
} else {
|
||||
console.warn("Unknown method: " + s.method);
|
||||
return undefined;
|
||||
const minified = await minify(str, s.filter);
|
||||
console.info("Minified " + s.file + " from " + originalLength + " to " + minified.length + " bytes");
|
||||
chunk += `const char ${s.name}[] PROGMEM = R"${s.prepend || ""}${minified}${s.append || ""}";\n\n`;
|
||||
return s.mangle ? s.mangle(chunk) : chunk;
|
||||
}
|
||||
} else if (s.method == "binary") {
|
||||
const result = hexdump(buf);
|
||||
chunk += `const uint16_t ${s.name}_length = ${buf.length};\n`;
|
||||
chunk += `const uint8_t ${s.name}[] PROGMEM = {\n${result}\n};\n\n`;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
function writeChunks(srcDir, specs, resultFile) {
|
||||
let src = `/*
|
||||
* More web UI HTML source arrays.
|
||||
* This file is auto generated, please don't make any changes manually.
|
||||
* Instead, see https://kno.wled.ge/advanced/custom-features/#changing-web-ui
|
||||
* to find out how to easily modify the web UI source!
|
||||
*/
|
||||
`;
|
||||
specs.forEach((s) => {
|
||||
const file = srcDir + "/" + s.file;
|
||||
try {
|
||||
console.info("Reading " + file + " as " + s.name);
|
||||
src += specToChunk(srcDir, s);
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
"Failed " + s.name + " from " + file,
|
||||
e.message.length > 60 ? e.message.substring(0, 60) : e.message
|
||||
);
|
||||
throw new Error("Unknown method: " + s.method);
|
||||
}
|
||||
|
||||
async function writeChunks(srcDir, specs, resultFile) {
|
||||
let src = multiHeader;
|
||||
for (const s of specs) {
|
||||
console.info("Reading " + srcDir + "/" + s.file + " as " + s.name);
|
||||
src += await specToChunk(srcDir, s);
|
||||
}
|
||||
});
|
||||
console.info("Writing " + src.length + " characters into " + resultFile);
|
||||
fs.writeFileSync(resultFile, src);
|
||||
}
|
||||
|
||||
// Check if a file is newer than a given time
|
||||
function isFileNewerThan(filePath, time) {
|
||||
try {
|
||||
const stats = fs.statSync(filePath);
|
||||
return stats.mtimeMs > time;
|
||||
} catch (e) {
|
||||
console.error(`Failed to get stats for file ${filePath}:`, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if any file in a folder (or its subfolders) is newer than a given time
|
||||
@ -248,21 +208,30 @@ function isAnyFileInFolderNewerThan(folderPath, time) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the web UI is already built
|
||||
function isAlreadyBuilt(folderPath) {
|
||||
let lastBuildTime = Infinity;
|
||||
|
||||
for (const file of output) {
|
||||
try {
|
||||
lastBuildTime = Math.min(lastBuildTime, fs.statSync(file).mtimeMs);
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
if (e.code !== 'ENOENT') throw e;
|
||||
console.info("File " + file + " does not exist. Rebuilding...");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !isAnyFileInFolderNewerThan(folderPath, lastBuildTime);
|
||||
return !isAnyFileInFolderNewerThan(folderPath, lastBuildTime) && !isFileNewerThan("tools/cdata.js", lastBuildTime);
|
||||
}
|
||||
|
||||
// Don't run this script if we're in a test environment
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
return;
|
||||
}
|
||||
|
||||
console.info(wledBanner);
|
||||
|
||||
if (isAlreadyBuilt("wled00/data") && process.argv[2] !== '--force' && process.argv[2] !== '-f') {
|
||||
console.info("Web UI is already built");
|
||||
return;
|
||||
|
@ -15,6 +15,9 @@
|
||||
#define PIR_SENSOR_OFF_SEC 600
|
||||
#endif
|
||||
|
||||
#ifndef PIR_SENSOR_MAX_SENSORS
|
||||
#define PIR_SENSOR_MAX_SENSORS 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This usermod handles PIR sensor states.
|
||||
@ -50,13 +53,13 @@ private:
|
||||
|
||||
volatile unsigned long offTimerStart = 0; // off timer start time
|
||||
volatile bool PIRtriggered = false; // did PIR trigger?
|
||||
bool sensorPinState = LOW; // current PIR sensor pin state
|
||||
bool initDone = false; // status of initialization
|
||||
unsigned long lastLoop = 0;
|
||||
bool sensorPinState[PIR_SENSOR_MAX_SENSORS] = {LOW}; // current PIR sensor pin state
|
||||
|
||||
// configurable parameters
|
||||
bool enabled = true; // PIR sensor enabled
|
||||
int8_t PIRsensorPin = PIR_SENSOR_PIN; // PIR sensor pin
|
||||
int8_t PIRsensorPin[PIR_SENSOR_MAX_SENSORS] = {PIR_SENSOR_PIN}; // PIR sensor pin
|
||||
uint32_t m_switchOffDelay = PIR_SENSOR_OFF_SEC*1000; // delay before switch off after the sensor state goes LOW (10min)
|
||||
uint8_t m_onPreset = 0; // on preset
|
||||
uint8_t m_offPreset = 0; // off preset
|
||||
@ -325,21 +328,29 @@ void PIRsensorSwitch::publishHomeAssistantAutodiscovery()
|
||||
|
||||
bool PIRsensorSwitch::updatePIRsensorState()
|
||||
{
|
||||
bool pinState = digitalRead(PIRsensorPin);
|
||||
if (pinState != sensorPinState) {
|
||||
sensorPinState = pinState; // change previous state
|
||||
bool stateChanged = false;
|
||||
bool allOff = true;
|
||||
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++) {
|
||||
if (PIRsensorPin[i] < 0) continue;
|
||||
|
||||
if (sensorPinState == HIGH) {
|
||||
bool pinState = digitalRead(PIRsensorPin[i]);
|
||||
if (pinState != sensorPinState[i]) {
|
||||
sensorPinState[i] = pinState; // change previous state
|
||||
stateChanged = true;
|
||||
|
||||
if (sensorPinState[i] == HIGH) {
|
||||
offTimerStart = 0;
|
||||
allOff = false;
|
||||
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true);
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (stateChanged) {
|
||||
publishMqtt(!allOff);
|
||||
// start switch off timer
|
||||
offTimerStart = millis();
|
||||
if (allOff) offTimerStart = millis();
|
||||
}
|
||||
publishMqtt(sensorPinState == HIGH);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return stateChanged;
|
||||
}
|
||||
|
||||
bool PIRsensorSwitch::handleOffTimer()
|
||||
@ -356,18 +367,21 @@ bool PIRsensorSwitch::handleOffTimer()
|
||||
|
||||
void PIRsensorSwitch::setup()
|
||||
{
|
||||
if (enabled) {
|
||||
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++) {
|
||||
sensorPinState[i] = LOW;
|
||||
if (PIRsensorPin[i] < 0) continue;
|
||||
// pin retrieved from cfg.json (readFromConfig()) prior to running setup()
|
||||
if (PIRsensorPin >= 0 && pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
|
||||
// PIR Sensor mode INPUT_PULLUP
|
||||
pinMode(PIRsensorPin, INPUT_PULLUP);
|
||||
sensorPinState = digitalRead(PIRsensorPin);
|
||||
if (pinManager.allocatePin(PIRsensorPin[i], false, PinOwner::UM_PIR)) {
|
||||
// PIR Sensor mode INPUT_PULLDOWN
|
||||
#ifdef ESP8266
|
||||
pinMode(PIRsensorPin[i], PIRsensorPin[i]==16 ? INPUT_PULLDOWN_16 : INPUT_PULLUP); // ESP8266 has INPUT_PULLDOWN on GPIO16 only
|
||||
#else
|
||||
pinMode(PIRsensorPin[i], INPUT_PULLDOWN);
|
||||
#endif
|
||||
sensorPinState[i] = digitalRead(PIRsensorPin[i]);
|
||||
} else {
|
||||
if (PIRsensorPin >= 0) {
|
||||
DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed."));
|
||||
}
|
||||
PIRsensorPin = -1; // allocation failed
|
||||
enabled = false;
|
||||
DEBUG_PRINT(F("PIRSensorSwitch pin ")); DEBUG_PRINTLN(i); DEBUG_PRINTLN(F(" allocation failed."));
|
||||
PIRsensorPin[i] = -1; // allocation failed
|
||||
}
|
||||
}
|
||||
initDone = true;
|
||||
@ -382,8 +396,8 @@ void PIRsensorSwitch::onMqttConnect(bool sessionPresent)
|
||||
|
||||
void PIRsensorSwitch::loop()
|
||||
{
|
||||
// only check sensors 4x/s
|
||||
if (!enabled || millis() - lastLoop < 250 || strip.isUpdating()) return;
|
||||
// only check sensors 5x/s
|
||||
if (!enabled || millis() - lastLoop < 200) return;
|
||||
lastLoop = millis();
|
||||
|
||||
if (!updatePIRsensorState()) {
|
||||
@ -396,37 +410,35 @@ void PIRsensorSwitch::addToJsonInfo(JsonObject &root)
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
|
||||
bool state = LOW;
|
||||
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++)
|
||||
if (PIRsensorPin[i] >= 0) state |= sensorPinState[i];
|
||||
|
||||
JsonArray infoArr = user.createNestedArray(FPSTR(_name));
|
||||
|
||||
String uiDomString;
|
||||
if (enabled) {
|
||||
if (offTimerStart > 0)
|
||||
{
|
||||
if (offTimerStart > 0) {
|
||||
uiDomString = "";
|
||||
unsigned int offSeconds = (m_switchOffDelay - (millis() - offTimerStart)) / 1000;
|
||||
if (offSeconds >= 3600)
|
||||
{
|
||||
if (offSeconds >= 3600) {
|
||||
uiDomString += (offSeconds / 3600);
|
||||
uiDomString += F("h ");
|
||||
offSeconds %= 3600;
|
||||
}
|
||||
if (offSeconds >= 60)
|
||||
{
|
||||
if (offSeconds >= 60) {
|
||||
uiDomString += (offSeconds / 60);
|
||||
offSeconds %= 60;
|
||||
}
|
||||
else if (uiDomString.length() > 0)
|
||||
{
|
||||
} else if (uiDomString.length() > 0) {
|
||||
uiDomString += 0;
|
||||
}
|
||||
if (uiDomString.length() > 0)
|
||||
{
|
||||
if (uiDomString.length() > 0) {
|
||||
uiDomString += F("min ");
|
||||
}
|
||||
uiDomString += (offSeconds);
|
||||
infoArr.add(uiDomString + F("s"));
|
||||
} else {
|
||||
infoArr.add(sensorPinState ? F("sensor on") : F("inactive"));
|
||||
infoArr.add(state ? F("sensor on") : F("inactive"));
|
||||
}
|
||||
} else {
|
||||
infoArr.add(F("disabled"));
|
||||
@ -446,9 +458,11 @@ void PIRsensorSwitch::addToJsonInfo(JsonObject &root)
|
||||
uiDomString += F("</button>");
|
||||
infoArr.add(uiDomString);
|
||||
|
||||
if (enabled) {
|
||||
JsonObject sensor = root[F("sensor")];
|
||||
if (sensor.isNull()) sensor = root.createNestedObject(F("sensor"));
|
||||
sensor[F("motion")] = sensorPinState || offTimerStart>0 ? true : false;
|
||||
sensor[F("motion")] = state || offTimerStart>0 ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
void PIRsensorSwitch::onStateChange(uint8_t mode) {
|
||||
@ -478,7 +492,8 @@ void PIRsensorSwitch::addToConfig(JsonObject &root)
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name));
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[FPSTR(_switchOffDelay)] = m_switchOffDelay / 1000;
|
||||
top["pin"] = PIRsensorPin;
|
||||
JsonArray pinArray = top.createNestedArray("pin");
|
||||
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++) pinArray.add(PIRsensorPin[i]);
|
||||
top[FPSTR(_onPreset)] = m_onPreset;
|
||||
top[FPSTR(_offPreset)] = m_offPreset;
|
||||
top[FPSTR(_nightTime)] = m_nightTimeOnly;
|
||||
@ -494,12 +509,20 @@ void PIRsensorSwitch::appendConfigData()
|
||||
{
|
||||
oappend(SET_F("addInfo('PIRsensorSwitch:HA-discovery',1,'HA=Home Assistant');")); // 0 is field type, 1 is actual field
|
||||
oappend(SET_F("addInfo('PIRsensorSwitch:override',1,'Cancel timer on change');")); // 0 is field type, 1 is actual field
|
||||
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++) {
|
||||
char str[128];
|
||||
sprintf_P(str, PSTR("addInfo('PIRsensorSwitch:pin[]',%d,'','#%d');"), i, i);
|
||||
oappend(str);
|
||||
}
|
||||
}
|
||||
|
||||
bool PIRsensorSwitch::readFromConfig(JsonObject &root)
|
||||
{
|
||||
bool oldEnabled = enabled;
|
||||
int8_t oldPin = PIRsensorPin;
|
||||
int8_t oldPin[PIR_SENSOR_MAX_SENSORS];
|
||||
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++) {
|
||||
oldPin[i] = PIRsensorPin[i];
|
||||
PIRsensorPin[i] = -1;
|
||||
}
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
@ -508,7 +531,13 @@ bool PIRsensorSwitch::readFromConfig(JsonObject &root)
|
||||
return false;
|
||||
}
|
||||
|
||||
PIRsensorPin = top["pin"] | PIRsensorPin;
|
||||
JsonArray pins = top["pin"];
|
||||
if (!pins.isNull()) {
|
||||
for (size_t i = 0; i < PIR_SENSOR_MAX_SENSORS; i++)
|
||||
if (i < pins.size()) PIRsensorPin[i] = pins[i] | PIRsensorPin[i];
|
||||
} else {
|
||||
PIRsensorPin[0] = top["pin"] | oldPin[0];
|
||||
}
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
|
||||
@ -530,26 +559,11 @@ bool PIRsensorSwitch::readFromConfig(JsonObject &root)
|
||||
// reading config prior to setup()
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
if (oldPin != PIRsensorPin || oldEnabled != enabled) {
|
||||
// check if pin is OK
|
||||
if (oldPin != PIRsensorPin && oldPin >= 0) {
|
||||
// if we are changing pin in settings page
|
||||
// deallocate old pin
|
||||
pinManager.deallocatePin(oldPin, PinOwner::UM_PIR);
|
||||
if (pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
|
||||
pinMode(PIRsensorPin, INPUT_PULLUP);
|
||||
} else {
|
||||
// allocation failed
|
||||
PIRsensorPin = -1;
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
if (enabled) {
|
||||
sensorPinState = digitalRead(PIRsensorPin);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++)
|
||||
if (oldPin[i] >= 0) pinManager.deallocatePin(oldPin[i], PinOwner::UM_PIR);
|
||||
setup();
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[FPSTR(_domoticzIDX)].isNull();
|
||||
return !(pins.isNull() || pins.size() != PIR_SENSOR_MAX_SENSORS);
|
||||
}
|
||||
|
@ -75,7 +75,11 @@ static uint8_t audioSyncEnabled = 0; // bit field: bit 0 - send, bit 1
|
||||
static bool udpSyncConnected = false; // UDP connection status -> true if connected to multicast group
|
||||
|
||||
// user settable parameters for limitSoundDynamics()
|
||||
static bool limiterOn = true; // bool: enable / disable dynamics limiter
|
||||
#ifdef UM_AUDIOREACTIVE_DYNAMICS_LIMITER_OFF
|
||||
static bool limiterOn = false; // bool: enable / disable dynamics limiter
|
||||
#else
|
||||
static bool limiterOn = true;
|
||||
#endif
|
||||
static uint16_t attackTime = 80; // int: attack time in milliseconds. Default 0.08sec
|
||||
static uint16_t decayTime = 1400; // int: decay time in milliseconds. Default 1.40sec
|
||||
// user settable options for FFTResult scaling
|
||||
@ -614,7 +618,12 @@ class AudioReactive : public Usermod {
|
||||
};
|
||||
|
||||
// set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer)
|
||||
#ifdef UM_AUDIOREACTIVE_ENABLE
|
||||
bool enabled = true;
|
||||
#else
|
||||
bool enabled = false;
|
||||
#endif
|
||||
|
||||
bool initDone = false;
|
||||
bool addPalettes = false;
|
||||
int8_t palettes = 0;
|
||||
|
@ -55,6 +55,11 @@ If you want to define default GPIOs during compile time, use the following (defa
|
||||
- `-D ES7243_SDAPIN` : GPIO for I2C SDA pin on ES7243 microphone (-1)
|
||||
- `-D ES7243_SCLPIN` : GPIO for I2C SCL pin on ES7243 microphone (-1)
|
||||
|
||||
Other options:
|
||||
|
||||
- `-D UM_AUDIOREACTIVE_ENABLE` : makes usermod default enabled (not the same as include into build option!)
|
||||
- `-D UM_AUDIOREACTIVE_DYNAMICS_LIMITER_OFF` : disables rise/fall limiter default
|
||||
|
||||
**NOTE** I2S is used for analog audio sampling. Hence, the analog *buttons* (i.e. potentiometers) are disabled when running this usermod with an analog microphone.
|
||||
|
||||
### Advanced Compile-Time Options
|
||||
|
@ -1,63 +0,0 @@
|
||||
# I2C 4 Line Display Usermod
|
||||
|
||||
First, thanks to the authors of the ssd11306_i2c_oled_u8g2 mod.
|
||||
|
||||
Provides a four line display using either
|
||||
128x32 or 128x64 OLED displays.
|
||||
It can operate independently, but starts to provide
|
||||
a relatively complete on-device UI when paired with the
|
||||
Rotary Encoder UI usermod. I strongly encourage you to use
|
||||
them together.
|
||||
|
||||
[See the pair of usermods in action](https://www.youtube.com/watch?v=tITQY80rIOA)
|
||||
|
||||
## Installation
|
||||
|
||||
Copy and update the example `platformio_override.ini.sample`
|
||||
from the Rotary Encoder UI usermode folder to the root directory of your particular build.
|
||||
This file should be placed in the same directory as `platformio.ini`.
|
||||
|
||||
### Define Your Options
|
||||
|
||||
* `USERMOD_FOUR_LINE_DISPLAY` - define this to have this mod included wled00\usermods_list.cpp - also tells Rotary Encoder usermod, if installed, the display is available
|
||||
* `FLD_PIN_SCL` - The display SCL pin, defaults to 5
|
||||
* `FLD_PIN_SDA` - The display SDA pin, defaults to 4
|
||||
|
||||
All of the parameters can be configured via the Usermods settings page, inluding GPIO pins.
|
||||
|
||||
### PlatformIO requirements
|
||||
|
||||
This usermod requires the `U8g2` and `Wire` libraries. See the
|
||||
`platformio_override.ini.sample` found in the Rotary Encoder
|
||||
UI usermod folder for how to include these using `platformio_override.ini`.
|
||||
|
||||
## Configuration
|
||||
|
||||
* `enabled` - enable/disable usermod
|
||||
* `pin` - GPIO pins used for display; I2C displays use Clk & Data; SPI displays can use SCK, MOSI, CS, DC & RST
|
||||
* `type` - display type in numeric format
|
||||
* 1 = I2C SSD1306 128x32
|
||||
* 2 = I2C SH1106 128x32
|
||||
* 3 = I2C SSD1306 128x64 (4 double-height lines)
|
||||
* 4 = I2C SSD1305 128x32
|
||||
* 5 = I2C SSD1305 128x64 (4 double-height lines)
|
||||
* 6 = SPI SSD1306 128x32
|
||||
* 7 = SPI SSD1306 128x64 (4 double-height lines)
|
||||
* `contrast` - set display contrast (higher contrast may reduce display lifetime)
|
||||
* `refreshRateSec` - display refresh time in seconds
|
||||
* `screenTimeOutSec` - screen saver time-out in seconds
|
||||
* `flip` - flip/rotate display 180°
|
||||
* `sleepMode` - enable/disable screen saver
|
||||
* `clockMode` - enable/disable clock display in screen saver mode
|
||||
* `i2c-freq-kHz` - I2C clock frequency in kHz (may help reduce dropped frames, range: 400-3400)
|
||||
|
||||
## Change Log
|
||||
|
||||
2021-02
|
||||
* First public release
|
||||
|
||||
2021-04
|
||||
* Adaptation for runtime configuration.
|
||||
|
||||
2021-11
|
||||
* Added configuration option description.
|
@ -1,742 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
|
||||
|
||||
//
|
||||
// Insired by the v1 usermod: ssd1306_i2c_oled_u8g2
|
||||
//
|
||||
// v2 usermod for using 128x32 or 128x64 i2c
|
||||
// OLED displays to provide a four line display
|
||||
// for WLED.
|
||||
//
|
||||
// Dependencies
|
||||
// * This usermod REQURES the ModeSortUsermod
|
||||
// * This Usermod works best, by far, when coupled
|
||||
// with RotaryEncoderUIUsermod.
|
||||
//
|
||||
// Make sure to enable NTP and set your time zone in WLED Config | Time.
|
||||
//
|
||||
// REQUIREMENT: You must add the following requirements to
|
||||
// REQUIREMENT: "lib_deps" within platformio.ini / platformio_override.ini
|
||||
// REQUIREMENT: * U8g2 (the version already in platformio.ini is fine)
|
||||
// REQUIREMENT: * Wire
|
||||
//
|
||||
|
||||
//The SCL and SDA pins are defined here.
|
||||
#ifndef FLD_PIN_SCL
|
||||
#define FLD_PIN_SCL i2c_scl
|
||||
#endif
|
||||
#ifndef FLD_PIN_SDA
|
||||
#define FLD_PIN_SDA i2c_sda
|
||||
#endif
|
||||
#ifndef FLD_PIN_CLOCKSPI
|
||||
#define FLD_PIN_CLOCKSPI spi_sclk
|
||||
#endif
|
||||
#ifndef FLD_PIN_DATASPI
|
||||
#define FLD_PIN_DATASPI spi_mosi
|
||||
#endif
|
||||
#ifndef FLD_PIN_CS
|
||||
#define FLD_PIN_CS spi_cs
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifndef FLD_PIN_DC
|
||||
#define FLD_PIN_DC 19
|
||||
#endif
|
||||
#ifndef FLD_PIN_RESET
|
||||
#define FLD_PIN_RESET 26
|
||||
#endif
|
||||
#else
|
||||
#ifndef FLD_PIN_DC
|
||||
#define FLD_PIN_DC 12
|
||||
#endif
|
||||
#ifndef FLD_PIN_RESET
|
||||
#define FLD_PIN_RESET 16
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef FLD_TYPE
|
||||
#ifndef FLD_SPI_DEFAULT
|
||||
#define FLD_TYPE SSD1306
|
||||
#else
|
||||
#define FLD_TYPE SSD1306_SPI
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// When to time out to the clock or blank the screen
|
||||
// if SLEEP_MODE_ENABLED.
|
||||
#define SCREEN_TIMEOUT_MS 60*1000 // 1 min
|
||||
|
||||
#define TIME_INDENT 0
|
||||
#define DATE_INDENT 2
|
||||
|
||||
// Minimum time between redrawing screen in ms
|
||||
#define USER_LOOP_REFRESH_RATE_MS 1000
|
||||
|
||||
// Extra char (+1) for null
|
||||
#define LINE_BUFFER_SIZE 16+1
|
||||
|
||||
typedef enum {
|
||||
FLD_LINE_BRIGHTNESS = 0,
|
||||
FLD_LINE_EFFECT_SPEED,
|
||||
FLD_LINE_EFFECT_INTENSITY,
|
||||
FLD_LINE_MODE,
|
||||
FLD_LINE_PALETTE,
|
||||
FLD_LINE_TIME
|
||||
} Line4Type;
|
||||
|
||||
typedef enum {
|
||||
NONE = 0,
|
||||
SSD1306, // U8X8_SSD1306_128X32_UNIVISION_HW_I2C
|
||||
SH1106, // U8X8_SH1106_128X64_WINSTAR_HW_I2C
|
||||
SSD1306_64, // U8X8_SSD1306_128X64_NONAME_HW_I2C
|
||||
SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C
|
||||
SSD1305_64, // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C
|
||||
SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI
|
||||
SSD1306_SPI64 // U8X8_SSD1306_128X64_NONAME_HW_SPI
|
||||
} DisplayType;
|
||||
|
||||
class FourLineDisplayUsermod : public Usermod {
|
||||
|
||||
private:
|
||||
|
||||
bool initDone = false;
|
||||
unsigned long lastTime = 0;
|
||||
|
||||
// HW interface & configuration
|
||||
U8X8 *u8x8 = nullptr; // pointer to U8X8 display object
|
||||
#ifndef FLD_SPI_DEFAULT
|
||||
int8_t ioPin[5] = {FLD_PIN_SCL, FLD_PIN_SDA, -1, -1, -1}; // I2C pins: SCL, SDA
|
||||
uint32_t ioFrequency = 400000; // in Hz (minimum is 100000, baseline is 400000 and maximum should be 3400000)
|
||||
#else
|
||||
int8_t ioPin[5] = {FLD_PIN_CLOCKSPI, FLD_PIN_DATASPI, FLD_PIN_CS, FLD_PIN_DC, FLD_PIN_RESET}; // SPI pins: CLK, MOSI, CS, DC, RST
|
||||
uint32_t ioFrequency = 1000000; // in Hz (minimum is 500kHz, baseline is 1MHz and maximum should be 20MHz)
|
||||
#endif
|
||||
DisplayType type = FLD_TYPE; // display type
|
||||
bool flip = false; // flip display 180°
|
||||
uint8_t contrast = 10; // screen contrast
|
||||
uint8_t lineHeight = 1; // 1 row or 2 rows
|
||||
uint32_t refreshRate = USER_LOOP_REFRESH_RATE_MS; // in ms
|
||||
uint32_t screenTimeout = SCREEN_TIMEOUT_MS; // in ms
|
||||
bool sleepMode = true; // allow screen sleep?
|
||||
bool clockMode = false; // display clock
|
||||
bool enabled = true;
|
||||
|
||||
// Next variables hold the previous known values to determine if redraw is
|
||||
// required.
|
||||
String knownSsid = "";
|
||||
IPAddress knownIp;
|
||||
uint8_t knownBrightness = 0;
|
||||
uint8_t knownEffectSpeed = 0;
|
||||
uint8_t knownEffectIntensity = 0;
|
||||
uint8_t knownMode = 0;
|
||||
uint8_t knownPalette = 0;
|
||||
uint8_t knownMinute = 99;
|
||||
uint8_t knownHour = 99;
|
||||
|
||||
bool displayTurnedOff = false;
|
||||
unsigned long lastUpdate = 0;
|
||||
unsigned long lastRedraw = 0;
|
||||
unsigned long overlayUntil = 0;
|
||||
Line4Type lineType = FLD_LINE_BRIGHTNESS;
|
||||
// Set to 2 or 3 to mark lines 2 or 3. Other values ignored.
|
||||
byte markLineNum = 0;
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
static const char _contrast[];
|
||||
static const char _refreshRate[];
|
||||
static const char _screenTimeOut[];
|
||||
static const char _flip[];
|
||||
static const char _sleepMode[];
|
||||
static const char _clockMode[];
|
||||
static const char _busClkFrequency[];
|
||||
|
||||
// If display does not work or looks corrupted check the
|
||||
// constructor reference:
|
||||
// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp
|
||||
// or check the gallery:
|
||||
// https://github.com/olikraus/u8g2/wiki/gallery
|
||||
|
||||
public:
|
||||
|
||||
// gets called once at boot. Do all initialization that doesn't depend on
|
||||
// network here
|
||||
void setup() {
|
||||
if (type == NONE || !enabled) return;
|
||||
|
||||
bool isHW;
|
||||
PinOwner po = PinOwner::UM_FourLineDisplay;
|
||||
if (type == SSD1306_SPI || type == SSD1306_SPI64) {
|
||||
isHW = (ioPin[0]==spi_sclk && ioPin[1]==spi_mosi);
|
||||
if (isHW) po = PinOwner::HW_SPI; // allow multiple allocations of HW I2C bus pins
|
||||
PinManagerPinType pins[5] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true }, { ioPin[3], true }, { ioPin[4], true }};
|
||||
if (!pinManager.allocateMultiplePins(pins, 5, po)) { type=NONE; return; }
|
||||
} else {
|
||||
isHW = (ioPin[0]==i2c_scl && ioPin[1]==i2c_sda);
|
||||
if (isHW) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
|
||||
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } };
|
||||
if (!pinManager.allocateMultiplePins(pins, 2, po)) { type=NONE; return; }
|
||||
}
|
||||
|
||||
DEBUG_PRINTLN(F("Allocating display."));
|
||||
switch (type) {
|
||||
case SSD1306:
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 1;
|
||||
break;
|
||||
case SH1106:
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 2;
|
||||
break;
|
||||
case SSD1306_64:
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 2;
|
||||
break;
|
||||
case SSD1305:
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 1;
|
||||
break;
|
||||
case SSD1305_64:
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
|
||||
else u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 2;
|
||||
break;
|
||||
case SSD1306_SPI:
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
|
||||
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
|
||||
lineHeight = 1;
|
||||
break;
|
||||
case SSD1306_SPI64:
|
||||
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
|
||||
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
|
||||
lineHeight = 2;
|
||||
break;
|
||||
default:
|
||||
u8x8 = nullptr;
|
||||
}
|
||||
|
||||
if (nullptr == u8x8) {
|
||||
DEBUG_PRINTLN(F("Display init failed."));
|
||||
pinManager.deallocateMultiplePins((const uint8_t*)ioPin, (type == SSD1306_SPI || type == SSD1306_SPI64) ? 5 : 2, po);
|
||||
type = NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
initDone = true;
|
||||
DEBUG_PRINTLN(F("Starting display."));
|
||||
/*if (!(type == SSD1306_SPI || type == SSD1306_SPI64))*/ u8x8->setBusClock(ioFrequency); // can be used for SPI too
|
||||
u8x8->begin();
|
||||
setFlipMode(flip);
|
||||
setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
|
||||
setPowerSave(0);
|
||||
drawString(0, 0, "Loading...");
|
||||
}
|
||||
|
||||
// gets called every time WiFi is (re-)connected. Initialize own network
|
||||
// interfaces here
|
||||
void connected() {}
|
||||
|
||||
/**
|
||||
* Da loop.
|
||||
*/
|
||||
void loop() {
|
||||
if (!enabled || millis() - lastUpdate < (clockMode?1000:refreshRate) || strip.isUpdating()) return;
|
||||
lastUpdate = millis();
|
||||
|
||||
redraw(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrappers for screen drawing
|
||||
*/
|
||||
void setFlipMode(uint8_t mode) {
|
||||
if (type == NONE || !enabled) return;
|
||||
u8x8->setFlipMode(mode);
|
||||
}
|
||||
void setContrast(uint8_t contrast) {
|
||||
if (type == NONE || !enabled) return;
|
||||
u8x8->setContrast(contrast);
|
||||
}
|
||||
void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false) {
|
||||
if (type == NONE || !enabled) return;
|
||||
u8x8->setFont(u8x8_font_chroma48medium8_r);
|
||||
if (!ignoreLH && lineHeight==2) u8x8->draw1x2String(col, row, string);
|
||||
else u8x8->drawString(col, row, string);
|
||||
}
|
||||
void draw2x2String(uint8_t col, uint8_t row, const char *string) {
|
||||
if (type == NONE || !enabled) return;
|
||||
u8x8->setFont(u8x8_font_chroma48medium8_r);
|
||||
u8x8->draw2x2String(col, row, string);
|
||||
}
|
||||
void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH=false) {
|
||||
if (type == NONE || !enabled) return;
|
||||
u8x8->setFont(font);
|
||||
if (!ignoreLH && lineHeight==2) u8x8->draw1x2Glyph(col, row, glyph);
|
||||
else u8x8->drawGlyph(col, row, glyph);
|
||||
}
|
||||
uint8_t getCols() {
|
||||
if (type==NONE || !enabled) return 0;
|
||||
return u8x8->getCols();
|
||||
}
|
||||
void clear() {
|
||||
if (type == NONE || !enabled) return;
|
||||
u8x8->clear();
|
||||
}
|
||||
void setPowerSave(uint8_t save) {
|
||||
if (type == NONE || !enabled) return;
|
||||
u8x8->setPowerSave(save);
|
||||
}
|
||||
|
||||
void center(String &line, uint8_t width) {
|
||||
int len = line.length();
|
||||
if (len<width) for (byte i=(width-len)/2; i>0; i--) line = ' ' + line;
|
||||
for (byte i=line.length(); i<width; i++) line += ' ';
|
||||
}
|
||||
|
||||
/**
|
||||
* Redraw the screen (but only if things have changed
|
||||
* or if forceRedraw).
|
||||
*/
|
||||
void redraw(bool forceRedraw) {
|
||||
static bool showName = false;
|
||||
unsigned long now = millis();
|
||||
|
||||
if (type == NONE || !enabled) return;
|
||||
if (overlayUntil > 0) {
|
||||
if (now >= overlayUntil) {
|
||||
// Time to display the overlay has elapsed.
|
||||
overlayUntil = 0;
|
||||
forceRedraw = true;
|
||||
} else {
|
||||
// We are still displaying the overlay
|
||||
// Don't redraw.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if values which are shown on display changed from the last time.
|
||||
if (forceRedraw ||
|
||||
(((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) ||
|
||||
(knownIp != (apActive ? IPAddress(4, 3, 2, 1) : Network.localIP())) ||
|
||||
(knownBrightness != bri) ||
|
||||
(knownEffectSpeed != effectSpeed) ||
|
||||
(knownEffectIntensity != effectIntensity) ||
|
||||
(knownMode != strip.getMainSegment().mode) ||
|
||||
(knownPalette != strip.getMainSegment().palette)) {
|
||||
knownHour = 99; // force time update
|
||||
lastRedraw = now; // update lastRedraw marker
|
||||
} else if (sleepMode && !displayTurnedOff && ((now - lastRedraw)/1000)%5 == 0) {
|
||||
// change line every 5s
|
||||
showName = !showName;
|
||||
switch (lineType) {
|
||||
case FLD_LINE_BRIGHTNESS:
|
||||
lineType = FLD_LINE_EFFECT_SPEED;
|
||||
break;
|
||||
case FLD_LINE_MODE:
|
||||
lineType = FLD_LINE_BRIGHTNESS;
|
||||
break;
|
||||
case FLD_LINE_PALETTE:
|
||||
lineType = clockMode ? FLD_LINE_MODE : FLD_LINE_BRIGHTNESS;
|
||||
break;
|
||||
case FLD_LINE_EFFECT_SPEED:
|
||||
lineType = FLD_LINE_EFFECT_INTENSITY;
|
||||
break;
|
||||
case FLD_LINE_EFFECT_INTENSITY:
|
||||
lineType = FLD_LINE_PALETTE;
|
||||
break;
|
||||
default:
|
||||
lineType = FLD_LINE_MODE;
|
||||
break;
|
||||
}
|
||||
knownHour = 99; // force time update
|
||||
// do not update lastRedraw marker if just switching row contenet
|
||||
} else {
|
||||
// Nothing to change.
|
||||
// Turn off display after 3 minutes with no change.
|
||||
if(sleepMode && !displayTurnedOff && (millis() - lastRedraw > screenTimeout)) {
|
||||
// We will still check if there is a change in redraw()
|
||||
// and turn it back on if it changed.
|
||||
sleepOrClock(true);
|
||||
} else if (displayTurnedOff && clockMode) {
|
||||
showTime();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Turn the display back on
|
||||
if (displayTurnedOff) sleepOrClock(false);
|
||||
|
||||
// Update last known values.
|
||||
knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
|
||||
knownIp = apActive ? IPAddress(4, 3, 2, 1) : Network.localIP();
|
||||
knownBrightness = bri;
|
||||
knownMode = strip.getMainSegment().mode;
|
||||
knownPalette = strip.getMainSegment().palette;
|
||||
knownEffectSpeed = effectSpeed;
|
||||
knownEffectIntensity = effectIntensity;
|
||||
|
||||
// Do the actual drawing
|
||||
String line;
|
||||
// First row with Wifi name
|
||||
drawGlyph(0, 0, 80, u8x8_font_open_iconic_embedded_1x1); // home icon
|
||||
line = knownSsid.substring(0, getCols() > 1 ? getCols() - 2 : 0);
|
||||
center(line, getCols()-2);
|
||||
drawString(1, 0, line.c_str());
|
||||
// Print `~` char to indicate that SSID is longer, than our display
|
||||
if (knownSsid.length() > (int)getCols()-1) {
|
||||
drawString(getCols() - 1, 0, "~");
|
||||
}
|
||||
|
||||
// Second row with IP or Psssword
|
||||
drawGlyph(0, lineHeight, 68, u8x8_font_open_iconic_embedded_1x1); // wifi icon
|
||||
// Print password in AP mode and if led is OFF.
|
||||
if (apActive && bri == 0) {
|
||||
drawString(1, lineHeight, apPass);
|
||||
} else {
|
||||
// alternate IP address and server name
|
||||
line = knownIp.toString();
|
||||
if (showName && strcmp(serverDescription, "WLED") != 0) {
|
||||
line = serverDescription;
|
||||
}
|
||||
center(line, getCols()-1);
|
||||
drawString(1, lineHeight, line.c_str());
|
||||
}
|
||||
|
||||
// draw third and fourth row
|
||||
drawLine(2, clockMode ? lineType : FLD_LINE_MODE);
|
||||
drawLine(3, clockMode ? FLD_LINE_TIME : lineType);
|
||||
|
||||
drawGlyph(0, 2*lineHeight, 66 + (bri > 0 ? 3 : 0), u8x8_font_open_iconic_weather_2x2); // sun/moon icon
|
||||
//if (markLineNum>1) drawGlyph(2, markLineNum*lineHeight, 66, u8x8_font_open_iconic_arrow_1x1); // arrow icon
|
||||
}
|
||||
|
||||
void drawLine(uint8_t line, Line4Type lineType) {
|
||||
char lineBuffer[LINE_BUFFER_SIZE];
|
||||
uint8_t printedChars;
|
||||
switch(lineType) {
|
||||
case FLD_LINE_BRIGHTNESS:
|
||||
sprintf_P(lineBuffer, PSTR("Brightness %3d"), bri);
|
||||
drawString(2, line*lineHeight, lineBuffer);
|
||||
break;
|
||||
case FLD_LINE_EFFECT_SPEED:
|
||||
sprintf_P(lineBuffer, PSTR("FX Speed %3d"), effectSpeed);
|
||||
drawString(2, line*lineHeight, lineBuffer);
|
||||
break;
|
||||
case FLD_LINE_EFFECT_INTENSITY:
|
||||
sprintf_P(lineBuffer, PSTR("FX Intens. %3d"), effectIntensity);
|
||||
drawString(2, line*lineHeight, lineBuffer);
|
||||
break;
|
||||
case FLD_LINE_MODE:
|
||||
printedChars = extractModeName(knownMode, JSON_mode_names, lineBuffer, LINE_BUFFER_SIZE-1);
|
||||
for (;printedChars < getCols()-2 && printedChars < LINE_BUFFER_SIZE-3; printedChars++) lineBuffer[printedChars]=' ';
|
||||
lineBuffer[printedChars] = 0;
|
||||
drawString(2, line*lineHeight, lineBuffer);
|
||||
break;
|
||||
case FLD_LINE_PALETTE:
|
||||
printedChars = extractModeName(knownPalette, JSON_palette_names, lineBuffer, LINE_BUFFER_SIZE-1);
|
||||
for (;printedChars < getCols()-2 && printedChars < LINE_BUFFER_SIZE-3; printedChars++) lineBuffer[printedChars]=' ';
|
||||
lineBuffer[printedChars] = 0;
|
||||
drawString(2, line*lineHeight, lineBuffer);
|
||||
break;
|
||||
case FLD_LINE_TIME:
|
||||
default:
|
||||
showTime(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If there screen is off or in clock is displayed,
|
||||
* this will return true. This allows us to throw away
|
||||
* the first input from the rotary encoder but
|
||||
* to wake up the screen.
|
||||
*/
|
||||
bool wakeDisplay() {
|
||||
if (type == NONE || !enabled) return false;
|
||||
knownHour = 99;
|
||||
if (displayTurnedOff) {
|
||||
// Turn the display back on
|
||||
sleepOrClock(false);
|
||||
redraw(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows you to show up to two lines as overlay for a
|
||||
* period of time.
|
||||
* Clears the screen and prints on the middle two lines.
|
||||
*/
|
||||
void overlay(const char* line1, const char *line2, long showHowLong) {
|
||||
if (type == NONE || !enabled) return;
|
||||
|
||||
if (displayTurnedOff) {
|
||||
// Turn the display back on (includes clear())
|
||||
sleepOrClock(false);
|
||||
} else {
|
||||
clear();
|
||||
}
|
||||
|
||||
// Print the overlay
|
||||
if (line1) {
|
||||
String buf = line1;
|
||||
center(buf, getCols());
|
||||
drawString(0, 1*lineHeight, buf.c_str());
|
||||
}
|
||||
if (line2) {
|
||||
String buf = line2;
|
||||
center(buf, getCols());
|
||||
drawString(0, 2*lineHeight, buf.c_str());
|
||||
}
|
||||
overlayUntil = millis() + showHowLong;
|
||||
}
|
||||
|
||||
void setLineType(byte lT) {
|
||||
lineType = (Line4Type) lT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Line 3 or 4 (last two lines) can be marked with an
|
||||
* arrow in the first column. Pass 2 or 3 to this to
|
||||
* specify which line to mark with an arrow.
|
||||
* Any other values are ignored.
|
||||
*/
|
||||
void setMarkLine(byte newMarkLineNum) {
|
||||
if (newMarkLineNum == 2 || newMarkLineNum == 3) {
|
||||
markLineNum = newMarkLineNum;
|
||||
}
|
||||
else {
|
||||
markLineNum = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable sleep (turn the display off) or clock mode.
|
||||
*/
|
||||
void sleepOrClock(bool enabled) {
|
||||
clear();
|
||||
if (enabled) {
|
||||
if (clockMode) showTime();
|
||||
else setPowerSave(1);
|
||||
displayTurnedOff = true;
|
||||
} else {
|
||||
setPowerSave(0);
|
||||
displayTurnedOff = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the current date and time in large characters
|
||||
* on the middle rows. Based 24 or 12 hour depending on
|
||||
* the useAMPM configuration.
|
||||
*/
|
||||
void showTime(bool fullScreen = true) {
|
||||
if (type == NONE || !enabled) return;
|
||||
char lineBuffer[LINE_BUFFER_SIZE];
|
||||
|
||||
updateLocalTime();
|
||||
byte minuteCurrent = minute(localTime);
|
||||
byte hourCurrent = hour(localTime);
|
||||
byte secondCurrent = second(localTime);
|
||||
if (knownMinute == minuteCurrent && knownHour == hourCurrent) {
|
||||
// Time hasn't changed.
|
||||
if (!fullScreen) return;
|
||||
}
|
||||
knownMinute = minuteCurrent;
|
||||
knownHour = hourCurrent;
|
||||
|
||||
byte currentMonth = month(localTime);
|
||||
sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(currentMonth), day(localTime));
|
||||
if (fullScreen)
|
||||
draw2x2String(DATE_INDENT, lineHeight==1 ? 0 : lineHeight, lineBuffer); // adjust for 8 line displays
|
||||
else
|
||||
drawString(2, lineHeight*3, lineBuffer);
|
||||
|
||||
byte showHour = hourCurrent;
|
||||
boolean isAM = false;
|
||||
if (useAMPM) {
|
||||
if (showHour == 0) {
|
||||
showHour = 12;
|
||||
isAM = true;
|
||||
}
|
||||
else if (showHour > 12) {
|
||||
showHour -= 12;
|
||||
isAM = false;
|
||||
}
|
||||
else {
|
||||
isAM = true;
|
||||
}
|
||||
}
|
||||
|
||||
sprintf_P(lineBuffer, (secondCurrent%2 || !fullScreen) ? PSTR("%2d:%02d") : PSTR("%2d %02d"), (useAMPM ? showHour : hourCurrent), minuteCurrent);
|
||||
// For time, we always use LINE_HEIGHT of 2 since
|
||||
// we are printing it big.
|
||||
if (fullScreen) {
|
||||
draw2x2String(TIME_INDENT+2, lineHeight*2, lineBuffer);
|
||||
sprintf_P(lineBuffer, PSTR("%02d"), secondCurrent);
|
||||
if (useAMPM) drawString(12+(fullScreen?0:2), lineHeight*2, (isAM ? "AM" : "PM"), true);
|
||||
else drawString(12, lineHeight*2+1, lineBuffer, true); // even with double sized rows print seconds in 1 line
|
||||
} else {
|
||||
drawString(9+(useAMPM?0:2), lineHeight*3, lineBuffer);
|
||||
if (useAMPM) drawString(12+(fullScreen?0:2), lineHeight*3, (isAM ? "AM" : "PM"), true);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
|
||||
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
|
||||
* Below it is shown how this could be used for e.g. a light sensor
|
||||
*/
|
||||
//void addToJsonInfo(JsonObject& root) {
|
||||
//JsonObject user = root["u"];
|
||||
//if (user.isNull()) user = root.createNestedObject("u");
|
||||
//JsonArray data = user.createNestedArray(F("4LineDisplay"));
|
||||
//data.add(F("Loaded."));
|
||||
//}
|
||||
|
||||
/*
|
||||
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
//void addToJsonState(JsonObject& root) {
|
||||
//}
|
||||
|
||||
/*
|
||||
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
//void readFromJsonState(JsonObject& root) {
|
||||
// if (!initDone) return; // prevent crash on boot applyPreset()
|
||||
//}
|
||||
|
||||
/*
|
||||
* 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(FPSTR(_name));
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
JsonArray io_pin = top.createNestedArray("pin");
|
||||
for (byte i=0; i<5; i++) io_pin.add(ioPin[i]);
|
||||
top["help4Pins"] = F("Clk,Data,CS,DC,RST"); // help for Settings page
|
||||
top["type"] = type;
|
||||
top["help4Type"] = F("1=SSD1306,2=SH1106,3=SSD1306_128x64,4=SSD1305,5=SSD1305_128x64,6=SSD1306_SPI,7=SSD1306_SPI_128x64"); // help for Settings page
|
||||
top[FPSTR(_flip)] = (bool) flip;
|
||||
top[FPSTR(_contrast)] = contrast;
|
||||
top[FPSTR(_refreshRate)] = refreshRate/1000;
|
||||
top[FPSTR(_screenTimeOut)] = screenTimeout/1000;
|
||||
top[FPSTR(_sleepMode)] = (bool) sleepMode;
|
||||
top[FPSTR(_clockMode)] = (bool) clockMode;
|
||||
top[FPSTR(_busClkFrequency)] = ioFrequency/1000;
|
||||
DEBUG_PRINTLN(F("4 Line Display config 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 :)
|
||||
*/
|
||||
bool readFromConfig(JsonObject& root) {
|
||||
bool needsRedraw = false;
|
||||
DisplayType newType = type;
|
||||
int8_t newPin[5]; for (byte i=0; i<5; i++) newPin[i] = ioPin[i];
|
||||
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
newType = top["type"] | newType;
|
||||
for (byte i=0; i<5; i++) newPin[i] = top["pin"][i] | ioPin[i];
|
||||
flip = top[FPSTR(_flip)] | flip;
|
||||
contrast = top[FPSTR(_contrast)] | contrast;
|
||||
refreshRate = (top[FPSTR(_refreshRate)] | refreshRate/1000) * 1000;
|
||||
screenTimeout = (top[FPSTR(_screenTimeOut)] | screenTimeout/1000) * 1000;
|
||||
sleepMode = top[FPSTR(_sleepMode)] | sleepMode;
|
||||
clockMode = top[FPSTR(_clockMode)] | clockMode;
|
||||
if (newType == SSD1306_SPI || newType == SSD1306_SPI64)
|
||||
ioFrequency = min(20000, max(500, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
|
||||
else
|
||||
ioFrequency = min(3400, max(100, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
for (byte i=0; i<5; i++) ioPin[i] = newPin[i];
|
||||
type = newType;
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
// changing parameters from settings page
|
||||
bool pinsChanged = false;
|
||||
for (byte i=0; i<5; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; }
|
||||
if (pinsChanged || type!=newType) {
|
||||
if (type != NONE) delete u8x8;
|
||||
PinOwner po = PinOwner::UM_FourLineDisplay;
|
||||
bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64);
|
||||
if (isSPI) {
|
||||
if (ioPin[0]==spi_sclk && ioPin[1]==spi_mosi) po = PinOwner::HW_SPI; // allow multiple allocations of HW SPI bus pins
|
||||
pinManager.deallocateMultiplePins((const uint8_t *)ioPin, 5, po);
|
||||
} else {
|
||||
if (ioPin[0]==i2c_scl && ioPin[1]==i2c_sda) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
|
||||
pinManager.deallocateMultiplePins((const uint8_t *)ioPin, 2, po);
|
||||
}
|
||||
for (byte i=0; i<5; i++) ioPin[i] = newPin[i];
|
||||
if (ioPin[0]<0 || ioPin[1]<0) { // data & clock must be > -1
|
||||
type = NONE;
|
||||
return true;
|
||||
} else type = newType;
|
||||
setup();
|
||||
needsRedraw |= true;
|
||||
}
|
||||
if (!(type == SSD1306_SPI || type == SSD1306_SPI64)) u8x8->setBusClock(ioFrequency); // can be used for SPI too
|
||||
setContrast(contrast);
|
||||
setFlipMode(flip);
|
||||
if (needsRedraw && !wakeDisplay()) redraw(true);
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[FPSTR(_enabled)].isNull();
|
||||
}
|
||||
|
||||
/*
|
||||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
||||
* This could be used in the future for the system to determine whether your usermod is installed.
|
||||
*/
|
||||
uint16_t getId() {
|
||||
return USERMOD_ID_FOUR_LINE_DISP;
|
||||
}
|
||||
};
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char FourLineDisplayUsermod::_name[] PROGMEM = "4LineDisplay";
|
||||
const char FourLineDisplayUsermod::_enabled[] PROGMEM = "enabled";
|
||||
const char FourLineDisplayUsermod::_contrast[] PROGMEM = "contrast";
|
||||
const char FourLineDisplayUsermod::_refreshRate[] PROGMEM = "refreshRateSec";
|
||||
const char FourLineDisplayUsermod::_screenTimeOut[] PROGMEM = "screenTimeOutSec";
|
||||
const char FourLineDisplayUsermod::_flip[] PROGMEM = "flip";
|
||||
const char FourLineDisplayUsermod::_sleepMode[] PROGMEM = "sleepMode";
|
||||
const char FourLineDisplayUsermod::_clockMode[] PROGMEM = "clockMode";
|
||||
const char FourLineDisplayUsermod::_busClkFrequency[] PROGMEM = "i2c-freq-kHz";
|
@ -1,33 +0,0 @@
|
||||
# Mode Sort
|
||||
|
||||
v2 usermod that provides data about modes and
|
||||
palettes to other usermods. Notably it provides:
|
||||
* A direct method for a mode or palette name
|
||||
* Ability to retrieve mode and palette names in
|
||||
alphabetical order
|
||||
|
||||
```char **getModesQStrings()```
|
||||
|
||||
Provides a char* array (pointers) to the names of the
|
||||
palettes contained in JSON_mode_names, in the same order as
|
||||
JSON_mode_names. These strings end in double quote (")
|
||||
(or \0 if there is a problem).
|
||||
|
||||
```byte *getModesAlphaIndexes()```
|
||||
|
||||
A byte array designating the indexes of names of the
|
||||
modes in alphabetical order. "Solid" will always remain
|
||||
at the top of the list.
|
||||
|
||||
```char **getPalettesQStrings()```
|
||||
|
||||
Provides a char* array (pointers) to the names of the
|
||||
palettes contained in JSON_palette_names, in the same order as
|
||||
JSON_palette_names. These strings end in double quote (")
|
||||
(or \0 if there is a problem).
|
||||
|
||||
```byte *getPalettesAlphaIndexes()```
|
||||
|
||||
A byte array designating the indexes of names of the
|
||||
palettes in alphabetical order. "Default" and those
|
||||
starting with "(" will always remain at the top of the list.
|
@ -1,244 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
//
|
||||
// v2 usermod that provides data about modes and
|
||||
// palettes to other usermods. Notably it provides:
|
||||
// * A direct method for a mode or palette name
|
||||
// * Ability to retrieve mode and palette names in
|
||||
// alphabetical order
|
||||
//
|
||||
// char **getModesQStrings()
|
||||
// Provides an array of char* (pointers) to the names of the
|
||||
// palettes within JSON_mode_names, in the same order as
|
||||
// JSON_mode_names. These strings end in double quote (")
|
||||
// (or \0 if there is a problem).
|
||||
//
|
||||
// byte *getModesAlphaIndexes()
|
||||
// An array of byte designating the indexes of names of the
|
||||
// modes in alphabetical order. "Solid" will always remain
|
||||
// at the front of the list.
|
||||
//
|
||||
// char **getPalettesQStrings()
|
||||
// Provides an array of char* (pointers) to the names of the
|
||||
// palettes within JSON_palette_names, in the same order as
|
||||
// JSON_palette_names. These strings end in double quote (")
|
||||
// (or \0 if there is a problem).
|
||||
//
|
||||
// byte *getPalettesAlphaIndexes()
|
||||
// An array of byte designating the indexes of names of the
|
||||
// palettes in alphabetical order. "Default" and those
|
||||
// starting with "(" will always remain at the front of the list.
|
||||
//
|
||||
|
||||
// Number of modes at the start of the list to not sort
|
||||
#define MODE_SORT_SKIP_COUNT 1
|
||||
|
||||
// Which list is being sorted
|
||||
char **listBeingSorted = nullptr;
|
||||
|
||||
/**
|
||||
* Modes and palettes are stored as strings that
|
||||
* end in a quote character. Compare two of them.
|
||||
* We are comparing directly within either
|
||||
* JSON_mode_names or JSON_palette_names.
|
||||
*/
|
||||
int re_qstringCmp(const void *ap, const void *bp) {
|
||||
char *a = listBeingSorted[*((byte *)ap)];
|
||||
char *b = listBeingSorted[*((byte *)bp)];
|
||||
int i = 0;
|
||||
do {
|
||||
char aVal = pgm_read_byte_near(a + i);
|
||||
if (aVal >= 97 && aVal <= 122) {
|
||||
// Lowercase
|
||||
aVal -= 32;
|
||||
}
|
||||
char bVal = pgm_read_byte_near(b + i);
|
||||
if (bVal >= 97 && bVal <= 122) {
|
||||
// Lowercase
|
||||
bVal -= 32;
|
||||
}
|
||||
// Relly we shouldn't ever get to '\0'
|
||||
if (aVal == '"' || bVal == '"' || aVal == '\0' || bVal == '\0') {
|
||||
// We're done. one is a substring of the other
|
||||
// or something happenend and the quote didn't stop us.
|
||||
if (aVal == bVal) {
|
||||
// Same value, probably shouldn't happen
|
||||
// with this dataset
|
||||
return 0;
|
||||
}
|
||||
else if (aVal == '"' || aVal == '\0') {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (aVal == bVal) {
|
||||
// Same characters. Move to the next.
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
// We're done
|
||||
if (aVal < bVal) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
} while (true);
|
||||
// We shouldn't get here.
|
||||
return 0;
|
||||
}
|
||||
|
||||
class ModeSortUsermod : public Usermod {
|
||||
private:
|
||||
|
||||
// Pointers the start of the mode names within JSON_mode_names
|
||||
char **modes_qstrings = nullptr;
|
||||
|
||||
// Array of mode indexes in alphabetical order.
|
||||
byte *modes_alpha_indexes = nullptr;
|
||||
|
||||
// Pointers the start of the palette names within JSON_palette_names
|
||||
char **palettes_qstrings = nullptr;
|
||||
|
||||
// Array of palette indexes in alphabetical order.
|
||||
byte *palettes_alpha_indexes = nullptr;
|
||||
|
||||
public:
|
||||
/**
|
||||
* setup() is called once at boot. WiFi is not yet connected at this point.
|
||||
* You can use it to initialize variables, sensors or similar.
|
||||
*/
|
||||
void setup() {
|
||||
// Sort the modes and palettes on startup
|
||||
// as they are guarantted to change.
|
||||
sortModesAndPalettes();
|
||||
}
|
||||
|
||||
char **getModesQStrings() {
|
||||
return modes_qstrings;
|
||||
}
|
||||
|
||||
byte *getModesAlphaIndexes() {
|
||||
return modes_alpha_indexes;
|
||||
}
|
||||
|
||||
char **getPalettesQStrings() {
|
||||
return palettes_qstrings;
|
||||
}
|
||||
|
||||
byte *getPalettesAlphaIndexes() {
|
||||
return palettes_alpha_indexes;
|
||||
}
|
||||
|
||||
/**
|
||||
* This Usermod doesn't have anything for loop.
|
||||
*/
|
||||
void loop() {}
|
||||
|
||||
/**
|
||||
* Sort the modes and palettes to the index arrays
|
||||
* modes_alpha_indexes and palettes_alpha_indexes.
|
||||
*/
|
||||
void sortModesAndPalettes() {
|
||||
modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount());
|
||||
modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
|
||||
re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
|
||||
|
||||
palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount());
|
||||
palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount());
|
||||
|
||||
int skipPaletteCount = 1;
|
||||
while (true) {
|
||||
// How many palette names start with '*' and should not be sorted?
|
||||
// (Also skipping the first one, 'Default').
|
||||
if (pgm_read_byte_near(palettes_qstrings[skipPaletteCount]) == '*') {
|
||||
skipPaletteCount++;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
re_sortModes(palettes_qstrings, palettes_alpha_indexes, strip.getPaletteCount(), skipPaletteCount);
|
||||
}
|
||||
|
||||
byte *re_initIndexArray(int numModes) {
|
||||
byte *indexes = (byte *)malloc(sizeof(byte) * numModes);
|
||||
for (byte i = 0; i < numModes; i++) {
|
||||
indexes[i] = i;
|
||||
}
|
||||
return indexes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of mode or palette names from the JSON string.
|
||||
* They don't end in '\0', they end in '"'.
|
||||
*/
|
||||
char **re_findModeStrings(const char json[], int numModes) {
|
||||
char **modeStrings = (char **)malloc(sizeof(char *) * numModes);
|
||||
uint8_t modeIndex = 0;
|
||||
bool insideQuotes = false;
|
||||
// advance past the mark for markLineNum that may exist.
|
||||
char singleJsonSymbol;
|
||||
|
||||
// Find the mode name in JSON
|
||||
bool complete = false;
|
||||
for (size_t i = 0; i < strlen_P(json); i++) {
|
||||
singleJsonSymbol = pgm_read_byte_near(json + i);
|
||||
if (singleJsonSymbol == '\0') break;
|
||||
switch (singleJsonSymbol) {
|
||||
case '"':
|
||||
insideQuotes = !insideQuotes;
|
||||
if (insideQuotes) {
|
||||
// We have a new mode or palette
|
||||
modeStrings[modeIndex] = (char *)(json + i + 1);
|
||||
}
|
||||
break;
|
||||
case '[':
|
||||
break;
|
||||
case ']':
|
||||
if (!insideQuotes) complete = true;
|
||||
break;
|
||||
case ',':
|
||||
if (!insideQuotes) modeIndex++;
|
||||
default:
|
||||
if (!insideQuotes) break;
|
||||
}
|
||||
if (complete) break;
|
||||
}
|
||||
return modeStrings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort either the modes or the palettes using quicksort.
|
||||
*/
|
||||
void re_sortModes(char **modeNames, byte *indexes, int count, int numSkip) {
|
||||
listBeingSorted = modeNames;
|
||||
qsort(indexes + numSkip, count - numSkip, sizeof(byte), re_qstringCmp);
|
||||
listBeingSorted = nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
void addToJsonState(JsonObject &root) {}
|
||||
|
||||
/*
|
||||
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
void readFromJsonState(JsonObject &root) {}
|
||||
|
||||
/*
|
||||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
||||
* This could be used in the future for the system to determine whether your usermod is installed.
|
||||
*/
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_MODE_SORT;
|
||||
}
|
||||
};
|
@ -1,48 +0,0 @@
|
||||
[platformio]
|
||||
default_envs = d1_mini
|
||||
; default_envs = esp32dev
|
||||
|
||||
[env:esp32dev]
|
||||
board = esp32dev
|
||||
platform = espressif32@3.2
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags =
|
||||
${common.build_flags_esp32}
|
||||
-D USERMOD_MODE_SORT
|
||||
-D USERMOD_FOUR_LINE_DISPLAY -D FLD_PIN_SCL=22 -D FLD_PIN_SDA=21
|
||||
-D USERMOD_ROTARY_ENCODER_UI -D ENCODER_DT_PIN=18 -D ENCODER_CLK_PIN=5 -D ENCODER_SW_PIN=19
|
||||
-D USERMOD_AUTO_SAVE -D AUTOSAVE_PRESET_NUM=1
|
||||
-D LEDPIN=16 -D BTNPIN=13
|
||||
upload_speed = 460800
|
||||
lib_ignore =
|
||||
ESPAsyncTCP
|
||||
ESPAsyncUDP
|
||||
|
||||
[env:d1_mini]
|
||||
board = d1_mini
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
upload_speed = 460800
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags =
|
||||
${common.build_flags_esp8266}
|
||||
-D USERMOD_MODE_SORT
|
||||
-D USERMOD_FOUR_LINE_DISPLAY -D FLD_PIN_SCL=5 -D FLD_PIN_SDA=4
|
||||
-D USERMOD_ROTARY_ENCODER_UI -D ENCODER_DT_PIN=12 -D ENCODER_CLK_PIN=14 -D ENCODER_SW_PIN=13
|
||||
-D USERMOD_AUTO_SAVE -D AUTOSAVE_PRESET_NUM=1
|
||||
-D LEDPIN=3 -D BTNPIN=0
|
||||
monitor_filters = esp8266_exception_decoder
|
||||
|
||||
[env]
|
||||
lib_deps =
|
||||
fastled/FastLED @ 3.3.2
|
||||
NeoPixelBus @ 2.6.0
|
||||
ESPAsyncTCP @ 1.2.0
|
||||
ESPAsyncUDP
|
||||
AsyncTCP @ 1.0.3
|
||||
IRremoteESP8266 @ 2.7.3
|
||||
https://github.com/lorol/LITTLEFS.git
|
||||
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.0
|
||||
U8g2@~2.27.2
|
||||
Wire
|
@ -1,39 +0,0 @@
|
||||
# Rotary Encoder UI Usermod
|
||||
|
||||
First, thanks to the authors of other Rotary Encoder usermods.
|
||||
|
||||
This usermod starts to provide a relatively complete on-device
|
||||
UI when paired with the Four Line Display usermod. I strongly
|
||||
encourage you to try them together.
|
||||
|
||||
[See the pair of usermods in action](https://www.youtube.com/watch?v=tITQY80rIOA)
|
||||
|
||||
## Installation
|
||||
|
||||
Copy and update the example `platformio_override.ini.sample` to the root directory of your particular build.
|
||||
This file should be placed in the same directory as `platformio.ini`.
|
||||
|
||||
### Define Your Options
|
||||
|
||||
* `USERMOD_ROTARY_ENCODER_UI` - define this to have this user mod included wled00\usermods_list.cpp
|
||||
* `USERMOD_ROTARY_ENCODER_GPIO` - define the GPIO function (INPUT, INPUT_PULLUP, etc...)
|
||||
* `USERMOD_FOUR_LINE_DISPLAY` - define this to have this the Four Line Display mod included wled00\usermods_list.cpp
|
||||
also tells this usermod that the display is available
|
||||
(see the Four Line Display usermod `readme.md` for more details)
|
||||
* `ENCODER_DT_PIN` - defaults to 12
|
||||
* `ENCODER_CLK_PIN` - defaults to 14
|
||||
* `ENCODER_SW_PIN` - defaults to 13
|
||||
* `USERMOD_ROTARY_ENCODER_GPIO` - GPIO functionality:
|
||||
`INPUT_PULLUP` to use internal pull-up
|
||||
`INPUT` to use pull-up on the PCB
|
||||
|
||||
### PlatformIO requirements
|
||||
|
||||
No special requirements.
|
||||
|
||||
Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`.
|
||||
|
||||
## Change Log
|
||||
|
||||
2021-02
|
||||
* First public release
|
@ -1,496 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
//
|
||||
// Inspired by the v1 usermods
|
||||
// * rotary_encoder_change_brightness
|
||||
// * rotary_encoder_change_effect
|
||||
//
|
||||
// v2 usermod that provides a rotary encoder-based UI.
|
||||
//
|
||||
// This usermod allows you to control:
|
||||
//
|
||||
// * Brightness
|
||||
// * Selected Effect
|
||||
// * Effect Speed
|
||||
// * Effect Intensity
|
||||
// * Palette
|
||||
//
|
||||
// Change between modes by pressing a button.
|
||||
//
|
||||
// Dependencies
|
||||
// * This usermod REQURES the ModeSortUsermod
|
||||
// * This Usermod works best coupled with
|
||||
// FourLineDisplayUsermod.
|
||||
//
|
||||
|
||||
#ifndef ENCODER_DT_PIN
|
||||
#define ENCODER_DT_PIN 12
|
||||
#endif
|
||||
|
||||
#ifndef ENCODER_CLK_PIN
|
||||
#define ENCODER_CLK_PIN 14
|
||||
#endif
|
||||
|
||||
#ifndef ENCODER_SW_PIN
|
||||
#define ENCODER_SW_PIN 13
|
||||
#endif
|
||||
|
||||
#ifndef USERMOD_FOUR_LINE_DISPLAY
|
||||
// These constants won't be defined if we aren't using FourLineDisplay.
|
||||
#define FLD_LINE_BRIGHTNESS 0
|
||||
#define FLD_LINE_MODE 0
|
||||
#define FLD_LINE_EFFECT_SPEED 0
|
||||
#define FLD_LINE_EFFECT_INTENSITY 0
|
||||
#define FLD_LINE_PALETTE 0
|
||||
#endif
|
||||
|
||||
|
||||
// The last UI state
|
||||
#define LAST_UI_STATE 4
|
||||
|
||||
|
||||
class RotaryEncoderUIUsermod : public Usermod {
|
||||
private:
|
||||
int fadeAmount = 10; // Amount to change every step (brightness)
|
||||
unsigned long currentTime;
|
||||
unsigned long loopTime;
|
||||
int8_t pinA = ENCODER_DT_PIN; // DT from encoder
|
||||
int8_t pinB = ENCODER_CLK_PIN; // CLK from encoder
|
||||
int8_t pinC = ENCODER_SW_PIN; // SW from encoder
|
||||
unsigned char select_state = 0; // 0: brightness, 1: effect, 2: effect speed
|
||||
unsigned char button_state = HIGH;
|
||||
unsigned char prev_button_state = HIGH;
|
||||
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
FourLineDisplayUsermod *display;
|
||||
#else
|
||||
void* display = nullptr;
|
||||
#endif
|
||||
|
||||
byte *modes_alpha_indexes = nullptr;
|
||||
byte *palettes_alpha_indexes = nullptr;
|
||||
|
||||
unsigned char Enc_A;
|
||||
unsigned char Enc_B;
|
||||
unsigned char Enc_A_prev = 0;
|
||||
|
||||
bool currentEffectAndPaletteInitialized = false;
|
||||
uint8_t effectCurrentIndex = 0;
|
||||
uint8_t effectPaletteIndex = 0;
|
||||
|
||||
bool initDone = false;
|
||||
bool enabled = true;
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
static const char _DT_pin[];
|
||||
static const char _CLK_pin[];
|
||||
static const char _SW_pin[];
|
||||
|
||||
public:
|
||||
/*
|
||||
* setup() is called once at boot. WiFi is not yet connected at this point.
|
||||
* You can use it to initialize variables, sensors or similar.
|
||||
*/
|
||||
void setup()
|
||||
{
|
||||
DEBUG_PRINTLN(F("Usermod Rotary Encoder init."));
|
||||
PinManagerPinType pins[3] = { { pinA, false }, { pinB, false }, { pinC, false } };
|
||||
if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_RotaryEncoderUI)) {
|
||||
// BUG: configuring this usermod with conflicting pins
|
||||
// will cause it to de-allocate pins it does not own
|
||||
// (at second config)
|
||||
// This is the exact type of bug solved by pinManager
|
||||
// tracking the owner tags....
|
||||
pinA = pinB = pinC = -1;
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef USERMOD_ROTARY_ENCODER_GPIO
|
||||
#define USERMOD_ROTARY_ENCODER_GPIO INPUT_PULLUP
|
||||
#endif
|
||||
pinMode(pinA, USERMOD_ROTARY_ENCODER_GPIO);
|
||||
pinMode(pinB, USERMOD_ROTARY_ENCODER_GPIO);
|
||||
pinMode(pinC, USERMOD_ROTARY_ENCODER_GPIO);
|
||||
|
||||
currentTime = millis();
|
||||
loopTime = currentTime;
|
||||
|
||||
ModeSortUsermod *modeSortUsermod = (ModeSortUsermod*) usermods.lookup(USERMOD_ID_MODE_SORT);
|
||||
modes_alpha_indexes = modeSortUsermod->getModesAlphaIndexes();
|
||||
palettes_alpha_indexes = modeSortUsermod->getPalettesAlphaIndexes();
|
||||
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
// This Usermod uses FourLineDisplayUsermod for the best experience.
|
||||
// But it's optional. But you want it.
|
||||
display = (FourLineDisplayUsermod*) usermods.lookup(USERMOD_ID_FOUR_LINE_DISP);
|
||||
if (display != nullptr) {
|
||||
display->setLineType(FLD_LINE_BRIGHTNESS);
|
||||
display->setMarkLine(3);
|
||||
}
|
||||
#endif
|
||||
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* connected() is called every time the WiFi is (re)connected
|
||||
* Use it to initialize network interfaces
|
||||
*/
|
||||
void connected()
|
||||
{
|
||||
//Serial.println("Connected to WiFi!");
|
||||
}
|
||||
|
||||
/*
|
||||
* loop() is called continuously. Here you can check for events, read sensors, etc.
|
||||
*
|
||||
* Tips:
|
||||
* 1. You can use "if (WLED_CONNECTED)" to check for a successful network connection.
|
||||
* Additionally, "if (WLED_MQTT_CONNECTED)" is available to check for a connection to an MQTT broker.
|
||||
*
|
||||
* 2. Try to avoid using the delay() function. NEVER use delays longer than 10 milliseconds.
|
||||
* Instead, use a timer check as shown here.
|
||||
*/
|
||||
void loop()
|
||||
{
|
||||
if (!enabled) return;
|
||||
|
||||
currentTime = millis(); // get the current elapsed time
|
||||
|
||||
// Initialize effectCurrentIndex and effectPaletteIndex to
|
||||
// current state. We do it here as (at least) effectCurrent
|
||||
// is not yet initialized when setup is called.
|
||||
if (!currentEffectAndPaletteInitialized) {
|
||||
findCurrentEffectAndPalette();
|
||||
}
|
||||
|
||||
if (currentTime >= (loopTime + 2)) // 2ms since last check of encoder = 500Hz
|
||||
{
|
||||
button_state = digitalRead(pinC);
|
||||
if (prev_button_state != button_state)
|
||||
{
|
||||
if (button_state == LOW)
|
||||
{
|
||||
prev_button_state = button_state;
|
||||
|
||||
char newState = select_state + 1;
|
||||
if (newState > LAST_UI_STATE) newState = 0;
|
||||
|
||||
bool changedState = true;
|
||||
if (display != nullptr) {
|
||||
switch(newState) {
|
||||
case 0:
|
||||
changedState = changeState("Brightness", FLD_LINE_BRIGHTNESS, 3);
|
||||
break;
|
||||
case 1:
|
||||
changedState = changeState("Select FX", FLD_LINE_MODE, 2);
|
||||
break;
|
||||
case 2:
|
||||
changedState = changeState("FX Speed", FLD_LINE_EFFECT_SPEED, 3);
|
||||
break;
|
||||
case 3:
|
||||
changedState = changeState("FX Intensity", FLD_LINE_EFFECT_INTENSITY, 3);
|
||||
break;
|
||||
case 4:
|
||||
changedState = changeState("Palette", FLD_LINE_PALETTE, 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (changedState) {
|
||||
select_state = newState;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_button_state = button_state;
|
||||
}
|
||||
}
|
||||
int Enc_A = digitalRead(pinA); // Read encoder pins
|
||||
int Enc_B = digitalRead(pinB);
|
||||
if ((!Enc_A) && (Enc_A_prev))
|
||||
{ // A has gone from high to low
|
||||
if (Enc_B == HIGH)
|
||||
{ // B is high so clockwise
|
||||
switch(select_state) {
|
||||
case 0:
|
||||
changeBrightness(true);
|
||||
break;
|
||||
case 1:
|
||||
changeEffect(true);
|
||||
break;
|
||||
case 2:
|
||||
changeEffectSpeed(true);
|
||||
break;
|
||||
case 3:
|
||||
changeEffectIntensity(true);
|
||||
break;
|
||||
case 4:
|
||||
changePalette(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (Enc_B == LOW)
|
||||
{ // B is low so counter-clockwise
|
||||
switch(select_state) {
|
||||
case 0:
|
||||
changeBrightness(false);
|
||||
break;
|
||||
case 1:
|
||||
changeEffect(false);
|
||||
break;
|
||||
case 2:
|
||||
changeEffectSpeed(false);
|
||||
break;
|
||||
case 3:
|
||||
changeEffectIntensity(false);
|
||||
break;
|
||||
case 4:
|
||||
changePalette(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Enc_A_prev = Enc_A; // Store value of A for next time
|
||||
loopTime = currentTime; // Updates loopTime
|
||||
}
|
||||
}
|
||||
|
||||
void findCurrentEffectAndPalette() {
|
||||
currentEffectAndPaletteInitialized = true;
|
||||
for (uint8_t i = 0; i < strip.getModeCount(); i++) {
|
||||
//byte value = modes_alpha_indexes[i];
|
||||
if (modes_alpha_indexes[i] == effectCurrent) {
|
||||
effectCurrentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < strip.getPaletteCount(); i++) {
|
||||
//byte value = palettes_alpha_indexes[i];
|
||||
if (palettes_alpha_indexes[i] == strip.getSegment(0).palette) {
|
||||
effectPaletteIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean changeState(const char *stateName, byte lineThreeMode, byte markedLine) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display != nullptr) {
|
||||
if (display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return false;
|
||||
}
|
||||
display->overlay("Mode change", stateName, 1500);
|
||||
display->setLineType(lineThreeMode);
|
||||
display->setMarkLine(markedLine);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void lampUdated() {
|
||||
colorUpdated(CALL_MODE_BUTTON);
|
||||
updateInterfaces(CALL_MODE_BUTTON);
|
||||
}
|
||||
|
||||
void changeBrightness(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (increase) {
|
||||
bri = (bri + fadeAmount <= 255) ? (bri + fadeAmount) : 255;
|
||||
}
|
||||
else {
|
||||
bri = (bri - fadeAmount >= 0) ? (bri - fadeAmount) : 0;
|
||||
}
|
||||
lampUdated();
|
||||
}
|
||||
|
||||
void changeEffect(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (increase) {
|
||||
effectCurrentIndex = (effectCurrentIndex + 1 >= strip.getModeCount()) ? 0 : (effectCurrentIndex + 1);
|
||||
}
|
||||
else {
|
||||
effectCurrentIndex = (effectCurrentIndex - 1 < 0) ? (strip.getModeCount() - 1) : (effectCurrentIndex - 1);
|
||||
}
|
||||
effectCurrent = modes_alpha_indexes[effectCurrentIndex];
|
||||
lampUdated();
|
||||
}
|
||||
|
||||
void changeEffectSpeed(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (increase) {
|
||||
effectSpeed = (effectSpeed + fadeAmount <= 255) ? (effectSpeed + fadeAmount) : 255;
|
||||
}
|
||||
else {
|
||||
effectSpeed = (effectSpeed - fadeAmount >= 0) ? (effectSpeed - fadeAmount) : 0;
|
||||
}
|
||||
lampUdated();
|
||||
}
|
||||
|
||||
void changeEffectIntensity(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (increase) {
|
||||
effectIntensity = (effectIntensity + fadeAmount <= 255) ? (effectIntensity + fadeAmount) : 255;
|
||||
}
|
||||
else {
|
||||
effectIntensity = (effectIntensity - fadeAmount >= 0) ? (effectIntensity - fadeAmount) : 0;
|
||||
}
|
||||
lampUdated();
|
||||
}
|
||||
|
||||
void changePalette(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (increase) {
|
||||
effectPaletteIndex = (effectPaletteIndex + 1 >= strip.getPaletteCount()) ? 0 : (effectPaletteIndex + 1);
|
||||
}
|
||||
else {
|
||||
effectPaletteIndex = (effectPaletteIndex - 1 < 0) ? (strip.getPaletteCount() - 1) : (effectPaletteIndex - 1);
|
||||
}
|
||||
effectPalette = palettes_alpha_indexes[effectPaletteIndex];
|
||||
lampUdated();
|
||||
}
|
||||
|
||||
/*
|
||||
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
|
||||
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
|
||||
* Below it is shown how this could be used for e.g. a light sensor
|
||||
*/
|
||||
/*
|
||||
void addToJsonInfo(JsonObject& root)
|
||||
{
|
||||
int reading = 20;
|
||||
//this code adds "u":{"Light":[20," lux"]} to the info object
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
JsonArray lightArr = user.createNestedArray("Light"); //name
|
||||
lightArr.add(reading); //value
|
||||
lightArr.add(" lux"); //unit
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
void addToJsonState(JsonObject &root)
|
||||
{
|
||||
//root["user0"] = userVar0;
|
||||
}
|
||||
|
||||
/*
|
||||
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
void readFromJsonState(JsonObject &root)
|
||||
{
|
||||
//userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
|
||||
//if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
|
||||
}
|
||||
|
||||
/**
|
||||
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
|
||||
*/
|
||||
void addToConfig(JsonObject &root) {
|
||||
// we add JSON object: {"Rotary-Encoder":{"DT-pin":12,"CLK-pin":14,"SW-pin":13}}
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[FPSTR(_DT_pin)] = pinA;
|
||||
top[FPSTR(_CLK_pin)] = pinB;
|
||||
top[FPSTR(_SW_pin)] = pinC;
|
||||
DEBUG_PRINTLN(F("Rotary Encoder config saved."));
|
||||
}
|
||||
|
||||
/**
|
||||
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
|
||||
*
|
||||
* The function should return true if configuration was successfully loaded or false if there was no configuration.
|
||||
*/
|
||||
bool readFromConfig(JsonObject &root) {
|
||||
// we look for JSON object: {"Rotary-Encoder":{"DT-pin":12,"CLK-pin":14,"SW-pin":13}}
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
int8_t newDTpin = top[FPSTR(_DT_pin)] | pinA;
|
||||
int8_t newCLKpin = top[FPSTR(_CLK_pin)] | pinB;
|
||||
int8_t newSWpin = top[FPSTR(_SW_pin)] | pinC;
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
pinA = newDTpin;
|
||||
pinB = newCLKpin;
|
||||
pinC = newSWpin;
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
// changing parameters from settings page
|
||||
if (pinA!=newDTpin || pinB!=newCLKpin || pinC!=newSWpin) {
|
||||
pinManager.deallocatePin(pinA, PinOwner::UM_RotaryEncoderUI);
|
||||
pinManager.deallocatePin(pinB, PinOwner::UM_RotaryEncoderUI);
|
||||
pinManager.deallocatePin(pinC, PinOwner::UM_RotaryEncoderUI);
|
||||
pinA = newDTpin;
|
||||
pinB = newCLKpin;
|
||||
pinC = newSWpin;
|
||||
if (pinA<0 || pinB<0 || pinC<0) {
|
||||
enabled = false;
|
||||
return true;
|
||||
}
|
||||
setup();
|
||||
}
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[FPSTR(_enabled)].isNull();
|
||||
}
|
||||
|
||||
/*
|
||||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
||||
* This could be used in the future for the system to determine whether your usermod is installed.
|
||||
*/
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_ROTARY_ENC_UI;
|
||||
}
|
||||
};
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char RotaryEncoderUIUsermod::_name[] PROGMEM = "Rotary-Encoder";
|
||||
const char RotaryEncoderUIUsermod::_enabled[] PROGMEM = "enabled";
|
||||
const char RotaryEncoderUIUsermod::_DT_pin[] PROGMEM = "DT-pin";
|
||||
const char RotaryEncoderUIUsermod::_CLK_pin[] PROGMEM = "CLK-pin";
|
||||
const char RotaryEncoderUIUsermod::_SW_pin[] PROGMEM = "SW-pin";
|
@ -63,6 +63,11 @@
|
||||
#define I_8266_U1_UCS_4 54
|
||||
#define I_8266_DM_UCS_4 55
|
||||
#define I_8266_BB_UCS_4 56
|
||||
//ESP8266 APA106
|
||||
#define I_8266_U0_APA106_3 81
|
||||
#define I_8266_U1_APA106_3 82
|
||||
#define I_8266_DM_APA106_3 83
|
||||
#define I_8266_BB_APA106_3 84
|
||||
|
||||
/*** ESP32 Neopixel methods ***/
|
||||
//RGB
|
||||
@ -100,6 +105,10 @@
|
||||
#define I_32_I0_UCS_4 61
|
||||
#define I_32_I1_UCS_4 62
|
||||
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
|
||||
#define I_32_RN_APA106_3 85
|
||||
#define I_32_I0_APA106_3 86
|
||||
#define I_32_I1_APA106_3 87
|
||||
#define I_32_BB_APA106_3 88 // bitbangging on ESP32 not recommended
|
||||
|
||||
//APA102
|
||||
#define I_HS_DOT_3 39 //hardware SPI
|
||||
@ -162,6 +171,11 @@
|
||||
#define B_8266_U1_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod> //4 chan, esp8266, gpio2
|
||||
#define B_8266_DM_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp8266Dma800KbpsMethod, NeoGammaNullMethod> //4 chan, esp8266, gpio3
|
||||
#define B_8266_BB_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp8266BitBang800KbpsMethod, NeoGammaNullMethod> //4 chan, esp8266, bb (any pin)
|
||||
//APA106
|
||||
#define B_8266_U0_APA106_3 NeoPixelBusLg<NeoRbgFeature, NeoEsp8266Uart0Apa106Method, NeoGammaNullMethod> //3 chan, esp8266, gpio1
|
||||
#define B_8266_U1_APA106_3 NeoPixelBusLg<NeoRbgFeature, NeoEsp8266Uart1Apa106Method, NeoGammaNullMethod> //3 chan, esp8266, gpio2
|
||||
#define B_8266_DM_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266DmaApa106Method, NeoGammaNullMethod> //3 chan, esp8266, gpio3
|
||||
#define B_8266_BB_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266BitBangApa106Method, NeoGammaNullMethod> //3 chan, esp8266, bb (any pin but 16)
|
||||
#endif
|
||||
|
||||
/*** ESP32 Neopixel methods ***/
|
||||
@ -229,6 +243,14 @@
|
||||
#define B_32_I1_UCS_4 NeoPixelBusLg<NeoRgbwUcs8904Feature, NeoEsp32I2s1800KbpsMethod, NeoGammaNullMethod>
|
||||
#endif
|
||||
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
|
||||
#define B_32_RN_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32RmtNApa106Method, NeoGammaNullMethod>
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
#define B_32_I0_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s0Apa106Method, NeoGammaNullMethod>
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
#define B_32_I1_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp32I2s1Apa106Method, NeoGammaNullMethod>
|
||||
#endif
|
||||
//#define B_32_BB_APA106_3 NeoPixelBusLg<NeoGrbFeature, NeoEsp8266BitBangApa106Method, NeoGammaNullMethod> // NeoEsp8266BitBang800KbpsMethod
|
||||
|
||||
#endif
|
||||
|
||||
@ -327,6 +349,10 @@ class PolyBus {
|
||||
case I_8266_U1_UCS_4: (static_cast<B_8266_U1_UCS_4*>(busPtr))->Begin(); break;
|
||||
case I_8266_DM_UCS_4: (static_cast<B_8266_DM_UCS_4*>(busPtr))->Begin(); break;
|
||||
case I_8266_BB_UCS_4: (static_cast<B_8266_BB_UCS_4*>(busPtr))->Begin(); break;
|
||||
case I_8266_U0_APA106_3: (static_cast<B_8266_U0_APA106_3*>(busPtr))->Begin(); break;
|
||||
case I_8266_U1_APA106_3: (static_cast<B_8266_U1_APA106_3*>(busPtr))->Begin(); break;
|
||||
case I_8266_DM_APA106_3: (static_cast<B_8266_DM_APA106_3*>(busPtr))->Begin(); break;
|
||||
case I_8266_BB_APA106_3: (static_cast<B_8266_BB_APA106_3*>(busPtr))->Begin(); break;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Begin(); break;
|
||||
@ -379,6 +405,14 @@ class PolyBus {
|
||||
case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->Begin(); break;
|
||||
#endif
|
||||
// case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->Begin(); break;
|
||||
case I_32_RN_APA106_3: (static_cast<B_32_RN_APA106_3*>(busPtr))->Begin(); break;
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_APA106_3: (static_cast<B_32_I0_APA106_3*>(busPtr))->Begin(); break;
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_APA106_3: (static_cast<B_32_I1_APA106_3*>(busPtr))->Begin(); break;
|
||||
#endif
|
||||
// case I_32_BB_APA106_3: (static_cast<B_32_BB_APA106_3*>(busPtr))->Begin(); break;
|
||||
// ESP32 can (and should, to avoid inadvertantly driving the chip select signal) specify the pins used for SPI, but only in begin()
|
||||
case I_HS_DOT_3: beginDotStar<B_HS_DOT_3*>(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break;
|
||||
case I_HS_LPD_3: beginDotStar<B_HS_LPD_3*>(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break;
|
||||
@ -427,6 +461,10 @@ class PolyBus {
|
||||
case I_8266_U1_UCS_4: busPtr = new B_8266_U1_UCS_4(len, pins[0]); break;
|
||||
case I_8266_DM_UCS_4: busPtr = new B_8266_DM_UCS_4(len, pins[0]); break;
|
||||
case I_8266_BB_UCS_4: busPtr = new B_8266_BB_UCS_4(len, pins[0]); break;
|
||||
case I_8266_U0_APA106_3: busPtr = new B_8266_U0_APA106_3(len, pins[0]); break;
|
||||
case I_8266_U1_APA106_3: busPtr = new B_8266_U1_APA106_3(len, pins[0]); break;
|
||||
case I_8266_DM_APA106_3: busPtr = new B_8266_DM_APA106_3(len, pins[0]); break;
|
||||
case I_8266_BB_APA106_3: busPtr = new B_8266_BB_APA106_3(len, pins[0]); break;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); break;
|
||||
@ -479,6 +517,14 @@ class PolyBus {
|
||||
case I_32_I1_UCS_4: busPtr = new B_32_I1_UCS_4(len, pins[0]); break;
|
||||
#endif
|
||||
// case I_32_BB_UCS_4: busPtr = new B_32_BB_UCS_4(len, pins[0], (NeoBusChannel)channel); break;
|
||||
case I_32_RN_APA106_3: busPtr = new B_32_RN_APA106_3(len, pins[0], (NeoBusChannel)channel); break;
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_APA106_3: busPtr = new B_32_I0_APA106_3(len, pins[0]); break;
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_APA106_3: busPtr = new B_32_I1_APA106_3(len, pins[0]); break;
|
||||
#endif
|
||||
// case I_32_BB_APA106_3: busPtr = new B_32_BB_APA106_3(len, pins[0], (NeoBusChannel)channel); break;
|
||||
#endif
|
||||
// for 2-wire: pins[1] is clk, pins[0] is dat. begin expects (len, clk, dat)
|
||||
case I_HS_DOT_3: busPtr = new B_HS_DOT_3(len, pins[1], pins[0]); break;
|
||||
@ -528,6 +574,10 @@ class PolyBus {
|
||||
case I_8266_U1_UCS_4: (static_cast<B_8266_U1_UCS_4*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_DM_UCS_4: (static_cast<B_8266_DM_UCS_4*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_BB_UCS_4: (static_cast<B_8266_BB_UCS_4*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_U0_APA106_3: (static_cast<B_8266_U0_APA106_3*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_U1_APA106_3: (static_cast<B_8266_U1_APA106_3*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_DM_APA106_3: (static_cast<B_8266_DM_APA106_3*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_BB_APA106_3: (static_cast<B_8266_BB_APA106_3*>(busPtr))->Show(consistent); break;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Show(consistent); break;
|
||||
@ -580,6 +630,14 @@ class PolyBus {
|
||||
case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->Show(consistent); break;
|
||||
#endif
|
||||
// case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->Show(consistent); break;
|
||||
case I_32_RN_APA106_3: (static_cast<B_32_RN_APA106_3*>(busPtr))->Show(consistent); break;
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_APA106_3: (static_cast<B_32_I0_APA106_3*>(busPtr))->Show(consistent); break;
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_APA106_3: (static_cast<B_32_I1_APA106_3*>(busPtr))->Show(consistent); break;
|
||||
#endif
|
||||
// case I_32_BB_APA106_3: (static_cast<B_32_BB_APA106_3*>(busPtr))->Show(consistent); break;
|
||||
#endif
|
||||
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Show(consistent); break;
|
||||
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->Show(consistent); break;
|
||||
@ -625,6 +683,10 @@ class PolyBus {
|
||||
case I_8266_U0_UCS_4: return (static_cast<B_8266_U0_UCS_4*>(busPtr))->CanShow(); break;
|
||||
case I_8266_U1_UCS_4: return (static_cast<B_8266_U1_UCS_4*>(busPtr))->CanShow(); break;
|
||||
case I_8266_DM_UCS_4: return (static_cast<B_8266_DM_UCS_4*>(busPtr))->CanShow(); break;
|
||||
case I_8266_U0_APA106_3: return (static_cast<B_8266_U0_APA106_3*>(busPtr))->CanShow(); break;
|
||||
case I_8266_U1_APA106_3: return (static_cast<B_8266_U1_APA106_3*>(busPtr))->CanShow(); break;
|
||||
case I_8266_DM_APA106_3: return (static_cast<B_8266_DM_APA106_3*>(busPtr))->CanShow(); break;
|
||||
case I_8266_BB_APA106_3: return (static_cast<B_8266_BB_APA106_3*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: return (static_cast<B_32_RN_NEO_3*>(busPtr))->CanShow(); break;
|
||||
@ -677,6 +739,14 @@ class PolyBus {
|
||||
case I_32_I1_UCS_4: return (static_cast<B_32_I1_UCS_4*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
// case I_32_BB_UCS_4: return (static_cast<B_32_BB_UCS_4*>(busPtr))->CanShow(); break;
|
||||
case I_32_RN_APA106_3: return (static_cast<B_32_RN_APA106_3*>(busPtr))->CanShow(); break;
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_APA106_3: return (static_cast<B_32_I0_APA106_3*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_APA106_3: return (static_cast<B_32_I1_APA106_3*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
// case I_32_BB_APA106_3: return (static_cast<B_32_BB_APA106_3*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
case I_HS_DOT_3: return (static_cast<B_HS_DOT_3*>(busPtr))->CanShow(); break;
|
||||
case I_SS_DOT_3: return (static_cast<B_SS_DOT_3*>(busPtr))->CanShow(); break;
|
||||
@ -747,6 +817,10 @@ class PolyBus {
|
||||
case I_8266_U1_UCS_4: (static_cast<B_8266_U1_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
|
||||
case I_8266_DM_UCS_4: (static_cast<B_8266_DM_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
|
||||
case I_8266_BB_UCS_4: (static_cast<B_8266_BB_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
|
||||
case I_8266_U0_APA106_3: (static_cast<B_8266_U0_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_8266_U1_APA106_3: (static_cast<B_8266_U1_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_8266_DM_APA106_3: (static_cast<B_8266_DM_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_8266_BB_APA106_3: (static_cast<B_8266_BB_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
@ -799,6 +873,14 @@ class PolyBus {
|
||||
case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
|
||||
#endif
|
||||
// case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->SetPixelColor(pix, Rgbw64Color(col)); break;
|
||||
case I_32_RN_APA106_3: (static_cast<B_32_RN_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_APA106_3: (static_cast<B_32_I0_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_APA106_3: (static_cast<B_32_I1_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
#endif
|
||||
// case I_32_BB_APA106_3: (static_cast<B_32_BB_APA106_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
#endif
|
||||
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
@ -845,6 +927,10 @@ class PolyBus {
|
||||
case I_8266_U1_UCS_4: (static_cast<B_8266_U1_UCS_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_DM_UCS_4: (static_cast<B_8266_DM_UCS_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_BB_UCS_4: (static_cast<B_8266_BB_UCS_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_U0_APA106_3: (static_cast<B_8266_U0_APA106_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_U1_APA106_3: (static_cast<B_8266_U1_APA106_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_DM_APA106_3: (static_cast<B_8266_DM_APA106_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_BB_APA106_3: (static_cast<B_8266_BB_APA106_3*>(busPtr))->SetLuminance(b); break;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetLuminance(b); break;
|
||||
@ -897,6 +983,14 @@ class PolyBus {
|
||||
case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->SetLuminance(b); break;
|
||||
#endif
|
||||
// case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_32_RN_APA106_3: (static_cast<B_32_RN_APA106_3*>(busPtr))->SetLuminance(b); break;
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_APA106_3: (static_cast<B_32_I0_APA106_3*>(busPtr))->SetLuminance(b); break;
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_APA106_3: (static_cast<B_32_I1_APA106_3*>(busPtr))->SetLuminance(b); break;
|
||||
#endif
|
||||
// case I_32_BB_APA106_3: (static_cast<B_32_BB_APA106_3*>(busPtr))->SetLuminance(b); break;
|
||||
#endif
|
||||
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetLuminance(b); break;
|
||||
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetLuminance(b); break;
|
||||
@ -944,6 +1038,10 @@ class PolyBus {
|
||||
case I_8266_U1_UCS_4: { Rgbw64Color c = (static_cast<B_8266_U1_UCS_4*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.W>>8); } break;
|
||||
case I_8266_DM_UCS_4: { Rgbw64Color c = (static_cast<B_8266_DM_UCS_4*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.W>>8); } break;
|
||||
case I_8266_BB_UCS_4: { Rgbw64Color c = (static_cast<B_8266_BB_UCS_4*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.W>>8); } break;
|
||||
case I_8266_U0_APA106_3: col = (static_cast<B_8266_U0_APA106_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_U1_APA106_3: col = (static_cast<B_8266_U1_APA106_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_DM_APA106_3: col = (static_cast<B_8266_DM_APA106_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_BB_APA106_3: col = (static_cast<B_8266_BB_APA106_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: col = (static_cast<B_32_RN_NEO_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
@ -996,6 +1094,14 @@ class PolyBus {
|
||||
case I_32_I1_UCS_4: { Rgbw64Color c = (static_cast<B_32_I1_UCS_4*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.W>>8); } break;
|
||||
#endif
|
||||
// case I_32_BB_UCS_4: col = (static_cast<B_32_BB_UCS_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_RN_APA106_3: col = (static_cast<B_32_RN_APA106_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_APA106_3: col = (static_cast<B_32_I0_APA106_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_APA106_3: col = (static_cast<B_32_I1_APA106_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#endif
|
||||
// case I_32_BB_APA106_3: col = (static_cast<B_32_BB_APA106_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#endif
|
||||
case I_HS_DOT_3: col = (static_cast<B_HS_DOT_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_SS_DOT_3: col = (static_cast<B_SS_DOT_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
@ -1061,6 +1167,10 @@ class PolyBus {
|
||||
case I_8266_U1_UCS_4: delete (static_cast<B_8266_U1_UCS_4*>(busPtr)); break;
|
||||
case I_8266_DM_UCS_4: delete (static_cast<B_8266_DM_UCS_4*>(busPtr)); break;
|
||||
case I_8266_BB_UCS_4: delete (static_cast<B_8266_BB_UCS_4*>(busPtr)); break;
|
||||
case I_8266_U0_APA106_3: delete (static_cast<B_8266_U0_APA106_3*>(busPtr)); break;
|
||||
case I_8266_U1_APA106_3: delete (static_cast<B_8266_U1_APA106_3*>(busPtr)); break;
|
||||
case I_8266_DM_APA106_3: delete (static_cast<B_8266_DM_APA106_3*>(busPtr)); break;
|
||||
case I_8266_BB_APA106_3: delete (static_cast<B_8266_BB_APA106_3*>(busPtr)); break;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: delete (static_cast<B_32_RN_NEO_3*>(busPtr)); break;
|
||||
@ -1113,6 +1223,14 @@ class PolyBus {
|
||||
case I_32_I1_UCS_4: delete (static_cast<B_32_I1_UCS_4*>(busPtr)); break;
|
||||
#endif
|
||||
// case I_32_BB_UCS_4: delete (static_cast<B_32_BB_UCS_4*>(busPtr)); break;
|
||||
case I_32_RN_APA106_3: delete (static_cast<B_32_RN_APA106_3*>(busPtr)); break;
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_APA106_3: delete (static_cast<B_32_I0_APA106_3*>(busPtr)); break;
|
||||
#endif
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_APA106_3: delete (static_cast<B_32_I1_APA106_3*>(busPtr)); break;
|
||||
#endif
|
||||
// case I_32_BB_APA106_3: delete (static_cast<B_32_BB_APA106_3*>(busPtr)); break;
|
||||
#endif
|
||||
case I_HS_DOT_3: delete (static_cast<B_HS_DOT_3*>(busPtr)); break;
|
||||
case I_SS_DOT_3: delete (static_cast<B_SS_DOT_3*>(busPtr)); break;
|
||||
@ -1172,6 +1290,8 @@ class PolyBus {
|
||||
return I_8266_U0_UCS_3 + offset;
|
||||
case TYPE_UCS8904:
|
||||
return I_8266_U0_UCS_4 + offset;
|
||||
case TYPE_APA106:
|
||||
return I_8266_U0_APA106_3 + offset;
|
||||
}
|
||||
#else //ESP32
|
||||
uint8_t offset = 0; //0 = RMT (num 0-7) 8 = I2S0 9 = I2S1
|
||||
@ -1210,6 +1330,8 @@ class PolyBus {
|
||||
return I_32_RN_UCS_3 + offset;
|
||||
case TYPE_UCS8904:
|
||||
return I_32_RN_UCS_4 + offset;
|
||||
case TYPE_APA106:
|
||||
return I_32_RN_APA106_3 + offset;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
104
wled00/cfg.cpp
104
wled00/cfg.cpp
@ -40,21 +40,39 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
linked_remote[12] = '\0';
|
||||
#endif
|
||||
|
||||
JsonObject nw_ins_0 = nw["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);
|
||||
size_t n = 0;
|
||||
JsonArray nw_ins = nw["ins"];
|
||||
if (!nw_ins.isNull()) {
|
||||
// as password are stored separately in wsec.json when reading configuration vector resize happens there, but for dynamic config we need to resize if necessary
|
||||
if (nw_ins.size() > 1 && nw_ins.size() > multiWiFi.size()) multiWiFi.resize(nw_ins.size()); // resize constructs objects while resizing
|
||||
for (JsonObject wifi : nw_ins) {
|
||||
JsonArray ip = wifi["ip"];
|
||||
JsonArray gw = wifi["gw"];
|
||||
JsonArray sn = wifi["sn"];
|
||||
char ssid[33] = "";
|
||||
char pass[65] = "";
|
||||
IPAddress nIP = (uint32_t)0U, nGW = (uint32_t)0U, nSN = (uint32_t)0x00FFFFFF; // little endian
|
||||
getStringFromJson(ssid, wifi[F("ssid")], 33);
|
||||
getStringFromJson(pass, wifi["psk"], 65); // password is not normally present but if it is, use it
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
CJSON(nIP[i], ip[i]);
|
||||
CJSON(nGW[i], gw[i]);
|
||||
CJSON(nSN[i], sn[i]);
|
||||
}
|
||||
if (strlen(ssid) > 0) strlcpy(multiWiFi[n].clientSSID, ssid, 33); // this will keep old SSID intact if not present in JSON
|
||||
if (strlen(pass) > 0) strlcpy(multiWiFi[n].clientPass, pass, 65); // this will keep old password intact if not present in JSON
|
||||
multiWiFi[n].staticIP = nIP;
|
||||
multiWiFi[n].staticGW = nGW;
|
||||
multiWiFi[n].staticSN = nSN;
|
||||
if (++n >= WLED_MAX_WIFI_COUNT) break;
|
||||
}
|
||||
}
|
||||
|
||||
JsonArray nw_ins_0_ip = nw_ins_0["ip"];
|
||||
JsonArray nw_ins_0_gw = nw_ins_0["gw"];
|
||||
JsonArray nw_ins_0_sn = nw_ins_0["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]);
|
||||
JsonArray dns = nw[F("dns")];
|
||||
if (!dns.isNull()) {
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
CJSON(dnsAddress[i], dns[i]);
|
||||
}
|
||||
}
|
||||
|
||||
JsonObject ap = doc["ap"];
|
||||
@ -212,7 +230,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
JsonObject btn_obj = hw["btn"];
|
||||
bool pull = btn_obj[F("pull")] | (!disablePullUp); // if true, pullup is enabled
|
||||
disablePullUp = !pull;
|
||||
JsonArray hw_btn_ins = btn_obj[F("ins")];
|
||||
JsonArray hw_btn_ins = btn_obj["ins"];
|
||||
if (!hw_btn_ins.isNull()) {
|
||||
for (uint8_t b = 0; b < WLED_MAX_BUTTONS; b++) { // deallocate existing button pins
|
||||
pinManager.deallocatePin(btnPin[b], PinOwner::Button); // does nothing if trying to deallocate a pin with PinOwner != Button
|
||||
@ -433,7 +451,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
if (e131Port == DDP_DEFAULT_PORT) e131Port = E131_DEFAULT_PORT; // prevent double DDP port allocation
|
||||
CJSON(e131Multicast, if_live[F("mc")]);
|
||||
|
||||
JsonObject if_live_dmx = if_live[F("dmx")];
|
||||
JsonObject if_live_dmx = if_live["dmx"];
|
||||
CJSON(e131Universe, if_live_dmx[F("uni")]);
|
||||
CJSON(e131SkipOutOfSequence, if_live_dmx[F("seqskip")]);
|
||||
CJSON(DMXAddress, if_live_dmx[F("addr")]);
|
||||
@ -507,6 +525,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
CJSON(analogClock12pixel, ol[F("o12pix")]);
|
||||
CJSON(analogClock5MinuteMarks, ol[F("o5m")]);
|
||||
CJSON(analogClockSecondsTrail, ol[F("osec")]);
|
||||
CJSON(analogClockSolidBlack, ol[F("osb")]);
|
||||
|
||||
//timed macro rules
|
||||
JsonObject tm = doc[F("timers")];
|
||||
@ -665,19 +684,23 @@ void serializeConfig() {
|
||||
#endif
|
||||
|
||||
JsonArray nw_ins = nw.createNestedArray("ins");
|
||||
for (size_t n = 0; n < multiWiFi.size(); n++) {
|
||||
JsonObject wifi = nw_ins.createNestedObject();
|
||||
wifi[F("ssid")] = multiWiFi[n].clientSSID;
|
||||
wifi[F("pskl")] = strlen(multiWiFi[n].clientPass);
|
||||
JsonArray wifi_ip = wifi.createNestedArray("ip");
|
||||
JsonArray wifi_gw = wifi.createNestedArray("gw");
|
||||
JsonArray wifi_sn = wifi.createNestedArray("sn");
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
wifi_ip.add(multiWiFi[n].staticIP[i]);
|
||||
wifi_gw.add(multiWiFi[n].staticGW[i]);
|
||||
wifi_sn.add(multiWiFi[n].staticSN[i]);
|
||||
}
|
||||
}
|
||||
|
||||
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]);
|
||||
JsonArray dns = nw.createNestedArray(F("dns"));
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
dns.add(dnsAddress[i]);
|
||||
}
|
||||
|
||||
JsonObject ap = root.createNestedObject("ap");
|
||||
@ -693,7 +716,7 @@ void serializeConfig() {
|
||||
ap_ip.add(2);
|
||||
ap_ip.add(1);
|
||||
|
||||
JsonObject wifi = root.createNestedObject("wifi");
|
||||
JsonObject wifi = root.createNestedObject(F("wifi"));
|
||||
wifi[F("sleep")] = !noWifiSleep;
|
||||
wifi[F("phy")] = force802_3g;
|
||||
|
||||
@ -721,7 +744,7 @@ void serializeConfig() {
|
||||
}
|
||||
#endif
|
||||
|
||||
JsonObject hw = root.createNestedObject("hw");
|
||||
JsonObject hw = root.createNestedObject(F("hw"));
|
||||
|
||||
JsonObject hw_led = hw.createNestedObject("led");
|
||||
hw_led[F("total")] = strip.getLengthTotal(); //provided for compatibility on downgrade and per-output ABL
|
||||
@ -973,6 +996,7 @@ void serializeConfig() {
|
||||
ol[F("o12pix")] = analogClock12pixel;
|
||||
ol[F("o5m")] = analogClock5MinuteMarks;
|
||||
ol[F("osec")] = analogClockSecondsTrail;
|
||||
ol[F("osb")] = analogClockSolidBlack;
|
||||
|
||||
JsonObject timers = root.createNestedObject(F("timers"));
|
||||
|
||||
@ -1048,8 +1072,17 @@ bool deserializeConfigSec() {
|
||||
|
||||
JsonObject root = pDoc->as<JsonObject>();
|
||||
|
||||
JsonObject nw_ins_0 = root["nw"]["ins"][0];
|
||||
getStringFromJson(clientPass, nw_ins_0["psk"], 65);
|
||||
size_t n = 0;
|
||||
JsonArray nw_ins = root["nw"]["ins"];
|
||||
if (!nw_ins.isNull()) {
|
||||
if (nw_ins.size() > 1 && nw_ins.size() > multiWiFi.size()) multiWiFi.resize(nw_ins.size()); // resize constructs objects while resizing
|
||||
for (JsonObject wifi : nw_ins) {
|
||||
char pw[65] = "";
|
||||
getStringFromJson(pw, wifi["psk"], 65);
|
||||
strlcpy(multiWiFi[n].clientPass, pw, 65);
|
||||
if (++n >= WLED_MAX_WIFI_COUNT) break;
|
||||
}
|
||||
}
|
||||
|
||||
JsonObject ap = root["ap"];
|
||||
getStringFromJson(apPass, ap["psk"] , 65);
|
||||
@ -1088,9 +1121,10 @@ void serializeConfigSec() {
|
||||
JsonObject nw = root.createNestedObject("nw");
|
||||
|
||||
JsonArray nw_ins = nw.createNestedArray("ins");
|
||||
|
||||
JsonObject nw_ins_0 = nw_ins.createNestedObject();
|
||||
nw_ins_0["psk"] = clientPass;
|
||||
for (size_t i = 0; i < multiWiFi.size(); i++) {
|
||||
JsonObject wifi = nw_ins.createNestedObject();
|
||||
wifi[F("psk")] = multiWiFi[i].clientPass;
|
||||
}
|
||||
|
||||
JsonObject ap = root.createNestedObject("ap");
|
||||
ap["psk"] = apPass;
|
||||
|
@ -15,6 +15,10 @@
|
||||
#define DEFAULT_MDNS_NAME "x"
|
||||
|
||||
//increase if you need more
|
||||
#ifndef WLED_MAX_WIFI_COUNT
|
||||
#define WLED_MAX_WIFI_COUNT 3
|
||||
#endif
|
||||
|
||||
#ifndef WLED_MAX_USERMODS
|
||||
#ifdef ESP8266
|
||||
#define WLED_MAX_USERMODS 4
|
||||
@ -157,6 +161,10 @@
|
||||
#define AP_BEHAVIOR_NO_CONN 1 //Open when no connection (either after boot or if connection is lost)
|
||||
#define AP_BEHAVIOR_ALWAYS 2 //Always open
|
||||
#define AP_BEHAVIOR_BUTTON_ONLY 3 //Only when button pressed for 6 sec
|
||||
#define AP_BEHAVIOR_TEMPORARY 4 //Open AP when no connection after boot but only temporary
|
||||
#ifndef WLED_AP_TIMEOUT
|
||||
#define WLED_AP_TIMEOUT 300000 //Temporary AP timeout
|
||||
#endif
|
||||
|
||||
//Notifier callMode
|
||||
#define CALL_MODE_INIT 0 //no updates on init, can be used to disable updates
|
||||
@ -235,6 +243,7 @@
|
||||
#define TYPE_WS2811_400KHZ 24 //half-speed WS2812 protocol, used by very old WS2811 units
|
||||
#define TYPE_TM1829 25
|
||||
#define TYPE_UCS8903 26
|
||||
#define TYPE_APA106 27
|
||||
#define TYPE_UCS8904 29 //first RGBW digital type (hardcoded in busmanager.cpp, memUsage())
|
||||
#define TYPE_SK6812_RGBW 30
|
||||
#define TYPE_TM1814 31
|
||||
@ -292,7 +301,7 @@
|
||||
#define BTN_TYPE_TOUCH_SWITCH 9
|
||||
|
||||
//Ethernet board types
|
||||
#define WLED_NUM_ETH_TYPES 11
|
||||
#define WLED_NUM_ETH_TYPES 12
|
||||
|
||||
#define WLED_ETH_NONE 0
|
||||
#define WLED_ETH_WT32_ETH01 1
|
||||
@ -305,6 +314,7 @@
|
||||
#define WLED_ETH_QUINLED_OCTA 8
|
||||
#define WLED_ETH_ABCWLEDV43ETH 9
|
||||
#define WLED_ETH_SERG74 10
|
||||
#define WLED_ETH_ESP32_POE_WROVER 11
|
||||
|
||||
//Hue error codes
|
||||
#define HUE_ERROR_INACTIVE 0
|
||||
@ -416,7 +426,7 @@
|
||||
#ifdef ESP8266
|
||||
#define SETTINGS_STACK_BUF_SIZE 2048
|
||||
#else
|
||||
#define SETTINGS_STACK_BUF_SIZE 3608 // warning: quite a large value for stack
|
||||
#define SETTINGS_STACK_BUF_SIZE 3840 // warning: quite a large value for stack (640 * WLED_MAX_USERMODS)
|
||||
#endif
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
|
@ -346,10 +346,14 @@ button {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
#Segments, #Presets, #Effects, #Colors {
|
||||
font-size: 19px;
|
||||
padding: 4px 0 0;
|
||||
}
|
||||
|
||||
#segutil, #segutil2, #segcont, #putil, #pcont, #pql, #fx, #palw,
|
||||
.fnd {
|
||||
max-width: 280px;
|
||||
font-size: 19px;
|
||||
}
|
||||
|
||||
#putil, #segutil, #segutil2 {
|
||||
@ -361,7 +365,7 @@ button {
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
#fx, #pql, #segcont, #pcont, #sliders, #picker, #qcs-w, #hexw, #pall, #ledmap,
|
||||
#fx, #pql, #segcont, #pcont, #sliders, #qcs-w, #hexw, #pall, #ledmap,
|
||||
.slider, .filter, .option, .segname, .pname, .fnd {
|
||||
margin: 0 auto;
|
||||
}
|
||||
@ -371,15 +375,10 @@ button {
|
||||
}
|
||||
|
||||
/* Quick load magin for simplified UI */
|
||||
.simplified #pql {
|
||||
.simplified #pql, .simplified #palw, .simplified #fx {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* Button margin for simplified UI */
|
||||
.simplified #fx .btn, .simplified #palw .btn {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.smooth { transition: transform calc(var(--f, 1)*.5s) ease-out }
|
||||
|
||||
.tab-label {
|
||||
@ -624,12 +623,10 @@ button {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
#info .btn {
|
||||
.infobtn {
|
||||
margin: 5px;
|
||||
}
|
||||
#info table .btn, #nodes table .btn {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#info div, #nodes div {
|
||||
max-width: 490px;
|
||||
margin: 0 auto;
|
||||
@ -784,14 +781,14 @@ input[type=range]::-moz-range-thumb {
|
||||
}
|
||||
|
||||
#picker {
|
||||
margin-top: 8px !important;
|
||||
margin: 4px auto 0 !important;
|
||||
max-width: max-content;
|
||||
}
|
||||
|
||||
/* buttons */
|
||||
.btn {
|
||||
padding: 8px;
|
||||
margin: 10px 4px;
|
||||
/*margin: 10px 4px;*/
|
||||
width: 230px;
|
||||
font-size: 19px;
|
||||
color: var(--c-d);
|
||||
@ -837,14 +834,14 @@ input[type=range]::-moz-range-thumb {
|
||||
text-overflow: clip;
|
||||
}
|
||||
.btn-xs {
|
||||
margin: 2px 0 0 0;
|
||||
}
|
||||
#putil .btn-xs {
|
||||
margin: 0;
|
||||
}
|
||||
#info .btn-xs {
|
||||
border: 1px solid var(--c-4);
|
||||
}
|
||||
#btns .btn-xs {
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
#putil .btn-s {
|
||||
width: 135px;
|
||||
@ -863,6 +860,15 @@ input[type=range]::-moz-range-thumb {
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
a.btn {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
padding: 9px 32px 7px 24px;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
/* Quick color select wrapper div */
|
||||
#qcs-w {
|
||||
@ -913,9 +919,6 @@ select {
|
||||
#tt {
|
||||
text-align: center;
|
||||
}
|
||||
.cl {
|
||||
background-color: #000;
|
||||
}
|
||||
select.sel-p, select.sel-pl, select.sel-ple {
|
||||
margin: 5px 0;
|
||||
width: 100%;
|
||||
@ -1018,7 +1021,7 @@ textarea {
|
||||
width: 50px !important;
|
||||
}
|
||||
|
||||
.segname, .pname, .bname {
|
||||
.segname, .pname {
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
@ -1028,9 +1031,6 @@ textarea {
|
||||
max-width: 170px;
|
||||
position: relative;
|
||||
}
|
||||
.bname {
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.segname .flr, .pname .flr {
|
||||
transform: rotate(0deg);
|
||||
@ -1065,27 +1065,24 @@ textarea {
|
||||
.newseg {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/*
|
||||
.ic {
|
||||
padding: 6px 0 0 0;
|
||||
}
|
||||
|
||||
.xxs {
|
||||
*/
|
||||
/* color selector */
|
||||
#csl button {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
margin: 5px;
|
||||
border: 2px solid var(--c-d) !important;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
.xxs-w {
|
||||
/* selected color selector */
|
||||
#csl .sl {
|
||||
margin: 2px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
#csl .xxs {
|
||||
border: 2px solid var(--c-d) !important;
|
||||
}
|
||||
#csl .xxs-w {
|
||||
border-width: 5px !important;
|
||||
}
|
||||
|
||||
@ -1290,15 +1287,11 @@ TD .checkmark, TD .radiomark {
|
||||
position: -webkit-sticky;
|
||||
position: sticky;
|
||||
border-radius: 21px;
|
||||
margin: 13px auto 0;
|
||||
margin: 0 auto 12px;
|
||||
min-height: 40px;
|
||||
border: 1px solid var(--c-2);
|
||||
}
|
||||
|
||||
#segutil .lstI {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Simplify segments */
|
||||
.simplified #segcont .lstI {
|
||||
margin-top: 4px;
|
||||
@ -1397,7 +1390,7 @@ dialog {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 8px 40px 8px 44px;
|
||||
margin: 5px auto 0;
|
||||
margin: 4px auto 12px;
|
||||
text-align: left;
|
||||
border-radius: 21px;
|
||||
background: var(--c-2);
|
||||
@ -1415,6 +1408,13 @@ dialog {
|
||||
background-color: var(--c-3);
|
||||
}
|
||||
|
||||
#fxFind.fnd input[type="text"] {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
#fxFind {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
/* segment & preset inner/expanded content */
|
||||
.segin,
|
||||
.presin {
|
||||
@ -1520,7 +1520,7 @@ dialog {
|
||||
#info .infobtn, #nodes .infobtn {
|
||||
width: 145px;
|
||||
}
|
||||
#info div, #nodes div {
|
||||
#info div, #nodes div, #nodes a.btn {
|
||||
max-width: 320px;
|
||||
}
|
||||
}
|
||||
|
@ -46,91 +46,91 @@
|
||||
<div id="Colors" class="tabcontent">
|
||||
<div id="picker" class="noslide"></div>
|
||||
<div id="hwrap" class="slider">
|
||||
<div tooltip="Hue" class="sliderwrap il">
|
||||
<div title="Hue" class="sliderwrap il">
|
||||
<input id="sliderH" class="noslide" oninput="fromH()" onchange="setColor(0)" max="359" min="0" type="range" value="0" step="any">
|
||||
<div class="sliderdisplay" style="background: linear-gradient(90deg, #f00 2%, #ff0 19%, #0f0 35%, #0ff 52%, #00f 68%, #f0f 85%, #f00)"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="swrap" class="slider">
|
||||
<div tooltip="Saturation" class="sliderwrap il">
|
||||
<div title="Saturation" class="sliderwrap il">
|
||||
<input id="sliderS" class="noslide" oninput="fromS()" onchange="setColor(0)" max="100" min="0" type="range" value="100" step="any">
|
||||
<div class="sliderdisplay" style="background: linear-gradient(90deg, #aaa 0%, #f00)"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="vwrap" class="slider">
|
||||
<div tooltip="Value/Brightness" class="sliderwrap il">
|
||||
<div title="Value/Brightness" class="sliderwrap il">
|
||||
<input id="sliderV" class="noslide" oninput="fromV()" onchange="setColor(0)" max="100" min="0" type="range" value="100" step="any" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="kwrap" class="slider">
|
||||
<div tooltip="Kelvin/Temperature" class="sliderwrap il">
|
||||
<div title="Kelvin/Temperature" class="sliderwrap il">
|
||||
<input id="sliderK" class="noslide" oninput="fromK()" onchange="setColor(0)" max="10091" min="1900" type="range" value="6550" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="rgbwrap">
|
||||
<div id="rwrap" class="slider">
|
||||
<div tooltip="Red channel" class="sliderwrap il">
|
||||
<div title="Red channel" class="sliderwrap il">
|
||||
<input id="sliderR" class="noslide" oninput="fromRgb()" onchange="setColor(0)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="gwrap" class="slider">
|
||||
<div tooltip="Green channel" class="sliderwrap il">
|
||||
<div title="Green channel" class="sliderwrap il">
|
||||
<input id="sliderG" class="noslide" oninput="fromRgb()" onchange="setColor(0)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="bwrap" class="slider">
|
||||
<div tooltip="Blue channel" class="sliderwrap il">
|
||||
<div title="Blue channel" class="sliderwrap il">
|
||||
<input id="sliderB" class="noslide" oninput="fromRgb()" onchange="setColor(0)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="wwrap" class="slider">
|
||||
<div id="whibri" tooltip="White channel" class="sliderwrap il">
|
||||
<div id="whibri" title="White channel" class="sliderwrap il">
|
||||
<input id="sliderW" class="noslide" oninput="fromW()" onchange="setColor(0)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="wbal" class="slider">
|
||||
<div tooltip="White balance" class="sliderwrap il">
|
||||
<div title="White balance" class="sliderwrap il">
|
||||
<input id="sliderA" class="noslide" onchange="setBalance(this.value)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="qcs-w">
|
||||
<div class="qcs" onclick="pC('#ff0000');" tooltip="Red" style="background-color:#ff0000;"></div>
|
||||
<div class="qcs" onclick="pC('#ffa000');" tooltip="Orange" style="background-color:#ffa000;"></div>
|
||||
<div class="qcs" onclick="pC('#ffc800');" tooltip="Yellow" style="background-color:#ffc800;"></div>
|
||||
<div class="qcs" onclick="pC('#ffe0a0');" tooltip="Warm White" style="background-color:#ffe0a0;"></div>
|
||||
<div class="qcs" onclick="pC('#ffffff');" tooltip="White" style="background-color:#ffffff;"></div>
|
||||
<div class="qcs qcsb" onclick="pC('#000000');" tooltip="Black" style="background-color:#000000;"></div><br>
|
||||
<div class="qcs" onclick="pC('#ff00ff');" tooltip="Pink" style="background-color:#ff00ff;"></div>
|
||||
<div class="qcs" onclick="pC('#0000ff');" tooltip="Blue" style="background-color:#0000ff;"></div>
|
||||
<div class="qcs" onclick="pC('#00ffc8');" tooltip="Cyan" style="background-color:#00ffc8;"></div>
|
||||
<div class="qcs" onclick="pC('#08ff00');" tooltip="Green" style="background-color:#08ff00;"></div>
|
||||
<div class="qcs" onclick="pC('rnd');" tooltip="Random" style="background:linear-gradient(to right, red, orange, yellow, green, blue, purple);transform: translateY(-11px);">R</div>
|
||||
<div class="qcs" onclick="pC('#ff0000');" style="background-color:#ff0000;"></div>
|
||||
<div class="qcs" onclick="pC('#ffa000');" style="background-color:#ffa000;"></div>
|
||||
<div class="qcs" onclick="pC('#ffc800');" style="background-color:#ffc800;"></div>
|
||||
<div class="qcs" onclick="pC('#ffe0a0');" style="background-color:#ffe0a0;"></div>
|
||||
<div class="qcs" onclick="pC('#ffffff');" style="background-color:#ffffff;"></div>
|
||||
<div class="qcs qcsb" onclick="pC('#000000');" style="background-color:#000000;"></div><br>
|
||||
<div class="qcs" onclick="pC('#ff00ff');" style="background-color:#ff00ff;"></div>
|
||||
<div class="qcs" onclick="pC('#0000ff');" style="background-color:#0000ff;"></div>
|
||||
<div class="qcs" onclick="pC('#00ffc8');" style="background-color:#00ffc8;"></div>
|
||||
<div class="qcs" onclick="pC('#08ff00');" style="background-color:#08ff00;"></div>
|
||||
<div class="qcs" onclick="pC('rnd');" title="Random" style="background:linear-gradient(to right, red, orange, yellow, green, blue, purple);transform: translateY(-11px);">R</div>
|
||||
</div>
|
||||
<div id="csl">
|
||||
<button id="csl0" tooltip="Select slot" class="btn xxs cl" onclick="selectSlot(0);" data-r="0" data-g="0" data-b="0" data-w="0">1</button>
|
||||
<button id="csl1" tooltip="Select slot" class="btn xxs cl" onclick="selectSlot(1);" data-r="0" data-g="0" data-b="0" data-w="0">2</button>
|
||||
<button id="csl2" tooltip="Select slot" class="btn xxs cl" onclick="selectSlot(2);" data-r="0" data-g="0" data-b="0" data-w="0">3</button>
|
||||
<button id="csl0" title="Select slot" class="btn" onclick="selectSlot(0);" data-r="0" data-g="0" data-b="0" data-w="0">1</button>
|
||||
<button id="csl1" title="Select slot" class="btn" onclick="selectSlot(1);" data-r="0" data-g="0" data-b="0" data-w="0">2</button>
|
||||
<button id="csl2" title="Select slot" class="btn" onclick="selectSlot(2);" data-r="0" data-g="0" data-b="0" data-w="0">3</button>
|
||||
</div>
|
||||
<p class="labels h" id="cslLabel"></p>
|
||||
<div id="hexw">
|
||||
<i class="icons sel-icon" onclick="tglRgb()"></i>
|
||||
<input id="hexc" tooltip="Hex RGB" type="text" class="noslide" onkeydown="hexEnter()" autocomplete="off" maxlength="8" />
|
||||
<input id="hexc" title="Hex RGB" type="text" class="noslide" onkeydown="hexEnter()" autocomplete="off" maxlength="8" />
|
||||
<button id="hexcnf" class="btn btn-xs" onclick="fromHex();"><i class="icons btn-icon"></i></button>
|
||||
</div>
|
||||
<div style="padding: 8px 0;" id="btns">
|
||||
<button class="btn btn-xs" tooltip="Pixel Magic Tool" type="button" id="pxmb" onclick="window.location.href=getURL('/pxmagic.htm')"><i class="icons btn-icon"></i></button>
|
||||
<button class="btn btn-xs" tooltip="Add custom palette" type="button" onclick="window.location.href=getURL('/cpal.htm')"><i class="icons btn-icon"></i></button>
|
||||
<button class="btn btn-xs" tooltip="Remove custom palette" type="button" id="rmPal" onclick="palettesData=null;localStorage.removeItem('wledPalx');requestJson({rmcpal:true});setTimeout(loadPalettes,250,loadPalettesData);"><i class="icons btn-icon"></i></button>
|
||||
<button class="btn btn-xs" title="Pixel Magic Tool" type="button" id="pxmb" onclick="window.location.href=getURL('/pxmagic.htm')"><i class="icons btn-icon"></i></button>
|
||||
<button class="btn btn-xs" title="Add custom palette" type="button" onclick="window.location.href=getURL('/cpal.htm')"><i class="icons btn-icon"></i></button>
|
||||
<button class="btn btn-xs" title="Remove custom palette" type="button" id="rmPal" onclick="palettesData=null;localStorage.removeItem('wledPalx');requestJson({rmcpal:true});setTimeout(loadPalettes,250,loadPalettesData);"><i class="icons btn-icon"></i></button>
|
||||
</div>
|
||||
<p class="labels" id="pall"><i class="icons sel-icon" onclick="tglHex()"></i> Color palette</p>
|
||||
<p class="labels hd" id="pall"><i class="icons sel-icon" onclick="tglHex()"></i> Color palette</p>
|
||||
<div id="palw" class="il">
|
||||
<div class="staytop fnd">
|
||||
<input type="text" placeholder="Search" oninput="search(this,'pallist')" onfocus="search(this,'pallist')" />
|
||||
@ -159,27 +159,27 @@
|
||||
<i class="icons clear-icon" onclick="clean(this);"></i>
|
||||
<i class="icons search-icon" style="cursor:pointer;"></i>
|
||||
<div id="filters" class="filter fade">
|
||||
<label id="filterPal" tooltip="Uses palette" class="check fchkl">🎨
|
||||
<label id="filterPal" title="Uses palette" class="check fchkl">🎨
|
||||
<input type="checkbox" data-flt="🎨" onchange="filterFx();">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label id="filter0D" tooltip="Single pixel" class="check fchkl">•
|
||||
<label id="filter0D" title="Single pixel" class="check fchkl">•
|
||||
<input type="checkbox" data-flt="•" onchange="filterFx();">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label id="filter1D" tooltip="1D" class="check fchkl">⋮
|
||||
<label id="filter1D" title="1D" class="check fchkl">⋮
|
||||
<input type="checkbox" data-flt="⋮" onchange="filterFx();">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label id="filter2D" tooltip="2D" class="check fchkl">▦
|
||||
<label id="filter2D" title="2D" class="check fchkl">▦
|
||||
<input type="checkbox" data-flt="▦" onchange="filterFx();">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label id="filterVol" tooltip="Volume" class="check fchkl">♪
|
||||
<label id="filterVol" title="Volume" class="check fchkl">♪
|
||||
<input type="checkbox" data-flt="♪" onchange="filterFx();">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label id="filterFreq" tooltip="Frequency" class="check fchkl">♫
|
||||
<label id="filterFreq" title="Frequency" class="check fchkl">♫
|
||||
<input type="checkbox" data-flt="♫" onchange="filterFx();">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
@ -199,16 +199,16 @@
|
||||
</div>
|
||||
<div id="sliders">
|
||||
<div id="slider0" class="slider">
|
||||
<i class="icons slider-icon" onclick="tglFreeze()"></i>
|
||||
<div tooltip="Effect speed" class="sliderwrap il">
|
||||
<i class="icons slider-icon" title="Freeze" onclick="tglFreeze()"></i>
|
||||
<div title="Effect speed" class="sliderwrap il">
|
||||
<input id="sliderSpeed" class="noslide" onchange="setSpeed()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
<output class="sliderbubble"></output>
|
||||
</div>
|
||||
<div id="slider1" class="slider">
|
||||
<i class="icons slider-icon" onclick="tglLabels()"></i>
|
||||
<div tooltip="Effect intensity" class="sliderwrap il">
|
||||
<i class="icons slider-icon" title="Toggle labels" onclick="tglLabels()"></i>
|
||||
<div title="Effect intensity" class="sliderwrap il">
|
||||
<input id="sliderIntensity" class="noslide" onchange="setIntensity()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
@ -216,7 +216,7 @@
|
||||
</div>
|
||||
<div id="slider2" class="slider hide">
|
||||
<i class="icons slider-icon"></i>
|
||||
<div tooltip="Custom 1" class="sliderwrap il">
|
||||
<div title="Custom 1" class="sliderwrap il">
|
||||
<input id="sliderC1" class="noslide" onchange="setCustom(1)" oninput="updateTrail(this)" max="255" min="0" type="range" value="0" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
@ -224,7 +224,7 @@
|
||||
</div>
|
||||
<div id="slider3" class="slider hide">
|
||||
<i class="icons slider-icon"></i>
|
||||
<div tooltip="Custom 2" class="sliderwrap il">
|
||||
<div title="Custom 2" class="sliderwrap il">
|
||||
<input id="sliderC2" class="noslide" onchange="setCustom(2)" oninput="updateTrail(this)" max="255" min="0" type="range" value="0" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
@ -232,22 +232,22 @@
|
||||
</div>
|
||||
<div id="slider4" class="slider hide">
|
||||
<i class="icons slider-icon"></i>
|
||||
<div tooltip="Custom 3" class="sliderwrap il">
|
||||
<div title="Custom 3" class="sliderwrap il">
|
||||
<input id="sliderC3" class="noslide" onchange="setCustom(3)" oninput="updateTrail(this)" max="31" min="0" type="range" value="0" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
<output class="sliderbubble"></output>
|
||||
</div>
|
||||
<div id="fxopt" class="option fade">
|
||||
<label id="opt0" tooltip="Check 1" class="check ochkl hide"><i class="icons"></i>
|
||||
<label id="opt0" title="Check 1" class="check ochkl hide"><i class="icons"></i>
|
||||
<input id="checkO1" type="checkbox" onchange="setOption(1, this.checked)">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label id="opt1" tooltip="Check 2" class="check ochkl hide"><i class="icons"></i>
|
||||
<label id="opt1" title="Check 2" class="check ochkl hide"><i class="icons"></i>
|
||||
<input id="checkO2" type="checkbox" onchange="setOption(2, this.checked)">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label id="opt2" tooltip="Check 3" class="check ochkl hide"><i class="icons"></i>
|
||||
<label id="opt2" title="Check 3" class="check ochkl hide"><i class="icons"></i>
|
||||
<input id="checkO3" type="checkbox" onchange="setOption(3, this.checked)">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
@ -256,6 +256,7 @@
|
||||
</div>
|
||||
|
||||
<div id="Segments" class="tabcontent">
|
||||
<p class="labels hd" id="segLabel">Segments</p>
|
||||
<div id="segcont">
|
||||
Loading...
|
||||
</div>
|
||||
|
@ -88,7 +88,6 @@ function setCSL(cs)
|
||||
function applyCfg()
|
||||
{
|
||||
cTheme(cfg.theme.base === "light");
|
||||
gId("Colors").style.paddingTop = cfg.comp.colors.picker ? "0" : "28px";
|
||||
var bg = cfg.theme.color.bg;
|
||||
if (bg) sCol('--c-1', bg);
|
||||
var l = cfg.comp.labels;
|
||||
@ -809,13 +808,13 @@ function populateSegments(s)
|
||||
`<span class="checkmark"></span>`+
|
||||
`</label>`+
|
||||
`<div class="segname ${smpl}" onclick="selSegEx(${i})">`+
|
||||
`<i class="icons e-icon frz" id="seg${i}frz" onclick="event.preventDefault();tglFreeze(${i});">&#x${inst.frz ? (li.live && li.liveseg==i?'e410':'e0e8') : 'e325'};</i>`+
|
||||
`<i class="icons e-icon frz" id="seg${i}frz" title="(un)Freeze" onclick="event.preventDefault();tglFreeze(${i});">&#x${inst.frz ? (li.live && li.liveseg==i?'e410':'e0e8') : 'e325'};</i>`+
|
||||
(inst.n ? inst.n : "Segment "+i) +
|
||||
`<div class="pop hide" onclick="event.preventDefault();event.stopPropagation();">`+
|
||||
`<i class="icons g-icon" style="color:${cG};" onclick="this.nextElementSibling.classList.toggle('hide');">ɸ${String.fromCharCode(inst.set+"A".charCodeAt(0))};</i>`+
|
||||
`<i class="icons g-icon" title="Set group" style="color:${cG};" onclick="this.nextElementSibling.classList.toggle('hide');">ɸ${String.fromCharCode(inst.set+"A".charCodeAt(0))};</i>`+
|
||||
`<div class="pop-c hide"><span style="color:var(--c-f);" onclick="setGrp(${i},0);">➊</span><span style="color:var(--c-r);" onclick="setGrp(${i},1);">➋</span><span style="color:var(--c-g);" onclick="setGrp(${i},2);">➌</span><span style="color:var(--c-l);" onclick="setGrp(${i},3);">➍</span></div>`+
|
||||
`</div> `+
|
||||
`<i class="icons edit-icon flr ${smpl}" id="seg${i}nedit" onclick="tglSegn(${i})"></i>`+
|
||||
`<i class="icons edit-icon flr ${smpl}" id="seg${i}nedit" title="Edit" onclick="tglSegn(${i})"></i>`+
|
||||
`</div>`+
|
||||
`<i class="icons e-icon flr ${smpl}" id="sege${i}" onclick="expand(${i})"></i>`+
|
||||
(cfg.comp.segpwr ? segp : '') +
|
||||
@ -846,7 +845,7 @@ function populateSegments(s)
|
||||
`<tr>`+
|
||||
`<td><input class="segn" id="seg${i}grp" type="number" min="1" max="255" value="${inst.grp}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>`+
|
||||
`<td><input class="segn" id="seg${i}spc" type="number" min="0" max="255" value="${inst.spc}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>`+
|
||||
`<td><button class="btn btn-xs" onclick="setSeg(${i})"><i class="icons btn-icon" id="segc${i}"></i></button></td>`+
|
||||
`<td><button class="btn btn-xs" title="Update" onclick="setSeg(${i})"><i class="icons btn-icon" id="segc${i}"></i></button></td>`+
|
||||
`</tr>`+
|
||||
`</table>`+
|
||||
`<div class="h bp" id="seg${i}len"></div>`+
|
||||
@ -898,6 +897,7 @@ function populateSegments(s)
|
||||
} else {
|
||||
gId("ledmap").classList.add('hide');
|
||||
}
|
||||
tooltip("#Segments");
|
||||
}
|
||||
|
||||
function populateEffects()
|
||||
@ -1088,7 +1088,7 @@ function populateNodes(i,n)
|
||||
for (var o of n.nodes) {
|
||||
if (o.name) {
|
||||
let onoff = `<i class="icons e-icon flr ${o.type&0x80?'':'off'}" onclick="rmtTgl('${o.ip}',this);""></i>`;
|
||||
var url = `<button class="btn" title="${o.ip}" onclick="location.assign('http://${o.ip}');"><div class="bname">${bname(o)}</div>${o.vid<2307130?'':onoff}</button>`;
|
||||
var url = `<a class="btn" title="${o.ip}" href="http://${o.ip}">${bname(o)}${o.vid<2307130?'':onoff}</a>`;
|
||||
urows += inforow(url,`${btype(o.type&0x7F)}<br><i>${o.vid==0?"N/A":o.vid}</i>`);
|
||||
nnodes++;
|
||||
}
|
||||
@ -1560,12 +1560,12 @@ function setEffectParameters(idx)
|
||||
// set html slider items on/off
|
||||
let sliders = d.querySelectorAll("#sliders .sliderwrap");
|
||||
sliders.forEach((slider, i)=>{
|
||||
let text = slider.getAttribute("tooltip");
|
||||
let text = slider.getAttribute("title");
|
||||
if ((!controlDefined && i<((idx<128)?2:nSliders)) || (slOnOff.length>i && slOnOff[i]!="")) {
|
||||
if (slOnOff.length>i && slOnOff[i]!="!") text = slOnOff[i];
|
||||
// restore overwritten default tooltips
|
||||
if (i<2 && slOnOff[i]==="!") text = i==0 ? "Effect speed" : "Effect intensity";
|
||||
slider.setAttribute("tooltip", text);
|
||||
slider.setAttribute("title", text);
|
||||
slider.parentElement.classList.remove('hide');
|
||||
} else
|
||||
slider.parentElement.classList.add('hide');
|
||||
@ -1575,10 +1575,10 @@ function setEffectParameters(idx)
|
||||
gId('fxopt').classList.remove('fade');
|
||||
let checks = d.querySelectorAll("#sliders .ochkl");
|
||||
checks.forEach((check, i)=>{
|
||||
let text = check.getAttribute("tooltip");
|
||||
let text = check.getAttribute("title");
|
||||
if (5+i<slOnOff.length && slOnOff[5+i]!=='') {
|
||||
if (slOnOff.length>5+i && slOnOff[5+i]!="!") text = slOnOff[5+i];
|
||||
check.setAttribute("tooltip", text);
|
||||
check.setAttribute("title", text);
|
||||
check.classList.remove('hide');
|
||||
} else
|
||||
check.classList.add('hide');
|
||||
@ -1876,7 +1876,7 @@ function resetUtil(off=false)
|
||||
+ '<label class="check schkl"><input type="checkbox" id="selall" onchange="selSegAll(this)"><span class="checkmark"></span></label>'
|
||||
+ `<div class="segname" ${off?'':'onclick="makeSeg()"'}><i class="icons btn-icon"></i>Add segment</div>`
|
||||
+ '<div class="pop hide" onclick="event.stopPropagation();">'
|
||||
+ `<i class="icons g-icon" onclick="this.nextElementSibling.classList.toggle('hide');"></i>`
|
||||
+ `<i class="icons g-icon" title="Select group" onclick="this.nextElementSibling.classList.toggle('hide');"></i>`
|
||||
+ '<div class="pop-c hide"><span style="color:var(--c-f);" onclick="selGrp(0);">➊</span><span style="color:var(--c-r);" onclick="selGrp(1);">➋</span><span style="color:var(--c-g);" onclick="selGrp(2);">➌</span><span style="color:var(--c-l);" onclick="selGrp(3);">➍</span></div>'
|
||||
+ '</div></div>';
|
||||
}
|
||||
@ -2514,8 +2514,8 @@ function selectSlot(b)
|
||||
{
|
||||
csel = b;
|
||||
var cd = gId('csl').children;
|
||||
for (let i of cd) i.classList.remove('xxs-w');
|
||||
cd[b].classList.add('xxs-w');
|
||||
for (let i of cd) i.classList.remove('sl');
|
||||
cd[b].classList.add('sl');
|
||||
setPicker(rgbStr(cd[b].dataset));
|
||||
// force slider update on initial load (picker "color:change" not fired if black)
|
||||
if (cpick.color.value == 0) updatePSliders();
|
||||
@ -2802,6 +2802,7 @@ function search(field, listId = null) {
|
||||
if (!listId) return;
|
||||
|
||||
const search = field.value !== '';
|
||||
const presets = listId === 'pcont';
|
||||
|
||||
// clear filter if searching in fxlist
|
||||
if (listId === 'fxlist' && search) {
|
||||
@ -2813,7 +2814,7 @@ function search(field, listId = null) {
|
||||
|
||||
const listItems = gId(listId).querySelectorAll('.lstI');
|
||||
// filter list items but leave (Default & Solid) always visible
|
||||
for (i = (listId === 'pcont' ? 0 : 1); i < listItems.length; i++) {
|
||||
for (i = (presets ? 0 : 1); i < listItems.length; i++) {
|
||||
const listItem = listItems[i];
|
||||
const listItemName = listItem.querySelector('.lstIname').innerText.toUpperCase();
|
||||
const searchIndex = listItemName.indexOf(field.value.toUpperCase());
|
||||
@ -2821,6 +2822,7 @@ function search(field, listId = null) {
|
||||
listItem.dataset.searchIndex = searchIndex;
|
||||
}
|
||||
|
||||
if (!presets) {
|
||||
// sort list items by search index and name
|
||||
const sortedListItems = Array.from(listItems).sort((a, b) => {
|
||||
const aSearchIndex = parseInt(a.dataset.searchIndex);
|
||||
@ -2845,6 +2847,7 @@ function search(field, listId = null) {
|
||||
firstVisibleItem.scrollIntoView({ behavior: "instant", block: "center" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clean(clearButton) {
|
||||
clearButton.style.display = 'none';
|
||||
@ -3071,14 +3074,19 @@ function mergeDeep(target, ...sources)
|
||||
return mergeDeep(target, ...sources);
|
||||
}
|
||||
|
||||
function tooltip()
|
||||
function tooltip(cont=null)
|
||||
{
|
||||
const elements = d.querySelectorAll("[tooltip]");
|
||||
const elements = d.querySelectorAll((cont?cont+" ":"")+"[title]");
|
||||
elements.forEach((element)=>{
|
||||
element.addEventListener("mouseover", ()=>{
|
||||
// save title
|
||||
element.setAttribute("data-title", element.getAttribute("title"));
|
||||
const tooltip = d.createElement("span");
|
||||
tooltip.className = "tooltip";
|
||||
tooltip.textContent = element.getAttribute("tooltip");
|
||||
tooltip.textContent = element.getAttribute("title");
|
||||
|
||||
// prevent default title popup
|
||||
element.removeAttribute("title");
|
||||
|
||||
let { top, left, width } = element.getBoundingClientRect();
|
||||
|
||||
@ -3101,6 +3109,8 @@ function tooltip()
|
||||
tooltip.classList.remove("visible");
|
||||
d.body.removeChild(tooltip);
|
||||
});
|
||||
// restore title
|
||||
element.setAttribute("title", element.getAttribute("data-title"));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -394,6 +394,7 @@ ${i+1}:
|
||||
<option value="24">400kHz</option>\
|
||||
<option value="25">TM1829</option>\
|
||||
<option value="26">UCS8903</option>\
|
||||
<option value="27">APA106/PL9823</option>\
|
||||
<option value="29">UCS8904 RGBW</option>\
|
||||
<option value="50">WS2801</option>\
|
||||
<option value="51">APA102</option>\
|
||||
|
@ -212,6 +212,7 @@
|
||||
12h LED: <input name="OM" type="number" min="0" max="255" required><br>
|
||||
Show 5min marks: <input type="checkbox" name="O5"><br>
|
||||
Seconds (as trail): <input type="checkbox" name="OS"><br>
|
||||
Show clock overlay only if all LEDs are solid black: <input type="checkbox" name="OB"><br>
|
||||
</div>
|
||||
Countdown Mode: <input type="checkbox" name="CE"><br>
|
||||
Countdown Goal:<br>
|
||||
|
@ -227,10 +227,10 @@
|
||||
} else if (typeof(fld) === "number") sel.classList.add("pin"); // a hack to add a class
|
||||
let arr = d.getElementsByName(um);
|
||||
let idx = arr[0].type==="hidden"?1:0; // ignore hidden field
|
||||
if (arr.length > 2) {
|
||||
if (arr.length > 1+idx) {
|
||||
// we have array of values (usually pins)
|
||||
for (let i of arr) {
|
||||
if (i.type === "number") break;
|
||||
if (i.nodeName === "INPUT" && i.type === "number") break;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
var d = document;
|
||||
var loc = false, locip, locproto = "http:";
|
||||
var scanLoops = 0, preScanSSID = "";
|
||||
|
||||
var maxNetworks = 3;
|
||||
function gId(e) { return d.getElementById(e); }
|
||||
function cE(e) { return d.createElement(e); }
|
||||
function toggle(el){gId(el).classList.toggle("hide"); gId('No'+el).classList.toggle("hide");}
|
||||
@ -52,13 +52,14 @@
|
||||
}
|
||||
scanLoops = 0;
|
||||
|
||||
let cs = gId("CS");
|
||||
if (cs) {
|
||||
let cs = d.querySelectorAll("#wifi_entries input[type=text]");
|
||||
for (let input of (cs||[])) {
|
||||
let found = false;
|
||||
let select = cE("select");
|
||||
select.setAttribute("id", "CS");
|
||||
select.setAttribute("name", "CS");
|
||||
select.setAttribute("onchange", "T()");
|
||||
preScanSSID = cs.value;
|
||||
select.id = input.id;
|
||||
select.name = input.name;
|
||||
select.setAttribute("onchange", "T(this)");
|
||||
preScanSSID = input.value;
|
||||
|
||||
for (let i = 0; i < select.children.length; i++) {
|
||||
select.removeChild(select.children[i]);
|
||||
@ -70,8 +71,9 @@
|
||||
option.setAttribute("value", networks[i].ssid);
|
||||
option.textContent = `${networks[i].ssid} (${networks[i].rssi} dBm)`;
|
||||
|
||||
if (networks[i].ssid === cs.value) {
|
||||
if (networks[i].ssid === input.value) {
|
||||
option.setAttribute("selected", "selected");
|
||||
found = true;
|
||||
}
|
||||
|
||||
select.appendChild(option);
|
||||
@ -79,10 +81,11 @@
|
||||
const option = cE("option");
|
||||
|
||||
option.setAttribute("value", "!Cs");
|
||||
option.textContent = `Other network...`;
|
||||
option.textContent = "Other network...";
|
||||
select.appendChild(option);
|
||||
|
||||
cs.replaceWith(select);
|
||||
if (input.value === "" || found) input.replaceWith(select);
|
||||
else select.remove();
|
||||
}
|
||||
|
||||
button.disabled = false;
|
||||
@ -90,17 +93,48 @@
|
||||
});
|
||||
}
|
||||
// replace WiFi select with custom SSID input field again
|
||||
function T() {
|
||||
let cs = gId("CS");
|
||||
function T(cs) {
|
||||
if (!cs || cs.value != "!Cs") return;
|
||||
let input = cE("input");
|
||||
input.type = "text";
|
||||
input.id = "CS";
|
||||
input.name ="CS";
|
||||
input.id = cs.id;
|
||||
input.name = cs.name;
|
||||
input.setAttribute("maxlength",32);
|
||||
input.value = preScanSSID;
|
||||
cs.replaceWith(input);
|
||||
}
|
||||
function resetWiFi(maxN = undefined) {
|
||||
if (maxN) maxNetworks = maxN;
|
||||
let entries = gId("wifi_entries").children
|
||||
for (let i = entries.length; i > 0; i--) entries[i-1].remove();
|
||||
btnWiFi(0);
|
||||
}
|
||||
function btnWiFi(i) {
|
||||
gId("wifi_add").style.display = (i<maxNetworks) ? "inline":"none";
|
||||
gId("wifi_rem").style.display = (i>1) ? "inline":"none";
|
||||
}
|
||||
function addWiFi(ssid="",pass="",ip=0,gw=0,sn=0x00ffffff) { // little endian
|
||||
var i = gId("wifi_entries").childNodes.length;
|
||||
if (i >= maxNetworks) return;
|
||||
var b = `<div id="net${i}"><hr class="sml">
|
||||
Network name (SSID${i==0?", empty to not connect":""}):<br><input type="text" id="CS${i}" name="CS${i}" maxlength="32" value="${ssid}" ${i>0?"required":""}><br>
|
||||
Network password:<br><input type="password" name="PW${i}" maxlength="64" value="${pass}"><br>
|
||||
Static IP (leave at 0.0.0.0 for DHCP)${i==0?"<br>Also used by Ethernet":""}:<br>
|
||||
<input name="IP${i}0" type="number" class="s" min="0" max="255" value="${ip&0xFF}" required>.<input name="IP${i}1" type="number" class="s" min="0" max="255" value="${(ip>>8)&0xFF}" required>.<input name="IP${i}2" type="number" class="s" min="0" max="255" value="${(ip>>16)&0xFF}" required>.<input name="IP${i}3" type="number" class="s" min="0" max="255" value="${(ip>>24)&0xFF}" required><br>
|
||||
Static gateway:<br>
|
||||
<input name="GW${i}0" type="number" class="s" min="0" max="255" value="${gw&0xFF}" required>.<input name="GW${i}1" type="number" class="s" min="0" max="255" value="${(gw>>8)&0xFF}" required>.<input name="GW${i}2" type="number" class="s" min="0" max="255" value="${(gw>>16)&0xFF}" required>.<input name="GW${i}3" type="number" class="s" min="0" max="255" value="${(gw>>24)&0xFF}" required><br>
|
||||
Static subnet mask:<br>
|
||||
<input name="SN${i}0" type="number" class="s" min="0" max="255" value="${sn&0xFF}" required>.<input name="SN${i}1" type="number" class="s" min="0" max="255" value="${(sn>>8)&0xFF}" required>.<input name="SN${i}2" type="number" class="s" min="0" max="255" value="${(sn>>16)&0xFF}" required>.<input name="SN${i}3" type="number" class="s" min="0" max="255" value="${(sn>>24)&0xFF}" required></div>`;
|
||||
gId("wifi_entries").insertAdjacentHTML("beforeend", b);
|
||||
btnWiFi(i+1);
|
||||
}
|
||||
function remWiFi() {
|
||||
const entries = gId("wifi_entries").children;
|
||||
const i = entries.length;
|
||||
if (i < 2) return;
|
||||
entries[i-1].remove();
|
||||
btnWiFi(i-1);
|
||||
}
|
||||
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
|
||||
function loadJS(FILE_URL, async = true) {
|
||||
let scE = cE("script");
|
||||
@ -158,24 +192,16 @@
|
||||
<h2>WiFi setup</h2>
|
||||
<h3>Connect to existing network</h3>
|
||||
<button type="button" id="scan" onclick="N()">Scan</button><br>
|
||||
Network name (SSID, empty to not connect):<br>
|
||||
<input type="text" id="CS" name="CS" maxlength="32"><br>
|
||||
Network password: <br> <input type="password" name="CP" maxlength="63"><br>
|
||||
Static IP (leave at 0.0.0.0 for DHCP):<br>
|
||||
<input name="I0" type="number" class="s" min="0" max="255" required> .
|
||||
<input name="I1" type="number" class="s" min="0" max="255" required> .
|
||||
<input name="I2" type="number" class="s" min="0" max="255" required> .
|
||||
<input name="I3" type="number" class="s" min="0" max="255" required><br>
|
||||
Static gateway:<br>
|
||||
<input name="G0" type="number" class="s" min="0" max="255" required> .
|
||||
<input name="G1" type="number" class="s" min="0" max="255" required> .
|
||||
<input name="G2" type="number" class="s" min="0" max="255" required> .
|
||||
<input name="G3" type="number" class="s" min="0" max="255" required><br>
|
||||
Static subnet mask:<br>
|
||||
<input name="S0" type="number" class="s" min="0" max="255" required> .
|
||||
<input name="S1" type="number" class="s" min="0" max="255" required> .
|
||||
<input name="S2" type="number" class="s" min="0" max="255" required> .
|
||||
<input name="S3" type="number" class="s" min="0" max="255" required><br>
|
||||
<div id="wifi">
|
||||
Wireless networks
|
||||
<div id="wifi_entries"></div>
|
||||
<hr class="sml">
|
||||
<button type="button" id="wifi_add" onclick="addWiFi()">+</button>
|
||||
<button type="button" id="wifi_rem" onclick="remWiFi()">-</button><br>
|
||||
</div>
|
||||
DNS server address:<br>
|
||||
<input name="D0" type="number" class="s" min="0" max="255" required>.<input name="D1" type="number" class="s" min="0" max="255" required>.<input name="D2" type="number" class="s" min="0" max="255" required>.<input name="D3" type="number" class="s" min="0" max="255" required><br>
|
||||
<br>
|
||||
mDNS address (leave empty for no mDNS):<br>
|
||||
http:// <input type="text" name="CM" maxlength="32"> .local<br>
|
||||
Client IP: <span class="sip"> Not connected </span> <br>
|
||||
@ -189,7 +215,9 @@
|
||||
<option value="0">No connection after boot</option>
|
||||
<option value="1">Disconnected</option>
|
||||
<option value="2">Always</option>
|
||||
<option value="3">Never (not recommended)</option></select><br>
|
||||
<option value="3">Never (not recommended)</option>
|
||||
<option value="4">Temporary (no connection after boot)</option>
|
||||
</select><br>
|
||||
AP IP: <span class="sip"> Not active </span><br>
|
||||
<h3>Experimental</h3>
|
||||
Force 802.11g mode (ESP8266 only): <input type="checkbox" name="FG"><br>
|
||||
@ -215,6 +243,7 @@
|
||||
<option value="0">None</option>
|
||||
<option value="9">ABC! WLED V43 & compatible</option>
|
||||
<option value="2">ESP32-POE</option>
|
||||
<option value="11">ESP32-POE-WROVER</option>
|
||||
<option value="6">ESP32Deux</option>
|
||||
<option value="7">KIT-VE</option>
|
||||
<option value="8">QuinLED-Dig-Octa & T-ETH-POE</option>
|
||||
|
@ -81,6 +81,7 @@ input:disabled {
|
||||
}
|
||||
input[type="text"],
|
||||
input[type="number"],
|
||||
input[type="password"],
|
||||
select {
|
||||
font-size: medium;
|
||||
margin: 2px;
|
||||
|
@ -490,7 +490,7 @@ void prepareArtnetPollReply(ArtPollReply *reply) {
|
||||
// Node is DHCP capable
|
||||
// Node supports 15 bit Port-Address (Art-Net 3 or 4)
|
||||
// Node is able to switch between ArtNet and sACN
|
||||
reply->reply_status_2 = (staticIP[0] == 0) ? 0x1F : 0x1D;
|
||||
reply->reply_status_2 = (multiWiFi[0].staticIP[0] == 0) ? 0x1F : 0x1D;
|
||||
|
||||
// RDM is disabled
|
||||
// Output style is continuous
|
||||
|
@ -48,6 +48,21 @@ bool getJsonValue(const JsonVariant& element, DestType& destination, const Defau
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef struct WiFiConfig {
|
||||
char clientSSID[33];
|
||||
char clientPass[65];
|
||||
IPAddress staticIP;
|
||||
IPAddress staticGW;
|
||||
IPAddress staticSN;
|
||||
WiFiConfig(const char *ssid="", const char *pass="", uint32_t ip=0, uint32_t gw=0, uint32_t subnet=0x00FFFFFF) // little endian
|
||||
: staticIP(ip)
|
||||
, staticGW(gw)
|
||||
, staticSN(subnet)
|
||||
{
|
||||
strncpy(clientSSID, ssid, 32); clientSSID[32] = 0;
|
||||
strncpy(clientPass, pass, 64); clientPass[64] = 0;
|
||||
}
|
||||
} wifi_config;
|
||||
|
||||
//colors.cpp
|
||||
// similar to NeoPixelBus NeoGammaTableMethod but allows dynamic changes (superseded by NPB::NeoGammaDynamicTableMethod)
|
||||
|
@ -259,14 +259,14 @@ void parseWiFiCommand(char* rpcData) {
|
||||
|
||||
uint8_t ssidLen = rpcData[1];
|
||||
if (ssidLen > len -1 || ssidLen > 32) return;
|
||||
memset(clientSSID, 0, 32);
|
||||
memcpy(clientSSID, rpcData+2, ssidLen);
|
||||
memset(multiWiFi[0].clientSSID, 0, 32);
|
||||
memcpy(multiWiFi[0].clientSSID, rpcData+2, ssidLen);
|
||||
|
||||
memset(clientPass, 0, 64);
|
||||
memset(multiWiFi[0].clientPass, 0, 64);
|
||||
if (len > ssidLen +1) {
|
||||
uint8_t passLen = rpcData[2+ssidLen];
|
||||
memset(clientPass, 0, 64);
|
||||
memcpy(clientPass, rpcData+3+ssidLen, passLen);
|
||||
memset(multiWiFi[0].clientPass, 0, 64);
|
||||
memcpy(multiWiFi[0].clientPass, rpcData+3+ssidLen, passLen);
|
||||
}
|
||||
|
||||
sendImprovStateResponse(0x03); //provisioning
|
||||
|
@ -470,6 +470,19 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
}
|
||||
}
|
||||
|
||||
JsonObject wifi = root[F("wifi")];
|
||||
if (!wifi.isNull()) {
|
||||
bool apMode = getBoolVal(wifi[F("ap")], apActive);
|
||||
if (!apActive && apMode) WLED::instance().initAP(); // start AP mode immediately
|
||||
else if (apActive && !apMode) { // stop AP mode immediately
|
||||
dnsServer.stop();
|
||||
WiFi.softAPdisconnect(true);
|
||||
apActive = false;
|
||||
}
|
||||
//bool restart = wifi[F("restart")] | false;
|
||||
//if (restart) forceReconnect = true;
|
||||
}
|
||||
|
||||
stateUpdated(callMode);
|
||||
if (presetToRestore) currentPreset = presetToRestore;
|
||||
|
||||
|
@ -123,6 +123,16 @@ const ethernet_settings ethernetBoards[] = {
|
||||
18, // eth_mdio,
|
||||
ETH_PHY_LAN8720, // eth_type,
|
||||
ETH_CLOCK_GPIO17_OUT // eth_clk_mode
|
||||
},
|
||||
|
||||
// ESP32-POE-WROVER
|
||||
{
|
||||
0, // eth_address,
|
||||
12, // eth_power,
|
||||
23, // eth_mdc,
|
||||
18, // eth_mdio,
|
||||
ETH_PHY_LAN8720, // eth_type,
|
||||
ETH_CLOCK_GPIO0_OUT // eth_clk_mode
|
||||
}
|
||||
};
|
||||
#endif
|
||||
@ -163,8 +173,8 @@ void WiFiEvent(WiFiEvent_t event)
|
||||
if (!apActive) {
|
||||
WiFi.disconnect(true);
|
||||
}
|
||||
if (staticIP != (uint32_t)0x00000000 && staticGateway != (uint32_t)0x00000000) {
|
||||
ETH.config(staticIP, staticGateway, staticSubnet, IPAddress(8, 8, 8, 8));
|
||||
if (multiWiFi[0].staticIP != (uint32_t)0x00000000 && multiWiFi[0].staticGW != (uint32_t)0x00000000) {
|
||||
ETH.config(multiWiFi[0].staticIP, multiWiFi[0].staticGW, multiWiFi[0].staticSN, dnsAddress);
|
||||
} else {
|
||||
ETH.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);
|
||||
}
|
||||
|
@ -89,6 +89,16 @@ void _overlayAnalogCountdown()
|
||||
|
||||
void handleOverlayDraw() {
|
||||
usermods.handleOverlayDraw();
|
||||
if (analogClockSolidBlack) {
|
||||
const Segment* segments = strip.getSegments();
|
||||
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
const Segment& segment = segments[i];
|
||||
if (!segment.isActive()) continue;
|
||||
if (segment.mode > 0 || segment.colors[0] > 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (overlayCurrent == 1) _overlayAnalogClock();
|
||||
}
|
||||
|
||||
|
@ -127,7 +127,7 @@ static bool remoteJson(int button)
|
||||
JsonObject fdo = pDoc->as<JsonObject>();
|
||||
if (fdo.isNull()) {
|
||||
// the received button does not exist
|
||||
if (!WLED_FS.exists("/remote.json")) errorFlag = ERR_FS_RMLOAD; //warn if file itself doesn't exist
|
||||
//if (!WLED_FS.exists("/remote.json")) errorFlag = ERR_FS_RMLOAD; //warn if file itself doesn't exist
|
||||
releaseJSONBufferLock();
|
||||
return parsed;
|
||||
}
|
||||
|
@ -19,21 +19,52 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
//WIFI SETTINGS
|
||||
if (subPage == SUBPAGE_WIFI)
|
||||
{
|
||||
char oldSSID[sizeof(clientSSID)];
|
||||
unsigned cnt = 0;
|
||||
for (size_t n = 0; n < WLED_MAX_WIFI_COUNT; n++) {
|
||||
char cs[4] = "CS"; cs[2] = 48+n; cs[3] = 0; //client SSID
|
||||
char pw[4] = "PW"; pw[2] = 48+n; pw[3] = 0; //client password
|
||||
char ip[5] = "IP"; ip[2] = 48+n; ip[4] = 0; //IP address
|
||||
char gw[5] = "GW"; gw[2] = 48+n; gw[4] = 0; //GW address
|
||||
char sn[5] = "SN"; sn[2] = 48+n; sn[4] = 0; //subnet mask
|
||||
if (request->hasArg(cs)) {
|
||||
if (n >= multiWiFi.size()) multiWiFi.push_back(WiFiConfig()); // expand vector by one
|
||||
char oldSSID[33]; strcpy(oldSSID, multiWiFi[n].clientSSID);
|
||||
char oldPass[65]; strcpy(oldPass, multiWiFi[n].clientPass);
|
||||
|
||||
strcpy(oldSSID, clientSSID);
|
||||
strlcpy(clientSSID,request->arg(F("CS")).c_str(), 33);
|
||||
if (!strcmp(oldSSID, clientSSID)) forceReconnect = true;
|
||||
|
||||
if (!isAsterisksOnly(request->arg(F("CP")).c_str(), 65)) {
|
||||
strlcpy(clientPass, request->arg(F("CP")).c_str(), 65);
|
||||
strlcpy(multiWiFi[n].clientSSID, request->arg(cs).c_str(), 33);
|
||||
if (strlen(oldSSID) == 0 || !strncmp(multiWiFi[n].clientSSID, oldSSID, 32)) {
|
||||
forceReconnect = true;
|
||||
}
|
||||
if (!isAsterisksOnly(request->arg(pw).c_str(), 65)) {
|
||||
strlcpy(multiWiFi[n].clientPass, request->arg(pw).c_str(), 65);
|
||||
forceReconnect = true;
|
||||
}
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
ip[3] = 48+i;
|
||||
gw[3] = 48+i;
|
||||
sn[3] = 48+i;
|
||||
multiWiFi[n].staticIP[i] = request->arg(ip).toInt();
|
||||
multiWiFi[n].staticGW[i] = request->arg(gw).toInt();
|
||||
multiWiFi[n].staticSN[i] = request->arg(sn).toInt();
|
||||
}
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
// remove unused
|
||||
if (cnt < multiWiFi.size()) {
|
||||
cnt = multiWiFi.size() - cnt;
|
||||
while (cnt--) multiWiFi.pop_back();
|
||||
multiWiFi.shrink_to_fit(); // release memory
|
||||
}
|
||||
|
||||
if (request->hasArg(F("D0"))) {
|
||||
dnsAddress = IPAddress(request->arg(F("D0")).toInt(),request->arg(F("D1")).toInt(),request->arg(F("D2")).toInt(),request->arg(F("D3")).toInt());
|
||||
}
|
||||
|
||||
strlcpy(cmDNS, request->arg(F("CM")).c_str(), 33);
|
||||
|
||||
apBehavior = request->arg(F("AB")).toInt();
|
||||
strcpy(oldSSID, apSSID);
|
||||
char oldSSID[33]; strcpy(oldSSID, apSSID);
|
||||
strlcpy(apSSID, request->arg(F("AS")).c_str(), 33);
|
||||
if (!strcmp(oldSSID, apSSID) && apActive) forceReconnect = true;
|
||||
apHide = request->hasArg(F("AH"));
|
||||
@ -61,21 +92,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
ethernetType = request->arg(F("ETH")).toInt();
|
||||
WLED::instance().initEthernet();
|
||||
#endif
|
||||
|
||||
char k[3]; k[2] = 0;
|
||||
for (int i = 0; i<4; i++)
|
||||
{
|
||||
k[1] = i+48;//ascii 0,1,2,3
|
||||
|
||||
k[0] = 'I'; //static IP
|
||||
staticIP[i] = request->arg(k).toInt();
|
||||
|
||||
k[0] = 'G'; //gateway
|
||||
staticGateway[i] = request->arg(k).toInt();
|
||||
|
||||
k[0] = 'S'; //subnet
|
||||
staticSubnet[i] = request->arg(k).toInt();
|
||||
}
|
||||
}
|
||||
|
||||
//LED SETTINGS
|
||||
@ -440,6 +456,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
analogClock12pixel = request->arg(F("OM")).toInt();
|
||||
analogClock5MinuteMarks = request->hasArg(F("O5"));
|
||||
analogClockSecondsTrail = request->hasArg(F("OS"));
|
||||
analogClockSolidBlack = request->hasArg(F("OB"));
|
||||
|
||||
countdownMode = request->hasArg(F("CE"));
|
||||
countdownYear = request->arg(F("CY")).toInt();
|
||||
|
@ -44,10 +44,6 @@
|
||||
#include "../usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h"
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_MODE_SORT
|
||||
#include "../usermods/usermod_v2_mode_sort/usermod_v2_mode_sort.h"
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_BH1750
|
||||
#include "../usermods/BH1750_v2/usermod_BH1750.h"
|
||||
#endif
|
||||
@ -58,19 +54,11 @@
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
#ifdef USE_ALT_DISPlAY
|
||||
#include "../usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h"
|
||||
#else
|
||||
#include "../usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_ROTARY_ENCODER_UI
|
||||
#ifdef USE_ALT_DISPlAY
|
||||
#include "../usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h"
|
||||
#else
|
||||
#include "../usermods/usermod_v2_rotary_encoder_ui/usermod_v2_rotary_encoder_ui.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_AUTO_SAVE
|
||||
@ -254,10 +242,6 @@ void registerUsermods()
|
||||
usermods.add(new PIRsensorSwitch());
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_MODE_SORT
|
||||
usermods.add(new ModeSortUsermod());
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
usermods.add(new FourLineDisplayUsermod());
|
||||
#endif
|
||||
|
143
wled00/wled.cpp
143
wled00/wled.cpp
@ -34,6 +34,8 @@ void WLED::reset()
|
||||
|
||||
void WLED::loop()
|
||||
{
|
||||
static uint32_t lastHeap = UINT32_MAX;
|
||||
static unsigned long heapTime = 0;
|
||||
#ifdef WLED_DEBUG
|
||||
static unsigned long lastRun = 0;
|
||||
unsigned long loopMillis = millis();
|
||||
@ -151,6 +153,21 @@ void WLED::loop()
|
||||
createEditHandler(false);
|
||||
}
|
||||
|
||||
// reconnect WiFi to clear stale allocations if heap gets too low
|
||||
if (millis() - heapTime > 15000) {
|
||||
uint32_t heap = ESP.getFreeHeap();
|
||||
if (heap < MIN_HEAP_SIZE && lastHeap < MIN_HEAP_SIZE) {
|
||||
DEBUG_PRINT(F("Heap too low! ")); DEBUG_PRINTLN(heap);
|
||||
forceReconnect = true;
|
||||
strip.resetSegments(); // remove all but one segments from memory
|
||||
} else if (heap < MIN_HEAP_SIZE) {
|
||||
DEBUG_PRINTLN(F("Heap low, purging segments."));
|
||||
strip.purgeSegments();
|
||||
}
|
||||
lastHeap = heap;
|
||||
heapTime = millis();
|
||||
}
|
||||
|
||||
//LED settings have been saved, re-init busses
|
||||
//This code block causes severe FPS drop on ESP32 with the original "if (busConfigs[0] != nullptr)" conditional. Investigate!
|
||||
if (doInitBusses) {
|
||||
@ -424,6 +441,7 @@ void WLED::setup()
|
||||
escapedMac.toLowerCase();
|
||||
|
||||
WLED_SET_AP_SSID(); // otherwise it is empty on first boot until config is saved
|
||||
multiWiFi.push_back(WiFiConfig(CLIENT_SSID,CLIENT_PASS)); // initialise vector with default WiFi
|
||||
|
||||
DEBUG_PRINTLN(F("Reading config"));
|
||||
deserializeConfigFromFS();
|
||||
@ -445,13 +463,16 @@ void WLED::setup()
|
||||
usermods.setup();
|
||||
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
|
||||
|
||||
if (strcmp(clientSSID, DEFAULT_CLIENT_SSID) == 0)
|
||||
if (strcmp(multiWiFi[0].clientSSID, DEFAULT_CLIENT_SSID) == 0)
|
||||
showWelcomePage = true;
|
||||
WiFi.persistent(false);
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
WiFi.onEvent(WiFiEvent);
|
||||
#endif
|
||||
|
||||
WiFi.mode(WIFI_STA); // enable scanning
|
||||
findWiFi(true); // start scanning for available WiFi-s
|
||||
|
||||
#ifdef WLED_ENABLE_ADALIGHT
|
||||
//Serial RX (Adalight, Improv, Serial JSON) only possible if GPIO3 unused
|
||||
//Serial TX (Debug, Improv, Serial JSON) only possible if GPIO1 unused
|
||||
@ -697,11 +718,52 @@ bool WLED::initEthernet()
|
||||
#else
|
||||
return false; // Ethernet not enabled for build
|
||||
#endif
|
||||
}
|
||||
|
||||
// performs asynchronous scan for available networks (which may take couple of seconds to finish)
|
||||
// returns configured WiFi ID with the strongest signal (or default if no configured networks available)
|
||||
int8_t WLED::findWiFi(bool doScan) {
|
||||
if (multiWiFi.size() <= 1) {
|
||||
DEBUG_PRINTLN(F("Defaulf WiFi used."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (doScan) WiFi.scanDelete(); // restart scan
|
||||
|
||||
int status = WiFi.scanComplete(); // complete scan may take as much as several seconds (usually <3s with not very crowded air)
|
||||
|
||||
if (status == WIFI_SCAN_FAILED) {
|
||||
DEBUG_PRINTLN(F("WiFi scan started."));
|
||||
WiFi.scanNetworks(true); // start scanning in asynchronous mode
|
||||
} else if (status >= 0) { // status contains number of found networks
|
||||
DEBUG_PRINT(F("WiFi scan completed: ")); DEBUG_PRINTLN(status);
|
||||
int rssi = -9999;
|
||||
int selected = selectedWiFi;
|
||||
for (int o = 0; o < status; o++) {
|
||||
DEBUG_PRINT(F(" WiFi available: ")); DEBUG_PRINT(WiFi.SSID(o));
|
||||
DEBUG_PRINT(F(" RSSI: ")); DEBUG_PRINT(WiFi.RSSI(o)); DEBUG_PRINTLN(F("dB"));
|
||||
for (unsigned n = 0; n < multiWiFi.size(); n++)
|
||||
if (!strcmp(WiFi.SSID(o).c_str(), multiWiFi[n].clientSSID)) {
|
||||
// find the WiFi with the strongest signal (but keep priority of entry if signal difference is not big)
|
||||
if ((n < selected && WiFi.RSSI(o) > rssi-10) || WiFi.RSSI(o) > rssi) {
|
||||
rssi = WiFi.RSSI(o);
|
||||
selected = n;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
DEBUG_PRINT(F("Selected: ")); DEBUG_PRINT(multiWiFi[selected].clientSSID);
|
||||
DEBUG_PRINT(F(" RSSI: ")); DEBUG_PRINT(rssi); DEBUG_PRINTLN(F("dB"));
|
||||
return selected;
|
||||
}
|
||||
//DEBUG_PRINT(F("WiFi scan running."));
|
||||
return status; // scan is still running or there was an error
|
||||
}
|
||||
|
||||
void WLED::initConnection()
|
||||
{
|
||||
DEBUG_PRINTLN(F("initConnection() called."));
|
||||
|
||||
#ifdef WLED_ENABLE_WEBSOCKETS
|
||||
ws.onEvent(wsEvent);
|
||||
#endif
|
||||
@ -719,8 +781,8 @@ void WLED::initConnection()
|
||||
WiFi.setPhyMode(force802_3g ? WIFI_PHY_MODE_11G : WIFI_PHY_MODE_11N);
|
||||
#endif
|
||||
|
||||
if (staticIP[0] != 0 && staticGateway[0] != 0) {
|
||||
WiFi.config(staticIP, staticGateway, staticSubnet, IPAddress(1, 1, 1, 1));
|
||||
if (multiWiFi[selectedWiFi].staticIP != 0U && multiWiFi[selectedWiFi].staticGW != 0U) {
|
||||
WiFi.config(multiWiFi[selectedWiFi].staticIP, multiWiFi[selectedWiFi].staticGW, multiWiFi[selectedWiFi].staticSN, dnsAddress);
|
||||
} else {
|
||||
WiFi.config(IPAddress((uint32_t)0), IPAddress((uint32_t)0), IPAddress((uint32_t)0));
|
||||
}
|
||||
@ -745,13 +807,14 @@ void WLED::initConnection()
|
||||
showWelcomePage = false;
|
||||
|
||||
DEBUG_PRINT(F("Connecting to "));
|
||||
DEBUG_PRINT(clientSSID);
|
||||
DEBUG_PRINT(multiWiFi[selectedWiFi].clientSSID);
|
||||
DEBUG_PRINTLN("...");
|
||||
|
||||
// convert the "serverDescription" into a valid DNS hostname (alphanumeric)
|
||||
char hostname[25];
|
||||
prepareHostname(hostname);
|
||||
WiFi.begin(clientSSID, clientPass);
|
||||
WiFi.begin(multiWiFi[selectedWiFi].clientSSID, multiWiFi[selectedWiFi].clientPass); // no harm if called multiple times
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#if defined(LOLIN_WIFI_FIX) && (defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32S3))
|
||||
WiFi.setTxPower(WIFI_POWER_8_5dBm);
|
||||
@ -843,35 +906,26 @@ void WLED::initInterfaces()
|
||||
|
||||
void WLED::handleConnection()
|
||||
{
|
||||
static bool scanDone = true;
|
||||
static byte stacO = 0;
|
||||
static uint32_t lastHeap = UINT32_MAX;
|
||||
static unsigned long heapTime = 0;
|
||||
unsigned long now = millis();
|
||||
const bool wifiConfigured = WLED_WIFI_CONFIGURED;
|
||||
|
||||
if (now < 2000 && (!WLED_WIFI_CONFIGURED || apBehavior == AP_BEHAVIOR_ALWAYS))
|
||||
// ignore connection handling if WiFi is configured and scan still running
|
||||
// or within first 2s if WiFi is not configured or AP is always active
|
||||
if ((wifiConfigured && multiWiFi.size() > 1 && WiFi.scanComplete() < 0) || (now < 2000 && (!wifiConfigured || apBehavior == AP_BEHAVIOR_ALWAYS)))
|
||||
return;
|
||||
|
||||
if (lastReconnectAttempt == 0) {
|
||||
DEBUG_PRINTLN(F("lastReconnectAttempt == 0"));
|
||||
if (lastReconnectAttempt == 0 || forceReconnect) {
|
||||
DEBUG_PRINTLN(F("Initial connect or forced reconnect."));
|
||||
selectedWiFi = findWiFi(); // find strongest WiFi
|
||||
initConnection();
|
||||
interfacesInited = false;
|
||||
forceReconnect = false;
|
||||
wasConnected = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// reconnect WiFi to clear stale allocations if heap gets too low
|
||||
if (now - heapTime > 5000) {
|
||||
uint32_t heap = ESP.getFreeHeap();
|
||||
if (heap < MIN_HEAP_SIZE && lastHeap < MIN_HEAP_SIZE) {
|
||||
DEBUG_PRINT(F("Heap too low! "));
|
||||
DEBUG_PRINTLN(heap);
|
||||
forceReconnect = true;
|
||||
strip.resetSegments();
|
||||
} else if (heap < MIN_HEAP_SIZE) {
|
||||
strip.purgeSegments();
|
||||
}
|
||||
lastHeap = heap;
|
||||
heapTime = now;
|
||||
}
|
||||
|
||||
byte stac = 0;
|
||||
if (apActive) {
|
||||
#ifdef ESP8266
|
||||
@ -885,7 +939,7 @@ void WLED::handleConnection()
|
||||
stacO = stac;
|
||||
DEBUG_PRINT(F("Connected AP clients: "));
|
||||
DEBUG_PRINTLN(stac);
|
||||
if (!WLED_CONNECTED && WLED_WIFI_CONFIGURED) { // trying to connect, but not connected
|
||||
if (!WLED_CONNECTED && wifiConfigured) { // trying to connect, but not connected
|
||||
if (stac)
|
||||
WiFi.disconnect(); // disable search so that AP can work
|
||||
else
|
||||
@ -893,36 +947,49 @@ void WLED::handleConnection()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (forceReconnect) {
|
||||
DEBUG_PRINTLN(F("Forcing reconnect."));
|
||||
initConnection();
|
||||
interfacesInited = false;
|
||||
forceReconnect = false;
|
||||
wasConnected = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Network.isConnected()) {
|
||||
if (interfacesInited) {
|
||||
if (scanDone && multiWiFi.size() > 1) {
|
||||
DEBUG_PRINTLN(F("WiFi scan initiated on disconnect."));
|
||||
findWiFi(true); // reinit scan
|
||||
scanDone = false;
|
||||
return; // try to connect in next iteration
|
||||
}
|
||||
DEBUG_PRINTLN(F("Disconnected!"));
|
||||
selectedWiFi = findWiFi();
|
||||
initConnection();
|
||||
interfacesInited = false;
|
||||
scanDone = true;
|
||||
}
|
||||
//send improv failed 6 seconds after second init attempt (24 sec. after provisioning)
|
||||
if (improvActive > 2 && now - lastReconnectAttempt > 6000) {
|
||||
sendImprovStateResponse(0x03, true);
|
||||
improvActive = 2;
|
||||
}
|
||||
if (now - lastReconnectAttempt > ((stac) ? 300000 : 18000) && WLED_WIFI_CONFIGURED) {
|
||||
if (now - lastReconnectAttempt > ((stac) ? 300000 : 18000) && wifiConfigured) {
|
||||
if (improvActive == 2) improvActive = 3;
|
||||
DEBUG_PRINTLN(F("Last reconnect too old."));
|
||||
if (++selectedWiFi >= multiWiFi.size()) selectedWiFi = 0; // we couldn't connect, try with another network from the list
|
||||
initConnection();
|
||||
}
|
||||
if (!apActive && now - lastReconnectAttempt > 12000 && (!wasConnected || apBehavior == AP_BEHAVIOR_NO_CONN)) {
|
||||
if (!(apBehavior == AP_BEHAVIOR_TEMPORARY && now > WLED_AP_TIMEOUT)) {
|
||||
DEBUG_PRINTLN(F("Not connected AP."));
|
||||
initAP();
|
||||
initAP(); // start AP only within first 5min
|
||||
}
|
||||
}
|
||||
if (apActive && apBehavior == AP_BEHAVIOR_TEMPORARY && now > WLED_AP_TIMEOUT && stac == 0) { // disconnect AP after 5min if no clients connected
|
||||
// if AP was enabled more than 10min after boot or if client was connected more than 10min after boot do not disconnect AP mode
|
||||
if (now < 2*WLED_AP_TIMEOUT) {
|
||||
dnsServer.stop();
|
||||
WiFi.softAPdisconnect(true);
|
||||
apActive = false;
|
||||
DEBUG_PRINTLN(F("Temporary AP disabled."));
|
||||
}
|
||||
}
|
||||
} else if (!interfacesInited) { //newly connected
|
||||
DEBUG_PRINTLN("");
|
||||
DEBUG_PRINTLN();
|
||||
DEBUG_PRINT(F("Connected! IP address: "));
|
||||
DEBUG_PRINTLN(Network.localIP());
|
||||
if (improvActive) {
|
||||
@ -940,7 +1007,7 @@ void WLED::handleConnection()
|
||||
dnsServer.stop();
|
||||
WiFi.softAPdisconnect(true);
|
||||
apActive = false;
|
||||
DEBUG_PRINTLN(F("Access point disabled (handle)."));
|
||||
DEBUG_PRINTLN(F("Access point disabled (connected)."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
// version code in format yymmddb (b = daily build)
|
||||
#define VERSION 2401270
|
||||
#define VERSION 2402010
|
||||
|
||||
//uncomment this if you have a "my_config.h" file you'd like to use
|
||||
//#define WLED_USE_MY_CONFIG
|
||||
@ -307,22 +307,21 @@ WLED_GLOBAL int8_t irPin _INIT(IRPIN);
|
||||
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)
|
||||
WLED_GLOBAL char clientSSID[33] _INIT(CLIENT_SSID);
|
||||
WLED_GLOBAL char clientPass[65] _INIT(CLIENT_PASS);
|
||||
WLED_GLOBAL uint8_t selectedWiFi _INIT(0);
|
||||
WLED_GLOBAL std::vector<WiFiConfig> multiWiFi;
|
||||
WLED_GLOBAL IPAddress dnsAddress _INIT_N((( 8, 8, 8, 8))); // Google's DNS
|
||||
WLED_GLOBAL char cmDNS[33] _INIT(MDNS_NAME); // mDNS address (*.local, replaced by wledXXXXXX if default is used)
|
||||
WLED_GLOBAL char apSSID[33] _INIT(""); // AP off by default (unless setup)
|
||||
WLED_GLOBAL byte apChannel _INIT(1); // 2.4GHz WiFi AP channel (1-13)
|
||||
WLED_GLOBAL byte apHide _INIT(0); // hidden AP SSID
|
||||
WLED_GLOBAL byte apBehavior _INIT(AP_BEHAVIOR_BOOT_NO_CONN); // access point opens when no connection after boot by default
|
||||
WLED_GLOBAL IPAddress staticIP _INIT_N((( 0, 0, 0, 0))); // static IP of ESP
|
||||
WLED_GLOBAL IPAddress staticGateway _INIT_N((( 0, 0, 0, 0))); // gateway (router) IP
|
||||
WLED_GLOBAL IPAddress staticSubnet _INIT_N(((255, 255, 255, 0))); // most common subnet in home networks
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
WLED_GLOBAL bool noWifiSleep _INIT(true); // disabling modem sleep modes will increase heat output and power usage, but may help with connection issues
|
||||
#else
|
||||
WLED_GLOBAL bool noWifiSleep _INIT(false);
|
||||
#endif
|
||||
WLED_GLOBAL bool force802_3g _INIT(false);
|
||||
#define WLED_WIFI_CONFIGURED (strlen(multiWiFi[0].clientSSID) >= 1 && strcmp(multiWiFi[0].clientSSID, DEFAULT_CLIENT_SSID) != 0)
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
#ifdef WLED_ETH_DEFAULT // default ethernet board type if specified
|
||||
@ -497,6 +496,7 @@ WLED_GLOBAL byte overlayMin _INIT(0), overlayMax _INIT(DEFAULT_LED_COUNT - 1);
|
||||
WLED_GLOBAL byte analogClock12pixel _INIT(0); // The pixel in your strip where "midnight" would be
|
||||
WLED_GLOBAL bool analogClockSecondsTrail _INIT(false); // Display seconds as trail of LEDs instead of a single pixel
|
||||
WLED_GLOBAL bool analogClock5MinuteMarks _INIT(false); // Light pixels at every 5-minute position
|
||||
WLED_GLOBAL bool analogClockSolidBlack _INIT(false); // Show clock overlay only if all LEDs are solid black (effect is 0 and color is black)
|
||||
|
||||
WLED_GLOBAL bool countdownMode _INIT(false); // Clock will count down towards date
|
||||
WLED_GLOBAL byte countdownYear _INIT(20), countdownMonth _INIT(1); // Countdown target date, year is last two digits
|
||||
@ -836,7 +836,6 @@ WLED_GLOBAL volatile uint8_t jsonBufferLock _INIT(0);
|
||||
#else
|
||||
#define WLED_CONNECTED (WiFi.status() == WL_CONNECTED)
|
||||
#endif
|
||||
#define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID, DEFAULT_CLIENT_SSID) != 0)
|
||||
|
||||
#ifndef WLED_AP_SSID_UNIQUE
|
||||
#define WLED_SET_AP_SSID() do { \
|
||||
@ -886,6 +885,7 @@ public:
|
||||
void initAP(bool resetAP = false);
|
||||
void initConnection();
|
||||
void initInterfaces();
|
||||
int8_t findWiFi(bool doScan = false);
|
||||
#if defined(STATUSLED)
|
||||
void handleStatusLED();
|
||||
#endif
|
||||
|
@ -74,8 +74,8 @@ void loadSettingsFromEEPROM()
|
||||
int lastEEPROMversion = EEPROM.read(377); //last EEPROM version before update
|
||||
|
||||
|
||||
readStringFromEEPROM( 0, clientSSID, 32);
|
||||
readStringFromEEPROM( 32, clientPass, 64);
|
||||
readStringFromEEPROM( 0, multiWiFi[0].clientSSID, 32);
|
||||
readStringFromEEPROM( 32, multiWiFi[0].clientPass, 64);
|
||||
readStringFromEEPROM( 96, cmDNS, 32);
|
||||
readStringFromEEPROM(128, apSSID, 32);
|
||||
readStringFromEEPROM(160, apPass, 64);
|
||||
|
@ -248,23 +248,34 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
|
||||
if (subPage == SUBPAGE_WIFI)
|
||||
{
|
||||
sappends('s',SET_F("CS"),clientSSID);
|
||||
|
||||
byte l = strlen(clientPass);
|
||||
char nS[10];
|
||||
size_t l;
|
||||
oappend(SET_F("resetWiFi("));
|
||||
oappend(itoa(WLED_MAX_WIFI_COUNT,nS,10));
|
||||
oappend(SET_F(");"));
|
||||
for (size_t n = 0; n < multiWiFi.size(); n++) {
|
||||
l = strlen(multiWiFi[n].clientPass);
|
||||
char fpass[l+1]; //fill password field with ***
|
||||
fpass[l] = 0;
|
||||
memset(fpass,'*',l);
|
||||
sappends('s',SET_F("CP"),fpass);
|
||||
|
||||
char k[3]; k[2] = 0; //IP addresses
|
||||
for (int i = 0; i<4; i++)
|
||||
{
|
||||
k[1] = 48+i; //ascii 0,1,2,3
|
||||
k[0] = 'I'; sappend('v',k,staticIP[i]);
|
||||
k[0] = 'G'; sappend('v',k,staticGateway[i]);
|
||||
k[0] = 'S'; sappend('v',k,staticSubnet[i]);
|
||||
oappend(SET_F("addWiFi(\""));
|
||||
oappend(multiWiFi[n].clientSSID);
|
||||
oappend(SET_F("\",\""));
|
||||
oappend(fpass);
|
||||
oappend(SET_F("\",0x"));
|
||||
oappend(itoa(multiWiFi[n].staticIP,nS,16));
|
||||
oappend(SET_F(",0x"));
|
||||
oappend(itoa(multiWiFi[n].staticGW,nS,16));
|
||||
oappend(SET_F(",0x"));
|
||||
oappend(itoa(multiWiFi[n].staticSN,nS,16));
|
||||
oappend(SET_F(");"));
|
||||
}
|
||||
|
||||
sappend('v',SET_F("D0"),dnsAddress[0]);
|
||||
sappend('v',SET_F("D1"),dnsAddress[1]);
|
||||
sappend('v',SET_F("D2"),dnsAddress[2]);
|
||||
sappend('v',SET_F("D3"),dnsAddress[3]);
|
||||
|
||||
sappends('s',SET_F("CM"),cmDNS);
|
||||
sappend('i',SET_F("AB"),apBehavior);
|
||||
sappends('s',SET_F("AS"),apSSID);
|
||||
@ -597,6 +608,7 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sappend('v',SET_F("OM"),analogClock12pixel);
|
||||
sappend('c',SET_F("OS"),analogClockSecondsTrail);
|
||||
sappend('c',SET_F("O5"),analogClock5MinuteMarks);
|
||||
sappend('c',SET_F("OB"),analogClockSolidBlack);
|
||||
|
||||
sappend('c',SET_F("CE"),countdownMode);
|
||||
sappend('v',SET_F("CY"),countdownYear);
|
||||
|
Loading…
x
Reference in New Issue
Block a user