Initial Upload for 1.0 release

pull/1/head
Qrome 2018-05-01 18:19:11 -07:00 committed by GitHub
parent ea7f478c27
commit 3372520982
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1502 additions and 0 deletions

96
README.md Normal file
View File

@ -0,0 +1,96 @@
/* 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)
## Features:
* Displays the print status from OctoPrint Server
* Estimated time remaining
* Time Printing
* Percentage complete
* Progress bar
* Screen turns off when printer is turned off or disconnected
* Screen turns on when printer is Operational or connected
* Sample rate is every 60 seconds when not printing
* Sample rate is every 10 seconds when printing
* Fully configurable from the web interface (not required to update Settings.h)
* Supports OTA (loading firmware over WiFi connection on same LAN)
* Basic Athentication to protect your settings
## Required Parts:
* Wemos D1 Mini: https://amzn.to/2qLyKJd
* 0.96" OLED I2C 128x64 Display (12864) SSD1306: https://amzn.to/2JDEAUF
## Wiring for the Wemos D1 Mini to the I2C SSD1306 OLED
SDA -> D2
SCL -> D5
VCC -> 5V+
GND -> GND-
## 3D Printed Case by Qrome:
https://www.thingiverse.com/thing:2884823
## 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.
* USB CH340G drivers: https://wiki.wemos.cc/downloads
* Enter http://arduino.esp8266.com/stable/package_esp8266com_index.json into Additional Board Manager URLs field. You can add multiple URLs, separating them with commas. This will add support for the Wemos D1 Mini to Arduino IDE.
* Open Boards Manager from Tools > Board menu and install esp8266 platform (and don't forget to select your ESP8266 board from Tools > Board menu after installation).
* Select Board: "WeMos D1 R2 & mini"
**Packages** -- the following packages and libraries are used (download and install):
ESP8266WiFi.h
ESP8266WebServer.h
ArduinoJson.h --> https://github.com/bblanchon/ArduinoJson
WiFiManager.h --> https://github.com/tzapu/WiFiManager
ESP8266mDNS.h
ArduinoOTA.h --> Arduino OTA Library
"SSD1306Wire.h" --> https://github.com/ThingPulse/esp8266-oled-ssd1306
"OLEDDisplayUi.h"
## Initial Configuration
You will can update the **Settings.h** file with your OctoPrint API Key or do it from the web interface.
* Your OctoPrint API Key from your OctoPrint -> User Settings -> Current API Key
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.
## Web Interface
The Marquee Scroller uses the **WiFiManager** so when it can't find the last network it was connected to
it will become a **AP Hotspot** -- connect to it with your phone and you can then enter your WiFi connection information.
After connected to your WiFi network it will display the IP addressed assigned to it and that can be
used to open a browser to the Web Interface. Everything can be configured there.
<p align="center">
<img src="/images/shot_01.png" width="200"/>
<img src="/images/shot_02.png" width="200"/>
<img src="/images/shot_03.png" width="200"/>
<img src="/images/shot_04.png" width="200"/>
</p>
## Contributors
David Payne
Daniel Eichhorn -- Author of the TimeClient class and OLEDDisplayUi
![Printer Monitor Temps](/images/temperatures.jpg)
![Printer Monitor Time Remaining](/images/time_remaining.jpg)
![Printer Monitor Printing Time](/images/printing_time.jpg)

BIN
images/Thumbs.db Normal file

Binary file not shown.

BIN
images/printing_time.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

BIN
images/shot_01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

BIN
images/shot_02.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

BIN
images/shot_03.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

BIN
images/shot_04.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

BIN
images/temperatures.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

BIN
images/time_remaining.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

View File

@ -0,0 +1,254 @@
/** 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.
*/
#include "OctoPrintClient.h"
OctoPrintClient::OctoPrintClient(String ApiKey, String server, int port) {
updateOctoPrintClient(ApiKey, server, port);
}
void OctoPrintClient::updateOctoPrintClient(String ApiKey, String server, int port) {
server.toCharArray(myServer, 100);
myApiKey = ApiKey;
myPort = port;
}
boolean OctoPrintClient::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 OctoPrintClient::getSubmitRequest(String apiGetData) {
WiFiClient printClient;
printClient.setTimeout(5000);
Serial.println("Getting Octoprint Data");
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);
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();
printerData.error = "Connection to " + String(myServer) + ":" + String(myPort) + " failed.";
printerData.state = "";
return printClient;
}
}
else {
Serial.println("Connection to OctoPrint failed: " + String(myServer) + ":" + String(myPort)); //error message if no client connect
Serial.println();
printerData.error = "Connection to OctoPrint failed: " + String(myServer) + ":" + String(myPort);
printerData.state = "";
return printClient;
}
// Check HTTP status
char status[32] = {0};
printClient.readBytesUntil('\r', status, sizeof(status));
if (strcmp(status, "HTTP/1.1 200 OK") != 0 && strcmp(status, "HTTP/1.1 409 CONFLICT") != 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 OctoPrintClient::getPrinterJobResults() {
if (!validate()) {
return;
}
String apiGetData = "GET /api/job HTTP/1.1";
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
JsonObject& root = jsonBuffer.parseObject(printClient);
if (!root.success()) {
Serial.println("OctoPrint Data Parsing failed: " + String(myServer) + ":" + String(myPort));
printerData.error = "OctoPrint Data Parsing failed: " + String(myServer) + ":" + String(myPort);
printerData.state = "";
return;
}
printerData.averagePrintTime = (const char*)root["job"]["averagePrintTime"];
printerData.estimatedPrintTime = (const char*)root["job"]["estimatedPrintTime"];
printerData.fileName = (const char*)root["job"]["file"]["name"];
printerData.fileSize = (const char*)root["job"]["file"]["size"];
printerData.lastPrintTime = (const char*)root["job"]["lastPrintTime"];
printerData.progressCompletion = (const char*)root["progress"]["completion"];
printerData.progressFilepos = (const char*)root["progress"]["filepos"];
printerData.progressPrintTime = (const char*)root["progress"]["printTime"];
printerData.progressPrintTimeLeft = (const char*)root["progress"]["printTimeLeft"];
printerData.filamentLength = (const char*)root["job"]["filament"]["tool0"]["length"];
printerData.state = (const char*)root["state"];
if (isOperational()) {
Serial.println("Status: " + printerData.state);
} else {
Serial.println("Printer Not Opperational");
}
//**** get the Printer Temps and Stat
apiGetData = "GET /api/printer?exclude=sd,history HTTP/1.1";
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);
if (!root2.success()) {
printerData.isPrinting = false;
printerData.toolTemp = "";
printerData.toolTargetTemp = "";
printerData.bedTemp = "";
printerData.bedTargetTemp = (const char*)root2["temperature"]["bed"]["target"];
return;
}
String printing = (const char*)root2["state"]["flags"]["printing"];
if (printing == "true") {
printerData.isPrinting = true;
}
printerData.toolTemp = (const char*)root2["temperature"]["tool0"]["actual"];
printerData.toolTargetTemp = (const char*)root2["temperature"]["tool0"]["target"];
printerData.bedTemp = (const char*)root2["temperature"]["bed"]["actual"];
printerData.bedTargetTemp = (const char*)root2["temperature"]["bed"]["target"];
if (isPrinting()) {
Serial.println("Status: " + printerData.state + " " + printerData.fileName + "(" + printerData.progressCompletion + "%)");
}
printClient.stop(); //stop client
}
String OctoPrintClient::getAveragePrintTime(){
return printerData.averagePrintTime;
}
String OctoPrintClient::getEstimatedPrintTime() {
return printerData.estimatedPrintTime;
}
String OctoPrintClient::getFileName() {
return printerData.fileName;
}
String OctoPrintClient::getFileSize() {
return printerData.fileSize;
}
String OctoPrintClient::getLastPrintTime(){
return printerData.lastPrintTime;
}
String OctoPrintClient::getProgressCompletion() {
return String(printerData.progressCompletion.toInt());
}
String OctoPrintClient::getProgressFilepos() {
return printerData.progressFilepos;
}
String OctoPrintClient::getProgressPrintTime() {
return printerData.progressPrintTime;
}
String OctoPrintClient::getProgressPrintTimeLeft() {
return printerData.progressPrintTimeLeft;
}
String OctoPrintClient::getState() {
return printerData.state;
}
boolean OctoPrintClient::isPrinting() {
return printerData.isPrinting;
}
boolean OctoPrintClient::isOperational() {
boolean operational = false;
if (printerData.state == "Operational" || isPrinting()) {
operational = true;
}
return operational;
}
String OctoPrintClient::getTempBedActual() {
return printerData.bedTemp;
}
String OctoPrintClient::getTempBedTarget() {
return printerData.bedTargetTemp;
}
String OctoPrintClient::getTempToolActual() {
return printerData.toolTemp;
}
String OctoPrintClient::getTempToolTarget() {
return printerData.toolTargetTemp;
}
String OctoPrintClient::getFilamentLength() {
return printerData.filamentLength;
}
String OctoPrintClient::getError() {
return printerData.error;
}

