Squashed commit of the following:

Remove sync receive
Disallow 2D effects on non-2D segments
Optimisations
Sync clarification
AR palettes
Return of 2 audio simulations
Bugfix in sync #3344
    - remove excessive segments
    - ignore inactive segments if not syncing bounds
    - send UDP/WS on segment change
    - pop_back() when removing last segment
Add pairing support for ESP-NOW sync
Reduce string RAM footprint
UDP parse optimisation
Make WizMote work with sync.
ESP-NOW wireless sync POC.
    - caveat: devices have to be on the same channel
    - clashes with WizMote handling ATM
This commit is contained in:
Blaz Kristan 2023-09-10 18:52:14 +02:00
parent 60c47cfca1
commit cc68e6b6e6
35 changed files with 6185 additions and 5863 deletions

6
.vscode/tasks.json vendored
View File

@ -9,8 +9,8 @@
],
"dependsOrder": "sequence",
"problemMatcher": [
"$platformio",
],
"$platformio"
]
},
{
"type": "PlatformIO",
@ -18,7 +18,7 @@
"task": "Build",
"group": {
"kind": "build",
"isDefault": true,
"isDefault": true
},
"problemMatcher": [
"$platformio"

View File

@ -181,6 +181,9 @@ lib_deps =
IRremoteESP8266 @ 2.8.2
makuna/NeoPixelBus @ 2.7.5
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.7
# ESP-NOW library (includes mandatory QuickDebug library)
; gmag11/QuickESPNow @ 0.6.2
https://github.com/blazoncek/QuickESPNow.git#optional-debug
#For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line
#TFT_eSPI
#For compatible OLED display uncomment following

View File

@ -51,6 +51,8 @@
#define PLOT_PRINTF(x...)
#endif
#define MAX_PALETTES 3
// use audio source class (ESP32 specific)
#include "audio_source.h"
constexpr i2s_port_t I2S_PORT = I2S_NUM_0; // I2S port to use (do not change !)
@ -614,6 +616,8 @@ class AudioReactive : public Usermod {
// set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer)
bool enabled = false;
bool initDone = false;
bool addPalettes = false;
CRGBPalette16 *palette[MAX_PALETTES];
// variables for UDP sound sync
WiFiUDP fftUdp; // UDP object for sound sync (from WiFi UDP, not Async UDP!)
@ -652,10 +656,15 @@ class AudioReactive : public Usermod {
static const char _inputLvl[];
static const char _analogmic[];
static const char _digitalmic[];
static const char _addPalettes[];
static const char UDP_SYNC_HEADER[];
static const char UDP_SYNC_HEADER_v1[];
// private methods
void removeAudioPalettes(void);
void createAudioPalettes(void);
CRGB getCRGBForBand(int x, int pal);
void fillAudioPalette(int pal);
////////////////////
// Debug support //
@ -1196,6 +1205,7 @@ class AudioReactive : public Usermod {
}
if (enabled) connectUDPSoundSync();
if (enabled && addPalettes) createAudioPalettes();
initDone = true;
}
@ -1358,6 +1368,7 @@ class AudioReactive : public Usermod {
lastTime = millis();
}
for (int i=0; i<MAX_PALETTES; i++) fillAudioPalette(i);
}
@ -1610,6 +1621,11 @@ class AudioReactive : public Usermod {
if (usermod[FPSTR(_enabled)].is<bool>()) {
enabled = usermod[FPSTR(_enabled)].as<bool>();
if (prevEnabled != enabled) onUpdateBegin(!enabled);
if (addPalettes) {
// add/remove custom/audioreactive palettes
if (prevEnabled && !enabled) removeAudioPalettes();
if (!prevEnabled && enabled) createAudioPalettes();
}
}
if (usermod[FPSTR(_inputLvl)].is<int>()) {
inputLevel = min(255,max(0,usermod[FPSTR(_inputLvl)].as<int>()));
@ -1657,6 +1673,7 @@ class AudioReactive : public Usermod {
{
JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled;
top[FPSTR(_addPalettes)] = addPalettes;
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
JsonObject amic = top.createNestedObject(FPSTR(_analogmic));
@ -1709,8 +1726,11 @@ class AudioReactive : public Usermod {
{
JsonObject top = root[FPSTR(_name)];
bool configComplete = !top.isNull();
bool oldEnabled = enabled;
bool oldAddPalettes = addPalettes;
configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled);
configComplete &= getJsonValue(top[FPSTR(_addPalettes)], addPalettes);
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
configComplete &= getJsonValue(top[FPSTR(_analogmic)]["pin"], audioPin);
@ -1744,6 +1764,11 @@ class AudioReactive : public Usermod {
configComplete &= getJsonValue(top["sync"][F("port")], audioSyncPort);
configComplete &= getJsonValue(top["sync"][F("mode")], audioSyncEnabled);
if (initDone) {
// add/remove custom/audioreactive palettes
if ((oldAddPalettes && !addPalettes) || (oldAddPalettes && !enabled)) removeAudioPalettes();
if ((addPalettes && !oldAddPalettes && enabled) || (addPalettes && !oldEnabled && enabled)) createAudioPalettes();
} // else setup() will create palettes
return configComplete;
}
@ -1819,6 +1844,84 @@ class AudioReactive : public Usermod {
}
};
void AudioReactive::removeAudioPalettes(void) {
for (int i=MAX_PALETTES-1; i>=0; i--) {
if (palette[i]) strip.customPalettes.pop_back();
palette[i] = nullptr;
}
}
void AudioReactive::createAudioPalettes(void) {
for (int i=0; i<MAX_PALETTES; i++)
if (strip.customPalettes.size() < 10) {
strip.customPalettes.push_back(CRGBPalette16(CRGB(BLACK)));
palette[i] = &strip.customPalettes.back();
} else {
palette[i] = nullptr;
}
}
// credit @netmindz ar palette, adapted for usermod @blazoncek
CRGB AudioReactive::getCRGBForBand(int x, int pal) {
CRGB value;
CHSV hsv;
int b;
switch (pal) {
case 2:
b = map(x, 0, 255, 0, NUM_GEQ_CHANNELS/2); // convert palette position to lower half of freq band
hsv = CHSV(fftResult[b], 255, x);
hsv2rgb_rainbow(hsv, value); // convert to R,G,B
break;
case 1:
b = map(x, 1, 255, 0, 10); // convert palette position to lower half of freq band
hsv = CHSV(fftResult[b], 255, map(fftResult[b], 0, 255, 30, 255)); // pick hue
hsv2rgb_rainbow(hsv, value); // convert to R,G,B
break;
default:
if (x == 1) {
value = CRGB(fftResult[10]/2, fftResult[4]/2, fftResult[0]/2);
} else if(x == 255) {
value = CRGB(fftResult[10]/2, fftResult[0]/2, fftResult[4]/2);
} else {
value = CRGB(fftResult[0]/2, fftResult[4]/2, fftResult[10]/2);
}
break;
}
return value;
}
void AudioReactive::fillAudioPalette(int pal) {
if (pal>=MAX_PALETTES || !palette[pal]) return; // palette does not exist
uint8_t tcp[16]; // Needs to be 4 times however many colors are being used.
// 3 colors = 12, 4 colors = 16, etc.
tcp[0] = 0; // anchor of first color - must be zero
tcp[1] = 0;
tcp[2] = 0;
tcp[3] = 0;
CRGB rgb = getCRGBForBand(1, pal);
tcp[4] = 1; // anchor of first color
tcp[5] = rgb.r;
tcp[6] = rgb.g;
tcp[7] = rgb.b;
rgb = getCRGBForBand(128, pal);
tcp[8] = 128;
tcp[9] = rgb.r;
tcp[10] = rgb.g;
tcp[11] = rgb.b;
rgb = getCRGBForBand(255, pal);
tcp[12] = 255; // anchor of last color - must be 255
tcp[13] = rgb.r;
tcp[14] = rgb.g;
tcp[15] = rgb.b;
palette[pal]->loadDynamicGradientPalette(tcp);
}
// strings to reduce flash memory usage (used more than twice)
const char AudioReactive::_name[] PROGMEM = "AudioReactive";
const char AudioReactive::_enabled[] PROGMEM = "enabled";
@ -1827,5 +1930,6 @@ const char AudioReactive::_inputLvl[] PROGMEM = "inputLevel";
const char AudioReactive::_analogmic[] PROGMEM = "analogmic";
#endif
const char AudioReactive::_digitalmic[] PROGMEM = "digitalmic";
const char AudioReactive::_addPalettes[] PROGMEM = "add-palettes";
const char AudioReactive::UDP_SYNC_HEADER[] PROGMEM = "00002"; // new sync header version, as format no longer compatible with previous structure
const char AudioReactive::UDP_SYNC_HEADER_v1[] PROGMEM = "00001"; // old sync header version - need to add backwards-compatibility feature

View File

@ -177,11 +177,11 @@ uint16_t color_wipe(bool rev, bool useRandomColors) {
SEGENV.step = 3;
}
if (SEGENV.step == 1) { //if flag set, change to new random color
SEGENV.aux1 = SEGMENT.get_random_wheel_index(SEGENV.aux0);
SEGENV.aux1 = get_random_wheel_index(SEGENV.aux0);
SEGENV.step = 2;
}
if (SEGENV.step == 3) {
SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux1);
SEGENV.aux0 = get_random_wheel_index(SEGENV.aux1);
SEGENV.step = 0;
}
}
@ -271,7 +271,7 @@ uint16_t mode_random_color(void) {
if (it != SEGENV.step) //new color
{
SEGENV.aux1 = SEGENV.aux0;
SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0); //aux0 will store our random color wheel index
SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0); //aux0 will store our random color wheel index
SEGENV.step = it;
}
@ -587,7 +587,7 @@ uint16_t mode_twinkle(void) {
uint16_t PRNG16 = SEGENV.aux1;
for (uint16_t i = 0; i < SEGENV.aux0; i++)
for (unsigned i = 0; i < SEGENV.aux0; i++)
{
PRNG16 = (uint16_t)(PRNG16 * 2053) + 13849; // next 'random' number
uint32_t p = (uint32_t)SEGLEN * (uint32_t)PRNG16;
@ -681,7 +681,7 @@ static const char _data_FX_MODE_SPARKLE[] PROGMEM = "Sparkle@!,,,,,,Overlay;!,!;
* Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
*/
uint16_t mode_flash_sparkle(void) {
if (!SEGMENT.check2) for(uint16_t i = 0; i < SEGLEN; i++) {
if (!SEGMENT.check2) for (int i = 0; i < SEGLEN; i++) {
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
}
@ -816,7 +816,7 @@ uint16_t chase(uint32_t color1, uint32_t color2, uint32_t color3, bool do_palett
if (a < SEGENV.step) //we hit the start again, choose new color for Chase random
{
SEGENV.aux1 = SEGENV.aux0; //store previous random color
SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0);
SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0);
}
color1 = SEGMENT.color_wheel(SEGENV.aux0);
}
@ -1056,7 +1056,7 @@ uint16_t mode_chase_flash_random(void) {
SEGENV.aux1 = (SEGENV.aux1 + 1) % SEGLEN;
if (SEGENV.aux1 == 0) {
SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0);
SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0);
}
}
return delay;
@ -2182,7 +2182,7 @@ uint16_t mode_colortwinkle() {
CRGB fastled_col, prev;
fract8 fadeUpAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>2) : 68-strip.getBrightness();
fract8 fadeDownAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>3) : 68-strip.getBrightness();
for (uint16_t i = 0; i < SEGLEN; i++) {
for (int i = 0; i < SEGLEN; i++) {
fastled_col = SEGMENT.getPixelColor(i);
prev = fastled_col;
uint16_t index = i >> 3;
@ -2209,9 +2209,9 @@ uint16_t mode_colortwinkle() {
}
}
for (uint16_t j = 0; j <= SEGLEN / 50; j++) {
for (unsigned j = 0; j <= SEGLEN / 50; j++) {
if (random8() <= SEGMENT.intensity) {
for (uint8_t times = 0; times < 5; times++) { //attempt to spawn a new pixel 5 times
for (unsigned times = 0; times < 5; times++) { //attempt to spawn a new pixel 5 times
int i = random16(SEGLEN);
if (SEGMENT.getPixelColor(i) == 0) {
fastled_col = ColorFromPalette(SEGPALETTE, random8(), 64, NOBLEND);
@ -4657,7 +4657,7 @@ static const char _data_FX_MODE_FLOWSTRIPE[] PROGMEM = "Flow Stripe@Hue speed,Ef
// Black hole
uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulmatelights.com/gallery/1012 , Modified by: Andrew Tuline
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -4691,7 +4691,7 @@ static const char _data_FX_MODE_2DBLACKHOLE[] PROGMEM = "Black Hole@Fade rate,Ou
// 2D Colored Bursts //
////////////////////////////
uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.soulmatelights.com/gallery/819-colored-bursts , modified by: Andrew Tuline
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -4743,7 +4743,7 @@ static const char _data_FX_MODE_2DCOLOREDBURSTS[] PROGMEM = "Colored Bursts@Spee
// 2D DNA //
/////////////////////
uint16_t mode_2Ddna(void) { // dna originally by by ldirko at https://pastebin.com/pCkkkzcs. Updated by Preyy. WLED conversion by Andrew Tuline.
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -4764,7 +4764,7 @@ static const char _data_FX_MODE_2DDNA[] PROGMEM = "DNA@Scroll speed,Blur;;!;2";
// 2D DNA Spiral //
/////////////////////////
uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulmatelights.com/gallery/810 , modified by: Andrew Tuline
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -4809,7 +4809,7 @@ static const char _data_FX_MODE_2DDNASPIRAL[] PROGMEM = "DNA Spiral@Scroll speed
// 2D Drift //
/////////////////////////
uint16_t mode_2DDrift() { // By: Stepko https://editor.soulmatelights.com/gallery/884-drift , Modified by: Andrew Tuline
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -4835,7 +4835,7 @@ static const char _data_FX_MODE_2DDRIFT[] PROGMEM = "Drift@Rotation speed,Blur a
// 2D Firenoise //
//////////////////////////
uint16_t mode_2Dfirenoise(void) { // firenoise2d. By Andrew Tuline. Yet another short routine.
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -4869,7 +4869,7 @@ static const char _data_FX_MODE_2DFIRENOISE[] PROGMEM = "Firenoise@X scale,Y sca
// 2D Frizzles //
//////////////////////////////
uint16_t mode_2DFrizzles(void) { // By: Stepko https://editor.soulmatelights.com/gallery/640-color-frizzles , Modified by: Andrew Tuline
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -4896,7 +4896,7 @@ typedef struct ColorCount {
} colorCount;
uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https://natureofcode.com/book/chapter-7-cellular-automata/ and https://github.com/DougHaber/nlife-color
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -5002,7 +5002,7 @@ static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!;!,!;!;2
// 2D Hiphotic //
/////////////////////////
uint16_t mode_2DHiphotic() { // By: ldirko https://editor.soulmatelights.com/gallery/810 , Modified by: Andrew Tuline
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -5034,7 +5034,7 @@ typedef struct Julia {
} julia;
uint16_t mode_2DJulia(void) { // An animated Julia set by Andrew Tuline.
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -5140,7 +5140,7 @@ static const char _data_FX_MODE_2DJULIA[] PROGMEM = "Julia@,Max iterations per p
// 2D Lissajous //
//////////////////////////////
uint16_t mode_2DLissajous(void) { // By: Andrew Tuline
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -5168,7 +5168,7 @@ static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,F
// 2D Matrix //
///////////////////////
uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams. Adapted by Andrew Tuline & improved by merkisoft and ewowi.
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -5230,7 +5230,7 @@ static const char _data_FX_MODE_2DMATRIX[] PROGMEM = "Matrix@!,Spawning rate,Tra
// 2D Metaballs //
/////////////////////////
uint16_t mode_2Dmetaballs(void) { // Metaballs by Stefan Petrick. Cannot have one of the dimensions be 2 or less. Adapted by Andrew Tuline.
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -5289,7 +5289,7 @@ static const char _data_FX_MODE_2DMETABALLS[] PROGMEM = "Metaballs@!;;!;2";
// 2D Noise //
//////////////////////
uint16_t mode_2Dnoise(void) { // By Andrew Tuline
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -5312,7 +5312,7 @@ static const char _data_FX_MODE_2DNOISE[] PROGMEM = "Noise2D@!,Scale;;!;2";
// 2D Plasma Ball //
//////////////////////////////
uint16_t mode_2DPlasmaball(void) { // By: Stepko https://editor.soulmatelights.com/gallery/659-plasm-ball , Modified by: Andrew Tuline
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -5352,7 +5352,7 @@ static const char _data_FX_MODE_2DPLASMABALL[] PROGMEM = "Plasma Ball@Speed,,Fad
// return (out_max - out_min) * (x - in_min) / (in_max - in_min) + out_min;
//}
uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https://editor.soulmatelights.com/gallery/762-polar-lights , Modified by: Andrew Tuline
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -5403,7 +5403,7 @@ static const char _data_FX_MODE_2DPOLARLIGHTS[] PROGMEM = "Polar Lights@!,Scale;
// 2D Pulser //
/////////////////////////
uint16_t mode_2DPulser(void) { // By: ldirko https://editor.soulmatelights.com/gallery/878-pulse-test , modifed by: Andrew Tuline
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -5425,7 +5425,7 @@ static const char _data_FX_MODE_2DPULSER[] PROGMEM = "Pulser@!,Blur;;!;2";
// 2D Sindots //
/////////////////////////
uint16_t mode_2DSindots(void) { // By: ldirko https://editor.soulmatelights.com/gallery/597-sin-dots , modified by: Andrew Tuline
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -5456,7 +5456,7 @@ static const char _data_FX_MODE_2DSINDOTS[] PROGMEM = "Sindots@!,Dot distance,Fa
// custom3 affects the blur amount.
uint16_t mode_2Dsquaredswirl(void) { // By: Mark Kriegsman. https://gist.github.com/kriegsman/368b316c55221134b160
// Modifed by: Andrew Tuline
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -5490,7 +5490,7 @@ static const char _data_FX_MODE_2DSQUAREDSWIRL[] PROGMEM = "Squared Swirl@,,,,Bl
// 2D Sun Radiation //
//////////////////////////////
uint16_t mode_2DSunradiation(void) { // By: ldirko https://editor.soulmatelights.com/gallery/599-sun-radiation , modified by: Andrew Tuline
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -5540,7 +5540,7 @@ static const char _data_FX_MODE_2DSUNRADIATION[] PROGMEM = "Sun Radiation@Varian
// 2D Tartan //
/////////////////////////
uint16_t mode_2Dtartan(void) { // By: Elliott Kember https://editor.soulmatelights.com/gallery/3-tartan , Modified by: Andrew Tuline
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -5579,7 +5579,7 @@ static const char _data_FX_MODE_2DTARTAN[] PROGMEM = "Tartan@X scale,Y scale,,,S
// 2D spaceships //
/////////////////////////
uint16_t mode_2Dspaceships(void) { //// Space ships by stepko (c)05.02.21 [https://editor.soulmatelights.com/gallery/639-space-ships], adapted by Blaz Kristan (AKA blazoncek)
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -5622,7 +5622,7 @@ static const char _data_FX_MODE_2DSPACESHIPS[] PROGMEM = "Spaceships@!,Blur;;!;2
//// Crazy bees by stepko (c)12.02.21 [https://editor.soulmatelights.com/gallery/651-crazy-bees], adapted by Blaz Kristan (AKA blazoncek)
#define MAX_BEES 5
uint16_t mode_2Dcrazybees(void) {
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -5694,7 +5694,7 @@ static const char _data_FX_MODE_2DCRAZYBEES[] PROGMEM = "Crazy Bees@!,Blur;;;2";
//// Ghost Rider by stepko (c)2021 [https://editor.soulmatelights.com/gallery/716-ghost-rider], adapted by Blaz Kristan (AKA blazoncek)
#define LIGHTERS_AM 64 // max lighters (adequate for 32x32 matrix)
uint16_t mode_2Dghostrider(void) {
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -5784,7 +5784,7 @@ static const char _data_FX_MODE_2DGHOSTRIDER[] PROGMEM = "Ghost Rider@Fade rate,
//// Floating Blobs by stepko (c)2021 [https://editor.soulmatelights.com/gallery/573-blobs], adapted by Blaz Kristan (AKA blazoncek)
#define MAX_BLOBS 8
uint16_t mode_2Dfloatingblobs(void) {
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -5882,7 +5882,7 @@ static const char _data_FX_MODE_2DBLOBS[] PROGMEM = "Blobs@!,# blobs,Blur,Trail;
// 2D Scrolling text //
////////////////////////////
uint16_t mode_2Dscrollingtext(void) {
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -5982,7 +5982,7 @@ static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Off
////////////////////////////
//// Drift Rose by stepko (c)2021 [https://editor.soulmatelights.com/gallery/1369-drift-rose-pattern], adapted by Blaz Kristan (AKA blazoncek)
uint16_t mode_2Ddriftrose(void) {
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -6136,7 +6136,7 @@ static const char _data_FX_MODE_RIPPLEPEAK[] PROGMEM = "Ripple Peak@Fade rate,Ma
/////////////////////////
// By: Mark Kriegsman https://gist.github.com/kriegsman/5adca44e14ad025e6d3b , modified by Andrew Tuline
uint16_t mode_2DSwirl(void) {
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -6180,7 +6180,7 @@ static const char _data_FX_MODE_2DSWIRL[] PROGMEM = "Swirl@!,Sensitivity,Blur;,B
/////////////////////////
// By: Stepko, https://editor.soulmatelights.com/gallery/652-wave , modified by Andrew Tuline
uint16_t mode_2DWaverly(void) {
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -7167,7 +7167,7 @@ static const char _data_FX_MODE_WATERFALL[] PROGMEM = "Waterfall@!,Adjust color,
// ** 2D GEQ //
/////////////////////////
uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma.
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const int NUM_BANDS = map(SEGMENT.custom1, 0, 255, 1, 16);
const uint16_t cols = SEGMENT.virtualWidth();
@ -7225,7 +7225,7 @@ static const char _data_FX_MODE_2DGEQ[] PROGMEM = "GEQ@Fade speed,Ripple decay,#
// ** 2D Funky plank //
/////////////////////////
uint16_t mode_2DFunkyPlank(void) { // Written by ??? Adapted by Will Tatam.
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -7317,7 +7317,7 @@ static uint8_t akemi[] PROGMEM = {
};
uint16_t mode_2DAkemi(void) {
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -7385,7 +7385,7 @@ static const char _data_FX_MODE_2DAKEMI[] PROGMEM = "Akemi@Color speed,Dance;Hea
// https://editor.soulmatelights.com/gallery/1089-distorsion-waves
// adapted for WLED by @blazoncek
uint16_t mode_2Ddistortionwaves() {
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -7440,7 +7440,7 @@ static const char _data_FX_MODE_2DDISTORTIONWAVES[] PROGMEM = "Distortion Waves@
//Idea from https://www.youtube.com/watch?v=DiHBgITrZck&ab_channel=StefanPetrick
// adapted for WLED by @blazoncek
uint16_t mode_2Dsoap() {
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -7552,7 +7552,7 @@ static const char _data_FX_MODE_2DSOAP[] PROGMEM = "Soap@!,Smoothness;;!;2";
//Stepko and Sutaburosu
// adapted for WLED by @blazoncek
uint16_t mode_2Doctopus() {
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
@ -7608,7 +7608,7 @@ static const char _data_FX_MODE_2DOCTOPUS[] PROGMEM = "Octopus@!,,Offset X,Offse
//@Stepko (https://editor.soulmatelights.com/gallery/1704-wavingcells)
// adapted for WLED by @blazoncek
uint16_t mode_2Dwavingcell() {
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();

View File

@ -109,20 +109,15 @@
#define PINK (uint32_t)0xFF1493
#define ULTRAWHITE (uint32_t)0xFFFFFFFF
#define DARKSLATEGRAY (uint32_t)0x2F4F4F
#define DARKSLATEGREY (uint32_t)0x2F4F4F
#define DARKSLATEGREY DARKSLATEGRAY
// options
// bit 7: segment is in transition mode
// bits 4-6: TBD
// bit 3: mirror effect within segment
// bit 2: segment is on
// bit 1: reverse segment
// bit 0: segment is selected
// segment options
#define NO_OPTIONS (uint16_t)0x0000
#define TRANSPOSED (uint16_t)0x0400 // rotated 90deg & reversed
#define REVERSE_Y_2D (uint16_t)0x0200
#define MIRROR_Y_2D (uint16_t)0x0100
#define TRANSITIONAL (uint16_t)0x0080
#define TRANSPOSED (uint16_t)0x0100 // rotated 90deg & reversed
#define MIRROR_Y_2D (uint16_t)0x0080
#define REVERSE_Y_2D (uint16_t)0x0040
#define RESET_REQ (uint16_t)0x0020
#define FROZEN (uint16_t)0x0010
#define MIRROR (uint16_t)0x0008
#define SEGMENT_ON (uint16_t)0x0004
#define REVERSE (uint16_t)0x0002
@ -348,12 +343,11 @@ typedef struct Segment {
bool mirror : 1; // 3 : mirrored
bool freeze : 1; // 4 : paused/frozen
bool reset : 1; // 5 : indicates that Segment runtime requires reset
bool transitional: 1; // 6 : transitional (there is transition occuring)
bool reverse_y : 1; // 7 : reversed Y (2D)
bool mirror_y : 1; // 8 : mirrored Y (2D)
bool transpose : 1; // 9 : transposed (2D, swapped X & Y)
uint8_t map1D2D : 3; // 10-12 : mapping for 1D effect on 2D (0-use as strip, 1-expand vertically, 2-circular/arc, 3-rectangular/corner, ...)
uint8_t soundSim : 1; // 13 : 0-1 sound simulation types ("soft" & "hard" or "on"/"off")
bool reverse_y : 1; // 6 : reversed Y (2D)
bool mirror_y : 1; // 7 : mirrored Y (2D)
bool transpose : 1; // 8 : transposed (2D, swapped X & Y)
uint8_t map1D2D : 3; // 9-11 : mapping for 1D effect on 2D (0-use as strip, 1-expand vertically, 2-circular/arc, 3-rectangular/corner, ...)
uint8_t soundSim : 2; // 12-13 : 0-3 sound simulation types ("soft" & "hard" or "on"/"off")
uint8_t set : 2; // 14-15 : 0-3 UI segment sets/groups
};
};
@ -519,6 +513,7 @@ typedef struct Segment {
inline bool getOption(uint8_t n) const { return ((options >> n) & 0x01); }
inline bool isSelected(void) const { return selected; }
inline bool isInTransition(void) const { return _t != nullptr; }
inline bool isActive(void) const { return stop > start; }
inline bool is2D(void) const { return (width()>1 && height()>1); }
inline bool hasRGB(void) const { return _isRGB; }
@ -569,15 +564,16 @@ typedef struct Segment {
void restoreSegenv(tmpsegd_t &tmpSegD);
#endif
uint16_t progress(void); //transition progression between 0-65535
uint8_t currentBri(uint8_t briNew, bool useCct = false);
uint8_t currentMode(uint8_t modeNew);
uint32_t currentColor(uint8_t slot, uint32_t colorNew);
uint8_t currentBri(bool useCct = false);
uint8_t currentMode(void);
uint32_t currentColor(uint8_t slot);
CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal);
CRGBPalette16 &currentPalette(CRGBPalette16 &tgt, uint8_t paletteID);
// 1D strip
uint16_t virtualLength(void) const;
void setPixelColor(int n, uint32_t c); // set relative pixel within segment with color
void setPixelColor(unsigned n, uint32_t c) { setPixelColor(int(n), c); }
void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } // automatically inline
void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } // automatically inline
void setPixelColor(float i, uint32_t c, bool aa = true);
@ -595,7 +591,6 @@ typedef struct Segment {
void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(n, RGBW32(r,g,b,w), fast); } // automatically inline
void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); } // automatically inline
void fadePixelColor(uint16_t n, uint8_t fade);
uint8_t get_random_wheel_index(uint8_t pos);
uint32_t color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255);
uint32_t color_wheel(uint8_t pos);
@ -606,6 +601,7 @@ typedef struct Segment {
#ifndef WLED_DISABLE_2D
uint16_t XY(uint16_t x, uint16_t y); // support function to get relative index within segment
void setPixelColorXY(int x, int y, uint32_t c); // set relative pixel within segment with color
void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); }
void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline
void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } // automatically inline
void setPixelColorXY(float x, float y, uint32_t c, bool aa = true);

View File

@ -134,7 +134,7 @@ void WS2812FX::setUpMatrix() {
#ifdef WLED_DEBUG
DEBUG_PRINT(F("Matrix ledmap:"));
for (uint16_t i=0; i<customMappingSize; i++) {
for (unsigned i=0; i<customMappingSize; i++) {
if (!(i%Segment::maxWidth)) DEBUG_PRINTLN();
DEBUG_PRINTF("%4d,", customMappingTable[i]);
}
@ -199,7 +199,7 @@ void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col)
if (!isActive()) return; // not active
if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit
uint8_t _bri_t = currentBri(on ? opacity : 0);
uint8_t _bri_t = currentBri();
if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t);
byte g = scale8(G(col), _bri_t);
@ -310,32 +310,17 @@ void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t
void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) {
if (!isActive()) return; // not active
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);
uint8_t r = R(col);
uint8_t g = G(col);
uint8_t b = B(col);
uint8_t w = W(col);
if (fast) {
r = qadd8(r, R(color));
g = qadd8(g, G(color));
b = qadd8(b, B(color));
w = qadd8(w, W(color));
col = RGBW32(r,g,b,w);
} else {
col = color_add(col, color);
}
setPixelColorXY(x, y, col);
setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color, fast));
}
void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) {
if (!isActive()) return; // not active
CRGB pix = CRGB(getPixelColorXY(x,y)).nscale8_video(fade);
setPixelColorXY(x, y, pix);
setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), fade, true));
}
// blurRow: perform a blur on a row of a rectangular matrix
void Segment::blurRow(uint16_t row, fract8 blur_amount) {
if (!isActive()) return; // not active
if (!isActive() || blur_amount == 0) return; // not active
const uint_fast16_t cols = virtualWidth();
const uint_fast16_t rows = virtualHeight();
@ -344,7 +329,7 @@ void Segment::blurRow(uint16_t row, fract8 blur_amount) {
uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black;
for (uint_fast16_t x = 0; x < cols; x++) {
for (unsigned x = 0; x < cols; x++) {
CRGB cur = getPixelColorXY(x, row);
CRGB before = cur; // remember color before blur
CRGB part = cur;
@ -363,7 +348,7 @@ void Segment::blurRow(uint16_t row, fract8 blur_amount) {
// blurCol: perform a blur on a column of a rectangular matrix
void Segment::blurCol(uint16_t col, fract8 blur_amount) {
if (!isActive()) return; // not active
if (!isActive() || blur_amount == 0) return; // not active
const uint_fast16_t cols = virtualWidth();
const uint_fast16_t rows = virtualHeight();
@ -372,7 +357,7 @@ void Segment::blurCol(uint16_t col, fract8 blur_amount) {
uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black;
for (uint_fast16_t y = 0; y < rows; y++) {
for (unsigned y = 0; y < rows; y++) {
CRGB cur = getPixelColorXY(col, y);
CRGB part = cur;
CRGB before = cur; // remember color before blur
@ -391,7 +376,7 @@ void Segment::blurCol(uint16_t col, fract8 blur_amount) {
// 1D Box blur (with added weight - blur_amount: [0=no blur, 255=max blur])
void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
if (!isActive()) return; // not active
if (!isActive() || blur_amount == 0) return; // not active
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
const uint16_t dim1 = vertical ? rows : cols;
@ -401,7 +386,7 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
const float keep = 3.f - 2.f*seep;
// 1D box blur
CRGB tmp[dim1];
for (uint16_t j = 0; j < dim1; j++) {
for (int j = 0; j < dim1; j++) {
uint16_t x = vertical ? i : j;
uint16_t y = vertical ? j : i;
int16_t xp = vertical ? x : x-1; // "signed" to prevent underflow
@ -417,7 +402,7 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
b = (curr.b*keep + (prev.b + next.b)*seep) / 3;
tmp[j] = CRGB(r,g,b);
}
for (uint16_t j = 0; j < dim1; j++) {
for (int j = 0; j < dim1; j++) {
uint16_t x = vertical ? i : j;
uint16_t y = vertical ? j : i;
setPixelColorXY(x, y, tmp[j]);
@ -440,7 +425,7 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
void Segment::blur1d(fract8 blur_amount) {
const uint16_t rows = virtualHeight();
for (uint16_t y = 0; y < rows; y++) blurRow(y, blur_amount);
for (unsigned y = 0; y < rows; y++) blurRow(y, blur_amount);
}
void Segment::moveX(int8_t delta, bool wrap) {
@ -498,7 +483,7 @@ void Segment::move(uint8_t dir, uint8_t delta, bool wrap) {
}
void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
if (!isActive()) return; // not active
if (!isActive() || radius == 0) return; // not active
// Bresenhams Algorithm
int d = 3 - (2*radius);
int y = radius, x = 0;
@ -523,7 +508,7 @@ void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
// by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs
void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
if (!isActive()) return; // not active
if (!isActive() || radius == 0) return; // not active
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
for (int16_t y = -radius; y <= radius; y++) {
@ -540,7 +525,7 @@ void Segment::nscale8(uint8_t scale) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
setPixelColorXY(x, y, CRGB(getPixelColorXY(x, y)).nscale8(scale));
}
}

View File

@ -89,11 +89,10 @@ bool Segment::_modeBlend = false;
Segment::Segment(const Segment &orig) {
//DEBUG_PRINTF("-- Copy segment constructor: %p -> %p\n", &orig, this);
memcpy((void*)this, (void*)&orig, sizeof(Segment));
transitional = false; // copied segment cannot be in transition
name = nullptr;
data = nullptr;
_dataLen = 0;
_t = nullptr;
_t = nullptr; // copied segment cannot be in transition
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._t) { _t = new Transition(orig._t->_dur); }
@ -103,11 +102,10 @@ Segment::Segment(const Segment &orig) {
Segment::Segment(Segment &&orig) noexcept {
//DEBUG_PRINTF("-- Move segment constructor: %p -> %p\n", &orig, this);
memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.transitional = false; // old segment cannot be in transition any more
orig.name = nullptr;
orig.data = nullptr;
orig._dataLen = 0;
orig._t = nullptr;
orig._t = nullptr; // old segment cannot be in transition any more
}
// copy assignment
@ -115,7 +113,6 @@ Segment& Segment::operator= (const Segment &orig) {
//DEBUG_PRINTF("-- Copying segment: %p -> %p\n", &orig, this);
if (this != &orig) {
// clean destination
transitional = false; // copied segment cannot be in transition
if (name) delete[] name;
if (_t) {
#ifndef WLED_DISABLE_MODE_BLEND
@ -126,12 +123,11 @@ Segment& Segment::operator= (const Segment &orig) {
deallocateData();
// copy source
memcpy((void*)this, (void*)&orig, sizeof(Segment));
transitional = false;
// erase pointers to allocated data
name = nullptr;
data = nullptr;
_dataLen = 0;
_t = nullptr;
_t = nullptr; // copied segment cannot be in transition
// copy source data
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); }
@ -144,7 +140,6 @@ Segment& Segment::operator= (const Segment &orig) {
Segment& Segment::operator= (Segment &&orig) noexcept {
//DEBUG_PRINTF("-- Moving segment: %p -> %p\n", &orig, this);
if (this != &orig) {
transitional = false; // just temporary
if (name) { delete[] name; name = nullptr; } // free old name
deallocateData(); // free old runtime data
if (_t) {
@ -155,11 +150,10 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
_t = nullptr;
}
memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.transitional = false; // old segment cannot be in transition
orig.name = nullptr;
orig.data = nullptr;
orig._dataLen = 0;
orig._t = nullptr;
orig._t = nullptr; // old segment cannot be in transition
}
return *this;
}
@ -237,7 +231,7 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
switch (pal) {
case 0: //default palette. Exceptions for specific effects above
targetPalette = PartyColors_p; break;
case 1: {//periodically replace palette with a random one. Transition palette change in 500ms
case 1: {//periodically replace palette with a random one
unsigned long timeSinceLastChange = millis() - _lastPaletteChange;
if (timeSinceLastChange > randomPaletteChangeTime * 1000U) {
_randomPalette = _newRandomPalette;
@ -301,26 +295,23 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
}
void Segment::startTransition(uint16_t dur) {
if (!dur) {
if (_t) _t->_dur = dur; // this will stop transition in next handleTransisiton()
else transitional = false;
if (dur == 0) {
if (isInTransition()) _t->_dur = dur; // this will stop transition in next handleTransisiton()
return;
}
if (transitional && _t) return; // already in transition no need to store anything
if (isInTransition()) return; // already in transition no need to store anything
// starting a transition has to occur before change so we get current values 1st
_t = new Transition(dur); // no previous transition running
if (!_t) return; // failed to allocate data
//DEBUG_PRINTF("-- Started transition: %p\n", this);
CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette);
_t->_palT = _palT;
loadPalette(_t->_palT, palette);
_t->_briT = on ? opacity : 0;
_t->_cctT = cct;
#ifndef WLED_DISABLE_MODE_BLEND
swapSegenv(_t->_segT);
_t->_modeT = mode;
_t->_segT._optionsT |= 0b0000000001000000; // mark old segment transitional
_t->_segT._dataLenT = 0;
_t->_segT._dataT = nullptr;
if (_dataLen > 0 && data) {
@ -334,14 +325,11 @@ void Segment::startTransition(uint16_t dur) {
#else
for (size_t i=0; i<NUM_COLORS; i++) _t->_colorT[i] = colors[i];
#endif
transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true);
}
void Segment::stopTransition() {
if (!transitional) return;
transitional = false; // finish transitioning segment
//DEBUG_PRINTF("-- Stopping transition: %p\n", this);
if (_t) {
if (isInTransition()) {
#ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT && _t->_segT._dataLenT > 0) {
//DEBUG_PRINTF("-- Released duplicate data (%d): %p\n", _t->_segT._dataLenT, _t->_segT._dataT);
@ -356,14 +344,13 @@ void Segment::stopTransition() {
}
void Segment::handleTransition() {
if (!transitional) return;
uint16_t _progress = progress();
if (_progress == 0xFFFFU) stopTransition();
}
// transition progression between 0-65535
uint16_t Segment::progress() {
if (transitional && _t) {
if (isInTransition()) {
unsigned long timeNow = millis();
if (_t->_dur > 0 && timeNow - _t->_start < _t->_dur) return (timeNow - _t->_start) * 0xFFFFU / _t->_dur;
}
@ -443,39 +430,40 @@ void Segment::restoreSegenv(tmpsegd_t &tmpSeg) {
}
#endif
uint8_t Segment::currentBri(uint8_t briNew, bool useCct) {
uint8_t Segment::currentBri(bool useCct) {
uint32_t prog = progress();
if (prog < 0xFFFFU) {
if (useCct) return ((briNew * prog) + _t->_cctT * (0xFFFFU - prog)) >> 16;
else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16;
uint32_t curBri = (useCct ? cct : (on ? opacity : 0)) * prog;
curBri += (useCct ? _t->_cctT : (on ? _t->_briT : 0)) * (0xFFFFU - prog);
return curBri / 0xFFFFU;
}
return briNew;
return (useCct ? cct : (on ? opacity : 0));
}
uint8_t Segment::currentMode(uint8_t newMode) {
uint8_t Segment::currentMode() {
#ifndef WLED_DISABLE_MODE_BLEND
uint16_t prog = progress(); // implicit check for transitional & _t in progress()
uint16_t prog = progress();
if (prog < 0xFFFFU) return _t->_modeT;
#endif
return newMode;
return mode;
}
uint32_t Segment::currentColor(uint8_t slot, uint32_t colorNew) {
uint32_t Segment::currentColor(uint8_t slot) {
#ifndef WLED_DISABLE_MODE_BLEND
return transitional && _t ? color_blend(_t->_segT._colorT[slot], colorNew, progress(), true) : colorNew;
return isInTransition() ? color_blend(_t->_segT._colorT[slot], colors[slot], progress(), true) : colors[slot];
#else
return transitional && _t ? color_blend(_t->_colorT[slot], colorNew, progress(), true) : colorNew;
return isInTransition() ? color_blend(_t->_colorT[slot], colors[slot], progress(), true) : colors[slot];
#endif
}
CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
loadPalette(targetPalette, pal);
if (progress() < 0xFFFFU) {
uint16_t prog = progress();
if (prog < 0xFFFFU) {
// blend palettes
// there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time)
// minimum blend time is 100ms maximum is 65535ms
unsigned long timeMS = millis() - _t->_start;
uint16_t noOfBlends = (255U * timeMS / _t->_dur) - _t->_prevPaletteBlends;
uint16_t noOfBlends = ((255U * prog) / 0xFFFFU) - _t->_prevPaletteBlends;
for (int i=0; i<noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, targetPalette, 48);
targetPalette = _t->_palT; // copy transitioning/temporary palette
}
@ -500,6 +488,8 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t
&& (!grp || (grouping == grp && spacing == spc))
&& (ofs == UINT16_MAX || ofs == offset)) return;
stateChanged = true; // send UDP/WS broadcast
if (stop) fill(BLACK); // turn old segment range off (clears pixels if changing spacing)
if (grp) { // prevent assignment of 0
grouping = grp;
@ -510,6 +500,10 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t
}
if (ofs < UINT16_MAX) offset = ofs;
DEBUG_PRINT(F("setUp segment: ")); DEBUG_PRINT(i1);
DEBUG_PRINT(','); DEBUG_PRINT(i2);
DEBUG_PRINT(F(" -> ")); DEBUG_PRINT(i1Y);
DEBUG_PRINT(','); DEBUG_PRINTLN(i2Y);
markForReset();
if (boundsUnchanged) return;
@ -564,7 +558,6 @@ void Segment::setCCT(uint16_t k) {
void Segment::setOpacity(uint8_t o) {
if (opacity == o) return;
if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change
DEBUG_PRINT(F("-- Setting opacity: ")); DEBUG_PRINTLN(o);
opacity = o;
stateChanged = true; // send UDP/WS broadcast
}
@ -574,7 +567,7 @@ void Segment::setOption(uint8_t n, bool val) {
if (fadeTransition && n == SEG_OPTION_ON && val != prevOn) startTransition(strip.getTransition()); // start transition prior to change
if (val) options |= 0x01 << n;
else options &= ~(0x01 << n);
if (!(n == SEG_OPTION_SELECTED || n == SEG_OPTION_RESET || n == SEG_OPTION_TRANSITIONAL)) stateChanged = true; // send UDP/WS broadcast
if (!(n == SEG_OPTION_SELECTED || n == SEG_OPTION_RESET)) stateChanged = true; // send UDP/WS broadcast
}
void Segment::setMode(uint8_t fx, bool loadDefaults) {
@ -743,7 +736,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
#endif
uint16_t len = length();
uint8_t _bri_t = currentBri(on ? opacity : 0);
uint8_t _bri_t = currentBri();
if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t);
byte g = scale8(G(col), _bri_t);
@ -877,10 +870,11 @@ uint8_t Segment::differs(Segment& b) const {
if (startY != b.startY) d |= SEG_DIFFERS_BOUNDS;
if (stopY != b.stopY) d |= SEG_DIFFERS_BOUNDS;
//bit pattern: (msb first) set:2, sound:1, mapping:3, transposed, mirrorY, reverseY, [transitional, reset,] paused, mirrored, on, reverse, [selected]
if ((options & 0b1111111110011110U) != (b.options & 0b1111111110011110U)) d |= SEG_DIFFERS_OPT;
//bit pattern: (msb first)
// set:2, sound:2, mapping:3, transposed, mirrorY, reverseY, [reset,] paused, mirrored, on, reverse, [selected]
if ((options & 0b1111111111011110U) != (b.options & 0b1111111111011110U)) d |= SEG_DIFFERS_OPT;
if ((options & 0x0001U) != (b.options & 0x0001U)) d |= SEG_DIFFERS_SEL;
for (uint8_t i = 0; i < NUM_COLORS; i++) if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL;
for (unsigned i = 0; i < NUM_COLORS; i++) if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL;
return d;
}
@ -912,7 +906,7 @@ void Segment::refreshLightCapabilities() {
segStopIdx = stop;
}
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
for (unsigned b = 0; b < busses.getNumBusses(); b++) {
Bus *bus = busses.getBus(b);
if (bus == nullptr || bus->getLength()==0) break;
if (!bus->isOk()) continue;
@ -942,7 +936,7 @@ void Segment::fill(uint32_t c) {
if (!isActive()) return; // not active
const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
const uint16_t rows = virtualHeight(); // will be 1 for 1D
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
if (is2D()) setPixelColorXY(x, y, c);
else setPixelColor(x, c);
}
@ -956,27 +950,12 @@ void Segment::blendPixelColor(int n, uint32_t color, uint8_t blend) {
// Adds the specified color with the existing pixel color perserving color balance.
void Segment::addPixelColor(int n, uint32_t color, bool fast) {
if (!isActive()) return; // not active
uint32_t col = getPixelColor(n);
uint8_t r = R(col);
uint8_t g = G(col);
uint8_t b = B(col);
uint8_t w = W(col);
if (fast) {
r = qadd8(r, R(color));
g = qadd8(g, G(color));
b = qadd8(b, B(color));
w = qadd8(w, W(color));
col = RGBW32(r,g,b,w);
} else {
col = color_add(col, color);
}
setPixelColor(n, col);
setPixelColor(n, color_add(getPixelColor(n), color, fast));
}
void Segment::fadePixelColor(uint16_t n, uint8_t fade) {
if (!isActive()) return; // not active
CRGB pix = CRGB(getPixelColor(n)).nscale8_video(fade);
setPixelColor(n, pix);
setPixelColor(n, color_fade(getPixelColor(n), fade, true));
}
/*
@ -996,7 +975,7 @@ void Segment::fade_out(uint8_t rate) {
int g2 = G(color);
int b2 = B(color);
for (uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x);
int w1 = W(color);
int r1 = R(color);
@ -1025,9 +1004,9 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) {
const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
const uint16_t rows = virtualHeight(); // will be 1 for 1D
for (uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
if (is2D()) setPixelColorXY(x, y, CRGB(getPixelColorXY(x,y)).nscale8(255-fadeBy));
else setPixelColor(x, CRGB(getPixelColor(x)).nscale8(255-fadeBy));
for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
if (is2D()) setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), 255-fadeBy));
else setPixelColor(x, color_fade(getPixelColor(x), 255-fadeBy));
}
}
@ -1040,34 +1019,26 @@ void Segment::blur(uint8_t blur_amount)
#ifndef WLED_DISABLE_2D
if (is2D()) {
// compatibility with 2D
const uint_fast16_t cols = virtualWidth();
const uint_fast16_t rows = virtualHeight();
for (uint_fast16_t i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows
for (uint_fast16_t k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns
const unsigned cols = virtualWidth();
const unsigned rows = virtualHeight();
for (unsigned i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows
for (unsigned k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns
return;
}
#endif
uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black;
uint_fast16_t vlength = virtualLength();
for(uint_fast16_t i = 0; i < vlength; i++)
{
CRGB cur = CRGB(getPixelColor(i));
CRGB part = cur;
CRGB before = cur; // remember color before blur
part.nscale8(seep);
cur.nscale8(keep);
cur += carryover;
if(i > 0) {
uint32_t carryover = BLACK;
unsigned vlength = virtualLength();
for (unsigned i = 0; i < vlength; i++) {
uint32_t cur = getPixelColor(i);
uint32_t part = color_fade(cur, seep);
cur = color_add(color_fade(cur, keep), carryover, true);
if (i > 0) {
uint32_t c = getPixelColor(i-1);
uint8_t r = R(c);
uint8_t g = G(c);
uint8_t b = B(c);
setPixelColor((uint16_t)(i-1), qadd8(r, part.red), qadd8(g, part.green), qadd8(b, part.blue));
setPixelColor(i-1, color_add(c, part, true));
}
if (before != cur) // optimization: only set pixel if color has changed
setPixelColor((uint16_t)i,cur.red, cur.green, cur.blue);
setPixelColor(i, cur);
carryover = part;
}
}
@ -1080,7 +1051,7 @@ void Segment::blur(uint8_t blur_amount)
uint32_t Segment::color_wheel(uint8_t pos) {
if (palette) return color_from_palette(pos, false, true, 0);
pos = 255 - pos;
if(pos < 85) {
if (pos < 85) {
return ((uint32_t)(255 - pos * 3) << 16) | ((uint32_t)(0) << 8) | (pos * 3);
} else if(pos < 170) {
pos -= 85;
@ -1091,21 +1062,6 @@ uint32_t Segment::color_wheel(uint8_t pos) {
}
}
/*
* Returns a new, random wheel index with a minimum distance of 42 from pos.
*/
uint8_t Segment::get_random_wheel_index(uint8_t pos) {
uint8_t r = 0, x = 0, y = 0, d = 0;
while(d < 42) {
r = random8();
x = abs(pos - r);
y = 255 - x;
d = MIN(x, y);
}
return r;
}
/*
* Gets a single color from the currently selected palette.
* @param i Palette Index (if mapping is true, the full palette will be _virtualSegmentLength long, if false, 255). Will wrap around automatically.
@ -1119,20 +1075,20 @@ uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_
{
// default palette or no RGB support on segment
if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) {
uint32_t color = currentColor(mcol, colors[mcol]);
uint32_t color = currentColor(mcol);
color = gamma32(color);
if (pbri == 255) return color;
return RGBW32(scale8_video(R(color),pbri), scale8_video(G(color),pbri), scale8_video(B(color),pbri), scale8_video(W(color),pbri));
return color_fade(color, pbri, true);
}
uint8_t paletteIndex = i;
if (mapping && virtualLength() > 1) paletteIndex = (i*255)/(virtualLength() -1);
if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
CRGB fastled_col;
if (!wrap && strip.paletteBlend != 3) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
CRGBPalette16 curPal;
if (transitional && _t) curPal = _t->_palT;
else loadPalette(curPal, palette);
fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global
curPal = currentPalette(curPal, palette);
//if (isInTransition()) curPal = _t->_palT;
//else loadPalette(curPal, palette);
CRGB fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global
return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, 0);
}
@ -1166,7 +1122,7 @@ void WS2812FX::finalizeInit(void)
const uint8_t defNumBusses = ((sizeof defDataPins) / (sizeof defDataPins[0]));
const uint8_t defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0]));
uint16_t prevLen = 0;
for (uint8_t i = 0; i < defNumBusses && i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
for (int i = 0; i < defNumBusses && i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
uint8_t defPin[] = {defDataPins[i]};
uint16_t start = prevLen;
uint16_t count = defCounts[(i < defNumCounts) ? i : defNumCounts -1];
@ -1177,7 +1133,7 @@ void WS2812FX::finalizeInit(void)
}
_length = 0;
for (uint8_t i=0; i<busses.getNumBusses(); i++) {
for (int i=0; i<busses.getNumBusses(); i++) {
Bus *bus = busses.getBus(i);
if (bus == nullptr) continue;
if (bus->getStart() + bus->getLength() > MAX_LEDS) break;
@ -1234,21 +1190,21 @@ void WS2812FX::service() {
if (!seg.freeze) { //only run effect function if not frozen
_virtualSegmentLength = seg.virtualLength();
_colors_t[0] = seg.currentColor(0, seg.colors[0]);
_colors_t[1] = seg.currentColor(1, seg.colors[1]);
_colors_t[2] = seg.currentColor(2, seg.colors[2]);
seg.currentPalette(_currentPalette, seg.palette);
_colors_t[0] = seg.currentColor(0);
_colors_t[1] = seg.currentColor(1);
_colors_t[2] = seg.currentColor(2);
seg.currentPalette(_currentPalette, seg.palette); // we need to pass reference
if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(seg.cct, true), correctWB);
for (uint8_t c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]);
if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(true), correctWB);
for (int c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]);
// Effect blending
// When two effects are being blended, each may have different segment data, this
// data needs to be saved first and then restored before running previous/transitional mode.
// data needs to be saved first and then restored before running previous mode.
// The blending will largely depend on the effect behaviour since actual output (LEDs) may be
// overwritten by later effect. To enable seamless blending for every effect, additional LED buffer
// would need to be allocated for each effect and then blended together for each pixel.
[[maybe_unused]] uint8_t tmpMode = seg.currentMode(seg.mode); // this will return old mode while in transition
[[maybe_unused]] uint8_t tmpMode = seg.currentMode(); // this will return old mode while in transition
delay = (*_mode[seg.mode])(); // run new/current mode
#ifndef WLED_DISABLE_MODE_BLEND
if (seg.mode != tmpMode) {
@ -1262,7 +1218,7 @@ void WS2812FX::service() {
}
#endif
if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++;
if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
if (seg.isInTransition() && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
}
seg.next_time = nowUp + delay;
@ -1335,13 +1291,13 @@ uint8_t WS2812FX::estimateCurrentAndLimitBri() {
size_t pLen = 0; //getLengthPhysical();
size_t powerSum = 0;
for (uint_fast8_t bNum = 0; bNum < busses.getNumBusses(); bNum++) {
for (unsigned bNum = 0; bNum < busses.getNumBusses(); bNum++) {
Bus *bus = busses.getBus(bNum);
if (!IS_DIGITAL(bus->getType())) continue; //exclude non-digital network busses
uint16_t len = bus->getLength();
pLen += len;
uint32_t busPowerSum = 0;
for (uint_fast16_t i = 0; i < len; i++) { //sum up the usage of each LED
for (unsigned i = 0; i < len; i++) { //sum up the usage of each LED
uint32_t c = bus->getPixelColor(i); // always returns original or restored color without brightness scaling
byte r = R(c), g = G(c), b = B(c), w = W(c);
@ -1597,10 +1553,12 @@ void WS2812FX::setSegment(uint8_t segId, uint16_t i1, uint16_t i2, uint8_t group
_qStart = i1; _qStop = i2; _qStartY = startY; _qStopY = stopY;
_qGrouping = grouping; _qSpacing = spacing; _qOffset = offset;
_queuedChangesSegId = segId;
DEBUG_PRINT(F("Segment queued: ")); DEBUG_PRINTLN(segId);
return; // queued changes are applied immediately after effect function returns
}
_segments[segId].setUp(i1, i2, grouping, spacing, offset, startY, stopY);
if (segId > 0 && segId == getSegmentsNum()-1 && i2 <= i1) _segments.pop_back(); // if last segment was deleted remove it from vector
}
void WS2812FX::setUpSegmentFromQueuedChanges() {
@ -1729,7 +1687,7 @@ void WS2812FX::fixInvalidSegments() {
bool WS2812FX::checkSegmentAlignment() {
bool aligned = false;
for (segment &seg : _segments) {
for (uint8_t b = 0; b<busses.getNumBusses(); b++) {
for (unsigned b = 0; b<busses.getNumBusses(); b++) {
Bus *bus = busses.getBus(b);
if (seg.start == bus->getStart() && seg.stop == bus->getStart() + bus->getLength()) aligned = true;
}
@ -1752,13 +1710,8 @@ uint8_t WS2812FX::setPixelSegment(uint8_t n) {
}
void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) {
if (i2 >= i)
{
for (uint16_t x = i; x <= i2; x++) setPixelColor(x, col);
} else
{
for (uint16_t x = i2; x <= i; x++) setPixelColor(x, col);
}
if (i2 < i) std::swap(i,i2);
for (unsigned x = i; x <= i2; x++) setPixelColor(x, col);
}
void WS2812FX::setTransitionMode(bool t) {
@ -1832,7 +1785,7 @@ bool WS2812FX::deserializeMap(uint8_t n) {
char fileName[32];
strcpy_P(fileName, PSTR("/ledmap"));
if (n) sprintf(fileName +7, "%d", n);
strcat(fileName, ".json");
strcat_P(fileName, PSTR(".json"));
bool isFile = WLED_FS.exists(fileName);
if (!isFile) {
@ -1866,7 +1819,7 @@ bool WS2812FX::deserializeMap(uint8_t n) {
if (!map.isNull() && map.size()) { // not an empty map
customMappingSize = map.size();
customMappingTable = new uint16_t[customMappingSize];
for (uint16_t i=0; i<customMappingSize; i++) {
for (unsigned i=0; i<customMappingSize; i++) {
customMappingTable[i] = (uint16_t) (map[i]<0 ? 0xFFFFU : map[i]);
}
}

View File

@ -66,7 +66,7 @@ uint8_t IRAM_ATTR ColorOrderMap::getPixelColorOrder(uint16_t pix, uint8_t defaul
if (_count == 0) return defaultColorOrder;
// upper nibble containd W swap information
uint8_t swapW = defaultColorOrder >> 4;
for (uint8_t i = 0; i < _count; i++) {
for (unsigned i = 0; i < _count; i++) {
if (pix >= _mappings[i].start && pix < (_mappings[i].start + _mappings[i].len)) {
return _mappings[i].colorOrder | (swapW << 4);
}
@ -180,7 +180,7 @@ void BusDigital::setBrightness(uint8_t b) {
// (which we can't rely on)
uint16_t hwLen = _len;
if (_type == TYPE_WS2812_1CH_X3) hwLen = NUM_ICS_WS2812_1CH_3X(_len); // only needs a third of "RGB" LEDs for NeoPixelBus
for (uint_fast16_t i = 0; i < hwLen; i++) {
for (unsigned i = 0; i < hwLen; i++) {
// use 0 as color order, actual order does not matter here as we just update the channel values as-is
uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, i, 0),prevBri);
PolyBus::setPixelColor(_busPtr, _iType, i, c, 0);
@ -261,7 +261,7 @@ uint32_t BusDigital::getPixelColor(uint16_t pix) {
uint8_t BusDigital::getPins(uint8_t* pinArray) {
uint8_t numPins = IS_2PIN(_type) ? 2 : 1;
for (uint8_t i = 0; i < numPins; i++) pinArray[i] = _pins[i];
for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i];
return numPins;
}
@ -305,7 +305,7 @@ BusPwm::BusPwm(BusConfig &bc)
}
#endif
for (uint8_t i = 0; i < numPins; i++) {
for (unsigned i = 0; i < numPins; i++) {
uint8_t currentPin = bc.pins[i];
if (!pinManager.allocatePin(currentPin, true, PinOwner::BusPwm)) {
deallocatePins(); return;
@ -384,7 +384,7 @@ uint32_t BusPwm::getPixelColor(uint16_t pix) {
void BusPwm::show() {
if (!_valid) return;
uint8_t numPins = NUM_PWM_PINS(_type);
for (uint8_t i = 0; i < numPins; i++) {
for (unsigned i = 0; i < numPins; i++) {
uint8_t scaled = (_data[i] * _bri) / 255;
if (_reversed) scaled = 255 - scaled;
#ifdef ESP8266
@ -398,7 +398,7 @@ void BusPwm::show() {
uint8_t BusPwm::getPins(uint8_t* pinArray) {
if (!_valid) return 0;
uint8_t numPins = NUM_PWM_PINS(_type);
for (uint8_t i = 0; i < numPins; i++) {
for (unsigned i = 0; i < numPins; i++) {
pinArray[i] = _pins[i];
}
return numPins;
@ -406,7 +406,7 @@ uint8_t BusPwm::getPins(uint8_t* pinArray) {
void BusPwm::deallocatePins() {
uint8_t numPins = NUM_PWM_PINS(_type);
for (uint8_t i = 0; i < numPins; i++) {
for (unsigned i = 0; i < numPins; i++) {
pinManager.deallocatePin(_pins[i], PinOwner::BusPwm);
if (!pinManager.isPinOk(_pins[i])) continue;
#ifdef ESP8266
@ -512,7 +512,7 @@ void BusNetwork::show() {
}
uint8_t BusNetwork::getPins(uint8_t* pinArray) {
for (uint8_t i = 0; i < 4; i++) {
for (unsigned i = 0; i < 4; i++) {
pinArray[i] = _client[i];
}
return 4;
@ -566,24 +566,24 @@ void BusManager::removeAll() {
DEBUG_PRINTLN(F("Removing all."));
//prevents crashes due to deleting busses while in use.
while (!canAllShow()) yield();
for (uint8_t i = 0; i < numBusses; i++) delete busses[i];
for (unsigned i = 0; i < numBusses; i++) delete busses[i];
numBusses = 0;
}
void BusManager::show() {
for (uint8_t i = 0; i < numBusses; i++) {
for (unsigned i = 0; i < numBusses; i++) {
busses[i]->show();
}
}
void BusManager::setStatusPixel(uint32_t c) {
for (uint8_t i = 0; i < numBusses; i++) {
for (unsigned i = 0; i < numBusses; i++) {
busses[i]->setStatusPixel(c);
}
}
void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c) {
for (uint8_t i = 0; i < numBusses; i++) {
for (unsigned i = 0; i < numBusses; i++) {
Bus* b = busses[i];
uint16_t bstart = b->getStart();
if (pix < bstart || pix >= bstart + b->getLength()) continue;
@ -592,7 +592,7 @@ void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c) {
}
void BusManager::setBrightness(uint8_t b) {
for (uint8_t i = 0; i < numBusses; i++) {
for (unsigned i = 0; i < numBusses; i++) {
busses[i]->setBrightness(b);
}
}
@ -607,7 +607,7 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) {
}
uint32_t BusManager::getPixelColor(uint16_t pix) {
for (uint8_t i = 0; i < numBusses; i++) {
for (unsigned i = 0; i < numBusses; i++) {
Bus* b = busses[i];
uint16_t bstart = b->getStart();
if (pix < bstart || pix >= bstart + b->getLength()) continue;
@ -617,7 +617,7 @@ uint32_t BusManager::getPixelColor(uint16_t pix) {
}
bool BusManager::canAllShow() {
for (uint8_t i = 0; i < numBusses; i++) {
for (unsigned i = 0; i < numBusses; i++) {
if (!busses[i]->canShow()) return false;
}
return true;
@ -631,7 +631,7 @@ Bus* BusManager::getBus(uint8_t busNr) {
//semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
uint16_t BusManager::getTotalLength() {
uint16_t len = 0;
for (uint8_t i=0; i<numBusses; i++) len += busses[i]->getLength();
for (unsigned i=0; i<numBusses; i++) len += busses[i]->getLength();
return len;
}

View File

@ -35,7 +35,14 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(simplifiedUI, id[F("sui")]);
#endif
JsonObject nw_ins_0 = doc["nw"]["ins"][0];
JsonObject nw = doc["nw"];
#ifndef WLED_DISABLE_ESPNOW
CJSON(enableESPNow, nw[F("espnow")]);
getStringFromJson(linked_remote, nw[F("linked_remote")], 13);
linked_remote[12] = '\0';
#endif
JsonObject nw_ins_0 = nw["ins"][0];
getStringFromJson(clientSSID, nw_ins_0[F("ssid")], 33);
//int nw_ins_0_pskl = nw_ins_0[F("pskl")];
//The WiFi PSK is normally not contained in the regular file for security reasons.
@ -216,7 +223,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (((buttonType[s] == BTN_TYPE_ANALOG) || (buttonType[s] == BTN_TYPE_ANALOG_INVERTED)) && (digitalPinToAnalogChannel(btnPin[s]) < 0))
{
// not an ADC analog pin
DEBUG_PRINTF("PIN ALLOC error: GPIO%d for analog button #%d is not an analog pin!\n", btnPin[s], s);
DEBUG_PRINT(F("PIN ALLOC error: GPIO")); DEBUG_PRINT(btnPin[s]);
DEBUG_PRINT(F("for analog button #")); DEBUG_PRINT(s);
DEBUG_PRINTLN(F(" is not an analog pin!"));
btnPin[s] = -1;
pinManager.deallocatePin(pin,PinOwner::Button);
}
@ -382,6 +391,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(udpPort, if_sync[F("port0")]); // 21324
CJSON(udpPort2, if_sync[F("port1")]); // 65506
#ifndef WLED_DISABLE_ESPNOW
CJSON(useESPNowSync, if_sync[F("espnow")]);
#endif
JsonObject if_sync_recv = if_sync["recv"];
CJSON(receiveNotificationBrightness, if_sync_recv["bri"]);
CJSON(receiveNotificationColor, if_sync_recv["col"]);
@ -389,17 +402,15 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(receiveGroups, if_sync_recv["grp"]);
CJSON(receiveSegmentOptions, if_sync_recv["seg"]);
CJSON(receiveSegmentBounds, if_sync_recv["sb"]);
//! following line might be a problem if called after boot
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects || receiveSegmentOptions);
JsonObject if_sync_send = if_sync["send"];
prev = notifyDirectDefault;
CJSON(notifyDirectDefault, if_sync_send[F("dir")]);
if (notifyDirectDefault != prev) notifyDirect = notifyDirectDefault;
CJSON(sendNotifications, if_sync_send["en"]);
sendNotificationsRT = sendNotifications;
CJSON(notifyDirect, if_sync_send[F("dir")]);
CJSON(notifyButton, if_sync_send["btn"]);
CJSON(notifyAlexa, if_sync_send["va"]);
CJSON(notifyHue, if_sync_send["hue"]);
CJSON(notifyMacro, if_sync_send["macro"]);
// CJSON(notifyMacro, if_sync_send["macro"]);
CJSON(syncGroups, if_sync_send["grp"]);
if (if_sync_send[F("twice")]) udpNumRetries = 1; // import setting from 0.13 and earlier
CJSON(udpNumRetries, if_sync_send["ret"]);
@ -409,7 +420,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(nodeBroadcastEnabled, if_nodes[F("bcast")]);
JsonObject if_live = interfaces["live"];
CJSON(receiveDirect, if_live["en"]);
CJSON(receiveDirect, if_live["en"]); // UDP/Hyperion realtime
CJSON(useMainSegmentOnly, if_live[F("mso")]);
CJSON(e131Port, if_live["port"]); // 5568
if (e131Port == DDP_DEFAULT_PORT) e131Port = E131_DEFAULT_PORT; // prevent double DDP port allocation
@ -453,13 +464,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(retainMqttMsg, if_mqtt[F("rtn")]);
#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
JsonObject if_hue = interfaces["hue"];
CJSON(huePollingEnabled, if_hue["en"]);
@ -647,6 +651,10 @@ void serializeConfig() {
#endif
JsonObject nw = doc.createNestedObject("nw");
#ifndef WLED_DISABLE_ESPNOW
nw[F("espnow")] = enableESPNow;
nw[F("linked_remote")] = linked_remote;
#endif
JsonArray nw_ins = nw.createNestedArray("ins");
@ -848,6 +856,10 @@ void serializeConfig() {
if_sync[F("port0")] = udpPort;
if_sync[F("port1")] = udpPort2;
#ifndef WLED_DISABLE_ESPNOW
if_sync[F("espnow")] = useESPNowSync;
#endif
JsonObject if_sync_recv = if_sync.createNestedObject("recv");
if_sync_recv["bri"] = receiveNotificationBrightness;
if_sync_recv["col"] = receiveNotificationColor;
@ -857,11 +869,12 @@ void serializeConfig() {
if_sync_recv["sb"] = receiveSegmentBounds;
JsonObject if_sync_send = if_sync.createNestedObject("send");
if_sync_send["en"] = sendNotifications;
if_sync_send[F("dir")] = notifyDirect;
if_sync_send["btn"] = notifyButton;
if_sync_send["va"] = notifyAlexa;
if_sync_send["hue"] = notifyHue;
if_sync_send["macro"] = notifyMacro;
// if_sync_send["macro"] = notifyMacro;
if_sync_send["grp"] = syncGroups;
if_sync_send["ret"] = udpNumRetries;
@ -870,7 +883,7 @@ void serializeConfig() {
if_nodes[F("bcast")] = nodeBroadcastEnabled;
JsonObject if_live = interfaces.createNestedObject("live");
if_live["en"] = receiveDirect;
if_live["en"] = receiveDirect; // UDP/Hyperion realtime
if_live[F("mso")] = useMainSegmentOnly;
if_live["port"] = e131Port;
if_live[F("mc")] = e131Multicast;
@ -888,6 +901,7 @@ void serializeConfig() {
if_live[F("no-gc")] = arlsDisableGammaCorrection;
if_live[F("offset")] = arlsOffset;
#ifndef WLED_DISABLE_ALEXA
JsonObject if_va = interfaces.createNestedObject("va");
if_va[F("alexa")] = alexaEnabled;
@ -896,6 +910,7 @@ void serializeConfig() {
if_va_macros.add(macroAlexaOff);
if_va["p"] = alexaNumPresets;
#endif
#ifdef WLED_ENABLE_MQTT
JsonObject if_mqtt = interfaces.createNestedObject("mqtt");
@ -912,13 +927,6 @@ void serializeConfig() {
if_mqtt_topics[F("group")] = mqttGroupTopic;
#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
JsonObject if_hue = interfaces.createNestedObject("hue");
if_hue["en"] = huePollingEnabled;
@ -1033,7 +1041,7 @@ bool deserializeConfigSec() {
JsonObject ap = doc["ap"];
getStringFromJson(apPass, ap["psk"] , 65);
JsonObject interfaces = doc["if"];
[[maybe_unused]] JsonObject interfaces = doc["if"];
#ifdef WLED_ENABLE_MQTT
JsonObject if_mqtt = interfaces["mqtt"];
@ -1072,7 +1080,7 @@ void serializeConfigSec() {
JsonObject ap = doc.createNestedObject("ap");
ap["psk"] = apPass;
JsonObject interfaces = doc.createNestedObject("if");
[[maybe_unused]] JsonObject interfaces = doc.createNestedObject("if");
#ifdef WLED_ENABLE_MQTT
JsonObject if_mqtt = interfaces.createNestedObject("mqtt");
if_mqtt["psk"] = mqttPass;

View File

@ -35,23 +35,59 @@ uint32_t color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16)
* color add function that preserves ratio
* idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule
*/
uint32_t color_add(uint32_t c1, uint32_t c2)
uint32_t color_add(uint32_t c1, uint32_t c2, bool fast)
{
uint32_t r = R(c1) + R(c2);
uint32_t g = G(c1) + G(c2);
uint32_t b = B(c1) + B(c2);
uint32_t w = W(c1) + W(c2);
uint16_t max = r;
if (g > max) max = g;
if (b > max) max = b;
if (w > max) max = w;
if (max < 256) return RGBW32(r, g, b, w);
else return RGBW32(r * 255 / max, g * 255 / max, b * 255 / max, w * 255 / max);
if (fast) {
uint8_t r = R(c1);
uint8_t g = G(c1);
uint8_t b = B(c1);
uint8_t w = W(c1);
r = qadd8(r, R(c2));
g = qadd8(g, G(c2));
b = qadd8(b, B(c2));
w = qadd8(w, W(c2));
return RGBW32(r,g,b,w);
} else {
uint32_t r = R(c1) + R(c2);
uint32_t g = G(c1) + G(c2);
uint32_t b = B(c1) + B(c2);
uint32_t w = W(c1) + W(c2);
uint16_t max = r;
if (g > max) max = g;
if (b > max) max = b;
if (w > max) max = w;
if (max < 256) return RGBW32(r, g, b, w);
else return RGBW32(r * 255 / max, g * 255 / max, b * 255 / max, w * 255 / max);
}
}
/*
* fades color toward black
* if using "video" method the resulting color will never become black unless it is already black
*/
uint32_t color_fade(uint32_t c1, uint8_t amount, bool video)
{
uint8_t r = R(c1);
uint8_t g = G(c1);
uint8_t b = B(c1);
uint8_t w = W(c1);
if (video) {
r = scale8_video(r, amount);
g = scale8_video(g, amount);
b = scale8_video(b, amount);
w = scale8_video(w, amount);
} else {
r = scale8(r, amount);
g = scale8(g, amount);
b = scale8(b, amount);
w = scale8(w, amount);
}
return RGBW32(r, g, b, w);
}
void setRandomColor(byte* rgb)
{
lastRandomIndex = strip.getMainSegment().get_random_wheel_index(lastRandomIndex);
lastRandomIndex = get_random_wheel_index(lastRandomIndex);
colorHStoRGB(lastRandomIndex*256,255,rgb);
}

View File

@ -166,7 +166,7 @@
#define CALL_MODE_NO_NOTIFY 5
#define CALL_MODE_FX_CHANGED 6 //no longer used
#define CALL_MODE_HUE 7
#define CALL_MODE_PRESET_CYCLE 8
#define CALL_MODE_PRESET_CYCLE 8 //no longer used
#define CALL_MODE_BLYNK 9 //no longer used
#define CALL_MODE_ALEXA 10
#define CALL_MODE_WS_SEND 11 //special call mode, not for notifier, updates websocket only
@ -270,6 +270,10 @@
#define COL_ORDER_GBR 5
#define COL_ORDER_MAX 5
//ESP-NOW
#define ESP_NOW_STATE_UNINIT 0
#define ESP_NOW_STATE_ON 1
#define ESP_NOW_STATE_ERROR 2
//Button type
#define BTN_TYPE_NONE 0
@ -313,10 +317,9 @@
#define SEG_OPTION_MIRROR 3 //Indicates that the effect will be mirrored within the segment
#define SEG_OPTION_FREEZE 4 //Segment contents will not be refreshed
#define SEG_OPTION_RESET 5 //Segment runtime requires reset
#define SEG_OPTION_TRANSITIONAL 6
#define SEG_OPTION_REVERSED_Y 7
#define SEG_OPTION_MIRROR_Y 8
#define SEG_OPTION_TRANSPOSED 9
#define SEG_OPTION_REVERSED_Y 6
#define SEG_OPTION_MIRROR_Y 7
#define SEG_OPTION_TRANSPOSED 8
//Segment differs return byte
#define SEG_DIFFERS_BRI 0x01 // opacity

View File

@ -1,6 +1,6 @@
//page js
var loc = false, locip, locproto = "http:";
var isOn = false, nlA = false, isLv = false, isInfo = false, isNodes = false, syncSend = false, syncTglRecv = true;
var isOn = false, nlA = false, isLv = false, isInfo = false, isNodes = false, syncSend = false/*, syncTglRecv = true*/;
var hasWhite = false, hasRGB = false, hasCCT = false;
var nlDur = 60, nlTar = 0;
var nlMode = false;
@ -613,7 +613,7 @@ function parseInfo(i) {
if (loc) name = "(L) " + name;
d.title = name;
ledCount = i.leds.count;
syncTglRecv = i.str;
//syncTglRecv = i.str;
maxSeg = i.leds.maxseg;
pmt = i.fs.pmt;
gId('buttonNodes').style.display = lastinfo.ndc > 0 ? null:"none";
@ -1688,7 +1688,7 @@ function toggleSync()
if (syncSend) showToast('Other lights in the network will now sync to this one.');
else showToast('This light and other lights in the network will no longer sync.');
var obj = {"udpn": {"send": syncSend}};
if (syncTglRecv) obj.udpn.recv = syncSend;
//if (syncTglRecv) obj.udpn.recv = syncSend;
requestJson(obj);
}

View File

@ -92,6 +92,12 @@
<h3>WLED Broadcast</h3>
UDP Port: <input name="UP" type="number" min="1" max="65535" class="d5" required><br>
2nd Port: <input name="U2" type="number" min="1" max="65535" class="d5" required><br>
<div id="NoESPNOW" class="hide">
<i class="warn">ESP-NOW support is disabled.<br></i>
</div>
<div id="ESPNOW">
Use ESP-NOW sync: <input type="checkbox" name="EN"><br><i>(in AP mode or no WiFi)</i><br>
</div>
<h3>Sync groups</h3>
<input name="GS" id="GS" type="number" style="display: none;"><!-- hidden inputs for bitwise group checkboxes -->
<input name="GR" id="GR" type="number" style="display: none;">
@ -129,14 +135,17 @@ UDP Port: <input name="UP" type="number" min="1" max="65535" class="d5" required
<td><input type="checkbox" id="R7" name="R7"></td>
<td><input type="checkbox" id="R8" name="R8"></td>
</tr>
</table><br>
Receive: <nowrap><input type="checkbox" name="RB">Brightness,</nowrap> <nowrap><input type="checkbox" name="RC">Color,</nowrap> <nowrap>and <input type="checkbox" name="RX">Effects</nowrap><br>
<input type="checkbox" name="SO"> Segment options, <input type="checkbox" name="SG"> bounds<br>
</table>
<h3>Receive</h3>
<nowrap><input type="checkbox" name="RB">Brightness,</nowrap> <nowrap><input type="checkbox" name="RC">Color,</nowrap> <nowrap>and <input type="checkbox" name="RX">Effects</nowrap><br>
<input type="checkbox" name="SO"> Segment options, <input type="checkbox" name="SG"> bounds
<h3>Send</h3>
Enable Sync on start: <input type="checkbox" name="SS"><br>
Send notifications on direct change: <input type="checkbox" name="SD"><br>
Send notifications on button press or IR: <input type="checkbox" name="SB"><br>
Send Alexa notifications: <input type="checkbox" name="SA"><br>
Send Philips Hue change notifications: <input type="checkbox" name="SH"><br>
Send Macro notifications: <input type="checkbox" name="SM"><br>
<!-- Send Macro notifications: <input type="checkbox" name="SM"><br> -->
UDP packet retransmissions: <input name="UR" type="number" min="0" max="30" class="d5" required><br><br>
<i>Reboot required to apply changes. </i>
<hr class="sml">

View File

@ -260,7 +260,7 @@
</div>
<h2>Web Setup</h2>
Server description: <input type="text" name="DS" maxlength="32"><br>
Sync button toggles both send and receive: <input type="checkbox" name="ST"><br>
<!-- Sync button toggles both send and receive: <input type="checkbox" name="ST"><br> -->
<div id="NoSimple" class="hide">
<i class="warn">This firmware build does not include simplified UI support.<br></i>
</div>

View File

@ -12,6 +12,7 @@
function gId(e) { return d.getElementById(e); }
function cE(e) { return d.createElement(e); }
function toggle(el){gId(el).classList.toggle("hide"); gId('No'+el).classList.toggle("hide");}
function H(){window.open("https://kno.wled.ge/features/settings/#wifi-settings");}
function B(){window.open(getURL("/settings"),"_self");}
function N() {
@ -194,14 +195,16 @@
<i>Can help with connectivity issues.<br>
Do not enable if WiFi is working correctly, increases power consumption.</i>
<div id="remd">
<h3>Wireless Remote</h3>
<h3>ESP-NOW Wireless</h3>
<div id="NoESPNOW" class="hide">
<i class="warn">This firmware build does not include ESP-NOW support.<br></i>
</div>
<div id="ESPNOW">
Enable ESP-NOW: <input type="checkbox" name="RE"><br>
<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>
Keep disabled if not using a remote or wireless sync, increases power consumption.<br></i>
Paired Remote MAC: <input type="text" name="RMAC" minlength="12" maxlength="12"><br>
Last device seen: <span class="rlid" onclick="d.Sf.RMAC.value=this.textContent;" style="cursor:pointer;">None</span> <br>
</div>
<div id="ethd">

View File

@ -489,7 +489,7 @@ function parseInfo() {
d.title = name;
isRgbw = li.leds.wv;
ledCount = li.leds.count;
syncTglRecv = li.str;
// syncTglRecv = li.str;
maxSeg = li.leds.maxseg;
pmt = li.fs.pmt;
cct = li.leds.cct;

View File

@ -63,7 +63,8 @@ class NeoGammaWLEDMethod {
#define gamma32(c) NeoGammaWLEDMethod::Correct32(c)
#define gamma8(c) NeoGammaWLEDMethod::rawGamma8(c)
uint32_t color_blend(uint32_t,uint32_t,uint16_t,bool b16=false);
uint32_t color_add(uint32_t,uint32_t);
uint32_t color_add(uint32_t,uint32_t, bool fast=false);
uint32_t color_fade(uint32_t c1, uint8_t amount, bool video=false);
inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); }
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb
void colorKtoRGB(uint16_t kelvin, byte* rgb);
@ -218,7 +219,7 @@ void deletePreset(byte index);
bool getPresetName(byte index, String& name);
//remote.cpp
void handleRemote();
void handleRemote(uint8_t *data, size_t len);
//set.cpp
bool isAsterisksOnly(const char* str, byte maxLen);
@ -234,6 +235,9 @@ void handleNotifications();
void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w);
void refreshNodeList();
void sendSysInfoUDP();
#ifndef WLED_DISABLE_ESPNOW
void espNowReceiveCB(uint8_t* address, uint8_t* data, uint8_t len, signed int rssi, bool broadcast);
#endif
//network.cpp
int getSignalQuality(int rssi);
@ -353,6 +357,7 @@ void checkSettingsPIN(const char *pin);
uint16_t crc16(const unsigned char* data_p, size_t length);
um_data_t* simulateSound(uint8_t simulationId);
void enumerateLedmaps();
uint8_t get_random_wheel_index(uint8_t pos);
#ifdef WLED_ADD_EEPROM_SUPPORT
//wled_eeprom.cpp

View File

@ -9,7 +9,7 @@
// Autogenerated from wled00/data/cpal/cpal.htm, do not edit!!
const uint16_t PAGE_cpal_L = 4721;
const uint8_t PAGE_cpal[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0xbd, 0x3b, 0x7f, 0x73, 0xdb, 0xb6,
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0xbd, 0x3b, 0x7f, 0x73, 0xdb, 0xb6,
0x92, 0xff, 0xe7, 0x53, 0x20, 0x4c, 0x5f, 0x42, 0xd6, 0x14, 0x45, 0xd2, 0xb6, 0x64, 0x4b, 0xa2,
0x3b, 0xa9, 0x93, 0x77, 0xce, 0x8d, 0xdd, 0x64, 0x5e, 0x7c, 0x6e, 0x7b, 0x3e, 0xbf, 0x31, 0x4d,
0x42, 0x12, 0x1b, 0x8a, 0xe0, 0x03, 0x21, 0xd9, 0xae, 0xac, 0xef, 0x7e, 0xbb, 0x00, 0x48, 0x91,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -349,9 +349,8 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
nightlightTargetBri = nl[F("tbri")] | nightlightTargetBri;
JsonObject udpn = root["udpn"];
notifyDirect = udpn["send"] | notifyDirect;
sendNotificationsRT = udpn["send"] | sendNotificationsRT;
syncGroups = udpn["sgrp"] | syncGroups;
receiveNotifications = udpn["recv"] | receiveNotifications;
receiveGroups = udpn["rgrp"] | receiveGroups;
if ((bool)udpn[F("nn")]) callMode = CALL_MODE_NO_NOTIFY; //send no notification just for this request
@ -567,8 +566,8 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
}
JsonObject udpn = root.createNestedObject("udpn");
udpn["send"] = notifyDirect;
udpn["recv"] = receiveNotifications;
udpn["send"] = sendNotificationsRT;
udpn["recv"] = receiveGroups != 0;
udpn["sgrp"] = syncGroups;
udpn["rgrp"] = receiveGroups;
@ -648,7 +647,7 @@ void serializeInfo(JsonObject root)
spi.add(spi_miso);
#endif
root[F("str")] = syncToggleReceive;
root[F("str")] = false; //syncToggleReceive;
root[F("name")] = serverDescription;
root[F("udpport")] = udpPort;

View File

@ -226,7 +226,7 @@ void handleNightlight()
if (!nightlightActiveOld) //init
{
nightlightStartTime = millis();
nightlightDelayMs = (int)(nightlightDelayMins*60000);
nightlightDelayMs = (unsigned)(nightlightDelayMins*60000);
nightlightActiveOld = true;
briNlT = bri;
for (byte i=0; i<4; i++) colNlT[i] = col[i]; // remember starting color

View File

@ -1,8 +1,5 @@
#include "wled.h"
#define ESP_NOW_STATE_UNINIT 0
#define ESP_NOW_STATE_ON 1
#define ESP_NOW_STATE_ERROR 2
#ifndef WLED_DISABLE_ESPNOW
#define NIGHT_MODE_DEACTIVATED -1
#define NIGHT_MODE_BRIGHTNESS 5
@ -17,14 +14,10 @@
#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 {
typedef struct WizMoteMessageStructure {
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
@ -36,12 +29,10 @@ typedef struct message_structure {
uint8_t byte11; // Unknown, maybe checksum
uint8_t byte12; // Unknown, maybe checksum
uint8_t byte13; // Unknown, maybe checksum
} message_structure;
} message_structure_t;
static int esp_now_state = ESP_NOW_STATE_UNINIT;
static uint32_t last_seq = UINT32_MAX;
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
static const byte brightnessSteps[] = {
@ -109,19 +100,13 @@ static void setOff() {
}
}
static void presetWithFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID) {
inline 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]);
void handleRemote(uint8_t *incomingData, size_t len) {
message_structure_t *incoming = reinterpret_cast<message_structure_t *>(incomingData);
if (strcmp(last_signal_src, linked_remote) != 0) {
DEBUG_PRINT(F("ESP Now Message Received from Unlinked Sender: "));
@ -129,27 +114,24 @@ void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
return;
}
if (len != sizeof(incoming)) {
if (len != sizeof(message_structure_t)) {
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);
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) {
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;
@ -160,41 +142,10 @@ void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
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) && (interfacesInited || apActive)) { // ESPNOW requires Wifi to be initialized (either STA, or AP Mode)
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
#else
void handleRemote(uint8_t *incomingData, size_t len) {}
#endif

View File

@ -19,27 +19,41 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
//WIFI SETTINGS
if (subPage == SUBPAGE_WIFI)
{
strlcpy(clientSSID,request->arg(F("CS")).c_str(), 33);
char oldSSID[sizeof(clientSSID)];
if (!isAsterisksOnly(request->arg(F("CP")).c_str(), 65)) strlcpy(clientPass, request->arg(F("CP")).c_str(), 65);
strcpy(oldSSID, clientSSID);
strlcpy(clientSSID,request->arg(F("CS")).c_str(), 33);
if (!strcmp(oldSSID, clientSSID)) forceReconnect = true;
if (!isAsterisksOnly(request->arg(F("CP")).c_str(), 65)) {
strlcpy(clientPass, request->arg(F("CP")).c_str(), 65);
forceReconnect = true;
}
strlcpy(cmDNS, request->arg(F("CM")).c_str(), 33);
apBehavior = request->arg(F("AB")).toInt();
strcpy(oldSSID, apSSID);
strlcpy(apSSID, request->arg(F("AS")).c_str(), 33);
if (!strcmp(oldSSID, apSSID) && apActive) forceReconnect = true;
apHide = request->hasArg(F("AH"));
int passlen = request->arg(F("AP")).length();
if (passlen == 0 || (passlen > 7 && !isAsterisksOnly(request->arg(F("AP")).c_str(), 65))) strlcpy(apPass, request->arg(F("AP")).c_str(), 65);
int t = request->arg(F("AC")).toInt(); if (t > 0 && t < 14) apChannel = t;
if (passlen == 0 || (passlen > 7 && !isAsterisksOnly(request->arg(F("AP")).c_str(), 65))) {
strlcpy(apPass, request->arg(F("AP")).c_str(), 65);
forceReconnect = true;
}
int t = request->arg(F("AC")).toInt();
if (t != apChannel) forceReconnect = true;
if (t > 0 && t < 14) apChannel = t;
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);
bool oldESPNow = enableESPNow;
enableESPNow = request->hasArg(F("RE"));
if (oldESPNow != enableESPNow) forceReconnect = true;
strlcpy(linked_remote, request->arg(F("RMAC")).c_str(), 13);
strlwr(linked_remote); //Normalize MAC format to lowercase
#endif
#ifdef WLED_USE_ETHERNET
@ -271,7 +285,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
if (subPage == SUBPAGE_UI)
{
strlcpy(serverDescription, request->arg(F("DS")).c_str(), 33);
syncToggleReceive = request->hasArg(F("ST"));
//syncToggleReceive = request->hasArg(F("ST"));
#ifdef WLED_ENABLE_SIMPLE_UI
if (simplifiedUI ^ request->hasArg(F("SU"))) {
// UI selection changed, invalidate browser cache
@ -293,6 +307,10 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
t = request->arg(F("U2")).toInt();
if (t > 0) udpPort2 = t;
#ifndef WLED_DISABLE_ESPNOW
useESPNowSync = request->hasArg(F("EN"));
#endif
syncGroups = request->arg(F("GS")).toInt();
receiveGroups = request->arg(F("GR")).toInt();
@ -301,9 +319,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
receiveNotificationEffects = request->hasArg(F("RX"));
receiveSegmentOptions = request->hasArg(F("SO"));
receiveSegmentBounds = request->hasArg(F("SG"));
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects || receiveSegmentOptions);
notifyDirectDefault = request->hasArg(F("SD"));
notifyDirect = notifyDirectDefault;
sendNotifications = request->hasArg(F("SS"));
notifyDirect = request->hasArg(F("SD"));
notifyButton = request->hasArg(F("SB"));
notifyAlexa = request->hasArg(F("SA"));
notifyHue = request->hasArg(F("SH"));
@ -317,7 +334,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
if (!nodeListEnabled) Nodes.clear();
nodeBroadcastEnabled = request->hasArg(F("NB"));
receiveDirect = request->hasArg(F("RD"));
receiveDirect = request->hasArg(F("RD")); // UDP realtime
useMainSegmentOnly = request->hasArg(F("MO"));
e131SkipOutOfSequence = request->hasArg(F("ES"));
e131Multicast = request->hasArg(F("EM"));
@ -1003,7 +1020,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
//toggle receive UDP direct notifications
pos = req.indexOf(F("RN="));
if (pos > 0) receiveNotifications = (req.charAt(pos+3) != '0');
if (pos > 0) receiveGroups = (req.charAt(pos+3) != '0') ? receiveGroups | 1 : receiveGroups & 0xFE;
//receive live data via UDP/Hyperion
pos = req.indexOf(F("RD="));

View File

@ -5,15 +5,26 @@
*/
#define UDP_SEG_SIZE 36
#define SEG_OFFSET (41+(MAX_NUM_SEGMENTS*UDP_SEG_SIZE))
#define SEG_OFFSET (41)
#define WLEDPACKETSIZE (41+(MAX_NUM_SEGMENTS*UDP_SEG_SIZE)+0)
#define UDP_IN_MAXSIZE 1472
#define PRESUMED_NETWORK_DELAY 3 //how many ms could it take on avg to reach the receiver? This will be added to transmitted times
typedef struct PartialEspNowPacket {
uint8_t magic;
uint8_t packet;
uint8_t segs;
uint8_t data[247];
} partial_packet_t;
void notify(byte callMode, bool followUp)
{
#ifndef WLED_DISABLE_ESPNOW
if (!udpConnected || !useESPNowSync) return;
#else
if (!udpConnected) return;
if (!syncGroups) return;
#endif
if (!syncGroups || !sendNotificationsRT) return;
switch (callMode)
{
case CALL_MODE_INIT: return;
@ -26,7 +37,7 @@ void notify(byte callMode, bool followUp)
case CALL_MODE_ALEXA: if (!notifyAlexa) return; break;
default: return;
}
byte udpOut[WLEDPACKETSIZE];
byte udpOut[WLEDPACKETSIZE]; //TODO: optimize size to use only active segments
Segment& mainseg = strip.getMainSegment();
udpOut[0] = 0; //0: wled notifier protocol 1: WARLS protocol
udpOut[1] = callMode;
@ -138,17 +149,232 @@ void notify(byte callMode, bool followUp)
//uint16_t offs = SEG_OFFSET;
//next value to be added has index: udpOut[offs + 0]
IPAddress broadcastIp;
broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP());
notifierUdp.beginPacket(broadcastIp, udpPort);
notifierUdp.write(udpOut, WLEDPACKETSIZE);
notifierUdp.endPacket();
#ifndef WLED_DISABLE_ESPNOW
if (enableESPNow && useESPNowSync && statusESPNow == ESP_NOW_STATE_ON) {
partial_packet_t buffer = {'W', 0, (uint8_t)s, {0}};
// send global data
DEBUG_PRINTLN(F("ESP-NOW sending first packet."));
memcpy(buffer.data, udpOut, 41);
auto err = quickEspNow.send(ESPNOW_BROADCAST_ADDRESS, reinterpret_cast<const uint8_t*>(&buffer), 41+3);
if (!err) {
// send segment data
buffer.packet++;
size_t packetSize = 0;
int32_t err = 0;
for (size_t i = 0; i < s; i++) {
memcpy(buffer.data + packetSize, &udpOut[41+i*UDP_SEG_SIZE], UDP_SEG_SIZE);
packetSize += UDP_SEG_SIZE;
if (packetSize + UDP_SEG_SIZE < sizeof(buffer.data)/sizeof(uint8_t)) continue;
DEBUG_PRINTF("ESP-NOW sending packet: %d (%d)\n", (int)buffer.packet, packetSize+3);
err = quickEspNow.send(ESPNOW_BROADCAST_ADDRESS, reinterpret_cast<const uint8_t*>(&buffer), packetSize+3);
buffer.packet++;
packetSize = 0;
if (err) break;
}
if (!err && packetSize > 0) {
DEBUG_PRINTF("ESP-NOW sending last packet: %d (%d)\n", (int)buffer.packet, packetSize+3);
err = quickEspNow.send(ESPNOW_BROADCAST_ADDRESS, reinterpret_cast<const uint8_t*>(&buffer), packetSize+3);
}
if (err) {
DEBUG_PRINTLN(F("ESP-NOW sending packet failed."));
}
}
}
if (udpConnected)
#endif
{
DEBUG_PRINTLN(F("UDP sending packet."));
IPAddress broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP());
notifierUdp.beginPacket(broadcastIp, udpPort);
notifierUdp.write(udpOut, WLEDPACKETSIZE); // TODO: add actual used buffer size
notifierUdp.endPacket();
}
notificationSentCallMode = callMode;
notificationSentTime = millis();
notificationCount = followUp ? notificationCount + 1 : 0;
}
void parseNotifyPacket(uint8_t *udpIn) {
//ignore notification if received within a second after sending a notification ourselves
if (millis() - notificationSentTime < 1000) return;
if (udpIn[1] > 199) return; //do not receive custom versions
//compatibilityVersionByte:
byte version = udpIn[11];
DEBUG_PRINT(F("UDP packet version: ")); DEBUG_PRINTLN(version);
// if we are not part of any sync group ignore message
if (version < 9) {
// legacy senders are treated as if sending in sync group 1 only
if (!(receiveGroups & 0x01)) return;
} else if (!(receiveGroups & udpIn[36])) return;
bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
//apply colors from notification to main segment, only if not syncing full segments
if ((receiveNotificationColor || !someSel) && (version < 11 || !receiveSegmentOptions)) {
// primary color, only apply white if intented (version > 0)
strip.setColor(0, RGBW32(udpIn[3], udpIn[4], udpIn[5], (version > 0) ? udpIn[10] : 0));
if (version > 1) {
strip.setColor(1, RGBW32(udpIn[12], udpIn[13], udpIn[14], udpIn[15])); // secondary color
}
if (version > 6) {
strip.setColor(2, RGBW32(udpIn[20], udpIn[21], udpIn[22], udpIn[23])); // tertiary color
if (version > 9 && udpIn[37] < 255) { // valid CCT/Kelvin value
uint16_t cct = udpIn[38];
if (udpIn[37] > 0) { //Kelvin
cct |= (udpIn[37] << 8);
}
strip.setCCT(cct);
}
}
}
bool timebaseUpdated = false;
//apply effects from notification
bool applyEffects = (receiveNotificationEffects || !someSel);
if (applyEffects && currentPlaylist >= 0) unloadPlaylist();
if (version > 10 && (receiveSegmentOptions || receiveSegmentBounds)) {
uint8_t numSrcSegs = udpIn[39];
DEBUG_PRINT(F("UDP segments: ")); DEBUG_PRINTLN(numSrcSegs);
// are we syncing bounds and slave has more active segments than master?
if (receiveSegmentBounds && numSrcSegs < strip.getActiveSegmentsNum()) {
DEBUG_PRINTLN(F("Removing excessive segments."));
for (size_t i=strip.getSegmentsNum(); i>numSrcSegs; i--) {
if (strip.getSegment(i).isActive()) {
strip.setSegment(i-1,0,0); // delete segment
}
}
}
size_t inactiveSegs = 0;
for (size_t i = 0; i < numSrcSegs && i < strip.getMaxSegments(); i++) {
uint16_t ofs = 41 + i*udpIn[40]; //start of segment offset byte
uint8_t id = udpIn[0 +ofs];
DEBUG_PRINT(F("UDP segment received: ")); DEBUG_PRINTLN(id);
if (id > strip.getSegmentsNum()) break;
else if (id == strip.getSegmentsNum()) {
if (receiveSegmentBounds && id < strip.getMaxSegments()) strip.appendSegment();
else break;
}
DEBUG_PRINT(F("UDP segment check: ")); DEBUG_PRINTLN(id);
Segment& selseg = strip.getSegment(id);
// if we are not syncing bounds skip unselected segments
if (selseg.isActive() && !(selseg.isSelected() || receiveSegmentBounds)) continue;
// ignore segment if it is inactive and we are not syncing bounds
if (!receiveSegmentBounds) {
if (!selseg.isActive()) {
inactiveSegs++;
continue;
} else {
id += inactiveSegs; // adjust id
}
}
DEBUG_PRINT(F("UDP segment processing: ")); DEBUG_PRINTLN(id);
uint16_t startY = 0, start = (udpIn[1+ofs] << 8 | udpIn[2+ofs]);
uint16_t stopY = 1, stop = (udpIn[3+ofs] << 8 | udpIn[4+ofs]);
uint16_t offset = (udpIn[7+ofs] << 8 | udpIn[8+ofs]);
if (!receiveSegmentOptions) {
//selseg.setUp(start, stop, selseg.grouping, selseg.spacing, offset, startY, stopY);
// we have to use strip.setSegment() instead of selseg.setUp() to prevent crash if segment would change during drawing
strip.setSegment(id, start, stop, selseg.grouping, selseg.spacing, offset, startY, stopY);
continue; // we do receive bounds, but not options
}
selseg.options = (selseg.options & 0x0071U) | (udpIn[9 +ofs] & 0x0E); // ignore selected, freeze, reset & transitional
selseg.setOpacity(udpIn[10+ofs]);
if (applyEffects) {
selseg.setMode(udpIn[11+ofs]);
selseg.speed = udpIn[12+ofs];
selseg.intensity = udpIn[13+ofs];
selseg.palette = udpIn[14+ofs];
}
if (receiveNotificationColor || !someSel) {
selseg.setColor(0, RGBW32(udpIn[15+ofs],udpIn[16+ofs],udpIn[17+ofs],udpIn[18+ofs]));
selseg.setColor(1, RGBW32(udpIn[19+ofs],udpIn[20+ofs],udpIn[21+ofs],udpIn[22+ofs]));
selseg.setColor(2, RGBW32(udpIn[23+ofs],udpIn[24+ofs],udpIn[25+ofs],udpIn[26+ofs]));
selseg.setCCT(udpIn[27+ofs]);
}
if (version > 11) {
// when applying synced options ignore selected as it may be used as indicator of which segments to sync
// freeze, reset & transitional should never be synced
selseg.options = (selseg.options & 0x0071U) | (udpIn[28+ofs]<<8) | (udpIn[9 +ofs] & 0x8E); // ignore selected, freeze, reset & transitional
if (applyEffects) {
selseg.custom1 = udpIn[29+ofs];
selseg.custom2 = udpIn[30+ofs];
selseg.custom3 = udpIn[31+ofs] & 0x1F;
selseg.check1 = (udpIn[31+ofs]>>5) & 0x1;
selseg.check1 = (udpIn[31+ofs]>>6) & 0x1;
selseg.check1 = (udpIn[31+ofs]>>7) & 0x1;
}
startY = (udpIn[32+ofs] << 8 | udpIn[33+ofs]);
stopY = (udpIn[34+ofs] << 8 | udpIn[35+ofs]);
}
if (receiveSegmentBounds) {
// we have to use strip.setSegment() instead of selseg.setUp() to prevent crash if segment would change during drawing
strip.setSegment(id, start, stop, udpIn[5+ofs], udpIn[6+ofs], offset, startY, stopY);
} else {
// we have to use strip.setSegment() instead of selseg.setUp() to prevent crash if segment would change during drawing
strip.setSegment(id, selseg.start, selseg.stop, udpIn[5+ofs], udpIn[6+ofs], selseg.offset, selseg.startY, selseg.stopY);
}
}
stateChanged = true;
}
// simple effect sync, applies to all selected segments
if (applyEffects && (version < 11 || !receiveSegmentOptions)) {
for (size_t i = 0; i < strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue;
seg.setMode(udpIn[8]);
seg.speed = udpIn[9];
if (version > 2) seg.intensity = udpIn[16];
if (version > 4) seg.setPalette(udpIn[19]);
}
stateChanged = true;
}
if (applyEffects && version > 5) {
uint32_t t = (udpIn[25] << 24) | (udpIn[26] << 16) | (udpIn[27] << 8) | (udpIn[28]);
t += PRESUMED_NETWORK_DELAY; //adjust trivially for network delay
t -= millis();
strip.timebase = t;
timebaseUpdated = true;
}
//adjust system time, but only if sender is more accurate than self
if (version > 7) {
Toki::Time tm;
tm.sec = (udpIn[30] << 24) | (udpIn[31] << 16) | (udpIn[32] << 8) | (udpIn[33]);
tm.ms = (udpIn[34] << 8) | (udpIn[35]);
if (udpIn[29] > toki.getTimeSource()) { //if sender's time source is more accurate
toki.adjust(tm, PRESUMED_NETWORK_DELAY); //adjust trivially for network delay
uint8_t ts = TOKI_TS_UDP;
if (udpIn[29] > 99) ts = TOKI_TS_UDP_NTP;
else if (udpIn[29] >= TOKI_TS_SEC) ts = TOKI_TS_UDP_SEC;
toki.setTime(tm, ts);
} else if (timebaseUpdated && toki.getTimeSource() > 99) { //if we both have good times, get a more accurate timebase
Toki::Time myTime = toki.getTime();
uint32_t diff = toki.msDifference(tm, myTime);
strip.timebase -= PRESUMED_NETWORK_DELAY; //no need to presume, use difference between NTP times at send and receive points
if (toki.isLater(tm, myTime)) {
strip.timebase += diff;
} else {
strip.timebase -= diff;
}
}
}
if (version > 3) {
transitionDelayTemp = ((udpIn[17] << 0) & 0xFF) + ((udpIn[18] << 8) & 0xFF00);
}
nightlightActive = udpIn[6];
if (nightlightActive) nightlightDelayMins = udpIn[7];
if (receiveNotificationBrightness || !someSel) bri = udpIn[2];
stateUpdated(CALL_MODE_NOTIFICATION);
}
void realtimeLock(uint32_t timeoutMs, byte md)
{
if (!realtimeMode && !realtimeOverride) {
@ -262,8 +488,6 @@ void handleNotifications()
}
}
if (!(receiveNotifications || receiveDirect)) return;
localIP = Network.localIP();
//notifier and UDP realtime
if (!packetSize || packetSize > UDP_IN_MAXSIZE) return;
@ -306,160 +530,9 @@ void handleNotifications()
}
//wled notifier, ignore if realtime packets active
if (udpIn[0] == 0 && !realtimeMode && receiveNotifications)
if (udpIn[0] == 0 && !realtimeMode && receiveGroups)
{
//ignore notification if received within a second after sending a notification ourselves
if (millis() - notificationSentTime < 1000) return;
if (udpIn[1] > 199) return; //do not receive custom versions
//compatibilityVersionByte:
byte version = udpIn[11];
// if we are not part of any sync group ignore message
if (version < 9 || version > 199) {
// legacy senders are treated as if sending in sync group 1 only
if (!(receiveGroups & 0x01)) return;
} else if (!(receiveGroups & udpIn[36])) return;
bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
//apply colors from notification to main segment, only if not syncing full segments
if ((receiveNotificationColor || !someSel) && (version < 11 || !receiveSegmentOptions)) {
// primary color, only apply white if intented (version > 0)
strip.setColor(0, RGBW32(udpIn[3], udpIn[4], udpIn[5], (version > 0) ? udpIn[10] : 0));
if (version > 1) {
strip.setColor(1, RGBW32(udpIn[12], udpIn[13], udpIn[14], udpIn[15])); // secondary color
}
if (version > 6) {
strip.setColor(2, RGBW32(udpIn[20], udpIn[21], udpIn[22], udpIn[23])); // tertiary color
if (version > 9 && version < 200 && udpIn[37] < 255) { // valid CCT/Kelvin value
uint16_t cct = udpIn[38];
if (udpIn[37] > 0) { //Kelvin
cct |= (udpIn[37] << 8);
}
strip.setCCT(cct);
}
}
}
bool timebaseUpdated = false;
//apply effects from notification
bool applyEffects = (receiveNotificationEffects || !someSel);
if (version < 200)
{
if (applyEffects && currentPlaylist >= 0) unloadPlaylist();
if (version > 10 && (receiveSegmentOptions || receiveSegmentBounds)) {
uint8_t numSrcSegs = udpIn[39];
for (size_t i = 0; i < numSrcSegs; i++) {
uint16_t ofs = 41 + i*udpIn[40]; //start of segment offset byte
uint8_t id = udpIn[0 +ofs];
if (id > strip.getSegmentsNum()) break;
Segment& selseg = strip.getSegment(id);
if (!selseg.isActive() || !selseg.isSelected()) continue; //do not apply to non selected segments
uint16_t startY = 0, start = (udpIn[1+ofs] << 8 | udpIn[2+ofs]);
uint16_t stopY = 1, stop = (udpIn[3+ofs] << 8 | udpIn[4+ofs]);
uint16_t offset = (udpIn[7+ofs] << 8 | udpIn[8+ofs]);
if (!receiveSegmentOptions) {
selseg.setUp(start, stop, selseg.grouping, selseg.spacing, offset, startY, stopY);
continue;
}
//for (size_t j = 1; j<4; j++) selseg.setOption(j, (udpIn[9 +ofs] >> j) & 0x01); //only take into account mirrored, on, reversed; ignore selected
selseg.options = (selseg.options & 0x0071U) | (udpIn[9 +ofs] & 0x0E); // ignore selected, freeze, reset & transitional
selseg.setOpacity(udpIn[10+ofs]);
if (applyEffects) {
strip.setMode(id, udpIn[11+ofs]);
selseg.speed = udpIn[12+ofs];
selseg.intensity = udpIn[13+ofs];
selseg.palette = udpIn[14+ofs];
}
if (receiveNotificationColor || !someSel) {
selseg.setColor(0, RGBW32(udpIn[15+ofs],udpIn[16+ofs],udpIn[17+ofs],udpIn[18+ofs]));
selseg.setColor(1, RGBW32(udpIn[19+ofs],udpIn[20+ofs],udpIn[21+ofs],udpIn[22+ofs]));
selseg.setColor(2, RGBW32(udpIn[23+ofs],udpIn[24+ofs],udpIn[25+ofs],udpIn[26+ofs]));
selseg.setCCT(udpIn[27+ofs]);
}
if (version > 11) {
// when applying synced options ignore selected as it may be used as indicator of which segments to sync
// freeze, reset & transitional should never be synced
selseg.options = (selseg.options & 0x0071U) | (udpIn[28+ofs]<<8) | (udpIn[9 +ofs] & 0x8E); // ignore selected, freeze, reset & transitional
if (applyEffects) {
selseg.custom1 = udpIn[29+ofs];
selseg.custom2 = udpIn[30+ofs];
selseg.custom3 = udpIn[31+ofs] & 0x1F;
selseg.check1 = (udpIn[31+ofs]>>5) & 0x1;
selseg.check1 = (udpIn[31+ofs]>>6) & 0x1;
selseg.check1 = (udpIn[31+ofs]>>7) & 0x1;
}
startY = (udpIn[32+ofs] << 8 | udpIn[33+ofs]);
stopY = (udpIn[34+ofs] << 8 | udpIn[35+ofs]);
}
if (receiveSegmentBounds) {
selseg.setUp(start, stop, udpIn[5+ofs], udpIn[6+ofs], offset, startY, stopY);
} else {
selseg.setUp(selseg.start, selseg.stop, udpIn[5+ofs], udpIn[6+ofs], selseg.offset, selseg.startY, selseg.stopY);
}
}
stateChanged = true;
}
// simple effect sync, applies to all selected segments
if (applyEffects && (version < 11 || !receiveSegmentOptions)) {
for (size_t i = 0; i < strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue;
seg.setMode(udpIn[8]);
seg.speed = udpIn[9];
if (version > 2) seg.intensity = udpIn[16];
if (version > 4) seg.setPalette(udpIn[19]);
}
stateChanged = true;
}
if (applyEffects && version > 5) {
uint32_t t = (udpIn[25] << 24) | (udpIn[26] << 16) | (udpIn[27] << 8) | (udpIn[28]);
t += PRESUMED_NETWORK_DELAY; //adjust trivially for network delay
t -= millis();
strip.timebase = t;
timebaseUpdated = true;
}
}
//adjust system time, but only if sender is more accurate than self
if (version > 7 && version < 200)
{
Toki::Time tm;
tm.sec = (udpIn[30] << 24) | (udpIn[31] << 16) | (udpIn[32] << 8) | (udpIn[33]);
tm.ms = (udpIn[34] << 8) | (udpIn[35]);
if (udpIn[29] > toki.getTimeSource()) { //if sender's time source is more accurate
toki.adjust(tm, PRESUMED_NETWORK_DELAY); //adjust trivially for network delay
uint8_t ts = TOKI_TS_UDP;
if (udpIn[29] > 99) ts = TOKI_TS_UDP_NTP;
else if (udpIn[29] >= TOKI_TS_SEC) ts = TOKI_TS_UDP_SEC;
toki.setTime(tm, ts);
} else if (timebaseUpdated && toki.getTimeSource() > 99) { //if we both have good times, get a more accurate timebase
Toki::Time myTime = toki.getTime();
uint32_t diff = toki.msDifference(tm, myTime);
strip.timebase -= PRESUMED_NETWORK_DELAY; //no need to presume, use difference between NTP times at send and receive points
if (toki.isLater(tm, myTime)) {
strip.timebase += diff;
} else {
strip.timebase -= diff;
}
}
}
if (version > 3)
{
transitionDelayTemp = ((udpIn[17] << 0) & 0xFF) + ((udpIn[18] << 8) & 0xFF00);
}
nightlightActive = udpIn[6];
if (nightlightActive) nightlightDelayMins = udpIn[7];
if (receiveNotificationBrightness || !someSel) bri = udpIn[2];
stateUpdated(CALL_MODE_NOTIFICATION);
parseNotifyPacket(udpIn);
return;
}
@ -739,7 +812,7 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8
if (sequenceNumber > 15) sequenceNumber = 0;
if (!ddpUdp.beginPacket(client, DDP_DEFAULT_PORT)) { // port defined in ESPAsyncE131.h
DEBUG_PRINTLN(F("WiFiUDP.beginPacket returned an error"));
//DEBUG_PRINTLN(F("WiFiUDP.beginPacket returned an error"));
return 1; // problem
}
@ -780,7 +853,7 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8
}
if (!ddpUdp.endPacket()) {
DEBUG_PRINTLN(F("WiFiUDP.endPacket returned an error"));
//DEBUG_PRINTLN(F("WiFiUDP.endPacket returned an error"));
return 1; // problem
}
@ -849,3 +922,78 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8
}
return 0;
}
#ifndef WLED_DISABLE_ESPNOW
// ESP-NOW message receive callback function
void espNowReceiveCB(uint8_t* address, uint8_t* data, uint8_t len, signed int rssi, bool broadcast) {
sprintf_P(last_signal_src, PSTR("%02x%02x%02x%02x%02x%02x"), address[0], address[1], address[2], address[3], address[4], address[5]);
#ifdef WLED_DEBUG
DEBUG_PRINT(F("ESP-NOW: ")); DEBUG_PRINT(last_signal_src); DEBUG_PRINT(F(" -> ")); DEBUG_PRINTLN(len);
for (int i=0; i<len; i++) DEBUG_PRINTF("%02x ", data[i]);
DEBUG_PRINTLN();
#endif
// handle WiZ Mote data
if (data[0] == 0x91 || data[0] == 0x81 || data[0] == 0x80) {
handleRemote(data, len);
return;
}
if (strlen(linked_remote) == 12 && strcmp(last_signal_src, linked_remote) != 0) {
DEBUG_PRINTLN(F("ESP-NOW unpaired remote sender."));
return;
}
partial_packet_t *buffer = reinterpret_cast<partial_packet_t *>(data);
if (len < 3 || !broadcast || buffer->magic != 'W' || !useESPNowSync || WLED_CONNECTED) {
DEBUG_PRINTLN(F("ESP-NOW unexpected packet, not syncing or connected to WiFi."));
return;
}
static uint8_t *udpIn = nullptr;
static uint8_t packetsReceived = 0; // bitfield (max 5 packets ATM)
static uint8_t segsReceived = 0;
static unsigned long lastProcessed = 0;
if (buffer->packet == 0) {
if (udpIn == nullptr) udpIn = (uint8_t *)malloc(WLEDPACKETSIZE); // we cannot use stack as we are in callback
DEBUG_PRINTLN(F("ESP-NOW inited UDP buffer."));
memcpy(udpIn, buffer->data, len-3); // global data (41 bytes)
packetsReceived |= 0x01 << buffer->packet;
segsReceived = 0;
return;
} else if (((len-3)/UDP_SEG_SIZE)*UDP_SEG_SIZE != (len-3)) {
DEBUG_PRINTF("ESP-NOW incorrect packet size: %d (%d) [%d]\n", (int)buffer->packet, (int)len-3, (int)UDP_SEG_SIZE);
if (udpIn) free(udpIn);
udpIn = nullptr;
packetsReceived = 0;
segsReceived = 0;
return;
}
if (!udpIn) return;
// TODO add verification if segsReceived > MAX_NUM_SEGMENTS or WLEDPACKETSIZE
memcpy(udpIn+41+segsReceived, buffer->data, len-3);
packetsReceived |= 0x01 << buffer->packet;
segsReceived += (len-3)/UDP_SEG_SIZE;
DEBUG_PRINTF("ESP-NOW packet received: %d (%d) [%d]\n", (int)buffer->packet, (int)len-3, (int)segsReceived);
if (segsReceived == buffer->segs) {
// last packet received
if (millis() - lastProcessed > 250) {
DEBUG_PRINTLN(F("ESP-NOW processing complete message."));
parseNotifyPacket(udpIn);
lastProcessed = millis();
} else {
DEBUG_PRINTLN(F("ESP-NOW ignoring complete message."));
}
free(udpIn);
udpIn = nullptr;
packetsReceived = 0;
segsReceived = 0;
}
}
#endif

View File

@ -416,9 +416,9 @@ uint16_t crc16(const unsigned char* data_p, size_t length) {
// (only 2 used as stored in 1 bit in segment options, consider switching to a single global simulation type)
typedef enum UM_SoundSimulations {
UMS_BeatSin = 0,
UMS_WeWillRockYou
//UMS_10_13,
//UMS_14_3
UMS_WeWillRockYou,
UMS_10_13,
UMS_14_3
} um_soundSimulations_t;
um_data_t* simulateSound(uint8_t simulationId)
@ -503,7 +503,7 @@ um_data_t* simulateSound(uint8_t simulationId)
fftResult[i] = 0;
}
break;
/*case UMS_10_3:
case UMS_10_13:
for (int i = 0; i<16; i++)
fftResult[i] = inoise8(beatsin8(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3);
volumeSmth = fftResult[8];
@ -512,7 +512,7 @@ um_data_t* simulateSound(uint8_t simulationId)
for (int i = 0; i<16; i++)
fftResult[i] = inoise8(beatsin8(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3);
volumeSmth = fftResult[8];
break;*/
break;
}
samplePeak = random8() > 250;
@ -573,3 +573,17 @@ void enumerateLedmaps() {
}
}
/*
* Returns a new, random color wheel index with a minimum distance of 42 from pos.
*/
uint8_t get_random_wheel_index(uint8_t pos) {
uint8_t r = 0, x = 0, y = 0, d = 0;
while (d < 42) {
r = random8();
x = abs(pos - r);
y = 255 - x;
d = MIN(x, y);
}
return r;
}

View File

@ -54,9 +54,6 @@ void WLED::loop()
handleIR(); // 2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too
#endif
handleConnection();
#ifndef WLED_DISABLE_ESPNOW
handleRemote();
#endif
handleSerial();
handleImprovWifiScan();
handleNotifications();
@ -676,6 +673,14 @@ void WLED::initConnection()
ws.onEvent(wsEvent);
#endif
#ifndef WLED_DISABLE_ESPNOW
if (statusESPNow == ESP_NOW_STATE_ON) {
DEBUG_PRINTLN(F("ESP-NOW stopping."));
quickEspNow.stop();
statusESPNow = ESP_NOW_STATE_UNINIT;
}
#endif
WiFi.disconnect(true); // close old connections
#ifdef ESP8266
WiFi.setPhyMode(WIFI_PHY_MODE_11N);
@ -692,7 +697,6 @@ void WLED::initConnection()
if (!WLED_WIFI_CONFIGURED) {
DEBUG_PRINTLN(F("No connection configured."));
if (!apActive) initAP(); // instantly go to ap mode
return;
} else if (!apActive) {
if (apBehavior == AP_BEHAVIOR_ALWAYS) {
DEBUG_PRINTLN(F("Access point ALWAYS enabled."));
@ -705,27 +709,43 @@ void WLED::initConnection()
}
showWelcomePage = false;
DEBUG_PRINT(F("Connecting to "));
DEBUG_PRINT(clientSSID);
DEBUG_PRINTLN("...");
if (WLED_WIFI_CONFIGURED) {
DEBUG_PRINT(F("Connecting to "));
DEBUG_PRINT(clientSSID);
DEBUG_PRINTLN("...");
// convert the "serverDescription" into a valid DNS hostname (alphanumeric)
char hostname[25];
prepareHostname(hostname);
#ifdef ESP8266
WiFi.hostname(hostname);
#endif
WiFi.begin(clientSSID, clientPass);
// convert the "serverDescription" into a valid DNS hostname (alphanumeric)
char hostname[25];
prepareHostname(hostname);
WiFi.begin(clientSSID, clientPass);
#ifdef ARDUINO_ARCH_ESP32
#if defined(LOLIN_WIFI_FIX) && (defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32S3))
WiFi.setTxPower(WIFI_POWER_8_5dBm);
WiFi.setTxPower(WIFI_POWER_8_5dBm);
#endif
WiFi.setSleep(!noWifiSleep);
WiFi.setHostname(hostname);
WiFi.setSleep(!noWifiSleep);
WiFi.setHostname(hostname);
#else
wifi_set_sleep_type((noWifiSleep) ? NONE_SLEEP_T : MODEM_SLEEP_T);
wifi_set_sleep_type((noWifiSleep) ? NONE_SLEEP_T : MODEM_SLEEP_T);
WiFi.hostname(hostname);
#endif
}
#ifndef WLED_DISABLE_ESPNOW
if (enableESPNow) {
quickEspNow.onDataRcvd(espNowReceiveCB);
bool espNowOK;
if (apActive) {
DEBUG_PRINTLN(F("ESP-NOW initing in AP mode."));
#ifdef ESP32
quickEspNow.setWiFiBandwidth(WIFI_IF_AP, WIFI_BW_HT20); // Only needed for ESP32 in case you need coexistence with ESP8266 in the same network
#endif //ESP32
espNowOK = quickEspNow.begin(apChannel, WIFI_IF_AP); // Same channel must be used for both AP and ESP-NOW
} else {
DEBUG_PRINTLN(F("ESP-NOW initing in STA mode."));
espNowOK = quickEspNow.begin(); // Use no parameters to start ESP-NOW on same channel as WiFi, in STA mode
}
statusESPNow = espNowOK ? ESP_NOW_STATE_ON : ESP_NOW_STATE_ERROR;
}
#endif
}
@ -850,8 +870,8 @@ void WLED::handleConnection()
if (!Network.isConnected()) {
if (interfacesInited) {
DEBUG_PRINTLN(F("Disconnected!"));
interfacesInited = false;
initConnection();
interfacesInited = false;
}
//send improv failed 6 seconds after second init attempt (24 sec. after provisioning)
if (improvActive > 2 && now - lastReconnectAttempt > 6000) {

View File

@ -77,6 +77,9 @@
}
#ifndef WLED_DISABLE_ESPNOW
#include <espnow.h>
#define WIFI_MODE_STA WIFI_STA
#define WIFI_MODE_AP WIFI_AP
#include <QuickEspNow.h>
#endif
#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)
@ -97,6 +100,7 @@
#ifndef WLED_DISABLE_ESPNOW
#include <esp_now.h>
#include <QuickEspNow.h>
#endif
#endif
#include <Wire.h>
@ -361,7 +365,7 @@ WLED_GLOBAL char serverDescription[33] _INIT("WLED"); // Name of module - use d
#else
WLED_GLOBAL char serverDescription[33] _INIT(SERVERNAME); // use predefined name
#endif
WLED_GLOBAL bool syncToggleReceive _INIT(false); // UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise
//WLED_GLOBAL bool syncToggleReceive _INIT(false); // UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise
WLED_GLOBAL bool simplifiedUI _INIT(false); // enable simplified UI
WLED_GLOBAL byte cacheInvalidate _INIT(0); // used to invalidate browser cache when switching from regular to simplified UI
@ -402,7 +406,7 @@ WLED_GLOBAL byte alexaNumPresets _INIT(0); // number of p
WLED_GLOBAL uint16_t realtimeTimeoutMs _INIT(2500); // ms timeout of realtime mode before returning to normal mode
WLED_GLOBAL int arlsOffset _INIT(0); // realtime LED offset
WLED_GLOBAL bool receiveDirect _INIT(true); // receive UDP realtime
WLED_GLOBAL bool receiveDirect _INIT(true); // receive UDP/Hyperion realtime
WLED_GLOBAL bool arlsDisableGammaCorrection _INIT(true); // activate if gamma correction is handled by the source
WLED_GLOBAL bool arlsForceMaxBri _INIT(false); // enable to force max brightness if source has very dark colors that would be black
@ -465,9 +469,11 @@ WLED_GLOBAL bool hueApplyColor _INIT(true);
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("");
WLED_GLOBAL bool enableESPNow _INIT(false); // global on/off for ESP-NOW
WLED_GLOBAL byte statusESPNow _INIT(ESP_NOW_STATE_UNINIT); // state of ESP-NOW stack (0 uninitialised, 1 initialised, 2 error)
WLED_GLOBAL bool useESPNowSync _INIT(false); // use ESP-NOW wireless technology for sync
WLED_GLOBAL char linked_remote[13] _INIT(""); // MAC of ESP-NOW remote (Wiz Mote)
WLED_GLOBAL char last_signal_src[13] _INIT(""); // last seen ESP-NOW sender
#endif
// Time CONFIG
@ -563,8 +569,8 @@ WLED_GLOBAL bool disablePullUp _INIT(false);
WLED_GLOBAL byte touchThreshold _INIT(TOUCH_THRESHOLD);
// notifications
WLED_GLOBAL bool notifyDirectDefault _INIT(notifyDirect);
WLED_GLOBAL bool receiveNotifications _INIT(true);
WLED_GLOBAL bool sendNotifications _INIT(false); // master notification switch
WLED_GLOBAL bool sendNotificationsRT _INIT(false); // master notification switch (runtime)
WLED_GLOBAL unsigned long notificationSentTime _INIT(0);
WLED_GLOBAL byte notificationSentCallMode _INIT(CALL_MODE_INIT);
WLED_GLOBAL uint8_t notificationCount _INIT(0);

View File

@ -83,8 +83,8 @@ void loadSettingsFromEEPROM()
nightlightDelayMinsDefault = EEPROM.read(224);
nightlightDelayMins = nightlightDelayMinsDefault;
nightlightMode = EEPROM.read(225);
notifyDirectDefault = EEPROM.read(226);
notifyDirect = notifyDirectDefault;
notifyDirect = EEPROM.read(226);
sendNotificationsRT = notifyDirect;
apChannel = EEPROM.read(227);
if (apChannel > 13 || apChannel < 1) apChannel = 1;
@ -163,7 +163,6 @@ void loadSettingsFromEEPROM()
receiveNotificationColor = EEPROM.read(391);
receiveNotificationEffects = EEPROM.read(392);
}
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
if (lastEEPROMversion > 4) {
#ifndef WLED_DISABLE_HUESYNC
@ -278,10 +277,10 @@ void loadSettingsFromEEPROM()
if (lastEEPROMversion > 13)
{
mqttEnabled = EEPROM.read(2299);
syncToggleReceive = EEPROM.read(397);
//syncToggleReceive = EEPROM.read(397);
} else {
mqttEnabled = true;
syncToggleReceive = false;
//syncToggleReceive = false;
}
if (lastEEPROMversion > 14)

View File

@ -585,7 +585,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
char s2[45] = "";
switch (subPage) {
case SUBPAGE_WIFI : 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)")); break;
case SUBPAGE_LEDS : strcpy_P(s, PSTR("LED")); break;
case SUBPAGE_UI : strcpy_P(s, PSTR("UI")); break;
case SUBPAGE_SYNC : strcpy_P(s, PSTR("Sync")); break;

View File

@ -31,7 +31,7 @@ void XML_response(AsyncWebServerRequest *request, char* dest)
oappend(SET_F("<ns>"));
oappendi(notifyDirect);
oappend(SET_F("</ns><nr>"));
oappendi(receiveNotifications);
oappendi(receiveGroups!=0);
oappend(SET_F("</nr><nl>"));
oappendi(nightlightActive);
oappend(SET_F("</nl><nf>"));
@ -280,11 +280,11 @@ void getSettingsJS(byte subPage, char* dest)
sappend('c',SET_F("WS"),noWifiSleep);
#ifndef WLED_DISABLE_ESPNOW
sappend('c',SET_F("RE"),enable_espnow_remote);
sappend('c',SET_F("RE"),enableESPNow);
sappends('s',SET_F("RMAC"),linked_remote);
#else
//hide remote settings if not compiled
oappend(SET_F("document.getElementById('remd').style.display='none';"));
oappend(SET_F("toggle('ESPNOW');")); // hide ESP-NOW setting
#endif
#ifdef WLED_USE_ETHERNET
@ -321,14 +321,11 @@ void getSettingsJS(byte subPage, char* dest)
}
#ifndef WLED_DISABLE_ESPNOW
if (last_signal_src[0] != 0) //Have seen an ESP-NOW Remote
{
if (strlen(last_signal_src) > 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
{
} else if (!enableESPNow) {
sappends('m',SET_F("(\"rlid\")[0]"),(char*)F("(Enable ESP-NOW to listen)"));
} else {
sappends('m',SET_F("(\"rlid\")[0]"),(char*)F("None"));
}
#endif
@ -468,7 +465,7 @@ void getSettingsJS(byte subPage, char* dest)
if (subPage == SUBPAGE_UI)
{
sappends('s',SET_F("DS"),serverDescription);
sappend('c',SET_F("ST"),syncToggleReceive);
//sappend('c',SET_F("ST"),syncToggleReceive);
#ifdef WLED_ENABLE_SIMPLE_UI
sappend('c',SET_F("SU"),simplifiedUI);
#else
@ -478,9 +475,15 @@ void getSettingsJS(byte subPage, char* dest)
if (subPage == SUBPAGE_SYNC)
{
char nS[32];
[[maybe_unused]] char nS[32];
sappend('v',SET_F("UP"),udpPort);
sappend('v',SET_F("U2"),udpPort2);
#ifndef WLED_DISABLE_ESPNOW
if (enableESPNow) sappend('c',SET_F("EN"),useESPNowSync);
else oappend(SET_F("toggle('ESPNOW');")); // hide ESP-NOW setting
#else
oappend(SET_F("toggle('ESPNOW');")); // hide ESP-NOW setting
#endif
sappend('v',SET_F("GS"),syncGroups);
sappend('v',SET_F("GR"),receiveGroups);
@ -489,10 +492,11 @@ void getSettingsJS(byte subPage, char* dest)
sappend('c',SET_F("RX"),receiveNotificationEffects);
sappend('c',SET_F("SO"),receiveSegmentOptions);
sappend('c',SET_F("SG"),receiveSegmentBounds);
sappend('c',SET_F("SD"),notifyDirectDefault);
sappend('c',SET_F("SS"),sendNotifications);
sappend('c',SET_F("SD"),notifyDirect);
sappend('c',SET_F("SB"),notifyButton);
sappend('c',SET_F("SH"),notifyHue);
sappend('c',SET_F("SM"),notifyMacro);
// sappend('c',SET_F("SM"),notifyMacro);
sappend('v',SET_F("UR"),udpNumRetries);
sappend('c',SET_F("NL"),nodeListEnabled);