Merge branch 'master' into solidRgbwLedStrips

This commit is contained in:
Aircoookie 2019-12-19 20:59:36 +01:00 committed by GitHub
commit 2188509d2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 5150 additions and 5341 deletions

View File

@ -259,3 +259,4 @@ build_flags =
-D WLED_ENABLE_5CH_LEDS
lib_deps =
${common.lib_deps_external}

View File

@ -10,13 +10,14 @@
A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B, WS2811, SK6812, APA102) LEDs!
### Features:
- WS2812FX library integrated for 80 special effects
- WS2812FX library integrated for almost 90 special effects
- FastLED noise effects and palettes
- Customizable Mobile and desktop UI with color and effect controls
- Modern UI with color, effect and segment controls
- Segments to set different effects and colors to parts of the LEDs
- 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.
- 16 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
@ -42,6 +43,12 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control
See the [wiki](https://github.com/Aircoookie/WLED/wiki)!
DrZzs has made some excellent video guides:
[Introduction, hardware and installation](https://www.youtube.com/watch?v=tXvtxwK3jRk)
[Settings, tips and tricks](https://www.youtube.com/watch?v=6eCE2BpLaUQ)
If you'd rather read, here is a very [detailed step-by-step beginner tutorial](https://tynick.com/blog/11-03-2019/getting-started-with-wled-on-esp8266/) by tynick!
### Other
Licensed under the MIT license
@ -52,3 +59,9 @@ 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 WLED really brightens up your every day, you can [![](https://img.shields.io/badge/send%20me%20a%20small%20gift-paypal-blue.svg?style=flat-square)](https://paypal.me/aircoookie)
*Disclaimer:*
If you are sensitive to photoeleptic seizures it is not recommended that you use this software.
In case you still want to try, don't use strobe, lighting or noise modes or high effect speed settings.
As per the MIT license, i assume no liability for any damage to you or any other person or equipment.

View File

@ -0,0 +1,8 @@
These files allow WLED 0.8.6 to report the temp sensor on the Quinled board to MQTT. I use it to report the board temp to Home Assistant via MQTT, so it will send notifications if something happens and the board start to heat up.
This code uses Aircookie's WLED software. It has a premade file for user modifications. I use it to publish the temperature from the dallas temperature sensor on the Quinled board. The entries for the top of the WLED00 file, initializes the required libraries, and variables for the sensor. The .ino file waits for 60 seconds, and checks to see if the MQTT server is connected (thanks Aircoookie). It then poles the sensor, and published it using the MQTT service already running, using the main topic programmed in the WLED UI.
To install:
Add the enties in the WLED00 file to the top of the same file from Aircoookies WLED.
Replace the WLED06_usermod.ino file in Aircoookies WLED folder.

View File

@ -0,0 +1,8 @@
//Intiating code for QuinLED Dig-Uno temp sensor
//Uncomment Celsius if that is your prefered temperature scale
#include <DallasTemperature.h>
OneWire oneWire(14);
DallasTemperature sensors(&oneWire);
long temptimer = millis();
long lastMeasure = 0;
//#define Celsius

View File

@ -0,0 +1,41 @@
//starts Dallas Temp service on boot
void userSetup()
{
// Start the DS18B20 sensor
sensors.begin();
}
//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
void userConnected()
{
}
void userLoop()
{
temptimer = millis();
// Timer to publishe new temperature every 60 seconds
if (temptimer - lastMeasure > 60000) {
lastMeasure = temptimer;
//Check if MQTT Connected, otherwise it will crash the 8266
if (mqtt != nullptr){
sensors.requestTemperatures();
//Gets prefered temperature scale based on selection in definitions section
#ifdef Celsius
float board_temperature = sensors.getTempCByIndex(0);
#else
float board_temperature = sensors.getTempFByIndex(0);
#endif
//Create character string populated with user defined device topic from the UI, and the read temperature. Then publish to MQTT server.
char subuf[38];
strcpy(subuf, mqttDeviceTopic);
strcat(subuf, "/temperature");
mqtt->publish(subuf, 0, true, String(board_temperature).c_str());
return;}
return;}
return;
}

18
usermods/readme.md Normal file
View File

@ -0,0 +1,18 @@
### Usermods
This folder serves as a repository for usermods (custom `wled06_usermod.ino` files)!
If you have created an usermod that you believe is useful (for example to support a particular sensor, display, feature...), feel free to contribute by opening a pull request!
In order for other people to be able to have fun with your usermod, please keep these points in mind:
- Create a folder in this folder with a descriptive name (for example `usermod_ds18b20_temp_sensor_mqtt`)
- Include your custom `wled06_usermod.ino` file
- If your usermod requieres changes to other WLED files, please write a `readme.md` outlining the steps one has to take to use the usermod
- Create a pull request!
- If your feature is useful for the majority of WLED users, I will consider adding it to the base code!
While I do my best to not break too much, keep in mind that as WLED is being updated, usermods might break.
I am not actively maintaining any usermod in this directory, that is your responsibility as the creator of the usermod.
Thank you for your help :)

View File

@ -0,0 +1,45 @@
//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t)
long lastTime = 0;
int delayMs = 10;
const int pinA = D6; //data
const int pinB = D7; //clk
int oldA = LOW;
//gets called once at boot. Do all initialization that doesn't depend on network here
void userSetup() {
pinMode(pinA, INPUT_PULLUP);
pinMode(pinB, INPUT_PULLUP);
}
//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
void userConnected() {
}
//loop. You can use "if (WLED_CONNECTED)" to check for successful connection
void userLoop() {
if (millis()-lastTime > delayMs) {
int A = digitalRead(pinA);
int B = digitalRead(pinB);
if (oldA == LOW && A == HIGH) {
if (oldB == HIGH) {
// bri += 10;
// if (bri > 250) bri = 10;
effectCurrent += 1;
if (effectCurrent >= MODE_COUNT) effectCurrent = 0;
}
else {
// bri -= 10;
// if (bri < 10) bri = 250;
effectCurrent -= 1;
if (effectCurrent < 0) effectCurrent = (MODE_COUNT-1);
}
oldA = A;
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification)
// 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa
colorUpdated(6);
lastTime = millis();
}
}

View File

@ -0,0 +1,35 @@
# SSD1306 128x32 OLED via I2C with u8g2
This usermod allows to connect 128x32 Oled display to WLED controlled and show
the next information:
- Current SSID
- IP address if obtained
* in AP mode and turned off lightning AP password is shown
- Current effect
- Current palette
- On/Off icon (sun/moon)
## Hardware
![Hardware connection](assets/hw_connection.png)
## Requirements
Functionality checked with:
- commit 095429a7df4f9e2b34dd464f7bbfd068df6558eb
- Wemos d1 mini
- PlatformIO
- Generic SSD1306 128x32 I2C OLED display from aliexpress
### Platformio
Add `U8g2@~2.27.2` dependency to `lib_deps_external` under `[common]` section in `platformio.ini`:
```ini
# platformio.ini
...
[common]
...
lib_deps_external =
...
U8g2@~2.27.2
...
```
### Arduino IDE
Install library `U8g2 by oliver` in `Tools | Include Library | Manage libraries` menu.

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -0,0 +1,149 @@
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
// If display does not work or looks corrupted check the
// constructor reference:
// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp
// or check the gallery:
// https://github.com/olikraus/u8g2/wiki/gallery
U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, 5,
4); // Pins are Reset, SCL, SDA
// gets called once at boot. Do all initialization that doesn't depend on
// network here
void userSetup() {
u8x8.begin();
u8x8.setPowerSave(0);
u8x8.setFont(u8x8_font_chroma48medium8_r);
u8x8.drawString(0, 0, "Loading...");
}
// gets called every time WiFi is (re-)connected. Initialize own network
// interfaces here
void userConnected() {}
// needRedraw marks if redraw is required to prevent often redrawing.
bool needRedraw = true;
// Next variables hold the previous known values to determine if redraw is
// required.
String knownSsid = "";
IPAddress knownIp;
uint8_t knownBrightness = 0;
uint8_t knownMode = 0;
uint8_t knownPalette = 0;
long lastUpdate = 0;
// How often we are redrawing screen
#define USER_LOOP_REFRESH_RATE_MS 5000
void userLoop() {
// Check if we time interval for redrawing passes.
if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS) {
return;
}
lastUpdate = millis();
// Check if values which are shown on display changed from the last tiem.
if ((apActive == true ? String(apSSID) : WiFi.SSID()) != knownSsid) {
needRedraw = true;
} else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP())) {
needRedraw = true;
} else if (knownBrightness != bri) {
needRedraw = true;
} else if (knownMode != strip.getMode()) {
needRedraw = true;
} else if (knownPalette != strip.getSegment(0).palette) {
needRedraw = true;
}
if (!needRedraw) {
return;
}
needRedraw = false;
// Update last known values.
knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
knownBrightness = bri;
knownMode = strip.getMode();
knownPalette = strip.getSegment(0).palette;
u8x8.clear();
u8x8.setFont(u8x8_font_chroma48medium8_r);
// First row with Wifi name
u8x8.setCursor(1, 0);
u8x8.print(ssid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0));
// Print `~` char to indicate that SSID is longer, than owr dicplay
if (ssid.length() > u8x8.getCols())
u8x8.print("~");
// Second row with IP or Psssword
u8x8.setCursor(1, 1);
// Print password in AP mode and if led is OFF.
if (apActive && bri == 0)
u8x8.print(apPass);
else
u8x8.print(ip);
// Third row with mode name
u8x8.setCursor(2, 2);
uint8_t qComma = 0;
bool insideQuotes = false;
uint8_t printedChars = 0;
char singleJsonSymbol;
// Find the mode name in JSON
for (size_t i = 0; i < strlen_P(JSON_mode_names); i++) {
singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i);
switch (singleJsonSymbol) {
case '"':
insideQuotes = !insideQuotes;
break;
case '[':
case ']':
break;
case ',':
qComma++;
default:
if (!insideQuotes || (qComma != knownMode))
break;
u8x8.print(singleJsonSymbol);
printedChars++;
}
if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2))
break;
}
// Fourth row with palette name
u8x8.setCursor(2, 3);
qComma = 0;
insideQuotes = false;
printedChars = 0;
// Looking for palette name in JSON.
for (size_t i = 0; i < strlen_P(JSON_palette_names); i++) {
singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i);
switch (singleJsonSymbol) {
case '"':
insideQuotes = !insideQuotes;
break;
case '[':
case ']':
break;
case ',':
qComma++;
default:
if (!insideQuotes || (qComma != knownPalette))
break;
u8x8.print(singleJsonSymbol);
printedChars++;
}
if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2))
break;
}
u8x8.setFont(u8x8_font_open_iconic_embedded_1x1);
u8x8.drawGlyph(0, 0, 80); // wifi icon
u8x8.drawGlyph(0, 1, 68); // home icon
u8x8.setFont(u8x8_font_open_iconic_weather_2x2);
u8x8.drawGlyph(0, 2, 66 + (bri > 0 ? 3 : 0)); // sun/moon icon
}

View File

@ -262,7 +262,7 @@ uint16_t WS2812FX::mode_dynamic(void) {
*/
uint16_t WS2812FX::mode_breath(void) {
uint16_t var = 0;
uint16_t counter = (now * ((SEGMENT.speed >> 3) +10)) & 0xFFFF;
uint16_t counter = (now * ((SEGMENT.speed >> 3) +10));
counter = (counter >> 2) + (counter >> 4); //0-16384 + 0-2048
if (counter < 16384) {
if (counter > 8192) counter = 8192 - (counter - 8192);
@ -282,9 +282,8 @@ uint16_t WS2812FX::mode_breath(void) {
* Fades the LEDs between two colors
*/
uint16_t WS2812FX::mode_fade(void) {
uint16_t counter = (now * ((SEGMENT.speed >> 3) +10)) & 0xFFFF;
if (counter > 32767) counter = 32768 - (counter - 32768);
uint8_t lum = counter >> 7;
uint16_t counter = (now * ((SEGMENT.speed >> 3) +10));
uint8_t lum = triwave16(counter) >> 8;
for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) {
setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum));
@ -536,9 +535,16 @@ uint16_t WS2812FX::mode_sparkle(void) {
for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) {
setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 1));
}
uint32_t cycleTime = 10 + (255 - SEGMENT.speed)*2;
uint32_t it = now / cycleTime;
if (it != SEGENV.step)
{
SEGENV.aux0 = random16(SEGLEN); // aux0 stores the random led index
SEGENV.step = it;
}
setPixelColor(SEGMENT.start + SEGENV.aux0, SEGCOLOR(0));
return 10 + (uint16_t)(255 - SEGMENT.speed);
return FRAMETIME;
}
@ -547,14 +553,9 @@ uint16_t WS2812FX::mode_sparkle(void) {
* Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
*/
uint16_t WS2812FX::mode_flash_sparkle(void) {
if(SEGENV.call == 0) {
for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) {
setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
}
}
uint16_t i = SEGMENT.start + SEGENV.aux0;
setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
if(random8(5) == 0) {
SEGENV.aux0 = random16(SEGLEN); // aux0 stores the random led index
@ -614,7 +615,6 @@ uint16_t WS2812FX::mode_multi_strobe(void) {
uint16_t WS2812FX::mode_android(void) {
if (SEGENV.call == 0)
{
SEGENV.aux0 = 0;
SEGENV.step = SEGMENT.start;
}
@ -1100,182 +1100,56 @@ uint16_t WS2812FX::mode_loading(void) {
}
/*
* Lights all LEDs after each other up starting from the outer edges and
* finishing in the middle. Then turns them in reverse order off. Repeat.
*/
uint16_t WS2812FX::mode_dual_color_wipe_in_out(void) {
int end = SEGLEN - SEGENV.step - 1;
bool odd = (SEGLEN % 2) == 1;
int mid = odd ? ((SEGLEN / 2) + 1) : (SEGLEN / 2);
if (SEGENV.step < mid) {
byte pindex = map(SEGENV.step, 0, mid -1, 0, 255);
uint32_t col = color_from_palette(pindex, false, false, 0);
setPixelColor(SEGMENT.start + SEGENV.step, col);
setPixelColor(SEGMENT.start + end, col);
} else {
if (odd) {
// If odd, we need to 'double count' the center LED (once to turn it on,
// once to turn it off). So trail one behind after the middle LED.
setPixelColor(SEGMENT.start + SEGENV.step - 1, SEGCOLOR(1));
setPixelColor(SEGMENT.start + end + 1, SEGCOLOR(1));
} else {
setPixelColor(SEGMENT.start + SEGENV.step, SEGCOLOR(1));
setPixelColor(SEGMENT.start + end, SEGCOLOR(1));
}
}
SEGENV.step++;
if (odd) {
if (SEGENV.step > SEGLEN) {
SEGENV.step = 0;
}
} else {
if (SEGENV.step >= SEGLEN) {
SEGENV.step = 0;
}
}
return SPEED_FORMULA_L;
}
/*
* Lights all LEDs after each other up starting from the outer edges and
* finishing in the middle. Then turns them in that order off. Repeat.
*/
uint16_t WS2812FX::mode_dual_color_wipe_in_in(void) {
bool odd = (SEGLEN % 2) == 1;
int mid = SEGLEN / 2;
byte pindex = 0;
uint32_t col = 0;
if (SEGENV.step <= mid)
//American Police Light with all LEDs Red and Blue
uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2)
{
pindex = map(SEGENV.step, 0, mid, 0, 255);
col = color_from_palette(pindex, false, false, 0);
}
if (odd) {
if (SEGENV.step <= mid) {
setPixelColor(SEGMENT.start + SEGENV.step, col);
setPixelColor(SEGMENT.start + SEGLEN - SEGENV.step - 1, col);
} else {
int i = SEGENV.step - mid;
setPixelColor(SEGMENT.start + i - 1, SEGCOLOR(1));
setPixelColor(SEGMENT.start + SEGLEN - i, SEGCOLOR(1));
}
} else {
if (SEGENV.step < mid) {
setPixelColor(SEGMENT.start + SEGENV.step, col);
setPixelColor(SEGMENT.start + SEGLEN - SEGENV.step - 1, col);
} else {
int i = SEGENV.step - mid;
setPixelColor(SEGMENT.start + i, SEGCOLOR(1));
setPixelColor(SEGMENT.start + SEGLEN - i - 1, SEGCOLOR(1));
}
}
SEGENV.step++;
if (odd) {
if (SEGENV.step > SEGLEN) {
SEGENV.step = 0;
}
} else {
if (SEGENV.step >= SEGLEN) {
SEGENV.step = 0;
}
}
return SPEED_FORMULA_L;
uint16_t counter = now * ((SEGMENT.speed >> 3) +1);
uint16_t idexR = (counter * SEGLEN) >> 16;
if (idexR >= SEGLEN) idexR = 0;
uint16_t topindex = SEGLEN >> 1;
uint16_t idexB = idexR + topindex;
if (idexR > topindex) idexB -= SEGLEN;
if (idexB >= SEGLEN) idexB = 0; //otherwise overflow on odd number of LEDs
setPixelColor(SEGMENT.start + idexR, color1);
setPixelColor(SEGMENT.start + idexB, color2);
return FRAMETIME;
}
/*
* Lights all LEDs after each other up starting from the middle and
* finishing at the edges. Then turns them off in that order. Repeat.
*/
uint16_t WS2812FX::mode_dual_color_wipe_out_out(void) {
int end = SEGLEN - SEGENV.step - 1;
bool odd = (SEGLEN % 2) == 1;
int mid = SEGLEN / 2;
byte pindex = 0;
uint32_t col = 0;
if (SEGENV.step <= mid)
//American Police Light with all LEDs Red and Blue
uint16_t WS2812FX::mode_police_all()
{
pindex = map(SEGENV.step, 0, mid, 255, 0);
col = color_from_palette(pindex, false, false, 0);
}
if (odd) {
if (SEGENV.step <= mid) {
setPixelColor(SEGMENT.start + mid + SEGENV.step, col);
setPixelColor(SEGMENT.start + mid - SEGENV.step, col);
} else {
setPixelColor(SEGMENT.start + SEGENV.step - 1, SEGCOLOR(1));
setPixelColor(SEGMENT.start + end + 1, SEGCOLOR(1));
}
} else {
if (SEGENV.step < mid) {
setPixelColor(SEGMENT.start + mid - SEGENV.step - 1, col);
setPixelColor(SEGMENT.start + mid + SEGENV.step, col);
} else {
setPixelColor(SEGMENT.start + SEGENV.step, SEGCOLOR(1));
setPixelColor(SEGMENT.start + end, SEGCOLOR(1));
}
}
SEGENV.step++;
if (odd) {
if (SEGENV.step > SEGLEN) {
SEGENV.step = 0;
}
} else {
if (SEGENV.step >= SEGLEN) {
SEGENV.step = 0;
}
}
return SPEED_FORMULA_L;
return police_base(RED, BLUE);
}
/*
* Lights all LEDs after each other up starting from the middle and
* finishing at the edges. Then turns them off in reverse order. Repeat.
*/
uint16_t WS2812FX::mode_dual_color_wipe_out_in(void) {
bool odd = (SEGLEN % 2) == 1;
int mid = SEGLEN / 2;
byte pindex = 0;
uint32_t col = 0;
if (SEGENV.step <= mid)
//Police Lights Red and Blue
uint16_t WS2812FX::mode_police()
{
pindex = map(SEGENV.step, 0, mid, 255, 0);
col = color_from_palette(pindex, false, false, 0);
fill(SEGCOLOR(1));
return police_base(RED, BLUE);
}
if (odd) {
if (SEGENV.step <= mid) {
setPixelColor(SEGMENT.start + mid + SEGENV.step, col);
setPixelColor(SEGMENT.start + mid - SEGENV.step, col);
} else {
int i = SEGENV.step - mid;
setPixelColor(SEGMENT.start + i - 1, SEGCOLOR(1));
setPixelColor(SEGMENT.start + SEGLEN - i, SEGCOLOR(1));
//Police All with custom colors
uint16_t WS2812FX::mode_two_areas()
{
return police_base(SEGCOLOR(0), SEGCOLOR(1));
}
} else {
if (SEGENV.step < mid) {
setPixelColor(SEGMENT.start + mid - SEGENV.step - 1, col);
setPixelColor(SEGMENT.start + mid + SEGENV.step, col);
} else {
int i = SEGENV.step - mid;
setPixelColor(SEGMENT.start + i, SEGCOLOR(1));
setPixelColor(SEGMENT.start + SEGLEN - i - 1, SEGCOLOR(1));
}
}
SEGENV.step++;
if (odd) {
if (SEGENV.step > SEGLEN) {
SEGENV.step = 0;
}
} else {
if (SEGENV.step >= SEGLEN) {
SEGENV.step = 0;
}
}
return SPEED_FORMULA_L;
//Police Lights with custom colors
uint16_t WS2812FX::mode_two_dots()
{
fill(SEGCOLOR(2));
uint32_t color2 = (SEGCOLOR(1) == SEGCOLOR(2)) ? SEGCOLOR(0) : SEGCOLOR(1);
return police_base(SEGCOLOR(0), color2);
}
@ -1657,13 +1531,13 @@ uint16_t WS2812FX::mode_palette()
}
bool noWrap = (paletteBlend == 2 || (paletteBlend == 0 && SEGMENT.speed == 0));
for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++)
for (uint16_t i = 0; i < SEGLEN; i++)
{
uint8_t colorIndex = (i * 255 / SEGLEN) - counter;
if (noWrap) colorIndex = map(colorIndex, 0, 255, 0, 240); //cut off blend at palette "end"
setPixelColor(i, color_from_palette(colorIndex, false, true, 255));
setPixelColor(SEGMENT.start + i, color_from_palette(colorIndex, false, true, 255));
}
return FRAMETIME;
}
@ -2197,7 +2071,7 @@ CRGB WS2812FX::twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat)
// "CalculateOneTwinkle" on each pixel. It then displays
// either the twinkle color of the background color,
// whichever is brighter.
void WS2812FX::twinklefox_base(bool cat)
uint16_t WS2812FX::twinklefox_base(bool cat)
{
// "PRNG16" is the pseudorandom number generator
// It MUST be reset to the same starting value each time
@ -2253,18 +2127,17 @@ void WS2812FX::twinklefox_base(bool cat)
setPixelColor(i, bg.r, bg.g, bg.b);
}
}
return FRAMETIME;
}
uint16_t WS2812FX::mode_twinklefox()
{
twinklefox_base(false);
return FRAMETIME;
return twinklefox_base(false);
}
uint16_t WS2812FX::mode_twinklecat()
{
twinklefox_base(true);
return FRAMETIME;
return twinklefox_base(true);
}
@ -2324,3 +2197,155 @@ uint16_t WS2812FX::mode_halloween_eyes()
return FRAMETIME;
}
//Speed slider sets amount of LEDs lit, intensity sets unlit
uint16_t WS2812FX::mode_static_pattern()
{
uint16_t lit = 1 + SEGMENT.speed;
uint16_t unlit = 1 + SEGMENT.intensity;
bool drawingLit = true;
uint16_t cnt = 0;
for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) {
setPixelColor(i, (drawingLit) ? color_from_palette(i, true, PALETTE_SOLID_WRAP, 0) : SEGCOLOR(1));
cnt++;
if (cnt >= ((drawingLit) ? lit : unlit)) {
cnt = 0;
drawingLit = !drawingLit;
}
}
return FRAMETIME;
}
uint16_t WS2812FX::mode_tri_static_pattern()
{
uint8_t segSize = (SEGMENT.intensity >> 5) +1;
uint8_t currSeg = 0;
uint16_t currSegCount = 0;
for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) {
if ( currSeg % 3 == 0 ) {
setPixelColor(i, SEGCOLOR(0));
} else if( currSeg % 3 == 1) {
setPixelColor(i, SEGCOLOR(1));
} else {
setPixelColor(i, (SEGCOLOR(2) > 0 ? SEGCOLOR(2) : WHITE));
}
currSegCount += 1;
if (currSegCount >= segSize) {
currSeg +=1;
currSegCount = 0;
}
}
return FRAMETIME;
}
uint16_t WS2812FX::spots_base(uint16_t threshold)
{
fill(SEGCOLOR(1));
uint16_t maxZones = SEGLEN >> 2;
uint16_t zones = 1 + ((SEGMENT.intensity * maxZones) >> 8);
uint16_t zoneLen = SEGLEN / zones;
uint16_t offset = (SEGLEN - zones * zoneLen) >> 1;
for (uint16_t z = 0; z < zones; z++)
{
uint16_t pos = offset + z * zoneLen;
for (uint16_t i = 0; i < zoneLen; i++)
{
uint16_t wave = triwave16((i * 0xFFFF) / zoneLen);
if (wave > threshold) {
uint16_t index = SEGMENT.start + pos + i;
uint8_t s = (wave - threshold)*255 / (0xFFFF - threshold);
setPixelColor(index, color_blend(color_from_palette(index, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), 255-s));
}
}
}
return FRAMETIME;
}
//Intensity slider sets number of "lights", speed sets LEDs per light
uint16_t WS2812FX::mode_spots()
{
return spots_base((255 - SEGMENT.speed) << 8);
}
//Intensity slider sets number of "lights", LEDs per light fade in and out
uint16_t WS2812FX::mode_spots_fade()
{
uint16_t counter = now * ((SEGMENT.speed >> 2) +8);
uint16_t t = triwave16(counter);
uint16_t tr = (t >> 1) + (t >> 2);
return spots_base(tr);
}
//Rainbow with glitter, inspired by https://gist.github.com/kriegsman/062e10f7f07ba8518af6
uint16_t WS2812FX::mode_glitter()
{
mode_palette();
if (SEGMENT.intensity > random8())
{
setPixelColor(SEGMENT.start + random16(SEGLEN), ULTRAWHITE);
}
return FRAMETIME;
}
//values close to 100 produce 5Hz flicker, which looks very candle-y
//Inspired by https://github.com/avanhanegem/ArduinoCandleEffectNeoPixel
//and https://cpldcpu.wordpress.com/2016/01/05/reverse-engineering-a-real-candle/
uint16_t WS2812FX::mode_candle()
{
if (SEGENV.call == 0) {
SEGENV.aux0 = 128; SEGENV.aux1 = 132; SEGENV.step = 1;
}
bool newTarget = false;
uint8_t s = SEGENV.aux0, target = SEGENV.aux1, fadeStep = SEGENV.step;
if (target > s) { //fade up
s = qadd8(s, fadeStep);
if (s >= target) newTarget = true;
} else {
s = qsub8(s, fadeStep);
if (s <= target) newTarget = true;
}
SEGENV.aux0 = s;
for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) {
setPixelColor(i, color_blend(color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), 255-s));
}
if (newTarget)
{
uint8_t valrange = SEGMENT.intensity;
uint8_t rndval = valrange >> 1;
target = random8(rndval) + random8(rndval);
if (target < (rndval >> 1)) target = (rndval >> 1) + random8(rndval);
uint8_t offset = (255 - valrange) >> 1;
target += offset;
uint8_t dif = (target > s) ? target - s : s - target;
//how much to move closer to target per frame
fadeStep = dif >> 2; //mode called every ~25 ms, so 4 frames to have a new target every 100ms
if (fadeStep == 0) fadeStep = 1;
SEGENV.step = fadeStep;
SEGENV.aux1 = target;
}
return FRAMETIME;
}

