diff --git a/.vs/BLLEDController/v16/.suo b/.vs/BLLEDController/v16/.suo new file mode 100644 index 0000000..121a984 Binary files /dev/null and b/.vs/BLLEDController/v16/.suo differ diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite new file mode 100644 index 0000000..25b3cee Binary files /dev/null and b/.vs/slnx.sqlite differ diff --git a/.vscode/settings.json b/.vscode/settings.json index 909b171..567aba2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -29,7 +29,10 @@ "filesystem": "cpp", "fstream": "cpp", "locale": "cpp", - "random": "cpp" + "random": "cpp", + "sstream": "cpp", + "cmath": "cpp", + "algorithm": "cpp" }, "cmake.configureOnOpen": false } \ No newline at end of file diff --git a/esphome.html b/esphome.html new file mode 100644 index 0000000..9445843 --- /dev/null +++ b/esphome.html @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/firmware/manifestexpirimental.json b/firmware/manifestexpirimental.json index d5035da..ef1b302 100644 --- a/firmware/manifestexpirimental.json +++ b/firmware/manifestexpirimental.json @@ -1,12 +1,12 @@ { "name": "BLLEDCONTROLLER", - "version": "Experimental 25 2 24", + "version": "Experimental 16 3 24", "new_install_prompt_erase": true, "builds": [ { "chipFamily": "ESP32", "parts": [ - { "path": "esp32dev/BLLC_Experimental_25.2.24.bin", "offset": 0 } + { "path": "esp32dev/BLLC_Experimental_16.3.24.bin", "offset": 0 } ] } ] diff --git a/src/AutoGrowBufferStream.h b/src/AutoGrowBufferStream.h index 66547f9..95c2fd0 100644 --- a/src/AutoGrowBufferStream.h +++ b/src/AutoGrowBufferStream.h @@ -29,7 +29,7 @@ class AutoGrowBufferStream : public Stream if (this->_len + 1 > this->buffer_size) { auto tmp = (char*)realloc(this->_buffer, this->buffer_size + BUFFER_INCREMENTS); if (tmp == NULL) { - Serial.println("Failed to grow buffer"); + Serial.println(F("Failed to grow buffer")); return 0; } this->_buffer = tmp; diff --git a/src/filesystem.h b/src/filesystem.h index 6b23d65..a10d912 100644 --- a/src/filesystem.h +++ b/src/filesystem.h @@ -26,29 +26,50 @@ char* generateRandomString(int length) { void saveFileSystem(){ Serial.println(F("Saving config")); - - DynamicJsonDocument json(512); - json["brightness"] = printerConfig.brightness; + + JsonDocument json; json["printerIp"] = printerConfig.printerIP; json["accessCode"] = printerConfig.accessCode; json["serialNumber"] = printerConfig.serialNumber; + json["ssid"] = globalVariables.SSID; + json["appw"] = globalVariables.APPW; //json["webpagePassword"] = printerConfig.webpagePassword; - + json["bssi"] = printerConfig.BSSID; + json["brightness"] = printerConfig.brightness; + // BLLED Settings json["replicatestate"] = printerConfig.replicatestate; json["errordetection"] = printerConfig.errordetection; json["finishindication"] = printerConfig.finishindication; + json["lidarLightsOff"] = printerConfig.lidarLightsOff; + json["inactivityEnabled"] = printerConfig.inactivityEnabled; + json["inactivityTimeOut"] = printerConfig.inactivityTimeOut; + json["discoMode"] = printerConfig.discoMode; + json["maintMode"] = printerConfig.maintMode; + // Debugging + json["debuging"] = printerConfig.debuging; + json["debugingchange"] = printerConfig.debugingchange; json["mqttdebug"] = printerConfig.mqttdebug; - - json["ssid"] = globalVariables.SSID; - json["appw"] = globalVariables.APPW; - + // Test - Fixed LED Colors + json["testRGB"] = printerConfig.testRGB; + json["testwarmwhite"] = printerConfig.testwarmwhite; + json["testcoldwhite"] = printerConfig.testcoldwhite; + // Customise LED Colors + json["wifiRGB"] = printerConfig.wifiRGB; + json["finishRGB"] = printerConfig.finishRGB; + json["pauseRGB"] = printerConfig.pauseRGB; + json["firstlayerRGB"] = printerConfig.firstlayerRGB; + json["nozzleclogRGB"] = printerConfig.nozzleclogRGB; + json["hmsSeriousRGB"] = printerConfig.hmsSeriousRGB; + json["hmsFatalRGB"] = printerConfig.hmsFatalRGB; + json["filamentRunoutRGB"] = printerConfig.filamentRunoutRGB; + json["frontCoverRGB"] = printerConfig.frontCoverRGB; + json["nozzleTempRGB"] = printerConfig.nozzleTempRGB; + json["bedTempRGB"] = printerConfig.bedTempRGB; File configFile = LittleFS.open(configPath, "w"); - if (!configFile) { Serial.println(F("Failed to save config")); return; } - serializeJson(json, configFile); configFile.close(); Serial.println(F("Config Saved")); @@ -66,41 +87,63 @@ void loadFileSystem(){ } attempts++; Serial.println(F("Failed to open config file, retrying..")); - delay(3000); + delay(2000); } - if (!configFile) { - Serial.println(F("Failed to open config file after retries")); + Serial.print(F("Failed to open config file after ")); + Serial.print(attempts); + Serial.println(F(" retries")); + Serial.println(F("Clearing config")); // LittleFS.remove(configPath); saveFileSystem(); return; } - size_t size = configFile.size(); - std::unique_ptr buf(new char[size]); configFile.readBytes(buf.get(), size); - DynamicJsonDocument json(512); - + JsonDocument json; auto deserializeError = deserializeJson(json, buf.get()); if (!deserializeError) { - printerConfig.brightness = json["brightness"]; - + strcpy(globalVariables.SSID, json["ssid"]); + strcpy(globalVariables.APPW, json["appw"]); strcpy(printerConfig.printerIP, json["printerIp"]); strcpy(printerConfig.accessCode, json["accessCode"]); strcpy(printerConfig.serialNumber, json["serialNumber"]); //strcpy(printerConfig.webpagePassword, json["webpagePassword"]); - - strcpy(globalVariables.SSID, json["ssid"]); - strcpy(globalVariables.APPW, json["appw"]); - + strcpy(printerConfig.BSSID, json["bssi"]); + printerConfig.brightness = json["brightness"]; + //BLLED Settings printerConfig.replicatestate = json["replicatestate"]; printerConfig.errordetection = json["errordetection"]; printerConfig.finishindication = json["finishindication"]; + printerConfig.lidarLightsOff = json["lidarLightsOff"]; + printerConfig.inactivityEnabled = json["inactivityEnabled"]; + printerConfig.inactivityTimeOut = json["inactivityTimeOut"]; + printerConfig.discoMode = json["discoMode"]; + printerConfig.maintMode = json["maintMode"]; + //Debugging + printerConfig.debuging = json["debuging"]; + printerConfig.debugingchange = json["debugingchange"]; printerConfig.mqttdebug = json["mqttdebug"]; + //Test - Fixed LED Colors + strcpy(printerConfig.testRGB, json["testRGB"]); + printerConfig.testwarmwhite = json["testwarmwhite"]; + printerConfig.testcoldwhite = json["testcoldwhite"]; + // Customise LED Colors + strcpy(printerConfig.wifiRGB, json["wifiRGB"]); + strcpy(printerConfig.finishRGB, json["finishRGB"]); + strcpy(printerConfig.pauseRGB, json["pauseRGB"]); + strcpy(printerConfig.firstlayerRGB, json["firstlayerRGB"]); + strcpy(printerConfig.nozzleclogRGB, json["nozzleclogRGB"]); + strcpy(printerConfig.hmsSeriousRGB, json["hmsSeriousRGB"]); + strcpy(printerConfig.hmsFatalRGB, json["hmsFatalRGB"]); + strcpy(printerConfig.filamentRunoutRGB, json["filamentRunoutRGB"]); + strcpy(printerConfig.frontCoverRGB, json["frontCoverRGB"]); + strcpy(printerConfig.nozzleTempRGB, json["nozzleTempRGB"]); + strcpy(printerConfig.bedTempRGB, json["bedTempRGB"]); Serial.println(F("Loaded config")); } else { @@ -131,6 +174,8 @@ void setupFileSystem(){ Serial.println(F("Failed to mount LittleFS")); LittleFS.format(); Serial.println(F("Formatting LittleFS")); + Serial.println(F("Restarting Device")); + delay(1000); ESP.restart(); } Serial.println(F("Mounted LittleFS")); diff --git a/src/leds.h b/src/leds.h index c935d45..00c5e47 100644 --- a/src/leds.h +++ b/src/leds.h @@ -16,7 +16,30 @@ int currentBlue = 0; int currentWarm = 0; int currentCold = 0; -void tweenToColor(int targetRed, int targetGreen, int targetBlue, int targetWarm, int targetCold, int duration) { +struct COLOR { + short r; + short g; + short b; +}; + +COLOR hex2rgb(String hex) { + COLOR color; + long hexcolor; + + if(hex.charAt(0) == '#') { + hex.remove(0,1); + } + while(hex.length() != 6) { + hex += "0"; + } + hexcolor = strtol(hex.c_str(), NULL, 16); + color.r = (hexcolor & 0xFF0000) >> 16; + color.g = (hexcolor & 0x00FF00) >> 8; + color.b = (hexcolor & 0x0000FF); + return color; +} + +void tweenToColor(int targetRed, int targetGreen, int targetBlue, int targetWarm, int targetCold, int duration = 500) { float brightness = (float)printerConfig.brightness/100.0; @@ -27,10 +50,23 @@ void tweenToColor(int targetRed, int targetGreen, int targetBlue, int targetWarm int brightenedCold = round(targetCold * brightness); if (brightenedRed == currentRed && brightenedGreen == currentGreen && brightenedBlue == currentBlue && brightenedWarm == currentWarm && brightenedCold == currentCold){ + // Already set to the requested color if (printerConfig.debuging){ - Serial.println(F("LEDS Trying to change to the same color.")); + Serial.print(F("LEDS already at color: (")); + Serial.print(currentRed); + Serial.print(F(", ")); + Serial.print(currentGreen); + Serial.print(F(", ")); + Serial.print(currentBlue); + Serial.print(F(", ")); + Serial.print(currentWarm); + Serial.print(F(", ")); + Serial.print(currentCold); + Serial.print(F(" Brightness: ")); + Serial.print(printerConfig.brightness); + Serial.println(F(")")); }; - return; // already at that color + return; } float stepTime = (float)duration / 255.0; int redStep = (brightenedRed - currentRed) / 255; @@ -65,19 +101,29 @@ void tweenToColor(int targetRed, int targetGreen, int targetBlue, int targetWarm analogWrite(bluePin, currentBlue); analogWrite(warmPin, currentWarm); analogWrite(coldPin, currentCold); - - //Serial.println(F("Leds: ")); - //Serial.println(currentRed); - //Serial.println(currentGreen); - //Serial.println(currentBlue); - //Serial.println(currentWarm); - //Serial.println(currentCold); +} + +//Helper functions to allow changes colors by using a string or integer hex code +//Example: tweenToColor("#33FD67") +void tweenToColor(String strTargetColor) { + COLOR targetcolor; + targetcolor = hex2rgb(strTargetColor); + tweenToColor(targetcolor.r, targetcolor.g, targetcolor.b, 0, 0); +} +//Example: tweenToColor(0xFFACA5) +void tweenToColor(int hexValue) { + COLOR targetcolor; + targetcolor.r = ((hexValue >> 16) & 0xFF) / 255.0; + targetcolor.g = ((hexValue >> 8) & 0xFF) / 255.0; + targetcolor.b = ((hexValue) & 0xFF) / 255.0; + tweenToColor(targetcolor.r, targetcolor.g, targetcolor.b, 0, 0); } float hue = 0.0; void RGBCycle() { - if (printerConfig.turbo == false) { + if (printerConfig.discoMode == false) { + //Skip changing colors if RGB Mode not enabled return; } if (printerVariables.online == false) { @@ -99,6 +145,15 @@ void RGBCycle() { currentWarm = 0; currentCold = 0; + //Adjust to set brightness level + float brightness = (float)printerConfig.brightness/100.0; + + int brightenedRed = round(currentRed * brightness); + int brightenedGreen = round(currentGreen * brightness); + int brightenedBlue = round(currentBlue * brightness); + int brightenedWarm = round(currentWarm * brightness); + int brightenedCold = round(currentCold * brightness); + analogWrite(redPin, currentRed); analogWrite(greenPin, currentGreen); analogWrite(bluePin, currentBlue); @@ -106,172 +161,419 @@ void RGBCycle() { analogWrite(coldPin, currentCold); } + + +void idleStatus(){ + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.print(F("Stage ")); + Serial.print(printerVariables.stage); + Serial.print(F(", IDLE Turning LEDs to WHITE. Brightness: ")); + Serial.println(printerConfig.brightness); + }; +} + + + void updateleds(){ - if (printerConfig.debuging){ - Serial.println(F("Updating leds")); + //Maintenance Mode - White lights on regardless of printer power, WiFi or MQTT connection + //priortised over Wifi Strength Display or Custom TEST color + if (printerConfig.maintMode && printerConfig.updateMaintenance){ + tweenToColor(0,0,0,255,255); //WHITE + printerConfig.updateMaintenance = false; + if (printerConfig.debugingchange){ + Serial.println(F("Maintenance Mode, turning LEDs to WHITE")); + }; + return; + } + + //Use LED to show WIFI Strength (enabled via Setup Webpage, priortised over Custom TEST color) + if (printerConfig.debugwifi == true && !printerConfig.maintMode){ + //<=-50 dBm Green, <= -60 dBm LightGreen, <= -70 dBm Yellow, <= -80 dBm Orange, >80 Red + if (WiFi.status() == WL_CONNECTED){ + long wifiNow = WiFi.RSSI(); + if (printerConfig.debugingchange){ + Serial.print(F("WiFi Strength Visialisation, display LEDs for: ")); + Serial.println(wifiNow); + } + if (wifiNow >= -50) tweenToColor(0,255,0,0,0); //GREEN + else if (wifiNow >= -60) tweenToColor(128,255,0,0,0); //LIGHTGREEN + else if (wifiNow >= -70) tweenToColor(255,255,0,0,0); //YELLOW + else if (wifiNow >= -80) tweenToColor(255,128,0,0,0); //ORANGE + else if (wifiNow < -80) tweenToColor(255,0,0,0,0); //RED + else tweenToColor(0,0,255,0,0); //BLUE + }; + return; + } + + //TEST Color Enabled - LED ON regardless of printer state + if (printerVariables.testcolorEnabled && !printerConfig.maintMode && !printerConfig.debugwifi){ + COLOR customColor = hex2rgb(printerConfig.testRGB); + + tweenToColor(customColor.r,customColor.g,customColor.b,printerConfig.testwarmwhite,printerConfig.testcoldwhite); //Variable Test Color + if (printerConfig.debugingchange){ + Serial.print(F("LED Test ON, Changing LEDs to selected color: ")); + Serial.print(customColor.r); + Serial.print(F(" ")); + Serial.print(customColor.g); + Serial.print(F(" ")); + Serial.print(customColor.b); + Serial.print(F(" ")); + Serial.print(printerConfig.testwarmwhite); + Serial.print(F(" ")); + Serial.print(printerConfig.testcoldwhite); + Serial.print(F(" Brightness: ")); + Serial.println(printerConfig.brightness); + }; + printerConfig.updateTestLEDS = false; + return; + } + + if(printerVariables.testcolorEnabled || printerConfig.maintMode || printerConfig.debugwifi){ + //Skip trying to set a color as it's in one of the override states + return; + } + + //From here the BBLP status sets the colors + if (printerConfig.debuging == true){ + Serial.println(F("Updating LEDs")); Serial.println(printerVariables.stage); Serial.println(printerVariables.gcodeState); - Serial.println(printerVariables.ledstate); + Serial.println(printerVariables.printerledstate); Serial.println(printerVariables.hmsstate); Serial.println(printerVariables.parsedHMS); } - //OFF - if (printerConfig.turbo == true){ + //TOGGLE LIGHTS via DOOR + //If door is closed twice in 6 seconds, it will flip the state of the lights + if (printerVariables.doorSwitchenabled == true){ + if (printerConfig.debugingchange){ + Serial.print(F("Door closed twice within 6 seconds - Toggling LEDs to ")); + } + if(currentWarm == 0 && currentCold == 0) + { + tweenToColor(0,0,0,255,255); //WHITE + if (printerConfig.debuging || printerConfig.debugingchange) { + Serial.println(F("ON")); + } + } + else + { + tweenToColor(0,0,0,0,0); //OFF + //Shortcut to idle state - note: light will go back on immediately if there is an MQTT change of any sort + printerVariables.inactivityLightsOff = true; + printerVariables.inactivityStartms = millis()-printerConfig.inactivityTimeOut; + if (printerConfig.debuging || printerConfig.debugingchange) { + Serial.println(F("OFF")); + } + } + printerVariables.doorSwitchenabled = false; + } + + + //OFF -- OFF -- OFF -- OFF + + if (printerConfig.discoMode == true){ + //Skip all the following code that sets specific colors if RGB Mode is on - done in RGBCycle() return; } - if (printerVariables.online == false){ //printer offline - tweenToColor(0,0,0,0,0,500); //OFF - if (printerConfig.debuging){ - Serial.println(F("Printer offline, Turning Leds off")); + //printer offline and MQTT disconnect more than 5 seconds. + if (printerVariables.online == false && (millis() - printerVariables.disconnectMQTTms) >= 5000){ + tweenToColor(0,0,0,0,0); //OFF + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.println(F("Printer offline, Turning LEDs OFF")); }; return; } - if (printerVariables.ledstate == false && printerConfig.replicatestate == true){ // replicate printer behaviour - tweenToColor(0,0,0,0,0,500); //OFF - if (printerConfig.debuging){ - Serial.println(F("Chamber light off, Turning Leds off")); + // replicate printer behaviour + if (printerVariables.printerledstate == false && printerConfig.replicatestate == true){ + tweenToColor(0,0,0,0,0); //OFF + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.println(F("LED Replication, Turning LEDs OFF")); }; return; } - if (printerVariables.stage == 14){ //Cleaning nozzle - tweenToColor(0,0,0,0,0,500); //OFF - if (printerConfig.debuging){ - Serial.println(F("Cleaning nozzle, Turning Leds off")); + //Cleaning nozzle + if (printerVariables.stage == 14 && printerConfig.lidarLightsOff){ + tweenToColor(0,0,0,0,0); //OFF + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.println(F("Stage 14, CLEANING NOZZLE, Turning LEDs OFF")); }; return; } - if (printerVariables.stage == 9){ //Scaning surface - tweenToColor(0,0,0,0,0,500); //OFF - if (printerConfig.debuging){ - Serial.println(F("Scanning Surface, Turning Leds off")); + //Auto Bed Leveling + if (printerVariables.stage == 1 && printerConfig.lidarLightsOff){ + tweenToColor(0,0,0,0,0); //OFF + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.println(F("Stage 1, BED LEVELING, Turning LEDs OFF")); + }; + return; + } + + //Calibrating Extrusion + if (printerVariables.stage == 8 && printerConfig.lidarLightsOff){ + tweenToColor(0,0,0,0,0); //OFF + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.println(F("Stage 8, CALIBRATING EXTRUSION, Turning LEDs OFF")); + }; + return; + } + + //Scaning surface + if (printerVariables.stage == 9 && printerConfig.lidarLightsOff){ + tweenToColor(0,0,0,0,0); //OFF + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.println(F("Stage 9, SCANNING BED SURFACE, Turning LEDs OFF")); + }; + return; + } + + //Inspecting First Layer + if ((printerVariables.stage == 10 && printerConfig.lidarLightsOff) + || (printerVariables.inspectingFirstLayer && printerConfig.lidarLightsOff)){ + tweenToColor(0,0,0,0,0); //OFF + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.println(F("Stage 10 / HMS 0C00, FIRST LAYER INSPECTION, Turning LEDs OFF")); + }; + return; + } + + //Calibrating MicroLidar + if (printerVariables.stage == 12 && printerConfig.lidarLightsOff){ + tweenToColor(0,0,0,0,0); //OFF + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.println(F("Stage 12, CALIBRATING MICRO LIDAR, Turning LEDs OFF")); + }; + return; + } + + // Idle Timeout (Has to be enabled) + if ((printerVariables.stage == -1 || printerVariables.stage == 255) + && !printerVariables.waitingForDoor + && (millis() - printerVariables.inactivityStartms) > printerConfig.inactivityTimeOut + && printerVariables.inactivityLightsOff == false + && printerConfig.inactivityEnabled){ + tweenToColor(0,0,0,0,0); //OFF + printerVariables.inactivityLightsOff = true; + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.print(F("Idle Timeout [")); + Serial.print((int)(printerConfig.inactivityTimeOut / 60000)); + Serial.println(F(" mins] - Turning LEDs OFF")); }; return; } - //RED + //RED -- RED -- RED -- RED - if (printerConfig.errordetection == true){ // allow errordetection to turn ledstrip red + // allow errordetection to turn ledstrip red + if (printerConfig.errordetection == true){ + + //SERIOUS HMS state if (printerVariables.parsedHMS == "Serious"){ - tweenToColor(255,0,0,0,0,500); //RED - if (printerConfig.debuging){ - Serial.println(F("Serious problem, Turning Leds red")); + tweenToColor(printerConfig.hmsSeriousRGB); //Customisable - Default is RED + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.print(F("HMS Severity, SERIOUS PROBLEM, Turning LEDs to ")); + Serial.println(printerConfig.hmsSeriousRGB); }; return; - } + }; + //FATAL HMS state if (printerVariables.parsedHMS == "Fatal"){ - tweenToColor(255,0,0,0,0,500); //RED - if (printerConfig.debuging){ - Serial.println(F("Fatal problem, Turning Leds red")); + tweenToColor(printerConfig.hmsFatalRGB); //Customisable - Default is RED + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.print(F("HMS Severity, FATAL PROBLEM, Turning LEDs to ")); + Serial.println(printerConfig.hmsFatalRGB); }; return; - } + }; - if (printerVariables.stage == 6){ //Fillament runout - tweenToColor(255,0,0,0,0,500); //RED - if (printerConfig.debuging){ - Serial.println(F("Fillament runout, Turning Leds red")); + //Fillament runout + if (printerVariables.stage == 6){ + tweenToColor(printerConfig.filamentRunoutRGB); //Customisable - Default is RED + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.print(F("Stage 17, FILAMENT RUNOUT, Turning LEDs to ")); + Serial.println(printerConfig.filamentRunoutRGB); }; return; - } + }; - if (printerVariables.stage == 17){ //Front Cover Removed - tweenToColor(255,0,0,0,0,500); //RED - if (printerConfig.debuging){ - Serial.println(F("Front Cover Removed, Turning Leds red")); + //Front Cover Removed + if (printerVariables.stage == 17){ + tweenToColor(printerConfig.frontCoverRGB); //Customisable - Default is RED + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.print(F("Stage 17, FRONT COVER REMOVED, Turning LEDs to ")); + Serial.println(printerConfig.frontCoverRGB); }; return; - } + }; - if (printerVariables.stage == 20){ //Nozzle Temp fail - tweenToColor(255,0,0,0,0,500); //RED - if (printerConfig.debuging){ - Serial.println(F("Nozzle Temp fail, Turning Leds red")); + //Nozzle Temp fail + if (printerVariables.stage == 20){ + tweenToColor(printerConfig.nozzleTempRGB); //Customisable - Default is RED + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.print(F("Stage 20, NOZZLE TEMP FAIL, Turning LEDs to ")); + Serial.println(printerConfig.nozzleTempRGB); }; return; - } + }; - if (printerVariables.stage == 21){ //Bed Temp Fail - tweenToColor(255,0,0,0,0,500); //RED - if (printerConfig.debuging){ - Serial.println(F("Bed Temp fail, Turning Leds red")); + //Bed Temp Fail + if (printerVariables.stage == 21){ + tweenToColor(printerConfig.bedTempRGB); //Customisable - Default is RED + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.print(F("Stage 21, BED TEMP FAIL, Turning LEDs to ")); + Serial.println(printerConfig.bedTempRGB); }; return; - } + }; }; - //GREEN - - if ((millis() - printerVariables.finishstartms) <= 300000 && printerVariables.gcodeState == "FINISH" && printerConfig.finishindication == true){ - tweenToColor(0,255,0,0,0,500); //ON - if (printerConfig.debuging){ - Serial.println(F("Finished print, Turning Leds green")); - Serial.println(F("Leds should stay on for: ")); - Serial.print((millis() - printerVariables.finishstartms)); - Serial.print(F(" MS")); + //BLUE -- BLUE -- BLUE -- BLUE + + // Pause (by user or via Gcode) + if ((printerVariables.stage == 6 || printerVariables.stage == 30) || printerVariables.gcodeState == "PAUSE"){ + tweenToColor(printerConfig.pauseRGB); //Customisable - Default is BLUE + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.print(F("Stage ")); + Serial.print(printerVariables.stage); + Serial.print(F(", gcodeState PAUSE, Turning LEDs to ")); + Serial.println(printerConfig.pauseRGB); }; return; } - //BLUE + //First Layer Error PAUSED + if (printerVariables.stage == 34){ + tweenToColor(printerConfig.firstlayerRGB); //Customisable - Default is BLUE + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.print(F("Stage 34, FIRST LAYER ERROR, PAUSED Turning LEDs to ")); + Serial.println(printerConfig.firstlayerRGB); + }; + return; + } - if (printerVariables.stage == 2 && printerVariables.gcodeState == "PAUSE"){// Pause - tweenToColor(0,0,255,0,0,500); //Blue - if (printerConfig.debuging){ - Serial.println(F("Pause, Turning Leds blue")); + //Nozzle Clog PAUSED + if (printerVariables.stage == 35){ + tweenToColor(printerConfig.nozzleclogRGB); //Customisable - Default is BLUE + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.print(F("Stage 35, NOZZLE CLOG, PAUSED Turning LEDs to ")); + Serial.println(printerConfig.nozzleclogRGB); }; return; } - //ON + //ON -- ON -- ON -- ON - if (printerVariables.stage == 0 || printerVariables.stage == 2 && printerVariables.gcodeState == "RUNNING"){ //Printing or Resume after Pausing - tweenToColor(0,0,0,255,255,500); //ON - if (printerConfig.debuging){ - Serial.println(F("Printing, Turning Leds On")); + //Initial Boot + if (printerVariables.initalisedLEDs == false) { + tweenToColor(0,0,0,255,255); //WHITE + printerVariables.initalisedLEDs = true; + printerVariables.inactivityStartms = millis(); + printerVariables.waitingForDoor = false; + printerVariables.lastdoorClosems = millis(); + Serial.println(F("Initial BLLED bootup - Turning LEDs to WHITE, Start IDLE timer")); + return; + } + + //Preheating Bed + if (printerVariables.stage == 2){ + tweenToColor(0,0,0,255,255); //WHITE + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.println(F("Stage 2, PREHEATING BED, Turning LEDs to WHITE")); }; return; } - if (printerVariables.stage == -1){ // Idle - tweenToColor(0,0,0,255,255,500); //ON - if (printerConfig.debuging){ - Serial.println(F("Idle, Turning Leds On")); + //Printing or Resume after Pausing + if (printerVariables.stage == 0 && printerVariables.gcodeState == "RUNNING"){ + tweenToColor(0,0,0,255,255); //WHITE + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.print(F("Stage 0, PRINTING - gcodeState RUNNING, Turning LEDs to WHITE. Brightness: ")); + Serial.println(printerConfig.brightness); }; return; } - if (printerVariables.stage == -2){ //Offline - tweenToColor(0,0,0,255,255,500); //ON - if (printerConfig.debuging){ - Serial.println(F("Stage -2, Turning Leds On")); + //for IDLE - P1 uses 255, X1 uses -1 + //This covers both IDLE & FINISH + if ((printerVariables.stage == -1 || printerVariables.stage == 255) + && printerVariables.waitingForDoor == false + && (millis() - printerVariables.inactivityStartms < printerConfig.inactivityTimeOut)){ + tweenToColor(0,0,0,255,255); //WHITE + idleStatus(); + return; + } + + //User Cancelled Print + if (printerVariables.gcodeState == "FAILED"){ + tweenToColor(0,0,0,255,255); //WHITE + idleStatus(); + return; + } + //Print file just sent + if (printerVariables.gcodeState == "PREPARE"){ + tweenToColor(0,0,0,255,255); //WHITE + idleStatus(); + return; + } + + //Offline + if (printerVariables.gcodeState == "OFFLINE" || printerVariables.stage == -2){ + tweenToColor(0,0,0,255,255); //WHITE + idleStatus(); + return; + } + + //GREEN -- GREEN -- GREEN -- GREEN + + //Sets to green when print finishes AND user wants Finish Indication enabled + if (printerVariables.finished == true && printerConfig.finishindication == true){ + tweenToColor(printerConfig.finishRGB); //Customisable - Default is GREEN + printerVariables.finished = false; + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.print(F("Finished print, Turning LEDs to ")); + Serial.println(printerConfig.finishRGB); }; return; } } + void setupLeds() { + Serial.println(F("Updating from setupleds")); pinMode(redPin, OUTPUT); pinMode(greenPin, OUTPUT); pinMode(bluePin, OUTPUT); pinMode(coldPin, OUTPUT); pinMode(warmPin, OUTPUT); - Serial.println(F("Updating from setupleds")); - updateleds(); } void ledsloop(){ RGBCycle(); - if((printerVariables.finishstartms > 0 && millis() - printerVariables.finishstartms) >= 300000 && printerVariables.gcodeState == "FINISH"){ - printerVariables.finishstartms = 0; - printerVariables.gcodeState = "IDLE"; - Serial.println(F("Updating from finishloop")); + + // Turn off GREEN if... finished and Door OPENED or CLOSED in last 5 secs AND user wants Finish Indication enabled + // Can't use Boolean finished as printer defaults back to IDLE via MQTT + if((printerVariables.waitingForDoor && printerConfig.finishindication == true + && ((millis() - printerVariables.lastdoorClosems) < 6000 || (millis() - printerVariables.lastdoorOpenms) < 6000))){ + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.println(F("Updating from finishloop after Door interaction - Starting IDLE timer")); + }; + printerVariables.waitingForDoor = false; + printerVariables.inactivityStartms = millis(); + updateleds(); + } + + //Need an trigger action to run updateleds() so lights turn off + //There is no change in the printer STATE, just monitoring the timer and triggering when over a threshhold + if(printerConfig.inactivityEnabled && (millis() - printerVariables.inactivityStartms) > printerConfig.inactivityTimeOut + && printerVariables.finished == false && printerVariables.inactivityLightsOff == false) + { + //Opening or Closing the Door will turn LEDs back on and restart the timer. updateleds(); } delay(10); diff --git a/src/main.cpp b/src/main.cpp index 4b02d71..202e6ed 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,54 +7,97 @@ #include "serialmanager.h" #include "wifi-manager.h" +void defaultcolors(){ + Serial.println(F("Setting default customisable colors")); + strcpy(printerConfig.testRGB, "#3F3CFB"); //Violet + strcpy(printerConfig.wifiRGB, "#FFA500"); //Orange + strcpy(printerConfig.finishRGB, "#00FF00"); //Green + strcpy(printerConfig.pauseRGB, "#0000FF"); //Blue + strcpy(printerConfig.firstlayerRGB, "#0000FF"); //Blue + strcpy(printerConfig.nozzleclogRGB, "#0000FF"); //Blue + strcpy(printerConfig.hmsSeriousRGB, "#FF0000"); //Red + strcpy(printerConfig.hmsFatalRGB, "#FF0000"); //Red + strcpy(printerConfig.filamentRunoutRGB, "#FF0000"); //Red + strcpy(printerConfig.frontCoverRGB, "#FF0000"); //Red + strcpy(printerConfig.nozzleTempRGB, "#FF0000"); //Red + strcpy(printerConfig.bedTempRGB, "#FF0000"); //Red +} + void setup(){ Serial.begin(115200); delay(100); Serial.println(F("Initializing")); Serial.println(ESP.getFreeHeap()); + defaultcolors(); setupLeds(); - tweenToColor(255,255,255,255,255,500); - delay(2000); - tweenToColor(255,0,0,0,0,500); + tweenToColor(255,255,255,255,255); //ALL LEDS ON + delay(1000); + + tweenToColor(255,0,0,0,0); //RED setupFileSystem(); loadFileSystem(); - delay(2000); - tweenToColor(255,165,0,0,0,500); + delay(500); + + tweenToColor(printerConfig.wifiRGB); //Customisable - Default is ORANGE setupSerial(); if (strlen(globalVariables.SSID) == 0 || strlen(globalVariables.APPW) == 0) { Serial.println(F("SSID or password is missing. Please configure both by going to: https://dutchdevelop.com/blled-configuration-setup/")); - tweenToColor(255,0,255,0,0,500); + tweenToColor(255,0,255,0,0); //PINK return; } - - if (!connectToWifi()){ + + scanNetwork(); //Sets the MAC address for following connection attempt + if(!connectToWifi()){ return; - }; - delay(2000); - tweenToColor(0,0,255,0,0,500); - setupWebserver(); + } - delay(2000); + tweenToColor(0,0,255,0,0); //BLUE + setupWebserver(); + delay(500); - tweenToColor(0,0,0,0,0,500); + + tweenToColor(34,224,238,0,0); //CYAN setupMqtt(); + Serial.println(); Serial.println(F("BLLED Controller started")); - + Serial.println(); + tweenToColor(0,0,0,0,0,3000); //Fade to Black before starting globalVariables.started = true; } void loop(){ serialLoop(); - if (globalVariables.started == true){ + if(printerConfig.maintMode){ + //Doesn't require monitoring of Wifi or MQTT if LEDs only need to be ON + webserverloop(); + if(printerConfig.updateMaintenance) updateleds(); + printerVariables.inactivityStartms = millis(); // Don't turn light off (due to inactivity) if in Maintenance Mode + } + else if (printerVariables.testcolorEnabled){ + //Doesn't require monitoring of Wifi or MQTT if LEDs set to a custom color + webserverloop(); + if(printerConfig.updateTestLEDS) ledsloop(); + } + else if (globalVariables.started){ mqttloop(); webserverloop(); ledsloop(); + if (WiFi.status() != WL_CONNECTED){ - Serial.println(F("Lost Connection, Reconnecting")); + Serial.println(F("Reconnecting to WiFi...")); WiFi.disconnect(); + delay(10); WiFi.reconnect(); } } -} \ No newline at end of file + if(printerConfig.rescanWiFiNetwork) + { + Serial.println(F("Web submitted refresh of Wifi Scan (assigning Strongest AP)")); + tweenToColor(printerConfig.wifiRGB); //Customisable - Default is ORANGE + scanNetwork(); //Sets the MAC address for following connection attempt + printerConfig.rescanWiFiNetwork = false; + updateleds(); + } +} diff --git a/src/mqttmanager.h b/src/mqttmanager.h index 4633a0c..ad2f4bf 100644 --- a/src/mqttmanager.h +++ b/src/mqttmanager.h @@ -3,9 +3,6 @@ #include #include -static int mqttbuffer = 32768; -static int mqttdocument = 32768; //16384 - #include #include #include @@ -26,126 +23,256 @@ AutoGrowBufferStream stream; unsigned long mqttattempt = (millis()-3000); +//With a Default BLLED +//Expected information when viewing MQTT status messages + +//gcode_state stg_cur BLLED LED control Comments +//------------------------------------------------------------------------ +//IDLE -1 White Printer just powered on +//RUNNING -1 White Printer sent print file +//RUNNING 2 White PREHEATING BED +//RUNNING 14 OFF (for Lidar) CLEANING NOZZLE +//RUNNING 1 OFF (for Lidar) BED LEVELING +//RUNNING 8 OFF (for Lidar) CALIBRATING EXTRUSION +//RUNNING 0 White All the printing happens here +//FINISH -1 Green After bed is lowered and filament retracted +//FINISH -1 Green BLLED logic waits for a door interaction +//FINISH -1 White After door interaction +//FINISH -1 OFF Inactivity after 30mins + + void connectMqtt(){ + if(WiFi.status() != WL_CONNECTED){ + //Abort MQTT connection attempt when no Wifi + return; + } if (!mqttClient.connected() && (millis() - mqttattempt) >= 3000){ - Serial.println(F("Connecting to mqtt")); + Serial.println(F("Connecting to mqtt...")); if (mqttClient.connect(clientId.c_str(),"bblp",printerConfig.accessCode)){ - Serial.println(F("MWTT connected, subscribing to topic:")); + Serial.print(F("MQTT connected, subscribing to MQTT Topic: ")); Serial.println(report_topic); mqttClient.subscribe(report_topic.c_str()); printerVariables.online = true; - Serial.println(F("Updating from mqtt connnection")); + printerVariables.disconnectMQTTms = 0; + Serial.println(F("Updating LEDs from MQTT connect")); updateleds(); }else{ Serial.println(F("Failed to connect with error code: ")); - Serial.println(mqttClient.state()); - switch (mqttClient.state()) - { - case -4: // MQTT_CONNECTION_TIMEOUT - Serial.println(F("MQTT TIMEOUT")); - break; - case -2: // MQTT_CONNECT_FAILED - Serial.println(F("MQTT CONNECT_FAILED")); - break; - case -3: // MQTT_CONNECTION_LOST - Serial.println(F("MQTT CONNECTION_LOST")); - break; - case -1: // MQTT_DISCONNECTED - Serial.println(F("MQTT DISCONNECTED")); - break; - case 1: - break; - case 2: - break; - case 3: - break; - case 4: - break; - case 5: // MQTT UNAUTHORIZED - Serial.println(F("MQTT UNAUTHORIZED")); - ESP.restart(); - break; + Serial.print(mqttClient.state()); + Serial.print(F(" ")); + ParseMQTTState(mqttClient.state()); + if(mqttClient.state() == 5){ + Serial.println(F("Restarting Device")); + delay(1000); + ESP.restart(); } } } } void ParseCallback(char *topic, byte *payload, unsigned int length){ - DynamicJsonDocument messageobject(mqttdocument); + JsonDocument messageobject; + + JsonDocument filter; + //Rather than showing the entire message to Serial - grabbing only the pertinent bits for BLLED. + //Device Status + filter["print"]["command"] = true; + filter["print"]["fail_reason"] = true; + filter["print"]["gcode_state"] = true; + filter["print"]["print_gcode_action"] = true; + filter["print"]["print_real_action"] = true; + filter["print"]["hms"] = true; + filter["print"]["lights_report"] = true; + filter["print"]["home_flag"] = true; + filter["print"]["stg_cur"] = true; + filter["print"]["print_error"] = true; + filter["print"]["wifi_signal"] = true; + + //Adding extra filters that looking interesting + //Could be useful for additional functionality that might be developed at a later date + //Color blend, the closer to 100% - I dunno + + //Print Job Status + //filter["print"]["subtask_name"] = true; //Print Name + //filter["print"]["layer_num"] = true; + //filter["print"]["total_layer_num"] = true; + //filter["print"]["mc_percent"] = true; + //filter["print"]["mc_print_error_code"] = true; + //filter["print"]["mc_print_stage"] = true; + //filter["print"]["mc_print_sub_stage"] = true; + //filter["print"]["mc_remaining_time"] = true; + + //Temperatures + //filter["print"]["bed_target_temper"] = true; + //filter["print"]["bed_temper"] = true; + //filter["print"]["nozzle_target_temper"] = true; + //filter["print"]["nozzle_temper"] = true; + //filter["print"]["chamber_temper"] = true; - DynamicJsonDocument filter(128); - filter["print"]["*"] = true; - filter["camera"]["*"] = true; - auto deserializeError = deserializeJson(messageobject, payload, length, DeserializationOption::Filter(filter)); if (!deserializeError){ if (printerConfig.debuging){ - Serial.println(F("Mqtt message received,")); - Serial.println(F("FreeHeap: ")); - Serial.print(ESP.getFreeHeap()); - Serial.println(); + Serial.print(F("Mqtt message received, ")); + Serial.print(F("FreeHeap: ")); + Serial.println(ESP.getFreeHeap()); + } + + bool Changed = false; + + if (messageobject["print"].containsKey("command")){ + if (messageobject["print"]["command"].as() == "gcode_line"){ + //gcode_line used a lot during print initialisations - Skip these + return; + } + } + if(messageobject.size() == 0) + { + //Null or Filtered essage that is not 'print' - Ignore + return; } + //Output Filtered MQTT message if (printerConfig.mqttdebug){ - Serial.println(F("Mqtt payload:")); - Serial.println(); + Serial.print(F("(Filtered) MQTT payload, ")); + Serial.print(messageobject.size()); + Serial.print(F(", ")); serializeJson(messageobject, Serial); Serial.println(); } - bool Changed = false; + //Check BBLP Stage if (messageobject["print"].containsKey("stg_cur")){ - printerVariables.stage = messageobject["print"]["stg_cur"]; - Changed = true; + if (printerVariables.stage != messageobject["print"]["stg_cur"].as() ){ + printerVariables.inactivityStartms = millis(); //restart idle timer + printerVariables.stage = messageobject["print"]["stg_cur"]; + + if (printerConfig.debugingchange || printerConfig.debuging){ + Serial.print(F("MQTT update - stg_cur now: ")); + Serial.println(printerVariables.stage); + } + Changed = true; + } }else{ if (printerConfig.debuging){ - Serial.println(F("stg_cur not in message")); + Serial.println(F("MQTT stg_cur not in message")); } } + //Check BBLP GCode State if (messageobject["print"].containsKey("gcode_state")){ - printerVariables.gcodeState = messageobject["print"]["gcode_state"].as(); - if (printerVariables.gcodeState == "FINISH"){ - if (printerVariables.finished == false){ + String mqttgcodeState = messageobject["print"]["gcode_state"].as(); + + if(mqttgcodeState =="RUNNING" || mqttgcodeState =="PAUSE"){ + //Never turn off light (due to idle timer) while in this state + printerVariables.inactivityStartms = millis(); + } + + // Onchange of gcodeState... + if(printerVariables.gcodeState != mqttgcodeState){ + printerVariables.inactivityStartms = millis(); //restart idle timer + + if(mqttgcodeState =="FINISH"){ printerVariables.finished = true; - printerVariables.finishstartms = millis(); + if(printerConfig.finishindication == true) printerVariables.waitingForDoor = true; } - }else{ - printerVariables.finished = false; + printerVariables.gcodeState = mqttgcodeState; + + if (printerConfig.debugingchange || printerConfig.debuging){ + Serial.print(F("MQTT update - gcode_state now: ")); + Serial.println(printerVariables.gcodeState); + } + Changed = true; } - Changed = true; } + if (messageobject["print"].containsKey("lights_report")) { JsonArray lightsReport = messageobject["print"]["lights_report"]; for (JsonObject light : lightsReport) { if (light["node"] == "chamber_light") { - printerVariables.ledstate = light["mode"] == "on"; - Changed = true; + if(printerVariables.printerledstate != (light["mode"] == "on")){ + printerVariables.printerledstate = light["mode"] == "on"; + if (printerConfig.debugingchange || printerConfig.debuging){ + Serial.print(F("MQTT chamber_light now: ")); + Serial.println(printerVariables.printerledstate); + } + Changed = true; + } } } }else{ if (printerConfig.debuging){ - Serial.println(F("lights_report not in message")); + Serial.println(F("MQTT lights_report not in message")); } } - + //Bambu Health Management System (HMS) if (messageobject["print"].containsKey("hms")){ + String oldHMS = ""; + oldHMS = printerVariables.parsedHMS; + printerVariables.hmsstate = false; printerVariables.parsedHMS = ""; for (const auto& hms : messageobject["print"]["hms"].as()) { if (ParseHMSSeverity(hms["code"]) != ""){ printerVariables.hmsstate = true; printerVariables.parsedHMS = ParseHMSSeverity(hms["code"]); + int attrib = hms["attr"]; + printerVariables.parsedHMSattrib = (attrib>>16); + } + } + if(oldHMS != printerVariables.parsedHMS){ + printerVariables.inspectingFirstLayer = false; + if(printerVariables.parsedHMS == "Common" && printerVariables.parsedHMSattrib == 3072) + { + printerVariables.inspectingFirstLayer = true; + } + + + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.print(F("MQTT update - parsedHMS now: ")); + if (printerVariables.parsedHMS.length() == 0) Serial.print(F("NULL")); + Serial.println(printerVariables.parsedHMS); + } + Changed = true; + } + } + + if (messageobject["print"].containsKey("home_flag")){ + //https://github.com/greghesp/ha-bambulab/blob/main/custom_components/bambu_lab/pybambu/const.py#L324 + + bool doorState = false; + long homeFlag = 0; + homeFlag = messageobject["print"]["home_flag"]; + doorState = homeFlag >> 23; //shift left 23 to the Door bit + doorState = doorState & 1; // remove any bits above Door bit + + if (printerVariables.doorOpen != doorState){ + printerVariables.inactivityStartms = millis(); //restart idle timer + + printerVariables.doorOpen = doorState; + printerVariables.inactivityLightsOff = false; + if (printerConfig.debugingchange)Serial.print(F("MQTT Door ")); + if (printerVariables.doorOpen){ + printerVariables.lastdoorOpenms = millis(); + if (printerConfig.debugingchange) Serial.println(F("Opened")); + } + else{ + if ((millis() - printerVariables.lastdoorClosems) < 6000){ + printerVariables.doorSwitchenabled = true; + } + printerVariables.lastdoorClosems = millis(); + if (printerConfig.debugingchange) Serial.println(F("Closed")); } + Changed = true; } - Changed = true; } if (Changed == true){ - Serial.println(F("Updating from mqtt")); + if (printerConfig.debuging){ + Serial.println(F("Change from mqtt")); + } updateleds(); } }else{ @@ -162,7 +289,7 @@ void mqttCallback(char *topic, byte *payload, unsigned int length){ void setupMqtt(){ clientId += String(random(0xffff), HEX); - Serial.println(F("Setting up MQTT with ip: ")); + Serial.print(F("Setting up MQTT with ip: ")); Serial.println(printerConfig.printerIP); device_topic = String("device/") + printerConfig.serialNumber; @@ -179,15 +306,27 @@ void setupMqtt(){ } void mqttloop(){ + if(WiFi.status() != WL_CONNECTED){ + //Abort MQTT connection attempt when no Wifi + return; + } if (!mqttClient.connected()){ printerVariables.online = false; - Serial.println(F("Updating from connect to mqtt")); - updateleds(); + //Only sent the timer from the first instance of a MQTT disconnect + if(printerVariables.disconnectMQTTms == 0) { + printerVariables.disconnectMQTTms = millis(); + //Record last time MQTT dropped connection + Serial.println(F("MQTT dropped during mqttloop")); + ParseMQTTState(mqttClient.state()); + } + delay(500); connectMqtt(); delay(32); return; } - + else{ + printerVariables.disconnectMQTTms = 0; + } mqttClient.loop(); delay(10); } diff --git a/src/mqttparsingutility.h b/src/mqttparsingutility.h index 8a906a4..1040314 100644 --- a/src/mqttparsingutility.h +++ b/src/mqttparsingutility.h @@ -19,5 +19,41 @@ String ParseHMSSeverity(int code){ // Provided by WolfWithSword return ""; } +void ParseMQTTState(int code){ + switch (code) + { + case -4: // MQTT_CONNECTION_TIMEOUT + Serial.println(F("MQTT TIMEOUT")); + break; + case -3: // MQTT_CONNECTION_LOST + Serial.println(F("MQTT CONNECTION_LOST")); + break; + case -2: // MQTT_CONNECT_FAILED + Serial.println(F("MQTT CONNECT_FAILED")); + break; + case -1: // MQTT_DISCONNECTED + Serial.println(F("MQTT DISCONNECTED")); + break; + case 0: // MQTT_CONNECTED + Serial.println(F("MQTT CONNECTED")); + break; + case 1: // MQTT_CONNECT_BAD_PROTOCOL + Serial.println(F("MQTT BAD PROTOCOL")); + break; + case 2: // MQTT_CONNECT_BAD_CLIENT_ID + Serial.println(F("MQTT BAD CLIENT ID")); + break; + case 3: // MQTT_CONNECT_UNAVAILABLE + Serial.println(F("MQTT UNAVAILABLE")); + break; + case 4: // MQTT_CONNECT_BAD_CREDENTIALS + Serial.println(F("MQTT BAD CREDENTIALS")); + break; + case 5: // MQTT UNAUTHORIZED + Serial.println(F("MQTT UNAUTHORIZED")); + break; + } +} + #endif \ No newline at end of file diff --git a/src/serialmanager.h b/src/serialmanager.h index 9de40c7..3d4de29 100644 --- a/src/serialmanager.h +++ b/src/serialmanager.h @@ -14,12 +14,12 @@ void setupSerial(){ void serialLoop(){ if (Serial.available() > 0) { String input = Serial.readStringUntil('\n'); - DynamicJsonDocument doc(256); + JsonDocument doc; deserializeJson(doc, input); if (doc.containsKey("ssid") && doc.containsKey("pass")) { - Serial.print("SSID "); + Serial.print(F("SSID ")); Serial.println(doc["ssid"].as()); - Serial.print("PASS "); + Serial.print(F("PASS ")); Serial.println(doc["pass"].as()); Serial.println(doc["printerip"].as()); @@ -34,6 +34,8 @@ void serialLoop(){ strcpy(printerConfig.serialNumber, doc["printerserial"]); saveFileSystem(); + Serial.println(F("Restarting Device")); + delay(1000); ESP.restart(); } diff --git a/src/types.h b/src/types.h index 6b599d3..6ca5fa0 100644 --- a/src/types.h +++ b/src/types.h @@ -8,13 +8,31 @@ extern "C" typedef struct PrinterVaraiblesStruct{ String parsedHMS = ""; - String gcodeState = "IDLE"; + int parsedHMSattrib = 0; + String gcodeState = "FINISH"; //Initialised to Finish so the logic doesn't + //assume a Print has just finished and needs + //to wait for a door interaction to continue int stage = 0; - bool ledstate = true; + bool testcolorEnabled = false; + bool printerledstate = true; bool hmsstate = false; bool online = false; bool finished = false; - unsigned long finishstartms; + bool initalisedLEDs = false; + bool inspectingFirstLayer = false; + //Time since + unsigned long disconnectMQTTms = 0; + //InactivityLightsOff Timer and status + bool inactivityLightsOff = false; // Are the lights out due to inactivity Timeout? + unsigned long inactivityStartms = 0; // Time the inactivity countdown is measured from + + //Door Monitoring + bool doorOpen = false; // Current State of Door + bool doorSwitchenabled = false; // Has door been closed twice within 6 seconds? + bool waitingForDoor = false; // Are we waiting for the door to be actuated? + unsigned long lastdoorClosems = 0; // Last time door was opened + unsigned long lastdoorOpenms = 0; // Last time door was closed + } PrinterVariables; @@ -23,7 +41,7 @@ extern "C" typedef struct GlobalVariablesStruct{ char SSID[32]; char APPW[32]; - String FWVersion = "EXPERIMENTAL 25.2.24"; + String FWVersion = "EXPERIMENTAL 16.3.24"; String Host = "BLLED"; bool started = false; } GlobalVariables; @@ -32,16 +50,46 @@ extern "C" typedef struct PrinterConfigStruct { - int brightness = 100; - bool replicatestate = true; - bool errordetection = true; - bool finishindication = true; - bool debuging = true; - bool mqttdebug = true; - bool turbo = false; - char printerIP[16]; - char accessCode[9]; - char serialNumber[16]; + char printerIP[16]; //BBLP IP Address - used for MQTT reports + char accessCode[9]; //BBLP Access Code - used for MQTT reports + char serialNumber[16]; //BBLP Serial Number - used for MQTT reports + + char BSSID[18]; //Nominated AP to connect to (Useful if multiple accesspoints with same name) + int brightness = 100; //Brightness of LEDS + bool rescanWiFiNetwork = false; //Scans available WiFi networks for strongest signal + // BLLED Settings + bool replicatestate = true; //LED will be on if the BBPL Light is on + bool errordetection = true; //Utilises Error Colors when BBLP give an error + bool finishindication = true; //Utilises Finish Color when print ends successfully + bool lidarLightsOff = true; //For X1C owners - option to turn off if you have a P1P with no Lidar. + bool inactivityEnabled = true; + int inactivityTimeOut = 1800000;// 1800000 = 30 mins + bool discoMode = false; //Cycles through RGB colors slowly for 'pretty' timelapse movie + bool maintMode = false; //White lights on, even if printer is unpowered + bool updateMaintenance = false; //When updateleds() is run, should the Maintenance LEDS be set? + // Debugging + bool debuging = false; //Debugging for all interactions through functions + bool debugingchange = true; //Default debugging level - to shows onChange + bool mqttdebug = false; //Writes each packet from BBLP to the serial log + bool debugwifi = false; //Changes LED to a color range that represents WiFi signal Strength + // Test - Fixed LED Colors + char testRGB[8]; //Test Color (RGB only) + int testwarmwhite = 100; //Testing just the Warm White LED line + int testcoldwhite = 100; //Testing just the Cold White LED line + bool updateTestLEDS = false; //When updateleds() is run, should the TEST LEDS be set? + // Customise LED Colors + char wifiRGB[8]; //These need to be 8 char: '#' + 123456 + end char '/0' + char finishRGB[8]; + char pauseRGB[8]; + char firstlayerRGB[8]; + char nozzleclogRGB[8]; + char hmsSeriousRGB[8]; + char hmsFatalRGB[8]; + char filamentRunoutRGB[8]; + char frontCoverRGB[8]; + char nozzleTempRGB[8]; + char bedTempRGB[8]; + //char webpagePassword[8]; } PrinterConfig; diff --git a/src/web-server.h b/src/web-server.h index e15e0ab..f249646 100644 --- a/src/web-server.h +++ b/src/web-server.h @@ -13,6 +13,8 @@ WebServer webServer(80); + + #include "www/setupPage.h" bool isAuthorized() { @@ -25,28 +27,75 @@ void handleSetup(){ return; } webServer.sendHeader(F("Content-Encoding"), F("gzip")); - webServer.send_P(200, "text/html", (const char*)setupPage_html_gz, (int)setupPage_html_gz_len); + webServer.send_P(200, "text/html", (const char*)setuppage_html_gz, (int)setuppage_html_gz_len); } void submitSetup(){ + bool newBSSID = false; if (webServer.args() > 0) { - + if( strcmp(printerConfig.BSSID,webServer.arg("apMAC").c_str()) != 0 ){ + newBSSID = true; + } strcpy(printerConfig.printerIP,webServer.arg("ip").c_str()); strcpy(printerConfig.accessCode,webServer.arg("code").c_str()); strcpy(printerConfig.serialNumber,webServer.arg("id").c_str()); - printerConfig.turbo = webServer.arg("turbo") == "on"; + + strcpy(printerConfig.BSSID,webServer.arg("apMAC").c_str()); + printerConfig.brightness = webServer.arg("brightnessslider").toInt(); + printerConfig.rescanWiFiNetwork = webServer.arg("rescanWiFiNetwork") == "on"; + // BLLED Settings printerConfig.replicatestate = webServer.arg("replicateLedState") == "on"; printerConfig.errordetection = webServer.arg("errorDetection") == "on"; printerConfig.finishindication = webServer.arg("finishIndication") == "on"; + printerConfig.lidarLightsOff = webServer.arg("lidarLightsOff") == "on"; + printerConfig.inactivityEnabled = webServer.arg("inactivityEnabled") == "on"; + printerConfig.inactivityTimeOut = (webServer.arg("inactivityMins").toInt() * 60000); + if(printerConfig.maintMode != (webServer.arg("maintMode") == "on")) + { + printerConfig.maintMode = webServer.arg("maintMode") == "on"; + printerConfig.updateMaintenance = printerConfig.maintMode; + if(printerConfig.maintMode && printerConfig.debugingchange){ + Serial.println(F("Maintence Mode is ON")); + Serial.println(F("**No MQTT msgs, TEST colors or Wifi strength visualisation**")); + Serial.println(); + } + } + + printerConfig.discoMode = webServer.arg("discoMode") == "on"; printerConfig.debuging = webServer.arg("debuging") == "on"; + printerConfig.debugingchange = webServer.arg("debugingchange") == "on"; printerConfig.mqttdebug = webServer.arg("mqttdebug") == "on"; - printerConfig.brightness = webServer.arg("brightnessslider").toInt(); + // Test - Fixed LED Colors + printerVariables.testcolorEnabled = !printerConfig.replicatestate; + printerConfig.updateTestLEDS = !printerConfig.replicatestate; + strcpy(printerConfig.testRGB,webServer.arg("testRGB").c_str()); + printerConfig.testcoldwhite = webServer.arg("cw_slider").toInt(); + printerConfig.testwarmwhite = webServer.arg("ww_slider").toInt(); + + printerConfig.debugwifi = webServer.arg("debugwifi") == "on"; + // Customise LED Colors + strcpy(printerConfig.wifiRGB,webServer.arg("wifiRGB").c_str()); + strcpy(printerConfig.finishRGB, webServer.arg("finishRGB").c_str()); + strcpy(printerConfig.pauseRGB, webServer.arg("pauseRGB").c_str()); + strcpy(printerConfig.firstlayerRGB, webServer.arg("firstlayerRGB").c_str()); + strcpy(printerConfig.nozzleclogRGB, webServer.arg("nozzleclogRGB").c_str()); + strcpy(printerConfig.hmsSeriousRGB, webServer.arg("hmsSeriousRGB").c_str()); + strcpy(printerConfig.hmsFatalRGB, webServer.arg("hmsFatalRGB").c_str()); + strcpy(printerConfig.filamentRunoutRGB, webServer.arg("filamentRunoutRGB").c_str()); + strcpy(printerConfig.frontCoverRGB, webServer.arg("frontCoverRGB").c_str()); + strcpy(printerConfig.nozzleTempRGB, webServer.arg("nozzleTempRGB").c_str()); + strcpy(printerConfig.bedTempRGB, webServer.arg("bedTempRGB").c_str()); saveFileSystem(); - Serial.println(F("Updating from webpage")); + Serial.println(F("Updating LEDS from webpage form submit")); updateleds(); handleSetup(); - + if(newBSSID) + { + Serial.println(F("New MAC address (BSSID) assigned. Restarting...")); + delay(1000); + ESP.restart(); + } } } @@ -56,18 +105,45 @@ void handleGetConfig(){ return; } - DynamicJsonDocument doc(300); - doc["brightness"] = printerConfig.brightness; - doc["turbo"] = printerConfig.turbo; + JsonDocument doc; doc["ip"] = printerConfig.printerIP; doc["code"] = printerConfig.accessCode; doc["id"] = printerConfig.serialNumber; + + doc["apMAC"] = printerConfig.BSSID; + doc["brightness"] = printerConfig.brightness; + // BLLED Settings doc["replicateled"] = printerConfig.replicatestate; doc["errordetection"] = printerConfig.errordetection; doc["finishindication"] = printerConfig.finishindication; + doc["lidarLightsOff"] = printerConfig.lidarLightsOff; + doc["inactivityEnabled"] = printerConfig.inactivityEnabled; + doc["inactivityMins"] =(int)( printerConfig.inactivityTimeOut / 60000); + doc["discoMode"] = printerConfig.discoMode; + doc["maintMode"] = printerConfig.maintMode; + // Debugging doc["debuging"] = printerConfig.debuging; + doc["debugingchange"] = printerConfig.debugingchange; doc["mqttdebug"] = printerConfig.mqttdebug; - + // Test - Fixed LED Colors + doc["testRGB"] = printerConfig.testRGB; + doc["cw_slider"] = printerConfig.testcoldwhite; + doc["ww_slider"] = printerConfig.testwarmwhite; + + doc["debugwifi"] = printerConfig.debugwifi; + // Customise LED Colors + doc["wifiRGB"] = printerConfig.wifiRGB; + doc["finishRGB"] = printerConfig.finishRGB; + doc["pauseRGB"] = printerConfig.pauseRGB; + doc["firstlayerRGB"] = printerConfig.firstlayerRGB; + doc["nozzleclogRGB"] = printerConfig.nozzleclogRGB; + doc["hmsSeriousRGB"] = printerConfig.hmsSeriousRGB; + doc["hmsFatalRGB"] = printerConfig.hmsFatalRGB; + doc["filamentRunoutRGB"] = printerConfig.filamentRunoutRGB; + doc["frontCoverRGB"] = printerConfig.frontCoverRGB; + doc["nozzleTempRGB"] = printerConfig.nozzleTempRGB; + doc["bedTempRGB"] = printerConfig.bedTempRGB; + const char* firmwareVersionChar = globalVariables.FWVersion.c_str(); doc["firmwareversion"] = firmwareVersionChar; @@ -94,6 +170,8 @@ void setupWebserver(){ webServer.on("/update", HTTP_POST, []() { webServer.sendHeader("Connection", "close"); webServer.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK"); + Serial.println(F("Restarting Device")); + delay(1000); ESP.restart(); }, []() { HTTPUpload& upload = webServer.upload(); @@ -119,6 +197,7 @@ void setupWebserver(){ webServer.begin(); Serial.println(F("Webserver started")); + Serial.println(); } void webserverloop(){ diff --git a/src/wifi-manager.h b/src/wifi-manager.h index 6550d57..5895198 100644 --- a/src/wifi-manager.h +++ b/src/wifi-manager.h @@ -12,42 +12,78 @@ bool shouldSaveConfig = true; int connectionAttempts = 0; void configModeCallback() { - Serial.println("Entered config mode"); - Serial.print("AP IP address: "); + Serial.println(F("Entered config mode")); + Serial.print(F("AP IP address: ")); Serial.println(WiFi.softAPIP()); } +int str2mac(const char* mac, uint8_t* values){ + if( 6 == sscanf( mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",&values[0], &values[1], &values[2],&values[3], &values[4], &values[5] ) ){ + return 1; + }else{ + return 0; + } +} + bool connectToWifi(){ Serial.println(F("-------------------------------------")); - WiFi.mode(WIFI_STA); + WiFi.mode(WIFI_STA); //ESP32 connects to an access point delay(10); - while (WiFi.status() != WL_CONNECTED) { + + if(strlen(printerConfig.BSSID) == 0){ WiFi.begin(globalVariables.SSID, globalVariables.APPW); - delay(1000); - - wl_status_t status = WiFi.status(); - switch (status) - { - case WL_IDLE_STATUS: - Serial.print(F("Connecting to WIFI.. Attempt #")); - Serial.print(connectionAttempts); - Serial.print(F(" SSID: ")); - Serial.print(globalVariables.SSID); - Serial.print(F(" APPW: ")); - Serial.println(globalVariables.APPW); - break; - case WL_NO_SSID_AVAIL: - Serial.print(F("Bad WiFi credentials")); - return false; - case WL_CONNECT_FAILED: - case WL_DISCONNECTED: - Serial.print(F("Check your WiFi credentials.")); - return false; - default: - break; + } + else{ + // Reference https://stackoverflow.com/questions/70415075/esp32-select-one-from-multiple-wifi-ap-with-same-name-ssid + uint8_t bssid[6] = {0}; + + //Function to convert String MAC address to 6 byte array + if(str2mac(printerConfig.BSSID,bssid)){ + WiFi.begin(globalVariables.SSID, globalVariables.APPW, 0, bssid); + } + else{ + if (printerConfig.debuging || printerConfig.debugingchange){ + Serial.print(F("Parsing MAC Address Failed, reverting to ")); + Serial.println(globalVariables.SSID); + } + WiFi.begin(globalVariables.SSID, globalVariables.APPW); } + } + + wl_status_t status = WiFi.status(); + + while (status != WL_CONNECTED) { - delay(10000); // Giving enough time to connect + if(status != WiFi.status()){ + status = WiFi.status(); + switch (status) + { + case WL_IDLE_STATUS: + Serial.print(F("Connecting to WIFI.. Attempt #")); + Serial.println(connectionAttempts); + Serial.print(F(" SSID: ")); + Serial.print(globalVariables.SSID); + Serial.print(F(" APPW: ")); + Serial.print(globalVariables.APPW); + if(strlen(printerConfig.BSSID) > 0){ + Serial.print(F(" BSSID: ")); + Serial.print(globalVariables.SSID); + } + Serial.println(); + break; + case WL_NO_SSID_AVAIL: + Serial.print(F("Bad WiFi credentials")); + return false; + case WL_CONNECT_FAILED: + Serial.print(F("Connect Failed.")); + break; + case WL_DISCONNECTED: + Serial.print(F("Disconnected. (Check low RSSI)")); + default: + break; + } + } + delay(1000); // Giving time to connect connectionAttempts++; } @@ -59,16 +95,81 @@ bool connectToWifi(){ WiFi.setTxPower(WIFI_POWER_19_5dBm); // https://github.com/G6EJD/ESP32-8266-Adjust-WiFi-RF-Power-Output/blob/main/README.md #endif - delay(1000); - int signalStrength = WiFi.RSSI(); - Serial.println(F("Connected To Wifi With Signal Strength: ")); - Serial.println(signalStrength); - Serial.println(); - delay(10); - Serial.print("IP_ADDRESS:"); // Unique identifier for the IP address + Serial.print(F("Connected To Wifi Access Point: ")); + Serial.println(globalVariables.SSID); + Serial.print(F("Specific BSSID: ")); //MAC address of connected AP + Serial.println(WiFi.BSSIDstr()); + Serial.print(F("RSSI (Signal Strength): ")); + Serial.println(WiFi.RSSI()); + Serial.print(F("IP_ADDRESS: ")); // Unique identifier for the IP address Serial.println(WiFi.localIP()); - + Serial.println(); return true; }; +void scanNetwork() +{ + //Reference / Credit - https://www.esp32.com/viewtopic.php?t=18979#p70299 + int bestRSSI = -200; + String bestBSSID = ""; + + Serial.println(F("Wifi network scan start")); + int n = WiFi.scanNetworks(); + Serial.println(F("Network scan complete")); + + if (n == 0) { + Serial.println(F("No wifi networks found :(")); + } else { + Serial.print(n); // Count + Serial.println(F(" Wifi networks found")); + Serial.println(F("-------------------------------------------------------------------------")); + for (int i = 0; i < n; ++i) { // Print SSID and RSSI for each network found + Serial.print(i + 1); + Serial.print(F(": ")); + Serial.print(WiFi.SSID(i)); + Serial.print(F(" (")); + Serial.print(WiFi.RSSI(i)); + Serial.print(F("dBm, ")); + if (WiFi.RSSI(i) < -50) Serial.print(F(" ")); //Spacing... because I just can't look at the results when misaligned. + Serial.print(constrain(2 * (WiFi.RSSI(i) + 100), 0, 100)); + Serial.print(F("%) ")); + Serial.print(F("BSSID: ")); + Serial.print(WiFi.BSSIDstr(i)); + Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? " open" : " encrypted"); + + if(WiFi.SSID(i) == globalVariables.SSID && WiFi.RSSI(i) > bestRSSI) + { + bestBSSID = WiFi.BSSIDstr(i); + bestRSSI = WiFi.RSSI(i); + } + delay(10); + } + Serial.println(); + if(printerConfig.BSSID == bestBSSID.c_str()){ + Serial.print(F("BSSID already set to: ")); + Serial.print(printerConfig.BSSID); + } + else{ + if(strlen(printerConfig.BSSID) == 0 || printerConfig.rescanWiFiNetwork){ + strcpy(printerConfig.BSSID,bestBSSID.c_str()); + Serial.print(F("Saving strongest AP for: ")); + Serial.println(globalVariables.SSID); + Serial.print(F("Strongest BSSID (MAC Address): ")); + Serial.print(printerConfig.BSSID); + } + else{ + Serial.print(F("Ignoring strongest AP for: ")); + Serial.println(globalVariables.SSID); + Serial.print(F("Using alrady saved BSSID (MAC Address): ")); + Serial.print(printerConfig.BSSID); + } + Serial.print(F(" RSSI (Signal Strength): ")); + Serial.print(bestRSSI); + Serial.println(F("dBm")); + } + } + Serial.println(); +} + + #endif diff --git a/src/www/Icon square.png b/src/www/Icon-square.png old mode 100755 new mode 100644 similarity index 100% rename from src/www/Icon square.png rename to src/www/Icon-square.png diff --git a/src/www/setupPage.h b/src/www/setupPage.h index c66260f..8b9381b 100644 --- a/src/www/setupPage.h +++ b/src/www/setupPage.h @@ -1,156 +1,278 @@ -//File: setuppage.html.gz, Size: 2403 //https://mischianti.org/online-converter-file-to-cpp-gzip-byte-array-3/ -#define setupPage_html_gz_len 2409 -const uint8_t setupPage_html_gz[] PROGMEM = { -0x1F, 0x8B, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xED, 0x5A, 0xEB, 0x73, 0xDB, 0x36, -0x12, 0xFF, 0x9E, 0xBF, 0x02, 0x51, 0x27, 0xA1, 0x3C, 0x0E, 0x45, 0xC9, 0x8A, 0xDD, 0x54, 0x91, -0x34, 0x93, 0xF8, 0x91, 0xDA, 0x75, 0x1A, 0xD7, 0x76, 0x32, 0xB9, 0x74, 0xFA, 0x01, 0x22, 0x41, -0x11, 0x36, 0x49, 0x30, 0x00, 0x68, 0x49, 0xB9, 0xE6, 0x7F, 0xBF, 0x05, 0x40, 0xBD, 0x40, 0x52, -0x7E, 0x34, 0x97, 0x9B, 0xC9, 0x9C, 0x9D, 0x58, 0x24, 0xB0, 0xDC, 0xC7, 0x6F, 0x17, 0xBB, 0x0B, -0x50, 0xFD, 0xC7, 0x07, 0xEF, 0xF6, 0x2F, 0xFF, 0x75, 0x76, 0x88, 0x22, 0x99, 0xC4, 0xC3, 0x47, -0x7D, 0xF3, 0x01, 0x9F, 0x04, 0x07, 0xC3, 0x47, 0x08, 0x7E, 0xFA, 0x31, 0x4D, 0xAF, 0x11, 0x27, -0xF1, 0xC0, 0x11, 0x72, 0x16, 0x13, 0x11, 0x11, 0x22, 0x1D, 0x14, 0x71, 0x12, 0x0E, 0x9C, 0x48, -0xCA, 0x4C, 0xF4, 0x3C, 0xCF, 0x0F, 0xD2, 0x2B, 0xD1, 0xF2, 0x63, 0x96, 0x07, 0x61, 0x8C, 0x39, -0x69, 0xF9, 0x2C, 0xF1, 0xF0, 0x15, 0x9E, 0x7A, 0x31, 0x1D, 0x09, 0x2F, 0x64, 0xA9, 0x74, 0xF1, -0x84, 0x08, 0x96, 0x10, 0x6F, 0xB7, 0xD5, 0xD9, 0x6D, 0x75, 0x3D, 0x5F, 0x08, 0x0F, 0xC7, 0x71, -0x2B, 0xA1, 0x69, 0x0B, 0xAE, 0x1D, 0x44, 0x53, 0x49, 0xC6, 0x9C, 0xCA, 0x19, 0x08, 0x8A, 0xF0, -0x6E, 0x67, 0xC7, 0xA5, 0xD3, 0xF8, 0xDD, 0xA7, 0x93, 0x69, 0xFC, 0x02, 0x5F, 0x75, 0xF6, 0xBF, -0xB4, 0x93, 0xDF, 0xAE, 0x66, 0x47, 0x97, 0x27, 0x7F, 0x4C, 0x3D, 0xB1, 0xFD, 0x7E, 0x6F, 0xC2, -0xDA, 0x6C, 0xEF, 0x6C, 0x7B, 0xFF, 0xD3, 0xD9, 0xF9, 0xC9, 0xC7, 0xED, 0xF1, 0x17, 0xD2, 0x3D, -0x89, 0x5E, 0x1C, 0x71, 0xE6, 0x25, 0x97, 0xB3, 0x53, 0xBE, 0xCB, 0xBD, 0x0F, 0xD3, 0xED, 0xFC, -0xC3, 0xCF, 0x27, 0x2F, 0xCE, 0x6F, 0xCE, 0xC3, 0x4F, 0xD9, 0xEE, 0xF6, 0xC7, 0x5F, 0x42, 0x7A, -0xF3, 0xE6, 0xE7, 0x57, 0x83, 0x81, 0x83, 0x7C, 0xCE, 0x84, 0x60, 0x9C, 0x8E, 0x69, 0x3A, 0x70, -0x70, 0xCA, 0xD2, 0x59, 0xC2, 0x72, 0xD0, 0x01, 0x8C, 0x22, 0x9C, 0x13, 0x9E, 0xB1, 0x98, 0xFA, -0xA0, 0x48, 0xCA, 0xDC, 0xF9, 0x90, 0x83, 0xBC, 0x02, 0x10, 0x0D, 0x83, 0xB9, 0x56, 0x3F, 0x23, -0x16, 0xCC, 0xD0, 0xBF, 0x17, 0xB7, 0x7A, 0x08, 0xFB, 0xD7, 0x63, 0xCE, 0xF2, 0x34, 0x70, 0x7D, -0x16, 0x33, 0xDE, 0x43, 0x3F, 0x85, 0x1D, 0xF5, 0xFB, 0x72, 0x8D, 0x4C, 0xC3, 0x12, 0xE2, 0x84, -0xC6, 0xB3, 0x1E, 0x7A, 0xC5, 0x29, 0x8E, 0x9F, 0x21, 0x81, 0x53, 0xE1, 0x0A, 0xC2, 0x69, 0xB8, -0xA4, 0xFD, 0xBA, 0xB8, 0x02, 0x5C, 0x53, 0x89, 0x69, 0x4A, 0xB8, 0x25, 0x31, 0xA0, 0x22, 0x8B, -0x31, 0xB0, 0x09, 0x63, 0x32, 0xB5, 0xA4, 0xC0, 0x88, 0x1B, 0x50, 0x4E, 0x7C, 0x49, 0x59, 0xDA, -0x43, 0xA0, 0x51, 0x9E, 0xA4, 0xEB, 0x34, 0x57, 0xB9, 0x90, 0x34, 0x9C, 0xB9, 0x8A, 0x3D, 0x49, -0x25, 0x10, 0xC1, 0x5F, 0xC2, 0xD7, 0x89, 0x70, 0x4C, 0xC7, 0xA9, 0x4B, 0x25, 0x49, 0x44, 0x35, -0x41, 0x44, 0xE8, 0x38, 0x82, 0x87, 0x3B, 0xED, 0xF6, 0x4D, 0x54, 0xA9, 0x7E, 0xCC, 0xC6, 0xCC, -0x7D, 0x80, 0x0D, 0x1B, 0x45, 0x5B, 0xFC, 0x2D, 0xAE, 0x13, 0x1A, 0xC8, 0x48, 0xEB, 0x94, 0x4D, -0x6B, 0xD5, 0xB5, 0xA7, 0x12, 0xCC, 0x21, 0x34, 0x5C, 0x6E, 0x08, 0x76, 0xD6, 0xE6, 0x6D, 0x73, -0x24, 0x99, 0x4A, 0x4B, 0xA6, 0x76, 0xAC, 0xA0, 0x5F, 0x48, 0x0F, 0x75, 0xF7, 0x6C, 0xDE, 0x7A, -0x72, 0x52, 0xC8, 0x1E, 0xB1, 0x38, 0xA8, 0x64, 0x1D, 0x32, 0x9E, 0xD4, 0x22, 0x55, 0x8E, 0xAF, -0x49, 0x04, 0xE0, 0xAC, 0xCB, 0xC9, 0x70, 0x10, 0xD0, 0x74, 0x6C, 0xAB, 0xAF, 0x9F, 0x67, 0x3C, -0x20, 0xDC, 0xE5, 0x38, 0xA0, 0xB9, 0x50, 0x00, 0x94, 0x09, 0xA6, 0x2E, 0x2C, 0xC2, 0x80, 0x4D, -0x7A, 0xA8, 0x0D, 0xBF, 0x8A, 0x02, 0xF1, 0xF1, 0x08, 0x37, 0xDB, 0xCF, 0x50, 0xF1, 0xAF, 0xD5, -0xDD, 0x7A, 0x59, 0x85, 0xF4, 0x5E, 0xFB, 0x49, 0x25, 0x98, 0x92, 0x65, 0xF5, 0x50, 0xC6, 0x78, -0x44, 0xE2, 0xBA, 0x78, 0x18, 0xC5, 0xCC, 0xBF, 0xAE, 0x94, 0x05, 0xAE, 0xAB, 0x16, 0x36, 0x62, -0x52, 0xB2, 0xA4, 0x87, 0x76, 0xEF, 0x81, 0xFE, 0x62, 0xDA, 0x78, 0xAE, 0x53, 0xF2, 0xDC, 0x7C, -0x2D, 0x77, 0xBB, 0xDD, 0x2A, 0x1B, 0x68, 0x9A, 0xE5, 0xF2, 0x4F, 0x39, 0xCB, 0xC8, 0xC0, 0x51, -0x41, 0xE1, 0xFC, 0x55, 0x1B, 0x8A, 0x4F, 0x6A, 0x5C, 0x55, 0xE5, 0x89, 0x35, 0x57, 0x95, 0xEC, -0x31, 0xF3, 0xF0, 0x24, 0x38, 0x48, 0x40, 0xD6, 0x0A, 0xD0, 0x4F, 0xBE, 0xEF, 0x6F, 0xC4, 0xA4, -0x1C, 0x0F, 0xB5, 0x56, 0x57, 0x1B, 0x27, 0xF2, 0x51, 0x42, 0xCB, 0xE6, 0x55, 0x64, 0xBD, 0xE7, -0xFB, 0xAF, 0x8E, 0x76, 0xDB, 0x95, 0x28, 0x6E, 0x8A, 0xD8, 0x87, 0xC3, 0x00, 0xA9, 0xDC, 0x62, -0xEA, 0xE7, 0x5C, 0x28, 0x79, 0x19, 0xA3, 0xE5, 0x8C, 0x55, 0x1F, 0x9A, 0x1B, 0x61, 0x59, 0x4C, -0xDE, 0xB6, 0x8C, 0x47, 0x39, 0x40, 0x9E, 0xFE, 0x1F, 0xA7, 0x5B, 0xD3, 0x9D, 0x64, 0xE3, 0x71, -0x4C, 0xBE, 0x79, 0x69, 0x58, 0xD1, 0x7D, 0x1E, 0xFD, 0x9D, 0xBA, 0x64, 0x5E, 0xA8, 0x50, 0x95, -0x89, 0xD6, 0xCB, 0xC1, 0x6D, 0x1C, 0xC4, 0x84, 0x4A, 0x3F, 0xB2, 0x58, 0x64, 0x4C, 0x50, 0x53, -0x7F, 0xA1, 0x95, 0xC2, 0x92, 0xDE, 0x58, 0xF0, 0x2F, 0x2C, 0xA4, 0x29, 0x34, 0x5C, 0xC4, 0xAD, -0xCF, 0x79, 0x7B, 0xB5, 0x85, 0xAC, 0xFB, 0xFC, 0x4E, 0x8A, 0xE9, 0xA5, 0x6C, 0xA9, 0xC7, 0x32, -0xEC, 0x43, 0xEB, 0x05, 0xD9, 0xBE, 0x52, 0x66, 0xBB, 0x5A, 0x60, 0xBB, 0x52, 0x9A, 0x80, 0x2C, -0x54, 0xF2, 0xE0, 0xD2, 0x7E, 0x3C, 0x82, 0x3C, 0x95, 0xDB, 0x31, 0xBD, 0x31, 0xFC, 0x74, 0xDC, -0x59, 0x3A, 0xC4, 0x24, 0x94, 0xA5, 0x41, 0x5E, 0xD2, 0x4B, 0xFD, 0xCC, 0x5D, 0x6F, 0x0F, 0x97, -0xD7, 0x61, 0x29, 0x75, 0x42, 0xE4, 0x8E, 0xAE, 0xA9, 0x74, 0x25, 0x87, 0xBE, 0xAC, 0x30, 0xA0, -0xF5, 0x5C, 0x58, 0xEA, 0x6D, 0x9A, 0xB4, 0x56, 0x64, 0xAD, 0x8F, 0x0C, 0x6A, 0xBD, 0x11, 0x81, -0xCA, 0x4F, 0xEE, 0x0B, 0xDE, 0xBC, 0x6D, 0x73, 0x9C, 0x6A, 0x47, 0xED, 0x94, 0x96, 0x65, 0xE1, -0xD8, 0xF2, 0x84, 0xC1, 0xF5, 0x79, 0x39, 0x71, 0x18, 0x0C, 0xCB, 0x13, 0x77, 0xE9, 0x45, 0xBE, -0x39, 0x8C, 0xBB, 0xAB, 0x25, 0xD4, 0xAA, 0x52, 0x3D, 0x3F, 0x22, 0xFE, 0x35, 0x09, 0xD0, 0x76, -0x4D, 0x2C, 0x56, 0x38, 0x7E, 0xA7, 0xF3, 0xCB, 0xDE, 0x51, 0x7D, 0x55, 0xEF, 0x85, 0xCC, 0xCF, -0x45, 0x3D, 0x43, 0xBB, 0x5D, 0x82, 0x62, 0x7C, 0x2B, 0xCB, 0x92, 0x96, 0xD5, 0xBE, 0x5F, 0x83, -0x4E, 0x75, 0x85, 0x3D, 0x03, 0x14, 0x24, 0x11, 0xF2, 0xB1, 0xA9, 0xFC, 0x67, 0xB5, 0x61, 0x6E, -0x22, 0xEE, 0x4E, 0x7C, 0x27, 0xC2, 0xEA, 0x2E, 0x00, 0xC8, 0xC7, 0xE4, 0xEE, 0x3D, 0xCE, 0xC6, -0x26, 0xC4, 0x48, 0xE8, 0x7B, 0x2B, 0x5B, 0xAB, 0xBE, 0xF0, 0x39, 0xCD, 0xE4, 0x72, 0x9F, 0x15, -0xE6, 0xA9, 0xDE, 0xC1, 0xA0, 0x3C, 0x0B, 0x40, 0xCB, 0x7D, 0x96, 0x86, 0x74, 0xDC, 0xDC, 0xB2, -0xE4, 0xDF, 0x60, 0x8E, 0xA6, 0x11, 0x47, 0x03, 0x94, 0x92, 0x09, 0xFA, 0xF8, 0xF6, 0xF4, 0x57, -0xD8, 0xA2, 0x9E, 0x93, 0xCF, 0x39, 0x11, 0xB2, 0x69, 0x19, 0x0F, 0x74, 0x2D, 0x96, 0x72, 0xD8, -0xEA, 0xCE, 0x84, 0x04, 0x96, 0x7E, 0xA4, 0x4C, 0x82, 0x47, 0xE7, 0xA2, 0x4A, 0xDC, 0x35, 0x06, -0x21, 0x6A, 0xAA, 0x27, 0xF5, 0x73, 0x17, 0xEA, 0x39, 0x34, 0x18, 0x0C, 0xD0, 0x73, 0xF4, 0xF4, -0xA9, 0xE6, 0xA8, 0x58, 0x41, 0xAC, 0xA8, 0xB1, 0x9D, 0x76, 0xBB, 0x8A, 0xC3, 0x5C, 0x4F, 0x5F, -0x9B, 0x70, 0x80, 0x25, 0x06, 0x99, 0x27, 0x17, 0xEF, 0x7E, 0x6F, 0x65, 0x98, 0x0B, 0x52, 0x70, -0x17, 0x19, 0x4B, 0x05, 0xB9, 0x84, 0x46, 0xD2, 0x52, 0x7B, 0xFE, 0x13, 0x40, 0x4C, 0x26, 0xB0, -0xEC, 0x5B, 0x63, 0x22, 0x0F, 0x63, 0xA2, 0x2E, 0x5F, 0xCF, 0x8E, 0x83, 0xA6, 0x43, 0x33, 0x67, -0xAB, 0x75, 0x83, 0xE3, 0x5C, 0xD9, 0xB2, 0x14, 0xD2, 0xA2, 0x19, 0xFA, 0xFB, 0xEF, 0x52, 0x8E, -0xB8, 0x95, 0x9D, 0xCF, 0x02, 0x52, 0xCD, 0x50, 0xCD, 0x3C, 0x88, 0x25, 0x0D, 0x6A, 0x34, 0x0C, -0x1E, 0xC4, 0x8E, 0x93, 0x0C, 0xF6, 0xEB, 0xE0, 0x89, 0x53, 0x12, 0x68, 0x8F, 0x00, 0xF7, 0xF9, -0xF2, 0x5A, 0xE3, 0xBF, 0x20, 0x8C, 0x89, 0x96, 0x14, 0xE2, 0x58, 0x90, 0x7B, 0x0A, 0x23, 0x9C, -0x33, 0x7E, 0x40, 0xA4, 0xD9, 0x4F, 0xD7, 0x49, 0xD2, 0x54, 0xC1, 0x9C, 0xEA, 0xA1, 0xB2, 0x42, -0x9A, 0x52, 0x11, 0x1D, 0xA7, 0x81, 0x52, 0x7A, 0x83, 0x34, 0x43, 0x47, 0x17, 0x74, 0x0F, 0x95, -0x17, 0x90, 0x51, 0x0E, 0xAB, 0x74, 0x5C, 0x27, 0x67, 0x3E, 0xFF, 0x50, 0xFE, 0xC9, 0x67, 0x29, -0x35, 0x8F, 0x3A, 0x01, 0x0B, 0x82, 0x87, 0x4A, 0x90, 0x39, 0x1F, 0xB1, 0x3A, 0xEE, 0x7A, 0xF2, -0xA1, 0x9C, 0x47, 0xBA, 0xB9, 0x48, 0x89, 0x10, 0x26, 0x5D, 0x57, 0x47, 0xF0, 0x92, 0x4A, 0xC9, -0x81, 0x34, 0x78, 0x3F, 0x29, 0x0D, 0x5B, 0xCA, 0x81, 0xE9, 0x0B, 0x1B, 0x5B, 0x2D, 0xB5, 0xA5, -0xDC, 0x37, 0x85, 0x1E, 0x44, 0x36, 0x5E, 0x2F, 0x08, 0x7B, 0xA8, 0xB1, 0x8D, 0x9A, 0x9B, 0x74, -0xD8, 0x82, 0x22, 0xD3, 0x78, 0xD2, 0xB8, 0xA7, 0x2A, 0x21, 0xE5, 0xC9, 0x04, 0x73, 0x72, 0x43, -0xB8, 0x80, 0x98, 0x2A, 0xAB, 0x70, 0x54, 0x10, 0xA0, 0x0F, 0x86, 0xA2, 0xD7, 0xD8, 0x6E, 0xAE, -0x05, 0xE5, 0xDA, 0xF3, 0x48, 0x29, 0xD3, 0x78, 0x9F, 0x5E, 0xA7, 0x6C, 0x92, 0xA2, 0x05, 0xD3, -0xB2, 0x52, 0x5F, 0xD7, 0x46, 0xBE, 0x56, 0xE4, 0xEC, 0x8C, 0xA4, 0x4D, 0xE7, 0xCD, 0xE1, 0xA5, -0xF3, 0x0C, 0x39, 0x1E, 0xA8, 0x6D, 0x6A, 0x01, 0xDC, 0x49, 0x9E, 0x93, 0x8A, 0x24, 0x2F, 0x48, -0x1A, 0x34, 0x2B, 0x0B, 0xDA, 0x04, 0xD6, 0x0C, 0x9B, 0x40, 0x15, 0x88, 0x19, 0x0E, 0x36, 0x65, -0xFE, 0x3B, 0x3B, 0x0C, 0x60, 0x82, 0xED, 0xD9, 0xE1, 0x0D, 0x10, 0x9C, 0x52, 0x01, 0x50, 0x11, -0xDE, 0x6C, 0xE8, 0xBA, 0xD9, 0x78, 0xB6, 0x64, 0x4F, 0xAA, 0xEA, 0xC2, 0x37, 0x0F, 0x0A, 0xD2, -0x92, 0x50, 0x75, 0x89, 0x2C, 0x02, 0xB5, 0x22, 0x08, 0xBE, 0x5A, 0x68, 0xAD, 0xD7, 0xD6, 0x15, -0xC4, 0x96, 0x97, 0xE5, 0x82, 0x6C, 0xCE, 0x03, 0x8E, 0xA0, 0x8B, 0x28, 0xC1, 0x46, 0x14, 0x0E, -0xAD, 0x8C, 0xEB, 0xCF, 0x03, 0x12, 0xE2, 0x3C, 0xD6, 0x65, 0xF8, 0x9F, 0xD5, 0x6C, 0xED, 0xFF, -0xB3, 0x77, 0x17, 0x26, 0x00, 0x8C, 0xF8, 0x0B, 0x22, 0xF3, 0x6C, 0x53, 0x08, 0xC8, 0x82, 0xE3, -0xAF, 0x50, 0xB7, 0xC1, 0x27, 0x4E, 0x01, 0x9B, 0x7B, 0x09, 0xDD, 0x8C, 0xE2, 0x83, 0x33, 0x53, -0x1B, 0xC0, 0x22, 0x6F, 0xEA, 0x4E, 0x26, 0x13, 0x57, 0x9F, 0xC1, 0xE5, 0x3C, 0x26, 0xA9, 0x2A, -0x74, 0x50, 0xB2, 0x2C, 0xBD, 0xBF, 0x61, 0xFF, 0x50, 0xD1, 0x3E, 0x2C, 0xBB, 0x87, 0xCA, 0x65, -0x8B, 0x63, 0xC2, 0x65, 0xD3, 0x01, 0xB3, 0x25, 0x24, 0x64, 0x81, 0x04, 0xBE, 0x81, 0x74, 0x27, -0x72, 0xDF, 0x07, 0xEF, 0x87, 0x79, 0x1C, 0xCF, 0x1E, 0x3B, 0x77, 0x59, 0x5C, 0x25, 0x4F, 0x28, -0xAB, 0x8B, 0x9E, 0x44, 0xB9, 0xE3, 0xA8, 0xB8, 0x6D, 0x2E, 0xA2, 0x13, 0x50, 0xE4, 0xB3, 0x0B, -0x28, 0xA1, 0xBE, 0x64, 0x80, 0xA3, 0xA2, 0x77, 0xB6, 0x2C, 0x51, 0x8A, 0x11, 0x34, 0x33, 0x38, -0x81, 0x0A, 0xC8, 0xC1, 0x1A, 0xF4, 0xE7, 0x5F, 0xF6, 0xB1, 0x80, 0x61, 0xAB, 0x0E, 0x3A, 0x0F, -0xB1, 0x1F, 0x35, 0x17, 0x98, 0xE9, 0x58, 0x7D, 0x86, 0xAE, 0xC9, 0xAC, 0x0A, 0xBD, 0x25, 0xD3, -0x56, 0x96, 0x8B, 0xA8, 0x69, 0x7C, 0xF3, 0xFE, 0xFC, 0x78, 0x9F, 0x25, 0xD0, 0x30, 0x81, 0x7E, -0x4D, 0xFD, 0xE4, 0x36, 0x72, 0x06, 0x0E, 0xFC, 0xAD, 0x98, 0xD7, 0x02, 0x6C, 0x85, 0xED, 0x85, -0xA0, 0x0C, 0xD0, 0x27, 0xFB, 0x83, 0x55, 0x91, 0x57, 0xB0, 0x3B, 0x6D, 0x3A, 0x4F, 0x2B, 0x23, -0x41, 0x27, 0x19, 0xF5, 0x88, 0x3D, 0xC9, 0x21, 0x32, 0x79, 0x6A, 0xD7, 0x9C, 0x45, 0xCB, 0x5B, -0xB4, 0xB9, 0x7D, 0xCF, 0xBC, 0x73, 0x79, 0xD4, 0x57, 0x3C, 0x8A, 0x1E, 0x38, 0xA0, 0x37, 0xC8, -0x8F, 0xB1, 0x10, 0x03, 0x67, 0x71, 0x34, 0xE2, 0x2C, 0x5B, 0xE2, 0xD5, 0xF9, 0xF5, 0xA3, 0xF5, -0x15, 0x22, 0x4D, 0x48, 0x93, 0xF1, 0x2A, 0xA1, 0x83, 0x04, 0xF7, 0x97, 0xAF, 0x6F, 0x82, 0x5C, -0xFA, 0x51, 0x00, 0x6B, 0x34, 0x66, 0x99, 0x7E, 0x75, 0x33, 0xC9, 0xE6, 0xAF, 0x02, 0xBC, 0x3C, -0x53, 0xA9, 0x51, 0x78, 0x3B, 0xED, 0x9D, 0xAE, 0xD7, 0x7E, 0xE1, 0x1D, 0xC3, 0x84, 0x2B, 0x3E, -0xE7, 0xEA, 0x2D, 0x4F, 0x06, 0x9D, 0x02, 0x04, 0xA2, 0x1C, 0x38, 0xA7, 0x8A, 0xA9, 0x25, 0x53, -0x64, 0x38, 0x5D, 0xD3, 0x4E, 0x1F, 0x8A, 0x0E, 0x5F, 0x9F, 0x9E, 0x1E, 0x1E, 0xA8, 0xC5, 0xC7, -0x59, 0x0C, 0x31, 0x0C, 0x08, 0x00, 0xDD, 0x8A, 0x4D, 0x1E, 0x18, 0x55, 0x6D, 0xE2, 0xFA, 0x99, -0xB8, 0x2D, 0x2E, 0xEA, 0x00, 0x6B, 0x04, 0xBC, 0xD1, 0x92, 0x39, 0x5A, 0x2C, 0x8F, 0x33, 0x3C, -0x26, 0x00, 0x71, 0xC7, 0x7E, 0xA8, 0x8B, 0x68, 0x30, 0x28, 0x95, 0xBA, 0xE1, 0xA2, 0xB4, 0x15, -0x23, 0x3D, 0x74, 0x38, 0xCD, 0x28, 0xA7, 0x6A, 0x01, 0xE0, 0x18, 0x75, 0x76, 0x5A, 0xDD, 0x76, -0x6B, 0xA7, 0x0B, 0x1C, 0xBB, 0x16, 0xC7, 0x6C, 0x58, 0x8A, 0xD9, 0xCB, 0x88, 0x0A, 0x88, 0x22, -0x48, 0x0D, 0x38, 0x8E, 0xD9, 0x44, 0xA0, 0x19, 0xCB, 0x91, 0x64, 0xA8, 0xC8, 0x17, 0x32, 0x22, -0xE8, 0xED, 0x1F, 0x97, 0x97, 0x68, 0xC4, 0xD9, 0x35, 0xE8, 0x4C, 0xD3, 0x90, 0x21, 0x9C, 0x06, -0xF0, 0x7F, 0x86, 0xD4, 0x51, 0x9F, 0x5A, 0x16, 0x20, 0x54, 0x14, 0xB6, 0xB4, 0xD6, 0x05, 0x7A, -0x96, 0xC4, 0xBE, 0x82, 0x09, 0x41, 0xC0, 0x46, 0x2C, 0x18, 0x98, 0x2C, 0x89, 0xB0, 0x5E, 0x5A, -0x83, 0xF5, 0x5C, 0x89, 0x60, 0x7F, 0xA1, 0x6F, 0x61, 0x2F, 0x67, 0x42, 0x74, 0x35, 0x91, 0xBF, -0x74, 0xCA, 0x86, 0xF4, 0xF5, 0xE1, 0xD8, 0xF0, 0xF8, 0xAC, 0xD7, 0xF7, 0xCC, 0x65, 0x99, 0xC4, -0x9C, 0x2E, 0xAD, 0x9C, 0x82, 0x2B, 0x80, 0xD5, 0x96, 0x04, 0xA5, 0xB0, 0x8A, 0xCC, 0x95, 0xA4, -0x32, 0x86, 0xCB, 0x43, 0x75, 0xD2, 0xA3, 0xCD, 0x3F, 0x3E, 0x53, 0x86, 0x72, 0xD5, 0xB1, 0xB0, -0x50, 0xC1, 0xC3, 0x57, 0x11, 0x71, 0x90, 0x5E, 0xB2, 0x03, 0xC7, 0x19, 0xF6, 0x47, 0xBC, 0x56, -0xAD, 0x57, 0x3A, 0xF5, 0x81, 0xF7, 0x03, 0x72, 0x6F, 0xFD, 0xF4, 0x1E, 0xA7, 0xD0, 0xD0, 0x5C, -0x97, 0x74, 0xC4, 0x86, 0xBD, 0xDE, 0xF3, 0x00, 0xC4, 0x0F, 0xD5, 0xF2, 0x82, 0xA8, 0x17, 0x7D, -0xE8, 0xF8, 0xE0, 0xFE, 0x18, 0x06, 0x0B, 0x0C, 0x83, 0x0A, 0xFD, 0xC4, 0x9C, 0xF1, 0x52, 0x3B, -0x58, 0xD2, 0xD4, 0x27, 0xB7, 0x2A, 0xB6, 0xB2, 0xC4, 0xEC, 0x73, 0xD8, 0x8A, 0x20, 0xD0, 0x8F, -0xAC, 0xAE, 0xED, 0xD5, 0x83, 0x53, 0x67, 0x78, 0x3E, 0xDF, 0x62, 0xE9, 0xA5, 0xA8, 0xEB, 0x5B, -0xCF, 0x5E, 0xE3, 0x65, 0x58, 0x2C, 0x5E, 0xE6, 0xA4, 0xB2, 0x46, 0x78, 0x09, 0x22, 0xDD, 0xEB, -0x8F, 0xD8, 0xD4, 0xC0, 0x54, 0xDE, 0x0C, 0x16, 0xA8, 0x95, 0x27, 0x36, 0xF0, 0x5F, 0x35, 0xB0, -0xE8, 0xF7, 0x87, 0x1B, 0xCD, 0xA8, 0x75, 0xE6, 0x7A, 0x3E, 0xFB, 0xEF, 0x80, 0x7E, 0xA8, 0x76, -0x9B, 0x68, 0xB1, 0x29, 0xFD, 0xBE, 0x80, 0x5B, 0x1B, 0xE2, 0x02, 0x6D, 0x6B, 0xF4, 0x87, 0x81, -0xFA, 0x48, 0x6F, 0xB5, 0xD1, 0x72, 0x4F, 0xFE, 0x7D, 0xC1, 0x2E, 0x9D, 0x08, 0x14, 0x70, 0x97, -0xC6, 0x7F, 0x18, 0xC0, 0xCF, 0xDF, 0xBC, 0x46, 0x6F, 0x4D, 0x4E, 0xFF, 0x8E, 0x38, 0x9B, 0x73, -0x84, 0x02, 0x5C, 0x73, 0xF3, 0xC3, 0x20, 0x7A, 0x50, 0x9C, 0xE2, 0x7C, 0x5F, 0x44, 0x17, 0x67, -0x4B, 0x05, 0xA8, 0x8B, 0xFB, 0x1F, 0x06, 0x57, 0xDD, 0x0C, 0x40, 0x27, 0xFC, 0xFD, 0xB1, 0x5D, -0x9E, 0xAB, 0x15, 0xE0, 0x2E, 0x07, 0xFE, 0x97, 0xE8, 0x1A, 0x33, 0x55, 0x83, 0x5D, 0x77, 0x82, -0x31, 0x5C, 0x3D, 0xB1, 0xD8, 0x6D, 0x3F, 0xB9, 0x53, 0x4F, 0xD4, 0xD0, 0xAF, 0x1E, 0x1A, 0x95, -0x8C, 0x1B, 0x06, 0x80, 0x8A, 0xF1, 0x84, 0xA6, 0x83, 0x46, 0x1B, 0x3E, 0xF1, 0x74, 0xD0, 0xE8, -0xB4, 0xE1, 0xCA, 0xB4, 0x45, 0xFA, 0x7A, 0x88, 0x74, 0x6B, 0xB4, 0xB9, 0x15, 0x2B, 0xBE, 0xF7, -0x30, 0x6F, 0xA7, 0x2E, 0x60, 0xC7, 0x8D, 0xCC, 0x51, 0x49, 0xCE, 0xAB, 0x92, 0x6E, 0xDF, 0x53, -0xAD, 0xB8, 0x35, 0xF6, 0xD8, 0x75, 0xFB, 0xC5, 0xD7, 0x02, 0x0C, 0x57, 0x73, 0xA3, 0xBA, 0x71, -0x1F, 0xDA, 0x93, 0xEB, 0x81, 0x53, 0x9C, 0x49, 0xC5, 0xCC, 0x64, 0xF2, 0x96, 0xFE, 0xAA, 0x5D, -0xC3, 0x33, 0x47, 0x33, 0x0D, 0xE8, 0xCB, 0xDF, 0xEB, 0x2B, 0xB4, 0xBA, 0x87, 0x32, 0x4C, 0x86, -0xAE, 0x5B, 0xB9, 0x91, 0x2A, 0x2E, 0x81, 0x4C, 0x6F, 0x2D, 0xD5, 0x5E, 0x53, 0x7F, 0xCF, 0xEF, -0x3F, 0xC7, 0x47, 0x1D, 0xEB, 0xFF, 0x27, 0x00, 0x00 +//File: setuppage.html.gz, Size: 4355 +#define setuppage_html_gz_len 4355 +const uint8_t setuppage_html_gz[] PROGMEM = { +0x1F, 0x8B, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xED, 0x5D, 0x79, 0x73, 0xDB, 0x38, +0xB2, 0xFF, 0x7F, 0xAB, 0xDE, 0x77, 0xC0, 0x68, 0x2A, 0xA1, 0x5C, 0x89, 0x0E, 0xDB, 0x71, 0x92, +0x75, 0x2C, 0x55, 0xF9, 0x52, 0xC6, 0x59, 0x67, 0xE2, 0xB5, 0x94, 0x99, 0x79, 0xB3, 0xB5, 0xB5, +0x05, 0x91, 0x90, 0x84, 0x98, 0x24, 0x38, 0x00, 0x69, 0xD9, 0x79, 0x9B, 0xEF, 0xFE, 0x1A, 0x20, +0x78, 0x1F, 0xA2, 0x64, 0x67, 0x37, 0xDA, 0xAC, 0x9C, 0xC4, 0x24, 0xD8, 0xEC, 0xE3, 0xD7, 0x8D, +0xC6, 0xC1, 0xA6, 0x72, 0xF4, 0xC3, 0xD9, 0x87, 0xD3, 0xC9, 0xFF, 0x5E, 0x9D, 0xA3, 0x85, 0xEF, +0xD8, 0xC3, 0xFF, 0xF9, 0xD3, 0x91, 0xFE, 0x2D, 0x8F, 0x08, 0xB6, 0xE0, 0x08, 0xC1, 0xE7, 0xC8, +0xA6, 0xEE, 0x0D, 0xE2, 0xC4, 0x1E, 0x18, 0xC2, 0xBF, 0xB7, 0x89, 0x58, 0x10, 0xE2, 0x1B, 0x68, +0xC1, 0xC9, 0x6C, 0x60, 0x2C, 0x7C, 0xDF, 0x13, 0x87, 0xBD, 0x9E, 0x69, 0xB9, 0x9F, 0x44, 0xD7, +0xB4, 0x59, 0x60, 0xCD, 0x6C, 0xCC, 0x49, 0xD7, 0x64, 0x4E, 0x0F, 0x7F, 0xC2, 0x77, 0x3D, 0x9B, +0x4E, 0x45, 0x6F, 0xC6, 0x5C, 0xBF, 0x83, 0x97, 0x44, 0x30, 0x87, 0xF4, 0x0E, 0xBA, 0xBB, 0x07, +0xDD, 0xFD, 0x9E, 0x29, 0x44, 0x0F, 0xDB, 0x76, 0xD7, 0xA1, 0x6E, 0x17, 0x8E, 0x0D, 0x44, 0x5D, +0x9F, 0xCC, 0x39, 0xF5, 0xEF, 0x41, 0xD0, 0x02, 0x1F, 0xEC, 0xEE, 0x75, 0xE8, 0x9D, 0xFD, 0xE1, +0xF7, 0x77, 0x77, 0xF6, 0x6B, 0xFC, 0x69, 0xF7, 0xF4, 0x73, 0xDF, 0xF9, 0xCB, 0xA7, 0xFB, 0xD1, +0xE4, 0xDD, 0x5F, 0xEF, 0x7A, 0xE2, 0xD9, 0xC7, 0x97, 0x4B, 0xD6, 0x67, 0x2F, 0xAF, 0x9E, 0x9D, +0xFE, 0x7E, 0x75, 0xFD, 0xEE, 0xB7, 0x67, 0xF3, 0xCF, 0x64, 0xFF, 0xDD, 0xE2, 0xF5, 0x88, 0xB3, +0x9E, 0x33, 0xB9, 0xBF, 0xE4, 0x07, 0xBC, 0xF7, 0xCB, 0xDD, 0xB3, 0xE0, 0x97, 0x57, 0xEF, 0x5E, +0x5F, 0xDF, 0x5E, 0xCF, 0x7E, 0xF7, 0x0E, 0x9E, 0xFD, 0xF6, 0xE7, 0x19, 0xBD, 0x7D, 0xFB, 0xEA, +0x78, 0x30, 0x30, 0x90, 0xC9, 0x99, 0x10, 0x8C, 0xD3, 0x39, 0x75, 0x07, 0x06, 0x76, 0x99, 0x7B, +0xEF, 0xB0, 0x00, 0x74, 0x00, 0xA3, 0x08, 0xE7, 0x84, 0x7B, 0xCC, 0xA6, 0x26, 0x28, 0xE2, 0xB2, +0x4E, 0xD4, 0x64, 0xA0, 0x5E, 0x84, 0x88, 0xC2, 0x41, 0x9F, 0xC8, 0xCF, 0x94, 0x59, 0xF7, 0xE8, +0xFF, 0x92, 0x73, 0xD5, 0x86, 0xCD, 0x9B, 0x39, 0x67, 0x81, 0x6B, 0x75, 0x4C, 0x66, 0x33, 0x7E, +0x88, 0x7E, 0x9C, 0xED, 0xCA, 0x9F, 0x37, 0x59, 0x3A, 0x05, 0xCD, 0x0C, 0x3B, 0xD4, 0xBE, 0x3F, +0x44, 0xC7, 0x9C, 0x62, 0xFB, 0x39, 0x12, 0xD8, 0x15, 0x1D, 0x41, 0x38, 0x9D, 0xA5, 0x88, 0xBF, +0x24, 0x87, 0x80, 0xAE, 0xEB, 0x63, 0xEA, 0x12, 0x9E, 0x97, 0x6A, 0x51, 0xE1, 0xD9, 0x18, 0x38, +0xCD, 0x6C, 0x72, 0x97, 0x97, 0x04, 0x4D, 0x1D, 0x8B, 0x72, 0x62, 0xFA, 0x94, 0xB9, 0x87, 0x08, +0xD4, 0x0A, 0x1C, 0x37, 0x47, 0xF4, 0x29, 0x10, 0x3E, 0x9D, 0xDD, 0x77, 0xA4, 0x04, 0xE2, 0xFA, +0x40, 0x05, 0xFF, 0x12, 0x9E, 0xA3, 0xC2, 0x36, 0x9D, 0xBB, 0x1D, 0xEA, 0x13, 0x47, 0x54, 0x50, +0x2C, 0x08, 0x9D, 0x2F, 0xE4, 0xED, 0xD8, 0x36, 0xDB, 0xBB, 0xFD, 0xFE, 0x13, 0xD4, 0x41, 0xF0, +0xCB, 0xBB, 0xDB, 0xA9, 0x30, 0xC9, 0x66, 0x73, 0xD6, 0xD9, 0xC8, 0xAE, 0x7A, 0x65, 0xF2, 0x32, +0xF2, 0x9C, 0x97, 0xD4, 0xF2, 0x17, 0x87, 0xA1, 0x6E, 0x15, 0x36, 0x94, 0x5D, 0x73, 0x30, 0x87, +0xD8, 0xE9, 0xF0, 0x90, 0x62, 0x2F, 0x4B, 0x50, 0xB0, 0xCB, 0x27, 0x77, 0x7E, 0x5E, 0xB0, 0x72, +0xBC, 0xA0, 0x9F, 0xC9, 0x21, 0xDA, 0x7F, 0x59, 0xE0, 0xAF, 0xAE, 0x2E, 0xB5, 0x02, 0x53, 0x66, +0x5B, 0x15, 0xEC, 0x67, 0x8C, 0x3B, 0xD5, 0xB0, 0x15, 0x83, 0x70, 0xB9, 0x00, 0xA0, 0x72, 0xB2, +0x3C, 0x6C, 0x59, 0xD4, 0x9D, 0x17, 0xCC, 0x50, 0x1C, 0x18, 0xB7, 0x08, 0xEF, 0x70, 0x6C, 0xD1, +0x40, 0x48, 0x28, 0x4A, 0x28, 0xEE, 0x3A, 0xD0, 0x5F, 0x2D, 0xB6, 0x3C, 0x44, 0x7D, 0xF8, 0x91, +0x24, 0x88, 0xCF, 0xA7, 0xB8, 0xDD, 0x7F, 0x8E, 0xF4, 0x9F, 0xEE, 0xFE, 0xCE, 0x9B, 0x52, 0xD8, +0x5F, 0xF7, 0x9F, 0x94, 0x03, 0xEB, 0x33, 0xAF, 0x0E, 0x56, 0x1B, 0x4F, 0x89, 0x5D, 0x19, 0x24, +0x53, 0x9B, 0x99, 0x37, 0xE5, 0x02, 0x65, 0x28, 0x96, 0x4B, 0x9C, 0x32, 0xDF, 0x67, 0xCE, 0x21, +0x3A, 0x58, 0xCB, 0x17, 0xF1, 0xF5, 0xD0, 0x93, 0xBB, 0x45, 0x4F, 0x46, 0xBD, 0x7F, 0x7F, 0x7F, +0xBF, 0xDC, 0x16, 0xEA, 0x7A, 0x81, 0xFF, 0x37, 0xFF, 0xDE, 0x23, 0x03, 0x43, 0x06, 0x8A, 0xF1, +0xF7, 0xEA, 0x18, 0xCD, 0xEB, 0x1E, 0xBB, 0xAE, 0xD4, 0x31, 0x19, 0xD7, 0x15, 0xED, 0x0A, 0x09, +0xE0, 0x5E, 0x70, 0x98, 0x80, 0x84, 0x67, 0xA1, 0x1F, 0x4D, 0xD3, 0xAC, 0x47, 0xA7, 0x24, 0x44, +0xAA, 0xCD, 0xAF, 0x30, 0x52, 0x04, 0x53, 0x87, 0x96, 0x98, 0x59, 0x92, 0x32, 0x5F, 0x9C, 0x1E, +0x8F, 0x0E, 0xFA, 0xE5, 0x88, 0xD6, 0x86, 0xF2, 0x43, 0xF0, 0x80, 0xE1, 0x20, 0xCF, 0xD7, 0x0C, +0xB8, 0x90, 0x32, 0x3D, 0x46, 0x4B, 0x92, 0x5D, 0x4D, 0xCC, 0xD6, 0x03, 0x14, 0x5F, 0x5D, 0xDD, +0xD3, 0xA7, 0x01, 0x38, 0xC0, 0xFD, 0x2F, 0x64, 0x6B, 0x40, 0xD6, 0xF5, 0xD9, 0x7C, 0x6E, 0x93, +0xAF, 0x31, 0xAA, 0xA4, 0x4C, 0x88, 0x7A, 0xC6, 0x6E, 0xF5, 0x18, 0xA0, 0xF5, 0x28, 0x4D, 0x5A, +0xD9, 0x81, 0x64, 0x35, 0x13, 0xB1, 0xA4, 0xBE, 0xB9, 0xC8, 0x73, 0xF1, 0x98, 0xA0, 0xE1, 0xA8, +0x0E, 0x13, 0x35, 0xEC, 0xD3, 0xDB, 0xBC, 0x37, 0x62, 0x53, 0xA9, 0x0B, 0xF3, 0x39, 0xD2, 0xA9, +0x49, 0x91, 0x2F, 0xAB, 0x47, 0xC2, 0xFD, 0x17, 0x0D, 0xD5, 0x53, 0xDD, 0x3D, 0xAF, 0x24, 0xF3, +0xB0, 0x09, 0xD3, 0x3B, 0x18, 0x26, 0xCA, 0x05, 0xE7, 0x9B, 0x23, 0xA9, 0xFD, 0x0A, 0x91, 0x02, +0x52, 0x56, 0xD1, 0xA9, 0x09, 0x14, 0x78, 0x0A, 0x59, 0x2D, 0x28, 0x04, 0x7C, 0x7D, 0x60, 0xAA, +0x88, 0xCC, 0x6B, 0x62, 0x93, 0x99, 0x5F, 0x6C, 0xE5, 0x45, 0xF5, 0xE4, 0x27, 0x0A, 0x88, 0x42, +0x7B, 0xB1, 0xAF, 0x16, 0xD3, 0x2D, 0x84, 0xF5, 0xF4, 0x86, 0xFA, 0x1D, 0x9F, 0xC3, 0x1C, 0x50, +0xDB, 0xD1, 0x7D, 0x21, 0xF2, 0x4A, 0xD6, 0x5E, 0xCD, 0xF5, 0xDA, 0x1A, 0xA7, 0x85, 0x08, 0x1E, +0x4E, 0x09, 0x4C, 0x23, 0xC8, 0xFA, 0x40, 0x46, 0x73, 0x44, 0xC3, 0xA8, 0xF0, 0xDC, 0x5E, 0xB1, +0xEF, 0x6A, 0x5F, 0x97, 0x5C, 0x09, 0x41, 0x7E, 0x51, 0x92, 0x63, 0x42, 0x3C, 0x4B, 0xAE, 0x34, +0x9A, 0xDE, 0x7C, 0x05, 0x48, 0x0F, 0x32, 0x03, 0x71, 0x7E, 0x98, 0x3B, 0x34, 0x17, 0xC4, 0xBC, +0x21, 0x16, 0x7A, 0x56, 0x15, 0xA3, 0x25, 0xA1, 0xB0, 0xB7, 0xFB, 0xE7, 0x97, 0xA3, 0xBA, 0x19, +0xC2, 0xE1, 0x8C, 0x99, 0x81, 0xA8, 0xE1, 0x99, 0x9F, 0x87, 0xC1, 0xA8, 0xDE, 0x80, 0x6B, 0x41, +0xD7, 0x8A, 0x68, 0xC8, 0xA0, 0x28, 0x67, 0x9D, 0x87, 0x21, 0x64, 0x90, 0x6D, 0xC8, 0x6F, 0x6D, +0xE9, 0xCE, 0xFC, 0x14, 0xAF, 0xE3, 0x88, 0x35, 0xA8, 0x9B, 0x51, 0x56, 0xCC, 0x28, 0xE0, 0x86, +0x39, 0x59, 0x67, 0xDE, 0x54, 0x3F, 0xAB, 0xD1, 0x52, 0x8E, 0x7A, 0xE9, 0x85, 0xDE, 0x91, 0x30, +0x39, 0xF5, 0xFC, 0xD4, 0xB2, 0x6F, 0x16, 0xB8, 0x6A, 0x29, 0x85, 0x02, 0xCF, 0x02, 0x6D, 0x2F, +0x2C, 0x9B, 0xB4, 0x77, 0xF2, 0x3A, 0xDC, 0x62, 0x8E, 0xEE, 0xD0, 0x00, 0x59, 0xE0, 0x3D, 0x07, +0x7A, 0x4B, 0x77, 0x4E, 0xFC, 0x73, 0x9B, 0xC8, 0xC3, 0x93, 0xFB, 0x0B, 0xAB, 0xDD, 0xA2, 0x2E, +0x06, 0x2E, 0xB7, 0x90, 0x18, 0xDF, 0x53, 0x57, 0xB4, 0xF2, 0xB8, 0xD0, 0x19, 0x6A, 0x57, 0xDD, +0x6B, 0x24, 0xF7, 0x9E, 0xBB, 0x78, 0x6A, 0x13, 0xCB, 0xD8, 0xE9, 0x6A, 0x87, 0x16, 0x14, 0x91, +0x9F, 0xBB, 0xAE, 0xB2, 0xA8, 0xAB, 0x47, 0x03, 0x50, 0xCB, 0x50, 0x03, 0x41, 0xBE, 0xFF, 0x7E, +0x41, 0xC4, 0x16, 0x85, 0x10, 0xA8, 0xE0, 0x20, 0xC7, 0xFE, 0x22, 0x83, 0xD4, 0xA7, 0xD4, 0x7D, +0x31, 0x78, 0x62, 0xC1, 0x96, 0x13, 0x22, 0xFC, 0x0F, 0x9E, 0x3C, 0x15, 0x1B, 0x20, 0x78, 0x79, +0x7E, 0x26, 0x19, 0x8C, 0x89, 0xEF, 0xC3, 0x44, 0x66, 0x4D, 0x08, 0x39, 0xF1, 0x60, 0xA9, 0x0F, +0xEE, 0xBB, 0x24, 0xD6, 0xD8, 0x87, 0xDF, 0xEB, 0x43, 0x58, 0x06, 0x80, 0xFC, 0x54, 0xCA, 0xB4, +0xC8, 0x34, 0x98, 0x2F, 0xE9, 0x8C, 0x26, 0xB2, 0x80, 0xCF, 0x0C, 0x03, 0xE6, 0x0F, 0x71, 0x44, +0xB9, 0x2B, 0x57, 0xA3, 0x7F, 0x6C, 0xDD, 0x62, 0xD7, 0x24, 0xD6, 0x06, 0xD0, 0x47, 0xB7, 0x9E, +0xD1, 0xDB, 0xF5, 0x60, 0xC7, 0xFA, 0xC6, 0xC8, 0x69, 0xDF, 0x64, 0xE0, 0xD6, 0x7E, 0xEA, 0x71, +0x0D, 0x53, 0xC2, 0x29, 0x73, 0x67, 0x74, 0x5E, 0x81, 0xEB, 0x82, 0x83, 0x12, 0x2E, 0x59, 0xA2, +0xDF, 0xDE, 0x5F, 0xFE, 0xE4, 0xFB, 0xDE, 0x35, 0xF9, 0x23, 0x80, 0x30, 0x6E, 0xE7, 0x71, 0x04, +0xC2, 0x2E, 0x73, 0x39, 0xC1, 0xD6, 0xBD, 0x90, 0x01, 0x6A, 0x2E, 0x64, 0xB6, 0x93, 0x01, 0xA3, +0x85, 0x15, 0xF9, 0xCB, 0x8F, 0x84, 0x5F, 0xDE, 0xAA, 0x6E, 0x54, 0x91, 0x8D, 0x06, 0x83, 0x01, +0x7A, 0x81, 0x9E, 0x3E, 0x55, 0x2C, 0x25, 0x2F, 0x18, 0x4F, 0x64, 0xDB, 0x5E, 0xBF, 0x5F, 0xCA, +0x22, 0x52, 0xD5, 0x54, 0x66, 0x9C, 0x61, 0x1F, 0x83, 0xD4, 0x77, 0xE3, 0x0F, 0x3F, 0x77, 0x3D, +0xCC, 0x05, 0xD1, 0xEC, 0x85, 0x07, 0xDD, 0x96, 0x4C, 0x60, 0xE5, 0x9A, 0xD7, 0x3C, 0xFA, 0x54, +0xE7, 0x2F, 0x0F, 0xFC, 0x7E, 0x8B, 0xED, 0x40, 0x9A, 0x93, 0x48, 0xE9, 0x52, 0x0F, 0xFD, 0xF3, +0x9F, 0xC5, 0x69, 0xC5, 0x4A, 0x7E, 0x26, 0xB3, 0x48, 0x39, 0x47, 0x79, 0x65, 0x33, 0x9E, 0xD4, +0xAA, 0xD0, 0xD1, 0xDA, 0x8C, 0x5F, 0x4D, 0xC2, 0xC9, 0x0A, 0x88, 0x09, 0x21, 0xAF, 0x4B, 0x51, +0x65, 0xF9, 0x61, 0xA5, 0x34, 0xC2, 0x39, 0xE3, 0x67, 0xC4, 0x0F, 0x37, 0xFD, 0xAA, 0x44, 0x29, +0x2A, 0x2B, 0xA2, 0xDA, 0x58, 0xD8, 0x8C, 0xBA, 0x54, 0x2C, 0x2E, 0x5C, 0x4B, 0xAA, 0x5D, 0x23, +0x2E, 0xA4, 0xA3, 0x31, 0xDD, 0xC6, 0x02, 0x55, 0x22, 0x85, 0xF4, 0x51, 0x25, 0x28, 0xBA, 0xFE, +0x60, 0x01, 0x61, 0x9F, 0x5B, 0x25, 0x46, 0xF7, 0xCC, 0x4D, 0x85, 0x39, 0x7F, 0xF8, 0xBE, 0x62, +0x55, 0x25, 0x27, 0x26, 0xD8, 0x58, 0x04, 0xCC, 0xF0, 0x30, 0xBF, 0x94, 0x93, 0x73, 0xF1, 0x61, +0x36, 0xAB, 0x92, 0x93, 0xA5, 0xDA, 0x58, 0x58, 0xCD, 0xEC, 0x24, 0xD7, 0x95, 0xF2, 0x84, 0x8F, +0x20, 0x52, 0x4E, 0xA6, 0x2A, 0x3A, 0x6E, 0x86, 0x46, 0x75, 0xE2, 0xFD, 0xFE, 0xDA, 0xDD, 0xD8, +0xC1, 0xB0, 0x7A, 0x7C, 0x1F, 0xE6, 0x9B, 0x72, 0x67, 0x45, 0x04, 0x9B, 0x07, 0x1F, 0x15, 0x26, +0xAB, 0x13, 0x11, 0x13, 0x6C, 0x2C, 0xC2, 0x87, 0x01, 0xE7, 0xFA, 0xED, 0x49, 0x39, 0x50, 0xFA, +0xE2, 0x86, 0xA9, 0x78, 0xF9, 0x8F, 0x70, 0x3D, 0x51, 0xCD, 0x1B, 0x96, 0x3E, 0x96, 0x5A, 0xB0, +0x49, 0x09, 0x30, 0x49, 0x5F, 0x57, 0xC4, 0x72, 0xB5, 0x88, 0x25, 0xE6, 0xCE, 0x43, 0x44, 0x94, +0xCF, 0xD4, 0xF2, 0x5D, 0x5F, 0x12, 0x6C, 0xEC, 0x02, 0xEC, 0xBD, 0x3F, 0x3E, 0x2D, 0xB7, 0x40, +0x5D, 0xDA, 0x0C, 0xFE, 0xA9, 0xDA, 0x9C, 0x70, 0x89, 0x10, 0x75, 0x10, 0x25, 0x54, 0x9B, 0xE1, +0xD3, 0xCA, 0x8B, 0x39, 0x0B, 0x67, 0x57, 0xAD, 0x9D, 0xAE, 0xDC, 0xC9, 0x3E, 0x0D, 0x37, 0x08, +0x40, 0x66, 0xEB, 0x24, 0x26, 0x3C, 0x44, 0xAD, 0x67, 0xA8, 0x5D, 0xA7, 0xC4, 0x0E, 0x2C, 0x46, +0x5B, 0x4F, 0x5A, 0xEB, 0xEA, 0x32, 0xA3, 0xE0, 0x6B, 0xCC, 0xC9, 0x2D, 0xE1, 0x02, 0x06, 0x95, +0xA2, 0x0E, 0x23, 0x4D, 0x80, 0x7E, 0x09, 0x29, 0x0E, 0x5B, 0xCF, 0xDA, 0x99, 0x51, 0x29, 0x73, +0x3F, 0x92, 0xDA, 0xB4, 0x3E, 0xBA, 0x37, 0x2E, 0x5B, 0xBA, 0x28, 0x66, 0xBA, 0x76, 0x90, 0x42, +0x6C, 0x54, 0xF6, 0x30, 0x7D, 0x71, 0x33, 0x17, 0x87, 0xA3, 0x68, 0x25, 0xEF, 0xF8, 0xF2, 0x66, +0xDC, 0x3D, 0x1C, 0x08, 0x52, 0xC9, 0x3C, 0xBA, 0xBA, 0xA9, 0xE6, 0x5C, 0xF8, 0x10, 0x26, 0x84, +0xD7, 0x68, 0x9F, 0x22, 0xD9, 0x4C, 0x8A, 0xCB, 0x3E, 0x7F, 0xB6, 0x89, 0x69, 0xB3, 0x79, 0xA5, +0x94, 0x0C, 0xC9, 0x66, 0x52, 0x16, 0x8E, 0x18, 0x13, 0x4E, 0x59, 0x20, 0x2A, 0xA5, 0x64, 0x48, +0x36, 0x96, 0x32, 0x02, 0x56, 0x76, 0x9D, 0x8C, 0x88, 0x60, 0x53, 0x9F, 0xD8, 0x58, 0x9E, 0x5D, +0x07, 0x2E, 0x0B, 0xAA, 0xC7, 0x84, 0x02, 0xD9, 0x86, 0xD2, 0x38, 0x74, 0xCB, 0x53, 0x76, 0x5B, +0x17, 0x01, 0x69, 0x92, 0x87, 0x44, 0xC0, 0x84, 0x38, 0xDE, 0x8A, 0x08, 0xD0, 0x24, 0x1B, 0xA6, +0x5A, 0x62, 0xD5, 0x8A, 0x48, 0xAE, 0xD7, 0xF2, 0x2F, 0xEC, 0x83, 0x54, 0xD0, 0xA5, 0x37, 0x9B, +0x4A, 0x48, 0xBE, 0x64, 0x9B, 0xBE, 0x94, 0x2D, 0x26, 0x3D, 0xE2, 0xB6, 0x8D, 0xB7, 0xE7, 0x13, +0xE3, 0x39, 0x32, 0x7A, 0x60, 0x4E, 0xB8, 0x4C, 0x85, 0x33, 0x9F, 0x07, 0xA4, 0x6C, 0xF9, 0x29, +0x88, 0x6B, 0xB5, 0x2B, 0xF6, 0xE1, 0x96, 0x30, 0x8F, 0x67, 0x4B, 0x58, 0xA1, 0xDA, 0x0C, 0x5B, +0xB5, 0xAB, 0xD2, 0xC6, 0xA3, 0x08, 0xA4, 0x6E, 0x6C, 0x59, 0xE7, 0xB7, 0x40, 0x70, 0x49, 0x05, +0xA4, 0x6F, 0xC2, 0xE5, 0x3E, 0x99, 0x17, 0xF8, 0xAD, 0xE7, 0x09, 0x7F, 0x52, 0xBA, 0x66, 0x7D, +0xF4, 0xA1, 0x8A, 0x74, 0x7D, 0xCC, 0x81, 0x99, 0x76, 0x6D, 0xD9, 0xD0, 0xF4, 0x25, 0x8F, 0x59, +0x76, 0xF5, 0x9F, 0xBB, 0xB8, 0xCA, 0xD1, 0x15, 0x0E, 0x96, 0x9E, 0x94, 0x3F, 0xD1, 0x79, 0xD9, +0x66, 0x8E, 0x7A, 0xB0, 0x3A, 0x62, 0xDC, 0x29, 0x82, 0x4F, 0x24, 0x9A, 0x5D, 0x8F, 0xAB, 0xDF, +0x67, 0x64, 0x86, 0x03, 0x3B, 0xDC, 0x6A, 0x78, 0xE8, 0xCE, 0x84, 0x0A, 0xA6, 0xAB, 0x0F, 0xE3, +0x30, 0x9A, 0x42, 0x15, 0xC6, 0xC4, 0x0F, 0xBC, 0xDA, 0x78, 0xF2, 0x35, 0xCF, 0x9F, 0x08, 0x06, +0x5F, 0xB4, 0x0D, 0x8D, 0x7F, 0x67, 0x72, 0xEF, 0x11, 0xC9, 0x08, 0x7B, 0xE1, 0xE2, 0x17, 0xCC, +0xEA, 0xDD, 0x75, 0x96, 0xCB, 0x65, 0x47, 0x55, 0x3A, 0x04, 0xDC, 0x26, 0xAE, 0x5C, 0xCB, 0xC3, +0x5A, 0xA2, 0xA0, 0xFB, 0x63, 0x6E, 0x94, 0x94, 0xEC, 0x93, 0xA4, 0xB6, 0x49, 0xCA, 0x7B, 0x26, +0xB6, 0x09, 0xF7, 0xDB, 0x46, 0xB4, 0xA5, 0x85, 0x04, 0xBE, 0x85, 0xE9, 0xA2, 0x08, 0x4C, 0x13, +0x62, 0x69, 0x16, 0xD8, 0xF6, 0xFD, 0x0F, 0x46, 0xC3, 0x2E, 0x5B, 0x74, 0x8A, 0x34, 0x5F, 0xEF, +0xC0, 0x48, 0xCF, 0x8C, 0xF4, 0x69, 0xB2, 0xC3, 0x06, 0x70, 0xF2, 0xFB, 0x31, 0x81, 0xE1, 0xCC, +0x67, 0x00, 0xA8, 0xA4, 0x37, 0x76, 0xF2, 0xE2, 0x24, 0x27, 0x0F, 0x73, 0x48, 0xE1, 0x3E, 0x4C, +0x68, 0x80, 0xD7, 0xDF, 0xFE, 0x9E, 0xA3, 0x88, 0xE4, 0xC8, 0xCA, 0x92, 0x73, 0x6C, 0x2E, 0xDA, +0x31, 0x78, 0x2A, 0xFE, 0x9F, 0xA3, 0x1B, 0x72, 0x5F, 0x0A, 0x63, 0xC2, 0xB6, 0xEB, 0x05, 0x62, +0xD1, 0x0E, 0xFD, 0xF4, 0xF1, 0xFA, 0xE2, 0x94, 0x39, 0x1E, 0x73, 0x41, 0xC5, 0xB6, 0xBA, 0xF5, +0x19, 0x32, 0x06, 0x06, 0xFC, 0x5B, 0x72, 0x5D, 0x49, 0x28, 0xE8, 0x5C, 0xE8, 0x5D, 0xD2, 0x08, +0x55, 0x72, 0x35, 0x48, 0x0B, 0xFD, 0xC4, 0x28, 0x04, 0xE2, 0xD3, 0x8A, 0xB8, 0x50, 0x19, 0x4C, +0xDE, 0x54, 0xBC, 0xCC, 0x21, 0x58, 0xB9, 0x5B, 0x98, 0xBC, 0x27, 0x0F, 0x01, 0xA2, 0x7D, 0xFF, +0xA3, 0x9E, 0x2E, 0x8B, 0x83, 0x43, 0xC9, 0x2A, 0x7A, 0x30, 0x60, 0xD1, 0x5B, 0x64, 0xDA, 0x58, +0x88, 0x81, 0x11, 0x3F, 0x6A, 0x36, 0x52, 0xCF, 0x09, 0xD2, 0x04, 0xD9, 0x32, 0xA7, 0x34, 0x95, +0xA2, 0xA4, 0xCE, 0x3C, 0x4D, 0x69, 0x20, 0xC1, 0xCD, 0xA4, 0xCA, 0x8E, 0xE3, 0x65, 0x77, 0x4E, +0xFD, 0x45, 0x30, 0x85, 0xD9, 0x17, 0xD7, 0x8F, 0xDF, 0x54, 0xA9, 0xDD, 0x59, 0xE0, 0x9B, 0x8B, +0x33, 0xE8, 0xDB, 0x36, 0xF3, 0x7A, 0x27, 0x97, 0x97, 0xE7, 0x67, 0xB2, 0x43, 0x71, 0x66, 0x43, +0x48, 0xF6, 0xE4, 0x4A, 0xB4, 0x07, 0x8C, 0x7A, 0xD0, 0x8D, 0x7A, 0x17, 0x70, 0xDB, 0x93, 0xBD, +0xBE, 0xF8, 0x23, 0x90, 0x65, 0x7A, 0x9E, 0x3B, 0x37, 0x20, 0x6E, 0xFD, 0x81, 0x71, 0x29, 0xC5, +0xE5, 0xD5, 0x11, 0x1E, 0x76, 0x33, 0x9A, 0xAB, 0xFA, 0x94, 0x61, 0x4E, 0x00, 0x40, 0x04, 0x74, +0x69, 0x83, 0x7B, 0x60, 0x71, 0x05, 0x00, 0xD9, 0x82, 0xA5, 0x82, 0xC4, 0xC5, 0x2E, 0x70, 0x47, +0xC0, 0x1E, 0x25, 0xFC, 0x51, 0xDC, 0xA3, 0xAE, 0xF0, 0x9C, 0x80, 0x17, 0x76, 0x0B, 0x77, 0xED, +0x21, 0x6A, 0x0D, 0x0A, 0x2B, 0x80, 0x61, 0x3C, 0xE3, 0xD7, 0x2D, 0x87, 0xE8, 0xFC, 0xCE, 0xA3, +0x9C, 0xCA, 0x0E, 0x83, 0x6D, 0x24, 0xEB, 0x10, 0xBB, 0x7B, 0x2F, 0x80, 0xE3, 0x7E, 0x9E, 0xA3, +0x37, 0x9C, 0x2C, 0xA8, 0x80, 0xF8, 0x82, 0x04, 0x82, 0x6D, 0x9B, 0x2D, 0x05, 0xBA, 0x67, 0x01, +0xF2, 0x19, 0xD2, 0x59, 0xC5, 0x5F, 0x10, 0xF4, 0xFE, 0xAF, 0x93, 0x09, 0x9A, 0x72, 0x76, 0x03, +0x3A, 0x52, 0x77, 0xC6, 0x10, 0x76, 0x2D, 0xF8, 0x7B, 0x8F, 0x64, 0x49, 0x85, 0xEC, 0x33, 0x20, +0x43, 0x68, 0xDD, 0xBB, 0x47, 0x3D, 0x2F, 0x2F, 0x44, 0x62, 0x81, 0x20, 0x7C, 0x17, 0xCC, 0x1A, +0x84, 0x29, 0x14, 0x61, 0xD5, 0xD7, 0x06, 0xD9, 0x44, 0x8A, 0x60, 0x98, 0x50, 0xA7, 0x03, 0x43, +0x07, 0x6B, 0x3A, 0xD3, 0xBF, 0xC9, 0x83, 0xA8, 0x78, 0x87, 0xC5, 0x06, 0x20, 0x61, 0x20, 0x77, +0x59, 0x87, 0x17, 0x57, 0x87, 0x47, 0x3D, 0xD5, 0x56, 0x46, 0x1C, 0x3E, 0xAE, 0x4F, 0x95, 0x20, +0x49, 0x38, 0xE5, 0x7D, 0xC8, 0x85, 0xEE, 0x15, 0x1E, 0xF9, 0xD4, 0xB7, 0xE1, 0xF0, 0x5C, 0x3E, +0x33, 0x57, 0xD6, 0x5F, 0x5C, 0x49, 0x3B, 0xB9, 0x5C, 0xB7, 0xB1, 0x99, 0x44, 0x87, 0xA7, 0x01, +0x31, 0x90, 0xEA, 0xCD, 0x03, 0xC3, 0x18, 0x1E, 0x4D, 0xF9, 0x0A, 0x0D, 0xD5, 0xBE, 0xED, 0xF0, +0x58, 0x25, 0x4A, 0x70, 0xBC, 0x45, 0xD6, 0x57, 0x56, 0xB1, 0xD0, 0xEA, 0x86, 0xC7, 0x05, 0x85, +0x71, 0xC8, 0x5F, 0x6D, 0x05, 0x83, 0xD8, 0x87, 0xA9, 0x4C, 0x2D, 0x63, 0x28, 0x67, 0xF5, 0xE0, +0xE2, 0x8B, 0xB3, 0x0D, 0xB0, 0xB5, 0x62, 0x6C, 0xAD, 0x12, 0x55, 0x45, 0xC4, 0x39, 0x51, 0xD4, +0x22, 0xB7, 0xD4, 0x24, 0x6B, 0xE9, 0x18, 0x6E, 0x2D, 0x0C, 0x7F, 0x95, 0xDB, 0x13, 0x1A, 0xDC, +0x2B, 0x59, 0xF4, 0x80, 0xE4, 0xB6, 0xC2, 0x71, 0xE8, 0xBB, 0x3A, 0xD5, 0x7D, 0xB9, 0x19, 0x17, +0x3E, 0x45, 0x1D, 0x18, 0xF2, 0x29, 0x6A, 0x59, 0xA4, 0x29, 0xCA, 0xC9, 0x75, 0xC5, 0x95, 0x90, +0x8F, 0x15, 0x31, 0xD9, 0x93, 0xA5, 0x94, 0x55, 0x5C, 0x56, 0x40, 0x16, 0x5A, 0xA3, 0x51, 0xD3, +0x27, 0x05, 0xE0, 0xA4, 0x65, 0xF9, 0xA8, 0x14, 0x1E, 0x31, 0x01, 0x02, 0x33, 0x83, 0x41, 0x02, +0x24, 0x72, 0xF0, 0x1D, 0xCC, 0x2A, 0xE6, 0xCA, 0xC8, 0x57, 0x90, 0x72, 0xE9, 0x67, 0xA2, 0x8E, +0xEA, 0x4C, 0xEA, 0xF9, 0x56, 0x33, 0x8B, 0x5F, 0x2A, 0x83, 0x9F, 0xBA, 0x53, 0xE1, 0xBD, 0x69, +0x7E, 0xD7, 0x7E, 0x23, 0x9C, 0x52, 0x69, 0x35, 0x5F, 0xEA, 0xB4, 0xEA, 0x5E, 0x75, 0x7F, 0x18, +0x2A, 0x59, 0x0E, 0x61, 0x01, 0x4F, 0x93, 0xDB, 0x15, 0x8B, 0xB4, 0xAB, 0xD4, 0x06, 0xD9, 0x94, +0xDD, 0x85, 0xEE, 0x02, 0x17, 0x98, 0xD8, 0xFD, 0x95, 0x8E, 0xE8, 0xCF, 0xC4, 0x5F, 0x32, 0x7E, +0x13, 0xB9, 0xAE, 0x78, 0xA1, 0xA9, 0xB0, 0xF4, 0x70, 0xA4, 0xB7, 0xB5, 0x86, 0x85, 0xC1, 0xA7, +0xF2, 0xEE, 0xCA, 0x38, 0xCF, 0x7F, 0x42, 0x67, 0xA5, 0xFF, 0xCD, 0x48, 0x4E, 0x57, 0x73, 0x19, +0xC3, 0x2B, 0xC2, 0x55, 0x1A, 0x97, 0x93, 0x32, 0xD5, 0xD1, 0xDC, 0xD0, 0x28, 0x24, 0x8D, 0x3C, +0x3A, 0xB9, 0x1E, 0xB6, 0xAF, 0x09, 0x8C, 0x01, 0x42, 0x0E, 0x1D, 0x02, 0x06, 0x34, 0x18, 0x3B, +0x84, 0x8F, 0x4E, 0xC6, 0xE3, 0x8B, 0xB3, 0x9D, 0x46, 0xBA, 0xE7, 0x07, 0xD3, 0xE2, 0xF5, 0xCA, +0xA0, 0x3A, 0xEA, 0x95, 0xF6, 0x4B, 0xB8, 0x43, 0x76, 0xEC, 0x55, 0xC9, 0x4A, 0x97, 0x69, 0x46, +0xFD, 0x64, 0x0C, 0xB3, 0x59, 0x14, 0x2E, 0x69, 0x02, 0x1E, 0x3E, 0xDE, 0x19, 0x4A, 0xFB, 0x4A, +0xB8, 0x94, 0xB7, 0xA6, 0x12, 0x53, 0x61, 0x77, 0x52, 0x0D, 0xDF, 0x55, 0x2B, 0xB4, 0x61, 0x7A, +0x45, 0x26, 0xD3, 0x50, 0xB3, 0x6C, 0xDB, 0x52, 0x55, 0x21, 0xAD, 0x52, 0xD6, 0xAD, 0x30, 0x16, +0x4B, 0xDA, 0x1D, 0xEA, 0x0E, 0x5A, 0xFD, 0x96, 0x4C, 0x0A, 0x83, 0x16, 0x08, 0x6B, 0x69, 0xFB, +0xD5, 0xF1, 0xAA, 0x24, 0x29, 0x89, 0x9E, 0x94, 0x51, 0x29, 0xCA, 0xFA, 0x24, 0x39, 0x39, 0x93, +0x92, 0xE8, 0x1C, 0xC4, 0xFB, 0xCC, 0x6B, 0x45, 0x1C, 0x0F, 0xAA, 0x19, 0xC6, 0xB7, 0xEA, 0xE9, +0xCF, 0x5B, 0xB9, 0x50, 0xC6, 0x76, 0x5C, 0x07, 0x31, 0xD4, 0x0D, 0xF1, 0xFC, 0x09, 0x26, 0x3A, +0x7B, 0xAB, 0x78, 0x81, 0xEF, 0xCA, 0xFD, 0x97, 0xA1, 0xFA, 0xC6, 0x33, 0x4F, 0xFE, 0x09, 0x6B, +0x9C, 0x79, 0x0A, 0x17, 0x98, 0x1B, 0xCE, 0xE7, 0x92, 0x99, 0x55, 0x7E, 0x75, 0xBE, 0xD5, 0xD9, +0xE9, 0x3A, 0xB2, 0x58, 0x4D, 0xA9, 0x95, 0xCD, 0x5A, 0xA5, 0xF4, 0xAD, 0xAA, 0x32, 0x02, 0xA6, +0xCF, 0x49, 0xD1, 0xAD, 0x70, 0xB0, 0x9C, 0x79, 0xBF, 0x69, 0x41, 0x30, 0x0C, 0xDB, 0x13, 0x09, +0x0C, 0x9B, 0xCD, 0xD4, 0x64, 0xC4, 0x91, 0xC5, 0x61, 0xD1, 0xAC, 0x16, 0x52, 0xD8, 0x49, 0x33, +0x23, 0x57, 0xA5, 0x31, 0x45, 0xF3, 0x4D, 0x87, 0x55, 0xEE, 0x51, 0xBA, 0x8E, 0xA9, 0x5C, 0xEB, +0x36, 0x07, 0xCB, 0xB9, 0x34, 0x05, 0xC5, 0xB6, 0x7C, 0x27, 0x6E, 0x2D, 0x14, 0x2D, 0x68, 0xC7, +0x16, 0xDA, 0xB7, 0xD9, 0xB5, 0x23, 0x65, 0x0C, 0x4A, 0xAC, 0xF9, 0x4E, 0x9C, 0x9B, 0xAB, 0x77, +0xD0, 0xAE, 0xCD, 0xB5, 0x6E, 0xB3, 0x63, 0x95, 0x15, 0xE8, 0x83, 0xCE, 0xCD, 0x97, 0xD2, 0xB0, +0xEF, 0xC4, 0xB5, 0xC5, 0xEA, 0x92, 0x68, 0x41, 0x5D, 0xBC, 0x50, 0x18, 0xE5, 0xD3, 0x7B, 0xEC, +0x5B, 0xED, 0xFF, 0x8B, 0xD8, 0x58, 0x34, 0xA1, 0x0E, 0x61, 0x00, 0x56, 0xFB, 0x3D, 0x75, 0x03, +0x9F, 0x88, 0x66, 0xCB, 0x8B, 0x14, 0xCA, 0x7A, 0x1A, 0x10, 0x56, 0x4D, 0xBF, 0x90, 0x25, 0xD1, +0xD1, 0x5B, 0x23, 0xAA, 0xD6, 0xB2, 0x55, 0xDC, 0xC4, 0xC8, 0x56, 0xDB, 0x14, 0xF0, 0x0F, 0x5B, +0xF5, 0x1A, 0xFD, 0xE7, 0xC0, 0x99, 0xC2, 0x22, 0x1D, 0x16, 0xE5, 0x14, 0x60, 0x97, 0xF3, 0x6C, +0xA9, 0x24, 0xD2, 0xD5, 0xE6, 0x6A, 0x4B, 0x51, 0x4E, 0x52, 0x04, 0x92, 0xFB, 0x75, 0xD2, 0x49, +0xC4, 0x52, 0x73, 0x8E, 0x78, 0x01, 0xB2, 0x9B, 0x59, 0xA9, 0x1F, 0x44, 0x0B, 0xF5, 0x83, 0x95, +0xCB, 0xE4, 0xAD, 0x0F, 0xF5, 0xA4, 0xD6, 0x48, 0x43, 0x9C, 0x34, 0x6C, 0x73, 0xEC, 0xBE, 0x97, +0x56, 0x10, 0x57, 0xD6, 0x01, 0x23, 0x69, 0xCC, 0x77, 0x92, 0xB8, 0x92, 0xB2, 0x2E, 0xED, 0xCD, +0xA4, 0x61, 0x9B, 0xBD, 0x29, 0x9F, 0x6F, 0x7F, 0x4F, 0x5E, 0x8C, 0x4A, 0x4F, 0x23, 0x27, 0x46, +0xE7, 0xDB, 0xEC, 0xC3, 0x33, 0x69, 0x84, 0xB4, 0xE2, 0x3B, 0x73, 0xA2, 0x2E, 0xEF, 0xCD, 0xB9, +0x52, 0xB7, 0xFE, 0x47, 0x38, 0x14, 0x9D, 0x2A, 0x63, 0xC4, 0x77, 0xE2, 0xD8, 0xA4, 0x94, 0x3A, +0x1A, 0x31, 0xE3, 0x86, 0x6D, 0x76, 0xA7, 0x7A, 0x58, 0x75, 0xC9, 0xBE, 0xA7, 0x2E, 0x5A, 0x78, +0x51, 0x28, 0x7A, 0x00, 0x54, 0x68, 0x2F, 0xDD, 0xCA, 0x4B, 0xDE, 0x6D, 0xDA, 0x6A, 0xC7, 0x47, +0x66, 0x3C, 0xD2, 0x13, 0x83, 0xC9, 0xD9, 0x57, 0xDA, 0x89, 0x96, 0xA1, 0xA4, 0xDC, 0x96, 0x7B, +0x27, 0xCF, 0xD0, 0x8B, 0x0B, 0x23, 0x5A, 0x51, 0xA8, 0x77, 0xAF, 0x9A, 0xE0, 0xB9, 0xD8, 0x1B, +0xEA, 0xE7, 0x00, 0xE1, 0xE3, 0x15, 0xFD, 0xCC, 0x08, 0x8D, 0x7D, 0xAE, 0x56, 0x02, 0xE8, 0x96, +0x62, 0xB5, 0x6E, 0x68, 0xB0, 0xB9, 0xAD, 0x18, 0x86, 0xFB, 0xF5, 0x7A, 0xAD, 0xA3, 0xDF, 0xEB, +0x15, 0xF2, 0xCD, 0x74, 0x77, 0x7E, 0xD8, 0x6F, 0x35, 0xD9, 0xC8, 0x2F, 0x02, 0xB6, 0x6A, 0xBF, +0x3C, 0x4B, 0x9D, 0xC0, 0xEB, 0x50, 0x0B, 0xD6, 0x42, 0x4D, 0xE5, 0xC4, 0x1C, 0x1E, 0xD8, 0x61, +0x0B, 0xFC, 0x1E, 0xDE, 0x81, 0x0B, 0x2C, 0x57, 0x8C, 0xB9, 0xAA, 0xA4, 0x3E, 0x3D, 0xDC, 0xAA, +0x86, 0x4D, 0x85, 0x3D, 0xA8, 0xC7, 0x16, 0xB8, 0x35, 0xEE, 0xC1, 0xF9, 0xCF, 0xFA, 0x23, 0xB3, +0x0A, 0xE9, 0x8D, 0x34, 0x6D, 0x92, 0xD8, 0xB3, 0xF4, 0xB5, 0x7D, 0xBE, 0x40, 0x9E, 0xCB, 0x01, +0xEB, 0x6A, 0x57, 0xDF, 0xC9, 0x36, 0xF1, 0xCA, 0xE4, 0x3A, 0x62, 0x17, 0xBE, 0x31, 0x1F, 0xBE, +0xE9, 0x5F, 0xF2, 0x22, 0xFD, 0x7E, 0x5F, 0xFE, 0xBC, 0x91, 0xBB, 0x14, 0x1D, 0x65, 0x41, 0xFC, +0xF5, 0x1D, 0x9B, 0xC8, 0x0D, 0x65, 0x9F, 0x45, 0x69, 0xE1, 0x55, 0xDF, 0xBB, 0x53, 0xCF, 0x44, +0xAE, 0xC7, 0xE3, 0x8B, 0xF0, 0xD9, 0x07, 0xE0, 0x0A, 0x04, 0x43, 0xED, 0x7D, 0x14, 0x37, 0x00, +0xD1, 0x18, 0xA4, 0xCB, 0xC7, 0x6F, 0x3A, 0x55, 0xC5, 0xF4, 0x9B, 0x04, 0xE5, 0x5A, 0x39, 0x26, +0xD1, 0x3C, 0x46, 0xAD, 0x04, 0xA9, 0x7E, 0x7F, 0x36, 0xEB, 0xF7, 0xE5, 0x33, 0x1E, 0x30, 0x50, +0x93, 0x95, 0xC2, 0xD6, 0xD9, 0xEF, 0xCB, 0xC7, 0xD7, 0x9D, 0x83, 0x7E, 0x64, 0x5D, 0x52, 0xCF, +0x21, 0xCB, 0x14, 0x0A, 0xB6, 0x9F, 0xDF, 0x99, 0xC4, 0xB6, 0xE1, 0xF6, 0xB0, 0x45, 0x2A, 0x5F, +0x51, 0x33, 0xB9, 0xB9, 0xF6, 0x18, 0xCF, 0x66, 0x18, 0x37, 0xD1, 0xFE, 0x60, 0x57, 0x69, 0xFF, +0xB2, 0x5F, 0xED, 0xAC, 0xB7, 0x8C, 0x59, 0x5F, 0x51, 0xD7, 0xD9, 0xAC, 0x29, 0xD2, 0x2F, 0x43, +0x5D, 0x5F, 0xD5, 0xE8, 0x3A, 0xC2, 0x94, 0x7F, 0x55, 0x5D, 0x5F, 0xBF, 0x6E, 0xA6, 0xEB, 0xAB, +0x50, 0xD7, 0xD7, 0x35, 0xBA, 0x5E, 0x31, 0xF6, 0x75, 0x75, 0xED, 0xF7, 0x1B, 0xE9, 0xFA, 0xD4, +0xF6, 0xDF, 0xD4, 0x6B, 0xFA, 0xD1, 0xE5, 0xC4, 0xA6, 0x32, 0x6D, 0x25, 0xFA, 0xAE, 0x9B, 0x92, +0xAB, 0xCA, 0x2A, 0xAA, 0xEF, 0x68, 0x9C, 0x0C, 0x1A, 0xEA, 0x93, 0xE8, 0xD0, 0x6C, 0x6A, 0x25, +0xE7, 0x68, 0xEA, 0xD1, 0xB0, 0x42, 0xB4, 0xE9, 0x1C, 0xEA, 0xD7, 0x05, 0xB5, 0x89, 0x2A, 0x55, +0xBC, 0x25, 0xCF, 0x51, 0x58, 0x42, 0xE7, 0x88, 0xB9, 0x78, 0x8E, 0x3C, 0xAE, 0xBE, 0x53, 0x07, +0xA9, 0xFA, 0x6B, 0x81, 0x9E, 0xA2, 0xD4, 0xCB, 0x5D, 0x14, 0xFE, 0xCC, 0x5D, 0xC6, 0x61, 0x16, +0xDB, 0xA8, 0xC8, 0x40, 0xE9, 0xD8, 0xB8, 0xBA, 0xA2, 0x70, 0xE7, 0xDA, 0x93, 0x32, 0x2D, 0x64, +0xEF, 0x00, 0x64, 0xA4, 0x8A, 0x55, 0x5A, 0xFA, 0x35, 0xC8, 0xD6, 0x10, 0x26, 0xB3, 0x48, 0x01, +0x76, 0x2A, 0xC1, 0x8A, 0xE6, 0x09, 0xEB, 0x8F, 0xAC, 0xC3, 0x4C, 0x95, 0x8A, 0x42, 0x5E, 0x55, +0xA9, 0xC4, 0x6F, 0x63, 0xEA, 0x89, 0x51, 0x7C, 0xAA, 0x37, 0xC0, 0x7F, 0xDC, 0x1F, 0xED, 0x9F, +0x8E, 0x4E, 0x8C, 0x75, 0x44, 0xAE, 0x11, 0xC7, 0x6B, 0x23, 0x96, 0x41, 0x29, 0x7E, 0x19, 0xB3, +0x35, 0xFC, 0x15, 0x73, 0x47, 0xC6, 0x88, 0xAC, 0x35, 0x78, 0x1C, 0x8C, 0x52, 0x95, 0x3C, 0x89, +0x1C, 0x5D, 0xC2, 0x93, 0x6A, 0xC8, 0xD4, 0xEE, 0xEC, 0x1D, 0x1C, 0x64, 0x6B, 0x77, 0xBE, 0x45, +0xD4, 0xCC, 0x04, 0x35, 0x08, 0x2A, 0xEB, 0xAB, 0xA1, 0x66, 0xE6, 0x51, 0x33, 0xFF, 0x7D, 0xA8, +0x35, 0xCF, 0x93, 0x47, 0x3F, 0xAD, 0x2C, 0x44, 0x6A, 0xBA, 0xBF, 0x21, 0xBB, 0x57, 0xEA, 0xCB, +0x4A, 0x1E, 0xB4, 0x1E, 0x8D, 0xF8, 0xAC, 0x53, 0x57, 0xA5, 0xEE, 0x3D, 0x19, 0x9E, 0x06, 0xC2, +0x67, 0x0E, 0x15, 0x24, 0x95, 0x74, 0xD5, 0x93, 0xDB, 0x30, 0x61, 0xAA, 0x19, 0x62, 0xA3, 0xC4, +0xD8, 0x4E, 0x6A, 0xE2, 0xA2, 0x62, 0x1C, 0xE0, 0x41, 0x6D, 0x1B, 0xA9, 0x37, 0x67, 0x88, 0xB5, +0xF3, 0x4D, 0x27, 0xD9, 0x5D, 0x29, 0xA3, 0x2A, 0x0D, 0x46, 0xAF, 0xCC, 0xEA, 0x34, 0x18, 0x9F, +0x46, 0x69, 0x70, 0x34, 0x3A, 0x3E, 0xE8, 0xF7, 0xD7, 0x4A, 0x83, 0x91, 0xF8, 0x6C, 0xCA, 0x0A, +0x19, 0x43, 0xC2, 0xA2, 0x23, 0x8A, 0x54, 0xA9, 0x3D, 0xEA, 0xA1, 0xB1, 0x89, 0x5D, 0x57, 0xED, +0xAF, 0xAD, 0xDD, 0x0B, 0xBF, 0x66, 0xDA, 0x68, 0x84, 0x5C, 0xF2, 0x4A, 0x70, 0xA6, 0x88, 0x25, +0x83, 0x5E, 0xBF, 0x3F, 0x1A, 0x3D, 0x06, 0x7A, 0x31, 0xEB, 0x96, 0xAE, 0x2E, 0x91, 0xBB, 0x53, +0xDB, 0x07, 0x59, 0xFC, 0x9E, 0xB3, 0x46, 0x2C, 0x39, 0x4F, 0x00, 0x93, 0x90, 0x6D, 0x02, 0x58, +0x06, 0xAF, 0x88, 0x71, 0x6B, 0x78, 0x25, 0x8F, 0x50, 0xFB, 0xAD, 0x7A, 0x11, 0x01, 0xFA, 0xFE, +0x47, 0x41, 0xF8, 0xCE, 0x36, 0x62, 0x97, 0x7D, 0x8F, 0x3B, 0x0E, 0xB9, 0x4C, 0xE3, 0xC3, 0x51, +0xCC, 0x87, 0x5D, 0x8A, 0xBD, 0x7A, 0xC3, 0x47, 0xCE, 0x60, 0xE5, 0x39, 0x0A, 0xEB, 0xD7, 0xDA, +0x21, 0xBC, 0x32, 0xA9, 0x06, 0xFF, 0x2A, 0x60, 0x1F, 0x1F, 0xDA, 0xEC, 0xCB, 0xEB, 0x1A, 0xDA, +0x5C, 0xE3, 0x23, 0x07, 0x68, 0x86, 0x7B, 0x6B, 0xF8, 0xB3, 0x3A, 0x45, 0xA7, 0x70, 0xFE, 0x6F, +0xC1, 0x74, 0x0D, 0x33, 0x1A, 0x21, 0x9A, 0x7D, 0x51, 0x5F, 0x23, 0x9A, 0x6B, 0x4C, 0x46, 0x18, +0x89, 0xE9, 0xC3, 0x83, 0x35, 0xC3, 0xBE, 0x35, 0xFC, 0xE9, 0xFD, 0x18, 0x86, 0x99, 0x5B, 0x22, +0xBF, 0xE4, 0xFE, 0x39, 0x1A, 0x9F, 0x5F, 0x5F, 0x7C, 0xF8, 0x38, 0xDE, 0xCE, 0xF8, 0x4C, 0x7F, +0x21, 0x41, 0x82, 0x65, 0xD2, 0xF4, 0x70, 0x24, 0xF3, 0x40, 0x46, 0xBC, 0xF3, 0x30, 0x8E, 0x8E, +0x27, 0xC7, 0x97, 0xDB, 0x18, 0x90, 0xC5, 0x6F, 0x5C, 0x88, 0x33, 0x68, 0xE1, 0xC2, 0x63, 0x07, +0x66, 0x41, 0x84, 0xCC, 0xA4, 0x61, 0x13, 0x0A, 0xDB, 0xB6, 0x33, 0x2C, 0xB3, 0xDF, 0x2B, 0x11, +0xE1, 0x99, 0x6D, 0x7C, 0xE4, 0xD0, 0xCC, 0x70, 0x07, 0x18, 0xE5, 0x29, 0x52, 0xE7, 0xE8, 0x9A, +0x38, 0xF0, 0x7B, 0x2B, 0xA7, 0x45, 0xD9, 0xAF, 0xCE, 0xC8, 0x8C, 0x3F, 0x71, 0xE3, 0x63, 0x07, +0x65, 0x86, 0x7D, 0x3C, 0x00, 0xC9, 0x73, 0x34, 0xC2, 0xD4, 0xDE, 0xCE, 0x88, 0x4C, 0x7D, 0x3B, +0x88, 0x46, 0x31, 0xDD, 0xF2, 0xC8, 0xB1, 0x98, 0xB0, 0x6E, 0x0D, 0x4F, 0x60, 0x81, 0xFA, 0x2F, +0xC0, 0xAE, 0xD9, 0x6A, 0xFE, 0x01, 0x8F, 0xA4, 0x57, 0xBE, 0xC4, 0x96, 0xBD, 0x76, 0xD4, 0x93, +0xAF, 0xE3, 0xE5, 0x6E, 0x38, 0xFA, 0xA1, 0xD3, 0x39, 0xD2, 0xDF, 0xA0, 0x1F, 0x3E, 0x93, 0x0C, +0x4F, 0x54, 0xD1, 0x80, 0x4D, 0xCD, 0x1B, 0xB9, 0xCA, 0x54, 0x5F, 0x63, 0x62, 0xB3, 0xB0, 0x3E, +0xBE, 0xAB, 0xFE, 0x7F, 0x9B, 0x56, 0x2F, 0x2C, 0x14, 0x6E, 0xBD, 0x31, 0x86, 0x1F, 0xD5, 0x11, +0x4A, 0xBF, 0xF6, 0x1E, 0x32, 0x19, 0x76, 0x3A, 0x29, 0x71, 0x69, 0x4B, 0xA3, 0x63, 0xA0, 0x0C, +0xBF, 0x2E, 0x40, 0x7D, 0x87, 0x40, 0xF8, 0x9F, 0xEC, 0xFC, 0x3F, 0x2D, 0x9C, 0x8E, 0x40, 0x7E, +0x67, 0x00, 0x00 }; \ No newline at end of file diff --git a/src/www/setuppage.html b/src/www/setuppage.html index d258569..d11264b 100644 --- a/src/www/setuppage.html +++ b/src/www/setuppage.html @@ -13,7 +13,7 @@ flex-direction: column; justify-content: center; align-items: center; - height: 100vh; + height: calc(100% - 100px); } .logo-container { display: flex; @@ -33,7 +33,7 @@ padding: 20px; border-radius: 10px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); - width: 60%; + width: 80%; margin-top: 20px; } label { @@ -134,6 +134,31 @@ }