diff --git a/README.md b/README.md index 5a29503..9dd4c44 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,14 @@ -/* The MIT License (MIT) - -Copyright (c) 2018 David Payne - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - # Printer Monitor (OctoPrint 3D Printer Monitor) +## New Easy Monitor Board Kit: +Now available is the Pre Loaded Monitor Board Kit that comes ready to plug and play on your network. +* Kit on Etsy: https://www.etsy.com/listing/823257424 +* New 3D printed case design for Monitor Board kit: https://www.thingiverse.com/thing:4538747 +* Configuration video: https://www.youtube.com/watch?v=kcBspqWhpIU + ## Features: -* Displays the print status from OctoPrint Server +* Displays the print status from OctoPrint or Repetier Server +* Option to display time and weather when printer is idle * Estimated time remaining * Time Printing * Percentage complete @@ -42,6 +26,7 @@ SOFTWARE. * Basic Authentication to protect your settings * Version 2.2 added the ability to update firmware through web interface from a compiled binary * Can query the Octoprint [PSU Control plugin](https://plugins.octoprint.org/plugins/psucontrol/) to enter clock or blank mode when PSU is off +* Repetier support added in version 3.0 -- define in Settings.h * Video: https://youtu.be/niRv9SCgAPk * Detailed build video by Chris Riley: https://youtu.be/Rm-l1FSuJpI @@ -49,12 +34,13 @@ SOFTWARE. * Wemos D1 Mini: https://amzn.to/2ImqD1n * 0.96" OLED I2C 128x64 Display (12864) SSD1306: https://amzn.to/3cyJekU * (optional) 1.3" I2C OLED Display: https://amzn.to/2IP0gRU (must uncomment #define DISPLAY_SH1106 in the Settings.h to use the 1.3" SSH1106 display) +* (optional) Pre loaded Monitor Board kit: https://www.etsy.com/listing/823257424 Note: Using the links provided here help to support these types of projects. Thank you for the support. ## Wiring for the Wemos D1 Mini to the I2C SSD1306 OLED SDA -> D2 -SCL -> D5 +SCL -> D5 / D1 -- for Easy Monitor Board VCC -> 5V+ GND -> GND- @@ -62,12 +48,17 @@ GND -> GND- ## 3D Printed Case by Qrome: https://www.thingiverse.com/thing:2884823 -- for the 0.96" OLED Display -https://www.thingiverse.com/thing:2934049 -- for the 1.3" OLED Display +https://www.thingiverse.com/thing:2934049 -- for the 1.3" OLED Display +https://www.thingiverse.com/thing:4538747 -- for 0.96" With Easy Monitor Board ## Upgrading from version 2.2 or Higher Version 2.2 introduced the ability to upgrade pre-compiled firmware from a binary file. In version 2.3 and on you should find binary files that can be uploaded to your printer monitor via the web interface. From the main menu in the web interface select "Firmware Update" and follow the prompts. * **printermonitor.ino.d1_mini_SSD1306.bin** - compiled for Wemos D1 Mini for the smaller 0.96" SSD1306 OLED (default) * **printermonitor.ino.d1_mini_SH1106.bin** - compiled for Wemos D1 Mini for the larger 1.3" SH1106 OLED +* **printermonitor.ino.d1_mini_repetier_SSD1306.bin** - Repetier version compiled for Wemos D1 Mini for the smaller 0.96" SSD1306 OLED (default) +* **printermonitor.ino.d1_mini_repetier_SH1106.bin** - Repetier version compiled for Wemos D1 Mini for the larger 1.3" SH1106 OLED +* **printermonitor.ino.d1_mini_easyboard.bin** - Version compiled for Easy Monitor Board for the smaller 0.96" SSD1306 OLED (SDA -> D2 and SCL -> D1) +* **printermonitor.ino.d1_mini_easyboard_repetier.bin** - Repetier version compiled for Easy Monitor Board for the smaller 0.96" SSD1306 OLED (SDA -> D2 and SCL -> D1) ## Compiling and Loading to Wemos D1 Mini It is recommended to use Arduino IDE. You will need to configure Arduino IDE to work with the Wemos board and USB port and installed the required USB drivers etc. @@ -92,7 +83,9 @@ Note Printer-Monitor version 2.5 and later include ArduinoJson (version 5.13.1). ## Initial Configuration All settings may be managed from the Web Interface, however, you may update the **Settings.h** file manually -- but it is not required. There is also an option to display current weather when the print is off-line. -* Your OctoPrint API Key from your OctoPrint -> User Settings -> Current API Key +* If you are using the Easy Monitor Board you must set the const int SCL_PIN = D1 in the Settings.h file. +* By default OctoPrint client is selected. If you wish to use Repetier then uncomment //#define USE_REPETIER_CLIENT in the Settings.h file. +* Your OctoPrint API Key from your OctoPrint -> User Settings -> Current API Key -- similar for Repetier API Key. * Optional OpenWeatherMap API Key -- if you want current weather when not printing. Get the api key from: https://openweathermap.org/ NOTE: The settings in the Settings.h are the default settings for the first loading. After loading you will manage changes to the settings via the Web Interface. If you want to change settings again in the settings.h, you will need to erase the file system on the Wemos or use the “Reset Settings” option in the Web Interface. @@ -133,3 +126,26 @@ Thanks for your contribution. ![Printer Monitor Temps](/images/temperatures.jpg) ![Printer Monitor Time Remaining](/images/time_remaining.jpg) ![Printer Monitor Printing Time](/images/printing_time.jpg) + +/* The MIT License (MIT) + +Copyright (c) 2018 David Payne + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ \ No newline at end of file diff --git a/images/Thumbs.db b/images/Thumbs.db index 3e48389..f1c7bad 100644 Binary files a/images/Thumbs.db and b/images/Thumbs.db differ diff --git a/printermonitor.ino.d1_mini_SH1106-2.5.bin b/printermonitor.ino.d1_mini_SH1106-2.5.bin deleted file mode 100644 index d03871c..0000000 Binary files a/printermonitor.ino.d1_mini_SH1106-2.5.bin and /dev/null differ diff --git a/printermonitor.ino.d1_mini_SH1106.bin b/printermonitor.ino.d1_mini_SH1106.bin new file mode 100644 index 0000000..d15595b Binary files /dev/null and b/printermonitor.ino.d1_mini_SH1106.bin differ diff --git a/printermonitor.ino.d1_mini_SSD1306-2.5.bin b/printermonitor.ino.d1_mini_SSD1306-2.5.bin deleted file mode 100644 index a3ba1e5..0000000 Binary files a/printermonitor.ino.d1_mini_SSD1306-2.5.bin and /dev/null differ diff --git a/printermonitor.ino.d1_mini_SSD1306.bin b/printermonitor.ino.d1_mini_SSD1306.bin new file mode 100644 index 0000000..d5d8a07 Binary files /dev/null and b/printermonitor.ino.d1_mini_SSD1306.bin differ diff --git a/printermonitor.ino.d1_mini_easyboard.bin b/printermonitor.ino.d1_mini_easyboard.bin new file mode 100644 index 0000000..740caa0 Binary files /dev/null and b/printermonitor.ino.d1_mini_easyboard.bin differ diff --git a/printermonitor.ino.d1_mini_easyboard_repetier.bin b/printermonitor.ino.d1_mini_easyboard_repetier.bin new file mode 100644 index 0000000..79b7009 Binary files /dev/null and b/printermonitor.ino.d1_mini_easyboard_repetier.bin differ diff --git a/printermonitor.ino.d1_mini_repetier_SH1106.bin b/printermonitor.ino.d1_mini_repetier_SH1106.bin new file mode 100644 index 0000000..1eab3c0 Binary files /dev/null and b/printermonitor.ino.d1_mini_repetier_SH1106.bin differ diff --git a/printermonitor.ino.d1_mini_repetier_SSD1306.bin b/printermonitor.ino.d1_mini_repetier_SSD1306.bin new file mode 100644 index 0000000..f408718 Binary files /dev/null and b/printermonitor.ino.d1_mini_repetier_SSD1306.bin differ diff --git a/printermonitor/OctoPrintClient.cpp b/printermonitor/OctoPrintClient.cpp index 7d19d2c..832835b 100644 --- a/printermonitor/OctoPrintClient.cpp +++ b/printermonitor/OctoPrintClient.cpp @@ -27,10 +27,10 @@ SOFTWARE. #include "OctoPrintClient.h" OctoPrintClient::OctoPrintClient(String ApiKey, String server, int port, String user, String pass, boolean psu) { - updateOctoPrintClient(ApiKey, server, port, user, pass, psu); + updatePrintClient(ApiKey, server, port, user, pass, psu); } -void OctoPrintClient::updateOctoPrintClient(String ApiKey, String server, int port, String user, String pass, boolean psu) { +void OctoPrintClient::updatePrintClient(String ApiKey, String server, int port, String user, String pass, boolean psu) { server.toCharArray(myServer, 100); myApiKey = ApiKey; myPort = port; @@ -236,6 +236,8 @@ void OctoPrintClient::getPrinterJobResults() { String printing = (const char*)root2["state"]["flags"]["printing"]; if (printing == "true") { printerData.isPrinting = true; + } else { + printerData.isPrinting = false; } printerData.toolTemp = (const char*)root2["temperature"]["tool0"]["actual"]; printerData.toolTargetTemp = (const char*)root2["temperature"]["tool0"]["target"]; @@ -394,3 +396,19 @@ String OctoPrintClient::getValueRounded(String value) { int rounded = (int)(f+0.5f); return String(rounded); } + +String OctoPrintClient::getPrinterType() { + return printerType; +} + +int OctoPrintClient::getPrinterPort() { + return myPort; +} + +String OctoPrintClient::getPrinterName() { + return printerData.printerName; +} + +void OctoPrintClient::setPrinterName(String printer) { + printerData.printerName = printer; +} diff --git a/printermonitor/OctoPrintClient.h b/printermonitor/OctoPrintClient.h index d8d7b8d..1761ccc 100644 --- a/printermonitor/OctoPrintClient.h +++ b/printermonitor/OctoPrintClient.h @@ -37,6 +37,7 @@ private: String myApiKey = ""; String encodedAuth = ""; boolean pollPsu; + const String printerType = "OctoPrint"; void resetPrintData(); boolean validate(); @@ -64,6 +65,7 @@ private: boolean isPrinting; boolean isPSUoff; String error; + String printerName; } PrinterStruct; PrinterStruct printerData; @@ -73,7 +75,7 @@ public: OctoPrintClient(String ApiKey, String server, int port, String user, String pass, boolean psu); void getPrinterJobResults(); void getPrinterPsuState(); - void updateOctoPrintClient(String ApiKey, String server, int port, String user, String pass, boolean psu); + void updatePrintClient(String ApiKey, String server, int port, String user, String pass, boolean psu); String getAveragePrintTime(); String getEstimatedPrintTime(); @@ -95,4 +97,8 @@ public: String getFilamentLength(); String getValueRounded(String value); String getError(); + String getPrinterType(); + int getPrinterPort(); + String getPrinterName(); + void setPrinterName(String printer); }; diff --git a/printermonitor/RepetierClient.cpp b/printermonitor/RepetierClient.cpp new file mode 100644 index 0000000..e547916 --- /dev/null +++ b/printermonitor/RepetierClient.cpp @@ -0,0 +1,367 @@ +/** The MIT License (MIT) + +Copyright (c) 2018 David Payne + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Additional Contributions: +/* 15 Jan 2019 : Owen Carter : Add psucontrol query via POST api call */ +/* 07 April 2019 : Jon Smith : Redesigned this class for Repetier Server */ + +#include "RepetierClient.h" + +RepetierClient::RepetierClient(String ApiKey, String server, int port, String user, String pass, boolean psu) { + updatePrintClient(ApiKey, server, port, user, pass, psu); +} + +void RepetierClient::updatePrintClient(String ApiKey, String server, int port, String user, String pass, boolean psu) { + server.toCharArray(myServer, 100); + myApiKey = ApiKey; + myPort = port; + encodedAuth = ""; + if (user != "") { + String userpass = user + ":" + pass; + base64 b64; + encodedAuth = b64.encode(userpass, true); + } + pollPsu = psu; +} + +boolean RepetierClient::validate() { + boolean rtnValue = false; + printerData.error = ""; + if (String(myServer) == "") { + printerData.error += "Server address is required; "; + } + if (myApiKey == "") { + printerData.error += "ApiKey is required; "; + } + if (printerData.error == "") { + rtnValue = true; + } + return rtnValue; +} + +WiFiClient RepetierClient::getSubmitRequest(String apiGetData) { + WiFiClient printClient; + printClient.setTimeout(5000); + + Serial.println("Getting Repetier Data via GET"); + Serial.println(apiGetData); + result = ""; + if (printClient.connect(myServer, myPort)) { //starts client connection, checks for connection + printClient.println(apiGetData); + printClient.println("Host: " + String(myServer) + ":" + String(myPort)); + printClient.println("X-Api-Key: " + myApiKey); + if (encodedAuth != "") { + printClient.print("Authorization: "); + printClient.println("Basic " + encodedAuth); + } + printClient.println("User-Agent: ArduinoWiFi/1.1"); + printClient.println("Connection: close"); + if (printClient.println() == 0) { + Serial.println("Connection to " + String(myServer) + ":" + String(myPort) + " failed."); + Serial.println(); + resetPrintData(); + printerData.error = "Connection to " + String(myServer) + ":" + String(myPort) + " failed."; + return printClient; + } + } + else { + Serial.println("Connection to Repetier failed: " + String(myServer) + ":" + String(myPort)); //error message if no client connect + Serial.println(); + resetPrintData(); + printerData.error = "Connection to Repetier failed: " + String(myServer) + ":" + String(myPort); + return printClient; + } +/* + // Check HTTP status + char status[32] = {0}; + printClient.readBytesUntil('\r', status, sizeof(status)); + if (strcmp(status, "HTTP/1.1 200 OK") != 0) { + Serial.print(F("Unexpected response: ")); + Serial.println(status); + printerData.state = ""; + printerData.error = "Response: " + String(status); + return printClient; + } + + // Skip HTTP headers + char endOfHeaders[] = "\r\n\r\n"; + if (!printClient.find(endOfHeaders)) { + Serial.println(F("Invalid response")); + printerData.error = "Invalid response from " + String(myServer) + ":" + String(myPort); + printerData.state = ""; + } +*/ + return printClient; +} + + +void RepetierClient::getPrinterJobResults() { + if (!validate()) { + return; + } + //**** get the Printer Job status + String apiGetData = "GET /printer/api/?a=listPrinter&apikey=" + myApiKey; + WiFiClient printClient = getSubmitRequest(apiGetData); + if (printerData.error != "") { + return; + } + const size_t bufferSize = JSON_OBJECT_SIZE(1) + JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3) + 2*JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(6) + 710; + DynamicJsonBuffer jsonBuffer(bufferSize); + + // Parse JSON object + JsonArray& root = jsonBuffer.parseArray(printClient); + + if (!root.success()) { + printerData.error = "Repetier Data Parsing failed: " + String(myServer) + ":" + String(myPort); + Serial.println(printerData.error); + printerData.state = ""; + return; + } + + int inx = 0; + int count = root.size(); + Serial.println("Size of root: " + String(count)); + for (int i = 0; i < count; i++) { + Serial.println("Printer: " + String((const char*)root[i]["slug"])); + if (String((const char*)root[i]["slug"]) == printerData.printerName) { + inx = i; + break; + } + } + + JsonObject& pr = root[inx]; + + //printerData.averagePrintTime = (const char*)pr[""]; + printerData.estimatedPrintTime = (const char*)pr["printTime"]; + printerData.fileName = (const char*) pr["job"]; + printerData.fileSize = (const char*) pr["totalLines"]; + //printerData.filamentLength = (const char*) pr[""]; + printerData.state = (const char*) pr["online"]; + //printerData.lastPrintTime = (const char*) pr[""]; + printerData.progressCompletion = (const char*) pr["done"]; + printerData.progressFilepos = (const char*) pr["linesSend"]; + printerData.progressPrintTime = (const char*) pr["printedTimeComp"]; + +//Figure out Time Left + long timeTot=0; + long timeElap=0; + long timeLeft=0; + if (printerData.estimatedPrintTime != "" ) { + timeTot = atol(pr["printTime"]); + } + if (printerData.progressPrintTime != "") { + timeElap= atol(pr["printedTimeComp"]); + } + timeLeft = timeTot-timeElap; + printerData.progressPrintTimeLeft = String(timeLeft); + + if (printerData.fileName != "none") { + printerData.isPrinting = true; + } else { + printerData.isPrinting = false; + } + + if (printerData.isPrinting) { + Serial.println("Printing: " + printerData.fileName); + } + + if (isOperational()) { + Serial.println("Status: " + printerData.state); + } else { + Serial.println("Printer Not Operational"); + } + + //**** get the Printer Temps and Stat + apiGetData = "GET /printer/api/?a=stateList&apikey=" + myApiKey; + printClient = getSubmitRequest(apiGetData); + if (printerData.error != "") { + return; + } + const size_t bufferSize2 = 3*JSON_OBJECT_SIZE(2) + 2*JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(9) + 300; + DynamicJsonBuffer jsonBuffer2(bufferSize2); + + //Parse JSON object + JsonObject& root2 = jsonBuffer2.parseObject(printClient); + + //Select printer + JsonObject& pr2 = root2[printerData.printerName]; + + if (!root2.success()) { + printerData.isPrinting = false; + printerData.toolTemp = ""; + printerData.toolTargetTemp = ""; + printerData.bedTemp = ""; + printerData.bedTargetTemp = ""; + return; + } + + printerData.toolTemp = (const char*) pr2["extruder"][0]["tempRead"]; + printerData.toolTargetTemp = (const char*) pr2["extruder"][0]["tempSet"]; + printerData.bedTemp = (const char*) pr2["heatedBeds"][0]["tempRead"]; + printerData.bedTargetTemp = (const char*) pr2["heatedBeds"][0]["tempSet"]; + + if (printerData.isPrinting) { + Serial.println("Status: " + printerData.state + " " + printerData.fileName + "(" + printerData.progressCompletion + "%)"); + } +} + +void RepetierClient::getPrinterPsuState() { + //**** get the PSU state (if enabled and printer operational) + //Not implemented in Repetier Server AFAIK +} + +// Reset all PrinterData +void RepetierClient::resetPrintData() { + printerData.averagePrintTime = ""; + printerData.estimatedPrintTime = ""; + printerData.fileName = ""; + printerData.fileSize = ""; + printerData.lastPrintTime = ""; + printerData.progressCompletion = ""; + printerData.progressFilepos = ""; + printerData.progressPrintTime = ""; + printerData.progressPrintTimeLeft = ""; + printerData.state = ""; + printerData.toolTemp = ""; + printerData.toolTargetTemp = ""; + printerData.filamentLength = ""; + printerData.bedTemp = ""; + printerData.bedTargetTemp = ""; + printerData.isPrinting = false; + printerData.isPSUoff = false; + printerData.error = ""; +} + +String RepetierClient::getAveragePrintTime(){ + return printerData.averagePrintTime; +} + +String RepetierClient::getEstimatedPrintTime() { + return printerData.estimatedPrintTime; +} + +String RepetierClient::getFileName() { + return printerData.fileName; +} + +String RepetierClient::getFileSize() { + return printerData.fileSize; +} + +String RepetierClient::getLastPrintTime(){ + return printerData.lastPrintTime; +} + +String RepetierClient::getProgressCompletion() { + return String(printerData.progressCompletion.toInt()); +} + +String RepetierClient::getProgressFilepos() { + return printerData.progressFilepos; +} + +String RepetierClient::getProgressPrintTime() { + return printerData.progressPrintTime; +} + +String RepetierClient::getProgressPrintTimeLeft() { + String rtnValue = printerData.progressPrintTimeLeft; + if (getProgressCompletion() == "100") { + rtnValue = "0"; // Print is done so this should be 0 this is a fix for OctoPrint + } + return rtnValue; +} + +String RepetierClient::getState() { + String rtnValue = "Offline"; + if (printerData.state == "1") { + rtnValue = "Operational"; + } + return rtnValue; +} + +boolean RepetierClient::isPrinting() { + return printerData.isPrinting; +} + +boolean RepetierClient::isPSUoff() { + return printerData.isPSUoff; +} + +boolean RepetierClient::isOperational() { + boolean operational = false; + if (printerData.state == "1" || isPrinting()) { + operational = true; + } + return operational; +} + +String RepetierClient::getTempBedActual() { + String temp = printerData.bedTemp; + temp.remove(temp.indexOf(".") + 3); + return temp; +} + +String RepetierClient::getTempBedTarget() { + return printerData.bedTargetTemp; +} + +String RepetierClient::getTempToolActual() { + String temp = printerData.toolTemp; + temp.remove(temp.indexOf(".") + 3); + return temp; +} + +String RepetierClient::getTempToolTarget() { + return printerData.toolTargetTemp; +} + +String RepetierClient::getFilamentLength() { + return printerData.filamentLength; +} + +String RepetierClient::getError() { + return printerData.error; +} + +String RepetierClient::getValueRounded(String value) { + float f = value.toFloat(); + int rounded = (int)(f+0.5f); + return String(rounded); +} + +String RepetierClient::getPrinterType() { + return printerType; +} + +int RepetierClient::getPrinterPort() { + return myPort; +} + +String RepetierClient::getPrinterName() { + return printerData.printerName; +} + +void RepetierClient::setPrinterName(String printer) { + printerData.printerName = printer; +} diff --git a/printermonitor/RepetierClient.h b/printermonitor/RepetierClient.h new file mode 100644 index 0000000..7c92105 --- /dev/null +++ b/printermonitor/RepetierClient.h @@ -0,0 +1,104 @@ +/** The MIT License (MIT) + +Copyright (c) 2018 David Payne + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/* 07 April 2019 : Jon Smith : added class for Repetier Server (kg4iae@github)*/ + + +#pragma once +#include +#include "libs/ArduinoJson/ArduinoJson.h" +#include + +class RepetierClient { + +private: + char myServer[100]; + int myPort = 3344; + String myApiKey = ""; + String encodedAuth = ""; + boolean pollPsu; + const String printerType = "Repetier"; + + void resetPrintData(); + boolean validate(); + WiFiClient getSubmitRequest(String apiGetData); + WiFiClient getPostRequest(String apiPostData, String apiPostBody); + + String result; + + typedef struct { + String averagePrintTime; + String estimatedPrintTime; + String fileName; + String fileSize; + String lastPrintTime; + String progressCompletion; + String progressFilepos; + String progressPrintTime; + String progressPrintTimeLeft; + String state; + String toolTemp; + String toolTargetTemp; + String filamentLength; + String bedTemp; + String bedTargetTemp; + boolean isPrinting; + boolean isPSUoff; + String error; + String printerName; + } PrinterStruct; + + PrinterStruct printerData; + + +public: + RepetierClient(String ApiKey, String server, int port, String user, String pass, boolean psu); + void getPrinterJobResults(); + void getPrinterPsuState(); + void updatePrintClient(String ApiKey, String server, int port, String user, String pass, boolean psu); + + String getAveragePrintTime(); + String getEstimatedPrintTime(); + String getFileName(); + String getFileSize(); + String getLastPrintTime(); + String getProgressCompletion(); + String getProgressFilepos(); + String getProgressPrintTime(); + String getProgressPrintTimeLeft(); + String getState(); + boolean isPrinting(); + boolean isOperational(); + boolean isPSUoff(); + String getTempBedActual(); + String getTempBedTarget(); + String getTempToolActual(); + String getTempToolTarget(); + String getFilamentLength(); + String getValueRounded(String value); + String getError(); + String getPrinterType(); + int getPrinterPort(); + String getPrinterName(); + void setPrinterName(String printer); +}; diff --git a/printermonitor/Settings.h b/printermonitor/Settings.h index d478fec..a149b82 100644 --- a/printermonitor/Settings.h +++ b/printermonitor/Settings.h @@ -45,6 +45,7 @@ SOFTWARE. #include #include #include "TimeClient.h" +#include "RepetierClient.h" #include "OctoPrintClient.h" #include "OpenWeatherMapClient.h" #include "WeatherStationFonts.h" @@ -57,13 +58,14 @@ SOFTWARE. // Start Settings //****************************** -// OctoPrint Monitoring -- Monitor your 3D printer OctoPrint Server -String OctoPrintApiKey = ""; // ApiKey from your User Account on OctoPrint -String OctoPrintHostName = "octopi";// Default 'octopi' -- or hostname if different (optional if your IP changes) -String OctoPrintServer = ""; // IP or Address of your OctoPrint Server (DO NOT include http://) -int OctoPrintPort = 80; // the port you are running your OctoPrint server on (usually 80); -String OctoAuthUser = ""; // only used if you have haproxy or basic athentintication turned on (not default) -String OctoAuthPass = ""; // only used with haproxy or basic auth (only needed if you must authenticate) +// OctoPrint / Repetier Monitoring -- Monitor your 3D OctoPrint or Repetier Server +//#define USE_REPETIER_CLIENT // Uncomment this line to use the Repetier Printer Server -- OctoPrint is used by default and is most common +String PrinterApiKey = ""; // ApiKey from your User Account on OctoPrint / Repetier +String PrinterHostName = "octopi";// Default 'octopi' -- or hostname if different (optional if your IP changes) +String PrinterServer = ""; // IP or Address of your OctoPrint / Repetier Server (DO NOT include http://) +int PrinterPort = 80; // the port you are running your OctoPrint / Repetier server on (usually 80); +String PrinterAuthUser = ""; // only used if you have haproxy or basic athentintication turned on (not default) +String PrinterAuthPass = ""; // only used with haproxy or basic auth (only needed if you must authenticate) // Weather Configuration boolean DISPLAYWEATHER = true; // true = show weather when not printing / false = no weather @@ -90,12 +92,13 @@ boolean DISPLAYCLOCK = true; // true = Show Clock when not printing / false = // Display Settings const int I2C_DISPLAY_ADDRESS = 0x3c; // I2C Address of your Display (usually 0x3c or 0x3d) const int SDA_PIN = D2; -const int SCL_PIN = D5; +const int SCL_PIN = D5; // original code D5 -- Monitor Easy Board use D1 boolean INVERT_DISPLAY = false; // true = pins at top | false = pins at the bottom //#define DISPLAY_SH1106 // Uncomment this line to use the SH1106 display -- SSD1306 is used by default and is most common // LED Settings -const int externalLight = LED_BUILTIN; // Set to unused pin, like D1, to disable use of built-in LED (LED_BUILTIN) +const int externalLight = LED_BUILTIN; // LED will always flash on bootup or Wifi Errors +boolean USE_FLASH = true; // true = System LED will Flash on Service Calls; false = disabled LED flashing // PSU Control boolean HAS_PSU = false; // Set to true if https://github.com/kantlivelong/OctoPrint-PSUControl/ in use diff --git a/printermonitor/printermonitor.ino b/printermonitor/printermonitor.ino index 5fea51d..8849850 100644 --- a/printermonitor/printermonitor.ino +++ b/printermonitor/printermonitor.ino @@ -1,1260 +1,1362 @@ -/** The MIT License (MIT) - -Copyright (c) 2018 David Payne - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -// Additional Contributions: -/* 15 Jan 2019 : Owen Carter : Add psucontrol option and processing */ - - /********************************************** - * Edit Settings.h for personalization - ***********************************************/ - -#include "Settings.h" - -#define VERSION "2.5" - -#define HOSTNAME "OctMon-" -#define CONFIG "/conf.txt" - -/* Useful Constants */ -#define SECS_PER_MIN (60UL) -#define SECS_PER_HOUR (3600UL) - -/* Useful Macros for getting elapsed time */ -#define numberOfSeconds(_time_) (_time_ % SECS_PER_MIN) -#define numberOfMinutes(_time_) ((_time_ / SECS_PER_MIN) % SECS_PER_MIN) -#define numberOfHours(_time_) (_time_ / SECS_PER_HOUR) - -// Initialize the oled display for I2C_DISPLAY_ADDRESS -// SDA_PIN and SCL_PIN -#if defined(DISPLAY_SH1106) - SH1106Wire display(I2C_DISPLAY_ADDRESS, SDA_PIN, SCL_PIN); -#else - SSD1306Wire display(I2C_DISPLAY_ADDRESS, SDA_PIN, SCL_PIN); // this is the default -#endif - -OLEDDisplayUi ui( &display ); - -void drawProgress(OLEDDisplay *display, int percentage, String label); -void drawOtaProgress(unsigned int, unsigned int); -void drawScreen1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y); -void drawScreen2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y); -void drawScreen3(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y); -void drawHeaderOverlay(OLEDDisplay *display, OLEDDisplayUiState* state); -void drawClock(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y); -void drawWeather(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y); -void drawClockHeaderOverlay(OLEDDisplay *display, OLEDDisplayUiState* state); - -// Set the number of Frames supported -const int numberOfFrames = 3; -FrameCallback frames[numberOfFrames]; -FrameCallback clockFrame[2]; -boolean isClockOn = false; - -OverlayCallback overlays[] = { drawHeaderOverlay }; -OverlayCallback clockOverlay[] = { drawClockHeaderOverlay }; -int numberOfOverlays = 1; - -// Time -TimeClient timeClient(UtcOffset); -long lastEpoch = 0; -long firstEpoch = 0; -long displayOffEpoch = 0; -String lastMinute = "xx"; -String lastSecond = "xx"; -String lastReportStatus = ""; -boolean displayOn = true; - -// OctoPrint Client -OctoPrintClient printerClient(OctoPrintApiKey, OctoPrintServer, OctoPrintPort, OctoAuthUser, OctoAuthPass, HAS_PSU); -int printerCount = 0; - -// Weather Client -OpenWeatherMapClient weatherClient(WeatherApiKey, CityIDs, 1, IS_METRIC, WeatherLanguage); - -//declairing prototypes -void configModeCallback (WiFiManager *myWiFiManager); -int8_t getWifiQuality(); - -ESP8266WebServer server(WEBSERVER_PORT); -ESP8266HTTPUpdateServer serverUpdater; - -String WEB_ACTIONS = " Home" - " Configure" - " Weather" - " Reset Settings" - " Forget WiFi" - " Firmware Update" - " About"; - -String CHANGE_FORM = "