View File

@ -42,7 +42,7 @@
/* Not used in all effects yet */
#define WLED_FPS 42
#define FRAMETIME 1000/WLED_FPS
#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 */
@ -84,7 +84,7 @@
#define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE )
#define IS_SELECTED ((SEGMENT.options & SELECTED) == SELECTED )
#define MODE_COUNT 83
#define MODE_COUNT 89
#define FX_MODE_STATIC 0
#define FX_MODE_BLINK 1
@ -134,10 +134,10 @@
#define FX_MODE_FIRE_FLICKER 45
#define FX_MODE_GRADIENT 46
#define FX_MODE_LOADING 47
#define FX_MODE_DUAL_COLOR_WIPE_IN_OUT 48
#define FX_MODE_DUAL_COLOR_WIPE_IN_IN 49
#define FX_MODE_DUAL_COLOR_WIPE_OUT_OUT 50
#define FX_MODE_DUAL_COLOR_WIPE_OUT_IN 51
#define FX_MODE_POLICE 48
#define FX_MODE_POLICE_ALL 49
#define FX_MODE_TWO_DOTS 50
#define FX_MODE_TWO_AREAS 51
#define FX_MODE_CIRCUS_COMBUSTUS 52
#define FX_MODE_HALLOWEEN 53
#define FX_MODE_TRICOLOR_CHASE 54
@ -149,7 +149,6 @@
#define FX_MODE_DUAL_LARSON_SCANNER 60
#define FX_MODE_RANDOM_CHASE 61
#define FX_MODE_OSCILLATE 62
//Modes that use FastLED -->
#define FX_MODE_PRIDE_2015 63
#define FX_MODE_JUGGLE 64
#define FX_MODE_PALETTE 65
@ -170,6 +169,12 @@
#define FX_MODE_TWINKLEFOX 80
#define FX_MODE_TWINKLECAT 81
#define FX_MODE_HALLOWEEN_EYES 82
#define FX_MODE_STATIC_PATTERN 83
#define FX_MODE_TRI_STATIC_PATTERN 84
#define FX_MODE_SPOTS 85
#define FX_MODE_SPOTS_FADE 86
#define FX_MODE_GLITTER 87
#define FX_MODE_CANDLE 88
class WS2812FX {
@ -177,7 +182,7 @@ class WS2812FX {
// segment parameters
public:
typedef struct Segment { // 21 bytes
typedef struct Segment { // 24 bytes
uint16_t start;
uint16_t stop; //segment invalid if stop == 0
uint8_t speed;
@ -185,6 +190,8 @@ class WS2812FX {
uint8_t palette;
uint8_t mode;
uint8_t options; //bit pattern: msb first: transitional tbd tbd tbd tbd paused reverse selected
uint8_t group, spacing;
uint8_t opacity;
uint32_t colors[NUM_COLORS];
void setOption(uint8_t n, bool val)
{
@ -271,10 +278,10 @@ class WS2812FX {
_mode[FX_MODE_FIRE_FLICKER] = &WS2812FX::mode_fire_flicker;
_mode[FX_MODE_GRADIENT] = &WS2812FX::mode_gradient;
_mode[FX_MODE_LOADING] = &WS2812FX::mode_loading;
_mode[FX_MODE_DUAL_COLOR_WIPE_IN_OUT] = &WS2812FX::mode_dual_color_wipe_in_out;
_mode[FX_MODE_DUAL_COLOR_WIPE_IN_IN] = &WS2812FX::mode_dual_color_wipe_in_in;
_mode[FX_MODE_DUAL_COLOR_WIPE_OUT_OUT] = &WS2812FX::mode_dual_color_wipe_out_out;
_mode[FX_MODE_DUAL_COLOR_WIPE_OUT_IN] = &WS2812FX::mode_dual_color_wipe_out_in;
_mode[FX_MODE_POLICE] = &WS2812FX::mode_police;
_mode[FX_MODE_POLICE_ALL] = &WS2812FX::mode_police_all;
_mode[FX_MODE_TWO_DOTS] = &WS2812FX::mode_two_dots;
_mode[FX_MODE_TWO_AREAS] = &WS2812FX::mode_two_areas;
_mode[FX_MODE_CIRCUS_COMBUSTUS] = &WS2812FX::mode_circus_combustus;
_mode[FX_MODE_HALLOWEEN] = &WS2812FX::mode_halloween;
_mode[FX_MODE_TRICOLOR_CHASE] = &WS2812FX::mode_tricolor_chase;
@ -308,6 +315,12 @@ class WS2812FX {
_mode[FX_MODE_TWINKLEFOX] = &WS2812FX::mode_twinklefox;
_mode[FX_MODE_TWINKLECAT] = &WS2812FX::mode_twinklecat;
_mode[FX_MODE_HALLOWEEN_EYES] = &WS2812FX::mode_halloween_eyes;
_mode[FX_MODE_STATIC_PATTERN] = &WS2812FX::mode_static_pattern;
_mode[FX_MODE_TRI_STATIC_PATTERN] = &WS2812FX::mode_tri_static_pattern;
_mode[FX_MODE_SPOTS] = &WS2812FX::mode_spots;
_mode[FX_MODE_SPOTS_FADE] = &WS2812FX::mode_spots_fade;
_mode[FX_MODE_GLITTER] = &WS2812FX::mode_glitter;
_mode[FX_MODE_CANDLE] = &WS2812FX::mode_candle;
_brightness = DEFAULT_BRIGHTNESS;
currentPalette = CRGBPalette16(CRGB::Black);
@ -322,15 +335,11 @@ class WS2812FX {
}
void
init(bool supportWhite, uint16_t countPixels, bool skipFirs),
init(bool supportWhite, uint16_t countPixels, bool skipFirs, uint8_t disableNLeds),
service(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 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),
@ -356,27 +365,33 @@ class WS2812FX {
reverseMode = false,
gammaCorrectBri = false,
gammaCorrectCol = true,
applyToAllSelected = true,
segmentsAreIdentical(Segment* a, Segment* b),
setEffectConfig(uint8_t m, uint8_t s, uint8_t i, uint8_t p);
uint8_t
returnedSegment = 0,
mainSegment = 0,
paletteFade = 0,
paletteBlend = 0,
colorOrder = 0,
milliampsPerLed = 55,
_disableNLeds = 0,
getBrightness(void),
getMode(void),
getSpeed(void),
getModeCount(void),
getPaletteCount(void),
getMaxSegments(void),
getFirstSelectedSegment(void),
getReturnedSegmentId(void),
//getFirstSelectedSegment(void),
getMainSegmentId(void),
gamma8(uint8_t),
get_random_wheel_index(uint8_t);
uint16_t
ablMilliampsMax,
currentMilliamps;
currentMilliamps,
triwave16(uint16_t),
getUsableCount();
uint32_t
timebase,
@ -384,6 +399,7 @@ class WS2812FX {
color_from_palette(uint16_t, bool, bool, uint8_t, uint8_t pbri = 255),
color_blend(uint32_t,uint32_t,uint8_t),
gamma32(uint32_t),
getLastShow(void),
getPixelColor(uint16_t),
getColor(void);
@ -447,10 +463,10 @@ class WS2812FX {
mode_fire_flicker(void),
mode_gradient(void),
mode_loading(void),
mode_dual_color_wipe_in_out(void),
mode_dual_color_wipe_in_in(void),
mode_dual_color_wipe_out_out(void),
mode_dual_color_wipe_out_in(void),
mode_police(void),
mode_police_all(void),
mode_two_dots(void),
mode_two_areas(void),
mode_circus_combustus(void),
mode_bicolor_chase(void),
mode_tricolor_chase(void),
@ -481,7 +497,14 @@ class WS2812FX {
mode_ripple(void),
mode_twinklefox(void),
mode_twinklecat(void),
mode_halloween_eyes(void);
mode_halloween_eyes(void),
mode_static_pattern(void),
mode_tri_static_pattern(void),
mode_spots(void),
mode_spots_fade(void),
mode_glitter(void),
mode_candle(void);
private:
NeoPixelWrapper *bus;
@ -492,14 +515,13 @@ class WS2812FX {
CRGBPalette16 targetPalette;
uint32_t now;
uint16_t _length;
uint16_t _length, _lengthRaw, _usableCount;
uint16_t _rand16seed;
uint8_t _brightness;
void handle_palette(void);
void fill(uint32_t);
bool modeUsesLock(uint8_t);
void twinklefox_base(bool);
bool
_modeUsesLock,
@ -524,8 +546,11 @@ class WS2812FX {
dissolve(uint32_t),
chase(uint32_t, uint32_t, uint32_t, bool),
gradient_base(bool),
police_base(uint32_t, uint32_t),
running(uint32_t, uint32_t),
tricolor_chase(uint32_t, uint32_t);
tricolor_chase(uint32_t, uint32_t),
twinklefox_base(bool),
spots_base(uint16_t);
CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat);
@ -534,9 +559,9 @@ class WS2812FX {
uint8_t _segment_index = 0;
uint8_t _segment_index_palette_last = 99;
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 _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 24 bytes per element
// start, stop, speed, intensity, palette, mode, options, 3 unused bytes (group, spacing, opacity), color[]
{ 0, 7, DEFAULT_SPEED, 128, 0, DEFAULT_MODE, NO_OPTIONS, 1, 0, 255, {DEFAULT_COLOR}}
};
segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 16 bytes per element
};
@ -546,13 +571,13 @@ class WS2812FX {
const char JSON_mode_names[] PROGMEM = R"=====([
"Solid","Blink","Breathe","Wipe","Wipe Random","Random Colors","Sweep","Dynamic","Colorloop","Rainbow",
"Scan","Dual Scan","Fade","Theater","Theater Rainbow","Running","Saw","Twinkle","Dissolve","Dissolve Rnd",
"Sparkle","Dark Sparkle","Sparkle+","Strobe","Strobe Rainbow","Mega Strobe","Blink Rainbow","Android","Chase","Chase Random",
"Sparkle","Sparkle Dark","Sparkle+","Strobe","Strobe Rainbow","Strobe Mega","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"
"Scanner","Lighthouse","Fireworks","Rain","Merry Christmas","Fire Flicker","Gradient","Loading","Police","Police All",
"Two Dots","Two Areas","Circus","Halloween","Tri Chase","Tri Wipe","Tri Fade","Lightning","ICU","Multi Comet",
"Scanner Dual ","Stream 2","Oscillate","Pride 2015","Juggle","Palette","Fire 2012","Colorwaves","Bpm","Fill Noise",
"Noise 1","Noise 2","Noise 3","Noise 4","Colortwinkles","Lake","Meteor","Meteor Smooth","Railway","Ripple",
"Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle"
])=====";
@ -561,7 +586,8 @@ const char JSON_palette_names[] PROGMEM = R"=====([
"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"
"Magenta","Magred","Yelmag","Yelblu","Orange & Teal","Tiamat","April Night","Orangery","C9","Sakura",
"Aurora"
])=====";
#endif

View File

@ -30,45 +30,61 @@
#define LED_SKIP_AMOUNT 1
#define MIN_SHOW_DELAY 15
void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst)
void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst, uint8_t disableNLeds)
{
if (supportWhite == _rgbwMode && countPixels == _length && _locked != NULL) return;
if (supportWhite == _rgbwMode && countPixels == _length && _locked != NULL && disableNLeds == _disableNLeds) return;
RESET_RUNTIME;
_rgbwMode = supportWhite;
_skipFirstMode = skipFirst;
_length = countPixels;
if (disableNLeds > 0) {
uint16_t groupCount = disableNLeds +1;
//since 1st led is lit, even partial group has a led lit, whereas int division truncates decimal.
bool hasExtraLight = _length % groupCount != 0;
_usableCount = _length/groupCount;
_usableCount += hasExtraLight ? 1 : 0;
} else {
_usableCount = _length;
}
_disableNLeds = disableNLeds;
uint8_t ty = 1;
if (supportWhite) ty =2;
uint16_t lengthRaw = _length;
if (_skipFirstMode) lengthRaw += LED_SKIP_AMOUNT;
bus->Begin((NeoPixelType)ty, lengthRaw);
_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;
_segments[0].stop = _usableCount;
unlockAll();
setBrightness(_brightness);
}
void WS2812FX::service() {
now = millis() + timebase; // Be aware, millis() rolls over every 49 days
if (now - _lastShow < MIN_SHOW_DELAY) return;
uint32_t nowUp = millis(); // Be aware, millis() rolls over every 49 days
now = nowUp + timebase;
if (nowUp - _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
if(nowUp > 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;
SEGENV.next_time = nowUp + delay;
if (SEGMENT.mode != FX_MODE_HALLOWEEN_EYES) SEGENV.call++;
}
}
@ -76,7 +92,6 @@ void WS2812FX::service() {
if(doShow) {
yield();
show();
_lastShow = millis();
}
_triggered = false;
}
@ -90,17 +105,18 @@ bool WS2812FX::modeUsesLock(uint8_t m)
}
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;
uint8_t w = (c >> 24);
uint8_t r = (c >> 16);
uint8_t g = (c >> 8);
uint8_t b = c ;
setPixelColor(n, r, g, b, w);
}
void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
{
i = i * (_disableNLeds+1);
if (_locked[i] && !_modeUsesLock) return;
if (IS_REVERSE) i = SEGMENT.stop -1 -i - SEGMENT.start; //reverse just individual segment
if (IS_REVERSE) i = SEGMENT.stop -1 -i + SEGMENT.start; //reverse just individual segment
byte tmpg = g;
switch (colorOrder) //0 = Grb, default
{
@ -117,17 +133,21 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
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));
if (i < _lengthRaw) bus->SetPixelColor(i, RgbwColor(r,g,b,w));
if (_disableNLeds > 0) {
for(uint16_t offCount=0; offCount < _disableNLeds; offCount++) {
if (i < _lengthRaw) bus->SetPixelColor((i + offCount + 1), RgbwColor(0,0,0,0));
}
}
} 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;
byte r2 = _segments[0].colors[1] >>16;
byte g2 = _segments[0].colors[1] >> 8;
byte b2 = _segments[0].colors[1];
byte w2 = _segments[0].colors[1] >>24;
for (int j=o; j< o+19; j++)
{
bus->SetPixelColor(j, RgbwColor(r2,g2,b2,w2));
@ -184,10 +204,6 @@ void WS2812FX::setCronixieDigits(byte d[])
//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
@ -197,12 +213,13 @@ void WS2812FX::show(void) {
//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
if (ablMilliampsMax > 149 && milliampsPerLed > 0) //0 mA per LED and too low numbers 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
uint32_t puPerMilliamp = 195075 / milliampsPerLed;
uint32_t powerBudget = (ablMilliampsMax - MA_FOR_ESP) * puPerMilliamp; //100mA for ESP power
if (powerBudget > puPerMilliamp * _length) //each LED uses about 1mA in standby, exclude that from power budget
{
powerBudget -= PU_PER_MA * _length;
powerBudget -= puPerMilliamp * _length;
} else
{
powerBudget = 0;
@ -232,10 +249,10 @@ void WS2812FX::show(void) {
uint8_t scaleB = (scaleI > 255) ? 255 : scaleI;
uint8_t newBri = scale8(_brightness, scaleB);
bus->SetBrightness(newBri);
currentMilliamps = (powerSum0 * newBri) / PU_PER_MA;
currentMilliamps = (powerSum0 * newBri) / puPerMilliamp;
} else
{
currentMilliamps = powerSum / PU_PER_MA;
currentMilliamps = powerSum / puPerMilliamp;
bus->SetBrightness(_brightness);
}
currentMilliamps += MA_FOR_ESP; //add power of ESP back to estimate
@ -246,6 +263,7 @@ void WS2812FX::show(void) {
}
bus->Show();
_lastShow = millis();
}
void WS2812FX::trigger() {
@ -284,39 +302,13 @@ uint8_t WS2812FX::getPaletteCount()
//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 mainSeg = getMainSegmentId();
Segment& seg = _segments[getMainSegmentId()];
uint8_t modePrev = seg.mode, speedPrev = seg.speed, intensityPrev = seg.intensity, palettePrev = seg.palette;
if (applyToAllSelected) {
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
{
if (_segments[i].isSelected())
@ -327,6 +319,12 @@ bool WS2812FX::setEffectConfig(uint8_t m, uint8_t s, uint8_t in, uint8_t p) {
setMode(i, m);
}
}
} else {
seg.speed = s;
seg.intensity = in;
seg.palette = p;
setMode(mainSegment, m);
}
if (seg.mode != modePrev || seg.speed != speedPrev || seg.intensity != intensityPrev || seg.palette != palettePrev) return true;
return false;
@ -338,10 +336,14 @@ void WS2812FX::setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w
void WS2812FX::setColor(uint8_t slot, uint32_t c) {
if (slot >= NUM_COLORS) return;
if (applyToAllSelected) {
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
{
if (_segments[i].isSelected()) _segments[i].colors[slot] = c;
}
} else {
_segments[getMainSegmentId()].colors[slot] = c;
}
}
void WS2812FX::setBrightness(uint8_t b) {
@ -352,11 +354,11 @@ void WS2812FX::setBrightness(uint8_t b) {
}
uint8_t WS2812FX::getMode(void) {
return _segments[getReturnedSegmentId()].mode;
return _segments[getMainSegmentId()].mode;
}
uint8_t WS2812FX::getSpeed(void) {
return _segments[getReturnedSegmentId()].speed;
return _segments[getMainSegmentId()].speed;
}
uint8_t WS2812FX::getBrightness(void) {
@ -367,7 +369,7 @@ uint8_t WS2812FX::getMaxSegments(void) {
return MAX_NUM_SEGMENTS;
}
uint8_t WS2812FX::getFirstSelectedSegment(void)
/*uint8_t WS2812FX::getFirstSelectedSegment(void)
{
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
{
@ -378,24 +380,22 @@ uint8_t WS2812FX::getFirstSelectedSegment(void)
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;
uint8_t WS2812FX::getMainSegmentId(void) {
if (mainSegment >= MAX_NUM_SEGMENTS) return 0;
return mainSegment;
}
uint32_t WS2812FX::getColor(void) {
return _segments[getReturnedSegmentId()].colors[0];
return _segments[getMainSegmentId()].colors[0];
}
uint32_t WS2812FX::getPixelColor(uint16_t i)
{
i = i * (_disableNLeds+1);
if (reverseMode) i = _length- 1 -i;
if (IS_REVERSE) i = SEGMENT.stop -1 -i - SEGMENT.start; //reverse just individual segment
if (IS_REVERSE) i = SEGMENT.stop -1 -i + SEGMENT.start; //reverse just individual segment
if (_skipFirstMode) i += LED_SKIP_AMOUNT;
if (_cronixieMode)
{
@ -416,6 +416,7 @@ uint32_t WS2812FX::getPixelColor(uint16_t i)
default: return 0;
}
}
if (i >= _lengthRaw) return 0;
RgbwColor lColor = bus->GetPixelColorRgbw(i);
byte r = lColor.R, g = lColor.G, b = lColor.B;
switch (colorOrder)
@ -441,6 +442,14 @@ WS2812FX::Segment* WS2812FX::getSegments(void) {
return _segments;
}
uint16_t WS2812FX::getUsableCount(void) {
return _usableCount;
}
uint32_t WS2812FX::getLastShow(void) {
return _lastShow;
}
void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2) {
if (n >= MAX_NUM_SEGMENTS) return;
Segment& seg = _segments[n];
@ -451,6 +460,7 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2) {
unlockRange(seg.start, seg.stop);
_modeUsesLock = true;
}
_segment_index = n; fill(0); //turn old segment range off
if (i2 <= i1) //disable segment
{
seg.stop = 0; return;
@ -471,6 +481,10 @@ void WS2812FX::resetSegments() {
_segments[0].speed = DEFAULT_SPEED;
_segments[0].stop = _length;
_segments[0].setOption(0, 1); //select
for (uint16_t i = 1; i < MAX_NUM_SEGMENTS; i++)
{
_segments[i].colors[0] = color_wheel(i*51);
}
}
void WS2812FX::setIndividual(uint16_t i, uint32_t col)
@ -532,7 +546,7 @@ void WS2812FX::unlockAll()
void WS2812FX::setTransitionMode(bool t)
{
_segment_index = 0;
_segment_index = getMainSegmentId();
SEGMENT.setOption(7,t);
if (!t) return;
unsigned long waitMax = millis() + 20; //refresh after 20 ms if transition enabled
@ -635,6 +649,12 @@ void WS2812FX::blur(uint8_t blur_amount)
}
}
uint16_t WS2812FX::triwave16(uint16_t in)
{
if (in < 0x8000) return in *2;
return 0xFFFF - (in - 0x8000)*2;
}
/*
* Put a value 0 to 255 in to get a color value.
* The colours are a transition r -> g -> b -> back to r
@ -695,7 +715,8 @@ void WS2812FX::handle_palette(void)
_segment_index_palette_last = _segment_index;
byte paletteIndex = SEGMENT.palette;
if ((SEGMENT.mode >= FX_MODE_METEOR) && SEGMENT.palette == 0) paletteIndex = 4;
if (SEGMENT.mode == FX_MODE_GLITTER && paletteIndex == 0) paletteIndex = 11;
if (SEGMENT.mode >= FX_MODE_METEOR && paletteIndex == 0) paletteIndex = 4;
switch (paletteIndex)
{
@ -709,6 +730,7 @@ void WS2812FX::handle_palette(void)
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
//case FX_MODE_GLITTER : targetPalette = RainbowColors_p; break;
default: targetPalette = PartyColors_p; break;//palette, bpm
}
@ -747,7 +769,8 @@ void WS2812FX::handle_palette(void)
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;}
CRGB ter = col_to_crgb(SEGCOLOR(2));
targetPalette = CRGBPalette16(ter,sec,prim); break;}
case 6: //Party colors
targetPalette = PartyColors_p; break;
case 7: //Cloud colors
@ -786,6 +809,22 @@ uint32_t WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8
return fastled_col.r*65536 + fastled_col.g*256 + fastled_col.b;
}
bool WS2812FX::segmentsAreIdentical(Segment* a, Segment* b)
{
//if (a->start != b->start) return false;
//if (a->stop != b->stop) return false;
for (uint8_t i = 0; i < NUM_COLORS; i++)
{
if (a->colors[i] != b->colors[i]) return false;
}
if (a->mode != b->mode) return false;
if (a->speed != b->speed) return false;
if (a->intensity != b->intensity) return false;
if (a->palette != b->palette) return false;
//if (a->getOption(1) != b->getOption(1)) return false; //reverse
return true;
}
//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,
@ -813,10 +852,10 @@ uint8_t WS2812FX::gamma8(uint8_t 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;
uint8_t w = (color >> 24);
uint8_t r = (color >> 16);
uint8_t g = (color >> 8);
uint8_t b = color;
w = gammaT[w];
r = gammaT[r];
g = gammaT[g];

View File

@ -5,18 +5,19 @@
//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.
#ifdef WLED_USE_H801
#define BTNPIN -1 //button pin. Needs to have pullup (gpio0 recommended)
#define IR_PIN 0 //infrared pin (-1 to disable) MagicHome: 4, H801 Wifi: 0
#else
//#define WLED_USE_ANALOG_LEDS //Uncomment for using "dumb" PWM controlled LEDs (see pins below, default R: gpio5, G: 12, B: 15, W: 13)
//#define WLED_USE_H801 //H801 5 channel controller. Please uncomment #define WLED_USE_ANALOG_LEDS as well
//#define WLED_USE_5CH
#define BTNPIN 0 //button pin. Needs to have pullup (gpio0 recommended)
#define IR_PIN 4 //infrared pin (-1 to disable) MagicHome: 4, H801 Wifi: 0
#endif
#define RLYPIN -1 //pin for relay, will be set HIGH if LEDs are on (-1 to disable). Also usable for standby leds, triggers,...
#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 RLYMDE 1 //mode for relay, 0: LOW if LEDs are on 1: HIGH if LEDs are on
//END CONFIGURATION
#ifdef USE_APA102
#define CLKPIN 0
#define DATAPIN 2
@ -33,6 +34,10 @@
#define BPIN 12 //B pin for analog LED strip
#define WPIN 14 //W pin for analog LED strip (W1: 14, W2: 04)
#define W2PIN 04 //W2 pin for analog LED strip
#undef BTNPIN
#undef IR_PIN
#define IR_PIN 0 //infrared pin (-1 to disable) MagicHome: 4, H801 Wifi: 0
#endif
#else
//PWM pins - PINs 5,12,13,15 are used with Magic Home LED Controller
#define RPIN 5 //R pin for analog LED strip
@ -40,6 +45,8 @@
#define BPIN 15 //B pin for analog LED strip
#define WPIN 13 //W pin for analog LED strip (W1: 14, W2: 04)
#endif
#undef RLYPIN
#define RLYPIN -1 //disable as pin 12 is used by analog LEDs
#endif
//automatically uses the right driver method for each platform
@ -168,66 +175,51 @@ public:
byte b;
switch (_type)
{
case NeoPixelType_Grb: {
_pGrb->Show();
#ifdef WLED_USE_ANALOG_LEDS
RgbColor color = _pGrb->GetPixelColor(0);
b = _pGrb->GetBrightness();
SetRgbwPwm(color.R * b / 255, color.G * b / 255, color.B * b / 255, 0);
#endif
}
break;
case NeoPixelType_Grbw: {
_pGrbw->Show();
#ifdef WLED_USE_ANALOG_LEDS
RgbwColor colorW = _pGrbw->GetPixelColor(0);
b = _pGrbw->GetBrightness();
// check color values for Warm / COld white mix (for RGBW) // EsplanexaDevice.cpp
#ifdef WLED_USE_5CH_LEDS
if (colorW.R == 255 & colorW.G == 255 && colorW.B == 255 && colorW.W == 255) {
SetRgbwPwm(0, 0, 0, 0, colorW.W * b / 255);
} else if (colorW.R == 127 & colorW.G == 127 && colorW.B == 127 && colorW.W == 255) {
SetRgbwPwm(0, 0, 0, colorW.W * b / 512, colorW.W * b / 255);
} else if (colorW.R == 0 & colorW.G == 0 && colorW.B == 0 && colorW.W == 255) {
SetRgbwPwm(0, 0, 0, colorW.W * b / 255, 0);
} else if (colorW.R == 130 & colorW.G == 90 && colorW.B == 0 && colorW.W == 255) {
SetRgbwPwm(0, 0, 0, colorW.W * b / 255, colorW.W * b / 512);
} else if (colorW.R == 255 & colorW.G == 153 && colorW.B == 0 && colorW.W == 255) {
SetRgbwPwm(0, 0, 0, colorW.W * b / 255, 0);
} else { // not only white colors
SetRgbwPwm(colorW.R * b / 255, colorW.G * b / 255, colorW.B * b / 255, colorW.W * b / 255);
}
#else
SetRgbwPwm(colorW.R * b / 255, colorW.G * b / 255, colorW.B * b / 255, colorW.W * b / 255);
#endif
#endif
}
break;
}
}
bool CanShow() const
{
switch (_type) {
case NeoPixelType_Grb: _pGrb->CanShow(); break;
case NeoPixelType_Grbw: _pGrbw->CanShow(); 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;
case NeoPixelType_Grb: _pGrb->Show(); break;
case NeoPixelType_Grbw: _pGrbw->Show(); 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;
case NeoPixelType_Grb: {
_pGrb->SetPixelColor(indexPixel, RgbColor(color.R,color.G,color.B));
#ifdef WLED_USE_ANALOG_LEDS
if (indexPixel != 0) return; //set analog LEDs from first pixel
b = _pGrb->GetBrightness();
SetRgbwPwm(color.R * b / 255, color.G * b / 255, color.B * b / 255, 0);
#endif
}
break;
case NeoPixelType_Grbw: {
_pGrbw->SetPixelColor(indexPixel, color);
#ifdef WLED_USE_ANALOG_LEDS
if (indexPixel != 0) return; //set analog LEDs from first pixel
b = _pGrbw->GetBrightness();
// check color values for Warm / Cold white mix (for RGBW) // EsplanexaDevice.cpp
#ifdef WLED_USE_5CH_LEDS
if (color.R == 255 & color.G == 255 && color.B == 255 && color.W == 255) {
SetRgbwPwm(0, 0, 0, 0, color.W * b / 255);
} else if (color.R == 127 & color.G == 127 && color.B == 127 && color.W == 255) {
SetRgbwPwm(0, 0, 0, color.W * b / 512, colorW.W * b / 255);
} else if (color.R == 0 & color.G == 0 && color.B == 0 && color.W == 255) {
SetRgbwPwm(0, 0, 0, color.W * b / 255, 0);
} else if (color.R == 130 & color.G == 90 && color.B == 0 && color.W == 255) {
SetRgbwPwm(0, 0, 0, color.W * b / 255, color.W * b / 512);
} else if (color.R == 255 & color.G == 153 && color.B == 0 && color.W == 255) {
SetRgbwPwm(0, 0, 0, color.W * b / 255, 0);
} else { // not only white colors
SetRgbwPwm(color.R * b / 255, colorW.G * b / 255, colorW.B * b / 255, color.W * b / 255);
}
#else
SetRgbwPwm(color.R * b / 255, color.G * b / 255, color.B * b / 255, color.W * b / 255);
#endif
#endif
}
break;
}
}
void SetBrightness(byte b)
@ -238,15 +230,6 @@ public:
}
}
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
@ -258,21 +241,6 @@ public:
return 0;
}
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;
}
}
private:
NeoPixelType _type;

View File

@ -18,7 +18,7 @@
#define ESP_PLATFORM
#define HAVE_CONFIG_H
#define F_CPU 240000000L
#define ARDUINO 10807
#define ARDUINO 10809
#define ARDUINO_ESP32_DEV
#define ARDUINO_ARCH_ESP32
#define ESP32

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

64
wled00/data/liveview.htm Normal file
View File

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<meta charset="utf-8">
<meta name="theme-color" content="#222222">
<title>WLED Live Preview</title>
<style>
body {
margin: 0;
}
#canv {
background: black;
filter: brightness(175%);
width: 100%;
height: 100%;
position: absolute;
}
</style>
</head>
<body>
<div id="canv" />
<script>
update();
var tmout = null;
function update()
{
if (document.hidden) {
clearTimeout(tmout);
tmout = setTimeout(update, 250);
return;
}
fetch('/json/live')
.then(res => {
if (!res.ok) {
clearTimeout(tmout);
tmout = setTimeout(update, 2500);
}
return res.json();
})
.then(json => {
var str = "linear-gradient(90deg,";
var len = json.leds.length;
for (i = 0; i < len; i++) {
var leddata = json.leds[i];
if (leddata.length > 6) leddata = leddata.substring(2);
str += "#" + leddata;
if (i < len -1) str += ","
}
str += ")";
document.getElementById("canv").style.background = str;
clearTimeout(tmout);
tmout = setTimeout(update, 40);
})
.catch(function (error) {
clearTimeout(tmout);
tmout = setTimeout(update, 2500);
})
}
</script>
</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.

View File

@ -1,657 +0,0 @@
/*
* 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,
};

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@
//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>)=====";
<html><body>No usermod custom web page set.</body></html>)=====";
//server message
@ -12,25 +12,75 @@ 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>
<style>.bt{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;display:inline-block;font-size:20px;margin:8px;margin-top:12px}body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;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>)=====";
<style>.bt{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;display:inline-block;font-size:20px;margin:8px;margin-top:12px}input[type=file]{font-size:16px}body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;line-height:200%}</style></head>
<body><h2>WLED Software Update</h2>Installed version: 0.9.0-b1<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
//liveview
const char PAGE_liveview[] PROGMEM = R"=====(<!DOCTYPE html>
<html><head>
<meta name=viewport content="width=device-width, initial-scale=1, minimum-scale=1">
<meta charset=utf-8>
<meta name=theme-color content=#222222>
<title>WLED Live Preview</title>
<style>
body {margin: 0;}
#canv {background: black;filter: brightness(175%);width: 100%;height: 100%;position: absolute;}
</style></head>
<body>
<div id="canv" />
<script>
update();
var tmout = null;
function update()
{
if (document.hidden) {
clearTimeout(tmout);
tmout = setTimeout(update, 250);
return;
}
fetch('/json/live')
.then(res => {
if (!res.ok) {
clearTimeout(tmout);
tmout = setTimeout(update, 2500);
}
return res.json();
})
.then(json => {
var str = "linear-gradient(90deg,";
var len = json.leds.length;
for (i = 0; i < len; i++) {
var leddata = json.leds[i];
if (leddata.length > 6) leddata = leddata.substring(2);
str += "#" + leddata;
if (i < len -1) str += ","
}
str += ")";
document.getElementById("canv").style.background = str;
clearTimeout(tmout);
tmout = setTimeout(update, 40);
})
.catch(function (error) {
clearTimeout(tmout);
tmout = setTimeout(update, 2500);
})
}
</script>
</body></html>)=====";
/*

View File

@ -3,12 +3,12 @@
*/
//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: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>)=====";
const char PAGE_settingsCss[] PROGMEM = R"=====(<style>body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;line-height:200%%;margin:0}hr{border-color:#666}button{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;display:inline-block;font-size:20px;margin:8px;margin-top:12px}.helpB{text-align:left;position:absolute;width:60px}input{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.5ch solid #333}input[type=number]{width:4em}select{background:#333;color:#fff;font-family:Verdana,sans-serif;border:0.5ch solid #333}td{padding:2px;}</style>)=====";
//settings menu
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>
<html><head><title>WLED Settings</title><style>body{text-align:center;background:#222;height:100%;margin:0}html{--h:11.55vh}button{background:#333;color:#fff;font-family:Verdana,Helvetica,sans-serif;border:.3ch solid #333;display:inline-block;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>
@ -23,7 +23,7 @@ const char PAGE_settings[] PROGMEM = R"=====(<!DOCTYPE html>
//wifi settings
const char PAGE_settings_wifi[] PROGMEM = R"=====(<!DOCTYPE html>
<html><head><meta name="viewport" content="width=500">
<html><head><meta name="viewport" content="width=500"><meta charset="utf-8">
<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">
@ -71,97 +71,95 @@ AP IP: <span class="sip"> Not active </span><hr>
//LED settings
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>
<html><head>
<meta charset=utf-8>
<meta name=viewport content="width=500">
<title>LED Settings</title>
<script>var d=document,laprev=55;function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#led-settings")}function B(){window.open("/settings","_self")}function S(){GetV();setABL()}function enABL(){var a=d.getElementById("able").checked;d.Sf.LA.value=(a)?laprev:0;d.getElementById("abl").style.display=(a)?"inline":"none";d.getElementById("psu2").style.display=(a)?"inline":"none";if(d.Sf.LA.value>0){setABL()}}function enLA(){var a=d.Sf.LAsel.value;d.Sf.LA.value=a;d.getElementById("LAdis").style.display=(a==50)?"inline":"none";UI()}function setABL(){d.getElementById("able").checked=true;d.Sf.LAsel.value=50;switch(parseInt(d.Sf.LA.value)){case 0:d.getElementById("able").checked=false;enABL();break;case 30:d.Sf.LAsel.value=30;break;case 35:d.Sf.LAsel.value=35;break;case 55:d.Sf.LAsel.value=55;break;default:d.getElementById("LAdis").style.display="inline"}UI()}function UI(){var b=d.querySelectorAll(".wc"),a=b.length;for(i=0;i<a;i++){b[i].style.display=(d.getElementById("rgbw").checked)?"inline":"none"}d.getElementById("ledwarning").style.display=(d.Sf.LC.value>1000)?"inline":"none";d.getElementById("ampwarning").style.display=(d.Sf.MA.value>7200)?"inline":"none";if(d.Sf.LA.value>0){laprev=d.Sf.LA.value}var j=Math.ceil((100+d.Sf.LC.value*laprev)/500)/2;j=(j>5)?Math.ceil(j):j;var g="";var e=(d.Sf.LAsel.value==30);if(j<1.02&&!e){g="ESP 5V pin with 1A USB supply"}else{g+=e?"12V ":"5V ";g+=j;g+="A supply connected to LEDs"}var h=Math.ceil((100+d.Sf.LC.value*laprev)/1500)/2;h=(h>5)?Math.ceil(h):h;var c="(for most effects, ~";c+=h;c+="A is enough)<br>";d.getElementById("psu").innerHTML=g;d.getElementById("psu2").innerHTML=c}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="1500" oninput=UI() required><br>
LED count: <input name=LC type=number min=1 max=1500 oninput=UI() required><br>
<div id=ledwarning style=color:orange;display:none>
&#9888; You might run into stability or lag issues.<br>
Use less than 1000 LEDs per ESP for the best experience!<br>
</div>
<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>
<b><span id=psu>?</span></b><br>
<span id=psu2><br></span>
<br>
Enable automatic brightness limiter: <input type=checkbox name=ABen onchange=enABL() id=able><br>
<div id=abl>
Maximum Current: <input name=MA type=number min=250 max=65000 oninput=UI() required> mA<br>
<div id=ampwarning style=color:orange;display:none>
&#9888; Your power supply provides high current.<br>
To improve the safety of your setup,<br>
please use thick cables,<br>
multiple power injection points and a fuse!<br>
</div>
<i>Automatically limits brightness to stay close to the limit.<br>
Keep at &lt;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>
<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>
<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>
If you are using an external power supply, enter its rating.<br>
(Current estimated usage: <span class=pow>unknown</span>)</i><br><br>
LED voltage (Max. current for a single LED):<br>
<select name=LAsel onchange=enLA()>
<option value=55 selected>5V default (55mA)</option>
<option value=35>5V efficient (35mA)</option>
<option value=30>12V (30mA)</option>
<option value=50>Custom</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><br>
Brightness factor: <input name="BF" type="number" min="0" max="255" required> %%
<span id=LAdis style=display:none>Custom max. current per LED: <input name=LA type=number min=0 max=255 id=la oninput=UI() required> mA<br></span>
<i>Keep at default if you are unsure about your type of LEDs.</i><br>
</div>
<br>
LEDs are 4-channel type (RGBW): <input type=checkbox name=EW onchange=UI() id=rgbw><br>
<span class=wc>
Auto-calculate white channel from RGB: <input type=checkbox name=AW><br></span>
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>
Apply preset <input name=BP type=number min=0 max=16 required> at boot (0 uses defaults)
<br>- <i>or</i> -<br>
Set current preset cycle setting as boot default: <input type=checkbox name=PC><br><br>
Use Gamma correction for color: <input type=checkbox name=GC> (strongly recommended)<br>
Use Gamma correction for brightness: <input type=checkbox name=GB> (not recommended)<br><br>
Brightness factor: <input name=BF type=number min=1 max=255 required> %
<h3>Transitions</h3>
Crossfade: <input type="checkbox" name="TF"><br>
Transition Time: <input name="TD" maxlength="5" size="2"> ms<br>
Enable transition for secondary color: <input type="checkbox" name="T2"><br>
Enable Palette transitions: <input type="checkbox" name="PF">
Crossfade: <input type=checkbox name=TF><br>
Transition Time: <input name=TD maxlength=5 size=2> ms<br>
Enable Palette transitions: <input type=checkbox name=PF>
<h3>Timed light</h3>
Default Duration: <input name="TL" type="number" min="1" max="255" required> min<br>
Default Target brightness: <input name="TB" type="number" min="0" max="255" required><br>
Fade down: <input type="checkbox" name="TW"><br>
Default Duration: <input name=TL type=number min=1 max=255 required> min<br>
Default Target brightness: <input name=TB type=number min=0 max=255 required><br>
Fade down: <input type=checkbox name=TW><br>
<h3>Advanced</h3>
Palette blending:
<select name="PB">
<option value="0">Linear (wrap if moving)</option>
<option value="1">Linear (always wrap)</option>
<option value="2">Linear (never wrap)</option>
<option value="3">None (not recommended)</option>
<select name=PB>
<option value=0>Linear (wrap if moving)</option>
<option value=1>Linear (always wrap)</option>
<option value=2>Linear (never wrap)</option>
<option value=3>None (not recommended)</option>
</select><br>
Reverse LED order (rotate 180): <input type="checkbox" name="RV"><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>)=====";
Reverse LED order (rotate 180): <input type=checkbox name=RV><br>
Skip first LED: <input type=checkbox name=SL><br>
Disable repeating N LEDs: <input type=number min=0 max=255 name=DL><br>
(Turns off N LEDs between each lit one, spacing out effects)<hr>
<button type=button onclick=B()>Back</button><button type=submit>Save</button>
</form></body></html>)=====";
//User Interface settings
const char PAGE_settings_ui[] PROGMEM = R"=====(<!DOCTYPE html>
<html><head><meta name="viewport" content="width=500">
<title>UI Settings</title><script>
<html><head><meta name="viewport" content="width=500"><meta charset="utf-8"><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;
%CSS%%SCSS%</head>
<body onload="S()">
@ -169,44 +167,8 @@ function gId(s){return document.getElementById(s);}function S(){GetV();Ct();}fun
<div class="helpB"><button type="button" onclick="H()">?</button></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
<h2>Web Setup</h2>
User Interface Mode:
<select name="UI">
<option value="0" selected>Auto</option>
<option value="1">Classic</option>
<option value="2">Mobile</option>
</select><br>
Server description: <input name="DS" maxlength="32"><br><br>
<i>The following options are for the classic UI!</i><br>
Use HSB sliders instead of RGB by default: <input type="checkbox" name="MD"><br>
Color Theme:
<select name="TH" onchange="Ct()">
<option value="0" selected>Night</option>
<option value="1">Modern</option>
<option value="2">Bright</option>
<option value="3">Wine</option>
<option value="4">Electric</option>
<option value="5">Mint</option>
<option value="6">Amber</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">Christmas</option>
<option value="14">The End</option>
<option value="15" id="co">Custom</option>
</select><br>
<div id="cth">
Please specify your custom hex colors (e.g. FF0000 for red)<br>
Custom accent color: <input maxlength=9 name="C0"><br>
Custom background: <input maxlength=9 name="C1"><br>
Custom panel color: <input maxlength=9 name="C2"><br>
Custom icon color: <input maxlength=9 name="C3"><br>
Custom shadow: <input maxlength=9 name="C4"><br>
Custom text color: <input maxlength=9 name="C5"><br></div>
Use font: <input maxlength=32 name="CF"><br>
Make sure the font you use is installed on your system!<br>
Server description: <input name="DS" maxlength="32"><br>
Sync button toggles both send and receive: <input type="checkbox" name="ST"><br><br>
<hr><button type="button" onclick="B()">Back</button><button type="submit">Save</button>
</form>
</body>
@ -215,7 +177,7 @@ Make sure the font you use is installed on your system!<br>
//sync settings
const char PAGE_settings_sync[] PROGMEM = R"=====(<!DOCTYPE html>
<html><head><meta name="viewport" content="width=500"><title>Sync Settings</title>
<html><head><meta name="viewport" content="width=500"><meta charset="utf-8"><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()">
@ -257,6 +219,7 @@ For best results, only use one of these services at a time.<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>
Enable MQTT: <input type="checkbox" name="MQ"><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>
@ -287,7 +250,7 @@ Hue status: <span class="hms"> Internal ESP Error! </span><hr>
//time and macro settings
const char PAGE_settings_time[] PROGMEM = R"=====(<!DOCTYPE html>
<html><head><meta name="viewport" content="width=500"><title>Time Settings</title>
<html><head><meta name="viewport" content="width=500"><meta charset="utf-8"><title>Time Settings</title>
<script>var d=document;function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings");}function B(){window.open("/settings","_self");}function S(){BTa();GetV();Cs();FC();}function gId(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;}}
@ -299,6 +262,7 @@ 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]+=gI
<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>
<input name="NS" maxlength="32"><br>
Use 24h format: <input type="checkbox" name="CF"><br>
Time zone:
<select name="TZ">
@ -325,7 +289,7 @@ Clock Overlay:
<select name="OL" onchange="Cs()">
<option value="0" id="cn" selected>None</option>
<option value="1" id="ca">Analog Clock</option>
<option value="2">Single Digit Clock</option>
<option value="2" disabled>-</option>
<option value="3" id="cc">Cronixie Clock</option>
</select><br>
<div id="coc">
@ -380,7 +344,7 @@ Time-Controlled macros:<br>
//security settings and about
const char PAGE_settings_sec[] PROGMEM = R"=====(<!DOCTYPE html>
<html><head><meta name="viewport" content="width=500">
<html><head><meta name="viewport" content="width=500"><meta charset="utf-8">
<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;
%CSS%%SCSS%</head>
@ -403,7 +367,7 @@ 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.6<br><br>
<a href="https://github.com/Aircoookie/WLED" target="_blank">WLED</a> version 0.9.0-b1<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>

1459
wled00/html_ui.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -565,6 +565,14 @@ DEFINE_GRADIENT_PALETTE( Sakura_gp ) {
195, 255, 82,103,
255, 223, 13, 17};
DEFINE_GRADIENT_PALETTE( Aurora_gp ) {
0, 1, 5, 45, //deep blue
64, 0,200, 23,
128, 0,255, 0, //green
170, 0,243, 45,
200, 0,135, 7,
255, 1, 5, 45};//deep blue
// Single array of defined cpt-city color palettes.
// This will let us programmatically choose one based on
@ -614,6 +622,7 @@ const TProgmemRGBGradientPalettePtr gGradientPalettes[] = {
Orangery_gp, //47-34 Orangery
C9_gp, //48-35 C9
Sakura_gp, //49-36 Sakura
Aurora_gp, //50-37 Aurora
};

View File

@ -1,82 +0,0 @@
/*
* E131.cpp
*
* Project: E131 - E.131 (sACN) library for Arduino
* Copyright (c) 2015 Shelby Merrick
* http://www.forkineye.com
*
* This program is provided free for you to use in any way that you wish,
* subject to the laws and regulations where you are using it. Due diligence
* is strongly suggested before using this code. Please give credit where due.
*
* The Author makes no warranty of any kind, express or implied, with regard
* to this program or the documentation contained in this document. The
* Author shall not be liable in any event for incidental or consequential
* damages in connection with, or arising out of, the furnishing, performance
* or use of these programs.
*
*/
#include "E131.h"
#include <string.h>
/* E1.17 ACN Packet Identifier */
const byte E131::ACN_ID[12] = { 0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00 };
/* Constructor */
E131::E131() {
#ifdef NO_DOUBLE_BUFFER
memset(pbuff1.raw, 0, sizeof(pbuff1.raw));
packet = &pbuff1;
pwbuff = packet;
#else
memset(pbuff1.raw, 0, sizeof(pbuff1.raw));
memset(pbuff2.raw, 0, sizeof(pbuff2.raw));
packet = &pbuff1;
pwbuff = &pbuff2;
#endif
stats.num_packets = 0;
stats.packet_errors = 0;
}
void E131::initUnicast() {
udp.begin(E131_DEFAULT_PORT);
}
void E131::initMulticast(uint16_t universe, uint8_t n) {
IPAddress address = IPAddress(239, 255, ((universe >> 8) & 0xff),
((universe >> 0) & 0xff));
#ifdef ARDUINO_ARCH_ESP32
ip4_addr_t ifaddr;
ip4_addr_t multicast_addr;
ifaddr.addr = static_cast<uint32_t>(WiFi.localIP());
for (uint8_t i = 1; i < n; i++) {
multicast_addr.addr = static_cast<uint32_t>(IPAddress(239, 255,
(((universe + i) >> 8) & 0xff), (((universe + i) >> 0)
& 0xff)));
igmp_joingroup(&ifaddr, &multicast_addr);
}
udp.beginMulticast(address, E131_DEFAULT_PORT);
#else
ip_addr_t ifaddr;
ip_addr_t multicast_addr;
ifaddr.addr = static_cast<uint32_t>(WiFi.localIP());
for (uint8_t i = 1; i < n; i++) {
multicast_addr.addr = static_cast<uint32_t>(IPAddress(239, 255,
(((universe + i) >> 8) & 0xff), (((universe + i) >> 0)
& 0xff)));
igmp_joingroup(&ifaddr, &multicast_addr);
}
udp.beginMulticast(WiFi.localIP(), address, E131_DEFAULT_PORT);
#endif
}
void E131::begin(e131_listen_t type, uint16_t universe, uint8_t n) {
if (type == E131_UNICAST)
initUnicast();
if (type == E131_MULTICAST)
initMulticast(universe, n);
}

View File

@ -1,196 +0,0 @@
/*
* E131.h
*
* Project: E131 - E.131 (sACN) library for Arduino
* Copyright (c) 2015 Shelby Merrick
* http://www.forkineye.com
*
* This program is provided free for you to use in any way that you wish,
* subject to the laws and regulations where you are using it. Due diligence
* is strongly suggested before using this code. Please give credit where due.
*
* The Author makes no warranty of any kind, express or implied, with regard
* to this program or the documentation contained in this document. The
* Author shall not be liable in any event for incidental or consequential
* damages in connection with, or arising out of, the furnishing, performance
* or use of these programs.
*
*/
#ifndef E131_H_
#define E131_H_
#include "Arduino.h"
/* Network interface detection. WiFi for ESP8266 and Ethernet for AVR */
#if defined (ARDUINO_ARCH_ESP8266)
# include <ESP8266WiFi.h>
# define NO_DOUBLE_BUFFER
#elif defined (ARDUINO_ARCH_ESP32)
# include <WiFi.h>
#endif
# include <WiFiUdp.h>
# include <lwip/ip_addr.h>
# include <lwip/igmp.h>
# define _UDP WiFiUDP
/* Defaults */
#define E131_DEFAULT_PORT 5568
/* E1.31 Packet Offsets */
#define E131_ROOT_PREAMBLE_SIZE 0
#define E131_ROOT_POSTAMBLE_SIZE 2
#define E131_ROOT_ID 4
#define E131_ROOT_FLENGTH 16
#define E131_ROOT_VECTOR 18
#define E131_ROOT_CID 22
#define E131_FRAME_FLENGTH 38
#define E131_FRAME_VECTOR 40
#define E131_FRAME_SOURCE 44
#define E131_FRAME_PRIORITY 108
#define E131_FRAME_RESERVED 109
#define E131_FRAME_SEQ 111
#define E131_FRAME_OPT 112
#define E131_FRAME_UNIVERSE 113
#define E131_DMP_FLENGTH 115
#define E131_DMP_VECTOR 117
#define E131_DMP_TYPE 118
#define E131_DMP_ADDR_FIRST 119
#define E131_DMP_ADDR_INC 121
#define E131_DMP_COUNT 123
#define E131_DMP_DATA 125
/* E1.31 Packet Structure */
typedef union {
struct {
/* Root Layer */
uint16_t preamble_size;
uint16_t postamble_size;
uint8_t acn_id[12];
uint16_t root_flength;
uint32_t root_vector;
uint8_t cid[16];
/* Frame Layer */
uint16_t frame_flength;
uint32_t frame_vector;
uint8_t source_name[64];
uint8_t priority;
uint16_t reserved;
uint8_t sequence_number;
uint8_t options;
uint16_t universe;
/* DMP Layer */
uint16_t dmp_flength;
uint8_t dmp_vector;
uint8_t type;
uint16_t first_address;
uint16_t address_increment;
uint16_t property_value_count;
uint8_t property_values[513];
} __attribute__((packed));
uint8_t raw[638];
} e131_packet_t;
/* Error Types */
typedef enum {
ERROR_NONE,
ERROR_IGNORE,
ERROR_ACN_ID,
ERROR_PACKET_SIZE,
ERROR_VECTOR_ROOT,
ERROR_VECTOR_FRAME,
ERROR_VECTOR_DMP
} e131_error_t;
/* E1.31 Listener Types */
typedef enum {
E131_UNICAST,
E131_MULTICAST
} e131_listen_t;
/* Status structure */
typedef struct {
uint32_t num_packets;
uint32_t packet_errors;
IPAddress last_clientIP;
uint16_t last_clientPort;
} e131_stats_t;
class E131 {
private:
/* Constants for packet validation */
static const uint8_t ACN_ID[];
static const uint32_t VECTOR_ROOT = 4;
static const uint32_t VECTOR_FRAME = 2;
static const uint8_t VECTOR_DMP = 2;
e131_packet_t pbuff1; /* Packet buffer */
#ifndef NO_DOUBLE_BUFFER
e131_packet_t pbuff2; /* Double buffer */
#endif
e131_packet_t *pwbuff; /* Pointer to working packet buffer */
_UDP udp; /* UDP handle */
/* Internal Initializers */
void initUnicast();
void initMulticast(uint16_t universe, uint8_t n = 1);
public:
uint8_t *data; /* Pointer to DMX channel data */
uint16_t universe; /* DMX Universe of last valid packet */
e131_packet_t *packet; /* Pointer to last valid packet */
e131_stats_t stats; /* Statistics tracker */
E131();
/* Generic UDP listener, no physical or IP configuration */
void begin(e131_listen_t type, uint16_t universe = 1, uint8_t n = 1);
/* Main packet parser */
inline uint16_t parsePacket() {
e131_error_t error;
uint16_t retval = 0;
int size = udp.parsePacket();
if (size) {
udp.readBytes(pwbuff->raw, size);
error = validate();
if (!error) {
#ifndef NO_DOUBLE_BUFFER
e131_packet_t *swap = packet;
packet = pwbuff;
pwbuff = swap;
#endif
universe = htons(packet->universe);
data = packet->property_values + 1;
retval = htons(packet->property_value_count) - 1;
stats.num_packets++;
stats.last_clientIP = udp.remoteIP();
stats.last_clientPort = udp.remotePort();
}
}
return retval;
}
/* Packet validater */
inline e131_error_t validate() {
if (memcmp(pwbuff->acn_id, ACN_ID, sizeof(pwbuff->acn_id)))
return ERROR_ACN_ID;
if (htonl(pwbuff->root_vector) != VECTOR_ROOT)
return ERROR_VECTOR_ROOT;
if (htonl(pwbuff->frame_vector) != VECTOR_FRAME)
return ERROR_VECTOR_FRAME;
if (pwbuff->dmp_vector != VECTOR_DMP)
return ERROR_VECTOR_DMP;
if (pwbuff->property_values[0] != 0)
return ERROR_IGNORE;
return ERROR_NONE;
}
};
#endif /* E131_H_ */

View File

@ -0,0 +1,116 @@
/*
* ESPAsyncE131.cpp
*
* Project: ESPAsyncE131 - Asynchronous E.131 (sACN) library for Arduino ESP8266 and ESP32
* Copyright (c) 2019 Shelby Merrick
* http://www.forkineye.com
*
* This program is provided free for you to use in any way that you wish,
* subject to the laws and regulations where you are using it. Due diligence
* is strongly suggested before using this code. Please give credit where due.
*
* The Author makes no warranty of any kind, express or implied, with regard
* to this program or the documentation contained in this document. The
* Author shall not be liable in any event for incidental or consequential
* damages in connection with, or arising out of, the furnishing, performance
* or use of these programs.
*
*/
#include "ESPAsyncE131.h"
#include <string.h>
// E1.17 ACN Packet Identifier
const byte ESPAsyncE131::ACN_ID[12] = { 0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00 };
// Constructor
ESPAsyncE131::ESPAsyncE131(e131_packet_callback_function callback) {
_callback = callback;
}
/////////////////////////////////////////////////////////
//
// Public begin() members
//
/////////////////////////////////////////////////////////
bool ESPAsyncE131::begin(e131_listen_t type, uint16_t universe, uint8_t n) {
bool success = false;
if (type == E131_UNICAST)
success = initUnicast();
if (type == E131_MULTICAST)
success = initMulticast(universe, n);
return success;
}
/////////////////////////////////////////////////////////
//
// Private init() members
//
/////////////////////////////////////////////////////////
bool ESPAsyncE131::initUnicast() {
bool success = false;
if (udp.listen(E131_DEFAULT_PORT)) {
udp.onPacket(std::bind(&ESPAsyncE131::parsePacket, this,
std::placeholders::_1));
success = true;
}
return success;
}
bool ESPAsyncE131::initMulticast(uint16_t universe, uint8_t n) {
bool success = false;
IPAddress address = IPAddress(239, 255, ((universe >> 8) & 0xff),
((universe >> 0) & 0xff));
if (udp.listenMulticast(address, E131_DEFAULT_PORT)) {
ip4_addr_t ifaddr;
ip4_addr_t multicast_addr;
ifaddr.addr = static_cast<uint32_t>(WiFi.localIP());
for (uint8_t i = 1; i < n; i++) {
multicast_addr.addr = static_cast<uint32_t>(IPAddress(239, 255,
(((universe + i) >> 8) & 0xff), (((universe + i) >> 0)
& 0xff)));
igmp_joingroup(&ifaddr, &multicast_addr);
}
udp.onPacket(std::bind(&ESPAsyncE131::parsePacket, this,
std::placeholders::_1));
success = true;
}
return success;
}
/////////////////////////////////////////////////////////
//
// Packet parsing - Private
//
/////////////////////////////////////////////////////////
void ESPAsyncE131::parsePacket(AsyncUDPPacket _packet) {
e131_error_t error = ERROR_NONE;
sbuff = reinterpret_cast<e131_packet_t *>(_packet.data());
if (memcmp(sbuff->acn_id, ESPAsyncE131::ACN_ID, sizeof(sbuff->acn_id)))
error = ERROR_ACN_ID;
if (htonl(sbuff->root_vector) != ESPAsyncE131::VECTOR_ROOT)
error = ERROR_VECTOR_ROOT;
if (htonl(sbuff->frame_vector) != ESPAsyncE131::VECTOR_FRAME)
error = ERROR_VECTOR_FRAME;
if (sbuff->dmp_vector != ESPAsyncE131::VECTOR_DMP)
error = ERROR_VECTOR_DMP;
if (sbuff->property_values[0] != 0)
error = ERROR_IGNORE;
if (!error) {
_callback(sbuff, _packet.remoteIP());
}
}

View File

@ -0,0 +1,151 @@
/*
* ESPAsyncE131.h
*
* Project: ESPAsyncE131 - Asynchronous E.131 (sACN) library for Arduino ESP8266 and ESP32
* Copyright (c) 2019 Shelby Merrick
* http://www.forkineye.com
*
* This program is provided free for you to use in any way that you wish,
* subject to the laws and regulations where you are using it. Due diligence
* is strongly suggested before using this code. Please give credit where due.
*
* The Author makes no warranty of any kind, express or implied, with regard
* to this program or the documentation contained in this document. The
* Author shall not be liable in any event for incidental or consequential
* damages in connection with, or arising out of, the furnishing, performance
* or use of these programs.
*
*/
#ifndef ESPASYNCE131_H_
#define ESPASYNCE131_H_
#ifdef ESP32
#include <WiFi.h>
#include <AsyncUDP.h>
#elif defined (ESP8266)
#include <ESPAsyncUDP.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#else
#error Platform not supported
#endif
#include <lwip/ip_addr.h>
#include <lwip/igmp.h>
#include <Arduino.h>
#if LWIP_VERSION_MAJOR == 1
typedef struct ip_addr ip4_addr_t;
#endif
// Defaults
#define E131_DEFAULT_PORT 5568
// E1.31 Packet Offsets
#define E131_ROOT_PREAMBLE_SIZE 0
#define E131_ROOT_POSTAMBLE_SIZE 2
#define E131_ROOT_ID 4
#define E131_ROOT_FLENGTH 16
#define E131_ROOT_VECTOR 18
#define E131_ROOT_CID 22
#define E131_FRAME_FLENGTH 38
#define E131_FRAME_VECTOR 40
#define E131_FRAME_SOURCE 44
#define E131_FRAME_PRIORITY 108
#define E131_FRAME_RESERVED 109
#define E131_FRAME_SEQ 111
#define E131_FRAME_OPT 112
#define E131_FRAME_UNIVERSE 113
#define E131_DMP_FLENGTH 115
#define E131_DMP_VECTOR 117
#define E131_DMP_TYPE 118
#define E131_DMP_ADDR_FIRST 119
#define E131_DMP_ADDR_INC 121
#define E131_DMP_COUNT 123
#define E131_DMP_DATA 125
// E1.31 Packet Structure
typedef union {
struct {
// Root Layer
uint16_t preamble_size;
uint16_t postamble_size;
uint8_t acn_id[12];
uint16_t root_flength;
uint32_t root_vector;
uint8_t cid[16];
// Frame Layer
uint16_t frame_flength;
uint32_t frame_vector;
uint8_t source_name[64];
uint8_t priority;
uint16_t reserved;
uint8_t sequence_number;
uint8_t options;
uint16_t universe;
// DMP Layer
uint16_t dmp_flength;
uint8_t dmp_vector;
uint8_t type;
uint16_t first_address;
uint16_t address_increment;
uint16_t property_value_count;
uint8_t property_values[513];
} __attribute__((packed));
uint8_t raw[638];
} e131_packet_t;
// Error Types
typedef enum {
ERROR_NONE,
ERROR_IGNORE,
ERROR_ACN_ID,
ERROR_PACKET_SIZE,
ERROR_VECTOR_ROOT,
ERROR_VECTOR_FRAME,
ERROR_VECTOR_DMP
} e131_error_t;
// E1.31 Listener Types
typedef enum {
E131_UNICAST,
E131_MULTICAST
} e131_listen_t;
// new packet callback
typedef void (*e131_packet_callback_function) (e131_packet_t* p, IPAddress clientIP);
class ESPAsyncE131 {
private:
// Constants for packet validation
static const uint8_t ACN_ID[];
static const uint32_t VECTOR_ROOT = 4;
static const uint32_t VECTOR_FRAME = 2;
static const uint8_t VECTOR_DMP = 2;
e131_packet_t *sbuff; // Pointer to scratch packet buffer
AsyncUDP udp; // AsyncUDP
// Internal Initializers
bool initUnicast();
bool initMulticast(uint16_t universe, uint8_t n = 1);
// Packet parser callback
void parsePacket(AsyncUDPPacket _packet);
e131_packet_callback_function _callback = nullptr;
public:
ESPAsyncE131(e131_packet_callback_function callback);
// Generic UDP listener, no physical or IP configuration
bool begin(e131_listen_t type, uint16_t universe = 1, uint8_t n = 1);
};
#endif // ESPASYNCE131_H_

View File

@ -443,7 +443,7 @@ public:
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
if (body.indexOf("devicetype") > 0) //client wants a hue api username, we don't care and give static
{
EA_DEBUGLN("devType");
body = "";
@ -453,7 +453,7 @@ public:
if (req.indexOf("state") > 0) //client wants to control light
{
server->send(200, "application/json", "[{\"success\":true}]"); //short valid response
server->send(200, "application/json", "[{\"success\":{\"/lights/1/state/\": true}}]");
uint32_t devId = req.substring(req.indexOf("lights")+7).toInt();
EA_DEBUG("ls"); EA_DEBUGLN(devId);
@ -550,7 +550,7 @@ public:
return true;
}
//we dont care about other api commands at this time and send empty JSON
//we don't care about other api commands at this time and send empty JSON
server->send(200, "application/json", "{}");
return true;
}

View File

@ -15,7 +15,7 @@
#include "ArduinoJson-v6.h"
#include <Print.h>
#define DYNAMYC_JSON_DOCUMENT_SIZE 4096
#define DYNAMIC_JSON_DOCUMENT_SIZE 8192
constexpr const char* JSON_MIMETYPE = "application/json";
@ -60,7 +60,7 @@ class AsyncJsonResponse: public AsyncAbstractResponse {
public:
AsyncJsonResponse(size_t maxJsonBufferSize = DYNAMYC_JSON_DOCUMENT_SIZE, bool isArray=false) : _jsonBuffer(maxJsonBufferSize), _isValid{false} {
AsyncJsonResponse(size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE, bool isArray=false) : _jsonBuffer(maxJsonBufferSize), _isValid{false} {
_code = 200;
_contentType = JSON_MIMETYPE;
if(isArray)
@ -90,7 +90,7 @@ class AsyncJsonResponse: public AsyncAbstractResponse {
}
};
typedef std::function<void(AsyncWebServerRequest *request, JsonObject json)> ArJsonRequestHandlerFunction;
typedef std::function<void(AsyncWebServerRequest *request)> ArJsonRequestHandlerFunction;
class AsyncCallbackJsonWebHandler: public AsyncWebHandler {
private:
@ -103,7 +103,7 @@ protected:
int _maxContentLength;
public:
AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize=DYNAMYC_JSON_DOCUMENT_SIZE)
AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize=DYNAMIC_JSON_DOCUMENT_SIZE)
: _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {}
void setMethod(WebRequestMethodComposite method){ _method = method; }
@ -127,16 +127,9 @@ public:
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);
_onRequest(request);
return;
}
}
request->send(_contentLength > _maxContentLength ? 413 : 400);
} else {
request->send(500);

View File

@ -1 +0,0 @@
#include "TimeLib.h"

View File

@ -12,7 +12,7 @@
#include "Timezone.h"
//THIS LINE WAS ADDED FOR COMPATIBILY WITH THE WLED DEPENDENCY STRUCTURE. REMOVE IF YOU USE IT OUTSIDE OF WLED!
#include "../time/Time.h"
#include "../time/TimeLib.h"
#ifdef __AVR__
#include <avr/eeprom.h>

View File

@ -17,7 +17,7 @@
#include <WProgram.h>
#endif
#include "../time/Time.h" //http://www.arduino.cc/playground/Code/Time
#include "../time/TimeLib.h" //http://www.arduino.cc/playground/Code/Time
//convenient constants for dstRules
enum week_t {Last, First, Second, Third, Fourth};

View File

@ -1,4 +1,4 @@
https://github.com/kitesurfer1404/WS2812FX/
The WS2812FX implementation was heavily altered and differs from its master branch.
Due to regural changes to the library code it is kept in the source dir for now.
Due to regular changes to the library code it is kept in the source dir for now.

View File

@ -3,7 +3,7 @@
*/
/*
* @title WLED project sketch
* @version 0.8.6
* @version 0.9.0-b1
* @author Christian Schwinne
*/
@ -14,16 +14,16 @@
//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_OTA //saves 14kb
//You need to choose some of these features to disable:
//#define WLED_DISABLE_ALEXA //saves 11kb
//#define WLED_DISABLE_BLYNK //saves 6kb
//#define WLED_DISABLE_CRONIXIE //saves 3kb
//#define WLED_DISABLE_HUESYNC //saves 4kb
//#define WLED_DISABLE_INFRARED //there is no pin left for this on ESP8266-01, saves 25kb (!)
#define WLED_ENABLE_MQTT //saves 12kb
#define WLED_ENABLE_ADALIGHT //saves 500b only
#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
@ -60,7 +60,6 @@
#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"
#ifndef WLED_DISABLE_ALEXA
@ -73,12 +72,11 @@
#ifndef WLED_DISABLE_BLYNK
#include "src/dependencies/blynk/BlynkSimpleEsp.h"
#endif
#include "src/dependencies/e131/E131.h"
#include "src/dependencies/e131/ESPAsyncE131.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_ui.h"
#include "html_settings.h"
#include "html_other.h"
#include "FX.h"
@ -106,8 +104,8 @@
//version code in format yymmddb (b = daily build)
#define VERSION 1911031
char versionString[] = "0.8.6";
#define VERSION 1912182
char versionString[] = "0.9.0-b1";
//AP and OTA default passwords (for maximum change them!)
@ -120,7 +118,7 @@ char otaPass[33] = "wledota";
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
char ntpServerName[33] = "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)
@ -145,13 +143,9 @@ bool autoRGBtoRGBW = false; //if RGBW enabled, calculate White
bool turnOnAtBoot = true; //turn on LEDs at power-up
byte bootPreset = 0; //save preset to load after power-up
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 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
byte col[]{255, 160, 0, 0}; //default RGB(W) color
byte colSec[]{0, 0, 0, 0}; //default RGB(W) secondary color
byte briS = 128; //default brightness
byte nightlightTargetBri = 0; //brightness after nightlight is over
byte nightlightDelayMins = 60;
@ -160,19 +154,14 @@ bool fadeTransition = true; //enable crossfading color transit
bool enableSecTransition = true; //also enable transition for secondary color
uint16_t transitionDelay = 750; //default crossfade duration in ms
//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)
uint8_t disableNLeds = 0; //disables N LEDs between active nodes. (Useful for spacing out lights for more traditional christmas light look)
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"; //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
bool useHSBDefault = useHSB;
bool syncToggleReceive = false; //UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise
//Sync CONFIG
@ -203,10 +192,10 @@ bool receiveDirect = true; //receive UDP realtime
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
bool e131Enabled = true; //settings for E1.31 (sACN) protocol
uint16_t e131Universe = 1;
uint16_t e131Universe = 1; //settings for E1.31 (sACN) protocol
bool e131Multicast = false;
bool mqttEnabled = 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)
@ -242,7 +231,7 @@ char cronixieDisplay[7] = "HHMMSS"; //Cronixie Display mask. See wled1
bool cronixieBacklight = true; //Allow digits to be back-illuminated
bool countdownMode = false; //Clock will count down towards date
byte countdownYear = 19, countdownMonth = 1; //Countdown target date, year is last two digits
byte countdownYear = 20, countdownMonth = 1; //Countdown target date, year is last two digits
byte countdownDay = 1, countdownHour = 0;
byte countdownMin = 0, countdownSec = 0;
@ -272,11 +261,9 @@ bool interfacesInited = false;
bool wasConnected = false;
//color
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};
@ -306,7 +293,7 @@ byte bri = briS;
byte briOld = 0;
byte briT = 0;
byte briIT = 0;
byte briLast = 127; //brightness before turned off. Used for toggle function
byte briLast = 128; //brightness before turned off. Used for toggle function
//button
bool buttonPressedBefore = false;
@ -322,16 +309,15 @@ byte notificationSentCallMode = 0;
bool notificationTwoRequired = false;
//effects
byte effectCurrent = effectDefault;
byte effectSpeed = effectSpeedDefault;
byte effectIntensity = effectIntensityDefault;
byte effectPalette = effectPaletteDefault;
byte effectCurrent = 0;
byte effectSpeed = 128;
byte effectIntensity = 128;
byte effectPalette = 0;
//network
bool udpConnected = false, udpRgbConnected = false;
//ui style
char cssCol[6][9]={"","","","","",""};
bool showWelcomePage = false;
//hue
@ -350,11 +336,6 @@ byte overlayCurrent = overlayDefault;
byte overlaySpeed = 200;
unsigned long overlayRefreshMs = 200;
unsigned long overlayRefreshedTime;
int overlayArr[6];
uint16_t overlayDur[6];
uint16_t overlayPauseDur[6];
int nixieClockI = -1;
bool nixiePause = false;
//cronixie
byte dP[]{0,0,0,0,0,0};
@ -392,6 +373,7 @@ unsigned long realtimeTimeout = 0;
long lastMqttReconnectAttempt = 0;
long lastInterfaceUpdate = 0;
byte interfaceUpdateCallMode = 0;
char mqttStatusTopic[40] = ""; //this must be global because of async handlers
#if AUXPIN >= 0
//auxiliary debug pin
@ -427,22 +409,35 @@ uint16_t ntpLocalPort = 2390;
char* obuf;
uint16_t olen = 0;
uint16_t savedPresets = 0;
int8_t currentPreset = -1;
bool isPreset = false;
byte errorFlag = 0;
String messageHead, messageSub;
byte optionType;
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;
//function prototypes
void colorFromUint32(uint32_t,bool=false);
void serveMessage(AsyncWebServerRequest*,uint16_t,String,String,byte);
void handleE131Packet(e131_packet_t*, IPAddress);
#define E131_MAX_UNIVERSE_COUNT 9
//udp interface objects
WiFiUDP notifierUdp, rgbUdp;
WiFiUDP ntpUdp;
E131* e131;
ESPAsyncE131 e131(handleE131Packet);
bool e131NewData = false;
//led fx library object
WS2812FX strip = WS2812FX();
@ -474,11 +469,6 @@ WS2812FX strip = WS2812FX();
#include "SPIFFSEditor.h"
#endif
//function prototypes
void serveMessage(AsyncWebServerRequest*,uint16_t,String,String,byte);
//turns all LEDs off and restarts ESP
void reset()
{
@ -495,7 +485,7 @@ void reset()
//append new c string to temp buffer efficiently
bool oappend(char* txt)
bool oappend(const char* txt)
{
uint16_t len = strlen(txt);
if (olen + len >= OMAX) return false; //buffer full
@ -535,7 +525,6 @@ void loop() {
handleAlexa();
handleOverlays();
if (doSendHADiscovery) sendHADiscoveryMQTT();
yield();
if (doReboot) reset();

File diff suppressed because one or more lines are too long

View File

@ -62,9 +62,6 @@
<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>
@ -179,9 +176,6 @@
<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>
@ -203,14 +197,14 @@
<ClInclude Include="src\dependencies\timezone\Timezone.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FX.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\e131\ESPAsyncE131.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>
@ -253,9 +247,6 @@
<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>
@ -268,5 +259,14 @@
<ClCompile Include="src\dependencies\timezone\Timezone.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FX.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FX_fcn.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\dependencies\e131\ESPAsyncE131.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -6,7 +6,7 @@
#define EEPSIZE 2560
//eeprom Version code, enables default settings instead of 0 init on update
#define EEPVER 11
#define EEPVER 14
//0 -> old version, default
//1 -> 0.4p 1711272 and up
//2 -> 0.4p 1711302 and up
@ -19,7 +19,14 @@
//9 -> 0.8.0
//10-> 0.8.2
//11-> 0.8.5-dev #mqttauth @TimothyBrown
//12-> 0.8.7-dev
//13-> 0.9.0-dev
//14-> 0.9.0-b1
void commit()
{
if (!EEPROM.commit()) errorFlag = 2;
}
/*
* Erase all configuration data
@ -30,7 +37,7 @@ void clearEEPROM()
{
EEPROM.write(i, 0);
}
EEPROM.commit();
commit();
}
@ -90,9 +97,6 @@ void saveSettingsToEEPROM()
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);
@ -111,10 +115,6 @@ void saveSettingsToEEPROM()
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);
@ -129,20 +129,14 @@ void saveSettingsToEEPROM()
EEPROM.write(367, (arlsOffset>=0));
EEPROM.write(368, abs(arlsOffset));
EEPROM.write(369, turnOnAtBoot);
EEPROM.write(370, useHSBDefault);
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(375, strip.milliampsPerLed); //was apWaitTimeSecs up to 0.8.5
EEPROM.write(376, apBehavior);
EEPROM.write(377, EEPVER); //eeprom was updated to latest
EEPROM.write(378, colSecS[0]);
EEPROM.write(379, colSecS[1]);
EEPROM.write(380, colSecS[2]);
EEPROM.write(381, colSecS[3]);
EEPROM.write(382, strip.paletteBlend);
EEPROM.write(383, strip.colorOrder);
@ -159,20 +153,13 @@ void saveSettingsToEEPROM()
EEPROM.write(394, abs(utcOffsetSecs) & 0xFF);
EEPROM.write(395, (abs(utcOffsetSecs) >> 8) & 0xFF);
EEPROM.write(396, (utcOffsetSecs<0)); //is negative
//397 was initLedsLast
EEPROM.write(397, syncToggleReceive);
EEPROM.write(398, (ledCount >> 8) & 0xFF);
EEPROM.write(399, !enableSecTransition);
//favorite setting (preset) memory (25 slots/ each 20byte)
//400 - 899 reserved
for (int k=0;k<6;k++){
int in = 900+k*8;
writeStringToEEPROM(in, cssCol[k], 8);
}
EEPROM.write(948,currentTheme);
writeStringToEEPROM(950, cssFont, 32);
//400 - 940 reserved
writeStringToEEPROM(990, ntpServerName, 32);
EEPROM.write(2048, huePollingEnabled);
//EEPROM.write(2049, hueUpdatingEnabled);
@ -227,7 +214,6 @@ void saveSettingsToEEPROM()
EEPROM.write(2200, !receiveDirect);
EEPROM.write(2201, notifyMacro); //was enableRealtime
EEPROM.write(2202, uiConfiguration);
EEPROM.write(2203, autoRGBtoRGBW);
EEPROM.write(2204, skipFirstLed);
@ -244,6 +230,8 @@ void saveSettingsToEEPROM()
saveCurrPresetCycConf = false;
}
EEPROM.write(2213, disableNLeds);
writeStringToEEPROM(2220, blynkApiKey, 35);
for (int i = 0; i < 8; ++i)
@ -254,6 +242,7 @@ void saveSettingsToEEPROM()
EEPROM.write(2290 + i, timerMacro[i] );
}
EEPROM.write(2299, mqttEnabled);
writeStringToEEPROM(2300, mqttServer, 32);
writeStringToEEPROM(2333, mqttDeviceTopic, 32);
writeStringToEEPROM(2366, mqttGroupTopic, 32);
@ -263,7 +252,7 @@ void saveSettingsToEEPROM()
EEPROM.write(2522, mqttPort & 0xFF);
EEPROM.write(2523, (mqttPort >> 8) & 0xFF);
EEPROM.commit();
commit();
}
@ -317,9 +306,6 @@ void loadSettingsFromEEPROM(bool first)
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];
briS = EEPROM.read(249); bri = briS;
if (!EEPROM.read(369) && first)
{
@ -340,8 +326,6 @@ void loadSettingsFromEEPROM(bool first)
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);
@ -358,36 +342,19 @@ void loadSettingsFromEEPROM(bool first)
arlsOffset = EEPROM.read(368);
if (!EEPROM.read(367)) arlsOffset = -arlsOffset;
turnOnAtBoot = EEPROM.read(369);
useHSBDefault = EEPROM.read(370);
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);
apBehavior = EEPROM.read(376);
}
//377 = lastEEPROMversion
if (lastEEPROMversion > 1) {
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;
aOtaEnabled = EEPROM.read(390);
receiveNotificationColor = EEPROM.read(391);
receiveNotificationEffects = EEPROM.read(392);
readStringFromEEPROM(950, cssFont, 32);
} else //keep receiving notification behavior from pre0.5.0 after update
{
receiveNotificationColor = receiveNotificationBrightness;
receiveNotificationEffects = receiveNotificationBrightness;
}
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
if (lastEEPROMversion > 4) {
huePollingEnabled = EEPROM.read(2048);
//hueUpdatingEnabled = EEPROM.read(2049);
@ -485,14 +452,29 @@ void loadSettingsFromEEPROM(bool first)
mqttPort = EEPROM.read(2522) + ((EEPROM.read(2523) << 8) & 0xFF00);
}
if (lastEEPROMversion > 11)
{
strip.milliampsPerLed = EEPROM.read(375);
} else if (strip.ablMilliampsMax == 65000) //65000 indicates disabled ABL in <0.8.7
{
strip.ablMilliampsMax = ABL_MILLIAMPS_DEFAULT;
strip.milliampsPerLed = 0; //disable ABL
}
if (lastEEPROMversion > 12)
{
readStringFromEEPROM(990, ntpServerName, 32);
}
if (lastEEPROMversion > 13)
{
mqttEnabled = EEPROM.read(2299);
syncToggleReceive = EEPROM.read(397);
} else {
mqttEnabled = true;
syncToggleReceive = false;
}
receiveDirect = !EEPROM.read(2200);
notifyMacro = EEPROM.read(2201);
uiConfiguration = EEPROM.read(2202);
#ifdef WLED_DISABLE_MOBILE_UI
uiConfiguration = 1;
//force default UI since mobile is unavailable
#endif
autoRGBtoRGBW = EEPROM.read(2203);
skipFirstLed = EEPROM.read(2204);
@ -508,6 +490,8 @@ void loadSettingsFromEEPROM(bool first)
presetApplyFx = EEPROM.read(2212);
}
disableNLeds = EEPROM.read(2213);
bootPreset = EEPROM.read(389);
wifiLock = EEPROM.read(393);
utcOffsetSecs = EEPROM.read(394) + ((EEPROM.read(395) << 8) & 0xFF00);
@ -517,29 +501,45 @@ void loadSettingsFromEEPROM(bool first)
//favorite setting (preset) memory (25 slots/ each 20byte)
//400 - 899 reserved
currentTheme = EEPROM.read(948);
for (int k=0;k<6;k++){
int in=900+k*8;
readStringFromEEPROM(in, cssCol[k], 8);
}
//custom macro memory (16 slots/ each 64byte)
//1024-2047 reserved
readStringFromEEPROM(2220, blynkApiKey, 35);
if (strlen(blynkApiKey) < 25) blynkApiKey[0] = 0;
//user MOD memory
//2944 - 3071 reserved
useHSB = useHSBDefault;
overlayCurrent = overlayDefault;
savedToPresets();
}
//PRESET PROTOCOL 20 bytes
//0: preset purpose byte 0:invalid 1:valid preset 1.0
//0: preset purpose byte 0:invalid 1:valid preset 2:segment preset 2.0
//1:a 2:r 3:g 4:b 5:w 6:er 7:eg 8:eb 9:ew 10:fx 11:sx | custom chase 12:numP 13:numS 14:(0:fs 1:both 2:fe) 15:step 16:ix 17: fp 18-19:Zeros
//determines which presets already contain save data
void savedToPresets()
{
for (byte index = 1; index < 16; index++)
{
uint16_t i = 380 + index*20;
if (EEPROM.read(i) == 1) {
savedPresets |= 0x01 << (index-1);
} else
{
savedPresets &= ~(0x01 << (index-1));
}
}
if (EEPROM.read(700) == 2) {
savedPresets |= 0x01 << 15;
} else
{
savedPresets &= ~(0x01 << 15);
}
}
bool applyPreset(byte index, bool loadBri = true, bool loadCol = true, bool loadFX = true)
{
@ -548,9 +548,11 @@ bool applyPreset(byte index, bool loadBri = true, bool loadCol = true, bool load
loadSettingsFromEEPROM(false);//load boot defaults
return true;
}
if (index > 25 || index < 1) return false;
if (index > 16 || index < 1) return false;
uint16_t i = 380 + index*20;
if (EEPROM.read(i) == 0) return false;
if (index < 16) {
if (EEPROM.read(i) != 1) return false;
strip.applyToAllSelected = true;
if (loadBri) bri = EEPROM.read(i+1);
if (loadCol)
{
@ -567,14 +569,26 @@ bool applyPreset(byte index, bool loadBri = true, bool loadCol = true, bool load
effectIntensity = EEPROM.read(i+16);
effectPalette = EEPROM.read(i+17);
}
} else {
if (EEPROM.read(i) != 2) return false;
strip.applyToAllSelected = false;
if (loadBri) bri = EEPROM.read(i+1);
WS2812FX::Segment* seg = strip.getSegments();
memcpy(seg, EEPROM.getDataPtr() +i+2, 240);
setValuesFromMainSeg();
}
currentPreset = index;
isPreset = true;
return true;
}
void savePreset(byte index)
{
if (index > 25) return;
if (index > 16) return;
if (index < 1) {saveSettingsToEEPROM();return;}
uint16_t i = 380 + index*20;//min400
if (index < 16) {
EEPROM.write(i, 1);
EEPROM.write(i+1, bri);
for (uint16_t j=0; j<4; j++)
@ -587,7 +601,17 @@ void savePreset(byte index)
EEPROM.write(i+16, effectIntensity);
EEPROM.write(i+17, effectPalette);
EEPROM.commit();
} else { //segment 16 can save segments
EEPROM.write(i, 2);
EEPROM.write(i+1, bri);
WS2812FX::Segment* seg = strip.getSegments();
memcpy(EEPROM.getDataPtr() +i+2, seg, 240);
}
commit();
savedToPresets();
currentPreset = index;
isPreset = true;
}
@ -629,5 +653,5 @@ void saveMacro(byte index, String mc, bool sing=true) //only commit on single sa
{
EEPROM.write(i, mc.charAt(i-s));
}
if (sing) EEPROM.commit();
if (sing) commit();
}

View File

@ -3,7 +3,7 @@
*/
//build XML response to HTTP /win API request
char* XML_response(AsyncWebServerRequest *request, bool includeTheme, char* dest = nullptr)
char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr)
{
char sbuf[(dest == nullptr)?1024:1]; //allocate local buffer if none passed
obuf = (dest == nullptr)? sbuf:dest;
@ -53,9 +53,7 @@ char* XML_response(AsyncWebServerRequest *request, bool includeTheme, char* dest
}
oappend("</wv><ws>");
oappendi(colSec[3]);
oappend("</ws><md>");
oappendi(useHSB);
oappend("</md><cy>");
oappend("</ws><cy>");
oappendi(presetCyclingEnabled);
oappend("</cy><ds>");
if (realtimeActive)
@ -77,34 +75,14 @@ char* XML_response(AsyncWebServerRequest *request, bool includeTheme, char* dest
} 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>");
oappend("</ds><ss>");
oappendi(strip.getMainSegmentId());
oappend("</ss></vs>");
if (request != nullptr) request->send(200, "text/xml", obuf);
}
//append a numeric setting to string buffer
void sappend(char stype, char* key, int val)
void sappend(char stype, const char* key, int val)
{
char ds[] = "d.Sf.";
@ -135,7 +113,7 @@ void sappend(char stype, char* key, int val)
}
//append a string setting to buffer
void sappends(char stype, char* key, char* val)
void sappends(char stype, const char* key, char* val)
{
switch(stype)
{
@ -223,8 +201,14 @@ void getSettingsJS(byte subPage, char* dest)
}
if (subPage == 2) {
#ifdef ESP8266
#if LEDPIN == 3
oappend("d.Sf.LC.max=500;");
#endif
#endif
sappend('v',"LC",ledCount);
sappend('v',"MA",strip.ablMilliampsMax);
sappend('v',"LA",strip.milliampsPerLed);
if (strip.currentMilliamps)
{
sappends('m',"(\"pow\")[0]","");
@ -232,33 +216,20 @@ void getSettingsJS(byte subPage, char* dest)
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",colS[3]);
sappend('v',"SR",colSecS[0]);
sappend('v',"SG",colSecS[1]);
sappend('v',"SB",colSecS[2]);
sappend('v',"SW",colSecS[3]);
sappend('c',"BO",turnOnAtBoot);
sappend('v',"BP",bootPreset);
oappend("f=");
oappendi(effectDefault);
oappend(";p=");
oappendi(effectPaletteDefault);
oappend(";");
sappend('v',"SX",effectSpeedDefault);
sappend('v',"IX",effectIntensityDefault);
sappend('c',"GB",strip.gammaCorrectBri);
sappend('c',"GC",strip.gammaCorrectCol);
sappend('c',"TF",fadeTransition);
sappend('v',"TD",transitionDelay);
sappend('v',"TD",transitionDelayDefault);
sappend('c',"PF",strip.paletteFade);
sappend('c',"T2",enableSecTransition);
sappend('v',"BF",briMultiplier);
sappend('v',"TB",nightlightTargetBri);
sappend('v',"TL",nightlightDelayMinsDefault);
@ -266,21 +237,13 @@ void getSettingsJS(byte subPage, char* dest)
sappend('i',"PB",strip.paletteBlend);
sappend('c',"RV",strip.reverseMode);
sappend('c',"SL",skipFirstLed);
sappend('v',"DL",disableNLeds);
}
if (subPage == 3)
{
sappend('i',"UI",uiConfiguration);
sappends('s',"DS",serverDescription);
sappend('c',"MD",useHSBDefault);
sappend('i',"TH",currentTheme);
char k[3]; k[0] = 'C'; k[2] = 0; //keys
for (int i=0; i<6; i++)
{
k[1] = 48+i; //ascii 0,1,2,3,4,5
sappends('s',k,cssCol[i]);
}
sappends('s',"CF",cssFont);
sappend('c',"ST",syncToggleReceive);
}
if (subPage == 4)
@ -307,6 +270,9 @@ void getSettingsJS(byte subPage, char* dest)
sappends('s',"AI",alexaInvocationName);
sappend('c',"SA",notifyAlexa);
sappends('s',"BK",(char*)((blynkEnabled)?"Hidden":""));
#ifdef WLED_ENABLE_MQTT
sappend('c',"MQ",mqttEnabled);
sappends('s',"MS",mqttServer);
sappend('v',"MQPORT",mqttPort);
sappends('s',"MQUSER",mqttUser);
@ -319,6 +285,11 @@ void getSettingsJS(byte subPage, char* dest)
sappends('s',"MQCID",mqttClientID);
sappends('s',"MD",mqttDeviceTopic);
sappends('s',"MG",mqttGroupTopic);
#endif
#ifdef WLED_DISABLE_HUESYNC
sappends('m',"(\"hms\")[0]","Unsupported in build");
#else
sappend('v',"H0",hueIP[0]);
sappend('v',"H1",hueIP[1]);
sappend('v',"H2",hueIP[2]);
@ -330,11 +301,13 @@ void getSettingsJS(byte subPage, char* dest)
sappend('c',"HB",hueApplyBri);
sappend('c',"HC",hueApplyColor);
sappends('m',"(\"hms\")[0]",hueError);
#endif
}
if (subPage == 5)
{
sappend('c',"NT",ntpEnabled);
sappends('s',"NS",ntpServerName);
sappend('c',"CF",!useAMPM);
sappend('i',"TZ",currentTimezone);
sappend('v',"UO",utcOffsetSecs);
@ -399,29 +372,3 @@ void getSettingsJS(byte subPage, char* dest)
}
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
}
}

View File

@ -14,6 +14,16 @@ void _setRandomColor(bool _sec,bool fromButton=false)
}
bool isAsterisksOnly(const char* str, byte maxLen)
{
for (byte i = 0; i < maxLen; i++) {
if (str[i] == 0) break;
if (str[i] != '*') return false;
}
return true;
}
//called upon POST settings form submit
void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
{
@ -23,16 +33,17 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
//WIFI SETTINGS
if (subPage == 1)
{
strcpy(clientSSID,request->arg("CS").c_str());
if (request->arg("CP").charAt(0) != '*') strcpy(clientPass, request->arg("CP").c_str());
strlcpy(clientSSID,request->arg("CS").c_str(), 33);
strcpy(cmDNS, request->arg("CM").c_str());
if (!isAsterisksOnly(request->arg("CP").c_str(), 65)) strlcpy(clientPass, request->arg("CP").c_str(), 65);
strlcpy(cmDNS, request->arg("CM").c_str(), 33);
apBehavior = request->arg("AB").toInt();
strcpy(apSSID, request->arg("AS").c_str());
strlcpy(apSSID, request->arg("AS").c_str(), 33);
apHide = request->hasArg("AH");
int passlen = request->arg("AP").length();
if (passlen == 0 || (passlen > 7 && request->arg("AP").charAt(0) != '*')) strcpy(apPass, request->arg("AP").c_str());
if (passlen == 0 || (passlen > 7 && !isAsterisksOnly(request->arg("AP").c_str(), 65))) strlcpy(apPass, request->arg("AP").c_str(), 65);
int t = request->arg("AC").toInt(); if (t > 0 && t < 14) apChannel = t;
char k[3]; k[2] = 0;
@ -56,44 +67,20 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
{
int t = request->arg("LC").toInt();
if (t > 0 && t <= MAX_LEDS) ledCount = t;
#ifndef ARDUINO_ARCH_ESP32
#ifdef ESP8266
#if LEDPIN == 3
if (ledCount > MAX_LEDS_DMA) ledCount = MAX_LEDS_DMA; //DMA method uses too much ram
#endif
#endif
strip.ablMilliampsMax = request->arg("MA").toInt();
strip.milliampsPerLed = request->arg("LA").toInt();
useRGBW = request->hasArg("EW");
strip.colorOrder = request->arg("CO").toInt();
autoRGBtoRGBW = request->hasArg("AW");
//ignore settings and save current brightness, colors and fx as default
if (request->hasArg("IS"))
{
for (byte i=0; i<4; i++)
{
colS[i] = col[i];
colSecS[i] = colSec[i];
}
briS = bri;
effectDefault = effectCurrent;
effectSpeedDefault = effectSpeed;
effectIntensityDefault = effectIntensity;
effectPaletteDefault = effectPalette;
} else {
colS[0] = request->arg("CR").toInt();
colS[1] = request->arg("CG").toInt();
colS[2] = request->arg("CB").toInt();
colSecS[0] = request->arg("SR").toInt();
colSecS[1] = request->arg("SG").toInt();
colSecS[2] = request->arg("SB").toInt();
colS[3] = request->arg("CW").toInt();
colSecS[3] = request->arg("SW").toInt();
briS = request->arg("CA").toInt();
effectDefault = request->arg("FX").toInt();
effectSpeedDefault = request->arg("SX").toInt();
effectIntensityDefault = request->arg("IX").toInt();
effectPaletteDefault = request->arg("FP").toInt();
}
saveCurrPresetCycConf = request->hasArg("PC");
turnOnAtBoot = request->hasArg("BO");
t = request->arg("BP").toInt();
@ -106,7 +93,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
if (t > 0) transitionDelay = t;
transitionDelayDefault = t;
strip.paletteFade = request->hasArg("PF");
enableSecTransition = request->hasArg("T2");
nightlightTargetBri = request->arg("TB").toInt();
t = request->arg("TL").toInt();
@ -118,6 +104,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
if (t >= 0 && t < 4) strip.paletteBlend = t;
strip.reverseMode = request->hasArg("RV");
skipFirstLed = request->hasArg("SL");
disableNLeds = request->arg("DL").toInt();
t = request->arg("BF").toInt();
if (t > 0) briMultiplier = t;
}
@ -125,19 +112,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
//UI
if (subPage == 3)
{
int t = request->arg("UI").toInt();
if (t >= 0 && t < 3) uiConfiguration = t;
strcpy(serverDescription, request->arg("DS").c_str());
useHSBDefault = request->hasArg("MD");
useHSB = useHSBDefault;
currentTheme = request->arg("TH").toInt();
char k[3]; k[0]='C'; k[2]=0;
for(int i=0;i<6;i++)
{
k[1] = i+48;
strcpy(cssCol[i],request->arg(k).c_str());
}
strcpy(cssFont,request->arg("CF").c_str());
strlcpy(serverDescription, request->arg("DS").c_str(), 33);
syncToggleReceive = request->hasArg("ST");
}
//SYNC
@ -171,21 +147,25 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
if (t >= -255 && t <= 255) arlsOffset = t;
alexaEnabled = request->hasArg("AL");
strcpy(alexaInvocationName, request->arg("AI").c_str());
strlcpy(alexaInvocationName, request->arg("AI").c_str(), 33);
if (request->hasArg("BK") && !request->arg("BK").equals("Hidden")) {
strcpy(blynkApiKey,request->arg("BK").c_str()); initBlynk(blynkApiKey);
strlcpy(blynkApiKey, request->arg("BK").c_str(), 36); initBlynk(blynkApiKey);
}
strcpy(mqttServer, request->arg("MS").c_str());
#ifdef WLED_ENABLE_MQTT
mqttEnabled = request->hasArg("MQ");
strlcpy(mqttServer, request->arg("MS").c_str(), 33);
t = request->arg("MQPORT").toInt();
if (t > 0) mqttPort = t;
strcpy(mqttUser, request->arg("MQUSER").c_str());
if (request->arg("MQPASS").charAt(0) != '*') strcpy(mqttPass, request->arg("MQPASS").c_str());
strcpy(mqttClientID, request->arg("MQCID").c_str());
strcpy(mqttDeviceTopic, request->arg("MD").c_str());
strcpy(mqttGroupTopic, request->arg("MG").c_str());
strlcpy(mqttUser, request->arg("MQUSER").c_str(), 41);
if (!isAsterisksOnly(request->arg("MQPASS").c_str(), 41)) strlcpy(mqttPass, request->arg("MQPASS").c_str(), 41);
strlcpy(mqttClientID, request->arg("MQCID").c_str(), 41);
strlcpy(mqttDeviceTopic, request->arg("MD").c_str(), 33);
strlcpy(mqttGroupTopic, request->arg("MG").c_str(), 33);
#endif
#ifndef WLED_DISABLE_HUESYNC
for (int i=0;i<4;i++){
String a = "H"+String(i);
hueIP[i] = request->arg(a).toInt();
@ -203,12 +183,14 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
huePollingEnabled = request->hasArg("HP");
hueStoreAllowed = true;
reconnectHue();
#endif
}
//TIME
if (subPage == 5)
{
ntpEnabled = request->hasArg("NT");
strlcpy(ntpServerName, request->arg("NS").c_str(), 33);
useAMPM = !request->hasArg("CF");
currentTimezone = request->arg("TZ").toInt();
utcOffsetSecs = request->arg("UO").toInt();
@ -296,7 +278,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
}
if (!otaLock && request->arg("OP").length() > 0)
{
strcpy(otaPass,request->arg("OP").c_str());
strlcpy(otaPass,request->arg("OP").c_str(), 33);
}
}
@ -308,7 +290,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
}
}
if (subPage != 6 || !doReboot) saveSettingsToEEPROM(); //do not save if factory reset
if (subPage == 2) strip.init(useRGBW,ledCount,skipFirstLed);
if (subPage == 2) {
strip.init(useRGBW,ledCount,skipFirstLed,disableNLeds);
}
if (subPage == 4) alexaInit();
}
@ -373,11 +357,45 @@ bool handleSet(AsyncWebServerRequest *request, const String& req)
}
pos = req.indexOf("IN");
if (pos < 1) XML_response(request, false);
if (pos < 1) XML_response(request);
return true;
//if you save a macro in one request, other commands in that request are ignored due to unwanted behavior otherwise
}
strip.applyToAllSelected = true;
//segment select (sets main segment)
byte prevMain = strip.getMainSegmentId();
pos = req.indexOf("SM=");
if (pos > 0) {
strip.mainSegment = getNumVal(&req, pos);
}
byte main = strip.getMainSegmentId();
if (main != prevMain) setValuesFromMainSeg();
pos = req.indexOf("SS=");
if (pos > 0) {
byte t = getNumVal(&req, pos);
if (t < strip.getMaxSegments()) main = t;
}
pos = req.indexOf("SV="); //segment selected
if (pos > 0) strip.getSegment(main).setOption(0, (req.charAt(pos+3) != '0'));
uint16_t startI = strip.getSegment(main).start;
uint16_t stopI = strip.getSegment(main).stop;
pos = req.indexOf("&S="); //segment start
if (pos > 0) {
startI = getNumVal(&req, pos);
}
pos = req.indexOf("S2="); //segment stop
if (pos > 0) {
stopI = getNumVal(&req, pos);
}
strip.setSegment(main, startI, stopI);
main = strip.getMainSegmentId();
//set brightness
updateVal(&req, "&A=", &bri);
@ -413,45 +431,12 @@ bool handleSet(AsyncWebServerRequest *request, const String& req)
colorFromDecOrHexString(colSec, (char*)req.substring(pos + 3).c_str());
}
//set 2nd to white
pos = req.indexOf("SW");
if (pos > 0) {
if(useRGBW) {
colSec[3] = 255;
colSec[0] = 0;
colSec[1] = 0;
colSec[2] = 0;
} else {
colSec[0] = 255;
colSec[1] = 255;
colSec[2] = 255;
}
}
//set 2nd to black
pos = req.indexOf("SB");
if (pos > 0) {
colSec[3] = 0;
colSec[0] = 0;
colSec[1] = 0;
colSec[2] = 0;
}
//set to random hue SR=0->1st SR=1->2nd
pos = req.indexOf("SR");
if (pos > 0) {
_setRandomColor(getNumVal(&req, pos));
}
//set 2nd to 1st
pos = req.indexOf("SP");
if (pos > 0) {
colSec[0] = col[0];
colSec[1] = col[1];
colSec[2] = col[2];
colSec[3] = col[3];
}
//swap 2nd & 1st
pos = req.indexOf("SC");
if (pos > 0) {
@ -470,27 +455,6 @@ bool handleSet(AsyncWebServerRequest *request, const String& req)
updateVal(&req, "IX=", &effectIntensity);
updateVal(&req, "FP=", &effectPalette, 0, strip.getPaletteCount()-1);
//set hue polling light: 0 -off
#ifndef WLED_DISABLE_HUESYNC
pos = req.indexOf("HP=");
if (pos > 0) {
int id = getNumVal(&req, pos);
if (id > 0)
{
if (id < 100) huePollLightId = id;
reconnectHue();
} else {
huePollingEnabled = false;
}
}
#endif
//set default control mode (0 - RGB, 1 - HSB)
pos = req.indexOf("MD=");
if (pos > 0) {
useHSB = getNumVal(&req, pos);
}
//set advanced overlay
pos = req.indexOf("OL=");
if (pos > 0) {
@ -601,7 +565,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req)
//Segment reverse
pos = req.indexOf("RV=");
if (pos > 0) strip.getSegment(0).setOption(1, req.charAt(pos+3) != '0');
if (pos > 0) strip.getSegment(main).setOption(1, req.charAt(pos+3) != '0');
//deactivate nightlight if target brightness is reached
if (bri == nightlightTargetBri) nightlightActive = false;
@ -660,17 +624,14 @@ bool handleSet(AsyncWebServerRequest *request, const String& req)
#ifndef WLED_DISABLE_CRONIXIE
pos = req.indexOf("NX="); //sets digits to code
if (pos > 0) {
strcpy(cronixieDisplay,req.substring(pos + 3, pos + 9).c_str());
strlcpy(cronixieDisplay, req.substring(pos + 3, pos + 9).c_str(), 6);
setCronixie();
}
if (req.indexOf("NB=") > 0) //sets backlight
pos = req.indexOf("NB=");
if (pos > 0) //sets backlight
{
cronixieBacklight = true;
if (req.indexOf("NB=0") > 0)
{
cronixieBacklight = false;
}
presetApplyFx = (req.charAt(pos+3) != '0');
if (overlayCurrent == 3) strip.setCronixieBacklight(cronixieBacklight);
overlayRefreshedTime = 0;
}
@ -692,7 +653,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req)
//internal call, does not send XML response
pos = req.indexOf("IN");
if (pos < 1) XML_response(request, (req.indexOf("&IT") > 0)); //include theme if firstload
if (pos < 1) XML_response(request);
pos = req.indexOf("&NN"); //do not send UDP notifications this time
colorUpdated((pos > 0) ? 5:1);

