Merge branch 'master' into dev

This commit is contained in:
Blaz Kristan 2021-06-15 23:36:12 +02:00
commit 1a80439825
58 changed files with 2731 additions and 1524 deletions

2
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,2 @@
github: [Aircoookie]
custom: ['https://paypal.me/Aircoookie']

View File

@ -2,6 +2,36 @@
### Builds after release 0.12.0 ### Builds after release 0.12.0
#### Build 2106140
- Updated main logo
- Reduced flash usage by 0.8kB by using 8-bit instead of 32-bit PNGs for welcome and 404 pages
- Added a check to stop Alexa reporting an error if state set by macro differs from the expected state
#### Build 2106100
- Added support for multiple buttons with various types (PR #1977)
- Fixed infinite playlists (PR #2020)
- Added `r` to playlist object, allows for shuffle regardless of the `repeat` value
- Improved accuracy of NTP time sync
- Added possibility for WLED UDP sync to sync system time
- Improved UDP sync accuracy, if both sender and receiver are NTP synced
- Fixed a cache issue with restored tabs
- Cache CORS request
- Disable WiFi sleep by default on ESP32
#### Build 2105230
- No longer retain MQTT `/v` topic to alleviate storage loads on MQTT broker
- Fixed Sunrise calculation (atan_t approx. used outside of value range)
#### Build 2105200
- Fixed WS281x output on ESP32
- Fixed potential out-of-bounds write in MQTT
- Fixed IR pin not changeable if IR disabled
- Fixed XML API <wv> containing -1 on Manual only RGBW mode (see #888, #1783)
#### Build 2105171 #### Build 2105171
- Always copy MQTT payloads to prevent non-0-terminated strings - Always copy MQTT payloads to prevent non-0-terminated strings

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 602 B

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "wled", "name": "wled",
"version": "0.12.0-b3", "version": "0.12.2-bl4",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

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

View File

@ -15,6 +15,7 @@
default_envs = nodemcuv2, esp01_1m_full, esp32dev, esp32_eth default_envs = nodemcuv2, esp01_1m_full, esp32dev, esp32_eth
# Single binaries (uncomment your board) # Single binaries (uncomment your board)
; default_envs = elekstube_ips
; default_envs = nodemcuv2 ; default_envs = nodemcuv2
; default_envs = esp01_1m_full ; default_envs = esp01_1m_full
; default_envs = esp07 ; default_envs = esp07
@ -47,7 +48,8 @@ extra_configs =
# arduino core 2.7.0 = platformIO 2.5.0 # arduino core 2.7.0 = platformIO 2.5.0
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
arduino_core_2_6_3 = espressif8266@2.3.3 arduino_core_2_6_3 = espressif8266@2.3.3
arduino_core_2_7_4 = espressif8266@^2.6.2 arduino_core_2_7_4 = espressif8266@2.6.2
arduino_core_3_0_0 = espressif8266@3.0.0
# Development platforms # Development platforms
arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop
@ -131,6 +133,8 @@ ldscript_4m1m = eagle.flash.4m1m.ld
build_flags = build_flags =
-DESP8266 -DESP8266
-DFP_IN_IROM -DFP_IN_IROM
;-Wno-deprecated-declarations
;-Wno-register
; NONOSDK22x_190703 = 2.2.2-dev(38a443e) ; NONOSDK22x_190703 = 2.2.2-dev(38a443e)
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703 -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
; lwIP 2 - Higher Bandwidth no Features ; lwIP 2 - Higher Bandwidth no Features
@ -418,3 +422,37 @@ platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m} board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} build_flags = ${common.build_flags_esp8266}
# ------------------------------------------------------------------------------
# EleksTube-IPS
# ------------------------------------------------------------------------------
[env:elekstube_ips]
board = esp32dev
platform = espressif32@3.2
upload_speed = 921600
lib_deps = ${env.lib_deps}
TFT_eSPI
build_flags = ${common.build_flags_esp32} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED
-D USERMOD_RTC
-D USERMOD_ELEKSTUBE_IPS
-D LEDPIN=12
-D RLYPIN=27
-D BTNPIN=34
-D WLED_DISABLE_INFRARED
-D DEFAULT_LED_COUNT=6
# Display config
-D ST7789_DRIVER
-D TFT_WIDTH=135
-D TFT_HEIGHT=240
-D CGRAM_OFFSET
-D TFT_SDA_READ
-D TFT_MOSI=23
-D TFT_SCLK=18
-D TFT_DC=25
-D TFT_RST=26
-D SPI_FREQUENCY=40000000
-D USER_SETUP_LOADED
monitor_filters = esp32_exception_decoder
lib_ignore =
ESPAsyncTCP
ESPAsyncUDP

View File

@ -37,6 +37,7 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control
- MQTT - MQTT
- Blynk IoT - Blynk IoT
- E1.31, Art-Net, DDP and TPM2.net - E1.31, Art-Net, DDP and TPM2.net
- [diyHue](https://github.com/diyhue/diyHue) (Wled is supported by diyHue, including Hue Sync Entertainment under udp. Thanks to [Gregory Mallios](https://github.com/gmallios))
- [Hyperion](https://github.com/hyperion-project/hyperion.ng) - [Hyperion](https://github.com/hyperion-project/hyperion.ng)
- UDP realtime - UDP realtime
- Alexa voice control (including dimming and color) - Alexa voice control (including dimming and color)

View File

@ -44,7 +44,7 @@ starlette==0.14.2
# via platformio # via platformio
tabulate==0.8.9 tabulate==0.8.9
# via platformio # via platformio
urllib3==1.26.4 urllib3==1.26.5
# via requests # via requests
uvicorn==0.13.4 uvicorn==0.13.4
# via platformio # via platformio

View File

@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x17B000,
app1, app, ota_1, 0x18B000,0x17B000,
spiffs, data, spiffs, 0x306000,0x0FA000,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 0x10000 0x17B000
5 app1 app ota_1 0x18B000 0x17B000
6 spiffs data spiffs 0x306000 0x0FA000

View File

@ -0,0 +1,70 @@
#ifndef CHIP_SELECT_H
#define CHIP_SELECT_H
#include "Hardware.h"
/*
* `digit`s are as defined in Hardware.h, 0 == seconds ones, 5 == hours tens.
*/
class ChipSelect {
private:
uint8_t digits_map;
const uint8_t all_on = 0x3F;
const uint8_t all_off = 0x00;
public:
ChipSelect() : digits_map(all_off) {}
void update() {
// Documented in README.md. Q7 and Q6 are unused. Q5 is Seconds Ones, Q0 is Hours Tens.
// Q7 is the first bit written, Q0 is the last. So we push two dummy bits, then start with
// Seconds Ones and end with Hours Tens.
// CS is Active Low, but digits_map is 1 for enable, 0 for disable. So we bit-wise NOT first.
uint8_t to_shift = (~digits_map) << 2;
digitalWrite(CSSR_LATCH_PIN, LOW);
shiftOut(CSSR_DATA_PIN, CSSR_CLOCK_PIN, LSBFIRST, to_shift);
digitalWrite(CSSR_LATCH_PIN, HIGH);
}
void begin()
{
pinMode(CSSR_LATCH_PIN, OUTPUT);
pinMode(CSSR_DATA_PIN, OUTPUT);
pinMode(CSSR_CLOCK_PIN, OUTPUT);
digitalWrite(CSSR_DATA_PIN, LOW);
digitalWrite(CSSR_CLOCK_PIN, LOW);
digitalWrite(CSSR_LATCH_PIN, LOW);
update();
}
// These speak the indexes defined in Hardware.h.
// So 0 is disabled, 1 is enabled (even though CS is active low, this gets mapped.)
// So bit 0 (LSB), is index 0, is SECONDS_ONES
// Translation to what the 74HC595 uses is done in update()
void setDigitMap(uint8_t map, bool update_=true) { digits_map = map; if (update_) update(); }
uint8_t getDigitMap() { return digits_map; }
// Helper functions
// Sets just the one digit by digit number
void setDigit(uint8_t digit, bool update_=true) { setDigitMap(0x01 << digit, update_); }
void setAll(bool update_=true) { setDigitMap(all_on, update_); }
void clear(bool update_=true) { setDigitMap(all_off, update_); }
void setSecondsOnes() { setDigit(SECONDS_ONES); }
void setSecondsTens() { setDigit(SECONDS_TENS); }
void setMinutesOnes() { setDigit(MINUTES_ONES); }
void setMinutesTens() { setDigit(MINUTES_TENS); }
void setHoursOnes() { setDigit(HOURS_ONES); }
void setHoursTens() { setDigit(HOURS_TENS); }
bool isSecondsOnes() { return (digits_map&SECONDS_ONES_MAP > 0); }
bool isSecondsTens() { return (digits_map&SECONDS_TENS_MAP > 0); }
bool isMinutesOnes() { return (digits_map&MINUTES_ONES_MAP > 0); }
bool isMinutesTens() { return (digits_map&MINUTES_TENS_MAP > 0); }
bool isHoursOnes() { return (digits_map&HOURS_ONES_MAP > 0); }
bool isHoursTens() { return (digits_map&HOURS_TENS_MAP > 0); }
};
#endif // CHIP_SELECT_H

View File

@ -0,0 +1,52 @@
/*
* Define the hardware for the EleksTube IPS clock. Mostly pin definitions
*/
#ifndef ELEKSTUBEHAX_HARDWARE_H
#define ELEKSTUBEHAX_HARDWARE_H
#include <stdint.h>
#include <Arduino.h> // for HIGH and LOW
// Common indexing scheme, used to identify the digit
#define SECONDS_ONES (0)
#define SECONDS_TENS (1)
#define MINUTES_ONES (2)
#define MINUTES_TENS (3)
#define HOURS_ONES (4)
#define HOURS_TENS (5)
#define NUM_DIGITS (6)
#define SECONDS_ONES_MAP (0x01 << SECONDS_ONES)
#define SECONDS_TENS_MAP (0x01 << SECONDS_TENS)
#define MINUTES_ONES_MAP (0x01 << MINUTES_ONES)
#define MINUTES_TENS_MAP (0x01 << MINUTES_TENS)
#define HOURS_ONES_MAP (0x01 << HOURS_ONES)
#define HOURS_TENS_MAP (0x01 << HOURS_TENS)
// WS2812 (or compatible) LEDs on the back of the display modules.
#define BACKLIGHTS_PIN (12)
// Buttons, active low, externally pulled up (with actual resistors!)
#define BUTTON_LEFT_PIN (33)
#define BUTTON_MODE_PIN (32)
#define BUTTON_RIGHT_PIN (35)
#define BUTTON_POWER_PIN (34)
// I2C to DS3231 RTC.
#define RTC_SCL_PIN (22)
#define RTC_SDA_PIN (21)
// Chip Select shift register, to select the display
#define CSSR_DATA_PIN (14)
#define CSSR_CLOCK_PIN (16)
#define CSSR_LATCH_PIN (17)
// SPI to displays
// DEFINED IN User_Setup.h
// Look for: TFT_MOSI, TFT_SCLK, TFT_CS, TFT_DC, and TFT_RST
// Power for all TFT displays are grounded through a MOSFET so they can all be turned off.
// Active HIGH.
#define TFT_ENABLE_PIN (27)
#endif // ELEKSTUBEHAX_HARDWARE_H

View File

@ -0,0 +1,218 @@
#ifndef TFTS_H
#define TFTS_H
#include "wled.h"
#include <FS.h>
#include <TFT_eSPI.h>
#include "Hardware.h"
#include "ChipSelect.h"
class TFTs : public TFT_eSPI {
private:
uint8_t digits[NUM_DIGITS];
// These read 16- and 32-bit types from the SD card file.
// BMP data is stored little-endian, Arduino is little-endian too.
// May need to reverse subscript order if porting elsewhere.
uint16_t read16(fs::File &f) {
uint16_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read(); // MSB
return result;
}
uint32_t read32(fs::File &f) {
uint32_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read();
((uint8_t *)&result)[2] = f.read();
((uint8_t *)&result)[3] = f.read(); // MSB
return result;
}
uint16_t output_buffer[TFT_HEIGHT][TFT_WIDTH];
// These BMP functions are stolen directly from the TFT_SPIFFS_BMP example in the TFT_eSPI library.
// Unfortunately, they aren't part of the library itself, so I had to copy them.
// I've modified drawBmp to buffer the whole image at once instead of doing it line-by-line.
//// BEGIN STOLEN CODE
// Draw directly from file stored in RGB565 format
bool drawBin(const char *filename) {
fs::File bmpFS;
// Open requested file on SD card
bmpFS = WLED_FS.open(filename, "r");
if (!bmpFS)
{
Serial.print(F("File not found: "));
Serial.println(filename);
return(false);
}
size_t sz = bmpFS.size();
if (sz <= 64800)
{
bool oldSwapBytes = getSwapBytes();
setSwapBytes(true);
int16_t h = sz / (135 * 2);
//draw img that is shorter than 240pix into the center
int16_t y = (height() - h) /2;
bmpFS.read((uint8_t *) output_buffer,sz);
if (!realtimeMode || realtimeOverride) strip.service();
pushImage(0, y, 135, h, (uint16_t *)output_buffer);
setSwapBytes(oldSwapBytes);
}
bmpFS.close();
return(true);
}
bool drawBmp(const char *filename) {
fs::File bmpFS;
// Open requested file on SD card
bmpFS = WLED_FS.open(filename, "r");
if (!bmpFS)
{
Serial.print(F("File not found: "));
Serial.println(filename);
return(false);
}
uint32_t seekOffset;
int16_t w, h, row;
uint8_t r, g, b;
uint16_t magic = read16(bmpFS);
if (magic == 0xFFFF) {
Serial.println(F("BMP not found!"));
bmpFS.close();
return(false);
}
if (magic != 0x4D42) {
Serial.print(F("File not a BMP. Magic: "));
Serial.println(magic);
bmpFS.close();
return(false);
}
read32(bmpFS);
read32(bmpFS);
seekOffset = read32(bmpFS);
read32(bmpFS);
w = read32(bmpFS);
h = read32(bmpFS);
if ((read16(bmpFS) != 1) || (read16(bmpFS) != 24) || (read32(bmpFS) != 0)) {
Serial.println(F("BMP format not recognized."));
bmpFS.close();
return(false);
}
//draw img that is shorter than 240pix into the center
int16_t y = (height() - h) /2;
bool oldSwapBytes = getSwapBytes();
setSwapBytes(true);
bmpFS.seek(seekOffset);
uint16_t padding = (4 - ((w * 3) & 3)) & 3;
uint8_t lineBuffer[w * 3 + padding];
uint8_t serviceStrip = (!realtimeMode || realtimeOverride) ? 7 : 0;
// row is decremented as the BMP image is drawn bottom up
for (row = h-1; row >= 0; row--) {
if ((row & 0b00000111) == serviceStrip) strip.service(); //still refresh backlight to mitigate stutter every few rows
bmpFS.read(lineBuffer, sizeof(lineBuffer));
uint8_t* bptr = lineBuffer;
// Convert 24 to 16 bit colours while copying to output buffer.
for (uint16_t col = 0; col < w; col++)
{
b = *bptr++;
g = *bptr++;
r = *bptr++;
output_buffer[row][col] = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}
}
pushImage(0, y, w, h, (uint16_t *)output_buffer);
setSwapBytes(oldSwapBytes);
bmpFS.close();
return(true);
}
public:
TFTs() : TFT_eSPI(), chip_select()
{ for (uint8_t digit=0; digit < NUM_DIGITS; digit++) digits[digit] = 0; }
// no == Do not send to TFT. yes == Send to TFT if changed. force == Send to TFT.
enum show_t { no, yes, force };
// A digit of 0xFF means blank the screen.
const static uint8_t blanked = 255;
void begin() {
pinMode(TFT_ENABLE_PIN, OUTPUT);
digitalWrite(TFT_ENABLE_PIN, HIGH); //enable displays on boot
// Start with all displays selected.
chip_select.begin();
chip_select.setAll();
// Initialize the super class.
init();
}
void showDigit(uint8_t digit) {
chip_select.setDigit(digit);
if (digits[digit] == blanked) {
fillScreen(TFT_BLACK);
}
else {
// Filenames are no bigger than "255.bmp\0"
char file_name[10];
sprintf(file_name, "/%d.bmp", digits[digit]);
if (WLED_FS.exists(file_name)) {
drawBmp(file_name);
} else {
sprintf(file_name, "/%d.bin", digits[digit]);
drawBin(file_name);
}
}
}
void setDigit(uint8_t digit, uint8_t value, show_t show=yes) {
uint8_t old_value = digits[digit];
digits[digit] = value;
if (show != no && (old_value != value || show == force)) {
showDigit(digit);
}
}
uint8_t getDigit(uint8_t digit) { return digits[digit]; }
void showAllDigits() { for (uint8_t digit=0; digit < NUM_DIGITS; digit++) showDigit(digit); }
// Making chip_select public so we don't have to proxy all methods, and the caller can just use it directly.
ChipSelect chip_select;
};
#endif // TFTS_H

View File

@ -0,0 +1,47 @@
/*
* This is intended to over-ride `User_Setup.h` that comes with the TFT_eSPI library.
* I hate having to modify the library code.
*/
// ST7789 135 x 240 display with no chip select line
#define ST7789_DRIVER // Configure all registers
#define TFT_WIDTH 135
#define TFT_HEIGHT 240
#define CGRAM_OFFSET // Library will add offsets required
//#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue
//#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red
//#define TFT_INVERSION_ON
//#define TFT_INVERSION_OFF
// EleksTube IPS
#define TFT_SDA_READ // Read and write on the MOSI/SDA pin, no separate MISO pin
#define TFT_MOSI 23
#define TFT_SCLK 18
//#define TFT_CS -1 // Not connected
#define TFT_DC 25 // Data Command, aka Register Select or RS
#define TFT_RST 26 // Connect reset to ensure display initialises
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
//#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
//#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
//#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
//#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:.
//#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
//#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
//#define SMOOTH_FONT
//#define SPI_FREQUENCY 27000000
#define SPI_FREQUENCY 40000000
/*
* To make the Library not over-write all this:
*/
#define USER_SETUP_LOADED

View File

@ -0,0 +1,31 @@
# EleksTube IPS Clock usermod
This usermod allows WLED to run on the EleksTube IPS clock.
It enables running all WLED effects on the background SK6812 lighting, while displaying digit bitmaps on the 6 IPS screens.
Code is largely based on https://github.com/SmittyHalibut/EleksTubeHAX by Mark Smith!
Supported:
- Display with custom bitmaps or raw RGB565 images (.bin) from filesystem
- Background lighting
- Power button
- RTC (with RTC usermod)
- Standard WLED time features (NTP, DST, timezones)
Not supported:
- 3 navigation buttons, on-device setup
Your images must be exactly 135 pixels wide and 1-240 pixels high.
## Installation
Compile and upload to clock using the `elekstube_ips` PlatformIO environment
Once uploaded (the clock can be flashed like any ESP32 module), go to `[WLED-IP]/edit` and upload the 0-9.bin files from [here](https://github.com/Aircoookie/NixieThemes/tree/master/themes/RealisticNixie/bin).
You can find more clockfaces in the [NixieThemes](https://github.com/Aircoookie/NixieThemes/) repo.
Use LED pin 12, relay pin 27 and button pin 34.
## Use of RGB565 images
Binary 16-bit per pixel RGB565 format `.bin` images are now supported. This has the benefit of only using 2/3rds of the file size a `.bmp` has.
The drawback is that this format cannot be handled by common image programs and that an extra conversion step is needed.
You can use https://lvgl.io/tools/imageconverter to convert your .bmp to a .bin file (settings `True color` and `Binary RGB565`)
Thank you to @RedNax67 for adding .bin support.

View File

@ -0,0 +1,60 @@
#pragma once
#include "TFTs.h"
#include "wled.h"
//Large parts of the code are from https://github.com/SmittyHalibut/EleksTubeHAX
class ElekstubeIPSUsermod : public Usermod {
private:
TFTs tfts;
void updateClockDisplay(TFTs::show_t show=TFTs::yes) {
bool set[6] = {false};
for (uint8_t i = 0; i<6; i++) {
char c = cronixieDisplay[i];
if (c >= '0' && c <= '9') {
tfts.setDigit(5-i, c - '0', show); set[i] = true;
} else if (c >= 'A' && c <= 'G') {
tfts.setDigit(5-i, c - 'A' + 10, show); set[i] = true; //10.bmp to 16.bmp static display
} else if (c == '-' || c == '_' || c == ' ') {
tfts.setDigit(5-i, 255, show); set[i] = true; //blank
} else {
set[i] = false; //display HHMMSS time
}
}
uint8_t hr = hour(localTime);
uint8_t hrTens = hr/10;
uint8_t mi = minute(localTime);
uint8_t mittens = mi/10;
uint8_t s = second(localTime);
uint8_t sTens = s/10;
if (!set[0]) tfts.setDigit(HOURS_TENS, hrTens, show);
if (!set[1]) tfts.setDigit(HOURS_ONES, hr - hrTens*10, show);
if (!set[2]) tfts.setDigit(MINUTES_TENS, mittens, show);
if (!set[3]) tfts.setDigit(MINUTES_ONES, mi - mittens*10, show);
if (!set[4]) tfts.setDigit(SECONDS_TENS, sTens, show);
if (!set[5]) tfts.setDigit(SECONDS_ONES, s - sTens*10, show);
}
unsigned long lastTime = 0;
public:
void setup() {
tfts.begin();
tfts.fillScreen(TFT_BLACK);
for (int8_t i = 5; i >= 0; i--) {
tfts.setDigit(i, 255, TFTs::force); //turn all off
}
}
void loop() {
if (toki.isTick()) {
updateLocalTime();
updateClockDisplay();
}
}
uint16_t getId()
{
return USERMOD_ID_ELEKSTUBE_IPS;
}
};

8
usermods/RTC/readme.md Normal file
View File

@ -0,0 +1,8 @@
# DS1307/DS3231 Real time clock
Gets the time from I2C RTC module on boot. This allows clocks to operate e.g. if temporarily no WiFi is available.
The stored time is updated each time NTP is synced.
## Installation
Add the build flag `-D USERMOD_RTC` to your platformio environment.

View File

@ -0,0 +1,35 @@
#pragma once
#include "src/dependencies/time/DS1307RTC.h"
#include "wled.h"
//Connect DS1307 to standard I2C pins (ESP32: GPIO 21 (SDA)/GPIO 22 (SCL))
class RTCUsermod : public Usermod {
private:
unsigned long lastTime = 0;
bool disabled = false;
public:
void setup() {
time_t rtcTime = RTC.get();
if (rtcTime) {
toki.setTime(rtcTime,TOKI_NO_MS_ACCURACY,TOKI_TS_RTC);
updateLocalTime();
} else {
if (!RTC.chipPresent()) disabled = true; //don't waste time if H/W error
}
}
void loop() {
if (!disabled && toki.isTick()) {
time_t t = toki.second();
if (t != RTC.get()) RTC.set(t); //set RTC to NTP/UI-provided value
}
}
uint16_t getId()
{
return USERMOD_ID_RTC;
}
};

View File

@ -0,0 +1,16 @@
; Options
; -------
; USERMOD_SN_PHOTORESISTOR - define this to have this user mod included wled00\usermods_list.cpp
; USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL - the number of milliseconds between measurements, defaults to 60 seconds
; USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT - the number of milliseconds after boot to take first measurement, defaults to 20 seconds
; USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE - the voltage supplied to the sensor, defaults to 5v
; USERMOD_SN_PHOTORESISTOR_ADC_PRECISION - the ADC precision is the number of distinguishable ADC inputs, defaults to 1024.0 (10 bits)
; USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE - the resistor size, defaults to 10000.0 (10K hms)
; USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE - the offset value to report on, defaults to 25
;
[env:usermod_sn_photoresistor_d1_mini]
extends = env:d1_mini
build_flags =
${common.build_flags_esp8266}
-D USERMOD_SN_PHOTORESISTOR
lib_deps = ${env.lib_deps}

View File

@ -0,0 +1,30 @@
# SN_Photoresistor usermod
This usermod will read from an attached photoresistor sensor like the KY-018 sensor.
The luminance is displayed both in the Info section of the web UI as well as published to the `/luminance` MQTT topic if enabled.
## Installation
Copy the example `platformio_override.ini` to the root directory. This file should be placed in the same directory as `platformio.ini`.
### Define Your Options
* `USERMOD_SN_PHOTORESISTOR` - define this to have this user mod included wled00\usermods_list.cpp
* `USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL` - the number of milliseconds between measurements, defaults to 60 seconds
* `USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 20 seconds
* `USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE` - the voltage supplied to the sensor, defaults to 5v
* `USERMOD_SN_PHOTORESISTOR_ADC_PRECISION` - the ADC precision is the number of distinguishable ADC inputs, defaults to 1024.0 (10 bits)
* `USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE` - the resistor size, defaults to 10000.0 (10K hms)
* `USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE` - the offset value to report on, defaults to 25
All parameters can be configured at runtime using Usermods settings page.
## Project link
* [QuinLED-Dig-Uno](https://quinled.info/2018/09/15/quinled-dig-uno/) - Project link
### PlatformIO requirements
If you are using `platformio_override.ini`, you should be able to refresh the task list and see your custom task, for example `env:usermod_sn_photoresistor_d1_mini`.
## Change Log

View File

@ -0,0 +1,128 @@
#pragma once
#include "wled.h"
//Pin defaults for QuinLed Dig-Uno
#define PHOTORESISTOR_PIN A0
// the frequency to check photoresistor, 10 seconds
#ifndef USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL
#define USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL 10000
#endif
// how many seconds after boot to take first measurement, 10 seconds
#ifndef USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT
#define USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT 10000
#endif
// supplied voltage
#ifndef USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE
#define USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE 5
#endif
// 10 bits
#ifndef USERMOD_SN_PHOTORESISTOR_ADC_PRECISION
#define USERMOD_SN_PHOTORESISTOR_ADC_PRECISION 1024.0
#endif
// resistor size 10K hms
#ifndef USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE
#define USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE 10000.0
#endif
// only report if differance grater than offset value
#ifndef USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE
#define USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE 5
#endif
class Usermod_SN_Photoresistor : public Usermod
{
private:
// set last reading as "40 sec before boot", so first reading is taken after 20 sec
unsigned long lastMeasurement = UINT32_MAX - (USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL - USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT);
// flag to indicate we have finished the first getTemperature call
// allows this library to report to the user how long until the first
// measurement
bool getLuminanceComplete = false;
uint16_t lastLDRValue = -1000;
bool checkBoundSensor(float newValue, float prevValue, float maxDiff)
{
return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff;
}
uint16_t getLuminance()
{
// http://forum.arduino.cc/index.php?topic=37555.0
// https://forum.arduino.cc/index.php?topic=185158.0
float volts = analogRead(PHOTORESISTOR_PIN) * (USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE / USERMOD_SN_PHOTORESISTOR_ADC_PRECISION);
float amps = volts / USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE;
float lux = amps * 1000000 * 2.0;
lastMeasurement = millis();
getLuminanceComplete = true;
return uint16_t(lux);
}
public:
void setup()
{
pinMode(PHOTORESISTOR_PIN, INPUT);
}
void loop()
{
unsigned long now = millis();
// check to see if we are due for taking a measurement
// lastMeasurement will not be updated until the conversion
// is complete the the reading is finished
if (now - lastMeasurement < USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL)
{
return;
}
uint16_t currentLDRValue = getLuminance();
if (checkBoundSensor(currentLDRValue, lastLDRValue, USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE))
{
lastLDRValue = currentLDRValue;
if (WLED_MQTT_CONNECTED)
{
char subuf[45];
strcpy(subuf, mqttDeviceTopic);
strcat_P(subuf, PSTR("/luminance"));
mqtt->publish(subuf, 0, true, String(lastLDRValue).c_str());
}
else
{
DEBUG_PRINTLN("Missing MQTT connection. Not publishing data");
}
}
}
void addToJsonInfo(JsonObject &root)
{
JsonObject user = root[F("u")];
if (user.isNull()) user = root.createNestedObject(F("u"));
JsonArray lux = user.createNestedArray(F("Luminance"));
if (!getLuminanceComplete)
{
// if we haven't read the sensor yet, let the user know
// that we are still waiting for the first measurement
lux.add((USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT - millis()) / 1000);
lux.add(F(" sec until read"));
return;
}
lux.add(lastLDRValue);
lux.add(F(" lux"));
}
uint16_t getId()
{
return USERMOD_ID_SN_PHOTORESISTOR;
}
};

View File

@ -0,0 +1,14 @@
#include "wled.h"
/*
* Register your v2 usermods here!
*/
#ifdef USERMOD_SN_PHOTORESISTOR
#include "../usermods/SN_Photoresistor/usermod_sn_photoresistor.h"
#endif
void registerUsermods()
{
#ifdef USERMOD_SN_PHOTORESISTOR
usermods.add(new Usermod_SN_Photoresistor());
#endif
}

View File

@ -42,30 +42,24 @@ uint16_t WS2812FX::mode_static(void) {
* Blink/strobe function * Blink/strobe function
* Alternate between color1 and color2 * Alternate between color1 and color2
* if(strobe == true) then create a strobe effect * if(strobe == true) then create a strobe effect
* NOTE: Maybe re-rework without timer
*/ */
uint16_t WS2812FX::blink(uint32_t color1, uint32_t color2, bool strobe, bool do_palette) { uint16_t WS2812FX::blink(uint32_t color1, uint32_t color2, bool strobe, bool do_palette) {
uint16_t stateTime = SEGENV.aux1;
uint32_t cycleTime = (255 - SEGMENT.speed)*20; uint32_t cycleTime = (255 - SEGMENT.speed)*20;
uint32_t onTime = 0; uint32_t onTime = FRAMETIME;
uint32_t offTime = cycleTime; if (!strobe) onTime += ((cycleTime * SEGMENT.intensity) >> 8);
cycleTime += FRAMETIME*2;
uint32_t it = now / cycleTime;
uint32_t rem = now % cycleTime;
if (!strobe) { bool on = false;
onTime = (cycleTime * SEGMENT.intensity) >> 8; if (it != SEGENV.step //new iteration, force on state for one frame, even if set time is too brief
offTime = cycleTime - onTime; || rem <= onTime) {
on = true;
} }
stateTime = ((SEGENV.aux0 & 1) == 0) ? onTime : offTime; SEGENV.step = it; //save previous iteration
stateTime += 20;
if (now - SEGENV.step > stateTime) uint32_t color = on ? color1 : color2;
{
SEGENV.aux0++;
SEGENV.aux1 = stateTime;
SEGENV.step = now;
}
uint32_t color = ((SEGENV.aux0 & 1) == 0) ? color1 : color2;
if (color == color1 && do_palette) if (color == color1 && do_palette)
{ {
for(uint16_t i = 0; i < SEGLEN; i++) { for(uint16_t i = 0; i < SEGLEN; i++) {

View File

@ -73,7 +73,7 @@
#define SEGENV _segment_runtimes[_segment_index] #define SEGENV _segment_runtimes[_segment_index]
#define SEGLEN _virtualSegmentLength #define SEGLEN _virtualSegmentLength
#define SEGACT SEGMENT.stop #define SEGACT SEGMENT.stop
#define SPEED_FORMULA_L (uint16_t)(5 + (50*(255 - SEGMENT.speed))/SEGLEN) #define SPEED_FORMULA_L 5U + (50U*(255U - SEGMENT.speed))/SEGLEN
#define RESET_RUNTIME memset(_segment_runtimes, 0, sizeof(_segment_runtimes)) #define RESET_RUNTIME memset(_segment_runtimes, 0, sizeof(_segment_runtimes))
// some common colors // some common colors

View File

@ -991,7 +991,7 @@ uint32_t WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8
} }
uint8_t paletteIndex = i; uint8_t paletteIndex = i;
if (mapping) paletteIndex = (i*255)/(SEGLEN -1); if (mapping && SEGLEN > 1) paletteIndex = (i*255)/(SEGLEN -1);
if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end" if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
CRGB fastled_col; CRGB fastled_col;
fastled_col = ColorFromPalette( currentPalette, paletteIndex, pbri, (paletteBlend == 3)? NOBLEND:LINEARBLEND); fastled_col = ColorFromPalette( currentPalette, paletteIndex, pbri, (paletteBlend == 3)? NOBLEND:LINEARBLEND);

View File

@ -46,7 +46,10 @@ void onAlexaChange(EspalexaDevice* dev)
bri = briLast; bri = briLast;
colorUpdated(NOTIFIER_CALL_MODE_ALEXA); colorUpdated(NOTIFIER_CALL_MODE_ALEXA);
} }
} else applyPreset(macroAlexaOn); } else {
applyPreset(macroAlexaOn);
if (bri == 0) espalexaDevice->setValue(briLast); //stop Alexa from complaining if macroAlexaOn does not actually turn on
}
} else if (m == EspalexaDeviceProperty::off) } else if (m == EspalexaDeviceProperty::off)
{ {
if (!macroAlexaOff) if (!macroAlexaOff)
@ -57,7 +60,10 @@ void onAlexaChange(EspalexaDevice* dev)
bri = 0; bri = 0;
colorUpdated(NOTIFIER_CALL_MODE_ALEXA); colorUpdated(NOTIFIER_CALL_MODE_ALEXA);
} }
} else applyPreset(macroAlexaOff); } else {
applyPreset(macroAlexaOff);
if (bri != 0) espalexaDevice->setValue(0); //stop Alexa from complaining if macroAlexaOff does not actually turn off
}
} else if (m == EspalexaDeviceProperty::bri) } else if (m == EspalexaDeviceProperty::bri)
{ {
bri = espalexaDevice->getValue(); bri = espalexaDevice->getValue();

View File

@ -160,7 +160,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
} }
CJSON(touchThreshold,hw[F("btn")][F("tt")]); CJSON(touchThreshold,hw[F("btn")][F("tt")]);
#ifndef WLED_DISABLE_INFRARED
int hw_ir_pin = hw["ir"]["pin"] | -2; // 4 int hw_ir_pin = hw["ir"]["pin"] | -2; // 4
if (hw_ir_pin > -2) { if (hw_ir_pin > -2) {
if (pinManager.allocatePin(hw_ir_pin,false)) { if (pinManager.allocatePin(hw_ir_pin,false)) {
@ -169,7 +168,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
irPin = -1; irPin = -1;
} }
} }
#endif
CJSON(irEnabled, hw["ir"]["type"]); CJSON(irEnabled, hw["ir"]["type"]);
JsonObject relay = hw[F("relay")]; JsonObject relay = hw[F("relay")];

View File

@ -55,6 +55,9 @@
#define USERMOD_ID_VL53L0X 12 //Usermod "usermod_vl53l0x_gestures.h" #define USERMOD_ID_VL53L0X 12 //Usermod "usermod_vl53l0x_gestures.h"
#define USERMOD_ID_MULTI_RELAY 13 //Usermod "usermod_multi_relay.h" #define USERMOD_ID_MULTI_RELAY 13 //Usermod "usermod_multi_relay.h"
#define USERMOD_ID_ANIMATED_STAIRCASE 14 //Usermod "Animated_Staircase.h" #define USERMOD_ID_ANIMATED_STAIRCASE 14 //Usermod "Animated_Staircase.h"
#define USERMOD_ID_RTC 15 //Usermod "usermod_rtc.h"
#define USERMOD_ID_ELEKSTUBE_IPS 16 //Usermod "usermod_elekstube_ips.h"
#define USERMOD_ID_SN_PHOTORESISTOR 17 //Usermod "usermod_sn_photoresistor.h"
//Access point behavior //Access point behavior
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot #define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
@ -190,6 +193,9 @@
#define SEG_OPTION_FREEZE 5 //Segment contents will not be refreshed #define SEG_OPTION_FREEZE 5 //Segment contents will not be refreshed
#define SEG_OPTION_TRANSITIONAL 7 #define SEG_OPTION_TRANSITIONAL 7
//Playlist option byte
#define PL_OPTION_SHUFFLE 0x01
// WLED Error modes // WLED Error modes
#define ERR_NONE 0 // All good :) #define ERR_NONE 0 // All good :)
#define ERR_EEP_COMMIT 2 // Could not commit to EEPROM (wrong flash layout?) #define ERR_EEP_COMMIT 2 // Could not commit to EEPROM (wrong flash layout?)
@ -235,7 +241,11 @@
// string temp buffer (now stored in stack locally) // string temp buffer (now stored in stack locally)
#define OMAX 2048 #define OMAX 2048
#define E131_MAX_UNIVERSE_COUNT 9 #ifdef WLED_USE_ETHERNET
#define E131_MAX_UNIVERSE_COUNT 20
#else
#define E131_MAX_UNIVERSE_COUNT 10
#endif
#define ABL_MILLIAMPS_DEFAULT 850 // auto lower brightness to stay close to milliampere limit #define ABL_MILLIAMPS_DEFAULT 850 // auto lower brightness to stay close to milliampere limit

View File

@ -39,7 +39,7 @@
</style> </style>
</head> </head>
<body> <body>
<img alt="" src=" data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsEAAA7BAbiRa+0AAAFMSURBVFhH7ZTfbYNADMaPKDNUrJA85CEjdIjOgNQV+sASlZgmI/AIK6AuQfngnDrmjtpHItQ/P+l0juHsz2cH9+fJ/G7nreldfnDnp+ln/ZIlxbIfQmIwJOekCrEJ8FUvASEWEXoBiuSERcTO75uhuwFWff86bi57n3ZC+rW3YLqB5rn11ldCEPNr2LwFJgHHy8G1bTsu3oKYX4N5BrQ8ZAYewSoBGDjr0ElWCUC/rT2X7MqynL7tG4Dc45BwEYM9H5w7DqHMdfNCURR9nue3Iobk55MtOYeLoOQ8vmoG6o+0FaLrOm9FwC3wayLgx5I2WHpGIGYorulfgPYQ3AZLz4hQ9TMBVVVleJGrRUWz2YgQOg8bPjzzrit7vwcRQb5NTiARRPPzMYItoCpoWZITMkao+mRkddpqQ6z6FN+DfwFJrOm55GfewC/CuU/E4tQYg7BPYQAAAABJRU5ErkJggg=="> <img alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAbUExURQAAAAB81gCU/zKq/////9bW1oCAgGhoaAAAAGPLX8AAAAAJdFJOU///////////AFNPeBIAAAAJcEhZcwAADsAAAA7AAWrWiQkAAACdSURBVDhPxc9bDoUgEANQebP/FUuHMjBGY/B+3EYR7RH0qC/ZBc6HwCljgHO+xZIVSI2sYgHaG7EBWh8jWoxTrCBFdDJ+BD4lbIHxAcz8APAVLTsrZE4eQD5qzt3cAFTYokC4YCN9Gybgu4yAQtBFLQXHuHABA7JMeOEC/E0W5uy9gv4vo5QHK2i7yq2C8UABM4HmL+CSTXCTF1DrCX6+Gp9zB5dsAAAAAElFTkSuQmCC">
<h1>404 Not Found</h1> <h1>404 Not Found</h1>
<b>Akemi does not know where you are headed...</b><br><br> <b>Akemi does not know where you are headed...</b><br><br>
<button onclick="window.location.href='/sliders'">Back to controls</button> <button onclick="window.location.href='/sliders'">Back to controls</button>

View File

@ -1032,6 +1032,8 @@ function cmpP(a, b)
function handleJson(s) function handleJson(s)
{ {
if (!s) return false; if (!s) return false;
var e1 = gId('fxlist');
var e2 = gId('selectPalette');
isOn = s.on; isOn = s.on;
gId('sliderBri').value= s.bri; gId('sliderBri').value= s.bri;
@ -1084,6 +1086,28 @@ function handleJson(s)
selectedPal = i.pal; selectedPal = i.pal;
selectedFx = i.fx; selectedFx = i.fx;
/*/--- AC addition ---//
// unfortunately this will trigger JSON request loop due to onchange event on input elements
// and it may not be necessary with websockes
// Effects
var selFx = e1.querySelector(`input[name="fx"][value="${i.fx}"]`);
if (selFx) selFx.checked = true;
else location.reload(); //effect list is gone (e.g. if restoring tab). Reload.
var selElement = e1.querySelector('.selected');
if (selElement) selElement.classList.remove('selected');
var selectedEffect = e1.querySelector(`.lstI[data-id="${i.fx}"]`);
if (selectedEffect) selectedEffect.classList.add('selected');
// Palettes
var selPa = e2.querySelector(`input[name="palette"][value="${i.pal}"]`);
if (selPa) selPa.checked = true;
selElement = e2.querySelector('.selected');
if (selElement) selElement.classList.remove('selected');
e2.querySelector(`.lstI[data-id="${i.pal}"]`).classList.add('selected');
//--- AC addition ---/*/
displayRover(lastinfo, s); displayRover(lastinfo, s);
clearErrorToast(); clearErrorToast();
@ -1097,9 +1121,6 @@ function requestJson(command, rinfo = true, verbose = true, callback = null)
lastUpdate = new Date(); lastUpdate = new Date();
if (!jsonTimeout) jsonTimeout = setTimeout(showErrorToast, 3000); if (!jsonTimeout) jsonTimeout = setTimeout(showErrorToast, 3000);
var req = null; var req = null;
var e1 = gId('fxlist');
var e2 = gId('selectPalette');
var url = (loc?`http://${locip}`:'') + (rinfo ? '/json/si': (command ? '/json/state':'/json/si')); var url = (loc?`http://${locip}`:'') + (rinfo ? '/json/si': (command ? '/json/state':'/json/si'));
var type = command ? 'post':'get'; var type = command ? 'post':'get';

View File

@ -283,11 +283,11 @@ Color Order:
<option value="4">BGR</option> <option value="4">BGR</option>
<option value="5">GBR</option> <option value="5">GBR</option>
</select><br> </select><br>
<span id="p0d${i}">Pin:</span> <input type="number" name="L0${i}" min="0" max="40" required style="width:35px" onchange="UI()"/> <span id="p0d${i}">Pin:</span> <input type="number" name="L0${i}" min="0" max="40" required style="width:40px" onchange="UI()"/>
<span id="p1d${i}">Clock:</span> <input type="number" name="L1${i}" min="0" max="40" style="width:35px" onchange="UI()"/> <span id="p1d${i}">Clock:</span> <input type="number" name="L1${i}" min="0" max="40" style="width:40px" onchange="UI()"/>
<span id="p2d${i}"></span><input type="number" name="L2${i}" min="0" max="40" style="width:35px" onchange="UI()"/> <span id="p2d${i}"></span><input type="number" name="L2${i}" min="0" max="40" style="width:40px" onchange="UI()"/>
<span id="p3d${i}"></span><input type="number" name="L3${i}" min="0" max="40" style="width:35px" onchange="UI()"/> <span id="p3d${i}"></span><input type="number" name="L3${i}" min="0" max="40" style="width:40px" onchange="UI()"/>
<span id="p4d${i}"></span><input type="number" name="L4${i}" min="0" max="40" style="width:35px" onchange="UI()"/> <span id="p4d${i}"></span><input type="number" name="L4${i}" min="0" max="40" style="width:40px" onchange="UI()"/>
<br> <br>
<span id="psd${i}">Start:</span> <input type="number" name="LS${i}" id="ls${i}" min="0" max="8191" value="${lastEnd(i)}" readonly required />&nbsp; <span id="psd${i}">Start:</span> <input type="number" name="LS${i}" id="ls${i}" min="0" max="8191" value="${lastEnd(i)}" readonly required />&nbsp;
<div id="dig${i}" style="display:inline"> <div id="dig${i}" style="display:inline">
@ -311,7 +311,7 @@ Reverse (rotated 180°): <input type="checkbox" name="CV${i}">
var c = gId("btns").innerHTML; var c = gId("btns").innerHTML;
var bt = "BT" + i; var bt = "BT" + i;
var be = "BE" + i; var be = "BE" + i;
c += `Button ${i} pin: <input type="number" min="-1" max="40" name="${bt}" onchange="UI()" style="width:35px" value="${p}">`; c += `Button ${i} pin: <input type="number" min="-1" max="40" name="${bt}" onchange="UI()" style="width:40px" value="${p}">`;
c += `<select name="${be}">` c += `<select name="${be}">`
c += `<option value="0" ${t==0?"selected":""}>Disabled</option>`; c += `<option value="0" ${t==0?"selected":""}>Disabled</option>`;
c += `<option value="2" ${t==2?"selected":""}>Pushbutton</option>`; c += `<option value="2" ${t==2?"selected":""}>Pushbutton</option>`;
@ -393,7 +393,7 @@ Reverse (rotated 180°): <input type="checkbox" name="CV${i}">
</div><hr style="width:260px"> </div><hr style="width:260px">
<div id="btns"></div> <div id="btns"></div>
Touch threshold: <input type="number" min="0" max="100" name="TT" required><br> Touch threshold: <input type="number" min="0" max="100" name="TT" required><br>
IR pin: <input type="number" min="-1" max="40" name="IR" onchange="UI()" style="width:35px"><select name="IT" onchange="UI()"> IR pin: <input type="number" min="-1" max="40" name="IR" onchange="UI()" style="width:40px"><select name="IT" onchange="UI()">
<option value="0">Remote disabled</option> <option value="0">Remote disabled</option>
<option value="1">24-key RGB</option> <option value="1">24-key RGB</option>
<option value="2">24-key with CT</option> <option value="2">24-key with CT</option>
@ -407,7 +407,7 @@ Reverse (rotated 180°): <input type="checkbox" name="CV${i}">
<div id="json" style="display:none;">JSON file: <input type="file" name="data" accept=".json"> <input type="button" value="Upload" onclick="uploadFile('/ir.json');"><br></div> <div id="json" style="display:none;">JSON file: <input type="file" name="data" accept=".json"> <input type="button" value="Upload" onclick="uploadFile('/ir.json');"><br></div>
<div id="toast"></div> <div id="toast"></div>
<a href="https://github.com/Aircoookie/WLED/wiki/Infrared-Control" target="_blank">IR info</a><br> <a href="https://github.com/Aircoookie/WLED/wiki/Infrared-Control" target="_blank">IR info</a><br>
Relay pin: <input type="number" min="-1" max="40" name="RL" onchange="UI()" style="width:35px"> invert <input type="checkbox" name="RM"><span style="cursor: pointer;" onclick="off('RL')">&nbsp;&#215;</span><br> Relay pin: <input type="number" min="-1" max="40" name="RL" onchange="UI()" style="width:40px"> invert <input type="checkbox" name="RM"><span style="cursor: pointer;" onclick="off('RL')">&nbsp;&#215;</span><br>
<hr style="width:260px"> <hr style="width:260px">
<h3>Defaults</h3> <h3>Defaults</h3>
Turn LEDs on after power up/reset: <input type="checkbox" name="BO"><br> Turn LEDs on after power up/reset: <input type="checkbox" name="BO"><br>

View File

@ -19,28 +19,6 @@ function GetV(){var d=document;}
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr> <button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
</div> </div>
<h2>Sync setup</h2> <h2>Sync setup</h2>
<!--
<h3>Button setup</h3>
Button type:
<select name=BT>
<option value=0>Disabled</option>
<option value=2>Pushbutton</option>
<option value=4>Switch</option>
</select><br>
Infrared remote:
<select name=IR>
<option value=0>Disabled</option>
<option value=1>24-key RGB</option>
<option value=2>24-key with CT</option>
<option value=3>40-key blue</option>
<option value=4>44-key RGB</option>
<option value=5>21-key RGB</option>
<option value=6>6-key black</option>
<option value=7>9-key red</option>
<option value=8>JSON remote</option>
</select><br>
<a href="https://github.com/Aircoookie/WLED/wiki/Infrared-Control" target="_blank">IR info</a>
-->
<h3>WLED Broadcast</h3> <h3>WLED Broadcast</h3>
UDP Port: <input name="UP" type="number" min="1" max="65535" class="d5" required><br> UDP Port: <input name="UP" type="number" min="1" max="65535" class="d5" required><br>
2nd Port: <input name="U2" type="number" min="1" max="65535" class="d5" required><br> 2nd Port: <input name="U2" type="number" min="1" max="65535" class="d5" required><br>

View File

@ -76,11 +76,11 @@
} }
} }
function addRow(i,p,l,d) { function addRow(i,p,l,d) {
var t = gId("macros"); var t = gId("macros"); // table
var rCnt = t.rows.length; var rCnt = t.rows.length; // get the number of rows.
var tr = t.insertRow(rCnt); var tr = t.insertRow(rCnt); // table row.
var td = document.createElement('td'); var td = document.createElement('td'); // TABLE DEFINITION.
td = tr.insertCell(0); td = tr.insertCell(0);
td.innerHTML = `Button ${i}:`; td.innerHTML = `Button ${i}:`;
td = tr.insertCell(1); td = tr.insertCell(1);
@ -179,17 +179,7 @@
<tbody> <tbody>
</tbody> </tbody>
</table> </table>
<div>For analog button set <i>double</i> to: <a href="https://github.com/Aircoookie/WLED/wiki/Macros#analog-button" target="_blank">Analog Button setup</a>
<ul>
<li>250=global brightness</li>
<li>249=effect speed</li>
<li>248=effect intensity</li>
<li>247=palette</li>
<li>200=primary color hue</li>
<li>0..32=segment X opacity</li>
</ul>
More details: <a href="https://github.com/Aircoookie/WLED/wiki/Macros#analog-button" target="_blank">Analog Button setup</a>
</div>
<h3>Time-controlled presets</h3> <h3>Time-controlled presets</h3>
<div style="display: inline-block"> <div style="display: inline-block">
<table id="TMT"> <table id="TMT">

View File

@ -31,11 +31,11 @@
} }
img { img {
width: 999px; width: 950px;
max-width: 85%; max-width: 82%;
image-rendering: pixelated; image-rendering: pixelated;
image-rendering: crisp-edges; image-rendering: crisp-edges;
margin: 25px 0 -10px 0; margin: 4vh 0 0 0;
animation: fi 1s; animation: fi 1s;
} }
@ -50,7 +50,7 @@
</style> </style>
</head> </head>
<body> <body>
<img alt="" src=" data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHEAAAAjCAYAAACjOOUsAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsAAAA7AAWrWiQkAAATfSURBVGhD7ZlPaBVHHMdnS2Ox5iKYGIyYJwQlUTHG6kUwtSAIBsEiBopgKR5i68mbIPgEwZun0uYgouIlobQIKQheIsSLaBSaRFoEc3gtMa9HLaiH7X4n81tn583u/HkTaZ/7gR+zMzvv93bfZ3/777GSkpL/AJFo3Tg/H7OuPtGxY7CTsZnjkd/3lRTi/qMKgfGXt8TAO6KfT2TG1f7u6ROlyBXgI9HaQRW4+JR3ox92Z1p1XO3PLCUVORHHvFMSDDeJBuJvH4mlZdS+C3c7B+Ln3fvivw59w0MMOzP/FYvj75K4PMhDDDszdbM7rk1V4hcPD/MQw858MbEnPvJgf3x29hQPMdwUbqc2qRIhCBVGrQk+Lzm9mq6NkIe2t20N7xOf7NjK2w13rlltM+Sh7VvLu+/YOMib6NyMVR7IQ9u7qY33ibb2bbxd/9mvVnkgD217ZTXvE72fbuHtle1X3VxIuH1QOZ2mmG5yaH4yr0giBKryVCDTJBICG+SpJDJNIiFQlacCmSaREKjKU4FMX5F+p1NIk8OExTwbgeD1b7+zotOrlUBQm2FFp1cbgeDtyzlWdHq1EQie/fMH8z29Br0myiwdr7PkWpSGC2se/8IqtWnRa+y7Ur+Q3FF97/X7ZFjV/4B1Dz1Pw5WrlZ/Y7T330gjFikkE9Xo9jSJ0Vah+Ru7nVWNRFWq3Iacai6qQ9kfOl1eNuirUfZ7wrcbgEqkCOyc6MmEDVdyrXUd5ENR3rUiqwI6LnTx8oQp8M783E66cWjiWCarMZlnRSix5PwSTSBXY0WFXdf9HfK+FKlSBoX6rjEQ8XMutD3nn+1Yg5L755Mrzk0qkh2xMwLOYr0iXa6CJZu9KQ+N7LSSoAuma6EKRn1TiwaUn0bO3r/gy7v42/3nf+cEzxFGq5jD1TYTYJhAqj4pt3iI/mdMpTbQRqD4HhqhA9S40r28L3ZWmz4kUjlAFNvucSNBzomtF5vlJJVarVb53mMgHLMBRRBEaNWez3xFqW5vN0+znyQ/5Ahlho6OjcVdXl+jxiVmhHn8Gq+jendq+cpPRvUO1fuUmo3mHavvKTUb3DtX2lZtM3jtUWRqQ3TRMBrLMjMgVkAiB2waX/1nwgURygTv885BICOzrH+BjPpBICBzo38nHfJBFksDFxUU2NjaWjhMNAwSJ1Ems/n2RDQ0NsQNzn4sVjF1vvyGWGOvp6cmsk+frJLpWISFXo1cVElI1+lQhIVejTxUSajVCYp5AoB0k6AhIRQqJo7OnGRe87gIfxl9N1Y8nlpcTKpUK+/rlSdFLDghpft5fUTho0GJD5WW0RetUWi1PgwMNmbtTFdhXaRBowdj2H3kuVGQeyIkg5GVQtE6mFfPoPMgUSqSjgo4Gdqk/whfxpGeSaqJIxjFGsbCwkF2fBHJh3ZH5RpE2RxtBedJtkmi1PLSMdXwgh0KJAIkBEiLQV5MODw9ndiBvp/I2VIwZd5T4EPJQLvRNGCVSYgr0xSotIyMjYkkPfV7eUFNOHa2cx+X3BkaJAIkoxJAWCBwfHxc9PdhBbFzSRgixzHfahVbNA2x/b8JKoi0QaKpE7KC8cVjGmOha06p5fAgq0aYSS8ITROLk5GRUq9UyAt/XUVgSGNyl0kNrMyBHchCUeSwJejotKSkp+VBh7F9fIy7OdlOyMwAAAABJRU5ErkJggg=="> <img alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAG0AAAAfCAMAAADazLOuAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAABLUExURQAAAAB81gCU/zKq///mo7sWMN8bO+ZIYtZaAP9rAP+HMsCiG+TAIOnMS0KqNU7KPnLUZOrq6v///4CAgGhoaL+/v6CgoExMTAAAAAlm4O8AAAAZdFJOU////////////////////////////////wABNAq3AAAACXBIWXMAAA7DAAAOwwHHb6hkAAACN0lEQVRIS73VjVLCMBAEYIr8CYKkrdj3f1J37zaXFCpTO+piaDgbPq9px9VQ0qyrvKj4q6m0Zr1h+M7xF1zRmnWzqV9/0d2jttGotO1uv9dUObwej5oqp7fzWVPl8n69aprzoOUUbbvdIbV3OLwitXc6vSG1d7m8I3feSEN0j2CeNbOY4MxigjOLCc4sZsTV2l1cCyy4wIILLLjAxtykltq2rbTU+qi01N5rXNO2leaFORoija2l5MM5a02ac9Ya16Sk5tgaPrUpjZub0BL6YqSxKwbH77XUUmSkJXSl8QtaMuyJhq5maL5nTKVpZC13VmtMpTFT2g4vJjTuGfMzzXftiUZnhdtgb1xofvypRon5TjNnxYN9zJo6K5ruSIzQtGuVZn0x91rKvdHBvm39E7SyZ4y06Gz8BDBFKzsXmhcwyfsGZ9VpbhoiCinaxPNmGWmWWrNU2jB0q6HvOhN1JUtCixQtp2g51ZVUXIPS2RMAD++T2nY/DrDjOMDO4wC7jmNYj3d73nrXug8Yt9uNB8xNU1cKNXWlUFNXCjV1pZhGTE83m2vWfYf/NGj4Bg1zu5JD3/MnH5ZWfLOksbmGWGjgXMN5/C2GXYGFFW9Nmtle6Xut0Gm+JsayCj8z0nhjGvYJzVf4aSzmNYsr+u7Q2JIdoX3YOQjOslmsW1jJ3120nE9gfo79hTaNdcsqVR610lvO47pllae9ReZ805zKo2a3iaY5c75pTmVCA6dJ5H7N0sr/asPwBehb7ifEhusRAAAAAElFTkSuQmCC">
<div class="main"> <div class="main">
<h1>Welcome to WLED!</h1> <h1>Welcome to WLED!</h1>
<h3>Thank you for installing my application!</h3> <h3>Thank you for installing my application!</h3>

View File

@ -100,8 +100,8 @@ void handleIR();
#include "src/dependencies/json/AsyncJson-v6.h" #include "src/dependencies/json/AsyncJson-v6.h"
#include "FX.h" #include "FX.h"
void deserializeSegment(JsonObject elem, byte it, bool fromPlaylist = false); void deserializeSegment(JsonObject elem, byte it, byte presetId = 0);
bool deserializeState(JsonObject root, bool fromPlaylist = false); bool deserializeState(JsonObject root, byte presetId = 0);
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true, uint8_t versionAPI = 1); void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true, uint8_t versionAPI = 1);
void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true); void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true);
void serializeInfo(JsonObject root); void serializeInfo(JsonObject root);
@ -130,6 +130,7 @@ bool initMqtt();
void publishMqtt(); void publishMqtt();
//ntp.cpp //ntp.cpp
void handleTime();
void handleNetworkTime(); void handleNetworkTime();
void sendNTPPacket(); void sendNTPPacket();
bool checkNTPResponse(); bool checkNTPResponse();
@ -140,6 +141,7 @@ void setCountdown();
byte weekdayMondayFirst(); byte weekdayMondayFirst();
void checkTimers(); void checkTimers();
void calculateSunriseAndSunset(); void calculateSunriseAndSunset();
void setTimeFromAPI(uint32_t timein);
//overlay.cpp //overlay.cpp
void initCronixie(); void initCronixie();
@ -156,11 +158,11 @@ void _drawOverlayCronixie();
//playlist.cpp //playlist.cpp
void shufflePlaylist(); void shufflePlaylist();
void unloadPlaylist(); void unloadPlaylist();
void loadPlaylist(JsonObject playlistObject); void loadPlaylist(JsonObject playlistObject, byte presetId = 0);
void handlePlaylist(); void handlePlaylist();
//presets.cpp //presets.cpp
bool applyPreset(byte index, bool fromPlaylist = false); bool applyPreset(byte index);
void savePreset(byte index, bool persist = true, const char* pname = nullptr, JsonObject saveobj = JsonObject()); void savePreset(byte index, bool persist = true, const char* pname = nullptr, JsonObject saveobj = JsonObject());
void deletePreset(byte index); void deletePreset(byte index);