Station Config:

" - "

" - "

" - "

" - "

" - "

" - "


" - "

Display Clock when printer is off

" - "

Use 24 Hour Clock (military time)

" - "

Flip display orientation

" - "

Use OctoPrint PSU control plugin for clock/blank

" - "

Clock Sync / Weather Refresh (minutes)

"; - -String THEME_FORM = "

Theme Color

" - "


" - "

Use Security Credentials for Configuration Changes

" - "

" - "

" - "
"; - -String WEATHER_FORM = "

Weather Config:

" - "

Display Weather when printer is off

" - "" - "" - "

" - "

" - "

Use Metric (Celsius)

" - "

Weather Language

" - "
" - ""; - -String LANG_OPTIONS = "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - ""; - -String COLOR_THEMES = "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - ""; - - -void setup() { - Serial.begin(115200); - SPIFFS.begin(); - delay(10); - - //New Line to clear from start garbage - Serial.println(); - - // Initialize digital pin for LED (little blue light on the Wemos D1 Mini) - pinMode(externalLight, OUTPUT); - - readSettings(); - - // initialize display - display.init(); - if (INVERT_DISPLAY) { - display.flipScreenVertically(); // connections at top of OLED display - } - display.clear(); - display.display(); - - //display.flipScreenVertically(); - display.setFont(ArialMT_Plain_16); - display.setTextAlignment(TEXT_ALIGN_CENTER); - display.setContrast(255); // default is 255 - display.drawString(64, 5, "Printer Monitor\nBy Qrome\nV" + String(VERSION)); - display.display(); - - //WiFiManager - //Local intialization. Once its business is done, there is no need to keep it around - WiFiManager wifiManager; - - // Uncomment for testing wifi manager - //wifiManager.resetSettings(); - wifiManager.setAPCallback(configModeCallback); - - String hostname(HOSTNAME); - hostname += String(ESP.getChipId(), HEX); - if (!wifiManager.autoConnect((const char *)hostname.c_str())) {// new addition - delay(3000); - WiFi.disconnect(true); - ESP.reset(); - delay(5000); - } - - // You can change the transition that is used - // SLIDE_LEFT, SLIDE_RIGHT, SLIDE_TOP, SLIDE_DOWN - ui.setFrameAnimation(SLIDE_LEFT); - ui.setTargetFPS(30); - ui.disableAllIndicators(); - ui.setFrames(frames, (numberOfFrames)); - frames[0] = drawScreen1; - frames[1] = drawScreen2; - frames[2] = drawScreen3; - clockFrame[0] = drawClock; - clockFrame[1] = drawWeather; - ui.setOverlays(overlays, numberOfOverlays); - - // Inital UI takes care of initalising the display too. - ui.init(); - if (INVERT_DISPLAY) { - display.flipScreenVertically(); //connections at top of OLED display - } - - // print the received signal strength: - Serial.print("Signal Strength (RSSI): "); - Serial.print(getWifiQuality()); - Serial.println("%"); - - if (ENABLE_OTA) { - ArduinoOTA.onStart([]() { - Serial.println("Start"); - }); - ArduinoOTA.onEnd([]() { - Serial.println("\nEnd"); - }); - ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { - Serial.printf("Progress: %u%%\r", (progress / (total / 100))); - }); - ArduinoOTA.onError([](ota_error_t error) { - Serial.printf("Error[%u]: ", error); - if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); - else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); - else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); - else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); - else if (error == OTA_END_ERROR) Serial.println("End Failed"); - }); - ArduinoOTA.setHostname((const char *)hostname.c_str()); - if (OTA_Password != "") { - ArduinoOTA.setPassword(((const char *)OTA_Password.c_str())); - } - ArduinoOTA.begin(); - } - - if (WEBSERVER_ENABLED) { - server.on("/", displayPrinterStatus); - server.on("/systemreset", handleSystemReset); - server.on("/forgetwifi", handleWifiReset); - server.on("/updateconfig", handleUpdateConfig); - server.on("/updateweatherconfig", handleUpdateWeather); - server.on("/configure", handleConfigure); - server.on("/configureweather", handleWeatherConfigure); - server.onNotFound(redirectHome); - serverUpdater.setup(&server, "/update", www_username, www_password); - // Start the server - server.begin(); - Serial.println("Server started"); - // Print the IP address - String webAddress = "http://" + WiFi.localIP().toString() + ":" + String(WEBSERVER_PORT) + "/"; - Serial.println("Use this URL : " + webAddress); - display.clear(); - display.setTextAlignment(TEXT_ALIGN_CENTER); - display.setFont(ArialMT_Plain_10); - display.drawString(64, 10, "Web Interface On"); - display.drawString(64, 20, "You May Connect to IP"); - display.setFont(ArialMT_Plain_16); - display.drawString(64, 30, WiFi.localIP().toString()); - display.drawString(64, 46, "Port: " + String(WEBSERVER_PORT)); - display.display(); - } else { - Serial.println("Web Interface is Disabled"); - display.clear(); - display.setTextAlignment(TEXT_ALIGN_CENTER); - display.setFont(ArialMT_Plain_10); - display.drawString(64, 10, "Web Interface is Off"); - display.drawString(64, 20, "Enable in Settings.h"); - display.display(); - } - flashLED(5, 500); - findMDNS(); //go find Octoprint Server by the hostname - Serial.println("*** Leaving setup()"); -} - -void findMDNS() { - if (OctoPrintHostName == "" || ENABLE_OTA == false) { - return; // nothing to do here - } - // We now query our network for 'web servers' service - // over tcp, and get the number of available devices - int n = MDNS.queryService("http", "tcp"); - if (n == 0) { - Serial.println("no services found - make sure OctoPrint server is turned on"); - return; - } - Serial.println("*** Looking for " + OctoPrintHostName + " over mDNS"); - for (int i = 0; i < n; ++i) { - // Going through every available service, - // we're searching for the one whose hostname - // matches what we want, and then get its IP - Serial.println("Found: " + MDNS.hostname(i)); - if (MDNS.hostname(i) == OctoPrintHostName) { - IPAddress serverIp = MDNS.IP(i); - OctoPrintServer = serverIp.toString(); - OctoPrintPort = MDNS.port(i); // save the port - Serial.println("*** Found OctoPrint Server " + OctoPrintHostName + " http://" + OctoPrintServer + ":" + OctoPrintPort); - writeSettings(); // update the settings - } - } -} - -//************************************************************ -// Main Looop -//************************************************************ -void loop() { - - //Get Time Update - if((getMinutesFromLastRefresh() >= minutesBetweenDataRefresh) || lastEpoch == 0) { - getUpdateTime(); - } - - if (lastMinute != timeClient.getMinutes() && !printerClient.isPrinting()) { - // Check status every 60 seconds - digitalWrite(externalLight, LOW); - lastMinute = timeClient.getMinutes(); // reset the check value - printerClient.getPrinterJobResults(); - printerClient.getPrinterPsuState(); - digitalWrite(externalLight, HIGH); - } else if (printerClient.isPrinting()) { - if (lastSecond != timeClient.getSeconds() && timeClient.getSeconds().endsWith("0")) { - lastSecond = timeClient.getSeconds(); - // every 10 seconds while printing get an update - digitalWrite(externalLight, LOW); - printerClient.getPrinterJobResults(); - printerClient.getPrinterPsuState(); - digitalWrite(externalLight, HIGH); - } - } - - checkDisplay(); // Check to see if the printer is on or offline and change display. - - ui.update(); - - if (WEBSERVER_ENABLED) { - server.handleClient(); - } - if (ENABLE_OTA) { - ArduinoOTA.handle(); - } -} - -void getUpdateTime() { - digitalWrite(externalLight, LOW); // turn on the LED - Serial.println(); - - if (displayOn && DISPLAYWEATHER) { - Serial.println("Getting Weather Data..."); - weatherClient.updateWeather(); - } - - Serial.println("Updating Time..."); - //Update the Time - timeClient.updateTime(); - lastEpoch = timeClient.getCurrentEpoch(); - Serial.println("Local time: " + timeClient.getAmPmFormattedTime()); - - digitalWrite(externalLight, HIGH); // turn off the LED -} - -boolean authentication() { - if (IS_BASIC_AUTH && (strlen(www_username) >= 1 && strlen(www_password) >= 1)) { - return server.authenticate(www_username, www_password); - } - return true; // Authentication not required -} - -void handleSystemReset() { - if (!authentication()) { - return server.requestAuthentication(); - } - Serial.println("Reset System Configuration"); - if (SPIFFS.remove(CONFIG)) { - redirectHome(); - ESP.restart(); - } -} - -void handleUpdateWeather() { - if (!authentication()) { - return server.requestAuthentication(); - } - DISPLAYWEATHER = server.hasArg("isWeatherEnabled"); - WeatherApiKey = server.arg("openWeatherMapApiKey"); - CityIDs[0] = server.arg("city1").toInt(); - IS_METRIC = server.hasArg("metric"); - WeatherLanguage = server.arg("language"); - writeSettings(); - isClockOn = false; // this will force a check for the display - checkDisplay(); - lastEpoch = 0; - redirectHome(); -} - -void handleUpdateConfig() { - boolean flipOld = INVERT_DISPLAY; - if (!authentication()) { - return server.requestAuthentication(); - } - OctoPrintApiKey = server.arg("octoPrintApiKey"); - OctoPrintHostName = server.arg("octoPrintHostName"); - OctoPrintServer = server.arg("octoPrintAddress"); - OctoPrintPort = server.arg("octoPrintPort").toInt(); - OctoAuthUser = server.arg("octoUser"); - OctoAuthPass = server.arg("octoPass"); - DISPLAYCLOCK = server.hasArg("isClockEnabled"); - IS_24HOUR = server.hasArg("is24hour"); - INVERT_DISPLAY = server.hasArg("invDisp"); - HAS_PSU = server.hasArg("hasPSU"); - minutesBetweenDataRefresh = server.arg("refresh").toInt(); - themeColor = server.arg("theme"); - UtcOffset = server.arg("utcoffset").toFloat(); - String temp = server.arg("userid"); - temp.toCharArray(www_username, sizeof(temp)); - temp = server.arg("stationpassword"); - temp.toCharArray(www_password, sizeof(temp)); - writeSettings(); - findMDNS(); - printerClient.getPrinterJobResults(); - printerClient.getPrinterPsuState(); - if (INVERT_DISPLAY != flipOld) { - ui.init(); - if(INVERT_DISPLAY) - display.flipScreenVertically(); - ui.update(); - } - checkDisplay(); - lastEpoch = 0; - redirectHome(); -} - -void handleWifiReset() { - if (!authentication()) { - return server.requestAuthentication(); - } - //WiFiManager - //Local intialization. Once its business is done, there is no need to keep it around - redirectHome(); - WiFiManager wifiManager; - wifiManager.resetSettings(); - ESP.restart(); -} - -void handleWeatherConfigure() { - if (!authentication()) { - return server.requestAuthentication(); - } - digitalWrite(externalLight, LOW); - String html = ""; - - server.sendHeader("Cache-Control", "no-cache, no-store"); - server.sendHeader("Pragma", "no-cache"); - server.sendHeader("Expires", "-1"); - server.setContentLength(CONTENT_LENGTH_UNKNOWN); - server.send(200, "text/html", ""); - - html = getHeader(); - server.sendContent(html); - - String form = WEATHER_FORM; - String isWeatherChecked = ""; - if (DISPLAYWEATHER) { - isWeatherChecked = "checked='checked'"; - } - form.replace("%IS_WEATHER_CHECKED%", isWeatherChecked); - form.replace("%WEATHERKEY%", WeatherApiKey); - form.replace("%CITYNAME1%", weatherClient.getCity(0)); - form.replace("%CITY1%", String(CityIDs[0])); - String checked = ""; - if (IS_METRIC) { - checked = "checked='checked'"; - } - form.replace("%METRIC%", checked); - String options = LANG_OPTIONS; - options.replace(">"+String(WeatherLanguage)+"<", " selected>"+String(WeatherLanguage)+"<"); - form.replace("%LANGUAGEOPTIONS%", options); - server.sendContent(form); - - html = getFooter(); - server.sendContent(html); - server.sendContent(""); - server.client().stop(); - digitalWrite(externalLight, HIGH); -} - -void handleConfigure() { - if (!authentication()) { - return server.requestAuthentication(); - } - digitalWrite(externalLight, LOW); - String html = ""; - - server.sendHeader("Cache-Control", "no-cache, no-store"); - server.sendHeader("Pragma", "no-cache"); - server.sendHeader("Expires", "-1"); - server.setContentLength(CONTENT_LENGTH_UNKNOWN); - server.send(200, "text/html", ""); - - html = getHeader(); - server.sendContent(html); - - String form = CHANGE_FORM; - - form.replace("%OCTOKEY%", OctoPrintApiKey); - form.replace("%OCTOHOST%", OctoPrintHostName); - form.replace("%OCTOADDRESS%", OctoPrintServer); - form.replace("%OCTOPORT%", String(OctoPrintPort)); - form.replace("%OCTOUSER%", OctoAuthUser); - form.replace("%OCTOPASS%", OctoAuthPass); - String isClockChecked = ""; - if (DISPLAYCLOCK) { - isClockChecked = "checked='checked'"; - } - form.replace("%IS_CLOCK_CHECKED%", isClockChecked); - String is24hourChecked = ""; - if (IS_24HOUR) { - is24hourChecked = "checked='checked'"; - } - form.replace("%IS_24HOUR_CHECKED%", is24hourChecked); - String isInvDisp = ""; - if (INVERT_DISPLAY) { - isInvDisp = "checked='checked'"; - } - form.replace("%IS_INVDISP_CHECKED%", isInvDisp); - String hasPSUchecked = ""; - if (HAS_PSU) { - hasPSUchecked = "checked='checked'"; - } - form.replace("%HAS_PSU_CHECKED%", hasPSUchecked); - - String options = ""; - options.replace(">"+String(minutesBetweenDataRefresh)+"<", " selected>"+String(minutesBetweenDataRefresh)+"<"); - form.replace("%OPTIONS%", options); - - server.sendContent(form); - - form = THEME_FORM; - - String themeOptions = COLOR_THEMES; - themeOptions.replace(">"+String(themeColor)+"<", " selected>"+String(themeColor)+"<"); - form.replace("%THEME_OPTIONS%", themeOptions); - form.replace("%UTCOFFSET%", String(UtcOffset)); - String isUseSecurityChecked = ""; - if (IS_BASIC_AUTH) { - isUseSecurityChecked = "checked='checked'"; - } - form.replace("%IS_BASICAUTH_CHECKED%", isUseSecurityChecked); - form.replace("%USERID%", String(www_username)); - form.replace("%STATIONPASSWORD%", String(www_password)); - - server.sendContent(form); - - html = getFooter(); - server.sendContent(html); - server.sendContent(""); - server.client().stop(); - digitalWrite(externalLight, HIGH); -} - -void displayMessage(String message) { - digitalWrite(externalLight, LOW); - - server.sendHeader("Cache-Control", "no-cache, no-store"); - server.sendHeader("Pragma", "no-cache"); - server.sendHeader("Expires", "-1"); - server.setContentLength(CONTENT_LENGTH_UNKNOWN); - server.send(200, "text/html", ""); - String html = getHeader(); - server.sendContent(String(html)); - server.sendContent(String(message)); - html = getFooter(); - server.sendContent(String(html)); - server.sendContent(""); - server.client().stop(); - - digitalWrite(externalLight, HIGH); -} - -void redirectHome() { - // Send them back to the Root Directory - server.sendHeader("Location", String("/"), true); - server.sendHeader("Cache-Control", "no-cache, no-store"); - server.sendHeader("Pragma", "no-cache"); - server.sendHeader("Expires", "-1"); - server.send(302, "text/plain", ""); - server.client().stop(); -} - -String getHeader() { - return getHeader(false); -} - -String getHeader(boolean refresh) { - String menu = WEB_ACTIONS; - - String html = ""; - html += "Printer Monitor"; - html += ""; - html += ""; - if (refresh) { - html += ""; - } - html += ""; - html += ""; - html += ""; - html += ""; - html += ""; - html += "