View File

@ -1,40 +1,82 @@
/*
* Utility for SPIFFS filesystem & Serial console
*/
enum class AdaState {
Header_A,
Header_d,
Header_a,
Header_CountHi,
Header_CountLo,
Header_CountCheck,
Data_Red,
Data_Green,
Data_Blue
};
void handleSerial()
{
if (Serial.available() > 0) //support for Adalight protocol to high-speed control LEDs over serial
{
if (!Serial.find("Ada")) return;
#ifdef WLED_ENABLE_ADALIGHT
static auto state = AdaState::Header_A;
static uint16_t count = 0;
static uint16_t pixel = 0;
static byte check = 0x00;
static byte red = 0x00;
static byte green = 0x00;
while (Serial.available() > 0)
{
yield();
byte next = Serial.read();
switch (state) {
case AdaState::Header_A:
if (next == 'A') state = AdaState::Header_d;
break;
case AdaState::Header_d:
if (next == 'd') state = AdaState::Header_a;
else state = AdaState::Header_A;
break;
case AdaState::Header_a:
if (next == 'a') state = AdaState::Header_CountHi;
else state = AdaState::Header_A;
break;
case AdaState::Header_CountHi:
pixel = 0;
count = next * 0x100;
check = next;
state = AdaState::Header_CountLo;
break;
case AdaState::Header_CountLo:
count += next + 1;
check = check ^ next ^ 0x55;
state = AdaState::Header_CountCheck;
break;
case AdaState::Header_CountCheck:
if (check == next) state = AdaState::Data_Red;
else state = AdaState::Header_A;
break;
case AdaState::Data_Red:
red = next;
state = AdaState::Data_Green;
break;
case AdaState::Data_Green:
green = next;
state = AdaState::Data_Blue;
break;
case AdaState::Data_Blue:
byte blue = next;
setRealtimePixel(pixel++, red, green, blue, 0);
if (--count > 0) state = AdaState::Data_Red;
else {
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++)
{
for (byte j=0; j<3; j++)
{
while (Serial.peek()<0) //no data yet available
{
yield();
to++;
if (to>15) {strip.show(); return;} //unexpected end of transmission
}
to = 0;
sc[j] = Serial.read();
}
setRealtimePixel(i,sc[0],sc[1],sc[2],0);
}
strip.show();
state = AdaState::Header_A;
}
break;
}
}
#endif
}