View File

@ -0,0 +1,87 @@
/** 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.
*/
#pragma once
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
class OctoPrintClient {
private:
char myServer[100];
int myPort = 80;
String myApiKey = "";
boolean validate();
WiFiClient getSubmitRequest(String apiGetData);
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;
String error;
} PrinterStruct;
PrinterStruct printerData;
public:
OctoPrintClient(String ApiKey, String server, int port);
void getPrinterJobResults();
void updateOctoPrintClient(String ApiKey, String server, int port);
String getAveragePrintTime();
String getEstimatedPrintTime();
String getFileName();
String getFileSize();
String getLastPrintTime();
String getProgressCompletion();
String getProgressFilepos();
String getProgressPrintTime();
String getProgressPrintTimeLeft();
String getState();
boolean isPrinting();
boolean isOperational();
String getTempBedActual();
String getTempBedTarget();
String getTempToolActual();
String getTempToolTarget();
String getFilamentLength();
String getError();
};

76
printermonitor/Settings.h Normal file
View File

@ -0,0 +1,76 @@
/** 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 is designed for the Wemos D1 ESP8266
* Wemos D1 Mini: https://amzn.to/2qLyKJd
* 0.96" OLED I2C 128x64 Display (12864) SSD1306
* OLED Display: https://amzn.to/2JDEAUF
******************************************************************************/
/******************************************************************************
* NOTE: The settings here 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.
******************************************************************************/
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
#include <ESP8266mDNS.h>
#include <ArduinoOTA.h>
#include "TimeClient.h"
#include "OctoPrintClient.h"
#include "FS.h"
#include "SSD1306Wire.h"
#include "OLEDDisplayUi.h"
//******************************
// Start Settings
//******************************
// OctoPrint Monitoring -- Monitor your 3D printer OctoPrint Server
String OctoPrintApiKey = ""; // ApiKey from your User Account on OctoPrint
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);
const int WEBSERVER_PORT = 80; // The port you can access this device on over HTTP
const boolean WEBSERVER_ENABLED = true; // Device will provide a web interface via http://[ip]:[port]/
char* www_username = "admin"; // User account for the Web Interface
char* www_password = "password"; // Password for the Web Interface
float UtcOffset = -7; // Hour offset from GMT for your timezone
int minutesBetweenDataRefresh = 60;
// 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;
boolean ENABLE_OTA = true; // this will allow you to load firmware to the device over WiFi (see OTA for ESP8266)
//******************************
// End Settings
//******************************
String themeColor = "light-green"; // this can be changed later in the web interface.