View File

@ -55,9 +55,9 @@ Please do not close or refresh the page :)</div></body></html>)=====";
const char PAGE_welcome[] PROGMEM = R"=====(<!DOCTYPE html><html><head><meta charset="utf-8"><meta 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="width=device-width" name="viewport"><meta name="theme-color"
content="#222222"><title>Welcome!</title><style> content="#222222"><title>Welcome!</title><style>
body{font-family:Verdana,Helvetica,sans-serif;text-align:center;background-color:#222;margin:0;color:#fff}button{outline:0;cursor:pointer;padding:8px;margin:10px;width:230px;text-transform:uppercase;font-family:helvetica;font-size:19px;background-color:#333;color:#fff;border:0 solid #fff;border-radius:25px;filter:drop-shadow(0 0 1px #000)}img{width:999px;max-width:85%;image-rendering:pixelated;image-rendering:crisp-edges;margin:25px 0 -10px 0;animation:fi 1s}@keyframes fi{from{opacity:0}to{opacity:1}}.main{animation:fi 1.5s .7s both} body{font-family:Verdana,Helvetica,sans-serif;text-align:center;background-color:#222;margin:0;color:#fff}button{outline:0;cursor:pointer;padding:8px;margin:10px;width:230px;text-transform:uppercase;font-family:helvetica;font-size:19px;background-color:#333;color:#fff;border:0 solid #fff;border-radius:25px;filter:drop-shadow(0 0 1px #000)}img{width:950px;max-width:82%;image-rendering:pixelated;image-rendering:crisp-edges;margin:4vh 0 0 0;animation:fi 1s}@keyframes fi{from{opacity:0}to{opacity:1}}.main{animation:fi 1.5s .7s both}
</style></head><body><img alt="" </style></head><body><img alt=""
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHEAAAAjCAYAAACjOOUsAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsAAAA7AAWrWiQkAAATfSURBVGhD7ZlPaBVHHMdnS2Ox5iKYGIyYJwQlUTHG6kUwtSAIBsEiBopgKR5i68mbIPgEwZun0uYgouIlobQIKQheIsSLaBSaRFoEc3gtMa9HLaiH7X4n81tn583u/HkTaZ/7gR+zMzvv93bfZ3/777GSkpL/AJFo3Tg/H7OuPtGxY7CTsZnjkd/3lRTi/qMKgfGXt8TAO6KfT2TG1f7u6ROlyBXgI9HaQRW4+JR3ox92Z1p1XO3PLCUVORHHvFMSDDeJBuJvH4mlZdS+C3c7B+Ln3fvivw59w0MMOzP/FYvj75K4PMhDDDszdbM7rk1V4hcPD/MQw858MbEnPvJgf3x29hQPMdwUbqc2qRIhCBVGrQk+Lzm9mq6NkIe2t20N7xOf7NjK2w13rlltM+Sh7VvLu+/YOMib6NyMVR7IQ9u7qY33ibb2bbxd/9mvVnkgD217ZTXvE72fbuHtle1X3VxIuH1QOZ2mmG5yaH4yr0giBKryVCDTJBICG+SpJDJNIiFQlacCmSaREKjKU4FMX5F+p1NIk8OExTwbgeD1b7+zotOrlUBQm2FFp1cbgeDtyzlWdHq1EQie/fMH8z29Br0myiwdr7PkWpSGC2se/8IqtWnRa+y7Ur+Q3FF97/X7ZFjV/4B1Dz1Pw5WrlZ/Y7T330gjFikkE9Xo9jSJ0Vah+Ru7nVWNRFWq3Iacai6qQ9kfOl1eNuirUfZ7wrcbgEqkCOyc6MmEDVdyrXUd5ENR3rUiqwI6LnTx8oQp8M783E66cWjiWCarMZlnRSix5PwSTSBXY0WFXdf9HfK+FKlSBoX6rjEQ8XMutD3nn+1Yg5L755Mrzk0qkh2xMwLOYr0iXa6CJZu9KQ+N7LSSoAuma6EKRn1TiwaUn0bO3r/gy7v42/3nf+cEzxFGq5jD1TYTYJhAqj4pt3iI/mdMpTbQRqD4HhqhA9S40r28L3ZWmz4kUjlAFNvucSNBzomtF5vlJJVarVb53mMgHLMBRRBEaNWez3xFqW5vN0+znyQ/5Ahlho6OjcVdXl+jxiVmhHn8Gq+jendq+cpPRvUO1fuUmo3mHavvKTUb3DtX2lZtM3jtUWRqQ3TRMBrLMjMgVkAiB2waX/1nwgURygTv885BICOzrH+BjPpBICBzo38nHfJBFksDFxUU2NjaWjhMNAwSJ1Ems/n2RDQ0NsQNzn4sVjF1vvyGWGOvp6cmsk+frJLpWISFXo1cVElI1+lQhIVejTxUSajVCYp5AoB0k6AhIRQqJo7OnGRe87gIfxl9N1Y8nlpcTKpUK+/rlSdFLDghpft5fUTho0GJD5WW0RetUWi1PgwMNmbtTFdhXaRBowdj2H3kuVGQeyIkg5GVQtE6mFfPoPMgUSqSjgo4Gdqk/whfxpGeSaqJIxjFGsbCwkF2fBHJh3ZH5RpE2RxtBedJtkmi1PLSMdXwgh0KJAIkBEiLQV5MODw9ndiBvp/I2VIwZd5T4EPJQLvRNGCVSYgr0xSotIyMjYkkPfV7eUFNOHa2cx+X3BkaJAIkoxJAWCBwfHxc9PdhBbFzSRgixzHfahVbNA2x/b8JKoi0QaKpE7KC8cVjGmOha06p5fAgq0aYSS8ITROLk5GRUq9UyAt/XUVgSGNyl0kNrMyBHchCUeSwJejotKSkp+VBh7F9fIy7OdlOyMwAAAABJRU5ErkJggg=="> src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAG0AAAAfCAMAAADazLOuAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAABLUExURQAAAAB81gCU/zKq///mo7sWMN8bO+ZIYtZaAP9rAP+HMsCiG+TAIOnMS0KqNU7KPnLUZOrq6v///4CAgGhoaL+/v6CgoExMTAAAAAlm4O8AAAAZdFJOU////////////////////////////////wABNAq3AAAACXBIWXMAAA7DAAAOwwHHb6hkAAACN0lEQVRIS73VjVLCMBAEYIr8CYKkrdj3f1J37zaXFCpTO+piaDgbPq9px9VQ0qyrvKj4q6m0Zr1h+M7xF1zRmnWzqV9/0d2jttGotO1uv9dUObwej5oqp7fzWVPl8n69aprzoOUUbbvdIbV3OLwitXc6vSG1d7m8I3feSEN0j2CeNbOY4MxigjOLCc4sZsTV2l1cCyy4wIILLLjAxtykltq2rbTU+qi01N5rXNO2leaFORoija2l5MM5a02ac9Ya16Sk5tgaPrUpjZub0BL6YqSxKwbH77XUUmSkJXSl8QtaMuyJhq5maL5nTKVpZC13VmtMpTFT2g4vJjTuGfMzzXftiUZnhdtgb1xofvypRon5TjNnxYN9zJo6K5ruSIzQtGuVZn0x91rKvdHBvm39E7SyZ4y06Gz8BDBFKzsXmhcwyfsGZ9VpbhoiCinaxPNmGWmWWrNU2jB0q6HvOhN1JUtCixQtp2g51ZVUXIPS2RMAD++T2nY/DrDjOMDO4wC7jmNYj3d73nrXug8Yt9uNB8xNU1cKNXWlUFNXCjV1pZhGTE83m2vWfYf/NGj4Bg1zu5JD3/MnH5ZWfLOksbmGWGjgXMN5/C2GXYGFFW9Nmtle6Xut0Gm+JsayCj8z0nhjGvYJzVf4aSzmNYsr+u7Q2JIdoX3YOQjOslmsW1jJ3120nE9gfo79hTaNdcsqVR610lvO47pllae9ReZ805zKo2a3iaY5c75pTmVCA6dJ5H7N0sr/asPwBehb7ifEhusRAAAAAElFTkSuQmCC">
<div class="main"><h1>Welcome to WLED!</h1><h3> <div class="main"><h1>Welcome to WLED!</h1><h3>
Thank you for installing my application!</h3><b>Next steps:</b><br><br> Thank you for installing my application!</h3><b>Next steps:</b><br><br>
Connect the module to your local WiFi here!<br><button Connect the module to your local WiFi here!<br><button
@ -95,7 +95,7 @@ content="width=device-width" name="viewport"><meta name="theme-color"
content="#222222"><title>Not found</title><style> content="#222222"><title>Not found</title><style>
body{font-family:Verdana,Helvetica,sans-serif;text-align:center;background-color:#222;margin:0;color:#fff}img{width:400px;max-width:50%;image-rendering:pixelated;image-rendering:crisp-edges;margin:25px 0 -10px 0}button{outline:0;cursor:pointer;padding:8px;margin:10px;width:230px;text-transform:uppercase;font-family:helvetica;font-size:19px;background-color:#333;color:#fff;border:0 solid #fff;border-radius:25px} body{font-family:Verdana,Helvetica,sans-serif;text-align:center;background-color:#222;margin:0;color:#fff}img{width:400px;max-width:50%;image-rendering:pixelated;image-rendering:crisp-edges;margin:25px 0 -10px 0}button{outline:0;cursor:pointer;padding:8px;margin:10px;width:230px;text-transform:uppercase;font-family:helvetica;font-size:19px;background-color:#333;color:#fff;border:0 solid #fff;border-radius:25px}
</style></head><body><img alt="" </style></head><body><img alt=""
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsEAAA7BAbiRa+0AAAFMSURBVFhH7ZTfbYNADMaPKDNUrJA85CEjdIjOgNQV+sASlZgmI/AIK6AuQfngnDrmjtpHItQ/P+l0juHsz2cH9+fJ/G7nreldfnDnp+ln/ZIlxbIfQmIwJOekCrEJ8FUvASEWEXoBiuSERcTO75uhuwFWff86bi57n3ZC+rW3YLqB5rn11ldCEPNr2LwFJgHHy8G1bTsu3oKYX4N5BrQ8ZAYewSoBGDjr0ElWCUC/rT2X7MqynL7tG4Dc45BwEYM9H5w7DqHMdfNCURR9nue3Iobk55MtOYeLoOQ8vmoG6o+0FaLrOm9FwC3wayLgx5I2WHpGIGYorulfgPYQ3AZLz4hQ9TMBVVVleJGrRUWz2YgQOg8bPjzzrit7vwcRQb5NTiARRPPzMYItoCpoWZITMkao+mRkddpqQ6z6FN+DfwFJrOm55GfewC/CuU/E4tQYg7BPYQAAAABJRU5ErkJggg=="> src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAbUExURQAAAAB81gCU/zKq/////9bW1oCAgGhoaAAAAGPLX8AAAAAJdFJOU///////////AFNPeBIAAAAJcEhZcwAADsAAAA7AAWrWiQkAAACdSURBVDhPxc9bDoUgEANQebP/FUuHMjBGY/B+3EYR7RH0qC/ZBc6HwCljgHO+xZIVSI2sYgHaG7EBWh8jWoxTrCBFdDJ+BD4lbIHxAcz8APAVLTsrZE4eQD5qzt3cAFTYokC4YCN9Gybgu4yAQtBFLQXHuHABA7JMeOEC/E0W5uy9gv4vo5QHK2i7yq2C8UABM4HmL+CSTXCTF1DrCX6+Gp9zB5dsAAAAAElFTkSuQmCC">
<h1>404 Not Found</h1><b>Akemi does not know where you are headed...</b><br><br> <h1>404 Not Found</h1><b>Akemi does not know where you are headed...</b><br><br>
<button onclick='window.location.href="/sliders"'>Back to controls</button> <button onclick='window.location.href="/sliders"'>Back to controls</button>
</body></html>)====="; </body></html>)=====";

File diff suppressed because one or more lines are too long

View File

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

View File

@ -6,7 +6,7 @@
* JSON API (De)serialization * JSON API (De)serialization
*/ */
void deserializeSegment(JsonObject elem, byte it, bool fromPlaylist) void deserializeSegment(JsonObject elem, byte it, byte presetId)
{ {
byte id = elem["id"] | it; byte id = elem["id"] | it;
if (id < strip.getMaxSegments()) if (id < strip.getMaxSegments())
@ -127,15 +127,18 @@ void deserializeSegment(JsonObject elem, byte it, bool fromPlaylist)
//temporary, strip object gets updated via colorUpdated() //temporary, strip object gets updated via colorUpdated()
if (id == strip.getMainSegmentId()) { if (id == strip.getMainSegmentId()) {
// it may be a good idea to also stop playlist if effect has changed byte effectPrev = effectCurrent;
if (!fromPlaylist && !elem["fx"].isNull()) unloadPlaylist(); effectCurrent = elem[F("fx")] | effectCurrent;
effectCurrent = elem["fx"] | effectCurrent; if (!presetId && effectCurrent != effectPrev) unloadPlaylist(); //stop playlist if active and FX changed manually
effectSpeed = elem[F("sx")] | effectSpeed; effectSpeed = elem[F("sx")] | effectSpeed;
effectIntensity = elem[F("ix")] | effectIntensity; effectIntensity = elem[F("ix")] | effectIntensity;
effectPalette = elem["pal"] | effectPalette; effectPalette = elem["pal"] | effectPalette;
} else { //permanent } else { //permanent
byte fx = elem["fx"] | seg.mode; byte fx = elem[F("fx")] | seg.mode;
if (fx != seg.mode && fx < strip.getModeCount()) strip.setMode(id, fx); if (fx != seg.mode && fx < strip.getModeCount()) {
strip.setMode(id, fx);
if (!presetId) unloadPlaylist(); //stop playlist if active and FX changed manually
}
seg.speed = elem[F("sx")] | seg.speed; seg.speed = elem[F("sx")] | seg.speed;
seg.intensity = elem[F("ix")] | seg.intensity; seg.intensity = elem[F("ix")] | seg.intensity;
seg.palette = elem["pal"] | seg.palette; seg.palette = elem["pal"] | seg.palette;
@ -190,7 +193,7 @@ void deserializeSegment(JsonObject elem, byte it, bool fromPlaylist)
} }
} }
bool deserializeState(JsonObject root, bool fromPlaylist) bool deserializeState(JsonObject root, byte presetId)
{ {
strip.applyToAllSelected = false; strip.applyToAllSelected = false;
bool stateResponse = root[F("v")] | false; bool stateResponse = root[F("v")] | false;
@ -202,13 +205,16 @@ bool deserializeState(JsonObject root, bool fromPlaylist)
if (root["on"].is<const char*>() && root["on"].as<const char*>()[0] == 't') toggleOnOff(); if (root["on"].is<const char*>() && root["on"].as<const char*>()[0] == 't') toggleOnOff();
int tr = root[F("transition")] | -1; int tr = -1;
if (!presetId || currentPlaylist < 0) { //do not apply transition time from preset if playlist active, as it would override playlist transition times
tr = root[F("transition")] | -1;
if (tr >= 0) if (tr >= 0)
{ {
transitionDelay = tr; transitionDelay = tr;
transitionDelay *= 100; transitionDelay *= 100;
transitionDelayTemp = transitionDelay; transitionDelayTemp = transitionDelay;
} }
}
tr = root[F("tt")] | -1; tr = root[F("tt")] | -1;
if (tr >= 0) if (tr >= 0)
@ -244,14 +250,7 @@ bool deserializeState(JsonObject root, bool fromPlaylist)
unsigned long timein = root[F("time")] | UINT32_MAX; //backup time source if NTP not synced unsigned long timein = root[F("time")] | UINT32_MAX; //backup time source if NTP not synced
if (timein != UINT32_MAX) { if (timein != UINT32_MAX) {
time_t prev = now(); setTimeFromAPI(timein);
if (millis() - ntpLastSyncTime > 50000000L) {
setTime(timein);
if (abs(now() - prev) > 60L) {
updateLocalTime();
calculateSunriseAndSunset();
}
}
if (presetsModifiedTime == 0) presetsModifiedTime = timein; if (presetsModifiedTime == 0) presetsModifiedTime = timein;
} }
@ -286,24 +285,30 @@ bool deserializeState(JsonObject root, bool fromPlaylist)
{ {
if (lowestActive == 99) lowestActive = s; if (lowestActive == 99) lowestActive = s;
if (sg.isSelected()) { if (sg.isSelected()) {
deserializeSegment(segVar, s, fromPlaylist); deserializeSegment(segVar, s, presetId);
didSet = true; didSet = true;
} }
} }
} }
if (!didSet && lowestActive < strip.getMaxSegments()) deserializeSegment(segVar, lowestActive, fromPlaylist); if (!didSet && lowestActive < strip.getMaxSegments()) deserializeSegment(segVar, lowestActive, presetId);
} else { //set only the segment with the specified ID } else { //set only the segment with the specified ID
deserializeSegment(segVar, id, fromPlaylist); deserializeSegment(segVar, it, presetId);
} }
} else { } else {
JsonArray segs = segVar.as<JsonArray>(); JsonArray segs = segVar.as<JsonArray>();
for (JsonObject elem : segs) for (JsonObject elem : segs)
{ {
deserializeSegment(elem, it, fromPlaylist); deserializeSegment(elem, it, presetId);
it++; it++;
} }
} }
#ifndef WLED_DISABLE_CRONIXIE
if (root["nx"].is<const char*>()) {
strncpy(cronixieDisplay, root["nx"], 6);
}
#endif
usermods.readFromJsonState(root); usermods.readFromJsonState(root);
int8_t ledmap = root[F("ledmap")] | -1; int8_t ledmap = root[F("ledmap")] | -1;
@ -320,7 +325,11 @@ bool deserializeState(JsonObject root, bool fromPlaylist)
deletePreset(ps); deletePreset(ps);
} }
ps = root["ps"] | -1; //load preset (clears state request!) ps = root["ps"] | -1; //load preset (clears state request!)
if (ps >= 0) {applyPreset(ps); return stateResponse;} if (ps >= 0) {
if (!presetId) unloadPlaylist(); //stop playlist if preset changed manually
applyPreset(ps);
return stateResponse;
}
//HTTP API commands //HTTP API commands
const char* httpwin = root["win"]; const char* httpwin = root["win"];
@ -333,7 +342,7 @@ bool deserializeState(JsonObject root, bool fromPlaylist)
JsonObject playlist = root[F("playlist")]; JsonObject playlist = root[F("playlist")];
if (!playlist.isNull()) { if (!playlist.isNull()) {
loadPlaylist(playlist); loadPlaylist(playlist, presetId);
noNotification = true; //do not notify both for this request and the first playlist entry noNotification = true; //do not notify both for this request and the first playlist entry
} }