Printer Monitor

"; - html += ""; - html += "
"; - return html; -} - -String getFooter() { - int8_t rssi = getWifiQuality(); - Serial.print("Signal Strength (RSSI): "); - Serial.print(rssi); - Serial.println("%"); - String html = "


"; - html += "
"; - html += "
"; - if (lastReportStatus != "") { - html += " Report Status: " + lastReportStatus + "
"; - } - html += " Version: " + String(VERSION) + "
"; - html += " Signal Strength: "; - html += String(rssi) + "%"; - html += "
"; - html += ""; - return html; -} - -void displayPrinterStatus() { - digitalWrite(externalLight, LOW); - String html = ""; - - server.sendHeader("Cache-Control", "no-cache, no-store"); - server.sendHeader("Pragma", "no-cache"); - server.sendHeader("Expires", "-1"); - server.setContentLength(CONTENT_LENGTH_UNKNOWN); - server.send(200, "text/html", ""); - server.sendContent(String(getHeader(true))); - - String displayTime = timeClient.getAmPmHours() + ":" + timeClient.getMinutes() + ":" + timeClient.getSeconds() + " " + timeClient.getAmPm(); - if (IS_24HOUR) { - displayTime = timeClient.getHours() + ":" + timeClient.getMinutes() + ":" + timeClient.getSeconds(); - } - - html += "