View File

@ -7,7 +7,12 @@ void wledInit()
EEPROM.begin(EEPSIZE);
ledCount = EEPROM.read(229) + ((EEPROM.read(398) << 8) & 0xFF00);
if (ledCount > MAX_LEDS || ledCount == 0) ledCount = 30;
#ifndef ARDUINO_ARCH_ESP32
disableNLeds = EEPROM.read(2213);
//this was reading 255 after inital flash causing bootloop. Don't know why.
disableNLeds = disableNLeds != 255 ? disableNLeds : 0;
#ifdef ESP8266
#if LEDPIN == 3
if (ledCount > MAX_LEDS_DMA) ledCount = MAX_LEDS_DMA; //DMA method uses too much ram
#endif
@ -25,7 +30,7 @@ void wledInit()
DEBUG_PRINT("heap ");
DEBUG_PRINTLN(ESP.getFreeHeap());
strip.init(EEPROM.read(372),ledCount,EEPROM.read(2204)); //init LEDs quickly
strip.init(EEPROM.read(372),ledCount,EEPROM.read(2204),disableNLeds); //init LEDs quickly
strip.setBrightness(0);
DEBUG_PRINT("LEDs inited. heap usage ~");
@ -85,10 +90,6 @@ void wledInit()
//HTTP server page init
initServer();
strip.service();
initConnection();
}
@ -139,7 +140,10 @@ void initAP(bool resetAP=false){
if (udpPort > 0 && udpPort != ntpLocalPort)
{
udpConnected = notifierUdp.begin(udpPort);
if (udpConnected && udpRgbPort != udpPort) udpRgbConnected = rgbUdp.begin(udpRgbPort);
}
if (udpRgbPort > 0 && udpRgbPort != ntpLocalPort && udpRgbPort != udpPort)
{
udpRgbConnected = rgbUdp.begin(udpRgbPort);
}
dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
@ -152,7 +156,7 @@ void initConnection()
{
WiFi.disconnect(); //close old connections
if (staticIP[0] != 0)
if (staticIP[0] != 0 && staticGateway[0] != 0)
{
WiFi.config(staticIP, staticGateway, staticSubnet, IPAddress(8,8,8,8));
} else
@ -230,7 +234,7 @@ void initInterfaces() {
if (ntpEnabled) ntpConnected = ntpUdp.begin(ntpLocalPort);
initBlynk(blynkApiKey);
initE131();
e131.begin((e131Multicast) ? E131_MULTICAST : E131_UNICAST , e131Universe, E131_MAX_UNIVERSE_COUNT);
reconnectHue();
initMqtt();
interfacesInited = true;
@ -238,10 +242,28 @@ void initInterfaces() {
}
byte stacO = 0;
uint32_t lastHeap;
unsigned long heapTime = 0;
void handleConnection() {
//TODO: reconnect if heap <8000
if (millis() < 2000 && (!WLED_WIFI_CONFIGURED || apBehavior == 2)) return;
if (lastReconnectAttempt == 0) initConnection();
//reconnect WiFi to clear stale allocations if heap gets too low
if (millis() - heapTime > 5000)
{
uint32_t heap = ESP.getFreeHeap();
if (heap < 9000 && lastHeap < 9000) {
DEBUG_PRINT("Heap too low! ");
DEBUG_PRINTLN(heap);
forceReconnect = true;
}
lastHeap = heap;
heapTime = millis();
}
byte stac = 0;
if (apActive) {
#ifdef ESP8266
stac = wifi_softap_get_station_num();
#else
@ -259,6 +281,7 @@ void handleConnection() {
else initConnection(); //restart search
}
}
}
if (forceReconnect) {
DEBUG_PRINTLN("Forcing reconnect.");
initConnection();
@ -273,7 +296,7 @@ void handleConnection() {
interfacesInited = false;
initConnection();
}
if (millis() - lastReconnectAttempt > 300000 && WLED_WIFI_CONFIGURED) initConnection();
if (millis() - lastReconnectAttempt > ((stac) ? 300000 : 20000) && WLED_WIFI_CONFIGURED) initConnection();
if (!apActive && millis() - lastReconnectAttempt > 12000 && (!wasConnected || apBehavior == 1)) initAP();
} else if (!interfacesInited) { //newly connected
DEBUG_PRINTLN("");
@ -307,13 +330,3 @@ int getSignalQuality(int rssi)
}
return quality;
}
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;
}

View File

@ -38,7 +38,7 @@ void notify(byte callMode, bool followUp=false)
//0: old 1: supports white 2: supports secondary color
//3: supports FX intensity, 24 byte packet 4: supports transitionDelay 5: sup palette
//6: supports timebase syncing, 29 byte packet 7: supports tertiary color
udpOut[11] = 6;
udpOut[11] = 7;
udpOut[12] = colSec[0];
udpOut[13] = colSec[1];
udpOut[14] = colSec[2];
@ -47,10 +47,12 @@ void notify(byte callMode, bool followUp=false)
udpOut[17] = (transitionDelay >> 0) & 0xFF;
udpOut[18] = (transitionDelay >> 8) & 0xFF;
udpOut[19] = effectPalette;
/*udpOut[20] = colTer[0];
udpOut[21] = colTer[1];
udpOut[22] = colTer[2];
udpOut[23] = colTer[3];*/
uint32_t colTer = strip.getSegment(strip.getMainSegmentId()).colors[2];
udpOut[20] = (colTer >> 16) & 0xFF;
udpOut[21] = (colTer >> 8) & 0xFF;
udpOut[22] = (colTer >> 0) & 0xFF;
udpOut[23] = (colTer >> 24) & 0xFF;
udpOut[24] = followUp;
uint32_t t = millis() + strip.timebase;
udpOut[25] = (t >> 24) & 0xFF;
@ -86,36 +88,26 @@ void arlsLock(uint32_t timeoutMs)
}
void initE131(){
if (WLED_CONNECTED && e131Enabled)
{
if (e131 == nullptr) e131 = new E131();
e131->begin((e131Multicast) ? E131_MULTICAST : E131_UNICAST , e131Universe);
} else {
e131Enabled = false;
}
}
void handleE131(){
void handleE131Packet(e131_packet_t* p, IPAddress clientIP){
//E1.31 protocol support
if(WLED_CONNECTED && e131Enabled) {
uint16_t len = e131->parsePacket();
if (!len || e131->universe < e131Universe || e131->universe > e131Universe +4) return;
uint16_t uni = htons(p->universe);
if (uni < e131Universe || uni >= e131Universe + E131_MAX_UNIVERSE_COUNT) return;
uint16_t len = htons(p->property_value_count) -1;
len /= 3; //one LED is 3 DMX channels
uint16_t multipacketOffset = (e131->universe - e131Universe)*170; //if more than 170 LEDs (510 channels), client will send in next higher universe
uint16_t multipacketOffset = (uni - e131Universe)*170; //if more than 170 LEDs (510 channels), client will send in next higher universe
if (ledCount <= multipacketOffset) return;
arlsLock(realtimeTimeoutMs);
if (len + multipacketOffset > ledCount) len = ledCount - multipacketOffset;
for (uint16_t i = 0; i < len; i++) {
int j = i * 3;
setRealtimePixel(i + multipacketOffset, e131->data[j], e131->data[j+1], e131->data[j+2], 0);
}
strip.show();
int j = i * 3 +1;
setRealtimePixel(i + multipacketOffset, p->property_values[j], p->property_values[j+1], p->property_values[j+2], 0);
}
e131NewData = true;
}
@ -126,7 +118,11 @@ void handleNotifications()
notify(notificationSentCallMode,true);
}
handleE131();
if (e131NewData && millis() - strip.getLastShow() > 15)
{
e131NewData = false;
strip.show();
}
//unlock strip when realtime UDP times out
if (realtimeActive && millis() > realtimeTimeout)
@ -176,6 +172,7 @@ void handleNotifications()
{
//ignore notification if received within a second after sending a notification ourselves
if (millis() - notificationSentTime < 1000) return;
if (udpIn[1] > 199) return; //do not receive custom versions
bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
//apply colors from notification
@ -184,7 +181,7 @@ void handleNotifications()
col[0] = udpIn[3];
col[1] = udpIn[4];
col[2] = udpIn[5];
if (udpIn[11] > 0) //check if sending modules white val is inteded
if (udpIn[11] > 0) //sending module's white val is intended
{
col[3] = udpIn[10];
if (udpIn[11] > 1)
@ -197,17 +194,14 @@ void handleNotifications()
if (udpIn[11] > 5)
{
uint32_t t = (udpIn[25] << 24) | (udpIn[26] << 16) | (udpIn[27] << 8) | (udpIn[28]);
t -= 2;
t += 2;
t -= millis();
strip.timebase = t;
}
/*if (udpIn[11] > 6)
if (udpIn[11] > 6)
{
colTer[0] = udpIn[20];
colTer[1] = udpIn[21];
colTer[2] = udpIn[22];
colSec[3] = udpIn[23];
}*/
strip.setColor(2, udpIn[20], udpIn[21], udpIn[22], udpIn[23]); //tertiary color
}
}
}
@ -231,7 +225,7 @@ void handleNotifications()
if (receiveNotificationBrightness || !someSel) bri = udpIn[2];
colorUpdated(3);
} else if (udpIn[0] > 0 && udpIn[0] < 4 && receiveDirect) //1 warls //2 drgb //3 drgbw
} else if (udpIn[0] > 0 && udpIn[0] < 5 && receiveDirect) //1 warls //2 drgb //3 drgbw
{
realtimeIP = notifierUdp.remoteIP();
DEBUG_PRINTLN(notifierUdp.remoteIP());
@ -273,7 +267,7 @@ void handleNotifications()
for (uint16_t i = 4; i < packetSize -2; i += 3)
{
if (id >= ledCount) break;
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]);
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0);
id++;
}
}