View File

@ -16,6 +16,9 @@
/* /*
* Acquires time from NTP server * Acquires time from NTP server
*/ */
//#define WLED_DEBUG_NTP
#define NTP_SYNC_INTERVAL 42000UL //Get fresh NTP time about twice per day
Timezone* tz; Timezone* tz;
#define TZ_UTC 0 #define TZ_UTC 0
@ -144,9 +147,28 @@ void updateTimezone() {
tz = new Timezone(tcrDaylight, tcrStandard); tz = new Timezone(tcrDaylight, tcrStandard);
} }
void handleTime() {
handleNetworkTime();
toki.millisecond();
toki.setTick();
if (toki.isTick()) //true only in the first loop after a new second started
{
#ifdef WLED_DEBUG_NTP
Serial.print(F("TICK! "));
toki.printTime(toki.getTime());
#endif
updateLocalTime();
checkTimers();
checkCountdown();
handleOverlays();
}
}
void handleNetworkTime() void handleNetworkTime()
{ {
if (ntpEnabled && ntpConnected && millis() - ntpLastSyncTime > 50000000L && WLED_CONNECTED) if (ntpEnabled && ntpConnected && millis() - ntpLastSyncTime > (1000*NTP_SYNC_INTERVAL) && WLED_CONNECTED)
{ {
if (millis() - ntpPacketSentTime > 10000) if (millis() - ntpPacketSentTime > 10000)
{ {
@ -193,35 +215,52 @@ void sendNTPPacket()
bool checkNTPResponse() bool checkNTPResponse()
{ {
int cb = ntpUdp.parsePacket(); int cb = ntpUdp.parsePacket();
if (cb) { if (!cb) return false;
uint32_t ntpPacketReceivedTime = millis();
DEBUG_PRINT(F("NTP recv, l=")); DEBUG_PRINT(F("NTP recv, l="));
DEBUG_PRINTLN(cb); DEBUG_PRINTLN(cb);
byte pbuf[NTP_PACKET_SIZE]; byte pbuf[NTP_PACKET_SIZE];
ntpUdp.read(pbuf, NTP_PACKET_SIZE); // read the packet into the buffer ntpUdp.read(pbuf, NTP_PACKET_SIZE); // read the packet into the buffer
unsigned long highWord = word(pbuf[40], pbuf[41]); Toki::Time arrived = toki.fromNTP(pbuf + 32);
unsigned long lowWord = word(pbuf[42], pbuf[43]); Toki::Time departed = toki.fromNTP(pbuf + 40);
if (highWord == 0 && lowWord == 0) return false; if (departed.sec == 0) return false;
//basic half roundtrip estimation
uint32_t serverDelay = toki.msDifference(arrived, departed);
uint32_t offset = (ntpPacketReceivedTime - ntpPacketSentTime - serverDelay) >> 1;
#ifdef WLED_DEBUG_NTP
//the time the packet departed the NTP server
toki.printTime(departed);
#endif
unsigned long secsSince1900 = highWord << 16 | lowWord; toki.adjust(departed, offset);
toki.setTime(departed, TOKI_TS_NTP);
DEBUG_PRINT(F("Unix time = ")); #ifdef WLED_DEBUG_NTP
unsigned long epoch = secsSince1900 - 2208988799UL; //subtract 70 years -1sec (on avg. more precision) Serial.print("Arrived: ");
setTime(epoch); toki.printTime(arrived);
DEBUG_PRINTLN(epoch); Serial.print("Time: ");
if (countdownTime - now() > 0) countdownOverTriggered = false; toki.printTime(departed);
Serial.print("Roundtrip: ");
Serial.println(ntpPacketReceivedTime - ntpPacketSentTime);
Serial.print("Offset: ");
Serial.println(offset);
Serial.print("Serverdelay: ");
Serial.println(serverDelay);
#endif
if (countdownTime - toki.second() > 0) countdownOverTriggered = false;
// if time changed re-calculate sunrise/sunset // if time changed re-calculate sunrise/sunset
updateLocalTime(); updateLocalTime();
calculateSunriseAndSunset(); calculateSunriseAndSunset();
return true; return true;
} }
return false;
}
void updateLocalTime() void updateLocalTime()
{ {
if (currentTimezone != tzCurrent) updateTimezone(); if (currentTimezone != tzCurrent) updateTimezone();
unsigned long tmc = now()+ utcOffsetSecs; unsigned long tmc = toki.second()+ utcOffsetSecs;
localTime = tz->toLocal(tmc); localTime = tz->toLocal(tmc);
} }
@ -245,13 +284,13 @@ void setCountdown()
{ {
if (currentTimezone != tzCurrent) updateTimezone(); if (currentTimezone != tzCurrent) updateTimezone();
countdownTime = tz->toUTC(getUnixTime(countdownHour, countdownMin, countdownSec, countdownDay, countdownMonth, countdownYear)); countdownTime = tz->toUTC(getUnixTime(countdownHour, countdownMin, countdownSec, countdownDay, countdownMonth, countdownYear));
if (countdownTime - now() > 0) countdownOverTriggered = false; if (countdownTime - toki.second() > 0) countdownOverTriggered = false;
} }
//returns true if countdown just over //returns true if countdown just over
bool checkCountdown() bool checkCountdown()
{ {
unsigned long n = now(); unsigned long n = toki.second();
if (countdownMode) localTime = countdownTime - n + utcOffsetSecs; if (countdownMode) localTime = countdownTime - n + utcOffsetSecs;
if (n > countdownTime) { if (n > countdownTime) {
if (countdownMode) localTime = n - countdownTime + utcOffsetSecs; if (countdownMode) localTime = n - countdownTime + utcOffsetSecs;
@ -410,3 +449,19 @@ void calculateSunriseAndSunset() {
} }
} }
} }
//time from JSON and HTTP API
void setTimeFromAPI(uint32_t timein) {
if (timein == 0 || timein == UINT32_MAX) return;
uint32_t prev = toki.second();
//only apply if more accurate or there is a significant difference to the "more accurate" time source
uint32_t diff = (timein > prev) ? timein - prev : prev - timein;
if (toki.getTimeSource() > TOKI_TS_JSON && diff < 60U) return;
toki.setTime(timein, TOKI_NO_MS_ACCURACY, TOKI_TS_JSON);
if (diff >= 60U) {
updateLocalTime();
calculateSunriseAndSunset();
}
if (presetsModifiedTime == 0) presetsModifiedTime = timein;
}

View File

@ -6,29 +6,24 @@
void initCronixie() void initCronixie()
{ {
if (overlayCurrent == 3 && !cronixieInit) if (overlayCurrent == 3 && dP[0] == 255) //if dP[0] is 255, cronixie is not yet init'ed
{ {
setCronixie(); setCronixie();
strip.getSegment(0).grouping = 10; //10 LEDs per digit strip.getSegment(0).grouping = 10; //10 LEDs per digit
cronixieInit = true; } else if (dP[0] < 255 && overlayCurrent != 3)
} else if (cronixieInit && overlayCurrent != 3)
{ {
strip.getSegment(0).grouping = 1; strip.getSegment(0).grouping = 1;
cronixieInit = false; dP[0] = 255;
} }
} }
void handleOverlays() void handleOverlays()
{
if (millis() - overlayRefreshedTime > overlayRefreshMs)
{ {
initCronixie(); initCronixie();
updateLocalTime(); if (overlayCurrent == 3) {
checkTimers(); _overlayCronixie();//Diamex cronixie clock kit
checkCountdown(); strip.trigger();
if (overlayCurrent == 3) _overlayCronixie();//Diamex cronixie clock kit
overlayRefreshedTime = millis();
} }
} }
@ -73,15 +68,14 @@ void _overlayAnalogClock()
if (!analogClockSecondsTrail) strip.setPixelColor(secondPixel, 0xFF0000); if (!analogClockSecondsTrail) strip.setPixelColor(secondPixel, 0xFF0000);
strip.setPixelColor(minutePixel, 0x00FF00); strip.setPixelColor(minutePixel, 0x00FF00);
strip.setPixelColor(hourPixel, 0x0000FF); strip.setPixelColor(hourPixel, 0x0000FF);
overlayRefreshMs = 998;
} }
void _overlayAnalogCountdown() void _overlayAnalogCountdown()
{ {
if ((unsigned long)now() < countdownTime) if ((unsigned long)toki.second() < countdownTime)
{ {
long diff = countdownTime - now(); long diff = countdownTime - toki.second();
double pval = 60; double pval = 60;
if (diff > 31557600L) //display in years if more than 365 days if (diff > 31557600L) //display in years if more than 365 days
{ {
@ -115,7 +109,6 @@ void _overlayAnalogCountdown()
strip.setRange(analogClock12pixel, analogClock12pixel + pixelCnt, ((uint32_t)colSec[3] << 24)| ((uint32_t)colSec[0] << 16) | ((uint32_t)colSec[1] << 8) | colSec[2]); strip.setRange(analogClock12pixel, analogClock12pixel + pixelCnt, ((uint32_t)colSec[3] << 24)| ((uint32_t)colSec[0] << 16) | ((uint32_t)colSec[1] << 8) | colSec[2]);
} }
} }
overlayRefreshMs = 998;
} }
@ -220,8 +213,6 @@ void setCronixie()
DEBUG_PRINT("cset "); DEBUG_PRINT("cset ");
DEBUG_PRINTLN(cronixieDisplay); DEBUG_PRINTLN(cronixieDisplay);
overlayRefreshMs = 1997; //Only refresh every 2secs if no seconds are displayed
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
dP[i] = 10; dP[i] = 10;
@ -241,8 +232,8 @@ void setCronixie()
case 'a': dP[i] = 58; i++; break; case 'a': dP[i] = 58; i++; break;
case 'm': dP[i] = 74 + getSameCodeLength('m',i,cronixieDisplay); i = i+dP[i]-74; break; case 'm': dP[i] = 74 + getSameCodeLength('m',i,cronixieDisplay); i = i+dP[i]-74; break;
case 'M': dP[i] = 24 + getSameCodeLength('M',i,cronixieDisplay); i = i+dP[i]-24; break; case 'M': dP[i] = 24 + getSameCodeLength('M',i,cronixieDisplay); i = i+dP[i]-24; break;
case 's': dP[i] = 80 + getSameCodeLength('s',i,cronixieDisplay); i = i+dP[i]-80; overlayRefreshMs = 497; break; //refresh more often bc. of secs case 's': dP[i] = 80 + getSameCodeLength('s',i,cronixieDisplay); i = i+dP[i]-80; break; //refresh more often bc. of secs
case 'S': dP[i] = 30 + getSameCodeLength('S',i,cronixieDisplay); i = i+dP[i]-30; overlayRefreshMs = 497; break; case 'S': dP[i] = 30 + getSameCodeLength('S',i,cronixieDisplay); i = i+dP[i]-30; break;
case 'Y': dP[i] = 36 + getSameCodeLength('Y',i,cronixieDisplay); i = i+dP[i]-36; break; case 'Y': dP[i] = 36 + getSameCodeLength('Y',i,cronixieDisplay); i = i+dP[i]-36; break;
case 'y': dP[i] = 86 + getSameCodeLength('y',i,cronixieDisplay); i = i+dP[i]-86; break; case 'y': dP[i] = 86 + getSameCodeLength('y',i,cronixieDisplay); i = i+dP[i]-86; break;
case 'I': dP[i] = 39 + getSameCodeLength('I',i,cronixieDisplay); i = i+dP[i]-39; break; //Month. Don't ask me why month and minute both start with M. case 'I': dP[i] = 39 + getSameCodeLength('I',i,cronixieDisplay); i = i+dP[i]-39; break; //Month. Don't ask me why month and minute both start with M.

View File

@ -5,18 +5,24 @@
*/ */
typedef struct PlaylistEntry { typedef struct PlaylistEntry {
uint8_t preset; uint8_t preset; //ID of the preset to apply
uint16_t dur; uint16_t dur; //Duration of the entry (in tenths of seconds)
uint16_t tr; uint16_t tr; //Duration of the transition TO this entry (in tenths of seconds)
} ple; } ple;
bool playlistEndless = false; byte playlistRepeat = 1; //how many times to repeat the playlist (0 = infinitely)
int8_t playlistRepeat = 1; byte playlistEndPreset = 0; //what preset to apply after playlist end (0 = stay on last preset)
byte playlistEndPreset = 0; byte playlistOptions = 0; //bit 0: shuffle playlist after each iteration. bits 1-7 TBD
PlaylistEntry *playlistEntries = nullptr; PlaylistEntry *playlistEntries = nullptr;
byte playlistLen; byte playlistLen; //number of playlist entries
int8_t playlistIndex = -1; int8_t playlistIndex = -1;
uint16_t playlistEntryDur = 0; uint16_t playlistEntryDur = 0; //duration of the current entry in tenths of seconds
//values we need to keep about the parent playlist while inside sub-playlist
//int8_t parentPlaylistIndex = -1;
//byte parentPlaylistRepeat = 0;
//byte parentPlaylistPresetId = 0; //for re-loading
void shufflePlaylist() { void shufflePlaylist() {
@ -42,12 +48,12 @@ void unloadPlaylist() {
playlistEntries = nullptr; playlistEntries = nullptr;
} }
currentPlaylist = playlistIndex = -1; currentPlaylist = playlistIndex = -1;
playlistLen = playlistEntryDur = 0; playlistLen = playlistEntryDur = playlistOptions = 0;
DEBUG_PRINTLN(F("Playlist unloaded.")); DEBUG_PRINTLN(F("Playlist unloaded."));
} }
void loadPlaylist(JsonObject playlistObj) { void loadPlaylist(JsonObject playlistObj, byte presetId) {
unloadPlaylist(); unloadPlaylist();
JsonArray presets = playlistObj["ps"]; JsonArray presets = playlistObj["ps"];
@ -68,12 +74,12 @@ void loadPlaylist(JsonObject playlistObj) {
it = 0; it = 0;
JsonArray durations = playlistObj["dur"]; JsonArray durations = playlistObj["dur"];
if (durations.isNull()) { if (durations.isNull()) {
playlistEntries[0].dur = playlistObj["dur"] | 100; playlistEntries[0].dur = playlistObj["dur"] | 100; //10 seconds as fallback
it = 1; it = 1;
} else { } else {
for (int dur : durations) { for (int dur : durations) {
if (it >= playlistLen) break; if (it >= playlistLen) break;
playlistEntries[it].dur = (dur > 0) ? dur : presetCycleTime; playlistEntries[it].dur = (dur > 1) ? dur : 100;
it++; it++;
} }
} }
@ -93,12 +99,19 @@ void loadPlaylist(JsonObject playlistObj) {
} }
for (int i = it; i < playlistLen; i++) playlistEntries[i].tr = playlistEntries[it -1].tr; for (int i = it; i < playlistLen; i++) playlistEntries[i].tr = playlistEntries[it -1].tr;
playlistRepeat = playlistObj[F("repeat")] | 0; int rep = playlistObj[F("repeat")];
bool shuffle = false;
if (rep < 0) { //support negative values as infinite + shuffle
rep = 0; shuffle = true;
}
playlistRepeat = rep;
if (playlistRepeat > 0) playlistRepeat++; //add one extra repetition immediately since it will be deducted on first start
playlistEndPreset = playlistObj[F("end")] | 0; playlistEndPreset = playlistObj[F("end")] | 0;
shuffle = shuffle || playlistObj["r"];
if (shuffle) playlistOptions += PL_OPTION_SHUFFLE;
if (playlistRepeat <= 0) playlistRepeat--; // make it endless (-2 == endless & random) currentPlaylist = presetId;
currentPlaylist = 0; //TODO here we need the preset ID where the playlist is saved
DEBUG_PRINTLN(F("Playlist loaded.")); DEBUG_PRINTLN(F("Playlist loaded."));
} }
@ -112,21 +125,21 @@ void handlePlaylist() {
++playlistIndex %= playlistLen; // -1 at 1st run (limit to playlistLen) ++playlistIndex %= playlistLen; // -1 at 1st run (limit to playlistLen)
if (!playlistRepeat && !playlistIndex) { //stop if repeat == 0 and restart of playlist // playlist roll-over
if (!playlistIndex) {
if (playlistRepeat == 1) { //stop if all repetitions are done
unloadPlaylist(); unloadPlaylist();
if (playlistEndPreset) applyPreset(playlistEndPreset); if (playlistEndPreset) applyPreset(playlistEndPreset);
return; return;
} }
// playlist roll-over if (playlistRepeat > 1) playlistRepeat--; // decrease repeat count on each index reset if not an endless playlist
if (!playlistIndex) { // playlistRepeat == 0: endless loop
// playlistRepeat < 0 => endless loop if (playlistOptions & PL_OPTION_SHUFFLE) shufflePlaylist(); // shuffle playlist and start over
if (playlistRepeat > 0) playlistRepeat--; // decrease repeat count on each index reset if not an endless playlist
if (playlistRepeat < -1) shufflePlaylist(); // shuffle playlist and start over
} }
jsonTransitionOnce = true; jsonTransitionOnce = true;
transitionDelayTemp = playlistEntries[playlistIndex].tr * 100; transitionDelayTemp = playlistEntries[playlistIndex].tr * 100;
playlistEntryDur = playlistEntries[playlistIndex].dur; playlistEntryDur = playlistEntries[playlistIndex].dur;
applyPreset(playlistEntries[playlistIndex].preset, true); applyPreset(playlistEntries[playlistIndex].preset);
} }
} }