Time: " + displayTime + "

"; - html += "

"; - html += "Host Name: " + OctoPrintHostName + "
"; - if (printerClient.getError() != "") { - html += "Status: Offline
"; - html += "Reason: " + printerClient.getError() + "
"; - } else { - html += "Status: " + printerClient.getState(); - if (printerClient.isPSUoff() && HAS_PSU) { - html += ", PSU off"; - } - html += "
"; - } - - if (printerClient.isPrinting()) { - html += "File: " + printerClient.getFileName() + "
"; - float fileSize = printerClient.getFileSize().toFloat(); - if (fileSize > 0) { - fileSize = fileSize / 1024; - html += "File Size: " + String(fileSize) + "KB
"; - } - int filamentLength = printerClient.getFilamentLength().toInt(); - if (filamentLength > 0) { - float fLength = float(filamentLength) / 1000; - html += "Filament: " + String(fLength) + "m
"; - } - - html += "Tool Temperature: " + printerClient.getTempToolActual() + "° C
"; - if ( printerClient.getTempBedActual() != 0 ) { - html += "Bed Temperature: " + printerClient.getTempBedActual() + "° C
"; - } - - int val = printerClient.getProgressPrintTimeLeft().toInt(); - int hours = numberOfHours(val); - int minutes = numberOfMinutes(val); - int seconds = numberOfSeconds(val); - html += "Est. Print Time Left: " + zeroPad(hours) + ":" + zeroPad(minutes) + ":" + zeroPad(seconds) + "
"; - - val = printerClient.getProgressPrintTime().toInt(); - hours = numberOfHours(val); - minutes = numberOfMinutes(val); - seconds = numberOfSeconds(val); - html += "Printing Time: " + zeroPad(hours) + ":" + zeroPad(minutes) + ":" + zeroPad(seconds) + "
"; - html += ""; - html += "

" + printerClient.getProgressCompletion() + "%
"; - } else { - html += "
"; - } - - html += "

"; - - server.sendContent(html); // spit out what we got - html = ""; - - if (DISPLAYWEATHER) { - if (weatherClient.getCity(0) == "") { - html += "

Please Configure Weather API

"; - if (weatherClient.getError() != "") { - html += "

Weather Error: " + weatherClient.getError() + "

"; - } - } else { - html += "

" + weatherClient.getCity(0) + ", " + weatherClient.getCountry(0) + "

"; - html += "
"; - html += "" + weatherClient.getDescription(0) + "
"; - html += weatherClient.getHumidity(0) + "% Humidity
"; - html += weatherClient.getWind(0) + " " + getSpeedSymbol() + " Wind
"; - html += "
"; - html += "

"; - html += weatherClient.getCondition(0) + " (" + weatherClient.getDescription(0) + ")
"; - html += weatherClient.getTempRounded(0) + getTempSymbol(true) + "
"; - html += " Map It!
"; - html += "