View File

@ -1,6 +1,23 @@
/*
* LED methods
*/
void setValuesFromMainSeg()
{
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
colorFromUint32(seg.colors[0]);
colorFromUint32(seg.colors[1], true);
effectCurrent = seg.mode;
effectSpeed = seg.speed;
effectIntensity = seg.intensity;
effectPalette = seg.palette;
}
void resetTimebase()
{
strip.timebase = 0 - millis();
}
void toggleOnOff()
{
@ -71,6 +88,8 @@ void colorUpdated(int callMode)
{
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification)
// 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa
if (callMode != 0 && callMode != 1 && callMode != 5) strip.applyToAllSelected = true; //if not from JSON api, which directly sets segments
bool fxChanged = strip.setEffectConfig(effectCurrent, effectSpeed, effectIntensity, effectPalette);
if (!colorChanged())
{
@ -82,10 +101,14 @@ void colorUpdated(int callMode)
notify(6);
if (callMode != 8) interfaceUpdateCallMode = 6;
if (realtimeTimeout == UINT32_MAX) realtimeTimeout = 0;
if (isPreset) {isPreset = false;}
else {currentPreset = -1;}
}
return; //no change
}
if (realtimeTimeout == UINT32_MAX) realtimeTimeout = 0;
if (isPreset) {isPreset = false;}
else {currentPreset = -1;}
if (callMode != 5 && nightlightActive && nightlightFade)
{
briNlT = bri;
@ -97,6 +120,7 @@ void colorUpdated(int callMode)
colIT[i] = col[i];
colSecIT[i] = colSec[i];
}
if (briT == 0 && callMode != 3) resetTimebase();
briIT = bri;
if (bri > 0) briLast = bri;