View File

@ -0,0 +1,152 @@
/**The MIT License (MIT)
Copyright (c) 2015 by Daniel Eichhorn
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.
*/
/*
Modified by David Payne for use in the Scrolling Marquee
*/
#include "TimeClient.h"
TimeClient::TimeClient(float utcOffset) {
myUtcOffset = utcOffset;
}
void TimeClient::updateTime() {
WiFiClient client;
if (!client.connect(ntpServerName, httpPort)) {
Serial.println("connection failed");
return;
}
// This will send the request to the server
client.print(String("GET / HTTP/1.1\r\n") +
String("Host: www.google.com\r\n") +
String("Connection: close\r\n\r\n"));
int repeatCounter = 0;
while(!client.available() && repeatCounter < 10) {
delay(1000);
Serial.println(".");
repeatCounter++;
}
String line;
int size = 0;
client.setNoDelay(false);
while(client.connected()) {
while((size = client.available()) > 0) {
line = client.readStringUntil('\n');
line.toUpperCase();
// example:
// date: Thu, 19 Nov 2015 20:25:40 GMT
if (line.startsWith("DATE: ")) {
Serial.println(line.substring(23, 25) + ":" + line.substring(26, 28) + ":" +line.substring(29, 31));
int parsedHours = line.substring(23, 25).toInt();
int parsedMinutes = line.substring(26, 28).toInt();
int parsedSeconds = line.substring(29, 31).toInt();
Serial.println(String(parsedHours) + ":" + String(parsedMinutes) + ":" + String(parsedSeconds));
localEpoc = (parsedHours * 60 * 60 + parsedMinutes * 60 + parsedSeconds);
Serial.println(localEpoc);
localMillisAtUpdate = millis();
client.stop();
}
}
}
}
void TimeClient::setUtcOffset(float utcOffset) {
myUtcOffset = utcOffset;
}
String TimeClient::getHours() {
if (localEpoc == 0) {
return "--";
}
int hours = ((getCurrentEpochWithUtcOffset() % 86400L) / 3600) % 24;
if (hours < 10) {
return "0" + String(hours);
}
return String(hours); // print the hour (86400 equals secs per day)
}
String TimeClient::getMinutes() {
if (localEpoc == 0) {
return "--";
}
int minutes = ((getCurrentEpochWithUtcOffset() % 3600) / 60);
if (minutes < 10 ) {
// In the first 10 minutes of each hour, we'll want a leading '0'
return "0" + String(minutes);
}
return String(minutes);
}
String TimeClient::getSeconds() {
if (localEpoc == 0) {
return "--";
}
int seconds = getCurrentEpochWithUtcOffset() % 60;
if ( seconds < 10 ) {
// In the first 10 seconds of each minute, we'll want a leading '0'
return "0" + String(seconds);
}
return String(seconds);
}
String TimeClient::getAmPmHours() {
int hours = getHours().toInt();
if (hours >= 13) {
hours = hours - 12;
}
if (hours == 0) {
hours = 12;
}
return String(hours);
}
String TimeClient::getAmPm() {
int hours = getHours().toInt();
String ampmValue = "AM";
if (hours >= 12) {
ampmValue = "PM";
}
return ampmValue;
}
String TimeClient::getFormattedTime() {
return getHours() + ":" + getMinutes() + ":" + getSeconds();
}
String TimeClient::getAmPmFormattedTime() {
return getAmPmHours() + ":" + getMinutes() + " " + getAmPm();
}
long TimeClient::getCurrentEpoch() {
return localEpoc + ((millis() - localMillisAtUpdate) / 1000);
}
long TimeClient::getCurrentEpochWithUtcOffset() {
return round(getCurrentEpoch() + 3600 * myUtcOffset + 86400L) % 86400L;
}