"; - } - - server.sendContent(html); // spit out what we got - html = ""; // fresh start - } - - server.sendContent(String(getFooter())); - server.sendContent(""); - server.client().stop(); - digitalWrite(externalLight, HIGH); -} - -void configModeCallback (WiFiManager *myWiFiManager) { - Serial.println("Entered config mode"); - Serial.println(WiFi.softAPIP()); - - display.clear(); - display.setTextAlignment(TEXT_ALIGN_CENTER); - display.setFont(ArialMT_Plain_10); - display.drawString(64, 0, "Wifi Manager"); - display.drawString(64, 10, "Please connect to AP"); - display.setFont(ArialMT_Plain_16); - display.drawString(64, 23, myWiFiManager->getConfigPortalSSID()); - display.setFont(ArialMT_Plain_10); - display.drawString(64, 42, "To setup Wifi connection"); - display.display(); - - Serial.println("Wifi Manager"); - Serial.println("Please connect to AP"); - Serial.println(myWiFiManager->getConfigPortalSSID()); - Serial.println("To setup Wifi Configuration"); - flashLED(20, 50); -} - -void flashLED(int number, int delayTime) { - for (int inx = 0; inx < number; inx++) { - delay(delayTime); - digitalWrite(externalLight, LOW); - delay(delayTime); - digitalWrite(externalLight, HIGH); - delay(delayTime); - } -} - -void drawScreen1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { - String bed = printerClient.getValueRounded(printerClient.getTempBedActual()); - String tool = printerClient.getValueRounded(printerClient.getTempToolActual()); - display->setTextAlignment(TEXT_ALIGN_CENTER); - display->setFont(ArialMT_Plain_16); - if (bed != "0") { - display->drawString(64 + x, 0 + y, "Bed / Tool Temp"); - } else { - display->drawString(64 + x, 0 + y, "Tool Temp"); - } - display->setTextAlignment(TEXT_ALIGN_LEFT); - display->setFont(ArialMT_Plain_24); - if (bed != "0") { - display->setTextAlignment(TEXT_ALIGN_LEFT); - display->drawString(2 + x, 14 + y, bed + "°"); - display->drawString(64 + x, 14 + y, tool + "°"); - } else { - display->setTextAlignment(TEXT_ALIGN_CENTER); - display->drawString(64 + x, 14 + y, tool + "°"); - } -} - -void drawScreen2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { - display->setTextAlignment(TEXT_ALIGN_CENTER); - display->setFont(ArialMT_Plain_16); - - display->drawString(64 + x, 0 + y, "Time Remaining"); - //display->setTextAlignment(TEXT_ALIGN_LEFT); - display->setFont(ArialMT_Plain_24); - int val = printerClient.getProgressPrintTimeLeft().toInt(); - int hours = numberOfHours(val); - int minutes = numberOfMinutes(val); - int seconds = numberOfSeconds(val); - - String time = zeroPad(hours) + ":" + zeroPad(minutes) + ":" + zeroPad(seconds); - display->drawString(64 + x, 14 + y, time); -} - -void drawScreen3(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { - display->setTextAlignment(TEXT_ALIGN_CENTER); - display->setFont(ArialMT_Plain_16); - - display->drawString(64 + x, 0 + y, "Printing Time"); - //display->setTextAlignment(TEXT_ALIGN_LEFT); - display->setFont(ArialMT_Plain_24); - int val = printerClient.getProgressPrintTime().toInt(); - int hours = numberOfHours(val); - int minutes = numberOfMinutes(val); - int seconds = numberOfSeconds(val); - - String time = zeroPad(hours) + ":" + zeroPad(minutes) + ":" + zeroPad(seconds); - display->drawString(64 + x, 14 + y, time); -} - -void drawClock(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { - display->setTextAlignment(TEXT_ALIGN_CENTER); - - String displayTime = timeClient.getAmPmHours() + ":" + timeClient.getMinutes() + ":" + timeClient.getSeconds(); - if (IS_24HOUR) { - displayTime = timeClient.getHours() + ":" + timeClient.getMinutes() + ":" + timeClient.getSeconds(); - } - display->setFont(ArialMT_Plain_16); - display->drawString(64 + x, 0 + y, OctoPrintHostName); - display->setFont(ArialMT_Plain_24); - display->drawString(64 + x, 17 + y, displayTime); -} - -void drawWeather(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { - display->setTextAlignment(TEXT_ALIGN_LEFT); - display->setFont(ArialMT_Plain_24); - display->drawString(0 + x, 0 + y, weatherClient.getTempRounded(0) + getTempSymbol()); - display->setTextAlignment(TEXT_ALIGN_LEFT); - display->setFont(ArialMT_Plain_24); - - display->setFont(ArialMT_Plain_16); - display->drawString(0 + x, 24 + y, weatherClient.getCondition(0)); - display->setFont((const uint8_t*)Meteocons_Plain_42); - display->drawString(86 + x, 0 + y, weatherClient.getWeatherIcon(0)); -} - -String getTempSymbol() { - return getTempSymbol(false); -} - -String getTempSymbol(boolean forHTML) { - String rtnValue = "F"; - if (IS_METRIC) { - rtnValue = "C"; - } - if (forHTML) { - rtnValue = "°" + rtnValue; - } else { - rtnValue = "°" + rtnValue; - } - return rtnValue; -} - -String getSpeedSymbol() { - String rtnValue = "mph"; - if (IS_METRIC) { - rtnValue = "kph"; - } - return rtnValue; -} - -String zeroPad(int value) { - String rtnValue = String(value); - if (value < 10) { - rtnValue = "0" + rtnValue; - } - return rtnValue; -} - -void drawHeaderOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) { - display->setColor(WHITE); - display->setFont(ArialMT_Plain_16); - String displayTime = timeClient.getAmPmHours() + ":" + timeClient.getMinutes(); - if (IS_24HOUR) { - displayTime = timeClient.getHours() + ":" + timeClient.getMinutes(); - } - display->setTextAlignment(TEXT_ALIGN_LEFT); - display->drawString(0, 48, displayTime); - - if (!IS_24HOUR) { - String ampm = timeClient.getAmPm(); - display->setFont(ArialMT_Plain_10); - display->drawString(39, 54, ampm); - } - - display->setFont(ArialMT_Plain_16); - display->setTextAlignment(TEXT_ALIGN_LEFT); - String percent = String(printerClient.getProgressCompletion()) + "%"; - display->drawString(64, 48, percent); - - // Draw indicator to show next update - int updatePos = (printerClient.getProgressCompletion().toFloat() / float(100)) * 128; - display->drawRect(0, 41, 128, 6); - display->drawHorizontalLine(0, 42, updatePos); - display->drawHorizontalLine(0, 43, updatePos); - display->drawHorizontalLine(0, 44, updatePos); - display->drawHorizontalLine(0, 45, updatePos); - - drawRssi(display); -} - -void drawClockHeaderOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) { - display->setColor(WHITE); - display->setFont(ArialMT_Plain_16); - display->setTextAlignment(TEXT_ALIGN_LEFT); - if (!IS_24HOUR) { - display->drawString(0, 48, timeClient.getAmPm()); - display->setTextAlignment(TEXT_ALIGN_CENTER); - if (printerClient.isPSUoff()) { - display->drawString(64, 47, "psu off"); - } else { - display->drawString(64, 47, "offline"); - } - } else { - if (printerClient.isPSUoff()) { - display->drawString(0, 47, "psu off"); - } else { - display->drawString(0, 47, "offline"); - } - } - display->setTextAlignment(TEXT_ALIGN_LEFT); - display->drawRect(0, 43, 128, 2); - - drawRssi(display); -} - -void drawRssi(OLEDDisplay *display) { - - - int8_t quality = getWifiQuality(); - for (int8_t i = 0; i < 4; i++) { - for (int8_t j = 0; j < 3 * (i + 2); j++) { - if (quality > i * 25 || j == 0) { - display->setPixel(114 + 4 * i, 63 - j); - } - } - } -} - -// converts the dBm to a range between 0 and 100% -int8_t getWifiQuality() { - int32_t dbm = WiFi.RSSI(); - if(dbm <= -100) { - return 0; - } else if(dbm >= -50) { - return 100; - } else { - return 2 * (dbm + 100); - } -} - - -void writeSettings() { - // Save decoded message to SPIFFS file for playback on power up. - File f = SPIFFS.open(CONFIG, "w"); - if (!f) { - Serial.println("File open failed!"); - } else { - Serial.println("Saving settings now..."); - f.println("UtcOffset=" + String(UtcOffset)); - f.println("octoKey=" + OctoPrintApiKey); - f.println("octoHost=" + OctoPrintHostName); - f.println("octoServer=" + OctoPrintServer); - f.println("octoPort=" + String(OctoPrintPort)); - f.println("octoUser=" + OctoAuthUser); - f.println("octoPass=" + OctoAuthPass); - f.println("refreshRate=" + String(minutesBetweenDataRefresh)); - f.println("themeColor=" + themeColor); - f.println("IS_BASIC_AUTH=" + String(IS_BASIC_AUTH)); - f.println("www_username=" + String(www_username)); - f.println("www_password=" + String(www_password)); - f.println("DISPLAYCLOCK=" + String(DISPLAYCLOCK)); - f.println("is24hour=" + String(IS_24HOUR)); - f.println("invertDisp=" + String(INVERT_DISPLAY)); - f.println("isWeather=" + String(DISPLAYWEATHER)); - f.println("weatherKey=" + WeatherApiKey); - f.println("CityID=" + String(CityIDs[0])); - f.println("isMetric=" + String(IS_METRIC)); - f.println("language=" + String(WeatherLanguage)); - f.println("hasPSU=" + String(HAS_PSU)); - } - f.close(); - readSettings(); - timeClient.setUtcOffset(UtcOffset); -} - -void readSettings() { - if (SPIFFS.exists(CONFIG) == false) { - Serial.println("Settings File does not yet exists."); - writeSettings(); - return; - } - File fr = SPIFFS.open(CONFIG, "r"); - String line; - while(fr.available()) { - line = fr.readStringUntil('\n'); - - if (line.indexOf("UtcOffset=") >= 0) { - UtcOffset = line.substring(line.lastIndexOf("UtcOffset=") + 10).toFloat(); - Serial.println("UtcOffset=" + String(UtcOffset)); - } - if (line.indexOf("octoKey=") >= 0) { - OctoPrintApiKey = line.substring(line.lastIndexOf("octoKey=") + 8); - OctoPrintApiKey.trim(); - Serial.println("OctoPrintApiKey=" + OctoPrintApiKey); - } - if (line.indexOf("octoHost=") >= 0) { - OctoPrintHostName = line.substring(line.lastIndexOf("octoHost=") + 9); - OctoPrintHostName.trim(); - Serial.println("OctoPrintHostName=" + OctoPrintHostName); - } - if (line.indexOf("octoServer=") >= 0) { - OctoPrintServer = line.substring(line.lastIndexOf("octoServer=") + 11); - OctoPrintServer.trim(); - Serial.println("OctoPrintServer=" + OctoPrintServer); - } - if (line.indexOf("octoPort=") >= 0) { - OctoPrintPort = line.substring(line.lastIndexOf("octoPort=") + 9).toInt(); - Serial.println("OctoPrintPort=" + String(OctoPrintPort)); - } - if (line.indexOf("octoUser=") >= 0) { - OctoAuthUser = line.substring(line.lastIndexOf("octoUser=") + 9); - OctoAuthUser.trim(); - Serial.println("OctoAuthUser=" + OctoAuthUser); - } - if (line.indexOf("octoPass=") >= 0) { - OctoAuthPass = line.substring(line.lastIndexOf("octoPass=") + 9); - OctoAuthPass.trim(); - Serial.println("OctoAuthPass=" + OctoAuthPass); - } - if (line.indexOf("refreshRate=") >= 0) { - minutesBetweenDataRefresh = line.substring(line.lastIndexOf("refreshRate=") + 12).toInt(); - Serial.println("minutesBetweenDataRefresh=" + String(minutesBetweenDataRefresh)); - } - if (line.indexOf("themeColor=") >= 0) { - themeColor = line.substring(line.lastIndexOf("themeColor=") + 11); - themeColor.trim(); - Serial.println("themeColor=" + themeColor); - } - if (line.indexOf("IS_BASIC_AUTH=") >= 0) { - IS_BASIC_AUTH = line.substring(line.lastIndexOf("IS_BASIC_AUTH=") + 14).toInt(); - Serial.println("IS_BASIC_AUTH=" + String(IS_BASIC_AUTH)); - } - if (line.indexOf("www_username=") >= 0) { - String temp = line.substring(line.lastIndexOf("www_username=") + 13); - temp.trim(); - temp.toCharArray(www_username, sizeof(temp)); - Serial.println("www_username=" + String(www_username)); - } - if (line.indexOf("www_password=") >= 0) { - String temp = line.substring(line.lastIndexOf("www_password=") + 13); - temp.trim(); - temp.toCharArray(www_password, sizeof(temp)); - Serial.println("www_password=" + String(www_password)); - } - if (line.indexOf("DISPLAYCLOCK=") >= 0) { - DISPLAYCLOCK = line.substring(line.lastIndexOf("DISPLAYCLOCK=") + 13).toInt(); - Serial.println("DISPLAYCLOCK=" + String(DISPLAYCLOCK)); - } - if (line.indexOf("is24hour=") >= 0) { - IS_24HOUR = line.substring(line.lastIndexOf("is24hour=") + 9).toInt(); - Serial.println("IS_24HOUR=" + String(IS_24HOUR)); - } - if(line.indexOf("invertDisp=") >= 0) { - INVERT_DISPLAY = line.substring(line.lastIndexOf("invertDisp=") + 11).toInt(); - Serial.println("INVERT_DISPLAY=" + String(INVERT_DISPLAY)); - } - if (line.indexOf("hasPSU=") >= 0) { - HAS_PSU = line.substring(line.lastIndexOf("hasPSU=") + 7).toInt(); - Serial.println("HAS_PSU=" + String(HAS_PSU)); - } - if (line.indexOf("isWeather=") >= 0) { - DISPLAYWEATHER = line.substring(line.lastIndexOf("isWeather=") + 10).toInt(); - Serial.println("DISPLAYWEATHER=" + String(DISPLAYWEATHER)); - } - if (line.indexOf("weatherKey=") >= 0) { - WeatherApiKey = line.substring(line.lastIndexOf("weatherKey=") + 11); - WeatherApiKey.trim(); - Serial.println("WeatherApiKey=" + WeatherApiKey); - } - if (line.indexOf("CityID=") >= 0) { - CityIDs[0] = line.substring(line.lastIndexOf("CityID=") + 7).toInt(); - Serial.println("CityID: " + String(CityIDs[0])); - } - if (line.indexOf("isMetric=") >= 0) { - IS_METRIC = line.substring(line.lastIndexOf("isMetric=") + 9).toInt(); - Serial.println("IS_METRIC=" + String(IS_METRIC)); - } - if (line.indexOf("language=") >= 0) { - WeatherLanguage = line.substring(line.lastIndexOf("language=") + 9); - WeatherLanguage.trim(); - Serial.println("WeatherLanguage=" + WeatherLanguage); - } - } - fr.close(); - printerClient.updateOctoPrintClient(OctoPrintApiKey, OctoPrintServer, OctoPrintPort, OctoAuthUser, OctoAuthPass, HAS_PSU); - weatherClient.updateWeatherApiKey(WeatherApiKey); - weatherClient.updateLanguage(WeatherLanguage); - weatherClient.setMetric(IS_METRIC); - weatherClient.updateCityIdList(CityIDs, 1); - timeClient.setUtcOffset(UtcOffset); -} - -int getMinutesFromLastRefresh() { - int minutes = (timeClient.getCurrentEpoch() - lastEpoch) / 60; - return minutes; -} - -int getMinutesFromLastDisplay() { - int minutes = (timeClient.getCurrentEpoch() - displayOffEpoch) / 60; - return minutes; -} - -// Toggle on and off the display if user defined times -void checkDisplay() { - if (!displayOn && DISPLAYCLOCK) { - enableDisplay(true); - } - if (displayOn && !(printerClient.isOperational() || printerClient.isPrinting()) && !DISPLAYCLOCK) { - // Put Display to sleep - display.clear(); - display.display(); - display.setFont(ArialMT_Plain_16); - display.setTextAlignment(TEXT_ALIGN_CENTER); - display.setContrast(255); // default is 255 - display.drawString(64, 5, "Printer Offline\nSleep Mode..."); - display.display(); - delay(5000); - enableDisplay(false); - Serial.println("Printer is offline going down to sleep..."); - return; - } else if (!displayOn && !DISPLAYCLOCK) { - if (printerClient.isOperational()) { - // Wake the Screen up - enableDisplay(true); - display.clear(); - display.display(); - display.setFont(ArialMT_Plain_16); - display.setTextAlignment(TEXT_ALIGN_CENTER); - display.setContrast(255); // default is 255 - display.drawString(64, 5, "Printer Online\nWake up..."); - display.display(); - Serial.println("Printer is online waking up..."); - delay(5000); - return; - } - } else if (DISPLAYCLOCK) { - if ((!printerClient.isOperational() || printerClient.isPSUoff()) && !isClockOn) { - Serial.println("Clock Mode is turned on."); - if (!DISPLAYWEATHER) { - ui.disableAutoTransition(); - ui.setFrames(clockFrame, 1); - clockFrame[0] = drawClock; - } else { - ui.enableAutoTransition(); - ui.setFrames(clockFrame, 2); - clockFrame[0] = drawClock; - clockFrame[1] = drawWeather; - } - ui.setOverlays(clockOverlay, numberOfOverlays); - isClockOn = true; - } else if (printerClient.isOperational() && !printerClient.isPSUoff() && isClockOn) { - Serial.println("Printer Monitor is active."); - ui.setFrames(frames, numberOfFrames); - ui.setOverlays(overlays, numberOfOverlays); - ui.enableAutoTransition(); - isClockOn = false; - } - } -} - -void enableDisplay(boolean enable) { - displayOn = enable; - if (enable) { - if (getMinutesFromLastDisplay() >= minutesBetweenDataRefresh) { - // The display has been off longer than the minutes between refresh -- need to get fresh data - lastEpoch = 0; // this should force a data pull - displayOffEpoch = 0; // reset - } - display.displayOn(); - Serial.println("Display was turned ON: " + timeClient.getFormattedTime()); - } else { - display.displayOff(); - Serial.println("Display was turned OFF: " + timeClient.getFormattedTime()); - displayOffEpoch = lastEpoch; - } -} +/** The MIT License (MIT) + +Copyright (c) 2018 David Payne + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Additional Contributions: +/* 15 Jan 2019 : Owen Carter : Add psucontrol option and processing */ + + /********************************************** + * Edit Settings.h for personalization + ***********************************************/ + +#include "Settings.h" + +#define VERSION "3.0" + +#define HOSTNAME "PrintMon-" +#define CONFIG "/conf.txt" + +/* Useful Constants */ +#define SECS_PER_MIN (60UL) +#define SECS_PER_HOUR (3600UL) + +/* Useful Macros for getting elapsed time */ +#define numberOfSeconds(_time_) (_time_ % SECS_PER_MIN) +#define numberOfMinutes(_time_) ((_time_ / SECS_PER_MIN) % SECS_PER_MIN) +#define numberOfHours(_time_) (_time_ / SECS_PER_HOUR) + +// Initialize the oled display for I2C_DISPLAY_ADDRESS +// SDA_PIN and SCL_PIN +#if defined(DISPLAY_SH1106) + SH1106Wire display(I2C_DISPLAY_ADDRESS, SDA_PIN, SCL_PIN); +#else + SSD1306Wire display(I2C_DISPLAY_ADDRESS, SDA_PIN, SCL_PIN); // this is the default +#endif + +OLEDDisplayUi ui( &display ); + +void drawProgress(OLEDDisplay *display, int percentage, String label); +void drawOtaProgress(unsigned int, unsigned int); +void drawScreen1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y); +void drawScreen2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y); +void drawScreen3(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y); +void drawHeaderOverlay(OLEDDisplay *display, OLEDDisplayUiState* state); +void drawClock(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y); +void drawWeather(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y); +void drawClockHeaderOverlay(OLEDDisplay *display, OLEDDisplayUiState* state); + +// Set the number of Frames supported +const int numberOfFrames = 3; +FrameCallback frames[numberOfFrames]; +FrameCallback clockFrame[2]; +boolean isClockOn = false; + +OverlayCallback overlays[] = { drawHeaderOverlay }; +OverlayCallback clockOverlay[] = { drawClockHeaderOverlay }; +int numberOfOverlays = 1; + +// Time +TimeClient timeClient(UtcOffset); +long lastEpoch = 0; +long firstEpoch = 0; +long displayOffEpoch = 0; +String lastMinute = "xx"; +String lastSecond = "xx"; +String lastReportStatus = ""; +boolean displayOn = true; + +// Printer Client +#if defined(USE_REPETIER_CLIENT) + RepetierClient printerClient(PrinterApiKey, PrinterServer, PrinterPort, PrinterAuthUser, PrinterAuthPass, HAS_PSU); +#else + OctoPrintClient printerClient(PrinterApiKey, PrinterServer, PrinterPort, PrinterAuthUser, PrinterAuthPass, HAS_PSU); +#endif +int printerCount = 0; + +// Weather Client +OpenWeatherMapClient weatherClient(WeatherApiKey, CityIDs, 1, IS_METRIC, WeatherLanguage); + +//declairing prototypes +void configModeCallback (WiFiManager *myWiFiManager); +int8_t getWifiQuality(); + +ESP8266WebServer server(WEBSERVER_PORT); +ESP8266HTTPUpdateServer serverUpdater; + +static const char WEB_ACTIONS[] PROGMEM = " Home" + " Configure" + " Weather" + " Reset Settings" + " Forget WiFi" + " Firmware Update" + " About"; + +String CHANGE_FORM = ""; // moved to config to make it dynamic + +static const char CLOCK_FORM[] PROGMEM = "