View File

@ -75,7 +75,15 @@ void handleNetworkTime()
void sendNTPPacket()
{
if (!ntpServerIP.fromString(ntpServerName)) //see if server is IP or domain
{
#ifdef ESP8266
WiFi.hostByName(ntpServerName, ntpServerIP, 750);
#else
WiFi.hostByName(ntpServerName, ntpServerIP);
#endif
}
DEBUG_PRINTLN("send NTP");
byte pbuf[NTP_PACKET_SIZE];
memset(pbuf, 0, NTP_PACKET_SIZE);

View File

@ -17,107 +17,6 @@ void initCronixie()
}
void _nixieDisplay(int num[], uint16_t dur[], uint16_t pausedur[], byte cnt)
{
strip.setRange(overlayMin, overlayMax, 0);
if (num[nixieClockI] >= 0 && !nixiePause)
{
strip.setIndividual(num[nixieClockI],((uint32_t)colT[3] << 24)| ((uint32_t)colT[0] << 16) | ((uint32_t)colT[1] << 8) | colT[2]);
strip.unlock(num[nixieClockI]);
}
if (!nixiePause)
{
overlayRefreshMs = dur[nixieClockI];
} else
{
overlayRefreshMs = pausedur[nixieClockI];
}
if (pausedur[nixieClockI] > 0 && !nixiePause)
{
nixiePause = true;
} else {
if (nixieClockI < cnt -1)
{
nixieClockI++;
} else
{
nixieClockI = -1;
}
nixiePause = false;
}
}
void _nixieNumber(int number, int dur)
{
if (nixieClockI < 0)
{
DEBUG_PRINT(number);
int digitCnt = -1;
int digits[4];
digits[3] = number/1000;
digits[2] = (number/100)%10;
digits[1] = (number/10)%10;
digits[0] = number%10;
if (number > 999) //four digits
{
digitCnt = 4;
} else if (number > 99) //three digits
{
digitCnt = 3;
} else if (number > 9) //two digits
{
digitCnt = 2;
} else { //single digit
digitCnt = 1;
}
DEBUG_PRINT(" ");
for (int i = 0; i < digitCnt; i++)
{
DEBUG_PRINT(digits[i]);
overlayArr[digitCnt-1-i] = digits[i];
overlayDur[digitCnt-1-i] = ((dur/4)*3)/digitCnt;
overlayPauseDur[digitCnt-1-i] = 0;
}
DEBUG_PRINTLN(" ");
for (int i = 1; i < digitCnt; i++)
{
if (overlayArr[i] == overlayArr[i-1])
{
overlayPauseDur[i-1] = dur/12;
overlayDur[i-1] = overlayDur[i-1]-dur/12;
}
}
for (int i = digitCnt; i < 6; i++)
{
overlayArr[i] = -1;
overlayDur[i] = 0;
overlayPauseDur[i] = 0;
}
overlayPauseDur[5] = dur/4;
for (int i = 0; i < 6; i++)
{
if (overlayArr[i] != -1)
{
overlayArr[i] = overlayArr[i] + overlayMin;
}
}
for (int i = 0; i <6; i++)
{
DEBUG_PRINT(overlayArr[i]);
DEBUG_PRINT(" ");
DEBUG_PRINT(overlayDur[i]);
DEBUG_PRINT(" ");
DEBUG_PRINT(overlayPauseDur[i]);
DEBUG_PRINT(" ");
}
DEBUG_PRINTLN(" ");
nixieClockI = 0;
} else {
_nixieDisplay(overlayArr, overlayDur, overlayPauseDur, 6);
}
}
void handleOverlays()
{
if (millis() - overlayRefreshedTime > overlayRefreshMs)
@ -129,14 +28,15 @@ void handleOverlays()
{
case 0: break;//no overlay
case 1: _overlayAnalogClock(); break;//2 analog clock
case 2: _overlayNixieClock(); break;//nixie 1-digit
case 2: break;//nixie 1-digit, removed
case 3: _overlayCronixie();//Diamex cronixie clock kit
}
if (!countdownMode || overlayCurrent < 2) checkCountdown(); //countdown macro activation must work
if (!countdownMode || overlayCurrent < 3) checkCountdown(); //countdown macro activation must work
overlayRefreshedTime = millis();
}
}
void _overlayAnalogClock()
{
int overlaySize = overlayMax - overlayMin +1;
@ -182,98 +82,6 @@ void _overlayAnalogClock()
overlayRefreshMs = 998;
}
void _overlayNixieClock()
{
#ifdef WLED_DISABLE_CRONIXIE
if (countdownMode) checkCountdown();
#else
if (countdownMode)
{
_overlayNixieCountdown(); return;
}
if (nixieClockI < 0)
{
overlayArr[0] = hour(local);
if (useAMPM) overlayArr[0] = overlayArr[0]%12;
overlayArr[1] = -1;
if (overlayArr[0] > 9)
{
overlayArr[1] = overlayArr[0]%10;
overlayArr[0] = overlayArr[0]/10;
}
overlayArr[2] = minute(local);
overlayArr[3] = overlayArr[2]%10;
overlayArr[2] = overlayArr[2]/10;
overlayArr[4] = -1;
overlayArr[5] = -1;
if (analogClockSecondsTrail)
{
overlayArr[4] = second(local);
overlayArr[5] = overlayArr[4]%10;
overlayArr[4] = overlayArr[4]/10;
}
for (int i = 0; i < 6; i++)
{
if (overlayArr[i] != -1)
{
overlayArr[i] = overlayArr[i] + overlayMin;
}
}
overlayDur[0] = 12 + 12*(255 - overlaySpeed);
if (overlayArr[1] == overlayArr[0])
{
overlayPauseDur[0] = 3 + 3*(255 - overlaySpeed);
} else
{
overlayPauseDur[0] = 0;
}
if (overlayArr[1] == -1)
{
overlayDur[1] = 0;
} else
{
overlayDur[1] = 12 + 12*(255 - overlaySpeed);
}
overlayPauseDur[1] = 9 + 9*(255 - overlaySpeed);
overlayDur[2] = 12 + 12*(255 - overlaySpeed);
if (overlayArr[2] == overlayArr[3])
{
overlayPauseDur[2] = 3 + 3*(255 - overlaySpeed);
} else
{
overlayPauseDur[2] = 0;
}
overlayDur[3] = 12 + 12*(255 - overlaySpeed);
overlayPauseDur[3] = 9 + 9*(255 - overlaySpeed);
if (overlayArr[4] == -1)
{
overlayDur[4] = 0;
overlayPauseDur[4] = 0;
overlayDur[5] = 0;
} else
{
overlayDur[4] = 12 + 12*(255 - overlaySpeed);
if (overlayArr[5] == overlayArr[4])
{
overlayPauseDur[4] = 3 + 3*(255 - overlaySpeed);
} else
{
overlayPauseDur[4] = 0;
}
overlayDur[5] = 12 + 12*(255 - overlaySpeed);
}
overlayPauseDur[5] = 22 + 22*(255 - overlaySpeed);
nixieClockI = 0;
} else
{
_nixieDisplay(overlayArr, overlayDur, overlayPauseDur, 6);
}
#endif
}
void _overlayAnalogCountdown()
{
@ -319,33 +127,3 @@ void _overlayAnalogCountdown()
}
overlayRefreshMs = 998;
}
void _overlayNixieCountdown()
{
if (now() >= countdownTime)
{
if (checkCountdown())
{
_nixieNumber(2019, 2019);
}
} else
{
long diff = countdownTime - now();
if (diff > 86313600L) //display in years if more than 999 days
{
diff = diff/31557600L;
} else if (diff > 3596400) //display in days if more than 999 hours
{
diff = diff/86400;
} else if (diff > 59940) //display in hours if more than 999 minutes
{
diff = diff/1440;
} else if (diff > 999) //display in minutes if more than 999 seconds
{
diff = diff/60;
}
_nixieNumber(diff, 800);
}
overlayRefreshMs = 998;
}