View File

@ -4,17 +4,17 @@
* Methods to handle saving and loading presets to/from the filesystem * Methods to handle saving and loading presets to/from the filesystem
*/ */
bool applyPreset(byte index, bool fromPlaylist) bool applyPreset(byte index)
{ {
if (index == 0) return false; if (index == 0) return false;
if (fileDoc) { // from POST "/json" handler (wled_server.cpp) if (fileDoc) {
errorFlag = readObjectFromFileUsingId("/presets.json", index, fileDoc) ? ERR_NONE : ERR_FS_PLOAD; errorFlag = readObjectFromFileUsingId("/presets.json", index, fileDoc) ? ERR_NONE : ERR_FS_PLOAD;
JsonObject fdo = fileDoc->as<JsonObject>(); JsonObject fdo = fileDoc->as<JsonObject>();
if (fdo["ps"] == index) fdo.remove("ps"); //remove load request for same presets to prevent recursive crash if (fdo["ps"] == index) fdo.remove("ps"); //remove load request for same presets to prevent recursive crash
#ifdef WLED_DEBUG_FS #ifdef WLED_DEBUG_FS
serializeJson(*fileDoc, Serial); serializeJson(*fileDoc, Serial);
#endif #endif
deserializeState(fdo, fromPlaylist); deserializeState(fdo, index);
} else { } else {
DEBUGFS_PRINTLN(F("Make read buf")); DEBUGFS_PRINTLN(F("Make read buf"));
DynamicJsonDocument fDoc(JSON_BUFFER_SIZE); DynamicJsonDocument fDoc(JSON_BUFFER_SIZE);
@ -24,7 +24,7 @@ bool applyPreset(byte index, bool fromPlaylist)
#ifdef WLED_DEBUG_FS #ifdef WLED_DEBUG_FS
serializeJson(fDoc, Serial); serializeJson(fDoc, Serial);
#endif #endif
deserializeState(fdo, fromPlaylist); deserializeState(fdo, index);
} }
if (!errorFlag) { if (!errorFlag) {
@ -35,6 +35,7 @@ bool applyPreset(byte index, bool fromPlaylist)
return false; return false;
} }
//persist=false is not currently honored
void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj) void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj)
{ {
if (index == 0 || index > 250) return; if (index == 0 || index > 250) return;
@ -55,6 +56,7 @@ void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj)
DEBUGFS_PRINTLN(F("Reuse recv buffer")); DEBUGFS_PRINTLN(F("Reuse recv buffer"));
sObj.remove(F("psave")); sObj.remove(F("psave"));
sObj.remove(F("v")); sObj.remove(F("v"));
if (!sObj["o"]) { if (!sObj["o"]) {
DEBUGFS_PRINTLN(F("Save current state")); DEBUGFS_PRINTLN(F("Save current state"));
serializeState(sObj, true, sObj["ib"], sObj["sb"]); serializeState(sObj, true, sObj["ib"], sObj["sb"]);
@ -68,13 +70,13 @@ void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj)
writeObjectToFileUsingId("/presets.json", index, fileDoc); writeObjectToFileUsingId("/presets.json", index, fileDoc);
} }
presetsModifiedTime = now(); //unix time presetsModifiedTime = toki.second(); //unix time
updateFSInfo(); updateFSInfo();
} }
void deletePreset(byte index) { void deletePreset(byte index) {
StaticJsonDocument<24> empty; StaticJsonDocument<24> empty;
writeObjectToFileUsingId("/presets.json", index, &empty); writeObjectToFileUsingId("/presets.json", index, &empty);
presetsModifiedTime = now(); //unix time presetsModifiedTime = toki.second(); //unix time
updateFSInfo(); updateFSInfo();
} }