Display Clock when printer is off

" + "

Use 24 Hour Clock (military time)

" + "

Flip display orientation

" + "

Flash System LED on Service Calls

" + "

Use OctoPrint PSU control plugin for clock/blank

" + "

Clock Sync / Weather Refresh (minutes)

"; + +static const char THEME_FORM[] PROGMEM = "

Theme Color

" + "


" + "

Use Security Credentials for Configuration Changes

" + "

" + "

" + ""; + +static const char WEATHER_FORM[] PROGMEM = "

Weather Config:

" + "

Display Weather when printer is off

" + "" + "" + "

" + "

Use Metric (Celsius)

" + "

Weather Language

" + "
" + ""; + +static const char LANG_OPTIONS[] PROGMEM = "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ""; + +static const char COLOR_THEMES[] PROGMEM = "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ""; + + +void setup() { + Serial.begin(115200); + SPIFFS.begin(); + delay(10); + + //New Line to clear from start garbage + Serial.println(); + + // Initialize digital pin for LED (little blue light on the Wemos D1 Mini) + pinMode(externalLight, OUTPUT); + + //Some Defaults before loading from Config.txt + PrinterPort = printerClient.getPrinterPort(); + + readSettings(); + + // initialize display + display.init(); + if (INVERT_DISPLAY) { + display.flipScreenVertically(); // connections at top of OLED display + } + display.clear(); + display.display(); + + //display.flipScreenVertically(); + + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.setContrast(255); // default is 255 + display.setFont(ArialMT_Plain_16); + display.drawString(64, 1, "Printer Monitor"); + display.setFont(ArialMT_Plain_10); + display.drawString(64, 18, "for " + printerClient.getPrinterType()); + display.setFont(ArialMT_Plain_16); + display.drawString(64, 30, "By Qrome"); + display.drawString(64, 46, "V" + String(VERSION)); + display.display(); + + //WiFiManager + //Local intialization. Once its business is done, there is no need to keep it around + WiFiManager wifiManager; + + // Uncomment for testing wifi manager + //wifiManager.resetSettings(); + wifiManager.setAPCallback(configModeCallback); + + String hostname(HOSTNAME); + hostname += String(ESP.getChipId(), HEX); + if (!wifiManager.autoConnect((const char *)hostname.c_str())) {// new addition + delay(3000); + WiFi.disconnect(true); + ESP.reset(); + delay(5000); + } + + // You can change the transition that is used + // SLIDE_LEFT, SLIDE_RIGHT, SLIDE_TOP, SLIDE_DOWN + ui.setFrameAnimation(SLIDE_LEFT); + ui.setTargetFPS(30); + ui.disableAllIndicators(); + ui.setFrames(frames, (numberOfFrames)); + frames[0] = drawScreen1; + frames[1] = drawScreen2; + frames[2] = drawScreen3; + clockFrame[0] = drawClock; + clockFrame[1] = drawWeather; + ui.setOverlays(overlays, numberOfOverlays); + + // Inital UI takes care of initalising the display too. + ui.init(); + if (INVERT_DISPLAY) { + display.flipScreenVertically(); //connections at top of OLED display + } + + // print the received signal strength: + Serial.print("Signal Strength (RSSI): "); + Serial.print(getWifiQuality()); + Serial.println("%"); + + if (ENABLE_OTA) { + ArduinoOTA.onStart([]() { + Serial.println("Start"); + }); + ArduinoOTA.onEnd([]() { + Serial.println("\nEnd"); + }); + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { + Serial.printf("Progress: %u%%\r", (progress / (total / 100))); + }); + ArduinoOTA.onError([](ota_error_t error) { + Serial.printf("Error[%u]: ", error); + if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); + else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); + else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); + else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); + else if (error == OTA_END_ERROR) Serial.println("End Failed"); + }); + ArduinoOTA.setHostname((const char *)hostname.c_str()); + if (OTA_Password != "") { + ArduinoOTA.setPassword(((const char *)OTA_Password.c_str())); + } + ArduinoOTA.begin(); + } + + if (WEBSERVER_ENABLED) { + server.on("/", displayPrinterStatus); + server.on("/systemreset", handleSystemReset); + server.on("/forgetwifi", handleWifiReset); + server.on("/updateconfig", handleUpdateConfig); + server.on("/updateweatherconfig", handleUpdateWeather); + server.on("/configure", handleConfigure); + server.on("/configureweather", handleWeatherConfigure); + server.onNotFound(redirectHome); + serverUpdater.setup(&server, "/update", www_username, www_password); + // Start the server + server.begin(); + Serial.println("Server started"); + // Print the IP address + String webAddress = "http://" + WiFi.localIP().toString() + ":" + String(WEBSERVER_PORT) + "/"; + Serial.println("Use this URL : " + webAddress); + display.clear(); + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.setFont(ArialMT_Plain_10); + display.drawString(64, 10, "Web Interface On"); + display.drawString(64, 20, "You May Connect to IP"); + display.setFont(ArialMT_Plain_16); + display.drawString(64, 30, WiFi.localIP().toString()); + display.drawString(64, 46, "Port: " + String(WEBSERVER_PORT)); + display.display(); + } else { + Serial.println("Web Interface is Disabled"); + display.clear(); + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.setFont(ArialMT_Plain_10); + display.drawString(64, 10, "Web Interface is Off"); + display.drawString(64, 20, "Enable in Settings.h"); + display.display(); + } + flashLED(5, 100); + findMDNS(); //go find Printer Server by the hostname + Serial.println("*** Leaving setup()"); +} + +void findMDNS() { + if (PrinterHostName == "" || ENABLE_OTA == false) { + return; // nothing to do here + } + // We now query our network for 'web servers' service + // over tcp, and get the number of available devices + int n = MDNS.queryService("http", "tcp"); + if (n == 0) { + Serial.println("no services found - make sure Printer server is turned on"); + return; + } + Serial.println("*** Looking for " + PrinterHostName + " over mDNS"); + for (int i = 0; i < n; ++i) { + // Going through every available service, + // we're searching for the one whose hostname + // matches what we want, and then get its IP + Serial.println("Found: " + MDNS.hostname(i)); + if (MDNS.hostname(i) == PrinterHostName) { + IPAddress serverIp = MDNS.IP(i); + PrinterServer = serverIp.toString(); + PrinterPort = MDNS.port(i); // save the port + Serial.println("*** Found Printer Server " + PrinterHostName + " http://" + PrinterServer + ":" + PrinterPort); + writeSettings(); // update the settings + } + } +} + +//************************************************************ +// Main Loop +//************************************************************ +void loop() { + + //Get Time Update + if((getMinutesFromLastRefresh() >= minutesBetweenDataRefresh) || lastEpoch == 0) { + getUpdateTime(); + } + + if (lastMinute != timeClient.getMinutes() && !printerClient.isPrinting()) { + // Check status every 60 seconds + ledOnOff(true); + lastMinute = timeClient.getMinutes(); // reset the check value + printerClient.getPrinterJobResults(); + printerClient.getPrinterPsuState(); + ledOnOff(false); + } else if (printerClient.isPrinting()) { + if (lastSecond != timeClient.getSeconds() && timeClient.getSeconds().endsWith("0")) { + lastSecond = timeClient.getSeconds(); + // every 10 seconds while printing get an update + ledOnOff(true); + printerClient.getPrinterJobResults(); + printerClient.getPrinterPsuState(); + ledOnOff(false); + } + } + + checkDisplay(); // Check to see if the printer is on or offline and change display. + + ui.update(); + + if (WEBSERVER_ENABLED) { + server.handleClient(); + } + if (ENABLE_OTA) { + ArduinoOTA.handle(); + } +} + +void getUpdateTime() { + ledOnOff(true); // turn on the LED + Serial.println(); + + if (displayOn && DISPLAYWEATHER) { + Serial.println("Getting Weather Data..."); + weatherClient.updateWeather(); + } + + Serial.println("Updating Time..."); + //Update the Time + timeClient.updateTime(); + lastEpoch = timeClient.getCurrentEpoch(); + Serial.println("Local time: " + timeClient.getAmPmFormattedTime()); + + ledOnOff(false); // turn off the LED +} + +boolean authentication() { + if (IS_BASIC_AUTH && (strlen(www_username) >= 1 && strlen(www_password) >= 1)) { + return server.authenticate(www_username, www_password); + } + return true; // Authentication not required +} + +void handleSystemReset() { + if (!authentication()) { + return server.requestAuthentication(); + } + Serial.println("Reset System Configuration"); + if (SPIFFS.remove(CONFIG)) { + redirectHome(); + ESP.restart(); + } +} + +void handleUpdateWeather() { + if (!authentication()) { + return server.requestAuthentication(); + } + DISPLAYWEATHER = server.hasArg("isWeatherEnabled"); + WeatherApiKey = server.arg("openWeatherMapApiKey"); + CityIDs[0] = server.arg("city1").toInt(); + IS_METRIC = server.hasArg("metric"); + WeatherLanguage = server.arg("language"); + writeSettings(); + isClockOn = false; // this will force a check for the display + checkDisplay(); + lastEpoch = 0; + redirectHome(); +} + +void handleUpdateConfig() { + boolean flipOld = INVERT_DISPLAY; + if (!authentication()) { + return server.requestAuthentication(); + } + if (server.hasArg("printer")) { + printerClient.setPrinterName(server.arg("printer")); + } + PrinterApiKey = server.arg("PrinterApiKey"); + PrinterHostName = server.arg("PrinterHostName"); + PrinterServer = server.arg("PrinterAddress"); + PrinterPort = server.arg("PrinterPort").toInt(); + PrinterAuthUser = server.arg("octoUser"); + PrinterAuthPass = server.arg("octoPass"); + DISPLAYCLOCK = server.hasArg("isClockEnabled"); + IS_24HOUR = server.hasArg("is24hour"); + INVERT_DISPLAY = server.hasArg("invDisp"); + USE_FLASH = server.hasArg("useFlash"); + HAS_PSU = server.hasArg("hasPSU"); + minutesBetweenDataRefresh = server.arg("refresh").toInt(); + themeColor = server.arg("theme"); + UtcOffset = server.arg("utcoffset").toFloat(); + String temp = server.arg("userid"); + temp.toCharArray(www_username, sizeof(temp)); + temp = server.arg("stationpassword"); + temp.toCharArray(www_password, sizeof(temp)); + writeSettings(); + findMDNS(); + printerClient.getPrinterJobResults(); + printerClient.getPrinterPsuState(); + if (INVERT_DISPLAY != flipOld) { + ui.init(); + if(INVERT_DISPLAY) + display.flipScreenVertically(); + ui.update(); + } + checkDisplay(); + lastEpoch = 0; + redirectHome(); +} + +void handleWifiReset() { + if (!authentication()) { + return server.requestAuthentication(); + } + //WiFiManager + //Local intialization. Once its business is done, there is no need to keep it around + redirectHome(); + WiFiManager wifiManager; + wifiManager.resetSettings(); + ESP.restart(); +} + +void handleWeatherConfigure() { + if (!authentication()) { + return server.requestAuthentication(); + } + ledOnOff(true); + String html = ""; + + server.sendHeader("Cache-Control", "no-cache, no-store"); + server.sendHeader("Pragma", "no-cache"); + server.sendHeader("Expires", "-1"); + server.setContentLength(CONTENT_LENGTH_UNKNOWN); + server.send(200, "text/html", ""); + + html = getHeader(); + server.sendContent(html); + + String form = FPSTR(WEATHER_FORM); + String isWeatherChecked = ""; + if (DISPLAYWEATHER) { + isWeatherChecked = "checked='checked'"; + } + form.replace("%IS_WEATHER_CHECKED%", isWeatherChecked); + form.replace("%WEATHERKEY%", WeatherApiKey); + form.replace("%CITYNAME1%", weatherClient.getCity(0)); + form.replace("%CITY1%", String(CityIDs[0])); + String checked = ""; + if (IS_METRIC) { + checked = "checked='checked'"; + } + form.replace("%METRIC%", checked); + String options = FPSTR(LANG_OPTIONS); + options.replace(">"+String(WeatherLanguage)+"<", " selected>"+String(WeatherLanguage)+"<"); + form.replace("%LANGUAGEOPTIONS%", options); + server.sendContent(form); + + html = getFooter(); + server.sendContent(html); + server.sendContent(""); + server.client().stop(); + ledOnOff(false); +} + +void handleConfigure() { + if (!authentication()) { + return server.requestAuthentication(); + } + ledOnOff(true); + String html = ""; + + server.sendHeader("Cache-Control", "no-cache, no-store"); + server.sendHeader("Pragma", "no-cache"); + server.sendHeader("Expires", "-1"); + server.setContentLength(CONTENT_LENGTH_UNKNOWN); + server.send(200, "text/html", ""); + + html = getHeader(); + server.sendContent(html); + + CHANGE_FORM = "