View File

@ -2,13 +2,20 @@
* Color conversion methods
*/
void colorFromUint32(uint32_t in)
void colorFromUint32(uint32_t in, bool secondary)
{
if (secondary) {
colSec[3] = in >> 24 & 0xFF;
colSec[0] = in >> 16 & 0xFF;
colSec[1] = in >> 8 & 0xFF;
colSec[2] = in & 0xFF;
} else {
col[3] = in >> 24 & 0xFF;
col[0] = in >> 16 & 0xFF;
col[1] = in >> 8 & 0xFF;
col[2] = in & 0xFF;
}
}
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) //hue, sat to rgb
{

View File

@ -2,6 +2,8 @@
* MQTT communication protocol for home automation
*/
#ifdef WLED_ENABLE_MQTT
void parseMQTTBriPayload(char* payload)
{
if (strstr(payload, "ON") || strstr(payload, "on") || strstr(payload, "true")) {bri = briLast; colorUpdated(1);}
@ -19,7 +21,6 @@ void onMqttConnect(bool sessionPresent)
{
//(re)subscribe to required topics
char subuf[38];
strcpy(subuf, mqttDeviceTopic);
if (mqttDeviceTopic[0] != 0)
{
@ -43,7 +44,6 @@ void onMqttConnect(bool sessionPresent)
mqtt->subscribe(subuf, 0);
}
doSendHADiscovery = true;
doPublishMqtt = true;
DEBUG_PRINTLN("MQTT ready");
}
@ -84,147 +84,29 @@ void publishMqtt()
strcat(subuf, "/g");
mqtt->publish(subuf, 0, true, s);
sprintf(s, "#%06X", col[3]*16777216 + col[0]*65536 + col[1]*256 + col[2]);
sprintf(s, "#%06X", (col[3] << 24) | (col[0] << 16) | (col[1] << 8) | (col[2]));
strcpy(subuf, mqttDeviceTopic);
strcat(subuf, "/c");
mqtt->publish(subuf, 0, true, s);
strcpy(subuf, mqttDeviceTopic);
strcat(subuf, "/status");
mqtt->publish(subuf, 0, true, "online");
char apires[1024];
XML_response(nullptr, false, apires);
XML_response(nullptr, apires);
strcpy(subuf, mqttDeviceTopic);
strcat(subuf, "/v");
mqtt->publish(subuf, 0, true, apires);
}
const char HA_static_JSON[] PROGMEM = R"=====(,"bri_val_tpl":"{{value}}","rgb_cmd_tpl":"{{'#%02x%02x%02x' | format(red, green, blue)}}","rgb_val_tpl":"{{value[1:3]|int(base=16)}},{{value[3:5]|int(base=16)}},{{value[5:7]|int(base=16)}}","qos":0,"opt":true,"pl_on":"ON","pl_off":"OFF","fx_val_tpl":"{{value}}","fx_list":[)=====";
char* buffer;
void sendHADiscoveryMQTT()
{
//TODO: With LwIP 1 the ESP loses MQTT connection and causes memory leak when sending discovery packet
#if ARDUINO_ARCH_ESP32 || LWIP_VERSION_MAJOR > 1
/*
YYYY is device topic
XXXX is device name
Send out HA MQTT Discovery message on MQTT connect (~2.4kB):
{
"name": "XXXX",
"stat_t":"YYYY/c",
"cmd_t":"YYYY",
"rgb_stat_t":"YYYY/c",
"rgb_cmd_t":"YYYY/col",
"bri_cmd_t":"YYYY",
"bri_stat_t":"YYYY/g",
"bri_val_tpl":"{{value}}",
"rgb_cmd_tpl":"{{'#%02x%02x%02x' | format(red, green, blue)}}",
"rgb_val_tpl":"{{value[1:3]|int(base=16)}},{{value[3:5]|int(base=16)}},{{value[5:7]|int(base=16)}}",
"qos": 0,
"opt":true,
"pl_on": "ON",
"pl_off": "OFF",
"fx_cmd_t":"YYYY/api",
"fx_stat_t":"YYYY/api",
"fx_val_tpl":"{{value}}",
"fx_list":[
"[FX=00] Solid",
"[FX=01] Blink",
"[FX=02] ...",
"[FX=79] Ripple"
]
}
*/
doSendHADiscovery = false;
if (mqtt == nullptr || !mqtt->connected()) return;
buffer = new char[2400];
if (!buffer) {delete[] buffer; return;}
char bufc[36], bufcol[38], bufg[36], bufapi[38];
strcpy(bufc, mqttDeviceTopic);
strcpy(bufcol, mqttDeviceTopic);
strcpy(bufg, mqttDeviceTopic);
strcpy(bufapi, mqttDeviceTopic);
strcat(bufc, "/c");
strcat(bufcol, "/col");
strcat(bufg, "/g");
strcat(bufapi, "/api");
StaticJsonDocument<JSON_OBJECT_SIZE(9) +512> root;
root["name"] = serverDescription;
root["stat_t"] = bufc;
root["cmd_t"] = mqttDeviceTopic;
root["rgb_stat_t"] = bufc;
root["rgb_cmd_t"] = bufcol;
root["bri_cmd_t"] = mqttDeviceTopic;
root["bri_stat_t"] = bufg;
root["fx_cmd_t"] = bufapi;
root["fx_stat_t"] = bufapi;
size_t jlen = measureJson(root);
//DEBUG_PRINTLN(jlen);
serializeJson(root, buffer, jlen);
//add values which don't change
strcpy_P(buffer + jlen -1, HA_static_JSON);
olen = 0;
obuf = buffer + jlen -1 + strlen_P(HA_static_JSON);
//add fx_list
uint16_t jmnlen = strlen_P(JSON_mode_names);
uint16_t nameStart = 0, nameEnd = 0;
int i = 0;
bool isNameStart = true;
for (uint16_t j = 0; j < jmnlen; j++)
{
if (pgm_read_byte(JSON_mode_names + j) == '\"' || j == jmnlen -1)
{
if (isNameStart)
{
nameStart = j +1;
}
else
{
nameEnd = j;
char mdnfx[64], mdn[56];
uint16_t namelen = nameEnd - nameStart;
strncpy_P(mdn, JSON_mode_names + nameStart, namelen);
mdn[namelen] = 0;
snprintf(mdnfx, 64, "\"[FX=%02d] %s\",", i, mdn);
oappend(mdnfx);
//DEBUG_PRINTLN(mdnfx);
i++;
}
isNameStart = !isNameStart;
}
}
olen--;
oappend("]}");
DEBUG_PRINT("HA Discovery Sending >>");
DEBUG_PRINTLN(buffer);
char pubt[25 + 12 + 8];
strcpy(pubt, "homeassistant/light/");
strcat(pubt, mqttClientID);
strcat(pubt, "/config");
bool success = mqtt->publish(pubt, 0, true, buffer);
DEBUG_PRINTLN(success);
yield();
delete[] buffer;
#endif
}
//HA autodiscovery was removed in favor of the native integration in HA v0.102.0
bool initMqtt()
{
lastMqttReconnectAttempt = millis();
if (mqttServer[0] == 0 || !WLED_CONNECTED) return false;
if (!mqttEnabled || mqttServer[0] == 0 || !WLED_CONNECTED) return false;
if (mqtt == nullptr) {
mqtt = new AsyncMqttClient();
@ -243,6 +125,15 @@ bool initMqtt()
}
mqtt->setClientId(mqttClientID);
if (mqttUser[0] && mqttPass[0]) mqtt->setCredentials(mqttUser, mqttPass);
strcpy(mqttStatusTopic, mqttDeviceTopic);
strcat(mqttStatusTopic, "/status");
mqtt->setWill(mqttStatusTopic, 0, true, "offline");
mqtt->connect();
return true;
}
#else
bool initMqtt(){return false;}
void publishMqtt(){}
#endif

View File

@ -37,6 +37,10 @@ void initServer()
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Methods", "*");
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "*");
server.on("/liveview", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/html", PAGE_liveview);
});
//settings page
server.on("/settings", HTTP_GET, [](AsyncWebServerRequest *request){
serveSettings(request);
@ -64,7 +68,7 @@ void initServer()
server.on("/settings/wifi", HTTP_POST, [](AsyncWebServerRequest *request){
if (!(wifiLock && otaLock)) handleSettingsSet(request, 1);
serveMessage(request, 200,"WiFi settings saved.","Reconnecting now...",255);
serveMessage(request, 200,"WiFi settings saved.","Please connect to the new IP (if changed)",129);
forceReconnect = true;
});
@ -75,7 +79,7 @@ void initServer()
server.on("/settings/ui", HTTP_POST, [](AsyncWebServerRequest *request){
handleSettingsSet(request, 3);
serveMessage(request, 200,"UI settings saved.","Reloading to apply theme...",122);
serveMessage(request, 200,"UI settings saved.","Redirecting...",1);
});
server.on("/settings/sync", HTTP_POST, [](AsyncWebServerRequest *request){
@ -90,7 +94,7 @@ void initServer()
server.on("/settings/sec", HTTP_POST, [](AsyncWebServerRequest *request){
handleSettingsSet(request, 6);
if (!doReboot) serveMessage(request, 200,"Security settings saved.","Rebooting now, please wait ~10 seconds...",129);
if (!doReboot) serveMessage(request, 200,"Security settings saved.","Rebooting, please wait ~10 seconds...",129);
doReboot = true;
});
@ -98,9 +102,20 @@ void initServer()
serveJson(request);
});
AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/json", [](AsyncWebServerRequest *request, JsonObject root) {
if (root.isNull()){request->send(500, "application/json", "{\"error\":\"Parsing failed\"}"); return;}
if (deserializeState(root)) { serveJson(request); return; } //if JSON contains "v" (verbose response)
AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/json", [](AsyncWebServerRequest *request) {
bool verboseResponse = false;
if (1) { //scope JsonDocument so it releases its buffer
DynamicJsonDocument jsonBuffer(8192);
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject));
JsonObject root = jsonBuffer.as<JsonObject>();
if (error || root.isNull()) {
request->send(400, "application/json", "{\"error\":10}"); return;
}
verboseResponse = deserializeState(root);
}
if (verboseResponse) { //if JSON contains "v"
serveJson(request); return;
}
request->send(200, "application/json", "{\"success\":true}");
});
server.addHandler(handler);
@ -143,9 +158,7 @@ void initServer()
//init ota page
#ifndef WLED_DISABLE_OTA
server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){
olen = 0;
getCSSColors();
request->send_P(200, "text/html", PAGE_update, msgProcessor);
request->send_P(200, "text/html", PAGE_update);
});
server.on("/update", HTTP_POST, [](AsyncWebServerRequest *request){
@ -158,7 +171,7 @@ void initServer()
},[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){
if(!index){
DEBUG_PRINTLN("OTA Update Start");
#ifndef ARDUINO_ARCH_ESP32
#ifdef ESP8266
Update.runAsync(true);
#endif
Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000);
@ -227,47 +240,15 @@ void serveIndexOrWelcome(AsyncWebServerRequest *request)
}
void getCSSColors()
{
char cs[6][9];
getThemeColors(cs);
oappend("<style>:root{--aCol:#"); oappend(cs[0]);
oappend(";--bCol:#"); oappend(cs[1]);
oappend(";--cCol:#"); oappend(cs[2]);
oappend(";--dCol:#"); oappend(cs[3]);
oappend(";--sCol:#"); oappend(cs[4]);
oappend(";--tCol:#"); oappend(cs[5]);
oappend(";--cFn:"); oappend(cssFont);
oappend(";}");
}
void serveIndex(AsyncWebServerRequest* request)
{
bool serveMobile = false;
if (uiConfiguration == 0 && request->hasHeader("User-Agent")) serveMobile = checkClientIsMobile(request->getHeader("User-Agent")->value());
else if (uiConfiguration == 2) serveMobile = true;
#ifdef WLED_ENABLE_FS_SERVING
if (serveMobile)
{
if (handleFileRead(request, "/index_mobile.htm")) return;
} else
{
if (handleFileRead(request, "/index.htm")) return;
}
#endif
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html",
(serveMobile) ? (uint8_t*)PAGE_indexM : PAGE_index,
(serveMobile) ? PAGE_indexM_L : PAGE_index_L);
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_index, PAGE_index_L);
//error message is not gzipped
#ifdef WLED_DISABLE_MOBILE_UI
if (!serveMobile) response->addHeader("Content-Encoding","gzip");
#else
response->addHeader("Content-Encoding","gzip");
#endif
request->send(response);
}
@ -275,13 +256,6 @@ void serveIndex(AsyncWebServerRequest* request)
String msgProcessor(const String& var)
{
if (var == "CSS") {
char css[512];
obuf = css;
olen = 0;
getCSSColors();
return String(obuf);
}
if (var == "MSG") {
String messageBody = messageHead;
messageBody += "</h2>";
@ -312,12 +286,6 @@ String msgProcessor(const String& var)
void serveMessage(AsyncWebServerRequest* request, uint16_t code, String headl, String subl="", byte optionT=255)
{
#ifndef ARDUINO_ARCH_ESP32
char buf[256];
obuf = buf;
#endif
olen = 0;
getCSSColors();
messageHead = headl;
messageSub = subl;
optionType = optionT;
@ -331,7 +299,6 @@ String settingsProcessor(const String& var)
if (var == "CSS") {
char buf[2048];
getSettingsJS(optionType, buf);
getCSSColors();
return String(buf);
}
if (var == "SCSS") return String(FPSTR(PAGE_settingsCss));
@ -373,6 +340,6 @@ void serveSettings(AsyncWebServerRequest* request)
case 5: request->send_P(200, "text/html", PAGE_settings_time, settingsProcessor); break;
case 6: request->send_P(200, "text/html", PAGE_settings_sec , settingsProcessor); break;
case 255: request->send_P(200, "text/html", PAGE_welcome); break;
default: request->send_P(200, "text/html", PAGE_settings , settingsProcessor);
default: request->send_P(200, "text/html", PAGE_settings);
}
}

View File

@ -2,50 +2,7 @@
* JSON API (De)serialization
*/
bool deserializeState(JsonObject root)
{
bool stateResponse = root["v"] | false;
bri = root["bri"] | bri;
bool on = root["on"] | (bri > 0);
if (!on != !bri) toggleOnOff();
if (root.containsKey("transition"))
{
transitionDelay = root["transition"];
transitionDelay *= 100;
}
if (root.containsKey("tt"))
{
transitionDelayTemp = root["tt"];
transitionDelayTemp *= 100;
jsonTransitionOnce = true;
}
int ps = root["ps"] | -1;
if (ps >= 0) applyPreset(ps);
int cy = root["pl"] | -1;
presetCyclingEnabled = (cy >= 0);
JsonObject nl = root["nl"];
nightlightActive = nl["on"] | nightlightActive;
nightlightDelayMins = nl["dur"] | nightlightDelayMins;
nightlightFade = nl["fade"] | nightlightFade;
nightlightTargetBri = nl["tbri"] | nightlightTargetBri;
JsonObject udpn = root["udpn"];
notifyDirect = udpn["send"] | notifyDirect;
receiveNotifications = udpn["recv"] | receiveNotifications;
bool noNotification = udpn["nn"]; //send no notification just for this request
int timein = root["time"] | -1;
if (timein != -1) setTime(timein);
int it = 0;
JsonArray segs = root["seg"];
for (JsonObject elem : segs)
void deserializeSegment(JsonObject elem, byte it)
{
byte id = elem["id"] | it;
if (id < strip.getMaxSegments())
@ -74,7 +31,7 @@ bool deserializeState(JsonObject root)
byte cp = copyArray(colX, rgbw);
seg.colors[i] = ((rgbw[3] << 24) | ((rgbw[0]&0xFF) << 16) | ((rgbw[1]&0xFF) << 8) | ((rgbw[2]&0xFF)));
if (cp == 1 && rgbw[0] == 0) seg.colors[i] = 0;
if (id == 0) //temporary
if (id == strip.getMainSegmentId()) //temporary
{
if (i == 0) {col[0] = rgbw[0]; col[1] = rgbw[1]; col[2] = rgbw[2]; col[3] = rgbw[3];}
if (i == 1) {colSec[0] = rgbw[0]; colSec[1] = rgbw[1]; colSec[2] = rgbw[2]; colSec[3] = rgbw[3];}
@ -88,7 +45,7 @@ bool deserializeState(JsonObject root)
seg.setOption(1, elem["rev"] | seg.getOption(1)); //reverse
//int cln = seg_0["cln"];
//temporary, strip object gets updated via colorUpdated()
if (id == 0) {
if (id == strip.getMainSegmentId()) {
effectCurrent = elem["fx"] | effectCurrent;
effectSpeed = elem["sx"] | effectSpeed;
effectIntensity = elem["ix"] | effectIntensity;
@ -101,10 +58,105 @@ bool deserializeState(JsonObject root)
seg.palette = elem["pal"] | seg.palette;
}
}
}
bool deserializeState(JsonObject root)
{
strip.applyToAllSelected = false;
bool stateResponse = root["v"] | false;
bri = root["bri"] | bri;
bool on = root["on"] | (bri > 0);
if (!on != !bri) toggleOnOff();
int tr = root["transition"] | -1;
if (tr >= 0)
{
transitionDelay = tr;
transitionDelay *= 100;
}
tr = root["tt"] | -1;
if (tr >= 0)
{
transitionDelayTemp = tr;
transitionDelayTemp *= 100;
jsonTransitionOnce = true;
}
int ps = root["ps"] | -1;
if (ps >= 0) applyPreset(ps);
int cy = root["pl"] | -2;
if (cy > -2) presetCyclingEnabled = (cy >= 0);
JsonObject ccnf = root["ccnf"];
presetCycleMin = ccnf["min"] | presetCycleMin;
presetCycleMax = ccnf["max"] | presetCycleMax;
tr = ccnf["time"] | -1;
if (tr >= 2)
{
presetCycleTime = tr;
presetCycleTime *= 100;
}
JsonObject nl = root["nl"];
nightlightActive = nl["on"] | nightlightActive;
nightlightDelayMins = nl["dur"] | nightlightDelayMins;
nightlightFade = nl["fade"] | nightlightFade;
nightlightTargetBri = nl["tbri"] | nightlightTargetBri;
JsonObject udpn = root["udpn"];
notifyDirect = udpn["send"] | notifyDirect;
receiveNotifications = udpn["recv"] | receiveNotifications;
bool noNotification = udpn["nn"]; //send no notification just for this request
int timein = root["time"] | -1;
if (timein != -1) setTime(timein);
byte prevMain = strip.getMainSegmentId();
strip.mainSegment = root["mainseg"] | prevMain;
if (strip.getMainSegmentId() != prevMain) setValuesFromMainSeg();
int it = 0;
JsonVariant segVar = root["seg"];
if (segVar.is<JsonObject>())
{
int id = segVar["id"] | -1;
if (id < 0) { //set all selected segments
bool didSet = false;
byte lowestActive = 99;
for (byte s = 0; s < strip.getMaxSegments(); s++)
{
WS2812FX::Segment sg = strip.getSegment(s);
if (sg.isActive())
{
if (lowestActive == 99) lowestActive = s;
if (sg.isSelected()) {
deserializeSegment(segVar, s);
didSet = true;
}
}
}
if (!didSet && lowestActive < strip.getMaxSegments()) deserializeSegment(segVar, lowestActive);
} else { //set only the segment with the specified ID
deserializeSegment(segVar, it);
}
} else {
JsonArray segs = segVar.as<JsonArray>();
for (JsonObject elem : segs)
{
deserializeSegment(elem, it);
it++;
}
}
colorUpdated(noNotification ? 5:1);
ps = root["psave"] | -1;
if (ps >= 0) savePreset(ps);
return stateResponse;
}
@ -133,19 +185,27 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id)
root["pal"] = seg.palette;
root["sel"] = seg.isSelected();
root["rev"] = seg.getOption(1);
root["cln"] = -1;
}
void serializeState(JsonObject root)
{
if (errorFlag) root["error"] = errorFlag;
root["on"] = (bri > 0);
root["bri"] = briLast;
root["transition"] = transitionDelay/100; //in 100ms
root["ps"] = -1; //
root["ps"] = currentPreset;
root["pss"] = savedPresets;
root["pl"] = (presetCyclingEnabled) ? 0: -1;
//temporary for preser cycle
JsonObject ccnf = root.createNestedObject("ccnf");
ccnf["min"] = presetCycleMin;
ccnf["max"] = presetCycleMax;
ccnf["time"] = presetCycleTime/100;
JsonObject nl = root.createNestedObject("nl");
nl["on"] = nightlightActive;
nl["dur"] = nightlightDelayMins;
@ -156,6 +216,8 @@ void serializeState(JsonObject root)
udpn["send"] = notifyDirect;
udpn["recv"] = receiveNotifications;
root["mainseg"] = strip.getMainSegmentId();
JsonArray seg = root.createNestedArray("seg");
for (byte s = 0; s < strip.getMaxSegments(); s++)
{
@ -176,12 +238,16 @@ void serializeInfo(JsonObject root)
JsonObject leds = root.createNestedObject("leds");
leds["count"] = ledCount;
leds["rgbw"] = useRGBW;
leds["wv"] = useRGBW && !autoRGBtoRGBW; //should a white channel slider be displayed?
JsonArray leds_pin = leds.createNestedArray("pin");
leds_pin.add(LEDPIN);
leds["pwr"] = strip.currentMilliamps;
leds["maxpwr"] = strip.ablMilliampsMax;
leds["maxseg"] = strip.getMaxSegments();
leds["seglock"] = false; //will be used in the future to prevent modifications to segment config
root["str"] = syncToggleReceive;
root["name"] = serverDescription;
root["udpport"] = udpPort;
@ -228,7 +294,7 @@ void serializeInfo(JsonObject root)
#ifndef WLED_DISABLE_HUESYNC
os += 0x04;
#endif
#ifndef WLED_DISABLE_MOBILE_UI
#ifdef WLED_ENABLE_ADALIGHT
os += 0x02;
#endif
#ifndef WLED_DISABLE_OTA
@ -248,6 +314,10 @@ void serveJson(AsyncWebServerRequest* request)
const String& url = request->url();
if (url.indexOf("state") > 0) subJson = 1;
else if (url.indexOf("info") > 0) subJson = 2;
else if (url.indexOf("live") > 0) {
serveLiveLeds(request);
return;
}
else if (url.indexOf("eff") > 0) {
request->send_P(200, "application/json", JSON_mode_names);
return;
@ -282,3 +352,24 @@ void serveJson(AsyncWebServerRequest* request)
response->setLength();
request->send(response);
}
#define MAX_LIVE_LEDS 180
void serveLiveLeds(AsyncWebServerRequest* request)
{
byte used = strip.getUsableCount();
byte n = (used -1) /MAX_LIVE_LEDS +1; //only serve every n'th LED if count over MAX_LIVE_LEDS
char buffer[2000] = "{\"leds\":[";
olen = 9;
obuf = buffer;
for (uint16_t i= 0; i < used; i += n)
{
olen += sprintf(buffer + olen, "\"%06X\",", strip.getPixelColor(i));
}
olen -= 1;
oappend("],\"n\":");
oappendi(n);
oappend("}");
request->send(200, "application/json", buffer);
}