Merge remote-tracking branch 'upstream/main' into pxmagic

This commit is contained in:
Alerson Jorge 2023-07-20 13:32:35 -03:00
commit cd6b3d7dee
60 changed files with 5164 additions and 5364 deletions

View File

@ -1,5 +1,38 @@
## WLED changelog ## WLED changelog
#### Build 2307130
- larger `oappend()` stack buffer (3.5k) for ESP32
- Preset cycle bugfix (#3262)
- Rotary encoder ALT fix for large LED count (#3276)
- effect updates (2D Plasmaball), `blur()` speedup
- On/Off toggle from nodes view (may show unknow device type on older versions) (#3291)
- various fixes and improvements (ABL, crashes when changing presets with different segments)
#### Build 2306270
- ESP-NOW remote support (#3237)
- Pixel Magic tool (display pixel art) (#3249)
- Websocket (peek) fallback when connection cannot be established, WS retries (#3267)
- Add WiFi network scan RPC command to Improv Serial (#3271)
- Longer (custom option available) segment name for ESP32
- various fixes and improvements
#### Build 2306210
- 0.14.0-b3 release
- respect global I2C in all usermods (no local initilaisation of I2C bus)
- Multi relay usermod compile-time enabled option (-D MULTI_RELAY_ENABLED=true|false)
#### Build 2306180
- Added client-side option for applying effect defaults from metadata
- Improved ESP8266 stability by reducing WebSocket response resends
- Updated ESP8266 core to 3.1.2
#### Build 2306141
- Lissajous improvements
- Scrolling Text improvements (leading 0)
#### Build 2306140
- Add settings PIN (un)locking to JSON post API
#### Build 2306130 #### Build 2306130
- Bumped version to 0.14-b3 (beta 3) - Bumped version to 0.14-b3 (beta 3)
- added pin dropdowns in LED preferences (not for LED pins) and usermods - added pin dropdowns in LED preferences (not for LED pins) and usermods

6
package-lock.json generated
View File

@ -1409,9 +1409,9 @@
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
}, },
"semver": { "semver": {
"version": "5.7.1", "version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="
}, },
"semver-diff": { "semver-diff": {
"version": "2.1.0", "version": "2.1.0",

View File

@ -10,7 +10,7 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# CI binaries # CI binaries
;; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth # ESP32 variant builds are temporarily excluded from CI due to toolchain issues on the GitHub Actions Linux environment ; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth # ESP32 variant builds are temporarily excluded from CI due to toolchain issues on the GitHub Actions Linux environment
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB
# Release binaries # Release binaries
@ -40,6 +40,8 @@ default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_
; default_envs = esp32dev_qio80 ; default_envs = esp32dev_qio80
; default_envs = esp32_eth_ota1mapp ; default_envs = esp32_eth_ota1mapp
; default_envs = esp32s2_saola ; default_envs = esp32s2_saola
; default_envs = esp32c3dev
; default_envs = lolin_s2_mini
src_dir = ./wled00 src_dir = ./wled00
data_dir = ./wled00/data data_dir = ./wled00/data
@ -60,13 +62,14 @@ arduino_core_2_7_4 = espressif8266@2.6.2
arduino_core_3_0_0 = espressif8266@3.0.0 arduino_core_3_0_0 = espressif8266@3.0.0
arduino_core_3_2_0 = espressif8266@3.2.0 arduino_core_3_2_0 = espressif8266@3.2.0
arduino_core_4_1_0 = espressif8266@4.1.0 arduino_core_4_1_0 = espressif8266@4.1.0
arduino_core_3_1_2 = espressif8266@4.2.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
arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/stage arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/stage
# Platform to use for ESP8266 # Platform to use for ESP8266
platform_wled_default = ${common.arduino_core_4_1_0} platform_wled_default = ${common.arduino_core_3_1_2}
# We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization # We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization
#platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7 #platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
platform_packages = platformio/framework-arduinoespressif8266 platform_packages = platformio/framework-arduinoespressif8266
@ -459,8 +462,8 @@ board = esp32-c3-devkitm-1
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3 build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3
-D WLED_WATCHDOG_TIMEOUT=0 -D WLED_WATCHDOG_TIMEOUT=0
; -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB
-DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip ;-DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip
upload_speed = 460800 upload_speed = 460800
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
lib_deps = ${esp32c3.lib_deps} lib_deps = ${esp32c3.lib_deps}
@ -572,10 +575,10 @@ platform = ${esp32s2.platform}
platform_packages = ${esp32s2.platform_packages} platform_packages = ${esp32s2.platform_packages}
board = lolin_s2_mini board = lolin_s2_mini
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
build_unflags = ${common.build_unflags} -DARDUINO_USB_CDC_ON_BOOT=1 build_unflags = ${common.build_unflags} #-DARDUINO_USB_CDC_ON_BOOT=1
build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=LolinS2 build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=LolinS2
-DBOARD_HAS_PSRAM -DBOARD_HAS_PSRAM
-DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_CDC_ON_BOOT=1 # try disabling and enabling unflag above in case of board-specific issues, will disable Serial
-DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0
-DARDUINO_USB_DFU_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0
-DLOLIN_WIFI_FIX ; seems to work much better with this -DLOLIN_WIFI_FIX ; seems to work much better with this

View File

@ -390,12 +390,6 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()=====";
method: "gzip", method: "gzip",
filter: "html-minify", filter: "html-minify",
}, },
{
file: "liveviewws.htm",
name: "PAGE_liveviewws",
method: "gzip",
filter: "html-minify",
},
{ {
file: "liveviewws2D.htm", file: "liveviewws2D.htm",
name: "PAGE_liveviewws2D", name: "PAGE_liveviewws2D",

View File

@ -113,8 +113,7 @@ private:
public: public:
void setup() void setup()
{ {
PinManagerPinType pins[2] = { { i2c_sda, true }, { i2c_scl, true } }; // allocate pins if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) return;
sensorFound = lightMeter.begin(); sensorFound = lightMeter.begin();
initDone = true; initDone = true;
} }
@ -174,7 +173,9 @@ public:
user = root.createNestedObject(F("u")); user = root.createNestedObject(F("u"));
JsonArray lux_json = user.createNestedArray(F("Luminance")); JsonArray lux_json = user.createNestedArray(F("Luminance"));
if (!sensorFound) { if (!enabled) {
lux_json.add(F("disabled"));
} else if (!sensorFound) {
// if no sensor // if no sensor
lux_json.add(F("BH1750 ")); lux_json.add(F("BH1750 "));
lux_json.add(F("Not Found")); lux_json.add(F("Not Found"));

View File

@ -184,8 +184,7 @@ private:
public: public:
void setup() void setup()
{ {
PinManagerPinType pins[2] = { { i2c_sda, true }, { i2c_scl, true } }; // allocate pins if (i2c_scl<0 || i2c_sda<0) { enabled = false; sensorType = 0; return; }
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { sensorType=0; return; }
if (!bme.begin()) if (!bme.begin())
{ {

View File

@ -271,10 +271,9 @@ void PIRsensorSwitch::publishMqtt(const char* state)
#ifndef WLED_DISABLE_MQTT #ifndef WLED_DISABLE_MQTT
//Check if MQTT Connected, otherwise it will crash the 8266 //Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED) { if (WLED_MQTT_CONNECTED) {
char subuf[64]; char buf[64];
strcpy(subuf, mqttDeviceTopic); sprintf_P(buf, PSTR("%s/motion"), mqttDeviceTopic); //max length: 33 + 7 = 40
strcat_P(subuf, PSTR("/motion")); mqtt->publish(buf, 0, false, state);
mqtt->publish(subuf, 0, false, state);
} }
#endif #endif
} }

View File

@ -12,8 +12,7 @@ class RTCUsermod : public Usermod {
public: public:
void setup() { void setup() {
PinManagerPinType pins[2] = { { i2c_scl, true }, { i2c_sda, true } }; if (i2c_scl<0 || i2c_sda<0) { disabled = true; return; }
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { disabled = true; return; }
RTC.begin(); RTC.begin();
time_t rtcTime = RTC.get(); time_t rtcTime = RTC.get();
if (rtcTime) { if (rtcTime) {
@ -25,8 +24,8 @@ class RTCUsermod : public Usermod {
} }
void loop() { void loop() {
if (strip.isUpdating()) return; if (disabled || strip.isUpdating()) return;
if (!disabled && toki.isTick()) { if (toki.isTick()) {
time_t t = toki.second(); time_t t = toki.second();
if (t != RTC.get()) RTC.set(t); //set RTC to NTP/UI-provided value if (t != RTC.get()) RTC.set(t); //set RTC to NTP/UI-provided value
} }

View File

@ -50,8 +50,7 @@ class UsermodVL53L0XGestures : public Usermod {
public: public:
void setup() { void setup() {
PinManagerPinType pins[2] = { { i2c_scl, true }, { i2c_sda, true } }; if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; }
sensor.setTimeout(150); sensor.setTimeout(150);
if (!sensor.init()) if (!sensor.init())

View File

@ -1149,6 +1149,13 @@ class AudioReactive : public Usermod {
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin); if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin);
break; break;
#endif #endif
case 6:
DEBUGSR_PRINTLN(F("AR: ES8388 Source"));
audioSource = new ES8388Source(SAMPLE_RATE, BLOCK_SIZE);
delay(100);
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
break;
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
// ADC over I2S is only possible on "classic" ESP32 // ADC over I2S is only possible on "classic" ESP32
case 0: case 0:
@ -1738,6 +1745,8 @@ class AudioReactive : public Usermod {
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
oappend(SET_F("addOption(dd,'Generic I2S PDM',5);")); oappend(SET_F("addOption(dd,'Generic I2S PDM',5);"));
#endif #endif
oappend(SET_F("addOption(dd,'ES8388',6);"));
oappend(SET_F("dd=addDropdown('AudioReactive','config:AGC');")); oappend(SET_F("dd=addDropdown('AudioReactive','config:AGC');"));
oappend(SET_F("addOption(dd,'Off',0);")); oappend(SET_F("addOption(dd,'Off',0);"));
oappend(SET_F("addOption(dd,'Normal',1);")); oappend(SET_F("addOption(dd,'Normal',1);"));

View File

@ -427,6 +427,122 @@ public:
} }
}; };
/* ES8388 Sound Modude
This is an I2S sound processing unit that requires ininitialization over
I2C before I2S data can be received.
*/
class ES8388Source : public I2SSource {
private:
void _es8388I2cWrite(uint8_t reg, uint8_t val) {
#ifndef ES8388_ADDR
Wire.beginTransmission(0x10);
#define ES8388_ADDR 0x10 // default address
#else
Wire.beginTransmission(ES8388_ADDR);
#endif
Wire.write((uint8_t)reg);
Wire.write((uint8_t)val);
uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK
if (i2cErr != 0) {
DEBUGSR_PRINTF("AR: ES8388 I2C write failed with error=%d (addr=0x%X, reg 0x%X, val 0x%X).\n", i2cErr, ES8388_ADDR, reg, val);
}
}
void _es8388InitAdc() {
// https://dl.radxa.com/rock2/docs/hw/ds/ES8388%20user%20Guide.pdf Section 10.1
// http://www.everest-semi.com/pdf/ES8388%20DS.pdf Better spec sheet, more clear.
// https://docs.google.com/spreadsheets/d/1CN3MvhkcPVESuxKyx1xRYqfUit5hOdsG45St9BCUm-g/edit#gid=0 generally
// Sets ADC to around what AudioReactive expects, and loops line-in to line-out/headphone for monitoring.
// Registries are decimal, settings are binary as that's how everything is listed in the docs
// ...which makes it easier to reference the docs.
//
_es8388I2cWrite( 8,0b00000000); // I2S to slave
_es8388I2cWrite( 2,0b11110011); // Power down DEM and STM
_es8388I2cWrite(43,0b10000000); // Set same LRCK
_es8388I2cWrite( 0,0b00000101); // Set chip to Play & Record Mode
_es8388I2cWrite(13,0b00000010); // Set MCLK/LRCK ratio to 256
_es8388I2cWrite( 1,0b01000000); // Power up analog and lbias
_es8388I2cWrite( 3,0b00000000); // Power up ADC, Analog Input, and Mic Bias
_es8388I2cWrite( 4,0b11111100); // Power down DAC, Turn on LOUT1 and ROUT1 and LOUT2 and ROUT2 power
_es8388I2cWrite( 2,0b01000000); // Power up DEM and STM and undocumented bit for "turn on line-out amp"
// #define use_es8388_mic
#ifdef use_es8388_mic
// The mics *and* line-in are BOTH connected to LIN2/RIN2 on the AudioKit
// so there's no way to completely eliminate the mics. It's also hella noisy.
// Line-in works OK on the AudioKit, generally speaking, as the mics really need
// amplification to be noticable in a quiet room. If you're in a very loud room,
// the mics on the AudioKit WILL pick up sound even in line-in mode.
// TL;DR: Don't use the AudioKit for anything, use the LyraT.
//
// The LyraT does a reasonable job with mic input as configured below.
// Pick one of these. If you have to use the mics, use a LyraT over an AudioKit if you can:
_es8388I2cWrite(10,0b00000000); // Use Lin1/Rin1 for ADC input (mic on LyraT)
//_es8388I2cWrite(10,0b01010000); // Use Lin2/Rin2 for ADC input (mic *and* line-in on AudioKit)
_es8388I2cWrite( 9,0b10001000); // Select Analog Input PGA Gain for ADC to +24dB (L+R)
_es8388I2cWrite(16,0b00000000); // Set ADC digital volume attenuation to 0dB (left)
_es8388I2cWrite(17,0b00000000); // Set ADC digital volume attenuation to 0dB (right)
_es8388I2cWrite(38,0b00011011); // Mixer - route LIN1/RIN1 to output after mic gain
_es8388I2cWrite(39,0b01000000); // Mixer - route LIN to mixL, +6dB gain
_es8388I2cWrite(42,0b01000000); // Mixer - route RIN to mixR, +6dB gain
_es8388I2cWrite(46,0b00100001); // LOUT1VOL - 0b00100001 = +4.5dB
_es8388I2cWrite(47,0b00100001); // ROUT1VOL - 0b00100001 = +4.5dB
_es8388I2cWrite(48,0b00100001); // LOUT2VOL - 0b00100001 = +4.5dB
_es8388I2cWrite(49,0b00100001); // ROUT2VOL - 0b00100001 = +4.5dB
// Music ALC - the mics like Auto Level Control
// You can also use this for line-in, but it's not really needed.
//
_es8388I2cWrite(18,0b11111000); // ALC: stereo, max gain +35.5dB, min gain -12dB
_es8388I2cWrite(19,0b00110000); // ALC: target -1.5dB, 0ms hold time
_es8388I2cWrite(20,0b10100110); // ALC: gain ramp up = 420ms/93ms, gain ramp down = check manual for calc
_es8388I2cWrite(21,0b00000110); // ALC: use "ALC" mode, no zero-cross, window 96 samples
_es8388I2cWrite(22,0b01011001); // ALC: noise gate threshold, PGA gain constant, noise gate enabled
#else
_es8388I2cWrite(10,0b01010000); // Use Lin2/Rin2 for ADC input ("line-in")
_es8388I2cWrite( 9,0b00000000); // Select Analog Input PGA Gain for ADC to 0dB (L+R)
_es8388I2cWrite(16,0b01000000); // Set ADC digital volume attenuation to -32dB (left)
_es8388I2cWrite(17,0b01000000); // Set ADC digital volume attenuation to -32dB (right)
_es8388I2cWrite(38,0b00001001); // Mixer - route LIN2/RIN2 to output
_es8388I2cWrite(39,0b01010000); // Mixer - route LIN to mixL, 0dB gain
_es8388I2cWrite(42,0b01010000); // Mixer - route RIN to mixR, 0dB gain
_es8388I2cWrite(46,0b00011011); // LOUT1VOL - 0b00011110 = +0dB, 0b00011011 = LyraT balance fix
_es8388I2cWrite(47,0b00011110); // ROUT1VOL - 0b00011110 = +0dB
_es8388I2cWrite(48,0b00011110); // LOUT2VOL - 0b00011110 = +0dB
_es8388I2cWrite(49,0b00011110); // ROUT2VOL - 0b00011110 = +0dB
#endif
}
public:
ES8388Source(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f, bool i2sMaster=true) :
I2SSource(sampleRate, blockSize, sampleScale) {
_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
};
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
if ((i2sckPin < 0) || (mclkPin < 0)) {
DEBUGSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
return;
}
// First route mclk, then configure ADC over I2C, then configure I2S
_es8388InitAdc();
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
}
void deinitialize() {
I2SSource::deinitialize();
}
};
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
#if !defined(SOC_I2S_SUPPORTS_ADC) && !defined(SOC_I2S_SUPPORTS_ADC_DAC) #if !defined(SOC_I2S_SUPPORTS_ADC) && !defined(SOC_I2S_SUPPORTS_ADC_DAC)

View File

@ -85,8 +85,7 @@ class MPU6050Driver : public Usermod {
* setup() is called once at boot. WiFi is not yet connected at this point. * setup() is called once at boot. WiFi is not yet connected at this point.
*/ */
void setup() { void setup() {
PinManagerPinType pins[2] = { { i2c_scl, true }, { i2c_sda, true } }; if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; }
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.setClock(400000U); // 400kHz I2C clock. Comment this line if having compilation difficulties Wire.setClock(400000U); // 400kHz I2C clock. Comment this line if having compilation difficulties
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE

View File

@ -5,14 +5,18 @@
#ifndef MULTI_RELAY_MAX_RELAYS #ifndef MULTI_RELAY_MAX_RELAYS
#define MULTI_RELAY_MAX_RELAYS 4 #define MULTI_RELAY_MAX_RELAYS 4
#else #else
#if MULTI_RELAY_MAX_RELAYS>16 #if MULTI_RELAY_MAX_RELAYS>8
#undef MULTI_RELAY_MAX_RELAYS #undef MULTI_RELAY_MAX_RELAYS
#define MULTI_RELAY_MAX_RELAYS 16 #define MULTI_RELAY_MAX_RELAYS 8
#warning Maximum relays set to 8
#endif #endif
#endif #endif
#ifndef MULTI_RELAY_PINS #ifndef MULTI_RELAY_PINS
#define MULTI_RELAY_PINS -1 #define MULTI_RELAY_PINS -1
#define MULTI_RELAY_ENABLED false
#else
#define MULTI_RELAY_ENABLED true
#endif #endif
#define WLED_DEBOUNCE_THRESHOLD 50 //only consider button input of at least 50ms as valid (debouncing) #define WLED_DEBOUNCE_THRESHOLD 50 //only consider button input of at least 50ms as valid (debouncing)
@ -45,7 +49,7 @@ typedef struct relay_t {
int8_t pin; int8_t pin;
struct { // reduces memory footprint struct { // reduces memory footprint
bool active : 1; // is the relay waiting to be switched bool active : 1; // is the relay waiting to be switched
bool mode : 1; // does On mean 1 or 0 (inverted output) bool invert : 1; // does On mean 1 or 0
bool state : 1; // 1 relay is On, 0 relay is Off bool state : 1; // 1 relay is On, 0 relay is Off
bool external : 1; // is the relay externally controlled bool external : 1; // is the relay externally controlled
int8_t button : 4; // which button triggers relay int8_t button : 4; // which button triggers relay
@ -315,7 +319,7 @@ int MultiRelay::getValue(String data, char separator, int index) {
//Write a byte to the IO expander //Write a byte to the IO expander
byte MultiRelay::IOexpanderWrite(byte address, byte _data ) { byte MultiRelay::IOexpanderWrite(byte address, byte _data ) {
Wire.beginTransmission(addrPcf8574 + address); Wire.beginTransmission(address);
Wire.write(_data); Wire.write(_data);
return Wire.endTransmission(); return Wire.endTransmission();
} }
@ -323,7 +327,7 @@ byte MultiRelay::IOexpanderWrite(byte address, byte _data ) {
//Read a byte from the IO expander //Read a byte from the IO expander
byte MultiRelay::IOexpanderRead(int address) { byte MultiRelay::IOexpanderRead(int address) {
byte _data = 0; byte _data = 0;
Wire.requestFrom(addrPcf8574 + address, 1); Wire.requestFrom(address, 1);
if (Wire.available()) { if (Wire.available()) {
_data = Wire.read(); _data = Wire.read();
} }
@ -335,7 +339,7 @@ byte MultiRelay::IOexpanderRead(int address) {
MultiRelay::MultiRelay() MultiRelay::MultiRelay()
: _switchTimerStart(0) : _switchTimerStart(0)
, enabled(false) , enabled(MULTI_RELAY_ENABLED)
, initDone(false) , initDone(false)
, usePcf8574(USE_PCF8574) , usePcf8574(USE_PCF8574)
, addrPcf8574(PCF8574_ADDRESS) , addrPcf8574(PCF8574_ADDRESS)
@ -347,7 +351,7 @@ MultiRelay::MultiRelay()
for (size_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { for (size_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
_relay[i].pin = i<sizeof(defPins) ? defPins[i] : -1; _relay[i].pin = i<sizeof(defPins) ? defPins[i] : -1;
_relay[i].delay = 0; _relay[i].delay = 0;
_relay[i].mode = false; _relay[i].invert = false;
_relay[i].active = false; _relay[i].active = false;
_relay[i].state = false; _relay[i].state = false;
_relay[i].external = false; _relay[i].external = false;
@ -359,25 +363,27 @@ MultiRelay::MultiRelay()
* switch relay on/off * switch relay on/off
*/ */
void MultiRelay::switchRelay(uint8_t relay, bool mode) { void MultiRelay::switchRelay(uint8_t relay, bool mode) {
if (relay>=MULTI_RELAY_MAX_RELAYS || (_relay[relay].pin<0 && !usePcf8574)) return; if (relay>=MULTI_RELAY_MAX_RELAYS || _relay[relay].pin<0) return;
_relay[relay].state = mode; _relay[relay].state = mode;
if (usePcf8574) { if (usePcf8574 && _relay[relay].pin >= 100) {
byte expander = relay/8; // we need to send all ouputs at the same time
uint16_t state = 0; uint8_t state = 0;
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) state |= (uint16_t)_relay[i].state << i; // fill relay states for all pins for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
state = (mode ? !_relay[relay].mode : _relay[relay].mode) ? state | (1<<relay) : state & ~(1<<relay); // take into account invert mode if (_relay[i].pin < 100) continue;
IOexpanderWrite(expander, state>>(8*expander)); uint8_t pin = _relay[i].pin - 100;
DEBUG_PRINT(F("PCF8574 Writing to ")); DEBUG_PRINT(addrPcf8574 + expander); DEBUG_PRINT(F(" with data ")); DEBUG_PRINTLN(state>>(8*expander)); state |= (_relay[i].invert ? !_relay[i].state : _relay[i].state) << pin; // fill relay states for all pins
} else { }
IOexpanderWrite(addrPcf8574, state);
DEBUG_PRINT(F("Writing to PCF8574: ")); DEBUG_PRINTLN(state);
} else if (_relay[relay].pin < 100) {
pinMode(_relay[relay].pin, OUTPUT); pinMode(_relay[relay].pin, OUTPUT);
digitalWrite(_relay[relay].pin, mode ? !_relay[relay].mode : _relay[relay].mode); digitalWrite(_relay[relay].pin, _relay[relay].invert ? !_relay[relay].state : _relay[relay].state);
} } else return;
publishMqtt(relay); publishMqtt(relay);
} }
uint8_t MultiRelay::getActiveRelayCount() { uint8_t MultiRelay::getActiveRelayCount() {
uint8_t count = 0; uint8_t count = 0;
if (usePcf8574) return MULTI_RELAY_MAX_RELAYS; // we don't know how many there are
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) if (_relay[i].pin>=0) count++; for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) if (_relay[i].pin>=0) count++;
return count; return count;
} }
@ -476,29 +482,28 @@ void MultiRelay::publishHomeAssistantAutodiscovery() {
void MultiRelay::setup() { void MultiRelay::setup() {
// pins retrieved from cfg.json (readFromConfig()) prior to running setup() // pins retrieved from cfg.json (readFromConfig()) prior to running setup()
// if we want PCF8574 expander I2C pins need to be valid // if we want PCF8574 expander I2C pins need to be valid
if (i2c_sda == i2c_scl && i2c_sda == -1) usePcf8574 = false; if (i2c_sda<0 || i2c_scl<0) usePcf8574 = false;
if (usePcf8574) { uint8_t state = 0;
uint16_t state = 0; for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) state |= (uint16_t)(_relay[i].external ? (_relay[i].mode ? !_relay[i].state : _relay[i].state) : (_relay[i].mode ? !offMode : offMode)) << i; // fill relay states for all pins if (usePcf8574 && _relay[i].pin >= 100) {
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i += 8) { uint8_t pin = _relay[i].pin - 100;
byte expander = i/8; if (!_relay[i].external) _relay[i].state = !offMode;
IOexpanderWrite(expander, state>>(8*expander)); // init expander (set all outputs) state |= (uint8_t)(_relay[i].invert ? !_relay[i].state : _relay[i].state) << pin;
delay(1); } else if (_relay[i].pin<100 && _relay[i].pin>=0) {
} if (pinManager.allocatePin(_relay[i].pin,true, PinOwner::UM_MultiRelay)) {
DEBUG_PRINTLN(F("PCF8574(s) inited."));
} else {
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].pin<0) continue;
if (!pinManager.allocatePin(_relay[i].pin,true, PinOwner::UM_MultiRelay)) {
_relay[i].pin = -1; // allocation failed
} else {
if (!_relay[i].external) _relay[i].state = !offMode; if (!_relay[i].external) _relay[i].state = !offMode;
switchRelay(i, _relay[i].state); switchRelay(i, _relay[i].state);
_relay[i].active = false; _relay[i].active = false;
} else {
_relay[i].pin = -1; // allocation failed
} }
} }
} }
if (usePcf8574) {
IOexpanderWrite(addrPcf8574, state); // init expander (set all outputs)
DEBUG_PRINTLN(F("PCF8574(s) inited."));
}
_oldMode = offMode; _oldMode = offMode;
initDone = true; initDone = true;
} }
@ -519,7 +524,7 @@ void MultiRelay::loop() {
_oldMode = offMode; _oldMode = offMode;
_switchTimerStart = millis(); _switchTimerStart = millis();
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if ((_relay[i].pin>=0 || usePcf8574) && !_relay[i].external) _relay[i].active = true; if ((_relay[i].pin>=0) && !_relay[i].external) _relay[i].active = true;
} }
} }
@ -635,7 +640,7 @@ void MultiRelay::addToJsonInfo(JsonObject &root) {
String uiDomString; String uiDomString;
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if ((_relay[i].pin<0 && !usePcf8574) || !_relay[i].external) continue; if (_relay[i].pin<0 || !_relay[i].external) continue;
uiDomString = F("Relay "); uiDomString += i; uiDomString = F("Relay "); uiDomString += i;
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
@ -726,7 +731,7 @@ void MultiRelay::addToConfig(JsonObject &root) {
String parName = FPSTR(_relay_str); parName += '-'; parName += i; String parName = FPSTR(_relay_str); parName += '-'; parName += i;
JsonObject relay = top.createNestedObject(parName); JsonObject relay = top.createNestedObject(parName);
relay["pin"] = _relay[i].pin; relay["pin"] = _relay[i].pin;
relay[FPSTR(_activeHigh)] = _relay[i].mode; relay[FPSTR(_activeHigh)] = _relay[i].invert;
relay[FPSTR(_delay_str)] = _relay[i].delay; relay[FPSTR(_delay_str)] = _relay[i].delay;
relay[FPSTR(_external)] = _relay[i].external; relay[FPSTR(_external)] = _relay[i].external;
relay[FPSTR(_button)] = _relay[i].button; relay[FPSTR(_button)] = _relay[i].button;
@ -735,9 +740,10 @@ void MultiRelay::addToConfig(JsonObject &root) {
} }
void MultiRelay::appendConfigData() { void MultiRelay::appendConfigData() {
oappend(SET_F("addInfo('MultiRelay:first-PCF8574',1,'<i>(not hex!)</i>','address');")); oappend(SET_F("addInfo('MultiRelay:PCF8574-address',1,'<i>(not hex!)</i>');"));
oappend(SET_F("addInfo('MultiRelay:broadcast-sec',1,'(MQTT message)');")); oappend(SET_F("addInfo('MultiRelay:broadcast-sec',1,'(MQTT message)');"));
oappend(SET_F("addInfo('MultiRelay:relay-0:pin',1,'(use -1 for PCF8574)');")); //oappend(SET_F("addInfo('MultiRelay:relay-0:pin',1,'(use -1 for PCF8574)');"));
oappend(SET_F("d.extra.push({'MultiRelay':{pin:[['P0',100],['P1',101],['P2',102],['P3',103],['P4',104],['P5',105],['P6',106],['P7',107]]}});"));
} }
/** /**
@ -762,7 +768,7 @@ bool MultiRelay::readFromConfig(JsonObject &root) {
usePcf8574 = top[FPSTR(_pcf8574)] | usePcf8574; usePcf8574 = top[FPSTR(_pcf8574)] | usePcf8574;
addrPcf8574 = top[FPSTR(_pcfAddress)] | addrPcf8574; addrPcf8574 = top[FPSTR(_pcfAddress)] | addrPcf8574;
// if I2C is not globally initialised just ignore // if I2C is not globally initialised just ignore
if (i2c_sda == i2c_scl && i2c_sda == -1) usePcf8574 = false; if (i2c_sda<0 || i2c_scl<0) usePcf8574 = false;
periodicBroadcastSec = top[FPSTR(_broadcast)] | periodicBroadcastSec; periodicBroadcastSec = top[FPSTR(_broadcast)] | periodicBroadcastSec;
periodicBroadcastSec = min(900,max(0,(int)periodicBroadcastSec)); periodicBroadcastSec = min(900,max(0,(int)periodicBroadcastSec));
HAautodiscovery = top[FPSTR(_HAautodiscovery)] | HAautodiscovery; HAautodiscovery = top[FPSTR(_HAautodiscovery)] | HAautodiscovery;
@ -771,14 +777,14 @@ bool MultiRelay::readFromConfig(JsonObject &root) {
String parName = FPSTR(_relay_str); parName += '-'; parName += i; String parName = FPSTR(_relay_str); parName += '-'; parName += i;
oldPin[i] = _relay[i].pin; oldPin[i] = _relay[i].pin;
_relay[i].pin = top[parName]["pin"] | _relay[i].pin; _relay[i].pin = top[parName]["pin"] | _relay[i].pin;
_relay[i].mode = top[parName][FPSTR(_activeHigh)] | _relay[i].mode; _relay[i].invert = top[parName][FPSTR(_activeHigh)] | _relay[i].invert;
_relay[i].external = top[parName][FPSTR(_external)] | _relay[i].external; _relay[i].external = top[parName][FPSTR(_external)] | _relay[i].external;
_relay[i].delay = top[parName][FPSTR(_delay_str)] | _relay[i].delay; _relay[i].delay = top[parName][FPSTR(_delay_str)] | _relay[i].delay;
_relay[i].button = top[parName][FPSTR(_button)] | _relay[i].button; _relay[i].button = top[parName][FPSTR(_button)] | _relay[i].button;
// begin backwards compatibility (beta) remove when 0.13 is released // begin backwards compatibility (beta) remove when 0.13 is released
parName += '-'; parName += '-';
_relay[i].pin = top[parName+"pin"] | _relay[i].pin; _relay[i].pin = top[parName+"pin"] | _relay[i].pin;
_relay[i].mode = top[parName+FPSTR(_activeHigh)] | _relay[i].mode; _relay[i].invert = top[parName+FPSTR(_activeHigh)] | _relay[i].invert;
_relay[i].external = top[parName+FPSTR(_external)] | _relay[i].external; _relay[i].external = top[parName+FPSTR(_external)] | _relay[i].external;
_relay[i].delay = top[parName+FPSTR(_delay_str)] | _relay[i].delay; _relay[i].delay = top[parName+FPSTR(_delay_str)] | _relay[i].delay;
// end compatibility // end compatibility
@ -792,22 +798,11 @@ bool MultiRelay::readFromConfig(JsonObject &root) {
} else { } else {
// deallocate all pins 1st // deallocate all pins 1st
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++)
if (oldPin[i]>=0) { if (oldPin[i]>=0 && oldPin[i]<100) {
pinManager.deallocatePin(oldPin[i], PinOwner::UM_MultiRelay); pinManager.deallocatePin(oldPin[i], PinOwner::UM_MultiRelay);
} }
// allocate new pins // allocate new pins
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { setup();
if (_relay[i].pin>=0 && pinManager.allocatePin(_relay[i].pin, true, PinOwner::UM_MultiRelay)) {
if (!_relay[i].external) {
_relay[i].state = !offMode;
switchRelay(i, _relay[i].state);
_oldMode = offMode;
}
} else {
_relay[i].pin = -1;
}
_relay[i].active = false;
}
DEBUG_PRINTLN(F(" config (re)loaded.")); DEBUG_PRINTLN(F(" config (re)loaded."));
} }
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features // use "return !top["newestParameter"].isNull();" when updating Usermod with new features
@ -825,4 +820,4 @@ const char MultiRelay::_button[] PROGMEM = "button";
const char MultiRelay::_broadcast[] PROGMEM = "broadcast-sec"; const char MultiRelay::_broadcast[] PROGMEM = "broadcast-sec";
const char MultiRelay::_HAautodiscovery[] PROGMEM = "HA-autodiscovery"; const char MultiRelay::_HAautodiscovery[] PROGMEM = "HA-autodiscovery";
const char MultiRelay::_pcf8574[] PROGMEM = "use-PCF8574"; const char MultiRelay::_pcf8574[] PROGMEM = "use-PCF8574";
const char MultiRelay::_pcfAddress[] PROGMEM = "first-PCF8574"; const char MultiRelay::_pcfAddress[] PROGMEM = "PCF8574-address";

View File

@ -16,7 +16,6 @@ class ShtUsermod : public Usermod
private: private:
bool enabled = false; // Is usermod enabled or not bool enabled = false; // Is usermod enabled or not
bool firstRunDone = false; // Remembers if the first config load run had been done bool firstRunDone = false; // Remembers if the first config load run had been done
bool pinAllocDone = true; // Remembers if we have allocated pins
bool initDone = false; // Remembers if the mod has been completely initialised bool initDone = false; // Remembers if the mod has been completely initialised
bool haMqttDiscovery = false; // Is MQTT discovery enabled or not bool haMqttDiscovery = false; // Is MQTT discovery enabled or not
bool haMqttDiscoveryDone = false; // Remembers if we already published the HA discovery topics bool haMqttDiscoveryDone = false; // Remembers if we already published the HA discovery topics
@ -94,7 +93,7 @@ void ShtUsermod::initShtTempHumiditySensor()
case USERMOD_SHT_TYPE_SHT85: shtTempHumidSensor = (SHT *) new SHT85(); break; case USERMOD_SHT_TYPE_SHT85: shtTempHumidSensor = (SHT *) new SHT85(); break;
} }
shtTempHumidSensor->begin(shtI2cAddress, i2c_sda, i2c_scl); shtTempHumidSensor->begin(shtI2cAddress); // uses &Wire
if (shtTempHumidSensor->readStatus() == 0xFFFF) { if (shtTempHumidSensor->readStatus() == 0xFFFF) {
DEBUG_PRINTF("[%s] SHT init failed!\n", _name); DEBUG_PRINTF("[%s] SHT init failed!\n", _name);
cleanup(); cleanup();
@ -132,13 +131,6 @@ void ShtUsermod::cleanupShtTempHumiditySensor()
void ShtUsermod::cleanup() void ShtUsermod::cleanup()
{ {
cleanupShtTempHumiditySensor(); cleanupShtTempHumiditySensor();
if (pinAllocDone) {
PinManagerPinType pins[2] = { { i2c_sda, true }, { i2c_scl, true } };
pinManager.deallocateMultiplePins(pins, 2, PinOwner::HW_I2C);
pinAllocDone = false;
}
enabled = false; enabled = false;
} }
@ -237,14 +229,12 @@ void ShtUsermod::appendDeviceToMqttDiscoveryMessage(JsonDocument& root) {
void ShtUsermod::setup() void ShtUsermod::setup()
{ {
if (enabled) { if (enabled) {
PinManagerPinType pins[2] = { { i2c_sda, true }, { i2c_scl, true } }; // GPIOs can be set to -1 , so check they're gt zero
// GPIOs can be set to -1 and allocateMultiplePins() will return true, so check they're gt zero if (i2c_sda < 0 || i2c_scl < 0) {
if (i2c_sda < 0 || i2c_scl < 0 || !pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { DEBUG_PRINTF("[%s] I2C bus not initialised!\n", _name);
DEBUG_PRINTF("[%s] SHT pin allocation failed!\n", _name);
cleanup(); cleanup();
return; return;
} }
pinAllocDone = true;
initShtTempHumiditySensor(); initShtTempHumiditySensor();

View File

@ -82,13 +82,14 @@ void DisplayTaskCode(void * parameter);
typedef enum { typedef enum {
NONE = 0, NONE = 0,
SSD1306, // U8X8_SSD1306_128X32_UNIVISION_HW_I2C SSD1306, // U8X8_SSD1306_128X32_UNIVISION_HW_I2C
SH1106, // U8X8_SH1106_128X64_WINSTAR_HW_I2C SH1106, // U8X8_SH1106_128X64_WINSTAR_HW_I2C
SSD1306_64, // U8X8_SSD1306_128X64_NONAME_HW_I2C SSD1306_64, // U8X8_SSD1306_128X64_NONAME_HW_I2C
SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C
SSD1305_64, // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C SSD1305_64, // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C
SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI
SSD1306_SPI64 // U8X8_SSD1306_128X64_NONAME_HW_SPI SSD1306_SPI64, // U8X8_SSD1306_128X64_NONAME_HW_SPI
SSD1309_SPI64 // U8X8_SSD1309_128X64_NONAME0_4W_HW_SPI
} DisplayType; } DisplayType;
@ -533,24 +534,18 @@ void FourLineDisplayUsermod::sleepOrClock(bool enabled) {
// gets called once at boot. Do all initialization that doesn't depend on // gets called once at boot. Do all initialization that doesn't depend on
// network here // network here
void FourLineDisplayUsermod::setup() { void FourLineDisplayUsermod::setup() {
if (type == NONE || !enabled) return; bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64 || type == SSD1309_SPI64);
bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64);
// check if pins are -1 and disable usermod as PinManager::allocateMultiplePins() will accept -1 as a valid pin // check if pins are -1 and disable usermod as PinManager::allocateMultiplePins() will accept -1 as a valid pin
if (isSPI) { if (isSPI) {
PinManagerPinType cspins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } }; if (spi_sclk<0 || spi_mosi<0 || ioPin[0]<0 || ioPin[1]<0 || ioPin[1]<0) {
if (ioPin[0]==-1 || ioPin[1]==-1 || ioPin[1]==-1) { type=NONE; return; }
if (!pinManager.allocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay)) { type=NONE; return; }
PinManagerPinType pins[2] = { { spi_sclk, true }, { spi_mosi, true } };
if (spi_sclk==-1 || spi_mosi==-1 || !pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_SPI)) {
pinManager.deallocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay);
type = NONE; type = NONE;
return; } else {
PinManagerPinType cspins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } };
if (!pinManager.allocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay)) { type = NONE; }
} }
} else { } else {
PinManagerPinType pins[2] = { {i2c_scl, true }, { i2c_sda, true } }; if (i2c_scl<0 || i2c_sda<0) { type=NONE; }
if (i2c_scl==-1 || i2c_sda==-1 || !pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { type=NONE; return; }
} }
DEBUG_PRINTLN(F("Allocating display.")); DEBUG_PRINTLN(F("Allocating display."));
@ -563,20 +558,16 @@ void FourLineDisplayUsermod::setup() {
case SSD1305_64: u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(); break; case SSD1305_64: u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(); break;
// U8X8 uses global SPI variable that is attached to VSPI bus on ESP32 // U8X8 uses global SPI variable that is attached to VSPI bus on ESP32
case SSD1306_SPI: u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset case SSD1306_SPI: u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset
case SSD1306_SPI64: u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset case SSD1306_SPI64: u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset
case SSD1309_SPI64: u8x8 = (U8X8 *) new U8X8_SSD1309_128X64_NONAME0_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset
// catchall // catchall
default: u8x8 = (U8X8 *) new U8X8_NULL(); break; default: u8x8 = (U8X8 *) new U8X8_NULL(); enabled = false; break; // catchall to create U8x8 instance
} }
if (nullptr == u8x8) { if (nullptr == u8x8) {
DEBUG_PRINTLN(F("Display init failed.")); DEBUG_PRINTLN(F("Display init failed."));
if (isSPI) { if (isSPI) {
int8_t pins[] = {spi_sclk, spi_mosi};
pinManager.deallocateMultiplePins((const uint8_t*)pins, 2, PinOwner::HW_SPI);
pinManager.deallocateMultiplePins((const uint8_t*)ioPin, 3, PinOwner::UM_FourLineDisplay); pinManager.deallocateMultiplePins((const uint8_t*)ioPin, 3, PinOwner::UM_FourLineDisplay);
} else {
int8_t pins[] = {i2c_scl, i2c_sda};
pinManager.deallocateMultiplePins((const uint8_t*)pins, 2, PinOwner::HW_I2C);
} }
type = NONE; type = NONE;
return; return;
@ -1215,6 +1206,7 @@ void FourLineDisplayUsermod::appendConfigData() {
oappend(SET_F("addOption(dd,'SSD1305 128x64',5);")); oappend(SET_F("addOption(dd,'SSD1305 128x64',5);"));
oappend(SET_F("addOption(dd,'SSD1306 SPI',6);")); oappend(SET_F("addOption(dd,'SSD1306 SPI',6);"));
oappend(SET_F("addOption(dd,'SSD1306 SPI 128x64',7);")); oappend(SET_F("addOption(dd,'SSD1306 SPI 128x64',7);"));
oappend(SET_F("addOption(dd,'SSD1309 SPI 128x64',8);"));
oappend(SET_F("addInfo('4LineDisplay:type',1,'<br><i class=\"warn\">Change may require reboot</i>','');")); oappend(SET_F("addInfo('4LineDisplay:type',1,'<br><i class=\"warn\">Change may require reboot</i>','');"));
oappend(SET_F("addInfo('4LineDisplay:pin[]',0,'','SPI CS');")); oappend(SET_F("addInfo('4LineDisplay:pin[]',0,'','SPI CS');"));
oappend(SET_F("addInfo('4LineDisplay:pin[]',1,'','SPI DC');")); oappend(SET_F("addInfo('4LineDisplay:pin[]',1,'','SPI DC');"));
@ -1306,38 +1298,30 @@ bool FourLineDisplayUsermod::readFromConfig(JsonObject& root) {
bool pinsChanged = false; bool pinsChanged = false;
for (byte i=0; i<3; i++) if (ioPin[i] != oldPin[i]) { pinsChanged = true; break; } for (byte i=0; i<3; i++) if (ioPin[i] != oldPin[i]) { pinsChanged = true; break; }
if (pinsChanged || type!=newType) { if (pinsChanged || type!=newType) {
bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64); bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64 || type == SSD1309_SPI64);
bool newSPI = (newType == SSD1306_SPI || newType == SSD1306_SPI64); bool newSPI = (newType == SSD1306_SPI || newType == SSD1306_SPI64 || newType == SSD1309_SPI64);
if (isSPI) { if (isSPI) {
if (pinsChanged || !newSPI) pinManager.deallocateMultiplePins((const uint8_t*)oldPin, 3, PinOwner::UM_FourLineDisplay); if (pinsChanged || !newSPI) pinManager.deallocateMultiplePins((const uint8_t*)oldPin, 3, PinOwner::UM_FourLineDisplay);
if (!newSPI) { if (!newSPI) {
// was SPI but is no longer SPI // was SPI but is no longer SPI
int8_t oldPins[] = {spi_sclk, spi_mosi}; if (i2c_scl<0 || i2c_sda<0) { newType=NONE; }
pinManager.deallocateMultiplePins((const uint8_t*)oldPins, 2, PinOwner::HW_SPI);
PinManagerPinType pins[2] = { {i2c_scl, true }, { i2c_sda, true } };
if (i2c_scl==-1 || i2c_sda==-1 || !pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { newType=NONE; }
} else { } else {
// still SPI but pins changed // still SPI but pins changed
PinManagerPinType cspins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } }; PinManagerPinType cspins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } };
if (ioPin[0]==-1 || ioPin[1]==-1 || ioPin[1]==-1) { newType=NONE; } if (ioPin[0]<0 || ioPin[1]<0 || ioPin[1]<0) { newType=NONE; }
else if (!pinManager.allocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay)) { newType=NONE; } else if (!pinManager.allocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay)) { newType=NONE; }
} }
} else if (newSPI) { } else if (newSPI) {
// was I2C but is now SPI // was I2C but is now SPI
int8_t oldPins[] = {i2c_scl, i2c_sda}; if (spi_sclk<0 || spi_mosi<0) {
pinManager.deallocateMultiplePins((const uint8_t*)oldPins, 2, PinOwner::HW_I2C); newType=NONE;
PinManagerPinType pins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } }; } else {
if (ioPin[0]==-1 || ioPin[1]==-1 || ioPin[1]==-1) { newType=NONE; } PinManagerPinType pins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } };
else if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_FourLineDisplay)) { newType=NONE; } if (ioPin[0]<0 || ioPin[1]<0 || ioPin[1]<0) { newType=NONE; }
else { else if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_FourLineDisplay)) { newType=NONE; }
PinManagerPinType pins[2] = { { spi_sclk, true }, { spi_mosi, true } };
if (spi_sclk==-1 || spi_mosi==-1 || !pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_SPI)) {
pinManager.deallocateMultiplePins(pins, 3, PinOwner::UM_FourLineDisplay);
newType = NONE;
}
} }
} else { } else {
// just I2C tye changed // just I2C type changed
} }
type = newType; type = newType;
switch (type) { switch (type) {
@ -1369,8 +1353,12 @@ bool FourLineDisplayUsermod::readFromConfig(JsonObject& root) {
u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1306_128x64_noname, u8x8_cad_001, u8x8_byte_arduino_hw_spi, u8x8_gpio_and_delay_arduino); u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1306_128x64_noname, u8x8_cad_001, u8x8_byte_arduino_hw_spi, u8x8_gpio_and_delay_arduino);
u8x8_SetPin_4Wire_HW_SPI(u8x8->getU8x8(), ioPin[0], ioPin[1], ioPin[2]); // Pins are cs, dc, reset u8x8_SetPin_4Wire_HW_SPI(u8x8->getU8x8(), ioPin[0], ioPin[1], ioPin[2]); // Pins are cs, dc, reset
break; break;
case SSD1309_SPI64:
u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1309_128x64_noname0, u8x8_cad_001, u8x8_byte_arduino_hw_spi, u8x8_gpio_and_delay_arduino);
u8x8_SetPin_4Wire_HW_SPI(u8x8->getU8x8(), ioPin[0], ioPin[1], ioPin[2]); // Pins are cs, dc, reset
default: default:
u8x8_Setup(u8x8->getU8x8(), u8x8_d_null_cb, u8x8_cad_empty, u8x8_byte_empty, u8x8_dummy_cb); u8x8_Setup(u8x8->getU8x8(), u8x8_d_null_cb, u8x8_cad_empty, u8x8_byte_empty, u8x8_dummy_cb);
enabled = false;
break; break;
} }
startDisplay(); startDisplay();

View File

@ -45,6 +45,10 @@
#define ENCODER_SW_PIN 19 #define ENCODER_SW_PIN 19
#endif #endif
#ifndef ENCODER_MAX_DELAY_MS // max delay between polling encoder pins
#define ENCODER_MAX_DELAY_MS 8 // 8 milliseconds => max 120 change impulses in 1 second, for full turn of a 30/30 encoder (4 changes per segment, 30 segments for one turn)
#endif
#ifndef USERMOD_USE_PCF8574 #ifndef USERMOD_USE_PCF8574
#undef USE_PCF8574 #undef USE_PCF8574
#define USE_PCF8574 false #define USE_PCF8574 false
@ -343,7 +347,7 @@ class RotaryEncoderUIUsermod : public Usermod {
*/ */
void addToConfig(JsonObject &root); void addToConfig(JsonObject &root);
//void appendConfigData(); void appendConfigData();
/** /**
* restore the changeable values * restore the changeable values
@ -376,6 +380,7 @@ class RotaryEncoderUIUsermod : public Usermod {
*/ */
byte RotaryEncoderUIUsermod::readPin(uint8_t pin) { byte RotaryEncoderUIUsermod::readPin(uint8_t pin) {
if (usePcf8574) { if (usePcf8574) {
if (pin >= 100) pin -= 100; // PCF I/O ports
return (pcfPortData>>pin) & 1; return (pcfPortData>>pin) & 1;
} else { } else {
return digitalRead(pin); return digitalRead(pin);
@ -473,7 +478,7 @@ void RotaryEncoderUIUsermod::setup()
DEBUG_PRINTLN(F("Usermod Rotary Encoder init.")); DEBUG_PRINTLN(F("Usermod Rotary Encoder init."));
if (usePcf8574) { if (usePcf8574) {
if ((i2c_sda == i2c_scl && i2c_sda == -1) || pinA<0 || pinB<0 || pinC<0) { if (i2c_sda < 0 || i2c_scl < 0 || pinA < 0 || pinB < 0 || pinC < 0) {
DEBUG_PRINTLN(F("I2C and/or PCF8574 pins unused, disabling.")); DEBUG_PRINTLN(F("I2C and/or PCF8574 pins unused, disabling."));
enabled = false; enabled = false;
return; return;
@ -538,8 +543,9 @@ void RotaryEncoderUIUsermod::setup()
*/ */
void RotaryEncoderUIUsermod::loop() void RotaryEncoderUIUsermod::loop()
{ {
if (!enabled || strip.isUpdating()) return; if (!enabled) return;
unsigned long currentTime = millis(); // get the current elapsed time unsigned long currentTime = millis(); // get the current elapsed time
if (strip.isUpdating() && ((currentTime - loopTime) < ENCODER_MAX_DELAY_MS)) return; // be nice, but not too nice
// Initialize effectCurrentIndex and effectPaletteIndex to // Initialize effectCurrentIndex and effectPaletteIndex to
// current state. We do it here as (at least) effectCurrent // current state. We do it here as (at least) effectCurrent
@ -1071,9 +1077,10 @@ void RotaryEncoderUIUsermod::addToConfig(JsonObject &root) {
DEBUG_PRINTLN(F("Rotary Encoder config saved.")); DEBUG_PRINTLN(F("Rotary Encoder config saved."));
} }
//void RotaryEncoderUIUsermod::appendConfigData() { void RotaryEncoderUIUsermod::appendConfigData() {
// oappend(SET_F("addInfo('RotaryEncoderUIUsermod:PCF8574-address',1,'<i>(not hex!)</i>');")); oappend(SET_F("addInfo('Rotary-Encoder:PCF8574-address',1,'<i>(not hex!)</i>');"));
//} oappend(SET_F("d.extra.push({'Rotary-Encoder':{pin:[['P0',100],['P1',101],['P2',102],['P3',103],['P4',104],['P5',105],['P6',106],['P7',107]]}});"));
}
/** /**
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json * readFromConfig() is called before setup() to populate properties from values stored in cfg.json
@ -1122,7 +1129,7 @@ bool RotaryEncoderUIUsermod::readFromConfig(JsonObject &root) {
pinManager.deallocatePin(pinIRQ, PinOwner::UM_RotaryEncoderUI); pinManager.deallocatePin(pinIRQ, PinOwner::UM_RotaryEncoderUI);
DEBUG_PRINTLN(F("Deallocated old IRQ pin.")); DEBUG_PRINTLN(F("Deallocated old IRQ pin."));
} }
pinIRQ = newIRQpin; pinIRQ = newIRQpin<100 ? newIRQpin : -1; // ignore PCF8574 pins
} else { } else {
pinManager.deallocatePin(pinA, PinOwner::UM_RotaryEncoderUI); pinManager.deallocatePin(pinA, PinOwner::UM_RotaryEncoderUI);
pinManager.deallocatePin(pinB, PinOwner::UM_RotaryEncoderUI); pinManager.deallocatePin(pinB, PinOwner::UM_RotaryEncoderUI);

View File

@ -1,25 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28010.2046
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wled00", "wled00\wled00.vcxproj", "{C5F80730-F44F-4478-BDAE-6634EFC2CA88}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.ActiveCfg = Debug|Win32
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.Build.0 = Debug|Win32
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.ActiveCfg = Release|Win32
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9A679C2B-61D3-400B-B96F-06E604E9CED2}
EndGlobalSection
EndGlobal

View File

@ -278,7 +278,7 @@ uint16_t mode_random_color(void) {
SEGMENT.fill(color_blend(SEGMENT.color_wheel(SEGENV.aux1), SEGMENT.color_wheel(SEGENV.aux0), fade)); SEGMENT.fill(color_blend(SEGMENT.color_wheel(SEGENV.aux1), SEGMENT.color_wheel(SEGENV.aux0), fade));
return FRAMETIME; return FRAMETIME;
} }
static const char _data_FX_MODE_RANDOM_COLOR[] PROGMEM = "Random Colors@!,Fade time;;!"; static const char _data_FX_MODE_RANDOM_COLOR[] PROGMEM = "Random Colors@!,Fade time;;!;01";
/* /*
@ -433,7 +433,7 @@ uint16_t mode_rainbow(void) {
return FRAMETIME; return FRAMETIME;
} }
static const char _data_FX_MODE_RAINBOW[] PROGMEM = "Colorloop@!,Saturation;;!"; static const char _data_FX_MODE_RAINBOW[] PROGMEM = "Colorloop@!,Saturation;;!;01";
/* /*
@ -5095,21 +5095,22 @@ uint16_t mode_2DLissajous(void) { // By: Andrew Tuline
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
SEGMENT.fadeToBlackBy(SEGMENT.intensity); SEGMENT.fadeToBlackBy(SEGMENT.intensity);
uint_fast16_t phase = (millis() * (1 + SEGENV.custom3)) /32; // allow user to control rotation speed
//for (int i=0; i < 4*(cols+rows); i ++) { //for (int i=0; i < 4*(cols+rows); i ++) {
for (int i=0; i < 256; i ++) { for (int i=0; i < 256; i ++) {
//float xlocn = float(sin8(now/4+i*(SEGMENT.speed>>5))) / 255.0f; //float xlocn = float(sin8(now/4+i*(SEGMENT.speed>>5))) / 255.0f;
//float ylocn = float(cos8(now/4+i*2)) / 255.0f; //float ylocn = float(cos8(now/4+i*2)) / 255.0f;
uint8_t xlocn = sin8(millis()/4+i*(SEGMENT.speed>>5)); uint_fast8_t xlocn = sin8(phase/2 + (i*SEGMENT.speed)/32);
uint8_t ylocn = cos8(millis()/4+i*2); uint_fast8_t ylocn = cos8(phase/2 + i*2);
xlocn = map(xlocn,0,255,0,cols-1); xlocn = (cols < 2) ? 1 : (map(2*xlocn, 0,511, 0,2*(cols-1)) +1) /2; // softhack007: "(2* ..... +1) /2" for proper rounding
ylocn = map(ylocn,0,255,0,rows-1); ylocn = (rows < 2) ? 1 : (map(2*ylocn, 0,511, 0,2*(rows-1)) +1) /2; // "rows > 1" is needed to avoid div/0 in map()
SEGMENT.setPixelColorXY(xlocn, ylocn, SEGMENT.color_from_palette(millis()/100+i, false, PALETTE_SOLID_WRAP, 0)); SEGMENT.setPixelColorXY((uint8_t)xlocn, (uint8_t)ylocn, SEGMENT.color_from_palette(millis()/100+i, false, PALETTE_SOLID_WRAP, 0));
} }
return FRAMETIME; return FRAMETIME;
} // mode_2DLissajous() } // mode_2DLissajous()
static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,Fade rate;!;!;2"; static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,Fade rate,,,Speed;!;!;2;;c3=15";
/////////////////////// ///////////////////////
@ -5273,7 +5274,7 @@ uint16_t mode_2DPlasmaball(void) { // By: Stepko https://edito
SEGMENT.fadeToBlackBy(SEGMENT.custom1>>2); SEGMENT.fadeToBlackBy(SEGMENT.custom1>>2);
float t = millis() / (33 - SEGMENT.speed/8); uint_fast32_t t = (millis() * 8) / (256 - SEGMENT.speed); // optimized to avoid float
for (int i = 0; i < cols; i++) { for (int i = 0; i < cols; i++) {
uint16_t thisVal = inoise8(i * 30, t, t); uint16_t thisVal = inoise8(i * 30, t, t);
uint16_t thisMax = map(thisVal, 0, 255, 0, cols-1); uint16_t thisMax = map(thisVal, 0, 255, 0, cols-1);
@ -5876,11 +5877,17 @@ uint16_t mode_2Dscrollingtext(void) {
case 4: letterWidth = 7; letterHeight = 9; break; case 4: letterWidth = 7; letterHeight = 9; break;
case 5: letterWidth = 5; letterHeight = 12; break; case 5: letterWidth = 5; letterHeight = 12; break;
} }
const bool zero = SEGMENT.check3;
const int yoffset = map(SEGMENT.intensity, 0, 255, -rows/2, rows/2) + (rows-letterHeight)/2; const int yoffset = map(SEGMENT.intensity, 0, 255, -rows/2, rows/2) + (rows-letterHeight)/2;
char text[33] = {'\0'}; char text[WLED_MAX_SEGNAME_LEN+1] = {'\0'};
if (SEGMENT.name) for (size_t i=0,j=0; i<strlen(SEGMENT.name); i++) if (SEGMENT.name[i]>31 && SEGMENT.name[i]<128) text[j++] = SEGMENT.name[i]; if (SEGMENT.name) for (size_t i=0,j=0; i<strlen(SEGMENT.name); i++) if (SEGMENT.name[i]>31 && SEGMENT.name[i]<128) text[j++] = SEGMENT.name[i];
if (!strlen(text) || !strncmp_P(text,PSTR("#DATE"),5) || !strncmp_P(text,PSTR("#DDMM"),5) || !strncmp_P(text,PSTR("#MMDD"),5) || !strncmp_P(text,PSTR("#TIME"),5) || !strncmp_P(text,PSTR("#HHMM"),5)) { // fallback if empty segment name: display date and time if (!strlen(text)
|| !strncmp_P(text,PSTR("#DATE"),5)
|| !strncmp_P(text,PSTR("#DDMM"),5)
|| !strncmp_P(text,PSTR("#MMDD"),5)
|| !strncmp_P(text,PSTR("#TIME"),5)
|| !strncmp_P(text,PSTR("#HHMM"),5)) { // fallback if empty segment name: display date and time
char sec[5]; char sec[5];
byte AmPmHour = hour(localTime); byte AmPmHour = hour(localTime);
boolean isitAM = true; boolean isitAM = true;
@ -5890,12 +5897,12 @@ uint16_t mode_2Dscrollingtext(void) {
} }
if (useAMPM) sprintf_P(sec, PSTR(" %2s"), (isitAM ? "AM" : "PM")); if (useAMPM) sprintf_P(sec, PSTR(" %2s"), (isitAM ? "AM" : "PM"));
else sprintf_P(sec, PSTR(":%02d"), second(localTime)); else sprintf_P(sec, PSTR(":%02d"), second(localTime));
if (!strncmp_P(text,PSTR("#DATE"),5)) sprintf_P(text, PSTR("%d.%d.%d"), day(localTime), month(localTime), year(localTime)); if (!strncmp_P(text,PSTR("#DATE"),5)) sprintf_P(text, zero?PSTR("%02d.%02d.%04d"):PSTR("%d.%d.%d"), day(localTime), month(localTime), year(localTime));
else if (!strncmp_P(text,PSTR("#DDMM"),5)) sprintf_P(text, PSTR("%d.%d"), day(localTime), month(localTime)); else if (!strncmp_P(text,PSTR("#DDMM"),5)) sprintf_P(text, zero?PSTR("%02d.%02d"):PSTR("%d.%d"), day(localTime), month(localTime));
else if (!strncmp_P(text,PSTR("#MMDD"),5)) sprintf_P(text, PSTR("%d/%d"), month(localTime), day(localTime)); else if (!strncmp_P(text,PSTR("#MMDD"),5)) sprintf_P(text, zero?PSTR("%02d/%02d"):PSTR("%d/%d"), month(localTime), day(localTime));
else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec); else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, zero?PSTR("%02d:%02d%s"):PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec);
else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, PSTR("%2d:%02d"), AmPmHour, minute(localTime)); else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, zero?PSTR("%02d:%02d"):PSTR("%d:%02d"), AmPmHour, minute(localTime));
else sprintf_P(text, PSTR("%s %d, %d %2d:%02d%s"), monthShortStr(month(localTime)), day(localTime), year(localTime), AmPmHour, minute(localTime), sec); else sprintf_P(text, zero?PSTR("%s %02d, %04d %02d:%02d%s"):PSTR("%s %d, %d %d:%02d%s"), monthShortStr(month(localTime)), day(localTime), year(localTime), AmPmHour, minute(localTime), sec);
} }
const int numberOfLetters = strlen(text); const int numberOfLetters = strlen(text);
@ -5922,7 +5929,7 @@ uint16_t mode_2Dscrollingtext(void) {
return FRAMETIME; return FRAMETIME;
} }
static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,,Gradient,Overlay;!,!,Gradient;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0"; static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,,Gradient,Overlay,0;!,!,Gradient;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0";
//////////////////////////// ////////////////////////////
@ -6833,7 +6840,7 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch
return FRAMETIME; return FRAMETIME;
} // mode_freqmatrix() } // mode_freqmatrix()
static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Time delay,Sound effect,Low bin,High bin,Sensivity;;;1f;m12=3,si=0"; // Corner, Beatsin static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Speed,Sound effect,Low bin,High bin,Sensivity;;;1f;m12=3,si=0"; // Corner, Beatsin
////////////////////// //////////////////////
@ -6937,7 +6944,7 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun
return FRAMETIME; return FRAMETIME;
} // mode_freqwave() } // mode_freqwave()
static const char _data_FX_MODE_FREQWAVE[] PROGMEM = "Freqwave@Time delay,Sound effect,Low bin,High bin,Pre-amp;;;1f;m12=2,si=0"; // Circle, Beatsin static const char _data_FX_MODE_FREQWAVE[] PROGMEM = "Freqwave@Speed,Sound effect,Low bin,High bin,Pre-amp;;;1f;m12=2,si=0"; // Circle, Beatsin
/////////////////////// ///////////////////////

View File

@ -487,7 +487,7 @@ typedef struct Segment {
//if (leds) Serial.printf(" [%u]", length()*sizeof(CRGB)); //if (leds) Serial.printf(" [%u]", length()*sizeof(CRGB));
//Serial.println(); //Serial.println();
//#endif //#endif
if (!Segment::_globalLeds && leds) free(leds); if (!Segment::_globalLeds && leds) { free(leds); leds = nullptr;} // reset to nullptr, to avoid race conditions
if (name) delete[] name; if (name) delete[] name;
if (_t) delete _t; if (_t) delete _t;
deallocateData(); deallocateData();
@ -507,9 +507,9 @@ typedef struct Segment {
inline bool hasRGB(void) const { return _isRGB; } inline bool hasRGB(void) const { return _isRGB; }
inline bool hasWhite(void) const { return _hasW; } inline bool hasWhite(void) const { return _hasW; }
inline bool isCCT(void) const { return _isCCT; } inline bool isCCT(void) const { return _isCCT; }
inline uint16_t width(void) const { return stop - start; } // segment width in physical pixels (length if 1D) inline uint16_t width(void) const { return (stop > start) ? (stop - start) : 0; } // segment width in physical pixels (length if 1D)
inline uint16_t height(void) const { return stopY - startY; } // segment height (if 2D) in physical pixels inline uint16_t height(void) const { return (stopY > startY) ? (stopY - startY) : 0; } // segment height (if 2D) in physical pixels // softhack007: make sure its always > 0
inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels
inline uint16_t groupLength(void) const { return grouping + spacing; } inline uint16_t groupLength(void) const { return grouping + spacing; }
inline uint8_t getLightCapabilities(void) const { return _capabilities; } inline uint8_t getLightCapabilities(void) const { return _capabilities; }
@ -710,7 +710,7 @@ class WS2812FX { // 96 bytes
panel.clear(); panel.clear();
#endif #endif
customPalettes.clear(); customPalettes.clear();
if (useLedsArray && Segment::_globalLeds) free(Segment::_globalLeds); if (useLedsArray && Segment::_globalLeds) {free(Segment::_globalLeds); Segment::_globalLeds = nullptr;} // reset to nullptr, to avoid race conditions
} }
static WS2812FX* getInstance(void) { return instance; } static WS2812FX* getInstance(void) { return instance; }

View File

@ -191,6 +191,8 @@ uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) {
uint16_t /*IRAM_ATTR*/ Segment::XY(uint16_t x, uint16_t y) { uint16_t /*IRAM_ATTR*/ Segment::XY(uint16_t x, uint16_t y) {
uint16_t width = virtualWidth(); // segment width in logical pixels uint16_t width = virtualWidth(); // segment width in logical pixels
uint16_t height = virtualHeight(); // segment height in logical pixels uint16_t height = virtualHeight(); // segment height in logical pixels
if (width == 0) return 0; // softhack007 avoid div/0
if (height == 0) return (x%width); // softhack007 avoid div/0
return (x%width) + (y%height) * width; return (x%width) + (y%height) * width;
} }
@ -202,7 +204,6 @@ void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col)
if (leds) leds[XY(x,y)] = col; if (leds) leds[XY(x,y)] = col;
uint8_t _bri_t = currentBri(on ? opacity : 0); uint8_t _bri_t = currentBri(on ? opacity : 0);
if (!_bri_t && !transitional) return;
if (_bri_t < 255) { if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t); byte r = scale8(R(col), _bri_t);
byte g = scale8(G(col), _bri_t); byte g = scale8(G(col), _bri_t);
@ -305,6 +306,7 @@ void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t
// Adds the specified color with the existing pixel color perserving color balance. // Adds the specified color with the existing pixel color perserving color balance.
void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) { void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) {
if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit
uint32_t col = getPixelColorXY(x,y); uint32_t col = getPixelColorXY(x,y);
uint8_t r = R(col); uint8_t r = R(col);
uint8_t g = G(col); uint8_t g = G(col);
@ -329,50 +331,54 @@ void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) {
// blurRow: perform a blur on a row of a rectangular matrix // blurRow: perform a blur on a row of a rectangular matrix
void Segment::blurRow(uint16_t row, fract8 blur_amount) { void Segment::blurRow(uint16_t row, fract8 blur_amount) {
const uint16_t cols = virtualWidth(); const uint_fast16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint_fast16_t rows = virtualHeight();
if (row >= rows) return; if (row >= rows) return;
// blur one row // blur one row
uint8_t keep = 255 - blur_amount; uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1; uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black; CRGB carryover = CRGB::Black;
for (uint16_t x = 0; x < cols; x++) { for (uint_fast16_t x = 0; x < cols; x++) {
CRGB cur = getPixelColorXY(x, row); CRGB cur = getPixelColorXY(x, row);
CRGB before = cur; // remember color before blur
CRGB part = cur; CRGB part = cur;
part.nscale8(seep); part.nscale8(seep);
cur.nscale8(keep); cur.nscale8(keep);
cur += carryover; cur += carryover;
if (x) { if (x>0) {
CRGB prev = CRGB(getPixelColorXY(x-1, row)) + part; CRGB prev = CRGB(getPixelColorXY(x-1, row)) + part;
setPixelColorXY(x-1, row, prev); setPixelColorXY(x-1, row, prev);
} }
setPixelColorXY(x, row, cur); if (before != cur) // optimization: only set pixel if color has changed
setPixelColorXY(x, row, cur);
carryover = part; carryover = part;
} }
} }
// blurCol: perform a blur on a column of a rectangular matrix // blurCol: perform a blur on a column of a rectangular matrix
void Segment::blurCol(uint16_t col, fract8 blur_amount) { void Segment::blurCol(uint16_t col, fract8 blur_amount) {
const uint16_t cols = virtualWidth(); const uint_fast16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint_fast16_t rows = virtualHeight();
if (col >= cols) return; if (col >= cols) return;
// blur one column // blur one column
uint8_t keep = 255 - blur_amount; uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1; uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black; CRGB carryover = CRGB::Black;
for (uint16_t i = 0; i < rows; i++) { for (uint_fast16_t y = 0; y < rows; y++) {
CRGB cur = getPixelColorXY(col, i); CRGB cur = getPixelColorXY(col, y);
CRGB part = cur; CRGB part = cur;
CRGB before = cur; // remember color before blur
part.nscale8(seep); part.nscale8(seep);
cur.nscale8(keep); cur.nscale8(keep);
cur += carryover; cur += carryover;
if (i) { if (y>0) {
CRGB prev = CRGB(getPixelColorXY(col, i-1)) + part; CRGB prev = CRGB(getPixelColorXY(col, y-1)) + part;
setPixelColorXY(col, i-1, prev); setPixelColorXY(col, y-1, prev);
} }
setPixelColorXY(col, i, cur); if (before != cur) // optimization: only set pixel if color has changed
setPixelColorXY(col, y, cur);
carryover = part; carryover = part;
} }
} }
@ -391,8 +397,8 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
for (uint16_t j = 0; j < dim1; j++) { for (uint16_t j = 0; j < dim1; j++) {
uint16_t x = vertical ? i : j; uint16_t x = vertical ? i : j;
uint16_t y = vertical ? j : i; uint16_t y = vertical ? j : i;
uint16_t xp = vertical ? x : x-1; int16_t xp = vertical ? x : x-1; // "signed" to prevent underflow
uint16_t yp = vertical ? y-1 : y; int16_t yp = vertical ? y-1 : y; // "signed" to prevent underflow
uint16_t xn = vertical ? x : x+1; uint16_t xn = vertical ? x : x+1;
uint16_t yn = vertical ? y+1 : y; uint16_t yn = vertical ? y+1 : y;
CRGB curr = getPixelColorXY(x,y); CRGB curr = getPixelColorXY(x,y);

View File

@ -90,18 +90,18 @@ Segment::Segment(const Segment &orig) {
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); } if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
if (orig.leds && !Segment::_globalLeds) { leds = (CRGB*)malloc(sizeof(CRGB)*length()); if (leds) memcpy(leds, orig.leds, sizeof(CRGB)*length()); } if (orig.leds && !Segment::_globalLeds && length() > 0) { leds = (CRGB*)malloc(sizeof(CRGB)*length()); if (leds) memcpy(leds, orig.leds, sizeof(CRGB)*length()); }
} }
// move constructor // move constructor
Segment::Segment(Segment &&orig) noexcept { Segment::Segment(Segment &&orig) noexcept {
//DEBUG_PRINTLN(F("-- Move segment constructor --")); //DEBUG_PRINTLN(F("-- Move segment constructor --"));
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.leds = nullptr;
orig.name = nullptr; orig.name = nullptr;
orig.data = nullptr; orig.data = nullptr;
orig._dataLen = 0; orig._dataLen = 0;
orig._t = nullptr; orig._t = nullptr;
orig.leds = nullptr;
} }
// copy assignment // copy assignment
@ -111,7 +111,7 @@ Segment& Segment::operator= (const Segment &orig) {
// clean destination // clean destination
if (name) delete[] name; if (name) delete[] name;
if (_t) delete _t; if (_t) delete _t;
if (leds && !Segment::_globalLeds) free(leds); if (leds && !Segment::_globalLeds) {free(leds); leds=nullptr;}
deallocateData(); deallocateData();
// copy source // copy source
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
@ -125,7 +125,7 @@ Segment& Segment::operator= (const Segment &orig) {
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); } if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
if (orig.leds && !Segment::_globalLeds) { leds = (CRGB*)malloc(sizeof(CRGB)*length()); if (leds) memcpy(leds, orig.leds, sizeof(CRGB)*length()); } if (orig.leds && !Segment::_globalLeds && length() > 0) { leds = (CRGB*)malloc(sizeof(CRGB)*length()); if (leds) memcpy(leds, orig.leds, sizeof(CRGB)*length()); }
} }
return *this; return *this;
} }
@ -137,7 +137,7 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
if (name) delete[] name; // free old name if (name) delete[] name; // free old name
deallocateData(); // free old runtime data deallocateData(); // free old runtime data
if (_t) delete _t; if (_t) delete _t;
if (leds && !Segment::_globalLeds) free(leds); if (leds && !Segment::_globalLeds) {free(leds); leds=nullptr;}
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.name = nullptr; orig.name = nullptr;
orig.data = nullptr; orig.data = nullptr;
@ -305,23 +305,26 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
} }
void Segment::startTransition(uint16_t dur) { void Segment::startTransition(uint16_t dur) {
if (transitional || _t) return; // already in transition no need to store anything if (!dur) {
transitional = false;
if (_t) {
delete _t;
_t = nullptr;
}
return;
}
if (transitional && _t) return; // already in transition no need to store anything
// starting a transition has to occur before change so we get current values 1st // starting a transition has to occur before change so we get current values 1st
uint8_t _briT = currentBri(on ? opacity : 0); _t = new Transition(dur); // no previous transition running
uint8_t _cctT = currentBri(cct, true);
CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette);
uint8_t _modeP = mode;
uint32_t _colorT[NUM_COLORS];
for (size_t i=0; i<NUM_COLORS; i++) _colorT[i] = currentColor(i, colors[i]);
if (!_t) _t = new Transition(dur); // no previous transition running
if (!_t) return; // failed to allocate data if (!_t) return; // failed to allocate data
_t->_briT = _briT;
_t->_cctT = _cctT; CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette);
_t->_briT = on ? opacity : 0;
_t->_cctT = cct;
_t->_palT = _palT; _t->_palT = _palT;
_t->_modeP = _modeP; _t->_modeP = mode;
for (size_t i=0; i<NUM_COLORS; i++) _t->_colorT[i] = _colorT[i]; for (size_t i=0; i<NUM_COLORS; i++) _t->_colorT[i] = colors[i];
transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true); transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true);
} }
@ -334,10 +337,10 @@ uint16_t Segment::progress() {
} }
uint8_t Segment::currentBri(uint8_t briNew, bool useCct) { uint8_t Segment::currentBri(uint8_t briNew, bool useCct) {
if (transitional && _t) { uint32_t prog = progress();
uint32_t prog = progress() + 1; if (transitional && _t && prog < 0xFFFFU) {
if (useCct) return ((briNew * prog) + _t->_cctT * (0x10000 - prog)) >> 16; if (useCct) return ((briNew * prog) + _t->_cctT * (0xFFFFU - prog)) >> 16;
else return ((briNew * prog) + _t->_briT * (0x10000 - prog)) >> 16; else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16;
} else { } else {
return briNew; return briNew;
} }
@ -368,6 +371,7 @@ CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal
void Segment::handleTransition() { void Segment::handleTransition() {
if (!transitional) return; if (!transitional) return;
uint16_t _progress = progress(); uint16_t _progress = progress();
if (_progress == 0xFFFFU) transitional = false; // finish transitioning segment
if (_t) { // thanks to @nXm AKA https://github.com/NMeirer if (_t) { // thanks to @nXm AKA https://github.com/NMeirer
if (_progress >= 32767U && _t->_modeP != mode) markForReset(); if (_progress >= 32767U && _t->_modeP != mode) markForReset();
if (_progress == 0xFFFFU) { if (_progress == 0xFFFFU) {
@ -375,7 +379,6 @@ void Segment::handleTransition() {
_t = nullptr; _t = nullptr;
} }
} }
if (_progress == 0xFFFFU) transitional = false; // finish transitioning segment
} }
void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y) { void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y) {
@ -544,6 +547,7 @@ uint16_t Segment::virtualLength() const {
} }
#endif #endif
uint16_t groupLen = groupLength(); uint16_t groupLen = groupLength();
if (groupLen < 1) groupLen = 1; // prevent division by zero - better safe than sorry ...
uint16_t vLength = (length() + groupLen - 1) / groupLen; uint16_t vLength = (length() + groupLen - 1) / groupLen;
if (mirror) vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED if (mirror) vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED
return vLength; return vLength;
@ -622,7 +626,6 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
uint16_t len = length(); uint16_t len = length();
uint8_t _bri_t = currentBri(on ? opacity : 0); uint8_t _bri_t = currentBri(on ? opacity : 0);
if (!_bri_t && !transitional && fadeTransition) return; // if _bri_t == 0 && segment is not transitionig && transitions are enabled then save a few CPU cycles
if (_bri_t < 255) { if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t); byte r = scale8(R(col), _bri_t);
byte g = scale8(G(col), _bri_t); byte g = scale8(G(col), _bri_t);
@ -727,7 +730,7 @@ uint32_t Segment::getPixelColor(int i)
i += start; i += start;
/* offset/phase */ /* offset/phase */
i += offset; i += offset;
if (i >= stop) i -= length(); if ((i >= stop) && (stop>0)) i -= length(); // avoids negative pixel index (stop = 0 is a possible value)
return strip.getPixelColor(i); return strip.getPixelColor(i);
} }
@ -884,6 +887,7 @@ void Segment::fade_out(uint8_t rate) {
// fades all pixels to black using nscale8() // fades all pixels to black using nscale8()
void Segment::fadeToBlackBy(uint8_t fadeBy) { void Segment::fadeToBlackBy(uint8_t fadeBy) {
if (fadeBy == 0) return; // optimization - no scaling to apply
const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
const uint16_t rows = virtualHeight(); // will be 1 for 1D const uint16_t rows = virtualHeight(); // will be 1 for 1D
@ -898,23 +902,26 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) {
*/ */
void Segment::blur(uint8_t blur_amount) void Segment::blur(uint8_t blur_amount)
{ {
if (blur_amount == 0) return; // optimization: 0 means "don't blur"
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
if (is2D()) { if (is2D()) {
// compatibility with 2D // compatibility with 2D
const uint16_t cols = virtualWidth(); const uint_fast16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint_fast16_t rows = virtualHeight();
for (uint16_t i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows for (uint_fast16_t i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows
for (uint16_t k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns for (uint_fast16_t k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns
return; return;
} }
#endif #endif
uint8_t keep = 255 - blur_amount; uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1; uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black; CRGB carryover = CRGB::Black;
for(uint16_t i = 0; i < virtualLength(); i++) uint_fast16_t vlength = virtualLength();
for(uint_fast16_t i = 0; i < vlength; i++)
{ {
CRGB cur = CRGB(getPixelColor(i)); CRGB cur = CRGB(getPixelColor(i));
CRGB part = cur; CRGB part = cur;
CRGB before = cur; // remember color before blur
part.nscale8(seep); part.nscale8(seep);
cur.nscale8(keep); cur.nscale8(keep);
cur += carryover; cur += carryover;
@ -923,9 +930,10 @@ void Segment::blur(uint8_t blur_amount)
uint8_t r = R(c); uint8_t r = R(c);
uint8_t g = G(c); uint8_t g = G(c);
uint8_t b = B(c); uint8_t b = B(c);
setPixelColor(i-1, qadd8(r, part.red), qadd8(g, part.green), qadd8(b, part.blue)); setPixelColor((uint16_t)(i-1), qadd8(r, part.red), qadd8(g, part.green), qadd8(b, part.blue));
} }
setPixelColor(i,cur.red, cur.green, cur.blue); if (before != cur) // optimization: only set pixel if color has changed
setPixelColor((uint16_t)i,cur.red, cur.green, cur.blue);
carryover = part; carryover = part;
} }
} }
@ -1075,7 +1083,7 @@ void WS2812FX::finalizeInit(void)
//else //else
//#endif //#endif
Segment::_globalLeds = (CRGB*) malloc(arrSize); Segment::_globalLeds = (CRGB*) malloc(arrSize);
memset(Segment::_globalLeds, 0, arrSize); if (Segment::_globalLeds && (arrSize > 0)) memset(Segment::_globalLeds, 0, arrSize);
} }
//segments are created in makeAutoSegments(); //segments are created in makeAutoSegments();
@ -1220,7 +1228,8 @@ void WS2812FX::estimateCurrentAndLimitBri() {
} }
uint32_t powerSum0 = powerSum; uint32_t powerSum0 = powerSum;
powerSum *= _brightness; //powerSum *= _brightness; // for NPBrightnessBus
powerSum *= 255; // no need to scale down powerSum - NPB-LG getPixelColor returns colors scaled down by brightness
if (powerSum > powerBudget) //scale brightness down to stay in current limit if (powerSum > powerBudget) //scale brightness down to stay in current limit
{ {
@ -1228,11 +1237,14 @@ void WS2812FX::estimateCurrentAndLimitBri() {
uint16_t scaleI = scale * 255; uint16_t scaleI = scale * 255;
uint8_t scaleB = (scaleI > 255) ? 255 : scaleI; uint8_t scaleB = (scaleI > 255) ? 255 : scaleI;
uint8_t newBri = scale8(_brightness, scaleB); uint8_t newBri = scale8(_brightness, scaleB);
busses.setBrightness(newBri); //to keep brightness uniform, sets virtual busses too // to keep brightness uniform, sets virtual busses too - softhack007: apply reductions immediately
currentMilliamps = (powerSum0 * newBri) / puPerMilliamp; if (scaleB < 255) busses.setBrightness(scaleB, true); // NPB-LG has already applied brightness, so its suffifient to post-apply scaling ==> use scaleB instead of newBri
busses.setBrightness(newBri, false); // set new brightness for next frame
//currentMilliamps = (powerSum0 * newBri) / puPerMilliamp; // for NPBrightnessBus
currentMilliamps = (powerSum0 * scaleB) / puPerMilliamp; // for NPBus-LG
} else { } else {
currentMilliamps = powerSum / puPerMilliamp; currentMilliamps = powerSum / puPerMilliamp;
busses.setBrightness(_brightness); busses.setBrightness(_brightness, false); // set new brightness for next frame
} }
currentMilliamps += MA_FOR_ESP; //add power of ESP back to estimate currentMilliamps += MA_FOR_ESP; //add power of ESP back to estimate
currentMilliamps += pLen; //add standby power back to estimate currentMilliamps += pLen; //add standby power back to estimate
@ -1590,7 +1602,7 @@ void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) {
} }
void WS2812FX::setTransitionMode(bool t) { void WS2812FX::setTransitionMode(bool t) {
for (segment &seg : _segments) if (!seg.transitional) seg.startTransition(t ? _transitionDur : 0); for (segment &seg : _segments) seg.startTransition(t ? _transitionDur : 0);
} }
#ifdef WLED_DEBUG #ifdef WLED_DEBUG

View File

@ -9,9 +9,9 @@
#include <IPAddress.h> #include <IPAddress.h>
#define NODE_TYPE_ID_UNDEFINED 0 #define NODE_TYPE_ID_UNDEFINED 0
#define NODE_TYPE_ID_ESP8266 82 #define NODE_TYPE_ID_ESP8266 82 // should be 1
#define NODE_TYPE_ID_ESP32 32 #define NODE_TYPE_ID_ESP32 32 // should be 2
#define NODE_TYPE_ID_ESP32S2 33 #define NODE_TYPE_ID_ESP32S2 33 // etc
#define NODE_TYPE_ID_ESP32S3 34 #define NODE_TYPE_ID_ESP32S3 34
#define NODE_TYPE_ID_ESP32C3 35 #define NODE_TYPE_ID_ESP32C3 35
@ -23,7 +23,13 @@ struct NodeStruct
String nodeName; String nodeName;
IPAddress ip; IPAddress ip;
uint8_t age; uint8_t age;
uint8_t nodeType; union {
uint8_t nodeType; // a waste of space as we only have 5 types
struct {
uint8_t type : 7; // still a waste of space (4 bits would be enough and future-proof)
bool on : 1;
};
};
uint32_t build; uint32_t build;
NodeStruct() : age(0), nodeType(0), build(0) NodeStruct() : age(0), nodeType(0), build(0)

View File

@ -126,15 +126,15 @@ bool BusDigital::canShow() {
return PolyBus::canShow(_busPtr, _iType); return PolyBus::canShow(_busPtr, _iType);
} }
void BusDigital::setBrightness(uint8_t b) { void BusDigital::setBrightness(uint8_t b, bool immediate) {
//Fix for turning off onboard LED breaking bus //Fix for turning off onboard LED breaking bus
#ifdef LED_BUILTIN #ifdef LED_BUILTIN
if (_bri == 0 && b > 0) { if (_bri == 0 && b > 0) {
if (_pins[0] == LED_BUILTIN || _pins[1] == LED_BUILTIN) PolyBus::begin(_busPtr, _iType, _pins); if (_pins[0] == LED_BUILTIN || _pins[1] == LED_BUILTIN) PolyBus::begin(_busPtr, _iType, _pins);
} }
#endif #endif
Bus::setBrightness(b); Bus::setBrightness(b, immediate);
PolyBus::setBrightness(_busPtr, _iType, b); PolyBus::setBrightness(_busPtr, _iType, b, immediate);
} }
//If LEDs are skipped, it is possible to use the first as a status LED. //If LEDs are skipped, it is possible to use the first as a status LED.
@ -515,9 +515,9 @@ void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c, int16_t cct)
} }
} }
void BusManager::setBrightness(uint8_t b) { void BusManager::setBrightness(uint8_t b, bool immediate) {
for (uint8_t i = 0; i < numBusses; i++) { for (uint8_t i = 0; i < numBusses; i++) {
busses[i]->setBrightness(b); busses[i]->setBrightness(b, immediate);
} }
} }

