Compare commits
176 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4a2ffc5d2 | ||
|
|
ba1117e10e | ||
|
|
0cd46f932a | ||
|
|
937f404583 | ||
|
|
d13d60d752 | ||
|
|
31e4e7c709 | ||
|
|
0d3a8ce31b | ||
|
|
be185b46a7 | ||
|
|
90fa5b3b93 | ||
|
|
733996772b | ||
|
|
d4c921ea2e | ||
|
|
2852061699 | ||
|
|
d8859b9f0a | ||
|
|
ae1bc96006 | ||
|
|
f30ffb4413 | ||
|
|
273c6467c8 | ||
|
|
846a1d007c | ||
|
|
1dccc8dc78 | ||
|
|
e0d67bd057 | ||
|
|
4b4b93ac04 | ||
|
|
4390aee1e0 | ||
|
|
4cddb16788 | ||
|
|
e1179fd8c8 | ||
|
|
cb77285277 | ||
|
|
6c9d161950 | ||
|
|
40aaac5868 | ||
|
|
e16b69594e | ||
|
|
4837bf007a | ||
|
|
705fd4dafd | ||
|
|
a3e28d3c66 | ||
|
|
4a6755c28a | ||
|
|
188fe5dc52 | ||
|
|
44a8ae457d | ||
|
|
92eafcfe1a | ||
|
|
b12b031fdd | ||
|
|
492ec489a1 | ||
|
|
c57124e876 | ||
|
|
95b33c9c34 | ||
|
|
c6d8b63e54 | ||
|
|
f0f02c4ea6 | ||
|
|
eb2cb6810a | ||
|
|
b3c090e9ed | ||
|
|
13366fc9f8 | ||
|
|
929af7830a | ||
|
|
13062cf0e4 | ||
|
|
b897a8a35f | ||
|
|
117dc5288d | ||
|
|
4b5a3bd3d5 | ||
|
|
b224a67ea7 | ||
|
|
793f919d59 | ||
|
|
315987b2f6 | ||
|
|
9b7db548a2 | ||
|
|
126b70f781 | ||
|
|
0bbff627e2 | ||
|
|
961d23e2a1 | ||
|
|
b03ff9a48a | ||
|
|
3ffb40fafa | ||
|
|
1a3b4ac2ac | ||
|
|
794e17442f | ||
|
|
238d7119e0 | ||
|
|
8a929a8348 | ||
|
|
cf77153647 | ||
|
|
a2da0b0641 | ||
|
|
73faa13811 | ||
|
|
1a71872c7b | ||
|
|
62fe7135bd | ||
|
|
078940d29f | ||
|
|
2fafe42c18 | ||
|
|
c8a7537157 | ||
|
|
d4bf1cb23d | ||
|
|
46e4350013 | ||
|
|
202eb0d854 | ||
|
|
898702346e | ||
|
|
b72e6f16ca | ||
|
|
b9c27ed324 | ||
|
|
0166dfe16e | ||
|
|
7274541722 | ||
|
|
709ff7a701 | ||
|
|
66c224c954 | ||
|
|
3f9b37aa7f | ||
|
|
0377958d8f | ||
|
|
cc1cfd70b8 | ||
|
|
bc125ad76c | ||
|
|
62a2246448 | ||
|
|
587cf751d8 | ||
|
|
600181ed07 | ||
|
|
f0e525d2e2 | ||
|
|
f86cdd8cde | ||
|
|
4a4c537a0d | ||
|
|
1caaf04dfa | ||
|
|
b422a80249 | ||
|
|
ba19e20833 | ||
|
|
c34ddb2bc3 | ||
|
|
aa315f8472 | ||
|
|
2af6af2bf0 | ||
|
|
5694ff7c97 | ||
|
|
76f1c689c1 | ||
|
|
a371239172 | ||
|
|
4fd904fbcc | ||
|
|
b73a257389 | ||
|
|
9e70d6b3e1 | ||
|
|
9caca37ab1 | ||
|
|
6e76fc0aa7 | ||
|
|
6171883758 | ||
|
|
942b68c948 | ||
|
|
9ca7ffa5a3 | ||
|
|
d1ce23c5ac | ||
|
|
b7b6d0a6bc | ||
|
|
10c51eea2c | ||
|
|
48d20c02a1 | ||
|
|
c5cc0b3f2b | ||
|
|
6ebef8846c | ||
|
|
5d1993935e | ||
|
|
caab8943cb | ||
|
|
f5c05b24fb | ||
|
|
940a0d006d | ||
|
|
8fe67a04d8 | ||
|
|
bec745d095 | ||
|
|
223fd35138 | ||
|
|
d3fc0309c0 | ||
|
|
830223f6e2 | ||
|
|
eb53c52499 | ||
|
|
96c9e2b4d6 | ||
|
|
c34b948bad | ||
|
|
9ac609f846 | ||
|
|
6aa47bfd1b | ||
|
|
ff46e6ea86 | ||
|
|
5489c74986 | ||
|
|
f6f8151150 | ||
|
|
a20d577f6c | ||
|
|
c4c2494dd1 | ||
|
|
c9c294a1d5 | ||
|
|
6359a8a8a2 | ||
|
|
f9b44381bd | ||
|
|
eb1ccb600b | ||
|
|
b2db61aa03 | ||
|
|
ee55a574de | ||
|
|
0998fd32cd | ||
|
|
686f2c4aa6 | ||
|
|
cd234673ea | ||
|
|
54d7a81f16 | ||
|
|
9d8d2c0aa1 | ||
|
|
17ce6b9507 | ||
|
|
296065a976 | ||
|
|
0b0f600f97 | ||
|
|
f1371d6737 | ||
|
|
03a33790e1 | ||
|
|
c0816c80ae | ||
|
|
071ebe6ef2 | ||
|
|
2126e42743 | ||
|
|
8a2b34adb4 | ||
|
|
93eb4d21bf | ||
|
|
868cedeed2 | ||
|
|
5c794f428a | ||
|
|
612d6f85bd | ||
|
|
e5cef6b877 | ||
|
|
f2a63c04a8 | ||
|
|
5a5064e070 | ||
|
|
478fa3132c | ||
|
|
a84859c211 | ||
|
|
67013bd58f | ||
|
|
b51be31d8a | ||
|
|
a3bef49124 | ||
|
|
a5cf553f17 | ||
|
|
640188f4e2 | ||
|
|
48265bbe02 | ||
|
|
6aaf544079 | ||
|
|
9904c10984 | ||
|
|
81c810eba4 | ||
|
|
5e6b1e8175 | ||
|
|
48c165b0b4 | ||
|
|
042605701e | ||
|
|
32cf1495d3 | ||
|
|
9577e49231 | ||
|
|
de19839145 | ||
|
|
f970780d6c |
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
.pio
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
.vscode
|
||||
!.vscode/extensions.json
|
||||
/wled00/Release
|
||||
/wled00/extLibs
|
||||
35
.travis.yml
Normal file
35
.travis.yml
Normal file
@@ -0,0 +1,35 @@
|
||||
# Continuous Integration (CI) is the practice, in software
|
||||
# engineering, of merging all developer working copies with a shared mainline
|
||||
# several times a day < https://docs.platformio.org/page/ci/index.html >
|
||||
#
|
||||
# Documentation:
|
||||
#
|
||||
# * Travis CI Embedded Builds with PlatformIO
|
||||
# < https://docs.travis-ci.com/user/integration/platformio/ >
|
||||
#
|
||||
# * PlatformIO integration with Travis CI
|
||||
# < https://docs.platformio.org/page/ci/travis.html >
|
||||
#
|
||||
# * User Guide for `platformio ci` command
|
||||
# < https://docs.platformio.org/page/userguide/cmd_ci.html >
|
||||
#
|
||||
#
|
||||
# Please choose one of the following templates (proposed below) and uncomment
|
||||
# it (remove "# " before each line) or use own configuration according to the
|
||||
# Travis CI documentation (see above).
|
||||
#
|
||||
|
||||
language: python
|
||||
python:
|
||||
- "2.7"
|
||||
sudo: false
|
||||
cache:
|
||||
directories:
|
||||
- "~/.platformio"
|
||||
env:
|
||||
- PLATFORMIO_CI_SRC=wled00
|
||||
install:
|
||||
- pip install -U platformio
|
||||
- platformio update
|
||||
script:
|
||||
- platformio ci --project-conf=./platformio.ini -v
|
||||
BIN
.vs/wled00/v15/.suo
Normal file
BIN
.vs/wled00/v15/.suo
Normal file
Binary file not shown.
7
.vscode/extensions.json
vendored
Normal file
7
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
]
|
||||
}
|
||||
39
include/README
Normal file
39
include/README
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
||||
46
lib/README
Normal file
46
lib/README
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
||||
163
platformio.ini
Normal file
163
platformio.ini
Normal file
@@ -0,0 +1,163 @@
|
||||
; PlatformIO Project Configuration File
|
||||
; Please visit documentation: https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
src_dir = ./wled00
|
||||
data_dir = ./wled00/data
|
||||
lib_extra_dirs = ./wled00/src
|
||||
; Please uncomment one of the 5 lines below to select your board
|
||||
; env_default = nodemcuv2
|
||||
; env_default = esp01
|
||||
; env_default = esp01_1m
|
||||
; env_default = d1_mini
|
||||
; env_default = esp32dev
|
||||
|
||||
|
||||
[common]
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
board_build.flash_mode = dout
|
||||
upload_speed = 115200
|
||||
upload_speed_fast = 921600
|
||||
build_flags =
|
||||
-w ; supresses all C/C++ warnings
|
||||
; -D VERSION=0.8.5
|
||||
; -D DEBUG
|
||||
# TODO replace libs in /lib with managed libs in here if possible.
|
||||
# If they are not changed it's just a matter of setting the correct version and change the import statement
|
||||
lib_deps_external =
|
||||
#Blynk@0.5.4(changed)
|
||||
#E131@1.0.0(changed)
|
||||
FastLED@3.3.2
|
||||
NeoPixelBus@2.5.1
|
||||
ESPAsyncTCP@1.2.0
|
||||
AsyncTCP@1.0.3
|
||||
Esp Async WebServer@1.2.0
|
||||
#ArduinoJson@5.13.5
|
||||
IRremoteESP8266@2.5.5
|
||||
#Time@1.5
|
||||
#Timezone@1.2.1
|
||||
|
||||
[common:esp8266]
|
||||
# ------------------------------------------------------------------------------
|
||||
# PLATFORM:
|
||||
# !! DO NOT confuse platformio's ESP8266 development platform with Arduino core for ESP8266
|
||||
# We use Arduino Core 2.5.0 (platformIO 2.0.4) as default
|
||||
#
|
||||
# arduino core 2.3.0 = platformIO 1.5.0
|
||||
# arduino core 2.4.0 = platformIO 1.6.0
|
||||
# arduino core 2.4.1 = platformIO 1.7.3
|
||||
# arduino core 2.4.2 = platformIO 1.8.0
|
||||
# arduino core 2.5.0 = platformIO 2.0.4
|
||||
# arduino core stage = platformIO feature#stage
|
||||
# ------------------------------------------------------------------------------
|
||||
arduino_core_2_3_0 = espressif8266@1.5.0
|
||||
arduino_core_2_4_0 = espressif8266@1.6.0
|
||||
arduino_core_2_4_1 = espressif8266@1.7.3
|
||||
arduino_core_2_4_2 = espressif8266@1.8.0
|
||||
arduino_core_2_5_0 = espressif8266@2.0.4
|
||||
arduino_core_2_5_2 = espressif8266@2.2.3
|
||||
arduino_core_stage = https://github.com/platformio/platform-espressif8266.git#feature/stage
|
||||
platform = ${common:esp8266.arduino_core_2_5_2}
|
||||
build_flags =
|
||||
-D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH
|
||||
-Wl,-Teagle.flash.4m1m.ld ;;;; Required for core > v2.5.0 or staging version 4MB Flash 3MB SPIFFs
|
||||
|
||||
[common:esp8266_1M]
|
||||
platform = espressif8266@1.8.0
|
||||
build_flags =
|
||||
-D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
|
||||
-Wl,-Teagle.flash.1m0.ld ;;;; Compile with no SPIFFS to leave space for OTA
|
||||
; -D WLED_DISABLE_MOBILE_UI
|
||||
; -D WLED_DISABLE_OTA
|
||||
; -D WLED_DISABLE_ALEXA
|
||||
-D WLED_DISABLE_BLYNK
|
||||
-D WLED_DISABLE_CRONIXIE
|
||||
; -D WLED_DISABLE_HUESYNC
|
||||
-D WLED_DISABLE_INFRARED
|
||||
|
||||
[common:esp8266_512k]
|
||||
platform = espressif8266@1.8.0
|
||||
build_flags =
|
||||
-D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
|
||||
-Wl,-Teagle.flash.512k0.ld ;;;; Compile with no SPIFFS
|
||||
; -D WLED_DISABLE_MOBILE_UI
|
||||
-D WLED_DISABLE_OTA
|
||||
; -D WLED_DISABLE_ALEXA
|
||||
-D WLED_DISABLE_BLYNK
|
||||
-D WLED_DISABLE_CRONIXIE
|
||||
; -D WLED_DISABLE_HUESYNC
|
||||
-D WLED_DISABLE_INFRARED
|
||||
|
||||
[common:esp32]
|
||||
platform = espressif32@1.7.0
|
||||
build_flags =
|
||||
-D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH
|
||||
-D ARDUINO_ARCH_ESP32
|
||||
-D WLED_DISABLE_INFRARED
|
||||
|
||||
# see: http://docs.platformio.org/en/latest/platforms/espressif8266.html
|
||||
[env:nodemcuv2]
|
||||
board = nodemcuv2
|
||||
platform = ${common:esp8266.platform}
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
upload_speed = ${common.upload_speed}
|
||||
framework = ${common.framework}
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
${common:esp8266.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_external}
|
||||
|
||||
[env:d1_mini]
|
||||
board = d1_mini
|
||||
platform = ${common:esp8266.platform}
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
upload_speed = ${common.upload_speed}
|
||||
framework = ${common.framework}
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
${common:esp8266.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_external}
|
||||
|
||||
[env:esp01_1m]
|
||||
board = esp01_1m
|
||||
platform = ${common:esp8266_1M.platform}
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
upload_speed = ${common.upload_speed}
|
||||
framework = ${common.framework}
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
${common:esp8266_1M.build_flags}
|
||||
# disable IR because there is no pin for it
|
||||
-D WLED_DISABLE_INFRARED
|
||||
lib_deps =
|
||||
${common.lib_deps_external}
|
||||
|
||||
[env:esp01]
|
||||
board = esp01
|
||||
platform = ${common:esp8266_512k.platform}
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
upload_speed = ${common.upload_speed}
|
||||
framework = ${common.framework}
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
${common:esp8266_512k.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_external}
|
||||
|
||||
# see: http://docs.platformio.org/en/latest/platforms/espressif32.html
|
||||
[env:esp32dev]
|
||||
board = esp32dev
|
||||
platform = ${common:esp32.platform}
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
upload_speed = ${common.upload_speed_fast}
|
||||
framework = ${common.framework}
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
${common:esp32.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_external}
|
||||
lib_ignore =
|
||||
IRremoteESP8266
|
||||
68
readme.md
68
readme.md
@@ -1,34 +1,42 @@
|
||||

|
||||

|
||||
|
||||
## Welcome to my project WLED! (v0.8.0)
|
||||
[](https://github.com/Aircoookie/WLED/releases)
|
||||
[](https://discord.gg/KuqP7NE)
|
||||
[](https://github.com/Aircoookie/WLED/wiki)
|
||||
[](https://github.com/Aircoookie/WLED-App)
|
||||
|
||||
A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B) LEDs!
|
||||
## Welcome to my project WLED!
|
||||
|
||||
A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B, WS2811, SK6812, APA102) LEDs!
|
||||
|
||||
### Features:
|
||||
- WS2812FX library integrated for over 70 special effects
|
||||
- FastLED noise effects and palettes
|
||||
- Customizable Mobile and desktop UI with color and effect controls
|
||||
- Settings page - configuration over network
|
||||
- Access Point and station mode - automatic failsafe AP
|
||||
- Support for RGBW strips
|
||||
- 25 user presets to save and load colors/effects easily, supports cycling through them.
|
||||
- Macro functions to automatically execute API calls
|
||||
- Nightlight function (gradually dims down)
|
||||
- Full OTA software updatability (HTTP + ArduinoOTA), password protectable
|
||||
- Configurable analog clock + support for the Cronixie kit by Diamex
|
||||
- WS2812FX library integrated for 80 special effects
|
||||
- FastLED noise effects and palettes
|
||||
- Customizable Mobile and desktop UI with color and effect controls
|
||||
- Settings page - configuration over network
|
||||
- Access Point and station mode - automatic failsafe AP
|
||||
- Support for RGBW strips
|
||||
- 25 user presets to save and load colors/effects easily, supports cycling through them.
|
||||
- Macro functions to automatically execute API calls
|
||||
- Nightlight function (gradually dims down)
|
||||
- Full OTA software updatability (HTTP + ArduinoOTA), password protectable
|
||||
- Configurable analog clock + support for the Cronixie kit by Diamex
|
||||
- Configurable Auto Brightness limit for safer operation
|
||||
|
||||
### Supported light control interfaces:
|
||||
- HTTP request API
|
||||
- Blynk IoT
|
||||
- MQTT
|
||||
- E1.31
|
||||
- Hyperion
|
||||
- UDP realtime
|
||||
- Alexa smart device (including dimming)
|
||||
- Sync to Philips hue lights
|
||||
- Adalight (PC ambilight via serial)
|
||||
- Sync color of multiple WLED devices (UDP notifier)
|
||||
- Simple timers/schedules (time from NTP, timezones/DST supported)
|
||||
- WLED app for Android and iOS
|
||||
- JSON and HTTP request APIs
|
||||
- MQTT
|
||||
- Blynk IoT
|
||||
- E1.31
|
||||
- Hyperion
|
||||
- UDP realtime
|
||||
- Alexa voice control (including dimming and color)
|
||||
- Sync to Philips hue lights
|
||||
- Adalight (PC ambilight via serial)
|
||||
- Sync color of multiple WLED devices (UDP notifier)
|
||||
- Infrared remotes (24-key RGB, receiver required)
|
||||
- Simple timers/schedules (time from NTP, timezones/DST supported)
|
||||
|
||||
### Quick start guide and documentation:
|
||||
|
||||
@@ -37,16 +45,10 @@ See the [wiki](https://github.com/Aircoookie/WLED/wiki)!
|
||||
### Other
|
||||
|
||||
Licensed under the MIT license
|
||||
Credits in About page!
|
||||
Credits [here](https://github.com/Aircoookie/WLED/wiki/Contributors-&-About)!
|
||||
|
||||
Uses Linearicons by Perxis!
|
||||
|
||||
Join the Discord [server](https://discord.gg/KuqP7NE) to discuss everything about WLED!
|
||||
You can also send me mails to [dev.aircoookie@gmail.com](mailto:dev.aircoookie@gmail.com).
|
||||
If you insist that you just love WLED too much, you can [send me a gift](https://paypal.me/aircoookie)!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
If WLED really brightens up your every day, you can [](https://paypal.me/aircoookie)
|
||||
|
||||
11
test/README
Normal file
11
test/README
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
This directory is intended for PIO Unit Testing and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PIO Unit Testing:
|
||||
- https://docs.platformio.org/page/plus/unit-testing.html
|
||||
25
wled00.sln
Normal file
25
wled00.sln
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.28010.2046
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wled00", "wled00\wled00.vcxproj", "{C5F80730-F44F-4478-BDAE-6634EFC2CA88}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.Build.0 = Debug|Win32
|
||||
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.ActiveCfg = Release|Win32
|
||||
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {9A679C2B-61D3-400B-B96F-06E604E9CED2}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
BIN
wled00/.vs/wled00/v15/.suo
Normal file
BIN
wled00/.vs/wled00/v15/.suo
Normal file
Binary file not shown.
2318
wled00/FX.cpp
Normal file
2318
wled00/FX.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,7 @@
|
||||
//pixelmethod now in NpbWrapper.h
|
||||
|
||||
/*
|
||||
WS2812FX.h - Library for WS2812 LED effects.
|
||||
Harm Aldick - 2016
|
||||
www.aldick.org
|
||||
FEATURES
|
||||
* A lot of blinken modes and counting
|
||||
* WS2812FX can be used as drop-in replacement for Adafruit NeoPixel Library
|
||||
NOTES
|
||||
* Uses the Adafruit NeoPixel library. Get it here:
|
||||
https://github.com/adafruit/Adafruit_NeoPixel
|
||||
LICENSE
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2016 Harm Aldick
|
||||
@@ -28,11 +20,7 @@
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
CHANGELOG
|
||||
2016-05-28 Initial beta release
|
||||
2016-06-03 Code cleanup, minor improvements, new modes
|
||||
2016-06-04 2 new fx, fixed setColor (now also resets _mode_color)
|
||||
2017-02-02 added external trigger functionality (e.g. for sound-to-light)
|
||||
|
||||
Modified for WLED
|
||||
*/
|
||||
|
||||
@@ -41,22 +29,32 @@
|
||||
|
||||
#include "NpbWrapper.h"
|
||||
|
||||
#define DEFAULT_BRIGHTNESS (uint8_t)50
|
||||
#define FASTLED_INTERNAL //remove annoying pragma messages
|
||||
#include "FastLED.h"
|
||||
|
||||
#define DEFAULT_BRIGHTNESS (uint8_t)127
|
||||
#define DEFAULT_MODE (uint8_t)0
|
||||
#define DEFAULT_SPEED (uint16_t)1000
|
||||
#define DEFAULT_COLOR (uint32_t)0xFF0000
|
||||
#define DEFAULT_SPEED (uint8_t)128
|
||||
#define DEFAULT_COLOR (uint32_t)0xFFAA00
|
||||
|
||||
#define min(a,b) ((a)<(b)?(a):(b))
|
||||
#define max(a,b) ((a)>(b)?(a):(b))
|
||||
|
||||
/* each segment uses 38 bytes of SRAM memory, so if you're application fails because of
|
||||
/* Not used in all effects yet */
|
||||
#define WLED_FPS 42
|
||||
#define FRAMETIME 1000/WLED_FPS
|
||||
|
||||
/* each segment uses 37 bytes of SRAM memory, so if you're application fails because of
|
||||
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
|
||||
#define MAX_NUM_SEGMENTS 10
|
||||
#define NUM_COLORS 3 /* number of colors per segment */
|
||||
|
||||
#define NUM_COLORS 3 /* number of colors per segment */
|
||||
#define SEGMENT _segments[_segment_index]
|
||||
#define SEGMENT_RUNTIME _segment_runtimes[_segment_index]
|
||||
#define SEGMENT_LENGTH (SEGMENT.stop - SEGMENT.start + 1)
|
||||
#define SPEED_FORMULA_L 5 + (50*(255 - SEGMENT.speed))/SEGMENT_LENGTH
|
||||
#define SEGCOLOR(x) gamma32(_segments[_segment_index].colors[x])
|
||||
#define SEGENV _segment_runtimes[_segment_index]
|
||||
#define SEGLEN SEGMENT.length()
|
||||
#define SEGACT SEGMENT.stop
|
||||
#define SPEED_FORMULA_L 5 + (50*(255 - SEGMENT.speed))/SEGLEN
|
||||
#define RESET_RUNTIME memset(_segment_runtimes, 0, sizeof(_segment_runtimes))
|
||||
|
||||
// some common colors
|
||||
@@ -74,15 +72,19 @@
|
||||
#define ULTRAWHITE (uint32_t)0xFFFFFFFF
|
||||
|
||||
// options
|
||||
// bit 8: reverse animation
|
||||
// bits 5-7: fade rate (0-7)
|
||||
// bit 4: gamma correction
|
||||
// bits 1-3: TBD
|
||||
// bit 7: segment is in transition mode
|
||||
// bits 2-6: TBD
|
||||
// bit 1: reverse segment
|
||||
// bit 0: segment is selected
|
||||
#define NO_OPTIONS (uint8_t)0x00
|
||||
#define REVERSE (uint8_t)0x80
|
||||
#define IS_REVERSE ((SEGMENT.options & REVERSE) == REVERSE)
|
||||
#define TRANSITIONAL (uint8_t)0x80
|
||||
#define REVERSE (uint8_t)0x02
|
||||
#define SELECTED (uint8_t)0x01
|
||||
#define IS_TRANSITIONAL ((SEGMENT.options & TRANSITIONAL) == TRANSITIONAL)
|
||||
#define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE )
|
||||
#define IS_SELECTED ((SEGMENT.options & SELECTED) == SELECTED )
|
||||
|
||||
#define MODE_COUNT 74
|
||||
#define MODE_COUNT 83
|
||||
|
||||
#define FX_MODE_STATIC 0
|
||||
#define FX_MODE_BLINK 1
|
||||
@@ -100,10 +102,10 @@
|
||||
#define FX_MODE_THEATER_CHASE 13
|
||||
#define FX_MODE_THEATER_CHASE_RAINBOW 14
|
||||
#define FX_MODE_RUNNING_LIGHTS 15
|
||||
#define FX_MODE_TWINKLE 16
|
||||
#define FX_MODE_TWINKLE_RANDOM 17
|
||||
#define FX_MODE_TWINKLE_FADE 18
|
||||
#define FX_MODE_TWINKLE_FADE_RANDOM 19
|
||||
#define FX_MODE_SAW 16
|
||||
#define FX_MODE_TWINKLE 17
|
||||
#define FX_MODE_DISSOLVE 18
|
||||
#define FX_MODE_DISSOLVE_RANDOM 19
|
||||
#define FX_MODE_SPARKLE 20
|
||||
#define FX_MODE_FLASH_SPARKLE 21
|
||||
#define FX_MODE_HYPER_SPARKLE 22
|
||||
@@ -127,7 +129,7 @@
|
||||
#define FX_MODE_LARSON_SCANNER 40
|
||||
#define FX_MODE_COMET 41
|
||||
#define FX_MODE_FIREWORKS 42
|
||||
#define FX_MODE_FIREWORKS_RANDOM 43
|
||||
#define FX_MODE_RAIN 43
|
||||
#define FX_MODE_MERRY_CHRISTMAS 44
|
||||
#define FX_MODE_FIRE_FLICKER 45
|
||||
#define FX_MODE_GRADIENT 46
|
||||
@@ -159,6 +161,16 @@
|
||||
#define FX_MODE_NOISE16_2 71
|
||||
#define FX_MODE_NOISE16_3 72
|
||||
#define FX_MODE_NOISE16_4 73
|
||||
#define FX_MODE_COLORTWINKLE 74
|
||||
#define FX_MODE_LAKE 75
|
||||
#define FX_MODE_METEOR 76
|
||||
#define FX_MODE_METEOR_SMOOTH 77
|
||||
#define FX_MODE_RAILWAY 78
|
||||
#define FX_MODE_RIPPLE 79
|
||||
#define FX_MODE_TWINKLEFOX 80
|
||||
#define FX_MODE_TWINKLECAT 81
|
||||
#define FX_MODE_HALLOWEEN_EYES 82
|
||||
|
||||
|
||||
class WS2812FX {
|
||||
typedef uint16_t (WS2812FX::*mode_ptr)(void);
|
||||
@@ -167,26 +179,52 @@ class WS2812FX {
|
||||
public:
|
||||
typedef struct Segment { // 21 bytes
|
||||
uint16_t start;
|
||||
uint16_t stop;
|
||||
uint16_t stop; //segment invalid if stop == 0
|
||||
uint8_t speed;
|
||||
uint8_t intensity;
|
||||
uint8_t palette;
|
||||
uint8_t mode;
|
||||
uint8_t options;
|
||||
uint8_t mode;
|
||||
uint8_t options; //bit pattern: msb first: transitional tbd tbd tbd tbd paused reverse selected
|
||||
uint32_t colors[NUM_COLORS];
|
||||
void setOption(uint8_t n, bool val)
|
||||
{
|
||||
if (val) {
|
||||
options |= 0x01 << n;
|
||||
} else
|
||||
{
|
||||
options &= ~(0x01 << n);
|
||||
}
|
||||
}
|
||||
bool getOption(uint8_t n)
|
||||
{
|
||||
return ((options >> n) & 0x01);
|
||||
}
|
||||
bool isSelected()
|
||||
{
|
||||
return getOption(0);
|
||||
}
|
||||
bool isActive()
|
||||
{
|
||||
return stop > start;
|
||||
}
|
||||
uint16_t length()
|
||||
{
|
||||
return stop - start;
|
||||
}
|
||||
} segment;
|
||||
|
||||
// segment runtime parameters
|
||||
typedef struct Segment_runtime { // 17 bytes
|
||||
typedef struct Segment_runtime { // 16 bytes
|
||||
unsigned long next_time;
|
||||
uint32_t counter_mode_step;
|
||||
uint32_t counter_mode_call;
|
||||
uint16_t aux_param;
|
||||
uint16_t aux_param2;
|
||||
uint8_t trans_act;
|
||||
uint32_t step;
|
||||
uint32_t call;
|
||||
uint16_t aux0;
|
||||
uint16_t aux1;
|
||||
void reset(){next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;};
|
||||
} segment_runtime;
|
||||
|
||||
WS2812FX() {
|
||||
//assign each member of the _mode[] array to its respective function reference
|
||||
_mode[FX_MODE_STATIC] = &WS2812FX::mode_static;
|
||||
_mode[FX_MODE_BLINK] = &WS2812FX::mode_blink;
|
||||
_mode[FX_MODE_COLOR_WIPE] = &WS2812FX::mode_color_wipe;
|
||||
@@ -201,10 +239,10 @@ class WS2812FX {
|
||||
_mode[FX_MODE_FADE] = &WS2812FX::mode_fade;
|
||||
_mode[FX_MODE_THEATER_CHASE] = &WS2812FX::mode_theater_chase;
|
||||
_mode[FX_MODE_THEATER_CHASE_RAINBOW] = &WS2812FX::mode_theater_chase_rainbow;
|
||||
_mode[FX_MODE_SAW] = &WS2812FX::mode_saw;
|
||||
_mode[FX_MODE_TWINKLE] = &WS2812FX::mode_twinkle;
|
||||
_mode[FX_MODE_TWINKLE_RANDOM] = &WS2812FX::mode_twinkle_random;
|
||||
_mode[FX_MODE_TWINKLE_FADE] = &WS2812FX::mode_twinkle_fade;
|
||||
_mode[FX_MODE_TWINKLE_FADE_RANDOM] = &WS2812FX::mode_twinkle_fade_random;
|
||||
_mode[FX_MODE_DISSOLVE] = &WS2812FX::mode_dissolve;
|
||||
_mode[FX_MODE_DISSOLVE_RANDOM] = &WS2812FX::mode_dissolve_random;
|
||||
_mode[FX_MODE_SPARKLE] = &WS2812FX::mode_sparkle;
|
||||
_mode[FX_MODE_FLASH_SPARKLE] = &WS2812FX::mode_flash_sparkle;
|
||||
_mode[FX_MODE_HYPER_SPARKLE] = &WS2812FX::mode_hyper_sparkle;
|
||||
@@ -228,7 +266,7 @@ class WS2812FX {
|
||||
_mode[FX_MODE_LARSON_SCANNER] = &WS2812FX::mode_larson_scanner;
|
||||
_mode[FX_MODE_COMET] = &WS2812FX::mode_comet;
|
||||
_mode[FX_MODE_FIREWORKS] = &WS2812FX::mode_fireworks;
|
||||
_mode[FX_MODE_FIREWORKS_RANDOM] = &WS2812FX::mode_fireworks_random;
|
||||
_mode[FX_MODE_RAIN] = &WS2812FX::mode_rain;
|
||||
_mode[FX_MODE_MERRY_CHRISTMAS] = &WS2812FX::mode_merry_christmas;
|
||||
_mode[FX_MODE_FIRE_FLICKER] = &WS2812FX::mode_fire_flicker;
|
||||
_mode[FX_MODE_GRADIENT] = &WS2812FX::mode_gradient;
|
||||
@@ -261,40 +299,41 @@ class WS2812FX {
|
||||
_mode[FX_MODE_NOISE16_2] = &WS2812FX::mode_noise16_2;
|
||||
_mode[FX_MODE_NOISE16_3] = &WS2812FX::mode_noise16_3;
|
||||
_mode[FX_MODE_NOISE16_4] = &WS2812FX::mode_noise16_4;
|
||||
_mode[FX_MODE_COLORTWINKLE] = &WS2812FX::mode_colortwinkle;
|
||||
_mode[FX_MODE_LAKE] = &WS2812FX::mode_lake;
|
||||
_mode[FX_MODE_METEOR] = &WS2812FX::mode_meteor;
|
||||
_mode[FX_MODE_METEOR_SMOOTH] = &WS2812FX::mode_meteor_smooth;
|
||||
_mode[FX_MODE_RAILWAY] = &WS2812FX::mode_railway;
|
||||
_mode[FX_MODE_RIPPLE] = &WS2812FX::mode_ripple;
|
||||
_mode[FX_MODE_TWINKLEFOX] = &WS2812FX::mode_twinklefox;
|
||||
_mode[FX_MODE_TWINKLECAT] = &WS2812FX::mode_twinklecat;
|
||||
_mode[FX_MODE_HALLOWEEN_EYES] = &WS2812FX::mode_halloween_eyes;
|
||||
|
||||
_brightness = DEFAULT_BRIGHTNESS;
|
||||
_running = false;
|
||||
_num_segments = 1;
|
||||
_segments[0].mode = DEFAULT_MODE;
|
||||
_segments[0].colors[0] = DEFAULT_COLOR;
|
||||
_segments[0].start = 0;
|
||||
_segments[0].speed = DEFAULT_SPEED;
|
||||
_reverseMode = false;
|
||||
_skipFirstMode = false;
|
||||
paletteFade = 0;
|
||||
paletteBlend = 0;
|
||||
_locked = NULL;
|
||||
_cronixieDigits = new byte[6];
|
||||
currentPalette = CRGBPalette16(CRGB::Black);
|
||||
targetPalette = CloudColors_p;
|
||||
ablMilliampsMax = 850;
|
||||
currentMilliamps = 0;
|
||||
timebase = 0;
|
||||
_locked = nullptr;
|
||||
_modeUsesLock = false;
|
||||
bus = new NeoPixelWrapper();
|
||||
RESET_RUNTIME;
|
||||
resetSegments();
|
||||
}
|
||||
|
||||
void
|
||||
init(bool supportWhite, uint16_t countPixels, bool skipFirst),
|
||||
init(bool supportWhite, uint16_t countPixels, bool skipFirs),
|
||||
service(void),
|
||||
clear(void),
|
||||
strip_off(void),
|
||||
blur(uint8_t),
|
||||
fade_out(uint8_t r),
|
||||
setMode(uint8_t m),
|
||||
setMode(uint8_t segid, uint8_t m),
|
||||
setSpeed(uint8_t s),
|
||||
setIntensity(uint8_t i),
|
||||
setPalette(uint8_t p),
|
||||
setColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
|
||||
setSecondaryColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
|
||||
setColor(uint32_t c),
|
||||
setSecondaryColor(uint32_t c),
|
||||
setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
|
||||
setColor(uint8_t slot, uint32_t c),
|
||||
setBrightness(uint8_t b),
|
||||
setReverseMode(bool b),
|
||||
driverModeCronixie(bool b),
|
||||
setCronixieDigits(byte* d),
|
||||
setCronixieBacklight(bool b),
|
||||
@@ -307,36 +346,49 @@ class WS2812FX {
|
||||
unlockAll(void),
|
||||
setTransitionMode(bool t),
|
||||
trigger(void),
|
||||
setNumSegments(uint8_t n),
|
||||
setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint8_t speed, uint8_t intensity, bool reverse),
|
||||
setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint8_t speed, uint8_t intensity, bool reverse),
|
||||
setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint8_t speed, uint8_t intensity, uint8_t options),
|
||||
setSegment(uint8_t n, uint16_t start, uint16_t stop),
|
||||
resetSegments(),
|
||||
setPixelColor(uint16_t n, uint32_t c),
|
||||
setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
|
||||
show(void);
|
||||
|
||||
bool
|
||||
reverseMode = false,
|
||||
gammaCorrectBri = false,
|
||||
gammaCorrectCol = true,
|
||||
setEffectConfig(uint8_t m, uint8_t s, uint8_t i, uint8_t p);
|
||||
|
||||
uint8_t
|
||||
paletteFade,
|
||||
paletteBlend,
|
||||
returnedSegment = 0,
|
||||
paletteFade = 0,
|
||||
paletteBlend = 0,
|
||||
colorOrder = 0,
|
||||
getBrightness(void),
|
||||
getMode(void),
|
||||
getSpeed(void),
|
||||
getNumSegments(void),
|
||||
getModeCount(void),
|
||||
getPaletteCount(void),
|
||||
getMaxSegments(void),
|
||||
getFirstSelectedSegment(void),
|
||||
getReturnedSegmentId(void),
|
||||
gamma8(uint8_t),
|
||||
get_random_wheel_index(uint8_t);
|
||||
|
||||
uint16_t
|
||||
ablMilliampsMax,
|
||||
currentMilliamps;
|
||||
|
||||
uint32_t
|
||||
timebase,
|
||||
color_wheel(uint8_t),
|
||||
color_from_palette(uint16_t, bool, bool, uint8_t, uint8_t pbri = 255),
|
||||
color_blend(uint32_t,uint32_t,uint8_t),
|
||||
gamma32(uint32_t),
|
||||
getPixelColor(uint16_t),
|
||||
getColor(void);
|
||||
|
||||
double
|
||||
getPowerEstimate(uint16_t leds, uint32_t c, byte b),
|
||||
getSafePowerMultiplier(double safeMilliAmps, uint16_t leds, uint32_t c, byte b);
|
||||
|
||||
WS2812FX::Segment
|
||||
getSegment(void);
|
||||
WS2812FX::Segment&
|
||||
getSegment(uint8_t n);
|
||||
|
||||
WS2812FX::Segment_runtime
|
||||
getSegmentRuntime(void);
|
||||
@@ -344,18 +396,6 @@ class WS2812FX {
|
||||
WS2812FX::Segment*
|
||||
getSegments(void);
|
||||
|
||||
// mode helper functions
|
||||
uint16_t
|
||||
blink(uint32_t, uint32_t, bool strobe),
|
||||
color_wipe(uint32_t, uint32_t, bool),
|
||||
theater_chase(uint32_t, uint32_t),
|
||||
twinkle(uint32_t),
|
||||
twinkle_fade(uint32_t),
|
||||
chase(uint32_t, uint32_t, uint32_t),
|
||||
running(uint32_t, uint32_t),
|
||||
fireworks(uint32_t),
|
||||
tricolor_chase(uint32_t, uint32_t, uint32_t);
|
||||
|
||||
// builtin modes
|
||||
uint16_t
|
||||
mode_static(void),
|
||||
@@ -378,10 +418,10 @@ class WS2812FX {
|
||||
mode_rainbow(void),
|
||||
mode_rainbow_cycle(void),
|
||||
mode_running_lights(void),
|
||||
mode_saw(void),
|
||||
mode_twinkle(void),
|
||||
mode_twinkle_random(void),
|
||||
mode_twinkle_fade(void),
|
||||
mode_twinkle_fade_random(void),
|
||||
mode_dissolve(void),
|
||||
mode_dissolve_random(void),
|
||||
mode_sparkle(void),
|
||||
mode_flash_sparkle(void),
|
||||
mode_hyper_sparkle(void),
|
||||
@@ -401,7 +441,7 @@ class WS2812FX {
|
||||
mode_larson_scanner(void),
|
||||
mode_comet(void),
|
||||
mode_fireworks(void),
|
||||
mode_fireworks_random(void),
|
||||
mode_rain(void),
|
||||
mode_merry_christmas(void),
|
||||
mode_halloween(void),
|
||||
mode_fire_flicker(void),
|
||||
@@ -416,6 +456,7 @@ class WS2812FX {
|
||||
mode_tricolor_chase(void),
|
||||
mode_tricolor_wipe(void),
|
||||
mode_tricolor_fade(void),
|
||||
mode_lightning(void),
|
||||
mode_icu(void),
|
||||
mode_multi_comet(void),
|
||||
mode_dual_larson_scanner(void),
|
||||
@@ -432,45 +473,95 @@ class WS2812FX {
|
||||
mode_noise16_2(void),
|
||||
mode_noise16_3(void),
|
||||
mode_noise16_4(void),
|
||||
mode_lightning(void);
|
||||
mode_colortwinkle(void),
|
||||
mode_lake(void),
|
||||
mode_meteor(void),
|
||||
mode_meteor_smooth(void),
|
||||
mode_railway(void),
|
||||
mode_ripple(void),
|
||||
mode_twinklefox(void),
|
||||
mode_twinklecat(void),
|
||||
mode_halloween_eyes(void);
|
||||
|
||||
private:
|
||||
NeoPixelWrapper *bus;
|
||||
|
||||
|
||||
uint32_t crgb_to_col(CRGB fastled);
|
||||
CRGB col_to_crgb(uint32_t);
|
||||
CRGBPalette16 currentPalette;
|
||||
CRGBPalette16 targetPalette;
|
||||
|
||||
uint32_t now;
|
||||
uint16_t _length;
|
||||
uint16_t _rand16seed;
|
||||
uint8_t _brightness;
|
||||
|
||||
void handle_palette(void);
|
||||
void fill(uint32_t);
|
||||
bool modeUsesLock(uint8_t);
|
||||
void twinklefox_base(bool);
|
||||
|
||||
double
|
||||
_cronixieSecMultiplier;
|
||||
|
||||
boolean
|
||||
_running,
|
||||
bool
|
||||
_modeUsesLock,
|
||||
_rgbwMode,
|
||||
_reverseMode,
|
||||
_cronixieMode,
|
||||
_cronixieBacklightEnabled,
|
||||
_skipFirstMode,
|
||||
_triggered;
|
||||
|
||||
byte* _locked;
|
||||
byte* _cronixieDigits;
|
||||
byte _cronixieDigits[6];
|
||||
|
||||
mode_ptr _mode[MODE_COUNT]; // SRAM footprint: 4 bytes per element
|
||||
|
||||
// mode helper functions
|
||||
uint16_t
|
||||
blink(uint32_t, uint32_t, bool strobe, bool),
|
||||
color_wipe(bool, bool),
|
||||
scan(bool),
|
||||
theater_chase(uint32_t, uint32_t, bool),
|
||||
running_base(bool),
|
||||
dissolve(uint32_t),
|
||||
chase(uint32_t, uint32_t, uint32_t, bool),
|
||||
gradient_base(bool),
|
||||
running(uint32_t, uint32_t),
|
||||
tricolor_chase(uint32_t, uint32_t);
|
||||
|
||||
CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat);
|
||||
|
||||
uint32_t _lastPaletteChange = 0;
|
||||
uint32_t _lastShow = 0;
|
||||
|
||||
uint8_t _segment_index = 0;
|
||||
uint8_t _segment_index_palette_last = 99;
|
||||
uint8_t _num_segments = 1;
|
||||
segment _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 20 bytes per element
|
||||
// start, stop, speed, intensity, mode, options, color[]
|
||||
{ 0, 7, DEFAULT_SPEED, 128, FX_MODE_STATIC, NO_OPTIONS, {DEFAULT_COLOR}}
|
||||
segment _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 21 bytes per element
|
||||
// start, stop, speed, intensity, palette, mode, options, color[]
|
||||
{ 0, 7, DEFAULT_SPEED, 128, 0, DEFAULT_MODE, NO_OPTIONS, {DEFAULT_COLOR}}
|
||||
};
|
||||
segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 17 bytes per element
|
||||
segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 16 bytes per element
|
||||
};
|
||||
|
||||
|
||||
//10 names per line
|
||||
const char JSON_mode_names[] PROGMEM = R"=====([
|
||||
"Solid","Blink","Breathe","Wipe","Wipe Random","Random Colors","Sweep","Dynamic","Colorloop","Rainbow",
|
||||
"Scan","Dual Scan","Fade","Chase","Chase Rainbow","Running","Saw","Twinkle","Dissolve","Dissolve Rnd",
|
||||
"Sparkle","Dark Sparkle","Sparkle+","Strobe","Strobe Rainbow","Mega Strobe","Blink Rainbow","Android","Chase","Chase Random",
|
||||
"Chase Rainbow","Chase Flash","Chase Flash Rnd","Rainbow Runner","Colorful","Traffic Light","Sweep Random","Running 2","Red & Blue","Stream",
|
||||
"Scanner","Lighthouse","Fireworks","Rain","Merry Christmas","Fire Flicker","Gradient","Loading","In Out","In In",
|
||||
"Out Out","Out In","Circus","Halloween","Tri Chase","Tri Wipe","Tri Fade","Lightning","ICU","Multi Comet",
|
||||
"Dual Scanner","Stream 2","Oscillate","Pride 2015","Juggle","Palette","Fire 2012","Colorwaves","BPM","Fill Noise",
|
||||
"Noise 1","Noise 2","Noise 3","Noise 4","Colortwinkles","Lake","Meteor","Smooth Meteor","Railway","Ripple",
|
||||
"Twinklefox","Twinklecat","Halloween Eyes"
|
||||
])=====";
|
||||
|
||||
|
||||
const char JSON_palette_names[] PROGMEM = R"=====([
|
||||
"Default","Random Cycle","Primary Color","Based on Primary","Set Colors","Based on Set","Party","Cloud","Lava","Ocean",
|
||||
"Forest","Rainbow","Rainbow Bands","Sunset","Rivendell","Breeze","Red & Blue","Yellowout","Analogous","Splash",
|
||||
"Pastel","Sunset 2","Beech","Vintage","Departure","Landscape","Beach","Sherbet","Hult","Hult 64",
|
||||
"Drywet","Jul","Grintage","Rewhi","Tertiary","Fire","Icefire","Cyane","Light Pink","Autumn",
|
||||
"Magenta","Magred","Yelmag","Yelblu","Orange & Teal","Tiamat","April Night","Orangery","C9","Sakura"
|
||||
])=====";
|
||||
|
||||
#endif
|
||||
825
wled00/FX_fcn.cpp
Normal file
825
wled00/FX_fcn.cpp
Normal file
@@ -0,0 +1,825 @@
|
||||
/*
|
||||
WS2812FX_fcn.cpp contains all utility functions
|
||||
Harm Aldick - 2016
|
||||
www.aldick.org
|
||||
LICENSE
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2016 Harm Aldick
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
Modified heavily for WLED
|
||||
*/
|
||||
|
||||
#include "FX.h"
|
||||
#include "palettes.h"
|
||||
|
||||
#define LED_SKIP_AMOUNT 1
|
||||
#define MIN_SHOW_DELAY 15
|
||||
|
||||
void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst)
|
||||
{
|
||||
if (supportWhite == _rgbwMode && countPixels == _length && _locked != NULL) return;
|
||||
RESET_RUNTIME;
|
||||
_rgbwMode = supportWhite;
|
||||
_skipFirstMode = skipFirst;
|
||||
_length = countPixels;
|
||||
|
||||
uint8_t ty = 1;
|
||||
if (supportWhite) ty =2;
|
||||
uint16_t lengthRaw = _length;
|
||||
if (_skipFirstMode) lengthRaw += LED_SKIP_AMOUNT;
|
||||
bus->Begin((NeoPixelType)ty, lengthRaw);
|
||||
|
||||
delete[] _locked;
|
||||
_locked = new byte[_length];
|
||||
|
||||
_segments[0].start = 0;
|
||||
_segments[0].stop = _length;
|
||||
|
||||
unlockAll();
|
||||
setBrightness(_brightness);
|
||||
}
|
||||
|
||||
void WS2812FX::service() {
|
||||
now = millis() + timebase; // Be aware, millis() rolls over every 49 days
|
||||
if (now - _lastShow < MIN_SHOW_DELAY) return;
|
||||
bool doShow = false;
|
||||
for(uint8_t i=0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
_segment_index = i;
|
||||
if (SEGMENT.isActive())
|
||||
{
|
||||
if(now > SEGENV.next_time || _triggered || (doShow && SEGMENT.mode == 0)) //last is temporary
|
||||
{
|
||||
doShow = true;
|
||||
handle_palette();
|
||||
uint16_t delay = (this->*_mode[SEGMENT.mode])();
|
||||
SEGENV.next_time = now + delay;
|
||||
if (SEGMENT.mode != FX_MODE_HALLOWEEN_EYES) SEGENV.call++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(doShow) {
|
||||
yield();
|
||||
show();
|
||||
_lastShow = millis();
|
||||
}
|
||||
_triggered = false;
|
||||
}
|
||||
|
||||
bool WS2812FX::modeUsesLock(uint8_t m)
|
||||
{
|
||||
if (m == FX_MODE_FIRE_2012 || m == FX_MODE_COLORTWINKLE ||
|
||||
m == FX_MODE_METEOR || m == FX_MODE_METEOR_SMOOTH ||
|
||||
m == FX_MODE_RIPPLE || m == FX_MODE_DYNAMIC ) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void WS2812FX::setPixelColor(uint16_t n, uint32_t c) {
|
||||
uint8_t w = (c >> 24) & 0xFF;
|
||||
uint8_t r = (c >> 16) & 0xFF;
|
||||
uint8_t g = (c >> 8) & 0xFF;
|
||||
uint8_t b = c & 0xFF;
|
||||
setPixelColor(n, r, g, b, w);
|
||||
}
|
||||
|
||||
void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
|
||||
{
|
||||
if (_locked[i] && !_modeUsesLock) return;
|
||||
if (IS_REVERSE) i = SEGMENT.stop -1 -i - SEGMENT.start; //reverse just individual segment
|
||||
byte tmpg = g;
|
||||
switch (colorOrder) //0 = Grb, default
|
||||
{
|
||||
case 0: break; //0 = Grb, default
|
||||
case 1: g = r; r = tmpg; break; //1 = Rgb, common for WS2811
|
||||
case 2: g = b; b = tmpg; break; //2 = Brg
|
||||
case 3: g = b; b = r; r = tmpg; //3 = Rbg
|
||||
}
|
||||
if (!_cronixieMode)
|
||||
{
|
||||
if (reverseMode) i = _length -1 -i;
|
||||
if (_skipFirstMode)
|
||||
{
|
||||
if (i < LED_SKIP_AMOUNT) bus->SetPixelColor(i, RgbwColor(0,0,0,0));
|
||||
i += LED_SKIP_AMOUNT;
|
||||
}
|
||||
|
||||
bus->SetPixelColor(i, RgbwColor(r,g,b,w));
|
||||
} else {
|
||||
if(i>6)return;
|
||||
byte o = 10*i;
|
||||
if (_cronixieBacklightEnabled && _cronixieDigits[i] <11)
|
||||
{
|
||||
byte r2 = (_segments[0].colors[1] >>16) & 0xFF;
|
||||
byte g2 = (_segments[0].colors[1] >> 8) & 0xFF;
|
||||
byte b2 = (_segments[0].colors[1] ) & 0xFF;
|
||||
byte w2 = (_segments[0].colors[1] >>24) & 0xFF;
|
||||
for (int j=o; j< o+19; j++)
|
||||
{
|
||||
bus->SetPixelColor(j, RgbwColor(r2,g2,b2,w2));
|
||||
}
|
||||
} else
|
||||
{
|
||||
for (int j=o; j< o+19; j++)
|
||||
{
|
||||
bus->SetPixelColor(j, RgbwColor(0,0,0,0));
|
||||
}
|
||||
}
|
||||
if (_skipFirstMode) o += LED_SKIP_AMOUNT;
|
||||
switch(_cronixieDigits[i])
|
||||
{
|
||||
case 0: bus->SetPixelColor(o+5, RgbwColor(r,g,b,w)); break;
|
||||
case 1: bus->SetPixelColor(o+0, RgbwColor(r,g,b,w)); break;
|
||||
case 2: bus->SetPixelColor(o+6, RgbwColor(r,g,b,w)); break;
|
||||
case 3: bus->SetPixelColor(o+1, RgbwColor(r,g,b,w)); break;
|
||||
case 4: bus->SetPixelColor(o+7, RgbwColor(r,g,b,w)); break;
|
||||
case 5: bus->SetPixelColor(o+2, RgbwColor(r,g,b,w)); break;
|
||||
case 6: bus->SetPixelColor(o+8, RgbwColor(r,g,b,w)); break;
|
||||
case 7: bus->SetPixelColor(o+3, RgbwColor(r,g,b,w)); break;
|
||||
case 8: bus->SetPixelColor(o+9, RgbwColor(r,g,b,w)); break;
|
||||
case 9: bus->SetPixelColor(o+4, RgbwColor(r,g,b,w)); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WS2812FX::driverModeCronixie(bool b)
|
||||
{
|
||||
_cronixieMode = b;
|
||||
_segments[0].stop = (b) ? 6 : _length;
|
||||
}
|
||||
|
||||
void WS2812FX::setCronixieBacklight(bool b)
|
||||
{
|
||||
_cronixieBacklightEnabled = b;
|
||||
}
|
||||
|
||||
void WS2812FX::setCronixieDigits(byte d[])
|
||||
{
|
||||
for (int i = 0; i<6; i++)
|
||||
{
|
||||
_cronixieDigits[i] = d[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//DISCLAIMER
|
||||
//The following function attemps to calculate the current LED power usage,
|
||||
//and will limit the brightness to stay below a set amperage threshold.
|
||||
//It is NOT a measurement and NOT guaranteed to stay within the ablMilliampsMax margin.
|
||||
//Stay safe with high amperage and have a reasonable safety margin!
|
||||
//I am NOT to be held liable for burned down garages!
|
||||
|
||||
//fine tune power estimation constants for your setup
|
||||
#define PU_PER_MA 3600 //power units per milliamperere for accurate power estimation
|
||||
//formula: 195075 divided by mA per fully lit LED, here ~54mA)
|
||||
//lowering the value increases the estimated usage and therefore makes the ABL more aggressive
|
||||
|
||||
#define MA_FOR_ESP 100 //how much mA does the ESP use (Wemos D1 about 80mA, ESP32 about 120mA)
|
||||
//you can set it to 0 if the ESP is powered by USB and the LEDs by external
|
||||
|
||||
void WS2812FX::show(void) {
|
||||
//power limit calculation
|
||||
//each LED can draw up 195075 "power units" (approx. 53mA)
|
||||
//one PU is the power it takes to have 1 channel 1 step brighter per brightness step
|
||||
//so A=2,R=255,G=0,B=0 would use 510 PU per LED (1mA is about 3700 PU)
|
||||
|
||||
if (ablMilliampsMax > 149 && ablMilliampsMax < 65000) //lower numbers and 65000 turn off calculation
|
||||
{
|
||||
uint32_t powerBudget = (ablMilliampsMax - MA_FOR_ESP) * PU_PER_MA; //100mA for ESP power
|
||||
if (powerBudget > PU_PER_MA * _length) //each LED uses about 1mA in standby, exclude that from power budget
|
||||
{
|
||||
powerBudget -= PU_PER_MA * _length;
|
||||
} else
|
||||
{
|
||||
powerBudget = 0;
|
||||
}
|
||||
|
||||
uint32_t powerSum = 0;
|
||||
|
||||
for (uint16_t i = 0; i < _length; i++) //sum up the usage of each LED
|
||||
{
|
||||
RgbwColor c = bus->GetPixelColorRgbw(i);
|
||||
powerSum += (c.R + c.G + c.B + c.W);
|
||||
}
|
||||
|
||||
if (_rgbwMode) //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
|
||||
{
|
||||
powerSum *= 3;
|
||||
powerSum = powerSum >> 2; //same as /= 4
|
||||
}
|
||||
|
||||
uint32_t powerSum0 = powerSum;
|
||||
powerSum *= _brightness;
|
||||
|
||||
if (powerSum > powerBudget) //scale brightness down to stay in current limit
|
||||
{
|
||||
float scale = (float)powerBudget / (float)powerSum;
|
||||
uint16_t scaleI = scale * 255;
|
||||
uint8_t scaleB = (scaleI > 255) ? 255 : scaleI;
|
||||
uint8_t newBri = scale8(_brightness, scaleB);
|
||||
bus->SetBrightness(newBri);
|
||||
currentMilliamps = (powerSum0 * newBri) / PU_PER_MA;
|
||||
} else
|
||||
{
|
||||
currentMilliamps = powerSum / PU_PER_MA;
|
||||
bus->SetBrightness(_brightness);
|
||||
}
|
||||
currentMilliamps += MA_FOR_ESP; //add power of ESP back to estimate
|
||||
currentMilliamps += _length; //add standby power back to estimate
|
||||
} else {
|
||||
currentMilliamps = 0;
|
||||
bus->SetBrightness(_brightness);
|
||||
}
|
||||
|
||||
bus->Show();
|
||||
}
|
||||
|
||||
void WS2812FX::trigger() {
|
||||
_triggered = true;
|
||||
}
|
||||
|
||||
void WS2812FX::setMode(uint8_t segid, uint8_t m) {
|
||||
if (segid >= MAX_NUM_SEGMENTS) return;
|
||||
|
||||
bool anyUsedLock = _modeUsesLock, anyUseLock = false;
|
||||
if (m >= MODE_COUNT) m = MODE_COUNT - 1;
|
||||
|
||||
if (_segments[segid].mode != m)
|
||||
{
|
||||
_segment_runtimes[segid].reset();
|
||||
_segments[segid].mode = m;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
if (modeUsesLock(_segments[i].mode)) anyUseLock = true;
|
||||
}
|
||||
if (anyUsedLock && !anyUseLock) unlockAll();
|
||||
_modeUsesLock = anyUseLock;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getModeCount()
|
||||
{
|
||||
return MODE_COUNT;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getPaletteCount()
|
||||
{
|
||||
return 13 + gGradientPaletteCount;
|
||||
}
|
||||
|
||||
//TODO transitions
|
||||
|
||||
void WS2812FX::setMode(uint8_t m) {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
if (_segments[i].isSelected()) setMode(i, m);
|
||||
}
|
||||
}
|
||||
|
||||
void WS2812FX::setSpeed(uint8_t s) {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
if (_segments[i].isSelected()) _segments[i].speed = s;
|
||||
}
|
||||
}
|
||||
|
||||
void WS2812FX::setIntensity(uint8_t in) {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
if (_segments[i].isSelected()) _segments[i].intensity = in;
|
||||
}
|
||||
}
|
||||
|
||||
void WS2812FX::setPalette(uint8_t p) {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
if (_segments[i].isSelected()) _segments[i].palette = p;
|
||||
}
|
||||
}
|
||||
|
||||
bool WS2812FX::setEffectConfig(uint8_t m, uint8_t s, uint8_t in, uint8_t p) {
|
||||
uint8_t retSeg = getReturnedSegmentId();
|
||||
Segment& seg = _segments[retSeg];
|
||||
uint8_t modePrev = seg.mode, speedPrev = seg.speed, intensityPrev = seg.intensity, palettePrev = seg.palette;
|
||||
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
if (_segments[i].isSelected())
|
||||
{
|
||||
_segments[i].speed = s;
|
||||
_segments[i].intensity = in;
|
||||
_segments[i].palette = p;
|
||||
setMode(i, m);
|
||||
}
|
||||
}
|
||||
|
||||
if (seg.mode != modePrev || seg.speed != speedPrev || seg.intensity != intensityPrev || seg.palette != palettePrev) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void WS2812FX::setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
|
||||
setColor(slot, ((uint32_t)w << 24) |((uint32_t)r << 16) | ((uint32_t)g << 8) | b);
|
||||
}
|
||||
|
||||
void WS2812FX::setColor(uint8_t slot, uint32_t c) {
|
||||
if (slot >= NUM_COLORS) return;
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
if (_segments[i].isSelected()) _segments[i].colors[slot] = c;
|
||||
}
|
||||
}
|
||||
|
||||
void WS2812FX::setBrightness(uint8_t b) {
|
||||
if (_brightness == b) return;
|
||||
_brightness = (gammaCorrectBri) ? gamma8(b) : b;
|
||||
_segment_index = 0;
|
||||
if (SEGENV.next_time > millis() + 22) show();//apply brightness change immediately if no refresh soon
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getMode(void) {
|
||||
return _segments[getReturnedSegmentId()].mode;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getSpeed(void) {
|
||||
return _segments[getReturnedSegmentId()].speed;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getBrightness(void) {
|
||||
return _brightness;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getMaxSegments(void) {
|
||||
return MAX_NUM_SEGMENTS;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getFirstSelectedSegment(void)
|
||||
{
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
if (_segments[i].isActive() && _segments[i].isSelected()) return i;
|
||||
}
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) //if none selected, get first active
|
||||
{
|
||||
if (_segments[i].isActive()) return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getReturnedSegmentId(void) {
|
||||
if (returnedSegment >= MAX_NUM_SEGMENTS || !_segments[returnedSegment].isActive())
|
||||
{
|
||||
return getFirstSelectedSegment();
|
||||
}
|
||||
return returnedSegment;
|
||||
}
|
||||
|
||||
uint32_t WS2812FX::getColor(void) {
|
||||
return _segments[getReturnedSegmentId()].colors[0];
|
||||
}
|
||||
|
||||
uint32_t WS2812FX::getPixelColor(uint16_t i)
|
||||
{
|
||||
if (reverseMode) i = _length- 1 -i;
|
||||
if (IS_REVERSE) i = SEGMENT.stop -1 -i - SEGMENT.start; //reverse just individual segment
|
||||
if (_skipFirstMode) i += LED_SKIP_AMOUNT;
|
||||
if (_cronixieMode)
|
||||
{
|
||||
if(i>6)return 0;
|
||||
byte o = 10*i;
|
||||
switch(_cronixieDigits[i])
|
||||
{
|
||||
case 0: i=o+5; break;
|
||||
case 1: i=o+0; break;
|
||||
case 2: i=o+6; break;
|
||||
case 3: i=o+1; break;
|
||||
case 4: i=o+7; break;
|
||||
case 5: i=o+2; break;
|
||||
case 6: i=o+8; break;
|
||||
case 7: i=o+3; break;
|
||||
case 8: i=o+9; break;
|
||||
case 9: i=o+4; break;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
RgbwColor lColor = bus->GetPixelColorRgbw(i);
|
||||
byte r = lColor.R, g = lColor.G, b = lColor.B;
|
||||
switch (colorOrder)
|
||||
{
|
||||
case 0: break; //0 = Grb
|
||||
case 1: r = lColor.G; g = lColor.R; break; //1 = Rgb, common for WS2811
|
||||
case 2: g = lColor.B; b = lColor.G; break; //2 = Brg
|
||||
case 3: r = lColor.B; g = lColor.R; b = lColor.G; //3 = Rbg
|
||||
}
|
||||
return ( (lColor.W << 24) | (r << 16) | (g << 8) | (b) );
|
||||
}
|
||||
|
||||
WS2812FX::Segment& WS2812FX::getSegment(uint8_t id) {
|
||||
if (id >= MAX_NUM_SEGMENTS) return _segments[0];
|
||||
return _segments[id];
|
||||
}
|
||||
|
||||
WS2812FX::Segment_runtime WS2812FX::getSegmentRuntime(void) {
|
||||
return SEGENV;
|
||||
}
|
||||
|
||||
WS2812FX::Segment* WS2812FX::getSegments(void) {
|
||||
return _segments;
|
||||
}
|
||||
|
||||
void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2) {
|
||||
if (n >= MAX_NUM_SEGMENTS) return;
|
||||
Segment& seg = _segments[n];
|
||||
if (seg.start == i1 && seg.stop == i2) return;
|
||||
if (seg.isActive() && modeUsesLock(seg.mode))
|
||||
{
|
||||
_modeUsesLock = false;
|
||||
unlockRange(seg.start, seg.stop);
|
||||
_modeUsesLock = true;
|
||||
}
|
||||
if (i2 <= i1) //disable segment
|
||||
{
|
||||
seg.stop = 0; return;
|
||||
}
|
||||
if (i1 < _length) seg.start = i1;
|
||||
seg.stop = i2;
|
||||
if (i2 > _length) seg.stop = _length;
|
||||
_segment_runtimes[n].reset();
|
||||
}
|
||||
|
||||
void WS2812FX::resetSegments() {
|
||||
memset(_segments, 0, sizeof(_segments));
|
||||
memset(_segment_runtimes, 0, sizeof(_segment_runtimes));
|
||||
_segment_index = 0;
|
||||
_segments[0].mode = DEFAULT_MODE;
|
||||
_segments[0].colors[0] = DEFAULT_COLOR;
|
||||
_segments[0].start = 0;
|
||||
_segments[0].speed = DEFAULT_SPEED;
|
||||
_segments[0].stop = _length;
|
||||
_segments[0].setOption(0, 1); //select
|
||||
}
|
||||
|
||||
void WS2812FX::setIndividual(uint16_t i, uint32_t col)
|
||||
{
|
||||
if (modeUsesLock(SEGMENT.mode)) return;
|
||||
if (i >= 0 && i < _length)
|
||||
{
|
||||
_locked[i] = false;
|
||||
setPixelColor(i, col);
|
||||
_locked[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col)
|
||||
{
|
||||
if (i2 >= i)
|
||||
{
|
||||
for (uint16_t x = i; x <= i2; x++) setIndividual(x,col);
|
||||
} else
|
||||
{
|
||||
for (uint16_t x = i2; x <= i; x++) setIndividual(x,col);
|
||||
}
|
||||
}
|
||||
|
||||
void WS2812FX::lock(uint16_t i)
|
||||
{
|
||||
if (_modeUsesLock) return;
|
||||
if (i < _length) _locked[i] = true;
|
||||
}
|
||||
|
||||
void WS2812FX::lockRange(uint16_t i, uint16_t i2)
|
||||
{
|
||||
if (_modeUsesLock) return;
|
||||
for (uint16_t x = i; x < i2; x++)
|
||||
{
|
||||
if (x < _length) _locked[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void WS2812FX::unlock(uint16_t i)
|
||||
{
|
||||
if (_modeUsesLock) return;
|
||||
if (i < _length) _locked[i] = false;
|
||||
}
|
||||
|
||||
void WS2812FX::unlockRange(uint16_t i, uint16_t i2)
|
||||
{
|
||||
if (_modeUsesLock) return;
|
||||
for (uint16_t x = i; x < i2; x++)
|
||||
{
|
||||
if (x < _length) _locked[x] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void WS2812FX::unlockAll()
|
||||
{
|
||||
for (int i=0; i < _length; i++) _locked[i] = false;
|
||||
}
|
||||
|
||||
void WS2812FX::setTransitionMode(bool t)
|
||||
{
|
||||
_segment_index = 0;
|
||||
SEGMENT.setOption(7,t);
|
||||
if (!t) return;
|
||||
unsigned long waitMax = millis() + 20; //refresh after 20 ms if transition enabled
|
||||
if (SEGMENT.mode == FX_MODE_STATIC && SEGENV.next_time > waitMax) SEGENV.next_time = waitMax;
|
||||
}
|
||||
|
||||
/*
|
||||
* color blend function
|
||||
*/
|
||||
uint32_t WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint8_t blend) {
|
||||
if(blend == 0) return color1;
|
||||
if(blend == 255) return color2;
|
||||
|
||||
uint32_t w1 = (color1 >> 24) & 0xff;
|
||||
uint32_t r1 = (color1 >> 16) & 0xff;
|
||||
uint32_t g1 = (color1 >> 8) & 0xff;
|
||||
uint32_t b1 = color1 & 0xff;
|
||||
|
||||
uint32_t w2 = (color2 >> 24) & 0xff;
|
||||
uint32_t r2 = (color2 >> 16) & 0xff;
|
||||
uint32_t g2 = (color2 >> 8) & 0xff;
|
||||
uint32_t b2 = color2 & 0xff;
|
||||
|
||||
uint32_t w3 = ((w2 * blend) + (w1 * (255 - blend))) >> 8;
|
||||
uint32_t r3 = ((r2 * blend) + (r1 * (255 - blend))) >> 8;
|
||||
uint32_t g3 = ((g2 * blend) + (g1 * (255 - blend))) >> 8;
|
||||
uint32_t b3 = ((b2 * blend) + (b1 * (255 - blend))) >> 8;
|
||||
|
||||
return ((w3 << 24) | (r3 << 16) | (g3 << 8) | (b3));
|
||||
}
|
||||
|
||||
/*
|
||||
* Fills segment with color
|
||||
*/
|
||||
void WS2812FX::fill(uint32_t c) {
|
||||
for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) {
|
||||
setPixelColor(i, c);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* fade out function, higher rate = quicker fade
|
||||
*/
|
||||
void WS2812FX::fade_out(uint8_t rate) {
|
||||
rate = (255-rate) >> 1;
|
||||
float mappedRate = float(rate) +1.1;
|
||||
|
||||
uint32_t color = SEGCOLOR(1); // target color
|
||||
int w2 = (color >> 24) & 0xff;
|
||||
int r2 = (color >> 16) & 0xff;
|
||||
int g2 = (color >> 8) & 0xff;
|
||||
int b2 = color & 0xff;
|
||||
|
||||
for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) {
|
||||
color = getPixelColor(i);
|
||||
int w1 = (color >> 24) & 0xff;
|
||||
int r1 = (color >> 16) & 0xff;
|
||||
int g1 = (color >> 8) & 0xff;
|
||||
int b1 = color & 0xff;
|
||||
|
||||
int wdelta = (w2 - w1) / mappedRate;
|
||||
int rdelta = (r2 - r1) / mappedRate;
|
||||
int gdelta = (g2 - g1) / mappedRate;
|
||||
int bdelta = (b2 - b1) / mappedRate;
|
||||
|
||||
// if fade isn't complete, make sure delta is at least 1 (fixes rounding issues)
|
||||
wdelta += (w2 == w1) ? 0 : (w2 > w1) ? 1 : -1;
|
||||
rdelta += (r2 == r1) ? 0 : (r2 > r1) ? 1 : -1;
|
||||
gdelta += (g2 == g1) ? 0 : (g2 > g1) ? 1 : -1;
|
||||
bdelta += (b2 == b1) ? 0 : (b2 > b1) ? 1 : -1;
|
||||
|
||||
setPixelColor(i, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* blurs segment content, source: FastLED colorutils.cpp
|
||||
*/
|
||||
void WS2812FX::blur(uint8_t blur_amount)
|
||||
{
|
||||
uint8_t keep = 255 - blur_amount;
|
||||
uint8_t seep = blur_amount >> 1;
|
||||
CRGB carryover = CRGB::Black;
|
||||
for(uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++)
|
||||
{
|
||||
CRGB cur = col_to_crgb(getPixelColor(i));
|
||||
CRGB part = cur;
|
||||
part.nscale8(seep);
|
||||
cur.nscale8(keep);
|
||||
cur += carryover;
|
||||
if(i > SEGMENT.start) {
|
||||
uint32_t c = getPixelColor(i-1);
|
||||
uint8_t r = (c >> 16 & 0xFF);
|
||||
uint8_t g = (c >> 8 & 0xFF);
|
||||
uint8_t b = (c & 0xFF);
|
||||
setPixelColor(i-1, qadd8(r, part.red), qadd8(g, part.green), qadd8(b, part.blue));
|
||||
}
|
||||
setPixelColor(i,cur.red, cur.green, cur.blue);
|
||||
carryover = part;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Put a value 0 to 255 in to get a color value.
|
||||
* The colours are a transition r -> g -> b -> back to r
|
||||
* Inspired by the Adafruit examples.
|
||||
*/
|
||||
uint32_t WS2812FX::color_wheel(uint8_t pos) {
|
||||
if (SEGMENT.palette) return color_from_palette(pos, false, true, 0);
|
||||
pos = 255 - pos;
|
||||
if(pos < 85) {
|
||||
return ((uint32_t)(255 - pos * 3) << 16) | ((uint32_t)(0) << 8) | (pos * 3);
|
||||
} else if(pos < 170) {
|
||||
pos -= 85;
|
||||
return ((uint32_t)(0) << 16) | ((uint32_t)(pos * 3) << 8) | (255 - pos * 3);
|
||||
} else {
|
||||
pos -= 170;
|
||||
return ((uint32_t)(pos * 3) << 16) | ((uint32_t)(255 - pos * 3) << 8) | (0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a new, random wheel index with a minimum distance of 42 from pos.
|
||||
*/
|
||||
uint8_t WS2812FX::get_random_wheel_index(uint8_t pos) {
|
||||
uint8_t r = 0, x = 0, y = 0, d = 0;
|
||||
|
||||
while(d < 42) {
|
||||
r = random8();
|
||||
x = abs(pos - r);
|
||||
y = 255 - x;
|
||||
d = min(x, y);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
uint32_t WS2812FX::crgb_to_col(CRGB fastled)
|
||||
{
|
||||
return (((uint32_t)fastled.red << 16) | ((uint32_t)fastled.green << 8) | fastled.blue);
|
||||
}
|
||||
|
||||
|
||||
CRGB WS2812FX::col_to_crgb(uint32_t color)
|
||||
{
|
||||
CRGB fastled_col;
|
||||
fastled_col.red = (color >> 16 & 0xFF);
|
||||
fastled_col.green = (color >> 8 & 0xFF);
|
||||
fastled_col.blue = (color & 0xFF);
|
||||
return fastled_col;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* FastLED palette modes helper function. Limitation: Due to memory reasons, multiple active segments with FastLED will disable the Palette transitions
|
||||
*/
|
||||
void WS2812FX::handle_palette(void)
|
||||
{
|
||||
bool singleSegmentMode = (_segment_index == _segment_index_palette_last);
|
||||
_segment_index_palette_last = _segment_index;
|
||||
|
||||
byte paletteIndex = SEGMENT.palette;
|
||||
if ((SEGMENT.mode >= FX_MODE_METEOR) && SEGMENT.palette == 0) paletteIndex = 4;
|
||||
|
||||
switch (paletteIndex)
|
||||
{
|
||||
case 0: {//default palette. Differs depending on effect
|
||||
switch (SEGMENT.mode)
|
||||
{
|
||||
case FX_MODE_FIRE_2012 : targetPalette = gGradientPalettes[22]; break;//heat palette
|
||||
case FX_MODE_COLORWAVES : targetPalette = gGradientPalettes[13]; break;//landscape 33
|
||||
case FX_MODE_FILLNOISE8 : targetPalette = OceanColors_p; break;
|
||||
case FX_MODE_NOISE16_1 : targetPalette = gGradientPalettes[17]; break;//Drywet
|
||||
case FX_MODE_NOISE16_2 : targetPalette = gGradientPalettes[30]; break;//Blue cyan yellow
|
||||
case FX_MODE_NOISE16_3 : targetPalette = gGradientPalettes[22]; break;//heat palette
|
||||
case FX_MODE_NOISE16_4 : targetPalette = gGradientPalettes[13]; break;//landscape 33
|
||||
|
||||
default: targetPalette = PartyColors_p; break;//palette, bpm
|
||||
}
|
||||
break;}
|
||||
case 1: {//periodically replace palette with a random one. Doesn't work with multiple FastLED segments
|
||||
if (!singleSegmentMode)
|
||||
{
|
||||
targetPalette = PartyColors_p; break; //fallback
|
||||
}
|
||||
if (millis() - _lastPaletteChange > 1000 + ((uint32_t)(255-SEGMENT.intensity))*100)
|
||||
{
|
||||
targetPalette = CRGBPalette16(
|
||||
CHSV(random8(), 255, random8(128, 255)),
|
||||
CHSV(random8(), 255, random8(128, 255)),
|
||||
CHSV(random8(), 192, random8(128, 255)),
|
||||
CHSV(random8(), 255, random8(128, 255)));
|
||||
_lastPaletteChange = millis();
|
||||
} break;}
|
||||
case 2: {//primary color only
|
||||
CRGB prim = col_to_crgb(SEGCOLOR(0));
|
||||
targetPalette = CRGBPalette16(prim); break;}
|
||||
case 3: {//based on primary
|
||||
//considering performance implications
|
||||
CRGB prim = col_to_crgb(SEGCOLOR(0));
|
||||
CHSV prim_hsv = rgb2hsv_approximate(prim);
|
||||
targetPalette = CRGBPalette16(
|
||||
CHSV(prim_hsv.h, prim_hsv.s, prim_hsv.v), //color itself
|
||||
CHSV(prim_hsv.h, max(prim_hsv.s - 50,0), prim_hsv.v), //less saturated
|
||||
CHSV(prim_hsv.h, prim_hsv.s, max(prim_hsv.v - 50,0)), //darker
|
||||
CHSV(prim_hsv.h, prim_hsv.s, prim_hsv.v)); //color itself
|
||||
break;}
|
||||
case 4: {//primary + secondary
|
||||
CRGB prim = col_to_crgb(SEGCOLOR(0));
|
||||
CRGB sec = col_to_crgb(SEGCOLOR(1));
|
||||
targetPalette = CRGBPalette16(sec,prim); break;}
|
||||
case 5: {//based on primary + secondary
|
||||
CRGB prim = col_to_crgb(SEGCOLOR(0));
|
||||
CRGB sec = col_to_crgb(SEGCOLOR(1));
|
||||
targetPalette = CRGBPalette16(sec,prim,CRGB::White); break;}
|
||||
case 6: //Party colors
|
||||
targetPalette = PartyColors_p; break;
|
||||
case 7: //Cloud colors
|
||||
targetPalette = CloudColors_p; break;
|
||||
case 8: //Lava colors
|
||||
targetPalette = LavaColors_p; break;
|
||||
case 9: //Ocean colors
|
||||
targetPalette = OceanColors_p; break;
|
||||
case 10: //Forest colors
|
||||
targetPalette = ForestColors_p; break;
|
||||
case 11: //Rainbow colors
|
||||
targetPalette = RainbowColors_p; break;
|
||||
case 12: //Rainbow stripe colors
|
||||
targetPalette = RainbowStripeColors_p; break;
|
||||
default: //progmem palettes
|
||||
targetPalette = gGradientPalettes[constrain(SEGMENT.palette -13, 0, gGradientPaletteCount -1)];
|
||||
}
|
||||
|
||||
if (singleSegmentMode && paletteFade) //only blend if just one segment uses FastLED mode
|
||||
{
|
||||
nblendPaletteTowardPalette(currentPalette, targetPalette, 48);
|
||||
} else
|
||||
{
|
||||
currentPalette = targetPalette;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri)
|
||||
{
|
||||
if (SEGMENT.palette == 0 && mcol < 3) return SEGCOLOR(mcol); //WS2812FX default
|
||||
uint8_t paletteIndex = i;
|
||||
if (mapping) paletteIndex = map(i,SEGMENT.start,SEGMENT.stop-1,0,255);
|
||||
if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
|
||||
CRGB fastled_col;
|
||||
fastled_col = ColorFromPalette( currentPalette, paletteIndex, pbri, (paletteBlend == 3)? NOBLEND:LINEARBLEND);
|
||||
return fastled_col.r*65536 + fastled_col.g*256 + fastled_col.b;
|
||||
}
|
||||
|
||||
//gamma 2.4 lookup table used for color correction
|
||||
const byte gammaT[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
|
||||
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
|
||||
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
|
||||
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
|
||||
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
|
||||
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
|
||||
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
|
||||
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
|
||||
90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
|
||||
115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
|
||||
144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
|
||||
177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
|
||||
215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };
|
||||
|
||||
uint8_t WS2812FX::gamma8(uint8_t b)
|
||||
{
|
||||
return gammaT[b];
|
||||
}
|
||||
|
||||
uint32_t WS2812FX::gamma32(uint32_t color)
|
||||
{
|
||||
if (!gammaCorrectCol) return color;
|
||||
uint8_t w = (color >> 24) & 0xFF;
|
||||
uint8_t r = (color >> 16) & 0xFF;
|
||||
uint8_t g = (color >> 8) & 0xFF;
|
||||
uint8_t b = color & 0xFF;
|
||||
w = gammaT[w];
|
||||
r = gammaT[r];
|
||||
g = gammaT[g];
|
||||
b = gammaT[b];
|
||||
return ((w << 24) | (r << 16) | (g << 8) | (b));
|
||||
}
|
||||
@@ -2,36 +2,57 @@
|
||||
#ifndef NpbWrapper_h
|
||||
#define NpbWrapper_h
|
||||
|
||||
//#define WORKAROUND_ESP32_BITBANG
|
||||
//see https://github.com/Aircoookie/WLED/issues/2 for flicker free ESP32 support
|
||||
//PIN CONFIGURATION
|
||||
#define LEDPIN 2 //strip pin. Any for ESP32, gpio2 or 3 is recommended for ESP8266 (gpio2/3 are labeled D4/RX on NodeMCU and Wemos)
|
||||
//#define USE_APA102 // Uncomment for using APA102 LEDs.
|
||||
#define BTNPIN 0 //button pin. Needs to have pullup (gpio0 recommended)
|
||||
#define IR_PIN 4 //infrared pin (-1 to disable)
|
||||
#define RLYPIN 12 //pin for relay, will be set HIGH if LEDs are on (-1 to disable). Also usable for standby leds, triggers,...
|
||||
#define AUXPIN -1 //debug auxiliary output pin (-1 to disable)
|
||||
|
||||
#define LEDPIN 2 //strip pin. Only effective for ESP32, ESP8266 must use gpio2
|
||||
#define RLYMDE 1 //mode for relay, 0: LOW if LEDs are on 1: HIGH if LEDs are on
|
||||
|
||||
#ifdef USE_APA102
|
||||
#define CLKPIN 0
|
||||
#define DATAPIN 2
|
||||
#if BTNPIN == CLKPIN || BTNPIN == DATAPIN
|
||||
#undef BTNPIN // Deactivate button pin if it conflicts with one of the APA102 pins.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//uncomment this if red and green are swapped
|
||||
//#define SWAPRG
|
||||
|
||||
//automatically uses the right driver method for each platform
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef WORKAROUND_ESP32_BITBANG
|
||||
#define PIXELMETHOD NeoEsp32BitBangWs2813Method
|
||||
#else
|
||||
#define PIXELMETHOD NeoEsp32RmtWS2813_V3Method
|
||||
#endif
|
||||
#ifdef USE_APA102
|
||||
#define PIXELMETHOD DotStarMethod
|
||||
#else
|
||||
#define PIXELMETHOD NeoEsp32Rmt0Ws2812xMethod
|
||||
#endif
|
||||
#else //esp8266
|
||||
//you may change to DMA method on pin GPIO3 here
|
||||
#define PIXELMETHOD NeoEsp8266Uart800KbpsMethod
|
||||
//#define PIXELMETHOD NeoEsp8266Dma800KbpsMethod
|
||||
//autoselect the right method depending on strip pin
|
||||
#ifdef USE_APA102
|
||||
#define PIXELMETHOD DotStarMethod
|
||||
#elif LEDPIN == 2
|
||||
#define PIXELMETHOD NeoEsp8266Uart1Ws2813Method //if you get an error here, try to change to NeoEsp8266UartWs2813Method or update Neopixelbus
|
||||
#elif LEDPIN == 3
|
||||
#define PIXELMETHOD NeoEsp8266Dma800KbpsMethod
|
||||
#else
|
||||
#define PIXELMETHOD NeoEsp8266BitBang800KbpsMethod
|
||||
#pragma message "Software BitBang will be used because of your selected LED pin. This may cause flicker. Use GPIO 2 or 3 for best results."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//handle swapping Red and Green automatically
|
||||
#ifdef SWAPRG
|
||||
#define PIXELFEATURE3 NeoRgbFeature
|
||||
#define PIXELFEATURE4 NeoRgbwFeature
|
||||
|
||||
//you can now change the color order in the web settings
|
||||
#ifdef USE_APA102
|
||||
#define PIXELFEATURE3 DotStarBgrFeature
|
||||
#define PIXELFEATURE4 DotStarLbgrFeature
|
||||
#else
|
||||
#define PIXELFEATURE3 NeoGrbFeature
|
||||
#define PIXELFEATURE4 NeoGrbwFeature
|
||||
#define PIXELFEATURE3 NeoGrbFeature
|
||||
#define PIXELFEATURE4 NeoGrbwFeature
|
||||
#endif
|
||||
|
||||
|
||||
#include <NeoPixelBrightnessBus.h>
|
||||
|
||||
enum NeoPixelType
|
||||
@@ -46,10 +67,10 @@ class NeoPixelWrapper
|
||||
{
|
||||
public:
|
||||
NeoPixelWrapper() :
|
||||
// initialize each member to null
|
||||
_pGrb(NULL),
|
||||
_pGrbw(NULL),
|
||||
_type(NeoPixelType_None)
|
||||
// initialize each member to null
|
||||
_pGrb(NULL),
|
||||
_pGrbw(NULL),
|
||||
_type(NeoPixelType_None)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -64,15 +85,23 @@ public:
|
||||
cleanup();
|
||||
_type = type;
|
||||
|
||||
switch (_type) {
|
||||
|
||||
switch (_type)
|
||||
{
|
||||
case NeoPixelType_Grb:
|
||||
#ifdef USE_APA102
|
||||
_pGrb = new NeoPixelBrightnessBus<PIXELFEATURE3,PIXELMETHOD>(countPixels, CLKPIN, DATAPIN);
|
||||
#else
|
||||
_pGrb = new NeoPixelBrightnessBus<PIXELFEATURE3,PIXELMETHOD>(countPixels, LEDPIN);
|
||||
#endif
|
||||
_pGrb->Begin();
|
||||
break;
|
||||
|
||||
case NeoPixelType_Grbw:
|
||||
#ifdef USE_APA102
|
||||
_pGrbw = new NeoPixelBrightnessBus<PIXELFEATURE4,PIXELMETHOD>(countPixels, CLKPIN, DATAPIN);
|
||||
#else
|
||||
_pGrbw = new NeoPixelBrightnessBus<PIXELFEATURE4,PIXELMETHOD>(countPixels, LEDPIN);
|
||||
#endif
|
||||
_pGrbw->Begin();
|
||||
break;
|
||||
}
|
||||
@@ -80,24 +109,13 @@ public:
|
||||
|
||||
void Show()
|
||||
{
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef WORKAROUND_ESP32_BITBANG
|
||||
delay(1);
|
||||
portDISABLE_INTERRUPTS(); //this is a workaround to prevent flickering (see https://github.com/adafruit/Adafruit_NeoPixel/issues/139)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
switch (_type) {
|
||||
switch (_type)
|
||||
{
|
||||
case NeoPixelType_Grb: _pGrb->Show(); break;
|
||||
case NeoPixelType_Grbw: _pGrbw->Show(); break;
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef WORKAROUND_ESP32_BITBANG
|
||||
portENABLE_INTERRUPTS();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CanShow() const
|
||||
{
|
||||
switch (_type) {
|
||||
@@ -106,65 +124,65 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void SetPixelColor(uint16_t indexPixel, RgbColor color)
|
||||
{
|
||||
switch (_type) {
|
||||
case NeoPixelType_Grb: _pGrb->SetPixelColor(indexPixel, color); break;
|
||||
case NeoPixelType_Grbw:_pGrbw->SetPixelColor(indexPixel, color); break;
|
||||
}
|
||||
void SetPixelColor(uint16_t indexPixel, RgbColor color)
|
||||
{
|
||||
switch (_type) {
|
||||
case NeoPixelType_Grb: _pGrb->SetPixelColor(indexPixel, color); break;
|
||||
case NeoPixelType_Grbw:_pGrbw->SetPixelColor(indexPixel, color); break;
|
||||
}
|
||||
}
|
||||
|
||||
void SetPixelColor(uint16_t indexPixel, RgbwColor color)
|
||||
{
|
||||
switch (_type) {
|
||||
case NeoPixelType_Grb: _pGrb->SetPixelColor(indexPixel, RgbColor(color.R,color.G,color.B)); break;
|
||||
case NeoPixelType_Grbw: _pGrbw->SetPixelColor(indexPixel, color); break;
|
||||
}
|
||||
void SetPixelColor(uint16_t indexPixel, RgbwColor color)
|
||||
{
|
||||
switch (_type) {
|
||||
case NeoPixelType_Grb: _pGrb->SetPixelColor(indexPixel, RgbColor(color.R,color.G,color.B)); break;
|
||||
case NeoPixelType_Grbw: _pGrbw->SetPixelColor(indexPixel, color); break;
|
||||
}
|
||||
}
|
||||
|
||||
void SetBrightness(byte b)
|
||||
{
|
||||
switch (_type) {
|
||||
case NeoPixelType_Grb: _pGrb->SetBrightness(b); break;
|
||||
case NeoPixelType_Grbw:_pGrbw->SetBrightness(b); break;
|
||||
}
|
||||
void SetBrightness(byte b)
|
||||
{
|
||||
switch (_type) {
|
||||
case NeoPixelType_Grb: _pGrb->SetBrightness(b); break;
|
||||
case NeoPixelType_Grbw:_pGrbw->SetBrightness(b); break;
|
||||
}
|
||||
}
|
||||
|
||||
RgbColor GetPixelColor(uint16_t indexPixel) const
|
||||
{
|
||||
switch (_type) {
|
||||
case NeoPixelType_Grb: return _pGrb->GetPixelColor(indexPixel); break;
|
||||
case NeoPixelType_Grbw: /*doesn't support it so we don't return it*/ break;
|
||||
}
|
||||
return 0;
|
||||
RgbColor GetPixelColor(uint16_t indexPixel) const
|
||||
{
|
||||
switch (_type) {
|
||||
case NeoPixelType_Grb: return _pGrb->GetPixelColor(indexPixel); break;
|
||||
case NeoPixelType_Grbw: /*doesn't support it so we don't return it*/ break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// NOTE: Due to feature differences, some support RGBW but the method name
|
||||
// here needs to be unique, thus GetPixeColorRgbw
|
||||
RgbwColor GetPixelColorRgbw(uint16_t indexPixel) const
|
||||
{
|
||||
switch (_type) {
|
||||
case NeoPixelType_Grb: return _pGrb->GetPixelColor(indexPixel); break;
|
||||
case NeoPixelType_Grbw: return _pGrbw->GetPixelColor(indexPixel); break;
|
||||
}
|
||||
return 0;
|
||||
// NOTE: Due to feature differences, some support RGBW but the method name
|
||||
// here needs to be unique, thus GetPixeColorRgbw
|
||||
RgbwColor GetPixelColorRgbw(uint16_t indexPixel) const
|
||||
{
|
||||
switch (_type) {
|
||||
case NeoPixelType_Grb: return _pGrb->GetPixelColor(indexPixel); break;
|
||||
case NeoPixelType_Grbw: return _pGrbw->GetPixelColor(indexPixel); break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ClearTo(RgbColor color)
|
||||
{
|
||||
switch (_type) {
|
||||
case NeoPixelType_Grb: _pGrb->ClearTo(color); break;
|
||||
case NeoPixelType_Grbw:_pGrbw->ClearTo(color); break;
|
||||
}
|
||||
void ClearTo(RgbColor color)
|
||||
{
|
||||
switch (_type) {
|
||||
case NeoPixelType_Grb: _pGrb->ClearTo(color); break;
|
||||
case NeoPixelType_Grbw:_pGrbw->ClearTo(color); break;
|
||||
}
|
||||
}
|
||||
|
||||
void ClearTo(RgbwColor color)
|
||||
{
|
||||
switch (_type) {
|
||||
case NeoPixelType_Grb: break;
|
||||
case NeoPixelType_Grbw:_pGrbw->ClearTo(color); break;
|
||||
}
|
||||
void ClearTo(RgbwColor color)
|
||||
{
|
||||
switch (_type) {
|
||||
case NeoPixelType_Grb: break;
|
||||
case NeoPixelType_Grbw:_pGrbw->ClearTo(color); break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
NeoPixelType _type;
|
||||
|
||||
2368
wled00/WS2812FX.cpp
2368
wled00/WS2812FX.cpp
File diff suppressed because it is too large
Load Diff
129
wled00/__vm/.wled00.vsarduino.h
Normal file
129
wled00/__vm/.wled00.vsarduino.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
Editor: https://www.visualmicro.com/
|
||||
This file is for intellisense purpose only.
|
||||
Visual micro (and the arduino ide) ignore this code during compilation. This code is automatically maintained by visualmicro, manual changes to this file will be overwritten
|
||||
The contents of the _vm sub folder can be deleted prior to publishing a project
|
||||
All non-arduino files created by visual micro and all visual studio project or solution files can be freely deleted and are not required to compile a sketch (do not delete your own code!).
|
||||
Note: debugger breakpoints are stored in '.sln' or '.asln' files, knowledge of last uploaded breakpoints is stored in the upload.vmps.xml file. Both files are required to continue a previous debug session without needing to compile and upload again
|
||||
|
||||
Hardware: ESP32 Dev Module, Platform=esp32, Package=esp32
|
||||
*/
|
||||
|
||||
#if defined(_VMICRO_INTELLISENSE)
|
||||
|
||||
#ifndef _VSARDUINO_H_
|
||||
#define _VSARDUINO_H_
|
||||
#define __ESP32_esp32__
|
||||
#define __ESP32_ESP32__
|
||||
#define ESP_PLATFORM
|
||||
#define HAVE_CONFIG_H
|
||||
#define F_CPU 240000000L
|
||||
#define ARDUINO 10807
|
||||
#define ARDUINO_ESP32_DEV
|
||||
#define ARDUINO_ARCH_ESP32
|
||||
#define ESP32
|
||||
#define CORE_DEBUG_LEVEL 0
|
||||
#define __cplusplus 201103L
|
||||
|
||||
#define _Pragma(x)
|
||||
#undef __cplusplus
|
||||
#define __cplusplus 201103L
|
||||
|
||||
#define __STDC__
|
||||
#define __ARM__
|
||||
#define __arm__
|
||||
#define __inline__
|
||||
#define __asm__(...)
|
||||
#define __extension__
|
||||
#define __ATTR_PURE__
|
||||
#define __ATTR_CONST__
|
||||
#define __volatile__
|
||||
|
||||
#define __ASM
|
||||
#define __INLINE
|
||||
#define __attribute__(noinline)
|
||||
|
||||
//#define _STD_BEGIN
|
||||
//#define EMIT
|
||||
#define WARNING
|
||||
#define _Lockit
|
||||
#define __CLR_OR_THIS_CALL
|
||||
#define C4005
|
||||
#define _NEW
|
||||
|
||||
typedef bool _Bool;
|
||||
typedef int _read;
|
||||
typedef int _seek;
|
||||
typedef int _write;
|
||||
typedef int _close;
|
||||
typedef int __cleanup;
|
||||
|
||||
//#define inline
|
||||
|
||||
#define __builtin_clz
|
||||
#define __builtin_clzl
|
||||
#define __builtin_clzll
|
||||
#define __builtin_labs
|
||||
#define __builtin_va_list
|
||||
typedef int __gnuc_va_list;
|
||||
|
||||
#define __ATOMIC_ACQ_REL
|
||||
|
||||
#define __CHAR_BIT__
|
||||
#define _EXFUN()
|
||||
|
||||
typedef unsigned char byte;
|
||||
extern "C" void __cxa_pure_virtual() {;}
|
||||
|
||||
typedef long __INTPTR_TYPE__ ;
|
||||
typedef long __UINTPTR_TYPE__ ;
|
||||
typedef long __SIZE_TYPE__ ;
|
||||
typedef long __PTRDIFF_TYPE__;
|
||||
|
||||
typedef long pthread_t;
|
||||
typedef long pthread_key_t;
|
||||
typedef long pthread_once_t;
|
||||
typedef long pthread_mutex_t;
|
||||
typedef long pthread_mutex_t;
|
||||
typedef long pthread_cond_t;
|
||||
|
||||
|
||||
|
||||
#include "arduino.h"
|
||||
#include <pins_arduino.h>
|
||||
|
||||
//#include "..\generic\Common.h"
|
||||
//#include "..\generic\pins_arduino.h"
|
||||
|
||||
//#undef F
|
||||
//#define F(string_literal) ((const PROGMEM char *)(string_literal))
|
||||
//#undef PSTR
|
||||
//#define PSTR(string_literal) ((const PROGMEM char *)(string_literal))
|
||||
//current vc++ does not understand this syntax so use older arduino example for intellisense
|
||||
//todo:move to the new clang/gcc project types.
|
||||
#define interrupts() sei()
|
||||
#define noInterrupts() cli()
|
||||
|
||||
#include "wled00.ino"
|
||||
#include "wled01_eeprom.ino"
|
||||
#include "wled02_xml.ino"
|
||||
#include "wled03_set.ino"
|
||||
#include "wled04_file.ino"
|
||||
#include "wled05_init.ino"
|
||||
#include "wled06_usermod.ino"
|
||||
#include "wled07_notify.ino"
|
||||
#include "wled08_led.ino"
|
||||
#include "wled09_button.ino"
|
||||
#include "wled10_ntp.ino"
|
||||
#include "wled11_ol.ino"
|
||||
#include "wled12_alexa.ino"
|
||||
#include "wled13_cronixie.ino"
|
||||
#include "wled14_colors.ino"
|
||||
#include "wled15_hue.ino"
|
||||
#include "wled16_blynk.ino"
|
||||
#include "wled17_mqtt.ino"
|
||||
#include "wled18_server.ino"
|
||||
#include "wled19_json.ino"
|
||||
#include "wled20_ir.ino"
|
||||
#endif
|
||||
#endif
|
||||
12
wled00/__vm/Compile.vmps.xml
Normal file
12
wled00/__vm/Compile.vmps.xml
Normal file
File diff suppressed because one or more lines are too long
9
wled00/__vm/Configuration.Release.vmps.xml
Normal file
9
wled00/__vm/Configuration.Release.vmps.xml
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -2,7 +2,7 @@
|
||||
<html>
|
||||
<head><meta charset="utf-8"><meta name="theme-color" content="#fff">
|
||||
<link rel='shortcut icon' type='image/x-icon' href='/favicon.ico'/>
|
||||
<title>WLED 0.8.0</title>
|
||||
<title>WLED 0.8.6</title>
|
||||
<script>
|
||||
var d=document;
|
||||
var w=window.getComputedStyle(d.querySelector("html"));
|
||||
@@ -18,6 +18,9 @@
|
||||
var nState = 0;
|
||||
var cv=0;
|
||||
var lm=0;
|
||||
var fxi=0;
|
||||
var fpi=0;
|
||||
var fxn=1;
|
||||
aC="";
|
||||
bC="";
|
||||
dC="";
|
||||
@@ -26,17 +29,22 @@
|
||||
{
|
||||
return d.getElementById(s);
|
||||
}
|
||||
|
||||
function Startup()
|
||||
function UCol()
|
||||
{
|
||||
var w=window.getComputedStyle(d.querySelector("html"));
|
||||
aC = w.getPropertyValue("--aCol");
|
||||
bC = w.getPropertyValue("--bCol");
|
||||
dC = w.getPropertyValue("--dCol");
|
||||
d.querySelector("meta[name=theme-color]").setAttribute("content",bC);
|
||||
}
|
||||
function Startup()
|
||||
{
|
||||
var w=window.getComputedStyle(d.querySelector("html"));
|
||||
UCol();
|
||||
CV(0);
|
||||
setInterval('GIO()', 5000);
|
||||
setInterval(GIO, 5000);
|
||||
GIO();
|
||||
setTimeout(function(){fillfx(0);}, 200);
|
||||
setTimeout(function(){fillfx(1);}, 500);
|
||||
}
|
||||
function GIO()
|
||||
{
|
||||
@@ -57,17 +65,29 @@
|
||||
} else {
|
||||
uwv = false;
|
||||
}
|
||||
Cf.TX.selectedIndex = this.responseXML.getElementsByTagName('fx')[0].childNodes[0].nodeValue;
|
||||
Cf.FP.selectedIndex = this.responseXML.getElementsByTagName('fp')[0].childNodes[0].nodeValue;
|
||||
fxi = this.responseXML.getElementsByTagName('fx')[0].childNodes[0].nodeValue;
|
||||
fpi = this.responseXML.getElementsByTagName('fp')[0].childNodes[0].nodeValue;
|
||||
d.Cf.SX.value = this.responseXML.getElementsByTagName('sx')[0].childNodes[0].nodeValue;
|
||||
d.Cf.IX.value = this.responseXML.getElementsByTagName('ix')[0].childNodes[0].nodeValue;
|
||||
nla = (this.responseXML.getElementsByTagName('nl')[0].innerHTML)!=0?true:false;
|
||||
if(firstload){
|
||||
d.Cf.NC.checked = (this.responseXML.getElementsByTagName('nf')[0].innerHTML)!=0?true:false;
|
||||
d.Cf.SN.value = this.responseXML.getElementsByTagName('nd')[0].childNodes[0].nodeValue;
|
||||
d.Cf.ST.value = this.responseXML.getElementsByTagName('nt')[0].childNodes[0].nodeValue;
|
||||
CV(parseInt(this.responseXML.getElementsByTagName('md')[0].childNodes[0].nodeValue));
|
||||
}firstload=false;
|
||||
d.Cf.NC.checked = (this.responseXML.getElementsByTagName('nf')[0].innerHTML)!=0?true:false;
|
||||
d.Cf.SN.value = this.responseXML.getElementsByTagName('nd')[0].childNodes[0].nodeValue;
|
||||
d.Cf.ST.value = this.responseXML.getElementsByTagName('nt')[0].childNodes[0].nodeValue;
|
||||
d.documentElement.style.setProperty("--aCol",this.responseXML.getElementsByTagName('ca')[0].childNodes[0].nodeValue);
|
||||
d.documentElement.style.setProperty("--bCol",this.responseXML.getElementsByTagName('cb')[0].childNodes[0].nodeValue);
|
||||
d.documentElement.style.setProperty("--cCol",this.responseXML.getElementsByTagName('cc')[0].childNodes[0].nodeValue);
|
||||
d.documentElement.style.setProperty("--dCol",this.responseXML.getElementsByTagName('cd')[0].childNodes[0].nodeValue);
|
||||
d.documentElement.style.setProperty("--sCol",this.responseXML.getElementsByTagName('cu')[0].childNodes[0].nodeValue);
|
||||
d.documentElement.style.setProperty("--tCol",this.responseXML.getElementsByTagName('ct')[0].childNodes[0].nodeValue);
|
||||
d.documentElement.style.setProperty("--cFn",this.responseXML.getElementsByTagName('cf')[0].childNodes[0].nodeValue);
|
||||
UCol();
|
||||
CV(parseInt(this.responseXML.getElementsByTagName('md')[0].childNodes[0].nodeValue));
|
||||
}else{
|
||||
Cf.TX.selectedIndex = fxi;
|
||||
Cf.IX.selectedIndex = fpi;
|
||||
}
|
||||
firstload=false;
|
||||
nState = 0;
|
||||
nState = (this.responseXML.getElementsByTagName('nr')[0].innerHTML)!=0?1:0;
|
||||
nState += (this.responseXML.getElementsByTagName('ns')[0].innerHTML)!=0?2:0;
|
||||
@@ -77,6 +97,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
if (firstload) resp+="&IT"
|
||||
request.open("GET", "win" +resp +nocache, true);
|
||||
request.send(null);
|
||||
resp="";
|
||||
@@ -172,7 +193,7 @@
|
||||
case 3: gId("path1").style.fill = aC; gId("path2").style.fill = aC;
|
||||
}
|
||||
tgb.style.fill=(Cf.SA.value>0)?aC:dC;
|
||||
fpX.style.display=(Cf.TX.selectedIndex>64)?"block":"none";
|
||||
fpX.style.display=(Cf.TX.selectedIndex>0)?"block":"none";
|
||||
fof.style.fill=(Cf.TX.selectedIndex>64)?aC:dC;
|
||||
fmr.style.fill=(Cf.TX.selectedIndex<1)?aC:dC;
|
||||
}
|
||||
@@ -192,10 +213,10 @@
|
||||
function SwFX(s)
|
||||
{
|
||||
var n=Cf.TX.selectedIndex+s;
|
||||
if (n==-1||n==74) return;
|
||||
if (n==-1||n==fxn) return;
|
||||
Cf.TX.selectedIndex =n;
|
||||
if (n < 0) Cf.TX.selectedIndex = 0;
|
||||
if (n > 73) Cf.TX.selectedIndex = 65;
|
||||
if (n > fxn) Cf.TX.selectedIndex = Math.min(65,fxn-1);
|
||||
GX();
|
||||
}
|
||||
function TgHSB()
|
||||
@@ -302,7 +323,6 @@
|
||||
rr=diffc(r);
|
||||
gg=diffc(g);
|
||||
bb=diffc(b);
|
||||
|
||||
if(r===v) {
|
||||
h=bb-gg;
|
||||
}else if (g===v){
|
||||
@@ -345,6 +365,31 @@
|
||||
{
|
||||
resp+="&PL=0";GIO();
|
||||
}
|
||||
function fillfx(fp)
|
||||
{
|
||||
e="<option>Error loading list!</option>";
|
||||
el = fp?Cf.FP:Cf.TX;
|
||||
fetch(fp?'/json/pal':'/json/eff')
|
||||
.then(res => {
|
||||
if (!res.ok) {
|
||||
el.innerHTML=e;
|
||||
}
|
||||
return res.json();
|
||||
})
|
||||
.then(json => {
|
||||
var x="";
|
||||
for (i in json) {
|
||||
x += "<option value=\""+i+"\">"+json[i]+" ("+i+")</option>";
|
||||
}
|
||||
el.innerHTML=x;
|
||||
el.selectedIndex=fp?fpi:fxi;
|
||||
if(!fp)fxn=json.length;
|
||||
UV();
|
||||
})
|
||||
.catch(function () {
|
||||
el.innerHTML=e;
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
:root {
|
||||
@@ -586,80 +631,7 @@
|
||||
<svg id="fmf" onclick="SwFX(1)"><use xlink:href="#lnr-arrow-right-circle"></use></svg>
|
||||
<svg id="fof" onclick="SwFX(99)"><use xlink:href="#lnr-rocket"></use></svg><br><br>
|
||||
<select name="TX" onchange="GX()">
|
||||
<option value="0" selected>Solid (0)</option>
|
||||
<option value="1">Blink (1)</option>
|
||||
<option value="2">Breath (2)</option>
|
||||
<option value="3">Wipe (3)</option>
|
||||
<option value="4">Wipe Random (4)</option>
|
||||
<option value="5">Color R (5)</option>
|
||||
<option value="6">Sweep (6)</option>
|
||||
<option value="7">Dynamic (7)</option>
|
||||
<option value="8">Colorloop (8)</option>
|
||||
<option value="9">Rainbow (9)</option>
|
||||
<option value="10">Scan (10)</option>
|
||||
<option value="11">Scan x2 (11)</option>
|
||||
<option value="12">Fade (12)</option>
|
||||
<option value="13">Chase (13)</option>
|
||||
<option value="14">Chase Cl (14)</option>
|
||||
<option value="15">Running (15)</option>
|
||||
<option value="16">Twinkle (16)</option>
|
||||
<option value="17">Twinkle R (17)</option>
|
||||
<option value="18">Twinkle Fade (18)</option>
|
||||
<option value="19">Twinkle RF (19)</option>
|
||||
<option value="20">Sparkle (20)</option>
|
||||
<option value="21">Sparkle Inv (21)</option>
|
||||
<option value="22">Sparkle Inv+ (22)</option>
|
||||
<option value="23">Strobe (23)</option>
|
||||
<option value="24">Strobe Cl (24)</option>
|
||||
<option value="25">Strobe + (25)</option>
|
||||
<option value="26">Blink Cl (26)</option>
|
||||
<option value="27">Android (27)</option>
|
||||
<option value="28">Chase (28)</option>
|
||||
<option value="29">Chase R (29)</option>
|
||||
<option value="30">Chase Rainbow (30)</option>
|
||||
<option value="31">Chase Flash (31)</option>
|
||||
<option value="32">Chase RF (32)</option>
|
||||
<option value="33">Chase Cl Inv (33)</option>
|
||||
<option value="34">Colorful (34)</option>
|
||||
<option value="35">Traffic Light (35)</option>
|
||||
<option value="36">Sweep R(36)</option>
|
||||
<option value="37">Running 2 (37)</option>
|
||||
<option value="38">Red/Blue (38)</option>
|
||||
<option value="39">Running R (39)</option>
|
||||
<option value="40">Scanner (40)</option>
|
||||
<option value="41">Lighthouse (41)</option>
|
||||
<option value="42">Fireworks (42)</option>
|
||||
<option value="43">Fireworks R (43)</option>
|
||||
<option value="44">Christmas (44)</option>
|
||||
<option value="45">Fire Flicker (45)</option>
|
||||
<option value="46">Gradient (46)</option>
|
||||
<option value="47">Loading (47)</option>
|
||||
<option value="48">Wipe IO (48)</option>
|
||||
<option value="49">Wipe II (49)</option>
|
||||
<option value="50">Wipe OO (50)</option>
|
||||
<option value="51">Wipe OI (51)</option>
|
||||
<option value="52">Circus (52)</option>
|
||||
<option value="53">Halloween (53)</option>
|
||||
<option value="54">Tricolor Chase (54)</option>
|
||||
<option value="55">Tricolor Wipe (55)</option>
|
||||
<option value="56">Tricolor Fade (56)</option>
|
||||
<option value="57">Lighting (57)</option>
|
||||
<option value="58">ICU (58)</option>
|
||||
<option value="59">Multi Comet (59)</option>
|
||||
<option value="60">Scanner x2 (60)</option>
|
||||
<option value="61">Random Chase (61)</option>
|
||||
<option value="62">Oscillate (62)</option>
|
||||
<option value="63">Pride 2015 (63)</option>
|
||||
<option value="64">Juggle (64)</option>
|
||||
<option value="65">Palette (65)</option>
|
||||
<option value="66">Fire 2012 (66)</option>
|
||||
<option value="67">Colorwaves (67)</option>
|
||||
<option value="68">BPM (68)</option>
|
||||
<option value="69">Fill Noise 8 (69)</option>
|
||||
<option value="70">Noise 16 1 (70)</option>
|
||||
<option value="71">Noise 16 2 (71)</option>
|
||||
<option value="72">Noise 16 3 (72)</option>
|
||||
<option value="73">Noise 16 4 (73)</option>
|
||||
<option>Loading...</option>
|
||||
</select><br><br>
|
||||
Set secondary color to
|
||||
<button type="button" onclick="CS(0)">White</button>
|
||||
@@ -671,51 +643,7 @@
|
||||
<div id="fpX">
|
||||
<br>FastLED Palette<br><br>
|
||||
<select name="FP" onchange="GP()">
|
||||
<option value="0" selected>Default</option>
|
||||
<option value="1">Random Cycle</option>
|
||||
<option value="2">Primary Color Only</option>
|
||||
<option value="3">Based on Primary</option>
|
||||
<option value="4">Set Colors Only</option>
|
||||
<option value="5">Based on Set Colors</option>
|
||||
<option value="6">Party</option>
|
||||
<option value="7">Cloud</option>
|
||||
<option value="8">Lava</option>
|
||||
<option value="9">Ocean</option>
|
||||
<option value="10">Forest</option>
|
||||
<option value="11">Rainbow</option>
|
||||
<option value="12">Rainbow Stripe</option>
|
||||
<option value="13">Sunset</option>
|
||||
<option value="14">Rivendell</option>
|
||||
<option value="15">Breeze</option>
|
||||
<option value="16">Red & Blue</option>
|
||||
<option value="17">Yellowout</option>
|
||||
<option value="18">Analogous</option>
|
||||
<option value="19">Splash</option>
|
||||
<option value="20">Pastel</option>
|
||||
<option value="21">Sunset2</option>
|
||||
<option value="22">Beech</option>
|
||||
<option value="23">Vintage</option>
|
||||
<option value="24">Departure</option>
|
||||
<option value="25">Landscape</option>
|
||||
<option value="26">Beach</option>
|
||||
<option value="27">Sherbet</option>
|
||||
<option value="28">Hult</option>
|
||||
<option value="29">Hult64</option>
|
||||
<option value="30">Drywet</option>
|
||||
<option value="31">Jul</option>
|
||||
<option value="32">Grintage</option>
|
||||
<option value="33">Rewhi</option>
|
||||
<option value="34">Tertiary</option>
|
||||
<option value="35">Fire</option>
|
||||
<option value="36">Icefire</option>
|
||||
<option value="37">Cyane</option>
|
||||
<option value="38">Light Pink</option>
|
||||
<option value="39">Autumn</option>
|
||||
<option value="40">Magenta</option>
|
||||
<option value="41">Magred</option>
|
||||
<option value="42">Yelmag</option>
|
||||
<option value="43">Yelblu</option>
|
||||
<option value="44">Orange & Teal</option>
|
||||
<option>Loading...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="slX" class="sl">
|
||||
@@ -730,14 +658,14 @@
|
||||
<svg id="psn" onclick="SwitchPS(1)"><use xlink:href="#lnr-arrow-right-circle"></use></svg>
|
||||
<svg id="pss" onclick="PSIO(true)"><use xlink:href="#lnr-arrow-down-circle"></use></svg>
|
||||
<br><input id="psI" name = "FF" type="number" value="1" min="1" max="25" step="1"><br><br>
|
||||
Click checkmark to apply <input type="checkbox" checked="true" name="BC"> brightness, <input type="checkbox" checked="true" name="CC"> color and <input type="checkbox" checked="true" name="FC"> effects.<br><br>
|
||||
Click checkmark to apply <input type="checkbox" checked name="CC"> color, <input type="checkbox" checked name="FC"> effects and <input type="checkbox" name="BC"> brightness.<br><br>
|
||||
Cycle through presets <input id="cy1" name="P1" type="number" value="1" min="1" max="25" step="1"> to <input id="cy2" name="P2" type="number" value="5" min="1" max="25" step="1">, keep each for <input id="cyT" name="PT" type="number" value="1250" min="50" max="65501" step="1">ms: <input type="checkbox" name="CY" onclick="uCY()"><br><br>
|
||||
<button type="button" onclick="R()">Apply boot config</button><br>
|
||||
</div>
|
||||
<div id="tlN" class="tools">
|
||||
Timed Light<br><br>
|
||||
<svg id="ntb" onclick="TgNl()"><use xlink:href="#lnr-power-switch"></use></svg><br><br>
|
||||
Gradually dim down <input type="checkbox" checked="true" name="NC"><br>
|
||||
Gradually dim down <input type="checkbox" checked name="NC"><br>
|
||||
1st slider sets duration (1-255min), 2nd sets target brightness.
|
||||
<div id="slN" class="sl">
|
||||
<input type="range" title="Duration" class="sds" name="SN" value="60" min="1" max="255" step="1"></div>
|
||||
|
||||
File diff suppressed because one or more lines are too long
100
wled00/data/jsontest.htm
Normal file
100
wled00/data/jsontest.htm
Normal file
@@ -0,0 +1,100 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>JSON client</title>
|
||||
<style>
|
||||
:root {
|
||||
--bCol:#333;--cCol:#222;--dCol:#666;--tCol:#fff;
|
||||
}
|
||||
body {
|
||||
font-family: Verdana, sans-serif;
|
||||
text-align: center;
|
||||
background: var(--cCol);
|
||||
color: var(--tCol);
|
||||
margin: 20px;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
button {
|
||||
background: var(--cCol);
|
||||
color: var(--tCol);
|
||||
border: 0.3ch solid var(--cCol);
|
||||
display: inline-block;
|
||||
font-size: 20px;
|
||||
margin: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
input {
|
||||
background: var(--cCol);
|
||||
color: var(--tCol);
|
||||
border: 0.5ch solid var(--cCol);
|
||||
width: 100%;
|
||||
}
|
||||
h1{
|
||||
margin: 0px;
|
||||
font-size: 20px;
|
||||
}
|
||||
h2{
|
||||
font-size: 16px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
form{
|
||||
background: var(--bCol);
|
||||
width: 500px;
|
||||
padding: 20px;
|
||||
-webkit-border-radius: 10px;
|
||||
-moz-border-radius: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
textarea{
|
||||
background: var(--cCol);
|
||||
color: var(--tCol);
|
||||
padding-top: 10px;
|
||||
width: 100%;
|
||||
font-family: monaco,monospace;
|
||||
font-size: 12px;
|
||||
-webkit-border-radius: 10px;
|
||||
-moz-border-radius: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<form name="cf">
|
||||
<h1>JSON API test tool</h1>
|
||||
<h2>URL:</h2>
|
||||
<input name="cu" type="text" size="60" value="http://192.168.4.1/json">
|
||||
<div id="buttons">
|
||||
<button type="button" onclick="rq('GET')">GET</button>
|
||||
<button type="button" onclick="rq('POST')">POST</button>
|
||||
</div>
|
||||
<h2>Body:</h2>
|
||||
<textarea name="bd" rows="8" cols="100"></textarea>
|
||||
<h2>Response:</h2>
|
||||
<textarea name="rsp" rows="25" cols="100"></textarea>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<script>
|
||||
function rq(cm)
|
||||
{
|
||||
var h = new XMLHttpRequest();
|
||||
h.open(cm, document.cf.cu.value, true);
|
||||
h.onreadystatechange = function()
|
||||
{
|
||||
if(h.readyState == 4)
|
||||
{
|
||||
if(h.status==200)
|
||||
{
|
||||
document.cf.rsp.value="Bad JSON: "+h.responseText
|
||||
document.cf.rsp.value=JSON.stringify(JSON.parse(h.responseText), null, '\t');
|
||||
}
|
||||
else
|
||||
{
|
||||
document.cf.rsp.value="Error "+h.status+"\r\n\n"+h.responseText;
|
||||
}
|
||||
}
|
||||
}
|
||||
h.send(document.cf.bd.value);
|
||||
}
|
||||
</script>
|
||||
@@ -1,5 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta content='width=device-width' name='viewport'>
|
||||
<title>WLED Message</title>
|
||||
<script>
|
||||
function B() {
|
||||
@@ -22,7 +23,7 @@
|
||||
--tCol: #328CC1;
|
||||
--cFn: Verdana;
|
||||
}
|
||||
button {
|
||||
.bt {
|
||||
background: var(--bCol);
|
||||
color: var(--tCol);
|
||||
border: 0.3ch solid var(--bCol);
|
||||
@@ -33,6 +34,9 @@
|
||||
margin: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
input[type=file] {
|
||||
font-size: 16px;
|
||||
}
|
||||
body {
|
||||
font-family: var(--cFn), sans-serif;
|
||||
text-align: center;
|
||||
@@ -46,6 +50,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<h2>Sample message.</h2>
|
||||
Sample detail.
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
4
wled00/data/update.htm
Normal file
4
wled00/data/update.htm
Normal file
@@ -0,0 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<html><head><meta content='width=device-width' name='viewport'><title>WLED Message</title><script>function B(){window.history.back()}</script>
|
||||
<style>:root{--aCol:#D9B310;--bCol:#0B3C5D;--cCol:#1D2731;--dCol:#328CC1;--sCol:#000;--tCol:#328CC1;--cFn:Verdana;}.bt{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:.3ch solid var(--bCol);display:inline-block;filter:drop-shadow(-5px -5px 5px var(--sCol));font-size:20px;margin:8px;margin-top:12px}input[type=file]{font-size:16px}body{font-family:var(--cFn),sans-serif;text-align:center;background:var(--cCol);color:var(--tCol);line-height:200%}</style></head>
|
||||
<body><h2>WLED Software Update</h2>Installed version: 0.8.5-dev<br>Download the latest binary: <a href="https://github.com/Aircoookie/WLED/releases"><img src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square"></a><br><form method='POST' action='/update' enctype='multipart/form-data'><input type='file' class="bt" name='update' required><br><input type='submit' class="bt" value='Update!'></form><button type="button" class="bt" onclick="B()">Back</button></body></html>
|
||||
@@ -1,28 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta content='width=device-width' name='viewport'>
|
||||
<meta name="theme-color" content="#333333">
|
||||
<title>WLED Setup</title>
|
||||
<style>
|
||||
:root {
|
||||
--aCol: #D9B310;
|
||||
--bCol: #0B3C5D;
|
||||
--cCol: #1D2731;
|
||||
--dCol: #328CC1;
|
||||
--sCol: #000;
|
||||
}
|
||||
body {
|
||||
font-family: Verdana, Helvetica, sans-serif;
|
||||
text-align: center;
|
||||
background: linear-gradient(var(--bCol),black);
|
||||
height: 100%;
|
||||
background-color: #333;
|
||||
margin: 0;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
color: var(--dCol);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
button {
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
margin: 10px;
|
||||
width: 230px;
|
||||
text-transform: uppercase;
|
||||
font-family: helvetica;
|
||||
font-size: 19px;
|
||||
background-color: #222;
|
||||
color: white;
|
||||
border: 0px solid white;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: var(--dCol);
|
||||
fill: #fff;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
@@ -35,10 +43,12 @@
|
||||
<svg><use xlink:href="#lnr-smile"></use></svg>
|
||||
<h1>Welcome to WLED!</h1>
|
||||
<h3>Thank you for installing my application!</h3>
|
||||
Take a quick look at the <a href="https://github.com/Aircoookie/WLED/wiki" target="_blank">wiki</a>!<br>
|
||||
If you encounter a bug or have a question/feature suggestion, feel free to open a GitHub issue!<br><br>
|
||||
<b>Next steps:</b><br><br>
|
||||
Connect the module to your local WiFi <a href="/settings/wifi">here</a>!<br><br>
|
||||
<i>Just trying this out in AP mode?</i> <a href="/sliders">Here are the controls.</a><br>
|
||||
Connect the module to your local WiFi here!<br>
|
||||
<button onclick="window.location.href='/settings/wifi'">WiFi settings</button><br>
|
||||
<i>Just trying this out in AP mode?</i><br>
|
||||
<button onclick="window.location.href='/sliders'">To the controls!</button>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
657
wled00/html_classic.h
Normal file
657
wled00/html_classic.h
Normal file
@@ -0,0 +1,657 @@
|
||||
/*
|
||||
* Binary arrays for the classic desktop UI index page.
|
||||
* gzip is used for smaller size and improved speeds.
|
||||
*
|
||||
* Workflow for creating them from HTML source:
|
||||
* 1. Minify HTML (https://htmlcompressor.com/compressor/) (optional)
|
||||
* 2. Compress with gzip (https://online-converting.com/archives/convert-to-gzip/)
|
||||
* 3. Convert gzip binary to c array (https://littlevgl.com/image-to-c-array) (select RAW as color format)
|
||||
* alternative: https://sourceforge.net/projects/bin2header/
|
||||
* 4. update length value
|
||||
*/
|
||||
|
||||
const uint16_t PAGE_index_L = 10256;
|
||||
|
||||
const uint8_t PAGE_index[] PROGMEM = {
|
||||
0x1f, 0x8b, 0x08, 0x08, 0x58, 0x17, 0x80, 0x5c, 0x00, 0x03, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65,
|
||||
0x73, 0x73, 0x65, 0x64, 0x20, 0x28, 0x31, 0x34, 0x29, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x00, 0xcc,
|
||||
0x5a, 0xe9, 0x72, 0xdb, 0x46, 0xb6, 0xfe, 0xaf, 0xa7, 0x80, 0xe9, 0x4a, 0x4c, 0x46, 0x04, 0x88,
|
||||
0x9d, 0x00, 0x29, 0x28, 0x65, 0x2b, 0xde, 0xaa, 0x1c, 0x45, 0x65, 0x69, 0x62, 0xa5, 0x32, 0x29,
|
||||
0x17, 0x88, 0x85, 0xc4, 0x18, 0x04, 0x68, 0x00, 0xa4, 0xa4, 0x30, 0x7a, 0xf7, 0xf9, 0x4e, 0x77,
|
||||
0x93, 0x04, 0xa8, 0x8d, 0xf4, 0x64, 0xe6, 0x5e, 0x57, 0x19, 0x40, 0xf7, 0x39, 0xdd, 0x67, 0x5f,
|
||||
0xba, 0xa9, 0xa3, 0x67, 0x3f, 0xfd, 0x72, 0x72, 0xf1, 0xdb, 0xd9, 0x6b, 0x69, 0x52, 0x4d, 0xd3,
|
||||
0xe3, 0x23, 0xf1, 0x8c, 0xfc, 0xf0, 0xf8, 0x68, 0x1a, 0x55, 0xbe, 0x14, 0x4c, 0xfc, 0xa2, 0x8c,
|
||||
0x2a, 0x6f, 0x5e, 0xc5, 0xb2, 0x23, 0xe6, 0x32, 0x7f, 0x1a, 0x79, 0xd5, 0x24, 0x9a, 0x46, 0x72,
|
||||
0x90, 0xa7, 0x79, 0x21, 0x05, 0x79, 0x56, 0x45, 0x59, 0xe5, 0x3d, 0x8f, 0xe3, 0xf8, 0xf8, 0x28,
|
||||
0x4d, 0xb2, 0x2f, 0x52, 0x11, 0xa5, 0xde, 0x8b, 0x72, 0x92, 0x17, 0x55, 0x30, 0xaf, 0xa4, 0x04,
|
||||
0x18, 0x2f, 0xa4, 0xea, 0x66, 0x16, 0x79, 0xc9, 0xd4, 0x1f, 0x47, 0xbd, 0x6b, 0x99, 0xa6, 0xa4,
|
||||
0x49, 0x11, 0xc5, 0xde, 0x8b, 0x5e, 0xec, 0x2f, 0x68, 0xa8, 0xe0, 0xf1, 0xa2, 0x77, 0x7c, 0x54,
|
||||
0x25, 0x55, 0x1a, 0x1d, 0x7f, 0xfa, 0xf0, 0xfa, 0x27, 0x49, 0x55, 0x1c, 0xc5, 0x3c, 0xea, 0xf1,
|
||||
0x19, 0xe9, 0xa8, 0x0c, 0x8a, 0x64, 0x56, 0x1d, 0x2f, 0xfc, 0x42, 0x0a, 0xbd, 0x30, 0x0f, 0xe6,
|
||||
0x53, 0x90, 0x1d, 0xd2, 0xf0, 0xca, 0xbb, 0x4a, 0xb2, 0x30, 0xbf, 0x52, 0xc6, 0x51, 0x75, 0x92,
|
||||
0x4f, 0x67, 0xf3, 0x2a, 0x0a, 0xcf, 0xab, 0x9b, 0x34, 0x6a, 0x87, 0xca, 0xd7, 0x79, 0x54, 0xdc,
|
||||
0x9c, 0x47, 0x69, 0x14, 0x54, 0x79, 0xd1, 0x6e, 0x91, 0x8c, 0xad, 0x4e, 0x67, 0x58, 0x44, 0xe5,
|
||||
0xcc, 0x6b, 0xb5, 0xd8, 0xf2, 0x2c, 0xf5, 0xbd, 0xd8, 0x4f, 0xcb, 0x88, 0x8f, 0x8a, 0xc6, 0xa8,
|
||||
0xac, 0x8f, 0xca, 0x2a, 0xaf, 0x8d, 0xe6, 0x57, 0x8b, 0x3a, 0x6c, 0x14, 0x7b, 0x55, 0x31, 0xe7,
|
||||
0x83, 0x38, 0x29, 0xca, 0x2a, 0xcd, 0xfd, 0x70, 0x33, 0x95, 0xfa, 0x65, 0x55, 0x5e, 0x7b, 0x2a,
|
||||
0xdf, 0xf6, 0xbc, 0xf2, 0xab, 0x48, 0x0c, 0x82, 0x85, 0xf8, 0x48, 0xa7, 0xe2, 0x23, 0xbe, 0x4e,
|
||||
0x56, 0x5f, 0xb3, 0xf5, 0xd7, 0x75, 0xe6, 0x69, 0x43, 0xff, 0x84, 0xb8, 0x1e, 0xb1, 0x67, 0xc8,
|
||||
0x9e, 0xf1, 0x3c, 0x0b, 0xaa, 0x04, 0xfa, 0x1c, 0xbf, 0x0f, 0xdb, 0x65, 0xe7, 0x60, 0x59, 0x44,
|
||||
0xd5, 0xbc, 0xc8, 0xa4, 0x90, 0xb4, 0xf1, 0x3a, 0x8d, 0x48, 0x4b, 0xaf, 0x6e, 0x18, 0x6c, 0x78,
|
||||
0x7b, 0xb0, 0xc6, 0xfe, 0xc7, 0x49, 0x9e, 0xb6, 0x81, 0x8d, 0x0d, 0x99, 0xde, 0xce, 0x8a, 0x7c,
|
||||
0x16, 0x15, 0xd5, 0xcd, 0xaf, 0x7e, 0x3a, 0x8f, 0xda, 0x2d, 0x59, 0xf6, 0x81, 0xd0, 0xea, 0x10,
|
||||
0xa9, 0xfb, 0xe1, 0x23, 0x0e, 0x0f, 0x1f, 0x82, 0x87, 0x02, 0xbe, 0x6d, 0x01, 0xf2, 0xa2, 0xdf,
|
||||
0xb7, 0xbd, 0xe8, 0x8f, 0x56, 0x47, 0x81, 0xa7, 0xbd, 0xac, 0xaa, 0x22, 0x19, 0xc1, 0x7c, 0xed,
|
||||
0x96, 0x70, 0xab, 0x56, 0x77, 0x74, 0xd2, 0xe0, 0x1b, 0x8a, 0x2b, 0xaa, 0xf9, 0x8c, 0x58, 0xff,
|
||||
0x46, 0xd3, 0x73, 0xc9, 0x87, 0x27, 0xbf, 0xb6, 0xd5, 0xce, 0x10, 0x44, 0xdf, 0x83, 0x50, 0xb1,
|
||||
0xf0, 0xd3, 0xf6, 0xdb, 0xf7, 0xbf, 0x74, 0x2d, 0x55, 0xc5, 0x2c, 0xbe, 0xda, 0x0c, 0x76, 0x91,
|
||||
0x4c, 0xa3, 0x7c, 0x5e, 0xb5, 0x57, 0xe4, 0xdb, 0x9d, 0x65, 0x9c, 0xa4, 0x69, 0x7c, 0x4d, 0x6b,
|
||||
0x6f, 0xbb, 0xba, 0xaa, 0x3e, 0x81, 0xa6, 0x11, 0x9a, 0x45, 0x68, 0x35, 0x19, 0xd8, 0xf6, 0x07,
|
||||
0xcb, 0x2c, 0x0f, 0xfc, 0x60, 0x12, 0x79, 0xad, 0xef, 0xd7, 0x5f, 0x87, 0x3f, 0xfb, 0xd5, 0x44,
|
||||
0x29, 0x7c, 0x88, 0x34, 0x6d, 0x77, 0x7e, 0xd0, 0x54, 0xf6, 0x8f, 0xd9, 0xbf, 0x88, 0x20, 0x4f,
|
||||
0x59, 0x79, 0x59, 0x74, 0x25, 0x5d, 0xfe, 0xfc, 0xe1, 0x5d, 0x55, 0xcd, 0x3e, 0xf2, 0xa9, 0x36,
|
||||
0xb9, 0x33, 0xfb, 0x52, 0xf2, 0xac, 0x40, 0xf8, 0xde, 0x94, 0xe4, 0x5d, 0x88, 0xde, 0x6c, 0x1c,
|
||||
0x79, 0x1b, 0x9e, 0x0e, 0x96, 0x49, 0xdc, 0xae, 0x26, 0x49, 0xa9, 0x30, 0x24, 0xee, 0x82, 0x9e,
|
||||
0xd9, 0x59, 0x4f, 0xd3, 0xb2, 0x79, 0xe9, 0x79, 0x24, 0x56, 0x0d, 0xb7, 0x9c, 0xe5, 0x59, 0x19,
|
||||
0x81, 0xe6, 0x33, 0x2f, 0x9b, 0xa7, 0x69, 0x67, 0x19, 0x2a, 0x27, 0xb1, 0x72, 0xfe, 0x52, 0x59,
|
||||
0x90, 0xad, 0xbd, 0x6d, 0xac, 0x9a, 0xdf, 0x95, 0xaf, 0x6e, 0x2e, 0xfc, 0xf1, 0x29, 0x8c, 0xdd,
|
||||
0x7e, 0xe1, 0x07, 0x2f, 0x3a, 0xbf, 0xab, 0x7f, 0x28, 0xc1, 0x24, 0x49, 0xc3, 0xd3, 0x3c, 0x8c,
|
||||
0x4a, 0x1a, 0x65, 0xf8, 0x60, 0x2e, 0x33, 0xe4, 0x9b, 0x7e, 0xdc, 0x6f, 0xd3, 0x20, 0xdd, 0x65,
|
||||
0xd3, 0xb7, 0xdf, 0xb0, 0xa9, 0xf6, 0xd4, 0xa6, 0xaf, 0xbe, 0x61, 0x53, 0xfd, 0xe1, 0x4d, 0xef,
|
||||
0x51, 0xf7, 0x03, 0x3b, 0x5d, 0x2d, 0x1e, 0x95, 0xf9, 0xd8, 0x53, 0x57, 0x16, 0xfa, 0xb4, 0x1f,
|
||||
0x8b, 0x4f, 0x6c, 0x3c, 0xa4, 0x7c, 0xc7, 0xd2, 0xd9, 0x6d, 0x84, 0xa4, 0xb7, 0xdc, 0xa4, 0x3f,
|
||||
0xb8, 0x36, 0x12, 0xd6, 0x8e, 0x54, 0xe2, 0xeb, 0xc7, 0xa9, 0x50, 0xc6, 0xdb, 0x75, 0xab, 0xd9,
|
||||
0x2e, 0xd6, 0xbf, 0xdc, 0x4f, 0x0b, 0xe5, 0x13, 0xfc, 0xb1, 0x4d, 0xdf, 0xef, 0xb9, 0x69, 0xf2,
|
||||
0xc4, 0xa6, 0x54, 0x82, 0x76, 0xb5, 0x7f, 0x26, 0x7c, 0x3e, 0xc9, 0xb2, 0xa8, 0x78, 0x77, 0xf1,
|
||||
0xf3, 0x87, 0xce, 0x33, 0x4f, 0xfd, 0x91, 0x0c, 0x33, 0xe0, 0xe6, 0x80, 0x2b, 0xad, 0xeb, 0x8f,
|
||||
0x70, 0x85, 0xd3, 0x13, 0x90, 0x8e, 0x82, 0x2f, 0x51, 0xb8, 0x3b, 0x99, 0xf8, 0x09, 0x32, 0x5c,
|
||||
0xbb, 0xa7, 0xfb, 0x29, 0x22, 0x0b, 0x77, 0x31, 0xd9, 0xc5, 0x9e, 0x9b, 0x56, 0x4f, 0x6d, 0xba,
|
||||
0x6a, 0x17, 0xc4, 0x62, 0x64, 0x3a, 0x94, 0x08, 0xaa, 0x38, 0xab, 0xb2, 0xb5, 0xae, 0x78, 0xdd,
|
||||
0x5d, 0xe3, 0xd9, 0x7f, 0x94, 0x66, 0x67, 0x57, 0xa2, 0xa3, 0xbd, 0x88, 0x8e, 0xfe, 0x1e, 0xa2,
|
||||
0xc1, 0x5e, 0x44, 0x1f, 0x4f, 0xdc, 0x3b, 0x13, 0x0d, 0xf7, 0x22, 0xfa, 0xb8, 0x9f, 0xec, 0x4c,
|
||||
0xb4, 0xdc, 0x8b, 0xe8, 0xfc, 0xef, 0x21, 0x5a, 0xed, 0x45, 0xf4, 0x71, 0xe7, 0xdd, 0xdd, 0xa6,
|
||||
0x6f, 0xb2, 0xdd, 0x69, 0xc6, 0x4f, 0xd0, 0xdc, 0xf4, 0x48, 0x33, 0x3a, 0x02, 0xa0, 0x45, 0xda,
|
||||
0x35, 0x6d, 0x4c, 0x9f, 0x30, 0x5c, 0x47, 0x54, 0x0f, 0x84, 0xf9, 0xc5, 0x25, 0x64, 0xa0, 0xee,
|
||||
0x2c, 0x0a, 0xdf, 0x67, 0x61, 0x74, 0xed, 0xa1, 0x8a, 0x0c, 0x79, 0x72, 0xdd, 0x9a, 0x9f, 0x25,
|
||||
0x54, 0x63, 0xd6, 0x3d, 0x35, 0xcf, 0x3f, 0xeb, 0x1e, 0x5a, 0x7c, 0xec, 0x9c, 0xd8, 0x8a, 0xfb,
|
||||
0x12, 0x9b, 0x36, 0x58, 0x6d, 0x74, 0xb8, 0xfb, 0x4e, 0xe5, 0x7d, 0x3b, 0xe9, 0xd8, 0x29, 0x6c,
|
||||
0xae, 0x38, 0x41, 0xf3, 0x5f, 0xb2, 0x35, 0x2d, 0x28, 0x24, 0x68, 0x35, 0x57, 0xed, 0x9a, 0xe6,
|
||||
0xc2, 0x6d, 0x72, 0xc3, 0x7f, 0xfc, 0x0a, 0x23, 0xdd, 0xe2, 0xdf, 0x41, 0x23, 0xe7, 0xd3, 0x56,
|
||||
0x87, 0x68, 0x2b, 0xdf, 0x5f, 0xb4, 0x0e, 0xd6, 0x9d, 0xe1, 0x2c, 0xca, 0xda, 0xad, 0xb7, 0xaf,
|
||||
0x2f, 0x5a, 0xdd, 0x16, 0x5a, 0xe6, 0xd6, 0x21, 0x43, 0x12, 0x8d, 0x67, 0x97, 0xd2, 0xfa, 0xa6,
|
||||
0x8b, 0x2c, 0xa3, 0x2c, 0x6c, 0xb3, 0x56, 0x6f, 0x7d, 0x4e, 0xaa, 0x77, 0xaf, 0x27, 0x6d, 0x76,
|
||||
0xca, 0xe0, 0x34, 0x5e, 0xa2, 0x69, 0xad, 0x75, 0x83, 0xc3, 0xd5, 0xfc, 0x47, 0x31, 0xff, 0x71,
|
||||
0x6b, 0xfe, 0xad, 0x98, 0x7f, 0xbb, 0x35, 0xff, 0x4a, 0xcc, 0x8b, 0xb6, 0x8a, 0x8a, 0x18, 0x1a,
|
||||
0x8c, 0xb5, 0x28, 0x9f, 0x04, 0x58, 0xb4, 0x34, 0x5c, 0x72, 0xde, 0xa6, 0xd7, 0x59, 0xbb, 0xac,
|
||||
0xb3, 0xf6, 0xe6, 0x92, 0x2f, 0xda, 0xf6, 0xb3, 0x35, 0xcd, 0x73, 0x81, 0xb0, 0x6a, 0x11, 0xd6,
|
||||
0x80, 0xf7, 0x02, 0xb0, 0x2a, 0xf3, 0x0f, 0x91, 0x8b, 0xaa, 0x8f, 0x6f, 0x5f, 0xad, 0x8e, 0x22,
|
||||
0x45, 0x77, 0xdc, 0x1d, 0x75, 0x93, 0x6e, 0xdc, 0x9d, 0x75, 0xbf, 0x76, 0xf9, 0xc9, 0x74, 0xe2,
|
||||
0xf1, 0x82, 0xf6, 0x8e, 0xef, 0xd3, 0x2d, 0xc5, 0xf8, 0x5c, 0x8c, 0x17, 0x9e, 0x6e, 0x59, 0xc3,
|
||||
0xc4, 0x63, 0x6d, 0x7f, 0x9c, 0xe6, 0x38, 0xa7, 0x4c, 0x7e, 0xb0, 0x3b, 0xc3, 0xd8, 0xc3, 0x4b,
|
||||
0x4e, 0x86, 0x33, 0x6f, 0xf1, 0x43, 0x5b, 0x93, 0x71, 0x74, 0xfb, 0xca, 0xbf, 0xe2, 0x1f, 0xf0,
|
||||
0x5d, 0xf1, 0x6f, 0x1a, 0x76, 0x68, 0x5c, 0x5e, 0x25, 0x55, 0x30, 0x69, 0x27, 0xdf, 0xd9, 0x9d,
|
||||
0x65, 0xe0, 0x97, 0x91, 0xa4, 0x0e, 0x0a, 0x6f, 0xd1, 0x1d, 0x7b, 0x55, 0x77, 0xe4, 0xcd, 0x86,
|
||||
0x23, 0xb4, 0xfb, 0x5f, 0x86, 0x0c, 0xa0, 0x01, 0xf0, 0x15, 0x80, 0xc5, 0x36, 0x40, 0x07, 0x60,
|
||||
0x26, 0x00, 0x55, 0x1d, 0x60, 0x08, 0xc0, 0x57, 0x00, 0x16, 0x75, 0x80, 0x09, 0x40, 0x05, 0xc0,
|
||||
0x6c, 0x1b, 0x60, 0x09, 0xe2, 0x04, 0xf8, 0x0a, 0x75, 0x35, 0x1b, 0xfb, 0x62, 0xab, 0x27, 0x1f,
|
||||
0x6f, 0xb5, 0xd3, 0xa3, 0x21, 0xf9, 0x57, 0x43, 0xcb, 0x67, 0x0d, 0xa3, 0x9e, 0x71, 0xd3, 0xbc,
|
||||
0x39, 0xdb, 0x32, 0xea, 0x1d, 0xeb, 0x20, 0x79, 0x2d, 0xf8, 0x99, 0x07, 0xc7, 0xf6, 0xce, 0x49,
|
||||
0x9a, 0x97, 0xd1, 0x79, 0x54, 0x55, 0x49, 0x36, 0x2e, 0x81, 0x48, 0xa7, 0xe5, 0x56, 0x99, 0xbe,
|
||||
0xa4, 0x83, 0x27, 0x4b, 0xa7, 0x61, 0x52, 0xce, 0x52, 0xff, 0xc6, 0x6b, 0x65, 0x79, 0x16, 0xb5,
|
||||
0x04, 0xbc, 0x18, 0x8f, 0x1e, 0x47, 0x98, 0x94, 0x8f, 0xc3, 0xd3, 0x4f, 0x8f, 0xc2, 0xab, 0xf4,
|
||||
0xf2, 0x09, 0xf8, 0xd9, 0x13, 0xf0, 0xd3, 0x87, 0xe0, 0x90, 0x7b, 0x71, 0xa4, 0xb3, 0x73, 0x1c,
|
||||
0x05, 0xd2, 0xc3, 0xec, 0x8c, 0xd2, 0x3c, 0xf8, 0xd2, 0x7a, 0x44, 0x21, 0x02, 0xe1, 0xf6, 0x40,
|
||||
0xf8, 0xd9, 0x62, 0xed, 0x65, 0x8f, 0x28, 0x49, 0x2c, 0x62, 0x57, 0x1a, 0x0d, 0x07, 0x7c, 0x58,
|
||||
0x6f, 0x9b, 0x25, 0x5a, 0xd3, 0x35, 0x1f, 0x56, 0x85, 0x58, 0xd2, 0xf0, 0xd7, 0x87, 0x15, 0x7b,
|
||||
0x17, 0xdb, 0x1c, 0x3c, 0xac, 0xc6, 0xb5, 0xd8, 0xc1, 0x02, 0x0e, 0x3e, 0x0d, 0x47, 0x02, 0x81,
|
||||
0x4e, 0xf6, 0x5e, 0x3b, 0x9d, 0x1e, 0xab, 0x9d, 0x1f, 0xfd, 0x93, 0x41, 0x78, 0x52, 0x77, 0x3a,
|
||||
0xe8, 0x42, 0x9f, 0x44, 0xd7, 0xed, 0x22, 0x0a, 0xbb, 0xe3, 0x22, 0x8a, 0xb2, 0xee, 0x88, 0xaa,
|
||||
0x1e, 0x4f, 0x10, 0xe3, 0x91, 0x47, 0xa3, 0xbf, 0xda, 0x0c, 0x72, 0x74, 0xe4, 0x74, 0xfe, 0x22,
|
||||
0xc4, 0xa3, 0x23, 0xcd, 0xa6, 0x4c, 0x4b, 0xd7, 0x36, 0x2f, 0x9e, 0xbf, 0x38, 0x6c, 0xab, 0xd7,
|
||||
0xe2, 0xe0, 0x7f, 0x88, 0x25, 0x1d, 0xa5, 0xca, 0xcf, 0xab, 0x02, 0x6e, 0xdb, 0x06, 0x9a, 0x52,
|
||||
0xa6, 0x49, 0x10, 0xb5, 0xb5, 0x4e, 0x8d, 0x66, 0x0a, 0x58, 0xe1, 0x87, 0x6d, 0x96, 0x80, 0x3a,
|
||||
0xe2, 0xfe, 0xa7, 0x85, 0xd9, 0xc8, 0x2f, 0x64, 0x82, 0x24, 0x28, 0x24, 0xed, 0xd6, 0xe1, 0xe8,
|
||||
0xe4, 0x10, 0xc9, 0xff, 0x70, 0xcd, 0x22, 0x43, 0x3f, 0x6c, 0x75, 0x1a, 0xd9, 0x9d, 0x32, 0xdd,
|
||||
0x01, 0x0e, 0x0d, 0xa3, 0x3c, 0xbc, 0x11, 0x02, 0x8f, 0xfc, 0xe0, 0xcb, 0xb8, 0xc8, 0xe7, 0x59,
|
||||
0xe8, 0xad, 0x48, 0xd5, 0x22, 0xba, 0x5b, 0x8b, 0xe6, 0x6e, 0x2d, 0x92, 0xd9, 0xc5, 0xc8, 0xbb,
|
||||
0xf3, 0xdd, 0x50, 0xe3, 0xeb, 0xa6, 0x76, 0x59, 0x4e, 0xd8, 0xce, 0xdb, 0x1b, 0x85, 0x67, 0x69,
|
||||
0x13, 0x1d, 0xc7, 0xa7, 0x35, 0xa8, 0x7a, 0x10, 0x24, 0xdc, 0x97, 0x57, 0x79, 0x08, 0x59, 0x77,
|
||||
0xe2, 0x19, 0x12, 0xb0, 0xb6, 0xf6, 0x01, 0xb6, 0x14, 0x2b, 0xd6, 0x20, 0xfd, 0x0e, 0xe8, 0xae,
|
||||
0x4f, 0xdf, 0xb3, 0x85, 0xbf, 0xeb, 0x16, 0xfa, 0xb7, 0x70, 0xe1, 0x9f, 0xdc, 0xe3, 0xf8, 0xfb,
|
||||
0x71, 0xe1, 0x93, 0xf3, 0x56, 0xe3, 0xa6, 0xca, 0x6a, 0xe5, 0x7c, 0xa3, 0xf2, 0x78, 0x76, 0xb9,
|
||||
0x15, 0x20, 0xed, 0x07, 0x4c, 0x24, 0x02, 0x67, 0x20, 0xf2, 0x50, 0x9c, 0xc7, 0xdb, 0xbb, 0xdf,
|
||||
0x59, 0x65, 0x9b, 0x6b, 0x32, 0xd3, 0xe2, 0x29, 0xec, 0x23, 0xed, 0x9e, 0xb8, 0xbb, 0x18, 0x5f,
|
||||
0x88, 0x0b, 0xae, 0x26, 0xf7, 0x9b, 0xc2, 0x71, 0xe1, 0xa9, 0xad, 0x61, 0xfd, 0xde, 0x4a, 0xe5,
|
||||
0x9d, 0x68, 0x1d, 0x43, 0xa7, 0x50, 0xb8, 0xbf, 0xd6, 0x9f, 0x5f, 0xbd, 0xb9, 0x64, 0xd7, 0xab,
|
||||
0xec, 0xfa, 0xd6, 0xbb, 0x87, 0xaf, 0xc3, 0x92, 0x72, 0x6e, 0xe6, 0x79, 0xb2, 0xf6, 0xd7, 0x5f,
|
||||
0x78, 0xc5, 0xd7, 0x59, 0x87, 0xc7, 0xe2, 0xf0, 0xbe, 0x66, 0x37, 0x63, 0xd8, 0x47, 0x6a, 0xe7,
|
||||
0x3e, 0xa0, 0xca, 0x80, 0xc7, 0xb4, 0xc5, 0x7d, 0x60, 0xd6, 0x2e, 0x4c, 0x93, 0xac, 0x6d, 0x5b,
|
||||
0x5d, 0xe0, 0xc8, 0x1a, 0x18, 0xbe, 0x6c, 0xf2, 0x7b, 0x31, 0x7e, 0x77, 0xfe, 0x4a, 0xa8, 0x24,
|
||||
0xa0, 0x42, 0x70, 0xb0, 0xc4, 0xbb, 0xf3, 0x23, 0xbb, 0xf6, 0x1c, 0xe0, 0xa9, 0xad, 0x3b, 0xf1,
|
||||
0x5f, 0x91, 0xcc, 0xa8, 0x8d, 0xac, 0x0b, 0x4b, 0x91, 0x72, 0x76, 0xde, 0xbe, 0x66, 0xb9, 0x80,
|
||||
0xca, 0xed, 0x1b, 0xa1, 0xb5, 0xf5, 0x79, 0xa0, 0x31, 0xdd, 0x39, 0xbc, 0x26, 0x8e, 0x1b, 0x73,
|
||||
0xb0, 0x53, 0x73, 0xa9, 0x76, 0x07, 0xe5, 0x58, 0xb7, 0xb6, 0x70, 0x74, 0xab, 0x2e, 0xc4, 0xd9,
|
||||
0xcb, 0x6a, 0x53, 0xfb, 0xf9, 0xd2, 0x57, 0xeb, 0x9b, 0x0c, 0x78, 0xda, 0xf7, 0x67, 0x2f, 0x3d,
|
||||
0x0d, 0x9e, 0x46, 0x6f, 0x58, 0xb7, 0x8e, 0x77, 0xd2, 0xc4, 0x3b, 0x11, 0x78, 0x27, 0xdb, 0x78,
|
||||
0x6f, 0x9a, 0x78, 0x97, 0x02, 0xef, 0x92, 0xf0, 0xea, 0x9c, 0x9c, 0xc3, 0x21, 0x4a, 0xea, 0x27,
|
||||
0x18, 0x4f, 0x24, 0x09, 0x1b, 0xad, 0x7c, 0xe7, 0xec, 0xdc, 0x5b, 0xed, 0xdb, 0x10, 0x68, 0xdb,
|
||||
0xc7, 0xce, 0x3e, 0x3c, 0x84, 0x77, 0x70, 0xc7, 0xe5, 0x7e, 0x41, 0xcb, 0xbe, 0xe9, 0x58, 0x0e,
|
||||
0x96, 0xf4, 0xeb, 0x03, 0xbb, 0x7f, 0x2b, 0x9b, 0x39, 0x0e, 0x41, 0x1c, 0x84, 0xaf, 0xee, 0xef,
|
||||
0x02, 0xca, 0x2a, 0xde, 0x06, 0x24, 0x19, 0x95, 0x06, 0xd6, 0x20, 0x94, 0xa3, 0xb8, 0xc3, 0x30,
|
||||
0x8a, 0xc0, 0x6b, 0xf5, 0x4a, 0x41, 0x0b, 0xab, 0x46, 0xf1, 0xe6, 0x6a, 0x6f, 0xdd, 0x4f, 0x35,
|
||||
0x3b, 0x28, 0xce, 0x0f, 0xc7, 0xda, 0x62, 0x28, 0xbc, 0x97, 0xa1, 0x15, 0xdd, 0x7b, 0x58, 0xe2,
|
||||
0xbc, 0x36, 0xbc, 0xf7, 0xbc, 0xbd, 0xe9, 0xdd, 0x0e, 0x96, 0xdb, 0xdd, 0x1b, 0xf7, 0xdc, 0xa6,
|
||||
0x82, 0x1a, 0x1e, 0x7c, 0x31, 0x3e, 0x65, 0x3f, 0x6f, 0xd0, 0xc5, 0xda, 0x33, 0x3c, 0x58, 0x3c,
|
||||
0xa1, 0x14, 0xac, 0x0d, 0x71, 0x0a, 0x43, 0x1c, 0x36, 0xae, 0xb0, 0xd6, 0xdd, 0xff, 0xe9, 0xc5,
|
||||
0x1a, 0x74, 0xd1, 0x00, 0xb5, 0xb7, 0xee, 0xd2, 0xc8, 0x63, 0x4e, 0xdf, 0x70, 0x8f, 0xc1, 0x9b,
|
||||
0x3c, 0x86, 0xf1, 0x55, 0xa3, 0xa1, 0x3e, 0x9c, 0x51, 0xc0, 0x22, 0xe3, 0x90, 0x1f, 0x3a, 0x0f,
|
||||
0x19, 0x87, 0xec, 0xfb, 0xd8, 0xe8, 0xac, 0xcf, 0xb6, 0x0f, 0xd4, 0xac, 0xf5, 0x09, 0xe6, 0xd4,
|
||||
0x53, 0xbf, 0xff, 0x78, 0x4a, 0x64, 0x9a, 0x5d, 0xfe, 0x16, 0x5c, 0x6b, 0x6d, 0x35, 0xfb, 0x1b,
|
||||
0xb8, 0x76, 0x77, 0xbd, 0x71, 0x07, 0xae, 0x3d, 0x2c, 0x06, 0xaf, 0xf2, 0xa2, 0xc3, 0x41, 0x4f,
|
||||
0x81, 0xa6, 0x62, 0xd4, 0x2d, 0x3c, 0xbf, 0x18, 0xb3, 0x3b, 0x0b, 0x3a, 0xfe, 0xf7, 0x70, 0xc8,
|
||||
0xc1, 0x81, 0x60, 0x33, 0xa5, 0xf1, 0xa9, 0x51, 0x6d, 0x4a, 0xe7, 0x53, 0x93, 0x6e, 0x89, 0x43,
|
||||
0x11, 0xcf, 0x6f, 0xfe, 0xaa, 0x47, 0xe9, 0x86, 0x49, 0x1c, 0x7b, 0x0b, 0x79, 0x9d, 0xf6, 0x6a,
|
||||
0xd3, 0xc1, 0xe6, 0x07, 0x8e, 0x60, 0xd5, 0xfb, 0xb4, 0x17, 0x72, 0xd0, 0xe9, 0xd9, 0x3d, 0x82,
|
||||
0x1f, 0x6a, 0x3d, 0x7d, 0x78, 0xcb, 0x32, 0x0f, 0x6d, 0x42, 0x97, 0xe3, 0x13, 0xaf, 0x5c, 0x25,
|
||||
0xff, 0x25, 0x0e, 0x64, 0x98, 0xee, 0x2d, 0x86, 0x45, 0xc1, 0xbe, 0x82, 0x76, 0x81, 0xb3, 0xc1,
|
||||
0x58, 0x7c, 0x8f, 0x3b, 0xc3, 0xd1, 0x48, 0x7c, 0x8f, 0x58, 0xd0, 0x17, 0x9e, 0xe7, 0x2d, 0x68,
|
||||
0x8b, 0xd1, 0x48, 0x1e, 0x8f, 0xf9, 0x26, 0x12, 0xe6, 0xc7, 0xab, 0xf9, 0xb6, 0xd6, 0x33, 0x3a,
|
||||
0x87, 0x45, 0x21, 0x8f, 0x46, 0x1b, 0xe8, 0x68, 0x0d, 0xd5, 0x09, 0x3a, 0x1e, 0xcb, 0x45, 0x31,
|
||||
0x64, 0x87, 0xf6, 0xc9, 0x11, 0x31, 0x74, 0x88, 0xdc, 0xb8, 0x46, 0x9e, 0x1c, 0x6b, 0x98, 0x92,
|
||||
0x69, 0x8a, 0xa1, 0x94, 0x28, 0x63, 0x8d, 0x53, 0xa4, 0x37, 0x19, 0x36, 0x4e, 0x91, 0x5e, 0xd9,
|
||||
0x08, 0xd3, 0xf3, 0x76, 0x42, 0xc1, 0x29, 0xce, 0x84, 0xf7, 0xf8, 0xcc, 0xa7, 0x87, 0x9c, 0xe5,
|
||||
0xd5, 0x43, 0x5e, 0xf2, 0x71, 0xcb, 0x81, 0x6a, 0x0e, 0x72, 0xb6, 0xd5, 0x4f, 0xaf, 0x01, 0x27,
|
||||
0xad, 0xad, 0x43, 0xe1, 0x66, 0x33, 0x16, 0x14, 0x77, 0x1c, 0x69, 0x7e, 0xf2, 0x5b, 0x7b, 0x9d,
|
||||
0x5f, 0x1b, 0xa9, 0xfc, 0xb7, 0x7a, 0xc0, 0x9d, 0xfc, 0xc6, 0x03, 0x0e, 0xef, 0x75, 0x2a, 0x47,
|
||||
0x5e, 0xd5, 0xf8, 0xb1, 0xf0, 0x4c, 0xdb, 0x0a, 0xe6, 0x33, 0x5d, 0x00, 0xf4, 0x6d, 0xc0, 0x85,
|
||||
0x00, 0xac, 0x62, 0xfc, 0x0e, 0x43, 0x1f, 0xeb, 0xe7, 0xce, 0x33, 0x16, 0xcb, 0x77, 0x70, 0xc4,
|
||||
0xcf, 0x7c, 0xf1, 0x0c, 0xa8, 0x91, 0xd7, 0x3a, 0xca, 0x67, 0x34, 0x7d, 0xfc, 0xba, 0x28, 0xf2,
|
||||
0x42, 0xa2, 0xcb, 0x18, 0xe4, 0x26, 0x34, 0xe8, 0x65, 0xf5, 0xec, 0xa8, 0x27, 0x60, 0xad, 0x61,
|
||||
0x94, 0x7a, 0xf1, 0xec, 0x47, 0x76, 0x88, 0x1d, 0xb0, 0xf2, 0x3e, 0x8c, 0x23, 0x32, 0x16, 0x26,
|
||||
0x5f, 0xf4, 0xfe, 0x55, 0xe6, 0x59, 0x6f, 0xe6, 0xa7, 0x2f, 0x06, 0xe2, 0x3b, 0x8a, 0xe3, 0x17,
|
||||
0x38, 0x04, 0x4c, 0x22, 0xf8, 0x7d, 0x54, 0x7a, 0xc7, 0x94, 0x15, 0x9f, 0xe1, 0x4b, 0xc9, 0xbf,
|
||||
0x74, 0x96, 0x51, 0x5a, 0xbb, 0x47, 0xa2, 0x74, 0x2d, 0x7e, 0xfd, 0x25, 0x38, 0xad, 0x26, 0x6e,
|
||||
0xc5, 0x62, 0x1a, 0x62, 0x35, 0xc5, 0xe9, 0x35, 0xfb, 0xe1, 0x38, 0x2f, 0xda, 0x89, 0x94, 0x64,
|
||||
0x12, 0x01, 0x3a, 0xcb, 0xeb, 0xc3, 0x35, 0xfb, 0x12, 0x77, 0xab, 0x7f, 0xb6, 0x5a, 0x87, 0xc9,
|
||||
0x61, 0xeb, 0x9f, 0xad, 0xe3, 0xd6, 0x21, 0xe1, 0xfc, 0x9e, 0xfc, 0x71, 0xd8, 0x92, 0xda, 0x6c,
|
||||
0xb2, 0x53, 0x13, 0xe7, 0xf6, 0xa0, 0xc1, 0xc5, 0x35, 0xe4, 0xbb, 0x73, 0x81, 0xf7, 0x63, 0x3c,
|
||||
0x4b, 0x06, 0x74, 0xbf, 0x47, 0xcc, 0x43, 0x5b, 0xf4, 0x6b, 0x36, 0xed, 0xa9, 0xa4, 0x51, 0x36,
|
||||
0xae, 0x26, 0xe2, 0x26, 0xab, 0xa3, 0x04, 0x3e, 0x53, 0xc4, 0xe6, 0x77, 0xd4, 0x6d, 0x01, 0x3b,
|
||||
0xb7, 0x47, 0x3d, 0xf1, 0x97, 0x00, 0xd2, 0x11, 0x2b, 0x23, 0xc7, 0x83, 0x22, 0xcf, 0xab, 0x25,
|
||||
0xbf, 0xbf, 0x1f, 0x3c, 0x0f, 0xdd, 0x91, 0xa1, 0xa9, 0x43, 0x7e, 0xb3, 0x3e, 0x78, 0xae, 0x8e,
|
||||
0x8c, 0xc0, 0x0a, 0x87, 0xfc, 0xce, 0x7b, 0xf0, 0x5c, 0x0b, 0xf5, 0xbe, 0xa1, 0x0d, 0xf9, 0x6d,
|
||||
0xf4, 0xe0, 0xb9, 0xa1, 0x3b, 0x41, 0x40, 0xc3, 0x92, 0x23, 0xab, 0xb4, 0xb0, 0x6a, 0x82, 0x82,
|
||||
0x37, 0xd9, 0xe0, 0xd7, 0xa8, 0x08, 0xfd, 0xcc, 0xbf, 0x55, 0x82, 0xaa, 0x48, 0x3f, 0x8f, 0xf2,
|
||||
0xeb, 0xe5, 0x14, 0xa9, 0x2b, 0xc9, 0x06, 0xfe, 0xbc, 0xca, 0x87, 0x57, 0x49, 0x58, 0x4d, 0x06,
|
||||
0x8e, 0xba, 0xb8, 0x1a, 0x6e, 0xce, 0x4a, 0xfc, 0x87, 0xed, 0x01, 0xd4, 0xdd, 0xe6, 0xc4, 0x3b,
|
||||
0xc3, 0x59, 0x5e, 0x26, 0x24, 0xd6, 0xc0, 0x1f, 0x95, 0x79, 0x3a, 0xaf, 0xa2, 0x61, 0x95, 0xcf,
|
||||
0x06, 0x96, 0xf5, 0xdd, 0x30, 0x8d, 0xe2, 0x6a, 0x60, 0xa9, 0xdf, 0x0d, 0xab, 0xc2, 0xcf, 0x4a,
|
||||
0x58, 0x66, 0x3a, 0x60, 0x5f, 0x29, 0xb2, 0x7e, 0x5b, 0x06, 0xa0, 0x4b, 0x0f, 0x9c, 0x93, 0x92,
|
||||
0xb4, 0x8a, 0x8a, 0x41, 0x58, 0xe4, 0x33, 0xb9, 0x9c, 0xf8, 0x61, 0x7e, 0x05, 0xe8, 0xec, 0x5a,
|
||||
0x62, 0x0f, 0xfa, 0xcf, 0xc9, 0x91, 0x34, 0x9d, 0xce, 0xad, 0x52, 0x86, 0xe5, 0x92, 0x33, 0x87,
|
||||
0x03, 0xe5, 0x77, 0xc3, 0x49, 0x94, 0x8c, 0x27, 0xd5, 0x40, 0xd3, 0x17, 0x93, 0x21, 0xe7, 0x5f,
|
||||
0x26, 0x06, 0x30, 0x04, 0x6a, 0x2a, 0x44, 0x92, 0x19, 0x2f, 0x4c, 0x2e, 0x31, 0x51, 0xb0, 0x55,
|
||||
0xf5, 0x19, 0xb1, 0x4a, 0x08, 0xde, 0xef, 0x2f, 0xae, 0x6e, 0x9f, 0x97, 0xe9, 0xcb, 0x65, 0x0d,
|
||||
0xac, 0x02, 0xbc, 0x51, 0xc6, 0x60, 0xfb, 0x34, 0x5a, 0xe5, 0x12, 0xdb, 0x16, 0xc7, 0x63, 0x20,
|
||||
0x75, 0x6f, 0xa2, 0x34, 0xcd, 0xaf, 0x3a, 0xb4, 0xcb, 0xc7, 0xe5, 0xee, 0xcb, 0x70, 0x78, 0x66,
|
||||
0x6b, 0xde, 0xee, 0xb1, 0x86, 0x9d, 0xbd, 0xd9, 0xaa, 0x57, 0x7b, 0xac, 0x62, 0xa7, 0x78, 0x5a,
|
||||
0xf4, 0x69, 0x8f, 0x45, 0x57, 0x93, 0xa4, 0xe2, 0xab, 0xde, 0xed, 0xb4, 0x8a, 0x2e, 0x0d, 0xf2,
|
||||
0x82, 0x7e, 0xb5, 0x17, 0x0a, 0x11, 0x57, 0x08, 0xc1, 0x8d, 0xcf, 0xef, 0x11, 0xba, 0x8b, 0x24,
|
||||
0x4f, 0xa3, 0x6a, 0x2d, 0xf7, 0xf9, 0x4e, 0xdb, 0x62, 0x93, 0x9b, 0x9a, 0xd8, 0xa7, 0x7b, 0x48,
|
||||
0x80, 0x5c, 0xf2, 0x75, 0x9e, 0x27, 0x25, 0x97, 0xe2, 0x62, 0x8f, 0x95, 0x2b, 0x8b, 0x2a, 0x55,
|
||||
0x9e, 0xa7, 0xe5, 0x7f, 0xec, 0x5b, 0x97, 0xdf, 0xa4, 0xf6, 0xf7, 0xfb, 0xfa, 0x12, 0xdd, 0x7a,
|
||||
0x2c, 0xe3, 0x3c, 0xab, 0xe4, 0xd8, 0x9f, 0x26, 0xe9, 0xcd, 0x2a, 0x7c, 0xdf, 0x64, 0x9d, 0xee,
|
||||
0xbb, 0x28, 0x5d, 0x44, 0x55, 0x12, 0xf8, 0xdd, 0x12, 0x91, 0x29, 0x97, 0x51, 0x91, 0xc4, 0xc3,
|
||||
0x2a, 0xba, 0xae, 0x64, 0x3f, 0x4d, 0xc6, 0xd9, 0x20, 0x88, 0xe8, 0x0f, 0x49, 0x1e, 0x73, 0x7a,
|
||||
0xbe, 0x19, 0xe5, 0xa5, 0x0e, 0x27, 0xda, 0x59, 0x87, 0x24, 0x85, 0xa7, 0x48, 0x29, 0x6a, 0x3d,
|
||||
0x89, 0x14, 0xd1, 0x2c, 0xf2, 0xab, 0x41, 0x96, 0x8b, 0xaf, 0x3a, 0xcc, 0xaf, 0x2a, 0x3f, 0x98,
|
||||
0x50, 0xfb, 0x34, 0x88, 0x93, 0xeb, 0x28, 0x1c, 0xd6, 0x33, 0x0e, 0x25, 0xb1, 0xce, 0x2d, 0xfd,
|
||||
0xcd, 0xcb, 0xb2, 0x46, 0xe3, 0x36, 0x89, 0x0b, 0x7f, 0x1a, 0x2d, 0x45, 0xe7, 0x3d, 0xa0, 0xc6,
|
||||
0x7b, 0x38, 0xca, 0x8b, 0x10, 0x49, 0x45, 0xdd, 0x2f, 0xbb, 0x0c, 0x1f, 0xc8, 0x80, 0x82, 0x9a,
|
||||
0x4d, 0x09, 0xe0, 0x3f, 0x4e, 0x78, 0xb7, 0xe5, 0x62, 0xcc, 0xfe, 0x94, 0x46, 0x48, 0x15, 0xb2,
|
||||
0x3c, 0x2a, 0x52, 0x9a, 0xbe, 0xa1, 0xa6, 0xa9, 0x0b, 0xf4, 0x85, 0x7b, 0xa6, 0xc7, 0x24, 0x9b,
|
||||
0xcd, 0xab, 0xe5, 0x7e, 0x42, 0x3f, 0xe0, 0x1b, 0x1b, 0x8f, 0xb8, 0x1d, 0xcd, 0xab, 0x2a, 0xcf,
|
||||
0xea, 0x8e, 0x57, 0x33, 0x7b, 0xc3, 0x44, 0x5c, 0x18, 0xa1, 0x7d, 0xc5, 0x0a, 0x26, 0x12, 0x94,
|
||||
0x94, 0x84, 0x52, 0x1d, 0x5f, 0x04, 0xc5, 0x28, 0xc7, 0xa6, 0xd3, 0x81, 0x06, 0x9d, 0xfe, 0xdd,
|
||||
0xfc, 0xf2, 0x4a, 0xbd, 0x0b, 0xbf, 0xd5, 0x0e, 0xfc, 0xfe, 0xdd, 0xdc, 0x31, 0x1b, 0xfd, 0xce,
|
||||
0xfe, 0x78, 0x30, 0x9b, 0x4f, 0x47, 0x51, 0xf1, 0xc7, 0xdf, 0xc5, 0x29, 0x77, 0x22, 0x23, 0x9a,
|
||||
0xd6, 0x69, 0xb0, 0xe4, 0xfb, 0xc7, 0x52, 0xbe, 0x8a, 0x46, 0x5f, 0x12, 0x04, 0xf6, 0x0c, 0x31,
|
||||
0x87, 0xb9, 0x20, 0xe2, 0x91, 0x22, 0x5c, 0x5e, 0x36, 0x21, 0x88, 0x7a, 0x77, 0xdd, 0x20, 0xce,
|
||||
0x83, 0x79, 0xb9, 0xcc, 0xe7, 0x15, 0x45, 0xfe, 0xe0, 0x3e, 0x8c, 0xc1, 0x6a, 0xeb, 0x12, 0xfc,
|
||||
0x44, 0x85, 0x5c, 0xcc, 0xb3, 0xcc, 0x1f, 0xa5, 0x91, 0x0c, 0xef, 0x0f, 0xbe, 0x3c, 0x54, 0xac,
|
||||
0x83, 0x79, 0x51, 0x42, 0xb8, 0x59, 0x9e, 0x6c, 0xa7, 0x98, 0x9a, 0x44, 0x3b, 0x50, 0xab, 0x26,
|
||||
0xd0, 0xe1, 0x9e, 0x1e, 0xbf, 0x89, 0xb0, 0x55, 0x4e, 0xd6, 0xb6, 0x6a, 0x3b, 0x47, 0xf7, 0xb9,
|
||||
0x15, 0x9a, 0x9c, 0x3e, 0xae, 0x48, 0x96, 0xeb, 0xe1, 0xd3, 0xf7, 0x72, 0x3e, 0xcd, 0xff, 0x94,
|
||||
0xd9, 0xe0, 0xbf, 0xa5, 0x9a, 0x1a, 0x81, 0xff, 0xb5, 0x5a, 0x76, 0x11, 0xbf, 0xfc, 0x56, 0xb9,
|
||||
0x59, 0x1e, 0x9d, 0xf9, 0x05, 0xfd, 0x75, 0x2c, 0x0f, 0x01, 0xd1, 0x8a, 0xd6, 0x01, 0x77, 0x66,
|
||||
0x1e, 0x62, 0x82, 0x92, 0xaf, 0x8c, 0x4a, 0x1e, 0x15, 0xf5, 0xc0, 0x7b, 0xfe, 0xa4, 0x72, 0xc5,
|
||||
0xca, 0x39, 0x0c, 0xbf, 0xf7, 0x4a, 0x6e, 0x8f, 0xbd, 0xb5, 0x5a, 0x33, 0x09, 0x1d, 0x12, 0xd8,
|
||||
0xd9, 0x40, 0x9c, 0x11, 0xa4, 0x24, 0xf4, 0x26, 0x79, 0x0a, 0x5d, 0xfc, 0x0b, 0x99, 0x85, 0xcd,
|
||||
0x30, 0xaa, 0x54, 0xbe, 0x7b, 0x41, 0x59, 0x1e, 0xaf, 0xf0, 0x8f, 0x7a, 0xfc, 0xef, 0x9f, 0xa9,
|
||||
0x11, 0x90, 0xf2, 0x8c, 0xfd, 0xb9, 0xc1, 0xfa, 0x4f, 0x4e, 0xa5, 0x80, 0x7e, 0xc7, 0xf7, 0x3e,
|
||||
0x7f, 0x46, 0xed, 0x4c, 0xb2, 0xcf, 0xb4, 0xf8, 0xf3, 0xc7, 0xd7, 0x2f, 0x7f, 0xfa, 0xed, 0xf3,
|
||||
0x67, 0x22, 0x34, 0xf3, 0x33, 0x81, 0x41, 0x3f, 0xf2, 0x1f, 0x7f, 0xe0, 0xa7, 0x3f, 0x45, 0x51,
|
||||
0xb0, 0x39, 0x60, 0x84, 0xb2, 0x18, 0x4b, 0x8c, 0x8e, 0x77, 0xb7, 0x32, 0x72, 0x71, 0xd5, 0x95,
|
||||
0x14, 0xea, 0x30, 0x5f, 0x44, 0x45, 0x0c, 0xcd, 0x0f, 0x26, 0x49, 0x18, 0x46, 0x38, 0x94, 0x45,
|
||||
0x45, 0x89, 0x15, 0x9e, 0xa6, 0x68, 0xd2, 0xf5, 0x34, 0xcd, 0x4a, 0x6f, 0x52, 0x55, 0xb3, 0x41,
|
||||
0xaf, 0x77, 0x75, 0x75, 0xa5, 0x5c, 0x19, 0x4a, 0x5e, 0x8c, 0x7b, 0x3a, 0x4e, 0x30, 0x3d, 0x50,
|
||||
0x01, 0xad, 0x30, 0x8a, 0xcb, 0x63, 0xa9, 0xd7, 0xfb, 0xc0, 0x9a, 0x10, 0xfa, 0x13, 0xeb, 0x52,
|
||||
0x09, 0xf2, 0x69, 0x2f, 0x46, 0x23, 0x08, 0x4e, 0x6e, 0xa6, 0xa3, 0x3c, 0x25, 0xb5, 0xa4, 0x59,
|
||||
0x21, 0xcf, 0xc8, 0xbe, 0x32, 0xbf, 0x32, 0x90, 0x16, 0x49, 0x74, 0xf5, 0x2a, 0xc7, 0x11, 0x51,
|
||||
0x95, 0x54, 0x49, 0x53, 0x75, 0x93, 0x3d, 0x5a, 0xc7, 0x47, 0xf4, 0xfb, 0x81, 0x14, 0x7a, 0xad,
|
||||
0x9f, 0x4d, 0xc7, 0x56, 0x4c, 0xc9, 0xd6, 0x4c, 0xc5, 0x0c, 0x64, 0x3c, 0x35, 0xc3, 0x91, 0x54,
|
||||
0x59, 0xb7, 0x14, 0x5b, 0xd6, 0x34, 0xc5, 0xb4, 0x35, 0xfe, 0x4d, 0x8f, 0x85, 0x6c, 0xda, 0xaa,
|
||||
0xe2, 0x04, 0xea, 0x0a, 0x8f, 0x21, 0xe8, 0x0c, 0x26, 0xad, 0xb1, 0x4a, 0x36, 0xe4, 0x20, 0x69,
|
||||
0x0d, 0x5a, 0x88, 0xa5, 0x12, 0x5b, 0xea, 0xca, 0x35, 0xf8, 0x66, 0xfd, 0x9f, 0x60, 0xac, 0x47,
|
||||
0x9c, 0xdd, 0xe1, 0xcf, 0xed, 0xeb, 0x58, 0x2d, 0x83, 0x79, 0xc5, 0xea, 0x83, 0x3f, 0xcd, 0x75,
|
||||
0x65, 0xc3, 0x55, 0x5c, 0xd3, 0x94, 0xf5, 0xbe, 0xa6, 0x58, 0x06, 0x36, 0xd4, 0x15, 0xb3, 0x2f,
|
||||
0x03, 0xcf, 0xd2, 0x9d, 0xd5, 0x4b, 0x4c, 0x6a, 0xb6, 0xa3, 0xb8, 0xf6, 0x6a, 0xc4, 0x17, 0x60,
|
||||
0x13, 0xc7, 0x54, 0x0c, 0xd7, 0x92, 0x74, 0x5b, 0x71, 0x2c, 0x70, 0x64, 0x43, 0x05, 0x7d, 0x47,
|
||||
0xea, 0xf7, 0x15, 0xbb, 0x8f, 0x6d, 0x0d, 0x60, 0xb9, 0xa6, 0x64, 0xba, 0x8a, 0x66, 0xc9, 0x80,
|
||||
0xb9, 0x9a, 0x05, 0xa9, 0xb0, 0x51, 0x9f, 0xb8, 0x37, 0xb0, 0x14, 0xca, 0x74, 0x6d, 0xc5, 0xd6,
|
||||
0x4d, 0x59, 0x33, 0x6c, 0xc5, 0x52, 0x35, 0x09, 0xd3, 0x96, 0x25, 0x9b, 0x8a, 0x6a, 0x98, 0x92,
|
||||
0xde, 0x57, 0x1c, 0xd5, 0x92, 0x0c, 0xc5, 0x76, 0x0c, 0x09, 0x9b, 0x39, 0xa4, 0xb0, 0xbe, 0xa2,
|
||||
0x1b, 0x66, 0x29, 0xf3, 0x49, 0x8e, 0x21, 0xf3, 0x49, 0x81, 0x02, 0x19, 0x49, 0x2e, 0xcb, 0x92,
|
||||
0x4c, 0x4d, 0xb1, 0x2d, 0xe2, 0xa3, 0xaf, 0xf4, 0x55, 0x1d, 0x4b, 0xa1, 0x81, 0xcd, 0xd0, 0xd0,
|
||||
0x5c, 0x45, 0xd5, 0x75, 0xb2, 0xaa, 0x03, 0xa2, 0x2a, 0x34, 0x0e, 0x4e, 0x75, 0xf0, 0x64, 0x18,
|
||||
0x40, 0xac, 0x3f, 0x4b, 0xf6, 0x94, 0x1b, 0x70, 0x99, 0x3d, 0x99, 0x25, 0x6d, 0x45, 0x73, 0xfa,
|
||||
0xb2, 0x8b, 0x6d, 0x2d, 0xa8, 0xa6, 0xaf, 0x18, 0x7d, 0x41, 0x45, 0x93, 0x39, 0x11, 0x12, 0x0f,
|
||||
0x62, 0xe9, 0x5c, 0x2e, 0x59, 0xd7, 0x14, 0xdd, 0x86, 0x4e, 0x1d, 0x45, 0x77, 0x1c, 0xc1, 0xbc,
|
||||
0x2c, 0xe4, 0x23, 0x0c, 0x5d, 0xa0, 0x4b, 0x84, 0xe1, 0x0a, 0x74, 0x2e, 0x5d, 0x9f, 0xa3, 0x5b,
|
||||
0x4c, 0xc7, 0x26, 0x6c, 0x6e, 0x28, 0x1a, 0xe3, 0xbc, 0xaf, 0x98, 0x50, 0x3c, 0x14, 0xee, 0xe8,
|
||||
0x5c, 0xa7, 0x06, 0xde, 0xd0, 0xa9, 0xe9, 0xba, 0x92, 0x05, 0xbf, 0xd1, 0x6c, 0xc9, 0xee, 0x2b,
|
||||
0x9a, 0xc6, 0x57, 0xf6, 0x2d, 0xb6, 0x44, 0x87, 0x1a, 0xc4, 0x90, 0x1b, 0xcb, 0xe1, 0x3e, 0x0e,
|
||||
0x0f, 0xe1, 0xbe, 0x41, 0x7b, 0x41, 0x00, 0x55, 0x17, 0x96, 0x97, 0x84, 0xab, 0x70, 0xe7, 0x90,
|
||||
0xd8, 0xcb, 0x16, 0xce, 0x21, 0x35, 0x9d, 0x83, 0x8f, 0x6a, 0xfe, 0xd8, 0xe3, 0x41, 0x76, 0xbc,
|
||||
0x1d, 0x6d, 0x74, 0x1d, 0xb4, 0x43, 0x94, 0xb9, 0xae, 0x03, 0x2f, 0x76, 0xa0, 0x4f, 0x7d, 0x22,
|
||||
0xf7, 0x6d, 0xe7, 0xa9, 0x50, 0x2b, 0xef, 0x0d, 0xad, 0x09, 0xad, 0xe4, 0x01, 0x04, 0x42, 0x9b,
|
||||
0x38, 0xd3, 0x36, 0x71, 0x56, 0xca, 0xb5, 0x99, 0xc7, 0x23, 0x8b, 0xf3, 0x64, 0xd9, 0xc6, 0xff,
|
||||
0x3b, 0x9e, 0x0c, 0xb5, 0xff, 0x18, 0x4f, 0xfa, 0xb7, 0xf2, 0xa4, 0x7f, 0x13, 0x4f, 0x7d, 0x9b,
|
||||
0x22, 0xc9, 0x72, 0x28, 0x41, 0x9a, 0xba, 0x62, 0x98, 0x94, 0x80, 0x68, 0x52, 0x36, 0x90, 0x31,
|
||||
0xc8, 0x9f, 0xe8, 0x9b, 0x1e, 0x65, 0x6d, 0x42, 0x5a, 0xcf, 0x4a, 0x7c, 0x03, 0x06, 0x92, 0x36,
|
||||
0xa0, 0xda, 0xc4, 0x06, 0xed, 0x4f, 0x4e, 0x4d, 0xb7, 0x6c, 0x2e, 0x39, 0x3c, 0x9e, 0x4b, 0xce,
|
||||
0x04, 0x70, 0xac, 0x0d, 0xa7, 0x25, 0x9f, 0xd8, 0x48, 0xb4, 0x79, 0xc8, 0x35, 0x90, 0x5c, 0x9b,
|
||||
0xd8, 0xa8, 0x4d, 0x7e, 0x54, 0x56, 0x51, 0x0c, 0xee, 0x91, 0x55, 0xdb, 0xc8, 0x8a, 0x79, 0x81,
|
||||
0x70, 0xaf, 0xd0, 0xe5, 0xbd, 0x42, 0x23, 0xff, 0xb3, 0x45, 0xee, 0xa3, 0xd2, 0x5b, 0x9a, 0xfe,
|
||||
0x7f, 0x28, 0xbd, 0xd3, 0x57, 0x9f, 0x96, 0xfe, 0x49, 0x4b, 0x6b, 0x3b, 0x5a, 0x7a, 0xed, 0xe3,
|
||||
0xff, 0x23, 0x59, 0x1f, 0xca, 0x63, 0xd3, 0x9c, 0xae, 0x8a, 0x9f, 0xcc, 0x63, 0x96, 0x6e, 0x82,
|
||||
0x69, 0x9a, 0x25, 0xae, 0x55, 0x45, 0xeb, 0xbb, 0xc4, 0x36, 0x92, 0xa6, 0x6b, 0x3b, 0xb2, 0x65,
|
||||
0x22, 0x83, 0xc3, 0xb6, 0x18, 0xaa, 0x2e, 0x6a, 0x8b, 0x65, 0xa0, 0x8a, 0x94, 0xe2, 0x8d, 0xa2,
|
||||
0xa2, 0xa2, 0x88, 0xae, 0x46, 0x02, 0x89, 0x4a, 0x90, 0x66, 0x29, 0x3a, 0x92, 0x35, 0xca, 0x83,
|
||||
0x8e, 0xed, 0x55, 0x8d, 0x68, 0xdb, 0x8a, 0xeb, 0x22, 0x8d, 0x43, 0x33, 0x16, 0x0a, 0x85, 0x61,
|
||||
0xb0, 0x72, 0x6e, 0xa2, 0xea, 0xda, 0xa8, 0x01, 0x06, 0xab, 0xd0, 0x0e, 0x9a, 0x27, 0x8b, 0x6a,
|
||||
0xb2, 0xab, 0x18, 0xba, 0xc5, 0x72, 0xbc, 0xa1, 0x1b, 0x12, 0x0a, 0x9a, 0x6a, 0xa9, 0x54, 0x91,
|
||||
0x4c, 0xf4, 0x0d, 0xa8, 0x25, 0xb6, 0x29, 0x5b, 0x96, 0x62, 0x92, 0x34, 0xa8, 0x25, 0x26, 0xc8,
|
||||
0x80, 0xb4, 0x0e, 0xad, 0xb9, 0x28, 0x2c, 0x28, 0x50, 0x8a, 0x8b, 0xca, 0xad, 0xab, 0x0c, 0x43,
|
||||
0x55, 0x0c, 0xc3, 0xa6, 0x8e, 0xc0, 0x45, 0x9b, 0x81, 0xfa, 0xa5, 0xeb, 0x25, 0x2a, 0x21, 0x55,
|
||||
0x6e, 0x14, 0x09, 0x94, 0x2a, 0x43, 0x51, 0x6d, 0x94, 0x76, 0x07, 0x5c, 0x22, 0x42, 0xc1, 0x96,
|
||||
0x8a, 0x49, 0x34, 0x07, 0x06, 0x5a, 0x1a, 0xd3, 0x41, 0xed, 0x74, 0x25, 0x0d, 0x1a, 0x47, 0x85,
|
||||
0x5e, 0x0f, 0x5d, 0x83, 0x1b, 0x17, 0x45, 0x0a, 0x45, 0xa9, 0x8f, 0x09, 0x88, 0x0b, 0x36, 0x0d,
|
||||
0xbc, 0x1a, 0x4f, 0x54, 0x38, 0x53, 0x33, 0x49, 0xf3, 0xd8, 0xbf, 0xdf, 0x87, 0x2d, 0x2d, 0xec,
|
||||
0xa0, 0xad, 0x76, 0xe0, 0x1b, 0x3a, 0xe0, 0x5a, 0x53, 0xfb, 0xa8, 0xc5, 0x50, 0x01, 0x71, 0xad,
|
||||
0x19, 0xe8, 0x26, 0x4c, 0xdb, 0x15, 0x4c, 0x71, 0x0e, 0xc9, 0x65, 0x34, 0xea, 0x19, 0x00, 0x36,
|
||||
0x99, 0x1c, 0xa6, 0x10, 0x2a, 0xa0, 0x5d, 0x1d, 0xe4, 0x3d, 0xb2, 0x42, 0xdf, 0xa0, 0x42, 0xe9,
|
||||
0x98, 0x50, 0xa2, 0x0a, 0x19, 0x1c, 0xa1, 0x19, 0x5b, 0x28, 0xca, 0x22, 0x25, 0x6a, 0xb6, 0x2d,
|
||||
0x99, 0xf0, 0x21, 0xf4, 0x40, 0x50, 0x81, 0x85, 0xf8, 0x80, 0x78, 0x30, 0xcd, 0x4a, 0xdd, 0x2b,
|
||||
0xf5, 0xbb, 0x2a, 0xea, 0x26, 0x74, 0x01, 0x19, 0x5d, 0x87, 0xfa, 0x33, 0xc5, 0xa0, 0xd2, 0xbe,
|
||||
0x65, 0x48, 0x31, 0xfe, 0xf3, 0x67, 0xa8, 0x19, 0x1d, 0x54, 0x9f, 0xf0, 0x1d, 0x97, 0x92, 0x1d,
|
||||
0xaa, 0x71, 0xdf, 0xd4, 0xc8, 0xb6, 0xae, 0x83, 0x7c, 0x0e, 0x2a, 0x1a, 0xc0, 0x3a, 0x7c, 0x4c,
|
||||
0x45, 0x73, 0xb6, 0x1a, 0x9b, 0x3a, 0xed, 0x4b, 0x4a, 0xd2, 0x6d, 0xf0, 0x06, 0x0b, 0xeb, 0x54,
|
||||
0xa9, 0x2d, 0xf0, 0x48, 0x5e, 0xd1, 0x78, 0x6a, 0x8e, 0xcb, 0x1a, 0x2b, 0x15, 0xc9, 0x1b, 0x9a,
|
||||
0xd4, 0x4c, 0xd1, 0x06, 0x58, 0xab, 0x5d, 0x56, 0xbb, 0xca, 0x16, 0xa6, 0x89, 0x59, 0x97, 0x9a,
|
||||
0x08, 0x34, 0x99, 0xa4, 0x77, 0xb4, 0x67, 0x2e, 0x03, 0x6a, 0x68, 0x5c, 0x2d, 0x90, 0x5c, 0x0d,
|
||||
0xf1, 0x76, 0xa9, 0xf1, 0x42, 0x64, 0xe9, 0xd0, 0x35, 0x56, 0xab, 0xd4, 0x28, 0x22, 0xca, 0xc1,
|
||||
0xba, 0x01, 0xab, 0x21, 0x73, 0x50, 0x49, 0x72, 0xd1, 0xf1, 0xd8, 0x2e, 0x7b, 0xf5, 0xc5, 0x24,
|
||||
0x35, 0x4f, 0xe4, 0xe0, 0xf6, 0x7a, 0xcc, 0x17, 0x51, 0x32, 0x05, 0x8b, 0x9a, 0xa1, 0xc1, 0xba,
|
||||
0x00, 0xaa, 0x30, 0x25, 0xba, 0x99, 0x26, 0x03, 0x3b, 0x04, 0x71, 0x90, 0x8f, 0x77, 0x88, 0x61,
|
||||
0x03, 0xa6, 0x22, 0xb7, 0x52, 0x41, 0xa6, 0x6f, 0x59, 0x01, 0x22, 0x40, 0x53, 0x29, 0x8c, 0x4d,
|
||||
0x84, 0xa1, 0x23, 0xab, 0xe8, 0xd7, 0x74, 0xd9, 0x26, 0x5f, 0x93, 0xa9, 0xf5, 0x92, 0x61, 0x7d,
|
||||
0xb7, 0xcf, 0x32, 0x8b, 0xa3, 0x1a, 0x64, 0x67, 0x8d, 0xf2, 0x37, 0xec, 0x62, 0x52, 0xab, 0x07,
|
||||
0x87, 0x41, 0x50, 0x23, 0xf2, 0x75, 0x8d, 0x50, 0x5c, 0x1d, 0x49, 0x11, 0x51, 0x81, 0x86, 0xdc,
|
||||
0x26, 0xdf, 0xd4, 0x75, 0x02, 0xb8, 0xd4, 0x1d, 0x53, 0x1e, 0x34, 0x4c, 0x72, 0x47, 0x38, 0x33,
|
||||
0xf4, 0xe0, 0xea, 0xf0, 0x3b, 0x32, 0x9e, 0xcb, 0xda, 0x47, 0xf8, 0x99, 0x18, 0xc1, 0xd1, 0x5d,
|
||||
0x97, 0x34, 0x6c, 0xd9, 0x4c, 0x49, 0xc4, 0x00, 0x29, 0x16, 0xad, 0x9d, 0xd9, 0x7c, 0xc2, 0xbf,
|
||||
0x75, 0xca, 0x9b, 0xd8, 0x59, 0x25, 0x3f, 0x85, 0x55, 0xd7, 0xcb, 0xc5, 0x66, 0x78, 0x69, 0x86,
|
||||
0x21, 0x51, 0x9f, 0x4e, 0xdd, 0x1d, 0x45, 0xaa, 0x04, 0x88, 0x2e, 0xd8, 0xd1, 0x05, 0x6f, 0x48,
|
||||
0x3c, 0x1a, 0xa2, 0x8a, 0x7c, 0x4a, 0xa7, 0xdc, 0x6f, 0xaa, 0x8e, 0xec, 0x90, 0xd7, 0x58, 0x5c,
|
||||
0x36, 0x4d, 0x88, 0x4a, 0xd1, 0x66, 0x18, 0x06, 0x6d, 0xeb, 0x3a, 0x2e, 0xa2, 0xcd, 0x85, 0xb3,
|
||||
0xda, 0x54, 0xd2, 0x24, 0x8d, 0x68, 0x30, 0xcd, 0xe0, 0xc5, 0xda, 0x77, 0x1c, 0x52, 0xc8, 0xde,
|
||||
0x06, 0x73, 0xf3, 0x3e, 0xf6, 0x47, 0xd0, 0xd1, 0xd1, 0x40, 0x8c, 0xd0, 0x73, 0x03, 0x56, 0xca,
|
||||
0x2c, 0x72, 0x41, 0xce, 0x90, 0x19, 0xc0, 0x61, 0x00, 0x53, 0x0d, 0x88, 0x48, 0x9f, 0xc5, 0xba,
|
||||
0xde, 0xa7, 0x90, 0x42, 0x8e, 0xd5, 0x49, 0x66, 0xdb, 0x70, 0x64, 0x8d, 0x0b, 0xc4, 0x88, 0xf5,
|
||||
0x25, 0x61, 0x1b, 0x6e, 0x29, 0x49, 0xd8, 0x86, 0x5b, 0x4a, 0x6a, 0xb0, 0x8f, 0x00, 0xe3, 0xc6,
|
||||
0x61, 0x96, 0x92, 0xb8, 0x75, 0x98, 0xa5, 0x24, 0x6e, 0x1d, 0xae, 0x0e, 0x89, 0x99, 0xc7, 0x60,
|
||||
0xa6, 0x62, 0xad, 0x3b, 0x92, 0x1e, 0xd7, 0xa7, 0xc4, 0xb4, 0xbb, 0x1a, 0x70, 0x03, 0x89, 0xb6,
|
||||
0x7b, 0x65, 0x28, 0xa9, 0x66, 0x22, 0x3a, 0xaa, 0x91, 0x91, 0x6c, 0x99, 0xdb, 0x86, 0x19, 0x8a,
|
||||
0x82, 0x68, 0x63, 0x68, 0x99, 0xab, 0x8d, 0xd9, 0x09, 0x01, 0x02, 0xd3, 0xe8, 0xcc, 0x4e, 0x26,
|
||||
0x05, 0x43, 0xcd, 0x6d, 0x98, 0x6d, 0xb8, 0xa1, 0x6c, 0x96, 0x85, 0x28, 0xa2, 0xb9, 0xa5, 0xa4,
|
||||
0x86, 0x1b, 0x6a, 0xc4, 0x9d, 0x4b, 0x11, 0x09, 0x4b, 0xb1, 0xbc, 0x47, 0xd6, 0x21, 0x4b, 0x49,
|
||||
0x2b, 0x75, 0xd9, 0xfc, 0x65, 0x92, 0x9a, 0x69, 0xe3, 0xbe, 0xca, 0x12, 0xe2, 0xda, 0x52, 0xc8,
|
||||
0xb2, 0xcc, 0x04, 0x62, 0x54, 0xc2, 0x34, 0xdc, 0x48, 0x6c, 0x1e, 0xea, 0xe7, 0x16, 0x0c, 0x40,
|
||||
0xce, 0x61, 0x56, 0xd2, 0x28, 0xd7, 0xda, 0xa6, 0xc5, 0xad, 0x64, 0x71, 0x42, 0x2e, 0xb7, 0x92,
|
||||
0x2b, 0x09, 0xbb, 0x30, 0x2b, 0xe9, 0x12, 0xd9, 0xc5, 0x12, 0x46, 0x12, 0x9c, 0x1b, 0xd2, 0x8a,
|
||||
0x73, 0x8d, 0x25, 0x17, 0x66, 0x24, 0x62, 0x13, 0xa5, 0x80, 0x19, 0x49, 0x75, 0xb8, 0x32, 0x24,
|
||||
0xe1, 0xb4, 0x64, 0x18, 0x9d, 0x5b, 0xc9, 0x66, 0xda, 0x44, 0x46, 0x67, 0x76, 0xea, 0xaf, 0x46,
|
||||
0xab, 0x20, 0x5a, 0xd9, 0xa8, 0x6e, 0x9b, 0xe6, 0x93, 0xd9, 0x87, 0xb2, 0x24, 0xb7, 0x0d, 0x33,
|
||||
0x94, 0x58, 0x2e, 0x0c, 0x25, 0x31, 0xe3, 0x38, 0xdc, 0x52, 0x12, 0x37, 0x0e, 0xb3, 0x94, 0xe0,
|
||||
0xc7, 0x14, 0xcc, 0x31, 0xdb, 0xd8, 0xc2, 0x52, 0x92, 0xc9, 0xb3, 0x2a, 0xb7, 0x14, 0x17, 0x70,
|
||||
0x65, 0x28, 0x89, 0x9b, 0x86, 0x07, 0x11, 0xec, 0xe4, 0x42, 0xb3, 0xdc, 0x4e, 0xc2, 0xb1, 0x85,
|
||||
0xa5, 0x64, 0x61, 0x1b, 0x6e, 0x29, 0x11, 0x1e, 0xc2, 0x52, 0x72, 0xc3, 0x52, 0xa5, 0xb0, 0x0f,
|
||||
0x8e, 0xd5, 0xb5, 0x79, 0x9c, 0xa7, 0x79, 0x0c, 0x49, 0x64, 0x29, 0x89, 0x87, 0x90, 0xc4, 0x43,
|
||||
0x48, 0xaa, 0x53, 0x72, 0x45, 0x7a, 0x13, 0xa6, 0x12, 0xe9, 0x8d, 0x9b, 0x4a, 0x6e, 0xb0, 0xce,
|
||||
0xb2, 0x9b, 0xc5, 0x0d, 0x65, 0xc9, 0xc2, 0x36, 0xdc, 0x52, 0x72, 0x4d, 0x19, 0xba, 0x2c, 0x8c,
|
||||
0xc3, 0x2c, 0x25, 0x37, 0x6c, 0x23, 0xd7, 0x94, 0x8b, 0xd1, 0x2a, 0x88, 0x44, 0xc4, 0xd4, 0xb3,
|
||||
0x9d, 0x30, 0x91, 0xfa, 0xef, 0xd2, 0xae, 0xf7, 0x47, 0x6e, 0x9b, 0x47, 0xff, 0x2b, 0xc2, 0x7b,
|
||||
0x5f, 0xfa, 0x02, 0xf5, 0xd6, 0x92, 0x2d, 0x59, 0x2e, 0xd2, 0x00, 0x4d, 0xee, 0xd2, 0x16, 0xb8,
|
||||
0x04, 0x8b, 0x6c, 0x70, 0xcd, 0x7d, 0x3a, 0x4c, 0xbc, 0x93, 0x1f, 0xc8, 0x64, 0x77, 0xb1, 0xde,
|
||||
0xa4, 0x4d, 0xfe, 0xfa, 0xe3, 0xf3, 0x90, 0x9e, 0xb1, 0x3d, 0xe3, 0x8d, 0x9b, 0x7c, 0xd8, 0xf1,
|
||||
0x68, 0x6d, 0x53, 0x12, 0x45, 0x52, 0xe4, 0x43, 0xda, 0xe3, 0xcc, 0xd2, 0xe9, 0xda, 0x98, 0xb5,
|
||||
0x9b, 0x2e, 0xb4, 0x89, 0x34, 0x16, 0x2a, 0xa9, 0x81, 0x1b, 0x16, 0x8a, 0xc3, 0x19, 0xd4, 0xdb,
|
||||
0xd6, 0x46, 0x57, 0xaa, 0xb0, 0xb5, 0xd1, 0x95, 0xda, 0x9b, 0x08, 0x33, 0xe6, 0x81, 0x74, 0xb1,
|
||||
0x52, 0xc9, 0x4c, 0x9c, 0xae, 0x54, 0x31, 0x16, 0xee, 0x46, 0x2d, 0x5c, 0xb6, 0x95, 0x52, 0x0b,
|
||||
0x17, 0xc7, 0x86, 0x6c, 0xd0, 0x96, 0xdc, 0x17, 0xc0, 0x3c, 0xd4, 0xd6, 0x4d, 0xd4, 0x4b, 0x1c,
|
||||
0x29, 0x89, 0xd9, 0xe1, 0x56, 0x1a, 0x98, 0xc2, 0x01, 0xcb, 0x94, 0xe1, 0xab, 0xc9, 0x48, 0xb1,
|
||||
0x4f, 0xb0, 0xf1, 0xe5, 0xa9, 0x84, 0x06, 0xe6, 0xa0, 0x47, 0x20, 0x14, 0x91, 0xa6, 0x47, 0xf6,
|
||||
0x4c, 0xd1, 0xa0, 0x2a, 0x28, 0x26, 0x25, 0xbe, 0x54, 0xeb, 0xc5, 0x79, 0x93, 0xcd, 0x19, 0xc2,
|
||||
0x0a, 0x30, 0xa1, 0x91, 0x55, 0x08, 0x10, 0x1c, 0x89, 0xf8, 0x4b, 0x42, 0x17, 0xb9, 0x21, 0x94,
|
||||
0x10, 0x5a, 0xdd, 0x7b, 0xea, 0x54, 0x44, 0xf1, 0x4f, 0x68, 0x0a, 0xcb, 0x06, 0x97, 0xd7, 0xb2,
|
||||
0x49, 0xc3, 0xbd, 0x6b, 0xa2, 0x35, 0x64, 0x97, 0xf7, 0x39, 0x28, 0x6a, 0x94, 0x5a, 0x71, 0x74,
|
||||
0xe0, 0x10, 0xd6, 0xf4, 0x66, 0xe1, 0x2f, 0x55, 0xd8, 0xb7, 0xf5, 0x7b, 0x03, 0x07, 0x4d, 0xf5,
|
||||
0x09, 0xe8, 0x46, 0x60, 0x67, 0xbc, 0xdd, 0x3b, 0xd0, 0xca, 0x94, 0xfb, 0x86, 0xa6, 0xac, 0x11,
|
||||
0x0e, 0x07, 0xb9, 0xab, 0x02, 0xfd, 0x94, 0x82, 0xb3, 0xd1, 0xd8, 0xd8, 0x6a, 0x78, 0x47, 0x60,
|
||||
0x7e, 0x80, 0x24, 0x61, 0x46, 0xe2, 0xa2, 0xc9, 0x5c, 0xc4, 0x82, 0x5b, 0x4b, 0xa7, 0x2b, 0xf1,
|
||||
0x88, 0x6a, 0xa8, 0x32, 0xc3, 0xdb, 0xec, 0x87, 0xb3, 0x9d, 0xce, 0xbf, 0x30, 0x66, 0xe8, 0xfc,
|
||||
0x0b, 0x72, 0x23, 0x15, 0xd3, 0x1e, 0x95, 0x01, 0x0e, 0xdc, 0x08, 0xca, 0x80, 0x5a, 0x07, 0x3d,
|
||||
0x30, 0x40, 0x1b, 0xc6, 0x04, 0xf5, 0xf1, 0x53, 0x31, 0x66, 0x86, 0xb1, 0x41, 0x0d, 0xbf, 0x30,
|
||||
0x82, 0xf3, 0x2f, 0x8c, 0x19, 0x23, 0x52, 0xc3, 0xfc, 0x0b, 0xe3, 0x86, 0x32, 0xa0, 0x50, 0x6e,
|
||||
0x4c, 0xd7, 0x46, 0x19, 0xe0, 0x94, 0x1b, 0x3a, 0x7f, 0x37, 0x9d, 0xbf, 0x35, 0xfb, 0x81, 0x01,
|
||||
0xc5, 0xc0, 0x0f, 0x95, 0x06, 0xe3, 0x56, 0x67, 0x1c, 0x80, 0x47, 0x8c, 0x25, 0xa5, 0x38, 0x38,
|
||||
0xb2, 0xa3, 0xb6, 0x2e, 0x6d, 0x01, 0x94, 0x01, 0x8e, 0xdc, 0x88, 0x1c, 0x35, 0x83, 0x90, 0xb2,
|
||||
0x09, 0xd6, 0xb0, 0xf5, 0x2c, 0x9d, 0x72, 0xc2, 0x38, 0x30, 0x15, 0x04, 0xfd, 0xde, 0x98, 0x77,
|
||||
0xa5, 0x1c, 0x28, 0x02, 0x3b, 0x33, 0x0e, 0x28, 0x3b, 0xc8, 0x80, 0xca, 0x91, 0x1d, 0xb5, 0x32,
|
||||
0xc0, 0x19, 0x37, 0x6c, 0x34, 0x36, 0x36, 0x32, 0xc0, 0x91, 0x1b, 0x26, 0xe0, 0x8d, 0x9b, 0xb0,
|
||||
0xc3, 0x0d, 0xe2, 0x50, 0x0c, 0xda, 0xa0, 0xca, 0x51, 0x4c, 0x4e, 0x37, 0x1d, 0xe7, 0x6f, 0xba,
|
||||
0xe1, 0x4c, 0x1d, 0x8c, 0x1b, 0xd3, 0x1e, 0x95, 0x01, 0xaa, 0x1c, 0xca, 0x80, 0xba, 0x18, 0x33,
|
||||
0xc0, 0x1a, 0x83, 0x38, 0xe8, 0x9c, 0xdd, 0x98, 0x19, 0xf6, 0xa9, 0x26, 0xac, 0x6c, 0x0b, 0x55,
|
||||
0x07, 0x63, 0x86, 0xaa, 0x83, 0xf1, 0x52, 0xe7, 0x6f, 0xca, 0x61, 0x0c, 0x50, 0xe5, 0x70, 0xd3,
|
||||
0xb5, 0x51, 0x06, 0xa8, 0x72, 0xb8, 0x89, 0x3a, 0x0c, 0xf3, 0xb7, 0xe6, 0xa9, 0x18, 0x1f, 0x16,
|
||||
0x24, 0x25, 0x09, 0x68, 0xba, 0xaf, 0x0b, 0x71, 0xbf, 0xac, 0xe4, 0x0b, 0xf3, 0x5c, 0x94, 0x03,
|
||||
0x35, 0x5d, 0x35, 0x96, 0xa3, 0xfb, 0xba, 0xc5, 0xef, 0x97, 0x76, 0xeb, 0xee, 0xab, 0x1e, 0x57,
|
||||
0xbf, 0xe8, 0x2e, 0xaf, 0x08, 0x20, 0xde, 0x6e, 0x37, 0xb7, 0x6b, 0xe0, 0xcc, 0x09, 0x28, 0x8f,
|
||||
0xad, 0x95, 0x70, 0xfa, 0x59, 0x94, 0x2d, 0xc5, 0x53, 0x46, 0xb0, 0x37, 0x01, 0x16, 0x46, 0x04,
|
||||
0x2d, 0x17, 0x64, 0xf1, 0xa6, 0xc3, 0x19, 0x36, 0x40, 0x20, 0xca, 0x59, 0x76, 0x04, 0xc5, 0xcd,
|
||||
0x83, 0x2c, 0x97, 0x0f, 0x70, 0x6a, 0xbd, 0xc4, 0x1b, 0xe4, 0x5e, 0xaa, 0xc5, 0x0d, 0x96, 0xcd,
|
||||
0x15, 0x9b, 0x25, 0x02, 0x5b, 0x1c, 0x65, 0x97, 0x4a, 0x08, 0x28, 0x02, 0x01, 0x7f, 0x9f, 0xf7,
|
||||
0x56, 0xa4, 0x92, 0xb0, 0x34, 0xcb, 0x6e, 0x20, 0xf7, 0x34, 0x11, 0x71, 0x3c, 0x83, 0x25, 0xd9,
|
||||
0x17, 0x86, 0x66, 0xa8, 0x64, 0x54, 0x99, 0xac, 0xae, 0x75, 0x0f, 0xf0, 0x12, 0x33, 0x47, 0x44,
|
||||
0x98, 0xd2, 0x17, 0x9c, 0x0e, 0xf9, 0xb4, 0xef, 0x10, 0x43, 0x51, 0x43, 0x4c, 0xb9, 0x22, 0x58,
|
||||
0x08, 0xc5, 0xc0, 0x56, 0x1f, 0x11, 0x83, 0x7a, 0x28, 0x48, 0x2b, 0x4e, 0x97, 0xec, 0xaa, 0x21,
|
||||
0x7a, 0xfa, 0x12, 0x0d, 0xf5, 0xb4, 0x16, 0x43, 0x51, 0xc3, 0x41, 0xa9, 0x10, 0xc2, 0x47, 0xf1,
|
||||
0x35, 0x65, 0x5b, 0x4b, 0xe5, 0xb0, 0x13, 0x00, 0x81, 0x89, 0xe2, 0x12, 0x4a, 0x24, 0x6b, 0x26,
|
||||
0x3d, 0x21, 0x04, 0x68, 0x90, 0x29, 0xb0, 0x0b, 0xeb, 0xa4, 0xb8, 0x89, 0x28, 0x45, 0xdd, 0x38,
|
||||
0x09, 0x9d, 0xb3, 0xf8, 0xfb, 0xd6, 0x9d, 0xf6, 0x5e, 0xee, 0x9b, 0x36, 0x8d, 0x52, 0x87, 0x3e,
|
||||
0x4c, 0xc7, 0x8d, 0xa6, 0x23, 0xe7, 0x44, 0x5f, 0x10, 0x7b, 0xa2, 0x67, 0x18, 0x81, 0x98, 0xe9,
|
||||
0x3a, 0x2b, 0x4f, 0x9c, 0xf1, 0x04, 0xa1, 0x3f, 0xf2, 0x1d, 0x49, 0xb4, 0x3a, 0x8a, 0xdf, 0x0f,
|
||||
0xdf, 0x02, 0xae, 0xb3, 0xac, 0x6c, 0x9d, 0x06, 0x66, 0xbb, 0x81, 0xd9, 0x1e, 0x96, 0x00, 0x2e,
|
||||
0x5b, 0x29, 0x5a, 0xca, 0xb4, 0x44, 0x49, 0xd7, 0x01, 0xe8, 0x79, 0x03, 0xb3, 0xa6, 0x2b, 0xe9,
|
||||
0x86, 0x95, 0xac, 0xe0, 0xef, 0x88, 0xed, 0x47, 0xac, 0xa8, 0x51, 0x04, 0x45, 0x41, 0xe5, 0x42,
|
||||
0x1b, 0x5f, 0x9e, 0xda, 0x14, 0xa0, 0x1f, 0x1d, 0x45, 0xa3, 0xcd, 0x50, 0xc7, 0x00, 0x07, 0x0d,
|
||||
0xfe, 0x5b, 0xf2, 0xb5, 0x35, 0xf4, 0x13, 0xf6, 0x25, 0x42, 0x70, 0x38, 0xed, 0x4a, 0x9c, 0x84,
|
||||
0x00, 0x47, 0x42, 0xf6, 0xab, 0x80, 0x34, 0x83, 0xfc, 0xbf, 0x92, 0xe1, 0x56, 0x32, 0x65, 0x38,
|
||||
0x6e, 0x12, 0x4d, 0x67, 0xcf, 0xd0, 0x5d, 0xac, 0x0b, 0xc0, 0x20, 0x31, 0x88, 0x67, 0x29, 0x0a,
|
||||
0x97, 0xc4, 0xc3, 0xa0, 0x3f, 0x2e, 0x7c, 0x81, 0x97, 0x27, 0xb6, 0x32, 0x42, 0x61, 0xda, 0xb3,
|
||||
0x2a, 0x83, 0x74, 0x05, 0x48, 0x04, 0x48, 0x1d, 0x98, 0x67, 0x17, 0x17, 0xc3, 0xc5, 0x12, 0x36,
|
||||
0xc3, 0x45, 0x47, 0x5c, 0x1f, 0x00, 0x1f, 0x6b, 0xc7, 0x32, 0x7f, 0x44, 0x01, 0xf5, 0xbe, 0x3d,
|
||||
0x0c, 0xa4, 0x1c, 0xe6, 0x55, 0x70, 0x3e, 0x49, 0xe7, 0x33, 0xf9, 0xcc, 0x0d, 0x32, 0x30, 0xdc,
|
||||
0x30, 0xc5, 0xbe, 0xc5, 0x84, 0x44, 0x48, 0x03, 0x40, 0xab, 0xad, 0x94, 0xc5, 0x95, 0xb0, 0xbe,
|
||||
0x05, 0x4b, 0x01, 0xf5, 0x89, 0x93, 0x04, 0xe0, 0x51, 0x46, 0x85, 0xb4, 0x8a, 0x10, 0x88, 0x00,
|
||||
0x32, 0x6a, 0x85, 0x6d, 0xd8, 0x92, 0x80, 0xb2, 0x44, 0xa0, 0x01, 0x4f, 0x35, 0x7a, 0x3b, 0x57,
|
||||
0xe8, 0xb9, 0x0e, 0xf9, 0x98, 0x3a, 0x26, 0x24, 0xb4, 0x2a, 0xd9, 0x74, 0x81, 0xc7, 0x78, 0xf4,
|
||||
0x65, 0x7d, 0x58, 0x9f, 0x43, 0x7b, 0x85, 0xc9, 0xe8, 0xef, 0x36, 0xb7, 0x2b, 0x2c, 0x46, 0x23,
|
||||
0x84, 0x91, 0xa1, 0x39, 0xd8, 0x8c, 0x32, 0xd2, 0x66, 0x20, 0xdc, 0xf3, 0x4c, 0x2c, 0x89, 0x6c,
|
||||
0x84, 0x28, 0xde, 0x25, 0xc2, 0x91, 0x9d, 0xe8, 0x64, 0x4b, 0xef, 0x15, 0x09, 0xaa, 0xd6, 0x87,
|
||||
0xa1, 0xed, 0xac, 0x0d, 0x97, 0x06, 0xf4, 0x6a, 0xee, 0xf6, 0xc2, 0x62, 0xef, 0x99, 0x7e, 0x42,
|
||||
0xc2, 0xa2, 0x01, 0x7a, 0x22, 0x44, 0x93, 0x07, 0x16, 0xd1, 0xca, 0x2e, 0x25, 0x42, 0xd3, 0xd0,
|
||||
0xfb, 0x2e, 0xd1, 0x5b, 0x3c, 0x03, 0x5d, 0xe1, 0x23, 0xf0, 0x89, 0x28, 0xf7, 0xb4, 0x3b, 0xd1,
|
||||
0x4a, 0xe6, 0xb6, 0x5a, 0x0c, 0x0c, 0x6c, 0x90, 0x6b, 0x24, 0xe8, 0xd3, 0x63, 0xec, 0x24, 0xf2,
|
||||
0x69, 0x84, 0x8c, 0x7e, 0xb6, 0xdc, 0x70, 0x3c, 0x1a, 0xc0, 0x49, 0x42, 0x43, 0x15, 0x6f, 0x7d,
|
||||
0xdd, 0x7b, 0xd8, 0x9e, 0xa0, 0x08, 0x5b, 0x20, 0x38, 0xc7, 0x8c, 0x57, 0x5d, 0xe5, 0x1d, 0x28,
|
||||
0x63, 0x3a, 0xe8, 0x28, 0x42, 0x6d, 0x03, 0x4e, 0x86, 0x9a, 0xd0, 0x61, 0x57, 0xd3, 0xc9, 0xce,
|
||||
0xc4, 0xf4, 0xb0, 0xb2, 0x44, 0x84, 0x61, 0x6b, 0x84, 0x59, 0x98, 0x0a, 0x5b, 0xbd, 0x87, 0x85,
|
||||
0x0d, 0xe2, 0x9a, 0x40, 0x6d, 0xf5, 0x94, 0xd3, 0x53, 0x3b, 0xa5, 0xe7, 0x8c, 0x9e, 0xb3, 0xde,
|
||||
0x9c, 0xf6, 0xd6, 0xb5, 0x1a, 0xc4, 0x89, 0x93, 0x4d, 0x19, 0x09, 0x35, 0x22, 0xfb, 0x0a, 0xbe,
|
||||
0x19, 0x86, 0xa8, 0xce, 0x75, 0xee, 0x15, 0xe6, 0x02, 0x2b, 0xab, 0x4a, 0xe7, 0xe5, 0x74, 0x5e,
|
||||
0xbb, 0x81, 0x1f, 0xce, 0xf8, 0xe1, 0x94, 0x5d, 0xce, 0xd8, 0xd5, 0x81, 0xd5, 0x40, 0xae, 0x00,
|
||||
0xd4, 0x51, 0xbf, 0xa1, 0x4a, 0xf0, 0xec, 0xc8, 0x63, 0xa7, 0x3c, 0x96, 0x35, 0xaf, 0x3d, 0xe2,
|
||||
0x70, 0x4f, 0x94, 0x21, 0x49, 0x80, 0x22, 0xa6, 0xa4, 0x22, 0x44, 0x22, 0x6a, 0xc7, 0xef, 0xba,
|
||||
0x4b, 0x02, 0x13, 0x47, 0x08, 0x82, 0xe4, 0x4d, 0x86, 0xdf, 0x2f, 0x43, 0xa7, 0xea, 0x41, 0x40,
|
||||
0x48, 0x41, 0x04, 0x04, 0x06, 0xa7, 0x06, 0x72, 0x19, 0x20, 0x43, 0x09, 0x48, 0x40, 0x8c, 0x58,
|
||||
0x89, 0x88, 0x38, 0x47, 0xcc, 0xc9, 0x59, 0xa5, 0xc1, 0x97, 0x5c, 0x02, 0x53, 0x81, 0x05, 0xc3,
|
||||
0xce, 0xd9, 0x20, 0xa2, 0x13, 0x6e, 0x49, 0x20, 0xb5, 0xf3, 0xb0, 0xbf, 0x4c, 0x01, 0xea, 0x31,
|
||||
0xc0, 0x50, 0x56, 0x1e, 0x38, 0xb3, 0x08, 0x0a, 0x44, 0xac, 0xcc, 0x25, 0x28, 0xd5, 0x04, 0x63,
|
||||
0xe1, 0x6f, 0x0a, 0x91, 0x86, 0x1e, 0x74, 0x82, 0x77, 0x8d, 0x90, 0x71, 0x47, 0xc0, 0x50, 0xe4,
|
||||
0x5d, 0x54, 0xab, 0x84, 0x58, 0x0f, 0x6d, 0x67, 0x6d, 0x84, 0x37, 0x89, 0x3d, 0x57, 0xe8, 0xab,
|
||||
0x04, 0xb3, 0x18, 0x57, 0x67, 0xa5, 0xe3, 0x06, 0x3a, 0xd6, 0xbd, 0xd3, 0xee, 0x9d, 0x8e, 0xaa,
|
||||
0xb6, 0x63, 0xea, 0xa2, 0x39, 0xa6, 0xc0, 0x6b, 0x45, 0x12, 0xc4, 0xa9, 0xe4, 0xe2, 0x47, 0x4e,
|
||||
0xc9, 0xd9, 0x94, 0x8c, 0x0f, 0xce, 0xf8, 0xe0, 0x94, 0x4d, 0xc0, 0x08, 0xc8, 0xa6, 0x4e, 0xba,
|
||||
0x0a, 0x88, 0xdd, 0xcb, 0x0c, 0x22, 0x08, 0xaa, 0xc9, 0x4e, 0xe3, 0xad, 0x36, 0xd6, 0x20, 0x8d,
|
||||
0xb7, 0xd7, 0x7d, 0xbf, 0x26, 0x5f, 0x50, 0xab, 0x7b, 0x1a, 0x2b, 0xb1, 0x74, 0xbb, 0x90, 0x45,
|
||||
0x6e, 0x5a, 0xa0, 0xb9, 0x38, 0x42, 0x2e, 0x81, 0x15, 0xf3, 0xd3, 0xe9, 0x77, 0xb8, 0x76, 0x25,
|
||||
0xf6, 0x80, 0x2a, 0x91, 0x95, 0x2d, 0xd1, 0x63, 0xfd, 0xd4, 0x73, 0xd6, 0xe0, 0x79, 0xd9, 0xac,
|
||||
0x77, 0x46, 0x0c, 0xf8, 0xf7, 0x84, 0xf8, 0xf0, 0xff, 0xae, 0x38, 0xf4, 0x72, 0x8a, 0x06, 0xac,
|
||||
0x10, 0x9b, 0x6e, 0x74, 0x9d, 0x1b, 0xc6, 0xe1, 0xf4, 0x9a, 0xdd, 0xb4, 0x93, 0x3c, 0xef, 0xb4,
|
||||
0x1b, 0x91, 0x68, 0x4e, 0x92, 0x10, 0x16, 0x88, 0x88, 0x63, 0xa2, 0x5e, 0xf4, 0x10, 0x6b, 0x56,
|
||||
0xb7, 0xcc, 0x2a, 0x40, 0x50, 0xd8, 0xe8, 0x7d, 0xa5, 0x52, 0xc4, 0xac, 0xb7, 0x9e, 0x29, 0x70,
|
||||
0xa6, 0xdd, 0x4d, 0x7b, 0xcd, 0x6e, 0xde, 0xbb, 0x12, 0x3e, 0x90, 0x17, 0x17, 0x64, 0x44, 0xdf,
|
||||
0x2f, 0xd2, 0xf7, 0x4a, 0xbf, 0x1b, 0xcf, 0xfc, 0xd4, 0x3a, 0x54, 0xbb, 0x19, 0x57, 0xf3, 0x0a,
|
||||
0x11, 0xd9, 0xdc, 0xde, 0x5e, 0xff, 0xc5, 0x22, 0xe1, 0xa2, 0x7b, 0x77, 0xdb, 0xed, 0xb6, 0x2b,
|
||||
0xc4, 0xc5, 0xb0, 0x73, 0xa6, 0x3c, 0x44, 0x65, 0xba, 0x11, 0x86, 0x8e, 0x0c, 0xc7, 0x14, 0x3c,
|
||||
0x77, 0x06, 0x9e, 0x0f, 0xed, 0xde, 0x00, 0x79, 0x47, 0x40, 0x3e, 0xda, 0x69, 0x94, 0x17, 0xe8,
|
||||
0xe5, 0xc3, 0x51, 0xc9, 0x1c, 0xda, 0x03, 0x70, 0x6f, 0xb7, 0x29, 0x95, 0xf6, 0xa8, 0x97, 0xe9,
|
||||
0x6d, 0xa1, 0x98, 0x92, 0x0f, 0xc5, 0x70, 0xdf, 0x24, 0x1d, 0x90, 0xf7, 0xe9, 0x80, 0xfd, 0x65,
|
||||
0x93, 0x49, 0x84, 0x7d, 0x06, 0xc0, 0x6e, 0x1b, 0xe6, 0x30, 0xeb, 0xe5, 0xcb, 0xc4, 0xe5, 0xb6,
|
||||
0x4c, 0x52, 0xa9, 0x89, 0xa3, 0x62, 0x9c, 0x51, 0x2a, 0xf8, 0xd9, 0xdb, 0xff, 0x8a, 0xd1, 0xff,
|
||||
0xf4, 0x53, 0x1c, 0x00, 0xbb, 0x59, 0xdb, 0x6e, 0x7c, 0xa1, 0x92, 0xe8, 0x97, 0x32, 0x54, 0xa7,
|
||||
0xc2, 0x29, 0x2f, 0xc3, 0x22, 0x7a, 0xea, 0x09, 0x01, 0xee, 0x82, 0xf8, 0x87, 0xc2, 0x2b, 0x7c,
|
||||
0x52, 0xac, 0xa2, 0xa9, 0xf7, 0x48, 0xf1, 0xf2, 0xa0, 0x11, 0xa5, 0x3b, 0xa8, 0xbe, 0x09, 0xde,
|
||||
0x5c, 0x6b, 0xc4, 0xb8, 0x22, 0xcf, 0x03, 0xc7, 0x34, 0x21, 0x5f, 0x98, 0xde, 0xc6, 0xc0, 0x0c,
|
||||
0x52, 0x37, 0xd4, 0x05, 0xdc, 0x5b, 0x72, 0x30, 0x2f, 0x30, 0x7a, 0x5b, 0xd8, 0xed, 0x3b, 0x23,
|
||||
0xe7, 0x8c, 0x7c, 0x77, 0x18, 0x49, 0x3c, 0x3d, 0x92, 0xa2, 0xe6, 0x28, 0xf9, 0x59, 0x50, 0xc9,
|
||||
0x1a, 0xea, 0x55, 0x82, 0x11, 0x87, 0x32, 0xb2, 0xd1, 0x17, 0x63, 0x2d, 0xb3, 0x53, 0x45, 0x43,
|
||||
0xd6, 0x14, 0x63, 0xde, 0xc8, 0x3e, 0x60, 0x56, 0x48, 0xbe, 0xb0, 0xab, 0xa0, 0xda, 0xe6, 0xff,
|
||||
0x81, 0x72, 0xb1, 0xe4, 0x7d, 0xbd, 0x76, 0x65, 0xf8, 0xec, 0x88, 0x07, 0x4c, 0xbb, 0x8a, 0x89,
|
||||
0x7a, 0x2d, 0xe5, 0xa6, 0xf6, 0xea, 0x55, 0x4c, 0xf5, 0x6b, 0xa6, 0x00, 0x75, 0x31, 0xd3, 0xaf,
|
||||
0x99, 0x02, 0x25, 0x37, 0xd5, 0xaf, 0x99, 0xfa, 0xe5, 0x99, 0x5a, 0x86, 0x99, 0xfa, 0x06, 0x37,
|
||||
0xd5, 0xaf, 0x99, 0xf2, 0x07, 0x37, 0xd3, 0xaf, 0x99, 0x02, 0xe5, 0x62, 0xaa, 0x5f, 0xc5, 0xbd,
|
||||
0xfa, 0x35, 0xd3, 0x91, 0x13, 0xca, 0xd4, 0x1f, 0xab, 0x8d, 0xe9, 0xd7, 0x54, 0x37, 0x4f, 0x64,
|
||||
0x7b, 0xfb, 0x13, 0xfa, 0xba, 0xac, 0x5f, 0x4d, 0x4e, 0x94, 0xae, 0x41, 0xbf, 0xa6, 0x42, 0x34,
|
||||
0xd2, 0xb0, 0xf1, 0xd6, 0x96, 0xf7, 0xdb, 0xe3, 0x68, 0x73, 0x6d, 0x8a, 0xd3, 0x1a, 0x36, 0x57,
|
||||
0xb0, 0x41, 0x45, 0x66, 0xa5, 0x37, 0x6e, 0x54, 0x0e, 0x74, 0x28, 0x4b, 0x18, 0x97, 0xd4, 0xa8,
|
||||
0x86, 0x0d, 0x0a, 0x56, 0xcc, 0x35, 0x6c, 0x34, 0x94, 0x78, 0x7a, 0x28, 0x6e, 0xa4, 0x62, 0x6e,
|
||||
0xac, 0x62, 0x6e, 0xa2, 0x62, 0xf3, 0x7d, 0xec, 0xa0, 0x61, 0x63, 0xde, 0x0c, 0xfa, 0xe5, 0xbe,
|
||||
0x49, 0xbf, 0x6e, 0xaf, 0xbb, 0xf7, 0xdb, 0x35, 0x48, 0x48, 0x42, 0x1c, 0xe8, 0xb4, 0xb8, 0xb1,
|
||||
0x68, 0x44, 0xc6, 0x58, 0x10, 0x81, 0x64, 0x40, 0x6c, 0x98, 0x65, 0xf4, 0xcc, 0x10, 0xe5, 0xfe,
|
||||
0xd0, 0x74, 0xf6, 0x2f, 0x1c, 0x9d, 0xfe, 0xdb, 0x0d, 0xff, 0x3e, 0x34, 0x87, 0x2b, 0xbe, 0x58,
|
||||
0x17, 0x2c, 0x09, 0x9a, 0x14, 0xa5, 0x1c, 0x15, 0xde, 0xf4, 0xf3, 0x72, 0x9b, 0x7b, 0x6a, 0x50,
|
||||
0x8e, 0x6a, 0x97, 0xf8, 0x71, 0x4a, 0x02, 0xb1, 0xaa, 0x48, 0x46, 0xb1, 0xe6, 0x23, 0xb1, 0xdc,
|
||||
0xae, 0x64, 0xf8, 0xc9, 0xda, 0x09, 0xc4, 0x58, 0xf4, 0x25, 0x92, 0xfa, 0x12, 0x08, 0x3a, 0x25,
|
||||
0x90, 0xd2, 0x4f, 0x89, 0x02, 0x62, 0xd4, 0xc8, 0x33, 0x48, 0x0c, 0xd6, 0x46, 0x3a, 0x60, 0xe2,
|
||||
0x79, 0x36, 0x99, 0x91, 0x54, 0x40, 0x0a, 0xa1, 0xce, 0x14, 0x11, 0x10, 0xaa, 0x82, 0x96, 0xfc,
|
||||
0x05, 0x14, 0x25, 0xc2, 0xc2, 0x0c, 0xed, 0xe6, 0x2c, 0x03, 0x62, 0x42, 0xe6, 0xa7, 0x45, 0x4d,
|
||||
0x05, 0xca, 0x4d, 0x00, 0x72, 0xc4, 0x94, 0x5c, 0x15, 0xb5, 0x44, 0xa4, 0x21, 0x0c, 0x35, 0x1c,
|
||||
0xc4, 0xb1, 0xf6, 0xe2, 0xf8, 0xd4, 0x2c, 0xd3, 0xac, 0x44, 0x62, 0x44, 0x52, 0x5b, 0xad, 0x02,
|
||||
0xd5, 0x54, 0x4a, 0xc3, 0xa8, 0xbb, 0x00, 0xde, 0xe1, 0x91, 0xbe, 0x13, 0xbb, 0x90, 0x25, 0x38,
|
||||
0xac, 0x82, 0xd6, 0xe2, 0xa0, 0x12, 0x25, 0x53, 0xca, 0x32, 0xca, 0x01, 0x99, 0xc6, 0x10, 0xd7,
|
||||
0xdb, 0xe3, 0x3b, 0x12, 0x05, 0x32, 0x01, 0x94, 0x33, 0xe0, 0xfb, 0x97, 0xa7, 0xa1, 0x46, 0x91,
|
||||
0x08, 0x02, 0x77, 0xe9, 0x2f, 0x22, 0xc8, 0x8e, 0x38, 0x5a, 0x56, 0xbf, 0xd5, 0x34, 0x6b, 0x5d,
|
||||
0x88, 0x2a, 0x87, 0x1c, 0x50, 0x2e, 0x12, 0x2a, 0xa6, 0x6e, 0x91, 0xf4, 0xa8, 0x25, 0xbc, 0x20,
|
||||
0xe2, 0x15, 0x10, 0xfb, 0x95, 0x1a, 0x41, 0xc8, 0x8a, 0x47, 0x66, 0x3d, 0x10, 0xeb, 0x35, 0xc0,
|
||||
0x19, 0xab, 0xc8, 0xac, 0x28, 0x22, 0x3f, 0x00, 0x14, 0x08, 0x68, 0xe0, 0xe1, 0x83, 0x19, 0x09,
|
||||
0xe1, 0x83, 0xc4, 0x3a, 0x15, 0xbd, 0xd9, 0x58, 0x83, 0xa5, 0x45, 0xd5, 0x10, 0xc0, 0x6e, 0x4a,
|
||||
0x62, 0x6f, 0x29, 0x31, 0x72, 0x14, 0x76, 0x34, 0x48, 0x1e, 0xd6, 0x5a, 0x08, 0xaa, 0x68, 0x9a,
|
||||
0x0c, 0xbd, 0xc1, 0x4a, 0x56, 0x34, 0xe9, 0x67, 0x29, 0x07, 0x45, 0x42, 0xa4, 0x03, 0x59, 0x15,
|
||||
0x98, 0x4a, 0xc4, 0x0c, 0x69, 0x38, 0x20, 0xa9, 0x04, 0xfc, 0xa7, 0x84, 0x7a, 0x22, 0xeb, 0x83,
|
||||
0xec, 0x10, 0x1b, 0xcb, 0xf5, 0x7c, 0x48, 0x65, 0xa3, 0xde, 0xb4, 0x56, 0x63, 0x22, 0x73, 0x8d,
|
||||
0x09, 0x4c, 0x89, 0x80, 0x08, 0xa5, 0xd9, 0x4a, 0xc8, 0x2a, 0x43, 0x44, 0xa4, 0x9e, 0x03, 0x95,
|
||||
0xb6, 0x4a, 0x9a, 0xe5, 0x96, 0xd9, 0x10, 0x80, 0xcf, 0x84, 0xa1, 0x83, 0xc7, 0xad, 0x59, 0x17,
|
||||
0x2b, 0x33, 0xbe, 0x64, 0xc4, 0x1c, 0xd4, 0x35, 0x2d, 0x59, 0x76, 0x1a, 0x10, 0x02, 0x23, 0x94,
|
||||
0x67, 0x22, 0x04, 0x85, 0xb9, 0x59, 0xa2, 0x43, 0x40, 0x6b, 0x72, 0x0e, 0x55, 0xa1, 0x05, 0xea,
|
||||
0x42, 0x50, 0x34, 0x5a, 0xb2, 0x36, 0x18, 0x89, 0x31, 0x44, 0xd5, 0xb2, 0x46, 0x28, 0x0a, 0x40,
|
||||
0x08, 0xa6, 0x25, 0xc1, 0x2c, 0xf7, 0x42, 0xe0, 0x9c, 0x6c, 0x3f, 0x03, 0x12, 0xc4, 0x8a, 0x97,
|
||||
0x92, 0xc1, 0x1b, 0x10, 0xb2, 0x44, 0x59, 0x06, 0x7e, 0x26, 0x26, 0x0e, 0x40, 0x98, 0xc3, 0x90,
|
||||
0x5b, 0x08, 0x56, 0x59, 0xa1, 0x2e, 0x86, 0x20, 0x13, 0xb2, 0x37, 0xd0, 0x22, 0xa6, 0x0d, 0x91,
|
||||
0x8c, 0x47, 0xb0, 0x85, 0x81, 0x66, 0x8f, 0xb0, 0x10, 0x80, 0x31, 0xf0, 0x7a, 0x56, 0x30, 0x30,
|
||||
0xc9, 0xcb, 0x59, 0xb6, 0x67, 0x4d, 0x84, 0x57, 0x0f, 0x48, 0x08, 0xc9, 0xc4, 0x36, 0x58, 0x43,
|
||||
0xa6, 0xa4, 0x38, 0x65, 0x65, 0x88, 0xba, 0x16, 0xdb, 0x96, 0x09, 0x7a, 0x87, 0xf0, 0xbb, 0x44,
|
||||
0x88, 0x8f, 0x42, 0x8e, 0x9a, 0xa0, 0xba, 0x57, 0x80, 0xa8, 0x25, 0x93, 0x6b, 0x44, 0xe2, 0x0d,
|
||||
0x52, 0x93, 0xb4, 0x1f, 0xc4, 0x4e, 0x6a, 0x24, 0x79, 0x1a, 0x4d, 0x4d, 0x06, 0x56, 0x40, 0xf9,
|
||||
0xa8, 0x1a, 0x29, 0x8a, 0x48, 0x4c, 0x2c, 0x31, 0x1c, 0xaf, 0x29, 0xef, 0x4e, 0x26, 0x0e, 0x68,
|
||||
0x5e, 0x84, 0x94, 0x29, 0x2d, 0x19, 0x9a, 0xc8, 0x3a, 0xb8, 0x83, 0x12, 0xdb, 0xc0, 0x14, 0xa2,
|
||||
0x1d, 0x50, 0x17, 0x26, 0x01, 0x2d, 0x30, 0xc2, 0x6a, 0xb8, 0x24, 0xe8, 0x1d, 0x2c, 0x93, 0xae,
|
||||
0x59, 0x3c, 0x85, 0x04, 0x29, 0x76, 0x0d, 0xa4, 0xbf, 0x59, 0xd6, 0xab, 0xdd, 0x06, 0x1b, 0x85,
|
||||
0x67, 0x4a, 0x44, 0x4b, 0x2a, 0x1a, 0x31, 0x21, 0x32, 0xfa, 0x94, 0x13, 0xe6, 0xc2, 0x2c, 0x40,
|
||||
0x83, 0x2c, 0x00, 0x26, 0x56, 0x21, 0x2d, 0x9e, 0x45, 0x41, 0xf8, 0x09, 0xe1, 0x40, 0x71, 0x16,
|
||||
0x70, 0x12, 0x14, 0xc4, 0xc9, 0x90, 0x2d, 0x85, 0x53, 0x62, 0x87, 0x89, 0xc8, 0x98, 0x66, 0x26,
|
||||
0x4c, 0x9b, 0x86, 0x99, 0xc7, 0x8a, 0xe5, 0x54, 0x0d, 0xba, 0xd5, 0x96, 0x42, 0x98, 0xa5, 0x53,
|
||||
0x05, 0x11, 0x45, 0x6a, 0xb8, 0x2b, 0xd5, 0xac, 0x52, 0x68, 0x4a, 0xa2, 0x98, 0xc8, 0xa6, 0xc9,
|
||||
0x10, 0x5b, 0x97, 0x09, 0xf6, 0xd4, 0x80, 0xa4, 0xf7, 0x4b, 0x3e, 0x88, 0x40, 0x9d, 0x88, 0x50,
|
||||
0x57, 0x0d, 0xfd, 0x1d, 0xd1, 0xd3, 0x04, 0x6b, 0x19, 0x35, 0xdb, 0x61, 0xc2, 0xa4, 0xc2, 0xc5,
|
||||
0xfc, 0x9f, 0x08, 0x7a, 0x1d, 0x81, 0x92, 0x03, 0x66, 0x41, 0xe5, 0x47, 0x5b, 0x42, 0x34, 0x07,
|
||||
0x09, 0x55, 0x81, 0xad, 0x34, 0x62, 0x07, 0x1a, 0x85, 0x2a, 0x57, 0x59, 0x5f, 0xe4, 0xce, 0x25,
|
||||
0x36, 0x03, 0xc4, 0xa2, 0x32, 0x1f, 0x34, 0x2f, 0x9a, 0x6a, 0x9a, 0x87, 0xd8, 0x70, 0xb5, 0x80,
|
||||
0xe3, 0x42, 0x38, 0x80, 0x07, 0x00, 0x4c, 0x51, 0x55, 0xaa, 0xf6, 0xaa, 0x25, 0x8a, 0x82, 0xb1,
|
||||
0xc2, 0x10, 0x25, 0xd8, 0xaa, 0xc4, 0x7a, 0xf7, 0x00, 0xca, 0xb2, 0x3e, 0xd6, 0x34, 0x9d, 0xfd,
|
||||
0xa4, 0xc5, 0xb6, 0x5a, 0x62, 0xf8, 0xd4, 0xc7, 0x96, 0xc1, 0xac, 0xd8, 0x7a, 0x58, 0xd6, 0x4e,
|
||||
0x22, 0x7f, 0xd4, 0x89, 0xc1, 0x02, 0xa9, 0xc9, 0xca, 0x35, 0x41, 0x54, 0xfa, 0x80, 0x65, 0xa3,
|
||||
0xa8, 0x04, 0x4c, 0x62, 0xc5, 0x44, 0x63, 0x90, 0xc9, 0x47, 0xcd, 0xe5, 0x00, 0x53, 0x07, 0xfe,
|
||||
0xe1, 0x3d, 0xc1, 0xbd, 0x98, 0x68, 0xcb, 0xbd, 0x30, 0x25, 0x8a, 0xd8, 0xc4, 0x96, 0x75, 0x67,
|
||||
0x95, 0x22, 0x37, 0x60, 0x5f, 0x22, 0x90, 0x95, 0x65, 0xfe, 0xa8, 0x15, 0xaf, 0x08, 0xb5, 0xa7,
|
||||
0x06, 0xd9, 0xe2, 0x80, 0xe2, 0x2b, 0x97, 0x85, 0x05, 0xb2, 0xdd, 0x74, 0x32, 0x89, 0x46, 0x5c,
|
||||
0x3e, 0x2c, 0x52, 0x04, 0x93, 0xe8, 0x88, 0x78, 0xee, 0x8b, 0xb8, 0xc3, 0x2b, 0x01, 0xd4, 0x0e,
|
||||
0xc8, 0x78, 0xf0, 0x48, 0x00, 0x4b, 0xe3, 0xa1, 0x19, 0x4c, 0x81, 0x57, 0xaa, 0x0a, 0x18, 0x06,
|
||||
0x76, 0x6a, 0x61, 0x72, 0xcb, 0x8a, 0x22, 0x40, 0xca, 0x99, 0x85, 0x40, 0xa8, 0x64, 0xac, 0xc0,
|
||||
0x64, 0x14, 0x0b, 0xb7, 0x9c, 0x1d, 0x00, 0x7d, 0xcc, 0xb6, 0xf2, 0xf1, 0xcb, 0xd3, 0xa6, 0x61,
|
||||
0x71, 0x95, 0x8b, 0x98, 0x21, 0x80, 0x21, 0xc0, 0xaf, 0x62, 0x14, 0xc5, 0x1f, 0x6f, 0xa1, 0x03,
|
||||
0xc0, 0x14, 0x29, 0x13, 0x91, 0xdb, 0x1f, 0x30, 0x65, 0x28, 0xda, 0xd0, 0x2e, 0xc9, 0x5f, 0x64,
|
||||
0x8d, 0x64, 0x7d, 0x3d, 0xf4, 0xbb, 0x82, 0xbd, 0x42, 0x61, 0x01, 0x0b, 0x01, 0x09, 0x61, 0x37,
|
||||
0x70, 0x1d, 0x2a, 0xae, 0xa9, 0xe8, 0x32, 0xac, 0xb9, 0x4c, 0x26, 0xf9, 0x0c, 0xc4, 0x17, 0x69,
|
||||
0xe6, 0xc8, 0x2c, 0x8d, 0x30, 0x06, 0x1d, 0xf0, 0x13, 0x95, 0x4a, 0xb8, 0x5a, 0xb4, 0x42, 0x77,
|
||||
0x15, 0x5c, 0xc0, 0xab, 0xb9, 0x13, 0xb4, 0x40, 0xf7, 0x51, 0xec, 0x1f, 0x87, 0x5e, 0xa4, 0xcf,
|
||||
0x46, 0x48, 0x1d, 0x5a, 0xa8, 0xdd, 0x2a, 0x15, 0xf6, 0x44, 0x94, 0x6e, 0x02, 0x96, 0x98, 0x2f,
|
||||
0xd6, 0xc1, 0x37, 0xa3, 0xc9, 0x04, 0xe6, 0xd3, 0x3d, 0x76, 0x33, 0xe8, 0xa7, 0x58, 0x83, 0x88,
|
||||
0x82, 0x2a, 0x20, 0xf9, 0xa8, 0x2f, 0x92, 0x1d, 0x23, 0xc3, 0x51, 0x2f, 0x03, 0xf5, 0x3b, 0x12,
|
||||
0xd7, 0xe4, 0xc6, 0x2a, 0x3e, 0x4a, 0x8d, 0x4d, 0x5a, 0x76, 0x77, 0x85, 0xe1, 0xf4, 0x9a, 0xfd,
|
||||
0x3d, 0x6b, 0xd0, 0xea, 0xcf, 0x57, 0xdd, 0x57, 0xbc, 0x3a, 0xb9, 0x92, 0xef, 0xdd, 0xd2, 0xf0,
|
||||
0x56, 0x42, 0x09, 0x9a, 0xe4, 0x1a, 0xcc, 0x6c, 0xcd, 0xe5, 0xfe, 0x0a, 0x22, 0x25, 0xab, 0x9f,
|
||||
0x2b, 0x4a, 0xb3, 0x1c, 0x50, 0x5e, 0x87, 0xe4, 0x55, 0x09, 0xe4, 0x94, 0x1b, 0x51, 0x8b, 0xc7,
|
||||
0x0c, 0x4a, 0x14, 0x75, 0x31, 0x37, 0x20, 0x02, 0x2b, 0x52, 0x24, 0xba, 0xc3, 0x1c, 0xbc, 0x6c,
|
||||
0x45, 0x7a, 0x10, 0xbf, 0x29, 0x97, 0x9a, 0x33, 0x61, 0xaa, 0x89, 0xc8, 0x0b, 0x08, 0x69, 0x0b,
|
||||
0x7a, 0x1b, 0xb8, 0x9d, 0xc2, 0x0c, 0x0b, 0x4f, 0x60, 0x56, 0x90, 0x31, 0x4f, 0x70, 0x11, 0x81,
|
||||
0x3d, 0x66, 0x56, 0x70, 0x26, 0xcd, 0x44, 0x59, 0x38, 0x7a, 0xa6, 0x4f, 0x9d, 0xa0, 0x72, 0x0d,
|
||||
0x46, 0x2e, 0xb1, 0x96, 0x24, 0xb6, 0x8d, 0x5e, 0x90, 0x9d, 0x5e, 0x3d, 0x5c, 0x81, 0xab, 0xf5,
|
||||
0x7c, 0x61, 0x57, 0x83, 0x38, 0x86, 0xd0, 0x00, 0x16, 0xe6, 0x93, 0x27, 0xf4, 0x0c, 0x30, 0x84,
|
||||
0x84, 0x87, 0x5c, 0x98, 0x8c, 0xd9, 0xb7, 0x7d, 0xab, 0x11, 0x1d, 0x0a, 0x30, 0x83, 0x96, 0x95,
|
||||
0x61, 0x73, 0xa4, 0xcf, 0xa6, 0x35, 0xa9, 0x15, 0x6c, 0xb9, 0x6f, 0xc9, 0xb2, 0x28, 0x53, 0xd5,
|
||||
0xc3, 0x0a, 0x26, 0x4f, 0x40, 0xc3, 0x53, 0xc1, 0x44, 0xdc, 0x8d, 0xb3, 0xa6, 0xdf, 0x09, 0x90,
|
||||
0xed, 0x46, 0x59, 0xd4, 0x25, 0xb0, 0x2c, 0xee, 0xc1, 0xb2, 0xb9, 0x1f, 0x64, 0x02, 0x15, 0x34,
|
||||
0x06, 0x4f, 0x2c, 0x5e, 0x16, 0x4b, 0xca, 0x74, 0x29, 0x6b, 0xfc, 0xe0, 0x1d, 0x83, 0xf3, 0x15,
|
||||
0xac, 0x89, 0x2d, 0x13, 0x22, 0x53, 0x5d, 0x19, 0x72, 0x1e, 0xce, 0x46, 0x85, 0x80, 0x80, 0xac,
|
||||
0x87, 0x6c, 0x90, 0xf5, 0x4e, 0x59, 0x8d, 0x42, 0x5d, 0xcd, 0x83, 0x0d, 0x6d, 0x65, 0x3d, 0x01,
|
||||
0xbc, 0xa8, 0x1d, 0x18, 0xeb, 0x0b, 0x63, 0x7d, 0x41, 0xd6, 0x07, 0xbf, 0x53, 0x96, 0x3b, 0xe3,
|
||||
0xbc, 0xf8, 0x8f, 0x23, 0x80, 0xb2, 0x1d, 0x03, 0x94, 0xed, 0x32, 0x40, 0xd9, 0x8d, 0x71, 0xcf,
|
||||
0xf1, 0xaa, 0x8d, 0x00, 0xc4, 0x31, 0x0b, 0xbf, 0x0e, 0xc9, 0xee, 0xc6, 0xd9, 0xf0, 0x15, 0xe8,
|
||||
0x6a, 0x6f, 0xed, 0x76, 0xf0, 0x1e, 0x95, 0x0a, 0x55, 0xd0, 0xf4, 0xb1, 0x83, 0x1e, 0x3a, 0xd5,
|
||||
0x43, 0xee, 0x73, 0x2d, 0x4a, 0x09, 0xa8, 0x87, 0xc1, 0x99, 0x22, 0x3a, 0x55, 0x44, 0x3e, 0x57,
|
||||
0xc4, 0x4a, 0xf3, 0x46, 0xb7, 0x5a, 0x6a, 0x62, 0xed, 0x54, 0xf7, 0x9c, 0x6a, 0x62, 0x18, 0x9a,
|
||||
0x83, 0x2a, 0x3a, 0x55, 0xc5, 0x42, 0x35, 0xd1, 0x99, 0x26, 0x16, 0xa6, 0x89, 0xd4, 0xad, 0xac,
|
||||
0xcb, 0xed, 0xa1, 0x5b, 0x78, 0x36, 0x4a, 0x97, 0x5b, 0x57, 0x98, 0xeb, 0xbd, 0x26, 0x36, 0xe5,
|
||||
0xfb, 0x73, 0x3e, 0x6c, 0x6e, 0xdf, 0xaf, 0x47, 0x7e, 0x14, 0xdc, 0xb0, 0xb2, 0xfd, 0x75, 0x05,
|
||||
0xcc, 0x75, 0x3f, 0x2b, 0x5a, 0xae, 0x67, 0xc0, 0x50, 0x42, 0xe9, 0x3e, 0x69, 0x55, 0x03, 0x22,
|
||||
0x34, 0x05, 0x2f, 0xf7, 0x17, 0xf6, 0x4a, 0xa0, 0x9a, 0x23, 0x42, 0x43, 0xbb, 0xdb, 0x0f, 0xc9,
|
||||
0xdd, 0x0f, 0xe1, 0xf6, 0xab, 0x20, 0xdc, 0xda, 0x4d, 0x41, 0x9e, 0xfd, 0xe9, 0x01, 0xf0, 0x72,
|
||||
0x5a, 0x93, 0x3d, 0x23, 0x32, 0xc7, 0xb3, 0xbe, 0x1c, 0xb8, 0x06, 0x11, 0xfc, 0x76, 0xd8, 0x74,
|
||||
0x11, 0xa5, 0x5d, 0x04, 0x69, 0x4f, 0x45, 0x52, 0xf0, 0x3d, 0x9a, 0x9a, 0x43, 0x49, 0x8c, 0x73,
|
||||
0xca, 0x23, 0xa8, 0xf1, 0x90, 0x30, 0x18, 0x17, 0xb1, 0x4c, 0xad, 0xe9, 0x49, 0xed, 0x3c, 0x91,
|
||||
0xfa, 0x28, 0x66, 0x9a, 0x84, 0xc2, 0x1a, 0x1a, 0x4c, 0x3b, 0x56, 0x56, 0xce, 0xa0, 0xc7, 0xdc,
|
||||
0x1d, 0xeb, 0xe6, 0x98, 0xc2, 0x14, 0xeb, 0xcd, 0x27, 0x11, 0xd6, 0xb8, 0xa3, 0x6d, 0xaa, 0x87,
|
||||
0x47, 0xad, 0x2c, 0x5f, 0x52, 0x8c, 0xf2, 0x25, 0xc5, 0x38, 0x5f, 0xb2, 0x1a, 0x25, 0xbd, 0xbc,
|
||||
0xfe, 0xeb, 0xea, 0x9f, 0x82, 0xa4, 0x49, 0x42, 0x4e, 0x3c, 0x08, 0x98, 0x67, 0x29, 0x88, 0x59,
|
||||
0x1d, 0xff, 0x4c, 0x76, 0x72, 0x5f, 0x4c, 0x35, 0x61, 0xae, 0x32, 0x4b, 0xaa, 0x74, 0x50, 0x35,
|
||||
0x03, 0x47, 0x27, 0x10, 0x6b, 0x9e, 0x41, 0xac, 0x61, 0x06, 0xb1, 0x86, 0x29, 0xc4, 0x5a, 0xcf,
|
||||
0x20, 0xd6, 0x30, 0x83, 0x58, 0xd3, 0xd1, 0xff, 0xa7, 0x10, 0x6b, 0x33, 0x85, 0x58, 0xf7, 0xa9,
|
||||
0x0a, 0x24, 0x67, 0x45, 0x9a, 0x99, 0xca, 0x13, 0xdd, 0x9f, 0x3c, 0xce, 0x72, 0x0c, 0x84, 0xf6,
|
||||
0x4b, 0xf8, 0x67, 0x37, 0x3c, 0x09, 0xb3, 0x88, 0x83, 0xf6, 0x8b, 0xea, 0x71, 0xb2, 0xa0, 0xab,
|
||||
0xac, 0xb9, 0x21, 0xe5, 0x8a, 0x45, 0x62, 0x47, 0x19, 0x08, 0x95, 0xeb, 0x78, 0x3a, 0xb1, 0x35,
|
||||
0xd2, 0x8a, 0x05, 0x04, 0xf5, 0x08, 0xc8, 0xfc, 0xb4, 0x07, 0x48, 0x87, 0xe7, 0xa0, 0x8f, 0x9e,
|
||||
0x94, 0xd4, 0x47, 0x13, 0x47, 0xb0, 0xe9, 0xe8, 0x01, 0xc5, 0x4f, 0x13, 0x80, 0xd4, 0xb3, 0x64,
|
||||
0x89, 0xf4, 0xd7, 0xa0, 0xb9, 0xe3, 0x1c, 0x84, 0x21, 0xa3, 0x84, 0x49, 0x8b, 0x31, 0x4c, 0xda,
|
||||
0xdb, 0x06, 0x5d, 0x99, 0xae, 0x70, 0xbf, 0xe1, 0x19, 0xe5, 0x8d, 0x3b, 0xf0, 0x06, 0xee, 0x85,
|
||||
0x33, 0x74, 0xd4, 0x53, 0x0f, 0x3d, 0x9e, 0xa2, 0x3d, 0xa9, 0x5a, 0x3f, 0xe9, 0x43, 0xe8, 0x0f,
|
||||
0xf8, 0x48, 0xfa, 0x83, 0xcb, 0x77, 0x9f, 0xa0, 0x65, 0x77, 0xaf, 0x1e, 0xd9, 0x63, 0xf2, 0x78,
|
||||
0x35, 0x10, 0x5e, 0xa6, 0x65, 0x4f, 0xc6, 0xe3, 0xdc, 0x9b, 0x57, 0xee, 0xfa, 0xaa, 0xdb, 0xbd,
|
||||
0xeb, 0xde, 0xff, 0xc2, 0x57, 0xba, 0x3e, 0x7c, 0xf0, 0xb1, 0xdf, 0xba, 0xbf, 0xf1, 0xb3, 0x62,
|
||||
0x3f, 0xf3, 0x07, 0xc3, 0xfe, 0x63, 0xfe, 0xbc, 0xba, 0xf4, 0x27, 0x97, 0x3c, 0x7c, 0x60, 0x8f,
|
||||
0xbd, 0x1b, 0xa1, 0x0f, 0x97, 0x63, 0x42, 0x7c, 0x15, 0xea, 0x69, 0x52, 0x78, 0x18, 0xf7, 0x34,
|
||||
0x89, 0x9b, 0xfe, 0x40, 0xe2, 0xf1, 0xff, 0xfc, 0x10, 0x16, 0x08, 0xb0, 0xfe, 0xed, 0x34, 0x85,
|
||||
0xd7, 0x7f, 0x4f, 0x28, 0x54, 0x0b, 0x14, 0x50, 0x0e, 0x73, 0x9a, 0xc0, 0xd5, 0x6e, 0x42, 0xa0,
|
||||
0x5e, 0x20, 0x80, 0x07, 0xf1, 0x16, 0x08, 0xf4, 0x63, 0x36, 0x3c, 0x5b, 0x62, 0x02, 0x42, 0x9c,
|
||||
0xd3, 0x04, 0xfa, 0xbb, 0x31, 0x81, 0x8b, 0x25, 0x02, 0xdd, 0xf5, 0x9b, 0xc9, 0xfd, 0xb2, 0xf2,
|
||||
0xef, 0x3e, 0xed, 0x57, 0xbc, 0xbb, 0x1c, 0x56, 0x7c, 0x78, 0x7d, 0xda, 0xc3, 0x07, 0x78, 0xe5,
|
||||
0x0f, 0x79, 0x24, 0xc7, 0xff, 0xeb, 0xf4, 0x67, 0xe5, 0x1e, 0xbf, 0x96, 0x8e, 0x5f, 0xdd, 0xee,
|
||||
0xef, 0xeb, 0x77, 0xbf, 0xda, 0x7d, 0x3d, 0xa4, 0x89, 0x6f, 0x8b, 0x70, 0x87, 0xb7, 0x45, 0x38,
|
||||
0xfe, 0x32, 0xdc, 0x2f, 0x8f, 0x98, 0xd2, 0xba, 0xda, 0xf6, 0xfd, 0x70, 0xf1, 0x65, 0xaf, 0xf4,
|
||||
0x2e, 0x7e, 0xb5, 0x37, 0xd9, 0x95, 0xee, 0xc3, 0xbb, 0x2b, 0x7c, 0x6e, 0xfe, 0xc6, 0x0b, 0xf7,
|
||||
0x5d, 0x7f, 0xb7, 0xbd, 0xf9, 0xc5, 0x63, 0x62, 0xfa, 0x1b, 0x59, 0x78, 0xe5, 0xfc, 0x6c, 0xcc,
|
||||
0x78, 0xab, 0xf9, 0x68, 0x20, 0xcf, 0x57, 0x0c, 0xe4, 0x5f, 0xcf, 0xb7, 0x97, 0x8e, 0xbf, 0x72,
|
||||
0xf1, 0xaf, 0xa3, 0x91, 0x3c, 0xff, 0x8e, 0x91, 0xec, 0x7e, 0x5b, 0xd3, 0xf9, 0x6f, 0x78, 0xc1,
|
||||
0xd7, 0x52, 0xf7, 0xbf, 0x7d, 0x4f, 0xf7, 0x8f, 0xd6, 0x74, 0xff, 0x48, 0xc8, 0x2f, 0xf5, 0xfe,
|
||||
0xe8, 0x1b, 0x7a, 0x9f, 0x8e, 0xe1, 0x6d, 0x3f, 0x1a, 0xcf, 0xef, 0x2b, 0xc6, 0xf3, 0xbb, 0x8c,
|
||||
0x66, 0x3e, 0x8c, 0xdf, 0x4f, 0x0c, 0xc3, 0xeb, 0x20, 0xca, 0xb3, 0x32, 0xc4, 0xd1, 0x40, 0xec,
|
||||
0x87, 0x1d, 0x8e, 0x58, 0x71, 0xb1, 0xa2, 0xeb, 0x8b, 0xcd, 0xdd, 0xc7, 0xdb, 0x0d, 0xdf, 0xa3,
|
||||
0x38, 0x1f, 0xc1, 0xc5, 0x37, 0x8f, 0x60, 0x36, 0x8e, 0x3f, 0xd7, 0x2c, 0xc9, 0x9f, 0x78, 0x0f,
|
||||
0xda, 0xd2, 0x9a, 0xfc, 0xf9, 0xed, 0x12, 0x71, 0xb7, 0x7b, 0x39, 0x32, 0xe0, 0x62, 0xdf, 0xff,
|
||||
0xeb, 0xf5, 0xeb, 0x6d, 0x77, 0xe7, 0xce, 0x37, 0x57, 0xdb, 0x1d, 0xf5, 0x57, 0xfe, 0x0e, 0x46,
|
||||
0xf0, 0xc3, 0xed, 0xde, 0x82, 0xf0, 0x25, 0xda, 0x45, 0xdb, 0x2e, 0x59, 0x11, 0x54, 0x08, 0x2d,
|
||||
0x98, 0xd2, 0xeb, 0x39, 0x15, 0xbf, 0x40, 0xe4, 0xa8, 0x86, 0x64, 0x81, 0xe0, 0x87, 0xd7, 0x53,
|
||||
0x82, 0xf7, 0xd3, 0x1b, 0xa7, 0xcd, 0x97, 0x46, 0x38, 0x23, 0xb8, 0x38, 0x4d, 0x4d, 0x14, 0x4e,
|
||||
0xed, 0xe5, 0x81, 0x6b, 0x7c, 0x1d, 0x97, 0x2e, 0xd2, 0x8b, 0x97, 0xa3, 0x75, 0x78, 0x89, 0x75,
|
||||
0xb0, 0x77, 0x6e, 0x8e, 0x5f, 0x2d, 0x63, 0xff, 0xc2, 0x26, 0xcb, 0x5b, 0x0f, 0xb4, 0x2e, 0xb6,
|
||||
0x77, 0xae, 0xdf, 0x76, 0xd7, 0x57, 0x97, 0x9b, 0xdb, 0xcf, 0x4e, 0x7f, 0xad, 0xf3, 0xee, 0x5a,
|
||||
0x2c, 0x2c, 0xdf, 0x50, 0xa6, 0x02, 0x63, 0xdf, 0xf7, 0x9b, 0xcc, 0xc5, 0x0f, 0xe5, 0xbf, 0x1f,
|
||||
0x52, 0x70, 0x1e, 0xfc, 0xa4, 0xe7, 0x1e, 0x7e, 0xed, 0x06, 0x61, 0xdc, 0x23, 0xbc, 0xcc, 0x6e,
|
||||
0xf5, 0x0d, 0xb2, 0x93, 0x3e, 0xe7, 0x2f, 0x1c, 0xae, 0xbe, 0x43, 0x76, 0xce, 0xf3, 0xdb, 0x77,
|
||||
0x12, 0xbf, 0x7e, 0x5e, 0x7d, 0x8b, 0xec, 0x95, 0x17, 0x7f, 0x6d, 0x6e, 0xdc, 0xf9, 0x4f, 0x17,
|
||||
0x87, 0x7b, 0x84, 0x01, 0x5f, 0xb9, 0x2d, 0xca, 0x6d, 0xc2, 0x36, 0xeb, 0x0d, 0xec, 0x9a, 0x0d,
|
||||
0x75, 0x50, 0x82, 0xd7, 0x37, 0x2f, 0x75, 0xb3, 0x7a, 0xb2, 0xe9, 0xef, 0xf0, 0x6b, 0xa5, 0xe7,
|
||||
0x9b, 0xdd, 0xf6, 0x4e, 0xd8, 0x76, 0x72, 0x25, 0x9f, 0x9c, 0x8f, 0x56, 0xf2, 0x7c, 0xf5, 0x4a,
|
||||
0xce, 0xb4, 0xfe, 0xe5, 0x1a, 0xad, 0x37, 0x65, 0xbc, 0xb8, 0xd9, 0x6e, 0x2f, 0x8f, 0xd5, 0xfe,
|
||||
0xe5, 0x5a, 0xb5, 0x7f, 0x79, 0xc2, 0xfa, 0xfd, 0xf1, 0x0f, 0xfa, 0xc7, 0xaf, 0x6a, 0x5e, 0xf5,
|
||||
0xef, 0xee, 0x3e, 0x1f, 0x8d, 0xe1, 0x8f, 0x6f, 0x19, 0xc3, 0xcc, 0x00, 0x9d, 0x4f, 0x0d, 0xd0,
|
||||
0x93, 0xcd, 0xa7, 0xeb, 0x5b, 0x18, 0xbb, 0xf3, 0xdb, 0x6d, 0xbf, 0xbd, 0xeb, 0x8f, 0x8c, 0xd0,
|
||||
0x4d, 0xbf, 0xdb, 0xaf, 0x32, 0x5f, 0xe5, 0xce, 0xf7, 0x96, 0x2f, 0x99, 0xa1, 0x19, 0x58, 0xb2,
|
||||
0xe4, 0x1e, 0xde, 0x8c, 0xf4, 0xdd, 0xde, 0x97, 0xff, 0xbd, 0x56, 0xe9, 0xa6, 0xbf, 0x3a, 0x26,
|
||||
0xfa, 0xbd, 0x96, 0xe9, 0x46, 0x3c, 0xa3, 0xc9, 0xe4, 0xf9, 0x6b, 0x4f, 0xf7, 0xd1, 0x1c, 0x85,
|
||||
0xbf, 0x33, 0x92, 0x60, 0xac, 0xae, 0x3c, 0x09, 0xff, 0x61, 0xe2, 0xfd, 0xc4, 0x8d, 0x5e, 0xd1,
|
||||
0x67, 0xcb, 0xeb, 0xb9, 0xbc, 0xde, 0x96, 0xd7, 0x56, 0xf7, 0x60, 0x9d, 0x1e, 0x63, 0x3c, 0x6e,
|
||||
0xcf, 0x6b, 0xa8, 0xda, 0xe6, 0xe6, 0x66, 0xf7, 0x79, 0x22, 0x59, 0x3c, 0x2d, 0xfe, 0xa2, 0xb3,
|
||||
0x17, 0x40, 0x9b, 0x9f, 0xf8, 0xf8, 0xa1, 0x9a, 0xb3, 0x1f, 0x57, 0x5c, 0xfc, 0x44, 0x2e, 0xde,
|
||||
0x52, 0x26, 0x7b, 0x27, 0xaa, 0x7c, 0xfa, 0x0e, 0x5e, 0xf9, 0x48, 0xae, 0x7c, 0xb5, 0x77, 0x26,
|
||||
0xcf, 0x0e, 0x23, 0xfd, 0x0c, 0x18, 0xe0, 0xee, 0xed, 0xed, 0xf5, 0xc7, 0x37, 0x6f, 0xdd, 0x8d,
|
||||
0x8a, 0x98, 0x3b, 0xb0, 0xa1, 0xfb, 0xec, 0x95, 0xc0, 0xb9, 0x5f, 0xcd, 0x06, 0x1a, 0xe2, 0x11,
|
||||
0x85, 0x60, 0x14, 0xc2, 0x09, 0x0a, 0xf1, 0x24, 0x85, 0x1f, 0xdd, 0xfb, 0xed, 0xf6, 0xc6, 0x6d,
|
||||
0x37, 0xdd, 0x5b, 0x87, 0xdd, 0x71, 0x4c, 0xed, 0x85, 0x51, 0x7b, 0x71, 0x6a, 0x3c, 0x21, 0xaa,
|
||||
0xe2, 0x45, 0xd5, 0xbc, 0x14, 0x11, 0xec, 0x19, 0xd1, 0x0f, 0xfd, 0xcf, 0xf7, 0x30, 0xe8, 0xf1,
|
||||
0xff, 0xee, 0xe5, 0x88, 0x2f, 0xeb, 0x1e, 0x6d, 0x5b, 0xf7, 0x58, 0xd5, 0xe7, 0x72, 0xe1, 0xaf,
|
||||
0x5c, 0xd9, 0x57, 0xd7, 0xd7, 0x77, 0xf8, 0xbd, 0xe8, 0xd7, 0xef, 0xde, 0x1c, 0xec, 0x29, 0x48,
|
||||
0xcc, 0xb4, 0xfb, 0xd9, 0x54, 0xbb, 0xf1, 0xeb, 0xba, 0x97, 0xee, 0xbf, 0xb1, 0x30, 0x47, 0x8a,
|
||||
0x7d, 0x35, 0x89, 0x4f, 0xf0, 0xce, 0xff, 0x7f, 0x18, 0x31, 0xee, 0x29, 0xfe, 0x76, 0xbb, 0xb9,
|
||||
0xfc, 0xb8, 0xd9, 0xc9, 0x30, 0x2f, 0xdf, 0x7d, 0x70, 0xd0, 0x80, 0x15, 0xc2, 0xf5, 0xec, 0xb1,
|
||||
0xde, 0xec, 0x7b, 0xd9, 0x6a, 0xf9, 0x8e, 0x45, 0x47, 0xe1, 0xb8, 0x1c, 0x3c, 0xc0, 0x1f, 0x10,
|
||||
0xe2, 0x47, 0x61, 0xf7, 0xbf, 0x7f, 0x74, 0x41, 0xa4, 0x8f, 0x67, 0x25, 0xde, 0x7b, 0x23, 0x9b,
|
||||
0xcc, 0x58, 0xd6, 0xf6, 0x36, 0xf6, 0xd9, 0x0a, 0x1b, 0xfb, 0x9f, 0x4b, 0xfe, 0xe5, 0x33, 0x5b,
|
||||
0xe3, 0x54, 0x4e, 0x44, 0xe6, 0xa0, 0x7c, 0x33, 0x83, 0xfe, 0x62, 0x8d, 0x41, 0x7f, 0xa1, 0xa3,
|
||||
0x3d, 0x84, 0x59, 0xc7, 0xbb, 0xca, 0x8b, 0x7b, 0x2d, 0xfa, 0xc4, 0x86, 0xab, 0x09, 0xf9, 0x09,
|
||||
0x61, 0xdf, 0xfe, 0x3f, 0xfa, 0x52, 0x5a, 0x0d, 0x37, 0x5f, 0x3b, 0xfc, 0x18, 0xc5, 0xe6, 0xd5,
|
||||
0xf5, 0xc7, 0xbb, 0x9f, 0x5f, 0xed, 0x36, 0x57, 0xef, 0xe5, 0x2a, 0x3d, 0x2f, 0x5f, 0xf0, 0x56,
|
||||
0x3e, 0xbc, 0xa2, 0x0f, 0x3f, 0x54, 0xfe, 0xff, 0x20, 0x24, 0xfe, 0x2a, 0xbe, 0x7c, 0x00, 0x00,
|
||||
};
|
||||
1223
wled00/html_mobile.h
Normal file
1223
wled00/html_mobile.h
Normal file
File diff suppressed because it is too large
Load Diff
53
wled00/html_other.h
Normal file
53
wled00/html_other.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Various pages
|
||||
*/
|
||||
|
||||
//USER HTML HERE (/u subpage)
|
||||
const char PAGE_usermod[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><body>No usermod installed or it doesn't specify a custom web page.</body></html>)=====";
|
||||
|
||||
|
||||
//server message
|
||||
const char PAGE_msg[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><head><meta content='width=device-width' name='viewport'>
|
||||
<title>WLED Message</title>
|
||||
<script>function B(){window.history.back()};function RS(){window.location = "/settings";}function RP(){top.location.href="/";}</script>
|
||||
%CSS%.bt{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:.3ch solid var(--bCol);display:inline-block;filter:drop-shadow(-5px -5px 5px var(--sCol));font-size:20px;margin:8px;margin-top:12px}body{font-family:var(--cFn),sans-serif;text-align:center;background:var(--cCol);color:var(--tCol);line-height:200%%;margin:0}</style></head>
|
||||
<body><h2>%MSG%</body></html>)=====";
|
||||
|
||||
|
||||
//firmware update page
|
||||
const char PAGE_update[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><head><meta content='width=device-width' name='viewport'><title>WLED Update</title><script>function B(){window.history.back()}</script>
|
||||
%CSS%.bt{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:.3ch solid var(--bCol);display:inline-block;filter:drop-shadow(-5px -5px 5px var(--sCol));font-size:20px;margin:8px;margin-top:12px}input[type=file]{font-size:16px}body{font-family:var(--cFn),sans-serif;text-align:center;background:var(--cCol);color:var(--tCol);line-height:200%%}</style></head>
|
||||
<body><h2>WLED Software Update</h2>Installed version: 0.8.6<br>Download the latest binary: <a href="https://github.com/Aircoookie/WLED/releases"><img src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square"></a><br><form method='POST' action='/update' enctype='multipart/form-data'><input type='file' class="bt" name='update' required><br><input type='submit' class="bt" value='Update!'></form><button type="button" class="bt" onclick="B()">Back</button></body></html>)=====";
|
||||
|
||||
|
||||
//new user welcome page
|
||||
#ifndef WLED_DISABLE_MOBILE_UI
|
||||
const char PAGE_welcome[] PROGMEM = R"=====(<!DOCTYPE html><html><head><meta charset=utf-8><meta content='width=device-width' name=viewport><meta name=theme-color content=#333333><title>WLED Setup</title> <style>body{font-family:Verdana,Helvetica,sans-serif;text-align:center;background-color:#333;margin:0;color:#fff}button{outline:0;cursor:pointer}.btn{padding:8px;margin:10px;width:230px;text-transform:uppercase;font-family:helvetica;font-size:19px;background-color:#222;color:white;border:0 solid white;border-radius:5px}svg{fill:#fff}</style></head>
|
||||
<body> <svg style=position:absolute;width:0;height:0;overflow:hidden version=1.1 xmlns=http://www.w3.org/2000/svg> <defs> <symbol id=lnr-smile viewBox="0 0 1024 1024"><path d="M486.4 1024c-129.922 0-252.067-50.594-343.936-142.464s-142.464-214.014-142.464-343.936c0-129.923 50.595-252.067 142.464-343.936s214.013-142.464 343.936-142.464c129.922 0 252.067 50.595 343.936 142.464s142.464 214.014 142.464 343.936-50.594 252.067-142.464 343.936c-91.869 91.87-214.014 142.464-343.936 142.464zM486.4 102.4c-239.97 0-435.2 195.23-435.2 435.2s195.23 435.2 435.2 435.2 435.2-195.23 435.2-435.2-195.23-435.2-435.2-435.2z"></path><path d="M332.8 409.6c-42.347 0-76.8-34.453-76.8-76.8s34.453-76.8 76.8-76.8 76.8 34.453 76.8 76.8-34.453 76.8-76.8 76.8zM332.8 307.2c-14.115 0-25.6 11.485-25.6 25.6s11.485 25.6 25.6 25.6 25.6-11.485 25.6-25.6-11.485-25.6-25.6-25.6z"></path><path d="M640 409.6c-42.349 0-76.8-34.453-76.8-76.8s34.451-76.8 76.8-76.8 76.8 34.453 76.8 76.8-34.451 76.8-76.8 76.8zM640 307.2c-14.115 0-25.6 11.485-25.6 25.6s11.485 25.6 25.6 25.6 25.6-11.485 25.6-25.6-11.485-25.6-25.6-25.6z"></path><path d="M486.4 870.4c-183.506 0-332.8-149.294-332.8-332.8 0-14.139 11.462-25.6 25.6-25.6s25.6 11.461 25.6 25.6c0 155.275 126.325 281.6 281.6 281.6s281.6-126.325 281.6-281.6c0-14.139 11.461-25.6 25.6-25.6s25.6 11.461 25.6 25.6c0 183.506-149.294 332.8-332.8 332.8z"></path></symbol> </defs></svg> <br><br>
|
||||
<svg><use xlink:href=#lnr-smile></use></svg><h1>Welcome to WLED!</h1><h3>Thank you for installing my application!</h3> If you encounter a bug or have a question/feature suggestion, feel free to open a GitHub issue!<br><br> <b>Next steps:</b><br><br> Connect the module to your local WiFi here!<br> <button class=btn onclick="window.location.href='/settings/wifi'">WiFi settings</button><br> <i>Just trying this out in AP mode?</i><br> <button class=btn onclick="window.location.href='/sliders'">To the controls!</button></body></html>)=====";
|
||||
#else
|
||||
const char PAGE_welcome[] PROGMEM = "";
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* favicon
|
||||
*/
|
||||
const uint8_t favicon[] PROGMEM = {
|
||||
0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x10, 0x10, 0x00, 0x00, 0x01, 0x00,
|
||||
0x18, 0x00, 0x86, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x89, 0x50,
|
||||
0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48,
|
||||
0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x08, 0x06,
|
||||
0x00, 0x00, 0x00, 0x1F, 0xF3, 0xFF, 0x61, 0x00, 0x00, 0x00, 0x4D, 0x49,
|
||||
0x44, 0x41, 0x54, 0x38, 0x8D, 0x63, 0xFC, 0xFF, 0xFF, 0x3F, 0x03, 0xB1,
|
||||
0x80, 0xD1, 0x9E, 0x01, 0x43, 0x31, 0x13, 0xD1, 0xBA, 0x71, 0x00, 0x8A,
|
||||
0x0D, 0x60, 0x21, 0xA4, 0x00, 0xD9, 0xD9, 0xFF, 0x0F, 0x32, 0x30, 0x52,
|
||||
0xDD, 0x05, 0xB4, 0xF1, 0x02, 0xB6, 0xD0, 0xA6, 0x99, 0x0B, 0x68, 0x1F,
|
||||
0x0B, 0xD8, 0x42, 0x9E, 0xAA, 0x2E, 0xA0, 0xD8, 0x00, 0x46, 0x06, 0x3B,
|
||||
0xCC, 0xCC, 0x40, 0xC8, 0xD9, 0x54, 0x75, 0x01, 0xE5, 0x5E, 0x20, 0x25,
|
||||
0x3B, 0x63, 0x03, 0x00, 0x3E, 0xB7, 0x11, 0x5A, 0x8D, 0x1C, 0x07, 0xB4,
|
||||
0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82
|
||||
};
|
||||
@@ -1,23 +1,15 @@
|
||||
/*
|
||||
* Settings html
|
||||
*/
|
||||
|
||||
|
||||
//common CSS of settings pages
|
||||
const char PAGE_settingsCss[] PROGMEM = R"=====(
|
||||
body{font-family:var(--cFn),sans-serif;text-align:center;background:var(--cCol);color:var(--tCol);line-height:200%;margin:0;background-attachment:fixed}hr{border-color:var(--dCol);filter:drop-shadow(-5px -5px 5px var(--sCol))}button{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:.3ch solid var(--bCol);display:inline-block;filter:drop-shadow(-5px -5px 5px var(--sCol));font-size:20px;margin:8px;margin-top:12px}.helpB{text-align:left;position:absolute;width:60px}input{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:.5ch solid var(--bCol);filter:drop-shadow(-5px -5px 5px var(--sCol))}input[type=number]{width:3em}select{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:0.5ch solid var(--bCol);filter:drop-shadow( -5px -5px 5px var(--sCol) );}</style>
|
||||
)=====";
|
||||
const char PAGE_settingsCss[] PROGMEM = R"=====(body{font-family:var(--cFn),sans-serif;text-align:center;background:var(--cCol);color:var(--tCol);line-height:200%%;margin:0;background-attachment:fixed}hr{border-color:var(--dCol);filter:drop-shadow(-5px -5px 5px var(--sCol))}button{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:.3ch solid var(--bCol);display:inline-block;filter:drop-shadow(-5px -5px 5px var(--sCol));font-size:20px;margin:8px;margin-top:12px}.helpB{text-align:left;position:absolute;width:60px}input{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:.5ch solid var(--bCol);filter:drop-shadow(-5px -5px 5px var(--sCol))}input[type=number]{width:4em}select{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:0.5ch solid var(--bCol);filter:drop-shadow( -5px -5px 5px var(--sCol) );}td{padding:2px;}</style>)=====";
|
||||
|
||||
|
||||
//settings menu
|
||||
const char PAGE_settings0[] PROGMEM = R"=====(
|
||||
<!DOCTYPE html>
|
||||
<html><head><title>WLED Settings</title>
|
||||
)=====";
|
||||
|
||||
const char PAGE_settings1[] PROGMEM = R"=====(
|
||||
body{text-align:center;background:var(--cCol);height:100%;margin:0;background-attachment:fixed}html{--h:11.55vh}button{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),Helvetica,sans-serif;border:.3ch solid var(--bCol);display:inline-block;filter:drop-shadow(-5px -5px 5px var(--sCol));font-size:8vmin;height:var(--h);width:95%;margin-top:2.4vh}</style>
|
||||
<script>function BB(){if(window.frameElement){document.getElementById("b").style.display="none";document.documentElement.style.setProperty("--h","13.86vh")}};</script>
|
||||
</head>
|
||||
const char PAGE_settings[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><head><title>WLED Settings</title>%CSS%body{text-align:center;background:var(--cCol);height:100%%;margin:0;background-attachment:fixed}html{--h:11.55vh}button{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),Helvetica,sans-serif;border:.3ch solid var(--bCol);display:inline-block;filter:drop-shadow(-5px -5px 5px var(--sCol));font-size:8vmin;height:var(--h);width:95%%;margin-top:2.4vh}</style>
|
||||
<script>function BB(){if(window.frameElement){document.getElementById("b").style.display="none";document.documentElement.style.setProperty("--h","13.86vh")}};</script></head>
|
||||
<body onload=BB()>
|
||||
<form action=/><button type=submit id=b>Back</button></form>
|
||||
<form action=/settings/wifi><button type=submit>WiFi Setup</button></form>
|
||||
@@ -26,24 +18,17 @@ body{text-align:center;background:var(--cCol);height:100%;margin:0;background-at
|
||||
<form action=/settings/sync><button type=submit>Sync Interfaces</button></form>
|
||||
<form action=/settings/time><button type=submit>Time & Macros</button></form>
|
||||
<form action=/settings/sec><button type=submit>Security & Updates</button></form>
|
||||
</body>
|
||||
</html>
|
||||
)=====";
|
||||
</body></html>)=====";
|
||||
|
||||
|
||||
//wifi settings
|
||||
const char PAGE_settings_wifi0[] PROGMEM = R"=====(
|
||||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<title>WiFi Settings</title><script>function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#wifi-settings");}function B(){window.history.back();}function GetV(){var d = document;
|
||||
)=====";
|
||||
|
||||
const char PAGE_settings_wifi1[] PROGMEM = R"=====(
|
||||
</head>
|
||||
<body onload="GetV()">
|
||||
const char PAGE_settings_wifi[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><head><meta name="viewport" content="width=500">
|
||||
<title>WiFi Settings</title><script>function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#wifi-settings");}function B(){window.history.back();}function GetV(){var d=document;
|
||||
%CSS%%SCSS%</head><body onload="GetV()">
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<div class="helpB"><button type="button" onclick="H()">?</button></div>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save & Reboot</button><hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save & Connect</button><hr>
|
||||
<h2>WiFi setup</h2>
|
||||
<h3>Connect to existing network</h3>
|
||||
Network name (SSID, empty to not connect): <br><input name="CS" maxlength="32"><br>
|
||||
@@ -65,63 +50,89 @@ Static subnet mask:<br>
|
||||
<input name="S3" type="number" min="0" max="255" required><br>
|
||||
mDNS address (leave empty for no mDNS):<br/>
|
||||
http:// <input name="CM" maxlength="32"> .local<br>
|
||||
Try connecting before opening AP for: <input name="AT" type="number" min="0" max="255" required> s <br>
|
||||
Client IP: <span class="sip"> Not connected </span><br>
|
||||
<h3>Configure Access Point</h3>
|
||||
AP SSID (leave empty for no AP):<br> <input name="AS" maxlength="32"><br>
|
||||
AP SSID (leave empty for no AP):<br><input name="AS" maxlength="32"><br>
|
||||
Hide AP name: <input type="checkbox" name="AH"><br>
|
||||
AP password (leave empty for open):<br> <input type="password" name="AP" maxlength="63"> <br>
|
||||
AP password (leave empty for open):<br> <input type="password" name="AP" maxlength="63"><br>
|
||||
Access Point WiFi channel: <input name="AC" type="number" min="1" max="13" required><br>
|
||||
AP opens:
|
||||
<select name="AB">
|
||||
<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>
|
||||
AP IP: <span class="sip"> Not active </span><hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save & Reboot</button>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save & Connect</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
)=====";
|
||||
</html>)=====";
|
||||
|
||||
|
||||
//LED settings
|
||||
const char PAGE_settings_leds0[] PROGMEM = R"=====(
|
||||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<title>LED Settings</title><script>function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#led-settings");}function B(){window.history.back();}function GetV(){var d = document;
|
||||
)=====";
|
||||
|
||||
const char PAGE_settings_leds1[] PROGMEM = R"=====(
|
||||
</head>
|
||||
<body onload="GetV()">
|
||||
const char PAGE_settings_leds[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><head><meta name="viewport" content="width=500">
|
||||
<title>LED Settings</title><script>var f=0,p=0;function H()
|
||||
{window.open("https://github.com/Aircoookie/WLED/wiki/Settings#led-settings");}
|
||||
function B(){window.open("/settings","_self");}function S(){GetV();setTimeout(function(){fillfx(0);},200);setTimeout(function(){fillfx(1);},400);UI();}
|
||||
function UI(){var myC=document.querySelectorAll('.wc'),l=myC.length;for(i=0;i<l;i++){myC[i].style.display=(document.getElementById('rgbw').checked)?'inline':'none';}
|
||||
var val=Math.ceil((100+document.Sf.LC.value*55)/500)/2;val=(val>5)?Math.ceil(val):val;var s="";if(val<1.02){s="ESP 5V pin with 1A USB supply";}else{s="External 5V ";s+=val;s+="A supply connected to LEDs";}document.getElementById('psu').innerHTML=s;}
|
||||
function fillfx(pl){e="<option>Error loading list!</option>";el=pl?Sf.FP:Sf.FX;fetch(pl?'/json/pal':'/json/eff').then(res=>{if(!res.ok){el.innerHTML=e;}
|
||||
return res.json();}).then(json=>{var x="";for(i in json){x+="<option value=\""+i+"\">"+json[i]+" ("+i+")</option>";}
|
||||
el.innerHTML=x;el.selectedIndex=pl?p:f;}).catch(function(){el.innerHTML=e;})}function GetV(){var d=document;%CSS%%SCSS%</head>
|
||||
<body onload="S()">
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<div class="helpB"><button type="button" onclick="H()">?</button></div>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
|
||||
<h2>LED setup</h2>
|
||||
LED count: <input name="LC" type="number" min="1" max="1200" required><br>
|
||||
LEDs are 4-channel type (RGBW): <input type="checkbox" name="EW"><br>
|
||||
<br>
|
||||
Apply preset <input name="BP" type="number" min="0" max="25" required> at boot (0 uses defaults)<br>
|
||||
Turn on after power up/reset: <input type="checkbox" name="BO"><br>
|
||||
<br>
|
||||
Default RGB color:
|
||||
LED count: <input name="LC" type="number" min="1" max="1500" oninput=UI() required><br>
|
||||
<i>Recommended power supply for brightest white:</i><br>
|
||||
<b><span id="psu">?</span></b><br><br>
|
||||
Maximum Current: <input name="MA" type="number" min="250" max="65000" required> mA<br>
|
||||
<i>Automatically limits brightness to stay close to the limit.<br>
|
||||
Keep at <1A if powering LEDs directly from the ESP 5V pin!<br>
|
||||
If you are using an external 5V supply, enter its rating.<br>
|
||||
"65000" completely diasbles the power calculation.<br>
|
||||
(Current estimated usage: <span class="pow">unknown</span>)</i><br><br>
|
||||
LEDs are 4-channel type (RGBW): <input type="checkbox" name="EW" onchange=UI() id="rgbw"><br>
|
||||
Color order:
|
||||
<select name="CO">
|
||||
<option value="0">GRB</option>
|
||||
<option value="1">RGB</option>
|
||||
<option value="2">BRG</option>
|
||||
<option value="3">RBG</option></select>
|
||||
<h3>Defaults</h3>
|
||||
Turn LEDs on after power up/reset: <input type="checkbox" name="BO"><br>
|
||||
Default brightness: <input name="CA" type="number" min="0" max="255" required> (0-255)<br><br>
|
||||
Set current color, brightness and effects as boot default: <input type="checkbox" name="IS"><br>
|
||||
Set current preset cycle setting as boot default: <input type="checkbox" name="PC">
|
||||
<br>- <i>or</i> -<br>
|
||||
Apply preset <input name="BP" type="number" min="0" max="25" required> at boot (0 uses defaults)
|
||||
<br>- <i>or</i> -<br>
|
||||
Default RGB<span class="wc">W</span> color:<br>
|
||||
<input name="CR" type="number" min="0" max="255" required>
|
||||
<input name="CG" type="number" min="0" max="255" required>
|
||||
<input name="CB" type="number" min="0" max="255" required><br>
|
||||
Default white value (only RGBW): <input name="CW" type="number" min="0" max="255" required><br>
|
||||
Auto-calculate white from RGB instead: <input type="checkbox" name="AW"><br>
|
||||
Default brightness: <input name="CA" type="number" min="0" max="255" required> (0-255)<br>
|
||||
Default effect ID: <input name="FX" type="number" min="0" max="57" required><br>
|
||||
Default effect speed: <input name="SX" type="number" min="0" max="255" required><br>
|
||||
Default effect intensity: <input name="IX" type="number" min="0" max="255" required><br>
|
||||
Default effect palette: <input name="FP" type="number" min="0" max="255" required><br>
|
||||
Default secondary RGB(W):<br>
|
||||
<input name="CB" type="number" min="0" max="255" required>
|
||||
<span class="wc"><input name="CW" type="number" min="0" max="255" required><br>
|
||||
Auto-calculate white from RGB instead: <input type="checkbox" name="AW"></span><br>
|
||||
Default secondary RGB<span class="wc">W</span>:<br>
|
||||
<input name="SR" type="number" min="0" max="255" required>
|
||||
<input name="SG" type="number" min="0" max="255" required>
|
||||
<input name="SB" type="number" min="0" max="255" required>
|
||||
<input name="SW" type="number" min="0" max="255" required><br>
|
||||
Ignore and use current color, brightness and effects: <input type="checkbox" name="IS"><br>
|
||||
Save current preset cycle configuration as boot default: <input type="checkbox" name="PC"><br>
|
||||
<br>
|
||||
<span class="wc"><input name="SW" type="number" min="0" max="255" required></span><br>
|
||||
Default effect ID:<br>
|
||||
<select name="FX">
|
||||
<option>Loading...</option>
|
||||
</select>
|
||||
<br>Default color palette:<br>
|
||||
<select name="FP">
|
||||
<option>Loading...</option>
|
||||
</select><br>
|
||||
Default effect speed: <input name="SX" type="number" min="0" max="255" required><br>
|
||||
Default effect intensity: <input name="IX" type="number" min="0" max="255" required><br><br>
|
||||
Use Gamma correction for color: <input type="checkbox" name="GC"> (strongly recommended)<br>
|
||||
Use Gamma correction for brightness: <input type="checkbox" name="GB"> (not recommended)<br>
|
||||
Brightness factor: <input name="BF" type="number" min="0" max="255" required> %
|
||||
Use Gamma correction for brightness: <input type="checkbox" name="GB"> (not recommended)<br><br>
|
||||
Brightness factor: <input name="BF" type="number" min="0" max="255" required> %%
|
||||
<h3>Transitions</h3>
|
||||
Crossfade: <input type="checkbox" name="TF"><br>
|
||||
Transition Time: <input name="TD" maxlength="5" size="2"> ms<br>
|
||||
@@ -140,25 +151,19 @@ Palette blending:
|
||||
<option value="3">None (not recommended)</option>
|
||||
</select><br>
|
||||
Reverse LED order (rotate 180): <input type="checkbox" name="RV"><br>
|
||||
Init LEDs after WiFi: <input type="checkbox" name="EI"><br>
|
||||
Skip first LED: <input type="checkbox" name="SL"><hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
)=====";
|
||||
</html>)=====";
|
||||
|
||||
|
||||
//User Interface settings
|
||||
const char PAGE_settings_ui0[] PROGMEM = R"=====(
|
||||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
const char PAGE_settings_ui[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><head><meta name="viewport" content="width=500">
|
||||
<title>UI Settings</title><script>
|
||||
function gId(s){return document.getElementById(s);}function S(){GetV();Ct();}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#user-interface-settings");}function B(){window.history.back();}function Ct(){if (gId("co").selected){gId("cth").style.display="block";}else{gId("cth").style.display="none";}}function GetV(){var d = document;
|
||||
)=====";
|
||||
|
||||
const char PAGE_settings_ui1[] PROGMEM = R"=====(
|
||||
</head>
|
||||
function gId(s){return document.getElementById(s);}function S(){GetV();Ct();}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#user-interface-settings");}function B(){window.history.back();}function Ct(){if (gId("co").selected){gId("cth").style.display="block";}else{gId("cth").style.display="none";}}function GetV(){var d=document;
|
||||
%CSS%%SCSS%</head>
|
||||
<body onload="S()">
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<div class="helpB"><button type="button" onclick="H()">?</button></div>
|
||||
@@ -182,13 +187,13 @@ Color Theme:
|
||||
<option value="4">Electric</option>
|
||||
<option value="5">Mint</option>
|
||||
<option value="6">Amber</option>
|
||||
<option value="7">Club</option>
|
||||
<option value="7">Dark</option>
|
||||
<option value="8">Air</option>
|
||||
<option value="9">Nixie</option>
|
||||
<option value="10">Terminal</option>
|
||||
<option value="11">C64</option>
|
||||
<option value="12">Easter</option>
|
||||
<option value="13">Placeholder</option>
|
||||
<option value="13">Christmas</option>
|
||||
<option value="14">The End</option>
|
||||
<option value="15" id="co">Custom</option>
|
||||
</select><br>
|
||||
@@ -205,53 +210,60 @@ Make sure the font you use is installed on your system!<br>
|
||||
<hr><button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
)=====";
|
||||
</html>)=====";
|
||||
|
||||
|
||||
//sync settings
|
||||
const char PAGE_settings_sync0[] PROGMEM = R"=====(
|
||||
<!DOCTYPE html>
|
||||
<html><head><title>Sync Settings</title>
|
||||
<script>function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#sync-settings");}function B(){window.open("/settings","_self");}function GetV(){var d = document;
|
||||
)=====";
|
||||
|
||||
const char PAGE_settings_sync1[] PROGMEM = R"=====(
|
||||
</head>
|
||||
const char PAGE_settings_sync[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><head><meta name="viewport" content="width=500"><title>Sync Settings</title>
|
||||
<script>function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#sync-settings");}function B(){window.open("/settings","_self");}function GetV(){var d=document;
|
||||
%CSS%%SCSS%</head>
|
||||
<body onload="GetV()">
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<div class="helpB"><button type="button" onclick="H()">?</button></div>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
|
||||
<h2>Sync setup</h2>
|
||||
<h3>Button setup</h3>
|
||||
On/Off button enabled: <input type="checkbox" name="BT">
|
||||
On/Off button enabled: <input type="checkbox" name="BT"><br>
|
||||
Infrared receiver enabled: <input type="checkbox" name="IR"><br>
|
||||
<a href="https://github.com/Aircoookie/WLED/wiki/Infrared-Control" target="_blank">IR info</a>
|
||||
<h3>WLED Broadcast</h3>
|
||||
UDP Port: <input name="UP" maxlength="5" size="4"><br>
|
||||
UDP Port: <input name="UP" type="number" min="1" max="65535" required><br>
|
||||
Receive <input type="checkbox" name="RB">Brightness, <input type="checkbox" name="RC">Color, and <input type="checkbox" name="RX">Effects<br>
|
||||
Send notifications on direct change: <input type="checkbox" name="SD"><br>
|
||||
Send notifications on button press: <input type="checkbox" name="SB"><br>
|
||||
Send Alexa notifications: <input type="checkbox" name="SA"><br>
|
||||
Send Philips Hue change notifications: <input type="checkbox" name="SH"><br>
|
||||
Send notifications twice: <input type="checkbox" name="S2"><br>
|
||||
Send Macro notifications: <input type="checkbox" name="SM"><br>
|
||||
Send notifications twice: <input type="checkbox" name="S2">
|
||||
<h3>Realtime</h3>
|
||||
Receive UDP realtime: <input type="checkbox" name="RD"><br><br>
|
||||
<i>E1.31 (sACN)</i><br>
|
||||
Use E1.31 multicast: <input type="checkbox" name="EM"><br>
|
||||
E1.31 universe: <input name="EU" type="number" min="1" max="63999" required><br>
|
||||
E1.31 start universe: <input name="EU" type="number" min="1" max="63999" required><br>
|
||||
<i>Reboot required.</i> Check out <a href="https://github.com/ahodges9/LedFx" target="_blank">LedFx</a>!<br><br>
|
||||
Timeout: <input name="ET" type="number" min="1" max="65000" required> ms<br>
|
||||
Force max brightness: <input type="checkbox" name="FB"><br>
|
||||
Disable realtime gamma correction: <input type="checkbox" name="RG"><br>
|
||||
Realtime LED offset: <input name="WO" type="number" min="-255" max="255" required><br>
|
||||
Enable UI access during realtime: <input type="checkbox" name="RU"> (can cause issues)
|
||||
Realtime LED offset: <input name="WO" type="number" min="-255" max="255" required>
|
||||
<h3>Alexa Voice Assistant</h3>
|
||||
Emulate Alexa device: <input type="checkbox" name="AL"><br>
|
||||
Alexa invocation name: <input name="AI" maxlength="32">
|
||||
<h3>Blynk</h3>
|
||||
<b>Blynk, MQTT and Hue sync all connect to external hosts!<br>
|
||||
This may impact the responsiveness of the ESP8266.</b><br>
|
||||
For best results, only use one of these services at a time.<br>
|
||||
(alternatively, connect a second ESP to them and use the UDP sync)<br><br>
|
||||
Device Auth token: <input name="BK" maxlength="33"><br>
|
||||
<i>Clear the token field to disable. </i><a href="https://github.com/Aircoookie/WLED/wiki/Blynk" target="_blank">Setup info</a>
|
||||
<h3>MQTT</h3>
|
||||
Broker: <input name="MS" maxlength="32"><br>
|
||||
Broker: <input name="MS" maxlength="32">
|
||||
Port: <input name="MQPORT" type="number" min="1" max="65535" required><br>
|
||||
<b>The MQTT credentials are sent over an unsecured connection.<br>
|
||||
Never use the MQTT password for another service!</b><br>
|
||||
Username: <input name="MQUSER" maxlength="40"><br>
|
||||
Password: <input type="password" input name="MQPASS" maxlength="40"><br>
|
||||
Client ID: <input name="MQCID" maxlength="40"><br>
|
||||
Device Topic: <input name="MD" maxlength="32"><br>
|
||||
Group Topic: <input name="MG" maxlength="32"><br>
|
||||
<i>Reboot required to apply changes. </i><a href="https://github.com/Aircoookie/WLED/wiki/MQTT" target="_blank">MQTT info</a>
|
||||
@@ -270,27 +282,25 @@ Hue status: <span class="hms"> Internal ESP Error! </span><hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
)=====";
|
||||
</html>)=====";
|
||||
|
||||
|
||||
//time and macro settings
|
||||
const char PAGE_settings_time0[] PROGMEM = R"=====(
|
||||
<!DOCTYPE html>
|
||||
<html><head><title>Time Settings</title>
|
||||
<script>function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings");}function B(){window.open("/settings","_self");}function S(){GetV();Cs();}function gId(s){return document.getElementById(s);}function Cs(){gId("cac").style.display="none";gId("coc").style.display="block";gId("ccc").style.display="none";if (gId("ca").selected){gId("cac").style.display="block";}if (gId("cc").selected){gId("coc").style.display="none";gId("ccc").style.display="block";}if (gId("cn").selected){gId("coc").style.display="none";}}function GetV(){var d = document;
|
||||
)=====";
|
||||
|
||||
const char PAGE_settings_time1[] PROGMEM = R"=====(
|
||||
</head>
|
||||
const char PAGE_settings_time[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><head><meta name="viewport" content="width=500"><title>Time Settings</title>
|
||||
<script>var d=document;function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings");}function B(){window.open("/settings","_self");}function S(){BTa();GetV();Cs();FC();}function gId(s){return d.getElementById(s);}function Cs(){gId("cac").style.display="none";gId("coc").style.display="block";gId("ccc").style.display="none";if (gId("ca").selected){gId("cac").style.display="block";}if (gId("cc").selected){gId("coc").style.display="none";gId("ccc").style.display="block";}if (gId("cn").selected){gId("coc").style.display="none";}}
|
||||
function BTa(){var ih="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Macro</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";for (i=0;i<8;i++){ih+="<tr><td><input name=\"W"+i+"\" id=\"W"+i+"\" type=\"number\" style=\"display:none\"><input id=\"W"+i+"0\" type=\"checkbox\"></td><td><input name=\"H"+i+"\" type=\"number\" min=\"0\" max=\"24\"></td><td><input name=\"N"+i+"\" type=\"number\" min=\"0\" max=\"59\"></td><td><input name=\"T"+i+"\" type=\"number\" min=\"0\" max=\"16\"></td>";for (j=1;j<8;j++) ih+="<td><input id=\"W"+i+j+"\" type=\"checkbox\"></td>";}gId("TMT").innerHTML=ih;}
|
||||
function FC(){for(j=0;j<8;j++){for(i=0;i<8;i++)gId("W"+i+j).checked=gId("W"+i).value>>j&1;}}
|
||||
function Wd(){a=[0,0,0,0,0,0,0,0];for(i=0;i<8;i++){m=1;for(j=0;j<8;j++){a[i]+=gId("W"+i+j).checked*m;m*=2;}gId("W"+i).value=a[i];}}function GetV(){
|
||||
%CSS%%SCSS%</head>
|
||||
<body onload="S()">
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<form id="form_s" name="Sf" method="post" onsubmit="Wd()">
|
||||
<div class="helpB"><button type="button" onclick="H()">?</button></div>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
|
||||
<h2>Time setup</h2>
|
||||
Get time from NTP server: <input type="checkbox" name="NT"><br>
|
||||
Use 24h format: <input type="checkbox" name="CF"><br>
|
||||
Time zone:
|
||||
Time zone:
|
||||
<select name="TZ">
|
||||
<option value="0" selected>GMT(UTC)</option>
|
||||
<option value="1">GMT/BST</option>
|
||||
@@ -329,7 +339,8 @@ Cronixie Display: <input name="CX" maxlength="6"><br>
|
||||
Cronixie Backlight: <input type="checkbox" name="CB"><br>
|
||||
</div>
|
||||
Countdown Mode: <input type="checkbox" name="CE"><br>
|
||||
Countdown Goal: Year: 20 <input name="CY" type="number" min="0" max="99" required> Month: <input name="CI" type="number" min="1" max="12" required> Day: <input name="CD" type="number" min="1" max="31" required><br>
|
||||
Countdown Goal:<br>
|
||||
Year: 20 <input name="CY" type="number" min="0" max="99" required> Month: <input name="CI" type="number" min="1" max="12" required> Day: <input name="CD" type="number" min="1" max="31" required><br>
|
||||
Hour: <input name="CH" type="number" min="0" max="23" required> Minute: <input name="CM" type="number" min="0" max="59" required> Second: <input name="CS" type="number" min="0" max="59" required><br>
|
||||
<h3>Advanced Macros</h3>
|
||||
Define API macros here:<br>
|
||||
@@ -350,45 +361,28 @@ Define API macros here:<br>
|
||||
15: <input name="M15" maxlength="64"><br>
|
||||
16: <input name="M16" maxlength="64"><br><br>
|
||||
<i>Use 0 for the default action instead of a macro</i><br>
|
||||
Boot Macro: <input name="MB" type="number" min="0" max="16" required><br>
|
||||
Alexa On/Off Macros: <input name="A0" type="number" min="0" max="16" required> <input name="A1" type="number" min="0" max="16" required><br>
|
||||
Button Macro: <input name="MP" type="number" min="0" max="16" required> Long Press: <input name="ML" type="number" min="0" max="16" required><br>
|
||||
Countdown-Over Macro: <input name="MC" type="number" min="0" max="16" required><br>
|
||||
Timed-Light-Over Macro: <input name="MN" type="number" min="0" max="16" required><br>
|
||||
Time-Controlled Macros (Hours/Minutes > Macro):<br>
|
||||
<input name="H0" type="number" min="0" max="24"> <input name="N0" type="number" min="0" max="59">
|
||||
> <input name="T0" type="number" min="0" max="16"><br>
|
||||
<input name="H1" type="number" min="0" max="24"> <input name="N1" type="number" min="0" max="59">
|
||||
> <input name="T1" type="number" min="0" max="16"><br>
|
||||
<input name="H2" type="number" min="0" max="24"> <input name="N2" type="number" min="0" max="59">
|
||||
> <input name="T2" type="number" min="0" max="16"><br>
|
||||
<input name="H3" type="number" min="0" max="24"> <input name="N3" type="number" min="0" max="59">
|
||||
> <input name="T3" type="number" min="0" max="16"><br>
|
||||
<input name="H4" type="number" min="0" max="24"> <input name="N4" type="number" min="0" max="59">
|
||||
> <input name="T4" type="number" min="0" max="16"><br>
|
||||
<input name="H5" type="number" min="0" max="24"> <input name="N5" type="number" min="0" max="59">
|
||||
> <input name="T5" type="number" min="0" max="16"><br>
|
||||
<input name="H6" type="number" min="0" max="24"> <input name="N6" type="number" min="0" max="59">
|
||||
> <input name="T6" type="number" min="0" max="16"><br>
|
||||
<input name="H7" type="number" min="0" max="24"> <input name="N7" type="number" min="0" max="59">
|
||||
> <input name="T7" type="number" min="0" max="16"><hr>
|
||||
Boot macro: <input name="MB" type="number" min="0" max="16" required><br>
|
||||
Alexa On/Off macros: <input name="A0" type="number" min="0" max="16" required> <input name="A1" type="number" min="0" max="16" required><br>
|
||||
Button short press macro: <input name="MP" type="number" min="0" max="16" required><br>
|
||||
Long press: <input name="ML" type="number" min="0" max="16" required> Double press: <input name="MD" type="number" min="0" max="16" required><br>
|
||||
Countdown-Over macro: <input name="MC" type="number" min="0" max="16" required><br>
|
||||
Timed-Light-Over macro: <input name="MN" type="number" min="0" max="16" required><br>
|
||||
Time-Controlled macros:<br>
|
||||
<div style="display: inline-block">
|
||||
<table id="TMT">
|
||||
</table></div><hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
)=====";
|
||||
</html>)=====";
|
||||
|
||||
|
||||
//security settings and about
|
||||
const char PAGE_settings_sec0[] PROGMEM = R"=====(
|
||||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
const char PAGE_settings_sec[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><head><meta name="viewport" content="width=500">
|
||||
<title>Misc Settings</title>
|
||||
<script>function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#security-settings");}function B(){window.open("/settings","_self");}function U(){window.open("/update","_self");}function GetV(){var d = document;
|
||||
)=====";
|
||||
|
||||
const char PAGE_settings_sec1[] PROGMEM = R"=====(
|
||||
</head>
|
||||
<script>function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#security-settings");}function B(){window.open("/settings","_self");}function U(){window.open("/update","_self");}function GetV(){var d=document;
|
||||
%CSS%%SCSS%</head>
|
||||
<body onload="GetV()">
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<div class="helpB"><button type="button" onclick="H()">?</button></div>
|
||||
@@ -401,9 +395,6 @@ The password should be changed when OTA is enabled.<br>
|
||||
<b>Disable OTA when not in use, otherwise an attacker can reflash device software!</b><br>
|
||||
<i>Settings on this page are only changable if OTA lock is disabled!</i><br>
|
||||
Deny access to WiFi settings if locked: <input type="checkbox" name="OW"><br><br>
|
||||
Disable recovery AP: <input type="checkbox" name="NA"><br>
|
||||
In case of an error there will be no wireless recovery possible!<br>
|
||||
Completely disables all Access Point functions.<br><br>
|
||||
Factory reset: <input type="checkbox" name="RS"><br>
|
||||
All EEPROM content (settings) will be erased.<br><br>
|
||||
HTTP traffic is unencrypted. An attacker in the same network can intercept form data!
|
||||
@@ -411,27 +402,13 @@ HTTP traffic is unencrypted. An attacker in the same network can intercept form
|
||||
<button type="button" onclick="U()">Manual OTA Update</button><br>
|
||||
Enable ArduinoOTA: <input type="checkbox" name="AO"><br>
|
||||
<h3>About</h3>
|
||||
<a href="https://github.com/Aircoookie/WLED" target="_blank">WLED</a> version 0.8.0<br><br>
|
||||
<b>Contributors:</b><br>
|
||||
StormPie <i>(Mobile HTML UI)</i><br><br>
|
||||
Thank you so much!<br><br>
|
||||
(c) 2016-2018 Christian Schwinne <br>
|
||||
<a href="https://github.com/Aircoookie/WLED" target="_blank">WLED</a> version 0.8.6<br><br>
|
||||
<a href="https://github.com/Aircoookie/WLED/wiki/Contributors-&-About" target="_blank">Contributors, dependencies and special thanks</a><br>
|
||||
A huge thank you to everyone who helped me create WLED!<br><br>
|
||||
(c) 2016-2019 Christian Schwinne <br>
|
||||
<i>Licensed under the MIT license</i><br><br>
|
||||
<b>Uses libraries:</b><br>
|
||||
<i>ESP8266/ESP32 Arduino Core</i><br>
|
||||
<i><a href="https://github.com/svenihoney/NeoPixelBus" target="_blank">NeoPixelBus</a> by Makuna (svenihoney fork)</i><br>
|
||||
<i><a href="https://github.com/FastLED/FastLED/" target="_blank">FastLED</a> library</i><br>
|
||||
<i>(ESP32) <a href="https://github.com/bbx10/WebServer_tng" target="_blank">WebServer_tng</a> by bbx10</i><br>
|
||||
<i><a href="https://github.com/kitesurfer1404/WS2812FX" target="_blank">WS2812FX</a> by kitesurfer1404 (modified)</i><br>
|
||||
<i><a href="https://github.com/JChristensen/Timezone" target="_blank">Timezone</a> library by JChristensen</i><br>
|
||||
<i><a href="https://github.com/blynkkk/blynk-library" target="_blank">Blynk</a> library (compacted)</i><br>
|
||||
<i><a href="https://github.com/forkineye/E131" target="_blank">E1.31</a> library by forkineye (modified)</i><br>
|
||||
<i><a href="https://github.com/knolleary/pubsubclient" target="_blank">PubSubClient</a> by knolleary (modified)</i><br>
|
||||
<i><a href="https://github.com/Aircoookie/Espalexa" target="_blank">Espalexa</a> by Aircoookie (modified)</i><br><br>
|
||||
<i>UI icons by <a href="https://linearicons.com" target="_blank">Linearicons</a> created by <a href="https://perxis.com" target="_blank">Perxis</a>! (CC-BY-SA 4.0)</i> <br><br>
|
||||
Server message: <span class="msg"> Response error! </span><hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save & Reboot</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
)=====";
|
||||
</html>)=====";
|
||||
157
wled00/htmls00.h
157
wled00/htmls00.h
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
95
wled00/ir_codes.h
Normal file
95
wled00/ir_codes.h
Normal file
@@ -0,0 +1,95 @@
|
||||
//Infrared codes
|
||||
|
||||
//Add your custom codes here
|
||||
#define IRCUSTOM_ONOFF 0xA55AEA15 //Pioneer RC-975R "+FAV" button (example)
|
||||
#define IRCUSTOM_MACRO1 0xFFFFFFFF //placeholder, will never be checked for
|
||||
|
||||
//Infrared codes for 24-key remote from http://woodsgood.ca/projects/2015/02/13/rgb-led-strip-controllers-ir-codes/
|
||||
#define IR24_BRIGHTER 0xF700FF
|
||||
#define IR24_DARKER 0xF7807F
|
||||
#define IR24_OFF 0xF740BF
|
||||
#define IR24_ON 0xF7C03F
|
||||
#define IR24_RED 0xF720DF
|
||||
#define IR24_REDDISH 0xF710EF
|
||||
#define IR24_ORANGE 0xF730CF
|
||||
#define IR24_YELLOWISH 0xF708F7
|
||||
#define IR24_YELLOW 0xF728D7
|
||||
#define IR24_GREEN 0xF7A05F
|
||||
#define IR24_GREENISH 0xF7906F
|
||||
#define IR24_TURQUOISE 0xF7B04F
|
||||
#define IR24_CYAN 0xF78877
|
||||
#define IR24_AQUA 0xF7A857
|
||||
#define IR24_BLUE 0xF7609F
|
||||
#define IR24_DEEPBLUE 0xF750AF
|
||||
#define IR24_PURPLE 0xF7708F
|
||||
#define IR24_MAGENTA 0xF748B7
|
||||
#define IR24_PINK 0xF76897
|
||||
#define IR24_WHITE 0xF7E01F
|
||||
#define IR24_FLASH 0xF7D02F
|
||||
#define IR24_STROBE 0xF7F00F
|
||||
#define IR24_FADE 0xF7C837
|
||||
#define IR24_SMOOTH 0xF7E817
|
||||
|
||||
/* 44-key defs, to be done later
|
||||
#define IR44_BPlus 0xFF3AC5 //
|
||||
#define IR44_BMinus 0xFFBA45 //
|
||||
#define IR44_ON 0xFF827D //
|
||||
#define IR44_OFF 0xFF02FD //
|
||||
#define IR44_R 0xFF1AE5 //
|
||||
#define IR44_G 0xFF9A65 //
|
||||
#define IR44_B 0xFFA25D //
|
||||
#define IR44_W 0xFF22DD //
|
||||
#define IR44_B1 0xFF2AD5 //
|
||||
#define IR44_B2 0xFFAA55 //
|
||||
#define IR44_B3 0xFF926D //
|
||||
#define IR44_B4 0xFF12ED //
|
||||
#define IR44_B5 0xFF0AF5 //
|
||||
#define IR44_B6 0xFF8A75 //
|
||||
#define IR44_B7 0xFFB24D //
|
||||
#define IR44_B8 0xFF32CD //
|
||||
#define IR44_B9 0xFF38C7 //
|
||||
#define IR44_B10 0xFFB847 //
|
||||
#define IR44_B11 0xFF7887 //
|
||||
#define IR44_B12 0xFFF807 //
|
||||
#define IR44_B13 0xFF18E7 //
|
||||
#define IR44_B14 0xFF9867 //
|
||||
#define IR44_B15 0xFF58A7 //
|
||||
#define IR44_B16 0xFFD827 //
|
||||
#define IR44_UPR 0xFF28D7 //
|
||||
#define IR44_UPG 0xFFA857 //
|
||||
#define IR44_UPB 0xFF6897 //
|
||||
#define IR44_QUICK 0xFFE817 //
|
||||
#define IR44_DOWNR 0xFF08F7 //
|
||||
#define IR44_DOWNG 0xFF8877 //
|
||||
#define IR44_DOWNB 0xFF48B7 //
|
||||
#define IR44_SLOW 0xFFC837 //
|
||||
#define IR44_DIY1 0xFF30CF //
|
||||
#define IR44_DIY2 0xFFB04F //
|
||||
#define IR44_DIY3 0xFF708F //
|
||||
#define IR44_AUTO 0xFFF00F //
|
||||
#define IR44_DIY4 0xFF10EF //
|
||||
#define IR44_DIY5 0xFF906F //
|
||||
#define IR44_DIY6 0xFF50AF //
|
||||
#define IR44_FLASH 0xFFD02F //
|
||||
#define IR44_JUMP3 0xFF20DF //
|
||||
#define IR44_JUMP7 0xFFA05F //
|
||||
#define IR44_FADE3 0xFF609F //
|
||||
#define IR44_FADE7 0xFFE01F //
|
||||
*/
|
||||
|
||||
#define COLOR_RED 0xFF0000
|
||||
#define COLOR_REDDISH 0xFF7800
|
||||
#define COLOR_ORANGE 0xFFA000
|
||||
#define COLOR_YELLOWISH 0xFFC800
|
||||
#define COLOR_YELLOW 0xFFFF00
|
||||
#define COLOR_GREEN 0x00FF00
|
||||
#define COLOR_GREENISH 0x00FF78
|
||||
#define COLOR_TURQUOISE 0x00FFA0
|
||||
#define COLOR_CYAN 0x00FFDC
|
||||
#define COLOR_AQUA 0x00C8FF
|
||||
#define COLOR_BLUE 0x00A0FF
|
||||
#define COLOR_DEEPBLUE 0x0000FF
|
||||
#define COLOR_PURPLE 0xAA00FF
|
||||
#define COLOR_MAGENTA 0xFF00DC
|
||||
#define COLOR_PINK 0xFF00A0
|
||||
#define COLOR_WHITE 0xFFFFDC
|
||||
@@ -500,6 +500,71 @@ DEFINE_GRADIENT_PALETTE( Orange_Teal_gp ) {
|
||||
200, 255, 72, 0,
|
||||
255, 255, 72, 0};
|
||||
|
||||
//Custom palette by Aircoookie
|
||||
|
||||
DEFINE_GRADIENT_PALETTE( Tiamat_gp ) {
|
||||
0, 1, 2, 14, //gc
|
||||
33, 2, 5, 35, //gc from 47, 61,126
|
||||
100, 13,135, 92, //gc from 88,242,247
|
||||
120, 43,255,193, //gc from 135,255,253
|
||||
140, 247, 7,249, //gc from 252, 69,253
|
||||
160, 193, 17,208, //gc from 231, 96,237
|
||||
180, 39,255,154, //gc from 130, 77,213
|
||||
200, 4,213,236, //gc from 57,122,248
|
||||
220, 39,252,135, //gc from 177,254,255
|
||||
240, 193,213,253, //gc from 203,239,253
|
||||
255, 255,249,255};
|
||||
|
||||
//Custom palette by Aircoookie
|
||||
|
||||
DEFINE_GRADIENT_PALETTE( April_Night_gp ) {
|
||||
0, 1, 5, 45, //deep blue
|
||||
10, 1, 5, 45,
|
||||
25, 5,169,175, //light blue
|
||||
40, 1, 5, 45,
|
||||
61, 1, 5, 45,
|
||||
76, 45,175, 31, //green
|
||||
91, 1, 5, 45,
|
||||
112, 1, 5, 45,
|
||||
127, 249,150, 5, //yellow
|
||||
143, 1, 5, 45,
|
||||
162, 1, 5, 45,
|
||||
178, 255, 92, 0, //pastel orange
|
||||
193, 1, 5, 45,
|
||||
214, 1, 5, 45,
|
||||
229, 223, 45, 72, //pink
|
||||
244, 1, 5, 45,
|
||||
255, 1, 5, 45};
|
||||
|
||||
DEFINE_GRADIENT_PALETTE( Orangery_gp ) {
|
||||
0, 255, 95, 23,
|
||||
30, 255, 82, 0,
|
||||
60, 223, 13, 8,
|
||||
90, 144, 44, 2,
|
||||
120, 255,110, 17,
|
||||
150, 255, 69, 0,
|
||||
180, 158, 13, 11,
|
||||
210, 241, 82, 17,
|
||||
255, 213, 37, 4};
|
||||
|
||||
//inspired by Mark Kriegsman https://gist.github.com/kriegsman/756ea6dcae8e30845b5a
|
||||
DEFINE_GRADIENT_PALETTE( C9_gp ) {
|
||||
0, 184, 4, 0, //red
|
||||
60, 184, 4, 0,
|
||||
65, 144, 44, 2, //amber
|
||||
125, 144, 44, 2,
|
||||
130, 4, 96, 2, //green
|
||||
190, 4, 96, 2,
|
||||
195, 7, 7, 88, //blue
|
||||
255, 7, 7, 88};
|
||||
|
||||
DEFINE_GRADIENT_PALETTE( Sakura_gp ) {
|
||||
0, 196, 19, 10,
|
||||
65, 255, 69, 45,
|
||||
130, 223, 45, 72,
|
||||
195, 255, 82,103,
|
||||
255, 223, 13, 17};
|
||||
|
||||
|
||||
// Single array of defined cpt-city color palettes.
|
||||
// This will let us programmatically choose one based on
|
||||
@@ -543,8 +608,13 @@ const TProgmemRGBGradientPalettePtr gGradientPalettes[] = {
|
||||
BlacK_Magenta_Red_gp, //41-28 Magred
|
||||
BlacK_Red_Magenta_Yellow_gp, //42-29 Yelmag
|
||||
Blue_Cyan_Yellow_gp, //43-30 Yelblu
|
||||
Orange_Teal_gp //44-31 Orange & Teal
|
||||
};
|
||||
Orange_Teal_gp, //44-31 Orange & Teal
|
||||
Tiamat_gp, //45-32 Tiamat
|
||||
April_Night_gp, //46-33 April Night
|
||||
Orangery_gp, //47-34 Orangery
|
||||
C9_gp, //48-35 C9
|
||||
Sakura_gp, //49-36 Sakura
|
||||
};
|
||||
|
||||
|
||||
// Count of how many cpt-city gradients are defined:
|
||||
@@ -552,4 +622,3 @@ const uint8_t gGradientPaletteCount =
|
||||
sizeof( gGradientPalettes) / sizeof( TProgmemRGBGradientPalettePtr );
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
877
wled00/src/dependencies/async-mqtt-client/AsyncMqttClient.cpp
Normal file
877
wled00/src/dependencies/async-mqtt-client/AsyncMqttClient.cpp
Normal file
@@ -0,0 +1,877 @@
|
||||
#include "AsyncMqttClient.hpp"
|
||||
|
||||
AsyncMqttClient::AsyncMqttClient()
|
||||
: _connected(false)
|
||||
, _connectPacketNotEnoughSpace(false)
|
||||
, _disconnectFlagged(false)
|
||||
, _tlsBadFingerprint(false)
|
||||
, _lastClientActivity(0)
|
||||
, _lastServerActivity(0)
|
||||
, _lastPingRequestTime(0)
|
||||
, _host(nullptr)
|
||||
, _useIp(false)
|
||||
#if ASYNC_TCP_SSL_ENABLED
|
||||
, _secure(false)
|
||||
#endif
|
||||
, _port(0)
|
||||
, _keepAlive(15)
|
||||
, _cleanSession(true)
|
||||
, _clientId(nullptr)
|
||||
, _username(nullptr)
|
||||
, _password(nullptr)
|
||||
, _willTopic(nullptr)
|
||||
, _willPayload(nullptr)
|
||||
, _willPayloadLength(0)
|
||||
, _willQos(0)
|
||||
, _willRetain(false)
|
||||
, _parsingInformation { .bufferState = AsyncMqttClientInternals::BufferState::NONE }
|
||||
, _currentParsedPacket(nullptr)
|
||||
, _remainingLengthBufferPosition(0)
|
||||
, _nextPacketId(1) {
|
||||
_client.onConnect([](void* obj, AsyncClient* c) { (static_cast<AsyncMqttClient*>(obj))->_onConnect(c); }, this);
|
||||
_client.onDisconnect([](void* obj, AsyncClient* c) { (static_cast<AsyncMqttClient*>(obj))->_onDisconnect(c); }, this);
|
||||
_client.onError([](void* obj, AsyncClient* c, int8_t error) { (static_cast<AsyncMqttClient*>(obj))->_onError(c, error); }, this);
|
||||
_client.onTimeout([](void* obj, AsyncClient* c, uint32_t time) { (static_cast<AsyncMqttClient*>(obj))->_onTimeout(c, time); }, this);
|
||||
_client.onAck([](void* obj, AsyncClient* c, size_t len, uint32_t time) { (static_cast<AsyncMqttClient*>(obj))->_onAck(c, len, time); }, this);
|
||||
_client.onData([](void* obj, AsyncClient* c, void* data, size_t len) { (static_cast<AsyncMqttClient*>(obj))->_onData(c, static_cast<char*>(data), len); }, this);
|
||||
_client.onPoll([](void* obj, AsyncClient* c) { (static_cast<AsyncMqttClient*>(obj))->_onPoll(c); }, this);
|
||||
|
||||
#ifdef ESP32
|
||||
sprintf(_generatedClientId, "esp32%06x", ESP.getEfuseMac());
|
||||
_xSemaphore = xSemaphoreCreateMutex();
|
||||
#elif defined(ESP8266)
|
||||
sprintf(_generatedClientId, "esp8266%06x", ESP.getChipId());
|
||||
#endif
|
||||
_clientId = _generatedClientId;
|
||||
|
||||
setMaxTopicLength(128);
|
||||
}
|
||||
|
||||
AsyncMqttClient::~AsyncMqttClient() {
|
||||
delete _currentParsedPacket;
|
||||
delete[] _parsingInformation.topicBuffer;
|
||||
#ifdef ESP32
|
||||
vSemaphoreDelete(_xSemaphore);
|
||||
#endif
|
||||
}
|
||||
|
||||
AsyncMqttClient& AsyncMqttClient::setKeepAlive(uint16_t keepAlive) {
|
||||
_keepAlive = keepAlive;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AsyncMqttClient& AsyncMqttClient::setClientId(const char* clientId) {
|
||||
_clientId = clientId;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AsyncMqttClient& AsyncMqttClient::setCleanSession(bool cleanSession) {
|
||||
_cleanSession = cleanSession;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AsyncMqttClient& AsyncMqttClient::setMaxTopicLength(uint16_t maxTopicLength) {
|
||||
_parsingInformation.maxTopicLength = maxTopicLength;
|
||||
delete[] _parsingInformation.topicBuffer;
|
||||
_parsingInformation.topicBuffer = new char[maxTopicLength + 1];
|
||||
return *this;
|
||||
}
|
||||
|
||||
AsyncMqttClient& AsyncMqttClient::setCredentials(const char* username, const char* password) {
|
||||
_username = username;
|
||||
_password = password;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AsyncMqttClient& AsyncMqttClient::setWill(const char* topic, uint8_t qos, bool retain, const char* payload, size_t length) {
|
||||
_willTopic = topic;
|
||||
_willQos = qos;
|
||||
_willRetain = retain;
|
||||
_willPayload = payload;
|
||||
_willPayloadLength = length;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AsyncMqttClient& AsyncMqttClient::setServer(IPAddress ip, uint16_t port) {
|
||||
_useIp = true;
|
||||
_ip = ip;
|
||||
_port = port;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AsyncMqttClient& AsyncMqttClient::setServer(const char* host, uint16_t port) {
|
||||
_useIp = false;
|
||||
_host = host;
|
||||
_port = port;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if ASYNC_TCP_SSL_ENABLED
|
||||
AsyncMqttClient& AsyncMqttClient::setSecure(bool secure) {
|
||||
_secure = secure;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AsyncMqttClient& AsyncMqttClient::addServerFingerprint(const uint8_t* fingerprint) {
|
||||
std::array<uint8_t, SHA1_SIZE> newFingerprint;
|
||||
memcpy(newFingerprint.data(), fingerprint, SHA1_SIZE);
|
||||
_secureServerFingerprints.push_back(newFingerprint);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
AsyncMqttClient& AsyncMqttClient::onConnect(AsyncMqttClientInternals::OnConnectUserCallback callback) {
|
||||
_onConnectUserCallbacks.push_back(callback);
|
||||
return *this;
|
||||
}
|
||||
|
||||
AsyncMqttClient& AsyncMqttClient::onDisconnect(AsyncMqttClientInternals::OnDisconnectUserCallback callback) {
|
||||
_onDisconnectUserCallbacks.push_back(callback);
|
||||
return *this;
|
||||
}
|
||||
|
||||
AsyncMqttClient& AsyncMqttClient::onSubscribe(AsyncMqttClientInternals::OnSubscribeUserCallback callback) {
|
||||
_onSubscribeUserCallbacks.push_back(callback);
|
||||
return *this;
|
||||
}
|
||||
|
||||
AsyncMqttClient& AsyncMqttClient::onUnsubscribe(AsyncMqttClientInternals::OnUnsubscribeUserCallback callback) {
|
||||
_onUnsubscribeUserCallbacks.push_back(callback);
|
||||
return *this;
|
||||
}
|
||||
|
||||
AsyncMqttClient& AsyncMqttClient::onMessage(AsyncMqttClientInternals::OnMessageUserCallback callback) {
|
||||
_onMessageUserCallbacks.push_back(callback);
|
||||
return *this;
|
||||
}
|
||||
|
||||
AsyncMqttClient& AsyncMqttClient::onPublish(AsyncMqttClientInternals::OnPublishUserCallback callback) {
|
||||
_onPublishUserCallbacks.push_back(callback);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void AsyncMqttClient::_freeCurrentParsedPacket() {
|
||||
delete _currentParsedPacket;
|
||||
_currentParsedPacket = nullptr;
|
||||
}
|
||||
|
||||
void AsyncMqttClient::_clear() {
|
||||
_lastPingRequestTime = 0;
|
||||
_connected = false;
|
||||
_disconnectFlagged = false;
|
||||
_connectPacketNotEnoughSpace = false;
|
||||
_tlsBadFingerprint = false;
|
||||
_freeCurrentParsedPacket();
|
||||
|
||||
_pendingPubRels.clear();
|
||||
_pendingPubRels.shrink_to_fit();
|
||||
|
||||
_toSendAcks.clear();
|
||||
_toSendAcks.shrink_to_fit();
|
||||
|
||||
_nextPacketId = 1;
|
||||
_parsingInformation.bufferState = AsyncMqttClientInternals::BufferState::NONE;
|
||||
}
|
||||
|
||||
/* TCP */
|
||||
void AsyncMqttClient::_onConnect(AsyncClient* client) {
|
||||
(void)client;
|
||||
|
||||
#if ASYNC_TCP_SSL_ENABLED
|
||||
if (_secure && _secureServerFingerprints.size() > 0) {
|
||||
SSL* clientSsl = _client.getSSL();
|
||||
|
||||
bool sslFoundFingerprint = false;
|
||||
for (std::array<uint8_t, SHA1_SIZE> fingerprint : _secureServerFingerprints) {
|
||||
if (ssl_match_fingerprint(clientSsl, fingerprint.data()) == SSL_OK) {
|
||||
sslFoundFingerprint = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sslFoundFingerprint) {
|
||||
_tlsBadFingerprint = true;
|
||||
_client.close(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
char fixedHeader[5];
|
||||
fixedHeader[0] = AsyncMqttClientInternals::PacketType.CONNECT;
|
||||
fixedHeader[0] = fixedHeader[0] << 4;
|
||||
fixedHeader[0] = fixedHeader[0] | AsyncMqttClientInternals::HeaderFlag.CONNECT_RESERVED;
|
||||
|
||||
uint16_t protocolNameLength = 4;
|
||||
char protocolNameLengthBytes[2];
|
||||
protocolNameLengthBytes[0] = protocolNameLength >> 8;
|
||||
protocolNameLengthBytes[1] = protocolNameLength & 0xFF;
|
||||
|
||||
char protocolLevel[1];
|
||||
protocolLevel[0] = 0x04;
|
||||
|
||||
char connectFlags[1];
|
||||
connectFlags[0] = 0;
|
||||
if (_cleanSession) connectFlags[0] |= AsyncMqttClientInternals::ConnectFlag.CLEAN_SESSION;
|
||||
if (_username != nullptr) connectFlags[0] |= AsyncMqttClientInternals::ConnectFlag.USERNAME;
|
||||
if (_password != nullptr) connectFlags[0] |= AsyncMqttClientInternals::ConnectFlag.PASSWORD;
|
||||
if (_willTopic != nullptr) {
|
||||
connectFlags[0] |= AsyncMqttClientInternals::ConnectFlag.WILL;
|
||||
if (_willRetain) connectFlags[0] |= AsyncMqttClientInternals::ConnectFlag.WILL_RETAIN;
|
||||
switch (_willQos) {
|
||||
case 0:
|
||||
connectFlags[0] |= AsyncMqttClientInternals::ConnectFlag.WILL_QOS0;
|
||||
break;
|
||||
case 1:
|
||||
connectFlags[0] |= AsyncMqttClientInternals::ConnectFlag.WILL_QOS1;
|
||||
break;
|
||||
case 2:
|
||||
connectFlags[0] |= AsyncMqttClientInternals::ConnectFlag.WILL_QOS2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
char keepAliveBytes[2];
|
||||
keepAliveBytes[0] = _keepAlive >> 8;
|
||||
keepAliveBytes[1] = _keepAlive & 0xFF;
|
||||
|
||||
uint16_t clientIdLength = strlen(_clientId);
|
||||
char clientIdLengthBytes[2];
|
||||
clientIdLengthBytes[0] = clientIdLength >> 8;
|
||||
clientIdLengthBytes[1] = clientIdLength & 0xFF;
|
||||
|
||||
// Optional fields
|
||||
uint16_t willTopicLength = 0;
|
||||
char willTopicLengthBytes[2];
|
||||
uint16_t willPayloadLength = _willPayloadLength;
|
||||
char willPayloadLengthBytes[2];
|
||||
if (_willTopic != nullptr) {
|
||||
willTopicLength = strlen(_willTopic);
|
||||
willTopicLengthBytes[0] = willTopicLength >> 8;
|
||||
willTopicLengthBytes[1] = willTopicLength & 0xFF;
|
||||
|
||||
if (_willPayload != nullptr && willPayloadLength == 0) willPayloadLength = strlen(_willPayload);
|
||||
|
||||
willPayloadLengthBytes[0] = willPayloadLength >> 8;
|
||||
willPayloadLengthBytes[1] = willPayloadLength & 0xFF;
|
||||
}
|
||||
|
||||
uint16_t usernameLength = 0;
|
||||
char usernameLengthBytes[2];
|
||||
if (_username != nullptr) {
|
||||
usernameLength = strlen(_username);
|
||||
usernameLengthBytes[0] = usernameLength >> 8;
|
||||
usernameLengthBytes[1] = usernameLength & 0xFF;
|
||||
}
|
||||
|
||||
uint16_t passwordLength = 0;
|
||||
char passwordLengthBytes[2];
|
||||
if (_password != nullptr) {
|
||||
passwordLength = strlen(_password);
|
||||
passwordLengthBytes[0] = passwordLength >> 8;
|
||||
passwordLengthBytes[1] = passwordLength & 0xFF;
|
||||
}
|
||||
|
||||
uint32_t remainingLength = 2 + protocolNameLength + 1 + 1 + 2 + 2 + clientIdLength; // always present
|
||||
if (_willTopic != nullptr) remainingLength += 2 + willTopicLength + 2 + willPayloadLength;
|
||||
if (_username != nullptr) remainingLength += 2 + usernameLength;
|
||||
if (_password != nullptr) remainingLength += 2 + passwordLength;
|
||||
uint8_t remainingLengthLength = AsyncMqttClientInternals::Helpers::encodeRemainingLength(remainingLength, fixedHeader + 1);
|
||||
|
||||
uint32_t neededSpace = 1 + remainingLengthLength;
|
||||
neededSpace += 2;
|
||||
neededSpace += protocolNameLength;
|
||||
neededSpace += 1;
|
||||
neededSpace += 1;
|
||||
neededSpace += 2;
|
||||
neededSpace += 2;
|
||||
neededSpace += clientIdLength;
|
||||
if (_willTopic != nullptr) {
|
||||
neededSpace += 2;
|
||||
neededSpace += willTopicLength;
|
||||
|
||||
neededSpace += 2;
|
||||
if (_willPayload != nullptr) neededSpace += willPayloadLength;
|
||||
}
|
||||
if (_username != nullptr) {
|
||||
neededSpace += 2;
|
||||
neededSpace += usernameLength;
|
||||
}
|
||||
if (_password != nullptr) {
|
||||
neededSpace += 2;
|
||||
neededSpace += passwordLength;
|
||||
}
|
||||
|
||||
SEMAPHORE_TAKE();
|
||||
if (_client.space() < neededSpace) {
|
||||
_connectPacketNotEnoughSpace = true;
|
||||
_client.close(true);
|
||||
SEMAPHORE_GIVE();
|
||||
return;
|
||||
}
|
||||
|
||||
_client.add(fixedHeader, 1 + remainingLengthLength);
|
||||
_client.add(protocolNameLengthBytes, 2);
|
||||
_client.add("MQTT", protocolNameLength);
|
||||
_client.add(protocolLevel, 1);
|
||||
_client.add(connectFlags, 1);
|
||||
_client.add(keepAliveBytes, 2);
|
||||
_client.add(clientIdLengthBytes, 2);
|
||||
_client.add(_clientId, clientIdLength);
|
||||
if (_willTopic != nullptr) {
|
||||
_client.add(willTopicLengthBytes, 2);
|
||||
_client.add(_willTopic, willTopicLength);
|
||||
|
||||
_client.add(willPayloadLengthBytes, 2);
|
||||
if (_willPayload != nullptr) _client.add(_willPayload, willPayloadLength);
|
||||
}
|
||||
if (_username != nullptr) {
|
||||
_client.add(usernameLengthBytes, 2);
|
||||
_client.add(_username, usernameLength);
|
||||
}
|
||||
if (_password != nullptr) {
|
||||
_client.add(passwordLengthBytes, 2);
|
||||
_client.add(_password, passwordLength);
|
||||
}
|
||||
_client.send();
|
||||
_lastClientActivity = millis();
|
||||
SEMAPHORE_GIVE();
|
||||
}
|
||||
|
||||
void AsyncMqttClient::_onDisconnect(AsyncClient* client) {
|
||||
(void)client;
|
||||
if (!_disconnectFlagged) {
|
||||
AsyncMqttClientDisconnectReason reason;
|
||||
|
||||
if (_connectPacketNotEnoughSpace) {
|
||||
reason = AsyncMqttClientDisconnectReason::ESP8266_NOT_ENOUGH_SPACE;
|
||||
} else if (_tlsBadFingerprint) {
|
||||
reason = AsyncMqttClientDisconnectReason::TLS_BAD_FINGERPRINT;
|
||||
} else {
|
||||
reason = AsyncMqttClientDisconnectReason::TCP_DISCONNECTED;
|
||||
}
|
||||
for (auto callback : _onDisconnectUserCallbacks) callback(reason);
|
||||
}
|
||||
_clear();
|
||||
}
|
||||
|
||||
void AsyncMqttClient::_onError(AsyncClient* client, int8_t error) {
|
||||
(void)client;
|
||||
(void)error;
|
||||
// _onDisconnect called anyway
|
||||
}
|
||||
|
||||
void AsyncMqttClient::_onTimeout(AsyncClient* client, uint32_t time) {
|
||||
(void)client;
|
||||
(void)time;
|
||||
// disconnection will be handled by ping/pong management
|
||||
}
|
||||
|
||||
void AsyncMqttClient::_onAck(AsyncClient* client, size_t len, uint32_t time) {
|
||||
(void)client;
|
||||
(void)len;
|
||||
(void)time;
|
||||
}
|
||||
|
||||
void AsyncMqttClient::_onData(AsyncClient* client, char* data, size_t len) {
|
||||
(void)client;
|
||||
size_t currentBytePosition = 0;
|
||||
char currentByte;
|
||||
do {
|
||||
switch (_parsingInformation.bufferState) {
|
||||
case AsyncMqttClientInternals::BufferState::NONE:
|
||||
currentByte = data[currentBytePosition++];
|
||||
_parsingInformation.packetType = currentByte >> 4;
|
||||
_parsingInformation.packetFlags = (currentByte << 4) >> 4;
|
||||
_parsingInformation.bufferState = AsyncMqttClientInternals::BufferState::REMAINING_LENGTH;
|
||||
_lastServerActivity = millis();
|
||||
switch (_parsingInformation.packetType) {
|
||||
case AsyncMqttClientInternals::PacketType.CONNACK:
|
||||
_currentParsedPacket = new AsyncMqttClientInternals::ConnAckPacket(&_parsingInformation, std::bind(&AsyncMqttClient::_onConnAck, this, std::placeholders::_1, std::placeholders::_2));
|
||||
break;
|
||||
case AsyncMqttClientInternals::PacketType.PINGRESP:
|
||||
_currentParsedPacket = new AsyncMqttClientInternals::PingRespPacket(&_parsingInformation, std::bind(&AsyncMqttClient::_onPingResp, this));
|
||||
break;
|
||||
case AsyncMqttClientInternals::PacketType.SUBACK:
|
||||
_currentParsedPacket = new AsyncMqttClientInternals::SubAckPacket(&_parsingInformation, std::bind(&AsyncMqttClient::_onSubAck, this, std::placeholders::_1, std::placeholders::_2));
|
||||
break;
|
||||
case AsyncMqttClientInternals::PacketType.UNSUBACK:
|
||||
_currentParsedPacket = new AsyncMqttClientInternals::UnsubAckPacket(&_parsingInformation, std::bind(&AsyncMqttClient::_onUnsubAck, this, std::placeholders::_1));
|
||||
break;
|
||||
case AsyncMqttClientInternals::PacketType.PUBLISH:
|
||||
_currentParsedPacket = new AsyncMqttClientInternals::PublishPacket(&_parsingInformation, std::bind(&AsyncMqttClient::_onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6, std::placeholders::_7, std::placeholders::_8, std::placeholders::_9), std::bind(&AsyncMqttClient::_onPublish, this, std::placeholders::_1, std::placeholders::_2));
|
||||
break;
|
||||
case AsyncMqttClientInternals::PacketType.PUBREL:
|
||||
_currentParsedPacket = new AsyncMqttClientInternals::PubRelPacket(&_parsingInformation, std::bind(&AsyncMqttClient::_onPubRel, this, std::placeholders::_1));
|
||||
break;
|
||||
case AsyncMqttClientInternals::PacketType.PUBACK:
|
||||
_currentParsedPacket = new AsyncMqttClientInternals::PubAckPacket(&_parsingInformation, std::bind(&AsyncMqttClient::_onPubAck, this, std::placeholders::_1));
|
||||
break;
|
||||
case AsyncMqttClientInternals::PacketType.PUBREC:
|
||||
_currentParsedPacket = new AsyncMqttClientInternals::PubRecPacket(&_parsingInformation, std::bind(&AsyncMqttClient::_onPubRec, this, std::placeholders::_1));
|
||||
break;
|
||||
case AsyncMqttClientInternals::PacketType.PUBCOMP:
|
||||
_currentParsedPacket = new AsyncMqttClientInternals::PubCompPacket(&_parsingInformation, std::bind(&AsyncMqttClient::_onPubComp, this, std::placeholders::_1));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AsyncMqttClientInternals::BufferState::REMAINING_LENGTH:
|
||||
currentByte = data[currentBytePosition++];
|
||||
_remainingLengthBuffer[_remainingLengthBufferPosition++] = currentByte;
|
||||
if (currentByte >> 7 == 0) {
|
||||
_parsingInformation.remainingLength = AsyncMqttClientInternals::Helpers::decodeRemainingLength(_remainingLengthBuffer);
|
||||
_remainingLengthBufferPosition = 0;
|
||||
if (_parsingInformation.remainingLength > 0) {
|
||||
_parsingInformation.bufferState = AsyncMqttClientInternals::BufferState::VARIABLE_HEADER;
|
||||
} else {
|
||||
// PINGRESP is a special case where it has no variable header, so the packet ends right here
|
||||
_parsingInformation.bufferState = AsyncMqttClientInternals::BufferState::NONE;
|
||||
_onPingResp();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AsyncMqttClientInternals::BufferState::VARIABLE_HEADER:
|
||||
_currentParsedPacket->parseVariableHeader(data, len, ¤tBytePosition);
|
||||
break;
|
||||
case AsyncMqttClientInternals::BufferState::PAYLOAD:
|
||||
_currentParsedPacket->parsePayload(data, len, ¤tBytePosition);
|
||||
break;
|
||||
default:
|
||||
currentBytePosition = len;
|
||||
}
|
||||
} while (currentBytePosition != len);
|
||||
}
|
||||
|
||||
void AsyncMqttClient::_onPoll(AsyncClient* client) {
|
||||
if (!_connected) return;
|
||||
|
||||
// if there is too much time the client has sent a ping request without a response, disconnect client to avoid half open connections
|
||||
if (_lastPingRequestTime != 0 && (millis() - _lastPingRequestTime) >= (_keepAlive * 1000 * 2)) {
|
||||
disconnect();
|
||||
return;
|
||||
// send ping to ensure the server will receive at least one message inside keepalive window
|
||||
} else if (_lastPingRequestTime == 0 && (millis() - _lastClientActivity) >= (_keepAlive * 1000 * 0.7)) {
|
||||
_sendPing();
|
||||
|
||||
// send ping to verify if the server is still there (ensure this is not a half connection)
|
||||
} else if (_connected && _lastPingRequestTime == 0 && (millis() - _lastServerActivity) >= (_keepAlive * 1000 * 0.7)) {
|
||||
_sendPing();
|
||||
}
|
||||
|
||||
// handle to send ack packets
|
||||
|
||||
_sendAcks();
|
||||
|
||||
// handle disconnect
|
||||
|
||||
if (_disconnectFlagged) {
|
||||
_sendDisconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/* MQTT */
|
||||
void AsyncMqttClient::_onPingResp() {
|
||||
_freeCurrentParsedPacket();
|
||||
_lastPingRequestTime = 0;
|
||||
}
|
||||
|
||||
void AsyncMqttClient::_onConnAck(bool sessionPresent, uint8_t connectReturnCode) {
|
||||
(void)sessionPresent;
|
||||
_freeCurrentParsedPacket();
|
||||
|
||||
if (connectReturnCode == 0) {
|
||||
_connected = true;
|
||||
for (auto callback : _onConnectUserCallbacks) callback(sessionPresent);
|
||||
} else {
|
||||
for (auto callback : _onDisconnectUserCallbacks) callback(static_cast<AsyncMqttClientDisconnectReason>(connectReturnCode));
|
||||
_disconnectFlagged = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncMqttClient::_onSubAck(uint16_t packetId, char status) {
|
||||
_freeCurrentParsedPacket();
|
||||
|
||||
for (auto callback : _onSubscribeUserCallbacks) callback(packetId, status);
|
||||
}
|
||||
|
||||
void AsyncMqttClient::_onUnsubAck(uint16_t packetId) {
|
||||
_freeCurrentParsedPacket();
|
||||
|
||||
for (auto callback : _onUnsubscribeUserCallbacks) callback(packetId);
|
||||
}
|
||||
|
||||
void AsyncMqttClient::_onMessage(char* topic, char* payload, uint8_t qos, bool dup, bool retain, size_t len, size_t index, size_t total, uint16_t packetId) {
|
||||
bool notifyPublish = true;
|
||||
|
||||
if (qos == 2) {
|
||||
for (AsyncMqttClientInternals::PendingPubRel pendingPubRel : _pendingPubRels) {
|
||||
if (pendingPubRel.packetId == packetId) {
|
||||
notifyPublish = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (notifyPublish) {
|
||||
AsyncMqttClientMessageProperties properties;
|
||||
properties.qos = qos;
|
||||
properties.dup = dup;
|
||||
properties.retain = retain;
|
||||
|
||||
for (auto callback : _onMessageUserCallbacks) callback(topic, payload, properties, len, index, total);
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncMqttClient::_onPublish(uint16_t packetId, uint8_t qos) {
|
||||
AsyncMqttClientInternals::PendingAck pendingAck;
|
||||
|
||||
if (qos == 1) {
|
||||
pendingAck.packetType = AsyncMqttClientInternals::PacketType.PUBACK;
|
||||
pendingAck.headerFlag = AsyncMqttClientInternals::HeaderFlag.PUBACK_RESERVED;
|
||||
pendingAck.packetId = packetId;
|
||||
_toSendAcks.push_back(pendingAck);
|
||||
} else if (qos == 2) {
|
||||
pendingAck.packetType = AsyncMqttClientInternals::PacketType.PUBREC;
|
||||
pendingAck.headerFlag = AsyncMqttClientInternals::HeaderFlag.PUBREC_RESERVED;
|
||||
pendingAck.packetId = packetId;
|
||||
_toSendAcks.push_back(pendingAck);
|
||||
|
||||
bool pubRelAwaiting = false;
|
||||
for (AsyncMqttClientInternals::PendingPubRel pendingPubRel : _pendingPubRels) {
|
||||
if (pendingPubRel.packetId == packetId) {
|
||||
pubRelAwaiting = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pubRelAwaiting) {
|
||||
AsyncMqttClientInternals::PendingPubRel pendingPubRel;
|
||||
pendingPubRel.packetId = packetId;
|
||||
_pendingPubRels.push_back(pendingPubRel);
|
||||
}
|
||||
|
||||
_sendAcks();
|
||||
}
|
||||
|
||||
_freeCurrentParsedPacket();
|
||||
}
|
||||
|
||||
void AsyncMqttClient::_onPubRel(uint16_t packetId) {
|
||||
_freeCurrentParsedPacket();
|
||||
|
||||
AsyncMqttClientInternals::PendingAck pendingAck;
|
||||
pendingAck.packetType = AsyncMqttClientInternals::PacketType.PUBCOMP;
|
||||
pendingAck.headerFlag = AsyncMqttClientInternals::HeaderFlag.PUBCOMP_RESERVED;
|
||||
pendingAck.packetId = packetId;
|
||||
_toSendAcks.push_back(pendingAck);
|
||||
|
||||
for (size_t i = 0; i < _pendingPubRels.size(); i++) {
|
||||
if (_pendingPubRels[i].packetId == packetId) {
|
||||
_pendingPubRels.erase(_pendingPubRels.begin() + i);
|
||||
_pendingPubRels.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
_sendAcks();
|
||||
}
|
||||
|
||||
void AsyncMqttClient::_onPubAck(uint16_t packetId) {
|
||||
_freeCurrentParsedPacket();
|
||||
|
||||
for (auto callback : _onPublishUserCallbacks) callback(packetId);
|
||||
}
|
||||
|
||||
void AsyncMqttClient::_onPubRec(uint16_t packetId) {
|
||||
_freeCurrentParsedPacket();
|
||||
|
||||
AsyncMqttClientInternals::PendingAck pendingAck;
|
||||
pendingAck.packetType = AsyncMqttClientInternals::PacketType.PUBREL;
|
||||
pendingAck.headerFlag = AsyncMqttClientInternals::HeaderFlag.PUBREL_RESERVED;
|
||||
pendingAck.packetId = packetId;
|
||||
_toSendAcks.push_back(pendingAck);
|
||||
|
||||
_sendAcks();
|
||||
}
|
||||
|
||||
void AsyncMqttClient::_onPubComp(uint16_t packetId) {
|
||||
_freeCurrentParsedPacket();
|
||||
|
||||
for (auto callback : _onPublishUserCallbacks) callback(packetId);
|
||||
}
|
||||
|
||||
bool AsyncMqttClient::_sendPing() {
|
||||
char fixedHeader[2];
|
||||
fixedHeader[0] = AsyncMqttClientInternals::PacketType.PINGREQ;
|
||||
fixedHeader[0] = fixedHeader[0] << 4;
|
||||
fixedHeader[0] = fixedHeader[0] | AsyncMqttClientInternals::HeaderFlag.PINGREQ_RESERVED;
|
||||
fixedHeader[1] = 0;
|
||||
|
||||
size_t neededSpace = 2;
|
||||
|
||||
SEMAPHORE_TAKE(false);
|
||||
if (_client.space() < neededSpace) { SEMAPHORE_GIVE(); return false; }
|
||||
|
||||
_client.add(fixedHeader, 2);
|
||||
_client.send();
|
||||
_lastClientActivity = millis();
|
||||
_lastPingRequestTime = millis();
|
||||
|
||||
SEMAPHORE_GIVE();
|
||||
return true;
|
||||
}
|
||||
|
||||
void AsyncMqttClient::_sendAcks() {
|
||||
uint8_t neededAckSpace = 2 + 2;
|
||||
|
||||
SEMAPHORE_TAKE();
|
||||
for (size_t i = 0; i < _toSendAcks.size(); i++) {
|
||||
if (_client.space() < neededAckSpace) break;
|
||||
|
||||
AsyncMqttClientInternals::PendingAck pendingAck = _toSendAcks[i];
|
||||
|
||||
char fixedHeader[2];
|
||||
fixedHeader[0] = pendingAck.packetType;
|
||||
fixedHeader[0] = fixedHeader[0] << 4;
|
||||
fixedHeader[0] = fixedHeader[0] | pendingAck.headerFlag;
|
||||
fixedHeader[1] = 2;
|
||||
|
||||
char packetIdBytes[2];
|
||||
packetIdBytes[0] = pendingAck.packetId >> 8;
|
||||
packetIdBytes[1] = pendingAck.packetId & 0xFF;
|
||||
|
||||
_client.add(fixedHeader, 2);
|
||||
_client.add(packetIdBytes, 2);
|
||||
_client.send();
|
||||
|
||||
_toSendAcks.erase(_toSendAcks.begin() + i);
|
||||
_toSendAcks.shrink_to_fit();
|
||||
|
||||
_lastClientActivity = millis();
|
||||
}
|
||||
SEMAPHORE_GIVE();
|
||||
}
|
||||
|
||||
bool AsyncMqttClient::_sendDisconnect() {
|
||||
if (!_connected) return true;
|
||||
|
||||
const uint8_t neededSpace = 2;
|
||||
|
||||
SEMAPHORE_TAKE(false);
|
||||
|
||||
if (_client.space() < neededSpace) { SEMAPHORE_GIVE(); return false; }
|
||||
|
||||
char fixedHeader[2];
|
||||
fixedHeader[0] = AsyncMqttClientInternals::PacketType.DISCONNECT;
|
||||
fixedHeader[0] = fixedHeader[0] << 4;
|
||||
fixedHeader[0] = fixedHeader[0] | AsyncMqttClientInternals::HeaderFlag.DISCONNECT_RESERVED;
|
||||
fixedHeader[1] = 0;
|
||||
|
||||
_client.add(fixedHeader, 2);
|
||||
_client.send();
|
||||
_client.close(true);
|
||||
|
||||
_disconnectFlagged = false;
|
||||
|
||||
SEMAPHORE_GIVE();
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t AsyncMqttClient::_getNextPacketId() {
|
||||
uint16_t nextPacketId = _nextPacketId;
|
||||
|
||||
if (_nextPacketId == 65535) _nextPacketId = 0; // 0 is forbidden
|
||||
_nextPacketId++;
|
||||
|
||||
return nextPacketId;
|
||||
}
|
||||
|
||||
bool AsyncMqttClient::connected() const {
|
||||
return _connected;
|
||||
}
|
||||
|
||||
void AsyncMqttClient::connect() {
|
||||
if (_connected) return;
|
||||
|
||||
#if ASYNC_TCP_SSL_ENABLED
|
||||
if (_useIp) {
|
||||
_client.connect(_ip, _port, _secure);
|
||||
} else {
|
||||
_client.connect(_host, _port, _secure);
|
||||
}
|
||||
#else
|
||||
if (_useIp) {
|
||||
_client.connect(_ip, _port);
|
||||
} else {
|
||||
_client.connect(_host, _port);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void AsyncMqttClient::disconnect(bool force) {
|
||||
if (!_connected) return;
|
||||
|
||||
if (force) {
|
||||
_client.close(true);
|
||||
} else {
|
||||
_disconnectFlagged = true;
|
||||
_sendDisconnect();
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t AsyncMqttClient::subscribe(const char* topic, uint8_t qos) {
|
||||
if (!_connected) return 0;
|
||||
|
||||
char fixedHeader[5];
|
||||
fixedHeader[0] = AsyncMqttClientInternals::PacketType.SUBSCRIBE;
|
||||
fixedHeader[0] = fixedHeader[0] << 4;
|
||||
fixedHeader[0] = fixedHeader[0] | AsyncMqttClientInternals::HeaderFlag.SUBSCRIBE_RESERVED;
|
||||
|
||||
uint16_t topicLength = strlen(topic);
|
||||
char topicLengthBytes[2];
|
||||
topicLengthBytes[0] = topicLength >> 8;
|
||||
topicLengthBytes[1] = topicLength & 0xFF;
|
||||
|
||||
char qosByte[1];
|
||||
qosByte[0] = qos;
|
||||
|
||||
uint8_t remainingLengthLength = AsyncMqttClientInternals::Helpers::encodeRemainingLength(2 + 2 + topicLength + 1, fixedHeader + 1);
|
||||
|
||||
size_t neededSpace = 0;
|
||||
neededSpace += 1 + remainingLengthLength;
|
||||
neededSpace += 2;
|
||||
neededSpace += 2;
|
||||
neededSpace += topicLength;
|
||||
neededSpace += 1;
|
||||
|
||||
SEMAPHORE_TAKE(0);
|
||||
if (_client.space() < neededSpace) { SEMAPHORE_GIVE(); return 0; }
|
||||
|
||||
uint16_t packetId = _getNextPacketId();
|
||||
char packetIdBytes[2];
|
||||
packetIdBytes[0] = packetId >> 8;
|
||||
packetIdBytes[1] = packetId & 0xFF;
|
||||
|
||||
_client.add(fixedHeader, 1 + remainingLengthLength);
|
||||
_client.add(packetIdBytes, 2);
|
||||
_client.add(topicLengthBytes, 2);
|
||||
_client.add(topic, topicLength);
|
||||
_client.add(qosByte, 1);
|
||||
_client.send();
|
||||
_lastClientActivity = millis();
|
||||
|
||||
SEMAPHORE_GIVE();
|
||||
return packetId;
|
||||
}
|
||||
|
||||
uint16_t AsyncMqttClient::unsubscribe(const char* topic) {
|
||||
if (!_connected) return 0;
|
||||
|
||||
char fixedHeader[5];
|
||||
fixedHeader[0] = AsyncMqttClientInternals::PacketType.UNSUBSCRIBE;
|
||||
fixedHeader[0] = fixedHeader[0] << 4;
|
||||
fixedHeader[0] = fixedHeader[0] | AsyncMqttClientInternals::HeaderFlag.UNSUBSCRIBE_RESERVED;
|
||||
|
||||
uint16_t topicLength = strlen(topic);
|
||||
char topicLengthBytes[2];
|
||||
topicLengthBytes[0] = topicLength >> 8;
|
||||
topicLengthBytes[1] = topicLength & 0xFF;
|
||||
|
||||
uint8_t remainingLengthLength = AsyncMqttClientInternals::Helpers::encodeRemainingLength(2 + 2 + topicLength, fixedHeader + 1);
|
||||
|
||||
size_t neededSpace = 0;
|
||||
neededSpace += 1 + remainingLengthLength;
|
||||
neededSpace += 2;
|
||||
neededSpace += 2;
|
||||
neededSpace += topicLength;
|
||||
|
||||
SEMAPHORE_TAKE(0);
|
||||
if (_client.space() < neededSpace) { SEMAPHORE_GIVE(); return 0; }
|
||||
|
||||
uint16_t packetId = _getNextPacketId();
|
||||
char packetIdBytes[2];
|
||||
packetIdBytes[0] = packetId >> 8;
|
||||
packetIdBytes[1] = packetId & 0xFF;
|
||||
|
||||
_client.add(fixedHeader, 1 + remainingLengthLength);
|
||||
_client.add(packetIdBytes, 2);
|
||||
_client.add(topicLengthBytes, 2);
|
||||
_client.add(topic, topicLength);
|
||||
_client.send();
|
||||
_lastClientActivity = millis();
|
||||
|
||||
SEMAPHORE_GIVE();
|
||||
return packetId;
|
||||
}
|
||||
|
||||
uint16_t AsyncMqttClient::publish(const char* topic, uint8_t qos, bool retain, const char* payload, size_t length, bool dup, uint16_t message_id) {
|
||||
if (!_connected) return 0;
|
||||
|
||||
char fixedHeader[5];
|
||||
fixedHeader[0] = AsyncMqttClientInternals::PacketType.PUBLISH;
|
||||
fixedHeader[0] = fixedHeader[0] << 4;
|
||||
if (dup) fixedHeader[0] |= AsyncMqttClientInternals::HeaderFlag.PUBLISH_DUP;
|
||||
if (retain) fixedHeader[0] |= AsyncMqttClientInternals::HeaderFlag.PUBLISH_RETAIN;
|
||||
switch (qos) {
|
||||
case 0:
|
||||
fixedHeader[0] |= AsyncMqttClientInternals::HeaderFlag.PUBLISH_QOS0;
|
||||
break;
|
||||
case 1:
|
||||
fixedHeader[0] |= AsyncMqttClientInternals::HeaderFlag.PUBLISH_QOS1;
|
||||
break;
|
||||
case 2:
|
||||
fixedHeader[0] |= AsyncMqttClientInternals::HeaderFlag.PUBLISH_QOS2;
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t topicLength = strlen(topic);
|
||||
char topicLengthBytes[2];
|
||||
topicLengthBytes[0] = topicLength >> 8;
|
||||
topicLengthBytes[1] = topicLength & 0xFF;
|
||||
|
||||
uint32_t payloadLength = length;
|
||||
if (payload != nullptr && payloadLength == 0) payloadLength = strlen(payload);
|
||||
|
||||
uint32_t remainingLength = 2 + topicLength + payloadLength;
|
||||
if (qos != 0) remainingLength += 2;
|
||||
uint8_t remainingLengthLength = AsyncMqttClientInternals::Helpers::encodeRemainingLength(remainingLength, fixedHeader + 1);
|
||||
|
||||
size_t neededSpace = 0;
|
||||
neededSpace += 1 + remainingLengthLength;
|
||||
neededSpace += 2;
|
||||
neededSpace += topicLength;
|
||||
if (qos != 0) neededSpace += 2;
|
||||
if (payload != nullptr) neededSpace += payloadLength;
|
||||
|
||||
SEMAPHORE_TAKE(0);
|
||||
if (_client.space() < neededSpace) { SEMAPHORE_GIVE(); return 0; }
|
||||
|
||||
uint16_t packetId = 0;
|
||||
char packetIdBytes[2];
|
||||
if (qos != 0) {
|
||||
if (dup && message_id > 0) {
|
||||
packetId = message_id;
|
||||
} else {
|
||||
packetId = _getNextPacketId();
|
||||
}
|
||||
|
||||
packetIdBytes[0] = packetId >> 8;
|
||||
packetIdBytes[1] = packetId & 0xFF;
|
||||
}
|
||||
|
||||
_client.add(fixedHeader, 1 + remainingLengthLength);
|
||||
_client.add(topicLengthBytes, 2);
|
||||
_client.add(topic, topicLength);
|
||||
if (qos != 0) _client.add(packetIdBytes, 2);
|
||||
if (payload != nullptr) _client.add(payload, payloadLength);
|
||||
_client.send();
|
||||
_lastClientActivity = millis();
|
||||
|
||||
SEMAPHORE_GIVE();
|
||||
if (qos != 0) {
|
||||
return packetId;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef SRC_ASYNCMQTTCLIENT_H_
|
||||
#define SRC_ASYNCMQTTCLIENT_H_
|
||||
|
||||
#include "AsyncMqttClient.hpp"
|
||||
|
||||
#endif // SRC_ASYNCMQTTCLIENT_H_
|
||||
166
wled00/src/dependencies/async-mqtt-client/AsyncMqttClient.hpp
Normal file
166
wled00/src/dependencies/async-mqtt-client/AsyncMqttClient.hpp
Normal file
@@ -0,0 +1,166 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#ifdef ESP32
|
||||
#include <AsyncTCP.h>
|
||||
#include <freertos/semphr.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESPAsyncTCP.h>
|
||||
#else
|
||||
#error Platform not supported
|
||||
#endif
|
||||
|
||||
#if ASYNC_TCP_SSL_ENABLED
|
||||
#include <tcp_axtls.h>
|
||||
#define SHA1_SIZE 20
|
||||
#endif
|
||||
|
||||
#include "AsyncMqttClient/Flags.hpp"
|
||||
#include "AsyncMqttClient/ParsingInformation.hpp"
|
||||
#include "AsyncMqttClient/MessageProperties.hpp"
|
||||
#include "AsyncMqttClient/Helpers.hpp"
|
||||
#include "AsyncMqttClient/Callbacks.hpp"
|
||||
#include "AsyncMqttClient/DisconnectReasons.hpp"
|
||||
#include "AsyncMqttClient/Storage.hpp"
|
||||
|
||||
#include "AsyncMqttClient/Packets/Packet.hpp"
|
||||
#include "AsyncMqttClient/Packets/ConnAckPacket.hpp"
|
||||
#include "AsyncMqttClient/Packets/PingRespPacket.hpp"
|
||||
#include "AsyncMqttClient/Packets/SubAckPacket.hpp"
|
||||
#include "AsyncMqttClient/Packets/UnsubAckPacket.hpp"
|
||||
#include "AsyncMqttClient/Packets/PublishPacket.hpp"
|
||||
#include "AsyncMqttClient/Packets/PubRelPacket.hpp"
|
||||
#include "AsyncMqttClient/Packets/PubAckPacket.hpp"
|
||||
#include "AsyncMqttClient/Packets/PubRecPacket.hpp"
|
||||
#include "AsyncMqttClient/Packets/PubCompPacket.hpp"
|
||||
|
||||
#if ESP32
|
||||
#define SEMAPHORE_TAKE(X) if (xSemaphoreTake(_xSemaphore, 1000 / portTICK_PERIOD_MS) != pdTRUE) { return X; } // Waits max 1000ms
|
||||
#define SEMAPHORE_GIVE() xSemaphoreGive(_xSemaphore);
|
||||
#elif defined(ESP8266)
|
||||
#define SEMAPHORE_TAKE(X) void()
|
||||
#define SEMAPHORE_GIVE() void()
|
||||
#endif
|
||||
|
||||
class AsyncMqttClient {
|
||||
public:
|
||||
AsyncMqttClient();
|
||||
~AsyncMqttClient();
|
||||
|
||||
AsyncMqttClient& setKeepAlive(uint16_t keepAlive);
|
||||
AsyncMqttClient& setClientId(const char* clientId);
|
||||
AsyncMqttClient& setCleanSession(bool cleanSession);
|
||||
AsyncMqttClient& setMaxTopicLength(uint16_t maxTopicLength);
|
||||
AsyncMqttClient& setCredentials(const char* username, const char* password = nullptr);
|
||||
AsyncMqttClient& setWill(const char* topic, uint8_t qos, bool retain, const char* payload = nullptr, size_t length = 0);
|
||||
AsyncMqttClient& setServer(IPAddress ip, uint16_t port);
|
||||
AsyncMqttClient& setServer(const char* host, uint16_t port);
|
||||
#if ASYNC_TCP_SSL_ENABLED
|
||||
AsyncMqttClient& setSecure(bool secure);
|
||||
AsyncMqttClient& addServerFingerprint(const uint8_t* fingerprint);
|
||||
#endif
|
||||
|
||||
AsyncMqttClient& onConnect(AsyncMqttClientInternals::OnConnectUserCallback callback);
|
||||
AsyncMqttClient& onDisconnect(AsyncMqttClientInternals::OnDisconnectUserCallback callback);
|
||||
AsyncMqttClient& onSubscribe(AsyncMqttClientInternals::OnSubscribeUserCallback callback);
|
||||
AsyncMqttClient& onUnsubscribe(AsyncMqttClientInternals::OnUnsubscribeUserCallback callback);
|
||||
AsyncMqttClient& onMessage(AsyncMqttClientInternals::OnMessageUserCallback callback);
|
||||
AsyncMqttClient& onPublish(AsyncMqttClientInternals::OnPublishUserCallback callback);
|
||||
|
||||
bool connected() const;
|
||||
void connect();
|
||||
void disconnect(bool force = false);
|
||||
uint16_t subscribe(const char* topic, uint8_t qos);
|
||||
uint16_t unsubscribe(const char* topic);
|
||||
uint16_t publish(const char* topic, uint8_t qos, bool retain, const char* payload = nullptr, size_t length = 0, bool dup = false, uint16_t message_id = 0);
|
||||
|
||||
private:
|
||||
AsyncClient _client;
|
||||
|
||||
bool _connected;
|
||||
bool _connectPacketNotEnoughSpace;
|
||||
bool _disconnectFlagged;
|
||||
bool _tlsBadFingerprint;
|
||||
uint32_t _lastClientActivity;
|
||||
uint32_t _lastServerActivity;
|
||||
uint32_t _lastPingRequestTime;
|
||||
|
||||
char _generatedClientId[13 + 1]; // esp8266abc123
|
||||
IPAddress _ip;
|
||||
const char* _host;
|
||||
bool _useIp;
|
||||
#if ASYNC_TCP_SSL_ENABLED
|
||||
bool _secure;
|
||||
#endif
|
||||
uint16_t _port;
|
||||
uint16_t _keepAlive;
|
||||
bool _cleanSession;
|
||||
const char* _clientId;
|
||||
const char* _username;
|
||||
const char* _password;
|
||||
const char* _willTopic;
|
||||
const char* _willPayload;
|
||||
uint16_t _willPayloadLength;
|
||||
uint8_t _willQos;
|
||||
bool _willRetain;
|
||||
|
||||
#if ASYNC_TCP_SSL_ENABLED
|
||||
std::vector<std::array<uint8_t, SHA1_SIZE>> _secureServerFingerprints;
|
||||
#endif
|
||||
|
||||
std::vector<AsyncMqttClientInternals::OnConnectUserCallback> _onConnectUserCallbacks;
|
||||
std::vector<AsyncMqttClientInternals::OnDisconnectUserCallback> _onDisconnectUserCallbacks;
|
||||
std::vector<AsyncMqttClientInternals::OnSubscribeUserCallback> _onSubscribeUserCallbacks;
|
||||
std::vector<AsyncMqttClientInternals::OnUnsubscribeUserCallback> _onUnsubscribeUserCallbacks;
|
||||
std::vector<AsyncMqttClientInternals::OnMessageUserCallback> _onMessageUserCallbacks;
|
||||
std::vector<AsyncMqttClientInternals::OnPublishUserCallback> _onPublishUserCallbacks;
|
||||
|
||||
AsyncMqttClientInternals::ParsingInformation _parsingInformation;
|
||||
AsyncMqttClientInternals::Packet* _currentParsedPacket;
|
||||
uint8_t _remainingLengthBufferPosition;
|
||||
char _remainingLengthBuffer[4];
|
||||
|
||||
uint16_t _nextPacketId;
|
||||
|
||||
std::vector<AsyncMqttClientInternals::PendingPubRel> _pendingPubRels;
|
||||
|
||||
std::vector<AsyncMqttClientInternals::PendingAck> _toSendAcks;
|
||||
|
||||
#ifdef ESP32
|
||||
SemaphoreHandle_t _xSemaphore = nullptr;
|
||||
#endif
|
||||
|
||||
void _clear();
|
||||
void _freeCurrentParsedPacket();
|
||||
|
||||
// TCP
|
||||
void _onConnect(AsyncClient* client);
|
||||
void _onDisconnect(AsyncClient* client);
|
||||
static void _onError(AsyncClient* client, int8_t error);
|
||||
void _onTimeout(AsyncClient* client, uint32_t time);
|
||||
static void _onAck(AsyncClient* client, size_t len, uint32_t time);
|
||||
void _onData(AsyncClient* client, char* data, size_t len);
|
||||
void _onPoll(AsyncClient* client);
|
||||
|
||||
// MQTT
|
||||
void _onPingResp();
|
||||
void _onConnAck(bool sessionPresent, uint8_t connectReturnCode);
|
||||
void _onSubAck(uint16_t packetId, char status);
|
||||
void _onUnsubAck(uint16_t packetId);
|
||||
void _onMessage(char* topic, char* payload, uint8_t qos, bool dup, bool retain, size_t len, size_t index, size_t total, uint16_t packetId);
|
||||
void _onPublish(uint16_t packetId, uint8_t qos);
|
||||
void _onPubRel(uint16_t packetId);
|
||||
void _onPubAck(uint16_t packetId);
|
||||
void _onPubRec(uint16_t packetId);
|
||||
void _onPubComp(uint16_t packetId);
|
||||
|
||||
bool _sendPing();
|
||||
void _sendAcks();
|
||||
bool _sendDisconnect();
|
||||
|
||||
uint16_t _getNextPacketId();
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "DisconnectReasons.hpp"
|
||||
#include "MessageProperties.hpp"
|
||||
|
||||
namespace AsyncMqttClientInternals {
|
||||
// user callbacks
|
||||
typedef std::function<void(bool sessionPresent)> OnConnectUserCallback;
|
||||
typedef std::function<void(AsyncMqttClientDisconnectReason reason)> OnDisconnectUserCallback;
|
||||
typedef std::function<void(uint16_t packetId, uint8_t qos)> OnSubscribeUserCallback;
|
||||
typedef std::function<void(uint16_t packetId)> OnUnsubscribeUserCallback;
|
||||
typedef std::function<void(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total)> OnMessageUserCallback;
|
||||
typedef std::function<void(uint16_t packetId)> OnPublishUserCallback;
|
||||
|
||||
// internal callbacks
|
||||
typedef std::function<void(bool sessionPresent, uint8_t connectReturnCode)> OnConnAckInternalCallback;
|
||||
typedef std::function<void()> OnPingRespInternalCallback;
|
||||
typedef std::function<void(uint16_t packetId, char status)> OnSubAckInternalCallback;
|
||||
typedef std::function<void(uint16_t packetId)> OnUnsubAckInternalCallback;
|
||||
typedef std::function<void(char* topic, char* payload, uint8_t qos, bool dup, bool retain, size_t len, size_t index, size_t total, uint16_t packetId)> OnMessageInternalCallback;
|
||||
typedef std::function<void(uint16_t packetId, uint8_t qos)> OnPublishInternalCallback;
|
||||
typedef std::function<void(uint16_t packetId)> OnPubRelInternalCallback;
|
||||
typedef std::function<void(uint16_t packetId)> OnPubAckInternalCallback;
|
||||
typedef std::function<void(uint16_t packetId)> OnPubRecInternalCallback;
|
||||
typedef std::function<void(uint16_t packetId)> OnPubCompInternalCallback;
|
||||
} // namespace AsyncMqttClientInternals
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
enum class AsyncMqttClientDisconnectReason : int8_t {
|
||||
TCP_DISCONNECTED = 0,
|
||||
|
||||
MQTT_UNACCEPTABLE_PROTOCOL_VERSION = 1,
|
||||
MQTT_IDENTIFIER_REJECTED = 2,
|
||||
MQTT_SERVER_UNAVAILABLE = 3,
|
||||
MQTT_MALFORMED_CREDENTIALS = 4,
|
||||
MQTT_NOT_AUTHORIZED = 5,
|
||||
|
||||
ESP8266_NOT_ENOUGH_SPACE = 6,
|
||||
|
||||
TLS_BAD_FINGERPRINT = 7
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
namespace AsyncMqttClientInternals {
|
||||
constexpr struct {
|
||||
const uint8_t RESERVED = 0;
|
||||
const uint8_t CONNECT = 1;
|
||||
const uint8_t CONNACK = 2;
|
||||
const uint8_t PUBLISH = 3;
|
||||
const uint8_t PUBACK = 4;
|
||||
const uint8_t PUBREC = 5;
|
||||
const uint8_t PUBREL = 6;
|
||||
const uint8_t PUBCOMP = 7;
|
||||
const uint8_t SUBSCRIBE = 8;
|
||||
const uint8_t SUBACK = 9;
|
||||
const uint8_t UNSUBSCRIBE = 10;
|
||||
const uint8_t UNSUBACK = 11;
|
||||
const uint8_t PINGREQ = 12;
|
||||
const uint8_t PINGRESP = 13;
|
||||
const uint8_t DISCONNECT = 14;
|
||||
const uint8_t RESERVED2 = 1;
|
||||
} PacketType;
|
||||
|
||||
constexpr struct {
|
||||
const uint8_t CONNECT_RESERVED = 0x00;
|
||||
const uint8_t CONNACK_RESERVED = 0x00;
|
||||
const uint8_t PUBLISH_DUP = 0x08;
|
||||
const uint8_t PUBLISH_QOS0 = 0x00;
|
||||
const uint8_t PUBLISH_QOS1 = 0x02;
|
||||
const uint8_t PUBLISH_QOS2 = 0x04;
|
||||
const uint8_t PUBLISH_QOSRESERVED = 0x06;
|
||||
const uint8_t PUBLISH_RETAIN = 0x01;
|
||||
const uint8_t PUBACK_RESERVED = 0x00;
|
||||
const uint8_t PUBREC_RESERVED = 0x00;
|
||||
const uint8_t PUBREL_RESERVED = 0x02;
|
||||
const uint8_t PUBCOMP_RESERVED = 0x00;
|
||||
const uint8_t SUBSCRIBE_RESERVED = 0x02;
|
||||
const uint8_t SUBACK_RESERVED = 0x00;
|
||||
const uint8_t UNSUBSCRIBE_RESERVED = 0x02;
|
||||
const uint8_t UNSUBACK_RESERVED = 0x00;
|
||||
const uint8_t PINGREQ_RESERVED = 0x00;
|
||||
const uint8_t PINGRESP_RESERVED = 0x00;
|
||||
const uint8_t DISCONNECT_RESERVED = 0x00;
|
||||
const uint8_t RESERVED2_RESERVED = 0x00;
|
||||
} HeaderFlag;
|
||||
|
||||
constexpr struct {
|
||||
const uint8_t USERNAME = 0x80;
|
||||
const uint8_t PASSWORD = 0x40;
|
||||
const uint8_t WILL_RETAIN = 0x20;
|
||||
const uint8_t WILL_QOS0 = 0x00;
|
||||
const uint8_t WILL_QOS1 = 0x08;
|
||||
const uint8_t WILL_QOS2 = 0x10;
|
||||
const uint8_t WILL = 0x04;
|
||||
const uint8_t CLEAN_SESSION = 0x02;
|
||||
const uint8_t RESERVED = 0x00;
|
||||
} ConnectFlag;
|
||||
} // namespace AsyncMqttClientInternals
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
namespace AsyncMqttClientInternals {
|
||||
class Helpers {
|
||||
public:
|
||||
static uint32_t decodeRemainingLength(char* bytes) {
|
||||
uint32_t multiplier = 1;
|
||||
uint32_t value = 0;
|
||||
uint8_t currentByte = 0;
|
||||
uint8_t encodedByte;
|
||||
do {
|
||||
encodedByte = bytes[currentByte++];
|
||||
value += (encodedByte & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
} while ((encodedByte & 128) != 0);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static uint8_t encodeRemainingLength(uint32_t remainingLength, char* destination) {
|
||||
uint8_t currentByte = 0;
|
||||
uint8_t bytesNeeded = 0;
|
||||
|
||||
do {
|
||||
uint8_t encodedByte = remainingLength % 128;
|
||||
remainingLength /= 128;
|
||||
if (remainingLength > 0) {
|
||||
encodedByte = encodedByte | 128;
|
||||
}
|
||||
|
||||
destination[currentByte++] = encodedByte;
|
||||
bytesNeeded++;
|
||||
} while (remainingLength > 0);
|
||||
|
||||
return bytesNeeded;
|
||||
}
|
||||
};
|
||||
} // namespace AsyncMqttClientInternals
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
struct AsyncMqttClientMessageProperties {
|
||||
uint8_t qos;
|
||||
bool dup;
|
||||
bool retain;
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "ConnAckPacket.hpp"
|
||||
|
||||
using AsyncMqttClientInternals::ConnAckPacket;
|
||||
|
||||
ConnAckPacket::ConnAckPacket(ParsingInformation* parsingInformation, OnConnAckInternalCallback callback)
|
||||
: _parsingInformation(parsingInformation)
|
||||
, _callback(callback)
|
||||
, _bytePosition(0)
|
||||
, _sessionPresent(false)
|
||||
, _connectReturnCode(0) {
|
||||
}
|
||||
|
||||
ConnAckPacket::~ConnAckPacket() {
|
||||
}
|
||||
|
||||
void ConnAckPacket::parseVariableHeader(char* data, size_t len, size_t* currentBytePosition) {
|
||||
char currentByte = data[(*currentBytePosition)++];
|
||||
if (_bytePosition++ == 0) {
|
||||
_sessionPresent = (currentByte << 7) >> 7;
|
||||
} else {
|
||||
_connectReturnCode = currentByte;
|
||||
_parsingInformation->bufferState = BufferState::NONE;
|
||||
_callback(_sessionPresent, _connectReturnCode);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnAckPacket::parsePayload(char* data, size_t len, size_t* currentBytePosition) {
|
||||
(void)data;
|
||||
(void)currentBytePosition;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Packet.hpp"
|
||||
#include "../ParsingInformation.hpp"
|
||||
#include "../Callbacks.hpp"
|
||||
|
||||
namespace AsyncMqttClientInternals {
|
||||
class ConnAckPacket : public Packet {
|
||||
public:
|
||||
explicit ConnAckPacket(ParsingInformation* parsingInformation, OnConnAckInternalCallback callback);
|
||||
~ConnAckPacket();
|
||||
|
||||
void parseVariableHeader(char* data, size_t len, size_t* currentBytePosition);
|
||||
void parsePayload(char* data, size_t len, size_t* currentBytePosition);
|
||||
|
||||
private:
|
||||
ParsingInformation* _parsingInformation;
|
||||
OnConnAckInternalCallback _callback;
|
||||
|
||||
uint8_t _bytePosition;
|
||||
bool _sessionPresent;
|
||||
uint8_t _connectReturnCode;
|
||||
};
|
||||
} // namespace AsyncMqttClientInternals
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
namespace AsyncMqttClientInternals {
|
||||
class Packet {
|
||||
public:
|
||||
virtual ~Packet() {}
|
||||
|
||||
virtual void parseVariableHeader(char* data, size_t len, size_t* currentBytePosition) = 0;
|
||||
virtual void parsePayload(char* data, size_t len, size_t* currentBytePosition) = 0;
|
||||
};
|
||||
} // namespace AsyncMqttClientInternals
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "PingRespPacket.hpp"
|
||||
|
||||
using AsyncMqttClientInternals::PingRespPacket;
|
||||
|
||||
PingRespPacket::PingRespPacket(ParsingInformation* parsingInformation, OnPingRespInternalCallback callback)
|
||||
: _parsingInformation(parsingInformation)
|
||||
, _callback(callback) {
|
||||
}
|
||||
|
||||
PingRespPacket::~PingRespPacket() {
|
||||
}
|
||||
|
||||
void PingRespPacket::parseVariableHeader(char* data, size_t len, size_t* currentBytePosition) {
|
||||
(void)data;
|
||||
(void)currentBytePosition;
|
||||
}
|
||||
|
||||
void PingRespPacket::parsePayload(char* data, size_t len, size_t* currentBytePosition) {
|
||||
(void)data;
|
||||
(void)currentBytePosition;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Packet.hpp"
|
||||
#include "../ParsingInformation.hpp"
|
||||
#include "../Callbacks.hpp"
|
||||
|
||||
namespace AsyncMqttClientInternals {
|
||||
class PingRespPacket : public Packet {
|
||||
public:
|
||||
explicit PingRespPacket(ParsingInformation* parsingInformation, OnPingRespInternalCallback callback);
|
||||
~PingRespPacket();
|
||||
|
||||
void parseVariableHeader(char* data, size_t len, size_t* currentBytePosition);
|
||||
void parsePayload(char* data, size_t len, size_t* currentBytePosition);
|
||||
|
||||
private:
|
||||
ParsingInformation* _parsingInformation;
|
||||
OnPingRespInternalCallback _callback;
|
||||
};
|
||||
} // namespace AsyncMqttClientInternals
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "PubAckPacket.hpp"
|
||||
|
||||
using AsyncMqttClientInternals::PubAckPacket;
|
||||
|
||||
PubAckPacket::PubAckPacket(ParsingInformation* parsingInformation, OnPubAckInternalCallback callback)
|
||||
: _parsingInformation(parsingInformation)
|
||||
, _callback(callback)
|
||||
, _bytePosition(0)
|
||||
, _packetIdMsb(0)
|
||||
, _packetId(0) {
|
||||
}
|
||||
|
||||
PubAckPacket::~PubAckPacket() {
|
||||
}
|
||||
|
||||
void PubAckPacket::parseVariableHeader(char* data, size_t len, size_t* currentBytePosition) {
|
||||
char currentByte = data[(*currentBytePosition)++];
|
||||
if (_bytePosition++ == 0) {
|
||||
_packetIdMsb = currentByte;
|
||||
} else {
|
||||
_packetId = currentByte | _packetIdMsb << 8;
|
||||
_parsingInformation->bufferState = BufferState::NONE;
|
||||
_callback(_packetId);
|
||||
}
|
||||
}
|
||||
|
||||
void PubAckPacket::parsePayload(char* data, size_t len, size_t* currentBytePosition) {
|
||||
(void)data;
|
||||
(void)currentBytePosition;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Packet.hpp"
|
||||
#include "../ParsingInformation.hpp"
|
||||
#include "../Callbacks.hpp"
|
||||
|
||||
namespace AsyncMqttClientInternals {
|
||||
class PubAckPacket : public Packet {
|
||||
public:
|
||||
explicit PubAckPacket(ParsingInformation* parsingInformation, OnPubAckInternalCallback callback);
|
||||
~PubAckPacket();
|
||||
|
||||
void parseVariableHeader(char* data, size_t len, size_t* currentBytePosition);
|
||||
void parsePayload(char* data, size_t len, size_t* currentBytePosition);
|
||||
|
||||
private:
|
||||
ParsingInformation* _parsingInformation;
|
||||
OnPubAckInternalCallback _callback;
|
||||
|
||||
uint8_t _bytePosition;
|
||||
char _packetIdMsb;
|
||||
uint16_t _packetId;
|
||||
};
|
||||
} // namespace AsyncMqttClientInternals
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "PubCompPacket.hpp"
|
||||
|
||||
using AsyncMqttClientInternals::PubCompPacket;
|
||||
|
||||
PubCompPacket::PubCompPacket(ParsingInformation* parsingInformation, OnPubCompInternalCallback callback)
|
||||
: _parsingInformation(parsingInformation)
|
||||
, _callback(callback)
|
||||
, _bytePosition(0)
|
||||
, _packetIdMsb(0)
|
||||
, _packetId(0) {
|
||||
}
|
||||
|
||||
PubCompPacket::~PubCompPacket() {
|
||||
}
|
||||
|
||||
void PubCompPacket::parseVariableHeader(char* data, size_t len, size_t* currentBytePosition) {
|
||||
char currentByte = data[(*currentBytePosition)++];
|
||||
if (_bytePosition++ == 0) {
|
||||
_packetIdMsb = currentByte;
|
||||
} else {
|
||||
_packetId = currentByte | _packetIdMsb << 8;
|
||||
_parsingInformation->bufferState = BufferState::NONE;
|
||||
_callback(_packetId);
|
||||
}
|
||||
}
|
||||
|
||||
void PubCompPacket::parsePayload(char* data, size_t len, size_t* currentBytePosition) {
|
||||
(void)data;
|
||||
(void)currentBytePosition;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Packet.hpp"
|
||||
#include "../ParsingInformation.hpp"
|
||||
#include "../Callbacks.hpp"
|
||||
|
||||
namespace AsyncMqttClientInternals {
|
||||
class PubCompPacket : public Packet {
|
||||
public:
|
||||
explicit PubCompPacket(ParsingInformation* parsingInformation, OnPubCompInternalCallback callback);
|
||||
~PubCompPacket();
|
||||
|
||||
void parseVariableHeader(char* data, size_t len, size_t* currentBytePosition);
|
||||
void parsePayload(char* data, size_t len, size_t* currentBytePosition);
|
||||
|
||||
private:
|
||||
ParsingInformation* _parsingInformation;
|
||||
OnPubCompInternalCallback _callback;
|
||||
|
||||
uint8_t _bytePosition;
|
||||
char _packetIdMsb;
|
||||
uint16_t _packetId;
|
||||
};
|
||||
} // namespace AsyncMqttClientInternals
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "PubRecPacket.hpp"
|
||||
|
||||
using AsyncMqttClientInternals::PubRecPacket;
|
||||
|
||||
PubRecPacket::PubRecPacket(ParsingInformation* parsingInformation, OnPubRecInternalCallback callback)
|
||||
: _parsingInformation(parsingInformation)
|
||||
, _callback(callback)
|
||||
, _bytePosition(0)
|
||||
, _packetIdMsb(0)
|
||||
, _packetId(0) {
|
||||
}
|
||||
|
||||
PubRecPacket::~PubRecPacket() {
|
||||
}
|
||||
|
||||
void PubRecPacket::parseVariableHeader(char* data, size_t len, size_t* currentBytePosition) {
|
||||
char currentByte = data[(*currentBytePosition)++];
|
||||
if (_bytePosition++ == 0) {
|
||||
_packetIdMsb = currentByte;
|
||||
} else {
|
||||
_packetId = currentByte | _packetIdMsb << 8;
|
||||
_parsingInformation->bufferState = BufferState::NONE;
|
||||
_callback(_packetId);
|
||||
}
|
||||
}
|
||||
|
||||
void PubRecPacket::parsePayload(char* data, size_t len, size_t* currentBytePosition) {
|
||||
(void)data;
|
||||
(void)currentBytePosition;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Packet.hpp"
|
||||
#include "../ParsingInformation.hpp"
|
||||
#include "../Callbacks.hpp"
|
||||
|
||||
namespace AsyncMqttClientInternals {
|
||||
class PubRecPacket : public Packet {
|
||||
public:
|
||||
explicit PubRecPacket(ParsingInformation* parsingInformation, OnPubRecInternalCallback callback);
|
||||
~PubRecPacket();
|
||||
|
||||
void parseVariableHeader(char* data, size_t len, size_t* currentBytePosition);
|
||||
void parsePayload(char* data, size_t len, size_t* currentBytePosition);
|
||||
|
||||
private:
|
||||
ParsingInformation* _parsingInformation;
|
||||
OnPubRecInternalCallback _callback;
|
||||
|
||||
uint8_t _bytePosition;
|
||||
char _packetIdMsb;
|
||||
uint16_t _packetId;
|
||||
};
|
||||
} // namespace AsyncMqttClientInternals
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "PubRelPacket.hpp"
|
||||
|
||||
using AsyncMqttClientInternals::PubRelPacket;
|
||||
|
||||
PubRelPacket::PubRelPacket(ParsingInformation* parsingInformation, OnPubRelInternalCallback callback)
|
||||
: _parsingInformation(parsingInformation)
|
||||
, _callback(callback)
|
||||
, _bytePosition(0)
|
||||
, _packetIdMsb(0)
|
||||
, _packetId(0) {
|
||||
}
|
||||
|
||||
PubRelPacket::~PubRelPacket() {
|
||||
}
|
||||
|
||||
void PubRelPacket::parseVariableHeader(char* data, size_t len, size_t* currentBytePosition) {
|
||||
char currentByte = data[(*currentBytePosition)++];
|
||||
if (_bytePosition++ == 0) {
|
||||
_packetIdMsb = currentByte;
|
||||
} else {
|
||||
_packetId = currentByte | _packetIdMsb << 8;
|
||||
_parsingInformation->bufferState = BufferState::NONE;
|
||||
_callback(_packetId);
|
||||
}
|
||||
}
|
||||
|
||||
void PubRelPacket::parsePayload(char* data, size_t len, size_t* currentBytePosition) {
|
||||
(void)data;
|
||||
(void)currentBytePosition;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Packet.hpp"
|
||||
#include "../ParsingInformation.hpp"
|
||||
#include "../Callbacks.hpp"
|
||||
|
||||
namespace AsyncMqttClientInternals {
|
||||
class PubRelPacket : public Packet {
|
||||
public:
|
||||
explicit PubRelPacket(ParsingInformation* parsingInformation, OnPubRelInternalCallback callback);
|
||||
~PubRelPacket();
|
||||
|
||||
void parseVariableHeader(char* data, size_t len, size_t* currentBytePosition);
|
||||
void parsePayload(char* data, size_t len, size_t* currentBytePosition);
|
||||
|
||||
private:
|
||||
ParsingInformation* _parsingInformation;
|
||||
OnPubRelInternalCallback _callback;
|
||||
|
||||
uint8_t _bytePosition;
|
||||
char _packetIdMsb;
|
||||
uint16_t _packetId;
|
||||
};
|
||||
} // namespace AsyncMqttClientInternals
|
||||
@@ -0,0 +1,91 @@
|
||||
#include "PublishPacket.hpp"
|
||||
|
||||
using AsyncMqttClientInternals::PublishPacket;
|
||||
|
||||
PublishPacket::PublishPacket(ParsingInformation* parsingInformation, OnMessageInternalCallback dataCallback, OnPublishInternalCallback completeCallback)
|
||||
: _parsingInformation(parsingInformation)
|
||||
, _dataCallback(dataCallback)
|
||||
, _completeCallback(completeCallback)
|
||||
, _dup(false)
|
||||
, _qos(0)
|
||||
, _retain(0)
|
||||
, _bytePosition(0)
|
||||
, _topicLengthMsb(0)
|
||||
, _topicLength(0)
|
||||
, _ignore(false)
|
||||
, _packetIdMsb(0)
|
||||
, _packetId(0)
|
||||
, _payloadLength(0)
|
||||
, _payloadBytesRead(0) {
|
||||
_dup = _parsingInformation->packetFlags & HeaderFlag.PUBLISH_DUP;
|
||||
_retain = _parsingInformation->packetFlags & HeaderFlag.PUBLISH_RETAIN;
|
||||
char qosMasked = _parsingInformation->packetFlags & 0x06;
|
||||
switch (qosMasked) {
|
||||
case HeaderFlag.PUBLISH_QOS0:
|
||||
_qos = 0;
|
||||
break;
|
||||
case HeaderFlag.PUBLISH_QOS1:
|
||||
_qos = 1;
|
||||
break;
|
||||
case HeaderFlag.PUBLISH_QOS2:
|
||||
_qos = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PublishPacket::~PublishPacket() {
|
||||
}
|
||||
|
||||
void PublishPacket::parseVariableHeader(char* data, size_t len, size_t* currentBytePosition) {
|
||||
char currentByte = data[(*currentBytePosition)++];
|
||||
if (_bytePosition == 0) {
|
||||
_topicLengthMsb = currentByte;
|
||||
} else if (_bytePosition == 1) {
|
||||
_topicLength = currentByte | _topicLengthMsb << 8;
|
||||
if (_topicLength > _parsingInformation->maxTopicLength) {
|
||||
_ignore = true;
|
||||
} else {
|
||||
_parsingInformation->topicBuffer[_topicLength] = '\0';
|
||||
}
|
||||
} else if (_bytePosition >= 2 && _bytePosition < 2 + _topicLength) {
|
||||
// Starting from here, _ignore might be true
|
||||
if (!_ignore) _parsingInformation->topicBuffer[_bytePosition - 2] = currentByte;
|
||||
if (_bytePosition == 2 + _topicLength - 1 && _qos == 0) {
|
||||
_preparePayloadHandling(_parsingInformation->remainingLength - (_bytePosition + 1));
|
||||
return;
|
||||
}
|
||||
} else if (_bytePosition == 2 + _topicLength) {
|
||||
_packetIdMsb = currentByte;
|
||||
} else {
|
||||
_packetId = currentByte | _packetIdMsb << 8;
|
||||
_preparePayloadHandling(_parsingInformation->remainingLength - (_bytePosition + 1));
|
||||
}
|
||||
_bytePosition++;
|
||||
}
|
||||
|
||||
void PublishPacket::_preparePayloadHandling(uint32_t payloadLength) {
|
||||
_payloadLength = payloadLength;
|
||||
if (payloadLength == 0) {
|
||||
_parsingInformation->bufferState = BufferState::NONE;
|
||||
if (!_ignore) {
|
||||
_dataCallback(_parsingInformation->topicBuffer, nullptr, _qos, _dup, _retain, 0, 0, 0, _packetId);
|
||||
_completeCallback(_packetId, _qos);
|
||||
}
|
||||
} else {
|
||||
_parsingInformation->bufferState = BufferState::PAYLOAD;
|
||||
}
|
||||
}
|
||||
|
||||
void PublishPacket::parsePayload(char* data, size_t len, size_t* currentBytePosition) {
|
||||
size_t remainToRead = len - (*currentBytePosition);
|
||||
if (_payloadBytesRead + remainToRead > _payloadLength) remainToRead = _payloadLength - _payloadBytesRead;
|
||||
|
||||
if (!_ignore) _dataCallback(_parsingInformation->topicBuffer, data + (*currentBytePosition), _qos, _dup, _retain, remainToRead, _payloadBytesRead, _payloadLength, _packetId);
|
||||
_payloadBytesRead += remainToRead;
|
||||
(*currentBytePosition) += remainToRead;
|
||||
|
||||
if (_payloadBytesRead == _payloadLength) {
|
||||
_parsingInformation->bufferState = BufferState::NONE;
|
||||
if (!_ignore) _completeCallback(_packetId, _qos);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Packet.hpp"
|
||||
#include "../Flags.hpp"
|
||||
#include "../ParsingInformation.hpp"
|
||||
#include "../Callbacks.hpp"
|
||||
|
||||
namespace AsyncMqttClientInternals {
|
||||
class PublishPacket : public Packet {
|
||||
public:
|
||||
explicit PublishPacket(ParsingInformation* parsingInformation, OnMessageInternalCallback dataCallback, OnPublishInternalCallback completeCallback);
|
||||
~PublishPacket();
|
||||
|
||||
void parseVariableHeader(char* data, size_t len, size_t* currentBytePosition);
|
||||
void parsePayload(char* data, size_t len, size_t* currentBytePosition);
|
||||
|
||||
private:
|
||||
ParsingInformation* _parsingInformation;
|
||||
OnMessageInternalCallback _dataCallback;
|
||||
OnPublishInternalCallback _completeCallback;
|
||||
|
||||
void _preparePayloadHandling(uint32_t payloadLength);
|
||||
|
||||
bool _dup;
|
||||
uint8_t _qos;
|
||||
bool _retain;
|
||||
|
||||
uint8_t _bytePosition;
|
||||
char _topicLengthMsb;
|
||||
uint16_t _topicLength;
|
||||
bool _ignore;
|
||||
char _packetIdMsb;
|
||||
uint16_t _packetId;
|
||||
uint32_t _payloadLength;
|
||||
uint32_t _payloadBytesRead;
|
||||
};
|
||||
} // namespace AsyncMqttClientInternals
|
||||
@@ -0,0 +1,46 @@
|
||||
#include "SubAckPacket.hpp"
|
||||
|
||||
using AsyncMqttClientInternals::SubAckPacket;
|
||||
|
||||
SubAckPacket::SubAckPacket(ParsingInformation* parsingInformation, OnSubAckInternalCallback callback)
|
||||
: _parsingInformation(parsingInformation)
|
||||
, _callback(callback)
|
||||
, _bytePosition(0)
|
||||
, _packetIdMsb(0)
|
||||
, _packetId(0) {
|
||||
}
|
||||
|
||||
SubAckPacket::~SubAckPacket() {
|
||||
}
|
||||
|
||||
void SubAckPacket::parseVariableHeader(char* data, size_t len, size_t* currentBytePosition) {
|
||||
char currentByte = data[(*currentBytePosition)++];
|
||||
if (_bytePosition++ == 0) {
|
||||
_packetIdMsb = currentByte;
|
||||
} else {
|
||||
_packetId = currentByte | _packetIdMsb << 8;
|
||||
_parsingInformation->bufferState = BufferState::PAYLOAD;
|
||||
}
|
||||
}
|
||||
|
||||
void SubAckPacket::parsePayload(char* data, size_t len, size_t* currentBytePosition) {
|
||||
char status = data[(*currentBytePosition)++];
|
||||
|
||||
/* switch (status) {
|
||||
case 0:
|
||||
Serial.println("Success QoS 0");
|
||||
break;
|
||||
case 1:
|
||||
Serial.println("Success QoS 1");
|
||||
break;
|
||||
case 2:
|
||||
Serial.println("Success QoS 2");
|
||||
break;
|
||||
case 0x80:
|
||||
Serial.println("Failure");
|
||||
break;
|
||||
} */
|
||||
|
||||
_parsingInformation->bufferState = BufferState::NONE;
|
||||
_callback(_packetId, status);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Packet.hpp"
|
||||
#include "../ParsingInformation.hpp"
|
||||
#include "../Callbacks.hpp"
|
||||
|
||||
namespace AsyncMqttClientInternals {
|
||||
class SubAckPacket : public Packet {
|
||||
public:
|
||||
explicit SubAckPacket(ParsingInformation* parsingInformation, OnSubAckInternalCallback callback);
|
||||
~SubAckPacket();
|
||||
|
||||
void parseVariableHeader(char* data, size_t len, size_t* currentBytePosition);
|
||||
void parsePayload(char* data, size_t len, size_t* currentBytePosition);
|
||||
|
||||
private:
|
||||
ParsingInformation* _parsingInformation;
|
||||
OnSubAckInternalCallback _callback;
|
||||
|
||||
uint8_t _bytePosition;
|
||||
char _packetIdMsb;
|
||||
uint16_t _packetId;
|
||||
};
|
||||
} // namespace AsyncMqttClientInternals
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "UnsubAckPacket.hpp"
|
||||
|
||||
using AsyncMqttClientInternals::UnsubAckPacket;
|
||||
|
||||
UnsubAckPacket::UnsubAckPacket(ParsingInformation* parsingInformation, OnUnsubAckInternalCallback callback)
|
||||
: _parsingInformation(parsingInformation)
|
||||
, _callback(callback)
|
||||
, _bytePosition(0)
|
||||
, _packetIdMsb(0)
|
||||
, _packetId(0) {
|
||||
}
|
||||
|
||||
UnsubAckPacket::~UnsubAckPacket() {
|
||||
}
|
||||
|
||||
void UnsubAckPacket::parseVariableHeader(char* data, size_t len, size_t* currentBytePosition) {
|
||||
char currentByte = data[(*currentBytePosition)++];
|
||||
if (_bytePosition++ == 0) {
|
||||
_packetIdMsb = currentByte;
|
||||
} else {
|
||||
_packetId = currentByte | _packetIdMsb << 8;
|
||||
_parsingInformation->bufferState = BufferState::NONE;
|
||||
_callback(_packetId);
|
||||
}
|
||||
}
|
||||
|
||||
void UnsubAckPacket::parsePayload(char* data, size_t len, size_t* currentBytePosition) {
|
||||
(void)data;
|
||||
(void)currentBytePosition;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Packet.hpp"
|
||||
#include "../ParsingInformation.hpp"
|
||||
#include "../Callbacks.hpp"
|
||||
|
||||
namespace AsyncMqttClientInternals {
|
||||
class UnsubAckPacket : public Packet {
|
||||
public:
|
||||
explicit UnsubAckPacket(ParsingInformation* parsingInformation, OnUnsubAckInternalCallback callback);
|
||||
~UnsubAckPacket();
|
||||
|
||||
void parseVariableHeader(char* data, size_t len, size_t* currentBytePosition);
|
||||
void parsePayload(char* data, size_t len, size_t* currentBytePosition);
|
||||
|
||||
private:
|
||||
ParsingInformation* _parsingInformation;
|
||||
OnUnsubAckInternalCallback _callback;
|
||||
|
||||
uint8_t _bytePosition;
|
||||
char _packetIdMsb;
|
||||
uint16_t _packetId;
|
||||
};
|
||||
} // namespace AsyncMqttClientInternals
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
namespace AsyncMqttClientInternals {
|
||||
enum class BufferState : uint8_t {
|
||||
NONE = 0,
|
||||
REMAINING_LENGTH = 2,
|
||||
VARIABLE_HEADER = 3,
|
||||
PAYLOAD = 4
|
||||
};
|
||||
|
||||
struct ParsingInformation {
|
||||
BufferState bufferState;
|
||||
|
||||
uint16_t maxTopicLength;
|
||||
char* topicBuffer;
|
||||
|
||||
uint8_t packetType;
|
||||
uint16_t packetFlags;
|
||||
uint32_t remainingLength;
|
||||
};
|
||||
} // namespace AsyncMqttClientInternals
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
namespace AsyncMqttClientInternals {
|
||||
struct PendingPubRel {
|
||||
uint16_t packetId;
|
||||
};
|
||||
|
||||
struct PendingAck {
|
||||
uint8_t packetType;
|
||||
uint8_t headerFlag;
|
||||
uint16_t packetId;
|
||||
};
|
||||
} // namespace AsyncMqttClientInternals
|
||||
21
wled00/src/dependencies/async-mqtt-client/LICENSE
Normal file
21
wled00/src/dependencies/async-mqtt-client/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Marvin Roger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
18
wled00/src/dependencies/async-mqtt-client/README.md
Normal file
18
wled00/src/dependencies/async-mqtt-client/README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
Async MQTT client for ESP8266 and ESP32 (Github: https://github.com/marvinroger/async-mqtt-client)
|
||||
=============================
|
||||
|
||||
[](https://travis-ci.org/marvinroger/async-mqtt-client)
|
||||
|
||||
An Arduino for ESP8266 and ESP32 asynchronous [MQTT](http://mqtt.org/) client implementation, built on [me-no-dev/ESPAsyncTCP (ESP8266)](https://github.com/me-no-dev/ESPAsyncTCP) | [me-no-dev/AsyncTCP (ESP32)](https://github.com/me-no-dev/AsyncTCP) .
|
||||
## Features
|
||||
|
||||
* Compliant with the 3.1.1 version of the protocol
|
||||
* Fully asynchronous
|
||||
* Subscribe at QoS 0, 1 and 2
|
||||
* Publish at QoS 0, 1 and 2
|
||||
* SSL/TLS support
|
||||
* Available in the [PlatformIO registry](http://platformio.org/lib/show/346/AsyncMqttClient)
|
||||
|
||||
## Requirements, installation and usage
|
||||
|
||||
The project is documented in the [/docs folder](docs).
|
||||
587
wled00/src/dependencies/espalexa/Espalexa.h
Normal file
587
wled00/src/dependencies/espalexa/Espalexa.h
Normal file
@@ -0,0 +1,587 @@
|
||||
#ifndef Espalexa_h
|
||||
#define Espalexa_h
|
||||
|
||||
/*
|
||||
* Alexa Voice On/Off/Brightness/Color Control. Emulates a Philips Hue bridge to Alexa.
|
||||
*
|
||||
* This was put together from these two excellent projects:
|
||||
* https://github.com/kakopappa/arduino-esp8266-alexa-wemo-switch
|
||||
* https://github.com/probonopd/ESP8266HueEmulator
|
||||
*/
|
||||
/*
|
||||
* @title Espalexa library
|
||||
* @version 2.4.3
|
||||
* @author Christian Schwinne
|
||||
* @license MIT
|
||||
* @contributors d-999
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
//you can use these defines for library config in your sketch. Just use them before #include <Espalexa.h>
|
||||
//#define ESPALEXA_ASYNC
|
||||
|
||||
//in case this is unwanted in your application (will disable the /espalexa value page)
|
||||
//#define ESPALEXA_NO_SUBPAGE
|
||||
|
||||
#ifndef ESPALEXA_MAXDEVICES
|
||||
#define ESPALEXA_MAXDEVICES 10 //this limit only has memory reasons, set it higher should you need to
|
||||
#endif
|
||||
|
||||
//#define ESPALEXA_DEBUG
|
||||
|
||||
#ifdef ESPALEXA_ASYNC
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <AsyncTCP.h>
|
||||
#else
|
||||
#include <ESPAsyncTCP.h>
|
||||
#endif
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#else
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <WiFi.h>
|
||||
#include <WebServer.h> //if you get an error here please update to ESP32 arduino core 1.0.0
|
||||
#else
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#endif
|
||||
#endif
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
#ifdef ESPALEXA_DEBUG
|
||||
#pragma message "Espalexa 2.4.3 debug mode"
|
||||
#define EA_DEBUG(x) Serial.print (x)
|
||||
#define EA_DEBUGLN(x) Serial.println (x)
|
||||
#else
|
||||
#define EA_DEBUG(x)
|
||||
#define EA_DEBUGLN(x)
|
||||
#endif
|
||||
|
||||
#include "EspalexaDevice.h"
|
||||
|
||||
|
||||
class Espalexa {
|
||||
private:
|
||||
//private member vars
|
||||
#ifdef ESPALEXA_ASYNC
|
||||
AsyncWebServer* serverAsync;
|
||||
AsyncWebServerRequest* server; //this saves many #defines
|
||||
String body = "";
|
||||
#elif defined ARDUINO_ARCH_ESP32
|
||||
WebServer* server;
|
||||
#else
|
||||
ESP8266WebServer* server;
|
||||
#endif
|
||||
uint8_t currentDeviceCount = 0;
|
||||
bool discoverable = true;
|
||||
|
||||
EspalexaDevice* devices[ESPALEXA_MAXDEVICES] = {};
|
||||
//Keep in mind that Device IDs go from 1 to DEVICES, cpp arrays from 0 to DEVICES-1!!
|
||||
|
||||
WiFiUDP espalexaUdp;
|
||||
IPAddress ipMulti;
|
||||
bool udpConnected = false;
|
||||
char packetBuffer[255]; //buffer to hold incoming udp packet
|
||||
String escapedMac=""; //lowercase mac address
|
||||
|
||||
//private member functions
|
||||
String boolString(bool st)
|
||||
{
|
||||
return(st)?"true":"false";
|
||||
}
|
||||
|
||||
String modeString(EspalexaColorMode m)
|
||||
{
|
||||
if (m == EspalexaColorMode::xy) return "xy";
|
||||
if (m == EspalexaColorMode::hs) return "hs";
|
||||
return "ct";
|
||||
}
|
||||
|
||||
String typeString(EspalexaDeviceType t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case EspalexaDeviceType::dimmable: return "Dimmable light";
|
||||
case EspalexaDeviceType::whitespectrum: return "Color temperature light";
|
||||
case EspalexaDeviceType::color: return "Color light";
|
||||
case EspalexaDeviceType::extendedcolor: return "Extended color light";
|
||||
}
|
||||
return "Light";
|
||||
}
|
||||
|
||||
String modelidString(EspalexaDeviceType t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case EspalexaDeviceType::dimmable: return "LWB010";
|
||||
case EspalexaDeviceType::whitespectrum: return "LWT010";
|
||||
case EspalexaDeviceType::color: return "LST001";
|
||||
case EspalexaDeviceType::extendedcolor: return "LCT015";
|
||||
}
|
||||
return "Plug";
|
||||
}
|
||||
|
||||
//Workaround functions courtesy of Sonoff-Tasmota
|
||||
uint32_t encodeLightId(uint8_t idx)
|
||||
{
|
||||
uint8_t mac[6];
|
||||
WiFi.macAddress(mac);
|
||||
uint32_t id = (mac[3] << 20) | (mac[4] << 12) | (mac[5] << 4) | (idx & 0xF);
|
||||
return id;
|
||||
}
|
||||
|
||||
uint32_t decodeLightId(uint32_t id) {
|
||||
return id & 0xF;
|
||||
}
|
||||
|
||||
//device JSON string: color+temperature device emulates LCT015, dimmable device LWB010, (TODO: on/off Plug 01, color temperature device LWT010, color device LST001)
|
||||
String deviceJsonString(uint8_t deviceId)
|
||||
{
|
||||
deviceId--;
|
||||
if (deviceId >= currentDeviceCount) return "{}"; //error
|
||||
EspalexaDevice* dev = devices[deviceId];
|
||||
|
||||
String json = "{\"state\":{\"on\":";
|
||||
json += boolString(dev->getValue());
|
||||
if (dev->getType() != EspalexaDeviceType::onoff) //bri support
|
||||
{
|
||||
json += ",\"bri\":" + String(dev->getLastValue()-1);
|
||||
if (static_cast<uint8_t>(dev->getType()) > 2) //color support
|
||||
{
|
||||
json += ",\"hue\":" + String(dev->getHue()) + ",\"sat\":" + String(dev->getSat());
|
||||
json += ",\"effect\":\"none\",\"xy\":[" + String(dev->getX()) + "," + String(dev->getY()) + "]";
|
||||
}
|
||||
if (static_cast<uint8_t>(dev->getType()) > 1 && dev->getType() != EspalexaDeviceType::color) //white spectrum support
|
||||
{
|
||||
json += ",\"ct\":" + String(dev->getCt());
|
||||
}
|
||||
}
|
||||
json += ",\"alert\":\"none";
|
||||
if (static_cast<uint8_t>(dev->getType()) > 1) json += "\",\"colormode\":\"" + modeString(dev->getColorMode());
|
||||
json += "\",\"mode\":\"homeautomation\",\"reachable\":true},";
|
||||
json += "\"type\":\"" + typeString(dev->getType());
|
||||
json += "\",\"name\":\"" + dev->getName();
|
||||
json += "\",\"modelid\":\"" + modelidString(dev->getType());
|
||||
json += "\",\"manufacturername\":\"Philips\",\"productname\":\"E" + String(static_cast<uint8_t>(dev->getType()));
|
||||
json += "\",\"uniqueid\":\"" + String(encodeLightId(deviceId+1));
|
||||
json += "\",\"swversion\":\"espalexa-2.4.3\"}";
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
//Espalexa status page /espalexa
|
||||
#ifndef ESPALEXA_NO_SUBPAGE
|
||||
void servePage()
|
||||
{
|
||||
EA_DEBUGLN("HTTP Req espalexa ...\n");
|
||||
String res = "Hello from Espalexa!\r\n\r\n";
|
||||
for (int i=0; i<currentDeviceCount; i++)
|
||||
{
|
||||
EspalexaDevice* dev = devices[i];
|
||||
res += "Value of device " + String(i+1) + " (" + dev->getName() + "): " + String(dev->getValue()) + " (" + typeString(dev->getType());
|
||||
if (static_cast<uint8_t>(dev->getType()) > 1) //color support
|
||||
{
|
||||
res += ", colormode=" + modeString(dev->getColorMode()) + ", r=" + String(dev->getR()) + ", g=" + String(dev->getG()) + ", b=" + String(dev->getB());
|
||||
res +=", ct=" + String(dev->getCt()) + ", hue=" + String(dev->getHue()) + ", sat=" + String(dev->getSat()) + ", x=" + String(dev->getX()) + ", y=" + String(dev->getY());
|
||||
}
|
||||
res += ")\r\n";
|
||||
}
|
||||
res += "\r\nFree Heap: " + (String)ESP.getFreeHeap();
|
||||
res += "\r\nUptime: " + (String)millis();
|
||||
res += "\r\n\r\nEspalexa library v2.4.3 by Christian Schwinne 2019";
|
||||
server->send(200, "text/plain", res);
|
||||
}
|
||||
#endif
|
||||
|
||||
//not found URI (only if internal webserver is used)
|
||||
void serveNotFound()
|
||||
{
|
||||
EA_DEBUGLN("Not-Found HTTP call:");
|
||||
#ifndef ESPALEXA_ASYNC
|
||||
EA_DEBUGLN("URI: " + server->uri());
|
||||
EA_DEBUGLN("Body: " + server->arg(0));
|
||||
if(!handleAlexaApiCall(server->uri(), server->arg(0)))
|
||||
#else
|
||||
EA_DEBUGLN("URI: " + server->url());
|
||||
EA_DEBUGLN("Body: " + body);
|
||||
if(!handleAlexaApiCall(server))
|
||||
#endif
|
||||
server->send(404, "text/plain", "Not Found (espalexa-internal)");
|
||||
}
|
||||
|
||||
//send description.xml device property page
|
||||
void serveDescription()
|
||||
{
|
||||
EA_DEBUGLN("# Responding to description.xml ... #\n");
|
||||
IPAddress localIP = WiFi.localIP();
|
||||
char s[16];
|
||||
sprintf(s, "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]);
|
||||
|
||||
String setup_xml = "<?xml version=\"1.0\" ?>"
|
||||
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">"
|
||||
"<specVersion><major>1</major><minor>0</minor></specVersion>"
|
||||
"<URLBase>http://"+ String(s) +":80/</URLBase>"
|
||||
"<device>"
|
||||
"<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>"
|
||||
"<friendlyName>Espalexa ("+ String(s) +")</friendlyName>"
|
||||
"<manufacturer>Royal Philips Electronics</manufacturer>"
|
||||
"<manufacturerURL>http://www.philips.com</manufacturerURL>"
|
||||
"<modelDescription>Philips hue Personal Wireless Lighting</modelDescription>"
|
||||
"<modelName>Philips hue bridge 2012</modelName>"
|
||||
"<modelNumber>929000226503</modelNumber>"
|
||||
"<modelURL>http://www.meethue.com</modelURL>"
|
||||
"<serialNumber>"+ escapedMac +"</serialNumber>"
|
||||
"<UDN>uuid:2f402f80-da50-11e1-9b23-"+ escapedMac +"</UDN>"
|
||||
"<presentationURL>index.html</presentationURL>"
|
||||
"</device>"
|
||||
"</root>";
|
||||
|
||||
server->send(200, "text/xml", setup_xml.c_str());
|
||||
|
||||
EA_DEBUG("Sending :");
|
||||
EA_DEBUGLN(setup_xml);
|
||||
}
|
||||
|
||||
//init the server
|
||||
void startHttpServer()
|
||||
{
|
||||
#ifdef ESPALEXA_ASYNC
|
||||
if (serverAsync == nullptr) {
|
||||
serverAsync = new AsyncWebServer(80);
|
||||
serverAsync->onNotFound([=](AsyncWebServerRequest *request){server = request; serveNotFound();});
|
||||
}
|
||||
|
||||
serverAsync->onRequestBody([=](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){
|
||||
char b[len +1];
|
||||
b[len] = 0;
|
||||
memcpy(b, data, len);
|
||||
body = b; //save the body so we can use it for the API call
|
||||
EA_DEBUG("Received body: ");
|
||||
EA_DEBUGLN(body);
|
||||
});
|
||||
#ifndef ESPALEXA_NO_SUBPAGE
|
||||
serverAsync->on("/espalexa", HTTP_GET, [=](AsyncWebServerRequest *request){server = request; servePage();});
|
||||
#endif
|
||||
serverAsync->on("/description.xml", HTTP_GET, [=](AsyncWebServerRequest *request){server = request; serveDescription();});
|
||||
serverAsync->begin();
|
||||
|
||||
#else
|
||||
if (server == nullptr) {
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
server = new WebServer(80);
|
||||
#else
|
||||
server = new ESP8266WebServer(80);
|
||||
#endif
|
||||
server->onNotFound([=](){serveNotFound();});
|
||||
}
|
||||
|
||||
#ifndef ESPALEXA_NO_SUBPAGE
|
||||
server->on("/espalexa", HTTP_GET, [=](){servePage();});
|
||||
#endif
|
||||
server->on("/description.xml", HTTP_GET, [=](){serveDescription();});
|
||||
server->begin();
|
||||
#endif
|
||||
}
|
||||
|
||||
//respond to UDP SSDP M-SEARCH
|
||||
void respondToSearch()
|
||||
{
|
||||
IPAddress localIP = WiFi.localIP();
|
||||
char s[16];
|
||||
sprintf(s, "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]);
|
||||
|
||||
String response =
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"EXT:\r\n"
|
||||
"CACHE-CONTROL: max-age=100\r\n" // SSDP_INTERVAL
|
||||
"LOCATION: http://"+ String(s) +":80/description.xml\r\n"
|
||||
"SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/1.17.0\r\n" // _modelName, _modelNumber
|
||||
"hue-bridgeid: "+ escapedMac +"\r\n"
|
||||
"ST: urn:schemas-upnp-org:device:basic:1\r\n" // _deviceType
|
||||
"USN: uuid:2f402f80-da50-11e1-9b23-"+ escapedMac +"::upnp:rootdevice\r\n" // _uuid::_deviceType
|
||||
"\r\n";
|
||||
|
||||
espalexaUdp.beginPacket(espalexaUdp.remoteIP(), espalexaUdp.remotePort());
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
espalexaUdp.write((uint8_t*)response.c_str(), response.length());
|
||||
#else
|
||||
espalexaUdp.write(response.c_str());
|
||||
#endif
|
||||
espalexaUdp.endPacket();
|
||||
}
|
||||
|
||||
public:
|
||||
Espalexa(){}
|
||||
|
||||
//initialize interfaces
|
||||
#ifdef ESPALEXA_ASYNC
|
||||
bool begin(AsyncWebServer* externalServer = nullptr)
|
||||
#elif defined ARDUINO_ARCH_ESP32
|
||||
bool begin(WebServer* externalServer = nullptr)
|
||||
#else
|
||||
bool begin(ESP8266WebServer* externalServer = nullptr)
|
||||
#endif
|
||||
{
|
||||
EA_DEBUGLN("Espalexa Begin...");
|
||||
EA_DEBUG("MAXDEVICES ");
|
||||
EA_DEBUGLN(ESPALEXA_MAXDEVICES);
|
||||
escapedMac = WiFi.macAddress();
|
||||
escapedMac.replace(":", "");
|
||||
escapedMac.toLowerCase();
|
||||
|
||||
#ifdef ESPALEXA_ASYNC
|
||||
serverAsync = externalServer;
|
||||
#else
|
||||
server = externalServer;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
udpConnected = espalexaUdp.beginMulticast(IPAddress(239, 255, 255, 250), 1900);
|
||||
#else
|
||||
udpConnected = espalexaUdp.beginMulticast(WiFi.localIP(), IPAddress(239, 255, 255, 250), 1900);
|
||||
#endif
|
||||
|
||||
if (udpConnected){
|
||||
|
||||
startHttpServer();
|
||||
EA_DEBUGLN("Done");
|
||||
return true;
|
||||
}
|
||||
EA_DEBUGLN("Failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
//service loop
|
||||
void loop() {
|
||||
#ifndef ESPALEXA_ASYNC
|
||||
if (server == nullptr) return; //only if begin() was not called
|
||||
server->handleClient();
|
||||
#endif
|
||||
|
||||
if (!udpConnected) return;
|
||||
int packetSize = espalexaUdp.parsePacket();
|
||||
if (!packetSize) return; //no new udp packet
|
||||
|
||||
EA_DEBUGLN("Got UDP!");
|
||||
int len = espalexaUdp.read(packetBuffer, 254);
|
||||
if (len > 0) {
|
||||
packetBuffer[len] = 0;
|
||||
}
|
||||
espalexaUdp.flush();
|
||||
if (!discoverable) return; //do not reply to M-SEARCH if not discoverable
|
||||
|
||||
String request = packetBuffer;
|
||||
if(request.indexOf("M-SEARCH") >= 0) {
|
||||
EA_DEBUGLN(request);
|
||||
if(request.indexOf("upnp:rootdevice") > 0 || request.indexOf("asic:1") > 0) {
|
||||
EA_DEBUGLN("Responding search req...");
|
||||
respondToSearch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool addDevice(EspalexaDevice* d)
|
||||
{
|
||||
EA_DEBUG("Adding device ");
|
||||
EA_DEBUGLN((currentDeviceCount+1));
|
||||
if (currentDeviceCount >= ESPALEXA_MAXDEVICES) return false;
|
||||
if (d == nullptr) return false;
|
||||
d->setId(currentDeviceCount);
|
||||
devices[currentDeviceCount] = d;
|
||||
currentDeviceCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
//brightness-only callback
|
||||
bool addDevice(String deviceName, BrightnessCallbackFunction callback, uint8_t initialValue = 0)
|
||||
{
|
||||
EA_DEBUG("Constructing device ");
|
||||
EA_DEBUGLN((currentDeviceCount+1));
|
||||
if (currentDeviceCount >= ESPALEXA_MAXDEVICES) return false;
|
||||
EspalexaDevice* d = new EspalexaDevice(deviceName, callback, initialValue);
|
||||
return addDevice(d);
|
||||
}
|
||||
|
||||
//brightness-only callback
|
||||
bool addDevice(String deviceName, ColorCallbackFunction callback, uint8_t initialValue = 0)
|
||||
{
|
||||
EA_DEBUG("Constructing device ");
|
||||
EA_DEBUGLN((currentDeviceCount+1));
|
||||
if (currentDeviceCount >= ESPALEXA_MAXDEVICES) return false;
|
||||
EspalexaDevice* d = new EspalexaDevice(deviceName, callback, initialValue);
|
||||
return addDevice(d);
|
||||
}
|
||||
|
||||
|
||||
bool addDevice(String deviceName, DeviceCallbackFunction callback, EspalexaDeviceType t = EspalexaDeviceType::dimmable, uint8_t initialValue = 0)
|
||||
{
|
||||
EA_DEBUG("Constructing device ");
|
||||
EA_DEBUGLN((currentDeviceCount+1));
|
||||
if (currentDeviceCount >= ESPALEXA_MAXDEVICES) return false;
|
||||
EspalexaDevice* d = new EspalexaDevice(deviceName, callback, t, initialValue);
|
||||
return addDevice(d);
|
||||
}
|
||||
|
||||
//basic implementation of Philips hue api functions needed for basic Alexa control
|
||||
#ifdef ESPALEXA_ASYNC
|
||||
bool handleAlexaApiCall(AsyncWebServerRequest* request)
|
||||
{
|
||||
server = request; //copy request reference
|
||||
String req = request->url(); //body from global variable
|
||||
EA_DEBUGLN(request->contentType());
|
||||
if (request->hasParam("body", true)) // This is necessary, otherwise ESP crashes if there is no body
|
||||
{
|
||||
EA_DEBUG("BodyMethod2");
|
||||
body = request->getParam("body", true)->value();
|
||||
}
|
||||
EA_DEBUG("FinalBody: ");
|
||||
EA_DEBUGLN(body);
|
||||
#else
|
||||
bool handleAlexaApiCall(String req, String body)
|
||||
{
|
||||
#endif
|
||||
EA_DEBUGLN("AlexaApiCall");
|
||||
if (req.indexOf("api") <0) return false; //return if not an API call
|
||||
EA_DEBUGLN("ok");
|
||||
|
||||
if (body.indexOf("devicetype") > 0) //client wants a hue api username, we dont care and give static
|
||||
{
|
||||
EA_DEBUGLN("devType");
|
||||
body = "";
|
||||
server->send(200, "application/json", "[{\"success\":{\"username\":\"2WLEDHardQrI3WHYTHoMcXHgEspsM8ZZRpSKtBQr\"}}]");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (req.indexOf("state") > 0) //client wants to control light
|
||||
{
|
||||
server->send(200, "application/json", "[{\"success\":true}]"); //short valid response
|
||||
|
||||
uint32_t devId = req.substring(req.indexOf("lights")+7).toInt();
|
||||
EA_DEBUG("ls"); EA_DEBUGLN(devId);
|
||||
devId = decodeLightId(devId);
|
||||
EA_DEBUGLN(devId);
|
||||
devId--; //zero-based for devices array
|
||||
if (devId >= currentDeviceCount) return true; //return if invalid ID
|
||||
|
||||
devices[devId]->setPropertyChanged(EspalexaDeviceProperty::none);
|
||||
|
||||
if (body.indexOf("false")>0) //OFF command
|
||||
{
|
||||
devices[devId]->setValue(0);
|
||||
devices[devId]->setPropertyChanged(EspalexaDeviceProperty::off);
|
||||
devices[devId]->doCallback();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (body.indexOf("true") >0) //ON command
|
||||
{
|
||||
devices[devId]->setValue(devices[devId]->getLastValue());
|
||||
devices[devId]->setPropertyChanged(EspalexaDeviceProperty::on);
|
||||
}
|
||||
|
||||
if (body.indexOf("bri") >0) //BRIGHTNESS command
|
||||
{
|
||||
uint8_t briL = body.substring(body.indexOf("bri") +5).toInt();
|
||||
if (briL == 255)
|
||||
{
|
||||
devices[devId]->setValue(255);
|
||||
} else {
|
||||
devices[devId]->setValue(briL+1);
|
||||
}
|
||||
devices[devId]->setPropertyChanged(EspalexaDeviceProperty::bri);
|
||||
}
|
||||
|
||||
if (body.indexOf("xy") >0) //COLOR command (XY mode)
|
||||
{
|
||||
devices[devId]->setColorXY(body.substring(body.indexOf("[") +1).toFloat(), body.substring(body.indexOf(",0") +1).toFloat());
|
||||
devices[devId]->setPropertyChanged(EspalexaDeviceProperty::xy);
|
||||
}
|
||||
|
||||
if (body.indexOf("hue") >0) //COLOR command (HS mode)
|
||||
{
|
||||
devices[devId]->setColor(body.substring(body.indexOf("hue") +5).toInt(), body.substring(body.indexOf("sat") +5).toInt());
|
||||
devices[devId]->setPropertyChanged(EspalexaDeviceProperty::hs);
|
||||
}
|
||||
|
||||
if (body.indexOf("ct") >0) //COLOR TEMP command (white spectrum)
|
||||
{
|
||||
devices[devId]->setColor(body.substring(body.indexOf("ct") +4).toInt());
|
||||
devices[devId]->setPropertyChanged(EspalexaDeviceProperty::ct);
|
||||
}
|
||||
|
||||
devices[devId]->doCallback();
|
||||
|
||||
#ifdef ESPALEXA_DEBUG
|
||||
if (devices[devId]->getLastChangedProperty() == EspalexaDeviceProperty::none)
|
||||
EA_DEBUGLN("STATE REQ WITHOUT BODY (likely Content-Type issue #6)");
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
int pos = req.indexOf("lights");
|
||||
if (pos > 0) //client wants light info
|
||||
{
|
||||
int devId = req.substring(pos+7).toInt();
|
||||
EA_DEBUG("l"); EA_DEBUGLN(devId);
|
||||
|
||||
if (devId == 0) //client wants all lights
|
||||
{
|
||||
EA_DEBUGLN("lAll");
|
||||
String jsonTemp = "{";
|
||||
for (int i = 0; i<currentDeviceCount; i++)
|
||||
{
|
||||
jsonTemp += "\"" + String(encodeLightId(i+1)) + "\":";
|
||||
jsonTemp += deviceJsonString(i+1);
|
||||
if (i < currentDeviceCount-1) jsonTemp += ",";
|
||||
}
|
||||
jsonTemp += "}";
|
||||
server->send(200, "application/json", jsonTemp);
|
||||
} else //client wants one light (devId)
|
||||
{
|
||||
devId = decodeLightId(devId);
|
||||
EA_DEBUGLN(devId);
|
||||
if (devId > currentDeviceCount)
|
||||
{
|
||||
server->send(200, "application/json", "{}");
|
||||
} else {
|
||||
server->send(200, "application/json", deviceJsonString(devId));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//we dont care about other api commands at this time and send empty JSON
|
||||
server->send(200, "application/json", "{}");
|
||||
return true;
|
||||
}
|
||||
|
||||
//set whether Alexa can discover any devices
|
||||
void setDiscoverable(bool d)
|
||||
{
|
||||
discoverable = d;
|
||||
}
|
||||
|
||||
//get EspalexaDevice at specific index
|
||||
EspalexaDevice* getDevice(uint8_t index)
|
||||
{
|
||||
if (index >= currentDeviceCount) return nullptr;
|
||||
return devices[index];
|
||||
}
|
||||
|
||||
//is an unique device ID
|
||||
String getEscapedMac()
|
||||
{
|
||||
return escapedMac;
|
||||
}
|
||||
|
||||
//convert brightness (0-255) to percentage
|
||||
uint8_t toPercent(uint8_t bri)
|
||||
{
|
||||
uint16_t perc = bri * 100;
|
||||
return perc / 255;
|
||||
}
|
||||
|
||||
~Espalexa(){delete devices;} //note: Espalexa is NOT meant to be destructed
|
||||
};
|
||||
|
||||
#endif
|
||||
320
wled00/src/dependencies/espalexa/EspalexaDevice.cpp
Normal file
320
wled00/src/dependencies/espalexa/EspalexaDevice.cpp
Normal file
@@ -0,0 +1,320 @@
|
||||
//EspalexaDevice Class
|
||||
|
||||
#include "EspalexaDevice.h"
|
||||
|
||||
EspalexaDevice::EspalexaDevice(){}
|
||||
|
||||
EspalexaDevice::EspalexaDevice(String deviceName, BrightnessCallbackFunction gnCallback, uint8_t initialValue) { //constructor for dimmable device
|
||||
|
||||
_deviceName = deviceName;
|
||||
_callback = gnCallback;
|
||||
_val = initialValue;
|
||||
_val_last = _val;
|
||||
_type = EspalexaDeviceType::dimmable;
|
||||
}
|
||||
|
||||
EspalexaDevice::EspalexaDevice(String deviceName, ColorCallbackFunction gnCallback, uint8_t initialValue) { //constructor for color device
|
||||
|
||||
_deviceName = deviceName;
|
||||
_callbackCol = gnCallback;
|
||||
_val = initialValue;
|
||||
_val_last = _val;
|
||||
_type = EspalexaDeviceType::extendedcolor;
|
||||
}
|
||||
|
||||
EspalexaDevice::EspalexaDevice(String deviceName, DeviceCallbackFunction gnCallback, EspalexaDeviceType t, uint8_t initialValue) { //constructor for general device
|
||||
|
||||
_deviceName = deviceName;
|
||||
_callbackDev = gnCallback;
|
||||
_type = t;
|
||||
if (t == EspalexaDeviceType::onoff) _type = EspalexaDeviceType::dimmable; //on/off is broken, so make dimmable device instead
|
||||
_val = initialValue;
|
||||
_val_last = _val;
|
||||
}
|
||||
|
||||
EspalexaDevice::~EspalexaDevice(){/*nothing to destruct*/}
|
||||
|
||||
uint8_t EspalexaDevice::getId()
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
|
||||
EspalexaColorMode EspalexaDevice::getColorMode()
|
||||
{
|
||||
return _mode;
|
||||
}
|
||||
|
||||
EspalexaDeviceType EspalexaDevice::getType()
|
||||
{
|
||||
return _type;
|
||||
}
|
||||
|
||||
String EspalexaDevice::getName()
|
||||
{
|
||||
return _deviceName;
|
||||
}
|
||||
|
||||
EspalexaDeviceProperty EspalexaDevice::getLastChangedProperty()
|
||||
{
|
||||
return _changed;
|
||||
}
|
||||
|
||||
uint8_t EspalexaDevice::getValue()
|
||||
{
|
||||
return _val;
|
||||
}
|
||||
|
||||
uint8_t EspalexaDevice::getPercent()
|
||||
{
|
||||
uint16_t perc = _val * 100;
|
||||
return perc / 255;
|
||||
}
|
||||
|
||||
uint8_t EspalexaDevice::getDegrees()
|
||||
{
|
||||
return getPercent();
|
||||
}
|
||||
|
||||
uint16_t EspalexaDevice::getHue()
|
||||
{
|
||||
return _hue;
|
||||
}
|
||||
|
||||
uint8_t EspalexaDevice::getSat()
|
||||
{
|
||||
return _sat;
|
||||
}
|
||||
|
||||
float EspalexaDevice::getX()
|
||||
{
|
||||
return _x;
|
||||
}
|
||||
|
||||
float EspalexaDevice::getY()
|
||||
{
|
||||
return _y;
|
||||
}
|
||||
|
||||
uint16_t EspalexaDevice::getCt()
|
||||
{
|
||||
if (_ct == 0) return 500;
|
||||
return _ct;
|
||||
}
|
||||
|
||||
uint32_t EspalexaDevice::getKelvin()
|
||||
{
|
||||
if (_ct == 0) return 2000;
|
||||
return 1000000/_ct;
|
||||
}
|
||||
|
||||
uint32_t EspalexaDevice::getRGB()
|
||||
{
|
||||
if (_rgb != 0) return _rgb; //color has not changed
|
||||
uint8_t rgb[3];
|
||||
float r, g, b;
|
||||
|
||||
if (_mode == EspalexaColorMode::none) return 0;
|
||||
|
||||
if (_mode == EspalexaColorMode::ct)
|
||||
{
|
||||
//TODO tweak a bit to match hue lamp characteristics
|
||||
//based on https://gist.github.com/paulkaplan/5184275
|
||||
float temp = 10000/ _ct; //kelvins = 1,000,000/mired (and that /100)
|
||||
float r, g, b;
|
||||
|
||||
if( temp <= 66 ){
|
||||
r = 255;
|
||||
g = temp;
|
||||
g = 99.470802 * log(g) - 161.119568;
|
||||
if( temp <= 19){
|
||||
b = 0;
|
||||
} else {
|
||||
b = temp-10;
|
||||
b = 138.517731 * log(b) - 305.044793;
|
||||
}
|
||||
} else {
|
||||
r = temp - 60;
|
||||
r = 329.698727 * pow(r, -0.13320476);
|
||||
g = temp - 60;
|
||||
g = 288.12217 * pow(g, -0.07551485 );
|
||||
b = 255;
|
||||
}
|
||||
|
||||
rgb[0] = (byte)constrain(r,0.1,255.1);
|
||||
rgb[1] = (byte)constrain(g,0.1,255.1);
|
||||
rgb[2] = (byte)constrain(b,0.1,255.1);
|
||||
} else if (_mode == EspalexaColorMode::hs)
|
||||
{
|
||||
float h = ((float)_hue)/65535.0;
|
||||
float s = ((float)_sat)/255.0;
|
||||
byte i = floor(h*6);
|
||||
float f = h * 6-i;
|
||||
float p = 255 * (1-s);
|
||||
float q = 255 * (1-f*s);
|
||||
float t = 255 * (1-(1-f)*s);
|
||||
switch (i%6) {
|
||||
case 0: rgb[0]=255,rgb[1]=t,rgb[2]=p;break;
|
||||
case 1: rgb[0]=q,rgb[1]=255,rgb[2]=p;break;
|
||||
case 2: rgb[0]=p,rgb[1]=255,rgb[2]=t;break;
|
||||
case 3: rgb[0]=p,rgb[1]=q,rgb[2]=255;break;
|
||||
case 4: rgb[0]=t,rgb[1]=p,rgb[2]=255;break;
|
||||
case 5: rgb[0]=255,rgb[1]=p,rgb[2]=q;
|
||||
}
|
||||
} else if (_mode == EspalexaColorMode::xy)
|
||||
{
|
||||
//Source: https://www.developers.meethue.com/documentation/color-conversions-rgb-xy
|
||||
float z = 1.0f - _x - _y;
|
||||
float X = (1.0f / _y) * _x;
|
||||
float Z = (1.0f / _y) * z;
|
||||
float r = (int)255*(X * 1.656492f - 0.354851f - Z * 0.255038f);
|
||||
float g = (int)255*(-X * 0.707196f + 1.655397f + Z * 0.036152f);
|
||||
float b = (int)255*(X * 0.051713f - 0.121364f + Z * 1.011530f);
|
||||
if (r > b && r > g && r > 1.0f) {
|
||||
// red is too big
|
||||
g = g / r;
|
||||
b = b / r;
|
||||
r = 1.0f;
|
||||
} else if (g > b && g > r && g > 1.0f) {
|
||||
// green is too big
|
||||
r = r / g;
|
||||
b = b / g;
|
||||
g = 1.0f;
|
||||
} else if (b > r && b > g && b > 1.0f) {
|
||||
// blue is too big
|
||||
r = r / b;
|
||||
g = g / b;
|
||||
b = 1.0f;
|
||||
}
|
||||
// Apply gamma correction
|
||||
r = r <= 0.0031308f ? 12.92f * r : (1.0f + 0.055f) * pow(r, (1.0f / 2.4f)) - 0.055f;
|
||||
g = g <= 0.0031308f ? 12.92f * g : (1.0f + 0.055f) * pow(g, (1.0f / 2.4f)) - 0.055f;
|
||||
b = b <= 0.0031308f ? 12.92f * b : (1.0f + 0.055f) * pow(b, (1.0f / 2.4f)) - 0.055f;
|
||||
|
||||
if (r > b && r > g) {
|
||||
// red is biggest
|
||||
if (r > 1.0f) {
|
||||
g = g / r;
|
||||
b = b / r;
|
||||
r = 1.0f;
|
||||
}
|
||||
} else if (g > b && g > r) {
|
||||
// green is biggest
|
||||
if (g > 1.0f) {
|
||||
r = r / g;
|
||||
b = b / g;
|
||||
g = 1.0f;
|
||||
}
|
||||
} else if (b > r && b > g) {
|
||||
// blue is biggest
|
||||
if (b > 1.0f) {
|
||||
r = r / b;
|
||||
g = g / b;
|
||||
b = 1.0f;
|
||||
}
|
||||
}
|
||||
rgb[0] = 255.0*r;
|
||||
rgb[1] = 255.0*g;
|
||||
rgb[2] = 255.0*b;
|
||||
}
|
||||
_rgb = ((rgb[0] << 16) | (rgb[1] << 8) | (rgb[2]));
|
||||
return _rgb;
|
||||
}
|
||||
|
||||
uint8_t EspalexaDevice::getR()
|
||||
{
|
||||
return (getRGB() >> 16) & 0xFF;
|
||||
}
|
||||
|
||||
uint8_t EspalexaDevice::getG()
|
||||
{
|
||||
return (getRGB() >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
uint8_t EspalexaDevice::getB()
|
||||
{
|
||||
return getRGB() & 0xFF;
|
||||
}
|
||||
|
||||
uint8_t EspalexaDevice::getLastValue()
|
||||
{
|
||||
if (_val_last == 0) return 255;
|
||||
return _val_last;
|
||||
}
|
||||
|
||||
void EspalexaDevice::setPropertyChanged(EspalexaDeviceProperty p)
|
||||
{
|
||||
_changed = p;
|
||||
}
|
||||
|
||||
void EspalexaDevice::setId(uint8_t id)
|
||||
{
|
||||
_id = id;
|
||||
}
|
||||
|
||||
//you need to re-discover the device for the Alexa name to change
|
||||
void EspalexaDevice::setName(String name)
|
||||
{
|
||||
_deviceName = name;
|
||||
}
|
||||
|
||||
void EspalexaDevice::setValue(uint8_t val)
|
||||
{
|
||||
if (_val != 0)
|
||||
{
|
||||
_val_last = _val;
|
||||
}
|
||||
if (val != 0)
|
||||
{
|
||||
_val_last = val;
|
||||
}
|
||||
_val = val;
|
||||
}
|
||||
|
||||
void EspalexaDevice::setPercent(uint8_t perc)
|
||||
{
|
||||
uint16_t val = perc * 255;
|
||||
val /= 100;
|
||||
if (val > 255) val = 255;
|
||||
setValue(val);
|
||||
}
|
||||
|
||||
void EspalexaDevice::setColorXY(float x, float y)
|
||||
{
|
||||
_x = x;
|
||||
_y = y;
|
||||
_rgb = 0;
|
||||
_mode = EspalexaColorMode::xy;
|
||||
}
|
||||
|
||||
void EspalexaDevice::setColor(uint16_t hue, uint8_t sat)
|
||||
{
|
||||
_hue = hue;
|
||||
_sat = sat;
|
||||
_rgb = 0;
|
||||
_mode = EspalexaColorMode::hs;
|
||||
}
|
||||
|
||||
void EspalexaDevice::setColor(uint16_t ct)
|
||||
{
|
||||
_ct = ct;
|
||||
_rgb = 0;
|
||||
_mode =EspalexaColorMode::ct;
|
||||
}
|
||||
|
||||
void EspalexaDevice::setColor(uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
float X = r * 0.664511f + g * 0.154324f + b * 0.162028f;
|
||||
float Y = r * 0.283881f + g * 0.668433f + b * 0.047685f;
|
||||
float Z = r * 0.000088f + g * 0.072310f + b * 0.986039f;
|
||||
_x = X / (X + Y + Z);
|
||||
_y = Y / (X + Y + Z);
|
||||
_rgb = ((r << 16) | (g << 8) | b);
|
||||
_mode = EspalexaColorMode::xy;
|
||||
}
|
||||
|
||||
void EspalexaDevice::doCallback()
|
||||
{
|
||||
if (_callback != nullptr) {_callback(_val); return;}
|
||||
if (_callbackDev != nullptr) {_callbackDev(this); return;}
|
||||
if (_callbackCol != nullptr) _callbackCol(_val, getRGB());
|
||||
}
|
||||
72
wled00/src/dependencies/espalexa/EspalexaDevice.h
Normal file
72
wled00/src/dependencies/espalexa/EspalexaDevice.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifndef EspalexaDevice_h
|
||||
#define EspalexaDevice_h
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
typedef class EspalexaDevice;
|
||||
|
||||
typedef void (*BrightnessCallbackFunction) (uint8_t b);
|
||||
typedef void (*DeviceCallbackFunction) (EspalexaDevice* d);
|
||||
typedef void (*ColorCallbackFunction) (uint8_t br, uint32_t col);
|
||||
|
||||
enum class EspalexaColorMode : uint8_t { none = 0, ct = 1, hs = 2, xy = 3 };
|
||||
enum class EspalexaDeviceType : uint8_t { onoff = 0, dimmable = 1, whitespectrum = 2, color = 3, extendedcolor = 4 };
|
||||
enum class EspalexaDeviceProperty : uint8_t { none = 0, on = 1, off = 2, bri = 3, hs = 4, ct = 5, xy = 6 };
|
||||
|
||||
class EspalexaDevice {
|
||||
private:
|
||||
String _deviceName;
|
||||
BrightnessCallbackFunction _callback = nullptr;
|
||||
DeviceCallbackFunction _callbackDev = nullptr;
|
||||
ColorCallbackFunction _callbackCol = nullptr;
|
||||
uint8_t _val, _val_last, _sat = 0;
|
||||
uint16_t _hue = 0, _ct = 0;
|
||||
float _x = 0.5, _y = 0.5;
|
||||
uint32_t _rgb = 0;
|
||||
uint8_t _id = 0;
|
||||
EspalexaDeviceType _type;
|
||||
EspalexaDeviceProperty _changed = EspalexaDeviceProperty::none;
|
||||
EspalexaColorMode _mode = EspalexaColorMode::xy;
|
||||
|
||||
public:
|
||||
EspalexaDevice();
|
||||
~EspalexaDevice();
|
||||
EspalexaDevice(String deviceName, BrightnessCallbackFunction bcb, uint8_t initialValue =0);
|
||||
EspalexaDevice(String deviceName, DeviceCallbackFunction dcb, EspalexaDeviceType t =EspalexaDeviceType::dimmable, uint8_t initialValue =0);
|
||||
EspalexaDevice(String deviceName, ColorCallbackFunction ccb, uint8_t initialValue =0);
|
||||
|
||||
String getName();
|
||||
uint8_t getId();
|
||||
EspalexaDeviceProperty getLastChangedProperty();
|
||||
uint8_t getValue();
|
||||
uint8_t getPercent();
|
||||
uint8_t getDegrees();
|
||||
uint16_t getHue();
|
||||
uint8_t getSat();
|
||||
uint16_t getCt();
|
||||
uint32_t getKelvin();
|
||||
float getX();
|
||||
float getY();
|
||||
uint32_t getRGB();
|
||||
uint8_t getR();
|
||||
uint8_t getG();
|
||||
uint8_t getB();
|
||||
EspalexaColorMode getColorMode();
|
||||
EspalexaDeviceType getType();
|
||||
|
||||
void setId(uint8_t id);
|
||||
void setPropertyChanged(EspalexaDeviceProperty p);
|
||||
void setValue(uint8_t bri);
|
||||
void setPercent(uint8_t perc);
|
||||
void setName(String name);
|
||||
void setColor(uint16_t ct);
|
||||
void setColor(uint16_t hue, uint8_t sat);
|
||||
void setColorXY(float x, float y);
|
||||
void setColor(uint8_t r, uint8_t g, uint8_t b);
|
||||
|
||||
void doCallback();
|
||||
|
||||
uint8_t getLastValue(); //last value that was not off (1-255)
|
||||
};
|
||||
|
||||
#endif
|
||||
21
wled00/src/dependencies/espalexa/LICENSE
Normal file
21
wled00/src/dependencies/espalexa/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Christian Schwinne
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
5663
wled00/src/dependencies/json/ArduinoJson-v6.h
Normal file
5663
wled00/src/dependencies/json/ArduinoJson-v6.h
Normal file
File diff suppressed because it is too large
Load Diff
160
wled00/src/dependencies/json/AsyncJson-v6.h
Normal file
160
wled00/src/dependencies/json/AsyncJson-v6.h
Normal file
@@ -0,0 +1,160 @@
|
||||
// AsyncJson-v6.h
|
||||
/*
|
||||
Original file at: https://github.com/baggior/ESPAsyncWebServer/blob/master/src/AsyncJson.h
|
||||
Only changes are ArduinoJson lib path and removed content-type check
|
||||
|
||||
Async Response to use with ArduinoJson and AsyncWebServer
|
||||
Written by Andrew Melvin (SticilFace) with help from me-no-dev and BBlanchon.
|
||||
|
||||
--------------------
|
||||
Async Request to use with ArduinoJson and AsyncWebServer
|
||||
Written by Arsène von Wyss (avonwyss)
|
||||
*/
|
||||
#ifndef ASYNC_JSON_H_
|
||||
#define ASYNC_JSON_H_
|
||||
#include "ArduinoJson-v6.h"
|
||||
#include <Print.h>
|
||||
|
||||
#define DYNAMYC_JSON_DOCUMENT_SIZE 4096
|
||||
|
||||
constexpr const char* JSON_MIMETYPE = "application/json";
|
||||
|
||||
/*
|
||||
* Json Response
|
||||
* */
|
||||
|
||||
class ChunkPrint : public Print {
|
||||
private:
|
||||
uint8_t* _destination;
|
||||
size_t _to_skip;
|
||||
size_t _to_write;
|
||||
size_t _pos;
|
||||
public:
|
||||
ChunkPrint(uint8_t* destination, size_t from, size_t len)
|
||||
: _destination(destination), _to_skip(from), _to_write(len), _pos{0} {}
|
||||
virtual ~ChunkPrint(){}
|
||||
size_t write(uint8_t c){
|
||||
if (_to_skip > 0) {
|
||||
_to_skip--;
|
||||
return 1;
|
||||
} else if (_to_write > 0) {
|
||||
_to_write--;
|
||||
_destination[_pos++] = c;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
size_t write(const uint8_t *buffer, size_t size)
|
||||
{
|
||||
return this->Print::write(buffer, size);
|
||||
}
|
||||
};
|
||||
|
||||
class AsyncJsonResponse: public AsyncAbstractResponse {
|
||||
private:
|
||||
|
||||
DynamicJsonDocument _jsonBuffer;
|
||||
|
||||
JsonVariant _root;
|
||||
bool _isValid;
|
||||
|
||||
public:
|
||||
|
||||
AsyncJsonResponse(size_t maxJsonBufferSize = DYNAMYC_JSON_DOCUMENT_SIZE, bool isArray=false) : _jsonBuffer(maxJsonBufferSize), _isValid{false} {
|
||||
_code = 200;
|
||||
_contentType = JSON_MIMETYPE;
|
||||
if(isArray)
|
||||
_root = _jsonBuffer.createNestedArray();
|
||||
else
|
||||
_root = _jsonBuffer.createNestedObject();
|
||||
}
|
||||
|
||||
~AsyncJsonResponse() {}
|
||||
JsonVariant & getRoot() { return _root; }
|
||||
bool _sourceValid() const { return _isValid; }
|
||||
size_t setLength() {
|
||||
|
||||
_contentLength = measureJson(_root);
|
||||
|
||||
if (_contentLength) { _isValid = true; }
|
||||
return _contentLength;
|
||||
}
|
||||
|
||||
size_t getSize() { return _jsonBuffer.size(); }
|
||||
|
||||
size_t _fillBuffer(uint8_t *data, size_t len){
|
||||
ChunkPrint dest(data, _sentLength, len);
|
||||
|
||||
serializeJson(_root, dest);
|
||||
return len;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::function<void(AsyncWebServerRequest *request, JsonObject json)> ArJsonRequestHandlerFunction;
|
||||
|
||||
class AsyncCallbackJsonWebHandler: public AsyncWebHandler {
|
||||
private:
|
||||
protected:
|
||||
const String _uri;
|
||||
WebRequestMethodComposite _method;
|
||||
ArJsonRequestHandlerFunction _onRequest;
|
||||
int _contentLength;
|
||||
const size_t maxJsonBufferSize;
|
||||
int _maxContentLength;
|
||||
public:
|
||||
|
||||
AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize=DYNAMYC_JSON_DOCUMENT_SIZE)
|
||||
: _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {}
|
||||
|
||||
void setMethod(WebRequestMethodComposite method){ _method = method; }
|
||||
void setMaxContentLength(int maxContentLength){ _maxContentLength = maxContentLength; }
|
||||
void onRequest(ArJsonRequestHandlerFunction fn){ _onRequest = fn; }
|
||||
|
||||
virtual bool canHandle(AsyncWebServerRequest *request) override final{
|
||||
if(!_onRequest)
|
||||
return false;
|
||||
|
||||
if(!(_method & request->method()))
|
||||
return false;
|
||||
|
||||
if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/")))
|
||||
return false;
|
||||
|
||||
request->addInterestingHeader("ANY");
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void handleRequest(AsyncWebServerRequest *request) override final {
|
||||
if(_onRequest) {
|
||||
if (request->_tempObject != NULL) {
|
||||
|
||||
DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize);
|
||||
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject));
|
||||
if(!error) {
|
||||
JsonObject json = jsonBuffer.as<JsonObject>();
|
||||
|
||||
_onRequest(request, json);
|
||||
return;
|
||||
}
|
||||
}
|
||||
request->send(_contentLength > _maxContentLength ? 413 : 400);
|
||||
} else {
|
||||
request->send(500);
|
||||
}
|
||||
}
|
||||
virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final {
|
||||
}
|
||||
virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final {
|
||||
if (_onRequest) {
|
||||
_contentLength = total;
|
||||
if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) {
|
||||
request->_tempObject = malloc(total);
|
||||
}
|
||||
if (request->_tempObject != NULL) {
|
||||
memcpy((uint8_t*)(request->_tempObject) + index, data, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;}
|
||||
};
|
||||
#endif
|
||||
@@ -1,20 +0,0 @@
|
||||
Copyright (c) 2008-2015 Nicholas O'Leary
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,601 +0,0 @@
|
||||
/*
|
||||
PubSubClient.cpp - A simple client for MQTT.
|
||||
Nick O'Leary
|
||||
http://knolleary.net
|
||||
*/
|
||||
|
||||
#include "PubSubClient.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
PubSubClient::PubSubClient() {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
this->_client = NULL;
|
||||
this->stream = NULL;
|
||||
setCallback(NULL);
|
||||
}
|
||||
|
||||
PubSubClient::PubSubClient(Client& client) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
}
|
||||
|
||||
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(addr, port);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
}
|
||||
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client, Stream& stream) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(addr,port);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
}
|
||||
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(addr, port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
}
|
||||
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(addr,port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
}
|
||||
|
||||
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(ip, port);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
}
|
||||
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client, Stream& stream) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(ip,port);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
}
|
||||
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(ip, port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
}
|
||||
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(ip,port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
}
|
||||
|
||||
PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(domain,port);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
}
|
||||
PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client, Stream& stream) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(domain,port);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
}
|
||||
PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(domain,port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
}
|
||||
PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(domain,port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
}
|
||||
|
||||
boolean PubSubClient::connect(const char *id) {
|
||||
return connect(id,NULL,NULL,0,0,0,0);
|
||||
}
|
||||
|
||||
boolean PubSubClient::connect(const char *id, const char *user, const char *pass) {
|
||||
return connect(id,user,pass,0,0,0,0);
|
||||
}
|
||||
|
||||
boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
|
||||
return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage);
|
||||
}
|
||||
|
||||
boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
|
||||
if (!connected()) {
|
||||
int result = 0;
|
||||
|
||||
if (domain != NULL) {
|
||||
result = _client->connect(this->domain, this->port);
|
||||
} else {
|
||||
result = _client->connect(this->ip, this->port);
|
||||
}
|
||||
if (result == 1) {
|
||||
nextMsgId = 1;
|
||||
// Leave room in the buffer for header and variable length field
|
||||
uint16_t length = 5;
|
||||
unsigned int j;
|
||||
|
||||
#if MQTT_VERSION == MQTT_VERSION_3_1
|
||||
uint8_t d[9] = {0x00,0x06,'M','Q','I','s','d','p', MQTT_VERSION};
|
||||
#define MQTT_HEADER_VERSION_LENGTH 9
|
||||
#elif MQTT_VERSION == MQTT_VERSION_3_1_1
|
||||
uint8_t d[7] = {0x00,0x04,'M','Q','T','T',MQTT_VERSION};
|
||||
#define MQTT_HEADER_VERSION_LENGTH 7
|
||||
#endif
|
||||
for (j = 0;j<MQTT_HEADER_VERSION_LENGTH;j++) {
|
||||
buffer[length++] = d[j];
|
||||
}
|
||||
|
||||
uint8_t v;
|
||||
if (willTopic) {
|
||||
v = 0x06|(willQos<<3)|(willRetain<<5);
|
||||
} else {
|
||||
v = 0x02;
|
||||
}
|
||||
|
||||
if(user != NULL) {
|
||||
v = v|0x80;
|
||||
|
||||
if(pass != NULL) {
|
||||
v = v|(0x80>>1);
|
||||
}
|
||||
}
|
||||
|
||||
buffer[length++] = v;
|
||||
|
||||
buffer[length++] = ((MQTT_KEEPALIVE) >> 8);
|
||||
buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF);
|
||||
length = writeString(id,buffer,length);
|
||||
if (willTopic) {
|
||||
length = writeString(willTopic,buffer,length);
|
||||
length = writeString(willMessage,buffer,length);
|
||||
}
|
||||
|
||||
if(user != NULL) {
|
||||
length = writeString(user,buffer,length);
|
||||
if(pass != NULL) {
|
||||
length = writeString(pass,buffer,length);
|
||||
}
|
||||
}
|
||||
|
||||
write(MQTTCONNECT,buffer,length-5);
|
||||
|
||||
lastInActivity = lastOutActivity = millis();
|
||||
|
||||
while (!_client->available()) {
|
||||
unsigned long t = millis();
|
||||
if (t-lastInActivity >= ((int32_t) MQTT_SOCKET_TIMEOUT*1000UL)) {
|
||||
_state = MQTT_CONNECTION_TIMEOUT;
|
||||
_client->stop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
uint8_t llen;
|
||||
uint16_t len = readPacket(&llen);
|
||||
|
||||
if (len == 4) {
|
||||
if (buffer[3] == 0) {
|
||||
lastInActivity = millis();
|
||||
pingOutstanding = false;
|
||||
_state = MQTT_CONNECTED;
|
||||
return true;
|
||||
} else {
|
||||
_state = buffer[3];
|
||||
}
|
||||
}
|
||||
_client->stop();
|
||||
} else {
|
||||
_state = MQTT_CONNECT_FAILED;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// reads a byte into result
|
||||
boolean PubSubClient::readByte(uint8_t * result) {
|
||||
uint32_t previousMillis = millis();
|
||||
while(!_client->available()) {
|
||||
uint32_t currentMillis = millis();
|
||||
if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*result = _client->read();
|
||||
return true;
|
||||
}
|
||||
|
||||
// reads a byte into result[*index] and increments index
|
||||
boolean PubSubClient::readByte(uint8_t * result, uint16_t * index){
|
||||
uint16_t current_index = *index;
|
||||
uint8_t * write_address = &(result[current_index]);
|
||||
if(readByte(write_address)){
|
||||
*index = current_index + 1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t PubSubClient::readPacket(uint8_t* lengthLength) {
|
||||
uint16_t len = 0;
|
||||
if(!readByte(buffer, &len)) return 0;
|
||||
bool isPublish = (buffer[0]&0xF0) == MQTTPUBLISH;
|
||||
uint32_t multiplier = 1;
|
||||
uint16_t length = 0;
|
||||
uint8_t digit = 0;
|
||||
uint16_t skip = 0;
|
||||
uint8_t start = 0;
|
||||
|
||||
do {
|
||||
if (len == 6) {
|
||||
// Invalid remaining length encoding - kill the connection
|
||||
_state = MQTT_DISCONNECTED;
|
||||
_client->stop();
|
||||
return 0;
|
||||
}
|
||||
if(!readByte(&digit)) return 0;
|
||||
buffer[len++] = digit;
|
||||
length += (digit & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
} while ((digit & 128) != 0);
|
||||
*lengthLength = len-1;
|
||||
|
||||
if (isPublish) {
|
||||
// Read in topic length to calculate bytes to skip over for Stream writing
|
||||
if(!readByte(buffer, &len)) return 0;
|
||||
if(!readByte(buffer, &len)) return 0;
|
||||
skip = (buffer[*lengthLength+1]<<8)+buffer[*lengthLength+2];
|
||||
start = 2;
|
||||
if (buffer[0]&MQTTQOS1) {
|
||||
// skip message id
|
||||
skip += 2;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint16_t i = start;i<length;i++) {
|
||||
if(!readByte(&digit)) return 0;
|
||||
if (this->stream) {
|
||||
if (isPublish && len-*lengthLength-2>skip) {
|
||||
this->stream->write(digit);
|
||||
}
|
||||
}
|
||||
if (len < MQTT_MAX_PACKET_SIZE) {
|
||||
buffer[len] = digit;
|
||||
}
|
||||
len++;
|
||||
}
|
||||
|
||||
if (!this->stream && len > MQTT_MAX_PACKET_SIZE) {
|
||||
len = 0; // This will cause the packet to be ignored.
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
boolean PubSubClient::loop() {
|
||||
if (connected()) {
|
||||
unsigned long t = millis();
|
||||
if ((t - lastInActivity > MQTT_KEEPALIVE*1000UL) || (t - lastOutActivity > MQTT_KEEPALIVE*1000UL)) {
|
||||
if (pingOutstanding) {
|
||||
this->_state = MQTT_CONNECTION_TIMEOUT;
|
||||
_client->stop();
|
||||
return false;
|
||||
} else {
|
||||
buffer[0] = MQTTPINGREQ;
|
||||
buffer[1] = 0;
|
||||
_client->write(buffer,2);
|
||||
lastOutActivity = t;
|
||||
lastInActivity = t;
|
||||
pingOutstanding = true;
|
||||
}
|
||||
}
|
||||
if (_client->available()) {
|
||||
uint8_t llen;
|
||||
uint16_t len = readPacket(&llen);
|
||||
uint16_t msgId = 0;
|
||||
uint8_t *payload;
|
||||
if (len > 0) {
|
||||
lastInActivity = t;
|
||||
uint8_t type = buffer[0]&0xF0;
|
||||
if (type == MQTTPUBLISH) {
|
||||
if (callback) {
|
||||
uint16_t tl = (buffer[llen+1]<<8)+buffer[llen+2]; /* topic length in bytes */
|
||||
memmove(buffer+llen+2,buffer+llen+3,tl); /* move topic inside buffer 1 byte to front */
|
||||
buffer[llen+2+tl] = 0; /* end the topic as a 'C' string with \x00 */
|
||||
char *topic = (char*) buffer+llen+2;
|
||||
|
||||
// make sure payload can be interpreted as 'C' string
|
||||
buffer[(len < MQTT_MAX_PACKET_SIZE) ? len : MQTT_MAX_PACKET_SIZE -1] = 0;
|
||||
|
||||
// msgId only present for QOS>0
|
||||
if ((buffer[0]&0x06) == MQTTQOS1) {
|
||||
msgId = (buffer[llen+3+tl]<<8)+buffer[llen+3+tl+1];
|
||||
payload = buffer+llen+3+tl+2;
|
||||
callback(topic,payload,len-llen-3-tl-2);
|
||||
|
||||
buffer[0] = MQTTPUBACK;
|
||||
buffer[1] = 2;
|
||||
buffer[2] = (msgId >> 8);
|
||||
buffer[3] = (msgId & 0xFF);
|
||||
_client->write(buffer,4);
|
||||
lastOutActivity = t;
|
||||
|
||||
} else {
|
||||
payload = buffer+llen+3+tl;
|
||||
callback(topic,payload,len-llen-3-tl);
|
||||
}
|
||||
}
|
||||
} else if (type == MQTTPINGREQ) {
|
||||
buffer[0] = MQTTPINGRESP;
|
||||
buffer[1] = 0;
|
||||
_client->write(buffer,2);
|
||||
} else if (type == MQTTPINGRESP) {
|
||||
pingOutstanding = false;
|
||||
}
|
||||
} else if (!connected()) {
|
||||
// readPacket has closed the connection
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean PubSubClient::publish(const char* topic, const char* payload) {
|
||||
return publish(topic,(const uint8_t*)payload,strlen(payload),false);
|
||||
}
|
||||
|
||||
boolean PubSubClient::publish(const char* topic, const char* payload, boolean retained) {
|
||||
return publish(topic,(const uint8_t*)payload,strlen(payload),retained);
|
||||
}
|
||||
|
||||
boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) {
|
||||
return publish(topic, payload, plength, false);
|
||||
}
|
||||
|
||||
boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
|
||||
if (connected()) {
|
||||
if (MQTT_MAX_PACKET_SIZE < 5 + 2+strlen(topic) + plength) {
|
||||
// Too long
|
||||
return false;
|
||||
}
|
||||
// Leave room in the buffer for header and variable length field
|
||||
uint16_t length = 5;
|
||||
length = writeString(topic,buffer,length);
|
||||
uint16_t i;
|
||||
for (i=0;i<plength;i++) {
|
||||
buffer[length++] = payload[i];
|
||||
}
|
||||
uint8_t header = MQTTPUBLISH;
|
||||
if (retained) {
|
||||
header |= 1;
|
||||
}
|
||||
return write(header,buffer,length-5);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean PubSubClient::publish_P(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
|
||||
uint8_t llen = 0;
|
||||
uint8_t digit;
|
||||
unsigned int rc = 0;
|
||||
uint16_t tlen;
|
||||
unsigned int pos = 0;
|
||||
unsigned int i;
|
||||
uint8_t header;
|
||||
unsigned int len;
|
||||
|
||||
if (!connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tlen = strlen(topic);
|
||||
|
||||
header = MQTTPUBLISH;
|
||||
if (retained) {
|
||||
header |= 1;
|
||||
}
|
||||
buffer[pos++] = header;
|
||||
len = plength + 2 + tlen;
|
||||
do {
|
||||
digit = len % 128;
|
||||
len = len / 128;
|
||||
if (len > 0) {
|
||||
digit |= 0x80;
|
||||
}
|
||||
buffer[pos++] = digit;
|
||||
llen++;
|
||||
} while(len>0);
|
||||
|
||||
pos = writeString(topic,buffer,pos);
|
||||
|
||||
rc += _client->write(buffer,pos);
|
||||
|
||||
for (i=0;i<plength;i++) {
|
||||
rc += _client->write((char)pgm_read_byte_near(payload + i));
|
||||
}
|
||||
|
||||
lastOutActivity = millis();
|
||||
|
||||
return rc == tlen + 4 + plength;
|
||||
}
|
||||
|
||||
boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) {
|
||||
uint8_t lenBuf[4];
|
||||
uint8_t llen = 0;
|
||||
uint8_t digit;
|
||||
uint8_t pos = 0;
|
||||
uint16_t rc;
|
||||
uint16_t len = length;
|
||||
do {
|
||||
digit = len % 128;
|
||||
len = len / 128;
|
||||
if (len > 0) {
|
||||
digit |= 0x80;
|
||||
}
|
||||
lenBuf[pos++] = digit;
|
||||
llen++;
|
||||
} while(len>0);
|
||||
|
||||
buf[4-llen] = header;
|
||||
for (int i=0;i<llen;i++) {
|
||||
buf[5-llen+i] = lenBuf[i];
|
||||
}
|
||||
|
||||
#ifdef MQTT_MAX_TRANSFER_SIZE
|
||||
uint8_t* writeBuf = buf+(4-llen);
|
||||
uint16_t bytesRemaining = length+1+llen; //Match the length type
|
||||
uint8_t bytesToWrite;
|
||||
boolean result = true;
|
||||
while((bytesRemaining > 0) && result) {
|
||||
bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining;
|
||||
rc = _client->write(writeBuf,bytesToWrite);
|
||||
result = (rc == bytesToWrite);
|
||||
bytesRemaining -= rc;
|
||||
writeBuf += rc;
|
||||
}
|
||||
return result;
|
||||
#else
|
||||
rc = _client->write(buf+(4-llen),length+1+llen);
|
||||
lastOutActivity = millis();
|
||||
return (rc == 1+llen+length);
|
||||
#endif
|
||||
}
|
||||
|
||||
boolean PubSubClient::subscribe(const char* topic) {
|
||||
return subscribe(topic, 0);
|
||||
}
|
||||
|
||||
boolean PubSubClient::subscribe(const char* topic, uint8_t qos) {
|
||||
if (qos > 1) {
|
||||
return false;
|
||||
}
|
||||
if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
|
||||
// Too long
|
||||
return false;
|
||||
}
|
||||
if (connected()) {
|
||||
// Leave room in the buffer for header and variable length field
|
||||
uint16_t length = 5;
|
||||
nextMsgId++;
|
||||
if (nextMsgId == 0) {
|
||||
nextMsgId = 1;
|
||||
}
|
||||
buffer[length++] = (nextMsgId >> 8);
|
||||
buffer[length++] = (nextMsgId & 0xFF);
|
||||
length = writeString((char*)topic, buffer,length);
|
||||
buffer[length++] = qos;
|
||||
return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-5);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean PubSubClient::unsubscribe(const char* topic) {
|
||||
if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
|
||||
// Too long
|
||||
return false;
|
||||
}
|
||||
if (connected()) {
|
||||
uint16_t length = 5;
|
||||
nextMsgId++;
|
||||
if (nextMsgId == 0) {
|
||||
nextMsgId = 1;
|
||||
}
|
||||
buffer[length++] = (nextMsgId >> 8);
|
||||
buffer[length++] = (nextMsgId & 0xFF);
|
||||
length = writeString(topic, buffer,length);
|
||||
return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-5);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PubSubClient::disconnect() {
|
||||
buffer[0] = MQTTDISCONNECT;
|
||||
buffer[1] = 0;
|
||||
_client->write(buffer,2);
|
||||
_state = MQTT_DISCONNECTED;
|
||||
_client->stop();
|
||||
lastInActivity = lastOutActivity = millis();
|
||||
}
|
||||
|
||||
uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, uint16_t pos) {
|
||||
const char* idp = string;
|
||||
uint16_t i = 0;
|
||||
pos += 2;
|
||||
while (*idp) {
|
||||
buf[pos++] = *idp++;
|
||||
i++;
|
||||
}
|
||||
buf[pos-i-2] = (i >> 8);
|
||||
buf[pos-i-1] = (i & 0xFF);
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
boolean PubSubClient::connected() {
|
||||
boolean rc;
|
||||
if (_client == NULL ) {
|
||||
rc = false;
|
||||
} else {
|
||||
rc = (int)_client->connected();
|
||||
if (!rc) {
|
||||
if (this->_state == MQTT_CONNECTED) {
|
||||
this->_state = MQTT_CONNECTION_LOST;
|
||||
_client->flush();
|
||||
_client->stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port) {
|
||||
IPAddress addr(ip[0],ip[1],ip[2],ip[3]);
|
||||
return setServer(addr,port);
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setServer(IPAddress ip, uint16_t port) {
|
||||
this->ip = ip;
|
||||
this->port = port;
|
||||
this->domain = NULL;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port) {
|
||||
this->domain = domain;
|
||||
this->port = port;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE) {
|
||||
this->callback = callback;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setClient(Client& client){
|
||||
this->_client = &client;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setStream(Stream& stream){
|
||||
this->stream = &stream;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int PubSubClient::state() {
|
||||
return this->_state;
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
PubSubClient.h - A simple client for MQTT.
|
||||
Nick O'Leary
|
||||
http://knolleary.net
|
||||
*/
|
||||
|
||||
#ifndef PubSubClient_h
|
||||
#define PubSubClient_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "IPAddress.h"
|
||||
#include "Client.h"
|
||||
#include "Stream.h"
|
||||
|
||||
#define MQTT_VERSION_3_1 3
|
||||
#define MQTT_VERSION_3_1_1 4
|
||||
|
||||
// MQTT_VERSION : Pick the version
|
||||
//#define MQTT_VERSION MQTT_VERSION_3_1
|
||||
#ifndef MQTT_VERSION
|
||||
#define MQTT_VERSION MQTT_VERSION_3_1_1
|
||||
#endif
|
||||
|
||||
// MQTT_MAX_PACKET_SIZE : Maximum packet size
|
||||
#ifndef MQTT_MAX_PACKET_SIZE
|
||||
#define MQTT_MAX_PACKET_SIZE 128
|
||||
#endif
|
||||
|
||||
// MQTT_KEEPALIVE : keepAlive interval in Seconds
|
||||
#ifndef MQTT_KEEPALIVE
|
||||
#define MQTT_KEEPALIVE 60
|
||||
#endif
|
||||
|
||||
// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds
|
||||
#ifndef MQTT_SOCKET_TIMEOUT
|
||||
#define MQTT_SOCKET_TIMEOUT 62
|
||||
#endif
|
||||
|
||||
// MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client
|
||||
// in each write call. Needed for the Arduino Wifi Shield. Leave undefined to
|
||||
// pass the entire MQTT packet in each write call.
|
||||
//#define MQTT_MAX_TRANSFER_SIZE 80
|
||||
|
||||
// Possible values for client.state()
|
||||
#define MQTT_CONNECTION_TIMEOUT -4
|
||||
#define MQTT_CONNECTION_LOST -3
|
||||
#define MQTT_CONNECT_FAILED -2
|
||||
#define MQTT_DISCONNECTED -1
|
||||
#define MQTT_CONNECTED 0
|
||||
#define MQTT_CONNECT_BAD_PROTOCOL 1
|
||||
#define MQTT_CONNECT_BAD_CLIENT_ID 2
|
||||
#define MQTT_CONNECT_UNAVAILABLE 3
|
||||
#define MQTT_CONNECT_BAD_CREDENTIALS 4
|
||||
#define MQTT_CONNECT_UNAUTHORIZED 5
|
||||
|
||||
#define MQTTCONNECT 1 << 4 // Client request to connect to Server
|
||||
#define MQTTCONNACK 2 << 4 // Connect Acknowledgment
|
||||
#define MQTTPUBLISH 3 << 4 // Publish message
|
||||
#define MQTTPUBACK 4 << 4 // Publish Acknowledgment
|
||||
#define MQTTPUBREC 5 << 4 // Publish Received (assured delivery part 1)
|
||||
#define MQTTPUBREL 6 << 4 // Publish Release (assured delivery part 2)
|
||||
#define MQTTPUBCOMP 7 << 4 // Publish Complete (assured delivery part 3)
|
||||
#define MQTTSUBSCRIBE 8 << 4 // Client Subscribe request
|
||||
#define MQTTSUBACK 9 << 4 // Subscribe Acknowledgment
|
||||
#define MQTTUNSUBSCRIBE 10 << 4 // Client Unsubscribe request
|
||||
#define MQTTUNSUBACK 11 << 4 // Unsubscribe Acknowledgment
|
||||
#define MQTTPINGREQ 12 << 4 // PING Request
|
||||
#define MQTTPINGRESP 13 << 4 // PING Response
|
||||
#define MQTTDISCONNECT 14 << 4 // Client is Disconnecting
|
||||
#define MQTTReserved 15 << 4 // Reserved
|
||||
|
||||
#define MQTTQOS0 (0 << 1)
|
||||
#define MQTTQOS1 (1 << 1)
|
||||
#define MQTTQOS2 (2 << 1)
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <functional>
|
||||
#define MQTT_CALLBACK_SIGNATURE std::function<void(char*, uint8_t*, unsigned int)> callback
|
||||
#else
|
||||
#define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int)
|
||||
#endif
|
||||
|
||||
class PubSubClient {
|
||||
private:
|
||||
Client* _client;
|
||||
uint8_t buffer[MQTT_MAX_PACKET_SIZE];
|
||||
uint16_t nextMsgId;
|
||||
unsigned long lastOutActivity;
|
||||
unsigned long lastInActivity;
|
||||
bool pingOutstanding;
|
||||
MQTT_CALLBACK_SIGNATURE;
|
||||
uint16_t readPacket(uint8_t*);
|
||||
boolean readByte(uint8_t * result);
|
||||
boolean readByte(uint8_t * result, uint16_t * index);
|
||||
boolean write(uint8_t header, uint8_t* buf, uint16_t length);
|
||||
uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos);
|
||||
IPAddress ip;
|
||||
const char* domain;
|
||||
uint16_t port;
|
||||
Stream* stream;
|
||||
int _state;
|
||||
public:
|
||||
PubSubClient();
|
||||
PubSubClient(Client& client);
|
||||
PubSubClient(IPAddress, uint16_t, Client& client);
|
||||
PubSubClient(IPAddress, uint16_t, Client& client, Stream&);
|
||||
PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
|
||||
PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
|
||||
PubSubClient(uint8_t *, uint16_t, Client& client);
|
||||
PubSubClient(uint8_t *, uint16_t, Client& client, Stream&);
|
||||
PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
|
||||
PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
|
||||
PubSubClient(const char*, uint16_t, Client& client);
|
||||
PubSubClient(const char*, uint16_t, Client& client, Stream&);
|
||||
PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
|
||||
PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
|
||||
|
||||
PubSubClient& setServer(IPAddress ip, uint16_t port);
|
||||
PubSubClient& setServer(uint8_t * ip, uint16_t port);
|
||||
PubSubClient& setServer(const char * domain, uint16_t port);
|
||||
PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE);
|
||||
PubSubClient& setClient(Client& client);
|
||||
PubSubClient& setStream(Stream& stream);
|
||||
|
||||
boolean connect(const char* id);
|
||||
boolean connect(const char* id, const char* user, const char* pass);
|
||||
boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
|
||||
boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
|
||||
void disconnect();
|
||||
boolean publish(const char* topic, const char* payload);
|
||||
boolean publish(const char* topic, const char* payload, boolean retained);
|
||||
boolean publish(const char* topic, const uint8_t * payload, unsigned int plength);
|
||||
boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
|
||||
boolean publish_P(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
|
||||
boolean subscribe(const char* topic);
|
||||
boolean subscribe(const char* topic, uint8_t qos);
|
||||
boolean unsubscribe(const char* topic);
|
||||
boolean loop();
|
||||
boolean connected();
|
||||
int state();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -16,7 +16,8 @@
|
||||
#else
|
||||
#include <WProgram.h>
|
||||
#endif
|
||||
#include <Time.h> //http://www.arduino.cc/playground/Code/Time
|
||||
|
||||
#include "../time/Time.h" //http://www.arduino.cc/playground/Code/Time
|
||||
|
||||
//convenient constants for dstRules
|
||||
enum week_t {Last, First, Second, Third, Fourth};
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
#include <Arduino.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiServer.h>
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include "WebServer.h"
|
||||
#include <Update.h>
|
||||
#else
|
||||
#include <ESP8266WebServer.h>
|
||||
#endif
|
||||
#include <WiFiUdp.h>
|
||||
#include "ESP8266HTTPUpdateServer.h"
|
||||
|
||||
|
||||
const char* ESP8266HTTPUpdateServer::_serverIndex =
|
||||
R"(<html><head><script>function B(){window.history.back()}</script></head><body><h2>WLED Software Update</h2><br>Get the latest binaries on the <a href="https://github.com/Aircoookie/WLED/tree/master/bin">project GitHub page</a>!<br>
|
||||
<i>Unsure which binary is correct? Go to the <a href="./build">/build subpage</a> for the details of this version.</i><br>
|
||||
<b>Be sure to upload a valid .bin file for your ESP! Otherwise you'll need USB recovery!</b><br>
|
||||
<br><br><button onclick='B()'>Back</button><br><br>
|
||||
<form method='POST' action='' enctype='multipart/form-data'>
|
||||
<input type='file' name='update'>
|
||||
<input type='submit' value='Update!'>
|
||||
</form>
|
||||
</body></html>)";
|
||||
const char* ESP8266HTTPUpdateServer::_failedResponse = R"(Update Failed!)";
|
||||
const char* ESP8266HTTPUpdateServer::_successResponse = R"(<script>setTimeout(function(){top.location.href="/";},20000);</script>Update Successful! Rebooting, please wait for redirect...)";
|
||||
|
||||
ESP8266HTTPUpdateServer::ESP8266HTTPUpdateServer(bool serial_debug)
|
||||
{
|
||||
_serial_output = serial_debug;
|
||||
_server = NULL;
|
||||
_username = NULL;
|
||||
_password = NULL;
|
||||
_authenticated = false;
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
void ESP8266HTTPUpdateServer::setup(WebServer *server, const char * path, const char * username, const char * password)
|
||||
#else
|
||||
void ESP8266HTTPUpdateServer::setup(ESP8266WebServer *server, const char * path, const char * username, const char * password)
|
||||
#endif
|
||||
{
|
||||
_server = server;
|
||||
_username = (char *)username;
|
||||
_password = (char *)password;
|
||||
|
||||
// handler for the /update form page
|
||||
_server->on(path, HTTP_GET, [&](){
|
||||
if(_username != NULL && _password != NULL && !_server->authenticate(_username, _password))
|
||||
return _server->requestAuthentication();
|
||||
_server->send(200, "text/html", _serverIndex);
|
||||
});
|
||||
|
||||
// handler for the /update form POST (once file upload finishes)
|
||||
_server->on(path, HTTP_POST, [&](){
|
||||
if(!_authenticated)
|
||||
return _server->requestAuthentication();
|
||||
_server->send(200, "text/html", Update.hasError() ? _failedResponse : _successResponse);
|
||||
ESP.restart();
|
||||
},[&](){
|
||||
// handler for the file upload, get's the sketch bytes, and writes
|
||||
// them through the Update object
|
||||
HTTPUpload& upload = _server->upload();
|
||||
if(upload.status == UPLOAD_FILE_START){
|
||||
if (_serial_output)
|
||||
Serial.setDebugOutput(true);
|
||||
|
||||
_authenticated = (_username == NULL || _password == NULL || _server->authenticate(_username, _password));
|
||||
if(!_authenticated){
|
||||
if (_serial_output)
|
||||
Serial.printf("Unauthenticated Update\n");
|
||||
return;
|
||||
}
|
||||
#ifndef ARDUINO_ARCH_ESP32
|
||||
WiFiUDP::stopAll();
|
||||
#endif
|
||||
if (_serial_output)
|
||||
Serial.printf("Update: %s\n", upload.filename.c_str());
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
uint32_t maxSketchSpace = 0x100000; //dirty workaround, limit to 1MB
|
||||
#else
|
||||
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
|
||||
#endif
|
||||
if(!Update.begin(maxSketchSpace)){//start with max available size
|
||||
if (_serial_output) Update.printError(Serial);
|
||||
}
|
||||
} else if(_authenticated && upload.status == UPLOAD_FILE_WRITE){
|
||||
if (_serial_output) Serial.printf(".");
|
||||
if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){
|
||||
if (_serial_output) Update.printError(Serial);
|
||||
|
||||
}
|
||||
} else if(_authenticated && upload.status == UPLOAD_FILE_END){
|
||||
if(Update.end(true)){ //true to set the size to the current progress
|
||||
if (_serial_output) Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
|
||||
} else {
|
||||
if (_serial_output) Update.printError(Serial);
|
||||
}
|
||||
if (_serial_output) Serial.setDebugOutput(false);
|
||||
} else if(_authenticated && upload.status == UPLOAD_FILE_ABORTED){
|
||||
Update.end();
|
||||
if (_serial_output) Serial.println("Update was aborted");
|
||||
}
|
||||
delay(0);
|
||||
});
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
#ifndef __HTTP_UPDATE_SERVER_H
|
||||
#define __HTTP_UPDATE_SERVER_H
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
class WebServer;
|
||||
|
||||
class ESP8266HTTPUpdateServer
|
||||
{
|
||||
private:
|
||||
bool _serial_output;
|
||||
WebServer *_server;
|
||||
static const char *_serverIndex;
|
||||
static const char *_failedResponse;
|
||||
static const char *_successResponse;
|
||||
char * _username;
|
||||
char * _password;
|
||||
bool _authenticated;
|
||||
public:
|
||||
ESP8266HTTPUpdateServer(bool serial_debug=false);
|
||||
|
||||
void setup(WebServer *server)
|
||||
{
|
||||
setup(server, NULL, NULL);
|
||||
}
|
||||
|
||||
void setup(WebServer *server, const char * path)
|
||||
{
|
||||
setup(server, path, NULL, NULL);
|
||||
}
|
||||
|
||||
void setup(WebServer *server, const char * username, const char * password)
|
||||
{
|
||||
setup(server, "/update", username, password);
|
||||
}
|
||||
|
||||
void setup(WebServer *server, const char * path, const char * username, const char * password);
|
||||
};
|
||||
#else
|
||||
class ESP8266WebServer;
|
||||
|
||||
class ESP8266HTTPUpdateServer
|
||||
{
|
||||
private:
|
||||
bool _serial_output;
|
||||
ESP8266WebServer *_server;
|
||||
static const char *_serverIndex;
|
||||
static const char *_failedResponse;
|
||||
static const char *_successResponse;
|
||||
char * _username;
|
||||
char * _password;
|
||||
bool _authenticated;
|
||||
public:
|
||||
ESP8266HTTPUpdateServer(bool serial_debug=false);
|
||||
|
||||
void setup(ESP8266WebServer *server)
|
||||
{
|
||||
setup(server, NULL, NULL);
|
||||
}
|
||||
|
||||
void setup(ESP8266WebServer *server, const char * path)
|
||||
{
|
||||
setup(server, path, NULL, NULL);
|
||||
}
|
||||
|
||||
void setup(ESP8266WebServer *server, const char * username, const char * password)
|
||||
{
|
||||
setup(server, "/update", username, password);
|
||||
}
|
||||
|
||||
void setup(ESP8266WebServer *server, const char * path, const char * username, const char * password);
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
ESP8266WebServer.h - Dead simple web-server.
|
||||
Supports only one simultaneous client, knows how to handle GET and POST.
|
||||
|
||||
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ESP8266WEBSERVER_H
|
||||
#define ESP8266WEBSERVER_H
|
||||
|
||||
#include "WebServer.h"
|
||||
|
||||
#endif //ESP8266WEBSERVER_H
|
||||
@@ -1,504 +0,0 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
(This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.)
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
{description}
|
||||
Copyright (C) {year} {fullname}
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random
|
||||
Hacker.
|
||||
|
||||
{signature of Ty Coon}, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
@@ -1,613 +0,0 @@
|
||||
/*
|
||||
Parsing.cpp - HTTP request parsing.
|
||||
|
||||
Copyright (c) 2015 Ivan Grokhotkov. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#ifdef ARDUINO_ARCH_ESP32 //only use this library if building for ESP32
|
||||
|
||||
#include "WiFiServer.h"
|
||||
#include "WiFiClient.h"
|
||||
#include "WebServer.h"
|
||||
|
||||
//#define DEBUG_ESP_HTTP_SERVER
|
||||
#ifdef DEBUG_ESP_PORT
|
||||
#define DEBUG_OUTPUT DEBUG_ESP_PORT
|
||||
#else
|
||||
#define DEBUG_OUTPUT Serial
|
||||
#endif
|
||||
|
||||
static char* readBytesWithTimeout(WiFiClient& client, size_t maxLength, size_t& dataLength, int timeout_ms)
|
||||
{
|
||||
char *buf = nullptr;
|
||||
dataLength = 0;
|
||||
while (dataLength < maxLength) {
|
||||
int tries = timeout_ms;
|
||||
size_t newLength;
|
||||
while (!(newLength = client.available()) && tries--) delay(1);
|
||||
if (!newLength) {
|
||||
break;
|
||||
}
|
||||
if (!buf) {
|
||||
buf = (char *) malloc(newLength + 1);
|
||||
if (!buf) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
char* newBuf = (char *) realloc(buf, dataLength + newLength + 1);
|
||||
if (!newBuf) {
|
||||
free(buf);
|
||||
return nullptr;
|
||||
}
|
||||
buf = newBuf;
|
||||
}
|
||||
client.readBytes(buf + dataLength, newLength);
|
||||
dataLength += newLength;
|
||||
buf[dataLength] = '\0';
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool WebServer::_parseRequest(WiFiClient& client) {
|
||||
// Read the first line of HTTP request
|
||||
String req = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
//reset header value
|
||||
for (int i = 0; i < _headerKeysCount; ++i) {
|
||||
_currentHeaders[i].value =String();
|
||||
}
|
||||
|
||||
// First line of HTTP request looks like "GET /path HTTP/1.1"
|
||||
// Retrieve the "/path" part by finding the spaces
|
||||
int addr_start = req.indexOf(' ');
|
||||
int addr_end = req.indexOf(' ', addr_start + 1);
|
||||
if (addr_start == -1 || addr_end == -1) {
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("Invalid request: ");
|
||||
DEBUG_OUTPUT.println(req);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
String methodStr = req.substring(0, addr_start);
|
||||
String url = req.substring(addr_start + 1, addr_end);
|
||||
String versionEnd = req.substring(addr_end + 8);
|
||||
_currentVersion = atoi(versionEnd.c_str());
|
||||
String searchStr = "";
|
||||
int hasSearch = url.indexOf('?');
|
||||
if (hasSearch != -1){
|
||||
searchStr = url.substring(hasSearch + 1);
|
||||
url = url.substring(0, hasSearch);
|
||||
}
|
||||
_currentUri = url;
|
||||
_chunked = false;
|
||||
|
||||
HTTPMethod method = HTTP_GET;
|
||||
if (methodStr == "POST") {
|
||||
method = HTTP_POST;
|
||||
} else if (methodStr == "DELETE") {
|
||||
method = HTTP_DELETE;
|
||||
} else if (methodStr == "OPTIONS") {
|
||||
method = HTTP_OPTIONS;
|
||||
} else if (methodStr == "PUT") {
|
||||
method = HTTP_PUT;
|
||||
} else if (methodStr == "PATCH") {
|
||||
method = HTTP_PATCH;
|
||||
}
|
||||
_currentMethod = method;
|
||||
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("method: ");
|
||||
DEBUG_OUTPUT.print(methodStr);
|
||||
DEBUG_OUTPUT.print(" url: ");
|
||||
DEBUG_OUTPUT.print(url);
|
||||
DEBUG_OUTPUT.print(" search: ");
|
||||
DEBUG_OUTPUT.println(searchStr);
|
||||
#endif
|
||||
|
||||
//attach handler
|
||||
RequestHandler* handler;
|
||||
for (handler = _firstHandler; handler; handler = handler->next()) {
|
||||
if (handler->canHandle(_currentMethod, _currentUri))
|
||||
break;
|
||||
}
|
||||
_currentHandler = handler;
|
||||
|
||||
String formData;
|
||||
// below is needed only when POST type request
|
||||
if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){
|
||||
String boundaryStr;
|
||||
String headerName;
|
||||
String headerValue;
|
||||
bool isForm = false;
|
||||
bool isEncoded = false;
|
||||
uint32_t contentLength = 0;
|
||||
//parse headers
|
||||
while(1){
|
||||
req = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (req == "") break;//no moar headers
|
||||
int headerDiv = req.indexOf(':');
|
||||
if (headerDiv == -1){
|
||||
break;
|
||||
}
|
||||
headerName = req.substring(0, headerDiv);
|
||||
headerValue = req.substring(headerDiv + 1);
|
||||
headerValue.trim();
|
||||
_collectHeader(headerName.c_str(),headerValue.c_str());
|
||||
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("headerName: ");
|
||||
DEBUG_OUTPUT.println(headerName);
|
||||
DEBUG_OUTPUT.print("headerValue: ");
|
||||
DEBUG_OUTPUT.println(headerValue);
|
||||
#endif
|
||||
|
||||
if (headerName.equalsIgnoreCase("Content-Type")){
|
||||
if (headerValue.startsWith("text/plain")){
|
||||
isForm = false;
|
||||
} else if (headerValue.startsWith("application/x-www-form-urlencoded")){
|
||||
isForm = false;
|
||||
isEncoded = true;
|
||||
} else if (headerValue.startsWith("multipart/")){
|
||||
boundaryStr = headerValue.substring(headerValue.indexOf('=')+1);
|
||||
isForm = true;
|
||||
}
|
||||
} else if (headerName.equalsIgnoreCase("Content-Length")){
|
||||
contentLength = headerValue.toInt();
|
||||
} else if (headerName.equalsIgnoreCase("Host")){
|
||||
_hostHeader = headerValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isForm){
|
||||
size_t plainLength;
|
||||
char* plainBuf = readBytesWithTimeout(client, contentLength, plainLength, HTTP_MAX_POST_WAIT);
|
||||
if (plainLength < contentLength) {
|
||||
free(plainBuf);
|
||||
return false;
|
||||
}
|
||||
if (contentLength > 0) {
|
||||
if (searchStr != "") searchStr += '&';
|
||||
if(isEncoded){
|
||||
//url encoded form
|
||||
String decoded = urlDecode(plainBuf);
|
||||
size_t decodedLen = decoded.length();
|
||||
memcpy(plainBuf, decoded.c_str(), decodedLen);
|
||||
plainBuf[decodedLen] = 0;
|
||||
searchStr += plainBuf;
|
||||
}
|
||||
_parseArguments(searchStr);
|
||||
if(!isEncoded){
|
||||
//plain post json or other data
|
||||
RequestArgument& arg = _currentArgs[_currentArgCount++];
|
||||
arg.key = "plain";
|
||||
arg.value = String(plainBuf);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("Plain: ");
|
||||
DEBUG_OUTPUT.println(plainBuf);
|
||||
#endif
|
||||
free(plainBuf);
|
||||
} else {
|
||||
// No content - but we can still have arguments in the URL.
|
||||
_parseArguments(searchStr);
|
||||
}
|
||||
}
|
||||
|
||||
if (isForm){
|
||||
_parseArguments(searchStr);
|
||||
if (!_parseForm(client, boundaryStr, contentLength)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String headerName;
|
||||
String headerValue;
|
||||
//parse headers
|
||||
while(1){
|
||||
req = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (req == "") break;//no moar headers
|
||||
int headerDiv = req.indexOf(':');
|
||||
if (headerDiv == -1){
|
||||
break;
|
||||
}
|
||||
headerName = req.substring(0, headerDiv);
|
||||
headerValue = req.substring(headerDiv + 2);
|
||||
_collectHeader(headerName.c_str(),headerValue.c_str());
|
||||
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("headerName: ");
|
||||
DEBUG_OUTPUT.println(headerName);
|
||||
DEBUG_OUTPUT.print("headerValue: ");
|
||||
DEBUG_OUTPUT.println(headerValue);
|
||||
#endif
|
||||
|
||||
if (headerName.equalsIgnoreCase("Host")){
|
||||
_hostHeader = headerValue;
|
||||
}
|
||||
}
|
||||
_parseArguments(searchStr);
|
||||
}
|
||||
client.flush();
|
||||
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("Request: ");
|
||||
DEBUG_OUTPUT.println(url);
|
||||
DEBUG_OUTPUT.print(" Arguments: ");
|
||||
DEBUG_OUTPUT.println(searchStr);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebServer::_collectHeader(const char* headerName, const char* headerValue) {
|
||||
for (int i = 0; i < _headerKeysCount; i++) {
|
||||
if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) {
|
||||
_currentHeaders[i].value=headerValue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WebServer::_parseArguments(String data) {
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("args: ");
|
||||
DEBUG_OUTPUT.println(data);
|
||||
#endif
|
||||
if (_currentArgs)
|
||||
delete[] _currentArgs;
|
||||
_currentArgs = 0;
|
||||
if (data.length() == 0) {
|
||||
_currentArgCount = 0;
|
||||
_currentArgs = new RequestArgument[1];
|
||||
return;
|
||||
}
|
||||
_currentArgCount = 1;
|
||||
|
||||
for (int i = 0; i < (int)data.length(); ) {
|
||||
i = data.indexOf('&', i);
|
||||
if (i == -1)
|
||||
break;
|
||||
++i;
|
||||
++_currentArgCount;
|
||||
}
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("args count: ");
|
||||
DEBUG_OUTPUT.println(_currentArgCount);
|
||||
#endif
|
||||
|
||||
_currentArgs = new RequestArgument[_currentArgCount+1];
|
||||
int pos = 0;
|
||||
int iarg;
|
||||
for (iarg = 0; iarg < _currentArgCount;) {
|
||||
int equal_sign_index = data.indexOf('=', pos);
|
||||
int next_arg_index = data.indexOf('&', pos);
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("pos ");
|
||||
DEBUG_OUTPUT.print(pos);
|
||||
DEBUG_OUTPUT.print("=@ ");
|
||||
DEBUG_OUTPUT.print(equal_sign_index);
|
||||
DEBUG_OUTPUT.print(" &@ ");
|
||||
DEBUG_OUTPUT.println(next_arg_index);
|
||||
#endif
|
||||
if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) {
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("arg missing value: ");
|
||||
DEBUG_OUTPUT.println(iarg);
|
||||
#endif
|
||||
if (next_arg_index == -1)
|
||||
break;
|
||||
pos = next_arg_index + 1;
|
||||
continue;
|
||||
}
|
||||
RequestArgument& arg = _currentArgs[iarg];
|
||||
arg.key = data.substring(pos, equal_sign_index);
|
||||
arg.value = urlDecode(data.substring(equal_sign_index + 1, next_arg_index));
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("arg ");
|
||||
DEBUG_OUTPUT.print(iarg);
|
||||
DEBUG_OUTPUT.print(" key: ");
|
||||
DEBUG_OUTPUT.print(arg.key);
|
||||
DEBUG_OUTPUT.print(" value: ");
|
||||
DEBUG_OUTPUT.println(arg.value);
|
||||
#endif
|
||||
++iarg;
|
||||
if (next_arg_index == -1)
|
||||
break;
|
||||
pos = next_arg_index + 1;
|
||||
}
|
||||
_currentArgCount = iarg;
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("args count: ");
|
||||
DEBUG_OUTPUT.println(_currentArgCount);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void WebServer::_uploadWriteByte(uint8_t b){
|
||||
if (_currentUpload.currentSize == HTTP_UPLOAD_BUFLEN){
|
||||
if(_currentHandler && _currentHandler->canUpload(_currentUri))
|
||||
_currentHandler->upload(*this, _currentUri, _currentUpload);
|
||||
_currentUpload.totalSize += _currentUpload.currentSize;
|
||||
_currentUpload.currentSize = 0;
|
||||
}
|
||||
_currentUpload.buf[_currentUpload.currentSize++] = b;
|
||||
}
|
||||
|
||||
uint8_t WebServer::_uploadReadByte(WiFiClient& client){
|
||||
int res = client.read();
|
||||
if(res == -1){
|
||||
while(!client.available() && client.connected())
|
||||
yield();
|
||||
res = client.read();
|
||||
}
|
||||
return (uint8_t)res;
|
||||
}
|
||||
|
||||
bool WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){
|
||||
(void) len;
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("Parse Form: Boundary: ");
|
||||
DEBUG_OUTPUT.print(boundary);
|
||||
DEBUG_OUTPUT.print(" Length: ");
|
||||
DEBUG_OUTPUT.println(len);
|
||||
#endif
|
||||
String line;
|
||||
int retry = 0;
|
||||
do {
|
||||
line = client.readStringUntil('\r');
|
||||
++retry;
|
||||
} while (line.length() == 0 && retry < 3);
|
||||
|
||||
client.readStringUntil('\n');
|
||||
//start reading the form
|
||||
if (line == ("--"+boundary)){
|
||||
RequestArgument* postArgs = new RequestArgument[32];
|
||||
int postArgsLen = 0;
|
||||
while(1){
|
||||
String argName;
|
||||
String argValue;
|
||||
String argType;
|
||||
String argFilename;
|
||||
bool argIsFile = false;
|
||||
|
||||
line = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase("Content-Disposition")){
|
||||
int nameStart = line.indexOf('=');
|
||||
if (nameStart != -1){
|
||||
argName = line.substring(nameStart+2);
|
||||
nameStart = argName.indexOf('=');
|
||||
if (nameStart == -1){
|
||||
argName = argName.substring(0, argName.length() - 1);
|
||||
} else {
|
||||
argFilename = argName.substring(nameStart+2, argName.length() - 1);
|
||||
argName = argName.substring(0, argName.indexOf('"'));
|
||||
argIsFile = true;
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("PostArg FileName: ");
|
||||
DEBUG_OUTPUT.println(argFilename);
|
||||
#endif
|
||||
//use GET to set the filename if uploading using blob
|
||||
if (argFilename == "blob" && hasArg("filename")) argFilename = arg("filename");
|
||||
}
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("PostArg Name: ");
|
||||
DEBUG_OUTPUT.println(argName);
|
||||
#endif
|
||||
argType = "text/plain";
|
||||
line = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase("Content-Type")){
|
||||
argType = line.substring(line.indexOf(':')+2);
|
||||
//skip next line
|
||||
client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
}
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("PostArg Type: ");
|
||||
DEBUG_OUTPUT.println(argType);
|
||||
#endif
|
||||
if (!argIsFile){
|
||||
while(1){
|
||||
line = client.readStringUntil('\r');
|
||||
client.readStringUntil('\n');
|
||||
if (line.startsWith("--"+boundary)) break;
|
||||
if (argValue.length() > 0) argValue += "\n";
|
||||
argValue += line;
|
||||
}
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("PostArg Value: ");
|
||||
DEBUG_OUTPUT.println(argValue);
|
||||
DEBUG_OUTPUT.println();
|
||||
#endif
|
||||
|
||||
RequestArgument& arg = postArgs[postArgsLen++];
|
||||
arg.key = argName;
|
||||
arg.value = argValue;
|
||||
|
||||
if (line == ("--"+boundary+"--")){
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.println("Done Parsing POST");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
_currentUpload.status = UPLOAD_FILE_START;
|
||||
_currentUpload.name = argName;
|
||||
_currentUpload.filename = argFilename;
|
||||
_currentUpload.type = argType;
|
||||
_currentUpload.totalSize = 0;
|
||||
_currentUpload.currentSize = 0;
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("Start File: ");
|
||||
DEBUG_OUTPUT.print(_currentUpload.filename);
|
||||
DEBUG_OUTPUT.print(" Type: ");
|
||||
DEBUG_OUTPUT.println(_currentUpload.type);
|
||||
#endif
|
||||
if(_currentHandler && _currentHandler->canUpload(_currentUri))
|
||||
_currentHandler->upload(*this, _currentUri, _currentUpload);
|
||||
_currentUpload.status = UPLOAD_FILE_WRITE;
|
||||
uint8_t argByte = _uploadReadByte(client);
|
||||
readfile:
|
||||
while(argByte != 0x0D){
|
||||
if (!client.connected()) return _parseFormUploadAborted();
|
||||
_uploadWriteByte(argByte);
|
||||
argByte = _uploadReadByte(client);
|
||||
}
|
||||
|
||||
argByte = _uploadReadByte(client);
|
||||
if (!client.connected()) return _parseFormUploadAborted();
|
||||
if (argByte == 0x0A){
|
||||
argByte = _uploadReadByte(client);
|
||||
if (!client.connected()) return _parseFormUploadAborted();
|
||||
if ((char)argByte != '-'){
|
||||
//continue reading the file
|
||||
_uploadWriteByte(0x0D);
|
||||
_uploadWriteByte(0x0A);
|
||||
goto readfile;
|
||||
} else {
|
||||
argByte = _uploadReadByte(client);
|
||||
if (!client.connected()) return _parseFormUploadAborted();
|
||||
if ((char)argByte != '-'){
|
||||
//continue reading the file
|
||||
_uploadWriteByte(0x0D);
|
||||
_uploadWriteByte(0x0A);
|
||||
_uploadWriteByte((uint8_t)('-'));
|
||||
goto readfile;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t endBuf[boundary.length()];
|
||||
client.readBytes(endBuf, boundary.length());
|
||||
|
||||
if (strstr((const char*)endBuf, boundary.c_str()) != NULL){
|
||||
if(_currentHandler && _currentHandler->canUpload(_currentUri))
|
||||
_currentHandler->upload(*this, _currentUri, _currentUpload);
|
||||
_currentUpload.totalSize += _currentUpload.currentSize;
|
||||
_currentUpload.status = UPLOAD_FILE_END;
|
||||
if(_currentHandler && _currentHandler->canUpload(_currentUri))
|
||||
_currentHandler->upload(*this, _currentUri, _currentUpload);
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("End File: ");
|
||||
DEBUG_OUTPUT.print(_currentUpload.filename);
|
||||
DEBUG_OUTPUT.print(" Type: ");
|
||||
DEBUG_OUTPUT.print(_currentUpload.type);
|
||||
DEBUG_OUTPUT.print(" Size: ");
|
||||
DEBUG_OUTPUT.println(_currentUpload.totalSize);
|
||||
#endif
|
||||
line = client.readStringUntil(0x0D);
|
||||
client.readStringUntil(0x0A);
|
||||
if (line == "--"){
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.println("Done Parsing POST");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
_uploadWriteByte(0x0D);
|
||||
_uploadWriteByte(0x0A);
|
||||
_uploadWriteByte((uint8_t)('-'));
|
||||
_uploadWriteByte((uint8_t)('-'));
|
||||
uint32_t i = 0;
|
||||
while(i < boundary.length()){
|
||||
_uploadWriteByte(endBuf[i++]);
|
||||
}
|
||||
argByte = _uploadReadByte(client);
|
||||
goto readfile;
|
||||
}
|
||||
} else {
|
||||
_uploadWriteByte(0x0D);
|
||||
goto readfile;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int iarg;
|
||||
int totalArgs = ((32 - postArgsLen) < _currentArgCount)?(32 - postArgsLen):_currentArgCount;
|
||||
for (iarg = 0; iarg < totalArgs; iarg++){
|
||||
RequestArgument& arg = postArgs[postArgsLen++];
|
||||
arg.key = _currentArgs[iarg].key;
|
||||
arg.value = _currentArgs[iarg].value;
|
||||
}
|
||||
if (_currentArgs) delete[] _currentArgs;
|
||||
_currentArgs = new RequestArgument[postArgsLen];
|
||||
for (iarg = 0; iarg < postArgsLen; iarg++){
|
||||
RequestArgument& arg = _currentArgs[iarg];
|
||||
arg.key = postArgs[iarg].key;
|
||||
arg.value = postArgs[iarg].value;
|
||||
}
|
||||
_currentArgCount = iarg;
|
||||
if (postArgs) delete[] postArgs;
|
||||
return true;
|
||||
}
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.print("Error: line: ");
|
||||
DEBUG_OUTPUT.println(line);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
String WebServer::urlDecode(const String& text)
|
||||
{
|
||||
String decoded = "";
|
||||
char temp[] = "0x00";
|
||||
unsigned int len = text.length();
|
||||
unsigned int i = 0;
|
||||
while (i < len)
|
||||
{
|
||||
char decodedChar;
|
||||
char encodedChar = text.charAt(i++);
|
||||
if ((encodedChar == '%') && (i + 1 < len))
|
||||
{
|
||||
temp[2] = text.charAt(i++);
|
||||
temp[3] = text.charAt(i++);
|
||||
|
||||
decodedChar = strtol(temp, NULL, 16);
|
||||
}
|
||||
else {
|
||||
if (encodedChar == '+')
|
||||
{
|
||||
decodedChar = ' ';
|
||||
}
|
||||
else {
|
||||
decodedChar = encodedChar; // normal ascii char
|
||||
}
|
||||
}
|
||||
decoded += decodedChar;
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
|
||||
bool WebServer::_parseFormUploadAborted(){
|
||||
_currentUpload.status = UPLOAD_FILE_ABORTED;
|
||||
if(_currentHandler && _currentHandler->canUpload(_currentUri))
|
||||
_currentHandler->upload(*this, _currentUri, _currentUpload);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
Notice by Aircoookie: Port of the ESP8266HTTPUpdateServer for ESP32 is also included in this directory.
|
||||
|
||||
# WebServer
|
||||
ESP8266/ESP32 WebServer library
|
||||
|
||||
This is an experimental port of the ESP8266WebServer library that should work
|
||||
on ESP8266 and ESP32. This is NOT an official repo supported by Espressif. Do
|
||||
not depend on this code for anything important or expect it to be updated. Once
|
||||
the official repo is created, this repo will be deleted.
|
||||
|
||||
Added Travis CI
|
||||
@@ -1,529 +0,0 @@
|
||||
/*
|
||||
WebServer.cpp - Dead simple web-server.
|
||||
Supports only one simultaneous client, knows how to handle GET and POST.
|
||||
|
||||
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
#ifdef ARDUINO_ARCH_ESP32 //only use this library if building for ESP32
|
||||
|
||||
#include <libb64/cencode.h>
|
||||
#include "WiFiServer.h"
|
||||
#include "WiFiClient.h"
|
||||
#include "WebServer.h"
|
||||
#include "FS.h"
|
||||
#include "detail/RequestHandlersImpl.h"
|
||||
|
||||
//#define DEBUG_ESP_HTTP_SERVER
|
||||
#ifdef DEBUG_ESP_PORT
|
||||
#define DEBUG_OUTPUT DEBUG_ESP_PORT
|
||||
#else
|
||||
#define DEBUG_OUTPUT Serial
|
||||
#endif
|
||||
|
||||
const char * AUTHORIZATION_HEADER = "Authorization";
|
||||
|
||||
WebServer::WebServer(IPAddress addr, int port)
|
||||
: _server(addr, port)
|
||||
, _currentMethod(HTTP_ANY)
|
||||
, _currentVersion(0)
|
||||
, _currentStatus(HC_NONE)
|
||||
, _statusChange(0)
|
||||
, _currentHandler(0)
|
||||
, _firstHandler(0)
|
||||
, _lastHandler(0)
|
||||
, _currentArgCount(0)
|
||||
, _currentArgs(0)
|
||||
, _headerKeysCount(0)
|
||||
, _currentHeaders(0)
|
||||
, _contentLength(0)
|
||||
, _chunked(false)
|
||||
{
|
||||
}
|
||||
|
||||
WebServer::WebServer(int port)
|
||||
: _server(port)
|
||||
, _currentMethod(HTTP_ANY)
|
||||
, _currentVersion(0)
|
||||
, _currentStatus(HC_NONE)
|
||||
, _statusChange(0)
|
||||
, _currentHandler(0)
|
||||
, _firstHandler(0)
|
||||
, _lastHandler(0)
|
||||
, _currentArgCount(0)
|
||||
, _currentArgs(0)
|
||||
, _headerKeysCount(0)
|
||||
, _currentHeaders(0)
|
||||
, _contentLength(0)
|
||||
, _chunked(false)
|
||||
{
|
||||
}
|
||||
|
||||
WebServer::~WebServer() {
|
||||
if (_currentHeaders)
|
||||
delete[]_currentHeaders;
|
||||
_headerKeysCount = 0;
|
||||
RequestHandler* handler = _firstHandler;
|
||||
while (handler) {
|
||||
RequestHandler* next = handler->next();
|
||||
delete handler;
|
||||
handler = next;
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
void WebServer::begin() {
|
||||
_currentStatus = HC_NONE;
|
||||
_server.begin();
|
||||
//if(!_headerKeysCount)
|
||||
//collectHeaders(0, 0);
|
||||
}
|
||||
|
||||
bool WebServer::authenticate(const char * username, const char * password){
|
||||
if(hasHeader(AUTHORIZATION_HEADER)){
|
||||
String authReq = header(AUTHORIZATION_HEADER);
|
||||
if(authReq.startsWith("Basic")){
|
||||
authReq = authReq.substring(6);
|
||||
authReq.trim();
|
||||
char toencodeLen = strlen(username)+strlen(password)+1;
|
||||
char *toencode = new char[toencodeLen + 1];
|
||||
if(toencode == NULL){
|
||||
authReq = String();
|
||||
return false;
|
||||
}
|
||||
char *encoded = new char[base64_encode_expected_len(toencodeLen)+1];
|
||||
if(encoded == NULL){
|
||||
authReq = String();
|
||||
delete[] toencode;
|
||||
return false;
|
||||
}
|
||||
sprintf(toencode, "%s:%s", username, password);
|
||||
if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equals(encoded)){
|
||||
authReq = String();
|
||||
delete[] toencode;
|
||||
delete[] encoded;
|
||||
return true;
|
||||
}
|
||||
delete[] toencode;
|
||||
delete[] encoded;
|
||||
}
|
||||
authReq = String();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WebServer::requestAuthentication(){
|
||||
sendHeader("WWW-Authenticate", "Basic realm=\"Login Required\"");
|
||||
send(401);
|
||||
}
|
||||
|
||||
void WebServer::on(const String &uri, WebServer::THandlerFunction handler) {
|
||||
on(uri, HTTP_ANY, handler);
|
||||
}
|
||||
|
||||
void WebServer::on(const String &uri, HTTPMethod method, WebServer::THandlerFunction fn) {
|
||||
on(uri, method, fn, _fileUploadHandler);
|
||||
}
|
||||
|
||||
void WebServer::on(const String &uri, HTTPMethod method, WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn) {
|
||||
_addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method));
|
||||
}
|
||||
|
||||
void WebServer::addHandler(RequestHandler* handler) {
|
||||
_addRequestHandler(handler);
|
||||
}
|
||||
|
||||
void WebServer::_addRequestHandler(RequestHandler* handler) {
|
||||
if (!_lastHandler) {
|
||||
_firstHandler = handler;
|
||||
_lastHandler = handler;
|
||||
}
|
||||
else {
|
||||
_lastHandler->next(handler);
|
||||
_lastHandler = handler;
|
||||
}
|
||||
}
|
||||
|
||||
void WebServer::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) {
|
||||
_addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header));
|
||||
}
|
||||
|
||||
void WebServer::handleClient() {
|
||||
if (_currentStatus == HC_NONE) {
|
||||
WiFiClient client = _server.available();
|
||||
if (!client) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.println("New client");
|
||||
#endif
|
||||
|
||||
_currentClient = client;
|
||||
_currentStatus = HC_WAIT_READ;
|
||||
_statusChange = millis();
|
||||
}
|
||||
|
||||
if (!_currentClient.connected()) {
|
||||
_currentClient = WiFiClient();
|
||||
_currentStatus = HC_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for data from client to become available
|
||||
if (_currentStatus == HC_WAIT_READ) {
|
||||
if (!_currentClient.available()) {
|
||||
if (millis() - _statusChange > HTTP_MAX_DATA_WAIT) {
|
||||
_currentClient = WiFiClient();
|
||||
_currentStatus = HC_NONE;
|
||||
}
|
||||
yield();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_parseRequest(_currentClient)) {
|
||||
_currentClient = WiFiClient();
|
||||
_currentStatus = HC_NONE;
|
||||
return;
|
||||
}
|
||||
_currentClient.setTimeout(HTTP_MAX_SEND_WAIT);
|
||||
_contentLength = CONTENT_LENGTH_NOT_SET;
|
||||
_handleRequest();
|
||||
|
||||
if (!_currentClient.connected()) {
|
||||
_currentClient = WiFiClient();
|
||||
_currentStatus = HC_NONE;
|
||||
return;
|
||||
} else {
|
||||
_currentStatus = HC_WAIT_CLOSE;
|
||||
_statusChange = millis();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (_currentStatus == HC_WAIT_CLOSE) {
|
||||
if (millis() - _statusChange > HTTP_MAX_CLOSE_WAIT) {
|
||||
_currentClient = WiFiClient();
|
||||
_currentStatus = HC_NONE;
|
||||
} else {
|
||||
yield();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebServer::close() {
|
||||
#ifdef ESP8266
|
||||
_server.stop();
|
||||
#else
|
||||
// TODO add ESP32 WiFiServer::stop()
|
||||
_server.end();
|
||||
#endif
|
||||
}
|
||||
|
||||
void WebServer::stop() {
|
||||
close();
|
||||
}
|
||||
|
||||
void WebServer::sendHeader(const String& name, const String& value, bool first) {
|
||||
String headerLine = name;
|
||||
headerLine += ": ";
|
||||
headerLine += value;
|
||||
headerLine += "\r\n";
|
||||
|
||||
if (first) {
|
||||
_responseHeaders = headerLine + _responseHeaders;
|
||||
}
|
||||
else {
|
||||
_responseHeaders += headerLine;
|
||||
}
|
||||
}
|
||||
|
||||
void WebServer::setContentLength(size_t contentLength) {
|
||||
_contentLength = contentLength;
|
||||
}
|
||||
|
||||
void WebServer::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) {
|
||||
response = "HTTP/1."+String(_currentVersion)+" ";
|
||||
response += String(code);
|
||||
response += " ";
|
||||
response += _responseCodeToString(code);
|
||||
response += "\r\n";
|
||||
|
||||
if (!content_type)
|
||||
content_type = "text/html";
|
||||
|
||||
sendHeader("Content-Type", content_type, true);
|
||||
if (_contentLength == CONTENT_LENGTH_NOT_SET) {
|
||||
sendHeader("Content-Length", String(contentLength));
|
||||
} else if (_contentLength != CONTENT_LENGTH_UNKNOWN) {
|
||||
sendHeader("Content-Length", String(_contentLength));
|
||||
} else if(_contentLength == CONTENT_LENGTH_UNKNOWN && _currentVersion){ //HTTP/1.1 or above client
|
||||
//let's do chunked
|
||||
_chunked = true;
|
||||
sendHeader("Accept-Ranges","none");
|
||||
sendHeader("Transfer-Encoding","chunked");
|
||||
}
|
||||
sendHeader("Connection", "close");
|
||||
|
||||
response += _responseHeaders;
|
||||
response += "\r\n";
|
||||
_responseHeaders = String();
|
||||
}
|
||||
|
||||
void WebServer::send(int code, const char* content_type, const String& content) {
|
||||
String header;
|
||||
// Can we asume the following?
|
||||
//if(code == 200 && content.length() == 0 && _contentLength == CONTENT_LENGTH_NOT_SET)
|
||||
// _contentLength = CONTENT_LENGTH_UNKNOWN;
|
||||
_prepareHeader(header, code, content_type, content.length());
|
||||
_currentClient.write(header.c_str(), header.length());
|
||||
if(content.length())
|
||||
sendContent(content);
|
||||
}
|
||||
|
||||
void WebServer::send_P(int code, PGM_P content_type, PGM_P content) {
|
||||
size_t contentLength = 0;
|
||||
|
||||
if (content != NULL) {
|
||||
contentLength = strlen_P(content);
|
||||
}
|
||||
|
||||
String header;
|
||||
char type[64];
|
||||
memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type));
|
||||
_prepareHeader(header, code, (const char* )type, contentLength);
|
||||
_currentClient.write(header.c_str(), header.length());
|
||||
sendContent_P(content);
|
||||
}
|
||||
|
||||
void WebServer::send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength) {
|
||||
String header;
|
||||
char type[64];
|
||||
memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type));
|
||||
_prepareHeader(header, code, (const char* )type, contentLength);
|
||||
sendContent(header);
|
||||
sendContent_P(content, contentLength);
|
||||
}
|
||||
|
||||
void WebServer::send(int code, char* content_type, const String& content) {
|
||||
send(code, (const char*)content_type, content);
|
||||
}
|
||||
|
||||
void WebServer::send(int code, const String& content_type, const String& content) {
|
||||
send(code, (const char*)content_type.c_str(), content);
|
||||
}
|
||||
|
||||
void WebServer::sendContent(const String& content) {
|
||||
const char * footer = "\r\n";
|
||||
size_t len = content.length();
|
||||
if(_chunked) {
|
||||
char * chunkSize = (char *)malloc(11);
|
||||
if(chunkSize){
|
||||
sprintf(chunkSize, "%x%s", len, footer);
|
||||
_currentClient.write(chunkSize, strlen(chunkSize));
|
||||
free(chunkSize);
|
||||
}
|
||||
}
|
||||
_currentClient.write(content.c_str(), len);
|
||||
if(_chunked){
|
||||
_currentClient.write(footer, 2);
|
||||
}
|
||||
}
|
||||
|
||||
void WebServer::sendContent_P(PGM_P content) {
|
||||
sendContent_P(content, strlen_P(content));
|
||||
}
|
||||
|
||||
void WebServer::sendContent_P(PGM_P content, size_t size) {
|
||||
const char * footer = "\r\n";
|
||||
if(_chunked) {
|
||||
char * chunkSize = (char *)malloc(11);
|
||||
if(chunkSize){
|
||||
sprintf(chunkSize, "%x%s", size, footer);
|
||||
_currentClient.write(chunkSize, strlen(chunkSize));
|
||||
free(chunkSize);
|
||||
}
|
||||
}
|
||||
_currentClient.write_P(content, size);
|
||||
if(_chunked){
|
||||
_currentClient.write(footer, 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String WebServer::arg(String name) {
|
||||
for (int i = 0; i < _currentArgCount; ++i) {
|
||||
if ( _currentArgs[i].key == name )
|
||||
return _currentArgs[i].value;
|
||||
}
|
||||
return String();
|
||||
}
|
||||
|
||||
String WebServer::arg(int i) {
|
||||
if (i < _currentArgCount)
|
||||
return _currentArgs[i].value;
|
||||
return String();
|
||||
}
|
||||
|
||||
String WebServer::argName(int i) {
|
||||
if (i < _currentArgCount)
|
||||
return _currentArgs[i].key;
|
||||
return String();
|
||||
}
|
||||
|
||||
int WebServer::args() {
|
||||
return _currentArgCount;
|
||||
}
|
||||
|
||||
bool WebServer::hasArg(String name) {
|
||||
for (int i = 0; i < _currentArgCount; ++i) {
|
||||
if (_currentArgs[i].key == name)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
String WebServer::header(String name) {
|
||||
for (int i = 0; i < _headerKeysCount; ++i) {
|
||||
if (_currentHeaders[i].key.equalsIgnoreCase(name))
|
||||
return _currentHeaders[i].value;
|
||||
}
|
||||
return String();
|
||||
}
|
||||
|
||||
//Modified by Aircoookie to work for WLED
|
||||
void WebServer::collectHeaders(String headerKey) {
|
||||
_headerKeysCount = 2;
|
||||
if (_currentHeaders) delete[]_currentHeaders;
|
||||
_currentHeaders = new RequestArgument[2];
|
||||
_currentHeaders[0].key = AUTHORIZATION_HEADER;
|
||||
_currentHeaders[1].key = headerKey;
|
||||
}
|
||||
|
||||
String WebServer::header(int i) {
|
||||
if (i < _headerKeysCount)
|
||||
return _currentHeaders[i].value;
|
||||
return String();
|
||||
}
|
||||
|
||||
String WebServer::headerName(int i) {
|
||||
if (i < _headerKeysCount)
|
||||
return _currentHeaders[i].key;
|
||||
return String();
|
||||
}
|
||||
|
||||
int WebServer::headers() {
|
||||
return _headerKeysCount;
|
||||
}
|
||||
|
||||
bool WebServer::hasHeader(String name) {
|
||||
for (int i = 0; i < _headerKeysCount; ++i) {
|
||||
if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String WebServer::hostHeader() {
|
||||
return _hostHeader;
|
||||
}
|
||||
|
||||
void WebServer::onFileUpload(THandlerFunction fn) {
|
||||
_fileUploadHandler = fn;
|
||||
}
|
||||
|
||||
void WebServer::onNotFound(THandlerFunction fn) {
|
||||
_notFoundHandler = fn;
|
||||
}
|
||||
|
||||
void WebServer::_handleRequest() {
|
||||
bool handled = false;
|
||||
if (!_currentHandler){
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.println("request handler not found");
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
handled = _currentHandler->handle(*this, _currentMethod, _currentUri);
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
if (!handled) {
|
||||
DEBUG_OUTPUT.println("request handler failed to handle request");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
if(_notFoundHandler) {
|
||||
_notFoundHandler();
|
||||
}
|
||||
else {
|
||||
send(404, "text/plain", String("Not found: ") + _currentUri);
|
||||
}
|
||||
}
|
||||
|
||||
_currentUri = String();
|
||||
}
|
||||
|
||||
String WebServer::_responseCodeToString(int code) {
|
||||
switch (code) {
|
||||
case 100: return F("Continue");
|
||||
case 101: return F("Switching Protocols");
|
||||
case 200: return F("OK");
|
||||
case 201: return F("Created");
|
||||
case 202: return F("Accepted");
|
||||
case 203: return F("Non-Authoritative Information");
|
||||
case 204: return F("No Content");
|
||||
case 205: return F("Reset Content");
|
||||
case 206: return F("Partial Content");
|
||||
case 300: return F("Multiple Choices");
|
||||
case 301: return F("Moved Permanently");
|
||||
case 302: return F("Found");
|
||||
case 303: return F("See Other");
|
||||
case 304: return F("Not Modified");
|
||||
case 305: return F("Use Proxy");
|
||||
case 307: return F("Temporary Redirect");
|
||||
case 400: return F("Bad Request");
|
||||
case 401: return F("Unauthorized");
|
||||
case 402: return F("Payment Required");
|
||||
case 403: return F("Forbidden");
|
||||
case 404: return F("Not Found");
|
||||
case 405: return F("Method Not Allowed");
|
||||
case 406: return F("Not Acceptable");
|
||||
case 407: return F("Proxy Authentication Required");
|
||||
case 408: return F("Request Time-out");
|
||||
case 409: return F("Conflict");
|
||||
case 410: return F("Gone");
|
||||
case 411: return F("Length Required");
|
||||
case 412: return F("Precondition Failed");
|
||||
case 413: return F("Request Entity Too Large");
|
||||
case 414: return F("Request-URI Too Large");
|
||||
case 415: return F("Unsupported Media Type");
|
||||
case 416: return F("Requested range not satisfiable");
|
||||
case 417: return F("Expectation Failed");
|
||||
case 500: return F("Internal Server Error");
|
||||
case 501: return F("Not Implemented");
|
||||
case 502: return F("Bad Gateway");
|
||||
case 503: return F("Service Unavailable");
|
||||
case 504: return F("Gateway Time-out");
|
||||
case 505: return F("HTTP Version not supported");
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,236 +0,0 @@
|
||||
/*
|
||||
WebServer.h - Dead simple web-server.
|
||||
Supports only one simultaneous client, knows how to handle GET and POST.
|
||||
|
||||
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
|
||||
*/
|
||||
|
||||
|
||||
#ifndef WEBSERVER_H
|
||||
#define WEBSERVER_H
|
||||
|
||||
#include <functional>
|
||||
#ifdef ESP8266
|
||||
#define WebServer ESP8266WebServer
|
||||
#include <ESP8266WiFi.h>
|
||||
#else
|
||||
#include <WiFi.h>
|
||||
#define write_P write
|
||||
#endif
|
||||
|
||||
enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE, HTTP_OPTIONS };
|
||||
enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END,
|
||||
UPLOAD_FILE_ABORTED };
|
||||
enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE };
|
||||
|
||||
#define HTTP_DOWNLOAD_UNIT_SIZE 1460
|
||||
|
||||
#ifndef HTTP_UPLOAD_BUFLEN
|
||||
#define HTTP_UPLOAD_BUFLEN 2048
|
||||
#endif
|
||||
|
||||
#define HTTP_MAX_DATA_WAIT 1000 //ms to wait for the client to send the request
|
||||
#define HTTP_MAX_POST_WAIT 1000 //ms to wait for POST data to arrive
|
||||
#define HTTP_MAX_SEND_WAIT 5000 //ms to wait for data chunk to be ACKed
|
||||
#define HTTP_MAX_CLOSE_WAIT 2000 //ms to wait for the client to close the connection
|
||||
|
||||
#define CONTENT_LENGTH_UNKNOWN ((size_t) -1)
|
||||
#define CONTENT_LENGTH_NOT_SET ((size_t) -2)
|
||||
|
||||
class WebServer;
|
||||
|
||||
typedef struct {
|
||||
HTTPUploadStatus status;
|
||||
String filename;
|
||||
String name;
|
||||
String type;
|
||||
size_t totalSize; // file size
|
||||
size_t currentSize; // size of data currently in buf
|
||||
uint8_t buf[HTTP_UPLOAD_BUFLEN];
|
||||
} HTTPUpload;
|
||||
|
||||
#include "detail/RequestHandler.h"
|
||||
|
||||
namespace fs {
|
||||
class FS;
|
||||
}
|
||||
|
||||
class WebServer
|
||||
{
|
||||
public:
|
||||
WebServer(IPAddress addr, int port = 80);
|
||||
WebServer(int port = 80);
|
||||
~WebServer();
|
||||
|
||||
void begin();
|
||||
void handleClient();
|
||||
|
||||
void close();
|
||||
void stop();
|
||||
|
||||
bool authenticate(const char * username, const char * password);
|
||||
void requestAuthentication();
|
||||
|
||||
typedef std::function<void(void)> THandlerFunction;
|
||||
void on(const String &uri, THandlerFunction handler);
|
||||
void on(const String &uri, HTTPMethod method, THandlerFunction fn);
|
||||
void on(const String &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn);
|
||||
void addHandler(RequestHandler* handler);
|
||||
void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL );
|
||||
void onNotFound(THandlerFunction fn); //called when handler is not assigned
|
||||
void onFileUpload(THandlerFunction fn); //handle file uploads
|
||||
|
||||
String uri() { return _currentUri; }
|
||||
HTTPMethod method() { return _currentMethod; }
|
||||
WiFiClient client() { return _currentClient; }
|
||||
HTTPUpload& upload() { return _currentUpload; }
|
||||
|
||||
String arg(String name); // get request argument value by name
|
||||
String arg(int i); // get request argument value by number
|
||||
String argName(int i); // get request argument name by number
|
||||
int args(); // get arguments count
|
||||
bool hasArg(String name); // check if argument exists
|
||||
void collectHeaders(String headerKey); // set the request headers to collect
|
||||
String header(String name); // get request header value by name
|
||||
String header(int i); // get request header value by number
|
||||
String headerName(int i); // get request header name by number
|
||||
int headers(); // get header count
|
||||
bool hasHeader(String name); // check if header exists
|
||||
|
||||
String hostHeader(); // get request host header if available or empty String if not
|
||||
|
||||
// send response to the client
|
||||
// code - HTTP response code, can be 200 or 404
|
||||
// content_type - HTTP content type, like "text/plain" or "image/png"
|
||||
// content - actual content body
|
||||
void send(int code, const char* content_type = NULL, const String& content = String(""));
|
||||
void send(int code, char* content_type, const String& content);
|
||||
void send(int code, const String& content_type, const String& content);
|
||||
void send_P(int code, PGM_P content_type, PGM_P content);
|
||||
void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength);
|
||||
|
||||
void setContentLength(size_t contentLength);
|
||||
void sendHeader(const String& name, const String& value, bool first = false);
|
||||
void sendContent(const String& content);
|
||||
void sendContent_P(PGM_P content);
|
||||
void sendContent_P(PGM_P content, size_t size);
|
||||
|
||||
static String urlDecode(const String& text);
|
||||
|
||||
#ifdef ESP8266
|
||||
template<typename T> size_t streamFile(T &file, const String& contentType){
|
||||
setContentLength(file.size());
|
||||
if (String(file.name()).endsWith(".gz") &&
|
||||
contentType != "application/x-gzip" &&
|
||||
contentType != "application/octet-stream"){
|
||||
sendHeader("Content-Encoding", "gzip");
|
||||
}
|
||||
send(200, contentType, "");
|
||||
return _currentClient.write(file);
|
||||
}
|
||||
#else
|
||||
template<typename T> size_t streamFile(T &file, const String& contentType){
|
||||
#define STREAMFILE_BUFSIZE 2*1460
|
||||
setContentLength(file.size());
|
||||
if (String(file.name()).endsWith(".gz") &&
|
||||
contentType != "application/x-gzip" &&
|
||||
contentType != "application/octet-stream") {
|
||||
sendHeader("Content-Encoding", "gzip");
|
||||
}
|
||||
send(200, contentType, "");
|
||||
uint8_t *buf = (uint8_t *)malloc(STREAMFILE_BUFSIZE);
|
||||
if (buf == NULL) {
|
||||
//DBG_OUTPUT_PORT.printf("streamFile malloc failed");
|
||||
return 0;
|
||||
}
|
||||
size_t totalBytesOut = 0;
|
||||
while (client().connected() && (file.available() > 0)) {
|
||||
int bytesOut;
|
||||
int bytesIn = file.read(buf, STREAMFILE_BUFSIZE);
|
||||
if (bytesIn <= 0) break;
|
||||
while (1) {
|
||||
bytesOut = 0;
|
||||
if (!client().connected()) break;
|
||||
bytesOut = client().write(buf, bytesIn);
|
||||
if (bytesIn == bytesOut) break;
|
||||
|
||||
//DBG_OUTPUT_PORT.printf("bytesIn %d != bytesOut %d\r\n",
|
||||
//bytesIn, bytesOut);
|
||||
delay(1);
|
||||
}
|
||||
totalBytesOut += bytesOut;
|
||||
yield();
|
||||
}
|
||||
if (totalBytesOut != file.size()) {
|
||||
//DBG_OUTPUT_PORT.printf("file size %d bytes out %d\r\n",
|
||||
// file.size(), totalBytesOut);
|
||||
}
|
||||
free(buf);
|
||||
return totalBytesOut;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void _addRequestHandler(RequestHandler* handler);
|
||||
void _handleRequest();
|
||||
bool _parseRequest(WiFiClient& client);
|
||||
void _parseArguments(String data);
|
||||
static String _responseCodeToString(int code);
|
||||
bool _parseForm(WiFiClient& client, String boundary, uint32_t len);
|
||||
bool _parseFormUploadAborted();
|
||||
void _uploadWriteByte(uint8_t b);
|
||||
uint8_t _uploadReadByte(WiFiClient& client);
|
||||
void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength);
|
||||
bool _collectHeader(const char* headerName, const char* headerValue);
|
||||
|
||||
struct RequestArgument {
|
||||
String key;
|
||||
String value;
|
||||
};
|
||||
|
||||
WiFiServer _server;
|
||||
|
||||
WiFiClient _currentClient;
|
||||
HTTPMethod _currentMethod;
|
||||
String _currentUri;
|
||||
uint8_t _currentVersion;
|
||||
HTTPClientStatus _currentStatus;
|
||||
unsigned long _statusChange;
|
||||
|
||||
RequestHandler* _currentHandler;
|
||||
RequestHandler* _firstHandler;
|
||||
RequestHandler* _lastHandler;
|
||||
THandlerFunction _notFoundHandler;
|
||||
THandlerFunction _fileUploadHandler;
|
||||
|
||||
int _currentArgCount;
|
||||
RequestArgument* _currentArgs;
|
||||
HTTPUpload _currentUpload;
|
||||
|
||||
int _headerKeysCount;
|
||||
RequestArgument* _currentHeaders;
|
||||
size_t _contentLength;
|
||||
String _responseHeaders;
|
||||
|
||||
String _hostHeader;
|
||||
bool _chunked;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif //WEBSERVER_H
|
||||
@@ -1,19 +0,0 @@
|
||||
#ifndef REQUESTHANDLER_H
|
||||
#define REQUESTHANDLER_H
|
||||
|
||||
class RequestHandler {
|
||||
public:
|
||||
virtual ~RequestHandler() { }
|
||||
virtual bool canHandle(HTTPMethod method, String uri) { (void) method; (void) uri; return false; }
|
||||
virtual bool canUpload(String uri) { (void) uri; return false; }
|
||||
virtual bool handle(WebServer& server, HTTPMethod requestMethod, String requestUri) { (void) server; (void) requestMethod; (void) requestUri; return false; }
|
||||
virtual void upload(WebServer& server, String requestUri, HTTPUpload& upload) { (void) server; (void) requestUri; (void) upload; }
|
||||
|
||||
RequestHandler* next() { return _next; }
|
||||
void next(RequestHandler* r) { _next = r; }
|
||||
|
||||
private:
|
||||
RequestHandler* _next = nullptr;
|
||||
};
|
||||
|
||||
#endif //REQUESTHANDLER_H
|
||||
@@ -1,191 +0,0 @@
|
||||
#ifndef REQUESTHANDLERSIMPL_H
|
||||
#define REQUESTHANDLERSIMPL_H
|
||||
|
||||
#include "RequestHandler.h"
|
||||
|
||||
#ifdef ESP8266
|
||||
// Table of extension->MIME strings stored in PROGMEM, needs to be global due to GCC section typing rules
|
||||
static const struct {const char endsWith[16]; const char mimeType[32];} mimeTable[] ICACHE_RODATA_ATTR = {
|
||||
#else
|
||||
static const struct {const char endsWith[16]; const char mimeType[32];} mimeTable[] = {
|
||||
#endif
|
||||
{ ".html", "text/html" },
|
||||
{ ".htm", "text/html" },
|
||||
{ ".css", "text/css" },
|
||||
{ ".txt", "text/plain" },
|
||||
{ ".js", "application/javascript" },
|
||||
{ ".json", "application/json" },
|
||||
{ ".png", "image/png" },
|
||||
{ ".gif", "image/gif" },
|
||||
{ ".jpg", "image/jpeg" },
|
||||
{ ".ico", "image/x-icon" },
|
||||
{ ".svg", "image/svg+xml" },
|
||||
{ ".ttf", "application/x-font-ttf" },
|
||||
{ ".otf", "application/x-font-opentype" },
|
||||
{ ".woff", "application/font-woff" },
|
||||
{ ".woff2", "application/font-woff2" },
|
||||
{ ".eot", "application/vnd.ms-fontobject" },
|
||||
{ ".sfnt", "application/font-sfnt" },
|
||||
{ ".xml", "text/xml" },
|
||||
{ ".pdf", "application/pdf" },
|
||||
{ ".zip", "application/zip" },
|
||||
{ ".gz", "application/x-gzip" },
|
||||
{ ".appcache", "text/cache-manifest" },
|
||||
{ "", "application/octet-stream" } };
|
||||
|
||||
class FunctionRequestHandler : public RequestHandler {
|
||||
public:
|
||||
FunctionRequestHandler(WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn, const String &uri, HTTPMethod method)
|
||||
: _fn(fn)
|
||||
, _ufn(ufn)
|
||||
, _uri(uri)
|
||||
, _method(method)
|
||||
{
|
||||
}
|
||||
|
||||
bool canHandle(HTTPMethod requestMethod, String requestUri) override {
|
||||
if (_method != HTTP_ANY && _method != requestMethod)
|
||||
return false;
|
||||
|
||||
if (requestUri != _uri)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool canUpload(String requestUri) override {
|
||||
if (!_ufn || !canHandle(HTTP_POST, requestUri))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool handle(WebServer& server, HTTPMethod requestMethod, String requestUri) override {
|
||||
(void) server;
|
||||
if (!canHandle(requestMethod, requestUri))
|
||||
return false;
|
||||
|
||||
_fn();
|
||||
return true;
|
||||
}
|
||||
|
||||
void upload(WebServer& server, String requestUri, HTTPUpload& upload) override {
|
||||
(void) server;
|
||||
(void) upload;
|
||||
if (canUpload(requestUri))
|
||||
_ufn();
|
||||
}
|
||||
|
||||
protected:
|
||||
WebServer::THandlerFunction _fn;
|
||||
WebServer::THandlerFunction _ufn;
|
||||
String _uri;
|
||||
HTTPMethod _method;
|
||||
};
|
||||
|
||||
class StaticRequestHandler : public RequestHandler {
|
||||
public:
|
||||
StaticRequestHandler(FS& fs, const char* path, const char* uri, const char* cache_header)
|
||||
: _fs(fs)
|
||||
, _uri(uri)
|
||||
, _path(path)
|
||||
, _cache_header(cache_header)
|
||||
{
|
||||
_isFile = fs.exists(path);
|
||||
#ifdef ESP8266
|
||||
DEBUGV("StaticRequestHandler: path=%s uri=%s isFile=%d, cache_header=%s\r\n", path, uri, _isFile, cache_header);
|
||||
#else
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.printf("StaticRequestHandler: path=%s uri=%s isFile=%d, cache_header=%s\r\n", path, uri, _isFile, cache_header);
|
||||
#endif
|
||||
#endif
|
||||
_baseUriLength = _uri.length();
|
||||
}
|
||||
|
||||
bool canHandle(HTTPMethod requestMethod, String requestUri) override {
|
||||
if (requestMethod != HTTP_GET)
|
||||
return false;
|
||||
|
||||
if ((_isFile && requestUri != _uri) || !requestUri.startsWith(_uri))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool handle(WebServer& server, HTTPMethod requestMethod, String requestUri) override {
|
||||
if (!canHandle(requestMethod, requestUri))
|
||||
return false;
|
||||
|
||||
#ifdef ESP8266
|
||||
DEBUGV("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str());
|
||||
#else
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.printf("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
String path(_path);
|
||||
|
||||
if (!_isFile) {
|
||||
// Base URI doesn't point to a file.
|
||||
// If a directory is requested, look for index file.
|
||||
if (requestUri.endsWith("/")) requestUri += "index.htm";
|
||||
|
||||
// Append whatever follows this URI in request to get the file path.
|
||||
path += requestUri.substring(_baseUriLength);
|
||||
}
|
||||
#ifdef ESP8266
|
||||
DEBUGV("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile);
|
||||
#else
|
||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||
DEBUG_OUTPUT.printf("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
String contentType = getContentType(path);
|
||||
|
||||
// look for gz file, only if the original specified path is not a gz. So part only works to send gzip via content encoding when a non compressed is asked for
|
||||
// if you point the the path to gzip you will serve the gzip as content type "application/x-gzip", not text or javascript etc...
|
||||
if (!path.endsWith(".gz") && !_fs.exists(path)) {
|
||||
String pathWithGz = path + ".gz";
|
||||
if(_fs.exists(pathWithGz))
|
||||
path += ".gz";
|
||||
}
|
||||
|
||||
File f = _fs.open(path, "r");
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
if (_cache_header.length() != 0)
|
||||
server.sendHeader("Cache-Control", _cache_header);
|
||||
|
||||
server.streamFile(f, contentType);
|
||||
return true;
|
||||
}
|
||||
|
||||
static String getContentType(const String& path) {
|
||||
char buff[sizeof(mimeTable[0].mimeType)];
|
||||
// Check all entries but last one for match, return if found
|
||||
for (size_t i=0; i < sizeof(mimeTable)/sizeof(mimeTable[0])-1; i++) {
|
||||
strcpy_P(buff, mimeTable[i].endsWith);
|
||||
if (path.endsWith(buff)) {
|
||||
strcpy_P(buff, mimeTable[i].mimeType);
|
||||
return String(buff);
|
||||
}
|
||||
}
|
||||
// Fall-through and just return default type
|
||||
strcpy_P(buff, mimeTable[sizeof(mimeTable)/sizeof(mimeTable[0])-1].mimeType);
|
||||
return String(buff);
|
||||
}
|
||||
|
||||
protected:
|
||||
FS _fs;
|
||||
String _uri;
|
||||
String _path;
|
||||
String _cache_header;
|
||||
bool _isFile;
|
||||
size_t _baseUriLength;
|
||||
};
|
||||
|
||||
|
||||
#endif //REQUESTHANDLERSIMPL_H
|
||||
@@ -3,50 +3,105 @@
|
||||
*/
|
||||
/*
|
||||
* @title WLED project sketch
|
||||
* @version 0.8.0
|
||||
* @version 0.8.6
|
||||
* @author Christian Schwinne
|
||||
*/
|
||||
|
||||
//ESP8266-01 got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.3.0 and the setting 512K(64K SPIFFS).
|
||||
//Uncomment the following line to disable some features (currently Mobile UI, welcome page and single digit + cronixie overlays) to compile for ESP8266-01
|
||||
//#define WLED_FLASH_512K_MODE
|
||||
//CURRENTLY NOT WORKING
|
||||
|
||||
//ESP8266-01 (blue) got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.4.2 and the setting 512K(No SPIFFS).
|
||||
|
||||
//ESP8266-01 (black) has 1MB flash and can thus fit the whole program. Use 1M(64K SPIFFS).
|
||||
//Uncomment some of the following lines to disable features to compile for ESP8266-01 (max flash size 434kB):
|
||||
|
||||
//You are required to disable over-the-air updates:
|
||||
//#define WLED_DISABLE_OTA
|
||||
|
||||
//You need to choose 1-2 of these features to disable:
|
||||
//#define WLED_DISABLE_ALEXA
|
||||
//#define WLED_DISABLE_BLYNK
|
||||
//#define WLED_DISABLE_CRONIXIE
|
||||
//#define WLED_DISABLE_HUESYNC
|
||||
//#define WLED_DISABLE_INFRARED //there is no pin left for this on ESP8266-01
|
||||
//#define WLED_DISABLE_MOBILE_UI
|
||||
|
||||
|
||||
#define WLED_DISABLE_FILESYSTEM //SPIFFS is not used by any WLED feature yet
|
||||
//#define WLED_ENABLE_FS_SERVING //Enable sending html file from SPIFFS before serving progmem version
|
||||
//#define WLED_ENABLE_FS_EDITOR //enable /edit page for editing SPIFFS content. Will also be disabled with OTA lock
|
||||
|
||||
//to toggle usb serial debug (un)comment the following line
|
||||
//#define WLED_DEBUG
|
||||
|
||||
|
||||
//library inclusions
|
||||
#include <Arduino.h>
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <WiFi.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include "src/dependencies/webserver/WebServer.h"
|
||||
#include <HTTPClient.h>
|
||||
#ifdef ESP8266
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#else
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <WiFi.h>
|
||||
#include "esp_wifi.h"
|
||||
#include <ESPmDNS.h>
|
||||
#include <AsyncTCP.h>
|
||||
#include "SPIFFS.h"
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <EEPROM.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#include <WiFiUDP.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <DNSServer.h>
|
||||
#include "src/dependencies/webserver/ESP8266HTTPUpdateServer.h"
|
||||
#ifndef WLED_DISABLE_OTA
|
||||
#include <ArduinoOTA.h>
|
||||
#endif
|
||||
#include <SPIFFSEditor.h>
|
||||
#include "src/dependencies/time/Time.h"
|
||||
#include "src/dependencies/time/TimeLib.h"
|
||||
#include "src/dependencies/timezone/Timezone.h"
|
||||
#include "src/dependencies/blynk/BlynkSimpleEsp.h"
|
||||
#ifndef WLED_DISABLE_ALEXA
|
||||
#define ESPALEXA_ASYNC
|
||||
#define ESPALEXA_NO_SUBPAGE
|
||||
#define ESPALEXA_MAXDEVICES 1
|
||||
//#define ESPALEXA_DEBUG
|
||||
#include "src/dependencies/espalexa/Espalexa.h"
|
||||
#endif
|
||||
#ifndef WLED_DISABLE_BLYNK
|
||||
#include "src/dependencies/blynk/BlynkSimpleEsp.h"
|
||||
#endif
|
||||
#include "src/dependencies/e131/E131.h"
|
||||
#include "src/dependencies/pubsubclient/PubSubClient.h"
|
||||
#include "htmls00.h"
|
||||
#include "htmls01.h"
|
||||
#include "htmls02.h"
|
||||
#include "WS2812FX.h"
|
||||
#include "src/dependencies/async-mqtt-client/AsyncMqttClient.h"
|
||||
#include "src/dependencies/json/AsyncJson-v6.h"
|
||||
#include "src/dependencies/json/ArduinoJson-v6.h"
|
||||
#include "html_classic.h"
|
||||
#include "html_mobile.h"
|
||||
#include "html_settings.h"
|
||||
#include "html_other.h"
|
||||
#include "FX.h"
|
||||
#include "ir_codes.h"
|
||||
|
||||
|
||||
#if IR_PIN < 0
|
||||
#ifndef WLED_DISABLE_INFRARED
|
||||
#define WLED_DISABLE_INFRARED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
/*#ifndef WLED_DISABLE_INFRARED
|
||||
#include <IRremote.h>
|
||||
#endif*/ //there are issues with ESP32 infrared, so it is disabled for now
|
||||
#else
|
||||
#ifndef WLED_DISABLE_INFRARED
|
||||
#include <IRremoteESP8266.h>
|
||||
#include <IRrecv.h>
|
||||
#include <IRutils.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
//version code in format yymmddb (b = daily build)
|
||||
#define VERSION 1810151
|
||||
char versionString[] = "0.8.0";
|
||||
#define VERSION 1910255
|
||||
char versionString[] = "0.8.6";
|
||||
|
||||
|
||||
//AP and OTA default passwords (for maximum change them!)
|
||||
@@ -54,18 +109,9 @@ char apPass[65] = "wled1234";
|
||||
char otaPass[33] = "wledota";
|
||||
|
||||
|
||||
//to toggle usb serial debug (un)comment following line(s)
|
||||
//#define DEBUG
|
||||
|
||||
|
||||
//spiffs FS only useful for debug (only ESP8266)
|
||||
//#define USEFS
|
||||
|
||||
|
||||
//Hardware CONFIG (only changeble HERE, not at runtime)
|
||||
//LED strip pin changeable in NpbWrapper.h. Only change for ESP32
|
||||
byte buttonPin = 0; //needs pull-up
|
||||
byte auxPin = 15; //debug feature, use e.g. for external relay with API call AX=
|
||||
//LED strip pin, button pin and IR pin changeable in NpbWrapper.h!
|
||||
|
||||
byte auxDefaultState = 0; //0: input 1: high 2: low
|
||||
byte auxTriggeredState = 0; //0: input 1: high 2: low
|
||||
char ntpServerName[] = "0.wled.pool.ntp.org"; //NTP server to use
|
||||
@@ -74,55 +120,49 @@ char ntpServerName[] = "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)
|
||||
char clientSSID[33] = "Your_Network";
|
||||
char clientPass[65] = "";
|
||||
char cmDNS[33] = "led"; //mDNS address (x.local), only for Apple and Windows, if Bonjour installed
|
||||
char apSSID[65] = ""; //AP off by default (unless setup)
|
||||
char cmDNS[33] = "x"; //mDNS address (placeholder, will be replaced by wledXXXXXXXXXXXX.local)
|
||||
char apSSID[33] = ""; //AP off by default (unless setup)
|
||||
byte apChannel = 1; //2.4GHz WiFi AP channel (1-13)
|
||||
byte apHide = 0; //hidden AP SSID
|
||||
byte apWaitTimeSecs = 32; //time to wait for connection before opening AP
|
||||
bool recoveryAPDisabled = false; //never open AP (not recommended)
|
||||
//byte apWaitTimeSecs = 32; //time to wait for connection before opening AP
|
||||
byte apBehavior = 0; //0: Open AP when no connection after boot 1: Open when no connection 2: Always open 3: Only when button pressed for 6 sec
|
||||
//bool recoveryAPDisabled = false; //never open AP (not recommended)
|
||||
IPAddress staticIP(0, 0, 0, 0); //static IP of ESP
|
||||
IPAddress staticGateway(0, 0, 0, 0); //gateway (router) IP
|
||||
IPAddress staticSubnet(255, 255, 255, 0); //most common subnet in home networks
|
||||
IPAddress staticDNS(8, 8, 8, 8); //only for NTP, google DNS server
|
||||
|
||||
|
||||
//LED CONFIG
|
||||
uint16_t ledCount = 10; //lowered to prevent accidental overcurrent
|
||||
uint16_t ledCount = 30; //overcurrent prevented by ABL
|
||||
bool useRGBW = false; //SK6812 strips can contain an extra White channel
|
||||
bool autoRGBtoRGBW = false; //if RGBW enabled, calculate White channel from RGB
|
||||
#define ABL_MILLIAMPS_DEFAULT 850; //auto lower brightness to stay close to milliampere limit
|
||||
bool turnOnAtBoot = true; //turn on LEDs at power-up
|
||||
byte bootPreset = 0; //save preset to load after power-up
|
||||
|
||||
byte colS[]{255, 159, 0}; //default RGB color
|
||||
byte colSecS[]{0, 0, 0}; //default RGB secondary color
|
||||
byte whiteS = 0; //default White channel
|
||||
byte whiteSecS = 0; //default secondary White channel
|
||||
byte colS[]{255, 159, 0, 0}; //default RGB(W) color
|
||||
byte colSecS[]{0, 0, 0, 0}; //default RGB(W) secondary color
|
||||
byte briS = 127; //default brightness
|
||||
byte effectDefault = 0;
|
||||
byte effectDefault = 0;
|
||||
byte effectSpeedDefault = 75;
|
||||
byte effectIntensityDefault = 128; //intensity is supported on some effects as an additional parameter (e.g. for blink you can change the duty cycle)
|
||||
byte effectPaletteDefault = 0; //palette is supported on the FastLED effects, otherwise it has no effect
|
||||
|
||||
bool useGammaCorrectionBri = false; //gamma correct brightness (not recommended)
|
||||
bool useGammaCorrectionRGB = true; //gamma correct colors (strongly recommended)
|
||||
|
||||
byte nightlightTargetBri = 0; //brightness after nightlight is over
|
||||
byte nightlightDelayMins = 60;
|
||||
bool nightlightFade = true; //if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over
|
||||
bool fadeTransition = true; //enable crossfading color transition
|
||||
bool enableSecTransition = true; //also enable transition for secondary color
|
||||
uint16_t transitionDelay = 900; //default crossfade duration in ms
|
||||
uint16_t transitionDelay = 750; //default crossfade duration in ms
|
||||
|
||||
bool reverseMode = false; //flip entire LED strip (reverses all effect directions)
|
||||
bool initLedsLast = false; //turn on LEDs only after WiFi connected/AP open
|
||||
//bool strip.reverseMode = false; //flip entire LED strip (reverses all effect directions) --> edit in WS2812FX.h
|
||||
bool skipFirstLed = false; //ignore first LED in strip (useful if you need the LED as signal repeater)
|
||||
byte briMultiplier = 100; //% of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127)
|
||||
|
||||
|
||||
//User Interface CONFIG
|
||||
char serverDescription[33] = "WLED Light"; //Name of module
|
||||
byte currentTheme = 0; //UI theme index for settings and classic UI
|
||||
byte uiConfiguration = 0; //0: automatic (depends on user-agent) 1: classic UI 2: mobile UI
|
||||
char serverDescription[33] = "WLED"; //Name of module
|
||||
byte currentTheme = 7; //UI theme index for settings and classic UI
|
||||
byte uiConfiguration = 2; //0: automatic (depends on user-agent) 1: classic UI 2: mobile UI
|
||||
bool useHSB = true; //classic UI: use HSB sliders instead of RGB by default
|
||||
char cssFont[33] = "Verdana"; //font to use in classic UI
|
||||
|
||||
@@ -130,7 +170,8 @@ bool useHSBDefault = useHSB;
|
||||
|
||||
|
||||
//Sync CONFIG
|
||||
bool buttonEnabled = true;
|
||||
bool buttonEnabled = true;
|
||||
bool irEnabled = false; //Infrared receiver
|
||||
|
||||
uint16_t udpPort = 21324; //WLED notifier default port
|
||||
uint16_t udpRgbPort = 19446; //Hyperion port
|
||||
@@ -138,8 +179,8 @@ uint16_t udpRgbPort = 19446; //Hyperion port
|
||||
bool receiveNotificationBrightness = true; //apply brightness from incoming notifications
|
||||
bool receiveNotificationColor = true; //apply color
|
||||
bool receiveNotificationEffects = true; //apply effects setup
|
||||
bool notifyDirect = true; //send notification if change via UI or HTTP API
|
||||
bool notifyButton = true;
|
||||
bool notifyDirect = false; //send notification if change via UI or HTTP API
|
||||
bool notifyButton = false; //send if updated by button or infrared remote
|
||||
bool notifyAlexa = false; //send notification if updated via Alexa
|
||||
bool notifyMacro = false; //send notification for macro
|
||||
bool notifyHue = true; //send notification if Hue light changes
|
||||
@@ -153,7 +194,6 @@ char blynkApiKey[36] = ""; //Auth token for Blynk server. If
|
||||
uint16_t realtimeTimeoutMs = 2500; //ms timeout of realtime mode before returning to normal mode
|
||||
int arlsOffset = 0; //realtime LED offset
|
||||
bool receiveDirect = true; //receive UDP realtime
|
||||
bool enableRealtimeUI = false; //web UI accessible during realtime mode (works on ESP32, lags out ESP8266)
|
||||
bool arlsDisableGammaCorrection = true; //activate if gamma correction is handled by the source
|
||||
bool arlsForceMaxBri = false; //enable to force max brightness if source has very dark colors that would be black
|
||||
|
||||
@@ -164,6 +204,10 @@ bool e131Multicast = false;
|
||||
char mqttDeviceTopic[33] = ""; //main MQTT topic (individual per device, default is wled/mac)
|
||||
char mqttGroupTopic[33] = "wled/all"; //second MQTT topic (for example to group devices)
|
||||
char mqttServer[33] = ""; //both domains and IPs should work (no SSL)
|
||||
char mqttUser[41] = ""; //optional: username for MQTT auth
|
||||
char mqttPass[41] = ""; //optional: password for MQTT auth
|
||||
char mqttClientID[41] = ""; //override the client ID
|
||||
uint16_t mqttPort = 1883;
|
||||
|
||||
bool huePollingEnabled = false; //poll hue bridge for light state
|
||||
uint16_t huePollIntervalMs = 2500; //low values (< 1sec) may cause lag but offer quicker response
|
||||
@@ -199,9 +243,9 @@ byte countdownMin = 0, countdownSec = 0;
|
||||
|
||||
byte macroBoot = 0; //macro loaded after startup
|
||||
byte macroNl = 0; //after nightlight delay over
|
||||
byte macroCountdown = 0;
|
||||
byte macroCountdown = 0;
|
||||
byte macroAlexaOn = 0, macroAlexaOff = 0;
|
||||
byte macroButton = 0, macroLongPress = 0;
|
||||
byte macroButton = 0, macroLongPress = 0, macroDoublePress = 0;
|
||||
|
||||
|
||||
//Security CONFIG
|
||||
@@ -213,19 +257,23 @@ bool aOtaEnabled = true; //ArduinoOTA allows easy updates d
|
||||
uint16_t userVar0 = 0, userVar1 = 0;
|
||||
|
||||
|
||||
|
||||
//internal global variable declarations
|
||||
//wifi
|
||||
bool apActive = false;
|
||||
bool forceReconnect = false;
|
||||
uint32_t lastReconnectAttempt = 0;
|
||||
bool interfacesInited = false;
|
||||
bool wasConnected = false;
|
||||
|
||||
//color
|
||||
byte col[]{255, 159, 0}; //target RGB color
|
||||
byte colOld[]{0, 0, 0}; //color before transition
|
||||
byte colT[]{0, 0, 0}; //current color
|
||||
byte colIT[]{0, 0, 0}; //color that was last sent to LEDs
|
||||
byte colSec[]{0, 0, 0};
|
||||
byte colSecT[]{0, 0, 0};
|
||||
byte colSecOld[]{0, 0, 0};
|
||||
byte colSecIT[]{0, 0, 0};
|
||||
byte white = whiteS, whiteOld, whiteT, whiteIT;
|
||||
byte whiteSec = whiteSecS, whiteSecOld, whiteSecT, whiteSecIT;
|
||||
byte col[]{255, 159, 0, 0}; //target RGB(W) color
|
||||
byte colOld[]{0, 0, 0, 0}; //color before transition
|
||||
byte colT[]{0, 0, 0, 0}; //current color
|
||||
byte colIT[]{0, 0, 0, 0}; //color that was last sent to LEDs
|
||||
byte colSec[]{0, 0, 0, 0};
|
||||
byte colSecT[]{0, 0, 0, 0};
|
||||
byte colSecOld[]{0, 0, 0, 0};
|
||||
byte colSecIT[]{0, 0, 0, 0};
|
||||
|
||||
byte lastRandomIndex = 0; //used to save last random color so the new one is not the same
|
||||
|
||||
@@ -240,10 +288,13 @@ float tperLast = 0; //crossfade transition progress, 0
|
||||
bool nightlightActive = false;
|
||||
bool nightlightActiveOld = false;
|
||||
uint32_t nightlightDelayMs = 10;
|
||||
uint8_t nightlightDelayMinsDefault = nightlightDelayMins;
|
||||
unsigned long nightlightStartTime;
|
||||
byte briNlT = 0; //current nightlight brightness
|
||||
|
||||
//brightness
|
||||
unsigned long lastOnTime = 0;
|
||||
bool offMode = !turnOnAtBoot;
|
||||
byte bri = briS;
|
||||
byte briOld = 0;
|
||||
byte briT = 0;
|
||||
@@ -252,7 +303,9 @@ byte briLast = 127; //brightness before turned off. Us
|
||||
|
||||
//button
|
||||
bool buttonPressedBefore = false;
|
||||
bool buttonLongPressed = false;
|
||||
unsigned long buttonPressedTime = 0;
|
||||
unsigned long buttonWaitTime = 0;
|
||||
|
||||
//notifications
|
||||
bool notifyDirectDefault = notifyDirect;
|
||||
@@ -268,23 +321,22 @@ byte effectIntensity = effectIntensityDefault;
|
||||
byte effectPalette = effectPaletteDefault;
|
||||
|
||||
//network
|
||||
bool onlyAP = false; //only Access Point active, no connection to home network
|
||||
bool udpConnected = false, udpRgbConnected = false;
|
||||
|
||||
//ui style
|
||||
char cssCol[6][9]={"","","","","",""};
|
||||
String cssColorString="";
|
||||
bool showWelcomePage = false;
|
||||
|
||||
//hue
|
||||
char hueError[25] = "Inactive";
|
||||
uint16_t hueFailCount = 0;
|
||||
//uint16_t hueFailCount = 0;
|
||||
float hueXLast=0, hueYLast=0;
|
||||
uint16_t hueHueLast=0, hueCtLast=0;
|
||||
byte hueSatLast=0, hueBriLast=0;
|
||||
unsigned long hueLastRequestSent = 0;
|
||||
unsigned long huePollIntervalMsTemp = huePollIntervalMs;
|
||||
bool hueAttempt = false;
|
||||
bool hueAuthRequired = false;
|
||||
bool hueReceived = false;
|
||||
bool hueStoreAllowed = false, hueNewKey = false;
|
||||
|
||||
//overlays
|
||||
byte overlayCurrent = overlayDefault;
|
||||
@@ -311,7 +363,7 @@ byte timerHours[] = {0,0,0,0,0,0,0,0};
|
||||
byte timerMinutes[] = {0,0,0,0,0,0,0,0};
|
||||
byte timerMacro[] = {0,0,0,0,0,0,0,0};
|
||||
byte timerWeekday[] = {255,255,255,255,255,255,255,255}; //weekdays to activate on
|
||||
//bit pattern of arr elem: 0b11111111: sat,fri,thu,wed,tue,mon,sun,validity
|
||||
//bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity
|
||||
|
||||
//blynk
|
||||
bool blynkEnabled = false;
|
||||
@@ -321,7 +373,7 @@ bool presetCyclingEnabled = false;
|
||||
byte presetCycleMin = 1, presetCycleMax = 5;
|
||||
uint16_t presetCycleTime = 1250;
|
||||
unsigned long presetCycledTime = 0; byte presetCycCurr = presetCycleMin;
|
||||
bool presetApplyBri = true, presetApplyCol = true, presetApplyFx = true;
|
||||
bool presetApplyBri = false, presetApplyCol = true, presetApplyFx = true;
|
||||
bool saveCurrPresetCycConf = false;
|
||||
|
||||
//realtime
|
||||
@@ -330,26 +382,26 @@ IPAddress realtimeIP = (0,0,0,0);
|
||||
unsigned long realtimeTimeout = 0;
|
||||
|
||||
//mqtt
|
||||
bool mqttInit = false;
|
||||
long lastMQTTReconnectAttempt = 0;
|
||||
long lastMqttReconnectAttempt = 0;
|
||||
long lastInterfaceUpdate = 0;
|
||||
byte interfaceUpdateCallMode = 0;
|
||||
uint32_t mqttFailedConAttempts = 0;
|
||||
|
||||
#if AUXPIN >= 0
|
||||
//auxiliary debug pin
|
||||
byte auxTime = 0;
|
||||
unsigned long auxStartTime = 0;
|
||||
bool auxActive = false, auxActiveBefore = false;
|
||||
#endif
|
||||
|
||||
//alexa udp
|
||||
WiFiUDP alexaUDP;
|
||||
IPAddress ipMulti(239, 255, 255, 250);
|
||||
unsigned int portMulti = 1900;
|
||||
String escapedMac;
|
||||
#ifndef WLED_DISABLE_ALEXA
|
||||
Espalexa espalexa;
|
||||
EspalexaDevice* espalexaDevice;
|
||||
#endif
|
||||
|
||||
//dns server
|
||||
DNSServer dnsServer;
|
||||
bool dnsActive = false;
|
||||
|
||||
//network time
|
||||
bool ntpConnected = false;
|
||||
@@ -360,22 +412,25 @@ IPAddress ntpServerIP;
|
||||
unsigned int ntpLocalPort = 2390;
|
||||
#define NTP_PACKET_SIZE 48
|
||||
|
||||
//string temp buffer
|
||||
#define OMAX 1750
|
||||
char obuf[OMAX];
|
||||
#define MAX_LEDS 1500
|
||||
#define MAX_LEDS_DMA 500
|
||||
|
||||
//string temp buffer (now stored in stack locally)
|
||||
#define OMAX 2048
|
||||
char* obuf;
|
||||
uint16_t olen = 0;
|
||||
|
||||
//server library objects
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
WebServer server(80);
|
||||
#else
|
||||
ESP8266WebServer server(80);
|
||||
#endif
|
||||
HTTPClient* hueClient = NULL;
|
||||
WiFiClient* mqttTCPClient = NULL;
|
||||
PubSubClient* mqtt = NULL;
|
||||
String messageHead, messageSub;
|
||||
byte optionType;
|
||||
|
||||
ESP8266HTTPUpdateServer httpUpdater;
|
||||
bool doReboot = false; //flag to initiate reboot from async handlers
|
||||
bool doPublishMqtt = false;
|
||||
bool doSendHADiscovery = true;
|
||||
|
||||
//server library objects
|
||||
AsyncWebServer server(80);
|
||||
AsyncClient* hueClient = NULL;
|
||||
AsyncMqttClient* mqtt = NULL;
|
||||
|
||||
//udp interface objects
|
||||
WiFiUDP notifierUdp, rgbUdp;
|
||||
@@ -385,14 +440,18 @@ E131* e131;
|
||||
//led fx library object
|
||||
WS2812FX strip = WS2812FX();
|
||||
|
||||
#define WLED_CONNECTED (WiFi.status() == WL_CONNECTED)
|
||||
#define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID,"Your_Network") != 0)
|
||||
|
||||
//debug macros
|
||||
#ifdef DEBUG
|
||||
#ifdef WLED_DEBUG
|
||||
#define DEBUG_PRINT(x) Serial.print (x)
|
||||
#define DEBUG_PRINTLN(x) Serial.println (x)
|
||||
#define DEBUG_PRINTF(x) Serial.printf (x)
|
||||
unsigned long debugTime = 0;
|
||||
int lastWifiState = 3;
|
||||
unsigned long wifiStateChangedTime = 0;
|
||||
int loops = 0;
|
||||
#else
|
||||
#define DEBUG_PRINT(x)
|
||||
#define DEBUG_PRINTLN(x)
|
||||
@@ -400,40 +459,28 @@ WS2812FX strip = WS2812FX();
|
||||
#endif
|
||||
|
||||
//filesystem
|
||||
#ifdef USEFS
|
||||
#include <FS.h>;
|
||||
File fsUploadFile;
|
||||
#ifndef WLED_DISABLE_FILESYSTEM
|
||||
#include <FS.h>
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include "SPIFFS.h"
|
||||
#endif
|
||||
#include "SPIFFSEditor.h"
|
||||
#endif
|
||||
|
||||
//gamma 2.4 lookup table used for color correction
|
||||
const byte gamma8[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
|
||||
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
|
||||
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
|
||||
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
|
||||
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
|
||||
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
|
||||
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
|
||||
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
|
||||
90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
|
||||
115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
|
||||
144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
|
||||
177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
|
||||
215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };
|
||||
|
||||
String txd = "Please disable OTA Lock in security settings!";
|
||||
|
||||
//function prototypes
|
||||
void serveMessage(int,String,String,int=255);
|
||||
void serveMessage(AsyncWebServerRequest*,uint16_t,String,String,byte);
|
||||
|
||||
|
||||
//turns all LEDs off and restarts ESP
|
||||
void reset()
|
||||
{
|
||||
briT = 0;
|
||||
long dly = millis();
|
||||
while(millis() - dly < 250)
|
||||
{
|
||||
yield(); //enough time to send response to client
|
||||
}
|
||||
setAllLeds();
|
||||
DEBUG_PRINTLN("MODULE RESET");
|
||||
ESP.restart();
|
||||
@@ -454,7 +501,7 @@ bool oappend(char* txt)
|
||||
//append new number to temp buffer efficiently
|
||||
bool oappendi(int i)
|
||||
{
|
||||
char s[11];
|
||||
char s[11];
|
||||
sprintf(s,"%ld", i);
|
||||
return oappend(s);
|
||||
}
|
||||
@@ -462,64 +509,68 @@ bool oappendi(int i)
|
||||
|
||||
//boot starts here
|
||||
void setup() {
|
||||
wledInit();
|
||||
wledInit();
|
||||
}
|
||||
|
||||
|
||||
//main program loop
|
||||
void loop() {
|
||||
server.handleClient();
|
||||
handleSerial();
|
||||
handleNotifications();
|
||||
handleTransitions();
|
||||
userLoop();
|
||||
|
||||
yield();
|
||||
handleButton();
|
||||
handleNetworkTime();
|
||||
if (!onlyAP)
|
||||
{
|
||||
handleAlexa();
|
||||
handleMQTT();
|
||||
}
|
||||
|
||||
handleOverlays();
|
||||
handleConnection();
|
||||
handleSerial();
|
||||
handleNotifications();
|
||||
handleTransitions();
|
||||
userLoop();
|
||||
|
||||
yield();
|
||||
|
||||
|
||||
if (!realtimeActive) //block stuff if WARLS/Adalight is enabled
|
||||
{
|
||||
if (dnsActive) dnsServer.processNextRequest();
|
||||
if (aOtaEnabled) ArduinoOTA.handle();
|
||||
handleNightlight();
|
||||
if (!onlyAP) {
|
||||
handleHue();
|
||||
handleBlynk();
|
||||
}
|
||||
if (briT) strip.service(); //do not update strip if off, prevents flicker on ESP32
|
||||
}
|
||||
|
||||
//DEBUG serial logging
|
||||
#ifdef DEBUG
|
||||
if (millis() - debugTime > 5000)
|
||||
{
|
||||
DEBUG_PRINTLN("---MODULE DEBUG INFO---");
|
||||
DEBUG_PRINT("Runtime: "); DEBUG_PRINTLN(millis());
|
||||
DEBUG_PRINT("Unix time: "); DEBUG_PRINTLN(now());
|
||||
DEBUG_PRINT("Free heap: "); DEBUG_PRINTLN(ESP.getFreeHeap());
|
||||
DEBUG_PRINT("Wifi state: "); DEBUG_PRINTLN(WiFi.status());
|
||||
if (WiFi.status() != lastWifiState)
|
||||
{
|
||||
wifiStateChangedTime = millis();
|
||||
}
|
||||
lastWifiState = WiFi.status();
|
||||
DEBUG_PRINT("State time: "); DEBUG_PRINTLN(wifiStateChangedTime);
|
||||
DEBUG_PRINT("NTP last sync: "); DEBUG_PRINTLN(ntpLastSyncTime);
|
||||
DEBUG_PRINT("Client IP: "); DEBUG_PRINTLN(WiFi.localIP());
|
||||
debugTime = millis();
|
||||
}
|
||||
yield();
|
||||
handleIO();
|
||||
handleIR();
|
||||
handleNetworkTime();
|
||||
handleAlexa();
|
||||
|
||||
handleOverlays();
|
||||
if (doSendHADiscovery) sendHADiscoveryMQTT();
|
||||
yield();
|
||||
if (doReboot) reset();
|
||||
|
||||
if (!realtimeActive) //block stuff if WARLS/Adalight is enabled
|
||||
{
|
||||
if (apActive) dnsServer.processNextRequest();
|
||||
#ifndef WLED_DISABLE_OTA
|
||||
if (WLED_CONNECTED && aOtaEnabled) ArduinoOTA.handle();
|
||||
#endif
|
||||
handleNightlight();
|
||||
yield();
|
||||
|
||||
handleHue();
|
||||
handleBlynk();
|
||||
|
||||
yield();
|
||||
if (!offMode) strip.service();
|
||||
}
|
||||
yield();
|
||||
if (millis() - lastMqttReconnectAttempt > 30000) initMqtt();
|
||||
|
||||
//DEBUG serial logging
|
||||
#ifdef WLED_DEBUG
|
||||
if (millis() - debugTime > 9999)
|
||||
{
|
||||
DEBUG_PRINTLN("---DEBUG INFO---");
|
||||
DEBUG_PRINT("Runtime: "); DEBUG_PRINTLN(millis());
|
||||
DEBUG_PRINT("Unix time: "); DEBUG_PRINTLN(now());
|
||||
DEBUG_PRINT("Free heap: "); DEBUG_PRINTLN(ESP.getFreeHeap());
|
||||
DEBUG_PRINT("Wifi state: "); DEBUG_PRINTLN(WiFi.status());
|
||||
if (WiFi.status() != lastWifiState)
|
||||
{
|
||||
wifiStateChangedTime = millis();
|
||||
}
|
||||
lastWifiState = WiFi.status();
|
||||
DEBUG_PRINT("State time: "); DEBUG_PRINTLN(wifiStateChangedTime);
|
||||
DEBUG_PRINT("NTP last sync: "); DEBUG_PRINTLN(ntpLastSyncTime);
|
||||
DEBUG_PRINT("Client IP: "); DEBUG_PRINTLN(WiFi.localIP());
|
||||
DEBUG_PRINT("Loops/sec: "); DEBUG_PRINTLN(loops/10);
|
||||
loops = 0;
|
||||
debugTime = millis();
|
||||
}
|
||||
loops++;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
367
wled00/wled00.vcxproj
Normal file
367
wled00/wled00.vcxproj
Normal file
File diff suppressed because one or more lines are too long
272
wled00/wled00.vcxproj.filters
Normal file
272
wled00/wled00.vcxproj.filters
Normal file
@@ -0,0 +1,272 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="wled00.ino" />
|
||||
<None Include="wled01_eeprom.ino" />
|
||||
<None Include="wled02_xml.ino" />
|
||||
<None Include="wled03_set.ino" />
|
||||
<None Include="wled04_file.ino" />
|
||||
<None Include="wled05_init.ino" />
|
||||
<None Include="wled06_usermod.ino" />
|
||||
<None Include="wled07_notify.ino" />
|
||||
<None Include="wled08_led.ino" />
|
||||
<None Include="wled09_button.ino" />
|
||||
<None Include="wled10_ntp.ino" />
|
||||
<None Include="wled11_ol.ino" />
|
||||
<None Include="wled12_alexa.ino" />
|
||||
<None Include="wled13_cronixie.ino" />
|
||||
<None Include="wled14_colors.ino" />
|
||||
<None Include="wled15_hue.ino" />
|
||||
<None Include="wled16_blynk.ino" />
|
||||
<None Include="wled17_mqtt.ino" />
|
||||
<None Include="wled18_server.ino" />
|
||||
<None Include="wled19_json.ino" />
|
||||
<None Include="wled20_ir.ino" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="__vm\.wled00.vsarduino.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="html_classic.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="html_mobile.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="html_other.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="html_settings.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ir_codes.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="NpbWrapper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="palettes.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WS2812FX.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Callbacks.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\DisconnectReasons.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Flags.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Helpers.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\MessageProperties.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\ParsingInformation.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Storage.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\ConnAckPacket.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\Packet.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PingRespPacket.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PubAckPacket.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PubCompPacket.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PublishPacket.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PubRecPacket.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PubRelPacket.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\SubAckPacket.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\UnsubAckPacket.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\blynk\BlynkSimpleEsp.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkApi.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkApiArduino.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkArduinoClient.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkConfig.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkDateTime.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkDebug.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkDetectDevice.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkEveryN.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkFifo.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkHandlers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkParam.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkProtocol.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkProtocolDefs.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkTemplates.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkTimer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkUtility.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkWidgetBase.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkWiFiCommon.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\e131\E131.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\espalexa\Espalexa.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\espalexa\EspalexaDevice.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\json\ArduinoJson-v6.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\json\AsyncJson-v6.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\time\Time.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\time\TimeLib.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\dependencies\timezone\Timezone.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="WS2812FX.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WS2812FX_fcn.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\dependencies\async-mqtt-client\AsyncMqttClient.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\ConnAckPacket.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PingRespPacket.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PubAckPacket.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PubCompPacket.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PublishPacket.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PubRecPacket.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PubRelPacket.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\SubAckPacket.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\UnsubAckPacket.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\dependencies\blynk\Blynk\BlynkDebug.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\dependencies\blynk\Blynk\BlynkHandlers.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\dependencies\blynk\Blynk\BlynkTimer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\dependencies\blynk\Blynk\utility.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\dependencies\e131\E131.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\dependencies\espalexa\EspalexaDevice.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\dependencies\time\DateStrings.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\dependencies\time\Time.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\dependencies\timezone\Timezone.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
4
wled00/wled00.vcxproj.user
Normal file
4
wled00/wled00.vcxproj.user
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
||||
@@ -3,10 +3,10 @@
|
||||
* EEPROM Map: https://github.com/Aircoookie/WLED/wiki/EEPROM-Map
|
||||
*/
|
||||
|
||||
#define EEPSIZE 3072
|
||||
#define EEPSIZE 2560
|
||||
|
||||
//eeprom Version code, enables default settings instead of 0 init on update
|
||||
#define EEPVER 9
|
||||
#define EEPVER 11
|
||||
//0 -> old version, default
|
||||
//1 -> 0.4p 1711272 and up
|
||||
//2 -> 0.4p 1711302 and up
|
||||
@@ -17,6 +17,9 @@
|
||||
//7 -> 0.7.1 and up
|
||||
//8 -> 0.8.0-a and up
|
||||
//9 -> 0.8.0
|
||||
//10-> 0.8.2
|
||||
//11-> 0.8.5-dev #mqttauth @TimothyBrown
|
||||
|
||||
|
||||
/*
|
||||
* Erase all configuration data
|
||||
@@ -30,6 +33,7 @@ void clearEEPROM()
|
||||
EEPROM.commit();
|
||||
}
|
||||
|
||||
|
||||
void writeStringToEEPROM(uint16_t pos, char* str, uint16_t len)
|
||||
{
|
||||
for (int i = 0; i < len; ++i)
|
||||
@@ -39,6 +43,7 @@ void writeStringToEEPROM(uint16_t pos, char* str, uint16_t len)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void readStringFromEEPROM(uint16_t pos, char* str, uint16_t len)
|
||||
{
|
||||
for (int i = 0; i < len; ++i)
|
||||
@@ -49,6 +54,7 @@ void readStringFromEEPROM(uint16_t pos, char* str, uint16_t len)
|
||||
str[len] = 0; //make sure every string is properly terminated. str must be at least len +1 big.
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write configuration to flash
|
||||
*/
|
||||
@@ -59,96 +65,101 @@ void saveSettingsToEEPROM()
|
||||
clearEEPROM();
|
||||
EEPROM.write(233, 233);
|
||||
}
|
||||
|
||||
|
||||
writeStringToEEPROM( 0, clientSSID, 32);
|
||||
writeStringToEEPROM( 32, clientPass, 64);
|
||||
writeStringToEEPROM( 96, cmDNS, 32);
|
||||
writeStringToEEPROM(128, apSSID, 32);
|
||||
writeStringToEEPROM(160, apPass, 64);
|
||||
|
||||
EEPROM.write(224, nightlightDelayMins);
|
||||
EEPROM.write(224, nightlightDelayMinsDefault);
|
||||
EEPROM.write(225, nightlightFade);
|
||||
EEPROM.write(226, notifyDirectDefault);
|
||||
EEPROM.write(227, apChannel);
|
||||
EEPROM.write(228, apHide);
|
||||
EEPROM.write(229, (ledCount >> 0) & 0xFF);
|
||||
EEPROM.write(229, ledCount & 0xFF);
|
||||
EEPROM.write(230, notifyButton);
|
||||
EEPROM.write(231, notifyTwice);
|
||||
EEPROM.write(232, buttonEnabled);
|
||||
//233 reserved for first boot flag
|
||||
|
||||
|
||||
for (int i = 0; i<4; i++) //ip addresses
|
||||
{
|
||||
EEPROM.write(234+i, staticIP[i]);
|
||||
EEPROM.write(238+i, staticGateway[i]);
|
||||
EEPROM.write(242+i, staticSubnet[i]);
|
||||
}
|
||||
|
||||
|
||||
EEPROM.write(246, colS[0]);
|
||||
EEPROM.write(247, colS[1]);
|
||||
EEPROM.write(248, colS[2]);
|
||||
EEPROM.write(249, briS);
|
||||
|
||||
|
||||
EEPROM.write(250, receiveNotificationBrightness);
|
||||
EEPROM.write(251, fadeTransition);
|
||||
EEPROM.write(252, reverseMode);
|
||||
EEPROM.write(253, (transitionDelayDefault >> 0) & 0xFF);
|
||||
EEPROM.write(252, strip.reverseMode);
|
||||
EEPROM.write(253, transitionDelayDefault & 0xFF);
|
||||
EEPROM.write(254, (transitionDelayDefault >> 8) & 0xFF);
|
||||
EEPROM.write(255, briMultiplier);
|
||||
|
||||
|
||||
//255,250,231,230,226 notifier bytes
|
||||
writeStringToEEPROM(256, otaPass, 32);
|
||||
|
||||
|
||||
EEPROM.write(288, nightlightTargetBri);
|
||||
EEPROM.write(289, otaLock);
|
||||
EEPROM.write(290, (udpPort >> 0) & 0xFF);
|
||||
EEPROM.write(290, udpPort & 0xFF);
|
||||
EEPROM.write(291, (udpPort >> 8) & 0xFF);
|
||||
writeStringToEEPROM(292, serverDescription, 32);
|
||||
|
||||
|
||||
EEPROM.write(324, effectDefault);
|
||||
EEPROM.write(325, effectSpeedDefault);
|
||||
EEPROM.write(326, effectIntensityDefault);
|
||||
|
||||
|
||||
EEPROM.write(327, ntpEnabled);
|
||||
EEPROM.write(328, currentTimezone);
|
||||
EEPROM.write(329, useAMPM);
|
||||
EEPROM.write(330, useGammaCorrectionBri);
|
||||
EEPROM.write(331, useGammaCorrectionRGB);
|
||||
EEPROM.write(330, strip.gammaCorrectBri);
|
||||
EEPROM.write(331, strip.gammaCorrectCol);
|
||||
EEPROM.write(332, overlayDefault);
|
||||
|
||||
|
||||
EEPROM.write(333, alexaEnabled);
|
||||
writeStringToEEPROM(334, alexaInvocationName, 32);
|
||||
EEPROM.write(366, notifyAlexa);
|
||||
|
||||
|
||||
EEPROM.write(367, (arlsOffset>=0));
|
||||
EEPROM.write(368, abs(arlsOffset));
|
||||
EEPROM.write(369, turnOnAtBoot);
|
||||
EEPROM.write(370, useHSBDefault);
|
||||
EEPROM.write(371, whiteS);
|
||||
EEPROM.write(371, colS[3]); //white default
|
||||
EEPROM.write(372, useRGBW);
|
||||
EEPROM.write(373, effectPaletteDefault);
|
||||
EEPROM.write(374, strip.paletteFade);
|
||||
EEPROM.write(375, apWaitTimeSecs);
|
||||
EEPROM.write(376, recoveryAPDisabled);
|
||||
|
||||
//EEPROM.write(375, apWaitTimeSecs);
|
||||
EEPROM.write(376, apBehavior);
|
||||
|
||||
EEPROM.write(377, EEPVER); //eeprom was updated to latest
|
||||
|
||||
|
||||
EEPROM.write(378, colSecS[0]);
|
||||
EEPROM.write(379, colSecS[1]);
|
||||
EEPROM.write(380, colSecS[2]);
|
||||
EEPROM.write(381, whiteSecS);
|
||||
EEPROM.write(381, colSecS[3]);
|
||||
EEPROM.write(382, strip.paletteBlend);
|
||||
|
||||
EEPROM.write(383, strip.colorOrder);
|
||||
|
||||
EEPROM.write(385, irEnabled);
|
||||
|
||||
EEPROM.write(387, strip.ablMilliampsMax & 0xFF);
|
||||
EEPROM.write(388, (strip.ablMilliampsMax >> 8) & 0xFF);
|
||||
EEPROM.write(389, bootPreset);
|
||||
EEPROM.write(390, aOtaEnabled);
|
||||
EEPROM.write(391, receiveNotificationColor);
|
||||
EEPROM.write(392, receiveNotificationEffects);
|
||||
EEPROM.write(393, wifiLock);
|
||||
|
||||
EEPROM.write(394, (abs(utcOffsetSecs) >> 0) & 0xFF);
|
||||
|
||||
EEPROM.write(394, abs(utcOffsetSecs) & 0xFF);
|
||||
EEPROM.write(395, (abs(utcOffsetSecs) >> 8) & 0xFF);
|
||||
EEPROM.write(396, (utcOffsetSecs<0)); //is negative
|
||||
EEPROM.write(397, initLedsLast);
|
||||
//397 was initLedsLast
|
||||
EEPROM.write(398, (ledCount >> 8) & 0xFF);
|
||||
EEPROM.write(399, !enableSecTransition);
|
||||
|
||||
@@ -170,7 +181,7 @@ void saveSettingsToEEPROM()
|
||||
EEPROM.write(i, hueIP[i-2050]);
|
||||
}
|
||||
writeStringToEEPROM(2054, hueApiKey, 46);
|
||||
EEPROM.write(2100, (huePollIntervalMs >> 0) & 0xFF);
|
||||
EEPROM.write(2100, huePollIntervalMs & 0xFF);
|
||||
EEPROM.write(2101, (huePollIntervalMs >> 8) & 0xFF);
|
||||
EEPROM.write(2102, notifyHue);
|
||||
EEPROM.write(2103, hueApplyOnOff);
|
||||
@@ -183,7 +194,7 @@ void saveSettingsToEEPROM()
|
||||
EEPROM.write(2152, analogClock12pixel);
|
||||
EEPROM.write(2153, analogClock5MinuteMarks);
|
||||
EEPROM.write(2154, analogClockSecondsTrail);
|
||||
|
||||
|
||||
EEPROM.write(2155, countdownMode);
|
||||
EEPROM.write(2156, countdownYear);
|
||||
EEPROM.write(2157, countdownMonth);
|
||||
@@ -196,7 +207,7 @@ void saveSettingsToEEPROM()
|
||||
writeStringToEEPROM(2165, cronixieDisplay, 6);
|
||||
EEPROM.write(2171, cronixieBacklight);
|
||||
setCronixie();
|
||||
|
||||
|
||||
EEPROM.write(2175, macroBoot);
|
||||
EEPROM.write(2176, macroAlexaOn);
|
||||
EEPROM.write(2177, macroAlexaOff);
|
||||
@@ -204,17 +215,18 @@ void saveSettingsToEEPROM()
|
||||
EEPROM.write(2179, macroLongPress);
|
||||
EEPROM.write(2180, macroCountdown);
|
||||
EEPROM.write(2181, macroNl);
|
||||
EEPROM.write(2182, macroDoublePress);
|
||||
|
||||
EEPROM.write(2190, (e131Universe >> 0) & 0xFF);
|
||||
EEPROM.write(2190, e131Universe & 0xFF);
|
||||
EEPROM.write(2191, (e131Universe >> 8) & 0xFF);
|
||||
EEPROM.write(2192, e131Multicast);
|
||||
EEPROM.write(2193, (realtimeTimeoutMs >> 0) & 0xFF);
|
||||
EEPROM.write(2193, realtimeTimeoutMs & 0xFF);
|
||||
EEPROM.write(2194, (realtimeTimeoutMs >> 8) & 0xFF);
|
||||
EEPROM.write(2195, arlsForceMaxBri);
|
||||
EEPROM.write(2196, arlsDisableGammaCorrection);
|
||||
|
||||
EEPROM.write(2200, !receiveDirect);
|
||||
EEPROM.write(2201, enableRealtimeUI);
|
||||
EEPROM.write(2201, notifyMacro); //was enableRealtime
|
||||
EEPROM.write(2202, uiConfiguration);
|
||||
EEPROM.write(2203, autoRGBtoRGBW);
|
||||
EEPROM.write(2204, skipFirstLed);
|
||||
@@ -222,7 +234,7 @@ void saveSettingsToEEPROM()
|
||||
if (saveCurrPresetCycConf)
|
||||
{
|
||||
EEPROM.write(2205, presetCyclingEnabled);
|
||||
EEPROM.write(2206, (presetCycleTime >> 0) & 0xFF);
|
||||
EEPROM.write(2206, presetCycleTime & 0xFF);
|
||||
EEPROM.write(2207, (presetCycleTime >> 8) & 0xFF);
|
||||
EEPROM.write(2208, presetCycleMin);
|
||||
EEPROM.write(2209, presetCycleMax);
|
||||
@@ -242,13 +254,19 @@ void saveSettingsToEEPROM()
|
||||
EEPROM.write(2290 + i, timerMacro[i] );
|
||||
}
|
||||
|
||||
writeStringToEEPROM(2300, mqttServer, 32);
|
||||
writeStringToEEPROM(2300, mqttServer, 32);
|
||||
writeStringToEEPROM(2333, mqttDeviceTopic, 32);
|
||||
writeStringToEEPROM(2366, mqttGroupTopic, 32);
|
||||
|
||||
writeStringToEEPROM(2366, mqttGroupTopic, 32);
|
||||
writeStringToEEPROM(2399, mqttUser, 40);
|
||||
writeStringToEEPROM(2440, mqttPass, 40);
|
||||
writeStringToEEPROM(2481, mqttClientID, 40);
|
||||
EEPROM.write(2522, mqttPort & 0xFF);
|
||||
EEPROM.write(2523, (mqttPort >> 8) & 0xFF);
|
||||
|
||||
EEPROM.commit();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Read all configuration from flash
|
||||
*/
|
||||
@@ -256,11 +274,13 @@ void loadSettingsFromEEPROM(bool first)
|
||||
{
|
||||
if (EEPROM.read(233) != 233) //first boot/reset to default
|
||||
{
|
||||
DEBUG_PRINT("Settings invalid, restoring defaults...");
|
||||
saveSettingsToEEPROM();
|
||||
DEBUG_PRINTLN("done");
|
||||
return;
|
||||
}
|
||||
int lastEEPROMversion = EEPROM.read(377); //last EEPROM version before update
|
||||
|
||||
|
||||
|
||||
readStringFromEEPROM( 0, clientSSID, 32);
|
||||
readStringFromEEPROM( 32, clientPass, 64);
|
||||
@@ -268,21 +288,22 @@ void loadSettingsFromEEPROM(bool first)
|
||||
readStringFromEEPROM(128, apSSID, 32);
|
||||
readStringFromEEPROM(160, apPass, 64);
|
||||
|
||||
nightlightDelayMins = EEPROM.read(224);
|
||||
nightlightDelayMinsDefault = EEPROM.read(224);
|
||||
nightlightDelayMins = nightlightDelayMinsDefault;
|
||||
nightlightFade = EEPROM.read(225);
|
||||
notifyDirectDefault = EEPROM.read(226);
|
||||
notifyDirect = notifyDirectDefault;
|
||||
|
||||
|
||||
apChannel = EEPROM.read(227);
|
||||
if (apChannel > 13 || apChannel < 1) apChannel = 1;
|
||||
apHide = EEPROM.read(228);
|
||||
if (apHide > 1) apHide = 1;
|
||||
ledCount = ((EEPROM.read(229) << 0) & 0xFF) + ((EEPROM.read(398) << 8) & 0xFF00); if (ledCount > 1200 || ledCount == 0) ledCount = 10;
|
||||
|
||||
ledCount = EEPROM.read(229) + ((EEPROM.read(398) << 8) & 0xFF00); if (ledCount > MAX_LEDS || ledCount == 0) ledCount = 30;
|
||||
|
||||
notifyButton = EEPROM.read(230);
|
||||
notifyTwice = EEPROM.read(231);
|
||||
buttonEnabled = EEPROM.read(232);
|
||||
|
||||
|
||||
staticIP[0] = EEPROM.read(234);
|
||||
staticIP[1] = EEPROM.read(235);
|
||||
staticIP[2] = EEPROM.read(236);
|
||||
@@ -295,7 +316,7 @@ void loadSettingsFromEEPROM(bool first)
|
||||
staticSubnet[1] = EEPROM.read(243);
|
||||
staticSubnet[2] = EEPROM.read(244);
|
||||
staticSubnet[3] = EEPROM.read(245);
|
||||
|
||||
|
||||
colS[0] = EEPROM.read(246); col[0] = colS[0];
|
||||
colS[1] = EEPROM.read(247); col[1] = colS[1];
|
||||
colS[2] = EEPROM.read(248); col[2] = colS[2];
|
||||
@@ -306,56 +327,56 @@ void loadSettingsFromEEPROM(bool first)
|
||||
}
|
||||
receiveNotificationBrightness = EEPROM.read(250);
|
||||
fadeTransition = EEPROM.read(251);
|
||||
reverseMode = EEPROM.read(252);
|
||||
transitionDelayDefault = ((EEPROM.read(253) << 0) & 0xFF) + ((EEPROM.read(254) << 8) & 0xFF00);
|
||||
strip.reverseMode = EEPROM.read(252);
|
||||
transitionDelayDefault = EEPROM.read(253) + ((EEPROM.read(254) << 8) & 0xFF00);
|
||||
transitionDelay = transitionDelayDefault;
|
||||
briMultiplier = EEPROM.read(255);
|
||||
|
||||
readStringFromEEPROM(256, otaPass, 32);
|
||||
|
||||
|
||||
nightlightTargetBri = EEPROM.read(288);
|
||||
otaLock = EEPROM.read(289);
|
||||
udpPort = ((EEPROM.read(290) << 0) & 0xFF) + ((EEPROM.read(291) << 8) & 0xFF00);
|
||||
udpPort = EEPROM.read(290) + ((EEPROM.read(291) << 8) & 0xFF00);
|
||||
|
||||
readStringFromEEPROM(292, serverDescription, 32);
|
||||
|
||||
|
||||
effectDefault = EEPROM.read(324); effectCurrent = effectDefault;
|
||||
effectSpeedDefault = EEPROM.read(325); effectSpeed = effectSpeedDefault;
|
||||
ntpEnabled = EEPROM.read(327);
|
||||
currentTimezone = EEPROM.read(328);
|
||||
useAMPM = EEPROM.read(329);
|
||||
useGammaCorrectionBri = EEPROM.read(330);
|
||||
useGammaCorrectionRGB = EEPROM.read(331);
|
||||
strip.gammaCorrectBri = EEPROM.read(330);
|
||||
strip.gammaCorrectCol = EEPROM.read(331);
|
||||
overlayDefault = EEPROM.read(332);
|
||||
if (lastEEPROMversion < 8 && overlayDefault > 0) overlayDefault--; //overlay mode 1 (solid) was removed
|
||||
|
||||
|
||||
alexaEnabled = EEPROM.read(333);
|
||||
|
||||
readStringFromEEPROM(334, alexaInvocationName, 32);
|
||||
|
||||
|
||||
notifyAlexa = EEPROM.read(366);
|
||||
arlsOffset = EEPROM.read(368);
|
||||
if (!EEPROM.read(367)) arlsOffset = -arlsOffset;
|
||||
turnOnAtBoot = EEPROM.read(369);
|
||||
useHSBDefault = EEPROM.read(370);
|
||||
whiteS = EEPROM.read(371); white = whiteS;
|
||||
colS[3] = EEPROM.read(371); col[3] = colS[3];
|
||||
useRGBW = EEPROM.read(372);
|
||||
effectPaletteDefault = EEPROM.read(373); effectPalette = effectPaletteDefault;
|
||||
//374 - strip.paletteFade
|
||||
|
||||
if (lastEEPROMversion > 0) {
|
||||
apWaitTimeSecs = EEPROM.read(375);
|
||||
recoveryAPDisabled = EEPROM.read(376);
|
||||
if (lastEEPROMversion > 0) {
|
||||
//apWaitTimeSecs = EEPROM.read(375);
|
||||
apBehavior = EEPROM.read(376);
|
||||
}
|
||||
//377 = lastEEPROMversion
|
||||
if (lastEEPROMversion > 1) {
|
||||
colSecS[0] = EEPROM.read(378); colSec[0] = colSecS[0];
|
||||
colSecS[1] = EEPROM.read(379); colSec[1] = colSecS[1];
|
||||
colSecS[2] = EEPROM.read(380); colSec[2] = colSecS[2];
|
||||
whiteSecS = EEPROM.read(381); whiteSec = whiteSecS;
|
||||
for (byte i=0; i<4; i++)
|
||||
{
|
||||
colSecS[i] = EEPROM.read(378+i); colSec[i] = colSecS[i];
|
||||
}
|
||||
}
|
||||
if (lastEEPROMversion > 3) {
|
||||
effectIntensityDefault = EEPROM.read(326); effectIntensity = effectIntensityDefault;
|
||||
effectIntensityDefault = EEPROM.read(326); effectIntensity = effectIntensityDefault;
|
||||
aOtaEnabled = EEPROM.read(390);
|
||||
receiveNotificationColor = EEPROM.read(391);
|
||||
receiveNotificationEffects = EEPROM.read(392);
|
||||
@@ -376,8 +397,8 @@ void loadSettingsFromEEPROM(bool first)
|
||||
}
|
||||
|
||||
readStringFromEEPROM(2054, hueApiKey, 46);
|
||||
|
||||
huePollIntervalMs = ((EEPROM.read(2100) << 0) & 0xFF) + ((EEPROM.read(2101) << 8) & 0xFF00);
|
||||
|
||||
huePollIntervalMs = EEPROM.read(2100) + ((EEPROM.read(2101) << 8) & 0xFF00);
|
||||
notifyHue = EEPROM.read(2102);
|
||||
hueApplyOnOff = EEPROM.read(2103);
|
||||
hueApplyBri = EEPROM.read(2104);
|
||||
@@ -401,7 +422,7 @@ void loadSettingsFromEEPROM(bool first)
|
||||
|
||||
readStringFromEEPROM(2165, cronixieDisplay, 6);
|
||||
cronixieBacklight = EEPROM.read(2171);
|
||||
|
||||
|
||||
macroBoot = EEPROM.read(2175);
|
||||
macroAlexaOn = EEPROM.read(2176);
|
||||
macroAlexaOff = EEPROM.read(2177);
|
||||
@@ -409,13 +430,15 @@ void loadSettingsFromEEPROM(bool first)
|
||||
macroLongPress = EEPROM.read(2179);
|
||||
macroCountdown = EEPROM.read(2180);
|
||||
macroNl = EEPROM.read(2181);
|
||||
macroDoublePress = EEPROM.read(2182);
|
||||
if (macroDoublePress > 16) macroDoublePress = 0;
|
||||
}
|
||||
|
||||
if (lastEEPROMversion > 6)
|
||||
{
|
||||
e131Universe = ((EEPROM.read(2190) << 0) & 0xFF) + ((EEPROM.read(2191) << 8) & 0xFF00);
|
||||
e131Universe = EEPROM.read(2190) + ((EEPROM.read(2191) << 8) & 0xFF00);
|
||||
e131Multicast = EEPROM.read(2192);
|
||||
realtimeTimeoutMs = ((EEPROM.read(2193) << 0) & 0xFF) + ((EEPROM.read(2194) << 8) & 0xFF00);
|
||||
realtimeTimeoutMs = EEPROM.read(2193) + ((EEPROM.read(2194) << 8) & 0xFF00);
|
||||
arlsForceMaxBri = EEPROM.read(2195);
|
||||
arlsDisableGammaCorrection = EEPROM.read(2196);
|
||||
}
|
||||
@@ -430,46 +453,65 @@ void loadSettingsFromEEPROM(bool first)
|
||||
timerHours[i] = EEPROM.read(2260 + i);
|
||||
timerMinutes[i] = EEPROM.read(2270 + i);
|
||||
timerWeekday[i] = EEPROM.read(2280 + i);
|
||||
timerMacro[i] = EEPROM.read(2290 + i);
|
||||
if (timerWeekday[i] == 0) timerWeekday[i] = 255;
|
||||
timerMacro[i] = EEPROM.read(2290 + i);
|
||||
}
|
||||
}
|
||||
|
||||
if (lastEEPROMversion > 8)
|
||||
{
|
||||
readStringFromEEPROM(2300, mqttServer, 32);
|
||||
readStringFromEEPROM(2300, mqttServer, 32);
|
||||
readStringFromEEPROM(2333, mqttDeviceTopic, 32);
|
||||
readStringFromEEPROM(2366, mqttGroupTopic, 32);
|
||||
readStringFromEEPROM(2366, mqttGroupTopic, 32);
|
||||
}
|
||||
|
||||
|
||||
if (lastEEPROMversion > 9)
|
||||
{
|
||||
strip.colorOrder = EEPROM.read(383);
|
||||
irEnabled = EEPROM.read(385);
|
||||
strip.ablMilliampsMax = EEPROM.read(387) + ((EEPROM.read(388) << 8) & 0xFF00);
|
||||
} else if (lastEEPROMversion > 1) //ABL is off by default when updating from version older than 0.8.2
|
||||
{
|
||||
strip.ablMilliampsMax = 65000;
|
||||
} else {
|
||||
strip.ablMilliampsMax = ABL_MILLIAMPS_DEFAULT;
|
||||
}
|
||||
|
||||
if (lastEEPROMversion > 10)
|
||||
{
|
||||
readStringFromEEPROM(2399, mqttUser, 40);
|
||||
readStringFromEEPROM(2440, mqttPass, 40);
|
||||
readStringFromEEPROM(2481, mqttClientID, 40);
|
||||
mqttPort = EEPROM.read(2522) + ((EEPROM.read(2523) << 8) & 0xFF00);
|
||||
}
|
||||
|
||||
receiveDirect = !EEPROM.read(2200);
|
||||
enableRealtimeUI = EEPROM.read(2201);
|
||||
notifyMacro = EEPROM.read(2201);
|
||||
uiConfiguration = EEPROM.read(2202);
|
||||
|
||||
#ifdef WLED_FLASH_512K_MODE
|
||||
|
||||
#ifdef WLED_DISABLE_MOBILE_UI
|
||||
uiConfiguration = 1;
|
||||
//force default UI since mobile is unavailable
|
||||
#endif
|
||||
|
||||
|
||||
autoRGBtoRGBW = EEPROM.read(2203);
|
||||
skipFirstLed = EEPROM.read(2204);
|
||||
|
||||
if (EEPROM.read(2210) || EEPROM.read(2211) || EEPROM.read(2212))
|
||||
{
|
||||
presetCyclingEnabled = EEPROM.read(2205);
|
||||
presetCycleTime = ((EEPROM.read(2206) << 0) & 0xFF) + ((EEPROM.read(2207) << 8) & 0xFF00);
|
||||
presetCycleTime = EEPROM.read(2206) + ((EEPROM.read(2207) << 8) & 0xFF00);
|
||||
presetCycleMin = EEPROM.read(2208);
|
||||
presetCycleMax = EEPROM.read(2209);
|
||||
presetApplyBri = EEPROM.read(2210);
|
||||
presetApplyCol = EEPROM.read(2211);
|
||||
presetApplyFx = EEPROM.read(2212);
|
||||
}
|
||||
|
||||
|
||||
bootPreset = EEPROM.read(389);
|
||||
wifiLock = EEPROM.read(393);
|
||||
utcOffsetSecs = ((EEPROM.read(394) << 0) & 0xFF) + ((EEPROM.read(395) << 8) & 0xFF00);
|
||||
utcOffsetSecs = EEPROM.read(394) + ((EEPROM.read(395) << 8) & 0xFF00);
|
||||
if (EEPROM.read(396)) utcOffsetSecs = -utcOffsetSecs; //negative
|
||||
initLedsLast = EEPROM.read(397);
|
||||
enableSecTransition = !EEPROM.read(399);
|
||||
|
||||
//favorite setting (preset) memory (25 slots/ each 20byte)
|
||||
@@ -485,53 +527,47 @@ void loadSettingsFromEEPROM(bool first)
|
||||
//1024-2047 reserved
|
||||
|
||||
readStringFromEEPROM(2220, blynkApiKey, 35);
|
||||
|
||||
|
||||
//user MOD memory
|
||||
//2944 - 3071 reserved
|
||||
|
||||
|
||||
useHSB = useHSBDefault;
|
||||
|
||||
strip.setMode(effectCurrent);
|
||||
strip.setSpeed(effectSpeed);
|
||||
strip.setIntensity(effectIntensity);
|
||||
strip.setPalette(effectPalette);
|
||||
overlayCurrent = overlayDefault;
|
||||
}
|
||||
|
||||
|
||||
//PRESET PROTOCOL 20 bytes
|
||||
//0: preset purpose byte 0:invalid 1:valid preset 1.0
|
||||
//1:a 2:r 3:g 4:b 5:w 6:er 7:eg 8:eb 9:ew 10:fx 11:sx | custom chase 12:numP 13:numS 14:(0:fs 1:both 2:fe) 15:step 16:ix 17: fp 18-19:Zeros
|
||||
|
||||
void applyPreset(byte index, bool loadBri, bool loadCol, bool loadFX)
|
||||
bool applyPreset(byte index, bool loadBri = true, bool loadCol = true, bool loadFX = true)
|
||||
{
|
||||
if (index == 255 || index == 0) loadSettingsFromEEPROM(false);//load boot defaults
|
||||
if (index > 25 || index < 1) return;
|
||||
if (index == 255 || index == 0)
|
||||
{
|
||||
loadSettingsFromEEPROM(false);//load boot defaults
|
||||
return true;
|
||||
}
|
||||
if (index > 25 || index < 1) return false;
|
||||
uint16_t i = 380 + index*20;
|
||||
if (EEPROM.read(i) == 0) return;
|
||||
if (EEPROM.read(i) == 0) return false;
|
||||
if (loadBri) bri = EEPROM.read(i+1);
|
||||
if (loadCol)
|
||||
{
|
||||
col[0] = EEPROM.read(i+2);
|
||||
col[1] = EEPROM.read(i+3);
|
||||
col[2] = EEPROM.read(i+4);
|
||||
white = EEPROM.read(i+5);
|
||||
colSec[0] = EEPROM.read(i+6);
|
||||
colSec[1] = EEPROM.read(i+7);
|
||||
colSec[2] = EEPROM.read(i+8);
|
||||
whiteSec = EEPROM.read(i+9);
|
||||
for (byte j=0; j<4; j++)
|
||||
{
|
||||
col[j] = EEPROM.read(i+j+2);
|
||||
colSec[j] = EEPROM.read(i+j+6);
|
||||
}
|
||||
}
|
||||
if (loadFX)
|
||||
{
|
||||
byte lastfx = effectCurrent;
|
||||
effectCurrent = EEPROM.read(i+10);
|
||||
effectSpeed = EEPROM.read(i+11);
|
||||
effectIntensity = EEPROM.read(i+16);
|
||||
effectPalette = EEPROM.read(i+17);
|
||||
if (lastfx != effectCurrent) strip.setMode(effectCurrent);
|
||||
strip.setSpeed(effectSpeed);
|
||||
strip.setIntensity(effectIntensity);
|
||||
strip.setPalette(effectPalette);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void savePreset(byte index)
|
||||
@@ -541,54 +577,49 @@ void savePreset(byte index)
|
||||
uint16_t i = 380 + index*20;//min400
|
||||
EEPROM.write(i, 1);
|
||||
EEPROM.write(i+1, bri);
|
||||
EEPROM.write(i+2, col[0]);
|
||||
EEPROM.write(i+3, col[1]);
|
||||
EEPROM.write(i+4, col[2]);
|
||||
EEPROM.write(i+5, white);
|
||||
EEPROM.write(i+6, colSec[0]);
|
||||
EEPROM.write(i+7, colSec[1]);
|
||||
EEPROM.write(i+8, colSec[2]);
|
||||
EEPROM.write(i+9, whiteSec);
|
||||
for (uint16_t j=0; j<4; j++)
|
||||
{
|
||||
EEPROM.write(i+j+2, col[j]);
|
||||
EEPROM.write(i+j+6, colSec[j]);
|
||||
}
|
||||
EEPROM.write(i+10, effectCurrent);
|
||||
EEPROM.write(i+11, effectSpeed);
|
||||
|
||||
|
||||
EEPROM.write(i+16, effectIntensity);
|
||||
EEPROM.write(i+17, effectPalette);
|
||||
EEPROM.commit();
|
||||
}
|
||||
|
||||
String loadMacro(byte index)
|
||||
|
||||
void loadMacro(byte index, char* m)
|
||||
{
|
||||
index-=1;
|
||||
String m="";
|
||||
if (index > 15) return m;
|
||||
for (int i = 1024+64*index; i < 1088+64*index; i++)
|
||||
{
|
||||
if (EEPROM.read(i) == 0) break;
|
||||
m += char(EEPROM.read(i));
|
||||
}
|
||||
if (m.charAt(0) < 65 || m.charAt(0) > 90) return ""; //do simple check if macro is valid (capital first letter)
|
||||
return m;
|
||||
if (index > 15) return;
|
||||
readStringFromEEPROM(1024+64*index, m, 64);
|
||||
}
|
||||
|
||||
|
||||
void applyMacro(byte index)
|
||||
{
|
||||
index-=1;
|
||||
if (index > 15) return;
|
||||
String mc="win&";
|
||||
mc += loadMacro(index+1);
|
||||
char m[65];
|
||||
loadMacro(index+1, m);
|
||||
mc += m;
|
||||
mc += "&IN"; //internal, no XML response
|
||||
if (!notifyMacro) mc += "&NN";
|
||||
String forbidden = "&M="; //dont apply if called by the macro itself to prevent loop
|
||||
/*
|
||||
* NOTE: loop is still possible if you call a different macro from a macro, which then calls the first macro again.
|
||||
* NOTE: loop is still possible if you call a different macro from a macro, which then calls the first macro again.
|
||||
* To prevent that, but also disable calling macros within macros, comment the next line out.
|
||||
*/
|
||||
forbidden = forbidden + index;
|
||||
if (mc.indexOf(forbidden) >= 0) return;
|
||||
handleSet(mc);
|
||||
handleSet(nullptr, mc);
|
||||
}
|
||||
|
||||
|
||||
void saveMacro(byte index, String mc, bool sing=true) //only commit on single save, not in settings
|
||||
{
|
||||
index-=1;
|
||||
@@ -600,4 +631,3 @@ void saveMacro(byte index, String mc, bool sing=true) //only commit on single sa
|
||||
}
|
||||
if (sing) EEPROM.commit();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,63 +2,112 @@
|
||||
* Sending XML status files to client
|
||||
*/
|
||||
|
||||
void XML_response(bool isHTTP)
|
||||
//build XML response to HTTP /win API request
|
||||
char* XML_response(AsyncWebServerRequest *request, bool includeTheme, char* dest = nullptr)
|
||||
{
|
||||
olen = 0;
|
||||
oappend("<?xml version = \"1.0\" ?><vs><ac>");
|
||||
if (nightlightActive && nightlightFade)
|
||||
{
|
||||
oappendi(briT);
|
||||
} else
|
||||
{
|
||||
oappendi(bri);
|
||||
}
|
||||
oappend("</ac>");
|
||||
char sbuf[(dest == nullptr)?1024:1]; //allocate local buffer if none passed
|
||||
obuf = (dest == nullptr)? sbuf:dest;
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
oappend("<cl>");
|
||||
oappendi(col[i]);
|
||||
oappend("</cl>");
|
||||
}
|
||||
oappend("<ns>");
|
||||
oappendi(notifyDirect);
|
||||
oappend("</ns><nr>");
|
||||
oappendi(receiveNotifications);
|
||||
oappend("</nr><nl>");
|
||||
oappendi(nightlightActive);
|
||||
oappend("</nl><nf>");
|
||||
oappendi(nightlightFade);
|
||||
oappend("</nf><nd>");
|
||||
oappendi(nightlightDelayMins);
|
||||
oappend("</nd><nt>");
|
||||
oappendi(nightlightTargetBri);
|
||||
oappend("</nt><fx>");
|
||||
oappendi(effectCurrent);
|
||||
oappend("</fx><sx>");
|
||||
oappendi(effectSpeed);
|
||||
oappend("</sx><ix>");
|
||||
oappendi(effectIntensity);
|
||||
oappend("</ix><fp>");
|
||||
oappendi(effectPalette);
|
||||
oappend("</fp><wv>");
|
||||
if (useRGBW && !autoRGBtoRGBW) {
|
||||
oappendi(white);
|
||||
} else {
|
||||
oappend("-1");
|
||||
}
|
||||
oappend("</wv><md>");
|
||||
oappendi(useHSB);
|
||||
oappend("</md><ds>");
|
||||
oappend(serverDescription);
|
||||
oappend("</ds></vs>");
|
||||
if (isHTTP) server.send(200, "text/xml", obuf);
|
||||
olen = 0;
|
||||
oappend("<?xml version=\"1.0\" ?><vs><ac>");
|
||||
oappendi((nightlightActive && nightlightFade) ? briT : bri);
|
||||
oappend("</ac>");
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
oappend("<cl>");
|
||||
oappendi(col[i]);
|
||||
oappend("</cl>");
|
||||
}
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
oappend("<cs>");
|
||||
oappendi(colSec[i]);
|
||||
oappend("</cs>");
|
||||
}
|
||||
oappend("<ns>");
|
||||
oappendi(notifyDirect);
|
||||
oappend("</ns><nr>");
|
||||
oappendi(receiveNotifications);
|
||||
oappend("</nr><nl>");
|
||||
oappendi(nightlightActive);
|
||||
oappend("</nl><nf>");
|
||||
oappendi(nightlightFade);
|
||||
oappend("</nf><nd>");
|
||||
oappendi(nightlightDelayMins);
|
||||
oappend("</nd><nt>");
|
||||
oappendi(nightlightTargetBri);
|
||||
oappend("</nt><fx>");
|
||||
oappendi(effectCurrent);
|
||||
oappend("</fx><sx>");
|
||||
oappendi(effectSpeed);
|
||||
oappend("</sx><ix>");
|
||||
oappendi(effectIntensity);
|
||||
oappend("</ix><fp>");
|
||||
oappendi(effectPalette);
|
||||
oappend("</fp><wv>");
|
||||
if (useRGBW && !autoRGBtoRGBW) {
|
||||
oappendi(col[3]);
|
||||
} else {
|
||||
oappend("-1");
|
||||
}
|
||||
oappend("</wv><ws>");
|
||||
oappendi(colSec[3]);
|
||||
oappend("</ws><md>");
|
||||
oappendi(useHSB);
|
||||
oappend("</md><cy>");
|
||||
oappendi(presetCyclingEnabled);
|
||||
oappend("</cy><ds>");
|
||||
if (realtimeActive)
|
||||
{
|
||||
String mesg = "Live ";
|
||||
if (realtimeIP[0] == 0)
|
||||
{
|
||||
mesg += "E1.31 mode";
|
||||
} else {
|
||||
mesg += "UDP from ";
|
||||
mesg += realtimeIP[0];
|
||||
for (int i = 1; i < 4; i++)
|
||||
{
|
||||
mesg += ".";
|
||||
mesg += realtimeIP[i];
|
||||
}
|
||||
}
|
||||
oappend((char*)mesg.c_str());
|
||||
} else {
|
||||
oappend(serverDescription);
|
||||
}
|
||||
|
||||
oappend("</ds>");
|
||||
if (includeTheme)
|
||||
{
|
||||
char cs[6][9];
|
||||
getThemeColors(cs);
|
||||
oappend("<th><ca>#");
|
||||
oappend(cs[0]);
|
||||
oappend("</ca><cb>#");
|
||||
oappend(cs[1]);
|
||||
oappend("</cb><cc>#");
|
||||
oappend(cs[2]);
|
||||
oappend("</cc><cd>#");
|
||||
oappend(cs[3]);
|
||||
oappend("</cd><cu>#");
|
||||
oappend(cs[4]);
|
||||
oappend("</cu><ct>#");
|
||||
oappend(cs[5]);
|
||||
oappend("</ct><cf>");
|
||||
oappend(cssFont);
|
||||
oappend("</cf></th>");
|
||||
}
|
||||
oappend("</vs>");
|
||||
if (request != nullptr) request->send(200, "text/xml", obuf);
|
||||
}
|
||||
|
||||
void sappend(char stype, char* key, int val) //append a setting to string buffer
|
||||
//append a numeric setting to string buffer
|
||||
void sappend(char stype, char* key, int val)
|
||||
{
|
||||
char ds[] = "d.Sf.";
|
||||
|
||||
|
||||
switch(stype)
|
||||
{
|
||||
case 'c': //checkbox
|
||||
@@ -85,34 +134,38 @@ void sappend(char stype, char* key, int val) //append a setting to string buffer
|
||||
}
|
||||
}
|
||||
|
||||
void sappends(char stype, char* key, char* val) //append a string setting
|
||||
//append a string setting to buffer
|
||||
void sappends(char stype, char* key, char* val)
|
||||
{
|
||||
switch(stype)
|
||||
{
|
||||
case 's': //string (we can interpret val as char*)
|
||||
oappend("d.Sf.");
|
||||
oappend(key);
|
||||
oappend(".value=\"");
|
||||
oappend(val);
|
||||
oappend("\";");
|
||||
break;
|
||||
oappend("d.Sf.");
|
||||
oappend(key);
|
||||
oappend(".value=\"");
|
||||
oappend(val);
|
||||
oappend("\";");
|
||||
break;
|
||||
case 'm': //message
|
||||
oappend("d.getElementsByClassName");
|
||||
oappend(key);
|
||||
oappend(".innerHTML=\"");
|
||||
oappend(val);
|
||||
oappend("\";");
|
||||
break;
|
||||
oappend("d.getElementsByClassName");
|
||||
oappend(key);
|
||||
oappend(".innerHTML=\"");
|
||||
oappend(val);
|
||||
oappend("\";");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void getSettingsJS(byte subPage) //get values for settings form in javascript
|
||||
|
||||
//get values for settings form in javascript
|
||||
void getSettingsJS(byte subPage, char* dest)
|
||||
{
|
||||
//0: menu 1: wifi 2: leds 3: ui 4: sync 5: time 6: sec
|
||||
DEBUG_PRINT("settings resp");
|
||||
DEBUG_PRINTLN(subPage);
|
||||
|
||||
olen = 0; obuf[0] = 0; //clear buffer
|
||||
obuf = dest;
|
||||
olen = 0;
|
||||
|
||||
if (subPage <1 || subPage >6) return;
|
||||
|
||||
if (subPage == 1) {
|
||||
@@ -121,7 +174,7 @@ void getSettingsJS(byte subPage) //get values for settings form in javascript
|
||||
byte l = strlen(clientPass);
|
||||
char fpass[l+1]; //fill password field with ***
|
||||
fpass[l] = 0;
|
||||
memset(fpass,'*',l);
|
||||
memset(fpass,'*',l);
|
||||
sappends('s',"CP",fpass);
|
||||
|
||||
char k[3]; k[2] = 0; //IP addresses
|
||||
@@ -134,16 +187,16 @@ void getSettingsJS(byte subPage) //get values for settings form in javascript
|
||||
}
|
||||
|
||||
sappends('s',"CM",cmDNS);
|
||||
sappend('v',"AT",apWaitTimeSecs);
|
||||
sappend('i',"AB",apBehavior);
|
||||
sappends('s',"AS",apSSID);
|
||||
sappend('c',"AH",apHide);
|
||||
|
||||
|
||||
l = strlen(apPass);
|
||||
char fapass[l+1]; //fill password field with ***
|
||||
fapass[l] = 0;
|
||||
memset(fapass,'*',l);
|
||||
memset(fapass,'*',l);
|
||||
sappends('s',"AP",fapass);
|
||||
|
||||
|
||||
sappend('v',"AC",apChannel);
|
||||
|
||||
if (WiFi.localIP()[0] != 0) //is connected
|
||||
@@ -156,7 +209,7 @@ void getSettingsJS(byte subPage) //get values for settings form in javascript
|
||||
{
|
||||
sappends('m',"(\"sip\")[0]","Not connected");
|
||||
}
|
||||
|
||||
|
||||
if (WiFi.softAPIP()[0] != 0) //is active
|
||||
{
|
||||
char s[16];
|
||||
@@ -168,44 +221,55 @@ void getSettingsJS(byte subPage) //get values for settings form in javascript
|
||||
sappends('m',"(\"sip\")[1]","Not active");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (subPage == 2) {
|
||||
sappend('v',"LC",ledCount);
|
||||
sappend('v',"MA",strip.ablMilliampsMax);
|
||||
if (strip.currentMilliamps)
|
||||
{
|
||||
sappends('m',"(\"pow\")[0]","");
|
||||
olen -= 2; //delete ";
|
||||
oappendi(strip.currentMilliamps);
|
||||
oappend("mA\";");
|
||||
}
|
||||
sappend('v',"CR",colS[0]);
|
||||
sappend('v',"CG",colS[1]);
|
||||
sappend('v',"CB",colS[2]);
|
||||
sappend('v',"CA",briS);
|
||||
sappend('c',"EW",useRGBW);
|
||||
sappend('i',"CO",strip.colorOrder);
|
||||
sappend('c',"AW",autoRGBtoRGBW);
|
||||
sappend('v',"CW",whiteS);
|
||||
sappend('v',"CW",colS[3]);
|
||||
sappend('v',"SR",colSecS[0]);
|
||||
sappend('v',"SG",colSecS[1]);
|
||||
sappend('v',"SB",colSecS[2]);
|
||||
sappend('v',"SW",whiteSecS);
|
||||
sappend('v',"SW",colSecS[3]);
|
||||
sappend('c',"BO",turnOnAtBoot);
|
||||
sappend('v',"BP",bootPreset);
|
||||
sappend('v',"FX",effectDefault);
|
||||
oappend("f=");
|
||||
oappendi(effectDefault);
|
||||
oappend(";p=");
|
||||
oappendi(effectPaletteDefault);
|
||||
oappend(";");
|
||||
sappend('v',"SX",effectSpeedDefault);
|
||||
sappend('v',"IX",effectIntensityDefault);
|
||||
sappend('v',"FP",effectPaletteDefault);
|
||||
sappend('c',"GB",useGammaCorrectionBri);
|
||||
sappend('c',"GC",useGammaCorrectionRGB);
|
||||
sappend('c',"GB",strip.gammaCorrectBri);
|
||||
sappend('c',"GC",strip.gammaCorrectCol);
|
||||
sappend('c',"TF",fadeTransition);
|
||||
sappend('v',"TD",transitionDelay);
|
||||
sappend('c',"PF",strip.paletteFade);
|
||||
sappend('c',"T2",enableSecTransition);
|
||||
sappend('v',"BF",briMultiplier);
|
||||
sappend('v',"TB",nightlightTargetBri);
|
||||
sappend('v',"TL",nightlightDelayMins);
|
||||
sappend('v',"TL",nightlightDelayMinsDefault);
|
||||
sappend('c',"TW",nightlightFade);
|
||||
sappend('i',"PB",strip.paletteBlend);
|
||||
sappend('c',"RV",reverseMode);
|
||||
sappend('c',"EI",initLedsLast);
|
||||
sappend('c',"RV",strip.reverseMode);
|
||||
sappend('c',"SL",skipFirstLed);
|
||||
}
|
||||
|
||||
if (subPage == 3)
|
||||
{
|
||||
{
|
||||
sappend('i',"UI",uiConfiguration);
|
||||
sappends('s',"DS",serverDescription);
|
||||
sappend('c',"MD",useHSBDefault);
|
||||
@@ -222,6 +286,7 @@ void getSettingsJS(byte subPage) //get values for settings form in javascript
|
||||
if (subPage == 4)
|
||||
{
|
||||
sappend('c',"BT",buttonEnabled);
|
||||
sappend('c',"IR",irEnabled);
|
||||
sappend('v',"UP",udpPort);
|
||||
sappend('c',"RB",receiveNotificationBrightness);
|
||||
sappend('c',"RC",receiveNotificationColor);
|
||||
@@ -229,6 +294,7 @@ void getSettingsJS(byte subPage) //get values for settings form in javascript
|
||||
sappend('c',"SD",notifyDirectDefault);
|
||||
sappend('c',"SB",notifyButton);
|
||||
sappend('c',"SH",notifyHue);
|
||||
sappend('c',"SM",notifyMacro);
|
||||
sappend('c',"S2",notifyTwice);
|
||||
sappend('c',"RD",receiveDirect);
|
||||
sappend('c',"EM",e131Multicast);
|
||||
@@ -237,12 +303,20 @@ void getSettingsJS(byte subPage) //get values for settings form in javascript
|
||||
sappend('c',"FB",arlsForceMaxBri);
|
||||
sappend('c',"RG",arlsDisableGammaCorrection);
|
||||
sappend('v',"WO",arlsOffset);
|
||||
sappend('c',"RU",enableRealtimeUI);
|
||||
sappend('c',"AL",alexaEnabled);
|
||||
sappends('s',"AI",alexaInvocationName);
|
||||
sappend('c',"SA",notifyAlexa);
|
||||
sappends('s',"BK",(char*)((blynkEnabled)?"Hidden":""));
|
||||
sappends('s',"MS",mqttServer);
|
||||
sappend('v',"MQPORT",mqttPort);
|
||||
sappends('s',"MQUSER",mqttUser);
|
||||
sappends('s',"MQPASS",mqttPass);
|
||||
byte l = strlen(mqttPass);
|
||||
char fpass[l+1]; //fill password field with ***
|
||||
fpass[l] = 0;
|
||||
memset(fpass,'*',l);
|
||||
sappends('s',"MQPASS",fpass);
|
||||
sappends('s',"MQCID",mqttClientID);
|
||||
sappends('s',"MD",mqttDeviceTopic);
|
||||
sappends('s',"MG",mqttGroupTopic);
|
||||
sappend('v',"H0",hueIP[0]);
|
||||
@@ -264,7 +338,9 @@ void getSettingsJS(byte subPage) //get values for settings form in javascript
|
||||
sappend('c',"CF",!useAMPM);
|
||||
sappend('i',"TZ",currentTimezone);
|
||||
sappend('v',"UO",utcOffsetSecs);
|
||||
sappends('m',"(\"times\")[0]",(char*)getTimeString().c_str());
|
||||
char tm[32];
|
||||
getTimeString(tm);
|
||||
sappends('m',"(\"times\")[0]",tm);
|
||||
sappend('i',"OL",overlayCurrent);
|
||||
sappend('v',"O1",overlayMin);
|
||||
sappend('v',"O2",overlayMax);
|
||||
@@ -280,14 +356,15 @@ void getSettingsJS(byte subPage) //get values for settings form in javascript
|
||||
sappend('v',"CH",countdownHour);
|
||||
sappend('v',"CM",countdownMin);
|
||||
sappend('v',"CS",countdownSec);
|
||||
|
||||
char k[4]; k[0]= 'M';
|
||||
for (int i=1;i<17;i++)
|
||||
{
|
||||
char m[65];
|
||||
loadMacro(i, m);
|
||||
sprintf(k+1,"%i",i);
|
||||
sappends('s',k,(char*)loadMacro(i).c_str());
|
||||
sappends('s',k,m);
|
||||
}
|
||||
|
||||
|
||||
sappend('v',"MB",macroBoot);
|
||||
sappend('v',"A0",macroAlexaOn);
|
||||
sappend('v',"A1",macroAlexaOff);
|
||||
@@ -295,6 +372,7 @@ void getSettingsJS(byte subPage) //get values for settings form in javascript
|
||||
sappend('v',"ML",macroLongPress);
|
||||
sappend('v',"MC",macroCountdown);
|
||||
sappend('v',"MN",macroNl);
|
||||
sappend('v',"MD",macroDoublePress);
|
||||
|
||||
k[2] = 0; //Time macros
|
||||
for (int i = 0; i<8; i++)
|
||||
@@ -303,6 +381,7 @@ void getSettingsJS(byte subPage) //get values for settings form in javascript
|
||||
k[0] = 'H'; sappend('v',k,timerHours[i]);
|
||||
k[0] = 'N'; sappend('v',k,timerMinutes[i]);
|
||||
k[0] = 'T'; sappend('v',k,timerMacro[i]);
|
||||
k[0] = 'W'; sappend('v',k,timerWeekday[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,7 +390,6 @@ void getSettingsJS(byte subPage) //get values for settings form in javascript
|
||||
sappend('c',"NO",otaLock);
|
||||
sappend('c',"OW",wifiLock);
|
||||
sappend('c',"AO",aOtaEnabled);
|
||||
sappend('c',"NA",recoveryAPDisabled);
|
||||
sappends('m',"(\"msg\")[0]","WLED ");
|
||||
olen -= 2; //delete ";
|
||||
oappend(versionString);
|
||||
@@ -321,3 +399,29 @@ void getSettingsJS(byte subPage) //get values for settings form in javascript
|
||||
}
|
||||
oappend("}</script>");
|
||||
}
|
||||
|
||||
|
||||
//get colors from current theme as c strings
|
||||
void getThemeColors(char o[][9])
|
||||
{
|
||||
switch (currentTheme)
|
||||
{
|
||||
// accent color (aCol) background (bCol) panel (cCol) controls (dCol) shadows (sCol) text (tCol)
|
||||
default: strcpy(o[0], "D9B310"); strcpy(o[1], "0B3C5D"); strcpy(o[2], "1D2731"); strcpy(o[3], "328CC1"); strcpy(o[4], "000"); strcpy(o[5], "328CC1"); break; //night
|
||||
case 1: strcpy(o[0], "eee"); strcpy(o[1], "ddd"); strcpy(o[2], "b9b9b9"); strcpy(o[3], "049"); strcpy(o[4], "777"); strcpy(o[5], "049"); break; //modern
|
||||
case 2: strcpy(o[0], "abb"); strcpy(o[1], "fff"); strcpy(o[2], "ddd"); strcpy(o[3], "000"); strcpy(o[4], "0004"); strcpy(o[5], "000"); break; //bright
|
||||
case 3: strcpy(o[0], "c09f80"); strcpy(o[1], "d7cec7"); strcpy(o[2], "76323f"); strcpy(o[3], "888"); strcpy(o[4], "3334"); strcpy(o[5], "888"); break; //wine
|
||||
case 4: strcpy(o[0], "3cc47c"); strcpy(o[1], "828081"); strcpy(o[2], "d9a803"); strcpy(o[3], "1e392a"); strcpy(o[4], "000a"); strcpy(o[5], "1e392a"); break; //electric
|
||||
case 5: strcpy(o[0], "57bc90"); strcpy(o[1], "a5a5af"); strcpy(o[2], "015249"); strcpy(o[3], "88c9d4"); strcpy(o[4], "0004"); strcpy(o[5], "88c9d4"); break; //mint
|
||||
case 6: strcpy(o[0], "f7c331"); strcpy(o[1], "dca"); strcpy(o[2], "6b7a8f"); strcpy(o[3], "f7882f"); strcpy(o[4], "0007"); strcpy(o[5], "f7882f"); break; //amber
|
||||
case 7: strcpy(o[0], "fff"); strcpy(o[1], "333"); strcpy(o[2], "222"); strcpy(o[3], "666"); strcpy(o[4], ""); strcpy(o[5], "fff"); break; //dark
|
||||
case 8: strcpy(o[0], "0ac"); strcpy(o[1], "124"); strcpy(o[2], "224"); strcpy(o[3], "003eff"); strcpy(o[4], "003eff"); strcpy(o[5], "003eff"); break; //air
|
||||
case 9: strcpy(o[0], "f70"); strcpy(o[1], "421"); strcpy(o[2], "221"); strcpy(o[3], "a50"); strcpy(o[4], "f70"); strcpy(o[5], "f70"); break; //nixie
|
||||
case 10: strcpy(o[0], "2d2"); strcpy(o[1], "010"); strcpy(o[2], "121"); strcpy(o[3], "060"); strcpy(o[4], "040"); strcpy(o[5], "3f3"); break; //terminal
|
||||
case 11: strcpy(o[0], "867ADE"); strcpy(o[1], "4033A3"); strcpy(o[2], "483AAA"); strcpy(o[3], "483AAA"); strcpy(o[4], ""); strcpy(o[5], "867ADE"); break; //c64
|
||||
case 12: strcpy(o[0], "fbe8a6"); strcpy(o[1], "d2fdff"); strcpy(o[2], "b4dfe5"); strcpy(o[3], "f4976c"); strcpy(o[4], ""); strcpy(o[5], "303c6c"); break; //easter
|
||||
case 13: strcpy(o[0], "d4af37"); strcpy(o[1], "173305"); strcpy(o[2], "308505"); strcpy(o[3], "f21313"); strcpy(o[4], "f002"); strcpy(o[5], "d4af37"); break; //christmas
|
||||
case 14: strcpy(o[0], "fc7"); strcpy(o[1], "49274a"); strcpy(o[2], "94618e"); strcpy(o[3], "f4decb"); strcpy(o[4], "0008"); strcpy(o[5], "f4decb"); break; //end
|
||||
case 15: for (int i=0;i<6;i++) strcpy(o[i], cssCol[i]); //custom
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,162 +3,77 @@
|
||||
*/
|
||||
void handleSerial()
|
||||
{
|
||||
if (Serial.available() > 0) //support for Adalight protocol to high-speed control LEDs over serial (gamma correction done by PC)
|
||||
if (Serial.available() > 0) //support for Adalight protocol to high-speed control LEDs over serial
|
||||
{
|
||||
if (Serial.find("Ada"))
|
||||
if (!Serial.find("Ada")) return;
|
||||
|
||||
if (!realtimeActive && bri == 0) strip.setBrightness(briLast);
|
||||
arlsLock(realtimeTimeoutMs);
|
||||
|
||||
yield();
|
||||
byte hi = Serial.read();
|
||||
byte ledc = Serial.read();
|
||||
byte chk = Serial.read();
|
||||
if(chk != (hi ^ ledc ^ 0x55)) return;
|
||||
if (ledCount < ledc) ledc = ledCount;
|
||||
|
||||
byte sc[3]; int t =-1; int to = 0;
|
||||
for (int i=0; i < ledc; i++)
|
||||
{
|
||||
if (!realtimeActive && bri == 0) strip.setBrightness(briLast);
|
||||
arlsLock(realtimeTimeoutMs);
|
||||
delay(1);
|
||||
byte hi = Serial.read();
|
||||
byte ledc = Serial.read();
|
||||
byte chk = Serial.read();
|
||||
if(chk != (hi ^ ledc ^ 0x55)) return;
|
||||
if (ledCount < ledc) ledc = ledCount;
|
||||
byte sc[3]; int t =-1; int to = 0;
|
||||
for (int i=0; i < ledc; i++)
|
||||
for (byte j=0; j<3; j++)
|
||||
{
|
||||
for (byte j=0; j<3; j++)
|
||||
while (Serial.peek()<0) //no data yet available
|
||||
{
|
||||
while (Serial.peek()<0) //no data yet available
|
||||
{
|
||||
delay(1);
|
||||
to++;
|
||||
if (to>5) {strip.show(); return;} //unexpected end of transmission
|
||||
}
|
||||
to = 0;
|
||||
sc[j] = Serial.read();
|
||||
yield();
|
||||
to++;
|
||||
if (to>15) {strip.show(); return;} //unexpected end of transmission
|
||||
}
|
||||
setRealtimePixel(i,sc[0],sc[1],sc[2],0);
|
||||
to = 0;
|
||||
sc[j] = Serial.read();
|
||||
}
|
||||
strip.show();
|
||||
setRealtimePixel(i,sc[0],sc[1],sc[2],0);
|
||||
}
|
||||
strip.show();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USEFS
|
||||
|
||||
String formatBytes(size_t bytes){
|
||||
if (bytes < 1024){
|
||||
return String(bytes)+"B";
|
||||
} else if(bytes < (1024 * 1024)){
|
||||
return String(bytes/1024.0)+"KB";
|
||||
} else if(bytes < (1024 * 1024 * 1024)){
|
||||
return String(bytes/1024.0/1024.0)+"MB";
|
||||
} else {
|
||||
return String(bytes/1024.0/1024.0/1024.0)+"GB";
|
||||
}
|
||||
}
|
||||
|
||||
String getContentType(String filename){
|
||||
if(server.hasArg("download")) return "application/octet-stream";
|
||||
#if !defined WLED_DISABLE_FILESYSTEM && defined WLED_ENABLE_FS_SERVING
|
||||
//Un-comment any file types you need
|
||||
String getContentType(AsyncWebServerRequest* request, String filename){
|
||||
if(request->hasArg("download")) return "application/octet-stream";
|
||||
else if(filename.endsWith(".htm")) return "text/html";
|
||||
else if(filename.endsWith(".html")) return "text/html";
|
||||
else if(filename.endsWith(".css")) return "text/css";
|
||||
else if(filename.endsWith(".js")) return "application/javascript";
|
||||
// else if(filename.endsWith(".css")) return "text/css";
|
||||
// else if(filename.endsWith(".js")) return "application/javascript";
|
||||
else if(filename.endsWith(".json")) return "application/json";
|
||||
else if(filename.endsWith(".png")) return "image/png";
|
||||
else if(filename.endsWith(".gif")) return "image/gif";
|
||||
// else if(filename.endsWith(".gif")) return "image/gif";
|
||||
else if(filename.endsWith(".jpg")) return "image/jpeg";
|
||||
else if(filename.endsWith(".ico")) return "image/x-icon";
|
||||
else if(filename.endsWith(".xml")) return "text/xml";
|
||||
else if(filename.endsWith(".pdf")) return "application/x-pdf";
|
||||
else if(filename.endsWith(".zip")) return "application/x-zip";
|
||||
else if(filename.endsWith(".gz")) return "application/x-gzip";
|
||||
// else if(filename.endsWith(".xml")) return "text/xml";
|
||||
// else if(filename.endsWith(".pdf")) return "application/x-pdf";
|
||||
// else if(filename.endsWith(".zip")) return "application/x-zip";
|
||||
// else if(filename.endsWith(".gz")) return "application/x-gzip";
|
||||
return "text/plain";
|
||||
}
|
||||
|
||||
bool handleFileRead(String path){
|
||||
DEBUG_PRINTLN("handleFileRead: " + path);
|
||||
bool handleFileRead(AsyncWebServerRequest* request, String path){
|
||||
DEBUG_PRINTLN("FileRead: " + path);
|
||||
if(path.endsWith("/")) path += "index.htm";
|
||||
String contentType = getContentType(path);
|
||||
String contentType = getContentType(request, path);
|
||||
String pathWithGz = path + ".gz";
|
||||
if(SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)){
|
||||
if(SPIFFS.exists(pathWithGz))
|
||||
path += ".gz";
|
||||
File file = SPIFFS.open(path, "r");
|
||||
size_t sent = server.streamFile(file, contentType);
|
||||
file.close();
|
||||
if(SPIFFS.exists(pathWithGz)){
|
||||
request->send(SPIFFS, pathWithGz, contentType);
|
||||
return true;
|
||||
}
|
||||
if(SPIFFS.exists(path)) {
|
||||
request->send(SPIFFS, path, contentType);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void handleFileUpload(){
|
||||
if(server.uri() != "/edit") return;
|
||||
HTTPUpload& upload = server.upload();
|
||||
if(upload.status == UPLOAD_FILE_START){
|
||||
String filename = upload.filename;
|
||||
if(!filename.startsWith("/")) filename = "/"+filename;
|
||||
DEBUG_PRINT("handleFileUpload Name: "); DEBUG_PRINTLN(filename);
|
||||
fsUploadFile = SPIFFS.open(filename, "w");
|
||||
filename = String();
|
||||
} else if(upload.status == UPLOAD_FILE_WRITE){
|
||||
//DEBUG_PRINT("handleFileUpload Data: "); DEBUG_PRINTLN(upload.currentSize);
|
||||
if(fsUploadFile)
|
||||
fsUploadFile.write(upload.buf, upload.currentSize);
|
||||
} else if(upload.status == UPLOAD_FILE_END){
|
||||
if(fsUploadFile)
|
||||
fsUploadFile.close();
|
||||
DEBUG_PRINT("handleFileUpload Size: "); DEBUG_PRINTLN(upload.totalSize);
|
||||
}
|
||||
}
|
||||
|
||||
void handleFileDelete(){
|
||||
if(server.args() == 0) return server.send(500, "text/plain", "BAD ARGS");
|
||||
String path = server.arg(0);
|
||||
DEBUG_PRINTLN("handleFileDelete: " + path);
|
||||
if(path == "/")
|
||||
return server.send(500, "text/plain", "BAD PATH");
|
||||
if(!SPIFFS.exists(path))
|
||||
return server.send(404, "text/plain", "FileNotFound");
|
||||
SPIFFS.remove(path);
|
||||
server.send(200, "text/plain", "");
|
||||
path = String();
|
||||
}
|
||||
|
||||
void handleFileList() {
|
||||
if(!server.hasArg("dir")) {server.send(500, "text/plain", "BAD ARGS"); return;}
|
||||
|
||||
String path = server.arg("dir");
|
||||
DEBUG_PRINTLN("handleFileList: " + path);
|
||||
Dir dir = SPIFFS.openDir(path);
|
||||
path = String();
|
||||
|
||||
String output = "[";
|
||||
while(dir.next()){
|
||||
File entry = dir.openFile("r");
|
||||
if (output != "[") output += ',';
|
||||
bool isDir = false;
|
||||
output += "{\"type\":\"";
|
||||
output += (isDir)?"dir":"file";
|
||||
output += "\",\"name\":\"";
|
||||
output += String(entry.name()).substring(1);
|
||||
output += "\"}";
|
||||
entry.close();
|
||||
}
|
||||
|
||||
output += "]";
|
||||
server.send(200, "text/json", output);
|
||||
}
|
||||
|
||||
void handleFileCreate(){
|
||||
if(server.args() == 0)
|
||||
return server.send(500, "text/plain", "BAD ARGS");
|
||||
String path = server.arg(0);
|
||||
DEBUG_PRINTLN("handleFileCreate: " + path);
|
||||
if(path == "/")
|
||||
return server.send(500, "text/plain", "BAD PATH");
|
||||
if(SPIFFS.exists(path))
|
||||
return server.send(500, "text/plain", "FILE EXISTS");
|
||||
File file = SPIFFS.open(path, "w");
|
||||
if(file)
|
||||
file.close();
|
||||
else
|
||||
return server.send(500, "text/plain", "CREATE FAILED");
|
||||
server.send(200, "text/plain", "");
|
||||
path = String();
|
||||
}
|
||||
|
||||
#else
|
||||
bool handleFileRead(String path){return false;}
|
||||
bool handleFileRead(AsyncWebServerRequest*, String path){return false;}
|
||||
#endif
|
||||
|
||||
@@ -3,37 +3,197 @@
|
||||
*/
|
||||
|
||||
void wledInit()
|
||||
{
|
||||
{
|
||||
EEPROM.begin(EEPSIZE);
|
||||
ledCount = ((EEPROM.read(229) << 0) & 0xFF) + ((EEPROM.read(398) << 8) & 0xFF00); if (ledCount > 1200 || ledCount == 0) ledCount = 10;
|
||||
//RMT eats up too much RAM
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (ledCount > 600) ledCount = 600;
|
||||
ledCount = EEPROM.read(229) + ((EEPROM.read(398) << 8) & 0xFF00);
|
||||
if (ledCount > MAX_LEDS || ledCount == 0) ledCount = 30;
|
||||
#ifndef ARDUINO_ARCH_ESP32
|
||||
#if LEDPIN == 3
|
||||
if (ledCount > MAX_LEDS_DMA) ledCount = MAX_LEDS_DMA; //DMA method uses too much ram
|
||||
#endif
|
||||
#endif
|
||||
if (!EEPROM.read(397)) strip.init(EEPROM.read(372),ledCount,EEPROM.read(2204)); //quick init
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.setTimeout(50);
|
||||
|
||||
#ifdef USEFS
|
||||
SPIFFS.begin();
|
||||
DEBUG_PRINTLN();
|
||||
DEBUG_PRINT("---WLED "); DEBUG_PRINT(versionString); DEBUG_PRINT(" "); DEBUG_PRINT(VERSION); DEBUG_PRINTLN(" INIT---");
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
DEBUG_PRINT("esp32 "); DEBUG_PRINTLN(ESP.getSdkVersion());
|
||||
#else
|
||||
DEBUG_PRINT("esp8266 "); DEBUG_PRINTLN(ESP.getCoreVersion());
|
||||
#endif
|
||||
|
||||
int heapPreAlloc = ESP.getFreeHeap();
|
||||
DEBUG_PRINT("heap ");
|
||||
DEBUG_PRINTLN(ESP.getFreeHeap());
|
||||
|
||||
strip.init(EEPROM.read(372),ledCount,EEPROM.read(2204)); //init LEDs quickly
|
||||
|
||||
DEBUG_PRINT("LEDs inited. heap usage ~");
|
||||
DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap());
|
||||
|
||||
#ifndef WLED_DISABLE_FILESYSTEM
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
SPIFFS.begin(true);
|
||||
#endif
|
||||
SPIFFS.begin();
|
||||
#endif
|
||||
|
||||
DEBUG_PRINTLN("Load EEPROM");
|
||||
loadSettingsFromEEPROM(true);
|
||||
if (!initLedsLast) initStrip();
|
||||
DEBUG_PRINT("CSSID: ");
|
||||
DEBUG_PRINT(clientSSID);
|
||||
buildCssColorString();
|
||||
userBeginPreConnection();
|
||||
beginStrip();
|
||||
userSetup();
|
||||
if (strcmp(clientSSID,"Your_Network") == 0) showWelcomePage = true;
|
||||
WiFi.persistent(false);
|
||||
|
||||
initCon();
|
||||
if (macroBoot>0) applyMacro(macroBoot);
|
||||
Serial.println("Ada");
|
||||
|
||||
DEBUG_PRINTLN("");
|
||||
DEBUG_PRINT("Connected! IP address: ");
|
||||
DEBUG_PRINTLN(WiFi.localIP());
|
||||
//generate module IDs
|
||||
escapedMac = WiFi.macAddress();
|
||||
escapedMac.replace(":", "");
|
||||
escapedMac.toLowerCase();
|
||||
if (strcmp(cmDNS,"x") == 0) //fill in unique mdns default
|
||||
{
|
||||
strcpy(cmDNS, "wled-");
|
||||
sprintf(cmDNS+5, "%*s", 6, escapedMac.c_str()+6);
|
||||
}
|
||||
if (mqttDeviceTopic[0] == 0)
|
||||
{
|
||||
strcpy(mqttDeviceTopic, "wled/");
|
||||
sprintf(mqttDeviceTopic+5, "%*s", 6, escapedMac.c_str()+6);
|
||||
}
|
||||
if (mqttClientID[0] == 0)
|
||||
{
|
||||
strcpy(mqttClientID, "WLED-");
|
||||
sprintf(mqttClientID+5, "%*s", 6, escapedMac.c_str()+6);
|
||||
}
|
||||
|
||||
strip.service();
|
||||
|
||||
#ifndef WLED_DISABLE_OTA
|
||||
if (aOtaEnabled)
|
||||
{
|
||||
ArduinoOTA.onStart([]() {
|
||||
#ifdef ESP8266
|
||||
wifi_set_sleep_type(NONE_SLEEP_T);
|
||||
#endif
|
||||
DEBUG_PRINTLN("Start ArduinoOTA");
|
||||
});
|
||||
if (strlen(cmDNS) > 0) ArduinoOTA.setHostname(cmDNS);
|
||||
}
|
||||
#endif
|
||||
|
||||
//HTTP server page init
|
||||
initServer();
|
||||
|
||||
strip.service();
|
||||
|
||||
initConnection();
|
||||
}
|
||||
|
||||
|
||||
void beginStrip()
|
||||
{
|
||||
// Initialize NeoPixel Strip and button
|
||||
|
||||
#ifdef BTNPIN
|
||||
pinMode(BTNPIN, INPUT_PULLUP);
|
||||
#endif
|
||||
|
||||
if (bootPreset>0) applyPreset(bootPreset, turnOnAtBoot, true, true);
|
||||
colorUpdated(0);
|
||||
|
||||
//init relay pin
|
||||
#if RLYPIN >= 0
|
||||
pinMode(RLYPIN, OUTPUT);
|
||||
#if RLYMDE
|
||||
digitalWrite(RLYPIN, bri);
|
||||
#else
|
||||
digitalWrite(RLYPIN, !bri);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//disable button if it is "pressed" unintentionally
|
||||
#ifdef BTNPIN
|
||||
if(digitalRead(BTNPIN) == LOW) buttonEnabled = false;
|
||||
#else
|
||||
buttonEnabled = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void initAP(bool resetAP=false){
|
||||
if (apBehavior == 3 && !resetAP) return;
|
||||
|
||||
if (!apSSID[0] || resetAP) strcpy(apSSID, "WLED-AP");
|
||||
if (resetAP) strcpy(apPass,"wled1234");
|
||||
DEBUG_PRINT("Opening access point ");
|
||||
DEBUG_PRINTLN(apSSID);
|
||||
WiFi.softAPConfig(IPAddress(4, 3, 2, 1), IPAddress(4, 3, 2, 1), IPAddress(255,255,255,0));
|
||||
WiFi.softAP(apSSID, apPass, apChannel, apHide);
|
||||
|
||||
if (!apActive) //start captive portal if AP active
|
||||
{
|
||||
DEBUG_PRINTLN("Init AP interfaces");
|
||||
server.begin();
|
||||
if (udpPort > 0 && udpPort != ntpLocalPort)
|
||||
{
|
||||
udpConnected = notifierUdp.begin(udpPort);
|
||||
if (udpConnected && udpRgbPort != udpPort) udpRgbConnected = rgbUdp.begin(udpRgbPort);
|
||||
}
|
||||
|
||||
dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
|
||||
dnsServer.start(53, "*", WiFi.softAPIP());
|
||||
}
|
||||
apActive = true;
|
||||
}
|
||||
|
||||
void initConnection()
|
||||
{
|
||||
WiFi.disconnect(); //close old connections
|
||||
|
||||
if (staticIP[0] != 0)
|
||||
{
|
||||
WiFi.config(staticIP, staticGateway, staticSubnet, IPAddress(8,8,8,8));
|
||||
} else
|
||||
{
|
||||
WiFi.config(0U, 0U, 0U);
|
||||
}
|
||||
|
||||
lastReconnectAttempt = millis();
|
||||
|
||||
if (!WLED_WIFI_CONFIGURED)
|
||||
{
|
||||
DEBUG_PRINT("No connection configured. ");
|
||||
if (!apActive) initAP(); //instantly go to ap mode
|
||||
return;
|
||||
} else if (!apActive) {
|
||||
if (apBehavior == 2)
|
||||
{
|
||||
initAP();
|
||||
} else
|
||||
{
|
||||
DEBUG_PRINTLN("Access point disabled.");
|
||||
WiFi.softAPdisconnect(true);
|
||||
}
|
||||
}
|
||||
showWelcomePage = false;
|
||||
|
||||
DEBUG_PRINT("Connecting to ");
|
||||
DEBUG_PRINT(clientSSID);
|
||||
DEBUG_PRINTLN("...");
|
||||
|
||||
#ifdef ESP8266
|
||||
WiFi.hostname(serverDescription);
|
||||
#endif
|
||||
WiFi.begin(clientSSID, clientPass);
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
WiFi.setHostname(serverDescription);
|
||||
#endif
|
||||
}
|
||||
|
||||
void initInterfaces() {
|
||||
DEBUG_PRINTLN("Init STA interfaces");
|
||||
|
||||
if (hueIP[0] == 0)
|
||||
{
|
||||
hueIP[0] = WiFi.localIP()[0];
|
||||
@@ -41,220 +201,104 @@ void wledInit()
|
||||
hueIP[2] = WiFi.localIP()[2];
|
||||
}
|
||||
|
||||
//init Alexa hue emulation
|
||||
if (alexaEnabled) alexaInit();
|
||||
|
||||
#ifndef WLED_DISABLE_OTA
|
||||
if (aOtaEnabled) ArduinoOTA.begin();
|
||||
#endif
|
||||
|
||||
strip.service();
|
||||
// Set up mDNS responder:
|
||||
if (strlen(cmDNS) > 0)
|
||||
{
|
||||
if (!aOtaEnabled) MDNS.begin(cmDNS);
|
||||
|
||||
DEBUG_PRINTLN("mDNS started");
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
MDNS.addService("wled", "tcp", 80);
|
||||
}
|
||||
server.begin();
|
||||
|
||||
if (udpPort > 0 && udpPort != ntpLocalPort)
|
||||
{
|
||||
udpConnected = notifierUdp.begin(udpPort);
|
||||
if (udpConnected && udpRgbPort != udpPort) udpRgbConnected = rgbUdp.begin(udpRgbPort);
|
||||
}
|
||||
if (ntpEnabled && WiFi.status() == WL_CONNECTED)
|
||||
if (ntpEnabled && WLED_CONNECTED)
|
||||
ntpConnected = ntpUdp.begin(ntpLocalPort);
|
||||
|
||||
//start captive portal if AP active
|
||||
if (onlyAP || strlen(apSSID) > 0)
|
||||
initBlynk(blynkApiKey);
|
||||
initE131();
|
||||
reconnectHue();
|
||||
initMqtt();
|
||||
interfacesInited = true;
|
||||
wasConnected = true;
|
||||
}
|
||||
|
||||
byte stacO = 0;
|
||||
|
||||
void handleConnection() {
|
||||
//TODO: reconnect if heap <8000
|
||||
byte stac = 0;
|
||||
#ifdef ESP8266
|
||||
stac = wifi_softap_get_station_num();
|
||||
#else
|
||||
wifi_sta_list_t stationList;
|
||||
esp_wifi_ap_get_sta_list(&stationList);
|
||||
stac = stationList.num;
|
||||
#endif
|
||||
if (stac != stacO)
|
||||
{
|
||||
dnsServer.setErrorReplyCode(DNSReplyCode::ServerFailure);
|
||||
dnsServer.start(53, "wled.me", WiFi.softAPIP());
|
||||
dnsActive = true;
|
||||
stacO = stac;
|
||||
DEBUG_PRINT("Connected AP clients: ");
|
||||
DEBUG_PRINTLN(stac);
|
||||
if (!WLED_CONNECTED && WLED_WIFI_CONFIGURED) { //trying to connect, but not connected
|
||||
if (stac) WiFi.disconnect(); //disable search so that AP can work
|
||||
else initConnection(); //restart search
|
||||
}
|
||||
}
|
||||
|
||||
prepareIds(); //UUID from MAC (for Alexa and MQTT)
|
||||
if (mqttDeviceTopic[0] == 0)
|
||||
{
|
||||
strcpy(mqttDeviceTopic, "wled/");
|
||||
strcat(mqttDeviceTopic, escapedMac.c_str());
|
||||
if (forceReconnect) {
|
||||
DEBUG_PRINTLN("Forcing reconnect.");
|
||||
initConnection();
|
||||
interfacesInited = false;
|
||||
forceReconnect = false;
|
||||
wasConnected = false;
|
||||
return;
|
||||
}
|
||||
|
||||
//smartInit, we only init some resources when connected
|
||||
if (!onlyAP && WiFi.status() == WL_CONNECTED)
|
||||
{
|
||||
mqttTCPClient = new WiFiClient();
|
||||
mqtt = new PubSubClient(*mqttTCPClient);
|
||||
mqttInit = initMQTT();
|
||||
}
|
||||
|
||||
if (!initLedsLast) strip.service();
|
||||
if (!WLED_CONNECTED) {
|
||||
if (interfacesInited) {
|
||||
DEBUG_PRINTLN("Disconnected!");
|
||||
interfacesInited = false;
|
||||
initConnection();
|
||||
}
|
||||
if (millis() - lastReconnectAttempt > 300000 && WLED_WIFI_CONFIGURED) initConnection();
|
||||
if (!apActive && millis() - lastReconnectAttempt > 12000 && (!wasConnected || apBehavior == 1)) initAP();
|
||||
} else if (!interfacesInited) { //newly connected
|
||||
DEBUG_PRINTLN("");
|
||||
DEBUG_PRINT("Connected! IP address: ");
|
||||
DEBUG_PRINTLN(WiFi.localIP());
|
||||
initInterfaces();
|
||||
userConnected();
|
||||
|
||||
//HTTP server page init
|
||||
initServer();
|
||||
|
||||
if (!initLedsLast) strip.service();
|
||||
//init Alexa hue emulation
|
||||
if (alexaEnabled && !onlyAP) alexaInit();
|
||||
|
||||
server.begin();
|
||||
DEBUG_PRINTLN("HTTP server started");
|
||||
|
||||
//init ArduinoOTA
|
||||
if (!onlyAP) {
|
||||
if (aOtaEnabled)
|
||||
//shut down AP
|
||||
if (apBehavior != 2 && apActive)
|
||||
{
|
||||
ArduinoOTA.onStart([]() {
|
||||
#ifndef ARDUINO_ARCH_ESP32
|
||||
wifi_set_sleep_type(NONE_SLEEP_T);
|
||||
#endif
|
||||
DEBUG_PRINTLN("Start ArduinoOTA");
|
||||
});
|
||||
if (strlen(cmDNS) > 0) ArduinoOTA.setHostname(cmDNS);
|
||||
ArduinoOTA.begin();
|
||||
}
|
||||
|
||||
if (!initLedsLast) strip.service();
|
||||
// Set up mDNS responder:
|
||||
if (strlen(cmDNS) > 0 && !onlyAP)
|
||||
{
|
||||
MDNS.begin(cmDNS);
|
||||
DEBUG_PRINTLN("mDNS responder started");
|
||||
// Add service to MDNS
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
}
|
||||
if (!initLedsLast) strip.service();
|
||||
|
||||
initBlynk(blynkApiKey);
|
||||
initE131();
|
||||
|
||||
hueClient = new HTTPClient();
|
||||
} else {
|
||||
e131Enabled = false;
|
||||
}
|
||||
|
||||
if (initLedsLast) initStrip();
|
||||
userBegin();
|
||||
if (macroBoot>0) applyMacro(macroBoot);
|
||||
Serial.println("Ada");
|
||||
}
|
||||
|
||||
void initStrip()
|
||||
{
|
||||
// Initialize NeoPixel Strip and button
|
||||
if (initLedsLast) strip.init(useRGBW,ledCount,skipFirstLed);
|
||||
strip.setReverseMode(reverseMode);
|
||||
strip.setColor(0);
|
||||
strip.setBrightness(255);
|
||||
|
||||
pinMode(buttonPin, INPUT_PULLUP);
|
||||
pinMode(4,OUTPUT); //this is only needed in special cases
|
||||
digitalWrite(4,LOW);
|
||||
|
||||
if (bootPreset>0) applyPreset(bootPreset, turnOnAtBoot, true, true);
|
||||
colorUpdated(0);
|
||||
if(digitalRead(buttonPin) == LOW) buttonEnabled = false; //disable button if it is "pressed" unintentionally
|
||||
}
|
||||
|
||||
void initAP(){
|
||||
bool set = apSSID[0];
|
||||
if (!set) strcpy(apSSID,"WLED-AP");
|
||||
WiFi.softAP(apSSID, apPass, apChannel, apHide);
|
||||
if (!set) apSSID[0] = 0;
|
||||
}
|
||||
|
||||
void initCon()
|
||||
{
|
||||
WiFi.disconnect(); //close old connections
|
||||
|
||||
if (staticIP[0] != 0)
|
||||
{
|
||||
WiFi.config(staticIP, staticGateway, staticSubnet, staticDNS);
|
||||
} else
|
||||
{
|
||||
WiFi.config(0U, 0U, 0U);
|
||||
}
|
||||
|
||||
if (strlen(apSSID)>0)
|
||||
{
|
||||
DEBUG_PRINT(" USING AP");
|
||||
DEBUG_PRINTLN(strlen(apSSID));
|
||||
initAP();
|
||||
} else
|
||||
{
|
||||
DEBUG_PRINTLN(" NO AP");
|
||||
WiFi.softAPdisconnect(true);
|
||||
}
|
||||
int fail_count = 0;
|
||||
if (strlen(clientSSID) <1 || strcmp(clientSSID,"Your_Network") == 0) fail_count = apWaitTimeSecs*2; //instantly go to ap mode
|
||||
#ifndef ARDUINO_ARCH_ESP32
|
||||
WiFi.hostname(serverDescription);
|
||||
#endif
|
||||
WiFi.begin(clientSSID, clientPass);
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
WiFi.setHostname(serverDescription);
|
||||
#endif
|
||||
unsigned long lastTry = 0;
|
||||
bool con = false;
|
||||
while(!con)
|
||||
{
|
||||
yield();
|
||||
if (!initLedsLast)
|
||||
{
|
||||
handleTransitions();
|
||||
handleButton();
|
||||
handleOverlays();
|
||||
if (briT) strip.service();
|
||||
}
|
||||
if (millis()-lastTry > 499) {
|
||||
con = (WiFi.status() == WL_CONNECTED);
|
||||
lastTry = millis();
|
||||
DEBUG_PRINTLN("C_NC");
|
||||
if (!recoveryAPDisabled && fail_count > apWaitTimeSecs*2)
|
||||
{
|
||||
WiFi.disconnect();
|
||||
DEBUG_PRINTLN("Can't connect. Opening AP...");
|
||||
onlyAP = true;
|
||||
initAP();
|
||||
return;
|
||||
}
|
||||
fail_count++;
|
||||
dnsServer.stop();
|
||||
WiFi.softAPdisconnect(true);
|
||||
apActive = false;
|
||||
DEBUG_PRINTLN("Access point disabled.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void getBuildInfo()
|
||||
{
|
||||
//fill string buffer with build info
|
||||
olen = 0;
|
||||
oappend("hard-coded build info:\r\n\n");
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
oappend("platform: esp32");
|
||||
#else
|
||||
oappend("platform: esp8266");
|
||||
#endif
|
||||
oappend("\r\nversion: ");
|
||||
oappend(versionString);
|
||||
oappend("\r\nbuild: ");
|
||||
oappendi(VERSION);
|
||||
oappend("\r\neepver: ");
|
||||
oappendi(EEPVER);
|
||||
#ifdef USEFS
|
||||
oappend("\r\nspiffs: true\r\n");
|
||||
#else
|
||||
oappend("\r\nspiffs: false\r\n");
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
oappend("debug: true\r\n");
|
||||
#else
|
||||
oappend("debug: false\r\n");
|
||||
#endif
|
||||
oappend("button-pin: gpio");
|
||||
oappendi(buttonPin);
|
||||
oappend("\r\n");
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
oappend("strip-pin: gpio");
|
||||
oappendi(LEDPIN);
|
||||
#else
|
||||
oappend("strip-pin: gpio2");
|
||||
#endif
|
||||
oappend("\r\nbuild-type: src\r\n");
|
||||
}
|
||||
|
||||
|
||||
bool checkClientIsMobile(String useragent)
|
||||
{
|
||||
//to save complexity this function is not comprehensive
|
||||
if (useragent.indexOf("Android") >= 0) return true;
|
||||
if (useragent.indexOf("iPhone") >= 0) return true;
|
||||
if (useragent.indexOf("iPod") >= 0) return true;
|
||||
if (useragent.indexOf("iPad") >= 0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user