Station Config:

" + "

" + "

"; + if (printerClient.getPrinterType() == "OctoPrint") { + CHANGE_FORM += "

"; + } + CHANGE_FORM += "

" + "

" + "

" + "

"; + if (printerClient.getPrinterType() == "Repetier") { + CHANGE_FORM += "" + "

" + ""; + } else { + CHANGE_FORM += "

"; + } + CHANGE_FORM += "

" + "

"; + + + + if (printerClient.getPrinterType() == "Repetier") { + html = ""; + + server.sendContent(html); + } else { + html = ""; + server.sendContent(html); + } + + String form = CHANGE_FORM; + + form.replace("%OCTOKEY%", PrinterApiKey); + form.replace("%OCTOHOST%", PrinterHostName); + form.replace("%OCTOADDRESS%", PrinterServer); + form.replace("%OCTOPORT%", String(PrinterPort)); + form.replace("%OCTOUSER%", PrinterAuthUser); + form.replace("%OCTOPASS%", PrinterAuthPass); + + server.sendContent(form); + + form = FPSTR(CLOCK_FORM); + + String isClockChecked = ""; + if (DISPLAYCLOCK) { + isClockChecked = "checked='checked'"; + } + form.replace("%IS_CLOCK_CHECKED%", isClockChecked); + String is24hourChecked = ""; + if (IS_24HOUR) { + is24hourChecked = "checked='checked'"; + } + form.replace("%IS_24HOUR_CHECKED%", is24hourChecked); + String isInvDisp = ""; + if (INVERT_DISPLAY) { + isInvDisp = "checked='checked'"; + } + form.replace("%IS_INVDISP_CHECKED%", isInvDisp); + String isFlashLED = ""; + if (USE_FLASH) { + isFlashLED = "checked='checked'"; + } + form.replace("%USEFLASH%", isFlashLED); + String hasPSUchecked = ""; + if (HAS_PSU) { + hasPSUchecked = "checked='checked'"; + } + form.replace("%HAS_PSU_CHECKED%", hasPSUchecked); + + String options = ""; + options.replace(">"+String(minutesBetweenDataRefresh)+"<", " selected>"+String(minutesBetweenDataRefresh)+"<"); + form.replace("%OPTIONS%", options); + + server.sendContent(form); + + form = FPSTR(THEME_FORM); + + String themeOptions = FPSTR(COLOR_THEMES); + themeOptions.replace(">"+String(themeColor)+"<", " selected>"+String(themeColor)+"<"); + form.replace("%THEME_OPTIONS%", themeOptions); + form.replace("%UTCOFFSET%", String(UtcOffset)); + String isUseSecurityChecked = ""; + if (IS_BASIC_AUTH) { + isUseSecurityChecked = "checked='checked'"; + } + form.replace("%IS_BASICAUTH_CHECKED%", isUseSecurityChecked); + form.replace("%USERID%", String(www_username)); + form.replace("%STATIONPASSWORD%", String(www_password)); + + server.sendContent(form); + + html = getFooter(); + server.sendContent(html); + server.sendContent(""); + server.client().stop(); + ledOnOff(false); +} + +void displayMessage(String message) { + ledOnOff(true); + + server.sendHeader("Cache-Control", "no-cache, no-store"); + server.sendHeader("Pragma", "no-cache"); + server.sendHeader("Expires", "-1"); + server.setContentLength(CONTENT_LENGTH_UNKNOWN); + server.send(200, "text/html", ""); + String html = getHeader(); + server.sendContent(String(html)); + server.sendContent(String(message)); + html = getFooter(); + server.sendContent(String(html)); + server.sendContent(""); + server.client().stop(); + + ledOnOff(false); +} + +void redirectHome() { + // Send them back to the Root Directory + server.sendHeader("Location", String("/"), true); + server.sendHeader("Cache-Control", "no-cache, no-store"); + server.sendHeader("Pragma", "no-cache"); + server.sendHeader("Expires", "-1"); + server.send(302, "text/plain", ""); + server.client().stop(); +} + +String getHeader() { + return getHeader(false); +} + +String getHeader(boolean refresh) { + String menu = FPSTR(WEB_ACTIONS); + + String html = ""; + html += "Printer Monitor"; + html += ""; + html += ""; + if (refresh) { + html += ""; + } + html += ""; + html += ""; + html += ""; + html += ""; + html += ""; + html += "

Printer Monitor

"; + html += ""; + html += "
"; + return html; +} + +String getFooter() { + int8_t rssi = getWifiQuality(); + Serial.print("Signal Strength (RSSI): "); + Serial.print(rssi); + Serial.println("%"); + String html = "


"; + html += "
"; + html += "
"; + if (lastReportStatus != "") { + html += " Report Status: " + lastReportStatus + "
"; + } + html += " Version: " + String(VERSION) + "
"; + html += " Signal Strength: "; + html += String(rssi) + "%"; + html += "
"; + html += ""; + return html; +} + +void displayPrinterStatus() { + ledOnOff(true); + String html = ""; + + server.sendHeader("Cache-Control", "no-cache, no-store"); + server.sendHeader("Pragma", "no-cache"); + server.sendHeader("Expires", "-1"); + server.setContentLength(CONTENT_LENGTH_UNKNOWN); + server.send(200, "text/html", ""); + server.sendContent(String(getHeader(true))); + + String displayTime = timeClient.getAmPmHours() + ":" + timeClient.getMinutes() + ":" + timeClient.getSeconds() + " " + timeClient.getAmPm(); + if (IS_24HOUR) { + displayTime = timeClient.getHours() + ":" + timeClient.getMinutes() + ":" + timeClient.getSeconds(); + } + + html += "

" + printerClient.getPrinterType() + " Monitor

"; + html += "

"; + if (printerClient.getPrinterType() == "Repetier") { + html += "Printer Name: " + printerClient.getPrinterName() + "
"; + } else { + html += "Host Name: " + PrinterHostName + "
"; + } + + if (printerClient.getError() != "") { + html += "Status: Offline
"; + html += "Reason: " + printerClient.getError() + "
"; + } else { + html += "Status: " + printerClient.getState(); + if (printerClient.isPSUoff() && HAS_PSU) { + html += ", PSU off"; + } + html += "
"; + } + + if (printerClient.isPrinting()) { + html += "File: " + printerClient.getFileName() + "
"; + float fileSize = printerClient.getFileSize().toFloat(); + if (fileSize > 0) { + fileSize = fileSize / 1024; + html += "File Size: " + String(fileSize) + "KB
"; + } + int filamentLength = printerClient.getFilamentLength().toInt(); + if (filamentLength > 0) { + float fLength = float(filamentLength) / 1000; + html += "Filament: " + String(fLength) + "m
"; + } + + html += "Tool Temperature: " + printerClient.getTempToolActual() + "° C
"; + if ( printerClient.getTempBedActual() != 0 ) { + html += "Bed Temperature: " + printerClient.getTempBedActual() + "° C
"; + } + + int val = printerClient.getProgressPrintTimeLeft().toInt(); + int hours = numberOfHours(val); + int minutes = numberOfMinutes(val); + int seconds = numberOfSeconds(val); + html += "Est. Print Time Left: " + zeroPad(hours) + ":" + zeroPad(minutes) + ":" + zeroPad(seconds) + "
"; + + val = printerClient.getProgressPrintTime().toInt(); + hours = numberOfHours(val); + minutes = numberOfMinutes(val); + seconds = numberOfSeconds(val); + html += "Printing Time: " + zeroPad(hours) + ":" + zeroPad(minutes) + ":" + zeroPad(seconds) + "
"; + html += ""; + html += "

" + printerClient.getProgressCompletion() + "%
"; + } else { + html += "
"; + } + + html += "

"; + + html += "

Time: " + displayTime + "

"; + + server.sendContent(html); // spit out what we got + html = ""; + + if (DISPLAYWEATHER) { + if (weatherClient.getCity(0) == "") { + html += "

Please Configure Weather API

"; + if (weatherClient.getError() != "") { + html += "

Weather Error: " + weatherClient.getError() + "

"; + } + } else { + html += "

" + weatherClient.getCity(0) + ", " + weatherClient.getCountry(0) + "

"; + html += "
"; + html += "" + weatherClient.getDescription(0) + "
"; + html += weatherClient.getHumidity(0) + "% Humidity
"; + html += weatherClient.getWind(0) + " " + getSpeedSymbol() + " Wind
"; + html += "
"; + html += "

"; + html += weatherClient.getCondition(0) + " (" + weatherClient.getDescription(0) + ")
"; + html += weatherClient.getTempRounded(0) + getTempSymbol(true) + "
"; + html += " Map It!
"; + html += "