View File

@ -108,7 +108,7 @@ class Bus {
virtual void setStatusPixel(uint32_t c) {} virtual void setStatusPixel(uint32_t c) {}
virtual void setPixelColor(uint16_t pix, uint32_t c) = 0; virtual void setPixelColor(uint16_t pix, uint32_t c) = 0;
virtual uint32_t getPixelColor(uint16_t pix) { return 0; } virtual uint32_t getPixelColor(uint16_t pix) { return 0; }
virtual void setBrightness(uint8_t b) { _bri = b; }; virtual void setBrightness(uint8_t b, bool immediate=false) { _bri = b; };
virtual void cleanup() = 0; virtual void cleanup() = 0;
virtual uint8_t getPins(uint8_t* pinArray) { return 0; } virtual uint8_t getPins(uint8_t* pinArray) { return 0; }
virtual uint16_t getLength() { return _len; } virtual uint16_t getLength() { return _len; }
@ -181,7 +181,7 @@ class BusDigital : public Bus {
bool canShow(); bool canShow();
void setBrightness(uint8_t b); void setBrightness(uint8_t b, bool immediate);
void setStatusPixel(uint32_t c); void setStatusPixel(uint32_t c);
@ -345,7 +345,7 @@ class BusManager {
void setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1); void setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1);
void setBrightness(uint8_t b); void setBrightness(uint8_t b, bool immediate=false); // immediate=true is for use in ABL, it applies brightness immediately (warning: inefficient)
void setSegmentCCT(int16_t cct, bool allowWBCorrection = false); void setSegmentCCT(int16_t cct, bool allowWBCorrection = false);

View File

@ -806,101 +806,101 @@ class PolyBus {
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
} }
}; };
static void setBrightness(void* busPtr, uint8_t busType, uint8_t b) { static void setBrightness(void* busPtr, uint8_t busType, uint8_t b, bool immediate) { // immediate=true is for use in ABL, it applies brightness immediately (warning: inefficient)
switch (busType) { switch (busType) {
case I_NONE: break; case I_NONE: break;
#ifdef ESP8266 #ifdef ESP8266
case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->SetLuminance(b); break; case I_8266_U0_NEO_3: (static_cast<B_8266_U0_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->SetLuminance(b); break; case I_8266_U1_NEO_3: (static_cast<B_8266_U1_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->SetLuminance(b); break; case I_8266_DM_NEO_3: (static_cast<B_8266_DM_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->SetLuminance(b); break; case I_8266_BB_NEO_3: (static_cast<B_8266_BB_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U0_NEO_4: (static_cast<B_8266_U0_NEO_4*>(busPtr))->SetLuminance(b); break; case I_8266_U0_NEO_4: (static_cast<B_8266_U0_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U1_NEO_4: (static_cast<B_8266_U1_NEO_4*>(busPtr))->SetLuminance(b); break; case I_8266_U1_NEO_4: (static_cast<B_8266_U1_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_DM_NEO_4: (static_cast<B_8266_DM_NEO_4*>(busPtr))->SetLuminance(b); break; case I_8266_DM_NEO_4: (static_cast<B_8266_DM_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_BB_NEO_4: (static_cast<B_8266_BB_NEO_4*>(busPtr))->SetLuminance(b); break; case I_8266_BB_NEO_4: (static_cast<B_8266_BB_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->SetLuminance(b); break; case I_8266_U0_400_3: (static_cast<B_8266_U0_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_400_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->SetLuminance(b); break; case I_8266_U1_400_3: (static_cast<B_8266_U1_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_400_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->SetLuminance(b); break; case I_8266_DM_400_3: (static_cast<B_8266_DM_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_400_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->SetLuminance(b); break; case I_8266_BB_400_3: (static_cast<B_8266_BB_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_400_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U0_TM1_4: (static_cast<B_8266_U0_TM1_4*>(busPtr))->SetLuminance(b); break; case I_8266_U0_TM1_4: (static_cast<B_8266_U0_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_TM1_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetLuminance(b); break; case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_TM1_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetLuminance(b); break; case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_TM1_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetLuminance(b); break; case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_TM1_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->SetLuminance(b); break; case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_TM2_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->SetLuminance(b); break; case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_TM2_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->SetLuminance(b); break; case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_TM2_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->SetLuminance(b); break; case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_TM2_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->SetLuminance(b); break; case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_UCS_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->SetLuminance(b); break; case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_UCS_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->SetLuminance(b); break; case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_UCS_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_BB_UCS_3: (static_cast<B_8266_BB_UCS_3*>(busPtr))->SetLuminance(b); break; case I_8266_BB_UCS_3: (static_cast<B_8266_BB_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_UCS_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U0_UCS_4: (static_cast<B_8266_U0_UCS_4*>(busPtr))->SetLuminance(b); break; case I_8266_U0_UCS_4: (static_cast<B_8266_U0_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U0_UCS_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_U1_UCS_4: (static_cast<B_8266_U1_UCS_4*>(busPtr))->SetLuminance(b); break; case I_8266_U1_UCS_4: (static_cast<B_8266_U1_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_U1_UCS_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_DM_UCS_4: (static_cast<B_8266_DM_UCS_4*>(busPtr))->SetLuminance(b); break; case I_8266_DM_UCS_4: (static_cast<B_8266_DM_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_DM_UCS_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_8266_BB_UCS_4: (static_cast<B_8266_BB_UCS_4*>(busPtr))->SetLuminance(b); break; case I_8266_BB_UCS_4: (static_cast<B_8266_BB_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_8266_BB_UCS_4*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetLuminance(b); break; case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->SetLuminance(b); break; case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetLuminance(b); break; case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
// case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->SetLuminance(b); break; // case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_BB_NEO_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetLuminance(b); break; case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetLuminance(b); break; case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetLuminance(b); break; case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
// case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->SetLuminance(b); break; // case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_BB_NEO_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetLuminance(b); break; case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_400_3*>(busPtr))->ApplyPostAdjustments(); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetLuminance(b); break; case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_400_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetLuminance(b); break; case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_400_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
// case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->SetLuminance(b); break; // case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_BB_400_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetLuminance(b); break; case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_TM1_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetLuminance(b); break; case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_TM2_3*>(busPtr))->ApplyPostAdjustments(); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetLuminance(b); break; case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_TM1_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->SetLuminance(b); break; case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_TM2_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetLuminance(b); break; case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_TM1_4*>(busPtr))->ApplyPostAdjustments(); break;
case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->SetLuminance(b); break; case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_TM2_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
case I_32_RN_UCS_3: (static_cast<B_32_RN_UCS_3*>(busPtr))->SetLuminance(b); break; case I_32_RN_UCS_3: (static_cast<B_32_RN_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_UCS_3*>(busPtr))->ApplyPostAdjustments(); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_3: (static_cast<B_32_I0_UCS_3*>(busPtr))->SetLuminance(b); break; case I_32_I0_UCS_3: (static_cast<B_32_I0_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_UCS_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->SetLuminance(b); break; case I_32_I1_UCS_3: (static_cast<B_32_I1_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_UCS_3*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
// case I_32_BB_UCS_3: (static_cast<B_32_BB_UCS_3*>(busPtr))->SetLuminance(b); break; // case I_32_BB_UCS_3: (static_cast<B_32_BB_UCS_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_BB_UCS_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->SetLuminance(b); break; case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_RN_UCS_4*>(busPtr))->ApplyPostAdjustments(); break;
#ifndef WLED_NO_I2S0_PIXELBUS #ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->SetLuminance(b); break; case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I0_UCS_4*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->SetLuminance(b); break; case I_32_I1_UCS_4: (static_cast<B_32_I1_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_I1_UCS_4*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
// case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->SetLuminance(b); break; // case I_32_BB_UCS_4: (static_cast<B_32_BB_UCS_4*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_32_BB_UCS_4*>(busPtr))->ApplyPostAdjustments(); break;
#endif #endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetLuminance(b); break; case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_HS_DOT_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetLuminance(b); break; case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_SS_DOT_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->SetLuminance(b); break; case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_HS_LPD_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->SetLuminance(b); break; case I_SS_LPD_3: (static_cast<B_SS_LPD_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_SS_LPD_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->SetLuminance(b); break; case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_HS_LPO_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->SetLuminance(b); break; case I_SS_LPO_3: (static_cast<B_SS_LPO_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_SS_LPO_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->SetLuminance(b); break; case I_HS_WS1_3: (static_cast<B_HS_WS1_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_HS_WS1_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->SetLuminance(b); break; case I_SS_WS1_3: (static_cast<B_SS_WS1_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_SS_WS1_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetLuminance(b); break; case I_HS_P98_3: (static_cast<B_HS_P98_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_HS_P98_3*>(busPtr))->ApplyPostAdjustments(); break;
case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetLuminance(b); break; case I_SS_P98_3: (static_cast<B_SS_P98_3*>(busPtr))->SetLuminance(b); if (immediate) (static_cast<B_SS_P98_3*>(busPtr))->ApplyPostAdjustments(); break;
} }
}; };
static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) { static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) {

View File

@ -197,6 +197,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
disablePullUp = !pull; disablePullUp = !pull;
JsonArray hw_btn_ins = btn_obj[F("ins")]; JsonArray hw_btn_ins = btn_obj[F("ins")];
if (!hw_btn_ins.isNull()) { if (!hw_btn_ins.isNull()) {
for (uint8_t b = 0; b < WLED_MAX_BUTTONS; b++) { // deallocate existing button pins
pinManager.deallocatePin(btnPin[b], PinOwner::Button); // does nothing if trying to deallocate a pin with PinOwner != Button
}
uint8_t s = 0; uint8_t s = 0;
for (JsonObject btn : hw_btn_ins) { for (JsonObject btn : hw_btn_ins) {
CJSON(buttonType[s], btn["type"]); CJSON(buttonType[s], btn["type"]);
@ -264,6 +267,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
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) {
pinManager.deallocatePin(irPin, PinOwner::IR);
if (pinManager.allocatePin(hw_ir_pin, false, PinOwner::IR)) { if (pinManager.allocatePin(hw_ir_pin, false, PinOwner::IR)) {
irPin = hw_ir_pin; irPin = hw_ir_pin;
} else { } else {
@ -276,6 +280,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
JsonObject relay = hw[F("relay")]; JsonObject relay = hw[F("relay")];
int hw_relay_pin = relay["pin"] | -2; int hw_relay_pin = relay["pin"] | -2;
if (hw_relay_pin > -2) { if (hw_relay_pin > -2) {
pinManager.deallocatePin(rlyPin, PinOwner::Relay);
if (pinManager.allocatePin(hw_relay_pin,true, PinOwner::Relay)) { if (pinManager.allocatePin(hw_relay_pin,true, PinOwner::Relay)) {
rlyPin = hw_relay_pin; rlyPin = hw_relay_pin;
pinMode(rlyPin, OUTPUT); pinMode(rlyPin, OUTPUT);
@ -443,6 +448,13 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(retainMqttMsg, if_mqtt[F("rtn")]); CJSON(retainMqttMsg, if_mqtt[F("rtn")]);
#endif #endif
#ifndef WLED_DISABLE_ESPNOW
JsonObject remote = doc["remote"];
CJSON(enable_espnow_remote, remote[F("remote_enabled")]);
getStringFromJson(linked_remote, remote[F("linked_remote")], 13);
#endif
#ifndef WLED_DISABLE_HUESYNC #ifndef WLED_DISABLE_HUESYNC
JsonObject if_hue = interfaces["hue"]; JsonObject if_hue = interfaces["hue"];
CJSON(huePollingEnabled, if_hue["en"]); CJSON(huePollingEnabled, if_hue["en"]);
@ -895,6 +907,13 @@ void serializeConfig() {
if_mqtt_topics[F("group")] = mqttGroupTopic; if_mqtt_topics[F("group")] = mqttGroupTopic;
#endif #endif
#ifndef WLED_DISABLE_ESPNOW
JsonObject remote = doc.createNestedObject(F("remote"));
remote[F("remote_enabled")] = enable_espnow_remote;
remote[F("linked_remote")] = linked_remote;
#endif
#ifndef WLED_DISABLE_HUESYNC #ifndef WLED_DISABLE_HUESYNC
JsonObject if_hue = interfaces.createNestedObject("hue"); JsonObject if_hue = interfaces.createNestedObject("hue");
if_hue["en"] = huePollingEnabled; if_hue["en"] = huePollingEnabled;

View File

@ -91,6 +91,21 @@
#endif #endif
#endif #endif
#ifndef WLED_MAX_SEGNAME_LEN
#ifdef ESP8266
#define WLED_MAX_SEGNAME_LEN 32
#else
#define WLED_MAX_SEGNAME_LEN 64
#endif
#else
#if WLED_MAX_SEGNAME_LEN<32
#undef WLED_MAX_SEGNAME_LEN
#define WLED_MAX_SEGNAME_LEN 32
#else
#warning WLED UI does not support modified maximum segment name length!
#endif
#endif
//Usermod IDs //Usermod IDs
#define USERMOD_ID_RESERVED 0 //Unused. Might indicate no usermod present #define USERMOD_ID_RESERVED 0 //Unused. Might indicate no usermod present
#define USERMOD_ID_UNSPECIFIED 1 //Default value for a general user mod that does not specify a custom ID #define USERMOD_ID_UNSPECIFIED 1 //Default value for a general user mod that does not specify a custom ID
@ -333,12 +348,29 @@
#define ERR_OVERCURRENT 31 // An attached current sensor has measured a current above the threshold (not implemented) #define ERR_OVERCURRENT 31 // An attached current sensor has measured a current above the threshold (not implemented)
#define ERR_UNDERVOLT 32 // An attached voltmeter has measured a voltage below the threshold (not implemented) #define ERR_UNDERVOLT 32 // An attached voltmeter has measured a voltage below the threshold (not implemented)
//Timer mode types // Timer mode types
#define NL_MODE_SET 0 //After nightlight time elapsed, set to target brightness #define NL_MODE_SET 0 //After nightlight time elapsed, set to target brightness
#define NL_MODE_FADE 1 //Fade to target brightness gradually #define NL_MODE_FADE 1 //Fade to target brightness gradually
#define NL_MODE_COLORFADE 2 //Fade to target brightness and secondary color gradually #define NL_MODE_COLORFADE 2 //Fade to target brightness and secondary color gradually
#define NL_MODE_SUN 3 //Sunrise/sunset. Target brightness is set immediately, then Sunrise effect is started. Max 60 min. #define NL_MODE_SUN 3 //Sunrise/sunset. Target brightness is set immediately, then Sunrise effect is started. Max 60 min.
// Settings sub page IDs
#define SUBPAGE_MENU 0
#define SUBPAGE_WIFI 1
#define SUBPAGE_LEDS 2
#define SUBPAGE_UI 3
#define SUBPAGE_SYNC 4
#define SUBPAGE_TIME 5
#define SUBPAGE_SEC 6
#define SUBPAGE_DMX 7
#define SUBPAGE_UM 8
#define SUBPAGE_UPDATE 9
#define SUBPAGE_2D 10
#define SUBPAGE_LOCK 251
#define SUBPAGE_PINREQ 252
#define SUBPAGE_CSS 253
#define SUBPAGE_JS 254
#define SUBPAGE_WELCOME 255
#define NTP_PACKET_SIZE 48 #define NTP_PACKET_SIZE 48
@ -371,7 +403,7 @@
#ifdef ESP8266 #ifdef ESP8266
#define SETTINGS_STACK_BUF_SIZE 2048 #define SETTINGS_STACK_BUF_SIZE 2048
#else #else
#define SETTINGS_STACK_BUF_SIZE 3096 #define SETTINGS_STACK_BUF_SIZE 3608 // warning: quite a large value for stack
#endif #endif
#ifdef WLED_USE_ETHERNET #ifdef WLED_USE_ETHERNET
@ -443,7 +475,10 @@
#define DEFAULT_LED_COUNT 30 #define DEFAULT_LED_COUNT 30
#endif #endif
#define INTERFACE_UPDATE_COOLDOWN 2000 //time in ms to wait between websockets, alexa, and MQTT updates #define INTERFACE_UPDATE_COOLDOWN 1000 // time in ms to wait between websockets, alexa, and MQTT updates
#define PIN_RETRY_COOLDOWN 3000 // time in ms after an incorrect attempt PIN and OTA pass will be rejected even if correct
#define PIN_TIMEOUT 900000 // time in ms after which the PIN will be required again, 15 minutes
// HW_PIN_SCL & HW_PIN_SDA are used for information in usermods settings page and usermods themselves // HW_PIN_SCL & HW_PIN_SDA are used for information in usermods settings page and usermods themselves
// which GPIO pins are actually used in a hardwarea layout (controller board) // which GPIO pins are actually used in a hardwarea layout (controller board)

View File

@ -42,6 +42,6 @@
<img alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAbUExURQAAAAB81gCU/zKq/////9bW1oCAgGhoaAAAAGPLX8AAAAAJdFJOU///////////AFNPeBIAAAAJcEhZcwAADsAAAA7AAWrWiQkAAACdSURBVDhPxc9bDoUgEANQebP/FUuHMjBGY/B+3EYR7RH0qC/ZBc6HwCljgHO+xZIVSI2sYgHaG7EBWh8jWoxTrCBFdDJ+BD4lbIHxAcz8APAVLTsrZE4eQD5qzt3cAFTYokC4YCN9Gybgu4yAQtBFLQXHuHABA7JMeOEC/E0W5uy9gv4vo5QHK2i7yq2C8UABM4HmL+CSTXCTF1DrCX6+Gp9zB5dsAAAAAElFTkSuQmCC"> <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>
</body> </body>
</html> </html>

View File

@ -134,7 +134,7 @@ button {
.off { .off {
color: var(--c-6) !important; color: var(--c-6) !important;
cursor: default !important; /* cursor: default !important; */
} }
.top .icons, .bot .icons { .top .icons, .bot .icons {
@ -1024,7 +1024,7 @@ textarea {
width: 50px !important; width: 50px !important;
} }
.segname, .pname { .segname, .pname, .bname {
white-space: nowrap; white-space: nowrap;
text-align: center; text-align: center;
overflow: hidden; overflow: hidden;
@ -1034,6 +1034,9 @@ textarea {
max-width: 170px; max-width: 170px;
position: relative; position: relative;
} }
.bname {
padding: 0 24px;
}
.segname .flr, .pname .flr { .segname .flr, .pname .flr {
transform: rotate(0deg); transform: rotate(0deg);

View File

@ -380,8 +380,8 @@
</div> </div>
</div> </div>
<div id="mliveview2D" class="modal"> <div id="mlv2D" class="modal">
<div id="kliveview2D" style="width:100%; height:100%">Loading...</div><br> <div id="klv2D" style="width:100%; height:100%">Loading...</div>
</div> </div>
<div id="rover" class="modal"> <div id="rover" class="modal">

View File

@ -22,11 +22,12 @@ var pN = "", pI = 0, pNum = 0;
var pmt = 1, pmtLS = 0, pmtLast = 0; var pmt = 1, pmtLS = 0, pmtLast = 0;
var lastinfo = {}; var lastinfo = {};
var isM = false, mw = 0, mh=0; var isM = false, mw = 0, mh=0;
var ws, cpick, ranges; var ws, cpick, ranges, wsRpt=0;
var cfg = { var cfg = {
theme:{base:"dark", bg:{url:""}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}}, theme:{base:"dark", bg:{url:""}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}},
comp :{colors:{picker: true, rgb: false, quick: true, hex: false}, comp :{colors:{picker: true, rgb: false, quick: true, hex: false},
labels:true, pcmbot:false, pid:true, seglen:false, segpwr:false, segexp:false, css:true, hdays:false, pxm: false} labels:true, pcmbot:false, pid:true, seglen:false, segpwr:false, segexp:false,
css:true, hdays:false, fxdef:true, pxm: false}
}; };
var hol = [ var hol = [
[0,11,24,4,"https://aircoookie.github.io/xmas.png"], // christmas [0,11,24,4,"https://aircoookie.github.io/xmas.png"], // christmas
@ -219,6 +220,7 @@ function onLoad()
// detect reverse proxy and/or HTTPS // detect reverse proxy and/or HTTPS
let pathn = l.pathname; let pathn = l.pathname;
let paths = pathn.slice(1,pathn.endsWith('/')?-1:undefined).split("/"); let paths = pathn.slice(1,pathn.endsWith('/')?-1:undefined).split("/");
//if (paths[0]==="sliders") paths.shift();
//while (paths[0]==="") paths.shift(); //while (paths[0]==="") paths.shift();
locproto = l.protocol; locproto = l.protocol;
locip = l.hostname + (l.port ? ":" + l.port : ""); locip = l.hostname + (l.port ? ":" + l.port : "");
@ -776,7 +778,7 @@ function populateSegments(s)
`<i class="icons e-icon flr" id="sege${i}" onclick="expand(${i})">&#xe395;</i>`+ `<i class="icons e-icon flr" id="sege${i}" onclick="expand(${i})">&#xe395;</i>`+
(cfg.comp.segpwr ? segp : '') + (cfg.comp.segpwr ? segp : '') +
`<div class="segin" id="seg${i}in">`+ `<div class="segin" id="seg${i}in">`+
`<input type="text" class="ptxt" id="seg${i}t" autocomplete="off" maxlength=32 value="${inst.n?inst.n:""}" placeholder="Enter name..."/>`+ `<input type="text" class="ptxt" id="seg${i}t" autocomplete="off" maxlength=${li.arch=="esp8266"?32:64} value="${inst.n?inst.n:""}" placeholder="Enter name..."/>`+
`<table class="infot segt">`+ `<table class="infot segt">`+
`<tr>`+ `<tr>`+
`<td>${isMSeg?'Start X':'Start LED'}</td>`+ `<td>${isMSeg?'Start X':'Start LED'}</td>`+
@ -829,6 +831,7 @@ function populateSegments(s)
resetUtil(noNewSegs); resetUtil(noNewSegs);
if (gId('selall')) gId('selall').checked = true; if (gId('selall')) gId('selall').checked = true;
for (var i = 0; i <= lSeg; i++) { for (var i = 0; i <= lSeg; i++) {
if (!gId(`seg${i}`)) continue;
updateLen(i); updateLen(i);
updateTrail(gId(`seg${i}bri`)); updateTrail(gId(`seg${i}bri`));
gId(`segr${i}`).classList.add("hide"); gId(`segr${i}`).classList.add("hide");
@ -1007,10 +1010,15 @@ function generateListItemHtml(listName, id, name, clickAction, extraHtml = '', e
function btype(b) function btype(b)
{ {
switch (b) { switch (b) {
case 2:
case 32: return "ESP32"; case 32: return "ESP32";
case 3:
case 33: return "ESP32-S2"; case 33: return "ESP32-S2";
case 4:
case 34: return "ESP32-S3"; case 34: return "ESP32-S3";
case 5:
case 35: return "ESP32-C3"; case 35: return "ESP32-C3";
case 1:
case 82: return "ESP8266"; case 82: return "ESP8266";
} }
return "?"; return "?";
@ -1031,8 +1039,9 @@ function populateNodes(i,n)
n.nodes.sort((a,b) => (a.name).localeCompare(b.name)); n.nodes.sort((a,b) => (a.name).localeCompare(b.name));
for (var o of n.nodes) { for (var o of n.nodes) {
if (o.name) { if (o.name) {
var url = `<button class="btn" title="${o.ip}" onclick="location.assign('http://${o.ip}');">${bname(o)}</button>`; let onoff = `<i class="icons e-icon flr ${o.type&0x80?'':'off'}" onclick="rmtTgl('${o.ip}',this);"">&#xe08f;</i>`;
urows += inforow(url,`${btype(o.type)}<br><i>${o.vid==0?"N/A":o.vid}</i>`); var url = `<button class="btn" title="${o.ip}" onclick="location.assign('http://${o.ip}');"><div class="bname">${bname(o)}</div>${o.vid<2307130?'':onoff}</button>`;
urows += inforow(url,`${btype(o.type&0x7F)}<br><i>${o.vid==0?"N/A":o.vid}</i>`);
nnodes++; nnodes++;
} }
} }
@ -1334,11 +1343,12 @@ function makeWS() {
}; };
ws.onclose = (e)=>{ ws.onclose = (e)=>{
gId('connind').style.backgroundColor = "var(--c-r)"; gId('connind').style.backgroundColor = "var(--c-r)";
setTimeout(makeWS,1500); // retry WS connection if (wsRpt++ < 5) setTimeout(makeWS,1500); // retry WS connection
ws = null; ws = null;
} }
ws.onopen = (e)=>{ ws.onopen = (e)=>{
//ws.send("{'v':true}"); // unnecessary (https://github.com/Aircoookie/WLED/blob/master/wled00/ws.cpp#L18) //ws.send("{'v':true}"); // unnecessary (https://github.com/Aircoookie/WLED/blob/master/wled00/ws.cpp#L18)
wsRpt = 0;
reqsLegal = true; reqsLegal = true;
} }
} }
@ -1640,6 +1650,7 @@ function requestJson(command=null)
//load presets and open websocket sequentially //load presets and open websocket sequentially
if (!pJson || isEmpty(pJson)) setTimeout(()=>{ if (!pJson || isEmpty(pJson)) setTimeout(()=>{
loadPresets(()=>{ loadPresets(()=>{
wsRpt = 0;
if (!(ws && ws.readyState === WebSocket.OPEN)) makeWS(); if (!(ws && ws.readyState === WebSocket.OPEN)) makeWS();
}); });
},25); },25);
@ -1687,28 +1698,23 @@ function toggleSync()
function toggleLiveview() function toggleLiveview()
{ {
//WLEDSR adding liveview2D support
if (isInfo && isM) toggleInfo(); if (isInfo && isM) toggleInfo();
if (isNodes && isM) toggleNodes(); if (isNodes && isM) toggleNodes();
if (isPXM && isM) togglePixelMagicTool(); if (isPXM && isM) togglePixelMagicTool();
isLv = !isLv; isLv = !isLv;
let wsOn = ws && ws.readyState === WebSocket.OPEN;
var lvID = "liveview"; var lvID = "liveview";
if (isM) { if (isM && wsOn) {
lvID = "liveview2D" lvID += "2D";
if (isLv) { if (isLv) gId('klv2D').innerHTML = `<iframe id="${lvID}" src="about:blank"></iframe>`;
var cn = '<iframe id="liveview2D" src="about:blank"></iframe>'; gId('mlv2D').style.transform = (isLv) ? "translateY(0px)":"translateY(100%)";
d.getElementById('kliveview2D').innerHTML = cn;
}
gId('mliveview2D').style.transform = (isLv) ? "translateY(0px)":"translateY(100%)";
} }
gId(lvID).style.display = (isLv) ? "block":"none"; gId(lvID).style.display = (isLv) ? "block":"none";
var url = getURL("/" + lvID); gId(lvID).src = (isLv) ? getURL("/" + lvID + ((wsOn) ? "?ws":"")):"about:blank";
gId(lvID).src = (isLv) ? url:"about:blank"; gId('buttonSr').classList.toggle("active");
gId('buttonSr').className = (isLv) ? "active":""; if (!isLv && wsOn) ws.send('{"lv":false}');
if (!isLv && ws && ws.readyState === WebSocket.OPEN) ws.send('{"lv":false}');
size(); size();
} }
@ -2088,14 +2094,14 @@ function tglSegn(s)
function selSegAll(o) function selSegAll(o)
{ {
var obj = {"seg":[]}; var obj = {"seg":[]};
for (let i=0; i<=lSeg; i++) obj.seg.push({"id":i,"sel":o.checked}); for (let i=0; i<=lSeg; i++) if (gId(`seg${i}`)) obj.seg.push({"id":i,"sel":o.checked});
requestJson(obj); requestJson(obj);
} }
function selSegEx(s) function selSegEx(s)
{ {
var obj = {"seg":[]}; var obj = {"seg":[]};
for (let i=0; i<=lSeg; i++) obj.seg.push({"id":i,"sel":(i==s)}); for (let i=0; i<=lSeg; i++) if (gId(`seg${i}`)) obj.seg.push({"id":i,"sel":(i==s)});
obj.mainseg = s; obj.mainseg = s;
requestJson(obj); requestJson(obj);
} }
@ -2113,7 +2119,7 @@ function selGrp(g)
event.stopPropagation(); event.stopPropagation();
var sel = gId(`segcont`).querySelectorAll(`div[data-set="${g}"]`); var sel = gId(`segcont`).querySelectorAll(`div[data-set="${g}"]`);
var obj = {"seg":[]}; var obj = {"seg":[]};
for (let i=0; i<=lSeg; i++) obj.seg.push({"id":i,"sel":false}); for (let i=0; i<=lSeg; i++) if (gId(`seg${i}`)) obj.seg.push({"id":i,"sel":false});
if (sel) for (let s of sel||[]) { if (sel) for (let s of sel||[]) {
let i = parseInt(s.id.substring(3)); let i = parseInt(s.id.substring(3));
obj.seg[i] = {"id":i,"sel":true}; obj.seg[i] = {"id":i,"sel":true};
@ -2280,8 +2286,7 @@ function setFX(ind = null)
} else { } else {
d.querySelector(`#fxlist input[name="fx"][value="${ind}"]`).checked = true; d.querySelector(`#fxlist input[name="fx"][value="${ind}"]`).checked = true;
} }
var obj = {"seg": {"fx": parseInt(ind), "fxdef": cfg.comp.fxdef}}; // fxdef sets effect parameters to default values
var obj = {"seg": {"fx": parseInt(ind),"fxdef":1}}; // fxdef sets effect parameters to default values, TODO add client setting
requestJson(obj); requestJson(obj);
} }
@ -2615,6 +2620,24 @@ function setBalance(b)
requestJson(obj); requestJson(obj);
} }
function rmtTgl(ip,i) {
event.preventDefault();
event.stopPropagation();
fetch(`http://${ip}/win&T=2`, {method: 'get'})
.then((r)=>{
return r.text();
})
.then((t)=>{
let c = (new window.DOMParser()).parseFromString(t, "text/xml");
// perhaps just i.classList.toggle("off"); would be enough
if (c.getElementsByTagName('ac')[0].textContent === "0") {
i.classList.add("off");
} else {
i.classList.remove("off");
}
});
}
var hc = 0; var hc = 0;
setInterval(()=>{ setInterval(()=>{
if (!isInfo) return; if (!isInfo) return;

View File

@ -17,14 +17,10 @@
position: absolute; position: absolute;
} }
</style> </style>
</head>
<body>
<div id="canv" />
<script> <script>
update(); var ws;
var tmout = null; var tmout = null;
function update() function update() // via HTTP (/json/live)
{ {
if (document.hidden) { if (document.hidden) {
clearTimeout(tmout); clearTimeout(tmout);
@ -57,8 +53,57 @@
clearTimeout(tmout); clearTimeout(tmout);
tmout = setTimeout(update, 2500); tmout = setTimeout(update, 2500);
}) })
}
function S() { // Startup function (onload)
let wsOn = (window.location.href.indexOf("?ws") > 0);
if (!wsOn) {update(); return;}
// Initialize WebSocket connection
try {
ws = top.window.ws;
} catch (e) {}
if (ws && ws.readyState === WebSocket.OPEN) {
//console.info("Peek uses top WS");
ws.send("{'lv':true}");
} else {
//console.info("Peek WS opening");
let l = window.location;
let pathn = l.pathname;
let paths = pathn.slice(1,pathn.endsWith('/')?-1:undefined).split("/");
let url = l.origin.replace("http","ws");
if (paths.length > 1) {
url += "/" + paths[0];
}
ws = new WebSocket(url+"/ws");
ws.onopen = function () {
//console.info("Peek WS open");
ws.send("{'lv':true}");
}
}
ws.binaryType = "arraybuffer";
ws.addEventListener('message', (e) => {
try {
if (toString.call(e.data) === '[object ArrayBuffer]') {
let leds = new Uint8Array(event.data);
if (leds[0] != 76) return; //'L'
let str = "linear-gradient(90deg,";
let len = leds.length;
let start = leds[1]==2 ? 4 : 2; // 1 = 1D, 2 = 1D/2D (leds[2]=w, leds[3]=h)
for (i = start; i < len; i+=3) {
str += `rgb(${leds[i]},${leds[i+1]},${leds[i+2]})`;
if (i < len -3) str += ","
}
str += ")";
document.getElementById("canv").style.background = str;
}
} catch (err) {
console.error("Peek WS error:",err);
}
});
} }
</script> </script>
</head>
<body onload="S()">
<div id="canv"></div>
</body> </body>
</html> </html>

View File

@ -1,68 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<meta charset="utf-8">
<meta name="theme-color" content="#222222">
<title>WLED Live Preview</title>
<style>
body {
margin: 0;
}
#canv {
background: black;
filter: brightness(175%);
width: 100%;
height: 100%;
position: absolute;
}
</style>
</head>
<body>
<div id="canv"></div>
<script>
var ws;
try {
ws = top.window.ws;
} catch (e) {}
if (ws && ws.readyState === WebSocket.OPEN) {
//console.info("Peek uses top WS");
ws.send("{'lv':true}");
} else {
//console.info("Peek WS opening");
let l = window.location;
let pathn = l.pathname;
let paths = pathn.slice(1,pathn.endsWith('/')?-1:undefined).split("/");
let url = l.origin.replace("http","ws");
if (paths.length > 1) {
url += "/" + paths[0];
}
ws = new WebSocket(url+"/ws");
ws.onopen = function () {
//console.info("Peek WS open");
ws.send("{'lv':true}");
}
}
ws.binaryType = "arraybuffer";
ws.addEventListener('message', (e) => {
try {
if (toString.call(e.data) === '[object ArrayBuffer]') {
let leds = new Uint8Array(event.data);
if (leds[0] != 76) return; //'L'
let str = "linear-gradient(90deg,";
let len = leds.length;
let start = leds[1]==2 ? 4 : 2; // 1 = 1D, 2 = 1D/2D (leds[2]=w, leds[3]=h)
for (i = start; i < len; i+=3) {
str += `rgb(${leds[i]},${leds[i+1]},${leds[i+2]})`;
if (i < len -3) str += ","
}
str += ")";
document.getElementById("canv").style.background = str;
}
} catch (err) {
console.error("Peek WS error:",err);
}
});
</script>
</body>
</html>

View File

@ -27,7 +27,8 @@
"segexp" : "Always expand first segment", "segexp" : "Always expand first segment",
"css": "Enable custom CSS", "css": "Enable custom CSS",
"hdays": "Enable custom Holidays list", "hdays": "Enable custom Holidays list",
"hdays": "Enable Pixel Magic Tool", "fxdef": "Use effect default parameters",
"pxm": "Enable Pixel Magic Tool"
}, },
"theme":{ "theme":{
"alpha": { "alpha": {

View File

@ -11,6 +11,7 @@
d.um_p = []; d.um_p = [];
d.rsvd = []; d.rsvd = [];
d.ro_gpio = []; d.ro_gpio = [];
d.extra = [];
var umCfg = {}; var umCfg = {};
var pins = [], pinO = [], owner; var pins = [], pinO = [], owner;
var loc = false, locip, locproto = "http:"; var loc = false, locip, locproto = "http:";
@ -30,16 +31,11 @@
// success event // success event
scE.addEventListener("load", () => { scE.addEventListener("load", () => {
GetV(); GetV();
for (let k=0; k<d.rsvd.length; k++) { pins.push(d.rsvd[k]); pinO.push("rsvd"); } for (let r of d.rsvd) { pins.push(r); pinO.push("rsvd"); } // reserved pins
if (d.um_p[0]==-1) d.um_p.shift(); if (d.um_p[0]==-1) d.um_p.shift(); // remove filler
d.Sf.SDA.max = d.max_gpio; d.Sf.SDA.max = d.Sf.SCL.max = d.Sf.MOSI.max = d.Sf.SCLK.max = d.Sf.MISO.max = d.max_gpio;
d.Sf.SCL.max = d.max_gpio; //for (let i of d.getElementsByTagName("input")) if (i.type === "number" && i.name.replace("[]","").substr(-3) === "pin") i.max = d.max_gpio;
d.Sf.MOSI.max = d.max_gpio; pinDropdowns(); // convert INPUT to SELECT for pins
d.Sf.SCLK.max = d.max_gpio;
d.Sf.MISO.max = d.max_gpio;
let inp = d.getElementsByTagName("input");
for (let i of inp) if (i.type === "number" && i.name.replace("[]","").substr(-3) === "pin") i.max = d.max_gpio;
pinDropdowns();
}); });
// error event // error event
scE.addEventListener("error", (ev) => { scE.addEventListener("error", (ev) => {
@ -77,6 +73,7 @@
function isF(n) { return n === +n && n !== (n|0); } function isF(n) { return n === +n && n !== (n|0); }
function isI(n) { return n === +n && n === (n|0); } function isI(n) { return n === +n && n === (n|0); }
function check(o,k) { // input object, pin owner key function check(o,k) { // input object, pin owner key
/* no longer necessary with pin dropdown fields
var n = o.name.replace("[]","").substr(-3); var n = o.name.replace("[]","").substr(-3);
if (o.type=="number" && n.substr(0,3)=="pin") { if (o.type=="number" && n.substr(0,3)=="pin") {
for (var i=0; i<pins.length; i++) { for (var i=0; i<pins.length; i++) {
@ -99,6 +96,7 @@
if (o.value==pins[i] || o.value<-1 || o.value>d.max_gpio) { o.style.color="red"; break; } else o.style.color=d.ro_gpio.some((e)=>e==parseInt(o.value,10))?"orange":"#fff"; if (o.value==pins[i] || o.value<-1 || o.value>d.max_gpio) { o.style.color="red"; break; } else o.style.color=d.ro_gpio.some((e)=>e==parseInt(o.value,10))?"orange":"#fff";
} }
} }
*/
} }
function getPins(o) { function getPins(o) {
if (isO(o)) { if (isO(o)) {
@ -181,6 +179,13 @@
if (j === v) opt.selected = true; // this is "our" pin if (j === v) opt.selected = true; // this is "our" pin
else if (pins.includes(j)) opt.disabled = true; // someone else's pin else if (pins.includes(j)) opt.disabled = true; // someone else's pin
} }
let um = i.name.split(":")[0];
d.extra.forEach((o)=>{
if (o[um] && o[um].pin) o[um].pin.forEach((e)=>{
let opt = addOption(sel,e[0],e[1]);
if (e[1]==v) opt.selected = true;
});
});
} }
} }
} }
@ -198,15 +203,17 @@
i.text = "unused"; i.text = "unused";
return return
} }
i.text = i.value; if (i.value<100) { // TODO remove this hack and use d.extra
if (i.value==oldV) { i.text = i.value;
i.disabled = false; if (i.value==oldV) {
i.disabled = false;
}
if (i.value==e.value) {
i.disabled = true;
i.text += ` ${txt}`;
}
if (d.ro_gpio.includes(parseInt(i.value))) i.text += " (R/O)";
} }
if (i.value==e.value) {
i.disabled = true;
i.text += ` ${txt}`;
}
if (d.ro_gpio.includes(parseInt(i.value))) i.text += " (R/O)";
}); });
} }
} }
@ -235,7 +242,7 @@
var att = inp.attributes[i]; var att = inp.attributes[i];
// type and value don't apply, so skip them // type and value don't apply, so skip them
// ** you might also want to skip style, or others -- modify as needed ** // ** you might also want to skip style, or others -- modify as needed **
if (att.name != 'type' && att.name != 'value' && att.name != 'class' && att.name != 'style') { if (att.name != 'type' && att.name != 'value' && att.name != 'class' && att.name != 'style' && att.name != 'oninput' && att.name != 'max' && att.name != 'min') {
sel.setAttribute(att.name, att.value); sel.setAttribute(att.name, att.value);
} }
} }
@ -243,7 +250,6 @@
sel.setAttribute("onchange", "UI(this)"); sel.setAttribute("onchange", "UI(this)");
// finally, replace the old input element with the new select element // finally, replace the old input element with the new select element
inp.parentElement.replaceChild(sel, inp); inp.parentElement.replaceChild(sel, inp);
if (arr[0].type==="hidden") arr[0].parentElement.removeChild(arr[0]); // remove hidden element from DOM
return sel; return sel;
} }
return null; return null;

View File

@ -193,6 +193,17 @@
Disable WiFi sleep: <input type="checkbox" name="WS"><br> Disable WiFi sleep: <input type="checkbox" name="WS"><br>
<i>Can help with connectivity issues.<br> <i>Can help with connectivity issues.<br>
Do not enable if WiFi is working correctly, increases power consumption.</i> Do not enable if WiFi is working correctly, increases power consumption.</i>
<div id="remd">
<h3>Wireless Remote</h3>
<i>Listen for events over ESP-NOW<br>
Keep disabled if not using a remote, increases power consumption.<br></i>
Enable Remote: <input type="checkbox" name="RE"><br>
Hardware MAC: <input type="text" name="RMAC"><br>
Last Seen: <span class="rlid">None</span> <br>
</div>
<div id="ethd"> <div id="ethd">
<h3>Ethernet Type</h3> <h3>Ethernet Type</h3>
<select name="ETH"> <select name="ETH">

View File

@ -171,6 +171,7 @@ async function onLoad()
// detect reverse proxy and/or HTTPS // detect reverse proxy and/or HTTPS
let pathn = l.pathname; let pathn = l.pathname;
let paths = pathn.slice(1,pathn.endsWith('/')?-1:undefined).split("/"); let paths = pathn.slice(1,pathn.endsWith('/')?-1:undefined).split("/");
if (paths[0]==="sliders") paths.shift();
//while (paths[0]==="") paths.shift(); //while (paths[0]==="") paths.shift();
locproto = l.protocol; locproto = l.protocol;
locip = l.hostname + (l.port ? ":" + l.port : ""); locip = l.hostname + (l.port ? ":" + l.port : "");

View File

@ -57,7 +57,7 @@
Connect the module to your local WiFi here!<br> Connect the module to your local WiFi here!<br>
<button onclick="window.location.href='./settings/wifi'">WiFi settings</button><br> <button onclick="window.location.href='./settings/wifi'">WiFi settings</button><br>
<i>Just trying this out in AP mode?</i><br> <i>Just trying this out in AP mode?</i><br>
<button onclick="window.location.href='./sliders'">To the controls!</button><br> <button onclick="window.location.href='./?sliders'">To the controls!</button><br>
</div> </div>
</body> </body>
</html> </html>

View File

@ -104,10 +104,20 @@ void sendHuePoll();
void onHueData(void* arg, AsyncClient* client, void *data, size_t len); void onHueData(void* arg, AsyncClient* client, void *data, size_t len);
//improv.cpp //improv.cpp
enum ImprovRPCType {
Command_Wifi = 0x01,
Request_State = 0x02,
Request_Info = 0x03,
Request_Scan = 0x04
};
void handleImprovPacket(); void handleImprovPacket();
void sendImprovRPCResult(ImprovRPCType type, uint8_t n_strings = 0, const char **strings = nullptr);
void sendImprovStateResponse(uint8_t state, bool error = false); void sendImprovStateResponse(uint8_t state, bool error = false);
void sendImprovInfoResponse(); void sendImprovInfoResponse();
void sendImprovRPCResponse(byte commandId); void startImprovWifiScan();
void handleImprovWifiScan();
void sendImprovIPRPCResult(ImprovRPCType type);
//ir.cpp //ir.cpp
void applyRepeatActions(); void applyRepeatActions();
@ -200,12 +210,16 @@ void serializePlaylist(JsonObject obj);
void initPresetsFile(); void initPresetsFile();
void handlePresets(); void handlePresets();
bool applyPreset(byte index, byte callMode = CALL_MODE_DIRECT_CHANGE); bool applyPreset(byte index, byte callMode = CALL_MODE_DIRECT_CHANGE);
void applyPresetWithFallback(uint8_t presetID, uint8_t callMode, uint8_t effectID = 0, uint8_t paletteID = 0);
inline bool applyTemporaryPreset() {return applyPreset(255);}; inline bool applyTemporaryPreset() {return applyPreset(255);};
void savePreset(byte index, const char* pname = nullptr, JsonObject saveobj = JsonObject()); void savePreset(byte index, const char* pname = nullptr, JsonObject saveobj = JsonObject());
inline void saveTemporaryPreset() {savePreset(255);}; inline void saveTemporaryPreset() {savePreset(255);};
void deletePreset(byte index); void deletePreset(byte index);
bool getPresetName(byte index, String& name); bool getPresetName(byte index, String& name);
//remote.cpp
void handleRemote();
//set.cpp //set.cpp
bool isAsterisksOnly(const char* str, byte maxLen); bool isAsterisksOnly(const char* str, byte maxLen);
void handleSettingsSet(AsyncWebServerRequest *request, byte subPage); void handleSettingsSet(AsyncWebServerRequest *request, byte subPage);
@ -335,6 +349,7 @@ void releaseJSONBufferLock();
uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen); uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen);
uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxLen, uint8_t *var = nullptr); uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxLen, uint8_t *var = nullptr);
int16_t extractModeDefaults(uint8_t mode, const char *segVar); int16_t extractModeDefaults(uint8_t mode, const char *segVar);
void checkSettingsPIN(const char *pin);
uint16_t crc16(const unsigned char* data_p, size_t length); uint16_t crc16(const unsigned char* data_p, size_t length);
um_data_t* simulateSound(uint8_t simulationId); um_data_t* simulateSound(uint8_t simulationId);
void enumerateLedmaps(); void enumerateLedmaps();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,11 @@
#define DIMPROV_PRINTF(x...) #define DIMPROV_PRINTF(x...)
#endif #endif
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
#undef WLED_DISABLE_IMPROV_WIFISCAN
#define WLED_DISABLE_IMPROV_WIFISCAN
#endif
#define IMPROV_VERSION 1 #define IMPROV_VERSION 1
void parseWiFiCommand(char *rpcData); void parseWiFiCommand(char *rpcData);
@ -28,20 +33,14 @@ enum ImprovPacketByte {
RPC_CommandType = 9 RPC_CommandType = 9
}; };
enum ImprovRPCType { #ifndef WLED_DISABLE_IMPROV_WIFISCAN
Command_Wifi = 0x01, static bool improvWifiScanRunning = false;
Request_State = 0x02, #endif
Request_Info = 0x03
};
//File dbgf;
//blocking function to parse an Improv Serial packet //blocking function to parse an Improv Serial packet
void handleImprovPacket() { void handleImprovPacket() {
uint8_t header[6] = {'I','M','P','R','O','V'}; uint8_t header[6] = {'I','M','P','R','O','V'};
//dbgf = WLED_FS.open("/improv.log","a");
bool timeout = false; bool timeout = false;
uint8_t waitTime = 25; uint8_t waitTime = 25;
uint16_t packetByte = 0; uint16_t packetByte = 0;
@ -62,12 +61,11 @@ void handleImprovPacket() {
byte next = Serial.read(); byte next = Serial.read();
DIMPROV_PRINT("Received improv byte: "); DIMPROV_PRINTF("%x\r\n",next); DIMPROV_PRINT("Received improv byte: "); DIMPROV_PRINTF("%x\r\n",next);
//f.write(next);
switch (packetByte) { switch (packetByte) {
case ImprovPacketByte::Version: { case ImprovPacketByte::Version: {
if (next != IMPROV_VERSION) { if (next != IMPROV_VERSION) {
DIMPROV_PRINTLN(F("Invalid version")); DIMPROV_PRINTLN(F("Invalid version"));
//dbgf.close();
return; return;
} }
break; break;
@ -75,7 +73,6 @@ void handleImprovPacket() {
case ImprovPacketByte::PacketType: { case ImprovPacketByte::PacketType: {
if (next != ImprovPacketType::RPC_Command) { if (next != ImprovPacketType::RPC_Command) {
DIMPROV_PRINTF("Non RPC-command improv packet type %i\n",next); DIMPROV_PRINTF("Non RPC-command improv packet type %i\n",next);
//dbgf.close();
return; return;
} }
if (!improvActive) improvActive = 1; if (!improvActive) improvActive = 1;
@ -89,7 +86,6 @@ void handleImprovPacket() {
if (checksum != next) { if (checksum != next) {
DIMPROV_PRINTF("Got RPC checksum %i, expected %i",next,checksum); DIMPROV_PRINTF("Got RPC checksum %i, expected %i",next,checksum);
sendImprovStateResponse(0x01, true); sendImprovStateResponse(0x01, true);
//dbgf.close();
return; return;
} }
@ -100,22 +96,23 @@ void handleImprovPacket() {
if (WLED_WIFI_CONFIGURED) improvState = 0x03; //provisioning if (WLED_WIFI_CONFIGURED) improvState = 0x03; //provisioning
if (Network.isConnected()) improvState = 0x04; //provisioned if (Network.isConnected()) improvState = 0x04; //provisioned
sendImprovStateResponse(improvState, false); sendImprovStateResponse(improvState, false);
if (improvState == 0x04) sendImprovRPCResponse(ImprovRPCType::Request_State); if (improvState == 0x04) sendImprovIPRPCResult(ImprovRPCType::Request_State);
break; break;
} }
case ImprovRPCType::Request_Info: sendImprovInfoResponse(); break; case ImprovRPCType::Request_Info: sendImprovInfoResponse(); break;
#ifndef WLED_DISABLE_IMPROV_WIFISCAN
case ImprovRPCType::Request_Scan: startImprovWifiScan(); break;
#endif
default: { default: {
DIMPROV_PRINTF("Unknown RPC command %i\n",next); DIMPROV_PRINTF("Unknown RPC command %i\n",next);
sendImprovStateResponse(0x02, true); sendImprovStateResponse(0x02, true);
} }
} }
//dbgf.close();
return; return;
} }
if (packetByte < 6) { //check header if (packetByte < 6) { //check header
if (next != header[packetByte]) { if (next != header[packetByte]) {
DIMPROV_PRINTLN(F("Invalid improv header")); DIMPROV_PRINTLN(F("Invalid improv header"));
//dbgf.close();
return; return;
} }
} else if (packetByte > 9) { //RPC data } else if (packetByte > 9) { //RPC data
@ -128,7 +125,6 @@ void handleImprovPacket() {
checksum += next; checksum += next;
packetByte++; packetByte++;
} }
//dbgf.close();
} }
void sendImprovStateResponse(uint8_t state, bool error) { void sendImprovStateResponse(uint8_t state, bool error) {
@ -147,79 +143,116 @@ void sendImprovStateResponse(uint8_t state, bool error) {
Serial.write('\n'); Serial.write('\n');
} }
void sendImprovRPCResponse(byte commandId) { // used by sendImprovIPRPCResult(), sendImprovInfoResponse(), and handleImprovWifiScan()
void sendImprovRPCResult(ImprovRPCType type, uint8_t n_strings, const char **strings) {
if (improvError > 0 && improvError < 3) sendImprovStateResponse(0x00, true); if (improvError > 0 && improvError < 3) sendImprovStateResponse(0x00, true);
uint8_t packetLen = 12; uint8_t packetLen = 12;
char out[64] = {'I','M','P','R','O','V'}; char out[256] = {'I','M','P','R','O','V'};
out[6] = IMPROV_VERSION; out[6] = IMPROV_VERSION;
out[7] = ImprovPacketType::RPC_Response; out[7] = ImprovPacketType::RPC_Response;
out[8] = 2; //Length (set below) //out[8] = 2; //Length (set below)
out[9] = commandId; out[9] = type;
out[10] = 0; //Data len (set below) //out[10] = 0; //Data len (set below)
out[11] = '\0'; //URL len (set below) uint16_t pos = 11;
if (Network.isConnected()) for (uint8_t s = 0; s < n_strings; s++) {
{ size_t len = strlen(strings[s]);
IPAddress localIP = Network.localIP(); if (pos + len > 254) continue; // simple buffer overflow guard
uint8_t len = sprintf(out+12, "http://%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]); out[pos++] = len;
if (len > 24) return; //sprintf fail? strcpy(out + pos, strings[s]);
out[11] = len; pos += len;
out[10] = 1 + len;
out[8] = 3 + len; //RPC command type + data len + url len + url
packetLen = 13 + len;
} }
packetLen = pos +1;
out[8] = pos -9; // Length of packet (excluding first 9 header bytes and final checksum byte)
out[10] = pos -11; // Data len
uint8_t checksum = 0; uint8_t checksum = 0;
for (uint8_t i = 0; i < packetLen -1; i++) checksum += out[i]; for (uint8_t i = 0; i < packetLen -1; i++) checksum += out[i];
out[packetLen -1] = checksum; out[packetLen -1] = checksum;
Serial.write((uint8_t*)out, packetLen); Serial.write((uint8_t*)out, packetLen);
Serial.write('\n'); Serial.write('\n');
DIMPROV_PRINT("RPC result checksum");
DIMPROV_PRINTLN(checksum);
}
void sendImprovIPRPCResult(ImprovRPCType type) {
if (Network.isConnected())
{
char urlStr[64];
IPAddress localIP = Network.localIP();
uint8_t len = sprintf(urlStr, "http://%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]);
if (len > 24) return; //sprintf fail?
const char *str[1] = {urlStr};
sendImprovRPCResult(type, 1, str);
} else {
sendImprovRPCResult(type, 0);
}
improvActive = 1; //no longer provisioning improvActive = 1; //no longer provisioning
} }
void sendImprovInfoResponse() { void sendImprovInfoResponse() {
if (improvError > 0 && improvError < 3) sendImprovStateResponse(0x00, true); const char* bString =
uint8_t packetLen = 12; #ifdef ESP8266
char out[128] = {'I','M','P','R','O','V'}; "esp8266"
out[6] = IMPROV_VERSION; #elif CONFIG_IDF_TARGET_ESP32C3
out[7] = ImprovPacketType::RPC_Response; "esp32-c3"
//out[8] = 2; //Length (set below) #elif CONFIG_IDF_TARGET_ESP32S2
out[9] = ImprovRPCType::Request_Info; "esp32-s2"
//out[10] = 0; //Data len (set below) #elif CONFIG_IDF_TARGET_ESP32S3
out[11] = 4; //Firmware len ("WLED") "esp32-s3";
out[12] = 'W'; out[13] = 'L'; out[14] = 'E'; out[15] = 'D'; #else // ESP32
uint8_t lengthSum = 17; "esp32";
uint8_t vlen = sprintf_P(out+lengthSum,PSTR("0.14.0-b3/%i"),VERSION); #endif
out[16] = vlen; lengthSum += vlen; ;
uint8_t hlen = 7;
#ifdef ESP8266
strcpy(out+lengthSum+1,"esp8266");
#else
hlen = 5;
strcpy(out+lengthSum+1,"esp32");
#endif
out[lengthSum] = hlen;
lengthSum += hlen + 1;
//Use serverDescription if it has been changed from the default "WLED", else mDNS name //Use serverDescription if it has been changed from the default "WLED", else mDNS name
bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0); bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0);
strcpy(out+lengthSum+1,useMdnsName ? cmDNS : serverDescription); char vString[20];
uint8_t nlen = strlen(useMdnsName ? cmDNS : serverDescription); sprintf_P(vString, PSTR("0.14.0-b3/%i"), VERSION);
out[lengthSum] = nlen; const char *str[4] = {"WLED", vString, bString, useMdnsName ? cmDNS : serverDescription};
lengthSum += nlen + 1;
packetLen = lengthSum +1; sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str);
out[8] = lengthSum -9;
out[10] = lengthSum -11;
uint8_t checksum = 0;
for (uint8_t i = 0; i < packetLen -1; i++) checksum += out[i];
out[packetLen -1] = checksum;
Serial.write((uint8_t*)out, packetLen);
Serial.write('\n');
DIMPROV_PRINT("Info checksum");
DIMPROV_PRINTLN(checksum);
} }
#ifndef WLED_DISABLE_IMPROV_WIFISCAN
void startImprovWifiScan() {
if (improvWifiScanRunning) return;
WiFi.scanNetworks(true);
improvWifiScanRunning = true;
}
void handleImprovWifiScan() {
if (!improvWifiScanRunning) return;
int16_t status = WiFi.scanComplete();
if (status == WIFI_SCAN_RUNNING) return;
// here scan completed or failed (-2)
improvWifiScanRunning = false;
for (int i = 0; i < status; i++) {
char rssiStr[8];
sprintf(rssiStr, "%d", WiFi.RSSI(i));
#ifdef ESP8266
bool isOpen = WiFi.encryptionType(i) == ENC_TYPE_NONE;
#else
bool isOpen = WiFi.encryptionType(i) == WIFI_AUTH_OPEN;
#endif
char ssidStr[33];
strcpy(ssidStr, WiFi.SSID(i).c_str());
const char *str[3] = {ssidStr, rssiStr, isOpen ? "NO":"YES"};
sendImprovRPCResult(ImprovRPCType::Request_Scan, 3, str);
}
sendImprovRPCResult(ImprovRPCType::Request_Scan, 0);
WiFi.scanDelete();
}
#else
void startImprovWifiScan() {}
void handleImprovWifiScan() {}
#endif
void parseWiFiCommand(char* rpcData) { void parseWiFiCommand(char* rpcData) {
uint8_t len = rpcData[0]; uint8_t len = rpcData[0];
if (!len || len > 126) return; if (!len || len > 126) return;

View File

@ -70,13 +70,9 @@ void decBrightness()
} }
} }
// apply preset or fallback to a effect and palette if it doesn't exist
void presetFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID) void presetFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID)
{ {
applyPreset(presetID, CALL_MODE_BUTTON_PRESET); applyPresetWithFallback(presetID, CALL_MODE_BUTTON_PRESET, effectID, paletteID);
//these two will be overwritten if preset exists in handlePresets()
effectCurrent = effectID;
effectPalette = paletteID;
} }
byte relativeChange(byte property, int8_t amount, byte lowerBoundary, byte higherBoundary) byte relativeChange(byte property, int8_t amount, byte lowerBoundary, byte higherBoundary)

View File

@ -34,7 +34,7 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
uint16_t start = elem["start"] | seg.start; uint16_t start = elem["start"] | seg.start;
if (stop < 0) { if (stop < 0) {
uint16_t len = elem["len"]; int len = elem["len"];
stop = (len > 0) ? start + len : seg.stop; stop = (len > 0) ? start + len : seg.stop;
} }
// 2D segments // 2D segments
@ -70,9 +70,10 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
const char * name = elem["n"].as<const char*>(); const char * name = elem["n"].as<const char*>();
size_t len = 0; size_t len = 0;
if (name != nullptr) len = strlen(name); if (name != nullptr) len = strlen(name);
if (len > 0 && len < 33) { if (len > 0) {
if (len > WLED_MAX_SEGNAME_LEN) len = WLED_MAX_SEGNAME_LEN;
seg.name = new char[len+1]; seg.name = new char[len+1];
if (seg.name) strlcpy(seg.name, name, 33); if (seg.name) strlcpy(seg.name, name, WLED_MAX_SEGNAME_LEN+1);
} else { } else {
// but is empty (already deleted above) // but is empty (already deleted above)
elem.remove("n"); elem.remove("n");
@ -425,7 +426,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
// a) already applied preset content (requires "seg" or "win" but will ignore the rest) // a) already applied preset content (requires "seg" or "win" but will ignore the rest)
if (!root["pd"].isNull() && stateChanged) { if (!root["pd"].isNull() && stateChanged) {
currentPreset = root[F("pd")] | currentPreset; currentPreset = root[F("pd")] | currentPreset;
if (root["win"].isNull()) presetCycCurr = currentPreset; if (root["win"].isNull()) presetCycCurr = currentPreset; // otherwise it was set in handleSet() [set.cpp]
presetToRestore = currentPreset; // stateUpdated() will clear the preset, so we need to restore it after presetToRestore = currentPreset; // stateUpdated() will clear the preset, so we need to restore it after
//unloadPlaylist(); // applying a preset unloads the playlist, may be needed here too? //unloadPlaylist(); // applying a preset unloads the playlist, may be needed here too?
} else if (!root["ps"].isNull()) { } else if (!root["ps"].isNull()) {
@ -472,7 +473,7 @@ void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset, b
root[F("stopY")] = seg.stopY; root[F("stopY")] = seg.stopY;
} }
} }
if (!forPreset) root["len"] = seg.stop - seg.start; if (!forPreset) root["len"] = (seg.stop >= seg.start) ? (seg.stop - seg.start) : 0;
root["grp"] = seg.grouping; root["grp"] = seg.grouping;
root[F("spc")] = seg.spacing; root[F("spc")] = seg.spacing;
root[F("of")] = seg.offset; root[F("of")] = seg.offset;

View File

@ -104,6 +104,7 @@ void stateUpdated(byte callMode) {
if (stateChanged) currentPreset = 0; //something changed, so we are no longer in the preset if (stateChanged) currentPreset = 0; //something changed, so we are no longer in the preset
if (callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) notify(callMode); if (callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) notify(callMode);
if (bri != briOld && nodeBroadcastEnabled) sendSysInfoUDP(); // update on state
//set flag to update ws and mqtt //set flag to update ws and mqtt
interfaceUpdateCallMode = callMode; interfaceUpdateCallMode = callMode;
@ -146,8 +147,8 @@ void stateUpdated(byte callMode) {
if (transitionActive) { if (transitionActive) {
briOld = briT; briOld = briT;
tperLast = 0; tperLast = 0;
} } else
strip.setTransitionMode(true); // force all segments to transition mode strip.setTransitionMode(true); // force all segments to transition mode
transitionActive = true; transitionActive = true;
transitionStartTime = millis(); transitionStartTime = millis();
} else { } else {

View File

@ -120,6 +120,15 @@ bool applyPreset(byte index, byte callMode)
return true; return true;
} }
// apply preset or fallback to a effect and palette if it doesn't exist
void applyPresetWithFallback(uint8_t index, uint8_t callMode, uint8_t effectID, uint8_t paletteID)
{
applyPreset(index, callMode);
//these two will be overwritten if preset exists in handlePresets()
effectCurrent = effectID;
effectPalette = paletteID;
}
void handlePresets() void handlePresets()
{ {
if (presetToSave) { if (presetToSave) {
@ -171,7 +180,7 @@ void handlePresets()
fdo.remove("ps"); // remove load request for presets to prevent recursive crash (if not called by button and contains preset cycling string "1~5~") fdo.remove("ps"); // remove load request for presets to prevent recursive crash (if not called by button and contains preset cycling string "1~5~")
deserializeState(fdo, CALL_MODE_NO_NOTIFY, tmpPreset); // may change presetToApply by calling applyPreset() deserializeState(fdo, CALL_MODE_NO_NOTIFY, tmpPreset); // may change presetToApply by calling applyPreset()
} }
if (!errorFlag && tmpPreset < 255 && changePreset) presetCycCurr = currentPreset = tmpPreset; if (!errorFlag && tmpPreset < 255 && changePreset) currentPreset = tmpPreset;
#if defined(ARDUINO_ARCH_ESP32) #if defined(ARDUINO_ARCH_ESP32)
//Aircoookie recommended not to delete buffer //Aircoookie recommended not to delete buffer

200
wled00/remote.cpp Normal file
View File

@ -0,0 +1,200 @@
#include "wled.h"
#define ESP_NOW_STATE_UNINIT 0
#define ESP_NOW_STATE_ON 1
#define ESP_NOW_STATE_ERROR 2
#define NIGHT_MODE_DEACTIVATED -1
#define NIGHT_MODE_BRIGHTNESS 5
#define WIZMOTE_BUTTON_ON 1
#define WIZMOTE_BUTTON_OFF 2
#define WIZMOTE_BUTTON_NIGHT 3
#define WIZMOTE_BUTTON_ONE 16
#define WIZMOTE_BUTTON_TWO 17
#define WIZMOTE_BUTTON_THREE 18
#define WIZMOTE_BUTTON_FOUR 19
#define WIZMOTE_BUTTON_BRIGHT_UP 9
#define WIZMOTE_BUTTON_BRIGHT_DOWN 8
#ifdef WLED_DISABLE_ESPNOW
void handleRemote(){}
#else
// This is kind of an esoteric strucure because it's pulled from the "Wizmote"
// product spec. That remote is used as the baseline for behavior and availability
// since it's broadly commercially available and works out of the box as a drop-in
typedef struct message_structure {
uint8_t program; // 0x91 for ON button, 0x81 for all others
uint8_t seq[4]; // Incremetal sequence number 32 bit unsigned integer LSB first
uint8_t byte5 = 32; // Unknown
uint8_t button; // Identifies which button is being pressed
uint8_t byte8 = 1; // Unknown, but always 0x01
uint8_t byte9 = 100; // Unnkown, but always 0x64
uint8_t byte10; // Unknown, maybe checksum
uint8_t byte11; // Unknown, maybe checksum
uint8_t byte12; // Unknown, maybe checksum
uint8_t byte13; // Unknown, maybe checksum
} message_structure;
static int esp_now_state = ESP_NOW_STATE_UNINIT;
static uint32_t last_seq = -1;
static int brightnessBeforeNightMode = NIGHT_MODE_DEACTIVATED;
static message_structure incoming;
// Pulled from the IR Remote logic but reduced to 10 steps with a constant of 3
const byte brightnessSteps[] = {
6, 9, 14, 22, 33, 50, 75, 113, 170, 255
};
const size_t numBrightnessSteps = sizeof(brightnessSteps) / sizeof(uint8_t);
bool nightModeActive() {
return brightnessBeforeNightMode != NIGHT_MODE_DEACTIVATED;
}
void activateNightMode() {
brightnessBeforeNightMode = bri;
bri = NIGHT_MODE_BRIGHTNESS;
}
bool resetNightMode() {
if (!nightModeActive()) {
return false;
}
bri = brightnessBeforeNightMode;
brightnessBeforeNightMode = NIGHT_MODE_DEACTIVATED;
return true;
}
// increment `bri` to the next `brightnessSteps` value
void brightnessUp() {
if (nightModeActive()) { return; }
// dumb incremental search is efficient enough for so few items
for (uint8_t index = 0; index < numBrightnessSteps; ++index) {
if (brightnessSteps[index] > bri) {
bri = brightnessSteps[index];
break;
}
}
}
// decrement `bri` to the next `brightnessSteps` value
void brightnessDown() {
if (nightModeActive()) { return; }
// dumb incremental search is efficient enough for so few items
for (int index = numBrightnessSteps - 1; index >= 0; --index) {
if (brightnessSteps[index] < bri) {
bri = brightnessSteps[index];
break;
}
}
}
void setOn() {
if (resetNightMode()) {
stateUpdated(CALL_MODE_BUTTON);
}
if (!bri) {
toggleOnOff();
}
}
void setOff() {
if (resetNightMode()) {
stateUpdated(CALL_MODE_BUTTON);
}
if (bri) {
toggleOnOff();
}
}
void presetWithFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID) {
applyPresetWithFallback(presetID, CALL_MODE_BUTTON_PRESET, effectID, paletteID);
}
// Callback function that will be executed when data is received
#ifdef ESP8266
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
#else
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
#endif
sprintf (last_signal_src, "%02x%02x%02x%02x%02x%02x",
mac [0], mac [1], mac [2], mac [3], mac [4], mac [5]);
if (strcmp(last_signal_src, linked_remote) != 0) {
DEBUG_PRINT(F("ESP Now Message Received from Unlinked Sender: "));
DEBUG_PRINTLN(last_signal_src);
return;
}
if (len != sizeof(incoming)) {
DEBUG_PRINT(F("Unknown incoming ESP Now message received of length "));
DEBUG_PRINTLN(len);
return;
}
memcpy(&(incoming.program), incomingData, sizeof(incoming));
uint32_t cur_seq = incoming.seq[0] | (incoming.seq[1] << 8) | (incoming.seq[2] << 16) | (incoming.seq[3] << 24);
if (cur_seq == last_seq) {
return;
}
DEBUG_PRINT(F("Incoming ESP Now Packet["));
DEBUG_PRINT(cur_seq);
DEBUG_PRINT(F("] from sender["));
DEBUG_PRINT(last_signal_src);
DEBUG_PRINT(F("] button: "));
DEBUG_PRINTLN(incoming.button);
switch (incoming.button) {
case WIZMOTE_BUTTON_ON : setOn(); stateUpdated(CALL_MODE_BUTTON); break;
case WIZMOTE_BUTTON_OFF : setOff(); stateUpdated(CALL_MODE_BUTTON); break;
case WIZMOTE_BUTTON_ONE : presetWithFallback(1, FX_MODE_STATIC, 0); resetNightMode(); break;
case WIZMOTE_BUTTON_TWO : presetWithFallback(2, FX_MODE_BREATH, 0); resetNightMode(); break;
case WIZMOTE_BUTTON_THREE : presetWithFallback(3, FX_MODE_FIRE_FLICKER, 0); resetNightMode(); break;
case WIZMOTE_BUTTON_FOUR : presetWithFallback(4, FX_MODE_RAINBOW, 0); resetNightMode(); break;
case WIZMOTE_BUTTON_NIGHT : activateNightMode(); stateUpdated(CALL_MODE_BUTTON); break;
case WIZMOTE_BUTTON_BRIGHT_UP : brightnessUp(); stateUpdated(CALL_MODE_BUTTON); break;
case WIZMOTE_BUTTON_BRIGHT_DOWN : brightnessDown(); stateUpdated(CALL_MODE_BUTTON); break;
default: break;
}
last_seq = cur_seq;
}
void handleRemote() {
if (enable_espnow_remote) {
if (esp_now_state == ESP_NOW_STATE_UNINIT) {
DEBUG_PRINTLN(F("Initializing ESP_NOW listener"));
// Init ESP-NOW
if (esp_now_init() != 0) {
DEBUG_PRINTLN(F("Error initializing ESP-NOW"));
esp_now_state = ESP_NOW_STATE_ERROR;
}
#ifdef ESP8266
esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
#endif
esp_now_register_recv_cb(OnDataRecv);
esp_now_state = ESP_NOW_STATE_ON;
}
} else {
if (esp_now_state == ESP_NOW_STATE_ON) {
DEBUG_PRINTLN(F("Disabling ESP-NOW Remote Listener"));
if (esp_now_deinit() != 0) {
DEBUG_PRINTLN(F("Error de-initializing ESP-NOW"));
}
esp_now_state = ESP_NOW_STATE_UNINIT;
} else if (esp_now_state == ESP_NOW_STATE_ERROR) {
//Clear any error states (allows retrying by cycling)
esp_now_state = ESP_NOW_STATE_UNINIT;
}
}
}
#endif

View File

@ -7,19 +7,17 @@
//called upon POST settings form submit //called upon POST settings form submit
void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
{ {
// PIN code request if (subPage == SUBPAGE_PINREQ)
if (subPage == 252)
{ {
correctPIN = (strlen(settingsPIN)==0 || strncmp(settingsPIN, request->arg(F("PIN")).c_str(), 4)==0); checkSettingsPIN(request->arg(F("PIN")).c_str());
lastEditTime = millis();
return; return;
} }
//0: menu 1: wifi 2: leds 3: ui 4: sync 5: time 6: sec 7: DMX 8: usermods 9: N/A 10: 2D //0: menu 1: wifi 2: leds 3: ui 4: sync 5: time 6: sec 7: DMX 8: usermods 9: N/A 10: 2D
if (subPage <1 || subPage >10 || !correctPIN) return; if (subPage < 1 || subPage > 10 || !correctPIN) return;
//WIFI SETTINGS //WIFI SETTINGS
if (subPage == 1) if (subPage == SUBPAGE_WIFI)
{ {
strlcpy(clientSSID,request->arg(F("CS")).c_str(), 33); strlcpy(clientSSID,request->arg(F("CS")).c_str(), 33);
@ -36,6 +34,14 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
noWifiSleep = request->hasArg(F("WS")); noWifiSleep = request->hasArg(F("WS"));
#ifndef WLED_DISABLE_ESPNOW
enable_espnow_remote = request->hasArg(F("RE"));
strlcpy(linked_remote,request->arg(F("RMAC")).c_str(), 13);
//Normalize MAC format to lowercase
strlcpy(linked_remote,strlwr(linked_remote), 13);
#endif
#ifdef WLED_USE_ETHERNET #ifdef WLED_USE_ETHERNET
ethernetType = request->arg(F("ETH")).toInt(); ethernetType = request->arg(F("ETH")).toInt();
WLED::instance().initEthernet(); WLED::instance().initEthernet();
@ -58,7 +64,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
} }
//LED SETTINGS //LED SETTINGS
if (subPage == 2) if (subPage == SUBPAGE_LEDS)
{ {
int t = 0; int t = 0;
@ -262,7 +268,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
} }
//UI //UI
if (subPage == 3) if (subPage == SUBPAGE_UI)
{ {
strlcpy(serverDescription, request->arg(F("DS")).c_str(), 33); strlcpy(serverDescription, request->arg(F("DS")).c_str(), 33);
syncToggleReceive = request->hasArg(F("ST")); syncToggleReceive = request->hasArg(F("ST"));
@ -280,7 +286,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
} }
//SYNC //SYNC
if (subPage == 4) if (subPage == SUBPAGE_SYNC)
{ {
int t = request->arg(F("UP")).toInt(); int t = request->arg(F("UP")).toInt();
if (t > 0) udpPort = t; if (t > 0) udpPort = t;
@ -379,7 +385,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
} }
//TIME //TIME
if (subPage == 5) if (subPage == SUBPAGE_TIME)
{ {
ntpEnabled = request->hasArg(F("NT")); ntpEnabled = request->hasArg(F("NT"));
strlcpy(ntpServerName, request->arg(F("NS")).c_str(), 33); strlcpy(ntpServerName, request->arg(F("NS")).c_str(), 33);
@ -453,7 +459,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
} }
//SECURITY //SECURITY
if (subPage == 6) if (subPage == SUBPAGE_SEC)
{ {
if (request->hasArg(F("RS"))) //complete factory reset if (request->hasArg(F("RS"))) //complete factory reset
{ {
@ -484,7 +490,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
if (otaLock && strcmp(otaPass,request->arg(F("OP")).c_str()) == 0) if (otaLock && strcmp(otaPass,request->arg(F("OP")).c_str()) == 0)
{ {
// brute force protection: do not unlock even if correct if last save was less than 3 seconds ago // brute force protection: do not unlock even if correct if last save was less than 3 seconds ago
if (millis() - lastEditTime > 3000) pwdCorrect = true; if (millis() - lastEditTime > PIN_RETRY_COOLDOWN) pwdCorrect = true;
} }
if (!otaLock && request->arg(F("OP")).length() > 0) if (!otaLock && request->arg(F("OP")).length() > 0)
{ {
@ -502,7 +508,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
} }
#ifdef WLED_ENABLE_DMX // include only if DMX is enabled #ifdef WLED_ENABLE_DMX // include only if DMX is enabled
if (subPage == 7) if (subPage == SUBPAGE_DMX)
{ {
int t = request->arg(F("PU")).toInt(); int t = request->arg(F("PU")).toInt();
if (t >= 0 && t <= 63999) e131ProxyUniverse = t; if (t >= 0 && t <= 63999) e131ProxyUniverse = t;
@ -532,7 +538,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
#endif #endif
//USERMODS //USERMODS
if (subPage == 8) if (subPage == SUBPAGE_UM)
{ {
if (!requestJSONBufferLock(5)) return; if (!requestJSONBufferLock(5)) return;
@ -672,7 +678,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
//2D panels //2D panels
if (subPage == 10) if (subPage == SUBPAGE_2D)
{ {
strip.isMatrix = request->arg(F("SOMP")).toInt(); strip.isMatrix = request->arg(F("SOMP")).toInt();
strip.panel.clear(); // release memory if allocated strip.panel.clear(); // release memory if allocated
@ -709,10 +715,10 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
lastEditTime = millis(); lastEditTime = millis();
// do not save if factory reset or LED settings (which are saved after LED re-init) // do not save if factory reset or LED settings (which are saved after LED re-init)
doSerializeConfig = subPage != 2 && !(subPage == 6 && doReboot); doSerializeConfig = subPage != SUBPAGE_LEDS && !(subPage == SUBPAGE_SEC && doReboot);
if (subPage == 8) doReboot = request->hasArg(F("RBT")); // prevent race condition on dual core system (set reboot here, after doSerializeConfig has been set) if (subPage == SUBPAGE_UM) doReboot = request->hasArg(F("RBT")); // prevent race condition on dual core system (set reboot here, after doSerializeConfig has been set)
#ifndef WLED_DISABLE_ALEXA #ifndef WLED_DISABLE_ALEXA
if (subPage == 4) alexaInit(); if (subPage == SUBPAGE_SYNC) alexaInit();
#endif #endif
} }

View File

@ -669,6 +669,7 @@ void sendSysInfoUDP()
#else #else
data[38] = NODE_TYPE_ID_UNDEFINED; data[38] = NODE_TYPE_ID_UNDEFINED;
#endif #endif
if (bri) data[38] |= 0x80U; // add on/off state
data[39] = ip[3]; // unit ID == last IP number data[39] = ip[3]; // unit ID == last IP number
uint32_t build = VERSION; uint32_t build = VERSION;

View File

@ -373,6 +373,16 @@ int16_t extractModeDefaults(uint8_t mode, const char *segVar)
} }
void checkSettingsPIN(const char* pin) {
if (!pin) return;
if (!correctPIN && millis() - lastEditTime < PIN_RETRY_COOLDOWN) return; // guard against PIN brute force
bool correctBefore = correctPIN;
correctPIN = (strlen(settingsPIN) == 0 || strncmp(settingsPIN, pin, 4) == 0);
if (correctBefore != correctPIN) createEditHandler(correctPIN);
lastEditTime = millis();
}
uint16_t crc16(const unsigned char* data_p, size_t length) { uint16_t crc16(const unsigned char* data_p, size_t length) {
uint8_t x; uint8_t x;
uint16_t crc = 0xFFFF; uint16_t crc = 0xFFFF;
@ -493,8 +503,8 @@ um_data_t* simulateSound(uint8_t simulationId)
} }
samplePeak = random8() > 250; samplePeak = random8() > 250;
FFT_MajorPeak = volumeSmth; FFT_MajorPeak = 21 + (volumeSmth*volumeSmth) / 8.0f; // walk thru full range of 21hz...8200hz
maxVol = 10; // this gets feedback fro UI maxVol = 31; // this gets feedback fro UI
binNum = 8; // this gets feedback fro UI binNum = 8; // this gets feedback fro UI
volumeRaw = volumeSmth; volumeRaw = volumeSmth;
my_magnitude = 10000.0 / 8.0f; //no idea if 10000 is a good value for FFT_Magnitude ??? my_magnitude = 10000.0 / 8.0f; //no idea if 10000 is a good value for FFT_Magnitude ???

View File

@ -46,7 +46,9 @@ void WLED::loop()
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
#endif #endif
handleConnection(); handleConnection();
handleRemote();
handleSerial(); handleSerial();
handleImprovWifiScan();
handleNotifications(); handleNotifications();
handleTransitions(); handleTransitions();
#ifdef WLED_ENABLE_DMX #ifdef WLED_ENABLE_DMX
@ -138,7 +140,7 @@ void WLED::loop()
} }
// 15min PIN time-out // 15min PIN time-out
if (strlen(settingsPIN)>0 && millis() - lastEditTime > 900000) { if (strlen(settingsPIN)>0 && correctPIN && millis() - lastEditTime > PIN_TIMEOUT) {
correctPIN = false; correctPIN = false;
createEditHandler(false); createEditHandler(false);
} }
@ -645,7 +647,6 @@ void WLED::initConnection()
ws.onEvent(wsEvent); ws.onEvent(wsEvent);
#endif #endif
WiFi.disconnect(true); // close old connections WiFi.disconnect(true); // close old connections
#ifdef ESP8266 #ifdef ESP8266
WiFi.setPhyMode(WIFI_PHY_MODE_11N); WiFi.setPhyMode(WIFI_PHY_MODE_11N);
@ -844,7 +845,7 @@ void WLED::handleConnection()
if (improvActive) { if (improvActive) {
if (improvError == 3) sendImprovStateResponse(0x00, true); if (improvError == 3) sendImprovStateResponse(0x00, true);
sendImprovStateResponse(0x04); sendImprovStateResponse(0x04);
if (improvActive > 1) sendImprovRPCResponse(0x01); if (improvActive > 1) sendImprovIPRPCResult(ImprovRPCType::Command_Wifi);
} }
initInterfaces(); initInterfaces();
userConnected(); userConnected();

View File

@ -8,7 +8,7 @@
*/ */
// version code in format yymmddb (b = daily build) // version code in format yymmddb (b = daily build)
#define VERSION 2306130 #define VERSION 2307130
//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
@ -36,7 +36,7 @@
#undef WLED_ENABLE_ADALIGHT // disable has priority over enable #undef WLED_ENABLE_ADALIGHT // disable has priority over enable
#endif #endif
//#define WLED_ENABLE_DMX // uses 3.5kb (use LEDPIN other than 2) //#define WLED_ENABLE_DMX // uses 3.5kb (use LEDPIN other than 2)
//#define WLED_ENABLE_JSONLIVE // peek LED output via /json/live (WS binary peek is always enabled) #define WLED_ENABLE_JSONLIVE // peek LED output via /json/live (WS binary peek is always enabled)
#ifndef WLED_DISABLE_LOXONE #ifndef WLED_DISABLE_LOXONE
#define WLED_ENABLE_LOXONE // uses 1.2kb #define WLED_ENABLE_LOXONE // uses 1.2kb
#endif #endif
@ -44,6 +44,8 @@
#define WLED_ENABLE_WEBSOCKETS #define WLED_ENABLE_WEBSOCKETS
#endif #endif
//#define WLED_DISABLE_ESPNOW // Removes dependence on esp now
#define WLED_ENABLE_FS_EDITOR // enable /edit page for editing FS content. Will also be disabled with OTA lock #define WLED_ENABLE_FS_EDITOR // enable /edit page for editing FS content. Will also be disabled with OTA lock
// to toggle usb serial debug (un)comment the following line // to toggle usb serial debug (un)comment the following line
@ -73,6 +75,9 @@
{ {
#include <user_interface.h> #include <user_interface.h>
} }
#ifndef WLED_DISABLE_ESPNOW
#include <espnow.h>
#endif
#else // ESP32 #else // ESP32
#include <HardwareSerial.h> // ensure we have the correct "Serial" on new MCUs (depends on ARDUINO_USB_MODE and ARDUINO_USB_CDC_ON_BOOT) #include <HardwareSerial.h> // ensure we have the correct "Serial" on new MCUs (depends on ARDUINO_USB_MODE and ARDUINO_USB_CDC_ON_BOOT)
#include <WiFi.h> #include <WiFi.h>
@ -89,6 +94,10 @@
#include <LittleFS.h> #include <LittleFS.h>
#endif #endif
#include "esp_task_wdt.h" #include "esp_task_wdt.h"
#ifndef WLED_DISABLE_ESPNOW
#include <esp_now.h>
#endif
#endif #endif
#include <Wire.h> #include <Wire.h>
#include <SPI.h> #include <SPI.h>
@ -444,6 +453,12 @@ WLED_GLOBAL bool hueApplyColor _INIT(true);
WLED_GLOBAL uint16_t serialBaud _INIT(1152); // serial baud rate, multiply by 100 WLED_GLOBAL uint16_t serialBaud _INIT(1152); // serial baud rate, multiply by 100
#ifndef WLED_DISABLE_ESPNOW
WLED_GLOBAL bool enable_espnow_remote _INIT(false);
WLED_GLOBAL char linked_remote[13] _INIT("");
WLED_GLOBAL char last_signal_src[13] _INIT("");
#endif
// Time CONFIG // Time CONFIG
WLED_GLOBAL bool ntpEnabled _INIT(false); // get internet time. Only required if you use clock overlays or time-activated macros WLED_GLOBAL bool ntpEnabled _INIT(false); // get internet time. Only required if you use clock overlays or time-activated macros
WLED_GLOBAL bool useAMPM _INIT(false); // 12h/24h clock format WLED_GLOBAL bool useAMPM _INIT(false); // 12h/24h clock format

File diff suppressed because one or more lines are too long

View File

@ -1,387 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Source Files\Dependencies">
<UniqueIdentifier>{72fe60da-ba26-45b4-82c1-bdff809975da}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\Dependencies">
<UniqueIdentifier>{8880888d-efea-4189-a25a-834b7b3bb756}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<None Include="wled00.ino" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="__vm\.wled00.vsarduino.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="html_classic.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="html_mobile.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="html_other.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="html_settings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FX.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\json\ArduinoJson-v6.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\json\AsyncJson-v6.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient.hpp">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkApi.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkApiArduino.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkArduinoClient.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkConfig.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkDateTime.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkDebug.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkDetectDevice.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkEveryN.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkFifo.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkHandlers.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkParam.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkProtocol.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkProtocolDefs.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\blynk\BlynkSimpleEsp.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkTemplates.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkTimer.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkUtility.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkWidgetBase.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\blynk\Blynk\BlynkWiFiCommon.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Callbacks.hpp">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\ConnAckPacket.hpp">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\DisconnectReasons.hpp">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\espalexa\Espalexa.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\espalexa\EspalexaDevice.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\e131\ESPAsyncE131.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Flags.hpp">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Helpers.hpp">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\MessageProperties.hpp">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="NpbWrapper.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\Packet.hpp">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\ParsingInformation.hpp">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PingRespPacket.hpp">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PubAckPacket.hpp">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PubCompPacket.hpp">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PublishPacket.hpp">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PubRecPacket.hpp">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PubRelPacket.hpp">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Storage.hpp">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\SubAckPacket.hpp">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\time\Time.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\time\TimeLib.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\timezone\Timezone.h">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\UnsubAckPacket.hpp">
<Filter>Header Files\Dependencies</Filter>
</ClInclude>
<ClInclude Include="alexa.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="blynk.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="button.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="colors.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="const.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="cronixie.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="dmx.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="file.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="html_ui.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="hue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ir.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="json.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="led.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="mqtt.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="notify.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ntp.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="overlay.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="set.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="usermod.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="wled.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="wled_eeprom.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="wled_server.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="xml.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ir_codes.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="palettes.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="FX.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FX_fcn.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\dependencies\blynk\Blynk\utility.cpp">
<Filter>Source Files\Dependencies</Filter>
</ClCompile>
<ClCompile Include="src\dependencies\async-mqtt-client\AsyncMqttClient.cpp">
<Filter>Source Files\Dependencies</Filter>
</ClCompile>
<ClCompile Include="src\dependencies\blynk\Blynk\BlynkDebug.cpp">
<Filter>Source Files\Dependencies</Filter>
</ClCompile>
<ClCompile Include="src\dependencies\blynk\Blynk\BlynkHandlers.cpp">
<Filter>Source Files\Dependencies</Filter>
</ClCompile>
<ClCompile Include="src\dependencies\blynk\Blynk\BlynkTimer.cpp">
<Filter>Source Files\Dependencies</Filter>
</ClCompile>
<ClCompile Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\ConnAckPacket.cpp">
<Filter>Source Files\Dependencies</Filter>
</ClCompile>
<ClCompile Include="src\dependencies\time\DateStrings.cpp">
<Filter>Source Files\Dependencies</Filter>
</ClCompile>
<ClCompile Include="src\dependencies\espalexa\EspalexaDevice.cpp">
<Filter>Source Files\Dependencies</Filter>
</ClCompile>
<ClCompile Include="src\dependencies\e131\ESPAsyncE131.cpp">
<Filter>Source Files\Dependencies</Filter>
</ClCompile>
<ClCompile Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PingRespPacket.cpp">
<Filter>Source Files\Dependencies</Filter>
</ClCompile>
<ClCompile Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PubAckPacket.cpp">
<Filter>Source Files\Dependencies</Filter>
</ClCompile>
<ClCompile Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PubCompPacket.cpp">
<Filter>Source Files\Dependencies</Filter>
</ClCompile>
<ClCompile Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PublishPacket.cpp">
<Filter>Source Files\Dependencies</Filter>
</ClCompile>
<ClCompile Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PubRecPacket.cpp">
<Filter>Source Files\Dependencies</Filter>
</ClCompile>
<ClCompile Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\PubRelPacket.cpp">
<Filter>Source Files\Dependencies</Filter>
</ClCompile>
<ClCompile Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\SubAckPacket.cpp">
<Filter>Source Files\Dependencies</Filter>
</ClCompile>
<ClCompile Include="src\dependencies\time\Time.cpp">
<Filter>Source Files\Dependencies</Filter>
</ClCompile>
<ClCompile Include="src\dependencies\timezone\Timezone.cpp">
<Filter>Source Files\Dependencies</Filter>
</ClCompile>
<ClCompile Include="src\dependencies\async-mqtt-client\AsyncMqttClient\Packets\UnsubAckPacket.cpp">
<Filter>Source Files\Dependencies</Filter>
</ClCompile>
<ClCompile Include="alexa.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="blynk.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="button.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="colors.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="cronixie.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="file.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="hue.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ir.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="json.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="led.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="mqtt.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="notify.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ntp.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="overlay.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="set.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="usermod.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="wled.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="wled_eeprom.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="wled_server.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="xml.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>

View File

@ -116,14 +116,6 @@ void initServer()
DefaultHeaders::Instance().addHeader(F("Access-Control-Allow-Headers"), "*"); DefaultHeaders::Instance().addHeader(F("Access-Control-Allow-Headers"), "*");
#ifdef WLED_ENABLE_WEBSOCKETS #ifdef WLED_ENABLE_WEBSOCKETS
server.on("/liveview", HTTP_GET, [](AsyncWebServerRequest *request){
if (handleIfNoneMatchCacheHeader(request)) return;
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_liveviewws, PAGE_liveviewws_length);
response->addHeader(FPSTR(s_content_enc),"gzip");
setStaticContentCacheHeaders(response);
request->send(response);
//request->send_P(200, "text/html", PAGE_liveviewws);
});
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
server.on("/liveview2D", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/liveview2D", HTTP_GET, [](AsyncWebServerRequest *request){
if (handleIfNoneMatchCacheHeader(request)) return; if (handleIfNoneMatchCacheHeader(request)) return;
@ -131,19 +123,16 @@ void initServer()
response->addHeader(FPSTR(s_content_enc),"gzip"); response->addHeader(FPSTR(s_content_enc),"gzip");
setStaticContentCacheHeaders(response); setStaticContentCacheHeaders(response);
request->send(response); request->send(response);
//request->send_P(200, "text/html", PAGE_liveviewws);
}); });
#endif #endif
#else #endif
server.on("/liveview", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/liveview", HTTP_GET, [](AsyncWebServerRequest *request){
if (handleIfNoneMatchCacheHeader(request)) return; if (handleIfNoneMatchCacheHeader(request)) return;
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_liveview, PAGE_liveview_length); AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_liveview, PAGE_liveview_length);
response->addHeader(FPSTR(s_content_enc),"gzip"); response->addHeader(FPSTR(s_content_enc),"gzip");
setStaticContentCacheHeaders(response); setStaticContentCacheHeaders(response);
request->send(response); request->send(response);
//request->send_P(200, "text/html", PAGE_liveview);
}); });
#endif
//settings page //settings page
server.on("/settings", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/settings", HTTP_GET, [](AsyncWebServerRequest *request){
@ -167,10 +156,6 @@ void initServer()
} }
}); });
server.on("/sliders", HTTP_GET, [](AsyncWebServerRequest *request){
serveIndex(request);
});
server.on("/welcome", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/welcome", HTTP_GET, [](AsyncWebServerRequest *request){
serveSettings(request); serveSettings(request);
}); });
@ -201,6 +186,8 @@ void initServer()
request->send(400, "application/json", F("{\"error\":9}")); // ERR_JSON request->send(400, "application/json", F("{\"error\":9}")); // ERR_JSON
return; return;
} }
if (root.containsKey("pin")) checkSettingsPIN(root["pin"].as<const char*>());
const String& url = request->url(); const String& url = request->url();
isConfig = url.indexOf("cfg") > -1; isConfig = url.indexOf("cfg") > -1;
if (!isConfig) { if (!isConfig) {
@ -245,19 +232,15 @@ void initServer()
request->send(200, "text/plain", (String)ESP.getFreeHeap()); request->send(200, "text/plain", (String)ESP.getFreeHeap());
}); });
#ifdef WLED_ENABLE_USERMOD_PAGE
server.on("/u", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/u", HTTP_GET, [](AsyncWebServerRequest *request){
if (handleIfNoneMatchCacheHeader(request)) return; if (handleIfNoneMatchCacheHeader(request)) return;
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_usermod, PAGE_usermod_length); AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_usermod, PAGE_usermod_length);
response->addHeader(FPSTR(s_content_enc),"gzip"); response->addHeader(FPSTR(s_content_enc),"gzip");
setStaticContentCacheHeaders(response); setStaticContentCacheHeaders(response);
request->send(response); request->send(response);
//request->send_P(200, "text/html", PAGE_usermod);
});
//Deprecated, use of /json/state and presets recommended instead
server.on("/url", HTTP_GET, [](AsyncWebServerRequest *request){
URL_response(request);
}); });
#endif
server.on("/teapot", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/teapot", HTTP_GET, [](AsyncWebServerRequest *request){
serveMessage(request, 418, F("418. I'm a teapot."), F("(Tangible Embedded Advanced Project Of Twinkling)"), 254); serveMessage(request, 418, F("418. I'm a teapot."), F("(Tangible Embedded Advanced Project Of Twinkling)"), 254);
@ -354,9 +337,14 @@ void initServer()
serveMessage(request, 501, "Not implemented", F("DMX support is not enabled in this build."), 254); serveMessage(request, 501, "Not implemented", F("DMX support is not enabled in this build."), 254);
}); });
#endif #endif
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
if (captivePortal(request)) return; if (captivePortal(request)) return;
serveIndexOrWelcome(request); if (!showWelcomePage || request->hasArg(F("sliders"))){
serveIndex(request);
} else {
serveSettings(request);
}
}); });
#ifdef WLED_ENABLE_PIXART #ifdef WLED_ENABLE_PIXART
@ -418,20 +406,9 @@ void initServer()
response->addHeader(FPSTR(s_content_enc),"gzip"); response->addHeader(FPSTR(s_content_enc),"gzip");
setStaticContentCacheHeaders(response); setStaticContentCacheHeaders(response);
request->send(response); request->send(response);
//request->send_P(404, "text/html", PAGE_404);
}); });
} }
void serveIndexOrWelcome(AsyncWebServerRequest *request)
{
if (!showWelcomePage){
serveIndex(request);
} else {
serveSettings(request);
}
}
bool handleIfNoneMatchCacheHeader(AsyncWebServerRequest* request) bool handleIfNoneMatchCacheHeader(AsyncWebServerRequest* request)
{ {
AsyncWebHeader* header = request->getHeader("If-None-Match"); AsyncWebHeader* header = request->getHeader("If-None-Match");
@ -573,64 +550,62 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
if (url.indexOf("sett") >= 0) if (url.indexOf("sett") >= 0)
{ {
if (url.indexOf(".js") > 0) subPage = 254; if (url.indexOf(".js") > 0) subPage = SUBPAGE_JS;
else if (url.indexOf(".css") > 0) subPage = 253; else if (url.indexOf(".css") > 0) subPage = SUBPAGE_CSS;
else if (url.indexOf("wifi") > 0) subPage = 1; else if (url.indexOf("wifi") > 0) subPage = SUBPAGE_WIFI;
else if (url.indexOf("leds") > 0) subPage = 2; else if (url.indexOf("leds") > 0) subPage = SUBPAGE_LEDS;
else if (url.indexOf("ui") > 0) subPage = 3; else if (url.indexOf("ui") > 0) subPage = SUBPAGE_UI;
else if (url.indexOf("sync") > 0) subPage = 4; else if (url.indexOf("sync") > 0) subPage = SUBPAGE_SYNC;
else if (url.indexOf("time") > 0) subPage = 5; else if (url.indexOf("time") > 0) subPage = SUBPAGE_TIME;
else if (url.indexOf("sec") > 0) subPage = 6; else if (url.indexOf("sec") > 0) subPage = SUBPAGE_SEC;
else if (url.indexOf("dmx") > 0) subPage = 7; else if (url.indexOf("dmx") > 0) subPage = SUBPAGE_DMX;
else if (url.indexOf("um") > 0) subPage = 8; else if (url.indexOf("um") > 0) subPage = SUBPAGE_UM;
else if (url.indexOf("2D") > 0) subPage = 10; else if (url.indexOf("2D") > 0) subPage = SUBPAGE_2D;
else if (url.indexOf("lock") > 0) subPage = 251; else if (url.indexOf("lock") > 0) subPage = SUBPAGE_LOCK;
} }
else if (url.indexOf("/update") >= 0) subPage = 9; // update page, for PIN check else if (url.indexOf("/update") >= 0) subPage = SUBPAGE_UPDATE; // update page, for PIN check
//else if (url.indexOf("/edit") >= 0) subPage = 10; //else if (url.indexOf("/edit") >= 0) subPage = 10;
else subPage = 255; // welcome page else subPage = SUBPAGE_WELCOME;
if (!correctPIN && strlen(settingsPIN) > 0 && (subPage > 0 && subPage < 11)) { if (!correctPIN && strlen(settingsPIN) > 0 && (subPage > 0 && subPage < 11)) {
originalSubPage = subPage; originalSubPage = subPage;
subPage = 252; // require PIN subPage = SUBPAGE_PINREQ; // require PIN
} }
// if OTA locked or too frequent PIN entry requests fail hard // if OTA locked or too frequent PIN entry requests fail hard
if ((subPage == 1 && wifiLock && otaLock) || (post && !correctPIN && millis()-lastEditTime < 3000)) if ((subPage == SUBPAGE_WIFI && wifiLock && otaLock) || (post && !correctPIN && millis()-lastEditTime < PIN_RETRY_COOLDOWN))
{ {
serveMessage(request, 500, "Access Denied", FPSTR(s_unlock_ota), 254); return; serveMessage(request, 500, "Access Denied", FPSTR(s_unlock_ota), 254); return;
} }
if (post) { //settings/set POST request, saving if (post) { //settings/set POST request, saving
if (subPage != 1 || !(wifiLock && otaLock)) handleSettingsSet(request, subPage); if (subPage != SUBPAGE_WIFI || !(wifiLock && otaLock)) handleSettingsSet(request, subPage);
char s[32]; char s[32];
char s2[45] = ""; char s2[45] = "";
switch (subPage) { switch (subPage) {
case 1: strcpy_P(s, PSTR("WiFi")); strcpy_P(s2, PSTR("Please connect to the new IP (if changed)")); forceReconnect = true; break; case SUBPAGE_WIFI : strcpy_P(s, PSTR("WiFi")); strcpy_P(s2, PSTR("Please connect to the new IP (if changed)")); forceReconnect = true; break;
case 2: strcpy_P(s, PSTR("LED")); break; case SUBPAGE_LEDS : strcpy_P(s, PSTR("LED")); break;
case 3: strcpy_P(s, PSTR("UI")); break; case SUBPAGE_UI : strcpy_P(s, PSTR("UI")); break;
case 4: strcpy_P(s, PSTR("Sync")); break; case SUBPAGE_SYNC : strcpy_P(s, PSTR("Sync")); break;
case 5: strcpy_P(s, PSTR("Time")); break; case SUBPAGE_TIME : strcpy_P(s, PSTR("Time")); break;
case 6: strcpy_P(s, PSTR("Security")); if (doReboot) strcpy_P(s2, PSTR("Rebooting, please wait ~10 seconds...")); break; case SUBPAGE_SEC : strcpy_P(s, PSTR("Security")); if (doReboot) strcpy_P(s2, PSTR("Rebooting, please wait ~10 seconds...")); break;
case 7: strcpy_P(s, PSTR("DMX")); break; case SUBPAGE_DMX : strcpy_P(s, PSTR("DMX")); break;
case 8: strcpy_P(s, PSTR("Usermods")); break; case SUBPAGE_UM : strcpy_P(s, PSTR("Usermods")); break;
case 10: strcpy_P(s, PSTR("2D")); break; case SUBPAGE_2D : strcpy_P(s, PSTR("2D")); break;
case 252: strcpy_P(s, correctPIN ? PSTR("PIN accepted") : PSTR("PIN rejected")); break; case SUBPAGE_PINREQ : strcpy_P(s, correctPIN ? PSTR("PIN accepted") : PSTR("PIN rejected")); break;
} }
if (subPage == 252) { if (subPage != SUBPAGE_PINREQ) strcat_P(s, PSTR(" settings saved."));
createEditHandler(correctPIN);
} else
strcat_P(s, PSTR(" settings saved."));
if (subPage == 252 && correctPIN) { if (subPage == SUBPAGE_PINREQ && correctPIN) {
subPage = originalSubPage; // on correct PIN load settings page the user intended subPage = originalSubPage; // on correct PIN load settings page the user intended
} else { } else {
if (!s2[0]) strcpy_P(s2, s_redirecting); if (!s2[0]) strcpy_P(s2, s_redirecting);
serveMessage(request, 200, s, s2, (subPage == 1 || ((subPage == 6 || subPage == 8) && doReboot)) ? 129 : (correctPIN ? 1 : 3)); bool redirectAfter9s = (subPage == SUBPAGE_WIFI || ((subPage == SUBPAGE_SEC || subPage == SUBPAGE_UM) && doReboot));
serveMessage(request, 200, s, s2, redirectAfter9s ? 129 : (correctPIN ? 1 : 3));
return; return;
} }
} }
@ -638,30 +613,30 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
AsyncWebServerResponse *response; AsyncWebServerResponse *response;
switch (subPage) switch (subPage)
{ {
case 1: response = request->beginResponse_P(200, "text/html", PAGE_settings_wifi, PAGE_settings_wifi_length); break; case SUBPAGE_WIFI : response = request->beginResponse_P(200, "text/html", PAGE_settings_wifi, PAGE_settings_wifi_length); break;
case 2: response = request->beginResponse_P(200, "text/html", PAGE_settings_leds, PAGE_settings_leds_length); break; case SUBPAGE_LEDS : response = request->beginResponse_P(200, "text/html", PAGE_settings_leds, PAGE_settings_leds_length); break;
case 3: response = request->beginResponse_P(200, "text/html", PAGE_settings_ui, PAGE_settings_ui_length); break; case SUBPAGE_UI : response = request->beginResponse_P(200, "text/html", PAGE_settings_ui, PAGE_settings_ui_length); break;
case 4: response = request->beginResponse_P(200, "text/html", PAGE_settings_sync, PAGE_settings_sync_length); break; case SUBPAGE_SYNC : response = request->beginResponse_P(200, "text/html", PAGE_settings_sync, PAGE_settings_sync_length); break;
case 5: response = request->beginResponse_P(200, "text/html", PAGE_settings_time, PAGE_settings_time_length); break; case SUBPAGE_TIME : response = request->beginResponse_P(200, "text/html", PAGE_settings_time, PAGE_settings_time_length); break;
case 6: response = request->beginResponse_P(200, "text/html", PAGE_settings_sec, PAGE_settings_sec_length); break; case SUBPAGE_SEC : response = request->beginResponse_P(200, "text/html", PAGE_settings_sec, PAGE_settings_sec_length); break;
#ifdef WLED_ENABLE_DMX #ifdef WLED_ENABLE_DMX
case 7: response = request->beginResponse_P(200, "text/html", PAGE_settings_dmx, PAGE_settings_dmx_length); break; case SUBPAGE_DMX : response = request->beginResponse_P(200, "text/html", PAGE_settings_dmx, PAGE_settings_dmx_length); break;
#endif #endif
case 8: response = request->beginResponse_P(200, "text/html", PAGE_settings_um, PAGE_settings_um_length); break; case SUBPAGE_UM : response = request->beginResponse_P(200, "text/html", PAGE_settings_um, PAGE_settings_um_length); break;
case 9: response = request->beginResponse_P(200, "text/html", PAGE_update, PAGE_update_length); break; case SUBPAGE_UPDATE : response = request->beginResponse_P(200, "text/html", PAGE_update, PAGE_update_length); break;
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
case 10: response = request->beginResponse_P(200, "text/html", PAGE_settings_2D, PAGE_settings_2D_length); break; case SUBPAGE_2D : response = request->beginResponse_P(200, "text/html", PAGE_settings_2D, PAGE_settings_2D_length); break;
#endif #endif
case 251: { case SUBPAGE_LOCK : {
correctPIN = !strlen(settingsPIN); // lock if a pin is set correctPIN = !strlen(settingsPIN); // lock if a pin is set
createEditHandler(correctPIN); createEditHandler(correctPIN);
serveMessage(request, 200, strlen(settingsPIN) > 0 ? PSTR("Settings locked") : PSTR("No PIN set"), FPSTR(s_redirecting), 1); serveMessage(request, 200, strlen(settingsPIN) > 0 ? PSTR("Settings locked") : PSTR("No PIN set"), FPSTR(s_redirecting), 1);
return; return;
} }
case 252: response = request->beginResponse_P(200, "text/html", PAGE_settings_pin, PAGE_settings_pin_length); break; case SUBPAGE_PINREQ : response = request->beginResponse_P(200, "text/html", PAGE_settings_pin, PAGE_settings_pin_length); break;
case 253: response = request->beginResponse_P(200, "text/css", PAGE_settingsCss, PAGE_settingsCss_length); break; case SUBPAGE_CSS : response = request->beginResponse_P(200, "text/css", PAGE_settingsCss, PAGE_settingsCss_length); break;
case 254: serveSettingsJS(request); return; case SUBPAGE_JS : serveSettingsJS(request); return;
case 255: response = request->beginResponse_P(200, "text/html", PAGE_welcome, PAGE_welcome_length); break; case SUBPAGE_WELCOME : response = request->beginResponse_P(200, "text/html", PAGE_welcome, PAGE_welcome_length); break;
default: response = request->beginResponse_P(200, "text/html", PAGE_settings, PAGE_settings_length); break; default: response = request->beginResponse_P(200, "text/html", PAGE_settings, PAGE_settings_length); break;
} }
response->addHeader(FPSTR(s_content_enc),"gzip"); response->addHeader(FPSTR(s_content_enc),"gzip");

View File

@ -54,14 +54,15 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
} }
releaseJSONBufferLock(); // will clean fileDoc releaseJSONBufferLock(); // will clean fileDoc
// force broadcast in 500ms after updating client if (!interfaceUpdateCallMode) { // individual client response only needed if no WS broadcast soon
if (verboseResponse) { if (verboseResponse) {
sendDataWs(client); sendDataWs(client);
lastInterfaceUpdate = millis() - (INTERFACE_UPDATE_COOLDOWN -500); } else {
} else { // we have to send something back otherwise WS connection closes
// we have to send something back otherwise WS connection closes client->text(F("{\"success\":true}"));
client->text(F("{\"success\":true}")); }
lastInterfaceUpdate = millis() - (INTERFACE_UPDATE_COOLDOWN -500); // force broadcast in 500ms after updating client
//lastInterfaceUpdate = millis() - (INTERFACE_UPDATE_COOLDOWN -500); // ESP8266 does not like this
} }
} }
} else { } else {

View File

@ -72,55 +72,6 @@ void XML_response(AsyncWebServerRequest *request, char* dest)
if (request != nullptr) request->send(200, "text/xml", obuf); if (request != nullptr) request->send(200, "text/xml", obuf);
} }
//Deprecated, use of /json/state and presets recommended instead
void URL_response(AsyncWebServerRequest *request)
{
char sbuf[256];
char s2buf[100];
obuf = s2buf;
olen = 0;
char s[16];
oappend(SET_F("http://"));
IPAddress localIP = Network.localIP();
sprintf(s, "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]);
oappend(s);
oappend(SET_F("/win&A="));
oappendi(bri);
oappend(SET_F("&CL=h"));
for (int i = 0; i < 3; i++)
{
sprintf(s,"%02X", col[i]);
oappend(s);
}
oappend(SET_F("&C2=h"));
for (int i = 0; i < 3; i++)
{
sprintf(s,"%02X", colSec[i]);
oappend(s);
}
oappend(SET_F("&FX="));
oappendi(effectCurrent);
oappend(SET_F("&SX="));
oappendi(effectSpeed);
oappend(SET_F("&IX="));
oappendi(effectIntensity);
oappend(SET_F("&FP="));
oappendi(effectPalette);
obuf = sbuf;
olen = 0;
oappend(SET_F("<html><body><a href=\""));
oappend(s2buf);
oappend(SET_F("\" target=\"_blank\">"));
oappend(s2buf);
oappend(SET_F("</a></body></html>"));
if (request != nullptr) request->send(200, "text/html", obuf);
}
void extractPin(JsonObject &obj, const char *key) { void extractPin(JsonObject &obj, const char *key) {
if (obj[key].is<JsonArray>()) { if (obj[key].is<JsonArray>()) {
JsonArray pins = obj[key].as<JsonArray>(); JsonArray pins = obj[key].as<JsonArray>();
@ -285,7 +236,7 @@ void getSettingsJS(byte subPage, char* dest)
if (subPage <0 || subPage >10) return; if (subPage <0 || subPage >10) return;
if (subPage == 0) if (subPage == SUBPAGE_MENU)
{ {
#ifndef WLED_DISABLE_2D // include only if 2D is compiled in #ifndef WLED_DISABLE_2D // include only if 2D is compiled in
oappend(PSTR("gId('2dbtn').style.display='';")); oappend(PSTR("gId('2dbtn').style.display='';"));
@ -295,7 +246,7 @@ void getSettingsJS(byte subPage, char* dest)
#endif #endif
} }
if (subPage == 1) if (subPage == SUBPAGE_WIFI)
{ {
sappends('s',SET_F("CS"),clientSSID); sappends('s',SET_F("CS"),clientSSID);
@ -328,6 +279,14 @@ void getSettingsJS(byte subPage, char* dest)
sappend('v',SET_F("AC"),apChannel); sappend('v',SET_F("AC"),apChannel);
sappend('c',SET_F("WS"),noWifiSleep); sappend('c',SET_F("WS"),noWifiSleep);
#ifndef WLED_DISABLE_ESPNOW
sappend('c',SET_F("RE"),enable_espnow_remote);
sappends('s',SET_F("RMAC"),linked_remote);
#else
//hide remote settings if not compiled
oappend(SET_F("document.getElementById('remd').style.display='none';"));
#endif
#ifdef WLED_USE_ETHERNET #ifdef WLED_USE_ETHERNET
sappend('v',SET_F("ETH"),ethernetType); sappend('v',SET_F("ETH"),ethernetType);
#else #else
@ -360,9 +319,22 @@ void getSettingsJS(byte subPage, char* dest)
{ {
sappends('m',SET_F("(\"sip\")[1]"),(char*)F("Not active")); sappends('m',SET_F("(\"sip\")[1]"),(char*)F("Not active"));
} }
#ifndef WLED_DISABLE_ESPNOW
if (last_signal_src[0] != 0) //Have seen an ESP-NOW Remote
{
sappends('m',SET_F("(\"rlid\")[0]"),last_signal_src);
} else if (!enable_espnow_remote)
{
sappends('m',SET_F("(\"rlid\")[0]"),(char*)F("(Enable remote to listen)"));
} else
{
sappends('m',SET_F("(\"rlid\")[0]"),(char*)F("None"));
}
#endif
} }
if (subPage == 2) if (subPage == SUBPAGE_LEDS)
{ {
char nS[32]; char nS[32];
@ -493,7 +465,7 @@ void getSettingsJS(byte subPage, char* dest)
sappend('c',SET_F("MSO"),!irApplyToAllSelected); sappend('c',SET_F("MSO"),!irApplyToAllSelected);
} }
if (subPage == 3) if (subPage == SUBPAGE_UI)
{ {
sappends('s',SET_F("DS"),serverDescription); sappends('s',SET_F("DS"),serverDescription);
sappend('c',SET_F("ST"),syncToggleReceive); sappend('c',SET_F("ST"),syncToggleReceive);
@ -504,7 +476,7 @@ void getSettingsJS(byte subPage, char* dest)
#endif #endif
} }
if (subPage == 4) if (subPage == SUBPAGE_SYNC)
{ {
sappend('v',SET_F("UP"),udpPort); sappend('v',SET_F("UP"),udpPort);
sappend('v',SET_F("U2"),udpPort2); sappend('v',SET_F("U2"),udpPort2);
@ -597,7 +569,7 @@ void getSettingsJS(byte subPage, char* dest)
sappend('v',SET_F("BD"),serialBaud); sappend('v',SET_F("BD"),serialBaud);
} }
if (subPage == 5) if (subPage == SUBPAGE_TIME)
{ {
sappend('c',SET_F("NT"),ntpEnabled); sappend('c',SET_F("NT"),ntpEnabled);
sappends('s',SET_F("NS"),ntpServerName); sappends('s',SET_F("NS"),ntpServerName);
@ -661,7 +633,7 @@ void getSettingsJS(byte subPage, char* dest)
} }
} }
if (subPage == 6) if (subPage == SUBPAGE_SEC)
{ {
byte l = strlen(settingsPIN); byte l = strlen(settingsPIN);
char fpass[l+1]; //fill PIN field with 0000 char fpass[l+1]; //fill PIN field with 0000
@ -683,7 +655,7 @@ void getSettingsJS(byte subPage, char* dest)
} }
#ifdef WLED_ENABLE_DMX // include only if DMX is enabled #ifdef WLED_ENABLE_DMX // include only if DMX is enabled
if (subPage == 7) if (subPage == SUBPAGE_DMX)
{ {
sappend('v',SET_F("PU"),e131ProxyUniverse); sappend('v',SET_F("PU"),e131ProxyUniverse);
@ -710,7 +682,7 @@ void getSettingsJS(byte subPage, char* dest)
} }
#endif #endif
if (subPage == 8) //usermods if (subPage == SUBPAGE_UM) //usermods
{ {
appendGPIOinfo(); appendGPIOinfo();
oappend(SET_F("numM=")); oappend(SET_F("numM="));
@ -729,7 +701,7 @@ void getSettingsJS(byte subPage, char* dest)
usermods.appendConfigData(); usermods.appendConfigData();
} }
if (subPage == 9) // update if (subPage == SUBPAGE_UPDATE) // update
{ {
sappends('m',SET_F("(\"sip\")[0]"),(char*)F("WLED ")); sappends('m',SET_F("(\"sip\")[0]"),(char*)F("WLED "));
olen -= 2; //delete "; olen -= 2; //delete ";
@ -745,7 +717,7 @@ void getSettingsJS(byte subPage, char* dest)
oappend(SET_F(")\";")); oappend(SET_F(")\";"));
} }
if (subPage == 10) // 2D matrices if (subPage == SUBPAGE_2D) // 2D matrices
{ {
sappend('v',SET_F("SOMP"),strip.isMatrix); sappend('v',SET_F("SOMP"),strip.isMatrix);
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D