View File

@ -78,9 +78,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
int t = 0; int t = 0;
if (rlyPin>=0 && pinManager.isPinAllocated(rlyPin)) pinManager.deallocatePin(rlyPin); if (rlyPin>=0 && pinManager.isPinAllocated(rlyPin)) pinManager.deallocatePin(rlyPin);
#ifndef WLED_DISABLE_INFRARED
if (irPin>=0 && pinManager.isPinAllocated(irPin)) pinManager.deallocatePin(irPin); if (irPin>=0 && pinManager.isPinAllocated(irPin)) pinManager.deallocatePin(irPin);
#endif
for (uint8_t s=0; s<WLED_MAX_BUTTONS; s++) for (uint8_t s=0; s<WLED_MAX_BUTTONS; s++)
if (btnPin[s]>=0 && pinManager.isPinAllocated(btnPin[s])) if (btnPin[s]>=0 && pinManager.isPinAllocated(btnPin[s]))
pinManager.deallocatePin(btnPin[s]); pinManager.deallocatePin(btnPin[s]);
@ -127,7 +125,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
ledCount = request->arg(F("LC")).toInt(); ledCount = request->arg(F("LC")).toInt();
// upate other pins // upate other pins
#ifndef WLED_DISABLE_INFRARED
int hw_ir_pin = request->arg(F("IR")).toInt(); int hw_ir_pin = request->arg(F("IR")).toInt();
if (pinManager.allocatePin(hw_ir_pin,false)) { if (pinManager.allocatePin(hw_ir_pin,false)) {
irPin = hw_ir_pin; irPin = hw_ir_pin;
@ -135,7 +132,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
irPin = -1; irPin = -1;
} }
irEnabled = request->arg(F("IT")).toInt(); irEnabled = request->arg(F("IT")).toInt();
#endif
int hw_rly_pin = request->arg(F("RL")).toInt(); int hw_rly_pin = request->arg(F("RL")).toInt();
if (pinManager.allocatePin(hw_rly_pin,true)) { if (pinManager.allocatePin(hw_rly_pin,true)) {
@ -202,8 +198,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
//SYNC //SYNC
if (subPage == 4) if (subPage == 4)
{ {
//buttonType = request->arg(F("BT")).toInt();
//irEnabled = request->arg(F("IR")).toInt();
int t = request->arg(F("UP")).toInt(); int t = request->arg(F("UP")).toInt();
if (t > 0) udpPort = t; if (t > 0) udpPort = t;
t = request->arg(F("U2")).toInt(); t = request->arg(F("U2")).toInt();
@ -814,14 +808,14 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
//set time (unix timestamp) //set time (unix timestamp)
pos = req.indexOf(F("ST=")); pos = req.indexOf(F("ST="));
if (pos > 0) { if (pos > 0) {
setTime(getNumVal(&req, pos)); setTimeFromAPI(getNumVal(&req, pos));
} }
//set countdown goal (unix timestamp) //set countdown goal (unix timestamp)
pos = req.indexOf(F("CT=")); pos = req.indexOf(F("CT="));
if (pos > 0) { if (pos > 0) {
countdownTime = getNumVal(&req, pos); countdownTime = getNumVal(&req, pos);
if (countdownTime - now() > 0) countdownOverTriggered = false; if (countdownTime - toki.second() > 0) countdownOverTriggered = false;
} }
pos = req.indexOf(F("LO=")); pos = req.indexOf(F("LO="));
@ -841,7 +835,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
pos = req.indexOf(F("NX=")); //sets digits to code pos = req.indexOf(F("NX=")); //sets digits to code
if (pos > 0) { if (pos > 0) {
strlcpy(cronixieDisplay, req.substring(pos + 3, pos + 9).c_str(), 6); strlcpy(cronixieDisplay, req.substring(pos + 3, pos + 9).c_str(), 7);
setCronixie(); setCronixie();
} }
@ -849,7 +843,6 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
if (pos > 0) //sets backlight if (pos > 0) //sets backlight
{ {
cronixieBacklight = (req.charAt(pos+3) != '0'); cronixieBacklight = (req.charAt(pos+3) != '0');
overlayRefreshedTime = 0;
} }
#endif #endif

View File

@ -0,0 +1,218 @@
/*
* DS1307RTC.h - library for DS1307 RTC
Copyright (c) Michael Margolis 2009
This library is intended to be uses with Arduino Time library functions
The library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
30 Dec 2009 - Initial release
5 Sep 2011 updated for Arduino 1.0
*/
#if defined (__AVR_ATtiny84__) || defined(__AVR_ATtiny85__) || (__AVR_ATtiny2313__)
#include <TinyWireM.h>
#define Wire TinyWireM
#else
#include <Wire.h>
#endif
#include "DS1307RTC.h"
#define DS1307_CTRL_ID 0x68
DS1307RTC::DS1307RTC()
{
Wire.begin();
}
// PUBLIC FUNCTIONS
time_t DS1307RTC::get() // Aquire data from buffer and convert to time_t
{
tmElements_t tm;
if (read(tm) == false) return 0;
return(makeTime(tm));
}
bool DS1307RTC::set(time_t t)
{
tmElements_t tm;
breakTime(t, tm);
return write(tm);
}
// Aquire data from the RTC chip in BCD format
bool DS1307RTC::read(tmElements_t &tm)
{
uint8_t sec;
Wire.beginTransmission(DS1307_CTRL_ID);
#if ARDUINO >= 100
Wire.write((uint8_t)0x00);
#else
Wire.send(0x00);
#endif
if (Wire.endTransmission() != 0) {
exists = false;
return false;
}
exists = true;
// request the 7 data fields (secs, min, hr, dow, date, mth, yr)
Wire.requestFrom(DS1307_CTRL_ID, tmNbrFields);
if (Wire.available() < tmNbrFields) return false;
#if ARDUINO >= 100
sec = Wire.read();
tm.Second = bcd2dec(sec & 0x7f);
tm.Minute = bcd2dec(Wire.read() );
tm.Hour = bcd2dec(Wire.read() & 0x3f); // mask assumes 24hr clock
tm.Wday = bcd2dec(Wire.read() );
tm.Day = bcd2dec(Wire.read() );
tm.Month = bcd2dec(Wire.read() );
tm.Year = y2kYearToTm((bcd2dec(Wire.read())));
#else
sec = Wire.receive();
tm.Second = bcd2dec(sec & 0x7f);
tm.Minute = bcd2dec(Wire.receive() );
tm.Hour = bcd2dec(Wire.receive() & 0x3f); // mask assumes 24hr clock
tm.Wday = bcd2dec(Wire.receive() );
tm.Day = bcd2dec(Wire.receive() );
tm.Month = bcd2dec(Wire.receive() );
tm.Year = y2kYearToTm((bcd2dec(Wire.receive())));
#endif
if (sec & 0x80) return false; // clock is halted
return true;
}
bool DS1307RTC::write(tmElements_t &tm)
{
// To eliminate any potential race conditions,
// stop the clock before writing the values,
// then restart it after.
Wire.beginTransmission(DS1307_CTRL_ID);
#if ARDUINO >= 100
Wire.write((uint8_t)0x00); // reset register pointer
Wire.write((uint8_t)0x80); // Stop the clock. The seconds will be written last
Wire.write(dec2bcd(tm.Minute));
Wire.write(dec2bcd(tm.Hour)); // sets 24 hour format
Wire.write(dec2bcd(tm.Wday));
Wire.write(dec2bcd(tm.Day));
Wire.write(dec2bcd(tm.Month));
Wire.write(dec2bcd(tmYearToY2k(tm.Year)));
#else
Wire.send(0x00); // reset register pointer
Wire.send(0x80); // Stop the clock. The seconds will be written last
Wire.send(dec2bcd(tm.Minute));
Wire.send(dec2bcd(tm.Hour)); // sets 24 hour format
Wire.send(dec2bcd(tm.Wday));
Wire.send(dec2bcd(tm.Day));
Wire.send(dec2bcd(tm.Month));
Wire.send(dec2bcd(tmYearToY2k(tm.Year)));
#endif
if (Wire.endTransmission() != 0) {
exists = false;
return false;
}
exists = true;
// Now go back and set the seconds, starting the clock back up as a side effect
Wire.beginTransmission(DS1307_CTRL_ID);
#if ARDUINO >= 100
Wire.write((uint8_t)0x00); // reset register pointer
Wire.write(dec2bcd(tm.Second)); // write the seconds, with the stop bit clear to restart
#else
Wire.send(0x00); // reset register pointer
Wire.send(dec2bcd(tm.Second)); // write the seconds, with the stop bit clear to restart
#endif
if (Wire.endTransmission() != 0) {
exists = false;
return false;
}
exists = true;
return true;
}
unsigned char DS1307RTC::isRunning()
{
Wire.beginTransmission(DS1307_CTRL_ID);
#if ARDUINO >= 100
Wire.write((uint8_t)0x00);
#else
Wire.send(0x00);
#endif
Wire.endTransmission();
// Just fetch the seconds register and check the top bit
Wire.requestFrom(DS1307_CTRL_ID, 1);
#if ARDUINO >= 100
return !(Wire.read() & 0x80);
#else
return !(Wire.receive() & 0x80);
#endif
}
void DS1307RTC::setCalibration(char calValue)
{
unsigned char calReg = abs(calValue) & 0x1f;
if (calValue >= 0) calReg |= 0x20; // S bit is positive to speed up the clock
Wire.beginTransmission(DS1307_CTRL_ID);
#if ARDUINO >= 100
Wire.write((uint8_t)0x07); // Point to calibration register
Wire.write(calReg);
#else
Wire.send(0x07); // Point to calibration register
Wire.send(calReg);
#endif
Wire.endTransmission();
}
char DS1307RTC::getCalibration()
{
Wire.beginTransmission(DS1307_CTRL_ID);
#if ARDUINO >= 100
Wire.write((uint8_t)0x07);
#else
Wire.send(0x07);
#endif
Wire.endTransmission();
Wire.requestFrom(DS1307_CTRL_ID, 1);
#if ARDUINO >= 100
unsigned char calReg = Wire.read();
#else
unsigned char calReg = Wire.receive();
#endif
char out = calReg & 0x1f;
if (!(calReg & 0x20)) out = -out; // S bit clear means a negative value
return out;
}
// PRIVATE FUNCTIONS
// Convert Decimal to Binary Coded Decimal (BCD)
uint8_t DS1307RTC::dec2bcd(uint8_t num)
{
return ((num/10 * 16) + (num % 10));
}
// Convert Binary Coded Decimal (BCD) to Decimal
uint8_t DS1307RTC::bcd2dec(uint8_t num)
{
return ((num/16 * 10) + (num % 16));
}
bool DS1307RTC::exists = false;
DS1307RTC RTC = DS1307RTC(); // create an instance for the user

View File

@ -0,0 +1,40 @@
/*
* DS1307RTC.h - library for DS1307 RTC
* This library is intended to be uses with Arduino Time library functions
*/
#ifndef DS1307RTC_h
#define DS1307RTC_h
#include "TimeLib.h"
// library interface description
class DS1307RTC
{
// user-accessible "public" interface
public:
DS1307RTC();
static time_t get();
static bool set(time_t t);
static bool read(tmElements_t &tm);
static bool write(tmElements_t &tm);
static bool chipPresent() { return exists; }
static unsigned char isRunning();
static void setCalibration(char calValue);
static char getCalibration();
private:
static bool exists;
static uint8_t dec2bcd(uint8_t num);
static uint8_t bcd2dec(uint8_t num);
};
#ifdef RTC
#undef RTC // workaround for Arduino Due, which defines "RTC"...
#endif
extern DS1307RTC RTC;
#endif

View File

@ -1,5 +1,9 @@
Readme file for Arduino Time Library Readme file for Arduino Time Library
! MODIFIED DISTRIBUTION FOR WLED
All timekeeping functions are removed, only conversion functions used.
Please see https://github.com/PaulStoffregen/Time for the full, original library
Time is a library that provides timekeeping functionality for Arduino. Time is a library that provides timekeeping functionality for Arduino.
The code is derived from the Playground DateTime library but is updated The code is derived from the Playground DateTime library but is updated
@ -14,26 +18,10 @@ for time synchronization.
The functions available in the library include: The functions available in the library include:
hour(); // the hour now (0-23) The time and date functions take a parameter for the time. This prevents
minute(); // the minute now (0-59)
second(); // the second now (0-59)
day(); // the day now (1-31)
weekday(); // day of the week (1-7), Sunday is day 1
month(); // the month now (1-12)
year(); // the full four digit year: (2009, 2010 etc)
there are also functions to return the hour in 12 hour format
hourFormat12(); // the hour now in 12 hour format
isAM(); // returns true if time now is AM
isPM(); // returns true if time now is PM
now(); // returns the current time as seconds since Jan 1 1970
The time and date functions can take an optional parameter for the time. This prevents
errors if the time rolls over between elements. For example, if a new minute begins errors if the time rolls over between elements. For example, if a new minute begins
between getting the minute and second, the values will be inconsistent. Using the between getting the minute and second, the values will be inconsistent. Using the
following functions eliminates this probglem following functions eliminates this problem
time_t t = now(); // store the current time in time variable t
hour(t); // returns the hour for the given time t hour(t); // returns the hour for the given time t
minute(t); // returns the minute for the given time t minute(t); // returns the minute for the given time t
second(t); // returns the second for the given time t second(t); // returns the second for the given time t
@ -43,25 +31,6 @@ following functions eliminates this probglem
year(t); // the year for the given time t year(t); // the year for the given time t
Functions for managing the timer services are:
setTime(t); // set the system time to the give time t
setTime(hr,min,sec,day,mnth,yr); // alternative to above, yr is 2 or 4 digit yr
// (2010 or 10 sets year to 2010)
adjustTime(adjustment); // adjust system time by adding the adjustment value
timeStatus(); // indicates if time has been set and recently synchronized
// returns one of the following enumerations:
timeNotSet // the time has never been set, the clock started at Jan 1 1970
timeNeedsSync // the time had been set but a sync attempt did not succeed
timeSet // the time is set and is synced
Time and Date values are not valid if the status is timeNotSet. Otherwise values can be used but
the returned time may have drifted if the status is timeNeedsSync.
setSyncProvider(getTimeFunction); // set the external time provider
setSyncInterval(interval); // set the number of seconds between re-sync
There are many convenience macros in the time.h file for time constants and conversion There are many convenience macros in the time.h file for time constants and conversion
of time units. of time units.

View File

@ -25,6 +25,7 @@
examples, add error checking and messages to RTC examples, examples, add error checking and messages to RTC examples,
add examples to DS1307RTC library. add examples to DS1307RTC library.
1.4 5 Sep 2014 - compatibility with Arduino 1.5.7 1.4 5 Sep 2014 - compatibility with Arduino 1.5.7
2.0 25 May 2021 - removed timing code, only used for conversion between unix and time
*/ */
#if ARDUINO >= 100 #if ARDUINO >= 100
@ -37,7 +38,6 @@
static tmElements_t tm; // a cache of time elements static tmElements_t tm; // a cache of time elements
static time_t cacheTime; // the time the cache was updated static time_t cacheTime; // the time the cache was updated
static uint32_t syncInterval = 300; // time sync will be attempted after this many seconds
void refreshCache(time_t t) { void refreshCache(time_t t) {
if (t != cacheTime) { if (t != cacheTime) {
@ -46,19 +46,11 @@ void refreshCache(time_t t) {
} }
} }
int hour() { // the hour now
return hour(now());
}
int hour(time_t t) { // the hour for the given time int hour(time_t t) { // the hour for the given time
refreshCache(t); refreshCache(t);
return tm.Hour; return tm.Hour;
} }
int hourFormat12() { // the hour now in 12 hour format
return hourFormat12(now());
}
int hourFormat12(time_t t) { // the hour for the given time in 12 hour format int hourFormat12(time_t t) { // the hour for the given time in 12 hour format
refreshCache(t); refreshCache(t);
if( tm.Hour == 0 ) if( tm.Hour == 0 )
@ -69,71 +61,39 @@ int hourFormat12(time_t t) { // the hour for the given time in 12 hour format
return tm.Hour ; return tm.Hour ;
} }
uint8_t isAM() { // returns true if time now is AM
return !isPM(now());
}
uint8_t isAM(time_t t) { // returns true if given time is AM uint8_t isAM(time_t t) { // returns true if given time is AM
return !isPM(t); return !isPM(t);
} }
uint8_t isPM() { // returns true if PM
return isPM(now());
}
uint8_t isPM(time_t t) { // returns true if PM uint8_t isPM(time_t t) { // returns true if PM
return (hour(t) >= 12); return (hour(t) >= 12);
} }
int minute() {
return minute(now());
}
int minute(time_t t) { // the minute for the given time int minute(time_t t) { // the minute for the given time
refreshCache(t); refreshCache(t);
return tm.Minute; return tm.Minute;
} }
int second() {
return second(now());
}
int second(time_t t) { // the second for the given time int second(time_t t) { // the second for the given time
refreshCache(t); refreshCache(t);
return tm.Second; return tm.Second;
} }
int day(){
return(day(now()));
}
int day(time_t t) { // the day for the given time (0-6) int day(time_t t) { // the day for the given time (0-6)
refreshCache(t); refreshCache(t);
return tm.Day; return tm.Day;
} }
int weekday() { // Sunday is day 1
return weekday(now());
}
int weekday(time_t t) { int weekday(time_t t) {
refreshCache(t); refreshCache(t);
return tm.Wday; return tm.Wday;
} }
int month(){
return month(now());
}
int month(time_t t) { // the month for the given time int month(time_t t) { // the month for the given time
refreshCache(t); refreshCache(t);
return tm.Month; return tm.Month;
} }
int year() { // as in Processing, the full four digit year: (2009, 2010 etc)
return year(now());
}
int year(time_t t) { // the year for the given time int year(time_t t) { // the year for the given time
refreshCache(t); refreshCache(t);
return tmYearToCalendar(tm.Year); return tmYearToCalendar(tm.Year);
@ -231,57 +191,6 @@ time_t makeTime(tmElements_t &tm){
seconds+= tm.Second; seconds+= tm.Second;
return (time_t)seconds; return (time_t)seconds;
} }
/*=====================================================*/
/* Low level system time functions */
static uint32_t sysTime = 0;
static uint32_t prevMillis = 0;
static uint32_t nextSyncTime = 0;
static timeStatus_t Status = timeNotSet;
getExternalTime getTimePtr; // pointer to external sync function
//setExternalTime setTimePtr; // not used in this version
#ifdef TIME_DRIFT_INFO // define this to get drift data
time_t sysUnsyncedTime = 0; // the time sysTime unadjusted by sync
#endif
time_t now() {
// calculate number of seconds passed since last call to now()
while (millis() - prevMillis >= 1000) {
// millis() and prevMillis are both unsigned ints thus the subtraction will always be the absolute value of the difference
sysTime++;
prevMillis += 1000;
#ifdef TIME_DRIFT_INFO
sysUnsyncedTime++; // this can be compared to the synced time to measure long term drift
#endif
}
if (nextSyncTime <= sysTime) {
if (getTimePtr != 0) {
time_t t = getTimePtr();
if (t != 0) {
setTime(t);
} else {
nextSyncTime = sysTime + syncInterval;
Status = (Status == timeNotSet) ? timeNotSet : timeNeedsSync;
}
}
}
return (time_t)sysTime;
}
void setTime(time_t t) {
#ifdef TIME_DRIFT_INFO
if(sysUnsyncedTime == 0)
sysUnsyncedTime = t; // store the time of the first call to set a valid Time
#endif
sysTime = (uint32_t)t;
nextSyncTime = (uint32_t)t + syncInterval;
Status = timeSet;
prevMillis = millis(); // restart counting from now (thanks to Korman for this fix)
}
time_t getUnixTime(int hr,int min,int sec,int dy, int mnth, int yr){ time_t getUnixTime(int hr,int min,int sec,int dy, int mnth, int yr){
// year can be given as full four digit year or two digts (2010 or 10 for 2010); // year can be given as full four digit year or two digts (2010 or 10 for 2010);
@ -298,28 +207,3 @@ time_t getUnixTime(int hr,int min,int sec,int dy, int mnth, int yr){
tm.Second = sec; tm.Second = sec;
return makeTime(tm); return makeTime(tm);
} }
void setTime(int hr,int min,int sec,int dy, int mnth, int yr){
setTime(getUnixTime(hr,min,sec,dy,mnth,yr));
}
void adjustTime(long adjustment) {
sysTime += adjustment;
}
// indicates if time has been set and recently synchronized
timeStatus_t timeStatus() {
now(); // required to actually update the status
return Status;
}
void setSyncProvider( getExternalTime getTimeFunction){
getTimePtr = getTimeFunction;
nextSyncTime = sysTime;
now(); // this will sync the clock
}
void setSyncInterval(time_t interval){ // set the number of seconds between re-sync
syncInterval = (uint32_t)interval;
nextSyncTime = sysTime + syncInterval;
}

View File

@ -31,8 +31,6 @@ typedef unsigned long time_t;
// but at least this hack lets us define C++ functions as intended. Hopefully // but at least this hack lets us define C++ functions as intended. Hopefully
// nothing too terrible will result from overriding the C library header?! // nothing too terrible will result from overriding the C library header?!
extern "C++" { extern "C++" {
typedef enum {timeNotSet, timeNeedsSync, timeSet
} timeStatus_t ;
typedef enum { typedef enum {
dowInvalid, dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday dowInvalid, dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday
@ -96,32 +94,18 @@ typedef time_t(*getExternalTime)();
/*============================================================================*/ /*============================================================================*/
/* time and date functions */ /* time and date functions */
int hour(); // the hour now
int hour(time_t t); // the hour for the given time int hour(time_t t); // the hour for the given time
int hourFormat12(); // the hour now in 12 hour format
int hourFormat12(time_t t); // the hour for the given time in 12 hour format int hourFormat12(time_t t); // the hour for the given time in 12 hour format
uint8_t isAM(); // returns true if time now is AM
uint8_t isAM(time_t t); // returns true the given time is AM uint8_t isAM(time_t t); // returns true the given time is AM
uint8_t isPM(); // returns true if time now is PM
uint8_t isPM(time_t t); // returns true the given time is PM uint8_t isPM(time_t t); // returns true the given time is PM
int minute(); // the minute now
int minute(time_t t); // the minute for the given time int minute(time_t t); // the minute for the given time
int second(); // the second now
int second(time_t t); // the second for the given time int second(time_t t); // the second for the given time
int day(); // the day now
int day(time_t t); // the day for the given time int day(time_t t); // the day for the given time
int weekday(); // the weekday now (Sunday is day 1)
int weekday(time_t t); // the weekday for the given time int weekday(time_t t); // the weekday for the given time
int month(); // the month now (Jan is month 1)
int month(time_t t); // the month for the given time int month(time_t t); // the month for the given time
int year(); // the full four digit year: (2009, 2010 etc)
int year(time_t t); // the year for the given time int year(time_t t); // the year for the given time
time_t now(); // return the current time as seconds since Jan 1 1970
void setTime(time_t t);
void setTime(int hr,int min,int sec,int day, int month, int yr);
time_t getUnixTime(int hr,int min,int sec,int day, int month, int yr); //added by Aircoookie to get epoch time time_t getUnixTime(int hr,int min,int sec,int day, int month, int yr); //added by Aircoookie to get epoch time
void adjustTime(long adjustment);
/* date strings */ /* date strings */
#define dt_MAX_STRING_LEN 9 // length of longest date string (excluding terminating null) #define dt_MAX_STRING_LEN 9 // length of longest date string (excluding terminating null)
@ -130,11 +114,6 @@ char* dayStr(uint8_t day);
char* monthShortStr(uint8_t month); char* monthShortStr(uint8_t month);
char* dayShortStr(uint8_t day); char* dayShortStr(uint8_t day);
/* time sync functions */
timeStatus_t timeStatus(); // indicates if time has been set and recently synchronized
void setSyncProvider( getExternalTime getTimeFunction); // identify the external time provider
void setSyncInterval(time_t interval); // set the number of seconds between re-sync
/* low level functions to convert to and from system time */ /* low level functions to convert to and from system time */
void breakTime(time_t time, tmElements_t &tm); // break time_t into elements void breakTime(time_t time, tmElements_t &tm); // break time_t into elements
time_t makeTime(tmElements_t &tm); // convert time elements into time_t time_t makeTime(tmElements_t &tm); // convert time elements into time_t

View File

@ -0,0 +1,161 @@
/*
Toki.h - Minimal millisecond accurate timekeeping.
LICENSE
The MIT License (MIT)
Copyright (c) 2021 Christian Schwinne
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <Arduino.h>
#define YEARS_70 2208988800UL
#define TOKI_NO_MS_ACCURACY 1000
//Time source. Sub-100 is second accuracy, higher ms accuracy. Higher value generally means more accurate
#define TOKI_TS_NONE 0 //unsynced (e.g. just after boot)
#define TOKI_TS_UDP 5 //synced via UDP from an instance whose time source is unsynced
#define TOKI_TS_BAD 10 //synced from a time source less than about +- 2s accurate
#define TOKI_TS_UDP_SEC 20 //synced via UDP from an instance whose time source is set from RTC/JSON
#define TOKI_TS_SEC 40 //general second-accurate time source
#define TOKI_TS_RTC 60 //second-accurate real time clock
#define TOKI_TS_JSON 70 //synced second-accurate from a client via JSON-API
#define TOKI_TS_UDP_NTP 110 //synced via UDP from an instance whose time source is NTP
#define TOKI_TS_MS 120 //general better-than-second accuracy time source
#define TOKI_TS_NTP 150 //NTP time, simple round trip estimation. Depending on network, mostly +- 50ms accurate
#define TOKI_TS_NTP_P 170 //NTP time with multi-step sync, higher accuracy. Not implemented in WLED
class Toki {
typedef enum {
inactive, marked, active
} TickT;
public:
typedef struct {
uint32_t sec;
uint16_t ms;
} Time;
private:
uint32_t fullSecondMillis = 0;
uint32_t unix = 0;
TickT tick = TickT::inactive;
uint8_t timeSrc = TOKI_TS_NONE;
public:
void setTime(Time t, uint8_t timeSource = TOKI_TS_MS) {
fullSecondMillis = millis() - t.ms;
unix = t.sec;
timeSrc = timeSource;
}
void setTime(uint32_t sec, uint16_t ms=TOKI_NO_MS_ACCURACY, uint8_t timeSource = TOKI_TS_MS) {
if (ms >= TOKI_NO_MS_ACCURACY) {
ms = millisecond(); //just keep current ms if not provided
if (timeSource > 99) timeSource = TOKI_TS_SEC; //lies
}
Time t = {sec, ms};
setTime(t, timeSource);
}
Time fromNTP(byte *timestamp) { //ntp timestamp is 8 bytes, 4 bytes second and 4 bytes sub-second fraction
unsigned long highWord = word(timestamp[0], timestamp[1]);
unsigned long lowWord = word(timestamp[2], timestamp[3]);
unsigned long unix = highWord << 16 | lowWord;
if (!unix) return {0,0};
unix -= YEARS_70; //NTP begins 1900, Unix 1970
unsigned long frac = word(timestamp[4], timestamp[5]); //65536ths of a second
frac = (frac*1000) >> 16; //convert to ms
return {unix, (uint16_t)frac};
}
uint16_t millisecond() {
uint32_t ms = millis() - fullSecondMillis;
while (ms > 999) {
ms -= 1000;
fullSecondMillis += 1000;
unix++;
if (tick == TickT::inactive) tick = TickT::marked; //marked, will be active on next loop
}
return ms;
}
uint32_t second() {
millisecond();
return unix;
}
//gets the absolute difference between two timestamps in milliseconds
uint32_t msDifference(const Time &t0, const Time &t1) {
bool t1BiggerSec = (t1.sec > t0.sec);
uint32_t secDiff = (t1BiggerSec) ? t1.sec - t0.sec : t0.sec - t1.sec;
uint32_t t0ms = t0.ms, t1ms = t1.ms;
if (t1BiggerSec) t1ms += secDiff*1000;
else t0ms += secDiff*1000;
uint32_t msDiff = (t1ms > t0ms) ? t1ms - t0ms : t0ms - t1ms;
return msDiff;
}
//return true if t1 is later than t0
bool isLater(const Time &t0, const Time &t1) {
if (t1.sec > t0.sec) return true;
if (t1.sec < t0.sec) return false;
if (t1.ms > t0.ms) return true;
return false;
}
void adjust(Time&t, int32_t offset) {
int32_t secs = offset /1000;
int32_t ms = offset - secs*1000;
t.sec += secs;
int32_t nms = t.ms + ms;
if (nms > 1000) {nms -= 1000; t.sec++;}
if (nms < 0) {nms += 1000; t.sec--;}
t.ms = nms;
}
Time getTime() {
Time t;
t.ms = millisecond();
t.sec = unix;
return t;
}
uint8_t getTimeSource() {
return timeSrc;
}
void setTick() {
if (tick == TickT::marked) tick = TickT::active;
}
void resetTick() {
if (tick == TickT::active) tick = TickT::inactive;
}
bool isTick() {
return (tick == TickT::active);
}
void printTime(const Time& t) {
Serial.printf_P(PSTR("%u,%03u\n"),t.sec,t.ms);
}
};

View File

@ -4,8 +4,9 @@
* UDP sync notifier / Realtime / Hyperion / TPM2.NET * UDP sync notifier / Realtime / Hyperion / TPM2.NET
*/ */
#define WLEDPACKETSIZE 29 #define WLEDPACKETSIZE 36
#define UDP_IN_MAXSIZE 1472 #define UDP_IN_MAXSIZE 1472
#define PRESUMED_NETWORK_DELAY 3 //how many ms could it take on avg to reach the receiver? This will be added to transmitted times
void notify(byte callMode, bool followUp) void notify(byte callMode, bool followUp)
{ {
@ -37,8 +38,8 @@ void notify(byte callMode, bool followUp)
//compatibilityVersionByte: //compatibilityVersionByte:
//0: old 1: supports white 2: supports secondary color //0: old 1: supports white 2: supports secondary color
//3: supports FX intensity, 24 byte packet 4: supports transitionDelay 5: sup palette //3: supports FX intensity, 24 byte packet 4: supports transitionDelay 5: sup palette
//6: supports timebase syncing, 29 byte packet 7: supports tertiary color //6: supports timebase syncing, 29 byte packet 7: supports tertiary color 8: supports sys time sync, 36 byte packet
udpOut[11] = 7; udpOut[11] = 8;
udpOut[12] = colSec[0]; udpOut[12] = colSec[0];
udpOut[13] = colSec[1]; udpOut[13] = colSec[1];
udpOut[14] = colSec[2]; udpOut[14] = colSec[2];
@ -60,6 +61,18 @@ void notify(byte callMode, bool followUp)
udpOut[27] = (t >> 8) & 0xFF; udpOut[27] = (t >> 8) & 0xFF;
udpOut[28] = (t >> 0) & 0xFF; udpOut[28] = (t >> 0) & 0xFF;
//sync system time
udpOut[29] = toki.getTimeSource();
Toki::Time tm = toki.getTime();
uint32_t unix = tm.sec;
udpOut[30] = (unix >> 24) & 0xFF;
udpOut[31] = (unix >> 16) & 0xFF;
udpOut[32] = (unix >> 8) & 0xFF;
udpOut[33] = (unix >> 0) & 0xFF;
uint16_t ms = tm.ms;
udpOut[34] = (ms >> 8) & 0xFF;
udpOut[35] = (ms >> 0) & 0xFF;
IPAddress broadcastIp; IPAddress broadcastIp;
broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP()); broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP());
@ -209,6 +222,9 @@ void handleNotifications()
if (millis() - notificationSentTime < 1000) return; if (millis() - notificationSentTime < 1000) return;
if (udpIn[1] > 199) return; //do not receive custom versions if (udpIn[1] > 199) return; //do not receive custom versions
//compatibilityVersionByte:
byte version = udpIn[11];
bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects); bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
//apply colors from notification //apply colors from notification
if (receiveNotificationColor || !someSel) if (receiveNotificationColor || !someSel)
@ -216,40 +232,66 @@ void handleNotifications()
col[0] = udpIn[3]; col[0] = udpIn[3];
col[1] = udpIn[4]; col[1] = udpIn[4];
col[2] = udpIn[5]; col[2] = udpIn[5];
if (udpIn[11] > 0) //sending module's white val is intended if (version > 0) //sending module's white val is intended
{ {
col[3] = udpIn[10]; col[3] = udpIn[10];
if (udpIn[11] > 1) if (version > 1)
{ {
colSec[0] = udpIn[12]; colSec[0] = udpIn[12];
colSec[1] = udpIn[13]; colSec[1] = udpIn[13];
colSec[2] = udpIn[14]; colSec[2] = udpIn[14];
colSec[3] = udpIn[15]; colSec[3] = udpIn[15];
} }
if (udpIn[11] > 5) if (version > 6)
{
uint32_t t = (udpIn[25] << 24) | (udpIn[26] << 16) | (udpIn[27] << 8) | (udpIn[28]);
t += 2;
t -= millis();
strip.timebase = t;
}
if (udpIn[11] > 6)
{ {
strip.setColor(2, udpIn[20], udpIn[21], udpIn[22], udpIn[23]); //tertiary color strip.setColor(2, udpIn[20], udpIn[21], udpIn[22], udpIn[23]); //tertiary color
} }
} }
} }
bool timebaseUpdated = false;
//apply effects from notification //apply effects from notification
if (udpIn[11] < 200 && (receiveNotificationEffects || !someSel)) if (version < 200 && (receiveNotificationEffects || !someSel))
{ {
if (udpIn[8] < strip.getModeCount()) effectCurrent = udpIn[8]; if (udpIn[8] < strip.getModeCount()) effectCurrent = udpIn[8];
effectSpeed = udpIn[9]; effectSpeed = udpIn[9];
if (udpIn[11] > 2) effectIntensity = udpIn[16]; if (version > 2) effectIntensity = udpIn[16];
if (udpIn[11] > 4 && udpIn[19] < strip.getPaletteCount()) effectPalette = udpIn[19]; if (version > 4 && udpIn[19] < strip.getPaletteCount()) effectPalette = udpIn[19];
if (version > 5)
{
uint32_t t = (udpIn[25] << 24) | (udpIn[26] << 16) | (udpIn[27] << 8) | (udpIn[28]);
t += PRESUMED_NETWORK_DELAY; //adjust trivially for network delay
t -= millis();
strip.timebase = t;
timebaseUpdated = true;
}
} }
if (udpIn[11] > 3) //adjust system time, but only if sender is more accurate than self
if (version > 7)
{
Toki::Time tm;
tm.sec = (udpIn[30] << 24) | (udpIn[31] << 16) | (udpIn[32] << 8) | (udpIn[33]);
tm.ms = (udpIn[34] << 8) | (udpIn[35]);
if (udpIn[29] > toki.getTimeSource()) { //if sender's time source is more accurate
toki.adjust(tm, PRESUMED_NETWORK_DELAY); //adjust trivially for network delay
uint8_t ts = TOKI_TS_UDP;
if (udpIn[29] > 99) ts = TOKI_TS_UDP_NTP;
else if (udpIn[29] >= TOKI_TS_SEC) ts = TOKI_TS_UDP_SEC;
toki.setTime(tm, ts);
} else if (timebaseUpdated && toki.getTimeSource() > 99) { //if we both have good times, get a more accurate timebase
Toki::Time myTime = toki.getTime();
uint32_t diff = toki.msDifference(tm, myTime);
strip.timebase -= PRESUMED_NETWORK_DELAY; //no need to presume, use difference between NTP times at send and receive points
if (toki.isLater(tm, myTime)) {
strip.timebase += diff;
} else {
strip.timebase -= diff;
}
}
}
if (version > 3)
{ {
transitionDelayTemp = ((udpIn[17] << 0) & 0xFF) + ((udpIn[18] << 8) & 0xFF00); transitionDelayTemp = ((udpIn[17] << 0) & 0xFF) + ((udpIn[18] << 8) & 0xFF00);
} }

View File

@ -14,6 +14,10 @@
#include "../usermods/Temperature/usermod_temperature.h" #include "../usermods/Temperature/usermod_temperature.h"
#endif #endif
#ifdef USERMOD_SN_PHOTORESISTOR
#include "../usermods/SN_Photoresistor/usermod_sn_photoresistor.h"
#endif
//#include "usermod_v2_empty.h" //#include "usermod_v2_empty.h"
#ifdef USERMOD_BUZZER #ifdef USERMOD_BUZZER
@ -61,6 +65,14 @@
#include "../usermods/multi_relay/usermod_multi_relay.h" #include "../usermods/multi_relay/usermod_multi_relay.h"
#endif #endif
#ifdef USERMOD_RTC
#include "../usermods/RTC/usermod_rtc.h"
#endif
#ifdef USERMOD_ELEKSTUBE_IPS
#include "../usermods/EleksTube_IPS/usermod_elekstube_ips.h"
#endif
void registerUsermods() void registerUsermods()
{ {
/* /*
@ -74,6 +86,10 @@ void registerUsermods()
usermods.add(new UsermodTemperature()); usermods.add(new UsermodTemperature());
#endif #endif
#ifdef USERMOD_SN_PHOTORESISTOR
usermods.add(new Usermod_SN_Photoresistor());
#endif
//usermods.add(new UsermodRenameMe()); //usermods.add(new UsermodRenameMe());
#ifdef USERMOD_BUZZER #ifdef USERMOD_BUZZER
@ -118,4 +134,12 @@ void registerUsermods()
#ifdef USERMOD_MULTI_RELAY #ifdef USERMOD_MULTI_RELAY
usermods.add(new MultiRelay()); usermods.add(new MultiRelay());
#endif #endif
#ifdef USERMOD_RTC
usermods.add(new RTCUsermod());
#endif
#ifdef USERMOD_ELEKSTUBE_IPS
usermods.add(new ElekstubeIPSUsermod());
#endif
} }

View File

@ -176,6 +176,7 @@ void WiFiEvent(WiFiEvent_t event)
void WLED::loop() void WLED::loop()
{ {
handleTime();
handleIR(); // 2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too handleIR(); // 2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too
handleConnection(); handleConnection();
handleSerial(); handleSerial();
@ -190,10 +191,8 @@ void WLED::loop()
yield(); yield();
handleIO(); handleIO();
handleIR(); handleIR();
handleNetworkTime();
handleAlexa(); handleAlexa();
handleOverlays();
yield(); yield();
if (doReboot) if (doReboot)
@ -246,6 +245,7 @@ void WLED::loop()
// refresh WLED nodes list // refresh WLED nodes list
refreshNodeList(); refreshNodeList();
if (nodeBroadcastEnabled) sendSysInfoUDP(); if (nodeBroadcastEnabled) sendSysInfoUDP();
yield();
} }
//LED settings have been saved, re-init busses //LED settings have been saved, re-init busses
@ -274,9 +274,9 @@ void WLED::loop()
if (millis() - debugTime > 9999) { if (millis() - debugTime > 9999) {
DEBUG_PRINTLN(F("---DEBUG INFO---")); DEBUG_PRINTLN(F("---DEBUG INFO---"));
DEBUG_PRINT(F("Runtime: ")); DEBUG_PRINTLN(millis()); DEBUG_PRINT(F("Runtime: ")); DEBUG_PRINTLN(millis());
DEBUG_PRINT(F("Unix time: ")); DEBUG_PRINTLN(now()); DEBUG_PRINT(F("Unix time: ")); toki.printTime(toki.getTime());
DEBUG_PRINT(F("Free heap: ")); DEBUG_PRINTLN(ESP.getFreeHeap()); DEBUG_PRINT(F("Free heap: ")); DEBUG_PRINTLN(ESP.getFreeHeap());
#ifdef ARDUINO_ARCH_ESP32 #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
if (psramFound()) { if (psramFound()) {
DEBUG_PRINT(F("Total PSRAM: ")); DEBUG_PRINT(ESP.getPsramSize()/1024); DEBUG_PRINTLN("kB"); DEBUG_PRINT(F("Total PSRAM: ")); DEBUG_PRINT(ESP.getPsramSize()/1024); DEBUG_PRINTLN("kB");
DEBUG_PRINT(F("Free PSRAM: ")); DEBUG_PRINT(ESP.getFreePsram()/1024); DEBUG_PRINTLN("kB"); DEBUG_PRINT(F("Free PSRAM: ")); DEBUG_PRINT(ESP.getFreePsram()/1024); DEBUG_PRINTLN("kB");
@ -298,6 +298,7 @@ void WLED::loop()
} }
loops++; loops++;
#endif // WLED_DEBUG #endif // WLED_DEBUG
toki.resetTick();
} }
void WLED::setup() void WLED::setup()
@ -325,31 +326,21 @@ void WLED::setup()
DEBUG_PRINTLN(ESP.getFreeHeap()); DEBUG_PRINTLN(ESP.getFreeHeap());
registerUsermods(); registerUsermods();
#ifdef ARDUINO_ARCH_ESP32 #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
if (psramFound()) { if (psramFound()) {
pinManager.allocatePin(16); // GPIO16 reserved for SPI RAM pinManager.allocatePin(16); // GPIO16 reserved for SPI RAM
pinManager.allocatePin(17); // GPIO17 reserved for SPI RAM pinManager.allocatePin(17); // GPIO17 reserved for SPI RAM
} }
/*
TODO make reservation for Ethernet pins
#ifdef WLED_USE_ETHERNET
if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) {
ethernet_settings es = ethernetBoards[ethernetType];
if (es.eth_power>0) pinManager.allocatePin(es.eth_power);
if (es.eth_mdc>0) pinManager.allocatePin(es.eth_mdc);
if (es.eth_mdio>0) pinManager.allocatePin(es.eth_mdio);
//if (es.eth_type>0) pinManager.allocatePin(es.eth_type);
//if (es.eth_clk_mode>0) pinManager.allocatePin(es.eth_clk_mode);
}
#endif
*/
#endif #endif
//DEBUG_PRINT(F("LEDs inited. heap usage ~"));
//DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap());
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
pinManager.allocatePin(1,true); // GPIO1 reserved for debug output pinManager.allocatePin(1,true); // GPIO1 reserved for debug output
#endif #endif
#ifdef WLED_USE_DMX //reserve GPIO2 as hardcoded DMX pin #ifdef WLED_USE_DMX //reserve GPIO2 as hardcoded DMX pin
pinManager.allocatePin(2,false); pinManager.allocatePin(2);
#endif #endif
bool fsinit = false; bool fsinit = false;
@ -374,7 +365,6 @@ void WLED::setup()
DEBUG_PRINTLN(F("Usermods setup")); DEBUG_PRINTLN(F("Usermods setup"));
userSetup(); userSetup();
usermods.setup(); usermods.setup();
if (strcmp(clientSSID, DEFAULT_CLIENT_SSID) == 0) if (strcmp(clientSSID, DEFAULT_CLIENT_SSID) == 0)
showWelcomePage = true; showWelcomePage = true;
WiFi.persistent(false); WiFi.persistent(false);

View File

@ -8,7 +8,7 @@
*/ */
// version code in format yymmddb (b = daily build) // version code in format yymmddb (b = daily build)
#define VERSION 2106151 #define VERSION 2106152
//uncomment this if you have a "my_config.h" file you'd like to use //uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG //#define WLED_USE_MY_CONFIG
@ -90,6 +90,7 @@
#include <SPIFFSEditor.h> #include <SPIFFSEditor.h>
#include "src/dependencies/time/TimeLib.h" #include "src/dependencies/time/TimeLib.h"
#include "src/dependencies/timezone/Timezone.h" #include "src/dependencies/timezone/Timezone.h"
#include "src/dependencies/toki/Toki.h"
#ifndef WLED_DISABLE_ALEXA #ifndef WLED_DISABLE_ALEXA
#define ESPALEXA_ASYNC #define ESPALEXA_ASYNC
@ -118,7 +119,7 @@
// The following is a construct to enable code to compile without it. // The following is a construct to enable code to compile without it.
// There is a code thet will still not use PSRAM though: // There is a code thet will still not use PSRAM though:
// AsyncJsonResponse is a derived class that implements DynamicJsonDocument (AsyncJson-v6.h) // AsyncJsonResponse is a derived class that implements DynamicJsonDocument (AsyncJson-v6.h)
#ifdef ARDUINO_ARCH_ESP32 #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
struct PSRAM_Allocator { struct PSRAM_Allocator {
void* allocate(size_t size) { void* allocate(size_t size) {
if (psramFound()) return ps_malloc(size); // use PSRAM if it exists if (psramFound()) return ps_malloc(size); // use PSRAM if it exists
@ -244,7 +245,12 @@ WLED_GLOBAL byte apBehavior _INIT(AP_BEHAVIOR_BOOT_NO_CONN); // access poi
WLED_GLOBAL IPAddress staticIP _INIT_N((( 0, 0, 0, 0))); // static IP of ESP WLED_GLOBAL IPAddress staticIP _INIT_N((( 0, 0, 0, 0))); // static IP of ESP
WLED_GLOBAL IPAddress staticGateway _INIT_N((( 0, 0, 0, 0))); // gateway (router) IP WLED_GLOBAL IPAddress staticGateway _INIT_N((( 0, 0, 0, 0))); // gateway (router) IP
WLED_GLOBAL IPAddress staticSubnet _INIT_N(((255, 255, 255, 0))); // most common subnet in home networks WLED_GLOBAL IPAddress staticSubnet _INIT_N(((255, 255, 255, 0))); // most common subnet in home networks
WLED_GLOBAL bool noWifiSleep _INIT(false); // disabling modem sleep modes will increase heat output and power usage, but may help with connection issues #ifdef ARDUINO_ARCH_ESP32
WLED_GLOBAL bool noWifiSleep _INIT(true); // disabling modem sleep modes will increase heat output and power usage, but may help with connection issues
#else
WLED_GLOBAL bool noWifiSleep _INIT(false);
#endif
#ifdef WLED_USE_ETHERNET #ifdef WLED_USE_ETHERNET
#ifdef WLED_ETH_DEFAULT // default ethernet board type if specified #ifdef WLED_ETH_DEFAULT // default ethernet board type if specified
WLED_GLOBAL int ethernetType _INIT(WLED_ETH_DEFAULT); // ethernet board type WLED_GLOBAL int ethernetType _INIT(WLED_ETH_DEFAULT); // ethernet board type
@ -350,7 +356,7 @@ WLED_GLOBAL bool useAMPM _INIT(false); // 12h/24h clock format
WLED_GLOBAL byte currentTimezone _INIT(0); // Timezone ID. Refer to timezones array in wled10_ntp.ino WLED_GLOBAL byte currentTimezone _INIT(0); // Timezone ID. Refer to timezones array in wled10_ntp.ino
WLED_GLOBAL int utcOffsetSecs _INIT(0); // Seconds to offset from UTC before timzone calculation WLED_GLOBAL int utcOffsetSecs _INIT(0); // Seconds to offset from UTC before timzone calculation
WLED_GLOBAL byte overlayDefault _INIT(0); // 0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie WLED_GLOBAL byte overlayDefault _INIT(0); // 0: no overlay 1: analog clock 2: single-digit clock 3: cronixie
WLED_GLOBAL byte overlayMin _INIT(0), overlayMax _INIT(ledCount - 1); // boundaries of overlay mode WLED_GLOBAL byte overlayMin _INIT(0), overlayMax _INIT(ledCount - 1); // boundaries of overlay mode
WLED_GLOBAL byte analogClock12pixel _INIT(0); // The pixel in your strip where "midnight" would be WLED_GLOBAL byte analogClock12pixel _INIT(0); // The pixel in your strip where "midnight" would be
@ -472,13 +478,9 @@ WLED_GLOBAL bool hueStoreAllowed _INIT(false), hueNewKey _INIT(false);
// overlays // overlays
WLED_GLOBAL byte overlayCurrent _INIT(overlayDefault); WLED_GLOBAL byte overlayCurrent _INIT(overlayDefault);
WLED_GLOBAL byte overlaySpeed _INIT(200);
WLED_GLOBAL unsigned long overlayRefreshMs _INIT(200);
WLED_GLOBAL unsigned long overlayRefreshedTime;
// cronixie // cronixie
WLED_GLOBAL byte dP[] _INIT_N(({ 0, 0, 0, 0, 0, 0 })); WLED_GLOBAL byte dP[] _INIT_N(({ 255, 255, 255, 255, 255, 255 }));
WLED_GLOBAL bool cronixieInit _INIT(false);
// countdown // countdown
WLED_GLOBAL unsigned long countdownTime _INIT(1514764800L); WLED_GLOBAL unsigned long countdownTime _INIT(1514764800L);
@ -541,6 +543,7 @@ WLED_GLOBAL float longitude _INIT(0.0);
WLED_GLOBAL float latitude _INIT(0.0); WLED_GLOBAL float latitude _INIT(0.0);
WLED_GLOBAL time_t sunrise _INIT(0); WLED_GLOBAL time_t sunrise _INIT(0);
WLED_GLOBAL time_t sunset _INIT(0); WLED_GLOBAL time_t sunset _INIT(0);
WLED_GLOBAL Toki toki _INIT(Toki());
// Temp buffer // Temp buffer
WLED_GLOBAL char* obuf; WLED_GLOBAL char* obuf;

View File

@ -11,6 +11,8 @@
#include <Arduino.h> //PI constant #include <Arduino.h> //PI constant
//#define WLED_DEBUG_MATH
#define modd(x, y) ((x) - (int)((x) / (y)) * (y)) #define modd(x, y) ((x) - (int)((x) / (y)) * (y))
float cos_t(float x) float cos_t(float x)
@ -24,38 +26,58 @@ float cos_t(float x)
} }
float xx = x * x; float xx = x * x;
return sign * (1 - ((xx) / (2)) + ((xx * xx) / (24)) - ((xx * xx * xx) / (720)) + ((xx * xx * xx * xx) / (40320)) - ((xx * xx * xx * xx * xx) / (3628800)) + ((xx * xx * xx * xx * xx * xx) / (479001600))); float res = sign * (1 - ((xx) / (2)) + ((xx * xx) / (24)) - ((xx * xx * xx) / (720)) + ((xx * xx * xx * xx) / (40320)) - ((xx * xx * xx * xx * xx) / (3628800)) + ((xx * xx * xx * xx * xx * xx) / (479001600)));
#ifdef WLED_DEBUG_MATH
Serial.printf("cos: %f,%f\n",res,cos(x));
#endif
return res;
} }
float sin_t(float x) { float sin_t(float x) {
return cos_t(HALF_PI - x); float res = cos_t(HALF_PI - x);
#ifdef WLED_DEBUG_MATH
Serial.printf("sin: %f,%f\n",res,sin(x));
#endif
return res;
} }
float tan_t(float x) { float tan_t(float x) {
float c = cos_t(x); float c = cos_t(x);
if (c==0.0) return 0; if (c==0.0) return 0;
return sin_t(x) / c; float res = sin_t(x) / c;
#ifdef WLED_DEBUG_MATH
Serial.printf("tan: %f,%f\n",res,tan(x));
#endif
return res;
} }
//https://stackoverflow.com/questions/3380628 //https://stackoverflow.com/questions/3380628
// Absolute error <= 6.7e-5 // Absolute error <= 6.7e-5
float acos_t(float x) { float acos_t(float x) {
float negate = float(x < 0); float negate = float(x < 0);
x = std::abs(x); float xabs = std::abs(x);
float ret = -0.0187293; float ret = -0.0187293;
ret = ret * x; ret = ret * xabs;
ret = ret + 0.0742610; ret = ret + 0.0742610;
ret = ret * x; ret = ret * xabs;
ret = ret - 0.2121144; ret = ret - 0.2121144;
ret = ret * x; ret = ret * xabs;
ret = ret + HALF_PI; ret = ret + HALF_PI;
ret = ret * sqrt(1.0-x); ret = ret * sqrt(1.0-xabs);
ret = ret - 2 * negate * ret; ret = ret - 2 * negate * ret;
return negate * PI + ret; float res = negate * PI + ret;
#ifdef WLED_DEBUG_MATH
Serial.printf("acos,%f,%f,%f\n",x,res,acos(x));
#endif
return res;
} }
float asin_t(float x) { float asin_t(float x) {
return HALF_PI - acos_t(x); float res = HALF_PI - acos_t(x);
#ifdef WLED_DEBUG_MATH
Serial.printf("asin,%f,%f,%f\n",x,res,asin(x));
#endif
return res;
} }
/* /*
@ -64,10 +86,35 @@ float asin_t(float x) {
#define B -0.287434475393028 #define B -0.287434475393028
#define C ((HALF_PI/2) - A - B) #define C ((HALF_PI/2) - A - B)
//polynominal factors for approximation between 1 and 5
#define C0 0.089494f
#define C1 0.974207f
#define C2 -0.326175f
#define C3 0.05375f
#define C4 -0.003445f
float atan_t(float x) { float atan_t(float x) {
if (x>1) return atan(x); bool neg = (x < 0);
#ifdef WLED_DEBUG_MATH
float xinput = x;
#endif
x = std::abs(x);
float res;
if (x > 5.0f) { //atan(x) converges to pi/2 - (1/x) for large values
res = HALF_PI - (1.0f/x);
}
else if (x > 1.0f) { //1 < x < 5
float xx = x * x; float xx = x * x;
return ((A*xx + B)*xx + C)*x; res = (C4*xx*xx)+(C3*xx*x)+(C2*xx)+(C1*x)+C0;
} else { //this approximation is only for x <= 1
float xx = x * x;
res = ((A*xx + B)*xx + C)*x;
}
if (neg) res = -res;
#ifdef WLED_DEBUG_MATH
Serial.printf("atan,%f,%f,%f\n",xinput,res,atan(xinput));
#endif
return res;
} }
*/ */
//https://stackoverflow.com/a/42542593 //https://stackoverflow.com/a/42542593
@ -110,12 +157,19 @@ float floor_t(float x) {
bool neg = x < 0; bool neg = x < 0;
int val = x; int val = x;
if (neg) val--; if (neg) val--;
#ifdef WLED_DEBUG_MATH
Serial.printf("floor: %f,%f\n",val,floor(x));
#endif
return val; return val;
} }
float fmod_t(float num, float denom) { float fmod_t(float num, float denom) {
int tquot = num / denom; int tquot = num / denom;
return num - tquot * denom; float res = num - tquot * denom;
#ifdef WLED_DEBUG_MATH
Serial.printf("fmod: %f,%f\n",res,fmod(num,denom));
#endif
return res;
} }
#endif #endif

View File

@ -247,7 +247,10 @@ void initServer()
//make API CORS compatible //make API CORS compatible
if (request->method() == HTTP_OPTIONS) if (request->method() == HTTP_OPTIONS)
{ {
request->send(200); return; AsyncWebServerResponse *response = request->beginResponse(200);
response->addHeader(F("Access-Control-Max-Age"), F("7200"));
request->send(response);
return;
} }
if(handleSet(request, request->url())) return; if(handleSet(request, request->url())) return;

View File

@ -412,8 +412,6 @@ void getSettingsJS(byte subPage, char* dest)
if (subPage == 4) if (subPage == 4)
{ {
//sappend('v',SET_F("BT"),buttonType);
//sappend('v',SET_F("IR"),irEnabled);
sappend('v',SET_F("UP"),udpPort); sappend('v',SET_F("UP"),udpPort);
sappend('v',SET_F("U2"),udpPort2); sappend('v',SET_F("U2"),udpPort2);
sappend('c',SET_F("RB"),receiveNotificationBrightness); sappend('c',SET_F("RB"),receiveNotificationBrightness);
@ -531,9 +529,6 @@ void getSettingsJS(byte subPage, char* dest)
sappend('v',SET_F("A1"),macroAlexaOff); sappend('v',SET_F("A1"),macroAlexaOff);
sappend('v',SET_F("MC"),macroCountdown); sappend('v',SET_F("MC"),macroCountdown);
sappend('v',SET_F("MN"),macroNl); sappend('v',SET_F("MN"),macroNl);
//sappend('v',SET_F("MP"),macroButton);
//sappend('v',SET_F("ML"),macroLongPress);
//sappend('v',SET_F("MD"),macroDoublePress);
for (uint8_t i=0; i<WLED_MAX_BUTTONS; i++) { for (uint8_t i=0; i<WLED_MAX_BUTTONS; i++) {
oappend(SET_F("addRow(")); oappend(SET_F("addRow("));
oappend(itoa(i,tm,10)); oappend(","); oappend(itoa(i,tm,10)); oappend(",");