"; + } + + server.sendContent(html); // spit out what we got + html = ""; // fresh start + } + + server.sendContent(String(getFooter())); + server.sendContent(""); + server.client().stop(); + ledOnOff(false); +} + +void configModeCallback (WiFiManager *myWiFiManager) { + Serial.println("Entered config mode"); + Serial.println(WiFi.softAPIP()); + + display.clear(); + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.setFont(ArialMT_Plain_10); + display.drawString(64, 0, "Wifi Manager"); + display.drawString(64, 10, "Please connect to AP"); + display.setFont(ArialMT_Plain_16); + display.drawString(64, 26, myWiFiManager->getConfigPortalSSID()); + display.setFont(ArialMT_Plain_10); + display.drawString(64, 46, "To setup Wifi connection"); + display.display(); + + Serial.println("Wifi Manager"); + Serial.println("Please connect to AP"); + Serial.println(myWiFiManager->getConfigPortalSSID()); + Serial.println("To setup Wifi Configuration"); + flashLED(20, 50); +} + +void ledOnOff(boolean value) { + if (USE_FLASH) { + if (value) { + digitalWrite(externalLight, LOW); // LED ON + } else { + digitalWrite(externalLight, HIGH); // LED OFF + } + } +} + +void flashLED(int number, int delayTime) { + for (int inx = 0; inx <= number; inx++) { + delay(delayTime); + digitalWrite(externalLight, LOW); // ON + delay(delayTime); + digitalWrite(externalLight, HIGH); // OFF + delay(delayTime); + } +} + +void drawScreen1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + String bed = printerClient.getValueRounded(printerClient.getTempBedActual()); + String tool = printerClient.getValueRounded(printerClient.getTempToolActual()); + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(ArialMT_Plain_16); + if (bed != "0") { + display->drawString(29 + x, 0 + y, "Tool"); + display->drawString(89 + x, 0 + y, "Bed"); + } else { + display->drawString(64 + x, 0 + y, "Tool Temp"); + } + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(ArialMT_Plain_24); + if (bed != "0") { + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->drawString(12 + x, 14 + y, tool + "°"); + display->drawString(74 + x, 14 + y, bed + "°"); + } else { + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->drawString(64 + x, 14 + y, tool + "°"); + } +} + +void drawScreen2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(ArialMT_Plain_16); + + display->drawString(64 + x, 0 + y, "Time Remaining"); + //display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(ArialMT_Plain_24); + int val = printerClient.getProgressPrintTimeLeft().toInt(); + int hours = numberOfHours(val); + int minutes = numberOfMinutes(val); + int seconds = numberOfSeconds(val); + + String time = zeroPad(hours) + ":" + zeroPad(minutes) + ":" + zeroPad(seconds); + display->drawString(64 + x, 14 + y, time); +} + +void drawScreen3(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(ArialMT_Plain_16); + + display->drawString(64 + x, 0 + y, "Printing Time"); + //display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(ArialMT_Plain_24); + int val = printerClient.getProgressPrintTime().toInt(); + int hours = numberOfHours(val); + int minutes = numberOfMinutes(val); + int seconds = numberOfSeconds(val); + + String time = zeroPad(hours) + ":" + zeroPad(minutes) + ":" + zeroPad(seconds); + display->drawString(64 + x, 14 + y, time); +} + +void drawClock(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + display->setTextAlignment(TEXT_ALIGN_CENTER); + + String displayTime = timeClient.getAmPmHours() + ":" + timeClient.getMinutes() + ":" + timeClient.getSeconds(); + if (IS_24HOUR) { + displayTime = timeClient.getHours() + ":" + timeClient.getMinutes() + ":" + timeClient.getSeconds(); + } + String displayName = PrinterHostName; + if (printerClient.getPrinterType() == "Repetier") { + displayName = printerClient.getPrinterName(); + } + display->setFont(ArialMT_Plain_16); + display->drawString(64 + x, 0 + y, displayName); + display->setFont(ArialMT_Plain_24); + display->drawString(64 + x, 17 + y, displayTime); +} + +void drawWeather(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(ArialMT_Plain_24); + display->drawString(0 + x, 0 + y, weatherClient.getTempRounded(0) + getTempSymbol()); + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(ArialMT_Plain_24); + + display->setFont(ArialMT_Plain_16); + display->drawString(0 + x, 24 + y, weatherClient.getCondition(0)); + display->setFont((const uint8_t*)Meteocons_Plain_42); + display->drawString(86 + x, 0 + y, weatherClient.getWeatherIcon(0)); +} + +String getTempSymbol() { + return getTempSymbol(false); +} + +String getTempSymbol(boolean forHTML) { + String rtnValue = "F"; + if (IS_METRIC) { + rtnValue = "C"; + } + if (forHTML) { + rtnValue = "°" + rtnValue; + } else { + rtnValue = "°" + rtnValue; + } + return rtnValue; +} + +String getSpeedSymbol() { + String rtnValue = "mph"; + if (IS_METRIC) { + rtnValue = "kph"; + } + return rtnValue; +} + +String zeroPad(int value) { + String rtnValue = String(value); + if (value < 10) { + rtnValue = "0" + rtnValue; + } + return rtnValue; +} + +void drawHeaderOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) { + display->setColor(WHITE); + display->setFont(ArialMT_Plain_16); + String displayTime = timeClient.getAmPmHours() + ":" + timeClient.getMinutes(); + if (IS_24HOUR) { + displayTime = timeClient.getHours() + ":" + timeClient.getMinutes(); + } + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->drawString(0, 48, displayTime); + + if (!IS_24HOUR) { + String ampm = timeClient.getAmPm(); + display->setFont(ArialMT_Plain_10); + display->drawString(39, 54, ampm); + } + + display->setFont(ArialMT_Plain_16); + display->setTextAlignment(TEXT_ALIGN_LEFT); + String percent = String(printerClient.getProgressCompletion()) + "%"; + display->drawString(64, 48, percent); + + // Draw indicator to show next update + int updatePos = (printerClient.getProgressCompletion().toFloat() / float(100)) * 128; + display->drawRect(0, 41, 128, 6); + display->drawHorizontalLine(0, 42, updatePos); + display->drawHorizontalLine(0, 43, updatePos); + display->drawHorizontalLine(0, 44, updatePos); + display->drawHorizontalLine(0, 45, updatePos); + + drawRssi(display); +} + +void drawClockHeaderOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) { + display->setColor(WHITE); + display->setFont(ArialMT_Plain_16); + display->setTextAlignment(TEXT_ALIGN_LEFT); + if (!IS_24HOUR) { + display->drawString(0, 48, timeClient.getAmPm()); + display->setTextAlignment(TEXT_ALIGN_CENTER); + if (printerClient.isPSUoff()) { + display->drawString(64, 47, "psu off"); + } else if (printerClient.getState() == "Operational") { + display->drawString(64, 47, "online"); + } else { + display->drawString(64, 47, "offline"); + } + } else { + if (printerClient.isPSUoff()) { + display->drawString(0, 47, "psu off"); + } else if (printerClient.getState() == "Operational") { + display->drawString(0, 47, "online"); + } else { + display->drawString(0, 47, "offline"); + } + } + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->drawRect(0, 43, 128, 2); + + drawRssi(display); +} + +void drawRssi(OLEDDisplay *display) { + + + int8_t quality = getWifiQuality(); + for (int8_t i = 0; i < 4; i++) { + for (int8_t j = 0; j < 3 * (i + 2); j++) { + if (quality > i * 25 || j == 0) { + display->setPixel(114 + 4 * i, 63 - j); + } + } + } +} + +// converts the dBm to a range between 0 and 100% +int8_t getWifiQuality() { + int32_t dbm = WiFi.RSSI(); + if(dbm <= -100) { + return 0; + } else if(dbm >= -50) { + return 100; + } else { + return 2 * (dbm + 100); + } +} + + +void writeSettings() { + // Save decoded message to SPIFFS file for playback on power up. + File f = SPIFFS.open(CONFIG, "w"); + if (!f) { + Serial.println("File open failed!"); + } else { + Serial.println("Saving settings now..."); + f.println("UtcOffset=" + String(UtcOffset)); + f.println("printerApiKey=" + PrinterApiKey); + f.println("printerHostName=" + PrinterHostName); + f.println("printerServer=" + PrinterServer); + f.println("printerPort=" + String(PrinterPort)); + f.println("printerName=" + printerClient.getPrinterName()); + f.println("printerAuthUser=" + PrinterAuthUser); + f.println("printerAuthPass=" + PrinterAuthPass); + f.println("refreshRate=" + String(minutesBetweenDataRefresh)); + f.println("themeColor=" + themeColor); + f.println("IS_BASIC_AUTH=" + String(IS_BASIC_AUTH)); + f.println("www_username=" + String(www_username)); + f.println("www_password=" + String(www_password)); + f.println("DISPLAYCLOCK=" + String(DISPLAYCLOCK)); + f.println("is24hour=" + String(IS_24HOUR)); + f.println("invertDisp=" + String(INVERT_DISPLAY)); + f.println("USE_FLASH=" + String(USE_FLASH)); + f.println("isWeather=" + String(DISPLAYWEATHER)); + f.println("weatherKey=" + WeatherApiKey); + f.println("CityID=" + String(CityIDs[0])); + f.println("isMetric=" + String(IS_METRIC)); + f.println("language=" + String(WeatherLanguage)); + f.println("hasPSU=" + String(HAS_PSU)); + } + f.close(); + readSettings(); + timeClient.setUtcOffset(UtcOffset); +} + +void readSettings() { + if (SPIFFS.exists(CONFIG) == false) { + Serial.println("Settings File does not yet exists."); + writeSettings(); + return; + } + File fr = SPIFFS.open(CONFIG, "r"); + String line; + while(fr.available()) { + line = fr.readStringUntil('\n'); + + if (line.indexOf("UtcOffset=") >= 0) { + UtcOffset = line.substring(line.lastIndexOf("UtcOffset=") + 10).toFloat(); + Serial.println("UtcOffset=" + String(UtcOffset)); + } + if (line.indexOf("printerApiKey=") >= 0) { + PrinterApiKey = line.substring(line.lastIndexOf("printerApiKey=") + 14); + PrinterApiKey.trim(); + Serial.println("PrinterApiKey=" + PrinterApiKey); + } + if (line.indexOf("printerHostName=") >= 0) { + PrinterHostName = line.substring(line.lastIndexOf("printerHostName=") + 16); + PrinterHostName.trim(); + Serial.println("PrinterHostName=" + PrinterHostName); + } + if (line.indexOf("printerServer=") >= 0) { + PrinterServer = line.substring(line.lastIndexOf("printerServer=") + 14); + PrinterServer.trim(); + Serial.println("PrinterServer=" + PrinterServer); + } + if (line.indexOf("printerPort=") >= 0) { + PrinterPort = line.substring(line.lastIndexOf("printerPort=") + 12).toInt(); + Serial.println("PrinterPort=" + String(PrinterPort)); + } + if (line.indexOf("printerName=") >= 0) { + String printer = line.substring(line.lastIndexOf("printerName=") + 12); + printer.trim(); + printerClient.setPrinterName(printer); + Serial.println("PrinterName=" + printerClient.getPrinterName()); + } + if (line.indexOf("printerAuthUser=") >= 0) { + PrinterAuthUser = line.substring(line.lastIndexOf("printerAuthUser=") + 16); + PrinterAuthUser.trim(); + Serial.println("PrinterAuthUser=" + PrinterAuthUser); + } + if (line.indexOf("printerAuthPass=") >= 0) { + PrinterAuthPass = line.substring(line.lastIndexOf("printerAuthPass=") + 16); + PrinterAuthPass.trim(); + Serial.println("PrinterAuthPass=" + PrinterAuthPass); + } + if (line.indexOf("refreshRate=") >= 0) { + minutesBetweenDataRefresh = line.substring(line.lastIndexOf("refreshRate=") + 12).toInt(); + Serial.println("minutesBetweenDataRefresh=" + String(minutesBetweenDataRefresh)); + } + if (line.indexOf("themeColor=") >= 0) { + themeColor = line.substring(line.lastIndexOf("themeColor=") + 11); + themeColor.trim(); + Serial.println("themeColor=" + themeColor); + } + if (line.indexOf("IS_BASIC_AUTH=") >= 0) { + IS_BASIC_AUTH = line.substring(line.lastIndexOf("IS_BASIC_AUTH=") + 14).toInt(); + Serial.println("IS_BASIC_AUTH=" + String(IS_BASIC_AUTH)); + } + if (line.indexOf("www_username=") >= 0) { + String temp = line.substring(line.lastIndexOf("www_username=") + 13); + temp.trim(); + temp.toCharArray(www_username, sizeof(temp)); + Serial.println("www_username=" + String(www_username)); + } + if (line.indexOf("www_password=") >= 0) { + String temp = line.substring(line.lastIndexOf("www_password=") + 13); + temp.trim(); + temp.toCharArray(www_password, sizeof(temp)); + Serial.println("www_password=" + String(www_password)); + } + if (line.indexOf("DISPLAYCLOCK=") >= 0) { + DISPLAYCLOCK = line.substring(line.lastIndexOf("DISPLAYCLOCK=") + 13).toInt(); + Serial.println("DISPLAYCLOCK=" + String(DISPLAYCLOCK)); + } + if (line.indexOf("is24hour=") >= 0) { + IS_24HOUR = line.substring(line.lastIndexOf("is24hour=") + 9).toInt(); + Serial.println("IS_24HOUR=" + String(IS_24HOUR)); + } + if(line.indexOf("invertDisp=") >= 0) { + INVERT_DISPLAY = line.substring(line.lastIndexOf("invertDisp=") + 11).toInt(); + Serial.println("INVERT_DISPLAY=" + String(INVERT_DISPLAY)); + } + if(line.indexOf("USE_FLASH=") >= 0) { + USE_FLASH = line.substring(line.lastIndexOf("USE_FLASH=") + 10).toInt(); + Serial.println("USE_FLASH=" + String(USE_FLASH)); + } + if (line.indexOf("hasPSU=") >= 0) { + HAS_PSU = line.substring(line.lastIndexOf("hasPSU=") + 7).toInt(); + Serial.println("HAS_PSU=" + String(HAS_PSU)); + } + if (line.indexOf("isWeather=") >= 0) { + DISPLAYWEATHER = line.substring(line.lastIndexOf("isWeather=") + 10).toInt(); + Serial.println("DISPLAYWEATHER=" + String(DISPLAYWEATHER)); + } + if (line.indexOf("weatherKey=") >= 0) { + WeatherApiKey = line.substring(line.lastIndexOf("weatherKey=") + 11); + WeatherApiKey.trim(); + Serial.println("WeatherApiKey=" + WeatherApiKey); + } + if (line.indexOf("CityID=") >= 0) { + CityIDs[0] = line.substring(line.lastIndexOf("CityID=") + 7).toInt(); + Serial.println("CityID: " + String(CityIDs[0])); + } + if (line.indexOf("isMetric=") >= 0) { + IS_METRIC = line.substring(line.lastIndexOf("isMetric=") + 9).toInt(); + Serial.println("IS_METRIC=" + String(IS_METRIC)); + } + if (line.indexOf("language=") >= 0) { + WeatherLanguage = line.substring(line.lastIndexOf("language=") + 9); + WeatherLanguage.trim(); + Serial.println("WeatherLanguage=" + WeatherLanguage); + } + } + fr.close(); + printerClient.updatePrintClient(PrinterApiKey, PrinterServer, PrinterPort, PrinterAuthUser, PrinterAuthPass, HAS_PSU); + weatherClient.updateWeatherApiKey(WeatherApiKey); + weatherClient.updateLanguage(WeatherLanguage); + weatherClient.setMetric(IS_METRIC); + weatherClient.updateCityIdList(CityIDs, 1); + timeClient.setUtcOffset(UtcOffset); +} + +int getMinutesFromLastRefresh() { + int minutes = (timeClient.getCurrentEpoch() - lastEpoch) / 60; + return minutes; +} + +int getMinutesFromLastDisplay() { + int minutes = (timeClient.getCurrentEpoch() - displayOffEpoch) / 60; + return minutes; +} + +// Toggle on and off the display if user defined times +void checkDisplay() { + if (!displayOn && DISPLAYCLOCK) { + enableDisplay(true); + } + if (displayOn && !printerClient.isPrinting() && !DISPLAYCLOCK) { + // Put Display to sleep + display.clear(); + display.display(); + display.setFont(ArialMT_Plain_16); + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.setContrast(255); // default is 255 + display.drawString(64, 5, "Printer Offline\nSleep Mode..."); + display.display(); + delay(5000); + enableDisplay(false); + Serial.println("Printer is offline going down to sleep..."); + return; + } else if (!displayOn && !DISPLAYCLOCK) { + if (printerClient.isOperational()) { + // Wake the Screen up + enableDisplay(true); + display.clear(); + display.display(); + display.setFont(ArialMT_Plain_16); + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.setContrast(255); // default is 255 + display.drawString(64, 5, "Printer Online\nWake up..."); + display.display(); + Serial.println("Printer is online waking up..."); + delay(5000); + return; + } + } else if (DISPLAYCLOCK) { + if ((!printerClient.isPrinting() || printerClient.isPSUoff()) && !isClockOn) { + Serial.println("Clock Mode is turned on."); + if (!DISPLAYWEATHER) { + ui.disableAutoTransition(); + ui.setFrames(clockFrame, 1); + clockFrame[0] = drawClock; + } else { + ui.enableAutoTransition(); + ui.setFrames(clockFrame, 2); + clockFrame[0] = drawClock; + clockFrame[1] = drawWeather; + } + ui.setOverlays(clockOverlay, numberOfOverlays); + isClockOn = true; + } else if (printerClient.isPrinting() && !printerClient.isPSUoff() && isClockOn) { + Serial.println("Printer Monitor is active."); + ui.setFrames(frames, numberOfFrames); + ui.setOverlays(overlays, numberOfOverlays); + ui.enableAutoTransition(); + isClockOn = false; + } + } +} + +void enableDisplay(boolean enable) { + displayOn = enable; + if (enable) { + if (getMinutesFromLastDisplay() >= minutesBetweenDataRefresh) { + // The display has been off longer than the minutes between refresh -- need to get fresh data + lastEpoch = 0; // this should force a data pull + displayOffEpoch = 0; // reset + } + display.displayOn(); + Serial.println("Display was turned ON: " + timeClient.getFormattedTime()); + } else { + display.displayOff(); + Serial.println("Display was turned OFF: " + timeClient.getFormattedTime()); + displayOffEpoch = lastEpoch; + } +}