View File

@ -0,0 +1,62 @@
/**The MIT License (MIT)
Copyright (c) 2015 by Daniel Eichhorn
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.
See more at http://blog.squix.ch
*/
/*
Modified by David Payne for use in the Scrolling Marquee
*/
#pragma once
#include <ESP8266WiFi.h>
#define NTP_PACKET_SIZE 48
class TimeClient {
private:
float myUtcOffset = 0;
long localEpoc = 0;
long localMillisAtUpdate;
const char* ntpServerName = "www.google.com";
const int httpPort = 80;
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
public:
TimeClient(float utcOffset);
void updateTime();
void setUtcOffset(float utcOffset);
String getHours();
String getAmPmHours();
String getAmPm();
String getMinutes();
String getSeconds();
String getFormattedTime();
String getAmPmFormattedTime();
long getCurrentEpoch();
long getCurrentEpochWithUtcOffset();
};

View File

@ -0,0 +1,775 @@
/** 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.
*/
/**********************************************
* Edit Settings.h for personalization
***********************************************/
#include "Settings.h"
#define VERSION "1.0"
#define HOSTNAME "ESP8266-"
#define CONFIG "/conf.txt"
/* Useful Constants */
#define SECS_PER_MIN (60UL)
#define SECS_PER_HOUR (3600UL)
#define SECS_PER_DAY (SECS_PER_HOUR * 24L)
/* 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_DAY) / SECS_PER_HOUR)
#define elapsedDays(_time_) ( _time_ / SECS_PER_DAY)
// Initialize the oled display for I2C_DISPLAY_ADDRESS
// SDA_PIN and SCL_PIN
SSD1306Wire display(I2C_DISPLAY_ADDRESS, SDA_PIN, SCL_PIN);
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);
// Set the number of Frames supported
const int numberOfFrames = 3;
FrameCallback frames[numberOfFrames];
OverlayCallback overlays[] = { drawHeaderOverlay };
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);
int printerCount = 0;
//declairing prototypes
void configModeCallback (WiFiManager *myWiFiManager);
int8_t getWifiQuality();
ESP8266WebServer server(WEBSERVER_PORT);
const String WEB_ACTIONS = "<a class='w3-bar-item w3-button' href='/'><i class='fa fa-home'></i> Home</a>"
"<a class='w3-bar-item w3-button' href='/configure'><i class='fa fa-cog'></i> Configure</a>"
"<a class='w3-bar-item w3-button' href='/systemreset' onclick='return confirm(\"Do you want to reset to default settings?\")'><i class='fa fa-undo'></i> Reset Settings</a>"
"<a class='w3-bar-item w3-button' href='/forgetwifi' onclick='return confirm(\"Do you want to forget to WiFi connection?\")'><i class='fa fa-wifi'></i> Forget WiFi</a>"
"<a class='w3-bar-item w3-button' href='https://github.com/Qrome' target='_blank'><i class='fa fa-question-circle'></i> About</a>";
const String CHANGE_FORM = "<form class='w3-container' action='/updateconfig' method='get'><h2>Station Config:</h2>"
"<label>OctoPrint API Key (get from your server)</label><input class='w3-input w3-border w3-margin-bottom' type='text' name='octoPrintApiKey' value='%OCTOKEY%' maxlength='60'>"
"<label>OctoPrint Address (do not include http://)</label><input class='w3-input w3-border w3-margin-bottom' type='text' name='octoPrintAddress' value='%OCTOADDRESS%' maxlength='60'>"
"<label>OctoPrint Port</label><input class='w3-input w3-border w3-margin-bottom' type='text' name='octoPrintPort' value='%OCTOPORT%' maxlength='5' onkeypress='return isNumberKey(event)'>"
"Time Refresh (minutes) <select class='w3-option w3-padding' name='refresh'>%OPTIONS%</select></p>"
"Theme Color <select class='w3-option w3-padding' name='theme'>%THEME_OPTIONS%</select></p>"
"<label>UTC Time Offset</label><input class='w3-input w3-border w3-margin-bottom' type='text' name='utcoffset' value='%UTCOFFSET%' maxlength='12'>"
"<label>User ID (for this interface)</label><input class='w3-input w3-border w3-margin-bottom' type='text' name='userid' value='%USERID%' maxlength='20'>"
"<label>Password </label><input class='w3-input w3-border w3-margin-bottom' type='password' name='stationpassword' value='%STATIONPASSWORD%'>"
"<button class='w3-button w3-block w3-grey w3-section w3-padding' type='submit'>Save</button></form>";
const String COLOR_THEMES = "<option>red</option>"
"<option>pink</option>"
"<option>purple</option>"
"<option>deep-purple</option>"
"<option>indigo</option>"
"<option>blue</option>"
"<option>light-blue</option>"
"<option>cyan</option>"
"<option>teal</option>"
"<option>green</option>"
"<option>light-green</option>"
"<option>lime</option>"
"<option>khaki</option>"
"<option>yellow</option>"
"<option>amber</option>"
"<option>orange</option>"
"<option>deep-orange</option>"
"<option>blue-grey</option>"
"<option>brown</option>"
"<option>grey</option>"
"<option>dark-grey</option>"
"<option>black</option>"
"<option>w3schools</option>";
// Change the externalLight to the pin you wish to use if other than the Built-in LED
int externalLight = LED_BUILTIN; // LED_BUILTIN is is the built in LED on the Wemos
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 dispaly
display.init();
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);
//or use this for auto generated name ESP + ChipID
wifiManager.autoConnect();
//Manual Wifi
String hostname(HOSTNAME);
hostname += String(ESP.getChipId(), HEX);
WiFi.hostname(hostname);
int cnt = 0;
while (WiFi.status() != WL_CONNECTED) {
digitalWrite(externalLight, LOW);
delay(500);
Serial.print(".");
cnt++;
digitalWrite(externalLight, HIGH);
}
// 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;
ui.setOverlays(overlays, numberOfOverlays);
// Inital UI takes care of initalising the display too.
ui.init();
// 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.begin();
}
if (WEBSERVER_ENABLED) {
server.on("/", displayPrinterStatus);
server.on("/systemreset", handleSystemReset);
server.on("/forgetwifi", handleWifiReset);
server.on("/updateconfig", handleUpdateConfig);
server.on("/configure", handleConfigure);
server.onNotFound(redirectHome);
// 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);
}
//************************************************************
// 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();
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();
digitalWrite(externalLight, HIGH);
}
}
checkDisplay(); // this will see if we need to turn it on or off for night mode.
ui.update();
if (WEBSERVER_ENABLED) {
server.handleClient();
}
if (ENABLE_OTA) {
ArduinoOTA.handle();
}
}
void getUpdateTime() {
digitalWrite(externalLight, LOW); // turn on the LED
Serial.println();
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
}
void handleSystemReset() {
if (!server.authenticate(www_username, www_password)) {
return server.requestAuthentication();
}
Serial.println("Reset System Configuration");
if (SPIFFS.remove(CONFIG)) {
redirectHome();
ESP.restart();
}
}
void handleUpdateConfig() {
if (!server.authenticate(www_username, www_password)) {
return server.requestAuthentication();
}
OctoPrintApiKey = server.arg("octoPrintApiKey");
OctoPrintServer = server.arg("octoPrintAddress");
OctoPrintPort = server.arg("octoPrintPort").toInt();
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();
lastEpoch = 0;
redirectHome();
}
void handleWifiReset() {
if (!server.authenticate(www_username, www_password)) {
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 handleConfigure() {
if (!server.authenticate(www_username, www_password)) {
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 = String(CHANGE_FORM);
form.replace("%OCTOKEY%", OctoPrintApiKey);
form.replace("%OCTOADDRESS%", OctoPrintServer);
form.replace("%OCTOPORT%", String(OctoPrintPort));
String options = "<option>10</option><option>15</option><option>20</option><option>30</option><option>60</option>";
options.replace(">"+String(minutesBetweenDataRefresh)+"<", " selected>"+String(minutesBetweenDataRefresh)+"<");
form.replace("%OPTIONS%", options);
String themeOptions = String(COLOR_THEMES);
themeOptions.replace(">"+String(themeColor)+"<", " selected>"+String(themeColor)+"<");
form.replace("%THEME_OPTIONS%", themeOptions);
form.replace("%UTCOFFSET%", String(UtcOffset));
form.replace("%USERID%", String(www_username));
form.replace("%STATIONPASSWORD%", String(www_password));
server.sendContent(String(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() {
String menu = String(WEB_ACTIONS);
String html = "<!DOCTYPE HTML>";
html += "<html><head><title>Printer Monitor</title><link rel='icon' href='data:;base64,='>";
html += "<meta name='viewport' content='width=device-width, initial-scale=1'>";
html += "<link rel='stylesheet' href='https://www.w3schools.com/w3css/4/w3.css'>";
html += "<link rel='stylesheet' href='https://www.w3schools.com/lib/w3-theme-" + themeColor + ".css'>";
html += "<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css'>";
html += "</head><body>";
html += "<nav class='w3-sidebar w3-bar-block w3-card' style='margin-top:88px' id='mySidebar'>";
html += "<div class='w3-container w3-theme-d2'>";
html += "<span onclick='closeSidebar()' class='w3-button w3-display-topright w3-large'><i class='fa fa-times'></i></span>";
html += "<div class='w3-cell w3-left w3-xxxlarge' style='width:60px'><i class='fa fa-cube'></i></div>";
html += "<div class='w3-padding'>Menu</div></div>";
html += menu;
html += "</nav>";
html += "<header class='w3-top w3-bar w3-theme'><button class='w3-bar-item w3-button w3-xxxlarge w3-hover-theme' onclick='openSidebar()'><i class='fa fa-bars'></i></button><h2 class='w3-bar-item'>Printer Monitor</h2></header>";
html += "<script>";
html += "function openSidebar(){document.getElementById('mySidebar').style.display='block'}function closeSidebar(){document.getElementById('mySidebar').style.display='none'}closeSidebar();";
html += "</script>";
html += "<br><div class='w3-container w3-large' style='margin-top:88px'>";
return html;
}
String getFooter() {
int8_t rssi = getWifiQuality();
Serial.print("Signal Strength (RSSI): ");
Serial.print(rssi);
Serial.println("%");
String html = "<br><br><br>";
html += "</div>";
html += "<footer class='w3-container w3-bottom w3-theme w3-margin-top'>";
if (lastReportStatus != "") {
html += "<i class='fa fa-external-link'></i> Report Status: " + lastReportStatus + "<br>";
}
html += "<i class='fa fa-paper-plane-o'></i> Version: " + String(VERSION) + "<br>";
html += "<i class='fa fa-rss'></i> Signal Strength: ";
html += String(rssi) + "%";
html += "</footer>";
html += "</body></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()));
html += "<div class='w3-cell-row' style='width:100%'><h2>Time: " + timeClient.getAmPmHours() + ":" + timeClient.getMinutes() + ":" + timeClient.getSeconds() + " " + timeClient.getAmPm() + "</h2></div><div class='w3-cell-row'>";
html += "<div class='w3-cell w3-container' style='width:100%'><p>";
if (printerClient.getError() != "") {
html += "Error: " + printerClient.getError() + "<br>";
}
html += "Status: " + printerClient.getState() + "<br>";
html += "File: " + printerClient.getFileName() + "<br>";
float fileSize = printerClient.getFileSize().toFloat();
if (fileSize > 0) {
fileSize = fileSize / 1024;
html += "File Size: " + String(fileSize) + "KB<br>";
}
int filamentLength = printerClient.getFilamentLength().toInt();
if (filamentLength > 0) {
float fLength = float(filamentLength) / 1000;
html += "Filament: " + String(fLength) + "m<br>";
}
html += "Tool Temperature: " + printerClient.getTempToolActual() + " C<br>";
html += "Bed Temperature: " + printerClient.getTempBedActual() + " C<br>";
int val = printerClient.getProgressPrintTimeLeft().toInt();
int days = elapsedDays(val);
int hours = numberOfHours(val);
int minutes = numberOfMinutes(val);
int seconds = numberOfSeconds(val);
html += "Est. Print Time Left: " + zeroPad(hours) + ":" + zeroPad(minutes) + ":" + zeroPad(seconds) + "<br>";
val = printerClient.getProgressPrintTime().toInt();
days = elapsedDays(val);
hours = numberOfHours(val);
minutes = numberOfMinutes(val);
seconds = numberOfSeconds(val);
html += "Printing Time: " + zeroPad(hours) + ":" + zeroPad(minutes) + ":" + zeroPad(seconds) + "<br>";
html += "<style>#myProgress {width: 100%;background-color: #ddd;}#myBar {width: " + printerClient.getProgressCompletion() + "%;height: 30px;background-color: #4CAF50;}</style>";
html += "<div id=\"myProgress\"><div id=\"myBar\" class=\"w3-medium w3-center\">" + printerClient.getProgressCompletion() + "%</div></div>";
html += "</p></div></div>";
server.sendContent(String(html)); // spit out what we got
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());
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) {
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(ArialMT_Plain_16);
display->drawString(64 + x, 0 + y, "Bed / Tool Temp");
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(ArialMT_Plain_24);
int bed = printerClient.getTempBedActual().toInt();
int tool = printerClient.getTempToolActual().toInt();
display->drawString(2 + x, 14 + y, String(bed));
display->drawString(64 + x, 14 + y, String(tool));
display->setFont(ArialMT_Plain_16);
display->drawString(38 + x, 14 + y, "C");
display->drawString(115 + x, 14 + y, "C");
}
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 days = elapsedDays(val);
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 days = elapsedDays(val);
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);
}
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 time = timeClient.getAmPmHours() + ":" + timeClient.getMinutes();
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->drawString(0, 48, time);
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);
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("octoServer=" + OctoPrintServer);
f.println("octoPort=" + String(OctoPrintPort));
f.println("refreshRate=" + String(minutesBetweenDataRefresh));
f.println("themeColor=" + themeColor);
f.println("www_username=" + String(www_username));
f.println("www_password=" + String(www_password));
}
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("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("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("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));
}
}
fr.close();
timeClient.setUtcOffset(UtcOffset);
printerClient.updateOctoPrintClient(OctoPrintApiKey, OctoPrintServer, OctoPrintPort);
printerClient.getPrinterJobResults();
}
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 && !(printerClient.isOperational() || printerClient.isPrinting())) {
// 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);
return;
} else if (!displayOn) {
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();
delay(5000);
return;
}
}
}
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;
}
}