From 2c3cad6e6171902f7fc73e20c54a14541cebc0ba Mon Sep 17 00:00:00 2001 From: Warren Spits Date: Wed, 27 Nov 2019 08:41:17 +1100 Subject: [PATCH 001/232] Pixel grouping support, configurable through JSON API. It probably breaks cronixie. --- wled00/FX.h | 8 ++-- wled00/FX_fcn.cpp | 101 +++++++++++++++++++++-------------------- wled00/wled03_set.ino | 4 +- wled00/wled05_init.ino | 2 +- wled00/wled19_json.ino | 3 +- 5 files changed, 63 insertions(+), 55 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index 5a3865ae..6eb7fdeb 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -187,6 +187,7 @@ class WS2812FX { uint8_t intensity; uint8_t palette; uint8_t mode; + uint8_t grouping; uint8_t options; //bit pattern: msb first: transitional tbd tbd tbd tbd paused reverse selected uint8_t group, spacing; uint8_t opacity; @@ -331,7 +332,7 @@ class WS2812FX { } void - init(bool supportWhite, uint16_t countPixels, bool skipFirs, uint8_t disableNLeds), + init(bool supportWhite, uint16_t countPixels, uint8_t grouping, uint8_t disableNLeds, bool skipFirst), service(void), blur(uint8_t), fade_out(uint8_t r), @@ -351,7 +352,7 @@ class WS2812FX { unlockAll(void), setTransitionMode(bool t), trigger(void), - setSegment(uint8_t n, uint16_t start, uint16_t stop), + setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping), resetSegments(), setPixelColor(uint16_t n, uint32_t c), setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), @@ -372,6 +373,7 @@ class WS2812FX { colorOrder = 0, milliampsPerLed = 55, _disableNLeds = 0, + _grouping = 1, getBrightness(void), getMode(void), getSpeed(void), @@ -508,7 +510,7 @@ class WS2812FX { CRGBPalette16 targetPalette; uint32_t now; - uint16_t _length, _lengthRaw, _usableCount; + uint16_t _length, _lengthRaw; uint16_t _rand16seed; uint8_t _brightness; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index b887332f..02cc7bd8 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -30,24 +30,14 @@ #define LED_SKIP_AMOUNT 1 #define MIN_SHOW_DELAY 15 -void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst, uint8_t disableNLeds) +void WS2812FX::init(bool supportWhite, uint16_t countPixels, uint8_t grouping, uint8_t disableNLeds, bool skipFirst) { - if (supportWhite == _rgbwMode && countPixels == _length && _locked != NULL && disableNLeds == _disableNLeds) return; + if (supportWhite == _rgbwMode && countPixels == _length && _locked != NULL && grouping == _grouping && disableNLeds == _disableNLeds) return; RESET_RUNTIME; _rgbwMode = supportWhite; _skipFirstMode = skipFirst; _length = countPixels; - - if (disableNLeds > 0) { - uint16_t groupCount = disableNLeds +1; - //since 1st led is lit, even partial group has a led lit, whereas int division truncates decimal. - bool hasExtraLight = _length % groupCount != 0; - _usableCount = _length/groupCount; - _usableCount += hasExtraLight ? 1 : 0; - } else { - _usableCount = _length; - } - + _grouping = grouping; _disableNLeds = disableNLeds; uint8_t ty = 1; @@ -63,12 +53,17 @@ void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst, uin _locked = new byte[_length]; _segments[0].start = 0; - _segments[0].stop = _usableCount; + _segments[0].stop = getUsableCount(); unlockAll(); setBrightness(_brightness); } +uint16_t WS2812FX::getUsableCount() { + uint16_t totalGroup = _grouping + _disableNLeds; + return (_length + totalGroup -1) / totalGroup; +} + void WS2812FX::service() { uint32_t nowUp = millis(); // Be aware, millis() rolls over every 49 days now = nowUp + timebase; @@ -114,29 +109,40 @@ void WS2812FX::setPixelColor(uint16_t n, uint32_t c) { void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) { - i = i * (_disableNLeds+1); if (_locked[i] && !_modeUsesLock) return; - if (IS_REVERSE) i = SEGMENT.stop -1 -i + SEGMENT.start; //reverse just individual segment - byte tmpg = g; + + RgbwColor color; + color.W = w; switch (colorOrder) //0 = Grb, default { - case 0: break; //0 = Grb, default - case 1: g = r; r = tmpg; break; //1 = Rgb, common for WS2811 - case 2: g = b; b = tmpg; break; //2 = Brg - case 3: g = b; b = r; r = tmpg; //3 = Rbg + case 0: color.G = g; color.R = r; color.B = b; break; // 0 = GRB + case 1: color.G = r; color.R = g; color.B = b; break; // 1 = RGB, common for WS2811 + case 2: color.G = b; color.R = r; color.B = g; break; // 2 = BRG + case 3: color.G = r; color.R = b; color.B = g; break; // 3 = RBG } if (!_cronixieMode) { - if (reverseMode) i = _length -1 -i; - if (_skipFirstMode) + uint16_t totalGroup = SEGMENT.grouping + _disableNLeds; + uint16_t iMax = (SEGMENT.stop - SEGMENT.start) * totalGroup; + int16_t iGroup = (i - SEGMENT.start) * totalGroup; + + /* reverse just an individual segment */ + int16_t realIndex; + if IS_REVERSE + realIndex = SEGMENT.stop - iGroup -1; + else + realIndex = SEGMENT.start + iGroup; + /* Reverse the whole string */ + if (reverseMode) realIndex = _length - 1 - realIndex; + + /* Set all the pixels in the group, ensuring _skipFirstMode is honored */ { - if (i < LED_SKIP_AMOUNT) bus->SetPixelColor(i, RgbwColor(0,0,0,0)); - i += LED_SKIP_AMOUNT; - } - if (i < _lengthRaw) bus->SetPixelColor(i, RgbwColor(r,g,b,w)); - if (_disableNLeds > 0) { - for(uint16_t offCount=0; offCount < _disableNLeds; offCount++) { - if (i < _lengthRaw) bus->SetPixelColor((i + offCount + 1), RgbwColor(0,0,0,0)); + bool reversed = reverseMode ^ IS_REVERSE; + uint16_t skip = _skipFirstMode ? LED_SKIP_AMOUNT : 0; + for (uint8_t l = 0; l < totalGroup; l++) { + int16_t indexSet = realIndex + (reversed ? -l : l); + if (indexSet >= SEGMENT.start && (indexSet < SEGMENT.start + iMax)) + bus->SetPixelColor(indexSet + skip, l < SEGMENT.grouping ? color : RgbwColor(0, 0, 0, 0)); } } } else { @@ -162,16 +168,16 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) if (_skipFirstMode) o += LED_SKIP_AMOUNT; switch(_cronixieDigits[i]) { - case 0: bus->SetPixelColor(o+5, RgbwColor(r,g,b,w)); break; - case 1: bus->SetPixelColor(o+0, RgbwColor(r,g,b,w)); break; - case 2: bus->SetPixelColor(o+6, RgbwColor(r,g,b,w)); break; - case 3: bus->SetPixelColor(o+1, RgbwColor(r,g,b,w)); break; - case 4: bus->SetPixelColor(o+7, RgbwColor(r,g,b,w)); break; - case 5: bus->SetPixelColor(o+2, RgbwColor(r,g,b,w)); break; - case 6: bus->SetPixelColor(o+8, RgbwColor(r,g,b,w)); break; - case 7: bus->SetPixelColor(o+3, RgbwColor(r,g,b,w)); break; - case 8: bus->SetPixelColor(o+9, RgbwColor(r,g,b,w)); break; - case 9: bus->SetPixelColor(o+4, RgbwColor(r,g,b,w)); break; + case 0: bus->SetPixelColor(o+5, color); break; + case 1: bus->SetPixelColor(o+0, color); break; + case 2: bus->SetPixelColor(o+6, color); break; + case 3: bus->SetPixelColor(o+1, color); break; + case 4: bus->SetPixelColor(o+7, color); break; + case 5: bus->SetPixelColor(o+2, color); break; + case 6: bus->SetPixelColor(o+8, color); break; + case 7: bus->SetPixelColor(o+3, color); break; + case 8: bus->SetPixelColor(o+9, color); break; + case 9: bus->SetPixelColor(o+4, color); break; } } } @@ -442,18 +448,14 @@ WS2812FX::Segment* WS2812FX::getSegments(void) { return _segments; } -uint16_t WS2812FX::getUsableCount(void) { - return _usableCount; -} - uint32_t WS2812FX::getLastShow(void) { return _lastShow; } -void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2) { +void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping) { if (n >= MAX_NUM_SEGMENTS) return; Segment& seg = _segments[n]; - if (seg.start == i1 && seg.stop == i2) return; + if (seg.start == i1 && seg.stop == i2 && seg.grouping == grouping) return; if (seg.isActive() && modeUsesLock(seg.mode)) { _modeUsesLock = false; @@ -465,9 +467,11 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2) { { seg.stop = 0; return; } + if (grouping > 0) seg.grouping = grouping; if (i1 < _length) seg.start = i1; + uint16_t maxStop = (_length - seg.start) / seg.grouping; seg.stop = i2; - if (i2 > _length) seg.stop = _length; + if (i2 > maxStop) seg.stop = maxStop; _segment_runtimes[n].reset(); } @@ -479,8 +483,9 @@ void WS2812FX::resetSegments() { _segments[0].colors[0] = DEFAULT_COLOR; _segments[0].start = 0; _segments[0].speed = DEFAULT_SPEED; - _segments[0].stop = _length; + _segments[0].stop = getUsableCount(); _segments[0].setOption(0, 1); //select + _segments[0].grouping = _grouping; } void WS2812FX::setIndividual(uint16_t i, uint32_t col) diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index 9bf64374..debb3d09 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -274,7 +274,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) } if (subPage != 6 || !doReboot) saveSettingsToEEPROM(); //do not save if factory reset if (subPage == 2) { - strip.init(useRGBW,ledCount,skipFirstLed,disableNLeds); + strip.init(useRGBW,ledCount,1,disableNLeds,skipFirstLed); } if (subPage == 4) alexaInit(); } @@ -375,7 +375,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) if (pos > 0) { stopI = getNumVal(&req, pos); } - strip.setSegment(main, startI, stopI); + strip.setSegment(main, startI, stopI, 1); main = strip.getMainSegmentId(); diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index a07ef3d7..2288646c 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -30,7 +30,7 @@ void wledInit() DEBUG_PRINT("heap "); DEBUG_PRINTLN(ESP.getFreeHeap()); - strip.init(EEPROM.read(372),ledCount,EEPROM.read(2204),disableNLeds); //init LEDs quickly + strip.init(EEPROM.read(372),ledCount,1,disableNLeds,EEPROM.read(2204)); //init LEDs quickly strip.setBrightness(0); DEBUG_PRINT("LEDs inited. heap usage ~"); diff --git a/wled00/wled19_json.ino b/wled00/wled19_json.ino index b232b0b6..c187afff 100644 --- a/wled00/wled19_json.ino +++ b/wled00/wled19_json.ino @@ -15,7 +15,7 @@ void deserializeSegment(JsonObject elem, byte it) uint16_t len = elem["len"]; stop = (len > 0) ? start + len : seg.stop; } - strip.setSegment(id, start, stop); + strip.setSegment(id, start, stop, 1); JsonArray colarr = elem["col"]; if (!colarr.isNull()) @@ -177,6 +177,7 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id) root["ix"] = seg.intensity; root["pal"] = seg.palette; root["sel"] = seg.isSelected(); + root["grp"] = seg.grouping; root["rev"] = seg.getOption(1); } From 55e2bc27c684afda1017890a2bc736c95b225cc1 Mon Sep 17 00:00:00 2001 From: Warren Spits Date: Thu, 5 Dec 2019 17:59:05 +1100 Subject: [PATCH 002/232] Converted segments to use lengths.Segment stop is now derived.Clears the skipped pixels.Fixed getPixelColor. --- wled00/FX.h | 27 ++++++----- wled00/FX_fcn.cpp | 101 ++++++++++++++++++++------------------- wled00/wled00.ino | 2 +- wled00/wled01_eeprom.ino | 4 +- wled00/wled02_xml.ino | 2 +- wled00/wled03_set.ino | 6 +-- wled00/wled05_init.ino | 6 +-- wled00/wled19_json.ino | 4 +- 8 files changed, 81 insertions(+), 71 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index 6eb7fdeb..f5f12230 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -44,7 +44,7 @@ #define WLED_FPS 42 #define FRAMETIME 1000/WLED_FPS -/* each segment uses 37 bytes of SRAM memory, so if you're application fails because of +/* each segment uses 41 bytes of SRAM memory, so if you're application fails because of insufficient memory, decreasing MAX_NUM_SEGMENTS may help */ #define MAX_NUM_SEGMENTS 10 @@ -180,14 +180,14 @@ class WS2812FX { // segment parameters public: - typedef struct Segment { // 24 bytes + typedef struct Segment { // 25 bytes uint16_t start; uint16_t stop; //segment invalid if stop == 0 + uint16_t rawLength; uint8_t speed; uint8_t intensity; uint8_t palette; uint8_t mode; - uint8_t grouping; uint8_t options; //bit pattern: msb first: transitional tbd tbd tbd tbd paused reverse selected uint8_t group, spacing; uint8_t opacity; @@ -217,6 +217,9 @@ class WS2812FX { { return stop - start; } + uint16_t ledGroup() { + return group + spacing; + } } segment; // segment runtime parameters @@ -332,7 +335,7 @@ class WS2812FX { } void - init(bool supportWhite, uint16_t countPixels, uint8_t grouping, uint8_t disableNLeds, bool skipFirst), + init(bool supportWhite, uint16_t countPixels, uint8_t group, uint8_t spacing, bool skipFirst), service(void), blur(uint8_t), fade_out(uint8_t r), @@ -352,7 +355,7 @@ class WS2812FX { unlockAll(void), setTransitionMode(bool t), trigger(void), - setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping), + setSegment(uint8_t n, uint16_t start, uint16_t length, uint8_t group, uint8_t spacing), resetSegments(), setPixelColor(uint16_t n, uint32_t c), setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), @@ -372,8 +375,8 @@ class WS2812FX { paletteBlend = 0, colorOrder = 0, milliampsPerLed = 55, - _disableNLeds = 0, - _grouping = 1, + _spacing = 0, + _group = 1, getBrightness(void), getMode(void), getSpeed(void), @@ -510,7 +513,7 @@ class WS2812FX { CRGBPalette16 targetPalette; uint32_t now; - uint16_t _length, _lengthRaw; + uint16_t _length; uint16_t _rand16seed; uint8_t _brightness; @@ -554,11 +557,13 @@ class WS2812FX { uint8_t _segment_index = 0; uint8_t _segment_index_palette_last = 99; - segment _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 24 bytes per element - // start, stop, speed, intensity, palette, mode, options, 3 unused bytes (group, spacing, opacity), color[] - { 0, 7, DEFAULT_SPEED, 128, 0, DEFAULT_MODE, NO_OPTIONS, 1, 0, 255, {DEFAULT_COLOR}} + segment _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 25 bytes per element + // start, stop, length, speed, intensity, palette, mode, options, group, spacing, opacity (unused), color[] + { 0, 7, 7, DEFAULT_SPEED, 128, 0, DEFAULT_MODE, NO_OPTIONS, 1, 0, 255, {DEFAULT_COLOR}} }; segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 16 bytes per element + + uint16_t realPixelIndex(uint16_t i); }; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 02cc7bd8..158a766c 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -30,38 +30,36 @@ #define LED_SKIP_AMOUNT 1 #define MIN_SHOW_DELAY 15 -void WS2812FX::init(bool supportWhite, uint16_t countPixels, uint8_t grouping, uint8_t disableNLeds, bool skipFirst) +void WS2812FX::init(bool supportWhite, uint16_t countPixels, uint8_t group, uint8_t spacing, bool skipFirst) { - if (supportWhite == _rgbwMode && countPixels == _length && _locked != NULL && grouping == _grouping && disableNLeds == _disableNLeds) return; + if (supportWhite == _rgbwMode && countPixels == _length && _locked != NULL && group == _group && spacing == _spacing) return; RESET_RUNTIME; _rgbwMode = supportWhite; _skipFirstMode = skipFirst; _length = countPixels; - _grouping = grouping; - _disableNLeds = disableNLeds; + _group = group; + _spacing = spacing; uint8_t ty = 1; if (supportWhite) ty =2; - _lengthRaw = _length; - if (_skipFirstMode) { - _lengthRaw += LED_SKIP_AMOUNT; - } + uint16_t lengthRaw = _length + (_skipFirstMode ? LED_SKIP_AMOUNT : 0); - bus->Begin((NeoPixelType)ty, _lengthRaw); + bus->Begin((NeoPixelType)ty, lengthRaw); delete[] _locked; _locked = new byte[_length]; _segments[0].start = 0; _segments[0].stop = getUsableCount(); + _segments[0].rawLength = _length; unlockAll(); setBrightness(_brightness); } uint16_t WS2812FX::getUsableCount() { - uint16_t totalGroup = _grouping + _disableNLeds; - return (_length + totalGroup -1) / totalGroup; + uint16_t ledGroup = _group + _spacing; + return (_length + ledGroup -1) / ledGroup; } void WS2812FX::service() { @@ -107,6 +105,21 @@ void WS2812FX::setPixelColor(uint16_t n, uint32_t c) { setPixelColor(n, r, g, b, w); } +uint16_t WS2812FX::realPixelIndex(uint16_t i) { + int16_t iGroup = (i - SEGMENT.start) * SEGMENT.ledGroup(); + + /* reverse just an individual segment */ + int16_t realIndex; + if IS_REVERSE + realIndex = SEGMENT.stop - iGroup -1; + else + realIndex = SEGMENT.start + iGroup; + /* Reverse the whole string */ + if (reverseMode) realIndex = _length - 1 - realIndex; + + return realIndex; +} + void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) { if (_locked[i] && !_modeUsesLock) return; @@ -122,27 +135,16 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) } if (!_cronixieMode) { - uint16_t totalGroup = SEGMENT.grouping + _disableNLeds; - uint16_t iMax = (SEGMENT.stop - SEGMENT.start) * totalGroup; - int16_t iGroup = (i - SEGMENT.start) * totalGroup; - - /* reverse just an individual segment */ - int16_t realIndex; - if IS_REVERSE - realIndex = SEGMENT.stop - iGroup -1; - else - realIndex = SEGMENT.start + iGroup; - /* Reverse the whole string */ - if (reverseMode) realIndex = _length - 1 - realIndex; - /* Set all the pixels in the group, ensuring _skipFirstMode is honored */ - { - bool reversed = reverseMode ^ IS_REVERSE; - uint16_t skip = _skipFirstMode ? LED_SKIP_AMOUNT : 0; - for (uint8_t l = 0; l < totalGroup; l++) { - int16_t indexSet = realIndex + (reversed ? -l : l); - if (indexSet >= SEGMENT.start && (indexSet < SEGMENT.start + iMax)) - bus->SetPixelColor(indexSet + skip, l < SEGMENT.grouping ? color : RgbwColor(0, 0, 0, 0)); + bool reversed = reverseMode ^ IS_REVERSE; + uint16_t realIndex = realPixelIndex(i); + uint16_t skip = _skipFirstMode ? LED_SKIP_AMOUNT : 0; + for (uint16_t l = 0; l < SEGMENT.ledGroup(); l++) { + int16_t indexSet = realIndex + (reversed ? -l : l); + if (indexSet >= SEGMENT.start && (indexSet < SEGMENT.start + SEGMENT.rawLength)) + bus->SetPixelColor(indexSet + skip, l < SEGMENT.group ? color : RgbwColor(0, 0, 0, 0)); + for (uint16_t l = 0; indexSet == 0 && l < skip; l++) {// Clear the skipped pixels + bus->SetPixelColor(l, RgbwColor(0, 0, 0, 0)); } } } else { @@ -399,10 +401,7 @@ uint32_t WS2812FX::getColor(void) { uint32_t WS2812FX::getPixelColor(uint16_t i) { - i = i * (_disableNLeds+1); - if (reverseMode) i = _length- 1 -i; - if (IS_REVERSE) i = SEGMENT.stop -1 -i + SEGMENT.start; //reverse just individual segment - if (_skipFirstMode) i += LED_SKIP_AMOUNT; + i = realPixelIndex(i) + (_skipFirstMode ? LED_SKIP_AMOUNT : 0); if (_cronixieMode) { if(i>6)return 0; @@ -422,7 +421,8 @@ uint32_t WS2812FX::getPixelColor(uint16_t i) default: return 0; } } - if (i >= _lengthRaw) return 0; + uint16_t skip = _skipFirstMode ? LED_SKIP_AMOUNT : 0; + if (i >= (_length + skip)) return 0; RgbwColor lColor = bus->GetPixelColorRgbw(i); byte r = lColor.R, g = lColor.G, b = lColor.B; switch (colorOrder) @@ -452,10 +452,10 @@ uint32_t WS2812FX::getLastShow(void) { return _lastShow; } -void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping) { +void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t length, uint8_t group, uint8_t spacing) { if (n >= MAX_NUM_SEGMENTS) return; Segment& seg = _segments[n]; - if (seg.start == i1 && seg.stop == i2 && seg.grouping == grouping) return; + if (seg.start == start && seg.rawLength == length && seg.group == group && seg.spacing == spacing) return; if (seg.isActive() && modeUsesLock(seg.mode)) { _modeUsesLock = false; @@ -463,16 +463,19 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping) _modeUsesLock = true; } _segment_index = n; fill(0); //turn old segment range off - if (i2 <= i1) //disable segment - { - seg.stop = 0; return; + + seg.rawLength = min(_length - start, length); + seg.spacing = spacing; + seg.group = group; + seg.start = start; + uint16_t ledGroup = seg.ledGroup(); + seg.stop = (seg.rawLength - seg.start + ledGroup -1) / ledGroup + seg.start; + if (seg.start >= seg.stop) { + seg.stop = 0; + } + else { + _segment_runtimes[n].reset(); } - if (grouping > 0) seg.grouping = grouping; - if (i1 < _length) seg.start = i1; - uint16_t maxStop = (_length - seg.start) / seg.grouping; - seg.stop = i2; - if (i2 > maxStop) seg.stop = maxStop; - _segment_runtimes[n].reset(); } void WS2812FX::resetSegments() { @@ -484,8 +487,10 @@ void WS2812FX::resetSegments() { _segments[0].start = 0; _segments[0].speed = DEFAULT_SPEED; _segments[0].stop = getUsableCount(); + _segments[0].rawLength = _length; _segments[0].setOption(0, 1); //select - _segments[0].grouping = _grouping; + _segments[0].group = _group; + _segments[0].spacing = _spacing; } void WS2812FX::setIndividual(uint16_t i, uint32_t col) diff --git a/wled00/wled00.ino b/wled00/wled00.ino index f3ce4833..7052ee94 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -149,7 +149,7 @@ uint16_t transitionDelay = 750; //default crossfade duration in ms //bool strip.reverseMode = false; //flip entire LED strip (reverses all effect directions) --> edit in WS2812FX.h bool skipFirstLed = false; //ignore first LED in strip (useful if you need the LED as signal repeater) -uint8_t disableNLeds = 0; //disables N LEDs between active nodes. (Useful for spacing out lights for more traditional christmas light look) +uint8_t spacing = 0; //disables N LEDs between active nodes. (Useful for spacing out lights for more traditional christmas light look) byte briMultiplier = 100; //% of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino index 879b345e..38ea7c41 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled01_eeprom.ino @@ -229,7 +229,7 @@ void saveSettingsToEEPROM() saveCurrPresetCycConf = false; } - EEPROM.write(2213, disableNLeds); + EEPROM.write(2213, spacing); writeStringToEEPROM(2220, blynkApiKey, 35); @@ -480,7 +480,7 @@ void loadSettingsFromEEPROM(bool first) presetApplyFx = EEPROM.read(2212); } - disableNLeds = EEPROM.read(2213); + spacing = EEPROM.read(2213); bootPreset = EEPROM.read(389); wifiLock = EEPROM.read(393); diff --git a/wled00/wled02_xml.ino b/wled00/wled02_xml.ino index 094b0358..8303f7fd 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/wled02_xml.ino @@ -237,7 +237,7 @@ void getSettingsJS(byte subPage, char* dest) sappend('i',"PB",strip.paletteBlend); sappend('c',"RV",strip.reverseMode); sappend('c',"SL",skipFirstLed); - sappend('v',"DL",disableNLeds); + sappend('v',"DL",spacing); } if (subPage == 3) diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index debb3d09..21332ddd 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -93,7 +93,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) if (t >= 0 && t < 4) strip.paletteBlend = t; strip.reverseMode = request->hasArg("RV"); skipFirstLed = request->hasArg("SL"); - disableNLeds = request->arg("DL").toInt(); + spacing = request->arg("DL").toInt(); t = request->arg("BF").toInt(); if (t > 0) briMultiplier = t; } @@ -274,7 +274,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) } if (subPage != 6 || !doReboot) saveSettingsToEEPROM(); //do not save if factory reset if (subPage == 2) { - strip.init(useRGBW,ledCount,1,disableNLeds,skipFirstLed); + strip.init(useRGBW,ledCount,1,spacing,skipFirstLed); } if (subPage == 4) alexaInit(); } @@ -375,7 +375,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) if (pos > 0) { stopI = getNumVal(&req, pos); } - strip.setSegment(main, startI, stopI, 1); + strip.setSegment(main, startI, stopI, 1, 0); main = strip.getMainSegmentId(); diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index 2288646c..3559d7b6 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -8,9 +8,9 @@ void wledInit() ledCount = EEPROM.read(229) + ((EEPROM.read(398) << 8) & 0xFF00); if (ledCount > MAX_LEDS || ledCount == 0) ledCount = 30; - disableNLeds = EEPROM.read(2213); + spacing = EEPROM.read(2213); //this was reading 255 after inital flash causing bootloop. Don't know why. - disableNLeds = disableNLeds != 255 ? disableNLeds : 0; + spacing = spacing!= 255 ? spacing : 0; #ifdef ESP8266 #if LEDPIN == 3 @@ -30,7 +30,7 @@ void wledInit() DEBUG_PRINT("heap "); DEBUG_PRINTLN(ESP.getFreeHeap()); - strip.init(EEPROM.read(372),ledCount,1,disableNLeds,EEPROM.read(2204)); //init LEDs quickly + strip.init(EEPROM.read(372),ledCount,1,spacing,EEPROM.read(2204)); //init LEDs quickly strip.setBrightness(0); DEBUG_PRINT("LEDs inited. heap usage ~"); diff --git a/wled00/wled19_json.ino b/wled00/wled19_json.ino index c187afff..0d1b7d4b 100644 --- a/wled00/wled19_json.ino +++ b/wled00/wled19_json.ino @@ -15,7 +15,7 @@ void deserializeSegment(JsonObject elem, byte it) uint16_t len = elem["len"]; stop = (len > 0) ? start + len : seg.stop; } - strip.setSegment(id, start, stop, 1); + strip.setSegment(id, start, stop, 1, 0); JsonArray colarr = elem["col"]; if (!colarr.isNull()) @@ -177,7 +177,7 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id) root["ix"] = seg.intensity; root["pal"] = seg.palette; root["sel"] = seg.isSelected(); - root["grp"] = seg.grouping; + root["grp"] = seg.group; root["rev"] = seg.getOption(1); } From 2dce4462a06005bbe8a0400cfd28df3626726ad6 Mon Sep 17 00:00:00 2001 From: Warren Spits Date: Mon, 9 Dec 2019 23:16:16 +1100 Subject: [PATCH 003/232] Saving and loading of grouping and spacing. --- wled00/FX_fcn.cpp | 2 ++ wled00/data/settings_leds.htm | Bin 16312 -> 7982 bytes wled00/html_settings.h | 5 +++-- wled00/wled00.ino | 1 + wled00/wled01_eeprom.ino | 2 ++ wled00/wled02_xml.ino | 3 ++- wled00/wled03_set.ino | 5 +++-- wled00/wled05_init.ino | 3 ++- wled00/wled19_json.ino | 15 +++++++-------- 9 files changed, 22 insertions(+), 14 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 158a766c..3a39b01b 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -50,6 +50,8 @@ void WS2812FX::init(bool supportWhite, uint16_t countPixels, uint8_t group, uint _locked = new byte[_length]; _segments[0].start = 0; + _segments[0].group = _group; + _segments[0].spacing = _spacing; _segments[0].stop = getUsableCount(); _segments[0].rawLength = _length; diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 450512b0270776388ca3f5133829d469be188177..eb9774593584332f4a0e23e70d1617231e8f3041 100644 GIT binary patch literal 7982 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 JKizBb{{UG%Wp@Ao literal 16312 zcmds;eNP<85yt!9jg;>&;dD0688G&7xnS%QZ|AdOUo3-tI-O1t7%;@Vg#`n#l8>I` z`E_k=chAhSyI?y}v;uZ!x~uE;sp{(C-~U<&zY8nj&tW;Nho8fXuo|kc8&1PP*bk4x zZ^LiGLd*4b*a%y_=aptGX#BQjZRqPjmrpX%wU_}|uQ&m_@`Xsqe# zRG&T3cq`r(!c4sKaYzbRU9EPaQO9~}ydoc;_fe`!>ps%Yk;a`xuHo#iR@=~=6Rq+- z@-!D7M(uS(bw?u)HJ;UxqzXTT$#AFt{6qg+CrWl0>7R#bjXsW2Pf9+la;ocYm>2Ie zk^(7rMSD{t(b0kKF6!<{ygJgIce>iv-Jg1Tn2GecJJZm}7g1wdvXfaZ>1sl}CTafB z=e8-=(lz9oX<6^CT5 z$2WYe*~+hSzhF)!+v6eAvJU>X8g+_q<0VVsGCbAO$#7fVvKgP<7C%^TS7YADUfX(Z zy3W^t;+ixuEeRI&8(-X)<+pn)PR14P=;}NDN&r+CF{I?4x{IDbvF*{R$n7bGs4QPa~4xyT>tGLE~w}o~yk1 zPSpB`i0kfu{`ZaOKm~7~*YA&`gPn+9(0wSHCE9XlGiK5a;bBu!I7?hT$oLwrJ4TO7 zf37e0DD=tuS|x{aa%ZfuDc-B-F`*W(KDlB z6sO}{Dn=?Tlh0P(iPVU+z1|G-EUp&xvmfoW7rxR5Ov}0Ms8WO9Uo`jEk+Rt+QFkIP zHzku{Z&^`{gQ_N9({eekV#!f>Li1bViT%J~tOwqXoWY^*5wTokwMvzF6=oI7e8!Q@ zQEgQI*_RyOnE&UEnhpN{plaqO{MYRtMFWwCQ_(q zw`Ff4ZL(*nN|9h*xJ)V?yD$c;??#g6MbDxNc$5#u3W#il5ubIo7TynPDI{wp1o7R zt^>RWd#Z^0Rd4^>ZOB!RBDbuqjpngdnZ_QyyQC9}SI=z+%5m(pu>Focrq!%(uFB zO*oc2fAqwf{V(f^`TpPgcO6CWiTvXz`bW<3lsZTos&0<7I^Oq1^qrjoy&1DvjCmkL ze9##4rWkWy-j(aMBPvspYxr8-VvN-dzKbd?v?;!~qCRt(hzL43Q<8<60L@-T>0U>D zZ|N>wOd{h4-Pwy&jhwmZ>i;$TYD#*>rP*ex-e z)wlGQ{wo%vqfO8ETya|I&YbRF6yN+#u7CGBs*PNy9&hM_-Z(oGdWX5kwP^~ja{3-Y zIqlBaa_3qzi%lyMu?1d>4(YXzvU1rOgce?q{D+PweRJQ{8^V1)y@S6V$6fLEci9>) z@nh_n$79`(zrAwARJ7r7q((hhQcL!pmR3QB<5+Ly_8t`fAoz}`=e%(?0{?!VfDH7L z&02Vrcb81V>9k}%eAAFMac9-4W%>UlZ=Ac9Hzr%xF*}c;H{pBvC_RAT82$M?HQ9s+ zI*usu|6q~NkFZI9zbGy)rAy8k52euqMGRQ~t1O>KVjeA3_*!EQvgmWmSgll6McgC4 z)7dCBtk)AD5BSGZ&M@t)DnGZhy8A%?E|NbIn?`Y<-35u@QY&qqmL6U7r&MH*;P;k=8q>`W6n--*&pF$3B&Jsxtd zZnB=8HL~84B!Y{!RR^!Y&T&)P14p5L7fJzeq}_sF)69`w?Q94nQz7gZVcG5u${*UdV?PX`}ruIbr1@V~dTkNBhB zAm9UypQAqUey>Q82@PA-K}}^`kr}}q?94t1ZmiG5UWlD1p1|H}TK2O&XT2)`7q2ve z-4q&N4mP4LWJT{qranUzk%pDL2V=2pv!Da|`0o0mH=5IW;%Zx+oY^{)MQ{%O<_n8V(b_s;08!PDr->0I!a$bKyUs$&D)7n+!YAD~NpO z!?ROA{o^AGRl*i$fYPzLfh zk=k;Htw2W%{X)Ud�crVZ5DS2%EUD+S0tMd@BJxFauOKo#wQgxoi0P590cd^_%lJ zJF@_jtg1pIlj>}J+!gK3Q$GWQ{=JVV5!F1lAW!4hR4sVl)tWMYO*TlLTCWH4Snzry zy~Sm=BwOH9Sa?|Fp7ZIMzs<|pg_Cp4r!6Y+b?*mjIkFtneLz<>A!z;JqJ5IOO%L&zS+)3QWdJ$RB=^X5XDB*Fnms zrh%JDzZj)Y(lmVxZkJ_qJHNsDe%>+PKT9H}Q|y;nU|Dba=aRF7%dw=6=!QN|WZ$I$ zxC2MSsL-WgcZ!c7Q@)~k;wV(u0ebJi_eAt9mLlcr(|yq6Zb|$Nred~8dQE)u{}B(w z$BML`-np~?Ft8`YAQG;}s3vcw2(vs&U5EkYKOx@nM6wI#kUk=x zwH7_4Sy`6acq0u_{ep0&A+NSUecRzU*Cm%tFUT!E4B=l0zjDORB_Kv#MQ$p^BIoSn zaU?-*^h;gqwWn9gWv}u29hxUS&hR#BE%wh=ed9~EdcC)ne9>ckeE!>Zk~h;aIE#*v zggp$k!!Z8qc39GkW=9Ns!v@%+$zzTNOFCB@J*Mh?>{PY2Mb(#Ct}}#P7eCYr`Kf3d z0xxSE`|)l>YW%t>#W>!)9;Hkf+r6NTpGx@8zO-%4TyyKnBxufRSRxcJWWR2EES~x4 zHFvBt%6KGBJ42#rRdYR8Rzf+d8}f)%Gx&H@Yd6!#i=IYY6P_JicDgn6KI|#uE*H@6j`*+`S;?K2%x62 zK1Wlx$^GMeEYn zqz0Q*--p#%Q0ZwTIe z6|0?ply0CrTD}>ixn_l3_t=+x?x|S3iq3aIZ~(&7Ycq_S)p;RrTHnmKU#g?j=G$xP z64JZ2ch&fxXQF;kS7V%LS`(GF6<4$#-As!O%j
Reverse LED order (rotate 180):
Skip first LED:
-Disable repeating N LEDs:
-(Turns off N LEDs between each lit one, spacing out effects)
+LED spacing:
+(Turns off N LEDs between each lit one, spacing out effects)
+LED grouping:
)====="; diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 7052ee94..9e3d86cd 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -150,6 +150,7 @@ uint16_t transitionDelay = 750; //default crossfade duration in ms //bool strip.reverseMode = false; //flip entire LED strip (reverses all effect directions) --> edit in WS2812FX.h bool skipFirstLed = false; //ignore first LED in strip (useful if you need the LED as signal repeater) uint8_t spacing = 0; //disables N LEDs between active nodes. (Useful for spacing out lights for more traditional christmas light look) +uint8_t group = 1; //Group LEDs into one logical LED byte briMultiplier = 100; //% of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino index 38ea7c41..1e3d26c8 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled01_eeprom.ino @@ -230,6 +230,7 @@ void saveSettingsToEEPROM() } EEPROM.write(2213, spacing); + EEPROM.write(2214, group); writeStringToEEPROM(2220, blynkApiKey, 35); @@ -481,6 +482,7 @@ void loadSettingsFromEEPROM(bool first) } spacing = EEPROM.read(2213); + group = max(1, EEPROM.read(2214)); bootPreset = EEPROM.read(389); wifiLock = EEPROM.read(393); diff --git a/wled00/wled02_xml.ino b/wled00/wled02_xml.ino index 8303f7fd..5adbd900 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/wled02_xml.ino @@ -237,7 +237,8 @@ void getSettingsJS(byte subPage, char* dest) sappend('i',"PB",strip.paletteBlend); sappend('c',"RV",strip.reverseMode); sappend('c',"SL",skipFirstLed); - sappend('v',"DL",spacing); + sappend('v',"SP",spacing); + sappend('v',"GR",group); } if (subPage == 3) diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index 21332ddd..9762ffba 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -93,7 +93,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) if (t >= 0 && t < 4) strip.paletteBlend = t; strip.reverseMode = request->hasArg("RV"); skipFirstLed = request->hasArg("SL"); - spacing = request->arg("DL").toInt(); + spacing = request->arg("SP").toInt(); + group = request->arg("GR").toInt(); t = request->arg("BF").toInt(); if (t > 0) briMultiplier = t; } @@ -274,7 +275,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) } if (subPage != 6 || !doReboot) saveSettingsToEEPROM(); //do not save if factory reset if (subPage == 2) { - strip.init(useRGBW,ledCount,1,spacing,skipFirstLed); + strip.init(useRGBW,ledCount,group,spacing,skipFirstLed); } if (subPage == 4) alexaInit(); } diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index 3559d7b6..32511be7 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -11,6 +11,7 @@ void wledInit() spacing = EEPROM.read(2213); //this was reading 255 after inital flash causing bootloop. Don't know why. spacing = spacing!= 255 ? spacing : 0; + group = max(1, EEPROM.read(2214)); #ifdef ESP8266 #if LEDPIN == 3 @@ -30,7 +31,7 @@ void wledInit() DEBUG_PRINT("heap "); DEBUG_PRINTLN(ESP.getFreeHeap()); - strip.init(EEPROM.read(372),ledCount,1,spacing,EEPROM.read(2204)); //init LEDs quickly + strip.init(EEPROM.read(372),ledCount,group,spacing,EEPROM.read(2204)); //init LEDs quickly strip.setBrightness(0); DEBUG_PRINT("LEDs inited. heap usage ~"); diff --git a/wled00/wled19_json.ino b/wled00/wled19_json.ino index 0d1b7d4b..cf3f6be8 100644 --- a/wled00/wled19_json.ino +++ b/wled00/wled19_json.ino @@ -8,14 +8,12 @@ void deserializeSegment(JsonObject elem, byte it) if (id < strip.getMaxSegments()) { WS2812FX::Segment& seg = strip.getSegment(id); - uint16_t start = elem["start"] | seg.start; - int stop = elem["stop"] | -1; + uint16_t start = elem.containsKey("start") ? elem["start"] : seg.start; + uint16_t len = elem.containsKey("len") ? elem["len"] : seg.rawLength; + uint8_t group = max(1, elem["grp"] | seg.group); + uint8_t spacing = elem.containsKey("spc") ? elem["spc"] : seg.spacing; - if (stop < 0) { - uint16_t len = elem["len"]; - stop = (len > 0) ? start + len : seg.stop; - } - strip.setSegment(id, start, stop, 1, 0); + strip.setSegment(id, start, len, group, spacing); JsonArray colarr = elem["col"]; if (!colarr.isNull()) @@ -158,7 +156,7 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id) root["id"] = id; root["start"] = seg.start; root["stop"] = seg.stop; - root["len"] = seg.stop - seg.start; + root["len"] = seg.rawLength; JsonArray colarr = root.createNestedArray("col"); @@ -178,6 +176,7 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id) root["pal"] = seg.palette; root["sel"] = seg.isSelected(); root["grp"] = seg.group; + root["spc"] = seg.spacing; root["rev"] = seg.getOption(1); } From b1cbbeb93599ccf24e960bc399a60d367739d906 Mon Sep 17 00:00:00 2001 From: Warren Spits Date: Tue, 10 Dec 2019 11:34:11 +1100 Subject: [PATCH 004/232] Segment reverse fix --- wled00/FX_fcn.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 3a39b01b..f68e7d0f 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -113,7 +113,7 @@ uint16_t WS2812FX::realPixelIndex(uint16_t i) { /* reverse just an individual segment */ int16_t realIndex; if IS_REVERSE - realIndex = SEGMENT.stop - iGroup -1; + realIndex = SEGMENT.rawLength + SEGMENT.start - iGroup -1; else realIndex = SEGMENT.start + iGroup; /* Reverse the whole string */ From bafd043aae7a6d9ad12f59623df617abaa462972 Mon Sep 17 00:00:00 2001 From: Max Hedge Date: Mon, 13 Jan 2020 17:09:20 +0000 Subject: [PATCH 005/232] Add WS2815 brightness limiter. Uses "magic value" of 255mA. --- wled00/FX_fcn.cpp | 24 ++++++++++++++++++++---- wled00/data/settings_leds.htm | Bin 16312 -> 16730 bytes wled00/html_settings.h | 3 ++- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 3081c2be..e5d237df 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -201,10 +201,17 @@ void WS2812FX::show(void) { //each LED can draw up 195075 "power units" (approx. 53mA) //one PU is the power it takes to have 1 channel 1 step brighter per brightness step //so A=2,R=255,G=0,B=0 would use 510 PU per LED (1mA is about 3700 PU) - - if (ablMilliampsMax > 149 && milliampsPerLed > 0) //0 mA per LED and too low numbers turn off calculation + bool useWackyWS2815PowerModel = false; + byte actualMilliampsPerLed = milliampsPerLed; + + if(milliampsPerLed == 255) { + useWackyWS2815PowerModel = true; + actualMilliampsPerLed = 12; // from testing an actual strip + } + + if (ablMilliampsMax > 149 && actualMilliampsPerLed > 0) //0 mA per LED and too low numbers turn off calculation { - uint32_t puPerMilliamp = 195075 / milliampsPerLed; + uint32_t puPerMilliamp = 195075 / actualMilliampsPerLed; uint32_t powerBudget = (ablMilliampsMax - MA_FOR_ESP) * puPerMilliamp; //100mA for ESP power if (powerBudget > puPerMilliamp * _length) //each LED uses about 1mA in standby, exclude that from power budget { @@ -219,9 +226,18 @@ void WS2812FX::show(void) { for (uint16_t i = 0; i < _length; i++) //sum up the usage of each LED { RgbwColor c = bus->GetPixelColorRgbw(i); - powerSum += (c.R + c.G + c.B + c.W); + + if(useWackyWS2815PowerModel) + { + powerSum += (max(max(max(c.R,c.G),c.B),c.W)) * 3; + } + else + { + powerSum += (c.R + c.G + c.B + c.W); + } } + if (_rgbwMode) //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less { powerSum *= 3; diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 450512b0270776388ca3f5133829d469be188177..e0ebfb0f56f7ad4a0280fbedb2fab1861d831705 100644 GIT binary patch delta 332 zcmdl{f2)acLk1@^Cj;l?a?U`0AkC1>kjPNXkjkLIU^KarQ=idj^LkE37H&?UfC2+A z0~b)KAgiXOErTsc34;ZKpsfW5GZR6(E;Qs0A+GO25&yeyPr`S zBv1y_QUs(kfx_Vo!9e8}KuxBTJ^2k;!3IyR<+qnqU{C`>1qMY7b-Vdzf?XagplOa_ z3Q&hV5Gx^@4CI3x6$TbpV6b9P*jz0zU25_J1D46p3=}3iX&XS@{&DKXfg8K40+ YBA3CDL347Uf%xPFT6&uoXiJCy0B9FGk^lez delta 80 zcmccB#JHn=Lk8#M6&zBN%Q)SDw7_N+t{cpg_p$LzuHX@vEXJz0c@|$E LED Settings - -

WLED Software Update

Installed version: 0.9.0-b1
Download the latest binary:


)====="; +

WLED Software Update

Installed version: 0.9.1
Download the latest binary:

)====="; //new user welcome page diff --git a/wled00/html_settings.h b/wled00/html_settings.h index c54bf5ff..ae08802b 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -223,7 +223,7 @@ Device Auth token:

MQTT

Enable MQTT:
Broker: -Port:
+Port:
The MQTT credentials are sent over an unsecured connection.
Never use the MQTT password for another service!

Username:
@@ -370,7 +370,7 @@ HTTP traffic is unencrypted. An attacker in the same network can intercept form
Enable ArduinoOTA:

About

-WLED version 0.9.0-b2

+WLED version 0.9.1

Contributors, dependencies and special thanks
A huge thank you to everyone who helped me create WLED!

(c) 2016-2020 Christian Schwinne
diff --git a/wled00/wled00.ino b/wled00/wled00.ino index f2f94145..5b17a6dc 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -3,7 +3,7 @@ */ /* * @title WLED project sketch - * @version 0.9.0-b2 + * @version 0.9.1 * @author Christian Schwinne */ @@ -90,9 +90,9 @@ #endif //version code in format yymmddb (b = daily build) -#define VERSION 2001281 +#define VERSION 2002021 -char versionString[] = "0.9.0-b2"; +char versionString[] = "0.9.1"; //AP and OTA default passwords (for maximum change them!) @@ -115,9 +115,7 @@ char cmDNS[33] = "x"; //mDNS address (placeholder, will char apSSID[33] = ""; //AP off by default (unless setup) byte apChannel = 1; //2.4GHz WiFi AP channel (1-13) byte apHide = 0; //hidden AP SSID -//byte apWaitTimeSecs = 32; //time to wait for connection before opening AP byte apBehavior = 0; //0: Open AP when no connection after boot 1: Open when no connection 2: Always open 3: Only when button pressed for 6 sec -//bool recoveryAPDisabled = false; //never open AP (not recommended) IPAddress staticIP(0, 0, 0, 0); //static IP of ESP IPAddress staticGateway(0, 0, 0, 0); //gateway (router) IP IPAddress staticSubnet(255, 255, 255, 0); //most common subnet in home networks diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index 0d113b46..b6027303 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -152,6 +152,7 @@ void initAP(bool resetAP=false){ void initConnection() { WiFi.disconnect(); //close old connections + WiFi.setPhyMode(WIFI_PHY_MODE_11N); if (staticIP[0] != 0 && staticGateway[0] != 0) { diff --git a/wled00/wled08_led.ino b/wled00/wled08_led.ino index 786049a0..480b9741 100644 --- a/wled00/wled08_led.ino +++ b/wled00/wled08_led.ino @@ -57,7 +57,7 @@ void setAllLeds() { } -void setLedsStandard() +void setLedsStandard(bool justColors = false) { for (byte i=0; i<4; i++) { @@ -66,6 +66,7 @@ void setLedsStandard() colSecOld[i] = colSec[i]; colSecT[i] = colSec[i]; } + if (justColors) return; briOld = bri; briT = bri; setAllLeds(); @@ -120,7 +121,12 @@ void colorUpdated(int callMode) colIT[i] = col[i]; colSecIT[i] = colSec[i]; } - if (briT == 0 && callMode != 3) resetTimebase(); + if (briT == 0) + { + setLedsStandard(true); //do not color transition if starting from off + if (callMode != 3) resetTimebase(); //effect start from beginning + } + briIT = bri; if (bri > 0) briLast = bri; From 71a5cfed4bbe88c51a52b8b2fa4a3244b27c6c45 Mon Sep 17 00:00:00 2001 From: Def3nder <33399267+Def3nder@users.noreply.github.com> Date: Sun, 9 Feb 2020 10:36:37 +0100 Subject: [PATCH 078/232] Intensity slider for "Police" and "Two Dots" (#670) Enable intensity slider for the Effects based on "police_base" the intensity slider controls the width of the "dot" from 1 pixel (as now) to 64 pixels. --- wled00/FX.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 12d40278..43428fd4 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -1227,6 +1227,7 @@ uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2) { uint16_t counter = now * ((SEGMENT.speed >> 2) +1); uint16_t idexR = (counter * SEGLEN) >> 16; + uint8_t size = 1 + SEGMENT.intensity >> 3; if (idexR >= SEGLEN) idexR = 0; uint16_t topindex = SEGLEN >> 1; @@ -1234,10 +1235,11 @@ uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2) if (SEGENV.call == 0) SEGENV.aux0 = idexR; if (idexB >= SEGLEN) idexB = 0; //otherwise overflow on odd number of LEDs - if (SEGENV.aux0 == idexR) { - setPixelColor(idexR, color1); - setPixelColor(idexB, color2); - } else { + for (uint8_t i=0; i <= size; i++) { + setPixelColor(idexR+i, color1); + setPixelColor(idexB+i, color2); + } + if (SEGENV.aux0 != idexR) { uint8_t gap = (SEGENV.aux0 < idexR)? idexR - SEGENV.aux0:SEGLEN - SEGENV.aux0 + idexR; for (uint8_t i = 0; i <= gap ; i++) { if ((idexR - i) < 0) idexR = SEGLEN-1 + i; From 17e43a7ff28044eab220fb08624ac2e64134788e Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sun, 9 Feb 2020 11:04:30 +0100 Subject: [PATCH 079/232] No phy mode for ESP32 --- wled00/wled05_init.ino | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index b6027303..0cc6ac7e 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -152,7 +152,9 @@ void initAP(bool resetAP=false){ void initConnection() { WiFi.disconnect(); //close old connections + #ifdef ESP8266 WiFi.setPhyMode(WIFI_PHY_MODE_11N); + #endif if (staticIP[0] != 0 && staticGateway[0] != 0) { From f646e9bc59d57d7b6fc068363af62eee2ffcbc4d Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sun, 9 Feb 2020 16:11:38 +0100 Subject: [PATCH 080/232] Swap order of API parsing to make it possible to override preset brightness --- wled00/wled03_set.ino | 76 +++++++++++++++++++++--------------------- wled00/wled19_json.ino | 6 ++-- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index a1ad3c6d..1840bd05 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -406,6 +406,44 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) main = strip.getMainSegmentId(); + //set presets + pos = req.indexOf("P1="); //sets first preset for cycle + if (pos > 0) presetCycleMin = getNumVal(&req, pos); + + pos = req.indexOf("P2="); //sets last preset for cycle + if (pos > 0) presetCycleMax = getNumVal(&req, pos); + + //preset cycle + pos = req.indexOf("CY="); + if (pos > 0) + { + presetCyclingEnabled = (req.charAt(pos+3) != '0'); + presetCycCurr = presetCycleMin; + } + + pos = req.indexOf("PT="); //sets cycle time in ms + if (pos > 0) { + int v = getNumVal(&req, pos); + if (v > 49) presetCycleTime = v; + } + + pos = req.indexOf("PA="); //apply brightness from preset + if (pos > 0) presetApplyBri = (req.charAt(pos+3) != '0'); + + pos = req.indexOf("PC="); //apply color from preset + if (pos > 0) presetApplyCol = (req.charAt(pos+3) != '0'); + + pos = req.indexOf("PX="); //apply effects from preset + if (pos > 0) presetApplyFx = (req.charAt(pos+3) != '0'); + + pos = req.indexOf("PS="); //saves current in preset + if (pos > 0) savePreset(getNumVal(&req, pos)); + + //apply preset + if (updateVal(&req, "PL=", &presetCycCurr, presetCycleMin, presetCycleMax)) { + applyPreset(presetCycCurr, presetApplyBri, presetApplyCol, presetApplyFx); + } + //set brightness updateVal(&req, "&A=", &bri); @@ -570,44 +608,6 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) if (countdownTime - now() > 0) countdownOverTriggered = false; } - //set presets - pos = req.indexOf("P1="); //sets first preset for cycle - if (pos > 0) presetCycleMin = getNumVal(&req, pos); - - pos = req.indexOf("P2="); //sets last preset for cycle - if (pos > 0) presetCycleMax = getNumVal(&req, pos); - - //preset cycle - pos = req.indexOf("CY="); - if (pos > 0) - { - presetCyclingEnabled = (req.charAt(pos+3) != '0'); - presetCycCurr = presetCycleMin; - } - - pos = req.indexOf("PT="); //sets cycle time in ms - if (pos > 0) { - int v = getNumVal(&req, pos); - if (v > 49) presetCycleTime = v; - } - - pos = req.indexOf("PA="); //apply brightness from preset - if (pos > 0) presetApplyBri = (req.charAt(pos+3) != '0'); - - pos = req.indexOf("PC="); //apply color from preset - if (pos > 0) presetApplyCol = (req.charAt(pos+3) != '0'); - - pos = req.indexOf("PX="); //apply effects from preset - if (pos > 0) presetApplyFx = (req.charAt(pos+3) != '0'); - - pos = req.indexOf("PS="); //saves current in preset - if (pos > 0) savePreset(getNumVal(&req, pos)); - - //apply preset - if (updateVal(&req, "PL=", &presetCycCurr, presetCycleMin, presetCycleMax)) { - applyPreset(presetCycCurr, presetApplyBri, presetApplyCol, presetApplyFx); - } - //cronixie #ifndef WLED_DISABLE_CRONIXIE //mode, 1 countdown diff --git a/wled00/wled19_json.ino b/wled00/wled19_json.ino index 5f36c58b..be80c3fe 100644 --- a/wled00/wled19_json.ino +++ b/wled00/wled19_json.ino @@ -66,6 +66,9 @@ bool deserializeState(JsonObject root) { strip.applyToAllSelected = false; bool stateResponse = root["v"] | false; + + int ps = root["ps"] | -1; + if (ps >= 0) applyPreset(ps); bri = root["bri"] | bri; @@ -86,9 +89,6 @@ bool deserializeState(JsonObject root) transitionDelayTemp *= 100; jsonTransitionOnce = true; } - - int ps = root["ps"] | -1; - if (ps >= 0) applyPreset(ps); int cy = root["pl"] | -2; if (cy > -2) presetCyclingEnabled = (cy >= 0); From e8481818c8d3687ea1af54b361ca06f6c6d5ead5 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sun, 9 Feb 2020 19:10:29 +0100 Subject: [PATCH 081/232] Small adjustments to new DMX options --- wled00/html_settings.h | 6 +++--- wled00/wled00.ino | 14 ++++++++++---- wled00/wled01_eeprom.ino | 3 +++ wled00/wled02_xml.ino | 22 ++++++++++++---------- wled00/wled04_file.ino | 4 ++-- wled00/wled07_notify.ino | 33 +++++++++++++++++---------------- wled00/wled08_led.ino | 2 +- wled00/wled19_json.ino | 2 +- 8 files changed, 49 insertions(+), 37 deletions(-) diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 50c7dbf6..c9c89e80 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -205,14 +205,14 @@ Receive UDP realtime:

E1.31 (sACN)
Use E1.31 multicast:
E1.31 start universe:
-Reboot required. Check out LedFx!

+Reboot required. Check out LedFx!
DMX start address:
DMX mode: disabled
Single RGB (3 Channels for all LEDs: Red Green Blue)
Single DRGB (4 Channels for all LEDs: Dimmer Red Green Blue)
- Effect (11 Channels parametrizing Effects: Dimmer Effect Speed Intensity Palette PriRed PriGreen PriBlue SecRed SecGreen SecBlue)
+ Effect (11 Channels for properties: Dimmer FX Speed Intensity Palette PriR PriG PriB SecR SecG SecB)
Multiple RGB (3 Channels for each LED: Red Green Blue)
- Multiple DRGB (1+3 Channels for each LED: Dimmer Red1 Green1 Blue1 Red2 Green2 Blue2...)
+ Multiple DRGB (1+3 Channels for each LED: Dimmer R1 G1 B1 R2 G2 B2...)

Timeout: ms
Force max brightness:
Disable realtime gamma correction:
diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 7d44092f..62a08261 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -191,8 +191,6 @@ uint16_t DMXAddress; //DMX start address of fixture, a. uint8_t DMXOldDimmer = 0; //only update brightness on change uint8_t e131LastSequenceNumber = 0; //to detect packet loss bool e131Multicast = false; //multicast or unicast -IPAddress e131ClientIP; //E1.31 client IP -String e131ClientUA; //E1.31 client User Agent bool mqttEnabled = false; char mqttDeviceTopic[33] = ""; //main MQTT topic (individual per device, default is wled/mac) @@ -366,10 +364,17 @@ bool presetApplyBri = false, presetApplyCol = true, presetApplyFx = true; bool saveCurrPresetCycConf = false; //realtime -bool realtimeActive = false; +#define REALTIME_MODE_INACTIVE 0 +#define REALTIME_MODE_GENERIC 1 +#define REALTIME_MODE_UDP 2 +#define REALTIME_MODE_HYPERION 3 +#define REALTIME_MODE_E131 4 +#define REALTIME_MODE_ADALIGHT 5 +byte realtimeMode = 0; IPAddress realtimeIP = (0,0,0,0); unsigned long realtimeTimeout = 0; + //mqtt long lastMqttReconnectAttempt = 0; long lastInterfaceUpdate = 0; @@ -431,6 +436,7 @@ AsyncMqttClient* mqtt = NULL; void colorFromUint32(uint32_t,bool=false); void serveMessage(AsyncWebServerRequest*,uint16_t,String,String,byte); void handleE131Packet(e131_packet_t*, IPAddress); +void arlsLock(uint32_t,byte); void handleOverlayDraw(); #define E131_MAX_UNIVERSE_COUNT 9 @@ -531,7 +537,7 @@ void loop() { yield(); if (doReboot) reset(); - if (!realtimeActive) //block stuff if WARLS/Adalight is enabled + if (!realtimeMode) //block stuff if WARLS/Adalight is enabled { if (apActive) dnsServer.processNextRequest(); #ifndef WLED_DISABLE_OTA diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino index 285b9bf6..ec4ea2a3 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled01_eeprom.ino @@ -479,6 +479,9 @@ void loadSettingsFromEEPROM(bool first) { DMXAddress = EEPROM.read(2197) + ((EEPROM.read(2198) << 8) & 0xFF00); DMXMode = EEPROM.read(2199); + } else { + DMXAddress = 1; + DMXMode = DMX_MODE_MULTIPLE_RGB; } diff --git a/wled00/wled02_xml.ino b/wled00/wled02_xml.ino index 9062afeb..aac4a9be 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/wled02_xml.ino @@ -58,27 +58,25 @@ char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr) oappend(""); oappendi(presetCyclingEnabled); oappend(""); - if (realtimeActive) + if (realtimeMode) { String mesg = "Live "; - if (realtimeIP[0] == 0) + if (realtimeMode == REALTIME_MODE_E131) { mesg += "E1.31 mode "; mesg += DMXMode; - mesg += " at fixture DMX Address "; + mesg += " at DMX Address "; mesg += DMXAddress; - mesg += " from '"; - mesg += e131ClientUA; - mesg += "' ("; - mesg += e131ClientIP[0]; + mesg += " from "; + mesg += realtimeIP[0]; for (int i = 1; i < 4; i++) { mesg += "."; - mesg += e131ClientIP[i]; + mesg += realtimeIP[i]; } - mesg += ") seq="; + mesg += " seq="; mesg += e131LastSequenceNumber; - } else { + } else if (realtimeMode == REALTIME_MODE_UDP || realtimeMode == REALTIME_MODE_HYPERION) { mesg += "UDP from "; mesg += realtimeIP[0]; for (int i = 1; i < 4; i++) @@ -86,6 +84,10 @@ char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr) mesg += "."; mesg += realtimeIP[i]; } + } else if (realtimeMode == REALTIME_MODE_ADALIGHT) { + mesg += "USB Adalight"; + } else { //generic + mesg += "data"; } oappend((char*)mesg.c_str()); } else { diff --git a/wled00/wled04_file.ino b/wled00/wled04_file.ino index 155337ba..de752328 100644 --- a/wled00/wled04_file.ino +++ b/wled00/wled04_file.ino @@ -67,8 +67,8 @@ void handleSerial() setRealtimePixel(pixel++, red, green, blue, 0); if (--count > 0) state = AdaState::Data_Red; else { - if (!realtimeActive && bri == 0) strip.setBrightness(briLast); - arlsLock(realtimeTimeoutMs); + if (!realtimeMode && bri == 0) strip.setBrightness(briLast); + arlsLock(realtimeTimeoutMs, REALTIME_MODE_ADALIGHT); strip.show(); state = AdaState::Header_A; diff --git a/wled00/wled07_notify.ino b/wled00/wled07_notify.ino index 870c5a85..ab55df28 100644 --- a/wled00/wled07_notify.ino +++ b/wled00/wled07_notify.ino @@ -72,14 +72,14 @@ void notify(byte callMode, bool followUp=false) } -void arlsLock(uint32_t timeoutMs) +void arlsLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC) { - if (!realtimeActive){ + if (!realtimeMode){ for (uint16_t i = 0; i < ledCount; i++) { strip.setPixelColor(i,0,0,0,0); } - realtimeActive = true; + realtimeMode = md; } realtimeTimeout = millis() + timeoutMs; if (timeoutMs == 255001 || timeoutMs == 65000) realtimeTimeout = UINT32_MAX; @@ -103,8 +103,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ e131LastSequenceNumber = p->sequence_number; // update status info - e131ClientIP = clientIP; - e131ClientUA = String((char *)p->source_name); + realtimeIP = clientIP; uint16_t uni = htons(p->universe); uint8_t previousUniverses = uni - e131Universe; @@ -141,7 +140,6 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ if (DMXOldDimmer != p->property_values[DMXAddress+0]) { DMXOldDimmer = p->property_values[DMXAddress+0]; bri = p->property_values[DMXAddress+0]; - strip.setBrightness(bri); } if (p->property_values[DMXAddress+1] < MODE_COUNT) effectCurrent = p->property_values[DMXAddress+ 1]; @@ -154,8 +152,13 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ colSec[0] = p->property_values[DMXAddress+ 8]; colSec[1] = p->property_values[DMXAddress+ 9]; colSec[2] = p->property_values[DMXAddress+10]; - fadeTransition = false; // act fast - colorUpdated(5); // don't send UDP + if (dmxChannels-DMXAddress+1 > 11) + { + col[3] = p->property_values[DMXAddress+11]; //white + colSec[3] = p->property_values[DMXAddress+12]; + } + transitionDelayTemp = 0; // act fast + colorUpdated(3); // don't send UDP return; // don't activate realtime live mode break; @@ -212,7 +215,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ break; } - arlsLock(realtimeTimeoutMs); + arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); e131NewData = true; } @@ -231,12 +234,10 @@ void handleNotifications() } //unlock strip when realtime UDP times out - if (realtimeActive && millis() > realtimeTimeout) + if (realtimeMode && millis() > realtimeTimeout) { strip.setBrightness(bri); - realtimeActive = false; - //strip.setMode(effectCurrent); - realtimeIP[0] = 0; + realtimeMode = REALTIME_MODE_INACTIVE; } //receive UDP notifications @@ -253,7 +254,7 @@ void handleNotifications() DEBUG_PRINTLN(rgbUdp.remoteIP()); uint8_t lbuf[packetSize]; rgbUdp.read(lbuf, packetSize); - arlsLock(realtimeTimeoutMs); + arlsLock(realtimeTimeoutMs, REALTIME_MODE_HYPERION); uint16_t id = 0; for (uint16_t i = 0; i < packetSize -2; i += 3) { @@ -273,7 +274,7 @@ void handleNotifications() notifierUdp.read(udpIn, packetSize); //wled notifier, block if realtime packets active - if (udpIn[0] == 0 && !realtimeActive && receiveNotifications) + if (udpIn[0] == 0 && !realtimeMode && receiveNotifications) { //ignore notification if received within a second after sending a notification ourselves if (millis() - notificationSentTime < 1000) return; @@ -340,7 +341,7 @@ void handleNotifications() realtimeTimeout = 0; return; } else { - arlsLock(udpIn[1]*1000 +1); + arlsLock(udpIn[1]*1000 +1, REALTIME_MODE_UDP); } if (udpIn[0] == 1) //warls { diff --git a/wled00/wled08_led.ino b/wled00/wled08_led.ino index 786049a0..7aa79c16 100644 --- a/wled00/wled08_led.ino +++ b/wled00/wled08_led.ino @@ -33,7 +33,7 @@ void toggleOnOff() void setAllLeds() { - if (!realtimeActive || !arlsForceMaxBri) + if (!realtimeMode || !arlsForceMaxBri) { double d = briT*briMultiplier; int val = d/100; diff --git a/wled00/wled19_json.ino b/wled00/wled19_json.ino index 5f36c58b..0a9328ba 100644 --- a/wled00/wled19_json.ino +++ b/wled00/wled19_json.ino @@ -255,7 +255,7 @@ void serializeInfo(JsonObject root) root["name"] = serverDescription; root["udpport"] = udpPort; - root["live"] = realtimeActive; + root["live"] = (bool)realtimeMode; root["fxcount"] = strip.getModeCount(); root["palcount"] = strip.getPaletteCount(); From a004d1647d9e7097c5bb4d55635361a14f79b0c3 Mon Sep 17 00:00:00 2001 From: Def3nder <33399267+Def3nder@users.noreply.github.com> Date: Tue, 11 Feb 2020 22:43:15 +0100 Subject: [PATCH 082/232] Simplify Code for Gradient / Loading effect (#671) --- wled00/FX.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 43428fd4..479714fe 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -1177,15 +1177,12 @@ uint16_t WS2812FX::mode_fire_flicker(void) { * Gradient run base function */ uint16_t WS2812FX::gradient_base(bool loading) { - uint16_t counter = now * (SEGMENT.speed >> 3) + 1; - SEGENV.step = counter * SEGLEN >> 16; - if (SEGMENT.speed == 0) SEGENV.step = SEGLEN >> 1; - if (SEGENV.call == 0) SEGENV.step = 0; - float per,val; //0.0 = sec 1.0 = pri - float brd = SEGMENT.intensity; - if (!loading) brd = SEGMENT.intensity/2; + uint16_t counter = now * ((SEGMENT.speed >> 2) + 1); + uint16_t pp = counter * SEGLEN >> 16; + if (SEGENV.call == 0) pp = 0; + float val; //0.0 = sec 1.0 = pri + float brd = loading ? SEGMENT.intensity : SEGMENT.intensity/2; if (brd <1.0) brd = 1.0; - int pp = SEGENV.step; int p1 = pp-SEGLEN; int p2 = pp+SEGLEN; @@ -1197,9 +1194,8 @@ uint16_t WS2812FX::gradient_base(bool loading) { } else { val = min(abs(pp-i),min(abs(p1-i),abs(p2-i))); } - per = val/brd; - if (per >1.0) per = 1.0; - setPixelColor(i, color_blend(SEGCOLOR(0), color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), per*255)); + val = (brd > val) ? val/brd * 255 : 255; + setPixelColor(i, color_blend(SEGCOLOR(0), color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), val)); } return FRAMETIME; From 9839cc638627a7f99d16082feb625c026dd77e37 Mon Sep 17 00:00:00 2001 From: fishbone-git Date: Sat, 15 Feb 2020 14:42:09 +0100 Subject: [PATCH 083/232] port multi-comet to use the memory allocation method --- wled00/FX.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 479714fe..b9e202a3 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -1451,13 +1451,14 @@ uint16_t WS2812FX::mode_tricolor_fade(void) */ uint16_t WS2812FX::mode_multi_comet(void) { - uint32_t cycleTime = 20 + (2 * (uint32_t)(255 - SEGMENT.speed)); + uint32_t cycleTime = 10 + (uint32_t)(255 - SEGMENT.speed); uint32_t it = now / cycleTime; if (SEGENV.step == it) return FRAMETIME; - + if (!SEGENV.allocateData(sizeof(uint16_t) * 8)) return mode_static(); //allocation failed + fade_out(SEGMENT.intensity); - - static uint16_t comets[] = {UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX}; + + uint16_t* comets = reinterpret_cast(SEGENV.data); for(uint8_t i=0; i < 8; i++) { if(comets[i] < SEGLEN) { From ffd958a1b3818cd42937369b3103eaed1ed7b42c Mon Sep 17 00:00:00 2001 From: Aircoookie Date: Sat, 15 Feb 2020 16:18:06 +0100 Subject: [PATCH 084/232] Quick color selectors --- wled00/data/index.htm | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/wled00/data/index.htm b/wled00/data/index.htm index 1cc88083..0c9864ca 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -103,7 +103,7 @@ td { .slider-icon { - transform: translateY(2px); + transform: translateY(3px); color: #ddd; } @@ -141,7 +141,7 @@ td { .tab { background-color: #222; color: #ddd; - filter: drop-shadow(0px 0px 2px #000); + //filter: drop-shadow(0px 0px 2px #000); } .bot { @@ -1337,12 +1337,28 @@ function selectSlot(b) { updateTrail(d.getElementById('sliderW')); } +var lasth = 0; +function pC(col) +{ + if (col == "rnd") + { + col = {h: 0, s: 0, v: 100}; + col.s = Math.floor((Math.random() * 50) + 50); + do { + col.h = Math.floor(Math.random() * 360); + } while (Math.abs(col.h - lasth) < 50); + lasth = col.h; + } + cpick.color.set(col); + setColor(false); +} + function setColor(fromPicker) { var cd = d.getElementById('csl').children; if (fromPicker && cd[csel].style.backgroundColor == 'rgb(0, 0, 0)') cpick.color.setChannel('hsv', 'v', 100); cd[csel].style.backgroundColor = cpick.color.rgbString; whites[csel] = d.getElementById('sliderW').value; - col = cpick.color.rgb; + var col = cpick.color.rgb; var obj = {"seg": {"col": [[col.r, col.g, col.b, whites[csel]],[],[]]}}; if (csel == 1) { obj = {"seg": {"col": [[],[col.r, col.g, col.b, whites[csel]],[]]}}; From 92a9d7d26a5be56fe42200e7e2682a7f69f9cc9b Mon Sep 17 00:00:00 2001 From: Aircoookie Date: Sat, 15 Feb 2020 16:42:05 +0100 Subject: [PATCH 085/232] Attempt to fix travis --- platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/platformio.ini b/platformio.ini index 84200955..cb1e42e8 100644 --- a/platformio.ini +++ b/platformio.ini @@ -80,6 +80,7 @@ arduino_core_2_6_2 = espressif8266@2.3.1 arduino_core_stage = https://github.com/platformio/platform-espressif8266.git#feature/stage platform = ${common:esp8266.arduino_core_2_6_2} build_flags = + -D ESP8266 -D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH -Wl,-Teagle.flash.4m1m.ld ;;;; Required for core > v2.5.0 or staging version 4MB Flash 3MB SPIFFs From d0b9f53d8c4d486284102f0975c80209bf0fb8fe Mon Sep 17 00:00:00 2001 From: fishbone-git Date: Sat, 15 Feb 2020 20:07:15 +0100 Subject: [PATCH 086/232] water ripple effect with a dimmed rainbow background --- wled00/FX.cpp | 32 +++++++++++++++++++++++++++++--- wled00/FX.h | 10 +++++++--- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 479714fe..53843fe1 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -2133,7 +2133,7 @@ typedef struct Ripple { uint16_t pos; } ripple; -uint16_t WS2812FX::mode_ripple() +uint16_t WS2812FX::ripple_base(bool rainbow) { uint16_t maxRipples = 1 + (SEGLEN >> 2); if (maxRipples > 100) maxRipples = 100; @@ -2143,8 +2143,25 @@ uint16_t WS2812FX::mode_ripple() Ripple* ripples = reinterpret_cast(SEGENV.data); - fill(SEGCOLOR(1)); - + // ranbow background or chosen background, all very dim. + if (rainbow) { + if (SEGENV.call ==0) { + SEGENV.aux0 = random8(); + SEGENV.aux1 = random8(); + } + if (SEGENV.aux0 == SEGENV.aux1) { + SEGENV.aux1 = random8(); + } + else if (SEGENV.aux1 > SEGENV.aux0) { + SEGENV.aux0++; + } else { + SEGENV.aux0--; + } + fill(color_blend(color_wheel(SEGENV.aux0),BLACK,235)); + } else { + fill(SEGCOLOR(1)); + } + //draw wave for (uint16_t i = 0; i < maxRipples; i++) { @@ -2188,6 +2205,15 @@ uint16_t WS2812FX::mode_ripple() return FRAMETIME; } +uint16_t WS2812FX::mode_ripple(void) { + return ripple_base(false); +} + +uint16_t WS2812FX::mode_ripple_rainbow(void) { + return ripple_base(true); +} + + // TwinkleFOX by Mark Kriegsman: https://gist.github.com/kriegsman/756ea6dcae8e30845b5a // diff --git a/wled00/FX.h b/wled00/FX.h index d4bf4ff5..998c546b 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -91,7 +91,7 @@ #define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) #define IS_SELECTED ((SEGMENT.options & SELECTED) == SELECTED ) -#define MODE_COUNT 99 +#define MODE_COUNT 100 #define FX_MODE_STATIC 0 #define FX_MODE_BLINK 1 @@ -192,6 +192,7 @@ #define FX_MODE_DRIP 96 #define FX_MODE_PLASMA 97 #define FX_MODE_PERCENT 98 +#define FX_MODE_RIPPLE_RAINBOW 99 class WS2812FX { typedef uint16_t (WS2812FX::*mode_ptr)(void); @@ -379,6 +380,7 @@ class WS2812FX { _mode[FX_MODE_DRIP] = &WS2812FX::mode_drip; _mode[FX_MODE_PLASMA] = &WS2812FX::mode_plasma; _mode[FX_MODE_PERCENT] = &WS2812FX::mode_percent; + _mode[FX_MODE_RIPPLE_RAINBOW] = &WS2812FX::mode_ripple_rainbow; _brightness = DEFAULT_BRIGHTNESS; currentPalette = CRGBPalette16(CRGB::Black); @@ -562,7 +564,8 @@ class WS2812FX { mode_popcorn(void), mode_drip(void), mode_plasma(void), - mode_percent(void); + mode_percent(void), + mode_ripple_rainbow(void); private: @@ -607,6 +610,7 @@ class WS2812FX { dissolve(uint32_t), chase(uint32_t, uint32_t, uint32_t, bool), gradient_base(bool), + ripple_base(bool), police_base(uint32_t, uint32_t), running(uint32_t, uint32_t), tricolor_chase(uint32_t, uint32_t), @@ -642,7 +646,7 @@ const char JSON_mode_names[] PROGMEM = R"=====([ "Scanner Dual","Stream 2","Oscillate","Pride 2015","Juggle","Palette","Fire 2012","Colorwaves","Bpm","Fill Noise", "Noise 1","Noise 2","Noise 3","Noise 4","Colortwinkles","Lake","Meteor","Meteor Smooth","Railway","Ripple", "Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks Starburst", -"Fireworks 1D","Bouncing Balls","Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn","Drip","Plasma","Percent" +"Fireworks 1D","Bouncing Balls","Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn","Drip","Plasma","Percent", "Ripple Rainbow" ])====="; From 3b52770a1b013422c8757db0c9716123cc953ce9 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sun, 16 Feb 2020 17:34:28 +0100 Subject: [PATCH 087/232] Fix segments when reverse --- wled00/FX_fcn.cpp | 5 ++++- wled00/wled00.ino | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 7c87a292..c1ee11af 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -125,9 +125,12 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) /* Set all the pixels in the group, ensuring _skipFirstMode is honored */ bool reversed = reverseMode ^ IS_REVERSE; uint16_t realIndex = realPixelIndex(i); + for (uint16_t j = 0; j < SEGMENT.grouping; j++) { int16_t indexSet = realIndex + (reversed ? -j : j); - if (indexSet >= SEGMENT.start && indexSet < SEGMENT.stop) bus->SetPixelColor(indexSet + skip, col); + int16_t indexSetRev = indexSet; + if (reverseMode) indexSetRev = _length - 1 - indexSet; + if (indexSetRev >= SEGMENT.start && indexSetRev < SEGMENT.stop) bus->SetPixelColor(indexSet + skip, col); } } else { //live data, etc. if (reverseMode) i = _length - 1 - i; diff --git a/wled00/wled00.ino b/wled00/wled00.ino index f186f5e8..245ba3a3 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -90,7 +90,7 @@ #endif //version code in format yymmddb (b = daily build) -#define VERSION 2002021 +#define VERSION 2002161 char versionString[] = "0.9.1"; From 0b7e0e166c42e629fae9db80ed6c7e12802a1ed7 Mon Sep 17 00:00:00 2001 From: fishbone-git <53238640+fishbone-git@users.noreply.github.com> Date: Mon, 17 Feb 2020 10:18:36 +0100 Subject: [PATCH 088/232] pio workaround for pio4.2.0 (#688) --- platformio.ini | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/platformio.ini b/platformio.ini index cb1e42e8..dd9bb0bc 100644 --- a/platformio.ini +++ b/platformio.ini @@ -128,6 +128,8 @@ build_flags = ${common:esp8266.build_flags} lib_deps = ${common.lib_deps_external} +lib_compat_mode = strict +lib_ignore = AsynTCP [env:d1_mini] board = d1_mini @@ -140,6 +142,8 @@ build_flags = ${common:esp8266.build_flags} lib_deps = ${common.lib_deps_external} +lib_compat_mode = strict +lib_ignore = AsynTCP [env:esp01_1m] board = esp01_1m @@ -154,6 +158,8 @@ build_flags = -D WLED_DISABLE_INFRARED lib_deps = ${common.lib_deps_external} +lib_compat_mode = strict +lib_ignore = AsynTCP [env:esp01] board = esp01 @@ -167,6 +173,8 @@ build_flags = -D WLED_DISABLE_INFRARED lib_deps = ${common.lib_deps_external} +lib_compat_mode = strict +lib_ignore = AsynTCP [env:esp07] board = esp07 @@ -179,6 +187,8 @@ build_flags = ${common:esp8266.build_flags} lib_deps = ${common.lib_deps_external} +lib_compat_mode = strict +lib_ignore = AsynTCP # see: http://docs.platformio.org/en/latest/platforms/espressif32.html [env:esp32dev] @@ -194,6 +204,7 @@ lib_deps = ${common.lib_deps_external} lib_ignore = ESPAsyncUDP +lib_compat_mode = strict [env:esp8285_4CH_MagicHome] board = esp8285 @@ -208,6 +219,8 @@ build_flags = -D WLED_USE_ANALOG_LEDS lib_deps = ${common.lib_deps_external} +lib_compat_mode = strict +lib_ignore = AsynTCP [env:esp8285_4CH_H801] board = esp8285 @@ -223,6 +236,8 @@ build_flags = -D WLED_USE_H801 lib_deps = ${common.lib_deps_external} +lib_compat_mode = strict +lib_ignore = AsynTCP [env:esp8285_5CH_H801] board = esp8285 @@ -239,4 +254,6 @@ build_flags = -D WLED_ENABLE_5CH_LEDS lib_deps = ${common.lib_deps_external} +lib_compat_mode = strict +lib_ignore = AsynTCP From 5befcd24b526cb5822f0dec3de071ba44af6fe2e Mon Sep 17 00:00:00 2001 From: Def3nder <33399267+Def3nder@users.noreply.github.com> Date: Mon, 17 Feb 2020 11:01:05 +0100 Subject: [PATCH 089/232] Effect "Heartbeat" (#680) --- wled00/FX.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ wled00/FX.h | 6 +++++- wled00/NpbWrapper.h | 9 +++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 36dcecc6..bb290038 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -3119,4 +3119,44 @@ uint16_t WS2812FX::mode_percent(void) { } return FRAMETIME; +} + +uint16_t WS2812FX::mode_heartbeat(void) { + static unsigned long lastBeat = 0; + static bool secondBeatActive = false; + + uint8_t bpm = 40 + (SEGMENT.speed >> 4); + uint32_t msPerBeat = (60000 / bpm); + uint32_t secondBeat = (msPerBeat / 3); + + // Get and translate the segment's size option + uint8_t size = 2 << ((SEGMENT.options >> 1) & 0x03); // 2,4,8,16 + + // copy pixels from the middle of the segment to the edges + uint16_t bytesPerPixelBlock = size * 4; + uint16_t centerOffset = (SEGLEN / 2) * 4; + uint16_t byteCount = centerOffset - bytesPerPixelBlock; + memmove(bus->GetPixels(), bus->GetPixels() + bytesPerPixelBlock, byteCount); + memmove(bus->GetPixels() + centerOffset + bytesPerPixelBlock, bus->GetPixels() + centerOffset, byteCount); + + fade_out(255 - SEGMENT.intensity); + + unsigned long beatTimer = millis() - lastBeat; + if((beatTimer > secondBeat) && !secondBeatActive) { // time for the second beat? + uint16_t startLed = (SEGLEN / 2) - size; + for (uint16_t i = startLed; i < startLed + (size * 2); i++) { + setPixelColor(i, SEGMENT.colors[0]); + } + secondBeatActive = 1; + } + if(beatTimer > msPerBeat) { // time to reset the beat timer? + uint16_t startLed = (SEGLEN / 2) - size; + for (uint16_t i = startLed; i < startLed + (size * 2); i++) { + setPixelColor(i, SEGMENT.colors[0]); + } + secondBeatActive = 0; + lastBeat = millis(); + } + + return FRAMETIME; } \ No newline at end of file diff --git a/wled00/FX.h b/wled00/FX.h index 998c546b..79629ece 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -193,6 +193,7 @@ #define FX_MODE_PLASMA 97 #define FX_MODE_PERCENT 98 #define FX_MODE_RIPPLE_RAINBOW 99 +#define FX_MODE_HEARTBEAT 100 class WS2812FX { typedef uint16_t (WS2812FX::*mode_ptr)(void); @@ -380,6 +381,7 @@ class WS2812FX { _mode[FX_MODE_DRIP] = &WS2812FX::mode_drip; _mode[FX_MODE_PLASMA] = &WS2812FX::mode_plasma; _mode[FX_MODE_PERCENT] = &WS2812FX::mode_percent; + _mode[FX_MODE_HEARTBEAT] = &WS2812FX::mode_heartbeat; _mode[FX_MODE_RIPPLE_RAINBOW] = &WS2812FX::mode_ripple_rainbow; _brightness = DEFAULT_BRIGHTNESS; @@ -565,6 +567,7 @@ class WS2812FX { mode_drip(void), mode_plasma(void), mode_percent(void), + mode_heartbeat(void); mode_ripple_rainbow(void); @@ -646,7 +649,8 @@ const char JSON_mode_names[] PROGMEM = R"=====([ "Scanner Dual","Stream 2","Oscillate","Pride 2015","Juggle","Palette","Fire 2012","Colorwaves","Bpm","Fill Noise", "Noise 1","Noise 2","Noise 3","Noise 4","Colortwinkles","Lake","Meteor","Meteor Smooth","Railway","Ripple", "Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks Starburst", -"Fireworks 1D","Bouncing Balls","Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn","Drip","Plasma","Percent", "Ripple Rainbow" +"Fireworks 1D","Bouncing Balls","Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn","Drip","Plasma","Percent","Ripple Rainbow", +"Heartbeat" ])====="; diff --git a/wled00/NpbWrapper.h b/wled00/NpbWrapper.h index 9f391556..54d21ac1 100644 --- a/wled00/NpbWrapper.h +++ b/wled00/NpbWrapper.h @@ -287,6 +287,15 @@ public: return 0; } + uint8_t* GetPixels(void) + { + switch (_type) { + case NeoPixelType_Grb: return _pGrb->Pixels(); break; + case NeoPixelType_Grbw: return _pGrbw->Pixels(); break; + } + return 0; + } + private: NeoPixelType _type; From b42847c135ae94729127b3b21ab22857164aba0f Mon Sep 17 00:00:00 2001 From: Aircoookie Date: Mon, 17 Feb 2020 11:12:39 +0100 Subject: [PATCH 090/232] Fix compilation --- wled00/FX.h | 2 +- wled00/wled00.ino | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index 79629ece..149476e3 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -567,8 +567,8 @@ class WS2812FX { mode_drip(void), mode_plasma(void), mode_percent(void), + mode_ripple_rainbow(void), mode_heartbeat(void); - mode_ripple_rainbow(void); private: diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 245ba3a3..2edc101a 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -90,7 +90,7 @@ #endif //version code in format yymmddb (b = daily build) -#define VERSION 2002161 +#define VERSION 2002171 char versionString[] = "0.9.1"; From 3062786bb3072225ef7b34913e7d0b7231877aa5 Mon Sep 17 00:00:00 2001 From: Aircoookie Date: Mon, 17 Feb 2020 18:02:14 +0100 Subject: [PATCH 091/232] Corrected effect mode count --- wled00/FX.h | 2 +- wled00/wled00.ino | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index 149476e3..aac07b68 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -91,7 +91,7 @@ #define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) #define IS_SELECTED ((SEGMENT.options & SELECTED) == SELECTED ) -#define MODE_COUNT 100 +#define MODE_COUNT 101 #define FX_MODE_STATIC 0 #define FX_MODE_BLINK 1 diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 2edc101a..43cfe83c 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -90,7 +90,7 @@ #endif //version code in format yymmddb (b = daily build) -#define VERSION 2002171 +#define VERSION 2002172 char versionString[] = "0.9.1"; From 9e62db52370e0c952656573d64095f17ec0b62e7 Mon Sep 17 00:00:00 2001 From: Aircoookie Date: Tue, 18 Feb 2020 14:22:20 +0100 Subject: [PATCH 092/232] Update html with PC mode --- wled00/data/index.htm | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/wled00/data/index.htm b/wled00/data/index.htm index 0c9864ca..0391645d 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -658,6 +658,12 @@ input[type=number]::-webkit-outer-spin-button { float: none; } } + +@media all and (max-width: 1250px) { + #buttonPcm { + display: none; + } +} @@ -673,6 +679,7 @@ input[type=number]::-webkit-outer-spin-button { +

Brightness

@@ -833,6 +840,7 @@ var savedPresets = 0; var currentPreset = -1; var lastUpdate = 0; var segCount = 0, ledCount = 0, lowestUnused = 0, maxSeg = 0; +var pcMode = false; var d = document; const ranges = RangeTouch.setup('input[type="range"]', {}); @@ -872,10 +880,12 @@ function updateTablinks(tabI) for (i = 0; i < tablinks.length; i++) { tablinks[i].className = tablinks[i].className.replace(" active", ""); } + if (pcMode) return; tablinks[tabI].className += " active"; } function openTab(tabI) { + if (pcMode) return; var i, tabcontent, tablinks; iSlide = tabI; _C.classList.toggle('smooth', false); @@ -1035,7 +1045,7 @@ function requestJson(command, verbose = true) { e1 = d.getElementById('fxlist'); e2 = d.getElementById('selectPalette'); - url = command ? 'http://10.10.1.26/json/state':'http://10.10.1.26/json'; + url = command ? '/json/state':'/json'; type = command ? 'post':'get'; if (command) @@ -1394,17 +1404,18 @@ let iSlide = 0, x0 = null, y0 = null, scrollS = 0, locked = false, w; function unify(e) { return e.changedTouches ? e.changedTouches[0] : e } function lock(e) { + if (pcMode) return; var l = e.target.classList; if (l.contains('noslide') || l.contains('iro__wheel__saturation') || l.contains('iro__slider__value') || l.contains('iro__slider')) return; x0 = unify(e).clientX; y0 = unify(e).clientY; scrollS = d.getElementsByClassName("tabcontent")[iSlide].scrollTop; - _C.classList.toggle('smooth', !(locked = true)) + _C.classList.toggle('smooth', !(locked = true)) } function drag(e) { - if (!locked) return; + if (!locked || pcMode) return; if (d.getElementsByClassName("tabcontent")[iSlide].scrollTop != scrollS) { move(e); return; } @@ -1417,7 +1428,7 @@ function drag(e) { } function move(e) { - if(!locked) return; + if(!locked || pcMode) return; var dx = unify(e).clientX - x0, s = Math.sign(dx), f = +(s*dx/w).toFixed(2); @@ -1437,6 +1448,23 @@ function size() { w = window.innerWidth; _C.style.setProperty('--tp', d.getElementById('top').clientHeight + "px"); _C.style.setProperty('--bt', d.getElementById('bot').clientHeight + "px"); + if (w < 1250) togglePcMode(); +} + +function togglePcMode() +{ + if (w < 1250 && !pcMode) return; + openTab(0); + pcMode = !pcMode; + if (w < 1250) pcMode = false; + updateTablinks(0); + d.getElementById('buttonPcm').className = (pcMode) ? "active":""; + if (pcMode) + { + _C.style.width = '100%'; + } else { + _C.style.width = '400%'; + } } size(); From 1fd0383f693fc9b80c27a8ff47f952e9ffb8bbc9 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Tue, 18 Feb 2020 18:52:47 +0100 Subject: [PATCH 093/232] Various improvements UI Quick color selectors UI PC mode Different Heartbeat effect PoliceAll improvements Clarified sync settings --- wled00/FX.cpp | 94 +- wled00/FX.h | 4 +- wled00/data/index.htm | 1970 +++++++++++++-------------- wled00/html_settings.h | 27 +- wled00/html_ui.h | 2937 ++++++++++++++++++++-------------------- wled00/wled00.ino | 2 +- 6 files changed, 2547 insertions(+), 2487 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index bb290038..680c7724 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -1219,11 +1219,10 @@ uint16_t WS2812FX::mode_loading(void) { //American Police Light with all LEDs Red and Blue -uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2) +uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2, bool all) { uint16_t counter = now * ((SEGMENT.speed >> 2) +1); uint16_t idexR = (counter * SEGLEN) >> 16; - uint8_t size = 1 + SEGMENT.intensity >> 3; if (idexR >= SEGLEN) idexR = 0; uint16_t topindex = SEGLEN >> 1; @@ -1231,19 +1230,31 @@ uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2) if (SEGENV.call == 0) SEGENV.aux0 = idexR; if (idexB >= SEGLEN) idexB = 0; //otherwise overflow on odd number of LEDs - for (uint8_t i=0; i <= size; i++) { - setPixelColor(idexR+i, color1); - setPixelColor(idexB+i, color2); - } - if (SEGENV.aux0 != idexR) { - uint8_t gap = (SEGENV.aux0 < idexR)? idexR - SEGENV.aux0:SEGLEN - SEGENV.aux0 + idexR; - for (uint8_t i = 0; i <= gap ; i++) { - if ((idexR - i) < 0) idexR = SEGLEN-1 + i; - if ((idexB - i) < 0) idexB = SEGLEN-1 + i; - setPixelColor(idexR-i, color1); - setPixelColor(idexB-i, color2); + if (all) { //different algo, ensuring immediate fill + if (idexB > idexR) { + fill(color2); + for (uint16_t i = idexR; i < idexB; i++) setPixelColor(i, color1); + } else { + fill(color1); + for (uint16_t i = idexB; i < idexR; i++) setPixelColor(i, color2); + } + } else { //regular dot-only mode + uint8_t size = 1 + SEGMENT.intensity >> 3; + if (size > SEGLEN/2) size = 1+ SEGLEN/2; + for (uint8_t i=0; i <= size; i++) { + setPixelColor(idexR+i, color1); + setPixelColor(idexB+i, color2); + } + if (SEGENV.aux0 != idexR) { + uint8_t gap = (SEGENV.aux0 < idexR)? idexR - SEGENV.aux0:SEGLEN - SEGENV.aux0 + idexR; + for (uint8_t i = 0; i <= gap ; i++) { + if ((idexR - i) < 0) idexR = SEGLEN-1 + i; + if ((idexB - i) < 0) idexB = SEGLEN-1 + i; + setPixelColor(idexR-i, color1); + setPixelColor(idexB-i, color2); + } + SEGENV.aux0 = idexR; } - SEGENV.aux0 = idexR; } return FRAMETIME; @@ -1253,7 +1264,7 @@ uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2) //American Police Light with all LEDs Red and Blue uint16_t WS2812FX::mode_police_all() { - return police_base(RED, BLUE); + return police_base(RED, BLUE, true); } @@ -1262,14 +1273,14 @@ uint16_t WS2812FX::mode_police() { fill(SEGCOLOR(1)); - return police_base(RED, BLUE); + return police_base(RED, BLUE, false); } //Police All with custom colors uint16_t WS2812FX::mode_two_areas() { - return police_base(SEGCOLOR(0), SEGCOLOR(1)); + return police_base(SEGCOLOR(0), SEGCOLOR(1), true); } @@ -1279,7 +1290,7 @@ uint16_t WS2812FX::mode_two_dots() fill(SEGCOLOR(2)); uint32_t color2 = (SEGCOLOR(1) == SEGCOLOR(2)) ? SEGCOLOR(0) : SEGCOLOR(1); - return police_base(SEGCOLOR(0), color2); + return police_base(SEGCOLOR(0), color2, false); } @@ -3087,8 +3098,8 @@ uint16_t WS2812FX::mode_percent(void) { uint16_t active_leds = (percent < 100) ? SEGLEN * percent / 100.0 : SEGLEN * (200 - percent) / 100.0; - if (SEGENV.call == 0) SEGENV.step = 0; - uint8_t size = (1 + ((SEGMENT.speed * SEGLEN) >> 11)) & 0xFF ; + uint8_t size = (1 + ((SEGMENT.speed * SEGLEN) >> 11)); + if (SEGMENT.speed == 255) size = 255; if (percent < 100) { for (uint16_t i = 0; i < SEGLEN; i++) { @@ -3122,41 +3133,28 @@ uint16_t WS2812FX::mode_percent(void) { } uint16_t WS2812FX::mode_heartbeat(void) { - static unsigned long lastBeat = 0; - static bool secondBeatActive = false; - uint8_t bpm = 40 + (SEGMENT.speed >> 4); uint32_t msPerBeat = (60000 / bpm); uint32_t secondBeat = (msPerBeat / 3); - // Get and translate the segment's size option - uint8_t size = 2 << ((SEGMENT.options >> 1) & 0x03); // 2,4,8,16 + uint32_t bri_lower = SEGENV.aux1; + bri_lower = bri_lower * 2042 / (2048 + SEGMENT.intensity); + SEGENV.aux1 = bri_lower; - // copy pixels from the middle of the segment to the edges - uint16_t bytesPerPixelBlock = size * 4; - uint16_t centerOffset = (SEGLEN / 2) * 4; - uint16_t byteCount = centerOffset - bytesPerPixelBlock; - memmove(bus->GetPixels(), bus->GetPixels() + bytesPerPixelBlock, byteCount); - memmove(bus->GetPixels() + centerOffset + bytesPerPixelBlock, bus->GetPixels() + centerOffset, byteCount); - - fade_out(255 - SEGMENT.intensity); - - unsigned long beatTimer = millis() - lastBeat; - if((beatTimer > secondBeat) && !secondBeatActive) { // time for the second beat? - uint16_t startLed = (SEGLEN / 2) - size; - for (uint16_t i = startLed; i < startLed + (size * 2); i++) { - setPixelColor(i, SEGMENT.colors[0]); - } - secondBeatActive = 1; + unsigned long beatTimer = millis() - SEGENV.step; + if((beatTimer > secondBeat) && !SEGENV.aux0) { // time for the second beat? + SEGENV.aux1 = UINT16_MAX; //full bri + SEGENV.aux0 = 1; } if(beatTimer > msPerBeat) { // time to reset the beat timer? - uint16_t startLed = (SEGLEN / 2) - size; - for (uint16_t i = startLed; i < startLed + (size * 2); i++) { - setPixelColor(i, SEGMENT.colors[0]); - } - secondBeatActive = 0; - lastBeat = millis(); + SEGENV.aux1 = UINT16_MAX; //full bri + SEGENV.aux0 = 0; + SEGENV.step = millis(); + } + + for (uint16_t i = 0; i < SEGLEN; i++) { + setPixelColor(i, color_blend(color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), 255 - (SEGENV.aux1 >> 8))); } return FRAMETIME; -} \ No newline at end of file +} diff --git a/wled00/FX.h b/wled00/FX.h index aac07b68..abfc11d3 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -381,8 +381,8 @@ class WS2812FX { _mode[FX_MODE_DRIP] = &WS2812FX::mode_drip; _mode[FX_MODE_PLASMA] = &WS2812FX::mode_plasma; _mode[FX_MODE_PERCENT] = &WS2812FX::mode_percent; - _mode[FX_MODE_HEARTBEAT] = &WS2812FX::mode_heartbeat; _mode[FX_MODE_RIPPLE_RAINBOW] = &WS2812FX::mode_ripple_rainbow; + _mode[FX_MODE_HEARTBEAT] = &WS2812FX::mode_heartbeat; _brightness = DEFAULT_BRIGHTNESS; currentPalette = CRGBPalette16(CRGB::Black); @@ -614,7 +614,7 @@ class WS2812FX { chase(uint32_t, uint32_t, uint32_t, bool), gradient_base(bool), ripple_base(bool), - police_base(uint32_t, uint32_t), + police_base(uint32_t, uint32_t, bool), running(uint32_t, uint32_t), tricolor_chase(uint32_t, uint32_t), twinklefox_base(bool), diff --git a/wled00/data/index.htm b/wled00/data/index.htm index 0391645d..d9d1ccc9 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -1,668 +1,668 @@ - - - - - - WLED - + + + + + +WLED + @@ -672,144 +672,144 @@ input[type=number]::-webkit-outer-spin-button {
-
-
- - - - - - -
-
-

Brightness

-
- -
- -
-
-
-
- -
+
+
+ + + + + + +
+
+

Brightness

+
+ +
+ +
+
+
+
+ +
-
-
-
-

White channel

-
- -
-
-
-
-
-
-
-
-
-

-
-
-
-
-
R
-
-
- - - -
-

Color palette

-
- - -
-
+
+
+
+

White channel

+
+ +
+
+
+
+
+
+
+
+
+

+
+
+
+
+
R
+
+
+ + + +
+

Color palette

+
+ + +
+
-
-

Effect speed

-
- -
- -
-
-
-

Effect intensity

-
- -
- -
-
-
-

Effect mode

-
- -
-
- Loading... -
-
-
- -
-
- Loading... -
-
- -
-
+
+

Effect speed

+
+ +
+ +
+
+
+

Effect intensity

+
+ +
+ +
+
+
+

Effect mode

+
+ +
+
+Loading... +
+
+
-
-

Load from slot

- - - -
- - - -
- - - -
- - - -
-
- Slot 16 can save all segments.

-
- First preset:
- Last preset:
- Time per preset: s
- Transition: s -
+
+
+Loading... +
+
+ +
+
+ +
+

Load from slot

+ + + +
+ + + +
+ + + +
+ + + +
+
+Slot 16 can save all segments.

+
+First preset:
+Last preset:
+Time per preset: s
+Transition: s +
- - - - + + + +
@@ -817,16 +817,16 @@ input[type=number]::-webkit-outer-spin-button { + + + + + + WLED + @@ -672,144 +672,144 @@ display: none;
-
-
- - - - - - -
-
-

Brightness

-
- -
- -
-
-
-
- -
+
+
+ + + + + + +
+
+

Brightness

+
+ +
+ +
+
+
+
+ +
-
-
-
-

White channel

-
- -
-
-
-
-
-
-
-
-
-

-
-
-
-
-
R
-
-
- - - -
-

Color palette

-
- - -
-
+
+
+
+

White channel

+
+ +
+
+
+
+
+
+
+
+
+

+
+
+
+
+
R
+
+
+ + + +
+

Color palette

+
+ + +
+
-
-

Effect speed

-
- -
- -
-
-
-

Effect intensity

-
- -
- -
-
-
-

Effect mode

-
- -
-
-Loading... -
-
-
+
+

Effect speed

+
+ +
+ +
+
+
+

Effect intensity

+
+ +
+ +
+
+
+

Effect mode

+
+ +
+
+ Loading... +
+
+
+ +
+
+ Loading... +
+
+ +
+
-
-
-Loading... -
-
- -
-
- -
-

Load from slot

- - - -
- - - -
- - - -
- - - -
-
-Slot 16 can save all segments.

-
-First preset:
-Last preset:
-Time per preset: s
-Transition: s -
+
+

Load from slot

+ + + +
+ + + +
+ + + +
+ + + +
+
+ Slot 16 can save all segments.

+
+ First preset:
+ Last preset:
+ Time per preset: s
+ Transition: s +
- - - - + + + +
@@ -817,16 +817,16 @@ Transition: /*! -* iro.js v4.4.0 -* 2016-2019 James Daniel -* Licensed under MPL 2.0 -* github.com/jaames/iro.js -*/ + * iro.js v4.4.0 + * 2016-2019 James Daniel + * Licensed under MPL 2.0 + * github.com/jaames/iro.js + */ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.iro=e()}(this,function(){"use strict";var c=function(){},i={},h=[],u=[];function p(t,e){var o,n,r,i,s=arguments,a=u;for(i=arguments.length;2t){var n=function(e){var t="".concat(e).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);return t?Math.max(0,(t[1]?t[1].length:0)-(t[2]?+t[2]:0)):0}(t);return parseFloat(e.toFixed(n))}return Math.round(e/t)*t}return function(){function n(e,r){(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,n),l.element(e)?this.element=e:l.string(e)&&(this.element=document.querySelector(e)),l.element(this.element)&&l.empty(this.element.rangeTouch)&&(this.config=Object.assign({},t,r),this.init())}return r=n,i=[{key:"setup",value:function(e){var r=1(t=100/c.width*(u.clientX-c.left))?t=0:100t?t-=(100-2*t)*f:50 3000) { -requestJson({}, true); -} + if (!document.hidden && new Date () - lastUpdate > 3000) { + requestJson({}, true); + } } function onLoad() { -size(); - -var cd = d.getElementById('csl').children; -for (i = 0; i < cd.length; i++) { -cd[i].style.backgroundColor = "rgb(0, 0, 0)"; -} -selectSlot(0); -updateTablinks(0); -resetUtil(); -cpick.on("input:end", function() { -setColor(true); -}); -setTimeout(requestJson, 25); -d.addEventListener("visibilitychange", handleVisibilityChange, false); - -d.getElementById("cv").style.opacity=0; + size(); + + var cd = d.getElementById('csl').children; + for (i = 0; i < cd.length; i++) { + cd[i].style.backgroundColor = "rgb(0, 0, 0)"; + } + selectSlot(0); + updateTablinks(0); + resetUtil(); + cpick.on("input:end", function() { + setColor(true); + }); + setTimeout(requestJson, 25); + d.addEventListener("visibilitychange", handleVisibilityChange, false); + + d.getElementById("cv").style.opacity=0; } function updateTablinks(tabI) { -tablinks = d.getElementsByClassName("tablinks"); -for (i = 0; i < tablinks.length; i++) { -tablinks[i].className = tablinks[i].className.replace(" active", ""); -} -if (pcMode) return; -tablinks[tabI].className += " active"; + tablinks = d.getElementsByClassName("tablinks"); + for (i = 0; i < tablinks.length; i++) { + tablinks[i].className = tablinks[i].className.replace(" active", ""); + } + if (pcMode) return; + tablinks[tabI].className += " active"; } function openTab(tabI) { -if (pcMode) return; -var i, tabcontent, tablinks; -iSlide = tabI; -_C.classList.toggle('smooth', false); -_C.style.setProperty('--i', iSlide); -updateTablinks(tabI); + if (pcMode) return; + var i, tabcontent, tablinks; + iSlide = tabI; + _C.classList.toggle('smooth', false); + _C.style.setProperty('--i', iSlide); + updateTablinks(tabI); } var timeout; function showToast(text, error = false) { -var x = d.getElementById("toast"); -x.innerHTML = text; -x.className = error ? "error":"show"; -clearTimeout(timeout); -x.style.animation = 'none'; -x.offsetHeight; -x.style.animation = null; -timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900); + var x = d.getElementById("toast"); + x.innerHTML = text; + x.className = error ? "error":"show"; + clearTimeout(timeout); + x.style.animation = 'none'; + x.offsetHeight; + x.style.animation = null; + timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900); } function showErrorToast() { -showToast('Connection to light failed. Please refresh the page. If the issue persists, reboot your ESP.', true); + showToast('Connection to light failed. Please refresh the page. If the issue persists, reboot your ESP.', true); } function clearErrorToast() { -d.getElementById("toast").className = d.getElementById("toast").className.replace("error", ""); + d.getElementById("toast").className = d.getElementById("toast").className.replace("error", ""); } function populateSegments(s) { -var cn = ""; -segCount = 0, lowestUnused = 0; + var cn = ""; + segCount = 0, lowestUnused = 0; + + for (y in s.seg) + { + segCount++; + + var inst=s.seg[y]; + var i = parseInt(inst.id); + if (i == lowestUnused) lowestUnused = i+1; + + cn += `
+ + +
+ + + + + + + + + +
Start LEDStop LED
+ + + + + + + + + +
GroupingSpacing
+
${inst.stop - inst.start} LED${inst.stop - inst.start > 1 ? "s":""} (${Math.ceil((inst.stop - inst.start)/(inst.grp + inst.spc))} virtual)
+ + + +
+

`; + } -for (y in s.seg) -{ -segCount++; - -var inst=s.seg[y]; -var i = parseInt(inst.id); -if (i == lowestUnused) lowestUnused = i+1; - -cn += `
- - -
- - - - - - - - - -
Start LEDStop LED
- - - - - - - - - -
GroupingSpacing
-
${inst.stop - inst.start} LED${inst.stop - inst.start > 1 ? "s":""} (${Math.ceil((inst.stop - inst.start)/(inst.grp + inst.spc))} virtual)
- - - -
-

`; -} - -d.getElementById('segcont').innerHTML = cn; -if (lowestUnused >= maxSeg) { -d.getElementById('segutil').innerHTML = 'Maximum number of segments reached.'; -noNewSegs = true; -} else if (noNewSegs) { -resetUtil(); -noNewSegs = false; -} + d.getElementById('segcont').innerHTML = cn; + if (lowestUnused >= maxSeg) { + d.getElementById('segutil').innerHTML = 'Maximum number of segments reached.'; + noNewSegs = true; + } else if (noNewSegs) { + resetUtil(); + noNewSegs = false; + } } function updateTrail(e) { -var progress = e.value *100 /255; -progress = parseInt(progress); -var val = `linear-gradient(90deg, white ${progress}%, #444 ${progress}%)`; -e.parentNode.getElementsByClassName('sliderdisplay')[0].style.background = val; + var progress = e.value *100 /255; + progress = parseInt(progress); + var val = `linear-gradient(90deg, white ${progress}%, #444 ${progress}%)`; + e.parentNode.getElementsByClassName('sliderdisplay')[0].style.background = val; } function updateLen(s) { -var start = parseInt(d.getElementById(`seg${s}s`).value); -var stop = parseInt(d.getElementById(`seg${s}e`).value); -var len = stop - start; -var out = "(delete)" -if (len > 1) { -out = `${len} LEDs`; -} else if (len == 1) { -out = "1 LED" -} - -if (d.getElementById(`seg${s}grp`) != null) -{ -var grp = parseInt(d.getElementById(`seg${s}grp`).value); -var spc = parseInt(d.getElementById(`seg${s}spc`).value); -if (grp == 0) grp = 1; -var virt = Math.ceil(len/(grp + spc)); -if (!isNaN(virt) && (grp > 1 || spc > 0)) out += ` (${virt} virtual)`; -} - -d.getElementById(`seg${s}len`).innerHTML = out; + var start = parseInt(d.getElementById(`seg${s}s`).value); + var stop = parseInt(d.getElementById(`seg${s}e`).value); + var len = stop - start; + var out = "(delete)" + if (len > 1) { + out = `${len} LEDs`; + } else if (len == 1) { + out = "1 LED" + } + + if (d.getElementById(`seg${s}grp`) != null) + { + var grp = parseInt(d.getElementById(`seg${s}grp`).value); + var spc = parseInt(d.getElementById(`seg${s}spc`).value); + if (grp == 0) grp = 1; + var virt = Math.ceil(len/(grp + spc)); + if (!isNaN(virt) && (grp > 1 || spc > 0)) out += ` (${virt} virtual)`; + } + + d.getElementById(`seg${s}len`).innerHTML = out; } function updateUI() { -d.getElementById('buttonPower').className = (isOn) ? "active":""; -d.getElementById('buttonNl').className = (nlA) ? "active":""; -d.getElementById('buttonSync').className = (syncSend) ? "active":""; + d.getElementById('buttonPower').className = (isOn) ? "active":""; + d.getElementById('buttonNl').className = (nlA) ? "active":""; + d.getElementById('buttonSync').className = (syncSend) ? "active":""; -d.getElementById('fxb' + selectedFx).style.backgroundColor = "#666"; -updateTrail(d.getElementById('sliderBri')); -updateTrail(d.getElementById('sliderSpeed')); -updateTrail(d.getElementById('sliderIntensity')); -updateTrail(d.getElementById('sliderW')); -if (isRgbw) d.getElementById('wwrap').style.display = "block"; + d.getElementById('fxb' + selectedFx).style.backgroundColor = "#666"; + updateTrail(d.getElementById('sliderBri')); + updateTrail(d.getElementById('sliderSpeed')); + updateTrail(d.getElementById('sliderIntensity')); + updateTrail(d.getElementById('sliderW')); + if (isRgbw) d.getElementById('wwrap').style.display = "block"; + + var btns = document.getElementsByClassName("psts"); + for (i = 0; i < btns.length; i++) { + btns[i].className = btns[i].className.replace(" active", ""); + if ((savedPresets >> i) & 0x01) btns[i].className += " stored"; + } + if (currentPreset > 0 && currentPreset <= btns.length) btns[currentPreset -1].className += " active"; -var btns = document.getElementsByClassName("psts"); -for (i = 0; i < btns.length; i++) { -btns[i].className = btns[i].className.replace(" active", ""); -if ((savedPresets >> i) & 0x01) btns[i].className += " stored"; -} -if (currentPreset > 0 && currentPreset <= btns.length) btns[currentPreset -1].className += " active"; - -spal = d.getElementById("selectPalette"); -spal.style.backgroundColor = (spal.selectedIndex > 0) ? "#666":"#333"; + spal = d.getElementById("selectPalette"); + spal.style.backgroundColor = (spal.selectedIndex > 0) ? "#666":"#333"; } function compare(a, b) { -if (a.name < b.name){ -return -1; -} -return 1; + if (a.name < b.name){ + return -1; + } + return 1; } var jsonTimeout; function requestJson(command, verbose = true) { -lastUpdate = new Date(); -if (!jsonTimeout) jsonTimeout = setTimeout(showErrorToast, 3000); -var req = null; -e1 = d.getElementById('fxlist'); -e2 = d.getElementById('selectPalette'); + lastUpdate = new Date(); + if (!jsonTimeout) jsonTimeout = setTimeout(showErrorToast, 3000); + var req = null; + e1 = d.getElementById('fxlist'); + e2 = d.getElementById('selectPalette'); -url = command ? '/json/state':'/json'; + url = command ? '/json/state':'/json'; + + type = command ? 'post':'get'; + if (command) + { + command.v = verbose; + req = JSON.stringify(command); + } + fetch + (url, { + method: type, + headers: { + "Content-type": "application/json; charset=UTF-8" + }, + body: req + }) + .then(res => { + if (!res.ok) { + showErrorToast(); + } + return res.json(); + }) + .then(json => { + clearTimeout(jsonTimeout); + jsonTimeout = null; + clearErrorToast(); + if (!json) showToast('Empty response', true); + if (json.success) return; + var s = json; + if (!command) { + var x='',y=''; + json.effects.shift(); //remove solid + for (i in json.effects) json.effects[i] = {"id": parseInt(i)+1, "name":json.effects[i]}; + json.effects.sort(compare); + for (i in json.effects) { + x += '
'; + } -type = command ? 'post':'get'; -if (command) -{ -command.v = verbose; -req = JSON.stringify(command); -} -fetch -(url, { -method: type, -headers: { -"Content-type": "application/json; charset=UTF-8" -}, -body: req -}) -.then(res => { -if (!res.ok) { -showErrorToast(); -} -return res.json(); -}) -.then(json => { -clearTimeout(jsonTimeout); -jsonTimeout = null; -clearErrorToast(); -if (!json) showToast('Empty response', true); -if (json.success) return; -var s = json; -if (!command) { -var x='',y=''; -json.effects.shift(); //remove solid -for (i in json.effects) json.effects[i] = {"id": parseInt(i)+1, "name":json.effects[i]}; -json.effects.sort(compare); -for (i in json.effects) { -x += '
'; -} + json.palettes.shift(); //remove default + for (i in json.palettes) json.palettes[i] = {"id": parseInt(i)+1, "name":json.palettes[i]}; + json.palettes.sort(compare); + for (i in json.palettes) { + y += ''; + } + e1.innerHTML=x; e2.innerHTML=y; + var info = json.info; + var name = info.name; + d.getElementById('namelabel').innerHTML = name; + if (name === "Dinnerbone") d.documentElement.style.transform = "rotate(180deg)"; + if (info.live) name = "(Live) " + name; + d.title = name; + isRgbw = info.leds.wv; + ledCount = info.leds.count; + syncTglRecv = info.str; + maxSeg = info.leds.maxseg; + s = json.state; + } + isOn = s.on; + d.getElementById('sliderBri').value= s.bri; + nlA = s.nl.on; + nlDur = s.nl.dur; + syncSend = s.udpn.send; + savedPresets = s.pss; + currentPreset = s.ps; + d.getElementById('cyToggle').checked = (s.pl < 0) ? false : true; + d.getElementById('cycs').value = s.ccnf.min; + d.getElementById('cyce').value = s.ccnf.max; + d.getElementById('cyct').value = s.ccnf.time /10; + d.getElementById('cyctt').value = s.transition /10; + + var selc=0; var ind=0; + populateSegments(s); + for (i in s.seg) + { + if(s.seg[i].sel) {selc = ind; break;} ind++; + } + var i=s.seg[selc]; + if (!i) { + showToast('No Segments!', true); + updateUI(); + return; + } + var cd = d.getElementById('csl').children; + for (e = 2; e >= 0; e--) + { + cd[e].style.backgroundColor = "rgb(" + i.col[e][0] + "," + i.col[e][1] + "," + i.col[e][2] + ")"; + if (isRgbw) whites[e] = i.col[e][3]; + selectSlot(csel); + } + d.getElementById('sliderSpeed').value = whites[csel]; -json.palettes.shift(); //remove default -for (i in json.palettes) json.palettes[i] = {"id": parseInt(i)+1, "name":json.palettes[i]}; -json.palettes.sort(compare); -for (i in json.palettes) { -y += ''; -} -e1.innerHTML=x; e2.innerHTML=y; -var info = json.info; -var name = info.name; -d.getElementById('namelabel').innerHTML = name; -if (name === "Dinnerbone") d.documentElement.style.transform = "rotate(180deg)"; -if (info.live) name = "(Live) " + name; -d.title = name; -isRgbw = info.leds.wv; -ledCount = info.leds.count; -syncTglRecv = info.str; -maxSeg = info.leds.maxseg; -s = json.state; -} -isOn = s.on; -d.getElementById('sliderBri').value= s.bri; -nlA = s.nl.on; -nlDur = s.nl.dur; -syncSend = s.udpn.send; -savedPresets = s.pss; -currentPreset = s.ps; -d.getElementById('cyToggle').checked = (s.pl < 0) ? false : true; -d.getElementById('cycs').value = s.ccnf.min; -d.getElementById('cyce').value = s.ccnf.max; -d.getElementById('cyct').value = s.ccnf.time /10; -d.getElementById('cyctt').value = s.transition /10; + d.getElementById('sliderSpeed').value = i.sx; + d.getElementById('sliderIntensity').value = i.ix; -var selc=0; var ind=0; -populateSegments(s); -for (i in s.seg) -{ -if(s.seg[i].sel) {selc = ind; break;} ind++; -} -var i=s.seg[selc]; -if (!i) { -showToast('No Segments!', true); -updateUI(); -return; -} -var cd = d.getElementById('csl').children; -for (e = 2; e >= 0; e--) -{ -cd[e].style.backgroundColor = "rgb(" + i.col[e][0] + "," + i.col[e][1] + "," + i.col[e][2] + ")"; -if (isRgbw) whites[e] = i.col[e][3]; -selectSlot(csel); -} -d.getElementById('sliderSpeed').value = whites[csel]; + d.getElementById('fxb' + selectedFx).style.backgroundColor = "#333"; + selectedFx = i.fx; + e2.value = i.pal; + if (!command) d.getElementById('Effects').scrollTop = d.getElementById('fxb' + selectedFx).offsetTop - d.getElementById('Effects').clientHeight/1.8; -d.getElementById('sliderSpeed').value = i.sx; -d.getElementById('sliderIntensity').value = i.ix; - -d.getElementById('fxb' + selectedFx).style.backgroundColor = "#333"; -selectedFx = i.fx; -e2.value = i.pal; -if (!command) d.getElementById('Effects').scrollTop = d.getElementById('fxb' + selectedFx).offsetTop - d.getElementById('Effects').clientHeight/1.8; - -if (s.error) showToast('WLED error ' + s.error, true); -updateUI(); -}) -.catch(function (error) { -showToast(error, true); -}) + if (s.error) showToast('WLED error ' + s.error, true); + updateUI(); + }) + .catch(function (error) { + showToast(error, true); + }) } function togglePower() { -isOn = !isOn; -var obj = {"on": isOn}; -obj.transition = parseInt(d.getElementById('cyctt').value*10); -requestJson(obj); + isOn = !isOn; + var obj = {"on": isOn}; + obj.transition = parseInt(d.getElementById('cyctt').value*10); + requestJson(obj); } function toggleNl() { -nlA = !nlA; -if (nlA) -{ -showToast('Nightlight active. Your light will turn off after ' + nlDur + ' minutes.'); -} else { -showToast('Nightlight deactivated.'); -} -var obj = {"nl": {"on": nlA}}; -requestJson(obj); + nlA = !nlA; + if (nlA) + { + showToast('Nightlight active. Your light will turn off after ' + nlDur + ' minutes.'); + } else { + showToast('Nightlight deactivated.'); + } + var obj = {"nl": {"on": nlA}}; + requestJson(obj); } function toggleSync() { -syncSend = !syncSend; -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; -requestJson(obj); + syncSend = !syncSend; + 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; + requestJson(obj); } function toggleLiveview() { -isLv = !isLv; -d.getElementById('liveview').style.display = (isLv) ? "block":"none"; -d.getElementById('liveview').src = (isLv) ? "/liveview":"about:blank"; -d.getElementById('buttonSr').className = (isLv) ? "active":""; + isLv = !isLv; + d.getElementById('liveview').style.display = (isLv) ? "block":"none"; + d.getElementById('liveview').src = (isLv) ? "/liveview":"about:blank"; + d.getElementById('buttonSr').className = (isLv) ? "active":""; } function makeSeg() { -var ns = 0; -if (lowestUnused > 0) { -var pend = d.getElementById(`seg${lowestUnused -1}e`).value; -if (pend < ledCount) ns = pend; -} -var cn = `
- -
- - - - - - - - - -
Start LEDStop LED
-
${ledCount - ns} LEDs
- -
-
`; -d.getElementById('segutil').innerHTML = cn; + var ns = 0; + if (lowestUnused > 0) { + var pend = d.getElementById(`seg${lowestUnused -1}e`).value; + if (pend < ledCount) ns = pend; + } + var cn = `
+ +
+ + + + + + + + + +
Start LEDStop LED
+
${ledCount - ns} LEDs
+ +
+
`; + d.getElementById('segutil').innerHTML = cn; } function resetUtil() { -var cn = `
`; -d.getElementById('segutil').innerHTML = cn; + var cn = `
`; + d.getElementById('segutil').innerHTML = cn; } function selSeg(s){ -var sel = d.getElementById(`seg${s}sel`).checked; -var obj = {"seg": {"id": s, "sel": sel}}; -requestJson(obj); + var sel = d.getElementById(`seg${s}sel`).checked; + var obj = {"seg": {"id": s, "sel": sel}}; + requestJson(obj); } function setSeg(s){ -var start = parseInt(d.getElementById(`seg${s}s`).value); -var stop = parseInt(d.getElementById(`seg${s}e`).value); -if (stop <= start) {delSeg(s); return;}; -var obj = {"seg": {"id": s, "start": start, "stop": stop}}; -if (d.getElementById(`seg${s}grp`)) -{ -var grp = parseInt(d.getElementById(`seg${s}grp`).value); -var spc = parseInt(d.getElementById(`seg${s}spc`).value); -obj.seg.grp = grp; -obj.seg.spc = spc; -} -requestJson(obj); + var start = parseInt(d.getElementById(`seg${s}s`).value); + var stop = parseInt(d.getElementById(`seg${s}e`).value); + if (stop <= start) {delSeg(s); return;}; + var obj = {"seg": {"id": s, "start": start, "stop": stop}}; + if (d.getElementById(`seg${s}grp`)) + { + var grp = parseInt(d.getElementById(`seg${s}grp`).value); + var spc = parseInt(d.getElementById(`seg${s}spc`).value); + obj.seg.grp = grp; + obj.seg.spc = spc; + } + requestJson(obj); } function delSeg(s){ -if (segCount < 2) { -showToast("You need to have multiple segments in order to delete one."); -return; -} -expanded[s] = false; -segCount--; -var obj = {"seg": {"id": s, "stop": 0}}; -requestJson(obj); + if (segCount < 2) { + showToast("You need to have multiple segments in order to delete one."); + return; + } + expanded[s] = false; + segCount--; + var obj = {"seg": {"id": s, "stop": 0}}; + requestJson(obj); } function setRev(s){ -var rev = d.getElementById(`seg${s}rev`).checked; -var obj = {"seg": {"id": s, "rev": rev}}; -requestJson(obj); + var rev = d.getElementById(`seg${s}rev`).checked; + var obj = {"seg": {"id": s, "rev": rev}}; + requestJson(obj); } function setX(ind) { -var obj = {"seg": {"fx": parseInt(ind)}}; -requestJson(obj); + var obj = {"seg": {"fx": parseInt(ind)}}; + requestJson(obj); } function setPalette() { -var obj = {"seg": {"pal": parseInt(d.getElementById('selectPalette').value)}}; -requestJson(obj); + var obj = {"seg": {"pal": parseInt(d.getElementById('selectPalette').value)}}; + requestJson(obj); } function setBri() { -var obj = {"bri": parseInt(d.getElementById('sliderBri').value)}; -obj.transition = parseInt(d.getElementById('cyctt').value*10); -requestJson(obj); + var obj = {"bri": parseInt(d.getElementById('sliderBri').value)}; + obj.transition = parseInt(d.getElementById('cyctt').value*10); + requestJson(obj); } function setSpeed() { -var obj = {"seg": {"sx": parseInt(d.getElementById('sliderSpeed').value)}}; -requestJson(obj); + var obj = {"seg": {"sx": parseInt(d.getElementById('sliderSpeed').value)}}; + requestJson(obj); } function setIntensity() { -var obj = {"seg": {"ix": parseInt(d.getElementById('sliderIntensity').value)}}; -requestJson(obj); + var obj = {"seg": {"ix": parseInt(d.getElementById('sliderIntensity').value)}}; + requestJson(obj); } function toggleCY() { -var obj = {"pl" : -1}; -if (d.getElementById('cyToggle').checked) -{ -obj = {"pl": 0, "ccnf": {"min": parseInt(d.getElementById('cycs').value), "max": parseInt(d.getElementById('cyce').value), "time": parseInt(d.getElementById('cyct').value*10)}}; -obj.transition = parseInt(d.getElementById('cyctt').value*10); -} - -requestJson(obj); + var obj = {"pl" : -1}; + if (d.getElementById('cyToggle').checked) + { + obj = {"pl": 0, "ccnf": {"min": parseInt(d.getElementById('cycs').value), "max": parseInt(d.getElementById('cyce').value), "time": parseInt(d.getElementById('cyct').value*10)}}; + obj.transition = parseInt(d.getElementById('cyctt').value*10); + } + + requestJson(obj); } function togglePS() { -ps = !ps; - -var btns = document.getElementsByClassName("psts"); -for (i = 0; i < btns.length; i++) { -if (ps) { -btns[i].className += " saving"; -} else { -btns[i].className = btns[i].className.replace(" saving", ""); -} -} - -d.getElementById("psLabel").innerHTML = (ps) ? "Save to slot":"Load from slot"; + ps = !ps; + + var btns = document.getElementsByClassName("psts"); + for (i = 0; i < btns.length; i++) { + if (ps) { + btns[i].className += " saving"; + } else { + btns[i].className = btns[i].className.replace(" saving", ""); + } + } + + d.getElementById("psLabel").innerHTML = (ps) ? "Save to slot":"Load from slot"; } function setPreset(i) { -var obj = {"ps": i} -if ((savedPresets >> (i-1)) & 0x01) { -showToast("Loading config from slot " + i +"."); -} else { -showToast("Slot " + i +" is empty! Use saving mode to save the current config to it."); -} -if (ps) { -obj = {"psave": i}; -showToast("Saving config to slot " + i +"."); -} -requestJson(obj); + var obj = {"ps": i} + if ((savedPresets >> (i-1)) & 0x01) { + showToast("Loading config from slot " + i +"."); + } else { + showToast("Slot " + i +" is empty! Use saving mode to save the current config to it."); + } + if (ps) { + obj = {"psave": i}; + showToast("Saving config to slot " + i +"."); + } + requestJson(obj); } function selectSlot(b) { -csel = b; -var cd = d.getElementById('csl').children; -for (i = 0; i < cd.length; i++) { -cd[i].style.border="2px solid white"; -cd[i].style.margin="5px"; -cd[i].style.width="42px"; -} -cd[csel].style.border="5px solid white"; -cd[csel].style.margin="2px"; -cd[csel].style.width="50px"; -if (cpick.color.rgbString !== cd[csel].style.backgroundColor) { -cpick.color.set(cd[csel].style.backgroundColor); -} -d.getElementById('sliderW').value = whites[csel]; -updateTrail(d.getElementById('sliderW')); + csel = b; + var cd = d.getElementById('csl').children; + for (i = 0; i < cd.length; i++) { + cd[i].style.border="2px solid white"; + cd[i].style.margin="5px"; + cd[i].style.width="42px"; + } + cd[csel].style.border="5px solid white"; + cd[csel].style.margin="2px"; + cd[csel].style.width="50px"; + if (cpick.color.rgbString !== cd[csel].style.backgroundColor) { + cpick.color.set(cd[csel].style.backgroundColor); + } + d.getElementById('sliderW').value = whites[csel]; + updateTrail(d.getElementById('sliderW')); } var lasth = 0; function pC(col) { -if (col == "rnd") -{ -col = {h: 0, s: 0, v: 100}; -col.s = Math.floor((Math.random() * 50) + 50); -do { -col.h = Math.floor(Math.random() * 360); -} while (Math.abs(col.h - lasth) < 50); -lasth = col.h; -} -cpick.color.set(col); -setColor(false); + if (col == "rnd") + { + col = {h: 0, s: 0, v: 100}; + col.s = Math.floor((Math.random() * 50) + 50); + do { + col.h = Math.floor(Math.random() * 360); + } while (Math.abs(col.h - lasth) < 50); + lasth = col.h; + } + cpick.color.set(col); + setColor(false); } function setColor(fromPicker) { -var cd = d.getElementById('csl').children; -if (fromPicker && cd[csel].style.backgroundColor == 'rgb(0, 0, 0)') cpick.color.setChannel('hsv', 'v', 100); -cd[csel].style.backgroundColor = cpick.color.rgbString; -whites[csel] = d.getElementById('sliderW').value; -var col = cpick.color.rgb; -var obj = {"seg": {"col": [[col.r, col.g, col.b, whites[csel]],[],[]]}}; -if (csel == 1) { -obj = {"seg": {"col": [[],[col.r, col.g, col.b, whites[csel]],[]]}}; -} else if (csel == 2) { -obj = {"seg": {"col": [[],[],[col.r, col.g, col.b, whites[csel]]]}}; -} -obj.transition = parseInt(d.getElementById('cyctt').value*10); -if (d.getElementById('selectPalette').value > 5) { -d.getElementById('selectPalette').value = 0; -obj.seg.pal = 0; -} -requestJson(obj); + var cd = d.getElementById('csl').children; + if (fromPicker && cd[csel].style.backgroundColor == 'rgb(0, 0, 0)') cpick.color.setChannel('hsv', 'v', 100); + cd[csel].style.backgroundColor = cpick.color.rgbString; + whites[csel] = d.getElementById('sliderW').value; + var col = cpick.color.rgb; + var obj = {"seg": {"col": [[col.r, col.g, col.b, whites[csel]],[],[]]}}; + if (csel == 1) { + obj = {"seg": {"col": [[],[col.r, col.g, col.b, whites[csel]],[]]}}; + } else if (csel == 2) { + obj = {"seg": {"col": [[],[],[col.r, col.g, col.b, whites[csel]]]}}; + } + obj.transition = parseInt(d.getElementById('cyctt').value*10); + if (d.getElementById('selectPalette').value > 5) { + d.getElementById('selectPalette').value = 0; + obj.seg.pal = 0; + } + requestJson(obj); } function expand(i) { -expanded[i] = !expanded[i]; -d.getElementById('seg' +i).style.display = (expanded[i]) ? "block":"none"; -d.getElementById('sege' +i).style.transform = (expanded[i]) ? "rotate(180deg)":"rotate(0deg)" + expanded[i] = !expanded[i]; + d.getElementById('seg' +i).style.display = (expanded[i]) ? "block":"none"; + d.getElementById('sege' +i).style.transform = (expanded[i]) ? "rotate(180deg)":"rotate(0deg)" } function unfocusSliders() { -d.getElementById("sliderBri").blur(); -d.getElementById("sliderSpeed").blur(); -d.getElementById("sliderIntensity").blur(); + d.getElementById("sliderBri").blur(); + d.getElementById("sliderSpeed").blur(); + d.getElementById("sliderIntensity").blur(); } //sliding UI @@ -1404,67 +1404,67 @@ let iSlide = 0, x0 = null, y0 = null, scrollS = 0, locked = false, w; function unify(e) { return e.changedTouches ? e.changedTouches[0] : e } function lock(e) { -if (pcMode) return; -var l = e.target.classList; -if (l.contains('noslide') || l.contains('iro__wheel__saturation') || l.contains('iro__slider__value') || l.contains('iro__slider')) return; -x0 = unify(e).clientX; -y0 = unify(e).clientY; -scrollS = d.getElementsByClassName("tabcontent")[iSlide].scrollTop; + if (pcMode) return; + var l = e.target.classList; + if (l.contains('noslide') || l.contains('iro__wheel__saturation') || l.contains('iro__slider__value') || l.contains('iro__slider')) return; + x0 = unify(e).clientX; + y0 = unify(e).clientY; + scrollS = d.getElementsByClassName("tabcontent")[iSlide].scrollTop; -_C.classList.toggle('smooth', !(locked = true)) + _C.classList.toggle('smooth', !(locked = true)) } function drag(e) { -if (!locked || pcMode) return; -if (d.getElementsByClassName("tabcontent")[iSlide].scrollTop != scrollS) { -move(e); return; -} - -//if (Math.abs(unify(e).clientX - x0) > Math.abs(unify(e).clientY - y0)) { -//if (e.cancelable) e.preventDefault(); -//} - -_C.style.setProperty('--tx', `${Math.round(unify(e).clientX - x0)}px`) + if (!locked || pcMode) return; + if (d.getElementsByClassName("tabcontent")[iSlide].scrollTop != scrollS) { + move(e); return; + } + + //if (Math.abs(unify(e).clientX - x0) > Math.abs(unify(e).clientY - y0)) { + //if (e.cancelable) e.preventDefault(); + //} + + _C.style.setProperty('--tx', `${Math.round(unify(e).clientX - x0)}px`) } function move(e) { -if(!locked || pcMode) return; -var dx = unify(e).clientX - x0, s = Math.sign(dx), -f = +(s*dx/w).toFixed(2); + if(!locked || pcMode) return; + var dx = unify(e).clientX - x0, s = Math.sign(dx), + f = +(s*dx/w).toFixed(2); -if((iSlide > 0 || s < 0) && (iSlide < N - 1 || s > 0) && f > .12) { -_C.style.setProperty('--i', iSlide -= s); -f = 1 - f; -updateTablinks(iSlide); -} - -_C.style.setProperty('--tx', '0px'); -_C.style.setProperty('--f', f); -_C.classList.toggle('smooth', !(locked = false)); -x0 = null + if((iSlide > 0 || s < 0) && (iSlide < N - 1 || s > 0) && f > .12) { + _C.style.setProperty('--i', iSlide -= s); + f = 1 - f; + updateTablinks(iSlide); + } + + _C.style.setProperty('--tx', '0px'); + _C.style.setProperty('--f', f); + _C.classList.toggle('smooth', !(locked = false)); + x0 = null } function size() { -w = window.innerWidth; -_C.style.setProperty('--tp', d.getElementById('top').clientHeight + "px"); -_C.style.setProperty('--bt', d.getElementById('bot').clientHeight + "px"); -if (w < 1250) togglePcMode(); + w = window.innerWidth; + _C.style.setProperty('--tp', d.getElementById('top').clientHeight + "px"); + _C.style.setProperty('--bt', d.getElementById('bot').clientHeight + "px"); + if (w < 1250) togglePcMode(); } function togglePcMode() { -if (w < 1250 && !pcMode) return; -openTab(0); -pcMode = !pcMode; -if (w < 1250) pcMode = false; -updateTablinks(0); -d.getElementById('buttonPcm').className = (pcMode) ? "active":""; -if (pcMode) -{ -_C.style.width = '100%'; -} else { -_C.style.width = '400%'; -} + if (w < 1250 && !pcMode) return; + openTab(0); + pcMode = !pcMode; + if (w < 1250) pcMode = false; + updateTablinks(0); + d.getElementById('buttonPcm').className = (pcMode) ? "active":""; + if (pcMode) + { + _C.style.width = '100%'; + } else { + _C.style.width = '400%'; + } } size(); diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 5fe0e919..07e49cea 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -118,7 +118,15 @@ LED voltage (Max. current for a single LED):

LEDs are 4-channel type (RGBW):
-Auto-calculate white channel from RGB:
+Auto-calculate white channel from RGB:
+ +
Color order:
-AP IP: Not active
+AP IP: Not active
+

Experimental

+Disable WiFi sleep:
+Can help with connectivity issues.
+Do not enable if WiFi is working correctly, increases power consumption.
+
diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 0a38a37a..746f98d0 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -38,6 +38,9 @@ #include #include #include + extern "C" { + #include + } #else #include #include "esp_wifi.h" @@ -120,6 +123,8 @@ byte apBehavior = AP_BEHAVIOR_BOOT_NO_CONN; //access point opens when no conne IPAddress staticIP(0, 0, 0, 0); //static IP of ESP IPAddress staticGateway(0, 0, 0, 0); //gateway (router) IP IPAddress staticSubnet(255, 255, 255, 0); //most common subnet in home networks +bool noWifiSleep = false; //disabling modem sleep modes will increase heat output and power usage, but may help with connection issues + //LED CONFIG uint16_t ledCount = 30; //overcurrent prevented by ABL diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino index bc19bd1d..40b01ae5 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled01_eeprom.ino @@ -6,7 +6,7 @@ #define EEPSIZE 2560 //eeprom Version code, enables default settings instead of 0 init on update -#define EEPVER 15 +#define EEPVER 16 //0 -> old version, default //1 -> 0.4p 1711272 and up //2 -> 0.4p 1711302 and up @@ -23,6 +23,7 @@ //13-> 0.9.0-dev //14-> 0.9.0-b1 //15-> 0.9.0-b3 +//16-> 0.9.1 void commit() { @@ -131,6 +132,8 @@ void saveSettingsToEEPROM() EEPROM.write(368, abs(arlsOffset)); EEPROM.write(369, turnOnAtBoot); + EEPROM.write(370, noWifiSleep); + EEPROM.write(372, useRGBW); EEPROM.write(374, strip.paletteFade); EEPROM.write(375, strip.milliampsPerLed); //was apWaitTimeSecs up to 0.8.5 @@ -484,6 +487,11 @@ void loadSettingsFromEEPROM(bool first) DMXMode = DMX_MODE_MULTIPLE_RGB; } + //if (lastEEPROMversion > 15) + //{ + noWifiSleep = EEPROM.read(370); + //} + receiveDirect = !EEPROM.read(2200); notifyMacro = EEPROM.read(2201); diff --git a/wled00/wled02_xml.ino b/wled00/wled02_xml.ino index 731d71ac..3a7e12ba 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/wled02_xml.ino @@ -194,6 +194,8 @@ void getSettingsJS(byte subPage, char* dest) sappends('s',"AP",fapass); sappend('v',"AC",apChannel); + sappend('c',"WS",noWifiSleep); + if (WiFi.localIP()[0] != 0) //is connected { diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index 8ee395c3..6655dafd 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -46,6 +46,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) if (passlen == 0 || (passlen > 7 && !isAsterisksOnly(request->arg("AP").c_str(), 65))) strlcpy(apPass, request->arg("AP").c_str(), 65); int t = request->arg("AC").toInt(); if (t > 0 && t < 14) apChannel = t; + noWifiSleep = request->hasArg("WS"); + char k[3]; k[2] = 0; for (int i = 0; i<4; i++) { diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index b26358cb..8d41756b 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -190,9 +190,14 @@ void initConnection() #ifdef ESP8266 WiFi.hostname(serverDescription); #endif + WiFi.begin(clientSSID, clientPass); + #ifdef ARDUINO_ARCH_ESP32 + if (noWifiSleep) WiFi.setSleep(false); WiFi.setHostname(serverDescription); + #else + if (noWifiSleep) wifi_set_sleep_type(NONE_SLEEP_T); #endif } diff --git a/wled00/wled17_mqtt.ino b/wled00/wled17_mqtt.ino index efc308a9..aa72b9e1 100644 --- a/wled00/wled17_mqtt.ino +++ b/wled00/wled17_mqtt.ino @@ -129,6 +129,7 @@ bool initMqtt() strcpy(mqttStatusTopic, mqttDeviceTopic); strcat(mqttStatusTopic, "/status"); mqtt->setWill(mqttStatusTopic, 0, true, "offline"); + mqtt->setKeepAlive(60); mqtt->connect(); return true; } From 3e3f46a683c45a612104dcb2657b49d152e5510d Mon Sep 17 00:00:00 2001 From: Ser Ko Date: Thu, 20 Feb 2020 20:46:00 -0500 Subject: [PATCH 101/232] Added new mod --- .../readme.md | 25 ++ .../wled06_usermod.ino | 213 ++++++++++++++++++ 2 files changed, 238 insertions(+) create mode 100644 usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md create mode 100644 usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md new file mode 100644 index 00000000..7a489f03 --- /dev/null +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md @@ -0,0 +1,25 @@ +# Wemos D1 mini and Wemos32 mini shield +## Project repository +- [Original repository](https://github.com/srg74/WLED-wemos-shield) - WLED Wemos shield repository +## Features +* SSD1306 128x32 or 128x64 I2C OLED display +* On screen IP address, SSID and controller status (e.g. ON or OFF, recent effect) +* Auto display shutoff for saving display lifetime +* Dallas temperature sensor +* Reporting temperature to MQTT broker +* Relay for energy saving + +## Hardware +![Shield](https://github.com/srg74/WLED-wemos-shield/blob/master/resources/Images/Assembly_8.jpg) + +## Wiki +![Wiki](https://github.com/srg74/WLED-wemos-shield/wiki) + +## Functionality checked with +* Wemos D1 mini original v3.1 and clones +* Wemos32 mini +* PlatformIO +* SSD1306 128x32 I2C OLED display +* DS18B20 (temperature sensor) +* Push button (N.O. momentary switch) + diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino new file mode 100644 index 00000000..0e1b1e66 --- /dev/null +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino @@ -0,0 +1,213 @@ +#include // from https://github.com/olikraus/u8g2/ +#include //Dallastemperature sensor + +//The SCL and SDA pins are defined here. +//ESP8266 Wemos D1 mini boards use SCL=5 SDA=4 +#define U8X8_PIN_SCL 5 +#define U8X8_PIN_SDA 4 +//ESP32 Wemos32 mini boards use SCL=22 SDA=21 +//#define U8X8_PIN_SCL 22 +//#define U8X8_PIN_SDA 21 + +// Dallas sensor +OneWire oneWire(13); //ESP8266 boards +//OneWire oneWire(23); //ESP32 boards +DallasTemperature sensor(&oneWire); +long temptimer = millis(); +long lastMeasure = 0; +#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit + +// If display does not work or looks corrupted check the +// constructor reference: +// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp +// or check the gallery: +// https://github.com/olikraus/u8g2/wiki/gallery +// --> First choise of cheap I2C OLED 128X32 0.91" +U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA +// --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3" +//U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA +// gets called once at boot. Do all initialization that doesn't depend on +// network here +void userSetup() { + sensor.begin(); //Start Dallas temperature sensor + u8x8.begin(); + u8x8.setPowerSave(0); + u8x8.setFlipMode(1); + u8x8.setContrast(10); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255 + u8x8.setFont(u8x8_font_chroma48medium8_r); + u8x8.drawString(0, 0, "Loading..."); +} + +// gets called every time WiFi is (re-)connected. Initialize own network +// interfaces here +void userConnected() {} + +// needRedraw marks if redraw is required to prevent often redrawing. +bool needRedraw = true; + +// Next variables hold the previous known values to determine if redraw is +// required. +String knownSsid = ""; +IPAddress knownIp; +uint8_t knownBrightness = 0; +uint8_t knownMode = 0; +uint8_t knownPalette = 0; + +long lastUpdate = 0; +long lastRedraw = 0; +bool displayTurnedOff = false; +// How often we are redrawing screen +#define USER_LOOP_REFRESH_RATE_MS 5000 + +void userLoop() { + +//----> Dallas temperature sensor MQTT publishing + temptimer = millis(); +// Timer to publishe new temperature every 60 seconds + if (temptimer - lastMeasure > 60000) + { + lastMeasure = temptimer; +//Check if MQTT Connected, otherwise it will crash the 8266 + if (mqtt != nullptr) + { + sensor.requestTemperatures(); +//Gets prefered temperature scale based on selection in definitions section + #ifdef Celsius + float board_temperature = sensor.getTempCByIndex(0); + #else + float board_temperature = sensor.getTempFByIndex(0); + #endif +//Create character string populated with user defined device topic from the UI, and the read temperature. Then publish to MQTT server. + char subuf[38]; + strcpy(subuf, mqttDeviceTopic); + strcat(subuf, "/temperature"); + mqtt->publish(subuf, 0, true, String(board_temperature).c_str()); + } + } + + // Check if we time interval for redrawing passes. + if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS) { + return; + } + lastUpdate = millis(); + + // Turn off display after 3 minutes with no change. + if(!displayTurnedOff && millis() - lastRedraw > 3*60*1000) { + u8x8.setPowerSave(1); + displayTurnedOff = true; + } + + // Check if values which are shown on display changed from the last time. + if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) { + needRedraw = true; + } else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP())) { + needRedraw = true; + } else if (knownBrightness != bri) { + needRedraw = true; + } else if (knownMode != strip.getMode()) { + needRedraw = true; + } else if (knownPalette != strip.getSegment(0).palette) { + needRedraw = true; + } + + if (!needRedraw) { + return; + } + needRedraw = false; + + if (displayTurnedOff) + { + u8x8.setPowerSave(0); + displayTurnedOff = false; + } + lastRedraw = millis(); + + // Update last known values. + #if defined(ESP8266) + knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID(); + #else + knownSsid = WiFi.SSID(); + #endif + knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP(); + knownBrightness = bri; + knownMode = strip.getMode(); + knownPalette = strip.getSegment(0).palette; + u8x8.clear(); + u8x8.setFont(u8x8_font_chroma48medium8_r); + + // First row with Wifi name + u8x8.setCursor(1, 0); + u8x8.print(knownSsid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0)); + // Print `~` char to indicate that SSID is longer, than owr dicplay + if (knownSsid.length() > u8x8.getCols()) + u8x8.print("~"); + + // Second row with IP or Psssword + u8x8.setCursor(1, 1); + // Print password in AP mode and if led is OFF. + if (apActive && bri == 0) + u8x8.print(apPass); + else + u8x8.print(knownIp); + + // Third row with mode name + u8x8.setCursor(2, 2); + uint8_t qComma = 0; + bool insideQuotes = false; + uint8_t printedChars = 0; + char singleJsonSymbol; + + // Find the mode name in JSON + for (size_t i = 0; i < strlen_P(JSON_mode_names); i++) { + singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i); + switch (singleJsonSymbol) { + case '"': + insideQuotes = !insideQuotes; + break; + case '[': + case ']': + break; + case ',': + qComma++; + default: + if (!insideQuotes || (qComma != knownMode)) + break; + u8x8.print(singleJsonSymbol); + printedChars++; + } + if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) + break; + } + // Fourth row with palette name + u8x8.setCursor(2, 3); + qComma = 0; + insideQuotes = false; + printedChars = 0; + // Looking for palette name in JSON. + for (size_t i = 0; i < strlen_P(JSON_palette_names); i++) { + singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i); + switch (singleJsonSymbol) { + case '"': + insideQuotes = !insideQuotes; + break; + case '[': + case ']': + break; + case ',': + qComma++; + default: + if (!insideQuotes || (qComma != knownPalette)) + break; + u8x8.print(singleJsonSymbol); + printedChars++; + } + if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) + break; + } + + u8x8.setFont(u8x8_font_open_iconic_embedded_1x1); + u8x8.drawGlyph(0, 0, 80); // wifi icon + u8x8.drawGlyph(0, 1, 68); // home icon + u8x8.setFont(u8x8_font_open_iconic_weather_2x2); + u8x8.drawGlyph(0, 2, 66 + (bri > 0 ? 3 : 0)); // sun/moon icon +} \ No newline at end of file From 451a6841c9a4fc7686e2392c11230d020d65adf0 Mon Sep 17 00:00:00 2001 From: srg74 <28492985+srg74@users.noreply.github.com> Date: Thu, 20 Feb 2020 20:48:25 -0500 Subject: [PATCH 102/232] Update readme.md --- usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md index 7a489f03..a92c0a47 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md @@ -12,8 +12,7 @@ ## Hardware ![Shield](https://github.com/srg74/WLED-wemos-shield/blob/master/resources/Images/Assembly_8.jpg) -## Wiki -![Wiki](https://github.com/srg74/WLED-wemos-shield/wiki) +- [Wiki](https://github.com/srg74/WLED-wemos-shield/wiki) ## Functionality checked with * Wemos D1 mini original v3.1 and clones From 5ca1e9268c21ced2b7a5104af55e91ea9239053e Mon Sep 17 00:00:00 2001 From: srg74 <28492985+srg74@users.noreply.github.com> Date: Thu, 20 Feb 2020 20:49:33 -0500 Subject: [PATCH 103/232] Update readme.md --- usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md index a92c0a47..a16beb29 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md @@ -12,7 +12,7 @@ ## Hardware ![Shield](https://github.com/srg74/WLED-wemos-shield/blob/master/resources/Images/Assembly_8.jpg) -- [Wiki](https://github.com/srg74/WLED-wemos-shield/wiki) +- [Wemos shield project Wiki](https://github.com/srg74/WLED-wemos-shield/wiki) ## Functionality checked with * Wemos D1 mini original v3.1 and clones From 6451522f89de23c7fbd45a8c332d7be457537ca1 Mon Sep 17 00:00:00 2001 From: srg74 <28492985+srg74@users.noreply.github.com> Date: Thu, 20 Feb 2020 20:50:38 -0500 Subject: [PATCH 104/232] Update readme.md --- usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md index a16beb29..fa0e4324 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md @@ -1,6 +1,7 @@ # Wemos D1 mini and Wemos32 mini shield ## Project repository - [Original repository](https://github.com/srg74/WLED-wemos-shield) - WLED Wemos shield repository +- [Wemos shield project Wiki](https://github.com/srg74/WLED-wemos-shield/wiki) ## Features * SSD1306 128x32 or 128x64 I2C OLED display * On screen IP address, SSID and controller status (e.g. ON or OFF, recent effect) @@ -12,8 +13,6 @@ ## Hardware ![Shield](https://github.com/srg74/WLED-wemos-shield/blob/master/resources/Images/Assembly_8.jpg) -- [Wemos shield project Wiki](https://github.com/srg74/WLED-wemos-shield/wiki) - ## Functionality checked with * Wemos D1 mini original v3.1 and clones * Wemos32 mini From bdc44a4070f8d168b3b9ca911164e5d309b9f9a3 Mon Sep 17 00:00:00 2001 From: srg74 <28492985+srg74@users.noreply.github.com> Date: Thu, 20 Feb 2020 20:53:17 -0500 Subject: [PATCH 105/232] Update readme.md --- usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md index fa0e4324..2584c919 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md @@ -2,6 +2,7 @@ ## Project repository - [Original repository](https://github.com/srg74/WLED-wemos-shield) - WLED Wemos shield repository - [Wemos shield project Wiki](https://github.com/srg74/WLED-wemos-shield/wiki) +- [Precompiled WLED firmware](https://github.com/srg74/WLED-wemos-shield/tree/master/resources/Firmware) ## Features * SSD1306 128x32 or 128x64 I2C OLED display * On screen IP address, SSID and controller status (e.g. ON or OFF, recent effect) From 863498f76219f2fc1dca1bb4e155d69182600bca Mon Sep 17 00:00:00 2001 From: srg74 <28492985+srg74@users.noreply.github.com> Date: Thu, 20 Feb 2020 22:43:59 -0500 Subject: [PATCH 106/232] Update readme.md --- .../readme.md | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md index 2584c919..8a1d7eca 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md @@ -4,21 +4,21 @@ - [Wemos shield project Wiki](https://github.com/srg74/WLED-wemos-shield/wiki) - [Precompiled WLED firmware](https://github.com/srg74/WLED-wemos-shield/tree/master/resources/Firmware) ## Features -* SSD1306 128x32 or 128x64 I2C OLED display -* On screen IP address, SSID and controller status (e.g. ON or OFF, recent effect) -* Auto display shutoff for saving display lifetime -* Dallas temperature sensor -* Reporting temperature to MQTT broker -* Relay for energy saving +- SSD1306 128x32 or 128x64 I2C OLED display +- On screen IP address, SSID and controller status (e.g. ON or OFF, recent effect) +- Auto display shutoff for saving display lifetime +- Dallas temperature sensor +- Reporting temperature to MQTT broker +- Relay for energy saving ## Hardware ![Shield](https://github.com/srg74/WLED-wemos-shield/blob/master/resources/Images/Assembly_8.jpg) ## Functionality checked with -* Wemos D1 mini original v3.1 and clones -* Wemos32 mini -* PlatformIO -* SSD1306 128x32 I2C OLED display -* DS18B20 (temperature sensor) -* Push button (N.O. momentary switch) +- Wemos D1 mini original v3.1 and clones +- Wemos32 mini +- PlatformIO +- SSD1306 128x32 I2C OLED display +- DS18B20 (temperature sensor) +- Push button (N.O. momentary switch) From b1961033b36a4c8c062f162e59f4788c912af330 Mon Sep 17 00:00:00 2001 From: srg74 <28492985+srg74@users.noreply.github.com> Date: Thu, 20 Feb 2020 22:48:20 -0500 Subject: [PATCH 107/232] Update readme.md --- usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md | 1 - 1 file changed, 1 deletion(-) diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md index 8a1d7eca..be97d4b8 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md @@ -21,4 +21,3 @@ - SSD1306 128x32 I2C OLED display - DS18B20 (temperature sensor) - Push button (N.O. momentary switch) - From fb59f1f0a0e6d710f7e5b0402184334ed48c73ab Mon Sep 17 00:00:00 2001 From: srg74 <28492985+srg74@users.noreply.github.com> Date: Fri, 21 Feb 2020 16:55:58 -0500 Subject: [PATCH 108/232] Update wled06_usermod.ino --- .../wled06_usermod.ino | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino index 0e1b1e66..9e6e0b79 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino @@ -1,17 +1,20 @@ #include // from https://github.com/olikraus/u8g2/ #include //Dallastemperature sensor - +#ifdef ARDUINO_ARCH_ESP32 //ESP32 boards +uint8_t SCL_PIN = 22; +uint8_t SDA_PIN = 21; +OneWire oneWire(23); +#else //ESP8266 boards +uint8_t SCL_PIN = 5; +uint8_t SDA_PIN = 4; +OneWire oneWire(13); +#endif //The SCL and SDA pins are defined here. -//ESP8266 Wemos D1 mini boards use SCL=5 SDA=4 -#define U8X8_PIN_SCL 5 -#define U8X8_PIN_SDA 4 -//ESP32 Wemos32 mini boards use SCL=22 SDA=21 -//#define U8X8_PIN_SCL 22 -//#define U8X8_PIN_SDA 21 +//ESP8266 Wemos D1 mini board use SCL=5 SDA=4 while ESP32 Wemos32 mini board use SCL=22 SDA=21 +#define U8X8_PIN_SCL SCL_PIN +#define U8X8_PIN_SDA SDA_PIN // Dallas sensor -OneWire oneWire(13); //ESP8266 boards -//OneWire oneWire(23); //ESP32 boards DallasTemperature sensor(&oneWire); long temptimer = millis(); long lastMeasure = 0; @@ -210,4 +213,4 @@ void userLoop() { u8x8.drawGlyph(0, 1, 68); // home icon u8x8.setFont(u8x8_font_open_iconic_weather_2x2); u8x8.drawGlyph(0, 2, 66 + (bri > 0 ? 3 : 0)); // sun/moon icon -} \ No newline at end of file +} From 1a4061fdb5b49270303b036d50dd1e791a1e17cc Mon Sep 17 00:00:00 2001 From: Def3nder Date: Sat, 22 Feb 2020 16:17:32 +0100 Subject: [PATCH 109/232] Refactor callMode (#702) --- .../wled06_usermod.ino | 2 +- .../stairway_wipe_basic/wled06_usermod.ino | 8 +- wled00/const.h | 13 +++ wled00/wled00.ino | 4 +- wled00/wled03_set.ino | 2 +- wled00/wled05_init.ino | 2 +- wled00/wled07_notify.ino | 26 +++--- wled00/wled08_led.ino | 38 +++++--- wled00/wled09_button.ino | 2 +- wled00/wled12_alexa.ino | 8 +- wled00/wled15_hue.ino | 2 +- wled00/wled16_blynk.ino | 14 +-- wled00/wled17_mqtt.ino | 4 +- wled00/wled19_json.ino | 2 +- wled00/wled20_ir.ino | 93 ++++++++----------- 15 files changed, 114 insertions(+), 106 deletions(-) diff --git a/usermods/rotary_encoder_change_effect/wled06_usermod.ino b/usermods/rotary_encoder_change_effect/wled06_usermod.ino index dbde9560..f004ba2f 100644 --- a/usermods/rotary_encoder_change_effect/wled06_usermod.ino +++ b/usermods/rotary_encoder_change_effect/wled06_usermod.ino @@ -39,7 +39,7 @@ void userLoop() { //call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification) // 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa - colorUpdated(6); + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); lastTime = millis(); } } diff --git a/usermods/stairway_wipe_basic/wled06_usermod.ino b/usermods/stairway_wipe_basic/wled06_usermod.ino index ab61c5af..3d493cc1 100644 --- a/usermods/stairway_wipe_basic/wled06_usermod.ino +++ b/usermods/stairway_wipe_basic/wled06_usermod.ino @@ -47,7 +47,7 @@ void userLoop() if (millis() + strip.timebase > (cycleTime - 25)) { //wipe complete effectCurrent = FX_MODE_STATIC; timeStaticStart = millis(); - colorUpdated(3); + colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); wipeState = 2; } } else if (wipeState == 2) { //static @@ -59,7 +59,7 @@ void userLoop() #ifdef STAIRCASE_WIPE_OFF effectCurrent = FX_MODE_COLOR_WIPE; strip.timebase = 360 + (255 - effectSpeed)*75 - millis(); //make sure wipe starts fully lit - colorUpdated(3); + colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); wipeState = 4; #else turnOff(); @@ -93,7 +93,7 @@ void startWipe() bool doReverse = (userVar0 == 2); seg.setOption(1, doReverse); - colorUpdated(3); + colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); } void turnOff() @@ -104,7 +104,7 @@ void turnOff() transitionDelayTemp = 4000; //fade out slowly #endif bri = 0; - colorUpdated(3); + colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); wipeState = 0; userVar0 = 0; previousUserVar0 = 0; diff --git a/wled00/const.h b/wled00/const.h index 1308bf5f..4d287572 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -7,6 +7,19 @@ #define AP_BEHAVIOR_ALWAYS 2 //Always open #define AP_BEHAVIOR_BUTTON_ONLY 3 //Only when button pressed for 6 sec +//Notifier callMode +#define NOTIFIER_CALL_MODE_INIT 0 // no updates on init, can be used to disable updates +#define NOTIFIER_CALL_MODE_DIRECT_CHANGE 1 +#define NOTIFIER_CALL_MODE_BUTTON 2 +#define NOTIFIER_CALL_MODE_NOTIFICATION 3 +#define NOTIFIER_CALL_MODE_NIGHTLIGHT 4 +#define NOTIFIER_CALL_MODE_NO_NOTIFY 5 +#define NOTIFIER_CALL_MODE_FX_CHANGED 6 +#define NOTIFIER_CALL_MODE_HUE 7 +#define NOTIFIER_CALL_MODE_PRESET_CYCLE 8 +#define NOTIFIER_CALL_MODE_BLYNK 9 +#define NOTIFIER_CALL_MODE_ALEXA 10 + //RGB to RGBW conversion mode #define RGBW_MODE_MANUAL_ONLY 0 //No automatic white channel calculation. Manual white channel slider #define RGBW_MODE_AUTO_BRIGHTER 1 //New algorithm. Adds as much white as the darkest RGBW channel diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 746f98d0..c04258f0 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -301,7 +301,7 @@ unsigned long buttonWaitTime = 0; bool notifyDirectDefault = notifyDirect; bool receiveNotifications = true; unsigned long notificationSentTime = 0; -byte notificationSentCallMode = 0; +byte notificationSentCallMode = NOTIFIER_CALL_MODE_INIT; bool notificationTwoRequired = false; //effects @@ -369,7 +369,7 @@ unsigned long realtimeTimeout = 0; //mqtt long lastMqttReconnectAttempt = 0; long lastInterfaceUpdate = 0; -byte interfaceUpdateCallMode = 0; +byte interfaceUpdateCallMode = NOTIFIER_CALL_MODE_INIT; char mqttStatusTopic[40] = ""; //this must be global because of async handlers #if AUXPIN >= 0 diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index 6655dafd..f7485917 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -651,7 +651,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) if (pos < 1) XML_response(request); pos = req.indexOf("&NN"); //do not send UDP notifications this time - colorUpdated((pos > 0) ? 5:1); + colorUpdated((pos > 0) ? NOTIFIER_CALL_MODE_NO_NOTIFY : NOTIFIER_CALL_MODE_DIRECT_CHANGE); return true; } diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index 8d41756b..820670c8 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -99,7 +99,7 @@ void beginStrip() #endif if (bootPreset>0) applyPreset(bootPreset, turnOnAtBoot, true, true); - colorUpdated(0); + colorUpdated(NOTIFIER_CALL_MODE_INIT); //init relay pin #if RLYPIN >= 0 diff --git a/wled00/wled07_notify.ino b/wled00/wled07_notify.ino index ab55df28..6af7416b 100644 --- a/wled00/wled07_notify.ino +++ b/wled00/wled07_notify.ino @@ -11,15 +11,15 @@ void notify(byte callMode, bool followUp=false) if (!udpConnected) return; switch (callMode) { - case 0: return; - case 1: if (!notifyDirect) return; break; - case 2: if (!notifyButton) return; break; - case 4: if (!notifyDirect) return; break; - case 6: if (!notifyDirect) return; break; //fx change - case 7: if (!notifyHue) return; break; - case 8: if (!notifyDirect) return; break; - case 9: if (!notifyDirect) return; break; - case 10: if (!notifyAlexa) return; break; + case NOTIFIER_CALL_MODE_INIT: return; + case NOTIFIER_CALL_MODE_DIRECT_CHANGE: if (!notifyDirect) return; break; + case NOTIFIER_CALL_MODE_BUTTON: if (!notifyButton) return; break; + case NOTIFIER_CALL_MODE_NIGHTLIGHT: if (!notifyDirect) return; break; + case NOTIFIER_CALL_MODE_FX_CHANGED: if (!notifyDirect) return; break; //fx change + case NOTIFIER_CALL_MODE_HUE: if (!notifyHue) return; break; + case NOTIFIER_CALL_MODE_PRESET_CYCLE: if (!notifyDirect) return; break; + case NOTIFIER_CALL_MODE_BLYNK: if (!notifyDirect) return; break; + case NOTIFIER_CALL_MODE_ALEXA: if (!notifyAlexa) return; break; default: return; } byte udpOut[WLEDPACKETSIZE]; @@ -157,9 +157,9 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ col[3] = p->property_values[DMXAddress+11]; //white colSec[3] = p->property_values[DMXAddress+12]; } - transitionDelayTemp = 0; // act fast - colorUpdated(3); // don't send UDP - return; // don't activate realtime live mode + transitionDelayTemp = 0; // act fast + colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); // don't send UDP + return; // don't activate realtime live mode break; case DMX_MODE_MULTIPLE_RGB: @@ -329,7 +329,7 @@ void handleNotifications() if (nightlightActive) nightlightDelayMins = udpIn[7]; if (receiveNotificationBrightness || !someSel) bri = udpIn[2]; - colorUpdated(3); + colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); } else if (udpIn[0] > 0 && udpIn[0] < 5 && receiveDirect) //1 warls //2 drgb //3 drgbw { diff --git a/wled00/wled08_led.ino b/wled00/wled08_led.ino index fb0e8a7e..124e43bd 100644 --- a/wled00/wled08_led.ino +++ b/wled00/wled08_led.ino @@ -79,6 +79,7 @@ bool colorChanged() { if (col[i] != colIT[i]) return true; if (colSec[i] != colSecIT[i]) return true; + if (col[i] != colNlT[i]) return true; } if (bri != briIT) return true; return false; @@ -89,18 +90,24 @@ void colorUpdated(int callMode) { //call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification) // 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa - if (callMode != 0 && callMode != 1 && callMode != 5) strip.applyToAllSelected = true; //if not from JSON api, which directly sets segments + if (callMode != NOTIFIER_CALL_MODE_INIT && + callMode != NOTIFIER_CALL_MODE_DIRECT_CHANGE && + callMode != NOTIFIER_CALL_MODE_NO_NOTIFY) strip.applyToAllSelected = true; //if not from JSON api, which directly sets segments bool fxChanged = strip.setEffectConfig(effectCurrent, effectSpeed, effectIntensity, effectPalette); if (!colorChanged()) { - if (nightlightActive && !nightlightActiveOld && callMode != 3 && callMode != 5) + if (nightlightActive && !nightlightActiveOld && + callMode != NOTIFIER_CALL_MODE_NOTIFICATION && + callMode != NOTIFIER_CALL_MODE_NO_NOTIFY) { - notify(4); interfaceUpdateCallMode = 4; return; + notify(NOTIFIER_CALL_MODE_NIGHTLIGHT); + interfaceUpdateCallMode = NOTIFIER_CALL_MODE_NIGHTLIGHT; + return; } else if (fxChanged) { - notify(6); - if (callMode != 8) interfaceUpdateCallMode = 6; + notify(NOTIFIER_CALL_MODE_FX_CHANGED); + if (callMode != NOTIFIER_CALL_MODE_PRESET_CYCLE) interfaceUpdateCallMode = NOTIFIER_CALL_MODE_FX_CHANGED; if (realtimeTimeout == UINT32_MAX) realtimeTimeout = 0; if (isPreset) {isPreset = false;} else {currentPreset = -1;} @@ -110,7 +117,7 @@ void colorUpdated(int callMode) if (realtimeTimeout == UINT32_MAX) realtimeTimeout = 0; if (isPreset) {isPreset = false;} else {currentPreset = -1;} - if (callMode != 5 && nightlightActive && nightlightFade) + if (callMode != NOTIFIER_CALL_MODE_NO_NOTIFY && nightlightActive && nightlightFade) { briNlT = bri; nightlightDelayMs -= (millis() - nightlightStartTime); @@ -123,8 +130,8 @@ void colorUpdated(int callMode) } if (briT == 0) { - setLedsStandard(true); //do not color transition if starting from off - if (callMode != 3) resetTimebase(); //effect start from beginning + setLedsStandard(true); //do not color transition if starting from off + if (callMode != NOTIFIER_CALL_MODE_NOTIFICATION) resetTimebase(); //effect start from beginning } briIT = bri; @@ -135,7 +142,7 @@ void colorUpdated(int callMode) if (fadeTransition) { //set correct delay if not using notification delay - if (callMode != 3 && !jsonTransitionOnce) transitionDelayTemp = transitionDelay; + if (callMode != NOTIFIER_CALL_MODE_NOTIFICATION && !jsonTransitionOnce) transitionDelayTemp = transitionDelay; jsonTransitionOnce = false; if (transitionDelayTemp == 0) {setLedsStandard(); strip.trigger(); return;} @@ -158,7 +165,7 @@ void colorUpdated(int callMode) strip.trigger(); } - if (callMode == 8) return; + if (callMode == NOTIFIER_CALL_MODE_PRESET_CYCLE) return; //set flag to update blynk and mqtt interfaceUpdateCallMode = callMode; } @@ -167,12 +174,13 @@ void colorUpdated(int callMode) void updateInterfaces(uint8_t callMode) { #ifndef WLED_DISABLE_ALEXA - if (espalexaDevice != nullptr && callMode != 10) { + if (espalexaDevice != nullptr && callMode != NOTIFIER_CALL_MODE_ALEXA) { espalexaDevice->setValue(bri); espalexaDevice->setColor(col[0], col[1], col[2]); } #endif - if (callMode != 9 && callMode != 5) updateBlynk(); + if (callMode != NOTIFIER_CALL_MODE_BLYNK && + callMode != NOTIFIER_CALL_MODE_NO_NOTIFY) updateBlynk(); doPublishMqtt = true; lastInterfaceUpdate = millis(); } @@ -233,7 +241,7 @@ void handleNightlight() { for (byte i=0; i<4; i++) col[i] = colNlT[i]+ ((colSec[i] - colNlT[i])*nper); // fading from actual color to secondary color } - colorUpdated(5); + colorUpdated(NOTIFIER_CALL_MODE_NO_NOTIFY); } if (nper >= 1) { @@ -241,7 +249,7 @@ void handleNightlight() if (!nightlightFade) { bri = nightlightTargetBri; - colorUpdated(5); + colorUpdated(NOTIFIER_CALL_MODE_NO_NOTIFY); } updateBlynk(); if (bri == 0) briLast = briNlT; @@ -257,7 +265,7 @@ void handleNightlight() applyPreset(presetCycCurr,presetApplyBri,presetApplyCol,presetApplyFx); presetCycCurr++; if (presetCycCurr > presetCycleMax) presetCycCurr = presetCycleMin; if (presetCycCurr > 25) presetCycCurr = 1; - colorUpdated(8); + colorUpdated(NOTIFIER_CALL_MODE_PRESET_CYCLE); presetCycledTime = millis(); } } diff --git a/wled00/wled09_button.ino b/wled00/wled09_button.ino index 86227dc3..1cd868be 100644 --- a/wled00/wled09_button.ino +++ b/wled00/wled09_button.ino @@ -7,7 +7,7 @@ void shortPressAction() if (!macroButton) { toggleOnOff(); - colorUpdated(2); + colorUpdated(NOTIFIER_CALL_MODE_BUTTON); } else { applyMacro(macroButton); } diff --git a/wled00/wled12_alexa.ino b/wled00/wled12_alexa.ino index c3bc01ab..9c27041b 100644 --- a/wled00/wled12_alexa.ino +++ b/wled00/wled12_alexa.ino @@ -41,7 +41,7 @@ void onAlexaChange(EspalexaDevice* dev) if (bri == 0) { bri = briLast; - colorUpdated(10); + colorUpdated(NOTIFIER_CALL_MODE_ALEXA); } } else applyMacro(macroAlexaOn); } else if (m == EspalexaDeviceProperty::off) @@ -52,13 +52,13 @@ void onAlexaChange(EspalexaDevice* dev) { briLast = bri; bri = 0; - colorUpdated(10); + colorUpdated(NOTIFIER_CALL_MODE_ALEXA); } } else applyMacro(macroAlexaOff); } else if (m == EspalexaDeviceProperty::bri) { bri = espalexaDevice->getValue(); - colorUpdated(10); + colorUpdated(NOTIFIER_CALL_MODE_ALEXA); } else //color { uint32_t color = espalexaDevice->getRGB(); @@ -67,7 +67,7 @@ void onAlexaChange(EspalexaDevice* dev) col[1] = ((color >> 8) & 0xFF); col[2] = (color & 0xFF); if (useRGBW && col[3] == 0) colorRGBtoRGBW(col); // do not touch white value if EspalexaDevice.cpp did set the white channel - colorUpdated(10); + colorUpdated(NOTIFIER_CALL_MODE_ALEXA); } } diff --git a/wled00/wled15_hue.ino b/wled00/wled15_hue.ino index da400f5e..77e6622c 100644 --- a/wled00/wled15_hue.ino +++ b/wled00/wled15_hue.ino @@ -6,7 +6,7 @@ void handleHue() { if (hueReceived) { - colorUpdated(7); hueReceived = false; + colorUpdated(NOTIFIER_CALL_MODE_HUE); hueReceived = false; if (hueStoreAllowed && hueNewKey) { saveSettingsToEEPROM(); //save api key diff --git a/wled00/wled16_blynk.ino b/wled00/wled16_blynk.ino index c09d0c18..ea4d6877 100644 --- a/wled00/wled16_blynk.ino +++ b/wled00/wled16_blynk.ino @@ -41,45 +41,45 @@ void updateBlynk() BLYNK_WRITE(V0) { bri = param.asInt();//bri - colorUpdated(9); + colorUpdated(NOTIFIER_CALL_MODE_BLYNK); } BLYNK_WRITE(V1) { blHue = param.asInt();//hue colorHStoRGB(blHue*10,blSat,(false)? colSec:col); - colorUpdated(9); + colorUpdated(NOTIFIER_CALL_MODE_BLYNK); } BLYNK_WRITE(V2) { blSat = param.asInt();//sat colorHStoRGB(blHue*10,blSat,(false)? colSec:col); - colorUpdated(9); + colorUpdated(NOTIFIER_CALL_MODE_BLYNK); } BLYNK_WRITE(V3) { bool on = (param.asInt()>0); - if (!on != !bri) {toggleOnOff(); colorUpdated(9);} + if (!on != !bri) {toggleOnOff(); colorUpdated(NOTIFIER_CALL_MODE_BLYNK);} } BLYNK_WRITE(V4) { effectCurrent = param.asInt()-1;//fx - colorUpdated(9); + colorUpdated(NOTIFIER_CALL_MODE_BLYNK); } BLYNK_WRITE(V5) { effectSpeed = param.asInt();//sx - colorUpdated(9); + colorUpdated(NOTIFIER_CALL_MODE_BLYNK); } BLYNK_WRITE(V6) { effectIntensity = param.asInt();//ix - colorUpdated(9); + colorUpdated(NOTIFIER_CALL_MODE_BLYNK); } BLYNK_WRITE(V7) diff --git a/wled00/wled17_mqtt.ino b/wled00/wled17_mqtt.ino index aa72b9e1..db34a742 100644 --- a/wled00/wled17_mqtt.ino +++ b/wled00/wled17_mqtt.ino @@ -12,7 +12,7 @@ void parseMQTTBriPayload(char* payload) uint8_t in = strtoul(payload, NULL, 10); if (in == 0 && bri > 0) briLast = bri; bri = in; - colorUpdated(1); + colorUpdated(NOTIFIER_CALL_MODE_DIRECT_CHANGE); } } @@ -60,7 +60,7 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties if (strstr(topic, "/col")) { colorFromDecOrHexString(col, (char*)payload); - colorUpdated(1); + colorUpdated(NOTIFIER_CALL_MODE_DIRECT_CHANGE); } else if (strstr(topic, "/api")) { String apireq = "win&"; diff --git a/wled00/wled19_json.ino b/wled00/wled19_json.ino index 219ef8bc..852dfdac 100644 --- a/wled00/wled19_json.ino +++ b/wled00/wled19_json.ino @@ -154,7 +154,7 @@ bool deserializeState(JsonObject root) } } - colorUpdated(noNotification ? 5:1); + colorUpdated(noNotification ? NOTIFIER_CALL_MODE_NO_NOTIFY : NOTIFIER_CALL_MODE_DIRECT_CHANGE); ps = root["psave"] | -1; if (ps >= 0) savePreset(ps); diff --git a/wled00/wled20_ir.ino b/wled00/wled20_ir.ino index 113b19b5..617518c3 100644 --- a/wled00/wled20_ir.ino +++ b/wled00/wled20_ir.ino @@ -29,7 +29,7 @@ bool decodeIRCustom(uint32_t code) default: return false; } - if (code != IRCUSTOM_MACRO1) colorUpdated(2); //don't update color again if we apply macro, it already does it + if (code != IRCUSTOM_MACRO1) colorUpdated(NOTIFIER_CALL_MODE_BUTTON); //don't update color again if we apply macro, it already does it return true; } @@ -51,25 +51,25 @@ void decodeIR(uint32_t code) irTimesRepeated++; if (lastValidCode == IR24_BRIGHTER || lastValidCode == IR40_BPLUS ) { - relativeChange(&bri, 10); colorUpdated(2); + relativeChange(&bri, 10); colorUpdated(NOTIFIER_CALL_MODE_BUTTON); } else if (lastValidCode == IR24_DARKER || lastValidCode == IR40_BMINUS ) { - relativeChange(&bri, -10, 5); colorUpdated(2); + relativeChange(&bri, -10, 5); colorUpdated(NOTIFIER_CALL_MODE_BUTTON); } if (lastValidCode == IR40_WPLUS) { - relativeChangeWhite(10); colorUpdated(2); + relativeChangeWhite(10); colorUpdated(NOTIFIER_CALL_MODE_BUTTON); } else if (lastValidCode == IR40_WMINUS) { - relativeChangeWhite(-10, 5); colorUpdated(2); + relativeChangeWhite(-10, 5); colorUpdated(NOTIFIER_CALL_MODE_BUTTON); } else if ((lastValidCode == IR24_ON || lastValidCode == IR40_ON) && irTimesRepeated > 7 ) { nightlightActive = true; nightlightStartTime = millis(); - colorUpdated(2); + colorUpdated(NOTIFIER_CALL_MODE_BUTTON); } return; } @@ -85,11 +85,12 @@ void decodeIR(uint32_t code) case 3: decodeIR40(code); break; // blue 40-key remote with 25%, 50%, 75% and 100% keys case 4: decodeIR44(code); break; // white 44-key remote with color-up/down keys and DIY1 to 6 keys case 5: decodeIR21(code); break; // white 21-key remote - case 6: decodeIR6(code); break; // black 6-key learning remote defaults: "CH" controls brightness, + case 6: decodeIR6(code); break; // black 6-key learning remote defaults: "CH" controls brightness, // "VOL +" controls effect, "VOL -" controls colour/palette, "MUTE" // sets bright plain white default: return; } + colorUpdated(NOTIFIER_CALL_MODE_BUTTON); //for notifier, IR is considered a button input } //code <= 0xF70000 also invalid } @@ -125,7 +126,6 @@ void decodeIR24(uint32_t code) default: return; } lastValidCode = code; - colorUpdated(2); //for notifier, IR is considered a button input } void decodeIR24OLD(uint32_t code) @@ -158,7 +158,6 @@ void decodeIR24OLD(uint32_t code) default: return; } lastValidCode = code; - colorUpdated(2); //for notifier, IR is considered a button input } @@ -194,7 +193,6 @@ void decodeIR24CT(uint32_t code) default: return; } lastValidCode = code; - colorUpdated(2); //for notifier, IR is considered a button input } @@ -253,7 +251,6 @@ void decodeIR40(uint32_t code) case IR40_FLASH : if (!applyPreset(4)) { effectCurrent = FX_MODE_RAINBOW; effectPalette = 0; } break; } lastValidCode = code; - colorUpdated(2); //for notifier, IR is considered a button input } void decodeIR44(uint32_t code) @@ -317,12 +314,11 @@ void decodeIR44(uint32_t code) case IR44_FADE7 : bri = 255; break; } lastValidCode = code; - colorUpdated(2); //for notifier, IR is considered a button input } void decodeIR21(uint32_t code) { - switch (code) { + switch (code) { case IR21_BRIGHTER: relativeChange(&bri, 10); break; case IR21_DARKER: relativeChange(&bri, -10, 5); break; case IR21_OFF: briLast = bri; bri = 0; break; @@ -345,51 +341,42 @@ void decodeIR21(uint32_t code) case IR21_FADE: if (!applyPreset(3)) { effectCurrent = FX_MODE_BREATH; effectPalette = 0; } break; case IR21_SMOOTH: if (!applyPreset(4)) { effectCurrent = FX_MODE_RAINBOW; effectPalette = 0; } break; default: return; - } - lastValidCode = code; - colorUpdated(2); //for notifier, IR is considered a button input + } + lastValidCode = code; } void decodeIR6(uint32_t code) { - - switch (code) { - case IR6_POWER: toggleOnOff(); break; - case IR6_CHANNEL_UP: relativeChange(&bri, 10); break; - case IR6_CHANNEL_DOWN: relativeChange(&bri, -10, 5); break; - case IR6_VOLUME_UP: /* next effect */ relativeChange(&effectCurrent, 1); break; - case IR6_VOLUME_DOWN: - /* next palette */ - - relativeChange(&effectPalette, 1); - - switch(lastIR6ColourIdx) - { - case 0: colorFromUint32(COLOR_RED); break; - case 1: colorFromUint32(COLOR_REDDISH); break; - case 2:colorFromUint32(COLOR_ORANGE); break; - case 3:colorFromUint32(COLOR_YELLOWISH); break; - case 4:colorFromUint32(COLOR_GREEN); break; - case 5:colorFromUint32(COLOR_GREENISH); break; - case 6:colorFromUint32(COLOR_TURQUOISE); break; - case 7: colorFromUint32(COLOR_CYAN); break; - case 8:colorFromUint32(COLOR_BLUE); break; - case 9:colorFromUint32(COLOR_DEEPBLUE); break; - case 10:colorFromUint32(COLOR_PURPLE); break; - case 11:colorFromUint32(COLOR_PINK); break; - case 12:colorFromUint32(COLOR_WHITE); break; - default:break; - - } - - lastIR6ColourIdx++; - if(lastIR6ColourIdx > 12) lastIR6ColourIdx = 0; - + switch (code) { + case IR6_POWER: toggleOnOff(); break; + case IR6_CHANNEL_UP: relativeChange(&bri, 10); break; + case IR6_CHANNEL_DOWN: relativeChange(&bri, -10, 5); break; + case IR6_VOLUME_UP: /* next effect */ relativeChange(&effectCurrent, 1); break; + case IR6_VOLUME_DOWN: + /* next palette */ + relativeChange(&effectPalette, 1); + switch(lastIR6ColourIdx) { + case 0: colorFromUint32(COLOR_RED); break; + case 1: colorFromUint32(COLOR_REDDISH); break; + case 2:colorFromUint32(COLOR_ORANGE); break; + case 3:colorFromUint32(COLOR_YELLOWISH); break; + case 4:colorFromUint32(COLOR_GREEN); break; + case 5:colorFromUint32(COLOR_GREENISH); break; + case 6:colorFromUint32(COLOR_TURQUOISE); break; + case 7: colorFromUint32(COLOR_CYAN); break; + case 8:colorFromUint32(COLOR_BLUE); break; + case 9:colorFromUint32(COLOR_DEEPBLUE); break; + case 10:colorFromUint32(COLOR_PURPLE); break; + case 11:colorFromUint32(COLOR_PINK); break; + case 12:colorFromUint32(COLOR_WHITE); break; + default:break; + } + lastIR6ColourIdx++; + if(lastIR6ColourIdx > 12) lastIR6ColourIdx = 0; break; - case IR6_MUTE: effectCurrent = 0; effectPalette = 0; colorFromUint32(COLOR_WHITE); bri=255; break; - } - lastValidCode = code; - colorUpdated(2); //for notifier, IR is considered a button input + case IR6_MUTE: effectCurrent = 0; effectPalette = 0; colorFromUint32(COLOR_WHITE); bri=255; break; + } + lastValidCode = code; } From a0b208cca0dff3147e82472797963816b02aa2ad Mon Sep 17 00:00:00 2001 From: srg74 <28492985+srg74@users.noreply.github.com> Date: Sat, 22 Feb 2020 10:18:18 -0500 Subject: [PATCH 110/232] Added new user mod for Wemos shield (#701) --- .../readme.md | 23 ++ .../wled06_usermod.ino | 216 ++++++++++++++++++ 2 files changed, 239 insertions(+) create mode 100644 usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md create mode 100644 usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md new file mode 100644 index 00000000..be97d4b8 --- /dev/null +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md @@ -0,0 +1,23 @@ +# Wemos D1 mini and Wemos32 mini shield +## Project repository +- [Original repository](https://github.com/srg74/WLED-wemos-shield) - WLED Wemos shield repository +- [Wemos shield project Wiki](https://github.com/srg74/WLED-wemos-shield/wiki) +- [Precompiled WLED firmware](https://github.com/srg74/WLED-wemos-shield/tree/master/resources/Firmware) +## Features +- SSD1306 128x32 or 128x64 I2C OLED display +- On screen IP address, SSID and controller status (e.g. ON or OFF, recent effect) +- Auto display shutoff for saving display lifetime +- Dallas temperature sensor +- Reporting temperature to MQTT broker +- Relay for energy saving + +## Hardware +![Shield](https://github.com/srg74/WLED-wemos-shield/blob/master/resources/Images/Assembly_8.jpg) + +## Functionality checked with +- Wemos D1 mini original v3.1 and clones +- Wemos32 mini +- PlatformIO +- SSD1306 128x32 I2C OLED display +- DS18B20 (temperature sensor) +- Push button (N.O. momentary switch) diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino new file mode 100644 index 00000000..9e6e0b79 --- /dev/null +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino @@ -0,0 +1,216 @@ +#include // from https://github.com/olikraus/u8g2/ +#include //Dallastemperature sensor +#ifdef ARDUINO_ARCH_ESP32 //ESP32 boards +uint8_t SCL_PIN = 22; +uint8_t SDA_PIN = 21; +OneWire oneWire(23); +#else //ESP8266 boards +uint8_t SCL_PIN = 5; +uint8_t SDA_PIN = 4; +OneWire oneWire(13); +#endif +//The SCL and SDA pins are defined here. +//ESP8266 Wemos D1 mini board use SCL=5 SDA=4 while ESP32 Wemos32 mini board use SCL=22 SDA=21 +#define U8X8_PIN_SCL SCL_PIN +#define U8X8_PIN_SDA SDA_PIN + +// Dallas sensor +DallasTemperature sensor(&oneWire); +long temptimer = millis(); +long lastMeasure = 0; +#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit + +// If display does not work or looks corrupted check the +// constructor reference: +// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp +// or check the gallery: +// https://github.com/olikraus/u8g2/wiki/gallery +// --> First choise of cheap I2C OLED 128X32 0.91" +U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA +// --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3" +//U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA +// gets called once at boot. Do all initialization that doesn't depend on +// network here +void userSetup() { + sensor.begin(); //Start Dallas temperature sensor + u8x8.begin(); + u8x8.setPowerSave(0); + u8x8.setFlipMode(1); + u8x8.setContrast(10); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255 + u8x8.setFont(u8x8_font_chroma48medium8_r); + u8x8.drawString(0, 0, "Loading..."); +} + +// gets called every time WiFi is (re-)connected. Initialize own network +// interfaces here +void userConnected() {} + +// needRedraw marks if redraw is required to prevent often redrawing. +bool needRedraw = true; + +// Next variables hold the previous known values to determine if redraw is +// required. +String knownSsid = ""; +IPAddress knownIp; +uint8_t knownBrightness = 0; +uint8_t knownMode = 0; +uint8_t knownPalette = 0; + +long lastUpdate = 0; +long lastRedraw = 0; +bool displayTurnedOff = false; +// How often we are redrawing screen +#define USER_LOOP_REFRESH_RATE_MS 5000 + +void userLoop() { + +//----> Dallas temperature sensor MQTT publishing + temptimer = millis(); +// Timer to publishe new temperature every 60 seconds + if (temptimer - lastMeasure > 60000) + { + lastMeasure = temptimer; +//Check if MQTT Connected, otherwise it will crash the 8266 + if (mqtt != nullptr) + { + sensor.requestTemperatures(); +//Gets prefered temperature scale based on selection in definitions section + #ifdef Celsius + float board_temperature = sensor.getTempCByIndex(0); + #else + float board_temperature = sensor.getTempFByIndex(0); + #endif +//Create character string populated with user defined device topic from the UI, and the read temperature. Then publish to MQTT server. + char subuf[38]; + strcpy(subuf, mqttDeviceTopic); + strcat(subuf, "/temperature"); + mqtt->publish(subuf, 0, true, String(board_temperature).c_str()); + } + } + + // Check if we time interval for redrawing passes. + if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS) { + return; + } + lastUpdate = millis(); + + // Turn off display after 3 minutes with no change. + if(!displayTurnedOff && millis() - lastRedraw > 3*60*1000) { + u8x8.setPowerSave(1); + displayTurnedOff = true; + } + + // Check if values which are shown on display changed from the last time. + if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) { + needRedraw = true; + } else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP())) { + needRedraw = true; + } else if (knownBrightness != bri) { + needRedraw = true; + } else if (knownMode != strip.getMode()) { + needRedraw = true; + } else if (knownPalette != strip.getSegment(0).palette) { + needRedraw = true; + } + + if (!needRedraw) { + return; + } + needRedraw = false; + + if (displayTurnedOff) + { + u8x8.setPowerSave(0); + displayTurnedOff = false; + } + lastRedraw = millis(); + + // Update last known values. + #if defined(ESP8266) + knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID(); + #else + knownSsid = WiFi.SSID(); + #endif + knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP(); + knownBrightness = bri; + knownMode = strip.getMode(); + knownPalette = strip.getSegment(0).palette; + u8x8.clear(); + u8x8.setFont(u8x8_font_chroma48medium8_r); + + // First row with Wifi name + u8x8.setCursor(1, 0); + u8x8.print(knownSsid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0)); + // Print `~` char to indicate that SSID is longer, than owr dicplay + if (knownSsid.length() > u8x8.getCols()) + u8x8.print("~"); + + // Second row with IP or Psssword + u8x8.setCursor(1, 1); + // Print password in AP mode and if led is OFF. + if (apActive && bri == 0) + u8x8.print(apPass); + else + u8x8.print(knownIp); + + // Third row with mode name + u8x8.setCursor(2, 2); + uint8_t qComma = 0; + bool insideQuotes = false; + uint8_t printedChars = 0; + char singleJsonSymbol; + + // Find the mode name in JSON + for (size_t i = 0; i < strlen_P(JSON_mode_names); i++) { + singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i); + switch (singleJsonSymbol) { + case '"': + insideQuotes = !insideQuotes; + break; + case '[': + case ']': + break; + case ',': + qComma++; + default: + if (!insideQuotes || (qComma != knownMode)) + break; + u8x8.print(singleJsonSymbol); + printedChars++; + } + if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) + break; + } + // Fourth row with palette name + u8x8.setCursor(2, 3); + qComma = 0; + insideQuotes = false; + printedChars = 0; + // Looking for palette name in JSON. + for (size_t i = 0; i < strlen_P(JSON_palette_names); i++) { + singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i); + switch (singleJsonSymbol) { + case '"': + insideQuotes = !insideQuotes; + break; + case '[': + case ']': + break; + case ',': + qComma++; + default: + if (!insideQuotes || (qComma != knownPalette)) + break; + u8x8.print(singleJsonSymbol); + printedChars++; + } + if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) + break; + } + + u8x8.setFont(u8x8_font_open_iconic_embedded_1x1); + u8x8.drawGlyph(0, 0, 80); // wifi icon + u8x8.drawGlyph(0, 1, 68); // home icon + u8x8.setFont(u8x8_font_open_iconic_weather_2x2); + u8x8.drawGlyph(0, 2, 66 + (bri > 0 ? 3 : 0)); // sun/moon icon +} From 447594b5ea2f1c6d323cb983bf97f83bdad91bbd Mon Sep 17 00:00:00 2001 From: Debashish Sahu Date: Sat, 22 Feb 2020 10:19:05 -0500 Subject: [PATCH 111/232] PIO cleanup and ESP8266/32 core bump (#699) * Try to fix TravisCI * Bump ESP8266/ESP32 core version Co-authored-by: Aircoookie --- platformio.ini | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/platformio.ini b/platformio.ini index 15440660..dd5f7a73 100644 --- a/platformio.ini +++ b/platformio.ini @@ -77,12 +77,14 @@ arduino_core_2_5_0 = espressif8266@2.0.4 arduino_core_2_5_2 = espressif8266@2.2.3 arduino_core_2_6_1 = espressif8266@2.3.0 arduino_core_2_6_2 = espressif8266@2.3.1 +arduino_core_2_6_3 = espressif8266@2.3.3 arduino_core_stage = https://github.com/platformio/platform-espressif8266.git#feature/stage -platform = ${common:esp8266.arduino_core_2_6_2} +platform = ${common:esp8266.arduino_core_2_6_3} build_flags = -D ESP8266 -D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH -Wl,-Teagle.flash.4m1m.ld ;;;; Required for core > v2.5.0 or staging version 4MB Flash 3MB SPIFFs +lib_ignore = AsyncTCP [common:esp8266_1M] platform = espressif8266@1.8.0 @@ -96,6 +98,7 @@ build_flags = -D WLED_DISABLE_CRONIXIE ; -D WLED_DISABLE_HUESYNC ; -D WLED_DISABLE_INFRARED +lib_ignore = ${common:esp8266.lib_ignore} [common:esp8266_512k] platform = espressif8266@1.8.0 @@ -109,12 +112,16 @@ build_flags = -D WLED_DISABLE_CRONIXIE -D WLED_DISABLE_HUESYNC ; -D WLED_DISABLE_INFRARED +lib_ignore = ${common:esp8266.lib_ignore} [common:esp32] -platform = espressif32@1.11.1 +platform = espressif32@1.11.2 build_flags = -D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH -D ARDUINO_ARCH_ESP32 +lib_ignore = + ESPAsyncTCP + ESPAsyncUDP # see: http://docs.platformio.org/en/latest/platforms/espressif8266.html [env:nodemcuv2] @@ -129,7 +136,7 @@ build_flags = lib_deps = ${common.lib_deps_external} lib_compat_mode = strict -lib_ignore = AsyncTCP +lib_ignore = ${common:esp8266.lib_ignore} [env:d1_mini] board = d1_mini @@ -143,7 +150,7 @@ build_flags = lib_deps = ${common.lib_deps_external} lib_compat_mode = strict -lib_ignore = AsyncTCP +lib_ignore = ${common:esp8266.lib_ignore} [env:esp01_1m] board = esp01_1m @@ -159,7 +166,7 @@ build_flags = lib_deps = ${common.lib_deps_external} lib_compat_mode = strict -lib_ignore = AsyncTCP +lib_ignore = ${common:esp8266.lib_ignore} [env:esp01] board = esp01 @@ -174,7 +181,7 @@ build_flags = lib_deps = ${common.lib_deps_external} lib_compat_mode = strict -lib_ignore = AsyncTCP +lib_ignore = ${common:esp8266.lib_ignore} [env:esp07] board = esp07 @@ -188,7 +195,7 @@ build_flags = lib_deps = ${common.lib_deps_external} lib_compat_mode = strict -lib_ignore = AsyncTCP +lib_ignore = ${common:esp8266.lib_ignore} # see: http://docs.platformio.org/en/latest/platforms/espressif32.html [env:esp32dev] @@ -202,8 +209,7 @@ build_flags = ${common:esp32.build_flags} lib_deps = ${common.lib_deps_external} -lib_ignore = - ESPAsyncUDP +lib_ignore = ${common:esp32.lib_ignore} lib_compat_mode = strict [env:esp8285_4CH_MagicHome] @@ -220,7 +226,7 @@ build_flags = lib_deps = ${common.lib_deps_external} lib_compat_mode = strict -lib_ignore = AsyncTCP +lib_ignore = ${common:esp8266.lib_ignore} [env:esp8285_4CH_H801] board = esp8285 @@ -237,7 +243,7 @@ build_flags = lib_deps = ${common.lib_deps_external} lib_compat_mode = strict -lib_ignore = AsyncTCP +lib_ignore = ${common:esp8266.lib_ignore} [env:esp8285_5CH_H801] board = esp8285 @@ -255,5 +261,4 @@ build_flags = lib_deps = ${common.lib_deps_external} lib_compat_mode = strict -lib_ignore = AsyncTCP - +lib_ignore = ${common:esp8266.lib_ignore} From e621fdec0ca722f247c60a1895439d1926b12287 Mon Sep 17 00:00:00 2001 From: Def3nder Date: Sat, 22 Feb 2020 17:20:34 +0100 Subject: [PATCH 112/232] Remove analog flicker (#678) * remove analog LED flicker run SetRgbwPwm from main loop and with GetPixelColor(0) to get all effects using fade_out() working. * correct unintended bitwise AND to logical AND * Update analogLastShow * new Arduino Core WaveForm library included * new Arduino Core only for 8266 * correct formating + define for MQTT_KEEP_ALIVE * fix for ESP32 * reduce scope of variable "done" * call analogWrite only if Color or Bri did change * Remove duplicate wifi sleep code Co-authored-by: Aircoookie --- wled00/FX.h | 9 +- wled00/FX_fcn.cpp | 34 ++ wled00/NpbWrapper.h | 39 +-- .../arduino/core_esp8266_waveform.cpp | 312 ++++++++++++++++++ .../arduino/core_esp8266_waveform.h | 71 ++++ wled00/wled00.ino | 16 + wled00/wled01_eeprom.ino | 2 +- wled00/wled05_init.ino | 4 +- wled00/wled17_mqtt.ino | 3 +- wled00/wled19_json.ino | 11 + 10 files changed, 464 insertions(+), 37 deletions(-) create mode 100644 wled00/src/dependencies/arduino/core_esp8266_waveform.cpp create mode 100644 wled00/src/dependencies/arduino/core_esp8266_waveform.h diff --git a/wled00/FX.h b/wled00/FX.h index 9f1fda41..eec42990 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -415,7 +415,8 @@ class WS2812FX { resetSegments(), setPixelColor(uint16_t n, uint32_t c), setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), - show(void); + show(void), + setRgbwPwm(void); bool reverseMode = false, @@ -627,6 +628,12 @@ class WS2812FX { uint32_t _lastPaletteChange = 0; uint32_t _lastShow = 0; + #ifdef WLED_USE_ANALOG_LEDS + uint32_t _analogLastShow = 0; + uint32_t _analogLastColor = 0; + uint8_t _analogLastBri = 0; + #endif + uint8_t _segment_index = 0; uint8_t _segment_index_palette_last = 99; segment _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 24 bytes per element diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 73ddbf79..0707392d 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -829,6 +829,40 @@ bool WS2812FX::segmentsAreIdentical(Segment* a, Segment* b) return true; } +#ifdef WLED_USE_ANALOG_LEDS +void WS2812FX::setRgbwPwm(void) { + uint32_t nowUp = millis(); // Be aware, millis() rolls over every 49 days + if (nowUp - _analogLastShow < MIN_SHOW_DELAY) return; + + _analogLastShow = nowUp; + + RgbwColor color = bus->GetPixelColorRgbw(0); + byte b = getBrightness(); + if (color == _analogLastColor && b == _analogLastBri) return; + + // check color values for Warm / Cold white mix (for RGBW) // EsplanexaDevice.cpp + #ifdef WLED_USE_5CH_LEDS + if (color.R == 255 && color.G == 255 && color.B == 255 && color.W == 255) { + bus->SetRgbwPwm(0, 0, 0, 0, color.W * b / 255); + } else if (color.R == 127 && color.G == 127 && color.B == 127 && color.W == 255) { + bus->SetRgbwPwm(0, 0, 0, color.W * b / 512, color.W * b / 255); + } else if (color.R == 0 && color.G == 0 && color.B == 0 && color.W == 255) { + bus->SetRgbwPwm(0, 0, 0, color.W * b / 255, 0); + } else if (color.R == 130 && color.G == 90 && color.B == 0 && color.W == 255) { + bus->SetRgbwPwm(0, 0, 0, color.W * b / 255, color.W * b / 512); + } else if (color.R == 255 && color.G == 153 && color.B == 0 && color.W == 255) { + bus->SetRgbwPwm(0, 0, 0, color.W * b / 255, 0); + } else { // not only white colors + bus->SetRgbwPwm(color.R * b / 255, color.G * b / 255, color.B * b / 255, color.W * b / 255); + } + #else + bus->SetRgbwPwm(color.R * b / 255, color.G * b / 255, color.B * b / 255, color.W * b / 255); + #endif +} +#else +void WS2812FX::setRgbwPwm() {} +#endif + //gamma 2.4 lookup table used for color correction const byte gammaT[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/wled00/NpbWrapper.h b/wled00/NpbWrapper.h index 54d21ac1..a9fbd7c2 100644 --- a/wled00/NpbWrapper.h +++ b/wled00/NpbWrapper.h @@ -3,7 +3,9 @@ #define NpbWrapper_h //PIN CONFIGURATION +#ifndef LEDPIN #define LEDPIN 2 //strip pin. Any for ESP32, gpio2 or 3 is recommended for ESP8266 (gpio2/3 are labeled D4/RX on NodeMCU and Wemos) +#endif //#define USE_APA102 // Uncomment for using APA102 LEDs. //#define USE_WS2801 // Uncomment for using WS2801 LEDs (make sure you have NeoPixelBus v2.5.6 or newer) //#define USE_LPD8806 // Uncomment for using LPD8806 @@ -164,7 +166,7 @@ public: #endif } #else // ESP8266 - //init PWM pins - PINs 5,12,13,15 are used with Magic Home LED Controller + //init PWM pins pinMode(RPIN, OUTPUT); pinMode(GPIN, OUTPUT); pinMode(BPIN, OUTPUT); @@ -185,9 +187,9 @@ public: void SetRgbwPwm(uint8_t r, uint8_t g, uint8_t b, uint8_t w, uint8_t w2=0) { #ifdef ARDUINO_ARCH_ESP32 - ledcWrite(0, r); //RPIN - ledcWrite(1, g); //GPIN - ledcWrite(2, b); //BPIN + ledcWrite(0, r); + ledcWrite(1, g); + ledcWrite(2, b); switch (_type) { case NeoPixelType_Grb: break; #ifdef WLED_USE_5CH_LEDS @@ -196,7 +198,7 @@ public: case NeoPixelType_Grbw: ledcWrite(3, w); break; #endif } - #else + #else // ESP8266 analogWrite(RPIN, r); analogWrite(GPIN, g); analogWrite(BPIN, b); @@ -227,11 +229,6 @@ public: switch (_type) { case NeoPixelType_Grb: { _pGrb->SetPixelColor(indexPixel, RgbColor(color.R,color.G,color.B)); - #ifdef WLED_USE_ANALOG_LEDS - if (indexPixel != 0) return; //set analog LEDs from first pixel - byte b = _pGrb->GetBrightness(); - SetRgbwPwm(color.R * b / 255, color.G * b / 255, color.B * b / 255, 0); - #endif } break; case NeoPixelType_Grbw: { @@ -240,28 +237,6 @@ public: #else _pGrbw->SetPixelColor(indexPixel, color); #endif - #ifdef WLED_USE_ANALOG_LEDS - if (indexPixel != 0) return; //set analog LEDs from first pixel - byte b = _pGrbw->GetBrightness(); - // check color values for Warm / Cold white mix (for RGBW) // EsplanexaDevice.cpp - #ifdef WLED_USE_5CH_LEDS - if (color.R == 255 & color.G == 255 && color.B == 255 && color.W == 255) { - SetRgbwPwm(0, 0, 0, 0, color.W * b / 255); - } else if (color.R == 127 & color.G == 127 && color.B == 127 && color.W == 255) { - SetRgbwPwm(0, 0, 0, color.W * b / 512, color.W * b / 255); - } else if (color.R == 0 & color.G == 0 && color.B == 0 && color.W == 255) { - SetRgbwPwm(0, 0, 0, color.W * b / 255, 0); - } else if (color.R == 130 & color.G == 90 && color.B == 0 && color.W == 255) { - SetRgbwPwm(0, 0, 0, color.W * b / 255, color.W * b / 512); - } else if (color.R == 255 & color.G == 153 && color.B == 0 && color.W == 255) { - SetRgbwPwm(0, 0, 0, color.W * b / 255, 0); - } else { // not only white colors - SetRgbwPwm(color.R * b / 255, color.G * b / 255, color.B * b / 255, color.W * b / 255); - } - #else - SetRgbwPwm(color.R * b / 255, color.G * b / 255, color.B * b / 255, color.W * b / 255); - #endif - #endif } break; } diff --git a/wled00/src/dependencies/arduino/core_esp8266_waveform.cpp b/wled00/src/dependencies/arduino/core_esp8266_waveform.cpp new file mode 100644 index 00000000..6a52b1b3 --- /dev/null +++ b/wled00/src/dependencies/arduino/core_esp8266_waveform.cpp @@ -0,0 +1,312 @@ +/* + esp8266_waveform - General purpose waveform generation and control, + supporting outputs on all pins in parallel. + + Copyright (c) 2018 Earle F. Philhower, III. All rights reserved. + + The core idea is to have a programmable waveform generator with a unique + high and low period (defined in microseconds). TIMER1 is set to 1-shot + mode and is always loaded with the time until the next edge of any live + waveforms. + + Up to one waveform generator per pin supported. + + Each waveform generator is synchronized to the ESP cycle counter, not the + timer. This allows for removing interrupt jitter and delay as the counter + always increments once per 80MHz clock. Changes to a waveform are + contiguous and only take effect on the next waveform transition, + allowing for smooth transitions. + + This replaces older tone(), analogWrite(), and the Servo classes. + + Everywhere in the code where "cycles" is used, it means ESP.getCycleTime() + cycles, not TIMER1 cycles (which may be 2 CPU clocks @ 160MHz). + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifdef ESP8266 +#include +#include "ets_sys.h" +#include "core_esp8266_waveform.h" + +extern "C" { + +// Maximum delay between IRQs +#define MAXIRQUS (10000) + +// Set/clear GPIO 0-15 by bitmask +#define SetGPIO(a) do { GPOS = a; } while (0) +#define ClearGPIO(a) do { GPOC = a; } while (0) + +// Waveform generator can create tones, PWM, and servos +typedef struct { + uint32_t nextServiceCycle; // ESP cycle timer when a transition required + uint32_t expiryCycle; // For time-limited waveform, the cycle when this waveform must stop + uint32_t nextTimeHighCycles; // Copy over low->high to keep smooth waveform + uint32_t nextTimeLowCycles; // Copy over high->low to keep smooth waveform +} Waveform; + +static Waveform waveform[17]; // State of all possible pins +static volatile uint32_t waveformState = 0; // Is the pin high or low, updated in NMI so no access outside the NMI code +static volatile uint32_t waveformEnabled = 0; // Is it actively running, updated in NMI so no access outside the NMI code + +// Enable lock-free by only allowing updates to waveformState and waveformEnabled from IRQ service routine +static volatile uint32_t waveformToEnable = 0; // Message to the NMI handler to start a waveform on a inactive pin +static volatile uint32_t waveformToDisable = 0; // Message to the NMI handler to disable a pin from waveform generation + +static uint32_t (*timer1CB)() = NULL; + + +// Non-speed critical bits +#pragma GCC optimize ("Os") + +static inline ICACHE_RAM_ATTR uint32_t GetCycleCount() { + uint32_t ccount; + __asm__ __volatile__("esync; rsr %0,ccount":"=a"(ccount)); + return ccount; +} + +// Interrupt on/off control +static ICACHE_RAM_ATTR void timer1Interrupt(); +static bool timerRunning = false; + +static void initTimer() { + timer1_disable(); + ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL); + ETS_FRC_TIMER1_NMI_INTR_ATTACH(timer1Interrupt); + timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE); + timerRunning = true; +} + +static void ICACHE_RAM_ATTR deinitTimer() { + ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL); + timer1_disable(); + timer1_isr_init(); + timerRunning = false; +} + +// Set a callback. Pass in NULL to stop it +void setTimer1Callback(uint32_t (*fn)()) { + timer1CB = fn; + if (!timerRunning && fn) { + initTimer(); + timer1_write(microsecondsToClockCycles(1)); // Cause an interrupt post-haste + } else if (timerRunning && !fn && !waveformEnabled) { + deinitTimer(); + } +} + +// Start up a waveform on a pin, or change the current one. Will change to the new +// waveform smoothly on next low->high transition. For immediate change, stopWaveform() +// first, then it will immediately begin. +int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS) { + if ((pin > 16) || isFlashInterfacePin(pin)) { + return false; + } + Waveform *wave = &waveform[pin]; + // Adjust to shave off some of the IRQ time, approximately + wave->nextTimeHighCycles = microsecondsToClockCycles(timeHighUS); + wave->nextTimeLowCycles = microsecondsToClockCycles(timeLowUS); + wave->expiryCycle = runTimeUS ? GetCycleCount() + microsecondsToClockCycles(runTimeUS) : 0; + if (runTimeUS && !wave->expiryCycle) { + wave->expiryCycle = 1; // expiryCycle==0 means no timeout, so avoid setting it + } + + uint32_t mask = 1<nextServiceCycle = GetCycleCount() + microsecondsToClockCycles(1); + waveformToEnable |= mask; + if (!timerRunning) { + initTimer(); + timer1_write(microsecondsToClockCycles(10)); + } else { + // Ensure timely service.... + if (T1L > microsecondsToClockCycles(10)) { + timer1_write(microsecondsToClockCycles(10)); + } + } + while (waveformToEnable) { + delay(0); // Wait for waveform to update + } + } + + return true; +} + +// Speed critical bits +#pragma GCC optimize ("O2") +// Normally would not want two copies like this, but due to different +// optimization levels the inline attribute gets lost if we try the +// other version. + +static inline ICACHE_RAM_ATTR uint32_t GetCycleCountIRQ() { + uint32_t ccount; + __asm__ __volatile__("rsr %0,ccount":"=a"(ccount)); + return ccount; +} + +static inline ICACHE_RAM_ATTR uint32_t min_u32(uint32_t a, uint32_t b) { + if (a < b) { + return a; + } + return b; +} + +// Stops a waveform on a pin +int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) { + // Can't possibly need to stop anything if there is no timer active + if (!timerRunning) { + return false; + } + // If user sends in a pin >16 but <32, this will always point to a 0 bit + // If they send >=32, then the shift will result in 0 and it will also return false + uint32_t mask = 1< microsecondsToClockCycles(10)) { + timer1_write(microsecondsToClockCycles(10)); + } + while (waveformToDisable) { + /* no-op */ // Can't delay() since stopWaveform may be called from an IRQ + } + if (!waveformEnabled && !timer1CB) { + deinitTimer(); + } + return true; +} + +// The SDK and hardware take some time to actually get to our NMI code, so +// decrement the next IRQ's timer value by a bit so we can actually catch the +// real CPU cycle counter we want for the waveforms. +#if F_CPU == 80000000 + #define DELTAIRQ (microsecondsToClockCycles(3)) +#else + #define DELTAIRQ (microsecondsToClockCycles(2)) +#endif + + +static ICACHE_RAM_ATTR void timer1Interrupt() { + // Optimize the NMI inner loop by keeping track of the min and max GPIO that we + // are generating. In the common case (1 PWM) these may be the same pin and + // we can avoid looking at the other pins. + static int startPin = 0; + static int endPin = 0; + + uint32_t nextEventCycles = microsecondsToClockCycles(MAXIRQUS); + uint32_t timeoutCycle = GetCycleCountIRQ() + microsecondsToClockCycles(14); + + if (waveformToEnable || waveformToDisable) { + // Handle enable/disable requests from main app. + waveformEnabled = (waveformEnabled & ~waveformToDisable) | waveformToEnable; // Set the requested waveforms on/off + waveformState &= ~waveformToEnable; // And clear the state of any just started + waveformToEnable = 0; + waveformToDisable = 0; + // Find the first GPIO being generated by checking GCC's find-first-set (returns 1 + the bit of the first 1 in an int32_t) + startPin = __builtin_ffs(waveformEnabled) - 1; + // Find the last bit by subtracting off GCC's count-leading-zeros (no offset in this one) + endPin = 32 - __builtin_clz(waveformEnabled); + } + + if (waveformEnabled) { + bool done = false; + do { + nextEventCycles = microsecondsToClockCycles(MAXIRQUS); + for (int i = startPin; i <= endPin; i++) { + uint32_t mask = 1<expiryCycle) { + int32_t expiryToGo = wave->expiryCycle - now; + if (expiryToGo < 0) { + // Done, remove! + waveformEnabled &= ~mask; + if (i == 16) { + GP16O &= ~1; + } else { + ClearGPIO(mask); + } + continue; + } + } + + // Check for toggles + int32_t cyclesToGo = wave->nextServiceCycle - now; + if (cyclesToGo < 0) { + cyclesToGo = -((-cyclesToGo) % (wave->nextTimeHighCycles + wave->nextTimeLowCycles)); + waveformState ^= mask; + if (waveformState & mask) { + if (i == 16) { + GP16O |= 1; // GPIO16 write slow as it's RMW + } else { + SetGPIO(mask); + } + wave->nextServiceCycle = now + wave->nextTimeHighCycles + cyclesToGo; + nextEventCycles = min_u32(nextEventCycles, min_u32(wave->nextTimeHighCycles + cyclesToGo, 1)); + } else { + if (i == 16) { + GP16O &= ~1; // GPIO16 write slow as it's RMW + } else { + ClearGPIO(mask); + } + wave->nextServiceCycle = now + wave->nextTimeLowCycles + cyclesToGo; + nextEventCycles = min_u32(nextEventCycles, min_u32(wave->nextTimeLowCycles + cyclesToGo, 1)); + } + } else { + uint32_t deltaCycles = wave->nextServiceCycle - now; + nextEventCycles = min_u32(nextEventCycles, deltaCycles); + } + } + + // Exit the loop if we've hit the fixed runtime limit or the next event is known to be after that timeout would occur + uint32_t now = GetCycleCountIRQ(); + int32_t cycleDeltaNextEvent = timeoutCycle - (now + nextEventCycles); + int32_t cyclesLeftTimeout = timeoutCycle - now; + done = (cycleDeltaNextEvent < 0) || (cyclesLeftTimeout < 0); + } while (!done); + } // if (waveformEnabled) + + if (timer1CB) { + nextEventCycles = min_u32(nextEventCycles, timer1CB()); + } + + if (nextEventCycles < microsecondsToClockCycles(10)) { + nextEventCycles = microsecondsToClockCycles(10); + } + nextEventCycles -= DELTAIRQ; + + // Do it here instead of global function to save time and because we know it's edge-IRQ +#if F_CPU == 160000000 + T1L = nextEventCycles >> 1; // Already know we're in range by MAXIRQUS +#else + T1L = nextEventCycles; // Already know we're in range by MAXIRQUS +#endif + TEIE |= TEIE1; // Edge int enable +} + +}; +#endif \ No newline at end of file diff --git a/wled00/src/dependencies/arduino/core_esp8266_waveform.h b/wled00/src/dependencies/arduino/core_esp8266_waveform.h new file mode 100644 index 00000000..24ce91fb --- /dev/null +++ b/wled00/src/dependencies/arduino/core_esp8266_waveform.h @@ -0,0 +1,71 @@ +/* + esp8266_waveform - General purpose waveform generation and control, + supporting outputs on all pins in parallel. + + Copyright (c) 2018 Earle F. Philhower, III. All rights reserved. + + The core idea is to have a programmable waveform generator with a unique + high and low period (defined in microseconds). TIMER1 is set to 1-shot + mode and is always loaded with the time until the next edge of any live + waveforms. + + Up to one waveform generator per pin supported. + + Each waveform generator is synchronized to the ESP cycle counter, not the + timer. This allows for removing interrupt jitter and delay as the counter + always increments once per 80MHz clock. Changes to a waveform are + contiguous and only take effect on the next waveform transition, + allowing for smooth transitions. + + This replaces older tone(), analogWrite(), and the Servo classes. + + Everywhere in the code where "cycles" is used, it means ESP.getCycleTime() + cycles, not TIMER1 cycles (which may be 2 CPU clocks @ 160MHz). + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#ifndef __ESP8266_WAVEFORM_H +#define __ESP8266_WAVEFORM_H + +#ifdef __cplusplus +extern "C" { +#endif + +// Start or change a waveform of the specified high and low times on specific pin. +// If runtimeUS > 0 then automatically stop it after that many usecs. +// Returns true or false on success or failure. +int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS); +// Stop a waveform, if any, on the specified pin. +// Returns true or false on success or failure. +int stopWaveform(uint8_t pin); + +// Add a callback function to be called on *EVERY* timer1 trigger. The +// callback returns the number of microseconds until the next desired call. +// However, since it is called every timer1 interrupt, it may be called +// again before this period. It should therefore use the ESP Cycle Counter +// to determine whether or not to perform an operation. +// Pass in NULL to disable the callback and, if no other waveforms being +// generated, stop the timer as well. +// Make sure the CB function has the ICACHE_RAM_ATTR decorator. +void setTimer1Callback(uint32_t (*fn)()); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/wled00/wled00.ino b/wled00/wled00.ino index c04258f0..ee97c85f 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -93,6 +93,18 @@ #include #endif +// remove flicker because PWM signal of RGB channels can become out of phase +#if defined(WLED_USE_ANALOG_LEDS) && defined(ESP8266) + #include "src/dependencies/arduino/core_esp8266_waveform.h" +#endif + +// enable additional debug output +#ifdef WLED_DEBUG + #ifndef ESP8266 + #include + #endif +#endif + //version code in format yymmddb (b = daily build) #define VERSION 2002192 @@ -526,6 +538,10 @@ void loop() { handleOverlays(); yield(); + #ifdef WLED_USE_ANALOG_LEDS + strip.setRgbwPwm(); + #endif + if (doReboot) reset(); if (!realtimeMode) //block stuff if WARLS/Adalight is enabled diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino index 40b01ae5..84e86529 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled01_eeprom.ino @@ -3,7 +3,7 @@ * EEPROM Map: https://github.com/Aircoookie/WLED/wiki/EEPROM-Map */ -#define EEPSIZE 2560 +#define EEPSIZE 2560 //Maximum is 4096 //eeprom Version code, enables default settings instead of 0 init on update #define EEPVER 16 diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index 820670c8..c4f0b45b 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -194,10 +194,10 @@ void initConnection() WiFi.begin(clientSSID, clientPass); #ifdef ARDUINO_ARCH_ESP32 - if (noWifiSleep) WiFi.setSleep(false); + WiFi.setSleep(!noWifiSleep); WiFi.setHostname(serverDescription); #else - if (noWifiSleep) wifi_set_sleep_type(NONE_SLEEP_T); + wifi_set_sleep_type((noWifiSleep) ? NONE_SLEEP_T : MODEM_SLEEP_T); #endif } diff --git a/wled00/wled17_mqtt.ino b/wled00/wled17_mqtt.ino index db34a742..741c807f 100644 --- a/wled00/wled17_mqtt.ino +++ b/wled00/wled17_mqtt.ino @@ -3,6 +3,7 @@ */ #ifdef WLED_ENABLE_MQTT +#define MQTT_KEEP_ALIVE_TIME 60 // contact the MQTT broker every 60 seconds void parseMQTTBriPayload(char* payload) { @@ -129,7 +130,7 @@ bool initMqtt() strcpy(mqttStatusTopic, mqttDeviceTopic); strcat(mqttStatusTopic, "/status"); mqtt->setWill(mqttStatusTopic, 0, true, "offline"); - mqtt->setKeepAlive(60); + mqtt->setKeepAlive(MQTT_KEEP_ALIVE_TIME); mqtt->connect(); return true; } diff --git a/wled00/wled19_json.ino b/wled00/wled19_json.ino index 852dfdac..6bf9aafd 100644 --- a/wled00/wled19_json.ino +++ b/wled00/wled19_json.ino @@ -267,14 +267,25 @@ void serializeInfo(JsonObject root) wifi_info["channel"] = WiFi.channel(); #ifdef ARDUINO_ARCH_ESP32 + #ifdef WLED_DEBUG + wifi_info["txPower"] = (int) WiFi.getTxPower(); + wifi_info["sleep"] = (bool) WiFi.getSleep(); + #endif root["arch"] = "esp32"; root["core"] = ESP.getSdkVersion(); //root["maxalloc"] = ESP.getMaxAllocHeap(); + #ifdef WLED_DEBUG + root["resetReason0"] = (int)rtc_get_reset_reason(0); + root["resetReason1"] = (int)rtc_get_reset_reason(1); + #endif root["lwip"] = 0; #else root["arch"] = "esp8266"; root["core"] = ESP.getCoreVersion(); //root["maxalloc"] = ESP.getMaxFreeBlockSize(); + #ifdef WLED_DEBUG + root["resetReason"] = (int)ESP.getResetInfoPtr()->reason; + #endif root["lwip"] = LWIP_VERSION_MAJOR; #endif From c66af86d88fae5e0b26fd9390ed13f73c53f8769 Mon Sep 17 00:00:00 2001 From: pille Date: Sat, 22 Feb 2020 18:13:21 +0100 Subject: [PATCH 113/232] fix out-of-sync detection on E1.31 packets --- wled00/wled07_notify.ino | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/wled00/wled07_notify.ino b/wled00/wled07_notify.ino index 6af7416b..fd83f1f8 100644 --- a/wled00/wled07_notify.ino +++ b/wled00/wled07_notify.ino @@ -91,13 +91,12 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ //E1.31 protocol support // skip out-of-sequence packets - if (p->sequence_number < e131LastSequenceNumber && p->sequence_number - e131LastSequenceNumber > -20){ + if (p->sequence_number < e131LastSequenceNumber && p->sequence_number > 20 && e131LastSequenceNumber < 250){ DEBUG_PRINT("skipping E1.31 frame (last seq="); DEBUG_PRINT(e131LastSequenceNumber); DEBUG_PRINT(", current seq="); DEBUG_PRINT(p->sequence_number); DEBUG_PRINTLN(")"); - e131LastSequenceNumber = p->sequence_number; return; } e131LastSequenceNumber = p->sequence_number; From 392277100c80ab9be062c71345d6d8361189e188 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sun, 23 Feb 2020 02:18:25 +0100 Subject: [PATCH 114/232] Release of WLED v0.9.1 --- wled00/wled08_led.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/wled08_led.ino b/wled00/wled08_led.ino index 124e43bd..8aeac5df 100644 --- a/wled00/wled08_led.ino +++ b/wled00/wled08_led.ino @@ -79,7 +79,7 @@ bool colorChanged() { if (col[i] != colIT[i]) return true; if (colSec[i] != colSecIT[i]) return true; - if (col[i] != colNlT[i]) return true; + //if (col[i] != colNlT[i]) return true; (this effectively made the ESP send out sync packets every time colorUpdated() is called, even if nothing changed) } if (bri != briIT) return true; return false; From 513bc2c0ab1577dff4638592277c9f5f89019938 Mon Sep 17 00:00:00 2001 From: Def3nder Date: Sun, 23 Feb 2020 22:21:32 +0100 Subject: [PATCH 115/232] Correct SunRise/NightLight with Color Fade (#710) --- wled00/wled08_led.ino | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wled00/wled08_led.ino b/wled00/wled08_led.ino index 8aeac5df..d1ccc0d6 100644 --- a/wled00/wled08_led.ino +++ b/wled00/wled08_led.ino @@ -231,15 +231,15 @@ void handleNightlight() nightlightDelayMs = (int)(nightlightDelayMins*60000); nightlightActiveOld = true; briNlT = bri; - for (byte i=0; i<4; i++) colNlT[i] = col[i]; // remember starting color + for (byte i=0; i<4; i++) colNlT[i] = colT[i]; // remember starting color } float nper = (millis() - nightlightStartTime)/((float)nightlightDelayMs); if (nightlightFade) { bri = briNlT + ((nightlightTargetBri - briNlT)*nper); - if (nightlightColorFade) // color fading only is enabled with "NF=2" + if (nightlightColorFade) // color fading only is enabled with "NF=2" { - for (byte i=0; i<4; i++) col[i] = colNlT[i]+ ((colSec[i] - colNlT[i])*nper); // fading from actual color to secondary color + for (byte i=0; i<4; i++) colT[i] = colNlT[i]+ ((colSecT[i] - colNlT[i])*nper); // fading from actual color to secondary color } colorUpdated(NOTIFIER_CALL_MODE_NO_NOTIFY); } From 8be72f6f2353e87c36a4ee93804fa83793128b23 Mon Sep 17 00:00:00 2001 From: jwingefeld Date: Sun, 23 Feb 2020 22:24:51 +0100 Subject: [PATCH 116/232] DMX Support for WLED (#704) * initial dmx setup * adds support for multiple fixtures, addr gaps, start addresses and all that good DMX stuff * removes init function. do not need. * adds some comments, removes others. words. * added menu entry and dummy HTML * added server request handler * cloned options page UI for DMX * only add code when DMX is enabled * added infobutton to HTML * DMX settings form * procedurally generated HTML form. OBACHT: Values still not coming from the EEPROM. * upped eeprom version to 15 * changed index for set to 255 to 6 because web interface wants it that way * gets values for XML from actual settings * changes the default values for dmx to blanks * reads and writes DMX settings from EEPROM (2550 - 2569) * fixes addressing bug in DMX EEPROM read * saves settings from WebUI to memory * disables DMX by default * changed a comment in the ENABLE_DMX line * makes the display of the DMX entry in settings dependant on WLED_DMX_ENABLE * adds the server listener for the DMX map * fixes a bug when selecting 255 for a channel at the dmx settings page * now actually reads the DMX settings back to the HTML UI. * cleans up a little * adds a warning message to the HTML UI when setting up defunct DMX settings * changed DMX EEPROM addressing to close a gap * basic DMX map * fixes a few styling flaws and bugs in the DMX map * changes config variables to uint16_t Co-authored-by: Aircoookie --- wled00/html_other.h | 30 +++++++ wled00/html_settings.h | 69 +++++++++++++-- wled00/wled00.ino | 187 +++++++++++++++++++++------------------ wled00/wled01_eeprom.ino | 26 +++++- wled00/wled02_xml.ino | 27 +++++- wled00/wled03_set.ino | 28 +++++- wled00/wled05_init.ino | 4 +- wled00/wled18_server.ino | 49 +++++++++- wled00/wled21_dmx.ino | 55 ++++++++++++ 9 files changed, 378 insertions(+), 97 deletions(-) create mode 100644 wled00/wled21_dmx.ino diff --git a/wled00/html_other.h b/wled00/html_other.h index c7d3e855..e27c64dd 100644 --- a/wled00/html_other.h +++ b/wled00/html_other.h @@ -15,6 +15,36 @@ const char PAGE_msg[] PROGMEM = R"=====(

%MSG%)====="; +//DMX channel map +const char PAGE_dmxmap[] PROGMEM = R"=====( + +DMX Map + + +
...
)====="; + //firmware update page const char PAGE_update[] PROGMEM = R"=====( diff --git a/wled00/html_settings.h b/wled00/html_settings.h index a3ba54a2..157d6b4c 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -1,23 +1,23 @@ /* - * Settings html - */ + Settings html +*/ //common CSS of settings pages const char PAGE_settingsCss[] PROGMEM = R"=====()====="; - //settings menu const char PAGE_settings[] PROGMEM = R"=====( -WLED Settings +WLED Settings
-
+
%DMXMENU%
+ )====="; @@ -172,6 +172,64 @@ Skip first LED:
)====="; +#ifdef WLED_ENABLE_DMX +//DMX Output settings +const char PAGE_settings_dmx[] PROGMEM = R"=====( +DMX Settings"); } diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index f7485917..0f2cb9ef 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -27,8 +27,9 @@ bool isAsterisksOnly(const char* str, byte maxLen) //called upon POST settings form submit void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) { - //0: menu 1: wifi 2: leds 3: ui 4: sync 5: time 6: sec - if (subPage <1 || subPage >6) return; + + //0: menu 1: wifi 2: leds 3: ui 4: sync 5: time 6: sec 7: DMX + if (subPage <1 || subPage >7) return; //WIFI SETTINGS if (subPage == 1) @@ -293,6 +294,29 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) aOtaEnabled = request->hasArg("AO"); } } + #ifdef WLED_ENABLE_DMX // include only if DMX is enabled + if (subPage == 7) + { + int t = request->arg("CN").toInt(); + if (t>0 && t<16) { + DMXChannels = t; + } + t = request->arg("CS").toInt(); + if (t>0 && t<513) { + DMXStart = t; + } + t = request->arg("CG").toInt(); + if (t>0 && t<513) { + DMXGap = t; + } + for (int i=0; i<15; i++) { + String argname = "CH" + String((i+1)); + t = request->arg(argname).toInt(); + DMXFixtureMap[i] = t; + } + } + + #endif if (subPage != 6 || !doReboot) saveSettingsToEEPROM(); //do not save if factory reset if (subPage == 2) { strip.init(useRGBW,ledCount,skipFirstLed); diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index c4f0b45b..bf1be5d9 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -83,7 +83,9 @@ void wledInit() if (strlen(cmDNS) > 0) ArduinoOTA.setHostname(cmDNS); } #endif - + #ifdef WLED_ENABLE_DMX + dmx.init(512); // initialize with bus length + #endif //HTTP server page init initServer(); } diff --git a/wled00/wled18_server.ino b/wled00/wled18_server.ino index f7162d3e..cf67e79f 100644 --- a/wled00/wled18_server.ino +++ b/wled00/wled18_server.ino @@ -82,6 +82,11 @@ void initServer() serveMessage(request, 200,"UI settings saved.","Redirecting...",1); }); + server.on("/settings/dmx", HTTP_POST, [](AsyncWebServerRequest *request){ + handleSettingsSet(request, 7); + serveMessage(request, 200,"UI settings saved.","Redirecting...",1); + }); + server.on("/settings/sync", HTTP_POST, [](AsyncWebServerRequest *request){ handleSettingsSet(request, 4); serveMessage(request, 200,"Sync settings saved.","Redirecting...",1); @@ -201,6 +206,16 @@ void initServer() }); } + + #ifdef WLED_ENABLE_DMX + server.on("/dmxmap", HTTP_GET, [](AsyncWebServerRequest *request){ + request->send_P(200, "text/html", PAGE_dmxmap , dmxProcessor); + }); + #else + server.on("/dmxmap", HTTP_GET, [](AsyncWebServerRequest *request){ + serveMessage(request, 501, "Not implemented", "DMX support is not enabled in this build.", 254); + }); + #endif server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ if (captivePortal(request)) return; serveIndexOrWelcome(request); @@ -301,10 +316,38 @@ String settingsProcessor(const String& var) getSettingsJS(optionType, buf); return String(buf); } + + #ifdef WLED_ENABLE_DMX + + if (var == "DMXMENU") { + return String("
"); + } + + #endif if (var == "SCSS") return String(FPSTR(PAGE_settingsCss)); return String(); } +String dmxProcessor(const String& var) +{ + String mapJS; + #ifdef WLED_ENABLE_DMX + if (var == "DMXVARS") { + mapJS += "\nCN=" + String(DMXChannels) + ";\n"; + mapJS += "CS=" + String(DMXStart) + ";\n"; + mapJS += "CG=" + String(DMXGap) + ";\n"; + mapJS += "LC=" + String(ledCount) + ";\n"; + mapJS += "var CH=["; + for (int i=0;i<15;i++) { + mapJS += String(DMXFixtureMap[i]) + ","; + } + mapJS += "0];"; + } + #endif + + return mapJS; +} + void serveSettings(AsyncWebServerRequest* request) { @@ -318,6 +361,9 @@ void serveSettings(AsyncWebServerRequest* request) else if (url.indexOf("sync") > 0) subPage = 4; else if (url.indexOf("time") > 0) subPage = 5; else if (url.indexOf("sec") > 0) subPage = 6; + #ifdef WLED_ENABLE_DMX // include only if DMX is enabled + else if (url.indexOf("dmx") > 0) subPage = 7; + #endif } else subPage = 255; //welcome page if (subPage == 1 && wifiLock && otaLock) @@ -339,7 +385,8 @@ void serveSettings(AsyncWebServerRequest* request) case 4: request->send_P(200, "text/html", PAGE_settings_sync, settingsProcessor); break; case 5: request->send_P(200, "text/html", PAGE_settings_time, settingsProcessor); break; case 6: request->send_P(200, "text/html", PAGE_settings_sec , settingsProcessor); break; + case 7: request->send_P(200, "text/html", PAGE_settings_dmx , settingsProcessor); break; case 255: request->send_P(200, "text/html", PAGE_welcome); break; - default: request->send_P(200, "text/html", PAGE_settings); + default: request->send_P(200, "text/html", PAGE_settings , settingsProcessor); } } diff --git a/wled00/wled21_dmx.ino b/wled00/wled21_dmx.ino new file mode 100644 index 00000000..4230f07d --- /dev/null +++ b/wled00/wled21_dmx.ino @@ -0,0 +1,55 @@ +/* + * Support for DMX via MAX485. + * Needs the espdmx library. You might have to change the output pin within the library. Sketchy, i know. + * https://github.com/Rickgg/ESP-Dmx + */ +#ifdef WLED_ENABLE_DMX + +void handleDMX() { + // TODO: calculate brightness manually if no shutter channel is set + + uint8_t brightness = strip.getBrightness(); + + for (int i = 0; i < ledCount; i++) { // uses the amount of LEDs as fixture count + + uint32_t in = strip.getPixelColor(i); // time to get the colors for the individual fixtures as suggested by AirCookie at issue #462 + byte w = in >> 24 & 0xFF; + byte r = in >> 16 & 0xFF; + byte g = in >> 8 & 0xFF; + byte b = in & 0xFF; + + int DMXFixtureStart = DMXStart + (DMXGap * i); + for (int j = 0; j < DMXChannels; j++) { + int DMXAddr = DMXFixtureStart + j; + switch (DMXFixtureMap[j]) { + case 0: // Set this channel to 0. Good way to tell strobe- and fade-functions to fuck right off. + dmx.write(DMXAddr, 0); + break; + case 1: // Red + dmx.write(DMXAddr, r); + break; + case 2: // Green + dmx.write(DMXAddr, g); + break; + case 3: // Blue + dmx.write(DMXAddr, b); + break; + case 4: // White + dmx.write(DMXAddr, w); + break; + case 5: // Shutter channel. Controls the brightness. + dmx.write(DMXAddr, brightness); + break; + case 6:// Sets this channel to 255. Like 0, but more wholesome. + dmx.write(DMXAddr, 255); + break; + } + } + } + + dmx.update(); // update the DMX bus +} + +#else +void handleDMX() {} +#endif From 9a0db83f83c6cbfc4dcc17763dd6983de72086a6 Mon Sep 17 00:00:00 2001 From: srg74 <28492985+srg74@users.noreply.github.com> Date: Sun, 23 Feb 2020 21:03:36 -0500 Subject: [PATCH 117/232] Added Heltec WiFi-Kit-8 with 0.91" display --- .../Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino index 9e6e0b79..3b7a05fe 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino @@ -2,7 +2,8 @@ #include //Dallastemperature sensor #ifdef ARDUINO_ARCH_ESP32 //ESP32 boards uint8_t SCL_PIN = 22; -uint8_t SDA_PIN = 21; +uint8_t SDA_PIN = 21; +// uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8 OneWire oneWire(23); #else //ESP8266 boards uint8_t SCL_PIN = 5; @@ -13,7 +14,7 @@ OneWire oneWire(13); //ESP8266 Wemos D1 mini board use SCL=5 SDA=4 while ESP32 Wemos32 mini board use SCL=22 SDA=21 #define U8X8_PIN_SCL SCL_PIN #define U8X8_PIN_SDA SDA_PIN - +//#define U8X8_PIN_RESET RST_PIN // Uncoment for Heltec WiFi-Kit-8 // Dallas sensor DallasTemperature sensor(&oneWire); long temptimer = millis(); @@ -29,6 +30,8 @@ long lastMeasure = 0; U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA // --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3" //U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA +// --> Third choise Heltec WiFi-Kit-8 with build-in OLED 128X32 0.91" +//U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_RESET, U8X8_PIN_SCL, U8X8_PIN_SDA); // Constracor for Heltec WiFi-Kit-8 // gets called once at boot. Do all initialization that doesn't depend on // network here void userSetup() { From a8e7aa99e8b6ba7413175cee738a6288bfcd765e Mon Sep 17 00:00:00 2001 From: srg74 <28492985+srg74@users.noreply.github.com> Date: Sun, 23 Feb 2020 22:48:58 -0500 Subject: [PATCH 118/232] Update wled06_usermod.ino --- usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino index 3b7a05fe..5373d54f 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino @@ -3,11 +3,11 @@ #ifdef ARDUINO_ARCH_ESP32 //ESP32 boards uint8_t SCL_PIN = 22; uint8_t SDA_PIN = 21; -// uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8 OneWire oneWire(23); #else //ESP8266 boards uint8_t SCL_PIN = 5; uint8_t SDA_PIN = 4; +// uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8 OneWire oneWire(13); #endif //The SCL and SDA pins are defined here. From e8fd5de5b26ec084d09e48749fb8608f6c182b5a Mon Sep 17 00:00:00 2001 From: Def3nder Date: Mon, 24 Feb 2020 12:18:30 +0100 Subject: [PATCH 119/232] HTML API "/url" for the current effect settings (#664) --- wled00/wled02_xml.ino | 47 ++++++++++++++++++++++++++++++++++++++++ wled00/wled18_server.ino | 4 ++++ 2 files changed, 51 insertions(+) diff --git a/wled00/wled02_xml.ino b/wled00/wled02_xml.ino index 561f7723..e5083131 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/wled02_xml.ino @@ -99,6 +99,53 @@ char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr) if (request != nullptr) request->send(200, "text/xml", obuf); } +char* URL_response(AsyncWebServerRequest *request) +{ + char sbuf[256]; //allocate local buffer if none passed + char s2buf[100]; + obuf = s2buf; + olen = 0; + + char s[16]; + oappend("http://"); + IPAddress localIP = WiFi.localIP(); + sprintf(s, "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]); + oappend(s); + oappend("/win&A="); + oappendi(bri); + oappend("&CL=h"); + for (int i = 0; i < 3; i++) + { + sprintf(s,"%02X", col[i]); + oappend(s); + } + oappend("&C2=h"); + for (int i = 0; i < 3; i++) + { + sprintf(s,"%02X", colSec[i]); + oappend(s); + } + oappend("&FX="); + oappendi(effectCurrent); + oappend("&SX="); + oappendi(effectSpeed); + oappend("&IX="); + oappendi(effectIntensity); + oappend("&FP="); + oappendi(effectPalette); + + obuf = sbuf; + olen = 0; + + oappend(""); + oappend(s2buf); + oappend(""); + + if (request != nullptr) request->send(200, "text/html", obuf); +} + //append a numeric setting to string buffer void sappend(char stype, const char* key, int val) { diff --git a/wled00/wled18_server.ino b/wled00/wled18_server.ino index cf67e79f..826425d1 100644 --- a/wled00/wled18_server.ino +++ b/wled00/wled18_server.ino @@ -143,6 +143,10 @@ void initServer() request->send_P(200, "text/html", PAGE_usermod); }); + server.on("/url", HTTP_GET, [](AsyncWebServerRequest *request){ + URL_response(request); + }); + server.on("/teapot", HTTP_GET, [](AsyncWebServerRequest *request){ serveMessage(request, 418, "418. I'm a teapot.", "(Tangible Embedded Advanced Project Of Twinkling)", 254); }); From 5d7e8924641492dc4a58f324f52db97e313c5293 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Mon, 24 Feb 2020 12:44:41 +0100 Subject: [PATCH 120/232] Different PIO settings --- platformio.ini | 22 ++++++++++++---------- readme.md | 2 +- wled00/wled00.ino | 4 ++-- wled00/wled19_json.ino | 1 - 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/platformio.ini b/platformio.ini index dd5f7a73..01799757 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,6 +16,10 @@ default_envs = nodemcuv2 ; default_envs = esp8285_4CH_MagicHome ; default_envs = esp8285_4CH_H801 ; default_envs = esp8285_5CH_H801 +# Release binaries follow +; default_envs = nodemcuv2, esp01, esp01_1m, esp32dev, + + [common] framework = arduino @@ -82,30 +86,28 @@ arduino_core_stage = https://github.com/platformio/platform-espressif8266.git#fe platform = ${common:esp8266.arduino_core_2_6_3} build_flags = -D ESP8266 - -D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH + -D PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH -Wl,-Teagle.flash.4m1m.ld ;;;; Required for core > v2.5.0 or staging version 4MB Flash 3MB SPIFFs lib_ignore = AsyncTCP [common:esp8266_1M] -platform = espressif8266@1.8.0 +platform = ${common:esp8266.arduino_core_2_6_3} build_flags = - -D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH - -Wl,-Teagle.flash.1m0.ld ;;;; Compile with no SPIFFS to leave space for OTA - ; -D WLED_DISABLE_MOBILE_UI + -D PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH + -Wl,-Teagle.flash.1m.ld ;;;; Compile with no SPIFFS to leave space for OTA ; -D WLED_DISABLE_OTA ; -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK -D WLED_DISABLE_CRONIXIE ; -D WLED_DISABLE_HUESYNC - ; -D WLED_DISABLE_INFRARED + -D WLED_DISABLE_INFRARED lib_ignore = ${common:esp8266.lib_ignore} [common:esp8266_512k] -platform = espressif8266@1.8.0 +platform = ${common:esp8266.arduino_core_2_6_3} build_flags = - -D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH - -Wl,-Teagle.flash.512k0.ld ;;;; Compile with no SPIFFS - ; -D WLED_DISABLE_MOBILE_UI + -D PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH + -Wl,-Teagle.flash.512k.ld ;;;; Compile with no SPIFFS -D WLED_DISABLE_OTA ; -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK diff --git a/readme.md b/readme.md index 044344ea..1cbe9b80 100644 --- a/readme.md +++ b/readme.md @@ -11,7 +11,7 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B, WS2811, SK6812, APA102) LEDs! ### Features: -- WS2812FX library integrated for almost 100 special effects +- WS2812FX library integrated for over 100 special effects - FastLED noise effects and palettes - Modern UI with color, effect and segment controls - Segments to set different effects and colors to parts of the LEDs diff --git a/wled00/wled00.ino b/wled00/wled00.ino index ee97c85f..921b204b 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -21,7 +21,7 @@ //#define WLED_DISABLE_BLYNK //saves 6kb //#define WLED_DISABLE_CRONIXIE //saves 3kb //#define WLED_DISABLE_HUESYNC //saves 4kb -//#define WLED_DISABLE_INFRARED //there is no pin left for this on ESP8266-01, saves 25kb (!) +//#define WLED_DISABLE_INFRARED //there is no pin left for this on ESP8266-01, saves 12kb #define WLED_ENABLE_MQTT //saves 12kb #define WLED_ENABLE_ADALIGHT //saves 500b only @@ -106,7 +106,7 @@ #endif //version code in format yymmddb (b = daily build) -#define VERSION 2002192 +#define VERSION 2002222 char versionString[] = "0.9.1"; diff --git a/wled00/wled19_json.ino b/wled00/wled19_json.ino index 6bf9aafd..5cc1f519 100644 --- a/wled00/wled19_json.ino +++ b/wled00/wled19_json.ino @@ -321,7 +321,6 @@ void serializeInfo(JsonObject root) root["brand"] = "WLED"; root["product"] = "DIY light"; - root["btype"] = "src"; root["mac"] = escapedMac; } From 7360b882acff7e0c6a52d675324d688ff39136e1 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Mon, 24 Feb 2020 12:54:23 +0100 Subject: [PATCH 121/232] DMX settings only when enabled --- wled00/wled00.ino | 7 ++++--- wled00/wled01_eeprom.ino | 8 +++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 4da5c700..60643975 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -262,13 +262,14 @@ bool aOtaEnabled = true; //ArduinoOTA allows easy updates d uint16_t userVar0 = 0, userVar1 = 0; +#ifdef WLED_ENABLE_DMX //dmx CONFIG -uint16_t DMXChannels = 7; // number of channels per fixture -uint16_t DMXFixtureMap[15] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +byte DMXChannels = 7; // number of channels per fixture +byte DMXFixtureMap[15] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // assigns the different channels to different functions. See wled21_dmx.ino for more information. uint16_t DMXGap = 10; // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. uint16_t DMXStart = 10; // start address of the first fixture - +#endif //internal global variable declarations diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino index 73508a4f..0ff9497f 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled01_eeprom.ino @@ -258,15 +258,17 @@ void saveSettingsToEEPROM() EEPROM.write(2523, (mqttPort >> 8) & 0xFF); // DMX (2530 - 2549) + #ifdef WLED_ENABLE_DMX EEPROM.write(2530, DMXChannels); EEPROM.write(2531, DMXGap & 0xFF); EEPROM.write(2532, (DMXGap >> 8) & 0xFF); EEPROM.write(2533, DMXStart & 0xFF); EEPROM.write(2534, (DMXStart >> 8) & 0xFF); - for (int i=0;i<15;i++) { + for (int i=0; i<15; i++) { EEPROM.write(2535+i, DMXFixtureMap[i]); } // last used: 2549. maybe leave a few bytes for future expansion and go on with 2600 kthxbye. + #endif commit(); } @@ -536,7 +538,7 @@ void loadSettingsFromEEPROM(bool first) readStringFromEEPROM(2220, blynkApiKey, 35); if (strlen(blynkApiKey) < 25) blynkApiKey[0] = 0; - + #ifdef WLED_ENABLE_DMX // DMX (2530 - 2549)2535 DMXChannels = EEPROM.read(2530); DMXGap = EEPROM.read(2531) + ((EEPROM.read(2532) << 8) & 0xFF00); @@ -545,7 +547,7 @@ void loadSettingsFromEEPROM(bool first) for (int i=0;i<15;i++) { DMXFixtureMap[i] = EEPROM.read(2535+i); } //last used: 2549. maybe leave a few bytes for future expansion and go on with 2600 kthxbye. - + #endif //user MOD memory //2944 - 3071 reserved From 0455c09e4cb2812d0c99acaf300a7627cadf805a Mon Sep 17 00:00:00 2001 From: zewelor Date: Mon, 24 Feb 2020 16:42:24 +0100 Subject: [PATCH 122/232] Allow platformio ini overrides --- .gitignore | 1 + platformio.ini | 1 + platformio_override.ini.example | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 platformio_override.ini.example diff --git a/.gitignore b/.gitignore index 2116e693..ea49cb51 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ !.vscode/extensions.json /wled00/Release /wled00/extLibs +/platformio_override.ini diff --git a/platformio.ini b/platformio.ini index 01799757..127457d2 100644 --- a/platformio.ini +++ b/platformio.ini @@ -6,6 +6,7 @@ src_dir = ./wled00 data_dir = ./wled00/data ;lib_extra_dirs = ./wled00/src lib_dir = ./wled00/src +extra_configs = platformio_override.ini ; Please uncomment one of the 5 lines below to select your board default_envs = nodemcuv2 ; default_envs = esp01 diff --git a/platformio_override.ini.example b/platformio_override.ini.example new file mode 100644 index 00000000..03a77056 --- /dev/null +++ b/platformio_override.ini.example @@ -0,0 +1,18 @@ +; Example PlatformIO Project Configuration Override +; ------------------------------------------------------------------------------ +; Copy to platformio_override.ini to activate overrides +; ------------------------------------------------------------------------------ +; Please visit documentation: https://docs.platformio.org/page/projectconf.html + +[env:esp8266_1m_custom] +platform = ${common:esp8266.arduino_core_2_6_3} +build_flags = + -D PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH + -Wl,-Teagle.flash.1m.ld ;;;; Compile with no SPIFFS to leave space for OTA + ; -D WLED_DISABLE_OTA + -D WLED_DISABLE_ALEXA + -D WLED_DISABLE_BLYNK + -D WLED_DISABLE_CRONIXIE + -D WLED_DISABLE_HUESYNC + -D WLED_DISABLE_INFRARED +lib_ignore = ${common:esp8266.lib_ignore} \ No newline at end of file From 89f60a0422385f30f476907473e4f8edb5646a8d Mon Sep 17 00:00:00 2001 From: cschwinne Date: Mon, 24 Feb 2020 17:25:40 +0100 Subject: [PATCH 123/232] Refactor colorUpdated() --- platformio.ini | 5 ++--- wled00/const.h | 29 +++++++++++++++++++++-------- wled00/wled00.ino | 2 +- wled00/wled07_notify.ino | 1 - wled00/wled08_led.ino | 34 +++++++++++++++------------------- 5 files changed, 39 insertions(+), 32 deletions(-) diff --git a/platformio.ini b/platformio.ini index 01799757..08033581 100644 --- a/platformio.ini +++ b/platformio.ini @@ -109,11 +109,11 @@ build_flags = -D PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH -Wl,-Teagle.flash.512k.ld ;;;; Compile with no SPIFFS -D WLED_DISABLE_OTA - ; -D WLED_DISABLE_ALEXA + -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK -D WLED_DISABLE_CRONIXIE -D WLED_DISABLE_HUESYNC - ; -D WLED_DISABLE_INFRARED + -D WLED_DISABLE_INFRARED lib_ignore = ${common:esp8266.lib_ignore} [common:esp32] @@ -179,7 +179,6 @@ framework = ${common.framework} build_flags = ${common.build_flags} ${common:esp8266_512k.build_flags} - -D WLED_DISABLE_INFRARED lib_deps = ${common.lib_deps_external} lib_compat_mode = strict diff --git a/wled00/const.h b/wled00/const.h index 4d287572..44065fec 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -8,13 +8,13 @@ #define AP_BEHAVIOR_BUTTON_ONLY 3 //Only when button pressed for 6 sec //Notifier callMode -#define NOTIFIER_CALL_MODE_INIT 0 // no updates on init, can be used to disable updates +#define NOTIFIER_CALL_MODE_INIT 0 //no updates on init, can be used to disable updates #define NOTIFIER_CALL_MODE_DIRECT_CHANGE 1 #define NOTIFIER_CALL_MODE_BUTTON 2 #define NOTIFIER_CALL_MODE_NOTIFICATION 3 #define NOTIFIER_CALL_MODE_NIGHTLIGHT 4 #define NOTIFIER_CALL_MODE_NO_NOTIFY 5 -#define NOTIFIER_CALL_MODE_FX_CHANGED 6 +#define NOTIFIER_CALL_MODE_FX_CHANGED 6 //no longer used #define NOTIFIER_CALL_MODE_HUE 7 #define NOTIFIER_CALL_MODE_PRESET_CYCLE 8 #define NOTIFIER_CALL_MODE_BLYNK 9 @@ -36,11 +36,24 @@ #define REALTIME_MODE_ADALIGHT 5 //E1.31 DMX modes -#define DMX_MODE_DISABLED 0 //not used -#define DMX_MODE_SINGLE_RGB 1 //all LEDs same RGB color (3 channels) -#define DMX_MODE_SINGLE_DRGB 2 //all LEDs same RGB color and master dimmer (4 channels) -#define DMX_MODE_EFFECT 3 //trigger standalone effects of WLED (11 channels) -#define DMX_MODE_MULTIPLE_RGB 4 //every LED is addressed with its own RGB (ledCount * 3 channels) -#define DMX_MODE_MULTIPLE_DRGB 5 //every LED is addressed with its own RGB and share a master dimmer (ledCount * 3 + 1 channels) +#define DMX_MODE_DISABLED 0 //not used +#define DMX_MODE_SINGLE_RGB 1 //all LEDs same RGB color (3 channels) +#define DMX_MODE_SINGLE_DRGB 2 //all LEDs same RGB color and master dimmer (4 channels) +#define DMX_MODE_EFFECT 3 //trigger standalone effects of WLED (11 channels) +#define DMX_MODE_MULTIPLE_RGB 4 //every LED is addressed with its own RGB (ledCount * 3 channels) +#define DMX_MODE_MULTIPLE_DRGB 5 //every LED is addressed with its own RGB and share a master dimmer (ledCount * 3 + 1 channels) + +//Light capability byte (unused) +#define TYPE_WS2812_RGB 0 +#define TYPE_SK6812_RGBW 1 +#define TYPE_WS2812_WWA 2 //amber + warm + cold white +#define TYPE_APA102 3 +#define TYPE_LPD8806 4 +#define TYPE_WS2801 5 +#define TYPE_ANALOG_1CH 6 //single channel PWM. Uses value of brightest RGBW channel +#define TYPE_ANALOG_2CH 7 //analog WW + CW +#define TYPE_ANALOG_3CH 8 //analog RGB +#define TYPE_ANALOG_4CH 9 //analog RGBW +#define TYPE_ANALOG_5CH 10 //analog RGB + WW + CW #endif diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 60643975..9b5a92ee 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -111,7 +111,7 @@ DMXESPSerial dmx; #endif //version code in format yymmddb (b = daily build) -#define VERSION 2002241 +#define VERSION 2002242 char versionString[] = "0.9.1"; diff --git a/wled00/wled07_notify.ino b/wled00/wled07_notify.ino index fd83f1f8..7f691dd0 100644 --- a/wled00/wled07_notify.ino +++ b/wled00/wled07_notify.ino @@ -15,7 +15,6 @@ void notify(byte callMode, bool followUp=false) case NOTIFIER_CALL_MODE_DIRECT_CHANGE: if (!notifyDirect) return; break; case NOTIFIER_CALL_MODE_BUTTON: if (!notifyButton) return; break; case NOTIFIER_CALL_MODE_NIGHTLIGHT: if (!notifyDirect) return; break; - case NOTIFIER_CALL_MODE_FX_CHANGED: if (!notifyDirect) return; break; //fx change case NOTIFIER_CALL_MODE_HUE: if (!notifyHue) return; break; case NOTIFIER_CALL_MODE_PRESET_CYCLE: if (!notifyDirect) return; break; case NOTIFIER_CALL_MODE_BLYNK: if (!notifyDirect) return; break; diff --git a/wled00/wled08_led.ino b/wled00/wled08_led.ino index d1ccc0d6..e3afcda5 100644 --- a/wled00/wled08_led.ino +++ b/wled00/wled08_led.ino @@ -95,28 +95,30 @@ void colorUpdated(int callMode) callMode != NOTIFIER_CALL_MODE_NO_NOTIFY) strip.applyToAllSelected = true; //if not from JSON api, which directly sets segments bool fxChanged = strip.setEffectConfig(effectCurrent, effectSpeed, effectIntensity, effectPalette); - if (!colorChanged()) + bool colChanged = colorChanged(); + + if (fxChanged || colChanged) { + if (realtimeTimeout == UINT32_MAX) realtimeTimeout = 0; + if (isPreset) {isPreset = false;} + else {currentPreset = -1;} + + notify(callMode); + + //set flag to update blynk and mqtt + if (callMode != NOTIFIER_CALL_MODE_PRESET_CYCLE) interfaceUpdateCallMode = callMode; + } else { if (nightlightActive && !nightlightActiveOld && callMode != NOTIFIER_CALL_MODE_NOTIFICATION && callMode != NOTIFIER_CALL_MODE_NO_NOTIFY) { notify(NOTIFIER_CALL_MODE_NIGHTLIGHT); interfaceUpdateCallMode = NOTIFIER_CALL_MODE_NIGHTLIGHT; - return; } - else if (fxChanged) { - notify(NOTIFIER_CALL_MODE_FX_CHANGED); - if (callMode != NOTIFIER_CALL_MODE_PRESET_CYCLE) interfaceUpdateCallMode = NOTIFIER_CALL_MODE_FX_CHANGED; - if (realtimeTimeout == UINT32_MAX) realtimeTimeout = 0; - if (isPreset) {isPreset = false;} - else {currentPreset = -1;} - } - return; //no change } - if (realtimeTimeout == UINT32_MAX) realtimeTimeout = 0; - if (isPreset) {isPreset = false;} - else {currentPreset = -1;} + + if (!colChanged) return; //following code is for e.g. initiating transitions + if (callMode != NOTIFIER_CALL_MODE_NO_NOTIFY && nightlightActive && nightlightFade) { briNlT = bri; @@ -137,8 +139,6 @@ void colorUpdated(int callMode) briIT = bri; if (bri > 0) briLast = bri; - notify(callMode); - if (fadeTransition) { //set correct delay if not using notification delay @@ -164,10 +164,6 @@ void colorUpdated(int callMode) setLedsStandard(); strip.trigger(); } - - if (callMode == NOTIFIER_CALL_MODE_PRESET_CYCLE) return; - //set flag to update blynk and mqtt - interfaceUpdateCallMode = callMode; } From 2e77dcc660c92c0be3de7da8a6f1974a0f31ab56 Mon Sep 17 00:00:00 2001 From: Def3nder Date: Mon, 24 Feb 2020 17:27:59 +0100 Subject: [PATCH 124/232] Correct sun rise (#713) --- wled00/wled00.ino | 6 +++--- wled00/wled08_led.ino | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 9b5a92ee..870b8a63 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -150,8 +150,8 @@ bool useRGBW = false; //SK6812 strips can contain an ext bool turnOnAtBoot = true; //turn on LEDs at power-up byte bootPreset = 0; //save preset to load after power-up -byte col[] {255, 160, 0, 0}; //default RGB(W) color -byte colSec[] {0, 0, 0, 0}; //default RGB(W) secondary color +byte col[] {255, 160, 0, 0}; //current RGB(W) primary color. col[] should be updated if you want to change the color. +byte colSec[] {0, 0, 0, 0}; //current RGB(W) secondary color byte briS = 128; //default brightness byte nightlightTargetBri = 0; //brightness after nightlight is over @@ -282,7 +282,7 @@ bool wasConnected = false; //color byte colOld[] {0, 0, 0, 0}; //color before transition -byte colT[] {0, 0, 0, 0}; //current color +byte colT[] {0, 0, 0, 0}; //color that is currently displayed on the LEDs byte colIT[] {0, 0, 0, 0}; //color that was last sent to LEDs byte colSecT[] {0, 0, 0, 0}; byte colSecOld[] {0, 0, 0, 0}; diff --git a/wled00/wled08_led.ino b/wled00/wled08_led.ino index e3afcda5..3764a57c 100644 --- a/wled00/wled08_led.ino +++ b/wled00/wled08_led.ino @@ -227,15 +227,15 @@ void handleNightlight() nightlightDelayMs = (int)(nightlightDelayMins*60000); nightlightActiveOld = true; briNlT = bri; - for (byte i=0; i<4; i++) colNlT[i] = colT[i]; // remember starting color + for (byte i=0; i<4; i++) colNlT[i] = col[i]; // remember starting color } float nper = (millis() - nightlightStartTime)/((float)nightlightDelayMs); if (nightlightFade) { bri = briNlT + ((nightlightTargetBri - briNlT)*nper); - if (nightlightColorFade) // color fading only is enabled with "NF=2" + if (nightlightColorFade) // color fading only is enabled with "NF=2" { - for (byte i=0; i<4; i++) colT[i] = colNlT[i]+ ((colSecT[i] - colNlT[i])*nper); // fading from actual color to secondary color + for (byte i=0; i<4; i++) col[i] = colNlT[i]+ ((colSec[i] - colNlT[i])*nper); // fading from actual color to secondary color } colorUpdated(NOTIFIER_CALL_MODE_NO_NOTIFY); } From 3dcc3492e8e92544330966dcdb216b912e6c6db9 Mon Sep 17 00:00:00 2001 From: zewelor Date: Mon, 24 Feb 2020 18:48:21 +0100 Subject: [PATCH 125/232] Allow to override some costs --- wled00/NpbWrapper.h | 19 ++++++++++++++++--- wled00/const.h | 5 +++++ wled00/wled00.ino | 18 +++++++++++++----- wled00/wled05_init.ino | 4 ++-- 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/wled00/NpbWrapper.h b/wled00/NpbWrapper.h index a9fbd7c2..9709d8aa 100644 --- a/wled00/NpbWrapper.h +++ b/wled00/NpbWrapper.h @@ -13,12 +13,25 @@ //#define WLED_USE_H801 //H801 controller. Please uncomment #define WLED_USE_ANALOG_LEDS as well //#define WLED_USE_5CH_LEDS //5 Channel H801 for cold and warm white +#ifndef BTNPIN #define BTNPIN 0 //button pin. Needs to have pullup (gpio0 recommended) -#define IR_PIN 4 //infrared pin (-1 to disable) MagicHome: 4, H801 Wifi: 0 -#define RLYPIN 12 //pin for relay, will be set HIGH if LEDs are on (-1 to disable). Also usable for standby leds, triggers,... -#define AUXPIN -1 //debug auxiliary output pin (-1 to disable) +#endif +#ifndef IR_PIN +#define IR_PIN 4 //infrared pin (-1 to disable) MagicHome: 4, H801 Wifi: 0 +#endif + +#ifndef RLYPIN +#define RLYPIN 12 //pin for relay, will be set HIGH if LEDs are on (-1 to disable). Also usable for standby leds, triggers,... +#endif + +#ifndef AUXPIN +#define AUXPIN -1 //debug auxiliary output pin (-1 to disable) +#endif + +#ifndef RLYMDE #define RLYMDE 1 //mode for relay, 0: LOW if LEDs are on 1: HIGH if LEDs are on +#endif //END CONFIGURATION diff --git a/wled00/const.h b/wled00/const.h index 4d287572..bf29ce76 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -1,6 +1,11 @@ #ifndef wled_const_h #define wled_const_h +//Defaults +#define DEFAULT_CLIENT_SSID "Your_Network" +#define DEFAULT_AP_PASS "wled1234" +#define DEFAULT_OTA_PASS "wledota" + //Access point behavior #define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot #define AP_BEHAVIOR_NO_CONN 1 //Open when no connection (either after boot or if connection is lost) diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 60643975..475dfb06 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -85,6 +85,14 @@ DMXESPSerial dmx; #include "ir_codes.h" #include "const.h" +#ifndef CLIENT_SSID +#define CLIENT_SSID DEFAULT_CLIENT_SSID +#endif + +#ifndef CLIENT_PASS +#define CLIENT_PASS "" +#endif + #if IR_PIN < 0 #ifndef WLED_DISABLE_INFRARED @@ -117,8 +125,8 @@ char versionString[] = "0.9.1"; //AP and OTA default passwords (for maximum change them!) -char apPass[65] = "wled1234"; -char otaPass[33] = "wledota"; +char apPass[65] = DEFAULT_AP_PASS; +char otaPass[33] = DEFAULT_OTA_PASS; //Hardware CONFIG (only changeble HERE, not at runtime) @@ -130,8 +138,8 @@ char ntpServerName[33] = "0.wled.pool.ntp.org";//NTP server to use //WiFi CONFIG (all these can be changed via web UI, no need to set them here) -char clientSSID[33] = "Your_Network"; -char clientPass[65] = ""; +char clientSSID[33] = CLIENT_SSID; +char clientPass[65] = CLIENT_PASS; char cmDNS[33] = "x"; //mDNS address (placeholder, will be replaced by wledXXXXXXXXXXXX.local) char apSSID[33] = ""; //AP off by default (unless setup) byte apChannel = 1; //2.4GHz WiFi AP channel (1-13) @@ -468,7 +476,7 @@ bool e131NewData = false; WS2812FX strip = WS2812FX(); #define WLED_CONNECTED (WiFi.status() == WL_CONNECTED) -#define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID,"Your_Network") != 0) +#define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID,DEFAULT_CLIENT_SSID) != 0) //debug macros #ifdef WLED_DEBUG diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index bf1be5d9..40e3bef6 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -43,7 +43,7 @@ void wledInit() loadSettingsFromEEPROM(true); beginStrip(); userSetup(); - if (strcmp(clientSSID,"Your_Network") == 0) showWelcomePage = true; + if (strcmp(clientSSID,DEFAULT_CLIENT_SSID) == 0) showWelcomePage = true; WiFi.persistent(false); if (macroBoot>0) applyMacro(macroBoot); @@ -126,7 +126,7 @@ void initAP(bool resetAP=false){ if (apBehavior == AP_BEHAVIOR_BUTTON_ONLY && !resetAP) return; if (!apSSID[0] || resetAP) strcpy(apSSID, "WLED-AP"); - if (resetAP) strcpy(apPass,"wled1234"); + if (resetAP) strcpy(apPass,DEFAULT_AP_PASS); DEBUG_PRINT("Opening access point "); DEBUG_PRINTLN(apSSID); WiFi.softAPConfig(IPAddress(4, 3, 2, 1), IPAddress(4, 3, 2, 1), IPAddress(255,255,255,0)); From 4a834ecfc8353b4daec0dc80bf58af7b05974bd9 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Mon, 24 Feb 2020 19:08:29 +0100 Subject: [PATCH 126/232] Fix UTC offset not working --- wled00/wled10_ntp.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wled00/wled10_ntp.ino b/wled00/wled10_ntp.ino index bf8df064..2c5a64c4 100644 --- a/wled00/wled10_ntp.ino +++ b/wled00/wled10_ntp.ino @@ -164,9 +164,9 @@ void setCountdown() bool checkCountdown() { unsigned long n = now(); - local = countdownTime - n; + if (countdownMode) local = countdownTime - n + utcOffsetSecs; if (n > countdownTime) { - local = n - countdownTime; + if (countdownMode) local = n - countdownTime + utcOffsetSecs; if (!countdownOverTriggered) { if (macroCountdown != 0) applyMacro(macroCountdown); From 2d75526395237e8fb78e9063a6588b31c86a3cce Mon Sep 17 00:00:00 2001 From: cschwinne Date: Mon, 24 Feb 2020 19:36:25 +0100 Subject: [PATCH 127/232] Fix brightness transition updating too often --- wled00/FX.h | 3 +++ wled00/FX_fcn.cpp | 5 +---- wled00/wled00.ino | 3 +-- wled00/wled01_eeprom.ino | 4 ++-- wled00/wled08_led.ino | 7 ------- 5 files changed, 7 insertions(+), 15 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index eec42990..a518317f 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -56,6 +56,9 @@ #define MAX_SEGMENT_DATA 8192 #endif +#define LED_SKIP_AMOUNT 1 +#define MIN_SHOW_DELAY 15 + #define NUM_COLORS 3 /* number of colors per segment */ #define SEGMENT _segments[_segment_index] #define SEGCOLOR(x) gamma32(_segments[_segment_index].colors[x]) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 0707392d..b495de64 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -27,9 +27,6 @@ #include "FX.h" #include "palettes.h" -#define LED_SKIP_AMOUNT 1 -#define MIN_SHOW_DELAY 15 - void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst) { if (supportWhite == _useRgbw && countPixels == _length) return; @@ -380,7 +377,7 @@ void WS2812FX::setBrightness(uint8_t b) { if (_brightness == b) return; _brightness = (gammaCorrectBri) ? gamma8(b) : b; _segment_index = 0; - if (SEGENV.next_time > millis() + 22) show();//apply brightness change immediately if no refresh soon + if (SEGENV.next_time > millis() + 22 && millis() - _lastShow > MIN_SHOW_DELAY) show();//apply brightness change immediately if no refresh soon } uint8_t WS2812FX::getMode(void) { diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 445406b9..66a17eea 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -119,7 +119,7 @@ DMXESPSerial dmx; #endif //version code in format yymmddb (b = daily build) -#define VERSION 2002242 +#define VERSION 2002243 char versionString[] = "0.9.1"; @@ -167,7 +167,6 @@ byte nightlightDelayMins = 60; bool nightlightFade = true; //if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over bool nightlightColorFade = false; //if enabled, light will gradually fade color from primary to secondary color. bool fadeTransition = true; //enable crossfading color transition -bool enableSecTransition = true; //also enable transition for secondary color uint16_t transitionDelay = 750; //default crossfade duration in ms bool skipFirstLed = false; //ignore first LED in strip (useful if you need the LED as signal repeater) diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino index 0ff9497f..703785c1 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled01_eeprom.ino @@ -159,7 +159,7 @@ void saveSettingsToEEPROM() EEPROM.write(396, (utcOffsetSecs<0)); //is negative EEPROM.write(397, syncToggleReceive); EEPROM.write(398, (ledCount >> 8) & 0xFF); - EEPROM.write(399, !enableSecTransition); + //EEPROM.write(399, was !enableSecTransition); //favorite setting (preset) memory (25 slots/ each 20byte) //400 - 940 reserved @@ -527,7 +527,7 @@ void loadSettingsFromEEPROM(bool first) wifiLock = EEPROM.read(393); utcOffsetSecs = EEPROM.read(394) + ((EEPROM.read(395) << 8) & 0xFF00); if (EEPROM.read(396)) utcOffsetSecs = -utcOffsetSecs; //negative - enableSecTransition = !EEPROM.read(399); + //!EEPROM.read(399); was enableSecTransition //favorite setting (preset) memory (25 slots/ each 20byte) //400 - 899 reserved diff --git a/wled00/wled08_led.ino b/wled00/wled08_led.ino index 3764a57c..93642fc2 100644 --- a/wled00/wled08_led.ino +++ b/wled00/wled08_led.ino @@ -40,13 +40,6 @@ void setAllLeds() { if (val > 255) val = 255; strip.setBrightness(val); } - if (!enableSecTransition) - { - for (byte i = 0; i<4; i++) - { - colSecT[i] = colSec[i]; - } - } if (useRGBW && strip.rgbwMode == RGBW_MODE_LEGACY) { colorRGBtoRGBW(colT); From 730ba12c8dc1421cec7983c27728210e2f196fc9 Mon Sep 17 00:00:00 2001 From: Def3nder Date: Mon, 24 Feb 2020 22:09:33 +0100 Subject: [PATCH 128/232] Indents for the #ifdef's in wled00.ino (#716) --- wled00/wled00.ino | 117 +++++++++++++++++++++++----------------------- 1 file changed, 59 insertions(+), 58 deletions(-) diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 66a17eea..c147dca3 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -36,22 +36,22 @@ //library inclusions #include #ifdef WLED_ENABLE_DMX -#include -DMXESPSerial dmx; + #include + DMXESPSerial dmx; #endif #ifdef ESP8266 - #include - #include - #include - extern "C" { - #include - } -#else -#include -#include "esp_wifi.h" -#include -#include -#include "SPIFFS.h" + #include + #include + #include + extern "C" { + #include + } +#else //ESP32 + #include + #include "esp_wifi.h" + #include + #include + #include "SPIFFS.h" #endif #include @@ -59,20 +59,20 @@ DMXESPSerial dmx; #include #include #ifndef WLED_DISABLE_OTA -#include + #include #endif #include #include "src/dependencies/time/TimeLib.h" #include "src/dependencies/timezone/Timezone.h" #ifndef WLED_DISABLE_ALEXA -#define ESPALEXA_ASYNC -#define ESPALEXA_NO_SUBPAGE -#define ESPALEXA_MAXDEVICES 1 -//#define ESPALEXA_DEBUG -#include "src/dependencies/espalexa/Espalexa.h" + #define ESPALEXA_ASYNC + #define ESPALEXA_NO_SUBPAGE + #define ESPALEXA_MAXDEVICES 1 + // #define ESPALEXA_DEBUG + #include "src/dependencies/espalexa/Espalexa.h" #endif #ifndef WLED_DISABLE_BLYNK -#include "src/dependencies/blynk/BlynkSimpleEsp.h" + #include "src/dependencies/blynk/BlynkSimpleEsp.h" #endif #include "src/dependencies/e131/ESPAsyncE131.h" #include "src/dependencies/async-mqtt-client/AsyncMqttClient.h" @@ -95,15 +95,15 @@ DMXESPSerial dmx; #if IR_PIN < 0 -#ifndef WLED_DISABLE_INFRARED -#define WLED_DISABLE_INFRARED -#endif + #ifndef WLED_DISABLE_INFRARED + #define WLED_DISABLE_INFRARED + #endif #endif #ifndef WLED_DISABLE_INFRARED -#include -#include -#include + #include + #include + #include #endif // remove flicker because PWM signal of RGB channels can become out of phase @@ -270,12 +270,12 @@ bool aOtaEnabled = true; //ArduinoOTA allows easy updates d uint16_t userVar0 = 0, userVar1 = 0; #ifdef WLED_ENABLE_DMX -//dmx CONFIG -byte DMXChannels = 7; // number of channels per fixture -byte DMXFixtureMap[15] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + //dmx CONFIG + byte DMXChannels = 7; // number of channels per fixture + byte DMXFixtureMap[15] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // assigns the different channels to different functions. See wled21_dmx.ino for more information. -uint16_t DMXGap = 10; // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. -uint16_t DMXStart = 10; // start address of the first fixture + uint16_t DMXGap = 10; // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. + uint16_t DMXStart = 10; // start address of the first fixture #endif @@ -398,7 +398,6 @@ byte realtimeMode = REALTIME_MODE_INACTIVE; IPAddress realtimeIP = (0,0,0,0); unsigned long realtimeTimeout = 0; - //mqtt long lastMqttReconnectAttempt = 0; long lastInterfaceUpdate = 0; @@ -406,17 +405,17 @@ byte interfaceUpdateCallMode = NOTIFIER_CALL_MODE_INIT; char mqttStatusTopic[40] = ""; //this must be global because of async handlers #if AUXPIN >= 0 -//auxiliary debug pin -byte auxTime = 0; -unsigned long auxStartTime = 0; -bool auxActive = false, auxActiveBefore = false; + //auxiliary debug pin + byte auxTime = 0; + unsigned long auxStartTime = 0; + bool auxActive = false, auxActiveBefore = false; #endif //alexa udp String escapedMac; #ifndef WLED_DISABLE_ALEXA -Espalexa espalexa; -EspalexaDevice* espalexaDevice; + Espalexa espalexa; + EspalexaDevice* espalexaDevice; #endif //dns server @@ -431,6 +430,7 @@ IPAddress ntpServerIP; uint16_t ntpLocalPort = 2390; #define NTP_PACKET_SIZE 48 +//maximum number of LEDs - MAX_LEDS is comming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue #define MAX_LEDS 1500 #define MAX_LEDS_DMA 500 @@ -439,6 +439,7 @@ uint16_t ntpLocalPort = 2390; char* obuf; uint16_t olen = 0; +//presets uint16_t savedPresets = 0; int8_t currentPreset = -1; bool isPreset = false; @@ -479,26 +480,26 @@ WS2812FX strip = WS2812FX(); //debug macros #ifdef WLED_DEBUG -#define DEBUG_PRINT(x) Serial.print (x) -#define DEBUG_PRINTLN(x) Serial.println (x) -#define DEBUG_PRINTF(x) Serial.printf (x) -unsigned long debugTime = 0; -int lastWifiState = 3; -unsigned long wifiStateChangedTime = 0; -int loops = 0; + #define DEBUG_PRINT(x) Serial.print (x) + #define DEBUG_PRINTLN(x) Serial.println (x) + #define DEBUG_PRINTF(x) Serial.printf (x) + unsigned long debugTime = 0; + int lastWifiState = 3; + unsigned long wifiStateChangedTime = 0; + int loops = 0; #else -#define DEBUG_PRINT(x) -#define DEBUG_PRINTLN(x) -#define DEBUG_PRINTF(x) + #define DEBUG_PRINT(x) + #define DEBUG_PRINTLN(x) + #define DEBUG_PRINTF(x) #endif //filesystem #ifndef WLED_DISABLE_FILESYSTEM -#include -#ifdef ARDUINO_ARCH_ESP32 -#include "SPIFFS.h" -#endif -#include "SPIFFSEditor.h" + #include + #ifdef ARDUINO_ARCH_ESP32 + #include "SPIFFS.h" + #endif + #include "SPIFFSEditor.h" #endif @@ -564,9 +565,9 @@ void loop() { handleOverlays(); yield(); - #ifdef WLED_USE_ANALOG_LEDS +#ifdef WLED_USE_ANALOG_LEDS strip.setRgbwPwm(); - #endif +#endif if (doReboot) reset(); @@ -586,9 +587,9 @@ void loop() { if (!offMode) strip.service(); } yield(); - #ifdef ESP8266 +#ifdef ESP8266 MDNS.update(); - #endif +#endif if (millis() - lastMqttReconnectAttempt > 30000) initMqtt(); //DEBUG serial logging From d5def30c5964e6ca0f0da75447cb48d1dfc4d818 Mon Sep 17 00:00:00 2001 From: Ser Ko Date: Mon, 24 Feb 2020 16:36:03 -0500 Subject: [PATCH 129/232] Update wled06_usermod.ino --- .../Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino index 3b7a05fe..9e6e0b79 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino @@ -2,8 +2,7 @@ #include //Dallastemperature sensor #ifdef ARDUINO_ARCH_ESP32 //ESP32 boards uint8_t SCL_PIN = 22; -uint8_t SDA_PIN = 21; -// uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8 +uint8_t SDA_PIN = 21; OneWire oneWire(23); #else //ESP8266 boards uint8_t SCL_PIN = 5; @@ -14,7 +13,7 @@ OneWire oneWire(13); //ESP8266 Wemos D1 mini board use SCL=5 SDA=4 while ESP32 Wemos32 mini board use SCL=22 SDA=21 #define U8X8_PIN_SCL SCL_PIN #define U8X8_PIN_SDA SDA_PIN -//#define U8X8_PIN_RESET RST_PIN // Uncoment for Heltec WiFi-Kit-8 + // Dallas sensor DallasTemperature sensor(&oneWire); long temptimer = millis(); @@ -30,8 +29,6 @@ long lastMeasure = 0; U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA // --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3" //U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA -// --> Third choise Heltec WiFi-Kit-8 with build-in OLED 128X32 0.91" -//U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_RESET, U8X8_PIN_SCL, U8X8_PIN_SDA); // Constracor for Heltec WiFi-Kit-8 // gets called once at boot. Do all initialization that doesn't depend on // network here void userSetup() { From 70bf957a8a3b0199960771980da957d910fd7279 Mon Sep 17 00:00:00 2001 From: srg74 <28492985+srg74@users.noreply.github.com> Date: Mon, 24 Feb 2020 16:44:56 -0500 Subject: [PATCH 130/232] Added Heltec WiFi-Kit-8 --- usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino index c0f6ce85..58a4e0e6 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino @@ -14,7 +14,7 @@ OneWire oneWire(13); //ESP8266 Wemos D1 mini board use SCL=5 SDA=4 while ESP32 Wemos32 mini board use SCL=22 SDA=21 #define U8X8_PIN_SCL SCL_PIN #define U8X8_PIN_SDA SDA_PIN - +//#define U8X8_PIN_RESET RST_PIN // Uncoment for Heltec WiFi-Kit-8 // Dallas sensor DallasTemperature sensor(&oneWire); long temptimer = millis(); @@ -30,6 +30,8 @@ long lastMeasure = 0; U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA // --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3" //U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA +// --> Third choise of Heltec WiFi-Kit-8 OLED 128X32 0.91" +//U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_RESET, U8X8_PIN_SCL, U8X8_PIN_SDA); // Constracor for Heltec WiFi-Kit-8 // gets called once at boot. Do all initialization that doesn't depend on // network here void userSetup() { From a78f17abec323055836fb1a517603e7ae5ade751 Mon Sep 17 00:00:00 2001 From: srg74 <28492985+srg74@users.noreply.github.com> Date: Mon, 24 Feb 2020 16:48:01 -0500 Subject: [PATCH 131/232] Update readme.md --- usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md index be97d4b8..765e5683 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md @@ -1,4 +1,5 @@ # Wemos D1 mini and Wemos32 mini shield +- Added third choice of controller Heltec WiFi-Kit-8. Totally DIY but with OLED display. ## Project repository - [Original repository](https://github.com/srg74/WLED-wemos-shield) - WLED Wemos shield repository - [Wemos shield project Wiki](https://github.com/srg74/WLED-wemos-shield/wiki) From 1221661ae32f7c0c08fe5e4891a7b6bd3efb8059 Mon Sep 17 00:00:00 2001 From: srg74 <28492985+srg74@users.noreply.github.com> Date: Mon, 24 Feb 2020 16:51:11 -0500 Subject: [PATCH 132/232] Added Heltec WiFi-Kit-8 to UserMod (#717) --- usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md | 1 + .../Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md index be97d4b8..765e5683 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md @@ -1,4 +1,5 @@ # Wemos D1 mini and Wemos32 mini shield +- Added third choice of controller Heltec WiFi-Kit-8. Totally DIY but with OLED display. ## Project repository - [Original repository](https://github.com/srg74/WLED-wemos-shield) - WLED Wemos shield repository - [Wemos shield project Wiki](https://github.com/srg74/WLED-wemos-shield/wiki) diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino index 9e6e0b79..58a4e0e6 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino @@ -7,13 +7,14 @@ OneWire oneWire(23); #else //ESP8266 boards uint8_t SCL_PIN = 5; uint8_t SDA_PIN = 4; +// uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8 OneWire oneWire(13); #endif //The SCL and SDA pins are defined here. //ESP8266 Wemos D1 mini board use SCL=5 SDA=4 while ESP32 Wemos32 mini board use SCL=22 SDA=21 #define U8X8_PIN_SCL SCL_PIN #define U8X8_PIN_SDA SDA_PIN - +//#define U8X8_PIN_RESET RST_PIN // Uncoment for Heltec WiFi-Kit-8 // Dallas sensor DallasTemperature sensor(&oneWire); long temptimer = millis(); @@ -29,6 +30,8 @@ long lastMeasure = 0; U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA // --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3" //U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA +// --> Third choise of Heltec WiFi-Kit-8 OLED 128X32 0.91" +//U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_RESET, U8X8_PIN_SCL, U8X8_PIN_SDA); // Constracor for Heltec WiFi-Kit-8 // gets called once at boot. Do all initialization that doesn't depend on // network here void userSetup() { From f304a6891fdfdab774b40ce1a47d62fd419f84a9 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Tue, 25 Feb 2020 02:19:12 +0100 Subject: [PATCH 133/232] Free more than 2kB of RAM Store Palettes in PROGMEM F() select long and rarely used strings Refactor hue error --- wled00/FX.h | 1 + wled00/FX_fcn.cpp | 25 +++++++---- wled00/NpbWrapper.h | 2 +- wled00/const.h | 9 ++++ wled00/html_settings.h | 2 +- wled00/palettes.h | 91 ++++++++++++++++++---------------------- wled00/wled00.ino | 4 +- wled00/wled02_xml.ino | 31 +++++++++----- wled00/wled15_hue.ino | 27 ++++++------ wled00/wled18_server.ino | 34 +++++++-------- wled00/wled19_json.ino | 2 +- 11 files changed, 123 insertions(+), 105 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index a518317f..abb8c6f0 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -591,6 +591,7 @@ class WS2812FX { uint8_t _brightness; static uint16_t _usedSegmentData; + void load_gradient_palette(uint8_t); void handle_palette(void); void fill(uint32_t); diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index b495de64..ee0ca73a 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -324,7 +324,7 @@ uint8_t WS2812FX::getModeCount() uint8_t WS2812FX::getPaletteCount() { - return 13 + gGradientPaletteCount; + return 13 + GRADIENT_PALETTE_COUNT; } //TODO transitions @@ -707,6 +707,15 @@ CRGB WS2812FX::col_to_crgb(uint32_t color) } +void WS2812FX::load_gradient_palette(uint8_t index) +{ + byte i = constrain(index, 0, GRADIENT_PALETTE_COUNT -1); + byte tcp[72]; //support gradient palettes with up to 18 entries + memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[i])), 72); + targetPalette.loadDynamicGradientPalette(tcp); +} + + /* * FastLED palette modes helper function. Limitation: Due to memory reasons, multiple active segments with FastLED will disable the Palette transitions */ @@ -724,13 +733,13 @@ void WS2812FX::handle_palette(void) case 0: {//default palette. Differs depending on effect switch (SEGMENT.mode) { - case FX_MODE_FIRE_2012 : targetPalette = gGradientPalettes[22]; break;//heat palette - case FX_MODE_COLORWAVES : targetPalette = gGradientPalettes[13]; break;//landscape 33 + case FX_MODE_FIRE_2012 : load_gradient_palette(22); break;//heat palette + case FX_MODE_COLORWAVES : load_gradient_palette(13); break;//landscape 33 case FX_MODE_FILLNOISE8 : targetPalette = OceanColors_p; break; - case FX_MODE_NOISE16_1 : targetPalette = gGradientPalettes[17]; break;//Drywet - case FX_MODE_NOISE16_2 : targetPalette = gGradientPalettes[30]; break;//Blue cyan yellow - case FX_MODE_NOISE16_3 : targetPalette = gGradientPalettes[22]; break;//heat palette - case FX_MODE_NOISE16_4 : targetPalette = gGradientPalettes[13]; break;//landscape 33 + case FX_MODE_NOISE16_1 : load_gradient_palette(17); break;//Drywet + case FX_MODE_NOISE16_2 : load_gradient_palette(30); break;//Blue cyan yellow + case FX_MODE_NOISE16_3 : load_gradient_palette(22); break;//heat palette + case FX_MODE_NOISE16_4 : load_gradient_palette(13); break;//landscape 33 //case FX_MODE_GLITTER : targetPalette = RainbowColors_p; break; default: targetPalette = PartyColors_p; break;//palette, bpm @@ -787,7 +796,7 @@ void WS2812FX::handle_palette(void) case 12: //Rainbow stripe colors targetPalette = RainbowStripeColors_p; break; default: //progmem palettes - targetPalette = gGradientPalettes[constrain(SEGMENT.palette -13, 0, gGradientPaletteCount -1)]; + load_gradient_palette(SEGMENT.palette -13); } if (singleSegmentMode && paletteFade) //only blend if just one segment uses FastLED mode diff --git a/wled00/NpbWrapper.h b/wled00/NpbWrapper.h index 9709d8aa..f12d057f 100644 --- a/wled00/NpbWrapper.h +++ b/wled00/NpbWrapper.h @@ -4,7 +4,7 @@ //PIN CONFIGURATION #ifndef LEDPIN -#define LEDPIN 2 //strip pin. Any for ESP32, gpio2 or 3 is recommended for ESP8266 (gpio2/3 are labeled D4/RX on NodeMCU and Wemos) +#define LEDPIN 5 //strip pin. Any for ESP32, gpio2 or 3 is recommended for ESP8266 (gpio2/3 are labeled D4/RX on NodeMCU and Wemos) #endif //#define USE_APA102 // Uncomment for using APA102 LEDs. //#define USE_WS2801 // Uncomment for using WS2801 LEDs (make sure you have NeoPixelBus v2.5.6 or newer) diff --git a/wled00/const.h b/wled00/const.h index 242536c9..b54215ca 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -61,4 +61,13 @@ #define TYPE_ANALOG_4CH 9 //analog RGBW #define TYPE_ANALOG_5CH 10 //analog RGB + WW + CW +//Hue error codes +#define HUE_ERROR_INACTIVE 0 +#define HUE_ERROR_UNAUTHORIZED 1 +#define HUE_ERROR_LIGHTID 3 +#define HUE_ERROR_PUSHLINK 101 +#define HUE_ERROR_JSON_PARSING 250 +#define HUE_ERROR_TIMEOUT 251 +#define HUE_ERROR_ACTIVE 255 + #endif diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 157d6b4c..7340e62d 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -335,7 +335,7 @@ Hue Bridge IP:

Press the pushlink button on the bridge, after that save this page!
(when first connecting)
-Hue status: Internal ESP Error!
+Hue status: Disabled in this build
diff --git a/wled00/palettes.h b/wled00/palettes.h index fbdd7bb5..29384395 100644 --- a/wled00/palettes.h +++ b/wled00/palettes.h @@ -13,7 +13,9 @@ #ifndef PalettesWLED_h #define PalettesWLED_h -DEFINE_GRADIENT_PALETTE( ib_jul01_gp ) { +#define GRADIENT_PALETTE_COUNT 38 + +const byte ib_jul01_gp[] PROGMEM = { 0, 194, 1, 1, 94, 1, 29, 18, 132, 57,131, 28, @@ -24,7 +26,7 @@ DEFINE_GRADIENT_PALETTE( ib_jul01_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 20 bytes of program space. -DEFINE_GRADIENT_PALETTE( es_vintage_57_gp ) { +const byte es_vintage_57_gp[] PROGMEM = { 0, 2, 1, 1, 53, 18, 1, 0, 104, 69, 29, 1, @@ -37,7 +39,7 @@ DEFINE_GRADIENT_PALETTE( es_vintage_57_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 32 bytes of program space. -DEFINE_GRADIENT_PALETTE( es_vintage_01_gp ) { +const byte es_vintage_01_gp[] PROGMEM = { 0, 4, 1, 1, 51, 16, 0, 1, 76, 97,104, 3, @@ -53,7 +55,7 @@ DEFINE_GRADIENT_PALETTE( es_vintage_01_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 20 bytes of program space. -DEFINE_GRADIENT_PALETTE( es_rivendell_15_gp ) { +const byte es_rivendell_15_gp[] PROGMEM = { 0, 1, 14, 5, 101, 16, 36, 14, 165, 56, 68, 30, @@ -67,7 +69,7 @@ DEFINE_GRADIENT_PALETTE( es_rivendell_15_gp ) { // Size: 36 bytes of program space. // Edited to be brighter -DEFINE_GRADIENT_PALETTE( rgi_15_gp ) { +const byte rgi_15_gp[] PROGMEM = { 0, 4, 1, 70, 31, 55, 1, 30, 63, 255, 4, 7, @@ -84,7 +86,7 @@ DEFINE_GRADIENT_PALETTE( rgi_15_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 8 bytes of program space. -DEFINE_GRADIENT_PALETTE( retro2_16_gp ) { +const byte retro2_16_gp[] PROGMEM = { 0, 188,135, 1, 255, 46, 7, 1}; @@ -94,7 +96,7 @@ DEFINE_GRADIENT_PALETTE( retro2_16_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 20 bytes of program space. -DEFINE_GRADIENT_PALETTE( Analogous_1_gp ) { +const byte Analogous_1_gp[] PROGMEM = { 0, 3, 0,255, 63, 23, 0,255, 127, 67, 0,255, @@ -107,7 +109,7 @@ DEFINE_GRADIENT_PALETTE( Analogous_1_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 20 bytes of program space. -DEFINE_GRADIENT_PALETTE( es_pinksplash_08_gp ) { +const byte es_pinksplash_08_gp[] PROGMEM = { 0, 126, 11,255, 127, 197, 1, 22, 175, 210,157,172, @@ -120,7 +122,7 @@ DEFINE_GRADIENT_PALETTE( es_pinksplash_08_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 16 bytes of program space. -DEFINE_GRADIENT_PALETTE( es_ocean_breeze_036_gp ) { +const byte es_ocean_breeze_036_gp[] PROGMEM = { 0, 1, 6, 7, 89, 1, 99,111, 153, 144,209,255, @@ -132,7 +134,7 @@ DEFINE_GRADIENT_PALETTE( es_ocean_breeze_036_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 88 bytes of program space. -DEFINE_GRADIENT_PALETTE( departure_gp ) { +const byte departure_gp[] PROGMEM = { 0, 8, 3, 0, 42, 23, 7, 0, 63, 75, 38, 6, @@ -152,7 +154,7 @@ DEFINE_GRADIENT_PALETTE( departure_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 36 bytes of program space. -DEFINE_GRADIENT_PALETTE( es_landscape_64_gp ) { +const byte es_landscape_64_gp[] PROGMEM = { 0, 0, 0, 0, 37, 2, 25, 1, 76, 15,115, 5, @@ -169,7 +171,7 @@ DEFINE_GRADIENT_PALETTE( es_landscape_64_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 24 bytes of program space. -DEFINE_GRADIENT_PALETTE( es_landscape_33_gp ) { +const byte es_landscape_33_gp[] PROGMEM = { 0, 1, 5, 0, 19, 32, 23, 1, 38, 161, 55, 1, @@ -183,7 +185,7 @@ DEFINE_GRADIENT_PALETTE( es_landscape_33_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 28 bytes of program space. -DEFINE_GRADIENT_PALETTE( rainbowsherbet_gp ) { +const byte rainbowsherbet_gp[] PROGMEM = { 0, 255, 33, 4, 43, 255, 68, 25, 86, 255, 7, 25, @@ -198,7 +200,7 @@ DEFINE_GRADIENT_PALETTE( rainbowsherbet_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 24 bytes of program space. -DEFINE_GRADIENT_PALETTE( gr65_hult_gp ) { +const byte gr65_hult_gp[] PROGMEM = { 0, 247,176,247, 48, 255,136,255, 89, 220, 29,226, @@ -212,7 +214,7 @@ DEFINE_GRADIENT_PALETTE( gr65_hult_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 32 bytes of program space. -DEFINE_GRADIENT_PALETTE( gr64_hult_gp ) { +const byte gr64_hult_gp[] PROGMEM = { 0, 1,124,109, 66, 1, 93, 79, 104, 52, 65, 1, @@ -228,7 +230,7 @@ DEFINE_GRADIENT_PALETTE( gr64_hult_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 28 bytes of program space. -DEFINE_GRADIENT_PALETTE( GMT_drywet_gp ) { +const byte GMT_drywet_gp[] PROGMEM = { 0, 47, 30, 2, 42, 213,147, 24, 84, 103,219, 52, @@ -243,7 +245,7 @@ DEFINE_GRADIENT_PALETTE( GMT_drywet_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 24 bytes of program space. -DEFINE_GRADIENT_PALETTE( ib15_gp ) { +const byte ib15_gp[] PROGMEM = { 0, 113, 91,147, 72, 157, 88, 78, 89, 208, 85, 33, @@ -257,7 +259,7 @@ DEFINE_GRADIENT_PALETTE( ib15_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 20 bytes of program space. -DEFINE_GRADIENT_PALETTE( Tertiary_01_gp ) { +const byte Tertiary_01_gp[] PROGMEM = { 0, 0, 1,255, 63, 3, 68, 45, 127, 23,255, 0, @@ -270,7 +272,7 @@ DEFINE_GRADIENT_PALETTE( Tertiary_01_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 52 bytes of program space. -DEFINE_GRADIENT_PALETTE( lava_gp ) { +const byte lava_gp[] PROGMEM = { 0, 0, 0, 0, 46, 18, 0, 0, 96, 113, 0, 0, @@ -291,7 +293,7 @@ DEFINE_GRADIENT_PALETTE( lava_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 28 bytes of program space. -DEFINE_GRADIENT_PALETTE( fierce_ice_gp ) { +const byte fierce_ice_gp[] PROGMEM = { 0, 0, 0, 0, 59, 0, 9, 45, 119, 0, 38,255, @@ -306,7 +308,7 @@ DEFINE_GRADIENT_PALETTE( fierce_ice_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 44 bytes of program space. -DEFINE_GRADIENT_PALETTE( Colorfull_gp ) { +const byte Colorfull_gp[] PROGMEM = { 0, 10, 85, 5, 25, 29,109, 18, 60, 59,138, 42, @@ -325,7 +327,7 @@ DEFINE_GRADIENT_PALETTE( Colorfull_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 44 bytes of program space. -DEFINE_GRADIENT_PALETTE( Pink_Purple_gp ) { +const byte Pink_Purple_gp[] PROGMEM = { 0, 19, 2, 39, 25, 26, 4, 45, 51, 33, 6, 52, @@ -344,7 +346,7 @@ DEFINE_GRADIENT_PALETTE( Pink_Purple_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 28 bytes of program space. -DEFINE_GRADIENT_PALETTE( Sunset_Real_gp ) { +const byte Sunset_Real_gp[] PROGMEM = { 0, 120, 0, 0, 22, 179, 22, 0, 51, 255,104, 0, @@ -359,7 +361,7 @@ DEFINE_GRADIENT_PALETTE( Sunset_Real_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 44 bytes of program space. -DEFINE_GRADIENT_PALETTE( Sunset_Yellow_gp ) { +const byte Sunset_Yellow_gp[] PROGMEM = { 0, 10, 62,123, 36, 56,130,103, 87, 153,225, 85, @@ -378,7 +380,7 @@ DEFINE_GRADIENT_PALETTE( Sunset_Yellow_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 60 bytes of program space. -DEFINE_GRADIENT_PALETTE( Beech_gp ) { +const byte Beech_gp[] PROGMEM = { 0, 255,252,214, 12, 255,252,214, 22, 255,252,214, @@ -401,7 +403,7 @@ DEFINE_GRADIENT_PALETTE( Beech_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 32 bytes of program space. -DEFINE_GRADIENT_PALETTE( Another_Sunset_gp ) { +const byte Another_Sunset_gp[] PROGMEM = { 0, 110, 49, 11, 29, 55, 34, 10, 68, 22, 22, 9, @@ -420,7 +422,7 @@ DEFINE_GRADIENT_PALETTE( Another_Sunset_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 52 bytes of program space. -DEFINE_GRADIENT_PALETTE( es_autumn_19_gp ) { +const byte es_autumn_19_gp[] PROGMEM = { 0, 26, 1, 1, 51, 67, 4, 1, 84, 118, 14, 1, @@ -441,7 +443,7 @@ DEFINE_GRADIENT_PALETTE( es_autumn_19_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 28 bytes of program space. -DEFINE_GRADIENT_PALETTE( BlacK_Blue_Magenta_White_gp ) { +const byte BlacK_Blue_Magenta_White_gp[] PROGMEM = { 0, 0, 0, 0, 42, 0, 0, 45, 84, 0, 0,255, @@ -456,7 +458,7 @@ DEFINE_GRADIENT_PALETTE( BlacK_Blue_Magenta_White_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 20 bytes of program space. -DEFINE_GRADIENT_PALETTE( BlacK_Magenta_Red_gp ) { +const byte BlacK_Magenta_Red_gp[] PROGMEM = { 0, 0, 0, 0, 63, 42, 0, 45, 127, 255, 0,255, @@ -469,7 +471,7 @@ DEFINE_GRADIENT_PALETTE( BlacK_Magenta_Red_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 28 bytes of program space. -DEFINE_GRADIENT_PALETTE( BlacK_Red_Magenta_Yellow_gp ) { +const byte BlacK_Red_Magenta_Yellow_gp[] PROGMEM = { 0, 0, 0, 0, 42, 42, 0, 0, 84, 255, 0, 0, @@ -484,7 +486,7 @@ DEFINE_GRADIENT_PALETTE( BlacK_Red_Magenta_Yellow_gp ) { // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 20 bytes of program space. -DEFINE_GRADIENT_PALETTE( Blue_Cyan_Yellow_gp ) { +const byte Blue_Cyan_Yellow_gp[] PROGMEM = { 0, 0, 0,255, 63, 0, 55,255, 127, 0,255,255, @@ -494,7 +496,7 @@ DEFINE_GRADIENT_PALETTE( Blue_Cyan_Yellow_gp ) { //Custom palette by Aircoookie -DEFINE_GRADIENT_PALETTE( Orange_Teal_gp ) { +const byte Orange_Teal_gp[] PROGMEM = { 0, 0,150, 92, 55, 0,150, 92, 200, 255, 72, 0, @@ -502,7 +504,7 @@ DEFINE_GRADIENT_PALETTE( Orange_Teal_gp ) { //Custom palette by Aircoookie -DEFINE_GRADIENT_PALETTE( Tiamat_gp ) { +const byte Tiamat_gp[] PROGMEM = { 0, 1, 2, 14, //gc 33, 2, 5, 35, //gc from 47, 61,126 100, 13,135, 92, //gc from 88,242,247 @@ -517,7 +519,7 @@ DEFINE_GRADIENT_PALETTE( Tiamat_gp ) { //Custom palette by Aircoookie -DEFINE_GRADIENT_PALETTE( April_Night_gp ) { +const byte April_Night_gp[] PROGMEM = { 0, 1, 5, 45, //deep blue 10, 1, 5, 45, 25, 5,169,175, //light blue @@ -536,7 +538,7 @@ DEFINE_GRADIENT_PALETTE( April_Night_gp ) { 244, 1, 5, 45, 255, 1, 5, 45}; -DEFINE_GRADIENT_PALETTE( Orangery_gp ) { +const byte Orangery_gp[] PROGMEM = { 0, 255, 95, 23, 30, 255, 82, 0, 60, 223, 13, 8, @@ -548,7 +550,7 @@ DEFINE_GRADIENT_PALETTE( Orangery_gp ) { 255, 213, 37, 4}; //inspired by Mark Kriegsman https://gist.github.com/kriegsman/756ea6dcae8e30845b5a -DEFINE_GRADIENT_PALETTE( C9_gp ) { +const byte C9_gp[] PROGMEM = { 0, 184, 4, 0, //red 60, 184, 4, 0, 65, 144, 44, 2, //amber @@ -558,14 +560,14 @@ DEFINE_GRADIENT_PALETTE( C9_gp ) { 195, 7, 7, 88, //blue 255, 7, 7, 88}; -DEFINE_GRADIENT_PALETTE( Sakura_gp ) { +const byte Sakura_gp[] PROGMEM = { 0, 196, 19, 10, 65, 255, 69, 45, 130, 223, 45, 72, 195, 255, 82,103, 255, 223, 13, 17}; -DEFINE_GRADIENT_PALETTE( Aurora_gp ) { +const byte Aurora_gp[] PROGMEM = { 0, 1, 5, 45, //deep blue 64, 0,200, 23, 128, 0,255, 0, //green @@ -578,13 +580,7 @@ DEFINE_GRADIENT_PALETTE( Aurora_gp ) { // This will let us programmatically choose one based on // a number, rather than having to activate each explicitly // by name every time. -// Since it is const, this array could also be moved -// into PROGMEM to save SRAM, but for simplicity of illustration -// we'll keep it in a regular SRAM array. -// -// This list of color palettes acts as a "playlist"; you can -// add or delete, or re-arrange as you wish. -const TProgmemRGBGradientPalettePtr gGradientPalettes[] = { +const byte* const gGradientPalettes[] PROGMEM = { Sunset_Real_gp, //13-00 Sunset es_rivendell_15_gp, //14-01 Rivendell es_ocean_breeze_036_gp, //15-02 Breeze @@ -625,9 +621,4 @@ const TProgmemRGBGradientPalettePtr gGradientPalettes[] = { Aurora_gp, //50-37 Aurora }; - -// Count of how many cpt-city gradients are defined: -const uint8_t gGradientPaletteCount = - sizeof( gGradientPalettes) / sizeof( TProgmemRGBGradientPalettePtr ); - #endif diff --git a/wled00/wled00.ino b/wled00/wled00.ino index c147dca3..93789577 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -119,7 +119,7 @@ #endif //version code in format yymmddb (b = daily build) -#define VERSION 2002243 +#define VERSION 2002251 char versionString[] = "0.9.1"; @@ -350,7 +350,7 @@ bool udpConnected = false, udpRgbConnected = false; bool showWelcomePage = false; //hue -char hueError[25] = "Inactive"; +byte hueError = HUE_ERROR_INACTIVE; //uint16_t hueFailCount = 0; float hueXLast = 0, hueYLast = 0; uint16_t hueHueLast = 0, hueCtLast = 0; diff --git a/wled00/wled02_xml.ino b/wled00/wled02_xml.ino index e5083131..d538e0c4 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/wled02_xml.ino @@ -9,7 +9,7 @@ char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr) obuf = (dest == nullptr)? sbuf:dest; olen = 0; - oappend(""); + oappend((const char*)F("")); oappendi((nightlightActive && nightlightFade) ? briT : bri); oappend(""); @@ -65,7 +65,7 @@ char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr) { mesg += "E1.31 mode "; mesg += DMXMode; - mesg += " at DMX Address "; + mesg += F(" at DMX Address "); mesg += DMXAddress; mesg += " from "; mesg += realtimeIP[0]; @@ -85,7 +85,7 @@ char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr) mesg += realtimeIP[i]; } } else if (realtimeMode == REALTIME_MODE_ADALIGHT) { - mesg += "USB Adalight"; + mesg += F("USB Adalight"); } else { //generic mesg += "data"; } @@ -137,11 +137,11 @@ char* URL_response(AsyncWebServerRequest *request) obuf = sbuf; olen = 0; - oappend(""); + oappend((const char*)F("\" target=\"_blank\">")); oappend(s2buf); - oappend(""); + oappend((const char*)F("")); if (request != nullptr) request->send(200, "text/html", obuf); } @@ -355,9 +355,7 @@ void getSettingsJS(byte subPage, char* dest) sappends('s',"MG",mqttGroupTopic); #endif - #ifdef WLED_DISABLE_HUESYNC - sappends('m',"(\"hms\")[0]","Unsupported in build"); - #else + #ifndef WLED_DISABLE_HUESYNC sappend('v',"H0",hueIP[0]); sappend('v',"H1",hueIP[1]); sappend('v',"H2",hueIP[2]); @@ -368,7 +366,20 @@ void getSettingsJS(byte subPage, char* dest) sappend('c',"HO",hueApplyOnOff); sappend('c',"HB",hueApplyBri); sappend('c',"HC",hueApplyColor); - sappends('m',"(\"hms\")[0]",hueError); + char hueErrorString[25]; + switch (hueError) + { + case HUE_ERROR_INACTIVE : strcpy(hueErrorString,(char*)F("Inactive")); break; + case HUE_ERROR_ACTIVE : strcpy(hueErrorString,(char*)F("Active")); break; + case HUE_ERROR_UNAUTHORIZED : strcpy(hueErrorString,(char*)F("Unauthorized")); break; + case HUE_ERROR_LIGHTID : strcpy(hueErrorString,(char*)F("Invalid light ID")); break; + case HUE_ERROR_PUSHLINK : strcpy(hueErrorString,(char*)F("Link button not pressed")); break; + case HUE_ERROR_JSON_PARSING : strcpy(hueErrorString,(char*)F("JSON parsing error")); break; + case HUE_ERROR_TIMEOUT : strcpy(hueErrorString,(char*)F("Timeout")); break; + default: sprintf(hueErrorString,"Bridge Error %i",hueError); + } + + sappends('m',"(\"hms\")[0]",hueErrorString); #endif } diff --git a/wled00/wled15_hue.ino b/wled00/wled15_hue.ino index 77e6622c..5f6b8f5b 100644 --- a/wled00/wled15_hue.ino +++ b/wled00/wled15_hue.ino @@ -23,7 +23,7 @@ void handleHue() reconnectHue(); } else { hueClient->close(); - if (hueError[0] == 'A') strcpy(hueError,"Inactive"); + if (hueError == HUE_ERROR_ACTIVE) hueError = HUE_ERROR_INACTIVE; } } @@ -44,7 +44,7 @@ void reconnectHue() void onHueError(void* arg, AsyncClient* client, int8_t error) { DEBUG_PRINTLN("Hue err"); - strcpy(hueError,"Request timeout"); + hueError = HUE_ERROR_TIMEOUT; } void onHueConnect(void* arg, AsyncClient* client) @@ -59,15 +59,15 @@ void sendHuePoll() String req = ""; if (hueAuthRequired) { - req += "POST /api HTTP/1.1\r\nHost: "; + req += F("POST /api HTTP/1.1\r\nHost: "); req += hueIP.toString(); - req += "\r\nContent-Length: 25\r\n\r\n{\"devicetype\":\"wled#esp\"}"; + req += F("\r\nContent-Length: 25\r\n\r\n{\"devicetype\":\"wled#esp\"}"); } else { req += "GET /api/"; req += hueApiKey; req += "/lights/" + String(huePollLightId); - req += " HTTP/1.1\r\nHost: "; + req += F(" HTTP/1.1\r\nHost: "); req += hueIP.toString(); req += "\r\n\r\n"; } @@ -93,21 +93,18 @@ void onHueData(void* arg, AsyncClient* client, void *data, size_t len) auto error = deserializeJson(root, str); if (error) { - strcpy(hueError,"JSON parsing error"); return; + hueError = HUE_ERROR_JSON_PARSING; return; } int hueErrorCode = root[0]["error"]["type"]; if (hueErrorCode)//hue bridge returned error { + hueError = hueErrorCode; switch (hueErrorCode) { - case 1: strcpy(hueError,"Unauthorized"); hueAuthRequired = true; break; - case 3: strcpy(hueError,"Invalid light ID"); huePollingEnabled = false; break; - case 101: strcpy(hueError,"Link button not pressed"); hueAuthRequired = true; break; - default: - char coerr[18]; - sprintf(coerr,"Bridge Error %i",hueErrorCode); - strcpy(hueError,coerr); + case 1: hueAuthRequired = true; break; //Unauthorized user + case 3: huePollingEnabled = false; break; //Invalid light ID + case 101: hueAuthRequired = true; break; //link button not presset } return; } @@ -133,7 +130,7 @@ void onHueData(void* arg, AsyncClient* client, void *data, size_t len) auto error = deserializeJson(root, str); if (error) { - strcpy(hueError,"JSON parsing error"); return; + hueError = HUE_ERROR_JSON_PARSING; return; } float hueX=0, hueY=0; @@ -173,7 +170,7 @@ void onHueData(void* arg, AsyncClient* client, void *data, size_t len) hueBri = 0; } - strcpy(hueError,"Active"); + hueError = HUE_ERROR_ACTIVE; //apply vals if (hueBri != hueBriLast) diff --git a/wled00/wled18_server.ino b/wled00/wled18_server.ino index 826425d1..f11a64be 100644 --- a/wled00/wled18_server.ino +++ b/wled00/wled18_server.ino @@ -62,44 +62,44 @@ void initServer() }); server.on("/reset", HTTP_GET, [](AsyncWebServerRequest *request){ - serveMessage(request, 200,"Rebooting now...","Please wait ~10 seconds...",129); + serveMessage(request, 200,"Rebooting now...",F("Please wait ~10 seconds..."),129); doReboot = true; }); server.on("/settings/wifi", HTTP_POST, [](AsyncWebServerRequest *request){ if (!(wifiLock && otaLock)) handleSettingsSet(request, 1); - serveMessage(request, 200,"WiFi settings saved.","Please connect to the new IP (if changed)",129); + serveMessage(request, 200,F("WiFi settings saved."),F("Please connect to the new IP (if changed)"),129); forceReconnect = true; }); server.on("/settings/leds", HTTP_POST, [](AsyncWebServerRequest *request){ handleSettingsSet(request, 2); - serveMessage(request, 200,"LED settings saved.","Redirecting...",1); + serveMessage(request, 200,F("LED settings saved."),"Redirecting...",1); }); server.on("/settings/ui", HTTP_POST, [](AsyncWebServerRequest *request){ handleSettingsSet(request, 3); - serveMessage(request, 200,"UI settings saved.","Redirecting...",1); + serveMessage(request, 200,F("UI settings saved."),"Redirecting...",1); }); server.on("/settings/dmx", HTTP_POST, [](AsyncWebServerRequest *request){ handleSettingsSet(request, 7); - serveMessage(request, 200,"UI settings saved.","Redirecting...",1); + serveMessage(request, 200,F("UI settings saved."),"Redirecting...",1); }); server.on("/settings/sync", HTTP_POST, [](AsyncWebServerRequest *request){ handleSettingsSet(request, 4); - serveMessage(request, 200,"Sync settings saved.","Redirecting...",1); + serveMessage(request, 200,F("Sync settings saved."),"Redirecting...",1); }); server.on("/settings/time", HTTP_POST, [](AsyncWebServerRequest *request){ handleSettingsSet(request, 5); - serveMessage(request, 200,"Time settings saved.","Redirecting...",1); + serveMessage(request, 200,F("Time settings saved."),"Redirecting...",1); }); server.on("/settings/sec", HTTP_POST, [](AsyncWebServerRequest *request){ handleSettingsSet(request, 6); - if (!doReboot) serveMessage(request, 200,"Security settings saved.","Rebooting, please wait ~10 seconds...",129); + if (!doReboot) serveMessage(request, 200,F("Security settings saved."),F("Rebooting, please wait ~10 seconds..."),129); doReboot = true; }); @@ -148,7 +148,7 @@ void initServer() }); server.on("/teapot", HTTP_GET, [](AsyncWebServerRequest *request){ - serveMessage(request, 418, "418. I'm a teapot.", "(Tangible Embedded Advanced Project Of Twinkling)", 254); + serveMessage(request, 418, F("418. I'm a teapot."), F("(Tangible Embedded Advanced Project Of Twinkling)"), 254); }); //if OTA is allowed @@ -161,7 +161,7 @@ void initServer() #endif #else server.on("/edit", HTTP_GET, [](AsyncWebServerRequest *request){ - serveMessage(request, 501, "Not implemented", "The SPIFFS editor is disabled in this build.", 254); + serveMessage(request, 501, "Not implemented", F("The SPIFFS editor is disabled in this build."), 254); }); #endif //init ota page @@ -173,9 +173,9 @@ void initServer() server.on("/update", HTTP_POST, [](AsyncWebServerRequest *request){ if (Update.hasError()) { - serveMessage(request, 500, "Failed updating firmware!", "Please check your file and retry!", 254); return; + serveMessage(request, 500, F("Failed updating firmware!"), F("Please check your file and retry!"), 254); return; } - serveMessage(request, 200, "Successfully updated firmware!", "Please wait while the module reboots...", 131); + serveMessage(request, 200, F("Successfully updated firmware!"), F("Please wait while the module reboots..."), 131); doReboot = true; },[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){ if(!index){ @@ -197,7 +197,7 @@ void initServer() #else server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){ - serveMessage(request, 501, "Not implemented", "OTA updates are disabled in this build.", 254); + serveMessage(request, 501, "Not implemented", F("OTA updates are disabled in this build."), 254); }); #endif } else @@ -217,7 +217,7 @@ void initServer() }); #else server.on("/dmxmap", HTTP_GET, [](AsyncWebServerRequest *request){ - serveMessage(request, 501, "Not implemented", "DMX support is not enabled in this build.", 254); + serveMessage(request, 501, "Not implemented", F("DMX support is not enabled in this build."), 254); }); #endif server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ @@ -292,10 +292,10 @@ String msgProcessor(const String& var) messageBody += ""; } else if (optt == 253) { - messageBody += "

"; //button to settings + messageBody += F("

"); //button to settings } else if (optt == 254) { - messageBody += "

"; + messageBody += F("

"); } return messageBody; } @@ -324,7 +324,7 @@ String settingsProcessor(const String& var) #ifdef WLED_ENABLE_DMX if (var == "DMXMENU") { - return String("
"); + return String(F("
")); } #endif diff --git a/wled00/wled19_json.ino b/wled00/wled19_json.ino index 5cc1f519..4d935973 100644 --- a/wled00/wled19_json.ino +++ b/wled00/wled19_json.ino @@ -343,7 +343,7 @@ void serveJson(AsyncWebServerRequest* request) return; } else if (url.length() > 6) { //not just /json - request->send( 501, "application/json", "{\"error\":\"Not implemented\"}"); + request->send( 501, "application/json", F("{\"error\":\"Not implemented\"}")); return; } From 755448f9f5a8ed9349f828a4b57f711725c421ee Mon Sep 17 00:00:00 2001 From: cschwinne Date: Tue, 25 Feb 2020 02:25:17 +0100 Subject: [PATCH 134/232] Fix LED pin changed --- readme.md | 2 +- wled00/NpbWrapper.h | 4 ++-- wled00/wled00.ino | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index 1cbe9b80..c796ca5c 100644 --- a/readme.md +++ b/readme.md @@ -12,7 +12,7 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control ### Features: - WS2812FX library integrated for over 100 special effects -- FastLED noise effects and palettes +- FastLED noise effects and 50 palettes - Modern UI with color, effect and segment controls - Segments to set different effects and colors to parts of the LEDs - Settings page - configuration over network diff --git a/wled00/NpbWrapper.h b/wled00/NpbWrapper.h index f12d057f..71367d98 100644 --- a/wled00/NpbWrapper.h +++ b/wled00/NpbWrapper.h @@ -4,7 +4,7 @@ //PIN CONFIGURATION #ifndef LEDPIN -#define LEDPIN 5 //strip pin. Any for ESP32, gpio2 or 3 is recommended for ESP8266 (gpio2/3 are labeled D4/RX on NodeMCU and Wemos) +#define LEDPIN 2 //strip pin. Any for ESP32, gpio2 or 3 is recommended for ESP8266 (gpio2/3 are labeled D4/RX on NodeMCU and Wemos) #endif //#define USE_APA102 // Uncomment for using APA102 LEDs. //#define USE_WS2801 // Uncomment for using WS2801 LEDs (make sure you have NeoPixelBus v2.5.6 or newer) @@ -30,7 +30,7 @@ #endif #ifndef RLYMDE -#define RLYMDE 1 //mode for relay, 0: LOW if LEDs are on 1: HIGH if LEDs are on +#define RLYMDE 1 //mode for relay, 0: LOW if LEDs are on 1: HIGH if LEDs are on #endif //END CONFIGURATION diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 93789577..f214929c 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -119,7 +119,7 @@ #endif //version code in format yymmddb (b = daily build) -#define VERSION 2002251 +#define VERSION 2002252 char versionString[] = "0.9.1"; @@ -430,7 +430,7 @@ IPAddress ntpServerIP; uint16_t ntpLocalPort = 2390; #define NTP_PACKET_SIZE 48 -//maximum number of LEDs - MAX_LEDS is comming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue +//maximum number of LEDs - MAX_LEDS is coming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue #define MAX_LEDS 1500 #define MAX_LEDS_DMA 500 From 143179cac9d98a48146c8f2a907b227250842c9f Mon Sep 17 00:00:00 2001 From: zewelor Date: Tue, 25 Feb 2020 08:42:28 +0100 Subject: [PATCH 135/232] Use new platformio ldscript syntax --- platformio.ini | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/platformio.ini b/platformio.ini index 581bc995..fcbb7b28 100644 --- a/platformio.ini +++ b/platformio.ini @@ -88,33 +88,33 @@ platform = ${common:esp8266.arduino_core_2_6_3} build_flags = -D ESP8266 -D PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH - -Wl,-Teagle.flash.4m1m.ld ;;;; Required for core > v2.5.0 or staging version 4MB Flash 3MB SPIFFs +board_build.ldscript = eagle.flash.4m1m.ld ;;;; Required for core > v2.5.0 or staging version 4MB Flash 3MB SPIFFs lib_ignore = AsyncTCP [common:esp8266_1M] platform = ${common:esp8266.arduino_core_2_6_3} build_flags = -D PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH - -Wl,-Teagle.flash.1m.ld ;;;; Compile with no SPIFFS to leave space for OTA ; -D WLED_DISABLE_OTA ; -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK -D WLED_DISABLE_CRONIXIE ; -D WLED_DISABLE_HUESYNC -D WLED_DISABLE_INFRARED +board_build.ldscript = eagle.flash.1m.ld ;;;; Compile with no SPIFFS to leave space for OTA lib_ignore = ${common:esp8266.lib_ignore} [common:esp8266_512k] platform = ${common:esp8266.arduino_core_2_6_3} build_flags = -D PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH - -Wl,-Teagle.flash.512k.ld ;;;; Compile with no SPIFFS -D WLED_DISABLE_OTA -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK -D WLED_DISABLE_CRONIXIE -D WLED_DISABLE_HUESYNC -D WLED_DISABLE_INFRARED +board_build.ldscript = eagle.flash.512k.ld ;;;; Compile with no SPIFFS lib_ignore = ${common:esp8266.lib_ignore} [common:esp32] @@ -136,6 +136,7 @@ framework = ${common.framework} build_flags = ${common.build_flags} ${common:esp8266.build_flags} +board_build.ldscript = ${common:esp8266.board_build.ldscript} lib_deps = ${common.lib_deps_external} lib_compat_mode = strict @@ -150,6 +151,7 @@ framework = ${common.framework} build_flags = ${common.build_flags} ${common:esp8266.build_flags} +board_build.ldscript = ${common:esp8266.board_build.ldscript} lib_deps = ${common.lib_deps_external} lib_compat_mode = strict @@ -166,6 +168,7 @@ build_flags = ${common:esp8266_1M.build_flags} # disable IR because there is no pin for it -D WLED_DISABLE_INFRARED +board_build.ldscript = ${common:esp8266_1M.board_build.ldscript} lib_deps = ${common.lib_deps_external} lib_compat_mode = strict @@ -180,6 +183,7 @@ framework = ${common.framework} build_flags = ${common.build_flags} ${common:esp8266_512k.build_flags} +board_build.ldscript = ${common:esp8266_512k.board_build.ldscript} lib_deps = ${common.lib_deps_external} lib_compat_mode = strict @@ -194,6 +198,7 @@ framework = ${common.framework} build_flags = ${common.build_flags} ${common:esp8266.build_flags} +board_build.ldscript = ${common:esp8266.board_build.ldscript} lib_deps = ${common.lib_deps_external} lib_compat_mode = strict @@ -225,6 +230,7 @@ build_flags = ${common:esp8266_1M.build_flags} -D WLED_DISABLE_HUESYNC -D WLED_USE_ANALOG_LEDS +board_build.ldscript = ${common:esp8266_1M.board_build.ldscript} lib_deps = ${common.lib_deps_external} lib_compat_mode = strict @@ -242,6 +248,7 @@ build_flags = -D WLED_DISABLE_HUESYNC -D WLED_USE_ANALOG_LEDS -D WLED_USE_H801 +board_build.ldscript = ${common:esp8266_1M.board_build.ldscript} lib_deps = ${common.lib_deps_external} lib_compat_mode = strict @@ -260,6 +267,7 @@ build_flags = -D WLED_USE_ANALOG_LEDS -D WLED_USE_H801 -D WLED_ENABLE_5CH_LEDS +board_build.ldscript = ${common:esp8266_1M.board_build.ldscript} lib_deps = ${common.lib_deps_external} lib_compat_mode = strict From a776b8ac31d6bf552db4cbdeb971114809dc7e2d Mon Sep 17 00:00:00 2001 From: Def3nder Date: Wed, 26 Feb 2020 11:17:56 +0100 Subject: [PATCH 136/232] Refactor platformio.ini (#721) * Override script updated accordingly * ldscriipt for 1m0 adjusted * backup files deleted * deletion of wled00,ino.cpp * remove travis build from PIO --- platformio.ini | 363 +++++++++++++------------------- platformio_override.ini.example | 29 ++- 2 files changed, 163 insertions(+), 229 deletions(-) diff --git a/platformio.ini b/platformio.ini index fcbb7b28..5549cc17 100644 --- a/platformio.ini +++ b/platformio.ini @@ -2,273 +2,208 @@ ; Please visit documentation: https://docs.platformio.org/page/projectconf.html [platformio] -src_dir = ./wled00 +src_dir = ./wled00 data_dir = ./wled00/data -;lib_extra_dirs = ./wled00/src -lib_dir = ./wled00/src -extra_configs = platformio_override.ini +lib_dir = ./wled00/src +extra_configs = + platformio_override.ini + ; Please uncomment one of the 5 lines below to select your board default_envs = nodemcuv2 ; default_envs = esp01 ; default_envs = esp01_1m ; default_envs = esp07 ; default_envs = d1_mini +; default_envs = d1_mini_debug ; default_envs = esp32dev ; default_envs = esp8285_4CH_MagicHome ; default_envs = esp8285_4CH_H801 ; default_envs = esp8285_5CH_H801 + # Release binaries follow -; default_envs = nodemcuv2, esp01, esp01_1m, esp32dev, - - +; default_envs = nodemcuv2, esp01, esp01_1m, esp32dev [common] -framework = arduino -monitor_speed = 115200 -board_build.flash_mode = dout -upload_speed = 115200 -upload_speed_fast = 921600 -build_flags = - -w ; supresses all C/C++ warnings - ; -D VERSION=0.8.5 - ; -D DEBUG - #build_flags for the IRremoteESP8266 library (enabled decoders have to appear here) - -D _IR_ENABLE_DEFAULT_=false - -D DECODE_HASH=true - -D DECODE_NEC=true - -D DECODE_SONY=true - -D DECODE_SAMSUNG=true - -D DECODE_LG=true - -# TODO replace libs in /lib with managed libs in here if possible. -# If they are not changed it's just a matter of setting the correct version and change the import statement -lib_deps_external = - #Blynk@0.5.4(changed) - #E131@1.0.0(changed) - FastLED@3.3.2 - NeoPixelBus@2.5.6 - ESPAsyncTCP@1.2.0 - ESPAsyncUDP@697c75a025 - AsyncTCP@1.0.3 - Esp Async WebServer@1.2.0 - #ArduinoJson@5.13.5 - #IRremoteESP8266@2.7.2 - https://github.com/crankyoldgit/IRremoteESP8266.git - #Time@1.5 - #Timezone@1.2.1 - #For use SSD1306 OLED display uncomment following - #U8g2@~2.27.2 - #For Dallas sensor uncomment following 2 lines - #DallasTemperature@~3.8.0 - #OneWire@~2.3.5 -[common:esp8266] # ------------------------------------------------------------------------------ # PLATFORM: # !! DO NOT confuse platformio's ESP8266 development platform with Arduino core for ESP8266 -# We use Arduino Core 2.5.0 (platformIO 2.0.4) as default # # arduino core 2.3.0 = platformIO 1.5.0 # arduino core 2.4.0 = platformIO 1.6.0 # arduino core 2.4.1 = platformIO 1.7.3 # arduino core 2.4.2 = platformIO 1.8.0 # arduino core 2.5.0 = platformIO 2.0.4 -# arduino core stage = platformIO feature#stage +# arduino core 2.5.1 = platformIO 2.1.1 +# arduino core 2.5.2 = platformIO 2.2.3 +# arduino core 2.6.1 = platformIO 2.3.0 +# arduino core 2.6.2 = platformIO 2.3.1 +# arduino core 2.6.3 = platformIO 2.3.2 # ------------------------------------------------------------------------------ arduino_core_2_3_0 = espressif8266@1.5.0 arduino_core_2_4_0 = espressif8266@1.6.0 arduino_core_2_4_1 = espressif8266@1.7.3 arduino_core_2_4_2 = espressif8266@1.8.0 arduino_core_2_5_0 = espressif8266@2.0.4 +arduino_core_2_5_1 = espressif8266@2.1.1 arduino_core_2_5_2 = espressif8266@2.2.3 arduino_core_2_6_1 = espressif8266@2.3.0 arduino_core_2_6_2 = espressif8266@2.3.1 -arduino_core_2_6_3 = espressif8266@2.3.3 -arduino_core_stage = https://github.com/platformio/platform-espressif8266.git#feature/stage -platform = ${common:esp8266.arduino_core_2_6_3} -build_flags = - -D ESP8266 - -D PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH -board_build.ldscript = eagle.flash.4m1m.ld ;;;; Required for core > v2.5.0 or staging version 4MB Flash 3MB SPIFFs -lib_ignore = AsyncTCP +arduino_core_2_6_3 = espressif8266@2.3.2 -[common:esp8266_1M] -platform = ${common:esp8266.arduino_core_2_6_3} -build_flags = - -D PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH - ; -D WLED_DISABLE_OTA - ; -D WLED_DISABLE_ALEXA - -D WLED_DISABLE_BLYNK - -D WLED_DISABLE_CRONIXIE - ; -D WLED_DISABLE_HUESYNC - -D WLED_DISABLE_INFRARED -board_build.ldscript = eagle.flash.1m.ld ;;;; Compile with no SPIFFS to leave space for OTA -lib_ignore = ${common:esp8266.lib_ignore} +# Development platforms +arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop +arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/stage -[common:esp8266_512k] -platform = ${common:esp8266.arduino_core_2_6_3} -build_flags = - -D PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH - -D WLED_DISABLE_OTA - -D WLED_DISABLE_ALEXA - -D WLED_DISABLE_BLYNK - -D WLED_DISABLE_CRONIXIE - -D WLED_DISABLE_HUESYNC - -D WLED_DISABLE_INFRARED -board_build.ldscript = eagle.flash.512k.ld ;;;; Compile with no SPIFFS -lib_ignore = ${common:esp8266.lib_ignore} +platform = ${common.arduino_core_2_4_2} +platform_latest = ${common.arduino_core_2_6_3} -[common:esp32] -platform = espressif32@1.11.2 -build_flags = - -D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH - -D ARDUINO_ARCH_ESP32 -lib_ignore = - ESPAsyncTCP - ESPAsyncUDP +# ------------------------------------------------------------------------------ +# FLAGS: DEBUG +# +# ------------------------------------------------------------------------------ +debug_flags = -D DEBUG=1 -D WLED_DEBUG -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM +#if needed (for memleaks etc) also add; -DDEBUG_ESP_OOM -include "umm_malloc/umm_malloc_cfg.h" + +# ------------------------------------------------------------------------------ +# FLAGS: ldscript +# ldscript_512k ( 512 KB) = 487 KB sketch, 4 KB eeprom, no spiffs, 16 KB reserved +# ldscript_1m0m (1024 KB) = 999 KB sketch, 4 KB eeprom, no spiffs, 16 KB reserved +# ldscript_2m1m (2048 KB) = 1019 KB sketch, 4 KB eeprom, 1004 KB spiffs, 16 KB reserved +# ldscript_4m1m (4096 KB) = 1019 KB sketch, 4 KB eeprom, 1002 KB spiffs, 16 KB reserved, 2048 KB empty/ota? +# ldscript_4m3m (4096 KB) = 1019 KB sketch, 4 KB eeprom, 3040 KB spiffs, 16 KB reserved +# +# Available lwIP variants (macros): +# -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH = v1.4 Higher Bandwidth (default) +# -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY = v2 Lower Memory +# -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH = v2 Higher Bandwidth +# +# BearSSL performance: +# When building with -DSECURE_CLIENT=SECURE_CLIENT_BEARSSL, please add `board_build.f_cpu = 160000000` to the environment configuration +# +# BearSSL ciphers: +# When building on core >= 2.5, you can add the build flag -DBEARSSL_SSL_BASIC in order to build BearSSL with a limited set of ciphers: +# TLS_RSA_WITH_AES_128_CBC_SHA256 / AES128-SHA256 +# TLS_RSA_WITH_AES_256_CBC_SHA256 / AES256-SHA256 +# TLS_RSA_WITH_AES_128_CBC_SHA / AES128-SHA +# TLS_RSA_WITH_AES_256_CBC_SHA / AES256-SHA +# This reduces the OTA size with ~45KB, so it's especially useful on low memory boards (512k/1m). +# ------------------------------------------------------------------------------ +build_flags = -g -w -DMQTT_MAX_PACKET_SIZE=1024 -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH + -DSECURE_CLIENT=SECURE_CLIENT_BEARSSL -DBEARSSL_SSL_BASIC +build_flags_esp8266 = ${common.build_flags} -DESP8266 +build_flags_esp32 = ${common.build_flags} -DARDUINO_ARCH_ESP32 + +ldscript_512k = eagle.flash.512k.ld ;for older versions change this to eagle.flash.512k0.ld +ldscript_1m0m = eagle.flash.1m.ld ;for older versions change this to eagle.flash.1m0.ld +ldscript_2m1m = eagle.flash.2m1m.ld +ldscript_4m1m = eagle.flash.4m1m.ld +ldscript_4m3m = eagle.flash.4m3m.ld + +shared_libdeps_dir = ./wled00/src + +# ------------------------------------------------------------------------------ +# COMMON SETTINGS: +# ------------------------------------------------------------------------------ +[env] +framework = arduino +board_build.flash_mode = dout +monitor_speed = 115200 +upload_speed = 115200 +lib_extra_dirs = + ${common.shared_libdeps_dir} + +# ------------------------------------------------------------------------------ +# LIBRARIES: required dependencies +# Please note that we don't always use the latest version of a library. +# +# The following libraries have been included (and some of them changd) in the source: +# ArduinoJson@5.13.5, Blynk@0.5.4(changed), E131@1.0.0(changed), Time@1.5, Timezone@1.2.1 +# ------------------------------------------------------------------------------ +lib_compat_mode = strict +lib_deps = + FastLED@3.3.2 + NeoPixelBus@2.5.6 + ESPAsyncTCP@1.2.0 + ESPAsyncUDP@697c75a025 + AsyncTCP@1.0.3 + Esp Async WebServer@1.2.0 + IRremoteESP8266@2.7.3 + #For use SSD1306 OLED display uncomment following + #U8g2@~2.27.2 + #For Dallas sensor uncomment following 2 lines + #DallasTemperature@~3.8.0 + #OneWire@~2.3.5 +lib_ignore = + AsyncTCP + +# ------------------------------------------------------------------------------ +# WLED BUILDS +# ------------------------------------------------------------------------------ -# see: http://docs.platformio.org/en/latest/platforms/espressif8266.html [env:nodemcuv2] board = nodemcuv2 -platform = ${common:esp8266.platform} -monitor_speed = ${common.monitor_speed} -upload_speed = ${common.upload_speed} -framework = ${common.framework} -build_flags = - ${common.build_flags} - ${common:esp8266.build_flags} -board_build.ldscript = ${common:esp8266.board_build.ldscript} -lib_deps = - ${common.lib_deps_external} -lib_compat_mode = strict -lib_ignore = ${common:esp8266.lib_ignore} - -[env:d1_mini] -board = d1_mini -platform = ${common:esp8266.platform} -monitor_speed = ${common.monitor_speed} -upload_speed = ${common.upload_speed} -framework = ${common.framework} -build_flags = - ${common.build_flags} - ${common:esp8266.build_flags} -board_build.ldscript = ${common:esp8266.board_build.ldscript} -lib_deps = - ${common.lib_deps_external} -lib_compat_mode = strict -lib_ignore = ${common:esp8266.lib_ignore} - -[env:esp01_1m] -board = esp01_1m -platform = ${common:esp8266_1M.platform} -monitor_speed = ${common.monitor_speed} -upload_speed = ${common.upload_speed} -framework = ${common.framework} -build_flags = - ${common.build_flags} - ${common:esp8266_1M.build_flags} - # disable IR because there is no pin for it - -D WLED_DISABLE_INFRARED -board_build.ldscript = ${common:esp8266_1M.board_build.ldscript} -lib_deps = - ${common.lib_deps_external} -lib_compat_mode = strict -lib_ignore = ${common:esp8266.lib_ignore} +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} [env:esp01] board = esp01 -platform = ${common:esp8266_512k.platform} -monitor_speed = ${common.monitor_speed} -upload_speed = ${common.upload_speed} -framework = ${common.framework} -build_flags = - ${common.build_flags} - ${common:esp8266_512k.build_flags} -board_build.ldscript = ${common:esp8266_512k.board_build.ldscript} -lib_deps = - ${common.lib_deps_external} -lib_compat_mode = strict -lib_ignore = ${common:esp8266.lib_ignore} +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_512k} +build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK + -D WLED_DISABLE_CRONIXIE -D WLED_DISABLE_HUESYNC -D WLED_DISABLE_INFRARED + +[env:esp01_1m] +board = esp01_1m +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_1m0m} +build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_BLYNK -D WLED_DISABLE_CRONIXIE -D WLED_DISABLE_INFRARED [env:esp07] board = esp07 -platform = ${common:esp8266.platform} -monitor_speed = ${common.monitor_speed} -upload_speed = ${common.upload_speed} -framework = ${common.framework} -build_flags = - ${common.build_flags} - ${common:esp8266.build_flags} -board_build.ldscript = ${common:esp8266.board_build.ldscript} -lib_deps = - ${common.lib_deps_external} -lib_compat_mode = strict -lib_ignore = ${common:esp8266.lib_ignore} +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} + +[env:d1_mini] +board = d1_mini +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} -# see: http://docs.platformio.org/en/latest/platforms/espressif32.html [env:esp32dev] board = esp32dev -platform = ${common:esp32.platform} -monitor_speed = ${common.monitor_speed} -upload_speed = ${common.upload_speed_fast} -framework = ${common.framework} -build_flags = - ${common.build_flags} - ${common:esp32.build_flags} -lib_deps = - ${common.lib_deps_external} -lib_ignore = ${common:esp32.lib_ignore} -lib_compat_mode = strict +platform = espressif32@1.11.2 +build_flags = ${common.build_flags_esp32} +lib_ignore = + ESPAsyncTCP + ESPAsyncUDP [env:esp8285_4CH_MagicHome] board = esp8285 -platform = ${common:esp8266_1M.platform} -monitor_speed = ${common.monitor_speed} -upload_speed = ${common.upload_speed} -framework = ${common.framework} -build_flags = - ${common.build_flags} - ${common:esp8266_1M.build_flags} - -D WLED_DISABLE_HUESYNC - -D WLED_USE_ANALOG_LEDS -board_build.ldscript = ${common:esp8266_1M.board_build.ldscript} -lib_deps = - ${common.lib_deps_external} -lib_compat_mode = strict -lib_ignore = ${common:esp8266.lib_ignore} +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_1m0m} +build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_HUESYNC -D WLED_USE_ANALOG_LEDS [env:esp8285_4CH_H801] board = esp8285 -platform = ${common:esp8266_1M.platform} -monitor_speed = ${common.monitor_speed} -upload_speed = ${common.upload_speed} -framework = ${common.framework} -build_flags = - ${common.build_flags} - ${common:esp8266_1M.build_flags} - -D WLED_DISABLE_HUESYNC - -D WLED_USE_ANALOG_LEDS - -D WLED_USE_H801 -board_build.ldscript = ${common:esp8266_1M.board_build.ldscript} -lib_deps = - ${common.lib_deps_external} -lib_compat_mode = strict -lib_ignore = ${common:esp8266.lib_ignore} +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_1m0m} +build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_HUESYNC -D WLED_USE_ANALOG_LEDS -D WLED_USE_H801 [env:esp8285_5CH_H801] board = esp8285 -platform = ${common:esp8266_1M.platform} -monitor_speed = ${common.monitor_speed} -upload_speed = ${common.upload_speed} -framework = ${common.framework} -build_flags = - ${common.build_flags} - ${common:esp8266_1M.build_flags} - -D WLED_DISABLE_HUESYNC - -D WLED_USE_ANALOG_LEDS - -D WLED_USE_H801 - -D WLED_ENABLE_5CH_LEDS -board_build.ldscript = ${common:esp8266_1M.board_build.ldscript} -lib_deps = - ${common.lib_deps_external} -lib_compat_mode = strict -lib_ignore = ${common:esp8266.lib_ignore} +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_1m0m} +build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_HUESYNC -D WLED_USE_ANALOG_LEDS -D WLED_USE_H801 -D WLED_ENABLE_5CH_LEDS + +# ------------------------------------------------------------------------------ +# DEVELOPMENT BOARDS +# ------------------------------------------------------------------------------ + +[env:d1_mini_debug] +board = d1_mini +build_type = debug +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} ${common.debug_flags} diff --git a/platformio_override.ini.example b/platformio_override.ini.example index 03a77056..09c30172 100644 --- a/platformio_override.ini.example +++ b/platformio_override.ini.example @@ -1,18 +1,17 @@ -; Example PlatformIO Project Configuration Override -; ------------------------------------------------------------------------------ -; Copy to platformio_override.ini to activate overrides -; ------------------------------------------------------------------------------ -; Please visit documentation: https://docs.platformio.org/page/projectconf.html +# Example PlatformIO Project Configuration Override +# ------------------------------------------------------------------------------ +# Copy to platformio_override.ini to activate overrides +# ------------------------------------------------------------------------------ +# Please visit documentation: https://docs.platformio.org/page/projectconf.html [env:esp8266_1m_custom] -platform = ${common:esp8266.arduino_core_2_6_3} -build_flags = - -D PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH - -Wl,-Teagle.flash.1m.ld ;;;; Compile with no SPIFFS to leave space for OTA - ; -D WLED_DISABLE_OTA - -D WLED_DISABLE_ALEXA +board = esp01_1m +platform = ${common.arduino_core_2_4_2} +board_build.ldscript = ${common.ldscript_1m0m} +build_flags = ${common.build_flags_esp8266} + -D WLED_DISABLE_OTA + -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK - -D WLED_DISABLE_CRONIXIE - -D WLED_DISABLE_HUESYNC - -D WLED_DISABLE_INFRARED -lib_ignore = ${common:esp8266.lib_ignore} \ No newline at end of file + -D WLED_DISABLE_CRONIXIE + -D WLED_DISABLE_HUESYNC + -D WLED_DISABLE_INFRARED \ No newline at end of file From 81da8261a00fe9e3e7a4ec7780fae3be8c5b2783 Mon Sep 17 00:00:00 2001 From: Ser Ko Date: Wed, 26 Feb 2020 19:16:33 -0500 Subject: [PATCH 137/232] Added board heltec_wifi_kit_8 --- platformio.ini | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/platformio.ini b/platformio.ini index 5549cc17..3300c58a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -14,6 +14,7 @@ default_envs = nodemcuv2 ; default_envs = esp01_1m ; default_envs = esp07 ; default_envs = d1_mini +; default_envs = heltec_wifi_kit_8 ; default_envs = d1_mini_debug ; default_envs = esp32dev ; default_envs = esp8285_4CH_MagicHome @@ -171,6 +172,12 @@ platform = ${common.platform_latest} board_build.ldscript = ${common.ldscript_4m1m} build_flags = ${common.build_flags_esp8266} +[env:heltec_wifi_kit_8] +board = d1_mini +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} + [env:esp32dev] board = esp32dev platform = espressif32@1.11.2 From 57d3120b45f7ce48149db3732ecabab9da9b1656 Mon Sep 17 00:00:00 2001 From: Ser Ko Date: Wed, 26 Feb 2020 19:21:57 -0500 Subject: [PATCH 138/232] Grammar correction --- usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino index 58a4e0e6..576795fb 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino @@ -31,7 +31,7 @@ U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_ // --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3" //U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA // --> Third choise of Heltec WiFi-Kit-8 OLED 128X32 0.91" -//U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_RESET, U8X8_PIN_SCL, U8X8_PIN_SDA); // Constracor for Heltec WiFi-Kit-8 +//U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_RESET, U8X8_PIN_SCL, U8X8_PIN_SDA); // Constructor for Heltec WiFi-Kit-8 // gets called once at boot. Do all initialization that doesn't depend on // network here void userSetup() { From e3269f6edce9fe21212bf7a3fee17af56ae86037 Mon Sep 17 00:00:00 2001 From: Ser Ko Date: Wed, 26 Feb 2020 20:36:27 -0500 Subject: [PATCH 139/232] Update wled06_usermod.ino --- usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino index 576795fb..d75cf0a7 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino @@ -32,8 +32,7 @@ U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_ //U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA // --> Third choise of Heltec WiFi-Kit-8 OLED 128X32 0.91" //U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_RESET, U8X8_PIN_SCL, U8X8_PIN_SDA); // Constructor for Heltec WiFi-Kit-8 -// gets called once at boot. Do all initialization that doesn't depend on -// network here +// gets called once at boot. Do all initialization that doesn't depend on network here void userSetup() { sensor.begin(); //Start Dallas temperature sensor u8x8.begin(); From 9eb646085eac4cbfe97454ed78d7154efc86467d Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 27 Feb 2020 09:52:48 +0100 Subject: [PATCH 140/232] Reduce CPU load during interrupt handler See https://github.com/esp8266/Arduino/pull/7057#issuecomment-591632232 --- .../dependencies/arduino/core_esp8266_waveform.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/wled00/src/dependencies/arduino/core_esp8266_waveform.cpp b/wled00/src/dependencies/arduino/core_esp8266_waveform.cpp index 6a52b1b3..57b3c315 100644 --- a/wled00/src/dependencies/arduino/core_esp8266_waveform.cpp +++ b/wled00/src/dependencies/arduino/core_esp8266_waveform.cpp @@ -224,8 +224,8 @@ static ICACHE_RAM_ATTR void timer1Interrupt() { endPin = 32 - __builtin_clz(waveformEnabled); } - if (waveformEnabled) { bool done = false; + if (waveformEnabled) { do { nextEventCycles = microsecondsToClockCycles(MAXIRQUS); for (int i = startPin; i <= endPin; i++) { @@ -257,7 +257,13 @@ static ICACHE_RAM_ATTR void timer1Interrupt() { // Check for toggles int32_t cyclesToGo = wave->nextServiceCycle - now; if (cyclesToGo < 0) { - cyclesToGo = -((-cyclesToGo) % (wave->nextTimeHighCycles + wave->nextTimeLowCycles)); + // See #7057 + // The following is a no-op unless we have overshot by an entire waveform cycle. + // As modulus is an expensive operation, this code is removed for now: + // cyclesToGo = -((-cyclesToGo) % (wave->nextTimeHighCycles + wave->nextTimeLowCycles)); + // + // Alternative version with lower CPU impact: + // while (-cyclesToGo > wave->nextTimeHighCycles + wave->nextTimeLowCycles) { cyclesToGo += wave->nextTimeHighCycles + wave->nextTimeLowCycles)}; waveformState ^= mask; if (waveformState & mask) { if (i == 16) { @@ -309,4 +315,4 @@ static ICACHE_RAM_ATTR void timer1Interrupt() { } }; -#endif \ No newline at end of file +#endif From cffee7bfa7902f417fa34a6b439fd77c92bd9800 Mon Sep 17 00:00:00 2001 From: Def3nder Date: Fri, 28 Feb 2020 16:27:18 +0100 Subject: [PATCH 141/232] Fix 24-key IR remote (#738) --- wled00/wled20_ir.ino | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/wled00/wled20_ir.ino b/wled00/wled20_ir.ino index 617518c3..c65a4c58 100644 --- a/wled00/wled20_ir.ino +++ b/wled00/wled20_ir.ino @@ -90,8 +90,8 @@ void decodeIR(uint32_t code) // sets bright plain white default: return; } - colorUpdated(NOTIFIER_CALL_MODE_BUTTON); //for notifier, IR is considered a button input } + colorUpdated(NOTIFIER_CALL_MODE_BUTTON); //for notifier, IR is considered a button input //code <= 0xF70000 also invalid } @@ -356,25 +356,25 @@ void decodeIR6(uint32_t code) /* next palette */ relativeChange(&effectPalette, 1); switch(lastIR6ColourIdx) { - case 0: colorFromUint32(COLOR_RED); break; - case 1: colorFromUint32(COLOR_REDDISH); break; - case 2:colorFromUint32(COLOR_ORANGE); break; - case 3:colorFromUint32(COLOR_YELLOWISH); break; - case 4:colorFromUint32(COLOR_GREEN); break; - case 5:colorFromUint32(COLOR_GREENISH); break; - case 6:colorFromUint32(COLOR_TURQUOISE); break; - case 7: colorFromUint32(COLOR_CYAN); break; - case 8:colorFromUint32(COLOR_BLUE); break; - case 9:colorFromUint32(COLOR_DEEPBLUE); break; - case 10:colorFromUint32(COLOR_PURPLE); break; - case 11:colorFromUint32(COLOR_PINK); break; - case 12:colorFromUint32(COLOR_WHITE); break; - default:break; + case 0: colorFromUint32(COLOR_RED); break; + case 1: colorFromUint32(COLOR_REDDISH); break; + case 2: colorFromUint32(COLOR_ORANGE); break; + case 3: colorFromUint32(COLOR_YELLOWISH); break; + case 4: colorFromUint32(COLOR_GREEN); break; + case 5: colorFromUint32(COLOR_GREENISH); break; + case 6: colorFromUint32(COLOR_TURQUOISE); break; + case 7: colorFromUint32(COLOR_CYAN); break; + case 8: colorFromUint32(COLOR_BLUE); break; + case 9: colorFromUint32(COLOR_DEEPBLUE); break; + case 10:colorFromUint32(COLOR_PURPLE); break; + case 11:colorFromUint32(COLOR_PINK); break; + case 12:colorFromUint32(COLOR_WHITE); break; + default: break; } lastIR6ColourIdx++; if(lastIR6ColourIdx > 12) lastIR6ColourIdx = 0; break; - case IR6_MUTE: effectCurrent = 0; effectPalette = 0; colorFromUint32(COLOR_WHITE); bri=255; break; + case IR6_MUTE: effectCurrent = 0; effectPalette = 0; colorFromUint32(COLOR_WHITE); bri=255; break; } lastValidCode = code; } From 480e7f5b5463a8c34f169e01791d0005d70c5125 Mon Sep 17 00:00:00 2001 From: Def3nder Date: Fri, 28 Feb 2020 18:53:38 +0100 Subject: [PATCH 142/232] Add all release environments to platformio.ini (#736) --- platformio.ini | 37 +++++++++++++++++++++++++++++---- platformio_override.ini.example | 19 ++++++++++++++++- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/platformio.ini b/platformio.ini index 5549cc17..9128d50e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -21,7 +21,7 @@ default_envs = nodemcuv2 ; default_envs = esp8285_5CH_H801 # Release binaries follow -; default_envs = nodemcuv2, esp01, esp01_1m, esp32dev +; default_envs = nodemcuv2, esp01, esp01_1m, esp32dev, custom_WS2801, custom_APA102, custom_LEDPIN_16, custom_LEDPIN_4 [common] # ------------------------------------------------------------------------------ @@ -48,7 +48,7 @@ arduino_core_2_5_1 = espressif8266@2.1.1 arduino_core_2_5_2 = espressif8266@2.2.3 arduino_core_2_6_1 = espressif8266@2.3.0 arduino_core_2_6_2 = espressif8266@2.3.1 -arduino_core_2_6_3 = espressif8266@2.3.2 +arduino_core_2_6_3 = espressif8266@2.3.3 # Development platforms arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop @@ -61,8 +61,9 @@ platform_latest = ${common.arduino_core_2_6_3} # FLAGS: DEBUG # # ------------------------------------------------------------------------------ -debug_flags = -D DEBUG=1 -D WLED_DEBUG -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM +debug_flags = -D DEBUG=1 -D WLED_DEBUG -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM #if needed (for memleaks etc) also add; -DDEBUG_ESP_OOM -include "umm_malloc/umm_malloc_cfg.h" +#-DDEBUG_ESP_CORE is not working right now # ------------------------------------------------------------------------------ # FLAGS: ldscript @@ -88,7 +89,7 @@ debug_flags = -D DEBUG=1 -D WLED_DEBUG -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG # TLS_RSA_WITH_AES_256_CBC_SHA / AES256-SHA # This reduces the OTA size with ~45KB, so it's especially useful on low memory boards (512k/1m). # ------------------------------------------------------------------------------ -build_flags = -g -w -DMQTT_MAX_PACKET_SIZE=1024 -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH +build_flags = -g -w -DMQTT_MAX_PACKET_SIZE=1024 -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH -DSECURE_CLIENT=SECURE_CLIENT_BEARSSL -DBEARSSL_SSL_BASIC build_flags_esp8266 = ${common.build_flags} -DESP8266 build_flags_esp32 = ${common.build_flags} -DARDUINO_ARCH_ESP32 @@ -207,3 +208,31 @@ build_type = debug platform = ${common.platform_latest} board_build.ldscript = ${common.ldscript_4m1m} build_flags = ${common.build_flags_esp8266} ${common.debug_flags} + +# ------------------------------------------------------------------------------ +# custom board configurations +# ------------------------------------------------------------------------------ + +[env:custom_LEDPIN_4] +board = d1_mini +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} -D LEDPIN=4 -D IRPIN=5 + +[env:custom_LEDPIN_16] +board = d1_mini +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} -D LEDPIN=16 + +[env:custom_APA102] +board = d1_mini +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} -D USE_APA102 + +[env:custom_WS2801] +board = d1_mini +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} -D USE_WS2801 diff --git a/platformio_override.ini.example b/platformio_override.ini.example index 09c30172..46119f15 100644 --- a/platformio_override.ini.example +++ b/platformio_override.ini.example @@ -14,4 +14,21 @@ build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_BLYNK -D WLED_DISABLE_CRONIXIE -D WLED_DISABLE_HUESYNC - -D WLED_DISABLE_INFRARED \ No newline at end of file + -D WLED_DISABLE_INFRARED +; PIN defines - uncomment and change, if needed: +; -D LEDPIN=2 +; -D BTNPIN=0 +; -D IR_PIN=4 +; -D RLYPIN=12 +; -D RLYMDE=1 +; digital LED strip types - uncomment only one ! - this will disable WS281x / SK681x support +; -D USE_APA102 +; -D USE_WS2801 +; -D USE_LPD8806 +; to drive analog LED strips (aka 5050), uncomment the following +; PWM pins 5,12,13,15 are used with Magic Home LED Controller (default) +; -D WLED_USE_ANALOG_LEDS +; for the H801 controler (PINs 15,13,12,14 (W2 = 04)) uncomment this +; -D WLED_USE_H801 +; and to enable channel 5 for RGBW-CT led strips this +; -D WLED_USE_5CH_LEDS From d0d56c4416203a5bc7991fbe13858656cdbdf69f Mon Sep 17 00:00:00 2001 From: Def3nder Date: Sat, 29 Feb 2020 16:30:15 +0100 Subject: [PATCH 143/232] Travis.CI builds only for default_envs (#739) --- .travis.yml | 14 +++++++++++--- platformio.ini | 20 +++++++++++++++----- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5d976261..882fe54a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,18 +18,26 @@ # it (remove "# " before each line) or use own configuration according to the # Travis CI documentation (see above). # +# * Test the Travis config here: +# < https://config.travis-ci.com/explore > +# language: python python: - - "2.7" -sudo: false + # - "2.7" + - "3.5" +os: linux cache: + bundler: true + ccache: true directories: - "~/.platformio" + - "~/.buildcache" env: - PLATFORMIO_CI_SRC=wled00 install: - pip install -U platformio - platformio update script: - - platformio ci --project-conf=./platformio.ini + # - platformio ci --project-conf=./platformio.ini + - platformio run \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 9128d50e..ed95659e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -5,11 +5,24 @@ src_dir = ./wled00 data_dir = ./wled00/data lib_dir = ./wled00/src +build_cache_dir = ~/.buildcache extra_configs = platformio_override.ini -; Please uncomment one of the 5 lines below to select your board -default_envs = nodemcuv2 +# ------------------------------------------------------------------------------ +# ENVIRONMENTS +# +# Please uncomment one of the lines below to select your board(s) +# ------------------------------------------------------------------------------ + +# Travis CI binaries +default_envs = d1_mini, esp01, esp01_1m, esp32dev + +# Release binaries follow +; default_envs = nodemcuv2, esp01, esp01_1m, esp32dev, custom_WS2801, custom_APA102, custom_LEDPIN_16, custom_LEDPIN_4 + +# Single binaries +; default_envs = nodemcuv2 ; default_envs = esp01 ; default_envs = esp01_1m ; default_envs = esp07 @@ -20,9 +33,6 @@ default_envs = nodemcuv2 ; default_envs = esp8285_4CH_H801 ; default_envs = esp8285_5CH_H801 -# Release binaries follow -; default_envs = nodemcuv2, esp01, esp01_1m, esp32dev, custom_WS2801, custom_APA102, custom_LEDPIN_16, custom_LEDPIN_4 - [common] # ------------------------------------------------------------------------------ # PLATFORM: From 14a5ab6740269a21986cf5971962fe65e85b107b Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sat, 29 Feb 2020 18:24:51 +0100 Subject: [PATCH 144/232] Fixed Chase modes --- wled00/FX.cpp | 135 ++++++++++++++++++++++++---------------------- wled00/wled00.ino | 2 +- 2 files changed, 71 insertions(+), 66 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index eb043522..022b4b23 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -379,7 +379,7 @@ uint16_t WS2812FX::mode_rainbow_cycle(void) { /* * theater chase function */ -uint16_t WS2812FX::theater_chase(uint32_t color1, uint32_t color2, bool dopalette) { +uint16_t WS2812FX::theater_chase(uint32_t color1, uint32_t color2, bool do_palette) { byte gap = 2 + ((255 - SEGMENT.intensity) >> 5); uint32_t cycleTime = 50 + (255 - SEGMENT.speed)*2; uint32_t it = now / cycleTime; @@ -391,7 +391,7 @@ uint16_t WS2812FX::theater_chase(uint32_t color1, uint32_t color2, bool dopalett for(uint16_t i = 0; i < SEGLEN; i++) { if((i % gap) == SEGENV.aux0) { - if (dopalette) + if (do_palette) { setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } else { @@ -690,58 +690,68 @@ uint16_t WS2812FX::mode_android(void) { * color1 = background color * color2 and color3 = colors of two adjacent leds */ -uint16_t WS2812FX::chase(uint32_t color1, uint32_t color2, uint32_t color3, bool dopalette) { +uint16_t WS2812FX::chase(uint32_t color1, uint32_t color2, uint32_t color3, bool do_palette) { uint16_t counter = now * ((SEGMENT.speed >> 2) + 1); uint16_t a = counter * SEGLEN >> 16; + + bool chase_random = (SEGMENT.mode == FX_MODE_CHASE_RANDOM); + if (chase_random) { + 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 = get_random_wheel_index(SEGENV.aux0); + } + color1 = color_wheel(SEGENV.aux0); + } SEGENV.step = a; - uint8_t size = 1 + (SEGMENT.intensity * SEGLEN >> 10); - if (SEGENV.call == 0) {SEGENV.aux0 = 0; SEGENV.aux1 = a;} + // Use intensity setting to vary chase up to 1/2 string length - uint16_t b = (a + size) % SEGLEN; - uint16_t c = (b + size) % SEGLEN; + uint8_t size = 1 + (SEGMENT.intensity * SEGLEN >> 10); - if (dopalette) color1 = color_from_palette(a, true, PALETTE_SOLID_WRAP, 1); + uint16_t b = a + size; //"trail" of chase, filled with color1 + if (b > SEGLEN) b -= SEGLEN; + uint16_t c = b + size; + if (c > SEGLEN) c -= SEGLEN; - setPixelColor(a, color1); - if (SEGENV.aux0 == 0) { // catch the first pixels after color change from "chase random" (because they have the "old" color) - for (uint16_t i = 0; i < a; i++) { - uint32_t color = getPixelColor(0); - setPixelColor(i, color1); + //background + if (do_palette) + { + for(uint16_t i = 0; i < SEGLEN; i++) { + setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); } - SEGENV.aux0 = 1; - } - setPixelColor(b, color2); - setPixelColor(c, color3); + } else fill(color1); - if (a != SEGENV.aux1) { // when speed is too fast, this catches the gaps - if (a > SEGENV.aux1) { - for (uint16_t i = SEGENV.aux1; i <= a; i++) { // sometimes the step-length varies from one to the next call - therefor "<= a" and not "< a" - setPixelColor(i, color1); - uint16_t b1 = (i + size) % SEGLEN; - uint16_t c1 = (b1 + size) % SEGLEN; - setPixelColor(b1, color2); - setPixelColor(c1, color3); - } - } else { - for (uint16_t i = SEGENV.aux1; i <= SEGLEN; i++) { // from last position to the end - setPixelColor(i, color1); - uint16_t b1 = (i + size) % SEGLEN; - uint16_t c1 = (b1 + size) % SEGLEN; - setPixelColor(b1, color2); - setPixelColor(c1, color3); - } - for (uint16_t i = 0; i < a; i++) { // from 0 to the actual position - setPixelColor(i, color1); - uint16_t b1 = (i + size) % SEGLEN; - uint16_t c1 = (b1 + size) % SEGLEN; - setPixelColor(b1, color2); - setPixelColor(c1, color3); - } - SEGENV.step = 0; - SEGENV.aux0 = 0; - } + //if random, fill old background between a and end + if (chase_random) + { + color1 = color_wheel(SEGENV.aux1); + for (uint16_t i = a; i < SEGLEN; i++) + setPixelColor(i, color1); + } + + //fill between points a and b with color2 + if (a < b) + { + for (uint16_t i = a; i < b; i++) + setPixelColor(i, color2); + } else { + for (uint16_t i = a; i < SEGLEN; i++) //fill until end + setPixelColor(i, color2); + for (uint16_t i = 0; i < b; i++) //fill from start until b + setPixelColor(i, color2); + } + + //fill between points b and c with color2 + if (b < c) + { + for (uint16_t i = b; i < c; i++) + setPixelColor(i, color3); + } else { + for (uint16_t i = b; i < SEGLEN; i++) //fill until end + setPixelColor(i, color3); + for (uint16_t i = 0; i < c; i++) //fill from start until c + setPixelColor(i, color3); } - SEGENV.aux1 = ++a; return FRAMETIME; } @@ -751,7 +761,7 @@ uint16_t WS2812FX::chase(uint32_t color1, uint32_t color2, uint32_t color3, bool * Bicolor chase, more primary color. */ uint16_t WS2812FX::mode_chase_color(void) { - return chase(SEGCOLOR(1), SEGCOLOR(0), SEGCOLOR(0), true); + return chase(SEGCOLOR(1), (SEGCOLOR(2)) ? SEGCOLOR(2) : SEGCOLOR(0), SEGCOLOR(0), true); } @@ -759,12 +769,19 @@ uint16_t WS2812FX::mode_chase_color(void) { * Primary running followed by random color. */ uint16_t WS2812FX::mode_chase_random(void) { - if (!SEGENV.allocateData(2)) return mode_static(); //allocation failed - if (SEGENV.call == 0) SEGENV.data[0] = 0; - if (SEGENV.step == 0) { - SEGENV.data[0] = get_random_wheel_index(SEGENV.data[0]); - } - return chase(color_wheel(SEGENV.data[0]), SEGCOLOR(0), SEGCOLOR(0), false); + return chase(SEGCOLOR(1), (SEGCOLOR(2)) ? SEGCOLOR(2) : SEGCOLOR(0), SEGCOLOR(0), false); +} + + +/* + * Primary, secondary running on rainbow. + */ +uint16_t WS2812FX::mode_chase_rainbow(void) { + uint8_t color_sep = 256 / SEGLEN; + uint8_t color_index = SEGENV.call & 0xFF; + uint32_t color = color_wheel(((SEGENV.step * color_sep) + color_index) & 0xFF); + + return chase(color, SEGCOLOR(0), SEGCOLOR(1), false); } @@ -860,18 +877,6 @@ uint16_t WS2812FX::mode_traffic_light(void) { } -/* - * Primary, secondary running on rainbow. - */ -uint16_t WS2812FX::mode_chase_rainbow(void) { - uint8_t color_sep = 256 / SEGLEN; - uint8_t color_index = SEGENV.call & 0xFF; - uint32_t color = color_wheel(((SEGENV.step * color_sep) + color_index) & 0xFF); - - return chase(color, SEGCOLOR(0), SEGCOLOR(1), 0); -} - - /* * Sec flashes running on prim. */ @@ -3161,4 +3166,4 @@ uint16_t WS2812FX::mode_heartbeat(void) { } return FRAMETIME; -} \ No newline at end of file +} diff --git a/wled00/wled00.ino b/wled00/wled00.ino index f214929c..2ce923df 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -119,7 +119,7 @@ #endif //version code in format yymmddb (b = daily build) -#define VERSION 2002252 +#define VERSION 2002291 char versionString[] = "0.9.1"; From 18dad0c72cce118b96126bbf9f622d64f1c94ee0 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sat, 29 Feb 2020 18:42:55 +0100 Subject: [PATCH 145/232] Remove defunct presetApplyCol and presetApplyFx --- wled00/wled00.ino | 2 +- wled00/wled01_eeprom.ino | 34 +++++++++++++++------------------- wled00/wled03_set.ino | 10 ++-------- wled00/wled05_init.ino | 2 +- wled00/wled08_led.ino | 2 +- wled00/wled13_cronixie.ino | 12 ++++++------ 6 files changed, 26 insertions(+), 36 deletions(-) diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 2ce923df..104592b0 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -390,7 +390,7 @@ bool presetCyclingEnabled = false; byte presetCycleMin = 1, presetCycleMax = 5; uint16_t presetCycleTime = 1250; unsigned long presetCycledTime = 0; byte presetCycCurr = presetCycleMin; -bool presetApplyBri = false, presetApplyCol = true, presetApplyFx = true; +bool presetApplyBri = true; bool saveCurrPresetCycConf = false; //realtime diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino index 703785c1..b0da6451 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled01_eeprom.ino @@ -232,8 +232,8 @@ void saveSettingsToEEPROM() EEPROM.write(2208, presetCycleMin); EEPROM.write(2209, presetCycleMax); EEPROM.write(2210, presetApplyBri); - EEPROM.write(2211, presetApplyCol); - EEPROM.write(2212, presetApplyFx); + // was EEPROM.write(2211, presetApplyCol); + // was EEPROM.write(2212, presetApplyFx); saveCurrPresetCycConf = false; } @@ -519,8 +519,8 @@ void loadSettingsFromEEPROM(bool first) presetCycleMin = EEPROM.read(2208); presetCycleMax = EEPROM.read(2209); presetApplyBri = EEPROM.read(2210); - presetApplyCol = EEPROM.read(2211); - presetApplyFx = EEPROM.read(2212); + //was presetApplyCol = EEPROM.read(2211); + //was presetApplyFx = EEPROM.read(2212); } bootPreset = EEPROM.read(389); @@ -583,7 +583,7 @@ void savedToPresets() } } -bool applyPreset(byte index, bool loadBri = true, bool loadCol = true, bool loadFX = true) +bool applyPreset(byte index, bool loadBri = true) { if (index == 255 || index == 0) { @@ -596,22 +596,18 @@ bool applyPreset(byte index, bool loadBri = true, bool loadCol = true, bool load if (EEPROM.read(i) != 1) return false; strip.applyToAllSelected = true; if (loadBri) bri = EEPROM.read(i+1); - if (loadCol) + + for (byte j=0; j<4; j++) { - for (byte j=0; j<4; j++) - { - col[j] = EEPROM.read(i+j+2); - colSec[j] = EEPROM.read(i+j+6); - } - strip.setColor(2, EEPROM.read(i+12), EEPROM.read(i+13), EEPROM.read(i+14), EEPROM.read(i+15)); //tertiary color - } - if (loadFX) - { - effectCurrent = EEPROM.read(i+10); - effectSpeed = EEPROM.read(i+11); - effectIntensity = EEPROM.read(i+16); - effectPalette = EEPROM.read(i+17); + col[j] = EEPROM.read(i+j+2); + colSec[j] = EEPROM.read(i+j+6); } + strip.setColor(2, EEPROM.read(i+12), EEPROM.read(i+13), EEPROM.read(i+14), EEPROM.read(i+15)); //tertiary color + + effectCurrent = EEPROM.read(i+10); + effectSpeed = EEPROM.read(i+11); + effectIntensity = EEPROM.read(i+16); + effectPalette = EEPROM.read(i+17); } else { if (EEPROM.read(i) != 2) return false; strip.applyToAllSelected = false; diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index 0f2cb9ef..69a7d9b7 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -460,18 +460,12 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) pos = req.indexOf("PA="); //apply brightness from preset if (pos > 0) presetApplyBri = (req.charAt(pos+3) != '0'); - pos = req.indexOf("PC="); //apply color from preset - if (pos > 0) presetApplyCol = (req.charAt(pos+3) != '0'); - - pos = req.indexOf("PX="); //apply effects from preset - if (pos > 0) presetApplyFx = (req.charAt(pos+3) != '0'); - pos = req.indexOf("PS="); //saves current in preset if (pos > 0) savePreset(getNumVal(&req, pos)); //apply preset if (updateVal(&req, "PL=", &presetCycCurr, presetCycleMin, presetCycleMax)) { - applyPreset(presetCycCurr, presetApplyBri, presetApplyCol, presetApplyFx); + applyPreset(presetCycCurr, presetApplyBri); } //set brightness @@ -653,7 +647,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) pos = req.indexOf("NB="); if (pos > 0) //sets backlight { - presetApplyFx = (req.charAt(pos+3) != '0'); + cronixieBacklight = (req.charAt(pos+3) != '0'); if (overlayCurrent == 3) strip.setCronixieBacklight(cronixieBacklight); overlayRefreshedTime = 0; } diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index 40e3bef6..29ae0e7a 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -100,7 +100,7 @@ void beginStrip() pinMode(BTNPIN, INPUT_PULLUP); #endif - if (bootPreset>0) applyPreset(bootPreset, turnOnAtBoot, true, true); + if (bootPreset > 0) applyPreset(bootPreset, turnOnAtBoot); colorUpdated(NOTIFIER_CALL_MODE_INIT); //init relay pin diff --git a/wled00/wled08_led.ino b/wled00/wled08_led.ino index 93642fc2..f6e8b4ee 100644 --- a/wled00/wled08_led.ino +++ b/wled00/wled08_led.ino @@ -251,7 +251,7 @@ void handleNightlight() //also handle preset cycle here if (presetCyclingEnabled && (millis() - presetCycledTime > presetCycleTime)) { - applyPreset(presetCycCurr,presetApplyBri,presetApplyCol,presetApplyFx); + applyPreset(presetCycCurr,presetApplyBri); presetCycCurr++; if (presetCycCurr > presetCycleMax) presetCycCurr = presetCycleMin; if (presetCycCurr > 25) presetCycCurr = 1; colorUpdated(NOTIFIER_CALL_MODE_PRESET_CYCLE); diff --git a/wled00/wled13_cronixie.ino b/wled00/wled13_cronixie.ino index de89aede..897c94f9 100644 --- a/wled00/wled13_cronixie.ino +++ b/wled00/wled13_cronixie.ino @@ -97,8 +97,8 @@ void setCronixie() case '-': dP[i] = 11; break; case 'r': dP[i] = random(1,7); break; //random btw. 1-6 case 'R': dP[i] = random(0,10); break; //random btw. 0-9 - case 't': break; //Test upw. - case 'T': break; //Test dnw. + //case 't': break; //Test upw. + //case 'T': break; //Test dnw. case 'b': dP[i] = 14 + getSameCodeLength('b',i,cronixieDisplay); i = i+dP[i]-14; break; case 'B': dP[i] = 14 + getSameCodeLength('B',i,cronixieDisplay); i = i+dP[i]-14; break; case 'h': dP[i] = 70 + getSameCodeLength('h',i,cronixieDisplay); i = i+dP[i]-70; break; @@ -113,8 +113,8 @@ void setCronixie() case 'y': dP[i] = 86 + getSameCodeLength('y',i,cronixieDisplay); i = i+dP[i]-86; break; case 'I': dP[i] = 39 + getSameCodeLength('I',i,cronixieDisplay); i = i+dP[i]-39; break; //Month. Don't ask me why month and minute both start with M. case 'i': dP[i] = 89 + getSameCodeLength('i',i,cronixieDisplay); i = i+dP[i]-89; break; - case 'W': break; - case 'w': break; + //case 'W': break; + //case 'w': break; case 'D': dP[i] = 43 + getSameCodeLength('D',i,cronixieDisplay); i = i+dP[i]-43; break; case 'd': dP[i] = 93 + getSameCodeLength('d',i,cronixieDisplay); i = i+dP[i]-93; break; case '0': dP[i] = 0; break; @@ -127,8 +127,8 @@ void setCronixie() case '7': dP[i] = 7; break; case '8': dP[i] = 8; break; case '9': dP[i] = 9; break; - case 'V': break; //user var0 - case 'v': break; //user var1 + //case 'V': break; //user var0 + //case 'v': break; //user var1 } } DEBUG_PRINT("result "); From 79da716a449a189b9fca7e51abf2faedd472783c Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sat, 29 Feb 2020 19:52:47 +0100 Subject: [PATCH 146/232] Split esp01_1m env into 1m_full and 1m_ota (closes #740 ) --- platformio.ini | 19 +++++++++++++------ wled00/wled00.ino | 4 +--- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/platformio.ini b/platformio.ini index ed95659e..c4e79a71 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,15 +16,16 @@ extra_configs = # ------------------------------------------------------------------------------ # Travis CI binaries -default_envs = d1_mini, esp01, esp01_1m, esp32dev +default_envs = d1_mini, esp01, esp01_1m_ota, esp32dev # Release binaries follow -; default_envs = nodemcuv2, esp01, esp01_1m, esp32dev, custom_WS2801, custom_APA102, custom_LEDPIN_16, custom_LEDPIN_4 +; default_envs = nodemcuv2, esp01, esp01_1m_ota, esp01_1m_full, esp32dev, custom_WS2801, custom_APA102, custom_LEDPIN_16, custom_LEDPIN_4 # Single binaries ; default_envs = nodemcuv2 ; default_envs = esp01 -; default_envs = esp01_1m +; default_envs = esp01_1m_ota +; default_envs = esp01_1m_full ; default_envs = esp07 ; default_envs = d1_mini ; default_envs = d1_mini_debug @@ -162,13 +163,19 @@ board = esp01 platform = ${common.platform_latest} board_build.ldscript = ${common.ldscript_512k} build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK - -D WLED_DISABLE_CRONIXIE -D WLED_DISABLE_HUESYNC -D WLED_DISABLE_INFRARED + -D WLED_DISABLE_HUESYNC -D WLED_DISABLE_INFRARED -[env:esp01_1m] +[env:esp01_1m_ota] board = esp01_1m platform = ${common.platform_latest} board_build.ldscript = ${common.ldscript_1m0m} -build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_BLYNK -D WLED_DISABLE_CRONIXIE -D WLED_DISABLE_INFRARED +build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK -D WLED_DISABLE_HUESYNC -D WLED_DISABLE_INFRARED + +[env:esp01_1m_full] +board = esp01_1m +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_1m0m} +build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA [env:esp07] board = esp07 diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 104592b0..fda0a110 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -119,7 +119,7 @@ #endif //version code in format yymmddb (b = daily build) -#define VERSION 2002291 +#define VERSION 2002292 char versionString[] = "0.9.1"; @@ -541,8 +541,6 @@ bool oappendi(int i) //boot starts here void setup() { - - wledInit(); } From f28502514f39546dcc00e20e5bb86a5446261b28 Mon Sep 17 00:00:00 2001 From: Def3nder Date: Sun, 1 Mar 2020 12:25:44 +0100 Subject: [PATCH 147/232] Remember lastColor and last Bri for Solid RGBW (#743) --- wled00/FX_fcn.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index ee0ca73a..2392392d 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -863,7 +863,9 @@ void WS2812FX::setRgbwPwm(void) { } #else bus->SetRgbwPwm(color.R * b / 255, color.G * b / 255, color.B * b / 255, color.W * b / 255); - #endif + #endif + _analogLastColor = color; + _analogLastBri = b; } #else void WS2812FX::setRgbwPwm() {} From b804101c24b4b1d72516f3da2fdef44050e65728 Mon Sep 17 00:00:00 2001 From: Def3nder Date: Mon, 2 Mar 2020 12:24:10 +0100 Subject: [PATCH 148/232] lift NeoPixelBus library to 2.5.7 (#747) --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index c4e79a71..0ef2630c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -134,7 +134,7 @@ lib_extra_dirs = lib_compat_mode = strict lib_deps = FastLED@3.3.2 - NeoPixelBus@2.5.6 + NeoPixelBus@2.5.7 ESPAsyncTCP@1.2.0 ESPAsyncUDP@697c75a025 AsyncTCP@1.0.3 From b86f58befe173590438c0aa427837fe872425d7d Mon Sep 17 00:00:00 2001 From: Def3nder Date: Mon, 2 Mar 2020 12:41:14 +0100 Subject: [PATCH 149/232] IR remotes can disable NightLight with "OFF" (#745) * IR can disable Nightlight with "OFF" --- wled00/wled08_led.ino | 1 - wled00/wled20_ir.ino | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/wled08_led.ino b/wled00/wled08_led.ino index f6e8b4ee..63009f98 100644 --- a/wled00/wled08_led.ino +++ b/wled00/wled08_led.ino @@ -72,7 +72,6 @@ bool colorChanged() { if (col[i] != colIT[i]) return true; if (colSec[i] != colSecIT[i]) return true; - //if (col[i] != colNlT[i]) return true; (this effectively made the ESP send out sync packets every time colorUpdated() is called, even if nothing changed) } if (bri != briIT) return true; return false; diff --git a/wled00/wled20_ir.ino b/wled00/wled20_ir.ino index c65a4c58..3cd604b2 100644 --- a/wled00/wled20_ir.ino +++ b/wled00/wled20_ir.ino @@ -91,6 +91,7 @@ void decodeIR(uint32_t code) default: return; } } + if (nightlightActive && bri == 0) nightlightActive = false; colorUpdated(NOTIFIER_CALL_MODE_BUTTON); //for notifier, IR is considered a button input //code <= 0xF70000 also invalid } From 267887908ef47851a7b2fddf19bf67213cd62952 Mon Sep 17 00:00:00 2001 From: Def3nder Date: Mon, 2 Mar 2020 13:31:34 +0100 Subject: [PATCH 150/232] New IRremote functions (#746) * Switch to strip.getPaletteCount() * fixed color conversion errors Co-authored-by: Aircoookie --- wled00/wled20_ir.ino | 89 +++++++++++++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 25 deletions(-) diff --git a/wled00/wled20_ir.ino b/wled00/wled20_ir.ino index 3cd604b2..64c131c2 100644 --- a/wled00/wled20_ir.ino +++ b/wled00/wled20_ir.ino @@ -35,14 +35,55 @@ bool decodeIRCustom(uint32_t code) //relatively change brightness, minumum A=5 -void relativeChange(byte* property, int8_t amount, byte lowerBoundary =0) +void relativeChange(byte* property, int8_t amount, byte lowerBoundary = 0, byte higherBoundary = 0xFF) { int16_t new_val = (int16_t) *property + amount; - if (new_val > 0xFF) new_val = 0xFF; + if (new_val > higherBoundary) new_val = higherBoundary; else if (new_val < lowerBoundary) new_val = lowerBoundary; - *property = new_val; + *property = (byte)constrain(new_val,0.1,255.1); } +void changeEffectSpeed(int8_t amount) +{ + if (effectCurrent != 0) { + int16_t new_val = (int16_t) effectSpeed + amount; + effectSpeed = (byte)constrain(new_val,0.1,255.1); + } else { // if Effect == "solid Color", change the hue of the primary color + CRGB fastled_col; + fastled_col.red = col[0]; + fastled_col.green = col[1]; + fastled_col.blue = col[2]; + CHSV prim_hsv = rgb2hsv_approximate(fastled_col); + int16_t new_val = (int16_t) prim_hsv.h + amount; + if (new_val > 255) new_val -= 255; // roll-over if bigger than 255 + if (new_val < 0) new_val += 255; // roll-over if smaller than 0 + prim_hsv.h = (byte)new_val; + hsv2rgb_rainbow(prim_hsv, fastled_col); + col[0] = fastled_col.red; + col[1] = fastled_col.green; + col[2] = fastled_col.blue; + } +} + +void changeEffectIntensity(int8_t amount) +{ + if (effectCurrent != 0) { + int16_t new_val = (int16_t) effectIntensity + amount; + effectIntensity = (byte)constrain(new_val,0.1,255.1); + } else { // if Effect == "solid Color", change the saturation of the primary color + CRGB fastled_col; + fastled_col.red = col[0]; + fastled_col.green = col[1]; + fastled_col.blue = col[2]; + CHSV prim_hsv = rgb2hsv_approximate(fastled_col); + int16_t new_val = (int16_t) prim_hsv.s + amount; + prim_hsv.s = (byte)constrain(new_val,0.1,255.1); // constrain to 0-255 + hsv2rgb_rainbow(prim_hsv, fastled_col); + col[0] = fastled_col.red; + col[1] = fastled_col.green; + col[2] = fastled_col.blue; + } +} void decodeIR(uint32_t code) { @@ -242,10 +283,10 @@ void decodeIR40(uint32_t code) case IR40_W50 : bri = 127; break; case IR40_W75 : bri = 191; break; case IR40_W100 : bri = 255; break; - case IR40_QUICK : relativeChange(&effectSpeed, 10); break; - case IR40_SLOW : relativeChange(&effectSpeed, -10, 5); break; - case IR40_JUMP7 : relativeChange(&effectIntensity, 10); break; - case IR40_AUTO : relativeChange(&effectIntensity, -10, 5); break; + case IR40_QUICK : changeEffectSpeed( 16); break; + case IR40_SLOW : changeEffectSpeed(-16); break; + case IR40_JUMP7 : changeEffectIntensity( 16); break; + case IR40_AUTO : changeEffectIntensity(-16); break; case IR40_JUMP3 : if (!applyPreset(1)) { effectCurrent = FX_MODE_STATIC; effectPalette = 0; } break; case IR40_FADE3 : if (!applyPreset(2)) { effectCurrent = FX_MODE_BREATH; effectPalette = 0; } break; case IR40_FADE7 : if (!applyPreset(3)) { effectCurrent = FX_MODE_FIRE_FLICKER; effectPalette = 0; } break; @@ -293,14 +334,14 @@ void decodeIR44(uint32_t code) case IR44_COLDWHITE2 : { if (useRGBW) { colorFromUint32(COLOR2_COLDWHITE2); effectCurrent = 0; } else colorFromUint24(COLOR_COLDWHITE2); } break; - case IR44_REDPLUS : relativeChange(&effectCurrent, 1); break; + case IR44_REDPLUS : relativeChange(&effectCurrent, 1, 0, MODE_COUNT); break; case IR44_REDMINUS : relativeChange(&effectCurrent, -1, 0); break; - case IR44_GREENPLUS : relativeChange(&effectPalette, 1); break; + case IR44_GREENPLUS : relativeChange(&effectPalette, 1, 0, strip.getPaletteCount() -1); break; case IR44_GREENMINUS : relativeChange(&effectPalette, -1, 0); break; - case IR44_BLUEPLUS : relativeChange(&effectIntensity, 10); break; - case IR44_BLUEMINUS : relativeChange(&effectIntensity, -10, 5); break; - case IR44_QUICK : relativeChange(&effectSpeed, 10); break; - case IR44_SLOW : relativeChange(&effectSpeed, -10, 5); break; + case IR44_BLUEPLUS : changeEffectIntensity( 16); break; + case IR44_BLUEMINUS : changeEffectIntensity(-16); break; + case IR44_QUICK : changeEffectSpeed( 16); break; + case IR44_SLOW : changeEffectSpeed(-16); break; case IR44_DIY1 : if (!applyPreset(1)) { effectCurrent = FX_MODE_STATIC; effectPalette = 0; } break; case IR44_DIY2 : if (!applyPreset(2)) { effectCurrent = FX_MODE_BREATH; effectPalette = 0; } break; case IR44_DIY3 : if (!applyPreset(3)) { effectCurrent = FX_MODE_FIRE_FLICKER; effectPalette = 0; } break; @@ -319,7 +360,7 @@ void decodeIR44(uint32_t code) void decodeIR21(uint32_t code) { - switch (code) { + switch (code) { case IR21_BRIGHTER: relativeChange(&bri, 10); break; case IR21_DARKER: relativeChange(&bri, -10, 5); break; case IR21_OFF: briLast = bri; bri = 0; break; @@ -342,20 +383,19 @@ void decodeIR21(uint32_t code) case IR21_FADE: if (!applyPreset(3)) { effectCurrent = FX_MODE_BREATH; effectPalette = 0; } break; case IR21_SMOOTH: if (!applyPreset(4)) { effectCurrent = FX_MODE_RAINBOW; effectPalette = 0; } break; default: return; - } - lastValidCode = code; + } + lastValidCode = code; } void decodeIR6(uint32_t code) { switch (code) { - case IR6_POWER: toggleOnOff(); break; - case IR6_CHANNEL_UP: relativeChange(&bri, 10); break; - case IR6_CHANNEL_DOWN: relativeChange(&bri, -10, 5); break; - case IR6_VOLUME_UP: /* next effect */ relativeChange(&effectCurrent, 1); break; - case IR6_VOLUME_DOWN: - /* next palette */ - relativeChange(&effectPalette, 1); + case IR6_POWER: toggleOnOff(); break; + case IR6_CHANNEL_UP: relativeChange(&bri, 10); break; + case IR6_CHANNEL_DOWN: relativeChange(&bri, -10, 5); break; + case IR6_VOLUME_UP: relativeChange(&effectCurrent, 1, 0, MODE_COUNT); break; // next effect + case IR6_VOLUME_DOWN: // next palette + relativeChange(&effectPalette, 1, 0, strip.getPaletteCount() -1); switch(lastIR6ColourIdx) { case 0: colorFromUint32(COLOR_RED); break; case 1: colorFromUint32(COLOR_REDDISH); break; @@ -373,8 +413,7 @@ void decodeIR6(uint32_t code) default: break; } lastIR6ColourIdx++; - if(lastIR6ColourIdx > 12) lastIR6ColourIdx = 0; - break; + if(lastIR6ColourIdx > 12) lastIR6ColourIdx = 0; break; case IR6_MUTE: effectCurrent = 0; effectPalette = 0; colorFromUint32(COLOR_WHITE); bri=255; break; } lastValidCode = code; From 89fa053310e149cb1a6caf1a2e2e2346fe03601d Mon Sep 17 00:00:00 2001 From: cschwinne Date: Tue, 3 Mar 2020 17:53:47 +0100 Subject: [PATCH 151/232] Add "np" API option to not commit Re-add IRremote flags to platformio.ini --- platformio.ini | 8 ++++++++ wled00/wled00.ino | 2 +- wled00/wled01_eeprom.ino | 8 ++++---- wled00/wled03_set.ino | 11 +++++++++-- wled00/wled19_json.ino | 5 ++++- 5 files changed, 26 insertions(+), 8 deletions(-) diff --git a/platformio.ini b/platformio.ini index c4e79a71..31cd30bd 100644 --- a/platformio.ini +++ b/platformio.ini @@ -102,6 +102,14 @@ debug_flags = -D DEBUG=1 -D WLED_DEBUG -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT # ------------------------------------------------------------------------------ build_flags = -g -w -DMQTT_MAX_PACKET_SIZE=1024 -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH -DSECURE_CLIENT=SECURE_CLIENT_BEARSSL -DBEARSSL_SSL_BASIC + #build_flags for the IRremoteESP8266 library (enabled decoders have to appear here) + -D _IR_ENABLE_DEFAULT_=false + -D DECODE_HASH=true + -D DECODE_NEC=true + -D DECODE_SONY=true + -D DECODE_SAMSUNG=true + -D DECODE_LG=true + build_flags_esp8266 = ${common.build_flags} -DESP8266 build_flags_esp32 = ${common.build_flags} -DARDUINO_ARCH_ESP32 diff --git a/wled00/wled00.ino b/wled00/wled00.ino index fda0a110..325cc73e 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -119,7 +119,7 @@ #endif //version code in format yymmddb (b = daily build) -#define VERSION 2002292 +#define VERSION 2003031 char versionString[] = "0.9.1"; diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino index b0da6451..acfc8dce 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled01_eeprom.ino @@ -621,7 +621,7 @@ bool applyPreset(byte index, bool loadBri = true) return true; } -void savePreset(byte index) +void savePreset(byte index, bool persist = true) { if (index > 16) return; if (index < 1) {saveSettingsToEEPROM();return;} @@ -653,7 +653,7 @@ void savePreset(byte index) memcpy(EEPROM.getDataPtr() +i+2, seg, 240); } - commit(); + if (persist) commit(); savedToPresets(); currentPreset = index; isPreset = true; @@ -689,7 +689,7 @@ void applyMacro(byte index) } -void saveMacro(byte index, String mc, bool sing=true) //only commit on single save, not in settings +void saveMacro(byte index, String mc, bool persist = true) //only commit on single save, not in settings { index-=1; if (index > 15) return; @@ -698,5 +698,5 @@ void saveMacro(byte index, String mc, bool sing=true) //only commit on single sa { EEPROM.write(i, mc.charAt(i-s)); } - if (sing) commit(); + if (persist) commit(); } diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index 69a7d9b7..d3897149 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -372,6 +372,13 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) DEBUG_PRINT("API req: "); DEBUG_PRINTLN(req); + //write presets and macros saved to flash directly? + bool persistSaves = true; + pos = req.indexOf("NP"); + if (pos > 0) { + persistSaves = false; + } + //save macro, requires &MS=() format pos = req.indexOf("&MS="); if (pos > 0) { @@ -381,7 +388,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) int en = req.indexOf(')'); String mc = req.substring(pos); if (en > 0) mc = req.substring(pos, en); - saveMacro(i, mc); + saveMacro(i, mc, persistSaves); } pos = req.indexOf("IN"); @@ -461,7 +468,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) if (pos > 0) presetApplyBri = (req.charAt(pos+3) != '0'); pos = req.indexOf("PS="); //saves current in preset - if (pos > 0) savePreset(getNumVal(&req, pos)); + if (pos > 0) savePreset(getNumVal(&req, pos), persistSaves); //apply preset if (updateVal(&req, "PL=", &presetCycCurr, presetCycleMin, presetCycleMax)) { diff --git a/wled00/wled19_json.ino b/wled00/wled19_json.ino index 4d935973..237912d6 100644 --- a/wled00/wled19_json.ino +++ b/wled00/wled19_json.ino @@ -156,8 +156,11 @@ bool deserializeState(JsonObject root) colorUpdated(noNotification ? NOTIFIER_CALL_MODE_NO_NOTIFY : NOTIFIER_CALL_MODE_DIRECT_CHANGE); + //write presets to flash directly? + bool persistSaves = !(root["np"] | false); + ps = root["psave"] | -1; - if (ps >= 0) savePreset(ps); + if (ps >= 0) savePreset(ps, persistSaves); return stateResponse; } From 4f34cfb6549e79b37b8d1a5eae20760a2e8d2bb4 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Wed, 4 Mar 2020 11:45:25 +0100 Subject: [PATCH 152/232] Add custom32_LEDPIN_16 environment (from #748 ) --- platformio.ini | 10 +++++++++- wled00/const.h | 12 +++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/platformio.ini b/platformio.ini index 6e1bae31..c1663c4e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -19,7 +19,7 @@ extra_configs = default_envs = d1_mini, esp01, esp01_1m_ota, esp32dev # Release binaries follow -; default_envs = nodemcuv2, esp01, esp01_1m_ota, esp01_1m_full, esp32dev, custom_WS2801, custom_APA102, custom_LEDPIN_16, custom_LEDPIN_4 +; default_envs = nodemcuv2, esp01, esp01_1m_ota, esp01_1m_full, esp32dev, custom_WS2801, custom_APA102, custom_LEDPIN_16, custom_LEDPIN_4, custom32_LEDPIN_16 # Single binaries ; default_envs = nodemcuv2 @@ -261,3 +261,11 @@ board = d1_mini platform = ${common.platform_latest} board_build.ldscript = ${common.ldscript_4m1m} build_flags = ${common.build_flags_esp8266} -D USE_WS2801 + +[env:custom32_LEDPIN_16] +board = esp32dev +platform = espressif32@1.11.2 +build_flags = ${common.build_flags_esp32} -D LEDPIN=16 +lib_ignore = + ESPAsyncTCP + ESPAsyncUDP diff --git a/wled00/const.h b/wled00/const.h index b54215ca..21be1953 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -49,17 +49,19 @@ #define DMX_MODE_MULTIPLE_DRGB 5 //every LED is addressed with its own RGB and share a master dimmer (ledCount * 3 + 1 channels) //Light capability byte (unused) -#define TYPE_WS2812_RGB 0 -#define TYPE_SK6812_RGBW 1 -#define TYPE_WS2812_WWA 2 //amber + warm + cold white -#define TYPE_APA102 3 -#define TYPE_LPD8806 4 +#define TYPE_NONE 0 //light is not configured +#define TYPE_RESERVED 1 //unused. Might indicate a "virtual" light +#define TYPE_WS2812_RGB 2 +#define TYPE_SK6812_RGBW 3 +#define TYPE_WS2812_WWA 4 //amber + warm + cold white #define TYPE_WS2801 5 #define TYPE_ANALOG_1CH 6 //single channel PWM. Uses value of brightest RGBW channel #define TYPE_ANALOG_2CH 7 //analog WW + CW #define TYPE_ANALOG_3CH 8 //analog RGB #define TYPE_ANALOG_4CH 9 //analog RGBW #define TYPE_ANALOG_5CH 10 //analog RGB + WW + CW +#define TYPE_APA102 11 +#define TYPE_LPD8806 12 //Hue error codes #define HUE_ERROR_INACTIVE 0 From ed3234d949294221d7a064cd2ec09b1a0e218303 Mon Sep 17 00:00:00 2001 From: srg74 Date: Wed, 4 Mar 2020 23:01:00 -0500 Subject: [PATCH 153/232] Update UserMod Updating UserMod as per request from discourse forum user. He want to use with ESP32 also as QuinLED board support to types - ESP8266 ans ESP32. Removed .txt file as it create an extra step in setup. --- usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.txt | 3 +-- usermods/QuinLED_Dig_Uno_Temp_MQTT/wled00.txt | 8 -------- .../wled06_usermod.ino | 19 +++++++++++++++---- 3 files changed, 16 insertions(+), 14 deletions(-) delete mode 100644 usermods/QuinLED_Dig_Uno_Temp_MQTT/wled00.txt diff --git a/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.txt b/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.txt index 61287336..eb8da7ee 100644 --- a/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.txt +++ b/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.txt @@ -1,8 +1,7 @@ -These files allow WLED 0.8.6 to report the temp sensor on the Quinled board to MQTT. I use it to report the board temp to Home Assistant via MQTT, so it will send notifications if something happens and the board start to heat up. +These files allow WLED 0.9.1 to report the temp sensor on the Quinled board to MQTT. I use it to report the board temp to Home Assistant via MQTT, so it will send notifications if something happens and the board start to heat up. This code uses Aircookie's WLED software. It has a premade file for user modifications. I use it to publish the temperature from the dallas temperature sensor on the Quinled board. The entries for the top of the WLED00 file, initializes the required libraries, and variables for the sensor. The .ino file waits for 60 seconds, and checks to see if the MQTT server is connected (thanks Aircoookie). It then poles the sensor, and published it using the MQTT service already running, using the main topic programmed in the WLED UI. To install: -Add the entries in the WLED00 file to the top of the same file from Aircoookies WLED. Replace the WLED06_usermod.ino file in Aircoookies WLED folder. diff --git a/usermods/QuinLED_Dig_Uno_Temp_MQTT/wled00.txt b/usermods/QuinLED_Dig_Uno_Temp_MQTT/wled00.txt deleted file mode 100644 index 661a7e24..00000000 --- a/usermods/QuinLED_Dig_Uno_Temp_MQTT/wled00.txt +++ /dev/null @@ -1,8 +0,0 @@ -//Intiating code for QuinLED Dig-Uno temp sensor -//Uncomment Celsius if that is your prefered temperature scale -#include -OneWire oneWire(14); -DallasTemperature sensors(&oneWire); -long temptimer = millis(); -long lastMeasure = 0; -//#define Celsius diff --git a/usermods/QuinLED_Dig_Uno_Temp_MQTT/wled06_usermod.ino b/usermods/QuinLED_Dig_Uno_Temp_MQTT/wled06_usermod.ino index 1309a4f8..d4b77ccc 100644 --- a/usermods/QuinLED_Dig_Uno_Temp_MQTT/wled06_usermod.ino +++ b/usermods/QuinLED_Dig_Uno_Temp_MQTT/wled06_usermod.ino @@ -1,8 +1,19 @@ -//starts Dallas Temp service on boot +//Intiating code for QuinLED Dig-Uno temp sensor +//Uncomment Celsius if that is your prefered temperature scale +#include //Dallastemperature sensor +#ifdef ARDUINO_ARCH_ESP32 //ESP32 boards +OneWire oneWire(18); +#else //ESP8266 boards +OneWire oneWire(14); +#endif +DallasTemperature sensor(&oneWire); +long temptimer = millis(); +long lastMeasure = 0; +#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit void userSetup() { // Start the DS18B20 sensor - sensors.begin(); + sensor.begin(); } //gets called every time WiFi is (re-)connected. Initialize own network interfaces here @@ -21,11 +32,11 @@ void userLoop() //Check if MQTT Connected, otherwise it will crash the 8266 if (mqtt != nullptr){ - sensors.requestTemperatures(); + sensor.requestTemperatures(); //Gets prefered temperature scale based on selection in definitions section #ifdef Celsius - float board_temperature = sensors.getTempCByIndex(0); + float board_temperature = sensor.getTempCByIndex(0); #else float board_temperature = sensors.getTempFByIndex(0); #endif From 8d669b12b6dc4edc0a0ba63cf0f3ef8ed0bf70d5 Mon Sep 17 00:00:00 2001 From: srg74 Date: Wed, 4 Mar 2020 23:15:12 -0500 Subject: [PATCH 154/232] Truing to fix failed build --- platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index a8d21c84..91bdd867 100644 --- a/platformio.ini +++ b/platformio.ini @@ -152,8 +152,8 @@ lib_deps = #For use SSD1306 OLED display uncomment following #U8g2@~2.27.2 #For Dallas sensor uncomment following 2 lines - #DallasTemperature@~3.8.0 - #OneWire@~2.3.5 + DallasTemperature@~3.8.0 + OneWire@~2.3.5 lib_ignore = AsyncTCP From 453f4b549b649bab18583bbefdd454d5316985dc Mon Sep 17 00:00:00 2001 From: Aircoookie Date: Thu, 5 Mar 2020 09:39:43 +0100 Subject: [PATCH 155/232] Trying to fix failed build (#756) --- platformio.ini | 11 +++++++++-- usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.txt | 3 +-- usermods/QuinLED_Dig_Uno_Temp_MQTT/wled00.txt | 8 -------- .../wled06_usermod.ino | 19 +++++++++++++++---- .../wled06_usermod.ino | 5 ++--- 5 files changed, 27 insertions(+), 19 deletions(-) delete mode 100644 usermods/QuinLED_Dig_Uno_Temp_MQTT/wled00.txt diff --git a/platformio.ini b/platformio.ini index c1663c4e..91bdd867 100644 --- a/platformio.ini +++ b/platformio.ini @@ -28,6 +28,7 @@ default_envs = d1_mini, esp01, esp01_1m_ota, esp32dev ; default_envs = esp01_1m_full ; default_envs = esp07 ; default_envs = d1_mini +; default_envs = heltec_wifi_kit_8 ; default_envs = d1_mini_debug ; default_envs = esp32dev ; default_envs = esp8285_4CH_MagicHome @@ -151,8 +152,8 @@ lib_deps = #For use SSD1306 OLED display uncomment following #U8g2@~2.27.2 #For Dallas sensor uncomment following 2 lines - #DallasTemperature@~3.8.0 - #OneWire@~2.3.5 + DallasTemperature@~3.8.0 + OneWire@~2.3.5 lib_ignore = AsyncTCP @@ -197,6 +198,12 @@ platform = ${common.platform_latest} board_build.ldscript = ${common.ldscript_4m1m} build_flags = ${common.build_flags_esp8266} +[env:heltec_wifi_kit_8] +board = d1_mini +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} + [env:esp32dev] board = esp32dev platform = espressif32@1.11.2 diff --git a/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.txt b/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.txt index 61287336..eb8da7ee 100644 --- a/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.txt +++ b/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.txt @@ -1,8 +1,7 @@ -These files allow WLED 0.8.6 to report the temp sensor on the Quinled board to MQTT. I use it to report the board temp to Home Assistant via MQTT, so it will send notifications if something happens and the board start to heat up. +These files allow WLED 0.9.1 to report the temp sensor on the Quinled board to MQTT. I use it to report the board temp to Home Assistant via MQTT, so it will send notifications if something happens and the board start to heat up. This code uses Aircookie's WLED software. It has a premade file for user modifications. I use it to publish the temperature from the dallas temperature sensor on the Quinled board. The entries for the top of the WLED00 file, initializes the required libraries, and variables for the sensor. The .ino file waits for 60 seconds, and checks to see if the MQTT server is connected (thanks Aircoookie). It then poles the sensor, and published it using the MQTT service already running, using the main topic programmed in the WLED UI. To install: -Add the entries in the WLED00 file to the top of the same file from Aircoookies WLED. Replace the WLED06_usermod.ino file in Aircoookies WLED folder. diff --git a/usermods/QuinLED_Dig_Uno_Temp_MQTT/wled00.txt b/usermods/QuinLED_Dig_Uno_Temp_MQTT/wled00.txt deleted file mode 100644 index 661a7e24..00000000 --- a/usermods/QuinLED_Dig_Uno_Temp_MQTT/wled00.txt +++ /dev/null @@ -1,8 +0,0 @@ -//Intiating code for QuinLED Dig-Uno temp sensor -//Uncomment Celsius if that is your prefered temperature scale -#include -OneWire oneWire(14); -DallasTemperature sensors(&oneWire); -long temptimer = millis(); -long lastMeasure = 0; -//#define Celsius diff --git a/usermods/QuinLED_Dig_Uno_Temp_MQTT/wled06_usermod.ino b/usermods/QuinLED_Dig_Uno_Temp_MQTT/wled06_usermod.ino index 1309a4f8..d4b77ccc 100644 --- a/usermods/QuinLED_Dig_Uno_Temp_MQTT/wled06_usermod.ino +++ b/usermods/QuinLED_Dig_Uno_Temp_MQTT/wled06_usermod.ino @@ -1,8 +1,19 @@ -//starts Dallas Temp service on boot +//Intiating code for QuinLED Dig-Uno temp sensor +//Uncomment Celsius if that is your prefered temperature scale +#include //Dallastemperature sensor +#ifdef ARDUINO_ARCH_ESP32 //ESP32 boards +OneWire oneWire(18); +#else //ESP8266 boards +OneWire oneWire(14); +#endif +DallasTemperature sensor(&oneWire); +long temptimer = millis(); +long lastMeasure = 0; +#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit void userSetup() { // Start the DS18B20 sensor - sensors.begin(); + sensor.begin(); } //gets called every time WiFi is (re-)connected. Initialize own network interfaces here @@ -21,11 +32,11 @@ void userLoop() //Check if MQTT Connected, otherwise it will crash the 8266 if (mqtt != nullptr){ - sensors.requestTemperatures(); + sensor.requestTemperatures(); //Gets prefered temperature scale based on selection in definitions section #ifdef Celsius - float board_temperature = sensors.getTempCByIndex(0); + float board_temperature = sensor.getTempCByIndex(0); #else float board_temperature = sensors.getTempFByIndex(0); #endif diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino index 58a4e0e6..d75cf0a7 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino @@ -31,9 +31,8 @@ U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_ // --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3" //U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA // --> Third choise of Heltec WiFi-Kit-8 OLED 128X32 0.91" -//U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_RESET, U8X8_PIN_SCL, U8X8_PIN_SDA); // Constracor for Heltec WiFi-Kit-8 -// gets called once at boot. Do all initialization that doesn't depend on -// network here +//U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_RESET, U8X8_PIN_SCL, U8X8_PIN_SDA); // Constructor for Heltec WiFi-Kit-8 +// gets called once at boot. Do all initialization that doesn't depend on network here void userSetup() { sensor.begin(); //Start Dallas temperature sensor u8x8.begin(); From 0d355dbf35bd382e28ae675c03094c927269c68a Mon Sep 17 00:00:00 2001 From: jwingefeld Date: Fri, 6 Mar 2020 23:45:45 +0100 Subject: [PATCH 156/232] Usermod: Project Cars rpm indicator and shift lights (#759) * project cars shiftlight usermod * added readme * rephrased stuff in readme.md because reasons Co-authored-by: Jan --- usermods/project_cars_shiftlight/readme.md | 23 +++++ .../wled06_usermod.ino | 96 +++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 usermods/project_cars_shiftlight/readme.md create mode 100644 usermods/project_cars_shiftlight/wled06_usermod.ino diff --git a/usermods/project_cars_shiftlight/readme.md b/usermods/project_cars_shiftlight/readme.md new file mode 100644 index 00000000..4490a2ba --- /dev/null +++ b/usermods/project_cars_shiftlight/readme.md @@ -0,0 +1,23 @@ +### Shift Light for Project Cars + +Turn your WLED lights into a rev light and shift indicator for Project Cars. + +It is pretty straight forward to use. + +1. Make sure, your WLED device and your PC/console are on the same network and can talk to each other + +2. Go to the gameplay settings menu in PCARS and enable UDP. There are 9 numbers you can choose from. This is the refresh rate. The lower the number, the better. But you might run into problems at faster rates. + +| Number | Updates/Second | +| ------ | -------------- | +| 1 | 60 | +| 2 | 50 | +| 3 | 40 | +| 4 | 30 | +| 5 | 20 | +| 6 | 15 | +| 7 | 10 | +| 8 | 05 | +| 9 | 1 | + +3. once you enter a race, WLED should automatically shift to PCARS mode. Done. diff --git a/usermods/project_cars_shiftlight/wled06_usermod.ino b/usermods/project_cars_shiftlight/wled06_usermod.ino new file mode 100644 index 00000000..b00c2946 --- /dev/null +++ b/usermods/project_cars_shiftlight/wled06_usermod.ino @@ -0,0 +1,96 @@ +/* + * Car rev display and shift indicator for Project Cars + * + * This works via the UDP telemetry function. You'll need to enable it in the settings of the game. + * I've had good results with settings around 5 (20 fps). + * + */ +const uint8_t PCARS_dimcolor = 20; +WiFiUDP UDP; +const unsigned int PCARS_localUdpPort = 5606; // local port to listen on +char PCARS_packet[2048]; + +char PCARS_tempChar[2]; // Temporary array for u16 conversion + +u16 PCARS_RPM; +u16 PCARS_maxRPM; + +long PCARS_lastRead = millis() - 2001; +float PCARS_rpmRatio; + + +void userSetup() +{ + UDP.begin(PCARS_localUdpPort); +} + +void userConnected() +{ + // new wifi, who dis? +} + +void userLoop() +{ + PCARS_readValues(); + if (PCARS_lastRead > millis() - 2000) { + PCARS_buildcolorbars(); + } +} + +void PCARS_readValues() { + + int PCARS_packetSize = UDP.parsePacket(); + if (PCARS_packetSize) { + int len = UDP.read(PCARS_packet, PCARS_packetSize); + if (len > 0) { + PCARS_packet[len] = 0; + } + if (len == 1367) { // Telemetry packet. Ignoring everything else. + PCARS_lastRead = millis(); + + arlsLock(realtimeTimeoutMs, REALTIME_MODE_GENERIC); + // current RPM + memcpy(&PCARS_tempChar, &PCARS_packet[124], 2); + PCARS_RPM = (PCARS_tempChar[1] << 8) + PCARS_tempChar[0]; + + // max RPM + memcpy(&PCARS_tempChar, &PCARS_packet[126], 2); + PCARS_maxRPM = (PCARS_tempChar[1] << 8) + PCARS_tempChar[0]; + + if (PCARS_maxRPM) { + PCARS_rpmRatio = constrain((float)PCARS_RPM / (float)PCARS_maxRPM, 0, 1); + } else { + PCARS_rpmRatio = 0.0; + } + } + } +} +void PCARS_buildcolorbars() { + boolean activated = false; + float ledratio = 0; + + for (uint16_t i = 0; i < ledCount; i++) { + if (PCARS_rpmRatio < .95 || (millis() % 100 > 70 )) { + + ledratio = (float)i / (float)ledCount; + if (ledratio < PCARS_rpmRatio) { + activated = true; + } else { + activated = false; + } + if (ledratio > 0.66) { + setRealtimePixel(i, 0, 0, PCARS_dimcolor + ((255 - PCARS_dimcolor)*activated), 0); + } else if (ledratio > 0.33) { + setRealtimePixel(i, PCARS_dimcolor + ((255 - PCARS_dimcolor)*activated), 0, 0, 0); + } else { + setRealtimePixel(i, 0, PCARS_dimcolor + ((255 - PCARS_dimcolor)*activated), 0, 0); + } + } + else { + setRealtimePixel(i, 0, 0, 0, 0); + + } + } + colorUpdated(5); + strip.show(); +} \ No newline at end of file From bffe2d7bd0e537275b55ca5d1c0cb01d99c1d216 Mon Sep 17 00:00:00 2001 From: pille Date: Tue, 10 Mar 2020 17:33:23 +0100 Subject: [PATCH 157/232] track E1.31 seqcuence numbers for each universe individually, wrt #742 --- wled00/wled00.ino | 5 ++--- wled00/wled02_xml.ino | 2 -- wled00/wled07_notify.ino | 33 +++++++++++++++++++-------------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 325cc73e..28fb3c0c 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -206,11 +206,12 @@ bool receiveDirect = true; //receive UDP realtime bool arlsDisableGammaCorrection = true; //activate if gamma correction is handled by the source bool arlsForceMaxBri = false; //enable to force max brightness if source has very dark colors that would be black +#define E131_MAX_UNIVERSE_COUNT 9 uint16_t e131Universe = 1; //settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) uint8_t DMXMode = DMX_MODE_MULTIPLE_RGB; //DMX mode (s.a.) uint16_t DMXAddress = 1; //DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] uint8_t DMXOldDimmer = 0; //only update brightness on change -uint8_t e131LastSequenceNumber = 0; //to detect packet loss +uint8_t e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; //to detect packet loss bool e131Multicast = false; //multicast or unicast bool mqttEnabled = false; @@ -464,8 +465,6 @@ void handleE131Packet(e131_packet_t*, IPAddress); void arlsLock(uint32_t,byte); void handleOverlayDraw(); -#define E131_MAX_UNIVERSE_COUNT 9 - //udp interface objects WiFiUDP notifierUdp, rgbUdp; WiFiUDP ntpUdp; diff --git a/wled00/wled02_xml.ino b/wled00/wled02_xml.ino index d538e0c4..f27086f6 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/wled02_xml.ino @@ -74,8 +74,6 @@ char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr) mesg += "."; mesg += realtimeIP[i]; } - mesg += " seq="; - mesg += e131LastSequenceNumber; } else if (realtimeMode == REALTIME_MODE_UDP || realtimeMode == REALTIME_MODE_HYPERION) { mesg += "UDP from "; mesg += realtimeIP[0]; diff --git a/wled00/wled07_notify.ino b/wled00/wled07_notify.ino index 7f691dd0..b3c98567 100644 --- a/wled00/wled07_notify.ino +++ b/wled00/wled07_notify.ino @@ -88,26 +88,31 @@ void arlsLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC) void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ //E1.31 protocol support - - // skip out-of-sequence packets - if (p->sequence_number < e131LastSequenceNumber && p->sequence_number > 20 && e131LastSequenceNumber < 250){ - DEBUG_PRINT("skipping E1.31 frame (last seq="); - DEBUG_PRINT(e131LastSequenceNumber); - DEBUG_PRINT(", current seq="); - DEBUG_PRINT(p->sequence_number); - DEBUG_PRINTLN(")"); - return; - } - e131LastSequenceNumber = p->sequence_number; - // update status info - realtimeIP = clientIP; - uint16_t uni = htons(p->universe); uint8_t previousUniverses = uni - e131Universe; uint16_t possibleLEDsInCurrentUniverse; uint16_t dmxChannels = htons(p->property_value_count) -1; + // only listen for universes we're handling & allocated memory + if (uni >= (e131Universe + E131_MAX_UNIVERSE_COUNT)) return; + + // skip out-of-sequence packets + if (p->sequence_number < e131LastSequenceNumber[uni-e131Universe] && p->sequence_number > 20 && e131LastSequenceNumber[uni-e131Universe] < 250){ + DEBUG_PRINT("skipping E1.31 frame (last seq="); + DEBUG_PRINT(e131LastSequenceNumber[uni-e131Universe]); + DEBUG_PRINT(", current seq="); + DEBUG_PRINT(p->sequence_number); + DEBUG_PRINT(", universe="); + DEBUG_PRINT(uni); + DEBUG_PRINTLN(")"); + return; + } + e131LastSequenceNumber[uni-e131Universe] = p->sequence_number; + + // update status info + realtimeIP = clientIP; + switch (DMXMode) { case DMX_MODE_DISABLED: return; // nothing to do From 62510ac6a3a81051b2871504dafaedc87009a7a0 Mon Sep 17 00:00:00 2001 From: pille Date: Tue, 10 Mar 2020 18:21:16 +0100 Subject: [PATCH 158/232] add E1.31 packet out-of-sequence config option, wrt #742 --- wled00/data/settings_sync.htm | 1 + wled00/html_settings.h | 1 + wled00/wled00.ino | 1 + wled00/wled01_eeprom.ino | 10 +++++++++- wled00/wled02_xml.ino | 1 + wled00/wled03_set.ino | 1 + wled00/wled07_notify.ino | 22 +++++++++++----------- 7 files changed, 25 insertions(+), 12 deletions(-) diff --git a/wled00/data/settings_sync.htm b/wled00/data/settings_sync.htm index b4bcf059..5cf4b608 100644 --- a/wled00/data/settings_sync.htm +++ b/wled00/data/settings_sync.htm @@ -85,6 +85,7 @@

Realtime

Receive UDP realtime:

E1.31 (sACN)
+ Skip out-of-sequence packets (freeze instead of flicker):
Multicast mode:
E1.31 start universe:
Reboot required. Check out LedFx!

diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 7340e62d..c7badc16 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -284,6 +284,7 @@ Send notifications twice:

Realtime

Receive UDP realtime:

E1.31 (sACN)
+Skip out-of-sequence packets (freeze instead of flicker):
Use E1.31 multicast:
E1.31 start universe:
Reboot required. Check out LedFx!
diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 28fb3c0c..a6b16924 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -213,6 +213,7 @@ uint16_t DMXAddress = 1; //DMX start address of fixture, a. uint8_t DMXOldDimmer = 0; //only update brightness on change uint8_t e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; //to detect packet loss bool e131Multicast = false; //multicast or unicast +bool e131SkipOutOfSequence = false; //freeze instead of flickering bool mqttEnabled = false; char mqttDeviceTopic[33] = ""; //main MQTT topic (individual per device, default is wled/mac) diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino index acfc8dce..1f0e8426 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled01_eeprom.ino @@ -6,7 +6,7 @@ #define EEPSIZE 2560 //Maximum is 4096 //eeprom Version code, enables default settings instead of 0 init on update -#define EEPVER 17 +#define EEPVER 18 //0 -> old version, default //1 -> 0.4p 1711272 and up //2 -> 0.4p 1711302 and up @@ -25,6 +25,7 @@ //15-> 0.9.0-b3 //16-> 0.9.1 //17-> 0.9.1-dmx +//18-> 0.9.1-e131 void commit() { @@ -208,6 +209,7 @@ void saveSettingsToEEPROM() EEPROM.write(2181, macroNl); EEPROM.write(2182, macroDoublePress); + EEPROM.write(2189, e131SkipOutOfSequence); EEPROM.write(2190, e131Universe & 0xFF); EEPROM.write(2191, (e131Universe >> 8) & 0xFF); EEPROM.write(2192, e131Multicast); @@ -505,6 +507,12 @@ void loadSettingsFromEEPROM(bool first) noWifiSleep = EEPROM.read(370); //} + if (lastEEPROMversion > 17) + { + e131SkipOutOfSequence = EEPROM.read(2189); + } else { + e131SkipOutOfSequence = true; + } receiveDirect = !EEPROM.read(2200); notifyMacro = EEPROM.read(2201); diff --git a/wled00/wled02_xml.ino b/wled00/wled02_xml.ino index f27086f6..100be482 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/wled02_xml.ino @@ -324,6 +324,7 @@ void getSettingsJS(byte subPage, char* dest) sappend('c',"SM",notifyMacro); sappend('c',"S2",notifyTwice); sappend('c',"RD",receiveDirect); + sappend('c',"ES",e131SkipOutOfSequence); sappend('c',"EM",e131Multicast); sappend('v',"EU",e131Universe); sappend('v',"DA",DMXAddress); diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index d3897149..5a7f4db6 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -138,6 +138,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) notifyTwice = request->hasArg("S2"); receiveDirect = request->hasArg("RD"); + e131SkipOutOfSequence = request->hasArg("ES"); e131Multicast = request->hasArg("EM"); t = request->arg("EU").toInt(); if (t > 0 && t <= 63999) e131Universe = t; diff --git a/wled00/wled07_notify.ino b/wled00/wled07_notify.ino index b3c98567..b1fab41e 100644 --- a/wled00/wled07_notify.ino +++ b/wled00/wled07_notify.ino @@ -97,17 +97,17 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ // only listen for universes we're handling & allocated memory if (uni >= (e131Universe + E131_MAX_UNIVERSE_COUNT)) return; - // skip out-of-sequence packets - if (p->sequence_number < e131LastSequenceNumber[uni-e131Universe] && p->sequence_number > 20 && e131LastSequenceNumber[uni-e131Universe] < 250){ - DEBUG_PRINT("skipping E1.31 frame (last seq="); - DEBUG_PRINT(e131LastSequenceNumber[uni-e131Universe]); - DEBUG_PRINT(", current seq="); - DEBUG_PRINT(p->sequence_number); - DEBUG_PRINT(", universe="); - DEBUG_PRINT(uni); - DEBUG_PRINTLN(")"); - return; - } + if (e131SkipOutOfSequence) + if (p->sequence_number < e131LastSequenceNumber[uni-e131Universe] && p->sequence_number > 20 && e131LastSequenceNumber[uni-e131Universe] < 250){ + DEBUG_PRINT("skipping E1.31 frame (last seq="); + DEBUG_PRINT(e131LastSequenceNumber[uni-e131Universe]); + DEBUG_PRINT(", current seq="); + DEBUG_PRINT(p->sequence_number); + DEBUG_PRINT(", universe="); + DEBUG_PRINT(uni); + DEBUG_PRINTLN(")"); + return; + } e131LastSequenceNumber[uni-e131Universe] = p->sequence_number; // update status info From d21e82b020909d76462d9a786d672004f41a24a1 Mon Sep 17 00:00:00 2001 From: Aircoookie Date: Thu, 12 Mar 2020 23:26:38 +0100 Subject: [PATCH 159/232] Create changelog --- CHANGELOG.md | 10 ++++++++++ wled00/wled00.ino | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..f9e5b0f3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,10 @@ +## WLED changelog + +### Development versions after 0.9.1 release + + +#### Build 2003121 + +Created changelog.md - make tracking changes to code easier +Merged pull request #766 by @pille: Fix E1.31 out-of sequence detection + diff --git a/wled00/wled00.ino b/wled00/wled00.ino index a6b16924..ba2d4b68 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -119,7 +119,7 @@ #endif //version code in format yymmddb (b = daily build) -#define VERSION 2003031 +#define VERSION 2003121 char versionString[] = "0.9.1"; From 5b312d4c8b325e59b08598aa6b9a635fa3e339fa Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 13 Mar 2020 08:04:55 +0100 Subject: [PATCH 160/232] Make it better readable Make it better readable and make use of single line for add/chgange and/or delete --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9e5b0f3..fd5aa846 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,6 @@ #### Build 2003121 -Created changelog.md - make tracking changes to code easier -Merged pull request #766 by @pille: Fix E1.31 out-of sequence detection +- Created changelog.md - make tracking changes to code easier +- Merged pull request #766 by @pille: Fix E1.31 out-of sequence detection From c2ab3f96a7a52ec9f80ce25f56b302d80a4c3a73 Mon Sep 17 00:00:00 2001 From: srg74 <28492985+srg74@users.noreply.github.com> Date: Fri, 13 Mar 2020 20:42:15 -0400 Subject: [PATCH 161/232] gitignore update --- .gitignore | 2 ++ platformio.ini | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ea49cb51..df0f9e85 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ /wled00/Release /wled00/extLibs /platformio_override.ini +.DS_Store +.gitignore diff --git a/platformio.ini b/platformio.ini index 91bdd867..cf0ba25c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -27,7 +27,7 @@ default_envs = d1_mini, esp01, esp01_1m_ota, esp32dev ; default_envs = esp01_1m_ota ; default_envs = esp01_1m_full ; default_envs = esp07 -; default_envs = d1_mini +default_envs = d1_mini ; default_envs = heltec_wifi_kit_8 ; default_envs = d1_mini_debug ; default_envs = esp32dev From 37a31a9b9470ac4ffde15a68f7728b95f248f74d Mon Sep 17 00:00:00 2001 From: srg74 <28492985+srg74@users.noreply.github.com> Date: Fri, 13 Mar 2020 20:44:05 -0400 Subject: [PATCH 162/232] Update platformio.ini --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index cf0ba25c..91bdd867 100644 --- a/platformio.ini +++ b/platformio.ini @@ -27,7 +27,7 @@ default_envs = d1_mini, esp01, esp01_1m_ota, esp32dev ; default_envs = esp01_1m_ota ; default_envs = esp01_1m_full ; default_envs = esp07 -default_envs = d1_mini +; default_envs = d1_mini ; default_envs = heltec_wifi_kit_8 ; default_envs = d1_mini_debug ; default_envs = esp32dev From d36796429e908694e477e0b6c3ec2d975ba4011e Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sat, 14 Mar 2020 11:28:42 +0100 Subject: [PATCH 163/232] Various small fixes Fixed color of main segment returned in JSON API during transition not being target color - Fixed arlsLock() being called after pixels set in E1.31, (closes #772) - Fixed HTTP API calls not having an effect if no segment selected (now applies to main segment) --- CHANGELOG.md | 6 ++++++ wled00/FX_fcn.cpp | 14 ++++++++++++-- wled00/wled00.ino | 2 +- wled00/wled07_notify.ino | 5 ++++- wled00/wled19_json.ino | 27 +++++++++++++++++++-------- 5 files changed, 42 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd5aa846..cbc45e92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ ### Development versions after 0.9.1 release +#### Build 2003141 + +- Fixed color of main segment returned in JSON API during transition not being target color (closes #765) +- Fixed arlsLock() being called after pixels set in E1.31 (closes #772) +- Fixed HTTP API calls not having an effect if no segment selected (now applies to main segment) + #### Build 2003121 - Created changelog.md - make tracking changes to code easier diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 2392392d..59f69da4 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -335,6 +335,8 @@ bool WS2812FX::setEffectConfig(uint8_t m, uint8_t s, uint8_t in, uint8_t p) { Segment& seg = _segments[getMainSegmentId()]; uint8_t modePrev = seg.mode, speedPrev = seg.speed, intensityPrev = seg.intensity, palettePrev = seg.palette; + bool applied = false; + if (applyToAllSelected) { for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) { @@ -344,9 +346,12 @@ bool WS2812FX::setEffectConfig(uint8_t m, uint8_t s, uint8_t in, uint8_t p) { _segments[i].intensity = in; _segments[i].palette = p; setMode(i, m); + applied = true; } } - } else { + } + + if (!applyToAllSelected || !applied) { seg.speed = s; seg.intensity = in; seg.palette = p; @@ -363,12 +368,17 @@ void WS2812FX::setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w void WS2812FX::setColor(uint8_t slot, uint32_t c) { if (slot >= NUM_COLORS) return; + + bool applied = false; + if (applyToAllSelected) { for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) { if (_segments[i].isSelected()) _segments[i].colors[slot] = c; } - } else { + } + + if (!applyToAllSelected || !applied) { _segments[getMainSegmentId()].colors[slot] = c; } } diff --git a/wled00/wled00.ino b/wled00/wled00.ino index ba2d4b68..f9648dcf 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -119,7 +119,7 @@ #endif //version code in format yymmddb (b = daily build) -#define VERSION 2003121 +#define VERSION 2003141 char versionString[] = "0.9.1"; diff --git a/wled00/wled07_notify.ino b/wled00/wled07_notify.ino index b1fab41e..8fcbdbe8 100644 --- a/wled00/wled07_notify.ino +++ b/wled00/wled07_notify.ino @@ -121,6 +121,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ case DMX_MODE_SINGLE_RGB: if (uni != e131Universe) return; if (dmxChannels-DMXAddress+1 < 3) return; + arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); for (uint16_t i = 0; i < ledCount; i++) setRealtimePixel(i, p->property_values[DMXAddress+0], p->property_values[DMXAddress+1], p->property_values[DMXAddress+2], 0); break; @@ -128,6 +129,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ case DMX_MODE_SINGLE_DRGB: if (uni != e131Universe) return; if (dmxChannels-DMXAddress+1 < 4) return; + arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); if (DMXOldDimmer != p->property_values[DMXAddress+0]) { DMXOldDimmer = p->property_values[DMXAddress+0]; bri = p->property_values[DMXAddress+0]; @@ -166,6 +168,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ break; case DMX_MODE_MULTIPLE_RGB: + arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); if (previousUniverses == 0) { // first universe of this fixture possibleLEDsInCurrentUniverse = (dmxChannels - DMXAddress + 1) / 3; @@ -187,6 +190,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ break; case DMX_MODE_MULTIPLE_DRGB: + arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); if (previousUniverses == 0) { // first universe of this fixture if (DMXOldDimmer != p->property_values[DMXAddress+0]) { @@ -218,7 +222,6 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ break; } - arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); e131NewData = true; } diff --git a/wled00/wled19_json.ino b/wled00/wled19_json.ino index 237912d6..df9bcead 100644 --- a/wled00/wled19_json.ino +++ b/wled00/wled19_json.ino @@ -31,12 +31,14 @@ void deserializeSegment(JsonObject elem, byte it) { int rgbw[] = {0,0,0,0}; byte cp = copyArray(colX, rgbw); - seg.colors[i] = ((rgbw[3] << 24) | ((rgbw[0]&0xFF) << 16) | ((rgbw[1]&0xFF) << 8) | ((rgbw[2]&0xFF))); + if (cp == 1 && rgbw[0] == 0) seg.colors[i] = 0; - if (id == strip.getMainSegmentId()) //temporary + if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment { if (i == 0) {col[0] = rgbw[0]; col[1] = rgbw[1]; col[2] = rgbw[2]; col[3] = rgbw[3];} if (i == 1) {colSec[0] = rgbw[0]; colSec[1] = rgbw[1]; colSec[2] = rgbw[2]; colSec[3] = rgbw[3];} + } else { + seg.colors[i] = ((rgbw[3] << 24) | ((rgbw[0]&0xFF) << 16) | ((rgbw[1]&0xFF) << 8) | ((rgbw[2]&0xFF))); } } } @@ -175,15 +177,24 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id) root["spc"] = seg.spacing; JsonArray colarr = root.createNestedArray("col"); - + for (uint8_t i = 0; i < 3; i++) { JsonArray colX = colarr.createNestedArray(); - colX.add((seg.colors[i] >> 16) & 0xFF); - colX.add((seg.colors[i] >> 8) & 0xFF); - colX.add((seg.colors[i]) & 0xFF); - if (useRGBW) - colX.add((seg.colors[i] >> 24) & 0xFF); + if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment + { + if (i == 0) { + colX.add(col[0]); colX.add(col[1]); colX.add(col[2]); if (useRGBW) colX.add(col[3]); + } else { + colX.add(colSec[0]); colX.add(colSec[1]); colX.add(colSec[2]); if (useRGBW) colX.add(colSec[3]); + } + } else { + colX.add((seg.colors[i] >> 16) & 0xFF); + colX.add((seg.colors[i] >> 8) & 0xFF); + colX.add((seg.colors[i]) & 0xFF); + if (useRGBW) + colX.add((seg.colors[i] >> 24) & 0xFF); + } } root["fx"] = seg.mode; From c246e5af35d71e7742d9bed4547a1bd4e9bd297e Mon Sep 17 00:00:00 2001 From: hobbyquaker Date: Sun, 15 Mar 2020 12:38:49 +0100 Subject: [PATCH 164/232] add battery-keypad-controller usermod --- usermods/battery_keypad_controller/README.md | 13 ++ .../assets/bat-key-ctrl-1.jpg | Bin 0 -> 326475 bytes .../assets/bat-key-ctrl-2.jpg | Bin 0 -> 256061 bytes .../assets/bat-key-ctrl-3.jpg | Bin 0 -> 352184 bytes .../wled06_usermod.ino | 152 ++++++++++++++++++ 5 files changed, 165 insertions(+) create mode 100644 usermods/battery_keypad_controller/README.md create mode 100644 usermods/battery_keypad_controller/assets/bat-key-ctrl-1.jpg create mode 100644 usermods/battery_keypad_controller/assets/bat-key-ctrl-2.jpg create mode 100644 usermods/battery_keypad_controller/assets/bat-key-ctrl-3.jpg create mode 100644 usermods/battery_keypad_controller/wled06_usermod.ino diff --git a/usermods/battery_keypad_controller/README.md b/usermods/battery_keypad_controller/README.md new file mode 100644 index 00000000..6262b21c --- /dev/null +++ b/usermods/battery_keypad_controller/README.md @@ -0,0 +1,13 @@ +# Battery powered controller with keypad + +I'm using this controller for a festival totem. Runs on 3 18650 Cells, can deliver >5A current. + +Via keypad one can select 8 presets, change effect, effect speed, effect intensity and palette. Brightness can be +adjusted with a potentiometer. + + +## Pictures + +![bat-key-ctrl-1](assets/bat-key-ctrl-1.jpg) +![bat-key-ctrl-2](assets/bat-key-ctrl-2.jpg) +![bat-key-ctrl-3](assets/bat-key-ctrl-3.jpg) diff --git a/usermods/battery_keypad_controller/assets/bat-key-ctrl-1.jpg b/usermods/battery_keypad_controller/assets/bat-key-ctrl-1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f352ba3a379134990283eb52ccb2659ae35d6be9 GIT binary patch literal 326475 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%0AEQe8comPWd+ccwLCR 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νRod~sS(0O+?jF~To^$uBjO~rk+hqroN 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%0DneBOz?+7C4uiY(5542cZ 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%0DM|6%Zyl`N96ZEANXbCzjSZE@Seg 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 z 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
2mCCnpZv9 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�$Ah{#N&)q<3`l=T}gFq9_r7= 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

0kFReCM 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%0DSGo^Q%%pYacdYv4h4b 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 S!k4urFaH2ulamvZKmXbHMfGt2 literal 0 HcmV?d00001 diff --git a/usermods/battery_keypad_controller/wled06_usermod.ino b/usermods/battery_keypad_controller/wled06_usermod.ino new file mode 100644 index 00000000..dd001c8d --- /dev/null +++ b/usermods/battery_keypad_controller/wled06_usermod.ino @@ -0,0 +1,152 @@ +/* + * WLED usermod for keypad and brightness-pot. + * 3'2020 https://github.com/hobbyquaker + */ + +#include +const byte keypad_rows = 4; +const byte keypad_cols = 4; +char keypad_keys[keypad_rows][keypad_cols] = { + {'1', '2', '3', 'A'}, + {'4', '5', '6', 'B'}, + {'7', '8', '9', 'C'}, + {'*', '0', '#', 'D'} +}; + +byte keypad_colPins[keypad_rows] = {D3, D2, D1, D0}; +byte keypad_rowPins[keypad_cols] = {D7, D6, D5, D4}; + +Keypad myKeypad = Keypad(makeKeymap(keypad_keys), keypad_rowPins, keypad_colPins, keypad_rows, keypad_cols); + +void userSetup() +{ + +} + +void userConnected() +{ + +} + +long lastTime = 0; +int delayMs = 20; //we want to do something every 2 seconds + +void userLoop() +{ + if (millis()-lastTime > delayMs) + { + + long analog = analogRead(0); + int new_bri = 1; + if (analog > 900) { + new_bri = 255; + } else if (analog > 30) { + new_bri = dim8_video(map(analog, 31, 900, 16, 255)); + } + if (bri != new_bri) { + bri = new_bri; + colorUpdated(1); + + } + + char myKey = myKeypad.getKey(); + if (myKey != NULL) { + switch (myKey) { + case '1': + applyPreset(1); + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case '2': + applyPreset(2); + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case '3': + applyPreset(3); + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case '4': + applyPreset(4); + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case '5': + applyPreset(5); + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case '6': + applyPreset(6); + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case 'A': + applyPreset(7); + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case 'B': + applyPreset(8); + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + + case '7': + effectCurrent += 1; + if (effectCurrent >= MODE_COUNT) effectCurrent = 0; + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case '*': + effectCurrent -= 1; + if (effectCurrent < 0) effectCurrent = (MODE_COUNT-1); + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + + case '8': + if (effectSpeed < 240) { + effectSpeed += 12; + } else if (effectSpeed < 255) { + effectSpeed += 1; + } + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case '0': + if (effectSpeed > 15) { + effectSpeed -= 12; + } else if (effectSpeed > 0) { + effectSpeed -= 1; + } + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + + case '9': + if (effectIntensity < 240) { + effectIntensity += 12; + } else if (effectIntensity < 255) { + effectIntensity += 1; + } + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case '#': + if (effectIntensity > 15) { + effectIntensity -= 12; + } else if (effectIntensity > 0) { + effectIntensity -= 1; + } + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + + case 'C': + effectPalette += 1; + if (effectPalette >= 50) effectPalette = 0; + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case 'D': + effectPalette -= 1; + if (effectPalette <= 0) effectPalette = 50; + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + break; + + } + + } + + lastTime = millis(); + } + +} \ No newline at end of file From dbef0e65833632e273e4b18418cb07d27e78d3c0 Mon Sep 17 00:00:00 2001 From: hobbyquaker Date: Sun, 15 Mar 2020 12:43:09 +0100 Subject: [PATCH 165/232] fix lint issues --- usermods/battery_keypad_controller/README.md | 1 - usermods/battery_keypad_controller/wled06_usermod.ino | 1 - 2 files changed, 2 deletions(-) diff --git a/usermods/battery_keypad_controller/README.md b/usermods/battery_keypad_controller/README.md index 6262b21c..77e3c1e4 100644 --- a/usermods/battery_keypad_controller/README.md +++ b/usermods/battery_keypad_controller/README.md @@ -5,7 +5,6 @@ I'm using this controller for a festival totem. Runs on 3 18650 Cells, can deliv Via keypad one can select 8 presets, change effect, effect speed, effect intensity and palette. Brightness can be adjusted with a potentiometer. - ## Pictures ![bat-key-ctrl-1](assets/bat-key-ctrl-1.jpg) diff --git a/usermods/battery_keypad_controller/wled06_usermod.ino b/usermods/battery_keypad_controller/wled06_usermod.ino index dd001c8d..877713b6 100644 --- a/usermods/battery_keypad_controller/wled06_usermod.ino +++ b/usermods/battery_keypad_controller/wled06_usermod.ino @@ -140,7 +140,6 @@ void userLoop() if (effectPalette <= 0) effectPalette = 50; colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); break; - break; } From c28027496d6bcc2cb61c10f0519acc52cbbb61e6 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Thu, 19 Mar 2020 00:37:35 -0400 Subject: [PATCH 166/232] Fix initialization bug when toggling skip first. --- wled00/FX_fcn.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 59f69da4..158e3923 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -29,11 +29,11 @@ void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst) { - if (supportWhite == _useRgbw && countPixels == _length) return; + if (supportWhite == _useRgbw && countPixels == _length && _skipFirstMode == skipFirst) return; RESET_RUNTIME; _useRgbw = supportWhite; - _skipFirstMode = skipFirst; _length = countPixels; + _skipFirstMode = skipFirst; uint8_t ty = 1; if (supportWhite) ty = 2; From e5c3629e2ef7b186a458241660d6fedb09542532 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sat, 21 Mar 2020 00:57:54 +0100 Subject: [PATCH 167/232] Added custom per-LED mapping --- CHANGELOG.md | 5 +++++ readme.md | 4 ++++ wled00/FX_fcn.cpp | 31 ++++++++++++++++++++++++++++++- wled00/NpbWrapper.h | 2 +- wled00/html_settings.h | 2 +- wled00/wled00.ino | 2 +- 6 files changed, 42 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbc45e92..631162c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ### Development versions after 0.9.1 release +#### Build 2003211 + +- Added custom mapping compile define to FX_fcn.h +- Merged pull request #784 by @TravisDean: Fixed initialization bug when toggling skip first +- Added link to youtube videos by Room31 to readme #### Build 2003141 diff --git a/readme.md b/readme.md index c796ca5c..53671c6b 100644 --- a/readme.md +++ b/readme.md @@ -50,6 +50,10 @@ DrZzs has made some excellent video guides: If you'd rather read, here is a very [detailed step-by-step beginner tutorial](https://tynick.com/blog/11-03-2019/getting-started-with-wled-on-esp8266/) by tynick! +Russian speakers, check out the videos by Room31: +[WLED Firmware Overview: Interface and Settings](https://youtu.be/h7lKsczEI7E) +[ESP8266 based LED controller for WS2812b strip. WLED Firmware + OpenHAB](https://youtu.be/K4ioTt3XvGc) + ### Other Licensed under the MIT license diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 59f69da4..faf9b293 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -27,6 +27,23 @@ #include "FX.h" #include "palettes.h" +//enable custom per-LED mapping. This can allow for better effects on matrices or special displays +//#define WLED_CUSTOM_LED_MAPPING + +#ifdef WLED_CUSTOM_LED_MAPPING +//this is just an example (30 LEDs). It will first set all even, then all uneven LEDs. +const uint16_t customMappingTable[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, + 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29}; + +//another example. Switches direction every 5 LEDs. +/*const uint16_t customMappingTable[] = { + 0, 1, 2, 3, 4, 9, 8, 7, 6, 5, 10, 11, 12, 13, 14, + 19, 18, 17, 16, 15, 20, 21, 22, 23, 24, 29, 28, 27, 26, 25};*/ + +const uint16_t customMappingSize = sizeof(customMappingTable)/sizeof(uint16_t); //30 in example +#endif + void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst) { if (supportWhite == _useRgbw && countPixels == _length) return; @@ -142,10 +159,16 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) int16_t indexSet = realIndex + (reversed ? -j : j); int16_t indexSetRev = indexSet; if (reverseMode) indexSetRev = _length - 1 - indexSet; + #ifdef WLED_CUSTOM_LED_MAPPING + if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet]; + #endif if (indexSetRev >= SEGMENT.start && indexSetRev < SEGMENT.stop) bus->SetPixelColor(indexSet + skip, col); } } else { //live data, etc. if (reverseMode) i = _length - 1 - i; + #ifdef WLED_CUSTOM_LED_MAPPING + if (i < customMappingSize) i = customMappingTable[i]; + #endif bus->SetPixelColor(i + skip, col); } if (skip && i == 0) { @@ -430,7 +453,13 @@ uint32_t WS2812FX::getColor(void) { uint32_t WS2812FX::getPixelColor(uint16_t i) { - i = realPixelIndex(i) + (_skipFirstMode ? LED_SKIP_AMOUNT : 0); + i = realPixelIndex(i); + + #ifdef WLED_CUSTOM_LED_MAPPING + if (i < customMappingSize) i = customMappingTable[i]; + #endif + + if (_skipFirstMode) i += LED_SKIP_AMOUNT; if (_cronixieMode) { diff --git a/wled00/NpbWrapper.h b/wled00/NpbWrapper.h index 71367d98..673d8249 100644 --- a/wled00/NpbWrapper.h +++ b/wled00/NpbWrapper.h @@ -4,7 +4,7 @@ //PIN CONFIGURATION #ifndef LEDPIN -#define LEDPIN 2 //strip pin. Any for ESP32, gpio2 or 3 is recommended for ESP8266 (gpio2/3 are labeled D4/RX on NodeMCU and Wemos) +#define LEDPIN 2 //strip pin. Any for ESP32, gpio2 or 3 is recommended for ESP8266 (gpio2/3 are labeled D4/RX on NodeMCU and Wemos) #endif //#define USE_APA102 // Uncomment for using APA102 LEDs. //#define USE_WS2801 // Uncomment for using WS2801 LEDs (make sure you have NeoPixelBus v2.5.6 or newer) diff --git a/wled00/html_settings.h b/wled00/html_settings.h index c7badc16..26601695 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -284,9 +284,9 @@ Send notifications twice:

Realtime

Receive UDP realtime:

E1.31 (sACN)
-Skip out-of-sequence packets (freeze instead of flicker):
Use E1.31 multicast:
E1.31 start universe:
+Skip out-of-sequence packets:
Reboot required. Check out
LedFx!
DMX start address:
DMX mode: diff --git a/wled00/wled00.ino b/wled00/wled00.ino index f9648dcf..69517073 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -119,7 +119,7 @@ #endif //version code in format yymmddb (b = daily build) -#define VERSION 2003141 +#define VERSION 2003211 char versionString[] = "0.9.1"; From a4f0c9195e820b936f215a0ce6fd987c7ca953c1 Mon Sep 17 00:00:00 2001 From: Thomas Stockklauser Date: Sun, 22 Mar 2020 11:38:29 +0100 Subject: [PATCH 168/232] 1. Fix an Issue withtin the DMX Compiler Flags if you dont use it in wled00.ino 2. Fix the Build / working environment for Visual Studio and make a better Folder Structure within the Visual Studio Project --- wled00/__vm/.wled00.vsarduino.h | 15 +++++---------- wled00/__vm/Compile.vmps.xml | 6 +++--- wled00/wled00.ino | 2 ++ wled00/wled00.vcxproj | 4 ++-- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/wled00/__vm/.wled00.vsarduino.h b/wled00/__vm/.wled00.vsarduino.h index 722f8378..2a9ef40a 100644 --- a/wled00/__vm/.wled00.vsarduino.h +++ b/wled00/__vm/.wled00.vsarduino.h @@ -17,8 +17,10 @@ #define __ESP32_ESP32__ #define ESP_PLATFORM #define HAVE_CONFIG_H +#define GCC_NOT_5_2_0 0 +#define WITH_POSIX #define F_CPU 240000000L -#define ARDUINO 10809 +#define ARDUINO 108011 #define ARDUINO_ESP32_DEV #define ARDUINO_ARCH_ESP32 #define ESP32 @@ -92,18 +94,11 @@ typedef long pthread_cond_t; #include "arduino.h" #include -//#include "..\generic\Common.h" -//#include "..\generic\pins_arduino.h" - -//#undef F -//#define F(string_literal) ((const PROGMEM char *)(string_literal)) -//#undef PSTR -//#define PSTR(string_literal) ((const PROGMEM char *)(string_literal)) -//current vc++ does not understand this syntax so use older arduino example for intellisense -//todo:move to the new clang/gcc project types. #define interrupts() sei() #define noInterrupts() cli() +#define ESP_LOGI(tag, ...) + #include "wled00.ino" #include "wled01_eeprom.ino" #include "wled02_xml.ino" diff --git a/wled00/__vm/Compile.vmps.xml b/wled00/__vm/Compile.vmps.xml index a7819d20..6390398e 100644 --- a/wled00/__vm/Compile.vmps.xml +++ b/wled00/__vm/Compile.vmps.xml @@ -1,8 +1,8 @@ - - + + - + diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 69517073..80f9d79b 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -552,7 +552,9 @@ void loop() { handleSerial(); handleNotifications(); handleTransitions(); +#ifdef WLED_ENABLE_DMX handleDMX(); +#endif userLoop(); yield(); diff --git a/wled00/wled00.vcxproj b/wled00/wled00.vcxproj index b9842e0a..2183ad26 100644 --- a/wled00/wled00.vcxproj +++ b/wled00/wled00.vcxproj @@ -73,9 +73,9 @@ true true true - $(ProjectDir)..\wled00;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\libraries\WiFi\src;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\libraries\ESPmDNS\src;C:\Program Files (x86)\Arduino\libraries\AsyncTCP;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\libraries\SPIFFS\src;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\libraries\FS\src;C:\Program Files (x86)\Arduino\libraries\ESPAsyncWebServer\src;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\libraries\EEPROM\src;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\libraries\DNSServer\src;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\libraries\ArduinoOTA\src;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\libraries\Update\src;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\libraries\AsyncUDP\src;$(ProjectDir)..\..\..\User\Thomas\Documents\Arduino\libraries\NeoPixelBus_by_Makuna\src;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\libraries\SPI\src;$(ProjectDir)..\..\..\User\Thomas\Documents\Arduino\libraries\FastLED;C:\Program Files (x86)\Arduino\libraries;$(ProjectDir)..\..\..\User\Thomas\Documents\Arduino\libraries;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\libraries;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\cores\esp32;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\cores\esp32\apps;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\cores\esp32\libb64;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\variants\esp32;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\config;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\app_trace;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\app_update;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\asio;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\bootloader_support;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\bt;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\coap;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\console;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\driver;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\esp-tls;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\esp32;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\esp_adc_cal;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\esp_event;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\esp_http_client;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\esp_http_server;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\esp_https_ota;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\esp_ringbuf;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\ethernet;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\expat;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\fatfs;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\freemodbus;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\freertos;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\heap;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\idf_test;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\jsmn;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\json;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\libsodium;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\log;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\lwip;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\mbedtls;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\mdns;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\micro-ecc;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\mqtt;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\newlib;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\nghttp;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\nvs_flash;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\openssl;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\protobuf-c;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\protocomm;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\pthread;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\sdmmc;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\smartconfig_ack;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\soc;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\spi_flash;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\spiffs;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\tcp_transport;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\tcpip_adapter;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\ulp;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\vfs;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\wear_levelling;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\wifi_provisioning;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\wpa_supplicant;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\xtensa-debug-module;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\esp32-camera;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\esp-face;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\hardware\esp32\1.0.2\tools\sdk\include\fb_gfx;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\1.22.0-80-g6c4433a-5.2.0\lib\gcc\xtensa-esp32-elf\5.2.0\include;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\1.22.0-80-g6c4433a-5.2.0\xtensa-esp32-elf\include\c++\5.2.0\xtensa-esp32-elf;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\1.22.0-80-g6c4433a-5.2.0\xtensa-esp32-elf\include;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\1.22.0-80-g6c4433a-5.2.0\xtensa-esp32-elf\include\c++\5.2.0;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\1.22.0-80-g6c4433a-5.2.0\include\c++\5.2.0;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\1.22.0-80-g6c4433a-5.2.0\include;C:\Users\thoma\AppData\Local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\1.22.0-80-g6c4433a-5.2.0\include\include;%(AdditionalIncludeDirectories) + $(ProjectDir)..\wled00;C:\Program Files (x86)\Arduino\libraries\WiFi\src;C:\Program Files (x86)\Arduino\libraries\WiFi\src\utility;C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM\src;C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino;C:\Program Files (x86)\Arduino\hardware\arduino\avr\variants\yun;C:\Program Files (x86)\Arduino\hardware\tools\avr\\lib\gcc\avr\7.3.0\include;C:\Program Files (x86)\Arduino\hardware\tools\avr\avr\include;C:\Program Files (x86)\Arduino\hardware\tools\avr\\lib\gcc\avr\7.3.0\include;C:\Program Files (x86)\Arduino\hardware\tools\avr\avr\include-fixed;C:\Program Files (x86)\Arduino\hardware\tools\avr\avr\include\avr;C:\Program Files (x86)\Arduino\hardware\tools\avr\lib\gcc\avr\4.9.2\include;C:\Program Files (x86)\Arduino\hardware\tools\avr\lib\gcc\avr\4.9.2\include;C:\Program Files (x86)\Arduino\hardware\tools\avr\lib\gcc\avr\4.9.3\include;%(AdditionalIncludeDirectories) $(ProjectDir)__vm\.wled00.vsarduino.h;%(ForcedIncludeFiles) - __ESP32_esp32__;__ESP32_ESP32__;ESP_PLATFORM;HAVE_CONFIG_H;F_CPU=240000000L;ARDUINO=10809;ARDUINO_ESP32_DEV;ARDUINO_ARCH_ESP32;ESP32;CORE_DEBUG_LEVEL=0;__cplusplus=201103L;_VMICRO_INTELLISENSE;%(PreprocessorDefinitions) + __AVR_atmega32u4__;__AVR_ATmega32U4__;__AVR_ATmega32u4__;F_CPU=16000000L;ARDUINO=108011;ARDUINO_AVR_YUN;ARDUINO_ARCH_AVR;USB_VID=0x2341;USB_PID=0x8041;__cplusplus=201103L;_VMICRO_INTELLISENSE;%(PreprocessorDefinitions) true From 53f09c0630385049e0d1bad125513c707e493b78 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sun, 22 Mar 2020 17:45:09 +0100 Subject: [PATCH 169/232] Moved Cronixie driver from FX library to drawOverlay handler --- CHANGELOG.md | 4 ++ wled00/FX.h | 7 --- wled00/FX_fcn.cpp | 117 +++++++------------------------------ wled00/wled00.ino | 2 +- wled00/wled03_set.ino | 6 -- wled00/wled11_ol.ino | 13 +++-- wled00/wled13_cronixie.ino | 43 +++++++++++--- 7 files changed, 69 insertions(+), 123 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 631162c7..321eea8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ### Development versions after 0.9.1 release +#### Build 2003221 + +- Moved Cronixie driver from FX library to drawOverlay handler + #### Build 2003211 - Added custom mapping compile define to FX_fcn.h diff --git a/wled00/FX.h b/wled00/FX.h index abb8c6f0..d24250f9 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -407,9 +407,6 @@ class WS2812FX { setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), setColor(uint8_t slot, uint32_t c), setBrightness(uint8_t b), - driverModeCronixie(bool b), - setCronixieDigits(byte* d), - setCronixieBacklight(bool b), setRange(uint16_t i, uint16_t i2, uint32_t col), setShowCallback(show_callback cb), setTransitionMode(bool t), @@ -597,13 +594,9 @@ class WS2812FX { bool _useRgbw = false, - _cronixieMode, - _cronixieBacklightEnabled, _skipFirstMode, _triggered; - byte _cronixieDigits[6]; - mode_ptr _mode[MODE_COUNT]; // SRAM footprint: 4 bytes per element show_callback _callback = nullptr; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 7bc02def..05568623 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -147,90 +147,32 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) } col.W = w; - if (!_cronixieMode) - { - uint16_t skip = _skipFirstMode ? LED_SKIP_AMOUNT : 0; - if (SEGLEN) {//from segment - /* Set all the pixels in the group, ensuring _skipFirstMode is honored */ - bool reversed = reverseMode ^ IS_REVERSE; - uint16_t realIndex = realPixelIndex(i); + uint16_t skip = _skipFirstMode ? LED_SKIP_AMOUNT : 0; + if (SEGLEN) {//from segment + /* Set all the pixels in the group, ensuring _skipFirstMode is honored */ + bool reversed = reverseMode ^ IS_REVERSE; + uint16_t realIndex = realPixelIndex(i); - for (uint16_t j = 0; j < SEGMENT.grouping; j++) { - int16_t indexSet = realIndex + (reversed ? -j : j); - int16_t indexSetRev = indexSet; - if (reverseMode) indexSetRev = _length - 1 - indexSet; - #ifdef WLED_CUSTOM_LED_MAPPING - if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet]; - #endif - if (indexSetRev >= SEGMENT.start && indexSetRev < SEGMENT.stop) bus->SetPixelColor(indexSet + skip, col); - } - } else { //live data, etc. - if (reverseMode) i = _length - 1 - i; + for (uint16_t j = 0; j < SEGMENT.grouping; j++) { + int16_t indexSet = realIndex + (reversed ? -j : j); + int16_t indexSetRev = indexSet; + if (reverseMode) indexSetRev = _length - 1 - indexSet; #ifdef WLED_CUSTOM_LED_MAPPING - if (i < customMappingSize) i = customMappingTable[i]; + if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet]; #endif - bus->SetPixelColor(i + skip, col); + if (indexSetRev >= SEGMENT.start && indexSetRev < SEGMENT.stop) bus->SetPixelColor(indexSet + skip, col); } - if (skip && i == 0) { - for (uint16_t j = 0; j < skip; j++) { - bus->SetPixelColor(j, RgbwColor(0, 0, 0, 0)); - } - } - return; + } else { //live data, etc. + if (reverseMode) i = _length - 1 - i; + #ifdef WLED_CUSTOM_LED_MAPPING + if (i < customMappingSize) i = customMappingTable[i]; + #endif + bus->SetPixelColor(i + skip, col); } - - //CRONIXIE - if(i>6)return; - byte o = 10*i; - if (_cronixieBacklightEnabled && _cronixieDigits[i] <11) - { - byte r2 = _segments[0].colors[1] >>16; - byte g2 = _segments[0].colors[1] >> 8; - byte b2 = _segments[0].colors[1]; - byte w2 = _segments[0].colors[1] >>24; - for (int j=o; j< o+19; j++) - { - bus->SetPixelColor(j, RgbwColor(r2,g2,b2,w2)); + if (skip && i == 0) { + for (uint16_t j = 0; j < skip; j++) { + bus->SetPixelColor(j, RgbwColor(0, 0, 0, 0)); } - } else - { - for (int j=o; j< o+19; j++) - { - bus->SetPixelColor(j, RgbwColor(0,0,0,0)); - } - } - if (_skipFirstMode) o += LED_SKIP_AMOUNT; - switch(_cronixieDigits[i]) - { - case 0: bus->SetPixelColor(o+5, col); break; - case 1: bus->SetPixelColor(o+0, col); break; - case 2: bus->SetPixelColor(o+6, col); break; - case 3: bus->SetPixelColor(o+1, col); break; - case 4: bus->SetPixelColor(o+7, col); break; - case 5: bus->SetPixelColor(o+2, col); break; - case 6: bus->SetPixelColor(o+8, col); break; - case 7: bus->SetPixelColor(o+3, col); break; - case 8: bus->SetPixelColor(o+9, col); break; - case 9: bus->SetPixelColor(o+4, col); break; - } -} - -void WS2812FX::driverModeCronixie(bool b) -{ - _cronixieMode = b; - _segments[0].stop = (b) ? 6 : _length; -} - -void WS2812FX::setCronixieBacklight(bool b) -{ - _cronixieBacklightEnabled = b; -} - -void WS2812FX::setCronixieDigits(byte d[]) -{ - for (int i = 0; i<6; i++) - { - _cronixieDigits[i] = d[i]; } } @@ -461,25 +403,6 @@ uint32_t WS2812FX::getPixelColor(uint16_t i) if (_skipFirstMode) i += LED_SKIP_AMOUNT; - if (_cronixieMode) - { - if(i>6)return 0; - byte o = 10*i; - switch(_cronixieDigits[i]) - { - case 0: i=o+5; break; - case 1: i=o+0; break; - case 2: i=o+6; break; - case 3: i=o+1; break; - case 4: i=o+7; break; - case 5: i=o+2; break; - case 6: i=o+8; break; - case 7: i=o+3; break; - case 8: i=o+9; break; - case 9: i=o+4; break; - default: return 0; - } - } if (i >= _lengthRaw) return 0; RgbwColor col = bus->GetPixelColorRgbw(i); diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 80f9d79b..55f714f5 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -119,7 +119,7 @@ #endif //version code in format yymmddb (b = daily build) -#define VERSION 2003211 +#define VERSION 2003221 char versionString[] = "0.9.1"; diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index 5a7f4db6..fc82256d 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -217,12 +217,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) analogClockSecondsTrail = request->hasArg("OS"); strcpy(cronixieDisplay,request->arg("CX").c_str()); - bool cbOld = cronixieBacklight; cronixieBacklight = request->hasArg("CB"); - if (cbOld != cronixieBacklight && overlayCurrent == 3) - { - strip.setCronixieBacklight(cronixieBacklight); overlayRefreshedTime = 0; - } countdownMode = request->hasArg("CE"); countdownYear = request->arg("CY").toInt(); countdownMonth = request->arg("CI").toInt(); @@ -656,7 +651,6 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) if (pos > 0) //sets backlight { cronixieBacklight = (req.charAt(pos+3) != '0'); - if (overlayCurrent == 3) strip.setCronixieBacklight(cronixieBacklight); overlayRefreshedTime = 0; } #endif diff --git a/wled00/wled11_ol.ino b/wled00/wled11_ol.ino index cfe8a1f9..93f7b96d 100644 --- a/wled00/wled11_ol.ino +++ b/wled00/wled11_ol.ino @@ -6,13 +6,12 @@ void initCronixie() { if (overlayCurrent == 3 && !cronixieInit) { - strip.driverModeCronixie(true); - strip.setCronixieBacklight(cronixieBacklight); setCronixie(); + strip.getSegment(0).grouping = 10; //10 LEDs per digit cronixieInit = true; } else if (cronixieInit && overlayCurrent != 3) { - strip.driverModeCronixie(false); + strip.getSegment(0).grouping = 1; cronixieInit = false; } } @@ -120,6 +119,10 @@ void _overlayAnalogCountdown() void handleOverlayDraw() { - if (overlayCurrent != 1) return; //only analog clock - _overlayAnalogClock(); + if (!overlayCurrent) return; + switch (overlayCurrent) + { + case 1: _overlayAnalogClock(); break; + case 3: _drawOverlayCronixie(); break; + } } diff --git a/wled00/wled13_cronixie.ino b/wled00/wled13_cronixie.ino index 897c94f9..dd687ca1 100644 --- a/wled00/wled13_cronixie.ino +++ b/wled00/wled13_cronixie.ino @@ -1,6 +1,10 @@ /* * Support for the Cronixie clock */ +#ifndef WLED_DISABLE_CRONIXIE +byte _digitOut[6] = {10,10,10,10,10,10}; +#endif + byte getSameCodeLength(char code, int index, char const cronixieDisplay[]) { byte counter = 0; @@ -161,7 +165,6 @@ void _overlayCronixie() if (h>12) h-=12; else if (h==0) h+=12; } - byte _digitOut[]{10,10,10,10,10,10}; for (int i = 0; i < 6; i++) { if (dP[i] < 12) _digitOut[i] = dP[i]; @@ -184,8 +187,8 @@ void _overlayCronixie() case 37: _digitOut[i] = y/10; _digitOut[i+1] = y- _digitOut[i]*10; i++; break; //YY case 39: _digitOut[i] = 2; _digitOut[i+1] = 0; _digitOut[i+2] = y/10; _digitOut[i+3] = y- _digitOut[i+2]*10; i+=3; break; //YYYY - case 16: _digitOut[i+2] = ((h0/3)&1)?1:0; i++; //BBB (BBBB NI) - case 15: _digitOut[i+1] = (h0>17 || (h0>5 && h0<12))?1:0; i++; //BB + //case 16: _digitOut[i+2] = ((h0/3)&1)?1:0; i++; //BBB (BBBB NI) + //case 15: _digitOut[i+1] = (h0>17 || (h0>5 && h0<12))?1:0; i++; //BB case 14: _digitOut[i] = (h0>11)?1:0; break; //B } } else @@ -195,8 +198,8 @@ void _overlayCronixie() case 71: _digitOut[i] = h/10; _digitOut[i+1] = h- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //hh case 75: _digitOut[i] = m/10; _digitOut[i+1] = m- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //mm case 81: _digitOut[i] = s/10; _digitOut[i+1] = s- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //ss - case 66: _digitOut[i+2] = ((h0/3)&1)?1:10; i++; //bbb (bbbb NI) - case 65: _digitOut[i+1] = (h0>17 || (h0>5 && h0<12))?1:10; i++; //bb + //case 66: _digitOut[i+2] = ((h0/3)&1)?1:10; i++; //bbb (bbbb NI) + //case 65: _digitOut[i+1] = (h0>17 || (h0>5 && h0<12))?1:10; i++; //bb case 64: _digitOut[i] = (h0>11)?1:10; break; //b case 93: _digitOut[i] = weekday(local); _digitOut[i]--; if (_digitOut[i]<1) _digitOut[i]= 7; break; //d @@ -208,7 +211,33 @@ void _overlayCronixie() } } } - strip.setCronixieDigits(_digitOut); - //strip.trigger(); //this has a drawback, no effects slower than RefreshMs. advantage: Quick update, not dependant on effect time + #endif +} + +void _drawOverlayCronixie() +{ + #ifndef WLED_DISABLE_CRONIXIE + byte offsets[] = {5, 0, 6, 1, 7, 2, 8, 3, 9, 4}; + + for (uint16_t i = 0; i < 6; i++) + { + byte o = 10*i; + byte excl = 10; + if(_digitOut[i] < 10) excl = offsets[_digitOut[i]]; + excl += o; + + if (cronixieBacklight && _digitOut[i] <11) + { + uint32_t col = strip.gamma32(strip.getSegment(0).colors[1]); + for (uint16_t j=o; j< o+10; j++) { + if (j != excl) strip.setPixelColor(j, col); + } + } else + { + for (uint16_t j=o; j< o+10; j++) { + if (j != excl) strip.setPixelColor(j, 0); + } + } + } #endif } From ed729c32d2d2e6a6ffefa29cd009047eadea08dd Mon Sep 17 00:00:00 2001 From: cschwinne Date: Wed, 25 Mar 2020 00:59:48 +0100 Subject: [PATCH 170/232] Fixed Alexa whites --- CHANGELOG.md | 4 ++ wled00/src/dependencies/espalexa/Espalexa.h | 13 ++--- .../dependencies/espalexa/EspalexaDevice.cpp | 51 ++++++++----------- .../dependencies/espalexa/EspalexaDevice.h | 6 +-- wled00/wled00.ino | 3 +- wled00/wled12_alexa.ino | 28 +++++++--- wled00/wled14_colors.ino | 6 +-- 7 files changed, 61 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 321eea8a..68243a3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ### Development versions after 0.9.1 release +#### Build 2003222 + +- Fixed Alexa Whites on non-RGBW lights (bump Espalexa to 2.4.5) + #### Build 2003221 - Moved Cronixie driver from FX library to drawOverlay handler diff --git a/wled00/src/dependencies/espalexa/Espalexa.h b/wled00/src/dependencies/espalexa/Espalexa.h index 49c9b6a9..7a1312f8 100644 --- a/wled00/src/dependencies/espalexa/Espalexa.h +++ b/wled00/src/dependencies/espalexa/Espalexa.h @@ -10,7 +10,7 @@ */ /* * @title Espalexa library - * @version 2.4.4 + * @version 2.4.5 * @author Christian Schwinne * @license MIT * @contributors d-999 @@ -49,7 +49,7 @@ #include #ifdef ESPALEXA_DEBUG - #pragma message "Espalexa 2.4.4 debug mode" + #pragma message "Espalexa 2.4.5 debug mode" #define EA_DEBUG(x) Serial.print (x) #define EA_DEBUGLN(x) Serial.println (x) #else @@ -164,7 +164,7 @@ private: json += "\",\"modelid\":\"" + modelidString(dev->getType()); json += "\",\"manufacturername\":\"Philips\",\"productname\":\"E" + String(static_cast(dev->getType())); json += "\",\"uniqueid\":\"" + String(encodeLightId(deviceId+1)); - json += "\",\"swversion\":\"espalexa-2.4.4\"}"; + json += "\",\"swversion\":\"espalexa-2.4.5\"}"; return json; } @@ -188,7 +188,7 @@ private: } res += "\r\nFree Heap: " + (String)ESP.getFreeHeap(); res += "\r\nUptime: " + (String)millis(); - res += "\r\n\r\nEspalexa library v2.4.4 by Christian Schwinne 2020"; + res += "\r\n\r\nEspalexa library v2.4.5 by Christian Schwinne 2020"; server->send(200, "text/plain", res); } #endif @@ -370,9 +370,10 @@ public: if (!discoverable) return; //do not reply to M-SEARCH if not discoverable String request = packetBuffer; - if(request.indexOf("M-SEARCH") >= 0) { + if(request.indexOf("M-SEA") >= 0) { //M-SEARCH EA_DEBUGLN(request); - if(request.indexOf("upnp:rootdevice") > 0 || request.indexOf("asic:1") > 0 || request.indexOf("ssdp:all") > 0) { + //match upnp:rootdevice, device:basic:1, ssdp:all and ssdp:discover + if(request.indexOf("np:rootd") > 0 || request.indexOf("asic:1") > 0 || request.indexOf("dp:all") > 0 || request.indexOf("dp:dis") > 0) { EA_DEBUGLN("Responding search req..."); respondToSearch(); } diff --git a/wled00/src/dependencies/espalexa/EspalexaDevice.cpp b/wled00/src/dependencies/espalexa/EspalexaDevice.cpp index 25d70bb0..520cf441 100644 --- a/wled00/src/dependencies/espalexa/EspalexaDevice.cpp +++ b/wled00/src/dependencies/espalexa/EspalexaDevice.cpp @@ -122,37 +122,28 @@ uint32_t EspalexaDevice::getRGB() float temp = 10000/ _ct; //kelvins = 1,000,000/mired (and that /100) float r, g, b; -// Cold white to warm white receiving from Alexa: _ct = 199, 234, 284, 350, 383 (from cold white to warm white) - switch (_ct) { - case 199: rgb[0]=255,rgb[1]=255,rgb[2]=255;rgb[3]=255;break; - case 234: rgb[0]=127,rgb[1]=127,rgb[2]=127;rgb[3]=255;break; - case 284: rgb[0]=0,rgb[1]=0,rgb[2]=0;rgb[3]=255;break; - case 350: rgb[0]=130,rgb[1]=90,rgb[2]=0;rgb[3]=255;break; - case 383: rgb[0]=255,rgb[1]=153,rgb[2]=0;rgb[3]=255;break; - default: { - if( temp <= 66 ){ - r = 255; - g = temp; - g = 99.470802 * log(g) - 161.119568; - if( temp <= 19){ - b = 0; - } else { - b = temp-10; - b = 138.517731 * log(b) - 305.044793; - } - } else { - r = temp - 60; - r = 329.698727 * pow(r, -0.13320476); - g = temp - 60; - g = 288.12217 * pow(g, -0.07551485 ); - b = 255; - } - - rgb[0] = (byte)constrain(r,0.1,255.1); - rgb[1] = (byte)constrain(g,0.1,255.1); - rgb[2] = (byte)constrain(b,0.1,255.1); + if (temp <= 66) { + r = 255; + g = temp; + g = 99.470802 * log(g) - 161.119568; + if (temp <= 19) { + b = 0; + } else { + b = temp-10; + b = 138.517731 * log(b) - 305.044793; } + } else { + r = temp - 60; + r = 329.698727 * pow(r, -0.13320476); + g = temp - 60; + g = 288.12217 * pow(g, -0.07551485 ); + b = 255; } + + rgb[0] = (byte)constrain(r,0.1,255.1); + rgb[1] = (byte)constrain(g,0.1,255.1); + rgb[2] = (byte)constrain(b,0.1,255.1); + } else if (_mode == EspalexaColorMode::hs) { float h = ((float)_hue)/65535.0; @@ -226,7 +217,7 @@ uint32_t EspalexaDevice::getRGB() rgb[1] = 255.0*g; rgb[2] = 255.0*b; } - _rgb = ((rgb[3] << 24) | (rgb[0] << 16) | (rgb[1] << 8) | (rgb[2])); //white value is only >0 if Alexa did provide a CT value, RGB colors will not be touched. + _rgb = ((rgb[0] << 16) | (rgb[1] << 8) | (rgb[2])); return _rgb; } diff --git a/wled00/src/dependencies/espalexa/EspalexaDevice.h b/wled00/src/dependencies/espalexa/EspalexaDevice.h index a6644916..4785591f 100644 --- a/wled00/src/dependencies/espalexa/EspalexaDevice.h +++ b/wled00/src/dependencies/espalexa/EspalexaDevice.h @@ -5,9 +5,9 @@ typedef class EspalexaDevice; -typedef void (*BrightnessCallbackFunction) (uint8_t b); -typedef void (*DeviceCallbackFunction) (EspalexaDevice* d); -typedef void (*ColorCallbackFunction) (uint8_t br, uint32_t col); +typedef std::function BrightnessCallbackFunction; +typedef std::function DeviceCallbackFunction; +typedef std::function ColorCallbackFunction; enum class EspalexaColorMode : uint8_t { none = 0, ct = 1, hs = 2, xy = 3 }; enum class EspalexaDeviceType : uint8_t { onoff = 0, dimmable = 1, whitespectrum = 2, color = 3, extendedcolor = 4 }; diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 55f714f5..7c64796d 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -7,7 +7,6 @@ * @author Christian Schwinne */ - //ESP8266-01 (blue) got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.4.2 and the setting 512K(No SPIFFS). //ESP8266-01 (black) has 1MB flash and can thus fit the whole program. Use 1M(64K SPIFFS). @@ -119,7 +118,7 @@ #endif //version code in format yymmddb (b = daily build) -#define VERSION 2003221 +#define VERSION 2003222 char versionString[] = "0.9.1"; diff --git a/wled00/wled12_alexa.ino b/wled00/wled12_alexa.ino index 9c27041b..5163640e 100644 --- a/wled00/wled12_alexa.ino +++ b/wled00/wled12_alexa.ino @@ -61,12 +61,28 @@ void onAlexaChange(EspalexaDevice* dev) colorUpdated(NOTIFIER_CALL_MODE_ALEXA); } else //color { - uint32_t color = espalexaDevice->getRGB(); - col[3] = ((color >> 24) & 0xFF); // white color from Alexa is "pure white only" - col[0] = ((color >> 16) & 0xFF); - col[1] = ((color >> 8) & 0xFF); - col[2] = (color & 0xFF); - if (useRGBW && col[3] == 0) colorRGBtoRGBW(col); // do not touch white value if EspalexaDevice.cpp did set the white channel + if (espalexaDevice->getColorMode() == EspalexaColorMode::ct) //shade of white + { + uint16_t ct = espalexaDevice->getCt(); + if (useRGBW) + { + switch (ct) { //these values empirically look good on RGBW + case 199: col[0]=255; col[1]=255; col[2]=255; col[3]=255; break; + case 234: col[0]=127; col[1]=127; col[2]=127; col[3]=255; break; + case 284: col[0]= 0; col[1]= 0; col[2]= 0; col[3]=255; break; + case 350: col[0]=130; col[1]= 90; col[2]= 0; col[3]=255; break; + case 383: col[0]=255; col[1]=153; col[2]= 0; col[3]=255; break; + } + } else { + colorCTtoRGB(ct, col); + } + } else { + uint32_t color = espalexaDevice->getRGB(); + + col[0] = ((color >> 16) & 0xFF); + col[1] = ((color >> 8) & 0xFF); + col[2] = ( color & 0xFF); + } colorUpdated(NOTIFIER_CALL_MODE_ALEXA); } } diff --git a/wled00/wled14_colors.ino b/wled00/wled14_colors.ino index c8ca0dcb..a1db307a 100644 --- a/wled00/wled14_colors.ino +++ b/wled00/wled14_colors.ino @@ -57,10 +57,9 @@ void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) //hue, sat to rgb case 4: rgb[0]=t,rgb[1]=p,rgb[2]=255;break; case 5: rgb[0]=255,rgb[1]=p,rgb[2]=q; } - if (useRGBW) colorRGBtoRGBW(col); + if (useRGBW && strip.rgbwMode == RGBW_MODE_LEGACY) colorRGBtoRGBW(col); } -#ifndef WLED_DISABLE_HUESYNC void colorCTtoRGB(uint16_t mired, byte* rgb) //white spectrum to rgb { //this is only an approximation using WS2812B with gamma correction enabled @@ -81,9 +80,10 @@ void colorCTtoRGB(uint16_t mired, byte* rgb) //white spectrum to rgb } else { rgb[0]=237;rgb[1]=255;rgb[2]=239;//150 } - if (useRGBW) colorRGBtoRGBW(col); + if (useRGBW && strip.rgbwMode == RGBW_MODE_LEGACY) colorRGBtoRGBW(col); } +#ifndef WLED_DISABLE_HUESYNC void colorXYtoRGB(float x, float y, byte* rgb) //coordinates to rgb (https://www.developers.meethue.com/documentation/color-conversions-rgb-xy) { float z = 1.0f - x - y; From 594c0b85507ec7e5ae29cfa72102d6a2aa58ba08 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Wed, 25 Mar 2020 04:00:55 -0400 Subject: [PATCH 171/232] Transform ino to h/cpp. Class WLED created. --- wled00/wled.cpp | 503 ++++++++++++++ wled00/wled.h | 529 +++++++++++++++ wled00/wled00.ino | 612 +----------------- wled00/wled05_init.ino | 339 ---------- ...{wled06_usermod.ino => wled06_usermod.cpp} | 1 + wled00/{wled12_alexa.ino => wled_alexa.cpp} | 10 +- wled00/wled_alexa.h | 16 + wled00/{wled16_blynk.ino => wled_blynk.cpp} | 6 +- wled00/wled_blynk.h | 13 + wled00/{wled09_button.ino => wled_button.cpp} | 2 + wled00/wled_button.h | 12 + wled00/{wled14_colors.ino => wled_colors.cpp} | 12 +- wled00/wled_colors.h | 20 + ...{wled13_cronixie.ino => wled_cronixie.cpp} | 20 +- wled00/wled_cronixie.h | 13 + wled00/{wled21_dmx.ino => wled_dmx.cpp} | 9 +- wled00/wled_dmx.h | 11 + wled00/{wled01_eeprom.ino => wled_eeprom.cpp} | 6 +- wled00/wled_eeprom.h | 22 + wled00/{wled04_file.ino => wled_file.cpp} | 14 +- wled00/wled_file.h | 12 + wled00/{wled15_hue.ino => wled_hue.cpp} | 7 +- wled00/wled_hue.h | 10 + wled00/{wled20_ir.ino => wled_ir.cpp} | 5 +- wled00/wled_ir.h | 9 + wled00/{wled19_json.ino => wled_json.cpp} | 5 +- wled00/wled_json.h | 15 + wled00/{wled08_led.ino => wled_led.cpp} | 8 +- wled00/wled_led.h | 19 + wled00/{wled17_mqtt.ino => wled_mqtt.cpp} | 5 +- wled00/wled_mqtt.h | 9 + wled00/{wled07_notify.ino => wled_notify.cpp} | 6 +- wled00/wled_notify.h | 17 + wled00/{wled10_ntp.ino => wled_ntp.cpp} | 62 +- wled00/wled_ntp.h | 76 +++ wled00/{wled11_ol.ino => wled_overlay.cpp} | 5 +- wled00/wled_overlay.h | 12 + wled00/{wled18_server.ino => wled_server.cpp} | 7 +- wled00/wled_server.h | 18 + wled00/{wled03_set.ino => wled_set.cpp} | 4 + wled00/wled_set.h | 12 + wled00/{wled02_xml.ino => wled_xml.cpp} | 7 +- wled00/wled_xml.h | 15 + 43 files changed, 1441 insertions(+), 1074 deletions(-) create mode 100644 wled00/wled.cpp create mode 100644 wled00/wled.h delete mode 100644 wled00/wled05_init.ino rename wled00/{wled06_usermod.ino => wled06_usermod.cpp} (97%) rename wled00/{wled12_alexa.ino => wled_alexa.cpp} (89%) create mode 100644 wled00/wled_alexa.h rename wled00/{wled16_blynk.ino => wled_blynk.cpp} (94%) create mode 100644 wled00/wled_blynk.h rename wled00/{wled09_button.ino => wled_button.cpp} (98%) create mode 100644 wled00/wled_button.h rename wled00/{wled14_colors.ino => wled_colors.cpp} (96%) create mode 100644 wled00/wled_colors.h rename wled00/{wled13_cronixie.ino => wled_cronixie.cpp} (97%) create mode 100644 wled00/wled_cronixie.h rename wled00/{wled21_dmx.ino => wled_dmx.cpp} (89%) create mode 100644 wled00/wled_dmx.h rename wled00/{wled01_eeprom.ino => wled_eeprom.cpp} (99%) create mode 100644 wled00/wled_eeprom.h rename wled00/{wled04_file.ino => wled_file.cpp} (95%) create mode 100644 wled00/wled_file.h rename wled00/{wled15_hue.ino => wled_hue.cpp} (99%) create mode 100644 wled00/wled_hue.h rename wled00/{wled20_ir.ino => wled_ir.cpp} (99%) create mode 100644 wled00/wled_ir.h rename wled00/{wled19_json.ino => wled_json.cpp} (99%) create mode 100644 wled00/wled_json.h rename wled00/{wled08_led.ino => wled_led.cpp} (98%) create mode 100644 wled00/wled_led.h rename wled00/{wled17_mqtt.ino => wled_mqtt.cpp} (98%) create mode 100644 wled00/wled_mqtt.h rename wled00/{wled07_notify.ino => wled_notify.cpp} (99%) create mode 100644 wled00/wled_notify.h rename wled00/{wled10_ntp.ino => wled_ntp.cpp} (58%) create mode 100644 wled00/wled_ntp.h rename wled00/{wled11_ol.ino => wled_overlay.cpp} (98%) create mode 100644 wled00/wled_overlay.h rename wled00/{wled18_server.ino => wled_server.cpp} (99%) create mode 100644 wled00/wled_server.h rename wled00/{wled03_set.ino => wled_set.cpp} (99%) create mode 100644 wled00/wled_set.h rename wled00/{wled02_xml.ino => wled_xml.cpp} (99%) create mode 100644 wled00/wled_xml.h diff --git a/wled00/wled.cpp b/wled00/wled.cpp new file mode 100644 index 00000000..50a83f27 --- /dev/null +++ b/wled00/wled.cpp @@ -0,0 +1,503 @@ +#include "wled.h" + +WLED::WLED() { + +} + +//turns all LEDs off and restarts ESP +void WLED::reset() +{ + briT = 0; + long dly = millis(); + while (millis() - dly < 250) + { + yield(); //enough time to send response to client + } + setAllLeds(); + DEBUG_PRINTLN("MODULE RESET"); + ESP.restart(); +} + +bool oappendi(int i) +{ + char s[11]; + sprintf(s, "%ld", i); + return oappend(s); +} + +bool oappend(const char *txt) +{ + uint16_t len = strlen(txt); + if (olen + len >= OMAX) + return false; //buffer full + strcpy(obuf + olen, txt); + olen += len; + return true; +} + +void WLED::loop() +{ + handleIR(); //2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too + handleConnection(); + handleSerial(); + handleNotifications(); + handleTransitions(); +#ifdef WLED_ENABLE_DMX + handleDMX(); +#endif + userLoop(); + + yield(); + handleIO(); + handleIR(); + handleNetworkTime(); + handleAlexa(); + + handleOverlays(); + yield(); +#ifdef WLED_USE_ANALOG_LEDS + strip.setRgbwPwm(); +#endif + + if (doReboot) + reset(); + + if (!realtimeMode) //block stuff if WARLS/Adalight is enabled + { + if (apActive) + dnsServer.processNextRequest(); +#ifndef WLED_DISABLE_OTA + if (WLED_CONNECTED && aOtaEnabled) + ArduinoOTA.handle(); +#endif + handleNightlight(); + yield(); + + handleHue(); + handleBlynk(); + + yield(); + if (!offMode) + strip.service(); + } + yield(); +#ifdef ESP8266 + MDNS.update(); +#endif + if (millis() - lastMqttReconnectAttempt > 30000) + initMqtt(); + +//DEBUG serial logging +#ifdef WLED_DEBUG + if (millis() - debugTime > 9999) + { + DEBUG_PRINTLN("---DEBUG INFO---"); + DEBUG_PRINT("Runtime: "); + DEBUG_PRINTLN(millis()); + DEBUG_PRINT("Unix time: "); + DEBUG_PRINTLN(now()); + DEBUG_PRINT("Free heap: "); + DEBUG_PRINTLN(ESP.getFreeHeap()); + DEBUG_PRINT("Wifi state: "); + DEBUG_PRINTLN(WiFi.status()); + if (WiFi.status() != lastWifiState) + { + wifiStateChangedTime = millis(); + } + lastWifiState = WiFi.status(); + DEBUG_PRINT("State time: "); + DEBUG_PRINTLN(wifiStateChangedTime); + DEBUG_PRINT("NTP last sync: "); + DEBUG_PRINTLN(ntpLastSyncTime); + DEBUG_PRINT("Client IP: "); + DEBUG_PRINTLN(WiFi.localIP()); + DEBUG_PRINT("Loops/sec: "); + DEBUG_PRINTLN(loops / 10); + loops = 0; + debugTime = millis(); + } + loops++; +#endif // WLED_DEBU +} + +void WLED::wledInit() +{ + EEPROM.begin(EEPSIZE); + ledCount = EEPROM.read(229) + ((EEPROM.read(398) << 8) & 0xFF00); + if (ledCount > MAX_LEDS || ledCount == 0) + ledCount = 30; + +#ifdef ESP8266 +#if LEDPIN == 3 + if (ledCount > MAX_LEDS_DMA) + ledCount = MAX_LEDS_DMA; //DMA method uses too much ram +#endif +#endif + Serial.begin(115200); + Serial.setTimeout(50); + DEBUG_PRINTLN(); + DEBUG_PRINT("---WLED "); + DEBUG_PRINT(versionString); + DEBUG_PRINT(" "); + DEBUG_PRINT(VERSION); + DEBUG_PRINTLN(" INIT---"); +#ifdef ARDUINO_ARCH_ESP32 + DEBUG_PRINT("esp32 "); + DEBUG_PRINTLN(ESP.getSdkVersion()); +#else + DEBUG_PRINT("esp8266 "); + DEBUG_PRINTLN(ESP.getCoreVersion()); +#endif + int heapPreAlloc = ESP.getFreeHeap(); + DEBUG_PRINT("heap "); + DEBUG_PRINTLN(ESP.getFreeHeap()); + + strip.init(EEPROM.read(372), ledCount, EEPROM.read(2204)); //init LEDs quickly + strip.setBrightness(0); + + DEBUG_PRINT("LEDs inited. heap usage ~"); + DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap()); + +#ifndef WLED_DISABLE_FILESYSTEM +#ifdef ARDUINO_ARCH_ESP32 + SPIFFS.begin(true); +#endif + SPIFFS.begin(); +#endif + + DEBUG_PRINTLN("Load EEPROM"); + loadSettingsFromEEPROM(true); + beginStrip(); + userSetup(); + if (strcmp(clientSSID, DEFAULT_CLIENT_SSID) == 0) + showWelcomePage = true; + WiFi.persistent(false); + + if (macroBoot > 0) + applyMacro(macroBoot); + Serial.println("Ada"); + + //generate module IDs + escapedMac = WiFi.macAddress(); + escapedMac.replace(":", ""); + escapedMac.toLowerCase(); + if (strcmp(cmDNS, "x") == 0) //fill in unique mdns default + { + strcpy(cmDNS, "wled-"); + sprintf(cmDNS + 5, "%*s", 6, escapedMac.c_str() + 6); + } + if (mqttDeviceTopic[0] == 0) + { + strcpy(mqttDeviceTopic, "wled/"); + sprintf(mqttDeviceTopic + 5, "%*s", 6, escapedMac.c_str() + 6); + } + if (mqttClientID[0] == 0) + { + strcpy(mqttClientID, "WLED-"); + sprintf(mqttClientID + 5, "%*s", 6, escapedMac.c_str() + 6); + } + + strip.service(); + +#ifndef WLED_DISABLE_OTA + if (aOtaEnabled) + { + ArduinoOTA.onStart([]() { +#ifdef ESP8266 + wifi_set_sleep_type(NONE_SLEEP_T); +#endif + DEBUG_PRINTLN("Start ArduinoOTA"); + }); + if (strlen(cmDNS) > 0) + ArduinoOTA.setHostname(cmDNS); + } +#endif +#ifdef WLED_ENABLE_DMX + dmx.init(512); // initialize with bus length +#endif + //HTTP server page init + initServer(); +} + +void WLED::beginStrip() +{ + // Initialize NeoPixel Strip and button + strip.setShowCallback(handleOverlayDraw); + +#ifdef BTNPIN + pinMode(BTNPIN, INPUT_PULLUP); +#endif + + if (bootPreset > 0) + applyPreset(bootPreset, turnOnAtBoot); + colorUpdated(NOTIFIER_CALL_MODE_INIT); + +//init relay pin +#if RLYPIN >= 0 + pinMode(RLYPIN, OUTPUT); +#if RLYMDE + digitalWrite(RLYPIN, bri); +#else + digitalWrite(RLYPIN, !bri); +#endif +#endif + + //disable button if it is "pressed" unintentionally +#ifdef BTNPIN + if (digitalRead(BTNPIN) == LOW) + buttonEnabled = false; +#else + buttonEnabled = false; +#endif +} + +void WLED::initAP(bool resetAP = false) +{ + if (apBehavior == AP_BEHAVIOR_BUTTON_ONLY && !resetAP) + return; + + if (!apSSID[0] || resetAP) + strcpy(apSSID, "WLED-AP"); + if (resetAP) + strcpy(apPass, DEFAULT_AP_PASS); + DEBUG_PRINT("Opening access point "); + DEBUG_PRINTLN(apSSID); + WiFi.softAPConfig(IPAddress(4, 3, 2, 1), IPAddress(4, 3, 2, 1), IPAddress(255, 255, 255, 0)); + WiFi.softAP(apSSID, apPass, apChannel, apHide); + + if (!apActive) //start captive portal if AP active + { + DEBUG_PRINTLN("Init AP interfaces"); + server.begin(); + if (udpPort > 0 && udpPort != ntpLocalPort) + { + udpConnected = notifierUdp.begin(udpPort); + } + if (udpRgbPort > 0 && udpRgbPort != ntpLocalPort && udpRgbPort != udpPort) + { + udpRgbConnected = rgbUdp.begin(udpRgbPort); + } + + dnsServer.setErrorReplyCode(DNSReplyCode::NoError); + dnsServer.start(53, "*", WiFi.softAPIP()); + } + apActive = true; +} + +void WLED::initConnection() +{ + WiFi.disconnect(); //close old connections +#ifdef ESP8266 + WiFi.setPhyMode(WIFI_PHY_MODE_11N); +#endif + + if (staticIP[0] != 0 && staticGateway[0] != 0) + { + WiFi.config(staticIP, staticGateway, staticSubnet, IPAddress(8, 8, 8, 8)); + } + else + { + WiFi.config(0U, 0U, 0U); + } + + lastReconnectAttempt = millis(); + + if (!WLED_WIFI_CONFIGURED) + { + DEBUG_PRINT("No connection configured. "); + if (!apActive) + initAP(); //instantly go to ap mode + return; + } + else if (!apActive) + { + if (apBehavior == AP_BEHAVIOR_ALWAYS) + { + initAP(); + } + else + { + DEBUG_PRINTLN("Access point disabled."); + WiFi.softAPdisconnect(true); + } + } + showWelcomePage = false; + + DEBUG_PRINT("Connecting to "); + DEBUG_PRINT(clientSSID); + DEBUG_PRINTLN("..."); + +#ifdef ESP8266 + WiFi.hostname(serverDescription); +#endif + + WiFi.begin(clientSSID, clientPass); + +#ifdef ARDUINO_ARCH_ESP32 + WiFi.setSleep(!noWifiSleep); + WiFi.setHostname(serverDescription); +#else + wifi_set_sleep_type((noWifiSleep) ? NONE_SLEEP_T : MODEM_SLEEP_T); +#endif +} + +void WLED::initInterfaces() +{ + DEBUG_PRINTLN("Init STA interfaces"); + + if (hueIP[0] == 0) + { + hueIP[0] = WiFi.localIP()[0]; + hueIP[1] = WiFi.localIP()[1]; + hueIP[2] = WiFi.localIP()[2]; + } + + //init Alexa hue emulation + if (alexaEnabled) + alexaInit(); + +#ifndef WLED_DISABLE_OTA + if (aOtaEnabled) + ArduinoOTA.begin(); +#endif + + strip.service(); + // Set up mDNS responder: + if (strlen(cmDNS) > 0) + { + if (!aOtaEnabled) + MDNS.begin(cmDNS); + + DEBUG_PRINTLN("mDNS started"); + MDNS.addService("http", "tcp", 80); + MDNS.addService("wled", "tcp", 80); + MDNS.addServiceTxt("wled", "tcp", "mac", escapedMac.c_str()); + } + server.begin(); + + if (udpPort > 0 && udpPort != ntpLocalPort) + { + udpConnected = notifierUdp.begin(udpPort); + if (udpConnected && udpRgbPort != udpPort) + udpRgbConnected = rgbUdp.begin(udpRgbPort); + } + if (ntpEnabled) + ntpConnected = ntpUdp.begin(ntpLocalPort); + + initBlynk(blynkApiKey); + e131.begin((e131Multicast) ? E131_MULTICAST : E131_UNICAST, e131Universe, E131_MAX_UNIVERSE_COUNT); + reconnectHue(); + initMqtt(); + interfacesInited = true; + wasConnected = true; +} + +byte stacO = 0; +uint32_t lastHeap; +unsigned long heapTime = 0; + +void WLED::handleConnection() +{ + if (millis() < 2000 && (!WLED_WIFI_CONFIGURED || apBehavior == AP_BEHAVIOR_ALWAYS)) + return; + if (lastReconnectAttempt == 0) + initConnection(); + + //reconnect WiFi to clear stale allocations if heap gets too low + if (millis() - heapTime > 5000) + { + uint32_t heap = ESP.getFreeHeap(); + if (heap < 9000 && lastHeap < 9000) + { + DEBUG_PRINT("Heap too low! "); + DEBUG_PRINTLN(heap); + forceReconnect = true; + } + lastHeap = heap; + heapTime = millis(); + } + + byte stac = 0; + if (apActive) + { +#ifdef ESP8266 + stac = wifi_softap_get_station_num(); +#else + wifi_sta_list_t stationList; + esp_wifi_ap_get_sta_list(&stationList); + stac = stationList.num; +#endif + if (stac != stacO) + { + stacO = stac; + DEBUG_PRINT("Connected AP clients: "); + DEBUG_PRINTLN(stac); + if (!WLED_CONNECTED && WLED_WIFI_CONFIGURED) + { //trying to connect, but not connected + if (stac) + WiFi.disconnect(); //disable search so that AP can work + else + initConnection(); //restart search + } + } + } + if (forceReconnect) + { + DEBUG_PRINTLN("Forcing reconnect."); + initConnection(); + interfacesInited = false; + forceReconnect = false; + wasConnected = false; + return; + } + if (!WLED_CONNECTED) + { + if (interfacesInited) + { + DEBUG_PRINTLN("Disconnected!"); + interfacesInited = false; + initConnection(); + } + if (millis() - lastReconnectAttempt > ((stac) ? 300000 : 20000) && WLED_WIFI_CONFIGURED) + initConnection(); + if (!apActive && millis() - lastReconnectAttempt > 12000 && (!wasConnected || apBehavior == AP_BEHAVIOR_NO_CONN)) + initAP(); + } + else if (!interfacesInited) + { //newly connected + DEBUG_PRINTLN(""); + DEBUG_PRINT("Connected! IP address: "); + DEBUG_PRINTLN(WiFi.localIP()); + initInterfaces(); + userConnected(); + + //shut down AP + if (apBehavior != AP_BEHAVIOR_ALWAYS && apActive) + { + dnsServer.stop(); + WiFi.softAPdisconnect(true); + apActive = false; + DEBUG_PRINTLN("Access point disabled."); + } + } +} + +//by https://github.com/tzapu/WiFiManager/blob/master/WiFiManager.cpp +int WLED::getSignalQuality(int rssi) +{ + int quality = 0; + + if (rssi <= -100) + { + quality = 0; + } + else if (rssi >= -50) + { + quality = 100; + } + else + { + quality = 2 * (rssi + 100); + } + return quality; +} \ No newline at end of file diff --git a/wled00/wled.h b/wled00/wled.h new file mode 100644 index 00000000..4f9b79af --- /dev/null +++ b/wled00/wled.h @@ -0,0 +1,529 @@ +#ifndef WLED_H +#define WLED_H + +/* + Main sketch, global variable declarations +*/ +/* + * @title WLED project sketch + * @version 0.9.1 + * @author Christian Schwinne + */ + +//ESP8266-01 (blue) got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.4.2 and the setting 512K(No SPIFFS). + +//ESP8266-01 (black) has 1MB flash and can thus fit the whole program. Use 1M(64K SPIFFS). +//Uncomment some of the following lines to disable features to compile for ESP8266-01 (max flash size 434kB): + +//You are required to disable over-the-air updates: +//#define WLED_DISABLE_OTA //saves 14kb + +//You need to choose some of these features to disable: +//#define WLED_DISABLE_ALEXA //saves 11kb +//#define WLED_DISABLE_BLYNK //saves 6kb +//#define WLED_DISABLE_CRONIXIE //saves 3kb +//#define WLED_DISABLE_HUESYNC //saves 4kb +//#define WLED_DISABLE_INFRARED //there is no pin left for this on ESP8266-01, saves 12kb +#define WLED_ENABLE_MQTT //saves 12kb +#define WLED_ENABLE_ADALIGHT //saves 500b only +//#define WLED_ENABLE_DMX //uses 3.5kb + +#define WLED_DISABLE_FILESYSTEM //SPIFFS is not used by any WLED feature yet +//#define WLED_ENABLE_FS_SERVING //Enable sending html file from SPIFFS before serving progmem version +//#define WLED_ENABLE_FS_EDITOR //enable /edit page for editing SPIFFS content. Will also be disabled with OTA lock + +//to toggle usb serial debug (un)comment the following line +//#define WLED_DEBUG + +//library inclusions +#include +#ifdef WLED_ENABLE_DMX +#include +DMXESPSerial dmx; +#endif +#ifdef ESP8266 +#include +#include +#include +extern "C" +{ +#include +} +#else //ESP32 +#include +#include "esp_wifi.h" +#include +#include +#include "SPIFFS.h" +#endif + +#include +#include +#include +#include +#ifndef WLED_DISABLE_OTA +#include +#endif +#include +#include "src/dependencies/time/TimeLib.h" +#include "src/dependencies/timezone/Timezone.h" +#ifndef WLED_DISABLE_ALEXA +#define ESPALEXA_ASYNC +#define ESPALEXA_NO_SUBPAGE +#define ESPALEXA_MAXDEVICES 1 +// #define ESPALEXA_DEBUG +#include "src/dependencies/espalexa/Espalexa.h" +#endif +#ifndef WLED_DISABLE_BLYNK +#include "src/dependencies/blynk/BlynkSimpleEsp.h" +#endif +#include "src/dependencies/e131/ESPAsyncE131.h" +#include "src/dependencies/async-mqtt-client/AsyncMqttClient.h" +#include "src/dependencies/json/AsyncJson-v6.h" +#include "src/dependencies/json/ArduinoJson-v6.h" +#include "html_ui.h" +#include "html_settings.h" +#include "html_other.h" +#include "FX.h" +#include "ir_codes.h" +#include "const.h" + +#ifndef CLIENT_SSID +#define CLIENT_SSID DEFAULT_CLIENT_SSID +#endif + +#ifndef CLIENT_PASS +#define CLIENT_PASS "" +#endif + +#if IR_PIN < 0 +#ifndef WLED_DISABLE_INFRARED +#define WLED_DISABLE_INFRARED +#endif +#endif + +#ifndef WLED_DISABLE_INFRARED +#include +#include +#include +#endif + +// remove flicker because PWM signal of RGB channels can become out of phase +#if defined(WLED_USE_ANALOG_LEDS) && defined(ESP8266) +#include "src/dependencies/arduino/core_esp8266_waveform.h" +#endif + +// enable additional debug output +#ifdef WLED_DEBUG +#ifndef ESP8266 +#include +#endif +#endif + +//version code in format yymmddb (b = daily build) +#define VERSION 2003222 + +char versionString[] = "0.9.1"; + +//AP and OTA default passwords (for maximum change them!) +char apPass[65] = DEFAULT_AP_PASS; +char otaPass[33] = DEFAULT_OTA_PASS; + +//Hardware CONFIG (only changeble HERE, not at runtime) +//LED strip pin, button pin and IR pin changeable in NpbWrapper.h! + +byte auxDefaultState = 0; //0: input 1: high 2: low +byte auxTriggeredState = 0; //0: input 1: high 2: low +char ntpServerName[33] = "0.wled.pool.ntp.org"; //NTP server to use + +//WiFi CONFIG (all these can be changed via web UI, no need to set them here) +char clientSSID[33] = CLIENT_SSID; +char clientPass[65] = CLIENT_PASS; +char cmDNS[33] = "x"; //mDNS address (placeholder, will be replaced by wledXXXXXXXXXXXX.local) +char apSSID[33] = ""; //AP off by default (unless setup) +byte apChannel = 1; //2.4GHz WiFi AP channel (1-13) +byte apHide = 0; //hidden AP SSID +byte apBehavior = AP_BEHAVIOR_BOOT_NO_CONN; //access point opens when no connection after boot by default +IPAddress staticIP(0, 0, 0, 0); //static IP of ESP +IPAddress staticGateway(0, 0, 0, 0); //gateway (router) IP +IPAddress staticSubnet(255, 255, 255, 0); //most common subnet in home networks +bool noWifiSleep = false; //disabling modem sleep modes will increase heat output and power usage, but may help with connection issues + +//LED CONFIG +uint16_t ledCount = 30; //overcurrent prevented by ABL +bool useRGBW = false; //SK6812 strips can contain an extra White channel +#define ABL_MILLIAMPS_DEFAULT 850; //auto lower brightness to stay close to milliampere limit +bool turnOnAtBoot = true; //turn on LEDs at power-up +byte bootPreset = 0; //save preset to load after power-up + +byte col[]{255, 160, 0, 0}; //current RGB(W) primary color. col[] should be updated if you want to change the color. +byte colSec[]{0, 0, 0, 0}; //current RGB(W) secondary color +byte briS = 128; //default brightness + +byte nightlightTargetBri = 0; //brightness after nightlight is over +byte nightlightDelayMins = 60; +bool nightlightFade = true; //if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over +bool nightlightColorFade = false; //if enabled, light will gradually fade color from primary to secondary color. +bool fadeTransition = true; //enable crossfading color transition +uint16_t transitionDelay = 750; //default crossfade duration in ms + +bool skipFirstLed = false; //ignore first LED in strip (useful if you need the LED as signal repeater) +byte briMultiplier = 100; //% of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) + +//User Interface CONFIG +char serverDescription[33] = "WLED"; //Name of module +bool syncToggleReceive = false; //UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise + +//Sync CONFIG +bool buttonEnabled = true; +byte irEnabled = 0; //Infrared receiver + +uint16_t udpPort = 21324; //WLED notifier default port +uint16_t udpRgbPort = 19446; //Hyperion port + +bool receiveNotificationBrightness = true; //apply brightness from incoming notifications +bool receiveNotificationColor = true; //apply color +bool receiveNotificationEffects = true; //apply effects setup +bool notifyDirect = false; //send notification if change via UI or HTTP API +bool notifyButton = false; //send if updated by button or infrared remote +bool notifyAlexa = false; //send notification if updated via Alexa +bool notifyMacro = false; //send notification for macro +bool notifyHue = true; //send notification if Hue light changes +bool notifyTwice = false; //notifications use UDP: enable if devices don't sync reliably + +bool alexaEnabled = true; //enable device discovery by Amazon Echo +char alexaInvocationName[33] = "Light"; //speech control name of device. Choose something voice-to-text can understand + +char blynkApiKey[36] = ""; //Auth token for Blynk server. If empty, no connection will be made + +uint16_t realtimeTimeoutMs = 2500; //ms timeout of realtime mode before returning to normal mode +int arlsOffset = 0; //realtime LED offset +bool receiveDirect = true; //receive UDP realtime +bool arlsDisableGammaCorrection = true; //activate if gamma correction is handled by the source +bool arlsForceMaxBri = false; //enable to force max brightness if source has very dark colors that would be black + +#define E131_MAX_UNIVERSE_COUNT 9 +uint16_t e131Universe = 1; //settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) +uint8_t DMXMode = DMX_MODE_MULTIPLE_RGB; //DMX mode (s.a.) +uint16_t DMXAddress = 1; //DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] +uint8_t DMXOldDimmer = 0; //only update brightness on change +uint8_t e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; //to detect packet loss +bool e131Multicast = false; //multicast or unicast +bool e131SkipOutOfSequence = false; //freeze instead of flickering + +bool mqttEnabled = false; +char mqttDeviceTopic[33] = ""; //main MQTT topic (individual per device, default is wled/mac) +char mqttGroupTopic[33] = "wled/all"; //second MQTT topic (for example to group devices) +char mqttServer[33] = ""; //both domains and IPs should work (no SSL) +char mqttUser[41] = ""; //optional: username for MQTT auth +char mqttPass[41] = ""; //optional: password for MQTT auth +char mqttClientID[41] = ""; //override the client ID +uint16_t mqttPort = 1883; + +bool huePollingEnabled = false; //poll hue bridge for light state +uint16_t huePollIntervalMs = 2500; //low values (< 1sec) may cause lag but offer quicker response +char hueApiKey[47] = "api"; //key token will be obtained from bridge +byte huePollLightId = 1; //ID of hue lamp to sync to. Find the ID in the hue app ("about" section) +IPAddress hueIP = (0, 0, 0, 0); //IP address of the bridge +bool hueApplyOnOff = true; +bool hueApplyBri = true; +bool hueApplyColor = true; + +//Time CONFIG +bool ntpEnabled = false; //get internet time. Only required if you use clock overlays or time-activated macros +bool useAMPM = false; //12h/24h clock format +byte currentTimezone = 0; //Timezone ID. Refer to timezones array in wled10_ntp.ino +int utcOffsetSecs = 0; //Seconds to offset from UTC before timzone calculation + +byte overlayDefault = 0; //0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie +byte overlayMin = 0, overlayMax = ledCount - 1; //boundaries of overlay mode + +byte analogClock12pixel = 0; //The pixel in your strip where "midnight" would be +bool analogClockSecondsTrail = false; //Display seconds as trail of LEDs instead of a single pixel +bool analogClock5MinuteMarks = false; //Light pixels at every 5-minute position + +char cronixieDisplay[7] = "HHMMSS"; //Cronixie Display mask. See wled13_cronixie.ino +bool cronixieBacklight = true; //Allow digits to be back-illuminated + +bool countdownMode = false; //Clock will count down towards date +byte countdownYear = 20, countdownMonth = 1; //Countdown target date, year is last two digits +byte countdownDay = 1, countdownHour = 0; +byte countdownMin = 0, countdownSec = 0; + +byte macroBoot = 0; //macro loaded after startup +byte macroNl = 0; //after nightlight delay over +byte macroCountdown = 0; +byte macroAlexaOn = 0, macroAlexaOff = 0; +byte macroButton = 0, macroLongPress = 0, macroDoublePress = 0; + +//Security CONFIG +bool otaLock = false; //prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks +bool wifiLock = false; //prevents access to WiFi settings when OTA lock is enabled +bool aOtaEnabled = true; //ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on + +uint16_t userVar0 = 0, userVar1 = 0; + +#ifdef WLED_ENABLE_DMX +//dmx CONFIG +byte DMXChannels = 7; // number of channels per fixture +byte DMXFixtureMap[15] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +// assigns the different channels to different functions. See wled21_dmx.ino for more information. +uint16_t DMXGap = 10; // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. +uint16_t DMXStart = 10; // start address of the first fixture +#endif + +//internal global variable declarations +//wifi +bool apActive = false; +bool forceReconnect = false; +uint32_t lastReconnectAttempt = 0; +bool interfacesInited = false; +bool wasConnected = false; + +//color +byte colOld[]{0, 0, 0, 0}; //color before transition +byte colT[]{0, 0, 0, 0}; //color that is currently displayed on the LEDs +byte colIT[]{0, 0, 0, 0}; //color that was last sent to LEDs +byte colSecT[]{0, 0, 0, 0}; +byte colSecOld[]{0, 0, 0, 0}; +byte colSecIT[]{0, 0, 0, 0}; + +byte lastRandomIndex = 0; //used to save last random color so the new one is not the same + +//transitions +bool transitionActive = false; +uint16_t transitionDelayDefault = transitionDelay; +uint16_t transitionDelayTemp = transitionDelay; +unsigned long transitionStartTime; +float tperLast = 0; //crossfade transition progress, 0.0f - 1.0f +bool jsonTransitionOnce = false; + +//nightlight +bool nightlightActive = false; +bool nightlightActiveOld = false; +uint32_t nightlightDelayMs = 10; +uint8_t nightlightDelayMinsDefault = nightlightDelayMins; +unsigned long nightlightStartTime; +byte briNlT = 0; //current nightlight brightness +byte colNlT[]{0, 0, 0, 0}; //current nightlight color + +//brightness +unsigned long lastOnTime = 0; +bool offMode = !turnOnAtBoot; +byte bri = briS; +byte briOld = 0; +byte briT = 0; +byte briIT = 0; +byte briLast = 128; //brightness before turned off. Used for toggle function +byte whiteLast = 128; //white channel before turned off. Used for toggle function + +//button +bool buttonPressedBefore = false; +bool buttonLongPressed = false; +unsigned long buttonPressedTime = 0; +unsigned long buttonWaitTime = 0; + +//notifications +bool notifyDirectDefault = notifyDirect; +bool receiveNotifications = true; +unsigned long notificationSentTime = 0; +byte notificationSentCallMode = NOTIFIER_CALL_MODE_INIT; +bool notificationTwoRequired = false; + +//effects +byte effectCurrent = 0; +byte effectSpeed = 128; +byte effectIntensity = 128; +byte effectPalette = 0; + +//network +bool udpConnected = false, udpRgbConnected = false; + +//ui style +bool showWelcomePage = false; + +//hue +byte hueError = HUE_ERROR_INACTIVE; +//uint16_t hueFailCount = 0; +float hueXLast = 0, hueYLast = 0; +uint16_t hueHueLast = 0, hueCtLast = 0; +byte hueSatLast = 0, hueBriLast = 0; +unsigned long hueLastRequestSent = 0; +bool hueAuthRequired = false; +bool hueReceived = false; +bool hueStoreAllowed = false, hueNewKey = false; + +//overlays +byte overlayCurrent = overlayDefault; +byte overlaySpeed = 200; +unsigned long overlayRefreshMs = 200; +unsigned long overlayRefreshedTime; + +//cronixie +byte dP[]{0, 0, 0, 0, 0, 0}; +bool cronixieInit = false; + +//countdown +unsigned long countdownTime = 1514764800L; +bool countdownOverTriggered = true; + +//timer +byte lastTimerMinute = 0; +byte timerHours[] = {0, 0, 0, 0, 0, 0, 0, 0}; +byte timerMinutes[] = {0, 0, 0, 0, 0, 0, 0, 0}; +byte timerMacro[] = {0, 0, 0, 0, 0, 0, 0, 0}; +byte timerWeekday[] = {255, 255, 255, 255, 255, 255, 255, 255}; //weekdays to activate on +//bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity + +//blynk +bool blynkEnabled = false; + +//preset cycling +bool presetCyclingEnabled = false; +byte presetCycleMin = 1, presetCycleMax = 5; +uint16_t presetCycleTime = 1250; +unsigned long presetCycledTime = 0; +byte presetCycCurr = presetCycleMin; +bool presetApplyBri = true; +bool saveCurrPresetCycConf = false; + +//realtime +byte realtimeMode = REALTIME_MODE_INACTIVE; +IPAddress realtimeIP = (0, 0, 0, 0); +unsigned long realtimeTimeout = 0; + +//mqtt +long lastMqttReconnectAttempt = 0; +long lastInterfaceUpdate = 0; +byte interfaceUpdateCallMode = NOTIFIER_CALL_MODE_INIT; +char mqttStatusTopic[40] = ""; //this must be global because of async handlers + +#if AUXPIN >= 0 + //auxiliary debug pin +byte auxTime = 0; +unsigned long auxStartTime = 0; +bool auxActive = false, auxActiveBefore = false; +#endif + +//alexa udp +String escapedMac; +#ifndef WLED_DISABLE_ALEXA +Espalexa espalexa; +EspalexaDevice *espalexaDevice; +#endif + +//dns server +DNSServer dnsServer; + +//network time +bool ntpConnected = false; +time_t local = 0; +unsigned long ntpLastSyncTime = 999000000L; +unsigned long ntpPacketSentTime = 999000000L; +IPAddress ntpServerIP; +uint16_t ntpLocalPort = 2390; +#define NTP_PACKET_SIZE 48 + +//maximum number of LEDs - MAX_LEDS is coming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue +#define MAX_LEDS 1500 +#define MAX_LEDS_DMA 500 + +//string temp buffer (now stored in stack locally) +#define OMAX 2048 +char *obuf; +uint16_t olen = 0; + +//presets +uint16_t savedPresets = 0; +int8_t currentPreset = -1; +bool isPreset = false; + +byte errorFlag = 0; + +String messageHead, messageSub; +byte optionType; + +bool doReboot = false; //flag to initiate reboot from async handlers +bool doPublishMqtt = false; + +//server library objects +AsyncWebServer server(80); +AsyncClient *hueClient = NULL; +AsyncMqttClient *mqtt = NULL; + +//function prototypes +void colorFromUint32(uint32_t, bool = false); +void serveMessage(AsyncWebServerRequest *, uint16_t, String, String, byte); +void handleE131Packet(e131_packet_t *, IPAddress); +void arlsLock(uint32_t, byte); +void handleOverlayDraw(); + +//udp interface objects +WiFiUDP notifierUdp, rgbUdp; +WiFiUDP ntpUdp; +ESPAsyncE131 e131(handleE131Packet); +bool e131NewData = false; + +//led fx library object +WS2812FX strip = WS2812FX(); + +#define WLED_CONNECTED (WiFi.status() == WL_CONNECTED) +#define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID, DEFAULT_CLIENT_SSID) != 0) + +//debug macros +#ifdef WLED_DEBUG +#define DEBUG_PRINT(x) Serial.print(x) +#define DEBUG_PRINTLN(x) Serial.println(x) +#define DEBUG_PRINTF(x) Serial.printf(x) +unsigned long debugTime = 0; +int lastWifiState = 3; +unsigned long wifiStateChangedTime = 0; +int loops = 0; +#else +#define DEBUG_PRINT(x) +#define DEBUG_PRINTLN(x) +#define DEBUG_PRINTF(x) +#endif + + +// TODO: Inline? +//append new c string to temp buffer efficiently +bool oappend(const char *txt); +//append new number to temp buffer efficiently +bool oappendi(int i); + +class WLED +{ +public: + static WLED &instance() + { + static WLED instance; + return instance; + } + + WLED(); + + void wledInit(); + + void reset(); + void loop(); + + + //boot starts here + void setup() + { + wledInit(); + } + + void loop(); + +private: + void wledInit(); + void beginStrip(); + + void handleConnection(); + void initAP(bool resetAP = false); + void initConnection(); + void initInterfaces(); +}; +#endif // WLED_H diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 7c64796d..fc06f211 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -1,617 +1,13 @@ /* - Main sketch, global variable declarations + Arduino Studio support file. */ -/* - * @title WLED project sketch - * @version 0.9.1 - * @author Christian Schwinne - */ +#include "wled.h" -//ESP8266-01 (blue) got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.4.2 and the setting 512K(No SPIFFS). - -//ESP8266-01 (black) has 1MB flash and can thus fit the whole program. Use 1M(64K SPIFFS). -//Uncomment some of the following lines to disable features to compile for ESP8266-01 (max flash size 434kB): - -//You are required to disable over-the-air updates: -//#define WLED_DISABLE_OTA //saves 14kb - -//You need to choose some of these features to disable: -//#define WLED_DISABLE_ALEXA //saves 11kb -//#define WLED_DISABLE_BLYNK //saves 6kb -//#define WLED_DISABLE_CRONIXIE //saves 3kb -//#define WLED_DISABLE_HUESYNC //saves 4kb -//#define WLED_DISABLE_INFRARED //there is no pin left for this on ESP8266-01, saves 12kb -#define WLED_ENABLE_MQTT //saves 12kb -#define WLED_ENABLE_ADALIGHT //saves 500b only -//#define WLED_ENABLE_DMX //uses 3.5kb - -#define WLED_DISABLE_FILESYSTEM //SPIFFS is not used by any WLED feature yet -//#define WLED_ENABLE_FS_SERVING //Enable sending html file from SPIFFS before serving progmem version -//#define WLED_ENABLE_FS_EDITOR //enable /edit page for editing SPIFFS content. Will also be disabled with OTA lock - -//to toggle usb serial debug (un)comment the following line -//#define WLED_DEBUG - -//library inclusions -#include -#ifdef WLED_ENABLE_DMX - #include - DMXESPSerial dmx; -#endif -#ifdef ESP8266 - #include - #include - #include - extern "C" { - #include - } -#else //ESP32 - #include - #include "esp_wifi.h" - #include - #include - #include "SPIFFS.h" -#endif - -#include -#include -#include -#include -#ifndef WLED_DISABLE_OTA - #include -#endif -#include -#include "src/dependencies/time/TimeLib.h" -#include "src/dependencies/timezone/Timezone.h" -#ifndef WLED_DISABLE_ALEXA - #define ESPALEXA_ASYNC - #define ESPALEXA_NO_SUBPAGE - #define ESPALEXA_MAXDEVICES 1 - // #define ESPALEXA_DEBUG - #include "src/dependencies/espalexa/Espalexa.h" -#endif -#ifndef WLED_DISABLE_BLYNK - #include "src/dependencies/blynk/BlynkSimpleEsp.h" -#endif -#include "src/dependencies/e131/ESPAsyncE131.h" -#include "src/dependencies/async-mqtt-client/AsyncMqttClient.h" -#include "src/dependencies/json/AsyncJson-v6.h" -#include "src/dependencies/json/ArduinoJson-v6.h" -#include "html_ui.h" -#include "html_settings.h" -#include "html_other.h" -#include "FX.h" -#include "ir_codes.h" -#include "const.h" - -#ifndef CLIENT_SSID -#define CLIENT_SSID DEFAULT_CLIENT_SSID -#endif - -#ifndef CLIENT_PASS -#define CLIENT_PASS "" -#endif - - -#if IR_PIN < 0 - #ifndef WLED_DISABLE_INFRARED - #define WLED_DISABLE_INFRARED - #endif -#endif - -#ifndef WLED_DISABLE_INFRARED - #include - #include - #include -#endif - -// remove flicker because PWM signal of RGB channels can become out of phase -#if defined(WLED_USE_ANALOG_LEDS) && defined(ESP8266) - #include "src/dependencies/arduino/core_esp8266_waveform.h" -#endif - -// enable additional debug output -#ifdef WLED_DEBUG - #ifndef ESP8266 - #include - #endif -#endif - -//version code in format yymmddb (b = daily build) -#define VERSION 2003222 - -char versionString[] = "0.9.1"; - - -//AP and OTA default passwords (for maximum change them!) -char apPass[65] = DEFAULT_AP_PASS; -char otaPass[33] = DEFAULT_OTA_PASS; - - -//Hardware CONFIG (only changeble HERE, not at runtime) -//LED strip pin, button pin and IR pin changeable in NpbWrapper.h! - -byte auxDefaultState = 0; //0: input 1: high 2: low -byte auxTriggeredState = 0; //0: input 1: high 2: low -char ntpServerName[33] = "0.wled.pool.ntp.org";//NTP server to use - - -//WiFi CONFIG (all these can be changed via web UI, no need to set them here) -char clientSSID[33] = CLIENT_SSID; -char clientPass[65] = CLIENT_PASS; -char cmDNS[33] = "x"; //mDNS address (placeholder, will be replaced by wledXXXXXXXXXXXX.local) -char apSSID[33] = ""; //AP off by default (unless setup) -byte apChannel = 1; //2.4GHz WiFi AP channel (1-13) -byte apHide = 0; //hidden AP SSID -byte apBehavior = AP_BEHAVIOR_BOOT_NO_CONN; //access point opens when no connection after boot by default -IPAddress staticIP(0, 0, 0, 0); //static IP of ESP -IPAddress staticGateway(0, 0, 0, 0); //gateway (router) IP -IPAddress staticSubnet(255, 255, 255, 0); //most common subnet in home networks -bool noWifiSleep = false; //disabling modem sleep modes will increase heat output and power usage, but may help with connection issues - - -//LED CONFIG -uint16_t ledCount = 30; //overcurrent prevented by ABL -bool useRGBW = false; //SK6812 strips can contain an extra White channel -#define ABL_MILLIAMPS_DEFAULT 850; //auto lower brightness to stay close to milliampere limit -bool turnOnAtBoot = true; //turn on LEDs at power-up -byte bootPreset = 0; //save preset to load after power-up - -byte col[] {255, 160, 0, 0}; //current RGB(W) primary color. col[] should be updated if you want to change the color. -byte colSec[] {0, 0, 0, 0}; //current RGB(W) secondary color -byte briS = 128; //default brightness - -byte nightlightTargetBri = 0; //brightness after nightlight is over -byte nightlightDelayMins = 60; -bool nightlightFade = true; //if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over -bool nightlightColorFade = false; //if enabled, light will gradually fade color from primary to secondary color. -bool fadeTransition = true; //enable crossfading color transition -uint16_t transitionDelay = 750; //default crossfade duration in ms - -bool skipFirstLed = false; //ignore first LED in strip (useful if you need the LED as signal repeater) -byte briMultiplier = 100; //% of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) - - -//User Interface CONFIG -char serverDescription[33] = "WLED"; //Name of module -bool syncToggleReceive = false; //UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise - - -//Sync CONFIG -bool buttonEnabled = true; -byte irEnabled = 0; //Infrared receiver - -uint16_t udpPort = 21324; //WLED notifier default port -uint16_t udpRgbPort = 19446; //Hyperion port - -bool receiveNotificationBrightness = true; //apply brightness from incoming notifications -bool receiveNotificationColor = true; //apply color -bool receiveNotificationEffects = true; //apply effects setup -bool notifyDirect = false; //send notification if change via UI or HTTP API -bool notifyButton = false; //send if updated by button or infrared remote -bool notifyAlexa = false; //send notification if updated via Alexa -bool notifyMacro = false; //send notification for macro -bool notifyHue = true; //send notification if Hue light changes -bool notifyTwice = false; //notifications use UDP: enable if devices don't sync reliably - -bool alexaEnabled = true; //enable device discovery by Amazon Echo -char alexaInvocationName[33] = "Light"; //speech control name of device. Choose something voice-to-text can understand - -char blynkApiKey[36] = ""; //Auth token for Blynk server. If empty, no connection will be made - -uint16_t realtimeTimeoutMs = 2500; //ms timeout of realtime mode before returning to normal mode -int arlsOffset = 0; //realtime LED offset -bool receiveDirect = true; //receive UDP realtime -bool arlsDisableGammaCorrection = true; //activate if gamma correction is handled by the source -bool arlsForceMaxBri = false; //enable to force max brightness if source has very dark colors that would be black - -#define E131_MAX_UNIVERSE_COUNT 9 -uint16_t e131Universe = 1; //settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) -uint8_t DMXMode = DMX_MODE_MULTIPLE_RGB; //DMX mode (s.a.) -uint16_t DMXAddress = 1; //DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] -uint8_t DMXOldDimmer = 0; //only update brightness on change -uint8_t e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; //to detect packet loss -bool e131Multicast = false; //multicast or unicast -bool e131SkipOutOfSequence = false; //freeze instead of flickering - -bool mqttEnabled = false; -char mqttDeviceTopic[33] = ""; //main MQTT topic (individual per device, default is wled/mac) -char mqttGroupTopic[33] = "wled/all"; //second MQTT topic (for example to group devices) -char mqttServer[33] = ""; //both domains and IPs should work (no SSL) -char mqttUser[41] = ""; //optional: username for MQTT auth -char mqttPass[41] = ""; //optional: password for MQTT auth -char mqttClientID[41] = ""; //override the client ID -uint16_t mqttPort = 1883; - -bool huePollingEnabled = false; //poll hue bridge for light state -uint16_t huePollIntervalMs = 2500; //low values (< 1sec) may cause lag but offer quicker response -char hueApiKey[47] = "api"; //key token will be obtained from bridge -byte huePollLightId = 1; //ID of hue lamp to sync to. Find the ID in the hue app ("about" section) -IPAddress hueIP = (0, 0, 0, 0); //IP address of the bridge -bool hueApplyOnOff = true; -bool hueApplyBri = true; -bool hueApplyColor = true; - - -//Time CONFIG -bool ntpEnabled = false; //get internet time. Only required if you use clock overlays or time-activated macros -bool useAMPM = false; //12h/24h clock format -byte currentTimezone = 0; //Timezone ID. Refer to timezones array in wled10_ntp.ino -int utcOffsetSecs = 0; //Seconds to offset from UTC before timzone calculation - -byte overlayDefault = 0; //0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie -byte overlayMin = 0, overlayMax = ledCount - 1; //boundaries of overlay mode - -byte analogClock12pixel = 0; //The pixel in your strip where "midnight" would be -bool analogClockSecondsTrail = false; //Display seconds as trail of LEDs instead of a single pixel -bool analogClock5MinuteMarks = false; //Light pixels at every 5-minute position - -char cronixieDisplay[7] = "HHMMSS"; //Cronixie Display mask. See wled13_cronixie.ino -bool cronixieBacklight = true; //Allow digits to be back-illuminated - -bool countdownMode = false; //Clock will count down towards date -byte countdownYear = 20, countdownMonth = 1; //Countdown target date, year is last two digits -byte countdownDay = 1, countdownHour = 0; -byte countdownMin = 0, countdownSec = 0; - - -byte macroBoot = 0; //macro loaded after startup -byte macroNl = 0; //after nightlight delay over -byte macroCountdown = 0; -byte macroAlexaOn = 0, macroAlexaOff = 0; -byte macroButton = 0, macroLongPress = 0, macroDoublePress = 0; - - -//Security CONFIG -bool otaLock = false; //prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks -bool wifiLock = false; //prevents access to WiFi settings when OTA lock is enabled -bool aOtaEnabled = true; //ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on - - -uint16_t userVar0 = 0, userVar1 = 0; - -#ifdef WLED_ENABLE_DMX - //dmx CONFIG - byte DMXChannels = 7; // number of channels per fixture - byte DMXFixtureMap[15] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - // assigns the different channels to different functions. See wled21_dmx.ino for more information. - uint16_t DMXGap = 10; // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. - uint16_t DMXStart = 10; // start address of the first fixture -#endif - - -//internal global variable declarations -//wifi -bool apActive = false; -bool forceReconnect = false; -uint32_t lastReconnectAttempt = 0; -bool interfacesInited = false; -bool wasConnected = false; - -//color -byte colOld[] {0, 0, 0, 0}; //color before transition -byte colT[] {0, 0, 0, 0}; //color that is currently displayed on the LEDs -byte colIT[] {0, 0, 0, 0}; //color that was last sent to LEDs -byte colSecT[] {0, 0, 0, 0}; -byte colSecOld[] {0, 0, 0, 0}; -byte colSecIT[] {0, 0, 0, 0}; - -byte lastRandomIndex = 0; //used to save last random color so the new one is not the same - -//transitions -bool transitionActive = false; -uint16_t transitionDelayDefault = transitionDelay; -uint16_t transitionDelayTemp = transitionDelay; -unsigned long transitionStartTime; -float tperLast = 0; //crossfade transition progress, 0.0f - 1.0f -bool jsonTransitionOnce = false; - -//nightlight -bool nightlightActive = false; -bool nightlightActiveOld = false; -uint32_t nightlightDelayMs = 10; -uint8_t nightlightDelayMinsDefault = nightlightDelayMins; -unsigned long nightlightStartTime; -byte briNlT = 0; //current nightlight brightness -byte colNlT[] {0, 0, 0, 0}; //current nightlight color - -//brightness -unsigned long lastOnTime = 0; -bool offMode = !turnOnAtBoot; -byte bri = briS; -byte briOld = 0; -byte briT = 0; -byte briIT = 0; -byte briLast = 128; //brightness before turned off. Used for toggle function -byte whiteLast = 128; //white channel before turned off. Used for toggle function - -//button -bool buttonPressedBefore = false; -bool buttonLongPressed = false; -unsigned long buttonPressedTime = 0; -unsigned long buttonWaitTime = 0; - -//notifications -bool notifyDirectDefault = notifyDirect; -bool receiveNotifications = true; -unsigned long notificationSentTime = 0; -byte notificationSentCallMode = NOTIFIER_CALL_MODE_INIT; -bool notificationTwoRequired = false; - -//effects -byte effectCurrent = 0; -byte effectSpeed = 128; -byte effectIntensity = 128; -byte effectPalette = 0; - -//network -bool udpConnected = false, udpRgbConnected = false; - -//ui style -bool showWelcomePage = false; - -//hue -byte hueError = HUE_ERROR_INACTIVE; -//uint16_t hueFailCount = 0; -float hueXLast = 0, hueYLast = 0; -uint16_t hueHueLast = 0, hueCtLast = 0; -byte hueSatLast = 0, hueBriLast = 0; -unsigned long hueLastRequestSent = 0; -bool hueAuthRequired = false; -bool hueReceived = false; -bool hueStoreAllowed = false, hueNewKey = false; - -//overlays -byte overlayCurrent = overlayDefault; -byte overlaySpeed = 200; -unsigned long overlayRefreshMs = 200; -unsigned long overlayRefreshedTime; - -//cronixie -byte dP[] {0, 0, 0, 0, 0, 0}; -bool cronixieInit = false; - -//countdown -unsigned long countdownTime = 1514764800L; -bool countdownOverTriggered = true; - -//timer -byte lastTimerMinute = 0; -byte timerHours[] = {0, 0, 0, 0, 0, 0, 0, 0}; -byte timerMinutes[] = {0, 0, 0, 0, 0, 0, 0, 0}; -byte timerMacro[] = {0, 0, 0, 0, 0, 0, 0, 0}; -byte timerWeekday[] = {255, 255, 255, 255, 255, 255, 255, 255}; //weekdays to activate on -//bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity - -//blynk -bool blynkEnabled = false; - -//preset cycling -bool presetCyclingEnabled = false; -byte presetCycleMin = 1, presetCycleMax = 5; -uint16_t presetCycleTime = 1250; -unsigned long presetCycledTime = 0; byte presetCycCurr = presetCycleMin; -bool presetApplyBri = true; -bool saveCurrPresetCycConf = false; - -//realtime -byte realtimeMode = REALTIME_MODE_INACTIVE; -IPAddress realtimeIP = (0,0,0,0); -unsigned long realtimeTimeout = 0; - -//mqtt -long lastMqttReconnectAttempt = 0; -long lastInterfaceUpdate = 0; -byte interfaceUpdateCallMode = NOTIFIER_CALL_MODE_INIT; -char mqttStatusTopic[40] = ""; //this must be global because of async handlers - -#if AUXPIN >= 0 - //auxiliary debug pin - byte auxTime = 0; - unsigned long auxStartTime = 0; - bool auxActive = false, auxActiveBefore = false; -#endif - -//alexa udp -String escapedMac; -#ifndef WLED_DISABLE_ALEXA - Espalexa espalexa; - EspalexaDevice* espalexaDevice; -#endif - -//dns server -DNSServer dnsServer; - -//network time -bool ntpConnected = false; -time_t local = 0; -unsigned long ntpLastSyncTime = 999000000L; -unsigned long ntpPacketSentTime = 999000000L; -IPAddress ntpServerIP; -uint16_t ntpLocalPort = 2390; -#define NTP_PACKET_SIZE 48 - -//maximum number of LEDs - MAX_LEDS is coming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue -#define MAX_LEDS 1500 -#define MAX_LEDS_DMA 500 - -//string temp buffer (now stored in stack locally) -#define OMAX 2048 -char* obuf; -uint16_t olen = 0; - -//presets -uint16_t savedPresets = 0; -int8_t currentPreset = -1; -bool isPreset = false; - -byte errorFlag = 0; - -String messageHead, messageSub; -byte optionType; - -bool doReboot = false; //flag to initiate reboot from async handlers -bool doPublishMqtt = false; - -//server library objects -AsyncWebServer server(80); -AsyncClient* hueClient = NULL; -AsyncMqttClient* mqtt = NULL; - -//function prototypes -void colorFromUint32(uint32_t, bool = false); -void serveMessage(AsyncWebServerRequest*, uint16_t, String, String, byte); -void handleE131Packet(e131_packet_t*, IPAddress); -void arlsLock(uint32_t,byte); -void handleOverlayDraw(); - -//udp interface objects -WiFiUDP notifierUdp, rgbUdp; -WiFiUDP ntpUdp; -ESPAsyncE131 e131(handleE131Packet); -bool e131NewData = false; - -//led fx library object -WS2812FX strip = WS2812FX(); - -#define WLED_CONNECTED (WiFi.status() == WL_CONNECTED) -#define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID,DEFAULT_CLIENT_SSID) != 0) - -//debug macros -#ifdef WLED_DEBUG - #define DEBUG_PRINT(x) Serial.print (x) - #define DEBUG_PRINTLN(x) Serial.println (x) - #define DEBUG_PRINTF(x) Serial.printf (x) - unsigned long debugTime = 0; - int lastWifiState = 3; - unsigned long wifiStateChangedTime = 0; - int loops = 0; -#else - #define DEBUG_PRINT(x) - #define DEBUG_PRINTLN(x) - #define DEBUG_PRINTF(x) -#endif - -//filesystem -#ifndef WLED_DISABLE_FILESYSTEM - #include - #ifdef ARDUINO_ARCH_ESP32 - #include "SPIFFS.h" - #endif - #include "SPIFFSEditor.h" -#endif - - - -//turns all LEDs off and restarts ESP -void reset() -{ - briT = 0; - long dly = millis(); - while (millis() - dly < 250) - { - yield(); //enough time to send response to client - } - setAllLeds(); - DEBUG_PRINTLN("MODULE RESET"); - ESP.restart(); -} - - -//append new c string to temp buffer efficiently -bool oappend(const char* txt) -{ - uint16_t len = strlen(txt); - if (olen + len >= OMAX) return false; //buffer full - strcpy(obuf + olen, txt); - olen += len; - return true; -} - - -//append new number to temp buffer efficiently -bool oappendi(int i) -{ - char s[11]; - sprintf(s, "%ld", i); - return oappend(s); -} - - -//boot starts here void setup() { - wledInit(); + //auto& wled = Wled(); } - -//main program loop void loop() { - handleIR(); //2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too - handleConnection(); - handleSerial(); - handleNotifications(); - handleTransitions(); -#ifdef WLED_ENABLE_DMX - handleDMX(); -#endif - userLoop(); - yield(); - handleIO(); - handleIR(); - handleNetworkTime(); - handleAlexa(); - handleOverlays(); - yield(); -#ifdef WLED_USE_ANALOG_LEDS - strip.setRgbwPwm(); -#endif - - if (doReboot) reset(); - - if (!realtimeMode) //block stuff if WARLS/Adalight is enabled - { - if (apActive) dnsServer.processNextRequest(); -#ifndef WLED_DISABLE_OTA - if (WLED_CONNECTED && aOtaEnabled) ArduinoOTA.handle(); -#endif - handleNightlight(); - yield(); - - handleHue(); - handleBlynk(); - - yield(); - if (!offMode) strip.service(); - } - yield(); -#ifdef ESP8266 - MDNS.update(); -#endif - if (millis() - lastMqttReconnectAttempt > 30000) initMqtt(); - - //DEBUG serial logging -#ifdef WLED_DEBUG - if (millis() - debugTime > 9999) - { - DEBUG_PRINTLN("---DEBUG INFO---"); - DEBUG_PRINT("Runtime: "); DEBUG_PRINTLN(millis()); - DEBUG_PRINT("Unix time: "); DEBUG_PRINTLN(now()); - DEBUG_PRINT("Free heap: "); DEBUG_PRINTLN(ESP.getFreeHeap()); - DEBUG_PRINT("Wifi state: "); DEBUG_PRINTLN(WiFi.status()); - if (WiFi.status() != lastWifiState) - { - wifiStateChangedTime = millis(); - } - lastWifiState = WiFi.status(); - DEBUG_PRINT("State time: "); DEBUG_PRINTLN(wifiStateChangedTime); - DEBUG_PRINT("NTP last sync: "); DEBUG_PRINTLN(ntpLastSyncTime); - DEBUG_PRINT("Client IP: "); DEBUG_PRINTLN(WiFi.localIP()); - DEBUG_PRINT("Loops/sec: "); DEBUG_PRINTLN(loops / 10); - loops = 0; - debugTime = millis(); - } - loops++; -#endif -} +} \ No newline at end of file diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino deleted file mode 100644 index 29ae0e7a..00000000 --- a/wled00/wled05_init.ino +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Setup code - */ - -void wledInit() -{ - EEPROM.begin(EEPSIZE); - ledCount = EEPROM.read(229) + ((EEPROM.read(398) << 8) & 0xFF00); - if (ledCount > MAX_LEDS || ledCount == 0) ledCount = 30; - - #ifdef ESP8266 - #if LEDPIN == 3 - if (ledCount > MAX_LEDS_DMA) ledCount = MAX_LEDS_DMA; //DMA method uses too much ram - #endif - #endif - Serial.begin(115200); - Serial.setTimeout(50); - DEBUG_PRINTLN(); - DEBUG_PRINT("---WLED "); DEBUG_PRINT(versionString); DEBUG_PRINT(" "); DEBUG_PRINT(VERSION); DEBUG_PRINTLN(" INIT---"); - #ifdef ARDUINO_ARCH_ESP32 - DEBUG_PRINT("esp32 "); DEBUG_PRINTLN(ESP.getSdkVersion()); - #else - DEBUG_PRINT("esp8266 "); DEBUG_PRINTLN(ESP.getCoreVersion()); - #endif - int heapPreAlloc = ESP.getFreeHeap(); - DEBUG_PRINT("heap "); - DEBUG_PRINTLN(ESP.getFreeHeap()); - - strip.init(EEPROM.read(372),ledCount,EEPROM.read(2204)); //init LEDs quickly - strip.setBrightness(0); - - DEBUG_PRINT("LEDs inited. heap usage ~"); - DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap()); - - #ifndef WLED_DISABLE_FILESYSTEM - #ifdef ARDUINO_ARCH_ESP32 - SPIFFS.begin(true); - #endif - SPIFFS.begin(); - #endif - - DEBUG_PRINTLN("Load EEPROM"); - loadSettingsFromEEPROM(true); - beginStrip(); - userSetup(); - if (strcmp(clientSSID,DEFAULT_CLIENT_SSID) == 0) showWelcomePage = true; - WiFi.persistent(false); - - if (macroBoot>0) applyMacro(macroBoot); - Serial.println("Ada"); - - //generate module IDs - escapedMac = WiFi.macAddress(); - escapedMac.replace(":", ""); - escapedMac.toLowerCase(); - if (strcmp(cmDNS,"x") == 0) //fill in unique mdns default - { - strcpy(cmDNS, "wled-"); - sprintf(cmDNS+5, "%*s", 6, escapedMac.c_str()+6); - } - if (mqttDeviceTopic[0] == 0) - { - strcpy(mqttDeviceTopic, "wled/"); - sprintf(mqttDeviceTopic+5, "%*s", 6, escapedMac.c_str()+6); - } - if (mqttClientID[0] == 0) - { - strcpy(mqttClientID, "WLED-"); - sprintf(mqttClientID+5, "%*s", 6, escapedMac.c_str()+6); - } - - strip.service(); - - #ifndef WLED_DISABLE_OTA - if (aOtaEnabled) - { - ArduinoOTA.onStart([]() { - #ifdef ESP8266 - wifi_set_sleep_type(NONE_SLEEP_T); - #endif - DEBUG_PRINTLN("Start ArduinoOTA"); - }); - if (strlen(cmDNS) > 0) ArduinoOTA.setHostname(cmDNS); - } - #endif - #ifdef WLED_ENABLE_DMX - dmx.init(512); // initialize with bus length - #endif - //HTTP server page init - initServer(); -} - - -void beginStrip() -{ - // Initialize NeoPixel Strip and button - strip.setShowCallback(handleOverlayDraw); - -#ifdef BTNPIN - pinMode(BTNPIN, INPUT_PULLUP); -#endif - - if (bootPreset > 0) applyPreset(bootPreset, turnOnAtBoot); - colorUpdated(NOTIFIER_CALL_MODE_INIT); - - //init relay pin - #if RLYPIN >= 0 - pinMode(RLYPIN, OUTPUT); - #if RLYMDE - digitalWrite(RLYPIN, bri); - #else - digitalWrite(RLYPIN, !bri); - #endif - #endif - - //disable button if it is "pressed" unintentionally -#ifdef BTNPIN - if(digitalRead(BTNPIN) == LOW) buttonEnabled = false; -#else - buttonEnabled = false; -#endif -} - - -void initAP(bool resetAP=false){ - if (apBehavior == AP_BEHAVIOR_BUTTON_ONLY && !resetAP) return; - - if (!apSSID[0] || resetAP) strcpy(apSSID, "WLED-AP"); - if (resetAP) strcpy(apPass,DEFAULT_AP_PASS); - DEBUG_PRINT("Opening access point "); - DEBUG_PRINTLN(apSSID); - WiFi.softAPConfig(IPAddress(4, 3, 2, 1), IPAddress(4, 3, 2, 1), IPAddress(255,255,255,0)); - WiFi.softAP(apSSID, apPass, apChannel, apHide); - - if (!apActive) //start captive portal if AP active - { - DEBUG_PRINTLN("Init AP interfaces"); - server.begin(); - if (udpPort > 0 && udpPort != ntpLocalPort) - { - udpConnected = notifierUdp.begin(udpPort); - } - if (udpRgbPort > 0 && udpRgbPort != ntpLocalPort && udpRgbPort != udpPort) - { - udpRgbConnected = rgbUdp.begin(udpRgbPort); - } - - dnsServer.setErrorReplyCode(DNSReplyCode::NoError); - dnsServer.start(53, "*", WiFi.softAPIP()); - } - apActive = true; -} - -void initConnection() -{ - WiFi.disconnect(); //close old connections - #ifdef ESP8266 - WiFi.setPhyMode(WIFI_PHY_MODE_11N); - #endif - - if (staticIP[0] != 0 && staticGateway[0] != 0) - { - WiFi.config(staticIP, staticGateway, staticSubnet, IPAddress(8,8,8,8)); - } else - { - WiFi.config(0U, 0U, 0U); - } - - lastReconnectAttempt = millis(); - - if (!WLED_WIFI_CONFIGURED) - { - DEBUG_PRINT("No connection configured. "); - if (!apActive) initAP(); //instantly go to ap mode - return; - } else if (!apActive) { - if (apBehavior == AP_BEHAVIOR_ALWAYS) - { - initAP(); - } else - { - DEBUG_PRINTLN("Access point disabled."); - WiFi.softAPdisconnect(true); - } - } - showWelcomePage = false; - - DEBUG_PRINT("Connecting to "); - DEBUG_PRINT(clientSSID); - DEBUG_PRINTLN("..."); - - #ifdef ESP8266 - WiFi.hostname(serverDescription); - #endif - - WiFi.begin(clientSSID, clientPass); - - #ifdef ARDUINO_ARCH_ESP32 - WiFi.setSleep(!noWifiSleep); - WiFi.setHostname(serverDescription); - #else - wifi_set_sleep_type((noWifiSleep) ? NONE_SLEEP_T : MODEM_SLEEP_T); - #endif -} - -void initInterfaces() { - DEBUG_PRINTLN("Init STA interfaces"); - - if (hueIP[0] == 0) - { - hueIP[0] = WiFi.localIP()[0]; - hueIP[1] = WiFi.localIP()[1]; - hueIP[2] = WiFi.localIP()[2]; - } - - //init Alexa hue emulation - if (alexaEnabled) alexaInit(); - - #ifndef WLED_DISABLE_OTA - if (aOtaEnabled) ArduinoOTA.begin(); - #endif - - strip.service(); - // Set up mDNS responder: - if (strlen(cmDNS) > 0) - { - if (!aOtaEnabled) MDNS.begin(cmDNS); - - DEBUG_PRINTLN("mDNS started"); - MDNS.addService("http", "tcp", 80); - MDNS.addService("wled", "tcp", 80); - MDNS.addServiceTxt("wled", "tcp", "mac", escapedMac.c_str()); - } - server.begin(); - - if (udpPort > 0 && udpPort != ntpLocalPort) - { - udpConnected = notifierUdp.begin(udpPort); - if (udpConnected && udpRgbPort != udpPort) udpRgbConnected = rgbUdp.begin(udpRgbPort); - } - if (ntpEnabled) ntpConnected = ntpUdp.begin(ntpLocalPort); - - initBlynk(blynkApiKey); - e131.begin((e131Multicast) ? E131_MULTICAST : E131_UNICAST , e131Universe, E131_MAX_UNIVERSE_COUNT); - reconnectHue(); - initMqtt(); - interfacesInited = true; - wasConnected = true; -} - -byte stacO = 0; -uint32_t lastHeap; -unsigned long heapTime = 0; - -void handleConnection() { - if (millis() < 2000 && (!WLED_WIFI_CONFIGURED || apBehavior == AP_BEHAVIOR_ALWAYS)) return; - if (lastReconnectAttempt == 0) initConnection(); - - //reconnect WiFi to clear stale allocations if heap gets too low - if (millis() - heapTime > 5000) - { - uint32_t heap = ESP.getFreeHeap(); - if (heap < 9000 && lastHeap < 9000) { - DEBUG_PRINT("Heap too low! "); - DEBUG_PRINTLN(heap); - forceReconnect = true; - } - lastHeap = heap; - heapTime = millis(); - } - - byte stac = 0; - if (apActive) { - #ifdef ESP8266 - stac = wifi_softap_get_station_num(); - #else - wifi_sta_list_t stationList; - esp_wifi_ap_get_sta_list(&stationList); - stac = stationList.num; - #endif - if (stac != stacO) - { - stacO = stac; - DEBUG_PRINT("Connected AP clients: "); - DEBUG_PRINTLN(stac); - if (!WLED_CONNECTED && WLED_WIFI_CONFIGURED) { //trying to connect, but not connected - if (stac) WiFi.disconnect(); //disable search so that AP can work - else initConnection(); //restart search - } - } - } - if (forceReconnect) { - DEBUG_PRINTLN("Forcing reconnect."); - initConnection(); - interfacesInited = false; - forceReconnect = false; - wasConnected = false; - return; - } - if (!WLED_CONNECTED) { - if (interfacesInited) { - DEBUG_PRINTLN("Disconnected!"); - interfacesInited = false; - initConnection(); - } - if (millis() - lastReconnectAttempt > ((stac) ? 300000 : 20000) && WLED_WIFI_CONFIGURED) initConnection(); - if (!apActive && millis() - lastReconnectAttempt > 12000 && (!wasConnected || apBehavior == AP_BEHAVIOR_NO_CONN)) initAP(); - } else if (!interfacesInited) { //newly connected - DEBUG_PRINTLN(""); - DEBUG_PRINT("Connected! IP address: "); - DEBUG_PRINTLN(WiFi.localIP()); - initInterfaces(); - userConnected(); - - //shut down AP - if (apBehavior != AP_BEHAVIOR_ALWAYS && apActive) - { - dnsServer.stop(); - WiFi.softAPdisconnect(true); - apActive = false; - DEBUG_PRINTLN("Access point disabled."); - } - } -} - -//by https://github.com/tzapu/WiFiManager/blob/master/WiFiManager.cpp -int getSignalQuality(int rssi) -{ - int quality = 0; - - if (rssi <= -100) { - quality = 0; - } else if (rssi >= -50) { - quality = 100; - } else { - quality = 2 * (rssi + 100); - } - return quality; -} diff --git a/wled00/wled06_usermod.ino b/wled00/wled06_usermod.cpp similarity index 97% rename from wled00/wled06_usermod.ino rename to wled00/wled06_usermod.cpp index 010d0f38..c780ffee 100644 --- a/wled00/wled06_usermod.ino +++ b/wled00/wled06_usermod.cpp @@ -1,3 +1,4 @@ +#include "wled.h" /* * This file allows you to add own functionality to WLED more easily * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality diff --git a/wled00/wled12_alexa.ino b/wled00/wled_alexa.cpp similarity index 89% rename from wled00/wled12_alexa.ino rename to wled00/wled_alexa.cpp index 5163640e..b68843cb 100644 --- a/wled00/wled12_alexa.ino +++ b/wled00/wled_alexa.cpp @@ -1,10 +1,6 @@ -/* - * Alexa Voice On/Off/Brightness Control. Emulates a Philips Hue bridge to Alexa. - * - * This was put together from these two excellent projects: - * https://github.com/kakopappa/arduino-esp8266-alexa-wemo-switch - * https://github.com/probonopd/ESP8266HueEmulator - */ +#include "wled_alexa.h" +#include "wled.h" +#include "src/dependencies/espalexa/EspalexaDevice.h" #ifndef WLED_DISABLE_ALEXA void onAlexaChange(EspalexaDevice* dev); diff --git a/wled00/wled_alexa.h b/wled00/wled_alexa.h new file mode 100644 index 00000000..4bf7bd12 --- /dev/null +++ b/wled00/wled_alexa.h @@ -0,0 +1,16 @@ +#ifndef WLED_ALEXA_H +#define WLED_ALEXA_H +/* + * Alexa Voice On/Off/Brightness Control. Emulates a Philips Hue bridge to Alexa. + * + * This was put together from these two excellent projects: + * https://github.com/kakopappa/arduino-esp8266-alexa-wemo-switch + * https://github.com/probonopd/ESP8266HueEmulator + */ +class EspalexaDevice; +void onAlexaChange(EspalexaDevice* dev); +void alexaInit(); +void handleAlexa(); +void onAlexaChange(EspalexaDevice* dev); + +#define WLED_ALEXA_H \ No newline at end of file diff --git a/wled00/wled16_blynk.ino b/wled00/wled_blynk.cpp similarity index 94% rename from wled00/wled16_blynk.ino rename to wled00/wled_blynk.cpp index ea4d6877..c6754a23 100644 --- a/wled00/wled16_blynk.ino +++ b/wled00/wled_blynk.cpp @@ -1,6 +1,6 @@ -/* - * Remote light control with the free Blynk app - */ +#include "wled_blynk.h" +#include "wled.h" +#include "src/dependencies/blynk/Blynk/BlynkHandlers.h" uint16_t blHue = 0; byte blSat = 255; diff --git a/wled00/wled_blynk.h b/wled00/wled_blynk.h new file mode 100644 index 00000000..e8c8341b --- /dev/null +++ b/wled00/wled_blynk.h @@ -0,0 +1,13 @@ +#ifndef WLED_BLYNK_H +#define WLED_BLYNK_H +#include +/* + * Remote light control with the free Blynk app + */ + +void initBlynk(const char* auth); +void handleBlynk(); +void updateBlynk(); +// Unsure if the macro expansions need to accessed through the declaration... TODO + +#endif //WLED_BLYNK_H \ No newline at end of file diff --git a/wled00/wled09_button.ino b/wled00/wled_button.cpp similarity index 98% rename from wled00/wled09_button.ino rename to wled00/wled_button.cpp index 1cd868be..12fe5b49 100644 --- a/wled00/wled09_button.ino +++ b/wled00/wled_button.cpp @@ -1,3 +1,5 @@ +#include "wled_button.h" +#include "wled.h" /* * Physical IO */ diff --git a/wled00/wled_button.h b/wled00/wled_button.h new file mode 100644 index 00000000..5f894477 --- /dev/null +++ b/wled00/wled_button.h @@ -0,0 +1,12 @@ +#ifndef WLED_BUTTON_H +#define WLED_BUTTON_H +#include +/* + * Physical IO + */ + +void shortPressAction(); +void handleButton(); +void handleIO(); + +#endif // WLED_BUTTON_H \ No newline at end of file diff --git a/wled00/wled14_colors.ino b/wled00/wled_colors.cpp similarity index 96% rename from wled00/wled14_colors.ino rename to wled00/wled_colors.cpp index a1db307a..dcd32e57 100644 --- a/wled00/wled14_colors.ino +++ b/wled00/wled_colors.cpp @@ -1,6 +1,5 @@ -/* - * Color conversion methods - */ +#include "wled_colors.h" +#include "wled.h" void colorFromUint32(uint32_t in, bool secondary) { @@ -18,7 +17,7 @@ void colorFromUint32(uint32_t in, bool secondary) } //load a color without affecting the white channel -void colorFromUint24(uint32_t in, bool secondary = false) +void colorFromUint24(uint32_t in, bool secondary) { if (secondary) { colSec[0] = in >> 16 & 0xFF; @@ -32,7 +31,7 @@ void colorFromUint24(uint32_t in, bool secondary = false) } //relatively change white brightness, minumum A=5 -void relativeChangeWhite(int8_t amount, byte lowerBoundary =0) +void relativeChangeWhite(int8_t amount, byte lowerBoundary) { int16_t new_val = (int16_t) col[3] + amount; if (new_val > 0xFF) new_val = 0xFF; @@ -149,7 +148,8 @@ void colorRGBtoXY(byte* rgb, float* xy) //rgb to coordinates (https://www.develo xy[0] = X / (X + Y + Z); xy[1] = Y / (X + Y + Z); } -#endif +#endif // WLED_DISABLE_HUESYNC + void colorFromDecOrHexString(byte* rgb, char* in) { diff --git a/wled00/wled_colors.h b/wled00/wled_colors.h new file mode 100644 index 00000000..2b3ff32c --- /dev/null +++ b/wled00/wled_colors.h @@ -0,0 +1,20 @@ +#ifndef WLED_COLORS_H +#define WLED_COLORS_H +#include +/* + * Color conversion methods + */ + +void colorFromUint32(uint32_t in, bool secondary); +void colorFromUint24(uint32_t in, bool secondary = false); +void relativeChangeWhite(int8_t amount, byte lowerBoundary = 0); +void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb +void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb + +void colorXYtoRGB(float x, float y, byte* rgb); // only defined if huesync disabled TODO +void colorRGBtoXY(byte* rgb, float* xy); // only defined if huesync disabled TODO + +void colorFromDecOrHexString(byte* rgb, char* in); +void colorRGBtoRGBW(byte* rgb); //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_MODE_LEGACY) + +#endif //WLED_COLORS_H \ No newline at end of file diff --git a/wled00/wled13_cronixie.ino b/wled00/wled_cronixie.cpp similarity index 97% rename from wled00/wled13_cronixie.ino rename to wled00/wled_cronixie.cpp index dd687ca1..107af752 100644 --- a/wled00/wled13_cronixie.ino +++ b/wled00/wled_cronixie.cpp @@ -1,9 +1,8 @@ -/* - * Support for the Cronixie clock - */ +#include "wled_cronixie.h" +#include "wled.h" + #ifndef WLED_DISABLE_CRONIXIE byte _digitOut[6] = {10,10,10,10,10,10}; -#endif byte getSameCodeLength(char code, int index, char const cronixieDisplay[]) { @@ -23,7 +22,6 @@ byte getSameCodeLength(char code, int index, char const cronixieDisplay[]) void setCronixie() { - #ifndef WLED_DISABLE_CRONIXIE /* * digit purpose index * 0-9 | 0-9 (incl. random) @@ -144,12 +142,10 @@ void setCronixie() DEBUG_PRINTLN((int)dP[5]); _overlayCronixie(); //refresh - #endif } void _overlayCronixie() { - #ifndef WLED_DISABLE_CRONIXIE byte h = hour(local); byte h0 = h; byte m = minute(local); @@ -211,12 +207,10 @@ void _overlayCronixie() } } } - #endif } void _drawOverlayCronixie() { - #ifndef WLED_DISABLE_CRONIXIE byte offsets[] = {5, 0, 6, 1, 7, 2, 8, 3, 9, 4}; for (uint16_t i = 0; i < 6; i++) @@ -239,5 +233,11 @@ void _drawOverlayCronixie() } } } - #endif } + +#else // WLED_DISABLE_CRONIXIE +byte getSameCodeLength(char code, int index, char const cronixieDisplay[]) {} +void setCronixie() {} +void _overlayCronixie() {} +void _drawOverlayCronixie() {} +#endif diff --git a/wled00/wled_cronixie.h b/wled00/wled_cronixie.h new file mode 100644 index 00000000..9dea69b3 --- /dev/null +++ b/wled00/wled_cronixie.h @@ -0,0 +1,13 @@ +#ifndef WLED_CRONIXIE_H +#define WLED_CRONIXIE_H +#include +/* + * Support for the Cronixie clock + */ + +byte getSameCodeLength(char code, int index, char const cronixieDisplay[]); +void setCronixie(); +void _overlayCronixie(); +void _drawOverlayCronixie(); + +#endif // WLED_CRONIXIE_H \ No newline at end of file diff --git a/wled00/wled21_dmx.ino b/wled00/wled_dmx.cpp similarity index 89% rename from wled00/wled21_dmx.ino rename to wled00/wled_dmx.cpp index 4230f07d..f620f777 100644 --- a/wled00/wled21_dmx.ino +++ b/wled00/wled_dmx.cpp @@ -1,10 +1,7 @@ -/* - * Support for DMX via MAX485. - * Needs the espdmx library. You might have to change the output pin within the library. Sketchy, i know. - * https://github.com/Rickgg/ESP-Dmx - */ -#ifdef WLED_ENABLE_DMX +#include "wled_dmx.h" +#include "wled.h" +#ifdef WLED_ENABLE_DMX void handleDMX() { // TODO: calculate brightness manually if no shutter channel is set diff --git a/wled00/wled_dmx.h b/wled00/wled_dmx.h new file mode 100644 index 00000000..94cbb0d2 --- /dev/null +++ b/wled00/wled_dmx.h @@ -0,0 +1,11 @@ +#ifndef WLED_DMX_H +#define WLED_DMX_H +/* + * Support for DMX via MAX485. + * Needs the espdmx library. You might have to change the output pin within the library. Sketchy, i know. + * https://github.com/Rickgg/ESP-Dmx + */ + +void handleDMX(); + +#endif //WLED_DMX_H \ No newline at end of file diff --git a/wled00/wled01_eeprom.ino b/wled00/wled_eeprom.cpp similarity index 99% rename from wled00/wled01_eeprom.ino rename to wled00/wled_eeprom.cpp index 1f0e8426..0ddbf57f 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled_eeprom.cpp @@ -1,7 +1,5 @@ -/* - * Methods to handle saving and loading to non-volatile memory - * EEPROM Map: https://github.com/Aircoookie/WLED/wiki/EEPROM-Map - */ +#include "wled_eeprom.h" +#include "wled.h" #define EEPSIZE 2560 //Maximum is 4096 diff --git a/wled00/wled_eeprom.h b/wled00/wled_eeprom.h new file mode 100644 index 00000000..fe5c2cbf --- /dev/null +++ b/wled00/wled_eeprom.h @@ -0,0 +1,22 @@ +#ifndef WLED_EPPROM_H +#define WLED_EPPROM_H +#include +/* + * Methods to handle saving and loading to non-volatile memory + * EEPROM Map: https://github.com/Aircoookie/WLED/wiki/EEPROM-Map + */ + +void commit(); +void clearEEPROM(); +void writeStringToEEPROM(uint16_t pos, char* str, uint16_t len); +void readStringFromEEPROM(uint16_t pos, char* str, uint16_t len); +void saveSettingsToEEPROM(); +void loadSettingsFromEEPROM(bool first); +void savedToPresets(); +bool applyPreset(byte index, bool loadBri = true); +void savePreset(byte index, bool persist = true); +void loadMacro(byte index, char* m); +void applyMacro(byte index); +void saveMacro(byte index, String mc, bool persist = true); //only commit on single save, not in settings + +#endif //WLED_EPPROM_H diff --git a/wled00/wled04_file.ino b/wled00/wled_file.cpp similarity index 95% rename from wled00/wled04_file.ino rename to wled00/wled_file.cpp index de752328..700d66ee 100644 --- a/wled00/wled04_file.ino +++ b/wled00/wled_file.cpp @@ -1,6 +1,14 @@ -/* - * Utility for SPIFFS filesystem & Serial console - */ +#include "wled_file.h" +#include "wled.h" +//filesystem +#ifndef WLED_DISABLE_FILESYSTEM +#include +#ifdef ARDUINO_ARCH_ESP32 +#include "SPIFFS.h" +#endif +#include "SPIFFSEditor.h" +#endif + enum class AdaState { Header_A, Header_d, diff --git a/wled00/wled_file.h b/wled00/wled_file.h new file mode 100644 index 00000000..7457ec4c --- /dev/null +++ b/wled00/wled_file.h @@ -0,0 +1,12 @@ +#ifndef WLED_FILE_H +#define WLED_FILE_H +#include +#include +/* + * Utility for SPIFFS filesystem & Serial console + */ + +void handleSerial(); +bool handleFileRead(AsyncWebServerRequest*, String path); + +#endif // WLED_FILE_H \ No newline at end of file diff --git a/wled00/wled15_hue.ino b/wled00/wled_hue.cpp similarity index 99% rename from wled00/wled15_hue.ino rename to wled00/wled_hue.cpp index 5f6b8f5b..082685d7 100644 --- a/wled00/wled15_hue.ino +++ b/wled00/wled_hue.cpp @@ -1,7 +1,8 @@ -/* - * Sync to Philips hue lights - */ +#include "wled_hue.h" +#include "wled.h" + #ifndef WLED_DISABLE_HUESYNC + void handleHue() { if (hueReceived) diff --git a/wled00/wled_hue.h b/wled00/wled_hue.h new file mode 100644 index 00000000..dcf39729 --- /dev/null +++ b/wled00/wled_hue.h @@ -0,0 +1,10 @@ +#ifndef WLED_HUE_H +#define WLED_HUE_H +/* + * Sync to Philips hue lights + */ + +void handleHue(); +void reconnectHue(); + +#endif //WLED_HUE_H \ No newline at end of file diff --git a/wled00/wled20_ir.ino b/wled00/wled_ir.cpp similarity index 99% rename from wled00/wled20_ir.ino rename to wled00/wled_ir.cpp index 64c131c2..53f0d23f 100644 --- a/wled00/wled20_ir.ino +++ b/wled00/wled_ir.cpp @@ -1,6 +1,5 @@ -/* - * Infrared sensor support for generic 24/40/44 key RGB remotes - */ +#include "wled_ir.h" +#include "wled.h" #if defined(WLED_DISABLE_INFRARED) void handleIR(){} diff --git a/wled00/wled_ir.h b/wled00/wled_ir.h new file mode 100644 index 00000000..3479f410 --- /dev/null +++ b/wled00/wled_ir.h @@ -0,0 +1,9 @@ +#ifndef WLED_IR_H +#define WLED_IR_H +/* + * Infrared sensor support for generic 24/40/44 key RGB remotes + */ + +void handleIR(); + +#endif //WLED_IR_H \ No newline at end of file diff --git a/wled00/wled19_json.ino b/wled00/wled_json.cpp similarity index 99% rename from wled00/wled19_json.ino rename to wled00/wled_json.cpp index df9bcead..43d3136d 100644 --- a/wled00/wled19_json.ino +++ b/wled00/wled_json.cpp @@ -1,6 +1,5 @@ -/* - * JSON API (De)serialization - */ +#include "wled_json.h" +#include "wled.h" void deserializeSegment(JsonObject elem, byte it) { diff --git a/wled00/wled_json.h b/wled00/wled_json.h new file mode 100644 index 00000000..83830faf --- /dev/null +++ b/wled00/wled_json.h @@ -0,0 +1,15 @@ +#ifndef WLED_JSON_H +#define WLED_JSON_H +/* + * JSON API (De)serialization + */ + +void deserializeSegment(JsonObject elem, byte it); +bool deserializeState(JsonObject root); +void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id); +void serializeState(JsonObject root); +void serializeInfo(JsonObject root); +void serveJson(AsyncWebServerRequest* request); +void serveLiveLeds(AsyncWebServerRequest* request); + +#endif //WLED_JSON_H \ No newline at end of file diff --git a/wled00/wled08_led.ino b/wled00/wled_led.cpp similarity index 98% rename from wled00/wled08_led.ino rename to wled00/wled_led.cpp index 63009f98..aefb004a 100644 --- a/wled00/wled08_led.ino +++ b/wled00/wled_led.cpp @@ -1,6 +1,6 @@ -/* - * LED methods - */ +#include "wled_led.h" +#include "wled.h" + void setValuesFromMainSeg() { WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId()); @@ -50,7 +50,7 @@ void setAllLeds() { } -void setLedsStandard(bool justColors = false) +void setLedsStandard(bool justColors) { for (byte i=0; i<4; i++) { diff --git a/wled00/wled_led.h b/wled00/wled_led.h new file mode 100644 index 00000000..aebe95e4 --- /dev/null +++ b/wled00/wled_led.h @@ -0,0 +1,19 @@ +#ifndef WLED_LED_H +#define WLED_LED_H +#include +/* + * LED methods + */ + +void setValuesFromMainSeg(); +void resetTimebase(); +void toggleOnOff(); +void setAllLeds(); +void setLedsStandard(bool justColors = false); +bool colorChanged(); +void colorUpdated(int callMode); +void updateInterfaces(uint8_t callMode); +void handleTransitions(); +void handleNightlight(); + +#endif \ No newline at end of file diff --git a/wled00/wled17_mqtt.ino b/wled00/wled_mqtt.cpp similarity index 98% rename from wled00/wled17_mqtt.ino rename to wled00/wled_mqtt.cpp index 741c807f..d4fc80c0 100644 --- a/wled00/wled17_mqtt.ino +++ b/wled00/wled_mqtt.cpp @@ -1,6 +1,5 @@ -/* - * MQTT communication protocol for home automation - */ +#include "wled_mqtt.h" +#include "wled.h" #ifdef WLED_ENABLE_MQTT #define MQTT_KEEP_ALIVE_TIME 60 // contact the MQTT broker every 60 seconds diff --git a/wled00/wled_mqtt.h b/wled00/wled_mqtt.h new file mode 100644 index 00000000..f1c7160e --- /dev/null +++ b/wled00/wled_mqtt.h @@ -0,0 +1,9 @@ +#ifndef WLED_MQTT_H +#define WLED_MQTT_H +/* + * MQTT communication protocol for home automation + */ +bool initMqtt(); +void publishMqtt(); + +#endif //WLED_MQTT_H \ No newline at end of file diff --git a/wled00/wled07_notify.ino b/wled00/wled_notify.cpp similarity index 99% rename from wled00/wled07_notify.ino rename to wled00/wled_notify.cpp index 8fcbdbe8..7827aba9 100644 --- a/wled00/wled07_notify.ino +++ b/wled00/wled_notify.cpp @@ -1,6 +1,6 @@ -/* - * UDP notifier - */ +#include "wled_notify.h" +#include "wled.h" +#include "src/dependencies/e131/ESPAsyncE131.h" #define WLEDPACKETSIZE 29 #define UDP_IN_MAXSIZE 1472 diff --git a/wled00/wled_notify.h b/wled00/wled_notify.h new file mode 100644 index 00000000..63d01142 --- /dev/null +++ b/wled00/wled_notify.h @@ -0,0 +1,17 @@ +#ifndef WLED_NOTIFY_H +#define WLED_NOTIFY_H +#include +#include "const.h" +/* + * UDP notifier + */ +union e131_packet_t; // Will this compile? +class IPAddress; + +void notify(byte callMode, bool followUp=false); +void arlsLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC); +void handleE131Packet(e131_packet_t* p, IPAddress clientIP); +void handleNotifications(); +void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w); + +#endif // WLED_NOTIFY_H \ No newline at end of file diff --git a/wled00/wled10_ntp.ino b/wled00/wled_ntp.cpp similarity index 58% rename from wled00/wled10_ntp.ino rename to wled00/wled_ntp.cpp index 2c5a64c4..82b99f96 100644 --- a/wled00/wled10_ntp.ino +++ b/wled00/wled_ntp.cpp @@ -1,63 +1,7 @@ -/* - * Acquires time from NTP server - */ +#include "wled_ntp.h" +#include "wled.h" +#include "wled_eeprom.h" -TimeChangeRule UTCr = {Last, Sun, Mar, 1, 0}; // UTC -Timezone tzUTC(UTCr, UTCr); - -TimeChangeRule BST = {Last, Sun, Mar, 1, 60}; // British Summer Time -TimeChangeRule GMT = {Last, Sun, Oct, 2, 0}; // Standard Time -Timezone tzUK(BST, GMT); - -TimeChangeRule CEST = {Last, Sun, Mar, 2, 120}; //Central European Summer Time -TimeChangeRule CET = {Last, Sun, Oct, 3, 60}; //Central European Standard Time -Timezone tzEUCentral(CEST, CET); - -TimeChangeRule EEST = {Last, Sun, Mar, 3, 180}; //Central European Summer Time -TimeChangeRule EET = {Last, Sun, Oct, 4, 120}; //Central European Standard Time -Timezone tzEUEastern(EEST, EET); - -TimeChangeRule EDT = {Second, Sun, Mar, 2, -240 }; //Daylight time = UTC - 4 hours -TimeChangeRule EST = {First, Sun, Nov, 2, -300 }; //Standard time = UTC - 5 hours -Timezone tzUSEastern(EDT, EST); - -TimeChangeRule CDT = {Second, Sun, Mar, 2, -300 }; //Daylight time = UTC - 5 hours -TimeChangeRule CST = {First, Sun, Nov, 2, -360 }; //Standard time = UTC - 6 hours -Timezone tzUSCentral(CDT, CST); - -Timezone tzCASaskatchewan(CST, CST); //Central without DST - -TimeChangeRule MDT = {Second, Sun, Mar, 2, -360 }; //Daylight time = UTC - 6 hours -TimeChangeRule MST = {First, Sun, Nov, 2, -420 }; //Standard time = UTC - 7 hours -Timezone tzUSMountain(MDT, MST); - -Timezone tzUSArizona(MST, MST); //Mountain without DST - -TimeChangeRule PDT = {Second, Sun, Mar, 2, -420 }; //Daylight time = UTC - 7 hours -TimeChangeRule PST = {First, Sun, Nov, 2, -480 }; //Standard time = UTC - 8 hours -Timezone tzUSPacific(PDT, PST); - -TimeChangeRule ChST = {Last, Sun, Mar, 1, 480}; // China Standard Time = UTC + 8 hours -Timezone tzChina(ChST, ChST); - -TimeChangeRule JST = {Last, Sun, Mar, 1, 540}; // Japan Standard Time = UTC + 9 hours -Timezone tzJapan(JST, JST); - -TimeChangeRule AEDT = {Second, Sun, Oct, 2, 660 }; //Daylight time = UTC + 11 hours -TimeChangeRule AEST = {First, Sun, Apr, 3, 600 }; //Standard time = UTC + 10 hours -Timezone tzAUEastern(AEDT, AEST); - -TimeChangeRule NZDT = {Second, Sun, Sep, 2, 780 }; //Daylight time = UTC + 13 hours -TimeChangeRule NZST = {First, Sun, Apr, 3, 720 }; //Standard time = UTC + 12 hours -Timezone tzNZ(NZDT, NZST); - -TimeChangeRule NKST = {Last, Sun, Mar, 1, 510}; //Pyongyang Time = UTC + 8.5 hours -Timezone tzNK(NKST, NKST); - -TimeChangeRule IST = {Last, Sun, Mar, 1, 330}; // India Standard Time = UTC + 5.5 hours -Timezone tzIndia(IST, IST); - -Timezone* timezones[] = {&tzUTC, &tzUK, &tzEUCentral, &tzEUEastern, &tzUSEastern, &tzUSCentral, &tzUSMountain, &tzUSArizona, &tzUSPacific, &tzChina, &tzJapan, &tzAUEastern, &tzNZ, &tzNK, &tzIndia, &tzCASaskatchewan}; void handleNetworkTime() { diff --git a/wled00/wled_ntp.h b/wled00/wled_ntp.h new file mode 100644 index 00000000..14293342 --- /dev/null +++ b/wled00/wled_ntp.h @@ -0,0 +1,76 @@ +#ifndef WLED_NTP_H +#define WLED_NTP_H +#include +#include "timezone/Timezone.h" + +/* + * Acquires time from NTP server + */ +TimeChangeRule UTCr = {Last, Sun, Mar, 1, 0}; // UTC +Timezone tzUTC(UTCr, UTCr); + +TimeChangeRule BST = {Last, Sun, Mar, 1, 60}; // British Summer Time +TimeChangeRule GMT = {Last, Sun, Oct, 2, 0}; // Standard Time +Timezone tzUK(BST, GMT); + +TimeChangeRule CEST = {Last, Sun, Mar, 2, 120}; //Central European Summer Time +TimeChangeRule CET = {Last, Sun, Oct, 3, 60}; //Central European Standard Time +Timezone tzEUCentral(CEST, CET); + +TimeChangeRule EEST = {Last, Sun, Mar, 3, 180}; //Central European Summer Time +TimeChangeRule EET = {Last, Sun, Oct, 4, 120}; //Central European Standard Time +Timezone tzEUEastern(EEST, EET); + +TimeChangeRule EDT = {Second, Sun, Mar, 2, -240 }; //Daylight time = UTC - 4 hours +TimeChangeRule EST = {First, Sun, Nov, 2, -300 }; //Standard time = UTC - 5 hours +Timezone tzUSEastern(EDT, EST); + +TimeChangeRule CDT = {Second, Sun, Mar, 2, -300 }; //Daylight time = UTC - 5 hours +TimeChangeRule CST = {First, Sun, Nov, 2, -360 }; //Standard time = UTC - 6 hours +Timezone tzUSCentral(CDT, CST); + +Timezone tzCASaskatchewan(CST, CST); //Central without DST + +TimeChangeRule MDT = {Second, Sun, Mar, 2, -360 }; //Daylight time = UTC - 6 hours +TimeChangeRule MST = {First, Sun, Nov, 2, -420 }; //Standard time = UTC - 7 hours +Timezone tzUSMountain(MDT, MST); + +Timezone tzUSArizona(MST, MST); //Mountain without DST + +TimeChangeRule PDT = {Second, Sun, Mar, 2, -420 }; //Daylight time = UTC - 7 hours +TimeChangeRule PST = {First, Sun, Nov, 2, -480 }; //Standard time = UTC - 8 hours +Timezone tzUSPacific(PDT, PST); + +TimeChangeRule ChST = {Last, Sun, Mar, 1, 480}; // China Standard Time = UTC + 8 hours +Timezone tzChina(ChST, ChST); + +TimeChangeRule JST = {Last, Sun, Mar, 1, 540}; // Japan Standard Time = UTC + 9 hours +Timezone tzJapan(JST, JST); + +TimeChangeRule AEDT = {Second, Sun, Oct, 2, 660 }; //Daylight time = UTC + 11 hours +TimeChangeRule AEST = {First, Sun, Apr, 3, 600 }; //Standard time = UTC + 10 hours +Timezone tzAUEastern(AEDT, AEST); + +TimeChangeRule NZDT = {Second, Sun, Sep, 2, 780 }; //Daylight time = UTC + 13 hours +TimeChangeRule NZST = {First, Sun, Apr, 3, 720 }; //Standard time = UTC + 12 hours +Timezone tzNZ(NZDT, NZST); + +TimeChangeRule NKST = {Last, Sun, Mar, 1, 510}; //Pyongyang Time = UTC + 8.5 hours +Timezone tzNK(NKST, NKST); + +TimeChangeRule IST = {Last, Sun, Mar, 1, 330}; // India Standard Time = UTC + 5.5 hours +Timezone tzIndia(IST, IST); + +Timezone* timezones[] = {&tzUTC, &tzUK, &tzEUCentral, &tzEUEastern, &tzUSEastern, &tzUSCentral, &tzUSMountain, &tzUSArizona, &tzUSPacific, &tzChina, &tzJapan, &tzAUEastern, &tzNZ, &tzNK, &tzIndia, &tzCASaskatchewan}; + +void handleNetworkTime(); +void sendNTPPacket(); +bool checkNTPResponse(); +void updateLocalTime(); +void getTimeString(char* out); +bool checkCountdown(); +void setCountdown(); +byte weekdayMondayFirst(); +void checkTimers(); + +#endif // WLED_NTP_H \ No newline at end of file diff --git a/wled00/wled11_ol.ino b/wled00/wled_overlay.cpp similarity index 98% rename from wled00/wled11_ol.ino rename to wled00/wled_overlay.cpp index 93f7b96d..93348e79 100644 --- a/wled00/wled11_ol.ino +++ b/wled00/wled_overlay.cpp @@ -1,6 +1,5 @@ -/* - * Used to draw clock overlays over the strip - */ +#include "wled_overlay.h" +#include "wled.h" void initCronixie() { diff --git a/wled00/wled_overlay.h b/wled00/wled_overlay.h new file mode 100644 index 00000000..47573978 --- /dev/null +++ b/wled00/wled_overlay.h @@ -0,0 +1,12 @@ +#ifndef WLED_OVERLAYS_H +#define WLED_OVERLAYS_H +#include +/* + * Used to draw clock overlays over the strip + */ + +void initCronixie(); +void handleOverlays(); +void handleOverlayDraw(); + +#endif // WLED_OVERLAY_H \ No newline at end of file diff --git a/wled00/wled18_server.ino b/wled00/wled_server.cpp similarity index 99% rename from wled00/wled18_server.ino rename to wled00/wled_server.cpp index f11a64be..c73e027f 100644 --- a/wled00/wled18_server.ino +++ b/wled00/wled_server.cpp @@ -1,6 +1,5 @@ -/* - * Server page definitions - */ +#include "wled_server.h" +#include "wled.h" //Is this an IP? bool isIp(String str) { @@ -303,7 +302,7 @@ String msgProcessor(const String& var) } -void serveMessage(AsyncWebServerRequest* request, uint16_t code, String headl, String subl="", byte optionT=255) +void serveMessage(AsyncWebServerRequest* request, uint16_t code, String headl, String subl, byte optionT) { messageHead = headl; messageSub = subl; diff --git a/wled00/wled_server.h b/wled00/wled_server.h new file mode 100644 index 00000000..08679e17 --- /dev/null +++ b/wled00/wled_server.h @@ -0,0 +1,18 @@ +#ifndef WLED_SERVER_H +#define WLED_SERVER_H +/* + * Server page declarations + */ + +bool isIp(String str); +bool captivePortal(AsyncWebServerRequest *request); +void initServer(); +void serveIndexOrWelcome(AsyncWebServerRequest *request); +void serveIndex(AsyncWebServerRequest* request); +String msgProcessor(const String& var); +void serveMessage(AsyncWebServerRequest* request, uint16_t code, String headl, String subl="", byte optionT=255); +String settingsProcessor(const String& var); +String dmxProcessor(const String& var); +void serveSettings(AsyncWebServerRequest* request); + +#endif //WLED_SERVER_H \ No newline at end of file diff --git a/wled00/wled03_set.ino b/wled00/wled_set.cpp similarity index 99% rename from wled00/wled03_set.ino rename to wled00/wled_set.cpp index fc82256d..d81cac91 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled_set.cpp @@ -1,3 +1,7 @@ +#include "wled_set.h" +#include "wled.h" +#include "wled_hue.h" +#include "wled_colors.h" /* * Receives client input */ diff --git a/wled00/wled_set.h b/wled00/wled_set.h new file mode 100644 index 00000000..7efde0d7 --- /dev/null +++ b/wled00/wled_set.h @@ -0,0 +1,12 @@ +#ifndef WLED_SET_H +#define WLED_SET_H +#include +#include + +void _setRandomColor(bool _sec,bool fromButton=false); +bool isAsterisksOnly(const char* str, byte maxLen); +void handleSettingsSet(AsyncWebServerRequest *request, byte subPage); +int getNumVal(const String* req, uint16_t pos); +bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte maxv=255); + +#endif // WLED_SET_H \ No newline at end of file diff --git a/wled00/wled02_xml.ino b/wled00/wled_xml.cpp similarity index 99% rename from wled00/wled02_xml.ino rename to wled00/wled_xml.cpp index 100be482..07009cf3 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/wled_xml.cpp @@ -1,6 +1,7 @@ -/* - * Sending XML status files to client - */ +#include "wled_xml.h" +#include "wled.h" +#include "wled_eeprom.h" + //build XML response to HTTP /win API request char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr) diff --git a/wled00/wled_xml.h b/wled00/wled_xml.h new file mode 100644 index 00000000..5cfb1dbb --- /dev/null +++ b/wled00/wled_xml.h @@ -0,0 +1,15 @@ +#ifndef WLED_XML_H +#define WLED_XML_H +#include +#include + +/* + * Sending XML status files to client + */ +char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr); +char* URL_response(AsyncWebServerRequest *request); +void sappend(char stype, const char* key, int val); +void sappends(char stype, const char* key, char* val); +void getSettingsJS(byte subPage, char* dest); + +#endif // WLED_XML_H \ No newline at end of file From 12131764d1e7ce305063021b6b4f581593eabfb3 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Wed, 25 Mar 2020 04:36:55 -0400 Subject: [PATCH 172/232] Prior to refactoring includes and forward definitions. --- .../src/dependencies/espalexa/EspalexaDevice.h | 2 +- wled00/wled.cpp | 18 ++++++++++++++++-- wled00/wled.h | 5 +---- wled00/wled00.ino | 6 +++--- wled00/wled_alexa.cpp | 1 + wled00/wled_alexa.h | 5 +++-- wled00/wled_eeprom.cpp | 5 ++++- wled00/wled_eeprom.h | 1 + wled00/wled_file.cpp | 4 ++++ wled00/wled_server.cpp | 5 +++++ wled00/wled_server.h | 1 + wled00/wled_set.cpp | 11 +++++++---- wled00/wled_set.h | 4 ++++ .../{wled06_usermod.cpp => wled_usermod.cpp} | 0 wled00/wled_usermod.h | 8 ++++++++ 15 files changed, 59 insertions(+), 17 deletions(-) rename wled00/{wled06_usermod.cpp => wled_usermod.cpp} (100%) create mode 100644 wled00/wled_usermod.h diff --git a/wled00/src/dependencies/espalexa/EspalexaDevice.h b/wled00/src/dependencies/espalexa/EspalexaDevice.h index 4785591f..b964e0e3 100644 --- a/wled00/src/dependencies/espalexa/EspalexaDevice.h +++ b/wled00/src/dependencies/espalexa/EspalexaDevice.h @@ -1,6 +1,6 @@ #ifndef EspalexaDevice_h #define EspalexaDevice_h - +#include #include "Arduino.h" typedef class EspalexaDevice; diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 50a83f27..97ed3878 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -1,4 +1,18 @@ #include "wled.h" +#include "wled_led.h" +#include "wled_ir.h" +#include "wled_notify.h" +#include "wled_alexa.h" +#include "wled_overlay.h" +#include "wled_file.h" +#include "wled_button.h" +#include "wled_ntp.h" +#include "wled_usermod.h" +#include "wled_blynk.h" +#include "wled_hue.h" +#include "wled_mqtt.h" +#include "wled_eeprom.h" +#include "wled_server.h" WLED::WLED() { @@ -251,7 +265,7 @@ void WLED::beginStrip() #endif } -void WLED::initAP(bool resetAP = false) +void WLED::initAP(bool resetAP) { if (apBehavior == AP_BEHAVIOR_BUTTON_ONLY && !resetAP) return; @@ -483,7 +497,7 @@ void WLED::handleConnection() } //by https://github.com/tzapu/WiFiManager/blob/master/WiFiManager.cpp -int WLED::getSignalQuality(int rssi) +int getSignalQuality(int rssi) { int quality = 0; diff --git a/wled00/wled.h b/wled00/wled.h index 4f9b79af..8ef30cbf 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -491,6 +491,7 @@ int loops = 0; bool oappend(const char *txt); //append new number to temp buffer efficiently bool oappendi(int i); +int getSignalQuality(int rssi); class WLED { @@ -503,8 +504,6 @@ public: WLED(); - void wledInit(); - void reset(); void loop(); @@ -515,8 +514,6 @@ public: wledInit(); } - void loop(); - private: void wledInit(); void beginStrip(); diff --git a/wled00/wled00.ino b/wled00/wled00.ino index fc06f211..ac993bf6 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -3,11 +3,11 @@ */ #include "wled.h" +WLED wled; void setup() { - //auto& wled = Wled(); + wled.instance(); // Force creation of static instance } void loop() { - - + wled.loop(); } \ No newline at end of file diff --git a/wled00/wled_alexa.cpp b/wled00/wled_alexa.cpp index b68843cb..ce63a019 100644 --- a/wled00/wled_alexa.cpp +++ b/wled00/wled_alexa.cpp @@ -1,6 +1,7 @@ #include "wled_alexa.h" #include "wled.h" #include "src/dependencies/espalexa/EspalexaDevice.h" +#include "const.h" #ifndef WLED_DISABLE_ALEXA void onAlexaChange(EspalexaDevice* dev); diff --git a/wled00/wled_alexa.h b/wled00/wled_alexa.h index 4bf7bd12..94ad0342 100644 --- a/wled00/wled_alexa.h +++ b/wled00/wled_alexa.h @@ -7,10 +7,11 @@ * https://github.com/kakopappa/arduino-esp8266-alexa-wemo-switch * https://github.com/probonopd/ESP8266HueEmulator */ -class EspalexaDevice; +#include "src/dependencies/espalexa/EspalexaDevice.h" + void onAlexaChange(EspalexaDevice* dev); void alexaInit(); void handleAlexa(); void onAlexaChange(EspalexaDevice* dev); -#define WLED_ALEXA_H \ No newline at end of file +#endif // WLED_ALEXA_H \ No newline at end of file diff --git a/wled00/wled_eeprom.cpp b/wled00/wled_eeprom.cpp index 0ddbf57f..1e2b0e62 100644 --- a/wled00/wled_eeprom.cpp +++ b/wled00/wled_eeprom.cpp @@ -1,7 +1,10 @@ #include "wled_eeprom.h" #include "wled.h" +#include "wled_cronixie.h" +#include "wled_ntp.h" +#include "wled_set.h" +#include "wled_led.h" -#define EEPSIZE 2560 //Maximum is 4096 //eeprom Version code, enables default settings instead of 0 init on update #define EEPVER 18 diff --git a/wled00/wled_eeprom.h b/wled00/wled_eeprom.h index fe5c2cbf..19f61925 100644 --- a/wled00/wled_eeprom.h +++ b/wled00/wled_eeprom.h @@ -5,6 +5,7 @@ * Methods to handle saving and loading to non-volatile memory * EEPROM Map: https://github.com/Aircoookie/WLED/wiki/EEPROM-Map */ +#define EEPSIZE 2560 //Maximum is 4096 void commit(); void clearEEPROM(); diff --git a/wled00/wled_file.cpp b/wled00/wled_file.cpp index 700d66ee..81b86d6d 100644 --- a/wled00/wled_file.cpp +++ b/wled00/wled_file.cpp @@ -1,5 +1,8 @@ #include "wled_file.h" #include "wled.h" +#include "wled_led.h" +#include "wled_notify.h" + //filesystem #ifndef WLED_DISABLE_FILESYSTEM #include @@ -21,6 +24,7 @@ enum class AdaState { Data_Blue }; +// Maybe Adalight should not be in filehandling? TODO void handleSerial() { #ifdef WLED_ENABLE_ADALIGHT diff --git a/wled00/wled_server.cpp b/wled00/wled_server.cpp index c73e027f..542abea4 100644 --- a/wled00/wled_server.cpp +++ b/wled00/wled_server.cpp @@ -1,5 +1,10 @@ #include "wled_server.h" #include "wled.h" +#include "wled_file.h" +#include "wled_set.h" +#include "wled_json.h" +#include "wled_xml.h" + //Is this an IP? bool isIp(String str) { diff --git a/wled00/wled_server.h b/wled00/wled_server.h index 08679e17..a03f83e1 100644 --- a/wled00/wled_server.h +++ b/wled00/wled_server.h @@ -1,5 +1,6 @@ #ifndef WLED_SERVER_H #define WLED_SERVER_H +#include /* * Server page declarations */ diff --git a/wled00/wled_set.cpp b/wled00/wled_set.cpp index d81cac91..aff3f0cd 100644 --- a/wled00/wled_set.cpp +++ b/wled00/wled_set.cpp @@ -1,10 +1,13 @@ #include "wled_set.h" #include "wled.h" -#include "wled_hue.h" #include "wled_colors.h" -/* - * Receives client input - */ +#include "wled_hue.h" +#include "wled_led.h" +#include "wled_blynk.h" +#include "wled_eeprom.h" +#include "wled_alexa.h" +#include "wled_cronixie.h" +#include "wled_xml.h" void _setRandomColor(bool _sec,bool fromButton=false) { diff --git a/wled00/wled_set.h b/wled00/wled_set.h index 7efde0d7..ebee8c92 100644 --- a/wled00/wled_set.h +++ b/wled00/wled_set.h @@ -2,10 +2,14 @@ #define WLED_SET_H #include #include +/* + * Receives client input + */ void _setRandomColor(bool _sec,bool fromButton=false); bool isAsterisksOnly(const char* str, byte maxLen); void handleSettingsSet(AsyncWebServerRequest *request, byte subPage); +bool handleSet(AsyncWebServerRequest *request, const String& req); int getNumVal(const String* req, uint16_t pos); bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte maxv=255); diff --git a/wled00/wled06_usermod.cpp b/wled00/wled_usermod.cpp similarity index 100% rename from wled00/wled06_usermod.cpp rename to wled00/wled_usermod.cpp diff --git a/wled00/wled_usermod.h b/wled00/wled_usermod.h new file mode 100644 index 00000000..61ef6b78 --- /dev/null +++ b/wled00/wled_usermod.h @@ -0,0 +1,8 @@ +#ifndef WLED_USERMOD_H +#define WLED_USERMOD_H + +void userSetup(); +void userConnected(); +void userLoop(); + +#endif // WLED_USERMOD_H \ No newline at end of file From 6f5e71164ae2174b47354c489b0ef0dda9fd66bd Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Wed, 25 Mar 2020 05:14:23 -0400 Subject: [PATCH 173/232] Further fixing of includes. --- wled00/wled.h | 2 +- wled00/wled_alexa.cpp | 4 +++- wled00/wled_button.cpp | 6 +++++- wled00/wled_eeprom.cpp | 6 +++--- wled00/wled_hue.cpp | 2 ++ wled00/wled_hue.h | 6 ++++++ wled00/wled_json.cpp | 2 ++ wled00/wled_json.h | 6 ++++++ wled00/wled_led.cpp | 5 +++++ wled00/wled_mqtt.cpp | 2 ++ wled00/wled_notify.cpp | 5 +++-- wled00/wled_notify.h | 2 +- 12 files changed, 39 insertions(+), 9 deletions(-) diff --git a/wled00/wled.h b/wled00/wled.h index 8ef30cbf..c04dfa87 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -514,7 +514,7 @@ public: wledInit(); } -private: +public: // TODO: privacy void wledInit(); void beginStrip(); diff --git a/wled00/wled_alexa.cpp b/wled00/wled_alexa.cpp index ce63a019..1eb33e71 100644 --- a/wled00/wled_alexa.cpp +++ b/wled00/wled_alexa.cpp @@ -1,7 +1,9 @@ #include "wled_alexa.h" #include "wled.h" -#include "src/dependencies/espalexa/EspalexaDevice.h" #include "const.h" +#include "wled_led.h" +#include "wled_eeprom.h" +#include "wled_colors.h" #ifndef WLED_DISABLE_ALEXA void onAlexaChange(EspalexaDevice* dev); diff --git a/wled00/wled_button.cpp b/wled00/wled_button.cpp index 12fe5b49..3825cc84 100644 --- a/wled00/wled_button.cpp +++ b/wled00/wled_button.cpp @@ -1,5 +1,9 @@ #include "wled_button.h" #include "wled.h" +#include "wled_led.h" +#include "wled_eeprom.h" +#include "wled_set.h" + /* * Physical IO */ @@ -46,7 +50,7 @@ void handleButton() if (dur > 6000) //long press { - initAP(true); + WLED::instance().initAP(true); } else if (!buttonLongPressed) { //short press if (macroDoublePress) diff --git a/wled00/wled_eeprom.cpp b/wled00/wled_eeprom.cpp index 1e2b0e62..04b86289 100644 --- a/wled00/wled_eeprom.cpp +++ b/wled00/wled_eeprom.cpp @@ -592,7 +592,7 @@ void savedToPresets() } } -bool applyPreset(byte index, bool loadBri = true) +bool applyPreset(byte index, bool loadBri) { if (index == 255 || index == 0) { @@ -630,7 +630,7 @@ bool applyPreset(byte index, bool loadBri = true) return true; } -void savePreset(byte index, bool persist = true) +void savePreset(byte index, bool persist) { if (index > 16) return; if (index < 1) {saveSettingsToEEPROM();return;} @@ -698,7 +698,7 @@ void applyMacro(byte index) } -void saveMacro(byte index, String mc, bool persist = true) //only commit on single save, not in settings +void saveMacro(byte index, String mc, bool persist) //only commit on single save, not in settings { index-=1; if (index > 15) return; diff --git a/wled00/wled_hue.cpp b/wled00/wled_hue.cpp index 082685d7..abdb4ffa 100644 --- a/wled00/wled_hue.cpp +++ b/wled00/wled_hue.cpp @@ -1,5 +1,7 @@ #include "wled_hue.h" #include "wled.h" +#include "wled_colors.h" +#include "wled_eeprom.h" #ifndef WLED_DISABLE_HUESYNC diff --git a/wled00/wled_hue.h b/wled00/wled_hue.h index dcf39729..e9aa22b2 100644 --- a/wled00/wled_hue.h +++ b/wled00/wled_hue.h @@ -3,8 +3,14 @@ /* * Sync to Philips hue lights */ +#include +#include void handleHue(); void reconnectHue(); +void onHueError(void* arg, AsyncClient* client, int8_t error); +void onHueConnect(void* arg, AsyncClient* client); +void sendHuePoll(); +void onHueData(void* arg, AsyncClient* client, void *data, size_t len); #endif //WLED_HUE_H \ No newline at end of file diff --git a/wled00/wled_json.cpp b/wled00/wled_json.cpp index 43d3136d..17359462 100644 --- a/wled00/wled_json.cpp +++ b/wled00/wled_json.cpp @@ -1,5 +1,7 @@ #include "wled_json.h" #include "wled.h" +#include "wled_eeprom.h" +#include "wled_led.h" void deserializeSegment(JsonObject elem, byte it) { diff --git a/wled00/wled_json.h b/wled00/wled_json.h index 83830faf..a7c2473a 100644 --- a/wled00/wled_json.h +++ b/wled00/wled_json.h @@ -3,6 +3,12 @@ /* * JSON API (De)serialization */ +#include +#include "ESPAsyncWebServer.h" +#include "src/dependencies/json/ArduinoJson-v6.h" +#include "src/dependencies/json/AsyncJson-v6.h" +#include "fx.h" +// TODO: AsynicWebServerRequest conflict? void deserializeSegment(JsonObject elem, byte it); bool deserializeState(JsonObject root); diff --git a/wled00/wled_led.cpp b/wled00/wled_led.cpp index aefb004a..bd657a53 100644 --- a/wled00/wled_led.cpp +++ b/wled00/wled_led.cpp @@ -1,5 +1,10 @@ #include "wled_led.h" #include "wled.h" +#include "wled_notify.h" +#include "wled_blynk.h" +#include "wled_eeprom.h" +#include "wled_mqtt.h" +#include "wled_colors.h" void setValuesFromMainSeg() { diff --git a/wled00/wled_mqtt.cpp b/wled00/wled_mqtt.cpp index d4fc80c0..ce69513b 100644 --- a/wled00/wled_mqtt.cpp +++ b/wled00/wled_mqtt.cpp @@ -1,5 +1,7 @@ #include "wled_mqtt.h" #include "wled.h" +#include "wled_notify.h" +#include "wled_led.h" #ifdef WLED_ENABLE_MQTT #define MQTT_KEEP_ALIVE_TIME 60 // contact the MQTT broker every 60 seconds diff --git a/wled00/wled_notify.cpp b/wled00/wled_notify.cpp index 7827aba9..7913b452 100644 --- a/wled00/wled_notify.cpp +++ b/wled00/wled_notify.cpp @@ -1,12 +1,13 @@ #include "wled_notify.h" #include "wled.h" #include "src/dependencies/e131/ESPAsyncE131.h" +#include "wled_led.h" #define WLEDPACKETSIZE 29 #define UDP_IN_MAXSIZE 1472 -void notify(byte callMode, bool followUp=false) +void notify(byte callMode, bool followUp) { if (!udpConnected) return; switch (callMode) @@ -71,7 +72,7 @@ void notify(byte callMode, bool followUp=false) } -void arlsLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC) +void arlsLock(uint32_t timeoutMs, byte md) { if (!realtimeMode){ for (uint16_t i = 0; i < ledCount; i++) diff --git a/wled00/wled_notify.h b/wled00/wled_notify.h index 63d01142..2a2865b3 100644 --- a/wled00/wled_notify.h +++ b/wled00/wled_notify.h @@ -5,7 +5,7 @@ /* * UDP notifier */ -union e131_packet_t; // Will this compile? +//union e131_packet_t; // Will this compile? class IPAddress; void notify(byte callMode, bool followUp=false); From ccf5e66f316f8d92063cf85a174abfec32226474 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Wed, 25 Mar 2020 05:43:12 -0400 Subject: [PATCH 174/232] Compiles, but does not link. --- wled00/wled_hue.cpp | 2 ++ wled00/wled_hue.h | 2 +- wled00/wled_mqtt.cpp | 3 +++ wled00/wled_notify.h | 1 + wled00/wled_overlay.cpp | 2 ++ wled00/wled_overlay.h | 2 ++ wled00/wled_server.h | 2 ++ wled00/wled_set.cpp | 4 ++-- wled00/wled_xml.cpp | 3 ++- 9 files changed, 17 insertions(+), 4 deletions(-) diff --git a/wled00/wled_hue.cpp b/wled00/wled_hue.cpp index abdb4ffa..61756ec0 100644 --- a/wled00/wled_hue.cpp +++ b/wled00/wled_hue.cpp @@ -2,6 +2,8 @@ #include "wled.h" #include "wled_colors.h" #include "wled_eeprom.h" +#include "wled_notify.h" +#include "wled_led.h" #ifndef WLED_DISABLE_HUESYNC diff --git a/wled00/wled_hue.h b/wled00/wled_hue.h index e9aa22b2..8b26f8b6 100644 --- a/wled00/wled_hue.h +++ b/wled00/wled_hue.h @@ -4,7 +4,7 @@ * Sync to Philips hue lights */ #include -#include +class AsyncClient; void handleHue(); void reconnectHue(); diff --git a/wled00/wled_mqtt.cpp b/wled00/wled_mqtt.cpp index ce69513b..5e96a18f 100644 --- a/wled00/wled_mqtt.cpp +++ b/wled00/wled_mqtt.cpp @@ -2,6 +2,9 @@ #include "wled.h" #include "wled_notify.h" #include "wled_led.h" +#include "wled_colors.h" +#include "wled_xml.h" +#include "wled_set.h" #ifdef WLED_ENABLE_MQTT #define MQTT_KEEP_ALIVE_TIME 60 // contact the MQTT broker every 60 seconds diff --git a/wled00/wled_notify.h b/wled00/wled_notify.h index 2a2865b3..04c65aa4 100644 --- a/wled00/wled_notify.h +++ b/wled00/wled_notify.h @@ -1,6 +1,7 @@ #ifndef WLED_NOTIFY_H #define WLED_NOTIFY_H #include +#include "src/dependencies/e131/ESPAsyncE131.h" #include "const.h" /* * UDP notifier diff --git a/wled00/wled_overlay.cpp b/wled00/wled_overlay.cpp index 93348e79..8cb70786 100644 --- a/wled00/wled_overlay.cpp +++ b/wled00/wled_overlay.cpp @@ -1,5 +1,7 @@ #include "wled_overlay.h" #include "wled.h" +#include "wled_cronixie.h" +#include "wled_ntp.h" void initCronixie() { diff --git a/wled00/wled_overlay.h b/wled00/wled_overlay.h index 47573978..ec18f45b 100644 --- a/wled00/wled_overlay.h +++ b/wled00/wled_overlay.h @@ -8,5 +8,7 @@ void initCronixie(); void handleOverlays(); void handleOverlayDraw(); +void _overlayAnalogCountdown(); +void _overlayAnalogClock(); #endif // WLED_OVERLAY_H \ No newline at end of file diff --git a/wled00/wled_server.h b/wled00/wled_server.h index a03f83e1..46adc962 100644 --- a/wled00/wled_server.h +++ b/wled00/wled_server.h @@ -4,6 +4,8 @@ /* * Server page declarations */ +class AsyncWebServerRequest; + bool isIp(String str); bool captivePortal(AsyncWebServerRequest *request); diff --git a/wled00/wled_set.cpp b/wled00/wled_set.cpp index aff3f0cd..66d9ff1e 100644 --- a/wled00/wled_set.cpp +++ b/wled00/wled_set.cpp @@ -9,7 +9,7 @@ #include "wled_cronixie.h" #include "wled_xml.h" -void _setRandomColor(bool _sec,bool fromButton=false) +void _setRandomColor(bool _sec,bool fromButton) { lastRandomIndex = strip.get_random_wheel_index(lastRandomIndex); if (_sec){ @@ -337,7 +337,7 @@ int getNumVal(const String* req, uint16_t pos) //helper to get int value at a position in string -bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte maxv=255) +bool updateVal(const String* req, const char* key, byte* val, byte minv, byte maxv) { int pos = req->indexOf(key); if (pos < 1) return false; diff --git a/wled00/wled_xml.cpp b/wled00/wled_xml.cpp index 07009cf3..4f2b3f45 100644 --- a/wled00/wled_xml.cpp +++ b/wled00/wled_xml.cpp @@ -1,10 +1,11 @@ #include "wled_xml.h" #include "wled.h" #include "wled_eeprom.h" +#include "wled_ntp.h" //build XML response to HTTP /win API request -char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr) +char* XML_response(AsyncWebServerRequest *request, char* dest) { char sbuf[(dest == nullptr)?1024:1]; //allocate local buffer if none passed obuf = (dest == nullptr)? sbuf:dest; From 07d6bfa98927c47a40742434f60986b485616eb3 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Wed, 25 Mar 2020 11:17:45 +0100 Subject: [PATCH 175/232] Added Pacifica effect (closes #790) --- CHANGELOG.md | 6 + wled00/FX.cpp | 106 ++++++++++++++++++ wled00/FX.h | 12 +- wled00/palettes.h | 11 +- .../dependencies/espalexa/EspalexaDevice.h | 1 + wled00/wled00.ino | 2 +- 6 files changed, 132 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68243a3b..f5caf7b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ### Development versions after 0.9.1 release +#### Build 2003251 + +- Added Pacifica effect (tentative, doesn't yet support other colors) +- Added Atlantica palette +- Fixed ESP32 build of Espalexa + #### Build 2003222 - Fixed Alexa Whites on non-RGBW lights (bump Espalexa to 2.4.5) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 022b4b23..1e3ec2d9 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -3094,6 +3094,7 @@ uint16_t WS2812FX::mode_plasma(void) { return FRAMETIME; } + /* * Percentage display * Intesity values from 0-100 turn on the leds. @@ -3167,3 +3168,108 @@ uint16_t WS2812FX::mode_heartbeat(void) { return FRAMETIME; } + + +// "Pacifica" +// Gentle, blue-green ocean waves. +// December 2019, Mark Kriegsman and Mary Corey March. +// For Dan. +// +// +// In this animation, there are four "layers" of waves of light. +// +// Each layer moves independently, and each is scaled separately. +// +// All four wave layers are added together on top of each other, and then +// another filter is applied that adds "whitecaps" of brightness where the +// waves line up with each other more. Finally, another pass is taken +// over the led array to 'deepen' (dim) the blues and greens. +// +// The speed and scale and motion each layer varies slowly within independent +// hand-chosen ranges, which is why the code has a lot of low-speed 'beatsin8' functions +// with a lot of oddly specific numeric ranges. +// +// These three custom blue-green color palettes were inspired by the colors found in +// the waters off the southern coast of California, https://goo.gl/maps/QQgd97jjHesHZVxQ7 +// +// Modified for WLED, based on https://github.com/FastLED/FastLED/blob/master/examples/Pacifica/Pacifica.ino +// +uint16_t WS2812FX::mode_pacifica() +{ + CRGBPalette16 pacifica_palette_1 = + { 0x000507, 0x000409, 0x00030B, 0x00030D, 0x000210, 0x000212, 0x000114, 0x000117, + 0x000019, 0x00001C, 0x000026, 0x000031, 0x00003B, 0x000046, 0x14554B, 0x28AA50 }; + CRGBPalette16 pacifica_palette_2 = + { 0x000507, 0x000409, 0x00030B, 0x00030D, 0x000210, 0x000212, 0x000114, 0x000117, + 0x000019, 0x00001C, 0x000026, 0x000031, 0x00003B, 0x000046, 0x0C5F52, 0x19BE5F }; + CRGBPalette16 pacifica_palette_3 = + { 0x000208, 0x00030E, 0x000514, 0x00061A, 0x000820, 0x000927, 0x000B2D, 0x000C33, + 0x000E39, 0x001040, 0x001450, 0x001860, 0x001C70, 0x002080, 0x1040BF, 0x2060FF }; + // Increment the four "color index start" counters, one for each wave layer. + // Each is incremented at a different speed, and the speeds vary over time. + uint16_t sCIStart1 = SEGENV.aux0, sCIStart2 = SEGENV.aux1, sCIStart3 = SEGENV.step, sCIStart4 = SEGENV.step >> 16; + //static uint16_t sCIStart1, sCIStart2, sCIStart3, sCIStart4; + uint32_t deltams = 26 + (SEGMENT.speed >> 3); + + uint16_t speedfactor1 = beatsin16(3, 179, 269); + uint16_t speedfactor2 = beatsin16(4, 179, 269); + uint32_t deltams1 = (deltams * speedfactor1) / 256; + uint32_t deltams2 = (deltams * speedfactor2) / 256; + uint32_t deltams21 = (deltams1 + deltams2) / 2; + sCIStart1 += (deltams1 * beatsin88(1011,10,13)); + sCIStart2 -= (deltams21 * beatsin88(777,8,11)); + sCIStart3 -= (deltams1 * beatsin88(501,5,7)); + sCIStart4 -= (deltams2 * beatsin88(257,4,6)); + SEGENV.aux0 = sCIStart1; SEGENV.aux1 = sCIStart2; + SEGENV.step = sCIStart4; SEGENV.step = (SEGENV.step << 16) + sCIStart3; + + // Clear out the LED array to a dim background blue-green + //fill(132618); + + uint8_t basethreshold = beatsin8( 9, 55, 65); + uint8_t wave = beat8( 7 ); + + for( uint16_t i = 0; i < SEGLEN; i++) { + CRGB c = CRGB(2, 6, 10); + // Render each of four layers, with different scales and speeds, that vary over time + c += pacifica_one_layer(i, pacifica_palette_1, sCIStart1, beatsin16(3, 11 * 256, 14 * 256), beatsin8(10, 70, 130), 0-beat16(301)); + c += pacifica_one_layer(i, pacifica_palette_2, sCIStart2, beatsin16(4, 6 * 256, 9 * 256), beatsin8(17, 40, 80), beat16(401)); + c += pacifica_one_layer(i, pacifica_palette_3, sCIStart3, 6 * 256 , beatsin8(9, 10,38) , 0-beat16(503)); + c += pacifica_one_layer(i, pacifica_palette_3, sCIStart4, 5 * 256 , beatsin8(8, 10,28) , beat16(601)); + + // Add extra 'white' to areas where the four layers of light have lined up brightly + uint8_t threshold = scale8( sin8( wave), 20) + basethreshold; + wave += 7; + uint8_t l = c.getAverageLight(); + if (l > threshold) { + uint8_t overage = l - threshold; + uint8_t overage2 = qadd8(overage, overage); + c += CRGB(overage, overage2, qadd8(overage2, overage2)); + } + + //deepen the blues and greens + c.blue = scale8(c.blue, 145); + c.green = scale8(c.green, 200); + c |= CRGB( 2, 5, 7); + + setPixelColor(i, c.red, c.green, c.blue); + } + + return FRAMETIME; +} + +// Add one layer of waves into the led array +CRGB WS2812FX::pacifica_one_layer(uint16_t i, CRGBPalette16& p, uint16_t cistart, uint16_t wavescale, uint8_t bri, uint16_t ioff) +{ + uint16_t ci = cistart; + uint16_t waveangle = ioff; + uint16_t wavescale_half = (wavescale >> 1) + 20; + + waveangle += ((120 + SEGMENT.intensity) * i); //original 250 * i + uint16_t s16 = sin16(waveangle) + 32768; + uint16_t cs = scale16(s16, wavescale_half) + wavescale_half; + ci += (cs * i); + uint16_t sindex16 = sin16(ci) + 32768; + uint8_t sindex8 = scale16(sindex16, 240); + return ColorFromPalette(p, sindex8, bri, LINEARBLEND); +} diff --git a/wled00/FX.h b/wled00/FX.h index d24250f9..b6ed7974 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -95,7 +95,7 @@ #define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) #define IS_SELECTED ((SEGMENT.options & SELECTED) == SELECTED ) -#define MODE_COUNT 101 +#define MODE_COUNT 102 #define FX_MODE_STATIC 0 #define FX_MODE_BLINK 1 @@ -198,6 +198,7 @@ #define FX_MODE_PERCENT 98 #define FX_MODE_RIPPLE_RAINBOW 99 #define FX_MODE_HEARTBEAT 100 +#define FX_MODE_PACIFICA 101 class WS2812FX { typedef uint16_t (WS2812FX::*mode_ptr)(void); @@ -387,6 +388,7 @@ class WS2812FX { _mode[FX_MODE_PERCENT] = &WS2812FX::mode_percent; _mode[FX_MODE_RIPPLE_RAINBOW] = &WS2812FX::mode_ripple_rainbow; _mode[FX_MODE_HEARTBEAT] = &WS2812FX::mode_heartbeat; + _mode[FX_MODE_PACIFICA] = &WS2812FX::mode_pacifica; _brightness = DEFAULT_BRIGHTNESS; currentPalette = CRGBPalette16(CRGB::Black); @@ -571,7 +573,8 @@ class WS2812FX { mode_plasma(void), mode_percent(void), mode_ripple_rainbow(void), - mode_heartbeat(void); + mode_heartbeat(void), + mode_pacifica(void); private: @@ -621,6 +624,7 @@ class WS2812FX { spots_base(uint16_t); CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat); + CRGB pacifica_one_layer(uint16_t i, CRGBPalette16& p, uint16_t cistart, uint16_t wavescale, uint8_t bri, uint16_t ioff); uint32_t _lastPaletteChange = 0; uint32_t _lastShow = 0; @@ -656,7 +660,7 @@ const char JSON_mode_names[] PROGMEM = R"=====([ "Noise 1","Noise 2","Noise 3","Noise 4","Colortwinkles","Lake","Meteor","Meteor Smooth","Railway","Ripple", "Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks Starburst", "Fireworks 1D","Bouncing Balls","Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn","Drip","Plasma","Percent","Ripple Rainbow", -"Heartbeat" +"Heartbeat","Pacifica" ])====="; @@ -666,7 +670,7 @@ const char JSON_palette_names[] PROGMEM = R"=====([ "Pastel","Sunset 2","Beech","Vintage","Departure","Landscape","Beach","Sherbet","Hult","Hult 64", "Drywet","Jul","Grintage","Rewhi","Tertiary","Fire","Icefire","Cyane","Light Pink","Autumn", "Magenta","Magred","Yelmag","Yelblu","Orange & Teal","Tiamat","April Night","Orangery","C9","Sakura", -"Aurora" +"Aurora","Atlantica" ])====="; #endif diff --git a/wled00/palettes.h b/wled00/palettes.h index 29384395..1b95e64b 100644 --- a/wled00/palettes.h +++ b/wled00/palettes.h @@ -13,7 +13,7 @@ #ifndef PalettesWLED_h #define PalettesWLED_h -#define GRADIENT_PALETTE_COUNT 38 +#define GRADIENT_PALETTE_COUNT 39 const byte ib_jul01_gp[] PROGMEM = { 0, 194, 1, 1, @@ -574,6 +574,14 @@ const byte Aurora_gp[] PROGMEM = { 170, 0,243, 45, 200, 0,135, 7, 255, 1, 5, 45};//deep blue + +const byte Atlantica_gp[] PROGMEM = { + 0, 0, 28,112, //#001C70 + 50, 32, 96,255, //#2060FF + 100, 0,243, 45, + 150, 12, 95, 82, //#0C5F52 + 200, 25,190, 95, //#19BE5F + 255, 40,170, 80};//#28AA50 // Single array of defined cpt-city color palettes. @@ -619,6 +627,7 @@ const byte* const gGradientPalettes[] PROGMEM = { C9_gp, //48-35 C9 Sakura_gp, //49-36 Sakura Aurora_gp, //50-37 Aurora + Atlantica_gp, //51-38 Atlantica }; #endif diff --git a/wled00/src/dependencies/espalexa/EspalexaDevice.h b/wled00/src/dependencies/espalexa/EspalexaDevice.h index 4785591f..0653a418 100644 --- a/wled00/src/dependencies/espalexa/EspalexaDevice.h +++ b/wled00/src/dependencies/espalexa/EspalexaDevice.h @@ -2,6 +2,7 @@ #define EspalexaDevice_h #include "Arduino.h" +#include typedef class EspalexaDevice; diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 7c64796d..a7eeb1f5 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -118,7 +118,7 @@ #endif //version code in format yymmddb (b = daily build) -#define VERSION 2003222 +#define VERSION 2003251 char versionString[] = "0.9.1"; From c4512b75d9e2b0c64179a7406131061f31d8ad1b Mon Sep 17 00:00:00 2001 From: cschwinne Date: Thu, 26 Mar 2020 01:44:38 +0100 Subject: [PATCH 176/232] Fixed live preview not displaying whole light if over 255 LEDs --- CHANGELOG.md | 30 +++++++++++++++++------------- wled00/wled00.ino | 2 +- wled00/wled19_json.ino | 4 ++-- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5caf7b5..56afa3ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,34 +2,38 @@ ### Development versions after 0.9.1 release +#### Build 2003261 + +- Fixed live preview not displaying whole light if over 255 LEDs + #### Build 2003251 -- Added Pacifica effect (tentative, doesn't yet support other colors) -- Added Atlantica palette -- Fixed ESP32 build of Espalexa +- Added Pacifica effect (tentative, doesn't yet support other colors) +- Added Atlantica palette +- Fixed ESP32 build of Espalexa #### Build 2003222 -- Fixed Alexa Whites on non-RGBW lights (bump Espalexa to 2.4.5) +- Fixed Alexa Whites on non-RGBW lights (bump Espalexa to 2.4.5) #### Build 2003221 -- Moved Cronixie driver from FX library to drawOverlay handler +- Moved Cronixie driver from FX library to drawOverlay handler #### Build 2003211 -- Added custom mapping compile define to FX_fcn.h -- Merged pull request #784 by @TravisDean: Fixed initialization bug when toggling skip first -- Added link to youtube videos by Room31 to readme +- Added custom mapping compile define to FX_fcn.h +- Merged pull request #784 by @TravisDean: Fixed initialization bug when toggling skip first +- Added link to youtube videos by Room31 to readme #### Build 2003141 -- Fixed color of main segment returned in JSON API during transition not being target color (closes #765) -- Fixed arlsLock() being called after pixels set in E1.31 (closes #772) -- Fixed HTTP API calls not having an effect if no segment selected (now applies to main segment) +- Fixed color of main segment returned in JSON API during transition not being target color (closes #765) +- Fixed arlsLock() being called after pixels set in E1.31 (closes #772) +- Fixed HTTP API calls not having an effect if no segment selected (now applies to main segment) #### Build 2003121 -- Created changelog.md - make tracking changes to code easier -- Merged pull request #766 by @pille: Fix E1.31 out-of sequence detection +- Created changelog.md - make tracking changes to code easier +- Merged pull request #766 by @pille: Fix E1.31 out-of sequence detection diff --git a/wled00/wled00.ino b/wled00/wled00.ino index a7eeb1f5..dc0fbb02 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -118,7 +118,7 @@ #endif //version code in format yymmddb (b = daily build) -#define VERSION 2003251 +#define VERSION 2003261 char versionString[] = "0.9.1"; diff --git a/wled00/wled19_json.ino b/wled00/wled19_json.ino index df9bcead..c0e23b4b 100644 --- a/wled00/wled19_json.ino +++ b/wled00/wled19_json.ino @@ -387,8 +387,8 @@ void serveJson(AsyncWebServerRequest* request) void serveLiveLeds(AsyncWebServerRequest* request) { - byte used = ledCount; - byte n = (used -1) /MAX_LIVE_LEDS +1; //only serve every n'th LED if count over MAX_LIVE_LEDS + uint16_t used = ledCount; + uint16_t n = (used -1) /MAX_LIVE_LEDS +1; //only serve every n'th LED if count over MAX_LIVE_LEDS char buffer[2000] = "{\"leds\":["; olen = 9; obuf = buffer; From f2329476ec18578e9acea0f9ebe1f6e332234aaa Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Thu, 26 Mar 2020 04:12:55 -0400 Subject: [PATCH 177/232] Fix linker errors. All global vars declared extern. --- wled00/wled.cpp | 364 +++++++++++++++++++++++++++++++++ wled00/wled.h | 469 +++++++++++++++++++++---------------------- wled00/wled_colors.h | 2 +- wled00/wled_ntp.cpp | 56 ++++++ wled00/wled_ntp.h | 57 ------ 5 files changed, 652 insertions(+), 296 deletions(-) diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 97ed3878..a720cb0b 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -13,6 +13,370 @@ #include "wled_mqtt.h" #include "wled_eeprom.h" #include "wled_server.h" +// Global Variable definitions +char versionString[] = "0.9.1"; + +//AP and OTA default passwords (for maximum change them!) +char apPass[65] = DEFAULT_AP_PASS; +char otaPass[33] = DEFAULT_OTA_PASS; + +//Hardware CONFIG (only changeble HERE, not at runtime) +//LED strip pin, button pin and IR pin changeable in NpbWrapper.h! + +byte auxDefaultState = 0; //0: input 1: high 2: low +byte auxTriggeredState = 0; //0: input 1: high 2: low +char ntpServerName[33] = "0.wled.pool.ntp.org"; //NTP server to use + +//WiFi CONFIG (all these can be changed via web UI, no need to set them here) +char clientSSID[33] = CLIENT_SSID; +char clientPass[65] = CLIENT_PASS; +char cmDNS[33] = "x"; //mDNS address (placeholder, will be replaced by wledXXXXXXXXXXXX.local) +char apSSID[33] = ""; //AP off by default (unless setup) +byte apChannel = 1; //2.4GHz WiFi AP channel (1-13) +byte apHide = 0; //hidden AP SSID +byte apBehavior = AP_BEHAVIOR_BOOT_NO_CONN; //access point opens when no connection after boot by default +IPAddress staticIP(0, 0, 0, 0); //static IP of ESP +IPAddress staticGateway(0, 0, 0, 0); //gateway (router) IP +IPAddress staticSubnet(255, 255, 255, 0); //most common subnet in home networks +bool noWifiSleep = false; //disabling modem sleep modes will increase heat output and power usage, but may help with connection issues + +//LED CONFIG +uint16_t ledCount = 30; //overcurrent prevented by ABL +bool useRGBW = false; //SK6812 strips can contain an extra White channel +#define ABL_MILLIAMPS_DEFAULT 850; //auto lower brightness to stay close to milliampere limit +bool turnOnAtBoot = true; //turn on LEDs at power-up +byte bootPreset = 0; //save preset to load after power-up + +byte col[]{255, 160, 0, 0}; //current RGB(W) primary color. col[] should be updated if you want to change the color. +byte colSec[]{0, 0, 0, 0}; //current RGB(W) secondary color +byte briS = 128; //default brightness + +byte nightlightTargetBri = 0; //brightness after nightlight is over +byte nightlightDelayMins = 60; +bool nightlightFade = true; //if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over +bool nightlightColorFade = false; //if enabled, light will gradually fade color from primary to secondary color. +bool fadeTransition = true; //enable crossfading color transition +uint16_t transitionDelay = 750; //default crossfade duration in ms + +bool skipFirstLed = false; //ignore first LED in strip (useful if you need the LED as signal repeater) +byte briMultiplier = 100; //% of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) + +//User Interface CONFIG +char serverDescription[33] = "WLED"; //Name of module +bool syncToggleReceive = false; //UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise + +//Sync CONFIG +bool buttonEnabled = true; +byte irEnabled = 0; //Infrared receiver + +uint16_t udpPort = 21324; //WLED notifier default port +uint16_t udpRgbPort = 19446; //Hyperion port + +bool receiveNotificationBrightness = true; //apply brightness from incoming notifications +bool receiveNotificationColor = true; //apply color +bool receiveNotificationEffects = true; //apply effects setup +bool notifyDirect = false; //send notification if change via UI or HTTP API +bool notifyButton = false; //send if updated by button or infrared remote +bool notifyAlexa = false; //send notification if updated via Alexa +bool notifyMacro = false; //send notification for macro +bool notifyHue = true; //send notification if Hue light changes +bool notifyTwice = false; //notifications use UDP: enable if devices don't sync reliably + +bool alexaEnabled = true; //enable device discovery by Amazon Echo +char alexaInvocationName[33] = "Light"; //speech control name of device. Choose something voice-to-text can understand + +char blynkApiKey[36] = ""; //Auth token for Blynk server. If empty, no connection will be made + +uint16_t realtimeTimeoutMs = 2500; //ms timeout of realtime mode before returning to normal mode +int arlsOffset = 0; //realtime LED offset +bool receiveDirect = true; //receive UDP realtime +bool arlsDisableGammaCorrection = true; //activate if gamma correction is handled by the source +bool arlsForceMaxBri = false; //enable to force max brightness if source has very dark colors that would be black + +#define E131_MAX_UNIVERSE_COUNT 9 +uint16_t e131Universe = 1; //settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) +uint8_t DMXMode = DMX_MODE_MULTIPLE_RGB; //DMX mode (s.a.) +uint16_t DMXAddress = 1; //DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] +uint8_t DMXOldDimmer = 0; //only update brightness on change +uint8_t e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; //to detect packet loss +bool e131Multicast = false; //multicast or unicast +bool e131SkipOutOfSequence = false; //freeze instead of flickering + +bool mqttEnabled = false; +char mqttDeviceTopic[33] = ""; //main MQTT topic (individual per device, default is wled/mac) +char mqttGroupTopic[33] = "wled/all"; //second MQTT topic (for example to group devices) +char mqttServer[33] = ""; //both domains and IPs should work (no SSL) +char mqttUser[41] = ""; //optional: username for MQTT auth +char mqttPass[41] = ""; //optional: password for MQTT auth +char mqttClientID[41] = ""; //override the client ID +uint16_t mqttPort = 1883; + +bool huePollingEnabled = false; //poll hue bridge for light state +uint16_t huePollIntervalMs = 2500; //low values (< 1sec) may cause lag but offer quicker response +char hueApiKey[47] = "api"; //key token will be obtained from bridge +byte huePollLightId = 1; //ID of hue lamp to sync to. Find the ID in the hue app ("about" section) +IPAddress hueIP = (0, 0, 0, 0); //IP address of the bridge +bool hueApplyOnOff = true; +bool hueApplyBri = true; +bool hueApplyColor = true; + +//Time CONFIG +bool ntpEnabled = false; //get internet time. Only required if you use clock overlays or time-activated macros +bool useAMPM = false; //12h/24h clock format +byte currentTimezone = 0; //Timezone ID. Refer to timezones array in wled10_ntp.ino +int utcOffsetSecs = 0; //Seconds to offset from UTC before timzone calculation + +byte overlayDefault = 0; //0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie +byte overlayMin = 0, overlayMax = ledCount - 1; //boundaries of overlay mode + +byte analogClock12pixel = 0; //The pixel in your strip where "midnight" would be +bool analogClockSecondsTrail = false; //Display seconds as trail of LEDs instead of a single pixel +bool analogClock5MinuteMarks = false; //Light pixels at every 5-minute position + +char cronixieDisplay[7] = "HHMMSS"; //Cronixie Display mask. See wled13_cronixie.ino +bool cronixieBacklight = true; //Allow digits to be back-illuminated + +bool countdownMode = false; //Clock will count down towards date +byte countdownYear = 20, countdownMonth = 1; //Countdown target date, year is last two digits +byte countdownDay = 1, countdownHour = 0; +byte countdownMin = 0, countdownSec = 0; + +byte macroBoot = 0; //macro loaded after startup +byte macroNl = 0; //after nightlight delay over +byte macroCountdown = 0; +byte macroAlexaOn = 0, macroAlexaOff = 0; +byte macroButton = 0, macroLongPress = 0, macroDoublePress = 0; + +//Security CONFIG +bool otaLock = false; //prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks +bool wifiLock = false; //prevents access to WiFi settings when OTA lock is enabled +bool aOtaEnabled = true; //ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on + +uint16_t userVar0 = 0, userVar1 = 0; + +#ifdef WLED_ENABLE_DMX +//dmx CONFIG +byte DMXChannels = 7; // number of channels per fixture +byte DMXFixtureMap[15] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +// assigns the different channels to different functions. See wled21_dmx.ino for more information. +uint16_t DMXGap = 10; // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. +uint16_t DMXStart = 10; // start address of the first fixture +#endif + +//internal global variable declarations +//wifi +bool apActive = false; +bool forceReconnect = false; +uint32_t lastReconnectAttempt = 0; +bool interfacesInited = false; +bool wasConnected = false; + +//color +byte colOld[]{0, 0, 0, 0}; //color before transition +byte colT[]{0, 0, 0, 0}; //color that is currently displayed on the LEDs +byte colIT[]{0, 0, 0, 0}; //color that was last sent to LEDs +byte colSecT[]{0, 0, 0, 0}; +byte colSecOld[]{0, 0, 0, 0}; +byte colSecIT[]{0, 0, 0, 0}; + +byte lastRandomIndex = 0; //used to save last random color so the new one is not the same + +//transitions +bool transitionActive = false; +uint16_t transitionDelayDefault = transitionDelay; +uint16_t transitionDelayTemp = transitionDelay; +unsigned long transitionStartTime; +float tperLast = 0; //crossfade transition progress, 0.0f - 1.0f +bool jsonTransitionOnce = false; + +//nightlight +bool nightlightActive = false; +bool nightlightActiveOld = false; +uint32_t nightlightDelayMs = 10; +uint8_t nightlightDelayMinsDefault = nightlightDelayMins; +unsigned long nightlightStartTime; +byte briNlT = 0; //current nightlight brightness +byte colNlT[]{0, 0, 0, 0}; //current nightlight color + +//brightness +unsigned long lastOnTime = 0; +bool offMode = !turnOnAtBoot; +byte bri = briS; +byte briOld = 0; +byte briT = 0; +byte briIT = 0; +byte briLast = 128; //brightness before turned off. Used for toggle function +byte whiteLast = 128; //white channel before turned off. Used for toggle function + +//button +bool buttonPressedBefore = false; +bool buttonLongPressed = false; +unsigned long buttonPressedTime = 0; +unsigned long buttonWaitTime = 0; + +//notifications +bool notifyDirectDefault = notifyDirect; +bool receiveNotifications = true; +unsigned long notificationSentTime = 0; +byte notificationSentCallMode = NOTIFIER_CALL_MODE_INIT; +bool notificationTwoRequired = false; + +//effects +byte effectCurrent = 0; +byte effectSpeed = 128; +byte effectIntensity = 128; +byte effectPalette = 0; + +//network +bool udpConnected = false, udpRgbConnected = false; + +//ui style +bool showWelcomePage = false; + +//hue +byte hueError = HUE_ERROR_INACTIVE; +//uint16_t hueFailCount = 0; +float hueXLast = 0, hueYLast = 0; +uint16_t hueHueLast = 0, hueCtLast = 0; +byte hueSatLast = 0, hueBriLast = 0; +unsigned long hueLastRequestSent = 0; +bool hueAuthRequired = false; +bool hueReceived = false; +bool hueStoreAllowed = false, hueNewKey = false; + +//overlays +byte overlayCurrent = overlayDefault; +byte overlaySpeed = 200; +unsigned long overlayRefreshMs = 200; +unsigned long overlayRefreshedTime; + +//cronixie +byte dP[]{0, 0, 0, 0, 0, 0}; +bool cronixieInit = false; + +//countdown +unsigned long countdownTime = 1514764800L; +bool countdownOverTriggered = true; + +//timer +byte lastTimerMinute = 0; +byte timerHours[] = {0, 0, 0, 0, 0, 0, 0, 0}; +byte timerMinutes[] = {0, 0, 0, 0, 0, 0, 0, 0}; +byte timerMacro[] = {0, 0, 0, 0, 0, 0, 0, 0}; +byte timerWeekday[] = {255, 255, 255, 255, 255, 255, 255, 255}; //weekdays to activate on +//bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity + +//blynk +bool blynkEnabled = false; + +//preset cycling +bool presetCyclingEnabled = false; +byte presetCycleMin = 1, presetCycleMax = 5; +uint16_t presetCycleTime = 1250; +unsigned long presetCycledTime = 0; +byte presetCycCurr = presetCycleMin; +bool presetApplyBri = true; +bool saveCurrPresetCycConf = false; + +//realtime +byte realtimeMode = REALTIME_MODE_INACTIVE; +IPAddress realtimeIP = (0, 0, 0, 0); +unsigned long realtimeTimeout = 0; + +//mqtt +long lastMqttReconnectAttempt = 0; +long lastInterfaceUpdate = 0; +byte interfaceUpdateCallMode = NOTIFIER_CALL_MODE_INIT; +char mqttStatusTopic[40] = ""; //this must be global because of async handlers + +#if AUXPIN >= 0 + //auxiliary debug pin +byte auxTime = 0; +unsigned long auxStartTime = 0; +bool auxActive = false, auxActiveBefore = false; +#endif + +//alexa udp +String escapedMac; +#ifndef WLED_DISABLE_ALEXA +Espalexa espalexa; +EspalexaDevice *espalexaDevice; +#endif + +//dns server +DNSServer dnsServer; + +//network time +bool ntpConnected = false; +time_t local = 0; +unsigned long ntpLastSyncTime = 999000000L; +unsigned long ntpPacketSentTime = 999000000L; +IPAddress ntpServerIP; +uint16_t ntpLocalPort = 2390; +#define NTP_PACKET_SIZE 48 + +//maximum number of LEDs - MAX_LEDS is coming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue +#define MAX_LEDS 1500 +#define MAX_LEDS_DMA 500 + +//string temp buffer (now stored in stack locally) +#define OMAX 2048 +char *obuf; +uint16_t olen = 0; + +//presets +uint16_t savedPresets = 0; +int8_t currentPreset = -1; +bool isPreset = false; + +byte errorFlag = 0; + +String messageHead, messageSub; +byte optionType; + +bool doReboot = false; //flag to initiate reboot from async handlers +bool doPublishMqtt = false; + +//server library objects +AsyncWebServer server(80); +AsyncClient *hueClient = NULL; +AsyncMqttClient *mqtt = NULL; + +//function prototypes +void colorFromUint32(uint32_t, bool = false); +void serveMessage(AsyncWebServerRequest *, uint16_t, String, String, byte); +void handleE131Packet(e131_packet_t *, IPAddress); +void arlsLock(uint32_t, byte); +void handleOverlayDraw(); + +//udp interface objects +WiFiUDP notifierUdp, rgbUdp; +WiFiUDP ntpUdp; +ESPAsyncE131 e131(handleE131Packet); +bool e131NewData = false; + +//led fx library object +WS2812FX strip = WS2812FX(); + +#define WLED_CONNECTED (WiFi.status() == WL_CONNECTED) +#define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID, DEFAULT_CLIENT_SSID) != 0) + +//debug macros +#ifdef WLED_DEBUG +#define DEBUG_PRINT(x) Serial.print(x) +#define DEBUG_PRINTLN(x) Serial.println(x) +#define DEBUG_PRINTF(x) Serial.printf(x) +unsigned long debugTime = 0; +int lastWifiState = 3; +unsigned long wifiStateChangedTime = 0; +int loops = 0; +#else +#define DEBUG_PRINT(x) +#define DEBUG_PRINTLN(x) +#define DEBUG_PRINTF(x) +#endif + + WLED::WLED() { diff --git a/wled00/wled.h b/wled00/wled.h index c04dfa87..04341446 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -123,305 +123,297 @@ extern "C" //version code in format yymmddb (b = daily build) #define VERSION 2003222 -char versionString[] = "0.9.1"; +extern char versionString[]; //AP and OTA default passwords (for maximum change them!) -char apPass[65] = DEFAULT_AP_PASS; -char otaPass[33] = DEFAULT_OTA_PASS; +extern char apPass[65]; +extern char otaPass[33]; //Hardware CONFIG (only changeble HERE, not at runtime) //LED strip pin, button pin and IR pin changeable in NpbWrapper.h! -byte auxDefaultState = 0; //0: input 1: high 2: low -byte auxTriggeredState = 0; //0: input 1: high 2: low -char ntpServerName[33] = "0.wled.pool.ntp.org"; //NTP server to use +extern byte auxDefaultState; //0: input 1: high 2: low +extern byte auxTriggeredState; //0: input 1: high 2: low +extern char ntpServerName[33]; //NTP server to use //WiFi CONFIG (all these can be changed via web UI, no need to set them here) -char clientSSID[33] = CLIENT_SSID; -char clientPass[65] = CLIENT_PASS; -char cmDNS[33] = "x"; //mDNS address (placeholder, will be replaced by wledXXXXXXXXXXXX.local) -char apSSID[33] = ""; //AP off by default (unless setup) -byte apChannel = 1; //2.4GHz WiFi AP channel (1-13) -byte apHide = 0; //hidden AP SSID -byte apBehavior = AP_BEHAVIOR_BOOT_NO_CONN; //access point opens when no connection after boot by default -IPAddress staticIP(0, 0, 0, 0); //static IP of ESP -IPAddress staticGateway(0, 0, 0, 0); //gateway (router) IP -IPAddress staticSubnet(255, 255, 255, 0); //most common subnet in home networks -bool noWifiSleep = false; //disabling modem sleep modes will increase heat output and power usage, but may help with connection issues +extern char clientSSID[33]; +extern char clientPass[65]; +extern char cmDNS[33]; //mDNS address (placeholder, will be replaced by wledXXXXXXXXXXXX.local) +extern char apSSID[33]; //AP off by default (unless setup) +extern byte apChannel; //2.4GHz WiFi AP channel (1-13) +extern byte apHide; //hidden AP SSID +extern byte apBehavior; //access point opens when no connection after boot by default +extern IPAddress staticIP; //static IP of ESP +extern IPAddress staticGateway; //gateway (router) IP +extern IPAddress staticSubnet; //most common subnet in home networks +extern bool noWifiSleep; //disabling modem sleep modes will increase heat output and power usage, but may help with connection issues //LED CONFIG -uint16_t ledCount = 30; //overcurrent prevented by ABL -bool useRGBW = false; //SK6812 strips can contain an extra White channel +extern uint16_t ledCount; //overcurrent prevented by ABL +extern bool useRGBW; //SK6812 strips can contain an extra White channel #define ABL_MILLIAMPS_DEFAULT 850; //auto lower brightness to stay close to milliampere limit -bool turnOnAtBoot = true; //turn on LEDs at power-up -byte bootPreset = 0; //save preset to load after power-up +extern bool turnOnAtBoot; //turn on LEDs at power-up +extern byte bootPreset; //save preset to load after power-up -byte col[]{255, 160, 0, 0}; //current RGB(W) primary color. col[] should be updated if you want to change the color. -byte colSec[]{0, 0, 0, 0}; //current RGB(W) secondary color -byte briS = 128; //default brightness +extern byte col[]; //current RGB(W) primary color. col[] should be updated if you want to change the color. +extern byte colSec[]; //current RGB(W) secondary color +extern byte briS; //default brightness -byte nightlightTargetBri = 0; //brightness after nightlight is over -byte nightlightDelayMins = 60; -bool nightlightFade = true; //if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over -bool nightlightColorFade = false; //if enabled, light will gradually fade color from primary to secondary color. -bool fadeTransition = true; //enable crossfading color transition -uint16_t transitionDelay = 750; //default crossfade duration in ms +extern byte nightlightTargetBri; //brightness after nightlight is over +extern byte nightlightDelayMins; +extern bool nightlightFade; //if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over +extern bool nightlightColorFade; //if enabled, light will gradually fade color from primary to secondary color. +extern bool fadeTransition; //enable crossfading color transition +extern uint16_t transitionDelay; //default crossfade duration in ms -bool skipFirstLed = false; //ignore first LED in strip (useful if you need the LED as signal repeater) -byte briMultiplier = 100; //% of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) +extern bool skipFirstLed; //ignore first LED in strip (useful if you need the LED as signal repeater) +extern byte briMultiplier; //% of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) //User Interface CONFIG -char serverDescription[33] = "WLED"; //Name of module -bool syncToggleReceive = false; //UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise +extern char serverDescription[33]; //Name of module +extern bool syncToggleReceive; //UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise //Sync CONFIG -bool buttonEnabled = true; -byte irEnabled = 0; //Infrared receiver +extern bool buttonEnabled; +extern byte irEnabled; //Infrared receiver -uint16_t udpPort = 21324; //WLED notifier default port -uint16_t udpRgbPort = 19446; //Hyperion port +extern uint16_t udpPort; //WLED notifier default port +extern uint16_t udpRgbPort; //Hyperion port -bool receiveNotificationBrightness = true; //apply brightness from incoming notifications -bool receiveNotificationColor = true; //apply color -bool receiveNotificationEffects = true; //apply effects setup -bool notifyDirect = false; //send notification if change via UI or HTTP API -bool notifyButton = false; //send if updated by button or infrared remote -bool notifyAlexa = false; //send notification if updated via Alexa -bool notifyMacro = false; //send notification for macro -bool notifyHue = true; //send notification if Hue light changes -bool notifyTwice = false; //notifications use UDP: enable if devices don't sync reliably +extern bool receiveNotificationBrightness; //apply brightness from incoming notifications +extern bool receiveNotificationColor; //apply color +extern bool receiveNotificationEffects; //apply effects setup +extern bool notifyDirect; //send notification if change via UI or HTTP API +extern bool notifyButton; //send if updated by button or infrared remote +extern bool notifyAlexa; //send notification if updated via Alexa +extern bool notifyMacro; //send notification for macro +extern bool notifyHue; //send notification if Hue light changes +extern bool notifyTwice; //notifications use UDP: enable if devices don't sync reliably -bool alexaEnabled = true; //enable device discovery by Amazon Echo -char alexaInvocationName[33] = "Light"; //speech control name of device. Choose something voice-to-text can understand +extern bool alexaEnabled; //enable device discovery by Amazon Echo +extern char alexaInvocationName[33]; //speech control name of device. Choose something voice-to-text can understand -char blynkApiKey[36] = ""; //Auth token for Blynk server. If empty, no connection will be made +extern char blynkApiKey[36]; //Auth token for Blynk server. If empty, no connection will be made -uint16_t realtimeTimeoutMs = 2500; //ms timeout of realtime mode before returning to normal mode -int arlsOffset = 0; //realtime LED offset -bool receiveDirect = true; //receive UDP realtime -bool arlsDisableGammaCorrection = true; //activate if gamma correction is handled by the source -bool arlsForceMaxBri = false; //enable to force max brightness if source has very dark colors that would be black +extern uint16_t realtimeTimeoutMs; //ms timeout of realtime mode before returning to normal mode +extern int arlsOffset; //realtime LED offset +extern bool receiveDirect; //receive UDP realtime +extern bool arlsDisableGammaCorrection; //activate if gamma correction is handled by the source +extern bool arlsForceMaxBri; //enable to force max brightness if source has very dark colors that would be black #define E131_MAX_UNIVERSE_COUNT 9 -uint16_t e131Universe = 1; //settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) -uint8_t DMXMode = DMX_MODE_MULTIPLE_RGB; //DMX mode (s.a.) -uint16_t DMXAddress = 1; //DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] -uint8_t DMXOldDimmer = 0; //only update brightness on change -uint8_t e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; //to detect packet loss -bool e131Multicast = false; //multicast or unicast -bool e131SkipOutOfSequence = false; //freeze instead of flickering +extern uint16_t e131Universe; //settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) +extern uint8_t DMXMode; //DMX mode (s.a.) +extern uint16_t DMXAddress; //DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] +extern uint8_t DMXOldDimmer; //only update brightness on change +extern uint8_t e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; //to detect packet loss +extern bool e131Multicast; //multicast or unicast +extern bool e131SkipOutOfSequence; //freeze instead of flickering -bool mqttEnabled = false; -char mqttDeviceTopic[33] = ""; //main MQTT topic (individual per device, default is wled/mac) -char mqttGroupTopic[33] = "wled/all"; //second MQTT topic (for example to group devices) -char mqttServer[33] = ""; //both domains and IPs should work (no SSL) -char mqttUser[41] = ""; //optional: username for MQTT auth -char mqttPass[41] = ""; //optional: password for MQTT auth -char mqttClientID[41] = ""; //override the client ID -uint16_t mqttPort = 1883; +extern bool mqttEnabled; +extern char mqttDeviceTopic[33]; //main MQTT topic (individual per device, default is wled/mac) +extern char mqttGroupTopic[33]; //second MQTT topic (for example to group devices) +extern char mqttServer[33]; //both domains and IPs should work (no SSL) +extern char mqttUser[41]; //optional: username for MQTT auth +extern char mqttPass[41]; //optional: password for MQTT auth +extern char mqttClientID[41]; //override the client ID +extern uint16_t mqttPort; -bool huePollingEnabled = false; //poll hue bridge for light state -uint16_t huePollIntervalMs = 2500; //low values (< 1sec) may cause lag but offer quicker response -char hueApiKey[47] = "api"; //key token will be obtained from bridge -byte huePollLightId = 1; //ID of hue lamp to sync to. Find the ID in the hue app ("about" section) -IPAddress hueIP = (0, 0, 0, 0); //IP address of the bridge -bool hueApplyOnOff = true; -bool hueApplyBri = true; -bool hueApplyColor = true; +extern bool huePollingEnabled; //poll hue bridge for light state +extern uint16_t huePollIntervalMs; //low values (< 1sec) may cause lag but offer quicker response +extern char hueApiKey[47]; //key token will be obtained from bridge +extern byte huePollLightId; //ID of hue lamp to sync to. Find the ID in the hue app ("about" section) +extern IPAddress hueIP; //IP address of the bridge +extern bool hueApplyOnOff; +extern bool hueApplyBri; +extern bool hueApplyColor; //Time CONFIG -bool ntpEnabled = false; //get internet time. Only required if you use clock overlays or time-activated macros -bool useAMPM = false; //12h/24h clock format -byte currentTimezone = 0; //Timezone ID. Refer to timezones array in wled10_ntp.ino -int utcOffsetSecs = 0; //Seconds to offset from UTC before timzone calculation +extern bool ntpEnabled; //get internet time. Only required if you use clock overlays or time-activated macros +extern bool useAMPM; //12h/24h clock format +extern byte currentTimezone; //Timezone ID. Refer to timezones array in wled_ntp.cpp +extern int utcOffsetSecs; //Seconds to offset from UTC before timzone calculation -byte overlayDefault = 0; //0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie -byte overlayMin = 0, overlayMax = ledCount - 1; //boundaries of overlay mode +extern byte overlayDefault; //0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie +extern byte overlayMin; //boundaries of overlay mode +extern byte overlayMax; -byte analogClock12pixel = 0; //The pixel in your strip where "midnight" would be -bool analogClockSecondsTrail = false; //Display seconds as trail of LEDs instead of a single pixel -bool analogClock5MinuteMarks = false; //Light pixels at every 5-minute position +extern byte analogClock12pixel; //The pixel in your strip where "midnight" would be +extern bool analogClockSecondsTrail; //Display seconds as trail of LEDs instead of a single pixel +extern bool analogClock5MinuteMarks; //Light pixels at every 5-minute position -char cronixieDisplay[7] = "HHMMSS"; //Cronixie Display mask. See wled13_cronixie.ino -bool cronixieBacklight = true; //Allow digits to be back-illuminated +extern char cronixieDisplay[7]; //Cronixie Display mask. See wled13_cronixie.ino +extern bool cronixieBacklight; //Allow digits to be back-illuminated -bool countdownMode = false; //Clock will count down towards date -byte countdownYear = 20, countdownMonth = 1; //Countdown target date, year is last two digits -byte countdownDay = 1, countdownHour = 0; -byte countdownMin = 0, countdownSec = 0; +extern bool countdownMode; //Clock will count down towards date +extern byte countdownYear, countdownMonth; //Countdown target date, year is last two digits +extern byte countdownDay, countdownHour; +extern byte countdownMin, countdownSec; -byte macroBoot = 0; //macro loaded after startup -byte macroNl = 0; //after nightlight delay over -byte macroCountdown = 0; -byte macroAlexaOn = 0, macroAlexaOff = 0; -byte macroButton = 0, macroLongPress = 0, macroDoublePress = 0; +extern byte macroBoot; //macro loaded after startup +extern byte macroNl; //after nightlight delay over +extern byte macroCountdown; +extern byte macroAlexaOn, macroAlexaOff; +extern byte macroButton, macroLongPress, macroDoublePress; //Security CONFIG -bool otaLock = false; //prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks -bool wifiLock = false; //prevents access to WiFi settings when OTA lock is enabled -bool aOtaEnabled = true; //ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on +extern bool otaLock; //prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks +extern bool wifiLock; //prevents access to WiFi settings when OTA lock is enabled +extern bool aOtaEnabled; //ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on -uint16_t userVar0 = 0, userVar1 = 0; +extern uint16_t userVar0, userVar1; #ifdef WLED_ENABLE_DMX //dmx CONFIG -byte DMXChannels = 7; // number of channels per fixture -byte DMXFixtureMap[15] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -// assigns the different channels to different functions. See wled21_dmx.ino for more information. -uint16_t DMXGap = 10; // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. -uint16_t DMXStart = 10; // start address of the first fixture +extern byte DMXChannels; // number of channels per fixture +extern byte DMXFixtureMap[15]; +extern // assigns the different channels to different functions. See wled21_dmx.ino for more information. +extern uint16_t DMXGap; // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. +extern uint16_t DMXStart; // start address of the first fixture #endif //internal global variable declarations //wifi -bool apActive = false; -bool forceReconnect = false; -uint32_t lastReconnectAttempt = 0; -bool interfacesInited = false; -bool wasConnected = false; +extern bool apActive; +extern bool forceReconnect; +extern uint32_t lastReconnectAttempt; +extern bool interfacesInited; +extern bool wasConnected; //color -byte colOld[]{0, 0, 0, 0}; //color before transition -byte colT[]{0, 0, 0, 0}; //color that is currently displayed on the LEDs -byte colIT[]{0, 0, 0, 0}; //color that was last sent to LEDs -byte colSecT[]{0, 0, 0, 0}; -byte colSecOld[]{0, 0, 0, 0}; -byte colSecIT[]{0, 0, 0, 0}; +extern byte colOld[]; //color before transition +extern byte colT[]; //color that is currently displayed on the LEDs +extern byte colIT[]; //color that was last sent to LEDs +extern byte colSecT[]; +extern byte colSecOld[]; +extern byte colSecIT[]; -byte lastRandomIndex = 0; //used to save last random color so the new one is not the same +extern byte lastRandomIndex; //used to save last random color so the new one is not the same //transitions -bool transitionActive = false; -uint16_t transitionDelayDefault = transitionDelay; -uint16_t transitionDelayTemp = transitionDelay; -unsigned long transitionStartTime; -float tperLast = 0; //crossfade transition progress, 0.0f - 1.0f -bool jsonTransitionOnce = false; +extern bool transitionActive; +extern uint16_t transitionDelayDefault; +extern uint16_t transitionDelayTemp; +extern unsigned long transitionStartTime; +extern float tperLast; //crossfade transition progress, 0.0f - 1.0f +extern bool jsonTransitionOnce; //nightlight -bool nightlightActive = false; -bool nightlightActiveOld = false; -uint32_t nightlightDelayMs = 10; -uint8_t nightlightDelayMinsDefault = nightlightDelayMins; -unsigned long nightlightStartTime; -byte briNlT = 0; //current nightlight brightness -byte colNlT[]{0, 0, 0, 0}; //current nightlight color +extern bool nightlightActive; +extern bool nightlightActiveOld; +extern uint32_t nightlightDelayMs; +extern uint8_t nightlightDelayMinsDefault; +extern unsigned long nightlightStartTime; +extern byte briNlT; //current nightlight brightness +extern byte colNlT[]; //current nightlight color -//brightness -unsigned long lastOnTime = 0; -bool offMode = !turnOnAtBoot; -byte bri = briS; -byte briOld = 0; -byte briT = 0; -byte briIT = 0; -byte briLast = 128; //brightness before turned off. Used for toggle function -byte whiteLast = 128; //white channel before turned off. Used for toggle function +extern unsigned long lastOnTime; +extern bool offMode; +extern byte bri; +extern byte briOld; +extern byte briT; +extern byte briIT; +extern byte briLast; //brightness before turned off. Used for toggle function +extern byte whiteLast; //white channel before turned off. Used for toggle function -//button -bool buttonPressedBefore = false; -bool buttonLongPressed = false; -unsigned long buttonPressedTime = 0; -unsigned long buttonWaitTime = 0; +extern bool buttonPressedBefore; +extern bool buttonLongPressed; +extern unsigned long buttonPressedTime; +extern unsigned long buttonWaitTime; -//notifications -bool notifyDirectDefault = notifyDirect; -bool receiveNotifications = true; -unsigned long notificationSentTime = 0; -byte notificationSentCallMode = NOTIFIER_CALL_MODE_INIT; -bool notificationTwoRequired = false; +extern bool notifyDirectDefault; +extern bool receiveNotifications; +extern unsigned long notificationSentTime; +extern byte notificationSentCallMode; +extern bool notificationTwoRequired; -//effects -byte effectCurrent = 0; -byte effectSpeed = 128; -byte effectIntensity = 128; -byte effectPalette = 0; +extern byte effectCurrent; +extern byte effectSpeed; +extern byte effectIntensity; +extern byte effectPalette; -//network -bool udpConnected = false, udpRgbConnected = false; +extern bool udpConnected, udpRgbConnected; -//ui style -bool showWelcomePage = false; +extern bool showWelcomePage; -//hue -byte hueError = HUE_ERROR_INACTIVE; -//uint16_t hueFailCount = 0; -float hueXLast = 0, hueYLast = 0; -uint16_t hueHueLast = 0, hueCtLast = 0; -byte hueSatLast = 0, hueBriLast = 0; -unsigned long hueLastRequestSent = 0; -bool hueAuthRequired = false; -bool hueReceived = false; -bool hueStoreAllowed = false, hueNewKey = false; +extern byte hueError; +//uint16_t hueFailCount; +extern float hueXLast, hueYLast; +extern uint16_t hueHueLast, hueCtLast; +extern byte hueSatLast, hueBriLast; +extern unsigned long hueLastRequestSent; +extern bool hueAuthRequired; +extern bool hueReceived; +extern bool hueStoreAllowed, hueNewKey; -//overlays -byte overlayCurrent = overlayDefault; -byte overlaySpeed = 200; -unsigned long overlayRefreshMs = 200; -unsigned long overlayRefreshedTime; +extern byte overlayCurrent; +extern byte overlaySpeed; +extern unsigned long overlayRefreshMs; +extern unsigned long overlayRefreshedTime; -//cronixie -byte dP[]{0, 0, 0, 0, 0, 0}; -bool cronixieInit = false; +extern byte dP[]; +extern bool cronixieInit; //countdown -unsigned long countdownTime = 1514764800L; -bool countdownOverTriggered = true; +extern unsigned long countdownTime; +extern bool countdownOverTriggered; //timer -byte lastTimerMinute = 0; -byte timerHours[] = {0, 0, 0, 0, 0, 0, 0, 0}; -byte timerMinutes[] = {0, 0, 0, 0, 0, 0, 0, 0}; -byte timerMacro[] = {0, 0, 0, 0, 0, 0, 0, 0}; -byte timerWeekday[] = {255, 255, 255, 255, 255, 255, 255, 255}; //weekdays to activate on +extern byte lastTimerMinute; +extern byte timerHours[]; +extern byte timerMinutes[]; +extern byte timerMacro[]; +extern byte timerWeekday[]; //weekdays to activate on //bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity //blynk -bool blynkEnabled = false; +extern bool blynkEnabled; //preset cycling -bool presetCyclingEnabled = false; -byte presetCycleMin = 1, presetCycleMax = 5; -uint16_t presetCycleTime = 1250; -unsigned long presetCycledTime = 0; -byte presetCycCurr = presetCycleMin; -bool presetApplyBri = true; -bool saveCurrPresetCycConf = false; +extern bool presetCyclingEnabled; +extern byte presetCycleMin, presetCycleMax; +extern uint16_t presetCycleTime; +extern unsigned long presetCycledTime; +extern byte presetCycCurr; +extern bool presetApplyBri; +extern bool saveCurrPresetCycConf; //realtime -byte realtimeMode = REALTIME_MODE_INACTIVE; -IPAddress realtimeIP = (0, 0, 0, 0); -unsigned long realtimeTimeout = 0; +extern byte realtimeMode; +extern IPAddress realtimeIP; +extern unsigned long realtimeTimeout; //mqtt -long lastMqttReconnectAttempt = 0; -long lastInterfaceUpdate = 0; -byte interfaceUpdateCallMode = NOTIFIER_CALL_MODE_INIT; -char mqttStatusTopic[40] = ""; //this must be global because of async handlers +extern long lastMqttReconnectAttempt; +extern long lastInterfaceUpdate; +extern byte interfaceUpdateCallMode; +extern char mqttStatusTopic[40]; //this must be global because of async handlers #if AUXPIN >= 0 - //auxiliary debug pin -byte auxTime = 0; -unsigned long auxStartTime = 0; -bool auxActive = false, auxActiveBefore = false; +//auxiliary debug pin +extern byte auxTime; +extern unsigned long auxStartTime; +extern bool auxActive; #endif //alexa udp -String escapedMac; +extern String escapedMac; #ifndef WLED_DISABLE_ALEXA -Espalexa espalexa; -EspalexaDevice *espalexaDevice; +extern Espalexa espalexa; +extern EspalexaDevice *espalexaDevice; #endif //dns server -DNSServer dnsServer; +extern DNSServer dnsServer; //network time -bool ntpConnected = false; -time_t local = 0; -unsigned long ntpLastSyncTime = 999000000L; -unsigned long ntpPacketSentTime = 999000000L; -IPAddress ntpServerIP; -uint16_t ntpLocalPort = 2390; +extern bool ntpConnected; +extern time_t local; +extern unsigned long ntpLastSyncTime; +extern unsigned long ntpPacketSentTime; +extern IPAddress ntpServerIP; +extern uint16_t ntpLocalPort; #define NTP_PACKET_SIZE 48 //maximum number of LEDs - MAX_LEDS is coming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue @@ -430,42 +422,43 @@ uint16_t ntpLocalPort = 2390; //string temp buffer (now stored in stack locally) #define OMAX 2048 -char *obuf; -uint16_t olen = 0; +extern char *obuf; +extern uint16_t olen; //presets -uint16_t savedPresets = 0; -int8_t currentPreset = -1; -bool isPreset = false; +extern uint16_t savedPresets; +extern int8_t currentPreset; +extern bool isPreset; -byte errorFlag = 0; +extern byte errorFlag; -String messageHead, messageSub; -byte optionType; +extern String messageHead, messageSub; +extern byte optionType; -bool doReboot = false; //flag to initiate reboot from async handlers -bool doPublishMqtt = false; +extern bool doReboot; //flag to initiate reboot from async handlers +extern bool doPublishMqtt; //server library objects -AsyncWebServer server(80); -AsyncClient *hueClient = NULL; -AsyncMqttClient *mqtt = NULL; +extern AsyncWebServer server; +extern AsyncClient *hueClient; +extern AsyncMqttClient *mqtt; //function prototypes -void colorFromUint32(uint32_t, bool = false); -void serveMessage(AsyncWebServerRequest *, uint16_t, String, String, byte); -void handleE131Packet(e131_packet_t *, IPAddress); -void arlsLock(uint32_t, byte); -void handleOverlayDraw(); +extern void colorFromUint32(uint32_t, bool); +extern void serveMessage(AsyncWebServerRequest *, uint16_t, String, String, byte); +extern void handleE131Packet(e131_packet_t *, IPAddress); +extern void arlsLock(uint32_t, byte); +extern void handleOverlayDraw(); //udp interface objects -WiFiUDP notifierUdp, rgbUdp; -WiFiUDP ntpUdp; -ESPAsyncE131 e131(handleE131Packet); -bool e131NewData = false; +extern WiFiUDP notifierUdp, rgbUdp; +extern WiFiUDP ntpUdp; +extern ESPAsyncE131 e131; +extern bool e131NewData; //led fx library object -WS2812FX strip = WS2812FX(); +extern WS2812FX strip; + #define WLED_CONNECTED (WiFi.status() == WL_CONNECTED) #define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID, DEFAULT_CLIENT_SSID) != 0) diff --git a/wled00/wled_colors.h b/wled00/wled_colors.h index 2b3ff32c..917311e7 100644 --- a/wled00/wled_colors.h +++ b/wled00/wled_colors.h @@ -5,7 +5,7 @@ * Color conversion methods */ -void colorFromUint32(uint32_t in, bool secondary); +void colorFromUint32(uint32_t in, bool secondary = false); void colorFromUint24(uint32_t in, bool secondary = false); void relativeChangeWhite(int8_t amount, byte lowerBoundary = 0); void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb diff --git a/wled00/wled_ntp.cpp b/wled00/wled_ntp.cpp index 82b99f96..d8d9c38a 100644 --- a/wled00/wled_ntp.cpp +++ b/wled00/wled_ntp.cpp @@ -1,7 +1,63 @@ #include "wled_ntp.h" #include "wled.h" #include "wled_eeprom.h" +TimeChangeRule UTCr = {Last, Sun, Mar, 1, 0}; // UTC +Timezone tzUTC(UTCr, UTCr); +TimeChangeRule BST = {Last, Sun, Mar, 1, 60}; // British Summer Time +TimeChangeRule GMT = {Last, Sun, Oct, 2, 0}; // Standard Time +Timezone tzUK(BST, GMT); + +TimeChangeRule CEST = {Last, Sun, Mar, 2, 120}; //Central European Summer Time +TimeChangeRule CET = {Last, Sun, Oct, 3, 60}; //Central European Standard Time +Timezone tzEUCentral(CEST, CET); + +TimeChangeRule EEST = {Last, Sun, Mar, 3, 180}; //Central European Summer Time +TimeChangeRule EET = {Last, Sun, Oct, 4, 120}; //Central European Standard Time +Timezone tzEUEastern(EEST, EET); + +TimeChangeRule EDT = {Second, Sun, Mar, 2, -240 }; //Daylight time = UTC - 4 hours +TimeChangeRule EST = {First, Sun, Nov, 2, -300 }; //Standard time = UTC - 5 hours +Timezone tzUSEastern(EDT, EST); + +TimeChangeRule CDT = {Second, Sun, Mar, 2, -300 }; //Daylight time = UTC - 5 hours +TimeChangeRule CST = {First, Sun, Nov, 2, -360 }; //Standard time = UTC - 6 hours +Timezone tzUSCentral(CDT, CST); + +Timezone tzCASaskatchewan(CST, CST); //Central without DST + +TimeChangeRule MDT = {Second, Sun, Mar, 2, -360 }; //Daylight time = UTC - 6 hours +TimeChangeRule MST = {First, Sun, Nov, 2, -420 }; //Standard time = UTC - 7 hours +Timezone tzUSMountain(MDT, MST); + +Timezone tzUSArizona(MST, MST); //Mountain without DST + +TimeChangeRule PDT = {Second, Sun, Mar, 2, -420 }; //Daylight time = UTC - 7 hours +TimeChangeRule PST = {First, Sun, Nov, 2, -480 }; //Standard time = UTC - 8 hours +Timezone tzUSPacific(PDT, PST); + +TimeChangeRule ChST = {Last, Sun, Mar, 1, 480}; // China Standard Time = UTC + 8 hours +Timezone tzChina(ChST, ChST); + +TimeChangeRule JST = {Last, Sun, Mar, 1, 540}; // Japan Standard Time = UTC + 9 hours +Timezone tzJapan(JST, JST); + +TimeChangeRule AEDT = {Second, Sun, Oct, 2, 660 }; //Daylight time = UTC + 11 hours +TimeChangeRule AEST = {First, Sun, Apr, 3, 600 }; //Standard time = UTC + 10 hours +Timezone tzAUEastern(AEDT, AEST); + +TimeChangeRule NZDT = {Second, Sun, Sep, 2, 780 }; //Daylight time = UTC + 13 hours +TimeChangeRule NZST = {First, Sun, Apr, 3, 720 }; //Standard time = UTC + 12 hours +Timezone tzNZ(NZDT, NZST); + +TimeChangeRule NKST = {Last, Sun, Mar, 1, 510}; //Pyongyang Time = UTC + 8.5 hours +Timezone tzNK(NKST, NKST); + +TimeChangeRule IST = {Last, Sun, Mar, 1, 330}; // India Standard Time = UTC + 5.5 hours +Timezone tzIndia(IST, IST); + +// Pick your timezone from here. +Timezone* timezones[] = {&tzUTC, &tzUK, &tzEUCentral, &tzEUEastern, &tzUSEastern, &tzUSCentral, &tzUSMountain, &tzUSArizona, &tzUSPacific, &tzChina, &tzJapan, &tzAUEastern, &tzNZ, &tzNK, &tzIndia, &tzCASaskatchewan}; void handleNetworkTime() { diff --git a/wled00/wled_ntp.h b/wled00/wled_ntp.h index 14293342..be683d14 100644 --- a/wled00/wled_ntp.h +++ b/wled00/wled_ntp.h @@ -2,66 +2,9 @@ #define WLED_NTP_H #include #include "timezone/Timezone.h" - /* * Acquires time from NTP server */ -TimeChangeRule UTCr = {Last, Sun, Mar, 1, 0}; // UTC -Timezone tzUTC(UTCr, UTCr); - -TimeChangeRule BST = {Last, Sun, Mar, 1, 60}; // British Summer Time -TimeChangeRule GMT = {Last, Sun, Oct, 2, 0}; // Standard Time -Timezone tzUK(BST, GMT); - -TimeChangeRule CEST = {Last, Sun, Mar, 2, 120}; //Central European Summer Time -TimeChangeRule CET = {Last, Sun, Oct, 3, 60}; //Central European Standard Time -Timezone tzEUCentral(CEST, CET); - -TimeChangeRule EEST = {Last, Sun, Mar, 3, 180}; //Central European Summer Time -TimeChangeRule EET = {Last, Sun, Oct, 4, 120}; //Central European Standard Time -Timezone tzEUEastern(EEST, EET); - -TimeChangeRule EDT = {Second, Sun, Mar, 2, -240 }; //Daylight time = UTC - 4 hours -TimeChangeRule EST = {First, Sun, Nov, 2, -300 }; //Standard time = UTC - 5 hours -Timezone tzUSEastern(EDT, EST); - -TimeChangeRule CDT = {Second, Sun, Mar, 2, -300 }; //Daylight time = UTC - 5 hours -TimeChangeRule CST = {First, Sun, Nov, 2, -360 }; //Standard time = UTC - 6 hours -Timezone tzUSCentral(CDT, CST); - -Timezone tzCASaskatchewan(CST, CST); //Central without DST - -TimeChangeRule MDT = {Second, Sun, Mar, 2, -360 }; //Daylight time = UTC - 6 hours -TimeChangeRule MST = {First, Sun, Nov, 2, -420 }; //Standard time = UTC - 7 hours -Timezone tzUSMountain(MDT, MST); - -Timezone tzUSArizona(MST, MST); //Mountain without DST - -TimeChangeRule PDT = {Second, Sun, Mar, 2, -420 }; //Daylight time = UTC - 7 hours -TimeChangeRule PST = {First, Sun, Nov, 2, -480 }; //Standard time = UTC - 8 hours -Timezone tzUSPacific(PDT, PST); - -TimeChangeRule ChST = {Last, Sun, Mar, 1, 480}; // China Standard Time = UTC + 8 hours -Timezone tzChina(ChST, ChST); - -TimeChangeRule JST = {Last, Sun, Mar, 1, 540}; // Japan Standard Time = UTC + 9 hours -Timezone tzJapan(JST, JST); - -TimeChangeRule AEDT = {Second, Sun, Oct, 2, 660 }; //Daylight time = UTC + 11 hours -TimeChangeRule AEST = {First, Sun, Apr, 3, 600 }; //Standard time = UTC + 10 hours -Timezone tzAUEastern(AEDT, AEST); - -TimeChangeRule NZDT = {Second, Sun, Sep, 2, 780 }; //Daylight time = UTC + 13 hours -TimeChangeRule NZST = {First, Sun, Apr, 3, 720 }; //Standard time = UTC + 12 hours -Timezone tzNZ(NZDT, NZST); - -TimeChangeRule NKST = {Last, Sun, Mar, 1, 510}; //Pyongyang Time = UTC + 8.5 hours -Timezone tzNK(NKST, NKST); - -TimeChangeRule IST = {Last, Sun, Mar, 1, 330}; // India Standard Time = UTC + 5.5 hours -Timezone tzIndia(IST, IST); - -Timezone* timezones[] = {&tzUTC, &tzUK, &tzEUCentral, &tzEUEastern, &tzUSEastern, &tzUSCentral, &tzUSMountain, &tzUSArizona, &tzUSPacific, &tzChina, &tzJapan, &tzAUEastern, &tzNZ, &tzNK, &tzIndia, &tzCASaskatchewan}; void handleNetworkTime(); void sendNTPPacket(); From c6ea2dff8a87dd4f3fedfa754218f23a1bd13016 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Thu, 26 Mar 2020 04:49:35 -0400 Subject: [PATCH 178/232] Add further includes. --- wled00/wled.cpp | 1 + wled00/wled_blynk.cpp | 3 +++ wled00/wled_ir.cpp | 5 ++++- wled00/wled_ir.h | 15 +++++++++++++++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/wled00/wled.cpp b/wled00/wled.cpp index a720cb0b..3b3d6936 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -1,4 +1,5 @@ #include "wled.h" +#include #include "wled_led.h" #include "wled_ir.h" #include "wled_notify.h" diff --git a/wled00/wled_blynk.cpp b/wled00/wled_blynk.cpp index c6754a23..2e3d9f7b 100644 --- a/wled00/wled_blynk.cpp +++ b/wled00/wled_blynk.cpp @@ -1,6 +1,9 @@ #include "wled_blynk.h" +#include "const.h" #include "wled.h" #include "src/dependencies/blynk/Blynk/BlynkHandlers.h" +#include "wled_led.h" +#include "wled_colors.h" uint16_t blHue = 0; byte blSat = 255; diff --git a/wled00/wled_ir.cpp b/wled00/wled_ir.cpp index 53f0d23f..f625148e 100644 --- a/wled00/wled_ir.cpp +++ b/wled00/wled_ir.cpp @@ -1,5 +1,8 @@ #include "wled_ir.h" #include "wled.h" +#include "wled_led.h" +#include "wled_colors.h" +#include "wled_eeprom.h" #if defined(WLED_DISABLE_INFRARED) void handleIR(){} @@ -34,7 +37,7 @@ bool decodeIRCustom(uint32_t code) //relatively change brightness, minumum A=5 -void relativeChange(byte* property, int8_t amount, byte lowerBoundary = 0, byte higherBoundary = 0xFF) +void relativeChange(byte* property, int8_t amount, byte lowerBoundary, byte higherBoundary) { int16_t new_val = (int16_t) *property + amount; if (new_val > higherBoundary) new_val = higherBoundary; diff --git a/wled00/wled_ir.h b/wled00/wled_ir.h index 3479f410..3f819da0 100644 --- a/wled00/wled_ir.h +++ b/wled00/wled_ir.h @@ -1,9 +1,24 @@ #ifndef WLED_IR_H #define WLED_IR_H +#include /* * Infrared sensor support for generic 24/40/44 key RGB remotes */ +bool decodeIRCustom(uint32_t code); +void relativeChange(byte* property, int8_t amount, byte lowerBoundary = 0, byte higherBoundary = 0xFF); +void changeEffectSpeed(int8_t amount); +void changeEffectIntensity(int8_t amount); +void decodeIR(uint32_t code); +void decodeIR24(uint32_t code); +void decodeIR24OLD(uint32_t code); +void decodeIR24CT(uint32_t code); +void decodeIR40(uint32_t code); +void decodeIR44(uint32_t code); +void decodeIR21(uint32_t code); +void decodeIR6(uint32_t code); + +void initIR(); void handleIR(); #endif //WLED_IR_H \ No newline at end of file From 7bf1c35dcfac940c9b048c67537a2d7fb5b35f83 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Thu, 26 Mar 2020 04:56:19 -0400 Subject: [PATCH 179/232] Change interface of BlynkSimpleEsp. --- wled00/src/dependencies/blynk/BlynkSimpleEsp.cpp | 5 +++++ wled00/src/dependencies/blynk/BlynkSimpleEsp.h | 4 +--- 2 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 wled00/src/dependencies/blynk/BlynkSimpleEsp.cpp diff --git a/wled00/src/dependencies/blynk/BlynkSimpleEsp.cpp b/wled00/src/dependencies/blynk/BlynkSimpleEsp.cpp new file mode 100644 index 00000000..a15d2c1b --- /dev/null +++ b/wled00/src/dependencies/blynk/BlynkSimpleEsp.cpp @@ -0,0 +1,5 @@ +#include "BlynkSimpleEsp.h" + +WiFiClient _blynkWifiClient; +BlynkArduinoClient _blynkTransport(_blynkWifiClient); +BlynkWifi Blynk(_blynkTransport); \ No newline at end of file diff --git a/wled00/src/dependencies/blynk/BlynkSimpleEsp.h b/wled00/src/dependencies/blynk/BlynkSimpleEsp.h index f6b2a0f1..6697686f 100644 --- a/wled00/src/dependencies/blynk/BlynkSimpleEsp.h +++ b/wled00/src/dependencies/blynk/BlynkSimpleEsp.h @@ -89,8 +89,6 @@ public: }; -static WiFiClient _blynkWifiClient; -static BlynkArduinoClient _blynkTransport(_blynkWifiClient); -BlynkWifi Blynk(_blynkTransport); +extern BlynkWifi Blynk; #endif From 30e3bbd0e8e226bc707c9e0e4b0777dcd87b0727 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Thu, 26 Mar 2020 05:18:19 -0400 Subject: [PATCH 180/232] Renamed min/max macros to fix potential std::min/max conflict (depending on include order). --- wled00/FX.cpp | 66 +++++++++++++++++++++++------------------------ wled00/FX.h | 4 +-- wled00/FX_fcn.cpp | 10 +++---- wled00/wled.h | 8 +++--- 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 022b4b23..a57e9dda 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -268,7 +268,7 @@ uint16_t WS2812FX::mode_breath(void) { counter = (counter >> 2) + (counter >> 4); //0-16384 + 0-2048 if (counter < 16384) { if (counter > 8192) counter = 8192 - (counter - 8192); - var = sin16(counter) / 103; //close to parabolic in range 0-8192, max val. 23170 + var = sin16(counter) / 103; //close to parabolic in range 0-8192, MAX val. 23170 } uint8_t lum = 30 + var; @@ -475,8 +475,8 @@ uint16_t WS2812FX::mode_twinkle(void) { uint32_t it = now / cycleTime; if (it != SEGENV.step) { - uint16_t maxOn = map(SEGMENT.intensity, 0, 255, 1, SEGLEN); // make sure at least one LED is on - if (SEGENV.aux0 >= maxOn) + uint16_t MAXOn = map(SEGMENT.intensity, 0, 255, 1, SEGLEN); // make sure at least one LED is on + if (SEGENV.aux0 >= MAXOn) { SEGENV.aux0 = 0; SEGENV.aux1 = random16(); //new seed for our PRNG @@ -601,7 +601,7 @@ uint16_t WS2812FX::mode_hyper_sparkle(void) { } if(random8(5) < 2) { - for(uint16_t i = 0; i < max(1, SEGLEN/3); i++) { + for(uint16_t i = 0; i < MAX(1, SEGLEN/3); i++) { setPixelColor(random16(SEGLEN), SEGCOLOR(1)); } return 20; @@ -1115,7 +1115,7 @@ uint16_t WS2812FX::mode_fireworks() { if (valid1) setPixelColor(SEGENV.aux0 , sv1); if (valid2) setPixelColor(SEGENV.aux1, sv2); - for(uint16_t i=0; i> 1)) == 0) { uint16_t index = random(SEGLEN); setPixelColor(index, color_from_palette(random8(), false, false, 0)); @@ -1162,12 +1162,12 @@ uint16_t WS2812FX::mode_fire_flicker(void) { byte r = (SEGCOLOR(0) >> 16) & 0xFF; byte g = (SEGCOLOR(0) >> 8) & 0xFF; byte b = (SEGCOLOR(0) & 0xFF); - byte lum = (SEGMENT.palette == 0) ? max(w, max(r, max(g, b))) : 255; + byte lum = (SEGMENT.palette == 0) ? MAX(w, MAX(r, MAX(g, b))) : 255; lum /= (((256-SEGMENT.intensity)/16)+1); for(uint16_t i = 0; i < SEGLEN; i++) { byte flicker = random8(lum); if (SEGMENT.palette == 0) { - setPixelColor(i, max(r - flicker, 0), max(g - flicker, 0), max(b - flicker, 0), max(w - flicker, 0)); + setPixelColor(i, MAX(r - flicker, 0), MAX(g - flicker, 0), MAX(b - flicker, 0), MAX(w - flicker, 0)); } else { setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, 255 - flicker)); } @@ -1197,7 +1197,7 @@ uint16_t WS2812FX::gradient_base(bool loading) { { val = abs(((i>pp) ? p2:pp) -i); } else { - val = min(abs(pp-i),min(abs(p1-i),abs(p2-i))); + val = MIN(abs(pp-i),MIN(abs(p1-i),abs(p2-i))); } val = (brd > val) ? val/brd * 255 : 255; setPixelColor(i, color_blend(SEGCOLOR(0), color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), val)); @@ -1597,8 +1597,8 @@ uint16_t WS2812FX::mode_oscillate(void) uint16_t WS2812FX::mode_lightning(void) { - uint16_t ledstart = random16(SEGLEN); // Determine starting location of flash - uint16_t ledlen = random16(SEGLEN -1 -ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1) + uint16_t ledstart = random16(SEGLEN); // DeterMINe starting location of flash + uint16_t ledlen = random16(SEGLEN -1 -ledstart); // DeterMINe length of flash (not to go beyond NUM_LEDS-1) uint8_t bri = 255/random8(1, 3); if (SEGENV.step == 0) @@ -1778,7 +1778,7 @@ uint16_t WS2812FX::mode_fire_2012() // Step 4. Map from heat cells to LED colors for (uint16_t j = 0; j < SEGLEN; j++) { - CRGB color = ColorFromPalette(currentPalette, min(heat[j],240), 255, LINEARBLEND); + CRGB color = ColorFromPalette(currentPalette, MIN(heat[j],240), 255, LINEARBLEND); setPixelColor(j, color.red, color.green, color.blue); } return FRAMETIME; @@ -2153,9 +2153,9 @@ typedef struct Ripple { uint16_t WS2812FX::ripple_base(bool rainbow) { - uint16_t maxRipples = 1 + (SEGLEN >> 2); - if (maxRipples > 100) maxRipples = 100; - uint16_t dataSize = sizeof(ripple) * maxRipples; + uint16_t MAXRipples = 1 + (SEGLEN >> 2); + if (MAXRipples > 100) MAXRipples = 100; + uint16_t dataSize = sizeof(ripple) * MAXRipples; if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed @@ -2181,7 +2181,7 @@ uint16_t WS2812FX::ripple_base(bool rainbow) } //draw wave - for (uint16_t i = 0; i < maxRipples; i++) + for (uint16_t i = 0; i < MAXRipples; i++) { uint16_t ripplestate = ripples[i].state; if (ripplestate) @@ -2480,8 +2480,8 @@ uint16_t WS2812FX::spots_base(uint16_t threshold) { fill(SEGCOLOR(1)); - uint16_t maxZones = SEGLEN >> 2; - uint16_t zones = 1 + ((SEGMENT.intensity * maxZones) >> 8); + uint16_t MAXZones = SEGLEN >> 2; + uint16_t zones = 1 + ((SEGMENT.intensity * MAXZones) >> 8); uint16_t zoneLen = SEGLEN / zones; uint16_t offset = (SEGLEN - zones * zoneLen) >> 1; @@ -2533,15 +2533,15 @@ typedef struct Ball { */ uint16_t WS2812FX::mode_bouncing_balls(void) { //allocate segment data - uint16_t maxNumBalls = 16; - uint16_t dataSize = sizeof(ball) * maxNumBalls; + uint16_t MAXNumBalls = 16; + uint16_t dataSize = sizeof(ball) * MAXNumBalls; if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed Ball* balls = reinterpret_cast(SEGENV.data); - // number of balls based on intensity setting to max of 7 (cycles colors) + // number of balls based on intensity setting to MAX of 7 (cycles colors) // non-chosen color is a random color - uint8_t numBalls = int(((SEGMENT.intensity * (maxNumBalls - 0.8f)) / 255) + 1); + uint8_t numBalls = int(((SEGMENT.intensity * (MAXNumBalls - 0.8f)) / 255) + 1); float gravity = -9.81; // standard value of gravity float impactVelocityStart = sqrt( -2 * gravity); @@ -2549,7 +2549,7 @@ uint16_t WS2812FX::mode_bouncing_balls(void) { unsigned long time = millis(); if (SEGENV.call == 0) { - for (uint8_t i = 0; i < maxNumBalls; i++) balls[i].lastBounceTime = time; + for (uint8_t i = 0; i < MAXNumBalls; i++) balls[i].lastBounceTime = time; } bool hasCol2 = SEGCOLOR(2); @@ -2573,7 +2573,7 @@ uint16_t WS2812FX::mode_bouncing_balls(void) { uint32_t color = SEGCOLOR(0); if (SEGMENT.palette) { - color = color_wheel(i*(256/max(numBalls, 8))); + color = color_wheel(i*(256/MAX(numBalls, 8))); } else if (hasCol2) { color = SEGCOLOR(i % NUM_COLORS); } @@ -2665,8 +2665,8 @@ typedef struct Spark { */ uint16_t WS2812FX::mode_popcorn(void) { //allocate segment data - uint16_t maxNumPopcorn = 24; - uint16_t dataSize = sizeof(spark) * maxNumPopcorn; + uint16_t MAXNumPopcorn = 24; + uint16_t dataSize = sizeof(spark) * MAXNumPopcorn; if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed Spark* popcorn = reinterpret_cast(SEGENV.data); @@ -2677,7 +2677,7 @@ uint16_t WS2812FX::mode_popcorn(void) { bool hasCol2 = SEGCOLOR(2); fill(hasCol2 ? BLACK : SEGCOLOR(1)); - uint8_t numPopcorn = SEGMENT.intensity*maxNumPopcorn/255; + uint8_t numPopcorn = SEGMENT.intensity*MAXNumPopcorn/255; if (numPopcorn == 0) numPopcorn = 1; for(uint8_t i = 0; i < numPopcorn; i++) { @@ -2792,13 +2792,13 @@ uint16_t WS2812FX::mode_starburst(void) { star* stars = reinterpret_cast(SEGENV.data); - float maxSpeed = 375.0f; // Max velocity + float MAXSpeed = 375.0f; // Max velocity float particleIgnition = 250.0f; // How long to "flash" float particleFadeTime = 1500.0f; // Fade out time for (int j = 0; j < numStars; j++) { - // speed to adjust chance of a burst, max is nearly always. + // speed to adjust chance of a burst, MAX is nearly always. if (random8((144-(SEGMENT.speed >> 1))) == 0 && stars[j].birth == 0) { // Pick a random color and location. @@ -2807,7 +2807,7 @@ uint16_t WS2812FX::mode_starburst(void) { stars[j].color = col_to_crgb(color_wheel(random8())); stars[j].pos = startPos; - stars[j].vel = maxSpeed * (float)(random8())/255.0 * multiplier; + stars[j].vel = MAXSpeed * (float)(random8())/255.0 * multiplier; stars[j].birth = it; stars[j].last = it; // more fragments means larger burst effect @@ -3026,7 +3026,7 @@ uint16_t WS2812FX::mode_drip(void) drops[j].pos = SEGLEN-1; // start at end drops[j].vel = 0; // speed drops[j].col = sourcedrop; // brightness - drops[j].colIndex = 1; // drop state (0 init, 1 forming, 2 falling, 5 bouncing) + drops[j].colIndex = 1; // drop state (0 init, 1 forMINg, 2 falling, 5 bouncing) } setPixelColor(SEGLEN-1,color_blend(BLACK,SEGCOLOR(0), sourcedrop));// water source @@ -3047,7 +3047,7 @@ uint16_t WS2812FX::mode_drip(void) if (drops[j].pos < 0) drops[j].pos = 0; drops[j].vel += gravity; - for (int i=1;i<7-drops[j].colIndex;i++) { // some minor math so we don't expand bouncing droplets + for (int i=1;i<7-drops[j].colIndex;i++) { // some MINor math so we don't expand bouncing droplets setPixelColor(int(drops[j].pos)+i,color_blend(BLACK,SEGCOLOR(0),drops[j].col/i)); //spread pixel with fade while falling } @@ -3055,7 +3055,7 @@ uint16_t WS2812FX::mode_drip(void) setPixelColor(0,color_blend(SEGCOLOR(0),BLACK,drops[j].col)); } } else { // we hit bottom - if (drops[j].colIndex > 2) { // already hit once, so back to forming + if (drops[j].colIndex > 2) { // already hit once, so back to forMINg drops[j].colIndex = 0; drops[j].col = sourcedrop; @@ -3100,7 +3100,7 @@ uint16_t WS2812FX::mode_plasma(void) { */ uint16_t WS2812FX::mode_percent(void) { - uint8_t percent = max(0, min(200, SEGMENT.intensity)); + uint8_t percent = MAX(0, MIN(200, SEGMENT.intensity)); uint16_t active_leds = (percent < 100) ? SEGLEN * percent / 100.0 : SEGLEN * (200 - percent) / 100.0; diff --git a/wled00/FX.h b/wled00/FX.h index d24250f9..210039ef 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -38,8 +38,8 @@ #define DEFAULT_SPEED (uint8_t)128 #define DEFAULT_COLOR (uint32_t)0xFFAA00 -#define min(a,b) ((a)<(b)?(a):(b)) -#define max(a,b) ((a)>(b)?(a):(b)) +#define MIN(a,b) ((a)<(b)?(a):(b)) +#define MAX(a,b) ((a)>(b)?(a):(b)) /* Not used in all effects yet */ #define WLED_FPS 42 diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 05568623..75ec6510 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -224,7 +224,7 @@ void WS2812FX::show(void) { if(useWackyWS2815PowerModel) { // ignore white component on WS2815 power calculation - powerSum += (max(max(c.R,c.G),c.B)) * 3; + powerSum += (MAX(MAX(c.R,c.G),c.B)) * 3; } else { @@ -638,7 +638,7 @@ uint32_t WS2812FX::color_wheel(uint8_t pos) { } /* - * Returns a new, random wheel index with a minimum distance of 42 from pos. + * Returns a new, random wheel index with a MINimum distance of 42 from pos. */ uint8_t WS2812FX::get_random_wheel_index(uint8_t pos) { uint8_t r = 0, x = 0, y = 0, d = 0; @@ -647,7 +647,7 @@ uint8_t WS2812FX::get_random_wheel_index(uint8_t pos) { r = random8(); x = abs(pos - r); y = 255 - x; - d = min(x, y); + d = MIN(x, y); } return r; } @@ -730,8 +730,8 @@ void WS2812FX::handle_palette(void) CHSV prim_hsv = rgb2hsv_approximate(prim); targetPalette = CRGBPalette16( CHSV(prim_hsv.h, prim_hsv.s, prim_hsv.v), //color itself - CHSV(prim_hsv.h, max(prim_hsv.s - 50,0), prim_hsv.v), //less saturated - CHSV(prim_hsv.h, prim_hsv.s, max(prim_hsv.v - 50,0)), //darker + CHSV(prim_hsv.h, MAX(prim_hsv.s - 50,0), prim_hsv.v), //less saturated + CHSV(prim_hsv.h, prim_hsv.s, MAX(prim_hsv.v - 50,0)), //darker CHSV(prim_hsv.h, prim_hsv.s, prim_hsv.v)); //color itself break;} case 4: {//primary + secondary diff --git a/wled00/wled.h b/wled00/wled.h index 04341446..3b744bcd 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -468,10 +468,10 @@ extern WS2812FX strip; #define DEBUG_PRINT(x) Serial.print(x) #define DEBUG_PRINTLN(x) Serial.println(x) #define DEBUG_PRINTF(x) Serial.printf(x) -unsigned long debugTime = 0; -int lastWifiState = 3; -unsigned long wifiStateChangedTime = 0; -int loops = 0; +extern unsigned long debugTime; +extern int lastWifiState; +extern unsigned long wifiStateChangedTime; +extern int loops; #else #define DEBUG_PRINT(x) #define DEBUG_PRINTLN(x) From ef125ff1095bd0525da6ca5ba9a398d0d06971a2 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Thu, 26 Mar 2020 19:43:23 +0100 Subject: [PATCH 181/232] Fixed compilation for Analog LEDs Fixed sync settings network port fields too small --- CHANGELOG.md | 33 +++++++++++++++++++-------------- wled00/FX.h | 2 +- wled00/html_settings.h | 6 +++--- wled00/wled00.ino | 2 +- 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56afa3ff..7d3a466c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,38 +2,43 @@ ### Development versions after 0.9.1 release +#### Build 2003262 + + - Fixed compilation for Analog LEDs + - Fixed sync settings network port fields too small + #### Build 2003261 -- Fixed live preview not displaying whole light if over 255 LEDs + - Fixed live preview not displaying whole light if over 255 LEDs #### Build 2003251 -- Added Pacifica effect (tentative, doesn't yet support other colors) -- Added Atlantica palette -- Fixed ESP32 build of Espalexa + - Added Pacifica effect (tentative, doesn't yet support other colors) + - Added Atlantica palette + - Fixed ESP32 build of Espalexa #### Build 2003222 -- Fixed Alexa Whites on non-RGBW lights (bump Espalexa to 2.4.5) + - Fixed Alexa Whites on non-RGBW lights (bump Espalexa to 2.4.5) #### Build 2003221 -- Moved Cronixie driver from FX library to drawOverlay handler + - Moved Cronixie driver from FX library to drawOverlay handler #### Build 2003211 -- Added custom mapping compile define to FX_fcn.h -- Merged pull request #784 by @TravisDean: Fixed initialization bug when toggling skip first -- Added link to youtube videos by Room31 to readme + - Added custom mapping compile define to FX_fcn.h + - Merged pull request #784 by @TravisDean: Fixed initialization bug when toggling skip first + - Added link to youtube videos by Room31 to readme #### Build 2003141 -- Fixed color of main segment returned in JSON API during transition not being target color (closes #765) -- Fixed arlsLock() being called after pixels set in E1.31 (closes #772) -- Fixed HTTP API calls not having an effect if no segment selected (now applies to main segment) + - Fixed color of main segment returned in JSON API during transition not being target color (closes #765) + - Fixed arlsLock() being called after pixels set in E1.31 (closes #772) + - Fixed HTTP API calls not having an effect if no segment selected (now applies to main segment) #### Build 2003121 -- Created changelog.md - make tracking changes to code easier -- Merged pull request #766 by @pille: Fix E1.31 out-of sequence detection + - Created changelog.md - make tracking changes to code easier + - Merged pull request #766 by @pille: Fix E1.31 out-of sequence detection diff --git a/wled00/FX.h b/wled00/FX.h index b6ed7974..a87da8e2 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -631,7 +631,7 @@ class WS2812FX { #ifdef WLED_USE_ANALOG_LEDS uint32_t _analogLastShow = 0; - uint32_t _analogLastColor = 0; + RgbwColor _analogLastColor = 0; uint8_t _analogLastBri = 0; #endif diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 26601695..284d9b30 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -3,7 +3,7 @@ */ //common CSS of settings pages -const char PAGE_settingsCss[] PROGMEM = R"=====()====="; +const char PAGE_settingsCss[] PROGMEM = R"=====()====="; //settings menu const char PAGE_settings[] PROGMEM = R"=====( @@ -273,7 +273,7 @@ Infrared remote:
IR info

WLED Broadcast

-UDP Port:
+UDP Port:
Receive Brightness, Color, and Effects
Send notifications on direct change:
Send notifications on button press:
@@ -316,7 +316,7 @@ Device Auth token:

MQTT

Enable MQTT:
Broker: -Port:
+Port:
The MQTT credentials are sent over an unsecured connection.
Never use the MQTT password for another service!

Username:
diff --git a/wled00/wled00.ino b/wled00/wled00.ino index dc0fbb02..728c5ef6 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -118,7 +118,7 @@ #endif //version code in format yymmddb (b = daily build) -#define VERSION 2003261 +#define VERSION 2003262 char versionString[] = "0.9.1"; From 77ce67d68588a2e31e811b6219ad7fcf591c293f Mon Sep 17 00:00:00 2001 From: feindsender Date: Fri, 27 Mar 2020 10:03:23 +0100 Subject: [PATCH 182/232] Add P9813 LED driver support Adds support for the P9813 LED driver --- wled00/NpbWrapper.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/wled00/NpbWrapper.h b/wled00/NpbWrapper.h index 673d8249..6e58b719 100644 --- a/wled00/NpbWrapper.h +++ b/wled00/NpbWrapper.h @@ -9,6 +9,7 @@ //#define USE_APA102 // Uncomment for using APA102 LEDs. //#define USE_WS2801 // Uncomment for using WS2801 LEDs (make sure you have NeoPixelBus v2.5.6 or newer) //#define USE_LPD8806 // Uncomment for using LPD8806 +//#define USE_P9813 // Uncomment for using P9813 LEDs (make sure you have NeoPixelBus v2.5.8 or newer) //#define WLED_USE_ANALOG_LEDS //Uncomment for using "dumb" PWM controlled LEDs (see pins below, default R: gpio5, G: 12, B: 15, W: 13) //#define WLED_USE_H801 //H801 controller. Please uncomment #define WLED_USE_ANALOG_LEDS as well //#define WLED_USE_5CH_LEDS //5 Channel H801 for cold and warm white @@ -35,7 +36,7 @@ //END CONFIGURATION -#if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) +#if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) || defined(USE_P9813) #define CLKPIN 0 #define DATAPIN 2 #if BTNPIN == CLKPIN || BTNPIN == DATAPIN @@ -73,6 +74,8 @@ #define PIXELMETHOD NeoWs2801Method #elif defined(USE_LPD8806) #define PIXELMETHOD Lpd8806Method + #elif defined(USE_P9813) + #define PIXELMETHOD P9813Method #else #define PIXELMETHOD NeoEsp32Rmt0Ws2812xMethod #endif @@ -84,6 +87,8 @@ #define PIXELMETHOD NeoWs2801Method #elif defined(USE_LPD8806) #define PIXELMETHOD Lpd8806Method + #elif defined(USE_P9813) + #define PIXELMETHOD P9813Method #elif LEDPIN == 2 #define PIXELMETHOD NeoEsp8266Uart1Ws2813Method //if you get an error here, try to change to NeoEsp8266UartWs2813Method or update Neopixelbus #elif LEDPIN == 3 @@ -101,7 +106,10 @@ #define PIXELFEATURE4 DotStarLbgrFeature #elif defined(USE_LPD8806) #define PIXELFEATURE3 Lpd8806GrbFeature - #define PIXELFEATURE4 Lpd8806GrbFeature + #define PIXELFEATURE4 Lpd8806GrbFeature +#elif defined(USE_P9813) + #define PIXELFEATURE3 P9813BgrFeature + #define PIXELFEATURE4 NeoGrbwFeature #else #define PIXELFEATURE3 NeoGrbFeature #define PIXELFEATURE4 NeoGrbwFeature @@ -143,7 +151,7 @@ public: switch (_type) { case NeoPixelType_Grb: - #if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) + #if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) || defined(USE_P9813) _pGrb = new NeoPixelBrightnessBus(countPixels, CLKPIN, DATAPIN); #else _pGrb = new NeoPixelBrightnessBus(countPixels, LEDPIN); @@ -152,7 +160,7 @@ public: break; case NeoPixelType_Grbw: - #if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) + #if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) || defined(USE_P9813) _pGrbw = new NeoPixelBrightnessBus(countPixels, CLKPIN, DATAPIN); #else _pGrbw = new NeoPixelBrightnessBus(countPixels, LEDPIN); From 0ce77bbc59b560d68ba74c332ef354fbfa438e4b Mon Sep 17 00:00:00 2001 From: Rares Serban Date: Sat, 28 Mar 2020 13:07:02 +0200 Subject: [PATCH 183/232] Add ability to set DMX fixtures to a segment and from what LED they should start. --- wled00/html_settings.h | 8 +++++++- wled00/wled00.ino | 4 +++- wled00/wled01_eeprom.ino | 5 +++++ wled00/wled02_xml.ino | 2 ++ wled00/wled03_set.ino | 8 ++++++++ wled00/wled21_dmx.ino | 15 +++++++++++---- 6 files changed, 36 insertions(+), 6 deletions(-) diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 284d9b30..7def2314 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -218,7 +218,13 @@ channels per fixture (15 max):
spacing between start channels: [ info ]
- +
+ +If wanting to use DMX together with LEDs. Number of fixtures will also depend on the segment and start LED settings below
+Using these settings, you can position your fixtures anywhere you want on the LED strip
+which segment should DMX use:
+start LED from the above channel from which DMX fixtures should start:
+

channel functions


diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 728c5ef6..851a9c45 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -23,7 +23,7 @@ //#define WLED_DISABLE_INFRARED //there is no pin left for this on ESP8266-01, saves 12kb #define WLED_ENABLE_MQTT //saves 12kb #define WLED_ENABLE_ADALIGHT //saves 500b only -//#define WLED_ENABLE_DMX //uses 3.5kb +//#define WLED_ENABLE_DMX //uses 3.5kb, if wanting to use DMX together with LEDs, you should change the LEDPIN to something other than 2 in NpbWrapper.h #define WLED_DISABLE_FILESYSTEM //SPIFFS is not used by any WLED feature yet //#define WLED_ENABLE_FS_SERVING //Enable sending html file from SPIFFS before serving progmem version @@ -277,6 +277,8 @@ uint16_t userVar0 = 0, userVar1 = 0; // assigns the different channels to different functions. See wled21_dmx.ino for more information. uint16_t DMXGap = 10; // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. uint16_t DMXStart = 10; // start address of the first fixture + uint16_t DMXSegment = 0; // segment which DMX fixtures use + uint16_t DMXStartLED = 0; // start LED from which DMX fixtures should start #endif diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino index 1f0e8426..c741d4e3 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled01_eeprom.ino @@ -270,6 +270,9 @@ void saveSettingsToEEPROM() for (int i=0; i<15; i++) { EEPROM.write(2535+i, DMXFixtureMap[i]); } // last used: 2549. maybe leave a few bytes for future expansion and go on with 2600 kthxbye. + + EEPROM.write(2550, DMXSegment); + EEPROM.write(2551, DMXStartLED); #endif commit(); @@ -551,6 +554,8 @@ void loadSettingsFromEEPROM(bool first) DMXChannels = EEPROM.read(2530); DMXGap = EEPROM.read(2531) + ((EEPROM.read(2532) << 8) & 0xFF00); DMXStart = EEPROM.read(2533) + ((EEPROM.read(2534) << 8) & 0xFF00); + DMXSegment = EEPROM.read(2550); + DMXStartLED = EEPROM.read(2551); for (int i=0;i<15;i++) { DMXFixtureMap[i] = EEPROM.read(2535+i); diff --git a/wled00/wled02_xml.ino b/wled00/wled02_xml.ino index 100be482..a05c1e97 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/wled02_xml.ino @@ -455,6 +455,8 @@ void getSettingsJS(byte subPage, char* dest) sappend('v',"CN",DMXChannels); sappend('v',"CG",DMXGap); sappend('v',"CS",DMXStart); + sappend('v',"SEG",DMXSegment); + sappend('v',"SL",DMXStartLED); sappend('i',"CH1",DMXFixtureMap[0]); sappend('i',"CH2",DMXFixtureMap[1]); diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index fc82256d..088f31ad 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -310,6 +310,14 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) t = request->arg(argname).toInt(); DMXFixtureMap[i] = t; } + t = request->arg("SEG").toInt(); + if (t>=0 && t<10) { + DMXSegment = t; + } + t = request->arg("SL").toInt(); + if (t>=0 && t<1500) { + DMXStartLED = t; + } } #endif diff --git a/wled00/wled21_dmx.ino b/wled00/wled21_dmx.ino index 4230f07d..e48b051a 100644 --- a/wled00/wled21_dmx.ino +++ b/wled00/wled21_dmx.ino @@ -5,20 +5,27 @@ */ #ifdef WLED_ENABLE_DMX -void handleDMX() { +void handleDMX() { + WS2812FX::Segment& seg = strip.getSegment(DMXSegment); + + if (!seg.isActive()) { + return; + } + // TODO: calculate brightness manually if no shutter channel is set - uint8_t brightness = strip.getBrightness(); - for (int i = 0; i < ledCount; i++) { // uses the amount of LEDs as fixture count + // Calculate the start LED depending on segment and DMXStartLED config + uint8_t startLed = seg.start + DMXStartLED; + for (int i = startLed; i < seg.stop; i++) { uint32_t in = strip.getPixelColor(i); // time to get the colors for the individual fixtures as suggested by AirCookie at issue #462 byte w = in >> 24 & 0xFF; byte r = in >> 16 & 0xFF; byte g = in >> 8 & 0xFF; byte b = in & 0xFF; - int DMXFixtureStart = DMXStart + (DMXGap * i); + int DMXFixtureStart = DMXStart + (DMXGap * (i - startLed)); for (int j = 0; j < DMXChannels; j++) { int DMXAddr = DMXFixtureStart + j; switch (DMXFixtureMap[j]) { From b8342f1c9cde82c15bfed4728130451a5487d6f3 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Sat, 28 Mar 2020 07:32:02 -0400 Subject: [PATCH 184/232] actually call the setup function. --- wled00/wled00.ino | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/wled00/wled00.ino b/wled00/wled00.ino index ac993bf6..522447d0 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -3,9 +3,10 @@ */ #include "wled.h" -WLED wled; +WLED& wled; void setup() { - wled.instance(); // Force creation of static instance + wled = WLED::instance(); + wled.setup(); } void loop() { From f35ab125eccb3fca86d27fb4dd7728bb5812e65d Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Sat, 28 Mar 2020 08:30:51 -0400 Subject: [PATCH 185/232] Rename files to remove wled_ --- wled00/{wled_alexa.cpp => alexa.cpp} | 8 +- wled00/{wled_alexa.h => alexa.h} | 32 +- wled00/{wled_blynk.cpp => blynk.cpp} | 6 +- wled00/{wled_blynk.h => blynk.h} | 24 +- wled00/{wled_button.cpp => button.cpp} | 8 +- wled00/{wled_button.h => button.h} | 22 +- wled00/{wled_colors.cpp => colors.cpp} | 2 +- wled00/{wled_colors.h => colors.h} | 38 +- wled00/{wled_cronixie.cpp => cronixie.cpp} | 2 +- wled00/{wled_cronixie.h => cronixie.h} | 24 +- wled00/{wled_dmx.cpp => dmx.cpp} | 2 +- wled00/{wled_dmx.h => dmx.h} | 20 +- wled00/{wled_eeprom.cpp => eeprom.cpp} | 10 +- wled00/{wled_eeprom.h => eeprom.h} | 46 +- wled00/{wled_file.cpp => file.cpp} | 6 +- wled00/{wled_file.h => file.h} | 22 +- wled00/{wled_hue.cpp => hue.cpp} | 10 +- wled00/{wled_hue.h => hue.h} | 30 +- wled00/{wled_ir.cpp => ir.cpp} | 8 +- wled00/{wled_ir.h => ir.h} | 46 +- wled00/{wled_json.cpp => json.cpp} | 6 +- wled00/{wled_json.h => json.h} | 40 +- wled00/{wled_led.cpp => led.cpp} | 12 +- wled00/{wled_led.h => led.h} | 36 +- wled00/{wled_mqtt.cpp => mqtt.cpp} | 12 +- wled00/{wled_mqtt.h => mqtt.h} | 16 +- wled00/{wled_notify.cpp => notify.cpp} | 4 +- wled00/{wled_notify.h => notify.h} | 34 +- wled00/{wled_ntp.cpp => ntp.cpp} | 4 +- wled00/{wled_ntp.h => ntp.h} | 35 +- wled00/{wled_overlay.cpp => overlay.cpp} | 6 +- wled00/{wled_overlay.h => overlay.h} | 26 +- wled00/{wled_server.cpp => server.cpp} | 10 +- wled00/{wled_server.h => server.h} | 40 +- wled00/{wled_set.cpp => set.cpp} | 18 +- wled00/{wled_set.h => set.h} | 30 +- wled00/{wled_usermod.cpp => usermod.cpp} | 0 wled00/{wled_usermod.h => usermod.h} | 14 +- wled00/wled.cpp | 1763 ++++++++++---------- wled00/wled.h | 1038 ++++++------ wled00/{wled_xml.cpp => xml.cpp} | 6 +- wled00/{wled_xml.h => xml.h} | 28 +- 42 files changed, 1772 insertions(+), 1772 deletions(-) rename wled00/{wled_alexa.cpp => alexa.cpp} (95%) rename wled00/{wled_alexa.h => alexa.h} (92%) rename wled00/{wled_blynk.cpp => blynk.cpp} (96%) rename wled00/{wled_blynk.h => blynk.h} (89%) rename wled00/{wled_button.cpp => button.cpp} (96%) rename wled00/{wled_button.h => button.h} (81%) rename wled00/{wled_colors.cpp => colors.cpp} (99%) rename wled00/{wled_colors.h => colors.h} (94%) rename wled00/{wled_cronixie.cpp => cronixie.cpp} (99%) rename wled00/{wled_cronixie.h => cronixie.h} (87%) rename wled00/{wled_dmx.cpp => dmx.cpp} (98%) rename wled00/{wled_dmx.h => dmx.h} (89%) rename wled00/{wled_eeprom.cpp => eeprom.cpp} (99%) rename wled00/{wled_eeprom.h => eeprom.h} (97%) rename wled00/{wled_file.cpp => file.cpp} (98%) rename wled00/{wled_file.h => file.h} (87%) rename wled00/{wled_hue.cpp => hue.cpp} (97%) rename wled00/{wled_hue.h => hue.h} (91%) rename wled00/{wled_ir.cpp => ir.cpp} (99%) rename wled00/{wled_ir.h => ir.h} (94%) rename wled00/{wled_json.cpp => json.cpp} (99%) rename wled00/{wled_json.h => json.h} (94%) rename wled00/{wled_led.cpp => led.cpp} (97%) rename wled00/{wled_led.h => led.h} (93%) rename wled00/{wled_mqtt.cpp => mqtt.cpp} (96%) rename wled00/{wled_mqtt.h => mqtt.h} (82%) rename wled00/{wled_notify.cpp => notify.cpp} (99%) rename wled00/{wled_notify.h => notify.h} (92%) rename wled00/{wled_ntp.cpp => ntp.cpp} (99%) rename wled00/{wled_ntp.h => ntp.h} (82%) rename wled00/{wled_overlay.cpp => overlay.cpp} (98%) rename wled00/{wled_overlay.h => overlay.h} (87%) rename wled00/{wled_server.cpp => server.cpp} (99%) rename wled00/{wled_server.h => server.h} (93%) rename wled00/{wled_set.cpp => set.cpp} (98%) rename wled00/{wled_set.h => set.h} (93%) rename wled00/{wled_usermod.cpp => usermod.cpp} (100%) rename wled00/{wled_usermod.h => usermod.h} (77%) rename wled00/{wled_xml.cpp => xml.cpp} (99%) rename wled00/{wled_xml.h => xml.h} (92%) diff --git a/wled00/wled_alexa.cpp b/wled00/alexa.cpp similarity index 95% rename from wled00/wled_alexa.cpp rename to wled00/alexa.cpp index 1eb33e71..621480d0 100644 --- a/wled00/wled_alexa.cpp +++ b/wled00/alexa.cpp @@ -1,9 +1,9 @@ -#include "wled_alexa.h" +#include "alexa.h" #include "wled.h" #include "const.h" -#include "wled_led.h" -#include "wled_eeprom.h" -#include "wled_colors.h" +#include "led.h" +#include "eeprom.h" +#include "colors.h" #ifndef WLED_DISABLE_ALEXA void onAlexaChange(EspalexaDevice* dev); diff --git a/wled00/wled_alexa.h b/wled00/alexa.h similarity index 92% rename from wled00/wled_alexa.h rename to wled00/alexa.h index 94ad0342..c2adfebe 100644 --- a/wled00/wled_alexa.h +++ b/wled00/alexa.h @@ -1,17 +1,17 @@ -#ifndef WLED_ALEXA_H -#define WLED_ALEXA_H -/* - * Alexa Voice On/Off/Brightness Control. Emulates a Philips Hue bridge to Alexa. - * - * This was put together from these two excellent projects: - * https://github.com/kakopappa/arduino-esp8266-alexa-wemo-switch - * https://github.com/probonopd/ESP8266HueEmulator - */ -#include "src/dependencies/espalexa/EspalexaDevice.h" - -void onAlexaChange(EspalexaDevice* dev); -void alexaInit(); -void handleAlexa(); -void onAlexaChange(EspalexaDevice* dev); - +#ifndef WLED_ALEXA_H +#define WLED_ALEXA_H +/* + * Alexa Voice On/Off/Brightness Control. Emulates a Philips Hue bridge to Alexa. + * + * This was put together from these two excellent projects: + * https://github.com/kakopappa/arduino-esp8266-alexa-wemo-switch + * https://github.com/probonopd/ESP8266HueEmulator + */ +#include "src/dependencies/espalexa/EspalexaDevice.h" + +void onAlexaChange(EspalexaDevice* dev); +void alexaInit(); +void handleAlexa(); +void onAlexaChange(EspalexaDevice* dev); + #endif // WLED_ALEXA_H \ No newline at end of file diff --git a/wled00/wled_blynk.cpp b/wled00/blynk.cpp similarity index 96% rename from wled00/wled_blynk.cpp rename to wled00/blynk.cpp index 2e3d9f7b..2307a694 100644 --- a/wled00/wled_blynk.cpp +++ b/wled00/blynk.cpp @@ -1,9 +1,9 @@ -#include "wled_blynk.h" +#include "blynk.h" #include "const.h" #include "wled.h" #include "src/dependencies/blynk/Blynk/BlynkHandlers.h" -#include "wled_led.h" -#include "wled_colors.h" +#include "led.h" +#include "colors.h" uint16_t blHue = 0; byte blSat = 255; diff --git a/wled00/wled_blynk.h b/wled00/blynk.h similarity index 89% rename from wled00/wled_blynk.h rename to wled00/blynk.h index e8c8341b..cf3a2e22 100644 --- a/wled00/wled_blynk.h +++ b/wled00/blynk.h @@ -1,13 +1,13 @@ -#ifndef WLED_BLYNK_H -#define WLED_BLYNK_H -#include -/* - * Remote light control with the free Blynk app - */ - -void initBlynk(const char* auth); -void handleBlynk(); -void updateBlynk(); -// Unsure if the macro expansions need to accessed through the declaration... TODO - +#ifndef WLED_BLYNK_H +#define WLED_BLYNK_H +#include +/* + * Remote light control with the free Blynk app + */ + +void initBlynk(const char* auth); +void handleBlynk(); +void updateBlynk(); +// Unsure if the macro expansions need to accessed through the declaration... TODO + #endif //WLED_BLYNK_H \ No newline at end of file diff --git a/wled00/wled_button.cpp b/wled00/button.cpp similarity index 96% rename from wled00/wled_button.cpp rename to wled00/button.cpp index 3825cc84..1afb54d5 100644 --- a/wled00/wled_button.cpp +++ b/wled00/button.cpp @@ -1,8 +1,8 @@ -#include "wled_button.h" +#include "button.h" #include "wled.h" -#include "wled_led.h" -#include "wled_eeprom.h" -#include "wled_set.h" +#include "led.h" +#include "eeprom.h" +#include "set.h" /* * Physical IO diff --git a/wled00/wled_button.h b/wled00/button.h similarity index 81% rename from wled00/wled_button.h rename to wled00/button.h index 5f894477..f974888e 100644 --- a/wled00/wled_button.h +++ b/wled00/button.h @@ -1,12 +1,12 @@ -#ifndef WLED_BUTTON_H -#define WLED_BUTTON_H -#include -/* - * Physical IO - */ - -void shortPressAction(); -void handleButton(); -void handleIO(); - +#ifndef WLED_BUTTON_H +#define WLED_BUTTON_H +#include +/* + * Physical IO + */ + +void shortPressAction(); +void handleButton(); +void handleIO(); + #endif // WLED_BUTTON_H \ No newline at end of file diff --git a/wled00/wled_colors.cpp b/wled00/colors.cpp similarity index 99% rename from wled00/wled_colors.cpp rename to wled00/colors.cpp index dcd32e57..6f247757 100644 --- a/wled00/wled_colors.cpp +++ b/wled00/colors.cpp @@ -1,4 +1,4 @@ -#include "wled_colors.h" +#include "colors.h" #include "wled.h" void colorFromUint32(uint32_t in, bool secondary) diff --git a/wled00/wled_colors.h b/wled00/colors.h similarity index 94% rename from wled00/wled_colors.h rename to wled00/colors.h index 917311e7..85e0adb5 100644 --- a/wled00/wled_colors.h +++ b/wled00/colors.h @@ -1,20 +1,20 @@ -#ifndef WLED_COLORS_H -#define WLED_COLORS_H -#include -/* - * Color conversion methods - */ - -void colorFromUint32(uint32_t in, bool secondary = false); -void colorFromUint24(uint32_t in, bool secondary = false); -void relativeChangeWhite(int8_t amount, byte lowerBoundary = 0); -void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb -void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb - -void colorXYtoRGB(float x, float y, byte* rgb); // only defined if huesync disabled TODO -void colorRGBtoXY(byte* rgb, float* xy); // only defined if huesync disabled TODO - -void colorFromDecOrHexString(byte* rgb, char* in); -void colorRGBtoRGBW(byte* rgb); //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_MODE_LEGACY) - +#ifndef WLED_COLORS_H +#define WLED_COLORS_H +#include +/* + * Color conversion methods + */ + +void colorFromUint32(uint32_t in, bool secondary = false); +void colorFromUint24(uint32_t in, bool secondary = false); +void relativeChangeWhite(int8_t amount, byte lowerBoundary = 0); +void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb +void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb + +void colorXYtoRGB(float x, float y, byte* rgb); // only defined if huesync disabled TODO +void colorRGBtoXY(byte* rgb, float* xy); // only defined if huesync disabled TODO + +void colorFromDecOrHexString(byte* rgb, char* in); +void colorRGBtoRGBW(byte* rgb); //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_MODE_LEGACY) + #endif //WLED_COLORS_H \ No newline at end of file diff --git a/wled00/wled_cronixie.cpp b/wled00/cronixie.cpp similarity index 99% rename from wled00/wled_cronixie.cpp rename to wled00/cronixie.cpp index 107af752..e3b951b8 100644 --- a/wled00/wled_cronixie.cpp +++ b/wled00/cronixie.cpp @@ -1,4 +1,4 @@ -#include "wled_cronixie.h" +#include "cronixie.h" #include "wled.h" #ifndef WLED_DISABLE_CRONIXIE diff --git a/wled00/wled_cronixie.h b/wled00/cronixie.h similarity index 87% rename from wled00/wled_cronixie.h rename to wled00/cronixie.h index 9dea69b3..f6eea6b4 100644 --- a/wled00/wled_cronixie.h +++ b/wled00/cronixie.h @@ -1,13 +1,13 @@ -#ifndef WLED_CRONIXIE_H -#define WLED_CRONIXIE_H -#include -/* - * Support for the Cronixie clock - */ - -byte getSameCodeLength(char code, int index, char const cronixieDisplay[]); -void setCronixie(); -void _overlayCronixie(); -void _drawOverlayCronixie(); - +#ifndef WLED_CRONIXIE_H +#define WLED_CRONIXIE_H +#include +/* + * Support for the Cronixie clock + */ + +byte getSameCodeLength(char code, int index, char const cronixieDisplay[]); +void setCronixie(); +void _overlayCronixie(); +void _drawOverlayCronixie(); + #endif // WLED_CRONIXIE_H \ No newline at end of file diff --git a/wled00/wled_dmx.cpp b/wled00/dmx.cpp similarity index 98% rename from wled00/wled_dmx.cpp rename to wled00/dmx.cpp index f620f777..d024ac71 100644 --- a/wled00/wled_dmx.cpp +++ b/wled00/dmx.cpp @@ -1,4 +1,4 @@ -#include "wled_dmx.h" +#include "dmx.h" #include "wled.h" #ifdef WLED_ENABLE_DMX diff --git a/wled00/wled_dmx.h b/wled00/dmx.h similarity index 89% rename from wled00/wled_dmx.h rename to wled00/dmx.h index 94cbb0d2..707dec91 100644 --- a/wled00/wled_dmx.h +++ b/wled00/dmx.h @@ -1,11 +1,11 @@ -#ifndef WLED_DMX_H -#define WLED_DMX_H -/* - * Support for DMX via MAX485. - * Needs the espdmx library. You might have to change the output pin within the library. Sketchy, i know. - * https://github.com/Rickgg/ESP-Dmx - */ - -void handleDMX(); - +#ifndef WLED_DMX_H +#define WLED_DMX_H +/* + * Support for DMX via MAX485. + * Needs the espdmx library. You might have to change the output pin within the library. Sketchy, i know. + * https://github.com/Rickgg/ESP-Dmx + */ + +void handleDMX(); + #endif //WLED_DMX_H \ No newline at end of file diff --git a/wled00/wled_eeprom.cpp b/wled00/eeprom.cpp similarity index 99% rename from wled00/wled_eeprom.cpp rename to wled00/eeprom.cpp index 04b86289..68b8c35f 100644 --- a/wled00/wled_eeprom.cpp +++ b/wled00/eeprom.cpp @@ -1,9 +1,9 @@ -#include "wled_eeprom.h" +#include "eeprom.h" #include "wled.h" -#include "wled_cronixie.h" -#include "wled_ntp.h" -#include "wled_set.h" -#include "wled_led.h" +#include "cronixie.h" +#include "ntp.h" +#include "set.h" +#include "led.h" //eeprom Version code, enables default settings instead of 0 init on update diff --git a/wled00/wled_eeprom.h b/wled00/eeprom.h similarity index 97% rename from wled00/wled_eeprom.h rename to wled00/eeprom.h index 19f61925..eae52b12 100644 --- a/wled00/wled_eeprom.h +++ b/wled00/eeprom.h @@ -1,23 +1,23 @@ -#ifndef WLED_EPPROM_H -#define WLED_EPPROM_H -#include -/* - * Methods to handle saving and loading to non-volatile memory - * EEPROM Map: https://github.com/Aircoookie/WLED/wiki/EEPROM-Map - */ -#define EEPSIZE 2560 //Maximum is 4096 - -void commit(); -void clearEEPROM(); -void writeStringToEEPROM(uint16_t pos, char* str, uint16_t len); -void readStringFromEEPROM(uint16_t pos, char* str, uint16_t len); -void saveSettingsToEEPROM(); -void loadSettingsFromEEPROM(bool first); -void savedToPresets(); -bool applyPreset(byte index, bool loadBri = true); -void savePreset(byte index, bool persist = true); -void loadMacro(byte index, char* m); -void applyMacro(byte index); -void saveMacro(byte index, String mc, bool persist = true); //only commit on single save, not in settings - -#endif //WLED_EPPROM_H +#ifndef WLED_EPPROM_H +#define WLED_EPPROM_H +#include +/* + * Methods to handle saving and loading to non-volatile memory + * EEPROM Map: https://github.com/Aircoookie/WLED/wiki/EEPROM-Map + */ +#define EEPSIZE 2560 //Maximum is 4096 + +void commit(); +void clearEEPROM(); +void writeStringToEEPROM(uint16_t pos, char* str, uint16_t len); +void readStringFromEEPROM(uint16_t pos, char* str, uint16_t len); +void saveSettingsToEEPROM(); +void loadSettingsFromEEPROM(bool first); +void savedToPresets(); +bool applyPreset(byte index, bool loadBri = true); +void savePreset(byte index, bool persist = true); +void loadMacro(byte index, char* m); +void applyMacro(byte index); +void saveMacro(byte index, String mc, bool persist = true); //only commit on single save, not in settings + +#endif //WLED_EPPROM_H diff --git a/wled00/wled_file.cpp b/wled00/file.cpp similarity index 98% rename from wled00/wled_file.cpp rename to wled00/file.cpp index 81b86d6d..dba27d4f 100644 --- a/wled00/wled_file.cpp +++ b/wled00/file.cpp @@ -1,7 +1,7 @@ -#include "wled_file.h" +#include "file.h" #include "wled.h" -#include "wled_led.h" -#include "wled_notify.h" +#include "led.h" +#include "notify.h" //filesystem #ifndef WLED_DISABLE_FILESYSTEM diff --git a/wled00/wled_file.h b/wled00/file.h similarity index 87% rename from wled00/wled_file.h rename to wled00/file.h index 7457ec4c..90731db6 100644 --- a/wled00/wled_file.h +++ b/wled00/file.h @@ -1,12 +1,12 @@ -#ifndef WLED_FILE_H -#define WLED_FILE_H -#include -#include -/* - * Utility for SPIFFS filesystem & Serial console - */ - -void handleSerial(); -bool handleFileRead(AsyncWebServerRequest*, String path); - +#ifndef WLED_FILE_H +#define WLED_FILE_H +#include +#include +/* + * Utility for SPIFFS filesystem & Serial console + */ + +void handleSerial(); +bool handleFileRead(AsyncWebServerRequest*, String path); + #endif // WLED_FILE_H \ No newline at end of file diff --git a/wled00/wled_hue.cpp b/wled00/hue.cpp similarity index 97% rename from wled00/wled_hue.cpp rename to wled00/hue.cpp index 61756ec0..484f2bd8 100644 --- a/wled00/wled_hue.cpp +++ b/wled00/hue.cpp @@ -1,9 +1,9 @@ -#include "wled_hue.h" +#include "hue.h" #include "wled.h" -#include "wled_colors.h" -#include "wled_eeprom.h" -#include "wled_notify.h" -#include "wled_led.h" +#include "colors.h" +#include "eeprom.h" +#include "notify.h" +#include "led.h" #ifndef WLED_DISABLE_HUESYNC diff --git a/wled00/wled_hue.h b/wled00/hue.h similarity index 91% rename from wled00/wled_hue.h rename to wled00/hue.h index 8b26f8b6..7ce629fb 100644 --- a/wled00/wled_hue.h +++ b/wled00/hue.h @@ -1,16 +1,16 @@ -#ifndef WLED_HUE_H -#define WLED_HUE_H -/* - * Sync to Philips hue lights - */ -#include -class AsyncClient; - -void handleHue(); -void reconnectHue(); -void onHueError(void* arg, AsyncClient* client, int8_t error); -void onHueConnect(void* arg, AsyncClient* client); -void sendHuePoll(); -void onHueData(void* arg, AsyncClient* client, void *data, size_t len); - +#ifndef WLED_HUE_H +#define WLED_HUE_H +/* + * Sync to Philips hue lights + */ +#include +class AsyncClient; + +void handleHue(); +void reconnectHue(); +void onHueError(void* arg, AsyncClient* client, int8_t error); +void onHueConnect(void* arg, AsyncClient* client); +void sendHuePoll(); +void onHueData(void* arg, AsyncClient* client, void *data, size_t len); + #endif //WLED_HUE_H \ No newline at end of file diff --git a/wled00/wled_ir.cpp b/wled00/ir.cpp similarity index 99% rename from wled00/wled_ir.cpp rename to wled00/ir.cpp index f625148e..fcc0f5dd 100644 --- a/wled00/wled_ir.cpp +++ b/wled00/ir.cpp @@ -1,8 +1,8 @@ -#include "wled_ir.h" +#include "ir.h" #include "wled.h" -#include "wled_led.h" -#include "wled_colors.h" -#include "wled_eeprom.h" +#include "led.h" +#include "colors.h" +#include "eeprom.h" #if defined(WLED_DISABLE_INFRARED) void handleIR(){} diff --git a/wled00/wled_ir.h b/wled00/ir.h similarity index 94% rename from wled00/wled_ir.h rename to wled00/ir.h index 3f819da0..e6ecb966 100644 --- a/wled00/wled_ir.h +++ b/wled00/ir.h @@ -1,24 +1,24 @@ -#ifndef WLED_IR_H -#define WLED_IR_H -#include -/* - * Infrared sensor support for generic 24/40/44 key RGB remotes - */ - -bool decodeIRCustom(uint32_t code); -void relativeChange(byte* property, int8_t amount, byte lowerBoundary = 0, byte higherBoundary = 0xFF); -void changeEffectSpeed(int8_t amount); -void changeEffectIntensity(int8_t amount); -void decodeIR(uint32_t code); -void decodeIR24(uint32_t code); -void decodeIR24OLD(uint32_t code); -void decodeIR24CT(uint32_t code); -void decodeIR40(uint32_t code); -void decodeIR44(uint32_t code); -void decodeIR21(uint32_t code); -void decodeIR6(uint32_t code); - -void initIR(); -void handleIR(); - +#ifndef WLED_IR_H +#define WLED_IR_H +#include +/* + * Infrared sensor support for generic 24/40/44 key RGB remotes + */ + +bool decodeIRCustom(uint32_t code); +void relativeChange(byte* property, int8_t amount, byte lowerBoundary = 0, byte higherBoundary = 0xFF); +void changeEffectSpeed(int8_t amount); +void changeEffectIntensity(int8_t amount); +void decodeIR(uint32_t code); +void decodeIR24(uint32_t code); +void decodeIR24OLD(uint32_t code); +void decodeIR24CT(uint32_t code); +void decodeIR40(uint32_t code); +void decodeIR44(uint32_t code); +void decodeIR21(uint32_t code); +void decodeIR6(uint32_t code); + +void initIR(); +void handleIR(); + #endif //WLED_IR_H \ No newline at end of file diff --git a/wled00/wled_json.cpp b/wled00/json.cpp similarity index 99% rename from wled00/wled_json.cpp rename to wled00/json.cpp index 17359462..73659e7e 100644 --- a/wled00/wled_json.cpp +++ b/wled00/json.cpp @@ -1,7 +1,7 @@ -#include "wled_json.h" +#include "json.h" #include "wled.h" -#include "wled_eeprom.h" -#include "wled_led.h" +#include "eeprom.h" +#include "led.h" void deserializeSegment(JsonObject elem, byte it) { diff --git a/wled00/wled_json.h b/wled00/json.h similarity index 94% rename from wled00/wled_json.h rename to wled00/json.h index a7c2473a..77d1b616 100644 --- a/wled00/wled_json.h +++ b/wled00/json.h @@ -1,21 +1,21 @@ -#ifndef WLED_JSON_H -#define WLED_JSON_H -/* - * JSON API (De)serialization - */ -#include -#include "ESPAsyncWebServer.h" -#include "src/dependencies/json/ArduinoJson-v6.h" -#include "src/dependencies/json/AsyncJson-v6.h" -#include "fx.h" -// TODO: AsynicWebServerRequest conflict? - -void deserializeSegment(JsonObject elem, byte it); -bool deserializeState(JsonObject root); -void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id); -void serializeState(JsonObject root); -void serializeInfo(JsonObject root); -void serveJson(AsyncWebServerRequest* request); -void serveLiveLeds(AsyncWebServerRequest* request); - +#ifndef WLED_JSON_H +#define WLED_JSON_H +/* + * JSON API (De)serialization + */ +#include +#include "ESPAsyncWebServer.h" +#include "src/dependencies/json/ArduinoJson-v6.h" +#include "src/dependencies/json/AsyncJson-v6.h" +#include "fx.h" +// TODO: AsynicWebServerRequest conflict? + +void deserializeSegment(JsonObject elem, byte it); +bool deserializeState(JsonObject root); +void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id); +void serializeState(JsonObject root); +void serializeInfo(JsonObject root); +void serveJson(AsyncWebServerRequest* request); +void serveLiveLeds(AsyncWebServerRequest* request); + #endif //WLED_JSON_H \ No newline at end of file diff --git a/wled00/wled_led.cpp b/wled00/led.cpp similarity index 97% rename from wled00/wled_led.cpp rename to wled00/led.cpp index bd657a53..4a16dcff 100644 --- a/wled00/wled_led.cpp +++ b/wled00/led.cpp @@ -1,10 +1,10 @@ -#include "wled_led.h" +#include "led.h" #include "wled.h" -#include "wled_notify.h" -#include "wled_blynk.h" -#include "wled_eeprom.h" -#include "wled_mqtt.h" -#include "wled_colors.h" +#include "notify.h" +#include "blynk.h" +#include "eeprom.h" +#include "mqtt.h" +#include "colors.h" void setValuesFromMainSeg() { diff --git a/wled00/wled_led.h b/wled00/led.h similarity index 93% rename from wled00/wled_led.h rename to wled00/led.h index aebe95e4..d5eb97a8 100644 --- a/wled00/wled_led.h +++ b/wled00/led.h @@ -1,19 +1,19 @@ -#ifndef WLED_LED_H -#define WLED_LED_H -#include -/* - * LED methods - */ - -void setValuesFromMainSeg(); -void resetTimebase(); -void toggleOnOff(); -void setAllLeds(); -void setLedsStandard(bool justColors = false); -bool colorChanged(); -void colorUpdated(int callMode); -void updateInterfaces(uint8_t callMode); -void handleTransitions(); -void handleNightlight(); - +#ifndef WLED_LED_H +#define WLED_LED_H +#include +/* + * LED methods + */ + +void setValuesFromMainSeg(); +void resetTimebase(); +void toggleOnOff(); +void setAllLeds(); +void setLedsStandard(bool justColors = false); +bool colorChanged(); +void colorUpdated(int callMode); +void updateInterfaces(uint8_t callMode); +void handleTransitions(); +void handleNightlight(); + #endif \ No newline at end of file diff --git a/wled00/wled_mqtt.cpp b/wled00/mqtt.cpp similarity index 96% rename from wled00/wled_mqtt.cpp rename to wled00/mqtt.cpp index 5e96a18f..b84184af 100644 --- a/wled00/wled_mqtt.cpp +++ b/wled00/mqtt.cpp @@ -1,10 +1,10 @@ -#include "wled_mqtt.h" +#include "mqtt.h" #include "wled.h" -#include "wled_notify.h" -#include "wled_led.h" -#include "wled_colors.h" -#include "wled_xml.h" -#include "wled_set.h" +#include "notify.h" +#include "led.h" +#include "colors.h" +#include "xml.h" +#include "set.h" #ifdef WLED_ENABLE_MQTT #define MQTT_KEEP_ALIVE_TIME 60 // contact the MQTT broker every 60 seconds diff --git a/wled00/wled_mqtt.h b/wled00/mqtt.h similarity index 82% rename from wled00/wled_mqtt.h rename to wled00/mqtt.h index f1c7160e..de602b98 100644 --- a/wled00/wled_mqtt.h +++ b/wled00/mqtt.h @@ -1,9 +1,9 @@ -#ifndef WLED_MQTT_H -#define WLED_MQTT_H -/* - * MQTT communication protocol for home automation - */ -bool initMqtt(); -void publishMqtt(); - +#ifndef WLED_MQTT_H +#define WLED_MQTT_H +/* + * MQTT communication protocol for home automation + */ +bool initMqtt(); +void publishMqtt(); + #endif //WLED_MQTT_H \ No newline at end of file diff --git a/wled00/wled_notify.cpp b/wled00/notify.cpp similarity index 99% rename from wled00/wled_notify.cpp rename to wled00/notify.cpp index 7913b452..30adfee0 100644 --- a/wled00/wled_notify.cpp +++ b/wled00/notify.cpp @@ -1,7 +1,7 @@ -#include "wled_notify.h" +#include "notify.h" #include "wled.h" #include "src/dependencies/e131/ESPAsyncE131.h" -#include "wled_led.h" +#include "led.h" #define WLEDPACKETSIZE 29 #define UDP_IN_MAXSIZE 1472 diff --git a/wled00/wled_notify.h b/wled00/notify.h similarity index 92% rename from wled00/wled_notify.h rename to wled00/notify.h index 04c65aa4..a0fbfde2 100644 --- a/wled00/wled_notify.h +++ b/wled00/notify.h @@ -1,18 +1,18 @@ -#ifndef WLED_NOTIFY_H -#define WLED_NOTIFY_H -#include -#include "src/dependencies/e131/ESPAsyncE131.h" -#include "const.h" -/* - * UDP notifier - */ -//union e131_packet_t; // Will this compile? -class IPAddress; - -void notify(byte callMode, bool followUp=false); -void arlsLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC); -void handleE131Packet(e131_packet_t* p, IPAddress clientIP); -void handleNotifications(); -void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w); - +#ifndef WLED_NOTIFY_H +#define WLED_NOTIFY_H +#include +#include "src/dependencies/e131/ESPAsyncE131.h" +#include "const.h" +/* + * UDP notifier + */ +//union e131_packet_t; // Will this compile? +class IPAddress; + +void notify(byte callMode, bool followUp=false); +void arlsLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC); +void handleE131Packet(e131_packet_t* p, IPAddress clientIP); +void handleNotifications(); +void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w); + #endif // WLED_NOTIFY_H \ No newline at end of file diff --git a/wled00/wled_ntp.cpp b/wled00/ntp.cpp similarity index 99% rename from wled00/wled_ntp.cpp rename to wled00/ntp.cpp index d8d9c38a..3435760b 100644 --- a/wled00/wled_ntp.cpp +++ b/wled00/ntp.cpp @@ -1,6 +1,6 @@ -#include "wled_ntp.h" +#include "ntp.h" #include "wled.h" -#include "wled_eeprom.h" +#include "eeprom.h" TimeChangeRule UTCr = {Last, Sun, Mar, 1, 0}; // UTC Timezone tzUTC(UTCr, UTCr); diff --git a/wled00/wled_ntp.h b/wled00/ntp.h similarity index 82% rename from wled00/wled_ntp.h rename to wled00/ntp.h index be683d14..9029661d 100644 --- a/wled00/wled_ntp.h +++ b/wled00/ntp.h @@ -1,19 +1,18 @@ -#ifndef WLED_NTP_H -#define WLED_NTP_H -#include -#include "timezone/Timezone.h" -/* - * Acquires time from NTP server - */ - -void handleNetworkTime(); -void sendNTPPacket(); -bool checkNTPResponse(); -void updateLocalTime(); -void getTimeString(char* out); -bool checkCountdown(); -void setCountdown(); -byte weekdayMondayFirst(); -void checkTimers(); - +#ifndef WLED_NTP_H +#define WLED_NTP_H +#include +/* + * Acquires time from NTP server + */ + +void handleNetworkTime(); +void sendNTPPacket(); +bool checkNTPResponse(); +void updateLocalTime(); +void getTimeString(char* out); +bool checkCountdown(); +void setCountdown(); +byte weekdayMondayFirst(); +void checkTimers(); + #endif // WLED_NTP_H \ No newline at end of file diff --git a/wled00/wled_overlay.cpp b/wled00/overlay.cpp similarity index 98% rename from wled00/wled_overlay.cpp rename to wled00/overlay.cpp index 8cb70786..ccdd3d0c 100644 --- a/wled00/wled_overlay.cpp +++ b/wled00/overlay.cpp @@ -1,7 +1,7 @@ -#include "wled_overlay.h" +#include "overlay.h" #include "wled.h" -#include "wled_cronixie.h" -#include "wled_ntp.h" +#include "cronixie.h" +#include "ntp.h" void initCronixie() { diff --git a/wled00/wled_overlay.h b/wled00/overlay.h similarity index 87% rename from wled00/wled_overlay.h rename to wled00/overlay.h index ec18f45b..b403a6e2 100644 --- a/wled00/wled_overlay.h +++ b/wled00/overlay.h @@ -1,14 +1,14 @@ -#ifndef WLED_OVERLAYS_H -#define WLED_OVERLAYS_H -#include -/* - * Used to draw clock overlays over the strip - */ - -void initCronixie(); -void handleOverlays(); -void handleOverlayDraw(); -void _overlayAnalogCountdown(); -void _overlayAnalogClock(); - +#ifndef WLED_OVERLAYS_H +#define WLED_OVERLAYS_H +#include +/* + * Used to draw clock overlays over the strip + */ + +void initCronixie(); +void handleOverlays(); +void handleOverlayDraw(); +void _overlayAnalogCountdown(); +void _overlayAnalogClock(); + #endif // WLED_OVERLAY_H \ No newline at end of file diff --git a/wled00/wled_server.cpp b/wled00/server.cpp similarity index 99% rename from wled00/wled_server.cpp rename to wled00/server.cpp index 542abea4..3249466c 100644 --- a/wled00/wled_server.cpp +++ b/wled00/server.cpp @@ -1,9 +1,9 @@ -#include "wled_server.h" +#include "server.h" #include "wled.h" -#include "wled_file.h" -#include "wled_set.h" -#include "wled_json.h" -#include "wled_xml.h" +#include "file.h" +#include "set.h" +#include "json.h" +#include "xml.h" //Is this an IP? diff --git a/wled00/wled_server.h b/wled00/server.h similarity index 93% rename from wled00/wled_server.h rename to wled00/server.h index 46adc962..ff7133d6 100644 --- a/wled00/wled_server.h +++ b/wled00/server.h @@ -1,21 +1,21 @@ -#ifndef WLED_SERVER_H -#define WLED_SERVER_H -#include -/* - * Server page declarations - */ -class AsyncWebServerRequest; - - -bool isIp(String str); -bool captivePortal(AsyncWebServerRequest *request); -void initServer(); -void serveIndexOrWelcome(AsyncWebServerRequest *request); -void serveIndex(AsyncWebServerRequest* request); -String msgProcessor(const String& var); -void serveMessage(AsyncWebServerRequest* request, uint16_t code, String headl, String subl="", byte optionT=255); -String settingsProcessor(const String& var); -String dmxProcessor(const String& var); -void serveSettings(AsyncWebServerRequest* request); - +#ifndef WLED_SERVER_H +#define WLED_SERVER_H +#include +/* + * Server page declarations + */ +class AsyncWebServerRequest; + + +bool isIp(String str); +bool captivePortal(AsyncWebServerRequest *request); +void initServer(); +void serveIndexOrWelcome(AsyncWebServerRequest *request); +void serveIndex(AsyncWebServerRequest* request); +String msgProcessor(const String& var); +void serveMessage(AsyncWebServerRequest* request, uint16_t code, String headl, String subl="", byte optionT=255); +String settingsProcessor(const String& var); +String dmxProcessor(const String& var); +void serveSettings(AsyncWebServerRequest* request); + #endif //WLED_SERVER_H \ No newline at end of file diff --git a/wled00/wled_set.cpp b/wled00/set.cpp similarity index 98% rename from wled00/wled_set.cpp rename to wled00/set.cpp index 66d9ff1e..1dfeb19d 100644 --- a/wled00/wled_set.cpp +++ b/wled00/set.cpp @@ -1,13 +1,13 @@ -#include "wled_set.h" +#include "set.h" #include "wled.h" -#include "wled_colors.h" -#include "wled_hue.h" -#include "wled_led.h" -#include "wled_blynk.h" -#include "wled_eeprom.h" -#include "wled_alexa.h" -#include "wled_cronixie.h" -#include "wled_xml.h" +#include "colors.h" +#include "hue.h" +#include "led.h" +#include "blynk.h" +#include "eeprom.h" +#include "alexa.h" +#include "cronixie.h" +#include "xml.h" void _setRandomColor(bool _sec,bool fromButton) { diff --git a/wled00/wled_set.h b/wled00/set.h similarity index 93% rename from wled00/wled_set.h rename to wled00/set.h index ebee8c92..6bc7dbd6 100644 --- a/wled00/wled_set.h +++ b/wled00/set.h @@ -1,16 +1,16 @@ -#ifndef WLED_SET_H -#define WLED_SET_H -#include -#include -/* - * Receives client input - */ - -void _setRandomColor(bool _sec,bool fromButton=false); -bool isAsterisksOnly(const char* str, byte maxLen); -void handleSettingsSet(AsyncWebServerRequest *request, byte subPage); -bool handleSet(AsyncWebServerRequest *request, const String& req); -int getNumVal(const String* req, uint16_t pos); -bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte maxv=255); - +#ifndef WLED_SET_H +#define WLED_SET_H +#include +#include +/* + * Receives client input + */ + +void _setRandomColor(bool _sec,bool fromButton=false); +bool isAsterisksOnly(const char* str, byte maxLen); +void handleSettingsSet(AsyncWebServerRequest *request, byte subPage); +bool handleSet(AsyncWebServerRequest *request, const String& req); +int getNumVal(const String* req, uint16_t pos); +bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte maxv=255); + #endif // WLED_SET_H \ No newline at end of file diff --git a/wled00/wled_usermod.cpp b/wled00/usermod.cpp similarity index 100% rename from wled00/wled_usermod.cpp rename to wled00/usermod.cpp diff --git a/wled00/wled_usermod.h b/wled00/usermod.h similarity index 77% rename from wled00/wled_usermod.h rename to wled00/usermod.h index 61ef6b78..8269c6e9 100644 --- a/wled00/wled_usermod.h +++ b/wled00/usermod.h @@ -1,8 +1,8 @@ -#ifndef WLED_USERMOD_H -#define WLED_USERMOD_H - -void userSetup(); -void userConnected(); -void userLoop(); - +#ifndef WLED_USERMOD_H +#define WLED_USERMOD_H + +void userSetup(); +void userConnected(); +void userLoop(); + #endif // WLED_USERMOD_H \ No newline at end of file diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 3b3d6936..106f503b 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -1,882 +1,883 @@ -#include "wled.h" -#include -#include "wled_led.h" -#include "wled_ir.h" -#include "wled_notify.h" -#include "wled_alexa.h" -#include "wled_overlay.h" -#include "wled_file.h" -#include "wled_button.h" -#include "wled_ntp.h" -#include "wled_usermod.h" -#include "wled_blynk.h" -#include "wled_hue.h" -#include "wled_mqtt.h" -#include "wled_eeprom.h" -#include "wled_server.h" -// Global Variable definitions -char versionString[] = "0.9.1"; - -//AP and OTA default passwords (for maximum change them!) -char apPass[65] = DEFAULT_AP_PASS; -char otaPass[33] = DEFAULT_OTA_PASS; - -//Hardware CONFIG (only changeble HERE, not at runtime) -//LED strip pin, button pin and IR pin changeable in NpbWrapper.h! - -byte auxDefaultState = 0; //0: input 1: high 2: low -byte auxTriggeredState = 0; //0: input 1: high 2: low -char ntpServerName[33] = "0.wled.pool.ntp.org"; //NTP server to use - -//WiFi CONFIG (all these can be changed via web UI, no need to set them here) -char clientSSID[33] = CLIENT_SSID; -char clientPass[65] = CLIENT_PASS; -char cmDNS[33] = "x"; //mDNS address (placeholder, will be replaced by wledXXXXXXXXXXXX.local) -char apSSID[33] = ""; //AP off by default (unless setup) -byte apChannel = 1; //2.4GHz WiFi AP channel (1-13) -byte apHide = 0; //hidden AP SSID -byte apBehavior = AP_BEHAVIOR_BOOT_NO_CONN; //access point opens when no connection after boot by default -IPAddress staticIP(0, 0, 0, 0); //static IP of ESP -IPAddress staticGateway(0, 0, 0, 0); //gateway (router) IP -IPAddress staticSubnet(255, 255, 255, 0); //most common subnet in home networks -bool noWifiSleep = false; //disabling modem sleep modes will increase heat output and power usage, but may help with connection issues - -//LED CONFIG -uint16_t ledCount = 30; //overcurrent prevented by ABL -bool useRGBW = false; //SK6812 strips can contain an extra White channel -#define ABL_MILLIAMPS_DEFAULT 850; //auto lower brightness to stay close to milliampere limit -bool turnOnAtBoot = true; //turn on LEDs at power-up -byte bootPreset = 0; //save preset to load after power-up - -byte col[]{255, 160, 0, 0}; //current RGB(W) primary color. col[] should be updated if you want to change the color. -byte colSec[]{0, 0, 0, 0}; //current RGB(W) secondary color -byte briS = 128; //default brightness - -byte nightlightTargetBri = 0; //brightness after nightlight is over -byte nightlightDelayMins = 60; -bool nightlightFade = true; //if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over -bool nightlightColorFade = false; //if enabled, light will gradually fade color from primary to secondary color. -bool fadeTransition = true; //enable crossfading color transition -uint16_t transitionDelay = 750; //default crossfade duration in ms - -bool skipFirstLed = false; //ignore first LED in strip (useful if you need the LED as signal repeater) -byte briMultiplier = 100; //% of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) - -//User Interface CONFIG -char serverDescription[33] = "WLED"; //Name of module -bool syncToggleReceive = false; //UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise - -//Sync CONFIG -bool buttonEnabled = true; -byte irEnabled = 0; //Infrared receiver - -uint16_t udpPort = 21324; //WLED notifier default port -uint16_t udpRgbPort = 19446; //Hyperion port - -bool receiveNotificationBrightness = true; //apply brightness from incoming notifications -bool receiveNotificationColor = true; //apply color -bool receiveNotificationEffects = true; //apply effects setup -bool notifyDirect = false; //send notification if change via UI or HTTP API -bool notifyButton = false; //send if updated by button or infrared remote -bool notifyAlexa = false; //send notification if updated via Alexa -bool notifyMacro = false; //send notification for macro -bool notifyHue = true; //send notification if Hue light changes -bool notifyTwice = false; //notifications use UDP: enable if devices don't sync reliably - -bool alexaEnabled = true; //enable device discovery by Amazon Echo -char alexaInvocationName[33] = "Light"; //speech control name of device. Choose something voice-to-text can understand - -char blynkApiKey[36] = ""; //Auth token for Blynk server. If empty, no connection will be made - -uint16_t realtimeTimeoutMs = 2500; //ms timeout of realtime mode before returning to normal mode -int arlsOffset = 0; //realtime LED offset -bool receiveDirect = true; //receive UDP realtime -bool arlsDisableGammaCorrection = true; //activate if gamma correction is handled by the source -bool arlsForceMaxBri = false; //enable to force max brightness if source has very dark colors that would be black - -#define E131_MAX_UNIVERSE_COUNT 9 -uint16_t e131Universe = 1; //settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) -uint8_t DMXMode = DMX_MODE_MULTIPLE_RGB; //DMX mode (s.a.) -uint16_t DMXAddress = 1; //DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] -uint8_t DMXOldDimmer = 0; //only update brightness on change -uint8_t e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; //to detect packet loss -bool e131Multicast = false; //multicast or unicast -bool e131SkipOutOfSequence = false; //freeze instead of flickering - -bool mqttEnabled = false; -char mqttDeviceTopic[33] = ""; //main MQTT topic (individual per device, default is wled/mac) -char mqttGroupTopic[33] = "wled/all"; //second MQTT topic (for example to group devices) -char mqttServer[33] = ""; //both domains and IPs should work (no SSL) -char mqttUser[41] = ""; //optional: username for MQTT auth -char mqttPass[41] = ""; //optional: password for MQTT auth -char mqttClientID[41] = ""; //override the client ID -uint16_t mqttPort = 1883; - -bool huePollingEnabled = false; //poll hue bridge for light state -uint16_t huePollIntervalMs = 2500; //low values (< 1sec) may cause lag but offer quicker response -char hueApiKey[47] = "api"; //key token will be obtained from bridge -byte huePollLightId = 1; //ID of hue lamp to sync to. Find the ID in the hue app ("about" section) -IPAddress hueIP = (0, 0, 0, 0); //IP address of the bridge -bool hueApplyOnOff = true; -bool hueApplyBri = true; -bool hueApplyColor = true; - -//Time CONFIG -bool ntpEnabled = false; //get internet time. Only required if you use clock overlays or time-activated macros -bool useAMPM = false; //12h/24h clock format -byte currentTimezone = 0; //Timezone ID. Refer to timezones array in wled10_ntp.ino -int utcOffsetSecs = 0; //Seconds to offset from UTC before timzone calculation - -byte overlayDefault = 0; //0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie -byte overlayMin = 0, overlayMax = ledCount - 1; //boundaries of overlay mode - -byte analogClock12pixel = 0; //The pixel in your strip where "midnight" would be -bool analogClockSecondsTrail = false; //Display seconds as trail of LEDs instead of a single pixel -bool analogClock5MinuteMarks = false; //Light pixels at every 5-minute position - -char cronixieDisplay[7] = "HHMMSS"; //Cronixie Display mask. See wled13_cronixie.ino -bool cronixieBacklight = true; //Allow digits to be back-illuminated - -bool countdownMode = false; //Clock will count down towards date -byte countdownYear = 20, countdownMonth = 1; //Countdown target date, year is last two digits -byte countdownDay = 1, countdownHour = 0; -byte countdownMin = 0, countdownSec = 0; - -byte macroBoot = 0; //macro loaded after startup -byte macroNl = 0; //after nightlight delay over -byte macroCountdown = 0; -byte macroAlexaOn = 0, macroAlexaOff = 0; -byte macroButton = 0, macroLongPress = 0, macroDoublePress = 0; - -//Security CONFIG -bool otaLock = false; //prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks -bool wifiLock = false; //prevents access to WiFi settings when OTA lock is enabled -bool aOtaEnabled = true; //ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on - -uint16_t userVar0 = 0, userVar1 = 0; - -#ifdef WLED_ENABLE_DMX -//dmx CONFIG -byte DMXChannels = 7; // number of channels per fixture -byte DMXFixtureMap[15] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -// assigns the different channels to different functions. See wled21_dmx.ino for more information. -uint16_t DMXGap = 10; // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. -uint16_t DMXStart = 10; // start address of the first fixture -#endif - -//internal global variable declarations -//wifi -bool apActive = false; -bool forceReconnect = false; -uint32_t lastReconnectAttempt = 0; -bool interfacesInited = false; -bool wasConnected = false; - -//color -byte colOld[]{0, 0, 0, 0}; //color before transition -byte colT[]{0, 0, 0, 0}; //color that is currently displayed on the LEDs -byte colIT[]{0, 0, 0, 0}; //color that was last sent to LEDs -byte colSecT[]{0, 0, 0, 0}; -byte colSecOld[]{0, 0, 0, 0}; -byte colSecIT[]{0, 0, 0, 0}; - -byte lastRandomIndex = 0; //used to save last random color so the new one is not the same - -//transitions -bool transitionActive = false; -uint16_t transitionDelayDefault = transitionDelay; -uint16_t transitionDelayTemp = transitionDelay; -unsigned long transitionStartTime; -float tperLast = 0; //crossfade transition progress, 0.0f - 1.0f -bool jsonTransitionOnce = false; - -//nightlight -bool nightlightActive = false; -bool nightlightActiveOld = false; -uint32_t nightlightDelayMs = 10; -uint8_t nightlightDelayMinsDefault = nightlightDelayMins; -unsigned long nightlightStartTime; -byte briNlT = 0; //current nightlight brightness -byte colNlT[]{0, 0, 0, 0}; //current nightlight color - -//brightness -unsigned long lastOnTime = 0; -bool offMode = !turnOnAtBoot; -byte bri = briS; -byte briOld = 0; -byte briT = 0; -byte briIT = 0; -byte briLast = 128; //brightness before turned off. Used for toggle function -byte whiteLast = 128; //white channel before turned off. Used for toggle function - -//button -bool buttonPressedBefore = false; -bool buttonLongPressed = false; -unsigned long buttonPressedTime = 0; -unsigned long buttonWaitTime = 0; - -//notifications -bool notifyDirectDefault = notifyDirect; -bool receiveNotifications = true; -unsigned long notificationSentTime = 0; -byte notificationSentCallMode = NOTIFIER_CALL_MODE_INIT; -bool notificationTwoRequired = false; - -//effects -byte effectCurrent = 0; -byte effectSpeed = 128; -byte effectIntensity = 128; -byte effectPalette = 0; - -//network -bool udpConnected = false, udpRgbConnected = false; - -//ui style -bool showWelcomePage = false; - -//hue -byte hueError = HUE_ERROR_INACTIVE; -//uint16_t hueFailCount = 0; -float hueXLast = 0, hueYLast = 0; -uint16_t hueHueLast = 0, hueCtLast = 0; -byte hueSatLast = 0, hueBriLast = 0; -unsigned long hueLastRequestSent = 0; -bool hueAuthRequired = false; -bool hueReceived = false; -bool hueStoreAllowed = false, hueNewKey = false; - -//overlays -byte overlayCurrent = overlayDefault; -byte overlaySpeed = 200; -unsigned long overlayRefreshMs = 200; -unsigned long overlayRefreshedTime; - -//cronixie -byte dP[]{0, 0, 0, 0, 0, 0}; -bool cronixieInit = false; - -//countdown -unsigned long countdownTime = 1514764800L; -bool countdownOverTriggered = true; - -//timer -byte lastTimerMinute = 0; -byte timerHours[] = {0, 0, 0, 0, 0, 0, 0, 0}; -byte timerMinutes[] = {0, 0, 0, 0, 0, 0, 0, 0}; -byte timerMacro[] = {0, 0, 0, 0, 0, 0, 0, 0}; -byte timerWeekday[] = {255, 255, 255, 255, 255, 255, 255, 255}; //weekdays to activate on -//bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity - -//blynk -bool blynkEnabled = false; - -//preset cycling -bool presetCyclingEnabled = false; -byte presetCycleMin = 1, presetCycleMax = 5; -uint16_t presetCycleTime = 1250; -unsigned long presetCycledTime = 0; -byte presetCycCurr = presetCycleMin; -bool presetApplyBri = true; -bool saveCurrPresetCycConf = false; - -//realtime -byte realtimeMode = REALTIME_MODE_INACTIVE; -IPAddress realtimeIP = (0, 0, 0, 0); -unsigned long realtimeTimeout = 0; - -//mqtt -long lastMqttReconnectAttempt = 0; -long lastInterfaceUpdate = 0; -byte interfaceUpdateCallMode = NOTIFIER_CALL_MODE_INIT; -char mqttStatusTopic[40] = ""; //this must be global because of async handlers - -#if AUXPIN >= 0 - //auxiliary debug pin -byte auxTime = 0; -unsigned long auxStartTime = 0; -bool auxActive = false, auxActiveBefore = false; -#endif - -//alexa udp -String escapedMac; -#ifndef WLED_DISABLE_ALEXA -Espalexa espalexa; -EspalexaDevice *espalexaDevice; -#endif - -//dns server -DNSServer dnsServer; - -//network time -bool ntpConnected = false; -time_t local = 0; -unsigned long ntpLastSyncTime = 999000000L; -unsigned long ntpPacketSentTime = 999000000L; -IPAddress ntpServerIP; -uint16_t ntpLocalPort = 2390; -#define NTP_PACKET_SIZE 48 - -//maximum number of LEDs - MAX_LEDS is coming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue -#define MAX_LEDS 1500 -#define MAX_LEDS_DMA 500 - -//string temp buffer (now stored in stack locally) -#define OMAX 2048 -char *obuf; -uint16_t olen = 0; - -//presets -uint16_t savedPresets = 0; -int8_t currentPreset = -1; -bool isPreset = false; - -byte errorFlag = 0; - -String messageHead, messageSub; -byte optionType; - -bool doReboot = false; //flag to initiate reboot from async handlers -bool doPublishMqtt = false; - -//server library objects -AsyncWebServer server(80); -AsyncClient *hueClient = NULL; -AsyncMqttClient *mqtt = NULL; - -//function prototypes -void colorFromUint32(uint32_t, bool = false); -void serveMessage(AsyncWebServerRequest *, uint16_t, String, String, byte); -void handleE131Packet(e131_packet_t *, IPAddress); -void arlsLock(uint32_t, byte); -void handleOverlayDraw(); - -//udp interface objects -WiFiUDP notifierUdp, rgbUdp; -WiFiUDP ntpUdp; -ESPAsyncE131 e131(handleE131Packet); -bool e131NewData = false; - -//led fx library object -WS2812FX strip = WS2812FX(); - -#define WLED_CONNECTED (WiFi.status() == WL_CONNECTED) -#define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID, DEFAULT_CLIENT_SSID) != 0) - -//debug macros -#ifdef WLED_DEBUG -#define DEBUG_PRINT(x) Serial.print(x) -#define DEBUG_PRINTLN(x) Serial.println(x) -#define DEBUG_PRINTF(x) Serial.printf(x) -unsigned long debugTime = 0; -int lastWifiState = 3; -unsigned long wifiStateChangedTime = 0; -int loops = 0; -#else -#define DEBUG_PRINT(x) -#define DEBUG_PRINTLN(x) -#define DEBUG_PRINTF(x) -#endif - - - -WLED::WLED() { - -} - -//turns all LEDs off and restarts ESP -void WLED::reset() -{ - briT = 0; - long dly = millis(); - while (millis() - dly < 250) - { - yield(); //enough time to send response to client - } - setAllLeds(); - DEBUG_PRINTLN("MODULE RESET"); - ESP.restart(); -} - -bool oappendi(int i) -{ - char s[11]; - sprintf(s, "%ld", i); - return oappend(s); -} - -bool oappend(const char *txt) -{ - uint16_t len = strlen(txt); - if (olen + len >= OMAX) - return false; //buffer full - strcpy(obuf + olen, txt); - olen += len; - return true; -} - -void WLED::loop() -{ - handleIR(); //2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too - handleConnection(); - handleSerial(); - handleNotifications(); - handleTransitions(); -#ifdef WLED_ENABLE_DMX - handleDMX(); -#endif - userLoop(); - - yield(); - handleIO(); - handleIR(); - handleNetworkTime(); - handleAlexa(); - - handleOverlays(); - yield(); -#ifdef WLED_USE_ANALOG_LEDS - strip.setRgbwPwm(); -#endif - - if (doReboot) - reset(); - - if (!realtimeMode) //block stuff if WARLS/Adalight is enabled - { - if (apActive) - dnsServer.processNextRequest(); -#ifndef WLED_DISABLE_OTA - if (WLED_CONNECTED && aOtaEnabled) - ArduinoOTA.handle(); -#endif - handleNightlight(); - yield(); - - handleHue(); - handleBlynk(); - - yield(); - if (!offMode) - strip.service(); - } - yield(); -#ifdef ESP8266 - MDNS.update(); -#endif - if (millis() - lastMqttReconnectAttempt > 30000) - initMqtt(); - -//DEBUG serial logging -#ifdef WLED_DEBUG - if (millis() - debugTime > 9999) - { - DEBUG_PRINTLN("---DEBUG INFO---"); - DEBUG_PRINT("Runtime: "); - DEBUG_PRINTLN(millis()); - DEBUG_PRINT("Unix time: "); - DEBUG_PRINTLN(now()); - DEBUG_PRINT("Free heap: "); - DEBUG_PRINTLN(ESP.getFreeHeap()); - DEBUG_PRINT("Wifi state: "); - DEBUG_PRINTLN(WiFi.status()); - if (WiFi.status() != lastWifiState) - { - wifiStateChangedTime = millis(); - } - lastWifiState = WiFi.status(); - DEBUG_PRINT("State time: "); - DEBUG_PRINTLN(wifiStateChangedTime); - DEBUG_PRINT("NTP last sync: "); - DEBUG_PRINTLN(ntpLastSyncTime); - DEBUG_PRINT("Client IP: "); - DEBUG_PRINTLN(WiFi.localIP()); - DEBUG_PRINT("Loops/sec: "); - DEBUG_PRINTLN(loops / 10); - loops = 0; - debugTime = millis(); - } - loops++; -#endif // WLED_DEBU -} - -void WLED::wledInit() -{ - EEPROM.begin(EEPSIZE); - ledCount = EEPROM.read(229) + ((EEPROM.read(398) << 8) & 0xFF00); - if (ledCount > MAX_LEDS || ledCount == 0) - ledCount = 30; - -#ifdef ESP8266 -#if LEDPIN == 3 - if (ledCount > MAX_LEDS_DMA) - ledCount = MAX_LEDS_DMA; //DMA method uses too much ram -#endif -#endif - Serial.begin(115200); - Serial.setTimeout(50); - DEBUG_PRINTLN(); - DEBUG_PRINT("---WLED "); - DEBUG_PRINT(versionString); - DEBUG_PRINT(" "); - DEBUG_PRINT(VERSION); - DEBUG_PRINTLN(" INIT---"); -#ifdef ARDUINO_ARCH_ESP32 - DEBUG_PRINT("esp32 "); - DEBUG_PRINTLN(ESP.getSdkVersion()); -#else - DEBUG_PRINT("esp8266 "); - DEBUG_PRINTLN(ESP.getCoreVersion()); -#endif - int heapPreAlloc = ESP.getFreeHeap(); - DEBUG_PRINT("heap "); - DEBUG_PRINTLN(ESP.getFreeHeap()); - - strip.init(EEPROM.read(372), ledCount, EEPROM.read(2204)); //init LEDs quickly - strip.setBrightness(0); - - DEBUG_PRINT("LEDs inited. heap usage ~"); - DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap()); - -#ifndef WLED_DISABLE_FILESYSTEM -#ifdef ARDUINO_ARCH_ESP32 - SPIFFS.begin(true); -#endif - SPIFFS.begin(); -#endif - - DEBUG_PRINTLN("Load EEPROM"); - loadSettingsFromEEPROM(true); - beginStrip(); - userSetup(); - if (strcmp(clientSSID, DEFAULT_CLIENT_SSID) == 0) - showWelcomePage = true; - WiFi.persistent(false); - - if (macroBoot > 0) - applyMacro(macroBoot); - Serial.println("Ada"); - - //generate module IDs - escapedMac = WiFi.macAddress(); - escapedMac.replace(":", ""); - escapedMac.toLowerCase(); - if (strcmp(cmDNS, "x") == 0) //fill in unique mdns default - { - strcpy(cmDNS, "wled-"); - sprintf(cmDNS + 5, "%*s", 6, escapedMac.c_str() + 6); - } - if (mqttDeviceTopic[0] == 0) - { - strcpy(mqttDeviceTopic, "wled/"); - sprintf(mqttDeviceTopic + 5, "%*s", 6, escapedMac.c_str() + 6); - } - if (mqttClientID[0] == 0) - { - strcpy(mqttClientID, "WLED-"); - sprintf(mqttClientID + 5, "%*s", 6, escapedMac.c_str() + 6); - } - - strip.service(); - -#ifndef WLED_DISABLE_OTA - if (aOtaEnabled) - { - ArduinoOTA.onStart([]() { -#ifdef ESP8266 - wifi_set_sleep_type(NONE_SLEEP_T); -#endif - DEBUG_PRINTLN("Start ArduinoOTA"); - }); - if (strlen(cmDNS) > 0) - ArduinoOTA.setHostname(cmDNS); - } -#endif -#ifdef WLED_ENABLE_DMX - dmx.init(512); // initialize with bus length -#endif - //HTTP server page init - initServer(); -} - -void WLED::beginStrip() -{ - // Initialize NeoPixel Strip and button - strip.setShowCallback(handleOverlayDraw); - -#ifdef BTNPIN - pinMode(BTNPIN, INPUT_PULLUP); -#endif - - if (bootPreset > 0) - applyPreset(bootPreset, turnOnAtBoot); - colorUpdated(NOTIFIER_CALL_MODE_INIT); - -//init relay pin -#if RLYPIN >= 0 - pinMode(RLYPIN, OUTPUT); -#if RLYMDE - digitalWrite(RLYPIN, bri); -#else - digitalWrite(RLYPIN, !bri); -#endif -#endif - - //disable button if it is "pressed" unintentionally -#ifdef BTNPIN - if (digitalRead(BTNPIN) == LOW) - buttonEnabled = false; -#else - buttonEnabled = false; -#endif -} - -void WLED::initAP(bool resetAP) -{ - if (apBehavior == AP_BEHAVIOR_BUTTON_ONLY && !resetAP) - return; - - if (!apSSID[0] || resetAP) - strcpy(apSSID, "WLED-AP"); - if (resetAP) - strcpy(apPass, DEFAULT_AP_PASS); - DEBUG_PRINT("Opening access point "); - DEBUG_PRINTLN(apSSID); - WiFi.softAPConfig(IPAddress(4, 3, 2, 1), IPAddress(4, 3, 2, 1), IPAddress(255, 255, 255, 0)); - WiFi.softAP(apSSID, apPass, apChannel, apHide); - - if (!apActive) //start captive portal if AP active - { - DEBUG_PRINTLN("Init AP interfaces"); - server.begin(); - if (udpPort > 0 && udpPort != ntpLocalPort) - { - udpConnected = notifierUdp.begin(udpPort); - } - if (udpRgbPort > 0 && udpRgbPort != ntpLocalPort && udpRgbPort != udpPort) - { - udpRgbConnected = rgbUdp.begin(udpRgbPort); - } - - dnsServer.setErrorReplyCode(DNSReplyCode::NoError); - dnsServer.start(53, "*", WiFi.softAPIP()); - } - apActive = true; -} - -void WLED::initConnection() -{ - WiFi.disconnect(); //close old connections -#ifdef ESP8266 - WiFi.setPhyMode(WIFI_PHY_MODE_11N); -#endif - - if (staticIP[0] != 0 && staticGateway[0] != 0) - { - WiFi.config(staticIP, staticGateway, staticSubnet, IPAddress(8, 8, 8, 8)); - } - else - { - WiFi.config(0U, 0U, 0U); - } - - lastReconnectAttempt = millis(); - - if (!WLED_WIFI_CONFIGURED) - { - DEBUG_PRINT("No connection configured. "); - if (!apActive) - initAP(); //instantly go to ap mode - return; - } - else if (!apActive) - { - if (apBehavior == AP_BEHAVIOR_ALWAYS) - { - initAP(); - } - else - { - DEBUG_PRINTLN("Access point disabled."); - WiFi.softAPdisconnect(true); - } - } - showWelcomePage = false; - - DEBUG_PRINT("Connecting to "); - DEBUG_PRINT(clientSSID); - DEBUG_PRINTLN("..."); - -#ifdef ESP8266 - WiFi.hostname(serverDescription); -#endif - - WiFi.begin(clientSSID, clientPass); - -#ifdef ARDUINO_ARCH_ESP32 - WiFi.setSleep(!noWifiSleep); - WiFi.setHostname(serverDescription); -#else - wifi_set_sleep_type((noWifiSleep) ? NONE_SLEEP_T : MODEM_SLEEP_T); -#endif -} - -void WLED::initInterfaces() -{ - DEBUG_PRINTLN("Init STA interfaces"); - - if (hueIP[0] == 0) - { - hueIP[0] = WiFi.localIP()[0]; - hueIP[1] = WiFi.localIP()[1]; - hueIP[2] = WiFi.localIP()[2]; - } - - //init Alexa hue emulation - if (alexaEnabled) - alexaInit(); - -#ifndef WLED_DISABLE_OTA - if (aOtaEnabled) - ArduinoOTA.begin(); -#endif - - strip.service(); - // Set up mDNS responder: - if (strlen(cmDNS) > 0) - { - if (!aOtaEnabled) - MDNS.begin(cmDNS); - - DEBUG_PRINTLN("mDNS started"); - MDNS.addService("http", "tcp", 80); - MDNS.addService("wled", "tcp", 80); - MDNS.addServiceTxt("wled", "tcp", "mac", escapedMac.c_str()); - } - server.begin(); - - if (udpPort > 0 && udpPort != ntpLocalPort) - { - udpConnected = notifierUdp.begin(udpPort); - if (udpConnected && udpRgbPort != udpPort) - udpRgbConnected = rgbUdp.begin(udpRgbPort); - } - if (ntpEnabled) - ntpConnected = ntpUdp.begin(ntpLocalPort); - - initBlynk(blynkApiKey); - e131.begin((e131Multicast) ? E131_MULTICAST : E131_UNICAST, e131Universe, E131_MAX_UNIVERSE_COUNT); - reconnectHue(); - initMqtt(); - interfacesInited = true; - wasConnected = true; -} - -byte stacO = 0; -uint32_t lastHeap; -unsigned long heapTime = 0; - -void WLED::handleConnection() -{ - if (millis() < 2000 && (!WLED_WIFI_CONFIGURED || apBehavior == AP_BEHAVIOR_ALWAYS)) - return; - if (lastReconnectAttempt == 0) - initConnection(); - - //reconnect WiFi to clear stale allocations if heap gets too low - if (millis() - heapTime > 5000) - { - uint32_t heap = ESP.getFreeHeap(); - if (heap < 9000 && lastHeap < 9000) - { - DEBUG_PRINT("Heap too low! "); - DEBUG_PRINTLN(heap); - forceReconnect = true; - } - lastHeap = heap; - heapTime = millis(); - } - - byte stac = 0; - if (apActive) - { -#ifdef ESP8266 - stac = wifi_softap_get_station_num(); -#else - wifi_sta_list_t stationList; - esp_wifi_ap_get_sta_list(&stationList); - stac = stationList.num; -#endif - if (stac != stacO) - { - stacO = stac; - DEBUG_PRINT("Connected AP clients: "); - DEBUG_PRINTLN(stac); - if (!WLED_CONNECTED && WLED_WIFI_CONFIGURED) - { //trying to connect, but not connected - if (stac) - WiFi.disconnect(); //disable search so that AP can work - else - initConnection(); //restart search - } - } - } - if (forceReconnect) - { - DEBUG_PRINTLN("Forcing reconnect."); - initConnection(); - interfacesInited = false; - forceReconnect = false; - wasConnected = false; - return; - } - if (!WLED_CONNECTED) - { - if (interfacesInited) - { - DEBUG_PRINTLN("Disconnected!"); - interfacesInited = false; - initConnection(); - } - if (millis() - lastReconnectAttempt > ((stac) ? 300000 : 20000) && WLED_WIFI_CONFIGURED) - initConnection(); - if (!apActive && millis() - lastReconnectAttempt > 12000 && (!wasConnected || apBehavior == AP_BEHAVIOR_NO_CONN)) - initAP(); - } - else if (!interfacesInited) - { //newly connected - DEBUG_PRINTLN(""); - DEBUG_PRINT("Connected! IP address: "); - DEBUG_PRINTLN(WiFi.localIP()); - initInterfaces(); - userConnected(); - - //shut down AP - if (apBehavior != AP_BEHAVIOR_ALWAYS && apActive) - { - dnsServer.stop(); - WiFi.softAPdisconnect(true); - apActive = false; - DEBUG_PRINTLN("Access point disabled."); - } - } -} - -//by https://github.com/tzapu/WiFiManager/blob/master/WiFiManager.cpp -int getSignalQuality(int rssi) -{ - int quality = 0; - - if (rssi <= -100) - { - quality = 0; - } - else if (rssi >= -50) - { - quality = 100; - } - else - { - quality = 2 * (rssi + 100); - } - return quality; +#include "wled.h" +#include +#include "led.h" +#include "ir.h" +#include "notify.h" +#include "alexa.h" +#include "overlay.h" +#include "file.h" +#include "button.h" +#include "ntp.h" +#include "usermod.h" +#include "blynk.h" +#include "hue.h" +#include "mqtt.h" +#include "eeprom.h" +#include "server.h" +// replaced +// Global Variable definitions +char versionString[] = "0.9.1"; + +//AP and OTA default passwords (for maximum change them!) +char apPass[65] = DEFAULT_AP_PASS; +char otaPass[33] = DEFAULT_OTA_PASS; + +//Hardware CONFIG (only changeble HERE, not at runtime) +//LED strip pin, button pin and IR pin changeable in NpbWrapper.h! + +byte auxDefaultState = 0; //0: input 1: high 2: low +byte auxTriggeredState = 0; //0: input 1: high 2: low +char ntpServerName[33] = "0.wled.pool.ntp.org"; //NTP server to use + +//WiFi CONFIG (all these can be changed via web UI, no need to set them here) +char clientSSID[33] = CLIENT_SSID; +char clientPass[65] = CLIENT_PASS; +char cmDNS[33] = "x"; //mDNS address (placeholder, will be replaced by wledXXXXXXXXXXXX.local) +char apSSID[33] = ""; //AP off by default (unless setup) +byte apChannel = 1; //2.4GHz WiFi AP channel (1-13) +byte apHide = 0; //hidden AP SSID +byte apBehavior = AP_BEHAVIOR_BOOT_NO_CONN; //access point opens when no connection after boot by default +IPAddress staticIP(0, 0, 0, 0); //static IP of ESP +IPAddress staticGateway(0, 0, 0, 0); //gateway (router) IP +IPAddress staticSubnet(255, 255, 255, 0); //most common subnet in home networks +bool noWifiSleep = false; //disabling modem sleep modes will increase heat output and power usage, but may help with connection issues + +//LED CONFIG +uint16_t ledCount = 30; //overcurrent prevented by ABL +bool useRGBW = false; //SK6812 strips can contain an extra White channel +#define ABL_MILLIAMPS_DEFAULT 850; //auto lower brightness to stay close to milliampere limit +bool turnOnAtBoot = true; //turn on LEDs at power-up +byte bootPreset = 0; //save preset to load after power-up + +byte col[]{255, 160, 0, 0}; //current RGB(W) primary color. col[] should be updated if you want to change the color. +byte colSec[]{0, 0, 0, 0}; //current RGB(W) secondary color +byte briS = 128; //default brightness + +byte nightlightTargetBri = 0; //brightness after nightlight is over +byte nightlightDelayMins = 60; +bool nightlightFade = true; //if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over +bool nightlightColorFade = false; //if enabled, light will gradually fade color from primary to secondary color. +bool fadeTransition = true; //enable crossfading color transition +uint16_t transitionDelay = 750; //default crossfade duration in ms + +bool skipFirstLed = false; //ignore first LED in strip (useful if you need the LED as signal repeater) +byte briMultiplier = 100; //% of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) + +//User Interface CONFIG +char serverDescription[33] = "WLED"; //Name of module +bool syncToggleReceive = false; //UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise + +//Sync CONFIG +bool buttonEnabled = true; +byte irEnabled = 0; //Infrared receiver + +uint16_t udpPort = 21324; //WLED notifier default port +uint16_t udpRgbPort = 19446; //Hyperion port + +bool receiveNotificationBrightness = true; //apply brightness from incoming notifications +bool receiveNotificationColor = true; //apply color +bool receiveNotificationEffects = true; //apply effects setup +bool notifyDirect = false; //send notification if change via UI or HTTP API +bool notifyButton = false; //send if updated by button or infrared remote +bool notifyAlexa = false; //send notification if updated via Alexa +bool notifyMacro = false; //send notification for macro +bool notifyHue = true; //send notification if Hue light changes +bool notifyTwice = false; //notifications use UDP: enable if devices don't sync reliably + +bool alexaEnabled = true; //enable device discovery by Amazon Echo +char alexaInvocationName[33] = "Light"; //speech control name of device. Choose something voice-to-text can understand + +char blynkApiKey[36] = ""; //Auth token for Blynk server. If empty, no connection will be made + +uint16_t realtimeTimeoutMs = 2500; //ms timeout of realtime mode before returning to normal mode +int arlsOffset = 0; //realtime LED offset +bool receiveDirect = true; //receive UDP realtime +bool arlsDisableGammaCorrection = true; //activate if gamma correction is handled by the source +bool arlsForceMaxBri = false; //enable to force max brightness if source has very dark colors that would be black + +#define E131_MAX_UNIVERSE_COUNT 9 +uint16_t e131Universe = 1; //settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) +uint8_t DMXMode = DMX_MODE_MULTIPLE_RGB; //DMX mode (s.a.) +uint16_t DMXAddress = 1; //DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] +uint8_t DMXOldDimmer = 0; //only update brightness on change +uint8_t e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; //to detect packet loss +bool e131Multicast = false; //multicast or unicast +bool e131SkipOutOfSequence = false; //freeze instead of flickering + +bool mqttEnabled = false; +char mqttDeviceTopic[33] = ""; //main MQTT topic (individual per device, default is wled/mac) +char mqttGroupTopic[33] = "wled/all"; //second MQTT topic (for example to group devices) +char mqttServer[33] = ""; //both domains and IPs should work (no SSL) +char mqttUser[41] = ""; //optional: username for MQTT auth +char mqttPass[41] = ""; //optional: password for MQTT auth +char mqttClientID[41] = ""; //override the client ID +uint16_t mqttPort = 1883; + +bool huePollingEnabled = false; //poll hue bridge for light state +uint16_t huePollIntervalMs = 2500; //low values (< 1sec) may cause lag but offer quicker response +char hueApiKey[47] = "api"; //key token will be obtained from bridge +byte huePollLightId = 1; //ID of hue lamp to sync to. Find the ID in the hue app ("about" section) +IPAddress hueIP = (0, 0, 0, 0); //IP address of the bridge +bool hueApplyOnOff = true; +bool hueApplyBri = true; +bool hueApplyColor = true; + +//Time CONFIG +bool ntpEnabled = false; //get internet time. Only required if you use clock overlays or time-activated macros +bool useAMPM = false; //12h/24h clock format +byte currentTimezone = 0; //Timezone ID. Refer to timezones array in wled10_ntp.ino +int utcOffsetSecs = 0; //Seconds to offset from UTC before timzone calculation + +byte overlayDefault = 0; //0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie +byte overlayMin = 0, overlayMax = ledCount - 1; //boundaries of overlay mode + +byte analogClock12pixel = 0; //The pixel in your strip where "midnight" would be +bool analogClockSecondsTrail = false; //Display seconds as trail of LEDs instead of a single pixel +bool analogClock5MinuteMarks = false; //Light pixels at every 5-minute position + +char cronixieDisplay[7] = "HHMMSS"; //Cronixie Display mask. See wled13_cronixie.ino +bool cronixieBacklight = true; //Allow digits to be back-illuminated + +bool countdownMode = false; //Clock will count down towards date +byte countdownYear = 20, countdownMonth = 1; //Countdown target date, year is last two digits +byte countdownDay = 1, countdownHour = 0; +byte countdownMin = 0, countdownSec = 0; + +byte macroBoot = 0; //macro loaded after startup +byte macroNl = 0; //after nightlight delay over +byte macroCountdown = 0; +byte macroAlexaOn = 0, macroAlexaOff = 0; +byte macroButton = 0, macroLongPress = 0, macroDoublePress = 0; + +//Security CONFIG +bool otaLock = false; //prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks +bool wifiLock = false; //prevents access to WiFi settings when OTA lock is enabled +bool aOtaEnabled = true; //ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on + +uint16_t userVar0 = 0, userVar1 = 0; + +#ifdef WLED_ENABLE_DMX +//dmx CONFIG +byte DMXChannels = 7; // number of channels per fixture +byte DMXFixtureMap[15] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +// assigns the different channels to different functions. See wled21_dmx.ino for more information. +uint16_t DMXGap = 10; // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. +uint16_t DMXStart = 10; // start address of the first fixture +#endif + +//internal global variable declarations +//wifi +bool apActive = false; +bool forceReconnect = false; +uint32_t lastReconnectAttempt = 0; +bool interfacesInited = false; +bool wasConnected = false; + +//color +byte colOld[]{0, 0, 0, 0}; //color before transition +byte colT[]{0, 0, 0, 0}; //color that is currently displayed on the LEDs +byte colIT[]{0, 0, 0, 0}; //color that was last sent to LEDs +byte colSecT[]{0, 0, 0, 0}; +byte colSecOld[]{0, 0, 0, 0}; +byte colSecIT[]{0, 0, 0, 0}; + +byte lastRandomIndex = 0; //used to save last random color so the new one is not the same + +//transitions +bool transitionActive = false; +uint16_t transitionDelayDefault = transitionDelay; +uint16_t transitionDelayTemp = transitionDelay; +unsigned long transitionStartTime; +float tperLast = 0; //crossfade transition progress, 0.0f - 1.0f +bool jsonTransitionOnce = false; + +//nightlight +bool nightlightActive = false; +bool nightlightActiveOld = false; +uint32_t nightlightDelayMs = 10; +uint8_t nightlightDelayMinsDefault = nightlightDelayMins; +unsigned long nightlightStartTime; +byte briNlT = 0; //current nightlight brightness +byte colNlT[]{0, 0, 0, 0}; //current nightlight color + +//brightness +unsigned long lastOnTime = 0; +bool offMode = !turnOnAtBoot; +byte bri = briS; +byte briOld = 0; +byte briT = 0; +byte briIT = 0; +byte briLast = 128; //brightness before turned off. Used for toggle function +byte whiteLast = 128; //white channel before turned off. Used for toggle function + +//button +bool buttonPressedBefore = false; +bool buttonLongPressed = false; +unsigned long buttonPressedTime = 0; +unsigned long buttonWaitTime = 0; + +//notifications +bool notifyDirectDefault = notifyDirect; +bool receiveNotifications = true; +unsigned long notificationSentTime = 0; +byte notificationSentCallMode = NOTIFIER_CALL_MODE_INIT; +bool notificationTwoRequired = false; + +//effects +byte effectCurrent = 0; +byte effectSpeed = 128; +byte effectIntensity = 128; +byte effectPalette = 0; + +//network +bool udpConnected = false, udpRgbConnected = false; + +//ui style +bool showWelcomePage = false; + +//hue +byte hueError = HUE_ERROR_INACTIVE; +//uint16_t hueFailCount = 0; +float hueXLast = 0, hueYLast = 0; +uint16_t hueHueLast = 0, hueCtLast = 0; +byte hueSatLast = 0, hueBriLast = 0; +unsigned long hueLastRequestSent = 0; +bool hueAuthRequired = false; +bool hueReceived = false; +bool hueStoreAllowed = false, hueNewKey = false; + +//overlays +byte overlayCurrent = overlayDefault; +byte overlaySpeed = 200; +unsigned long overlayRefreshMs = 200; +unsigned long overlayRefreshedTime; + +//cronixie +byte dP[]{0, 0, 0, 0, 0, 0}; +bool cronixieInit = false; + +//countdown +unsigned long countdownTime = 1514764800L; +bool countdownOverTriggered = true; + +//timer +byte lastTimerMinute = 0; +byte timerHours[] = {0, 0, 0, 0, 0, 0, 0, 0}; +byte timerMinutes[] = {0, 0, 0, 0, 0, 0, 0, 0}; +byte timerMacro[] = {0, 0, 0, 0, 0, 0, 0, 0}; +byte timerWeekday[] = {255, 255, 255, 255, 255, 255, 255, 255}; //weekdays to activate on +//bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity + +//blynk +bool blynkEnabled = false; + +//preset cycling +bool presetCyclingEnabled = false; +byte presetCycleMin = 1, presetCycleMax = 5; +uint16_t presetCycleTime = 1250; +unsigned long presetCycledTime = 0; +byte presetCycCurr = presetCycleMin; +bool presetApplyBri = true; +bool saveCurrPresetCycConf = false; + +//realtime +byte realtimeMode = REALTIME_MODE_INACTIVE; +IPAddress realtimeIP = (0, 0, 0, 0); +unsigned long realtimeTimeout = 0; + +//mqtt +long lastMqttReconnectAttempt = 0; +long lastInterfaceUpdate = 0; +byte interfaceUpdateCallMode = NOTIFIER_CALL_MODE_INIT; +char mqttStatusTopic[40] = ""; //this must be global because of async handlers + +#if AUXPIN >= 0 + //auxiliary debug pin +byte auxTime = 0; +unsigned long auxStartTime = 0; +bool auxActive = false, auxActiveBefore = false; +#endif + +//alexa udp +String escapedMac; +#ifndef WLED_DISABLE_ALEXA +Espalexa espalexa; +EspalexaDevice *espalexaDevice; +#endif + +//dns server +DNSServer dnsServer; + +//network time +bool ntpConnected = false; +time_t local = 0; +unsigned long ntpLastSyncTime = 999000000L; +unsigned long ntpPacketSentTime = 999000000L; +IPAddress ntpServerIP; +uint16_t ntpLocalPort = 2390; +#define NTP_PACKET_SIZE 48 + +//maximum number of LEDs - MAX_LEDS is coming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue +#define MAX_LEDS 1500 +#define MAX_LEDS_DMA 500 + +//string temp buffer (now stored in stack locally) +#define OMAX 2048 +char *obuf; +uint16_t olen = 0; + +//presets +uint16_t savedPresets = 0; +int8_t currentPreset = -1; +bool isPreset = false; + +byte errorFlag = 0; + +String messageHead, messageSub; +byte optionType; + +bool doReboot = false; //flag to initiate reboot from async handlers +bool doPublishMqtt = false; + +//server library objects +AsyncWebServer server(80); +AsyncClient *hueClient = NULL; +AsyncMqttClient *mqtt = NULL; + +//function prototypes +void colorFromUint32(uint32_t, bool = false); +void serveMessage(AsyncWebServerRequest *, uint16_t, String, String, byte); +void handleE131Packet(e131_packet_t *, IPAddress); +void arlsLock(uint32_t, byte); +void handleOverlayDraw(); + +//udp interface objects +WiFiUDP notifierUdp, rgbUdp; +WiFiUDP ntpUdp; +ESPAsyncE131 e131(handleE131Packet); +bool e131NewData = false; + +//led fx library object +WS2812FX strip = WS2812FX(); + +#define WLED_CONNECTED (WiFi.status() == WL_CONNECTED) +#define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID, DEFAULT_CLIENT_SSID) != 0) + +//debug macros +#ifdef WLED_DEBUG +#define DEBUG_PRINT(x) Serial.print(x) +#define DEBUG_PRINTLN(x) Serial.println(x) +#define DEBUG_PRINTF(x) Serial.printf(x) +unsigned long debugTime = 0; +int lastWifiState = 3; +unsigned long wifiStateChangedTime = 0; +int loops = 0; +#else +#define DEBUG_PRINT(x) +#define DEBUG_PRINTLN(x) +#define DEBUG_PRINTF(x) +#endif + + + +WLED::WLED() { + +} + +//turns all LEDs off and restarts ESP +void WLED::reset() +{ + briT = 0; + long dly = millis(); + while (millis() - dly < 250) + { + yield(); //enough time to send response to client + } + setAllLeds(); + DEBUG_PRINTLN("MODULE RESET"); + ESP.restart(); +} + +bool oappendi(int i) +{ + char s[11]; + sprintf(s, "%ld", i); + return oappend(s); +} + +bool oappend(const char *txt) +{ + uint16_t len = strlen(txt); + if (olen + len >= OMAX) + return false; //buffer full + strcpy(obuf + olen, txt); + olen += len; + return true; +} + +void WLED::loop() +{ + handleIR(); //2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too + handleConnection(); + handleSerial(); + handleNotifications(); + handleTransitions(); +#ifdef WLED_ENABLE_DMX + handleDMX(); +#endif + userLoop(); + + yield(); + handleIO(); + handleIR(); + handleNetworkTime(); + handleAlexa(); + + handleOverlays(); + yield(); +#ifdef WLED_USE_ANALOG_LEDS + strip.setRgbwPwm(); +#endif + + if (doReboot) + reset(); + + if (!realtimeMode) //block stuff if WARLS/Adalight is enabled + { + if (apActive) + dnsServer.processNextRequest(); +#ifndef WLED_DISABLE_OTA + if (WLED_CONNECTED && aOtaEnabled) + ArduinoOTA.handle(); +#endif + handleNightlight(); + yield(); + + handleHue(); + handleBlynk(); + + yield(); + if (!offMode) + strip.service(); + } + yield(); +#ifdef ESP8266 + MDNS.update(); +#endif + if (millis() - lastMqttReconnectAttempt > 30000) + initMqtt(); + +//DEBUG serial logging +#ifdef WLED_DEBUG + if (millis() - debugTime > 9999) + { + DEBUG_PRINTLN("---DEBUG INFO---"); + DEBUG_PRINT("Runtime: "); + DEBUG_PRINTLN(millis()); + DEBUG_PRINT("Unix time: "); + DEBUG_PRINTLN(now()); + DEBUG_PRINT("Free heap: "); + DEBUG_PRINTLN(ESP.getFreeHeap()); + DEBUG_PRINT("Wifi state: "); + DEBUG_PRINTLN(WiFi.status()); + if (WiFi.status() != lastWifiState) + { + wifiStateChangedTime = millis(); + } + lastWifiState = WiFi.status(); + DEBUG_PRINT("State time: "); + DEBUG_PRINTLN(wifiStateChangedTime); + DEBUG_PRINT("NTP last sync: "); + DEBUG_PRINTLN(ntpLastSyncTime); + DEBUG_PRINT("Client IP: "); + DEBUG_PRINTLN(WiFi.localIP()); + DEBUG_PRINT("Loops/sec: "); + DEBUG_PRINTLN(loops / 10); + loops = 0; + debugTime = millis(); + } + loops++; +#endif // WLED_DEBU +} + +void WLED::wledInit() +{ + EEPROM.begin(EEPSIZE); + ledCount = EEPROM.read(229) + ((EEPROM.read(398) << 8) & 0xFF00); + if (ledCount > MAX_LEDS || ledCount == 0) + ledCount = 30; + +#ifdef ESP8266 +#if LEDPIN == 3 + if (ledCount > MAX_LEDS_DMA) + ledCount = MAX_LEDS_DMA; //DMA method uses too much ram +#endif +#endif + Serial.begin(115200); + Serial.setTimeout(50); + DEBUG_PRINTLN(); + DEBUG_PRINT("---WLED "); + DEBUG_PRINT(versionString); + DEBUG_PRINT(" "); + DEBUG_PRINT(VERSION); + DEBUG_PRINTLN(" INIT---"); +#ifdef ARDUINO_ARCH_ESP32 + DEBUG_PRINT("esp32 "); + DEBUG_PRINTLN(ESP.getSdkVersion()); +#else + DEBUG_PRINT("esp8266 "); + DEBUG_PRINTLN(ESP.getCoreVersion()); +#endif + int heapPreAlloc = ESP.getFreeHeap(); + DEBUG_PRINT("heap "); + DEBUG_PRINTLN(ESP.getFreeHeap()); + + strip.init(EEPROM.read(372), ledCount, EEPROM.read(2204)); //init LEDs quickly + strip.setBrightness(0); + + DEBUG_PRINT("LEDs inited. heap usage ~"); + DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap()); + +#ifndef WLED_DISABLE_FILESYSTEM +#ifdef ARDUINO_ARCH_ESP32 + SPIFFS.begin(true); +#endif + SPIFFS.begin(); +#endif + + DEBUG_PRINTLN("Load EEPROM"); + loadSettingsFromEEPROM(true); + beginStrip(); + userSetup(); + if (strcmp(clientSSID, DEFAULT_CLIENT_SSID) == 0) + showWelcomePage = true; + WiFi.persistent(false); + + if (macroBoot > 0) + applyMacro(macroBoot); + Serial.println("Ada"); + + //generate module IDs + escapedMac = WiFi.macAddress(); + escapedMac.replace(":", ""); + escapedMac.toLowerCase(); + if (strcmp(cmDNS, "x") == 0) //fill in unique mdns default + { + strcpy(cmDNS, "wled-"); + sprintf(cmDNS + 5, "%*s", 6, escapedMac.c_str() + 6); + } + if (mqttDeviceTopic[0] == 0) + { + strcpy(mqttDeviceTopic, "wled/"); + sprintf(mqttDeviceTopic + 5, "%*s", 6, escapedMac.c_str() + 6); + } + if (mqttClientID[0] == 0) + { + strcpy(mqttClientID, "WLED-"); + sprintf(mqttClientID + 5, "%*s", 6, escapedMac.c_str() + 6); + } + + strip.service(); + +#ifndef WLED_DISABLE_OTA + if (aOtaEnabled) + { + ArduinoOTA.onStart([]() { +#ifdef ESP8266 + wifi_set_sleep_type(NONE_SLEEP_T); +#endif + DEBUG_PRINTLN("Start ArduinoOTA"); + }); + if (strlen(cmDNS) > 0) + ArduinoOTA.setHostname(cmDNS); + } +#endif +#ifdef WLED_ENABLE_DMX + dmx.init(512); // initialize with bus length +#endif + //HTTP server page init + initServer(); +} + +void WLED::beginStrip() +{ + // Initialize NeoPixel Strip and button + strip.setShowCallback(handleOverlayDraw); + +#ifdef BTNPIN + pinMode(BTNPIN, INPUT_PULLUP); +#endif + + if (bootPreset > 0) + applyPreset(bootPreset, turnOnAtBoot); + colorUpdated(NOTIFIER_CALL_MODE_INIT); + +//init relay pin +#if RLYPIN >= 0 + pinMode(RLYPIN, OUTPUT); +#if RLYMDE + digitalWrite(RLYPIN, bri); +#else + digitalWrite(RLYPIN, !bri); +#endif +#endif + + //disable button if it is "pressed" unintentionally +#ifdef BTNPIN + if (digitalRead(BTNPIN) == LOW) + buttonEnabled = false; +#else + buttonEnabled = false; +#endif +} + +void WLED::initAP(bool resetAP) +{ + if (apBehavior == AP_BEHAVIOR_BUTTON_ONLY && !resetAP) + return; + + if (!apSSID[0] || resetAP) + strcpy(apSSID, "WLED-AP"); + if (resetAP) + strcpy(apPass, DEFAULT_AP_PASS); + DEBUG_PRINT("Opening access point "); + DEBUG_PRINTLN(apSSID); + WiFi.softAPConfig(IPAddress(4, 3, 2, 1), IPAddress(4, 3, 2, 1), IPAddress(255, 255, 255, 0)); + WiFi.softAP(apSSID, apPass, apChannel, apHide); + + if (!apActive) //start captive portal if AP active + { + DEBUG_PRINTLN("Init AP interfaces"); + server.begin(); + if (udpPort > 0 && udpPort != ntpLocalPort) + { + udpConnected = notifierUdp.begin(udpPort); + } + if (udpRgbPort > 0 && udpRgbPort != ntpLocalPort && udpRgbPort != udpPort) + { + udpRgbConnected = rgbUdp.begin(udpRgbPort); + } + + dnsServer.setErrorReplyCode(DNSReplyCode::NoError); + dnsServer.start(53, "*", WiFi.softAPIP()); + } + apActive = true; +} + +void WLED::initConnection() +{ + WiFi.disconnect(); //close old connections +#ifdef ESP8266 + WiFi.setPhyMode(WIFI_PHY_MODE_11N); +#endif + + if (staticIP[0] != 0 && staticGateway[0] != 0) + { + WiFi.config(staticIP, staticGateway, staticSubnet, IPAddress(8, 8, 8, 8)); + } + else + { + WiFi.config(0U, 0U, 0U); + } + + lastReconnectAttempt = millis(); + + if (!WLED_WIFI_CONFIGURED) + { + DEBUG_PRINT("No connection configured. "); + if (!apActive) + initAP(); //instantly go to ap mode + return; + } + else if (!apActive) + { + if (apBehavior == AP_BEHAVIOR_ALWAYS) + { + initAP(); + } + else + { + DEBUG_PRINTLN("Access point disabled."); + WiFi.softAPdisconnect(true); + } + } + showWelcomePage = false; + + DEBUG_PRINT("Connecting to "); + DEBUG_PRINT(clientSSID); + DEBUG_PRINTLN("..."); + +#ifdef ESP8266 + WiFi.hostname(serverDescription); +#endif + + WiFi.begin(clientSSID, clientPass); + +#ifdef ARDUINO_ARCH_ESP32 + WiFi.setSleep(!noWifiSleep); + WiFi.setHostname(serverDescription); +#else + wifi_set_sleep_type((noWifiSleep) ? NONE_SLEEP_T : MODEM_SLEEP_T); +#endif +} + +void WLED::initInterfaces() +{ + DEBUG_PRINTLN("Init STA interfaces"); + + if (hueIP[0] == 0) + { + hueIP[0] = WiFi.localIP()[0]; + hueIP[1] = WiFi.localIP()[1]; + hueIP[2] = WiFi.localIP()[2]; + } + + //init Alexa hue emulation + if (alexaEnabled) + alexaInit(); + +#ifndef WLED_DISABLE_OTA + if (aOtaEnabled) + ArduinoOTA.begin(); +#endif + + strip.service(); + // Set up mDNS responder: + if (strlen(cmDNS) > 0) + { + if (!aOtaEnabled) + MDNS.begin(cmDNS); + + DEBUG_PRINTLN("mDNS started"); + MDNS.addService("http", "tcp", 80); + MDNS.addService("wled", "tcp", 80); + MDNS.addServiceTxt("wled", "tcp", "mac", escapedMac.c_str()); + } + server.begin(); + + if (udpPort > 0 && udpPort != ntpLocalPort) + { + udpConnected = notifierUdp.begin(udpPort); + if (udpConnected && udpRgbPort != udpPort) + udpRgbConnected = rgbUdp.begin(udpRgbPort); + } + if (ntpEnabled) + ntpConnected = ntpUdp.begin(ntpLocalPort); + + initBlynk(blynkApiKey); + e131.begin((e131Multicast) ? E131_MULTICAST : E131_UNICAST, e131Universe, E131_MAX_UNIVERSE_COUNT); + reconnectHue(); + initMqtt(); + interfacesInited = true; + wasConnected = true; +} + +byte stacO = 0; +uint32_t lastHeap; +unsigned long heapTime = 0; + +void WLED::handleConnection() +{ + if (millis() < 2000 && (!WLED_WIFI_CONFIGURED || apBehavior == AP_BEHAVIOR_ALWAYS)) + return; + if (lastReconnectAttempt == 0) + initConnection(); + + //reconnect WiFi to clear stale allocations if heap gets too low + if (millis() - heapTime > 5000) + { + uint32_t heap = ESP.getFreeHeap(); + if (heap < 9000 && lastHeap < 9000) + { + DEBUG_PRINT("Heap too low! "); + DEBUG_PRINTLN(heap); + forceReconnect = true; + } + lastHeap = heap; + heapTime = millis(); + } + + byte stac = 0; + if (apActive) + { +#ifdef ESP8266 + stac = wifi_softap_get_station_num(); +#else + wifi_sta_list_t stationList; + esp_wifi_ap_get_sta_list(&stationList); + stac = stationList.num; +#endif + if (stac != stacO) + { + stacO = stac; + DEBUG_PRINT("Connected AP clients: "); + DEBUG_PRINTLN(stac); + if (!WLED_CONNECTED && WLED_WIFI_CONFIGURED) + { //trying to connect, but not connected + if (stac) + WiFi.disconnect(); //disable search so that AP can work + else + initConnection(); //restart search + } + } + } + if (forceReconnect) + { + DEBUG_PRINTLN("Forcing reconnect."); + initConnection(); + interfacesInited = false; + forceReconnect = false; + wasConnected = false; + return; + } + if (!WLED_CONNECTED) + { + if (interfacesInited) + { + DEBUG_PRINTLN("Disconnected!"); + interfacesInited = false; + initConnection(); + } + if (millis() - lastReconnectAttempt > ((stac) ? 300000 : 20000) && WLED_WIFI_CONFIGURED) + initConnection(); + if (!apActive && millis() - lastReconnectAttempt > 12000 && (!wasConnected || apBehavior == AP_BEHAVIOR_NO_CONN)) + initAP(); + } + else if (!interfacesInited) + { //newly connected + DEBUG_PRINTLN(""); + DEBUG_PRINT("Connected! IP address: "); + DEBUG_PRINTLN(WiFi.localIP()); + initInterfaces(); + userConnected(); + + //shut down AP + if (apBehavior != AP_BEHAVIOR_ALWAYS && apActive) + { + dnsServer.stop(); + WiFi.softAPdisconnect(true); + apActive = false; + DEBUG_PRINTLN("Access point disabled."); + } + } +} + +//by https://github.com/tzapu/WiFiManager/blob/master/WiFiManager.cpp +int getSignalQuality(int rssi) +{ + int quality = 0; + + if (rssi <= -100) + { + quality = 0; + } + else if (rssi >= -50) + { + quality = 100; + } + else + { + quality = 2 * (rssi + 100); + } + return quality; } \ No newline at end of file diff --git a/wled00/wled.h b/wled00/wled.h index 3b744bcd..c6f791cd 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -1,519 +1,519 @@ -#ifndef WLED_H -#define WLED_H - -/* - Main sketch, global variable declarations -*/ -/* - * @title WLED project sketch - * @version 0.9.1 - * @author Christian Schwinne - */ - -//ESP8266-01 (blue) got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.4.2 and the setting 512K(No SPIFFS). - -//ESP8266-01 (black) has 1MB flash and can thus fit the whole program. Use 1M(64K SPIFFS). -//Uncomment some of the following lines to disable features to compile for ESP8266-01 (max flash size 434kB): - -//You are required to disable over-the-air updates: -//#define WLED_DISABLE_OTA //saves 14kb - -//You need to choose some of these features to disable: -//#define WLED_DISABLE_ALEXA //saves 11kb -//#define WLED_DISABLE_BLYNK //saves 6kb -//#define WLED_DISABLE_CRONIXIE //saves 3kb -//#define WLED_DISABLE_HUESYNC //saves 4kb -//#define WLED_DISABLE_INFRARED //there is no pin left for this on ESP8266-01, saves 12kb -#define WLED_ENABLE_MQTT //saves 12kb -#define WLED_ENABLE_ADALIGHT //saves 500b only -//#define WLED_ENABLE_DMX //uses 3.5kb - -#define WLED_DISABLE_FILESYSTEM //SPIFFS is not used by any WLED feature yet -//#define WLED_ENABLE_FS_SERVING //Enable sending html file from SPIFFS before serving progmem version -//#define WLED_ENABLE_FS_EDITOR //enable /edit page for editing SPIFFS content. Will also be disabled with OTA lock - -//to toggle usb serial debug (un)comment the following line -//#define WLED_DEBUG - -//library inclusions -#include -#ifdef WLED_ENABLE_DMX -#include -DMXESPSerial dmx; -#endif -#ifdef ESP8266 -#include -#include -#include -extern "C" -{ -#include -} -#else //ESP32 -#include -#include "esp_wifi.h" -#include -#include -#include "SPIFFS.h" -#endif - -#include -#include -#include -#include -#ifndef WLED_DISABLE_OTA -#include -#endif -#include -#include "src/dependencies/time/TimeLib.h" -#include "src/dependencies/timezone/Timezone.h" -#ifndef WLED_DISABLE_ALEXA -#define ESPALEXA_ASYNC -#define ESPALEXA_NO_SUBPAGE -#define ESPALEXA_MAXDEVICES 1 -// #define ESPALEXA_DEBUG -#include "src/dependencies/espalexa/Espalexa.h" -#endif -#ifndef WLED_DISABLE_BLYNK -#include "src/dependencies/blynk/BlynkSimpleEsp.h" -#endif -#include "src/dependencies/e131/ESPAsyncE131.h" -#include "src/dependencies/async-mqtt-client/AsyncMqttClient.h" -#include "src/dependencies/json/AsyncJson-v6.h" -#include "src/dependencies/json/ArduinoJson-v6.h" -#include "html_ui.h" -#include "html_settings.h" -#include "html_other.h" -#include "FX.h" -#include "ir_codes.h" -#include "const.h" - -#ifndef CLIENT_SSID -#define CLIENT_SSID DEFAULT_CLIENT_SSID -#endif - -#ifndef CLIENT_PASS -#define CLIENT_PASS "" -#endif - -#if IR_PIN < 0 -#ifndef WLED_DISABLE_INFRARED -#define WLED_DISABLE_INFRARED -#endif -#endif - -#ifndef WLED_DISABLE_INFRARED -#include -#include -#include -#endif - -// remove flicker because PWM signal of RGB channels can become out of phase -#if defined(WLED_USE_ANALOG_LEDS) && defined(ESP8266) -#include "src/dependencies/arduino/core_esp8266_waveform.h" -#endif - -// enable additional debug output -#ifdef WLED_DEBUG -#ifndef ESP8266 -#include -#endif -#endif - -//version code in format yymmddb (b = daily build) -#define VERSION 2003222 - -extern char versionString[]; - -//AP and OTA default passwords (for maximum change them!) -extern char apPass[65]; -extern char otaPass[33]; - -//Hardware CONFIG (only changeble HERE, not at runtime) -//LED strip pin, button pin and IR pin changeable in NpbWrapper.h! - -extern byte auxDefaultState; //0: input 1: high 2: low -extern byte auxTriggeredState; //0: input 1: high 2: low -extern char ntpServerName[33]; //NTP server to use - -//WiFi CONFIG (all these can be changed via web UI, no need to set them here) -extern char clientSSID[33]; -extern char clientPass[65]; -extern char cmDNS[33]; //mDNS address (placeholder, will be replaced by wledXXXXXXXXXXXX.local) -extern char apSSID[33]; //AP off by default (unless setup) -extern byte apChannel; //2.4GHz WiFi AP channel (1-13) -extern byte apHide; //hidden AP SSID -extern byte apBehavior; //access point opens when no connection after boot by default -extern IPAddress staticIP; //static IP of ESP -extern IPAddress staticGateway; //gateway (router) IP -extern IPAddress staticSubnet; //most common subnet in home networks -extern bool noWifiSleep; //disabling modem sleep modes will increase heat output and power usage, but may help with connection issues - -//LED CONFIG -extern uint16_t ledCount; //overcurrent prevented by ABL -extern bool useRGBW; //SK6812 strips can contain an extra White channel -#define ABL_MILLIAMPS_DEFAULT 850; //auto lower brightness to stay close to milliampere limit -extern bool turnOnAtBoot; //turn on LEDs at power-up -extern byte bootPreset; //save preset to load after power-up - -extern byte col[]; //current RGB(W) primary color. col[] should be updated if you want to change the color. -extern byte colSec[]; //current RGB(W) secondary color -extern byte briS; //default brightness - -extern byte nightlightTargetBri; //brightness after nightlight is over -extern byte nightlightDelayMins; -extern bool nightlightFade; //if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over -extern bool nightlightColorFade; //if enabled, light will gradually fade color from primary to secondary color. -extern bool fadeTransition; //enable crossfading color transition -extern uint16_t transitionDelay; //default crossfade duration in ms - -extern bool skipFirstLed; //ignore first LED in strip (useful if you need the LED as signal repeater) -extern byte briMultiplier; //% of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) - -//User Interface CONFIG -extern char serverDescription[33]; //Name of module -extern bool syncToggleReceive; //UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise - -//Sync CONFIG -extern bool buttonEnabled; -extern byte irEnabled; //Infrared receiver - -extern uint16_t udpPort; //WLED notifier default port -extern uint16_t udpRgbPort; //Hyperion port - -extern bool receiveNotificationBrightness; //apply brightness from incoming notifications -extern bool receiveNotificationColor; //apply color -extern bool receiveNotificationEffects; //apply effects setup -extern bool notifyDirect; //send notification if change via UI or HTTP API -extern bool notifyButton; //send if updated by button or infrared remote -extern bool notifyAlexa; //send notification if updated via Alexa -extern bool notifyMacro; //send notification for macro -extern bool notifyHue; //send notification if Hue light changes -extern bool notifyTwice; //notifications use UDP: enable if devices don't sync reliably - -extern bool alexaEnabled; //enable device discovery by Amazon Echo -extern char alexaInvocationName[33]; //speech control name of device. Choose something voice-to-text can understand - -extern char blynkApiKey[36]; //Auth token for Blynk server. If empty, no connection will be made - -extern uint16_t realtimeTimeoutMs; //ms timeout of realtime mode before returning to normal mode -extern int arlsOffset; //realtime LED offset -extern bool receiveDirect; //receive UDP realtime -extern bool arlsDisableGammaCorrection; //activate if gamma correction is handled by the source -extern bool arlsForceMaxBri; //enable to force max brightness if source has very dark colors that would be black - -#define E131_MAX_UNIVERSE_COUNT 9 -extern uint16_t e131Universe; //settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) -extern uint8_t DMXMode; //DMX mode (s.a.) -extern uint16_t DMXAddress; //DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] -extern uint8_t DMXOldDimmer; //only update brightness on change -extern uint8_t e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; //to detect packet loss -extern bool e131Multicast; //multicast or unicast -extern bool e131SkipOutOfSequence; //freeze instead of flickering - -extern bool mqttEnabled; -extern char mqttDeviceTopic[33]; //main MQTT topic (individual per device, default is wled/mac) -extern char mqttGroupTopic[33]; //second MQTT topic (for example to group devices) -extern char mqttServer[33]; //both domains and IPs should work (no SSL) -extern char mqttUser[41]; //optional: username for MQTT auth -extern char mqttPass[41]; //optional: password for MQTT auth -extern char mqttClientID[41]; //override the client ID -extern uint16_t mqttPort; - -extern bool huePollingEnabled; //poll hue bridge for light state -extern uint16_t huePollIntervalMs; //low values (< 1sec) may cause lag but offer quicker response -extern char hueApiKey[47]; //key token will be obtained from bridge -extern byte huePollLightId; //ID of hue lamp to sync to. Find the ID in the hue app ("about" section) -extern IPAddress hueIP; //IP address of the bridge -extern bool hueApplyOnOff; -extern bool hueApplyBri; -extern bool hueApplyColor; - -//Time CONFIG -extern bool ntpEnabled; //get internet time. Only required if you use clock overlays or time-activated macros -extern bool useAMPM; //12h/24h clock format -extern byte currentTimezone; //Timezone ID. Refer to timezones array in wled_ntp.cpp -extern int utcOffsetSecs; //Seconds to offset from UTC before timzone calculation - -extern byte overlayDefault; //0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie -extern byte overlayMin; //boundaries of overlay mode -extern byte overlayMax; - -extern byte analogClock12pixel; //The pixel in your strip where "midnight" would be -extern bool analogClockSecondsTrail; //Display seconds as trail of LEDs instead of a single pixel -extern bool analogClock5MinuteMarks; //Light pixels at every 5-minute position - -extern char cronixieDisplay[7]; //Cronixie Display mask. See wled13_cronixie.ino -extern bool cronixieBacklight; //Allow digits to be back-illuminated - -extern bool countdownMode; //Clock will count down towards date -extern byte countdownYear, countdownMonth; //Countdown target date, year is last two digits -extern byte countdownDay, countdownHour; -extern byte countdownMin, countdownSec; - -extern byte macroBoot; //macro loaded after startup -extern byte macroNl; //after nightlight delay over -extern byte macroCountdown; -extern byte macroAlexaOn, macroAlexaOff; -extern byte macroButton, macroLongPress, macroDoublePress; - -//Security CONFIG -extern bool otaLock; //prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks -extern bool wifiLock; //prevents access to WiFi settings when OTA lock is enabled -extern bool aOtaEnabled; //ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on - -extern uint16_t userVar0, userVar1; - -#ifdef WLED_ENABLE_DMX -//dmx CONFIG -extern byte DMXChannels; // number of channels per fixture -extern byte DMXFixtureMap[15]; -extern // assigns the different channels to different functions. See wled21_dmx.ino for more information. -extern uint16_t DMXGap; // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. -extern uint16_t DMXStart; // start address of the first fixture -#endif - -//internal global variable declarations -//wifi -extern bool apActive; -extern bool forceReconnect; -extern uint32_t lastReconnectAttempt; -extern bool interfacesInited; -extern bool wasConnected; - -//color -extern byte colOld[]; //color before transition -extern byte colT[]; //color that is currently displayed on the LEDs -extern byte colIT[]; //color that was last sent to LEDs -extern byte colSecT[]; -extern byte colSecOld[]; -extern byte colSecIT[]; - -extern byte lastRandomIndex; //used to save last random color so the new one is not the same - -//transitions -extern bool transitionActive; -extern uint16_t transitionDelayDefault; -extern uint16_t transitionDelayTemp; -extern unsigned long transitionStartTime; -extern float tperLast; //crossfade transition progress, 0.0f - 1.0f -extern bool jsonTransitionOnce; - -//nightlight -extern bool nightlightActive; -extern bool nightlightActiveOld; -extern uint32_t nightlightDelayMs; -extern uint8_t nightlightDelayMinsDefault; -extern unsigned long nightlightStartTime; -extern byte briNlT; //current nightlight brightness -extern byte colNlT[]; //current nightlight color - -extern unsigned long lastOnTime; -extern bool offMode; -extern byte bri; -extern byte briOld; -extern byte briT; -extern byte briIT; -extern byte briLast; //brightness before turned off. Used for toggle function -extern byte whiteLast; //white channel before turned off. Used for toggle function - -extern bool buttonPressedBefore; -extern bool buttonLongPressed; -extern unsigned long buttonPressedTime; -extern unsigned long buttonWaitTime; - -extern bool notifyDirectDefault; -extern bool receiveNotifications; -extern unsigned long notificationSentTime; -extern byte notificationSentCallMode; -extern bool notificationTwoRequired; - -extern byte effectCurrent; -extern byte effectSpeed; -extern byte effectIntensity; -extern byte effectPalette; - -extern bool udpConnected, udpRgbConnected; - -extern bool showWelcomePage; - -extern byte hueError; -//uint16_t hueFailCount; -extern float hueXLast, hueYLast; -extern uint16_t hueHueLast, hueCtLast; -extern byte hueSatLast, hueBriLast; -extern unsigned long hueLastRequestSent; -extern bool hueAuthRequired; -extern bool hueReceived; -extern bool hueStoreAllowed, hueNewKey; - -extern byte overlayCurrent; -extern byte overlaySpeed; -extern unsigned long overlayRefreshMs; -extern unsigned long overlayRefreshedTime; - -extern byte dP[]; -extern bool cronixieInit; - -//countdown -extern unsigned long countdownTime; -extern bool countdownOverTriggered; - -//timer -extern byte lastTimerMinute; -extern byte timerHours[]; -extern byte timerMinutes[]; -extern byte timerMacro[]; -extern byte timerWeekday[]; //weekdays to activate on -//bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity - -//blynk -extern bool blynkEnabled; - -//preset cycling -extern bool presetCyclingEnabled; -extern byte presetCycleMin, presetCycleMax; -extern uint16_t presetCycleTime; -extern unsigned long presetCycledTime; -extern byte presetCycCurr; -extern bool presetApplyBri; -extern bool saveCurrPresetCycConf; - -//realtime -extern byte realtimeMode; -extern IPAddress realtimeIP; -extern unsigned long realtimeTimeout; - -//mqtt -extern long lastMqttReconnectAttempt; -extern long lastInterfaceUpdate; -extern byte interfaceUpdateCallMode; -extern char mqttStatusTopic[40]; //this must be global because of async handlers - -#if AUXPIN >= 0 -//auxiliary debug pin -extern byte auxTime; -extern unsigned long auxStartTime; -extern bool auxActive; -#endif - -//alexa udp -extern String escapedMac; -#ifndef WLED_DISABLE_ALEXA -extern Espalexa espalexa; -extern EspalexaDevice *espalexaDevice; -#endif - -//dns server -extern DNSServer dnsServer; - -//network time -extern bool ntpConnected; -extern time_t local; -extern unsigned long ntpLastSyncTime; -extern unsigned long ntpPacketSentTime; -extern IPAddress ntpServerIP; -extern uint16_t ntpLocalPort; -#define NTP_PACKET_SIZE 48 - -//maximum number of LEDs - MAX_LEDS is coming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue -#define MAX_LEDS 1500 -#define MAX_LEDS_DMA 500 - -//string temp buffer (now stored in stack locally) -#define OMAX 2048 -extern char *obuf; -extern uint16_t olen; - -//presets -extern uint16_t savedPresets; -extern int8_t currentPreset; -extern bool isPreset; - -extern byte errorFlag; - -extern String messageHead, messageSub; -extern byte optionType; - -extern bool doReboot; //flag to initiate reboot from async handlers -extern bool doPublishMqtt; - -//server library objects -extern AsyncWebServer server; -extern AsyncClient *hueClient; -extern AsyncMqttClient *mqtt; - -//function prototypes -extern void colorFromUint32(uint32_t, bool); -extern void serveMessage(AsyncWebServerRequest *, uint16_t, String, String, byte); -extern void handleE131Packet(e131_packet_t *, IPAddress); -extern void arlsLock(uint32_t, byte); -extern void handleOverlayDraw(); - -//udp interface objects -extern WiFiUDP notifierUdp, rgbUdp; -extern WiFiUDP ntpUdp; -extern ESPAsyncE131 e131; -extern bool e131NewData; - -//led fx library object -extern WS2812FX strip; - - -#define WLED_CONNECTED (WiFi.status() == WL_CONNECTED) -#define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID, DEFAULT_CLIENT_SSID) != 0) - -//debug macros -#ifdef WLED_DEBUG -#define DEBUG_PRINT(x) Serial.print(x) -#define DEBUG_PRINTLN(x) Serial.println(x) -#define DEBUG_PRINTF(x) Serial.printf(x) -extern unsigned long debugTime; -extern int lastWifiState; -extern unsigned long wifiStateChangedTime; -extern int loops; -#else -#define DEBUG_PRINT(x) -#define DEBUG_PRINTLN(x) -#define DEBUG_PRINTF(x) -#endif - - -// TODO: Inline? -//append new c string to temp buffer efficiently -bool oappend(const char *txt); -//append new number to temp buffer efficiently -bool oappendi(int i); -int getSignalQuality(int rssi); - -class WLED -{ -public: - static WLED &instance() - { - static WLED instance; - return instance; - } - - WLED(); - - void reset(); - void loop(); - - - //boot starts here - void setup() - { - wledInit(); - } - -public: // TODO: privacy - void wledInit(); - void beginStrip(); - - void handleConnection(); - void initAP(bool resetAP = false); - void initConnection(); - void initInterfaces(); -}; -#endif // WLED_H +#ifndef WLED_H +#define WLED_H + +/* + Main sketch, global variable declarations +*/ +/* + * @title WLED project sketch + * @version 0.9.1 + * @author Christian Schwinne + */ + +//ESP8266-01 (blue) got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.4.2 and the setting 512K(No SPIFFS). + +//ESP8266-01 (black) has 1MB flash and can thus fit the whole program. Use 1M(64K SPIFFS). +//Uncomment some of the following lines to disable features to compile for ESP8266-01 (max flash size 434kB): + +//You are required to disable over-the-air updates: +//#define WLED_DISABLE_OTA //saves 14kb + +//You need to choose some of these features to disable: +//#define WLED_DISABLE_ALEXA //saves 11kb +//#define WLED_DISABLE_BLYNK //saves 6kb +//#define WLED_DISABLE_CRONIXIE //saves 3kb +//#define WLED_DISABLE_HUESYNC //saves 4kb +//#define WLED_DISABLE_INFRARED //there is no pin left for this on ESP8266-01, saves 12kb +#define WLED_ENABLE_MQTT //saves 12kb +#define WLED_ENABLE_ADALIGHT //saves 500b only +//#define WLED_ENABLE_DMX //uses 3.5kb + +#define WLED_DISABLE_FILESYSTEM //SPIFFS is not used by any WLED feature yet +//#define WLED_ENABLE_FS_SERVING //Enable sending html file from SPIFFS before serving progmem version +//#define WLED_ENABLE_FS_EDITOR //enable /edit page for editing SPIFFS content. Will also be disabled with OTA lock + +//to toggle usb serial debug (un)comment the following line +//#define WLED_DEBUG + +//library inclusions +#include +#ifdef WLED_ENABLE_DMX +#include +DMXESPSerial dmx; +#endif +#ifdef ESP8266 +#include +#include +#include +extern "C" +{ +#include +} +#else //ESP32 +#include +#include "esp_wifi.h" +#include +#include +#include "SPIFFS.h" +#endif + +#include +#include +#include +#include +#ifndef WLED_DISABLE_OTA +#include +#endif +#include +#include "src/dependencies/time/TimeLib.h" +#include "src/dependencies/timezone/Timezone.h" +#ifndef WLED_DISABLE_ALEXA +#define ESPALEXA_ASYNC +#define ESPALEXA_NO_SUBPAGE +#define ESPALEXA_MAXDEVICES 1 +// #define ESPALEXA_DEBUG +#include "src/dependencies/espalexa/Espalexa.h" +#endif +#ifndef WLED_DISABLE_BLYNK +#include "src/dependencies/blynk/BlynkSimpleEsp.h" +#endif +#include "src/dependencies/e131/ESPAsyncE131.h" +#include "src/dependencies/async-mqtt-client/AsyncMqttClient.h" +#include "src/dependencies/json/AsyncJson-v6.h" +#include "src/dependencies/json/ArduinoJson-v6.h" +#include "html_ui.h" +#include "html_settings.h" +#include "html_other.h" +#include "FX.h" +#include "ir_codes.h" +#include "const.h" + +#ifndef CLIENT_SSID +#define CLIENT_SSID DEFAULT_CLIENT_SSID +#endif + +#ifndef CLIENT_PASS +#define CLIENT_PASS "" +#endif + +#if IR_PIN < 0 +#ifndef WLED_DISABLE_INFRARED +#define WLED_DISABLE_INFRARED +#endif +#endif + +#ifndef WLED_DISABLE_INFRARED +#include +#include +#include +#endif + +// remove flicker because PWM signal of RGB channels can become out of phase +#if defined(WLED_USE_ANALOG_LEDS) && defined(ESP8266) +#include "src/dependencies/arduino/core_esp8266_waveform.h" +#endif + +// enable additional debug output +#ifdef WLED_DEBUG +#ifndef ESP8266 +#include +#endif +#endif + +//version code in format yymmddb (b = daily build) +#define VERSION 2003222 + +extern char versionString[]; + +//AP and OTA default passwords (for maximum change them!) +extern char apPass[65]; +extern char otaPass[33]; + +//Hardware CONFIG (only changeble HERE, not at runtime) +//LED strip pin, button pin and IR pin changeable in NpbWrapper.h! + +extern byte auxDefaultState; //0: input 1: high 2: low +extern byte auxTriggeredState; //0: input 1: high 2: low +extern char ntpServerName[33]; //NTP server to use + +//WiFi CONFIG (all these can be changed via web UI, no need to set them here) +extern char clientSSID[33]; +extern char clientPass[65]; +extern char cmDNS[33]; //mDNS address (placeholder, will be replaced by wledXXXXXXXXXXXX.local) +extern char apSSID[33]; //AP off by default (unless setup) +extern byte apChannel; //2.4GHz WiFi AP channel (1-13) +extern byte apHide; //hidden AP SSID +extern byte apBehavior; //access point opens when no connection after boot by default +extern IPAddress staticIP; //static IP of ESP +extern IPAddress staticGateway; //gateway (router) IP +extern IPAddress staticSubnet; //most common subnet in home networks +extern bool noWifiSleep; //disabling modem sleep modes will increase heat output and power usage, but may help with connection issues + +//LED CONFIG +extern uint16_t ledCount; //overcurrent prevented by ABL +extern bool useRGBW; //SK6812 strips can contain an extra White channel +#define ABL_MILLIAMPS_DEFAULT 850; //auto lower brightness to stay close to milliampere limit +extern bool turnOnAtBoot; //turn on LEDs at power-up +extern byte bootPreset; //save preset to load after power-up + +extern byte col[]; //current RGB(W) primary color. col[] should be updated if you want to change the color. +extern byte colSec[]; //current RGB(W) secondary color +extern byte briS; //default brightness + +extern byte nightlightTargetBri; //brightness after nightlight is over +extern byte nightlightDelayMins; +extern bool nightlightFade; //if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over +extern bool nightlightColorFade; //if enabled, light will gradually fade color from primary to secondary color. +extern bool fadeTransition; //enable crossfading color transition +extern uint16_t transitionDelay; //default crossfade duration in ms + +extern bool skipFirstLed; //ignore first LED in strip (useful if you need the LED as signal repeater) +extern byte briMultiplier; //% of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) + +//User Interface CONFIG +extern char serverDescription[33]; //Name of module +extern bool syncToggleReceive; //UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise + +//Sync CONFIG +extern bool buttonEnabled; +extern byte irEnabled; //Infrared receiver + +extern uint16_t udpPort; //WLED notifier default port +extern uint16_t udpRgbPort; //Hyperion port + +extern bool receiveNotificationBrightness; //apply brightness from incoming notifications +extern bool receiveNotificationColor; //apply color +extern bool receiveNotificationEffects; //apply effects setup +extern bool notifyDirect; //send notification if change via UI or HTTP API +extern bool notifyButton; //send if updated by button or infrared remote +extern bool notifyAlexa; //send notification if updated via Alexa +extern bool notifyMacro; //send notification for macro +extern bool notifyHue; //send notification if Hue light changes +extern bool notifyTwice; //notifications use UDP: enable if devices don't sync reliably + +extern bool alexaEnabled; //enable device discovery by Amazon Echo +extern char alexaInvocationName[33]; //speech control name of device. Choose something voice-to-text can understand + +extern char blynkApiKey[36]; //Auth token for Blynk server. If empty, no connection will be made + +extern uint16_t realtimeTimeoutMs; //ms timeout of realtime mode before returning to normal mode +extern int arlsOffset; //realtime LED offset +extern bool receiveDirect; //receive UDP realtime +extern bool arlsDisableGammaCorrection; //activate if gamma correction is handled by the source +extern bool arlsForceMaxBri; //enable to force max brightness if source has very dark colors that would be black + +#define E131_MAX_UNIVERSE_COUNT 9 +extern uint16_t e131Universe; //settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) +extern uint8_t DMXMode; //DMX mode (s.a.) +extern uint16_t DMXAddress; //DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] +extern uint8_t DMXOldDimmer; //only update brightness on change +extern uint8_t e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; //to detect packet loss +extern bool e131Multicast; //multicast or unicast +extern bool e131SkipOutOfSequence; //freeze instead of flickering + +extern bool mqttEnabled; +extern char mqttDeviceTopic[33]; //main MQTT topic (individual per device, default is wled/mac) +extern char mqttGroupTopic[33]; //second MQTT topic (for example to group devices) +extern char mqttServer[33]; //both domains and IPs should work (no SSL) +extern char mqttUser[41]; //optional: username for MQTT auth +extern char mqttPass[41]; //optional: password for MQTT auth +extern char mqttClientID[41]; //override the client ID +extern uint16_t mqttPort; + +extern bool huePollingEnabled; //poll hue bridge for light state +extern uint16_t huePollIntervalMs; //low values (< 1sec) may cause lag but offer quicker response +extern char hueApiKey[47]; //key token will be obtained from bridge +extern byte huePollLightId; //ID of hue lamp to sync to. Find the ID in the hue app ("about" section) +extern IPAddress hueIP; //IP address of the bridge +extern bool hueApplyOnOff; +extern bool hueApplyBri; +extern bool hueApplyColor; + +//Time CONFIG +extern bool ntpEnabled; //get internet time. Only required if you use clock overlays or time-activated macros +extern bool useAMPM; //12h/24h clock format +extern byte currentTimezone; //Timezone ID. Refer to timezones array in wled_ntp.cpp +extern int utcOffsetSecs; //Seconds to offset from UTC before timzone calculation + +extern byte overlayDefault; //0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie +extern byte overlayMin; //boundaries of overlay mode +extern byte overlayMax; + +extern byte analogClock12pixel; //The pixel in your strip where "midnight" would be +extern bool analogClockSecondsTrail; //Display seconds as trail of LEDs instead of a single pixel +extern bool analogClock5MinuteMarks; //Light pixels at every 5-minute position + +extern char cronixieDisplay[7]; //Cronixie Display mask. See wled13_cronixie.ino +extern bool cronixieBacklight; //Allow digits to be back-illuminated + +extern bool countdownMode; //Clock will count down towards date +extern byte countdownYear, countdownMonth; //Countdown target date, year is last two digits +extern byte countdownDay, countdownHour; +extern byte countdownMin, countdownSec; + +extern byte macroBoot; //macro loaded after startup +extern byte macroNl; //after nightlight delay over +extern byte macroCountdown; +extern byte macroAlexaOn, macroAlexaOff; +extern byte macroButton, macroLongPress, macroDoublePress; + +//Security CONFIG +extern bool otaLock; //prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks +extern bool wifiLock; //prevents access to WiFi settings when OTA lock is enabled +extern bool aOtaEnabled; //ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on + +extern uint16_t userVar0, userVar1; + +#ifdef WLED_ENABLE_DMX +//dmx CONFIG +extern byte DMXChannels; // number of channels per fixture +extern byte DMXFixtureMap[15]; +extern // assigns the different channels to different functions. See wled21_dmx.ino for more information. +extern uint16_t DMXGap; // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. +extern uint16_t DMXStart; // start address of the first fixture +#endif + +//internal global variable declarations +//wifi +extern bool apActive; +extern bool forceReconnect; +extern uint32_t lastReconnectAttempt; +extern bool interfacesInited; +extern bool wasConnected; + +//color +extern byte colOld[]; //color before transition +extern byte colT[]; //color that is currently displayed on the LEDs +extern byte colIT[]; //color that was last sent to LEDs +extern byte colSecT[]; +extern byte colSecOld[]; +extern byte colSecIT[]; + +extern byte lastRandomIndex; //used to save last random color so the new one is not the same + +//transitions +extern bool transitionActive; +extern uint16_t transitionDelayDefault; +extern uint16_t transitionDelayTemp; +extern unsigned long transitionStartTime; +extern float tperLast; //crossfade transition progress, 0.0f - 1.0f +extern bool jsonTransitionOnce; + +//nightlight +extern bool nightlightActive; +extern bool nightlightActiveOld; +extern uint32_t nightlightDelayMs; +extern uint8_t nightlightDelayMinsDefault; +extern unsigned long nightlightStartTime; +extern byte briNlT; //current nightlight brightness +extern byte colNlT[]; //current nightlight color + +extern unsigned long lastOnTime; +extern bool offMode; +extern byte bri; +extern byte briOld; +extern byte briT; +extern byte briIT; +extern byte briLast; //brightness before turned off. Used for toggle function +extern byte whiteLast; //white channel before turned off. Used for toggle function + +extern bool buttonPressedBefore; +extern bool buttonLongPressed; +extern unsigned long buttonPressedTime; +extern unsigned long buttonWaitTime; + +extern bool notifyDirectDefault; +extern bool receiveNotifications; +extern unsigned long notificationSentTime; +extern byte notificationSentCallMode; +extern bool notificationTwoRequired; + +extern byte effectCurrent; +extern byte effectSpeed; +extern byte effectIntensity; +extern byte effectPalette; + +extern bool udpConnected, udpRgbConnected; + +extern bool showWelcomePage; + +extern byte hueError; +//uint16_t hueFailCount; +extern float hueXLast, hueYLast; +extern uint16_t hueHueLast, hueCtLast; +extern byte hueSatLast, hueBriLast; +extern unsigned long hueLastRequestSent; +extern bool hueAuthRequired; +extern bool hueReceived; +extern bool hueStoreAllowed, hueNewKey; + +extern byte overlayCurrent; +extern byte overlaySpeed; +extern unsigned long overlayRefreshMs; +extern unsigned long overlayRefreshedTime; + +extern byte dP[]; +extern bool cronixieInit; + +//countdown +extern unsigned long countdownTime; +extern bool countdownOverTriggered; + +//timer +extern byte lastTimerMinute; +extern byte timerHours[]; +extern byte timerMinutes[]; +extern byte timerMacro[]; +extern byte timerWeekday[]; //weekdays to activate on +//bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity + +//blynk +extern bool blynkEnabled; + +//preset cycling +extern bool presetCyclingEnabled; +extern byte presetCycleMin, presetCycleMax; +extern uint16_t presetCycleTime; +extern unsigned long presetCycledTime; +extern byte presetCycCurr; +extern bool presetApplyBri; +extern bool saveCurrPresetCycConf; + +//realtime +extern byte realtimeMode; +extern IPAddress realtimeIP; +extern unsigned long realtimeTimeout; + +//mqtt +extern long lastMqttReconnectAttempt; +extern long lastInterfaceUpdate; +extern byte interfaceUpdateCallMode; +extern char mqttStatusTopic[40]; //this must be global because of async handlers + +#if AUXPIN >= 0 +//auxiliary debug pin +extern byte auxTime; +extern unsigned long auxStartTime; +extern bool auxActive; +#endif + +//alexa udp +extern String escapedMac; +#ifndef WLED_DISABLE_ALEXA +extern Espalexa espalexa; +extern EspalexaDevice *espalexaDevice; +#endif + +//dns server +extern DNSServer dnsServer; + +//network time +extern bool ntpConnected; +extern time_t local; +extern unsigned long ntpLastSyncTime; +extern unsigned long ntpPacketSentTime; +extern IPAddress ntpServerIP; +extern uint16_t ntpLocalPort; +#define NTP_PACKET_SIZE 48 + +//maximum number of LEDs - MAX_LEDS is coming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue +#define MAX_LEDS 1500 +#define MAX_LEDS_DMA 500 + +//string temp buffer (now stored in stack locally) +#define OMAX 2048 +extern char *obuf; +extern uint16_t olen; + +//presets +extern uint16_t savedPresets; +extern int8_t currentPreset; +extern bool isPreset; + +extern byte errorFlag; + +extern String messageHead, messageSub; +extern byte optionType; + +extern bool doReboot; //flag to initiate reboot from async handlers +extern bool doPublishMqtt; + +//server library objects +extern AsyncWebServer server; +extern AsyncClient *hueClient; +extern AsyncMqttClient *mqtt; + +//function prototypes +extern void colorFromUint32(uint32_t, bool); +extern void serveMessage(AsyncWebServerRequest *, uint16_t, String, String, byte); +extern void handleE131Packet(e131_packet_t *, IPAddress); +extern void arlsLock(uint32_t, byte); +extern void handleOverlayDraw(); + +//udp interface objects +extern WiFiUDP notifierUdp, rgbUdp; +extern WiFiUDP ntpUdp; +extern ESPAsyncE131 e131; +extern bool e131NewData; + +//led fx library object +extern WS2812FX strip; + + +#define WLED_CONNECTED (WiFi.status() == WL_CONNECTED) +#define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID, DEFAULT_CLIENT_SSID) != 0) + +//debug macros +#ifdef WLED_DEBUG +#define DEBUG_PRINT(x) Serial.print(x) +#define DEBUG_PRINTLN(x) Serial.println(x) +#define DEBUG_PRINTF(x) Serial.printf(x) +extern unsigned long debugTime; +extern int lastWifiState; +extern unsigned long wifiStateChangedTime; +extern int loops; +#else +#define DEBUG_PRINT(x) +#define DEBUG_PRINTLN(x) +#define DEBUG_PRINTF(x) +#endif + + +// TODO: Inline? +//append new c string to temp buffer efficiently +bool oappend(const char *txt); +//append new number to temp buffer efficiently +bool oappendi(int i); +int getSignalQuality(int rssi); + +class WLED +{ +public: + static WLED &instance() + { + static WLED instance; + return instance; + } + + WLED(); + + void reset(); + void loop(); + + + //boot starts here + void setup() + { + wledInit(); + } + +public: // TODO: privacy + void wledInit(); + void beginStrip(); + + void handleConnection(); + void initAP(bool resetAP = false); + void initConnection(); + void initInterfaces(); +}; +#endif // WLED_H diff --git a/wled00/wled_xml.cpp b/wled00/xml.cpp similarity index 99% rename from wled00/wled_xml.cpp rename to wled00/xml.cpp index 4f2b3f45..930ab4b0 100644 --- a/wled00/wled_xml.cpp +++ b/wled00/xml.cpp @@ -1,7 +1,7 @@ -#include "wled_xml.h" +#include "xml.h" #include "wled.h" -#include "wled_eeprom.h" -#include "wled_ntp.h" +#include "eeprom.h" +#include "ntp.h" //build XML response to HTTP /win API request diff --git a/wled00/wled_xml.h b/wled00/xml.h similarity index 92% rename from wled00/wled_xml.h rename to wled00/xml.h index 5cfb1dbb..02298295 100644 --- a/wled00/wled_xml.h +++ b/wled00/xml.h @@ -1,15 +1,15 @@ -#ifndef WLED_XML_H -#define WLED_XML_H -#include -#include - -/* - * Sending XML status files to client - */ -char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr); -char* URL_response(AsyncWebServerRequest *request); -void sappend(char stype, const char* key, int val); -void sappends(char stype, const char* key, char* val); -void getSettingsJS(byte subPage, char* dest); - +#ifndef WLED_XML_H +#define WLED_XML_H +#include +#include + +/* + * Sending XML status files to client + */ +char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr); +char* URL_response(AsyncWebServerRequest *request); +void sappend(char stype, const char* key, int val); +void sappends(char stype, const char* key, char* val); +void getSettingsJS(byte subPage, char* dest); + #endif // WLED_XML_H \ No newline at end of file From f99f13a090581ea037850b167edbe15d3d0daf98 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Sat, 28 Mar 2020 08:45:20 -0400 Subject: [PATCH 186/232] Avoid name collision. Fix wled instance access in ino. --- usermods/blynk_relay_control/wled06_usermod.ino | 2 +- usermods/stairway_wipe_basic/wled06_usermod.ino | 2 +- wled00/alexa.cpp | 2 +- wled00/button.cpp | 2 +- wled00/hue.cpp | 2 +- wled00/ir.cpp | 2 +- wled00/json.cpp | 2 +- wled00/led.cpp | 2 +- wled00/ntp.cpp | 4 +++- wled00/set.cpp | 2 +- wled00/usermod.cpp | 2 +- wled00/wled.cpp | 3 +-- wled00/wled00.ino | 6 ++---- wled00/{eeprom.cpp => wled_eeprom.cpp} | 3 ++- wled00/{eeprom.h => wled_eeprom.h} | 0 wled00/xml.cpp | 2 +- 16 files changed, 19 insertions(+), 19 deletions(-) rename wled00/{eeprom.cpp => wled_eeprom.cpp} (99%) rename wled00/{eeprom.h => wled_eeprom.h} (100%) diff --git a/usermods/blynk_relay_control/wled06_usermod.ino b/usermods/blynk_relay_control/wled06_usermod.ino index 1004b1ed..d4028ea5 100644 --- a/usermods/blynk_relay_control/wled06_usermod.ino +++ b/usermods/blynk_relay_control/wled06_usermod.ino @@ -1,7 +1,7 @@ /* * This file allows you to add own functionality to WLED more easily * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality - * EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in wled01_eeprom.h) + * EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in wled_eeprom.h) * bytes 2400+ are currently ununsed, but might be used for future wled features */ diff --git a/usermods/stairway_wipe_basic/wled06_usermod.ino b/usermods/stairway_wipe_basic/wled06_usermod.ino index 3d493cc1..0cc85df7 100644 --- a/usermods/stairway_wipe_basic/wled06_usermod.ino +++ b/usermods/stairway_wipe_basic/wled06_usermod.ino @@ -1,7 +1,7 @@ /* * This file allows you to add own functionality to WLED more easily * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality - * EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in wled01_eeprom.h) + * EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in wled_eeprom.h) * bytes 2400+ are currently ununsed, but might be used for future wled features */ diff --git a/wled00/alexa.cpp b/wled00/alexa.cpp index 621480d0..e35f5b35 100644 --- a/wled00/alexa.cpp +++ b/wled00/alexa.cpp @@ -2,7 +2,7 @@ #include "wled.h" #include "const.h" #include "led.h" -#include "eeprom.h" +#include "wled_eeprom.h" #include "colors.h" #ifndef WLED_DISABLE_ALEXA diff --git a/wled00/button.cpp b/wled00/button.cpp index 1afb54d5..a80061ee 100644 --- a/wled00/button.cpp +++ b/wled00/button.cpp @@ -1,7 +1,7 @@ #include "button.h" #include "wled.h" #include "led.h" -#include "eeprom.h" +#include "wled_eeprom.h" #include "set.h" /* diff --git a/wled00/hue.cpp b/wled00/hue.cpp index 484f2bd8..a186a314 100644 --- a/wled00/hue.cpp +++ b/wled00/hue.cpp @@ -1,7 +1,7 @@ #include "hue.h" #include "wled.h" #include "colors.h" -#include "eeprom.h" +#include "wled_eeprom.h" #include "notify.h" #include "led.h" diff --git a/wled00/ir.cpp b/wled00/ir.cpp index fcc0f5dd..32b0ee93 100644 --- a/wled00/ir.cpp +++ b/wled00/ir.cpp @@ -2,7 +2,7 @@ #include "wled.h" #include "led.h" #include "colors.h" -#include "eeprom.h" +#include "wled_eeprom.h" #if defined(WLED_DISABLE_INFRARED) void handleIR(){} diff --git a/wled00/json.cpp b/wled00/json.cpp index 73659e7e..6d71e414 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -1,6 +1,6 @@ #include "json.h" #include "wled.h" -#include "eeprom.h" +#include "wled_eeprom.h" #include "led.h" void deserializeSegment(JsonObject elem, byte it) diff --git a/wled00/led.cpp b/wled00/led.cpp index 4a16dcff..7f4d6309 100644 --- a/wled00/led.cpp +++ b/wled00/led.cpp @@ -2,7 +2,7 @@ #include "wled.h" #include "notify.h" #include "blynk.h" -#include "eeprom.h" +#include "wled_eeprom.h" #include "mqtt.h" #include "colors.h" diff --git a/wled00/ntp.cpp b/wled00/ntp.cpp index 3435760b..20663109 100644 --- a/wled00/ntp.cpp +++ b/wled00/ntp.cpp @@ -1,6 +1,8 @@ #include "ntp.h" +#include "src/dependencies/timezone/Timezone.h" #include "wled.h" -#include "eeprom.h" +#include "wled_eeprom.h" + TimeChangeRule UTCr = {Last, Sun, Mar, 1, 0}; // UTC Timezone tzUTC(UTCr, UTCr); diff --git a/wled00/set.cpp b/wled00/set.cpp index 1dfeb19d..0a7a4ff4 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -4,7 +4,7 @@ #include "hue.h" #include "led.h" #include "blynk.h" -#include "eeprom.h" +#include "wled_eeprom.h" #include "alexa.h" #include "cronixie.h" #include "xml.h" diff --git a/wled00/usermod.cpp b/wled00/usermod.cpp index c780ffee..6589c07c 100644 --- a/wled00/usermod.cpp +++ b/wled00/usermod.cpp @@ -2,7 +2,7 @@ /* * This file allows you to add own functionality to WLED more easily * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality - * EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in wled01_eeprom.h) + * EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in wled01_wled_eeprom.h) * bytes 2400+ are currently ununsed, but might be used for future wled features */ diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 106f503b..2d474eb3 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -12,9 +12,8 @@ #include "blynk.h" #include "hue.h" #include "mqtt.h" -#include "eeprom.h" +#include "wled_eeprom.h" #include "server.h" -// replaced // Global Variable definitions char versionString[] = "0.9.1"; diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 522447d0..e3334837 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -3,12 +3,10 @@ */ #include "wled.h" -WLED& wled; void setup() { - wled = WLED::instance(); - wled.setup(); + WLED::instance().setup(); } void loop() { - wled.loop(); + WLED::instance().loop(); } \ No newline at end of file diff --git a/wled00/eeprom.cpp b/wled00/wled_eeprom.cpp similarity index 99% rename from wled00/eeprom.cpp rename to wled00/wled_eeprom.cpp index 68b8c35f..0087641a 100644 --- a/wled00/eeprom.cpp +++ b/wled00/wled_eeprom.cpp @@ -1,4 +1,5 @@ -#include "eeprom.h" +#include "wled_eeprom.h" +#include #include "wled.h" #include "cronixie.h" #include "ntp.h" diff --git a/wled00/eeprom.h b/wled00/wled_eeprom.h similarity index 100% rename from wled00/eeprom.h rename to wled00/wled_eeprom.h diff --git a/wled00/xml.cpp b/wled00/xml.cpp index 930ab4b0..09e42011 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -1,6 +1,6 @@ #include "xml.h" #include "wled.h" -#include "eeprom.h" +#include "wled_eeprom.h" #include "ntp.h" From cc2de04f6b3c912291bbf57d15a06f40ccf99620 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Sat, 28 Mar 2020 09:24:07 -0400 Subject: [PATCH 187/232] Avoid name collision. --- wled00/wled.cpp | 2 +- wled00/{server.cpp => wled_server.cpp} | 2 +- wled00/{server.h => wled_server.h} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename wled00/{server.cpp => wled_server.cpp} (99%) rename wled00/{server.h => wled_server.h} (100%) diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 2d474eb3..4945ffb7 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -13,7 +13,7 @@ #include "hue.h" #include "mqtt.h" #include "wled_eeprom.h" -#include "server.h" +#include "wled_server.h" // Global Variable definitions char versionString[] = "0.9.1"; diff --git a/wled00/server.cpp b/wled00/wled_server.cpp similarity index 99% rename from wled00/server.cpp rename to wled00/wled_server.cpp index 3249466c..d5b4eebc 100644 --- a/wled00/server.cpp +++ b/wled00/wled_server.cpp @@ -1,4 +1,4 @@ -#include "server.h" +#include "wled_server.h" #include "wled.h" #include "file.h" #include "set.h" diff --git a/wled00/server.h b/wled00/wled_server.h similarity index 100% rename from wled00/server.h rename to wled00/wled_server.h From 8264ca4e9e283f71a95ea5c1e84e03d9d1717d49 Mon Sep 17 00:00:00 2001 From: Aircoookie Date: Sat, 28 Mar 2020 21:30:32 +0100 Subject: [PATCH 188/232] Create stale.yml (to reduce older open issues) --- .github/stale.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 00000000..811db619 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,20 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 120 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - keep + - enhancement + - confirmed +# Label to use when marking an issue as stale +staleLabel: stale +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + Hey! This issue has been open for quite some time without any new comments now. + It will be closed automatically in a week if no further activity occurs. + + Thank you for using WLED! +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false From d2f55e1064a1188f0b831979c4e9ddde96c29a53 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Mon, 30 Mar 2020 03:55:44 -0400 Subject: [PATCH 189/232] Make dmx.h header only. --- wled00/dmx.cpp | 52 ----------------------------------------------- wled00/dmx.h | 55 +++++++++++++++++++++++++++++++++++++++++++++++++- wled00/wled.h | 4 ---- 3 files changed, 54 insertions(+), 57 deletions(-) delete mode 100644 wled00/dmx.cpp diff --git a/wled00/dmx.cpp b/wled00/dmx.cpp deleted file mode 100644 index d024ac71..00000000 --- a/wled00/dmx.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "dmx.h" -#include "wled.h" - -#ifdef WLED_ENABLE_DMX -void handleDMX() { - // TODO: calculate brightness manually if no shutter channel is set - - uint8_t brightness = strip.getBrightness(); - - for (int i = 0; i < ledCount; i++) { // uses the amount of LEDs as fixture count - - uint32_t in = strip.getPixelColor(i); // time to get the colors for the individual fixtures as suggested by AirCookie at issue #462 - byte w = in >> 24 & 0xFF; - byte r = in >> 16 & 0xFF; - byte g = in >> 8 & 0xFF; - byte b = in & 0xFF; - - int DMXFixtureStart = DMXStart + (DMXGap * i); - for (int j = 0; j < DMXChannels; j++) { - int DMXAddr = DMXFixtureStart + j; - switch (DMXFixtureMap[j]) { - case 0: // Set this channel to 0. Good way to tell strobe- and fade-functions to fuck right off. - dmx.write(DMXAddr, 0); - break; - case 1: // Red - dmx.write(DMXAddr, r); - break; - case 2: // Green - dmx.write(DMXAddr, g); - break; - case 3: // Blue - dmx.write(DMXAddr, b); - break; - case 4: // White - dmx.write(DMXAddr, w); - break; - case 5: // Shutter channel. Controls the brightness. - dmx.write(DMXAddr, brightness); - break; - case 6:// Sets this channel to 255. Like 0, but more wholesome. - dmx.write(DMXAddr, 255); - break; - } - } - } - - dmx.update(); // update the DMX bus -} - -#else -void handleDMX() {} -#endif diff --git a/wled00/dmx.h b/wled00/dmx.h index 707dec91..76aab484 100644 --- a/wled00/dmx.h +++ b/wled00/dmx.h @@ -1,11 +1,64 @@ #ifndef WLED_DMX_H #define WLED_DMX_H +#include "wled.h" /* * Support for DMX via MAX485. * Needs the espdmx library. You might have to change the output pin within the library. Sketchy, i know. * https://github.com/Rickgg/ESP-Dmx */ -void handleDMX(); +#ifdef WLED_ENABLE_DMX +#include +DMXESPSerial dmx; + +#ifdef WLED_ENABLE_DMX +void handleDMX() { + // TODO: calculate brightness manually if no shutter channel is set + + uint8_t brightness = strip.getBrightness(); + + for (int i = 0; i < ledCount; i++) { // uses the amount of LEDs as fixture count + + uint32_t in = strip.getPixelColor(i); // time to get the colors for the individual fixtures as suggested by AirCookie at issue #462 + byte w = in >> 24 & 0xFF; + byte r = in >> 16 & 0xFF; + byte g = in >> 8 & 0xFF; + byte b = in & 0xFF; + + int DMXFixtureStart = DMXStart + (DMXGap * i); + for (int j = 0; j < DMXChannels; j++) { + int DMXAddr = DMXFixtureStart + j; + switch (DMXFixtureMap[j]) { + case 0: // Set this channel to 0. Good way to tell strobe- and fade-functions to fuck right off. + dmx.write(DMXAddr, 0); + break; + case 1: // Red + dmx.write(DMXAddr, r); + break; + case 2: // Green + dmx.write(DMXAddr, g); + break; + case 3: // Blue + dmx.write(DMXAddr, b); + break; + case 4: // White + dmx.write(DMXAddr, w); + break; + case 5: // Shutter channel. Controls the brightness. + dmx.write(DMXAddr, brightness); + break; + case 6:// Sets this channel to 255. Like 0, but more wholesome. + dmx.write(DMXAddr, 255); + break; + } + } + } + + dmx.update(); // update the DMX bus +} + +#else +void handleDMX() {} +#endif #endif //WLED_DMX_H \ No newline at end of file diff --git a/wled00/wled.h b/wled00/wled.h index c6f791cd..e5c4f493 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -37,10 +37,6 @@ //library inclusions #include -#ifdef WLED_ENABLE_DMX -#include -DMXESPSerial dmx; -#endif #ifdef ESP8266 #include #include From 3e1eb02f54b674cbe74064f81b4eca987394e97f Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Mon, 30 Mar 2020 04:21:47 -0400 Subject: [PATCH 190/232] Comment cleanup and line reduction. --- wled00/wled.h | 345 ++++++++++++++++++-------------------------------- 1 file changed, 124 insertions(+), 221 deletions(-) diff --git a/wled00/wled.h b/wled00/wled.h index e5c4f493..9e373216 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -1,41 +1,39 @@ #ifndef WLED_H #define WLED_H - /* Main sketch, global variable declarations -*/ -/* - * @title WLED project sketch - * @version 0.9.1 - * @author Christian Schwinne + @title WLED project sketch + @version 0.9.1 + @author Christian Schwinne */ -//ESP8266-01 (blue) got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.4.2 and the setting 512K(No SPIFFS). +// ESP8266-01 (blue) got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.4.2 and the setting 512K(No SPIFFS). -//ESP8266-01 (black) has 1MB flash and can thus fit the whole program. Use 1M(64K SPIFFS). -//Uncomment some of the following lines to disable features to compile for ESP8266-01 (max flash size 434kB): +// ESP8266-01 (black) has 1MB flash and can thus fit the whole program. Use 1M(64K SPIFFS). +// Uncomment some of the following lines to disable features to compile for ESP8266-01 (max flash size 434kB): +// Alternatively, with platformio pass your chosen flags to your custom build target in platformio.ini.override -//You are required to disable over-the-air updates: +// You are required to disable over-the-air updates: //#define WLED_DISABLE_OTA //saves 14kb -//You need to choose some of these features to disable: +// You need to choose some of these features to disable: //#define WLED_DISABLE_ALEXA //saves 11kb //#define WLED_DISABLE_BLYNK //saves 6kb //#define WLED_DISABLE_CRONIXIE //saves 3kb //#define WLED_DISABLE_HUESYNC //saves 4kb //#define WLED_DISABLE_INFRARED //there is no pin left for this on ESP8266-01, saves 12kb -#define WLED_ENABLE_MQTT //saves 12kb -#define WLED_ENABLE_ADALIGHT //saves 500b only +#define WLED_ENABLE_MQTT //saves 12kb +#define WLED_ENABLE_ADALIGHT //saves 500b only //#define WLED_ENABLE_DMX //uses 3.5kb #define WLED_DISABLE_FILESYSTEM //SPIFFS is not used by any WLED feature yet //#define WLED_ENABLE_FS_SERVING //Enable sending html file from SPIFFS before serving progmem version //#define WLED_ENABLE_FS_EDITOR //enable /edit page for editing SPIFFS content. Will also be disabled with OTA lock -//to toggle usb serial debug (un)comment the following line +// to toggle usb serial debug (un)comment the following line //#define WLED_DEBUG -//library inclusions +// Library inclusions. #include #ifdef ESP8266 #include @@ -116,225 +114,171 @@ extern "C" #endif #endif -//version code in format yymmddb (b = daily build) +// version code in format yymmddb (b = daily build) #define VERSION 2003222 +// Global external variable declaration. See wled.cpp for definitions and comments. extern char versionString[]; - -//AP and OTA default passwords (for maximum change them!) extern char apPass[65]; extern char otaPass[33]; - -//Hardware CONFIG (only changeble HERE, not at runtime) -//LED strip pin, button pin and IR pin changeable in NpbWrapper.h! - -extern byte auxDefaultState; //0: input 1: high 2: low -extern byte auxTriggeredState; //0: input 1: high 2: low -extern char ntpServerName[33]; //NTP server to use - -//WiFi CONFIG (all these can be changed via web UI, no need to set them here) +extern byte auxDefaultState; +extern byte auxTriggeredState; +extern char ntpServerName[33]; extern char clientSSID[33]; extern char clientPass[65]; -extern char cmDNS[33]; //mDNS address (placeholder, will be replaced by wledXXXXXXXXXXXX.local) -extern char apSSID[33]; //AP off by default (unless setup) -extern byte apChannel; //2.4GHz WiFi AP channel (1-13) -extern byte apHide; //hidden AP SSID -extern byte apBehavior; //access point opens when no connection after boot by default -extern IPAddress staticIP; //static IP of ESP -extern IPAddress staticGateway; //gateway (router) IP -extern IPAddress staticSubnet; //most common subnet in home networks -extern bool noWifiSleep; //disabling modem sleep modes will increase heat output and power usage, but may help with connection issues - -//LED CONFIG -extern uint16_t ledCount; //overcurrent prevented by ABL -extern bool useRGBW; //SK6812 strips can contain an extra White channel -#define ABL_MILLIAMPS_DEFAULT 850; //auto lower brightness to stay close to milliampere limit -extern bool turnOnAtBoot; //turn on LEDs at power-up -extern byte bootPreset; //save preset to load after power-up - -extern byte col[]; //current RGB(W) primary color. col[] should be updated if you want to change the color. -extern byte colSec[]; //current RGB(W) secondary color -extern byte briS; //default brightness - -extern byte nightlightTargetBri; //brightness after nightlight is over +extern char cmDNS[33]; +extern char apSSID[33]; +extern byte apChannel; +extern byte apHide; +extern byte apBehavior; +extern IPAddress staticIP; +extern IPAddress staticGateway; +extern IPAddress staticSubnet; +extern bool noWifiSleep; +extern uint16_t ledCount; +extern bool useRGBW; +#define ABL_MILLIAMPS_DEFAULT 850; +extern bool turnOnAtBoot; +extern byte bootPreset; +extern byte col[]; +extern byte colSec[]; +extern byte briS; +extern byte nightlightTargetBri; extern byte nightlightDelayMins; -extern bool nightlightFade; //if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over -extern bool nightlightColorFade; //if enabled, light will gradually fade color from primary to secondary color. -extern bool fadeTransition; //enable crossfading color transition -extern uint16_t transitionDelay; //default crossfade duration in ms - -extern bool skipFirstLed; //ignore first LED in strip (useful if you need the LED as signal repeater) -extern byte briMultiplier; //% of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) - -//User Interface CONFIG -extern char serverDescription[33]; //Name of module -extern bool syncToggleReceive; //UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise - -//Sync CONFIG +extern bool nightlightFade; +extern bool nightlightColorFade; +extern bool fadeTransition; +extern uint16_t transitionDelay; +extern bool skipFirstLed; +extern byte briMultiplier; +extern char serverDescription[33]; +extern bool syncToggleReceive; extern bool buttonEnabled; -extern byte irEnabled; //Infrared receiver - -extern uint16_t udpPort; //WLED notifier default port -extern uint16_t udpRgbPort; //Hyperion port - -extern bool receiveNotificationBrightness; //apply brightness from incoming notifications -extern bool receiveNotificationColor; //apply color -extern bool receiveNotificationEffects; //apply effects setup -extern bool notifyDirect; //send notification if change via UI or HTTP API -extern bool notifyButton; //send if updated by button or infrared remote -extern bool notifyAlexa; //send notification if updated via Alexa -extern bool notifyMacro; //send notification for macro -extern bool notifyHue; //send notification if Hue light changes -extern bool notifyTwice; //notifications use UDP: enable if devices don't sync reliably - -extern bool alexaEnabled; //enable device discovery by Amazon Echo -extern char alexaInvocationName[33]; //speech control name of device. Choose something voice-to-text can understand - -extern char blynkApiKey[36]; //Auth token for Blynk server. If empty, no connection will be made - -extern uint16_t realtimeTimeoutMs; //ms timeout of realtime mode before returning to normal mode -extern int arlsOffset; //realtime LED offset -extern bool receiveDirect; //receive UDP realtime -extern bool arlsDisableGammaCorrection; //activate if gamma correction is handled by the source -extern bool arlsForceMaxBri; //enable to force max brightness if source has very dark colors that would be black - +extern byte irEnabled; +extern uint16_t udpPort; +extern uint16_t udpRgbPort; +extern bool receiveNotificationBrightness; +extern bool receiveNotificationColor; +extern bool receiveNotificationEffects; +extern bool notifyDirect; +extern bool notifyButton; +extern bool notifyAlexa; +extern bool notifyMacro; +extern bool notifyHue; +extern bool notifyTwice; +extern bool alexaEnabled; +extern char alexaInvocationName[33]; +extern char blynkApiKey[36]; +extern uint16_t realtimeTimeoutMs; +extern int arlsOffset; +extern bool receiveDirect; +extern bool arlsDisableGammaCorrection; +extern bool arlsForceMaxBri; #define E131_MAX_UNIVERSE_COUNT 9 -extern uint16_t e131Universe; //settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) -extern uint8_t DMXMode; //DMX mode (s.a.) -extern uint16_t DMXAddress; //DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] -extern uint8_t DMXOldDimmer; //only update brightness on change -extern uint8_t e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; //to detect packet loss -extern bool e131Multicast; //multicast or unicast -extern bool e131SkipOutOfSequence; //freeze instead of flickering - +extern uint16_t e131Universe; +extern uint8_t DMXMode; +extern uint16_t DMXAddress; +extern uint8_t DMXOldDimmer; +extern uint8_t e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; +extern bool e131Multicast; +extern bool e131SkipOutOfSequence; extern bool mqttEnabled; -extern char mqttDeviceTopic[33]; //main MQTT topic (individual per device, default is wled/mac) -extern char mqttGroupTopic[33]; //second MQTT topic (for example to group devices) -extern char mqttServer[33]; //both domains and IPs should work (no SSL) -extern char mqttUser[41]; //optional: username for MQTT auth -extern char mqttPass[41]; //optional: password for MQTT auth -extern char mqttClientID[41]; //override the client ID +extern char mqttDeviceTopic[33]; +extern char mqttGroupTopic[33]; +extern char mqttServer[33]; +extern char mqttUser[41]; +extern char mqttPass[41]; +extern char mqttClientID[41]; extern uint16_t mqttPort; - -extern bool huePollingEnabled; //poll hue bridge for light state -extern uint16_t huePollIntervalMs; //low values (< 1sec) may cause lag but offer quicker response -extern char hueApiKey[47]; //key token will be obtained from bridge -extern byte huePollLightId; //ID of hue lamp to sync to. Find the ID in the hue app ("about" section) -extern IPAddress hueIP; //IP address of the bridge +extern bool huePollingEnabled; +extern uint16_t huePollIntervalMs; +extern char hueApiKey[47]; +extern byte huePollLightId; +extern IPAddress hueIP; extern bool hueApplyOnOff; extern bool hueApplyBri; extern bool hueApplyColor; - -//Time CONFIG -extern bool ntpEnabled; //get internet time. Only required if you use clock overlays or time-activated macros -extern bool useAMPM; //12h/24h clock format -extern byte currentTimezone; //Timezone ID. Refer to timezones array in wled_ntp.cpp -extern int utcOffsetSecs; //Seconds to offset from UTC before timzone calculation - -extern byte overlayDefault; //0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie -extern byte overlayMin; //boundaries of overlay mode +extern bool ntpEnabled; +extern bool useAMPM; +extern byte currentTimezone; +extern int utcOffsetSecs; +extern byte overlayDefault; +extern byte overlayMin; extern byte overlayMax; - -extern byte analogClock12pixel; //The pixel in your strip where "midnight" would be -extern bool analogClockSecondsTrail; //Display seconds as trail of LEDs instead of a single pixel -extern bool analogClock5MinuteMarks; //Light pixels at every 5-minute position - -extern char cronixieDisplay[7]; //Cronixie Display mask. See wled13_cronixie.ino -extern bool cronixieBacklight; //Allow digits to be back-illuminated - -extern bool countdownMode; //Clock will count down towards date -extern byte countdownYear, countdownMonth; //Countdown target date, year is last two digits +extern byte analogClock12pixel; +extern bool analogClockSecondsTrail; +extern bool analogClock5MinuteMarks; +extern char cronixieDisplay[7]; +extern bool cronixieBacklight; +extern bool countdownMode; +extern byte countdownYear, countdownMonth; extern byte countdownDay, countdownHour; extern byte countdownMin, countdownSec; - -extern byte macroBoot; //macro loaded after startup -extern byte macroNl; //after nightlight delay over +extern byte macroBoot; +extern byte macroNl; extern byte macroCountdown; extern byte macroAlexaOn, macroAlexaOff; extern byte macroButton, macroLongPress, macroDoublePress; - -//Security CONFIG -extern bool otaLock; //prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks -extern bool wifiLock; //prevents access to WiFi settings when OTA lock is enabled -extern bool aOtaEnabled; //ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on - +extern bool otaLock; +extern bool wifiLock; +extern bool aOtaEnabled; extern uint16_t userVar0, userVar1; - #ifdef WLED_ENABLE_DMX -//dmx CONFIG -extern byte DMXChannels; // number of channels per fixture +extern byte DMXChannels; extern byte DMXFixtureMap[15]; -extern // assigns the different channels to different functions. See wled21_dmx.ino for more information. -extern uint16_t DMXGap; // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. -extern uint16_t DMXStart; // start address of the first fixture +extern +extern uint16_t DMXGap; +extern uint16_t DMXStart; #endif - -//internal global variable declarations -//wifi extern bool apActive; extern bool forceReconnect; extern uint32_t lastReconnectAttempt; extern bool interfacesInited; extern bool wasConnected; - -//color -extern byte colOld[]; //color before transition -extern byte colT[]; //color that is currently displayed on the LEDs -extern byte colIT[]; //color that was last sent to LEDs +extern byte colOld[]; +extern byte colT[]; +extern byte colIT[]; extern byte colSecT[]; extern byte colSecOld[]; extern byte colSecIT[]; - -extern byte lastRandomIndex; //used to save last random color so the new one is not the same - -//transitions +extern byte lastRandomIndex; extern bool transitionActive; extern uint16_t transitionDelayDefault; extern uint16_t transitionDelayTemp; extern unsigned long transitionStartTime; -extern float tperLast; //crossfade transition progress, 0.0f - 1.0f +extern float tperLast; extern bool jsonTransitionOnce; - -//nightlight extern bool nightlightActive; extern bool nightlightActiveOld; extern uint32_t nightlightDelayMs; extern uint8_t nightlightDelayMinsDefault; extern unsigned long nightlightStartTime; -extern byte briNlT; //current nightlight brightness -extern byte colNlT[]; //current nightlight color - +extern byte briNlT; +extern byte colNlT[]; extern unsigned long lastOnTime; extern bool offMode; extern byte bri; extern byte briOld; extern byte briT; extern byte briIT; -extern byte briLast; //brightness before turned off. Used for toggle function -extern byte whiteLast; //white channel before turned off. Used for toggle function - +extern byte briLast; +extern byte whiteLast; extern bool buttonPressedBefore; extern bool buttonLongPressed; extern unsigned long buttonPressedTime; extern unsigned long buttonWaitTime; - extern bool notifyDirectDefault; extern bool receiveNotifications; extern unsigned long notificationSentTime; extern byte notificationSentCallMode; extern bool notificationTwoRequired; - extern byte effectCurrent; extern byte effectSpeed; extern byte effectIntensity; extern byte effectPalette; - extern bool udpConnected, udpRgbConnected; - extern bool showWelcomePage; - extern byte hueError; -//uint16_t hueFailCount; extern float hueXLast, hueYLast; extern uint16_t hueHueLast, hueCtLast; extern byte hueSatLast, hueBriLast; @@ -342,31 +286,20 @@ extern unsigned long hueLastRequestSent; extern bool hueAuthRequired; extern bool hueReceived; extern bool hueStoreAllowed, hueNewKey; - extern byte overlayCurrent; extern byte overlaySpeed; extern unsigned long overlayRefreshMs; extern unsigned long overlayRefreshedTime; - extern byte dP[]; extern bool cronixieInit; - -//countdown extern unsigned long countdownTime; extern bool countdownOverTriggered; - -//timer extern byte lastTimerMinute; extern byte timerHours[]; extern byte timerMinutes[]; extern byte timerMacro[]; -extern byte timerWeekday[]; //weekdays to activate on -//bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity - -//blynk +extern byte timerWeekday[]; extern bool blynkEnabled; - -//preset cycling extern bool presetCyclingEnabled; extern byte presetCycleMin, presetCycleMax; extern uint16_t presetCycleTime; @@ -374,36 +307,24 @@ extern unsigned long presetCycledTime; extern byte presetCycCurr; extern bool presetApplyBri; extern bool saveCurrPresetCycConf; - -//realtime extern byte realtimeMode; extern IPAddress realtimeIP; extern unsigned long realtimeTimeout; - -//mqtt extern long lastMqttReconnectAttempt; extern long lastInterfaceUpdate; extern byte interfaceUpdateCallMode; -extern char mqttStatusTopic[40]; //this must be global because of async handlers - +extern char mqttStatusTopic[40]; #if AUXPIN >= 0 -//auxiliary debug pin extern byte auxTime; extern unsigned long auxStartTime; extern bool auxActive; #endif - -//alexa udp extern String escapedMac; #ifndef WLED_DISABLE_ALEXA extern Espalexa espalexa; extern EspalexaDevice *espalexaDevice; #endif - -//dns server extern DNSServer dnsServer; - -//network time extern bool ntpConnected; extern time_t local; extern unsigned long ntpLastSyncTime; @@ -411,51 +332,35 @@ extern unsigned long ntpPacketSentTime; extern IPAddress ntpServerIP; extern uint16_t ntpLocalPort; #define NTP_PACKET_SIZE 48 - -//maximum number of LEDs - MAX_LEDS is coming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue #define MAX_LEDS 1500 #define MAX_LEDS_DMA 500 - -//string temp buffer (now stored in stack locally) #define OMAX 2048 extern char *obuf; extern uint16_t olen; - -//presets extern uint16_t savedPresets; extern int8_t currentPreset; extern bool isPreset; - extern byte errorFlag; - extern String messageHead, messageSub; extern byte optionType; - -extern bool doReboot; //flag to initiate reboot from async handlers +extern bool doReboot; extern bool doPublishMqtt; - -//server library objects extern AsyncWebServer server; extern AsyncClient *hueClient; extern AsyncMqttClient *mqtt; +extern WiFiUDP notifierUdp, rgbUdp; +extern WiFiUDP ntpUdp; +extern ESPAsyncE131 e131; +extern bool e131NewData; +extern WS2812FX strip; -//function prototypes +// Function prototypes extern void colorFromUint32(uint32_t, bool); extern void serveMessage(AsyncWebServerRequest *, uint16_t, String, String, byte); extern void handleE131Packet(e131_packet_t *, IPAddress); extern void arlsLock(uint32_t, byte); extern void handleOverlayDraw(); -//udp interface objects -extern WiFiUDP notifierUdp, rgbUdp; -extern WiFiUDP ntpUdp; -extern ESPAsyncE131 e131; -extern bool e131NewData; - -//led fx library object -extern WS2812FX strip; - - #define WLED_CONNECTED (WiFi.status() == WL_CONNECTED) #define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID, DEFAULT_CLIENT_SSID) != 0) @@ -474,11 +379,9 @@ extern int loops; #define DEBUG_PRINTF(x) #endif - -// TODO: Inline? -//append new c string to temp buffer efficiently +// append new c string to temp buffer efficiently bool oappend(const char *txt); -//append new number to temp buffer efficiently +// append new number to temp buffer efficiently bool oappendi(int i); int getSignalQuality(int rssi); From 408d63825a5176f370fd04b6ea4662c6a3d27286 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Mon, 30 Mar 2020 04:26:41 -0400 Subject: [PATCH 191/232] Function prototype cleanup. --- wled00/set.cpp | 1 + wled00/wled.cpp | 8 +------- wled00/wled.h | 7 ------- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/wled00/set.cpp b/wled00/set.cpp index 0a7a4ff4..f26795c8 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -8,6 +8,7 @@ #include "alexa.h" #include "cronixie.h" #include "xml.h" +#include "wled_server.h" void _setRandomColor(bool _sec,bool fromButton) { diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 4945ffb7..c6dac0d2 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -14,6 +14,7 @@ #include "mqtt.h" #include "wled_eeprom.h" #include "wled_server.h" + // Global Variable definitions char versionString[] = "0.9.1"; @@ -343,13 +344,6 @@ AsyncWebServer server(80); AsyncClient *hueClient = NULL; AsyncMqttClient *mqtt = NULL; -//function prototypes -void colorFromUint32(uint32_t, bool = false); -void serveMessage(AsyncWebServerRequest *, uint16_t, String, String, byte); -void handleE131Packet(e131_packet_t *, IPAddress); -void arlsLock(uint32_t, byte); -void handleOverlayDraw(); - //udp interface objects WiFiUDP notifierUdp, rgbUdp; WiFiUDP ntpUdp; diff --git a/wled00/wled.h b/wled00/wled.h index 9e373216..e825668e 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -354,13 +354,6 @@ extern ESPAsyncE131 e131; extern bool e131NewData; extern WS2812FX strip; -// Function prototypes -extern void colorFromUint32(uint32_t, bool); -extern void serveMessage(AsyncWebServerRequest *, uint16_t, String, String, byte); -extern void handleE131Packet(e131_packet_t *, IPAddress); -extern void arlsLock(uint32_t, byte); -extern void handleOverlayDraw(); - #define WLED_CONNECTED (WiFi.status() == WL_CONNECTED) #define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID, DEFAULT_CLIENT_SSID) != 0) From de63bdac3951707f781691afd788d8e8a4319bc3 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Mon, 30 Mar 2020 04:35:52 -0400 Subject: [PATCH 192/232] Code reorg. --- wled00/wled.cpp | 11 +---------- wled00/wled.h | 11 +++-------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/wled00/wled.cpp b/wled00/wled.cpp index c6dac0d2..6edd58e2 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -356,23 +356,14 @@ WS2812FX strip = WS2812FX(); #define WLED_CONNECTED (WiFi.status() == WL_CONNECTED) #define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID, DEFAULT_CLIENT_SSID) != 0) -//debug macros +//debug macro variable definitions #ifdef WLED_DEBUG -#define DEBUG_PRINT(x) Serial.print(x) -#define DEBUG_PRINTLN(x) Serial.println(x) -#define DEBUG_PRINTF(x) Serial.printf(x) unsigned long debugTime = 0; int lastWifiState = 3; unsigned long wifiStateChangedTime = 0; int loops = 0; -#else -#define DEBUG_PRINT(x) -#define DEBUG_PRINTLN(x) -#define DEBUG_PRINTF(x) #endif - - WLED::WLED() { } diff --git a/wled00/wled.h b/wled00/wled.h index e825668e..abcffac1 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -381,28 +381,23 @@ int getSignalQuality(int rssi); class WLED { public: + WLED(); static WLED &instance() { static WLED instance; return instance; } - WLED(); - - void reset(); - void loop(); - - //boot starts here void setup() { wledInit(); } + void loop(); + void reset(); -public: // TODO: privacy void wledInit(); void beginStrip(); - void handleConnection(); void initAP(bool resetAP = false); void initConnection(); From 40ac760285425a6c1d7f6c2b8c9d0bee49f7e342 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Mon, 30 Mar 2020 04:41:42 -0400 Subject: [PATCH 193/232] Move getSignalQuality to json.cpp --- wled00/json.cpp | 20 ++++++++++++++++++++ wled00/wled.cpp | 20 -------------------- wled00/wled.h | 1 - 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/wled00/json.cpp b/wled00/json.cpp index 6d71e414..782dc8f6 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -249,6 +249,26 @@ void serializeState(JsonObject root) } } +//by https://github.com/tzapu/WiFiManager/blob/master/WiFiManager.cpp +int getSignalQuality(int rssi) +{ + int quality = 0; + + if (rssi <= -100) + { + quality = 0; + } + else if (rssi >= -50) + { + quality = 100; + } + else + { + quality = 2 * (rssi + 100); + } + return quality; +} + void serializeInfo(JsonObject root) { root["ver"] = versionString; diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 6edd58e2..18bc4fca 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -845,23 +845,3 @@ void WLED::handleConnection() } } } - -//by https://github.com/tzapu/WiFiManager/blob/master/WiFiManager.cpp -int getSignalQuality(int rssi) -{ - int quality = 0; - - if (rssi <= -100) - { - quality = 0; - } - else if (rssi >= -50) - { - quality = 100; - } - else - { - quality = 2 * (rssi + 100); - } - return quality; -} \ No newline at end of file diff --git a/wled00/wled.h b/wled00/wled.h index abcffac1..07606676 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -376,7 +376,6 @@ extern int loops; bool oappend(const char *txt); // append new number to temp buffer efficiently bool oappendi(int i); -int getSignalQuality(int rssi); class WLED { From 7e21955211282daa581ee912da9d426c7f46aba8 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Mon, 30 Mar 2020 04:43:37 -0400 Subject: [PATCH 194/232] wledInit -> setup --- wled00/wled.cpp | 2 +- wled00/wled.h | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 18bc4fca..fe3d7bd9 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -484,7 +484,7 @@ void WLED::loop() #endif // WLED_DEBU } -void WLED::wledInit() +void WLED::setup() { EEPROM.begin(EEPSIZE); ledCount = EEPROM.read(229) + ((EEPROM.read(398) << 8) & 0xFF00); diff --git a/wled00/wled.h b/wled00/wled.h index 07606676..9262c6f1 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -388,14 +388,11 @@ public: } //boot starts here - void setup() - { - wledInit(); - } + void setup(); + void loop(); void reset(); - void wledInit(); void beginStrip(); void handleConnection(); void initAP(bool resetAP = false); From c54092c932ee84ca3a385f38ceb28bbc40b83967 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Mon, 30 Mar 2020 05:04:20 -0400 Subject: [PATCH 195/232] Remove redundant defines. --- wled00/wled.cpp | 13 +------------ wled00/wled.h | 7 ++++++- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/wled00/wled.cpp b/wled00/wled.cpp index fe3d7bd9..670868e6 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -45,7 +45,6 @@ bool noWifiSleep = false; //disabling modem sleep modes will i //LED CONFIG uint16_t ledCount = 30; //overcurrent prevented by ABL bool useRGBW = false; //SK6812 strips can contain an extra White channel -#define ABL_MILLIAMPS_DEFAULT 850; //auto lower brightness to stay close to milliampere limit bool turnOnAtBoot = true; //turn on LEDs at power-up byte bootPreset = 0; //save preset to load after power-up @@ -95,7 +94,6 @@ bool receiveDirect = true; //receive UDP realtime bool arlsDisableGammaCorrection = true; //activate if gamma correction is handled by the source bool arlsForceMaxBri = false; //enable to force max brightness if source has very dark colors that would be black -#define E131_MAX_UNIVERSE_COUNT 9 uint16_t e131Universe = 1; //settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) uint8_t DMXMode = DMX_MODE_MULTIPLE_RGB; //DMX mode (s.a.) uint16_t DMXAddress = 1; //DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] @@ -315,14 +313,8 @@ unsigned long ntpLastSyncTime = 999000000L; unsigned long ntpPacketSentTime = 999000000L; IPAddress ntpServerIP; uint16_t ntpLocalPort = 2390; -#define NTP_PACKET_SIZE 48 -//maximum number of LEDs - MAX_LEDS is coming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue -#define MAX_LEDS 1500 -#define MAX_LEDS_DMA 500 - -//string temp buffer (now stored in stack locally) -#define OMAX 2048 +// Temp buffer char *obuf; uint16_t olen = 0; @@ -353,9 +345,6 @@ bool e131NewData = false; //led fx library object WS2812FX strip = WS2812FX(); -#define WLED_CONNECTED (WiFi.status() == WL_CONNECTED) -#define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID, DEFAULT_CLIENT_SSID) != 0) - //debug macro variable definitions #ifdef WLED_DEBUG unsigned long debugTime = 0; diff --git a/wled00/wled.h b/wled00/wled.h index 4eebead2..3e6c11be 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -137,7 +137,7 @@ extern IPAddress staticSubnet; extern bool noWifiSleep; extern uint16_t ledCount; extern bool useRGBW; -#define ABL_MILLIAMPS_DEFAULT 850; +#define ABL_MILLIAMPS_DEFAULT 850; //auto lower brightness to stay close to milliampere limit extern bool turnOnAtBoot; extern byte bootPreset; extern byte col[]; @@ -332,11 +332,16 @@ extern unsigned long ntpPacketSentTime; extern IPAddress ntpServerIP; extern uint16_t ntpLocalPort; #define NTP_PACKET_SIZE 48 + +//maximum number of LEDs - MAX_LEDS is coming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue #define MAX_LEDS 1500 #define MAX_LEDS_DMA 500 + +//string temp buffer (now stored in stack locally) #define OMAX 2048 extern char *obuf; extern uint16_t olen; + extern uint16_t savedPresets; extern int8_t currentPreset; extern bool isPreset; From a47d48c9735eaef7bd96d84a15c83cafc2c1e5bc Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Mon, 30 Mar 2020 05:19:09 -0400 Subject: [PATCH 196/232] Add clang format. --- .clang-format | 4 + wled00/wled.cpp | 829 +++++++++++++++++++++++------------------------- 2 files changed, 403 insertions(+), 430 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..56df2b76 --- /dev/null +++ b/.clang-format @@ -0,0 +1,4 @@ +--- + BasedOnStyle: Webkit + IndentWidth: 2 + \ No newline at end of file diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 670868e6..fd47592f 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -1,19 +1,19 @@ #include "wled.h" -#include -#include "led.h" -#include "ir.h" -#include "notify.h" #include "alexa.h" -#include "overlay.h" -#include "file.h" -#include "button.h" -#include "ntp.h" -#include "usermod.h" #include "blynk.h" +#include "button.h" +#include "file.h" #include "hue.h" +#include "ir.h" +#include "led.h" #include "mqtt.h" +#include "notify.h" +#include "ntp.h" +#include "overlay.h" +#include "usermod.h" #include "wled_eeprom.h" #include "wled_server.h" +#include // Global Variable definitions char versionString[] = "0.9.1"; @@ -25,131 +25,131 @@ char otaPass[33] = DEFAULT_OTA_PASS; //Hardware CONFIG (only changeble HERE, not at runtime) //LED strip pin, button pin and IR pin changeable in NpbWrapper.h! -byte auxDefaultState = 0; //0: input 1: high 2: low -byte auxTriggeredState = 0; //0: input 1: high 2: low +byte auxDefaultState = 0; //0: input 1: high 2: low +byte auxTriggeredState = 0; //0: input 1: high 2: low char ntpServerName[33] = "0.wled.pool.ntp.org"; //NTP server to use //WiFi CONFIG (all these can be changed via web UI, no need to set them here) char clientSSID[33] = CLIENT_SSID; char clientPass[65] = CLIENT_PASS; -char cmDNS[33] = "x"; //mDNS address (placeholder, will be replaced by wledXXXXXXXXXXXX.local) -char apSSID[33] = ""; //AP off by default (unless setup) -byte apChannel = 1; //2.4GHz WiFi AP channel (1-13) -byte apHide = 0; //hidden AP SSID +char cmDNS[33] = "x"; //mDNS address (placeholder, will be replaced by wledXXXXXXXXXXXX.local) +char apSSID[33] = ""; //AP off by default (unless setup) +byte apChannel = 1; //2.4GHz WiFi AP channel (1-13) +byte apHide = 0; //hidden AP SSID byte apBehavior = AP_BEHAVIOR_BOOT_NO_CONN; //access point opens when no connection after boot by default -IPAddress staticIP(0, 0, 0, 0); //static IP of ESP -IPAddress staticGateway(0, 0, 0, 0); //gateway (router) IP -IPAddress staticSubnet(255, 255, 255, 0); //most common subnet in home networks -bool noWifiSleep = false; //disabling modem sleep modes will increase heat output and power usage, but may help with connection issues +IPAddress staticIP(0, 0, 0, 0); //static IP of ESP +IPAddress staticGateway(0, 0, 0, 0); //gateway (router) IP +IPAddress staticSubnet(255, 255, 255, 0); //most common subnet in home networks +bool noWifiSleep = false; //disabling modem sleep modes will increase heat output and power usage, but may help with connection issues //LED CONFIG -uint16_t ledCount = 30; //overcurrent prevented by ABL -bool useRGBW = false; //SK6812 strips can contain an extra White channel -bool turnOnAtBoot = true; //turn on LEDs at power-up -byte bootPreset = 0; //save preset to load after power-up +uint16_t ledCount = 30; //overcurrent prevented by ABL +bool useRGBW = false; //SK6812 strips can contain an extra White channel +bool turnOnAtBoot = true; //turn on LEDs at power-up +byte bootPreset = 0; //save preset to load after power-up -byte col[]{255, 160, 0, 0}; //current RGB(W) primary color. col[] should be updated if you want to change the color. -byte colSec[]{0, 0, 0, 0}; //current RGB(W) secondary color -byte briS = 128; //default brightness +byte col[] { 255, 160, 0, 0 }; //current RGB(W) primary color. col[] should be updated if you want to change the color. +byte colSec[] { 0, 0, 0, 0 }; //current RGB(W) secondary color +byte briS = 128; //default brightness byte nightlightTargetBri = 0; //brightness after nightlight is over byte nightlightDelayMins = 60; -bool nightlightFade = true; //if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over +bool nightlightFade = true; //if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over bool nightlightColorFade = false; //if enabled, light will gradually fade color from primary to secondary color. -bool fadeTransition = true; //enable crossfading color transition -uint16_t transitionDelay = 750; //default crossfade duration in ms +bool fadeTransition = true; //enable crossfading color transition +uint16_t transitionDelay = 750; //default crossfade duration in ms bool skipFirstLed = false; //ignore first LED in strip (useful if you need the LED as signal repeater) -byte briMultiplier = 100; //% of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) +byte briMultiplier = 100; //% of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) //User Interface CONFIG char serverDescription[33] = "WLED"; //Name of module -bool syncToggleReceive = false; //UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise +bool syncToggleReceive = false; //UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise //Sync CONFIG bool buttonEnabled = true; byte irEnabled = 0; //Infrared receiver -uint16_t udpPort = 21324; //WLED notifier default port +uint16_t udpPort = 21324; //WLED notifier default port uint16_t udpRgbPort = 19446; //Hyperion port bool receiveNotificationBrightness = true; //apply brightness from incoming notifications -bool receiveNotificationColor = true; //apply color -bool receiveNotificationEffects = true; //apply effects setup -bool notifyDirect = false; //send notification if change via UI or HTTP API -bool notifyButton = false; //send if updated by button or infrared remote -bool notifyAlexa = false; //send notification if updated via Alexa -bool notifyMacro = false; //send notification for macro -bool notifyHue = true; //send notification if Hue light changes -bool notifyTwice = false; //notifications use UDP: enable if devices don't sync reliably +bool receiveNotificationColor = true; //apply color +bool receiveNotificationEffects = true; //apply effects setup +bool notifyDirect = false; //send notification if change via UI or HTTP API +bool notifyButton = false; //send if updated by button or infrared remote +bool notifyAlexa = false; //send notification if updated via Alexa +bool notifyMacro = false; //send notification for macro +bool notifyHue = true; //send notification if Hue light changes +bool notifyTwice = false; //notifications use UDP: enable if devices don't sync reliably -bool alexaEnabled = true; //enable device discovery by Amazon Echo +bool alexaEnabled = true; //enable device discovery by Amazon Echo char alexaInvocationName[33] = "Light"; //speech control name of device. Choose something voice-to-text can understand char blynkApiKey[36] = ""; //Auth token for Blynk server. If empty, no connection will be made -uint16_t realtimeTimeoutMs = 2500; //ms timeout of realtime mode before returning to normal mode -int arlsOffset = 0; //realtime LED offset -bool receiveDirect = true; //receive UDP realtime +uint16_t realtimeTimeoutMs = 2500; //ms timeout of realtime mode before returning to normal mode +int arlsOffset = 0; //realtime LED offset +bool receiveDirect = true; //receive UDP realtime bool arlsDisableGammaCorrection = true; //activate if gamma correction is handled by the source -bool arlsForceMaxBri = false; //enable to force max brightness if source has very dark colors that would be black +bool arlsForceMaxBri = false; //enable to force max brightness if source has very dark colors that would be black -uint16_t e131Universe = 1; //settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) -uint8_t DMXMode = DMX_MODE_MULTIPLE_RGB; //DMX mode (s.a.) -uint16_t DMXAddress = 1; //DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] -uint8_t DMXOldDimmer = 0; //only update brightness on change +uint16_t e131Universe = 1; //settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) +uint8_t DMXMode = DMX_MODE_MULTIPLE_RGB; //DMX mode (s.a.) +uint16_t DMXAddress = 1; //DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] +uint8_t DMXOldDimmer = 0; //only update brightness on change uint8_t e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; //to detect packet loss -bool e131Multicast = false; //multicast or unicast -bool e131SkipOutOfSequence = false; //freeze instead of flickering +bool e131Multicast = false; //multicast or unicast +bool e131SkipOutOfSequence = false; //freeze instead of flickering bool mqttEnabled = false; -char mqttDeviceTopic[33] = ""; //main MQTT topic (individual per device, default is wled/mac) +char mqttDeviceTopic[33] = ""; //main MQTT topic (individual per device, default is wled/mac) char mqttGroupTopic[33] = "wled/all"; //second MQTT topic (for example to group devices) -char mqttServer[33] = ""; //both domains and IPs should work (no SSL) -char mqttUser[41] = ""; //optional: username for MQTT auth -char mqttPass[41] = ""; //optional: password for MQTT auth -char mqttClientID[41] = ""; //override the client ID +char mqttServer[33] = ""; //both domains and IPs should work (no SSL) +char mqttUser[41] = ""; //optional: username for MQTT auth +char mqttPass[41] = ""; //optional: password for MQTT auth +char mqttClientID[41] = ""; //override the client ID uint16_t mqttPort = 1883; -bool huePollingEnabled = false; //poll hue bridge for light state +bool huePollingEnabled = false; //poll hue bridge for light state uint16_t huePollIntervalMs = 2500; //low values (< 1sec) may cause lag but offer quicker response -char hueApiKey[47] = "api"; //key token will be obtained from bridge -byte huePollLightId = 1; //ID of hue lamp to sync to. Find the ID in the hue app ("about" section) -IPAddress hueIP = (0, 0, 0, 0); //IP address of the bridge +char hueApiKey[47] = "api"; //key token will be obtained from bridge +byte huePollLightId = 1; //ID of hue lamp to sync to. Find the ID in the hue app ("about" section) +IPAddress hueIP = (0, 0, 0, 0); //IP address of the bridge bool hueApplyOnOff = true; bool hueApplyBri = true; bool hueApplyColor = true; //Time CONFIG -bool ntpEnabled = false; //get internet time. Only required if you use clock overlays or time-activated macros -bool useAMPM = false; //12h/24h clock format +bool ntpEnabled = false; //get internet time. Only required if you use clock overlays or time-activated macros +bool useAMPM = false; //12h/24h clock format byte currentTimezone = 0; //Timezone ID. Refer to timezones array in wled10_ntp.ino -int utcOffsetSecs = 0; //Seconds to offset from UTC before timzone calculation +int utcOffsetSecs = 0; //Seconds to offset from UTC before timzone calculation -byte overlayDefault = 0; //0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie +byte overlayDefault = 0; //0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie byte overlayMin = 0, overlayMax = ledCount - 1; //boundaries of overlay mode -byte analogClock12pixel = 0; //The pixel in your strip where "midnight" would be +byte analogClock12pixel = 0; //The pixel in your strip where "midnight" would be bool analogClockSecondsTrail = false; //Display seconds as trail of LEDs instead of a single pixel bool analogClock5MinuteMarks = false; //Light pixels at every 5-minute position char cronixieDisplay[7] = "HHMMSS"; //Cronixie Display mask. See wled13_cronixie.ino -bool cronixieBacklight = true; //Allow digits to be back-illuminated +bool cronixieBacklight = true; //Allow digits to be back-illuminated -bool countdownMode = false; //Clock will count down towards date +bool countdownMode = false; //Clock will count down towards date byte countdownYear = 20, countdownMonth = 1; //Countdown target date, year is last two digits byte countdownDay = 1, countdownHour = 0; byte countdownMin = 0, countdownSec = 0; byte macroBoot = 0; //macro loaded after startup -byte macroNl = 0; //after nightlight delay over +byte macroNl = 0; //after nightlight delay over byte macroCountdown = 0; byte macroAlexaOn = 0, macroAlexaOff = 0; byte macroButton = 0, macroLongPress = 0, macroDoublePress = 0; //Security CONFIG -bool otaLock = false; //prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks -bool wifiLock = false; //prevents access to WiFi settings when OTA lock is enabled +bool otaLock = false; //prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks +bool wifiLock = false; //prevents access to WiFi settings when OTA lock is enabled bool aOtaEnabled = true; //ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on uint16_t userVar0 = 0, userVar1 = 0; @@ -157,9 +157,9 @@ uint16_t userVar0 = 0, userVar1 = 0; #ifdef WLED_ENABLE_DMX //dmx CONFIG byte DMXChannels = 7; // number of channels per fixture -byte DMXFixtureMap[15] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +byte DMXFixtureMap[15] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // assigns the different channels to different functions. See wled21_dmx.ino for more information. -uint16_t DMXGap = 10; // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. +uint16_t DMXGap = 10; // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. uint16_t DMXStart = 10; // start address of the first fixture #endif @@ -172,12 +172,12 @@ bool interfacesInited = false; bool wasConnected = false; //color -byte colOld[]{0, 0, 0, 0}; //color before transition -byte colT[]{0, 0, 0, 0}; //color that is currently displayed on the LEDs -byte colIT[]{0, 0, 0, 0}; //color that was last sent to LEDs -byte colSecT[]{0, 0, 0, 0}; -byte colSecOld[]{0, 0, 0, 0}; -byte colSecIT[]{0, 0, 0, 0}; +byte colOld[] { 0, 0, 0, 0 }; //color before transition +byte colT[] { 0, 0, 0, 0 }; //color that is currently displayed on the LEDs +byte colIT[] { 0, 0, 0, 0 }; //color that was last sent to LEDs +byte colSecT[] { 0, 0, 0, 0 }; +byte colSecOld[] { 0, 0, 0, 0 }; +byte colSecIT[] { 0, 0, 0, 0 }; byte lastRandomIndex = 0; //used to save last random color so the new one is not the same @@ -195,8 +195,8 @@ bool nightlightActiveOld = false; uint32_t nightlightDelayMs = 10; uint8_t nightlightDelayMinsDefault = nightlightDelayMins; unsigned long nightlightStartTime; -byte briNlT = 0; //current nightlight brightness -byte colNlT[]{0, 0, 0, 0}; //current nightlight color +byte briNlT = 0; //current nightlight brightness +byte colNlT[] { 0, 0, 0, 0 }; //current nightlight color //brightness unsigned long lastOnTime = 0; @@ -205,7 +205,7 @@ byte bri = briS; byte briOld = 0; byte briT = 0; byte briIT = 0; -byte briLast = 128; //brightness before turned off. Used for toggle function +byte briLast = 128; //brightness before turned off. Used for toggle function byte whiteLast = 128; //white channel before turned off. Used for toggle function //button @@ -251,7 +251,7 @@ unsigned long overlayRefreshMs = 200; unsigned long overlayRefreshedTime; //cronixie -byte dP[]{0, 0, 0, 0, 0, 0}; +byte dP[] { 0, 0, 0, 0, 0, 0 }; bool cronixieInit = false; //countdown @@ -260,10 +260,10 @@ bool countdownOverTriggered = true; //timer byte lastTimerMinute = 0; -byte timerHours[] = {0, 0, 0, 0, 0, 0, 0, 0}; -byte timerMinutes[] = {0, 0, 0, 0, 0, 0, 0, 0}; -byte timerMacro[] = {0, 0, 0, 0, 0, 0, 0, 0}; -byte timerWeekday[] = {255, 255, 255, 255, 255, 255, 255, 255}; //weekdays to activate on +byte timerHours[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +byte timerMinutes[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +byte timerMacro[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +byte timerWeekday[] = { 255, 255, 255, 255, 255, 255, 255, 255 }; //weekdays to activate on //bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity //blynk @@ -300,7 +300,7 @@ bool auxActive = false, auxActiveBefore = false; String escapedMac; #ifndef WLED_DISABLE_ALEXA Espalexa espalexa; -EspalexaDevice *espalexaDevice; +EspalexaDevice* espalexaDevice; #endif //dns server @@ -315,7 +315,7 @@ IPAddress ntpServerIP; uint16_t ntpLocalPort = 2390; // Temp buffer -char *obuf; +char* obuf; uint16_t olen = 0; //presets @@ -333,8 +333,8 @@ bool doPublishMqtt = false; //server library objects AsyncWebServer server(80); -AsyncClient *hueClient = NULL; -AsyncMqttClient *mqtt = NULL; +AsyncClient* hueClient = NULL; +AsyncMqttClient* mqtt = NULL; //udp interface objects WiFiUDP notifierUdp, rgbUdp; @@ -353,396 +353,376 @@ unsigned long wifiStateChangedTime = 0; int loops = 0; #endif -WLED::WLED() { - +WLED::WLED() +{ } //turns all LEDs off and restarts ESP void WLED::reset() { - briT = 0; - long dly = millis(); - while (millis() - dly < 250) - { - yield(); //enough time to send response to client - } - setAllLeds(); - DEBUG_PRINTLN("MODULE RESET"); - ESP.restart(); + briT = 0; + long dly = millis(); + while (millis() - dly < 250) { + yield(); //enough time to send response to client + } + setAllLeds(); + DEBUG_PRINTLN("MODULE RESET"); + ESP.restart(); } bool oappendi(int i) { - char s[11]; - sprintf(s, "%ld", i); - return oappend(s); + char s[11]; + sprintf(s, "%ld", i); + return oappend(s); } -bool oappend(const char *txt) +bool oappend(const char* txt) { - uint16_t len = strlen(txt); - if (olen + len >= OMAX) - return false; //buffer full - strcpy(obuf + olen, txt); - olen += len; - return true; + uint16_t len = strlen(txt); + if (olen + len >= OMAX) + return false; //buffer full + strcpy(obuf + olen, txt); + olen += len; + return true; } void WLED::loop() { - handleIR(); //2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too - handleConnection(); - handleSerial(); - handleNotifications(); - handleTransitions(); + handleIR(); //2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too + handleConnection(); + handleSerial(); + handleNotifications(); + handleTransitions(); #ifdef WLED_ENABLE_DMX - handleDMX(); + handleDMX(); #endif - userLoop(); + userLoop(); - yield(); - handleIO(); - handleIR(); - handleNetworkTime(); - handleAlexa(); + yield(); + handleIO(); + handleIR(); + handleNetworkTime(); + handleAlexa(); - handleOverlays(); - yield(); + handleOverlays(); + yield(); #ifdef WLED_USE_ANALOG_LEDS - strip.setRgbwPwm(); + strip.setRgbwPwm(); #endif - if (doReboot) - reset(); + if (doReboot) + reset(); - if (!realtimeMode) //block stuff if WARLS/Adalight is enabled - { - if (apActive) - dnsServer.processNextRequest(); + if (!realtimeMode) //block stuff if WARLS/Adalight is enabled + { + if (apActive) + dnsServer.processNextRequest(); #ifndef WLED_DISABLE_OTA - if (WLED_CONNECTED && aOtaEnabled) - ArduinoOTA.handle(); + if (WLED_CONNECTED && aOtaEnabled) + ArduinoOTA.handle(); #endif - handleNightlight(); - yield(); - - handleHue(); - handleBlynk(); - - yield(); - if (!offMode) - strip.service(); - } + handleNightlight(); yield(); + + handleHue(); + handleBlynk(); + + yield(); + if (!offMode) + strip.service(); + } + yield(); #ifdef ESP8266 - MDNS.update(); + MDNS.update(); #endif - if (millis() - lastMqttReconnectAttempt > 30000) - initMqtt(); + if (millis() - lastMqttReconnectAttempt > 30000) + initMqtt(); //DEBUG serial logging #ifdef WLED_DEBUG - if (millis() - debugTime > 9999) - { - DEBUG_PRINTLN("---DEBUG INFO---"); - DEBUG_PRINT("Runtime: "); - DEBUG_PRINTLN(millis()); - DEBUG_PRINT("Unix time: "); - DEBUG_PRINTLN(now()); - DEBUG_PRINT("Free heap: "); - DEBUG_PRINTLN(ESP.getFreeHeap()); - DEBUG_PRINT("Wifi state: "); - DEBUG_PRINTLN(WiFi.status()); - if (WiFi.status() != lastWifiState) - { - wifiStateChangedTime = millis(); - } - lastWifiState = WiFi.status(); - DEBUG_PRINT("State time: "); - DEBUG_PRINTLN(wifiStateChangedTime); - DEBUG_PRINT("NTP last sync: "); - DEBUG_PRINTLN(ntpLastSyncTime); - DEBUG_PRINT("Client IP: "); - DEBUG_PRINTLN(WiFi.localIP()); - DEBUG_PRINT("Loops/sec: "); - DEBUG_PRINTLN(loops / 10); - loops = 0; - debugTime = millis(); + if (millis() - debugTime > 9999) { + DEBUG_PRINTLN("---DEBUG INFO---"); + DEBUG_PRINT("Runtime: "); + DEBUG_PRINTLN(millis()); + DEBUG_PRINT("Unix time: "); + DEBUG_PRINTLN(now()); + DEBUG_PRINT("Free heap: "); + DEBUG_PRINTLN(ESP.getFreeHeap()); + DEBUG_PRINT("Wifi state: "); + DEBUG_PRINTLN(WiFi.status()); + if (WiFi.status() != lastWifiState) { + wifiStateChangedTime = millis(); } - loops++; + lastWifiState = WiFi.status(); + DEBUG_PRINT("State time: "); + DEBUG_PRINTLN(wifiStateChangedTime); + DEBUG_PRINT("NTP last sync: "); + DEBUG_PRINTLN(ntpLastSyncTime); + DEBUG_PRINT("Client IP: "); + DEBUG_PRINTLN(WiFi.localIP()); + DEBUG_PRINT("Loops/sec: "); + DEBUG_PRINTLN(loops / 10); + loops = 0; + debugTime = millis(); + } + loops++; #endif // WLED_DEBU } void WLED::setup() { - EEPROM.begin(EEPSIZE); - ledCount = EEPROM.read(229) + ((EEPROM.read(398) << 8) & 0xFF00); - if (ledCount > MAX_LEDS || ledCount == 0) - ledCount = 30; + EEPROM.begin(EEPSIZE); + ledCount = EEPROM.read(229) + ((EEPROM.read(398) << 8) & 0xFF00); + if (ledCount > MAX_LEDS || ledCount == 0) + ledCount = 30; #ifdef ESP8266 #if LEDPIN == 3 - if (ledCount > MAX_LEDS_DMA) - ledCount = MAX_LEDS_DMA; //DMA method uses too much ram + if (ledCount > MAX_LEDS_DMA) + ledCount = MAX_LEDS_DMA; //DMA method uses too much ram #endif #endif - Serial.begin(115200); - Serial.setTimeout(50); - DEBUG_PRINTLN(); - DEBUG_PRINT("---WLED "); - DEBUG_PRINT(versionString); - DEBUG_PRINT(" "); - DEBUG_PRINT(VERSION); - DEBUG_PRINTLN(" INIT---"); + Serial.begin(115200); + Serial.setTimeout(50); + DEBUG_PRINTLN(); + DEBUG_PRINT("---WLED "); + DEBUG_PRINT(versionString); + DEBUG_PRINT(" "); + DEBUG_PRINT(VERSION); + DEBUG_PRINTLN(" INIT---"); #ifdef ARDUINO_ARCH_ESP32 - DEBUG_PRINT("esp32 "); - DEBUG_PRINTLN(ESP.getSdkVersion()); + DEBUG_PRINT("esp32 "); + DEBUG_PRINTLN(ESP.getSdkVersion()); #else - DEBUG_PRINT("esp8266 "); - DEBUG_PRINTLN(ESP.getCoreVersion()); + DEBUG_PRINT("esp8266 "); + DEBUG_PRINTLN(ESP.getCoreVersion()); #endif - int heapPreAlloc = ESP.getFreeHeap(); - DEBUG_PRINT("heap "); - DEBUG_PRINTLN(ESP.getFreeHeap()); + int heapPreAlloc = ESP.getFreeHeap(); + DEBUG_PRINT("heap "); + DEBUG_PRINTLN(ESP.getFreeHeap()); - strip.init(EEPROM.read(372), ledCount, EEPROM.read(2204)); //init LEDs quickly - strip.setBrightness(0); + strip.init(EEPROM.read(372), ledCount, EEPROM.read(2204)); //init LEDs quickly + strip.setBrightness(0); - DEBUG_PRINT("LEDs inited. heap usage ~"); - DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap()); + DEBUG_PRINT("LEDs inited. heap usage ~"); + DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap()); #ifndef WLED_DISABLE_FILESYSTEM #ifdef ARDUINO_ARCH_ESP32 - SPIFFS.begin(true); + SPIFFS.begin(true); #endif - SPIFFS.begin(); + SPIFFS.begin(); #endif - DEBUG_PRINTLN("Load EEPROM"); - loadSettingsFromEEPROM(true); - beginStrip(); - userSetup(); - if (strcmp(clientSSID, DEFAULT_CLIENT_SSID) == 0) - showWelcomePage = true; - WiFi.persistent(false); + DEBUG_PRINTLN("Load EEPROM"); + loadSettingsFromEEPROM(true); + beginStrip(); + userSetup(); + if (strcmp(clientSSID, DEFAULT_CLIENT_SSID) == 0) + showWelcomePage = true; + WiFi.persistent(false); - if (macroBoot > 0) - applyMacro(macroBoot); - Serial.println("Ada"); + if (macroBoot > 0) + applyMacro(macroBoot); + Serial.println("Ada"); - //generate module IDs - escapedMac = WiFi.macAddress(); - escapedMac.replace(":", ""); - escapedMac.toLowerCase(); - if (strcmp(cmDNS, "x") == 0) //fill in unique mdns default - { - strcpy(cmDNS, "wled-"); - sprintf(cmDNS + 5, "%*s", 6, escapedMac.c_str() + 6); - } - if (mqttDeviceTopic[0] == 0) - { - strcpy(mqttDeviceTopic, "wled/"); - sprintf(mqttDeviceTopic + 5, "%*s", 6, escapedMac.c_str() + 6); - } - if (mqttClientID[0] == 0) - { - strcpy(mqttClientID, "WLED-"); - sprintf(mqttClientID + 5, "%*s", 6, escapedMac.c_str() + 6); - } + //generate module IDs + escapedMac = WiFi.macAddress(); + escapedMac.replace(":", ""); + escapedMac.toLowerCase(); + if (strcmp(cmDNS, "x") == 0) //fill in unique mdns default + { + strcpy(cmDNS, "wled-"); + sprintf(cmDNS + 5, "%*s", 6, escapedMac.c_str() + 6); + } + if (mqttDeviceTopic[0] == 0) { + strcpy(mqttDeviceTopic, "wled/"); + sprintf(mqttDeviceTopic + 5, "%*s", 6, escapedMac.c_str() + 6); + } + if (mqttClientID[0] == 0) { + strcpy(mqttClientID, "WLED-"); + sprintf(mqttClientID + 5, "%*s", 6, escapedMac.c_str() + 6); + } - strip.service(); + strip.service(); #ifndef WLED_DISABLE_OTA - if (aOtaEnabled) - { - ArduinoOTA.onStart([]() { + if (aOtaEnabled) { + ArduinoOTA.onStart([]() { #ifdef ESP8266 - wifi_set_sleep_type(NONE_SLEEP_T); + wifi_set_sleep_type(NONE_SLEEP_T); #endif - DEBUG_PRINTLN("Start ArduinoOTA"); - }); - if (strlen(cmDNS) > 0) - ArduinoOTA.setHostname(cmDNS); - } + DEBUG_PRINTLN("Start ArduinoOTA"); + }); + if (strlen(cmDNS) > 0) + ArduinoOTA.setHostname(cmDNS); + } #endif #ifdef WLED_ENABLE_DMX - dmx.init(512); // initialize with bus length + dmx.init(512); // initialize with bus length #endif - //HTTP server page init - initServer(); + //HTTP server page init + initServer(); } void WLED::beginStrip() { - // Initialize NeoPixel Strip and button - strip.setShowCallback(handleOverlayDraw); + // Initialize NeoPixel Strip and button + strip.setShowCallback(handleOverlayDraw); #ifdef BTNPIN - pinMode(BTNPIN, INPUT_PULLUP); + pinMode(BTNPIN, INPUT_PULLUP); #endif - if (bootPreset > 0) - applyPreset(bootPreset, turnOnAtBoot); - colorUpdated(NOTIFIER_CALL_MODE_INIT); + if (bootPreset > 0) + applyPreset(bootPreset, turnOnAtBoot); + colorUpdated(NOTIFIER_CALL_MODE_INIT); //init relay pin #if RLYPIN >= 0 - pinMode(RLYPIN, OUTPUT); + pinMode(RLYPIN, OUTPUT); #if RLYMDE - digitalWrite(RLYPIN, bri); + digitalWrite(RLYPIN, bri); #else - digitalWrite(RLYPIN, !bri); + digitalWrite(RLYPIN, !bri); #endif #endif - //disable button if it is "pressed" unintentionally + //disable button if it is "pressed" unintentionally #ifdef BTNPIN - if (digitalRead(BTNPIN) == LOW) - buttonEnabled = false; -#else + if (digitalRead(BTNPIN) == LOW) buttonEnabled = false; +#else + buttonEnabled = false; #endif } void WLED::initAP(bool resetAP) { - if (apBehavior == AP_BEHAVIOR_BUTTON_ONLY && !resetAP) - return; + if (apBehavior == AP_BEHAVIOR_BUTTON_ONLY && !resetAP) + return; - if (!apSSID[0] || resetAP) - strcpy(apSSID, "WLED-AP"); - if (resetAP) - strcpy(apPass, DEFAULT_AP_PASS); - DEBUG_PRINT("Opening access point "); - DEBUG_PRINTLN(apSSID); - WiFi.softAPConfig(IPAddress(4, 3, 2, 1), IPAddress(4, 3, 2, 1), IPAddress(255, 255, 255, 0)); - WiFi.softAP(apSSID, apPass, apChannel, apHide); + if (!apSSID[0] || resetAP) + strcpy(apSSID, "WLED-AP"); + if (resetAP) + strcpy(apPass, DEFAULT_AP_PASS); + DEBUG_PRINT("Opening access point "); + DEBUG_PRINTLN(apSSID); + WiFi.softAPConfig(IPAddress(4, 3, 2, 1), IPAddress(4, 3, 2, 1), IPAddress(255, 255, 255, 0)); + WiFi.softAP(apSSID, apPass, apChannel, apHide); - if (!apActive) //start captive portal if AP active - { - DEBUG_PRINTLN("Init AP interfaces"); - server.begin(); - if (udpPort > 0 && udpPort != ntpLocalPort) - { - udpConnected = notifierUdp.begin(udpPort); - } - if (udpRgbPort > 0 && udpRgbPort != ntpLocalPort && udpRgbPort != udpPort) - { - udpRgbConnected = rgbUdp.begin(udpRgbPort); - } - - dnsServer.setErrorReplyCode(DNSReplyCode::NoError); - dnsServer.start(53, "*", WiFi.softAPIP()); + if (!apActive) //start captive portal if AP active + { + DEBUG_PRINTLN("Init AP interfaces"); + server.begin(); + if (udpPort > 0 && udpPort != ntpLocalPort) { + udpConnected = notifierUdp.begin(udpPort); } - apActive = true; + if (udpRgbPort > 0 && udpRgbPort != ntpLocalPort && udpRgbPort != udpPort) { + udpRgbConnected = rgbUdp.begin(udpRgbPort); + } + + dnsServer.setErrorReplyCode(DNSReplyCode::NoError); + dnsServer.start(53, "*", WiFi.softAPIP()); + } + apActive = true; } void WLED::initConnection() { - WiFi.disconnect(); //close old connections + WiFi.disconnect(); //close old connections #ifdef ESP8266 - WiFi.setPhyMode(WIFI_PHY_MODE_11N); + WiFi.setPhyMode(WIFI_PHY_MODE_11N); #endif - if (staticIP[0] != 0 && staticGateway[0] != 0) - { - WiFi.config(staticIP, staticGateway, staticSubnet, IPAddress(8, 8, 8, 8)); - } - else - { - WiFi.config(0U, 0U, 0U); - } + if (staticIP[0] != 0 && staticGateway[0] != 0) { + WiFi.config(staticIP, staticGateway, staticSubnet, IPAddress(8, 8, 8, 8)); + } else { + WiFi.config(0U, 0U, 0U); + } - lastReconnectAttempt = millis(); + lastReconnectAttempt = millis(); - if (!WLED_WIFI_CONFIGURED) - { - DEBUG_PRINT("No connection configured. "); - if (!apActive) - initAP(); //instantly go to ap mode - return; + if (!WLED_WIFI_CONFIGURED) { + DEBUG_PRINT("No connection configured. "); + if (!apActive) + initAP(); //instantly go to ap mode + return; + } else if (!apActive) { + if (apBehavior == AP_BEHAVIOR_ALWAYS) { + initAP(); + } else { + DEBUG_PRINTLN("Access point disabled."); + WiFi.softAPdisconnect(true); } - else if (!apActive) - { - if (apBehavior == AP_BEHAVIOR_ALWAYS) - { - initAP(); - } - else - { - DEBUG_PRINTLN("Access point disabled."); - WiFi.softAPdisconnect(true); - } - } - showWelcomePage = false; + } + showWelcomePage = false; - DEBUG_PRINT("Connecting to "); - DEBUG_PRINT(clientSSID); - DEBUG_PRINTLN("..."); + DEBUG_PRINT("Connecting to "); + DEBUG_PRINT(clientSSID); + DEBUG_PRINTLN("..."); #ifdef ESP8266 - WiFi.hostname(serverDescription); + WiFi.hostname(serverDescription); #endif - WiFi.begin(clientSSID, clientPass); + WiFi.begin(clientSSID, clientPass); #ifdef ARDUINO_ARCH_ESP32 - WiFi.setSleep(!noWifiSleep); - WiFi.setHostname(serverDescription); + WiFi.setSleep(!noWifiSleep); + WiFi.setHostname(serverDescription); #else - wifi_set_sleep_type((noWifiSleep) ? NONE_SLEEP_T : MODEM_SLEEP_T); + wifi_set_sleep_type((noWifiSleep) ? NONE_SLEEP_T : MODEM_SLEEP_T); #endif } void WLED::initInterfaces() { - DEBUG_PRINTLN("Init STA interfaces"); + DEBUG_PRINTLN("Init STA interfaces"); - if (hueIP[0] == 0) - { - hueIP[0] = WiFi.localIP()[0]; - hueIP[1] = WiFi.localIP()[1]; - hueIP[2] = WiFi.localIP()[2]; - } + if (hueIP[0] == 0) { + hueIP[0] = WiFi.localIP()[0]; + hueIP[1] = WiFi.localIP()[1]; + hueIP[2] = WiFi.localIP()[2]; + } - //init Alexa hue emulation - if (alexaEnabled) - alexaInit(); + //init Alexa hue emulation + if (alexaEnabled) + alexaInit(); #ifndef WLED_DISABLE_OTA - if (aOtaEnabled) - ArduinoOTA.begin(); + if (aOtaEnabled) + ArduinoOTA.begin(); #endif - strip.service(); - // Set up mDNS responder: - if (strlen(cmDNS) > 0) - { - if (!aOtaEnabled) - MDNS.begin(cmDNS); + strip.service(); + // Set up mDNS responder: + if (strlen(cmDNS) > 0) { + if (!aOtaEnabled) + MDNS.begin(cmDNS); - DEBUG_PRINTLN("mDNS started"); - MDNS.addService("http", "tcp", 80); - MDNS.addService("wled", "tcp", 80); - MDNS.addServiceTxt("wled", "tcp", "mac", escapedMac.c_str()); - } - server.begin(); + DEBUG_PRINTLN("mDNS started"); + MDNS.addService("http", "tcp", 80); + MDNS.addService("wled", "tcp", 80); + MDNS.addServiceTxt("wled", "tcp", "mac", escapedMac.c_str()); + } + server.begin(); - if (udpPort > 0 && udpPort != ntpLocalPort) - { - udpConnected = notifierUdp.begin(udpPort); - if (udpConnected && udpRgbPort != udpPort) - udpRgbConnected = rgbUdp.begin(udpRgbPort); - } - if (ntpEnabled) - ntpConnected = ntpUdp.begin(ntpLocalPort); + if (udpPort > 0 && udpPort != ntpLocalPort) { + udpConnected = notifierUdp.begin(udpPort); + if (udpConnected && udpRgbPort != udpPort) + udpRgbConnected = rgbUdp.begin(udpRgbPort); + } + if (ntpEnabled) + ntpConnected = ntpUdp.begin(ntpLocalPort); - initBlynk(blynkApiKey); - e131.begin((e131Multicast) ? E131_MULTICAST : E131_UNICAST, e131Universe, E131_MAX_UNIVERSE_COUNT); - reconnectHue(); - initMqtt(); - interfacesInited = true; - wasConnected = true; + initBlynk(blynkApiKey); + e131.begin((e131Multicast) ? E131_MULTICAST : E131_UNICAST, e131Universe, E131_MAX_UNIVERSE_COUNT); + reconnectHue(); + initMqtt(); + interfacesInited = true; + wasConnected = true; } byte stacO = 0; @@ -751,86 +731,75 @@ unsigned long heapTime = 0; void WLED::handleConnection() { - if (millis() < 2000 && (!WLED_WIFI_CONFIGURED || apBehavior == AP_BEHAVIOR_ALWAYS)) - return; - if (lastReconnectAttempt == 0) - initConnection(); + if (millis() < 2000 && (!WLED_WIFI_CONFIGURED || apBehavior == AP_BEHAVIOR_ALWAYS)) + return; + if (lastReconnectAttempt == 0) + initConnection(); - //reconnect WiFi to clear stale allocations if heap gets too low - if (millis() - heapTime > 5000) - { - uint32_t heap = ESP.getFreeHeap(); - if (heap < 9000 && lastHeap < 9000) - { - DEBUG_PRINT("Heap too low! "); - DEBUG_PRINTLN(heap); - forceReconnect = true; - } - lastHeap = heap; - heapTime = millis(); + //reconnect WiFi to clear stale allocations if heap gets too low + if (millis() - heapTime > 5000) { + uint32_t heap = ESP.getFreeHeap(); + if (heap < 9000 && lastHeap < 9000) { + DEBUG_PRINT("Heap too low! "); + DEBUG_PRINTLN(heap); + forceReconnect = true; } + lastHeap = heap; + heapTime = millis(); + } - byte stac = 0; - if (apActive) - { + byte stac = 0; + if (apActive) { #ifdef ESP8266 - stac = wifi_softap_get_station_num(); + stac = wifi_softap_get_station_num(); #else - wifi_sta_list_t stationList; - esp_wifi_ap_get_sta_list(&stationList); - stac = stationList.num; + wifi_sta_list_t stationList; + esp_wifi_ap_get_sta_list(&stationList); + stac = stationList.num; #endif - if (stac != stacO) - { - stacO = stac; - DEBUG_PRINT("Connected AP clients: "); - DEBUG_PRINTLN(stac); - if (!WLED_CONNECTED && WLED_WIFI_CONFIGURED) - { //trying to connect, but not connected - if (stac) - WiFi.disconnect(); //disable search so that AP can work - else - initConnection(); //restart search - } - } + if (stac != stacO) { + stacO = stac; + DEBUG_PRINT("Connected AP clients: "); + DEBUG_PRINTLN(stac); + if (!WLED_CONNECTED && WLED_WIFI_CONFIGURED) { //trying to connect, but not connected + if (stac) + WiFi.disconnect(); //disable search so that AP can work + else + initConnection(); //restart search + } } - if (forceReconnect) - { - DEBUG_PRINTLN("Forcing reconnect."); - initConnection(); - interfacesInited = false; - forceReconnect = false; - wasConnected = false; - return; + } + if (forceReconnect) { + DEBUG_PRINTLN("Forcing reconnect."); + initConnection(); + interfacesInited = false; + forceReconnect = false; + wasConnected = false; + return; + } + if (!WLED_CONNECTED) { + if (interfacesInited) { + DEBUG_PRINTLN("Disconnected!"); + interfacesInited = false; + initConnection(); } - if (!WLED_CONNECTED) - { - if (interfacesInited) - { - DEBUG_PRINTLN("Disconnected!"); - interfacesInited = false; - initConnection(); - } - if (millis() - lastReconnectAttempt > ((stac) ? 300000 : 20000) && WLED_WIFI_CONFIGURED) - initConnection(); - if (!apActive && millis() - lastReconnectAttempt > 12000 && (!wasConnected || apBehavior == AP_BEHAVIOR_NO_CONN)) - initAP(); - } - else if (!interfacesInited) - { //newly connected - DEBUG_PRINTLN(""); - DEBUG_PRINT("Connected! IP address: "); - DEBUG_PRINTLN(WiFi.localIP()); - initInterfaces(); - userConnected(); + if (millis() - lastReconnectAttempt > ((stac) ? 300000 : 20000) && WLED_WIFI_CONFIGURED) + initConnection(); + if (!apActive && millis() - lastReconnectAttempt > 12000 && (!wasConnected || apBehavior == AP_BEHAVIOR_NO_CONN)) + initAP(); + } else if (!interfacesInited) { //newly connected + DEBUG_PRINTLN(""); + DEBUG_PRINT("Connected! IP address: "); + DEBUG_PRINTLN(WiFi.localIP()); + initInterfaces(); + userConnected(); - //shut down AP - if (apBehavior != AP_BEHAVIOR_ALWAYS && apActive) - { - dnsServer.stop(); - WiFi.softAPdisconnect(true); - apActive = false; - DEBUG_PRINTLN("Access point disabled."); - } + //shut down AP + if (apBehavior != AP_BEHAVIOR_ALWAYS && apActive) { + dnsServer.stop(); + WiFi.softAPdisconnect(true); + apActive = false; + DEBUG_PRINTLN("Access point disabled."); } + } } From ffbedbc1e62dd25c4a4e798182a831666fe3e3d2 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Mon, 30 Mar 2020 06:27:36 -0400 Subject: [PATCH 197/232] Add clang format to git ignore. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ea49cb51..5e61e156 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ /wled00/Release /wled00/extLibs /platformio_override.ini +.clang-format From 8d75c06852e44e3b87b3b2002a40cee4796af7d0 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Mon, 30 Mar 2020 06:42:21 -0400 Subject: [PATCH 198/232] Format changes. --- .clang-format | 10 +- wled00/wled.cpp | 366 ++++++++++++++++++++++++------------------------ wled00/wled.h | 188 +++++++++++++------------ 3 files changed, 287 insertions(+), 277 deletions(-) diff --git a/.clang-format b/.clang-format index 56df2b76..aedd2246 100644 --- a/.clang-format +++ b/.clang-format @@ -1,4 +1,12 @@ --- BasedOnStyle: Webkit IndentWidth: 2 - \ No newline at end of file + AlignTrailingComments: true + SpacesBeforeTrailingComments: 8 + AllowShortIfStatementsOnASingleLine: Always + AllowShortLoopsOnASingleLine: true + AllowShortLambdasOnASingleLine: true + AllowShortCaseLabelsOnASingleLine: true + AllowShortFunctionsOnASingleLine: All + AllowShortBlocksOnASingleLine: true + IndentCaseLabels: true \ No newline at end of file diff --git a/wled00/wled.cpp b/wled00/wled.cpp index fd47592f..48231599 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -18,224 +18,224 @@ // Global Variable definitions char versionString[] = "0.9.1"; -//AP and OTA default passwords (for maximum change them!) +// AP and OTA default passwords (for maximum change them!) char apPass[65] = DEFAULT_AP_PASS; char otaPass[33] = DEFAULT_OTA_PASS; -//Hardware CONFIG (only changeble HERE, not at runtime) -//LED strip pin, button pin and IR pin changeable in NpbWrapper.h! +// Hardware CONFIG (only changeble HERE, not at runtime) +// LED strip pin, button pin and IR pin changeable in NpbWrapper.h! -byte auxDefaultState = 0; //0: input 1: high 2: low -byte auxTriggeredState = 0; //0: input 1: high 2: low -char ntpServerName[33] = "0.wled.pool.ntp.org"; //NTP server to use +byte auxDefaultState = 0; // 0: input 1: high 2: low +byte auxTriggeredState = 0; // 0: input 1: high 2: low +char ntpServerName[33] = "0.wled.pool.ntp.org"; // NTP server to use -//WiFi CONFIG (all these can be changed via web UI, no need to set them here) +// WiFi CONFIG (all these can be changed via web UI, no need to set them here) char clientSSID[33] = CLIENT_SSID; char clientPass[65] = CLIENT_PASS; -char cmDNS[33] = "x"; //mDNS address (placeholder, will be replaced by wledXXXXXXXXXXXX.local) -char apSSID[33] = ""; //AP off by default (unless setup) -byte apChannel = 1; //2.4GHz WiFi AP channel (1-13) -byte apHide = 0; //hidden AP SSID -byte apBehavior = AP_BEHAVIOR_BOOT_NO_CONN; //access point opens when no connection after boot by default -IPAddress staticIP(0, 0, 0, 0); //static IP of ESP -IPAddress staticGateway(0, 0, 0, 0); //gateway (router) IP -IPAddress staticSubnet(255, 255, 255, 0); //most common subnet in home networks -bool noWifiSleep = false; //disabling modem sleep modes will increase heat output and power usage, but may help with connection issues +char cmDNS[33] = "x"; // mDNS address (placeholder, will be replaced by wledXXXXXXXXXXXX.local) +char apSSID[33] = ""; // AP off by default (unless setup) +byte apChannel = 1; // 2.4GHz WiFi AP channel (1-13) +byte apHide = 0; // hidden AP SSID +byte apBehavior = AP_BEHAVIOR_BOOT_NO_CONN; // access point opens when no connection after boot by default +IPAddress staticIP(0, 0, 0, 0); // static IP of ESP +IPAddress staticGateway(0, 0, 0, 0); // gateway (router) IP +IPAddress staticSubnet(255, 255, 255, 0); // most common subnet in home networks +bool noWifiSleep = false; // disabling modem sleep modes will increase heat output and power usage, but may help with connection issues -//LED CONFIG -uint16_t ledCount = 30; //overcurrent prevented by ABL -bool useRGBW = false; //SK6812 strips can contain an extra White channel -bool turnOnAtBoot = true; //turn on LEDs at power-up -byte bootPreset = 0; //save preset to load after power-up +// LED CONFIG +uint16_t ledCount = 30; // overcurrent prevented by ABL +bool useRGBW = false; // SK6812 strips can contain an extra White channel +bool turnOnAtBoot = true; // turn on LEDs at power-up +byte bootPreset = 0; // save preset to load after power-up -byte col[] { 255, 160, 0, 0 }; //current RGB(W) primary color. col[] should be updated if you want to change the color. -byte colSec[] { 0, 0, 0, 0 }; //current RGB(W) secondary color -byte briS = 128; //default brightness +byte col[] { 255, 160, 0, 0 }; // current RGB(W) primary color. col[] should be updated if you want to change the color. +byte colSec[] { 0, 0, 0, 0 }; // current RGB(W) secondary color +byte briS = 128; // default brightness -byte nightlightTargetBri = 0; //brightness after nightlight is over +byte nightlightTargetBri = 0; // brightness after nightlight is over byte nightlightDelayMins = 60; -bool nightlightFade = true; //if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over -bool nightlightColorFade = false; //if enabled, light will gradually fade color from primary to secondary color. -bool fadeTransition = true; //enable crossfading color transition -uint16_t transitionDelay = 750; //default crossfade duration in ms +bool nightlightFade = true; // if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over +bool nightlightColorFade = false; // if enabled, light will gradually fade color from primary to secondary color. +bool fadeTransition = true; // enable crossfading color transition +uint16_t transitionDelay = 750; // default crossfade duration in ms -bool skipFirstLed = false; //ignore first LED in strip (useful if you need the LED as signal repeater) -byte briMultiplier = 100; //% of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) +bool skipFirstLed = false; // ignore first LED in strip (useful if you need the LED as signal repeater) +byte briMultiplier = 100; // % of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) -//User Interface CONFIG -char serverDescription[33] = "WLED"; //Name of module -bool syncToggleReceive = false; //UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise +// User Interface CONFIG +char serverDescription[33] = "WLED"; // Name of module +bool syncToggleReceive = false; // UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise -//Sync CONFIG +// Sync CONFIG bool buttonEnabled = true; -byte irEnabled = 0; //Infrared receiver +byte irEnabled = 0; // Infrared receiver -uint16_t udpPort = 21324; //WLED notifier default port -uint16_t udpRgbPort = 19446; //Hyperion port +uint16_t udpPort = 21324; // WLED notifier default port +uint16_t udpRgbPort = 19446; // Hyperion port -bool receiveNotificationBrightness = true; //apply brightness from incoming notifications -bool receiveNotificationColor = true; //apply color -bool receiveNotificationEffects = true; //apply effects setup -bool notifyDirect = false; //send notification if change via UI or HTTP API -bool notifyButton = false; //send if updated by button or infrared remote -bool notifyAlexa = false; //send notification if updated via Alexa -bool notifyMacro = false; //send notification for macro -bool notifyHue = true; //send notification if Hue light changes -bool notifyTwice = false; //notifications use UDP: enable if devices don't sync reliably +bool receiveNotificationBrightness = true; // apply brightness from incoming notifications +bool receiveNotificationColor = true; // apply color +bool receiveNotificationEffects = true; // apply effects setup +bool notifyDirect = false; // send notification if change via UI or HTTP API +bool notifyButton = false; // send if updated by button or infrared remote +bool notifyAlexa = false; // send notification if updated via Alexa +bool notifyMacro = false; // send notification for macro +bool notifyHue = true; // send notification if Hue light changes +bool notifyTwice = false; // notifications use UDP: enable if devices don't sync reliably -bool alexaEnabled = true; //enable device discovery by Amazon Echo -char alexaInvocationName[33] = "Light"; //speech control name of device. Choose something voice-to-text can understand +bool alexaEnabled = true; // enable device discovery by Amazon Echo +char alexaInvocationName[33] = "Light"; // speech control name of device. Choose something voice-to-text can understand -char blynkApiKey[36] = ""; //Auth token for Blynk server. If empty, no connection will be made +char blynkApiKey[36] = ""; // Auth token for Blynk server. If empty, no connection will be made -uint16_t realtimeTimeoutMs = 2500; //ms timeout of realtime mode before returning to normal mode -int arlsOffset = 0; //realtime LED offset -bool receiveDirect = true; //receive UDP realtime -bool arlsDisableGammaCorrection = true; //activate if gamma correction is handled by the source -bool arlsForceMaxBri = false; //enable to force max brightness if source has very dark colors that would be black +uint16_t realtimeTimeoutMs = 2500; // ms timeout of realtime mode before returning to normal mode +int arlsOffset = 0; // realtime LED offset +bool receiveDirect = true; // receive UDP realtime +bool arlsDisableGammaCorrection = true; // activate if gamma correction is handled by the source +bool arlsForceMaxBri = false; // enable to force max brightness if source has very dark colors that would be black -uint16_t e131Universe = 1; //settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) -uint8_t DMXMode = DMX_MODE_MULTIPLE_RGB; //DMX mode (s.a.) -uint16_t DMXAddress = 1; //DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] -uint8_t DMXOldDimmer = 0; //only update brightness on change -uint8_t e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; //to detect packet loss -bool e131Multicast = false; //multicast or unicast -bool e131SkipOutOfSequence = false; //freeze instead of flickering +uint16_t e131Universe = 1; // settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) +uint8_t DMXMode = DMX_MODE_MULTIPLE_RGB; // DMX mode (s.a.) +uint16_t DMXAddress = 1; // DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] +uint8_t DMXOldDimmer = 0; // only update brightness on change +uint8_t e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; // to detect packet loss +bool e131Multicast = false; // multicast or unicast +bool e131SkipOutOfSequence = false; // freeze instead of flickering bool mqttEnabled = false; -char mqttDeviceTopic[33] = ""; //main MQTT topic (individual per device, default is wled/mac) -char mqttGroupTopic[33] = "wled/all"; //second MQTT topic (for example to group devices) -char mqttServer[33] = ""; //both domains and IPs should work (no SSL) -char mqttUser[41] = ""; //optional: username for MQTT auth -char mqttPass[41] = ""; //optional: password for MQTT auth -char mqttClientID[41] = ""; //override the client ID +char mqttDeviceTopic[33] = ""; // main MQTT topic (individual per device, default is wled/mac) +char mqttGroupTopic[33] = "wled/all"; // second MQTT topic (for example to group devices) +char mqttServer[33] = ""; // both domains and IPs should work (no SSL) +char mqttUser[41] = ""; // optional: username for MQTT auth +char mqttPass[41] = ""; // optional: password for MQTT auth +char mqttClientID[41] = ""; // override the client ID uint16_t mqttPort = 1883; -bool huePollingEnabled = false; //poll hue bridge for light state -uint16_t huePollIntervalMs = 2500; //low values (< 1sec) may cause lag but offer quicker response -char hueApiKey[47] = "api"; //key token will be obtained from bridge -byte huePollLightId = 1; //ID of hue lamp to sync to. Find the ID in the hue app ("about" section) -IPAddress hueIP = (0, 0, 0, 0); //IP address of the bridge +bool huePollingEnabled = false; // poll hue bridge for light state +uint16_t huePollIntervalMs = 2500; // low values (< 1sec) may cause lag but offer quicker response +char hueApiKey[47] = "api"; // key token will be obtained from bridge +byte huePollLightId = 1; // ID of hue lamp to sync to. Find the ID in the hue app ("about" section) +IPAddress hueIP = (0, 0, 0, 0); // IP address of the bridge bool hueApplyOnOff = true; bool hueApplyBri = true; bool hueApplyColor = true; -//Time CONFIG -bool ntpEnabled = false; //get internet time. Only required if you use clock overlays or time-activated macros -bool useAMPM = false; //12h/24h clock format -byte currentTimezone = 0; //Timezone ID. Refer to timezones array in wled10_ntp.ino -int utcOffsetSecs = 0; //Seconds to offset from UTC before timzone calculation +// Time CONFIG +bool ntpEnabled = false; // get internet time. Only required if you use clock overlays or time-activated macros +bool useAMPM = false; // 12h/24h clock format +byte currentTimezone = 0; // Timezone ID. Refer to timezones array in wled10_ntp.ino +int utcOffsetSecs = 0; // Seconds to offset from UTC before timzone calculation -byte overlayDefault = 0; //0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie -byte overlayMin = 0, overlayMax = ledCount - 1; //boundaries of overlay mode +byte overlayDefault = 0; // 0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie +byte overlayMin = 0, overlayMax = ledCount - 1; // boundaries of overlay mode -byte analogClock12pixel = 0; //The pixel in your strip where "midnight" would be -bool analogClockSecondsTrail = false; //Display seconds as trail of LEDs instead of a single pixel -bool analogClock5MinuteMarks = false; //Light pixels at every 5-minute position +byte analogClock12pixel = 0; // The pixel in your strip where "midnight" would be +bool analogClockSecondsTrail = false; // Display seconds as trail of LEDs instead of a single pixel +bool analogClock5MinuteMarks = false; // Light pixels at every 5-minute position -char cronixieDisplay[7] = "HHMMSS"; //Cronixie Display mask. See wled13_cronixie.ino -bool cronixieBacklight = true; //Allow digits to be back-illuminated +char cronixieDisplay[7] = "HHMMSS"; // Cronixie Display mask. See wled13_cronixie.ino +bool cronixieBacklight = true; // Allow digits to be back-illuminated -bool countdownMode = false; //Clock will count down towards date -byte countdownYear = 20, countdownMonth = 1; //Countdown target date, year is last two digits +bool countdownMode = false; // Clock will count down towards date +byte countdownYear = 20, countdownMonth = 1; // Countdown target date, year is last two digits byte countdownDay = 1, countdownHour = 0; byte countdownMin = 0, countdownSec = 0; -byte macroBoot = 0; //macro loaded after startup -byte macroNl = 0; //after nightlight delay over +byte macroBoot = 0; // macro loaded after startup +byte macroNl = 0; // after nightlight delay over byte macroCountdown = 0; byte macroAlexaOn = 0, macroAlexaOff = 0; byte macroButton = 0, macroLongPress = 0, macroDoublePress = 0; -//Security CONFIG -bool otaLock = false; //prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks -bool wifiLock = false; //prevents access to WiFi settings when OTA lock is enabled -bool aOtaEnabled = true; //ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on +// Security CONFIG +bool otaLock = false; // prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks +bool wifiLock = false; // prevents access to WiFi settings when OTA lock is enabled +bool aOtaEnabled = true; // ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on uint16_t userVar0 = 0, userVar1 = 0; #ifdef WLED_ENABLE_DMX -//dmx CONFIG -byte DMXChannels = 7; // number of channels per fixture -byte DMXFixtureMap[15] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -// assigns the different channels to different functions. See wled21_dmx.ino for more information. -uint16_t DMXGap = 10; // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. -uint16_t DMXStart = 10; // start address of the first fixture + // dmx CONFIG + byte DMXChannels = 7; // number of channels per fixture + byte DMXFixtureMap[15] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + // assigns the different channels to different functions. See wled21_dmx.ino for more information. + uint16_t DMXGap = 10; // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. + uint16_t DMXStart = 10; // start address of the first fixture #endif -//internal global variable declarations -//wifi +// internal global variable declarations +// wifi bool apActive = false; bool forceReconnect = false; uint32_t lastReconnectAttempt = 0; bool interfacesInited = false; bool wasConnected = false; -//color -byte colOld[] { 0, 0, 0, 0 }; //color before transition -byte colT[] { 0, 0, 0, 0 }; //color that is currently displayed on the LEDs -byte colIT[] { 0, 0, 0, 0 }; //color that was last sent to LEDs +// color +byte colOld[] { 0, 0, 0, 0 }; // color before transition +byte colT[] { 0, 0, 0, 0 }; // color that is currently displayed on the LEDs +byte colIT[] { 0, 0, 0, 0 }; // color that was last sent to LEDs byte colSecT[] { 0, 0, 0, 0 }; byte colSecOld[] { 0, 0, 0, 0 }; byte colSecIT[] { 0, 0, 0, 0 }; -byte lastRandomIndex = 0; //used to save last random color so the new one is not the same +byte lastRandomIndex = 0; // used to save last random color so the new one is not the same -//transitions +// transitions bool transitionActive = false; uint16_t transitionDelayDefault = transitionDelay; uint16_t transitionDelayTemp = transitionDelay; unsigned long transitionStartTime; -float tperLast = 0; //crossfade transition progress, 0.0f - 1.0f +float tperLast = 0; // crossfade transition progress, 0.0f - 1.0f bool jsonTransitionOnce = false; -//nightlight +// nightlight bool nightlightActive = false; bool nightlightActiveOld = false; uint32_t nightlightDelayMs = 10; uint8_t nightlightDelayMinsDefault = nightlightDelayMins; unsigned long nightlightStartTime; -byte briNlT = 0; //current nightlight brightness -byte colNlT[] { 0, 0, 0, 0 }; //current nightlight color +byte briNlT = 0; // current nightlight brightness +byte colNlT[] { 0, 0, 0, 0 }; // current nightlight color -//brightness +// brightness unsigned long lastOnTime = 0; bool offMode = !turnOnAtBoot; byte bri = briS; byte briOld = 0; byte briT = 0; byte briIT = 0; -byte briLast = 128; //brightness before turned off. Used for toggle function -byte whiteLast = 128; //white channel before turned off. Used for toggle function +byte briLast = 128; // brightness before turned off. Used for toggle function +byte whiteLast = 128; // white channel before turned off. Used for toggle function -//button +// button bool buttonPressedBefore = false; bool buttonLongPressed = false; unsigned long buttonPressedTime = 0; unsigned long buttonWaitTime = 0; -//notifications +// notifications bool notifyDirectDefault = notifyDirect; bool receiveNotifications = true; unsigned long notificationSentTime = 0; byte notificationSentCallMode = NOTIFIER_CALL_MODE_INIT; bool notificationTwoRequired = false; -//effects +// effects byte effectCurrent = 0; byte effectSpeed = 128; byte effectIntensity = 128; byte effectPalette = 0; -//network +// network bool udpConnected = false, udpRgbConnected = false; -//ui style +// ui style bool showWelcomePage = false; -//hue +// hue byte hueError = HUE_ERROR_INACTIVE; -//uint16_t hueFailCount = 0; +// uint16_t hueFailCount = 0; float hueXLast = 0, hueYLast = 0; uint16_t hueHueLast = 0, hueCtLast = 0; byte hueSatLast = 0, hueBriLast = 0; @@ -244,32 +244,32 @@ bool hueAuthRequired = false; bool hueReceived = false; bool hueStoreAllowed = false, hueNewKey = false; -//overlays +// overlays byte overlayCurrent = overlayDefault; byte overlaySpeed = 200; unsigned long overlayRefreshMs = 200; unsigned long overlayRefreshedTime; -//cronixie +// cronixie byte dP[] { 0, 0, 0, 0, 0, 0 }; bool cronixieInit = false; -//countdown +// countdown unsigned long countdownTime = 1514764800L; bool countdownOverTriggered = true; -//timer +// timer byte lastTimerMinute = 0; byte timerHours[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; byte timerMinutes[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; byte timerMacro[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; -byte timerWeekday[] = { 255, 255, 255, 255, 255, 255, 255, 255 }; //weekdays to activate on -//bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity +byte timerWeekday[] = { 255, 255, 255, 255, 255, 255, 255, 255 }; // weekdays to activate on +// bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity -//blynk +// blynk bool blynkEnabled = false; -//preset cycling +// preset cycling bool presetCyclingEnabled = false; byte presetCycleMin = 1, presetCycleMax = 5; uint16_t presetCycleTime = 1250; @@ -278,35 +278,35 @@ byte presetCycCurr = presetCycleMin; bool presetApplyBri = true; bool saveCurrPresetCycConf = false; -//realtime +// realtime byte realtimeMode = REALTIME_MODE_INACTIVE; IPAddress realtimeIP = (0, 0, 0, 0); unsigned long realtimeTimeout = 0; -//mqtt +// mqtt long lastMqttReconnectAttempt = 0; long lastInterfaceUpdate = 0; byte interfaceUpdateCallMode = NOTIFIER_CALL_MODE_INIT; -char mqttStatusTopic[40] = ""; //this must be global because of async handlers +char mqttStatusTopic[40] = ""; // this must be global because of async handlers #if AUXPIN >= 0 - //auxiliary debug pin -byte auxTime = 0; -unsigned long auxStartTime = 0; -bool auxActive = false, auxActiveBefore = false; + // auxiliary debug pin + byte auxTime = 0; + unsigned long auxStartTime = 0; + bool auxActive = false, auxActiveBefore = false; #endif -//alexa udp +// alexa udp String escapedMac; #ifndef WLED_DISABLE_ALEXA -Espalexa espalexa; -EspalexaDevice* espalexaDevice; + Espalexa espalexa; + EspalexaDevice* espalexaDevice; #endif -//dns server +// dns server DNSServer dnsServer; -//network time +// network time bool ntpConnected = false; time_t local = 0; unsigned long ntpLastSyncTime = 999000000L; @@ -318,7 +318,7 @@ uint16_t ntpLocalPort = 2390; char* obuf; uint16_t olen = 0; -//presets +// presets uint16_t savedPresets = 0; int8_t currentPreset = -1; bool isPreset = false; @@ -328,42 +328,42 @@ byte errorFlag = 0; String messageHead, messageSub; byte optionType; -bool doReboot = false; //flag to initiate reboot from async handlers +bool doReboot = false; // flag to initiate reboot from async handlers bool doPublishMqtt = false; -//server library objects +// server library objects AsyncWebServer server(80); AsyncClient* hueClient = NULL; AsyncMqttClient* mqtt = NULL; -//udp interface objects +// udp interface objects WiFiUDP notifierUdp, rgbUdp; WiFiUDP ntpUdp; ESPAsyncE131 e131(handleE131Packet); bool e131NewData = false; -//led fx library object +// led fx library object WS2812FX strip = WS2812FX(); -//debug macro variable definitions +// debug macro variable definitions #ifdef WLED_DEBUG -unsigned long debugTime = 0; -int lastWifiState = 3; -unsigned long wifiStateChangedTime = 0; -int loops = 0; + unsigned long debugTime = 0; + int lastWifiState = 3; + unsigned long wifiStateChangedTime = 0; + int loops = 0; #endif WLED::WLED() { } -//turns all LEDs off and restarts ESP +// turns all LEDs off and restarts ESP void WLED::reset() { briT = 0; long dly = millis(); while (millis() - dly < 250) { - yield(); //enough time to send response to client + yield(); // enough time to send response to client } setAllLeds(); DEBUG_PRINTLN("MODULE RESET"); @@ -381,7 +381,7 @@ bool oappend(const char* txt) { uint16_t len = strlen(txt); if (olen + len >= OMAX) - return false; //buffer full + return false; // buffer full strcpy(obuf + olen, txt); olen += len; return true; @@ -389,7 +389,7 @@ bool oappend(const char* txt) void WLED::loop() { - handleIR(); //2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too + handleIR(); // 2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too handleConnection(); handleSerial(); handleNotifications(); @@ -414,7 +414,7 @@ void WLED::loop() if (doReboot) reset(); - if (!realtimeMode) //block stuff if WARLS/Adalight is enabled + if (!realtimeMode) // block stuff if WARLS/Adalight is enabled { if (apActive) dnsServer.processNextRequest(); @@ -439,7 +439,7 @@ void WLED::loop() if (millis() - lastMqttReconnectAttempt > 30000) initMqtt(); -//DEBUG serial logging +// DEBUG serial logging #ifdef WLED_DEBUG if (millis() - debugTime > 9999) { DEBUG_PRINTLN("---DEBUG INFO---"); @@ -467,7 +467,7 @@ void WLED::loop() debugTime = millis(); } loops++; -#endif // WLED_DEBU +#endif // WLED_DEBU } void WLED::setup() @@ -478,10 +478,10 @@ void WLED::setup() ledCount = 30; #ifdef ESP8266 -#if LEDPIN == 3 - if (ledCount > MAX_LEDS_DMA) - ledCount = MAX_LEDS_DMA; //DMA method uses too much ram -#endif + #if LEDPIN == 3 + if (ledCount > MAX_LEDS_DMA) + ledCount = MAX_LEDS_DMA; // DMA method uses too much ram + #endif #endif Serial.begin(115200); Serial.setTimeout(50); @@ -502,17 +502,17 @@ void WLED::setup() DEBUG_PRINT("heap "); DEBUG_PRINTLN(ESP.getFreeHeap()); - strip.init(EEPROM.read(372), ledCount, EEPROM.read(2204)); //init LEDs quickly + strip.init(EEPROM.read(372), ledCount, EEPROM.read(2204)); // init LEDs quickly strip.setBrightness(0); DEBUG_PRINT("LEDs inited. heap usage ~"); DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap()); #ifndef WLED_DISABLE_FILESYSTEM -#ifdef ARDUINO_ARCH_ESP32 - SPIFFS.begin(true); -#endif - SPIFFS.begin(); + #ifdef ARDUINO_ARCH_ESP32 + SPIFFS.begin(true); + #endif + SPIFFS.begin(); #endif DEBUG_PRINTLN("Load EEPROM"); @@ -527,11 +527,11 @@ void WLED::setup() applyMacro(macroBoot); Serial.println("Ada"); - //generate module IDs + // generate module IDs escapedMac = WiFi.macAddress(); escapedMac.replace(":", ""); escapedMac.toLowerCase(); - if (strcmp(cmDNS, "x") == 0) //fill in unique mdns default + if (strcmp(cmDNS, "x") == 0) // fill in unique mdns default { strcpy(cmDNS, "wled-"); sprintf(cmDNS + 5, "%*s", 6, escapedMac.c_str() + 6); @@ -560,9 +560,9 @@ void WLED::setup() } #endif #ifdef WLED_ENABLE_DMX - dmx.init(512); // initialize with bus length + dmx.init(512); // initialize with bus length #endif - //HTTP server page init + // HTTP server page init initServer(); } @@ -579,7 +579,7 @@ void WLED::beginStrip() applyPreset(bootPreset, turnOnAtBoot); colorUpdated(NOTIFIER_CALL_MODE_INIT); -//init relay pin +// init relay pin #if RLYPIN >= 0 pinMode(RLYPIN, OUTPUT); #if RLYMDE @@ -589,7 +589,7 @@ void WLED::beginStrip() #endif #endif - //disable button if it is "pressed" unintentionally + // disable button if it is "pressed" unintentionally #ifdef BTNPIN if (digitalRead(BTNPIN) == LOW) buttonEnabled = false; @@ -612,7 +612,7 @@ void WLED::initAP(bool resetAP) WiFi.softAPConfig(IPAddress(4, 3, 2, 1), IPAddress(4, 3, 2, 1), IPAddress(255, 255, 255, 0)); WiFi.softAP(apSSID, apPass, apChannel, apHide); - if (!apActive) //start captive portal if AP active + if (!apActive) // start captive portal if AP active { DEBUG_PRINTLN("Init AP interfaces"); server.begin(); @@ -631,7 +631,7 @@ void WLED::initAP(bool resetAP) void WLED::initConnection() { - WiFi.disconnect(); //close old connections + WiFi.disconnect(); // close old connections #ifdef ESP8266 WiFi.setPhyMode(WIFI_PHY_MODE_11N); #endif @@ -647,7 +647,7 @@ void WLED::initConnection() if (!WLED_WIFI_CONFIGURED) { DEBUG_PRINT("No connection configured. "); if (!apActive) - initAP(); //instantly go to ap mode + initAP(); // instantly go to ap mode return; } else if (!apActive) { if (apBehavior == AP_BEHAVIOR_ALWAYS) { @@ -687,7 +687,7 @@ void WLED::initInterfaces() hueIP[2] = WiFi.localIP()[2]; } - //init Alexa hue emulation + // init Alexa hue emulation if (alexaEnabled) alexaInit(); @@ -736,7 +736,7 @@ void WLED::handleConnection() if (lastReconnectAttempt == 0) initConnection(); - //reconnect WiFi to clear stale allocations if heap gets too low + // reconnect WiFi to clear stale allocations if heap gets too low if (millis() - heapTime > 5000) { uint32_t heap = ESP.getFreeHeap(); if (heap < 9000 && lastHeap < 9000) { @@ -761,11 +761,11 @@ void WLED::handleConnection() stacO = stac; DEBUG_PRINT("Connected AP clients: "); DEBUG_PRINTLN(stac); - if (!WLED_CONNECTED && WLED_WIFI_CONFIGURED) { //trying to connect, but not connected + if (!WLED_CONNECTED && WLED_WIFI_CONFIGURED) { // trying to connect, but not connected if (stac) - WiFi.disconnect(); //disable search so that AP can work + WiFi.disconnect(); // disable search so that AP can work else - initConnection(); //restart search + initConnection(); // restart search } } } @@ -787,14 +787,14 @@ void WLED::handleConnection() initConnection(); if (!apActive && millis() - lastReconnectAttempt > 12000 && (!wasConnected || apBehavior == AP_BEHAVIOR_NO_CONN)) initAP(); - } else if (!interfacesInited) { //newly connected + } else if (!interfacesInited) { // newly connected DEBUG_PRINTLN(""); DEBUG_PRINT("Connected! IP address: "); DEBUG_PRINTLN(WiFi.localIP()); initInterfaces(); userConnected(); - //shut down AP + // shut down AP if (apBehavior != AP_BEHAVIOR_ALWAYS && apActive) { dnsServer.stop(); WiFi.softAPdisconnect(true); diff --git a/wled00/wled.h b/wled00/wled.h index 3e6c11be..72686a00 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -14,41 +14,41 @@ // Alternatively, with platformio pass your chosen flags to your custom build target in platformio.ini.override // You are required to disable over-the-air updates: -//#define WLED_DISABLE_OTA //saves 14kb +//#define WLED_DISABLE_OTA // saves 14kb // You need to choose some of these features to disable: -//#define WLED_DISABLE_ALEXA //saves 11kb -//#define WLED_DISABLE_BLYNK //saves 6kb -//#define WLED_DISABLE_CRONIXIE //saves 3kb -//#define WLED_DISABLE_HUESYNC //saves 4kb -//#define WLED_DISABLE_INFRARED //there is no pin left for this on ESP8266-01, saves 12kb -#define WLED_ENABLE_MQTT //saves 12kb -#define WLED_ENABLE_ADALIGHT //saves 500b only -//#define WLED_ENABLE_DMX //uses 3.5kb +//#define WLED_DISABLE_ALEXA // saves 11kb +//#define WLED_DISABLE_BLYNK // saves 6kb +//#define WLED_DISABLE_CRONIXIE // saves 3kb +//#define WLED_DISABLE_HUESYNC // saves 4kb +//#define WLED_DISABLE_INFRARED // there is no pin left for this on ESP8266-01, saves 12kb +#define WLED_ENABLE_MQTT // saves 12kb +#define WLED_ENABLE_ADALIGHT // saves 500b only +//#define WLED_ENABLE_DMX // uses 3.5kb -#define WLED_DISABLE_FILESYSTEM //SPIFFS is not used by any WLED feature yet -//#define WLED_ENABLE_FS_SERVING //Enable sending html file from SPIFFS before serving progmem version -//#define WLED_ENABLE_FS_EDITOR //enable /edit page for editing SPIFFS content. Will also be disabled with OTA lock +#define WLED_DISABLE_FILESYSTEM // SPIFFS is not used by any WLED feature yet +//#define WLED_ENABLE_FS_SERVING // Enable sending html file from SPIFFS before serving progmem version +//#define WLED_ENABLE_FS_EDITOR // enable /edit page for editing SPIFFS content. Will also be disabled with OTA lock // to toggle usb serial debug (un)comment the following line //#define WLED_DEBUG // Library inclusions. #include -#ifdef ESP8266 -#include -#include -#include -extern "C" -{ -#include -} -#else //ESP32 -#include -#include "esp_wifi.h" -#include -#include -#include "SPIFFS.h" + #ifdef ESP8266 + #include + #include + #include + extern "C" + { + #include + } +#else // ESP32 + #include + #include "esp_wifi.h" + #include + #include + #include "SPIFFS.h" #endif #include @@ -56,25 +56,28 @@ extern "C" #include #include #ifndef WLED_DISABLE_OTA -#include + #include #endif #include #include "src/dependencies/time/TimeLib.h" #include "src/dependencies/timezone/Timezone.h" + #ifndef WLED_DISABLE_ALEXA -#define ESPALEXA_ASYNC -#define ESPALEXA_NO_SUBPAGE -#define ESPALEXA_MAXDEVICES 1 -// #define ESPALEXA_DEBUG -#include "src/dependencies/espalexa/Espalexa.h" + #define ESPALEXA_ASYNC + #define ESPALEXA_NO_SUBPAGE + #define ESPALEXA_MAXDEVICES 1 + // #define ESPALEXA_DEBUG + #include "src/dependencies/espalexa/Espalexa.h" #endif #ifndef WLED_DISABLE_BLYNK -#include "src/dependencies/blynk/BlynkSimpleEsp.h" + #include "src/dependencies/blynk/BlynkSimpleEsp.h" #endif + #include "src/dependencies/e131/ESPAsyncE131.h" #include "src/dependencies/async-mqtt-client/AsyncMqttClient.h" #include "src/dependencies/json/AsyncJson-v6.h" #include "src/dependencies/json/ArduinoJson-v6.h" + #include "html_ui.h" #include "html_settings.h" #include "html_other.h" @@ -83,35 +86,35 @@ extern "C" #include "const.h" #ifndef CLIENT_SSID -#define CLIENT_SSID DEFAULT_CLIENT_SSID + #define CLIENT_SSID DEFAULT_CLIENT_SSID #endif #ifndef CLIENT_PASS -#define CLIENT_PASS "" + #define CLIENT_PASS "" #endif #if IR_PIN < 0 -#ifndef WLED_DISABLE_INFRARED -#define WLED_DISABLE_INFRARED -#endif + #ifndef WLED_DISABLE_INFRARED + #define WLED_DISABLE_INFRARED + #endif #endif #ifndef WLED_DISABLE_INFRARED -#include -#include -#include + #include + #include + #include #endif // remove flicker because PWM signal of RGB channels can become out of phase #if defined(WLED_USE_ANALOG_LEDS) && defined(ESP8266) -#include "src/dependencies/arduino/core_esp8266_waveform.h" + #include "src/dependencies/arduino/core_esp8266_waveform.h" #endif // enable additional debug output #ifdef WLED_DEBUG -#ifndef ESP8266 -#include -#endif + #ifndef ESP8266 + #include + #endif #endif // version code in format yymmddb (b = daily build) @@ -137,7 +140,7 @@ extern IPAddress staticSubnet; extern bool noWifiSleep; extern uint16_t ledCount; extern bool useRGBW; -#define ABL_MILLIAMPS_DEFAULT 850; //auto lower brightness to stay close to milliampere limit +#define ABL_MILLIAMPS_DEFAULT 850; // auto lower brightness to stay close to milliampere limit extern bool turnOnAtBoot; extern byte bootPreset; extern byte col[]; @@ -224,11 +227,10 @@ extern bool wifiLock; extern bool aOtaEnabled; extern uint16_t userVar0, userVar1; #ifdef WLED_ENABLE_DMX -extern byte DMXChannels; -extern byte DMXFixtureMap[15]; -extern -extern uint16_t DMXGap; -extern uint16_t DMXStart; + extern byte DMXChannels; + extern byte DMXFixtureMap[15]; + extern uint16_t DMXGap; + extern uint16_t DMXStart; #endif extern bool apActive; extern bool forceReconnect; @@ -315,15 +317,17 @@ extern long lastInterfaceUpdate; extern byte interfaceUpdateCallMode; extern char mqttStatusTopic[40]; #if AUXPIN >= 0 -extern byte auxTime; -extern unsigned long auxStartTime; -extern bool auxActive; + extern byte auxTime; + extern unsigned long auxStartTime; + extern bool auxActive; #endif extern String escapedMac; #ifndef WLED_DISABLE_ALEXA -extern Espalexa espalexa; -extern EspalexaDevice *espalexaDevice; + extern Espalexa espalexa; + extern EspalexaDevice *espalexaDevice; #endif + +#define NTP_PACKET_SIZE 48 extern DNSServer dnsServer; extern bool ntpConnected; extern time_t local; @@ -331,15 +335,14 @@ extern unsigned long ntpLastSyncTime; extern unsigned long ntpPacketSentTime; extern IPAddress ntpServerIP; extern uint16_t ntpLocalPort; -#define NTP_PACKET_SIZE 48 -//maximum number of LEDs - MAX_LEDS is coming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue +// maximum number of LEDs - MAX_LEDS is coming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue #define MAX_LEDS 1500 #define MAX_LEDS_DMA 500 -//string temp buffer (now stored in stack locally) +// string temp buffer (now stored in stack locally) #define OMAX 2048 -extern char *obuf; +extern char* obuf; extern uint16_t olen; extern uint16_t savedPresets; @@ -348,11 +351,11 @@ extern bool isPreset; extern byte errorFlag; extern String messageHead, messageSub; extern byte optionType; -extern bool doReboot; +extern bool doReboot; extern bool doPublishMqtt; extern AsyncWebServer server; -extern AsyncClient *hueClient; -extern AsyncMqttClient *mqtt; +extern AsyncClient* hueClient; +extern AsyncMqttClient* mqtt; extern WiFiUDP notifierUdp, rgbUdp; extern WiFiUDP ntpUdp; extern ESPAsyncE131 e131; @@ -362,46 +365,45 @@ extern WS2812FX strip; #define WLED_CONNECTED (WiFi.status() == WL_CONNECTED) #define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID, DEFAULT_CLIENT_SSID) != 0) -//debug macros +// debug macros #ifdef WLED_DEBUG -#define DEBUG_PRINT(x) Serial.print(x) -#define DEBUG_PRINTLN(x) Serial.println(x) -#define DEBUG_PRINTF(x) Serial.printf(x) -extern unsigned long debugTime; -extern int lastWifiState; -extern unsigned long wifiStateChangedTime; -extern int loops; + #define DEBUG_PRINT(x) Serial.print(x) + #define DEBUG_PRINTLN(x) Serial.println(x) + #define DEBUG_PRINTF(x) Serial.printf(x) + extern unsigned long debugTime; + extern int lastWifiState; + extern unsigned long wifiStateChangedTime; + extern int loops; #else -#define DEBUG_PRINT(x) -#define DEBUG_PRINTLN(x) -#define DEBUG_PRINTF(x) + #define DEBUG_PRINT(x) + #define DEBUG_PRINTLN(x) + #define DEBUG_PRINTF(x) #endif // append new c string to temp buffer efficiently -bool oappend(const char *txt); +bool oappend(const char* txt); // append new number to temp buffer efficiently bool oappendi(int i); -class WLED -{ +class WLED { public: - WLED(); - static WLED &instance() - { - static WLED instance; - return instance; - } + WLED(); + static WLED& instance() + { + static WLED instance; + return instance; + } - //boot starts here - void setup(); + // boot starts here + void setup(); - void loop(); - void reset(); + void loop(); + void reset(); - void beginStrip(); - void handleConnection(); - void initAP(bool resetAP = false); - void initConnection(); - void initInterfaces(); + void beginStrip(); + void handleConnection(); + void initAP(bool resetAP = false); + void initConnection(); + void initInterfaces(); }; -#endif // WLED_H +#endif // WLED_H From aea07f42d1a55155d79269ec775959a41c8e38fd Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Mon, 30 Mar 2020 06:56:51 -0400 Subject: [PATCH 199/232] Fix mistaken substitute. --- wled00/FX.cpp | 50 +++++++++++++++++++++++------------------------ wled00/FX_fcn.cpp | 2 +- wled00/blynk.h | 2 +- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index e5408a42..7230a9b3 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -268,7 +268,7 @@ uint16_t WS2812FX::mode_breath(void) { counter = (counter >> 2) + (counter >> 4); //0-16384 + 0-2048 if (counter < 16384) { if (counter > 8192) counter = 8192 - (counter - 8192); - var = sin16(counter) / 103; //close to parabolic in range 0-8192, MAX val. 23170 + var = sin16(counter) / 103; //close to parabolic in range 0-8192, max val. 23170 } uint8_t lum = 30 + var; @@ -475,8 +475,8 @@ uint16_t WS2812FX::mode_twinkle(void) { uint32_t it = now / cycleTime; if (it != SEGENV.step) { - uint16_t MAXOn = map(SEGMENT.intensity, 0, 255, 1, SEGLEN); // make sure at least one LED is on - if (SEGENV.aux0 >= MAXOn) + uint16_t maxOn = map(SEGMENT.intensity, 0, 255, 1, SEGLEN); // make sure at least one LED is on + if (SEGENV.aux0 >= maxOn) { SEGENV.aux0 = 0; SEGENV.aux1 = random16(); //new seed for our PRNG @@ -1597,8 +1597,8 @@ uint16_t WS2812FX::mode_oscillate(void) uint16_t WS2812FX::mode_lightning(void) { - uint16_t ledstart = random16(SEGLEN); // DeterMINe starting location of flash - uint16_t ledlen = random16(SEGLEN -1 -ledstart); // DeterMINe length of flash (not to go beyond NUM_LEDS-1) + uint16_t ledstart = random16(SEGLEN); // Determine starting location of flash + uint16_t ledlen = random16(SEGLEN -1 -ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1) uint8_t bri = 255/random8(1, 3); if (SEGENV.step == 0) @@ -2153,9 +2153,9 @@ typedef struct Ripple { uint16_t WS2812FX::ripple_base(bool rainbow) { - uint16_t MAXRipples = 1 + (SEGLEN >> 2); - if (MAXRipples > 100) MAXRipples = 100; - uint16_t dataSize = sizeof(ripple) * MAXRipples; + uint16_t maxRipples = 1 + (SEGLEN >> 2); + if (maxRipples > 100) maxRipples = 100; + uint16_t dataSize = sizeof(ripple) * maxRipples; if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed @@ -2181,7 +2181,7 @@ uint16_t WS2812FX::ripple_base(bool rainbow) } //draw wave - for (uint16_t i = 0; i < MAXRipples; i++) + for (uint16_t i = 0; i < maxRipples; i++) { uint16_t ripplestate = ripples[i].state; if (ripplestate) @@ -2480,8 +2480,8 @@ uint16_t WS2812FX::spots_base(uint16_t threshold) { fill(SEGCOLOR(1)); - uint16_t MAXZones = SEGLEN >> 2; - uint16_t zones = 1 + ((SEGMENT.intensity * MAXZones) >> 8); + uint16_t maxZones = SEGLEN >> 2; + uint16_t zones = 1 + ((SEGMENT.intensity * maxZones) >> 8); uint16_t zoneLen = SEGLEN / zones; uint16_t offset = (SEGLEN - zones * zoneLen) >> 1; @@ -2533,15 +2533,15 @@ typedef struct Ball { */ uint16_t WS2812FX::mode_bouncing_balls(void) { //allocate segment data - uint16_t MAXNumBalls = 16; - uint16_t dataSize = sizeof(ball) * MAXNumBalls; + uint16_t maxNumBalls = 16; + uint16_t dataSize = sizeof(ball) * maxNumBalls; if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed Ball* balls = reinterpret_cast(SEGENV.data); - // number of balls based on intensity setting to MAX of 7 (cycles colors) + // number of balls based on intensity setting to max of 7 (cycles colors) // non-chosen color is a random color - uint8_t numBalls = int(((SEGMENT.intensity * (MAXNumBalls - 0.8f)) / 255) + 1); + uint8_t numBalls = int(((SEGMENT.intensity * (maxNumBalls - 0.8f)) / 255) + 1); float gravity = -9.81; // standard value of gravity float impactVelocityStart = sqrt( -2 * gravity); @@ -2549,7 +2549,7 @@ uint16_t WS2812FX::mode_bouncing_balls(void) { unsigned long time = millis(); if (SEGENV.call == 0) { - for (uint8_t i = 0; i < MAXNumBalls; i++) balls[i].lastBounceTime = time; + for (uint8_t i = 0; i < maxNumBalls; i++) balls[i].lastBounceTime = time; } bool hasCol2 = SEGCOLOR(2); @@ -2665,8 +2665,8 @@ typedef struct Spark { */ uint16_t WS2812FX::mode_popcorn(void) { //allocate segment data - uint16_t MAXNumPopcorn = 24; - uint16_t dataSize = sizeof(spark) * MAXNumPopcorn; + uint16_t maxNumPopcorn = 24; + uint16_t dataSize = sizeof(spark) * maxNumPopcorn; if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed Spark* popcorn = reinterpret_cast(SEGENV.data); @@ -2677,7 +2677,7 @@ uint16_t WS2812FX::mode_popcorn(void) { bool hasCol2 = SEGCOLOR(2); fill(hasCol2 ? BLACK : SEGCOLOR(1)); - uint8_t numPopcorn = SEGMENT.intensity*MAXNumPopcorn/255; + uint8_t numPopcorn = SEGMENT.intensity*maxNumPopcorn/255; if (numPopcorn == 0) numPopcorn = 1; for(uint8_t i = 0; i < numPopcorn; i++) { @@ -2792,13 +2792,13 @@ uint16_t WS2812FX::mode_starburst(void) { star* stars = reinterpret_cast(SEGENV.data); - float MAXSpeed = 375.0f; // Max velocity + float maxSpeed = 375.0f; // Max velocity float particleIgnition = 250.0f; // How long to "flash" float particleFadeTime = 1500.0f; // Fade out time for (int j = 0; j < numStars; j++) { - // speed to adjust chance of a burst, MAX is nearly always. + // speed to adjust chance of a burst, max is nearly always. if (random8((144-(SEGMENT.speed >> 1))) == 0 && stars[j].birth == 0) { // Pick a random color and location. @@ -2807,7 +2807,7 @@ uint16_t WS2812FX::mode_starburst(void) { stars[j].color = col_to_crgb(color_wheel(random8())); stars[j].pos = startPos; - stars[j].vel = MAXSpeed * (float)(random8())/255.0 * multiplier; + stars[j].vel = maxSpeed * (float)(random8())/255.0 * multiplier; stars[j].birth = it; stars[j].last = it; // more fragments means larger burst effect @@ -3026,7 +3026,7 @@ uint16_t WS2812FX::mode_drip(void) drops[j].pos = SEGLEN-1; // start at end drops[j].vel = 0; // speed drops[j].col = sourcedrop; // brightness - drops[j].colIndex = 1; // drop state (0 init, 1 forMINg, 2 falling, 5 bouncing) + drops[j].colIndex = 1; // drop state (0 init, 1 forming, 2 falling, 5 bouncing) } setPixelColor(SEGLEN-1,color_blend(BLACK,SEGCOLOR(0), sourcedrop));// water source @@ -3047,7 +3047,7 @@ uint16_t WS2812FX::mode_drip(void) if (drops[j].pos < 0) drops[j].pos = 0; drops[j].vel += gravity; - for (int i=1;i<7-drops[j].colIndex;i++) { // some MINor math so we don't expand bouncing droplets + for (int i=1;i<7-drops[j].colIndex;i++) { // some minor math so we don't expand bouncing droplets setPixelColor(int(drops[j].pos)+i,color_blend(BLACK,SEGCOLOR(0),drops[j].col/i)); //spread pixel with fade while falling } @@ -3055,7 +3055,7 @@ uint16_t WS2812FX::mode_drip(void) setPixelColor(0,color_blend(SEGCOLOR(0),BLACK,drops[j].col)); } } else { // we hit bottom - if (drops[j].colIndex > 2) { // already hit once, so back to forMINg + if (drops[j].colIndex > 2) { // already hit once, so back to forming drops[j].colIndex = 0; drops[j].col = sourcedrop; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 75ec6510..de89a430 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -638,7 +638,7 @@ uint32_t WS2812FX::color_wheel(uint8_t pos) { } /* - * Returns a new, random wheel index with a MINimum distance of 42 from pos. + * Returns a new, random wheel index with a minimum distance of 42 from pos. */ uint8_t WS2812FX::get_random_wheel_index(uint8_t pos) { uint8_t r = 0, x = 0, y = 0, d = 0; diff --git a/wled00/blynk.h b/wled00/blynk.h index cf3a2e22..f17f774a 100644 --- a/wled00/blynk.h +++ b/wled00/blynk.h @@ -8,6 +8,6 @@ void initBlynk(const char* auth); void handleBlynk(); void updateBlynk(); -// Unsure if the macro expansions need to accessed through the declaration... TODO +// TODO: Make sure that the macro expansions are handled correctly. #endif //WLED_BLYNK_H \ No newline at end of file From 8ff4f50f79aa0f663f25408c621d235225734272 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Mon, 30 Mar 2020 07:24:45 -0400 Subject: [PATCH 200/232] Fix visual studio project files and organize. --- wled00/wled00.vcxproj | 317 +++++----------- wled00/wled00.vcxproj.filters | 657 ++++++++++++++++++++-------------- 2 files changed, 483 insertions(+), 491 deletions(-) diff --git a/wled00/wled00.vcxproj b/wled00/wled00.vcxproj index 2183ad26..e095b8f7 100644 --- a/wled00/wled00.vcxproj +++ b/wled00/wled00.vcxproj @@ -104,231 +104,103 @@ CppCode - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -350,6 +222,11 @@ + + + + + VisualMicroDebugger @@ -362,4 +239,4 @@ - \ No newline at end of file + diff --git a/wled00/wled00.vcxproj.filters b/wled00/wled00.vcxproj.filters index e2c2b219..ef1f47bd 100644 --- a/wled00/wled00.vcxproj.filters +++ b/wled00/wled00.vcxproj.filters @@ -1,272 +1,387 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - - - - - - - - - - - - - - - - - - - - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {72fe60da-ba26-45b4-82c1-bdff809975da} + + + {8880888d-efea-4189-a25a-834b7b3bb756} + + + + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + \ No newline at end of file From a9a7720a1158dc5b7a2c95c430bcebbc14c4ae3f Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Mon, 30 Mar 2020 07:45:33 -0400 Subject: [PATCH 201/232] Import dmx output library into project. --- wled00/dmx.h | 39 ++++----- wled00/src/dependencies/dmx/ESPDMX.cpp | 106 +++++++++++++++++++++++++ wled00/src/dependencies/dmx/ESPDMX.h | 31 ++++++++ wled00/wled.cpp | 1 + 4 files changed, 158 insertions(+), 19 deletions(-) create mode 100644 wled00/src/dependencies/dmx/ESPDMX.cpp create mode 100644 wled00/src/dependencies/dmx/ESPDMX.h diff --git a/wled00/dmx.h b/wled00/dmx.h index 76aab484..0ab2a4a2 100644 --- a/wled00/dmx.h +++ b/wled00/dmx.h @@ -1,64 +1,65 @@ #ifndef WLED_DMX_H #define WLED_DMX_H -#include "wled.h" +#include "wled.h" /* * Support for DMX via MAX485. - * Needs the espdmx library. You might have to change the output pin within the library. Sketchy, i know. + * Change the output pin in src/dependencies/ESPDMX.cpp if needed. + * Library from: * https://github.com/Rickgg/ESP-Dmx */ #ifdef WLED_ENABLE_DMX -#include +#include "src/dependencies/dmx/ESPDMX.h" DMXESPSerial dmx; -#ifdef WLED_ENABLE_DMX -void handleDMX() { +void handleDMX() +{ // TODO: calculate brightness manually if no shutter channel is set - + uint8_t brightness = strip.getBrightness(); - for (int i = 0; i < ledCount; i++) { // uses the amount of LEDs as fixture count + for (int i = 0; i < ledCount; i++) { // uses the amount of LEDs as fixture count - uint32_t in = strip.getPixelColor(i); // time to get the colors for the individual fixtures as suggested by AirCookie at issue #462 + uint32_t in = strip.getPixelColor(i); // time to get the colors for the individual fixtures as suggested by AirCookie at issue #462 byte w = in >> 24 & 0xFF; byte r = in >> 16 & 0xFF; - byte g = in >> 8 & 0xFF; - byte b = in & 0xFF; + byte g = in >> 8 & 0xFF; + byte b = in & 0xFF; int DMXFixtureStart = DMXStart + (DMXGap * i); for (int j = 0; j < DMXChannels; j++) { int DMXAddr = DMXFixtureStart + j; switch (DMXFixtureMap[j]) { - case 0: // Set this channel to 0. Good way to tell strobe- and fade-functions to fuck right off. + case 0: // Set this channel to 0. Good way to tell strobe- and fade-functions to fuck right off. dmx.write(DMXAddr, 0); break; - case 1: // Red + case 1: // Red dmx.write(DMXAddr, r); break; - case 2: // Green + case 2: // Green dmx.write(DMXAddr, g); break; - case 3: // Blue + case 3: // Blue dmx.write(DMXAddr, b); break; - case 4: // White + case 4: // White dmx.write(DMXAddr, w); break; - case 5: // Shutter channel. Controls the brightness. + case 5: // Shutter channel. Controls the brightness. dmx.write(DMXAddr, brightness); break; - case 6:// Sets this channel to 255. Like 0, but more wholesome. + case 6: // Sets this channel to 255. Like 0, but more wholesome. dmx.write(DMXAddr, 255); break; } } } - dmx.update(); // update the DMX bus + dmx.update(); // update the DMX bus } #else void handleDMX() {} #endif -#endif //WLED_DMX_H \ No newline at end of file +#endif //WLED_DMX_H \ No newline at end of file diff --git a/wled00/src/dependencies/dmx/ESPDMX.cpp b/wled00/src/dependencies/dmx/ESPDMX.cpp new file mode 100644 index 00000000..f3ece1c8 --- /dev/null +++ b/wled00/src/dependencies/dmx/ESPDMX.cpp @@ -0,0 +1,106 @@ +// - - - - - +// ESPDMX - A Arduino library for sending and receiving DMX using the builtin serial hardware port. +// ESPDMX.cpp: Library implementation file +// +// Copyright (C) 2015 Rick +// This work is licensed under a GNU style license. +// +// Last change: Marcel Seerig +// +// Documentation and samples are available at https://github.com/Rickgg/ESP-Dmx +// - - - - - + +/* ----- LIBRARIES ----- */ +#include + +#include "ESPDMX.h" + + + +#define dmxMaxChannel 512 +#define defaultMax 32 + +#define DMXSPEED 250000 +#define DMXFORMAT SERIAL_8N2 +#define BREAKSPEED 83333 +#define BREAKFORMAT SERIAL_8N1 + +bool dmxStarted = false; +int sendPin = 2; //dafault on ESP8266 + +//DMX value array and size. Entry 0 will hold startbyte +uint8_t dmxData[dmxMaxChannel] = {}; +int chanSize; + + +void DMXESPSerial::init() { + chanSize = defaultMax; + + Serial1.begin(DMXSPEED); + pinMode(sendPin, OUTPUT); + dmxStarted = true; +} + +// Set up the DMX-Protocol +void DMXESPSerial::init(int chanQuant) { + + if (chanQuant > dmxMaxChannel || chanQuant <= 0) { + chanQuant = defaultMax; + } + + chanSize = chanQuant; + + Serial1.begin(DMXSPEED); + pinMode(sendPin, OUTPUT); + dmxStarted = true; +} + +// Function to read DMX data +uint8_t DMXESPSerial::read(int Channel) { + if (dmxStarted == false) init(); + + if (Channel < 1) Channel = 1; + if (Channel > dmxMaxChannel) Channel = dmxMaxChannel; + return(dmxData[Channel]); +} + +// Function to send DMX data +void DMXESPSerial::write(int Channel, uint8_t value) { + if (dmxStarted == false) init(); + + if (Channel < 1) Channel = 1; + if (Channel > chanSize) Channel = chanSize; + if (value < 0) value = 0; + if (value > 255) value = 255; + + dmxData[Channel] = value; +} + +void DMXESPSerial::end() { + delete dmxData; + chanSize = 0; + Serial1.end(); + dmxStarted == false; +} + +void DMXESPSerial::update() { + if (dmxStarted == false) init(); + + //Send break + digitalWrite(sendPin, HIGH); + Serial1.begin(BREAKSPEED, BREAKFORMAT); + Serial1.write(0); + Serial1.flush(); + delay(1); + Serial1.end(); + + //send data + Serial1.begin(DMXSPEED, DMXFORMAT); + digitalWrite(sendPin, LOW); + Serial1.write(dmxData, chanSize); + Serial1.flush(); + delay(1); + Serial1.end(); +} + +// Function to update the DMX bus diff --git a/wled00/src/dependencies/dmx/ESPDMX.h b/wled00/src/dependencies/dmx/ESPDMX.h new file mode 100644 index 00000000..4585bdd2 --- /dev/null +++ b/wled00/src/dependencies/dmx/ESPDMX.h @@ -0,0 +1,31 @@ +// - - - - - +// ESPDMX - A Arduino library for sending and receiving DMX using the builtin serial hardware port. +// ESPDMX.cpp: Library implementation file +// +// Copyright (C) 2015 Rick +// This work is licensed under a GNU style license. +// +// Last change: Marcel Seerig +// +// Documentation and samples are available at https://github.com/Rickgg/ESP-Dmx +// - - - - - + +#include + + +#ifndef ESPDMX_h +#define ESPDMX_h + +// ---- Methods ---- + +class DMXESPSerial { +public: + void init(); + void init(int MaxChan); + uint8_t read(int Channel); + void write(int channel, uint8_t value); + void update(); + void end(); +}; + +#endif diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 48231599..fdb60776 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -2,6 +2,7 @@ #include "alexa.h" #include "blynk.h" #include "button.h" +#include "dmx.h" #include "file.h" #include "hue.h" #include "ir.h" From ad70fcba7fc2dbda16ec78b27a1ad443378a5ef4 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Mon, 30 Mar 2020 08:00:58 -0400 Subject: [PATCH 202/232] Typo fix. --- wled00/overlay.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wled00/overlay.h b/wled00/overlay.h index b403a6e2..f7b54d5d 100644 --- a/wled00/overlay.h +++ b/wled00/overlay.h @@ -1,5 +1,5 @@ -#ifndef WLED_OVERLAYS_H -#define WLED_OVERLAYS_H +#ifndef WLED_OVERLAY_H +#define WLED_OVERLAY_H #include /* * Used to draw clock overlays over the strip From cf29ddc8d0d25a240648f044d798eb4ebd59f710 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Mon, 30 Mar 2020 08:03:55 -0400 Subject: [PATCH 203/232] Typo fix. --- wled00/usermod.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/usermod.cpp b/wled00/usermod.cpp index 6589c07c..96c1f8a6 100644 --- a/wled00/usermod.cpp +++ b/wled00/usermod.cpp @@ -2,7 +2,7 @@ /* * This file allows you to add own functionality to WLED more easily * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality - * EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in wled01_wled_eeprom.h) + * EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in wled_eeprom.h) * bytes 2400+ are currently ununsed, but might be used for future wled features */ From 9875fbef0aa2c159eec8496dfd77fda80e8cd930 Mon Sep 17 00:00:00 2001 From: Travis J Dean Date: Mon, 30 Mar 2020 08:34:20 -0400 Subject: [PATCH 204/232] Layout changes. --- wled00/wled.h | 2 +- wled00/wled00.vcxproj.filters | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/wled00/wled.h b/wled00/wled.h index 72686a00..70b197c8 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -35,7 +35,7 @@ // Library inclusions. #include - #ifdef ESP8266 +#ifdef ESP8266 #include #include #include diff --git a/wled00/wled00.vcxproj.filters b/wled00/wled00.vcxproj.filters index ef1f47bd..39fe509e 100644 --- a/wled00/wled00.vcxproj.filters +++ b/wled00/wled00.vcxproj.filters @@ -135,9 +135,6 @@ Header Files\Dependencies - - Header Files\Dependencies - Header Files\Dependencies @@ -147,9 +144,6 @@ Header Files\Dependencies - - Header Files\Dependencies - Header Files\Dependencies @@ -258,6 +252,12 @@ Header Files + + Header Files + + + Header Files + From a88ae2ca56a277d070906832a534960f1eecc26c Mon Sep 17 00:00:00 2001 From: Travis Dean Date: Mon, 30 Mar 2020 08:48:01 -0400 Subject: [PATCH 205/232] Delete .clang-format --- .clang-format | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 .clang-format diff --git a/.clang-format b/.clang-format deleted file mode 100644 index aedd2246..00000000 --- a/.clang-format +++ /dev/null @@ -1,12 +0,0 @@ ---- - BasedOnStyle: Webkit - IndentWidth: 2 - AlignTrailingComments: true - SpacesBeforeTrailingComments: 8 - AllowShortIfStatementsOnASingleLine: Always - AllowShortLoopsOnASingleLine: true - AllowShortLambdasOnASingleLine: true - AllowShortCaseLabelsOnASingleLine: true - AllowShortFunctionsOnASingleLine: All - AllowShortBlocksOnASingleLine: true - IndentCaseLabels: true \ No newline at end of file From 6268cadc950db2e58d1837361ebe2e64db9f27f5 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Tue, 31 Mar 2020 02:38:08 +0200 Subject: [PATCH 206/232] Function definitions in func_declare.h Significantly reducing number of header files --- CHANGELOG.md | 36 ++--- wled00/alexa.cpp | 14 +- wled00/alexa.h | 17 --- wled00/blynk.cpp | 8 +- wled00/blynk.h | 13 -- wled00/button.cpp | 4 - wled00/button.h | 12 -- wled00/colors.cpp | 5 +- wled00/colors.h | 20 --- wled00/const.h | 7 +- wled00/cronixie.cpp | 243 ------------------------------- wled00/cronixie.h | 13 -- wled00/{dmx.h => dmx.cpp} | 5 +- wled00/e131.cpp | 144 +++++++++++++++++++ wled00/file.cpp | 86 +---------- wled00/file.h | 12 -- wled00/func_declare.h | 183 ++++++++++++++++++++++++ wled00/hue.cpp | 9 +- wled00/hue.h | 16 --- wled00/ir.cpp | 8 +- wled00/ir.h | 24 ---- wled00/json.cpp | 7 +- wled00/json.h | 21 --- wled00/led.cpp | 10 +- wled00/led.h | 19 --- wled00/mqtt.cpp | 10 +- wled00/mqtt.h | 9 -- wled00/notify.h | 18 --- wled00/ntp.cpp | 6 +- wled00/ntp.h | 18 --- wled00/overlay.cpp | 253 ++++++++++++++++++++++++++++++++- wled00/overlay.h | 14 -- wled00/set.cpp | 14 +- wled00/set.h | 16 --- wled00/{notify.cpp => udp.cpp} | 148 +------------------ wled00/usermod.cpp | 2 +- wled00/usermod.h | 8 -- wled00/wled.cpp | 17 +-- wled00/wled.h | 9 +- wled00/wled00.ino | 6 +- wled00/wled_eeprom.cpp | 9 +- wled00/wled_eeprom.h | 23 --- wled00/wled_serial.cpp | 83 +++++++++++ wled00/wled_server.cpp | 8 +- wled00/wled_server.h | 21 --- wled00/xml.cpp | 6 +- wled00/xml.h | 15 -- 47 files changed, 759 insertions(+), 890 deletions(-) delete mode 100644 wled00/alexa.h delete mode 100644 wled00/blynk.h delete mode 100644 wled00/button.h delete mode 100644 wled00/colors.h delete mode 100644 wled00/cronixie.cpp delete mode 100644 wled00/cronixie.h rename wled00/{dmx.h => dmx.cpp} (96%) create mode 100644 wled00/e131.cpp delete mode 100644 wled00/file.h create mode 100644 wled00/func_declare.h delete mode 100644 wled00/hue.h delete mode 100644 wled00/ir.h delete mode 100644 wled00/json.h delete mode 100644 wled00/led.h delete mode 100644 wled00/mqtt.h delete mode 100644 wled00/notify.h delete mode 100644 wled00/ntp.h delete mode 100644 wled00/overlay.h delete mode 100644 wled00/set.h rename wled00/{notify.cpp => udp.cpp} (55%) delete mode 100644 wled00/usermod.h delete mode 100644 wled00/wled_eeprom.h create mode 100644 wled00/wled_serial.cpp delete mode 100644 wled00/wled_server.h delete mode 100644 wled00/xml.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d3a466c..a9804b1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,43 +2,47 @@ ### Development versions after 0.9.1 release +#### Build 2003300 + +- Major change of project structure from .ino to .cpp and func_declare.h + #### Build 2003262 - - Fixed compilation for Analog LEDs - - Fixed sync settings network port fields too small +- Fixed compilation for Analog LEDs +- Fixed sync settings network port fields too small #### Build 2003261 - - Fixed live preview not displaying whole light if over 255 LEDs +- Fixed live preview not displaying whole light if over 255 LEDs #### Build 2003251 - - Added Pacifica effect (tentative, doesn't yet support other colors) - - Added Atlantica palette - - Fixed ESP32 build of Espalexa +- Added Pacifica effect (tentative, doesn't yet support other colors) +- Added Atlantica palette +- Fixed ESP32 build of Espalexa #### Build 2003222 - - Fixed Alexa Whites on non-RGBW lights (bump Espalexa to 2.4.5) +- Fixed Alexa Whites on non-RGBW lights (bump Espalexa to 2.4.5) #### Build 2003221 - - Moved Cronixie driver from FX library to drawOverlay handler +- Moved Cronixie driver from FX library to drawOverlay handler #### Build 2003211 - - Added custom mapping compile define to FX_fcn.h - - Merged pull request #784 by @TravisDean: Fixed initialization bug when toggling skip first - - Added link to youtube videos by Room31 to readme +- Added custom mapping compile define to FX_fcn.h +- Merged pull request #784 by @TravisDean: Fixed initialization bug when toggling skip first +- Added link to youtube videos by Room31 to readme #### Build 2003141 - - Fixed color of main segment returned in JSON API during transition not being target color (closes #765) - - Fixed arlsLock() being called after pixels set in E1.31 (closes #772) - - Fixed HTTP API calls not having an effect if no segment selected (now applies to main segment) +- Fixed color of main segment returned in JSON API during transition not being target color (closes #765) +- Fixed arlsLock() being called after pixels set in E1.31 (closes #772) +- Fixed HTTP API calls not having an effect if no segment selected (now applies to main segment) #### Build 2003121 - - Created changelog.md - make tracking changes to code easier - - Merged pull request #766 by @pille: Fix E1.31 out-of sequence detection +- Created changelog.md - make tracking changes to code easier +- Merged pull request #766 by @pille: Fix E1.31 out-of sequence detection diff --git a/wled00/alexa.cpp b/wled00/alexa.cpp index e35f5b35..c9f61ff2 100644 --- a/wled00/alexa.cpp +++ b/wled00/alexa.cpp @@ -1,9 +1,13 @@ -#include "alexa.h" #include "wled.h" -#include "const.h" -#include "led.h" -#include "wled_eeprom.h" -#include "colors.h" + +/* + * Alexa Voice On/Off/Brightness/Color Control. Emulates a Philips Hue bridge to Alexa. + * + * This was put together from these two excellent projects: + * https://github.com/kakopappa/arduino-esp8266-alexa-wemo-switch + * https://github.com/probonopd/ESP8266HueEmulator + */ +#include "src/dependencies/espalexa/EspalexaDevice.h" #ifndef WLED_DISABLE_ALEXA void onAlexaChange(EspalexaDevice* dev); diff --git a/wled00/alexa.h b/wled00/alexa.h deleted file mode 100644 index c2adfebe..00000000 --- a/wled00/alexa.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef WLED_ALEXA_H -#define WLED_ALEXA_H -/* - * Alexa Voice On/Off/Brightness Control. Emulates a Philips Hue bridge to Alexa. - * - * This was put together from these two excellent projects: - * https://github.com/kakopappa/arduino-esp8266-alexa-wemo-switch - * https://github.com/probonopd/ESP8266HueEmulator - */ -#include "src/dependencies/espalexa/EspalexaDevice.h" - -void onAlexaChange(EspalexaDevice* dev); -void alexaInit(); -void handleAlexa(); -void onAlexaChange(EspalexaDevice* dev); - -#endif // WLED_ALEXA_H \ No newline at end of file diff --git a/wled00/blynk.cpp b/wled00/blynk.cpp index 2307a694..39b43ba8 100644 --- a/wled00/blynk.cpp +++ b/wled00/blynk.cpp @@ -1,9 +1,9 @@ -#include "blynk.h" -#include "const.h" #include "wled.h" #include "src/dependencies/blynk/Blynk/BlynkHandlers.h" -#include "led.h" -#include "colors.h" + +/* + * Remote light control with the free Blynk app + */ uint16_t blHue = 0; byte blSat = 255; diff --git a/wled00/blynk.h b/wled00/blynk.h deleted file mode 100644 index f17f774a..00000000 --- a/wled00/blynk.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef WLED_BLYNK_H -#define WLED_BLYNK_H -#include -/* - * Remote light control with the free Blynk app - */ - -void initBlynk(const char* auth); -void handleBlynk(); -void updateBlynk(); -// TODO: Make sure that the macro expansions are handled correctly. - -#endif //WLED_BLYNK_H \ No newline at end of file diff --git a/wled00/button.cpp b/wled00/button.cpp index a80061ee..e71cce96 100644 --- a/wled00/button.cpp +++ b/wled00/button.cpp @@ -1,8 +1,4 @@ -#include "button.h" #include "wled.h" -#include "led.h" -#include "wled_eeprom.h" -#include "set.h" /* * Physical IO diff --git a/wled00/button.h b/wled00/button.h deleted file mode 100644 index f974888e..00000000 --- a/wled00/button.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef WLED_BUTTON_H -#define WLED_BUTTON_H -#include -/* - * Physical IO - */ - -void shortPressAction(); -void handleButton(); -void handleIO(); - -#endif // WLED_BUTTON_H \ No newline at end of file diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 6f247757..f76499a0 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -1,6 +1,9 @@ -#include "colors.h" #include "wled.h" +/* + * Color conversion methods + */ + void colorFromUint32(uint32_t in, bool secondary) { if (secondary) { diff --git a/wled00/colors.h b/wled00/colors.h deleted file mode 100644 index 85e0adb5..00000000 --- a/wled00/colors.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef WLED_COLORS_H -#define WLED_COLORS_H -#include -/* - * Color conversion methods - */ - -void colorFromUint32(uint32_t in, bool secondary = false); -void colorFromUint24(uint32_t in, bool secondary = false); -void relativeChangeWhite(int8_t amount, byte lowerBoundary = 0); -void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb -void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb - -void colorXYtoRGB(float x, float y, byte* rgb); // only defined if huesync disabled TODO -void colorRGBtoXY(byte* rgb, float* xy); // only defined if huesync disabled TODO - -void colorFromDecOrHexString(byte* rgb, char* in); -void colorRGBtoRGBW(byte* rgb); //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_MODE_LEGACY) - -#endif //WLED_COLORS_H \ No newline at end of file diff --git a/wled00/const.h b/wled00/const.h index 21be1953..535a5987 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -1,5 +1,5 @@ -#ifndef wled_const_h -#define wled_const_h +#ifndef WLED_CONST_H +#define WLED_CONST_H //Defaults #define DEFAULT_CLIENT_SSID "Your_Network" @@ -72,4 +72,7 @@ #define HUE_ERROR_TIMEOUT 251 #define HUE_ERROR_ACTIVE 255 +//EEPROM size +#define EEPSIZE 2560 //Maximum is 4096 + #endif diff --git a/wled00/cronixie.cpp b/wled00/cronixie.cpp deleted file mode 100644 index e3b951b8..00000000 --- a/wled00/cronixie.cpp +++ /dev/null @@ -1,243 +0,0 @@ -#include "cronixie.h" -#include "wled.h" - -#ifndef WLED_DISABLE_CRONIXIE -byte _digitOut[6] = {10,10,10,10,10,10}; - -byte getSameCodeLength(char code, int index, char const cronixieDisplay[]) -{ - byte counter = 0; - - for (int i = index+1; i < 6; i++) - { - if (cronixieDisplay[i] == code) - { - counter++; - } else { - return counter; - } - } - return counter; -} - -void setCronixie() -{ - /* - * digit purpose index - * 0-9 | 0-9 (incl. random) - * 10 | blank - * 11 | blank, bg off - * 12 | test upw. - * 13 | test dnw. - * 14 | binary AM/PM - * 15 | BB upper +50 for no trailing 0 - * 16 | BBB - * 17 | BBBB - * 18 | BBBBB - * 19 | BBBBBB - * 20 | H - * 21 | HH - * 22 | HHH - * 23 | HHHH - * 24 | M - * 25 | MM - * 26 | MMM - * 27 | MMMM - * 28 | MMMMM - * 29 | MMMMMM - * 30 | S - * 31 | SS - * 32 | SSS - * 33 | SSSS - * 34 | SSSSS - * 35 | SSSSSS - * 36 | Y - * 37 | YY - * 38 | YYYY - * 39 | I - * 40 | II - * 41 | W - * 42 | WW - * 43 | D - * 44 | DD - * 45 | DDD - * 46 | V - * 47 | VV - * 48 | VVV - * 49 | VVVV - * 50 | VVVVV - * 51 | VVVVVV - * 52 | v - * 53 | vv - * 54 | vvv - * 55 | vvvv - * 56 | vvvvv - * 57 | vvvvvv - */ - - //H HourLower | HH - Hour 24. | AH - Hour 12. | HHH Hour of Month | HHHH Hour of Year - //M MinuteUpper | MM Minute of Hour | MMM Minute of 12h | MMMM Minute of Day | MMMMM Minute of Month | MMMMMM Minute of Year - //S SecondUpper | SS Second of Minute | SSS Second of 10 Minute | SSSS Second of Hour | SSSSS Second of Day | SSSSSS Second of Week - //B AM/PM | BB 0-6/6-12/12-18/18-24 | BBB 0-3... | BBBB 0-1.5... | BBBBB 0-1 | BBBBBB 0-0.5 - - //Y YearLower | YY - Year LU | YYYY - Std. - //I MonthLower | II - Month of Year - //W Week of Month | WW Week of Year - //D Day of Week | DD Day Of Month | DDD Day Of Year - - DEBUG_PRINT("cset "); - DEBUG_PRINTLN(cronixieDisplay); - - overlayRefreshMs = 1997; //Only refresh every 2secs if no seconds are displayed - - for (int i = 0; i < 6; i++) - { - dP[i] = 10; - switch (cronixieDisplay[i]) - { - case '_': dP[i] = 10; break; - case '-': dP[i] = 11; break; - case 'r': dP[i] = random(1,7); break; //random btw. 1-6 - case 'R': dP[i] = random(0,10); break; //random btw. 0-9 - //case 't': break; //Test upw. - //case 'T': break; //Test dnw. - case 'b': dP[i] = 14 + getSameCodeLength('b',i,cronixieDisplay); i = i+dP[i]-14; break; - case 'B': dP[i] = 14 + getSameCodeLength('B',i,cronixieDisplay); i = i+dP[i]-14; break; - case 'h': dP[i] = 70 + getSameCodeLength('h',i,cronixieDisplay); i = i+dP[i]-70; break; - case 'H': dP[i] = 20 + getSameCodeLength('H',i,cronixieDisplay); i = i+dP[i]-20; break; - case 'A': dP[i] = 108; i++; break; - case 'a': dP[i] = 58; i++; break; - case 'm': dP[i] = 74 + getSameCodeLength('m',i,cronixieDisplay); i = i+dP[i]-74; break; - case 'M': dP[i] = 24 + getSameCodeLength('M',i,cronixieDisplay); i = i+dP[i]-24; break; - case 's': dP[i] = 80 + getSameCodeLength('s',i,cronixieDisplay); i = i+dP[i]-80; overlayRefreshMs = 497; break; //refresh more often bc. of secs - case 'S': dP[i] = 30 + getSameCodeLength('S',i,cronixieDisplay); i = i+dP[i]-30; overlayRefreshMs = 497; break; - case 'Y': dP[i] = 36 + getSameCodeLength('Y',i,cronixieDisplay); i = i+dP[i]-36; break; - case 'y': dP[i] = 86 + getSameCodeLength('y',i,cronixieDisplay); i = i+dP[i]-86; break; - case 'I': dP[i] = 39 + getSameCodeLength('I',i,cronixieDisplay); i = i+dP[i]-39; break; //Month. Don't ask me why month and minute both start with M. - case 'i': dP[i] = 89 + getSameCodeLength('i',i,cronixieDisplay); i = i+dP[i]-89; break; - //case 'W': break; - //case 'w': break; - case 'D': dP[i] = 43 + getSameCodeLength('D',i,cronixieDisplay); i = i+dP[i]-43; break; - case 'd': dP[i] = 93 + getSameCodeLength('d',i,cronixieDisplay); i = i+dP[i]-93; break; - case '0': dP[i] = 0; break; - case '1': dP[i] = 1; break; - case '2': dP[i] = 2; break; - case '3': dP[i] = 3; break; - case '4': dP[i] = 4; break; - case '5': dP[i] = 5; break; - case '6': dP[i] = 6; break; - case '7': dP[i] = 7; break; - case '8': dP[i] = 8; break; - case '9': dP[i] = 9; break; - //case 'V': break; //user var0 - //case 'v': break; //user var1 - } - } - DEBUG_PRINT("result "); - for (int i = 0; i < 5; i++) - { - DEBUG_PRINT((int)dP[i]); - DEBUG_PRINT(" "); - } - DEBUG_PRINTLN((int)dP[5]); - - _overlayCronixie(); //refresh -} - -void _overlayCronixie() -{ - byte h = hour(local); - byte h0 = h; - byte m = minute(local); - byte s = second(local); - byte d = day(local); - byte mi = month(local); - int y = year(local); - //this has to be changed in time for 22nd century - y -= 2000; if (y<0) y += 30; //makes countdown work - - if (useAMPM && !countdownMode) - { - if (h>12) h-=12; - else if (h==0) h+=12; - } - for (int i = 0; i < 6; i++) - { - if (dP[i] < 12) _digitOut[i] = dP[i]; - else { - if (dP[i] < 65) - { - switch(dP[i]) - { - case 21: _digitOut[i] = h/10; _digitOut[i+1] = h- _digitOut[i]*10; i++; break; //HH - case 25: _digitOut[i] = m/10; _digitOut[i+1] = m- _digitOut[i]*10; i++; break; //MM - case 31: _digitOut[i] = s/10; _digitOut[i+1] = s- _digitOut[i]*10; i++; break; //SS - - case 20: _digitOut[i] = h- (h/10)*10; break; //H - case 24: _digitOut[i] = m/10; break; //M - case 30: _digitOut[i] = s/10; break; //S - - case 43: _digitOut[i] = weekday(local); _digitOut[i]--; if (_digitOut[i]<1) _digitOut[i]= 7; break; //D - case 44: _digitOut[i] = d/10; _digitOut[i+1] = d- _digitOut[i]*10; i++; break; //DD - case 40: _digitOut[i] = mi/10; _digitOut[i+1] = mi- _digitOut[i]*10; i++; break; //II - case 37: _digitOut[i] = y/10; _digitOut[i+1] = y- _digitOut[i]*10; i++; break; //YY - case 39: _digitOut[i] = 2; _digitOut[i+1] = 0; _digitOut[i+2] = y/10; _digitOut[i+3] = y- _digitOut[i+2]*10; i+=3; break; //YYYY - - //case 16: _digitOut[i+2] = ((h0/3)&1)?1:0; i++; //BBB (BBBB NI) - //case 15: _digitOut[i+1] = (h0>17 || (h0>5 && h0<12))?1:0; i++; //BB - case 14: _digitOut[i] = (h0>11)?1:0; break; //B - } - } else - { - switch(dP[i]) - { - case 71: _digitOut[i] = h/10; _digitOut[i+1] = h- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //hh - case 75: _digitOut[i] = m/10; _digitOut[i+1] = m- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //mm - case 81: _digitOut[i] = s/10; _digitOut[i+1] = s- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //ss - //case 66: _digitOut[i+2] = ((h0/3)&1)?1:10; i++; //bbb (bbbb NI) - //case 65: _digitOut[i+1] = (h0>17 || (h0>5 && h0<12))?1:10; i++; //bb - case 64: _digitOut[i] = (h0>11)?1:10; break; //b - - case 93: _digitOut[i] = weekday(local); _digitOut[i]--; if (_digitOut[i]<1) _digitOut[i]= 7; break; //d - case 94: _digitOut[i] = d/10; _digitOut[i+1] = d- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //dd - case 90: _digitOut[i] = mi/10; _digitOut[i+1] = mi- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //ii - case 87: _digitOut[i] = y/10; _digitOut[i+1] = y- _digitOut[i]*10; i++; break; //yy - case 89: _digitOut[i] = 2; _digitOut[i+1] = 0; _digitOut[i+2] = y/10; _digitOut[i+3] = y- _digitOut[i+2]*10; i+=3; break; //yyyy - } - } - } - } -} - -void _drawOverlayCronixie() -{ - byte offsets[] = {5, 0, 6, 1, 7, 2, 8, 3, 9, 4}; - - for (uint16_t i = 0; i < 6; i++) - { - byte o = 10*i; - byte excl = 10; - if(_digitOut[i] < 10) excl = offsets[_digitOut[i]]; - excl += o; - - if (cronixieBacklight && _digitOut[i] <11) - { - uint32_t col = strip.gamma32(strip.getSegment(0).colors[1]); - for (uint16_t j=o; j< o+10; j++) { - if (j != excl) strip.setPixelColor(j, col); - } - } else - { - for (uint16_t j=o; j< o+10; j++) { - if (j != excl) strip.setPixelColor(j, 0); - } - } - } -} - -#else // WLED_DISABLE_CRONIXIE -byte getSameCodeLength(char code, int index, char const cronixieDisplay[]) {} -void setCronixie() {} -void _overlayCronixie() {} -void _drawOverlayCronixie() {} -#endif diff --git a/wled00/cronixie.h b/wled00/cronixie.h deleted file mode 100644 index f6eea6b4..00000000 --- a/wled00/cronixie.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef WLED_CRONIXIE_H -#define WLED_CRONIXIE_H -#include -/* - * Support for the Cronixie clock - */ - -byte getSameCodeLength(char code, int index, char const cronixieDisplay[]); -void setCronixie(); -void _overlayCronixie(); -void _drawOverlayCronixie(); - -#endif // WLED_CRONIXIE_H \ No newline at end of file diff --git a/wled00/dmx.h b/wled00/dmx.cpp similarity index 96% rename from wled00/dmx.h rename to wled00/dmx.cpp index 0ab2a4a2..596624a6 100644 --- a/wled00/dmx.h +++ b/wled00/dmx.cpp @@ -1,6 +1,5 @@ -#ifndef WLED_DMX_H -#define WLED_DMX_H #include "wled.h" + /* * Support for DMX via MAX485. * Change the output pin in src/dependencies/ESPDMX.cpp if needed. @@ -61,5 +60,3 @@ void handleDMX() #else void handleDMX() {} #endif - -#endif //WLED_DMX_H \ No newline at end of file diff --git a/wled00/e131.cpp b/wled00/e131.cpp new file mode 100644 index 00000000..2034f84d --- /dev/null +++ b/wled00/e131.cpp @@ -0,0 +1,144 @@ +#include "wled.h" + +/* + * E1.31 handler + */ + +void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ + //E1.31 protocol support + + uint16_t uni = htons(p->universe); + uint8_t previousUniverses = uni - e131Universe; + uint16_t possibleLEDsInCurrentUniverse; + uint16_t dmxChannels = htons(p->property_value_count) -1; + + // only listen for universes we're handling & allocated memory + if (uni >= (e131Universe + E131_MAX_UNIVERSE_COUNT)) return; + + if (e131SkipOutOfSequence) + if (p->sequence_number < e131LastSequenceNumber[uni-e131Universe] && p->sequence_number > 20 && e131LastSequenceNumber[uni-e131Universe] < 250){ + DEBUG_PRINT("skipping E1.31 frame (last seq="); + DEBUG_PRINT(e131LastSequenceNumber[uni-e131Universe]); + DEBUG_PRINT(", current seq="); + DEBUG_PRINT(p->sequence_number); + DEBUG_PRINT(", universe="); + DEBUG_PRINT(uni); + DEBUG_PRINTLN(")"); + return; + } + e131LastSequenceNumber[uni-e131Universe] = p->sequence_number; + + // update status info + realtimeIP = clientIP; + + switch (DMXMode) { + case DMX_MODE_DISABLED: + return; // nothing to do + break; + + case DMX_MODE_SINGLE_RGB: + if (uni != e131Universe) return; + if (dmxChannels-DMXAddress+1 < 3) return; + arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); + for (uint16_t i = 0; i < ledCount; i++) + setRealtimePixel(i, p->property_values[DMXAddress+0], p->property_values[DMXAddress+1], p->property_values[DMXAddress+2], 0); + break; + + case DMX_MODE_SINGLE_DRGB: + if (uni != e131Universe) return; + if (dmxChannels-DMXAddress+1 < 4) return; + arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); + if (DMXOldDimmer != p->property_values[DMXAddress+0]) { + DMXOldDimmer = p->property_values[DMXAddress+0]; + bri = p->property_values[DMXAddress+0]; + strip.setBrightness(bri); + } + for (uint16_t i = 0; i < ledCount; i++) + setRealtimePixel(i, p->property_values[DMXAddress+1], p->property_values[DMXAddress+2], p->property_values[DMXAddress+3], 0); + break; + + case DMX_MODE_EFFECT: + if (uni != e131Universe) return; + if (dmxChannels-DMXAddress+1 < 11) return; + if (DMXOldDimmer != p->property_values[DMXAddress+0]) { + DMXOldDimmer = p->property_values[DMXAddress+0]; + bri = p->property_values[DMXAddress+0]; + } + if (p->property_values[DMXAddress+1] < MODE_COUNT) + effectCurrent = p->property_values[DMXAddress+ 1]; + effectSpeed = p->property_values[DMXAddress+ 2]; // flickers + effectIntensity = p->property_values[DMXAddress+ 3]; + effectPalette = p->property_values[DMXAddress+ 4]; + col[0] = p->property_values[DMXAddress+ 5]; + col[1] = p->property_values[DMXAddress+ 6]; + col[2] = p->property_values[DMXAddress+ 7]; + colSec[0] = p->property_values[DMXAddress+ 8]; + colSec[1] = p->property_values[DMXAddress+ 9]; + colSec[2] = p->property_values[DMXAddress+10]; + if (dmxChannels-DMXAddress+1 > 11) + { + col[3] = p->property_values[DMXAddress+11]; //white + colSec[3] = p->property_values[DMXAddress+12]; + } + transitionDelayTemp = 0; // act fast + colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); // don't send UDP + return; // don't activate realtime live mode + break; + + case DMX_MODE_MULTIPLE_RGB: + arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); + if (previousUniverses == 0) { + // first universe of this fixture + possibleLEDsInCurrentUniverse = (dmxChannels - DMXAddress + 1) / 3; + for (uint16_t i = 0; i < ledCount; i++) { + if (i >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) + setRealtimePixel(i, p->property_values[DMXAddress+i*3+0], p->property_values[DMXAddress+i*3+1], p->property_values[DMXAddress+i*3+2], 0); + } + } else if (previousUniverses > 0 && uni < (e131Universe + E131_MAX_UNIVERSE_COUNT)) { + // additional universe(s) of this fixture + uint16_t numberOfLEDsInPreviousUniverses = ((512 - DMXAddress + 1) / 3); // first universe + if (previousUniverses > 1) numberOfLEDsInPreviousUniverses += (512 / 3) * (previousUniverses - 1); // extended universe(s) before current + possibleLEDsInCurrentUniverse = dmxChannels / 3; + for (uint16_t i = numberOfLEDsInPreviousUniverses; i < ledCount; i++) { + uint8_t j = i - numberOfLEDsInPreviousUniverses; + if (j >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) + setRealtimePixel(i, p->property_values[j*3+1], p->property_values[j*3+2], p->property_values[j*3+3], 0); + } + } + break; + + case DMX_MODE_MULTIPLE_DRGB: + arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); + if (previousUniverses == 0) { + // first universe of this fixture + if (DMXOldDimmer != p->property_values[DMXAddress+0]) { + DMXOldDimmer = p->property_values[DMXAddress+0]; + bri = p->property_values[DMXAddress+0]; + strip.setBrightness(bri); + } + possibleLEDsInCurrentUniverse = (dmxChannels - DMXAddress) / 3; + for (uint16_t i = 0; i < ledCount; i++) { + if (i >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) + setRealtimePixel(i, p->property_values[DMXAddress+i*3+1], p->property_values[DMXAddress+i*3+2], p->property_values[DMXAddress+i*3+3], 0); + } + } else if (previousUniverses > 0 && uni < (e131Universe + E131_MAX_UNIVERSE_COUNT)) { + // additional universe(s) of this fixture + uint16_t numberOfLEDsInPreviousUniverses = ((512 - DMXAddress + 1) / 3); // first universe + if (previousUniverses > 1) numberOfLEDsInPreviousUniverses += (512 / 3) * (previousUniverses - 1); // extended universe(s) before current + possibleLEDsInCurrentUniverse = dmxChannels / 3; + for (uint16_t i = numberOfLEDsInPreviousUniverses; i < ledCount; i++) { + uint8_t j = i - numberOfLEDsInPreviousUniverses; + if (j >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) + setRealtimePixel(i, p->property_values[j*3+1], p->property_values[j*3+2], p->property_values[j*3+3], 0); + } + } + break; + + default: + DEBUG_PRINTLN("unknown E1.31 DMX mode"); + return; // nothing to do + break; + } + + e131NewData = true; +} diff --git a/wled00/file.cpp b/wled00/file.cpp index dba27d4f..eb0dcb93 100644 --- a/wled00/file.cpp +++ b/wled00/file.cpp @@ -1,7 +1,8 @@ -#include "file.h" #include "wled.h" -#include "led.h" -#include "notify.h" + +/* + * Utility for SPIFFS filesystem + */ //filesystem #ifndef WLED_DISABLE_FILESYSTEM @@ -12,85 +13,6 @@ #include "SPIFFSEditor.h" #endif -enum class AdaState { - Header_A, - Header_d, - Header_a, - Header_CountHi, - Header_CountLo, - Header_CountCheck, - Data_Red, - Data_Green, - Data_Blue -}; - -// Maybe Adalight should not be in filehandling? TODO -void handleSerial() -{ - #ifdef WLED_ENABLE_ADALIGHT - static auto state = AdaState::Header_A; - static uint16_t count = 0; - static uint16_t pixel = 0; - static byte check = 0x00; - static byte red = 0x00; - static byte green = 0x00; - - while (Serial.available() > 0) - { - yield(); - byte next = Serial.read(); - switch (state) { - case AdaState::Header_A: - if (next == 'A') state = AdaState::Header_d; - break; - case AdaState::Header_d: - if (next == 'd') state = AdaState::Header_a; - else state = AdaState::Header_A; - break; - case AdaState::Header_a: - if (next == 'a') state = AdaState::Header_CountHi; - else state = AdaState::Header_A; - break; - case AdaState::Header_CountHi: - pixel = 0; - count = next * 0x100; - check = next; - state = AdaState::Header_CountLo; - break; - case AdaState::Header_CountLo: - count += next + 1; - check = check ^ next ^ 0x55; - state = AdaState::Header_CountCheck; - break; - case AdaState::Header_CountCheck: - if (check == next) state = AdaState::Data_Red; - else state = AdaState::Header_A; - break; - case AdaState::Data_Red: - red = next; - state = AdaState::Data_Green; - break; - case AdaState::Data_Green: - green = next; - state = AdaState::Data_Blue; - break; - case AdaState::Data_Blue: - byte blue = next; - setRealtimePixel(pixel++, red, green, blue, 0); - if (--count > 0) state = AdaState::Data_Red; - else { - if (!realtimeMode && bri == 0) strip.setBrightness(briLast); - arlsLock(realtimeTimeoutMs, REALTIME_MODE_ADALIGHT); - - strip.show(); - state = AdaState::Header_A; - } - break; - } - } - #endif -} - #if !defined WLED_DISABLE_FILESYSTEM && defined WLED_ENABLE_FS_SERVING //Un-comment any file types you need diff --git a/wled00/file.h b/wled00/file.h deleted file mode 100644 index 90731db6..00000000 --- a/wled00/file.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef WLED_FILE_H -#define WLED_FILE_H -#include -#include -/* - * Utility for SPIFFS filesystem & Serial console - */ - -void handleSerial(); -bool handleFileRead(AsyncWebServerRequest*, String path); - -#endif // WLED_FILE_H \ No newline at end of file diff --git a/wled00/func_declare.h b/wled00/func_declare.h new file mode 100644 index 00000000..db3cb4bc --- /dev/null +++ b/wled00/func_declare.h @@ -0,0 +1,183 @@ +#ifndef WLED_FUNC_DECLARE_H +#define WLED_FUNC_DECLARE_H +#include +#include "src/dependencies/espalexa/EspalexaDevice.h" +#include "src/dependencies/e131/ESPAsyncE131.h" + +/* + * All globally accessible functions are declared here + */ + +//alexa.cpp +void onAlexaChange(EspalexaDevice* dev); +void alexaInit(); +void handleAlexa(); +void onAlexaChange(EspalexaDevice* dev); + +//blynk.cpp +void initBlynk(const char* auth); +void handleBlynk(); +void updateBlynk(); + +//button.cpp +void shortPressAction(); +void handleButton(); +void handleIO(); + +//colors.cpp +void colorFromUint32(uint32_t in, bool secondary = false); +void colorFromUint24(uint32_t in, bool secondary = false); +void relativeChangeWhite(int8_t amount, byte lowerBoundary = 0); +void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb +void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb + +void colorXYtoRGB(float x, float y, byte* rgb); // only defined if huesync disabled TODO +void colorRGBtoXY(byte* rgb, float* xy); // only defined if huesync disabled TODO + +void colorFromDecOrHexString(byte* rgb, char* in); +void colorRGBtoRGBW(byte* rgb); //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_MODE_LEGACY) + +//e131.cpp +void handleE131Packet(e131_packet_t* p, IPAddress clientIP); + +//file.cpp +bool handleFileRead(AsyncWebServerRequest*, String path); + +//dmx.cpp +void handleDMX(); + +//hue.cpp +void handleHue(); +void reconnectHue(); +void onHueError(void* arg, AsyncClient* client, int8_t error); +void onHueConnect(void* arg, AsyncClient* client); +void sendHuePoll(); +void onHueData(void* arg, AsyncClient* client, void *data, size_t len); + +//ir.cpp +bool decodeIRCustom(uint32_t code); +void relativeChange(byte* property, int8_t amount, byte lowerBoundary = 0, byte higherBoundary = 0xFF); +void changeEffectSpeed(int8_t amount); +void changeEffectIntensity(int8_t amount); +void decodeIR(uint32_t code); +void decodeIR24(uint32_t code); +void decodeIR24OLD(uint32_t code); +void decodeIR24CT(uint32_t code); +void decodeIR40(uint32_t code); +void decodeIR44(uint32_t code); +void decodeIR21(uint32_t code); +void decodeIR6(uint32_t code); + +void initIR(); +void handleIR(); + +//json.cpp +#include "ESPAsyncWebServer.h" +#include "src/dependencies/json/ArduinoJson-v6.h" +#include "src/dependencies/json/AsyncJson-v6.h" +#include "FX.h" +// TODO: AsynicWebServerRequest conflict? + +void deserializeSegment(JsonObject elem, byte it); +bool deserializeState(JsonObject root); +void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id); +void serializeState(JsonObject root); +void serializeInfo(JsonObject root); +void serveJson(AsyncWebServerRequest* request); +void serveLiveLeds(AsyncWebServerRequest* request); + +//led.cpp +void setValuesFromMainSeg(); +void resetTimebase(); +void toggleOnOff(); +void setAllLeds(); +void setLedsStandard(bool justColors = false); +bool colorChanged(); +void colorUpdated(int callMode); +void updateInterfaces(uint8_t callMode); +void handleTransitions(); +void handleNightlight(); + +//mqtt.cpp +bool initMqtt(); +void publishMqtt(); + +//notify.cpp +void notify(byte callMode, bool followUp=false); +void arlsLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC); +void handleNotifications(); +void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w); + +//ntp.cpp +void handleNetworkTime(); +void sendNTPPacket(); +bool checkNTPResponse(); +void updateLocalTime(); +void getTimeString(char* out); +bool checkCountdown(); +void setCountdown(); +byte weekdayMondayFirst(); +void checkTimers(); + +//overlay.cpp +void initCronixie(); +void handleOverlays(); +void handleOverlayDraw(); +void _overlayAnalogCountdown(); +void _overlayAnalogClock(); + +byte getSameCodeLength(char code, int index, char const cronixieDisplay[]); +void setCronixie(); +void _overlayCronixie(); +void _drawOverlayCronixie(); + +//set.cpp +void _setRandomColor(bool _sec,bool fromButton=false); +bool isAsterisksOnly(const char* str, byte maxLen); +void handleSettingsSet(AsyncWebServerRequest *request, byte subPage); +bool handleSet(AsyncWebServerRequest *request, const String& req); +int getNumVal(const String* req, uint16_t pos); +bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte maxv=255); + +//usermod.cpp +void userSetup(); +void userConnected(); +void userLoop(); + +//wled_eeprom.cpp +void commit(); +void clearEEPROM(); +void writeStringToEEPROM(uint16_t pos, char* str, uint16_t len); +void readStringFromEEPROM(uint16_t pos, char* str, uint16_t len); +void saveSettingsToEEPROM(); +void loadSettingsFromEEPROM(bool first); +void savedToPresets(); +bool applyPreset(byte index, bool loadBri = true); +void savePreset(byte index, bool persist = true); +void loadMacro(byte index, char* m); +void applyMacro(byte index); +void saveMacro(byte index, String mc, bool persist = true); //only commit on single save, not in settings + +//wled_serial.cpp +void handleSerial(); + +//wled_server.cpp +bool isIp(String str); +bool captivePortal(AsyncWebServerRequest *request); +void initServer(); +void serveIndexOrWelcome(AsyncWebServerRequest *request); +void serveIndex(AsyncWebServerRequest* request); +String msgProcessor(const String& var); +void serveMessage(AsyncWebServerRequest* request, uint16_t code, String headl, String subl="", byte optionT=255); +String settingsProcessor(const String& var); +String dmxProcessor(const String& var); +void serveSettings(AsyncWebServerRequest* request); + +//xml.cpp +char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr); +char* URL_response(AsyncWebServerRequest *request); +void sappend(char stype, const char* key, int val); +void sappends(char stype, const char* key, char* val); +void getSettingsJS(byte subPage, char* dest); + +#endif diff --git a/wled00/hue.cpp b/wled00/hue.cpp index a186a314..77091f63 100644 --- a/wled00/hue.cpp +++ b/wled00/hue.cpp @@ -1,9 +1,8 @@ -#include "hue.h" #include "wled.h" -#include "colors.h" -#include "wled_eeprom.h" -#include "notify.h" -#include "led.h" + +/* + * Sync to Philips hue lights + */ #ifndef WLED_DISABLE_HUESYNC diff --git a/wled00/hue.h b/wled00/hue.h deleted file mode 100644 index 7ce629fb..00000000 --- a/wled00/hue.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef WLED_HUE_H -#define WLED_HUE_H -/* - * Sync to Philips hue lights - */ -#include -class AsyncClient; - -void handleHue(); -void reconnectHue(); -void onHueError(void* arg, AsyncClient* client, int8_t error); -void onHueConnect(void* arg, AsyncClient* client); -void sendHuePoll(); -void onHueData(void* arg, AsyncClient* client, void *data, size_t len); - -#endif //WLED_HUE_H \ No newline at end of file diff --git a/wled00/ir.cpp b/wled00/ir.cpp index 32b0ee93..046991db 100644 --- a/wled00/ir.cpp +++ b/wled00/ir.cpp @@ -1,8 +1,8 @@ -#include "ir.h" #include "wled.h" -#include "led.h" -#include "colors.h" -#include "wled_eeprom.h" + +/* + * Infrared sensor support for generic 24/40/44 key RGB remotes + */ #if defined(WLED_DISABLE_INFRARED) void handleIR(){} diff --git a/wled00/ir.h b/wled00/ir.h deleted file mode 100644 index e6ecb966..00000000 --- a/wled00/ir.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef WLED_IR_H -#define WLED_IR_H -#include -/* - * Infrared sensor support for generic 24/40/44 key RGB remotes - */ - -bool decodeIRCustom(uint32_t code); -void relativeChange(byte* property, int8_t amount, byte lowerBoundary = 0, byte higherBoundary = 0xFF); -void changeEffectSpeed(int8_t amount); -void changeEffectIntensity(int8_t amount); -void decodeIR(uint32_t code); -void decodeIR24(uint32_t code); -void decodeIR24OLD(uint32_t code); -void decodeIR24CT(uint32_t code); -void decodeIR40(uint32_t code); -void decodeIR44(uint32_t code); -void decodeIR21(uint32_t code); -void decodeIR6(uint32_t code); - -void initIR(); -void handleIR(); - -#endif //WLED_IR_H \ No newline at end of file diff --git a/wled00/json.cpp b/wled00/json.cpp index e79737c4..31208bf5 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -1,7 +1,8 @@ -#include "json.h" #include "wled.h" -#include "wled_eeprom.h" -#include "led.h" + +/* + * JSON API (De)serialization + */ void deserializeSegment(JsonObject elem, byte it) { diff --git a/wled00/json.h b/wled00/json.h deleted file mode 100644 index 77d1b616..00000000 --- a/wled00/json.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef WLED_JSON_H -#define WLED_JSON_H -/* - * JSON API (De)serialization - */ -#include -#include "ESPAsyncWebServer.h" -#include "src/dependencies/json/ArduinoJson-v6.h" -#include "src/dependencies/json/AsyncJson-v6.h" -#include "fx.h" -// TODO: AsynicWebServerRequest conflict? - -void deserializeSegment(JsonObject elem, byte it); -bool deserializeState(JsonObject root); -void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id); -void serializeState(JsonObject root); -void serializeInfo(JsonObject root); -void serveJson(AsyncWebServerRequest* request); -void serveLiveLeds(AsyncWebServerRequest* request); - -#endif //WLED_JSON_H \ No newline at end of file diff --git a/wled00/led.cpp b/wled00/led.cpp index 7f4d6309..c3f50cc2 100644 --- a/wled00/led.cpp +++ b/wled00/led.cpp @@ -1,10 +1,8 @@ -#include "led.h" #include "wled.h" -#include "notify.h" -#include "blynk.h" -#include "wled_eeprom.h" -#include "mqtt.h" -#include "colors.h" + +/* + * LED methods + */ void setValuesFromMainSeg() { diff --git a/wled00/led.h b/wled00/led.h deleted file mode 100644 index d5eb97a8..00000000 --- a/wled00/led.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef WLED_LED_H -#define WLED_LED_H -#include -/* - * LED methods - */ - -void setValuesFromMainSeg(); -void resetTimebase(); -void toggleOnOff(); -void setAllLeds(); -void setLedsStandard(bool justColors = false); -bool colorChanged(); -void colorUpdated(int callMode); -void updateInterfaces(uint8_t callMode); -void handleTransitions(); -void handleNightlight(); - -#endif \ No newline at end of file diff --git a/wled00/mqtt.cpp b/wled00/mqtt.cpp index b84184af..9c15f5bb 100644 --- a/wled00/mqtt.cpp +++ b/wled00/mqtt.cpp @@ -1,10 +1,8 @@ -#include "mqtt.h" #include "wled.h" -#include "notify.h" -#include "led.h" -#include "colors.h" -#include "xml.h" -#include "set.h" + +/* + * MQTT communication protocol for home automation + */ #ifdef WLED_ENABLE_MQTT #define MQTT_KEEP_ALIVE_TIME 60 // contact the MQTT broker every 60 seconds diff --git a/wled00/mqtt.h b/wled00/mqtt.h deleted file mode 100644 index de602b98..00000000 --- a/wled00/mqtt.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef WLED_MQTT_H -#define WLED_MQTT_H -/* - * MQTT communication protocol for home automation - */ -bool initMqtt(); -void publishMqtt(); - -#endif //WLED_MQTT_H \ No newline at end of file diff --git a/wled00/notify.h b/wled00/notify.h deleted file mode 100644 index a0fbfde2..00000000 --- a/wled00/notify.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef WLED_NOTIFY_H -#define WLED_NOTIFY_H -#include -#include "src/dependencies/e131/ESPAsyncE131.h" -#include "const.h" -/* - * UDP notifier - */ -//union e131_packet_t; // Will this compile? -class IPAddress; - -void notify(byte callMode, bool followUp=false); -void arlsLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC); -void handleE131Packet(e131_packet_t* p, IPAddress clientIP); -void handleNotifications(); -void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w); - -#endif // WLED_NOTIFY_H \ No newline at end of file diff --git a/wled00/ntp.cpp b/wled00/ntp.cpp index 20663109..d6332fd2 100644 --- a/wled00/ntp.cpp +++ b/wled00/ntp.cpp @@ -1,7 +1,9 @@ -#include "ntp.h" #include "src/dependencies/timezone/Timezone.h" #include "wled.h" -#include "wled_eeprom.h" + +/* + * Acquires time from NTP server + */ TimeChangeRule UTCr = {Last, Sun, Mar, 1, 0}; // UTC Timezone tzUTC(UTCr, UTCr); diff --git a/wled00/ntp.h b/wled00/ntp.h deleted file mode 100644 index 9029661d..00000000 --- a/wled00/ntp.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef WLED_NTP_H -#define WLED_NTP_H -#include -/* - * Acquires time from NTP server - */ - -void handleNetworkTime(); -void sendNTPPacket(); -bool checkNTPResponse(); -void updateLocalTime(); -void getTimeString(char* out); -bool checkCountdown(); -void setCountdown(); -byte weekdayMondayFirst(); -void checkTimers(); - -#endif // WLED_NTP_H \ No newline at end of file diff --git a/wled00/overlay.cpp b/wled00/overlay.cpp index ccdd3d0c..ceb6fc20 100644 --- a/wled00/overlay.cpp +++ b/wled00/overlay.cpp @@ -1,7 +1,8 @@ -#include "overlay.h" #include "wled.h" -#include "cronixie.h" -#include "ntp.h" + +/* + * Used to draw clock overlays over the strip + */ void initCronixie() { @@ -127,3 +128,249 @@ void handleOverlayDraw() { case 3: _drawOverlayCronixie(); break; } } + + +/* + * Support for the Cronixie clock + */ + +#ifndef WLED_DISABLE_CRONIXIE +byte _digitOut[6] = {10,10,10,10,10,10}; + +byte getSameCodeLength(char code, int index, char const cronixieDisplay[]) +{ + byte counter = 0; + + for (int i = index+1; i < 6; i++) + { + if (cronixieDisplay[i] == code) + { + counter++; + } else { + return counter; + } + } + return counter; +} + +void setCronixie() +{ + /* + * digit purpose index + * 0-9 | 0-9 (incl. random) + * 10 | blank + * 11 | blank, bg off + * 12 | test upw. + * 13 | test dnw. + * 14 | binary AM/PM + * 15 | BB upper +50 for no trailing 0 + * 16 | BBB + * 17 | BBBB + * 18 | BBBBB + * 19 | BBBBBB + * 20 | H + * 21 | HH + * 22 | HHH + * 23 | HHHH + * 24 | M + * 25 | MM + * 26 | MMM + * 27 | MMMM + * 28 | MMMMM + * 29 | MMMMMM + * 30 | S + * 31 | SS + * 32 | SSS + * 33 | SSSS + * 34 | SSSSS + * 35 | SSSSSS + * 36 | Y + * 37 | YY + * 38 | YYYY + * 39 | I + * 40 | II + * 41 | W + * 42 | WW + * 43 | D + * 44 | DD + * 45 | DDD + * 46 | V + * 47 | VV + * 48 | VVV + * 49 | VVVV + * 50 | VVVVV + * 51 | VVVVVV + * 52 | v + * 53 | vv + * 54 | vvv + * 55 | vvvv + * 56 | vvvvv + * 57 | vvvvvv + */ + + //H HourLower | HH - Hour 24. | AH - Hour 12. | HHH Hour of Month | HHHH Hour of Year + //M MinuteUpper | MM Minute of Hour | MMM Minute of 12h | MMMM Minute of Day | MMMMM Minute of Month | MMMMMM Minute of Year + //S SecondUpper | SS Second of Minute | SSS Second of 10 Minute | SSSS Second of Hour | SSSSS Second of Day | SSSSSS Second of Week + //B AM/PM | BB 0-6/6-12/12-18/18-24 | BBB 0-3... | BBBB 0-1.5... | BBBBB 0-1 | BBBBBB 0-0.5 + + //Y YearLower | YY - Year LU | YYYY - Std. + //I MonthLower | II - Month of Year + //W Week of Month | WW Week of Year + //D Day of Week | DD Day Of Month | DDD Day Of Year + + DEBUG_PRINT("cset "); + DEBUG_PRINTLN(cronixieDisplay); + + overlayRefreshMs = 1997; //Only refresh every 2secs if no seconds are displayed + + for (int i = 0; i < 6; i++) + { + dP[i] = 10; + switch (cronixieDisplay[i]) + { + case '_': dP[i] = 10; break; + case '-': dP[i] = 11; break; + case 'r': dP[i] = random(1,7); break; //random btw. 1-6 + case 'R': dP[i] = random(0,10); break; //random btw. 0-9 + //case 't': break; //Test upw. + //case 'T': break; //Test dnw. + case 'b': dP[i] = 14 + getSameCodeLength('b',i,cronixieDisplay); i = i+dP[i]-14; break; + case 'B': dP[i] = 14 + getSameCodeLength('B',i,cronixieDisplay); i = i+dP[i]-14; break; + case 'h': dP[i] = 70 + getSameCodeLength('h',i,cronixieDisplay); i = i+dP[i]-70; break; + case 'H': dP[i] = 20 + getSameCodeLength('H',i,cronixieDisplay); i = i+dP[i]-20; break; + case 'A': dP[i] = 108; i++; break; + case 'a': dP[i] = 58; i++; break; + case 'm': dP[i] = 74 + getSameCodeLength('m',i,cronixieDisplay); i = i+dP[i]-74; break; + case 'M': dP[i] = 24 + getSameCodeLength('M',i,cronixieDisplay); i = i+dP[i]-24; break; + case 's': dP[i] = 80 + getSameCodeLength('s',i,cronixieDisplay); i = i+dP[i]-80; overlayRefreshMs = 497; break; //refresh more often bc. of secs + case 'S': dP[i] = 30 + getSameCodeLength('S',i,cronixieDisplay); i = i+dP[i]-30; overlayRefreshMs = 497; break; + case 'Y': dP[i] = 36 + getSameCodeLength('Y',i,cronixieDisplay); i = i+dP[i]-36; break; + case 'y': dP[i] = 86 + getSameCodeLength('y',i,cronixieDisplay); i = i+dP[i]-86; break; + case 'I': dP[i] = 39 + getSameCodeLength('I',i,cronixieDisplay); i = i+dP[i]-39; break; //Month. Don't ask me why month and minute both start with M. + case 'i': dP[i] = 89 + getSameCodeLength('i',i,cronixieDisplay); i = i+dP[i]-89; break; + //case 'W': break; + //case 'w': break; + case 'D': dP[i] = 43 + getSameCodeLength('D',i,cronixieDisplay); i = i+dP[i]-43; break; + case 'd': dP[i] = 93 + getSameCodeLength('d',i,cronixieDisplay); i = i+dP[i]-93; break; + case '0': dP[i] = 0; break; + case '1': dP[i] = 1; break; + case '2': dP[i] = 2; break; + case '3': dP[i] = 3; break; + case '4': dP[i] = 4; break; + case '5': dP[i] = 5; break; + case '6': dP[i] = 6; break; + case '7': dP[i] = 7; break; + case '8': dP[i] = 8; break; + case '9': dP[i] = 9; break; + //case 'V': break; //user var0 + //case 'v': break; //user var1 + } + } + DEBUG_PRINT("result "); + for (int i = 0; i < 5; i++) + { + DEBUG_PRINT((int)dP[i]); + DEBUG_PRINT(" "); + } + DEBUG_PRINTLN((int)dP[5]); + + _overlayCronixie(); //refresh +} + +void _overlayCronixie() +{ + byte h = hour(local); + byte h0 = h; + byte m = minute(local); + byte s = second(local); + byte d = day(local); + byte mi = month(local); + int y = year(local); + //this has to be changed in time for 22nd century + y -= 2000; if (y<0) y += 30; //makes countdown work + + if (useAMPM && !countdownMode) + { + if (h>12) h-=12; + else if (h==0) h+=12; + } + for (int i = 0; i < 6; i++) + { + if (dP[i] < 12) _digitOut[i] = dP[i]; + else { + if (dP[i] < 65) + { + switch(dP[i]) + { + case 21: _digitOut[i] = h/10; _digitOut[i+1] = h- _digitOut[i]*10; i++; break; //HH + case 25: _digitOut[i] = m/10; _digitOut[i+1] = m- _digitOut[i]*10; i++; break; //MM + case 31: _digitOut[i] = s/10; _digitOut[i+1] = s- _digitOut[i]*10; i++; break; //SS + + case 20: _digitOut[i] = h- (h/10)*10; break; //H + case 24: _digitOut[i] = m/10; break; //M + case 30: _digitOut[i] = s/10; break; //S + + case 43: _digitOut[i] = weekday(local); _digitOut[i]--; if (_digitOut[i]<1) _digitOut[i]= 7; break; //D + case 44: _digitOut[i] = d/10; _digitOut[i+1] = d- _digitOut[i]*10; i++; break; //DD + case 40: _digitOut[i] = mi/10; _digitOut[i+1] = mi- _digitOut[i]*10; i++; break; //II + case 37: _digitOut[i] = y/10; _digitOut[i+1] = y- _digitOut[i]*10; i++; break; //YY + case 39: _digitOut[i] = 2; _digitOut[i+1] = 0; _digitOut[i+2] = y/10; _digitOut[i+3] = y- _digitOut[i+2]*10; i+=3; break; //YYYY + + //case 16: _digitOut[i+2] = ((h0/3)&1)?1:0; i++; //BBB (BBBB NI) + //case 15: _digitOut[i+1] = (h0>17 || (h0>5 && h0<12))?1:0; i++; //BB + case 14: _digitOut[i] = (h0>11)?1:0; break; //B + } + } else + { + switch(dP[i]) + { + case 71: _digitOut[i] = h/10; _digitOut[i+1] = h- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //hh + case 75: _digitOut[i] = m/10; _digitOut[i+1] = m- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //mm + case 81: _digitOut[i] = s/10; _digitOut[i+1] = s- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //ss + //case 66: _digitOut[i+2] = ((h0/3)&1)?1:10; i++; //bbb (bbbb NI) + //case 65: _digitOut[i+1] = (h0>17 || (h0>5 && h0<12))?1:10; i++; //bb + case 64: _digitOut[i] = (h0>11)?1:10; break; //b + + case 93: _digitOut[i] = weekday(local); _digitOut[i]--; if (_digitOut[i]<1) _digitOut[i]= 7; break; //d + case 94: _digitOut[i] = d/10; _digitOut[i+1] = d- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //dd + case 90: _digitOut[i] = mi/10; _digitOut[i+1] = mi- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //ii + case 87: _digitOut[i] = y/10; _digitOut[i+1] = y- _digitOut[i]*10; i++; break; //yy + case 89: _digitOut[i] = 2; _digitOut[i+1] = 0; _digitOut[i+2] = y/10; _digitOut[i+3] = y- _digitOut[i+2]*10; i+=3; break; //yyyy + } + } + } + } +} + +void _drawOverlayCronixie() +{ + byte offsets[] = {5, 0, 6, 1, 7, 2, 8, 3, 9, 4}; + + for (uint16_t i = 0; i < 6; i++) + { + byte o = 10*i; + byte excl = 10; + if(_digitOut[i] < 10) excl = offsets[_digitOut[i]]; + excl += o; + + if (cronixieBacklight && _digitOut[i] <11) + { + uint32_t col = strip.gamma32(strip.getSegment(0).colors[1]); + for (uint16_t j=o; j< o+10; j++) { + if (j != excl) strip.setPixelColor(j, col); + } + } else + { + for (uint16_t j=o; j< o+10; j++) { + if (j != excl) strip.setPixelColor(j, 0); + } + } + } +} + +#else // WLED_DISABLE_CRONIXIE +byte getSameCodeLength(char code, int index, char const cronixieDisplay[]) {} +void setCronixie() {} +void _overlayCronixie() {} +void _drawOverlayCronixie() {} +#endif diff --git a/wled00/overlay.h b/wled00/overlay.h deleted file mode 100644 index f7b54d5d..00000000 --- a/wled00/overlay.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef WLED_OVERLAY_H -#define WLED_OVERLAY_H -#include -/* - * Used to draw clock overlays over the strip - */ - -void initCronixie(); -void handleOverlays(); -void handleOverlayDraw(); -void _overlayAnalogCountdown(); -void _overlayAnalogClock(); - -#endif // WLED_OVERLAY_H \ No newline at end of file diff --git a/wled00/set.cpp b/wled00/set.cpp index f26795c8..7fa2a327 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -1,14 +1,8 @@ -#include "set.h" #include "wled.h" -#include "colors.h" -#include "hue.h" -#include "led.h" -#include "blynk.h" -#include "wled_eeprom.h" -#include "alexa.h" -#include "cronixie.h" -#include "xml.h" -#include "wled_server.h" + +/* + * Receives client input + */ void _setRandomColor(bool _sec,bool fromButton) { diff --git a/wled00/set.h b/wled00/set.h deleted file mode 100644 index 6bc7dbd6..00000000 --- a/wled00/set.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef WLED_SET_H -#define WLED_SET_H -#include -#include -/* - * Receives client input - */ - -void _setRandomColor(bool _sec,bool fromButton=false); -bool isAsterisksOnly(const char* str, byte maxLen); -void handleSettingsSet(AsyncWebServerRequest *request, byte subPage); -bool handleSet(AsyncWebServerRequest *request, const String& req); -int getNumVal(const String* req, uint16_t pos); -bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte maxv=255); - -#endif // WLED_SET_H \ No newline at end of file diff --git a/wled00/notify.cpp b/wled00/udp.cpp similarity index 55% rename from wled00/notify.cpp rename to wled00/udp.cpp index 30adfee0..52879803 100644 --- a/wled00/notify.cpp +++ b/wled00/udp.cpp @@ -1,12 +1,12 @@ -#include "notify.h" #include "wled.h" -#include "src/dependencies/e131/ESPAsyncE131.h" -#include "led.h" + +/* + * UDP sync notifier + */ #define WLEDPACKETSIZE 29 #define UDP_IN_MAXSIZE 1472 - void notify(byte callMode, bool followUp) { if (!udpConnected) return; @@ -87,146 +87,6 @@ void arlsLock(uint32_t timeoutMs, byte md) } -void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ - //E1.31 protocol support - - uint16_t uni = htons(p->universe); - uint8_t previousUniverses = uni - e131Universe; - uint16_t possibleLEDsInCurrentUniverse; - uint16_t dmxChannels = htons(p->property_value_count) -1; - - // only listen for universes we're handling & allocated memory - if (uni >= (e131Universe + E131_MAX_UNIVERSE_COUNT)) return; - - if (e131SkipOutOfSequence) - if (p->sequence_number < e131LastSequenceNumber[uni-e131Universe] && p->sequence_number > 20 && e131LastSequenceNumber[uni-e131Universe] < 250){ - DEBUG_PRINT("skipping E1.31 frame (last seq="); - DEBUG_PRINT(e131LastSequenceNumber[uni-e131Universe]); - DEBUG_PRINT(", current seq="); - DEBUG_PRINT(p->sequence_number); - DEBUG_PRINT(", universe="); - DEBUG_PRINT(uni); - DEBUG_PRINTLN(")"); - return; - } - e131LastSequenceNumber[uni-e131Universe] = p->sequence_number; - - // update status info - realtimeIP = clientIP; - - switch (DMXMode) { - case DMX_MODE_DISABLED: - return; // nothing to do - break; - - case DMX_MODE_SINGLE_RGB: - if (uni != e131Universe) return; - if (dmxChannels-DMXAddress+1 < 3) return; - arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); - for (uint16_t i = 0; i < ledCount; i++) - setRealtimePixel(i, p->property_values[DMXAddress+0], p->property_values[DMXAddress+1], p->property_values[DMXAddress+2], 0); - break; - - case DMX_MODE_SINGLE_DRGB: - if (uni != e131Universe) return; - if (dmxChannels-DMXAddress+1 < 4) return; - arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); - if (DMXOldDimmer != p->property_values[DMXAddress+0]) { - DMXOldDimmer = p->property_values[DMXAddress+0]; - bri = p->property_values[DMXAddress+0]; - strip.setBrightness(bri); - } - for (uint16_t i = 0; i < ledCount; i++) - setRealtimePixel(i, p->property_values[DMXAddress+1], p->property_values[DMXAddress+2], p->property_values[DMXAddress+3], 0); - break; - - case DMX_MODE_EFFECT: - if (uni != e131Universe) return; - if (dmxChannels-DMXAddress+1 < 11) return; - if (DMXOldDimmer != p->property_values[DMXAddress+0]) { - DMXOldDimmer = p->property_values[DMXAddress+0]; - bri = p->property_values[DMXAddress+0]; - } - if (p->property_values[DMXAddress+1] < MODE_COUNT) - effectCurrent = p->property_values[DMXAddress+ 1]; - effectSpeed = p->property_values[DMXAddress+ 2]; // flickers - effectIntensity = p->property_values[DMXAddress+ 3]; - effectPalette = p->property_values[DMXAddress+ 4]; - col[0] = p->property_values[DMXAddress+ 5]; - col[1] = p->property_values[DMXAddress+ 6]; - col[2] = p->property_values[DMXAddress+ 7]; - colSec[0] = p->property_values[DMXAddress+ 8]; - colSec[1] = p->property_values[DMXAddress+ 9]; - colSec[2] = p->property_values[DMXAddress+10]; - if (dmxChannels-DMXAddress+1 > 11) - { - col[3] = p->property_values[DMXAddress+11]; //white - colSec[3] = p->property_values[DMXAddress+12]; - } - transitionDelayTemp = 0; // act fast - colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); // don't send UDP - return; // don't activate realtime live mode - break; - - case DMX_MODE_MULTIPLE_RGB: - arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); - if (previousUniverses == 0) { - // first universe of this fixture - possibleLEDsInCurrentUniverse = (dmxChannels - DMXAddress + 1) / 3; - for (uint16_t i = 0; i < ledCount; i++) { - if (i >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) - setRealtimePixel(i, p->property_values[DMXAddress+i*3+0], p->property_values[DMXAddress+i*3+1], p->property_values[DMXAddress+i*3+2], 0); - } - } else if (previousUniverses > 0 && uni < (e131Universe + E131_MAX_UNIVERSE_COUNT)) { - // additional universe(s) of this fixture - uint16_t numberOfLEDsInPreviousUniverses = ((512 - DMXAddress + 1) / 3); // first universe - if (previousUniverses > 1) numberOfLEDsInPreviousUniverses += (512 / 3) * (previousUniverses - 1); // extended universe(s) before current - possibleLEDsInCurrentUniverse = dmxChannels / 3; - for (uint16_t i = numberOfLEDsInPreviousUniverses; i < ledCount; i++) { - uint8_t j = i - numberOfLEDsInPreviousUniverses; - if (j >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) - setRealtimePixel(i, p->property_values[j*3+1], p->property_values[j*3+2], p->property_values[j*3+3], 0); - } - } - break; - - case DMX_MODE_MULTIPLE_DRGB: - arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); - if (previousUniverses == 0) { - // first universe of this fixture - if (DMXOldDimmer != p->property_values[DMXAddress+0]) { - DMXOldDimmer = p->property_values[DMXAddress+0]; - bri = p->property_values[DMXAddress+0]; - strip.setBrightness(bri); - } - possibleLEDsInCurrentUniverse = (dmxChannels - DMXAddress) / 3; - for (uint16_t i = 0; i < ledCount; i++) { - if (i >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) - setRealtimePixel(i, p->property_values[DMXAddress+i*3+1], p->property_values[DMXAddress+i*3+2], p->property_values[DMXAddress+i*3+3], 0); - } - } else if (previousUniverses > 0 && uni < (e131Universe + E131_MAX_UNIVERSE_COUNT)) { - // additional universe(s) of this fixture - uint16_t numberOfLEDsInPreviousUniverses = ((512 - DMXAddress + 1) / 3); // first universe - if (previousUniverses > 1) numberOfLEDsInPreviousUniverses += (512 / 3) * (previousUniverses - 1); // extended universe(s) before current - possibleLEDsInCurrentUniverse = dmxChannels / 3; - for (uint16_t i = numberOfLEDsInPreviousUniverses; i < ledCount; i++) { - uint8_t j = i - numberOfLEDsInPreviousUniverses; - if (j >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) - setRealtimePixel(i, p->property_values[j*3+1], p->property_values[j*3+2], p->property_values[j*3+3], 0); - } - } - break; - - default: - DEBUG_PRINTLN("unknown E1.31 DMX mode"); - return; // nothing to do - break; - } - - e131NewData = true; -} - - void handleNotifications() { //send second notification if enabled diff --git a/wled00/usermod.cpp b/wled00/usermod.cpp index 96c1f8a6..2c7c881f 100644 --- a/wled00/usermod.cpp +++ b/wled00/usermod.cpp @@ -2,7 +2,7 @@ /* * This file allows you to add own functionality to WLED more easily * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality - * EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in wled_eeprom.h) + * EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in const.h) * bytes 2400+ are currently ununsed, but might be used for future wled features */ diff --git a/wled00/usermod.h b/wled00/usermod.h deleted file mode 100644 index 8269c6e9..00000000 --- a/wled00/usermod.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef WLED_USERMOD_H -#define WLED_USERMOD_H - -void userSetup(); -void userConnected(); -void userLoop(); - -#endif // WLED_USERMOD_H \ No newline at end of file diff --git a/wled00/wled.cpp b/wled00/wled.cpp index fdb60776..7579a9c5 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -1,23 +1,8 @@ #include "wled.h" -#include "alexa.h" -#include "blynk.h" -#include "button.h" -#include "dmx.h" -#include "file.h" -#include "hue.h" -#include "ir.h" -#include "led.h" -#include "mqtt.h" -#include "notify.h" -#include "ntp.h" -#include "overlay.h" -#include "usermod.h" -#include "wled_eeprom.h" -#include "wled_server.h" #include // Global Variable definitions -char versionString[] = "0.9.1"; +char versionString[] = "0.9.1n"; // AP and OTA default passwords (for maximum change them!) char apPass[65] = DEFAULT_AP_PASS; diff --git a/wled00/wled.h b/wled00/wled.h index 70b197c8..926fa0c8 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -3,10 +3,13 @@ /* Main sketch, global variable declarations @title WLED project sketch - @version 0.9.1 + @version 0.9.1n @author Christian Schwinne */ +// version code in format yymmddb (b = daily build) +#define VERSION 2003300 + // ESP8266-01 (blue) got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.4.2 and the setting 512K(No SPIFFS). // ESP8266-01 (black) has 1MB flash and can thus fit the whole program. Use 1M(64K SPIFFS). @@ -78,6 +81,7 @@ #include "src/dependencies/json/AsyncJson-v6.h" #include "src/dependencies/json/ArduinoJson-v6.h" +#include "func_declare.h" #include "html_ui.h" #include "html_settings.h" #include "html_other.h" @@ -117,9 +121,6 @@ #endif #endif -// version code in format yymmddb (b = daily build) -#define VERSION 2003301 - // Global external variable declaration. See wled.cpp for definitions and comments. extern char versionString[]; extern char apPass[65]; diff --git a/wled00/wled00.ino b/wled00/wled00.ino index e3334837..f806e5b3 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -1,6 +1,6 @@ /* - Arduino Studio support file. -*/ + * Arduino IDE compatibility file. + */ #include "wled.h" void setup() { @@ -9,4 +9,4 @@ void setup() { void loop() { WLED::instance().loop(); -} \ No newline at end of file +} diff --git a/wled00/wled_eeprom.cpp b/wled00/wled_eeprom.cpp index 0087641a..ccb16ff5 100644 --- a/wled00/wled_eeprom.cpp +++ b/wled00/wled_eeprom.cpp @@ -1,11 +1,10 @@ -#include "wled_eeprom.h" #include #include "wled.h" -#include "cronixie.h" -#include "ntp.h" -#include "set.h" -#include "led.h" +/* + * Methods to handle saving and loading to non-volatile memory + * EEPROM Map: https://github.com/Aircoookie/WLED/wiki/EEPROM-Map + */ //eeprom Version code, enables default settings instead of 0 init on update #define EEPVER 18 diff --git a/wled00/wled_eeprom.h b/wled00/wled_eeprom.h deleted file mode 100644 index eae52b12..00000000 --- a/wled00/wled_eeprom.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef WLED_EPPROM_H -#define WLED_EPPROM_H -#include -/* - * Methods to handle saving and loading to non-volatile memory - * EEPROM Map: https://github.com/Aircoookie/WLED/wiki/EEPROM-Map - */ -#define EEPSIZE 2560 //Maximum is 4096 - -void commit(); -void clearEEPROM(); -void writeStringToEEPROM(uint16_t pos, char* str, uint16_t len); -void readStringFromEEPROM(uint16_t pos, char* str, uint16_t len); -void saveSettingsToEEPROM(); -void loadSettingsFromEEPROM(bool first); -void savedToPresets(); -bool applyPreset(byte index, bool loadBri = true); -void savePreset(byte index, bool persist = true); -void loadMacro(byte index, char* m); -void applyMacro(byte index); -void saveMacro(byte index, String mc, bool persist = true); //only commit on single save, not in settings - -#endif //WLED_EPPROM_H diff --git a/wled00/wled_serial.cpp b/wled00/wled_serial.cpp new file mode 100644 index 00000000..a472729d --- /dev/null +++ b/wled00/wled_serial.cpp @@ -0,0 +1,83 @@ +#include "wled.h" + +/* + * Adalight handler + */ + +enum class AdaState { + Header_A, + Header_d, + Header_a, + Header_CountHi, + Header_CountLo, + Header_CountCheck, + Data_Red, + Data_Green, + Data_Blue +}; + +void handleSerial() +{ + #ifdef WLED_ENABLE_ADALIGHT + static auto state = AdaState::Header_A; + static uint16_t count = 0; + static uint16_t pixel = 0; + static byte check = 0x00; + static byte red = 0x00; + static byte green = 0x00; + + while (Serial.available() > 0) + { + yield(); + byte next = Serial.read(); + switch (state) { + case AdaState::Header_A: + if (next == 'A') state = AdaState::Header_d; + break; + case AdaState::Header_d: + if (next == 'd') state = AdaState::Header_a; + else state = AdaState::Header_A; + break; + case AdaState::Header_a: + if (next == 'a') state = AdaState::Header_CountHi; + else state = AdaState::Header_A; + break; + case AdaState::Header_CountHi: + pixel = 0; + count = next * 0x100; + check = next; + state = AdaState::Header_CountLo; + break; + case AdaState::Header_CountLo: + count += next + 1; + check = check ^ next ^ 0x55; + state = AdaState::Header_CountCheck; + break; + case AdaState::Header_CountCheck: + if (check == next) state = AdaState::Data_Red; + else state = AdaState::Header_A; + break; + case AdaState::Data_Red: + red = next; + state = AdaState::Data_Green; + break; + case AdaState::Data_Green: + green = next; + state = AdaState::Data_Blue; + break; + case AdaState::Data_Blue: + byte blue = next; + setRealtimePixel(pixel++, red, green, blue, 0); + if (--count > 0) state = AdaState::Data_Red; + else { + if (!realtimeMode && bri == 0) strip.setBrightness(briLast); + arlsLock(realtimeTimeoutMs, REALTIME_MODE_ADALIGHT); + + strip.show(); + state = AdaState::Header_A; + } + break; + } + } + #endif +} diff --git a/wled00/wled_server.cpp b/wled00/wled_server.cpp index d5b4eebc..cd623bc4 100644 --- a/wled00/wled_server.cpp +++ b/wled00/wled_server.cpp @@ -1,10 +1,8 @@ -#include "wled_server.h" #include "wled.h" -#include "file.h" -#include "set.h" -#include "json.h" -#include "xml.h" +/* + * Integrated HTTP web server page declarations + */ //Is this an IP? bool isIp(String str) { diff --git a/wled00/wled_server.h b/wled00/wled_server.h deleted file mode 100644 index ff7133d6..00000000 --- a/wled00/wled_server.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef WLED_SERVER_H -#define WLED_SERVER_H -#include -/* - * Server page declarations - */ -class AsyncWebServerRequest; - - -bool isIp(String str); -bool captivePortal(AsyncWebServerRequest *request); -void initServer(); -void serveIndexOrWelcome(AsyncWebServerRequest *request); -void serveIndex(AsyncWebServerRequest* request); -String msgProcessor(const String& var); -void serveMessage(AsyncWebServerRequest* request, uint16_t code, String headl, String subl="", byte optionT=255); -String settingsProcessor(const String& var); -String dmxProcessor(const String& var); -void serveSettings(AsyncWebServerRequest* request); - -#endif //WLED_SERVER_H \ No newline at end of file diff --git a/wled00/xml.cpp b/wled00/xml.cpp index 09e42011..3af1e54c 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -1,8 +1,8 @@ -#include "xml.h" #include "wled.h" -#include "wled_eeprom.h" -#include "ntp.h" +/* + * Sending XML status files to client + */ //build XML response to HTTP /win API request char* XML_response(AsyncWebServerRequest *request, char* dest) diff --git a/wled00/xml.h b/wled00/xml.h deleted file mode 100644 index 02298295..00000000 --- a/wled00/xml.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef WLED_XML_H -#define WLED_XML_H -#include -#include - -/* - * Sending XML status files to client - */ -char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr); -char* URL_response(AsyncWebServerRequest *request); -void sappend(char stype, const char* key, int val); -void sappends(char stype, const char* key, char* val); -void getSettingsJS(byte subPage, char* dest); - -#endif // WLED_XML_H \ No newline at end of file From 5cb2a39746e922fecc996a80617c79bf16bb766d Mon Sep 17 00:00:00 2001 From: cschwinne Date: Mon, 6 Apr 2020 02:25:17 +0200 Subject: [PATCH 207/232] Consolidated global variables in wled.h --- CHANGELOG.md | 4 + wled00/const.h | 13 ++ wled00/dmx.cpp | 2 +- wled00/wled.cpp | 339 +-------------------------- wled00/wled.h | 610 ++++++++++++++++++++++++++++-------------------- 5 files changed, 375 insertions(+), 593 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9804b1f..8df63ff8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ### Development versions after 0.9.1 release +#### Build 2004060 + +- Consolidated global variables in wled.h + #### Build 2003300 - Major change of project structure from .ino to .cpp and func_declare.h diff --git a/wled00/const.h b/wled00/const.h index 535a5987..ef19e51b 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -75,4 +75,17 @@ //EEPROM size #define EEPSIZE 2560 //Maximum is 4096 +#define NTP_PACKET_SIZE 48 + +// maximum number of LEDs - MAX_LEDS is coming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue +#define MAX_LEDS 1500 +#define MAX_LEDS_DMA 500 + +// string temp buffer (now stored in stack locally) +#define OMAX 2048 + +#define E131_MAX_UNIVERSE_COUNT 9 + +#define ABL_MILLIAMPS_DEFAULT 850; // auto lower brightness to stay close to milliampere limit + #endif diff --git a/wled00/dmx.cpp b/wled00/dmx.cpp index 596624a6..ea087124 100644 --- a/wled00/dmx.cpp +++ b/wled00/dmx.cpp @@ -19,7 +19,7 @@ void handleDMX() for (int i = 0; i < ledCount; i++) { // uses the amount of LEDs as fixture count - uint32_t in = strip.getPixelColor(i); // time to get the colors for the individual fixtures as suggested by AirCookie at issue #462 + uint32_t in = strip.getPixelColor(i); // get the colors for the individual fixtures as suggested by Aircoookie in issue #462 byte w = in >> 24 & 0xFF; byte r = in >> 16 & 0xFF; byte g = in >> 8 & 0xFF; diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 7579a9c5..701b98e3 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -1,344 +1,7 @@ +#define WLED_DEFINE_GLOBAL_VARS //only in one source file, wled.cpp! #include "wled.h" #include -// Global Variable definitions -char versionString[] = "0.9.1n"; - -// AP and OTA default passwords (for maximum change them!) -char apPass[65] = DEFAULT_AP_PASS; -char otaPass[33] = DEFAULT_OTA_PASS; - -// Hardware CONFIG (only changeble HERE, not at runtime) -// LED strip pin, button pin and IR pin changeable in NpbWrapper.h! - -byte auxDefaultState = 0; // 0: input 1: high 2: low -byte auxTriggeredState = 0; // 0: input 1: high 2: low -char ntpServerName[33] = "0.wled.pool.ntp.org"; // NTP server to use - -// WiFi CONFIG (all these can be changed via web UI, no need to set them here) -char clientSSID[33] = CLIENT_SSID; -char clientPass[65] = CLIENT_PASS; -char cmDNS[33] = "x"; // mDNS address (placeholder, will be replaced by wledXXXXXXXXXXXX.local) -char apSSID[33] = ""; // AP off by default (unless setup) -byte apChannel = 1; // 2.4GHz WiFi AP channel (1-13) -byte apHide = 0; // hidden AP SSID -byte apBehavior = AP_BEHAVIOR_BOOT_NO_CONN; // access point opens when no connection after boot by default -IPAddress staticIP(0, 0, 0, 0); // static IP of ESP -IPAddress staticGateway(0, 0, 0, 0); // gateway (router) IP -IPAddress staticSubnet(255, 255, 255, 0); // most common subnet in home networks -bool noWifiSleep = false; // disabling modem sleep modes will increase heat output and power usage, but may help with connection issues - -// LED CONFIG -uint16_t ledCount = 30; // overcurrent prevented by ABL -bool useRGBW = false; // SK6812 strips can contain an extra White channel -bool turnOnAtBoot = true; // turn on LEDs at power-up -byte bootPreset = 0; // save preset to load after power-up - -byte col[] { 255, 160, 0, 0 }; // current RGB(W) primary color. col[] should be updated if you want to change the color. -byte colSec[] { 0, 0, 0, 0 }; // current RGB(W) secondary color -byte briS = 128; // default brightness - -byte nightlightTargetBri = 0; // brightness after nightlight is over -byte nightlightDelayMins = 60; -bool nightlightFade = true; // if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over -bool nightlightColorFade = false; // if enabled, light will gradually fade color from primary to secondary color. -bool fadeTransition = true; // enable crossfading color transition -uint16_t transitionDelay = 750; // default crossfade duration in ms - -bool skipFirstLed = false; // ignore first LED in strip (useful if you need the LED as signal repeater) -byte briMultiplier = 100; // % of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) - -// User Interface CONFIG -char serverDescription[33] = "WLED"; // Name of module -bool syncToggleReceive = false; // UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise - -// Sync CONFIG -bool buttonEnabled = true; -byte irEnabled = 0; // Infrared receiver - -uint16_t udpPort = 21324; // WLED notifier default port -uint16_t udpRgbPort = 19446; // Hyperion port - -bool receiveNotificationBrightness = true; // apply brightness from incoming notifications -bool receiveNotificationColor = true; // apply color -bool receiveNotificationEffects = true; // apply effects setup -bool notifyDirect = false; // send notification if change via UI or HTTP API -bool notifyButton = false; // send if updated by button or infrared remote -bool notifyAlexa = false; // send notification if updated via Alexa -bool notifyMacro = false; // send notification for macro -bool notifyHue = true; // send notification if Hue light changes -bool notifyTwice = false; // notifications use UDP: enable if devices don't sync reliably - -bool alexaEnabled = true; // enable device discovery by Amazon Echo -char alexaInvocationName[33] = "Light"; // speech control name of device. Choose something voice-to-text can understand - -char blynkApiKey[36] = ""; // Auth token for Blynk server. If empty, no connection will be made - -uint16_t realtimeTimeoutMs = 2500; // ms timeout of realtime mode before returning to normal mode -int arlsOffset = 0; // realtime LED offset -bool receiveDirect = true; // receive UDP realtime -bool arlsDisableGammaCorrection = true; // activate if gamma correction is handled by the source -bool arlsForceMaxBri = false; // enable to force max brightness if source has very dark colors that would be black - -uint16_t e131Universe = 1; // settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) -uint8_t DMXMode = DMX_MODE_MULTIPLE_RGB; // DMX mode (s.a.) -uint16_t DMXAddress = 1; // DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] -uint8_t DMXOldDimmer = 0; // only update brightness on change -uint8_t e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; // to detect packet loss -bool e131Multicast = false; // multicast or unicast -bool e131SkipOutOfSequence = false; // freeze instead of flickering - -bool mqttEnabled = false; -char mqttDeviceTopic[33] = ""; // main MQTT topic (individual per device, default is wled/mac) -char mqttGroupTopic[33] = "wled/all"; // second MQTT topic (for example to group devices) -char mqttServer[33] = ""; // both domains and IPs should work (no SSL) -char mqttUser[41] = ""; // optional: username for MQTT auth -char mqttPass[41] = ""; // optional: password for MQTT auth -char mqttClientID[41] = ""; // override the client ID -uint16_t mqttPort = 1883; - -bool huePollingEnabled = false; // poll hue bridge for light state -uint16_t huePollIntervalMs = 2500; // low values (< 1sec) may cause lag but offer quicker response -char hueApiKey[47] = "api"; // key token will be obtained from bridge -byte huePollLightId = 1; // ID of hue lamp to sync to. Find the ID in the hue app ("about" section) -IPAddress hueIP = (0, 0, 0, 0); // IP address of the bridge -bool hueApplyOnOff = true; -bool hueApplyBri = true; -bool hueApplyColor = true; - -// Time CONFIG -bool ntpEnabled = false; // get internet time. Only required if you use clock overlays or time-activated macros -bool useAMPM = false; // 12h/24h clock format -byte currentTimezone = 0; // Timezone ID. Refer to timezones array in wled10_ntp.ino -int utcOffsetSecs = 0; // Seconds to offset from UTC before timzone calculation - -byte overlayDefault = 0; // 0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie -byte overlayMin = 0, overlayMax = ledCount - 1; // boundaries of overlay mode - -byte analogClock12pixel = 0; // The pixel in your strip where "midnight" would be -bool analogClockSecondsTrail = false; // Display seconds as trail of LEDs instead of a single pixel -bool analogClock5MinuteMarks = false; // Light pixels at every 5-minute position - -char cronixieDisplay[7] = "HHMMSS"; // Cronixie Display mask. See wled13_cronixie.ino -bool cronixieBacklight = true; // Allow digits to be back-illuminated - -bool countdownMode = false; // Clock will count down towards date -byte countdownYear = 20, countdownMonth = 1; // Countdown target date, year is last two digits -byte countdownDay = 1, countdownHour = 0; -byte countdownMin = 0, countdownSec = 0; - -byte macroBoot = 0; // macro loaded after startup -byte macroNl = 0; // after nightlight delay over -byte macroCountdown = 0; -byte macroAlexaOn = 0, macroAlexaOff = 0; -byte macroButton = 0, macroLongPress = 0, macroDoublePress = 0; - -// Security CONFIG -bool otaLock = false; // prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks -bool wifiLock = false; // prevents access to WiFi settings when OTA lock is enabled -bool aOtaEnabled = true; // ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on - -uint16_t userVar0 = 0, userVar1 = 0; - -#ifdef WLED_ENABLE_DMX - // dmx CONFIG - byte DMXChannels = 7; // number of channels per fixture - byte DMXFixtureMap[15] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - // assigns the different channels to different functions. See wled21_dmx.ino for more information. - uint16_t DMXGap = 10; // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. - uint16_t DMXStart = 10; // start address of the first fixture -#endif - -// internal global variable declarations -// wifi -bool apActive = false; -bool forceReconnect = false; -uint32_t lastReconnectAttempt = 0; -bool interfacesInited = false; -bool wasConnected = false; - -// color -byte colOld[] { 0, 0, 0, 0 }; // color before transition -byte colT[] { 0, 0, 0, 0 }; // color that is currently displayed on the LEDs -byte colIT[] { 0, 0, 0, 0 }; // color that was last sent to LEDs -byte colSecT[] { 0, 0, 0, 0 }; -byte colSecOld[] { 0, 0, 0, 0 }; -byte colSecIT[] { 0, 0, 0, 0 }; - -byte lastRandomIndex = 0; // used to save last random color so the new one is not the same - -// transitions -bool transitionActive = false; -uint16_t transitionDelayDefault = transitionDelay; -uint16_t transitionDelayTemp = transitionDelay; -unsigned long transitionStartTime; -float tperLast = 0; // crossfade transition progress, 0.0f - 1.0f -bool jsonTransitionOnce = false; - -// nightlight -bool nightlightActive = false; -bool nightlightActiveOld = false; -uint32_t nightlightDelayMs = 10; -uint8_t nightlightDelayMinsDefault = nightlightDelayMins; -unsigned long nightlightStartTime; -byte briNlT = 0; // current nightlight brightness -byte colNlT[] { 0, 0, 0, 0 }; // current nightlight color - -// brightness -unsigned long lastOnTime = 0; -bool offMode = !turnOnAtBoot; -byte bri = briS; -byte briOld = 0; -byte briT = 0; -byte briIT = 0; -byte briLast = 128; // brightness before turned off. Used for toggle function -byte whiteLast = 128; // white channel before turned off. Used for toggle function - -// button -bool buttonPressedBefore = false; -bool buttonLongPressed = false; -unsigned long buttonPressedTime = 0; -unsigned long buttonWaitTime = 0; - -// notifications -bool notifyDirectDefault = notifyDirect; -bool receiveNotifications = true; -unsigned long notificationSentTime = 0; -byte notificationSentCallMode = NOTIFIER_CALL_MODE_INIT; -bool notificationTwoRequired = false; - -// effects -byte effectCurrent = 0; -byte effectSpeed = 128; -byte effectIntensity = 128; -byte effectPalette = 0; - -// network -bool udpConnected = false, udpRgbConnected = false; - -// ui style -bool showWelcomePage = false; - -// hue -byte hueError = HUE_ERROR_INACTIVE; -// uint16_t hueFailCount = 0; -float hueXLast = 0, hueYLast = 0; -uint16_t hueHueLast = 0, hueCtLast = 0; -byte hueSatLast = 0, hueBriLast = 0; -unsigned long hueLastRequestSent = 0; -bool hueAuthRequired = false; -bool hueReceived = false; -bool hueStoreAllowed = false, hueNewKey = false; - -// overlays -byte overlayCurrent = overlayDefault; -byte overlaySpeed = 200; -unsigned long overlayRefreshMs = 200; -unsigned long overlayRefreshedTime; - -// cronixie -byte dP[] { 0, 0, 0, 0, 0, 0 }; -bool cronixieInit = false; - -// countdown -unsigned long countdownTime = 1514764800L; -bool countdownOverTriggered = true; - -// timer -byte lastTimerMinute = 0; -byte timerHours[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; -byte timerMinutes[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; -byte timerMacro[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; -byte timerWeekday[] = { 255, 255, 255, 255, 255, 255, 255, 255 }; // weekdays to activate on -// bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity - -// blynk -bool blynkEnabled = false; - -// preset cycling -bool presetCyclingEnabled = false; -byte presetCycleMin = 1, presetCycleMax = 5; -uint16_t presetCycleTime = 1250; -unsigned long presetCycledTime = 0; -byte presetCycCurr = presetCycleMin; -bool presetApplyBri = true; -bool saveCurrPresetCycConf = false; - -// realtime -byte realtimeMode = REALTIME_MODE_INACTIVE; -IPAddress realtimeIP = (0, 0, 0, 0); -unsigned long realtimeTimeout = 0; - -// mqtt -long lastMqttReconnectAttempt = 0; -long lastInterfaceUpdate = 0; -byte interfaceUpdateCallMode = NOTIFIER_CALL_MODE_INIT; -char mqttStatusTopic[40] = ""; // this must be global because of async handlers - -#if AUXPIN >= 0 - // auxiliary debug pin - byte auxTime = 0; - unsigned long auxStartTime = 0; - bool auxActive = false, auxActiveBefore = false; -#endif - -// alexa udp -String escapedMac; -#ifndef WLED_DISABLE_ALEXA - Espalexa espalexa; - EspalexaDevice* espalexaDevice; -#endif - -// dns server -DNSServer dnsServer; - -// network time -bool ntpConnected = false; -time_t local = 0; -unsigned long ntpLastSyncTime = 999000000L; -unsigned long ntpPacketSentTime = 999000000L; -IPAddress ntpServerIP; -uint16_t ntpLocalPort = 2390; - -// Temp buffer -char* obuf; -uint16_t olen = 0; - -// presets -uint16_t savedPresets = 0; -int8_t currentPreset = -1; -bool isPreset = false; - -byte errorFlag = 0; - -String messageHead, messageSub; -byte optionType; - -bool doReboot = false; // flag to initiate reboot from async handlers -bool doPublishMqtt = false; - -// server library objects -AsyncWebServer server(80); -AsyncClient* hueClient = NULL; -AsyncMqttClient* mqtt = NULL; - -// udp interface objects -WiFiUDP notifierUdp, rgbUdp; -WiFiUDP ntpUdp; -ESPAsyncE131 e131(handleE131Packet); -bool e131NewData = false; - -// led fx library object -WS2812FX strip = WS2812FX(); - -// debug macro variable definitions -#ifdef WLED_DEBUG - unsigned long debugTime = 0; - int lastWifiState = 3; - unsigned long wifiStateChangedTime = 0; - int loops = 0; -#endif - WLED::WLED() { } diff --git a/wled00/wled.h b/wled00/wled.h index 926fa0c8..0c6d3114 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2003300 +#define VERSION 2004060 // ESP8266-01 (blue) got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.4.2 and the setting 512K(No SPIFFS). @@ -119,268 +119,370 @@ #ifndef ESP8266 #include #endif -#endif - -// Global external variable declaration. See wled.cpp for definitions and comments. -extern char versionString[]; -extern char apPass[65]; -extern char otaPass[33]; -extern byte auxDefaultState; -extern byte auxTriggeredState; -extern char ntpServerName[33]; -extern char clientSSID[33]; -extern char clientPass[65]; -extern char cmDNS[33]; -extern char apSSID[33]; -extern byte apChannel; -extern byte apHide; -extern byte apBehavior; -extern IPAddress staticIP; -extern IPAddress staticGateway; -extern IPAddress staticSubnet; -extern bool noWifiSleep; -extern uint16_t ledCount; -extern bool useRGBW; -#define ABL_MILLIAMPS_DEFAULT 850; // auto lower brightness to stay close to milliampere limit -extern bool turnOnAtBoot; -extern byte bootPreset; -extern byte col[]; -extern byte colSec[]; -extern byte briS; -extern byte nightlightTargetBri; -extern byte nightlightDelayMins; -extern bool nightlightFade; -extern bool nightlightColorFade; -extern bool fadeTransition; -extern uint16_t transitionDelay; -extern bool skipFirstLed; -extern byte briMultiplier; -extern char serverDescription[33]; -extern bool syncToggleReceive; -extern bool buttonEnabled; -extern byte irEnabled; -extern uint16_t udpPort; -extern uint16_t udpRgbPort; -extern bool receiveNotificationBrightness; -extern bool receiveNotificationColor; -extern bool receiveNotificationEffects; -extern bool notifyDirect; -extern bool notifyButton; -extern bool notifyAlexa; -extern bool notifyMacro; -extern bool notifyHue; -extern bool notifyTwice; -extern bool alexaEnabled; -extern char alexaInvocationName[33]; -extern char blynkApiKey[36]; -extern uint16_t realtimeTimeoutMs; -extern int arlsOffset; -extern bool receiveDirect; -extern bool arlsDisableGammaCorrection; -extern bool arlsForceMaxBri; -#define E131_MAX_UNIVERSE_COUNT 9 -extern uint16_t e131Universe; -extern uint8_t DMXMode; -extern uint16_t DMXAddress; -extern uint8_t DMXOldDimmer; -extern uint8_t e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; -extern bool e131Multicast; -extern bool e131SkipOutOfSequence; -extern bool mqttEnabled; -extern char mqttDeviceTopic[33]; -extern char mqttGroupTopic[33]; -extern char mqttServer[33]; -extern char mqttUser[41]; -extern char mqttPass[41]; -extern char mqttClientID[41]; -extern uint16_t mqttPort; -extern bool huePollingEnabled; -extern uint16_t huePollIntervalMs; -extern char hueApiKey[47]; -extern byte huePollLightId; -extern IPAddress hueIP; -extern bool hueApplyOnOff; -extern bool hueApplyBri; -extern bool hueApplyColor; -extern bool ntpEnabled; -extern bool useAMPM; -extern byte currentTimezone; -extern int utcOffsetSecs; -extern byte overlayDefault; -extern byte overlayMin; -extern byte overlayMax; -extern byte analogClock12pixel; -extern bool analogClockSecondsTrail; -extern bool analogClock5MinuteMarks; -extern char cronixieDisplay[7]; -extern bool cronixieBacklight; -extern bool countdownMode; -extern byte countdownYear, countdownMonth; -extern byte countdownDay, countdownHour; -extern byte countdownMin, countdownSec; -extern byte macroBoot; -extern byte macroNl; -extern byte macroCountdown; -extern byte macroAlexaOn, macroAlexaOff; -extern byte macroButton, macroLongPress, macroDoublePress; -extern bool otaLock; -extern bool wifiLock; -extern bool aOtaEnabled; -extern uint16_t userVar0, userVar1; -#ifdef WLED_ENABLE_DMX - extern byte DMXChannels; - extern byte DMXFixtureMap[15]; - extern uint16_t DMXGap; - extern uint16_t DMXStart; -#endif -extern bool apActive; -extern bool forceReconnect; -extern uint32_t lastReconnectAttempt; -extern bool interfacesInited; -extern bool wasConnected; -extern byte colOld[]; -extern byte colT[]; -extern byte colIT[]; -extern byte colSecT[]; -extern byte colSecOld[]; -extern byte colSecIT[]; -extern byte lastRandomIndex; -extern bool transitionActive; -extern uint16_t transitionDelayDefault; -extern uint16_t transitionDelayTemp; -extern unsigned long transitionStartTime; -extern float tperLast; -extern bool jsonTransitionOnce; -extern bool nightlightActive; -extern bool nightlightActiveOld; -extern uint32_t nightlightDelayMs; -extern uint8_t nightlightDelayMinsDefault; -extern unsigned long nightlightStartTime; -extern byte briNlT; -extern byte colNlT[]; -extern unsigned long lastOnTime; -extern bool offMode; -extern byte bri; -extern byte briOld; -extern byte briT; -extern byte briIT; -extern byte briLast; -extern byte whiteLast; -extern bool buttonPressedBefore; -extern bool buttonLongPressed; -extern unsigned long buttonPressedTime; -extern unsigned long buttonWaitTime; -extern bool notifyDirectDefault; -extern bool receiveNotifications; -extern unsigned long notificationSentTime; -extern byte notificationSentCallMode; -extern bool notificationTwoRequired; -extern byte effectCurrent; -extern byte effectSpeed; -extern byte effectIntensity; -extern byte effectPalette; -extern bool udpConnected, udpRgbConnected; -extern bool showWelcomePage; -extern byte hueError; -extern float hueXLast, hueYLast; -extern uint16_t hueHueLast, hueCtLast; -extern byte hueSatLast, hueBriLast; -extern unsigned long hueLastRequestSent; -extern bool hueAuthRequired; -extern bool hueReceived; -extern bool hueStoreAllowed, hueNewKey; -extern byte overlayCurrent; -extern byte overlaySpeed; -extern unsigned long overlayRefreshMs; -extern unsigned long overlayRefreshedTime; -extern byte dP[]; -extern bool cronixieInit; -extern unsigned long countdownTime; -extern bool countdownOverTriggered; -extern byte lastTimerMinute; -extern byte timerHours[]; -extern byte timerMinutes[]; -extern byte timerMacro[]; -extern byte timerWeekday[]; -extern bool blynkEnabled; -extern bool presetCyclingEnabled; -extern byte presetCycleMin, presetCycleMax; -extern uint16_t presetCycleTime; -extern unsigned long presetCycledTime; -extern byte presetCycCurr; -extern bool presetApplyBri; -extern bool saveCurrPresetCycConf; -extern byte realtimeMode; -extern IPAddress realtimeIP; -extern unsigned long realtimeTimeout; -extern long lastMqttReconnectAttempt; -extern long lastInterfaceUpdate; -extern byte interfaceUpdateCallMode; -extern char mqttStatusTopic[40]; -#if AUXPIN >= 0 - extern byte auxTime; - extern unsigned long auxStartTime; - extern bool auxActive; -#endif -extern String escapedMac; -#ifndef WLED_DISABLE_ALEXA - extern Espalexa espalexa; - extern EspalexaDevice *espalexaDevice; -#endif - -#define NTP_PACKET_SIZE 48 -extern DNSServer dnsServer; -extern bool ntpConnected; -extern time_t local; -extern unsigned long ntpLastSyncTime; -extern unsigned long ntpPacketSentTime; -extern IPAddress ntpServerIP; -extern uint16_t ntpLocalPort; - -// maximum number of LEDs - MAX_LEDS is coming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue -#define MAX_LEDS 1500 -#define MAX_LEDS_DMA 500 - -// string temp buffer (now stored in stack locally) -#define OMAX 2048 -extern char* obuf; -extern uint16_t olen; - -extern uint16_t savedPresets; -extern int8_t currentPreset; -extern bool isPreset; -extern byte errorFlag; -extern String messageHead, messageSub; -extern byte optionType; -extern bool doReboot; -extern bool doPublishMqtt; -extern AsyncWebServer server; -extern AsyncClient* hueClient; -extern AsyncMqttClient* mqtt; -extern WiFiUDP notifierUdp, rgbUdp; -extern WiFiUDP ntpUdp; -extern ESPAsyncE131 e131; -extern bool e131NewData; -extern WS2812FX strip; - -#define WLED_CONNECTED (WiFi.status() == WL_CONNECTED) -#define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID, DEFAULT_CLIENT_SSID) != 0) - -// debug macros -#ifdef WLED_DEBUG #define DEBUG_PRINT(x) Serial.print(x) #define DEBUG_PRINTLN(x) Serial.println(x) #define DEBUG_PRINTF(x) Serial.printf(x) - extern unsigned long debugTime; - extern int lastWifiState; - extern unsigned long wifiStateChangedTime; - extern int loops; #else #define DEBUG_PRINT(x) #define DEBUG_PRINTLN(x) #define DEBUG_PRINTF(x) #endif +#ifndef WLED_DEFINE_GLOBAL_VARS +# define WLED_GLOBAL extern +# define _INIT(x) +# define _INIT_N(x) +#else +# define WLED_GLOBAL +# define _INIT(x) = x + +//needed to ignore commas in array definitions +#define UNPACK( ... ) __VA_ARGS__ +# define _INIT_N(x) UNPACK x +#endif + +// Global Variable definitions +WLED_GLOBAL char versionString[] _INIT("0.9.1n"); + +// AP and OTA default passwords (for maximum security change them!) +WLED_GLOBAL char apPass[65] _INIT(DEFAULT_AP_PASS); +WLED_GLOBAL char otaPass[33] _INIT(DEFAULT_OTA_PASS); + +// Hardware CONFIG (only changeble HERE, not at runtime) +// LED strip pin, button pin and IR pin changeable in NpbWrapper.h! + +WLED_GLOBAL byte auxDefaultState _INIT(0); // 0: input 1: high 2: low +WLED_GLOBAL byte auxTriggeredState _INIT(0); // 0: input 1: high 2: low +WLED_GLOBAL char ntpServerName[33] _INIT("0.wled.pool.ntp.org"); // NTP server to use + +// WiFi CONFIG (all these can be changed via web UI, no need to set them here) +WLED_GLOBAL char clientSSID[33] _INIT(CLIENT_SSID); +WLED_GLOBAL char clientPass[65] _INIT(CLIENT_PASS); +WLED_GLOBAL char cmDNS[33] _INIT("x"); // mDNS address (placeholder, is replaced by wledXXXXXX.local) +WLED_GLOBAL char apSSID[33] _INIT(""); // AP off by default (unless setup) +WLED_GLOBAL byte apChannel _INIT(1); // 2.4GHz WiFi AP channel (1-13) +WLED_GLOBAL byte apHide _INIT(0); // hidden AP SSID +WLED_GLOBAL byte apBehavior _INIT(AP_BEHAVIOR_BOOT_NO_CONN); // access point opens when no connection after boot by default +WLED_GLOBAL IPAddress staticIP _INIT_N((( 0, 0, 0, 0))); // static IP of ESP +WLED_GLOBAL IPAddress staticGateway _INIT_N((( 0, 0, 0, 0))); // gateway (router) IP +WLED_GLOBAL IPAddress staticSubnet _INIT_N(((255, 255, 255, 0))); // most common subnet in home networks +WLED_GLOBAL bool noWifiSleep _INIT(false); // disabling modem sleep modes will increase heat output and power usage, but may help with connection issues + +// LED CONFIG +WLED_GLOBAL uint16_t ledCount _INIT(30); // overcurrent prevented by ABL +WLED_GLOBAL bool useRGBW _INIT(false); // SK6812 strips can contain an extra White channel +WLED_GLOBAL bool turnOnAtBoot _INIT(true); // turn on LEDs at power-up +WLED_GLOBAL byte bootPreset _INIT(0); // save preset to load after power-up + +WLED_GLOBAL byte col[] _INIT_N(({ 255, 160, 0, 0 })); // current RGB(W) primary color. col[] should be updated if you want to change the color. +WLED_GLOBAL byte colSec[] _INIT_N(({ 0, 0, 0, 0 })); // current RGB(W) secondary color +WLED_GLOBAL byte briS _INIT(128); // default brightness + +WLED_GLOBAL byte nightlightTargetBri _INIT(0); // brightness after nightlight is over +WLED_GLOBAL byte nightlightDelayMins _INIT(60); +WLED_GLOBAL bool nightlightFade _INIT(true); // if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over +WLED_GLOBAL bool nightlightColorFade _INIT(false); // if enabled, light will gradually fade color from primary to secondary color. +WLED_GLOBAL bool fadeTransition _INIT(true); // enable crossfading color transition +WLED_GLOBAL uint16_t transitionDelay _INIT(750); // default crossfade duration in ms + +WLED_GLOBAL bool skipFirstLed _INIT(false); // ignore first LED in strip (useful if you need the LED as signal repeater) +WLED_GLOBAL byte briMultiplier _INIT(100); // % of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) + +// User Interface CONFIG +WLED_GLOBAL char serverDescription[33] _INIT("WLED"); // Name of module +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 + +// Sync CONFIG +WLED_GLOBAL bool buttonEnabled _INIT(true); +WLED_GLOBAL byte irEnabled _INIT(0); // Infrared receiver + +WLED_GLOBAL uint16_t udpPort _INIT(21324); // WLED notifier default port +WLED_GLOBAL uint16_t udpRgbPort _INIT(19446); // Hyperion port + +WLED_GLOBAL bool receiveNotificationBrightness _INIT(true); // apply brightness from incoming notifications +WLED_GLOBAL bool receiveNotificationColor _INIT(true); // apply color +WLED_GLOBAL bool receiveNotificationEffects _INIT(true); // apply effects setup +WLED_GLOBAL bool notifyDirect _INIT(false); // send notification if change via UI or HTTP API +WLED_GLOBAL bool notifyButton _INIT(false); // send if updated by button or infrared remote +WLED_GLOBAL bool notifyAlexa _INIT(false); // send notification if updated via Alexa +WLED_GLOBAL bool notifyMacro _INIT(false); // send notification for macro +WLED_GLOBAL bool notifyHue _INIT(true); // send notification if Hue light changes +WLED_GLOBAL bool notifyTwice _INIT(false); // notifications use UDP: enable if devices don't sync reliably + +WLED_GLOBAL bool alexaEnabled _INIT(true); // enable device discovery by Amazon Echo +WLED_GLOBAL char alexaInvocationName[33] _INIT("Light"); // speech control name of device. Choose something voice-to-text can understand + +WLED_GLOBAL char blynkApiKey[36] _INIT(""); // Auth token for Blynk server. If empty, no connection will be made + +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 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 + +WLED_GLOBAL uint16_t e131Universe _INIT(1); // settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) +WLED_GLOBAL byte DMXMode _INIT(DMX_MODE_MULTIPLE_RGB); // DMX mode (s.a.) +WLED_GLOBAL uint16_t DMXAddress _INIT(1); // DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] +WLED_GLOBAL byte DMXOldDimmer _INIT(0); // only update brightness on change +WLED_GLOBAL byte e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; // to detect packet loss +WLED_GLOBAL bool e131Multicast _INIT(false); // multicast or unicast +WLED_GLOBAL bool e131SkipOutOfSequence _INIT(false); // freeze instead of flickering + +WLED_GLOBAL bool mqttEnabled _INIT(false); +WLED_GLOBAL char mqttDeviceTopic[33] _INIT(""); // main MQTT topic (individual per device, default is wled/mac) +WLED_GLOBAL char mqttGroupTopic[33] _INIT("wled/all"); // second MQTT topic (for example to group devices) +WLED_GLOBAL char mqttServer[33] _INIT(""); // both domains and IPs should work (no SSL) +WLED_GLOBAL char mqttUser[41] _INIT(""); // optional: username for MQTT auth +WLED_GLOBAL char mqttPass[41] _INIT(""); // optional: password for MQTT auth +WLED_GLOBAL char mqttClientID[41] _INIT(""); // override the client ID +WLED_GLOBAL uint16_t mqttPort _INIT(1883); + +WLED_GLOBAL bool huePollingEnabled _INIT(false); // poll hue bridge for light state +WLED_GLOBAL uint16_t huePollIntervalMs _INIT(2500); // low values (< 1sec) may cause lag but offer quicker response +WLED_GLOBAL char hueApiKey[47] _INIT("api"); // key token will be obtained from bridge +WLED_GLOBAL byte huePollLightId _INIT(1); // ID of hue lamp to sync to. Find the ID in the hue app ("about" section) +WLED_GLOBAL IPAddress hueIP _INIT((0, 0, 0, 0)); // IP address of the bridge +WLED_GLOBAL bool hueApplyOnOff _INIT(true); +WLED_GLOBAL bool hueApplyBri _INIT(true); +WLED_GLOBAL bool hueApplyColor _INIT(true); + +// Time CONFIG +WLED_GLOBAL bool ntpEnabled _INIT(false); // get internet time. Only required if you use clock overlays or time-activated macros +WLED_GLOBAL bool useAMPM _INIT(false); // 12h/24h clock format +WLED_GLOBAL byte currentTimezone _INIT(0); // Timezone ID. Refer to timezones array in wled10_ntp.ino +WLED_GLOBAL int utcOffsetSecs _INIT(0); // Seconds to offset from UTC before timzone calculation + +WLED_GLOBAL byte overlayDefault _INIT(0); // 0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie +WLED_GLOBAL byte overlayMin _INIT(0), overlayMax _INIT(ledCount - 1); // boundaries of overlay mode + +WLED_GLOBAL byte analogClock12pixel _INIT(0); // The pixel in your strip where "midnight" would be +WLED_GLOBAL bool analogClockSecondsTrail _INIT(false); // Display seconds as trail of LEDs instead of a single pixel +WLED_GLOBAL bool analogClock5MinuteMarks _INIT(false); // Light pixels at every 5-minute position + +WLED_GLOBAL char cronixieDisplay[7] _INIT("HHMMSS"); // Cronixie Display mask. See wled13_cronixie.ino +WLED_GLOBAL bool cronixieBacklight _INIT(true); // Allow digits to be back-illuminated + +WLED_GLOBAL bool countdownMode _INIT(false); // Clock will count down towards date +WLED_GLOBAL byte countdownYear _INIT(20), countdownMonth _INIT(1); // Countdown target date, year is last two digits +WLED_GLOBAL byte countdownDay _INIT(1), countdownHour _INIT(0); +WLED_GLOBAL byte countdownMin _INIT(0), countdownSec _INIT(0); + +WLED_GLOBAL byte macroBoot _INIT(0); // macro loaded after startup +WLED_GLOBAL byte macroNl _INIT(0); // after nightlight delay over +WLED_GLOBAL byte macroCountdown _INIT(0); +WLED_GLOBAL byte macroAlexaOn _INIT(0), macroAlexaOff _INIT(0); +WLED_GLOBAL byte macroButton _INIT(0), macroLongPress _INIT(0), macroDoublePress _INIT(0); + +// Security CONFIG +WLED_GLOBAL bool otaLock _INIT(false); // prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks +WLED_GLOBAL bool wifiLock _INIT(false); // prevents access to WiFi settings when OTA lock is enabled +WLED_GLOBAL bool aOtaEnabled _INIT(true); // ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on + +WLED_GLOBAL uint16_t userVar0 _INIT(0), userVar1 _INIT(0); + +#ifdef WLED_ENABLE_DMX + // dmx CONFIG + WLED_GLOBAL byte DMXChannels _INIT(7); // number of channels per fixture + WLED_GLOBAL byte DMXFixtureMap[15] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 })); + // assigns the different channels to different functions. See wled21_dmx.ino for more information. + WLED_GLOBAL uint16_t DMXGap _INIT(10); // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. + WLED_GLOBAL uint16_t DMXStart _INIT(10); // start address of the first fixture +#endif + +// internal global variable declarations +// wifi +WLED_GLOBAL bool apActive _INIT(false); +WLED_GLOBAL bool forceReconnect _INIT(false); +WLED_GLOBAL uint32_t lastReconnectAttempt _INIT(0); +WLED_GLOBAL bool interfacesInited _INIT(false); +WLED_GLOBAL bool wasConnected _INIT(false); + +// color +WLED_GLOBAL byte colOld[] _INIT_N(({ 0, 0, 0, 0 })); // color before transition +WLED_GLOBAL byte colT[] _INIT_N(({ 0, 0, 0, 0 })); // color that is currently displayed on the LEDs +WLED_GLOBAL byte colIT[] _INIT_N(({ 0, 0, 0, 0 })); // color that was last sent to LEDs +WLED_GLOBAL byte colSecT[] _INIT_N(({ 0, 0, 0, 0 })); +WLED_GLOBAL byte colSecOld[] _INIT_N(({ 0, 0, 0, 0 })); +WLED_GLOBAL byte colSecIT[] _INIT_N(({ 0, 0, 0, 0 })); + +WLED_GLOBAL byte lastRandomIndex _INIT(0); // used to save last random color so the new one is not the same + +// transitions +WLED_GLOBAL bool transitionActive _INIT(false); +WLED_GLOBAL uint16_t transitionDelayDefault _INIT(transitionDelay); +WLED_GLOBAL uint16_t transitionDelayTemp _INIT(transitionDelay); +WLED_GLOBAL unsigned long transitionStartTime; +WLED_GLOBAL float tperLast _INIT(0); // crossfade transition progress, 0.0f - 1.0f +WLED_GLOBAL bool jsonTransitionOnce _INIT(false); + +// nightlight +WLED_GLOBAL bool nightlightActive _INIT(false); +WLED_GLOBAL bool nightlightActiveOld _INIT(false); +WLED_GLOBAL uint32_t nightlightDelayMs _INIT(10); +WLED_GLOBAL byte nightlightDelayMinsDefault _INIT(nightlightDelayMins); +WLED_GLOBAL unsigned long nightlightStartTime; +WLED_GLOBAL byte briNlT _INIT(0); // current nightlight brightness +WLED_GLOBAL byte colNlT[] _INIT_N(({ 0, 0, 0, 0 })); // current nightlight color + +// brightness +WLED_GLOBAL unsigned long lastOnTime _INIT(0); +WLED_GLOBAL bool offMode _INIT(!turnOnAtBoot); +WLED_GLOBAL byte bri _INIT(briS); +WLED_GLOBAL byte briOld _INIT(0); +WLED_GLOBAL byte briT _INIT(0); +WLED_GLOBAL byte briIT _INIT(0); +WLED_GLOBAL byte briLast _INIT(128); // brightness before turned off. Used for toggle function +WLED_GLOBAL byte whiteLast _INIT(128); // white channel before turned off. Used for toggle function + +// button +WLED_GLOBAL bool buttonPressedBefore _INIT(false); +WLED_GLOBAL bool buttonLongPressed _INIT(false); +WLED_GLOBAL unsigned long buttonPressedTime _INIT(0); +WLED_GLOBAL unsigned long buttonWaitTime _INIT(0); + +// notifications +WLED_GLOBAL bool notifyDirectDefault _INIT(notifyDirect); +WLED_GLOBAL bool receiveNotifications _INIT(true); +WLED_GLOBAL unsigned long notificationSentTime _INIT(0); +WLED_GLOBAL byte notificationSentCallMode _INIT(NOTIFIER_CALL_MODE_INIT); +WLED_GLOBAL bool notificationTwoRequired _INIT(false); + +// effects +WLED_GLOBAL byte effectCurrent _INIT(0); +WLED_GLOBAL byte effectSpeed _INIT(128); +WLED_GLOBAL byte effectIntensity _INIT(128); +WLED_GLOBAL byte effectPalette _INIT(0); + +// network +WLED_GLOBAL bool udpConnected _INIT(false), udpRgbConnected _INIT(false); + +// ui style +WLED_GLOBAL bool showWelcomePage _INIT(false); + +// hue +WLED_GLOBAL byte hueError _INIT(HUE_ERROR_INACTIVE); +// WLED_GLOBAL uint16_t hueFailCount _INIT(0); +WLED_GLOBAL float hueXLast _INIT(0), hueYLast _INIT(0); +WLED_GLOBAL uint16_t hueHueLast _INIT(0), hueCtLast _INIT(0); +WLED_GLOBAL byte hueSatLast _INIT(0), hueBriLast _INIT(0); +WLED_GLOBAL unsigned long hueLastRequestSent _INIT(0); +WLED_GLOBAL bool hueAuthRequired _INIT(false); +WLED_GLOBAL bool hueReceived _INIT(false); +WLED_GLOBAL bool hueStoreAllowed _INIT(false), hueNewKey _INIT(false); + +// overlays +WLED_GLOBAL byte overlayCurrent _INIT(overlayDefault); +WLED_GLOBAL byte overlaySpeed _INIT(200); +WLED_GLOBAL unsigned long overlayRefreshMs _INIT(200); +WLED_GLOBAL unsigned long overlayRefreshedTime; + +// cronixie +WLED_GLOBAL byte dP[] _INIT_N(({ 0, 0, 0, 0, 0, 0 })); +WLED_GLOBAL bool cronixieInit _INIT(false); + +// countdown +WLED_GLOBAL unsigned long countdownTime _INIT(1514764800L); +WLED_GLOBAL bool countdownOverTriggered _INIT(true); + +// timer +WLED_GLOBAL byte lastTimerMinute _INIT(0); +WLED_GLOBAL byte timerHours[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0 })); +WLED_GLOBAL byte timerMinutes[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0 })); +WLED_GLOBAL byte timerMacro[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0 })); +WLED_GLOBAL byte timerWeekday[] _INIT_N(({ 255, 255, 255, 255, 255, 255, 255, 255 })); // weekdays to activate on +// bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity + +// blynk +WLED_GLOBAL bool blynkEnabled _INIT(false); + +// preset cycling +WLED_GLOBAL bool presetCyclingEnabled _INIT(false); +WLED_GLOBAL byte presetCycleMin _INIT(1), presetCycleMax _INIT(5); +WLED_GLOBAL uint16_t presetCycleTime _INIT(1250); +WLED_GLOBAL unsigned long presetCycledTime _INIT(0); +WLED_GLOBAL byte presetCycCurr _INIT(presetCycleMin); +WLED_GLOBAL bool presetApplyBri _INIT(true); +WLED_GLOBAL bool saveCurrPresetCycConf _INIT(false); + +// realtime +WLED_GLOBAL byte realtimeMode _INIT(REALTIME_MODE_INACTIVE); +WLED_GLOBAL IPAddress realtimeIP _INIT((0, 0, 0, 0)); +WLED_GLOBAL unsigned long realtimeTimeout _INIT(0); + +// mqtt +WLED_GLOBAL long lastMqttReconnectAttempt _INIT(0); +WLED_GLOBAL long lastInterfaceUpdate _INIT(0); +WLED_GLOBAL byte interfaceUpdateCallMode _INIT(NOTIFIER_CALL_MODE_INIT); +WLED_GLOBAL char mqttStatusTopic[40] _INIT(""); // this must be global because of async handlers + +#if AUXPIN >= 0 + // auxiliary debug pin + WLED_GLOBAL byte auxTime _INIT(0); + WLED_GLOBAL unsigned long auxStartTime _INIT(0); + WLED_GLOBAL bool auxActive _INIT(false, auxActiveBefore _INIT(false); +#endif + +// alexa udp +WLED_GLOBAL String escapedMac; +#ifndef WLED_DISABLE_ALEXA + WLED_GLOBAL Espalexa espalexa; + WLED_GLOBAL EspalexaDevice* espalexaDevice; +#endif + +// dns server +WLED_GLOBAL DNSServer dnsServer; + +// network time +WLED_GLOBAL bool ntpConnected _INIT(false); +WLED_GLOBAL time_t local _INIT(0); +WLED_GLOBAL unsigned long ntpLastSyncTime _INIT(999000000L); +WLED_GLOBAL unsigned long ntpPacketSentTime _INIT(999000000L); +WLED_GLOBAL IPAddress ntpServerIP; +WLED_GLOBAL uint16_t ntpLocalPort _INIT(2390); + +// Temp buffer +WLED_GLOBAL char* obuf; +WLED_GLOBAL uint16_t olen _INIT(0); + +// presets +WLED_GLOBAL uint16_t savedPresets _INIT(0); +WLED_GLOBAL int8_t currentPreset _INIT(-1); +WLED_GLOBAL bool isPreset _INIT(false); + +WLED_GLOBAL byte errorFlag _INIT(0); + +WLED_GLOBAL String messageHead, messageSub; +WLED_GLOBAL byte optionType; + +WLED_GLOBAL bool doReboot _INIT(false); // flag to initiate reboot from async handlers +WLED_GLOBAL bool doPublishMqtt _INIT(false); + +// server library objects +WLED_GLOBAL AsyncWebServer server _INIT_N(((80))); +WLED_GLOBAL AsyncClient* hueClient _INIT(NULL); +WLED_GLOBAL AsyncMqttClient* mqtt _INIT(NULL); + +// udp interface objects +WLED_GLOBAL WiFiUDP notifierUdp, rgbUdp; +WLED_GLOBAL WiFiUDP ntpUdp; +WLED_GLOBAL ESPAsyncE131 e131 _INIT_N(((handleE131Packet))); +WLED_GLOBAL bool e131NewData _INIT(false); + +// led fx library object +WLED_GLOBAL WS2812FX strip _INIT(WS2812FX()); + +// debug macro variable definitions +#ifdef WLED_DEBUG + WLED_GLOBAL unsigned long debugTime _INIT(0); + WLED_GLOBAL int lastWifiState _INIT(3); + WLED_GLOBAL unsigned long wifiStateChangedTime _INIT(0); + WLED_GLOBAL int loops _INIT(0); +#endif + + +#define WLED_CONNECTED (WiFi.status() == WL_CONNECTED) +#define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID, DEFAULT_CLIENT_SSID) != 0) + // append new c string to temp buffer efficiently bool oappend(const char* txt); // append new number to temp buffer efficiently From 8da985b6d03a7a77b2bc6932dbe95fcb9fb36bd2 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Tue, 7 Apr 2020 00:04:09 +0200 Subject: [PATCH 208/232] Fixed RBG and BGR getPixelColor (#825) Improved formatting --- CHANGELOG.md | 5 + wled00/FX_fcn.cpp | 4 +- wled00/const.h | 4 + wled00/{func_declare.h => fcn_declare.h} | 4 +- wled00/html_other.h | 2 +- wled00/html_settings.h | 4 +- wled00/wled.cpp | 4 + wled00/wled.h | 114 ++++++++++++----------- wled00/wled00.ino | 11 ++- 9 files changed, 90 insertions(+), 62 deletions(-) rename wled00/{func_declare.h => fcn_declare.h} (99%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8df63ff8..d92a1966 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ### Development versions after 0.9.1 release +#### Build 2004061 + +- Fixed RBG and BGR getPixelColor (#825) +- Improved formatting + #### Build 2004060 - Consolidated global variables in wled.h diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index de89a430..69b8c88a 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -412,8 +412,8 @@ uint32_t WS2812FX::getPixelColor(uint16_t i) case 0: return ((col.W << 24) | (col.G << 8) | (col.R << 16) | (col.B)); //0 = GRB, default case 1: return ((col.W << 24) | (col.R << 8) | (col.G << 16) | (col.B)); //1 = RGB, common for WS2811 case 2: return ((col.W << 24) | (col.B << 8) | (col.R << 16) | (col.G)); //2 = BRG - case 3: return ((col.W << 24) | (col.R << 8) | (col.B << 16) | (col.G)); //3 = RBG - case 4: return ((col.W << 24) | (col.B << 8) | (col.G << 16) | (col.R)); //4 = BGR + case 3: return ((col.W << 24) | (col.B << 8) | (col.G << 16) | (col.R)); //3 = RBG + case 4: return ((col.W << 24) | (col.R << 8) | (col.B << 16) | (col.G)); //4 = BGR case 5: return ((col.W << 24) | (col.G << 8) | (col.B << 16) | (col.R)); //5 = GBR } return 0; diff --git a/wled00/const.h b/wled00/const.h index ef19e51b..173d7e7d 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -1,6 +1,10 @@ #ifndef WLED_CONST_H #define WLED_CONST_H +/* + * Readability defines and their associated numerical values + compile-time constants + */ + //Defaults #define DEFAULT_CLIENT_SSID "Your_Network" #define DEFAULT_AP_PASS "wled1234" diff --git a/wled00/func_declare.h b/wled00/fcn_declare.h similarity index 99% rename from wled00/func_declare.h rename to wled00/fcn_declare.h index db3cb4bc..c0826d6a 100644 --- a/wled00/func_declare.h +++ b/wled00/fcn_declare.h @@ -1,5 +1,5 @@ -#ifndef WLED_FUNC_DECLARE_H -#define WLED_FUNC_DECLARE_H +#ifndef WLED_FCN_DECLARE_H +#define WLED_FCN_DECLARE_H #include #include "src/dependencies/espalexa/EspalexaDevice.h" #include "src/dependencies/e131/ESPAsyncE131.h" diff --git a/wled00/html_other.h b/wled00/html_other.h index e27c64dd..ce36cff4 100644 --- a/wled00/html_other.h +++ b/wled00/html_other.h @@ -1,5 +1,5 @@ /* - * Various pages + * Various web pages */ //USER HTML HERE (/u subpage) diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 284d9b30..e2d94f97 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -1,6 +1,6 @@ /* - Settings html -*/ + * Settings html + */ //common CSS of settings pages const char PAGE_settingsCss[] PROGMEM = R"=====()====="; diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 701b98e3..b68723bb 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -2,6 +2,10 @@ #include "wled.h" #include +/* + * Main WLED class implementation. Mostly initialization and connection logic + */ + WLED::WLED() { } diff --git a/wled00/wled.h b/wled00/wled.h index 0c6d3114..6f4b2cb0 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2004060 +#define VERSION 2004061 // ESP8266-01 (blue) got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.4.2 and the setting 512K(No SPIFFS). @@ -81,7 +81,7 @@ #include "src/dependencies/json/AsyncJson-v6.h" #include "src/dependencies/json/ArduinoJson-v6.h" -#include "func_declare.h" +#include "fcn_declare.h" #include "html_ui.h" #include "html_settings.h" #include "html_other.h" @@ -128,6 +128,12 @@ #define DEBUG_PRINTF(x) #endif +// GLOBAL VARIABLES +// both declared and defined in header (solution from http://www.keil.com/support/docs/1868.htm) +// +//e.g. byte test = 2 becomes WLED_GLOBAL byte test _INIT(2); +// int arr[]{0,1,2} becomes WLED_GLOBAL int arr[] _INIT_N(({0,1,2})); + #ifndef WLED_DEFINE_GLOBAL_VARS # define WLED_GLOBAL extern # define _INIT(x) @@ -170,13 +176,13 @@ WLED_GLOBAL bool noWifiSleep _INIT(false); // disabling // LED CONFIG WLED_GLOBAL uint16_t ledCount _INIT(30); // overcurrent prevented by ABL -WLED_GLOBAL bool useRGBW _INIT(false); // SK6812 strips can contain an extra White channel +WLED_GLOBAL bool useRGBW _INIT(false); // SK6812 strips can contain an extra White channel WLED_GLOBAL bool turnOnAtBoot _INIT(true); // turn on LEDs at power-up -WLED_GLOBAL byte bootPreset _INIT(0); // save preset to load after power-up +WLED_GLOBAL byte bootPreset _INIT(0); // save preset to load after power-up -WLED_GLOBAL byte col[] _INIT_N(({ 255, 160, 0, 0 })); // current RGB(W) primary color. col[] should be updated if you want to change the color. -WLED_GLOBAL byte colSec[] _INIT_N(({ 0, 0, 0, 0 })); // current RGB(W) secondary color -WLED_GLOBAL byte briS _INIT(128); // default brightness +WLED_GLOBAL byte col[] _INIT_N(({ 255, 160, 0, 0 })); // current RGB(W) primary color. col[] should be updated if you want to change the color. +WLED_GLOBAL byte colSec[] _INIT_N(({ 0, 0, 0, 0 })); // current RGB(W) secondary color +WLED_GLOBAL byte briS _INIT(128); // default brightness WLED_GLOBAL byte nightlightTargetBri _INIT(0); // brightness after nightlight is over WLED_GLOBAL byte nightlightDelayMins _INIT(60); @@ -189,52 +195,52 @@ WLED_GLOBAL bool skipFirstLed _INIT(false); // ignore first LED in strip WLED_GLOBAL byte briMultiplier _INIT(100); // % of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) // User Interface CONFIG -WLED_GLOBAL char serverDescription[33] _INIT("WLED"); // Name of module -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 char serverDescription[33] _INIT("WLED"); // Name of module +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 // Sync CONFIG -WLED_GLOBAL bool buttonEnabled _INIT(true); -WLED_GLOBAL byte irEnabled _INIT(0); // Infrared receiver +WLED_GLOBAL bool buttonEnabled _INIT(true); +WLED_GLOBAL byte irEnabled _INIT(0); // Infrared receiver -WLED_GLOBAL uint16_t udpPort _INIT(21324); // WLED notifier default port -WLED_GLOBAL uint16_t udpRgbPort _INIT(19446); // Hyperion port +WLED_GLOBAL uint16_t udpPort _INIT(21324); // WLED notifier default port +WLED_GLOBAL uint16_t udpRgbPort _INIT(19446); // Hyperion port -WLED_GLOBAL bool receiveNotificationBrightness _INIT(true); // apply brightness from incoming notifications -WLED_GLOBAL bool receiveNotificationColor _INIT(true); // apply color -WLED_GLOBAL bool receiveNotificationEffects _INIT(true); // apply effects setup -WLED_GLOBAL bool notifyDirect _INIT(false); // send notification if change via UI or HTTP API -WLED_GLOBAL bool notifyButton _INIT(false); // send if updated by button or infrared remote -WLED_GLOBAL bool notifyAlexa _INIT(false); // send notification if updated via Alexa -WLED_GLOBAL bool notifyMacro _INIT(false); // send notification for macro -WLED_GLOBAL bool notifyHue _INIT(true); // send notification if Hue light changes -WLED_GLOBAL bool notifyTwice _INIT(false); // notifications use UDP: enable if devices don't sync reliably +WLED_GLOBAL bool receiveNotificationBrightness _INIT(true); // apply brightness from incoming notifications +WLED_GLOBAL bool receiveNotificationColor _INIT(true); // apply color +WLED_GLOBAL bool receiveNotificationEffects _INIT(true); // apply effects setup +WLED_GLOBAL bool notifyDirect _INIT(false); // send notification if change via UI or HTTP API +WLED_GLOBAL bool notifyButton _INIT(false); // send if updated by button or infrared remote +WLED_GLOBAL bool notifyAlexa _INIT(false); // send notification if updated via Alexa +WLED_GLOBAL bool notifyMacro _INIT(false); // send notification for macro +WLED_GLOBAL bool notifyHue _INIT(true); // send notification if Hue light changes +WLED_GLOBAL bool notifyTwice _INIT(false); // notifications use UDP: enable if devices don't sync reliably -WLED_GLOBAL bool alexaEnabled _INIT(true); // enable device discovery by Amazon Echo -WLED_GLOBAL char alexaInvocationName[33] _INIT("Light"); // speech control name of device. Choose something voice-to-text can understand +WLED_GLOBAL bool alexaEnabled _INIT(true); // enable device discovery by Amazon Echo +WLED_GLOBAL char alexaInvocationName[33] _INIT("Light"); // speech control name of device. Choose something voice-to-text can understand -WLED_GLOBAL char blynkApiKey[36] _INIT(""); // Auth token for Blynk server. If empty, no connection will be made +WLED_GLOBAL char blynkApiKey[36] _INIT(""); // Auth token for Blynk server. If empty, no connection will be made -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 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 +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 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 -WLED_GLOBAL uint16_t e131Universe _INIT(1); // settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) -WLED_GLOBAL byte DMXMode _INIT(DMX_MODE_MULTIPLE_RGB); // DMX mode (s.a.) -WLED_GLOBAL uint16_t DMXAddress _INIT(1); // DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] -WLED_GLOBAL byte DMXOldDimmer _INIT(0); // only update brightness on change -WLED_GLOBAL byte e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; // to detect packet loss -WLED_GLOBAL bool e131Multicast _INIT(false); // multicast or unicast -WLED_GLOBAL bool e131SkipOutOfSequence _INIT(false); // freeze instead of flickering +WLED_GLOBAL uint16_t e131Universe _INIT(1); // settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) +WLED_GLOBAL byte DMXMode _INIT(DMX_MODE_MULTIPLE_RGB); // DMX mode (s.a.) +WLED_GLOBAL uint16_t DMXAddress _INIT(1); // DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] +WLED_GLOBAL byte DMXOldDimmer _INIT(0); // only update brightness on change +WLED_GLOBAL byte e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; // to detect packet loss +WLED_GLOBAL bool e131Multicast _INIT(false); // multicast or unicast +WLED_GLOBAL bool e131SkipOutOfSequence _INIT(false); // freeze instead of flickering WLED_GLOBAL bool mqttEnabled _INIT(false); -WLED_GLOBAL char mqttDeviceTopic[33] _INIT(""); // main MQTT topic (individual per device, default is wled/mac) -WLED_GLOBAL char mqttGroupTopic[33] _INIT("wled/all"); // second MQTT topic (for example to group devices) -WLED_GLOBAL char mqttServer[33] _INIT(""); // both domains and IPs should work (no SSL) -WLED_GLOBAL char mqttUser[41] _INIT(""); // optional: username for MQTT auth -WLED_GLOBAL char mqttPass[41] _INIT(""); // optional: password for MQTT auth -WLED_GLOBAL char mqttClientID[41] _INIT(""); // override the client ID +WLED_GLOBAL char mqttDeviceTopic[33] _INIT(""); // main MQTT topic (individual per device, default is wled/mac) +WLED_GLOBAL char mqttGroupTopic[33] _INIT("wled/all"); // second MQTT topic (for example to group devices) +WLED_GLOBAL char mqttServer[33] _INIT(""); // both domains and IPs should work (no SSL) +WLED_GLOBAL char mqttUser[41] _INIT(""); // optional: username for MQTT auth +WLED_GLOBAL char mqttPass[41] _INIT(""); // optional: password for MQTT auth +WLED_GLOBAL char mqttClientID[41] _INIT(""); // override the client ID WLED_GLOBAL uint16_t mqttPort _INIT(1883); WLED_GLOBAL bool huePollingEnabled _INIT(false); // poll hue bridge for light state @@ -255,30 +261,30 @@ WLED_GLOBAL int utcOffsetSecs _INIT(0); // Seconds to offset from UTC WLED_GLOBAL byte overlayDefault _INIT(0); // 0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie WLED_GLOBAL byte overlayMin _INIT(0), overlayMax _INIT(ledCount - 1); // boundaries of overlay mode -WLED_GLOBAL byte analogClock12pixel _INIT(0); // The pixel in your strip where "midnight" would be -WLED_GLOBAL bool analogClockSecondsTrail _INIT(false); // Display seconds as trail of LEDs instead of a single pixel -WLED_GLOBAL bool analogClock5MinuteMarks _INIT(false); // Light pixels at every 5-minute position +WLED_GLOBAL byte analogClock12pixel _INIT(0); // The pixel in your strip where "midnight" would be +WLED_GLOBAL bool analogClockSecondsTrail _INIT(false); // Display seconds as trail of LEDs instead of a single pixel +WLED_GLOBAL bool analogClock5MinuteMarks _INIT(false); // Light pixels at every 5-minute position WLED_GLOBAL char cronixieDisplay[7] _INIT("HHMMSS"); // Cronixie Display mask. See wled13_cronixie.ino WLED_GLOBAL bool cronixieBacklight _INIT(true); // Allow digits to be back-illuminated WLED_GLOBAL bool countdownMode _INIT(false); // Clock will count down towards date -WLED_GLOBAL byte countdownYear _INIT(20), countdownMonth _INIT(1); // Countdown target date, year is last two digits -WLED_GLOBAL byte countdownDay _INIT(1), countdownHour _INIT(0); -WLED_GLOBAL byte countdownMin _INIT(0), countdownSec _INIT(0); +WLED_GLOBAL byte countdownYear _INIT(20), countdownMonth _INIT(1); // Countdown target date, year is last two digits +WLED_GLOBAL byte countdownDay _INIT(1) , countdownHour _INIT(0); +WLED_GLOBAL byte countdownMin _INIT(0) , countdownSec _INIT(0); WLED_GLOBAL byte macroBoot _INIT(0); // macro loaded after startup -WLED_GLOBAL byte macroNl _INIT(0); // after nightlight delay over +WLED_GLOBAL byte macroNl _INIT(0); // after nightlight delay over WLED_GLOBAL byte macroCountdown _INIT(0); WLED_GLOBAL byte macroAlexaOn _INIT(0), macroAlexaOff _INIT(0); WLED_GLOBAL byte macroButton _INIT(0), macroLongPress _INIT(0), macroDoublePress _INIT(0); // Security CONFIG -WLED_GLOBAL bool otaLock _INIT(false); // prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks -WLED_GLOBAL bool wifiLock _INIT(false); // prevents access to WiFi settings when OTA lock is enabled -WLED_GLOBAL bool aOtaEnabled _INIT(true); // ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on +WLED_GLOBAL bool otaLock _INIT(false); // prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks +WLED_GLOBAL bool wifiLock _INIT(false); // prevents access to WiFi settings when OTA lock is enabled +WLED_GLOBAL bool aOtaEnabled _INIT(true); // ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on -WLED_GLOBAL uint16_t userVar0 _INIT(0), userVar1 _INIT(0); +WLED_GLOBAL uint16_t userVar0 _INIT(0), userVar1 _INIT(0); //available for use in usermod #ifdef WLED_ENABLE_DMX // dmx CONFIG diff --git a/wled00/wled00.ino b/wled00/wled00.ino index f806e5b3..866543ab 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -1,5 +1,14 @@ /* - * Arduino IDE compatibility file. + * WLED Arduino IDE compatibility file. + * + * Where has everything gone? + * + * In April 2020, the project's structure underwent a major change. + * Global variables are now found in file "wled.h" + * Global function declarations are found in "fcn_declare.h" + * + * Usermod compatibility: Existing wled06_usermod.ino mods should continue to work. Delete usermod.cpp. + * New usermods should use usermod.cpp instead. */ #include "wled.h" From a31da3186f4327c37a0baf23d38774cb40cb89c4 Mon Sep 17 00:00:00 2001 From: Bukovina Date: Wed, 8 Apr 2020 08:35:32 -0400 Subject: [PATCH 209/232] UserMod files update --- usermods/Enclosure_with_OLED_temp_ESP07/readme.md | 2 ++ .../{wled06_usermod.ino => usermod.cpp} | 2 ++ usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md | 1 + .../{wled06_usermod.ino => usermod.cpp} | 2 ++ 4 files changed, 7 insertions(+) rename usermods/Enclosure_with_OLED_temp_ESP07/{wled06_usermod.ino => usermod.cpp} (99%) rename usermods/Wemos_D1_mini+Wemos32_mini_shield/{wled06_usermod.ino => usermod.cpp} (99%) diff --git a/usermods/Enclosure_with_OLED_temp_ESP07/readme.md b/usermods/Enclosure_with_OLED_temp_ESP07/readme.md index 817fb7e4..f39cece6 100644 --- a/usermods/Enclosure_with_OLED_temp_ESP07/readme.md +++ b/usermods/Enclosure_with_OLED_temp_ESP07/readme.md @@ -1,5 +1,7 @@ # Almost universal controller board for outdoor applications This usermod is using ideas from @mrVanboy and @400killer + +Installation of file: Copy and replace file in wled00 directory ## Project repository - [Original repository](https://github.com/srg74/Controller-for-WLED-firmware) - Main controller repository ## Features diff --git a/usermods/Enclosure_with_OLED_temp_ESP07/wled06_usermod.ino b/usermods/Enclosure_with_OLED_temp_ESP07/usermod.cpp similarity index 99% rename from usermods/Enclosure_with_OLED_temp_ESP07/wled06_usermod.ino rename to usermods/Enclosure_with_OLED_temp_ESP07/usermod.cpp index 99d155ca..9724a1b7 100644 --- a/usermods/Enclosure_with_OLED_temp_ESP07/wled06_usermod.ino +++ b/usermods/Enclosure_with_OLED_temp_ESP07/usermod.cpp @@ -1,3 +1,5 @@ +#include "wled.h" +#include #include // from https://github.com/olikraus/u8g2/ #include //Dallastemperature sensor //The SCL and SDA pins are defined here. diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md index 765e5683..39bc8148 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md @@ -1,4 +1,5 @@ # Wemos D1 mini and Wemos32 mini shield +- Installation of file: Copy and replace file in wled00 directory - Added third choice of controller Heltec WiFi-Kit-8. Totally DIY but with OLED display. ## Project repository - [Original repository](https://github.com/srg74/WLED-wemos-shield) - WLED Wemos shield repository diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino b/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod.cpp similarity index 99% rename from usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino rename to usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod.cpp index d75cf0a7..579fbfe4 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/wled06_usermod.ino +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod.cpp @@ -1,3 +1,5 @@ +#include "wled.h" +#include #include // from https://github.com/olikraus/u8g2/ #include //Dallastemperature sensor #ifdef ARDUINO_ARCH_ESP32 //ESP32 boards From 88b67b9541d9b67f886f005cb406080f3a695893 Mon Sep 17 00:00:00 2001 From: Bukovina Date: Wed, 8 Apr 2020 08:40:44 -0400 Subject: [PATCH 210/232] Usermod changes --- .../Enclosure_with_OLED_temp_ESP07/readme.md | 5 +++++ .../readme.md | 22 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/usermods/Enclosure_with_OLED_temp_ESP07/readme.md b/usermods/Enclosure_with_OLED_temp_ESP07/readme.md index f39cece6..b90beefe 100644 --- a/usermods/Enclosure_with_OLED_temp_ESP07/readme.md +++ b/usermods/Enclosure_with_OLED_temp_ESP07/readme.md @@ -27,6 +27,11 @@ Uncomment `U8g2@~2.27.3`,`DallasTemperature@~3.8.0`,`OneWire@~2.3.5 under` `[com ```ini # platformio.ini ... +[platformio] +... +; default_envs = esp01_1m_full +default_envs = esp07 +... [common] ... lib_deps_external = diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md index 39bc8148..53405a5e 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md @@ -23,3 +23,25 @@ - SSD1306 128x32 I2C OLED display - DS18B20 (temperature sensor) - Push button (N.O. momentary switch) + +### Platformio requirements +Uncomment `U8g2@~2.27.3`,`DallasTemperature@~3.8.0`,`OneWire@~2.3.5 under` `[common]` section in `platformio.ini`: +```ini +# platformio.ini +... +[platformio] +... +; default_envs = esp07 +default_envs = d1_mini +... +[common] +... +lib_deps_external = + ... + #For use SSD1306 OLED display uncomment following + U8g2@~2.27.3 + #For Dallas sensor uncomment following 2 lines + DallasTemperature@~3.8.0 + OneWire@~2.3.5 +... +``` From 5be88dd1880df13c3e4a990724963fc18266b325 Mon Sep 17 00:00:00 2001 From: Bukovina Date: Wed, 8 Apr 2020 09:18:26 -0400 Subject: [PATCH 211/232] QuinLED mod update --- platformio.ini | 2 +- .../{readme.txt => readme.md} | 33 +++++++++++++++++-- .../{wled06_usermod.ino => usermod.cpp} | 2 ++ 3 files changed, 33 insertions(+), 4 deletions(-) rename usermods/QuinLED_Dig_Uno_Temp_MQTT/{readme.txt => readme.md} (55%) rename usermods/QuinLED_Dig_Uno_Temp_MQTT/{wled06_usermod.ino => usermod.cpp} (97%) diff --git a/platformio.ini b/platformio.ini index 91bdd867..5530f9a2 100644 --- a/platformio.ini +++ b/platformio.ini @@ -30,7 +30,7 @@ default_envs = d1_mini, esp01, esp01_1m_ota, esp32dev ; default_envs = d1_mini ; default_envs = heltec_wifi_kit_8 ; default_envs = d1_mini_debug -; default_envs = esp32dev +default_envs = esp32dev ; default_envs = esp8285_4CH_MagicHome ; default_envs = esp8285_4CH_H801 ; default_envs = esp8285_5CH_H801 diff --git a/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.txt b/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.md similarity index 55% rename from usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.txt rename to usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.md index eb8da7ee..60fc31f7 100644 --- a/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.txt +++ b/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.md @@ -1,7 +1,34 @@ -These files allow WLED 0.9.1 to report the temp sensor on the Quinled board to MQTT. I use it to report the board temp to Home Assistant via MQTT, so it will send notifications if something happens and the board start to heat up. +# QuinLED Dig Uno board +These files allow WLED 0.9.1 to report the temp sensor on the Quinled board to MQTT. I use it to report the board temp to Home Assistant via MQTT, so it will send notifications if something happens and the board start to heat up. This code uses Aircookie's WLED software. It has a premade file for user modifications. I use it to publish the temperature from the dallas temperature sensor on the Quinled board. The entries for the top of the WLED00 file, initializes the required libraries, and variables for the sensor. The .ino file waits for 60 seconds, and checks to see if the MQTT server is connected (thanks Aircoookie). It then poles the sensor, and published it using the MQTT service already running, using the main topic programmed in the WLED UI. -To install: +Installation of file: Copy and replace file in wled00 directory -Replace the WLED06_usermod.ino file in Aircoookies WLED folder. +## Project link + +* [QuinLED-Dig-Uno](https://quinled.info/2018/09/15/quinled-dig-uno/) - Project link + +### Platformio requirements + +Uncomment `DallasTemperature@~3.8.0`,`OneWire@~2.3.5 under` `[common]` section in `platformio.ini`: + +```ini +# platformio.ini +... +[platformio] +... +; default_envs = esp07 +default_envs = d1_mini +... +[common] +... +lib_deps_external = + ... + #For use SSD1306 OLED display uncomment following + U8g2@~2.27.3 + #For Dallas sensor uncomment following 2 lines + DallasTemperature@~3.8.0 + OneWire@~2.3.5 +... +``` diff --git a/usermods/QuinLED_Dig_Uno_Temp_MQTT/wled06_usermod.ino b/usermods/QuinLED_Dig_Uno_Temp_MQTT/usermod.cpp similarity index 97% rename from usermods/QuinLED_Dig_Uno_Temp_MQTT/wled06_usermod.ino rename to usermods/QuinLED_Dig_Uno_Temp_MQTT/usermod.cpp index d4b77ccc..5b4e2e5c 100644 --- a/usermods/QuinLED_Dig_Uno_Temp_MQTT/wled06_usermod.ino +++ b/usermods/QuinLED_Dig_Uno_Temp_MQTT/usermod.cpp @@ -1,3 +1,5 @@ +#include +#include "wled.h" //Intiating code for QuinLED Dig-Uno temp sensor //Uncomment Celsius if that is your prefered temperature scale #include //Dallastemperature sensor From b5b1dbfe85bcc9b95fa379b56ec4dfc3ccb6d1a9 Mon Sep 17 00:00:00 2001 From: Bukovina Date: Wed, 8 Apr 2020 09:30:49 -0400 Subject: [PATCH 212/232] Fix platformio.ini --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 5530f9a2..91bdd867 100644 --- a/platformio.ini +++ b/platformio.ini @@ -30,7 +30,7 @@ default_envs = d1_mini, esp01, esp01_1m_ota, esp32dev ; default_envs = d1_mini ; default_envs = heltec_wifi_kit_8 ; default_envs = d1_mini_debug -default_envs = esp32dev +; default_envs = esp32dev ; default_envs = esp8285_4CH_MagicHome ; default_envs = esp8285_4CH_H801 ; default_envs = esp8285_5CH_H801 From 3a84292f82acaa66bb0e1b1565d192fe581cdffc Mon Sep 17 00:00:00 2001 From: srg74 <28492985+srg74@users.noreply.github.com> Date: Thu, 9 Apr 2020 09:06:44 -0400 Subject: [PATCH 213/232] Update readme.md --- usermods/readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usermods/readme.md b/usermods/readme.md index 350212fb..34ddef0f 100644 --- a/usermods/readme.md +++ b/usermods/readme.md @@ -1,13 +1,13 @@ ### Usermods -This folder serves as a repository for usermods (custom `wled06_usermod.ino` files)! +This folder serves as a repository for usermods (custom `usermod.cpp` files)! If you have created an usermod that you believe is useful (for example to support a particular sensor, display, feature...), feel free to contribute by opening a pull request! In order for other people to be able to have fun with your usermod, please keep these points in mind: - Create a folder in this folder with a descriptive name (for example `usermod_ds18b20_temp_sensor_mqtt`) -- Include your custom `wled06_usermod.ino` file +- Include your custom `usermod.cpp` file - If your usermod requires changes to other WLED files, please write a `readme.md` outlining the steps one has to take to use the usermod - Create a pull request! - If your feature is useful for the majority of WLED users, I will consider adding it to the base code! From 19945e4ccb77f9ce84a48bec734b5aa626b07b7c Mon Sep 17 00:00:00 2001 From: cschwinne Date: Fri, 10 Apr 2020 12:30:08 +0200 Subject: [PATCH 214/232] Fix DMX compilation --- CHANGELOG.md | 51 +- wled00/FX.cpp | 16 +- wled00/FX.h | 4 +- wled00/FX_fcn.cpp | 12 +- wled00/NpbWrapper.h | 16 +- wled00/{wled12_alexa.ino => alexa.cpp} | 5 +- wled00/{wled16_blynk.ino => blynk.cpp} | 3 + wled00/{wled09_button.ino => button.cpp} | 4 +- wled00/{wled14_colors.ino => colors.cpp} | 9 +- wled00/const.h | 24 +- wled00/dmx.cpp | 67 ++ wled00/e131.cpp | 144 ++++ wled00/fcn_declare.h | 184 ++++++ wled00/file.cpp | 55 ++ wled00/html_other.h | 2 +- wled00/html_settings.h | 17 +- wled00/{wled15_hue.ino => hue.cpp} | 4 + wled00/{wled20_ir.ino => ir.cpp} | 4 +- wled00/{wled19_json.ino => json.cpp} | 22 + wled00/{wled08_led.ino => led.cpp} | 5 +- wled00/{wled17_mqtt.ino => mqtt.cpp} | 2 + wled00/{wled10_ntp.ino => ntp.cpp} | 4 + wled00/{wled13_cronixie.ino => overlay.cpp} | 147 ++++- wled00/{wled03_set.ino => set.cpp} | 18 +- .../src/dependencies/blynk/BlynkSimpleEsp.cpp | 5 + .../src/dependencies/blynk/BlynkSimpleEsp.h | 4 +- wled00/src/dependencies/dmx/ESPDMX.cpp | 106 +++ wled00/src/dependencies/dmx/ESPDMX.h | 31 + .../dependencies/espalexa/EspalexaDevice.h | 2 +- wled00/{wled07_notify.ino => udp.cpp} | 149 +---- wled00/{wled06_usermod.ino => usermod.cpp} | 3 +- wled00/wled.cpp | 458 +++++++++++++ wled00/wled.h | 519 +++++++++++++++ wled00/wled00.ino | 624 +----------------- wled00/wled00.vcxproj | 317 +++------ wled00/wled00.vcxproj.filters | 547 +++++++++------ wled00/wled05_init.ino | 339 ---------- wled00/wled11_ol.ino | 128 ---- wled00/wled21_dmx.ino | 62 -- wled00/{wled01_eeprom.ino => wled_eeprom.cpp} | 19 +- wled00/{wled04_file.ino => wled_serial.cpp} | 46 +- wled00/{wled18_server.ino => wled_server.cpp} | 6 +- wled00/{wled02_xml.ino => xml.cpp} | 5 +- 43 files changed, 2333 insertions(+), 1856 deletions(-) rename wled00/{wled12_alexa.ino => alexa.cpp} (93%) rename wled00/{wled16_blynk.ino => blynk.cpp} (95%) rename wled00/{wled09_button.ino => button.cpp} (97%) rename wled00/{wled14_colors.ino => colors.cpp} (97%) create mode 100644 wled00/dmx.cpp create mode 100644 wled00/e131.cpp create mode 100644 wled00/fcn_declare.h create mode 100644 wled00/file.cpp rename wled00/{wled15_hue.ino => hue.cpp} (99%) rename wled00/{wled20_ir.ino => ir.cpp} (99%) rename wled00/{wled19_json.ino => json.cpp} (97%) rename wled00/{wled08_led.ino => led.cpp} (99%) rename wled00/{wled17_mqtt.ino => mqtt.cpp} (99%) rename wled00/{wled10_ntp.ino => ntp.cpp} (98%) rename wled00/{wled13_cronixie.ino => overlay.cpp} (66%) rename wled00/{wled03_set.ino => set.cpp} (98%) create mode 100644 wled00/src/dependencies/blynk/BlynkSimpleEsp.cpp create mode 100644 wled00/src/dependencies/dmx/ESPDMX.cpp create mode 100644 wled00/src/dependencies/dmx/ESPDMX.h rename wled00/{wled07_notify.ino => udp.cpp} (55%) rename wled00/{wled06_usermod.ino => usermod.cpp} (92%) create mode 100644 wled00/wled.cpp create mode 100644 wled00/wled.h delete mode 100644 wled00/wled05_init.ino delete mode 100644 wled00/wled11_ol.ino delete mode 100644 wled00/wled21_dmx.ino rename wled00/{wled01_eeprom.ino => wled_eeprom.cpp} (97%) rename wled00/{wled04_file.ino => wled_serial.cpp} (55%) rename wled00/{wled18_server.ino => wled_server.cpp} (99%) rename wled00/{wled02_xml.ino => xml.cpp} (99%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d3a466c..31c22f52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,43 +2,62 @@ ### Development versions after 0.9.1 release + +#### Build 2004100 + +- Fixed DMX output compilation +- Added DMX start LED setting + +#### Build 2004061 + +- Fixed RBG and BGR getPixelColor (#825) +- Improved formatting + +#### Build 2004060 + +- Consolidated global variables in wled.h + +#### Build 2003300 + +- Major change of project structure from .ino to .cpp and func_declare.h + #### Build 2003262 - - Fixed compilation for Analog LEDs - - Fixed sync settings network port fields too small +- Fixed compilation for Analog LEDs +- Fixed sync settings network port fields too small #### Build 2003261 - - Fixed live preview not displaying whole light if over 255 LEDs +- Fixed live preview not displaying whole light if over 255 LEDs #### Build 2003251 - - Added Pacifica effect (tentative, doesn't yet support other colors) - - Added Atlantica palette - - Fixed ESP32 build of Espalexa +- Added Pacifica effect (tentative, doesn't yet support other colors) +- Added Atlantica palette +- Fixed ESP32 build of Espalexa #### Build 2003222 - - Fixed Alexa Whites on non-RGBW lights (bump Espalexa to 2.4.5) +- Fixed Alexa Whites on non-RGBW lights (bump Espalexa to 2.4.5) #### Build 2003221 - - Moved Cronixie driver from FX library to drawOverlay handler +- Moved Cronixie driver from FX library to drawOverlay handler #### Build 2003211 - - Added custom mapping compile define to FX_fcn.h - - Merged pull request #784 by @TravisDean: Fixed initialization bug when toggling skip first - - Added link to youtube videos by Room31 to readme +- Added custom mapping compile define to FX_fcn.h +- Merged pull request #784 by @TravisDean: Fixed initialization bug when toggling skip first +- Added link to youtube videos by Room31 to readme #### Build 2003141 - - Fixed color of main segment returned in JSON API during transition not being target color (closes #765) - - Fixed arlsLock() being called after pixels set in E1.31 (closes #772) - - Fixed HTTP API calls not having an effect if no segment selected (now applies to main segment) +- Fixed color of main segment returned in JSON API during transition not being target color (closes #765) +- Fixed arlsLock() being called after pixels set in E1.31 (closes #772) +- Fixed HTTP API calls not having an effect if no segment selected (now applies to main segment) #### Build 2003121 - - Created changelog.md - make tracking changes to code easier - - Merged pull request #766 by @pille: Fix E1.31 out-of sequence detection +- Created changelog.md - make tracking changes to code easier +- Merged pull request #766 by @pille: Fix E1.31 out-of sequence detection diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 1e3ec2d9..7230a9b3 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -601,7 +601,7 @@ uint16_t WS2812FX::mode_hyper_sparkle(void) { } if(random8(5) < 2) { - for(uint16_t i = 0; i < max(1, SEGLEN/3); i++) { + for(uint16_t i = 0; i < MAX(1, SEGLEN/3); i++) { setPixelColor(random16(SEGLEN), SEGCOLOR(1)); } return 20; @@ -1115,7 +1115,7 @@ uint16_t WS2812FX::mode_fireworks() { if (valid1) setPixelColor(SEGENV.aux0 , sv1); if (valid2) setPixelColor(SEGENV.aux1, sv2); - for(uint16_t i=0; i> 1)) == 0) { uint16_t index = random(SEGLEN); setPixelColor(index, color_from_palette(random8(), false, false, 0)); @@ -1162,12 +1162,12 @@ uint16_t WS2812FX::mode_fire_flicker(void) { byte r = (SEGCOLOR(0) >> 16) & 0xFF; byte g = (SEGCOLOR(0) >> 8) & 0xFF; byte b = (SEGCOLOR(0) & 0xFF); - byte lum = (SEGMENT.palette == 0) ? max(w, max(r, max(g, b))) : 255; + byte lum = (SEGMENT.palette == 0) ? MAX(w, MAX(r, MAX(g, b))) : 255; lum /= (((256-SEGMENT.intensity)/16)+1); for(uint16_t i = 0; i < SEGLEN; i++) { byte flicker = random8(lum); if (SEGMENT.palette == 0) { - setPixelColor(i, max(r - flicker, 0), max(g - flicker, 0), max(b - flicker, 0), max(w - flicker, 0)); + setPixelColor(i, MAX(r - flicker, 0), MAX(g - flicker, 0), MAX(b - flicker, 0), MAX(w - flicker, 0)); } else { setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, 255 - flicker)); } @@ -1197,7 +1197,7 @@ uint16_t WS2812FX::gradient_base(bool loading) { { val = abs(((i>pp) ? p2:pp) -i); } else { - val = min(abs(pp-i),min(abs(p1-i),abs(p2-i))); + val = MIN(abs(pp-i),MIN(abs(p1-i),abs(p2-i))); } val = (brd > val) ? val/brd * 255 : 255; setPixelColor(i, color_blend(SEGCOLOR(0), color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), val)); @@ -1778,7 +1778,7 @@ uint16_t WS2812FX::mode_fire_2012() // Step 4. Map from heat cells to LED colors for (uint16_t j = 0; j < SEGLEN; j++) { - CRGB color = ColorFromPalette(currentPalette, min(heat[j],240), 255, LINEARBLEND); + CRGB color = ColorFromPalette(currentPalette, MIN(heat[j],240), 255, LINEARBLEND); setPixelColor(j, color.red, color.green, color.blue); } return FRAMETIME; @@ -2573,7 +2573,7 @@ uint16_t WS2812FX::mode_bouncing_balls(void) { uint32_t color = SEGCOLOR(0); if (SEGMENT.palette) { - color = color_wheel(i*(256/max(numBalls, 8))); + color = color_wheel(i*(256/MAX(numBalls, 8))); } else if (hasCol2) { color = SEGCOLOR(i % NUM_COLORS); } @@ -3101,7 +3101,7 @@ uint16_t WS2812FX::mode_plasma(void) { */ uint16_t WS2812FX::mode_percent(void) { - uint8_t percent = max(0, min(200, SEGMENT.intensity)); + uint8_t percent = MAX(0, MIN(200, SEGMENT.intensity)); uint16_t active_leds = (percent < 100) ? SEGLEN * percent / 100.0 : SEGLEN * (200 - percent) / 100.0; diff --git a/wled00/FX.h b/wled00/FX.h index a87da8e2..a38a1f54 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -38,8 +38,8 @@ #define DEFAULT_SPEED (uint8_t)128 #define DEFAULT_COLOR (uint32_t)0xFFAA00 -#define min(a,b) ((a)<(b)?(a):(b)) -#define max(a,b) ((a)>(b)?(a):(b)) +#define MIN(a,b) ((a)<(b)?(a):(b)) +#define MAX(a,b) ((a)>(b)?(a):(b)) /* Not used in all effects yet */ #define WLED_FPS 42 diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 05568623..69b8c88a 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -224,7 +224,7 @@ void WS2812FX::show(void) { if(useWackyWS2815PowerModel) { // ignore white component on WS2815 power calculation - powerSum += (max(max(c.R,c.G),c.B)) * 3; + powerSum += (MAX(MAX(c.R,c.G),c.B)) * 3; } else { @@ -412,8 +412,8 @@ uint32_t WS2812FX::getPixelColor(uint16_t i) case 0: return ((col.W << 24) | (col.G << 8) | (col.R << 16) | (col.B)); //0 = GRB, default case 1: return ((col.W << 24) | (col.R << 8) | (col.G << 16) | (col.B)); //1 = RGB, common for WS2811 case 2: return ((col.W << 24) | (col.B << 8) | (col.R << 16) | (col.G)); //2 = BRG - case 3: return ((col.W << 24) | (col.R << 8) | (col.B << 16) | (col.G)); //3 = RBG - case 4: return ((col.W << 24) | (col.B << 8) | (col.G << 16) | (col.R)); //4 = BGR + case 3: return ((col.W << 24) | (col.B << 8) | (col.G << 16) | (col.R)); //3 = RBG + case 4: return ((col.W << 24) | (col.R << 8) | (col.B << 16) | (col.G)); //4 = BGR case 5: return ((col.W << 24) | (col.G << 8) | (col.B << 16) | (col.R)); //5 = GBR } return 0; @@ -647,7 +647,7 @@ uint8_t WS2812FX::get_random_wheel_index(uint8_t pos) { r = random8(); x = abs(pos - r); y = 255 - x; - d = min(x, y); + d = MIN(x, y); } return r; } @@ -730,8 +730,8 @@ void WS2812FX::handle_palette(void) CHSV prim_hsv = rgb2hsv_approximate(prim); targetPalette = CRGBPalette16( CHSV(prim_hsv.h, prim_hsv.s, prim_hsv.v), //color itself - CHSV(prim_hsv.h, max(prim_hsv.s - 50,0), prim_hsv.v), //less saturated - CHSV(prim_hsv.h, prim_hsv.s, max(prim_hsv.v - 50,0)), //darker + CHSV(prim_hsv.h, MAX(prim_hsv.s - 50,0), prim_hsv.v), //less saturated + CHSV(prim_hsv.h, prim_hsv.s, MAX(prim_hsv.v - 50,0)), //darker CHSV(prim_hsv.h, prim_hsv.s, prim_hsv.v)); //color itself break;} case 4: {//primary + secondary diff --git a/wled00/NpbWrapper.h b/wled00/NpbWrapper.h index 673d8249..6e58b719 100644 --- a/wled00/NpbWrapper.h +++ b/wled00/NpbWrapper.h @@ -9,6 +9,7 @@ //#define USE_APA102 // Uncomment for using APA102 LEDs. //#define USE_WS2801 // Uncomment for using WS2801 LEDs (make sure you have NeoPixelBus v2.5.6 or newer) //#define USE_LPD8806 // Uncomment for using LPD8806 +//#define USE_P9813 // Uncomment for using P9813 LEDs (make sure you have NeoPixelBus v2.5.8 or newer) //#define WLED_USE_ANALOG_LEDS //Uncomment for using "dumb" PWM controlled LEDs (see pins below, default R: gpio5, G: 12, B: 15, W: 13) //#define WLED_USE_H801 //H801 controller. Please uncomment #define WLED_USE_ANALOG_LEDS as well //#define WLED_USE_5CH_LEDS //5 Channel H801 for cold and warm white @@ -35,7 +36,7 @@ //END CONFIGURATION -#if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) +#if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) || defined(USE_P9813) #define CLKPIN 0 #define DATAPIN 2 #if BTNPIN == CLKPIN || BTNPIN == DATAPIN @@ -73,6 +74,8 @@ #define PIXELMETHOD NeoWs2801Method #elif defined(USE_LPD8806) #define PIXELMETHOD Lpd8806Method + #elif defined(USE_P9813) + #define PIXELMETHOD P9813Method #else #define PIXELMETHOD NeoEsp32Rmt0Ws2812xMethod #endif @@ -84,6 +87,8 @@ #define PIXELMETHOD NeoWs2801Method #elif defined(USE_LPD8806) #define PIXELMETHOD Lpd8806Method + #elif defined(USE_P9813) + #define PIXELMETHOD P9813Method #elif LEDPIN == 2 #define PIXELMETHOD NeoEsp8266Uart1Ws2813Method //if you get an error here, try to change to NeoEsp8266UartWs2813Method or update Neopixelbus #elif LEDPIN == 3 @@ -101,7 +106,10 @@ #define PIXELFEATURE4 DotStarLbgrFeature #elif defined(USE_LPD8806) #define PIXELFEATURE3 Lpd8806GrbFeature - #define PIXELFEATURE4 Lpd8806GrbFeature + #define PIXELFEATURE4 Lpd8806GrbFeature +#elif defined(USE_P9813) + #define PIXELFEATURE3 P9813BgrFeature + #define PIXELFEATURE4 NeoGrbwFeature #else #define PIXELFEATURE3 NeoGrbFeature #define PIXELFEATURE4 NeoGrbwFeature @@ -143,7 +151,7 @@ public: switch (_type) { case NeoPixelType_Grb: - #if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) + #if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) || defined(USE_P9813) _pGrb = new NeoPixelBrightnessBus(countPixels, CLKPIN, DATAPIN); #else _pGrb = new NeoPixelBrightnessBus(countPixels, LEDPIN); @@ -152,7 +160,7 @@ public: break; case NeoPixelType_Grbw: - #if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) + #if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) || defined(USE_P9813) _pGrbw = new NeoPixelBrightnessBus(countPixels, CLKPIN, DATAPIN); #else _pGrbw = new NeoPixelBrightnessBus(countPixels, LEDPIN); diff --git a/wled00/wled12_alexa.ino b/wled00/alexa.cpp similarity index 93% rename from wled00/wled12_alexa.ino rename to wled00/alexa.cpp index 5163640e..c9f61ff2 100644 --- a/wled00/wled12_alexa.ino +++ b/wled00/alexa.cpp @@ -1,10 +1,13 @@ +#include "wled.h" + /* - * Alexa Voice On/Off/Brightness Control. Emulates a Philips Hue bridge to Alexa. + * Alexa Voice On/Off/Brightness/Color Control. Emulates a Philips Hue bridge to Alexa. * * This was put together from these two excellent projects: * https://github.com/kakopappa/arduino-esp8266-alexa-wemo-switch * https://github.com/probonopd/ESP8266HueEmulator */ +#include "src/dependencies/espalexa/EspalexaDevice.h" #ifndef WLED_DISABLE_ALEXA void onAlexaChange(EspalexaDevice* dev); diff --git a/wled00/wled16_blynk.ino b/wled00/blynk.cpp similarity index 95% rename from wled00/wled16_blynk.ino rename to wled00/blynk.cpp index ea4d6877..39b43ba8 100644 --- a/wled00/wled16_blynk.ino +++ b/wled00/blynk.cpp @@ -1,3 +1,6 @@ +#include "wled.h" +#include "src/dependencies/blynk/Blynk/BlynkHandlers.h" + /* * Remote light control with the free Blynk app */ diff --git a/wled00/wled09_button.ino b/wled00/button.cpp similarity index 97% rename from wled00/wled09_button.ino rename to wled00/button.cpp index 1cd868be..e71cce96 100644 --- a/wled00/wled09_button.ino +++ b/wled00/button.cpp @@ -1,3 +1,5 @@ +#include "wled.h" + /* * Physical IO */ @@ -44,7 +46,7 @@ void handleButton() if (dur > 6000) //long press { - initAP(true); + WLED::instance().initAP(true); } else if (!buttonLongPressed) { //short press if (macroDoublePress) diff --git a/wled00/wled14_colors.ino b/wled00/colors.cpp similarity index 97% rename from wled00/wled14_colors.ino rename to wled00/colors.cpp index a1db307a..f76499a0 100644 --- a/wled00/wled14_colors.ino +++ b/wled00/colors.cpp @@ -1,3 +1,5 @@ +#include "wled.h" + /* * Color conversion methods */ @@ -18,7 +20,7 @@ void colorFromUint32(uint32_t in, bool secondary) } //load a color without affecting the white channel -void colorFromUint24(uint32_t in, bool secondary = false) +void colorFromUint24(uint32_t in, bool secondary) { if (secondary) { colSec[0] = in >> 16 & 0xFF; @@ -32,7 +34,7 @@ void colorFromUint24(uint32_t in, bool secondary = false) } //relatively change white brightness, minumum A=5 -void relativeChangeWhite(int8_t amount, byte lowerBoundary =0) +void relativeChangeWhite(int8_t amount, byte lowerBoundary) { int16_t new_val = (int16_t) col[3] + amount; if (new_val > 0xFF) new_val = 0xFF; @@ -149,7 +151,8 @@ void colorRGBtoXY(byte* rgb, float* xy) //rgb to coordinates (https://www.develo xy[0] = X / (X + Y + Z); xy[1] = Y / (X + Y + Z); } -#endif +#endif // WLED_DISABLE_HUESYNC + void colorFromDecOrHexString(byte* rgb, char* in) { diff --git a/wled00/const.h b/wled00/const.h index 21be1953..173d7e7d 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -1,5 +1,9 @@ -#ifndef wled_const_h -#define wled_const_h +#ifndef WLED_CONST_H +#define WLED_CONST_H + +/* + * Readability defines and their associated numerical values + compile-time constants + */ //Defaults #define DEFAULT_CLIENT_SSID "Your_Network" @@ -72,4 +76,20 @@ #define HUE_ERROR_TIMEOUT 251 #define HUE_ERROR_ACTIVE 255 +//EEPROM size +#define EEPSIZE 2560 //Maximum is 4096 + +#define NTP_PACKET_SIZE 48 + +// maximum number of LEDs - MAX_LEDS is coming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue +#define MAX_LEDS 1500 +#define MAX_LEDS_DMA 500 + +// string temp buffer (now stored in stack locally) +#define OMAX 2048 + +#define E131_MAX_UNIVERSE_COUNT 9 + +#define ABL_MILLIAMPS_DEFAULT 850; // auto lower brightness to stay close to milliampere limit + #endif diff --git a/wled00/dmx.cpp b/wled00/dmx.cpp new file mode 100644 index 00000000..ccd3b064 --- /dev/null +++ b/wled00/dmx.cpp @@ -0,0 +1,67 @@ +#include "wled.h" + +/* + * Support for DMX via MAX485. + * Change the output pin in src/dependencies/ESPDMX.cpp if needed. + * Library from: + * https://github.com/Rickgg/ESP-Dmx + */ + +#ifdef WLED_ENABLE_DMX +#include "src/dependencies/dmx/ESPDMX.h" +DMXESPSerial dmx; + +void handleDMX() +{ + // TODO: calculate brightness manually if no shutter channel is set + + uint8_t brightness = strip.getBrightness(); + + for (int i = DMXStartLED; i < ledCount; i++) { // uses the amount of LEDs as fixture count + + uint32_t in = strip.getPixelColor(i); // get the colors for the individual fixtures as suggested by Aircoookie in issue #462 + byte w = in >> 24 & 0xFF; + byte r = in >> 16 & 0xFF; + byte g = in >> 8 & 0xFF; + byte b = in & 0xFF; + + int DMXFixtureStart = DMXStart + (DMXGap * (i - DMXStartLED)); + for (int j = 0; j < DMXChannels; j++) { + int DMXAddr = DMXFixtureStart + j; + switch (DMXFixtureMap[j]) { + case 0: // Set this channel to 0. Good way to tell strobe- and fade-functions to fuck right off. + dmx.write(DMXAddr, 0); + break; + case 1: // Red + dmx.write(DMXAddr, r); + break; + case 2: // Green + dmx.write(DMXAddr, g); + break; + case 3: // Blue + dmx.write(DMXAddr, b); + break; + case 4: // White + dmx.write(DMXAddr, w); + break; + case 5: // Shutter channel. Controls the brightness. + dmx.write(DMXAddr, brightness); + break; + case 6: // Sets this channel to 255. Like 0, but more wholesome. + dmx.write(DMXAddr, 255); + break; + } + } + } + + dmx.update(); // update the DMX bus +} + +void initDMX() { + dmx.init(512); // initialize with bus length +} + +#else +void handleDMX() {} +void initDMX() {} +#endif diff --git a/wled00/e131.cpp b/wled00/e131.cpp new file mode 100644 index 00000000..2034f84d --- /dev/null +++ b/wled00/e131.cpp @@ -0,0 +1,144 @@ +#include "wled.h" + +/* + * E1.31 handler + */ + +void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ + //E1.31 protocol support + + uint16_t uni = htons(p->universe); + uint8_t previousUniverses = uni - e131Universe; + uint16_t possibleLEDsInCurrentUniverse; + uint16_t dmxChannels = htons(p->property_value_count) -1; + + // only listen for universes we're handling & allocated memory + if (uni >= (e131Universe + E131_MAX_UNIVERSE_COUNT)) return; + + if (e131SkipOutOfSequence) + if (p->sequence_number < e131LastSequenceNumber[uni-e131Universe] && p->sequence_number > 20 && e131LastSequenceNumber[uni-e131Universe] < 250){ + DEBUG_PRINT("skipping E1.31 frame (last seq="); + DEBUG_PRINT(e131LastSequenceNumber[uni-e131Universe]); + DEBUG_PRINT(", current seq="); + DEBUG_PRINT(p->sequence_number); + DEBUG_PRINT(", universe="); + DEBUG_PRINT(uni); + DEBUG_PRINTLN(")"); + return; + } + e131LastSequenceNumber[uni-e131Universe] = p->sequence_number; + + // update status info + realtimeIP = clientIP; + + switch (DMXMode) { + case DMX_MODE_DISABLED: + return; // nothing to do + break; + + case DMX_MODE_SINGLE_RGB: + if (uni != e131Universe) return; + if (dmxChannels-DMXAddress+1 < 3) return; + arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); + for (uint16_t i = 0; i < ledCount; i++) + setRealtimePixel(i, p->property_values[DMXAddress+0], p->property_values[DMXAddress+1], p->property_values[DMXAddress+2], 0); + break; + + case DMX_MODE_SINGLE_DRGB: + if (uni != e131Universe) return; + if (dmxChannels-DMXAddress+1 < 4) return; + arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); + if (DMXOldDimmer != p->property_values[DMXAddress+0]) { + DMXOldDimmer = p->property_values[DMXAddress+0]; + bri = p->property_values[DMXAddress+0]; + strip.setBrightness(bri); + } + for (uint16_t i = 0; i < ledCount; i++) + setRealtimePixel(i, p->property_values[DMXAddress+1], p->property_values[DMXAddress+2], p->property_values[DMXAddress+3], 0); + break; + + case DMX_MODE_EFFECT: + if (uni != e131Universe) return; + if (dmxChannels-DMXAddress+1 < 11) return; + if (DMXOldDimmer != p->property_values[DMXAddress+0]) { + DMXOldDimmer = p->property_values[DMXAddress+0]; + bri = p->property_values[DMXAddress+0]; + } + if (p->property_values[DMXAddress+1] < MODE_COUNT) + effectCurrent = p->property_values[DMXAddress+ 1]; + effectSpeed = p->property_values[DMXAddress+ 2]; // flickers + effectIntensity = p->property_values[DMXAddress+ 3]; + effectPalette = p->property_values[DMXAddress+ 4]; + col[0] = p->property_values[DMXAddress+ 5]; + col[1] = p->property_values[DMXAddress+ 6]; + col[2] = p->property_values[DMXAddress+ 7]; + colSec[0] = p->property_values[DMXAddress+ 8]; + colSec[1] = p->property_values[DMXAddress+ 9]; + colSec[2] = p->property_values[DMXAddress+10]; + if (dmxChannels-DMXAddress+1 > 11) + { + col[3] = p->property_values[DMXAddress+11]; //white + colSec[3] = p->property_values[DMXAddress+12]; + } + transitionDelayTemp = 0; // act fast + colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); // don't send UDP + return; // don't activate realtime live mode + break; + + case DMX_MODE_MULTIPLE_RGB: + arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); + if (previousUniverses == 0) { + // first universe of this fixture + possibleLEDsInCurrentUniverse = (dmxChannels - DMXAddress + 1) / 3; + for (uint16_t i = 0; i < ledCount; i++) { + if (i >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) + setRealtimePixel(i, p->property_values[DMXAddress+i*3+0], p->property_values[DMXAddress+i*3+1], p->property_values[DMXAddress+i*3+2], 0); + } + } else if (previousUniverses > 0 && uni < (e131Universe + E131_MAX_UNIVERSE_COUNT)) { + // additional universe(s) of this fixture + uint16_t numberOfLEDsInPreviousUniverses = ((512 - DMXAddress + 1) / 3); // first universe + if (previousUniverses > 1) numberOfLEDsInPreviousUniverses += (512 / 3) * (previousUniverses - 1); // extended universe(s) before current + possibleLEDsInCurrentUniverse = dmxChannels / 3; + for (uint16_t i = numberOfLEDsInPreviousUniverses; i < ledCount; i++) { + uint8_t j = i - numberOfLEDsInPreviousUniverses; + if (j >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) + setRealtimePixel(i, p->property_values[j*3+1], p->property_values[j*3+2], p->property_values[j*3+3], 0); + } + } + break; + + case DMX_MODE_MULTIPLE_DRGB: + arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); + if (previousUniverses == 0) { + // first universe of this fixture + if (DMXOldDimmer != p->property_values[DMXAddress+0]) { + DMXOldDimmer = p->property_values[DMXAddress+0]; + bri = p->property_values[DMXAddress+0]; + strip.setBrightness(bri); + } + possibleLEDsInCurrentUniverse = (dmxChannels - DMXAddress) / 3; + for (uint16_t i = 0; i < ledCount; i++) { + if (i >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) + setRealtimePixel(i, p->property_values[DMXAddress+i*3+1], p->property_values[DMXAddress+i*3+2], p->property_values[DMXAddress+i*3+3], 0); + } + } else if (previousUniverses > 0 && uni < (e131Universe + E131_MAX_UNIVERSE_COUNT)) { + // additional universe(s) of this fixture + uint16_t numberOfLEDsInPreviousUniverses = ((512 - DMXAddress + 1) / 3); // first universe + if (previousUniverses > 1) numberOfLEDsInPreviousUniverses += (512 / 3) * (previousUniverses - 1); // extended universe(s) before current + possibleLEDsInCurrentUniverse = dmxChannels / 3; + for (uint16_t i = numberOfLEDsInPreviousUniverses; i < ledCount; i++) { + uint8_t j = i - numberOfLEDsInPreviousUniverses; + if (j >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) + setRealtimePixel(i, p->property_values[j*3+1], p->property_values[j*3+2], p->property_values[j*3+3], 0); + } + } + break; + + default: + DEBUG_PRINTLN("unknown E1.31 DMX mode"); + return; // nothing to do + break; + } + + e131NewData = true; +} diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h new file mode 100644 index 00000000..7261efcb --- /dev/null +++ b/wled00/fcn_declare.h @@ -0,0 +1,184 @@ +#ifndef WLED_FCN_DECLARE_H +#define WLED_FCN_DECLARE_H +#include +#include "src/dependencies/espalexa/EspalexaDevice.h" +#include "src/dependencies/e131/ESPAsyncE131.h" + +/* + * All globally accessible functions are declared here + */ + +//alexa.cpp +void onAlexaChange(EspalexaDevice* dev); +void alexaInit(); +void handleAlexa(); +void onAlexaChange(EspalexaDevice* dev); + +//blynk.cpp +void initBlynk(const char* auth); +void handleBlynk(); +void updateBlynk(); + +//button.cpp +void shortPressAction(); +void handleButton(); +void handleIO(); + +//colors.cpp +void colorFromUint32(uint32_t in, bool secondary = false); +void colorFromUint24(uint32_t in, bool secondary = false); +void relativeChangeWhite(int8_t amount, byte lowerBoundary = 0); +void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb +void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb + +void colorXYtoRGB(float x, float y, byte* rgb); // only defined if huesync disabled TODO +void colorRGBtoXY(byte* rgb, float* xy); // only defined if huesync disabled TODO + +void colorFromDecOrHexString(byte* rgb, char* in); +void colorRGBtoRGBW(byte* rgb); //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_MODE_LEGACY) + +//dmx.cpp +void initDMX(); +void handleDMX(); + +//e131.cpp +void handleE131Packet(e131_packet_t* p, IPAddress clientIP); + +//file.cpp +bool handleFileRead(AsyncWebServerRequest*, String path); + +//hue.cpp +void handleHue(); +void reconnectHue(); +void onHueError(void* arg, AsyncClient* client, int8_t error); +void onHueConnect(void* arg, AsyncClient* client); +void sendHuePoll(); +void onHueData(void* arg, AsyncClient* client, void *data, size_t len); + +//ir.cpp +bool decodeIRCustom(uint32_t code); +void relativeChange(byte* property, int8_t amount, byte lowerBoundary = 0, byte higherBoundary = 0xFF); +void changeEffectSpeed(int8_t amount); +void changeEffectIntensity(int8_t amount); +void decodeIR(uint32_t code); +void decodeIR24(uint32_t code); +void decodeIR24OLD(uint32_t code); +void decodeIR24CT(uint32_t code); +void decodeIR40(uint32_t code); +void decodeIR44(uint32_t code); +void decodeIR21(uint32_t code); +void decodeIR6(uint32_t code); + +void initIR(); +void handleIR(); + +//json.cpp +#include "ESPAsyncWebServer.h" +#include "src/dependencies/json/ArduinoJson-v6.h" +#include "src/dependencies/json/AsyncJson-v6.h" +#include "FX.h" +// TODO: AsynicWebServerRequest conflict? + +void deserializeSegment(JsonObject elem, byte it); +bool deserializeState(JsonObject root); +void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id); +void serializeState(JsonObject root); +void serializeInfo(JsonObject root); +void serveJson(AsyncWebServerRequest* request); +void serveLiveLeds(AsyncWebServerRequest* request); + +//led.cpp +void setValuesFromMainSeg(); +void resetTimebase(); +void toggleOnOff(); +void setAllLeds(); +void setLedsStandard(bool justColors = false); +bool colorChanged(); +void colorUpdated(int callMode); +void updateInterfaces(uint8_t callMode); +void handleTransitions(); +void handleNightlight(); + +//mqtt.cpp +bool initMqtt(); +void publishMqtt(); + +//notify.cpp +void notify(byte callMode, bool followUp=false); +void arlsLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC); +void handleNotifications(); +void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w); + +//ntp.cpp +void handleNetworkTime(); +void sendNTPPacket(); +bool checkNTPResponse(); +void updateLocalTime(); +void getTimeString(char* out); +bool checkCountdown(); +void setCountdown(); +byte weekdayMondayFirst(); +void checkTimers(); + +//overlay.cpp +void initCronixie(); +void handleOverlays(); +void handleOverlayDraw(); +void _overlayAnalogCountdown(); +void _overlayAnalogClock(); + +byte getSameCodeLength(char code, int index, char const cronixieDisplay[]); +void setCronixie(); +void _overlayCronixie(); +void _drawOverlayCronixie(); + +//set.cpp +void _setRandomColor(bool _sec,bool fromButton=false); +bool isAsterisksOnly(const char* str, byte maxLen); +void handleSettingsSet(AsyncWebServerRequest *request, byte subPage); +bool handleSet(AsyncWebServerRequest *request, const String& req); +int getNumVal(const String* req, uint16_t pos); +bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte maxv=255); + +//usermod.cpp +void userSetup(); +void userConnected(); +void userLoop(); + +//wled_eeprom.cpp +void commit(); +void clearEEPROM(); +void writeStringToEEPROM(uint16_t pos, char* str, uint16_t len); +void readStringFromEEPROM(uint16_t pos, char* str, uint16_t len); +void saveSettingsToEEPROM(); +void loadSettingsFromEEPROM(bool first); +void savedToPresets(); +bool applyPreset(byte index, bool loadBri = true); +void savePreset(byte index, bool persist = true); +void loadMacro(byte index, char* m); +void applyMacro(byte index); +void saveMacro(byte index, String mc, bool persist = true); //only commit on single save, not in settings + +//wled_serial.cpp +void handleSerial(); + +//wled_server.cpp +bool isIp(String str); +bool captivePortal(AsyncWebServerRequest *request); +void initServer(); +void serveIndexOrWelcome(AsyncWebServerRequest *request); +void serveIndex(AsyncWebServerRequest* request); +String msgProcessor(const String& var); +void serveMessage(AsyncWebServerRequest* request, uint16_t code, String headl, String subl="", byte optionT=255); +String settingsProcessor(const String& var); +String dmxProcessor(const String& var); +void serveSettings(AsyncWebServerRequest* request); + +//xml.cpp +char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr); +char* URL_response(AsyncWebServerRequest *request); +void sappend(char stype, const char* key, int val); +void sappends(char stype, const char* key, char* val); +void getSettingsJS(byte subPage, char* dest); + +#endif diff --git a/wled00/file.cpp b/wled00/file.cpp new file mode 100644 index 00000000..eb0dcb93 --- /dev/null +++ b/wled00/file.cpp @@ -0,0 +1,55 @@ +#include "wled.h" + +/* + * Utility for SPIFFS filesystem + */ + +//filesystem +#ifndef WLED_DISABLE_FILESYSTEM +#include +#ifdef ARDUINO_ARCH_ESP32 +#include "SPIFFS.h" +#endif +#include "SPIFFSEditor.h" +#endif + + +#if !defined WLED_DISABLE_FILESYSTEM && defined WLED_ENABLE_FS_SERVING +//Un-comment any file types you need +String getContentType(AsyncWebServerRequest* request, String filename){ + if(request->hasArg("download")) return "application/octet-stream"; + else if(filename.endsWith(".htm")) return "text/html"; + else if(filename.endsWith(".html")) return "text/html"; +// else if(filename.endsWith(".css")) return "text/css"; +// else if(filename.endsWith(".js")) return "application/javascript"; + else if(filename.endsWith(".json")) return "application/json"; + else if(filename.endsWith(".png")) return "image/png"; +// else if(filename.endsWith(".gif")) return "image/gif"; + else if(filename.endsWith(".jpg")) return "image/jpeg"; + else if(filename.endsWith(".ico")) return "image/x-icon"; +// else if(filename.endsWith(".xml")) return "text/xml"; +// else if(filename.endsWith(".pdf")) return "application/x-pdf"; +// else if(filename.endsWith(".zip")) return "application/x-zip"; +// else if(filename.endsWith(".gz")) return "application/x-gzip"; + return "text/plain"; +} + +bool handleFileRead(AsyncWebServerRequest* request, String path){ + DEBUG_PRINTLN("FileRead: " + path); + if(path.endsWith("/")) path += "index.htm"; + String contentType = getContentType(request, path); + String pathWithGz = path + ".gz"; + if(SPIFFS.exists(pathWithGz)){ + request->send(SPIFFS, pathWithGz, contentType); + return true; + } + if(SPIFFS.exists(path)) { + request->send(SPIFFS, path, contentType); + return true; + } + return false; +} + +#else +bool handleFileRead(AsyncWebServerRequest*, String path){return false;} +#endif diff --git a/wled00/html_other.h b/wled00/html_other.h index e27c64dd..ce36cff4 100644 --- a/wled00/html_other.h +++ b/wled00/html_other.h @@ -1,5 +1,5 @@ /* - * Various pages + * Various web pages */ //USER HTML HERE (/u subpage) diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 7def2314..fa019533 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -1,6 +1,6 @@ /* - Settings html -*/ + * Settings html + */ //common CSS of settings pages const char PAGE_settingsCss[] PROGMEM = R"=====()====="; @@ -214,17 +214,12 @@ function S(){GCH(15);GetV();mMap();}function H(){window.open("https://github.com Number of fixtures is taken from LED config page
-channels per fixture (15 max):
-start channel:
-spacing between start channels: [ info ]
+Channels per fixture (15 max):
+Start channel:
+Spacing between start channels: [ info ]

- -If wanting to use DMX together with LEDs. Number of fixtures will also depend on the segment and start LED settings below
-Using these settings, you can position your fixtures anywhere you want on the LED strip
-which segment should DMX use:
-start LED from the above channel from which DMX fixtures should start:
- +DMX fixtures start LED:

channel functions


diff --git a/wled00/wled15_hue.ino b/wled00/hue.cpp similarity index 99% rename from wled00/wled15_hue.ino rename to wled00/hue.cpp index 5f6b8f5b..77091f63 100644 --- a/wled00/wled15_hue.ino +++ b/wled00/hue.cpp @@ -1,7 +1,11 @@ +#include "wled.h" + /* * Sync to Philips hue lights */ + #ifndef WLED_DISABLE_HUESYNC + void handleHue() { if (hueReceived) diff --git a/wled00/wled20_ir.ino b/wled00/ir.cpp similarity index 99% rename from wled00/wled20_ir.ino rename to wled00/ir.cpp index 64c131c2..046991db 100644 --- a/wled00/wled20_ir.ino +++ b/wled00/ir.cpp @@ -1,3 +1,5 @@ +#include "wled.h" + /* * Infrared sensor support for generic 24/40/44 key RGB remotes */ @@ -35,7 +37,7 @@ bool decodeIRCustom(uint32_t code) //relatively change brightness, minumum A=5 -void relativeChange(byte* property, int8_t amount, byte lowerBoundary = 0, byte higherBoundary = 0xFF) +void relativeChange(byte* property, int8_t amount, byte lowerBoundary, byte higherBoundary) { int16_t new_val = (int16_t) *property + amount; if (new_val > higherBoundary) new_val = higherBoundary; diff --git a/wled00/wled19_json.ino b/wled00/json.cpp similarity index 97% rename from wled00/wled19_json.ino rename to wled00/json.cpp index c0e23b4b..31208bf5 100644 --- a/wled00/wled19_json.ino +++ b/wled00/json.cpp @@ -1,3 +1,5 @@ +#include "wled.h" + /* * JSON API (De)serialization */ @@ -248,6 +250,26 @@ void serializeState(JsonObject root) } } +//by https://github.com/tzapu/WiFiManager/blob/master/WiFiManager.cpp +int getSignalQuality(int rssi) +{ + int quality = 0; + + if (rssi <= -100) + { + quality = 0; + } + else if (rssi >= -50) + { + quality = 100; + } + else + { + quality = 2 * (rssi + 100); + } + return quality; +} + void serializeInfo(JsonObject root) { root["ver"] = versionString; diff --git a/wled00/wled08_led.ino b/wled00/led.cpp similarity index 99% rename from wled00/wled08_led.ino rename to wled00/led.cpp index 63009f98..c3f50cc2 100644 --- a/wled00/wled08_led.ino +++ b/wled00/led.cpp @@ -1,6 +1,9 @@ +#include "wled.h" + /* * LED methods */ + void setValuesFromMainSeg() { WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId()); @@ -50,7 +53,7 @@ void setAllLeds() { } -void setLedsStandard(bool justColors = false) +void setLedsStandard(bool justColors) { for (byte i=0; i<4; i++) { diff --git a/wled00/wled17_mqtt.ino b/wled00/mqtt.cpp similarity index 99% rename from wled00/wled17_mqtt.ino rename to wled00/mqtt.cpp index 741c807f..9c15f5bb 100644 --- a/wled00/wled17_mqtt.ino +++ b/wled00/mqtt.cpp @@ -1,3 +1,5 @@ +#include "wled.h" + /* * MQTT communication protocol for home automation */ diff --git a/wled00/wled10_ntp.ino b/wled00/ntp.cpp similarity index 98% rename from wled00/wled10_ntp.ino rename to wled00/ntp.cpp index 2c5a64c4..d6332fd2 100644 --- a/wled00/wled10_ntp.ino +++ b/wled00/ntp.cpp @@ -1,3 +1,6 @@ +#include "src/dependencies/timezone/Timezone.h" +#include "wled.h" + /* * Acquires time from NTP server */ @@ -57,6 +60,7 @@ Timezone tzNK(NKST, NKST); TimeChangeRule IST = {Last, Sun, Mar, 1, 330}; // India Standard Time = UTC + 5.5 hours Timezone tzIndia(IST, IST); +// Pick your timezone from here. Timezone* timezones[] = {&tzUTC, &tzUK, &tzEUCentral, &tzEUEastern, &tzUSEastern, &tzUSCentral, &tzUSMountain, &tzUSArizona, &tzUSPacific, &tzChina, &tzJapan, &tzAUEastern, &tzNZ, &tzNK, &tzIndia, &tzCASaskatchewan}; void handleNetworkTime() diff --git a/wled00/wled13_cronixie.ino b/wled00/overlay.cpp similarity index 66% rename from wled00/wled13_cronixie.ino rename to wled00/overlay.cpp index dd687ca1..ceb6fc20 100644 --- a/wled00/wled13_cronixie.ino +++ b/wled00/overlay.cpp @@ -1,9 +1,141 @@ +#include "wled.h" + +/* + * Used to draw clock overlays over the strip + */ + +void initCronixie() +{ + if (overlayCurrent == 3 && !cronixieInit) + { + setCronixie(); + strip.getSegment(0).grouping = 10; //10 LEDs per digit + cronixieInit = true; + } else if (cronixieInit && overlayCurrent != 3) + { + strip.getSegment(0).grouping = 1; + cronixieInit = false; + } +} + + +void handleOverlays() +{ + if (millis() - overlayRefreshedTime > overlayRefreshMs) + { + initCronixie(); + updateLocalTime(); + checkTimers(); + checkCountdown(); + if (overlayCurrent == 3) _overlayCronixie();//Diamex cronixie clock kit + overlayRefreshedTime = millis(); + } +} + + +void _overlayAnalogClock() +{ + int overlaySize = overlayMax - overlayMin +1; + if (countdownMode) + { + _overlayAnalogCountdown(); return; + } + double hourP = ((double)(hour(local)%12))/12; + double minuteP = ((double)minute(local))/60; + hourP = hourP + minuteP/12; + double secondP = ((double)second(local))/60; + int hourPixel = floor(analogClock12pixel + overlaySize*hourP); + if (hourPixel > overlayMax) hourPixel = overlayMin -1 + hourPixel - overlayMax; + int minutePixel = floor(analogClock12pixel + overlaySize*minuteP); + if (minutePixel > overlayMax) minutePixel = overlayMin -1 + minutePixel - overlayMax; + int secondPixel = floor(analogClock12pixel + overlaySize*secondP); + if (secondPixel > overlayMax) secondPixel = overlayMin -1 + secondPixel - overlayMax; + if (analogClockSecondsTrail) + { + if (secondPixel < analogClock12pixel) + { + strip.setRange(analogClock12pixel, overlayMax, 0xFF0000); + strip.setRange(overlayMin, secondPixel, 0xFF0000); + } else + { + strip.setRange(analogClock12pixel, secondPixel, 0xFF0000); + } + } + if (analogClock5MinuteMarks) + { + int pix; + for (int i = 0; i <= 12; i++) + { + pix = analogClock12pixel + round((overlaySize / 12.0) *i); + if (pix > overlayMax) pix -= overlaySize; + strip.setPixelColor(pix, 0x00FFAA); + } + } + if (!analogClockSecondsTrail) strip.setPixelColor(secondPixel, 0xFF0000); + strip.setPixelColor(minutePixel, 0x00FF00); + strip.setPixelColor(hourPixel, 0x0000FF); + overlayRefreshMs = 998; +} + + +void _overlayAnalogCountdown() +{ + if (now() < countdownTime) + { + long diff = countdownTime - now(); + double pval = 60; + if (diff > 31557600L) //display in years if more than 365 days + { + pval = 315576000L; //10 years + } else if (diff > 2592000L) //display in months if more than a month + { + pval = 31557600L; //1 year + } else if (diff > 604800) //display in weeks if more than a week + { + pval = 2592000L; //1 month + } else if (diff > 86400) //display in days if more than 24 hours + { + pval = 604800; //1 week + } else if (diff > 3600) //display in hours if more than 60 minutes + { + pval = 86400; //1 day + } else if (diff > 60) //display in minutes if more than 60 seconds + { + pval = 3600; //1 hour + } + int overlaySize = overlayMax - overlayMin +1; + double perc = (pval-(double)diff)/pval; + if (perc > 1.0) perc = 1.0; + byte pixelCnt = perc*overlaySize; + if (analogClock12pixel + pixelCnt > overlayMax) + { + strip.setRange(analogClock12pixel, overlayMax, ((uint32_t)colSec[3] << 24)| ((uint32_t)colSec[0] << 16) | ((uint32_t)colSec[1] << 8) | colSec[2]); + strip.setRange(overlayMin, overlayMin +pixelCnt -(1+ overlayMax -analogClock12pixel), ((uint32_t)colSec[3] << 24)| ((uint32_t)colSec[0] << 16) | ((uint32_t)colSec[1] << 8) | colSec[2]); + } else + { + strip.setRange(analogClock12pixel, analogClock12pixel + pixelCnt, ((uint32_t)colSec[3] << 24)| ((uint32_t)colSec[0] << 16) | ((uint32_t)colSec[1] << 8) | colSec[2]); + } + } + overlayRefreshMs = 998; +} + + +void handleOverlayDraw() { + if (!overlayCurrent) return; + switch (overlayCurrent) + { + case 1: _overlayAnalogClock(); break; + case 3: _drawOverlayCronixie(); break; + } +} + + /* * Support for the Cronixie clock */ + #ifndef WLED_DISABLE_CRONIXIE byte _digitOut[6] = {10,10,10,10,10,10}; -#endif byte getSameCodeLength(char code, int index, char const cronixieDisplay[]) { @@ -23,7 +155,6 @@ byte getSameCodeLength(char code, int index, char const cronixieDisplay[]) void setCronixie() { - #ifndef WLED_DISABLE_CRONIXIE /* * digit purpose index * 0-9 | 0-9 (incl. random) @@ -144,12 +275,10 @@ void setCronixie() DEBUG_PRINTLN((int)dP[5]); _overlayCronixie(); //refresh - #endif } void _overlayCronixie() { - #ifndef WLED_DISABLE_CRONIXIE byte h = hour(local); byte h0 = h; byte m = minute(local); @@ -211,12 +340,10 @@ void _overlayCronixie() } } } - #endif } void _drawOverlayCronixie() { - #ifndef WLED_DISABLE_CRONIXIE byte offsets[] = {5, 0, 6, 1, 7, 2, 8, 3, 9, 4}; for (uint16_t i = 0; i < 6; i++) @@ -239,5 +366,11 @@ void _drawOverlayCronixie() } } } - #endif } + +#else // WLED_DISABLE_CRONIXIE +byte getSameCodeLength(char code, int index, char const cronixieDisplay[]) {} +void setCronixie() {} +void _overlayCronixie() {} +void _drawOverlayCronixie() {} +#endif diff --git a/wled00/wled03_set.ino b/wled00/set.cpp similarity index 98% rename from wled00/wled03_set.ino rename to wled00/set.cpp index 088f31ad..fb7b8596 100644 --- a/wled00/wled03_set.ino +++ b/wled00/set.cpp @@ -1,8 +1,10 @@ +#include "wled.h" + /* * Receives client input */ -void _setRandomColor(bool _sec,bool fromButton=false) +void _setRandomColor(bool _sec,bool fromButton) { lastRandomIndex = strip.get_random_wheel_index(lastRandomIndex); if (_sec){ @@ -305,19 +307,15 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) if (t>0 && t<513) { DMXGap = t; } + t = request->arg("SL").toInt(); + if (t>=0 && t < MAX_LEDS) { + DMXStartLED = t; + } for (int i=0; i<15; i++) { String argname = "CH" + String((i+1)); t = request->arg(argname).toInt(); DMXFixtureMap[i] = t; } - t = request->arg("SEG").toInt(); - if (t>=0 && t<10) { - DMXSegment = t; - } - t = request->arg("SL").toInt(); - if (t>=0 && t<1500) { - DMXStartLED = t; - } } #endif @@ -338,7 +336,7 @@ int getNumVal(const String* req, uint16_t pos) //helper to get int value at a position in string -bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte maxv=255) +bool updateVal(const String* req, const char* key, byte* val, byte minv, byte maxv) { int pos = req->indexOf(key); if (pos < 1) return false; diff --git a/wled00/src/dependencies/blynk/BlynkSimpleEsp.cpp b/wled00/src/dependencies/blynk/BlynkSimpleEsp.cpp new file mode 100644 index 00000000..60ba109e --- /dev/null +++ b/wled00/src/dependencies/blynk/BlynkSimpleEsp.cpp @@ -0,0 +1,5 @@ +#include "BlynkSimpleEsp.h" + +WiFiClient _blynkWifiClient; +BlynkArduinoClient _blynkTransport(_blynkWifiClient); +BlynkWifi Blynk(_blynkTransport); \ No newline at end of file diff --git a/wled00/src/dependencies/blynk/BlynkSimpleEsp.h b/wled00/src/dependencies/blynk/BlynkSimpleEsp.h index f6b2a0f1..6697686f 100644 --- a/wled00/src/dependencies/blynk/BlynkSimpleEsp.h +++ b/wled00/src/dependencies/blynk/BlynkSimpleEsp.h @@ -89,8 +89,6 @@ public: }; -static WiFiClient _blynkWifiClient; -static BlynkArduinoClient _blynkTransport(_blynkWifiClient); -BlynkWifi Blynk(_blynkTransport); +extern BlynkWifi Blynk; #endif diff --git a/wled00/src/dependencies/dmx/ESPDMX.cpp b/wled00/src/dependencies/dmx/ESPDMX.cpp new file mode 100644 index 00000000..f3ece1c8 --- /dev/null +++ b/wled00/src/dependencies/dmx/ESPDMX.cpp @@ -0,0 +1,106 @@ +// - - - - - +// ESPDMX - A Arduino library for sending and receiving DMX using the builtin serial hardware port. +// ESPDMX.cpp: Library implementation file +// +// Copyright (C) 2015 Rick +// This work is licensed under a GNU style license. +// +// Last change: Marcel Seerig +// +// Documentation and samples are available at https://github.com/Rickgg/ESP-Dmx +// - - - - - + +/* ----- LIBRARIES ----- */ +#include + +#include "ESPDMX.h" + + + +#define dmxMaxChannel 512 +#define defaultMax 32 + +#define DMXSPEED 250000 +#define DMXFORMAT SERIAL_8N2 +#define BREAKSPEED 83333 +#define BREAKFORMAT SERIAL_8N1 + +bool dmxStarted = false; +int sendPin = 2; //dafault on ESP8266 + +//DMX value array and size. Entry 0 will hold startbyte +uint8_t dmxData[dmxMaxChannel] = {}; +int chanSize; + + +void DMXESPSerial::init() { + chanSize = defaultMax; + + Serial1.begin(DMXSPEED); + pinMode(sendPin, OUTPUT); + dmxStarted = true; +} + +// Set up the DMX-Protocol +void DMXESPSerial::init(int chanQuant) { + + if (chanQuant > dmxMaxChannel || chanQuant <= 0) { + chanQuant = defaultMax; + } + + chanSize = chanQuant; + + Serial1.begin(DMXSPEED); + pinMode(sendPin, OUTPUT); + dmxStarted = true; +} + +// Function to read DMX data +uint8_t DMXESPSerial::read(int Channel) { + if (dmxStarted == false) init(); + + if (Channel < 1) Channel = 1; + if (Channel > dmxMaxChannel) Channel = dmxMaxChannel; + return(dmxData[Channel]); +} + +// Function to send DMX data +void DMXESPSerial::write(int Channel, uint8_t value) { + if (dmxStarted == false) init(); + + if (Channel < 1) Channel = 1; + if (Channel > chanSize) Channel = chanSize; + if (value < 0) value = 0; + if (value > 255) value = 255; + + dmxData[Channel] = value; +} + +void DMXESPSerial::end() { + delete dmxData; + chanSize = 0; + Serial1.end(); + dmxStarted == false; +} + +void DMXESPSerial::update() { + if (dmxStarted == false) init(); + + //Send break + digitalWrite(sendPin, HIGH); + Serial1.begin(BREAKSPEED, BREAKFORMAT); + Serial1.write(0); + Serial1.flush(); + delay(1); + Serial1.end(); + + //send data + Serial1.begin(DMXSPEED, DMXFORMAT); + digitalWrite(sendPin, LOW); + Serial1.write(dmxData, chanSize); + Serial1.flush(); + delay(1); + Serial1.end(); +} + +// Function to update the DMX bus diff --git a/wled00/src/dependencies/dmx/ESPDMX.h b/wled00/src/dependencies/dmx/ESPDMX.h new file mode 100644 index 00000000..4585bdd2 --- /dev/null +++ b/wled00/src/dependencies/dmx/ESPDMX.h @@ -0,0 +1,31 @@ +// - - - - - +// ESPDMX - A Arduino library for sending and receiving DMX using the builtin serial hardware port. +// ESPDMX.cpp: Library implementation file +// +// Copyright (C) 2015 Rick +// This work is licensed under a GNU style license. +// +// Last change: Marcel Seerig +// +// Documentation and samples are available at https://github.com/Rickgg/ESP-Dmx +// - - - - - + +#include + + +#ifndef ESPDMX_h +#define ESPDMX_h + +// ---- Methods ---- + +class DMXESPSerial { +public: + void init(); + void init(int MaxChan); + uint8_t read(int Channel); + void write(int channel, uint8_t value); + void update(); + void end(); +}; + +#endif diff --git a/wled00/src/dependencies/espalexa/EspalexaDevice.h b/wled00/src/dependencies/espalexa/EspalexaDevice.h index 0653a418..4ebdd2b8 100644 --- a/wled00/src/dependencies/espalexa/EspalexaDevice.h +++ b/wled00/src/dependencies/espalexa/EspalexaDevice.h @@ -1,6 +1,6 @@ #ifndef EspalexaDevice_h #define EspalexaDevice_h - +#include #include "Arduino.h" #include diff --git a/wled00/wled07_notify.ino b/wled00/udp.cpp similarity index 55% rename from wled00/wled07_notify.ino rename to wled00/udp.cpp index 8fcbdbe8..52879803 100644 --- a/wled00/wled07_notify.ino +++ b/wled00/udp.cpp @@ -1,12 +1,13 @@ +#include "wled.h" + /* - * UDP notifier + * UDP sync notifier */ #define WLEDPACKETSIZE 29 #define UDP_IN_MAXSIZE 1472 - -void notify(byte callMode, bool followUp=false) +void notify(byte callMode, bool followUp) { if (!udpConnected) return; switch (callMode) @@ -71,7 +72,7 @@ void notify(byte callMode, bool followUp=false) } -void arlsLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC) +void arlsLock(uint32_t timeoutMs, byte md) { if (!realtimeMode){ for (uint16_t i = 0; i < ledCount; i++) @@ -86,146 +87,6 @@ void arlsLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC) } -void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ - //E1.31 protocol support - - uint16_t uni = htons(p->universe); - uint8_t previousUniverses = uni - e131Universe; - uint16_t possibleLEDsInCurrentUniverse; - uint16_t dmxChannels = htons(p->property_value_count) -1; - - // only listen for universes we're handling & allocated memory - if (uni >= (e131Universe + E131_MAX_UNIVERSE_COUNT)) return; - - if (e131SkipOutOfSequence) - if (p->sequence_number < e131LastSequenceNumber[uni-e131Universe] && p->sequence_number > 20 && e131LastSequenceNumber[uni-e131Universe] < 250){ - DEBUG_PRINT("skipping E1.31 frame (last seq="); - DEBUG_PRINT(e131LastSequenceNumber[uni-e131Universe]); - DEBUG_PRINT(", current seq="); - DEBUG_PRINT(p->sequence_number); - DEBUG_PRINT(", universe="); - DEBUG_PRINT(uni); - DEBUG_PRINTLN(")"); - return; - } - e131LastSequenceNumber[uni-e131Universe] = p->sequence_number; - - // update status info - realtimeIP = clientIP; - - switch (DMXMode) { - case DMX_MODE_DISABLED: - return; // nothing to do - break; - - case DMX_MODE_SINGLE_RGB: - if (uni != e131Universe) return; - if (dmxChannels-DMXAddress+1 < 3) return; - arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); - for (uint16_t i = 0; i < ledCount; i++) - setRealtimePixel(i, p->property_values[DMXAddress+0], p->property_values[DMXAddress+1], p->property_values[DMXAddress+2], 0); - break; - - case DMX_MODE_SINGLE_DRGB: - if (uni != e131Universe) return; - if (dmxChannels-DMXAddress+1 < 4) return; - arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); - if (DMXOldDimmer != p->property_values[DMXAddress+0]) { - DMXOldDimmer = p->property_values[DMXAddress+0]; - bri = p->property_values[DMXAddress+0]; - strip.setBrightness(bri); - } - for (uint16_t i = 0; i < ledCount; i++) - setRealtimePixel(i, p->property_values[DMXAddress+1], p->property_values[DMXAddress+2], p->property_values[DMXAddress+3], 0); - break; - - case DMX_MODE_EFFECT: - if (uni != e131Universe) return; - if (dmxChannels-DMXAddress+1 < 11) return; - if (DMXOldDimmer != p->property_values[DMXAddress+0]) { - DMXOldDimmer = p->property_values[DMXAddress+0]; - bri = p->property_values[DMXAddress+0]; - } - if (p->property_values[DMXAddress+1] < MODE_COUNT) - effectCurrent = p->property_values[DMXAddress+ 1]; - effectSpeed = p->property_values[DMXAddress+ 2]; // flickers - effectIntensity = p->property_values[DMXAddress+ 3]; - effectPalette = p->property_values[DMXAddress+ 4]; - col[0] = p->property_values[DMXAddress+ 5]; - col[1] = p->property_values[DMXAddress+ 6]; - col[2] = p->property_values[DMXAddress+ 7]; - colSec[0] = p->property_values[DMXAddress+ 8]; - colSec[1] = p->property_values[DMXAddress+ 9]; - colSec[2] = p->property_values[DMXAddress+10]; - if (dmxChannels-DMXAddress+1 > 11) - { - col[3] = p->property_values[DMXAddress+11]; //white - colSec[3] = p->property_values[DMXAddress+12]; - } - transitionDelayTemp = 0; // act fast - colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); // don't send UDP - return; // don't activate realtime live mode - break; - - case DMX_MODE_MULTIPLE_RGB: - arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); - if (previousUniverses == 0) { - // first universe of this fixture - possibleLEDsInCurrentUniverse = (dmxChannels - DMXAddress + 1) / 3; - for (uint16_t i = 0; i < ledCount; i++) { - if (i >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) - setRealtimePixel(i, p->property_values[DMXAddress+i*3+0], p->property_values[DMXAddress+i*3+1], p->property_values[DMXAddress+i*3+2], 0); - } - } else if (previousUniverses > 0 && uni < (e131Universe + E131_MAX_UNIVERSE_COUNT)) { - // additional universe(s) of this fixture - uint16_t numberOfLEDsInPreviousUniverses = ((512 - DMXAddress + 1) / 3); // first universe - if (previousUniverses > 1) numberOfLEDsInPreviousUniverses += (512 / 3) * (previousUniverses - 1); // extended universe(s) before current - possibleLEDsInCurrentUniverse = dmxChannels / 3; - for (uint16_t i = numberOfLEDsInPreviousUniverses; i < ledCount; i++) { - uint8_t j = i - numberOfLEDsInPreviousUniverses; - if (j >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) - setRealtimePixel(i, p->property_values[j*3+1], p->property_values[j*3+2], p->property_values[j*3+3], 0); - } - } - break; - - case DMX_MODE_MULTIPLE_DRGB: - arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); - if (previousUniverses == 0) { - // first universe of this fixture - if (DMXOldDimmer != p->property_values[DMXAddress+0]) { - DMXOldDimmer = p->property_values[DMXAddress+0]; - bri = p->property_values[DMXAddress+0]; - strip.setBrightness(bri); - } - possibleLEDsInCurrentUniverse = (dmxChannels - DMXAddress) / 3; - for (uint16_t i = 0; i < ledCount; i++) { - if (i >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) - setRealtimePixel(i, p->property_values[DMXAddress+i*3+1], p->property_values[DMXAddress+i*3+2], p->property_values[DMXAddress+i*3+3], 0); - } - } else if (previousUniverses > 0 && uni < (e131Universe + E131_MAX_UNIVERSE_COUNT)) { - // additional universe(s) of this fixture - uint16_t numberOfLEDsInPreviousUniverses = ((512 - DMXAddress + 1) / 3); // first universe - if (previousUniverses > 1) numberOfLEDsInPreviousUniverses += (512 / 3) * (previousUniverses - 1); // extended universe(s) before current - possibleLEDsInCurrentUniverse = dmxChannels / 3; - for (uint16_t i = numberOfLEDsInPreviousUniverses; i < ledCount; i++) { - uint8_t j = i - numberOfLEDsInPreviousUniverses; - if (j >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) - setRealtimePixel(i, p->property_values[j*3+1], p->property_values[j*3+2], p->property_values[j*3+3], 0); - } - } - break; - - default: - DEBUG_PRINTLN("unknown E1.31 DMX mode"); - return; // nothing to do - break; - } - - e131NewData = true; -} - - void handleNotifications() { //send second notification if enabled diff --git a/wled00/wled06_usermod.ino b/wled00/usermod.cpp similarity index 92% rename from wled00/wled06_usermod.ino rename to wled00/usermod.cpp index 010d0f38..2c7c881f 100644 --- a/wled00/wled06_usermod.ino +++ b/wled00/usermod.cpp @@ -1,7 +1,8 @@ +#include "wled.h" /* * This file allows you to add own functionality to WLED more easily * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality - * EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in wled01_eeprom.h) + * EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in const.h) * bytes 2400+ are currently ununsed, but might be used for future wled features */ diff --git a/wled00/wled.cpp b/wled00/wled.cpp new file mode 100644 index 00000000..7f8ef597 --- /dev/null +++ b/wled00/wled.cpp @@ -0,0 +1,458 @@ +#define WLED_DEFINE_GLOBAL_VARS //only in one source file, wled.cpp! +#include "wled.h" +#include + +/* + * Main WLED class implementation. Mostly initialization and connection logic + */ + +WLED::WLED() +{ +} + +// turns all LEDs off and restarts ESP +void WLED::reset() +{ + briT = 0; + long dly = millis(); + while (millis() - dly < 250) { + yield(); // enough time to send response to client + } + setAllLeds(); + DEBUG_PRINTLN("MODULE RESET"); + ESP.restart(); +} + +bool oappendi(int i) +{ + char s[11]; + sprintf(s, "%ld", i); + return oappend(s); +} + +bool oappend(const char* txt) +{ + uint16_t len = strlen(txt); + if (olen + len >= OMAX) + return false; // buffer full + strcpy(obuf + olen, txt); + olen += len; + return true; +} + +void WLED::loop() +{ + handleIR(); // 2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too + handleConnection(); + handleSerial(); + handleNotifications(); + handleTransitions(); +#ifdef WLED_ENABLE_DMX + handleDMX(); +#endif + userLoop(); + + yield(); + handleIO(); + handleIR(); + handleNetworkTime(); + handleAlexa(); + + handleOverlays(); + yield(); +#ifdef WLED_USE_ANALOG_LEDS + strip.setRgbwPwm(); +#endif + + if (doReboot) + reset(); + + if (!realtimeMode) // block stuff if WARLS/Adalight is enabled + { + if (apActive) + dnsServer.processNextRequest(); +#ifndef WLED_DISABLE_OTA + if (WLED_CONNECTED && aOtaEnabled) + ArduinoOTA.handle(); +#endif + handleNightlight(); + yield(); + + handleHue(); + handleBlynk(); + + yield(); + if (!offMode) + strip.service(); + } + yield(); +#ifdef ESP8266 + MDNS.update(); +#endif + if (millis() - lastMqttReconnectAttempt > 30000) + initMqtt(); + +// DEBUG serial logging +#ifdef WLED_DEBUG + if (millis() - debugTime > 9999) { + DEBUG_PRINTLN("---DEBUG INFO---"); + DEBUG_PRINT("Runtime: "); + DEBUG_PRINTLN(millis()); + DEBUG_PRINT("Unix time: "); + DEBUG_PRINTLN(now()); + DEBUG_PRINT("Free heap: "); + DEBUG_PRINTLN(ESP.getFreeHeap()); + DEBUG_PRINT("Wifi state: "); + DEBUG_PRINTLN(WiFi.status()); + if (WiFi.status() != lastWifiState) { + wifiStateChangedTime = millis(); + } + lastWifiState = WiFi.status(); + DEBUG_PRINT("State time: "); + DEBUG_PRINTLN(wifiStateChangedTime); + DEBUG_PRINT("NTP last sync: "); + DEBUG_PRINTLN(ntpLastSyncTime); + DEBUG_PRINT("Client IP: "); + DEBUG_PRINTLN(WiFi.localIP()); + DEBUG_PRINT("Loops/sec: "); + DEBUG_PRINTLN(loops / 10); + loops = 0; + debugTime = millis(); + } + loops++; +#endif // WLED_DEBU +} + +void WLED::setup() +{ + EEPROM.begin(EEPSIZE); + ledCount = EEPROM.read(229) + ((EEPROM.read(398) << 8) & 0xFF00); + if (ledCount > MAX_LEDS || ledCount == 0) + ledCount = 30; + +#ifdef ESP8266 + #if LEDPIN == 3 + if (ledCount > MAX_LEDS_DMA) + ledCount = MAX_LEDS_DMA; // DMA method uses too much ram + #endif +#endif + Serial.begin(115200); + Serial.setTimeout(50); + DEBUG_PRINTLN(); + DEBUG_PRINT("---WLED "); + DEBUG_PRINT(versionString); + DEBUG_PRINT(" "); + DEBUG_PRINT(VERSION); + DEBUG_PRINTLN(" INIT---"); +#ifdef ARDUINO_ARCH_ESP32 + DEBUG_PRINT("esp32 "); + DEBUG_PRINTLN(ESP.getSdkVersion()); +#else + DEBUG_PRINT("esp8266 "); + DEBUG_PRINTLN(ESP.getCoreVersion()); +#endif + int heapPreAlloc = ESP.getFreeHeap(); + DEBUG_PRINT("heap "); + DEBUG_PRINTLN(ESP.getFreeHeap()); + + strip.init(EEPROM.read(372), ledCount, EEPROM.read(2204)); // init LEDs quickly + strip.setBrightness(0); + + DEBUG_PRINT("LEDs inited. heap usage ~"); + DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap()); + +#ifndef WLED_DISABLE_FILESYSTEM + #ifdef ARDUINO_ARCH_ESP32 + SPIFFS.begin(true); + #endif + SPIFFS.begin(); +#endif + + DEBUG_PRINTLN("Load EEPROM"); + loadSettingsFromEEPROM(true); + beginStrip(); + userSetup(); + if (strcmp(clientSSID, DEFAULT_CLIENT_SSID) == 0) + showWelcomePage = true; + WiFi.persistent(false); + + if (macroBoot > 0) + applyMacro(macroBoot); + Serial.println("Ada"); + + // generate module IDs + escapedMac = WiFi.macAddress(); + escapedMac.replace(":", ""); + escapedMac.toLowerCase(); + if (strcmp(cmDNS, "x") == 0) // fill in unique mdns default + { + strcpy(cmDNS, "wled-"); + sprintf(cmDNS + 5, "%*s", 6, escapedMac.c_str() + 6); + } + if (mqttDeviceTopic[0] == 0) { + strcpy(mqttDeviceTopic, "wled/"); + sprintf(mqttDeviceTopic + 5, "%*s", 6, escapedMac.c_str() + 6); + } + if (mqttClientID[0] == 0) { + strcpy(mqttClientID, "WLED-"); + sprintf(mqttClientID + 5, "%*s", 6, escapedMac.c_str() + 6); + } + + strip.service(); + +#ifndef WLED_DISABLE_OTA + if (aOtaEnabled) { + ArduinoOTA.onStart([]() { +#ifdef ESP8266 + wifi_set_sleep_type(NONE_SLEEP_T); +#endif + DEBUG_PRINTLN("Start ArduinoOTA"); + }); + if (strlen(cmDNS) > 0) + ArduinoOTA.setHostname(cmDNS); + } +#endif +#ifdef WLED_ENABLE_DMX + initDMX(); +#endif + // HTTP server page init + initServer(); +} + +void WLED::beginStrip() +{ + // Initialize NeoPixel Strip and button + strip.setShowCallback(handleOverlayDraw); + +#ifdef BTNPIN + pinMode(BTNPIN, INPUT_PULLUP); +#endif + + if (bootPreset > 0) + applyPreset(bootPreset, turnOnAtBoot); + colorUpdated(NOTIFIER_CALL_MODE_INIT); + +// init relay pin +#if RLYPIN >= 0 + pinMode(RLYPIN, OUTPUT); +#if RLYMDE + digitalWrite(RLYPIN, bri); +#else + digitalWrite(RLYPIN, !bri); +#endif +#endif + + // disable button if it is "pressed" unintentionally +#ifdef BTNPIN + if (digitalRead(BTNPIN) == LOW) + buttonEnabled = false; +#else + buttonEnabled = false; +#endif +} + +void WLED::initAP(bool resetAP) +{ + if (apBehavior == AP_BEHAVIOR_BUTTON_ONLY && !resetAP) + return; + + if (!apSSID[0] || resetAP) + strcpy(apSSID, "WLED-AP"); + if (resetAP) + strcpy(apPass, DEFAULT_AP_PASS); + DEBUG_PRINT("Opening access point "); + DEBUG_PRINTLN(apSSID); + WiFi.softAPConfig(IPAddress(4, 3, 2, 1), IPAddress(4, 3, 2, 1), IPAddress(255, 255, 255, 0)); + WiFi.softAP(apSSID, apPass, apChannel, apHide); + + if (!apActive) // start captive portal if AP active + { + DEBUG_PRINTLN("Init AP interfaces"); + server.begin(); + if (udpPort > 0 && udpPort != ntpLocalPort) { + udpConnected = notifierUdp.begin(udpPort); + } + if (udpRgbPort > 0 && udpRgbPort != ntpLocalPort && udpRgbPort != udpPort) { + udpRgbConnected = rgbUdp.begin(udpRgbPort); + } + + dnsServer.setErrorReplyCode(DNSReplyCode::NoError); + dnsServer.start(53, "*", WiFi.softAPIP()); + } + apActive = true; +} + +void WLED::initConnection() +{ + WiFi.disconnect(); // close old connections +#ifdef ESP8266 + WiFi.setPhyMode(WIFI_PHY_MODE_11N); +#endif + + if (staticIP[0] != 0 && staticGateway[0] != 0) { + WiFi.config(staticIP, staticGateway, staticSubnet, IPAddress(8, 8, 8, 8)); + } else { + WiFi.config(0U, 0U, 0U); + } + + lastReconnectAttempt = millis(); + + if (!WLED_WIFI_CONFIGURED) { + DEBUG_PRINT("No connection configured. "); + if (!apActive) + initAP(); // instantly go to ap mode + return; + } else if (!apActive) { + if (apBehavior == AP_BEHAVIOR_ALWAYS) { + initAP(); + } else { + DEBUG_PRINTLN("Access point disabled."); + WiFi.softAPdisconnect(true); + } + } + showWelcomePage = false; + + DEBUG_PRINT("Connecting to "); + DEBUG_PRINT(clientSSID); + DEBUG_PRINTLN("..."); + +#ifdef ESP8266 + WiFi.hostname(serverDescription); +#endif + + WiFi.begin(clientSSID, clientPass); + +#ifdef ARDUINO_ARCH_ESP32 + WiFi.setSleep(!noWifiSleep); + WiFi.setHostname(serverDescription); +#else + wifi_set_sleep_type((noWifiSleep) ? NONE_SLEEP_T : MODEM_SLEEP_T); +#endif +} + +void WLED::initInterfaces() +{ + DEBUG_PRINTLN("Init STA interfaces"); + + if (hueIP[0] == 0) { + hueIP[0] = WiFi.localIP()[0]; + hueIP[1] = WiFi.localIP()[1]; + hueIP[2] = WiFi.localIP()[2]; + } + + // init Alexa hue emulation + if (alexaEnabled) + alexaInit(); + +#ifndef WLED_DISABLE_OTA + if (aOtaEnabled) + ArduinoOTA.begin(); +#endif + + strip.service(); + // Set up mDNS responder: + if (strlen(cmDNS) > 0) { + if (!aOtaEnabled) + MDNS.begin(cmDNS); + + DEBUG_PRINTLN("mDNS started"); + MDNS.addService("http", "tcp", 80); + MDNS.addService("wled", "tcp", 80); + MDNS.addServiceTxt("wled", "tcp", "mac", escapedMac.c_str()); + } + server.begin(); + + if (udpPort > 0 && udpPort != ntpLocalPort) { + udpConnected = notifierUdp.begin(udpPort); + if (udpConnected && udpRgbPort != udpPort) + udpRgbConnected = rgbUdp.begin(udpRgbPort); + } + if (ntpEnabled) + ntpConnected = ntpUdp.begin(ntpLocalPort); + + initBlynk(blynkApiKey); + e131.begin((e131Multicast) ? E131_MULTICAST : E131_UNICAST, e131Universe, E131_MAX_UNIVERSE_COUNT); + reconnectHue(); + initMqtt(); + interfacesInited = true; + wasConnected = true; +} + +byte stacO = 0; +uint32_t lastHeap; +unsigned long heapTime = 0; + +void WLED::handleConnection() +{ + if (millis() < 2000 && (!WLED_WIFI_CONFIGURED || apBehavior == AP_BEHAVIOR_ALWAYS)) + return; + if (lastReconnectAttempt == 0) + initConnection(); + + // reconnect WiFi to clear stale allocations if heap gets too low + if (millis() - heapTime > 5000) { + uint32_t heap = ESP.getFreeHeap(); + if (heap < 9000 && lastHeap < 9000) { + DEBUG_PRINT("Heap too low! "); + DEBUG_PRINTLN(heap); + forceReconnect = true; + } + lastHeap = heap; + heapTime = millis(); + } + + byte stac = 0; + if (apActive) { +#ifdef ESP8266 + stac = wifi_softap_get_station_num(); +#else + wifi_sta_list_t stationList; + esp_wifi_ap_get_sta_list(&stationList); + stac = stationList.num; +#endif + if (stac != stacO) { + stacO = stac; + DEBUG_PRINT("Connected AP clients: "); + DEBUG_PRINTLN(stac); + if (!WLED_CONNECTED && WLED_WIFI_CONFIGURED) { // trying to connect, but not connected + if (stac) + WiFi.disconnect(); // disable search so that AP can work + else + initConnection(); // restart search + } + } + } + if (forceReconnect) { + DEBUG_PRINTLN("Forcing reconnect."); + initConnection(); + interfacesInited = false; + forceReconnect = false; + wasConnected = false; + return; + } + if (!WLED_CONNECTED) { + if (interfacesInited) { + DEBUG_PRINTLN("Disconnected!"); + interfacesInited = false; + initConnection(); + } + if (millis() - lastReconnectAttempt > ((stac) ? 300000 : 20000) && WLED_WIFI_CONFIGURED) + initConnection(); + if (!apActive && millis() - lastReconnectAttempt > 12000 && (!wasConnected || apBehavior == AP_BEHAVIOR_NO_CONN)) + initAP(); + } else if (!interfacesInited) { // newly connected + DEBUG_PRINTLN(""); + DEBUG_PRINT("Connected! IP address: "); + DEBUG_PRINTLN(WiFi.localIP()); + initInterfaces(); + userConnected(); + + // shut down AP + if (apBehavior != AP_BEHAVIOR_ALWAYS && apActive) { + dnsServer.stop(); + WiFi.softAPdisconnect(true); + apActive = false; + DEBUG_PRINTLN("Access point disabled."); + } + } +} diff --git a/wled00/wled.h b/wled00/wled.h new file mode 100644 index 00000000..3c82efbd --- /dev/null +++ b/wled00/wled.h @@ -0,0 +1,519 @@ +#ifndef WLED_H +#define WLED_H +/* + Main sketch, global variable declarations + @title WLED project sketch + @version 0.9.1n + @author Christian Schwinne + */ + +// version code in format yymmddb (b = daily build) +#define VERSION 2004100 + +// ESP8266-01 (blue) got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.4.2 and the setting 512K(No SPIFFS). + +// ESP8266-01 (black) has 1MB flash and can thus fit the whole program. Use 1M(64K SPIFFS). +// Uncomment some of the following lines to disable features to compile for ESP8266-01 (max flash size 434kB): +// Alternatively, with platformio pass your chosen flags to your custom build target in platformio.ini.override + +// You are required to disable over-the-air updates: +//#define WLED_DISABLE_OTA // saves 14kb + +// You need to choose some of these features to disable: +//#define WLED_DISABLE_ALEXA // saves 11kb +//#define WLED_DISABLE_BLYNK // saves 6kb +//#define WLED_DISABLE_CRONIXIE // saves 3kb +//#define WLED_DISABLE_HUESYNC // saves 4kb +//#define WLED_DISABLE_INFRARED // there is no pin left for this on ESP8266-01, saves 12kb +#define WLED_ENABLE_MQTT // saves 12kb +#define WLED_ENABLE_ADALIGHT // saves 500b only +//#define WLED_ENABLE_DMX // uses 3.5kb (use LEDPIN other than 2) + +#define WLED_DISABLE_FILESYSTEM // SPIFFS is not used by any WLED feature yet +//#define WLED_ENABLE_FS_SERVING // Enable sending html file from SPIFFS before serving progmem version +//#define WLED_ENABLE_FS_EDITOR // enable /edit page for editing SPIFFS content. Will also be disabled with OTA lock + +// to toggle usb serial debug (un)comment the following line +//#define WLED_DEBUG + +// Library inclusions. +#include +#ifdef ESP8266 + #include + #include + #include + extern "C" + { + #include + } +#else // ESP32 + #include + #include "esp_wifi.h" + #include + #include + #include "SPIFFS.h" +#endif + +#include +#include +#include +#include +#ifndef WLED_DISABLE_OTA + #include +#endif +#include +#include "src/dependencies/time/TimeLib.h" +#include "src/dependencies/timezone/Timezone.h" + +#ifndef WLED_DISABLE_ALEXA + #define ESPALEXA_ASYNC + #define ESPALEXA_NO_SUBPAGE + #define ESPALEXA_MAXDEVICES 1 + // #define ESPALEXA_DEBUG + #include "src/dependencies/espalexa/Espalexa.h" +#endif +#ifndef WLED_DISABLE_BLYNK + #include "src/dependencies/blynk/BlynkSimpleEsp.h" +#endif + +#include "src/dependencies/e131/ESPAsyncE131.h" +#include "src/dependencies/async-mqtt-client/AsyncMqttClient.h" +#include "src/dependencies/json/AsyncJson-v6.h" +#include "src/dependencies/json/ArduinoJson-v6.h" + +#include "fcn_declare.h" +#include "html_ui.h" +#include "html_settings.h" +#include "html_other.h" +#include "FX.h" +#include "ir_codes.h" +#include "const.h" + +#ifndef CLIENT_SSID + #define CLIENT_SSID DEFAULT_CLIENT_SSID +#endif + +#ifndef CLIENT_PASS + #define CLIENT_PASS "" +#endif + +#if IR_PIN < 0 + #ifndef WLED_DISABLE_INFRARED + #define WLED_DISABLE_INFRARED + #endif +#endif + +#ifndef WLED_DISABLE_INFRARED + #include + #include + #include +#endif + +// remove flicker because PWM signal of RGB channels can become out of phase +#if defined(WLED_USE_ANALOG_LEDS) && defined(ESP8266) + #include "src/dependencies/arduino/core_esp8266_waveform.h" +#endif + +// enable additional debug output +#ifdef WLED_DEBUG + #ifndef ESP8266 + #include + #endif + #define DEBUG_PRINT(x) Serial.print(x) + #define DEBUG_PRINTLN(x) Serial.println(x) + #define DEBUG_PRINTF(x) Serial.printf(x) +#else + #define DEBUG_PRINT(x) + #define DEBUG_PRINTLN(x) + #define DEBUG_PRINTF(x) +#endif + +// GLOBAL VARIABLES +// both declared and defined in header (solution from http://www.keil.com/support/docs/1868.htm) +// +//e.g. byte test = 2 becomes WLED_GLOBAL byte test _INIT(2); +// int arr[]{0,1,2} becomes WLED_GLOBAL int arr[] _INIT_N(({0,1,2})); + +#ifndef WLED_DEFINE_GLOBAL_VARS +# define WLED_GLOBAL extern +# define _INIT(x) +# define _INIT_N(x) +#else +# define WLED_GLOBAL +# define _INIT(x) = x + +//needed to ignore commas in array definitions +#define UNPACK( ... ) __VA_ARGS__ +# define _INIT_N(x) UNPACK x +#endif + +// Global Variable definitions +WLED_GLOBAL char versionString[] _INIT("0.9.1n"); + +// AP and OTA default passwords (for maximum security change them!) +WLED_GLOBAL char apPass[65] _INIT(DEFAULT_AP_PASS); +WLED_GLOBAL char otaPass[33] _INIT(DEFAULT_OTA_PASS); + +// Hardware CONFIG (only changeble HERE, not at runtime) +// LED strip pin, button pin and IR pin changeable in NpbWrapper.h! + +WLED_GLOBAL byte auxDefaultState _INIT(0); // 0: input 1: high 2: low +WLED_GLOBAL byte auxTriggeredState _INIT(0); // 0: input 1: high 2: low +WLED_GLOBAL char ntpServerName[33] _INIT("0.wled.pool.ntp.org"); // NTP server to use + +// WiFi CONFIG (all these can be changed via web UI, no need to set them here) +WLED_GLOBAL char clientSSID[33] _INIT(CLIENT_SSID); +WLED_GLOBAL char clientPass[65] _INIT(CLIENT_PASS); +WLED_GLOBAL char cmDNS[33] _INIT("x"); // mDNS address (placeholder, is replaced by wledXXXXXX.local) +WLED_GLOBAL char apSSID[33] _INIT(""); // AP off by default (unless setup) +WLED_GLOBAL byte apChannel _INIT(1); // 2.4GHz WiFi AP channel (1-13) +WLED_GLOBAL byte apHide _INIT(0); // hidden AP SSID +WLED_GLOBAL byte apBehavior _INIT(AP_BEHAVIOR_BOOT_NO_CONN); // access point opens when no connection after boot by default +WLED_GLOBAL IPAddress staticIP _INIT_N((( 0, 0, 0, 0))); // static IP of ESP +WLED_GLOBAL IPAddress staticGateway _INIT_N((( 0, 0, 0, 0))); // gateway (router) IP +WLED_GLOBAL IPAddress staticSubnet _INIT_N(((255, 255, 255, 0))); // most common subnet in home networks +WLED_GLOBAL bool noWifiSleep _INIT(false); // disabling modem sleep modes will increase heat output and power usage, but may help with connection issues + +// LED CONFIG +WLED_GLOBAL uint16_t ledCount _INIT(30); // overcurrent prevented by ABL +WLED_GLOBAL bool useRGBW _INIT(false); // SK6812 strips can contain an extra White channel +WLED_GLOBAL bool turnOnAtBoot _INIT(true); // turn on LEDs at power-up +WLED_GLOBAL byte bootPreset _INIT(0); // save preset to load after power-up + +WLED_GLOBAL byte col[] _INIT_N(({ 255, 160, 0, 0 })); // current RGB(W) primary color. col[] should be updated if you want to change the color. +WLED_GLOBAL byte colSec[] _INIT_N(({ 0, 0, 0, 0 })); // current RGB(W) secondary color +WLED_GLOBAL byte briS _INIT(128); // default brightness + +WLED_GLOBAL byte nightlightTargetBri _INIT(0); // brightness after nightlight is over +WLED_GLOBAL byte nightlightDelayMins _INIT(60); +WLED_GLOBAL bool nightlightFade _INIT(true); // if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over +WLED_GLOBAL bool nightlightColorFade _INIT(false); // if enabled, light will gradually fade color from primary to secondary color. +WLED_GLOBAL bool fadeTransition _INIT(true); // enable crossfading color transition +WLED_GLOBAL uint16_t transitionDelay _INIT(750); // default crossfade duration in ms + +WLED_GLOBAL bool skipFirstLed _INIT(false); // ignore first LED in strip (useful if you need the LED as signal repeater) +WLED_GLOBAL byte briMultiplier _INIT(100); // % of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) + +// User Interface CONFIG +WLED_GLOBAL char serverDescription[33] _INIT("WLED"); // Name of module +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 + +// Sync CONFIG +WLED_GLOBAL bool buttonEnabled _INIT(true); +WLED_GLOBAL byte irEnabled _INIT(0); // Infrared receiver + +WLED_GLOBAL uint16_t udpPort _INIT(21324); // WLED notifier default port +WLED_GLOBAL uint16_t udpRgbPort _INIT(19446); // Hyperion port + +WLED_GLOBAL bool receiveNotificationBrightness _INIT(true); // apply brightness from incoming notifications +WLED_GLOBAL bool receiveNotificationColor _INIT(true); // apply color +WLED_GLOBAL bool receiveNotificationEffects _INIT(true); // apply effects setup +WLED_GLOBAL bool notifyDirect _INIT(false); // send notification if change via UI or HTTP API +WLED_GLOBAL bool notifyButton _INIT(false); // send if updated by button or infrared remote +WLED_GLOBAL bool notifyAlexa _INIT(false); // send notification if updated via Alexa +WLED_GLOBAL bool notifyMacro _INIT(false); // send notification for macro +WLED_GLOBAL bool notifyHue _INIT(true); // send notification if Hue light changes +WLED_GLOBAL bool notifyTwice _INIT(false); // notifications use UDP: enable if devices don't sync reliably + +WLED_GLOBAL bool alexaEnabled _INIT(true); // enable device discovery by Amazon Echo +WLED_GLOBAL char alexaInvocationName[33] _INIT("Light"); // speech control name of device. Choose something voice-to-text can understand + +WLED_GLOBAL char blynkApiKey[36] _INIT(""); // Auth token for Blynk server. If empty, no connection will be made + +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 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 + +WLED_GLOBAL uint16_t e131Universe _INIT(1); // settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) +WLED_GLOBAL byte DMXMode _INIT(DMX_MODE_MULTIPLE_RGB); // DMX mode (s.a.) +WLED_GLOBAL uint16_t DMXAddress _INIT(1); // DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] +WLED_GLOBAL byte DMXOldDimmer _INIT(0); // only update brightness on change +WLED_GLOBAL byte e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; // to detect packet loss +WLED_GLOBAL bool e131Multicast _INIT(false); // multicast or unicast +WLED_GLOBAL bool e131SkipOutOfSequence _INIT(false); // freeze instead of flickering + +WLED_GLOBAL bool mqttEnabled _INIT(false); +WLED_GLOBAL char mqttDeviceTopic[33] _INIT(""); // main MQTT topic (individual per device, default is wled/mac) +WLED_GLOBAL char mqttGroupTopic[33] _INIT("wled/all"); // second MQTT topic (for example to group devices) +WLED_GLOBAL char mqttServer[33] _INIT(""); // both domains and IPs should work (no SSL) +WLED_GLOBAL char mqttUser[41] _INIT(""); // optional: username for MQTT auth +WLED_GLOBAL char mqttPass[41] _INIT(""); // optional: password for MQTT auth +WLED_GLOBAL char mqttClientID[41] _INIT(""); // override the client ID +WLED_GLOBAL uint16_t mqttPort _INIT(1883); + +WLED_GLOBAL bool huePollingEnabled _INIT(false); // poll hue bridge for light state +WLED_GLOBAL uint16_t huePollIntervalMs _INIT(2500); // low values (< 1sec) may cause lag but offer quicker response +WLED_GLOBAL char hueApiKey[47] _INIT("api"); // key token will be obtained from bridge +WLED_GLOBAL byte huePollLightId _INIT(1); // ID of hue lamp to sync to. Find the ID in the hue app ("about" section) +WLED_GLOBAL IPAddress hueIP _INIT((0, 0, 0, 0)); // IP address of the bridge +WLED_GLOBAL bool hueApplyOnOff _INIT(true); +WLED_GLOBAL bool hueApplyBri _INIT(true); +WLED_GLOBAL bool hueApplyColor _INIT(true); + +// Time CONFIG +WLED_GLOBAL bool ntpEnabled _INIT(false); // get internet time. Only required if you use clock overlays or time-activated macros +WLED_GLOBAL bool useAMPM _INIT(false); // 12h/24h clock format +WLED_GLOBAL byte currentTimezone _INIT(0); // Timezone ID. Refer to timezones array in wled10_ntp.ino +WLED_GLOBAL int utcOffsetSecs _INIT(0); // Seconds to offset from UTC before timzone calculation + +WLED_GLOBAL byte overlayDefault _INIT(0); // 0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie +WLED_GLOBAL byte overlayMin _INIT(0), overlayMax _INIT(ledCount - 1); // boundaries of overlay mode + +WLED_GLOBAL byte analogClock12pixel _INIT(0); // The pixel in your strip where "midnight" would be +WLED_GLOBAL bool analogClockSecondsTrail _INIT(false); // Display seconds as trail of LEDs instead of a single pixel +WLED_GLOBAL bool analogClock5MinuteMarks _INIT(false); // Light pixels at every 5-minute position + +WLED_GLOBAL char cronixieDisplay[7] _INIT("HHMMSS"); // Cronixie Display mask. See wled13_cronixie.ino +WLED_GLOBAL bool cronixieBacklight _INIT(true); // Allow digits to be back-illuminated + +WLED_GLOBAL bool countdownMode _INIT(false); // Clock will count down towards date +WLED_GLOBAL byte countdownYear _INIT(20), countdownMonth _INIT(1); // Countdown target date, year is last two digits +WLED_GLOBAL byte countdownDay _INIT(1) , countdownHour _INIT(0); +WLED_GLOBAL byte countdownMin _INIT(0) , countdownSec _INIT(0); + +WLED_GLOBAL byte macroBoot _INIT(0); // macro loaded after startup +WLED_GLOBAL byte macroNl _INIT(0); // after nightlight delay over +WLED_GLOBAL byte macroCountdown _INIT(0); +WLED_GLOBAL byte macroAlexaOn _INIT(0), macroAlexaOff _INIT(0); +WLED_GLOBAL byte macroButton _INIT(0), macroLongPress _INIT(0), macroDoublePress _INIT(0); + +// Security CONFIG +WLED_GLOBAL bool otaLock _INIT(false); // prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks +WLED_GLOBAL bool wifiLock _INIT(false); // prevents access to WiFi settings when OTA lock is enabled +WLED_GLOBAL bool aOtaEnabled _INIT(true); // ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on + +WLED_GLOBAL uint16_t userVar0 _INIT(0), userVar1 _INIT(0); //available for use in usermod + +#ifdef WLED_ENABLE_DMX + // dmx CONFIG + WLED_GLOBAL byte DMXChannels _INIT(7); // number of channels per fixture + WLED_GLOBAL byte DMXFixtureMap[15] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 })); + // assigns the different channels to different functions. See wled21_dmx.ino for more information. + WLED_GLOBAL uint16_t DMXGap _INIT(10); // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. + WLED_GLOBAL uint16_t DMXStart _INIT(10); // start address of the first fixture + WLED_GLOBAL uint16_t DMXStartLED _INIT(0); // LED from which DMX fixtures start +#endif + +// internal global variable declarations +// wifi +WLED_GLOBAL bool apActive _INIT(false); +WLED_GLOBAL bool forceReconnect _INIT(false); +WLED_GLOBAL uint32_t lastReconnectAttempt _INIT(0); +WLED_GLOBAL bool interfacesInited _INIT(false); +WLED_GLOBAL bool wasConnected _INIT(false); + +// color +WLED_GLOBAL byte colOld[] _INIT_N(({ 0, 0, 0, 0 })); // color before transition +WLED_GLOBAL byte colT[] _INIT_N(({ 0, 0, 0, 0 })); // color that is currently displayed on the LEDs +WLED_GLOBAL byte colIT[] _INIT_N(({ 0, 0, 0, 0 })); // color that was last sent to LEDs +WLED_GLOBAL byte colSecT[] _INIT_N(({ 0, 0, 0, 0 })); +WLED_GLOBAL byte colSecOld[] _INIT_N(({ 0, 0, 0, 0 })); +WLED_GLOBAL byte colSecIT[] _INIT_N(({ 0, 0, 0, 0 })); + +WLED_GLOBAL byte lastRandomIndex _INIT(0); // used to save last random color so the new one is not the same + +// transitions +WLED_GLOBAL bool transitionActive _INIT(false); +WLED_GLOBAL uint16_t transitionDelayDefault _INIT(transitionDelay); +WLED_GLOBAL uint16_t transitionDelayTemp _INIT(transitionDelay); +WLED_GLOBAL unsigned long transitionStartTime; +WLED_GLOBAL float tperLast _INIT(0); // crossfade transition progress, 0.0f - 1.0f +WLED_GLOBAL bool jsonTransitionOnce _INIT(false); + +// nightlight +WLED_GLOBAL bool nightlightActive _INIT(false); +WLED_GLOBAL bool nightlightActiveOld _INIT(false); +WLED_GLOBAL uint32_t nightlightDelayMs _INIT(10); +WLED_GLOBAL byte nightlightDelayMinsDefault _INIT(nightlightDelayMins); +WLED_GLOBAL unsigned long nightlightStartTime; +WLED_GLOBAL byte briNlT _INIT(0); // current nightlight brightness +WLED_GLOBAL byte colNlT[] _INIT_N(({ 0, 0, 0, 0 })); // current nightlight color + +// brightness +WLED_GLOBAL unsigned long lastOnTime _INIT(0); +WLED_GLOBAL bool offMode _INIT(!turnOnAtBoot); +WLED_GLOBAL byte bri _INIT(briS); +WLED_GLOBAL byte briOld _INIT(0); +WLED_GLOBAL byte briT _INIT(0); +WLED_GLOBAL byte briIT _INIT(0); +WLED_GLOBAL byte briLast _INIT(128); // brightness before turned off. Used for toggle function +WLED_GLOBAL byte whiteLast _INIT(128); // white channel before turned off. Used for toggle function + +// button +WLED_GLOBAL bool buttonPressedBefore _INIT(false); +WLED_GLOBAL bool buttonLongPressed _INIT(false); +WLED_GLOBAL unsigned long buttonPressedTime _INIT(0); +WLED_GLOBAL unsigned long buttonWaitTime _INIT(0); + +// notifications +WLED_GLOBAL bool notifyDirectDefault _INIT(notifyDirect); +WLED_GLOBAL bool receiveNotifications _INIT(true); +WLED_GLOBAL unsigned long notificationSentTime _INIT(0); +WLED_GLOBAL byte notificationSentCallMode _INIT(NOTIFIER_CALL_MODE_INIT); +WLED_GLOBAL bool notificationTwoRequired _INIT(false); + +// effects +WLED_GLOBAL byte effectCurrent _INIT(0); +WLED_GLOBAL byte effectSpeed _INIT(128); +WLED_GLOBAL byte effectIntensity _INIT(128); +WLED_GLOBAL byte effectPalette _INIT(0); + +// network +WLED_GLOBAL bool udpConnected _INIT(false), udpRgbConnected _INIT(false); + +// ui style +WLED_GLOBAL bool showWelcomePage _INIT(false); + +// hue +WLED_GLOBAL byte hueError _INIT(HUE_ERROR_INACTIVE); +// WLED_GLOBAL uint16_t hueFailCount _INIT(0); +WLED_GLOBAL float hueXLast _INIT(0), hueYLast _INIT(0); +WLED_GLOBAL uint16_t hueHueLast _INIT(0), hueCtLast _INIT(0); +WLED_GLOBAL byte hueSatLast _INIT(0), hueBriLast _INIT(0); +WLED_GLOBAL unsigned long hueLastRequestSent _INIT(0); +WLED_GLOBAL bool hueAuthRequired _INIT(false); +WLED_GLOBAL bool hueReceived _INIT(false); +WLED_GLOBAL bool hueStoreAllowed _INIT(false), hueNewKey _INIT(false); + +// overlays +WLED_GLOBAL byte overlayCurrent _INIT(overlayDefault); +WLED_GLOBAL byte overlaySpeed _INIT(200); +WLED_GLOBAL unsigned long overlayRefreshMs _INIT(200); +WLED_GLOBAL unsigned long overlayRefreshedTime; + +// cronixie +WLED_GLOBAL byte dP[] _INIT_N(({ 0, 0, 0, 0, 0, 0 })); +WLED_GLOBAL bool cronixieInit _INIT(false); + +// countdown +WLED_GLOBAL unsigned long countdownTime _INIT(1514764800L); +WLED_GLOBAL bool countdownOverTriggered _INIT(true); + +// timer +WLED_GLOBAL byte lastTimerMinute _INIT(0); +WLED_GLOBAL byte timerHours[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0 })); +WLED_GLOBAL byte timerMinutes[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0 })); +WLED_GLOBAL byte timerMacro[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0 })); +WLED_GLOBAL byte timerWeekday[] _INIT_N(({ 255, 255, 255, 255, 255, 255, 255, 255 })); // weekdays to activate on +// bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity + +// blynk +WLED_GLOBAL bool blynkEnabled _INIT(false); + +// preset cycling +WLED_GLOBAL bool presetCyclingEnabled _INIT(false); +WLED_GLOBAL byte presetCycleMin _INIT(1), presetCycleMax _INIT(5); +WLED_GLOBAL uint16_t presetCycleTime _INIT(1250); +WLED_GLOBAL unsigned long presetCycledTime _INIT(0); +WLED_GLOBAL byte presetCycCurr _INIT(presetCycleMin); +WLED_GLOBAL bool presetApplyBri _INIT(true); +WLED_GLOBAL bool saveCurrPresetCycConf _INIT(false); + +// realtime +WLED_GLOBAL byte realtimeMode _INIT(REALTIME_MODE_INACTIVE); +WLED_GLOBAL IPAddress realtimeIP _INIT((0, 0, 0, 0)); +WLED_GLOBAL unsigned long realtimeTimeout _INIT(0); + +// mqtt +WLED_GLOBAL long lastMqttReconnectAttempt _INIT(0); +WLED_GLOBAL long lastInterfaceUpdate _INIT(0); +WLED_GLOBAL byte interfaceUpdateCallMode _INIT(NOTIFIER_CALL_MODE_INIT); +WLED_GLOBAL char mqttStatusTopic[40] _INIT(""); // this must be global because of async handlers + +#if AUXPIN >= 0 + // auxiliary debug pin + WLED_GLOBAL byte auxTime _INIT(0); + WLED_GLOBAL unsigned long auxStartTime _INIT(0); + WLED_GLOBAL bool auxActive _INIT(false, auxActiveBefore _INIT(false); +#endif + +// alexa udp +WLED_GLOBAL String escapedMac; +#ifndef WLED_DISABLE_ALEXA + WLED_GLOBAL Espalexa espalexa; + WLED_GLOBAL EspalexaDevice* espalexaDevice; +#endif + +// dns server +WLED_GLOBAL DNSServer dnsServer; + +// network time +WLED_GLOBAL bool ntpConnected _INIT(false); +WLED_GLOBAL time_t local _INIT(0); +WLED_GLOBAL unsigned long ntpLastSyncTime _INIT(999000000L); +WLED_GLOBAL unsigned long ntpPacketSentTime _INIT(999000000L); +WLED_GLOBAL IPAddress ntpServerIP; +WLED_GLOBAL uint16_t ntpLocalPort _INIT(2390); + +// Temp buffer +WLED_GLOBAL char* obuf; +WLED_GLOBAL uint16_t olen _INIT(0); + +// presets +WLED_GLOBAL uint16_t savedPresets _INIT(0); +WLED_GLOBAL int8_t currentPreset _INIT(-1); +WLED_GLOBAL bool isPreset _INIT(false); + +WLED_GLOBAL byte errorFlag _INIT(0); + +WLED_GLOBAL String messageHead, messageSub; +WLED_GLOBAL byte optionType; + +WLED_GLOBAL bool doReboot _INIT(false); // flag to initiate reboot from async handlers +WLED_GLOBAL bool doPublishMqtt _INIT(false); + +// server library objects +WLED_GLOBAL AsyncWebServer server _INIT_N(((80))); +WLED_GLOBAL AsyncClient* hueClient _INIT(NULL); +WLED_GLOBAL AsyncMqttClient* mqtt _INIT(NULL); + +// udp interface objects +WLED_GLOBAL WiFiUDP notifierUdp, rgbUdp; +WLED_GLOBAL WiFiUDP ntpUdp; +WLED_GLOBAL ESPAsyncE131 e131 _INIT_N(((handleE131Packet))); +WLED_GLOBAL bool e131NewData _INIT(false); + +// led fx library object +WLED_GLOBAL WS2812FX strip _INIT(WS2812FX()); + +// debug macro variable definitions +#ifdef WLED_DEBUG + WLED_GLOBAL unsigned long debugTime _INIT(0); + WLED_GLOBAL int lastWifiState _INIT(3); + WLED_GLOBAL unsigned long wifiStateChangedTime _INIT(0); + WLED_GLOBAL int loops _INIT(0); +#endif + + +#define WLED_CONNECTED (WiFi.status() == WL_CONNECTED) +#define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID, DEFAULT_CLIENT_SSID) != 0) + +// append new c string to temp buffer efficiently +bool oappend(const char* txt); +// append new number to temp buffer efficiently +bool oappendi(int i); + +class WLED { +public: + WLED(); + static WLED& instance() + { + static WLED instance; + return instance; + } + + // boot starts here + void setup(); + + void loop(); + void reset(); + + void beginStrip(); + void handleConnection(); + void initAP(bool resetAP = false); + void initConnection(); + void initInterfaces(); +}; +#endif // WLED_H diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 851a9c45..866543ab 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -1,619 +1,21 @@ /* - Main sketch, global variable declarations -*/ -/* - * @title WLED project sketch - * @version 0.9.1 - * @author Christian Schwinne + * WLED Arduino IDE compatibility file. + * + * Where has everything gone? + * + * In April 2020, the project's structure underwent a major change. + * Global variables are now found in file "wled.h" + * Global function declarations are found in "fcn_declare.h" + * + * Usermod compatibility: Existing wled06_usermod.ino mods should continue to work. Delete usermod.cpp. + * New usermods should use usermod.cpp instead. */ +#include "wled.h" -//ESP8266-01 (blue) got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.4.2 and the setting 512K(No SPIFFS). - -//ESP8266-01 (black) has 1MB flash and can thus fit the whole program. Use 1M(64K SPIFFS). -//Uncomment some of the following lines to disable features to compile for ESP8266-01 (max flash size 434kB): - -//You are required to disable over-the-air updates: -//#define WLED_DISABLE_OTA //saves 14kb - -//You need to choose some of these features to disable: -//#define WLED_DISABLE_ALEXA //saves 11kb -//#define WLED_DISABLE_BLYNK //saves 6kb -//#define WLED_DISABLE_CRONIXIE //saves 3kb -//#define WLED_DISABLE_HUESYNC //saves 4kb -//#define WLED_DISABLE_INFRARED //there is no pin left for this on ESP8266-01, saves 12kb -#define WLED_ENABLE_MQTT //saves 12kb -#define WLED_ENABLE_ADALIGHT //saves 500b only -//#define WLED_ENABLE_DMX //uses 3.5kb, if wanting to use DMX together with LEDs, you should change the LEDPIN to something other than 2 in NpbWrapper.h - -#define WLED_DISABLE_FILESYSTEM //SPIFFS is not used by any WLED feature yet -//#define WLED_ENABLE_FS_SERVING //Enable sending html file from SPIFFS before serving progmem version -//#define WLED_ENABLE_FS_EDITOR //enable /edit page for editing SPIFFS content. Will also be disabled with OTA lock - -//to toggle usb serial debug (un)comment the following line -//#define WLED_DEBUG - -//library inclusions -#include -#ifdef WLED_ENABLE_DMX - #include - DMXESPSerial dmx; -#endif -#ifdef ESP8266 - #include - #include - #include - extern "C" { - #include - } -#else //ESP32 - #include - #include "esp_wifi.h" - #include - #include - #include "SPIFFS.h" -#endif - -#include -#include -#include -#include -#ifndef WLED_DISABLE_OTA - #include -#endif -#include -#include "src/dependencies/time/TimeLib.h" -#include "src/dependencies/timezone/Timezone.h" -#ifndef WLED_DISABLE_ALEXA - #define ESPALEXA_ASYNC - #define ESPALEXA_NO_SUBPAGE - #define ESPALEXA_MAXDEVICES 1 - // #define ESPALEXA_DEBUG - #include "src/dependencies/espalexa/Espalexa.h" -#endif -#ifndef WLED_DISABLE_BLYNK - #include "src/dependencies/blynk/BlynkSimpleEsp.h" -#endif -#include "src/dependencies/e131/ESPAsyncE131.h" -#include "src/dependencies/async-mqtt-client/AsyncMqttClient.h" -#include "src/dependencies/json/AsyncJson-v6.h" -#include "src/dependencies/json/ArduinoJson-v6.h" -#include "html_ui.h" -#include "html_settings.h" -#include "html_other.h" -#include "FX.h" -#include "ir_codes.h" -#include "const.h" - -#ifndef CLIENT_SSID -#define CLIENT_SSID DEFAULT_CLIENT_SSID -#endif - -#ifndef CLIENT_PASS -#define CLIENT_PASS "" -#endif - - -#if IR_PIN < 0 - #ifndef WLED_DISABLE_INFRARED - #define WLED_DISABLE_INFRARED - #endif -#endif - -#ifndef WLED_DISABLE_INFRARED - #include - #include - #include -#endif - -// remove flicker because PWM signal of RGB channels can become out of phase -#if defined(WLED_USE_ANALOG_LEDS) && defined(ESP8266) - #include "src/dependencies/arduino/core_esp8266_waveform.h" -#endif - -// enable additional debug output -#ifdef WLED_DEBUG - #ifndef ESP8266 - #include - #endif -#endif - -//version code in format yymmddb (b = daily build) -#define VERSION 2003262 - -char versionString[] = "0.9.1"; - - -//AP and OTA default passwords (for maximum change them!) -char apPass[65] = DEFAULT_AP_PASS; -char otaPass[33] = DEFAULT_OTA_PASS; - - -//Hardware CONFIG (only changeble HERE, not at runtime) -//LED strip pin, button pin and IR pin changeable in NpbWrapper.h! - -byte auxDefaultState = 0; //0: input 1: high 2: low -byte auxTriggeredState = 0; //0: input 1: high 2: low -char ntpServerName[33] = "0.wled.pool.ntp.org";//NTP server to use - - -//WiFi CONFIG (all these can be changed via web UI, no need to set them here) -char clientSSID[33] = CLIENT_SSID; -char clientPass[65] = CLIENT_PASS; -char cmDNS[33] = "x"; //mDNS address (placeholder, will be replaced by wledXXXXXXXXXXXX.local) -char apSSID[33] = ""; //AP off by default (unless setup) -byte apChannel = 1; //2.4GHz WiFi AP channel (1-13) -byte apHide = 0; //hidden AP SSID -byte apBehavior = AP_BEHAVIOR_BOOT_NO_CONN; //access point opens when no connection after boot by default -IPAddress staticIP(0, 0, 0, 0); //static IP of ESP -IPAddress staticGateway(0, 0, 0, 0); //gateway (router) IP -IPAddress staticSubnet(255, 255, 255, 0); //most common subnet in home networks -bool noWifiSleep = false; //disabling modem sleep modes will increase heat output and power usage, but may help with connection issues - - -//LED CONFIG -uint16_t ledCount = 30; //overcurrent prevented by ABL -bool useRGBW = false; //SK6812 strips can contain an extra White channel -#define ABL_MILLIAMPS_DEFAULT 850; //auto lower brightness to stay close to milliampere limit -bool turnOnAtBoot = true; //turn on LEDs at power-up -byte bootPreset = 0; //save preset to load after power-up - -byte col[] {255, 160, 0, 0}; //current RGB(W) primary color. col[] should be updated if you want to change the color. -byte colSec[] {0, 0, 0, 0}; //current RGB(W) secondary color -byte briS = 128; //default brightness - -byte nightlightTargetBri = 0; //brightness after nightlight is over -byte nightlightDelayMins = 60; -bool nightlightFade = true; //if enabled, light will gradually dim towards the target bri. Otherwise, it will instantly set after delay over -bool nightlightColorFade = false; //if enabled, light will gradually fade color from primary to secondary color. -bool fadeTransition = true; //enable crossfading color transition -uint16_t transitionDelay = 750; //default crossfade duration in ms - -bool skipFirstLed = false; //ignore first LED in strip (useful if you need the LED as signal repeater) -byte briMultiplier = 100; //% of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) - - -//User Interface CONFIG -char serverDescription[33] = "WLED"; //Name of module -bool syncToggleReceive = false; //UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise - - -//Sync CONFIG -bool buttonEnabled = true; -byte irEnabled = 0; //Infrared receiver - -uint16_t udpPort = 21324; //WLED notifier default port -uint16_t udpRgbPort = 19446; //Hyperion port - -bool receiveNotificationBrightness = true; //apply brightness from incoming notifications -bool receiveNotificationColor = true; //apply color -bool receiveNotificationEffects = true; //apply effects setup -bool notifyDirect = false; //send notification if change via UI or HTTP API -bool notifyButton = false; //send if updated by button or infrared remote -bool notifyAlexa = false; //send notification if updated via Alexa -bool notifyMacro = false; //send notification for macro -bool notifyHue = true; //send notification if Hue light changes -bool notifyTwice = false; //notifications use UDP: enable if devices don't sync reliably - -bool alexaEnabled = true; //enable device discovery by Amazon Echo -char alexaInvocationName[33] = "Light"; //speech control name of device. Choose something voice-to-text can understand - -char blynkApiKey[36] = ""; //Auth token for Blynk server. If empty, no connection will be made - -uint16_t realtimeTimeoutMs = 2500; //ms timeout of realtime mode before returning to normal mode -int arlsOffset = 0; //realtime LED offset -bool receiveDirect = true; //receive UDP realtime -bool arlsDisableGammaCorrection = true; //activate if gamma correction is handled by the source -bool arlsForceMaxBri = false; //enable to force max brightness if source has very dark colors that would be black - -#define E131_MAX_UNIVERSE_COUNT 9 -uint16_t e131Universe = 1; //settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes) -uint8_t DMXMode = DMX_MODE_MULTIPLE_RGB; //DMX mode (s.a.) -uint16_t DMXAddress = 1; //DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] -uint8_t DMXOldDimmer = 0; //only update brightness on change -uint8_t e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; //to detect packet loss -bool e131Multicast = false; //multicast or unicast -bool e131SkipOutOfSequence = false; //freeze instead of flickering - -bool mqttEnabled = false; -char mqttDeviceTopic[33] = ""; //main MQTT topic (individual per device, default is wled/mac) -char mqttGroupTopic[33] = "wled/all"; //second MQTT topic (for example to group devices) -char mqttServer[33] = ""; //both domains and IPs should work (no SSL) -char mqttUser[41] = ""; //optional: username for MQTT auth -char mqttPass[41] = ""; //optional: password for MQTT auth -char mqttClientID[41] = ""; //override the client ID -uint16_t mqttPort = 1883; - -bool huePollingEnabled = false; //poll hue bridge for light state -uint16_t huePollIntervalMs = 2500; //low values (< 1sec) may cause lag but offer quicker response -char hueApiKey[47] = "api"; //key token will be obtained from bridge -byte huePollLightId = 1; //ID of hue lamp to sync to. Find the ID in the hue app ("about" section) -IPAddress hueIP = (0, 0, 0, 0); //IP address of the bridge -bool hueApplyOnOff = true; -bool hueApplyBri = true; -bool hueApplyColor = true; - - -//Time CONFIG -bool ntpEnabled = false; //get internet time. Only required if you use clock overlays or time-activated macros -bool useAMPM = false; //12h/24h clock format -byte currentTimezone = 0; //Timezone ID. Refer to timezones array in wled10_ntp.ino -int utcOffsetSecs = 0; //Seconds to offset from UTC before timzone calculation - -byte overlayDefault = 0; //0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie -byte overlayMin = 0, overlayMax = ledCount - 1; //boundaries of overlay mode - -byte analogClock12pixel = 0; //The pixel in your strip where "midnight" would be -bool analogClockSecondsTrail = false; //Display seconds as trail of LEDs instead of a single pixel -bool analogClock5MinuteMarks = false; //Light pixels at every 5-minute position - -char cronixieDisplay[7] = "HHMMSS"; //Cronixie Display mask. See wled13_cronixie.ino -bool cronixieBacklight = true; //Allow digits to be back-illuminated - -bool countdownMode = false; //Clock will count down towards date -byte countdownYear = 20, countdownMonth = 1; //Countdown target date, year is last two digits -byte countdownDay = 1, countdownHour = 0; -byte countdownMin = 0, countdownSec = 0; - - -byte macroBoot = 0; //macro loaded after startup -byte macroNl = 0; //after nightlight delay over -byte macroCountdown = 0; -byte macroAlexaOn = 0, macroAlexaOff = 0; -byte macroButton = 0, macroLongPress = 0, macroDoublePress = 0; - - -//Security CONFIG -bool otaLock = false; //prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks -bool wifiLock = false; //prevents access to WiFi settings when OTA lock is enabled -bool aOtaEnabled = true; //ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on - - -uint16_t userVar0 = 0, userVar1 = 0; - -#ifdef WLED_ENABLE_DMX - //dmx CONFIG - byte DMXChannels = 7; // number of channels per fixture - byte DMXFixtureMap[15] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - // assigns the different channels to different functions. See wled21_dmx.ino for more information. - uint16_t DMXGap = 10; // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig. - uint16_t DMXStart = 10; // start address of the first fixture - uint16_t DMXSegment = 0; // segment which DMX fixtures use - uint16_t DMXStartLED = 0; // start LED from which DMX fixtures should start -#endif - - -//internal global variable declarations -//wifi -bool apActive = false; -bool forceReconnect = false; -uint32_t lastReconnectAttempt = 0; -bool interfacesInited = false; -bool wasConnected = false; - -//color -byte colOld[] {0, 0, 0, 0}; //color before transition -byte colT[] {0, 0, 0, 0}; //color that is currently displayed on the LEDs -byte colIT[] {0, 0, 0, 0}; //color that was last sent to LEDs -byte colSecT[] {0, 0, 0, 0}; -byte colSecOld[] {0, 0, 0, 0}; -byte colSecIT[] {0, 0, 0, 0}; - -byte lastRandomIndex = 0; //used to save last random color so the new one is not the same - -//transitions -bool transitionActive = false; -uint16_t transitionDelayDefault = transitionDelay; -uint16_t transitionDelayTemp = transitionDelay; -unsigned long transitionStartTime; -float tperLast = 0; //crossfade transition progress, 0.0f - 1.0f -bool jsonTransitionOnce = false; - -//nightlight -bool nightlightActive = false; -bool nightlightActiveOld = false; -uint32_t nightlightDelayMs = 10; -uint8_t nightlightDelayMinsDefault = nightlightDelayMins; -unsigned long nightlightStartTime; -byte briNlT = 0; //current nightlight brightness -byte colNlT[] {0, 0, 0, 0}; //current nightlight color - -//brightness -unsigned long lastOnTime = 0; -bool offMode = !turnOnAtBoot; -byte bri = briS; -byte briOld = 0; -byte briT = 0; -byte briIT = 0; -byte briLast = 128; //brightness before turned off. Used for toggle function -byte whiteLast = 128; //white channel before turned off. Used for toggle function - -//button -bool buttonPressedBefore = false; -bool buttonLongPressed = false; -unsigned long buttonPressedTime = 0; -unsigned long buttonWaitTime = 0; - -//notifications -bool notifyDirectDefault = notifyDirect; -bool receiveNotifications = true; -unsigned long notificationSentTime = 0; -byte notificationSentCallMode = NOTIFIER_CALL_MODE_INIT; -bool notificationTwoRequired = false; - -//effects -byte effectCurrent = 0; -byte effectSpeed = 128; -byte effectIntensity = 128; -byte effectPalette = 0; - -//network -bool udpConnected = false, udpRgbConnected = false; - -//ui style -bool showWelcomePage = false; - -//hue -byte hueError = HUE_ERROR_INACTIVE; -//uint16_t hueFailCount = 0; -float hueXLast = 0, hueYLast = 0; -uint16_t hueHueLast = 0, hueCtLast = 0; -byte hueSatLast = 0, hueBriLast = 0; -unsigned long hueLastRequestSent = 0; -bool hueAuthRequired = false; -bool hueReceived = false; -bool hueStoreAllowed = false, hueNewKey = false; - -//overlays -byte overlayCurrent = overlayDefault; -byte overlaySpeed = 200; -unsigned long overlayRefreshMs = 200; -unsigned long overlayRefreshedTime; - -//cronixie -byte dP[] {0, 0, 0, 0, 0, 0}; -bool cronixieInit = false; - -//countdown -unsigned long countdownTime = 1514764800L; -bool countdownOverTriggered = true; - -//timer -byte lastTimerMinute = 0; -byte timerHours[] = {0, 0, 0, 0, 0, 0, 0, 0}; -byte timerMinutes[] = {0, 0, 0, 0, 0, 0, 0, 0}; -byte timerMacro[] = {0, 0, 0, 0, 0, 0, 0, 0}; -byte timerWeekday[] = {255, 255, 255, 255, 255, 255, 255, 255}; //weekdays to activate on -//bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity - -//blynk -bool blynkEnabled = false; - -//preset cycling -bool presetCyclingEnabled = false; -byte presetCycleMin = 1, presetCycleMax = 5; -uint16_t presetCycleTime = 1250; -unsigned long presetCycledTime = 0; byte presetCycCurr = presetCycleMin; -bool presetApplyBri = true; -bool saveCurrPresetCycConf = false; - -//realtime -byte realtimeMode = REALTIME_MODE_INACTIVE; -IPAddress realtimeIP = (0,0,0,0); -unsigned long realtimeTimeout = 0; - -//mqtt -long lastMqttReconnectAttempt = 0; -long lastInterfaceUpdate = 0; -byte interfaceUpdateCallMode = NOTIFIER_CALL_MODE_INIT; -char mqttStatusTopic[40] = ""; //this must be global because of async handlers - -#if AUXPIN >= 0 - //auxiliary debug pin - byte auxTime = 0; - unsigned long auxStartTime = 0; - bool auxActive = false, auxActiveBefore = false; -#endif - -//alexa udp -String escapedMac; -#ifndef WLED_DISABLE_ALEXA - Espalexa espalexa; - EspalexaDevice* espalexaDevice; -#endif - -//dns server -DNSServer dnsServer; - -//network time -bool ntpConnected = false; -time_t local = 0; -unsigned long ntpLastSyncTime = 999000000L; -unsigned long ntpPacketSentTime = 999000000L; -IPAddress ntpServerIP; -uint16_t ntpLocalPort = 2390; -#define NTP_PACKET_SIZE 48 - -//maximum number of LEDs - MAX_LEDS is coming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue -#define MAX_LEDS 1500 -#define MAX_LEDS_DMA 500 - -//string temp buffer (now stored in stack locally) -#define OMAX 2048 -char* obuf; -uint16_t olen = 0; - -//presets -uint16_t savedPresets = 0; -int8_t currentPreset = -1; -bool isPreset = false; - -byte errorFlag = 0; - -String messageHead, messageSub; -byte optionType; - -bool doReboot = false; //flag to initiate reboot from async handlers -bool doPublishMqtt = false; - -//server library objects -AsyncWebServer server(80); -AsyncClient* hueClient = NULL; -AsyncMqttClient* mqtt = NULL; - -//function prototypes -void colorFromUint32(uint32_t, bool = false); -void serveMessage(AsyncWebServerRequest*, uint16_t, String, String, byte); -void handleE131Packet(e131_packet_t*, IPAddress); -void arlsLock(uint32_t,byte); -void handleOverlayDraw(); - -//udp interface objects -WiFiUDP notifierUdp, rgbUdp; -WiFiUDP ntpUdp; -ESPAsyncE131 e131(handleE131Packet); -bool e131NewData = false; - -//led fx library object -WS2812FX strip = WS2812FX(); - -#define WLED_CONNECTED (WiFi.status() == WL_CONNECTED) -#define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID,DEFAULT_CLIENT_SSID) != 0) - -//debug macros -#ifdef WLED_DEBUG - #define DEBUG_PRINT(x) Serial.print (x) - #define DEBUG_PRINTLN(x) Serial.println (x) - #define DEBUG_PRINTF(x) Serial.printf (x) - unsigned long debugTime = 0; - int lastWifiState = 3; - unsigned long wifiStateChangedTime = 0; - int loops = 0; -#else - #define DEBUG_PRINT(x) - #define DEBUG_PRINTLN(x) - #define DEBUG_PRINTF(x) -#endif - -//filesystem -#ifndef WLED_DISABLE_FILESYSTEM - #include - #ifdef ARDUINO_ARCH_ESP32 - #include "SPIFFS.h" - #endif - #include "SPIFFSEditor.h" -#endif - - - -//turns all LEDs off and restarts ESP -void reset() -{ - briT = 0; - long dly = millis(); - while (millis() - dly < 250) - { - yield(); //enough time to send response to client - } - setAllLeds(); - DEBUG_PRINTLN("MODULE RESET"); - ESP.restart(); -} - - -//append new c string to temp buffer efficiently -bool oappend(const char* txt) -{ - uint16_t len = strlen(txt); - if (olen + len >= OMAX) return false; //buffer full - strcpy(obuf + olen, txt); - olen += len; - return true; -} - - -//append new number to temp buffer efficiently -bool oappendi(int i) -{ - char s[11]; - sprintf(s, "%ld", i); - return oappend(s); -} - - -//boot starts here void setup() { - wledInit(); + WLED::instance().setup(); } - -//main program loop void loop() { - handleIR(); //2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too - handleConnection(); - handleSerial(); - handleNotifications(); - handleTransitions(); -#ifdef WLED_ENABLE_DMX - handleDMX(); -#endif - userLoop(); - - yield(); - handleIO(); - handleIR(); - handleNetworkTime(); - handleAlexa(); - - handleOverlays(); - yield(); -#ifdef WLED_USE_ANALOG_LEDS - strip.setRgbwPwm(); -#endif - - if (doReboot) reset(); - - if (!realtimeMode) //block stuff if WARLS/Adalight is enabled - { - if (apActive) dnsServer.processNextRequest(); -#ifndef WLED_DISABLE_OTA - if (WLED_CONNECTED && aOtaEnabled) ArduinoOTA.handle(); -#endif - handleNightlight(); - yield(); - - handleHue(); - handleBlynk(); - - yield(); - if (!offMode) strip.service(); - } - yield(); -#ifdef ESP8266 - MDNS.update(); -#endif - if (millis() - lastMqttReconnectAttempt > 30000) initMqtt(); - - //DEBUG serial logging -#ifdef WLED_DEBUG - if (millis() - debugTime > 9999) - { - DEBUG_PRINTLN("---DEBUG INFO---"); - DEBUG_PRINT("Runtime: "); DEBUG_PRINTLN(millis()); - DEBUG_PRINT("Unix time: "); DEBUG_PRINTLN(now()); - DEBUG_PRINT("Free heap: "); DEBUG_PRINTLN(ESP.getFreeHeap()); - DEBUG_PRINT("Wifi state: "); DEBUG_PRINTLN(WiFi.status()); - if (WiFi.status() != lastWifiState) - { - wifiStateChangedTime = millis(); - } - lastWifiState = WiFi.status(); - DEBUG_PRINT("State time: "); DEBUG_PRINTLN(wifiStateChangedTime); - DEBUG_PRINT("NTP last sync: "); DEBUG_PRINTLN(ntpLastSyncTime); - DEBUG_PRINT("Client IP: "); DEBUG_PRINTLN(WiFi.localIP()); - DEBUG_PRINT("Loops/sec: "); DEBUG_PRINTLN(loops / 10); - loops = 0; - debugTime = millis(); - } - loops++; -#endif + WLED::instance().loop(); } diff --git a/wled00/wled00.vcxproj b/wled00/wled00.vcxproj index 2183ad26..e095b8f7 100644 --- a/wled00/wled00.vcxproj +++ b/wled00/wled00.vcxproj @@ -104,231 +104,103 @@ CppCode - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode -
- - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - - - CppCode - + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -350,6 +222,11 @@ + + + + + VisualMicroDebugger @@ -362,4 +239,4 @@ -
\ No newline at end of file + diff --git a/wled00/wled00.vcxproj.filters b/wled00/wled00.vcxproj.filters index e2c2b219..3ed232a9 100644 --- a/wled00/wled00.vcxproj.filters +++ b/wled00/wled00.vcxproj.filters @@ -13,29 +13,15 @@ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + {72fe60da-ba26-45b4-82c1-bdff809975da} + + + {8880888d-efea-4189-a25a-834b7b3bb756} + - - - - - - - - - - - - - - - - - - - - @@ -53,219 +39,348 @@ Header Files - + Header Files + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files\Dependencies + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + Header Files Header Files - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - Source Files Source Files + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files\Dependencies + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + Source Files diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino deleted file mode 100644 index 29ae0e7a..00000000 --- a/wled00/wled05_init.ino +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Setup code - */ - -void wledInit() -{ - EEPROM.begin(EEPSIZE); - ledCount = EEPROM.read(229) + ((EEPROM.read(398) << 8) & 0xFF00); - if (ledCount > MAX_LEDS || ledCount == 0) ledCount = 30; - - #ifdef ESP8266 - #if LEDPIN == 3 - if (ledCount > MAX_LEDS_DMA) ledCount = MAX_LEDS_DMA; //DMA method uses too much ram - #endif - #endif - Serial.begin(115200); - Serial.setTimeout(50); - DEBUG_PRINTLN(); - DEBUG_PRINT("---WLED "); DEBUG_PRINT(versionString); DEBUG_PRINT(" "); DEBUG_PRINT(VERSION); DEBUG_PRINTLN(" INIT---"); - #ifdef ARDUINO_ARCH_ESP32 - DEBUG_PRINT("esp32 "); DEBUG_PRINTLN(ESP.getSdkVersion()); - #else - DEBUG_PRINT("esp8266 "); DEBUG_PRINTLN(ESP.getCoreVersion()); - #endif - int heapPreAlloc = ESP.getFreeHeap(); - DEBUG_PRINT("heap "); - DEBUG_PRINTLN(ESP.getFreeHeap()); - - strip.init(EEPROM.read(372),ledCount,EEPROM.read(2204)); //init LEDs quickly - strip.setBrightness(0); - - DEBUG_PRINT("LEDs inited. heap usage ~"); - DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap()); - - #ifndef WLED_DISABLE_FILESYSTEM - #ifdef ARDUINO_ARCH_ESP32 - SPIFFS.begin(true); - #endif - SPIFFS.begin(); - #endif - - DEBUG_PRINTLN("Load EEPROM"); - loadSettingsFromEEPROM(true); - beginStrip(); - userSetup(); - if (strcmp(clientSSID,DEFAULT_CLIENT_SSID) == 0) showWelcomePage = true; - WiFi.persistent(false); - - if (macroBoot>0) applyMacro(macroBoot); - Serial.println("Ada"); - - //generate module IDs - escapedMac = WiFi.macAddress(); - escapedMac.replace(":", ""); - escapedMac.toLowerCase(); - if (strcmp(cmDNS,"x") == 0) //fill in unique mdns default - { - strcpy(cmDNS, "wled-"); - sprintf(cmDNS+5, "%*s", 6, escapedMac.c_str()+6); - } - if (mqttDeviceTopic[0] == 0) - { - strcpy(mqttDeviceTopic, "wled/"); - sprintf(mqttDeviceTopic+5, "%*s", 6, escapedMac.c_str()+6); - } - if (mqttClientID[0] == 0) - { - strcpy(mqttClientID, "WLED-"); - sprintf(mqttClientID+5, "%*s", 6, escapedMac.c_str()+6); - } - - strip.service(); - - #ifndef WLED_DISABLE_OTA - if (aOtaEnabled) - { - ArduinoOTA.onStart([]() { - #ifdef ESP8266 - wifi_set_sleep_type(NONE_SLEEP_T); - #endif - DEBUG_PRINTLN("Start ArduinoOTA"); - }); - if (strlen(cmDNS) > 0) ArduinoOTA.setHostname(cmDNS); - } - #endif - #ifdef WLED_ENABLE_DMX - dmx.init(512); // initialize with bus length - #endif - //HTTP server page init - initServer(); -} - - -void beginStrip() -{ - // Initialize NeoPixel Strip and button - strip.setShowCallback(handleOverlayDraw); - -#ifdef BTNPIN - pinMode(BTNPIN, INPUT_PULLUP); -#endif - - if (bootPreset > 0) applyPreset(bootPreset, turnOnAtBoot); - colorUpdated(NOTIFIER_CALL_MODE_INIT); - - //init relay pin - #if RLYPIN >= 0 - pinMode(RLYPIN, OUTPUT); - #if RLYMDE - digitalWrite(RLYPIN, bri); - #else - digitalWrite(RLYPIN, !bri); - #endif - #endif - - //disable button if it is "pressed" unintentionally -#ifdef BTNPIN - if(digitalRead(BTNPIN) == LOW) buttonEnabled = false; -#else - buttonEnabled = false; -#endif -} - - -void initAP(bool resetAP=false){ - if (apBehavior == AP_BEHAVIOR_BUTTON_ONLY && !resetAP) return; - - if (!apSSID[0] || resetAP) strcpy(apSSID, "WLED-AP"); - if (resetAP) strcpy(apPass,DEFAULT_AP_PASS); - DEBUG_PRINT("Opening access point "); - DEBUG_PRINTLN(apSSID); - WiFi.softAPConfig(IPAddress(4, 3, 2, 1), IPAddress(4, 3, 2, 1), IPAddress(255,255,255,0)); - WiFi.softAP(apSSID, apPass, apChannel, apHide); - - if (!apActive) //start captive portal if AP active - { - DEBUG_PRINTLN("Init AP interfaces"); - server.begin(); - if (udpPort > 0 && udpPort != ntpLocalPort) - { - udpConnected = notifierUdp.begin(udpPort); - } - if (udpRgbPort > 0 && udpRgbPort != ntpLocalPort && udpRgbPort != udpPort) - { - udpRgbConnected = rgbUdp.begin(udpRgbPort); - } - - dnsServer.setErrorReplyCode(DNSReplyCode::NoError); - dnsServer.start(53, "*", WiFi.softAPIP()); - } - apActive = true; -} - -void initConnection() -{ - WiFi.disconnect(); //close old connections - #ifdef ESP8266 - WiFi.setPhyMode(WIFI_PHY_MODE_11N); - #endif - - if (staticIP[0] != 0 && staticGateway[0] != 0) - { - WiFi.config(staticIP, staticGateway, staticSubnet, IPAddress(8,8,8,8)); - } else - { - WiFi.config(0U, 0U, 0U); - } - - lastReconnectAttempt = millis(); - - if (!WLED_WIFI_CONFIGURED) - { - DEBUG_PRINT("No connection configured. "); - if (!apActive) initAP(); //instantly go to ap mode - return; - } else if (!apActive) { - if (apBehavior == AP_BEHAVIOR_ALWAYS) - { - initAP(); - } else - { - DEBUG_PRINTLN("Access point disabled."); - WiFi.softAPdisconnect(true); - } - } - showWelcomePage = false; - - DEBUG_PRINT("Connecting to "); - DEBUG_PRINT(clientSSID); - DEBUG_PRINTLN("..."); - - #ifdef ESP8266 - WiFi.hostname(serverDescription); - #endif - - WiFi.begin(clientSSID, clientPass); - - #ifdef ARDUINO_ARCH_ESP32 - WiFi.setSleep(!noWifiSleep); - WiFi.setHostname(serverDescription); - #else - wifi_set_sleep_type((noWifiSleep) ? NONE_SLEEP_T : MODEM_SLEEP_T); - #endif -} - -void initInterfaces() { - DEBUG_PRINTLN("Init STA interfaces"); - - if (hueIP[0] == 0) - { - hueIP[0] = WiFi.localIP()[0]; - hueIP[1] = WiFi.localIP()[1]; - hueIP[2] = WiFi.localIP()[2]; - } - - //init Alexa hue emulation - if (alexaEnabled) alexaInit(); - - #ifndef WLED_DISABLE_OTA - if (aOtaEnabled) ArduinoOTA.begin(); - #endif - - strip.service(); - // Set up mDNS responder: - if (strlen(cmDNS) > 0) - { - if (!aOtaEnabled) MDNS.begin(cmDNS); - - DEBUG_PRINTLN("mDNS started"); - MDNS.addService("http", "tcp", 80); - MDNS.addService("wled", "tcp", 80); - MDNS.addServiceTxt("wled", "tcp", "mac", escapedMac.c_str()); - } - server.begin(); - - if (udpPort > 0 && udpPort != ntpLocalPort) - { - udpConnected = notifierUdp.begin(udpPort); - if (udpConnected && udpRgbPort != udpPort) udpRgbConnected = rgbUdp.begin(udpRgbPort); - } - if (ntpEnabled) ntpConnected = ntpUdp.begin(ntpLocalPort); - - initBlynk(blynkApiKey); - e131.begin((e131Multicast) ? E131_MULTICAST : E131_UNICAST , e131Universe, E131_MAX_UNIVERSE_COUNT); - reconnectHue(); - initMqtt(); - interfacesInited = true; - wasConnected = true; -} - -byte stacO = 0; -uint32_t lastHeap; -unsigned long heapTime = 0; - -void handleConnection() { - if (millis() < 2000 && (!WLED_WIFI_CONFIGURED || apBehavior == AP_BEHAVIOR_ALWAYS)) return; - if (lastReconnectAttempt == 0) initConnection(); - - //reconnect WiFi to clear stale allocations if heap gets too low - if (millis() - heapTime > 5000) - { - uint32_t heap = ESP.getFreeHeap(); - if (heap < 9000 && lastHeap < 9000) { - DEBUG_PRINT("Heap too low! "); - DEBUG_PRINTLN(heap); - forceReconnect = true; - } - lastHeap = heap; - heapTime = millis(); - } - - byte stac = 0; - if (apActive) { - #ifdef ESP8266 - stac = wifi_softap_get_station_num(); - #else - wifi_sta_list_t stationList; - esp_wifi_ap_get_sta_list(&stationList); - stac = stationList.num; - #endif - if (stac != stacO) - { - stacO = stac; - DEBUG_PRINT("Connected AP clients: "); - DEBUG_PRINTLN(stac); - if (!WLED_CONNECTED && WLED_WIFI_CONFIGURED) { //trying to connect, but not connected - if (stac) WiFi.disconnect(); //disable search so that AP can work - else initConnection(); //restart search - } - } - } - if (forceReconnect) { - DEBUG_PRINTLN("Forcing reconnect."); - initConnection(); - interfacesInited = false; - forceReconnect = false; - wasConnected = false; - return; - } - if (!WLED_CONNECTED) { - if (interfacesInited) { - DEBUG_PRINTLN("Disconnected!"); - interfacesInited = false; - initConnection(); - } - if (millis() - lastReconnectAttempt > ((stac) ? 300000 : 20000) && WLED_WIFI_CONFIGURED) initConnection(); - if (!apActive && millis() - lastReconnectAttempt > 12000 && (!wasConnected || apBehavior == AP_BEHAVIOR_NO_CONN)) initAP(); - } else if (!interfacesInited) { //newly connected - DEBUG_PRINTLN(""); - DEBUG_PRINT("Connected! IP address: "); - DEBUG_PRINTLN(WiFi.localIP()); - initInterfaces(); - userConnected(); - - //shut down AP - if (apBehavior != AP_BEHAVIOR_ALWAYS && apActive) - { - dnsServer.stop(); - WiFi.softAPdisconnect(true); - apActive = false; - DEBUG_PRINTLN("Access point disabled."); - } - } -} - -//by https://github.com/tzapu/WiFiManager/blob/master/WiFiManager.cpp -int getSignalQuality(int rssi) -{ - int quality = 0; - - if (rssi <= -100) { - quality = 0; - } else if (rssi >= -50) { - quality = 100; - } else { - quality = 2 * (rssi + 100); - } - return quality; -} diff --git a/wled00/wled11_ol.ino b/wled00/wled11_ol.ino deleted file mode 100644 index 93f7b96d..00000000 --- a/wled00/wled11_ol.ino +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Used to draw clock overlays over the strip - */ - -void initCronixie() -{ - if (overlayCurrent == 3 && !cronixieInit) - { - setCronixie(); - strip.getSegment(0).grouping = 10; //10 LEDs per digit - cronixieInit = true; - } else if (cronixieInit && overlayCurrent != 3) - { - strip.getSegment(0).grouping = 1; - cronixieInit = false; - } -} - - -void handleOverlays() -{ - if (millis() - overlayRefreshedTime > overlayRefreshMs) - { - initCronixie(); - updateLocalTime(); - checkTimers(); - checkCountdown(); - if (overlayCurrent == 3) _overlayCronixie();//Diamex cronixie clock kit - overlayRefreshedTime = millis(); - } -} - - -void _overlayAnalogClock() -{ - int overlaySize = overlayMax - overlayMin +1; - if (countdownMode) - { - _overlayAnalogCountdown(); return; - } - double hourP = ((double)(hour(local)%12))/12; - double minuteP = ((double)minute(local))/60; - hourP = hourP + minuteP/12; - double secondP = ((double)second(local))/60; - int hourPixel = floor(analogClock12pixel + overlaySize*hourP); - if (hourPixel > overlayMax) hourPixel = overlayMin -1 + hourPixel - overlayMax; - int minutePixel = floor(analogClock12pixel + overlaySize*minuteP); - if (minutePixel > overlayMax) minutePixel = overlayMin -1 + minutePixel - overlayMax; - int secondPixel = floor(analogClock12pixel + overlaySize*secondP); - if (secondPixel > overlayMax) secondPixel = overlayMin -1 + secondPixel - overlayMax; - if (analogClockSecondsTrail) - { - if (secondPixel < analogClock12pixel) - { - strip.setRange(analogClock12pixel, overlayMax, 0xFF0000); - strip.setRange(overlayMin, secondPixel, 0xFF0000); - } else - { - strip.setRange(analogClock12pixel, secondPixel, 0xFF0000); - } - } - if (analogClock5MinuteMarks) - { - int pix; - for (int i = 0; i <= 12; i++) - { - pix = analogClock12pixel + round((overlaySize / 12.0) *i); - if (pix > overlayMax) pix -= overlaySize; - strip.setPixelColor(pix, 0x00FFAA); - } - } - if (!analogClockSecondsTrail) strip.setPixelColor(secondPixel, 0xFF0000); - strip.setPixelColor(minutePixel, 0x00FF00); - strip.setPixelColor(hourPixel, 0x0000FF); - overlayRefreshMs = 998; -} - - -void _overlayAnalogCountdown() -{ - if (now() < countdownTime) - { - long diff = countdownTime - now(); - double pval = 60; - if (diff > 31557600L) //display in years if more than 365 days - { - pval = 315576000L; //10 years - } else if (diff > 2592000L) //display in months if more than a month - { - pval = 31557600L; //1 year - } else if (diff > 604800) //display in weeks if more than a week - { - pval = 2592000L; //1 month - } else if (diff > 86400) //display in days if more than 24 hours - { - pval = 604800; //1 week - } else if (diff > 3600) //display in hours if more than 60 minutes - { - pval = 86400; //1 day - } else if (diff > 60) //display in minutes if more than 60 seconds - { - pval = 3600; //1 hour - } - int overlaySize = overlayMax - overlayMin +1; - double perc = (pval-(double)diff)/pval; - if (perc > 1.0) perc = 1.0; - byte pixelCnt = perc*overlaySize; - if (analogClock12pixel + pixelCnt > overlayMax) - { - strip.setRange(analogClock12pixel, overlayMax, ((uint32_t)colSec[3] << 24)| ((uint32_t)colSec[0] << 16) | ((uint32_t)colSec[1] << 8) | colSec[2]); - strip.setRange(overlayMin, overlayMin +pixelCnt -(1+ overlayMax -analogClock12pixel), ((uint32_t)colSec[3] << 24)| ((uint32_t)colSec[0] << 16) | ((uint32_t)colSec[1] << 8) | colSec[2]); - } else - { - strip.setRange(analogClock12pixel, analogClock12pixel + pixelCnt, ((uint32_t)colSec[3] << 24)| ((uint32_t)colSec[0] << 16) | ((uint32_t)colSec[1] << 8) | colSec[2]); - } - } - overlayRefreshMs = 998; -} - - -void handleOverlayDraw() { - if (!overlayCurrent) return; - switch (overlayCurrent) - { - case 1: _overlayAnalogClock(); break; - case 3: _drawOverlayCronixie(); break; - } -} diff --git a/wled00/wled21_dmx.ino b/wled00/wled21_dmx.ino deleted file mode 100644 index e48b051a..00000000 --- a/wled00/wled21_dmx.ino +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Support for DMX via MAX485. - * Needs the espdmx library. You might have to change the output pin within the library. Sketchy, i know. - * https://github.com/Rickgg/ESP-Dmx - */ -#ifdef WLED_ENABLE_DMX - -void handleDMX() { - WS2812FX::Segment& seg = strip.getSegment(DMXSegment); - - if (!seg.isActive()) { - return; - } - - // TODO: calculate brightness manually if no shutter channel is set - uint8_t brightness = strip.getBrightness(); - - // Calculate the start LED depending on segment and DMXStartLED config - uint8_t startLed = seg.start + DMXStartLED; - - for (int i = startLed; i < seg.stop; i++) { - uint32_t in = strip.getPixelColor(i); // time to get the colors for the individual fixtures as suggested by AirCookie at issue #462 - byte w = in >> 24 & 0xFF; - byte r = in >> 16 & 0xFF; - byte g = in >> 8 & 0xFF; - byte b = in & 0xFF; - - int DMXFixtureStart = DMXStart + (DMXGap * (i - startLed)); - for (int j = 0; j < DMXChannels; j++) { - int DMXAddr = DMXFixtureStart + j; - switch (DMXFixtureMap[j]) { - case 0: // Set this channel to 0. Good way to tell strobe- and fade-functions to fuck right off. - dmx.write(DMXAddr, 0); - break; - case 1: // Red - dmx.write(DMXAddr, r); - break; - case 2: // Green - dmx.write(DMXAddr, g); - break; - case 3: // Blue - dmx.write(DMXAddr, b); - break; - case 4: // White - dmx.write(DMXAddr, w); - break; - case 5: // Shutter channel. Controls the brightness. - dmx.write(DMXAddr, brightness); - break; - case 6:// Sets this channel to 255. Like 0, but more wholesome. - dmx.write(DMXAddr, 255); - break; - } - } - } - - dmx.update(); // update the DMX bus -} - -#else -void handleDMX() {} -#endif diff --git a/wled00/wled01_eeprom.ino b/wled00/wled_eeprom.cpp similarity index 97% rename from wled00/wled01_eeprom.ino rename to wled00/wled_eeprom.cpp index c741d4e3..b06371ee 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled_eeprom.cpp @@ -1,10 +1,11 @@ +#include +#include "wled.h" + /* * Methods to handle saving and loading to non-volatile memory * EEPROM Map: https://github.com/Aircoookie/WLED/wiki/EEPROM-Map */ -#define EEPSIZE 2560 //Maximum is 4096 - //eeprom Version code, enables default settings instead of 0 init on update #define EEPVER 18 //0 -> old version, default @@ -270,9 +271,6 @@ void saveSettingsToEEPROM() for (int i=0; i<15; i++) { EEPROM.write(2535+i, DMXFixtureMap[i]); } // last used: 2549. maybe leave a few bytes for future expansion and go on with 2600 kthxbye. - - EEPROM.write(2550, DMXSegment); - EEPROM.write(2551, DMXStartLED); #endif commit(); @@ -554,12 +552,11 @@ void loadSettingsFromEEPROM(bool first) DMXChannels = EEPROM.read(2530); DMXGap = EEPROM.read(2531) + ((EEPROM.read(2532) << 8) & 0xFF00); DMXStart = EEPROM.read(2533) + ((EEPROM.read(2534) << 8) & 0xFF00); - DMXSegment = EEPROM.read(2550); - DMXStartLED = EEPROM.read(2551); for (int i=0;i<15;i++) { DMXFixtureMap[i] = EEPROM.read(2535+i); - } //last used: 2549. maybe leave a few bytes for future expansion and go on with 2600 kthxbye. + } //last used: 2549 + EEPROM.write(2550, DMXStartLED); #endif //user MOD memory @@ -596,7 +593,7 @@ void savedToPresets() } } -bool applyPreset(byte index, bool loadBri = true) +bool applyPreset(byte index, bool loadBri) { if (index == 255 || index == 0) { @@ -634,7 +631,7 @@ bool applyPreset(byte index, bool loadBri = true) return true; } -void savePreset(byte index, bool persist = true) +void savePreset(byte index, bool persist) { if (index > 16) return; if (index < 1) {saveSettingsToEEPROM();return;} @@ -702,7 +699,7 @@ void applyMacro(byte index) } -void saveMacro(byte index, String mc, bool persist = true) //only commit on single save, not in settings +void saveMacro(byte index, String mc, bool persist) //only commit on single save, not in settings { index-=1; if (index > 15) return; diff --git a/wled00/wled04_file.ino b/wled00/wled_serial.cpp similarity index 55% rename from wled00/wled04_file.ino rename to wled00/wled_serial.cpp index de752328..a472729d 100644 --- a/wled00/wled04_file.ino +++ b/wled00/wled_serial.cpp @@ -1,6 +1,9 @@ +#include "wled.h" + /* - * Utility for SPIFFS filesystem & Serial console + * Adalight handler */ + enum class AdaState { Header_A, Header_d, @@ -78,44 +81,3 @@ void handleSerial() } #endif } - - -#if !defined WLED_DISABLE_FILESYSTEM && defined WLED_ENABLE_FS_SERVING -//Un-comment any file types you need -String getContentType(AsyncWebServerRequest* request, String filename){ - if(request->hasArg("download")) return "application/octet-stream"; - else if(filename.endsWith(".htm")) return "text/html"; - else if(filename.endsWith(".html")) return "text/html"; -// else if(filename.endsWith(".css")) return "text/css"; -// else if(filename.endsWith(".js")) return "application/javascript"; - else if(filename.endsWith(".json")) return "application/json"; - else if(filename.endsWith(".png")) return "image/png"; -// else if(filename.endsWith(".gif")) return "image/gif"; - else if(filename.endsWith(".jpg")) return "image/jpeg"; - else if(filename.endsWith(".ico")) return "image/x-icon"; -// else if(filename.endsWith(".xml")) return "text/xml"; -// else if(filename.endsWith(".pdf")) return "application/x-pdf"; -// else if(filename.endsWith(".zip")) return "application/x-zip"; -// else if(filename.endsWith(".gz")) return "application/x-gzip"; - return "text/plain"; -} - -bool handleFileRead(AsyncWebServerRequest* request, String path){ - DEBUG_PRINTLN("FileRead: " + path); - if(path.endsWith("/")) path += "index.htm"; - String contentType = getContentType(request, path); - String pathWithGz = path + ".gz"; - if(SPIFFS.exists(pathWithGz)){ - request->send(SPIFFS, pathWithGz, contentType); - return true; - } - if(SPIFFS.exists(path)) { - request->send(SPIFFS, path, contentType); - return true; - } - return false; -} - -#else -bool handleFileRead(AsyncWebServerRequest*, String path){return false;} -#endif diff --git a/wled00/wled18_server.ino b/wled00/wled_server.cpp similarity index 99% rename from wled00/wled18_server.ino rename to wled00/wled_server.cpp index f11a64be..cd623bc4 100644 --- a/wled00/wled18_server.ino +++ b/wled00/wled_server.cpp @@ -1,5 +1,7 @@ +#include "wled.h" + /* - * Server page definitions + * Integrated HTTP web server page declarations */ //Is this an IP? @@ -303,7 +305,7 @@ String msgProcessor(const String& var) } -void serveMessage(AsyncWebServerRequest* request, uint16_t code, String headl, String subl="", byte optionT=255) +void serveMessage(AsyncWebServerRequest* request, uint16_t code, String headl, String subl, byte optionT) { messageHead = headl; messageSub = subl; diff --git a/wled00/wled02_xml.ino b/wled00/xml.cpp similarity index 99% rename from wled00/wled02_xml.ino rename to wled00/xml.cpp index a05c1e97..1e14ea7d 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/xml.cpp @@ -1,9 +1,11 @@ +#include "wled.h" + /* * Sending XML status files to client */ //build XML response to HTTP /win API request -char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr) +char* XML_response(AsyncWebServerRequest *request, char* dest) { char sbuf[(dest == nullptr)?1024:1]; //allocate local buffer if none passed obuf = (dest == nullptr)? sbuf:dest; @@ -455,7 +457,6 @@ void getSettingsJS(byte subPage, char* dest) sappend('v',"CN",DMXChannels); sappend('v',"CG",DMXGap); sappend('v',"CS",DMXStart); - sappend('v',"SEG",DMXSegment); sappend('v',"SL",DMXStartLED); sappend('i',"CH1",DMXFixtureMap[0]); From 3e9b44d19384ac98fd914f76c2882217fa495530 Mon Sep 17 00:00:00 2001 From: Ament Martin Date: Fri, 10 Apr 2020 21:33:16 +0200 Subject: [PATCH 215/232] Added support for BW-LT11 --- platformio_override.ini.example | 4 +++- wled00/NpbWrapper.h | 14 ++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/platformio_override.ini.example b/platformio_override.ini.example index 46119f15..ba829aa1 100644 --- a/platformio_override.ini.example +++ b/platformio_override.ini.example @@ -28,7 +28,9 @@ build_flags = ${common.build_flags_esp8266} ; to drive analog LED strips (aka 5050), uncomment the following ; PWM pins 5,12,13,15 are used with Magic Home LED Controller (default) ; -D WLED_USE_ANALOG_LEDS -; for the H801 controler (PINs 15,13,12,14 (W2 = 04)) uncomment this +; for the H801 controller (PINs 15,13,12,14 (W2 = 04)) uncomment this ; -D WLED_USE_H801 +; for the BW-LT11 controller (PINs 12,4,14,5 ) uncomment this +; -D WLED_USE_BWLT11 ; and to enable channel 5 for RGBW-CT led strips this ; -D WLED_USE_5CH_LEDS diff --git a/wled00/NpbWrapper.h b/wled00/NpbWrapper.h index 6e58b719..1f360bfc 100644 --- a/wled00/NpbWrapper.h +++ b/wled00/NpbWrapper.h @@ -55,12 +55,18 @@ #undef BTNPIN #undef IR_PIN #define IR_PIN 0 //infrared pin (-1 to disable) MagicHome: 4, H801 Wifi: 0 + #elif defined(WLED_USE_BWLT11) + //PWM pins - to use with BW-LT11 + #define RPIN 12 //R pin for analog LED strip + #define GPIN 4 //G pin for analog LED strip + #define BPIN 14 //B pin for analog LED strip + #define WPIN 5 //W pin for analog LED strip #else //PWM pins - PINs 5,12,13,15 are used with Magic Home LED Controller - #define RPIN 5 //R pin for analog LED strip - #define GPIN 12 //G pin for analog LED strip - #define BPIN 15 //B pin for analog LED strip - #define WPIN 13 //W pin for analog LED strip + #define RPIN 5 //R pin for analog LED strip + #define GPIN 12 //G pin for analog LED strip + #define BPIN 15 //B pin for analog LED strip + #define WPIN 13 //W pin for analog LED strip #endif #undef RLYPIN #define RLYPIN -1 //disable as pin 12 is used by analog LEDs From e12757dbb94d498acc98c6440e28398cc0446f88 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Mon, 13 Apr 2020 00:42:27 +0200 Subject: [PATCH 216/232] Add Art-Net support (#417) --- CHANGELOG.md | 6 +- platformio.ini | 10 + wled00/const.h | 1 + wled00/data/settings_sync.htm | 240 ++++++++---------- wled00/e131.cpp | 96 ++++--- wled00/fcn_declare.h | 14 +- wled00/html_settings.h | 108 ++++---- wled00/set.cpp | 6 +- wled00/src/dependencies/e131/ESPAsyncE131.cpp | 57 +++-- wled00/src/dependencies/e131/ESPAsyncE131.h | 52 ++-- wled00/udp.cpp | 6 +- wled00/wled.cpp | 2 +- wled00/wled.h | 3 +- wled00/wled_eeprom.cpp | 11 +- wled00/wled_serial.cpp | 2 +- wled00/xml.cpp | 6 +- 16 files changed, 325 insertions(+), 295 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31c22f52..1277b32e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ### Development versions after 0.9.1 release +#### Build 2004120 + +- Added Art-Net support +- Added OTA platform to platformio.ini #### Build 2004100 @@ -23,7 +27,7 @@ #### Build 2003262 -- Fixed compilation for Analog LEDs +- Fixed compilation for Analog LEDs - Fixed sync settings network port fields too small #### Build 2003261 diff --git a/platformio.ini b/platformio.ini index 91bdd867..49517613 100644 --- a/platformio.ini +++ b/platformio.ini @@ -30,6 +30,7 @@ default_envs = d1_mini, esp01, esp01_1m_ota, esp32dev ; default_envs = d1_mini ; default_envs = heltec_wifi_kit_8 ; default_envs = d1_mini_debug +; default_envs = d1_mini_ota ; default_envs = esp32dev ; default_envs = esp8285_4CH_MagicHome ; default_envs = esp8285_4CH_H801 @@ -241,6 +242,15 @@ platform = ${common.platform_latest} board_build.ldscript = ${common.ldscript_4m1m} build_flags = ${common.build_flags_esp8266} ${common.debug_flags} +[env:d1_mini_ota] +board = d1_mini +upload_protocol = espota +# exchange for your WLED IP +upload_port = "10.10.1.27" +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} + # ------------------------------------------------------------------------------ # custom board configurations # ------------------------------------------------------------------------------ diff --git a/wled00/const.h b/wled00/const.h index 173d7e7d..5cd4b02f 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -43,6 +43,7 @@ #define REALTIME_MODE_HYPERION 3 #define REALTIME_MODE_E131 4 #define REALTIME_MODE_ADALIGHT 5 +#define REALTIME_MODE_ARTNET 6 //E1.31 DMX modes #define DMX_MODE_DISABLED 0 //not used diff --git a/wled00/data/settings_sync.htm b/wled00/data/settings_sync.htm index 5cf4b608..91fe8992 100644 --- a/wled00/data/settings_sync.htm +++ b/wled00/data/settings_sync.htm @@ -1,141 +1,105 @@ - - - - Sync Settings - - - - -
-
-
-

Sync setup

-

Button setup

- On/Off button enabled:
- Infrared receiver enabled:
- IR info -

WLED Sync UDP Broadcast

- UDP Port:
- Receive Brightness, Color, and Effects
- Send notifications on direct change:
- Send notifications on button press:
- Send Alexa notifications:
- Send Philips Hue change notifications:
- Send Macro notifications:
- Send notifications twice: -

Realtime

- Receive UDP realtime:

- E1.31 (sACN)
- Skip out-of-sequence packets (freeze instead of flicker):
- Multicast mode:
- E1.31 start universe:
- Reboot required. Check out LedFx!

- DMX start address:
- DMX mode: disabled
- Single RGB (3 Channels for all LEDs: Red Green Blue)
- Single DRGB (4 Channels for all LEDs: Dimmer Red Green Blue)
- Effect (11 Channels parametrizing Effects: Dimmer Effect Speed Intensity Palette PriRed PriGreen PriBlue SecRed SecGreen SecBlue)
- Multiple RGB (3 Channels for each LED: Red Green Blue)
- Multiple DRGB (1+3 Channels for each LED: Dimmer Red1 Green1 Blue1 Red2 Green2 Blue2...)
- Reboot required. Check out LedFx!

- Timeout: ms
- Force max brightness:
- Disable realtime gamma correction:
- Realtime LED offset: -

Alexa Voice Assistant

- Emulate Alexa device:
- Alexa invocation name: -

Blynk

- Blynk, MQTT and Hue sync all connect to external hosts!
- This may impact the responsiveness of the ESP8266.

- For best results, only use one of these services at a time.
- (alternatively, connect a second ESP to them and use the UDP sync)

- Device Auth token:
- Clear the token field to disable. Setup info -

MQTT

- Enable MQTT:
- Broker:
- Port:
- The MQTT credentials are sent over an unsecured connection.
- Never use the MQTT password for another service!

- Username:
- Password:
- Client ID:
- Device Topic:
- Group Topic:
- MQTT info -

Philips Hue

- You can find the bridge IP and the light number in the 'About' section of the hue app.
- Poll Hue light every ms:
- Then, receive On/Off, Brightness, and Color
- Hue Bridge IP:
- . - . - . -
- Press the pushlink button on the bridge, after that save this page!
- (when first connecting)
- Hue status: Internal ESP Error!
- -
+Sync Settings + + + +
+
+
+

Sync setup

+

Button setup

+On/Off button enabled:
+Infrared remote: +
+IR info +

WLED Broadcast

+UDP Port:
+Receive Brightness, Color, and Effects
+Send notifications on direct change:
+Send notifications on button press:
+Send Alexa notifications:
+Send Philips Hue change notifications:
+Send Macro notifications:
+Send notifications twice: +

Realtime

+Receive UDP realtime:

+Network DMX input
+Type: +
+
Port:
+Multicast:
+Start universe:
+Reboot required. Check out LedFx!
+Skip out-of-sequence packets:
+DMX start address:
+DMX mode: +
+E1.31 info
+Timeout: ms
+Force max brightness:
+Disable realtime gamma correction:
+Realtime LED offset: +

Alexa Voice Assistant

+Emulate Alexa device:
+Alexa invocation name: +

Blynk

+Blynk, MQTT and Hue sync all connect to external hosts!
+This may impact the responsiveness of the ESP8266.

+For best results, only use one of these services at a time.
+(alternatively, connect a second ESP to them and use the UDP sync)

+Device Auth token:
+Clear the token field to disable. Setup info +

MQTT

+Enable MQTT:
+Broker: +Port:
+The MQTT credentials are sent over an unsecured connection.
+Never use the MQTT password for another service!

+Username:
+Password:
+Client ID:
+Device Topic:
+Group Topic:
+Reboot required to apply changes. MQTT info +

Philips Hue

+You can find the bridge IP and the light number in the 'About' section of the hue app.
+Poll Hue light every ms:
+Then, receive On/Off, Brightness, and Color
+Hue Bridge IP:
+ . + . + . +
+Press the pushlink button on the bridge, after that save this page!
+(when first connecting)
+Hue status: Disabled in this build
+ +
\ No newline at end of file diff --git a/wled00/e131.cpp b/wled00/e131.cpp index 2034f84d..6e774194 100644 --- a/wled00/e131.cpp +++ b/wled00/e131.cpp @@ -4,29 +4,45 @@ * E1.31 handler */ -void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ +void handleE131Packet(e131_packet_t* p, IPAddress clientIP, bool isArtnet){ //E1.31 protocol support - uint16_t uni = htons(p->universe); - uint8_t previousUniverses = uni - e131Universe; - uint16_t possibleLEDsInCurrentUniverse; - uint16_t dmxChannels = htons(p->property_value_count) -1; + uint16_t uni = 0, dmxChannels = 0; + uint8_t* e131_data = nullptr; + uint8_t seq = 0, mde = REALTIME_MODE_E131; + + if (isArtnet) + { + uni = p->art_universe; + dmxChannels = htons(p->art_length); + e131_data = p->art_data; + seq = p->art_sequence_number; + mde = REALTIME_MODE_ARTNET; + } else { + uni = htons(p->universe); + dmxChannels = htons(p->property_value_count) -1; + e131_data = p->property_values; + seq = p->sequence_number; + } // only listen for universes we're handling & allocated memory if (uni >= (e131Universe + E131_MAX_UNIVERSE_COUNT)) return; + uint8_t previousUniverses = uni - e131Universe; + uint16_t possibleLEDsInCurrentUniverse; + if (e131SkipOutOfSequence) - if (p->sequence_number < e131LastSequenceNumber[uni-e131Universe] && p->sequence_number > 20 && e131LastSequenceNumber[uni-e131Universe] < 250){ + if (seq < e131LastSequenceNumber[uni-e131Universe] && seq > 20 && e131LastSequenceNumber[uni-e131Universe] < 250){ DEBUG_PRINT("skipping E1.31 frame (last seq="); DEBUG_PRINT(e131LastSequenceNumber[uni-e131Universe]); DEBUG_PRINT(", current seq="); - DEBUG_PRINT(p->sequence_number); + DEBUG_PRINT(seq); DEBUG_PRINT(", universe="); DEBUG_PRINT(uni); DEBUG_PRINTLN(")"); return; } - e131LastSequenceNumber[uni-e131Universe] = p->sequence_number; + e131LastSequenceNumber[uni-e131Universe] = seq; // update status info realtimeIP = clientIP; @@ -39,46 +55,46 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ case DMX_MODE_SINGLE_RGB: if (uni != e131Universe) return; if (dmxChannels-DMXAddress+1 < 3) return; - arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); + realtimeLock(realtimeTimeoutMs, mde); for (uint16_t i = 0; i < ledCount; i++) - setRealtimePixel(i, p->property_values[DMXAddress+0], p->property_values[DMXAddress+1], p->property_values[DMXAddress+2], 0); + setRealtimePixel(i, e131_data[DMXAddress+0], e131_data[DMXAddress+1], e131_data[DMXAddress+2], 0); break; case DMX_MODE_SINGLE_DRGB: if (uni != e131Universe) return; if (dmxChannels-DMXAddress+1 < 4) return; - arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); - if (DMXOldDimmer != p->property_values[DMXAddress+0]) { - DMXOldDimmer = p->property_values[DMXAddress+0]; - bri = p->property_values[DMXAddress+0]; + realtimeLock(realtimeTimeoutMs, mde); + if (DMXOldDimmer != e131_data[DMXAddress+0]) { + DMXOldDimmer = e131_data[DMXAddress+0]; + bri = e131_data[DMXAddress+0]; strip.setBrightness(bri); } for (uint16_t i = 0; i < ledCount; i++) - setRealtimePixel(i, p->property_values[DMXAddress+1], p->property_values[DMXAddress+2], p->property_values[DMXAddress+3], 0); + setRealtimePixel(i, e131_data[DMXAddress+1], e131_data[DMXAddress+2], e131_data[DMXAddress+3], 0); break; case DMX_MODE_EFFECT: if (uni != e131Universe) return; if (dmxChannels-DMXAddress+1 < 11) return; - if (DMXOldDimmer != p->property_values[DMXAddress+0]) { - DMXOldDimmer = p->property_values[DMXAddress+0]; - bri = p->property_values[DMXAddress+0]; + if (DMXOldDimmer != e131_data[DMXAddress+0]) { + DMXOldDimmer = e131_data[DMXAddress+0]; + bri = e131_data[DMXAddress+0]; } - if (p->property_values[DMXAddress+1] < MODE_COUNT) - effectCurrent = p->property_values[DMXAddress+ 1]; - effectSpeed = p->property_values[DMXAddress+ 2]; // flickers - effectIntensity = p->property_values[DMXAddress+ 3]; - effectPalette = p->property_values[DMXAddress+ 4]; - col[0] = p->property_values[DMXAddress+ 5]; - col[1] = p->property_values[DMXAddress+ 6]; - col[2] = p->property_values[DMXAddress+ 7]; - colSec[0] = p->property_values[DMXAddress+ 8]; - colSec[1] = p->property_values[DMXAddress+ 9]; - colSec[2] = p->property_values[DMXAddress+10]; + if (e131_data[DMXAddress+1] < MODE_COUNT) + effectCurrent = e131_data[DMXAddress+ 1]; + effectSpeed = e131_data[DMXAddress+ 2]; // flickers + effectIntensity = e131_data[DMXAddress+ 3]; + effectPalette = e131_data[DMXAddress+ 4]; + col[0] = e131_data[DMXAddress+ 5]; + col[1] = e131_data[DMXAddress+ 6]; + col[2] = e131_data[DMXAddress+ 7]; + colSec[0] = e131_data[DMXAddress+ 8]; + colSec[1] = e131_data[DMXAddress+ 9]; + colSec[2] = e131_data[DMXAddress+10]; if (dmxChannels-DMXAddress+1 > 11) { - col[3] = p->property_values[DMXAddress+11]; //white - colSec[3] = p->property_values[DMXAddress+12]; + col[3] = e131_data[DMXAddress+11]; //white + colSec[3] = e131_data[DMXAddress+12]; } transitionDelayTemp = 0; // act fast colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); // don't send UDP @@ -86,13 +102,13 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ break; case DMX_MODE_MULTIPLE_RGB: - arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); + realtimeLock(realtimeTimeoutMs, mde); if (previousUniverses == 0) { // first universe of this fixture possibleLEDsInCurrentUniverse = (dmxChannels - DMXAddress + 1) / 3; for (uint16_t i = 0; i < ledCount; i++) { if (i >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) - setRealtimePixel(i, p->property_values[DMXAddress+i*3+0], p->property_values[DMXAddress+i*3+1], p->property_values[DMXAddress+i*3+2], 0); + setRealtimePixel(i, e131_data[DMXAddress+i*3+0], e131_data[DMXAddress+i*3+1], e131_data[DMXAddress+i*3+2], 0); } } else if (previousUniverses > 0 && uni < (e131Universe + E131_MAX_UNIVERSE_COUNT)) { // additional universe(s) of this fixture @@ -102,24 +118,24 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ for (uint16_t i = numberOfLEDsInPreviousUniverses; i < ledCount; i++) { uint8_t j = i - numberOfLEDsInPreviousUniverses; if (j >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) - setRealtimePixel(i, p->property_values[j*3+1], p->property_values[j*3+2], p->property_values[j*3+3], 0); + setRealtimePixel(i, e131_data[j*3+1], e131_data[j*3+2], e131_data[j*3+3], 0); } } break; case DMX_MODE_MULTIPLE_DRGB: - arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131); + realtimeLock(realtimeTimeoutMs, mde); if (previousUniverses == 0) { // first universe of this fixture - if (DMXOldDimmer != p->property_values[DMXAddress+0]) { - DMXOldDimmer = p->property_values[DMXAddress+0]; - bri = p->property_values[DMXAddress+0]; + if (DMXOldDimmer != e131_data[DMXAddress+0]) { + DMXOldDimmer = e131_data[DMXAddress+0]; + bri = e131_data[DMXAddress+0]; strip.setBrightness(bri); } possibleLEDsInCurrentUniverse = (dmxChannels - DMXAddress) / 3; for (uint16_t i = 0; i < ledCount; i++) { if (i >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) - setRealtimePixel(i, p->property_values[DMXAddress+i*3+1], p->property_values[DMXAddress+i*3+2], p->property_values[DMXAddress+i*3+3], 0); + setRealtimePixel(i, e131_data[DMXAddress+i*3+1], e131_data[DMXAddress+i*3+2], e131_data[DMXAddress+i*3+3], 0); } } else if (previousUniverses > 0 && uni < (e131Universe + E131_MAX_UNIVERSE_COUNT)) { // additional universe(s) of this fixture @@ -129,7 +145,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ for (uint16_t i = numberOfLEDsInPreviousUniverses; i < ledCount; i++) { uint8_t j = i - numberOfLEDsInPreviousUniverses; if (j >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) - setRealtimePixel(i, p->property_values[j*3+1], p->property_values[j*3+2], p->property_values[j*3+3], 0); + setRealtimePixel(i, e131_data[j*3+1], e131_data[j*3+2], e131_data[j*3+3], 0); } } break; diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 7261efcb..2d8401bc 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -42,7 +42,7 @@ void initDMX(); void handleDMX(); //e131.cpp -void handleE131Packet(e131_packet_t* p, IPAddress clientIP); +void handleE131Packet(e131_packet_t* p, IPAddress clientIP, bool isArtnet); //file.cpp bool handleFileRead(AsyncWebServerRequest*, String path); @@ -103,12 +103,6 @@ void handleNightlight(); bool initMqtt(); void publishMqtt(); -//notify.cpp -void notify(byte callMode, bool followUp=false); -void arlsLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC); -void handleNotifications(); -void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w); - //ntp.cpp void handleNetworkTime(); void sendNTPPacket(); @@ -140,6 +134,12 @@ bool handleSet(AsyncWebServerRequest *request, const String& req); int getNumVal(const String* req, uint16_t pos); bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte maxv=255); +//udp.cpp +void notify(byte callMode, bool followUp=false); +void realtimeLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC); +void handleNotifications(); +void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w); + //usermod.cpp void userSetup(); void userConnected(); diff --git a/wled00/html_settings.h b/wled00/html_settings.h index fa019533..009dda50 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -252,16 +252,16 @@ Sync button toggles both send and receive:
//sync settings const char PAGE_settings_sync[] PROGMEM = R"=====( -Sync Settings - -

WLED Software Update

Installed version: 0.9.1
Download the latest binary:

)====="; +

WLED Software Update

Installed version: 0.10.0p
Download the latest binary:

)====="; //new user welcome page diff --git a/wled00/html_settings.h b/wled00/html_settings.h index b44f2942..1e6db0d0 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -470,7 +470,7 @@ HTTP traffic is unencrypted. An attacker in the same network can intercept form
Enable ArduinoOTA:

About

-WLED version 0.9.1

+WLED version 0.10.0p

Contributors, dependencies and special thanks
A huge thank you to everyone who helped me create WLED!

(c) 2016-2020 Christian Schwinne
diff --git a/wled00/html_ui.h b/wled00/html_ui.h index 45cd9da9..981e3845 100644 --- a/wled00/html_ui.h +++ b/wled00/html_ui.h @@ -10,1500 +10,1688 @@ * 4. update length value */ -const uint16_t PAGE_index_L = 23873; //length of the binary payload +const uint16_t PAGE_index_L = 26884; //length of the binary payload const uint8_t PAGE_index[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x08, 0x6d, 0xb9, 0x4d, 0x5e, 0x00, 0x03, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2e, - 0x68, 0x74, 0x6d, 0x00, 0xcc, 0x7d, 0x6b, 0x73, 0xda, 0xc8, 0xb6, 0xe8, 0xf7, 0xa9, 0xca, 0x7f, - 0x60, 0x3c, 0x75, 0x76, 0x92, 0xed, 0x60, 0x49, 0xbc, 0x8c, 0x93, 0x38, 0x67, 0xb7, 0x84, 0x00, - 0x01, 0x02, 0x04, 0x48, 0x80, 0x4f, 0xed, 0x0f, 0x42, 0x08, 0x21, 0x10, 0x08, 0x24, 0x81, 0x80, - 0xa9, 0xb9, 0xbf, 0xfd, 0xae, 0x6e, 0x3d, 0x10, 0x2f, 0xdb, 0x99, 0x9c, 0x5d, 0x75, 0x98, 0x31, - 0xd0, 0xaf, 0xd5, 0xab, 0xd7, 0x7b, 0x2d, 0xb5, 0x9d, 0xef, 0xbf, 0x97, 0x5a, 0x5c, 0x6f, 0xd8, - 0xe6, 0x53, 0x53, 0x6f, 0x61, 0xfd, 0xf8, 0xf0, 0xdb, 0xf7, 0xf8, 0x53, 0x57, 0xc7, 0xf8, 0x73, - 0xa1, 0x7b, 0x6a, 0x6a, 0xa9, 0x2e, 0xf4, 0xe7, 0xbb, 0xad, 0xa9, 0xfb, 0x2b, 0xdb, 0xf1, 0xee, - 0x52, 0x9a, 0xbd, 0xf4, 0xf4, 0xa5, 0xf7, 0x7c, 0xe7, 0x9b, 0x63, 0x6f, 0xfa, 0x3c, 0xd6, 0xb7, - 0xa6, 0xa6, 0xa7, 0x49, 0xe3, 0x4b, 0xca, 0x5c, 0x9a, 0x9e, 0xa9, 0x5a, 0x69, 0x57, 0x53, 0x2d, - 0xfd, 0x99, 0xf9, 0x92, 0x5a, 0x40, 0xcf, 0x62, 0xb3, 0x88, 0x3a, 0xee, 0x62, 0xb0, 0xda, 0x54, - 0x75, 0x5c, 0x1d, 0xc0, 0x6c, 0xbc, 0x49, 0xba, 0x78, 0x77, 0xb6, 0x9d, 0x37, 0xd5, 0x17, 0x7a, - 0x5a, 0xb3, 0x2d, 0xdb, 0x49, 0xec, 0xf8, 0x47, 0x86, 0xbc, 0x12, 0x40, 0xa2, 0x91, 0xbd, 0xee, - 0xde, 0x85, 0x4b, 0xd5, 0xd5, 0xca, 0xd2, 0xd3, 0x0b, 0x7b, 0x64, 0xc2, 0x87, 0xaf, 0x8f, 0xd2, - 0xd0, 0x91, 0xd6, 0xd4, 0x95, 0x3a, 0xb2, 0x74, 0xb2, 0xd2, 0x32, 0x97, 0xf3, 0x94, 0xa3, 0x5b, - 0xcf, 0x77, 0xee, 0x14, 0x4e, 0xa4, 0x6d, 0xbc, 0x94, 0x09, 0x80, 0xee, 0x52, 0x53, 0x47, 0x9f, - 0x3c, 0xdf, 0x8d, 0x55, 0x4f, 0xfd, 0x6a, 0x2e, 0x54, 0x43, 0xa7, 0x76, 0x69, 0x3c, 0xf0, 0x6d, - 0xa4, 0xba, 0x7a, 0x21, 0xf7, 0x05, 0x21, 0xc4, 0x22, 0xc4, 0x23, 0x1e, 0xde, 0xf1, 0x67, 0x05, - 0x71, 0x15, 0xfc, 0xad, 0x6c, 0xc0, 0x9b, 0x60, 0x49, 0xbd, 0xb9, 0xd6, 0xe4, 0xa6, 0x76, 0x1d, - 0xf7, 0x95, 0x64, 0x4b, 0xe8, 0x94, 0x05, 0xfc, 0x55, 0x0a, 0x66, 0x1b, 0x64, 0x6e, 0x95, 0x6a, - 0x53, 0x43, 0xdc, 0xc3, 0x33, 0xb5, 0x0e, 0x5f, 0x96, 0x5b, 0x02, 0x33, 0x83, 0x2e, 0xaa, 0xed, - 0xb7, 0x76, 0x46, 0xb3, 0xa2, 0x23, 0x59, 0xdc, 0xf1, 0x4f, 0x95, 0x82, 0x26, 0x71, 0xf5, 0x52, - 0x1f, 0x4d, 0x57, 0xa8, 0xf4, 0x92, 0x99, 0x14, 0xdb, 0xe2, 0x8c, 0xed, 0x66, 0xa5, 0x3e, 0x5d, - 0x94, 0xea, 0x19, 0xba, 0xae, 0xbe, 0x70, 0x19, 0x63, 0xc2, 0x3d, 0x4d, 0xb9, 0xe5, 0xda, 0xde, - 0xd8, 0x4d, 0x03, 0x75, 0x8c, 0xe1, 0xe3, 0x41, 0xdc, 0xa1, 0x7d, 0xd3, 0x92, 0xc7, 0x52, 0xd5, - 0x1a, 0x98, 0xc8, 0x6a, 0x65, 0xc4, 0x12, 0x2a, 0x15, 0xb2, 0xbc, 0xb2, 0x6e, 0x56, 0x91, 0x4e, - 0x23, 0x82, 0x88, 0x55, 0xee, 0xcd, 0xbb, 0x1b, 0x69, 0xc1, 0x71, 0x77, 0x14, 0xa6, 0x86, 0x67, - 0x7a, 0x96, 0xfe, 0xa3, 0xdf, 0xe0, 0x4b, 0xdf, 0xa9, 0xe0, 0x3b, 0x74, 0xba, 0x9a, 0x63, 0xae, - 0xbc, 0x1f, 0x93, 0xcd, 0x52, 0xf3, 0x4c, 0x7b, 0x99, 0x9a, 0xe8, 0xfa, 0x78, 0xa4, 0x6a, 0xf3, - 0x4f, 0x9f, 0xff, 0xfc, 0xeb, 0x3b, 0x15, 0x8e, 0xe2, 0x79, 0xde, 0x9e, 0x2c, 0xf8, 0xd7, 0x04, - 0x18, 0x91, 0x9e, 0xa8, 0x9a, 0x9e, 0xfa, 0xf3, 0xc3, 0x6f, 0x61, 0x63, 0x61, 0x5a, 0xfb, 0xaf, - 0xa9, 0xbb, 0xbe, 0x00, 0x34, 0x74, 0xef, 0xbe, 0x7d, 0xf8, 0xcd, 0x75, 0xb4, 0xaf, 0xa9, 0x8d, - 0x63, 0x7d, 0x22, 0x24, 0xc6, 0x7c, 0x32, 0x35, 0x15, 0xc3, 0x07, 0x42, 0x93, 0x35, 0xbe, 0x3d, - 0x99, 0x7c, 0x8b, 0x04, 0x83, 0xc8, 0x45, 0x44, 0xfb, 0x31, 0xfd, 0x54, 0xe9, 0x18, 0x88, 0x50, - 0x1e, 0xb1, 0x2e, 0xbc, 0xb9, 0xe4, 0x6b, 0xc5, 0x91, 0x42, 0x0a, 0xdf, 0x7a, 0xb1, 0x6d, 0x79, - 0x5f, 0xdc, 0xe3, 0x2f, 0x1c, 0x59, 0x40, 0xfa, 0x8c, 0x52, 0x9e, 0x46, 0x42, 0xbf, 0xe9, 0x0d, - 0x07, 0x84, 0x83, 0x36, 0xee, 0xec, 0xe0, 0xaf, 0x72, 0x67, 0xc2, 0x1a, 0xdd, 0xce, 0x4b, 0xa6, - 0x7c, 0xd0, 0x70, 0x7b, 0xed, 0xe3, 0x77, 0xc2, 0x48, 0xb2, 0x9e, 0x65, 0x97, 0xa3, 0xaa, 0xb5, - 0xc0, 0x4d, 0x0f, 0xaf, 0xec, 0xc3, 0xe6, 0xac, 0x32, 0x5f, 0xe6, 0x07, 0x6b, 0xaa, 0x32, 0xb5, - 0x86, 0xa4, 0x6d, 0x0c, 0xf1, 0xd4, 0x26, 0x96, 0x8b, 0x12, 0xec, 0x50, 0x10, 0x19, 0x15, 0x8f, - 0xc1, 0x50, 0xa5, 0x4c, 0x90, 0x9a, 0xe3, 0xb7, 0x1a, 0xd2, 0x86, 0x88, 0x79, 0xb2, 0x47, 0x83, - 0x4e, 0x0e, 0x9a, 0xc3, 0x31, 0xee, 0xac, 0xe3, 0x45, 0x9c, 0xbd, 0x44, 0xa8, 0x97, 0x5d, 0xec, - 0xb6, 0xc3, 0x0c, 0x0f, 0xf0, 0xe6, 0x04, 0x15, 0x05, 0x0f, 0x95, 0x5f, 0xfc, 0x45, 0xc5, 0x35, - 0x46, 0xfd, 0x72, 0x0e, 0x77, 0x56, 0x06, 0xe4, 0xf0, 0x46, 0x88, 0x24, 0x8d, 0xca, 0xcc, 0x66, - 0xd8, 0x67, 0x2c, 0x68, 0xbe, 0x58, 0xe4, 0x3c, 0xf8, 0x68, 0xe5, 0xba, 0x24, 0x37, 0xa4, 0xdc, - 0x80, 0xdd, 0x6a, 0x59, 0x8c, 0x9f, 0x63, 0x9c, 0x1c, 0x8a, 0x1c, 0x51, 0x84, 0x8d, 0x2a, 0x30, - 0x28, 0x63, 0x24, 0x5e, 0x90, 0x8b, 0x3b, 0xb9, 0x36, 0x5a, 0xcd, 0xb9, 0x03, 0x1e, 0xd6, 0x5d, - 0x24, 0xfa, 0x7c, 0xed, 0x2a, 0x89, 0x3b, 0xd7, 0x29, 0x8f, 0x31, 0xcb, 0xb3, 0xa8, 0x24, 0x52, - 0x45, 0x1a, 0x16, 0x97, 0xf0, 0xff, 0xf1, 0x40, 0xe2, 0x25, 0x44, 0x5f, 0x02, 0x3c, 0xf0, 0x9b, - 0x86, 0x35, 0x86, 0xb4, 0x59, 0x1f, 0x7f, 0x12, 0xbe, 0x57, 0x71, 0x67, 0x17, 0xa3, 0xce, 0x56, - 0xf0, 0x30, 0x2b, 0x61, 0x6e, 0xf2, 0x48, 0x68, 0xa1, 0x6c, 0xae, 0xb4, 0x34, 0xda, 0x2d, 0x56, - 0xcc, 0x09, 0x94, 0x61, 0x6f, 0xb8, 0x55, 0xae, 0xb1, 0x35, 0x98, 0x56, 0x69, 0x95, 0xeb, 0x8c, - 0xa6, 0x2d, 0x9d, 0xa7, 0x72, 0x83, 0xc3, 0xd4, 0xdc, 0x08, 0x5e, 0x6e, 0x96, 0x35, 0xed, 0x4d, - 0x7d, 0x91, 0x73, 0xda, 0xe6, 0x6e, 0xd3, 0x98, 0xe7, 0xf6, 0x83, 0x99, 0x78, 0xdf, 0x14, 0x73, - 0xf9, 0xd2, 0xcc, 0xd2, 0xdb, 0xfd, 0x1c, 0x55, 0x9d, 0x73, 0xba, 0x24, 0x51, 0x54, 0x96, 0xa2, - 0xfc, 0x10, 0xbf, 0xdb, 0xf0, 0x6d, 0x0c, 0xbf, 0x79, 0x7f, 0x13, 0xfe, 0x2c, 0x84, 0xdf, 0xc8, - 0xe5, 0xa8, 0x99, 0xd5, 0xba, 0x80, 0xcf, 0x52, 0xf7, 0xe2, 0xe4, 0xd0, 0xa1, 0xc4, 0x6a, 0xd1, - 0x9e, 0x6c, 0x76, 0x4f, 0xf9, 0x6a, 0x46, 0x9b, 0x0c, 0x3b, 0x4f, 0x52, 0xf5, 0x60, 0x4c, 0x6a, - 0x9d, 0xc7, 0x7c, 0xd5, 0x9b, 0xeb, 0x0c, 0x5b, 0x18, 0x56, 0x6d, 0x77, 0x5c, 0xe8, 0x64, 0xd5, - 0xea, 0x60, 0x38, 0xd6, 0x76, 0xcc, 0xb2, 0xaa, 0xc8, 0xe3, 0x56, 0xe7, 0x90, 0xaf, 0xb6, 0x7c, - 0x8d, 0xe9, 0xec, 0x7b, 0x55, 0x10, 0x26, 0x99, 0xf5, 0x33, 0x55, 0xb6, 0xa8, 0x01, 0x17, 0x4b, - 0x84, 0x7e, 0xff, 0xd1, 0x17, 0xc0, 0x9f, 0x50, 0x14, 0x42, 0xc5, 0x33, 0x7e, 0x02, 0x37, 0x4b, - 0x5a, 0x3e, 0xec, 0x63, 0x4f, 0x96, 0x00, 0xeb, 0x9a, 0x87, 0x39, 0x1b, 0xaf, 0x3f, 0xbe, 0xb0, - 0x14, 0x66, 0x5b, 0x52, 0xd8, 0x67, 0x94, 0x14, 0xaa, 0x88, 0x4a, 0x75, 0xbf, 0x81, 0xb0, 0xaa, - 0xf0, 0xb0, 0x0e, 0x8c, 0xe5, 0x94, 0xef, 0x48, 0x15, 0x61, 0xcf, 0x9b, 0x35, 0x83, 0x57, 0x84, - 0x9e, 0x7c, 0x68, 0x1e, 0xc4, 0x01, 0xc8, 0x24, 0x86, 0xbb, 0x17, 0xa8, 0xc2, 0x5a, 0x38, 0x08, - 0x5c, 0x9d, 0xca, 0xac, 0x2d, 0xd3, 0xe9, 0xd7, 0x57, 0x43, 0x05, 0xe0, 0xdc, 0x23, 0x4e, 0xdc, - 0x8b, 0x46, 0x7d, 0xa6, 0x28, 0xb2, 0x53, 0xf7, 0x81, 0x2e, 0xcd, 0x01, 0x90, 0x5d, 0x74, 0xb0, - 0xf9, 0xe0, 0x24, 0xd4, 0x42, 0x2c, 0xa8, 0x2a, 0x27, 0x77, 0x84, 0x0e, 0x2f, 0xb3, 0xa6, 0x38, - 0x15, 0xcc, 0x61, 0x8f, 0xef, 0xf2, 0x1d, 0xa1, 0x2b, 0x1f, 0xca, 0x5d, 0x9e, 0x11, 0x0f, 0xda, - 0x01, 0xce, 0xc2, 0xa1, 0x92, 0x30, 0xa3, 0xd6, 0xf6, 0x4c, 0xb4, 0x64, 0x56, 0xd9, 0x16, 0xd6, - 0x48, 0xe6, 0xfa, 0x94, 0xba, 0xee, 0xd7, 0xbd, 0xe4, 0x3e, 0xd5, 0x16, 0x05, 0xe3, 0xeb, 0x27, - 0x45, 0x51, 0xba, 0x6e, 0x70, 0xfe, 0x1a, 0x8f, 0xca, 0x52, 0x0b, 0x21, 0x8b, 0xc7, 0xf2, 0x8a, - 0x6a, 0x12, 0x3f, 0x40, 0x5d, 0x2d, 0x8b, 0x86, 0x88, 0xdd, 0x95, 0x0e, 0xf7, 0xa8, 0x55, 0x2c, - 0xce, 0xa4, 0x2a, 0xdf, 0x6e, 0xe7, 0xd0, 0xe3, 0x21, 0x14, 0x38, 0x83, 0x55, 0xa8, 0x47, 0xbe, - 0xb4, 0xc6, 0xe7, 0x07, 0x24, 0x25, 0x4c, 0xcb, 0x9e, 0x30, 0xa8, 0x1a, 0xfc, 0xa0, 0x3c, 0x95, - 0x81, 0xcd, 0x39, 0x96, 0xf5, 0x87, 0x6d, 0x80, 0xb3, 0xa8, 0xed, 0x73, 0x6c, 0x6d, 0x3f, 0x64, - 0x9a, 0x25, 0xed, 0x1e, 0xf5, 0xb4, 0x8c, 0x78, 0x10, 0xfa, 0xe5, 0x43, 0x8e, 0x15, 0x7d, 0x31, - 0xd3, 0x84, 0xcf, 0xe6, 0x01, 0xc6, 0xb8, 0xe1, 0x4c, 0x30, 0x86, 0x55, 0x01, 0xe6, 0x0a, 0x7b, - 0xa1, 0x52, 0xee, 0x48, 0x21, 0xac, 0x22, 0x0b, 0x74, 0xd8, 0xd5, 0xf7, 0x2e, 0xc5, 0x4f, 0x45, - 0xb5, 0xc2, 0x3c, 0x95, 0xc0, 0x67, 0xdd, 0xb7, 0xad, 0x1a, 0x2f, 0x31, 0xc5, 0x51, 0x65, 0x2a, - 0x76, 0xdb, 0x7b, 0xd7, 0x11, 0x7b, 0x53, 0x4b, 0x00, 0xc7, 0xd2, 0x6a, 0xec, 0xc4, 0xf6, 0x21, - 0x3f, 0xac, 0xb0, 0x53, 0x45, 0x2a, 0x95, 0xfb, 0xbc, 0x82, 0x3a, 0xca, 0xac, 0x8c, 0x14, 0x70, - 0x3e, 0xfd, 0x52, 0x8e, 0xea, 0x21, 0x89, 0xf3, 0x59, 0xa1, 0x2b, 0x71, 0xee, 0x5a, 0x9c, 0xf9, - 0xf9, 0x56, 0x55, 0xa6, 0x24, 0xde, 0xcd, 0xb5, 0x78, 0x97, 0x92, 0xaa, 0x72, 0xae, 0xd5, 0xf3, - 0xf7, 0x75, 0xd3, 0x43, 0xfc, 0x54, 0xf0, 0xeb, 0xdd, 0xb9, 0x4f, 0xf5, 0x2d, 0xbe, 0x75, 0x58, - 0x2d, 0x1b, 0xfb, 0xe2, 0x46, 0x2a, 0x0f, 0xf3, 0x0d, 0xce, 0xcf, 0x2b, 0x73, 0xb4, 0x69, 0xec, - 0x9f, 0x96, 0xad, 0x99, 0xc7, 0xb3, 0x98, 0xee, 0x42, 0x09, 0xf8, 0xd5, 0x22, 0xe6, 0x1e, 0x04, - 0x27, 0x83, 0x69, 0xca, 0x77, 0xc5, 0x8e, 0xb8, 0xd3, 0xfa, 0xe5, 0x5d, 0x8e, 0x2d, 0xef, 0x86, - 0x4a, 0x19, 0x69, 0x2d, 0x24, 0x69, 0x20, 0x1b, 0xc2, 0xb2, 0x61, 0xf0, 0xcb, 0xda, 0x4c, 0xa6, - 0xc3, 0xb3, 0x67, 0xcb, 0x40, 0xab, 0xf8, 0xbc, 0x33, 0x71, 0x9f, 0xa0, 0xc9, 0x12, 0x99, 0x5e, - 0x5f, 0x71, 0x73, 0x93, 0x4a, 0x67, 0x3e, 0xe3, 0x8c, 0xb9, 0x5e, 0x5d, 0x58, 0x95, 0x8e, 0xc2, - 0x4a, 0x1d, 0xf9, 0x65, 0x55, 0x9d, 0xe6, 0x6a, 0x9c, 0x29, 0xbe, 0x54, 0x3a, 0xc5, 0x22, 0x9c, - 0x65, 0x58, 0x66, 0xf8, 0x6c, 0x73, 0x96, 0xbb, 0x6f, 0xce, 0xc6, 0x9d, 0xf2, 0xce, 0x28, 0x8a, - 0xc0, 0x1b, 0xc2, 0x6b, 0x86, 0x56, 0x05, 0xae, 0x5c, 0x87, 0xf3, 0xb8, 0x32, 0xaf, 0x54, 0xd4, - 0x4e, 0x4e, 0xaf, 0x4e, 0xf3, 0xab, 0xce, 0x5c, 0x91, 0x1a, 0xdc, 0x7c, 0xd5, 0x35, 0x79, 0xa3, - 0x32, 0xf3, 0x57, 0x3a, 0x3f, 0x8c, 0xd6, 0x86, 0xb0, 0xf2, 0x15, 0x9d, 0x9b, 0x47, 0xfc, 0xa6, - 0xa9, 0xc2, 0xb0, 0x34, 0x43, 0x0d, 0x15, 0xf1, 0x39, 0xd4, 0xc7, 0x26, 0xae, 0xa3, 0xe9, 0x48, - 0x2a, 0xb2, 0x25, 0x83, 0xdf, 0x22, 0x29, 0xc7, 0x96, 0x7c, 0xbe, 0x85, 0xba, 0x2e, 0x0b, 0xf2, - 0xb9, 0x45, 0x5d, 0xe0, 0xb9, 0xaf, 0x55, 0x6a, 0x66, 0x91, 0xad, 0xcd, 0x86, 0x14, 0x6e, 0x37, - 0x7b, 0x52, 0xa6, 0xb9, 0xd7, 0x36, 0xa8, 0x57, 0x64, 0xdb, 0x06, 0x3f, 0x41, 0xbd, 0x1c, 0xdb, - 0xf6, 0x79, 0x90, 0x09, 0x97, 0x15, 0xa7, 0xc3, 0x09, 0xea, 0x00, 0x1d, 0x0e, 0x5a, 0x06, 0xf3, - 0xbc, 0x6c, 0x80, 0xec, 0x74, 0x08, 0x5d, 0x2a, 0xac, 0x2f, 0xef, 0x9b, 0x40, 0xaf, 0x9a, 0x29, - 0x9a, 0x2c, 0xc8, 0x56, 0xd9, 0x10, 0x77, 0x8a, 0x34, 0x2c, 0xb1, 0x65, 0xa1, 0x54, 0xf2, 0xc7, - 0x8b, 0xd2, 0x5e, 0xec, 0xc1, 0xde, 0x0d, 0xae, 0x2e, 0x09, 0x9c, 0xcf, 0x4b, 0xfc, 0x5e, 0x90, - 0x5e, 0x0c, 0xad, 0x85, 0xc7, 0x91, 0xaf, 0x0c, 0x90, 0xc1, 0x73, 0x48, 0x19, 0x56, 0x90, 0xdf, - 0xe9, 0x22, 0xbf, 0x58, 0x7d, 0x31, 0x8a, 0x33, 0xde, 0x2f, 0xb2, 0x9c, 0x6f, 0xce, 0x39, 0xe4, - 0xb2, 0x3c, 0x2b, 0x9a, 0x7c, 0x65, 0x58, 0x2d, 0x19, 0x78, 0x5c, 0x2c, 0x2b, 0xbe, 0xc0, 0x22, - 0xa9, 0x7a, 0x7f, 0x38, 0x3c, 0x8d, 0xfa, 0x87, 0x22, 0xd5, 0x67, 0x5c, 0xb6, 0x02, 0x60, 0x4b, - 0xbe, 0xde, 0x04, 0x5c, 0x60, 0x1b, 0x78, 0xab, 0xf8, 0x02, 0x67, 0xd8, 0xc2, 0x08, 0x19, 0x72, - 0x89, 0x9b, 0x73, 0xac, 0x84, 0xc7, 0x8a, 0x7c, 0x07, 0xd9, 0x32, 0xf4, 0xcb, 0xdc, 0xfc, 0x27, - 0xe7, 0x2f, 0x6b, 0xa3, 0x76, 0xb4, 0x1f, 0x56, 0x31, 0x1f, 0x39, 0x54, 0x4e, 0x2a, 0x31, 0x92, - 0xa8, 0x20, 0x36, 0x07, 0xae, 0x89, 0xed, 0x62, 0xb9, 0x12, 0xa7, 0x40, 0xf3, 0x0e, 0xc8, 0x55, - 0x57, 0xa4, 0x43, 0xbd, 0x02, 0xbd, 0x09, 0x64, 0x0b, 0xf7, 0x85, 0xb2, 0x73, 0xe0, 0x77, 0xf2, - 0x80, 0xdd, 0x6b, 0xd5, 0xda, 0x41, 0x63, 0x40, 0xde, 0x40, 0xf7, 0x86, 0x07, 0xa0, 0x31, 0xc8, - 0xe1, 0xb0, 0x8a, 0x0e, 0xb1, 0x9e, 0xce, 0x9a, 0x1c, 0xf0, 0x03, 0x74, 0x14, 0xe4, 0x12, 0xce, - 0xcf, 0x57, 0x59, 0xdc, 0x17, 0xe9, 0xa9, 0x2f, 0xa0, 0xf6, 0x6c, 0x98, 0x95, 0x3b, 0xda, 0x50, - 0x99, 0xa2, 0x4e, 0xcb, 0xd4, 0x96, 0x20, 0xff, 0xcb, 0xda, 0xc1, 0x86, 0xb0, 0x70, 0x18, 0xc9, - 0x9b, 0x33, 0xef, 0xed, 0xc1, 0xbc, 0xec, 0x3b, 0x55, 0x4e, 0xa2, 0x60, 0x9c, 0xaa, 0xb1, 0xbe, - 0x33, 0x54, 0x5e, 0x14, 0x83, 0x93, 0x2c, 0x05, 0x64, 0x4d, 0x95, 0x5f, 0xca, 0x72, 0x19, 0x58, - 0xb9, 0x00, 0xf9, 0xb5, 0x64, 0xab, 0x26, 0xb0, 0x8a, 0xb2, 0xe0, 0x39, 0x75, 0x67, 0x0c, 0x64, - 0xd8, 0xa7, 0x6d, 0x86, 0x30, 0xd9, 0x70, 0x8f, 0x52, 0x2e, 0xd3, 0x64, 0xf8, 0x41, 0xa5, 0x9d, - 0x3f, 0xcc, 0x5e, 0xf8, 0xa2, 0x21, 0xa0, 0xe2, 0x7c, 0x48, 0x37, 0x9b, 0x1c, 0xef, 0xb6, 0x0f, - 0x45, 0x57, 0xe8, 0x49, 0x4d, 0x34, 0xd3, 0x42, 0x38, 0xe5, 0x48, 0x17, 0x22, 0xdd, 0x98, 0x2a, - 0x56, 0x1f, 0xd5, 0x38, 0x99, 0xd8, 0x79, 0x65, 0x72, 0xaf, 0xa0, 0x82, 0xcb, 0x15, 0x7c, 0xf0, - 0xd7, 0x1c, 0x0e, 0x79, 0xf8, 0xfd, 0x3b, 0x74, 0xf2, 0xc0, 0xef, 0xb5, 0x6c, 0xc3, 0xe7, 0x41, - 0x06, 0xb5, 0x6a, 0x13, 0xe8, 0xd0, 0xe7, 0xf3, 0xad, 0x31, 0xc7, 0x9b, 0x82, 0x59, 0xa6, 0x7b, - 0xf3, 0xfc, 0xb0, 0x2f, 0x33, 0x89, 0x36, 0xf3, 0xe2, 0x71, 0xf7, 0x13, 0xda, 0xaa, 0x75, 0xe9, - 0x55, 0x71, 0xeb, 0xa1, 0x86, 0x13, 0xcf, 0x3b, 0x5f, 0x17, 0xb6, 0x29, 0x65, 0xd0, 0x32, 0x3b, - 0x40, 0xad, 0x69, 0xbd, 0x7e, 0xe8, 0xd3, 0x01, 0xbf, 0xf3, 0x32, 0xf0, 0xbb, 0xe1, 0x60, 0x4f, - 0x53, 0x97, 0x48, 0xf0, 0x68, 0x85, 0x3c, 0xc4, 0x3c, 0x3a, 0x60, 0x9e, 0x81, 0xde, 0x64, 0xc1, - 0xaf, 0x4c, 0x9b, 0x3d, 0x51, 0x11, 0x3a, 0xf2, 0x8c, 0x0d, 0x79, 0x0f, 0xf2, 0x6f, 0xf1, 0x7b, - 0x71, 0x29, 0xf8, 0xda, 0x8c, 0x3f, 0x88, 0x25, 0x71, 0xaf, 0x71, 0xb5, 0x2e, 0x57, 0x66, 0x7a, - 0xd9, 0x97, 0x3a, 0x8f, 0x06, 0x4a, 0x47, 0xda, 0x55, 0xcd, 0x3c, 0x5f, 0x9e, 0xe4, 0xe6, 0xa8, - 0xbb, 0xea, 0xc3, 0xcf, 0x13, 0xb7, 0x43, 0x52, 0x0d, 0x74, 0xbd, 0x0a, 0xd9, 0xc2, 0x40, 0x64, - 0x15, 0x51, 0x74, 0x7d, 0x45, 0x63, 0x95, 0x51, 0x6e, 0x55, 0xde, 0x2a, 0x7a, 0x99, 0x99, 0x65, - 0xfb, 0x8d, 0xbe, 0x40, 0xd7, 0xe6, 0xc6, 0x54, 0xcc, 0x4a, 0xe0, 0x31, 0x5e, 0xfa, 0x4a, 0x77, - 0x38, 0xaf, 0xc9, 0xc3, 0xa9, 0x00, 0x3a, 0x3a, 0x9a, 0xe4, 0x10, 0xf0, 0x86, 0x43, 0xed, 0xc7, - 0xfe, 0x86, 0x18, 0x09, 0x11, 0xd5, 0xb1, 0xec, 0x3e, 0x81, 0x48, 0x23, 0x34, 0x02, 0x1f, 0x8b, - 0x24, 0x3f, 0xe9, 0x17, 0xb4, 0x4a, 0xe8, 0x17, 0x66, 0x82, 0x19, 0xc8, 0x5d, 0x0d, 0xec, 0x1f, - 0x3e, 0x53, 0x73, 0x9a, 0xb4, 0x7b, 0x17, 0x32, 0x29, 0x87, 0x3c, 0xeb, 0x85, 0x32, 0x0c, 0xeb, - 0xb1, 0x3f, 0x19, 0x72, 0xa8, 0xcc, 0xd6, 0xba, 0x03, 0x7b, 0xdb, 0xd8, 0xb9, 0x23, 0xb0, 0xc9, - 0x85, 0xae, 0x6c, 0x49, 0x32, 0x6f, 0xd5, 0x74, 0xb3, 0xb8, 0xad, 0xec, 0xdc, 0x6d, 0x23, 0xbb, - 0xaa, 0x75, 0x15, 0xe4, 0xd6, 0xf6, 0x5a, 0x01, 0x94, 0x4d, 0xe2, 0x7b, 0xf6, 0xb2, 0xb6, 0xf7, - 0xcf, 0xdb, 0x4c, 0x97, 0x76, 0x99, 0xa6, 0xec, 0x35, 0x90, 0x8d, 0x40, 0xae, 0xe5, 0xc7, 0x16, - 0xfd, 0x52, 0x69, 0x1d, 0x3c, 0x25, 0xb0, 0x85, 0xc9, 0xf6, 0xec, 0xa9, 0xb0, 0x3b, 0xd3, 0x05, - 0xf6, 0xac, 0x8d, 0xca, 0x4a, 0xf7, 0x20, 0x87, 0x30, 0xdd, 0x28, 0xc8, 0x88, 0xf8, 0x4b, 0x72, - 0x83, 0x86, 0x81, 0xe3, 0x43, 0x4c, 0x1c, 0xde, 0x04, 0x1e, 0x2e, 0x81, 0x06, 0x87, 0x58, 0xb7, - 0x31, 0x8d, 0xf6, 0x58, 0x07, 0xe5, 0x48, 0xc7, 0xad, 0xa6, 0xcf, 0x57, 0x6b, 0x3e, 0xf1, 0x8d, - 0x81, 0xac, 0x9a, 0x44, 0x47, 0x97, 0x17, 0xbe, 0x63, 0xa6, 0x2d, 0x81, 0x66, 0x11, 0x7d, 0xb2, - 0x60, 0x5f, 0xf7, 0xe5, 0x29, 0xcd, 0x42, 0xfc, 0xa1, 0xd4, 0x76, 0x35, 0xad, 0xc6, 0x0c, 0x3d, - 0xa0, 0x8f, 0xa0, 0xef, 0x73, 0xdb, 0x2a, 0x3b, 0x6e, 0x88, 0x55, 0xba, 0xca, 0x85, 0xf8, 0x53, - 0xe6, 0x3c, 0x8b, 0xb6, 0x52, 0x66, 0xb6, 0xf7, 0x74, 0xd1, 0x8a, 0xe9, 0xfa, 0x22, 0xf7, 0xe4, - 0x9e, 0x69, 0xd8, 0x22, 0xcf, 0xf2, 0x05, 0xa0, 0x9b, 0x5b, 0xe9, 0x16, 0xfb, 0x12, 0xb6, 0xaf, - 0x07, 0xa5, 0x81, 0x8c, 0x72, 0x86, 0x3d, 0xbc, 0xd4, 0xd1, 0x54, 0x16, 0x58, 0xc6, 0x6f, 0x97, - 0x40, 0xa7, 0x95, 0x9e, 0xfb, 0xd8, 0x99, 0xed, 0x16, 0xf5, 0x01, 0xdd, 0xad, 0xed, 0xe4, 0x90, - 0xd6, 0x2c, 0xd3, 0xdc, 0xd2, 0x62, 0x33, 0x57, 0xec, 0xf0, 0xf1, 0x9c, 0x27, 0xaf, 0xde, 0xe9, - 0x36, 0xca, 0x66, 0xf1, 0xa5, 0xc1, 0x69, 0xcb, 0xd6, 0x94, 0x97, 0x4a, 0x68, 0xc5, 0x81, 0x54, - 0x36, 0x9a, 0x12, 0xc0, 0xcf, 0x88, 0x2c, 0xd0, 0xb1, 0x1a, 0xf8, 0x1f, 0xce, 0xa1, 0x32, 0x6e, - 0x49, 0x21, 0xf6, 0x70, 0x8e, 0x44, 0xe3, 0x2c, 0xde, 0x08, 0x68, 0x78, 0x94, 0x9d, 0x9d, 0xbc, - 0x6c, 0xee, 0xe4, 0x4e, 0x13, 0xeb, 0x6f, 0x37, 0xa1, 0xdf, 0x33, 0x6d, 0x00, 0xf4, 0x51, 0xca, - 0x6c, 0x40, 0x6b, 0xf0, 0x6b, 0xd5, 0xf6, 0x2c, 0x3f, 0xae, 0xec, 0x6c, 0xad, 0xd2, 0xcb, 0x01, - 0xfe, 0x92, 0xdc, 0x31, 0x73, 0xdb, 0xa6, 0xba, 0x71, 0xd6, 0x05, 0xef, 0xea, 0x18, 0x5a, 0xc9, - 0x10, 0x87, 0xd0, 0xf7, 0xed, 0xb9, 0xb6, 0x1f, 0x70, 0x06, 0x55, 0x99, 0xd1, 0x46, 0xb3, 0x5b, - 0xdc, 0x74, 0xa6, 0x92, 0x6c, 0xd4, 0x37, 0x6b, 0xa3, 0x9d, 0x71, 0xc8, 0x27, 0x0b, 0x69, 0x53, - 0x2f, 0x77, 0xdf, 0x39, 0xd4, 0xb4, 0x7a, 0xa9, 0xa8, 0xb6, 0xbb, 0x88, 0x69, 0xec, 0xf3, 0x95, - 0x32, 0x0e, 0xb2, 0x0c, 0x1e, 0x87, 0x97, 0x25, 0x9c, 0x61, 0x61, 0x3b, 0xc5, 0x06, 0xf1, 0x23, - 0x0b, 0x52, 0xd0, 0x11, 0x70, 0xbc, 0xd8, 0xf7, 0x10, 0x44, 0x68, 0x38, 0xce, 0xa4, 0xf0, 0x44, - 0x2a, 0xc8, 0x86, 0x58, 0x22, 0x49, 0x3e, 0x8e, 0xb8, 0x70, 0x70, 0x8a, 0xb3, 0x2b, 0xfe, 0x80, - 0x63, 0x0d, 0xde, 0xc7, 0xd2, 0x0c, 0xf6, 0xf0, 0x45, 0x66, 0x55, 0xbf, 0x86, 0xe7, 0x83, 0x5e, - 0xe2, 0xb8, 0x17, 0xe2, 0x3e, 0x12, 0xbc, 0xb2, 0x03, 0x2a, 0xaf, 0xe1, 0x78, 0x6d, 0x85, 0xf5, - 0x14, 0x52, 0x96, 0x06, 0x0e, 0xf6, 0x80, 0xb8, 0x16, 0xe2, 0x72, 0xa8, 0x27, 0x11, 0x7f, 0x5e, - 0xe6, 0x3b, 0x45, 0x96, 0xed, 0xf0, 0x83, 0xda, 0x8e, 0xb7, 0x50, 0x2c, 0x87, 0x9a, 0x42, 0xf4, - 0x76, 0x2f, 0xe2, 0xef, 0x25, 0x4c, 0x73, 0x16, 0xcb, 0xe5, 0x41, 0xec, 0x35, 0x89, 0x5e, 0xca, - 0x98, 0xe6, 0x6c, 0x64, 0x4f, 0xd9, 0x83, 0x9c, 0x39, 0xd7, 0xe5, 0xda, 0x41, 0x8e, 0x63, 0x21, - 0x64, 0x32, 0xf7, 0x94, 0xcf, 0x71, 0x93, 0x6d, 0x11, 0x81, 0x4b, 0x64, 0x51, 0x73, 0xb8, 0x51, - 0xba, 0xe2, 0x93, 0x58, 0x6d, 0xa2, 0xdc, 0xd4, 0x9f, 0xb3, 0x8a, 0x56, 0x15, 0xbb, 0x9a, 0xc0, - 0x76, 0x25, 0xad, 0xdd, 0xd5, 0x76, 0x2c, 0x33, 0xe6, 0xdb, 0xdd, 0xa6, 0xd2, 0x98, 0xb3, 0x07, - 0xb0, 0x30, 0xf9, 0xea, 0x94, 0x59, 0x76, 0xe6, 0x2f, 0x52, 0xfb, 0x30, 0xcc, 0xc8, 0x2c, 0xe8, - 0xcb, 0x74, 0x2c, 0x35, 0x21, 0xc6, 0x90, 0xf9, 0x97, 0xca, 0xcb, 0x8e, 0xd6, 0xd1, 0x4b, 0x5e, - 0xf7, 0x21, 0xed, 0x1a, 0x5a, 0x8f, 0x90, 0xb1, 0xb2, 0x10, 0x8d, 0x14, 0x80, 0x64, 0xe5, 0xee, - 0x11, 0x9e, 0xce, 0x0a, 0x8c, 0xbc, 0x91, 0xaa, 0xa2, 0x4f, 0xb9, 0xda, 0x46, 0xe1, 0x24, 0xb2, - 0x37, 0xb5, 0xe1, 0x9f, 0xc0, 0x87, 0x81, 0xff, 0xd7, 0x76, 0x35, 0xbf, 0x5c, 0x97, 0x69, 0xab, - 0xae, 0x75, 0xe5, 0x39, 0xa7, 0x68, 0x02, 0xec, 0x39, 0x6a, 0xf5, 0x56, 0xac, 0xd4, 0xb3, 0xf3, - 0x7d, 0xd8, 0x83, 0x2b, 0x6b, 0xb5, 0x1a, 0xa7, 0xec, 0xba, 0x73, 0xab, 0x47, 0xf4, 0x1b, 0x68, - 0x4f, 0xdd, 0xbb, 0x40, 0xdf, 0xba, 0x82, 0x59, 0x54, 0x35, 0xd0, 0x1a, 0x95, 0x0c, 0xd4, 0x09, - 0xe2, 0xe2, 0x7e, 0xf9, 0x18, 0xbb, 0x33, 0x3c, 0xc4, 0x41, 0xe2, 0x9e, 0x67, 0x43, 0xbf, 0x9c, - 0xad, 0x05, 0x3a, 0x0c, 0x31, 0xb1, 0x76, 0x16, 0xe7, 0xc8, 0x81, 0x0d, 0x30, 0xb5, 0x6a, 0x15, - 0xcb, 0x6c, 0xe0, 0xbb, 0xfb, 0x65, 0x92, 0x63, 0x08, 0x0b, 0x1c, 0x4f, 0x89, 0x7e, 0x53, 0xa9, - 0x95, 0x84, 0x7d, 0xad, 0xcd, 0xf4, 0xc9, 0x27, 0xaa, 0xaf, 0xa9, 0xc9, 0xd0, 0xaa, 0x99, 0x43, - 0xab, 0x0d, 0x6d, 0x71, 0xd6, 0xd8, 0x75, 0x32, 0x9d, 0x79, 0x87, 0x1e, 0xcf, 0x5f, 0x20, 0x70, - 0x13, 0x67, 0xb8, 0xcf, 0x27, 0xe3, 0x5e, 0x17, 0xf2, 0x01, 0x53, 0x9c, 0x41, 0xdf, 0x0c, 0x64, - 0x1a, 0xf2, 0x93, 0x1c, 0x82, 0xfc, 0x64, 0x2f, 0x80, 0xbf, 0x13, 0x0e, 0x8d, 0xa7, 0x27, 0xf3, - 0x45, 0x17, 0x7b, 0xe5, 0x49, 0xad, 0xe7, 0xef, 0x26, 0xf3, 0x1a, 0x37, 0xe9, 0xd5, 0x2a, 0x2f, - 0xfc, 0xb8, 0x0a, 0x01, 0x67, 0x15, 0x60, 0x56, 0xc4, 0x41, 0x9e, 0x93, 0x96, 0xf4, 0xbe, 0xcd, - 0x85, 0xf3, 0x24, 0xbe, 0x1e, 0xc1, 0x03, 0x38, 0xc4, 0x1f, 0xac, 0x29, 0xca, 0x41, 0xb4, 0xc8, - 0x19, 0x3e, 0x16, 0x3a, 0x1a, 0xec, 0x1e, 0xce, 0xaf, 0xa4, 0x20, 0x07, 0x88, 0xce, 0xd1, 0x39, - 0xfa, 0x64, 0xe1, 0x20, 0x85, 0xf6, 0x4d, 0x64, 0xca, 0xbd, 0x2b, 0x71, 0xca, 0xa1, 0xef, 0x34, - 0x4d, 0x6d, 0xd1, 0x3c, 0x60, 0x99, 0x52, 0x6b, 0xfa, 0x60, 0x41, 0x37, 0x4b, 0x4a, 0xbe, 0xbe, - 0xdf, 0xf4, 0x86, 0x99, 0xa6, 0xaf, 0xd0, 0x4c, 0x53, 0xdb, 0xf3, 0x53, 0x1d, 0x62, 0x16, 0x79, - 0xa6, 0x65, 0xdb, 0x73, 0x23, 0x9c, 0x3b, 0xcc, 0xd4, 0x7c, 0xe1, 0xb1, 0xd9, 0xeb, 0xd2, 0xb0, - 0xa6, 0xa6, 0x55, 0x9a, 0xb3, 0x39, 0xe4, 0x0d, 0xfa, 0x44, 0x98, 0x0a, 0x83, 0x66, 0xb3, 0xa7, - 0x68, 0xf7, 0xcd, 0xc3, 0xb8, 0x5b, 0x61, 0xc7, 0x79, 0x62, 0x6f, 0xa4, 0x12, 0xf0, 0x51, 0x86, - 0xfc, 0xae, 0xe6, 0x04, 0x75, 0x1c, 0xc8, 0xed, 0x40, 0xcf, 0xb0, 0xef, 0xe5, 0x7b, 0xf0, 0x29, - 0x89, 0x0e, 0xe5, 0xb3, 0x7d, 0x9c, 0x1f, 0xa3, 0xf2, 0x10, 0xd7, 0xdc, 0x94, 0xf8, 0x3b, 0xd6, - 0x3b, 0xf2, 0x1a, 0x42, 0xfc, 0x01, 0xe7, 0xcf, 0x21, 0xce, 0x92, 0xb0, 0xbe, 0xf1, 0x58, 0xdf, - 0xa6, 0x38, 0x0e, 0xc1, 0x7e, 0xb2, 0x79, 0xc0, 0xf9, 0x9b, 0x08, 0xf9, 0x1b, 0x9c, 0xaf, 0x27, - 0x32, 0x28, 0xf8, 0x54, 0x58, 0xdc, 0x2f, 0x69, 0x64, 0xac, 0x09, 0xb1, 0x2d, 0xf8, 0xf9, 0x29, - 0xd0, 0x88, 0xc1, 0x9f, 0x3c, 0xec, 0x5f, 0xee, 0x96, 0x15, 0x43, 0x60, 0xfb, 0x90, 0x13, 0x39, - 0x75, 0xe6, 0xa5, 0x6f, 0xf0, 0x3b, 0x11, 0x8b, 0x12, 0x63, 0xd4, 0x14, 0x4a, 0x75, 0x39, 0x65, - 0x92, 0x71, 0x90, 0x35, 0x78, 0x5a, 0x67, 0xdc, 0xf5, 0xda, 0xb4, 0xfb, 0xf5, 0xe9, 0x90, 0x05, - 0x3f, 0xbd, 0x5e, 0xdb, 0x68, 0x5d, 0xb3, 0x6c, 0xfc, 0x03, 0x78, 0xbe, 0x40, 0x6e, 0xb8, 0xea, - 0x2b, 0x60, 0x87, 0x14, 0x6b, 0x18, 0xc8, 0x2f, 0xf8, 0x27, 0xb0, 0x0f, 0x0e, 0x36, 0x0e, 0x2d, - 0x8c, 0x6c, 0x19, 0xcc, 0x10, 0xba, 0x9a, 0xd7, 0xc5, 0xfe, 0x1b, 0x64, 0x30, 0xf0, 0xdf, 0x33, - 0xc8, 0x77, 0x66, 0x41, 0xdc, 0x08, 0x7d, 0x11, 0x0f, 0xcd, 0x70, 0x7e, 0x6c, 0x6f, 0xf9, 0x48, - 0x6e, 0x23, 0xfe, 0x77, 0x4f, 0xdb, 0x78, 0xbc, 0xb6, 0xc7, 0x79, 0x84, 0x08, 0xbe, 0x7d, 0xda, - 0xea, 0x2d, 0x25, 0xd0, 0x19, 0x61, 0x3a, 0x86, 0xb8, 0xa9, 0x5f, 0xb6, 0x9a, 0x89, 0x76, 0xb3, - 0x2f, 0x6b, 0x90, 0x07, 0xd2, 0x90, 0x03, 0x86, 0x76, 0x96, 0x3e, 0x6d, 0x67, 0x59, 0x7b, 0x51, - 0x83, 0xef, 0xe0, 0xc8, 0xee, 0x37, 0x3b, 0xd7, 0xaa, 0x75, 0x5c, 0xb5, 0x66, 0xbe, 0x3c, 0x76, - 0xb3, 0xb2, 0x8a, 0x46, 0x92, 0x3a, 0x96, 0x21, 0x2e, 0xdc, 0x47, 0xb0, 0xcf, 0xf7, 0x0a, 0xdb, - 0x5b, 0x1a, 0x55, 0xa6, 0xde, 0x18, 0xfc, 0x40, 0xb5, 0x73, 0xc8, 0xdd, 0x0f, 0x00, 0xc6, 0x79, - 0x1b, 0xd8, 0x6c, 0x81, 0x8f, 0x08, 0xf7, 0x92, 0x47, 0x15, 0x73, 0xb8, 0xa8, 0x4c, 0x5d, 0x8b, - 0xda, 0xac, 0x64, 0x09, 0xa2, 0xa5, 0x40, 0x9e, 0xc0, 0x86, 0xe7, 0xb1, 0x3c, 0xe1, 0xf8, 0x4e, - 0xb8, 0x15, 0x87, 0x1e, 0xe3, 0xa1, 0x59, 0xec, 0xfb, 0x49, 0x6c, 0x00, 0x76, 0x6c, 0x08, 0xf1, - 0x23, 0x0d, 0xf4, 0x32, 0x85, 0x41, 0xa7, 0xd5, 0xb3, 0x0c, 0x53, 0x62, 0xf3, 0x60, 0x63, 0xc7, - 0x6d, 0x88, 0xab, 0x06, 0xe5, 0x79, 0x31, 0xdb, 0xa4, 0x79, 0x5d, 0xe2, 0x84, 0xcb, 0x33, 0x19, - 0x76, 0xbd, 0xcc, 0x19, 0x4b, 0x75, 0x5e, 0x66, 0xbb, 0xfc, 0x94, 0x95, 0xfa, 0xf6, 0xb2, 0xce, - 0x4a, 0x75, 0x8e, 0xf8, 0x14, 0xf0, 0x41, 0x6c, 0x96, 0x1a, 0x48, 0xed, 0x19, 0xda, 0x8b, 0xa8, - 0x04, 0x71, 0x28, 0xb6, 0x5b, 0x80, 0xdf, 0x80, 0xdd, 0x41, 0x7c, 0xb9, 0xd7, 0x66, 0xcd, 0xae, - 0x96, 0x05, 0xb9, 0xcc, 0x96, 0x0f, 0x22, 0xfb, 0x2e, 0x1e, 0x5f, 0xb1, 0x51, 0x33, 0xfb, 0x51, - 0x80, 0x54, 0x42, 0x88, 0x3e, 0xa9, 0xb5, 0x1b, 0xf9, 0x42, 0xb9, 0xcc, 0x0f, 0x37, 0x0d, 0x88, - 0x97, 0x4e, 0xdb, 0x4a, 0x64, 0x67, 0xe6, 0x1d, 0xbf, 0xda, 0x4c, 0xae, 0x85, 0xcf, 0xa7, 0x75, - 0xe4, 0xa7, 0x7b, 0xf2, 0xb6, 0x31, 0x1f, 0xca, 0x78, 0xdd, 0x49, 0x1b, 0x55, 0x9c, 0xa3, 0xad, - 0x92, 0x40, 0xbe, 0x39, 0x84, 0xeb, 0x35, 0x06, 0xc2, 0xf5, 0x0a, 0x03, 0x35, 0x49, 0x99, 0xdc, - 0xbf, 0x25, 0xdf, 0x0e, 0xea, 0x92, 0x1a, 0x44, 0xe0, 0xb3, 0x82, 0xb3, 0x9c, 0xe7, 0x08, 0x25, - 0x71, 0x16, 0xd7, 0x2d, 0xe0, 0xec, 0xc3, 0xec, 0x59, 0xfb, 0x70, 0xd6, 0x1e, 0x9c, 0xb5, 0x71, - 0xec, 0x1a, 0xe4, 0x44, 0x53, 0xbe, 0xd3, 0xea, 0x42, 0xfc, 0xc8, 0xfb, 0x6a, 0xcd, 0x98, 0x57, - 0x39, 0x34, 0xb7, 0x2a, 0x4c, 0x34, 0x16, 0xe4, 0x4f, 0x8b, 0x58, 0x7e, 0x55, 0xb5, 0xb2, 0x97, - 0x03, 0x79, 0xdb, 0x22, 0x68, 0x0f, 0x49, 0x7b, 0x71, 0x94, 0x3f, 0xce, 0x67, 0xdd, 0xd1, 0xa0, - 0x94, 0xa3, 0x3a, 0x51, 0x5e, 0x05, 0x32, 0x59, 0x62, 0x87, 0x02, 0xd7, 0x19, 0x8a, 0x30, 0xf7, - 0xac, 0x86, 0xf0, 0x78, 0x6f, 0x70, 0x20, 0xb3, 0x04, 0xa6, 0x6b, 0xad, 0xf7, 0x47, 0xf9, 0x0d, - 0xf6, 0xc3, 0xf0, 0xfb, 0x0e, 0x7c, 0x0f, 0x64, 0x7b, 0x2f, 0x87, 0xf5, 0x80, 0x80, 0x9e, 0x63, - 0x54, 0xa7, 0x71, 0x3d, 0xb6, 0x84, 0xed, 0x99, 0x8f, 0xeb, 0x46, 0x60, 0xc7, 0x24, 0x6d, 0x09, - 0xf4, 0xd9, 0x57, 0x7d, 0xbe, 0x5f, 0x86, 0xd0, 0x26, 0x53, 0x2f, 0xdd, 0xd7, 0xeb, 0x88, 0x7b, - 0xe2, 0x0b, 0x46, 0xcf, 0xf0, 0x67, 0xa5, 0xbe, 0x24, 0x0e, 0x51, 0x75, 0x68, 0xb7, 0x73, 0xb6, - 0x8d, 0x2a, 0x74, 0x6f, 0xcd, 0xb6, 0x4a, 0x68, 0x37, 0x2f, 0x75, 0xc5, 0x20, 0x54, 0xee, 0x6e, - 0x29, 0x4b, 0x6a, 0x28, 0x68, 0xe3, 0x92, 0xd2, 0x2a, 0xea, 0xf2, 0x25, 0x11, 0xe2, 0x1b, 0x61, - 0xc7, 0xb3, 0x75, 0xbf, 0xb2, 0x5e, 0xaf, 0x9d, 0x7b, 0xc6, 0x46, 0x5c, 0x81, 0x2a, 0xac, 0x21, - 0xce, 0x81, 0xd0, 0xa9, 0xda, 0x0f, 0x90, 0x9a, 0x23, 0xa3, 0xfd, 0x84, 0xed, 0x6d, 0xa9, 0x4c, - 0xea, 0x8b, 0x1a, 0x7e, 0xee, 0x31, 0x42, 0x6c, 0x11, 0x09, 0x3e, 0x5a, 0x22, 0xce, 0x25, 0x95, - 0x5b, 0xb6, 0x07, 0xf6, 0xb5, 0x03, 0x76, 0x77, 0x27, 0x5b, 0xe0, 0x7f, 0x1f, 0x11, 0xe4, 0x51, - 0x3c, 0xce, 0xa7, 0x02, 0x5f, 0x64, 0xe1, 0x1c, 0x03, 0x62, 0xb0, 0x8e, 0x08, 0xfb, 0x61, 0x7b, - 0x8c, 0x8e, 0xf3, 0x4b, 0xd0, 0x86, 0x58, 0xb3, 0x0e, 0xb1, 0xd9, 0x8b, 0x42, 0x49, 0x68, 0x2f, - 0x14, 0x70, 0x9c, 0xe8, 0xce, 0x44, 0xa3, 0xee, 0x08, 0x07, 0xb0, 0xbf, 0x6b, 0xc8, 0xad, 0xb0, - 0x2d, 0xf5, 0x21, 0x16, 0xb3, 0x20, 0x82, 0xeb, 0xd5, 0x60, 0xae, 0x0c, 0xbc, 0x56, 0x94, 0xf5, - 0x5e, 0xdc, 0x83, 0x6d, 0x5e, 0x2b, 0x3d, 0xf0, 0xb7, 0xd6, 0x8b, 0x22, 0x9a, 0xed, 0x27, 0x84, - 0x5a, 0x6b, 0x0a, 0xfb, 0x0c, 0x18, 0xc7, 0x35, 0x35, 0x65, 0x52, 0x58, 0x2b, 0xd6, 0xe8, 0x7e, - 0xcd, 0xc8, 0x33, 0xd1, 0x08, 0x8b, 0x90, 0x42, 0x09, 0xe8, 0xd1, 0x42, 0x68, 0x27, 0xe3, 0xd8, - 0xaf, 0x8a, 0x38, 0x2d, 0x28, 0x67, 0x13, 0xbf, 0x20, 0x12, 0x3c, 0x83, 0xf8, 0xe0, 0x76, 0x3e, - 0x0b, 0x67, 0x1a, 0x90, 0x58, 0x0d, 0xf2, 0x81, 0x77, 0xe4, 0x59, 0x07, 0x64, 0x43, 0xdc, 0x59, - 0x56, 0xe0, 0x2c, 0xfc, 0xb2, 0x81, 0x73, 0x78, 0x88, 0xad, 0x40, 0xc9, 0x2a, 0x9d, 0x85, 0x11, - 0xcb, 0xa4, 0xcc, 0x77, 0x0a, 0x10, 0x4b, 0x74, 0xaa, 0xc6, 0xe3, 0xbd, 0x75, 0x08, 0x73, 0x76, - 0x36, 0xcc, 0xe9, 0x67, 0x67, 0x6d, 0x43, 0x04, 0x62, 0x0c, 0x9e, 0x18, 0xf0, 0x37, 0xd8, 0x4f, - 0x56, 0x66, 0xb5, 0x42, 0x87, 0x7f, 0x0a, 0xe0, 0xed, 0x8a, 0x04, 0x7e, 0x8f, 0x09, 0xe1, 0x9b, - 0xbe, 0xdd, 0x46, 0xb4, 0xce, 0x03, 0xad, 0xca, 0xbb, 0x69, 0x07, 0x94, 0xff, 0xbe, 0x3d, 0xd3, - 0x32, 0x72, 0xc7, 0x18, 0x04, 0x6d, 0xb0, 0xf3, 0xb3, 0x21, 0x69, 0x23, 0x1c, 0x40, 0x73, 0x5b, - 0xca, 0x30, 0xda, 0x0a, 0xda, 0xcf, 0x03, 0xfa, 0x80, 0xbf, 0x06, 0x79, 0xec, 0x21, 0x56, 0x43, - 0xa2, 0x8f, 0xb2, 0xa8, 0x44, 0xfc, 0x37, 0xce, 0xa3, 0x4b, 0xd8, 0xa7, 0x62, 0x7e, 0x37, 0x25, - 0x99, 0xb4, 0x9b, 0x3b, 0xad, 0x0a, 0x31, 0x37, 0xe6, 0xf7, 0x7b, 0xea, 0x02, 0x3d, 0x2c, 0x0f, - 0xb5, 0x9e, 0x36, 0x60, 0x7d, 0xde, 0x68, 0xf3, 0xf4, 0x53, 0x7d, 0x08, 0xa1, 0x35, 0xf0, 0xcf, - 0xe2, 0xe9, 0x62, 0x8f, 0xf7, 0x1f, 0x7b, 0x3d, 0x7f, 0x65, 0x08, 0xa5, 0xfb, 0xf5, 0xe1, 0x86, - 0x5d, 0x0a, 0xdb, 0x1c, 0xf0, 0x78, 0x84, 0xd7, 0xa0, 0xda, 0x0e, 0xe0, 0x14, 0x29, 0xb7, 0xa6, - 0x28, 0x52, 0x65, 0x35, 0x59, 0x16, 0x96, 0xbd, 0xde, 0xae, 0x49, 0x0d, 0xdc, 0x62, 0xaf, 0x44, - 0xb3, 0x56, 0x59, 0x66, 0xeb, 0x90, 0x57, 0x74, 0xba, 0x45, 0x0c, 0x63, 0xd3, 0xa0, 0x65, 0xb0, - 0x1d, 0x92, 0x02, 0xed, 0x0d, 0x81, 0x49, 0x03, 0xcf, 0xda, 0xb4, 0x17, 0xad, 0x23, 0xa1, 0x3e, - 0xc8, 0xcb, 0x16, 0xcb, 0x0b, 0xd6, 0x1f, 0x09, 0xe1, 0x67, 0x7d, 0x1e, 0xd8, 0xe5, 0x1a, 0xd8, - 0xed, 0x32, 0x27, 0x09, 0x55, 0xc8, 0x05, 0xd9, 0xd6, 0x7c, 0x74, 0x6f, 0xb4, 0x0b, 0x10, 0x9f, - 0xf2, 0x8f, 0x94, 0x8d, 0x75, 0xa8, 0x82, 0x3a, 0xe6, 0x12, 0x72, 0xb4, 0x47, 0x05, 0x75, 0x5d, - 0xc6, 0x44, 0x3c, 0xd8, 0xc0, 0xed, 0x3d, 0xd3, 0x1b, 0x06, 0xaa, 0xe5, 0xe3, 0x7a, 0x2f, 0x0d, - 0xf1, 0x01, 0xe8, 0x03, 0x68, 0x11, 0x17, 0x84, 0x06, 0x16, 0xd0, 0x70, 0x50, 0xf3, 0x35, 0xb6, - 0x89, 0x9f, 0xbb, 0x80, 0xde, 0x03, 0x5d, 0xb2, 0x6c, 0x2f, 0xf8, 0xac, 0xf9, 0xcd, 0x66, 0xbb, - 0x27, 0x3e, 0x75, 0x16, 0xa3, 0x7b, 0xb7, 0xea, 0xb3, 0x46, 0x9b, 0x16, 0xd1, 0xae, 0x98, 0xb5, - 0xc7, 0xed, 0x7b, 0x83, 0x1f, 0xb2, 0xad, 0x95, 0xf6, 0xd4, 0x87, 0xd8, 0xdc, 0x1c, 0xf5, 0x50, - 0xbb, 0x6b, 0x3c, 0xd9, 0x16, 0x44, 0x5c, 0x95, 0xea, 0x3d, 0x03, 0x21, 0x38, 0xd5, 0xe2, 0x33, - 0xb6, 0x2a, 0xb1, 0xf5, 0xc3, 0x80, 0x1e, 0xf7, 0x98, 0x0a, 0xea, 0x7a, 0x8e, 0x17, 0xd5, 0xe1, - 0xcb, 0x03, 0xac, 0x0f, 0x0e, 0x3e, 0x1f, 0x30, 0xdb, 0x3e, 0xa9, 0x8b, 0x75, 0xa4, 0x40, 0x96, - 0xc1, 0x2f, 0x5d, 0xd4, 0x9d, 0x77, 0xe2, 0xb2, 0x89, 0xeb, 0x29, 0x3e, 0xf0, 0x6f, 0xa7, 0x65, - 0xcb, 0xd7, 0xea, 0x3a, 0x67, 0x6d, 0xe6, 0x65, 0xf1, 0xd2, 0xae, 0x1d, 0x7f, 0x60, 0xcf, 0xd0, - 0xff, 0x4e, 0xcf, 0xfc, 0x71, 0xd4, 0x6e, 0x3c, 0x6d, 0x6b, 0x67, 0x6b, 0x88, 0xff, 0xe7, 0x3a, - 0x38, 0x9e, 0x34, 0x20, 0xcb, 0x23, 0xcf, 0xaf, 0x38, 0x19, 0xeb, 0xe2, 0xb2, 0xe9, 0x57, 0xc0, - 0x4c, 0x49, 0x45, 0xca, 0x28, 0x6d, 0xdb, 0xc3, 0x1c, 0xeb, 0x1f, 0x82, 0x7a, 0x7b, 0xf8, 0xfc, - 0x01, 0x9c, 0x14, 0xb7, 0xc5, 0xcf, 0x96, 0x0a, 0x3e, 0x8e, 0xc6, 0x50, 0x40, 0xe3, 0xbd, 0xc6, - 0x61, 0xb3, 0xd0, 0x16, 0x7b, 0x7c, 0x1b, 0x55, 0x21, 0x0e, 0x45, 0x45, 0xbf, 0x28, 0x16, 0xc3, - 0xf9, 0x0a, 0x9e, 0x4f, 0x4b, 0x64, 0x3e, 0xec, 0x31, 0x67, 0x41, 0xc6, 0xb9, 0x5d, 0xe9, 0x40, - 0xe1, 0xb8, 0xb4, 0x2d, 0x4a, 0x2c, 0xdd, 0x3b, 0x50, 0xc1, 0xfa, 0xd0, 0xde, 0x26, 0xe8, 0xd9, - 0x08, 0xec, 0xca, 0xac, 0xec, 0x6b, 0x4b, 0x21, 0xa8, 0x21, 0x42, 0x4c, 0x4e, 0x74, 0x85, 0x11, - 0xa0, 0x0f, 0xe2, 0x51, 0x46, 0xdc, 0x6b, 0x38, 0x16, 0x60, 0x30, 0xef, 0xb1, 0x3e, 0x60, 0x1d, - 0x2a, 0xe3, 0xf8, 0xc0, 0xd7, 0x0e, 0xa8, 0xb0, 0xd9, 0x9b, 0xb3, 0x43, 0x55, 0xd1, 0x86, 0x45, - 0xd3, 0x52, 0xeb, 0x6d, 0xf1, 0x45, 0xd9, 0x1d, 0xf6, 0x75, 0x88, 0x54, 0xe1, 0xfb, 0x58, 0xd9, - 0xcd, 0xf6, 0x75, 0xc5, 0xb6, 0x8b, 0x3b, 0x6b, 0x50, 0x6d, 0x0b, 0xeb, 0x3d, 0xe4, 0xb9, 0x7d, - 0x98, 0x5f, 0x51, 0x34, 0xad, 0x68, 0x3a, 0xf5, 0x37, 0xd7, 0x02, 0x76, 0xe5, 0x33, 0xfe, 0xe7, - 0x10, 0x2f, 0x01, 0x45, 0xd9, 0xc1, 0xfb, 0xe5, 0xe0, 0x5d, 0x36, 0xb0, 0x17, 0xd6, 0x50, 0xa2, - 0xba, 0xe9, 0xf4, 0xac, 0x3d, 0xc0, 0x36, 0x55, 0xe8, 0xe5, 0x58, 0xb0, 0xdb, 0xe7, 0xb2, 0xf0, - 0xd2, 0x93, 0x13, 0x6d, 0xc5, 0x8a, 0xea, 0x08, 0x61, 0xdd, 0x60, 0x7c, 0xda, 0xf6, 0xfc, 0xd8, - 0x8f, 0x53, 0x8e, 0x1b, 0xc7, 0x90, 0x3a, 0xed, 0x31, 0x95, 0x6d, 0x5e, 0xac, 0x2c, 0xe5, 0xff, - 0x68, 0x9c, 0x6a, 0x94, 0x3b, 0x9c, 0x64, 0x49, 0xa4, 0x0e, 0xb1, 0xa6, 0xee, 0x15, 0xc4, 0xc8, - 0x25, 0xf0, 0x97, 0x0b, 0x54, 0xc2, 0xb5, 0x7f, 0xfe, 0x5d, 0xcf, 0x30, 0xd8, 0x9d, 0xd4, 0x87, - 0x18, 0x1e, 0xc7, 0x85, 0x0b, 0x51, 0x3a, 0x7d, 0x86, 0x81, 0xe9, 0x1a, 0xd4, 0x5c, 0x86, 0x9c, - 0x54, 0x2a, 0x1e, 0x44, 0x7a, 0xde, 0x2b, 0xb3, 0x67, 0x31, 0xbb, 0x31, 0xaf, 0xd5, 0x58, 0xfb, - 0x45, 0x80, 0xc4, 0x04, 0x62, 0x66, 0x49, 0xe0, 0xfd, 0x75, 0xad, 0x3b, 0xcc, 0x82, 0x44, 0x8b, - 0xa5, 0x2e, 0xeb, 0xb6, 0x7a, 0x53, 0x24, 0x4b, 0x38, 0xb7, 0xcc, 0x51, 0x73, 0xa5, 0xdc, 0x51, - 0xe6, 0xe1, 0xba, 0x9d, 0x1b, 0xd6, 0x58, 0x86, 0x87, 0x66, 0x05, 0xb9, 0x0d, 0x6e, 0xd8, 0x96, - 0xac, 0xbc, 0x2e, 0xcd, 0xbb, 0x02, 0xd5, 0x16, 0x24, 0xbe, 0xa7, 0x41, 0xae, 0x6b, 0x7b, 0x8a, - 0x69, 0xbb, 0x4d, 0xdf, 0x6f, 0x74, 0x66, 0x52, 0xb6, 0xe7, 0x87, 0xcf, 0xaf, 0x20, 0x7e, 0x21, - 0xf9, 0x0e, 0x7e, 0x7e, 0x87, 0xfb, 0x24, 0xad, 0x57, 0x93, 0xe4, 0x5e, 0x0d, 0x62, 0x20, 0x24, - 0x89, 0x03, 0x90, 0xef, 0x2d, 0x7e, 0xbe, 0x01, 0x72, 0x5d, 0x5a, 0xdf, 0x5b, 0x15, 0x6a, 0x3b, - 0xbb, 0xbf, 0xe7, 0x47, 0x2b, 0xd4, 0x6b, 0xe6, 0xc0, 0x49, 0x4d, 0xeb, 0xb9, 0xc2, 0x63, 0x57, - 0x5e, 0x14, 0x66, 0x5d, 0x9b, 0xf5, 0x5c, 0x88, 0x49, 0x6a, 0xf7, 0x13, 0xd4, 0x3d, 0xd4, 0x2b, - 0x06, 0x3f, 0xa2, 0x36, 0x83, 0xfb, 0x6c, 0xa6, 0x92, 0xd1, 0x77, 0x9c, 0xe7, 0xb0, 0xec, 0xa3, - 0xe7, 0x07, 0xae, 0x3c, 0x28, 0x05, 0xb2, 0xb5, 0x97, 0x7c, 0x6e, 0x37, 0xf0, 0x8b, 0xc5, 0x27, - 0xc8, 0x0b, 0xa3, 0x07, 0x90, 0x19, 0x83, 0xdb, 0x66, 0xc8, 0x33, 0xc9, 0x92, 0x8a, 0xea, 0x14, - 0xd8, 0x8d, 0x52, 0x18, 0x97, 0x93, 0x67, 0x30, 0x42, 0xe2, 0x29, 0x74, 0xf0, 0x3c, 0xf3, 0xd0, - 0xa6, 0x9a, 0x28, 0xf1, 0x00, 0xf3, 0x5e, 0xbc, 0x7e, 0xcf, 0x80, 0xb3, 0xaf, 0x3e, 0x68, 0xc5, - 0xeb, 0x18, 0xfc, 0xe0, 0xae, 0x29, 0xc3, 0x38, 0xd7, 0xc1, 0x6d, 0x05, 0xb7, 0x05, 0xf2, 0xe4, - 0x9a, 0xc6, 0xed, 0xba, 0x0f, 0xed, 0x32, 0x1e, 0x47, 0x4e, 0xdc, 0xe6, 0x5c, 0x3c, 0xdf, 0x09, - 0x8a, 0x51, 0xe4, 0x6e, 0x03, 0x9e, 0x3f, 0x88, 0xd7, 0xd7, 0xf1, 0x78, 0x49, 0x89, 0xdb, 0x64, - 0x3d, 0x4f, 0xf6, 0xab, 0xc6, 0xf0, 0x39, 0x42, 0x08, 0x02, 0xef, 0xd8, 0x46, 0xc7, 0x36, 0x7b, - 0x5c, 0x5f, 0xe3, 0xa3, 0xf5, 0xc1, 0x7e, 0x04, 0x5e, 0x30, 0xbe, 0x0e, 0xf0, 0x0b, 0x4f, 0x64, - 0x63, 0xa3, 0xa7, 0xe3, 0xd5, 0xaa, 0xc1, 0x82, 0x1d, 0x15, 0xd8, 0x16, 0x82, 0x70, 0x7a, 0xe6, - 0x73, 0x13, 0xd4, 0xa8, 0xa0, 0x03, 0xf0, 0x1b, 0xb5, 0x33, 0x2c, 0xb2, 0x79, 0x01, 0x75, 0xe7, - 0xac, 0xe4, 0x97, 0x65, 0x43, 0x71, 0x59, 0xd5, 0xaf, 0x88, 0xe8, 0x65, 0xcd, 0x82, 0x39, 0x69, - 0x1b, 0x63, 0x83, 0xcd, 0x0e, 0xab, 0x3b, 0xc3, 0xd8, 0x73, 0x35, 0x43, 0xd8, 0xa0, 0x99, 0xcb, - 0xf5, 0xb0, 0xd1, 0xb6, 0xfa, 0x5c, 0xdf, 0xae, 0x39, 0xc8, 0xde, 0x70, 0xf6, 0xb0, 0xee, 0x1a, - 0x31, 0x3f, 0xd7, 0xa8, 0xec, 0x92, 0x82, 0x1c, 0x79, 0x19, 0xd7, 0x28, 0x4d, 0x5e, 0x2d, 0x54, - 0xcf, 0x1d, 0xf9, 0x23, 0x05, 0x57, 0x35, 0xd0, 0x71, 0x5d, 0x15, 0x95, 0x12, 0xfc, 0xf3, 0x61, - 0x1c, 0xe2, 0xbc, 0xa8, 0x8d, 0x0d, 0x3b, 0x79, 0xc8, 0x1c, 0xb5, 0x71, 0x3d, 0x01, 0x14, 0x3a, - 0x6e, 0xe3, 0x7a, 0x1f, 0x47, 0xc7, 0x6d, 0x48, 0x71, 0xc8, 0x73, 0x3a, 0xec, 0x0b, 0x58, 0x7c, - 0x95, 0x83, 0xf0, 0x48, 0x38, 0xb6, 0x0d, 0xc0, 0xa7, 0x64, 0x1f, 0xdb, 0x3e, 0x8c, 0x73, 0xf3, - 0xb8, 0x1d, 0x5c, 0x22, 0x3a, 0x8e, 0xc3, 0x7e, 0x7d, 0x5c, 0x12, 0x88, 0xdb, 0x06, 0x9e, 0x5f, - 0x8c, 0xdb, 0xb0, 0x1f, 0x36, 0x40, 0x0c, 0xb8, 0x6c, 0x06, 0x55, 0xac, 0xfe, 0x0b, 0x68, 0x8f, - 0xda, 0x7f, 0xda, 0x08, 0x25, 0x7e, 0x23, 0x22, 0xb6, 0x8f, 0x2a, 0x32, 0xd2, 0x0c, 0xf6, 0x80, - 0x2a, 0x73, 0x34, 0xf2, 0xd9, 0x0d, 0x3e, 0xbd, 0x28, 0xa1, 0x0d, 0x2a, 0xb1, 0xd1, 0x9a, 0xe8, - 0xb3, 0xfb, 0xd2, 0x1f, 0x33, 0xa3, 0x4a, 0x79, 0x8f, 0x20, 0x79, 0x78, 0x91, 0xd8, 0x25, 0xaa, - 0xca, 0x38, 0x88, 0x9f, 0xa2, 0x6a, 0x2d, 0x9a, 0x53, 0x19, 0x65, 0xc0, 0xbe, 0x56, 0xc6, 0xd6, - 0x68, 0xa1, 0xec, 0x87, 0x83, 0x8e, 0xf5, 0xc2, 0xb1, 0xa6, 0xde, 0x65, 0x6b, 0xc3, 0xcc, 0x53, - 0x73, 0x94, 0x79, 0xda, 0x90, 0xfa, 0x7a, 0xa5, 0x88, 0x46, 0x06, 0x4b, 0xe3, 0xbd, 0x5e, 0x7c, - 0xd6, 0x42, 0x95, 0x1c, 0x86, 0xb7, 0x47, 0x15, 0x1e, 0x8d, 0x11, 0x6e, 0x4b, 0xb8, 0xf2, 0x6a, - 0xa2, 0xea, 0x1c, 0x7f, 0xd6, 0x10, 0x48, 0x01, 0xe0, 0xd6, 0x24, 0xeb, 0x08, 0x8e, 0xf8, 0xca, - 0x0b, 0xce, 0x23, 0xde, 0xf7, 0xfa, 0x9c, 0x9a, 0xd8, 0xce, 0x42, 0xf5, 0x3e, 0x7d, 0xc4, 0x77, - 0x84, 0x3e, 0x7e, 0xfe, 0xf6, 0xe1, 0xb7, 0xbf, 0x3e, 0xfc, 0xf6, 0xe1, 0x37, 0x7c, 0x63, 0x0d, - 0x5f, 0x39, 0xf2, 0xec, 0x8d, 0x36, 0x4d, 0xab, 0xe4, 0xb6, 0xd2, 0xd7, 0xd4, 0x42, 0x5d, 0x9a, - 0xab, 0x8d, 0x45, 0xee, 0x16, 0x45, 0x33, 0x47, 0xf6, 0x78, 0x8f, 0x67, 0x2e, 0x54, 0xc7, 0x30, - 0x61, 0x0e, 0x0d, 0x03, 0xf8, 0x4a, 0x93, 0xe1, 0xd8, 0x9b, 0xe5, 0x38, 0xb8, 0x6a, 0xf6, 0x35, - 0xf5, 0x07, 0xc3, 0x30, 0xdf, 0xce, 0x6e, 0x30, 0x55, 0x75, 0x6b, 0xab, 0x7b, 0xa6, 0xa6, 0x7e, - 0x49, 0x29, 0xba, 0x33, 0x56, 0x97, 0xf0, 0xc5, 0x55, 0x97, 0x6e, 0xda, 0xd5, 0x1d, 0x73, 0x12, - 0xcd, 0x76, 0xcd, 0x83, 0xfe, 0x35, 0xc5, 0x3c, 0xae, 0x76, 0xd0, 0x13, 0x42, 0xf3, 0xa7, 0xa6, - 0xa7, 0x43, 0xd3, 0xd3, 0x77, 0x5e, 0x5a, 0xb5, 0x4c, 0x03, 0xf6, 0xd5, 0xf4, 0xa5, 0xa7, 0x3b, - 0xd0, 0x89, 0xef, 0xa6, 0xcd, 0x4d, 0x2f, 0x1d, 0xa0, 0xae, 0xa9, 0x96, 0x65, 0x6f, 0xbc, 0xaf, - 0xa9, 0xa5, 0xbd, 0xd4, 0x13, 0xa3, 0x1b, 0xd8, 0x04, 0x36, 0xb2, 0x74, 0x2d, 0x31, 0xb6, 0xb0, - 0x0f, 0x37, 0x06, 0xdc, 0xab, 0xfd, 0x57, 0xe7, 0x46, 0xdb, 0xab, 0xab, 0xf4, 0xd4, 0x34, 0xa6, - 0x80, 0xdd, 0xd4, 0x8b, 0xc8, 0xe0, 0x39, 0x70, 0xbe, 0x95, 0xea, 0x00, 0xae, 0x49, 0x4a, 0x7f, - 0x39, 0x92, 0x71, 0xaa, 0xe3, 0xf9, 0x70, 0x60, 0x9a, 0xfe, 0x2f, 0x98, 0x42, 0xae, 0xff, 0xc5, - 0xad, 0x95, 0xed, 0x9a, 0x01, 0x27, 0x26, 0xe6, 0x4e, 0x1f, 0x43, 0x8f, 0xbd, 0xd5, 0x1d, 0x57, - 0x73, 0x6c, 0xcb, 0x4a, 0x8f, 0xf4, 0xa9, 0xba, 0x35, 0xf1, 0x2e, 0x21, 0x26, 0x04, 0xfc, 0x2a, - 0xc9, 0x1b, 0x86, 0x5e, 0xed, 0x52, 0x74, 0x2a, 0x83, 0xdf, 0x8f, 0xd4, 0xfc, 0x63, 0x3c, 0x1e, - 0x47, 0xd3, 0xbd, 0x31, 0xe1, 0xfa, 0x35, 0xba, 0x92, 0x4e, 0x72, 0x00, 0x2c, 0x32, 0x5f, 0x53, - 0x9b, 0xd5, 0x4a, 0x77, 0x34, 0xd5, 0xd5, 0xcf, 0x38, 0x95, 0x23, 0x9c, 0x0a, 0x64, 0x63, 0xe3, - 0x79, 0xf6, 0x12, 0x43, 0x04, 0x16, 0x58, 0xe6, 0x52, 0x8f, 0x71, 0xd3, 0x36, 0x8e, 0x8b, 0xf7, - 0x5e, 0xd9, 0x66, 0x08, 0x9e, 0x2c, 0x78, 0xb0, 0xd4, 0x91, 0x6e, 0xb9, 0xe7, 0xf2, 0xb4, 0x52, - 0xc7, 0x63, 0x73, 0x69, 0x7c, 0x4d, 0x15, 0x4f, 0xf0, 0x27, 0x4b, 0xfe, 0xc0, 0xb7, 0x13, 0xc9, - 0x32, 0xbc, 0xea, 0x92, 0x42, 0x23, 0x1b, 0x70, 0x00, 0x74, 0x1f, 0x33, 0x04, 0x2f, 0x27, 0x20, - 0x6f, 0x2e, 0x29, 0x4e, 0x7f, 0x14, 0x0a, 0x05, 0x4c, 0x6b, 0x07, 0x96, 0x2e, 0x0d, 0x90, 0x82, - 0x31, 0x20, 0x0a, 0x84, 0xc5, 0xb2, 0x69, 0xa5, 0x1d, 0x2b, 0xc6, 0x6e, 0xe4, 0x98, 0x64, 0x93, - 0x08, 0x9d, 0xc4, 0x51, 0x1f, 0x7c, 0x47, 0xc5, 0x04, 0xb9, 0x8e, 0x84, 0x67, 0xaf, 0x82, 0x93, - 0x58, 0xfa, 0xc4, 0x0b, 0xbe, 0x85, 0x88, 0x9c, 0xaa, 0x0b, 0xe0, 0x92, 0xc9, 0x64, 0xa0, 0xeb, - 0x90, 0x36, 0x97, 0x63, 0x7d, 0x07, 0xe4, 0x8c, 0x37, 0xc0, 0x57, 0x26, 0xdd, 0x8b, 0x6b, 0x80, - 0x1f, 0x83, 0x6b, 0x80, 0x1f, 0x63, 0x26, 0xe0, 0x9b, 0x83, 0x98, 0xcc, 0xa0, 0xd5, 0xd6, 0x29, - 0x67, 0x32, 0x01, 0xba, 0x98, 0x0f, 0xe9, 0x58, 0xcc, 0xa0, 0x63, 0x6c, 0xba, 0x2b, 0x4b, 0x05, - 0x58, 0xe6, 0x92, 0x8c, 0x8d, 0x2c, 0x5b, 0x9b, 0x7f, 0x3b, 0x72, 0x20, 0x4d, 0xe8, 0x8d, 0x0f, - 0x7b, 0xa4, 0xba, 0x87, 0x6f, 0x7d, 0x12, 0x51, 0xc1, 0x5f, 0xd2, 0xb0, 0x9c, 0x28, 0x59, 0x74, - 0xde, 0x50, 0x6c, 0x1f, 0x0b, 0xff, 0xf5, 0x0e, 0xc1, 0x0a, 0x8e, 0xe7, 0x5a, 0xe6, 0x18, 0xb4, - 0x09, 0x9f, 0xf2, 0xc3, 0x6f, 0x78, 0xea, 0x51, 0xd2, 0xc8, 0x57, 0xb0, 0x3a, 0xfa, 0xf0, 0x53, - 0x76, 0xb5, 0xfb, 0x7c, 0x5d, 0x72, 0x1f, 0x40, 0x0f, 0xc9, 0xea, 0xa4, 0xf0, 0xa4, 0x19, 0x40, - 0x1a, 0xa3, 0x9f, 0x0f, 0x91, 0x8f, 0xb9, 0x1a, 0x22, 0xb1, 0x30, 0xc7, 0x63, 0x4b, 0xbf, 0x01, - 0x71, 0x62, 0x11, 0x76, 0x4e, 0x2c, 0x5b, 0x85, 0xb3, 0x11, 0x86, 0x5d, 0x15, 0xdd, 0xa4, 0xa8, - 0x46, 0x70, 0x26, 0x13, 0x6c, 0xc1, 0x12, 0x87, 0x70, 0x6c, 0x0f, 0x4e, 0xf0, 0x89, 0x1e, 0xeb, - 0xc6, 0xe7, 0x68, 0x24, 0x94, 0x92, 0x78, 0x56, 0x8a, 0x7e, 0xc8, 0xba, 0xf1, 0xf6, 0xfa, 0x8e, - 0x28, 0xef, 0x25, 0x0c, 0xa6, 0x18, 0x41, 0x09, 0x25, 0x83, 0x88, 0xfe, 0x2d, 0x36, 0xde, 0x3c, - 0x72, 0xa0, 0x3e, 0x96, 0xb9, 0xd5, 0xf1, 0x25, 0xe4, 0xa4, 0xf1, 0x09, 0x04, 0x25, 0x06, 0x18, - 0xea, 0xec, 0xa9, 0x2d, 0x1a, 0xd9, 0x0e, 0xf0, 0x0b, 0x0e, 0x9d, 0xd0, 0x01, 0x10, 0x06, 0x0c, - 0xe6, 0x8a, 0xf9, 0x0f, 0xa4, 0xfa, 0x94, 0xc8, 0x13, 0xd3, 0xf2, 0x30, 0x84, 0xb1, 0x63, 0xaf, - 0xd2, 0xee, 0x54, 0x1d, 0xdb, 0xfe, 0x27, 0x62, 0xa0, 0xc2, 0x1f, 0xec, 0x33, 0x8e, 0x87, 0x04, - 0x3d, 0x7e, 0x5d, 0xc1, 0x4f, 0xd5, 0xeb, 0x14, 0xd9, 0x23, 0x7a, 0x47, 0x93, 0x74, 0x89, 0xa5, - 0xb9, 0x9c, 0x82, 0xe7, 0xc1, 0x3c, 0x0e, 0x59, 0x8e, 0xc1, 0x25, 0x8e, 0x1a, 0xd2, 0x21, 0xc9, - 0xba, 0x90, 0x61, 0x37, 0x3d, 0xd5, 0x1f, 0x9a, 0xa6, 0x1d, 0xf7, 0xb7, 0x57, 0x89, 0xfd, 0x63, - 0x2b, 0x82, 0x2d, 0x66, 0xf8, 0x46, 0x87, 0xdf, 0xf0, 0x92, 0xd4, 0xf5, 0x57, 0x4c, 0x8c, 0x6b, - 0x90, 0x02, 0xfb, 0x5e, 0x0c, 0x85, 0x3d, 0xa0, 0x41, 0x26, 0x7f, 0x8d, 0x02, 0x5f, 0xa7, 0xd8, - 0x75, 0xdc, 0xe0, 0x56, 0x36, 0x9b, 0x4d, 0x1c, 0x41, 0xd7, 0xf5, 0x2b, 0x00, 0x1e, 0x70, 0x50, - 0xb0, 0xd5, 0x6f, 0x40, 0x08, 0x2c, 0xea, 0xa9, 0x32, 0x04, 0x10, 0xde, 0x5a, 0x96, 0xfa, 0xdd, - 0x5c, 0xe0, 0x0b, 0xf1, 0x2a, 0x71, 0x91, 0xd7, 0x20, 0xe0, 0xcb, 0xe9, 0x2a, 0x48, 0x38, 0xc1, - 0x3e, 0x9d, 0x5e, 0x06, 0x56, 0xec, 0x94, 0xe1, 0x61, 0x0b, 0xc4, 0x5e, 0xfb, 0xb4, 0x55, 0x9d, - 0x4f, 0x30, 0xed, 0xf3, 0x3f, 0xf1, 0x18, 0x16, 0xa8, 0x48, 0xcc, 0xc9, 0x28, 0xee, 0x4c, 0xa5, - 0x53, 0xc1, 0x24, 0x6f, 0xf5, 0x25, 0x55, 0x00, 0x22, 0x7e, 0x8e, 0x7b, 0x46, 0x5e, 0xd8, 0xf3, - 0x39, 0x56, 0xf4, 0x34, 0xb1, 0xe6, 0x67, 0x0b, 0xbe, 0x5d, 0xb7, 0x58, 0x9f, 0x12, 0x18, 0x78, - 0xbb, 0x2f, 0x29, 0x02, 0xfb, 0x3e, 0x5c, 0x6c, 0x42, 0xfb, 0x33, 0x15, 0xa3, 0x97, 0x26, 0xf8, - 0x7d, 0x7e, 0x8f, 0x57, 0xc7, 0x7c, 0x08, 0xef, 0xe8, 0x27, 0x2c, 0x54, 0x28, 0xae, 0x47, 0x15, - 0x71, 0x74, 0x1c, 0xab, 0x6d, 0x2f, 0x95, 0x37, 0x49, 0x1e, 0xdc, 0x15, 0x23, 0xf1, 0x39, 0xe9, - 0x6d, 0x19, 0x26, 0x14, 0xa4, 0x91, 0xbd, 0xc3, 0xd2, 0x4d, 0x3a, 0x03, 0x65, 0x48, 0x43, 0xd7, - 0x85, 0x11, 0xc0, 0x68, 0x03, 0x2a, 0xfe, 0xd7, 0x94, 0xba, 0xf1, 0xec, 0x6f, 0x17, 0xc1, 0xcc, - 0x9b, 0xc7, 0xfa, 0x83, 0x9f, 0x4c, 0x20, 0x96, 0x72, 0x13, 0x42, 0x9d, 0x8e, 0x5d, 0x67, 0xb8, - 0x31, 0x69, 0x13, 0xcc, 0x5c, 0x1b, 0x5c, 0x46, 0x14, 0x59, 0x86, 0x3e, 0x60, 0x61, 0xdb, 0xde, - 0x34, 0xf5, 0x67, 0xea, 0xba, 0x79, 0x4d, 0xca, 0xc3, 0xe4, 0x4b, 0x8a, 0xf9, 0xfc, 0xcf, 0x87, - 0xbc, 0xfb, 0x39, 0xa5, 0x43, 0x10, 0x93, 0x06, 0xff, 0x95, 0x8a, 0x69, 0x9b, 0x8e, 0x03, 0x8a, - 0xd8, 0xb6, 0xc3, 0x7f, 0xe9, 0xc8, 0x8b, 0x44, 0xb8, 0x45, 0xa6, 0x27, 0x19, 0x07, 0xe0, 0x43, - 0x82, 0xd5, 0xbc, 0x6e, 0xab, 0x5e, 0x8d, 0xee, 0xae, 0x04, 0x09, 0x37, 0x75, 0xf3, 0xd2, 0xad, - 0xc7, 0xd6, 0x7a, 0x62, 0xe9, 0xb8, 0x4d, 0xac, 0x7d, 0x1a, 0xe2, 0xe4, 0x85, 0x9b, 0xf0, 0xb5, - 0xb3, 0x8d, 0xeb, 0x99, 0x93, 0x7d, 0x3a, 0x14, 0x9e, 0xc4, 0xc8, 0x31, 0xe2, 0xc0, 0xe4, 0xb4, - 0x57, 0xaa, 0x66, 0x7a, 0x7b, 0x6c, 0xe0, 0x9e, 0xf2, 0x17, 0x36, 0xef, 0xd1, 0x25, 0x62, 0x46, - 0xfc, 0x5f, 0x5a, 0xdf, 0x02, 0x08, 0xf7, 0x4c, 0x3e, 0x5d, 0x4f, 0xdd, 0x63, 0x73, 0x97, 0xf4, - 0x4b, 0x91, 0x43, 0x3a, 0x92, 0x25, 0x8a, 0x95, 0x01, 0x27, 0x6d, 0xbe, 0x3f, 0x19, 0x8a, 0xbb, - 0x4e, 0x82, 0xa3, 0x80, 0xd7, 0x01, 0xa1, 0xc8, 0xa9, 0x2f, 0xe3, 0xa4, 0x3f, 0xc2, 0xad, 0x99, - 0x20, 0x7d, 0x81, 0x99, 0x99, 0xa7, 0x23, 0x7b, 0xa2, 0xd1, 0x4c, 0x3c, 0x9a, 0x2f, 0x26, 0x46, - 0x3d, 0x5b, 0x75, 0x89, 0x46, 0x1d, 0xcf, 0x7f, 0x9d, 0x0b, 0xf9, 0x7c, 0x9e, 0xd8, 0x83, 0x5d, - 0x3a, 0xe4, 0xe1, 0x13, 0x61, 0xe1, 0x99, 0xf7, 0xbf, 0x16, 0xed, 0x84, 0x62, 0xec, 0xa8, 0x63, - 0x73, 0x03, 0x54, 0x0b, 0xa2, 0xd2, 0xa3, 0xca, 0x15, 0x82, 0xf6, 0x85, 0xe4, 0xc4, 0x07, 0xcd, - 0xc4, 0x02, 0x92, 0x0f, 0xa4, 0xe6, 0x8a, 0xd5, 0x19, 0x7c, 0x4a, 0xe7, 0x03, 0x53, 0x17, 0x49, - 0x28, 0x88, 0x18, 0x01, 0x7c, 0xe9, 0xa8, 0x5e, 0x63, 0x63, 0x40, 0x8f, 0x07, 0x77, 0x6a, 0xfb, - 0x27, 0x44, 0xc1, 0xc4, 0x86, 0x54, 0x70, 0xa1, 0x86, 0x38, 0xaa, 0x63, 0xdd, 0x5c, 0x82, 0x5c, - 0xe4, 0xdd, 0x2f, 0xc9, 0x46, 0x2a, 0x83, 0xdf, 0x1c, 0x1d, 0xab, 0xfd, 0x39, 0x4c, 0xdd, 0x71, - 0x6c, 0xe7, 0x02, 0xe8, 0x15, 0x4a, 0x8f, 0x32, 0xb7, 0x77, 0x8b, 0x60, 0xfe, 0x6b, 0xae, 0xef, - 0x27, 0x0e, 0xe4, 0x01, 0x6e, 0x34, 0x8a, 0x8d, 0xa2, 0x63, 0x2f, 0x52, 0x7f, 0x1e, 0xa3, 0x83, - 0x54, 0x82, 0xa7, 0x7f, 0x61, 0xe6, 0x1f, 0x07, 0x03, 0xea, 0xa4, 0x12, 0xa8, 0xfc, 0x75, 0x1a, - 0x96, 0x86, 0x32, 0x8c, 0xc1, 0x46, 0x9a, 0xf3, 0xf1, 0xe3, 0x09, 0x9f, 0xd4, 0x11, 0x98, 0xa3, - 0x4d, 0x90, 0x95, 0x12, 0x0b, 0x95, 0xc5, 0x10, 0xe3, 0x0d, 0xb2, 0x41, 0xe8, 0x4d, 0xf8, 0xc6, - 0x90, 0xcd, 0x9c, 0xc8, 0x12, 0x90, 0x91, 0x13, 0x29, 0xcf, 0xe5, 0x72, 0x97, 0x82, 0xf2, 0x3a, - 0xbb, 0x62, 0xf1, 0x48, 0xc7, 0x8a, 0x60, 0x2e, 0x57, 0x1b, 0xef, 0x7f, 0xbc, 0xfd, 0x4a, 0x7f, - 0x06, 0xb9, 0x30, 0xf4, 0x7f, 0x13, 0x77, 0x19, 0x6a, 0x1c, 0xce, 0x55, 0x54, 0xe8, 0xd6, 0xf4, - 0xf3, 0xf0, 0x2e, 0x93, 0xa1, 0x4f, 0x45, 0x32, 0x68, 0xc6, 0x76, 0x30, 0x0a, 0x53, 0xa2, 0x2f, - 0x57, 0xb9, 0x76, 0x9a, 0xe7, 0x5e, 0x4d, 0xf6, 0x2e, 0xd0, 0xfb, 0x3a, 0xb1, 0xb5, 0x8d, 0x7b, - 0x2d, 0x59, 0xbc, 0x3a, 0xfb, 0x6b, 0x6c, 0x3c, 0x82, 0xc4, 0xc1, 0xd9, 0x2c, 0x97, 0x24, 0x17, - 0x81, 0xbd, 0xb5, 0x39, 0x06, 0x73, 0x6a, 0x5d, 0x23, 0xd3, 0x9b, 0x0d, 0x70, 0xbe, 0xc4, 0x29, - 0xc9, 0x02, 0xc7, 0x18, 0xa9, 0x9f, 0x68, 0x70, 0xcf, 0xc1, 0xff, 0x9f, 0xdf, 0x8b, 0x84, 0x37, - 0xdd, 0x2c, 0x46, 0x27, 0x69, 0x7c, 0xa0, 0xcf, 0x11, 0x2a, 0x41, 0xeb, 0x3a, 0x67, 0x4f, 0x44, - 0x20, 0x30, 0x20, 0x97, 0x48, 0xbe, 0xc2, 0xbf, 0x64, 0x70, 0xf2, 0x18, 0xd9, 0xb4, 0x6b, 0x28, - 0xe3, 0x2a, 0x07, 0x69, 0xbc, 0x93, 0x54, 0x97, 0xec, 0x7d, 0x3f, 0x79, 0x12, 0x7b, 0x45, 0xa4, - 0x49, 0xc4, 0x0c, 0xa1, 0x0b, 0xbf, 0x02, 0xee, 0x7f, 0x87, 0x7e, 0xd7, 0x73, 0xc8, 0xc7, 0x20, - 0x5c, 0xbb, 0x85, 0xb1, 0xfb, 0xd3, 0x64, 0x39, 0x97, 0xf7, 0x10, 0xbf, 0xeb, 0xba, 0x70, 0xa3, - 0x10, 0x74, 0x03, 0x15, 0xc8, 0x91, 0xac, 0x34, 0x84, 0x54, 0xe7, 0xa1, 0xfa, 0x4f, 0x42, 0xd8, - 0x44, 0xa5, 0x89, 0xbf, 0x03, 0xe1, 0xef, 0x09, 0x75, 0xe1, 0x16, 0x53, 0x92, 0x72, 0x9a, 0x66, - 0x32, 0x09, 0xef, 0xeb, 0xe3, 0x1a, 0xca, 0x49, 0xc8, 0x70, 0x1a, 0x53, 0x10, 0x1d, 0x8b, 0xe6, - 0x9c, 0x71, 0x23, 0xb2, 0x5e, 0x39, 0xfa, 0xcc, 0x81, 0x26, 0x62, 0xe0, 0x60, 0x97, 0x91, 0x63, - 0x46, 0x30, 0xce, 0x32, 0xfb, 0x60, 0x7c, 0x05, 0x81, 0x47, 0x40, 0xac, 0x93, 0xb2, 0x59, 0x18, - 0xd2, 0x46, 0xfb, 0x14, 0x42, 0x7f, 0xfa, 0x4a, 0x0e, 0x8b, 0xeb, 0x0e, 0x7f, 0xd0, 0x34, 0x9d, - 0xc8, 0x61, 0xbd, 0xd3, 0x7c, 0xad, 0x78, 0x62, 0x5a, 0x99, 0xd3, 0x93, 0x64, 0x2f, 0x3d, 0xf6, - 0xd3, 0x0d, 0x7d, 0x3c, 0x4d, 0xd8, 0xa2, 0xea, 0xe8, 0xa5, 0xa2, 0x9d, 0x8e, 0x1c, 0x63, 0x90, - 0xfc, 0x4f, 0x9d, 0xe5, 0x18, 0x0b, 0xa6, 0xc7, 0x1b, 0x47, 0x8d, 0x82, 0x42, 0xe2, 0x8e, 0x23, - 0xfb, 0x84, 0x71, 0xc4, 0xbf, 0xb6, 0x9a, 0xde, 0x9a, 0xae, 0x39, 0x32, 0x2d, 0xe2, 0x56, 0xa7, - 0xe6, 0x78, 0xac, 0x2f, 0x13, 0xb3, 0x8e, 0xfa, 0x19, 0xab, 0x67, 0x76, 0x0c, 0x96, 0x00, 0xfe, - 0x3b, 0x21, 0x5a, 0x3a, 0x59, 0x76, 0x3b, 0x8b, 0xba, 0x23, 0x72, 0x3d, 0x9e, 0x0b, 0xdc, 0x59, - 0xed, 0xe1, 0xaf, 0x08, 0xd4, 0x59, 0x89, 0x08, 0x9f, 0xae, 0xf8, 0x9e, 0x0a, 0x51, 0x80, 0xcd, - 0x5a, 0x73, 0xd3, 0xfe, 0x71, 0x7d, 0x98, 0x8e, 0xc4, 0x45, 0x10, 0x3c, 0x7e, 0x91, 0xdc, 0x27, - 0x78, 0x9c, 0xb9, 0xaa, 0x2b, 0x67, 0x11, 0xfc, 0x59, 0x01, 0x27, 0x04, 0x3b, 0x3a, 0x85, 0x9b, - 0x4d, 0x40, 0x82, 0xe6, 0x05, 0x8f, 0xc3, 0x5c, 0xd9, 0x4a, 0xd8, 0xb1, 0x5c, 0x42, 0xd9, 0x82, - 0xaa, 0xf5, 0x1b, 0x21, 0x01, 0x31, 0xdf, 0x57, 0xfa, 0xaf, 0x74, 0x25, 0x15, 0x1d, 0xff, 0x2a, - 0x72, 0xf2, 0xd7, 0xbd, 0xdd, 0xad, 0x71, 0xbf, 0x5b, 0x58, 0xdf, 0x36, 0xde, 0xa4, 0xf8, 0xe5, - 0x3b, 0xb4, 0x52, 0xd0, 0x5a, 0xba, 0xcf, 0x1f, 0xa7, 0x9e, 0xb7, 0xfa, 0x4a, 0x51, 0xbe, 0xef, - 0x3f, 0xf8, 0xd9, 0x07, 0xdb, 0x31, 0xa8, 0x0c, 0xc8, 0x18, 0x9e, 0xff, 0x31, 0x15, 0xfc, 0x16, - 0xfc, 0x47, 0xb0, 0xbd, 0x1f, 0x53, 0x81, 0xae, 0x87, 0x0d, 0x6c, 0xd0, 0x9e, 0x3f, 0x92, 0x63, - 0x7e, 0xfc, 0xf1, 0x7d, 0x65, 0x5b, 0x7b, 0x03, 0x38, 0x4a, 0x5c, 0x24, 0x80, 0x04, 0xd1, 0xc1, - 0xf6, 0x1a, 0xde, 0xf3, 0xf4, 0x97, 0x3c, 0xfd, 0x91, 0xfa, 0xf1, 0x1d, 0xc3, 0xfb, 0x71, 0xf7, - 0x19, 0x30, 0x4d, 0x3b, 0x3a, 0x20, 0xee, 0x9d, 0x8a, 0x49, 0xa8, 0x5b, 0x99, 0x73, 0xf1, 0x39, - 0x9a, 0x90, 0x0c, 0x5d, 0xc0, 0xc1, 0x4e, 0x21, 0x19, 0x17, 0xa5, 0xc3, 0x58, 0x0e, 0x2b, 0xc6, - 0x49, 0xc5, 0x22, 0x01, 0x21, 0xd8, 0xed, 0xeb, 0xc9, 0xc6, 0x57, 0x62, 0x9b, 0xf0, 0x01, 0x02, - 0x21, 0x36, 0x89, 0x81, 0x1c, 0x80, 0x1f, 0x57, 0xfe, 0x2e, 0x14, 0xcd, 0xbd, 0xe5, 0x45, 0x48, - 0xba, 0x11, 0xe8, 0x6d, 0x90, 0xaf, 0xd2, 0x89, 0xba, 0x89, 0xbd, 0x22, 0xbf, 0x70, 0xfe, 0xe7, - 0xdb, 0xe6, 0x23, 0x59, 0x6b, 0x49, 0x78, 0x84, 0x25, 0x38, 0x01, 0xdd, 0xf9, 0xf7, 0xb9, 0x13, - 0xf9, 0x45, 0xd3, 0x93, 0x3f, 0x8d, 0x35, 0x4f, 0x0d, 0x22, 0xa6, 0x7a, 0xf4, 0x43, 0x5f, 0x35, - 0x85, 0xc9, 0xb4, 0xf4, 0x88, 0x15, 0x18, 0xa3, 0x8c, 0x7b, 0x85, 0xd6, 0xa1, 0x1a, 0xe4, 0x03, - 0x6d, 0xbd, 0x26, 0xf7, 0x98, 0x84, 0x13, 0x53, 0xb7, 0xc6, 0xd7, 0x84, 0x3f, 0x39, 0x78, 0xab, - 0xff, 0x06, 0xd5, 0x8e, 0xb1, 0xed, 0x09, 0xed, 0x82, 0x2a, 0xd9, 0xad, 0x35, 0x71, 0x70, 0x69, - 0x2e, 0x97, 0xf8, 0x39, 0xd3, 0x0a, 0xcc, 0x4d, 0x50, 0x7b, 0xfb, 0x92, 0x7a, 0x7d, 0x01, 0x1c, - 0xfc, 0x74, 0x41, 0xea, 0xcf, 0xd4, 0xab, 0x8a, 0x1e, 0x55, 0xd0, 0x8d, 0x13, 0xd3, 0x98, 0x25, - 0xd5, 0x44, 0x42, 0xfd, 0x53, 0x19, 0x8f, 0x1e, 0x48, 0x9c, 0x58, 0xa4, 0x60, 0x1e, 0x7d, 0x7c, - 0x46, 0xf0, 0xb0, 0xdb, 0xb9, 0x49, 0xeb, 0x43, 0x9f, 0x33, 0x37, 0x9e, 0xe8, 0x84, 0xa6, 0xe8, - 0xaa, 0x70, 0x04, 0x53, 0x56, 0xae, 0xe7, 0xde, 0x90, 0xde, 0xd3, 0xda, 0x72, 0xb1, 0x58, 0x4c, - 0x44, 0xce, 0x63, 0x7d, 0xa2, 0x6e, 0xac, 0x10, 0xe7, 0x07, 0xd7, 0xb3, 0x1d, 0x7d, 0xfc, 0x1e, - 0x25, 0x88, 0xc4, 0xf5, 0x6a, 0xea, 0xf2, 0xe0, 0xaa, 0xdb, 0x50, 0x45, 0x7f, 0x11, 0x0e, 0xc1, - 0xe7, 0x75, 0x70, 0xc5, 0x2c, 0x73, 0x09, 0x2e, 0x34, 0xee, 0xcb, 0xc9, 0x69, 0x99, 0xe9, 0x3c, - 0x09, 0x2d, 0x14, 0x92, 0xcf, 0xbc, 0x32, 0xc5, 0x93, 0xca, 0xf4, 0xad, 0x14, 0xe3, 0x8a, 0x76, - 0x1f, 0x1f, 0x78, 0x65, 0x03, 0x75, 0xbc, 0xa5, 0xc8, 0x01, 0x5e, 0x53, 0xd5, 0x9a, 0x5c, 0x88, - 0x46, 0xb0, 0x2a, 0x40, 0x2b, 0x59, 0x2f, 0x1b, 0x9f, 0x3f, 0xb8, 0x4b, 0x1c, 0x22, 0xf2, 0xf4, - 0xc5, 0xe4, 0x31, 0xde, 0x77, 0x8a, 0x90, 0x42, 0x53, 0x3d, 0x88, 0xe4, 0x6f, 0xf9, 0xd6, 0xab, - 0x71, 0xe2, 0x79, 0xa0, 0x91, 0xcd, 0x24, 0x24, 0x37, 0xee, 0x0d, 0xbd, 0xf6, 0xe5, 0xd6, 0xaf, - 0x3d, 0xe4, 0xd2, 0xa6, 0x73, 0xeb, 0x84, 0x34, 0xd8, 0x7f, 0x3c, 0x46, 0x51, 0x56, 0x36, 0x7f, - 0x6d, 0x23, 0xfa, 0x44, 0x53, 0xb6, 0x57, 0x41, 0xbc, 0x1f, 0x42, 0x40, 0x12, 0x62, 0x3d, 0x6e, - 0xd2, 0xfd, 0xa4, 0xfa, 0x75, 0x79, 0xc0, 0x28, 0xf4, 0x4e, 0x3c, 0x67, 0xa1, 0x4f, 0xe1, 0xc3, - 0xfe, 0xf3, 0x37, 0xb9, 0x7a, 0x5a, 0xea, 0x8c, 0x80, 0x86, 0xc1, 0x68, 0x14, 0xd6, 0xe5, 0x5f, - 0x0f, 0x79, 0x5f, 0x91, 0x42, 0x4c, 0xeb, 0xb8, 0xca, 0x77, 0xc4, 0x6f, 0xe5, 0x6e, 0x71, 0x6f, - 0xb0, 0x71, 0xf8, 0x67, 0x75, 0x12, 0x48, 0x1d, 0x7b, 0xe2, 0x75, 0x89, 0x27, 0xb5, 0x31, 0x88, - 0x4b, 0x0e, 0x60, 0xca, 0xc7, 0x95, 0x91, 0x6f, 0x57, 0xe3, 0xc3, 0xdb, 0x5c, 0x89, 0xe8, 0x16, - 0x3e, 0x95, 0x09, 0xb8, 0xf3, 0xff, 0x52, 0xa7, 0xc4, 0xbc, 0x42, 0x84, 0xa0, 0x52, 0x74, 0xc1, - 0xd8, 0xaf, 0xe4, 0x3b, 0x58, 0xba, 0x77, 0x80, 0x48, 0xf8, 0xa0, 0xe3, 0xdc, 0xaf, 0xea, 0xc4, - 0x0b, 0x72, 0xa0, 0xb8, 0x50, 0x7c, 0x77, 0xf7, 0xed, 0x06, 0x3f, 0xaf, 0x67, 0x6b, 0x6f, 0xa1, - 0x73, 0xdc, 0xe2, 0xa2, 0x40, 0x9c, 0x04, 0x70, 0x6d, 0x41, 0xc0, 0xbb, 0xa7, 0xa3, 0x41, 0x39, - 0x11, 0x99, 0xa0, 0x71, 0xac, 0xb4, 0x9f, 0xc4, 0xc9, 0x57, 0x83, 0x91, 0x48, 0x86, 0x89, 0xc3, - 0xcb, 0x86, 0xc1, 0xc6, 0x65, 0x8e, 0x12, 0x3d, 0x7e, 0xcd, 0xe5, 0xc3, 0xa7, 0xaf, 0x61, 0xad, - 0xe0, 0xd6, 0xf0, 0x2b, 0x43, 0xa1, 0x9d, 0x8c, 0x9f, 0xe6, 0x87, 0x41, 0x4d, 0x10, 0xd3, 0x5f, - 0xb5, 0x1f, 0x71, 0x39, 0x74, 0x34, 0x8a, 0xd7, 0x8f, 0x12, 0x97, 0x3b, 0x62, 0xa9, 0x3a, 0x51, - 0x01, 0xdd, 0x38, 0xd5, 0xc2, 0x84, 0x91, 0xbb, 0x69, 0x11, 0xdf, 0x95, 0x9c, 0x16, 0x7e, 0x22, - 0x39, 0x3d, 0xf1, 0xcf, 0x7f, 0x33, 0x42, 0x7c, 0x77, 0x6e, 0x9a, 0xa0, 0x5d, 0xf8, 0x38, 0x2c, - 0x26, 0x85, 0x79, 0x91, 0x7c, 0x93, 0x9f, 0x5c, 0xf0, 0x79, 0x53, 0x90, 0xf5, 0xdd, 0x4a, 0x5d, - 0x8e, 0x83, 0xb0, 0xe1, 0xba, 0xa8, 0xfe, 0x6b, 0xa1, 0x8f, 0x4d, 0x35, 0xa5, 0x5a, 0x56, 0x0a, - 0x66, 0xa6, 0x3e, 0x25, 0x1e, 0x0b, 0x14, 0x72, 0x79, 0xfc, 0xc4, 0xef, 0xcf, 0x8b, 0xa7, 0xbf, - 0x11, 0x21, 0x83, 0x7b, 0x3d, 0xa7, 0xf7, 0x5b, 0x12, 0x37, 0x2d, 0x1e, 0xa6, 0xa7, 0xfb, 0x1e, - 0x31, 0xbb, 0x52, 0xc7, 0x38, 0x0e, 0xbe, 0x85, 0x16, 0x93, 0xc9, 0xd3, 0x21, 0x5e, 0x7f, 0x04, - 0x38, 0xb5, 0xb5, 0xc5, 0x8d, 0x8d, 0xe0, 0x7f, 0x48, 0xa1, 0xc2, 0x3f, 0x48, 0xf5, 0x9d, 0x8a, - 0xfe, 0x86, 0x19, 0xb9, 0xb0, 0x64, 0x2f, 0x61, 0xe7, 0xf1, 0xf3, 0x9d, 0xbd, 0x6c, 0xc0, 0xe7, - 0xa7, 0xcf, 0xf8, 0xef, 0x80, 0xc1, 0xd8, 0xd8, 0xdc, 0xa6, 0x4c, 0xe8, 0xd6, 0xb6, 0x77, 0x29, - 0xcd, 0x52, 0x5d, 0x17, 0x66, 0x04, 0x8f, 0xc5, 0xee, 0x7e, 0xe0, 0x89, 0x38, 0xda, 0xc1, 0x7f, - 0x19, 0x2b, 0x25, 0x0b, 0x0f, 0x0f, 0x0f, 0xdf, 0x29, 0x98, 0x8f, 0x41, 0x2e, 0xed, 0xf0, 0x2f, - 0x60, 0x11, 0x00, 0x67, 0x0b, 0x53, 0x04, 0x05, 0x68, 0x87, 0xbe, 0x89, 0xf9, 0x76, 0xf7, 0xa3, - 0x6b, 0x3b, 0xce, 0xfe, 0x4b, 0x04, 0x2a, 0xb5, 0xd4, 0xf5, 0xb1, 0x9b, 0xaa, 0xa9, 0x5b, 0xb5, - 0x4b, 0xe0, 0xfc, 0x1e, 0x40, 0xfe, 0x4e, 0xc5, 0x80, 0x63, 0xec, 0x42, 0xe0, 0xe1, 0xa5, 0x9d, - 0x3b, 0x82, 0x2d, 0x30, 0x88, 0xfc, 0x1d, 0xb3, 0xc4, 0x38, 0x7e, 0xdc, 0x7d, 0xa5, 0x7b, 0xe4, - 0x2d, 0xf1, 0x4a, 0xd2, 0x1d, 0xb2, 0x14, 0x03, 0x08, 0x29, 0x89, 0xab, 0x75, 0x77, 0x40, 0x1a, - 0xcd, 0x32, 0xb5, 0x39, 0x06, 0x6b, 0x18, 0x96, 0x4e, 0x7a, 0x81, 0x40, 0x31, 0x64, 0xc3, 0xba, - 0xfb, 0xf1, 0xdd, 0x8c, 0x9a, 0xe4, 0x72, 0xcf, 0xdd, 0x8f, 0x7f, 0xfc, 0xb1, 0xd3, 0xe9, 0xe2, - 0xe4, 0xdb, 0x77, 0xca, 0x84, 0xd4, 0x36, 0x81, 0x45, 0xf0, 0x40, 0xf2, 0xee, 0x07, 0x01, 0xf3, - 0x9d, 0x5a, 0xc1, 0xa1, 0x82, 0xdd, 0xae, 0xa2, 0xd0, 0xb4, 0x2e, 0xf6, 0x6f, 0x5a, 0x98, 0x3b, - 0x57, 0x37, 0xcc, 0xa8, 0x99, 0xdb, 0x1b, 0xf6, 0xcc, 0xc5, 0x3b, 0x36, 0xec, 0xee, 0x97, 0xda, - 0xc5, 0x96, 0xb8, 0xf3, 0xe6, 0xa6, 0x0c, 0x53, 0xb8, 0xbd, 0x29, 0x5e, 0xf9, 0xf6, 0x9e, 0x97, - 0x44, 0x6e, 0x84, 0x77, 0x59, 0x6e, 0xee, 0x9a, 0x63, 0xe8, 0x57, 0x68, 0xeb, 0x90, 0xc5, 0x37, - 0x36, 0x8e, 0xb7, 0xf2, 0xcd, 0x25, 0x58, 0x9f, 0x07, 0x30, 0x00, 0x24, 0x23, 0x7f, 0xc0, 0x7f, - 0xdd, 0x2e, 0xf5, 0x9c, 0xfa, 0x48, 0xb9, 0xba, 0x87, 0xef, 0x8f, 0xb9, 0x1f, 0xbf, 0xdd, 0xe2, - 0xec, 0x6b, 0x84, 0xe6, 0xec, 0xe5, 0xc4, 0x34, 0xde, 0x3c, 0x35, 0xe8, 0xe9, 0xa5, 0x6c, 0x69, - 0xa2, 0x3d, 0xd6, 0x6f, 0xf3, 0x37, 0x3b, 0x7e, 0xe5, 0xd0, 0x5c, 0x0a, 0x2f, 0x3e, 0xdf, 0x37, - 0x52, 0xc9, 0x48, 0x93, 0x43, 0x63, 0x43, 0x24, 0x3e, 0x06, 0x33, 0x1d, 0xdf, 0xfd, 0x60, 0x49, - 0x74, 0xb4, 0xd4, 0x5d, 0x17, 0x83, 0x38, 0x55, 0x13, 0xd3, 0x22, 0xf3, 0x4f, 0x71, 0x4a, 0x25, - 0x2e, 0x7a, 0x45, 0xf2, 0x17, 0x88, 0xc2, 0xe9, 0xe2, 0x44, 0xb5, 0x37, 0x82, 0x43, 0xa2, 0x22, - 0x8c, 0x4d, 0x30, 0x06, 0x5b, 0x13, 0x52, 0x4c, 0x71, 0xad, 0x1a, 0x3a, 0x75, 0x0f, 0x7a, 0xb0, - 0x8a, 0xd9, 0x4b, 0x32, 0xf5, 0xf9, 0x6e, 0xb3, 0x1a, 0x83, 0xdb, 0xed, 0x39, 0xaa, 0x69, 0x7d, - 0xf2, 0xa6, 0xa6, 0x0b, 0x63, 0x60, 0x04, 0x9f, 0xef, 0x32, 0xf9, 0xfc, 0x1d, 0xfe, 0x0b, 0x8a, - 0xcf, 0x77, 0xcc, 0x5d, 0x8a, 0xa4, 0xd0, 0x77, 0xa4, 0xe0, 0x7d, 0x97, 0xda, 0xaa, 0xd6, 0x06, - 0x5a, 0x4c, 0xa6, 0x78, 0x97, 0xa2, 0xae, 0x62, 0x14, 0x5a, 0x48, 0xa0, 0x75, 0x44, 0xa3, 0x5b, - 0x9f, 0x26, 0x79, 0x24, 0x49, 0x10, 0x8e, 0x2e, 0x59, 0x81, 0x15, 0x73, 0xb4, 0xe7, 0x3b, 0x75, - 0x84, 0x6f, 0xcb, 0x8d, 0x2c, 0x75, 0x39, 0xc7, 0x70, 0x82, 0x89, 0x57, 0x40, 0x24, 0xf7, 0x4f, - 0x81, 0x39, 0x8d, 0x2e, 0xbe, 0xdc, 0x25, 0x59, 0xc3, 0x61, 0xc7, 0xea, 0xde, 0x25, 0x78, 0x1b, - 0xc6, 0x6c, 0x27, 0xb3, 0x82, 0xaa, 0x76, 0x3c, 0x0b, 0x2c, 0x22, 0x3e, 0x4d, 0xe2, 0x14, 0xd1, - 0x44, 0xff, 0x92, 0xcf, 0xc1, 0xa5, 0xcc, 0xbb, 0x1f, 0x7d, 0xec, 0xa1, 0xf1, 0x1f, 0x98, 0x5c, - 0x2e, 0x75, 0xeb, 0x92, 0xdd, 0x6f, 0x73, 0xac, 0x7f, 0xb1, 0xff, 0x29, 0xff, 0xc8, 0x51, 0x3e, - 0x4d, 0x54, 0xcb, 0xd5, 0x7f, 0x8e, 0x8d, 0xf4, 0x7f, 0x80, 0x8d, 0x89, 0x25, 0xa4, 0xe8, 0x7b, - 0xee, 0x04, 0xa0, 0x33, 0xa1, 0x89, 0x2b, 0xee, 0xd3, 0x47, 0x48, 0x46, 0x21, 0x0c, 0xa1, 0x3f, - 0x7e, 0xfe, 0x16, 0x7b, 0xab, 0x8b, 0x70, 0x28, 0x9c, 0xf3, 0xed, 0x8c, 0xf2, 0xaf, 0xc1, 0x54, - 0xdf, 0x01, 0x53, 0xfd, 0x49, 0x98, 0x5a, 0xf1, 0x6d, 0x98, 0x78, 0xce, 0xcf, 0xc0, 0x9c, 0xd0, - 0xea, 0x9b, 0x30, 0xf1, 0x9c, 0x9f, 0x82, 0x39, 0x21, 0xb7, 0xd2, 0x5f, 0x87, 0x89, 0x6b, 0x00, - 0x37, 0x61, 0xa6, 0x70, 0xe9, 0xfc, 0x1c, 0x30, 0x4d, 0xbf, 0xcd, 0xa8, 0x60, 0x4e, 0x0c, 0xf8, - 0xfb, 0xc8, 0x79, 0x9f, 0x00, 0xbc, 0x8d, 0x30, 0x9e, 0xf3, 0x7e, 0x22, 0x60, 0x2c, 0xde, 0x82, - 0x19, 0xcc, 0xf9, 0x19, 0x98, 0x98, 0xbd, 0x6f, 0xc1, 0xc4, 0x73, 0x7e, 0x02, 0x66, 0x11, 0x9f, - 0xec, 0x0d, 0x98, 0x64, 0xce, 0xbb, 0x61, 0x3a, 0xcb, 0xf1, 0xeb, 0xf0, 0x70, 0x11, 0x20, 0x95, - 0xbc, 0x8e, 0x4d, 0x22, 0xf9, 0xd4, 0xf5, 0xe7, 0xbe, 0x69, 0x86, 0x5c, 0xd4, 0xbb, 0xfb, 0xd1, - 0xb9, 0xaa, 0xeb, 0x24, 0x60, 0x75, 0xad, 0x64, 0x50, 0x17, 0x22, 0x86, 0xeb, 0x98, 0x9a, 0x95, - 0x82, 0xa8, 0x2f, 0x81, 0x5f, 0x50, 0xb5, 0xef, 0x5a, 0xb6, 0xf7, 0x89, 0xc6, 0x40, 0x99, 0x2b, - 0x6e, 0xfb, 0x9d, 0xeb, 0x19, 0xbc, 0x3e, 0xf3, 0xf7, 0xd7, 0x67, 0xf0, 0xfa, 0xec, 0x35, 0xf7, - 0x7d, 0x61, 0xc2, 0x89, 0x85, 0x05, 0x8a, 0x59, 0x10, 0xab, 0x10, 0xa7, 0x9f, 0x7a, 0x97, 0xcb, - 0x0e, 0xaf, 0x56, 0x87, 0xfe, 0x7a, 0x94, 0x8d, 0xfc, 0x75, 0xf8, 0x14, 0xe9, 0x18, 0x16, 0xe3, - 0xa9, 0x41, 0x34, 0x1d, 0x0c, 0xb5, 0x83, 0x9d, 0xce, 0xec, 0x7c, 0xd8, 0x1b, 0xe4, 0x0b, 0xdf, - 0x83, 0x47, 0x13, 0x3f, 0x4a, 0x41, 0xa9, 0xf6, 0x3b, 0x15, 0xb6, 0x8f, 0x23, 0x3c, 0xbe, 0x2b, - 0xf4, 0x7b, 0x72, 0x80, 0x0a, 0xa0, 0xdf, 0x76, 0x99, 0x18, 0x83, 0xf0, 0x32, 0xe1, 0x2d, 0xcf, - 0x78, 0x41, 0x9b, 0x60, 0x7e, 0xca, 0x5d, 0x41, 0x1a, 0x71, 0xc5, 0xbb, 0x05, 0x17, 0xc7, 0xde, - 0x15, 0xd1, 0x64, 0x33, 0xf9, 0xbf, 0x15, 0xd1, 0x74, 0xf1, 0xd6, 0x6f, 0xf8, 0x48, 0x32, 0xe7, - 0x27, 0xa3, 0x9c, 0xff, 0x84, 0x7b, 0xbc, 0x45, 0x3e, 0x5c, 0x24, 0xc4, 0xcf, 0x64, 0xf6, 0xb7, - 0x49, 0x18, 0x9c, 0x38, 0xbc, 0xa6, 0xf7, 0x2e, 0x82, 0xe6, 0xe8, 0xa7, 0xbf, 0x45, 0x50, 0x21, - 0x42, 0xe6, 0x0d, 0xa2, 0xc6, 0xf3, 0xfe, 0x0f, 0x13, 0x76, 0x11, 0x86, 0xe9, 0x6f, 0xd3, 0x34, - 0x73, 0xc5, 0x86, 0x11, 0xe3, 0x81, 0xe7, 0x4c, 0x76, 0x23, 0xfa, 0xc4, 0x8c, 0x78, 0x83, 0xc0, - 0x80, 0x75, 0x71, 0x15, 0xe6, 0xb5, 0x1c, 0x60, 0xb2, 0xb3, 0x4c, 0x97, 0xe8, 0x4e, 0x98, 0xc3, - 0x43, 0xee, 0x9e, 0x98, 0x16, 0xf8, 0xc7, 0x4b, 0x35, 0xec, 0xea, 0xc6, 0x02, 0xdf, 0x55, 0x7b, - 0x2b, 0x42, 0x75, 0x75, 0x03, 0x77, 0xdf, 0x84, 0x9f, 0x98, 0xb7, 0xf1, 0x02, 0x7e, 0xbf, 0xaa, - 0xfe, 0x65, 0x75, 0x6b, 0x3b, 0x10, 0xb0, 0xbe, 0x62, 0x00, 0x48, 0x60, 0xec, 0x36, 0x48, 0x26, - 0x74, 0x4e, 0x75, 0x8c, 0x43, 0x8a, 0xdc, 0x21, 0x74, 0xc1, 0xc6, 0x86, 0x84, 0xbf, 0xb4, 0xcb, - 0xd8, 0xe6, 0xe1, 0x67, 0x53, 0xa7, 0x24, 0x85, 0x54, 0x12, 0xde, 0x03, 0xc3, 0xfe, 0x86, 0x63, - 0x78, 0x15, 0x40, 0xe6, 0x1d, 0x9e, 0xe1, 0x55, 0x00, 0xd9, 0x0b, 0xd7, 0xf0, 0x93, 0x00, 0x72, - 0x18, 0x40, 0x2e, 0x06, 0x10, 0x72, 0xf9, 0x27, 0x81, 0xe4, 0x31, 0x90, 0xfc, 0x2f, 0x60, 0x51, - 0xc0, 0x00, 0x0a, 0xbf, 0x00, 0xe0, 0x11, 0x03, 0x78, 0xfc, 0x05, 0x00, 0x45, 0x0c, 0xa0, 0xf8, - 0x8b, 0x74, 0x78, 0xc2, 0x40, 0x9e, 0x7e, 0x01, 0x0b, 0x26, 0x08, 0x35, 0xe8, 0x5f, 0x01, 0x11, - 0x08, 0xe5, 0xaf, 0x48, 0x25, 0x43, 0xc4, 0x92, 0xc9, 0xfc, 0x22, 0x35, 0x18, 0x22, 0x9c, 0xcc, - 0xaf, 0x48, 0x27, 0x43, 0xc4, 0x93, 0xc9, 0xfd, 0x0a, 0x08, 0x22, 0x9c, 0xcc, 0xaf, 0x48, 0x27, - 0x43, 0xc4, 0x93, 0x29, 0x9c, 0xd3, 0x23, 0xf8, 0x25, 0x82, 0x10, 0x4a, 0xf0, 0x38, 0x03, 0x3f, - 0x3a, 0xc2, 0xc6, 0xa7, 0x1b, 0x3c, 0xf2, 0xc5, 0x36, 0x3d, 0x76, 0x5b, 0x81, 0x27, 0x21, 0xf3, - 0x46, 0xf6, 0xee, 0x2e, 0xb4, 0x4e, 0x3d, 0x52, 0xe2, 0x49, 0xfa, 0xab, 0xb0, 0xe8, 0xd3, 0x0d, - 0xc3, 0x27, 0x77, 0xa5, 0x2e, 0x4f, 0x36, 0x21, 0x0f, 0x7b, 0x60, 0x23, 0xec, 0x63, 0xf0, 0x20, - 0x31, 0x8f, 0x04, 0x97, 0x08, 0xb1, 0xe4, 0x92, 0x29, 0x58, 0x7e, 0x30, 0x70, 0x29, 0xa6, 0x90, - 0xd2, 0xa0, 0xd7, 0x55, 0xb7, 0x3a, 0x29, 0x19, 0xbb, 0xa1, 0xd9, 0x7e, 0x08, 0x81, 0xe0, 0xa5, - 0x6f, 0x9f, 0x2b, 0xa0, 0x48, 0x4a, 0xdb, 0x6b, 0xd6, 0xeb, 0x07, 0xd3, 0xf6, 0xb7, 0x0e, 0xc6, - 0x0d, 0xff, 0xf6, 0xc1, 0xca, 0xa6, 0xe3, 0x7a, 0xa9, 0x15, 0xc1, 0xe1, 0x6b, 0x2a, 0x11, 0x0d, - 0x00, 0x3e, 0xee, 0x65, 0x08, 0x10, 0xe0, 0x15, 0xdc, 0x9e, 0x38, 0xd6, 0x83, 0x88, 0x8b, 0x67, - 0x72, 0x47, 0x67, 0x7e, 0x17, 0x42, 0x6f, 0xa8, 0x37, 0x81, 0xeb, 0xef, 0x02, 0x9e, 0x89, 0x80, - 0xe7, 0x63, 0xe0, 0xd9, 0x08, 0x38, 0xae, 0xb3, 0xa6, 0xf0, 0x25, 0xcd, 0xeb, 0x1b, 0x78, 0xef, - 0xda, 0x80, 0x7e, 0x88, 0xb6, 0x28, 0xe4, 0x1f, 0xf2, 0x38, 0x6f, 0xd2, 0x57, 0xb8, 0x97, 0x39, - 0x1e, 0x06, 0x66, 0xfc, 0x70, 0xc3, 0x2d, 0x13, 0x97, 0x67, 0xce, 0x76, 0x7b, 0xe7, 0x76, 0x6f, - 0x6c, 0x46, 0x3f, 0x3c, 0xc2, 0x66, 0xaf, 0x97, 0xb5, 0x82, 0x1a, 0xfb, 0xc8, 0xf6, 0x02, 0xb1, - 0xc0, 0x5f, 0x2e, 0x35, 0x10, 0xff, 0x92, 0xa9, 0xb9, 0x9c, 0x27, 0xb5, 0xcf, 0x5e, 0xe9, 0xcb, - 0x9e, 0x3a, 0x82, 0xd0, 0xe5, 0x56, 0xd5, 0x33, 0xcc, 0x52, 0x6e, 0x14, 0x5b, 0x71, 0xcd, 0xec, - 0x46, 0xb1, 0xf5, 0xed, 0x4d, 0x99, 0xbf, 0x55, 0x6a, 0x0d, 0xf3, 0x91, 0xbf, 0xbd, 0x6b, 0xe6, - 0xd6, 0xae, 0xd9, 0xdc, 0xe8, 0x95, 0x5a, 0x7a, 0xa8, 0xc7, 0x7f, 0x7b, 0xdb, 0xec, 0xad, 0x6d, - 0xe9, 0x9c, 0x76, 0x7b, 0xdb, 0x38, 0xfa, 0xba, 0x51, 0x59, 0x4e, 0x04, 0x6a, 0xe4, 0x17, 0x42, - 0xae, 0x54, 0x22, 0xe3, 0x5f, 0xec, 0x3e, 0x8e, 0x7d, 0x88, 0xff, 0x09, 0x95, 0x0f, 0xbf, 0x51, - 0xff, 0xfc, 0xfd, 0xc3, 0x6f, 0xff, 0x4c, 0x99, 0x8e, 0xfd, 0x30, 0x73, 0x53, 0xdb, 0xdc, 0x43, - 0xee, 0x81, 0xc6, 0x1d, 0x19, 0x9a, 0x29, 0xa4, 0xe1, 0xed, 0x29, 0x55, 0x23, 0xbf, 0x11, 0x52, - 0x52, 0x97, 0xa6, 0x6e, 0xe1, 0x91, 0x86, 0xa9, 0x41, 0x94, 0xaf, 0x8f, 0x53, 0x9b, 0x25, 0x44, - 0xe2, 0x29, 0xb1, 0xdd, 0x48, 0x65, 0x82, 0x35, 0x86, 0xe9, 0x4d, 0x37, 0xa3, 0x07, 0xcd, 0x5e, - 0x50, 0x33, 0x15, 0xaf, 0xa2, 0x02, 0xb0, 0x30, 0x46, 0x7d, 0xf8, 0xed, 0xf7, 0xe8, 0xdf, 0x6b, - 0xf9, 0xe4, 0x7d, 0xd1, 0x3f, 0xff, 0x79, 0x67, 0x8f, 0x66, 0xc0, 0xc8, 0xbb, 0xe7, 0x67, 0xac, - 0x13, 0xf6, 0x24, 0xa5, 0xef, 0xf0, 0xd5, 0x28, 0xf7, 0x1f, 0xff, 0xb8, 0xc3, 0x80, 0x27, 0xe6, - 0x12, 0x92, 0xb8, 0xdf, 0xa3, 0x41, 0x30, 0xf1, 0x1b, 0x4b, 0xff, 0xef, 0xe0, 0xe3, 0x21, 0x9c, - 0xfa, 0x0c, 0xb9, 0xef, 0xd7, 0xbb, 0x08, 0xec, 0x11, 0x52, 0xb0, 0xfa, 0x1f, 0xff, 0x08, 0x3e, - 0x1f, 0xd4, 0xc5, 0xf8, 0xbf, 0x83, 0xaf, 0x9f, 0xf4, 0xcf, 0x5f, 0xbd, 0x07, 0x40, 0x0a, 0xaf, - 0xfc, 0x8b, 0x64, 0x23, 0x5f, 0x62, 0xac, 0x00, 0xa5, 0x8d, 0xab, 0x83, 0x02, 0x3a, 0x26, 0xa0, - 0xf5, 0x6d, 0xab, 0x3a, 0x29, 0xed, 0x39, 0x31, 0xfa, 0xd7, 0x17, 0xf3, 0x19, 0xde, 0xa6, 0xcf, - 0xff, 0xf3, 0xef, 0x2f, 0x1b, 0x78, 0xfb, 0x16, 0xff, 0xfb, 0x33, 0xab, 0xe0, 0x44, 0x78, 0x85, - 0xfd, 0x65, 0xf9, 0xc5, 0xf9, 0x62, 0x7e, 0x71, 0x9f, 0x55, 0xc7, 0xd8, 0x10, 0x91, 0xf9, 0xa2, - 0x3e, 0x6f, 0xbe, 0x4d, 0x6c, 0xe7, 0x93, 0x79, 0xec, 0x7b, 0xb0, 0xf4, 0xa5, 0xe1, 0x4d, 0xbf, - 0xfd, 0x7f, 0xce, 0xae, 0xc5, 0xb9, 0x6d, 0x1b, 0xe9, 0xff, 0x2b, 0x11, 0x2f, 0xf5, 0x10, 0x16, - 0xf4, 0x72, 0xbf, 0x66, 0x5a, 0x2a, 0x18, 0x4e, 0xe2, 0x24, 0x4d, 0xee, 0xf2, 0xe8, 0xc5, 0x69, - 0x7a, 0x77, 0x8e, 0xeb, 0xa1, 0x29, 0x4a, 0x62, 0x4d, 0x93, 0x3a, 0x92, 0x92, 0xa5, 0x8a, 0xfa, - 0xdf, 0xbf, 0xdf, 0x2e, 0xf8, 0x80, 0x24, 0x3a, 0xed, 0x75, 0xda, 0x88, 0x24, 0x1e, 0x0b, 0x60, - 0xdf, 0xbb, 0x00, 0xe9, 0xb3, 0xa7, 0x61, 0xaf, 0x37, 0x16, 0xf3, 0xfe, 0x62, 0x99, 0xcd, 0xed, - 0xec, 0x32, 0xbc, 0x12, 0xdc, 0x14, 0x93, 0x8f, 0x97, 0x51, 0xd4, 0x51, 0x41, 0xdf, 0x9f, 0x87, - 0xd1, 0x24, 0x0d, 0xe2, 0x93, 0x13, 0x7b, 0x5e, 0x76, 0x2b, 0x8a, 0xb2, 0x43, 0x53, 0x2b, 0xe4, - 0x04, 0xb1, 0x7c, 0x1e, 0x3c, 0x32, 0x8a, 0xc6, 0x55, 0xfb, 0xb1, 0x08, 0xa7, 0xb6, 0x1d, 0x2b, - 0xf4, 0x4a, 0x16, 0xb6, 0x10, 0x27, 0x27, 0xab, 0x24, 0x9c, 0x3c, 0x1a, 0x76, 0x94, 0x8a, 0xa9, - 0x48, 0xe8, 0xd9, 0xc5, 0x55, 0x73, 0x73, 0x4e, 0x31, 0xcf, 0x09, 0xbe, 0x7c, 0x00, 0xad, 0x92, - 0x44, 0x81, 0x67, 0xe0, 0x99, 0xe6, 0x14, 0x2b, 0x9a, 0xa8, 0x90, 0x76, 0xaa, 0x1a, 0x4a, 0xd4, - 0x64, 0xcb, 0x05, 0x35, 0x41, 0x03, 0x8c, 0xe4, 0x42, 0xd7, 0x59, 0x4e, 0xa5, 0xfb, 0x1a, 0x20, - 0x28, 0xbf, 0xc8, 0xe9, 0xb0, 0xa6, 0x1d, 0x83, 0x9a, 0x19, 0xdf, 0x36, 0x10, 0x68, 0x8c, 0x54, - 0x75, 0x46, 0x42, 0xc8, 0xf4, 0xe4, 0x24, 0x71, 0xbd, 0x4b, 0xaf, 0x9c, 0x66, 0x6f, 0x74, 0xd5, - 0x55, 0xb1, 0xe3, 0x29, 0xa5, 0x96, 0xae, 0xa7, 0x2e, 0xe3, 0x2b, 0xc7, 0x2b, 0xe7, 0x2c, 0x64, - 0xa2, 0x52, 0xa6, 0x60, 0xa4, 0xe2, 0xe0, 0xfe, 0x91, 0x3f, 0x4e, 0x83, 0x7c, 0x99, 0xc6, 0x8f, - 0xa2, 0x7e, 0x0c, 0x5f, 0xe1, 0x3d, 0xfd, 0x25, 0xa6, 0x5c, 0x46, 0x35, 0xaa, 0x94, 0x87, 0x07, - 0x2f, 0xc7, 0xd0, 0x90, 0xb0, 0x20, 0x53, 0x7a, 0xc6, 0x81, 0xab, 0xd1, 0xe4, 0x04, 0xa8, 0xbd, - 0x0d, 0x36, 0x47, 0xc5, 0x54, 0x28, 0xa3, 0x5d, 0xcd, 0x07, 0xef, 0x34, 0x1f, 0x10, 0x36, 0x99, - 0x17, 0x10, 0xc5, 0x3f, 0x0a, 0x44, 0x7e, 0x99, 0x5c, 0xa9, 0x00, 0x3f, 0xd5, 0x24, 0xf2, 0xa6, - 0x47, 0xa4, 0x7b, 0x68, 0x5a, 0xe7, 0x58, 0x6a, 0x0b, 0x37, 0xe7, 0x6e, 0xae, 0xf9, 0xd6, 0x5f, - 0xa6, 0x74, 0x46, 0x55, 0x05, 0x62, 0x47, 0xe0, 0x03, 0xd5, 0xd2, 0xf8, 0x27, 0x84, 0x5a, 0x61, - 0x16, 0xb8, 0xe5, 0xb5, 0x0f, 0x33, 0x98, 0x44, 0x2b, 0x70, 0x7b, 0x3f, 0x9f, 0x07, 0x71, 0xff, - 0x26, 0x8c, 0x27, 0xf6, 0x51, 0x9d, 0x70, 0x60, 0x2b, 0xc9, 0x74, 0x26, 0xcb, 0x5c, 0x4e, 0xd5, - 0x80, 0xb6, 0x86, 0x8b, 0x60, 0x6d, 0xbb, 0x4e, 0x56, 0xcc, 0x8a, 0xb8, 0x58, 0x14, 0x8f, 0x45, - 0x91, 0x2e, 0xe6, 0x45, 0x72, 0x9f, 0x15, 0x77, 0xb1, 0x5f, 0xc4, 0xf9, 0x7d, 0x01, 0x71, 0xba, - 0xf4, 0xe7, 0x57, 0xc5, 0xef, 0x49, 0x52, 0xfc, 0x9a, 0xa4, 0x93, 0x41, 0x08, 0xa4, 0x9b, 0x52, - 0x91, 0xd9, 0xb9, 0xd8, 0x76, 0xf2, 0xfe, 0xf5, 0x24, 0x4c, 0xf3, 0x0d, 0xd6, 0x56, 0xdd, 0xaa, - 0xce, 0x10, 0x6c, 0x31, 0x52, 0x2a, 0xd1, 0xe4, 0x22, 0x26, 0x09, 0x40, 0xb4, 0x06, 0x2d, 0x24, - 0x6e, 0x15, 0x16, 0xf3, 0x71, 0x4e, 0x0d, 0x89, 0x71, 0xc7, 0xa2, 0x81, 0xf6, 0x06, 0xdd, 0x9a, - 0x0e, 0x17, 0x1a, 0x8f, 0x15, 0x82, 0xfb, 0xfa, 0xc3, 0x0d, 0xe1, 0xef, 0xc1, 0xe4, 0x7d, 0x45, - 0x6f, 0x10, 0xae, 0x28, 0xf2, 0x9a, 0xfe, 0xfd, 0x3c, 0x79, 0x4b, 0xbb, 0xc0, 0xe7, 0x5e, 0x06, - 0x0c, 0x50, 0xed, 0x7e, 0x49, 0x03, 0xfb, 0x25, 0xad, 0x43, 0x23, 0xfc, 0x9d, 0x0d, 0x05, 0x90, - 0x1b, 0x9c, 0x02, 0xc9, 0x68, 0x98, 0x28, 0xaf, 0x6f, 0x99, 0xf5, 0x12, 0x65, 0x8c, 0x56, 0x9e, - 0xa0, 0x04, 0xea, 0x17, 0xd9, 0x18, 0xd2, 0x58, 0x0b, 0x60, 0x22, 0xaa, 0x85, 0xc6, 0xc4, 0x2e, - 0x89, 0xd0, 0x35, 0x34, 0x21, 0x70, 0x34, 0xb0, 0x46, 0x17, 0x95, 0xe0, 0x47, 0x54, 0xfc, 0x13, - 0x34, 0x73, 0x7b, 0xdf, 0xcc, 0x2d, 0xef, 0xeb, 0x23, 0xcc, 0xb4, 0xe0, 0x31, 0xf4, 0x07, 0xd1, - 0xf7, 0x2e, 0x59, 0x05, 0xe7, 0x34, 0xa7, 0x3d, 0x64, 0xad, 0x08, 0x59, 0x92, 0x35, 0x95, 0xd8, - 0x62, 0x2e, 0x16, 0xdb, 0x1d, 0x9a, 0xa6, 0x45, 0xc3, 0xd2, 0x98, 0x4a, 0x97, 0x59, 0x42, 0x5a, - 0xe0, 0x71, 0x2e, 0xd6, 0x4a, 0xe0, 0x11, 0xb5, 0x4f, 0x83, 0xa9, 0x2e, 0x8a, 0x6c, 0x40, 0x61, - 0xf1, 0x8f, 0xec, 0x58, 0xe6, 0x46, 0x13, 0xdd, 0xbd, 0xc3, 0x48, 0x4f, 0x49, 0xfd, 0x58, 0x9c, - 0x7d, 0xd6, 0xdd, 0x68, 0x50, 0xc8, 0x75, 0x9b, 0xac, 0x1f, 0x95, 0x25, 0x45, 0x01, 0xc6, 0xe1, - 0xce, 0x7d, 0x3f, 0xcb, 0x3e, 0x05, 0xeb, 0x5c, 0xc5, 0x45, 0x61, 0x61, 0x6a, 0xd4, 0xfc, 0xd0, - 0x88, 0xc4, 0x7a, 0x45, 0x47, 0x50, 0x6a, 0x2c, 0x87, 0x1a, 0xcb, 0x7c, 0x89, 0x1b, 0xd8, 0xd0, - 0x72, 0xd0, 0x4e, 0x5a, 0xf9, 0xd6, 0xcd, 0x62, 0x61, 0xd6, 0x1e, 0x29, 0x2e, 0x94, 0x9e, 0x9c, - 0x74, 0xc0, 0xc7, 0x6a, 0xda, 0x07, 0x2f, 0xe4, 0x76, 0x28, 0x5c, 0x2a, 0xec, 0x5a, 0x8b, 0xb5, - 0xe5, 0xd0, 0xdd, 0x6e, 0x57, 0x23, 0x64, 0x42, 0xce, 0x79, 0x9a, 0x2c, 0xb3, 0x68, 0x73, 0x41, - 0x99, 0xb2, 0x38, 0x48, 0x5f, 0x7f, 0x7a, 0xf7, 0x56, 0x23, 0x24, 0x66, 0xe9, 0x08, 0xab, 0x42, - 0x68, 0xe2, 0xeb, 0x6b, 0xfa, 0xa2, 0x0d, 0xaf, 0xb3, 0x41, 0x6a, 0x62, 0x11, 0x57, 0x0c, 0x31, - 0xaa, 0x15, 0xf3, 0xed, 0xe8, 0x4a, 0x53, 0x1f, 0x96, 0x10, 0xa8, 0x06, 0xd9, 0x88, 0xe8, 0x8b, - 0xc8, 0xf3, 0x03, 0x7b, 0x70, 0xee, 0x2d, 0xc0, 0x2d, 0xc1, 0xe3, 0x81, 0x04, 0x0c, 0x00, 0x39, - 0x64, 0xf0, 0x7e, 0xb6, 0xbc, 0xd1, 0x68, 0x82, 0x4b, 0x24, 0x63, 0x37, 0x21, 0xe9, 0xf0, 0x26, - 0x93, 0x97, 0xf4, 0x4a, 0xe4, 0xdb, 0x10, 0x7e, 0x28, 0x66, 0x63, 0x07, 0x72, 0x22, 0x33, 0x52, - 0x3e, 0x9a, 0x99, 0xda, 0x2a, 0x25, 0x09, 0x76, 0x54, 0x96, 0x65, 0x8c, 0xd2, 0xe6, 0x11, 0x16, - 0x53, 0x88, 0xcb, 0xe0, 0x4a, 0xc5, 0x0d, 0x2a, 0x38, 0xff, 0xd5, 0x61, 0x4e, 0xb3, 0x08, 0x97, - 0xe5, 0x7d, 0x07, 0xfa, 0x3d, 0x20, 0xb4, 0x83, 0xa5, 0xf3, 0x74, 0xb3, 0xcd, 0xb9, 0x5b, 0x69, - 0x3b, 0x60, 0x39, 0xe2, 0x9d, 0xef, 0xe5, 0x3e, 0xa9, 0x8b, 0xed, 0x4e, 0x2b, 0xcc, 0x98, 0xd0, - 0x4f, 0x46, 0x0c, 0x78, 0xca, 0x16, 0x41, 0x14, 0x71, 0x7c, 0x63, 0x95, 0x92, 0xae, 0x67, 0xfc, - 0xac, 0x92, 0x55, 0xe8, 0x50, 0x9e, 0x02, 0x23, 0xcc, 0x53, 0x34, 0xd8, 0x11, 0xce, 0x7e, 0x5d, - 0x93, 0xfb, 0xe6, 0xb8, 0x25, 0xce, 0xca, 0xc1, 0x8b, 0x82, 0x89, 0x1c, 0xbb, 0x9e, 0x7b, 0x04, - 0xf5, 0xfd, 0x85, 0x6d, 0x1d, 0xbf, 0x7f, 0x31, 0xfa, 0xe1, 0x87, 0x1f, 0x06, 0x0c, 0xcb, 0x92, - 0x07, 0x68, 0x6f, 0x70, 0x69, 0xce, 0xcc, 0x69, 0xb1, 0x9d, 0xc4, 0x14, 0x34, 0x22, 0x74, 0xf3, - 0x5f, 0x1f, 0x4e, 0xc6, 0x34, 0xa0, 0x09, 0x02, 0x54, 0x8b, 0x85, 0x46, 0x05, 0x14, 0x65, 0x2d, - 0xfa, 0x5a, 0xa8, 0x1a, 0x25, 0x41, 0x2a, 0xa3, 0xd6, 0xa7, 0xf0, 0x93, 0x0c, 0x9a, 0x5e, 0xe6, - 0x7d, 0x9a, 0xe2, 0x15, 0x29, 0x15, 0x42, 0xe6, 0x6b, 0xf2, 0x88, 0x3e, 0xa9, 0xa1, 0x9c, 0xc1, - 0x4e, 0xcb, 0x6b, 0xfc, 0x34, 0x46, 0xe0, 0xa7, 0x03, 0x4d, 0xfe, 0xba, 0x9f, 0xcd, 0xc3, 0x69, - 0xae, 0x75, 0x39, 0x1c, 0xc5, 0x45, 0x12, 0x83, 0xa3, 0x5e, 0x84, 0x93, 0x77, 0xc9, 0x32, 0x86, - 0x01, 0x6c, 0x29, 0x34, 0x75, 0xf1, 0xb3, 0x46, 0x75, 0xc9, 0x50, 0x6c, 0x3f, 0x75, 0xbb, 0xe0, - 0xb7, 0x99, 0xd2, 0xec, 0x90, 0x1a, 0x7e, 0x4d, 0xda, 0x4f, 0xee, 0x31, 0xd7, 0x8b, 0xcf, 0x3f, - 0xbe, 0x8c, 0x02, 0x72, 0xb9, 0x30, 0xaf, 0xda, 0xca, 0x76, 0x6c, 0xeb, 0xfa, 0x1a, 0x11, 0xa2, - 0xe7, 0xe7, 0xa4, 0xca, 0xaf, 0x2d, 0x66, 0x3a, 0x31, 0xd6, 0xa2, 0xf4, 0x73, 0x3d, 0x46, 0x58, - 0xab, 0x5c, 0x80, 0xce, 0x0c, 0xfd, 0x4a, 0x03, 0x9c, 0x9c, 0xa4, 0x7d, 0x3a, 0xf6, 0x14, 0x4f, - 0xb4, 0x86, 0x85, 0x20, 0xf4, 0x7a, 0x9f, 0x30, 0x1d, 0x42, 0x80, 0x0c, 0x8b, 0x02, 0x4b, 0x17, - 0x32, 0x6b, 0xe6, 0xfe, 0xb3, 0xa9, 0x76, 0x59, 0xc7, 0xc0, 0x07, 0xc9, 0xd4, 0x8c, 0xcc, 0x41, - 0xe9, 0xec, 0x41, 0x20, 0x2a, 0x27, 0xab, 0x66, 0x03, 0xf0, 0x32, 0xe9, 0x63, 0x52, 0xc5, 0xa5, - 0x56, 0x53, 0x46, 0xd5, 0x91, 0x56, 0x0a, 0x44, 0x45, 0x34, 0x03, 0x1b, 0xe0, 0x80, 0x45, 0x14, - 0xe6, 0xa4, 0x3c, 0x09, 0xc5, 0xcd, 0x42, 0xc0, 0x62, 0x64, 0xa1, 0x6b, 0x9c, 0x93, 0xb2, 0x76, - 0xb5, 0xd9, 0xfa, 0x4c, 0xd1, 0x27, 0xcf, 0xc9, 0x36, 0x0a, 0xa0, 0xac, 0x1c, 0xb8, 0x8a, 0x93, - 0xc4, 0x67, 0x47, 0xb6, 0xef, 0x03, 0x8b, 0x79, 0x40, 0x80, 0x09, 0x1c, 0x38, 0x59, 0xe6, 0xdc, - 0xc1, 0x1c, 0xc2, 0x7c, 0xaa, 0x64, 0x4d, 0xe3, 0x2c, 0x84, 0xcd, 0x90, 0x1f, 0x80, 0x17, 0x78, - 0x04, 0x40, 0x56, 0xd8, 0xdf, 0x23, 0x0b, 0xfc, 0x04, 0x19, 0x32, 0x51, 0xe0, 0x9e, 0x49, 0x1f, - 0x82, 0x5a, 0x99, 0x53, 0x42, 0x59, 0x8b, 0xf3, 0xe3, 0x57, 0x6b, 0x37, 0x23, 0x0d, 0xc2, 0xb8, - 0xc6, 0x77, 0xaa, 0x78, 0xf5, 0xcd, 0x6a, 0xe1, 0xd0, 0xa7, 0xa0, 0x40, 0x2e, 0x59, 0x1d, 0x98, - 0x35, 0xe7, 0x08, 0xcf, 0xf2, 0x74, 0xe9, 0xe7, 0x49, 0xca, 0xae, 0x41, 0x35, 0xb0, 0x8c, 0xe0, - 0x2a, 0xfa, 0xea, 0x25, 0x16, 0xca, 0xa6, 0x62, 0x8c, 0x7e, 0x9d, 0x88, 0x5d, 0xd4, 0xb4, 0x7f, - 0xad, 0x97, 0x79, 0x5e, 0x01, 0x01, 0x8f, 0x47, 0x28, 0xf6, 0x1f, 0x80, 0x45, 0x7d, 0xa9, 0x6b, - 0x07, 0x92, 0x97, 0x1a, 0x63, 0x0b, 0xd7, 0xfe, 0xcd, 0x4e, 0xa5, 0x2f, 0xbf, 0xe5, 0xa9, 0xcb, - 0x1c, 0x30, 0xe8, 0xef, 0x4c, 0x12, 0xe2, 0x31, 0x9a, 0x87, 0x2e, 0x6f, 0x61, 0x6a, 0x50, 0x9e, - 0x95, 0xbe, 0x77, 0xaa, 0x3e, 0xda, 0xc6, 0x1c, 0x7d, 0x99, 0x30, 0x19, 0x3a, 0x69, 0x3f, 0x06, - 0x61, 0x9e, 0xa3, 0x2f, 0xcd, 0xb0, 0x7e, 0x60, 0xb6, 0xd3, 0x3d, 0xf5, 0x40, 0xa3, 0xfd, 0x81, - 0x24, 0x62, 0xb0, 0x1c, 0x5c, 0x83, 0x8b, 0x9d, 0x19, 0x13, 0xe3, 0x3e, 0x20, 0x57, 0x26, 0xc9, - 0x1d, 0x17, 0x8d, 0x3b, 0x5b, 0xa3, 0x99, 0xe8, 0x32, 0x53, 0x56, 0xb6, 0x22, 0x26, 0x55, 0x3e, - 0xd8, 0x13, 0x48, 0x0a, 0xc2, 0x59, 0xfc, 0x41, 0x9b, 0x69, 0x00, 0xf5, 0x4f, 0x4e, 0x66, 0xc0, - 0x60, 0xe9, 0xf1, 0xfb, 0xb0, 0x1e, 0x1d, 0x70, 0x5d, 0x87, 0x1c, 0x38, 0x9f, 0x22, 0x13, 0xdb, - 0x53, 0xbe, 0xb4, 0x23, 0x35, 0x73, 0x0f, 0x78, 0xac, 0x94, 0xe3, 0x76, 0x0d, 0x58, 0xbd, 0xf0, - 0x66, 0x49, 0x4f, 0x38, 0xed, 0x1d, 0x6d, 0x4f, 0x88, 0x36, 0xbf, 0xd0, 0x03, 0x17, 0x44, 0xe0, - 0x43, 0xad, 0xa7, 0xc6, 0x79, 0x7f, 0x4a, 0xe9, 0x2f, 0xe6, 0x4f, 0xc4, 0x4d, 0x7b, 0x22, 0x6e, - 0xd6, 0x89, 0xf1, 0x5f, 0x61, 0x72, 0xd6, 0x97, 0x73, 0x15, 0x1a, 0x80, 0x10, 0x4b, 0x1e, 0xf0, - 0xbd, 0x5c, 0x18, 0x61, 0x5f, 0xa5, 0x1d, 0x10, 0xe4, 0x88, 0xed, 0x51, 0x53, 0x98, 0xd7, 0xda, - 0x65, 0x99, 0xa2, 0xb2, 0xf1, 0x4c, 0xe5, 0x04, 0x5e, 0x49, 0x19, 0xcf, 0x4d, 0x28, 0x9e, 0x5b, - 0x5e, 0x4e, 0x2f, 0x27, 0x57, 0x7d, 0x4a, 0x07, 0x5c, 0x29, 0xbe, 0xe5, 0xfc, 0xd2, 0x4e, 0x53, - 0xb1, 0x73, 0x7d, 0x72, 0xb2, 0x60, 0xa7, 0x5c, 0x2d, 0xca, 0x6e, 0x8d, 0x33, 0x56, 0x0b, 0xd8, - 0x82, 0x9d, 0x0f, 0xad, 0xac, 0xe6, 0x86, 0x7e, 0x99, 0x9b, 0xfa, 0x45, 0xcf, 0x76, 0xce, 0xec, - 0x76, 0x11, 0x52, 0x42, 0x64, 0xe6, 0xce, 0x4d, 0x95, 0xa2, 0xa1, 0xd8, 0x46, 0x19, 0x17, 0x81, - 0xbf, 0x31, 0x83, 0x45, 0x1d, 0xe3, 0x96, 0xc3, 0x80, 0x27, 0x0e, 0xc5, 0xb9, 0x56, 0xa0, 0x12, - 0xc1, 0x35, 0xa9, 0x06, 0x04, 0xe5, 0xa5, 0xf3, 0x4d, 0xe8, 0xcf, 0x38, 0x3c, 0x07, 0x16, 0xe1, - 0xac, 0x4f, 0x61, 0x96, 0x26, 0xf8, 0xb7, 0x52, 0x55, 0x30, 0x0c, 0x2b, 0x35, 0x84, 0x31, 0x08, - 0xdc, 0xa0, 0x2c, 0x70, 0x86, 0x84, 0x63, 0x5a, 0xc6, 0xaa, 0xf6, 0x12, 0xef, 0xd4, 0x70, 0x7c, - 0xf7, 0x74, 0x35, 0xbe, 0xeb, 0x76, 0xf5, 0x50, 0x1b, 0x35, 0xbf, 0xbc, 0xbb, 0x92, 0x37, 0x6a, - 0x73, 0x40, 0xab, 0x7b, 0x05, 0xcc, 0xdd, 0xb8, 0x1b, 0x43, 0x4e, 0xf6, 0x1e, 0xd0, 0x1c, 0x1e, - 0xb4, 0x73, 0x43, 0xb1, 0xa2, 0x43, 0x4b, 0x1a, 0xeb, 0x75, 0xdd, 0xbb, 0xf6, 0xb4, 0xdb, 0x95, - 0x8b, 0xcb, 0xfb, 0x2b, 0xb5, 0xc1, 0xd2, 0x6f, 0xa0, 0xe6, 0x6b, 0x7c, 0x6e, 0x1a, 0x7c, 0xba, - 0x9d, 0xb4, 0x28, 0x36, 0x0d, 0xaa, 0xfa, 0x20, 0xc9, 0x9d, 0x2d, 0x9c, 0x94, 0x85, 0x65, 0x79, - 0x39, 0xeb, 0x76, 0x09, 0xc0, 0xae, 0x5c, 0xc2, 0xf5, 0xc1, 0x12, 0xae, 0xf5, 0x12, 0x22, 0x38, - 0x8b, 0x98, 0xbe, 0xcf, 0x22, 0xcc, 0x2a, 0xf5, 0x5e, 0x71, 0x4c, 0xdb, 0x18, 0x9f, 0x7b, 0x31, - 0x35, 0x28, 0x4a, 0xf3, 0x02, 0x7c, 0x9f, 0x6f, 0xf4, 0x2c, 0x75, 0x9d, 0x9c, 0xf6, 0x7a, 0x8d, - 0x4b, 0x3a, 0x79, 0x3a, 0x2b, 0x93, 0x07, 0x93, 0x71, 0xf8, 0x14, 0xa6, 0x0c, 0x83, 0x99, 0xd1, - 0xcd, 0x92, 0x7d, 0x64, 0x7b, 0x0d, 0x55, 0x45, 0xb7, 0xf2, 0x1c, 0x0a, 0xf7, 0x90, 0xab, 0xec, - 0x5b, 0x15, 0x89, 0x16, 0x4b, 0x76, 0xeb, 0xd6, 0x60, 0xd6, 0x0d, 0x3e, 0x9c, 0x23, 0xa6, 0xbc, - 0xad, 0x35, 0x9f, 0xdb, 0x59, 0xb7, 0xeb, 0xf0, 0x93, 0x93, 0x0b, 0x7b, 0x2d, 0x9b, 0x86, 0xc2, - 0x39, 0x2f, 0x8a, 0x07, 0xda, 0x42, 0x02, 0x8c, 0x86, 0x62, 0xeb, 0x2b, 0xf0, 0x13, 0x05, 0x00, - 0xe5, 0xfa, 0x43, 0x34, 0x98, 0xf5, 0x46, 0x50, 0x63, 0xbd, 0x1e, 0x3f, 0x4c, 0x4e, 0x4e, 0x26, - 0xdd, 0xee, 0xf8, 0x06, 0x4c, 0x71, 0xbb, 0xf3, 0xe1, 0x3e, 0xf8, 0x60, 0x49, 0x56, 0xa8, 0x9e, - 0x66, 0x1a, 0xa8, 0x3c, 0x9f, 0x8c, 0xb0, 0xbe, 0x78, 0x75, 0x46, 0x84, 0x9c, 0x3b, 0x53, 0xbd, - 0xf8, 0xc2, 0xf1, 0x01, 0xcf, 0xdb, 0x93, 0x9c, 0xf7, 0xd0, 0x5b, 0x94, 0xb3, 0x8a, 0xb3, 0x20, - 0xcd, 0x9f, 0x07, 0xa4, 0x4f, 0x31, 0x00, 0x74, 0x19, 0xab, 0x12, 0x2c, 0x4a, 0x9e, 0x13, 0x0d, - 0xa7, 0x0d, 0xdd, 0xc9, 0x85, 0x5e, 0x08, 0x83, 0x92, 0x77, 0x20, 0xc1, 0x07, 0x9b, 0xae, 0xa4, - 0xb8, 0xb5, 0xdd, 0x9a, 0x3c, 0x85, 0xe3, 0xd1, 0x34, 0x02, 0xa1, 0xc1, 0x4a, 0xbd, 0xde, 0x95, - 0xa0, 0xa6, 0x3e, 0xb5, 0xdb, 0x41, 0x71, 0x2d, 0x58, 0xda, 0xae, 0x2b, 0x59, 0x5c, 0xf6, 0x1f, - 0x88, 0x62, 0x84, 0xdc, 0x17, 0x51, 0x2d, 0x33, 0x31, 0x0f, 0x55, 0x86, 0xb5, 0x4d, 0x46, 0x0b, - 0xa1, 0xac, 0x06, 0xa8, 0x38, 0xac, 0x2d, 0x0a, 0x8a, 0x47, 0x63, 0x49, 0xf7, 0xfc, 0x53, 0x21, - 0x7a, 0x26, 0x9a, 0xfe, 0x81, 0xb0, 0x2a, 0x85, 0x48, 0x96, 0x85, 0x7c, 0xd5, 0xd0, 0x0c, 0xa1, - 0x50, 0xa0, 0xc7, 0x81, 0x57, 0x4f, 0x20, 0xb0, 0x22, 0x8b, 0xd5, 0x5b, 0xd5, 0xba, 0x7c, 0x77, - 0x86, 0x1f, 0xdd, 0x9c, 0x72, 0x45, 0x1c, 0x52, 0x1f, 0x0f, 0x4e, 0xdd, 0x31, 0x34, 0xad, 0x3e, - 0x30, 0xd5, 0xe9, 0x52, 0x40, 0x71, 0x64, 0x32, 0x6c, 0xfc, 0xb9, 0x0f, 0x46, 0xd6, 0x4f, 0x99, - 0xce, 0xc3, 0x38, 0x71, 0xdf, 0xda, 0x09, 0x64, 0xba, 0x74, 0x38, 0xf7, 0x35, 0x06, 0x8c, 0xbe, - 0x7d, 0x50, 0x04, 0x7b, 0x31, 0x2d, 0xe3, 0x68, 0x8e, 0x67, 0x6a, 0x5c, 0x1d, 0xb4, 0x2b, 0x0a, - 0x8a, 0xf7, 0x65, 0x8a, 0x1f, 0xc3, 0x27, 0xa6, 0x47, 0xb6, 0x5d, 0x39, 0xda, 0xc3, 0x9d, 0x2f, - 0x6d, 0x57, 0x3e, 0x36, 0x32, 0x03, 0x74, 0x26, 0x1b, 0x54, 0x2b, 0x99, 0x6a, 0x5c, 0x5a, 0x23, - 0x58, 0xfc, 0x60, 0xb7, 0xd3, 0xf1, 0x90, 0x99, 0xb9, 0xf9, 0xb8, 0x47, 0x45, 0xb8, 0x17, 0x55, - 0xca, 0x8d, 0x09, 0x42, 0xe0, 0x92, 0x3c, 0x21, 0xe9, 0x63, 0xb3, 0x57, 0x3d, 0x60, 0x15, 0x94, - 0x10, 0x76, 0x29, 0x25, 0x18, 0xdc, 0x3f, 0xca, 0x6d, 0x02, 0x21, 0x37, 0x7d, 0xfa, 0x76, 0xac, - 0x1d, 0x33, 0x40, 0xe0, 0xa4, 0xac, 0xdd, 0x70, 0xad, 0xd8, 0x73, 0x8e, 0x40, 0x88, 0x12, 0x86, - 0xba, 0x83, 0x77, 0x41, 0x46, 0x0b, 0x9c, 0xed, 0x5d, 0xa6, 0x57, 0x07, 0x3e, 0x54, 0x5e, 0xf9, - 0x79, 0x71, 0xe3, 0xd3, 0x70, 0xb3, 0xea, 0x49, 0x7a, 0xac, 0x30, 0x10, 0xd2, 0xa5, 0x72, 0x84, - 0x18, 0xa8, 0x72, 0x55, 0xe2, 0x06, 0x6b, 0x77, 0xd5, 0x1a, 0xcd, 0x18, 0xc7, 0x18, 0x06, 0xd5, - 0x89, 0x81, 0xe4, 0xdf, 0x4c, 0xdb, 0xc3, 0x59, 0xa8, 0x8c, 0x3e, 0x93, 0xa3, 0xa3, 0xdd, 0xf2, - 0x81, 0xfc, 0x55, 0xa2, 0x18, 0xfd, 0x9d, 0xf6, 0x80, 0x69, 0x5a, 0x6a, 0x7f, 0xa5, 0x33, 0x85, - 0x75, 0x86, 0x96, 0xaa, 0xea, 0x07, 0xaa, 0xa9, 0x73, 0x3e, 0xb9, 0x39, 0x85, 0xfe, 0x2c, 0xc8, - 0x5f, 0x04, 0x69, 0xb8, 0x0a, 0x26, 0x17, 0xf4, 0xe2, 0xd3, 0xab, 0x34, 0xb9, 0xe3, 0x04, 0x92, - 0xf6, 0xda, 0xc9, 0x55, 0x83, 0xe7, 0xe8, 0x1a, 0x21, 0xd3, 0x2f, 0x61, 0x14, 0x1d, 0x07, 0x52, - 0x75, 0xa9, 0xcd, 0x59, 0x44, 0xb3, 0xf8, 0x63, 0xe0, 0x07, 0x80, 0x5f, 0x42, 0xfd, 0x4a, 0xa5, - 0x8e, 0x1a, 0x29, 0xe5, 0x12, 0x77, 0xca, 0x69, 0xe6, 0x6c, 0xed, 0x6d, 0xcd, 0x5d, 0xe7, 0xfa, - 0x99, 0xf1, 0x61, 0x3c, 0x37, 0x2d, 0xc1, 0x6c, 0xd5, 0xad, 0x22, 0x5f, 0x93, 0x5b, 0x31, 0xe8, - 0xba, 0x0f, 0x3f, 0x31, 0xb7, 0xe2, 0xaa, 0x9b, 0x50, 0x41, 0x20, 0x4d, 0x14, 0x8f, 0x24, 0x67, - 0xcd, 0x30, 0xf0, 0x48, 0x5f, 0x39, 0x36, 0x0f, 0xfb, 0xd9, 0x26, 0xf6, 0x6b, 0xcf, 0xfb, 0x67, - 0x3e, 0x72, 0xc4, 0x2b, 0x22, 0x2c, 0xb9, 0x94, 0x8a, 0x74, 0xde, 0x80, 0x84, 0x23, 0x90, 0x8f, - 0x92, 0x55, 0x25, 0x99, 0xe4, 0x9e, 0x20, 0xbd, 0x69, 0xc2, 0x05, 0xf0, 0x5d, 0xa7, 0x19, 0xb5, - 0x0c, 0x1f, 0x24, 0xfb, 0x1b, 0xd5, 0x04, 0x11, 0x0a, 0x50, 0x76, 0x08, 0xe3, 0xc0, 0xac, 0xd6, - 0x6b, 0x63, 0x4f, 0xc4, 0x58, 0x9a, 0x07, 0x5f, 0x44, 0x17, 0x30, 0x09, 0x8b, 0x22, 0x82, 0x5f, - 0x72, 0x80, 0x32, 0x1f, 0x5e, 0x8a, 0x9e, 0x28, 0x5c, 0x95, 0xbc, 0x61, 0xe2, 0x95, 0x9a, 0x16, - 0xc5, 0x04, 0x6a, 0x67, 0x2f, 0x66, 0xe1, 0xf0, 0xf2, 0x4e, 0x2d, 0x48, 0xeb, 0xff, 0x69, 0x76, - 0x89, 0xd4, 0x3b, 0x9b, 0x93, 0x97, 0x51, 0x49, 0x87, 0x3f, 0xec, 0x64, 0xc3, 0xb1, 0x12, 0xd4, - 0x98, 0x97, 0x08, 0x0b, 0x2d, 0xa7, 0x25, 0xb1, 0x89, 0x26, 0xf3, 0xba, 0x62, 0x69, 0xd0, 0x75, - 0x21, 0xcf, 0xb4, 0xf6, 0xe2, 0x8f, 0x9d, 0x2d, 0xa3, 0xc9, 0x01, 0x41, 0x4a, 0x62, 0x3d, 0x50, - 0x6b, 0xb3, 0x2b, 0x27, 0x5c, 0x8a, 0xfb, 0x0e, 0xf9, 0xb4, 0xea, 0xdf, 0x5a, 0x5c, 0x76, 0xac, - 0x19, 0xc6, 0x6b, 0x66, 0x6d, 0x4c, 0xce, 0xdf, 0x63, 0x3a, 0x93, 0x28, 0xea, 0x90, 0x65, 0x6b, - 0xa5, 0xc2, 0x01, 0x4f, 0x93, 0xb6, 0x1e, 0xc9, 0xce, 0x35, 0x14, 0x86, 0xca, 0x4b, 0x35, 0xd5, - 0x0c, 0x0c, 0x2c, 0xb2, 0xd6, 0x3d, 0xaf, 0xc5, 0xc2, 0xaf, 0x30, 0xde, 0x56, 0x6f, 0x53, 0xb4, - 0x3b, 0xa5, 0xe5, 0xa0, 0xe2, 0x22, 0xf6, 0x16, 0x40, 0x48, 0x69, 0xdc, 0xab, 0x95, 0xda, 0x77, - 0xea, 0xc1, 0x5a, 0x7b, 0x0e, 0x73, 0xa4, 0xd3, 0x15, 0x1b, 0x79, 0x03, 0x17, 0x94, 0x93, 0x11, - 0x7f, 0x10, 0x1c, 0xdf, 0x6b, 0x2e, 0x5e, 0x23, 0x78, 0x4d, 0xc5, 0x18, 0x1e, 0xdb, 0x0c, 0xb6, - 0x3e, 0x3c, 0xd0, 0xac, 0xf7, 0x27, 0x27, 0x6b, 0xde, 0xea, 0xe0, 0x40, 0x03, 0x37, 0xee, 0x6f, - 0x30, 0x84, 0x6b, 0xc8, 0x0d, 0xfb, 0x04, 0x8e, 0xbd, 0x51, 0xa1, 0x34, 0xd9, 0x51, 0x85, 0x88, - 0x3d, 0xef, 0xd1, 0xc2, 0xa7, 0xf0, 0xbd, 0x46, 0x5c, 0x73, 0x4b, 0x1c, 0x1c, 0x1e, 0x85, 0xc6, - 0xd0, 0xf5, 0x1a, 0xf0, 0x50, 0x03, 0x96, 0x6f, 0xf0, 0x44, 0x61, 0x28, 0x25, 0x02, 0xe0, 0x65, - 0x87, 0x2c, 0x0e, 0x3a, 0x49, 0x95, 0xa9, 0x95, 0xc4, 0xb8, 0x34, 0x5d, 0x3b, 0xdb, 0x93, 0x85, - 0x6a, 0xeb, 0x69, 0x55, 0x14, 0x23, 0xce, 0xa3, 0x52, 0x8b, 0xb6, 0xa0, 0x95, 0x40, 0x3e, 0x43, - 0xdc, 0x4a, 0xc1, 0x6e, 0x82, 0x68, 0x73, 0x2a, 0x57, 0xf0, 0x75, 0x8d, 0x88, 0x8d, 0xc7, 0xe5, - 0xbc, 0x3c, 0x9c, 0x79, 0x8a, 0x04, 0x80, 0x1a, 0x5c, 0x66, 0x1a, 0x67, 0xb7, 0xca, 0x6c, 0x3b, - 0xbe, 0xd5, 0x6d, 0x70, 0xb1, 0x6f, 0xf7, 0x43, 0xbd, 0x1b, 0xb9, 0x82, 0xc5, 0x23, 0x4f, 0xbe, - 0x25, 0x6c, 0x5e, 0xe9, 0xb0, 0x99, 0x3c, 0xf5, 0xcd, 0xc9, 0xc9, 0x5b, 0x7b, 0xc3, 0x99, 0x53, - 0x5a, 0xa7, 0xba, 0xc1, 0xcc, 0x3b, 0x71, 0x93, 0x29, 0x3b, 0x07, 0x7e, 0x2e, 0x54, 0x3e, 0xbe, - 0x50, 0x17, 0x47, 0xa8, 0x1b, 0x0b, 0xfb, 0x5c, 0x5d, 0x88, 0xb2, 0xe3, 0xf8, 0xc6, 0x1c, 0xe9, - 0x5c, 0xde, 0x3c, 0xe0, 0xd4, 0x9e, 0x9b, 0x84, 0xde, 0xed, 0x68, 0xa0, 0x0e, 0xb4, 0x4b, 0xe2, - 0xbe, 0xae, 0xf6, 0x60, 0x9c, 0x6b, 0x4a, 0x9a, 0x9a, 0xd9, 0xb7, 0x16, 0xa1, 0xab, 0x4b, 0x89, - 0x05, 0xe5, 0x1d, 0x05, 0xc1, 0xd7, 0x5a, 0x16, 0xce, 0x61, 0xe5, 0xe9, 0x64, 0x79, 0xbd, 0xb5, - 0x29, 0x5a, 0xaa, 0x78, 0x17, 0x47, 0x3b, 0x04, 0xb9, 0x18, 0x7f, 0x82, 0xd3, 0xc6, 0x69, 0xb2, - 0x5d, 0xa3, 0x83, 0xdf, 0x9a, 0x1b, 0x1a, 0xb4, 0xc4, 0xf1, 0xa1, 0x7d, 0xdd, 0x57, 0x00, 0xf1, - 0x5d, 0xab, 0xb9, 0x2b, 0xcb, 0x6d, 0x92, 0xbd, 0x9b, 0x4a, 0x92, 0xc7, 0x0f, 0xbb, 0x6a, 0x41, - 0xe9, 0x94, 0xd3, 0x86, 0xeb, 0x91, 0xb7, 0x16, 0x3c, 0xe8, 0xad, 0xbd, 0xb7, 0x0d, 0x65, 0x11, - 0xc0, 0xe1, 0xaf, 0x90, 0x09, 0x1f, 0x2d, 0xd8, 0x33, 0x34, 0xdc, 0xbe, 0x59, 0xe6, 0x46, 0xfb, - 0x8e, 0x3a, 0xb1, 0x5a, 0x6d, 0x8a, 0xc9, 0xca, 0x07, 0x61, 0x35, 0x14, 0xe8, 0x47, 0xad, 0xd2, - 0x72, 0xfd, 0xa0, 0xb5, 0x5a, 0x73, 0x5b, 0x14, 0xb4, 0x1f, 0xc5, 0x40, 0x0e, 0x50, 0x0d, 0x7f, - 0xae, 0x19, 0xed, 0xc6, 0xc8, 0x83, 0x55, 0x9e, 0x86, 0xde, 0xbb, 0xdd, 0xee, 0x9a, 0x2d, 0x8f, - 0x14, 0x01, 0x63, 0xfa, 0xb4, 0x8a, 0x8a, 0xc7, 0x29, 0x42, 0xb9, 0xb6, 0x0d, 0x01, 0xf8, 0x59, - 0x0c, 0xaa, 0x01, 0x7f, 0xff, 0xd7, 0xc1, 0xb7, 0xee, 0x2a, 0xd4, 0x23, 0xbc, 0xb3, 0x37, 0x8d, - 0x7b, 0x29, 0xb7, 0x74, 0x6c, 0x9b, 0x56, 0xed, 0xec, 0x1f, 0x24, 0x28, 0xd1, 0x54, 0x5b, 0x58, - 0x7b, 0xbf, 0xc0, 0x40, 0x97, 0x30, 0xb1, 0x58, 0xaa, 0x67, 0xb3, 0xf6, 0x81, 0xcd, 0xd7, 0xa6, - 0x89, 0x41, 0x12, 0xb8, 0x52, 0x42, 0x92, 0x70, 0xb4, 0x61, 0xbf, 0xdc, 0x9d, 0x17, 0x32, 0xd3, - 0x27, 0x9e, 0x77, 0x12, 0x58, 0xf0, 0x4b, 0xed, 0x6d, 0x4c, 0x1f, 0x93, 0xff, 0x2a, 0x84, 0x9c, - 0x14, 0x23, 0x9f, 0x52, 0x38, 0x03, 0x0c, 0xdd, 0xa4, 0xe9, 0x2e, 0xb6, 0xbb, 0x9d, 0xb6, 0x02, - 0x6b, 0x65, 0x81, 0xdf, 0xb3, 0x60, 0x92, 0xdc, 0xc7, 0x96, 0xbc, 0x2d, 0x9f, 0x08, 0xb5, 0x16, - 0x42, 0x6e, 0xfd, 0xb4, 0x5c, 0x58, 0xf2, 0x05, 0x1d, 0x13, 0x59, 0xfa, 0x73, 0x2c, 0x26, 0xcd, - 0x2d, 0xf9, 0x4b, 0xf9, 0xa8, 0x1b, 0x3e, 0x2f, 0x9f, 0x30, 0x8a, 0x85, 0xc8, 0xa0, 0x1e, 0x86, - 0x76, 0xb4, 0x2b, 0x52, 0xe7, 0x34, 0xe7, 0xa0, 0x94, 0x60, 0x9a, 0x57, 0x5e, 0xe2, 0x74, 0x19, - 0x4e, 0x94, 0xfd, 0xce, 0xcb, 0xe7, 0xfd, 0xd4, 0x8b, 0x27, 0xc9, 0x9d, 0x2d, 0xba, 0x23, 0xd1, - 0xcf, 0x93, 0x32, 0xcf, 0xf7, 0xed, 0x13, 0x73, 0xbf, 0xe9, 0x3b, 0xb1, 0xab, 0xb6, 0x32, 0xf5, - 0xb6, 0xf0, 0x35, 0x53, 0xf9, 0xfa, 0x9a, 0x64, 0xc8, 0x36, 0x03, 0x0c, 0xa5, 0x13, 0x87, 0x65, - 0x26, 0xcf, 0xe6, 0x6d, 0xcd, 0xba, 0xf2, 0x30, 0x7c, 0x10, 0x46, 0x2c, 0x72, 0xb4, 0x8b, 0x60, - 0x9e, 0xe4, 0xb8, 0xd1, 0x24, 0x65, 0x8f, 0xeb, 0x72, 0x2d, 0x5f, 0x5c, 0xf1, 0x1a, 0xe4, 0x76, - 0xe1, 0x65, 0x19, 0x3c, 0x21, 0xa7, 0x33, 0xda, 0x89, 0x9d, 0xcc, 0xdb, 0xc0, 0x19, 0xca, 0xc5, - 0x84, 0x78, 0xdf, 0x0a, 0xf1, 0x00, 0xc8, 0x1c, 0x98, 0x89, 0x34, 0xb3, 0xab, 0x3d, 0x1e, 0x60, - 0x66, 0xa5, 0xb9, 0xea, 0x4d, 0x62, 0x5b, 0x13, 0x95, 0x34, 0xa0, 0x26, 0x48, 0x46, 0x3e, 0x3e, - 0x9f, 0xee, 0x9b, 0x7c, 0xd2, 0x05, 0x97, 0xc3, 0x2b, 0x07, 0x01, 0x0a, 0xe5, 0x09, 0xa3, 0x10, - 0x3d, 0xff, 0x25, 0xe3, 0xfa, 0xfe, 0xdf, 0x08, 0xd8, 0xea, 0xd9, 0x90, 0x0b, 0xf1, 0x9c, 0xde, - 0xf5, 0x01, 0xde, 0xcf, 0xb9, 0x1a, 0x5e, 0x3d, 0x8d, 0x90, 0xdd, 0x87, 0xbc, 0x99, 0xc6, 0x5b, - 0x39, 0x62, 0x4b, 0x7f, 0x81, 0xe2, 0xd1, 0xda, 0xe1, 0xcb, 0x0b, 0xe7, 0xc6, 0xae, 0x72, 0xa8, - 0xf2, 0xf2, 0x56, 0xfe, 0x22, 0xcf, 0xe5, 0xf3, 0x36, 0x1c, 0x69, 0xd2, 0xeb, 0x65, 0xbd, 0xa1, - 0x93, 0x6f, 0xb6, 0xde, 0x99, 0xb1, 0x2e, 0x3e, 0x3d, 0xfb, 0xf8, 0xc9, 0x12, 0x3a, 0x27, 0x32, - 0x66, 0xa0, 0xb7, 0x1a, 0xf6, 0x2f, 0xce, 0x43, 0x7d, 0xde, 0x7d, 0xf8, 0xfc, 0x72, 0xbf, 0xcb, - 0xb9, 0xee, 0xf2, 0xfc, 0xc1, 0x2e, 0x2f, 0xdf, 0xbf, 0xb0, 0x84, 0xbc, 0xff, 0x73, 0x93, 0xdd, - 0x81, 0x18, 0x3b, 0x18, 0xdd, 0x26, 0xcc, 0xfd, 0xdc, 0xd8, 0x9a, 0xf2, 0x6d, 0xe4, 0xd8, 0x5b, - 0x85, 0x33, 0x8f, 0xdc, 0x62, 0xfa, 0x9b, 0x24, 0xcf, 0x66, 0x04, 0x33, 0x51, 0x83, 0x5f, 0x6d, - 0xdb, 0xed, 0xf8, 0x73, 0xb8, 0xc6, 0x41, 0x81, 0x59, 0xa4, 0x50, 0x72, 0xa2, 0x2f, 0x4e, 0x33, - 0x6f, 0xea, 0xa5, 0xe1, 0x20, 0xd4, 0x9b, 0xbf, 0x60, 0xdd, 0x58, 0x0d, 0xc2, 0x9f, 0xe6, 0xe0, - 0x93, 0x22, 0xfc, 0x29, 0x99, 0xe0, 0xc7, 0x9b, 0x18, 0xb5, 0xa9, 0x3a, 0x78, 0xe7, 0xb9, 0x8a, - 0x4a, 0xe1, 0x91, 0xc4, 0x6e, 0xaa, 0x19, 0xc5, 0x4f, 0xa2, 0xae, 0x35, 0x18, 0x58, 0xdd, 0xb4, - 0x3f, 0x4f, 0xb2, 0x1c, 0x97, 0x05, 0x84, 0x8a, 0x92, 0xb6, 0xb8, 0xcd, 0x02, 0x2f, 0xf5, 0xe7, - 0xdd, 0xdc, 0x31, 0x4e, 0x90, 0xbc, 0x3a, 0xde, 0x55, 0x4a, 0x7b, 0xf1, 0x53, 0x35, 0xfa, 0x7e, - 0xe8, 0x0e, 0x9d, 0x51, 0x1d, 0xf9, 0x9e, 0x2a, 0x96, 0xce, 0x9f, 0xde, 0x0c, 0x50, 0x23, 0xd3, - 0xfd, 0x47, 0xeb, 0xdd, 0x23, 0xab, 0x6b, 0xe7, 0xdd, 0xe4, 0x94, 0x4b, 0xfd, 0x24, 0x83, 0x8f, - 0x28, 0xba, 0x16, 0x95, 0x06, 0x55, 0x69, 0x16, 0xc6, 0x65, 0xe9, 0x33, 0x94, 0x27, 0x5c, 0x4b, - 0xbf, 0x43, 0x5c, 0xc3, 0xf2, 0xba, 0x0f, 0x23, 0x6e, 0x85, 0x11, 0x9b, 0x31, 0xd7, 0x63, 0xd3, - 0xde, 0xa7, 0x92, 0xac, 0xf3, 0x32, 0x8d, 0xaa, 0x59, 0x2f, 0x6c, 0xde, 0x4b, 0x90, 0x5b, 0xde, - 0xa6, 0x74, 0xac, 0x30, 0x85, 0x92, 0xd0, 0x8c, 0x60, 0xc9, 0x35, 0x82, 0x84, 0xb5, 0xdc, 0xe0, - 0x77, 0x23, 0xf5, 0xdf, 0xeb, 0xd8, 0xd6, 0x9f, 0x4e, 0xb7, 0xf8, 0x83, 0x89, 0x68, 0x05, 0xaa, - 0x23, 0x46, 0x04, 0x1c, 0x10, 0xd4, 0x92, 0xa5, 0x36, 0x21, 0xae, 0x98, 0xc5, 0xa4, 0xfd, 0xb7, - 0xbc, 0x6b, 0xfa, 0x1a, 0x16, 0xda, 0xf9, 0x0c, 0x3f, 0x80, 0xe4, 0x35, 0x49, 0xc3, 0x59, 0x48, - 0xb1, 0x6e, 0x47, 0x77, 0xf4, 0xc3, 0xd4, 0xa7, 0xe1, 0x5a, 0xe6, 0x70, 0x7d, 0xcd, 0x49, 0x28, - 0x4b, 0xa6, 0x4e, 0x20, 0xe9, 0x83, 0x7a, 0x8e, 0x45, 0x1f, 0x7c, 0xb0, 0x38, 0xc5, 0x99, 0xdc, - 0x06, 0xfa, 0x3b, 0x11, 0x96, 0x73, 0x26, 0xf5, 0xb3, 0x63, 0xd1, 0x2b, 0x86, 0xd6, 0xee, 0xcf, - 0xc1, 0xe6, 0xcf, 0x8f, 0x31, 0xec, 0xde, 0xd9, 0x9f, 0x84, 0x3e, 0x9d, 0x4e, 0x01, 0x5d, 0xec, - 0x1e, 0xef, 0x9d, 0x37, 0x51, 0xdb, 0xb5, 0x33, 0x04, 0xa2, 0x40, 0x76, 0xe7, 0x7b, 0x09, 0xf4, - 0x72, 0x1e, 0x5a, 0xea, 0x85, 0x3a, 0x55, 0xe5, 0x6e, 0xc7, 0x5a, 0xe7, 0x47, 0xf5, 0x2c, 0x4d, - 0xbd, 0x0d, 0x25, 0x26, 0xa3, 0x0d, 0xfb, 0x44, 0x72, 0x5b, 0xe6, 0xc9, 0xbf, 0x7d, 0x32, 0xdc, - 0x89, 0xfe, 0x9d, 0xb7, 0xb0, 0xf7, 0x0d, 0x70, 0x7d, 0x2c, 0x45, 0xc8, 0xdf, 0xf7, 0x14, 0x5b, - 0x4d, 0xe5, 0xc0, 0x26, 0x2d, 0xa7, 0x41, 0xb2, 0x64, 0xd6, 0x07, 0xe1, 0x6a, 0x4b, 0x40, 0x31, - 0x53, 0x60, 0x58, 0x82, 0x9c, 0x2c, 0x41, 0xf0, 0xa0, 0x25, 0xc8, 0xf7, 0x52, 0x4f, 0x07, 0x96, - 0x20, 0x30, 0x2d, 0xc1, 0x75, 0xfd, 0x1a, 0xe3, 0xb3, 0x78, 0x06, 0x27, 0x72, 0x7f, 0xee, 0xa5, - 0x4b, 0x58, 0x9b, 0xf5, 0xfe, 0xfd, 0x3c, 0x08, 0x22, 0x6e, 0x59, 0x32, 0x21, 0xec, 0x11, 0x9d, - 0x86, 0x49, 0xfc, 0xdb, 0xfb, 0x30, 0xe3, 0x7c, 0xe2, 0x61, 0xeb, 0x17, 0x21, 0x7d, 0x79, 0x0d, - 0x20, 0xdd, 0x1e, 0x50, 0xd4, 0xcd, 0x7b, 0x76, 0xe0, 0xf6, 0x12, 0x07, 0x6e, 0x65, 0xd2, 0xcb, - 0xc5, 0x37, 0x54, 0x86, 0x7f, 0x7c, 0xb3, 0x93, 0xc6, 0x92, 0xaa, 0x64, 0x97, 0x89, 0xb2, 0x52, - 0x14, 0x08, 0x47, 0x24, 0x0b, 0x4c, 0x62, 0x49, 0x47, 0x8b, 0xf4, 0xf7, 0x5c, 0x7e, 0xe1, 0x67, - 0x8a, 0x40, 0x35, 0x97, 0x7c, 0xe4, 0xcf, 0xbb, 0xc8, 0x90, 0x13, 0x10, 0x11, 0xf4, 0xd6, 0x3c, - 0x5b, 0xc9, 0x4c, 0x25, 0x83, 0xb3, 0x5e, 0x4c, 0xa9, 0x0a, 0x76, 0x2d, 0xf6, 0x11, 0x60, 0x87, - 0xfd, 0x39, 0x85, 0x3b, 0xa7, 0xb6, 0xa1, 0x01, 0xe0, 0xaa, 0x52, 0x0e, 0x65, 0x30, 0x1a, 0x0e, - 0x4f, 0xed, 0xac, 0x47, 0x9b, 0x59, 0xfa, 0xe3, 0x5d, 0xd0, 0x25, 0x42, 0xfa, 0x2a, 0xeb, 0xc6, - 0x72, 0x4e, 0xbf, 0x5f, 0x95, 0x4c, 0xc6, 0x86, 0x25, 0xf5, 0xd7, 0x51, 0x12, 0x59, 0x7e, 0x3c, - 0x28, 0x79, 0x58, 0x3a, 0x65, 0xf5, 0xa1, 0x14, 0x8b, 0xbf, 0x04, 0x43, 0xd2, 0x0a, 0xc0, 0xe0, - 0xdd, 0xcc, 0x62, 0x8f, 0x99, 0x9e, 0xe8, 0x0b, 0x36, 0x5e, 0xf4, 0x23, 0x5d, 0xe8, 0x95, 0x23, - 0xb9, 0x0d, 0x27, 0x4e, 0xe5, 0x72, 0x70, 0xf3, 0x8c, 0x5e, 0xdc, 0x92, 0xdb, 0x64, 0x3a, 0xa5, - 0x13, 0xdf, 0xd6, 0xf0, 0x1b, 0x96, 0x90, 0x64, 0xa1, 0xdf, 0x6e, 0xb5, 0x6a, 0xb1, 0x68, 0x69, - 0x4b, 0x9f, 0x24, 0x6e, 0x6d, 0x5d, 0x96, 0x95, 0x5f, 0x49, 0x41, 0x29, 0x49, 0xad, 0x60, 0x10, - 0xad, 0x8b, 0x86, 0xd4, 0x2e, 0x8f, 0x25, 0x33, 0x33, 0xc5, 0x76, 0x27, 0x7f, 0x3c, 0x90, 0x9d, - 0x5a, 0x72, 0x00, 0x95, 0xb4, 0x3c, 0x00, 0xd3, 0x26, 0x51, 0x2e, 0x27, 0xce, 0x2b, 0xdb, 0x97, - 0x73, 0x99, 0x0d, 0xce, 0x24, 0x3c, 0xff, 0xee, 0xa8, 0xff, 0x9d, 0xa8, 0x85, 0x7c, 0x9e, 0x45, - 0xb6, 0xd5, 0x3d, 0xe2, 0x6b, 0x80, 0xeb, 0x5a, 0x92, 0x3f, 0xb2, 0x2c, 0xe9, 0x9b, 0xf6, 0x02, - 0x13, 0xde, 0xe9, 0x19, 0xb7, 0x6a, 0x99, 0x72, 0xda, 0x99, 0x97, 0x97, 0xdf, 0xc4, 0xb4, 0xa4, - 0x0f, 0xfb, 0x2f, 0xfd, 0x8d, 0x03, 0x06, 0xab, 0xa7, 0xce, 0x1f, 0x21, 0xed, 0x7e, 0xb6, 0xad, - 0xbf, 0x59, 0xdd, 0x0a, 0xeb, 0x18, 0x88, 0xa0, 0x43, 0x57, 0x32, 0x90, 0xb7, 0xd5, 0xa7, 0x2b, - 0x1e, 0x56, 0x69, 0xe5, 0x60, 0x51, 0xd5, 0xb2, 0x7d, 0x2c, 0x56, 0x8e, 0xb2, 0xfe, 0x32, 0x4d, - 0x2f, 0xec, 0xaf, 0x88, 0x21, 0x77, 0x7f, 0xb8, 0x08, 0x2d, 0x1b, 0xed, 0x40, 0xb5, 0xca, 0x2c, - 0x91, 0x57, 0x89, 0x11, 0x1f, 0x5c, 0x3f, 0xa4, 0x56, 0xcc, 0x03, 0x3d, 0x96, 0xdb, 0xd4, 0x49, - 0x59, 0x4b, 0x56, 0x42, 0x76, 0xb1, 0x9a, 0x55, 0xca, 0xb2, 0x2a, 0xfa, 0xc0, 0x8f, 0x30, 0x41, - 0x7e, 0x37, 0x6a, 0xec, 0x9d, 0x87, 0xe0, 0xdf, 0x99, 0x57, 0x25, 0x64, 0xeb, 0x3c, 0xa2, 0xc1, - 0xbe, 0xd0, 0x1b, 0x7e, 0x8c, 0x7a, 0xe8, 0x9c, 0x42, 0xd2, 0xa7, 0xaf, 0x2e, 0x41, 0xac, 0x13, - 0xfa, 0xde, 0x11, 0x1d, 0x51, 0xa8, 0x55, 0x0e, 0xa4, 0x3a, 0xd3, 0x6a, 0x01, 0xcc, 0x11, 0x29, - 0xaf, 0x97, 0xd5, 0xb2, 0x9a, 0xed, 0x29, 0x05, 0x3c, 0x1a, 0x3a, 0x63, 0x9c, 0xa3, 0xa9, 0x9d, - 0xf7, 0x52, 0x04, 0x2d, 0x74, 0x17, 0xf4, 0x42, 0x51, 0x1e, 0x71, 0xe6, 0xc9, 0x7a, 0xb9, 0x17, - 0x9f, 0xd9, 0x01, 0xf9, 0xf3, 0xf3, 0x76, 0xb5, 0xa1, 0x9d, 0x7b, 0x72, 0x25, 0x6d, 0xff, 0x94, - 0xfe, 0x78, 0xd2, 0xa0, 0xd4, 0x1f, 0x30, 0xf2, 0xac, 0x41, 0x96, 0x1a, 0xd2, 0x1d, 0x96, 0xad, - 0xd7, 0xff, 0xdf, 0x14, 0xe1, 0xc2, 0x69, 0xde, 0x0d, 0x4e, 0xe1, 0xfd, 0x44, 0x70, 0x39, 0xfb, - 0x49, 0xac, 0xfd, 0xb7, 0x58, 0x6e, 0xe7, 0xa0, 0x52, 0xe6, 0x18, 0x40, 0x41, 0xe9, 0x41, 0x74, - 0xba, 0x14, 0xe4, 0x7a, 0x07, 0x3b, 0xca, 0x0f, 0xd4, 0xe6, 0xe3, 0xbf, 0xa6, 0xae, 0xee, 0x8d, - 0x9e, 0xd2, 0x76, 0xd8, 0x24, 0x58, 0x7f, 0x98, 0xda, 0xd6, 0x37, 0x74, 0x0e, 0x50, 0x2d, 0xbc, - 0x34, 0x0b, 0x5e, 0xd1, 0xe7, 0x9b, 0xa8, 0x5f, 0xe5, 0x56, 0xb9, 0x01, 0xeb, 0xb3, 0xd8, 0x31, - 0xd2, 0xfe, 0xff, 0x32, 0xc5, 0x8e, 0x7a, 0xbd, 0x89, 0x73, 0xca, 0x0b, 0x3f, 0x31, 0x7c, 0x92, - 0x7f, 0x9b, 0x47, 0x9f, 0x9a, 0x18, 0x06, 0x6d, 0x08, 0xd5, 0x17, 0x14, 0x3b, 0xd9, 0x67, 0x12, - 0x2a, 0x41, 0xef, 0xc1, 0xfd, 0x5d, 0x59, 0xb6, 0xeb, 0x5c, 0xf6, 0xbe, 0x7c, 0xe9, 0x5e, 0xb9, - 0x5f, 0xbe, 0x4c, 0x4e, 0xbf, 0x7c, 0xe9, 0xe3, 0xd2, 0xfd, 0xc6, 0x15, 0xc5, 0x5e, 0x05, 0x95, - 0x58, 0xf2, 0x1f, 0xca, 0xba, 0xfc, 0xf2, 0x25, 0x2b, 0xbe, 0x7c, 0xb1, 0xaf, 0xba, 0x90, 0xad, - 0xbf, 0x43, 0x9a, 0x2e, 0x25, 0x1e, 0xb3, 0x87, 0x1e, 0x71, 0x0f, 0x98, 0xc2, 0xb5, 0xe4, 0x3f, - 0xff, 0xe7, 0xbe, 0x0f, 0x82, 0xfa, 0x0f, 0x6f, 0xc3, 0x7c, 0x0c, 0x66, 0x2f, 0xd7, 0xa4, 0x64, - 0x67, 0x37, 0x56, 0xf7, 0x1f, 0x10, 0xe9, 0xfc, 0xb0, 0xd8, 0xb3, 0xba, 0xff, 0x04, 0xcf, 0xec, - 0x97, 0x43, 0x07, 0x71, 0xf3, 0xe4, 0xa8, 0x58, 0x37, 0x47, 0x80, 0x63, 0xfd, 0x8a, 0xb5, 0xff, - 0xcd, 0x2d, 0x86, 0x6b, 0x5a, 0x74, 0x8a, 0x02, 0xfb, 0x72, 0xd8, 0xfb, 0xc1, 0xeb, 0x4d, 0x9f, - 0xf5, 0x5e, 0x5d, 0x6d, 0xe1, 0x90, 0x5b, 0x32, 0x3c, 0x2c, 0x3d, 0xa3, 0xd2, 0x6c, 0x1f, 0xa8, - 0xd5, 0x8d, 0xe1, 0x06, 0x57, 0xff, 0x5b, 0x8f, 0x41, 0x70, 0xef, 0x6b, 0x2d, 0xaa, 0x46, 0x51, - 0x4b, 0xa3, 0xb0, 0xfa, 0x9f, 0x5b, 0xf8, 0x5f, 0x6b, 0x51, 0x35, 0x9a, 0x1f, 0xc4, 0x6a, 0x2c, - 0x1a, 0x49, 0x7c, 0xae, 0x5f, 0xb6, 0xea, 0x8c, 0xca, 0xf4, 0x8b, 0x7e, 0x57, 0x07, 0x5c, 0x3d, - 0x04, 0x57, 0x0f, 0xe5, 0x0a, 0xff, 0x3c, 0x67, 0xb4, 0x93, 0x55, 0x7c, 0x4f, 0x2f, 0xb8, 0xe5, - 0xe0, 0x6c, 0x08, 0xfd, 0x16, 0x36, 0xda, 0xd9, 0xfa, 0xfc, 0xa1, 0x22, 0xe8, 0x5e, 0xd8, 0x41, - 0xa7, 0x03, 0xc7, 0x00, 0xb8, 0x6e, 0x29, 0x05, 0x4a, 0xdb, 0xdb, 0x6a, 0xa6, 0x6c, 0xeb, 0x11, - 0xac, 0x1f, 0xae, 0xcb, 0xa2, 0x87, 0xea, 0x76, 0xe3, 0xb9, 0x19, 0xab, 0x62, 0xbe, 0x2d, 0x7e, - 0xc9, 0xd1, 0xde, 0x3b, 0x85, 0x47, 0x47, 0x67, 0x70, 0x73, 0x4a, 0xa4, 0x22, 0x54, 0x1f, 0x18, - 0x2c, 0x60, 0x52, 0xf9, 0x5b, 0xf9, 0xfd, 0xee, 0xf1, 0x40, 0xc7, 0x45, 0xb9, 0x10, 0x3a, 0xba, - 0xab, 0x66, 0xad, 0xf2, 0xfa, 0x70, 0x01, 0x83, 0x20, 0x16, 0x74, 0x0f, 0x1a, 0xd7, 0xcb, 0x3f, - 0x6c, 0x4c, 0x0c, 0x78, 0xd8, 0xb8, 0x5e, 0xb3, 0xd1, 0x18, 0xde, 0x37, 0xbd, 0x62, 0x9f, 0xe5, - 0xf4, 0xf9, 0x5e, 0xcc, 0x78, 0x9e, 0x57, 0x6d, 0x57, 0xe4, 0x5a, 0x65, 0x2b, 0xb3, 0xa5, 0x95, - 0xf2, 0xf9, 0x40, 0xdc, 0xcc, 0xaa, 0x9b, 0x1b, 0x7d, 0x62, 0xb0, 0x9a, 0xcc, 0x3e, 0x64, 0x6b, - 0x5e, 0x35, 0xcb, 0xaa, 0x9b, 0x95, 0xd1, 0x9e, 0xc7, 0xe0, 0xf6, 0xbc, 0xcb, 0xf4, 0x40, 0x8f, - 0xa8, 0x3c, 0x93, 0x98, 0x23, 0xea, 0xbc, 0x7f, 0x44, 0x6c, 0xca, 0x9f, 0x6f, 0xb0, 0x51, 0x0c, - 0x56, 0x0b, 0x27, 0x8f, 0xd8, 0x5d, 0xd1, 0xaf, 0x88, 0x21, 0x6a, 0xae, 0x56, 0xaa, 0x72, 0xb8, - 0x50, 0x87, 0x84, 0x3c, 0xd7, 0x9f, 0x1a, 0x3a, 0xb0, 0x36, 0xd5, 0x1e, 0x3d, 0x75, 0xbd, 0xcc, - 0xaf, 0xd4, 0x51, 0x6c, 0x54, 0x56, 0x48, 0x5b, 0x27, 0xf2, 0xe8, 0x34, 0x6e, 0xc2, 0x47, 0x47, - 0xf7, 0x07, 0x80, 0x6b, 0x1c, 0x07, 0x66, 0x2e, 0xa4, 0x8a, 0x38, 0x31, 0xe7, 0x79, 0x5e, 0xe5, - 0xbf, 0xe6, 0x8c, 0xd6, 0x4f, 0xc9, 0x47, 0x60, 0xab, 0xc5, 0xdf, 0xed, 0xcf, 0x07, 0x4f, 0x86, - 0xec, 0xf1, 0xb2, 0x0f, 0xca, 0x1e, 0x2f, 0x1b, 0x7f, 0xf8, 0xba, 0x6c, 0x23, 0xe0, 0x37, 0xd2, - 0x6b, 0x31, 0x02, 0x36, 0x31, 0xe8, 0xd1, 0xa1, 0xbd, 0x18, 0x36, 0xa8, 0x97, 0xd0, 0x19, 0x0b, - 0xbe, 0x0b, 0x4f, 0x13, 0xf2, 0x61, 0xf9, 0x9e, 0x1e, 0x05, 0x3d, 0xfb, 0x2a, 0xfd, 0xe6, 0x49, - 0x69, 0x0e, 0x60, 0xd5, 0xcf, 0xbe, 0xfb, 0xee, 0xf4, 0x12, 0x6e, 0xb1, 0xcc, 0xf0, 0x5f, 0x24, - 0xe3, 0xab, 0x4b, 0xff, 0x4a, 0xce, 0x74, 0x31, 0x1e, 0x65, 0x59, 0xc5, 0xc5, 0x37, 0xba, 0xb8, - 0x6c, 0x49, 0x55, 0x54, 0xac, 0x91, 0x0b, 0x8a, 0x7f, 0x4a, 0x5e, 0x83, 0x8c, 0x47, 0x0b, 0xe1, - 0x15, 0xa4, 0x03, 0x74, 0xe5, 0x15, 0xcc, 0xf8, 0x8e, 0xbc, 0xf5, 0x1b, 0xbe, 0x0b, 0x4b, 0x0b, - 0xe9, 0xad, 0x75, 0x06, 0x03, 0x4e, 0x5d, 0x63, 0x33, 0xcb, 0x12, 0x4f, 0x85, 0x3d, 0xda, 0x5b, - 0x0c, 0x31, 0x7b, 0x4a, 0xa4, 0x86, 0x88, 0xe2, 0xbd, 0x41, 0x58, 0xe5, 0x6b, 0xc2, 0x32, 0x55, - 0x93, 0x39, 0x81, 0x1a, 0x9a, 0xc9, 0x92, 0x04, 0x05, 0x76, 0x0c, 0xfb, 0x3e, 0xf0, 0xba, 0x76, - 0xfc, 0x34, 0x75, 0x9f, 0x38, 0xc3, 0xbd, 0x6c, 0x4a, 0x4c, 0x0d, 0x52, 0xa0, 0x0c, 0x0d, 0xce, - 0xcc, 0x8a, 0x94, 0x2a, 0x12, 0xf8, 0xf9, 0xa8, 0xf8, 0xbf, 0x32, 0x1c, 0x83, 0x22, 0x7b, 0x32, - 0x3c, 0x0d, 0xa0, 0xcb, 0xc8, 0x82, 0xfa, 0xd0, 0x67, 0x74, 0x8d, 0x76, 0x0d, 0x21, 0x5f, 0x67, - 0x51, 0x2b, 0x21, 0x35, 0xfd, 0x92, 0x9a, 0x7e, 0xb1, 0xb2, 0xcf, 0x7a, 0x01, 0xc8, 0x01, 0x3c, - 0x50, 0x5a, 0xc2, 0x8d, 0x1d, 0x8a, 0x4d, 0x42, 0x95, 0x3e, 0x1d, 0x05, 0xbd, 0x1f, 0xb0, 0xba, - 0xe0, 0x34, 0x19, 0xa4, 0xe3, 0x7a, 0x5c, 0xc0, 0x2f, 0x87, 0x0d, 0x65, 0xe4, 0x7c, 0x07, 0xfb, - 0x5d, 0x8d, 0x1a, 0x3d, 0x80, 0x75, 0x75, 0x76, 0x9a, 0xf7, 0x23, 0xcd, 0x3d, 0xa7, 0x76, 0x80, - 0x41, 0x86, 0x43, 0x37, 0x70, 0xce, 0x86, 0x43, 0x0c, 0x5c, 0x4e, 0x22, 0xe8, 0x26, 0xd5, 0x78, - 0x67, 0x18, 0x8f, 0x32, 0x12, 0xa2, 0x6d, 0xcc, 0x18, 0x4b, 0xe5, 0xca, 0xc1, 0xd9, 0x8e, 0xb4, - 0x35, 0x2d, 0x96, 0x72, 0x68, 0x26, 0x83, 0xf3, 0x59, 0x66, 0x65, 0xe8, 0xfd, 0x43, 0x40, 0x98, - 0x07, 0xc0, 0x00, 0x03, 0x0d, 0x88, 0x76, 0x7d, 0x6a, 0xc2, 0xa0, 0xed, 0xdc, 0x63, 0x21, 0x0c, - 0xea, 0x0c, 0x6b, 0x6d, 0x74, 0x2a, 0x47, 0xc8, 0x38, 0xfa, 0x57, 0x9e, 0x91, 0xe1, 0x03, 0x2c, - 0x74, 0xb8, 0x45, 0x9f, 0xb2, 0x19, 0x9b, 0xa6, 0x29, 0x97, 0x76, 0xd2, 0x9f, 0x17, 0x45, 0xd2, - 0xcf, 0xe8, 0x67, 0x45, 0x3f, 0x9e, 0xa8, 0xb2, 0xcf, 0x15, 0x6c, 0x1d, 0x9d, 0x27, 0xd5, 0xf1, - 0x70, 0xb3, 0x3f, 0x2f, 0x05, 0xac, 0xdf, 0x8e, 0x0d, 0x43, 0xbe, 0x6d, 0xa3, 0x1b, 0x79, 0x9b, - 0x55, 0x5e, 0x67, 0xa6, 0xe3, 0xd8, 0x46, 0x1e, 0x0d, 0xe7, 0x0f, 0x0d, 0x67, 0xe6, 0x33, 0x84, - 0xf7, 0xc6, 0x7c, 0x8e, 0xc5, 0xae, 0x1e, 0xff, 0x10, 0x95, 0xb5, 0x56, 0x3d, 0x42, 0x9e, 0x21, - 0xab, 0x94, 0x4a, 0xdf, 0x7a, 0x8e, 0x71, 0xd0, 0xc2, 0x73, 0x47, 0x20, 0x91, 0xb7, 0x13, 0x25, - 0x8d, 0xa2, 0xaf, 0x2f, 0x0c, 0xfc, 0x7e, 0xbc, 0xb0, 0xb9, 0x66, 0x3a, 0x5e, 0x58, 0xd4, 0x70, - 0xc1, 0xfe, 0xc2, 0xb2, 0x83, 0x85, 0x45, 0x6d, 0x0b, 0xa3, 0xf1, 0xff, 0xa7, 0x85, 0x55, 0xe2, - 0xf0, 0x47, 0x0b, 0xab, 0x6d, 0xe5, 0x57, 0xb8, 0x18, 0x6d, 0xca, 0xc9, 0x93, 0xc7, 0x07, 0x57, - 0x11, 0xdd, 0x28, 0x98, 0xa4, 0x9b, 0x59, 0x75, 0x73, 0xc3, 0x51, 0xdf, 0x3e, 0xc8, 0x56, 0xae, - 0xae, 0x8e, 0xe7, 0xab, 0x11, 0xf1, 0xb4, 0x1d, 0xa8, 0xff, 0xf4, 0x83, 0x75, 0xe0, 0x93, 0x41, - 0x76, 0xff, 0xbf, 0xb9, 0x27, 0xed, 0x6e, 0xdb, 0xb8, 0xf6, 0xf3, 0xcb, 0x39, 0xf9, 0x0f, 0x23, - 0x24, 0x35, 0x01, 0x13, 0xdc, 0xe4, 0xa5, 0x29, 0x28, 0x4a, 0xc7, 0x71, 0x9c, 0x17, 0xf7, 0x39, - 0x8e, 0x4f, 0x65, 0x37, 0xc9, 0x51, 0x75, 0x6c, 0x2e, 0xa0, 0x84, 0x06, 0x02, 0x58, 0x00, 0x94, - 0xa8, 0xca, 0xfc, 0xef, 0xef, 0x2e, 0x33, 0x83, 0x19, 0x2c, 0x14, 0xed, 0xa6, 0x7d, 0xef, 0x34, - 0x95, 0x89, 0xd9, 0xe7, 0xce, 0x9d, 0x3b, 0x77, 0x9b, 0x3b, 0x6e, 0x3a, 0xf9, 0x87, 0x8b, 0xf7, - 0x56, 0x7c, 0xa0, 0x87, 0xc8, 0xb9, 0xe3, 0xd7, 0xa1, 0xfc, 0xca, 0xe8, 0xeb, 0x11, 0x7f, 0x79, - 0xb0, 0x0d, 0x27, 0x45, 0xa1, 0xab, 0x3e, 0x78, 0xb0, 0x7f, 0x5d, 0xe8, 0x1b, 0xbf, 0x1e, 0x9f, - 0xfb, 0x78, 0x6d, 0xf0, 0x20, 0x6c, 0x3f, 0x45, 0x61, 0x2a, 0x42, 0xf2, 0x34, 0xf2, 0x10, 0xc5, - 0xe3, 0x1c, 0x70, 0x33, 0x05, 0x7c, 0x4c, 0x00, 0x07, 0x33, 0x60, 0xe3, 0x22, 0xb9, 0x44, 0x8a, - 0x45, 0xd9, 0x0f, 0x92, 0x20, 0x30, 0x83, 0x20, 0xd1, 0xcf, 0x3c, 0xfa, 0xe7, 0x82, 0xff, 0x99, - 0x79, 0x95, 0xa6, 0x76, 0x43, 0x10, 0x26, 0x23, 0x61, 0x98, 0x17, 0x16, 0x10, 0x47, 0x7f, 0x7c, - 0xf8, 0x0b, 0xc1, 0x02, 0xe1, 0x20, 0x3f, 0x0e, 0xcf, 0x11, 0x0c, 0xf2, 0xe3, 0xd1, 0x39, 0x43, - 0x70, 0xfa, 0xe9, 0xf5, 0x70, 0xe5, 0xf8, 0xe3, 0xb1, 0x6c, 0x24, 0xb6, 0x1b, 0x29, 0x5b, 0x28, - 0xab, 0x9b, 0x7d, 0xce, 0x2b, 0xab, 0xb6, 0xa3, 0x3c, 0xf4, 0xa5, 0x3a, 0xda, 0xb9, 0x4e, 0x00, - 0xb0, 0x3d, 0xd6, 0x09, 0x0f, 0x59, 0xbd, 0x9d, 0xee, 0x5d, 0x2b, 0x28, 0xa3, 0xd6, 0x8a, 0x75, - 0x2a, 0x50, 0x4d, 0x21, 0x7b, 0xde, 0x75, 0xfe, 0xc0, 0xbf, 0x62, 0xf8, 0x25, 0xf1, 0xbe, 0x6c, - 0x74, 0x3f, 0xbc, 0x0f, 0x8b, 0x26, 0xc4, 0x47, 0x55, 0x60, 0x89, 0xbc, 0x70, 0xe6, 0x18, 0xc8, - 0x8b, 0x5f, 0x04, 0xc2, 0xb4, 0x19, 0xf1, 0xef, 0xab, 0xbb, 0x3f, 0xe2, 0xc3, 0x5c, 0x2a, 0x00, - 0x45, 0xee, 0xf1, 0x0e, 0xd5, 0x76, 0x39, 0x00, 0x34, 0xd6, 0x88, 0x2f, 0x69, 0x0f, 0xdf, 0x9f, - 0x46, 0x75, 0x32, 0x3e, 0xec, 0x13, 0xe6, 0xae, 0xc9, 0x03, 0x02, 0x70, 0x58, 0x99, 0xb0, 0x2a, - 0xfe, 0xff, 0xe9, 0x7f, 0x59, 0xc9, 0xaa, 0xf4, 0x87, 0x8d, 0x3c, 0x4b, 0xa9, 0x3a, 0xa5, 0x1b, - 0xac, 0xda, 0x34, 0xc6, 0xb1, 0xa6, 0xde, 0x6a, 0x03, 0x99, 0x83, 0x4a, 0xbe, 0x00, 0xca, 0x94, - 0xca, 0x46, 0xc7, 0xe7, 0x18, 0x7a, 0xce, 0x57, 0x4b, 0xd4, 0xec, 0x1b, 0xaa, 0xc5, 0xa7, 0xfd, - 0xa7, 0x4f, 0x9f, 0x1a, 0xd9, 0x4b, 0x2b, 0xfb, 0xd1, 0xa3, 0xfe, 0xa3, 0x47, 0x8f, 0xca, 0xec, - 0xa1, 0x9d, 0xfd, 0x64, 0x68, 0x66, 0x2d, 0xcd, 0xac, 0xa7, 0x95, 0x86, 0x87, 0x43, 0x2b, 0xfb, - 0x9b, 0x4a, 0xc3, 0x4b, 0x3b, 0x7b, 0x34, 0xac, 0x0e, 0xf9, 0xdc, 0xe0, 0x05, 0x1d, 0x43, 0x1d, - 0x18, 0x10, 0x63, 0x61, 0x9d, 0x7f, 0x80, 0x1e, 0x21, 0x31, 0x38, 0x28, 0xe0, 0x86, 0xc0, 0xde, - 0x20, 0xfa, 0x35, 0x16, 0x40, 0xa6, 0x4b, 0x16, 0x19, 0x37, 0xc3, 0x8b, 0xb7, 0x5c, 0xa2, 0xb6, - 0x5c, 0xa2, 0xb7, 0x5c, 0xa2, 0xb7, 0x5c, 0xe3, 0xa0, 0xb9, 0x5e, 0xa6, 0xea, 0x65, 0xba, 0x5e, - 0xa6, 0xea, 0x59, 0x13, 0x62, 0x01, 0x29, 0x90, 0xe6, 0x90, 0x80, 0x0d, 0x64, 0x8d, 0x43, 0x0e, - 0x89, 0x69, 0x23, 0x7d, 0x63, 0xcb, 0x90, 0xd9, 0x78, 0xb3, 0x6b, 0x58, 0x91, 0x1a, 0x56, 0xa4, - 0x87, 0x15, 0xe9, 0x61, 0x6d, 0x4b, 0x6d, 0x2f, 0x86, 0x8a, 0x9f, 0x66, 0x6d, 0x3a, 0xed, 0x74, - 0x87, 0x9e, 0xd8, 0x56, 0x60, 0x17, 0x7d, 0xfe, 0x01, 0x3d, 0x54, 0xd4, 0xd8, 0x12, 0xa9, 0xb7, - 0x75, 0xfd, 0x63, 0xbb, 0xd1, 0x41, 0x5a, 0x1b, 0x88, 0xa5, 0x21, 0xcc, 0xff, 0x81, 0x14, 0xf8, - 0xf7, 0x9a, 0x1f, 0x00, 0x5c, 0xe9, 0xc7, 0x8f, 0xc8, 0x84, 0x4b, 0x4d, 0x64, 0xf7, 0xf0, 0x61, - 0x06, 0xff, 0x4f, 0x98, 0x71, 0xe5, 0x27, 0xa8, 0xc2, 0xb1, 0xbc, 0xb1, 0x31, 0x71, 0xa5, 0xc0, - 0x4f, 0x8f, 0x51, 0x21, 0xa3, 0x0d, 0x32, 0x4f, 0xd8, 0x3b, 0x7c, 0xc8, 0x1e, 0x95, 0x7a, 0x2f, - 0xde, 0xb7, 0x0d, 0xa3, 0x49, 0x0c, 0x52, 0xe3, 0xe4, 0x51, 0xff, 0x69, 0x1b, 0x02, 0x63, 0x89, - 0x7c, 0x07, 0x32, 0x60, 0xfe, 0xf5, 0x76, 0x97, 0x41, 0x83, 0x7b, 0x56, 0x16, 0x8d, 0xb0, 0x66, - 0xd1, 0xe0, 0xb0, 0xfe, 0x6f, 0xd3, 0x55, 0xa0, 0x46, 0xf9, 0x23, 0xa5, 0xf8, 0x9f, 0x61, 0xea, - 0xe0, 0x53, 0xcd, 0x22, 0x56, 0x48, 0xfe, 0xc9, 0x04, 0x82, 0xea, 0x95, 0xa6, 0x91, 0xbd, 0x67, - 0x66, 0xd4, 0xf1, 0xb3, 0x4d, 0x90, 0xfb, 0xd9, 0x2d, 0xfc, 0xd9, 0x04, 0x09, 0x40, 0xf4, 0x96, - 0xfe, 0xca, 0x61, 0x83, 0xb8, 0xa5, 0x06, 0x0e, 0x3f, 0xab, 0x5a, 0xef, 0x66, 0xdd, 0xf8, 0xfd, - 0xda, 0xff, 0x4f, 0x56, 0x95, 0xe7, 0xdd, 0x88, 0xf4, 0xb1, 0x53, 0x18, 0x5d, 0x0a, 0xe2, 0x55, - 0x15, 0x31, 0xe1, 0xa4, 0xa6, 0x1b, 0x2e, 0xe4, 0x82, 0x8a, 0xaf, 0xa4, 0x34, 0x2b, 0x2c, 0x94, - 0x72, 0x5c, 0x3a, 0x39, 0xd0, 0x34, 0x7a, 0x06, 0x4a, 0x8d, 0x8b, 0xde, 0x24, 0xe9, 0x1a, 0x09, - 0x83, 0x43, 0x46, 0xbd, 0x52, 0xf8, 0xd6, 0x32, 0x77, 0x81, 0x12, 0xf7, 0x50, 0x6b, 0x8c, 0x2b, - 0xda, 0xe8, 0xec, 0x61, 0xf4, 0xe9, 0xba, 0x7b, 0x8a, 0xfd, 0x41, 0xbd, 0xd7, 0xe6, 0x23, 0xa7, - 0xa1, 0x11, 0xbb, 0x34, 0x23, 0xb6, 0x61, 0xb8, 0x73, 0xe9, 0xf8, 0xf9, 0xc3, 0xdd, 0x38, 0xee, - 0xe4, 0xce, 0x4e, 0x1c, 0x77, 0xae, 0x9d, 0xad, 0xd1, 0x55, 0xa9, 0x82, 0x77, 0xdd, 0x8c, 0xf4, - 0x3a, 0xd1, 0xf9, 0x24, 0x47, 0xb7, 0x68, 0xa5, 0x76, 0xa7, 0xbb, 0x64, 0x2d, 0xfe, 0x3f, 0x51, - 0x9b, 0xff, 0x4f, 0x78, 0x15, 0x15, 0x3f, 0xa4, 0xe9, 0x6f, 0xc8, 0x61, 0x44, 0x45, 0x30, 0x23, - 0x6f, 0x55, 0x47, 0x89, 0xae, 0xfc, 0xcc, 0xee, 0x44, 0x3b, 0xab, 0xc1, 0xe8, 0xc2, 0x2c, 0x0b, - 0xd9, 0xb5, 0xcc, 0x48, 0xa7, 0xcd, 0xcf, 0x5e, 0x52, 0xcf, 0xe8, 0x81, 0xfb, 0x52, 0xc1, 0x6a, - 0x64, 0x9d, 0x66, 0xf3, 0x49, 0xb9, 0x65, 0x28, 0x63, 0xa2, 0xb4, 0x4d, 0xfc, 0x29, 0xfb, 0xd5, - 0xdd, 0xc0, 0xe0, 0x5c, 0x87, 0x29, 0x35, 0x0e, 0xcf, 0x31, 0x6a, 0xa2, 0x25, 0x02, 0x3a, 0xc9, - 0xf1, 0xcf, 0x35, 0xfe, 0x99, 0x1a, 0xae, 0x2d, 0x4c, 0x8c, 0x4a, 0x75, 0x2f, 0xef, 0x01, 0x1a, - 0x04, 0x6d, 0x13, 0x8e, 0xa7, 0x41, 0x2a, 0x2e, 0xd3, 0xb5, 0xac, 0xae, 0x4a, 0x83, 0xcd, 0x4b, - 0xbd, 0x97, 0xad, 0x6e, 0x9b, 0x01, 0x47, 0x0d, 0x20, 0xdc, 0xfa, 0x40, 0x2b, 0xd2, 0x75, 0x71, - 0x42, 0x85, 0xf8, 0xf7, 0x44, 0x25, 0x06, 0x66, 0xe2, 0xd9, 0x9d, 0x76, 0x4e, 0x0a, 0xfe, 0xe9, - 0x73, 0x70, 0xd7, 0x3c, 0xb8, 0x03, 0xfa, 0x62, 0x64, 0xac, 0x0a, 0x33, 0xe7, 0xbc, 0xb1, 0x6b, - 0x7a, 0x18, 0xc6, 0xb1, 0x5c, 0xb3, 0xa2, 0xaa, 0x6b, 0x56, 0xf4, 0x79, 0xae, 0x59, 0x91, 0xc9, - 0x90, 0xa5, 0x49, 0xab, 0x0d, 0x1e, 0x8f, 0x1a, 0x03, 0x61, 0xc6, 0x2e, 0xfb, 0x22, 0x44, 0x39, - 0xfd, 0x0b, 0xb8, 0x77, 0x52, 0x04, 0x67, 0xc5, 0xb9, 0xd7, 0x07, 0xec, 0x7a, 0x31, 0x85, 0x3d, - 0x64, 0x9e, 0x62, 0xa9, 0x31, 0x21, 0xaa, 0x1f, 0xa0, 0x51, 0x13, 0x7b, 0xf0, 0xdd, 0x04, 0x6a, - 0x7d, 0xfc, 0x48, 0xff, 0x00, 0x87, 0x07, 0xa3, 0x53, 0x0e, 0x7a, 0x69, 0x15, 0x19, 0xa1, 0x04, - 0x72, 0xdc, 0x0d, 0xc9, 0x8d, 0x9d, 0x86, 0xa6, 0xa3, 0x44, 0x01, 0xa7, 0x6d, 0x63, 0x8b, 0xd8, - 0x27, 0xca, 0xe3, 0x91, 0x09, 0x86, 0xe5, 0xd2, 0x84, 0x83, 0x26, 0x6f, 0x08, 0x80, 0x4f, 0x9b, - 0x38, 0x1f, 0xdf, 0x89, 0x82, 0x1a, 0x94, 0x1b, 0x27, 0x75, 0x58, 0xa0, 0x55, 0x1b, 0xbb, 0xf1, - 0x69, 0xa5, 0xe4, 0xf5, 0x98, 0x50, 0x5b, 0xd2, 0x20, 0x63, 0x54, 0x1b, 0x23, 0x36, 0x62, 0x73, - 0xf4, 0x52, 0xe1, 0x14, 0x12, 0xa7, 0x0c, 0xcb, 0x55, 0x8d, 0x68, 0xd4, 0x1b, 0x8d, 0x87, 0x47, - 0x09, 0x5e, 0xdd, 0x21, 0x65, 0x94, 0xce, 0x3e, 0x4b, 0xba, 0xa3, 0xf3, 0xb1, 0xcb, 0x5b, 0xc8, - 0xd3, 0xa3, 0x93, 0xd0, 0x0b, 0x7d, 0x84, 0x2f, 0x60, 0xcc, 0x7c, 0x5a, 0xc0, 0x48, 0x4c, 0xa7, - 0x51, 0x13, 0x1f, 0x68, 0x11, 0xa1, 0xdb, 0x68, 0x32, 0x1c, 0x47, 0x47, 0x99, 0x0e, 0x57, 0xd4, - 0xed, 0x7a, 0x19, 0x90, 0x31, 0x73, 0x29, 0xd2, 0xca, 0x4c, 0x4c, 0x52, 0xf0, 0x7b, 0xce, 0xc8, - 0x1c, 0xa4, 0xbd, 0xec, 0xf6, 0x64, 0x5b, 0x26, 0xea, 0xbb, 0x19, 0x63, 0x66, 0x66, 0x63, 0x66, - 0x75, 0xf8, 0x59, 0x88, 0xcf, 0x03, 0xd5, 0xb5, 0x43, 0xca, 0x25, 0xd6, 0xbd, 0xe3, 0xb3, 0xbe, - 0xa8, 0xae, 0x20, 0x85, 0xc1, 0x33, 0x05, 0x60, 0x83, 0xae, 0x91, 0x4d, 0xaa, 0x3c, 0x19, 0x98, - 0x72, 0x62, 0x75, 0xe0, 0xe2, 0x70, 0x71, 0x1a, 0xb7, 0x29, 0x34, 0x1e, 0xaf, 0xe1, 0x40, 0xc7, - 0x02, 0x30, 0xc7, 0x94, 0x87, 0x9f, 0x56, 0x37, 0x56, 0x1d, 0x8f, 0x2a, 0x2d, 0x1a, 0x90, 0x47, - 0xb8, 0xa7, 0xcd, 0x70, 0x4f, 0x11, 0xee, 0x18, 0x3b, 0xc9, 0x80, 0x7b, 0x8a, 0x70, 0xd7, 0x9a, - 0x4f, 0x7b, 0x40, 0x0a, 0x43, 0xd8, 0xd9, 0x38, 0x31, 0x9d, 0x8d, 0x13, 0xbc, 0x20, 0x66, 0x88, - 0xa0, 0xd5, 0x31, 0xa6, 0x49, 0xc5, 0x4f, 0x54, 0xc1, 0x38, 0x8c, 0x95, 0xfb, 0xb5, 0x7d, 0x9e, - 0x90, 0x13, 0xa8, 0xa3, 0x1c, 0x3d, 0xcd, 0xa6, 0x8c, 0x13, 0xa2, 0x02, 0xc3, 0x0a, 0xdd, 0xe5, - 0x43, 0x61, 0x66, 0x5c, 0xed, 0x90, 0x04, 0xab, 0xb2, 0xb8, 0xf2, 0xf0, 0xa8, 0x9f, 0x19, 0x9c, - 0x41, 0x94, 0xbb, 0x5e, 0xbf, 0x7e, 0x9c, 0x2a, 0xe7, 0xe8, 0xa6, 0x83, 0x76, 0xe8, 0x3b, 0x14, - 0xbd, 0xd0, 0x91, 0x0e, 0x4e, 0x95, 0x13, 0x57, 0x6a, 0x8a, 0x43, 0x9a, 0xba, 0x7a, 0x8f, 0x8c, - 0xc2, 0x35, 0x1b, 0x3d, 0x86, 0xc6, 0x41, 0x5b, 0xcf, 0x6d, 0x3c, 0xde, 0x2b, 0xa0, 0x6b, 0xe3, - 0xb0, 0xbc, 0x3b, 0xe9, 0x54, 0x3a, 0xa1, 0x8b, 0xb4, 0xb5, 0xb1, 0x48, 0x8f, 0xe6, 0x12, 0xbb, - 0x3d, 0xe9, 0x51, 0xda, 0x52, 0x9e, 0x5d, 0x9e, 0xcd, 0xe2, 0x8d, 0x6c, 0x86, 0x04, 0x89, 0xc9, - 0x0d, 0xa0, 0x42, 0x36, 0x64, 0xdf, 0xd3, 0x96, 0xb6, 0xd9, 0x81, 0xfa, 0xbe, 0xa6, 0x91, 0x4c, - 0x55, 0xf7, 0x6a, 0x45, 0x32, 0x33, 0x02, 0x57, 0xe0, 0xa1, 0x50, 0x4a, 0x2a, 0x8b, 0xe8, 0xba, - 0x22, 0x0f, 0x50, 0xe3, 0x6f, 0xf8, 0x75, 0x12, 0x25, 0x9d, 0x28, 0x89, 0x23, 0xe9, 0xcb, 0x5f, - 0x52, 0x20, 0x48, 0x98, 0x69, 0xde, 0x4a, 0xae, 0x8b, 0xf9, 0x88, 0x9a, 0xcc, 0x59, 0x2a, 0x47, - 0xd4, 0xcd, 0x33, 0x14, 0x0c, 0x25, 0x17, 0x51, 0x8e, 0x25, 0xac, 0x3b, 0x55, 0x82, 0xf8, 0x03, - 0x12, 0x2a, 0xf3, 0x99, 0x15, 0x5f, 0x7d, 0x15, 0x4e, 0xc5, 0xf2, 0xe6, 0x2d, 0x68, 0x2f, 0xf2, - 0x35, 0x97, 0x20, 0x43, 0xb9, 0x00, 0x99, 0xd1, 0x88, 0x9d, 0x75, 0x0b, 0x73, 0x7b, 0xd3, 0x8d, - 0xfe, 0xa2, 0xe2, 0xdc, 0xc8, 0x93, 0x7a, 0x34, 0x1c, 0x2a, 0x29, 0x87, 0x7e, 0x1a, 0xf2, 0x69, - 0xf0, 0x8d, 0xaf, 0xa5, 0x13, 0x76, 0x7d, 0x34, 0x25, 0x93, 0xd2, 0x01, 0xd2, 0xd0, 0xd5, 0xc0, - 0x59, 0x69, 0x48, 0x43, 0x76, 0x12, 0x09, 0xc1, 0x50, 0xa5, 0x22, 0xd0, 0xf9, 0xb6, 0x33, 0x54, - 0x00, 0x1b, 0xaa, 0xf4, 0x25, 0x0c, 0xe4, 0x87, 0x76, 0x15, 0x0c, 0x9c, 0x69, 0x52, 0x44, 0xa5, - 0x5b, 0xa1, 0x6f, 0x4a, 0xdc, 0x3c, 0x48, 0x53, 0xa0, 0x0c, 0x46, 0x87, 0xbe, 0x7a, 0xa0, 0xe1, - 0xa9, 0x2f, 0xb9, 0x40, 0xc2, 0x20, 0x3a, 0x81, 0x16, 0x85, 0x7f, 0x5d, 0xf8, 0x17, 0x85, 0xff, - 0xbe, 0xf0, 0xaf, 0x8a, 0x89, 0xeb, 0x5e, 0x9b, 0xfc, 0xbb, 0x5f, 0x58, 0xb1, 0x09, 0x99, 0x77, - 0xce, 0xab, 0x31, 0x5c, 0x54, 0x98, 0x0c, 0x42, 0xae, 0xd2, 0x89, 0x66, 0xb2, 0x72, 0xa1, 0xf5, - 0xba, 0xe3, 0x2c, 0xba, 0xcc, 0x36, 0xa8, 0x28, 0x22, 0xb2, 0x40, 0xa3, 0xdc, 0xfa, 0xcc, 0x25, - 0x3c, 0xd8, 0xfa, 0xc8, 0x5d, 0xd3, 0x1d, 0xaf, 0xac, 0xae, 0x88, 0x0d, 0x4d, 0x2b, 0xbc, 0x1c, - 0xc1, 0x49, 0x58, 0x46, 0xf0, 0xf8, 0xc7, 0x3a, 0xcc, 0x6e, 0x4f, 0xe9, 0x95, 0x02, 0x32, 0xff, - 0x8e, 0xed, 0x1b, 0xf3, 0x7c, 0x4d, 0x0c, 0x6f, 0x9e, 0x49, 0xea, 0x4d, 0x3e, 0x16, 0x4e, 0xcc, - 0x81, 0xcf, 0x31, 0xdc, 0x88, 0x6e, 0x09, 0xe6, 0xb8, 0xb8, 0x25, 0x72, 0x7a, 0x92, 0xb9, 0x9e, - 0xe5, 0xe1, 0xee, 0x7c, 0xf7, 0xd3, 0x8f, 0xcf, 0x39, 0xa2, 0x39, 0x86, 0x2b, 0x0f, 0x17, 0xce, - 0x39, 0xca, 0x85, 0xd1, 0xd6, 0xe0, 0x5a, 0x27, 0x00, 0x85, 0xc9, 0xd2, 0xbc, 0x62, 0x50, 0x81, - 0x09, 0xc0, 0x7f, 0x01, 0xc2, 0xd0, 0x35, 0xde, 0x66, 0x28, 0x2f, 0x64, 0xd1, 0xc2, 0x68, 0x60, - 0xbe, 0x2f, 0xf0, 0xa8, 0x73, 0x2f, 0x8a, 0xc9, 0x1d, 0xe3, 0xd5, 0x65, 0xe1, 0x3f, 0x2f, 0x37, - 0x6e, 0x70, 0x55, 0xf8, 0xeb, 0x28, 0x00, 0x81, 0x64, 0xe5, 0xeb, 0xeb, 0x5e, 0x41, 0xe1, 0xff, - 0x40, 0xd8, 0x1a, 0x7c, 0xed, 0x9f, 0x12, 0x46, 0x20, 0x33, 0xff, 0x33, 0x22, 0x53, 0xf0, 0x4f, - 0x54, 0x4b, 0x47, 0x71, 0x70, 0x27, 0x23, 0xf9, 0xbd, 0x03, 0x49, 0xfc, 0xaf, 0x3e, 0x2f, 0xe7, - 0xb3, 0x6c, 0xfe, 0x06, 0xc4, 0xd9, 0xe0, 0x7b, 0x9f, 0xdc, 0x9c, 0xde, 0x21, 0x87, 0xff, 0x0f, - 0xfe, 0xfd, 0x43, 0xb8, 0x79, 0x09, 0x0d, 0xff, 0xe2, 0x83, 0x4c, 0xfa, 0x36, 0x85, 0xaf, 0xe0, - 0xd7, 0xad, 0x7f, 0x1d, 0x66, 0x39, 0xe1, 0x25, 0x45, 0x00, 0x05, 0x01, 0x1f, 0x5d, 0xe0, 0xab, - 0x1c, 0x80, 0x0e, 0x4d, 0x47, 0x31, 0xe2, 0x40, 0x78, 0xf4, 0x7b, 0xa3, 0xa3, 0xf7, 0xa5, 0x3b, - 0x56, 0xe1, 0xe1, 0x09, 0x03, 0x13, 0x44, 0x9a, 0x0f, 0xe9, 0xf2, 0x12, 0x0b, 0xac, 0xc9, 0x05, - 0x85, 0x30, 0x28, 0x40, 0x6e, 0x0c, 0x17, 0x6f, 0x68, 0x43, 0xe7, 0x93, 0xf7, 0x88, 0xb2, 0x5b, - 0x7c, 0x6f, 0x50, 0x06, 0x21, 0xfd, 0x0b, 0x9e, 0x18, 0x74, 0xc3, 0x41, 0x5c, 0xcb, 0x90, 0xa2, - 0x18, 0xea, 0x25, 0x0f, 0x06, 0x03, 0x23, 0xb4, 0x68, 0x3e, 0x05, 0xd0, 0x14, 0x45, 0x3e, 0xa0, - 0xd7, 0x02, 0xe8, 0x86, 0x44, 0x35, 0xc0, 0x28, 0xa1, 0xfc, 0xef, 0x11, 0x60, 0xb4, 0xf8, 0xd4, - 0x00, 0xa3, 0x4e, 0x39, 0x07, 0x38, 0x03, 0xbc, 0x20, 0xec, 0x97, 0x09, 0xd8, 0xda, 0x3d, 0x41, - 0x47, 0x0d, 0x8d, 0x39, 0xcd, 0xa1, 0x64, 0x73, 0x86, 0xe3, 0xe4, 0xa8, 0x50, 0xfc, 0x4c, 0xa2, - 0xc2, 0x92, 0x64, 0x64, 0xee, 0x1d, 0x67, 0xfd, 0x30, 0x01, 0x64, 0x26, 0x67, 0x9e, 0x89, 0xf9, - 0x81, 0xa1, 0xcb, 0x7c, 0x0a, 0x41, 0xa4, 0x9d, 0x7d, 0xe8, 0xa4, 0x67, 0x2d, 0x41, 0x44, 0x81, - 0xad, 0xdc, 0xac, 0x7f, 0x93, 0x45, 0x85, 0xcc, 0xf3, 0x1a, 0x35, 0xfe, 0xc8, 0xb9, 0x66, 0x74, - 0x55, 0x1d, 0x38, 0xc4, 0x2d, 0xef, 0xdd, 0x3b, 0xa0, 0x47, 0xcf, 0x4f, 0x4f, 0x03, 0xba, 0xb8, - 0xb6, 0xbe, 0x9a, 0x31, 0x5d, 0x1c, 0x3d, 0xf1, 0x6f, 0x30, 0x4c, 0x1b, 0xfa, 0x14, 0x8d, 0x79, - 0xe8, 0xa6, 0x26, 0x41, 0x39, 0x87, 0xf0, 0x5d, 0xbb, 0x93, 0xd0, 0x14, 0x13, 0x99, 0xa0, 0x99, - 0x64, 0x82, 0x60, 0x20, 0x03, 0xd4, 0x1c, 0xa0, 0x88, 0x59, 0x70, 0x88, 0x38, 0x4d, 0x32, 0xc8, - 0xa5, 0xaa, 0xad, 0x03, 0xc0, 0x53, 0x8c, 0xd9, 0xda, 0x94, 0x0b, 0xbf, 0x01, 0x8d, 0x79, 0xa6, - 0x5b, 0x38, 0xd8, 0xda, 0xcb, 0xb0, 0xc5, 0x67, 0xeb, 0x4f, 0x9b, 0xca, 0xd8, 0x52, 0x1a, 0x9e, - 0x62, 0xf3, 0xa6, 0x62, 0x18, 0x29, 0x0f, 0x6f, 0x8e, 0xe2, 0x15, 0x37, 0x28, 0x13, 0x4f, 0x28, - 0xe0, 0xe7, 0x4f, 0xd9, 0x3b, 0x85, 0x86, 0xc1, 0xda, 0x67, 0x64, 0x0d, 0x22, 0x9f, 0xc3, 0xa0, - 0x04, 0xed, 0x23, 0x7a, 0x4d, 0x05, 0x1e, 0x3c, 0x38, 0xe0, 0x1f, 0xd0, 0xfd, 0xeb, 0xe9, 0x6b, - 0xea, 0x9c, 0x0d, 0x3a, 0x41, 0xea, 0xcb, 0x20, 0x62, 0x3b, 0x1a, 0xf9, 0x96, 0x4b, 0x6c, 0x35, - 0x26, 0xee, 0x28, 0xfb, 0xbd, 0xcc, 0x01, 0x20, 0xe0, 0x3c, 0x83, 0xa9, 0x9f, 0xc8, 0xc9, 0x04, - 0x73, 0x3f, 0x64, 0xa2, 0xdd, 0x54, 0x1d, 0xa7, 0x2d, 0x69, 0x3a, 0x2a, 0x97, 0xae, 0x77, 0x15, - 0xbb, 0x96, 0x85, 0xae, 0x56, 0xc5, 0x6d, 0x53, 0xa1, 0x35, 0xfc, 0x46, 0x39, 0x83, 0xfe, 0x99, - 0xd2, 0xdf, 0x39, 0x5e, 0xb1, 0x04, 0x30, 0x84, 0x3a, 0x62, 0x50, 0xe4, 0xe2, 0x15, 0xe0, 0x03, - 0x89, 0xc0, 0x80, 0xad, 0x39, 0x24, 0xc8, 0xdc, 0xed, 0xd6, 0x8c, 0x4c, 0x4a, 0x88, 0x15, 0x2d, - 0xdd, 0xd1, 0x71, 0xa1, 0xe4, 0x6e, 0xb3, 0x53, 0x46, 0x70, 0xc7, 0x51, 0x92, 0x5a, 0x88, 0xd7, - 0x1e, 0x50, 0x67, 0x37, 0x70, 0x4f, 0x82, 0xbf, 0xf5, 0xdd, 0xbf, 0x2d, 0xba, 0x68, 0xd3, 0x3b, - 0x09, 0xce, 0xc2, 0x17, 0xe7, 0xee, 0x59, 0xb7, 0x77, 0x7e, 0xc2, 0x49, 0x5f, 0x0f, 0xca, 0xb0, - 0x58, 0x27, 0x5a, 0xdf, 0x38, 0xf4, 0xdd, 0xe2, 0x6c, 0x74, 0x7e, 0x82, 0x7f, 0x74, 0xd0, 0x21, - 0xaf, 0x07, 0x89, 0x87, 0xe7, 0x27, 0x5d, 0xfc, 0x1b, 0xa0, 0xdd, 0x6f, 0xb8, 0x35, 0xbc, 0x57, - 0x0d, 0x97, 0x56, 0x0c, 0xe9, 0xf7, 0x7d, 0xb4, 0x09, 0x17, 0x74, 0x75, 0xa6, 0xae, 0xab, 0x0c, - 0x07, 0x85, 0xf7, 0xb0, 0x50, 0x19, 0x06, 0x69, 0x31, 0xe2, 0xa9, 0x86, 0x78, 0x3d, 0xc8, 0xb5, - 0x37, 0x16, 0xb9, 0x9f, 0x55, 0xb6, 0x93, 0x61, 0x31, 0x44, 0x75, 0xa4, 0xb4, 0x1a, 0x3e, 0x9f, - 0x26, 0x49, 0x5a, 0x88, 0x39, 0x3d, 0x82, 0x2a, 0x1f, 0xd2, 0x9b, 0xc2, 0x7f, 0xba, 0x33, 0x07, - 0x78, 0x36, 0xa6, 0x6c, 0x89, 0xe7, 0xc7, 0x7d, 0x89, 0x14, 0x00, 0xb7, 0x13, 0x29, 0x42, 0xd1, - 0xf7, 0x24, 0x0c, 0xe2, 0xbe, 0xbc, 0x03, 0x48, 0x97, 0xb5, 0xad, 0xcc, 0xd6, 0x43, 0xdf, 0x6c, - 0xd1, 0xac, 0x01, 0x2d, 0x40, 0x06, 0x62, 0x8c, 0x95, 0xdc, 0xcf, 0x34, 0xc5, 0xd5, 0x7d, 0x30, - 0x01, 0x6c, 0x52, 0xcc, 0x29, 0xf6, 0x1c, 0x55, 0x60, 0x6e, 0x09, 0xde, 0x6c, 0x82, 0x3e, 0x4a, - 0x67, 0xe4, 0x90, 0x8f, 0x6f, 0x14, 0xe0, 0xed, 0x49, 0x22, 0x9a, 0x41, 0x15, 0x4f, 0xb2, 0xc9, - 0xe8, 0xa8, 0x2a, 0xbb, 0x1a, 0x81, 0x94, 0x4a, 0xb9, 0x15, 0x10, 0xc0, 0xfc, 0x08, 0xee, 0x90, - 0x74, 0xd1, 0xbd, 0x64, 0x58, 0x08, 0x35, 0x11, 0x44, 0x6c, 0x03, 0x46, 0x27, 0x6b, 0x79, 0x1d, - 0x07, 0x9f, 0x50, 0x71, 0x9b, 0x21, 0xf4, 0x2c, 0x8e, 0x5d, 0xb3, 0x4a, 0x18, 0x74, 0x48, 0x0e, - 0x39, 0x33, 0xdf, 0xd4, 0x39, 0xef, 0x00, 0x82, 0x59, 0x0b, 0xb3, 0x9e, 0x9c, 0x85, 0xe7, 0x01, - 0x07, 0x44, 0xc6, 0xbd, 0x5c, 0xeb, 0x2d, 0xc4, 0x0a, 0x53, 0x49, 0xd9, 0x30, 0xd6, 0xd4, 0x24, - 0xec, 0xf3, 0x03, 0xc2, 0xae, 0x6e, 0x88, 0xd7, 0x86, 0x46, 0xbe, 0xf6, 0x3c, 0x83, 0xf6, 0x4a, - 0x95, 0x7b, 0x23, 0xc0, 0xc7, 0x16, 0x12, 0x44, 0x7d, 0x3a, 0x2f, 0x1e, 0x3c, 0x40, 0x9c, 0xfb, - 0x71, 0x5d, 0x90, 0x62, 0xfb, 0xa7, 0x59, 0x1e, 0x66, 0xc0, 0x92, 0x58, 0x12, 0x89, 0x31, 0xb6, - 0xa2, 0x59, 0x13, 0x66, 0x96, 0x40, 0x2d, 0x06, 0x87, 0x79, 0xcb, 0x9b, 0x0b, 0x1b, 0x48, 0x65, - 0xc6, 0x18, 0x33, 0xce, 0x1c, 0x73, 0x2f, 0x59, 0x14, 0xff, 0x9e, 0xd5, 0x00, 0xb0, 0x00, 0x3e, - 0xcd, 0xe3, 0x35, 0xf4, 0x2d, 0x1d, 0x20, 0x59, 0x65, 0x8e, 0x6d, 0x53, 0xb4, 0x3c, 0x8f, 0xa7, - 0x8b, 0x7c, 0x55, 0x84, 0x5a, 0x37, 0xf8, 0x5f, 0x3f, 0xe5, 0x39, 0x97, 0xed, 0xe2, 0xdb, 0xc0, - 0x20, 0xe5, 0x21, 0xa7, 0x4b, 0xc4, 0x16, 0x0e, 0xda, 0x7c, 0x3d, 0x2b, 0xb2, 0x90, 0x1c, 0x76, - 0x35, 0xa5, 0x58, 0xdb, 0xa2, 0x5b, 0x68, 0x79, 0x60, 0xf2, 0xce, 0xa7, 0xdb, 0x8f, 0x8c, 0xcb, - 0x61, 0x82, 0x07, 0x3d, 0x08, 0xa8, 0xc0, 0x3a, 0x05, 0xb5, 0xf9, 0x39, 0xc0, 0x06, 0x97, 0x17, - 0x84, 0x81, 0x3f, 0xd0, 0x83, 0x51, 0x3f, 0x24, 0x41, 0x47, 0x25, 0x32, 0x60, 0x84, 0xdc, 0x20, - 0xac, 0x54, 0xaf, 0xec, 0x0f, 0xef, 0x2e, 0xe9, 0xcb, 0xde, 0xec, 0x6d, 0xd8, 0x67, 0xc6, 0xa1, - 0xb2, 0xff, 0x65, 0x8c, 0x5f, 0xbc, 0x78, 0xc9, 0xb0, 0x9c, 0xc8, 0xbb, 0x1b, 0x0d, 0x85, 0x6e, - 0xc2, 0xd9, 0xff, 0x44, 0xc5, 0xbb, 0x7d, 0x8a, 0xd2, 0x74, 0x9e, 0xd1, 0x88, 0x26, 0xce, 0xd5, - 0x34, 0x89, 0x56, 0xeb, 0x98, 0x6d, 0x27, 0x72, 0xe3, 0xeb, 0xb0, 0xa6, 0x2e, 0x45, 0x44, 0x6a, - 0x26, 0x24, 0xac, 0xc9, 0xd3, 0x40, 0x84, 0x75, 0x2d, 0xb2, 0xf4, 0x76, 0x9f, 0x29, 0x1b, 0xcd, - 0x8f, 0xda, 0x9b, 0xe7, 0xf0, 0x00, 0xaa, 0x79, 0x5d, 0xa7, 0x8d, 0xe6, 0x14, 0x4a, 0x5b, 0x1e, - 0x9e, 0x38, 0xd5, 0x1b, 0xfa, 0x4e, 0xe0, 0x34, 0xdc, 0xaa, 0x77, 0xc6, 0x67, 0xd6, 0xd5, 0x6f, - 0xf3, 0xe2, 0x77, 0x79, 0xed, 0xbb, 0x41, 0xc7, 0x8d, 0x4a, 0x2b, 0x35, 0x62, 0x60, 0x35, 0x01, - 0x9d, 0x1a, 0x30, 0x8d, 0x22, 0xcc, 0x12, 0x0b, 0x72, 0x30, 0x32, 0x91, 0x0d, 0x70, 0xac, 0x69, - 0x0a, 0x78, 0xf0, 0x68, 0x40, 0x01, 0x8b, 0x0a, 0x3b, 0xf1, 0x9a, 0xa9, 0x52, 0x8d, 0x84, 0xa0, - 0x9d, 0x0f, 0x16, 0x11, 0x08, 0x67, 0x08, 0xc2, 0x11, 0xc5, 0x45, 0xac, 0xdc, 0x83, 0x06, 0x32, - 0x6d, 0x9c, 0x95, 0x14, 0xd0, 0xa6, 0x8c, 0x75, 0xeb, 0x5c, 0x45, 0xb0, 0xd0, 0x40, 0x53, 0xd1, - 0x25, 0x75, 0x57, 0xb1, 0xe9, 0x86, 0x8a, 0xa1, 0xdf, 0xc2, 0x74, 0x57, 0x41, 0x7c, 0x9e, 0x83, - 0x4b, 0xa2, 0x77, 0x71, 0xdb, 0xed, 0x6a, 0x7f, 0x89, 0x3e, 0xa8, 0x83, 0x39, 0x2b, 0x5a, 0x1e, - 0x5a, 0xc8, 0x5f, 0xf2, 0xca, 0x83, 0x43, 0xf2, 0x4e, 0x55, 0xfb, 0x78, 0x78, 0xec, 0x16, 0x76, - 0xb5, 0xb5, 0xba, 0xdd, 0xdd, 0x9b, 0x93, 0xcd, 0x13, 0x18, 0x8c, 0x62, 0x32, 0x44, 0x1f, 0x85, - 0x23, 0x8a, 0xbc, 0x34, 0x21, 0x9f, 0x9f, 0x27, 0xc3, 0xe3, 0xe2, 0xa4, 0xe8, 0x4d, 0xd0, 0x66, - 0xd9, 0x3b, 0x7c, 0x08, 0xbc, 0xc0, 0x32, 0x78, 0x22, 0x4b, 0x74, 0x27, 0x87, 0xd0, 0x7b, 0xef, - 0xc9, 0x10, 0x12, 0x41, 0xa2, 0xed, 0x02, 0x39, 0xe2, 0x3b, 0x7e, 0x69, 0x2f, 0xf2, 0xfc, 0x69, - 0xb9, 0x52, 0x79, 0xf3, 0x4a, 0x19, 0xe8, 0x5c, 0x2e, 0x12, 0xf1, 0x5c, 0xbc, 0x24, 0x7d, 0x19, - 0x9a, 0x63, 0x41, 0xde, 0x3b, 0xd5, 0x6b, 0xec, 0xbe, 0x2e, 0x26, 0x7d, 0x48, 0xa5, 0x59, 0x14, - 0x0d, 0x2f, 0x35, 0x56, 0x04, 0xb9, 0x7b, 0xc5, 0x8b, 0x91, 0xc3, 0xd2, 0x35, 0x93, 0xe6, 0x71, - 0x48, 0xea, 0x2b, 0x3c, 0x23, 0x38, 0x09, 0x5d, 0x26, 0x5d, 0x8d, 0x13, 0x25, 0xf2, 0x72, 0x0c, - 0x77, 0x38, 0xf3, 0x4e, 0x1c, 0xa9, 0x7b, 0x0c, 0xa4, 0xfa, 0x0e, 0x8e, 0xf5, 0xed, 0x39, 0x05, - 0x99, 0xcf, 0x4c, 0xa7, 0x25, 0x00, 0x08, 0xa5, 0x01, 0x09, 0xf6, 0x39, 0x64, 0x7b, 0xe6, 0xaf, - 0x51, 0xef, 0xe4, 0xb1, 0x50, 0x3a, 0x58, 0x4d, 0x2f, 0x42, 0x81, 0xcf, 0x57, 0x90, 0x6f, 0x53, - 0x2e, 0x26, 0x82, 0x1e, 0xd5, 0xf5, 0x45, 0x92, 0xbe, 0x0e, 0x6f, 0x4e, 0xc3, 0x0b, 0x9d, 0x34, - 0xe6, 0x32, 0x51, 0xfe, 0x53, 0x62, 0x94, 0x8a, 0x9f, 0x95, 0x1f, 0x51, 0xfe, 0xea, 0xba, 0xfc, - 0xc2, 0x68, 0x55, 0xa7, 0x30, 0x68, 0x3b, 0xe5, 0xed, 0x05, 0xc6, 0xdc, 0xc2, 0x62, 0x20, 0x11, - 0x51, 0x9d, 0xbf, 0x5c, 0xcc, 0x6e, 0x2a, 0x7d, 0xdc, 0xe0, 0xf3, 0xc1, 0xd8, 0xf1, 0xd9, 0xd0, - 0x87, 0xff, 0x9d, 0xcb, 0x64, 0x90, 0x58, 0xa7, 0x20, 0x4e, 0x60, 0x8b, 0x67, 0x54, 0x5c, 0x65, - 0x24, 0xf1, 0x77, 0xeb, 0x0c, 0x52, 0x9f, 0x0e, 0x65, 0x02, 0x3f, 0xfa, 0x18, 0x2e, 0xbe, 0xdf, - 0x40, 0xaa, 0x4a, 0x9c, 0x43, 0xaa, 0xf1, 0x89, 0xef, 0x17, 0x2d, 0xf8, 0x15, 0xa2, 0xdc, 0x2c, - 0xc5, 0x2f, 0x10, 0xc8, 0xe7, 0x89, 0x26, 0xa2, 0x37, 0x92, 0x19, 0x18, 0x76, 0x8e, 0x55, 0x9b, - 0x66, 0x23, 0xe1, 0xc5, 0x73, 0x54, 0xca, 0x60, 0x92, 0x2f, 0x00, 0x49, 0xcc, 0xaf, 0xf4, 0x06, - 0x68, 0xe8, 0xbb, 0x04, 0x83, 0x56, 0x70, 0x0a, 0x6c, 0x41, 0x80, 0xa8, 0x51, 0x7d, 0x45, 0xef, - 0x71, 0x57, 0x66, 0x8f, 0x85, 0xd5, 0x51, 0x04, 0x49, 0x24, 0x3d, 0x0a, 0x22, 0xa4, 0x38, 0xce, - 0x52, 0xd6, 0xee, 0x13, 0xcf, 0xe6, 0x36, 0x72, 0x42, 0xbe, 0xb8, 0xa3, 0x05, 0x96, 0x73, 0xc2, - 0x37, 0x9d, 0xa1, 0x2e, 0xe2, 0x1c, 0x3e, 0x57, 0x62, 0x28, 0x64, 0x5c, 0xe7, 0x2b, 0xf9, 0xe0, - 0x33, 0x54, 0xf9, 0xf2, 0x0b, 0x56, 0x34, 0x8a, 0xc3, 0xa7, 0x43, 0x1f, 0x3e, 0x6c, 0x6d, 0x1f, - 0x0f, 0xf2, 0xcb, 0x2f, 0x64, 0xc3, 0x9a, 0xf3, 0x66, 0x4d, 0xe3, 0x5f, 0xd1, 0x09, 0x24, 0x8a, - 0xa3, 0xe2, 0x56, 0xba, 0x59, 0x7b, 0xd8, 0x5e, 0xb4, 0x14, 0xee, 0x81, 0x3e, 0x60, 0x2f, 0x23, - 0xe0, 0x57, 0x12, 0xf1, 0xe0, 0x01, 0x8d, 0xe4, 0x3b, 0x04, 0x25, 0x14, 0xeb, 0x99, 0xa0, 0x3d, - 0x16, 0x8f, 0x86, 0xb0, 0xe3, 0xb1, 0x6e, 0x16, 0x02, 0xd7, 0x91, 0x17, 0x7f, 0xce, 0x53, 0x62, - 0xab, 0x08, 0x61, 0xb0, 0xeb, 0x2d, 0xfd, 0x67, 0x0c, 0x20, 0x4d, 0x50, 0xa7, 0xc5, 0x1d, 0xa2, - 0x0d, 0xc9, 0x35, 0x66, 0x4e, 0xc0, 0xc4, 0x0d, 0x29, 0x0f, 0xf5, 0x6f, 0x6f, 0x5f, 0x2e, 0xdc, - 0xce, 0x3c, 0x8f, 0x3b, 0x5e, 0x19, 0x7f, 0x16, 0xda, 0x4a, 0x33, 0xe1, 0x46, 0xb4, 0x32, 0x22, - 0x12, 0x47, 0x50, 0x4f, 0xe9, 0x21, 0x04, 0x9a, 0xde, 0xb0, 0xe5, 0xf9, 0x02, 0xed, 0x6f, 0x7c, - 0xe2, 0x96, 0xef, 0xcb, 0xf2, 0x33, 0xa9, 0x13, 0x41, 0x5e, 0xc5, 0xb0, 0xc4, 0xf8, 0x9f, 0xe7, - 0xf0, 0x28, 0xed, 0x87, 0x5f, 0xbf, 0xfc, 0x42, 0xbe, 0x12, 0x29, 0x1f, 0xd0, 0xe1, 0x34, 0x42, - 0xb3, 0x77, 0x45, 0x14, 0xd3, 0xa0, 0x69, 0xa1, 0xfa, 0x30, 0x5f, 0x53, 0xcd, 0x6e, 0xb0, 0x68, - 0x34, 0x43, 0xf5, 0xfa, 0xb5, 0x86, 0x07, 0xfe, 0x29, 0x5f, 0xb7, 0x70, 0x0d, 0xb8, 0xf9, 0xe2, - 0xf0, 0x09, 0xe6, 0x2e, 0xea, 0x41, 0x6e, 0xd8, 0x6b, 0x87, 0x16, 0x4c, 0x99, 0x31, 0x5a, 0x56, - 0xd2, 0xe7, 0x85, 0x67, 0xa0, 0xd6, 0x60, 0xe9, 0xcc, 0xaf, 0x1d, 0x4f, 0x82, 0x45, 0xde, 0x6b, - 0x9d, 0x0c, 0xc7, 0xd5, 0x25, 0xaa, 0x4c, 0xbd, 0x98, 0xce, 0x5e, 0x7a, 0x5f, 0x7e, 0x01, 0xb3, - 0x51, 0xcf, 0x09, 0x55, 0x96, 0x29, 0xff, 0xf6, 0xf6, 0xb9, 0x0a, 0xdb, 0xee, 0x96, 0x6f, 0x0e, - 0x79, 0x0d, 0x4b, 0xa5, 0x32, 0x6b, 0x0b, 0xa6, 0x32, 0x70, 0xd9, 0x74, 0x0c, 0x78, 0x24, 0x3c, - 0x4d, 0xe9, 0x3a, 0x40, 0xbe, 0x23, 0xa6, 0x64, 0xb7, 0x41, 0x1f, 0x37, 0x47, 0xa2, 0x1b, 0x62, - 0x31, 0x6f, 0x55, 0x4f, 0xf0, 0x49, 0x36, 0x36, 0x9a, 0xc7, 0xc9, 0x98, 0x3d, 0x74, 0x01, 0x1b, - 0x54, 0x23, 0x35, 0x40, 0xa8, 0xb7, 0x92, 0x08, 0x02, 0x6a, 0x87, 0xd4, 0xda, 0x66, 0x3f, 0x32, - 0x51, 0x3e, 0x43, 0xe9, 0xeb, 0x51, 0x43, 0x6e, 0x44, 0x4a, 0x52, 0x9e, 0xc9, 0x4b, 0xf8, 0x7e, - 0xff, 0x9c, 0x7b, 0xc7, 0xb5, 0xed, 0xf3, 0xd3, 0x68, 0x6e, 0x27, 0xbf, 0x4a, 0xd3, 0xe2, 0xb2, - 0x63, 0x2c, 0x1e, 0x14, 0xe3, 0x65, 0xa2, 0xf7, 0xe8, 0xa4, 0xce, 0xab, 0xd3, 0xeb, 0x45, 0x50, - 0x88, 0x9b, 0x6c, 0x40, 0x52, 0x1a, 0xa7, 0x9a, 0x05, 0x71, 0x2b, 0x8c, 0x65, 0x63, 0x63, 0x4e, - 0xf9, 0x65, 0x7a, 0xf3, 0x16, 0x9f, 0x66, 0x72, 0x29, 0x3c, 0xa0, 0x08, 0x51, 0xa2, 0x56, 0x44, - 0x8d, 0x26, 0x49, 0x01, 0x7c, 0x9a, 0x76, 0xa2, 0x7c, 0xd2, 0x09, 0x7b, 0xd8, 0x94, 0x8f, 0x43, - 0xe0, 0xd4, 0xa0, 0x25, 0x4a, 0x34, 0x57, 0x8e, 0x1b, 0x3e, 0x11, 0x0e, 0xfd, 0x80, 0xb3, 0x0f, - 0x7b, 0x46, 0x18, 0xcf, 0xe3, 0x70, 0x9a, 0x29, 0xfc, 0x97, 0x23, 0xe4, 0x36, 0x79, 0xc6, 0xc0, - 0x13, 0x5f, 0x11, 0x47, 0x0c, 0x8d, 0x74, 0x90, 0x8f, 0xee, 0x50, 0x26, 0x7b, 0x16, 0xb2, 0x41, - 0xa2, 0xa5, 0x34, 0x71, 0x69, 0x02, 0x56, 0x9b, 0x1b, 0x85, 0x14, 0x63, 0xa7, 0x19, 0x4c, 0xb1, - 0xb0, 0x47, 0xba, 0x69, 0xc2, 0x2c, 0x1a, 0x2d, 0xa3, 0x95, 0x00, 0x7a, 0x76, 0xf8, 0xa7, 0xe1, - 0xd0, 0xab, 0x21, 0x08, 0x16, 0x22, 0x95, 0x04, 0x43, 0x94, 0xb7, 0xbc, 0x06, 0x70, 0xe7, 0x79, - 0x0a, 0x40, 0x92, 0x81, 0x8a, 0x52, 0x41, 0xb7, 0xcd, 0x01, 0xce, 0x11, 0x9c, 0x3c, 0x7d, 0xf1, - 0x06, 0xa0, 0x80, 0x97, 0x9d, 0xc2, 0x25, 0x50, 0x95, 0x4b, 0x51, 0x5c, 0x86, 0x02, 0x8f, 0xf7, - 0xbe, 0x78, 0xb9, 0xa4, 0x8f, 0x28, 0xcf, 0xd7, 0xf4, 0x74, 0x5c, 0x0e, 0x88, 0x92, 0xfb, 0x50, - 0x70, 0x06, 0x38, 0x22, 0x6e, 0x53, 0x38, 0x3c, 0x5f, 0x9c, 0xbe, 0xe9, 0x77, 0x2c, 0x1a, 0xab, - 0x47, 0x44, 0xc0, 0xad, 0x0e, 0xa9, 0x75, 0x25, 0x2d, 0x30, 0xec, 0x51, 0xaa, 0x04, 0x0f, 0x2f, - 0xaa, 0xb1, 0xed, 0x8c, 0x31, 0xac, 0x52, 0x92, 0x69, 0x42, 0xf5, 0x46, 0x99, 0x9b, 0x33, 0xf9, - 0x20, 0x1a, 0x8f, 0xeb, 0xe4, 0x38, 0x44, 0x04, 0x2f, 0x76, 0x9c, 0xbf, 0x7c, 0x6a, 0x21, 0xf5, - 0xb8, 0xc5, 0xbb, 0x44, 0x68, 0x7c, 0xbe, 0xe0, 0x56, 0x54, 0xbd, 0x6e, 0x57, 0x1f, 0x1c, 0xa8, - 0x31, 0x9a, 0x50, 0x91, 0xb3, 0x5b, 0xc5, 0x66, 0x20, 0xd1, 0xd1, 0x17, 0xaa, 0xb1, 0x40, 0x3f, - 0x5a, 0xe0, 0x50, 0x71, 0x0b, 0x43, 0xde, 0xc4, 0xea, 0xd1, 0xab, 0xf6, 0x1f, 0x75, 0x47, 0xd4, - 0x3a, 0x0c, 0x17, 0x28, 0xc4, 0x07, 0xeb, 0x69, 0xde, 0xf0, 0xc2, 0x69, 0x79, 0x2e, 0x31, 0x9f, - 0x5f, 0xfe, 0xc6, 0xef, 0x40, 0xf2, 0xcc, 0xc5, 0xd7, 0x77, 0xd1, 0x76, 0xe7, 0x7b, 0x89, 0xd0, - 0x18, 0x96, 0xa1, 0xe7, 0xb5, 0xcd, 0xa7, 0x8b, 0x63, 0x68, 0xc1, 0xc5, 0x1c, 0xcf, 0xc1, 0x46, - 0x70, 0xf8, 0xc8, 0x10, 0xc1, 0x6e, 0x52, 0x81, 0x83, 0x03, 0xc7, 0xd9, 0xb6, 0x3f, 0xa7, 0x88, - 0x23, 0x69, 0x78, 0x4f, 0x71, 0xe7, 0x9b, 0xcc, 0x62, 0x19, 0x67, 0xd0, 0x99, 0x62, 0xdc, 0x80, - 0xe2, 0xd2, 0xee, 0xdd, 0xac, 0xa8, 0x2f, 0x3d, 0xde, 0x10, 0x87, 0x65, 0x3c, 0x28, 0xc7, 0xe5, - 0xe5, 0x60, 0xf9, 0xe1, 0xba, 0x3f, 0x35, 0xbe, 0x93, 0x1d, 0x5e, 0xc0, 0x42, 0x36, 0xb5, 0x4f, - 0x9f, 0x76, 0x27, 0xd4, 0x07, 0x36, 0x40, 0x66, 0x00, 0xfa, 0x41, 0x6f, 0x54, 0x16, 0x8b, 0x63, - 0xba, 0xef, 0x2e, 0x5e, 0xbd, 0xf8, 0xee, 0x68, 0x00, 0x5f, 0x2a, 0x2d, 0x5d, 0x99, 0x49, 0x03, - 0x59, 0x5a, 0xd5, 0x91, 0x2b, 0x60, 0x3f, 0x7e, 0x88, 0xfc, 0x60, 0x62, 0x2f, 0xc3, 0xce, 0xf7, - 0x10, 0xbf, 0xbe, 0x53, 0x2c, 0x63, 0x6f, 0xb4, 0xd5, 0xaf, 0x21, 0xaa, 0xd5, 0xc1, 0x51, 0x6d, - 0x6b, 0xaf, 0x4c, 0xbf, 0x0a, 0x13, 0x05, 0x19, 0x63, 0xb8, 0x7b, 0x0e, 0x67, 0xf7, 0xf3, 0x8c, - 0xe5, 0x70, 0x1a, 0x06, 0x93, 0xae, 0xf6, 0x1a, 0x8b, 0x84, 0xd3, 0xa0, 0x84, 0x72, 0x0d, 0xdc, - 0xff, 0x0d, 0x9c, 0xd3, 0x0a, 0xc4, 0x49, 0x13, 0xda, 0xc8, 0x39, 0x94, 0x29, 0x9f, 0x09, 0xec, - 0x8b, 0x6c, 0xb5, 0xf3, 0xad, 0x4e, 0x7a, 0x8e, 0xdb, 0x9e, 0x17, 0x54, 0xf9, 0x9d, 0x41, 0x9c, - 0xaf, 0xe6, 0x3b, 0x81, 0xdc, 0x30, 0x08, 0xa8, 0xf2, 0x79, 0xb0, 0x35, 0xf6, 0xc2, 0xa5, 0x98, - 0xad, 0xac, 0x71, 0x00, 0x3b, 0xe4, 0x1c, 0x1b, 0xab, 0x07, 0x7c, 0xb6, 0x81, 0x56, 0x88, 0xda, - 0x6d, 0x99, 0xc0, 0x84, 0x8f, 0x70, 0x1f, 0xe5, 0xb4, 0x81, 0x04, 0x8c, 0x82, 0xc3, 0x7f, 0x84, - 0xc0, 0xa5, 0xba, 0xcd, 0x55, 0xbc, 0x81, 0xab, 0xc0, 0x29, 0xba, 0x42, 0x4d, 0x0a, 0x84, 0x52, - 0x71, 0x1d, 0x65, 0xc5, 0x7a, 0x1a, 0x7b, 0xfa, 0xd1, 0xc6, 0x1d, 0xd4, 0x62, 0x9e, 0x2c, 0xf5, - 0x14, 0xe6, 0x15, 0x9a, 0x80, 0x9e, 0x42, 0x9a, 0x80, 0x49, 0x9a, 0x30, 0x54, 0x34, 0x61, 0x47, - 0x9b, 0x0b, 0xa4, 0x83, 0xb2, 0xcd, 0x45, 0xa5, 0xcd, 0x85, 0x49, 0x14, 0xf9, 0xa5, 0xca, 0x47, - 0x7f, 0x54, 0x6d, 0x36, 0x10, 0x63, 0x90, 0xfd, 0x15, 0x39, 0xfe, 0x4b, 0x88, 0xd6, 0xe4, 0x50, - 0x2c, 0x94, 0xb7, 0xc3, 0x3e, 0x34, 0x19, 0xea, 0x57, 0x9e, 0x93, 0x87, 0x66, 0x2a, 0x34, 0x19, - 0xca, 0xfc, 0x1e, 0x34, 0xd9, 0x7a, 0x48, 0x15, 0x5f, 0x71, 0xfd, 0xa0, 0xce, 0xd4, 0xba, 0x34, - 0x24, 0xdf, 0x53, 0xef, 0x78, 0x16, 0x07, 0x36, 0x4f, 0xe4, 0xd1, 0x66, 0x9d, 0x63, 0xc7, 0x13, - 0x29, 0xc5, 0x36, 0x73, 0x01, 0x1d, 0xf9, 0xe6, 0x7a, 0xa5, 0xad, 0x4e, 0xf5, 0x21, 0xe1, 0x1f, - 0xa7, 0x9b, 0xe8, 0x6a, 0x7d, 0x25, 0x78, 0x87, 0x88, 0x74, 0xa9, 0x5f, 0x11, 0x16, 0x18, 0x85, - 0xf4, 0x12, 0xd8, 0x19, 0x39, 0x2b, 0xe4, 0xd4, 0x4c, 0x5d, 0x04, 0x72, 0x28, 0x38, 0x15, 0x21, - 0xc3, 0x11, 0x08, 0x57, 0xe7, 0x4a, 0xa9, 0xd1, 0x14, 0xa8, 0x9a, 0xb4, 0x18, 0x35, 0xf9, 0x51, - 0xb2, 0xbc, 0x19, 0xb0, 0x51, 0x6e, 0x58, 0xf2, 0x15, 0xab, 0x2c, 0xbd, 0x80, 0xd6, 0xb0, 0x6a, - 0xc8, 0xda, 0x1d, 0xf1, 0x70, 0x34, 0x1c, 0x0a, 0xbc, 0x82, 0x07, 0xcd, 0x18, 0xd9, 0x9a, 0x29, - 0x50, 0x69, 0x9e, 0xe4, 0x18, 0xa0, 0x16, 0x64, 0x7f, 0xe0, 0xbb, 0x30, 0xbd, 0x0b, 0x75, 0xe7, - 0xe1, 0x4f, 0xc3, 0x45, 0x78, 0xe1, 0xb3, 0x9a, 0x03, 0xd6, 0x5d, 0xd5, 0xda, 0xfe, 0xc1, 0x17, - 0x5f, 0x3d, 0x7e, 0xfc, 0xd8, 0x4a, 0xf2, 0x70, 0xe1, 0x42, 0xf3, 0xe5, 0x95, 0x16, 0x29, 0xa9, - 0xc3, 0x58, 0x2f, 0x5d, 0x76, 0x3a, 0xde, 0xd9, 0xb0, 0x2e, 0xad, 0xc2, 0x60, 0x60, 0x48, 0x2d, - 0xd2, 0x19, 0x12, 0x1c, 0x83, 0xad, 0x62, 0x52, 0x60, 0x4c, 0xae, 0xb6, 0xd8, 0x1f, 0x08, 0xad, - 0xf3, 0x6d, 0xfe, 0xc1, 0x63, 0xf8, 0xa8, 0x69, 0x13, 0x79, 0xd8, 0xab, 0x6a, 0x58, 0xad, 0x0a, - 0x54, 0x0b, 0x79, 0x6c, 0xa6, 0x2f, 0x34, 0x04, 0x99, 0xc1, 0xcc, 0xb7, 0xe3, 0x72, 0x50, 0x79, - 0xcf, 0x91, 0xb8, 0x09, 0xc5, 0x81, 0x5a, 0xd1, 0xca, 0x73, 0x89, 0x0f, 0x78, 0x88, 0x25, 0x44, - 0xdf, 0xf2, 0x0f, 0x36, 0xa2, 0x50, 0xdb, 0x13, 0xab, 0xb4, 0x33, 0xc2, 0x82, 0x8e, 0x04, 0x08, - 0x16, 0x6a, 0x1d, 0x2a, 0x90, 0xb6, 0x0f, 0x9e, 0x38, 0x60, 0x91, 0xa0, 0x04, 0x13, 0x52, 0xbc, - 0x7d, 0x66, 0x4a, 0xd5, 0x2b, 0x60, 0x5a, 0xcd, 0xf7, 0x83, 0xef, 0x6a, 0x6e, 0x56, 0xc5, 0x51, - 0x52, 0xaf, 0xc0, 0xd3, 0x7a, 0xb2, 0x7f, 0xa5, 0xa4, 0x42, 0x6a, 0x0b, 0x9f, 0x25, 0xbd, 0x86, - 0x39, 0x0f, 0x5c, 0xa6, 0xca, 0x44, 0x90, 0x65, 0xfd, 0x03, 0xb6, 0xa8, 0x63, 0x71, 0x0f, 0xf5, - 0x31, 0x54, 0x04, 0xc9, 0xfe, 0xc7, 0x8f, 0x34, 0xac, 0x63, 0x68, 0xda, 0x23, 0x98, 0x23, 0xaf, - 0x8a, 0x47, 0x00, 0x16, 0x2d, 0xa9, 0x79, 0x3b, 0x35, 0x51, 0x83, 0x86, 0x8e, 0x3f, 0xd8, 0x44, - 0x80, 0xa5, 0xc6, 0x26, 0xcc, 0x7b, 0xf7, 0xd2, 0x65, 0x80, 0xd6, 0xe9, 0x09, 0x3f, 0x07, 0xfc, - 0x06, 0x5f, 0x7b, 0xeb, 0xd8, 0xb2, 0x85, 0x8b, 0x9a, 0x48, 0x0f, 0x69, 0xa5, 0x94, 0xb5, 0x03, - 0x92, 0x01, 0xda, 0x9a, 0x78, 0x1d, 0x57, 0xeb, 0x27, 0xf1, 0xb3, 0xfd, 0xab, 0x9f, 0xde, 0x26, - 0xf3, 0x6a, 0x03, 0x4a, 0xc3, 0x59, 0x6f, 0xa5, 0xa9, 0x9d, 0xe5, 0x66, 0xd6, 0xc1, 0x55, 0xd0, - 0xaa, 0x49, 0xaf, 0x5d, 0x99, 0xf4, 0x15, 0x5e, 0x7f, 0x2c, 0x85, 0x71, 0xa2, 0x4c, 0x0d, 0xb4, - 0x96, 0x36, 0xfc, 0xb7, 0x59, 0xd4, 0xf1, 0xbc, 0x3d, 0x0b, 0x9f, 0xae, 0xc2, 0x70, 0xb1, 0x7f, - 0xf1, 0x97, 0xa8, 0x7c, 0xc8, 0xa3, 0xe2, 0x76, 0xff, 0x2a, 0x3f, 0x77, 0x34, 0x92, 0xb1, 0x6e, - 0xd7, 0x6b, 0xd0, 0xbf, 0xdd, 0xdc, 0x64, 0xd3, 0x55, 0x47, 0x01, 0x40, 0x92, 0x2c, 0x9c, 0x38, - 0xfb, 0x19, 0x6a, 0x29, 0x6c, 0x56, 0x24, 0xb9, 0xa1, 0x0d, 0x6d, 0x55, 0x10, 0xad, 0x40, 0x9e, - 0x6d, 0x54, 0x0e, 0x61, 0x03, 0x35, 0xc5, 0x10, 0x26, 0x56, 0x95, 0x42, 0xb5, 0xb4, 0x76, 0x85, - 0x10, 0x4e, 0xcd, 0xb5, 0xb4, 0xc7, 0xc7, 0xc7, 0x22, 0x82, 0x6d, 0x24, 0x86, 0x9b, 0x21, 0x10, - 0x97, 0x7a, 0xf3, 0xa4, 0x11, 0x02, 0x9a, 0x96, 0xc1, 0x81, 0x5e, 0x6a, 0x94, 0x6c, 0x4d, 0x33, - 0xec, 0x38, 0xdc, 0x88, 0x76, 0xe2, 0xd1, 0xc4, 0x9c, 0x81, 0x6c, 0xda, 0x2e, 0xd2, 0x1b, 0xed, - 0xd0, 0x3d, 0x81, 0x68, 0xbb, 0xa2, 0x23, 0xa8, 0x2e, 0x88, 0x33, 0x1e, 0xbe, 0x99, 0x02, 0x35, - 0xc5, 0xdb, 0x38, 0x63, 0x2e, 0xda, 0x8a, 0x92, 0x2e, 0xe7, 0x4a, 0xe4, 0x7d, 0x89, 0xce, 0x70, - 0x44, 0x24, 0x10, 0xf5, 0x09, 0x5b, 0x03, 0xe7, 0x2b, 0xbc, 0x94, 0x5b, 0xdb, 0xe1, 0xe8, 0x28, - 0x08, 0x67, 0x97, 0x3b, 0xf5, 0xc5, 0x4c, 0x2b, 0xbc, 0xa6, 0xf4, 0x46, 0x17, 0xae, 0x0f, 0xfd, - 0xf0, 0xe8, 0xdc, 0x26, 0x0b, 0x11, 0xa9, 0xdb, 0xb7, 0xfa, 0x53, 0x7e, 0x21, 0x2e, 0xfc, 0x3d, - 0x4f, 0x93, 0xb7, 0x75, 0xe5, 0x93, 0xa9, 0x25, 0x86, 0xbe, 0xae, 0x40, 0xf2, 0xf3, 0x05, 0xf0, - 0x66, 0xb3, 0x34, 0x0f, 0x25, 0xbb, 0x40, 0xdd, 0x5a, 0xea, 0x7b, 0xa5, 0x7e, 0x76, 0x35, 0x3d, - 0x34, 0x5a, 0xf7, 0xcc, 0xae, 0x6c, 0x85, 0x8f, 0xad, 0x9c, 0xf1, 0x59, 0x57, 0x2d, 0x49, 0x2f, - 0x0c, 0x44, 0xe9, 0x8b, 0xe0, 0xc0, 0x1e, 0x35, 0xea, 0x9d, 0x97, 0x1b, 0xb4, 0x7b, 0x76, 0xb0, - 0x4a, 0x78, 0xd8, 0x58, 0xc2, 0x5a, 0x96, 0x0e, 0xeb, 0x5c, 0xd7, 0x19, 0x2e, 0xa1, 0x9c, 0x1c, - 0x00, 0xbc, 0x33, 0xc0, 0x01, 0x0e, 0xe8, 0x1e, 0x55, 0x27, 0xe0, 0xaf, 0x0e, 0x95, 0x44, 0xde, - 0xd3, 0x2e, 0xba, 0x4a, 0xa1, 0xbf, 0xa0, 0x03, 0xdd, 0x74, 0xe4, 0x54, 0x65, 0x26, 0x13, 0x5c, - 0xf9, 0xd1, 0x47, 0x83, 0x8c, 0x04, 0x1a, 0x29, 0xa5, 0x71, 0x2a, 0x7f, 0x3e, 0xfd, 0xe9, 0xb5, - 0xf4, 0x62, 0x88, 0x96, 0xb7, 0xba, 0x9e, 0xd4, 0x0e, 0x85, 0x05, 0xfa, 0x24, 0xba, 0x30, 0x36, - 0x32, 0x1c, 0x5c, 0x85, 0xc5, 0x65, 0xba, 0x08, 0x88, 0xfb, 0xf5, 0xbf, 0xfc, 0xe2, 0x32, 0x9c, - 0x02, 0x2d, 0xc8, 0x03, 0xcc, 0x72, 0xa4, 0xe3, 0x69, 0x8f, 0x1e, 0x42, 0x0d, 0x80, 0x54, 0xae, - 0xf0, 0x66, 0x0e, 0x69, 0xd8, 0x68, 0xec, 0x63, 0x01, 0x1c, 0x71, 0x86, 0x97, 0x38, 0xde, 0xbd, - 0xfd, 0xbe, 0xf7, 0x0d, 0x9e, 0xc6, 0xd0, 0x04, 0xba, 0x06, 0x04, 0x08, 0x55, 0xd4, 0x70, 0x7f, - 0xf9, 0x05, 0x3d, 0xee, 0xec, 0x66, 0x68, 0x03, 0x39, 0xd6, 0x96, 0x05, 0xf8, 0xec, 0xa7, 0xbf, - 0x69, 0x1d, 0x99, 0xa9, 0xa4, 0xb2, 0xd0, 0x08, 0xcb, 0x61, 0x4f, 0x9c, 0xac, 0x5b, 0xc3, 0x24, - 0xd9, 0x9c, 0xa5, 0x41, 0x34, 0x91, 0x01, 0x2a, 0xd8, 0xe8, 0x20, 0x57, 0xb8, 0xa6, 0x15, 0x33, - 0x31, 0xc9, 0x33, 0x74, 0xa2, 0x9d, 0x17, 0xe8, 0x3b, 0x82, 0x43, 0x58, 0x81, 0x6c, 0x12, 0x1a, - 0x4a, 0x36, 0x2c, 0x8e, 0xa5, 0xfb, 0xf9, 0x7a, 0x3e, 0x47, 0xae, 0xd1, 0x56, 0x00, 0x23, 0xe5, - 0x23, 0xf0, 0xc8, 0x86, 0xd5, 0x02, 0x68, 0x85, 0xea, 0xa4, 0xd3, 0xf1, 0x6f, 0x27, 0x9d, 0x23, - 0x76, 0x58, 0x57, 0x32, 0xe5, 0xd0, 0x39, 0x96, 0xd6, 0xc9, 0xa3, 0x01, 0xe7, 0x10, 0x0f, 0x4d, - 0x1d, 0x85, 0xcb, 0x25, 0x60, 0x57, 0xae, 0x1f, 0x28, 0x15, 0x83, 0x01, 0x1b, 0xd2, 0x45, 0x9e, - 0x02, 0xf5, 0xd6, 0xf4, 0x33, 0x4a, 0x84, 0x59, 0xde, 0xb3, 0xbe, 0x50, 0xcb, 0x32, 0x11, 0x77, - 0x4e, 0x04, 0xd2, 0x89, 0xa1, 0x0f, 0xf3, 0xba, 0x23, 0xa0, 0x90, 0xb8, 0x9d, 0x9d, 0xa0, 0x52, - 0x7c, 0x5b, 0xeb, 0x3f, 0xcd, 0x0a, 0x57, 0x52, 0x06, 0x83, 0x6c, 0xd7, 0xba, 0x85, 0x99, 0x6e, - 0x90, 0xa6, 0x75, 0x8e, 0xf8, 0x14, 0x56, 0x12, 0x04, 0xd0, 0x41, 0x16, 0xac, 0xf0, 0x50, 0xed, - 0x56, 0x47, 0xd7, 0x8f, 0x16, 0xa2, 0xdb, 0xb1, 0x65, 0xc7, 0x5f, 0xdc, 0xb6, 0x72, 0xde, 0xd8, - 0x39, 0x6e, 0xc8, 0x23, 0x02, 0xd5, 0xed, 0x1c, 0x0d, 0xb8, 0x67, 0x92, 0xa1, 0x3a, 0x8a, 0xba, - 0x51, 0xe1, 0x15, 0x6f, 0xd2, 0x26, 0x68, 0x4a, 0xef, 0xfd, 0xda, 0xc4, 0x54, 0x15, 0xcf, 0xfe, - 0xdc, 0x17, 0xa2, 0x46, 0x79, 0x0d, 0xd2, 0x72, 0x14, 0xbb, 0x61, 0x5a, 0x76, 0x0d, 0x40, 0xbd, - 0x65, 0xa0, 0xda, 0x88, 0xd3, 0xe9, 0x56, 0x3b, 0x01, 0x00, 0x01, 0x1c, 0x8f, 0x1b, 0x32, 0x28, - 0x5a, 0x77, 0xc7, 0x42, 0xb0, 0x2d, 0x52, 0x3d, 0xe3, 0x81, 0xe7, 0xcd, 0x58, 0x84, 0x87, 0xc6, - 0xf7, 0xed, 0x58, 0x08, 0xa5, 0x5b, 0x5d, 0xa6, 0x12, 0xb7, 0xfb, 0xf8, 0x5b, 0xe2, 0x7b, 0xc2, - 0x27, 0x31, 0xa6, 0x50, 0xfb, 0x8d, 0xdc, 0x18, 0x66, 0x90, 0x90, 0x5b, 0x91, 0x30, 0x65, 0x05, - 0x92, 0x07, 0xa9, 0x1d, 0xe0, 0x8e, 0x9d, 0xef, 0xa8, 0xc4, 0x0c, 0xfd, 0x66, 0x90, 0x0b, 0xa9, - 0xf8, 0xf7, 0x28, 0xef, 0x19, 0x15, 0x03, 0x94, 0xac, 0x78, 0x29, 0xdd, 0x46, 0x1a, 0x7d, 0x83, - 0x12, 0x1a, 0xd9, 0xf1, 0x88, 0x91, 0xc1, 0x21, 0xc5, 0x70, 0xa6, 0x7a, 0x6a, 0x90, 0x8e, 0xfb, - 0x8a, 0x3e, 0x1d, 0xe0, 0xe8, 0xf4, 0x58, 0x8b, 0xa8, 0x88, 0x43, 0x63, 0x2c, 0xca, 0xb4, 0xcd, - 0xd5, 0xc3, 0x45, 0xde, 0xbf, 0xb9, 0x86, 0x74, 0xc3, 0x4a, 0x5c, 0xe6, 0xcc, 0x31, 0x05, 0x0f, - 0x61, 0xcb, 0x46, 0x4e, 0xf9, 0x40, 0x82, 0x21, 0x43, 0xdb, 0x8e, 0xcb, 0x3a, 0x90, 0x04, 0xcc, - 0x36, 0x56, 0x52, 0xd0, 0xa4, 0x23, 0x41, 0x72, 0x18, 0x6c, 0xa7, 0xc7, 0xdb, 0xd0, 0x8d, 0x80, - 0x34, 0x98, 0x47, 0xe9, 0xba, 0x00, 0x65, 0x67, 0x59, 0x84, 0x42, 0x33, 0x19, 0xf5, 0xf3, 0x7e, - 0x12, 0x73, 0x65, 0x65, 0x5e, 0xa7, 0x94, 0xc5, 0x3a, 0x93, 0xc3, 0x94, 0xc6, 0xfd, 0xbc, 0xbf, - 0x5e, 0xac, 0xa0, 0x6b, 0xf8, 0xc2, 0x0c, 0xdb, 0xaa, 0x9e, 0xf7, 0x57, 0x39, 0x9a, 0xac, 0xaa, - 0x56, 0x75, 0x4c, 0x6f, 0x1c, 0xd6, 0xfc, 0xf6, 0x2d, 0x19, 0xb0, 0xc8, 0x3c, 0x4b, 0x2a, 0x10, - 0x62, 0x3e, 0xfa, 0xab, 0x18, 0xb8, 0x05, 0x62, 0x37, 0x48, 0x8c, 0x17, 0x81, 0x52, 0x04, 0x34, - 0x35, 0x31, 0xcf, 0xd5, 0xa4, 0xa8, 0xab, 0xf9, 0x3c, 0x59, 0xe2, 0x5d, 0xf8, 0xb6, 0xd2, 0x61, - 0x43, 0xe9, 0xe9, 0xa6, 0xad, 0x74, 0x51, 0x2f, 0x8d, 0xc6, 0x21, 0x31, 0x18, 0x0d, 0x5b, 0xab, - 0xd8, 0x75, 0x08, 0xe3, 0x22, 0xda, 0x7a, 0x5c, 0x49, 0x3b, 0x2d, 0xcc, 0x27, 0xc0, 0xb7, 0xf2, - 0x16, 0x59, 0x90, 0x15, 0xb5, 0xc1, 0xee, 0x61, 0x6d, 0x6d, 0xc3, 0x88, 0x11, 0x2d, 0x5d, 0xb6, - 0x57, 0xa0, 0x95, 0x3a, 0x8c, 0x61, 0x9f, 0x63, 0x83, 0x84, 0x30, 0x8b, 0xb1, 0xe0, 0xeb, 0xf3, - 0x5b, 0xfc, 0x20, 0x2b, 0x87, 0x64, 0xaa, 0x22, 0x69, 0xe3, 0xc0, 0xa2, 0xe7, 0x5a, 0x36, 0xac, - 0x1a, 0x9d, 0x5e, 0xa7, 0x42, 0x0d, 0xe0, 0xc0, 0x38, 0xbf, 0x4a, 0xe1, 0x6d, 0xac, 0x4e, 0xda, - 0xb2, 0xe5, 0x4f, 0xb2, 0xbc, 0x23, 0x5c, 0x0e, 0x81, 0x5e, 0xa0, 0x7a, 0x09, 0x20, 0x10, 0xf6, - 0x7a, 0x92, 0x3d, 0x59, 0x9c, 0x85, 0xf7, 0xd8, 0xdc, 0x71, 0x07, 0xe2, 0x13, 0x4f, 0x31, 0x94, - 0x3c, 0x1b, 0x9e, 0xc3, 0x97, 0xe3, 0x5b, 0x69, 0xa3, 0x86, 0xb4, 0x43, 0x4a, 0x2b, 0xf7, 0xb7, - 0x14, 0x54, 0xd8, 0xef, 0x04, 0x0a, 0x20, 0xd4, 0x54, 0xd9, 0x47, 0xe7, 0x63, 0xcb, 0x94, 0x8f, - 0x6e, 0x24, 0x92, 0xbb, 0xb8, 0x47, 0xca, 0xd2, 0x4b, 0x2e, 0xdb, 0xc5, 0x9a, 0xe7, 0x2d, 0xd2, - 0x61, 0x73, 0xc5, 0xa8, 0x9f, 0x37, 0xe3, 0x61, 0x4d, 0x3c, 0x33, 0xaa, 0x44, 0x9b, 0xdf, 0x43, - 0x02, 0x95, 0xcc, 0xbc, 0xe5, 0x49, 0x13, 0xf5, 0x97, 0x1b, 0x62, 0x5d, 0x8d, 0xde, 0x56, 0xa4, - 0x4f, 0xb2, 0x19, 0x94, 0x7a, 0xd7, 0x2f, 0xf8, 0x60, 0x45, 0x91, 0x6f, 0x9e, 0xa5, 0x71, 0xfc, - 0x36, 0x5d, 0xb5, 0x30, 0xc8, 0xb5, 0x21, 0xb2, 0x91, 0xf6, 0x2d, 0x29, 0x84, 0x76, 0x35, 0xcc, - 0x7e, 0x64, 0x6c, 0xcc, 0x1d, 0x8c, 0xfa, 0xdf, 0x8c, 0x95, 0x46, 0x27, 0xef, 0x93, 0x65, 0xd1, - 0xe2, 0xc9, 0x7e, 0x7e, 0xf5, 0xe2, 0x3b, 0x69, 0x4e, 0xa6, 0xfe, 0xb8, 0x48, 0x0b, 0x6a, 0x13, - 0xc3, 0x38, 0x27, 0xe7, 0x78, 0x2d, 0x76, 0xb8, 0xb2, 0x4d, 0x6b, 0xa3, 0x54, 0x1a, 0xc1, 0x7a, - 0xb6, 0x30, 0xc4, 0xc6, 0x79, 0xd2, 0x66, 0x48, 0xff, 0x18, 0xa6, 0xd2, 0x07, 0xf8, 0xaf, 0x52, - 0x74, 0xcd, 0xfe, 0x4e, 0xcc, 0x00, 0x86, 0xbe, 0x20, 0x6f, 0x2b, 0x3c, 0xea, 0x21, 0xd1, 0x24, - 0x1b, 0xbb, 0xb4, 0x46, 0x36, 0xc1, 0x79, 0x38, 0x92, 0x6e, 0x26, 0xa5, 0x94, 0x04, 0x6d, 0xd5, - 0x0d, 0xac, 0x3c, 0xb2, 0xd7, 0x31, 0x0f, 0x8b, 0x4f, 0x80, 0x03, 0xf8, 0x47, 0x9d, 0xa9, 0xf1, - 0x33, 0x69, 0x2a, 0x35, 0xa8, 0x02, 0x42, 0x9a, 0x2d, 0xd0, 0x2c, 0x71, 0xf6, 0xc5, 0xaf, 0x68, - 0x4a, 0xe6, 0xa4, 0x9b, 0x28, 0x8e, 0x05, 0xdf, 0xaf, 0x5b, 0x2e, 0x05, 0x5d, 0xdf, 0x25, 0x50, - 0xf3, 0x61, 0xd2, 0x85, 0xdf, 0x40, 0x91, 0xf1, 0xd9, 0xd1, 0x3e, 0x49, 0x39, 0x52, 0x4d, 0xd7, - 0xda, 0xc3, 0x22, 0xa4, 0x3e, 0x60, 0x51, 0x16, 0xb2, 0x82, 0x0d, 0xad, 0x24, 0x06, 0x68, 0x49, - 0xa0, 0xc1, 0x60, 0xb7, 0xdb, 0xfd, 0x27, 0x8d, 0xaa, 0x1d, 0x69, 0x67, 0x2f, 0x4f, 0xb5, 0x03, - 0xf5, 0x5b, 0x02, 0x40, 0xeb, 0x7a, 0xaa, 0x50, 0xf8, 0x09, 0x24, 0x09, 0x39, 0xe7, 0x1c, 0x29, - 0x32, 0x1a, 0xda, 0x93, 0xb0, 0xb8, 0x49, 0xb3, 0xdf, 0x18, 0x06, 0x49, 0x7a, 0x43, 0x9e, 0x6f, - 0x68, 0xaf, 0x47, 0xef, 0x40, 0x60, 0x4b, 0xc3, 0x1d, 0x73, 0x7e, 0x8b, 0x45, 0x24, 0x54, 0x61, - 0x24, 0xe9, 0xfd, 0xcd, 0x8b, 0x38, 0xc5, 0x17, 0x6b, 0xa9, 0x93, 0x46, 0xd8, 0xe0, 0xf9, 0x4c, - 0xd0, 0xc1, 0x33, 0x1a, 0x7e, 0xa8, 0xb9, 0x10, 0x90, 0xd4, 0xe4, 0x24, 0xd3, 0xe1, 0x61, 0x2d, - 0x3e, 0xd1, 0x33, 0xe6, 0x41, 0x0c, 0x40, 0xec, 0x09, 0x50, 0x64, 0x8d, 0xae, 0xa3, 0xf0, 0x46, - 0xa1, 0x38, 0x79, 0x05, 0x1e, 0xe0, 0xbf, 0x8d, 0xb4, 0x2c, 0x96, 0xc5, 0x1b, 0xf4, 0x41, 0x2e, - 0x56, 0x22, 0x1d, 0x03, 0x2b, 0x86, 0x64, 0xac, 0xfb, 0x7b, 0x9b, 0xc9, 0xe6, 0x56, 0xe5, 0x81, - 0xca, 0x83, 0x06, 0xa6, 0x33, 0xbc, 0x57, 0x3a, 0x8b, 0xa7, 0xc9, 0x6f, 0x3b, 0xf5, 0x7d, 0x0d, - 0xea, 0x46, 0xd9, 0x9a, 0xa5, 0xe9, 0xb3, 0xe7, 0x7f, 0x35, 0xfd, 0x0d, 0x8f, 0x6a, 0x57, 0xcb, - 0x68, 0x89, 0x72, 0x2d, 0xac, 0x1b, 0x52, 0xc4, 0x50, 0x97, 0x5a, 0x31, 0xce, 0xb5, 0xe8, 0x52, - 0xad, 0x6a, 0xbd, 0x51, 0xa9, 0x33, 0x97, 0xcd, 0x52, 0xed, 0x23, 0xed, 0x7c, 0xe8, 0x71, 0xa7, - 0x2b, 0x5e, 0xb2, 0xad, 0xe1, 0x23, 0xd1, 0xee, 0x73, 0x60, 0xf9, 0x17, 0x98, 0xfd, 0x6d, 0x61, - 0xeb, 0x87, 0x37, 0x9e, 0x65, 0x5a, 0xaa, 0x19, 0xe0, 0xb5, 0xbd, 0xfd, 0x3f, 0x6c, 0x5d, 0xb7, - 0x06, 0xfa, 0x79, 0x86, 0xf6, 0x24, 0x6f, 0x33, 0xbc, 0x5a, 0x8d, 0x7f, 0x86, 0x21, 0xd8, 0xaa, - 0xff, 0x39, 0x66, 0x77, 0x23, 0xed, 0x93, 0x06, 0xb8, 0xd3, 0x48, 0xdc, 0x36, 0x40, 0x69, 0x2c, - 0xd6, 0xa2, 0x49, 0x0f, 0x90, 0x88, 0x6d, 0x28, 0x7b, 0xda, 0x6d, 0xc5, 0xe5, 0x34, 0xb6, 0x8c, - 0xb7, 0x56, 0xf3, 0x4d, 0x76, 0x5c, 0x7b, 0xfc, 0x63, 0x61, 0x1a, 0xee, 0x6a, 0x06, 0x5e, 0xcb, - 0x9c, 0xf9, 0xa1, 0x99, 0x25, 0x6a, 0xb4, 0x3c, 0xce, 0x93, 0xda, 0x26, 0x35, 0x3a, 0x52, 0x1b, - 0x50, 0x6e, 0x8f, 0x9a, 0x92, 0x01, 0x15, 0xae, 0xbd, 0x48, 0x50, 0x70, 0xb2, 0x72, 0x0a, 0x7a, - 0x97, 0x03, 0xd0, 0x2b, 0x50, 0xa1, 0xf2, 0xf0, 0x8b, 0x27, 0x30, 0xfa, 0x66, 0x4a, 0x13, 0x78, - 0xb6, 0x58, 0x28, 0xfb, 0xa6, 0xa5, 0x4f, 0xf8, 0x17, 0x27, 0x22, 0x7d, 0x7a, 0x72, 0xef, 0x4e, - 0x0b, 0x10, 0xed, 0x54, 0x24, 0x47, 0x8f, 0xa0, 0x0f, 0x5a, 0xaa, 0xaa, 0x30, 0x1b, 0x48, 0x0d, - 0x02, 0xa5, 0x81, 0xc8, 0x7d, 0x81, 0xea, 0x61, 0xfc, 0x15, 0xc6, 0xfb, 0x9d, 0xa4, 0x72, 0x55, - 0xcb, 0xa1, 0xfc, 0xc7, 0x8d, 0x87, 0x74, 0x8e, 0x61, 0xd5, 0xa3, 0x09, 0xf7, 0x0e, 0x8b, 0xbb, - 0x50, 0x00, 0x1a, 0x2b, 0xcd, 0xda, 0xf6, 0xde, 0x79, 0xd3, 0xdd, 0x91, 0x80, 0x9b, 0xa0, 0xef, - 0x74, 0x45, 0x9f, 0xe9, 0x4a, 0x1f, 0x97, 0xbb, 0x6d, 0x7c, 0xff, 0x97, 0xa6, 0x41, 0x3c, 0xbb, - 0x21, 0xa7, 0xcf, 0x3d, 0xc3, 0x5f, 0x23, 0x8d, 0x9b, 0x84, 0xbf, 0x4a, 0x33, 0x7a, 0xcf, 0x92, - 0x2e, 0x0c, 0xec, 0x22, 0xe0, 0x2a, 0xe7, 0xba, 0x23, 0x71, 0x58, 0xe1, 0x7b, 0x1d, 0xe0, 0xfb, - 0x80, 0x2b, 0x81, 0xe3, 0x09, 0x78, 0x9c, 0xcb, 0xe9, 0x75, 0x28, 0xae, 0xd6, 0x71, 0x11, 0xad, - 0xe2, 0xb0, 0x34, 0xeb, 0xe3, 0xcb, 0xf5, 0x18, 0x5a, 0x02, 0x8b, 0xc8, 0x87, 0xc1, 0x91, 0x11, - 0x72, 0xaa, 0xb2, 0xa3, 0xf6, 0xe5, 0xca, 0xcf, 0x0d, 0x9b, 0xbd, 0xea, 0xbc, 0xd7, 0xbb, 0x7f, - 0x01, 0x69, 0xc1, 0x86, 0x7b, 0xe3, 0x2d, 0xba, 0x60, 0x68, 0xbc, 0x45, 0xef, 0x8b, 0x1d, 0x5b, - 0x08, 0xb2, 0xf7, 0xdf, 0x42, 0xe8, 0xed, 0x81, 0x0a, 0xec, 0xeb, 0xbd, 0x87, 0xf2, 0x8b, 0x1b, - 0x19, 0xfa, 0xdd, 0x6a, 0xc3, 0xcb, 0x8d, 0xa5, 0x1d, 0x84, 0x92, 0x7b, 0x37, 0x2c, 0xed, 0x09, - 0x6e, 0x89, 0x9b, 0xd5, 0xc6, 0x41, 0x8c, 0x33, 0x5b, 0xbf, 0xd7, 0x30, 0x21, 0x71, 0x6e, 0xef, - 0x11, 0x7c, 0x9b, 0x45, 0x6e, 0x6d, 0x66, 0xb3, 0x2c, 0xba, 0xa7, 0xd3, 0xaa, 0x92, 0xca, 0xfb, - 0x8f, 0x08, 0x43, 0x48, 0xcd, 0x50, 0x12, 0xaf, 0x8f, 0x58, 0x81, 0x2b, 0xdf, 0xec, 0x33, 0x70, - 0x4b, 0x9c, 0xdf, 0x1f, 0x56, 0x5a, 0xac, 0x6f, 0xef, 0x3f, 0xda, 0xab, 0xff, 0x9a, 0x7e, 0x60, - 0xcf, 0x31, 0x30, 0x1b, 0xff, 0xfc, 0xd7, 0x7a, 0xff, 0xab, 0xd8, 0x11, 0x01, 0x32, 0xa1, 0x6d, - 0xa4, 0xb0, 0x41, 0x81, 0xc7, 0x48, 0x67, 0xb4, 0x10, 0xa0, 0x77, 0xae, 0x83, 0x9a, 0x33, 0x9a, - 0x0b, 0xde, 0x5f, 0xdb, 0x39, 0x19, 0x53, 0xa1, 0xe7, 0x41, 0x4d, 0xbc, 0xc9, 0x76, 0x5f, 0x85, - 0xd0, 0xac, 0x80, 0xfa, 0xb9, 0x7b, 0x6b, 0x58, 0x58, 0xb2, 0xfd, 0x3d, 0xf0, 0x8c, 0x80, 0xba, - 0x27, 0xb0, 0xdf, 0x9c, 0x32, 0xb0, 0xe9, 0x0a, 0xd6, 0x01, 0x29, 0x47, 0xff, 0x9d, 0x26, 0x70, - 0x92, 0x1b, 0xf2, 0x16, 0x6b, 0x38, 0x9b, 0xab, 0xa7, 0xd7, 0x18, 0x5f, 0xc6, 0x12, 0x53, 0x3f, - 0xd5, 0x70, 0x2e, 0xdb, 0x30, 0x5c, 0xba, 0x9b, 0x7d, 0x46, 0x60, 0xf0, 0xaf, 0x50, 0xba, 0x70, - 0x6c, 0x96, 0x87, 0x46, 0x08, 0x02, 0xd7, 0x29, 0x9e, 0x28, 0x70, 0x6c, 0xe4, 0x71, 0x0a, 0x47, - 0xb3, 0x83, 0x97, 0x7d, 0x04, 0x5e, 0x4f, 0xe6, 0x84, 0x46, 0x7a, 0x47, 0x3c, 0x9e, 0x1b, 0xd5, - 0xf1, 0x37, 0x47, 0xa5, 0xca, 0xb6, 0xc5, 0x88, 0xef, 0x46, 0xbd, 0x91, 0x57, 0x1a, 0xf2, 0xed, - 0x33, 0xee, 0x15, 0x47, 0xdc, 0x11, 0x7c, 0xc7, 0xb1, 0x1c, 0x00, 0xd9, 0x00, 0x22, 0xd1, 0x75, - 0xf8, 0x2c, 0x6b, 0x12, 0xea, 0x9d, 0x53, 0xb3, 0x9c, 0x00, 0x01, 0x9f, 0xee, 0x93, 0x1f, 0x88, - 0x77, 0x18, 0x17, 0x85, 0x80, 0x84, 0xc1, 0x59, 0x78, 0x92, 0x34, 0x59, 0x90, 0xf2, 0xa5, 0xca, - 0x5c, 0xf5, 0x07, 0x59, 0x51, 0xd1, 0xb7, 0x6e, 0xa4, 0xf0, 0xfa, 0x95, 0x53, 0x9b, 0xa2, 0x54, - 0x0a, 0xb3, 0x1b, 0xdb, 0x7d, 0x73, 0xfb, 0x65, 0x33, 0x4d, 0x83, 0xde, 0x8b, 0x30, 0x69, 0x15, - 0x28, 0x1b, 0xf2, 0xe5, 0x7d, 0xba, 0xd9, 0xf8, 0xdf, 0x7b, 0xd5, 0x8a, 0xb8, 0x86, 0x89, 0x73, - 0xb8, 0xda, 0xb0, 0x91, 0x92, 0xb5, 0xa9, 0x74, 0xeb, 0xc3, 0x28, 0xc6, 0x61, 0x8a, 0x27, 0xce, - 0x93, 0xd5, 0xa6, 0x9a, 0xc5, 0xc1, 0x99, 0x9d, 0xc7, 0x87, 0x9c, 0xb5, 0xa5, 0x5c, 0xd2, 0xc5, - 0x56, 0xba, 0x78, 0xd2, 0xd8, 0x85, 0x59, 0x52, 0xf5, 0x72, 0xa8, 0x7b, 0x31, 0x73, 0x65, 0x47, - 0x4f, 0x86, 0x9c, 0x4b, 0xc6, 0x76, 0xba, 0xd0, 0xc5, 0x51, 0xd5, 0xf4, 0x9b, 0x12, 0xe2, 0x60, - 0x02, 0xfc, 0x7c, 0x65, 0x0c, 0xb6, 0x0a, 0x96, 0xc1, 0x60, 0x54, 0x46, 0x74, 0xbe, 0xa7, 0xca, - 0x6e, 0x9d, 0xf4, 0xcf, 0xed, 0xfa, 0xe8, 0x4f, 0x70, 0xeb, 0xd1, 0xd7, 0x80, 0xd0, 0x9d, 0xe2, - 0x52, 0xea, 0x36, 0xca, 0x5b, 0x1a, 0xcf, 0x5d, 0x18, 0xad, 0x32, 0x47, 0xa0, 0xab, 0x41, 0x8c, - 0x5e, 0x6a, 0x4e, 0x96, 0x2c, 0x1c, 0xe5, 0x6f, 0x80, 0x28, 0x73, 0x77, 0x49, 0x47, 0x40, 0x4e, - 0x7f, 0xaf, 0x03, 0x7c, 0xd1, 0x11, 0x91, 0x16, 0x32, 0xfb, 0xb9, 0xf2, 0x5e, 0xe3, 0xd7, 0x90, - 0xec, 0xf7, 0xb6, 0xc5, 0x43, 0xf1, 0x64, 0xe8, 0x01, 0xe6, 0x3e, 0x21, 0xfa, 0xba, 0x48, 0x05, - 0x37, 0xd9, 0xbf, 0xb4, 0x6b, 0x55, 0x2b, 0xe1, 0x9b, 0x02, 0xb4, 0x35, 0x61, 0xe2, 0xc0, 0x93, - 0x72, 0xfe, 0x74, 0x96, 0xbb, 0x5c, 0x97, 0x2f, 0x20, 0x5e, 0x7a, 0x80, 0x8c, 0xdc, 0xb0, 0x9a, - 0x1c, 0x65, 0x4b, 0x94, 0xa9, 0xae, 0x45, 0x1a, 0xcb, 0x6b, 0x77, 0x7c, 0x19, 0x4f, 0xdf, 0xb2, - 0xaa, 0xd1, 0x22, 0x99, 0x8f, 0x11, 0x92, 0xe9, 0xa2, 0x65, 0x29, 0x74, 0xee, 0xb9, 0x61, 0x10, - 0x92, 0x65, 0x75, 0x72, 0x12, 0xda, 0x89, 0x08, 0x08, 0xf2, 0x8e, 0x79, 0x1d, 0xb1, 0xe3, 0x89, - 0xca, 0xf0, 0xe5, 0x73, 0x59, 0x6e, 0xe7, 0x32, 0xbf, 0xee, 0xf8, 0xa2, 0x83, 0x7f, 0x46, 0xec, - 0xca, 0x72, 0x5f, 0xdb, 0xa2, 0x11, 0xa5, 0xc7, 0x78, 0x5b, 0xb4, 0x44, 0xaa, 0x66, 0xd7, 0x16, - 0x1b, 0x0f, 0x15, 0xd9, 0x20, 0x8c, 0xa8, 0x34, 0xda, 0xc2, 0x5a, 0x43, 0x3e, 0xfc, 0x7b, 0x76, - 0x86, 0xeb, 0x92, 0xf9, 0xb4, 0x3c, 0x17, 0xfc, 0xcf, 0xcc, 0xb7, 0x90, 0xfa, 0xdc, 0x3f, 0xc3, - 0xff, 0xce, 0xb5, 0xc8, 0xc6, 0xb4, 0x4a, 0xfb, 0x7e, 0xb6, 0xb4, 0x0b, 0x75, 0xf6, 0x6a, 0x9a, - 0xdb, 0x35, 0x3c, 0x4b, 0x55, 0xf3, 0x87, 0xf7, 0x35, 0xbf, 0x57, 0x0f, 0xb2, 0xf9, 0x7f, 0x9d, - 0x01, 0x69, 0xe6, 0xd0, 0x1a, 0x39, 0x79, 0x71, 0x2c, 0x9e, 0xb4, 0xba, 0x74, 0x37, 0x95, 0xe7, - 0xad, 0xaf, 0x84, 0x4b, 0xf6, 0x32, 0x1b, 0xee, 0x79, 0x94, 0xc8, 0xfb, 0x3d, 0x11, 0xd3, 0x04, - 0xf3, 0xf6, 0x0e, 0xf0, 0x3e, 0xc6, 0x67, 0x9b, 0x7a, 0xa4, 0x23, 0xba, 0x51, 0x83, 0xb2, 0xd8, - 0xa8, 0xb9, 0xa7, 0xce, 0x18, 0xef, 0x1e, 0x99, 0x8d, 0x99, 0xbe, 0x00, 0xb5, 0xe6, 0x2a, 0xbe, - 0x01, 0x81, 0x4a, 0xe0, 0xcf, 0x9a, 0x67, 0x6b, 0xb2, 0x04, 0x96, 0x2d, 0xe7, 0x78, 0x76, 0x79, - 0xdb, 0xa5, 0x39, 0x2d, 0xe0, 0x00, 0xeb, 0x33, 0x8b, 0xd7, 0x99, 0xeb, 0x35, 0x8d, 0xd3, 0x31, - 0xc4, 0x89, 0x7d, 0x0a, 0x6a, 0xbe, 0xdf, 0x2c, 0x4c, 0xc3, 0x1b, 0x0c, 0xb0, 0x04, 0x9e, 0x41, - 0xef, 0x5e, 0xaa, 0xab, 0xe5, 0xef, 0x9f, 0x9b, 0xfc, 0xa5, 0x1d, 0x99, 0xa8, 0x83, 0x21, 0x1d, - 0x8a, 0x69, 0x94, 0xa0, 0xfb, 0xad, 0x2f, 0x5e, 0x43, 0xc9, 0xc7, 0xc4, 0x9a, 0x02, 0x32, 0x08, - 0x7d, 0x0f, 0x15, 0xa8, 0xcc, 0x66, 0x28, 0x5d, 0xa3, 0x7c, 0x71, 0x5b, 0xfe, 0x64, 0x63, 0xdf, - 0xa9, 0x90, 0xf7, 0xf1, 0xa4, 0x39, 0x5f, 0x86, 0x03, 0xb8, 0x19, 0x57, 0x00, 0x86, 0x5e, 0x66, - 0xe8, 0xac, 0xf7, 0x5f, 0x2a, 0x3c, 0x77, 0x25, 0x12, 0x06, 0x2c, 0x42, 0x43, 0x70, 0x0c, 0x90, - 0x47, 0x42, 0x61, 0xc3, 0x1e, 0x7b, 0x72, 0xc3, 0xdd, 0xd7, 0x6b, 0x11, 0x5f, 0x75, 0xec, 0x06, - 0x7d, 0x7d, 0x56, 0x69, 0xea, 0xd5, 0xb4, 0x73, 0xb7, 0x23, 0xf5, 0xbb, 0x40, 0x44, 0x3f, 0x7e, - 0x14, 0x66, 0x46, 0xf3, 0x1b, 0xc3, 0x2d, 0xe5, 0xac, 0xc7, 0x0c, 0xa0, 0xcc, 0x8e, 0x42, 0x70, - 0xd2, 0x96, 0x43, 0x25, 0xb8, 0x2a, 0xd0, 0xa8, 0x58, 0x19, 0x90, 0x7e, 0xdb, 0x90, 0xfe, 0x2b, - 0x1e, 0x48, 0x1a, 0xe2, 0x3b, 0xaf, 0x53, 0xcb, 0x6b, 0xc5, 0x8e, 0x77, 0xc6, 0x8b, 0x78, 0x5e, - 0xda, 0x65, 0x69, 0x55, 0x76, 0x5f, 0x29, 0x3e, 0x70, 0xf5, 0x5a, 0x92, 0xad, 0xb3, 0x66, 0xe7, - 0x5c, 0x64, 0xd3, 0x0b, 0x63, 0x01, 0x0e, 0x64, 0x71, 0x98, 0x73, 0x6d, 0x2d, 0xaa, 0x34, 0xea, - 0x53, 0x86, 0x8a, 0xde, 0xf8, 0x72, 0xc2, 0xd4, 0x17, 0xfa, 0x5e, 0x61, 0x04, 0x4d, 0x61, 0x2a, - 0x94, 0x10, 0xe9, 0xb1, 0x13, 0x7d, 0xe8, 0x57, 0xa1, 0x09, 0xe7, 0xff, 0x06, 0xb8, 0x8a, 0x63, - 0xd1, 0x56, 0xe2, 0x57, 0x28, 0x71, 0x8b, 0xfe, 0xf0, 0x77, 0xaa, 0x2d, 0x7c, 0x1e, 0x20, 0x99, - 0xa3, 0x23, 0x52, 0x0c, 0x73, 0xa9, 0x47, 0x05, 0x19, 0x63, 0xc1, 0xad, 0x04, 0x64, 0xe3, 0xa5, - 0xeb, 0x62, 0x03, 0x70, 0xfc, 0x20, 0x6f, 0x56, 0x71, 0x8c, 0xb3, 0xe6, 0x71, 0x6d, 0x57, 0x9b, - 0x0f, 0x35, 0xf8, 0xca, 0x89, 0x32, 0x7c, 0x77, 0x82, 0x97, 0x02, 0x4a, 0x6c, 0x1a, 0x70, 0x88, - 0x5a, 0xf7, 0x85, 0xe6, 0xb7, 0x28, 0x7a, 0xd5, 0x62, 0x03, 0x9b, 0x1c, 0xfa, 0x81, 0xc4, 0xae, - 0x9b, 0x3f, 0x5c, 0x6c, 0x06, 0x37, 0x9e, 0x8e, 0xcc, 0x76, 0xc8, 0xde, 0xa8, 0xf8, 0xf0, 0x93, - 0xdc, 0xf9, 0xe8, 0xb3, 0x8c, 0x17, 0x06, 0xd8, 0x35, 0x07, 0xaf, 0x11, 0xc8, 0x8c, 0x23, 0xa0, - 0x14, 0x3d, 0x79, 0x9b, 0x80, 0x2d, 0x5d, 0x90, 0xb9, 0x84, 0x5f, 0xfd, 0x11, 0x1f, 0x91, 0xf7, - 0xdf, 0x45, 0x17, 0x3d, 0x58, 0x5b, 0x92, 0x67, 0x61, 0x30, 0x23, 0x68, 0x6d, 0x39, 0xae, 0x5d, - 0x4e, 0x2f, 0x6f, 0xad, 0xdf, 0x0f, 0xeb, 0x0e, 0x70, 0xe5, 0x9d, 0x5d, 0xd7, 0xe0, 0x97, 0x78, - 0x57, 0x5e, 0x16, 0xd8, 0x0f, 0xf7, 0x99, 0xe7, 0xf3, 0xd4, 0x2e, 0x45, 0x92, 0x57, 0xe3, 0xff, - 0x28, 0x1c, 0x85, 0xb8, 0x03, 0xa0, 0xa2, 0x27, 0xd8, 0x4d, 0x04, 0x3c, 0xe9, 0x0d, 0x0b, 0xb9, - 0xfc, 0x66, 0xb5, 0xd8, 0x31, 0xec, 0x15, 0x74, 0x57, 0x3f, 0xb3, 0x8a, 0x74, 0x55, 0xf1, 0x76, - 0x40, 0x5f, 0x16, 0x10, 0x39, 0x76, 0x4d, 0x6e, 0x56, 0x34, 0xb6, 0x35, 0x4b, 0x8b, 0xf6, 0xb6, - 0x10, 0xd1, 0x6f, 0x60, 0x29, 0x47, 0x87, 0xc8, 0x74, 0x4b, 0x35, 0x05, 0xe1, 0x97, 0xdb, 0xaa, - 0xc5, 0x90, 0xd9, 0x5a, 0x02, 0x50, 0xf5, 0x71, 0xfd, 0x0f, 0x6a, 0xc8, 0xa9, 0x82, 0x21, 0x10, - 0xb3, 0xa2, 0x63, 0xa1, 0xc8, 0x72, 0xb5, 0x11, 0xd4, 0x82, 0xa5, 0x34, 0xc5, 0xd3, 0x68, 0xbd, - 0x41, 0x32, 0xbf, 0xaa, 0x1a, 0x74, 0xd5, 0x70, 0xaa, 0x26, 0x5d, 0xe3, 0xc8, 0xa0, 0x79, 0x68, - 0x98, 0x92, 0x7c, 0x07, 0x35, 0x3b, 0xf8, 0x02, 0x7d, 0xc7, 0x92, 0xf5, 0xeb, 0x65, 0x1e, 0xab, - 0x32, 0x12, 0x52, 0x3a, 0x30, 0x49, 0xdb, 0x12, 0x25, 0xb0, 0x42, 0xaf, 0x79, 0x8b, 0x49, 0x34, - 0xa9, 0xc5, 0xec, 0xe8, 0x70, 0x8c, 0x7c, 0x28, 0x88, 0xff, 0x58, 0x71, 0x39, 0xa0, 0xd5, 0x7a, - 0xf1, 0xab, 0x14, 0x8d, 0x6b, 0xe9, 0x0d, 0x36, 0x8d, 0x58, 0x6b, 0x07, 0x83, 0xa8, 0x97, 0x2f, - 0xa3, 0x67, 0xd5, 0x2b, 0xec, 0xea, 0x02, 0xc9, 0x11, 0xe2, 0x17, 0x50, 0xfd, 0xbd, 0xba, 0x68, - 0x2e, 0xbf, 0xab, 0x87, 0x74, 0x8d, 0x43, 0xc2, 0x7a, 0xf7, 0x75, 0x40, 0xc5, 0xd7, 0xab, 0x3d, - 0x4b, 0xab, 0xc8, 0x4a, 0xf5, 0xe2, 0x47, 0x03, 0x38, 0x59, 0xa2, 0x55, 0x41, 0x56, 0x47, 0xf4, - 0x45, 0xa7, 0x1f, 0x97, 0xc5, 0x55, 0x7c, 0xfc, 0xbf, 0x9e, 0x8c, 0x6f, 0xef, 0xb7, 0x13, 0x01, - 0x00, + 0x1f, 0x8b, 0x08, 0x08, 0x3e, 0xb4, 0xac, 0x5e, 0x00, 0x03, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2d, + 0x69, 0x33, 0x2e, 0x68, 0x74, 0x6d, 0x00, 0xcc, 0x7c, 0x6b, 0x73, 0xe2, 0xc8, 0xb2, 0xe0, 0xf7, + 0x89, 0x98, 0xff, 0xc0, 0x71, 0xc7, 0x6e, 0x77, 0x5f, 0x37, 0x46, 0xe2, 0x65, 0xdc, 0xaf, 0x38, + 0x25, 0x21, 0x9e, 0x16, 0x46, 0x80, 0x04, 0x78, 0xe3, 0x7e, 0x10, 0x42, 0x08, 0x81, 0x40, 0x58, + 0x12, 0x08, 0x98, 0x98, 0xfd, 0xed, 0x9b, 0x59, 0x7a, 0x20, 0x5e, 0xb6, 0x7b, 0xfa, 0x9e, 0x88, + 0x65, 0xa6, 0x81, 0x52, 0x55, 0x65, 0x65, 0xe5, 0x3b, 0xb3, 0xca, 0x7c, 0xff, 0x57, 0xf9, 0x89, + 0xef, 0x0d, 0xdb, 0x42, 0x6a, 0xea, 0x2d, 0xac, 0x9f, 0x7f, 0xfe, 0xf1, 0x3d, 0xfe, 0xd4, 0xd5, + 0x31, 0x7e, 0x2e, 0x74, 0x4f, 0x4d, 0x2d, 0xd5, 0x85, 0xfe, 0xe3, 0x66, 0x63, 0xea, 0xfe, 0xca, + 0x76, 0xbc, 0x9b, 0x94, 0x66, 0x2f, 0x3d, 0x7d, 0xe9, 0xfd, 0xb8, 0xf1, 0xcd, 0xb1, 0x37, 0xfd, + 0x31, 0xd6, 0x37, 0xa6, 0xa6, 0xa7, 0x69, 0xe3, 0x4b, 0xca, 0x5c, 0x9a, 0x9e, 0xa9, 0x5a, 0x69, + 0x57, 0x53, 0x2d, 0xfd, 0x07, 0xfb, 0x25, 0xb5, 0x80, 0x27, 0x8b, 0xf5, 0x22, 0x7a, 0x70, 0x13, + 0x83, 0xd5, 0xa6, 0xaa, 0xe3, 0xea, 0x00, 0x66, 0xed, 0x4d, 0xd2, 0xa5, 0x9b, 0x93, 0xe5, 0xbc, + 0xa9, 0xbe, 0xd0, 0xd3, 0x9a, 0x6d, 0xd9, 0x4e, 0x62, 0xc5, 0x0f, 0x59, 0xfa, 0x4a, 0x00, 0x89, + 0x7a, 0x76, 0xba, 0x7b, 0x13, 0x4e, 0x55, 0x57, 0x2b, 0x4b, 0x4f, 0x2f, 0xec, 0x91, 0x09, 0x1f, + 0xbe, 0x3e, 0x4a, 0xc3, 0x83, 0xb4, 0xa6, 0xae, 0xd4, 0x91, 0xa5, 0xd3, 0x99, 0x96, 0xb9, 0x9c, + 0xa7, 0x1c, 0xdd, 0xfa, 0x71, 0xe3, 0x4e, 0x61, 0x47, 0xda, 0xda, 0x4b, 0x99, 0x00, 0xe8, 0x26, + 0x35, 0x75, 0xf4, 0xc9, 0x8f, 0x9b, 0xb1, 0xea, 0xa9, 0x5f, 0xcd, 0x85, 0x6a, 0xe8, 0x99, 0x6d, + 0x1a, 0x3b, 0xbe, 0x8d, 0x54, 0x57, 0x2f, 0xe6, 0xbf, 0x10, 0x42, 0x38, 0x42, 0x04, 0x22, 0xc0, + 0x3b, 0x7e, 0x56, 0x09, 0x5f, 0xc5, 0x6f, 0x15, 0x03, 0xde, 0xea, 0x96, 0xd4, 0x9b, 0x6b, 0x2d, + 0x7e, 0x6a, 0x37, 0xf1, 0x59, 0x59, 0xb6, 0xea, 0x9d, 0x4a, 0x1d, 0xbf, 0x4a, 0xc1, 0x68, 0x83, + 0x8e, 0xad, 0x65, 0xda, 0x99, 0x21, 0x3e, 0x11, 0xd8, 0x46, 0x47, 0xa8, 0xc8, 0x4f, 0x75, 0x76, + 0x06, 0x8f, 0x32, 0x6d, 0xff, 0x69, 0x6b, 0xb4, 0xaa, 0x3a, 0x91, 0xc5, 0xad, 0xf0, 0x50, 0x2d, + 0x6a, 0x12, 0xdf, 0x2c, 0xf7, 0xc9, 0x74, 0x45, 0xca, 0xcf, 0xd9, 0x49, 0xa9, 0x2d, 0xce, 0xb8, + 0x6e, 0x4e, 0xea, 0x33, 0x25, 0xa9, 0x99, 0x65, 0x9a, 0xea, 0x33, 0x9f, 0x35, 0x26, 0xfc, 0xc3, + 0x94, 0x5f, 0xbe, 0xd8, 0x6b, 0xbb, 0x65, 0x90, 0x8e, 0x31, 0xbc, 0xdf, 0x8b, 0x5b, 0xb2, 0x6b, + 0x59, 0xf2, 0x58, 0xaa, 0x59, 0x03, 0x93, 0x58, 0x4f, 0x59, 0xb1, 0x4c, 0xca, 0xc5, 0x9c, 0xa0, + 0xbc, 0xb4, 0x6a, 0x44, 0x67, 0x08, 0x45, 0xc4, 0xaa, 0xf4, 0xe6, 0xdd, 0xb5, 0xb4, 0xe0, 0xf9, + 0x9b, 0x0c, 0x52, 0xc3, 0x33, 0x3d, 0x4b, 0xff, 0xd9, 0x7f, 0x14, 0xca, 0xdf, 0x33, 0xc1, 0x77, + 0x78, 0xe8, 0x6a, 0x8e, 0xb9, 0xf2, 0x7e, 0x4e, 0xd6, 0x4b, 0xcd, 0x33, 0xed, 0x65, 0x6a, 0xa2, + 0xeb, 0xe3, 0x91, 0xaa, 0xcd, 0x3f, 0x7d, 0xfe, 0xeb, 0xef, 0xef, 0x99, 0xb0, 0x17, 0xc7, 0x79, + 0x3b, 0x3a, 0xe1, 0xdf, 0x13, 0x60, 0x44, 0x7a, 0xa2, 0x6a, 0x7a, 0xea, 0xaf, 0x3f, 0xff, 0x08, + 0x1b, 0x0b, 0xd3, 0xda, 0x7d, 0x4d, 0xdd, 0xf4, 0xeb, 0x40, 0x43, 0xf7, 0xe6, 0xdb, 0x9f, 0x7f, + 0xb8, 0x8e, 0xf6, 0x35, 0xb5, 0x76, 0xac, 0x4f, 0x94, 0xc4, 0xc8, 0x27, 0x53, 0x53, 0x11, 0x3e, + 0x10, 0x9a, 0xce, 0xf1, 0xed, 0xc9, 0xe4, 0x5b, 0x24, 0x18, 0x54, 0x2e, 0x22, 0xda, 0x8f, 0x99, + 0x87, 0x6a, 0xc7, 0x20, 0x94, 0xf2, 0x84, 0x73, 0xe1, 0xcd, 0xa5, 0x5f, 0xab, 0x8e, 0x14, 0x52, + 0xf8, 0xda, 0x8b, 0x6b, 0xcb, 0xbb, 0xd2, 0x0e, 0xbf, 0xf0, 0x74, 0x02, 0x7d, 0x66, 0x94, 0x0b, + 0x0c, 0xa9, 0xf7, 0x5b, 0xde, 0x70, 0x40, 0x39, 0x68, 0xe3, 0xc3, 0x0e, 0x7e, 0x95, 0x3b, 0x13, + 0xce, 0xe8, 0x76, 0x9e, 0xb3, 0x95, 0xbd, 0x86, 0xed, 0x17, 0x1f, 0xdf, 0x29, 0x23, 0xe9, 0x7c, + 0x8e, 0x5b, 0x8e, 0x6a, 0xd6, 0x02, 0x9b, 0x1e, 0xce, 0xec, 0xc3, 0xe2, 0x9c, 0x32, 0x5f, 0x16, + 0x06, 0x2f, 0x99, 0xea, 0xd4, 0x1a, 0xd2, 0xb6, 0x31, 0xc4, 0xa1, 0x2d, 0x94, 0x8b, 0x32, 0xac, + 0x50, 0x14, 0x59, 0x15, 0xfb, 0xa0, 0xab, 0x5a, 0xa1, 0x48, 0xcd, 0xf1, 0xad, 0x41, 0xb4, 0x21, + 0x61, 0x1f, 0xec, 0xd1, 0xa0, 0x93, 0x87, 0xe6, 0x70, 0x8c, 0x0f, 0x9b, 0x38, 0x89, 0xb7, 0x97, + 0x84, 0xf4, 0x72, 0x8b, 0xed, 0x66, 0x98, 0x15, 0x00, 0xde, 0x9c, 0xa2, 0xa2, 0x60, 0x57, 0xe5, + 0xd9, 0x5f, 0x54, 0x5d, 0x63, 0xd4, 0xaf, 0xe4, 0xf1, 0x61, 0x75, 0x40, 0x37, 0x6f, 0x84, 0x48, + 0x32, 0xa4, 0xc2, 0xae, 0x87, 0x7d, 0xd6, 0x82, 0xe6, 0xb3, 0x45, 0xf7, 0x83, 0x5b, 0xab, 0x34, + 0x25, 0xf9, 0x51, 0xca, 0x0f, 0xb8, 0x8d, 0x96, 0x43, 0xfc, 0x1c, 0xe3, 0x68, 0x53, 0x74, 0x8b, + 0x22, 0x2c, 0x54, 0x85, 0x4e, 0x19, 0x91, 0x78, 0x26, 0x2e, 0x3e, 0xe4, 0xdb, 0x64, 0x35, 0xe7, + 0xf7, 0xd8, 0xad, 0xbb, 0x44, 0xf4, 0x85, 0xc6, 0x45, 0x12, 0x77, 0x2e, 0x53, 0x1e, 0x31, 0x2b, + 0x70, 0xa4, 0x2c, 0x66, 0x4a, 0x0c, 0x4c, 0x2e, 0xe3, 0xff, 0x71, 0x47, 0xe2, 0x55, 0x8f, 0xbe, + 0x04, 0x78, 0xe0, 0x9b, 0x86, 0x1a, 0x43, 0xdb, 0x9c, 0x8f, 0x9f, 0x94, 0xef, 0x35, 0x7c, 0xd8, + 0x45, 0xd4, 0xb9, 0x2a, 0x76, 0x73, 0x12, 0x72, 0x53, 0x20, 0xf5, 0x27, 0x92, 0xcb, 0x97, 0x97, + 0x46, 0xfb, 0x89, 0x13, 0xf3, 0xf5, 0x8c, 0x61, 0xaf, 0xf9, 0x55, 0xfe, 0x71, 0x63, 0xb0, 0x4f, + 0xe5, 0x55, 0xbe, 0x33, 0x9a, 0x3e, 0xe9, 0x42, 0x26, 0x3f, 0xd8, 0x4f, 0xcd, 0x75, 0xdd, 0xcb, + 0xcf, 0x72, 0xa6, 0xbd, 0x6e, 0x2e, 0xf2, 0x4e, 0xdb, 0xdc, 0xae, 0x1f, 0xe7, 0xf9, 0xdd, 0x60, + 0x26, 0xde, 0xb6, 0xc4, 0x7c, 0xa1, 0x3c, 0xb3, 0xf4, 0x76, 0x3f, 0x9f, 0xa9, 0xcd, 0x79, 0x5d, + 0x92, 0x32, 0x99, 0x5c, 0x26, 0xe3, 0x87, 0xf8, 0x5d, 0x87, 0x6f, 0x23, 0xfc, 0xd6, 0xed, 0x55, + 0xf8, 0xb3, 0x10, 0xfe, 0x63, 0x3e, 0x9f, 0x99, 0x59, 0x4f, 0x67, 0xf0, 0xb9, 0xcc, 0xad, 0x38, + 0xd9, 0x77, 0x32, 0x62, 0xad, 0x64, 0x4f, 0xd6, 0xdb, 0x87, 0x42, 0x2d, 0xab, 0x4d, 0x86, 0x9d, + 0x07, 0xa9, 0xb6, 0x37, 0x26, 0x8d, 0xce, 0x7d, 0xa1, 0xe6, 0xcd, 0x75, 0x96, 0x2b, 0x0e, 0x6b, + 0xb6, 0x3b, 0x2e, 0x76, 0x72, 0x6a, 0x6d, 0x30, 0x1c, 0x6b, 0x5b, 0x76, 0x59, 0x53, 0xe4, 0xf1, + 0x53, 0x67, 0x5f, 0xa8, 0x3d, 0xf9, 0x1a, 0xdb, 0xd9, 0xf5, 0x6a, 0x20, 0x4c, 0x32, 0xe7, 0x67, + 0x6b, 0x5c, 0x49, 0x03, 0x2e, 0x96, 0x29, 0xfd, 0xfe, 0xa3, 0x2f, 0x80, 0x3f, 0xc9, 0x64, 0x08, + 0x29, 0x9d, 0xf0, 0x13, 0xb8, 0x59, 0xd6, 0x0a, 0xe1, 0x33, 0xee, 0x68, 0x0a, 0xb0, 0xae, 0xb5, + 0x9f, 0x73, 0xf1, 0xfc, 0xc3, 0x0b, 0xa5, 0x30, 0xf7, 0x24, 0x85, 0xcf, 0x8c, 0xb2, 0x92, 0x29, + 0x91, 0x72, 0xd3, 0x7f, 0x24, 0xa8, 0x2a, 0x02, 0xcc, 0x03, 0x63, 0x39, 0x15, 0x3a, 0x52, 0xb5, + 0xbe, 0x13, 0xcc, 0x86, 0x21, 0x28, 0xf5, 0x9e, 0xbc, 0x6f, 0xed, 0xc5, 0x01, 0xc8, 0x24, 0xc2, + 0xdd, 0xd5, 0x33, 0xc5, 0x97, 0xfa, 0xbe, 0xce, 0x37, 0x33, 0xd9, 0x17, 0xcb, 0x74, 0xfa, 0xcd, + 0xd5, 0x50, 0x01, 0x38, 0xb7, 0x84, 0x17, 0x77, 0xa2, 0xd1, 0x9c, 0x29, 0x8a, 0xec, 0x34, 0x7d, + 0xa0, 0x4b, 0x6b, 0x00, 0x64, 0x17, 0x1d, 0x34, 0x1f, 0xbc, 0x44, 0x9e, 0x08, 0x07, 0xaa, 0xca, + 0xcb, 0x9d, 0x7a, 0x47, 0x90, 0x39, 0x53, 0x9c, 0xd6, 0xcd, 0x61, 0x4f, 0xe8, 0x0a, 0x9d, 0x7a, + 0x57, 0xde, 0x57, 0xba, 0x02, 0x2b, 0xee, 0xb5, 0x3d, 0xec, 0x85, 0x27, 0xe5, 0xfa, 0x2c, 0xf3, + 0x62, 0xcf, 0x44, 0x4b, 0xe6, 0x94, 0x4d, 0xf1, 0x85, 0xc8, 0x7c, 0x3f, 0xa3, 0xbe, 0xf4, 0x9b, + 0x5e, 0x72, 0x9d, 0xda, 0x53, 0x06, 0xfa, 0x5f, 0x1e, 0x14, 0x45, 0xe9, 0xba, 0xc1, 0xfe, 0x1b, + 0x02, 0xa9, 0x48, 0x4f, 0x84, 0x58, 0x02, 0xca, 0x2b, 0x69, 0x48, 0xc2, 0x80, 0x74, 0xb5, 0x1c, + 0x19, 0x12, 0x6e, 0x5b, 0xde, 0xdf, 0x92, 0xa7, 0x52, 0x69, 0x26, 0xd5, 0x84, 0x76, 0x3b, 0x4f, + 0xee, 0xf7, 0xa1, 0xc0, 0x19, 0x9c, 0x92, 0xb9, 0x17, 0xca, 0x2f, 0xb8, 0x7f, 0x40, 0x52, 0x42, + 0x5a, 0xf6, 0xea, 0x83, 0x9a, 0x21, 0x0c, 0x2a, 0x53, 0x19, 0xd8, 0x9c, 0xe7, 0x38, 0x7f, 0xd8, + 0x06, 0x38, 0x8b, 0xc6, 0x2e, 0xcf, 0x35, 0x76, 0x43, 0xb6, 0x55, 0xd6, 0x6e, 0x49, 0x4f, 0xcb, + 0x8a, 0xfb, 0x7a, 0xbf, 0xb2, 0xcf, 0x73, 0xa2, 0x2f, 0x66, 0x5b, 0xf0, 0xd9, 0xda, 0x43, 0x1f, + 0x3f, 0x9c, 0xd5, 0x8d, 0x61, 0xad, 0x0e, 0x63, 0xeb, 0xbb, 0x7a, 0xb5, 0xd2, 0x91, 0x42, 0x58, + 0x25, 0x0e, 0xe8, 0xb0, 0x6d, 0xee, 0xdc, 0x8c, 0x30, 0x15, 0xd5, 0x2a, 0xfb, 0x50, 0x06, 0x9f, + 0x75, 0xdb, 0xb6, 0x1a, 0x82, 0xc4, 0x96, 0x46, 0xd5, 0xa9, 0xd8, 0x6d, 0xef, 0x5c, 0x47, 0xec, + 0x4d, 0xad, 0x3a, 0x38, 0x96, 0xa7, 0xc7, 0xad, 0xd8, 0xde, 0x17, 0x86, 0x55, 0x6e, 0xaa, 0x48, + 0xe5, 0x4a, 0x5f, 0x50, 0x48, 0x47, 0x99, 0x55, 0x88, 0x02, 0xce, 0xa7, 0x5f, 0xce, 0x67, 0x7a, + 0x44, 0xe2, 0x7d, 0xae, 0xde, 0x95, 0x78, 0xf7, 0x45, 0x9c, 0xf9, 0x85, 0xa7, 0x9a, 0x9c, 0x91, + 0x04, 0x37, 0xff, 0x24, 0xb8, 0x19, 0xa9, 0x26, 0xe7, 0x9f, 0x7a, 0xfe, 0xae, 0x69, 0x7a, 0x44, + 0x98, 0xd6, 0xfd, 0x66, 0x77, 0xee, 0x67, 0xfa, 0x96, 0xf0, 0xb4, 0x5f, 0x2d, 0x1f, 0x77, 0xa5, + 0xb5, 0x54, 0x19, 0x16, 0x1e, 0x79, 0xbf, 0xa0, 0xcc, 0xc9, 0xfa, 0x71, 0xf7, 0xb0, 0x7c, 0x9a, + 0x79, 0x02, 0x87, 0x74, 0xaf, 0x97, 0x81, 0x5f, 0x4f, 0xd4, 0xdc, 0x83, 0xe0, 0x64, 0x91, 0xa6, + 0x42, 0x57, 0xec, 0x88, 0x5b, 0xad, 0x5f, 0xd9, 0xe6, 0xb9, 0xca, 0x76, 0xa8, 0x54, 0x88, 0xf6, + 0x44, 0x24, 0x0d, 0x64, 0xa3, 0xbe, 0x7c, 0x34, 0x84, 0x65, 0x63, 0x26, 0x33, 0xe1, 0xde, 0x73, + 0x15, 0xa0, 0x55, 0xbc, 0xdf, 0x99, 0xb8, 0x4b, 0xd0, 0x64, 0x49, 0x4c, 0xaf, 0xaf, 0xb8, 0xf9, + 0x49, 0xb5, 0x33, 0x9f, 0xf1, 0xc6, 0x5c, 0xaf, 0x2d, 0xac, 0x6a, 0x47, 0xe1, 0xa4, 0x8e, 0xfc, + 0xbc, 0xaa, 0x4d, 0xf3, 0x0d, 0xde, 0x14, 0x9f, 0xab, 0x9d, 0x52, 0x09, 0xf6, 0x32, 0xac, 0xb0, + 0x42, 0xae, 0x35, 0xcb, 0xdf, 0xb6, 0x66, 0xe3, 0x4e, 0x65, 0x6b, 0x94, 0x44, 0xe0, 0x0d, 0xe5, + 0x35, 0xcb, 0xa8, 0x75, 0xbe, 0xd2, 0x84, 0xfd, 0xb8, 0xb2, 0xa0, 0x54, 0xd5, 0x4e, 0x5e, 0xaf, + 0x4d, 0x0b, 0xab, 0xce, 0x5c, 0x91, 0x1e, 0xf9, 0xf9, 0xaa, 0x6b, 0x0a, 0x46, 0x75, 0xe6, 0xaf, + 0x74, 0x61, 0x18, 0xcd, 0x0d, 0x61, 0x15, 0xaa, 0x3a, 0x3f, 0x8f, 0xf8, 0xcd, 0x64, 0x8a, 0xc3, + 0xf2, 0x8c, 0x3c, 0xaa, 0x44, 0xc8, 0x93, 0x3e, 0x9a, 0xb8, 0x8e, 0xa6, 0x13, 0xa9, 0xc4, 0x95, + 0x0d, 0x61, 0x43, 0xa4, 0x3c, 0x57, 0xf6, 0x85, 0x27, 0xd2, 0x75, 0x39, 0x90, 0xcf, 0x0d, 0xe9, + 0x02, 0xcf, 0x7d, 0xad, 0xda, 0x30, 0x4b, 0x5c, 0x63, 0x36, 0xcc, 0x60, 0xbb, 0xd5, 0x93, 0xb2, + 0xad, 0x9d, 0xb6, 0x26, 0xbd, 0x12, 0xd7, 0x36, 0x84, 0x09, 0xe9, 0xe5, 0xb9, 0xb6, 0x2f, 0x80, + 0x4c, 0xb8, 0x9c, 0x38, 0x1d, 0x4e, 0x48, 0x07, 0xe8, 0xb0, 0xd7, 0xb2, 0xc8, 0xf3, 0x8a, 0x01, + 0xb2, 0xd3, 0xa1, 0x74, 0xa9, 0x72, 0xbe, 0xbc, 0x6b, 0x01, 0xbd, 0x1a, 0xa6, 0x68, 0x72, 0x20, + 0x5b, 0x15, 0x43, 0xdc, 0x2a, 0xd2, 0xb0, 0xcc, 0x55, 0xea, 0xe5, 0xb2, 0x3f, 0x5e, 0x94, 0x77, + 0x62, 0x0f, 0xd6, 0x7e, 0xe4, 0x9b, 0x52, 0x9d, 0xf7, 0x05, 0x49, 0xd8, 0xd5, 0xa5, 0x67, 0x43, + 0x7b, 0xc2, 0x7e, 0xe2, 0x2b, 0x03, 0x62, 0x08, 0x3c, 0x51, 0x86, 0x55, 0xe2, 0x77, 0xba, 0xc4, + 0x2f, 0xd5, 0x9e, 0x8d, 0xd2, 0x4c, 0xf0, 0x4b, 0x1c, 0xef, 0x9b, 0x73, 0x9e, 0xb8, 0x9c, 0xc0, + 0x89, 0xa6, 0x50, 0x1d, 0xd6, 0xca, 0x06, 0xf6, 0x8b, 0x15, 0xc5, 0xaf, 0x73, 0x44, 0xaa, 0xdd, + 0xee, 0xf7, 0x0f, 0xa3, 0xfe, 0xbe, 0x94, 0xe9, 0xb3, 0x2e, 0x57, 0x05, 0xb0, 0x65, 0x5f, 0x6f, + 0x01, 0x2e, 0xb0, 0x0c, 0xbc, 0x55, 0xfd, 0x3a, 0x6f, 0xd8, 0xf5, 0x11, 0x31, 0xe4, 0x32, 0x3f, + 0xe7, 0x39, 0x09, 0xfb, 0x4a, 0x42, 0x87, 0xd8, 0x32, 0x3c, 0x97, 0xf9, 0xf9, 0x2f, 0x8e, 0x5f, + 0x36, 0x46, 0xed, 0x68, 0x3d, 0x54, 0x31, 0x9f, 0x38, 0x99, 0xbc, 0x54, 0x66, 0x25, 0x51, 0x21, + 0x5c, 0x1e, 0x5c, 0x13, 0xd7, 0x45, 0xb9, 0x12, 0xa7, 0x40, 0xf3, 0x0e, 0xc8, 0x55, 0x57, 0x64, + 0x42, 0xbd, 0x02, 0xbd, 0x09, 0x64, 0x0b, 0x9f, 0x85, 0xb2, 0xb3, 0x17, 0xb6, 0xf2, 0x80, 0xdb, + 0x69, 0xb5, 0xc6, 0x5e, 0x63, 0x41, 0xde, 0x40, 0xf7, 0x86, 0x7b, 0xa0, 0x31, 0xc8, 0xe1, 0xb0, + 0x46, 0xf6, 0xb1, 0x9e, 0xce, 0x5a, 0x3c, 0xf0, 0x03, 0x74, 0x14, 0xe4, 0x12, 0xf6, 0x2f, 0xd4, + 0x38, 0x7c, 0x16, 0xe9, 0xa9, 0x5f, 0x27, 0xed, 0xd9, 0x30, 0x27, 0x77, 0xb4, 0xa1, 0x32, 0x25, + 0x9d, 0x27, 0x53, 0x5b, 0x82, 0xfc, 0x2f, 0x1b, 0x7b, 0x1b, 0xc2, 0xc2, 0x61, 0x24, 0x6f, 0xce, + 0xbc, 0xb7, 0x03, 0xf3, 0xb2, 0xeb, 0xd4, 0x78, 0x29, 0x03, 0xfd, 0x99, 0x06, 0xe7, 0x3b, 0x43, + 0xe5, 0x59, 0x31, 0x78, 0xc9, 0x52, 0x40, 0xd6, 0x54, 0xf9, 0xb9, 0x22, 0x57, 0x80, 0x95, 0x0b, + 0x90, 0x5f, 0x4b, 0xb6, 0x1a, 0x75, 0x4e, 0x51, 0x16, 0x02, 0xaf, 0x6e, 0x8d, 0x81, 0x0c, 0xeb, + 0xb4, 0xcd, 0x10, 0x26, 0x17, 0xae, 0x51, 0xce, 0x67, 0x5b, 0xac, 0x30, 0xa8, 0xb6, 0x0b, 0xfb, + 0xd9, 0xb3, 0x50, 0x32, 0xea, 0xa4, 0x34, 0x1f, 0x32, 0xad, 0x16, 0x2f, 0xb8, 0xed, 0x7d, 0xc9, + 0xad, 0xf7, 0xa4, 0x16, 0x99, 0x69, 0x21, 0x9c, 0x4a, 0xa4, 0x0b, 0x91, 0x6e, 0x4c, 0x15, 0xab, + 0x4f, 0x1a, 0xbc, 0x4c, 0xed, 0xbc, 0x32, 0xb9, 0x55, 0x48, 0xd1, 0xe5, 0x8b, 0x3e, 0xf8, 0x6b, + 0x1e, 0x43, 0x1e, 0x61, 0xf7, 0x0e, 0x9d, 0xdc, 0x0b, 0x3b, 0x2d, 0xf7, 0xe8, 0x0b, 0x20, 0x83, + 0x5a, 0xad, 0x05, 0x74, 0xe8, 0x0b, 0x85, 0xa7, 0x31, 0x2f, 0x98, 0x75, 0xb3, 0xc2, 0xf4, 0xe6, + 0x85, 0x61, 0x5f, 0x66, 0x13, 0x6d, 0xf6, 0xd9, 0xe3, 0x6f, 0x27, 0x8c, 0xd5, 0xe8, 0x32, 0xab, + 0xd2, 0xc6, 0x23, 0x8f, 0x4e, 0x3c, 0xee, 0x74, 0x5e, 0xd8, 0xce, 0x28, 0x83, 0x27, 0xb3, 0x03, + 0xd4, 0x9a, 0x36, 0x9b, 0xfb, 0x3e, 0x13, 0xf0, 0xbb, 0x20, 0x03, 0xbf, 0x1f, 0x1d, 0xf4, 0x34, + 0x4d, 0x89, 0x06, 0x8f, 0x56, 0xc8, 0x43, 0xe4, 0xd1, 0x1e, 0x79, 0x06, 0x7a, 0x93, 0x03, 0xbf, + 0x32, 0x6d, 0xf5, 0x44, 0xa5, 0xde, 0x91, 0x67, 0x5c, 0xc8, 0x7b, 0x90, 0x7f, 0x4b, 0xd8, 0x89, + 0xcb, 0xba, 0xaf, 0xcd, 0x84, 0xbd, 0x58, 0x16, 0x77, 0x1a, 0xdf, 0xe8, 0xf2, 0x15, 0xb6, 0x97, + 0x7b, 0x6e, 0x0a, 0x64, 0xa0, 0x74, 0xa4, 0x6d, 0xcd, 0x2c, 0x08, 0x95, 0x49, 0x7e, 0x4e, 0xba, + 0xab, 0x3e, 0xfc, 0x7b, 0xe0, 0xb7, 0x44, 0x6a, 0x80, 0xae, 0xd7, 0x20, 0x5b, 0x18, 0x88, 0x9c, + 0x22, 0x8a, 0xae, 0xaf, 0x68, 0x9c, 0x32, 0xca, 0xaf, 0x2a, 0x1b, 0x45, 0xaf, 0xb0, 0xb3, 0x5c, + 0xff, 0xb1, 0x5f, 0x67, 0x1a, 0x73, 0x63, 0x2a, 0xe6, 0x24, 0xf0, 0x18, 0xcf, 0x7d, 0xa5, 0x3b, + 0x9c, 0x37, 0xe4, 0xe1, 0xb4, 0x0e, 0x3a, 0x3a, 0x9a, 0xe4, 0x09, 0xf0, 0x86, 0x27, 0xed, 0xfb, + 0xfe, 0x9a, 0x1a, 0x09, 0x91, 0x34, 0x51, 0x76, 0x1f, 0x40, 0xa4, 0x09, 0x19, 0x81, 0x8f, 0x25, + 0x92, 0x9f, 0xf4, 0x0b, 0x5a, 0x35, 0xf4, 0x0b, 0xb3, 0xba, 0x19, 0xc8, 0x5d, 0x03, 0xec, 0x1f, + 0xee, 0xa9, 0x35, 0x4d, 0xda, 0xbd, 0x33, 0x99, 0x94, 0x43, 0x9e, 0xf5, 0x42, 0x19, 0x86, 0xf9, + 0xe8, 0x4f, 0x86, 0x3c, 0xa9, 0x70, 0x8d, 0xee, 0xc0, 0xde, 0x3c, 0x6e, 0xdd, 0x11, 0xd8, 0xe4, + 0x62, 0x57, 0xb6, 0x24, 0x59, 0xb0, 0x1a, 0xba, 0x59, 0xda, 0x54, 0xb7, 0xee, 0xe6, 0x31, 0xb7, + 0x6a, 0x74, 0x15, 0xe2, 0x36, 0x76, 0x5a, 0x11, 0x94, 0x4d, 0x12, 0x7a, 0xf6, 0xb2, 0xb1, 0xf3, + 0x4f, 0xdb, 0x6c, 0x97, 0x71, 0xd9, 0x96, 0xec, 0x3d, 0x12, 0x9b, 0x80, 0x5c, 0xcb, 0xf7, 0x4f, + 0xcc, 0x73, 0xf5, 0x69, 0xef, 0x29, 0x81, 0x2d, 0x4c, 0xb6, 0x67, 0x0f, 0xc5, 0xed, 0x89, 0x2e, + 0x70, 0x27, 0x6d, 0x52, 0x51, 0xba, 0x7b, 0x39, 0x84, 0xe9, 0x46, 0x41, 0x46, 0xc4, 0x5f, 0x9a, + 0x1b, 0x3c, 0x1a, 0x18, 0x1f, 0x22, 0x71, 0x04, 0x13, 0x78, 0xb8, 0x04, 0x1a, 0xec, 0x63, 0xdd, + 0x46, 0x1a, 0xed, 0x50, 0x07, 0xe5, 0x48, 0xc7, 0xad, 0x96, 0x2f, 0xd4, 0x1a, 0x3e, 0xf5, 0x8d, + 0x81, 0xac, 0x9a, 0x54, 0x47, 0x97, 0x67, 0xbe, 0x63, 0xa6, 0x2d, 0x81, 0x66, 0x11, 0x7d, 0x72, + 0x60, 0x5f, 0x77, 0x95, 0x29, 0xc3, 0x41, 0xfc, 0xa1, 0x34, 0xb6, 0x0d, 0xad, 0xc1, 0x0e, 0x3d, + 0xa0, 0x4f, 0x5d, 0xdf, 0xe5, 0x37, 0x35, 0x6e, 0xfc, 0x28, 0xd6, 0x98, 0x1a, 0x1f, 0xe2, 0x9f, + 0x31, 0xe7, 0x39, 0xb2, 0x91, 0xb2, 0xb3, 0x9d, 0xa7, 0x8b, 0x56, 0x4c, 0xd7, 0x67, 0xb9, 0x27, + 0xf7, 0x4c, 0xc3, 0x16, 0x05, 0x4e, 0x28, 0x02, 0xdd, 0xdc, 0x6a, 0xb7, 0xd4, 0x97, 0xd0, 0xbe, + 0xee, 0x95, 0x47, 0x62, 0x54, 0xb2, 0xdc, 0xfe, 0xb9, 0x49, 0xa6, 0x72, 0x9d, 0x63, 0xfd, 0x76, + 0x19, 0x74, 0x5a, 0xe9, 0xb9, 0xf7, 0x9d, 0xd9, 0x76, 0xd1, 0x1c, 0x30, 0xdd, 0xc6, 0x56, 0x0e, + 0x69, 0xcd, 0xb1, 0xad, 0x0d, 0x23, 0xb6, 0xf2, 0xa5, 0x8e, 0x10, 0x8f, 0x79, 0xf0, 0x9a, 0x9d, + 0xee, 0x63, 0xc5, 0x2c, 0x3d, 0x3f, 0xf2, 0xda, 0xf2, 0x69, 0x2a, 0x48, 0x65, 0xb2, 0xe2, 0x41, + 0x2a, 0x1f, 0x5b, 0x12, 0xc0, 0xcf, 0x8a, 0x1c, 0xd0, 0xb1, 0x16, 0xf8, 0x1f, 0xde, 0xc9, 0x64, + 0xdd, 0xb2, 0x42, 0xed, 0xe1, 0x9c, 0x88, 0xc6, 0x49, 0xbc, 0x11, 0xd0, 0xf0, 0x20, 0x3b, 0x5b, + 0x79, 0xd9, 0xda, 0xca, 0x9d, 0x16, 0xea, 0x6f, 0x37, 0xa1, 0xdf, 0x33, 0x6d, 0x00, 0xf4, 0x51, + 0x2a, 0x5c, 0x40, 0x6b, 0xf0, 0x6b, 0xb5, 0xf6, 0xac, 0x30, 0xae, 0x6e, 0x6d, 0xad, 0xda, 0xcb, + 0x03, 0xfe, 0x92, 0xdc, 0x31, 0xf3, 0x9b, 0x96, 0xba, 0x76, 0x5e, 0x8a, 0xde, 0xc5, 0x3e, 0xb2, + 0x92, 0x21, 0x0e, 0x61, 0x6e, 0xdb, 0x73, 0x6d, 0x37, 0xe0, 0x8d, 0x4c, 0x75, 0xc6, 0x18, 0xad, + 0x6e, 0x69, 0xdd, 0x99, 0x4a, 0xb2, 0xd1, 0x5c, 0xbf, 0x18, 0xed, 0xac, 0x43, 0x3f, 0x39, 0x48, + 0x9b, 0x7a, 0xf9, 0xdb, 0xce, 0xbe, 0xa1, 0x35, 0xcb, 0x25, 0xb5, 0xdd, 0x25, 0xec, 0xe3, 0xae, + 0x50, 0xad, 0x60, 0x90, 0x65, 0x08, 0x18, 0x5e, 0x96, 0x31, 0xc3, 0x42, 0x3b, 0xc5, 0x05, 0xf1, + 0x23, 0x07, 0x52, 0xd0, 0xa9, 0x63, 0xbc, 0xd8, 0xf7, 0x08, 0x44, 0x68, 0x18, 0x67, 0x66, 0x70, + 0x60, 0x26, 0xc8, 0x86, 0x38, 0x2a, 0x49, 0x3e, 0x46, 0x5c, 0x18, 0x9c, 0x62, 0x76, 0x25, 0xec, + 0x31, 0xd6, 0x10, 0x7c, 0x94, 0x66, 0xb0, 0x87, 0xcf, 0x32, 0xa7, 0xfa, 0x0d, 0x1c, 0x0f, 0x7a, + 0x89, 0x71, 0x2f, 0xc4, 0x7d, 0x34, 0x78, 0xe5, 0x06, 0x99, 0x82, 0x86, 0xf1, 0xda, 0x0a, 0xf5, + 0x14, 0x52, 0x96, 0x47, 0x0c, 0xf6, 0x80, 0xb8, 0x16, 0xe1, 0xf3, 0xa4, 0x27, 0x51, 0x7f, 0x5e, + 0x11, 0x3a, 0x25, 0x8e, 0xeb, 0x08, 0x83, 0xc6, 0x56, 0xb0, 0x48, 0x2c, 0x87, 0x9a, 0x42, 0xf5, + 0x76, 0x27, 0xe2, 0xf7, 0x32, 0xd2, 0x9c, 0x43, 0xb9, 0xdc, 0x8b, 0xbd, 0x16, 0xd5, 0x4b, 0x19, + 0x69, 0xce, 0x45, 0xf6, 0x94, 0xdb, 0xcb, 0xd9, 0x53, 0x5d, 0x6e, 0xec, 0xe5, 0x38, 0x16, 0x22, + 0x26, 0x7b, 0x9b, 0xf1, 0x79, 0x7e, 0xb2, 0x29, 0x11, 0x70, 0x89, 0x1c, 0x69, 0x0d, 0xd7, 0x4a, + 0x57, 0x7c, 0x10, 0x6b, 0x2d, 0x92, 0x9f, 0xfa, 0x73, 0x4e, 0xd1, 0x6a, 0x62, 0x57, 0xab, 0x73, + 0x5d, 0x49, 0x6b, 0x77, 0xb5, 0x2d, 0xc7, 0x8e, 0x85, 0x76, 0xb7, 0xa5, 0x3c, 0xce, 0xb9, 0x3d, + 0x58, 0x98, 0x42, 0x6d, 0xca, 0x2e, 0x3b, 0xf3, 0x67, 0xa9, 0xbd, 0x1f, 0x66, 0x65, 0x0e, 0xf4, + 0x65, 0x3a, 0x96, 0x5a, 0x10, 0x63, 0xc8, 0xc2, 0x73, 0xf5, 0x79, 0xcb, 0xe8, 0xe4, 0xb9, 0xa0, + 0xfb, 0x90, 0x76, 0x0d, 0xad, 0x7b, 0xc8, 0x58, 0x39, 0x88, 0x46, 0x8a, 0x40, 0xb2, 0x4a, 0xf7, + 0x00, 0x4f, 0xe7, 0xea, 0xac, 0xbc, 0x96, 0x6a, 0xa2, 0x9f, 0x71, 0xb5, 0xb5, 0xc2, 0x4b, 0x74, + 0xed, 0xcc, 0x5a, 0x78, 0x00, 0x1f, 0x06, 0xfe, 0x5f, 0xdb, 0x36, 0xfc, 0x4a, 0x53, 0x66, 0xac, + 0xa6, 0xd6, 0x95, 0xe7, 0xbc, 0xa2, 0xd5, 0x61, 0xcd, 0xd1, 0x53, 0x6f, 0xc5, 0x49, 0x3d, 0xbb, + 0xd0, 0x87, 0x35, 0xf8, 0x8a, 0xd6, 0x68, 0xf0, 0xca, 0xb6, 0x3b, 0xb7, 0x7a, 0x54, 0xbf, 0x81, + 0xf6, 0x99, 0x5b, 0x17, 0xe8, 0xdb, 0x54, 0x90, 0x45, 0x35, 0x83, 0xbc, 0x90, 0xb2, 0x41, 0x3a, + 0x41, 0x5c, 0xdc, 0xaf, 0x1c, 0x62, 0x77, 0x56, 0x80, 0x38, 0x48, 0xdc, 0x09, 0x5c, 0xe8, 0x97, + 0x73, 0x8d, 0x40, 0x87, 0x21, 0x26, 0xd6, 0x4e, 0xe2, 0x1c, 0x39, 0xb0, 0x01, 0xa6, 0x56, 0xab, + 0xa1, 0xcc, 0x06, 0xbe, 0xbb, 0x5f, 0xa1, 0x39, 0x46, 0x7d, 0x81, 0xf1, 0x94, 0xe8, 0xb7, 0x94, + 0x46, 0xb9, 0xbe, 0x6b, 0xb4, 0xd9, 0x3e, 0xfd, 0x24, 0xcd, 0x97, 0xcc, 0x64, 0x68, 0x35, 0xcc, + 0xa1, 0xd5, 0x86, 0xb6, 0x38, 0x7b, 0xdc, 0x76, 0xb2, 0x9d, 0x79, 0x87, 0x19, 0xcf, 0x9f, 0x21, + 0x70, 0x13, 0x67, 0xf8, 0xcc, 0xa7, 0xfd, 0x5e, 0x17, 0xf2, 0x01, 0x53, 0x9c, 0xc1, 0xb3, 0x19, + 0xc8, 0x34, 0xe4, 0x27, 0x79, 0x02, 0xf9, 0xc9, 0xae, 0x0e, 0xfe, 0xae, 0xbe, 0x7f, 0x7c, 0x78, + 0x30, 0x9f, 0x75, 0xb1, 0x57, 0x99, 0x34, 0x7a, 0xfe, 0x76, 0x32, 0x6f, 0xf0, 0x93, 0x5e, 0xa3, + 0xfa, 0x2c, 0x8c, 0x6b, 0x10, 0x70, 0xd6, 0x00, 0x66, 0x55, 0x1c, 0x14, 0x78, 0x69, 0xc9, 0xec, + 0xda, 0x7c, 0x38, 0x4e, 0x12, 0x9a, 0x11, 0x3c, 0x80, 0x43, 0xfd, 0xc1, 0x4b, 0x26, 0xe3, 0x10, + 0x46, 0xe4, 0x0d, 0x1f, 0x85, 0x8e, 0x01, 0xbb, 0x87, 0xf9, 0x95, 0x14, 0xe4, 0x00, 0xd1, 0x3e, + 0x3a, 0x07, 0x9f, 0x5c, 0xdf, 0x4b, 0xa1, 0x7d, 0x13, 0xd9, 0x4a, 0xef, 0x42, 0x9c, 0xb2, 0xef, + 0x3b, 0x2d, 0x53, 0x5b, 0xb4, 0xf6, 0x28, 0x53, 0x6a, 0x43, 0x1f, 0x2c, 0x98, 0x56, 0x59, 0x29, + 0x34, 0x77, 0xeb, 0xde, 0x30, 0xdb, 0xf2, 0x15, 0x86, 0x6d, 0x69, 0x3b, 0x61, 0xaa, 0x43, 0xcc, + 0x22, 0xcf, 0xb4, 0x5c, 0x7b, 0x6e, 0x84, 0x63, 0x87, 0xd9, 0x86, 0x5f, 0xbf, 0x6f, 0xf5, 0xba, + 0x0c, 0xcc, 0x69, 0x68, 0xd5, 0xd6, 0x6c, 0x0e, 0x79, 0x83, 0x3e, 0xa9, 0x4f, 0xeb, 0x83, 0x56, + 0xab, 0xa7, 0x68, 0xb7, 0xad, 0xfd, 0xb8, 0x5b, 0xe5, 0xc6, 0x05, 0x6a, 0x6f, 0xa4, 0x32, 0xf0, + 0x51, 0x86, 0xfc, 0xae, 0xe1, 0x04, 0x75, 0x1c, 0xc8, 0xed, 0x40, 0xcf, 0xd0, 0xf7, 0x0a, 0x3d, + 0xf8, 0x94, 0x44, 0x27, 0xe3, 0x73, 0x7d, 0xcc, 0x8f, 0x49, 0x65, 0x88, 0x35, 0x37, 0x25, 0xfe, + 0x8e, 0x7a, 0x47, 0x5f, 0x43, 0x88, 0x3f, 0x60, 0xff, 0x79, 0xc2, 0x5b, 0x12, 0xea, 0x9b, 0x80, + 0xfa, 0x36, 0xc5, 0x38, 0x04, 0xfd, 0x64, 0x6b, 0x8f, 0xf9, 0x9b, 0x08, 0xf9, 0x1b, 0xec, 0xaf, + 0x27, 0xb2, 0x24, 0xf8, 0x54, 0x38, 0x7c, 0x2e, 0x69, 0xb4, 0xaf, 0x05, 0xb1, 0x2d, 0xf8, 0xf9, + 0x29, 0xd0, 0x88, 0xc5, 0x4f, 0x01, 0xd6, 0xaf, 0x74, 0x2b, 0x8a, 0x51, 0xe7, 0xfa, 0x90, 0x13, + 0x39, 0x4d, 0xf6, 0xb9, 0x6f, 0x08, 0x5b, 0x11, 0x45, 0x89, 0x35, 0x1a, 0x4a, 0x46, 0x75, 0x79, + 0x65, 0x92, 0x75, 0x88, 0x35, 0x78, 0x78, 0xc9, 0xba, 0x2f, 0x2f, 0xa6, 0xdd, 0x6f, 0x4e, 0x87, + 0x1c, 0xf8, 0xe9, 0x97, 0x17, 0x9b, 0xbc, 0x34, 0x2c, 0x1b, 0xff, 0x01, 0x9e, 0xcf, 0x90, 0x1b, + 0xae, 0xfa, 0x0a, 0xd8, 0x21, 0xc5, 0x1a, 0x06, 0xf2, 0x0b, 0xfe, 0x09, 0xec, 0x83, 0x83, 0xc6, + 0xe1, 0x09, 0x91, 0xad, 0x80, 0x19, 0x22, 0x17, 0xf3, 0xba, 0xd8, 0x7f, 0x83, 0x0c, 0x06, 0xfe, + 0x7b, 0x06, 0xf9, 0xce, 0x2c, 0x88, 0x1b, 0xe1, 0x59, 0xc4, 0x43, 0x33, 0x1c, 0x1f, 0xdb, 0x5b, + 0x21, 0x92, 0xdb, 0x88, 0xff, 0xdd, 0xe3, 0x36, 0xf6, 0x37, 0x76, 0x98, 0x47, 0x88, 0xe0, 0xdb, + 0xa7, 0x4f, 0xbd, 0xa5, 0x04, 0x3a, 0x53, 0x9f, 0x8e, 0x21, 0x6e, 0xea, 0x57, 0xac, 0x56, 0xa2, + 0xdd, 0xea, 0xcb, 0x1a, 0xe4, 0x81, 0x0c, 0xe4, 0x80, 0xa1, 0x9d, 0x65, 0x8e, 0xdb, 0x39, 0xce, + 0x5e, 0x34, 0xe0, 0x3b, 0x38, 0xb2, 0xdb, 0xf5, 0xd6, 0xb5, 0x1a, 0x1d, 0x57, 0x6d, 0x98, 0xcf, + 0xf7, 0xdd, 0x9c, 0xac, 0x92, 0x91, 0xa4, 0x8e, 0x65, 0x88, 0x0b, 0x77, 0x11, 0xec, 0xd3, 0xb5, + 0xc2, 0xf6, 0x86, 0x21, 0xd5, 0xa9, 0x37, 0x06, 0x3f, 0x50, 0xeb, 0xec, 0xf3, 0xb7, 0x03, 0x80, + 0x71, 0xda, 0x06, 0x36, 0x5b, 0xe0, 0x23, 0xc2, 0xb5, 0xe4, 0x51, 0xd5, 0x1c, 0x2e, 0xaa, 0x53, + 0xd7, 0xca, 0xac, 0x57, 0xb2, 0x04, 0xd1, 0x52, 0x20, 0x4f, 0x60, 0xc3, 0x0b, 0x28, 0x4f, 0x18, + 0xdf, 0xd5, 0xaf, 0xc5, 0xa1, 0x87, 0x78, 0x68, 0x16, 0xfb, 0x7e, 0x1a, 0x1b, 0x80, 0x1d, 0x1b, + 0x42, 0xfc, 0xc8, 0x00, 0xbd, 0xcc, 0xfa, 0xa0, 0xf3, 0xd4, 0xb3, 0x0c, 0x53, 0xe2, 0x0a, 0x60, + 0x63, 0xc7, 0x6d, 0x88, 0xab, 0x06, 0x95, 0x79, 0x29, 0xd7, 0x62, 0x04, 0x5d, 0xe2, 0xeb, 0xe7, + 0x7b, 0x32, 0xec, 0x66, 0x85, 0x37, 0x96, 0xea, 0xbc, 0xc2, 0x75, 0x85, 0x29, 0x27, 0xf5, 0xed, + 0x65, 0x93, 0x93, 0x9a, 0x3c, 0xf5, 0x29, 0xe0, 0x83, 0xb8, 0x5c, 0x66, 0x20, 0xb5, 0x67, 0x64, + 0x27, 0x92, 0x32, 0xc4, 0xa1, 0x68, 0xb7, 0x00, 0xbf, 0x01, 0xb7, 0x85, 0xf8, 0x72, 0xa7, 0xcd, + 0x5a, 0x5d, 0x2d, 0x07, 0x72, 0x99, 0xab, 0xec, 0x45, 0xee, 0x5d, 0x3c, 0xbe, 0x60, 0xa3, 0x66, + 0xf6, 0x7d, 0x1d, 0x52, 0x89, 0x7a, 0xf4, 0x99, 0x79, 0x71, 0x23, 0x5f, 0x28, 0x57, 0x84, 0xe1, + 0xfa, 0x11, 0xe2, 0xa5, 0xe3, 0xb6, 0x12, 0xd9, 0x99, 0x79, 0xc7, 0xaf, 0xb5, 0x92, 0x73, 0xe1, + 0xf3, 0xe1, 0x25, 0xf2, 0xd3, 0x3d, 0x79, 0xf3, 0x38, 0x1f, 0xca, 0x38, 0xef, 0xa8, 0x4d, 0xaa, + 0xce, 0xc1, 0x56, 0x49, 0x20, 0xdf, 0x3c, 0xc1, 0x7a, 0x8d, 0x41, 0xb0, 0x5e, 0x61, 0x90, 0x16, + 0x2d, 0x93, 0xfb, 0xd7, 0xe4, 0xdb, 0x21, 0x5d, 0x5a, 0x83, 0x08, 0x7c, 0x56, 0xb0, 0x97, 0xd3, + 0x1c, 0xa1, 0x2c, 0xce, 0xe2, 0xba, 0x05, 0xec, 0x7d, 0x98, 0x3b, 0x69, 0xef, 0x4f, 0xda, 0x83, + 0x93, 0x36, 0xc6, 0xae, 0x41, 0x4e, 0x34, 0x15, 0x3a, 0x4f, 0x5d, 0x88, 0x1f, 0x05, 0x5f, 0x6d, + 0x18, 0xf3, 0x1a, 0x4f, 0xe6, 0x56, 0x95, 0x8d, 0xfa, 0x82, 0xfc, 0x69, 0x11, 0xcb, 0xaf, 0xaa, + 0x56, 0x77, 0x72, 0x20, 0x6f, 0x1b, 0x02, 0xed, 0x21, 0x6d, 0x2f, 0x0e, 0xf2, 0xc7, 0xfb, 0x9c, + 0x3b, 0x1a, 0x94, 0xf3, 0x99, 0x4e, 0x94, 0x57, 0x81, 0x4c, 0x96, 0xb9, 0x61, 0x9d, 0xef, 0x0c, + 0x45, 0x18, 0x7b, 0x52, 0x43, 0xb8, 0xbf, 0x35, 0x78, 0x90, 0x59, 0x0a, 0xd3, 0xb5, 0x5e, 0x76, + 0x07, 0xf9, 0x0d, 0xd6, 0x43, 0xf8, 0x7d, 0x07, 0xbe, 0x07, 0xb2, 0xbd, 0x93, 0xc3, 0x7a, 0x40, + 0x40, 0xcf, 0x31, 0x69, 0x32, 0x58, 0x8f, 0x2d, 0xa3, 0x3d, 0xf3, 0xb1, 0x6e, 0x04, 0x76, 0x4c, + 0xd2, 0x96, 0x40, 0x9f, 0x5d, 0xcd, 0x17, 0xfa, 0x15, 0x08, 0x6d, 0xb2, 0xcd, 0xf2, 0x6d, 0xb3, + 0x49, 0xf8, 0x07, 0xa1, 0x68, 0xf4, 0x0c, 0x7f, 0x56, 0xee, 0x4b, 0xe2, 0x90, 0xd4, 0x86, 0x76, + 0x3b, 0x6f, 0xdb, 0xa4, 0xca, 0xf4, 0x5e, 0xb8, 0xa7, 0x32, 0xd9, 0xce, 0xcb, 0x5d, 0x31, 0x08, + 0x95, 0xbb, 0x9b, 0x8c, 0x25, 0x3d, 0x2a, 0x64, 0xed, 0xd2, 0xd2, 0x2a, 0xe9, 0x0a, 0x65, 0x11, + 0xe2, 0x9b, 0xfa, 0x56, 0xe0, 0x9a, 0x7e, 0xf5, 0xe5, 0xe5, 0xc5, 0xb9, 0x65, 0x6d, 0xc2, 0x17, + 0x33, 0xc5, 0x17, 0x88, 0x73, 0x20, 0x74, 0xaa, 0xf5, 0x03, 0xa4, 0xe6, 0xc4, 0x68, 0x3f, 0xa0, + 0xbd, 0x2d, 0x57, 0x68, 0x7d, 0x51, 0xc3, 0x73, 0x8f, 0x11, 0xe1, 0x4a, 0xa4, 0xee, 0x93, 0x25, + 0xe1, 0x5d, 0x5a, 0xb9, 0xe5, 0x7a, 0x60, 0x5f, 0x3b, 0x60, 0x77, 0xb7, 0xb2, 0x05, 0xfe, 0xf7, + 0x9e, 0x40, 0x1e, 0x25, 0x60, 0x3e, 0x15, 0xf8, 0x22, 0x0b, 0x73, 0x0c, 0x88, 0xc1, 0x3a, 0x22, + 0xac, 0x87, 0xf6, 0x98, 0x1c, 0xc6, 0x97, 0xa1, 0x0d, 0xb1, 0x66, 0x13, 0x62, 0xb3, 0x67, 0x25, + 0x23, 0x91, 0x5d, 0xbd, 0x88, 0x71, 0xa2, 0x3b, 0x13, 0x8d, 0xa6, 0x53, 0xdf, 0x83, 0xfd, 0x7d, + 0x81, 0xdc, 0x0a, 0x6d, 0xa9, 0x0f, 0xb1, 0x98, 0x05, 0x11, 0x5c, 0xaf, 0x01, 0x63, 0x65, 0xe0, + 0xb5, 0xa2, 0xbc, 0xec, 0xc4, 0x1d, 0xd8, 0xe6, 0x17, 0xa5, 0x07, 0xfe, 0xd6, 0x7a, 0x56, 0x44, + 0xb3, 0xfd, 0x40, 0xc8, 0xd3, 0x4b, 0x06, 0x7d, 0x06, 0xf4, 0x63, 0x4d, 0x4d, 0x99, 0x14, 0x5f, + 0x14, 0x6b, 0x74, 0xfb, 0xc2, 0xca, 0x33, 0xd1, 0x08, 0x8b, 0x90, 0xf5, 0x32, 0xd0, 0xe3, 0x89, + 0x90, 0xad, 0x8c, 0xb1, 0x5f, 0x8d, 0xf0, 0x5a, 0x50, 0xce, 0xa6, 0x7e, 0x41, 0xa4, 0x78, 0x06, + 0xf1, 0xc1, 0xf5, 0x7c, 0x16, 0xf6, 0x34, 0xa0, 0xb1, 0x1a, 0xe4, 0x03, 0xef, 0xc8, 0xb3, 0xf6, + 0xc4, 0x86, 0xb8, 0xb3, 0xa2, 0xc0, 0x5e, 0x84, 0xe5, 0x23, 0xe6, 0xf0, 0x10, 0x5b, 0x81, 0x92, + 0x55, 0x3b, 0x0b, 0x23, 0x96, 0x49, 0x59, 0xe8, 0x14, 0x21, 0x96, 0xe8, 0xd4, 0x8c, 0xfb, 0x5b, + 0x6b, 0x1f, 0xe6, 0xec, 0x5c, 0x98, 0xd3, 0xcf, 0x4e, 0xda, 0x86, 0x08, 0xc4, 0x18, 0x3c, 0xb0, + 0xe0, 0x6f, 0xd0, 0x4f, 0x56, 0x67, 0x8d, 0x62, 0x47, 0x78, 0x08, 0xe0, 0x6d, 0x4b, 0x14, 0x7e, + 0x8f, 0x0d, 0xe1, 0x9b, 0xbe, 0xdd, 0x26, 0x8c, 0x2e, 0x00, 0xad, 0x2a, 0xdb, 0x69, 0x07, 0x94, + 0xff, 0xb6, 0x3d, 0xd3, 0xb2, 0x72, 0xc7, 0x18, 0x04, 0x6d, 0xb0, 0xf3, 0xb3, 0x21, 0x6d, 0x13, + 0x0c, 0xa0, 0xf9, 0x4d, 0xc6, 0x30, 0xda, 0x0a, 0xd9, 0xcd, 0x03, 0xfa, 0x80, 0xbf, 0x06, 0x79, + 0xec, 0x11, 0x4e, 0x23, 0xa2, 0x4f, 0x72, 0xa4, 0x4c, 0xfd, 0x37, 0xe6, 0xd1, 0x65, 0xf4, 0xa9, + 0xc8, 0xef, 0x96, 0x24, 0xd3, 0x76, 0x6b, 0xab, 0xd5, 0x20, 0xe6, 0x46, 0x7e, 0xbf, 0xa7, 0x2e, + 0xd0, 0x43, 0x79, 0x68, 0xf4, 0xb4, 0x01, 0xe7, 0x0b, 0x46, 0x5b, 0x60, 0x1e, 0x9a, 0x43, 0x08, + 0xad, 0x81, 0x7f, 0x96, 0xc0, 0x94, 0x7a, 0x82, 0x7f, 0xdf, 0xeb, 0xf9, 0x2b, 0xa3, 0x5e, 0xbe, + 0x7d, 0xd9, 0x5f, 0xb1, 0x4b, 0x61, 0x9b, 0x07, 0x1e, 0x8f, 0x70, 0x0e, 0x69, 0x6c, 0x01, 0x4e, + 0x29, 0xe3, 0x36, 0x14, 0x45, 0xaa, 0xae, 0x26, 0xcb, 0xe2, 0xb2, 0xd7, 0xdb, 0xb6, 0x32, 0x03, + 0xb7, 0xd4, 0x2b, 0x33, 0x9c, 0x55, 0x91, 0xb9, 0x26, 0xe4, 0x15, 0x9d, 0x6e, 0x09, 0x61, 0xac, + 0x1f, 0x19, 0x19, 0x6c, 0x87, 0xa4, 0x40, 0x7b, 0x4d, 0x61, 0x32, 0xc0, 0xb3, 0x36, 0xe3, 0x45, + 0xf3, 0x68, 0xa8, 0x0f, 0xf2, 0xb2, 0x41, 0x79, 0x41, 0xfd, 0x91, 0x08, 0x9e, 0xf5, 0x79, 0x60, + 0x97, 0x1b, 0x60, 0xb7, 0x2b, 0xbc, 0x54, 0xaf, 0x41, 0x2e, 0xc8, 0x3d, 0xcd, 0x47, 0xb7, 0x46, + 0xbb, 0x08, 0xf1, 0xa9, 0x70, 0x9f, 0xb1, 0x51, 0x87, 0xaa, 0xa4, 0x63, 0x2e, 0x21, 0x47, 0xbb, + 0x57, 0x48, 0xd7, 0x65, 0x4d, 0x22, 0x80, 0x0d, 0xdc, 0xdc, 0xb2, 0xbd, 0x61, 0xa0, 0x5a, 0x3e, + 0xd6, 0x7b, 0x19, 0x88, 0x0f, 0x40, 0x1f, 0x40, 0x8b, 0xf8, 0x20, 0x34, 0xb0, 0x80, 0x86, 0x83, + 0x86, 0xaf, 0x71, 0x2d, 0x3c, 0x77, 0x01, 0xbd, 0x07, 0xba, 0xe4, 0xb8, 0x5e, 0xf0, 0xd9, 0xf0, + 0x5b, 0xad, 0x76, 0x4f, 0x7c, 0xe8, 0x2c, 0x46, 0xb7, 0x6e, 0xcd, 0xe7, 0x8c, 0x36, 0x23, 0x92, + 0x6d, 0x29, 0x67, 0x8f, 0xdb, 0xb7, 0x86, 0x30, 0xe4, 0x9e, 0x56, 0xda, 0x43, 0x1f, 0x62, 0x73, + 0x73, 0xd4, 0x23, 0xed, 0xae, 0xf1, 0x60, 0x5b, 0x10, 0x71, 0x55, 0x6b, 0xb7, 0x2c, 0x84, 0xe0, + 0x99, 0x27, 0x21, 0x6b, 0xab, 0x12, 0xd7, 0xdc, 0x0f, 0x98, 0x71, 0x8f, 0xad, 0x92, 0xae, 0xe7, + 0x78, 0x51, 0x1d, 0xbe, 0x32, 0x40, 0x7d, 0x70, 0x70, 0x7f, 0xc0, 0x6c, 0xfb, 0xa8, 0x2e, 0xd6, + 0x91, 0x02, 0x59, 0x06, 0xbf, 0x74, 0x56, 0x77, 0xde, 0x8a, 0xcb, 0x16, 0xd6, 0x53, 0x7c, 0xe0, + 0xdf, 0x56, 0xcb, 0x55, 0x2e, 0xd5, 0x75, 0x4e, 0xda, 0xec, 0xf3, 0xe2, 0xb9, 0xdd, 0x38, 0xfc, + 0x83, 0x35, 0x43, 0xff, 0x3b, 0x3d, 0xf1, 0xc7, 0x51, 0xfb, 0xf1, 0x61, 0xd3, 0x38, 0x99, 0x43, + 0xfd, 0x3f, 0xdf, 0xc1, 0x78, 0xd2, 0x80, 0x2c, 0x8f, 0x9e, 0x5f, 0xf1, 0x32, 0xea, 0xe2, 0xb2, + 0xe5, 0x57, 0xc1, 0x4c, 0x49, 0xa5, 0x8c, 0x51, 0xde, 0xb4, 0x87, 0x79, 0xce, 0xdf, 0x07, 0xf5, + 0xf6, 0xf0, 0xfc, 0x01, 0x9c, 0x14, 0xbf, 0xc1, 0xb3, 0xa5, 0xa2, 0x8f, 0xd1, 0x18, 0x09, 0x68, + 0xbc, 0xd3, 0x78, 0x34, 0x0b, 0x6d, 0xb1, 0x27, 0xb4, 0x49, 0x0d, 0xe2, 0x50, 0x52, 0xf2, 0x4b, + 0x62, 0x29, 0x1c, 0xaf, 0xe0, 0x78, 0x46, 0xa2, 0xe3, 0x61, 0x8d, 0x39, 0x07, 0x32, 0xce, 0x6f, + 0xcb, 0xfb, 0x0c, 0xc6, 0xa5, 0x6d, 0x51, 0xe2, 0x98, 0xde, 0x3e, 0x13, 0xcc, 0x0f, 0xed, 0x6d, + 0x82, 0x9e, 0x8f, 0x81, 0x5d, 0x99, 0x55, 0x7c, 0x6d, 0x59, 0x0f, 0x6a, 0x88, 0x10, 0x93, 0x53, + 0x5d, 0x61, 0xeb, 0xf0, 0x0c, 0xe2, 0x51, 0x56, 0xdc, 0x69, 0x18, 0x0b, 0xb0, 0xc8, 0x7b, 0xd4, + 0x07, 0xd4, 0xa1, 0x0a, 0xc6, 0x07, 0xbe, 0xb6, 0x27, 0xc5, 0xf5, 0xce, 0x9c, 0xed, 0x6b, 0x8a, + 0x36, 0x2c, 0x99, 0x96, 0xda, 0x6c, 0x8b, 0xcf, 0xca, 0x76, 0xbf, 0x6b, 0x42, 0xa4, 0x0a, 0xdf, + 0xc7, 0xca, 0x76, 0xb6, 0x6b, 0x2a, 0xb6, 0x5d, 0xda, 0x5a, 0x83, 0x5a, 0xbb, 0xfe, 0xb2, 0x83, + 0x3c, 0xb7, 0x0f, 0xe3, 0xab, 0x8a, 0xa6, 0x95, 0x4c, 0xa7, 0xf9, 0xe6, 0x5c, 0xc0, 0xae, 0x72, + 0xc2, 0xff, 0x3c, 0x11, 0x24, 0xa0, 0x28, 0x37, 0x78, 0xbf, 0x1c, 0xbc, 0xcb, 0x06, 0xf6, 0xc2, + 0x1a, 0x4a, 0x54, 0x37, 0x9d, 0x9e, 0xb4, 0x07, 0x68, 0x53, 0xeb, 0xbd, 0x3c, 0x07, 0x76, 0xfb, + 0x54, 0x16, 0x9e, 0x7b, 0x72, 0xa2, 0xad, 0x58, 0x51, 0x1d, 0x21, 0xac, 0x1b, 0x8c, 0x8f, 0xdb, + 0x9e, 0x1f, 0xfb, 0xf1, 0x8c, 0xe3, 0xc6, 0x31, 0xa4, 0xce, 0x78, 0x6c, 0x75, 0x53, 0x10, 0xab, + 0x4b, 0xf9, 0x3f, 0x1a, 0xa7, 0x1a, 0x95, 0x0e, 0x2f, 0x59, 0x12, 0xad, 0x43, 0xbc, 0x64, 0x6e, + 0x15, 0xc2, 0xca, 0x65, 0xf0, 0x97, 0x0b, 0x52, 0xc6, 0xda, 0xbf, 0xf0, 0xae, 0x33, 0x0c, 0x6e, + 0x2b, 0xf5, 0x21, 0x86, 0xc7, 0xb8, 0x70, 0x21, 0x4a, 0xc7, 0x67, 0x18, 0x48, 0xd7, 0xa0, 0xe6, + 0x32, 0xe4, 0xa5, 0x72, 0x69, 0x2f, 0x32, 0xf3, 0x5e, 0x85, 0x3b, 0x89, 0xd9, 0x8d, 0x79, 0xa3, + 0xc1, 0xd9, 0xcf, 0x75, 0x48, 0x4c, 0x20, 0x66, 0x96, 0xea, 0x82, 0xff, 0xd2, 0xe8, 0x0e, 0x73, + 0x20, 0xd1, 0x62, 0xb9, 0xcb, 0xb9, 0x4f, 0xbd, 0x29, 0x91, 0x25, 0xcc, 0x2d, 0xf3, 0x99, 0xb9, + 0x52, 0xe9, 0x28, 0xf3, 0x70, 0xde, 0xd6, 0x0d, 0x6b, 0x2c, 0xc3, 0x7d, 0xab, 0x4a, 0xdc, 0x47, + 0x7e, 0xd8, 0x96, 0xac, 0x82, 0x2e, 0xcd, 0xbb, 0xf5, 0x4c, 0xbb, 0x2e, 0x09, 0x3d, 0x0d, 0x72, + 0x5d, 0xdb, 0x53, 0x4c, 0xdb, 0x6d, 0xf9, 0xfe, 0x63, 0x67, 0x26, 0xe5, 0x7a, 0x7e, 0x78, 0x7e, + 0x05, 0xf1, 0x0b, 0xcd, 0x77, 0xf0, 0xfc, 0x0e, 0x9f, 0x49, 0x5a, 0xaf, 0x21, 0xc9, 0xbd, 0x06, + 0xc4, 0x40, 0x44, 0x12, 0x07, 0x20, 0xdf, 0x1b, 0x3c, 0xdf, 0x00, 0xb9, 0x2e, 0xbf, 0xdc, 0x5a, + 0xd5, 0xcc, 0x66, 0x76, 0x7b, 0x2b, 0x8c, 0x56, 0xa4, 0xd7, 0xca, 0x83, 0x93, 0x9a, 0x36, 0xf3, + 0xc5, 0xfb, 0xae, 0xbc, 0x28, 0xce, 0xba, 0x36, 0xe7, 0xb9, 0x10, 0x93, 0x34, 0x6e, 0x27, 0xa4, + 0xbb, 0x6f, 0x56, 0x0d, 0x61, 0x94, 0x59, 0x0f, 0x6e, 0x73, 0xd9, 0x6a, 0x56, 0xdf, 0xf2, 0x9e, + 0xc3, 0x71, 0xf7, 0x9e, 0x1f, 0xb8, 0xf2, 0xa0, 0x14, 0xc8, 0x35, 0x9e, 0x0b, 0xf9, 0xed, 0xc0, + 0x2f, 0x95, 0x1e, 0x20, 0x2f, 0x8c, 0x0e, 0x20, 0xb3, 0x06, 0xbf, 0xc9, 0xd2, 0x33, 0xc9, 0xb2, + 0x4a, 0x9a, 0x19, 0xb0, 0x1b, 0xe5, 0x30, 0x2e, 0xa7, 0x67, 0x30, 0xf5, 0xc4, 0x29, 0x74, 0x70, + 0x9e, 0xb9, 0x6f, 0x67, 0x5a, 0x24, 0x71, 0x80, 0x79, 0x2b, 0x5e, 0xbe, 0x67, 0xc0, 0xdb, 0x17, + 0x0f, 0x5a, 0x71, 0x1e, 0x8b, 0x07, 0x77, 0x2d, 0x19, 0xfa, 0xf9, 0x0e, 0xb6, 0x15, 0x6c, 0xd7, + 0xe9, 0xc9, 0x35, 0x83, 0xed, 0xa6, 0x0f, 0xed, 0x0a, 0xf6, 0x13, 0x27, 0x6e, 0xf3, 0x2e, 0x8e, + 0x77, 0x82, 0x62, 0x14, 0xbd, 0xdb, 0x80, 0xe3, 0x07, 0xf1, 0xfc, 0x26, 0xf6, 0x97, 0x95, 0xb8, + 0x4d, 0xe7, 0x0b, 0x74, 0xbd, 0x5a, 0x0c, 0x9f, 0xa7, 0x84, 0xa0, 0xf0, 0x0e, 0x6d, 0x72, 0x68, + 0x73, 0x87, 0xf9, 0x0d, 0x21, 0x9a, 0x1f, 0xac, 0x47, 0xe1, 0x05, 0xfd, 0x2f, 0x01, 0x7e, 0xe1, + 0x8e, 0x6c, 0x34, 0x7a, 0x3a, 0xce, 0x56, 0x0d, 0x0e, 0xec, 0x68, 0x9d, 0x7b, 0x22, 0x10, 0x4e, + 0xcf, 0x7c, 0x7e, 0x42, 0x1e, 0xab, 0x64, 0x0f, 0xfc, 0x26, 0xed, 0x2c, 0x47, 0x6c, 0xa1, 0x4e, + 0xba, 0x73, 0x4e, 0xf2, 0x2b, 0xb2, 0xa1, 0xb8, 0x9c, 0xea, 0x57, 0x45, 0xf2, 0xfc, 0xc2, 0x81, + 0x39, 0x69, 0x1b, 0x63, 0x83, 0xcb, 0x0d, 0x6b, 0x5b, 0xc3, 0xd8, 0xf1, 0x0d, 0xa3, 0xbe, 0x26, + 0x33, 0x97, 0xef, 0xa1, 0xd1, 0xb6, 0xfa, 0x7c, 0xdf, 0x6e, 0x38, 0xc4, 0x5e, 0xf3, 0xf6, 0xb0, + 0xe9, 0x1a, 0x31, 0x3f, 0x5f, 0x48, 0xc5, 0xa5, 0x05, 0x39, 0xfa, 0x32, 0x2e, 0x51, 0x9a, 0xbe, + 0x9e, 0x48, 0x33, 0x7f, 0xe0, 0x8f, 0x14, 0x5c, 0xd5, 0x20, 0x87, 0x79, 0x35, 0x52, 0x4e, 0xf0, + 0xcf, 0x87, 0x7e, 0x88, 0xf3, 0xa2, 0x36, 0x1a, 0x76, 0x7a, 0xc8, 0x1c, 0xb5, 0xb1, 0x9e, 0x00, + 0x0a, 0x1d, 0xb7, 0xb1, 0xde, 0xc7, 0x33, 0x71, 0x1b, 0x52, 0x1c, 0x7a, 0x4e, 0x87, 0xbe, 0x80, + 0xc3, 0xab, 0x1c, 0x94, 0x47, 0xf5, 0x43, 0xdb, 0x00, 0x7c, 0xca, 0xf6, 0xa1, 0xed, 0x43, 0x3f, + 0x3f, 0x8f, 0xdb, 0xc1, 0x25, 0xa2, 0x43, 0x3f, 0xac, 0xd7, 0xc7, 0x92, 0x40, 0xdc, 0x36, 0x70, + 0x7c, 0x29, 0x6e, 0xc3, 0x7a, 0x68, 0x80, 0x58, 0x70, 0xd9, 0x2c, 0xa9, 0x5a, 0xfd, 0x67, 0xd0, + 0x1e, 0xb5, 0xff, 0xb0, 0xae, 0x97, 0x85, 0xb5, 0x48, 0xb8, 0x3e, 0xa9, 0xca, 0x44, 0x33, 0xb8, + 0x3d, 0xa9, 0xce, 0xc9, 0xc8, 0xe7, 0xd6, 0xb8, 0x7b, 0x51, 0x22, 0x6b, 0x52, 0xe6, 0xa2, 0x39, + 0xd1, 0x67, 0xf7, 0xb9, 0x3f, 0x66, 0x47, 0xd5, 0xca, 0x8e, 0x40, 0xf2, 0xf0, 0x2c, 0x71, 0x4b, + 0x52, 0x93, 0x31, 0x88, 0x9f, 0x92, 0x5a, 0x23, 0x1a, 0x53, 0x1d, 0x65, 0xc1, 0xbe, 0x56, 0xc7, + 0xd6, 0x68, 0xa1, 0xec, 0x86, 0x83, 0x8e, 0xf5, 0xcc, 0x73, 0xa6, 0xde, 0xe5, 0x1a, 0xc3, 0xec, + 0x43, 0x6b, 0x94, 0x7d, 0x58, 0xd3, 0xfa, 0x7a, 0xb5, 0x44, 0x46, 0x06, 0xc7, 0xe0, 0x5a, 0xcf, + 0x3e, 0x67, 0x91, 0x6a, 0x1e, 0xe1, 0xed, 0x48, 0x55, 0x20, 0x63, 0x82, 0x6d, 0x09, 0x2b, 0xaf, + 0x26, 0xa9, 0xcd, 0xf1, 0xb3, 0x41, 0x40, 0x0a, 0x00, 0xb7, 0x16, 0x9d, 0x47, 0x71, 0xc4, 0x2b, + 0x2f, 0x98, 0x47, 0xbc, 0xef, 0xf5, 0x39, 0x35, 0xb1, 0x9d, 0x85, 0xea, 0x7d, 0xfa, 0x88, 0x77, + 0x84, 0x3e, 0x7e, 0xfe, 0xf6, 0xe7, 0x1f, 0x7f, 0xff, 0xf9, 0xc7, 0x9f, 0x7f, 0x7c, 0x75, 0x6c, + 0xdb, 0xc3, 0x3b, 0x47, 0xe9, 0xb4, 0x96, 0x1e, 0x19, 0x5f, 0x53, 0x1f, 0x58, 0x96, 0xfd, 0x16, + 0x34, 0xbd, 0x2d, 0x34, 0x27, 0x93, 0x49, 0xd8, 0xcc, 0xa6, 0xa0, 0x99, 0xcd, 0x66, 0xc3, 0x66, + 0x0e, 0x9b, 0xb9, 0x5c, 0x2e, 0x6c, 0xe6, 0xb1, 0x99, 0xcf, 0xe7, 0xc3, 0x66, 0x01, 0x9b, 0x85, + 0x42, 0x21, 0x6c, 0x16, 0xb1, 0x59, 0x2c, 0x16, 0xc3, 0x66, 0x09, 0x9b, 0xa5, 0x52, 0x29, 0x6c, + 0x8e, 0xb0, 0x39, 0x1a, 0x8d, 0xc2, 0xa6, 0x86, 0x4d, 0x4d, 0xd3, 0xc2, 0xa6, 0x8e, 0x4d, 0x5d, + 0xd7, 0xc3, 0xe6, 0x18, 0x9b, 0xe3, 0xf1, 0x38, 0x6c, 0x3a, 0xfa, 0xf8, 0xeb, 0x87, 0x52, 0x8e, + 0x8d, 0x36, 0x84, 0x57, 0xf0, 0x70, 0x3f, 0x9e, 0xbd, 0xd6, 0xa6, 0x69, 0x95, 0x5e, 0xbf, 0xfa, + 0x9a, 0x5a, 0xa8, 0x4b, 0x73, 0xb5, 0xb6, 0xe8, 0x65, 0xa9, 0x68, 0xe4, 0xc8, 0x1e, 0xef, 0x70, + 0xe4, 0x42, 0x75, 0x0c, 0x13, 0xc6, 0x30, 0xd0, 0x81, 0x77, 0xb4, 0x0c, 0xc7, 0x5e, 0x2f, 0xc7, + 0xc1, 0xdd, 0xb9, 0xaf, 0xa9, 0x8d, 0xea, 0x7c, 0x0a, 0x68, 0x83, 0x34, 0x3b, 0xba, 0x99, 0x55, + 0xd3, 0xad, 0x8d, 0xee, 0x99, 0x9a, 0xfa, 0x25, 0xa5, 0xe8, 0xce, 0x58, 0x5d, 0xc2, 0x17, 0x57, + 0x5d, 0xba, 0x69, 0x57, 0x77, 0xcc, 0x49, 0x34, 0xda, 0x35, 0xf7, 0xfa, 0xd7, 0x14, 0x7b, 0xbf, + 0xda, 0xc2, 0x93, 0x13, 0xa0, 0xde, 0x16, 0x81, 0x7a, 0xfa, 0xd6, 0x4b, 0xab, 0x96, 0x69, 0x00, + 0x12, 0x9a, 0xbe, 0xf4, 0x74, 0x07, 0xf7, 0xe6, 0xeb, 0xa3, 0xb9, 0xe9, 0xa5, 0x83, 0x7d, 0x68, + 0xaa, 0x65, 0xd9, 0x6b, 0xef, 0x6b, 0x6a, 0x69, 0x2f, 0xf5, 0x44, 0xef, 0x1a, 0x96, 0x82, 0xe5, + 0x2c, 0x5d, 0x4b, 0xf4, 0x2d, 0xec, 0xfd, 0x95, 0x0e, 0xf7, 0xe2, 0xf3, 0x8b, 0x63, 0xa3, 0xe5, + 0xd5, 0x55, 0x7a, 0x6a, 0x1a, 0x53, 0xc0, 0x6e, 0xea, 0x45, 0x34, 0xf1, 0x1c, 0xd8, 0xe5, 0x4a, + 0x75, 0x00, 0xd7, 0x24, 0xd9, 0xbf, 0x1c, 0x68, 0x3a, 0xd5, 0x71, 0x3c, 0x6c, 0x9b, 0x61, 0xfe, + 0x17, 0x0c, 0xa1, 0x97, 0x1b, 0xe3, 0xd6, 0xca, 0x76, 0xcd, 0x80, 0x2d, 0x13, 0x73, 0xab, 0x23, + 0x23, 0xed, 0x8d, 0xee, 0xb8, 0x9a, 0x63, 0x5b, 0x56, 0x7a, 0xa4, 0x4f, 0xd5, 0x8d, 0x89, 0xab, + 0x84, 0x98, 0x50, 0xf0, 0xab, 0x24, 0xa3, 0x58, 0x66, 0xb5, 0x4d, 0x31, 0xa9, 0x2c, 0xbe, 0x9f, + 0xd3, 0x74, 0x1c, 0xcb, 0xf6, 0x68, 0xed, 0x79, 0xf6, 0x12, 0x67, 0x02, 0xe9, 0x2c, 0x73, 0xa9, + 0xc7, 0x30, 0xb5, 0xb5, 0xe3, 0xe2, 0x9c, 0x95, 0x6d, 0x86, 0xe4, 0xa6, 0x13, 0xee, 0x2c, 0x75, + 0xa4, 0x5b, 0xee, 0xa9, 0x50, 0xac, 0xd4, 0xf1, 0xd8, 0x5c, 0x82, 0x6e, 0x94, 0x8e, 0xd6, 0xa5, + 0x53, 0x3e, 0xe0, 0x9d, 0x49, 0x3a, 0x0d, 0x67, 0x9d, 0xef, 0x6c, 0x64, 0x03, 0x0e, 0x8b, 0xaf, + 0xa9, 0xfb, 0x2c, 0xe5, 0xbf, 0x13, 0x90, 0x25, 0x7f, 0x49, 0x18, 0x8a, 0x88, 0xb8, 0xef, 0x00, + 0x80, 0xa5, 0x01, 0x3c, 0x1c, 0x03, 0xba, 0x40, 0x16, 0x94, 0x2f, 0x2b, 0xed, 0x58, 0x31, 0x8e, + 0x23, 0xc7, 0xa4, 0x4b, 0x45, 0x48, 0x05, 0xb0, 0x82, 0x3e, 0xdf, 0x51, 0x57, 0x2b, 0xdd, 0xb9, + 0x8c, 0x8a, 0x67, 0xaf, 0x82, 0xfd, 0x58, 0xfa, 0xc4, 0x0b, 0xbe, 0x85, 0xe8, 0x1c, 0x4b, 0xfe, + 0x01, 0xa3, 0x2c, 0x62, 0xb4, 0x4f, 0x9b, 0xcb, 0xb1, 0x0e, 0xa6, 0x20, 0x56, 0xb1, 0x3b, 0xbc, + 0xd4, 0xe9, 0x9e, 0x5d, 0x54, 0xfc, 0x18, 0x5c, 0x54, 0xfc, 0x18, 0x0b, 0x3e, 0xde, 0x6d, 0x44, + 0x92, 0x83, 0xdd, 0xb1, 0x8e, 0xb5, 0x21, 0x1b, 0x20, 0x8d, 0x3c, 0x49, 0xc7, 0xa2, 0x02, 0x0f, + 0xc6, 0xa6, 0xbb, 0xb2, 0x54, 0x80, 0x65, 0x2e, 0x69, 0xdf, 0xc8, 0xb2, 0xb5, 0xf9, 0xb7, 0x03, + 0x37, 0xd2, 0x94, 0xf6, 0xb8, 0xe5, 0x03, 0x07, 0xee, 0xa6, 0x6b, 0xe3, 0x70, 0x6b, 0x32, 0x80, + 0x9f, 0xcf, 0x26, 0x88, 0x62, 0x2e, 0x27, 0x81, 0x89, 0xf3, 0xf0, 0x06, 0x6b, 0x1a, 0xe0, 0x53, + 0x4d, 0x8a, 0xc8, 0x72, 0x2c, 0x9b, 0xc1, 0x14, 0x57, 0x37, 0xde, 0x9e, 0x71, 0x5f, 0x3c, 0x9e, + 0x30, 0xa6, 0x33, 0x2e, 0xe9, 0x32, 0x7d, 0x48, 0x95, 0x06, 0x8d, 0xf0, 0xd7, 0xd4, 0x1a, 0x99, + 0xa4, 0xa9, 0xae, 0x7e, 0x62, 0x23, 0x92, 0xac, 0x9c, 0xeb, 0xbb, 0x73, 0x88, 0xc8, 0xb9, 0x83, + 0x40, 0xa6, 0x23, 0xe1, 0x2a, 0x25, 0xe6, 0x6d, 0x54, 0xeb, 0x7c, 0x1e, 0xe5, 0xf3, 0x1b, 0x13, + 0x5d, 0xcb, 0x1c, 0x83, 0x1d, 0x40, 0xde, 0xfe, 0xf9, 0x07, 0xce, 0x3f, 0xe0, 0x4b, 0xbf, 0x82, + 0xf1, 0xd4, 0x87, 0x9f, 0x72, 0x2b, 0x6a, 0xb2, 0xae, 0xea, 0x1c, 0x50, 0xc2, 0xa2, 0x30, 0x92, + 0x4a, 0x94, 0x66, 0x81, 0x61, 0xc8, 0xba, 0x42, 0xc8, 0xb8, 0x58, 0xae, 0x43, 0xfc, 0x16, 0xe6, + 0x78, 0x6c, 0xe9, 0xaf, 0xc2, 0x9d, 0x58, 0x54, 0xac, 0x27, 0x96, 0xad, 0x7a, 0x87, 0x0d, 0x9d, + 0x2b, 0x72, 0x52, 0x71, 0x2f, 0x5b, 0xdb, 0xc3, 0xbe, 0x1c, 0xdb, 0x83, 0x4d, 0x7d, 0x62, 0xc6, + 0xba, 0x11, 0xf7, 0x84, 0x4a, 0x13, 0x8f, 0x4a, 0x31, 0x77, 0x39, 0x37, 0xc6, 0x42, 0xdf, 0x52, + 0x4b, 0x74, 0x0e, 0x83, 0x2d, 0x45, 0x50, 0x42, 0xa1, 0xa3, 0xf6, 0xe0, 0x9a, 0x3c, 0x5f, 0xdd, + 0x7f, 0x60, 0x53, 0x2c, 0x73, 0xa3, 0xe3, 0x7d, 0xf1, 0xa4, 0x25, 0x0d, 0x64, 0x23, 0x06, 0x18, + 0x1a, 0xb2, 0x63, 0xe1, 0x1d, 0xd9, 0x0e, 0xb0, 0x10, 0xf6, 0x9e, 0x60, 0x2b, 0x88, 0x30, 0x82, + 0xb9, 0xee, 0xd8, 0xb2, 0xd7, 0xf8, 0x39, 0x31, 0x2d, 0x0f, 0xa1, 0x8d, 0x1d, 0x7b, 0x95, 0x76, + 0xa7, 0xea, 0xd8, 0xf6, 0x3f, 0x51, 0xcb, 0x1b, 0xfe, 0xc3, 0x48, 0xe1, 0xb0, 0xe1, 0x51, 0xa0, + 0x63, 0xd7, 0x2d, 0xe0, 0xb1, 0xe5, 0xb9, 0xa4, 0x75, 0x88, 0xea, 0xc1, 0x66, 0x9f, 0x63, 0x6c, + 0x2e, 0xa7, 0xe0, 0x58, 0x91, 0xed, 0xa1, 0x14, 0x84, 0xea, 0x10, 0x6d, 0x3b, 0xa4, 0x49, 0x92, + 0x8d, 0x21, 0xf3, 0xde, 0x74, 0xc4, 0xda, 0x61, 0x23, 0x60, 0x26, 0x13, 0x58, 0xc4, 0x66, 0x16, + 0x95, 0x33, 0x7c, 0x63, 0xc2, 0x6f, 0x38, 0x25, 0x75, 0xf9, 0x15, 0x93, 0xe4, 0x12, 0xa4, 0xc0, + 0x7d, 0x95, 0x42, 0x5d, 0x08, 0x28, 0x91, 0x2d, 0x5c, 0xa2, 0xc3, 0xd7, 0x29, 0x7a, 0xc6, 0xd7, + 0xf9, 0x97, 0xbb, 0xc0, 0x3f, 0xfd, 0xf3, 0x05, 0x60, 0x77, 0x18, 0x0c, 0x6d, 0xf4, 0xd7, 0xa1, + 0x15, 0x2f, 0x40, 0x0b, 0xf4, 0x26, 0x00, 0xf7, 0x3e, 0x18, 0xa9, 0x7f, 0x99, 0x0b, 0xfc, 0x63, + 0x07, 0x95, 0x06, 0x08, 0xaf, 0x80, 0xc3, 0xbf, 0x3f, 0x50, 0x41, 0x33, 0x9c, 0x20, 0x04, 0x5d, + 0x06, 0x6e, 0xe0, 0x58, 0x38, 0xc2, 0x16, 0xa8, 0x8b, 0xf6, 0x29, 0x80, 0xb1, 0xfc, 0xfc, 0x5f, + 0xd8, 0x87, 0x60, 0x22, 0xf5, 0xa0, 0xbd, 0xf8, 0x30, 0x95, 0x0e, 0x17, 0xf2, 0x56, 0x5f, 0x52, + 0x45, 0x20, 0xf5, 0xe7, 0xf8, 0xc9, 0xc8, 0x0b, 0x9f, 0x7c, 0x8e, 0xed, 0x44, 0x9a, 0x3a, 0xc5, + 0x93, 0x09, 0xdf, 0x2e, 0x1b, 0xbf, 0x4f, 0x09, 0x0c, 0xbc, 0xed, 0x97, 0x14, 0x85, 0x7d, 0x1b, + 0x4e, 0x36, 0xa1, 0xfd, 0x39, 0x13, 0xa3, 0x97, 0xa6, 0xf8, 0x7d, 0x7e, 0x4f, 0x68, 0x83, 0x1c, + 0x0a, 0xff, 0x0c, 0x23, 0x61, 0xe0, 0x22, 0x4b, 0x1f, 0xab, 0x93, 0xa3, 0x63, 0xf4, 0xba, 0x39, + 0x57, 0xfa, 0x24, 0x79, 0xf0, 0x51, 0x8c, 0xc4, 0xe7, 0x64, 0xe8, 0xc2, 0xb2, 0xa1, 0xb8, 0x8d, + 0xec, 0x2d, 0x6a, 0x02, 0x7d, 0x18, 0x28, 0x0e, 0x78, 0x83, 0xed, 0x99, 0xf1, 0x40, 0xb4, 0x01, + 0x15, 0xff, 0x6b, 0x4a, 0x5d, 0x7b, 0xf6, 0xb7, 0xb3, 0x88, 0xee, 0xcd, 0x6d, 0x7d, 0x10, 0x26, + 0x13, 0x08, 0x28, 0xdd, 0x84, 0xe8, 0xa7, 0xe3, 0x08, 0x24, 0x5c, 0x98, 0xb6, 0x29, 0x66, 0xae, + 0x0d, 0xde, 0xe7, 0x24, 0xd6, 0x0e, 0xfd, 0xc9, 0x02, 0x12, 0x94, 0x69, 0xea, 0xaf, 0xd4, 0x65, + 0xeb, 0x9c, 0x14, 0x8b, 0xc9, 0x97, 0x14, 0xfb, 0xf9, 0xbf, 0xee, 0x0a, 0xee, 0xe7, 0x94, 0x0e, + 0xce, 0x35, 0x0d, 0x4e, 0x3b, 0x15, 0x93, 0x38, 0x1d, 0x07, 0x69, 0xb1, 0x87, 0x80, 0xff, 0xd2, + 0x91, 0x47, 0x3a, 0xf5, 0x8c, 0x49, 0x57, 0x8c, 0x7b, 0x05, 0xa3, 0x7b, 0xd9, 0xbc, 0xbd, 0x1a, + 0xe9, 0x5e, 0x08, 0xb9, 0xde, 0x50, 0xe4, 0xf3, 0x20, 0x29, 0x36, 0xf9, 0x13, 0x4b, 0xc7, 0x36, + 0x75, 0x19, 0x69, 0xd3, 0xd3, 0x17, 0x6e, 0x22, 0xc4, 0x98, 0xad, 0x5d, 0xcf, 0x9c, 0xec, 0xd2, + 0xa1, 0x24, 0x25, 0x7a, 0x0e, 0xf1, 0x1b, 0xaa, 0x96, 0xbd, 0x52, 0x35, 0xd3, 0xdb, 0xa1, 0x65, + 0x7c, 0x28, 0x9c, 0x19, 0xcb, 0x7b, 0x97, 0xca, 0x1c, 0xf5, 0xa5, 0x69, 0x7d, 0x03, 0x20, 0xdc, + 0x13, 0x61, 0x75, 0x3d, 0x75, 0x87, 0x16, 0x32, 0xe9, 0xdc, 0x22, 0xaf, 0x76, 0x20, 0x4e, 0x94, + 0x3d, 0x00, 0x4e, 0xda, 0x7c, 0x77, 0xd4, 0x15, 0x3f, 0xba, 0x14, 0x70, 0x06, 0x8c, 0x0f, 0xa8, + 0x46, 0x37, 0x7f, 0x14, 0x7c, 0x26, 0x55, 0x96, 0x3d, 0xf0, 0xe7, 0x43, 0x88, 0x13, 0x1b, 0xa4, + 0x7d, 0xd0, 0x99, 0x7d, 0x38, 0xef, 0xcd, 0xc6, 0xbd, 0x85, 0x44, 0xd4, 0xf3, 0xc1, 0xb3, 0x55, + 0x97, 0xea, 0xdd, 0x81, 0x30, 0xaf, 0x32, 0xa9, 0x10, 0xd8, 0x8e, 0x6d, 0x3a, 0x64, 0xf4, 0x03, + 0xe5, 0xf3, 0xfb, 0xd3, 0xba, 0x50, 0xf2, 0x1d, 0x75, 0x6c, 0xae, 0x81, 0xb6, 0x05, 0x8a, 0xca, + 0x41, 0x4b, 0x8b, 0x41, 0xfb, 0x4c, 0xca, 0x62, 0x3a, 0x14, 0x62, 0x61, 0x2a, 0x04, 0x12, 0x76, + 0xc1, 0x50, 0x0d, 0x3e, 0xa5, 0x0b, 0x81, 0x75, 0x8c, 0xa4, 0x19, 0xc4, 0x91, 0x02, 0x3e, 0xf7, + 0x83, 0xaf, 0x31, 0x3b, 0x20, 0xce, 0x9d, 0x3b, 0xb5, 0xfd, 0x23, 0x0a, 0x21, 0x2f, 0x20, 0x9f, + 0x5e, 0xa8, 0x21, 0x8e, 0xea, 0x58, 0x37, 0x97, 0x20, 0x3d, 0x05, 0xf7, 0x4b, 0xb2, 0x91, 0xca, + 0xe2, 0x9b, 0xa3, 0xa3, 0xa5, 0x38, 0x85, 0xa9, 0x3b, 0x8e, 0xed, 0x9c, 0x01, 0x3d, 0x27, 0xfb, + 0x87, 0x51, 0xf6, 0xfa, 0x6a, 0xb1, 0x50, 0x42, 0xb2, 0xa4, 0x1e, 0xe7, 0x5f, 0x11, 0xdd, 0x42, + 0xbd, 0x5b, 0x6d, 0x93, 0x81, 0x48, 0x32, 0x13, 0x0b, 0xbb, 0xce, 0x17, 0xce, 0x66, 0xb3, 0xfa, + 0x15, 0xfa, 0x0e, 0x3f, 0x45, 0xde, 0xe7, 0x5a, 0xc4, 0x98, 0x77, 0xbf, 0x1d, 0xa7, 0x8d, 0x27, + 0x9a, 0x7d, 0xc5, 0xc6, 0x06, 0x14, 0xc2, 0x7c, 0x05, 0x37, 0x13, 0xf3, 0x3c, 0x17, 0x77, 0x39, + 0x51, 0x38, 0x10, 0xf7, 0x65, 0x8f, 0xfb, 0x40, 0xd8, 0x5f, 0xcb, 0xfe, 0xee, 0x03, 0x79, 0x0b, + 0xa8, 0xc2, 0x5e, 0x8c, 0x28, 0xaf, 0xa4, 0xc6, 0x1f, 0xcc, 0x85, 0x41, 0xa5, 0x20, 0x94, 0xfb, + 0x6c, 0x28, 0x51, 0x91, 0xf9, 0x2b, 0x14, 0x8e, 0xa1, 0x9d, 0x04, 0xbc, 0x01, 0x8c, 0xf9, 0x26, + 0xb0, 0xbf, 0xb1, 0xf6, 0xe4, 0x1f, 0x98, 0x77, 0x4c, 0xb3, 0x4e, 0xa7, 0x15, 0x99, 0xf7, 0x4c, + 0x9b, 0xea, 0xaa, 0xe3, 0xc5, 0xd1, 0x7a, 0x48, 0x0e, 0xca, 0x5e, 0x34, 0x7e, 0xa7, 0x61, 0x61, + 0x31, 0x19, 0x16, 0x7e, 0x98, 0x30, 0x71, 0x7e, 0x09, 0x1b, 0x3f, 0x59, 0x3e, 0x34, 0xee, 0xf8, + 0xe4, 0xc4, 0xfa, 0xd3, 0x09, 0xff, 0x86, 0xd4, 0x6d, 0xe2, 0xa8, 0x0b, 0xdd, 0x8d, 0x84, 0x15, + 0xdd, 0xba, 0x63, 0x2f, 0x52, 0x7f, 0x1d, 0x62, 0xe1, 0x54, 0xc2, 0xde, 0xfc, 0x8d, 0xdc, 0x39, + 0x74, 0x06, 0xca, 0x9a, 0x4a, 0x68, 0xc6, 0xdf, 0xc7, 0x39, 0x5a, 0xb8, 0x6d, 0x04, 0x1b, 0x99, + 0xfb, 0x8f, 0x1f, 0x8f, 0xcc, 0x86, 0x3a, 0x02, 0x87, 0xba, 0xf6, 0xf4, 0x88, 0xef, 0x6c, 0x0e, + 0x21, 0xc6, 0x0b, 0xe4, 0x92, 0x52, 0x40, 0x17, 0x73, 0xa2, 0x6d, 0x9c, 0xa8, 0xc3, 0xc1, 0xa6, + 0xe5, 0x3f, 0x9f, 0x5b, 0xaf, 0xd7, 0x6d, 0x48, 0x2c, 0xa3, 0xe9, 0xb8, 0x72, 0x60, 0x2e, 0x57, + 0x6b, 0xef, 0xff, 0x78, 0xbb, 0x95, 0xfe, 0x03, 0x78, 0x62, 0xe8, 0xff, 0x4d, 0xc3, 0xbe, 0xd0, + 0x59, 0x60, 0xe9, 0x42, 0x85, 0xc7, 0x9a, 0x7e, 0x9a, 0xde, 0x64, 0x43, 0x85, 0x89, 0x35, 0x2a, + 0x68, 0xc6, 0x8e, 0x3c, 0x0a, 0xca, 0xa3, 0x2f, 0x17, 0x35, 0xfa, 0xb8, 0x68, 0x75, 0x51, 0xcc, + 0xcf, 0xd0, 0xfb, 0x3a, 0xb1, 0xb5, 0xb5, 0x7b, 0xa9, 0x82, 0x74, 0x71, 0xf4, 0xd7, 0xd8, 0xef, + 0x05, 0xb9, 0xb4, 0xb3, 0x5e, 0x2e, 0x69, 0x05, 0x01, 0xd6, 0xd6, 0xe6, 0x09, 0xed, 0x09, 0xc5, + 0x25, 0x92, 0x9e, 0x5c, 0x80, 0xf3, 0x39, 0x4e, 0x49, 0x46, 0x38, 0xc6, 0x48, 0xfd, 0xc4, 0x40, + 0x98, 0x19, 0xfc, 0xff, 0xf9, 0xbd, 0x48, 0x78, 0xd3, 0xf5, 0x62, 0x74, 0x54, 0x93, 0x0b, 0x44, + 0x3d, 0x42, 0xa5, 0xb8, 0xda, 0x5e, 0xe5, 0xec, 0x25, 0x41, 0x08, 0x9c, 0xdb, 0x39, 0xae, 0xaf, + 0xb0, 0x31, 0xe9, 0xb8, 0xef, 0x23, 0xe7, 0x7b, 0x09, 0x73, 0xac, 0x5c, 0xd2, 0xc6, 0x3b, 0x29, + 0x76, 0xce, 0xe5, 0xf7, 0x53, 0x29, 0xb1, 0x56, 0x44, 0xa1, 0x44, 0x08, 0x1c, 0x46, 0xa4, 0x17, + 0xc0, 0xfd, 0x8f, 0x92, 0xf1, 0xb2, 0x7b, 0xb9, 0x5f, 0x6d, 0x5f, 0x43, 0xdc, 0xfd, 0x65, 0xea, + 0x9c, 0x4a, 0x7f, 0x88, 0xe6, 0x65, 0xcd, 0xb8, 0x52, 0xe3, 0xbd, 0x82, 0xca, 0xc4, 0x84, 0x14, + 0x00, 0x9c, 0xd8, 0x69, 0x9a, 0xfa, 0x8b, 0x10, 0xd6, 0x51, 0xdd, 0xf2, 0x9f, 0x40, 0xf8, 0x67, + 0x22, 0x5e, 0x7c, 0x83, 0x37, 0x49, 0xa9, 0x4d, 0xb3, 0x89, 0x8a, 0xe2, 0x07, 0x1f, 0xeb, 0xac, + 0x47, 0x21, 0xf0, 0x71, 0x8c, 0x4c, 0x15, 0x2f, 0x1a, 0x73, 0xc2, 0x94, 0xc8, 0xa4, 0xe5, 0x99, + 0x93, 0x50, 0x2f, 0x91, 0xe0, 0x85, 0x60, 0x7c, 0x37, 0xe9, 0x70, 0x93, 0x18, 0xdc, 0xb9, 0x66, + 0xb2, 0x8f, 0x7d, 0xc0, 0xe2, 0xda, 0x51, 0xce, 0x1d, 0x20, 0x3a, 0x72, 0xcc, 0x08, 0x8d, 0x93, + 0xc2, 0x59, 0xd0, 0xbf, 0x82, 0x58, 0x3c, 0x20, 0xfb, 0x51, 0x6d, 0x3d, 0x0c, 0x47, 0xa2, 0x95, + 0x8b, 0x61, 0xf0, 0xf8, 0x4a, 0x3d, 0x08, 0x73, 0xb8, 0x0f, 0x0c, 0xc3, 0x24, 0xea, 0x41, 0xde, + 0x71, 0xd5, 0xa3, 0x74, 0x64, 0xb2, 0xd9, 0x63, 0x62, 0xe4, 0xce, 0xc3, 0xd3, 0x87, 0x2b, 0x0a, + 0xfe, 0x5a, 0xd9, 0x23, 0xe0, 0xdb, 0xb9, 0x16, 0xfb, 0x53, 0x93, 0xfa, 0xc2, 0x13, 0x09, 0xc8, + 0x16, 0x7e, 0x69, 0x5f, 0x87, 0x30, 0x22, 0x3d, 0x5e, 0x3b, 0x6a, 0x94, 0x33, 0xd1, 0x38, 0x34, + 0x32, 0x7e, 0x88, 0x2f, 0xfe, 0x15, 0x7f, 0x7a, 0x63, 0xba, 0xe6, 0xc8, 0xb4, 0xa8, 0x03, 0x9f, + 0x9a, 0xe3, 0xb1, 0xbe, 0x4c, 0x8c, 0x3a, 0x68, 0x7d, 0xac, 0xf4, 0xb9, 0x31, 0x98, 0x19, 0xf8, + 0xef, 0x88, 0x80, 0xe9, 0x64, 0xa5, 0xff, 0x24, 0x35, 0x8d, 0x48, 0x77, 0x7f, 0x2a, 0xc6, 0x17, + 0xeb, 0x7b, 0x7f, 0x47, 0x00, 0x4f, 0x2a, 0xb3, 0xb8, 0x47, 0x2c, 0x44, 0xe5, 0xdf, 0x2a, 0xcc, + 0x06, 0x38, 0xbd, 0x68, 0x6e, 0xda, 0x3f, 0xcc, 0x0f, 0x53, 0xb1, 0xb8, 0xdc, 0x88, 0xfd, 0x67, + 0x45, 0xb3, 0x04, 0xd7, 0xb3, 0x17, 0xf5, 0xf0, 0x24, 0xcd, 0x3d, 0x8f, 0xe5, 0x10, 0xec, 0xe8, + 0x18, 0x6e, 0x2e, 0x01, 0x89, 0x46, 0xb2, 0x27, 0x15, 0x84, 0xa3, 0x1a, 0x93, 0x95, 0x50, 0x95, + 0xe4, 0xc9, 0x40, 0x70, 0xe4, 0xf5, 0x46, 0x08, 0x42, 0xfd, 0xc4, 0x85, 0xe7, 0x17, 0x1e, 0x25, + 0x4d, 0x09, 0xfe, 0x4a, 0x43, 0xf2, 0x97, 0x30, 0xdc, 0x8d, 0x71, 0xbb, 0x5d, 0x58, 0xdf, 0xd6, + 0xde, 0xa4, 0xf4, 0xe5, 0x3b, 0xb4, 0x52, 0xd0, 0x5a, 0xba, 0x3f, 0x3e, 0x4e, 0x3d, 0x6f, 0xf5, + 0x35, 0x93, 0xf1, 0x7d, 0xff, 0xce, 0xcf, 0xdd, 0xd9, 0x8e, 0x91, 0x81, 0xd0, 0x9a, 0xc1, 0xf1, + 0x1f, 0x53, 0xc1, 0x0f, 0x84, 0x7c, 0x04, 0xeb, 0xfe, 0x31, 0x15, 0x98, 0x91, 0xb0, 0x81, 0x26, + 0xf3, 0xc7, 0x47, 0x2a, 0xd6, 0x1f, 0x7f, 0x7e, 0x5f, 0xd9, 0xd6, 0xce, 0x00, 0xbe, 0x52, 0x5f, + 0x0c, 0x20, 0x41, 0x8c, 0xd0, 0x23, 0xc0, 0x7b, 0x81, 0xf9, 0x52, 0x60, 0x3e, 0x66, 0x7e, 0x7e, + 0x47, 0x78, 0x3f, 0x6f, 0x3e, 0x03, 0xa6, 0x69, 0x47, 0x07, 0xc4, 0xbd, 0x63, 0x91, 0x09, 0x75, + 0x2e, 0x7b, 0x2a, 0x4a, 0x07, 0xeb, 0x94, 0x65, 0x8a, 0xa8, 0x0e, 0xc5, 0x64, 0x1c, 0x96, 0x0e, + 0x23, 0xc8, 0x33, 0xe3, 0x93, 0x80, 0x10, 0xac, 0xf6, 0xf5, 0x68, 0xe1, 0x0b, 0xb1, 0x54, 0x78, + 0xfa, 0x48, 0x89, 0x4d, 0x63, 0x2e, 0x07, 0xe0, 0x1f, 0xc7, 0xee, 0x49, 0xa5, 0x73, 0xaf, 0xf9, + 0x29, 0x9a, 0x73, 0x07, 0x3a, 0x1c, 0x14, 0x78, 0x98, 0xe8, 0x04, 0xfb, 0x6f, 0xcc, 0x35, 0xe9, + 0x6f, 0x71, 0xfc, 0xf5, 0xcf, 0xcc, 0xca, 0x69, 0x00, 0xbb, 0x04, 0x9f, 0xa3, 0x3b, 0xff, 0x7d, + 0xea, 0xb3, 0xfe, 0x07, 0xed, 0xd3, 0x49, 0x71, 0xe0, 0xd8, 0x82, 0x22, 0x3b, 0xa2, 0x7f, 0xcc, + 0x45, 0xdb, 0x99, 0x4c, 0x7b, 0x0e, 0x18, 0x82, 0xc5, 0xca, 0xba, 0x17, 0x98, 0x10, 0xea, 0x47, + 0x21, 0x50, 0xe6, 0x4b, 0x0a, 0x81, 0xb4, 0x9d, 0x98, 0xba, 0x35, 0xbe, 0xa4, 0x15, 0xc9, 0xce, + 0x6b, 0xcf, 0xaf, 0x50, 0xf0, 0x10, 0x64, 0x5f, 0xa2, 0x63, 0xf1, 0x15, 0xda, 0x1f, 0x62, 0x5d, + 0x73, 0xb9, 0xc4, 0x33, 0xec, 0x15, 0xd8, 0xa4, 0xa0, 0xd8, 0xfd, 0x25, 0xf5, 0xfa, 0x04, 0xd8, + 0xfe, 0xf1, 0x84, 0xd4, 0x5f, 0xa9, 0x57, 0xed, 0x40, 0x7c, 0xce, 0x77, 0x64, 0x3f, 0x73, 0xb4, + 0x94, 0x4f, 0x79, 0x70, 0xc1, 0xff, 0xd2, 0xe1, 0x90, 0x08, 0x1e, 0x67, 0xe4, 0x67, 0xc9, 0x19, + 0x93, 0x48, 0xc7, 0xde, 0x53, 0xd5, 0xa1, 0xe2, 0x02, 0xb8, 0xab, 0x01, 0x7a, 0xe8, 0xe1, 0x2f, + 0x46, 0xe4, 0x01, 0x0e, 0x4b, 0xdd, 0x07, 0x34, 0x68, 0xb2, 0x18, 0x8e, 0x18, 0xeb, 0x13, 0x75, + 0x6d, 0x1d, 0xb0, 0x34, 0xb5, 0x23, 0xe3, 0x1a, 0xec, 0x86, 0x49, 0x9c, 0xb0, 0x6e, 0xb7, 0xc9, + 0x68, 0x23, 0xcf, 0x9c, 0x0a, 0x62, 0x3c, 0xd0, 0x09, 0xed, 0xe9, 0x45, 0x41, 0x0e, 0x86, 0xac, + 0x5c, 0xcf, 0x7d, 0x5d, 0x05, 0x2f, 0x1d, 0x48, 0x95, 0x92, 0x39, 0xc7, 0x11, 0xfe, 0x77, 0xae, + 0x67, 0x3b, 0xfa, 0xf8, 0x9f, 0x6a, 0xf5, 0x45, 0xaa, 0xdd, 0xb9, 0xea, 0x26, 0xb4, 0x3f, 0xff, + 0x83, 0x30, 0x29, 0x9e, 0xef, 0x02, 0x0d, 0xe3, 0xde, 0x38, 0x86, 0xd1, 0x96, 0x93, 0xd7, 0x85, + 0xaa, 0x58, 0x4c, 0x56, 0xb9, 0xb2, 0xa5, 0xeb, 0xb7, 0x4f, 0x5e, 0xcf, 0x3a, 0x8f, 0x76, 0x7b, + 0xb8, 0x73, 0x90, 0x0b, 0x2c, 0xcf, 0x35, 0x9b, 0x15, 0xb2, 0xda, 0x77, 0x82, 0x1a, 0xc5, 0xe9, + 0x09, 0x53, 0xea, 0xca, 0x01, 0x0b, 0x38, 0x92, 0x2f, 0xa9, 0xe8, 0x84, 0xf9, 0x8a, 0x38, 0xab, + 0x81, 0x84, 0xbd, 0x42, 0x9b, 0xa9, 0x6a, 0x4d, 0xce, 0x04, 0x3a, 0x40, 0x36, 0x20, 0x4d, 0xb2, + 0xb6, 0x3f, 0x3e, 0xbd, 0xb8, 0x91, 0x20, 0xe4, 0xf1, 0x29, 0x79, 0x48, 0xca, 0x5f, 0xa2, 0x64, + 0xc8, 0xac, 0xa9, 0x1e, 0x64, 0x6b, 0xd7, 0x62, 0x9c, 0x8b, 0x49, 0xc0, 0x69, 0xd8, 0x97, 0xcb, + 0x26, 0xd4, 0x2e, 0x7e, 0x1a, 0x46, 0x4f, 0xe7, 0x4b, 0x5f, 0xac, 0x3e, 0x87, 0x56, 0x49, 0x9b, + 0xce, 0xad, 0x23, 0x0a, 0xa1, 0x07, 0xcf, 0x66, 0xc3, 0x98, 0x37, 0x57, 0xb8, 0xb4, 0x10, 0x73, + 0xa4, 0xe6, 0x9b, 0x8b, 0x20, 0x98, 0x77, 0x40, 0x48, 0x46, 0x8e, 0xc9, 0xeb, 0x07, 0x01, 0x91, + 0xa8, 0xc9, 0xbe, 0xca, 0x90, 0xa3, 0x1a, 0xfd, 0xf9, 0x96, 0xa3, 0x4c, 0x2b, 0x71, 0xbe, 0xcc, + 0x1c, 0xc3, 0x87, 0xc5, 0xe7, 0x6f, 0xb2, 0xfb, 0xf8, 0xbc, 0x26, 0x02, 0x1a, 0x26, 0x0b, 0x51, + 0xd8, 0x5d, 0x78, 0x4f, 0x7a, 0xf2, 0x8a, 0x6e, 0x20, 0x0f, 0xe2, 0x13, 0x89, 0x03, 0x96, 0x2b, + 0x97, 0xd6, 0x3a, 0x83, 0xe5, 0xc3, 0xdf, 0x82, 0x4b, 0xa0, 0x76, 0x78, 0x12, 0xcf, 0x4b, 0x5c, + 0xe1, 0x89, 0x41, 0x9c, 0x73, 0x06, 0x39, 0x12, 0xd7, 0xc8, 0xbe, 0x5d, 0x8c, 0xdf, 0xaf, 0xf3, + 0x3b, 0xa2, 0x5e, 0x78, 0x1a, 0x1d, 0xf0, 0xe8, 0xff, 0xa6, 0x8e, 0x49, 0x7a, 0x9d, 0x14, 0xf9, + 0xcf, 0x97, 0x98, 0xfc, 0x95, 0x7e, 0x07, 0xc3, 0xfd, 0x7e, 0x40, 0xc5, 0xcf, 0xe7, 0xdc, 0xfc, + 0xaa, 0x4e, 0x3c, 0xdd, 0x49, 0x96, 0x42, 0x53, 0x37, 0x37, 0xdf, 0xae, 0x70, 0xf8, 0x72, 0xba, + 0xfe, 0x16, 0x52, 0x87, 0x25, 0xce, 0x4e, 0xbc, 0x92, 0x00, 0x2e, 0x4d, 0x08, 0xf8, 0xf8, 0x70, + 0xb0, 0x3d, 0x47, 0x42, 0x54, 0x38, 0xaa, 0xa0, 0x47, 0x85, 0xcb, 0x30, 0x3a, 0xbc, 0x94, 0xcf, + 0x84, 0xf2, 0x14, 0xc9, 0x36, 0x8d, 0x3e, 0x72, 0x61, 0xfc, 0x77, 0x9e, 0x5b, 0x46, 0x57, 0x53, + 0xf2, 0x85, 0xf0, 0x66, 0x4a, 0x58, 0x39, 0xba, 0xd6, 0xfd, 0x4a, 0x57, 0x68, 0x58, 0x4f, 0x6e, + 0x59, 0x85, 0x59, 0xd8, 0x45, 0x4b, 0x73, 0x7a, 0x7b, 0x32, 0x91, 0xd7, 0x26, 0xee, 0xf2, 0xc5, + 0xd2, 0x76, 0xa4, 0x1a, 0x41, 0xb4, 0x72, 0xd1, 0x28, 0x5e, 0xb5, 0xa0, 0xef, 0x2a, 0x33, 0x14, + 0x7f, 0xbd, 0xcc, 0x70, 0x29, 0x18, 0xf9, 0x47, 0x61, 0xfc, 0xbb, 0xab, 0x0c, 0xe7, 0xd7, 0xbd, + 0x62, 0xb2, 0x98, 0x67, 0x25, 0x95, 0x38, 0x79, 0x2f, 0x5d, 0x3c, 0xcc, 0x89, 0x2f, 0x2d, 0xa9, + 0xcb, 0x71, 0x10, 0x23, 0x5d, 0x16, 0xe1, 0x7f, 0x2f, 0xf4, 0xb1, 0xa9, 0xa6, 0x54, 0xcb, 0x4a, + 0xc1, 0xc8, 0xd4, 0xa7, 0xc4, 0x61, 0x47, 0xa1, 0x40, 0xef, 0x39, 0x04, 0x8f, 0x81, 0x63, 0xe1, + 0xe3, 0xdc, 0x7d, 0x1e, 0x1f, 0xff, 0x15, 0xde, 0xaf, 0x0b, 0xab, 0x3d, 0x51, 0x3d, 0xaa, 0x10, + 0xf3, 0xf3, 0x2d, 0xf0, 0xc5, 0x52, 0x21, 0x82, 0x73, 0x7c, 0xf1, 0x26, 0xae, 0xdd, 0xdd, 0xd1, + 0x6b, 0x76, 0x27, 0x77, 0x30, 0x13, 0x37, 0x00, 0xef, 0xa6, 0xc7, 0x1b, 0x3b, 0x6c, 0xfd, 0x42, + 0xf9, 0xeb, 0xd0, 0xf9, 0x16, 0x62, 0x6c, 0x36, 0xff, 0x10, 0x62, 0xf6, 0x21, 0xc0, 0xaa, 0xad, + 0x2d, 0xae, 0x2c, 0x04, 0xff, 0x43, 0x86, 0x1d, 0xfe, 0x94, 0xe3, 0xf7, 0x4c, 0xf4, 0xeb, 0x9f, + 0xf4, 0x32, 0xac, 0xbd, 0x84, 0x95, 0xc7, 0x3f, 0x6e, 0xec, 0xe5, 0x23, 0x7c, 0x7e, 0xfa, 0x8c, + 0xbf, 0xa0, 0x09, 0x7d, 0x63, 0x73, 0x93, 0x32, 0xe1, 0xb1, 0xb6, 0xb9, 0x49, 0x69, 0x96, 0xea, + 0xba, 0x30, 0x22, 0xb8, 0x66, 0x70, 0xf3, 0x13, 0x07, 0x62, 0x8c, 0x88, 0xbf, 0x29, 0x99, 0x92, + 0xeb, 0x77, 0x77, 0x77, 0xdf, 0x33, 0x30, 0x1e, 0x41, 0x2e, 0xed, 0xf0, 0xb7, 0x23, 0x29, 0x80, + 0x93, 0x89, 0x29, 0x8a, 0x02, 0xb4, 0x43, 0x37, 0xc9, 0x7e, 0xbb, 0xf9, 0xd9, 0xb5, 0x1d, 0x67, + 0xf7, 0x25, 0x02, 0x95, 0x5a, 0xea, 0xfa, 0xd8, 0x4d, 0x35, 0xd4, 0x8d, 0xda, 0xa5, 0x70, 0xfe, + 0x15, 0x40, 0xfe, 0x9e, 0x89, 0x01, 0xc7, 0xd8, 0x85, 0xc0, 0xc3, 0x2b, 0xa5, 0x37, 0x14, 0x5b, + 0x60, 0x11, 0xfd, 0x05, 0xd0, 0x44, 0x3f, 0xde, 0x2f, 0xba, 0xf0, 0x18, 0x04, 0x02, 0x67, 0xd2, + 0xc7, 0x21, 0x53, 0x11, 0x40, 0x48, 0x49, 0x2c, 0x17, 0xdf, 0x00, 0x69, 0x34, 0xcb, 0xd4, 0xe6, + 0x08, 0xd6, 0x30, 0x2c, 0x9d, 0x3e, 0x05, 0x02, 0xc5, 0x90, 0x0d, 0xeb, 0xe6, 0xe7, 0x77, 0x33, + 0x6a, 0xd2, 0x4b, 0xa7, 0x37, 0x3f, 0xff, 0xf7, 0x87, 0xad, 0xce, 0x94, 0x26, 0xdf, 0xbe, 0x67, + 0xcc, 0x9f, 0xdf, 0x57, 0x09, 0x2c, 0x82, 0x0b, 0x1e, 0x37, 0x3f, 0x29, 0x98, 0xef, 0x99, 0x15, + 0x6c, 0x2a, 0x58, 0xed, 0x22, 0x0a, 0x2d, 0xeb, 0x6c, 0xfd, 0x96, 0x85, 0xdc, 0xb9, 0xb8, 0x60, + 0x56, 0xcd, 0x5e, 0x5f, 0xb0, 0x67, 0x2e, 0xde, 0xb1, 0x60, 0x77, 0xb7, 0xd4, 0xce, 0x96, 0xc4, + 0x87, 0x57, 0x17, 0x65, 0xd9, 0xe2, 0xf5, 0x45, 0x71, 0xe6, 0xdb, 0x6b, 0x9e, 0x13, 0xf9, 0x31, + 0xbc, 0x5a, 0x78, 0x75, 0xd5, 0x3c, 0xcb, 0xbc, 0x42, 0x5b, 0x5d, 0x9f, 0xbf, 0xb9, 0x6a, 0xfd, + 0x6c, 0xd1, 0x3a, 0x58, 0x88, 0xab, 0x0b, 0xe6, 0xf2, 0xa3, 0xeb, 0x0b, 0xe2, 0xcc, 0x2b, 0x0b, + 0xc6, 0x6b, 0xf8, 0xe6, 0x12, 0x8c, 0xe9, 0x1d, 0xd8, 0x33, 0x5a, 0x1e, 0xba, 0xc3, 0x5f, 0xa1, + 0x4d, 0xfd, 0x48, 0x7d, 0xcc, 0xb8, 0xba, 0x87, 0x77, 0xa9, 0xdd, 0x8f, 0xdf, 0xae, 0xc9, 0xd1, + 0x6b, 0x6c, 0xe5, 0xed, 0xe5, 0xc4, 0x34, 0xde, 0xdc, 0x2d, 0x58, 0x85, 0x73, 0x49, 0xd6, 0x44, + 0x7b, 0xac, 0x7f, 0xf2, 0x9c, 0xb5, 0x7e, 0x55, 0xa2, 0x72, 0xe3, 0x57, 0xc8, 0xcc, 0xa7, 0x10, + 0xc0, 0xe9, 0xda, 0x91, 0x11, 0x88, 0x6c, 0x47, 0x68, 0xde, 0xa8, 0x8e, 0xc5, 0x60, 0xa6, 0xe3, + 0x9b, 0x9f, 0x1c, 0x0d, 0x0a, 0x97, 0xba, 0xeb, 0x22, 0x88, 0x63, 0xc5, 0x34, 0x2d, 0x3a, 0xfe, + 0x18, 0xa7, 0x54, 0xe2, 0xf2, 0x6f, 0x24, 0xf1, 0x81, 0xf0, 0x1d, 0x4f, 0x4e, 0x9c, 0x6c, 0x44, + 0x70, 0x68, 0x30, 0x88, 0xd8, 0x04, 0x7d, 0xb0, 0x34, 0x25, 0xc7, 0x14, 0x8f, 0x67, 0xe0, 0xa1, + 0xee, 0xc1, 0x13, 0x54, 0x6a, 0x7b, 0x49, 0x87, 0xfe, 0xb8, 0x59, 0xaf, 0xc6, 0x10, 0x5b, 0xf4, + 0x1c, 0xd5, 0xb4, 0x3e, 0x79, 0x53, 0xd3, 0x85, 0x3e, 0x30, 0xbb, 0x3f, 0x6e, 0xb2, 0x85, 0xc2, + 0x0d, 0xfe, 0xda, 0xf1, 0x8f, 0x1b, 0xf6, 0x26, 0x45, 0x8b, 0x36, 0x37, 0xf4, 0x8c, 0xe7, 0x06, + 0xfc, 0xae, 0xb5, 0x86, 0x16, 0x9b, 0x2d, 0xdd, 0xa4, 0x32, 0x17, 0x31, 0x0a, 0x6d, 0x32, 0xd0, + 0x3a, 0xa2, 0xd1, 0xb5, 0x4f, 0x93, 0x9e, 0xcc, 0x53, 0x84, 0xa3, 0x5b, 0xb6, 0x60, 0x37, 0x1d, + 0xed, 0xc7, 0x8d, 0x3a, 0xc2, 0x4b, 0xde, 0x23, 0x4b, 0x5d, 0xce, 0x11, 0x4e, 0x30, 0xf0, 0x02, + 0x88, 0xe4, 0xfa, 0x29, 0x30, 0xe0, 0xd1, 0x0d, 0xc6, 0x9b, 0x24, 0x6b, 0x78, 0x8c, 0x18, 0xdc, + 0x9b, 0x04, 0x6f, 0xc3, 0xf0, 0xf4, 0x68, 0x54, 0x70, 0xfc, 0x12, 0x8f, 0x02, 0x1b, 0x8c, 0xbb, + 0x49, 0xec, 0x22, 0x1a, 0xe8, 0x9f, 0xf3, 0x39, 0xf8, 0x53, 0x85, 0x9b, 0x9f, 0x7d, 0x0c, 0x3a, + 0xf0, 0xc7, 0xa0, 0x97, 0x4b, 0xdd, 0x3a, 0x67, 0xf7, 0xdb, 0x1c, 0xeb, 0x9f, 0xad, 0x7f, 0xcc, + 0x3f, 0xba, 0x95, 0x4f, 0x13, 0xd5, 0x72, 0xf5, 0x5f, 0x63, 0x23, 0xf3, 0x1f, 0x60, 0x63, 0x62, + 0x0a, 0x3d, 0x8b, 0x38, 0x75, 0x3b, 0xf0, 0x30, 0xa1, 0x8d, 0x2b, 0xfe, 0xd3, 0xc7, 0x0f, 0x93, + 0x09, 0x44, 0x56, 0xcc, 0xc7, 0xcf, 0xdf, 0x62, 0xff, 0x78, 0x16, 0xed, 0x85, 0x63, 0xbe, 0x9d, + 0x50, 0xfe, 0x35, 0x98, 0xea, 0x3b, 0x60, 0xaa, 0xbf, 0x08, 0x53, 0x2b, 0xbd, 0x0d, 0x13, 0xc7, + 0xfc, 0x0a, 0x4c, 0xb0, 0x71, 0x6f, 0xc2, 0xc4, 0x31, 0xbf, 0x02, 0x13, 0x5f, 0x6f, 0xc1, 0xc4, + 0xd7, 0x75, 0x98, 0x29, 0x3c, 0xd1, 0x39, 0x05, 0xcc, 0x30, 0x6f, 0x33, 0x2a, 0x18, 0x13, 0x03, + 0xfe, 0x3e, 0x72, 0xde, 0x27, 0x00, 0x6f, 0x23, 0x8c, 0x63, 0xde, 0x4f, 0x04, 0xc4, 0xe2, 0x2d, + 0x98, 0xc1, 0x98, 0x5f, 0x81, 0x89, 0xec, 0x7d, 0x0b, 0x26, 0x8e, 0xf9, 0x05, 0x98, 0x25, 0xdc, + 0xd9, 0x1b, 0x30, 0xe9, 0x98, 0x77, 0xc3, 0x74, 0x96, 0xe3, 0xd7, 0xe1, 0xe1, 0xdf, 0x04, 0xa6, + 0x92, 0x7f, 0x9e, 0x44, 0x93, 0x93, 0xd4, 0xe5, 0xab, 0x0e, 0x69, 0x96, 0xde, 0xb8, 0xbe, 0xf9, + 0xd9, 0xb9, 0xa8, 0xeb, 0x34, 0x44, 0x76, 0xad, 0x64, 0x18, 0x19, 0x22, 0x86, 0x35, 0x69, 0xcd, + 0x4a, 0x41, 0x9c, 0x99, 0xc0, 0x2f, 0x38, 0x46, 0xea, 0x5a, 0xb6, 0xf7, 0x89, 0x41, 0xa0, 0xec, + 0x05, 0xd7, 0xfd, 0xce, 0xf9, 0x2c, 0xce, 0xcf, 0xfe, 0xf3, 0xf9, 0x59, 0x9c, 0x9f, 0xbb, 0xe4, + 0xbe, 0xcf, 0x4c, 0x38, 0xb5, 0xb0, 0x40, 0x31, 0x0b, 0xe2, 0x15, 0xea, 0xf4, 0x53, 0xef, 0x72, + 0xd9, 0xe1, 0x1f, 0xda, 0x84, 0xfe, 0x7a, 0x94, 0x8b, 0xfc, 0x75, 0x78, 0xac, 0x79, 0x08, 0xc4, + 0x71, 0x68, 0x10, 0xbf, 0x07, 0x5d, 0xed, 0x60, 0xa5, 0x13, 0x3b, 0x1f, 0x3e, 0x0d, 0x32, 0x94, + 0xef, 0xc1, 0x59, 0xd9, 0xcf, 0x72, 0x50, 0x6a, 0xff, 0x9e, 0x09, 0xdb, 0x87, 0x1e, 0x01, 0x6f, + 0x70, 0xfe, 0x2b, 0xd9, 0x91, 0x09, 0xa0, 0x5f, 0x77, 0x99, 0x88, 0x41, 0x78, 0x2b, 0xfc, 0x9a, + 0x67, 0x3c, 0xa3, 0x4d, 0x30, 0x3e, 0xe5, 0xae, 0x20, 0x71, 0xb9, 0xe0, 0xdd, 0x82, 0xbb, 0xbd, + 0xef, 0x8a, 0x68, 0x72, 0xd9, 0xc2, 0x3f, 0x8a, 0x68, 0xba, 0xb8, 0xf4, 0x1b, 0x3e, 0x92, 0x8e, + 0xf9, 0xc5, 0x28, 0xe7, 0x3f, 0xe1, 0x1e, 0xaf, 0x91, 0x0f, 0x2b, 0xa4, 0x78, 0x16, 0xb8, 0xbb, + 0x4e, 0xc2, 0x60, 0xc7, 0xe1, 0x4d, 0xea, 0x77, 0x11, 0x34, 0xcf, 0x3c, 0xfc, 0x23, 0x82, 0xd6, + 0x23, 0x64, 0xde, 0x20, 0x6a, 0x3c, 0xee, 0xff, 0x63, 0xc2, 0x2e, 0xc2, 0x30, 0xfd, 0x6d, 0x9a, + 0x66, 0x2f, 0xd8, 0x30, 0x6a, 0x3c, 0x70, 0xcc, 0x64, 0x3b, 0x62, 0x8e, 0xcc, 0x88, 0x37, 0x08, + 0x0c, 0x58, 0x17, 0x0b, 0x4b, 0xaf, 0xe5, 0x00, 0x93, 0xad, 0x65, 0xba, 0x54, 0x77, 0xc2, 0xaa, + 0xc1, 0xdd, 0xdd, 0x5d, 0x62, 0x58, 0xe0, 0x1f, 0xcf, 0xd5, 0xb0, 0xab, 0x1b, 0x0b, 0xbc, 0xac, + 0xf9, 0x56, 0x84, 0xea, 0xea, 0x06, 0x3e, 0xbe, 0x0a, 0x3f, 0x31, 0x6e, 0xed, 0x05, 0xfc, 0x7e, + 0x55, 0xfd, 0x2b, 0xea, 0xc6, 0x76, 0x20, 0x60, 0x7d, 0xc5, 0x00, 0xd0, 0xc0, 0xd8, 0x7d, 0xa4, + 0x99, 0xd0, 0x29, 0xd5, 0x11, 0x87, 0x14, 0xbd, 0x4a, 0xeb, 0x82, 0x8d, 0x0d, 0x09, 0x7f, 0x6e, + 0x97, 0xd1, 0xe6, 0xe1, 0x39, 0xe3, 0x31, 0x49, 0xdb, 0x8e, 0x0e, 0xef, 0x81, 0x61, 0x7f, 0xc3, + 0x31, 0xbc, 0x0a, 0x20, 0xfb, 0x0e, 0xcf, 0xf0, 0x2a, 0x80, 0xdc, 0x99, 0x6b, 0xf8, 0x45, 0x00, + 0x79, 0x04, 0x90, 0x8f, 0x01, 0x84, 0x5c, 0xfe, 0x45, 0x20, 0x05, 0x04, 0x52, 0xf8, 0x0d, 0x2c, + 0x8a, 0x08, 0xa0, 0xf8, 0x1b, 0x00, 0xee, 0x11, 0xc0, 0xfd, 0x6f, 0x00, 0x28, 0x21, 0x80, 0xd2, + 0x6f, 0xd2, 0xe1, 0x01, 0x81, 0x3c, 0xfc, 0x06, 0x16, 0x6c, 0x10, 0x6a, 0x30, 0xbf, 0x03, 0x22, + 0x10, 0xca, 0xdf, 0x91, 0x4a, 0x96, 0x8a, 0x25, 0x9b, 0xfd, 0x4d, 0x6a, 0xb0, 0x54, 0x38, 0xd9, + 0xdf, 0x91, 0x4e, 0x96, 0x8a, 0x27, 0x9b, 0xff, 0x1d, 0x10, 0x54, 0x38, 0xd9, 0xdf, 0x91, 0x4e, + 0x96, 0x8a, 0x27, 0x5b, 0x3c, 0xa5, 0x47, 0xf0, 0x67, 0x60, 0x21, 0x94, 0xe0, 0xe4, 0x06, 0x4f, + 0xcc, 0xd0, 0xf8, 0x74, 0x83, 0xa3, 0x79, 0xb4, 0xe9, 0xb1, 0xdb, 0x0a, 0x3c, 0x09, 0x1d, 0x37, + 0xb2, 0xb7, 0x37, 0xa1, 0x75, 0xea, 0xd1, 0x32, 0x4f, 0xd2, 0x5f, 0x85, 0x85, 0x9f, 0x6e, 0x18, + 0x3e, 0xb9, 0x2b, 0x75, 0x79, 0xb4, 0x08, 0x3d, 0xdd, 0x82, 0x85, 0xd0, 0xc7, 0x60, 0x27, 0x35, + 0x8f, 0x14, 0x97, 0x08, 0xb1, 0xe4, 0x94, 0x29, 0x58, 0x7e, 0x30, 0x70, 0x29, 0xb6, 0x98, 0xd2, + 0xe0, 0xa9, 0xab, 0x6e, 0x74, 0x5a, 0xa4, 0x76, 0x43, 0xb3, 0x7d, 0x17, 0x02, 0xc1, 0xa9, 0x6f, + 0xef, 0x2b, 0xa0, 0x48, 0x4a, 0xdb, 0x69, 0xd6, 0xeb, 0x1b, 0xd3, 0x76, 0xd7, 0x36, 0xc6, 0x0f, + 0xff, 0xf1, 0xc6, 0x2a, 0xa6, 0xe3, 0x7a, 0xa9, 0x15, 0xc5, 0xe1, 0x6b, 0x2a, 0x11, 0x0d, 0x00, + 0x3e, 0xee, 0x79, 0x08, 0x10, 0xe0, 0x15, 0xdc, 0xd7, 0x39, 0xd4, 0x83, 0xa8, 0x8b, 0x67, 0xf3, + 0x07, 0x67, 0x7e, 0x13, 0x42, 0x7f, 0x54, 0xaf, 0x02, 0xd7, 0xdf, 0x05, 0x3c, 0x1b, 0x01, 0x2f, + 0xc4, 0xc0, 0x73, 0x11, 0x70, 0xac, 0xec, 0xa6, 0xf0, 0x5e, 0xf2, 0xe5, 0x05, 0xbc, 0x77, 0x2d, + 0xc0, 0xdc, 0x45, 0x4b, 0x14, 0x0b, 0x77, 0x05, 0xcc, 0x9b, 0xf4, 0x15, 0x3e, 0x65, 0x0f, 0x9b, + 0x81, 0x11, 0x3f, 0xdd, 0x70, 0xc9, 0xc4, 0xa5, 0xad, 0x93, 0xd5, 0xde, 0xb9, 0xdc, 0xff, 0xa3, + 0xed, 0x48, 0x7b, 0xdb, 0xc6, 0x95, 0xdf, 0x1f, 0xf0, 0xfe, 0x83, 0x2d, 0xec, 0x33, 0xa4, 0x9a, + 0x91, 0xed, 0xa4, 0xa7, 0x5d, 0xc2, 0x70, 0x9c, 0x34, 0xcd, 0x6e, 0x8e, 0x36, 0x49, 0xdb, 0xed, + 0x06, 0xd9, 0x40, 0x96, 0x28, 0x5b, 0x89, 0x2c, 0x79, 0x25, 0xf9, 0x4a, 0xec, 0xff, 0xfe, 0x66, + 0x86, 0x3a, 0xe8, 0xab, 0xbb, 0x28, 0xde, 0x2b, 0xd0, 0x48, 0xe2, 0x31, 0x24, 0xe7, 0x9e, 0xa1, + 0x44, 0xff, 0xcd, 0x60, 0x75, 0xf3, 0x0d, 0x0c, 0xf6, 0xe3, 0xb4, 0x96, 0xcc, 0xea, 0xf7, 0xc2, + 0x44, 0xb2, 0x05, 0xde, 0x6c, 0x4a, 0x20, 0x9e, 0x8d, 0xe0, 0x05, 0x8f, 0xaa, 0xf4, 0x85, 0x23, + 0x11, 0xdc, 0x58, 0x3d, 0x70, 0x5d, 0x76, 0x65, 0x3d, 0xd3, 0x28, 0x65, 0x47, 0xc2, 0x15, 0x73, + 0x66, 0x3b, 0x12, 0xae, 0x7f, 0x3f, 0x68, 0xe3, 0xa7, 0x52, 0xad, 0x69, 0x3c, 0xf2, 0xd3, 0xa3, + 0xee, 0xff, 0x54, 0x5a, 0x3b, 0x73, 0xbf, 0x7e, 0x7a, 0xd8, 0x83, 0x5d, 0xc3, 0xd6, 0x5f, 0xda, + 0xbb, 0x87, 0xcd, 0xbd, 0xaf, 0x1d, 0x99, 0x65, 0xc5, 0x51, 0xa3, 0xcf, 0xf4, 0xb6, 0x64, 0x22, + 0xf3, 0xe3, 0x4e, 0xb6, 0xd4, 0xe1, 0x46, 0x60, 0xce, 0xa3, 0xf4, 0x4d, 0xde, 0x8a, 0x2f, 0x89, + 0x5f, 0x90, 0xc9, 0x80, 0x60, 0xd8, 0x07, 0x6d, 0x06, 0xbe, 0x7c, 0x9a, 0x79, 0x55, 0x5e, 0x93, + 0x1d, 0x05, 0xfd, 0xec, 0x17, 0xcb, 0xbc, 0xaf, 0x87, 0x97, 0x57, 0xd3, 0xfa, 0x6f, 0x27, 0x7d, + 0x3c, 0x44, 0xb1, 0x73, 0x71, 0xfd, 0x65, 0x70, 0xfc, 0x05, 0x4f, 0xb0, 0xea, 0xd3, 0x2f, 0x7c, + 0x9d, 0x77, 0x3b, 0x74, 0xfa, 0xe3, 0x1f, 0x9f, 0xfd, 0x13, 0x79, 0x82, 0xd8, 0xef, 0x17, 0xd7, + 0x57, 0xf5, 0xd3, 0x4e, 0x14, 0xbf, 0xb4, 0x5f, 0xd3, 0x49, 0x53, 0x57, 0xc1, 0xe7, 0x2f, 0x0d, + 0x3c, 0x9c, 0x6a, 0xf6, 0x30, 0x9d, 0xbc, 0xfd, 0xfe, 0x99, 0x4e, 0xe2, 0xfa, 0xd5, 0x3e, 0x1e, + 0xfc, 0x61, 0x4f, 0x3b, 0x9d, 0xa3, 0x18, 0x4f, 0x18, 0x7c, 0x73, 0xd4, 0xb1, 0x9d, 0xc9, 0x5f, + 0xf8, 0xc3, 0x5d, 0x9d, 0xdf, 0xce, 0xae, 0xbf, 0x5c, 0x1d, 0x7e, 0xfd, 0x38, 0x10, 0x6f, 0x9c, + 0xfe, 0xec, 0x21, 0x99, 0x4e, 0x8f, 0x3b, 0x47, 0x87, 0x23, 0xb7, 0x56, 0x7b, 0xf5, 0xf4, 0xe5, + 0x68, 0xfa, 0xe8, 0x07, 0xa3, 0xa7, 0x0f, 0x9d, 0xe0, 0xb7, 0xf0, 0xfa, 0xc6, 0xeb, 0x5f, 0x7c, + 0xb8, 0xb9, 0xa9, 0xff, 0xf8, 0x97, 0xd3, 0x76, 0xfc, 0x3b, 0x39, 0xb2, 0x2f, 0x3d, 0xf1, 0x7b, + 0xf5, 0xe4, 0x55, 0x30, 0xb9, 0x38, 0xb3, 0x9e, 0x02, 0xcf, 0x7f, 0xed, 0x36, 0x2e, 0x26, 0xd5, + 0x5a, 0xf2, 0x74, 0x60, 0xbf, 0xa9, 0x1e, 0x24, 0xc0, 0x1b, 0xb5, 0x9b, 0xd1, 0xb8, 0xf6, 0xd0, + 0x13, 0x6f, 0xdf, 0xbc, 0xb1, 0xdf, 0xbe, 0xc2, 0x93, 0xf2, 0x8f, 0x3f, 0xf7, 0x61, 0xb8, 0xfe, + 0x31, 0x9e, 0xb5, 0xd5, 0xc9, 0x6e, 0x4f, 0xf1, 0xe0, 0x2c, 0x3c, 0x6f, 0xf5, 0x90, 0xce, 0x6d, + 0xc6, 0xdb, 0xfe, 0x21, 0x9d, 0x51, 0x86, 0xc7, 0xf1, 0x2b, 0xb7, 0x1e, 0xe2, 0xe1, 0x18, 0xa7, + 0xfb, 0xd9, 0xeb, 0xc8, 0xdb, 0xd3, 0xce, 0x67, 0x3c, 0xd7, 0xac, 0x0b, 0xa0, 0x4e, 0x2f, 0xbe, + 0xbf, 0xaa, 0x5a, 0x1f, 0x27, 0x4e, 0xfc, 0xfd, 0xca, 0x7a, 0x9a, 0x3c, 0xfc, 0xf6, 0xee, 0xc1, + 0x3d, 0x7f, 0xe3, 0x4e, 0x9e, 0x6a, 0xf5, 0xea, 0xf7, 0x83, 0x6a, 0x6d, 0xbf, 0x7a, 0x70, 0xf0, + 0x7a, 0xfa, 0x36, 0x6f, 0xde, 0x07, 0x40, 0x03, 0xc4, 0xfa, 0xf1, 0x29, 0x12, 0x63, 0xb0, 0x3a, + 0xa9, 0x9f, 0x9d, 0xdf, 0xd1, 0xf1, 0xe5, 0xd8, 0x89, 0xdc, 0x33, 0xfb, 0xe9, 0xf7, 0xa1, 0x9f, + 0xfc, 0x71, 0x30, 0xff, 0xf0, 0xda, 0x1d, 0x4f, 0x27, 0xfb, 0x62, 0xf8, 0xae, 0xea, 0x54, 0x83, + 0x57, 0x81, 0x38, 0x78, 0xba, 0x39, 0xb0, 0xff, 0x70, 0x47, 0xd5, 0x5a, 0xe7, 0xe3, 0xff, 0x6f, + 0x1e, 0x12, 0x4f, 0xdf, 0x47, 0x07, 0x4f, 0x17, 0xbd, 0x37, 0xbd, 0x93, 0x2f, 0x22, 0x7e, 0xf3, + 0xfd, 0xe9, 0xed, 0xf4, 0xf2, 0xe0, 0xe0, 0xa5, 0x3b, 0x14, 0x63, 0xf7, 0xe0, 0x55, 0x6f, 0xe8, + 0xd4, 0x9e, 0xde, 0xcd, 0xab, 0x93, 0x77, 0x63, 0xf7, 0x69, 0xf6, 0xf6, 0xff, 0x43, 0x93, 0xfc, + 0xb6, 0x7b, 0xea, 0xd8, 0x2f, 0x67, 0x07, 0xd5, 0xfd, 0xf3, 0xaf, 0x8d, 0xa8, 0x77, 0xe2, 0x0e, + 0x3f, 0x8c, 0xa2, 0x6a, 0xed, 0xb5, 0xa8, 0xf9, 0xef, 0xc6, 0xe2, 0xcd, 0x81, 0x7b, 0x53, 0x1d, + 0x25, 0x8d, 0xea, 0xc7, 0xfd, 0x5a, 0x2f, 0xa8, 0xd6, 0xfc, 0x93, 0xdf, 0xdf, 0xee, 0x80, 0xf3, + 0x3f, 0xc3, 0xd7, 0x4f, 0xfd, 0xeb, 0x7f, 0xaa, 0x8d, 0xdf, 0xd6, 0x1e, 0xcf, 0xbe, 0x77, 0xff, + 0xea, 0xcc, 0x4e, 0x2f, 0x6e, 0xe6, 0x7f, 0xc8, 0x5f, 0x6f, 0xba, 0xfe, 0xf2, 0xf5, 0xf2, 0xea, + 0xb7, 0x57, 0xdd, 0xef, 0xa7, 0xa7, 0x5c, 0xcb, 0x95, 0x91, 0x92, 0xd9, 0x45, 0xdd, 0xf1, 0x38, + 0xc9, 0xb7, 0xc5, 0xf3, 0xed, 0xf0, 0xad, 0x0e, 0x2e, 0xfa, 0x84, 0xe9, 0x0b, 0x09, 0x8a, 0xda, + 0x8c, 0xc4, 0x5f, 0x63, 0x11, 0x27, 0xbf, 0xc6, 0x61, 0xa0, 0x07, 0x63, 0xdf, 0x07, 0xed, 0x79, + 0x25, 0x5c, 0x30, 0xea, 0x83, 0xdd, 0xda, 0x77, 0x3b, 0xa4, 0xd5, 0x0d, 0xcd, 0xae, 0x1f, 0xc6, + 0xa2, 0x24, 0x37, 0x29, 0x7f, 0xe4, 0x76, 0x6f, 0x87, 0x85, 0xca, 0xfc, 0xe4, 0x23, 0xc2, 0xa1, + 0xed, 0xf9, 0x6f, 0xde, 0xa3, 0xf7, 0x0f, 0xa7, 0x83, 0x28, 0x21, 0x97, 0x64, 0x15, 0xa0, 0x1d, + 0xb8, 0x57, 0xe4, 0xfc, 0xd2, 0xfa, 0x7a, 0x78, 0xb2, 0x9b, 0xfc, 0x5d, 0xca, 0xd5, 0xb9, 0xad, + 0x79, 0x9a, 0xe7, 0x96, 0x23, 0x4a, 0x53, 0x2f, 0x19, 0x94, 0x64, 0x0d, 0x02, 0xa7, 0x6f, 0x67, + 0xc9, 0xa2, 0xec, 0xbf, 0x79, 0xfd, 0xb2, 0x95, 0xba, 0x76, 0xa5, 0xde, 0xbc, 0xd4, 0xf1, 0x22, + 0x3b, 0x0c, 0xc3, 0x47, 0x4f, 0xd0, 0x2b, 0x13, 0xc9, 0x40, 0xc8, 0x97, 0x0b, 0xec, 0x70, 0x38, + 0x1c, 0x07, 0x94, 0x62, 0xca, 0xdd, 0xc0, 0x0d, 0xab, 0x42, 0xdf, 0x28, 0x6f, 0x31, 0x11, 0x6b, + 0xa9, 0x26, 0x3c, 0xd1, 0x69, 0x6d, 0x37, 0x7a, 0x95, 0x1d, 0x7c, 0x60, 0x87, 0x76, 0xc1, 0x05, + 0xa9, 0xeb, 0x14, 0x96, 0xc6, 0x40, 0x8d, 0xde, 0xd8, 0xf3, 0xf1, 0x85, 0xf0, 0x92, 0x90, 0xa6, + 0x9d, 0x51, 0x29, 0xac, 0x0b, 0xc7, 0x8e, 0xc0, 0x6d, 0xca, 0x5e, 0x32, 0x01, 0x23, 0x16, 0x4e, + 0x4d, 0xd9, 0xf7, 0x7b, 0x38, 0x26, 0x4f, 0x3b, 0x12, 0xc9, 0x38, 0x0a, 0x4a, 0x49, 0x08, 0x77, + 0x60, 0xa2, 0xd0, 0xff, 0xc3, 0x70, 0x00, 0x17, 0x8e, 0x3e, 0x60, 0x8c, 0xf1, 0x01, 0x2e, 0x99, + 0xbe, 0xb3, 0x86, 0x31, 0xf0, 0x1e, 0xdf, 0x5b, 0xc1, 0x37, 0x75, 0x00, 0x05, 0x51, 0x20, 0x22, + 0x73, 0x17, 0x07, 0xac, 0x86, 0x29, 0x67, 0x61, 0x44, 0x2e, 0xcb, 0x65, 0x36, 0x2b, 0xa8, 0x14, + 0x3f, 0x24, 0xff, 0x66, 0xff, 0x7d, 0xb5, 0xff, 0x38, 0x48, 0x3c, 0x1f, 0xa6, 0x8d, 0x64, 0xff, + 0x1b, 0x8a, 0x7f, 0x08, 0x23, 0x58, 0x3d, 0x3a, 0xce, 0x22, 0xa2, 0x63, 0x01, 0x61, 0x68, 0x56, + 0xf2, 0xc0, 0xd5, 0xc4, 0xef, 0xf5, 0x91, 0x92, 0x82, 0xde, 0x17, 0x02, 0x34, 0x10, 0x3a, 0x42, + 0xd7, 0x4d, 0x57, 0x0d, 0x58, 0x19, 0x22, 0x0e, 0xe2, 0x70, 0x1c, 0xd9, 0xc0, 0x35, 0x03, 0x11, + 0x94, 0x82, 0x10, 0x13, 0x8c, 0x88, 0x65, 0x73, 0x83, 0xf6, 0x40, 0xd9, 0x9c, 0xee, 0x88, 0x33, + 0x6d, 0x0b, 0xa1, 0x37, 0x96, 0x85, 0xee, 0xe3, 0x16, 0xe2, 0xe7, 0x2f, 0xa8, 0xd4, 0x5e, 0x94, + 0xff, 0xfd, 0xaf, 0x17, 0x25, 0x2f, 0x0a, 0xcd, 0x87, 0xb8, 0x34, 0x79, 0x69, 0xbe, 0x34, 0xeb, + 0x58, 0xb0, 0x5f, 0x6f, 0xbc, 0xde, 0x83, 0x3f, 0xef, 0x4a, 0xbf, 0xd2, 0xd7, 0xd7, 0x47, 0x56, + 0xe0, 0x09, 0x1f, 0x6b, 0xce, 0x3c, 0x5b, 0x04, 0x31, 0xac, 0x68, 0x0c, 0xeb, 0x8a, 0x4a, 0xe7, + 0x9f, 0xce, 0x4a, 0xfb, 0xb2, 0x4f, 0x1f, 0xf8, 0x7e, 0xdc, 0x33, 0x61, 0xcd, 0xb5, 0x07, 0x0b, + 0x7b, 0xd5, 0x24, 0x58, 0xa8, 0xab, 0xfd, 0xfb, 0x5f, 0xe5, 0xec, 0x07, 0x5c, 0xf5, 0x84, 0x09, + 0xe3, 0x59, 0x0b, 0x7b, 0x0f, 0xc0, 0x52, 0x1a, 0xe7, 0xe8, 0x78, 0x87, 0x6e, 0x49, 0xcc, 0xf0, + 0x8d, 0xff, 0xb8, 0x52, 0xd1, 0x10, 0xb0, 0xeb, 0x05, 0xc2, 0xd1, 0xca, 0x59, 0x25, 0x30, 0xce, + 0xd8, 0x17, 0x6d, 0x79, 0x31, 0xd3, 0xa6, 0x5c, 0xe8, 0x46, 0x53, 0xcb, 0xc0, 0x16, 0x90, 0x64, + 0xef, 0x4a, 0x45, 0x5e, 0x4d, 0x6b, 0xe8, 0xb4, 0xe5, 0xad, 0x2e, 0x8c, 0x66, 0x62, 0xc2, 0xa4, + 0xb0, 0xe7, 0x92, 0x52, 0x9e, 0x2c, 0x9f, 0x15, 0x4c, 0x09, 0x99, 0x1b, 0xe8, 0xe2, 0xc1, 0xb4, + 0x5a, 0x13, 0x60, 0x4a, 0x9b, 0x2b, 0xb5, 0x4b, 0xe6, 0x71, 0xf8, 0x33, 0xe0, 0xb7, 0x77, 0x6c, + 0x0c, 0x7f, 0x5a, 0xf9, 0x0f, 0xd2, 0x8e, 0xe4, 0x8a, 0xb0, 0x47, 0xc8, 0x02, 0x16, 0x31, 0x8f, + 0xc5, 0xdc, 0x8a, 0xfa, 0x63, 0xf2, 0x4b, 0x99, 0xc5, 0xc7, 0x2d, 0xe0, 0x0c, 0xdd, 0x2b, 0xca, + 0x4c, 0x5f, 0x04, 0xfd, 0x64, 0xd0, 0xda, 0x7f, 0xef, 0xed, 0xed, 0xb5, 0x8c, 0x81, 0x39, 0x1a, + 0xc7, 0x03, 0x3d, 0xbe, 0xf5, 0xee, 0x0c, 0x6a, 0x0a, 0x93, 0x47, 0xd5, 0x5a, 0xe6, 0xc2, 0xb4, + 0x07, 0x9e, 0xef, 0x44, 0x22, 0xa8, 0x54, 0xf4, 0x41, 0xda, 0x6d, 0xb1, 0x48, 0x3b, 0x14, 0xb5, + 0x06, 0x73, 0x84, 0x2f, 0x12, 0x51, 0x52, 0x8a, 0x5a, 0x59, 0xfb, 0x96, 0xe1, 0xb9, 0xba, 0x1e, + 0x70, 0xe8, 0x15, 0x8e, 0x74, 0xc3, 0xa8, 0x54, 0x26, 0xa1, 0xe7, 0x94, 0xea, 0x65, 0xce, 0x03, + 0x2c, 0x32, 0xe4, 0xec, 0x82, 0xac, 0xb9, 0x3a, 0xa7, 0x80, 0xe6, 0x24, 0xfc, 0x58, 0x40, 0xe8, + 0x12, 0xfa, 0xc2, 0x52, 0xf0, 0x8c, 0x73, 0x0a, 0x38, 0xd9, 0x00, 0xa6, 0x47, 0xbc, 0xa0, 0x44, + 0x4e, 0xb6, 0xc4, 0xc0, 0x26, 0xd0, 0x00, 0x46, 0x6a, 0x43, 0x40, 0xa5, 0x35, 0xb3, 0x00, 0xab, + 0x00, 0x02, 0xe5, 0xd7, 0x09, 0x7e, 0xa2, 0xa4, 0x07, 0x40, 0xcd, 0x98, 0x6e, 0x0b, 0x08, 0x38, + 0x46, 0xc4, 0xcb, 0x0d, 0xc3, 0x60, 0x51, 0xa5, 0x12, 0xb6, 0xad, 0x5b, 0x2b, 0x9d, 0xe6, 0x5e, + 0xe3, 0xae, 0xca, 0x83, 0xa6, 0xc5, 0x39, 0x1f, 0xb7, 0x2d, 0x7e, 0x1b, 0xdc, 0x35, 0xad, 0x74, + 0xce, 0x06, 0x0b, 0x79, 0x44, 0x14, 0xf4, 0x79, 0x20, 0xa6, 0x25, 0xbb, 0x95, 0xea, 0x22, 0xdf, + 0x0c, 0x40, 0x03, 0x5d, 0xe0, 0x4f, 0x33, 0x27, 0xcc, 0xcf, 0x51, 0xc5, 0x2d, 0x78, 0xb0, 0x12, + 0x18, 0x1a, 0x44, 0x5d, 0xc4, 0x5c, 0xce, 0x58, 0xb4, 0x25, 0x9a, 0x9a, 0x02, 0x6a, 0x1f, 0xc5, + 0x7c, 0xa3, 0x18, 0x0b, 0x99, 0xbf, 0xcc, 0xf9, 0xe0, 0x5c, 0xf2, 0x01, 0x62, 0x93, 0x78, 0x01, + 0x25, 0x59, 0x18, 0xc9, 0x6d, 0x78, 0xc7, 0x05, 0xfc, 0xc9, 0x26, 0x91, 0x14, 0x3d, 0x7c, 0xd9, + 0x43, 0xd2, 0x3a, 0x81, 0xa5, 0x6e, 0xe1, 0xe6, 0xa4, 0x9d, 0x48, 0xbe, 0xb5, 0xc7, 0x11, 0x7e, + 0x99, 0xc5, 0x85, 0xb1, 0x44, 0xf0, 0x82, 0x6f, 0x69, 0xfc, 0x29, 0x0a, 0x87, 0x5e, 0x2c, 0xda, + 0xe9, 0xd5, 0x04, 0x3d, 0x1b, 0xfa, 0x13, 0xe0, 0x76, 0x13, 0x74, 0x4e, 0x60, 0xf6, 0xbc, 0xc0, + 0xd1, 0x37, 0xea, 0x8c, 0x26, 0x28, 0x0b, 0x8c, 0xcf, 0xc3, 0x71, 0xc2, 0x5c, 0x5e, 0xc3, 0x37, + 0xde, 0x16, 0x62, 0xa6, 0xb7, 0x9b, 0xf1, 0xa2, 0xbf, 0x08, 0x16, 0xa3, 0xc5, 0x2f, 0xc6, 0x22, + 0x1a, 0x0d, 0x16, 0xe1, 0x34, 0x5e, 0x0c, 0x03, 0x7b, 0x11, 0x24, 0xd3, 0x05, 0x88, 0xd3, 0xad, + 0x3d, 0xb8, 0x5b, 0x3c, 0x85, 0xe1, 0xe2, 0xcf, 0x30, 0x72, 0x6a, 0x1e, 0x20, 0x5d, 0x95, 0x8a, + 0x58, 0x4f, 0x8c, 0xe7, 0x72, 0x62, 0xde, 0x3b, 0x5e, 0x94, 0xcc, 0x61, 0x6d, 0xd9, 0x2d, 0x2f, + 0xd7, 0x81, 0x2d, 0x1a, 0x9c, 0x87, 0x92, 0x5c, 0xc8, 0x24, 0x02, 0x88, 0x56, 0xa0, 0x05, 0xc5, + 0x2d, 0xc3, 0x62, 0xd2, 0x4a, 0xb0, 0x21, 0x32, 0x6e, 0xcb, 0x28, 0xa0, 0x9d, 0x42, 0xb7, 0xa2, + 0xc3, 0xb5, 0xc4, 0x63, 0x86, 0x60, 0x53, 0x9e, 0x93, 0xe8, 0x3d, 0x09, 0xe7, 0x22, 0xa3, 0x37, + 0x10, 0x6e, 0xb1, 0x48, 0x72, 0xfa, 0x9b, 0x49, 0x78, 0x86, 0x2f, 0xb7, 0x75, 0x21, 0x40, 0xd2, + 0x0d, 0xac, 0x5d, 0x2d, 0x29, 0x60, 0x1f, 0xe3, 0x3a, 0x24, 0xc2, 0xcf, 0x75, 0x50, 0x00, 0x89, + 0xc2, 0x29, 0x20, 0x19, 0x05, 0x13, 0x25, 0xf9, 0x2d, 0xb1, 0x5e, 0xc8, 0x95, 0xd1, 0xd2, 0xcf, + 0x6c, 0x00, 0xf5, 0xa3, 0xb8, 0x05, 0xd2, 0x98, 0x0b, 0x60, 0x68, 0x64, 0x0b, 0x45, 0x97, 0xa4, + 0x14, 0x1a, 0xb2, 0x06, 0x27, 0x04, 0x1c, 0x0d, 0x58, 0xc3, 0x0b, 0x0f, 0xe1, 0x8f, 0x91, 0xf1, + 0x8f, 0x28, 0xe6, 0x76, 0x51, 0xcc, 0x2d, 0x31, 0xe5, 0x87, 0x7b, 0xb8, 0xe0, 0x16, 0xe8, 0x0f, + 0xa4, 0xef, 0x10, 0x4c, 0x45, 0x17, 0xe7, 0xb4, 0x82, 0xac, 0x09, 0x22, 0x8b, 0x91, 0xa6, 0x32, + 0x9e, 0x61, 0x2e, 0x1a, 0x99, 0x11, 0x9c, 0xa6, 0x86, 0xc3, 0xe2, 0x98, 0x5c, 0x96, 0x69, 0x06, + 0xd3, 0x80, 0xc7, 0xa9, 0x58, 0x2a, 0x81, 0x12, 0xb6, 0x8f, 0x84, 0x2b, 0x8b, 0x7c, 0x1d, 0xa0, + 0x90, 0xf8, 0xfb, 0x7a, 0xc0, 0x12, 0xa5, 0x89, 0xec, 0x5e, 0x26, 0xa4, 0x47, 0xa8, 0x7e, 0x34, + 0xda, 0xe2, 0x96, 0xdd, 0x70, 0x50, 0x90, 0xeb, 0x6d, 0xb2, 0xbe, 0x51, 0x16, 0x2e, 0x16, 0xc0, + 0x38, 0xd4, 0xd9, 0xb4, 0xe3, 0xf8, 0x46, 0xcc, 0x12, 0x1e, 0x2c, 0x16, 0x1a, 0x4c, 0x0d, 0x9b, + 0xaf, 0x1b, 0x91, 0x40, 0xae, 0x68, 0x03, 0x4a, 0x8e, 0x65, 0x4f, 0x62, 0x99, 0x2e, 0x41, 0x01, + 0x1b, 0xb4, 0x1c, 0x68, 0x27, 0xa9, 0x7c, 0xf3, 0x66, 0x81, 0xa1, 0xd6, 0x6e, 0x28, 0x2e, 0x28, + 0xad, 0x54, 0xca, 0xc0, 0xc7, 0xdc, 0x35, 0x81, 0x17, 0x12, 0xdd, 0x33, 0xda, 0x58, 0x58, 0xd5, + 0x46, 0x33, 0xad, 0x89, 0x77, 0xcb, 0x65, 0x8e, 0x10, 0x07, 0x33, 0x80, 0x51, 0x38, 0x8e, 0xfd, + 0xf9, 0x35, 0x6e, 0xc7, 0x81, 0x73, 0xf3, 0xf1, 0xe6, 0xfc, 0x4c, 0x22, 0x24, 0x20, 0xe9, 0xf0, + 0xb2, 0x42, 0xd0, 0xc4, 0xf7, 0xf7, 0x78, 0x08, 0x2c, 0xad, 0xb3, 0x40, 0x6a, 0xa8, 0x21, 0x57, + 0xd4, 0x61, 0x54, 0x2d, 0xa0, 0xdb, 0xc6, 0x9d, 0xa4, 0x3e, 0x58, 0x42, 0x40, 0x35, 0x90, 0x0d, + 0x89, 0x3e, 0xf2, 0x2d, 0x5b, 0xe8, 0xb5, 0xae, 0x35, 0x02, 0x6e, 0x11, 0xbf, 0xd4, 0x18, 0xc0, + 0x00, 0x20, 0xeb, 0x0c, 0x6e, 0xc6, 0xe3, 0x9e, 0x44, 0x13, 0xb8, 0x3e, 0x2c, 0x68, 0x87, 0x28, + 0x1d, 0x96, 0xe3, 0x1c, 0xe3, 0xc1, 0x23, 0x67, 0x5e, 0x9c, 0x08, 0x98, 0x8d, 0x2e, 0x98, 0xc3, + 0x62, 0x54, 0x3e, 0x92, 0x99, 0xb6, 0x55, 0x32, 0x14, 0x6c, 0x3f, 0x2d, 0x8b, 0x09, 0xa5, 0xc5, + 0x23, 0x58, 0x4c, 0xc3, 0xb8, 0x15, 0x77, 0x3c, 0x28, 0x50, 0x41, 0x9b, 0x6c, 0x65, 0xe2, 0x34, + 0x0d, 0x71, 0x99, 0xde, 0x97, 0x41, 0xbf, 0x0b, 0xf2, 0x03, 0x8d, 0xe7, 0x24, 0x9a, 0x3f, 0x27, + 0xd4, 0x2d, 0xb5, 0x1d, 0x60, 0x39, 0x82, 0xa5, 0x6d, 0x25, 0x36, 0xaa, 0x8b, 0xe7, 0xa5, 0x54, + 0x98, 0x01, 0xa2, 0x1f, 0x8d, 0x18, 0xe0, 0x29, 0x1e, 0x09, 0xdf, 0xa7, 0x24, 0xaa, 0x96, 0x4a, + 0xba, 0x9c, 0x71, 0x27, 0x93, 0x55, 0xd0, 0xa1, 0x34, 0x05, 0x42, 0x98, 0xc5, 0x71, 0xb0, 0x0d, + 0x9c, 0xfd, 0x39, 0xc3, 0x1c, 0x51, 0xb3, 0x9d, 0xe2, 0x2c, 0x1d, 0x7c, 0xb1, 0x20, 0x22, 0x07, + 0x6d, 0xab, 0xbd, 0x01, 0xf5, 0xe2, 0x5a, 0xd7, 0x36, 0xbf, 0x3a, 0x6e, 0xbc, 0x7b, 0xf7, 0xae, + 0x46, 0xb0, 0x34, 0xb6, 0x86, 0xf6, 0x02, 0x97, 0xea, 0xcc, 0x9a, 0x5b, 0x6c, 0x27, 0x32, 0x05, + 0x8e, 0x08, 0xba, 0xf9, 0xe7, 0x87, 0x63, 0x01, 0x0e, 0xa8, 0x82, 0x00, 0xaa, 0x05, 0x86, 0x44, + 0x05, 0x28, 0xca, 0x5c, 0xf4, 0xa5, 0x50, 0x15, 0x4a, 0x02, 0x55, 0x46, 0xae, 0x4f, 0xc1, 0x4f, + 0x52, 0x68, 0x7a, 0x9b, 0x98, 0x38, 0xc5, 0x3b, 0x54, 0x2a, 0x88, 0xcc, 0x8f, 0xe8, 0x11, 0xdd, + 0xf0, 0x3a, 0xeb, 0x83, 0x9d, 0x66, 0xf7, 0xf0, 0xa7, 0x30, 0x02, 0x9f, 0xd6, 0x34, 0xf9, 0x47, + 0x33, 0x1e, 0x78, 0x6e, 0x22, 0x75, 0x39, 0x38, 0x8a, 0xa3, 0x30, 0x00, 0x8e, 0x3a, 0xf2, 0x9c, + 0xf3, 0x10, 0x7c, 0xee, 0x4a, 0x65, 0x4b, 0xa1, 0xaa, 0x8b, 0x3b, 0x85, 0xea, 0x62, 0x9e, 0xf1, + 0x7c, 0x53, 0xad, 0x02, 0xbf, 0xf5, 0xb9, 0x64, 0x87, 0x48, 0xf1, 0x6b, 0x22, 0x33, 0x9c, 0xc2, + 0x5c, 0xaf, 0xbf, 0x9e, 0x1c, 0xfb, 0x02, 0x5d, 0x2e, 0x98, 0x57, 0x6e, 0x65, 0xcb, 0xba, 0x76, + 0x7f, 0x0f, 0x21, 0x88, 0x65, 0x27, 0xa8, 0xca, 0xef, 0x35, 0x62, 0x3a, 0xa3, 0x25, 0x45, 0xe9, + 0x4b, 0x3e, 0x86, 0x97, 0xab, 0x5c, 0x00, 0x1d, 0x2b, 0xfa, 0x15, 0x07, 0xa8, 0x54, 0x22, 0x13, + 0xdf, 0xe6, 0x0e, 0x1c, 0xa9, 0x61, 0x41, 0x10, 0xf6, 0xf6, 0x6e, 0x60, 0x3a, 0x88, 0x00, 0xe6, + 0x2d, 0x16, 0xb0, 0x74, 0x83, 0xc5, 0xc5, 0xdc, 0xbf, 0xa8, 0x6a, 0x97, 0x74, 0x0c, 0xf8, 0x20, + 0x31, 0xef, 0xa3, 0x39, 0x48, 0x9d, 0x3d, 0x10, 0x88, 0xcc, 0xc9, 0xca, 0xd9, 0x00, 0x78, 0x19, + 0xf5, 0x31, 0xaa, 0xe2, 0x54, 0xab, 0x71, 0xa5, 0x6a, 0x43, 0x2b, 0x09, 0x23, 0x23, 0x9a, 0x82, + 0x0d, 0xe0, 0x80, 0x91, 0xef, 0x25, 0xa8, 0x3c, 0x11, 0xc5, 0xc5, 0x42, 0x80, 0xc5, 0xd0, 0x42, + 0xe7, 0x38, 0x47, 0x65, 0xdd, 0x96, 0x66, 0xeb, 0x2b, 0xa6, 0xb8, 0x69, 0x4e, 0xba, 0x52, 0x00, + 0xca, 0xaa, 0x09, 0xae, 0xa2, 0x13, 0xda, 0xe4, 0xc8, 0x9a, 0x36, 0x60, 0x31, 0x11, 0x08, 0x18, + 0xc1, 0x01, 0x27, 0xb3, 0x84, 0x3a, 0xa8, 0x43, 0xa8, 0x4f, 0x99, 0xac, 0x49, 0x9c, 0x79, 0x60, + 0x33, 0xd8, 0x25, 0xe0, 0x05, 0x3c, 0x02, 0x40, 0x96, 0x67, 0xae, 0x90, 0x05, 0xfc, 0x04, 0xe6, + 0x11, 0x51, 0xc0, 0x3d, 0x63, 0x36, 0x08, 0x6a, 0x66, 0x4e, 0x11, 0x65, 0x5b, 0x9c, 0x1f, 0x3b, + 0x5b, 0xbb, 0x1a, 0x69, 0x20, 0xc6, 0x25, 0xbe, 0x23, 0x4e, 0xab, 0x2f, 0x56, 0x0b, 0x0e, 0x7d, + 0x04, 0x14, 0x48, 0x18, 0xa9, 0x03, 0xb5, 0xa6, 0x0b, 0x01, 0x55, 0x12, 0x8d, 0xed, 0x24, 0x8c, + 0xc8, 0x35, 0xc8, 0x06, 0x66, 0x3e, 0xb8, 0x8a, 0x36, 0x3f, 0x86, 0x85, 0x92, 0xa9, 0x68, 0x41, + 0xbf, 0xb2, 0x4f, 0x2e, 0x6a, 0x64, 0xde, 0xcb, 0x65, 0x76, 0x33, 0x20, 0xc0, 0xe3, 0x3e, 0x14, + 0xdb, 0x3b, 0x60, 0x61, 0x5f, 0xec, 0x5a, 0x06, 0xc9, 0x8b, 0x94, 0xb1, 0x8d, 0xb6, 0xfe, 0xa0, + 0x47, 0xcc, 0x66, 0x07, 0x34, 0x75, 0x96, 0x00, 0x0c, 0x4c, 0xe3, 0x22, 0xe2, 0x61, 0x34, 0x0b, + 0xba, 0x9c, 0x81, 0xa9, 0x81, 0xf2, 0x38, 0xf5, 0xbd, 0x23, 0x7e, 0xa5, 0x2b, 0x73, 0xb4, 0x59, + 0x48, 0x64, 0x28, 0x47, 0x66, 0x00, 0x84, 0x39, 0x84, 0xbe, 0x38, 0xc3, 0xfc, 0x81, 0xd8, 0x4e, + 0xf6, 0x94, 0x03, 0x35, 0x56, 0x07, 0x62, 0x10, 0x83, 0x25, 0xc0, 0x35, 0x70, 0xd1, 0x63, 0x65, + 0x62, 0xd4, 0x07, 0xc8, 0x15, 0x33, 0x74, 0xc7, 0x8d, 0xc2, 0x9d, 0xcd, 0xd1, 0x8c, 0x74, 0xe9, + 0x43, 0xe8, 0x39, 0x41, 0x26, 0xe5, 0x36, 0xb0, 0x27, 0x20, 0x49, 0x78, 0xfd, 0xe0, 0x52, 0x9a, + 0x69, 0x00, 0x6a, 0x57, 0x2a, 0x7d, 0xc0, 0x60, 0xea, 0xf1, 0xdb, 0x60, 0x3d, 0xca, 0xc0, 0x75, + 0x65, 0x74, 0xe0, 0x6c, 0x8c, 0x4c, 0x74, 0x8b, 0xdb, 0x4c, 0xf7, 0x79, 0xbf, 0xbd, 0xc6, 0x63, + 0xa9, 0x1c, 0x6f, 0xd7, 0x80, 0xd9, 0x31, 0x0f, 0x1a, 0xb3, 0x8c, 0xe6, 0xf6, 0x8e, 0xba, 0x65, + 0x18, 0xdb, 0xfc, 0x42, 0x0b, 0xb8, 0xc0, 0x07, 0x3e, 0x94, 0x7a, 0xaa, 0x95, 0x98, 0x2e, 0xee, + 0xb1, 0x11, 0x7f, 0x42, 0xdc, 0xb4, 0x22, 0xe2, 0x6a, 0x9d, 0xd1, 0xfa, 0x19, 0x26, 0x27, 0x7d, + 0x39, 0xe0, 0x9e, 0x02, 0x08, 0x62, 0xc9, 0x35, 0xbe, 0x67, 0x23, 0x25, 0xec, 0xcb, 0xb4, 0x03, + 0x04, 0x39, 0xc6, 0xf3, 0x46, 0x53, 0x30, 0xaf, 0xb9, 0xcb, 0xe2, 0x42, 0x65, 0xe1, 0x99, 0x32, + 0x07, 0xbc, 0x92, 0x34, 0x9e, 0x73, 0x30, 0x9e, 0x1b, 0xdf, 0xba, 0xb7, 0xce, 0x9d, 0x89, 0x7b, + 0x0e, 0x77, 0x9c, 0x6e, 0x69, 0x13, 0x6b, 0x29, 0xa9, 0x58, 0xbe, 0xaf, 0x54, 0x46, 0xe4, 0x94, + 0xf3, 0x51, 0xda, 0xad, 0x70, 0xc6, 0x72, 0x01, 0x1b, 0x91, 0xf3, 0x21, 0x95, 0xd5, 0x40, 0xd1, + 0x2f, 0x03, 0x55, 0xbf, 0xc8, 0xd9, 0x0e, 0x88, 0xdd, 0xae, 0x3d, 0xdc, 0x75, 0xe9, 0xb7, 0x07, + 0xaa, 0x4a, 0x91, 0x50, 0x74, 0xa5, 0x8c, 0x8a, 0x80, 0xbf, 0x61, 0x06, 0xa3, 0x3c, 0xc6, 0x4d, + 0x87, 0x01, 0x9e, 0x58, 0x17, 0xe7, 0x5c, 0x81, 0x32, 0x08, 0xae, 0x51, 0x35, 0x40, 0x50, 0x9e, + 0x3a, 0xdf, 0x88, 0xfe, 0x98, 0xc2, 0x73, 0xc0, 0x22, 0x38, 0xeb, 0x2e, 0x98, 0x25, 0x07, 0xfe, + 0x4f, 0x78, 0x16, 0x0c, 0x83, 0x95, 0xaa, 0x83, 0x31, 0x10, 0x6d, 0x91, 0x16, 0x34, 0xeb, 0x88, + 0x63, 0x5c, 0xc6, 0x24, 0xf7, 0x12, 0x87, 0xbc, 0xde, 0x1a, 0xbe, 0x9f, 0xb4, 0x86, 0xd5, 0xaa, + 0x1c, 0x6a, 0xce, 0x07, 0xb7, 0xc3, 0x3b, 0xd6, 0xe3, 0xf3, 0x35, 0x5a, 0x4d, 0x39, 0x60, 0xae, + 0xd7, 0x9e, 0x2b, 0x72, 0xb2, 0xf2, 0x00, 0xcd, 0xc1, 0x83, 0x6e, 0xf6, 0x30, 0x56, 0x6c, 0xe2, + 0x92, 0x5a, 0x72, 0x5d, 0xd3, 0xb6, 0xee, 0x56, 0xab, 0x6c, 0x74, 0x3b, 0xbd, 0xe3, 0x73, 0x58, + 0x7a, 0x0f, 0xd4, 0x7c, 0x8e, 0xcf, 0x79, 0x81, 0xcf, 0x76, 0x39, 0x5a, 0x2c, 0xe6, 0x05, 0xaa, + 0x4c, 0x20, 0xc9, 0x50, 0x37, 0x9a, 0x11, 0x09, 0xcb, 0xf8, 0xb6, 0x5f, 0xad, 0x22, 0x80, 0x65, + 0xba, 0x84, 0xfb, 0xb5, 0x25, 0xdc, 0xcb, 0x25, 0xf8, 0xe0, 0x2c, 0xc2, 0xf4, 0x6d, 0x12, 0x61, + 0x52, 0xa9, 0x53, 0x4e, 0x31, 0x6d, 0x61, 0x7c, 0xa6, 0x86, 0xab, 0x50, 0x14, 0xe7, 0x05, 0xf0, + 0x6d, 0xba, 0x91, 0xb3, 0x94, 0x75, 0xcc, 0xdd, 0xdb, 0x2b, 0x5c, 0x52, 0xe7, 0x7d, 0x3f, 0x4d, + 0x1e, 0x38, 0x2d, 0xef, 0x3d, 0x98, 0x32, 0x18, 0x4c, 0x8d, 0x6e, 0xc6, 0xe4, 0x23, 0xeb, 0x33, + 0x50, 0x55, 0x78, 0xcb, 0xba, 0xa0, 0x70, 0xd7, 0xb9, 0x4a, 0x7f, 0xe4, 0xbe, 0xb1, 0xc5, 0x92, + 0x3d, 0xb6, 0x73, 0x30, 0xb3, 0x02, 0x1f, 0xcd, 0x0d, 0xa6, 0x7c, 0xcc, 0x35, 0x5f, 0xbb, 0x3c, + 0xdb, 0xae, 0xc3, 0x2b, 0x95, 0x6b, 0x7d, 0xc6, 0x8a, 0x86, 0x46, 0xb3, 0xbb, 0x58, 0xec, 0x68, + 0x0b, 0x12, 0xa0, 0x34, 0x34, 0x9e, 0x6d, 0x0e, 0xfc, 0x84, 0x01, 0x40, 0xba, 0x7e, 0x0f, 0x1a, + 0xf4, 0xf7, 0x1a, 0xa0, 0xc6, 0xf6, 0xf6, 0xe8, 0xc1, 0xa9, 0x54, 0x9c, 0x6a, 0xb5, 0xd5, 0x03, + 0xa6, 0x78, 0x5c, 0xda, 0xe0, 0x3e, 0xd8, 0xc0, 0x92, 0xa4, 0x50, 0x2d, 0xc9, 0x34, 0xa0, 0xf2, + 0x6c, 0x34, 0xc2, 0xf2, 0x62, 0xe5, 0x19, 0x11, 0x74, 0xee, 0x54, 0xf5, 0x62, 0x1b, 0x4d, 0x1b, + 0xe0, 0x59, 0x2b, 0x92, 0x73, 0x01, 0x7a, 0x0b, 0x73, 0x56, 0x41, 0x2c, 0xa2, 0xe4, 0x50, 0xa0, + 0x3e, 0x85, 0x01, 0x40, 0x97, 0x91, 0x2a, 0x81, 0x45, 0xb1, 0x2e, 0xd2, 0xd0, 0x2d, 0xe8, 0x8e, + 0x2e, 0xf4, 0xc8, 0x50, 0x28, 0x39, 0x04, 0x12, 0x5c, 0xea, 0x78, 0x45, 0xc5, 0x2d, 0xed, 0x96, + 0xf3, 0x1e, 0x1c, 0x8f, 0xa2, 0x11, 0x10, 0x1a, 0x58, 0x69, 0x6f, 0xef, 0xce, 0xc0, 0xa6, 0x36, + 0xb6, 0x5b, 0x82, 0xe2, 0x1a, 0x91, 0xb4, 0xdd, 0x67, 0xb2, 0x38, 0x36, 0x77, 0x44, 0x31, 0x06, + 0x5b, 0x15, 0x51, 0x29, 0x33, 0x01, 0x0d, 0x95, 0x86, 0xb5, 0x45, 0x46, 0x0b, 0x42, 0x59, 0x09, + 0x90, 0x53, 0x58, 0xbb, 0x58, 0x60, 0x3c, 0x1a, 0x30, 0xbc, 0xa7, 0x3f, 0x19, 0xa2, 0xfb, 0x46, + 0xd1, 0x5f, 0x18, 0x5a, 0xa6, 0x10, 0xd1, 0xb2, 0xa0, 0xaf, 0xea, 0xa9, 0x21, 0x14, 0x14, 0xc8, + 0x71, 0xc0, 0xab, 0x47, 0x10, 0xb0, 0x22, 0x8d, 0xd4, 0x5b, 0xd6, 0x3a, 0xfd, 0x16, 0x99, 0x1e, + 0xdb, 0x09, 0xe6, 0x8a, 0x28, 0xa4, 0xde, 0x1c, 0x1c, 0xbb, 0xc3, 0xd0, 0xb8, 0x7a, 0xa1, 0xaa, + 0xd3, 0xb1, 0x01, 0x8a, 0x23, 0x66, 0x5e, 0xe1, 0xcf, 0x5d, 0x2a, 0x59, 0x3f, 0xae, 0x3a, 0x0f, + 0xad, 0xb0, 0x7d, 0xa6, 0x87, 0x20, 0xd3, 0xa9, 0xc3, 0xb9, 0xaa, 0x31, 0xc0, 0xe8, 0xeb, 0x6b, + 0x45, 0x60, 0x2f, 0xdc, 0x34, 0x8e, 0xa6, 0x78, 0x26, 0xc7, 0xd5, 0x5a, 0xbb, 0xc5, 0x02, 0xe3, + 0x7d, 0x16, 0xc1, 0x1f, 0xc5, 0x27, 0xc6, 0x47, 0xb2, 0x5d, 0x09, 0xb4, 0x07, 0x77, 0x3e, 0xb5, + 0x5d, 0x49, 0x4b, 0xc9, 0x0c, 0x44, 0x62, 0xe2, 0x01, 0xd5, 0x52, 0xa6, 0x6a, 0xa5, 0xd6, 0x08, + 0x2c, 0xbe, 0x58, 0x2e, 0x65, 0x3c, 0xa4, 0x66, 0x6e, 0xae, 0x56, 0xa8, 0x08, 0xee, 0x45, 0x96, + 0x72, 0x23, 0x82, 0x20, 0xb8, 0x30, 0x09, 0x51, 0xfa, 0xc8, 0xec, 0x65, 0x0f, 0xb0, 0x0a, 0x4c, + 0x08, 0xb7, 0x31, 0x25, 0x28, 0xa6, 0xa5, 0x44, 0x47, 0x10, 0x6c, 0x6e, 0xe2, 0xcf, 0xad, 0xe8, + 0x01, 0x01, 0x04, 0x9c, 0xa4, 0xb5, 0x73, 0xaa, 0x35, 0x56, 0x9c, 0x23, 0x20, 0x44, 0x0a, 0x83, + 0x0f, 0xc1, 0xbb, 0x40, 0xa3, 0x05, 0x9c, 0x6d, 0xdd, 0x46, 0x77, 0x6b, 0x3e, 0x54, 0x92, 0xf9, + 0x79, 0x41, 0xe1, 0xd3, 0x50, 0xb3, 0xec, 0x89, 0x59, 0xa4, 0x30, 0x20, 0xa4, 0x8b, 0x58, 0x03, + 0x62, 0xa0, 0xcc, 0x55, 0x09, 0x0a, 0xac, 0x0d, 0xb3, 0x35, 0xaa, 0x31, 0x8e, 0x32, 0x0c, 0x54, + 0x87, 0x0a, 0x92, 0x1f, 0x54, 0xdb, 0x43, 0x59, 0xa8, 0x18, 0x0f, 0xa3, 0x94, 0xd1, 0x6e, 0xfa, + 0x80, 0xfe, 0x2a, 0x52, 0x0c, 0xa8, 0x49, 0x41, 0xa5, 0xcb, 0x52, 0xed, 0xcf, 0x65, 0xa6, 0x30, + 0xcf, 0xd0, 0x62, 0x55, 0xfe, 0x80, 0x35, 0x79, 0xce, 0x27, 0x51, 0xa7, 0x60, 0xf6, 0x45, 0x72, + 0x24, 0x22, 0x6f, 0x22, 0x9c, 0x6b, 0xfc, 0x84, 0xfc, 0x43, 0x14, 0x0e, 0x29, 0x81, 0x24, 0xbd, + 0x76, 0x74, 0xd5, 0xc0, 0x73, 0x6c, 0x2b, 0x21, 0xd3, 0x37, 0xcf, 0xf7, 0x37, 0x03, 0xa9, 0xbc, + 0x54, 0xa7, 0x2c, 0xa2, 0x5a, 0x7c, 0x25, 0x6c, 0x01, 0xf0, 0x53, 0xa8, 0x3f, 0xa8, 0x94, 0x51, + 0x23, 0xa6, 0x5c, 0x82, 0x72, 0x3a, 0xcd, 0x84, 0xac, 0xbd, 0x2e, 0xb9, 0xab, 0x2b, 0x9f, 0x09, + 0x1f, 0xca, 0x73, 0xd1, 0x12, 0x98, 0x2d, 0xbb, 0xe5, 0xe8, 0x6b, 0x52, 0x2b, 0x02, 0x9d, 0xf7, + 0xa1, 0x27, 0xe2, 0x56, 0xb8, 0xca, 0x26, 0x58, 0x20, 0x98, 0x8a, 0xe2, 0x06, 0xa3, 0xac, 0x19, + 0x0c, 0xdc, 0x90, 0x57, 0x8a, 0xcd, 0x3d, 0x33, 0x9e, 0x07, 0x76, 0xee, 0x79, 0x7f, 0xa1, 0xf7, + 0x9a, 0x69, 0x45, 0x88, 0xa5, 0x36, 0xa6, 0x22, 0x9b, 0xa7, 0x40, 0xc2, 0x06, 0x90, 0x0f, 0x93, + 0x55, 0x29, 0x99, 0xd8, 0x8a, 0x20, 0x9d, 0x16, 0xe1, 0x02, 0xf0, 0x5d, 0xb9, 0x18, 0x35, 0x0d, + 0x1f, 0x18, 0xf9, 0x1b, 0xd9, 0x04, 0x21, 0x14, 0xc0, 0xec, 0x10, 0x8c, 0x03, 0x66, 0x35, 0x5f, + 0x1b, 0x79, 0x22, 0xca, 0xd2, 0x2c, 0xf0, 0x45, 0x64, 0x01, 0x91, 0x70, 0xb1, 0xf0, 0xc1, 0x2f, + 0x59, 0x43, 0x99, 0x0d, 0x5e, 0x8a, 0x9c, 0x28, 0xb8, 0x2a, 0x49, 0xc1, 0xc4, 0x13, 0xee, 0x2e, + 0x16, 0x0e, 0xa8, 0x9d, 0x95, 0x98, 0x85, 0xc2, 0xcb, 0x21, 0x1f, 0xa1, 0xd6, 0xff, 0xc7, 0xec, + 0xe2, 0xf3, 0x73, 0x9d, 0x92, 0x97, 0x7e, 0x4a, 0x87, 0xbf, 0xed, 0xa4, 0x83, 0x63, 0x65, 0x60, + 0x63, 0x5a, 0x22, 0x58, 0x68, 0xe6, 0xa6, 0xc4, 0x46, 0x9a, 0x0c, 0xf2, 0x8a, 0xb1, 0x42, 0xd7, + 0x11, 0xdb, 0x97, 0xda, 0x8b, 0xce, 0xb9, 0x1e, 0xfb, 0xce, 0x1a, 0x41, 0x52, 0x62, 0xed, 0xa8, + 0xd5, 0xc9, 0x95, 0x33, 0xda, 0x18, 0xf7, 0xad, 0xf3, 0x69, 0xd6, 0x7f, 0x6b, 0x71, 0xda, 0x31, + 0x67, 0x18, 0xab, 0x98, 0xb5, 0x32, 0x39, 0x7b, 0x85, 0xe9, 0x54, 0xa2, 0xf0, 0x75, 0x96, 0xcd, + 0x95, 0x0a, 0x05, 0x3c, 0x45, 0xda, 0xba, 0xc1, 0xca, 0xf7, 0xa0, 0x30, 0x78, 0x92, 0xaa, 0xa9, + 0x62, 0x60, 0xc0, 0x22, 0x69, 0xdd, 0x6e, 0x2e, 0x16, 0x76, 0x86, 0xf1, 0x6d, 0xf5, 0x3a, 0x46, + 0xbb, 0x2e, 0x2e, 0x07, 0x2a, 0xae, 0x03, 0x6b, 0x04, 0x08, 0x49, 0x8d, 0x7b, 0xb6, 0x52, 0x7d, + 0xc8, 0x77, 0xd6, 0xea, 0x03, 0x30, 0x47, 0x32, 0x5d, 0x31, 0x67, 0x3d, 0x70, 0x41, 0x29, 0x19, + 0xf1, 0x37, 0xc1, 0xf1, 0x54, 0x72, 0xf1, 0x0c, 0x82, 0xd7, 0xc8, 0x68, 0x81, 0xc7, 0xd6, 0x07, + 0x5b, 0xef, 0xad, 0x69, 0xd6, 0x69, 0xa5, 0x32, 0xa3, 0xad, 0x0e, 0x0a, 0x34, 0xe0, 0xa6, 0xfd, + 0x00, 0x86, 0x70, 0x06, 0x72, 0x43, 0x3e, 0x41, 0x53, 0x9f, 0x73, 0x8f, 0xa9, 0xec, 0xc8, 0x3d, + 0x88, 0x3d, 0xa7, 0xd0, 0xc2, 0xc6, 0xf0, 0x3d, 0x47, 0x5c, 0x71, 0x8b, 0x1c, 0xec, 0x6d, 0x84, + 0xc6, 0xa0, 0xeb, 0x25, 0xe0, 0xba, 0x04, 0xcc, 0x4e, 0xe1, 0x09, 0xc3, 0x50, 0x4c, 0x04, 0x80, + 0x97, 0xed, 0x91, 0x38, 0xc8, 0x24, 0x55, 0xcc, 0x27, 0x0c, 0xc6, 0xc5, 0xe9, 0xea, 0xf1, 0x8a, + 0x2c, 0x64, 0x5b, 0x4f, 0x93, 0xc5, 0xa2, 0x41, 0x79, 0x54, 0x6c, 0xb1, 0x2d, 0x68, 0x45, 0x90, + 0x1d, 0x88, 0x5b, 0x31, 0xd8, 0x0d, 0x21, 0xda, 0x74, 0xd9, 0x04, 0x7c, 0x5d, 0x25, 0x62, 0xa3, + 0x71, 0x29, 0x2f, 0x0f, 0xce, 0x3c, 0x46, 0x02, 0x80, 0x1a, 0xb8, 0xf4, 0x25, 0xce, 0x1e, 0xb9, + 0xda, 0xb6, 0xf5, 0x28, 0xdb, 0xc0, 0x45, 0x7f, 0x5c, 0x0d, 0xf5, 0x7a, 0x6c, 0x02, 0x16, 0x0f, + 0x3d, 0xf9, 0x2d, 0x61, 0xf3, 0x44, 0x86, 0xcd, 0xe8, 0xa9, 0xcf, 0x2b, 0x95, 0x33, 0x7d, 0x4e, + 0x99, 0x53, 0x5c, 0x27, 0xef, 0xc1, 0xcc, 0xcb, 0x41, 0x91, 0x29, 0xeb, 0x02, 0x7e, 0xae, 0x79, + 0xd2, 0xba, 0xe6, 0xd7, 0x1b, 0xa8, 0x6b, 0x19, 0x7a, 0x97, 0x5f, 0x1b, 0x69, 0xc7, 0x56, 0x4f, + 0x1d, 0xa9, 0xcb, 0x7a, 0x3b, 0x9c, 0xda, 0xae, 0x4a, 0xe8, 0xe5, 0x12, 0x07, 0x2a, 0x83, 0x76, + 0x09, 0xdb, 0x1f, 0xb3, 0x3d, 0x98, 0xe6, 0x3d, 0x26, 0x4d, 0xd5, 0xec, 0xdb, 0x16, 0xa1, 0xcb, + 0x4b, 0x91, 0x05, 0xd9, 0x10, 0x83, 0xe0, 0x7b, 0x29, 0x0b, 0x5d, 0xb0, 0xf2, 0xf8, 0xf9, 0x5a, + 0xbe, 0xb5, 0x69, 0x6c, 0xa9, 0xa2, 0x5d, 0x1c, 0xe9, 0x10, 0x24, 0x46, 0xeb, 0x06, 0x9c, 0x36, + 0x4a, 0x93, 0x2d, 0x0b, 0x1d, 0x7c, 0xa6, 0x6e, 0x68, 0xe0, 0x12, 0x5b, 0xeb, 0xf6, 0x75, 0x55, + 0x01, 0x04, 0xc3, 0xad, 0xe6, 0x2e, 0x2d, 0xd7, 0x51, 0xf6, 0x7a, 0x99, 0x24, 0xb7, 0x76, 0xbb, + 0x6a, 0x22, 0x75, 0xca, 0x71, 0xc3, 0x75, 0xc3, 0x5b, 0x13, 0x3b, 0xbd, 0xb5, 0x0b, 0x5d, 0x51, + 0x16, 0x02, 0x1c, 0xfe, 0x0c, 0x99, 0xe0, 0xa3, 0x89, 0x15, 0x43, 0x43, 0xed, 0x8b, 0x65, 0xce, + 0xa5, 0xef, 0x28, 0x13, 0xab, 0xd9, 0xa6, 0x18, 0xcb, 0x7c, 0x10, 0x52, 0x43, 0x42, 0x3e, 0x4a, + 0x95, 0x96, 0xc8, 0x07, 0xa9, 0xd5, 0x8a, 0xdb, 0xc5, 0x02, 0xf7, 0xa3, 0x08, 0xc8, 0x1a, 0xaa, + 0xc1, 0x9f, 0x2b, 0x46, 0xeb, 0x29, 0x79, 0xb0, 0xcc, 0xd3, 0x90, 0x7b, 0xb7, 0xcf, 0xcb, 0x62, + 0xcb, 0x23, 0x82, 0x80, 0x31, 0x7a, 0x9f, 0x45, 0xc5, 0xad, 0x08, 0x42, 0xb9, 0x6d, 0x1b, 0x02, + 0xe0, 0x67, 0x11, 0xa8, 0x02, 0xfc, 0xf4, 0xe7, 0xc1, 0x6f, 0xdd, 0x55, 0xc8, 0x47, 0x38, 0xd7, + 0xe7, 0x85, 0x7b, 0xc9, 0x9e, 0xf1, 0xdb, 0x30, 0x5c, 0x75, 0x73, 0xf5, 0x45, 0x82, 0x14, 0x4d, + 0xb9, 0x85, 0xd5, 0x57, 0x0b, 0x14, 0x74, 0x19, 0x2a, 0x16, 0x53, 0xf5, 0xac, 0xd6, 0xee, 0xd8, + 0x7c, 0x2d, 0x9a, 0x28, 0x24, 0x01, 0x57, 0xca, 0x60, 0x28, 0x1c, 0xdb, 0xb0, 0x9f, 0xee, 0xce, + 0x1b, 0x2c, 0x96, 0x9f, 0x55, 0x2d, 0x19, 0x60, 0xc1, 0x4e, 0xb5, 0xb7, 0x32, 0x7d, 0x98, 0xfc, + 0x0f, 0x21, 0x24, 0xa8, 0x18, 0xe9, 0x2d, 0x85, 0x7d, 0x80, 0x21, 0x9b, 0x14, 0xdd, 0x8d, 0xe7, + 0xe5, 0x52, 0x5a, 0x81, 0x19, 0xbe, 0xff, 0x33, 0x8e, 0x85, 0x13, 0x4e, 0x03, 0x8d, 0x3d, 0xa6, + 0x4f, 0x88, 0x5a, 0x0d, 0x42, 0x6e, 0xf9, 0x34, 0x1e, 0x69, 0xec, 0x08, 0xdf, 0xb9, 0x1a, 0xdb, + 0x03, 0x7c, 0x7b, 0x24, 0xd1, 0xd8, 0xb7, 0xf4, 0x51, 0x36, 0x3c, 0x4c, 0x9f, 0x60, 0x14, 0x0d, + 0x22, 0x83, 0x7c, 0x18, 0xdc, 0xd1, 0xce, 0x48, 0x9d, 0xe0, 0x9c, 0x45, 0x2a, 0xc1, 0x38, 0xaf, + 0x24, 0xc5, 0xe9, 0xd8, 0x73, 0xb8, 0x7e, 0x6e, 0x25, 0x03, 0x33, 0xb2, 0x02, 0x27, 0x1c, 0xea, + 0x46, 0xb5, 0x61, 0x98, 0x49, 0x98, 0xe6, 0xf9, 0x0e, 0x5e, 0xab, 0xfb, 0x4d, 0xaf, 0x8c, 0x65, + 0xb6, 0x95, 0x29, 0xb7, 0x85, 0xef, 0x89, 0xca, 0xf7, 0xf7, 0x28, 0x43, 0xba, 0x1a, 0x60, 0x70, + 0x99, 0x38, 0x4c, 0x33, 0x79, 0x3a, 0x6d, 0x6b, 0xe6, 0x95, 0xeb, 0xe1, 0x83, 0xa1, 0xc4, 0x22, + 0x1b, 0xbb, 0x08, 0xea, 0x9b, 0x1c, 0x3d, 0x49, 0x52, 0xf2, 0xb8, 0x6e, 0x67, 0xec, 0xe8, 0x8e, + 0xd6, 0xc0, 0x9e, 0x47, 0x56, 0x1c, 0x83, 0x27, 0xd4, 0x2c, 0x37, 0x96, 0xc6, 0x92, 0x25, 0xdb, + 0xc0, 0x29, 0xca, 0x45, 0x85, 0x38, 0xdd, 0x0a, 0x71, 0x0d, 0xc8, 0x00, 0x30, 0xe3, 0x4b, 0x66, + 0xe7, 0x2b, 0x3c, 0x40, 0xcc, 0x8a, 0x73, 0x95, 0x9b, 0xc4, 0xba, 0x24, 0x2a, 0x6a, 0x40, 0x49, + 0x90, 0x18, 0x7d, 0x7c, 0xfa, 0x84, 0xc0, 0xb9, 0x91, 0x05, 0xb7, 0xf5, 0xbb, 0x26, 0x04, 0x28, + 0x98, 0x27, 0xf4, 0x3d, 0xe8, 0xf9, 0x3b, 0x0b, 0xf2, 0xfb, 0xef, 0x10, 0xb0, 0xe5, 0xb3, 0x41, + 0x17, 0xe2, 0x10, 0x3f, 0x28, 0x06, 0xbc, 0x77, 0xa9, 0x1a, 0xbc, 0x7a, 0x1c, 0x21, 0x9e, 0x7a, + 0xb4, 0x99, 0x46, 0x5b, 0x39, 0xc6, 0x33, 0xfe, 0x80, 0x5e, 0x69, 0xd6, 0xa4, 0xcb, 0x51, 0xb3, + 0xa7, 0x67, 0x39, 0x54, 0x76, 0xfb, 0xc8, 0xbe, 0xb1, 0x2e, 0x3b, 0xdc, 0x86, 0x23, 0x49, 0x7a, + 0xb9, 0xac, 0x53, 0x7c, 0xbd, 0x5e, 0x97, 0x3b, 0x33, 0xda, 0xf5, 0x4d, 0xe7, 0xea, 0x46, 0x33, + 0x64, 0x4e, 0xa4, 0x45, 0x40, 0x1f, 0x25, 0xec, 0x6f, 0xcd, 0x5d, 0x7d, 0xce, 0x2f, 0xbf, 0x1e, + 0xaf, 0x76, 0xe9, 0xca, 0x2e, 0x87, 0x3b, 0xbb, 0x1c, 0x5f, 0x1c, 0x69, 0x06, 0x9b, 0xfe, 0xb3, + 0xc9, 0x2e, 0x81, 0x18, 0x4b, 0x30, 0xba, 0x45, 0x98, 0xfb, 0xb5, 0xb0, 0x35, 0xe9, 0xb1, 0x27, + 0x81, 0x35, 0xf1, 0xfa, 0x16, 0xba, 0xc5, 0xf8, 0x33, 0x9e, 0x9d, 0x3e, 0xc2, 0x0c, 0x79, 0xed, + 0x4f, 0x5d, 0x6f, 0x97, 0xed, 0x01, 0xb8, 0xc6, 0x62, 0x01, 0xb3, 0x88, 0x40, 0xc9, 0x19, 0xa6, + 0xf1, 0x22, 0xb6, 0x5c, 0x2b, 0xf2, 0x6a, 0x9e, 0xdc, 0xfc, 0x05, 0xd6, 0x0d, 0x78, 0xcd, 0xfb, + 0x34, 0x00, 0x3e, 0x59, 0x78, 0x9f, 0x42, 0x07, 0xfe, 0x58, 0x8e, 0x52, 0x1b, 0xf1, 0xb5, 0xc3, + 0x55, 0xb2, 0xa8, 0x14, 0x3c, 0x92, 0xa0, 0x1d, 0x49, 0x46, 0xb1, 0x43, 0xbf, 0xaa, 0xd5, 0x6a, + 0x5a, 0x35, 0x32, 0x07, 0x61, 0x9c, 0xc0, 0x65, 0x04, 0x42, 0x85, 0x49, 0x5b, 0xb8, 0x8d, 0x85, + 0x15, 0xd9, 0x83, 0x6a, 0xd2, 0x54, 0xde, 0x20, 0xf9, 0xb0, 0xb9, 0xab, 0x14, 0xed, 0x05, 0xef, + 0x79, 0xe3, 0x6d, 0xbd, 0x5d, 0x6f, 0x36, 0xf2, 0xc8, 0xf7, 0x05, 0x27, 0xe9, 0xfc, 0x74, 0x5a, + 0x83, 0x1a, 0x16, 0xad, 0x3e, 0x6a, 0xe7, 0x25, 0xad, 0xaa, 0x27, 0xd5, 0xf0, 0x05, 0x95, 0xda, + 0x61, 0x0c, 0x3e, 0xa2, 0x51, 0xd5, 0xb0, 0x54, 0x64, 0xa5, 0xb1, 0x17, 0xa4, 0xa5, 0x1d, 0x28, + 0x0f, 0xa9, 0x16, 0xff, 0xd6, 0xe1, 0xea, 0xa5, 0xd7, 0x55, 0x18, 0xc1, 0x56, 0x18, 0x81, 0x1a, + 0x73, 0xfd, 0xa2, 0xda, 0xfb, 0x88, 0xa1, 0x75, 0x1e, 0x47, 0x7e, 0x36, 0xeb, 0x91, 0x4e, 0x7b, + 0x09, 0xec, 0x99, 0xb6, 0x29, 0x9b, 0x9a, 0x17, 0x81, 0x92, 0x90, 0x8c, 0xa0, 0xb1, 0x19, 0x04, + 0x09, 0x33, 0x36, 0x87, 0xbf, 0x73, 0x26, 0x7f, 0x1e, 0xf3, 0x39, 0xff, 0x11, 0x18, 0x8d, 0x8e, + 0x0c, 0x87, 0x56, 0x40, 0x75, 0x88, 0x11, 0x01, 0x0e, 0x10, 0x54, 0x63, 0xa9, 0x36, 0x41, 0xae, + 0xe8, 0x07, 0xa8, 0xfd, 0x9f, 0x69, 0xd7, 0xf4, 0x23, 0x58, 0xe8, 0xe6, 0x57, 0xf0, 0x03, 0x50, + 0x5e, 0xc3, 0xc8, 0xeb, 0x7b, 0x18, 0xeb, 0x96, 0x65, 0x47, 0xdb, 0x8b, 0x6c, 0x1c, 0x6e, 0xcb, + 0x1c, 0xee, 0xef, 0x29, 0x09, 0xa5, 0xb1, 0xa8, 0x29, 0x18, 0x1e, 0x23, 0xdd, 0xd4, 0xf0, 0x1c, + 0x2b, 0x8d, 0x52, 0x9c, 0xe1, 0xa3, 0x90, 0xc7, 0x5f, 0x69, 0xcd, 0x7d, 0x26, 0x9f, 0x9b, 0x1a, + 0x9e, 0x63, 0xa0, 0x2d, 0xff, 0x19, 0x6c, 0x3a, 0x55, 0x97, 0x60, 0xef, 0xed, 0xff, 0x43, 0xe8, + 0xae, 0xeb, 0x02, 0x74, 0x63, 0xf9, 0xcb, 0xca, 0xfb, 0x26, 0xfc, 0x79, 0xd6, 0xac, 0x03, 0xa2, + 0x80, 0xec, 0xcd, 0xb7, 0x0c, 0xd0, 0x4b, 0x79, 0x68, 0x26, 0x17, 0xda, 0xcc, 0x2a, 0x97, 0x4b, + 0xd2, 0x3a, 0x27, 0xbc, 0x13, 0x45, 0xd6, 0x1c, 0x13, 0x93, 0xfe, 0x9c, 0x7c, 0x22, 0xf6, 0x9c, + 0xe6, 0xc9, 0x0f, 0x5e, 0xd7, 0x97, 0x86, 0x39, 0xb4, 0x46, 0xfa, 0xaa, 0x01, 0xce, 0x5f, 0x4b, + 0x31, 0xd8, 0xd3, 0x8a, 0x62, 0xcb, 0xa9, 0x2c, 0x74, 0xd4, 0x72, 0x12, 0x24, 0x49, 0x66, 0xfe, + 0x22, 0x5c, 0x6e, 0x09, 0x30, 0x66, 0x12, 0x8a, 0x25, 0x48, 0xd0, 0x12, 0x88, 0x9d, 0x96, 0x20, + 0x59, 0x49, 0x3d, 0xad, 0x59, 0x02, 0xa1, 0x5a, 0x82, 0xfb, 0xfc, 0xac, 0x84, 0x4e, 0xd0, 0x07, + 0x27, 0x72, 0x75, 0xee, 0xa9, 0x4b, 0x98, 0x9b, 0x75, 0x73, 0x3a, 0x10, 0xc2, 0xa7, 0x96, 0x29, + 0x13, 0x82, 0x3d, 0xc2, 0xb7, 0x61, 0x42, 0xfb, 0x71, 0xea, 0xc5, 0x94, 0x4f, 0x5c, 0x6f, 0x7d, + 0xe4, 0xe1, 0x51, 0xbd, 0x00, 0xb2, 0xbd, 0x07, 0x28, 0xaa, 0x26, 0x7b, 0xba, 0x68, 0xef, 0x85, + 0x4d, 0x70, 0x2b, 0xc3, 0xbd, 0xc4, 0xf8, 0x0f, 0x96, 0xc1, 0x7f, 0xba, 0x59, 0x32, 0x65, 0x49, + 0x59, 0xb2, 0x4b, 0x45, 0x59, 0x2a, 0x0a, 0x88, 0x23, 0x94, 0x05, 0x22, 0x31, 0xc3, 0x57, 0x8b, + 0xe4, 0x39, 0x78, 0xdf, 0xe8, 0x19, 0x23, 0x50, 0xc9, 0x25, 0x57, 0x74, 0x2c, 0x1e, 0xf3, 0x28, + 0x01, 0xe1, 0x83, 0xde, 0x1a, 0xc4, 0x13, 0x16, 0xf3, 0xb0, 0xb6, 0xbf, 0x17, 0x60, 0xaa, 0x82, + 0x5c, 0x8b, 0x55, 0x04, 0xe8, 0x9e, 0x39, 0xc0, 0x70, 0xe7, 0x85, 0xae, 0x68, 0x00, 0x70, 0x55, + 0x31, 0x87, 0x52, 0x6b, 0xd4, 0xeb, 0x2f, 0xf4, 0x78, 0x0f, 0x37, 0xb3, 0xe4, 0x81, 0xa9, 0xa0, + 0x4b, 0x0c, 0x66, 0xf3, 0xb8, 0x1a, 0xb0, 0x01, 0xfe, 0xfd, 0xa1, 0x64, 0x12, 0x36, 0x34, 0x26, + 0x0f, 0x7d, 0x0b, 0x59, 0x7a, 0x18, 0x63, 0xb8, 0x5b, 0x3a, 0x59, 0x76, 0xfe, 0x9b, 0x46, 0x27, + 0xe8, 0xa1, 0xb4, 0x02, 0x60, 0xe0, 0xdd, 0x58, 0x23, 0x8f, 0x19, 0x9f, 0xf0, 0xe4, 0x3f, 0xcb, + 0x3f, 0xc1, 0x0b, 0x7e, 0xd7, 0xcc, 0x9e, 0x3d, 0xa7, 0x99, 0xb9, 0x1c, 0xd4, 0x3c, 0xc6, 0xaf, + 0xc3, 0xd9, 0x73, 0xe8, 0xba, 0xf8, 0x59, 0x99, 0x56, 0xff, 0x0f, 0x49, 0x48, 0x38, 0x92, 0x47, + 0x68, 0x68, 0xb9, 0x58, 0x6c, 0x69, 0x8b, 0x3f, 0xf5, 0xb1, 0xb5, 0x75, 0x5a, 0x96, 0x1e, 0xfe, + 0x06, 0xa5, 0x28, 0xb5, 0x06, 0x81, 0xd8, 0xba, 0x68, 0x90, 0xda, 0xf1, 0xa6, 0x64, 0xc6, 0xaa, + 0xd8, 0x2e, 0xd9, 0xc9, 0x9a, 0xec, 0xe4, 0x92, 0x03, 0x50, 0x51, 0xcb, 0x03, 0x60, 0xdc, 0x24, + 0x4a, 0x98, 0xd3, 0xfc, 0xa0, 0xdb, 0xec, 0xbf, 0xcc, 0x3d, 0x6d, 0x73, 0xdb, 0x36, 0xd2, 0xdf, + 0x3b, 0x93, 0xff, 0x40, 0xb3, 0x49, 0x4c, 0x4a, 0x14, 0xf5, 0xe2, 0xda, 0xd7, 0x52, 0x66, 0x34, + 0x89, 0xdb, 0x4c, 0x72, 0xbd, 0x34, 0xb9, 0x3a, 0xe9, 0xf5, 0xce, 0xd6, 0x93, 0xd2, 0x12, 0x2c, + 0x71, 0x22, 0x93, 0x2e, 0x09, 0xc9, 0xf6, 0xc9, 0xfa, 0xef, 0xb7, 0x2f, 0x00, 0x09, 0x52, 0x94, + 0x93, 0xdc, 0xdc, 0x87, 0x67, 0x9a, 0xca, 0xc4, 0x3b, 0xb0, 0x58, 0x2c, 0x76, 0x17, 0x8b, 0xc5, + 0xdc, 0xcb, 0xbb, 0x03, 0x0f, 0x38, 0xff, 0x76, 0xdf, 0x3f, 0x74, 0x8b, 0x45, 0x3e, 0xcf, 0x17, + 0x8e, 0xdd, 0xde, 0xc2, 0x6b, 0xa8, 0xae, 0x6d, 0x7b, 0xf4, 0x78, 0x89, 0x87, 0x8e, 0xaf, 0x5d, + 0xe8, 0xf0, 0x86, 0x7b, 0xdc, 0x48, 0x65, 0x54, 0xb7, 0xf3, 0x48, 0x2a, 0x4f, 0xf0, 0xb6, 0x37, + 0x81, 0xfd, 0xdf, 0x9b, 0xdc, 0x05, 0x80, 0x60, 0x45, 0xd7, 0xc9, 0xf5, 0x7e, 0xfb, 0x37, 0xc7, + 0xfe, 0xd6, 0x6e, 0x6b, 0xa8, 0x43, 0x43, 0x58, 0x3b, 0xd0, 0x4a, 0xaa, 0xe4, 0x6f, 0xda, 0x3f, + 0xd6, 0x6e, 0x92, 0xa6, 0x1a, 0x5b, 0xe8, 0x9c, 0xcd, 0x6d, 0x11, 0x71, 0xf4, 0x0a, 0x87, 0x7b, + 0x9d, 0xd8, 0x5f, 0x21, 0x42, 0x6e, 0x3e, 0x3b, 0x08, 0x5e, 0x1b, 0xcd, 0x95, 0x32, 0xc9, 0x54, + 0xc0, 0xd3, 0xcb, 0x88, 0x6e, 0xc7, 0xd5, 0x67, 0x2b, 0xa1, 0x86, 0x1e, 0x7b, 0xeb, 0x2c, 0xc8, + 0x88, 0x4a, 0xea, 0x45, 0x76, 0xba, 0x9a, 0x69, 0x62, 0xa9, 0xa3, 0xde, 0x52, 0x10, 0xb6, 0xa0, + 0x49, 0x7b, 0x51, 0xee, 0x77, 0x11, 0x08, 0xff, 0xc1, 0x5c, 0xc7, 0xe0, 0x5e, 0x17, 0xe1, 0x1c, + 0x54, 0x17, 0xbd, 0xc1, 0xc7, 0x84, 0xbb, 0xec, 0x14, 0x52, 0x1f, 0x6d, 0xe0, 0x61, 0x59, 0xa7, + 0xe8, 0xc8, 0x11, 0x4d, 0x14, 0x0a, 0x92, 0x03, 0xab, 0x3a, 0x67, 0xb2, 0x00, 0xc8, 0xb1, 0x08, + 0xa3, 0x4e, 0x5e, 0xac, 0xd5, 0xbc, 0x42, 0x14, 0x20, 0x68, 0xd0, 0x8c, 0xa1, 0x84, 0xac, 0x8e, + 0xec, 0x64, 0x20, 0xb4, 0xe0, 0x97, 0xe8, 0xc4, 0xae, 0x32, 0x71, 0xa6, 0xce, 0x46, 0x32, 0x4a, + 0x06, 0x8e, 0x40, 0x7e, 0x7e, 0xde, 0x4c, 0x36, 0x98, 0xb9, 0x47, 0x56, 0xd2, 0x99, 0xb4, 0xf0, + 0x89, 0xde, 0xae, 0xa2, 0x1f, 0xb0, 0xc9, 0x13, 0x05, 0x59, 0x72, 0x4d, 0x57, 0x30, 0x6c, 0x1e, + 0xff, 0x9f, 0x19, 0x88, 0x0b, 0x2d, 0xd9, 0x16, 0x2d, 0xe0, 0x7e, 0x16, 0xc0, 0x72, 0xfa, 0x69, + 0xc2, 0xfc, 0x5b, 0xe2, 0xad, 0xe7, 0x30, 0x4b, 0x79, 0x60, 0x54, 0x0a, 0x33, 0xdd, 0x5d, 0xb4, + 0x96, 0x2e, 0xb2, 0xde, 0x62, 0x83, 0xfa, 0x81, 0x62, 0xfb, 0xf8, 0xd3, 0xa4, 0xd5, 0x9d, 0xfe, + 0x31, 0x1e, 0x87, 0x4d, 0xc5, 0xed, 0xdb, 0x4b, 0xc7, 0x7e, 0x82, 0x76, 0x80, 0xe1, 0x75, 0x94, + 0xe5, 0xe2, 0x25, 0x7a, 0xa5, 0xc4, 0x72, 0x9a, 0xad, 0x1a, 0x09, 0xa2, 0x67, 0x49, 0x60, 0xa8, + 0xfd, 0x7f, 0x37, 0x97, 0x1d, 0x96, 0x7a, 0x9d, 0x48, 0xd4, 0x0b, 0x1f, 0x19, 0x3c, 0xc9, 0x3f, + 0x4d, 0xd3, 0xa7, 0x52, 0x86, 0x81, 0x3c, 0x08, 0xea, 0x53, 0x94, 0x9d, 0x9c, 0x81, 0x07, 0x24, + 0x81, 0xcf, 0xe0, 0xfe, 0x1a, 0xda, 0xce, 0x28, 0x38, 0xeb, 0x9c, 0x9f, 0xb7, 0xc7, 0xa3, 0xf3, + 0xf3, 0x69, 0xeb, 0xfc, 0xdc, 0x87, 0x3f, 0xed, 0x27, 0x23, 0xf7, 0xbe, 0x92, 0x80, 0x31, 0xb6, + 0xf7, 0x73, 0x68, 0x9f, 0x9d, 0x9f, 0xe7, 0xf7, 0xe7, 0xe7, 0xce, 0xb8, 0x0d, 0x6b, 0xeb, 0xaf, + 0xb0, 0x9a, 0xce, 0x3c, 0x08, 0xe6, 0xbb, 0x82, 0xf0, 0x0d, 0x75, 0xba, 0x23, 0xdb, 0xfb, 0xfb, + 0x57, 0x97, 0xdd, 0x59, 0xd5, 0xbf, 0xe8, 0x18, 0xe6, 0x57, 0x31, 0xfb, 0xe9, 0x16, 0x89, 0xec, + 0xec, 0xc2, 0x6e, 0xff, 0x0c, 0x4b, 0x5a, 0xd6, 0xa3, 0x23, 0xbb, 0xfd, 0x77, 0xc0, 0x99, 0x6a, + 0x3c, 0xd0, 0x20, 0xca, 0x9e, 0x6e, 0x45, 0x73, 0x76, 0x10, 0x70, 0xec, 0xff, 0x83, 0xb1, 0x7f, + 0x3b, 0xba, 0xef, 0xdd, 0xe2, 0xa0, 0x33, 0x88, 0x70, 0xce, 0x7a, 0x9d, 0x1f, 0xa2, 0xce, 0xe5, + 0xf3, 0xce, 0xcb, 0xf1, 0x1a, 0x18, 0x72, 0xdb, 0x8b, 0xeb, 0xb1, 0x03, 0x8c, 0xcd, 0xab, 0x95, + 0xda, 0xed, 0x04, 0xd8, 0x60, 0xfd, 0xcf, 0x7e, 0x0c, 0x13, 0x1e, 0x3d, 0x94, 0x43, 0x67, 0x5a, + 0x34, 0x64, 0x8a, 0xf5, 0x3f, 0xca, 0x31, 0x79, 0x28, 0x87, 0xce, 0x34, 0xaf, 0xc9, 0x6a, 0xb4, + 0x34, 0xd2, 0xe4, 0x84, 0x6f, 0x74, 0xef, 0xf5, 0x95, 0xfa, 0x85, 0x2f, 0x04, 0x03, 0x56, 0xf7, + 0x00, 0xab, 0x7b, 0xde, 0x0a, 0xfe, 0x8f, 0x82, 0xfe, 0xc6, 0xd3, 0xf2, 0x3d, 0x5e, 0x24, 0x92, + 0x80, 0xd9, 0xb0, 0xe8, 0xd7, 0xb0, 0x47, 0x07, 0xeb, 0x09, 0x79, 0x44, 0x04, 0xda, 0x0b, 0xfb, + 0x60, 0xb0, 0x07, 0x8c, 0x01, 0xc0, 0xba, 0x21, 0x16, 0x40, 0xda, 0x9c, 0x97, 0x91, 0xb2, 0xa9, + 0x84, 0xb8, 0xdd, 0x9d, 0x96, 0x2f, 0x76, 0xa5, 0x6d, 0x86, 0x73, 0x53, 0x56, 0x85, 0xfe, 0x36, + 0xf0, 0x25, 0x5b, 0x67, 0xef, 0x28, 0x1e, 0x6d, 0xd9, 0xe0, 0x4a, 0x54, 0xa4, 0x82, 0xa8, 0xde, + 0x35, 0x50, 0xc0, 0x9c, 0xe5, 0x03, 0xef, 0xfb, 0xcd, 0xe3, 0x2e, 0xcb, 0x45, 0xd2, 0x75, 0x59, + 0xba, 0xd3, 0xbd, 0x0e, 0x65, 0x61, 0x5c, 0x40, 0x55, 0x20, 0x0a, 0x8e, 0x6a, 0x99, 0x8b, 0xe1, + 0xd7, 0x33, 0x23, 0x02, 0xd6, 0x33, 0x17, 0x63, 0x36, 0x32, 0x03, 0xf7, 0x8d, 0xd7, 0x6c, 0x72, + 0x89, 0x97, 0x75, 0xa0, 0xc7, 0x73, 0xa9, 0xf3, 0xae, 0x90, 0xb5, 0xca, 0x57, 0x66, 0x4e, 0x3b, + 0x23, 0xfb, 0x40, 0xf8, 0x98, 0xe9, 0x8f, 0x0b, 0xb6, 0x18, 0xd4, 0x9d, 0xa9, 0xd6, 0x6c, 0xcf, + 0x75, 0xb6, 0x5c, 0x7f, 0xac, 0x8c, 0xfc, 0xd4, 0x06, 0xe5, 0xa7, 0x53, 0xa6, 0x1d, 0x25, 0x16, + 0xca, 0x26, 0x51, 0x82, 0xd4, 0x79, 0x63, 0x21, 0x9a, 0x92, 0x8f, 0x28, 0x07, 0xa2, 0x01, 0xd5, + 0xe2, 0xa9, 0x7a, 0x7b, 0x91, 0xcf, 0xb8, 0xdd, 0xa1, 0x1e, 0x69, 0x28, 0x81, 0x85, 0xaa, 0x4f, + 0xe4, 0x09, 0xfb, 0x33, 0xac, 0xed, 0x36, 0xfa, 0x8c, 0x1e, 0x8b, 0x9e, 0xc9, 0x71, 0xb8, 0x25, + 0x1b, 0xa9, 0x04, 0xcf, 0x61, 0x45, 0x1e, 0x5a, 0xe3, 0xa6, 0x64, 0x3a, 0x5a, 0x6d, 0x00, 0x58, + 0xe3, 0x44, 0x98, 0xba, 0x10, 0x2d, 0x71, 0x42, 0x9f, 0xe7, 0x52, 0xeb, 0xbf, 0xe6, 0x04, 0xd6, + 0xf7, 0xe9, 0xaf, 0x00, 0xad, 0x06, 0x7e, 0xd7, 0x9f, 0x77, 0x8f, 0x7a, 0xc4, 0xf1, 0x12, 0x0f, + 0x4a, 0x1c, 0x2f, 0x6d, 0xfe, 0xc0, 0xeb, 0xd2, 0x1e, 0x01, 0x7c, 0x23, 0x5e, 0x8b, 0x71, 0x61, + 0x4f, 0x14, 0x1d, 0x34, 0xda, 0x4b, 0x60, 0x0f, 0xea, 0xa4, 0x68, 0x63, 0x41, 0x5f, 0x71, 0x2b, + 0x45, 0x1e, 0x96, 0xbe, 0x31, 0xe8, 0x62, 0x78, 0x12, 0x66, 0x4f, 0x8e, 0xd4, 0x76, 0x00, 0xbb, + 0xfa, 0xe0, 0xf0, 0xb0, 0x75, 0x06, 0x6c, 0xb1, 0x97, 0xc3, 0x7f, 0x0b, 0x2f, 0x19, 0x9f, 0x4d, + 0xc6, 0xde, 0x8c, 0xa3, 0x21, 0xe8, 0xa9, 0x24, 0x8a, 0xbe, 0xe0, 0x68, 0x95, 0x13, 0x93, 0x30, + 0x9a, 0x81, 0x0b, 0x33, 0xfe, 0x3e, 0x7d, 0x05, 0xd3, 0xb8, 0x35, 0x10, 0x1a, 0x41, 0xd6, 0x85, + 0xa2, 0x34, 0x82, 0x19, 0x7d, 0x21, 0xb7, 0x7e, 0x41, 0x5f, 0xb1, 0xda, 0x21, 0xa3, 0x5b, 0xd6, + 0x60, 0x00, 0x53, 0x57, 0xee, 0x99, 0x2a, 0x26, 0x0a, 0xe3, 0x0e, 0x9e, 0x2d, 0xc6, 0xd0, 0x7b, + 0x54, 0xa4, 0xc6, 0x20, 0xc5, 0x47, 0xdd, 0x58, 0xeb, 0x6b, 0x62, 0xa5, 0xaa, 0xc9, 0x03, 0x11, + 0xf6, 0x4c, 0x65, 0x49, 0x0a, 0x11, 0x4e, 0x02, 0xfb, 0x7b, 0x37, 0x6a, 0x3b, 0xc9, 0x71, 0x36, + 0x3a, 0x0a, 0x7a, 0x15, 0x6d, 0x4a, 0x82, 0x19, 0x32, 0x00, 0x19, 0x64, 0x18, 0x98, 0x09, 0x19, + 0x26, 0xa4, 0xc0, 0xe7, 0x43, 0xc2, 0x77, 0x4a, 0x1c, 0x03, 0x42, 0x76, 0xd4, 0x6b, 0x09, 0xa0, + 0x65, 0xb8, 0x83, 0x4e, 0x80, 0x9e, 0xe1, 0xdf, 0xc5, 0xa6, 0x9c, 0xc8, 0x57, 0xf9, 0xa2, 0x71, + 0x22, 0x79, 0xfe, 0xd2, 0x62, 0xfe, 0x92, 0xd0, 0x19, 0x74, 0x04, 0x4c, 0x07, 0xc0, 0x01, 0xd5, + 0x12, 0xa3, 0x24, 0x40, 0xd9, 0x24, 0x0e, 0xb3, 0xe3, 0xbe, 0xe8, 0xfc, 0x00, 0xa3, 0x13, 0xad, + 0xb4, 0x9b, 0x0d, 0x8b, 0x76, 0xa1, 0x7e, 0xd5, 0x6c, 0xec, 0x2d, 0x82, 0x43, 0xd8, 0xbf, 0x75, + 0xab, 0x8b, 0x1d, 0x50, 0x0f, 0x07, 0x2d, 0xe9, 0x2f, 0x18, 0x7b, 0x5a, 0x8e, 0x80, 0x46, 0x7a, + 0xbd, 0x91, 0x08, 0x06, 0xbd, 0x1e, 0x34, 0xac, 0x3a, 0x21, 0xda, 0xa9, 0x6e, 0x6f, 0x00, 0xed, + 0xa1, 0x46, 0xc2, 0x6d, 0x6a, 0x33, 0x81, 0xa1, 0x52, 0x62, 0x77, 0xb0, 0x41, 0x6a, 0x8d, 0x83, + 0x45, 0x1d, 0x9a, 0x89, 0xe0, 0x64, 0xcb, 0x1c, 0x1a, 0x74, 0xbf, 0x5e, 0x11, 0xf4, 0x03, 0xaa, + 0x01, 0x08, 0x94, 0x55, 0x34, 0xd3, 0x53, 0xb3, 0x0e, 0x3c, 0xce, 0xdd, 0x5e, 0x84, 0xa2, 0xd0, + 0xb0, 0x16, 0x9b, 0x8e, 0x66, 0x84, 0x0c, 0xd3, 0x3f, 0x65, 0x23, 0x43, 0x06, 0x2c, 0x68, 0xdc, + 0xc2, 0x56, 0x36, 0x43, 0x73, 0x6b, 0x92, 0x9e, 0x93, 0xfa, 0xf3, 0xfb, 0xfb, 0xd4, 0xcf, 0xf1, + 0x67, 0x85, 0x3f, 0x91, 0xab, 0xb5, 0xcf, 0xba, 0x6e, 0x96, 0xce, 0x53, 0x6d, 0x1e, 0x6e, 0x96, + 0xa7, 0xa1, 0x00, 0xea, 0x37, 0x43, 0xc3, 0x58, 0xdf, 0x8e, 0x51, 0x0c, 0xb9, 0x4d, 0xad, 0xd7, + 0x99, 0xb1, 0x1c, 0x5b, 0xae, 0x47, 0x83, 0xf9, 0x83, 0x8c, 0x33, 0x33, 0x0c, 0x8b, 0xf7, 0xc2, + 0x0c, 0x27, 0xee, 0xa6, 0x68, 0xbf, 0x0e, 0xca, 0x82, 0xaa, 0x6e, 0x01, 0xcf, 0x58, 0xab, 0xa8, + 0x4a, 0x5f, 0x47, 0x81, 0x61, 0x68, 0x11, 0x8d, 0xfa, 0x30, 0x45, 0xd1, 0xc6, 0x55, 0x73, 0xb4, + 0x78, 0x78, 0x60, 0x80, 0xef, 0xdb, 0x03, 0x9b, 0x33, 0xd2, 0xd1, 0xc0, 0x16, 0x25, 0x16, 0x54, + 0x07, 0x96, 0xd7, 0x06, 0xb6, 0x68, 0x1a, 0x18, 0xb6, 0xff, 0x55, 0x03, 0xd3, 0xcb, 0xe1, 0x73, + 0x03, 0x2b, 0xf6, 0xca, 0x07, 0xb0, 0x18, 0xf2, 0xa8, 0xce, 0x23, 0xc7, 0x07, 0xac, 0x22, 0x14, + 0x43, 0x61, 0x12, 0x3f, 0x66, 0xfa, 0xe3, 0x82, 0xa4, 0xbe, 0x6a, 0x95, 0x8d, 0x58, 0xad, 0xcd, + 0xf3, 0xc3, 0x3e, 0xe2, 0xb4, 0x23, 0xc2, 0x7f, 0xf9, 0xe2, 0x56, 0x4c, 0x70, 0x43, 0x1e, 0x39, + 0x69, 0xf8, 0xa7, 0x83, 0xf7, 0x56, 0x3c, 0xa0, 0x87, 0xc8, 0xb9, 0x63, 0x68, 0xa0, 0x42, 0x19, + 0x85, 0x0e, 0x38, 0xe4, 0xc2, 0x32, 0x0c, 0xa5, 0x2c, 0x8a, 0x3e, 0x7d, 0xfa, 0xe5, 0x65, 0xa1, + 0x6d, 0x0c, 0x7d, 0x37, 0xf6, 0xf0, 0xda, 0xe0, 0x9e, 0xd8, 0xbd, 0x8b, 0xc2, 0x50, 0x2c, 0xc5, + 0xd3, 0xa8, 0x4d, 0x14, 0xb7, 0x73, 0xc0, 0xcd, 0x14, 0xf0, 0x31, 0x01, 0x1c, 0xcc, 0x80, 0x8d, + 0x8b, 0xd5, 0x14, 0x69, 0x16, 0xe5, 0xcb, 0x20, 0x09, 0x02, 0x33, 0x08, 0x12, 0x7e, 0xe6, 0xd2, + 0x9f, 0x19, 0xff, 0xb9, 0x70, 0x6b, 0x55, 0x3d, 0x0c, 0x41, 0x18, 0x8c, 0x82, 0x61, 0x2e, 0x2b, + 0x40, 0xec, 0xff, 0xa5, 0xf5, 0x3b, 0xc1, 0x02, 0xe1, 0xa0, 0x02, 0x83, 0x31, 0x82, 0x41, 0x05, + 0x0e, 0xc6, 0x0c, 0xc1, 0xe8, 0xeb, 0xcb, 0xe1, 0xcc, 0x71, 0xe0, 0x3b, 0x55, 0xc9, 0xa2, 0x5a, + 0x49, 0x59, 0x43, 0x59, 0xdc, 0x6c, 0x73, 0x52, 0x9b, 0xb5, 0x07, 0xf2, 0x43, 0x5b, 0xba, 0xa1, + 0x07, 0xe7, 0x09, 0x00, 0xf6, 0x05, 0xf3, 0x84, 0x9b, 0x6c, 0xb1, 0x9c, 0x3e, 0x3b, 0x57, 0x90, + 0x47, 0xcf, 0x15, 0xeb, 0x54, 0xa0, 0x98, 0x46, 0xf6, 0xbc, 0x6d, 0x3f, 0xe1, 0xaf, 0x05, 0x7c, + 0x29, 0xbc, 0x2f, 0x2b, 0xfd, 0x32, 0xbc, 0x17, 0xb2, 0x09, 0xf1, 0x51, 0x15, 0x58, 0x22, 0x2f, + 0xec, 0x39, 0x06, 0xf2, 0x62, 0x88, 0x40, 0x98, 0x36, 0x23, 0xfe, 0xe7, 0xca, 0x7e, 0x39, 0xe2, + 0xc3, 0x58, 0x6a, 0x00, 0x45, 0xee, 0x71, 0x8d, 0x6a, 0xbb, 0x1c, 0x00, 0xba, 0x28, 0x10, 0x5f, + 0xd1, 0x1e, 0xbe, 0x3f, 0x8d, 0xea, 0x64, 0x7c, 0xd4, 0x52, 0xe4, 0x8e, 0xc9, 0x03, 0x02, 0x70, + 0x58, 0x99, 0x70, 0x2d, 0xff, 0xff, 0xe9, 0x7f, 0x59, 0xc9, 0xaa, 0xf5, 0x87, 0x8d, 0x3c, 0x4b, + 0xa9, 0x3a, 0xa5, 0x1b, 0xac, 0xc5, 0xd1, 0x18, 0x3b, 0xb4, 0x7c, 0x5f, 0x1c, 0x90, 0xd9, 0xa8, + 0xe4, 0x0b, 0x20, 0x4f, 0xa9, 0x6c, 0xb4, 0x3d, 0x76, 0xd4, 0x6b, 0xe3, 0x2b, 0xe7, 0x80, 0x26, + 0xa5, 0x6a, 0xf1, 0xc8, 0x3f, 0x3a, 0x3a, 0x32, 0x92, 0x2f, 0x2b, 0xc9, 0x07, 0x07, 0xfe, 0xc1, + 0xc1, 0x41, 0x99, 0xdc, 0xab, 0x26, 0x1f, 0xf6, 0xcc, 0xa4, 0x4b, 0x33, 0xe9, 0xa8, 0x56, 0x71, + 0xaf, 0x57, 0x49, 0xfe, 0xbe, 0x56, 0xf1, 0x65, 0x35, 0xb9, 0xdf, 0xab, 0x77, 0x79, 0x6c, 0xf0, + 0x82, 0xb6, 0xa1, 0x0e, 0x0c, 0x88, 0xb1, 0xa8, 0xec, 0x7f, 0x80, 0x1e, 0x82, 0x18, 0x1c, 0x14, + 0x70, 0x05, 0xb0, 0x37, 0x88, 0x7e, 0x8d, 0x19, 0x90, 0xe9, 0x52, 0x59, 0x86, 0xcd, 0xf0, 0xe2, + 0x25, 0x97, 0xe8, 0x25, 0x97, 0x14, 0x4b, 0x2e, 0x29, 0x96, 0x5c, 0x63, 0xa7, 0xb9, 0x5c, 0xa6, + 0xcb, 0x65, 0x45, 0xb9, 0x4c, 0x97, 0xab, 0x0c, 0x88, 0x05, 0xa4, 0x40, 0x1d, 0x87, 0x04, 0x7c, + 0x40, 0xd6, 0xd8, 0x65, 0x41, 0x4c, 0x1b, 0xe9, 0x1b, 0x77, 0x74, 0x99, 0x0f, 0x6f, 0x1e, 0xea, + 0x56, 0xac, 0xbb, 0x15, 0x17, 0xdd, 0x8a, 0x8b, 0x6e, 0x6d, 0x4a, 0x6d, 0x2f, 0x3e, 0xb7, 0x13, + 0x65, 0xbb, 0x74, 0xda, 0xe9, 0x03, 0x7a, 0xe2, 0xaa, 0x02, 0x5b, 0xfa, 0xfc, 0x01, 0x2d, 0xd4, + 0xd4, 0xd8, 0x0a, 0xa9, 0x37, 0xdb, 0xfa, 0xc7, 0xdd, 0x87, 0x0e, 0xea, 0xb4, 0x81, 0x58, 0x1a, + 0xc2, 0xfc, 0x57, 0xa4, 0xc0, 0xff, 0xec, 0xf1, 0x03, 0x80, 0x2b, 0xbd, 0xbf, 0x47, 0x26, 0x5c, + 0x69, 0x22, 0xdb, 0x83, 0x56, 0x06, 0xff, 0x27, 0xcc, 0xb8, 0xf2, 0xc3, 0xab, 0x62, 0xa8, 0x6e, + 0x6c, 0x84, 0x8e, 0x12, 0xf8, 0xe9, 0x09, 0x56, 0x64, 0xb4, 0x41, 0xe6, 0x11, 0x9d, 0x41, 0x8b, + 0x2d, 0x2a, 0x8b, 0xb5, 0xf8, 0xb9, 0x65, 0x18, 0x87, 0x0b, 0x90, 0x1a, 0xc3, 0x03, 0xff, 0x68, + 0x17, 0x02, 0x63, 0x8e, 0xfc, 0x01, 0x64, 0xc0, 0xf4, 0xd5, 0xe6, 0xa1, 0x03, 0x0d, 0x6e, 0x59, + 0x9f, 0x68, 0x88, 0xad, 0x13, 0x0d, 0x7e, 0x1a, 0xe9, 0x7d, 0x7a, 0x1d, 0xe8, 0x5e, 0xbe, 0xa1, + 0x18, 0xef, 0xbf, 0x38, 0xea, 0xe0, 0x5d, 0xad, 0x42, 0xac, 0x90, 0xfc, 0xd3, 0x11, 0x08, 0xaa, + 0x57, 0x9a, 0x7a, 0xf6, 0x91, 0x99, 0x51, 0xdb, 0xcb, 0x6e, 0x83, 0xdc, 0xcb, 0xee, 0xe0, 0xe7, + 0x36, 0x48, 0x00, 0xa2, 0x77, 0xf4, 0xab, 0xba, 0x0d, 0xe2, 0x96, 0xee, 0x38, 0x7c, 0xd6, 0xb5, + 0xde, 0xcd, 0xba, 0xf1, 0xcf, 0x6b, 0xff, 0xbf, 0x5a, 0x55, 0x9e, 0xb7, 0x63, 0xd2, 0xc7, 0x46, + 0xd0, 0xbb, 0x14, 0xc4, 0xab, 0x3a, 0x62, 0xc2, 0x4e, 0x4d, 0x37, 0x5c, 0xc8, 0x04, 0x15, 0xdf, + 0xa1, 0x6b, 0x56, 0x58, 0x68, 0xe5, 0xb8, 0x32, 0x72, 0xa0, 0x61, 0x74, 0x0c, 0x94, 0x1a, 0xca, + 0x4e, 0x98, 0xb4, 0x8d, 0x88, 0xee, 0x80, 0x51, 0xaf, 0x14, 0xbe, 0x0b, 0x99, 0x5b, 0xa2, 0xc4, + 0xdd, 0x2b, 0x34, 0xc6, 0x35, 0x6d, 0x74, 0xd6, 0x8a, 0xbf, 0x5e, 0x77, 0x4f, 0xbe, 0x3f, 0xa8, + 0xf5, 0xad, 0xf1, 0xa8, 0x61, 0x14, 0x88, 0x5d, 0x1e, 0x23, 0xee, 0xc2, 0x70, 0x7b, 0x6e, 0x7b, + 0x79, 0xeb, 0x61, 0x1c, 0xb7, 0x73, 0xfb, 0x41, 0x1c, 0xb7, 0x57, 0xf6, 0xc6, 0x68, 0xaa, 0x54, + 0xc1, 0x3b, 0x4e, 0x46, 0x7a, 0x9d, 0x78, 0x1c, 0xe6, 0x68, 0x16, 0xad, 0xd5, 0xee, 0x74, 0x97, + 0x6c, 0x87, 0xfd, 0x4f, 0xbc, 0xcb, 0xfe, 0x47, 0x5c, 0xc5, 0xf2, 0x55, 0x9a, 0x7e, 0x42, 0x0e, + 0x23, 0x96, 0xc1, 0x05, 0x59, 0xab, 0xda, 0x5a, 0x74, 0x25, 0xeb, 0x16, 0xbc, 0x0b, 0xae, 0xc2, + 0xd0, 0x3b, 0x91, 0x65, 0x82, 0x4d, 0xcb, 0x8c, 0x78, 0x5a, 0xfc, 0x6c, 0x25, 0xf5, 0x7c, 0x82, + 0xaf, 0x94, 0x95, 0x0a, 0x56, 0x23, 0xe9, 0x34, 0x9b, 0x84, 0xe5, 0x92, 0xa1, 0x84, 0x50, 0x6b, + 0x9b, 0x38, 0xa8, 0xda, 0x2d, 0x9a, 0x81, 0xce, 0x39, 0x36, 0x53, 0x6a, 0xec, 0x9e, 0x6d, 0x94, + 0xc4, 0x93, 0x08, 0x68, 0x24, 0xc7, 0x9f, 0x15, 0xfe, 0x44, 0x86, 0x69, 0x0b, 0x13, 0xa3, 0x52, + 0xdd, 0xcb, 0x6b, 0x80, 0x3a, 0x41, 0xcb, 0x84, 0xfd, 0x69, 0x90, 0x8a, 0xcb, 0x34, 0x2d, 0xdb, + 0x56, 0xa5, 0xc1, 0xe2, 0xa5, 0xd6, 0xcb, 0x5a, 0x37, 0xcd, 0x80, 0xa3, 0x0a, 0x10, 0x6e, 0x3e, + 0xd0, 0x8a, 0x74, 0x29, 0x47, 0x94, 0x89, 0xbf, 0x43, 0x1d, 0x19, 0x98, 0x91, 0x67, 0xeb, 0xc2, + 0x38, 0x29, 0xf8, 0xb7, 0xc7, 0x1e, 0xe4, 0xf3, 0x60, 0x0d, 0xf4, 0xc5, 0x48, 0xb8, 0x96, 0x66, + 0xca, 0xb8, 0xb1, 0x69, 0x7a, 0x68, 0xcf, 0xae, 0x98, 0x66, 0xc5, 0x75, 0xd3, 0xac, 0xf8, 0xbf, + 0x33, 0xcd, 0x8a, 0x4d, 0x86, 0x2c, 0x4d, 0x76, 0x9e, 0xc1, 0xe3, 0x56, 0x63, 0x20, 0xcc, 0xd0, + 0x61, 0x5b, 0x84, 0x38, 0xa7, 0xbf, 0x80, 0x7b, 0x23, 0x19, 0x9c, 0xc9, 0xb1, 0xeb, 0x03, 0x76, + 0xfd, 0x14, 0xc1, 0x1a, 0x32, 0x77, 0xb1, 0xd4, 0x18, 0x10, 0x95, 0x0f, 0xf0, 0x50, 0x13, 0x5b, + 0xf0, 0x9c, 0x04, 0x4a, 0xdd, 0xdf, 0xd3, 0x1f, 0xe0, 0xf0, 0xa0, 0x77, 0xda, 0x40, 0x2f, 0xad, + 0x23, 0x23, 0xe4, 0x40, 0x8e, 0xbb, 0x21, 0xba, 0xb1, 0x51, 0x61, 0x1a, 0x4a, 0x48, 0xd8, 0x6d, + 0x1b, 0x6b, 0xc4, 0x36, 0x51, 0x1e, 0x8f, 0x4d, 0x30, 0x5c, 0x5e, 0x9a, 0x70, 0x28, 0xc8, 0x1b, + 0x02, 0xe0, 0xeb, 0x06, 0xce, 0xdb, 0x77, 0xa2, 0xa1, 0x06, 0xf9, 0x86, 0xc9, 0x36, 0x2c, 0xf0, + 0x54, 0x1b, 0x9b, 0xf1, 0x68, 0xa6, 0xd4, 0xf5, 0x18, 0x51, 0x9c, 0xa4, 0x41, 0x42, 0x7f, 0xab, + 0x8f, 0x58, 0x49, 0x95, 0xa3, 0x57, 0x0a, 0x27, 0x41, 0x9c, 0x32, 0x4c, 0x57, 0xdd, 0xa3, 0x51, + 0xa7, 0x3f, 0xec, 0x1d, 0x27, 0x78, 0x75, 0x87, 0x94, 0x51, 0x45, 0xf2, 0x59, 0xd2, 0xee, 0x8f, + 0x87, 0x0e, 0x2f, 0x21, 0xb7, 0xe8, 0x9d, 0x82, 0x9e, 0xf0, 0x10, 0xbe, 0x80, 0x31, 0x93, 0x48, + 0x42, 0x4f, 0x4c, 0xa3, 0x51, 0x13, 0x1f, 0x68, 0x12, 0xa1, 0xd9, 0x38, 0xec, 0x0d, 0xe3, 0xe3, + 0xac, 0x70, 0x57, 0xd4, 0x6e, 0xbb, 0x19, 0x90, 0x31, 0x73, 0x2a, 0xd2, 0xda, 0x48, 0x4c, 0x52, + 0xf0, 0xbf, 0x1c, 0x91, 0xd9, 0xc9, 0xea, 0xb4, 0x57, 0x07, 0xbb, 0x63, 0xa0, 0x9e, 0x93, 0x31, + 0x66, 0x66, 0x55, 0xcc, 0xac, 0x77, 0x3f, 0x13, 0xf8, 0xc4, 0xe2, 0xb6, 0x76, 0x48, 0x9b, 0xc4, + 0x3a, 0x6b, 0xde, 0xeb, 0x65, 0x7d, 0x06, 0xc9, 0xb1, 0x9d, 0x29, 0x00, 0x1b, 0x74, 0x8d, 0xce, + 0xa4, 0xca, 0x9d, 0x81, 0x29, 0x27, 0x16, 0x07, 0x2e, 0x0e, 0x27, 0xa7, 0x71, 0x99, 0x42, 0xe5, + 0x8b, 0x25, 0x6c, 0xe8, 0x98, 0x01, 0xc6, 0x98, 0x72, 0xf7, 0xd3, 0xfa, 0xc2, 0xda, 0xc6, 0xa3, + 0x5a, 0x8d, 0x06, 0xe4, 0x11, 0xee, 0x69, 0x33, 0xdc, 0x53, 0x84, 0x3b, 0xfa, 0x4e, 0x32, 0xe0, + 0x9e, 0x22, 0xdc, 0x0b, 0xcd, 0x67, 0xb5, 0x43, 0x1a, 0x43, 0xd8, 0xd8, 0x38, 0x31, 0x8d, 0x8d, + 0x13, 0xbc, 0x20, 0x66, 0x88, 0xa0, 0xf5, 0x3e, 0xa6, 0x49, 0xcd, 0x4e, 0x54, 0xc3, 0x58, 0x2c, + 0xb4, 0xf9, 0x75, 0x75, 0x3f, 0x21, 0x23, 0x50, 0x5b, 0x1b, 0x7a, 0x9a, 0x55, 0x19, 0x3b, 0x44, + 0x0d, 0x86, 0x35, 0xba, 0xcb, 0x9b, 0xc2, 0x85, 0x71, 0xb5, 0x43, 0x11, 0xac, 0xda, 0xe4, 0xaa, + 0xcd, 0x63, 0x7b, 0xcf, 0xe0, 0x04, 0xa2, 0xdc, 0xdb, 0xe5, 0xb7, 0xb7, 0x53, 0x6d, 0x1c, 0xdd, + 0xb4, 0xd1, 0xf6, 0x3c, 0x9b, 0x5c, 0x24, 0xdb, 0xca, 0xc0, 0xa9, 0xb6, 0xe3, 0x2a, 0x4d, 0xb1, + 0xa0, 0xa1, 0xeb, 0xf7, 0x5d, 0xe9, 0x4d, 0x08, 0xa3, 0x45, 0x61, 0x6c, 0xb4, 0xdb, 0xa9, 0x8d, + 0xdb, 0x7b, 0x0d, 0x74, 0xbb, 0x38, 0x2c, 0x77, 0xad, 0x8c, 0x4a, 0x43, 0xba, 0x48, 0xbb, 0xd5, + 0x17, 0x65, 0xd1, 0x5c, 0x62, 0xb7, 0xab, 0x2c, 0x4a, 0x77, 0xe4, 0x67, 0x93, 0x67, 0x33, 0x7b, + 0x23, 0x9b, 0xa1, 0x40, 0x62, 0x72, 0x03, 0xa8, 0x90, 0x15, 0x6c, 0x7b, 0xba, 0xa3, 0x6e, 0x36, + 0xa0, 0xfe, 0x5c, 0xd5, 0x48, 0xa6, 0xea, 0x6b, 0xb5, 0x26, 0x99, 0x19, 0x8e, 0x2b, 0x70, 0x53, + 0x28, 0x25, 0x95, 0x69, 0xbc, 0xaa, 0xc9, 0x03, 0x54, 0xf9, 0x3b, 0x7e, 0x02, 0x4d, 0x4b, 0x27, + 0x5a, 0xe2, 0x48, 0x7c, 0xf5, 0xa5, 0x04, 0x82, 0x84, 0x99, 0xe6, 0x8d, 0xe2, 0xba, 0x98, 0x8f, + 0xd8, 0x92, 0x39, 0x4b, 0xe5, 0x88, 0xbe, 0x79, 0x86, 0x82, 0xa1, 0xe2, 0x22, 0xca, 0xbe, 0x88, + 0x6d, 0xa3, 0x4a, 0x10, 0x7f, 0x40, 0x42, 0x65, 0x3e, 0xb3, 0x66, 0xab, 0xaf, 0xdd, 0xa9, 0x54, + 0xac, 0x79, 0x25, 0xad, 0x45, 0xbe, 0xe6, 0x12, 0x64, 0x28, 0x17, 0x20, 0x33, 0x1a, 0xb3, 0xb1, + 0xae, 0x34, 0x97, 0x37, 0xdd, 0xe8, 0x97, 0x35, 0xe3, 0x46, 0x1e, 0xd4, 0x41, 0xaf, 0xa7, 0xa5, + 0x1c, 0xfa, 0x34, 0xe4, 0xd3, 0xe0, 0x7b, 0xaf, 0x90, 0x4e, 0xd8, 0xf4, 0xd1, 0x94, 0x4c, 0x4a, + 0x03, 0x48, 0x43, 0x57, 0x03, 0x7b, 0xa5, 0x21, 0x0d, 0x55, 0xa3, 0x48, 0x08, 0x86, 0x22, 0x35, + 0x81, 0xce, 0xab, 0x1a, 0x43, 0x05, 0xb0, 0xa0, 0x4a, 0x5b, 0xc2, 0x40, 0x05, 0x0a, 0x53, 0xc1, + 0xc0, 0x8e, 0x12, 0x19, 0x97, 0x66, 0x85, 0x9e, 0x29, 0x71, 0x73, 0x27, 0x4d, 0x81, 0x32, 0xe8, + 0x0f, 0x3c, 0xfd, 0x0a, 0xd4, 0x91, 0xa7, 0xb8, 0x40, 0xc2, 0x20, 0xda, 0x81, 0xa6, 0xd2, 0x5b, + 0x49, 0x6f, 0x26, 0xbd, 0x8f, 0xd2, 0xbb, 0x92, 0xa1, 0xe3, 0xac, 0x4c, 0xfe, 0xdd, 0x93, 0x15, + 0xdf, 0x84, 0xcc, 0x3b, 0xe7, 0x75, 0x1f, 0x2e, 0xda, 0x4d, 0x06, 0x21, 0x57, 0x69, 0x44, 0x13, + 0x5e, 0x3b, 0x50, 0xfb, 0xb6, 0xe1, 0x2c, 0x9a, 0xcc, 0x36, 0xa8, 0x28, 0x62, 0x3a, 0x81, 0x46, + 0xb9, 0xf5, 0xb9, 0x43, 0x78, 0xb0, 0xf1, 0x90, 0xbb, 0xa6, 0x3b, 0x5e, 0xd9, 0xb6, 0x22, 0x56, + 0x98, 0xa7, 0xf0, 0xaa, 0x07, 0x23, 0x51, 0x7a, 0xf0, 0xf8, 0x73, 0x29, 0xb2, 0xbb, 0x53, 0x7a, + 0x0a, 0x89, 0x8e, 0x7f, 0x87, 0xd5, 0x1b, 0xf3, 0x7c, 0x4d, 0x0c, 0x6f, 0x9e, 0x29, 0xea, 0x4d, + 0x36, 0x16, 0xf6, 0x82, 0x9d, 0xdb, 0xa2, 0xbb, 0x91, 0xa2, 0x26, 0x18, 0xe3, 0xf4, 0x8e, 0xc8, + 0xe9, 0x28, 0x73, 0xdc, 0x8a, 0x85, 0xbb, 0xfd, 0xe3, 0xdb, 0x37, 0x27, 0xfc, 0x6c, 0x0a, 0xba, + 0xc5, 0x15, 0x53, 0x7b, 0x8c, 0x72, 0x61, 0xbc, 0x31, 0xb8, 0xd6, 0x10, 0xa0, 0x10, 0x5e, 0x9a, + 0x57, 0x0c, 0x6a, 0x30, 0x01, 0xf8, 0x4f, 0x41, 0x18, 0x5a, 0xe1, 0x6d, 0x86, 0xf2, 0x42, 0x16, + 0x4d, 0x4c, 0x01, 0xcc, 0x8f, 0x12, 0xb7, 0x3a, 0x67, 0x26, 0xc3, 0x35, 0xe3, 0xd5, 0x5c, 0x7a, + 0x27, 0xe5, 0xc2, 0x0d, 0xae, 0xa4, 0xb7, 0x8c, 0x03, 0x10, 0x48, 0xae, 0xbd, 0xe2, 0xba, 0x57, + 0x20, 0xbd, 0x57, 0x84, 0xad, 0xc1, 0x63, 0xef, 0x94, 0x30, 0x02, 0x99, 0xf9, 0x7f, 0x20, 0x32, + 0x05, 0xff, 0x46, 0xb5, 0x74, 0xbc, 0x08, 0xd6, 0xca, 0x93, 0xdf, 0x07, 0x90, 0xc4, 0x7f, 0xf3, + 0x78, 0x3a, 0x9f, 0x67, 0x93, 0x77, 0x20, 0xce, 0x06, 0x2f, 0x3d, 0x32, 0x73, 0xfa, 0x80, 0x1c, + 0xfe, 0x9f, 0xfc, 0xfd, 0x4a, 0xdc, 0xbe, 0x86, 0x8a, 0x7f, 0xf7, 0x40, 0x26, 0x7d, 0x9f, 0x42, + 0x28, 0xf8, 0xe7, 0xc6, 0x43, 0x9f, 0xa2, 0x84, 0x97, 0xe4, 0x01, 0x14, 0x04, 0x7c, 0x34, 0x81, + 0xaf, 0x73, 0x00, 0x85, 0x6b, 0x3a, 0xf2, 0x11, 0x07, 0xc2, 0xa3, 0xd7, 0xe9, 0x1f, 0x7f, 0x2c, + 0xcd, 0xb1, 0xa4, 0x8b, 0x3b, 0x0c, 0x0c, 0x10, 0x69, 0x3e, 0xc4, 0xab, 0x4b, 0x2c, 0x30, 0x27, + 0x33, 0x72, 0x61, 0x20, 0x41, 0x6e, 0x14, 0xd3, 0x77, 0xb4, 0xa0, 0xf3, 0xf0, 0x23, 0xa2, 0xec, + 0x06, 0xdf, 0x69, 0x56, 0x4e, 0x48, 0x7f, 0xc5, 0x1d, 0x83, 0x6e, 0x38, 0x58, 0x2b, 0xe5, 0x52, + 0x14, 0x5d, 0xbd, 0xe4, 0x41, 0xb7, 0x6b, 0xb8, 0x16, 0xcd, 0x23, 0x00, 0x8d, 0x94, 0x79, 0x97, + 0x9e, 0x24, 0xa2, 0x1b, 0x12, 0x75, 0x07, 0xa3, 0x84, 0xf2, 0xff, 0x0b, 0x07, 0xa3, 0xf2, 0x6b, + 0x1d, 0x8c, 0xda, 0xe5, 0x18, 0x60, 0x0f, 0x70, 0x03, 0xe1, 0x97, 0x11, 0x58, 0xdb, 0x67, 0x9c, + 0x8e, 0x1a, 0x1a, 0x73, 0x1a, 0x43, 0xc9, 0xe6, 0xf4, 0x86, 0xc9, 0xb1, 0xd4, 0xfc, 0x4c, 0xa2, + 0xdd, 0x92, 0x64, 0x74, 0xdc, 0x3b, 0xcc, 0x7c, 0x91, 0x00, 0x32, 0x93, 0x31, 0x4f, 0x68, 0x06, + 0xd0, 0x75, 0x99, 0x47, 0x2e, 0x88, 0x0a, 0x63, 0x1f, 0xda, 0xe9, 0x59, 0x4b, 0x10, 0x93, 0x63, + 0x2b, 0x27, 0xf3, 0x6f, 0xb2, 0x58, 0xaa, 0x34, 0xb7, 0x51, 0xe3, 0x8f, 0x9c, 0x6b, 0x46, 0x57, + 0xd5, 0x81, 0x43, 0xdc, 0xf0, 0xda, 0x5d, 0x03, 0x3d, 0x3a, 0x39, 0x3d, 0x0d, 0xe8, 0xe2, 0xda, + 0xf2, 0xea, 0x82, 0xe9, 0x62, 0xff, 0xd0, 0xbb, 0x41, 0x37, 0x6d, 0x68, 0x53, 0x34, 0xe4, 0xae, + 0x9b, 0x9a, 0x04, 0x6d, 0x1c, 0xc2, 0x77, 0xed, 0x46, 0xc2, 0x14, 0x13, 0x99, 0xa0, 0x99, 0x64, + 0x82, 0x60, 0xa0, 0x1c, 0xd4, 0xec, 0xa1, 0x88, 0x29, 0xd9, 0x45, 0x5c, 0x41, 0x32, 0xc8, 0xa4, + 0x6a, 0x57, 0x03, 0x80, 0xa7, 0xe8, 0xb3, 0xb5, 0x29, 0x15, 0xbe, 0x01, 0x8d, 0x79, 0xa4, 0x1b, + 0xd8, 0xd8, 0x76, 0xe7, 0xe1, 0x13, 0x9f, 0x8d, 0x17, 0x35, 0xe5, 0xa9, 0x4a, 0x69, 0xb8, 0x8b, + 0x4d, 0x9a, 0xb2, 0xa1, 0xa7, 0x3c, 0xbc, 0x39, 0x8a, 0x57, 0xdc, 0x20, 0xcf, 0x22, 0x24, 0x87, + 0x9f, 0x6f, 0xb3, 0x0f, 0x1a, 0x0d, 0x83, 0xa5, 0xc7, 0xc8, 0x1a, 0xc4, 0x1e, 0xbb, 0x41, 0x09, + 0x76, 0xf7, 0xe8, 0x17, 0xca, 0xf0, 0xf4, 0xe9, 0x1e, 0x7f, 0x40, 0xf3, 0xbf, 0x44, 0xbf, 0x50, + 0xe3, 0x7c, 0xa0, 0x13, 0xa4, 0x9e, 0x72, 0x22, 0xf6, 0x40, 0x25, 0x2f, 0x38, 0xc7, 0xa6, 0xc0, + 0xc4, 0x07, 0xf2, 0xbe, 0x54, 0x29, 0x00, 0x04, 0x1c, 0x67, 0x10, 0x79, 0x89, 0x1a, 0x4c, 0x30, + 0xf1, 0x04, 0x13, 0xed, 0xa6, 0xe2, 0x38, 0x6c, 0x45, 0xd3, 0x51, 0xb9, 0xb4, 0x7a, 0x28, 0xdb, + 0x4a, 0x65, 0xba, 0xba, 0x96, 0x77, 0x4d, 0x99, 0x96, 0xf0, 0x8d, 0x72, 0x06, 0xfd, 0x89, 0xe8, + 0x77, 0x82, 0x57, 0x2c, 0x01, 0x0c, 0xa2, 0xf0, 0x18, 0x14, 0x3b, 0x78, 0x05, 0x78, 0x4f, 0x21, + 0x30, 0x60, 0x6b, 0x0e, 0x11, 0x2a, 0x75, 0xb3, 0x31, 0x3d, 0x93, 0x12, 0x62, 0xc5, 0x97, 0x4e, + 0xff, 0x99, 0xd4, 0x72, 0xb7, 0xd9, 0x28, 0x23, 0xb8, 0x6d, 0x6b, 0x49, 0x4d, 0xe0, 0xb5, 0x07, + 0xd4, 0xd9, 0x75, 0x9d, 0x51, 0x70, 0xee, 0x3b, 0xe7, 0xd3, 0x36, 0x9e, 0xe9, 0x8d, 0x82, 0x33, + 0xf1, 0xd3, 0xd8, 0x39, 0x6b, 0x77, 0xc6, 0x23, 0x8e, 0x7a, 0xdc, 0x2d, 0xdd, 0x62, 0x8d, 0x0a, + 0x7d, 0x63, 0xcf, 0x73, 0xe4, 0x59, 0x7f, 0x3c, 0xc2, 0x9f, 0xc2, 0xe9, 0x90, 0xdb, 0x81, 0xc8, + 0xc1, 0x78, 0xd4, 0xc6, 0xdf, 0x00, 0xcf, 0xfd, 0x7a, 0x1b, 0xc3, 0x7a, 0xd5, 0x30, 0x69, 0x45, + 0x97, 0x7e, 0x2f, 0xe3, 0x5b, 0x31, 0xa5, 0xab, 0x33, 0xdb, 0xba, 0x4a, 0xd1, 0x95, 0x6e, 0x4b, + 0xea, 0x04, 0x83, 0xb4, 0x18, 0xfe, 0x54, 0x05, 0x5e, 0x0f, 0x72, 0xaa, 0x0b, 0x8b, 0xcc, 0xcf, + 0x6a, 0xcb, 0xc9, 0x38, 0x31, 0x44, 0x75, 0xa4, 0x3a, 0x35, 0x3c, 0x89, 0x12, 0x74, 0x4c, 0x3d, + 0xa1, 0xb7, 0xdd, 0xd5, 0x6b, 0xbd, 0x11, 0xfc, 0x2b, 0x1a, 0xb3, 0x81, 0x67, 0x63, 0xca, 0x96, + 0xb8, 0xde, 0xc2, 0x57, 0x48, 0x01, 0x70, 0x1b, 0x29, 0x11, 0x8a, 0xc2, 0xa1, 0x08, 0x16, 0xbe, + 0xba, 0x03, 0x48, 0x97, 0xb5, 0x2b, 0x89, 0x3b, 0x37, 0x7d, 0xb3, 0x46, 0xb3, 0x04, 0xd4, 0x00, + 0x09, 0x88, 0x31, 0x95, 0x68, 0x3f, 0x2b, 0x28, 0x6e, 0xd1, 0x06, 0x13, 0xc0, 0x26, 0xc5, 0x9c, + 0x66, 0xcf, 0x51, 0x05, 0xe6, 0x94, 0xe0, 0xcd, 0x42, 0xb4, 0x51, 0x3a, 0x23, 0x83, 0x7c, 0x74, + 0xa5, 0x8d, 0xb7, 0x27, 0x89, 0x68, 0x06, 0x75, 0x3c, 0xc9, 0xc2, 0xfe, 0x71, 0x5d, 0x76, 0x35, + 0x1c, 0x29, 0x95, 0x72, 0x2b, 0x20, 0x80, 0x19, 0x08, 0xd6, 0x48, 0xba, 0xe8, 0x5e, 0x32, 0x4c, + 0x84, 0x1e, 0x08, 0x22, 0xb6, 0x01, 0xa3, 0xd1, 0x52, 0x5d, 0xc7, 0xc1, 0x77, 0xda, 0x9c, 0x66, + 0x08, 0x3d, 0x5f, 0x2c, 0x1c, 0xb3, 0x88, 0x08, 0xf6, 0x49, 0x0e, 0x39, 0x33, 0x1f, 0xee, 0x1b, + 0xef, 0x03, 0x82, 0x55, 0x26, 0x66, 0x19, 0x9e, 0x89, 0x71, 0xc0, 0x0e, 0x91, 0x71, 0x2d, 0x6f, + 0xb5, 0x26, 0xb0, 0x40, 0xa4, 0x28, 0x1b, 0xfa, 0x9a, 0x0a, 0x85, 0x7f, 0x19, 0x2f, 0x40, 0xd4, + 0x74, 0x8a, 0x8a, 0x78, 0x6e, 0xa8, 0xe7, 0x4b, 0xd7, 0x35, 0x68, 0xaf, 0x52, 0xb9, 0x37, 0x02, + 0x7c, 0x58, 0x41, 0x82, 0xd8, 0xa7, 0xfd, 0xe2, 0xe9, 0x53, 0xc4, 0xb9, 0x37, 0x4b, 0x49, 0x8a, + 0xed, 0xb7, 0x17, 0xb9, 0xc8, 0x80, 0x25, 0xa9, 0x48, 0x24, 0x46, 0xdf, 0x64, 0xb3, 0x26, 0xcc, + 0xcc, 0x81, 0x5a, 0x0c, 0x76, 0xf3, 0x96, 0x37, 0x67, 0x36, 0x90, 0xca, 0xf4, 0x31, 0x66, 0xec, + 0x39, 0xe6, 0x5a, 0xaa, 0x50, 0xfc, 0xcf, 0xcc, 0x06, 0x80, 0x05, 0xf0, 0x69, 0xb2, 0x58, 0x42, + 0xdb, 0xca, 0x00, 0x92, 0x55, 0xe6, 0x58, 0x37, 0x79, 0xcb, 0x73, 0x79, 0xb8, 0xc8, 0x57, 0xc5, + 0xa8, 0x75, 0x83, 0xff, 0xfc, 0x94, 0xc7, 0x5c, 0xd6, 0x7b, 0x91, 0x4e, 0xef, 0x40, 0xca, 0x43, + 0x4e, 0x97, 0x88, 0x2d, 0x6c, 0xb4, 0xf9, 0xf2, 0x42, 0x66, 0x82, 0x0c, 0x76, 0x0b, 0x4a, 0xb1, + 0xac, 0x8a, 0x6e, 0xa2, 0x62, 0x81, 0xc9, 0x2b, 0x9f, 0x6e, 0x3f, 0x32, 0x2e, 0x8b, 0x04, 0x37, + 0x7a, 0x10, 0x50, 0x81, 0x75, 0x0a, 0xb6, 0xc6, 0x67, 0x03, 0x1b, 0x5c, 0x5e, 0x10, 0x06, 0xfe, + 0xa0, 0xe8, 0x8c, 0xfe, 0x50, 0x04, 0x1d, 0x95, 0xc8, 0x80, 0x11, 0x6a, 0x81, 0xb0, 0x52, 0xbd, + 0xb6, 0x3e, 0xdc, 0x75, 0xe2, 0xab, 0xd6, 0xaa, 0xcb, 0xd0, 0x67, 0xc6, 0xa1, 0xb6, 0xfe, 0x95, + 0x8f, 0x5f, 0xbc, 0x78, 0xc9, 0xb0, 0x0c, 0xd5, 0xdd, 0x8d, 0x86, 0x4c, 0x37, 0xe2, 0xe2, 0xe7, + 0x58, 0x7e, 0xf8, 0x92, 0xac, 0x34, 0x9c, 0xe7, 0xd4, 0xa3, 0xd0, 0xbe, 0x8a, 0x92, 0xf8, 0x7a, + 0xb9, 0xe0, 0xb3, 0x13, 0xb5, 0xf0, 0x0b, 0xb7, 0xa6, 0x0e, 0x79, 0x44, 0x6a, 0x26, 0x24, 0xac, + 0xc9, 0x2b, 0x80, 0x08, 0xf3, 0x2a, 0xb3, 0xf4, 0xee, 0x4b, 0x86, 0x6c, 0x54, 0xdf, 0xdf, 0x5d, + 0x3d, 0xbb, 0x07, 0xd0, 0xd5, 0x17, 0x65, 0x76, 0xd1, 0x1c, 0xa9, 0xb5, 0xe5, 0x62, 0x64, 0xd7, + 0x6f, 0xe8, 0xdb, 0x81, 0xdd, 0x70, 0xab, 0xde, 0x1e, 0x9e, 0x55, 0xae, 0x7e, 0x9b, 0x17, 0xbf, + 0xcb, 0x6b, 0xdf, 0x0d, 0x3a, 0x6e, 0x54, 0x5a, 0xe9, 0x1e, 0x03, 0xab, 0x09, 0xe8, 0xd4, 0x80, + 0x69, 0xe4, 0x61, 0x96, 0x58, 0x90, 0xbd, 0xbe, 0x89, 0x6c, 0x80, 0x63, 0x4d, 0x43, 0xc0, 0x8d, + 0xa7, 0x00, 0x14, 0xb0, 0xa8, 0xb0, 0x12, 0x57, 0x4c, 0x95, 0xb6, 0x48, 0x08, 0x9e, 0xf3, 0xc1, + 0x24, 0x02, 0xe1, 0x14, 0x20, 0x1c, 0x91, 0x5f, 0xc4, 0xda, 0x3d, 0x68, 0x20, 0xd3, 0xc6, 0x5e, + 0x49, 0x0e, 0x6d, 0x4a, 0x5f, 0xb7, 0xf6, 0x55, 0x0c, 0x13, 0x0d, 0x34, 0x15, 0x4d, 0x52, 0x1f, + 0xca, 0x16, 0xdd, 0x52, 0x36, 0xb4, 0x5b, 0x88, 0x1e, 0xca, 0x88, 0x6f, 0x80, 0x71, 0x4e, 0xb4, + 0x2e, 0xde, 0x75, 0xbb, 0xda, 0xbb, 0x44, 0x1b, 0xd4, 0xee, 0x84, 0x15, 0x2d, 0xad, 0x0a, 0xf2, + 0x97, 0xbc, 0x72, 0x77, 0x40, 0xd6, 0xa9, 0x7a, 0x1d, 0xf7, 0x9e, 0x39, 0xb2, 0x5a, 0x6c, 0xa9, + 0x6f, 0x77, 0x77, 0x26, 0x74, 0xe6, 0x09, 0x0c, 0x86, 0x0c, 0x7b, 0x68, 0xa3, 0x70, 0x4c, 0x9e, + 0x97, 0x42, 0xb2, 0xf9, 0x39, 0xec, 0x3d, 0x93, 0x23, 0xd9, 0x09, 0xf1, 0xcc, 0xb2, 0x33, 0x68, + 0x01, 0x2f, 0x70, 0x19, 0x1c, 0xaa, 0x1c, 0xed, 0x70, 0x00, 0xad, 0x77, 0x0e, 0x7b, 0x10, 0x09, + 0x12, 0x6d, 0x1b, 0xc8, 0x11, 0xdf, 0xf1, 0x4b, 0x3b, 0xb1, 0xeb, 0x45, 0xe5, 0x4c, 0xe5, 0xcd, + 0x33, 0x65, 0xa0, 0x73, 0x39, 0x49, 0xc4, 0x73, 0xf1, 0x94, 0xf8, 0xca, 0x35, 0xc7, 0x94, 0xac, + 0x77, 0xea, 0xd7, 0xd8, 0xbd, 0x22, 0x9b, 0xb2, 0x21, 0x55, 0xc7, 0xa2, 0x78, 0xf0, 0xb2, 0xc5, + 0x8a, 0x20, 0x77, 0xaf, 0x79, 0x31, 0x32, 0x58, 0x5a, 0x31, 0x69, 0x1e, 0x0a, 0x52, 0x5f, 0xe1, + 0x1e, 0xc1, 0x51, 0x68, 0x32, 0xe9, 0x14, 0x38, 0x51, 0x22, 0x2f, 0xfb, 0x70, 0x87, 0x3d, 0x6f, + 0x64, 0x2b, 0xdd, 0x63, 0xa0, 0xd4, 0x77, 0xb0, 0xad, 0x6f, 0xc6, 0xe4, 0x64, 0x3e, 0x33, 0x8d, + 0x96, 0x00, 0x20, 0x14, 0x07, 0x24, 0xd8, 0x63, 0x97, 0xed, 0x99, 0xb7, 0x44, 0xbd, 0x93, 0xcb, + 0x42, 0x69, 0xf7, 0x3a, 0x9a, 0x09, 0x0b, 0x9f, 0xaf, 0xa0, 0x97, 0x04, 0xd2, 0x89, 0x15, 0x5a, + 0x97, 0xd1, 0x22, 0x17, 0x1e, 0x06, 0xe2, 0xeb, 0x21, 0x27, 0x5c, 0xe7, 0x65, 0x7c, 0x92, 0xfe, + 0x22, 0x6e, 0x4e, 0xc5, 0xac, 0x88, 0x52, 0x79, 0xe2, 0xfc, 0x6d, 0x62, 0xe4, 0x5a, 0x3c, 0x2f, + 0x03, 0x71, 0xfe, 0xb7, 0x95, 0x19, 0xc2, 0x07, 0x70, 0xca, 0x30, 0xba, 0xb5, 0x3a, 0x85, 0xd1, + 0x55, 0x63, 0xde, 0xcf, 0xd0, 0x39, 0x17, 0x16, 0x03, 0xd1, 0x89, 0x4a, 0xfd, 0x3a, 0xbb, 0xb8, + 0xa9, 0xb5, 0x79, 0x33, 0xc7, 0xd7, 0xc9, 0x20, 0xf2, 0xac, 0xe7, 0xc1, 0x7f, 0x63, 0x15, 0x0d, + 0xa2, 0x6d, 0x44, 0x0f, 0x92, 0x40, 0x02, 0x65, 0xd7, 0x09, 0xd7, 0xe8, 0x35, 0x9a, 0xe3, 0xb1, + 0x56, 0x1d, 0x9d, 0x2c, 0x7e, 0x5c, 0x66, 0x10, 0x79, 0xd4, 0x53, 0x11, 0xfc, 0x32, 0xb5, 0x98, + 0xbe, 0xbc, 0x85, 0x58, 0x1d, 0x39, 0x81, 0x58, 0x23, 0x88, 0x8f, 0x2c, 0x4e, 0xf9, 0xa9, 0xc4, + 0xdc, 0xcc, 0xc5, 0x2f, 0x18, 0xa8, 0x37, 0x14, 0x43, 0xab, 0xd3, 0x57, 0x09, 0xe8, 0xb6, 0x8e, + 0x55, 0xa3, 0x66, 0x25, 0x62, 0x76, 0x82, 0x4a, 0x1d, 0x8c, 0x02, 0x90, 0x8b, 0xa9, 0x19, 0x82, + 0xce, 0x42, 0x91, 0x04, 0x9d, 0x5e, 0x70, 0x0c, 0x2c, 0x61, 0x00, 0xbc, 0x4a, 0x55, 0x5f, 0x7a, + 0x64, 0x93, 0x37, 0xf8, 0xee, 0x4c, 0x01, 0x42, 0x0e, 0x1b, 0x73, 0x80, 0xcd, 0xdf, 0x18, 0x05, + 0xb0, 0x4a, 0xbd, 0xe1, 0x41, 0x14, 0xc9, 0xa8, 0x16, 0x91, 0x6b, 0x1c, 0x4d, 0x29, 0xd1, 0xfb, + 0xc4, 0x19, 0x3a, 0x8d, 0xfc, 0x96, 0x67, 0xad, 0x09, 0x8d, 0xf4, 0xf0, 0x62, 0x9e, 0xd7, 0xf5, + 0x66, 0x88, 0xaf, 0xf8, 0x10, 0x34, 0xae, 0xe3, 0xc9, 0x27, 0x88, 0x42, 0x6c, 0xc7, 0x87, 0x52, + 0x0c, 0x55, 0x90, 0x63, 0x7f, 0x7b, 0xad, 0x94, 0xb9, 0xd6, 0xfa, 0xd1, 0x37, 0xac, 0xe2, 0xb4, + 0x06, 0x47, 0x3d, 0x0f, 0x02, 0x55, 0x3d, 0x23, 0x8f, 0xe1, 0xd1, 0x37, 0xd4, 0xd8, 0xa3, 0x6f, + 0x0a, 0x9e, 0x9f, 0x75, 0x9c, 0xbf, 0xa1, 0xf9, 0x49, 0xbc, 0x88, 0xe5, 0x9d, 0x32, 0xf0, 0x76, + 0xb1, 0xbe, 0xf8, 0xd2, 0x72, 0xf6, 0x8a, 0xad, 0x7d, 0x1e, 0x03, 0xa7, 0x94, 0x58, 0x4f, 0x9f, + 0x52, 0x4f, 0x7e, 0xc4, 0x49, 0x80, 0x6c, 0x1d, 0x73, 0x52, 0x9e, 0x59, 0x07, 0x3d, 0xa0, 0x35, + 0x58, 0x76, 0xeb, 0xf9, 0x27, 0x68, 0x76, 0x43, 0xff, 0x8c, 0xc6, 0xd3, 0x04, 0x35, 0x69, 0xdc, + 0x18, 0x9e, 0x5c, 0x39, 0x98, 0x0b, 0x5b, 0xad, 0xb9, 0x40, 0x28, 0x9c, 0x1e, 0x58, 0x61, 0x68, + 0xd9, 0xc0, 0x5d, 0x8a, 0xc0, 0xa6, 0x42, 0xbc, 0xda, 0x10, 0x0f, 0x87, 0x14, 0x88, 0xaf, 0x21, + 0x88, 0xa5, 0x16, 0xa7, 0xc0, 0x64, 0xc1, 0xba, 0x44, 0x3a, 0xf2, 0x5a, 0x8a, 0x2b, 0x67, 0x1f, + 0x62, 0x5f, 0x5f, 0xef, 0xeb, 0x06, 0xf6, 0x28, 0xb3, 0xfb, 0xe8, 0x9b, 0x75, 0x59, 0x0e, 0x1a, + 0x01, 0x16, 0xd5, 0xb1, 0x5f, 0x42, 0xfd, 0x16, 0x4e, 0xbd, 0x6f, 0xbd, 0x03, 0xe9, 0x37, 0x17, + 0x16, 0x0c, 0x5f, 0x64, 0xfc, 0xe6, 0xd2, 0xeb, 0x77, 0x7b, 0xb6, 0xcb, 0x8d, 0x95, 0x8d, 0xe4, + 0xd5, 0x46, 0xd4, 0xc2, 0x37, 0x87, 0x4c, 0x13, 0x49, 0xf8, 0x82, 0x3d, 0x52, 0xdc, 0xd1, 0x8b, + 0xbb, 0xd7, 0x53, 0x67, 0x7f, 0x92, 0x2f, 0xf6, 0xdd, 0xd2, 0x91, 0x2f, 0x80, 0x27, 0xcd, 0x2c, + 0x27, 0x26, 0x3c, 0xb3, 0x62, 0xeb, 0x18, 0xca, 0x69, 0x85, 0x8e, 0x85, 0x67, 0x98, 0x38, 0xee, + 0xc9, 0x14, 0x0f, 0x32, 0x99, 0x75, 0x41, 0x47, 0x30, 0x33, 0x12, 0xf1, 0xf8, 0x51, 0x7b, 0x80, + 0x10, 0x9a, 0x67, 0x03, 0x7e, 0xe3, 0x3f, 0xd7, 0xe6, 0x5e, 0xf0, 0x72, 0xc4, 0x17, 0x4e, 0xf1, + 0x95, 0xeb, 0x47, 0xdf, 0xa8, 0x37, 0xbd, 0xd5, 0x73, 0x87, 0x1c, 0x47, 0xeb, 0xed, 0x83, 0x8c, + 0x17, 0x34, 0x0f, 0x84, 0x77, 0x3e, 0x4c, 0x9f, 0x79, 0x5e, 0x61, 0xf0, 0xba, 0x34, 0x69, 0x42, + 0x52, 0xa3, 0x0e, 0x4e, 0x01, 0x8d, 0x17, 0x7f, 0xca, 0x67, 0x42, 0x9c, 0x0a, 0xeb, 0x58, 0xc5, + 0x08, 0x8f, 0x51, 0x12, 0xb8, 0x01, 0x6b, 0x70, 0x88, 0xc5, 0xa6, 0xdb, 0x6e, 0x84, 0xd8, 0x2e, + 0x8a, 0x10, 0x53, 0x1f, 0x14, 0xed, 0xc0, 0x58, 0x5d, 0x1b, 0x61, 0xf7, 0x16, 0x90, 0xed, 0xc9, + 0xca, 0x76, 0x15, 0xbc, 0xd4, 0xcd, 0xe1, 0xb0, 0xa7, 0x50, 0xa1, 0x19, 0x5d, 0xae, 0x27, 0x57, + 0xfb, 0x2e, 0xa1, 0x1b, 0x0e, 0x0d, 0xb0, 0x4d, 0xbd, 0x36, 0x4b, 0x34, 0xa1, 0x1c, 0x6e, 0x05, + 0x9d, 0x6b, 0x30, 0x95, 0xd1, 0xc5, 0x6b, 0xc6, 0x30, 0xfd, 0xaa, 0x64, 0x6d, 0xfe, 0xf3, 0x17, + 0x77, 0x27, 0xda, 0xb1, 0xbe, 0x53, 0x3e, 0x3d, 0xe9, 0x36, 0xe0, 0x80, 0x4e, 0xdc, 0xc2, 0x04, + 0x9d, 0x80, 0xf8, 0x50, 0x78, 0xe9, 0xc7, 0x35, 0xd1, 0x14, 0x5f, 0x3c, 0x61, 0x60, 0x5b, 0x11, + 0x9d, 0xac, 0xa1, 0x15, 0xa2, 0xad, 0x06, 0x82, 0xb0, 0x60, 0x92, 0xe7, 0xaa, 0x37, 0xb9, 0x86, + 0x46, 0xf5, 0x38, 0x18, 0xb3, 0x85, 0x36, 0x40, 0x46, 0x57, 0xb2, 0x05, 0x08, 0xfd, 0x64, 0x26, + 0x16, 0x82, 0x89, 0x41, 0xa7, 0x46, 0x9a, 0x8a, 0x16, 0x84, 0x45, 0x51, 0x5b, 0x20, 0x27, 0x7b, + 0x94, 0xc1, 0x68, 0x94, 0x4d, 0x00, 0xad, 0xf2, 0x99, 0x72, 0xaf, 0x18, 0x0e, 0xce, 0x19, 0xe9, + 0xb7, 0x79, 0x88, 0xaf, 0x21, 0xfc, 0xf1, 0x84, 0xbb, 0x85, 0x48, 0xe3, 0xf3, 0x2c, 0x39, 0xfb, + 0xf9, 0x55, 0x9a, 0xca, 0xf9, 0xbe, 0x81, 0x15, 0x90, 0x8d, 0xe7, 0x9f, 0xde, 0x2b, 0x56, 0xea, + 0xca, 0xfd, 0x4e, 0x27, 0x86, 0x4c, 0x5c, 0x65, 0xc3, 0xb2, 0xa0, 0x29, 0x1c, 0x1a, 0x6b, 0x58, + 0x32, 0x5e, 0x0f, 0x8d, 0xc1, 0xe6, 0xf3, 0xf4, 0xe6, 0x3d, 0x3e, 0xdd, 0xe9, 0x90, 0x67, 0x47, + 0x4b, 0xa0, 0x32, 0xa4, 0x32, 0x5c, 0xf2, 0xbd, 0xd4, 0xb4, 0xf6, 0xd5, 0x93, 0x9f, 0xd8, 0xc2, + 0x6d, 0xf9, 0xae, 0x07, 0x0e, 0x0d, 0x6a, 0xa2, 0x48, 0x73, 0x4a, 0xb9, 0xe2, 0x91, 0x65, 0xd3, + 0x07, 0xb0, 0x2d, 0xd8, 0x32, 0x02, 0x7f, 0x02, 0x54, 0x2a, 0xd3, 0x2b, 0x4e, 0xf5, 0x90, 0xeb, + 0xe4, 0x11, 0x83, 0x38, 0x73, 0x45, 0x74, 0x14, 0x2a, 0xd9, 0x47, 0x11, 0x68, 0x9f, 0x12, 0xd9, + 0x28, 0x94, 0xcf, 0x92, 0x76, 0xe4, 0x26, 0x06, 0xdb, 0x02, 0x34, 0xe0, 0x4a, 0x21, 0xa6, 0x79, + 0x6d, 0x5b, 0xd5, 0x9e, 0xde, 0x36, 0xa1, 0x1c, 0xf5, 0x96, 0xf1, 0xcd, 0xc2, 0x35, 0xff, 0x43, + 0xaf, 0xb7, 0xbd, 0x84, 0x30, 0x13, 0x69, 0x93, 0x18, 0xa2, 0x4c, 0x64, 0x0a, 0x00, 0xef, 0x9f, + 0xa4, 0x00, 0x24, 0xe5, 0x63, 0x2a, 0xb5, 0xc8, 0x51, 0x00, 0xc0, 0x19, 0xe8, 0xf5, 0xb4, 0xa0, + 0xd5, 0x19, 0x3f, 0x32, 0x48, 0x8f, 0xa7, 0x21, 0x67, 0xe6, 0x5b, 0xaf, 0xf9, 0x25, 0xb5, 0x38, + 0xcf, 0x97, 0xf4, 0xb4, 0x70, 0x0e, 0x88, 0x92, 0x7b, 0xea, 0xd9, 0x36, 0xeb, 0x2e, 0x05, 0xbe, + 0xe5, 0xa7, 0xd3, 0x77, 0x3e, 0xe0, 0x81, 0xb1, 0xac, 0x8b, 0x1e, 0x11, 0x70, 0xeb, 0x5d, 0xda, + 0x39, 0x93, 0x15, 0x30, 0x7c, 0x41, 0xae, 0x12, 0x3c, 0x3c, 0xa9, 0xc6, 0x7a, 0x34, 0xfa, 0x00, + 0xd5, 0xfc, 0x8a, 0x6f, 0xcd, 0x5d, 0x89, 0x53, 0x99, 0x39, 0x99, 0x64, 0xaa, 0x42, 0xf8, 0x88, + 0xfb, 0x96, 0xbe, 0xa0, 0x9e, 0x49, 0xcd, 0x49, 0x4c, 0xa3, 0x3b, 0x24, 0x36, 0xc6, 0x9d, 0x48, + 0xd9, 0xfd, 0xfe, 0xe8, 0x3b, 0x86, 0x38, 0x39, 0x97, 0xcf, 0x6a, 0xe9, 0x8e, 0x84, 0xad, 0x1c, + 0x8b, 0xb5, 0x38, 0x5f, 0xf7, 0xe0, 0xa8, 0xcc, 0x0d, 0xe2, 0xd1, 0xc3, 0xd9, 0x21, 0x00, 0x35, + 0xb6, 0xa8, 0x4c, 0xf7, 0xa8, 0x28, 0x07, 0xe2, 0x2f, 0x82, 0x01, 0x3b, 0x33, 0xb2, 0x1c, 0xfa, + 0xdb, 0x06, 0xb2, 0x61, 0xc3, 0x2f, 0x87, 0x80, 0xbe, 0xf6, 0x11, 0xa3, 0x21, 0x60, 0x5b, 0x01, + 0xfd, 0x05, 0xfa, 0x87, 0x99, 0x00, 0x10, 0x2e, 0xc6, 0x20, 0x7e, 0x63, 0x35, 0x40, 0x6f, 0x1c, + 0xec, 0xf4, 0xfd, 0x3d, 0xd5, 0xe7, 0x62, 0x85, 0x18, 0x2e, 0xea, 0xa3, 0x11, 0xa9, 0xea, 0xe6, + 0x30, 0xa7, 0x54, 0x1f, 0x7e, 0x40, 0x85, 0x45, 0x4d, 0xcc, 0xd1, 0x60, 0xcb, 0x40, 0x77, 0xa0, + 0x84, 0x6b, 0xa9, 0xba, 0xb1, 0x3d, 0x95, 0x2e, 0x81, 0x8d, 0x39, 0xfc, 0x81, 0x08, 0x13, 0xb7, + 0xa4, 0xb2, 0x10, 0x10, 0xb0, 0x39, 0x14, 0x16, 0x8b, 0xbc, 0xc7, 0x16, 0x0e, 0x1a, 0x73, 0x73, + 0xb9, 0xe6, 0x1a, 0x39, 0x57, 0x91, 0x46, 0xd0, 0xc3, 0xfa, 0x5a, 0x00, 0x2b, 0xaa, 0x33, 0x17, + 0x13, 0x1b, 0x17, 0x9a, 0x12, 0xf2, 0x20, 0xdf, 0x16, 0x12, 0x20, 0x67, 0x98, 0xa5, 0x37, 0x0e, + 0x1e, 0xaf, 0xe0, 0x8d, 0x5e, 0xcf, 0xc2, 0xb7, 0x1f, 0x71, 0xbb, 0xb7, 0x19, 0x1f, 0x54, 0xd9, + 0x3f, 0x8e, 0x65, 0xf6, 0xec, 0x58, 0x4e, 0xf5, 0xb3, 0x7f, 0x90, 0x5f, 0x4e, 0xed, 0x67, 0x8f, + 0x51, 0x82, 0xdb, 0x1c, 0x77, 0xe5, 0xd4, 0x4c, 0x84, 0x7a, 0x38, 0x11, 0x3e, 0x36, 0x8f, 0xd7, + 0x58, 0xa3, 0xca, 0xd3, 0x85, 0x5a, 0xfe, 0xd8, 0xea, 0xc4, 0x75, 0x4a, 0x8a, 0x11, 0x7e, 0x8d, + 0x33, 0x2e, 0xf1, 0x70, 0x82, 0xaf, 0xb6, 0x69, 0xdc, 0x12, 0x11, 0xb2, 0x53, 0xb1, 0x0f, 0x0b, + 0x51, 0x60, 0x00, 0x65, 0x49, 0xdc, 0x6d, 0x55, 0x02, 0xfe, 0x29, 0x74, 0xd5, 0x7d, 0x8d, 0x2b, + 0xd7, 0x37, 0x19, 0x15, 0x82, 0xb5, 0x9c, 0xfb, 0x10, 0x28, 0xa3, 0x97, 0x38, 0xc6, 0x5f, 0x58, + 0xb1, 0x3c, 0xa1, 0xd6, 0xa7, 0x1a, 0xb2, 0x58, 0xe8, 0x19, 0x7a, 0x33, 0x41, 0xa6, 0x13, 0x03, + 0xdd, 0x90, 0x42, 0x43, 0x55, 0x1d, 0xfc, 0x16, 0x2d, 0x15, 0x99, 0x11, 0x77, 0x7a, 0x80, 0x11, + 0xd0, 0xb4, 0xae, 0x1e, 0x93, 0x70, 0x1a, 0x9e, 0xdb, 0x43, 0x18, 0xaf, 0xba, 0xa5, 0xad, 0xab, + 0xd7, 0x75, 0x87, 0xd6, 0x61, 0xcf, 0x6a, 0x99, 0x2a, 0x75, 0x88, 0xed, 0x82, 0xd0, 0xbc, 0x55, + 0xcd, 0x15, 0xd7, 0x83, 0xfd, 0x5f, 0xc2, 0x94, 0xe5, 0x0c, 0x1b, 0xda, 0xd3, 0xc9, 0x51, 0x28, + 0xde, 0xc3, 0x8c, 0xfd, 0x65, 0x09, 0x3e, 0x80, 0x3e, 0x0d, 0x7e, 0x79, 0xf6, 0x69, 0xac, 0x86, + 0x06, 0x51, 0x78, 0x1d, 0x0a, 0xa9, 0x0d, 0xd5, 0x81, 0x78, 0x53, 0xa0, 0x00, 0xca, 0xe0, 0xa8, + 0xd4, 0x50, 0x99, 0x70, 0x9a, 0x2c, 0xea, 0xf5, 0xce, 0xdc, 0x25, 0x33, 0x4a, 0xed, 0x4d, 0x90, + 0xba, 0xdb, 0x3f, 0x2f, 0x97, 0xf3, 0xe8, 0xd3, 0x52, 0x43, 0x33, 0xf6, 0x57, 0x22, 0xf3, 0x49, + 0xf5, 0x93, 0xff, 0x23, 0x96, 0x73, 0x07, 0x5f, 0x31, 0xef, 0xf9, 0xb8, 0x7e, 0x54, 0x01, 0x20, + 0x59, 0xf1, 0x2c, 0x8a, 0xcb, 0x02, 0x93, 0x44, 0xa7, 0xe1, 0x37, 0x31, 0x5f, 0x10, 0x82, 0xc6, + 0xff, 0x58, 0x3d, 0x5e, 0x53, 0x7d, 0x1b, 0xcb, 0x06, 0xec, 0x9a, 0x24, 0x1b, 0x5b, 0x3f, 0x1b, + 0x7a, 0x4c, 0x87, 0x88, 0xc5, 0xab, 0x94, 0xd0, 0x4b, 0x7a, 0xeb, 0x1c, 0xb0, 0x0f, 0x7b, 0xbe, + 0xc1, 0x2f, 0xdd, 0x75, 0xfb, 0xc5, 0x12, 0xd8, 0x62, 0xdb, 0x83, 0x9a, 0xe2, 0xa9, 0x5b, 0x4d, + 0x3a, 0x8d, 0x67, 0x49, 0x44, 0xd7, 0x93, 0x88, 0x25, 0xc2, 0x4c, 0x37, 0xf1, 0x65, 0xec, 0xe7, + 0x1c, 0xdf, 0xb6, 0x9f, 0x58, 0x8e, 0xdd, 0xb6, 0x54, 0x6c, 0x96, 0xe7, 0xc0, 0x52, 0xd8, 0xd6, + 0xf4, 0xc5, 0x95, 0x6b, 0xd7, 0x6a, 0xfa, 0x70, 0x8d, 0xa4, 0x95, 0x74, 0x9d, 0x06, 0x99, 0x85, + 0xd9, 0xa0, 0x78, 0xb7, 0x96, 0xfb, 0x25, 0x60, 0x35, 0xe1, 0xb0, 0xed, 0xe1, 0xaf, 0x67, 0x5b, + 0x9f, 0x5e, 0xd4, 0x6b, 0xfc, 0x09, 0x04, 0xb9, 0x2b, 0x44, 0x54, 0x2d, 0xcb, 0xda, 0x1e, 0xa2, + 0x48, 0x2d, 0xd7, 0x9b, 0xe7, 0x27, 0x16, 0xb0, 0xbc, 0x19, 0xb9, 0xba, 0x89, 0xfd, 0xab, 0x68, + 0x52, 0xaf, 0x26, 0x59, 0x81, 0xc4, 0x97, 0x5c, 0x51, 0x05, 0xb1, 0x8f, 0x6e, 0xdd, 0x0a, 0x82, + 0x87, 0x3e, 0x79, 0x33, 0x41, 0x41, 0x87, 0xc3, 0x8b, 0x1b, 0x10, 0x63, 0x20, 0xcc, 0xe3, 0x83, + 0x05, 0x8c, 0x60, 0xa6, 0x35, 0xbc, 0x2d, 0x74, 0x7c, 0x5a, 0xed, 0xbb, 0x15, 0x8e, 0x83, 0x26, + 0xaf, 0x79, 0xad, 0xeb, 0x97, 0xd3, 0x9d, 0xdc, 0x5c, 0xef, 0x44, 0x78, 0x88, 0xd9, 0x9f, 0x3d, + 0x20, 0x70, 0x0f, 0x4d, 0x21, 0x5b, 0x2d, 0x81, 0x3b, 0x44, 0x7f, 0xb4, 0x5b, 0x9b, 0x71, 0x7d, + 0xba, 0x86, 0x76, 0xbb, 0x90, 0x79, 0xf1, 0xb0, 0x29, 0xa4, 0x2c, 0x67, 0xff, 0xa9, 0xee, 0xda, + 0x9f, 0x1b, 0x37, 0x8e, 0xf4, 0xcf, 0x97, 0xaa, 0xfc, 0x0f, 0x20, 0x62, 0x1f, 0x08, 0x8b, 0x4f, + 0xad, 0x94, 0x5b, 0x93, 0xa2, 0x5c, 0x8e, 0xe3, 0xcb, 0x6d, 0x6a, 0xb3, 0x71, 0x79, 0xd7, 0x95, + 0xb8, 0xb6, 0xb6, 0x2c, 0x3e, 0x40, 0x11, 0x59, 0x88, 0x40, 0x11, 0xd4, 0x8a, 0x2a, 0x85, 0xff, + 0xfb, 0xf5, 0xd7, 0x3d, 0x03, 0xcc, 0x00, 0x03, 0x92, 0x72, 0xec, 0x5c, 0x5d, 0xd5, 0xd6, 0x8a, + 0x98, 0x67, 0x4f, 0xcf, 0xab, 0xa7, 0xfb, 0x9b, 0x9e, 0x47, 0xad, 0x61, 0x88, 0xcd, 0xad, 0x0e, + 0x09, 0x7a, 0x34, 0x24, 0x28, 0x4e, 0x29, 0x24, 0x48, 0xd0, 0xc5, 0x28, 0x44, 0x78, 0xba, 0xd6, + 0xe3, 0x13, 0x3b, 0x82, 0x49, 0x52, 0x58, 0x25, 0x30, 0x3e, 0x1b, 0x16, 0x69, 0xaf, 0x99, 0xd6, + 0x50, 0x53, 0x1c, 0x9b, 0xa3, 0x99, 0x9f, 0xc1, 0x55, 0x23, 0x96, 0xa8, 0xe2, 0x17, 0x74, 0xf9, + 0x4d, 0x76, 0x1d, 0xc8, 0x2f, 0x07, 0x78, 0xf9, 0x7c, 0xf5, 0x91, 0x9f, 0xd7, 0xfd, 0xcf, 0xf5, + 0x2c, 0x87, 0xe6, 0xe7, 0x8a, 0xcf, 0x4e, 0x9e, 0x9c, 0xf9, 0x39, 0xcd, 0x2c, 0xdd, 0xc9, 0x63, + 0xc2, 0x54, 0x0c, 0x75, 0xf5, 0x9e, 0x0e, 0x66, 0xfc, 0x1c, 0xab, 0x40, 0xc7, 0x29, 0x14, 0xd5, + 0xb7, 0x11, 0x13, 0xfa, 0x1e, 0xc6, 0x42, 0x0e, 0xb5, 0x69, 0x82, 0x6d, 0x4d, 0xbf, 0x4e, 0x30, + 0xf2, 0xfd, 0x7d, 0xf5, 0x89, 0x59, 0x8e, 0xbb, 0x9b, 0x6e, 0x84, 0x06, 0xbc, 0x14, 0x5f, 0x3c, + 0x0b, 0xcb, 0x74, 0xea, 0xa7, 0x7c, 0xcb, 0x36, 0xc0, 0x5b, 0xa0, 0xf5, 0x10, 0x2c, 0x2a, 0xfe, + 0x76, 0xa7, 0xaa, 0xa6, 0xf4, 0xaa, 0xdf, 0x41, 0xc4, 0xde, 0x7a, 0x5e, 0xd6, 0x7a, 0x4f, 0x56, + 0xf0, 0x47, 0x5d, 0x7c, 0x78, 0xcb, 0x64, 0x43, 0xa9, 0xb5, 0xea, 0x08, 0x3d, 0x02, 0x69, 0x75, + 0x97, 0x31, 0xc5, 0x45, 0xab, 0x23, 0x14, 0x68, 0xd4, 0x2c, 0xe9, 0x75, 0xbd, 0x78, 0x85, 0xf6, + 0xc5, 0x97, 0x97, 0xfc, 0x0a, 0x6d, 0x8d, 0x66, 0x1a, 0x38, 0xae, 0xf2, 0xf9, 0xd3, 0xae, 0x84, + 0xeb, 0x40, 0x01, 0xd6, 0x62, 0x43, 0x11, 0x5b, 0x09, 0xe5, 0xe7, 0x6d, 0xcb, 0x7d, 0x10, 0x11, + 0xb4, 0x0f, 0xb2, 0xd7, 0x1e, 0x8f, 0xdf, 0x6f, 0xa6, 0x3d, 0xd0, 0x9d, 0x22, 0xcd, 0xcc, 0x04, + 0x7d, 0x55, 0x92, 0xbb, 0x3c, 0xd5, 0xfd, 0x2a, 0x70, 0x9d, 0x32, 0xb3, 0xa0, 0xb1, 0x5a, 0xdb, + 0x63, 0xc0, 0x57, 0x23, 0x44, 0x3d, 0x6e, 0x02, 0xd9, 0x60, 0xe2, 0x0f, 0x7c, 0x68, 0xab, 0x26, + 0xb4, 0x7c, 0x6a, 0xa5, 0x56, 0x77, 0x48, 0x0d, 0x14, 0x25, 0xa9, 0xaf, 0x87, 0x06, 0x28, 0x66, + 0x6e, 0x72, 0x55, 0x13, 0x5f, 0x4e, 0x30, 0xaf, 0xa3, 0xb5, 0x66, 0x68, 0x63, 0x53, 0x4e, 0x24, + 0x2e, 0x3a, 0x91, 0x38, 0x07, 0x69, 0x69, 0x76, 0x12, 0x65, 0x8a, 0x87, 0x6a, 0xc1, 0x7a, 0x6e, + 0x9f, 0xfd, 0x89, 0xf6, 0xe2, 0x2c, 0x5e, 0xdf, 0x36, 0x77, 0x19, 0x0e, 0xfc, 0x65, 0xfc, 0x2f, + 0xd2, 0x63, 0xb7, 0x9b, 0xcc, 0xc9, 0x96, 0xa1, 0x62, 0xcb, 0xf9, 0xe5, 0x65, 0x95, 0x1d, 0x94, + 0xe5, 0x57, 0xed, 0xa7, 0x3c, 0x9b, 0x1f, 0xec, 0x29, 0x07, 0x49, 0x94, 0xe5, 0xe7, 0x75, 0x90, + 0x31, 0x2b, 0x57, 0xde, 0x2c, 0xb3, 0xe8, 0x48, 0xa2, 0x35, 0x72, 0x1e, 0x5f, 0x2d, 0x20, 0x2d, + 0x7d, 0xf6, 0x64, 0x2c, 0xdf, 0x34, 0x99, 0xa7, 0xf3, 0x6d, 0x7d, 0x1e, 0x53, 0x42, 0xfb, 0xc5, + 0x6a, 0x5a, 0x99, 0xbe, 0x7b, 0xd8, 0x98, 0x2b, 0xc6, 0xe0, 0xe5, 0xd2, 0xb5, 0x62, 0x70, 0x6d, + 0x0f, 0x1b, 0x12, 0x3b, 0x63, 0x92, 0x14, 0x1e, 0x72, 0x79, 0xf9, 0x9c, 0xd9, 0x69, 0x94, 0x3f, + 0xdb, 0xc4, 0x7e, 0x8d, 0xbd, 0x71, 0x6e, 0x2f, 0xcb, 0xa8, 0xf4, 0x0f, 0x9b, 0x58, 0xaf, 0xcc, + 0x15, 0xa6, 0xbd, 0xdb, 0xd0, 0x29, 0x54, 0x8c, 0xb8, 0x26, 0xbb, 0xf5, 0x98, 0x30, 0x75, 0xbe, + 0x95, 0x3e, 0xa0, 0xca, 0xa9, 0xb5, 0x7d, 0x27, 0xe5, 0x0a, 0x3b, 0x6a, 0xb0, 0xf3, 0x04, 0xb6, + 0xce, 0xd7, 0xcb, 0x82, 0x7b, 0xf3, 0xca, 0x52, 0x2b, 0xcd, 0xb0, 0x97, 0xda, 0x81, 0x66, 0xdc, + 0x81, 0x32, 0x17, 0xd8, 0xa4, 0x54, 0x99, 0x8b, 0x4a, 0x99, 0x0b, 0x73, 0xc7, 0x92, 0xce, 0x78, + 0xf1, 0x5f, 0xba, 0x4c, 0xc7, 0x1e, 0xb9, 0x89, 0x3e, 0xe9, 0x5d, 0xf2, 0xfb, 0x08, 0x78, 0xc2, + 0xc8, 0x5b, 0x68, 0xbc, 0xeb, 0x29, 0x1b, 0x26, 0xe5, 0xaf, 0xf4, 0x0c, 0x15, 0x53, 0xd9, 0x30, + 0x29, 0xcd, 0x2f, 0xb1, 0x61, 0x5a, 0x5c, 0x87, 0xe8, 0x5a, 0x1c, 0x88, 0xea, 0x12, 0x15, 0xb8, + 0x9d, 0xae, 0xb7, 0x2e, 0xb1, 0x4a, 0xb4, 0x8c, 0x86, 0xdc, 0x71, 0x3d, 0x51, 0x76, 0x08, 0xb7, + 0x32, 0x01, 0x65, 0x01, 0xb1, 0x59, 0x29, 0x2b, 0xa8, 0xbe, 0x29, 0xff, 0x97, 0xe9, 0x2e, 0xbe, + 0xbb, 0xbf, 0xf3, 0x64, 0xba, 0x7b, 0xe9, 0x12, 0x8b, 0x02, 0x8b, 0x6a, 0x1e, 0xfc, 0xd0, 0xaf, + 0xa2, 0x85, 0x7e, 0x1d, 0x1e, 0x0a, 0x1f, 0xd3, 0xe8, 0xa4, 0x34, 0xe6, 0xea, 0xd0, 0x00, 0xfa, + 0x8a, 0x58, 0xa5, 0xbd, 0x37, 0x35, 0xc1, 0x2e, 0x73, 0xd5, 0xbe, 0xae, 0xa3, 0xd4, 0x72, 0x5c, + 0xa9, 0x9c, 0x2c, 0x17, 0x94, 0xd8, 0x50, 0xae, 0xf1, 0x54, 0xa9, 0x35, 0xfa, 0xa6, 0x9c, 0x8c, + 0x37, 0xa1, 0xd3, 0x5a, 0x60, 0x66, 0x8f, 0x44, 0x74, 0x04, 0xe5, 0xd1, 0x44, 0xec, 0xe2, 0xb6, + 0xfe, 0x30, 0xdb, 0xa4, 0xb7, 0x10, 0xac, 0xa1, 0x37, 0x13, 0xbb, 0xa2, 0xf7, 0x05, 0x9d, 0x0c, + 0x3d, 0x38, 0x7f, 0x80, 0xd0, 0x58, 0x46, 0x17, 0x32, 0xa5, 0x0e, 0xd3, 0x27, 0x52, 0x39, 0x94, + 0xdd, 0xc8, 0x2d, 0xec, 0xee, 0xad, 0xbe, 0x6d, 0xfb, 0xe5, 0x60, 0x11, 0xdd, 0x76, 0xc4, 0x6e, + 0x86, 0xf5, 0x4b, 0xe5, 0xda, 0x7f, 0xde, 0xf1, 0x7e, 0x77, 0x71, 0x71, 0x61, 0x05, 0x85, 0x18, + 0x30, 0x91, 0xf9, 0xe6, 0x5f, 0x83, 0xf6, 0x37, 0xb0, 0x26, 0x7c, 0x10, 0xd2, 0xe1, 0xae, 0xa6, + 0xde, 0x27, 0x62, 0x88, 0xa4, 0x06, 0xad, 0x33, 0x98, 0x9c, 0x97, 0x4c, 0x69, 0x35, 0xf0, 0x37, + 0xdf, 0xe7, 0xc4, 0x5d, 0x9b, 0x55, 0x2c, 0x42, 0x98, 0x8c, 0x38, 0x94, 0x57, 0x78, 0x59, 0x2a, + 0x78, 0x48, 0x1a, 0x3a, 0x29, 0x6b, 0x54, 0xcd, 0x4a, 0xdb, 0x04, 0xd4, 0x89, 0x28, 0xa0, 0x2b, + 0x24, 0xa8, 0x08, 0xd1, 0x33, 0xfa, 0x6d, 0x79, 0xfa, 0x28, 0xf4, 0xd5, 0xfc, 0xa1, 0xe4, 0x74, + 0x78, 0xe7, 0x61, 0x25, 0x29, 0x6e, 0x20, 0x7a, 0xac, 0xf7, 0x10, 0xc6, 0xf2, 0x1b, 0x7b, 0x30, + 0x73, 0xd9, 0x13, 0x2b, 0xb5, 0x3f, 0x44, 0x42, 0x5f, 0x31, 0x0f, 0x89, 0x1a, 0x49, 0xa5, 0x8d, + 0xfa, 0x26, 0xf4, 0x5a, 0xa2, 0xfd, 0x2c, 0x8f, 0x3c, 0x14, 0x7c, 0x52, 0x4b, 0x39, 0x7b, 0x85, + 0x4d, 0xd9, 0xfc, 0x34, 0xfe, 0x66, 0x73, 0x33, 0x2b, 0xa8, 0xe4, 0x5a, 0x27, 0xd0, 0x41, 0x48, + 0xfd, 0xda, 0x14, 0x4a, 0x07, 0xc3, 0xad, 0x56, 0xc9, 0xcd, 0x23, 0x9a, 0x11, 0xd4, 0xe6, 0x3e, + 0xa7, 0x3e, 0x43, 0x75, 0x61, 0x61, 0xe8, 0x12, 0xdc, 0x27, 0x92, 0x87, 0xd0, 0x52, 0x71, 0x12, + 0x62, 0x24, 0x14, 0x69, 0x20, 0x0b, 0xea, 0x8d, 0x90, 0x79, 0x8e, 0x63, 0x8e, 0x47, 0xeb, 0x28, + 0x92, 0xee, 0xb9, 0xfc, 0xfb, 0x69, 0x12, 0x36, 0xaf, 0x78, 0x9a, 0x68, 0xaa, 0xf8, 0xc6, 0x5e, + 0xa8, 0x44, 0x41, 0xee, 0x1a, 0xa5, 0x3f, 0xbc, 0x6a, 0x0b, 0x43, 0xeb, 0x6b, 0xde, 0xec, 0x7e, + 0xbb, 0x4d, 0xd7, 0xdf, 0x41, 0x1a, 0x08, 0x6c, 0x35, 0x6a, 0x1b, 0x66, 0xf1, 0x50, 0x49, 0x07, + 0xb0, 0x37, 0x8c, 0xf8, 0xe0, 0xd9, 0x54, 0xc4, 0x9b, 0xa4, 0x9a, 0x7f, 0x9d, 0x7c, 0x7d, 0x7a, + 0xf6, 0xb7, 0x8f, 0xeb, 0x79, 0xb5, 0x00, 0x6d, 0x5e, 0xaf, 0x97, 0xe2, 0x2a, 0x67, 0xb9, 0x9b, + 0x05, 0xe8, 0x85, 0xc2, 0x00, 0x1e, 0x36, 0x5b, 0xea, 0x7e, 0x07, 0x27, 0x1d, 0xc7, 0x96, 0x46, + 0xb5, 0x38, 0x90, 0xf4, 0x11, 0x84, 0x47, 0xd7, 0x51, 0x95, 0xf8, 0x6d, 0x16, 0x45, 0x8b, 0xd3, + 0x93, 0xbf, 0x82, 0x9d, 0x25, 0x8f, 0xb7, 0x8f, 0xa7, 0x67, 0xf9, 0x5b, 0x50, 0x0c, 0x32, 0x01, + 0x16, 0x84, 0x0e, 0xe3, 0xe6, 0x03, 0x84, 0xaf, 0x40, 0x33, 0x40, 0x2d, 0x6f, 0x68, 0xb8, 0xdc, + 0x86, 0x29, 0x0e, 0xfc, 0xb3, 0x2d, 0x6b, 0x98, 0x0b, 0x63, 0x73, 0x93, 0x91, 0x2c, 0xcb, 0xb7, + 0x6e, 0x03, 0x19, 0x0a, 0xa8, 0x19, 0xc7, 0x10, 0x58, 0x35, 0x8c, 0xd5, 0xc2, 0x9a, 0x8d, 0x62, + 0x68, 0x5a, 0xdb, 0xc2, 0x28, 0x5c, 0x5f, 0x7b, 0x31, 0x4d, 0x23, 0x6f, 0xb0, 0x1b, 0xd0, 0xe2, + 0x52, 0x2f, 0x9e, 0xad, 0x62, 0xb4, 0xa6, 0x6d, 0x44, 0x45, 0xa9, 0xac, 0x6a, 0x36, 0x9e, 0x81, + 0x66, 0x1c, 0x26, 0xa2, 0x1d, 0x78, 0x35, 0x31, 0x5b, 0xa0, 0x8a, 0xb6, 0x93, 0x74, 0x87, 0x07, + 0xec, 0x6f, 0xbf, 0xfd, 0x0d, 0xed, 0xf3, 0x89, 0xd3, 0xe6, 0x20, 0xe3, 0xf0, 0xbb, 0x29, 0xad, + 0xa6, 0xb8, 0x33, 0x3e, 0x96, 0xa4, 0x8d, 0x43, 0xb2, 0x2d, 0xb1, 0x6a, 0xf0, 0xbe, 0xc2, 0x95, + 0x0d, 0xd1, 0x81, 0xd2, 0xd0, 0xa7, 0x9e, 0x6a, 0x77, 0xbb, 0xf3, 0xee, 0xef, 0x43, 0x1a, 0xfe, + 0xfa, 0xe3, 0x45, 0x58, 0x37, 0x00, 0xaa, 0x8e, 0xfe, 0x1e, 0x0e, 0x2e, 0xda, 0x71, 0x27, 0x6f, + 0x9a, 0xf4, 0x1b, 0x24, 0x28, 0x86, 0x47, 0xe1, 0x4a, 0x98, 0x27, 0x7d, 0x2f, 0xa1, 0xb6, 0x81, + 0x53, 0xc4, 0x15, 0x50, 0x36, 0x51, 0x44, 0x70, 0x2a, 0xe8, 0xa5, 0x7e, 0x6c, 0x0f, 0xb2, 0x1d, + 0x28, 0x31, 0x42, 0xe0, 0xd8, 0x3a, 0xd4, 0x8a, 0xe8, 0x3c, 0xbd, 0x57, 0x3a, 0xe5, 0x38, 0xa3, + 0x9c, 0xfc, 0x17, 0xd3, 0x96, 0xf7, 0x08, 0x44, 0x2a, 0x76, 0x53, 0xfb, 0xce, 0x43, 0x9d, 0xda, + 0xf7, 0x00, 0xdb, 0x64, 0x15, 0x1b, 0x42, 0x9c, 0x2b, 0x45, 0x52, 0xd5, 0x9e, 0xdd, 0x30, 0x36, + 0x20, 0x86, 0xdc, 0x85, 0xc7, 0xf0, 0xe8, 0xb4, 0xe9, 0x31, 0xf5, 0xd0, 0x81, 0x26, 0x77, 0x7b, + 0x8f, 0x66, 0xd3, 0x94, 0xd6, 0x4a, 0x2a, 0x6f, 0xef, 0xd6, 0xc7, 0x31, 0x23, 0xb0, 0x0b, 0x3a, + 0xe6, 0x4a, 0x95, 0x15, 0xcc, 0x05, 0x99, 0x3f, 0xca, 0x71, 0x75, 0x8d, 0xff, 0xb8, 0x5a, 0x44, + 0x32, 0x47, 0x7b, 0xda, 0xf1, 0x66, 0x85, 0xc5, 0x75, 0xca, 0xaf, 0xfa, 0x62, 0xae, 0xf0, 0x8f, + 0xb0, 0x34, 0x19, 0x30, 0xc0, 0x66, 0x5f, 0x7c, 0xaa, 0x2f, 0xb0, 0xf0, 0x1f, 0x79, 0xba, 0x7e, + 0x57, 0xb7, 0x79, 0x9a, 0xb6, 0x7c, 0xaa, 0xeb, 0x6e, 0xba, 0x5e, 0x74, 0xbc, 0x8d, 0xc2, 0xae, + 0x08, 0xd8, 0x88, 0xda, 0x33, 0x4b, 0xf3, 0x48, 0x7d, 0x0b, 0x5a, 0xc3, 0x84, 0xef, 0x68, 0x10, + 0x49, 0x81, 0xf9, 0x68, 0x19, 0x75, 0x85, 0x66, 0xc5, 0xb6, 0xd5, 0xd1, 0xb6, 0x10, 0x76, 0x04, + 0x71, 0xa2, 0x3a, 0x9c, 0xc8, 0xd2, 0x46, 0x4b, 0x12, 0xbb, 0x86, 0x4e, 0xb8, 0xc5, 0x72, 0x07, + 0xdc, 0x24, 0x03, 0x41, 0xa2, 0x73, 0x67, 0x0a, 0x6b, 0xc2, 0x04, 0x82, 0x28, 0xb8, 0xdf, 0x60, + 0x72, 0x49, 0x0b, 0xbf, 0xf2, 0x82, 0x3e, 0xc8, 0xeb, 0xe7, 0x71, 0x30, 0xf2, 0x74, 0xfb, 0x8d, + 0x60, 0x5c, 0x1f, 0x0b, 0x46, 0xf2, 0x55, 0x20, 0x4e, 0xa8, 0xbf, 0x94, 0x32, 0x9f, 0xa5, 0x4a, + 0xf5, 0x38, 0x35, 0xc9, 0x30, 0x80, 0x8a, 0xc0, 0xf4, 0xb2, 0x49, 0xf6, 0xc5, 0x66, 0x8b, 0xe3, + 0x0f, 0x4e, 0x0f, 0x65, 0xd1, 0x59, 0x4a, 0x44, 0x8f, 0x02, 0xa2, 0x35, 0x50, 0x05, 0xaa, 0x48, + 0x99, 0x5a, 0xea, 0xa3, 0x07, 0xb0, 0x97, 0xe2, 0xfc, 0x98, 0x41, 0x38, 0xf4, 0xfd, 0xe7, 0xb7, + 0x7f, 0x7d, 0xa3, 0xa0, 0xd4, 0xf1, 0xf2, 0xb1, 0xc8, 0xa7, 0x84, 0xf8, 0x68, 0x8b, 0x8b, 0x51, + 0x6d, 0xaa, 0x9e, 0x31, 0x44, 0x77, 0xd1, 0x76, 0x95, 0x2e, 0x46, 0x7c, 0x00, 0xeb, 0xb0, 0xc9, + 0x86, 0x96, 0xfa, 0x7c, 0x84, 0x28, 0x5f, 0xdd, 0x7e, 0xeb, 0x22, 0xce, 0x1f, 0xd1, 0x4e, 0x98, + 0xc1, 0x3d, 0x00, 0xdb, 0x8a, 0xb9, 0xad, 0x63, 0x8f, 0x0e, 0x65, 0x1b, 0xdc, 0x24, 0xff, 0xe1, + 0xdd, 0x7f, 0x77, 0x5f, 0x42, 0xd8, 0xa2, 0x22, 0x80, 0x4f, 0x1e, 0xa1, 0x6b, 0x80, 0x0e, 0xf9, + 0xed, 0x6f, 0x7a, 0xdb, 0x15, 0x89, 0xaa, 0x1b, 0x40, 0xa4, 0xae, 0x0b, 0x90, 0x11, 0x7d, 0xf6, + 0xd2, 0x8f, 0x85, 0xb5, 0xd7, 0x34, 0xb7, 0x5a, 0x23, 0x13, 0xe9, 0x50, 0x93, 0x04, 0x17, 0xa5, + 0x21, 0x48, 0x15, 0x67, 0xd9, 0xc2, 0xcd, 0x11, 0x45, 0x19, 0xec, 0x31, 0xa5, 0x86, 0x49, 0xcd, + 0xbe, 0x6b, 0x0e, 0xc7, 0xd0, 0xb0, 0xee, 0x07, 0xdf, 0x02, 0xc0, 0x0e, 0x12, 0x32, 0x3a, 0x1e, + 0x47, 0x86, 0xb9, 0x18, 0xc9, 0x91, 0xba, 0x97, 0xdf, 0xcf, 0xe7, 0x38, 0x40, 0x54, 0xe4, 0x6b, + 0xaa, 0x8c, 0xd9, 0xa3, 0x0a, 0xd6, 0xbd, 0x4a, 0x12, 0x18, 0x0f, 0xa8, 0x12, 0x6b, 0x55, 0x7e, + 0xca, 0x73, 0x4d, 0x41, 0xd0, 0x79, 0x9c, 0x04, 0x57, 0x72, 0x9b, 0x56, 0xeb, 0x0d, 0x06, 0xfe, + 0xb5, 0x82, 0x4e, 0x5e, 0xf5, 0x25, 0x86, 0x8f, 0x77, 0x4c, 0x40, 0xb4, 0x5c, 0xd2, 0xd0, 0xcd, + 0x7b, 0xf9, 0x2a, 0x5e, 0xa2, 0x29, 0x5e, 0xbf, 0x2f, 0x28, 0x5f, 0x5a, 0xcb, 0x68, 0xd3, 0x2e, + 0xb6, 0xcd, 0x78, 0xed, 0x99, 0xe9, 0x43, 0xeb, 0x4b, 0x34, 0xe9, 0x4f, 0x7e, 0x4c, 0x07, 0x67, + 0x43, 0xe3, 0x1e, 0x9e, 0x0d, 0x69, 0x63, 0x64, 0xad, 0xf1, 0xa8, 0x92, 0x7c, 0x5f, 0xab, 0x3f, + 0xdd, 0x6c, 0xdb, 0x6a, 0x11, 0x32, 0x76, 0xeb, 0x5a, 0xb5, 0xd4, 0xd2, 0x1d, 0xb6, 0xb2, 0xe0, + 0x4a, 0x84, 0x2f, 0x7d, 0xb8, 0xa5, 0xed, 0x4f, 0xce, 0xfc, 0x90, 0xa5, 0xce, 0xaa, 0xd4, 0xf5, + 0xe2, 0x85, 0x77, 0x16, 0xd8, 0x6a, 0x8d, 0xbf, 0xb7, 0x9b, 0xd2, 0x85, 0x63, 0xff, 0xda, 0x11, + 0xc7, 0x6b, 0xe1, 0x59, 0x70, 0xd5, 0x97, 0x9a, 0xf9, 0x78, 0x1f, 0xe8, 0xf9, 0xc7, 0x89, 0x33, + 0x59, 0x01, 0x5c, 0xdc, 0x54, 0x57, 0x8b, 0x6b, 0x0d, 0xd3, 0x59, 0x42, 0xfb, 0xf3, 0x54, 0x8e, + 0x1a, 0xe9, 0x0b, 0x96, 0x96, 0x54, 0x1c, 0xe6, 0x69, 0x59, 0x35, 0x31, 0xf5, 0x51, 0x98, 0x6a, + 0x0f, 0x9c, 0xe0, 0xac, 0x5a, 0x09, 0x31, 0x88, 0xf8, 0x78, 0xed, 0x88, 0xe0, 0xa7, 0x84, 0x02, + 0x6b, 0x80, 0xc1, 0x30, 0x3a, 0x2c, 0x77, 0xbd, 0xc9, 0x6e, 0xec, 0x45, 0xe7, 0xc6, 0xf7, 0xa3, + 0x66, 0x9e, 0x18, 0x70, 0x78, 0x2b, 0xe0, 0x72, 0xf1, 0x5b, 0x4d, 0x85, 0xb5, 0xc8, 0x60, 0x08, + 0xe1, 0x2a, 0x9c, 0x7b, 0x21, 0x22, 0x58, 0x05, 0x53, 0xd9, 0x64, 0x55, 0x06, 0xd6, 0x56, 0x70, + 0x39, 0x40, 0x67, 0xfd, 0x91, 0x53, 0xcc, 0xb0, 0x0b, 0x42, 0xfe, 0xac, 0xdc, 0x3f, 0x70, 0x08, + 0x16, 0xfe, 0x26, 0x65, 0x6f, 0x09, 0xc3, 0x97, 0x38, 0xc7, 0x87, 0x85, 0x29, 0x13, 0x24, 0x61, + 0x9b, 0x65, 0xfe, 0x29, 0x3a, 0xfd, 0xf6, 0x6b, 0x0e, 0x81, 0x30, 0xa0, 0x6a, 0xdf, 0x83, 0xe2, + 0x6d, 0xbc, 0x4d, 0x22, 0x83, 0x22, 0x8d, 0xab, 0x95, 0x42, 0x60, 0xc0, 0x7e, 0xf8, 0x44, 0xe1, + 0x06, 0x16, 0xb5, 0x8c, 0x99, 0xb3, 0x8a, 0x84, 0xd6, 0x39, 0x0b, 0xa0, 0xcb, 0xf1, 0x62, 0xf5, + 0x2f, 0x10, 0xaa, 0x65, 0x1e, 0x0a, 0xa2, 0xc3, 0xd6, 0x58, 0x76, 0x50, 0xc5, 0x5a, 0xc5, 0x55, + 0x11, 0xbf, 0x5f, 0xf1, 0xba, 0x61, 0x1b, 0xe7, 0x11, 0x84, 0x7a, 0x74, 0x37, 0xf0, 0xb6, 0x04, + 0x86, 0x5b, 0xb2, 0x19, 0xa5, 0xea, 0x78, 0xb9, 0x86, 0x80, 0x09, 0xfe, 0x38, 0x17, 0x0b, 0xda, + 0xc1, 0x73, 0x88, 0xc2, 0x6a, 0x53, 0xda, 0xd9, 0x06, 0x96, 0x32, 0x01, 0x2b, 0xe7, 0xbd, 0x75, + 0x22, 0x99, 0x35, 0x1e, 0x98, 0x43, 0x16, 0x2c, 0x47, 0x19, 0x20, 0xe5, 0xbc, 0x77, 0xbf, 0xc8, + 0x88, 0x24, 0xfa, 0x42, 0x84, 0x0d, 0x03, 0xce, 0x7b, 0x59, 0x0e, 0xa0, 0x57, 0x15, 0x06, 0x8c, + 0x70, 0x27, 0x59, 0xf3, 0xc7, 0x77, 0x0c, 0xfb, 0x62, 0x18, 0x25, 0x6b, 0xfc, 0x58, 0x8e, 0xed, + 0x65, 0x09, 0x09, 0x3b, 0x2c, 0x34, 0xb2, 0xd6, 0xca, 0x1b, 0x69, 0xbd, 0x97, 0xab, 0x88, 0x79, + 0xae, 0x1b, 0xc5, 0x55, 0xcd, 0xe7, 0xeb, 0x25, 0x9c, 0x7f, 0x35, 0xa5, 0x8e, 0x1c, 0xa9, 0xa7, + 0xbb, 0xa6, 0xd4, 0xdb, 0x7a, 0x6a, 0x98, 0x96, 0xbd, 0xfe, 0x70, 0xd0, 0x98, 0xc5, 0xce, 0xc3, + 0x43, 0x38, 0xe6, 0xe9, 0x2c, 0x99, 0x0a, 0x94, 0xf5, 0x7c, 0x42, 0x47, 0x20, 0x99, 0x73, 0x0b, + 0x06, 0x35, 0x3a, 0xec, 0xb6, 0xd6, 0x72, 0x61, 0x98, 0x5e, 0xe3, 0x65, 0x5b, 0xac, 0xac, 0x40, + 0x93, 0x46, 0x09, 0x8d, 0x7d, 0x14, 0xc8, 0xa3, 0x6b, 0x31, 0xf6, 0xc4, 0x5f, 0xd8, 0x1e, 0x1f, + 0x6c, 0x9b, 0x55, 0x32, 0x61, 0xac, 0x2c, 0xb3, 0x48, 0xaa, 0xd1, 0x0a, 0xad, 0xb8, 0x0a, 0xd5, + 0x7a, 0x93, 0x7a, 0x9a, 0x80, 0x96, 0xb1, 0x57, 0x96, 0x7a, 0x80, 0xb1, 0xde, 0xd5, 0xcb, 0x92, + 0x9f, 0x85, 0x90, 0x05, 0x5f, 0xce, 0x69, 0x0d, 0x82, 0x36, 0x95, 0x38, 0x10, 0x75, 0xbb, 0x4a, + 0x14, 0x5a, 0xbc, 0x8f, 0x8e, 0x60, 0x63, 0xb5, 0xfd, 0x3c, 0xa1, 0x94, 0xef, 0x07, 0x1f, 0x18, + 0x6f, 0x64, 0x85, 0x0d, 0x1d, 0x61, 0xe7, 0x1f, 0xc4, 0xb8, 0x5e, 0x39, 0xf3, 0x0a, 0x7e, 0x9e, + 0x12, 0x08, 0x06, 0x42, 0xd2, 0xbe, 0x00, 0x5f, 0x0c, 0xc8, 0x2d, 0x70, 0xef, 0xa1, 0x5e, 0x3d, + 0x0e, 0x1e, 0xd8, 0x8b, 0x2e, 0x57, 0xe5, 0x22, 0xe7, 0x87, 0x06, 0x45, 0x83, 0x3b, 0x63, 0xdc, + 0xcb, 0xdd, 0xe3, 0xb0, 0x76, 0xd2, 0x37, 0xb2, 0xc4, 0xbb, 0x5f, 0x42, 0x99, 0x01, 0x97, 0xa2, + 0x45, 0xc3, 0x15, 0xf4, 0x3f, 0xee, 0x2d, 0x77, 0x2c, 0x6b, 0x1b, 0xb5, 0x65, 0xac, 0xc6, 0x34, + 0x85, 0x21, 0x97, 0xee, 0xe0, 0x5b, 0xd9, 0xac, 0x71, 0x22, 0x9a, 0x6f, 0xd2, 0x24, 0x79, 0x97, + 0x66, 0x0d, 0x12, 0x7d, 0x8d, 0x44, 0x81, 0x36, 0xbe, 0x63, 0xdd, 0xe2, 0xa1, 0x82, 0xe5, 0xe2, + 0x8c, 0x40, 0x20, 0xfb, 0xc3, 0xde, 0xcb, 0xb1, 0x56, 0x0e, 0xe6, 0x3d, 0xc6, 0xe3, 0x59, 0xf2, + 0x1f, 0x9f, 0xf1, 0x04, 0x84, 0xc9, 0xf5, 0x49, 0x92, 0x86, 0xa1, 0xcd, 0xc2, 0xe9, 0x9c, 0x6f, + 0x03, 0x17, 0xa7, 0xa6, 0xb6, 0x2a, 0xd3, 0x9a, 0x28, 0x95, 0x42, 0x60, 0x7a, 0x49, 0x89, 0xbd, + 0x49, 0x7a, 0xab, 0x92, 0xab, 0xc2, 0xec, 0x03, 0x9e, 0x42, 0x23, 0x43, 0x5b, 0xa6, 0xb0, 0xfa, + 0xb2, 0x74, 0xb7, 0xf0, 0x57, 0x6d, 0xb6, 0xe9, 0xec, 0x1f, 0x2c, 0x75, 0xc0, 0x01, 0x20, 0x5f, + 0x2d, 0x81, 0x4c, 0x41, 0x81, 0xe6, 0x5a, 0x72, 0x48, 0x2b, 0x69, 0xaf, 0x42, 0x5f, 0x0c, 0x15, + 0x46, 0xbc, 0x3c, 0xf9, 0x51, 0x59, 0x75, 0xac, 0xa2, 0x50, 0xf6, 0x26, 0x11, 0xb2, 0x64, 0x5b, + 0x68, 0xd1, 0x1f, 0xbd, 0x73, 0x27, 0x5f, 0x2b, 0xd4, 0x87, 0xb1, 0x54, 0x80, 0xfd, 0x02, 0xe6, + 0x14, 0x8d, 0x46, 0xcf, 0xfb, 0x11, 0x87, 0x71, 0x09, 0x7a, 0x88, 0x93, 0xc4, 0x13, 0x2f, 0x23, + 0xcb, 0xa5, 0xc7, 0x4e, 0x8c, 0x98, 0xff, 0xb2, 0xc3, 0x9c, 0xd1, 0x6f, 0x5a, 0xa6, 0xef, 0x21, + 0x21, 0x05, 0x36, 0x10, 0xca, 0x5d, 0xc3, 0x22, 0xe2, 0x3a, 0x00, 0xce, 0x51, 0x19, 0x6c, 0x6e, + 0xad, 0x13, 0xe2, 0x96, 0x62, 0x1a, 0x11, 0xbb, 0xdf, 0x9f, 0xde, 0x68, 0xa8, 0x0e, 0x15, 0x64, + 0xb5, 0xdc, 0xea, 0x5a, 0xfa, 0xb7, 0x62, 0x40, 0xa1, 0x4b, 0xac, 0x72, 0xe1, 0xaf, 0x74, 0x94, + 0x51, 0x6d, 0xce, 0xb1, 0x4c, 0x03, 0xb3, 0xba, 0x8e, 0xb6, 0x0f, 0xe9, 0xe6, 0xa3, 0xf0, 0x60, + 0x9d, 0x3e, 0xf0, 0xb5, 0x1e, 0x40, 0x5f, 0x61, 0x69, 0x24, 0xf9, 0x37, 0x3a, 0xd0, 0xe6, 0x77, + 0x48, 0xa2, 0xb8, 0x4a, 0x94, 0xa4, 0xc7, 0x8b, 0xf7, 0x92, 0x74, 0x7d, 0x4b, 0x89, 0x50, 0x89, + 0x93, 0x37, 0xd8, 0xb4, 0x99, 0x3b, 0xd8, 0xb8, 0xe9, 0x87, 0x6e, 0x0b, 0x33, 0x49, 0x37, 0x4e, + 0x09, 0x35, 0x21, 0x72, 0xc9, 0x36, 0xbf, 0x11, 0x19, 0xc7, 0x60, 0xc4, 0x89, 0x0c, 0x85, 0xf4, + 0xf5, 0x29, 0x8e, 0x1e, 0xf4, 0x10, 0xe7, 0x2b, 0x50, 0x2d, 0xfc, 0x75, 0xab, 0x65, 0x54, 0x72, + 0xa7, 0x0e, 0x85, 0x32, 0x39, 0x15, 0x27, 0x02, 0xd2, 0xc3, 0x99, 0x1c, 0x97, 0x49, 0xbe, 0xaa, + 0x9d, 0xcc, 0xfb, 0xba, 0xd4, 0x9b, 0x91, 0x5f, 0xfc, 0x76, 0x2b, 0x90, 0xcd, 0xfa, 0x37, 0x73, + 0xb3, 0x56, 0x38, 0x3f, 0xf5, 0xa7, 0x33, 0xb8, 0xe3, 0x99, 0x25, 0xd3, 0xf5, 0xc7, 0x83, 0x0a, + 0x68, 0x87, 0xfe, 0x5b, 0xd1, 0x6e, 0xa9, 0x9e, 0x8b, 0x0b, 0x33, 0x2e, 0xce, 0xb1, 0x08, 0xa8, + 0xb8, 0xa6, 0xae, 0x8a, 0xb5, 0xe4, 0xd7, 0x21, 0x99, 0x51, 0x8b, 0x97, 0xa1, 0x93, 0x3c, 0xc4, + 0x34, 0xe8, 0xea, 0x54, 0x61, 0x27, 0x6b, 0xe7, 0x9a, 0xda, 0xfe, 0xaa, 0xde, 0xf4, 0xa2, 0x64, + 0xab, 0xf1, 0x76, 0x9b, 0xef, 0xa6, 0x1f, 0x21, 0xed, 0xb4, 0x8b, 0xa3, 0xf3, 0x5a, 0x5f, 0x27, + 0xab, 0x9b, 0x5e, 0xbd, 0x41, 0x91, 0x2a, 0x93, 0x19, 0xda, 0x60, 0xd9, 0xb0, 0xb2, 0x75, 0x87, + 0xa5, 0x05, 0x4b, 0x15, 0xcb, 0xb9, 0xaf, 0x8a, 0x0b, 0x67, 0xa1, 0x54, 0x9a, 0xc9, 0x00, 0xdf, + 0x1b, 0x30, 0x39, 0x27, 0x78, 0xac, 0x8e, 0xc5, 0x82, 0x46, 0x4c, 0xc5, 0xbe, 0x89, 0x1e, 0xb4, + 0x29, 0xd7, 0xb3, 0x09, 0x31, 0x81, 0x58, 0xb3, 0x8d, 0x13, 0x20, 0x55, 0xe0, 0xa1, 0xfe, 0x7f, + 0xa0, 0x9f, 0xac, 0xd6, 0xfd, 0x3c, 0x20, 0xd4, 0x3a, 0x6f, 0x42, 0xb1, 0x58, 0x85, 0xff, 0xcb, + 0x18, 0x1b, 0xab, 0xb4, 0x9f, 0x03, 0x8b, 0x32, 0xc2, 0x9e, 0x45, 0xee, 0x41, 0xfc, 0x4d, 0x13, + 0x81, 0x8c, 0xc3, 0x29, 0xeb, 0x24, 0x49, 0x88, 0xf8, 0xc4, 0xd6, 0xd2, 0x13, 0x51, 0x24, 0xde, + 0x6a, 0x9a, 0x58, 0x50, 0x12, 0xab, 0x78, 0x17, 0xaa, 0xc4, 0xa6, 0x7f, 0xec, 0x99, 0x30, 0x82, + 0x1a, 0xdc, 0xc4, 0x02, 0x57, 0xb8, 0xb5, 0xe2, 0x6e, 0x1c, 0x84, 0x03, 0xaa, 0x6a, 0x54, 0xa4, + 0x27, 0xb7, 0x9a, 0x7a, 0x35, 0xbd, 0x12, 0x4c, 0x2b, 0xdd, 0xd8, 0x63, 0x67, 0xd9, 0x65, 0x13, + 0x8a, 0x15, 0x04, 0x43, 0xc1, 0xe6, 0x0a, 0xa7, 0xa7, 0x5f, 0xd2, 0x80, 0xe1, 0xcb, 0x29, 0x37, + 0xe0, 0xeb, 0xc5, 0x42, 0x4f, 0x51, 0x4b, 0x85, 0xf4, 0x2f, 0x36, 0xa4, 0x40, 0x61, 0x1a, 0x58, + 0x5b, 0xbd, 0xfb, 0x62, 0x71, 0x18, 0xbd, 0x67, 0xcd, 0x10, 0x1c, 0x3e, 0xc1, 0xbd, 0xa9, 0x17, + 0x5f, 0x4d, 0x90, 0x41, 0x4c, 0x5e, 0x4f, 0x22, 0xde, 0x51, 0x3a, 0x71, 0xa9, 0x85, 0x2c, 0x24, + 0xcd, 0x50, 0xca, 0x49, 0x1e, 0x7e, 0x05, 0x09, 0x73, 0xc4, 0x07, 0xe1, 0x7d, 0xa1, 0x54, 0x3d, + 0xb2, 0x0b, 0x2b, 0x30, 0x6a, 0x1e, 0x3e, 0x15, 0xa7, 0xcd, 0xe6, 0xf5, 0x32, 0x07, 0x94, 0xf5, + 0xa6, 0x38, 0x82, 0x8f, 0x1d, 0xc4, 0x6b, 0x15, 0x58, 0xde, 0xf1, 0x84, 0x34, 0x94, 0xe8, 0x92, + 0xb0, 0x8c, 0x7b, 0x5b, 0x55, 0x8a, 0xb6, 0x36, 0x45, 0xff, 0x76, 0xec, 0x02, 0x8b, 0x39, 0xc8, + 0x7a, 0x35, 0x91, 0xda, 0x69, 0xc4, 0x2d, 0x34, 0x9f, 0xc6, 0x5a, 0xf3, 0xbb, 0x3f, 0xda, 0x7c, + 0x76, 0xb0, 0x30, 0x92, 0x22, 0xf8, 0x3b, 0xcd, 0xf8, 0x33, 0xcd, 0x0a, 0x69, 0xea, 0x30, 0xc4, + 0xe0, 0xff, 0x12, 0x99, 0xa0, 0x87, 0x99, 0xd4, 0x4c, 0xff, 0x1b, 0x61, 0x52, 0x24, 0xfd, 0x7f, + 0xe2, 0x20, 0x5b, 0x18, 0x83, 0x8c, 0x99, 0xab, 0x01, 0xe5, 0x57, 0xb0, 0xca, 0x59, 0x32, 0xad, + 0x4f, 0xc7, 0x02, 0xda, 0x26, 0x69, 0x3f, 0x26, 0x11, 0x78, 0x35, 0xfd, 0x14, 0x79, 0x77, 0xf7, + 0xc9, 0x36, 0xce, 0x92, 0xa8, 0x44, 0x3e, 0x51, 0x8c, 0x20, 0x47, 0x20, 0x1e, 0xcb, 0x5d, 0x5d, + 0x53, 0xcd, 0x50, 0x80, 0x87, 0xf3, 0x0f, 0x06, 0x9a, 0x49, 0xd7, 0xd9, 0xed, 0x1e, 0xef, 0x37, + 0xee, 0xa7, 0xc1, 0x73, 0x47, 0x2d, 0x30, 0x6a, 0xc5, 0xa8, 0x05, 0x3c, 0xed, 0xc0, 0x3c, 0xa2, + 0xe8, 0xd3, 0xe7, 0x11, 0xe0, 0x70, 0x30, 0xaf, 0x7c, 0xfa, 0x19, 0xf3, 0x08, 0x20, 0xca, 0x82, + 0xa8, 0xc6, 0x1a, 0xf8, 0x28, 0xd4, 0xd2, 0x38, 0xcd, 0xfc, 0xc3, 0x69, 0x47, 0xa2, 0x12, 0x31, + 0x79, 0xbc, 0x06, 0x00, 0x30, 0x47, 0x27, 0x8c, 0x42, 0xc6, 0x86, 0xa9, 0x51, 0x78, 0x32, 0x15, + 0x7f, 0x6f, 0xc7, 0xd0, 0x2f, 0x34, 0xd0, 0xb0, 0xdc, 0x59, 0x2a, 0x79, 0x4a, 0x79, 0x72, 0xc1, + 0xca, 0x42, 0xd8, 0x6e, 0x58, 0xa9, 0xe9, 0x6f, 0x36, 0x4d, 0x0e, 0xb6, 0xab, 0x6a, 0x6a, 0x7c, + 0x6e, 0xd3, 0xc0, 0xdd, 0x5a, 0xcb, 0x8e, 0x31, 0xb3, 0xae, 0xc5, 0x0d, 0xff, 0x2d, 0x8a, 0x01, + 0x0c, 0x08, 0xa8, 0xaa, 0xea, 0x14, 0x6b, 0x76, 0xe5, 0xbb, 0x53, 0x08, 0xb7, 0xf4, 0x5d, 0x4e, + 0x5e, 0x1d, 0x1a, 0xf5, 0x85, 0xfa, 0xab, 0x99, 0x8c, 0xf8, 0x24, 0x32, 0x6a, 0x7a, 0xb4, 0x67, + 0x93, 0xf2, 0x1a, 0x9b, 0x78, 0x8d, 0x0a, 0x7e, 0x97, 0xc5, 0x8b, 0x9f, 0xa1, 0x76, 0xf8, 0xe6, + 0xc7, 0x7a, 0x5b, 0xb2, 0x04, 0xf7, 0x1f, 0x49, 0x4c, 0x6e, 0xda, 0x4a, 0x1c, 0x4a, 0x73, 0x19, + 0xc7, 0x46, 0x09, 0x23, 0xdc, 0xe8, 0xf1, 0xa1, 0xad, 0x66, 0xbe, 0xe0, 0xde, 0xe3, 0x41, 0xc6, + 0x98, 0x4a, 0xf4, 0x90, 0x72, 0xc2, 0x5d, 0xce, 0xb1, 0x0c, 0x91, 0x99, 0x81, 0xaf, 0x61, 0x1d, + 0xcb, 0x61, 0x0d, 0xbc, 0xfd, 0x2f, 0x31, 0x74, 0x99, 0xa9, 0x27, 0x32, 0xfb, 0xbb, 0xb7, 0xc2, + 0x6c, 0x76, 0xe7, 0xd2, 0x62, 0x83, 0xc4, 0xaf, 0x89, 0x60, 0xe2, 0x83, 0x66, 0xde, 0x00, 0x66, + 0x12, 0xb4, 0xd1, 0x14, 0x70, 0x13, 0xdf, 0xd2, 0x02, 0x3d, 0x17, 0xf7, 0xa4, 0xca, 0x30, 0x2e, + 0x1f, 0xbb, 0x21, 0x7f, 0x44, 0xfc, 0x6b, 0x18, 0xe6, 0x7c, 0x5b, 0x8e, 0x65, 0x0a, 0xe9, 0x84, + 0xfe, 0x16, 0x3b, 0x32, 0xed, 0xbf, 0x79, 0x92, 0x02, 0xc5, 0x0f, 0xdf, 0x1e, 0x02, 0xa6, 0xe1, + 0x00, 0xe7, 0x12, 0xca, 0x82, 0xbb, 0x63, 0x16, 0x64, 0x39, 0x4f, 0x82, 0x06, 0x0c, 0x56, 0x3b, + 0xee, 0x0e, 0xc3, 0x12, 0x87, 0x65, 0xcb, 0x08, 0xaf, 0xc5, 0xad, 0xaf, 0x27, 0x8e, 0x94, 0x4a, + 0x02, 0xe4, 0xd6, 0x9c, 0x77, 0xe6, 0xf7, 0xfc, 0x26, 0x9d, 0x99, 0xff, 0xd6, 0x4c, 0x07, 0x3c, + 0x0f, 0x3b, 0xad, 0x6b, 0x79, 0x3f, 0xc0, 0xf9, 0x2a, 0x33, 0x09, 0x1e, 0x60, 0xa5, 0x91, 0xdc, + 0xd8, 0x55, 0xa4, 0x81, 0x5c, 0xba, 0x3e, 0x8a, 0x8a, 0xb7, 0x3d, 0xcb, 0xa9, 0x82, 0xf4, 0x5f, + 0xd9, 0xb4, 0x29, 0xd4, 0x18, 0x32, 0xc5, 0xcd, 0xba, 0xa5, 0xfc, 0xb2, 0x18, 0x17, 0xd1, 0xa7, + 0x09, 0xed, 0xda, 0xec, 0x20, 0xd8, 0x1f, 0xe5, 0x74, 0x67, 0x36, 0xfe, 0x75, 0xdd, 0x90, 0xb0, + 0xd7, 0xeb, 0x89, 0x7f, 0x9e, 0xed, 0x04, 0x6c, 0x20, 0x16, 0x0c, 0xf6, 0x4f, 0x60, 0x24, 0x93, + 0xb7, 0x90, 0x26, 0xfe, 0x65, 0xb6, 0xab, 0x46, 0xc9, 0x0b, 0x50, 0xfe, 0xc5, 0xb9, 0x44, 0xed, + 0x39, 0x96, 0xed, 0x1f, 0x95, 0x2a, 0x2e, 0x9d, 0x55, 0x98, 0x29, 0x75, 0x2d, 0xe7, 0x45, 0x2d, + 0x66, 0xac, 0xaa, 0xe8, 0x72, 0x20, 0xb1, 0x0c, 0xa6, 0x61, 0x67, 0x27, 0xe2, 0xba, 0xbd, 0x78, + 0xb8, 0xd2, 0x6b, 0x4d, 0xe8, 0x90, 0x56, 0xa1, 0xc1, 0x36, 0x7b, 0x08, 0x1b, 0x8c, 0xcc, 0x18, + 0xce, 0x47, 0xb2, 0x8c, 0x0f, 0xda, 0x81, 0xfe, 0xd6, 0x6c, 0x03, 0x7a, 0x06, 0x2a, 0xb3, 0x30, + 0xc4, 0x43, 0xa5, 0xb7, 0x52, 0xca, 0xb0, 0xf2, 0x66, 0xe7, 0x37, 0x6d, 0xa2, 0xb6, 0x44, 0x8b, + 0x6b, 0xcf, 0x3a, 0x9b, 0xf5, 0xc2, 0xd7, 0x78, 0x22, 0x0c, 0x99, 0xa7, 0x15, 0x6f, 0x01, 0x39, + 0xff, 0xff, 0x69, 0x84, 0xab, 0xd5, 0x18, 0xb4, 0x14, 0xd9, 0xab, 0xfa, 0x03, 0x90, 0x5b, 0xd1, + 0x24, 0x5a, 0xa7, 0x77, 0xb4, 0x40, 0x7e, 0xe1, 0x5d, 0xf2, 0xc5, 0xf6, 0x4b, 0x5e, 0x5f, 0x17, + 0xa9, 0x27, 0x45, 0xf6, 0x56, 0x76, 0xae, 0x6a, 0xa6, 0x17, 0xe2, 0x3a, 0x60, 0x8f, 0x86, 0x93, + 0x4c, 0x2f, 0xf1, 0xd3, 0x59, 0xde, 0x96, 0xbc, 0xe2, 0x6b, 0x68, 0x15, 0xd2, 0x60, 0x94, 0x82, + 0x75, 0xe3, 0x38, 0x5a, 0x0d, 0x99, 0x6a, 0x5f, 0xa4, 0x89, 0x72, 0x49, 0x23, 0x8e, 0x6a, 0x9a, + 0xb7, 0x63, 0x15, 0x8f, 0x67, 0x98, 0xd8, 0xa7, 0x52, 0xa9, 0x49, 0x38, 0x71, 0xc2, 0x80, 0x93, + 0x65, 0x76, 0xc6, 0x78, 0x1e, 0x1c, 0x08, 0x60, 0x79, 0x60, 0xba, 0xea, 0x09, 0x42, 0xaf, 0x42, + 0xbe, 0x7a, 0x93, 0xbb, 0x1d, 0xac, 0xf2, 0x4f, 0x41, 0xc7, 0x0b, 0xf0, 0xdf, 0x50, 0xf0, 0x6e, + 0xc7, 0xca, 0xf6, 0x9c, 0x43, 0x7a, 0x0c, 0xc7, 0x50, 0xe5, 0xa0, 0x72, 0xe3, 0xdf, 0xec, 0x71, + 0xa8, 0x97, 0x0d, 0x1e, 0x11, 0x95, 0x42, 0x1b, 0x0e, 0x27, 0x14, 0x4f, 0x7f, 0xdf, 0xbf, 0x47, + 0xbf, 0x6c, 0x3a, 0xdc, 0x3d, 0xb7, 0xf2, 0x67, 0xd6, 0xb1, 0x06, 0xf5, 0x87, 0xce, 0x7b, 0xfc, + 0xfb, 0x50, 0x1c, 0x79, 0x65, 0xad, 0x2a, 0xa0, 0xfb, 0x0d, 0xe5, 0x52, 0x9e, 0x93, 0x8a, 0x96, + 0x72, 0x8d, 0x8b, 0x01, 0xba, 0xf8, 0xf3, 0x63, 0xc5, 0x9f, 0x54, 0x83, 0x2a, 0xfe, 0xd7, 0x93, + 0x9d, 0xd9, 0x9b, 0xc2, 0x5c, 0xcd, 0x5e, 0x25, 0xbc, 0x6e, 0x28, 0xa3, 0xe9, 0x7a, 0x45, 0x81, + 0xfc, 0x45, 0x4d, 0xae, 0x4e, 0xba, 0x94, 0xe9, 0x6c, 0x32, 0x7c, 0x29, 0x40, 0xd7, 0xd5, 0xfc, + 0xfa, 0x05, 0xfc, 0x23, 0xac, 0x18, 0x28, 0xa0, 0x42, 0xce, 0x29, 0x84, 0x02, 0x5e, 0x0c, 0x7e, + 0xaf, 0x83, 0x26, 0x93, 0xe1, 0xc5, 0x45, 0xc8, 0x39, 0x5f, 0x98, 0x81, 0x83, 0x97, 0xa1, 0x2e, + 0xce, 0xb5, 0x68, 0xad, 0xa2, 0xe9, 0x66, 0x5b, 0x58, 0x05, 0xe6, 0x6a, 0xec, 0xdd, 0xe0, 0xa1, + 0xc3, 0xcf, 0x9e, 0x56, 0xf3, 0x3d, 0x8f, 0xd7, 0xcf, 0x3b, 0x34, 0x61, 0x3f, 0x0f, 0x6f, 0xe8, + 0xeb, 0x4b, 0x69, 0x71, 0xc5, 0x59, 0xd0, 0x9f, 0xfe, 0x47, 0x9d, 0xa2, 0x94, 0xe3, 0x2f, 0x84, + 0xb5, 0x7d, 0x87, 0x57, 0xf7, 0xaf, 0xe3, 0xcd, 0x3c, 0x4d, 0xd3, 0x8f, 0x71, 0xd4, 0x87, 0x39, + 0xb6, 0xff, 0x10, 0x7f, 0x8c, 0x7d, 0x8b, 0x5f, 0x24, 0xad, 0x16, 0xde, 0x77, 0xcc, 0x35, 0x8f, + 0xc2, 0xbf, 0x67, 0x61, 0xc3, 0xb8, 0x24, 0x83, 0xb4, 0xe5, 0xe9, 0x6d, 0xb6, 0x75, 0x4e, 0x07, + 0x16, 0x3b, 0x48, 0x7a, 0x62, 0x83, 0xd8, 0x6c, 0x5b, 0x69, 0xa9, 0x3c, 0x8f, 0x29, 0x31, 0xa6, + 0x4c, 0x04, 0xb0, 0xe4, 0x32, 0xde, 0xdc, 0x79, 0xdf, 0xb3, 0x9b, 0x19, 0xde, 0x85, 0x84, 0x32, + 0xc6, 0xa0, 0x78, 0xa6, 0x4e, 0xa2, 0xea, 0xed, 0x6c, 0xb5, 0x89, 0x96, 0x28, 0xa2, 0xcf, 0x55, + 0xd7, 0x05, 0x27, 0x75, 0x61, 0x5a, 0xb9, 0xdf, 0x30, 0xaf, 0x43, 0x93, 0x48, 0x6a, 0x7c, 0x3a, + 0x3b, 0x8c, 0x06, 0x7b, 0xe0, 0x9d, 0xc5, 0x0e, 0x13, 0x99, 0x91, 0xd3, 0x69, 0x29, 0x73, 0x96, + 0x15, 0x99, 0x85, 0x59, 0x46, 0xa1, 0x6a, 0x71, 0x15, 0xdc, 0xd5, 0x48, 0x07, 0xc8, 0x67, 0xed, + 0xbe, 0x08, 0x8d, 0xe7, 0xf9, 0x7d, 0x2e, 0x6f, 0x19, 0xe4, 0x4d, 0x5e, 0x77, 0x8a, 0xa3, 0x2c, + 0x49, 0xa4, 0xb3, 0xe4, 0x7e, 0xd3, 0x76, 0x5a, 0xae, 0x7c, 0xe3, 0xe0, 0x78, 0x4a, 0xc2, 0xe2, + 0x68, 0x67, 0x26, 0x66, 0xf2, 0xfa, 0x7d, 0xa4, 0x80, 0x68, 0xf0, 0xc3, 0x2b, 0xed, 0xf0, 0xef, + 0xa7, 0x6f, 0x4c, 0xb1, 0xdf, 0xf6, 0x4a, 0x1d, 0xc0, 0x9d, 0xe7, 0x76, 0x1a, 0xaf, 0x81, 0x72, + 0xef, 0x78, 0x6f, 0x28, 0xe5, 0x05, 0x8f, 0x7e, 0x3a, 0xf0, 0x7b, 0x85, 0x23, 0x2b, 0x5a, 0xfc, + 0x77, 0x03, 0x85, 0x48, 0xed, 0x78, 0x8f, 0xe5, 0x4f, 0xc1, 0x3d, 0xbc, 0xf5, 0x94, 0x6b, 0x05, + 0x85, 0x6c, 0x52, 0xde, 0x08, 0x1f, 0xc6, 0x15, 0x86, 0x01, 0xdc, 0x0b, 0xf8, 0xda, 0x7f, 0xe8, + 0xa7, 0xd9, 0x2a, 0x5e, 0x50, 0xa9, 0x13, 0x1c, 0x8e, 0x51, 0xe9, 0x98, 0x18, 0x79, 0x36, 0xef, + 0x51, 0x53, 0xbb, 0xea, 0xa9, 0xab, 0x02, 0x6a, 0xc5, 0x66, 0x50, 0xf8, 0xed, 0x2c, 0xfc, 0x6f, + 0xa9, 0x55, 0x3c, 0xd1, 0xcd, 0xce, 0xdb, 0x81, 0xb2, 0xa5, 0xd0, 0xde, 0xf6, 0xcf, 0x7f, 0x7a, + 0x66, 0x04, 0x3f, 0x48, 0xc3, 0x5e, 0x0a, 0x7f, 0xfa, 0xa9, 0x7c, 0xab, 0xb0, 0x21, 0x9d, 0xf5, + 0x90, 0x25, 0xa5, 0x39, 0x90, 0x28, 0x30, 0xef, 0xb7, 0x31, 0x5f, 0x35, 0x6b, 0xb4, 0x9f, 0x54, + 0x0a, 0x7f, 0x74, 0x84, 0xff, 0x88, 0x45, 0xb6, 0xe0, 0xf8, 0x41, 0x47, 0x6d, 0xca, 0x2f, 0x99, + 0x1f, 0xbe, 0x97, 0x4e, 0xfc, 0x50, 0x42, 0x54, 0xb8, 0x57, 0x0e, 0xfb, 0x24, 0x6b, 0xb5, 0x8b, + 0xbe, 0x64, 0xd8, 0x47, 0x0d, 0xdd, 0xb1, 0xd8, 0x4c, 0x6f, 0x8d, 0x0e, 0x68, 0xa9, 0xe4, 0xd4, + 0xe6, 0x5a, 0x5f, 0x54, 0x0f, 0xf7, 0xcf, 0x21, 0x15, 0x77, 0xdc, 0x54, 0x83, 0xb9, 0x2e, 0x40, + 0x5b, 0xf1, 0x7a, 0x8a, 0xb5, 0x38, 0x35, 0x7b, 0x4e, 0xdb, 0xee, 0xa8, 0x2d, 0x37, 0x9f, 0x3d, + 0x19, 0x0e, 0x71, 0xaa, 0x9c, 0x26, 0x91, 0x6d, 0x47, 0x87, 0xf4, 0x6c, 0x77, 0x53, 0x6b, 0xa3, + 0xaa, 0x4c, 0xda, 0x78, 0xb0, 0x89, 0xec, 0xd0, 0x6a, 0xe7, 0xe8, 0x47, 0x2e, 0xbd, 0xe3, 0x15, + 0xa2, 0x28, 0x7b, 0x0f, 0x5f, 0xec, 0x68, 0xa2, 0x51, 0x3d, 0x14, 0x78, 0xd6, 0xce, 0xbf, 0x58, + 0xec, 0xfa, 0x0f, 0x61, 0xe1, 0x03, 0xe8, 0x5c, 0xf6, 0x1e, 0x3c, 0xbc, 0xad, 0x66, 0x1f, 0x6e, + 0xe3, 0xe0, 0x2a, 0x9c, 0x20, 0x05, 0x71, 0x41, 0x4e, 0x45, 0x5c, 0xd1, 0x6c, 0xed, 0xaa, 0x7b, + 0x72, 0x62, 0x35, 0xa6, 0xc8, 0x25, 0xfd, 0xea, 0x0d, 0x45, 0x7a, 0x38, 0xee, 0x50, 0xce, 0xeb, + 0x4e, 0x04, 0x5a, 0x09, 0x62, 0x86, 0x54, 0xda, 0x72, 0x5c, 0xf3, 0x30, 0x57, 0xba, 0x9e, 0x3b, + 0xce, 0xeb, 0x80, 0x0e, 0x2c, 0xc1, 0x21, 0x5f, 0x76, 0x4b, 0x38, 0xbc, 0x53, 0x09, 0x4e, 0x1b, + 0x7f, 0x22, 0x0e, 0x87, 0x7a, 0xa6, 0x60, 0xd9, 0xa9, 0x89, 0xc6, 0x8c, 0x33, 0xf0, 0x9e, 0x88, + 0xa9, 0xc0, 0xb8, 0xaa, 0xcd, 0x8a, 0xf7, 0x3a, 0xf6, 0x4f, 0xac, 0x7a, 0x68, 0xe5, 0xdc, 0x3c, + 0xb7, 0x69, 0x56, 0x81, 0x59, 0x39, 0x37, 0x12, 0x1b, 0x5a, 0xc0, 0x38, 0x2f, 0xf6, 0x74, 0x23, + 0x07, 0xb4, 0xa3, 0xf7, 0x86, 0x4e, 0xcc, 0x60, 0xdd, 0xaf, 0x91, 0x4c, 0xed, 0xd5, 0xd9, 0x65, + 0x58, 0x66, 0x14, 0x64, 0x04, 0xd0, 0x16, 0x2b, 0x74, 0xde, 0xc5, 0x01, 0x5e, 0x6f, 0xe1, 0xe7, + 0x53, 0xd7, 0x79, 0xa8, 0x53, 0x66, 0x5b, 0x4a, 0xe8, 0x00, 0x3c, 0xa4, 0xdb, 0x0a, 0x67, 0x8c, + 0xb2, 0x2c, 0x67, 0x93, 0x8d, 0xaa, 0x28, 0x89, 0xc6, 0x91, 0xe3, 0x0f, 0x45, 0x57, 0x16, 0xc2, + 0x0d, 0x07, 0x8b, 0xaa, 0xaa, 0xf0, 0x63, 0xdb, 0x52, 0x3f, 0xc7, 0x4d, 0xfe, 0x4b, 0xe1, 0xf5, + 0xb2, 0x70, 0x7c, 0xcb, 0xce, 0x75, 0xb4, 0x4f, 0xdc, 0x32, 0xa7, 0xd2, 0x6a, 0x3c, 0xd0, 0x0c, + 0x19, 0x9e, 0x5f, 0xf2, 0x5d, 0xb6, 0x96, 0x73, 0x59, 0x6a, 0x09, 0x65, 0x98, 0x53, 0x56, 0x6a, + 0x71, 0xa3, 0x2b, 0xdf, 0xbc, 0xda, 0x53, 0xec, 0xf5, 0xa4, 0x12, 0xad, 0x02, 0x42, 0x73, 0x31, + 0xd7, 0x6e, 0x25, 0x07, 0xf6, 0xd5, 0x8b, 0xb2, 0xac, 0x27, 0xdb, 0x85, 0xaf, 0xed, 0xcb, 0x4b, + 0x71, 0x81, 0x1d, 0x4e, 0x0a, 0x6f, 0x1c, 0x8d, 0x73, 0xb9, 0x45, 0x6d, 0xbc, 0xab, 0x0a, 0x17, + 0xa1, 0x36, 0x5c, 0x45, 0xf3, 0xa1, 0x0a, 0x57, 0x31, 0xb6, 0x51, 0xee, 0xa0, 0x62, 0xac, 0xb0, + 0x2a, 0x82, 0x72, 0x06, 0x90, 0x92, 0x03, 0x4b, 0x2d, 0x55, 0x4f, 0x73, 0xa1, 0xd3, 0xc8, 0xc9, + 0x97, 0x67, 0xa3, 0x1e, 0x1b, 0x05, 0x1a, 0xa8, 0x69, 0x18, 0xae, 0xa9, 0x63, 0xdf, 0xc8, 0xf2, + 0xa7, 0xa6, 0x70, 0xcd, 0xdb, 0x6a, 0x20, 0xef, 0x47, 0x52, 0x42, 0xfc, 0xb1, 0x3c, 0xaa, 0x52, + 0xa9, 0xf5, 0xe4, 0x77, 0x29, 0x0c, 0xfd, 0xe9, 0xc3, 0x5a, 0x3c, 0xde, 0x7e, 0xb4, 0xbd, 0x6d, + 0xd6, 0xd3, 0x97, 0x9e, 0xe5, 0xeb, 0x19, 0x0e, 0x55, 0x81, 0xad, 0x02, 0x73, 0x88, 0xc6, 0xea, + 0x49, 0x55, 0xb8, 0xd3, 0x1f, 0xaa, 0x21, 0xbd, 0x07, 0x49, 0xc8, 0x77, 0xac, 0x02, 0x4e, 0x7e, + 0x9f, 0x9d, 0x98, 0x5a, 0x7b, 0x1d, 0xaf, 0x27, 0xbf, 0xea, 0xd3, 0xce, 0x1b, 0x67, 0x5b, 0x46, + 0x40, 0xe0, 0x8a, 0x14, 0xff, 0x58, 0x6d, 0xef, 0x92, 0xeb, 0xff, 0x05, 0x15, 0xdd, 0xcc, 0x5e, + 0xe4, 0x36, 0x01, 0x00, }; diff --git a/wled00/json.cpp b/wled00/json.cpp index 8e781fc8..61e76a65 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -293,7 +293,7 @@ void serializeInfo(JsonObject root) { root["ver"] = versionString; root["vid"] = VERSION; - root["cn"] = WLED_CODENAME; + //root["cn"] = WLED_CODENAME; JsonObject leds = root.createNestedObject("leds"); leds["count"] = ledCount; diff --git a/wled00/wled.h b/wled00/wled.h index f3ffe780..66b8d495 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2004300 +#define VERSION 2005010 // ESP8266-01 (blue) got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.4.2 and the setting 512K(No SPIFFS). From df1f516f6b6aaeb52e681062822fb601199cad8d Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sun, 3 May 2020 01:48:23 +0200 Subject: [PATCH 228/232] Added ACST and ACST/ACDT timezones ( closes #881 ) --- CHANGELOG.md | 4 ++++ wled00/html_settings.h | 2 ++ wled00/ntp.cpp | 7 ++++++- wled00/wled.h | 2 +- 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7956427..128d5525 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ### Development versions after 0.9.1 release +#### Build 2005020 + +- Added ACST and ACST/ACDT timezones + #### Build 2005010 - Added module info page to web UI diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 1e6db0d0..d3b78d4d 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -384,6 +384,8 @@ Time zone: + +
UTC offset: seconds (max. 18 hours)
Current local time is unknown. diff --git a/wled00/ntp.cpp b/wled00/ntp.cpp index d6332fd2..4836ed34 100644 --- a/wled00/ntp.cpp +++ b/wled00/ntp.cpp @@ -60,8 +60,13 @@ Timezone tzNK(NKST, NKST); TimeChangeRule IST = {Last, Sun, Mar, 1, 330}; // India Standard Time = UTC + 5.5 hours Timezone tzIndia(IST, IST); +TimeChangeRule ACST = {First, Sun, Apr, 3, 570}; //Australian Central Standard = UTC + 9.5 hours +TimeChangeRule ACDT = {First, Sun, Oct, 2, 630}; //Australian Central Daylight = UTC + 10.5 hours +Timezone tzAUNorthern(ACST, ACST); +Timezone tzAUSouthern(ACDT, ACST); + // Pick your timezone from here. -Timezone* timezones[] = {&tzUTC, &tzUK, &tzEUCentral, &tzEUEastern, &tzUSEastern, &tzUSCentral, &tzUSMountain, &tzUSArizona, &tzUSPacific, &tzChina, &tzJapan, &tzAUEastern, &tzNZ, &tzNK, &tzIndia, &tzCASaskatchewan}; +Timezone* timezones[] = {&tzUTC, &tzUK, &tzEUCentral, &tzEUEastern, &tzUSEastern, &tzUSCentral, &tzUSMountain, &tzUSArizona, &tzUSPacific, &tzChina, &tzJapan, &tzAUEastern, &tzNZ, &tzNK, &tzIndia, &tzCASaskatchewan, &tzAUNorthern, &tzAUSouthern}; void handleNetworkTime() { diff --git a/wled00/wled.h b/wled00/wled.h index 66b8d495..1a04b3e5 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2005010 +#define VERSION 2005020 // ESP8266-01 (blue) got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.4.2 and the setting 512K(No SPIFFS). From 71886c162b7df2ce027b32c0334b83152e39884c Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sun, 3 May 2020 20:57:53 +0200 Subject: [PATCH 229/232] Release of WLED v0.10.0 DMX Single RGW and Single DRGB modes now support an additional white channel Improved palettes derived from set colors and changed their names --- CHANGELOG.md | 9 ++++++++- platformio.ini | 2 +- wled00/FX.h | 2 +- wled00/FX_fcn.cpp | 26 +++++++++++++------------- wled00/data/index.htm | 2 +- wled00/data/settings_sec.htm | 2 +- wled00/e131.cpp | 7 +++++-- wled00/html_other.h | 2 +- wled00/html_settings.h | 2 +- wled00/wled.h | 6 +++--- 10 files changed, 35 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 128d5525..dadc3c20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ ## WLED changelog -### Development versions after 0.9.1 release +### WLED version 0.10.0 + +#### Build 2005030 + +- DMX Single RGW and Single DRGB modes now support an additional white channel +- Improved palettes derived from set colors and changed their names + +### Development versions between 0.9.1 and 0.10.0 release #### Build 2005020 diff --git a/platformio.ini b/platformio.ini index 2075a7db..492799df 100644 --- a/platformio.ini +++ b/platformio.ini @@ -183,7 +183,7 @@ build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA -D WLED_DISABLE_ board = esp01_1m platform = ${common.platform_latest} board_build.ldscript = ${common.ldscript_1m0m} -build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK -D WLED_DISABLE_HUESYNC -D WLED_DISABLE_INFRARED +build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK -D WLED_DISABLE_CRONIXIE -D WLED_DISABLE_HUESYNC -D WLED_DISABLE_INFRARED [env:esp01_1m_full] board = esp01_1m diff --git a/wled00/FX.h b/wled00/FX.h index 7022fede..c690a4e5 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -672,7 +672,7 @@ const char JSON_mode_names[] PROGMEM = R"=====([ const char JSON_palette_names[] PROGMEM = R"=====([ -"Default","Random Cycle","Primary Color","Based on Primary","Set Colors","Based on Set","Party","Cloud","Lava","Ocean", +"Default","* Random Cycle","* Color 1","* Colors 1&2","* Color Gradient","* Colors Only","Party","Cloud","Lava","Ocean", "Forest","Rainbow","Rainbow Bands","Sunset","Rivendell","Breeze","Red & Blue","Yellowout","Analogous","Splash", "Pastel","Sunset 2","Beech","Vintage","Departure","Landscape","Beach","Sherbet","Hult","Hult 64", "Drywet","Jul","Grintage","Rewhi","Tertiary","Fire","Icefire","Cyane","Light Pink","Autumn", diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index e25ffb0c..25e460e2 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -743,25 +743,25 @@ void WS2812FX::handle_palette(void) case 2: {//primary color only CRGB prim = col_to_crgb(SEGCOLOR(0)); targetPalette = CRGBPalette16(prim); break;} - case 3: {//based on primary - //considering performance implications - CRGB prim = col_to_crgb(SEGCOLOR(0)); - CHSV prim_hsv = rgb2hsv_approximate(prim); - targetPalette = CRGBPalette16( - CHSV(prim_hsv.h, prim_hsv.s, prim_hsv.v), //color itself - CHSV(prim_hsv.h, MAX(prim_hsv.s - 50,0), prim_hsv.v), //less saturated - CHSV(prim_hsv.h, prim_hsv.s, MAX(prim_hsv.v - 50,0)), //darker - CHSV(prim_hsv.h, prim_hsv.s, prim_hsv.v)); //color itself - break;} - case 4: {//primary + secondary + case 3: {//primary + secondary CRGB prim = col_to_crgb(SEGCOLOR(0)); CRGB sec = col_to_crgb(SEGCOLOR(1)); - targetPalette = CRGBPalette16(sec,prim); break;} - case 5: {//based on primary + secondary + targetPalette = CRGBPalette16(prim,prim,sec,sec); break;} + case 4: {//primary + secondary + tertiary CRGB prim = col_to_crgb(SEGCOLOR(0)); CRGB sec = col_to_crgb(SEGCOLOR(1)); CRGB ter = col_to_crgb(SEGCOLOR(2)); targetPalette = CRGBPalette16(ter,sec,prim); break;} + case 5: {//primary + secondary (+tert if not off), more distinct + CRGB prim = col_to_crgb(SEGCOLOR(0)); + CRGB sec = col_to_crgb(SEGCOLOR(1)); + if (SEGCOLOR(2)) { + CRGB ter = col_to_crgb(SEGCOLOR(2)); + targetPalette = CRGBPalette16(prim,prim,prim,prim,prim,sec,sec,sec,sec,sec,ter,ter,ter,ter,ter,prim); + } else { + targetPalette = CRGBPalette16(prim,prim,prim,prim,prim,prim,prim,prim,sec,sec,sec,sec,sec,sec,sec,sec); + } + break;} case 6: //Party colors targetPalette = PartyColors_p; break; case 7: //Cloud colors diff --git a/wled00/data/index.htm b/wled00/data/index.htm index e4c87779..9bf3de6b 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -950,7 +950,7 @@ input[type=number]::-webkit-outer-spin-button {

-Made with by Aircoookie and the WLED community +Made with by Aircoookie and the WLED community