Description
Basic Infos
- [ X ] This issue complies with the issue POLICY doc.
- [ X ] I have read the documentation at readthedocs and the issue is not addressed there.
- [ X ] I have tested that the issue is present in current master branch (aka latest git).
- [ X ] I have searched the issue tracker for a similar issue.
- If there is a stack dump, I have decoded it.
- [ X ] I have filled out all fields below.
Platform
- Hardware: [LoLin NodeMCU V3]
- Core Version: [SDK:2.2.1(cfd48f3)/Core:win-2.5.0-dev/lwIP:2.0.3(STABLE-2_0_3_RELEASE/glue:arduino-2.4.1-13-g163bb82)/BearSSL:94e9704]
- Development Env: [Arduino IDE]
- Operating System: [Windows]
Settings in IDE
- Module: [Nodemcu 1.0]
- Flash Mode: [qio|dio|other]
- Flash Size: [4MB]
- lwip Variant: [v2 Lower Memory]
- Reset Method: [ck|nodemcu]
- Flash Frequency: [40Mhz]
- CPU Frequency: [80Mhz|]
- Upload Using: [SERIAL]
- Upload Speed: [115200]
Problem Description
I have been chasing some random WDT issues for about a week and it appears that the problem is caused when you have a really low WiFi signal or an unstable connection.
My one was running at levels in the -83 down to -91 at times, I was not expecting it to even work that low but to my surprise what seems to happen is the WDT triggers a reboot.
I was receiving both of these at various times.
The only error i seem to be getting is
ets Jan 8 2013,rst cause:4, boot mode:(1,7)
wdt reset
ets Jan 8 2013,rst cause:4, boot mode:(1,6)
wdt reset
I did have a few stack dumps however I was not able to get them converted using the exception decider for some reason and since moving the Nodemcu I don't seem to have received any more.
Initial thought were it may have been caused by power issues, I have installed a 3.3v supply with a 1000uf cap and know the supply will quite happily deliver up to about 2A so that should be more than enough. In addition the control systems for the Fan etc have been optically isolated just to be sure that there were no feedback issues.
The code for this project is quite sizable and has bits all over the place at the moment as it is a work in progress so please keep that in mind. :)
C Sketch
// include the lib's for the File System
#include <FS.h>
#include <ArduinoJson.h>
// Include the libs for the Sensors
#include "DHTesp.h"
#include <OneWire.h>
#include <DallasTemperature.h>
// Include the lib's for timer related stuff
#include <Time.h>
#include <TimeLib.h>
#include <Ticker.h>
// Include the lib's for WiFi
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ESP8266mDNS.h>
#include <ESP8266HTTPUpdateServer.h>
// Include the lib's for PID Controll
#include <PID_v1.h>
#include <PID_AutoTune_v0.h>
//Include Other libs
#include <math.h>
// Set up the ticke's for the various timed routines
Ticker prn; //Ticler for the print to serial port
Ticker udtemp; //Ticker to read and update the temperature from the Dallas 1 wire sensors
Ticker doPID; //Ticker for the PID controll of Fan and Heater
Ticker prnLog; //Ticker to Print and Output the log
Ticker DHTSensor; //Ticker to Read the DHT Sensor
//IPAddress apIP(192, 168, 4, 1); // Defining a static IP address: local & gateway
// ESSID and Password of the WiFi Access Point to connect to, and Set out Hostname.
//const char *ssid = "ESP8266";
//const char *password = "testesp1";
//const char *host = "Cloche-Heater";
const char *ssid = "ESP8266"; //If this device is set up as an AP this is the ESSID
const char *password = "testesp1"; //If this device is set up as an AP this is the password
const char *host = "Cloche-Heater"; //Host Name of this device
const char *apid = "MyESSID"; //ESSID of the AP to connect to
const char *appass = "MyPassword"; //Password of the AP to connect to
// Define a web server at port 80 for HTTP
ESP8266WebServer server(80);
ESP8266HTTPUpdateServer httpUpdater;
// Define the pins used for the sensors and controll's
#define FAN D5 //PWM Fan Pin
#define HEATER D6 //PWM Heater Pin
#define TEMPERATURE_BUS D4 //Dallas 1 wire Temperature Sensors
#define TEMPERATURE_PRECISION 12 //Dallas 1 wire Temperature Sensor Precision
#define SOILSENS A0 //Analog pin for Soil Moisture Sensor
#define DHT_PIN D1 //DHT Humidity/Temperature Sensor
//If we are going to update set this to 1 and halt all other processes.
bool OTAupdate = 0;
//int nowTime;
//int opperation = 0; //0=normal run, 1=calibrate heater, 2=calibate fan
int fanspeed = 21;
double SetpointA, InputA, OutputA;
double Sensor[3];
double iTemp, oTemp, gTemp;
double tempDiff = 5;
int soilmoisture;
int drysoil = 840;
int water = 0;
bool setDiff = 0;
bool logToFile = 0;
//double Kp=32, Ki=0.5, Kd=0.5;
//double Kp=600, Ki=100, Kd=0.7;
double Kp=600, Ki=0.5, Kd=0.5;
OneWire oneWire(TEMPERATURE_BUS);
PID PIDa(&InputA, &OutputA, &SetpointA, Kp, Ki, Kd, DIRECT);
File Logfile;
//Structure of the Soil Sensor readings
struct SoilSensor {
int moisture;
int dry = 840;
int sat = 0;
};
// Configuration that we'll store on disk
struct Config {
bool fileLogStatus; // The satatus of our Logging Function
int tempDiffValue; // The temperature diference between the Inside and Outside probes
};
const char *filename = "/config.txt"; // File to store the config in
Config config; // global configuration object
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
DeviceAddress Sensor0 = { 0x28, 0x5A, 0x82, 0x23, 0x17, 0x13, 0x01, 0x98 }; //Address for Sensor1 Outside the cabinet
DeviceAddress Sensor1 = { 0x28, 0xEB, 0x09, 0x2F, 0x17, 0x13, 0x01, 0x01 }; //Address for Sensor2 Ground Temperature
DeviceAddress Sensor2 = { 0x28, 0xFB, 0x03, 0xBE, 0x16, 0x13, 0x01, 0x22 }; //Address for Sensor3 Inside the cabinet
DHTesp dht;
bool getDHT = false;
float humidity;
float temperature;
void dhtTicker() {
getDHT = true;
}
const int ledPin = D4; // an LED is connected to NodeMCU pin D1 (ESP8266 GPIO5) via a 1K Ohm resistor
bool ledState = false;
bool consoleLog = true;
bool fileLog = false;
void handleRoot() {
String s = MAIN_page; //Read HTML contents
server.send(200, "text/html", s); //Send web page
}
void handleDiffChange(){
if (server.args() > 0 ) {
for ( uint8_t i = 0; i < server.args(); i++ ) {
if (server.argName(i) == "diff") {
// do something here with value from server.arg(i);
String diff = server.arg(i);
tempDiff = diff.toFloat();
config.fileLogStatus = fileLog;
config.tempDiffValue = tempDiff;
Serial.println("Settings Changed, Saving to Config File");
saveConfiguration(filename, config); //diffrential temperature change so save new value to config
server.send(200, "text/plain", String(tempDiff)); //Send Temperature Offset value only to client ajax request
// handleRoot();
}
}
}
}
// Send the temperature and Sensor data to the Wireless Client
void sendTempUpdate(){
String message;
message = String(Sensor[0]); //Outside Temperature
message +="|";
message += String(Sensor[1]); //Ground Temperature
message +="|";
message += String(Sensor[2]); //Inside Temperature
message +="|";
message += String(tempDiff); //Temperature Differential
message +="|";
message += String(setDiff); //Server Handshake for settings
message +="|";
message += String(soilmoisture); //Moisture content of the soil
message +="|";
message += String(humidity); //Reletive Humidity
// message +="|";
// message += String(logToFile);
server.send(200, "text/plain", String(message));
}
void handleNotFound() {
//digitalWrite ( LED_BUILTIN, 0 );
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += ( server.method() == HTTP_GET ) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for ( uint8_t i = 0; i < server.args(); i++ ) {
message += " " + server.argName ( i ) + ": " + server.arg ( i ) + "\n";
}
server.send ( 404, "text/plain", message );
//digitalWrite ( LED_BUILTIN, 1 ); //turn the built in LED on pin DO of NodeMCU off
}
void calculatePID(){
InputA = Sensor[2];
SetpointA = Sensor[0] + tempDiff;
PIDa.Compute();
fanspeed = map(OutputA, 0, 1024, 45, 300);
analogWrite(FAN, (int)fanspeed);
analogWrite(HEATER, (int)OutputA);
soilmoisture = analogRead(SOILSENS);
}
void updateTemperature(){
sensors.requestTemperatures();
Sensor[0] = sensors.getTempC(Sensor0);
oTemp = Sensor[0];
Sensor[1] = sensors.getTempC(Sensor1);
gTemp = Sensor[1];
Sensor[2] = sensors.getTempC(Sensor2);
iTemp = Sensor[2];
}
void printToLog(){
String prnLogMsg;
int nowTime = now();
prnLogMsg = String(now());
prnLogMsg += ", t1:";
prnLogMsg += Sensor[0];
prnLogMsg += ", t2:";
prnLogMsg += Sensor[1];
prnLogMsg += ", t3:";
prnLogMsg += Sensor[2];
prnLogMsg += ", h:";
prnLogMsg += (int)OutputA;
prnLogMsg += ", f:";
prnLogMsg += (int)fanspeed;
prnLogMsg += ", s:";
prnLogMsg += soilmoisture;
//prnLogMsg += (int)soilmoisture;
prnLogMsg += ", h:";
prnLogMsg += humidity;
//prnLogMsg += (int)humidity;
prnLogMsg += "\n";
if(consoleLog){ //Log to console if this is set
Serial.print(prnLogMsg);
}
if(fileLog){ //Log to File if this is set
Logfile = SPIFFS.open("/ClocheLog.csv", "a"); // this opens the file in append-mode
if (!Logfile) {
Serial.println("File doesn't exist yet");
}
Logfile.print(prnLogMsg);
Logfile.close();
}
}
long stringToLong(String s){
char arr[32];
s.toCharArray(arr, sizeof(arr));
return atol(arr);
}
void setLocalTime(){
if (server.args() > 0 ) {
if (server.argName(0) == "hr") {
//setTime(hr,min,sec,day,month,yr);
Serial.println(server.arg(0).toInt());
Serial.println(server.arg(1).toInt());
Serial.println(server.arg(2).toInt());
setTime(server.arg(0).toInt(), server.arg(1).toInt(),server.arg(2).toInt(), server.arg(3).toInt(), server.arg(4).toInt(), server.arg(5).toInt());
// do something here with value from server.arg(i);
Serial.print(" Time Set to: ");
long thisTime = now();
Serial.print(thisTime);
Serial.print(" ");
Serial.print(hour(thisTime));
Serial.print(":");
Serial.print(minute(thisTime));
Serial.print(":");
Serial.print(second(thisTime));
Serial.print(" ");
Serial.print(day(thisTime));
Serial.print("/");
Serial.print(month(thisTime));
Serial.print("/");
Serial.println(year(thisTime));
//setTime(123445);
server.send(200, "text/plain", "Time Set"); //Send Temperature Offset value only to client ajax request
}
}
}
void prepUpdate(){
Serial.print("Preparing for Firmare Update");
//Detatch the timers prior to the update
prnLog.detach();
udtemp.detach();
doPID.detach();
//Send a message to the user with a redirect to the page
server.send(200, "text/html", "<head><meta http-equiv=\"refresh\" content=\"5;url=/update\"></head><body>Please wait while Firmware Update Page is loaded<br> Or <a href=\"/update\">Click here if page does not automaticly refresh</a></body>");
//Close and Stop the web server
server.close();
server.stop();
//Initiate the Web Update Server
httpUpdater.setup(&server);
//Restart the server
server.begin();
}
void logData(){
//Activate or Deactivate Logging to file.
String message;
if (server.args() > 0 ) {
for ( uint8_t i = 0; i < server.args(); i++ ) {
if (server.argName(i) == "LogStatus"){
Serial.println("LogStatus Requested");
message = config.fileLogStatus;
message += "|";
message += config.tempDiffValue;
message += "\n";
//server.send(200, "text/plain", message);
}
if (server.argName(i) == "logcmd") {
setTime(server.arg(1).toInt(), server.arg(2).toInt(),server.arg(3).toInt(), server.arg(4).toInt(), server.arg(5).toInt(), server.arg(6).toInt());
// request received to start or stop log;
String LogStatus = server.arg(i);
if(LogStatus == "ON"){
//start logging
Serial.println("Logging to File ON");
fileLog = true;
prnLog.attach(10, printToLog); //output the data ever 10 seconds
message = "ON";
//server.send(200, "text/plain", "ON");
}
if(LogStatus == "OFF"){
//stop logging
Serial.println("Logging to File OFF");
fileLog = false;
startSPIFFS();
prnLog.detach();
message = "OFF";
//server.send(200, "text/plain", "OFF");
}
// server.send(200, "text/plain", "OK");
}
}
}
server.send(200, "text/plain", message); //send message back to client
config.fileLogStatus = fileLog;
config.tempDiffValue = tempDiff;
Serial.print("Saving new config :");
Serial.print(" fileLog = ");
Serial.print(config.fileLogStatus);
Serial.print(" tempDiff = ");
Serial.println(config.tempDiffValue);
saveConfiguration(filename, config); // Save the settings to the config file
}
void sendLogFile(){
handleFileRead("/ClocheLog.csv");
}
void getDHTData(){
// delay(2000);
humidity = dht.getHumidity();
temperature = dht.getTemperature();
}
// Loads the configuration from a file
void loadConfiguration(const char *filename, Config &config) {
// Open file for reading
File file = SPIFFS.open(filename, "r");
// Allocate the document on the stack.
// Don't forget to change the capacity to match your requirements.
// Use arduinojson.org/assistant to compute the capacity.
StaticJsonDocument<80> doc;
// Deserialize the JSON document
DeserializationError error = deserializeJson(doc, file);
if (error)
Serial.println(F("Failed to read file, using default configuration"));
// Get the root object in the document
JsonObject root = doc.as<JsonObject>();
// Copy values from the JsonObject to the Config
config.fileLogStatus = root["fileLog"] | false;
config.tempDiffValue = root["tempDiff"] | 5;
// Close the file (File's destructor doesn't close the file)
file.close();
}
// Saves the configuration to a file
void saveConfiguration(const char *filename, const Config &config) {
// Delete existing file, otherwise the configuration is appended to the file
SPIFFS.remove(filename);
// Open file for writing
File file = SPIFFS.open(filename, "w");
if (!file) {
Serial.println(F("Failed to create file"));
return;
}
// Allocate the document on the stack.
// Don't forget to change the capacity to match your requirements.
// Use arduinojson.org/assistant to compute the capacity.
StaticJsonDocument<80> doc;
// Make our document contain an object
JsonObject root = doc.to<JsonObject>();
// Set the values in the object
root["fileLog"] = config.fileLogStatus;
root["tempDiff"] = config.tempDiffValue;
// Serialize JSON to file
if (serializeJson(doc, file) == 0) {
Serial.println(F("Failed to write to file"));
}
// Close the file (File's destructor doesn't close the file)
file.close();
}
void setup() {
Serial.begin(115200);
while (!Serial) continue;
Serial.println("Cloche Temperature Mamagement System");
pinMode(FAN, OUTPUT);
pinMode(HEATER, OUTPUT);
analogWriteFreq(10000);
ESP.wdtDisable(); //dissable the watchdog as we dont need it
dht.setup(DHT_PIN, DHTesp::DHT11); // Connect DHT sensor to GPIO 17
Serial.println();
Serial.println("Configuring access point...");
// Initialize SPIFFS library
while (!SPIFFS.begin()) {
Serial.println(F("Failed to initialize SD library"));
delay(1000);
}
Serial.println("SPIFFS opened:");
// Should load default config if run for the first time
Serial.println(F("Loading configuration..."));
loadConfiguration(filename, config);
fileLog = config.fileLogStatus;
tempDiff = config.tempDiffValue;
if(config.fileLogStatus){
prnLog.attach(10, printToLog);
}
//set-up the custom IP address
// WiFi.mode(WIFI_AP_STA);
// WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); // subnet FF FF FF 00
/* You can remove the password parameter if you want the AP to be open. */
// WiFi.softAP(ssid, password);
// IPAddress myIP = WiFi.softAPIP();
WiFi.begin(apid, appass);
while (WiFi.status() != WL_CONNECTED) { //Wait for connection
delay(500);
}
// Serial.print("AP IP address: ");
// Serial.println(myIP);
Serial.print("IP address: ");
Serial.println(WiFi.localIP()); //Print the local IP
Serial.print("Gateway IP: ");
Serial.println(WiFi.gatewayIP()); //Print the Gareway IP address
//httpUpdater.setup(&server);
server.on("/", handleRoot );
server.on("/updateData",sendTempUpdate);
server.on("/updatefw",prepUpdate);
server.on("/setDiff", handleDiffChange);
server.on("/setTime", setLocalTime);
server.on("/log", logData);
server.on("/getlog", handleDownload);
server.on("/ClocheLog.csv", handleDownload);
server.on ( "/inline", []() {
server.send ( 200, "text/plain", "this works as well" );
} );
server.onNotFound ( handleNotFound );
server.begin();
Serial.println("HTTP server started");
sensors.begin(); //start monitoring the sensors
PIDa.SetMode(AUTOMATIC); //start up the PID heating system
PIDa.SetOutputLimits(0, 1024);
delay(15);
// locate devices on the bus
Serial.print("Locating devices...");
Serial.print("Found ");
Serial.print(sensors.getDeviceCount(), DEC);
Serial.println(" devices.");
//Check the temperature sensors
if (!sensors.getAddress(Sensor0, 0)) Serial.println("Unable to find address for Device 0");
if (!sensors.getAddress(Sensor1, 1)) Serial.println("Unable to find address for Device 1");
if (!sensors.getAddress(Sensor2, 2)) Serial.println("Unable to find address for Device 2");
// set the resolution to 12 bit per device
sensors.setResolution(Sensor0, TEMPERATURE_PRECISION);
sensors.setResolution(Sensor1, TEMPERATURE_PRECISION);
sensors.setResolution(Sensor2, TEMPERATURE_PRECISION);
sensors.requestTemperatures();
Sensor[0] = sensors.getTempC(Sensor0);
Sensor[1] = sensors.getTempC(Sensor1);
Sensor[2] = sensors.getTempC(Sensor2);
Serial.println("Staring Temperature Monitor");
udtemp.attach(2, updateTemperature); //update the temperature's every 2 seconds
Serial.println("Starting PID Heating Controll");
doPID.attach(5, calculatePID); // Calculate and Update the PID.
Serial.println("Starting Humiditiy Sensor");
DHTSensor.attach(2, dhtTicker);
}
void loop() {
server.handleClient();
if (getDHT) {
getDHTData();
getDHT = false;
}
}
void startSPIFFS() { // Start the SPIFFS and list all contents
SPIFFS.begin(); // Start the SPI Flash File System (SPIFFS)
Serial.println("SPIFFS started. Contents:");
{
Dir dir = SPIFFS.openDir("/");
while (dir.next()) { // List the file system contents
String fileName = dir.fileName();
size_t fileSize = dir.fileSize();
Serial.printf("\tFS File: %s, size: %s\r\n", fileName.c_str(), formatBytes(fileSize).c_str());
}
Serial.printf("\n");
}
}
String formatBytes(size_t bytes) { // convert sizes in bytes to KB and MB
if (bytes < 1024) {
return String(bytes) + "B";
} else if (bytes < (1024 * 1024)) {
return String(bytes / 1024.0) + "KB";
} else if (bytes < (1024 * 1024 * 1024)) {
return String(bytes / 1024.0 / 1024.0) + "MB";
}
}
bool handleFileRead(String path) { // send the right file to the client (if it exists)
Serial.println("handleFileRead: " + path);
// if (path.endsWith("/")) path += "index.html"; // If a folder is requested, send the index file
// String contentType = getContentType(path); // Get the MIME type
// String pathWithGz = path + ".gz";
if (SPIFFS.exists(path)) { // If the file exists, either as a compressed archive, or normal
File file = SPIFFS.open(path, "r"); // Open the file
size_t sent = server.streamFile(file, "application/octet-stream"); // Send it to the client
file.close(); // Close the file again
Serial.println(String("\tSent file: ") + path);
return true;
}
Serial.println(String("\tFile Not Found: ") + path); // If the file doesn't exist, return false
return false;
}
void handleDownload(){
if (!SPIFFS.begin()) {
Serial.println("SPIFFS failed to mount !\r\n");
}
else {
String str = "";
File f = SPIFFS.open("/ClocheLog.csv", "r");
if (!f) {
Serial.println("Can't open SPIFFS file !\r\n");
}
else {
char buf[1024];
int siz = f.size();
while(siz > 0) {
size_t len = std::min((int)(sizeof(buf) - 1), siz);
f.read((uint8_t *)buf, len);
buf[len] = 0;
str += buf;
siz -= sizeof(buf) - 1;
}
f.close();
Serial.println("Sending Log File");
server.send(200, "text/plain", str);
}
}
}
const char MAIN_page[] PROGMEM = R"=====(
<!DOCTYPE html>
<html lang="en" >
<head>
<title>Antartic Cloche Heater V1.01b</title>
<style>
body {
background-color: #37474f;
font-family: source-sans-pro, sans-serif;
font-size: 16px;
}
.scale{
display:flex;
width: 100%;
justify-content: space-between;
color:white;
}
.outerWrap {
overflow:hidden;
display: flex;
justify-content: center;
flex-direction:column;
margin-top: 1em;
}
.fixOff{
position:relative;
left:20px;
}
.inwrap{
display:flex;
flex-direction: column;
justify-content:center;
}
.offsetLabel{
align-self:center;
font-size: 1.4em !important;
}
.adjustOffset{
align-self:center;
//background-color: #aeb4bf;
padding: 2em;
margin-top: 1em;
display:flex;
flex-direction: column;
justify-content: space-around;
min-height: 80px;
}
.title {
letter-spacing: -0.025em;
}
#intext{
padding-top: 20px;
}
:root {
--therm-left: 5%;
--therm-top: 5px;
--therm-width: 300px;
--therm-height: 300px;
}
.thermometer {
align-self: center;
font-family: sans-serif;
color: #333;
min-width: 300px;
min-height: 300px;
height: 70vh;
width: 70vh;
max-width:75vw;
max-height:75vw;
margin: 0px 0px;
border-radius: 50%;
background-color: #ededed;
box-shadow: 2px 4px 8px 0 rgba(0, 0, 0, 0.4);
}
.ring {
position: relative;
left: 2%;
top: 2%;
width: 96%;
height: 96%;
margin: 0px, 0px;
border-radius: 50%;
background-color: rgba(255, 170, 0, 1);
background: linear-gradient(
to right,
rgba(66, 165, 245, 1) 0%,
rgba(244, 255, 84, 1) 35%,
rgba(255, 153, 43, 1) 75%,
rgba(255, 38, 38, 1) 100%
);
box-shadow: inset 2px 4px 4px 0px rgba(0, 0, 0, 0.3);
.dial-bottom {
position: relative;
top: 62%;
left: 25%;
width: 50%;
height: 50%;
background-color: #ededed;
border-radius: 0 0 0 95%;
transform: rotate(-45deg);
}
}
.temperatureContainer {
position: relative;
top: -79%;
left: 17%;
width: 65%;
height: 65%;
background-color: #fff;
border-radius: 50%;
box-shadow: 1px 2px 4px 0 rgba(0, 0, 0, 0.25);
// padding: 0em;
// text-align: center;
}
.pointer {
position: absolute;
top: -25%;
left: 48.6%;
width: 3%;
height: 21%;
border-radius: 50%;
background-color: #fff;
border: solid 2px #fff;
transform-origin: 25% 290%;
box-shadow: 1px 2px 4px 0 rgba(0, 0, 0, 0.25);
}
.pointer.inside {
background-color: #ffaa00;
transform-origin: 25% 330%;
}
.pointer.outside {
background-color: #ff3c00;
transform: rotate(30deg);
}
.pointer.ground{
background-color: #56c120;
transform-origin: 25% 330%;
}
.title {
display:flex;
justify-content:center;
font-size: 6.5vh;
text-align: center;
text-shadow: 1px 2px 4px #212121;
}
.temperature {
position: relative;
top: calc(var(--therm-width) / 20);
left: 0%;
font-size: 6vh;
margin-top: -0.6em;
text-align: center;
text-shadow: 1px 2px 4px #212121;
margin-bottom: 0.3em;
}
.inside {
color: #ffaa00;
}
.outside {
color: #ff3c00;
}
.ground{
color: #56c120;
}
.offsetLabel {
color: #fbe9e7;
font-size: calc(var(--therm-width) / 20);
//text-align: center;
text-shadow: 1px 2px 4px #212121;
}
.offsetTemp {
align-self:center;
color: #dd2c00;
font-size: font-size: 1.4em !important;
//text-align: center;
// text-shadow: 1px 1px 1px #FBE9E7;
}
.degree {
font-size: 0.6em;
position: absolute;
margin-top: 0.2em;
}
.offsetTemperature {
align-self:center;
// background-color: #ff3c00;
width:300px;
}
[type="range"] {
-webkit-appearance: none;
margin: 0px 0px;
width: var(--therm-width);
height: 3%;
position: relative;
}
[type="range"]:focus {
outline: 0;
}
[type="range"]:focus::-webkit-slider-runnable-track {
background: #fbfbfc;
}
[type="range"]:focus::-ms-fill-lower {
background: #eceff1;
}
[type="range"]:focus::-ms-fill-upper {
background: #fbfbfc;
}
[type="range"]::-webkit-slider-runnable-track {
cursor: pointer;
height: 8px;
transition: all 0.2s ease;
width: 100%;
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.2), 0 0 1px rgba(13, 13, 13, 0.2);
background: #eceff1;
border: 4px solid #cfd8dc;
border-radius: 5px;
}
[type="range"]::-webkit-slider-thumb {
box-shadow: 4px 4px 4px rgba(0, 0, 0, 0.2), 0 0 4px rgba(13, 13, 13, 0.2);
background: #607d8b;
border: 2px solid #eceff1;
border-radius: 12px;
cursor: pointer;
height: 24px;
width: 24px;
-webkit-appearance: none;
margin-top: -10px;
}
[type="range"]::-moz-range-track {
cursor: pointer;
height: 8px;
transition: all 0.2s ease;
width: 100%;
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.2), 0 0 1px rgba(13, 13, 13, 0.2);
background: #eceff1;
border: 2px solid #cfd8dc;
border-radius: 5px;
}
[type="range"]::-moz-range-thumb {
box-shadow: 4px 4px 4px rgba(0, 0, 0, 0.2), 0 0 4px rgba(13, 13, 13, 0.2);
background: #607d8b;
border: 2px solid #eceff1;
border-radius: 12px;
cursor: pointer;
height: 24px;
width: 24px;
}
[type="range"]::-ms-track {
cursor: pointer;
height: 8px;
transition: all 0.2s ease;
width: 100%;
background: transparent;
border-color: transparent;
border-width: 12px 0;
color: transparent;
}
[type="range"]::-ms-fill-lower {
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.2), 0 0 1px rgba(13, 13, 13, 0.2);
background: #dde3e6;
border: 2px solid #cfd8dc;
border-radius: 10px;
}
[type="range"]::-ms-fill-upper {
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.2), 0 0 1px rgba(13, 13, 13, 0.2);
background: #eceff1;
border: 2px solid #cfd8dc;
border-radius: 10px;
}
[type="range"]::-ms-thumb {
box-shadow: 4px 4px 4px rgba(0, 0, 0, 0.2), 0 0 4px rgba(13, 13, 13, 0.2);
background: #607d8b;
border: 2px solid #eceff1;
border-radius: 12px;
cursor: pointer;
height: 24px;
width: 24px;
margin-top: 0;
}
.topcoat-range-input--vertical {
-webkit-transform: rotate(-90deg);
-moz-transform: rotate(-90deg);
-ms-transform: rotate(-90deg);
transform: rotate(-90deg);
}
@media only screen and (max-width: 600px) {
.title, .temperature{
font-size: 6.2vw;
}
}
.time{
font-family: sans-serif;
color: white;
}
.date{
font-family: sans-serif;
color: white;
}
</style>
<script>
window.console = window.console || function(t) {};
</script>
<script>
if (document.location.search.match(/type=embed/gi)) {
window.parent.postMessage("resize", "*");
}
</script>
</head>
<body translate="no" onload="GetParamaters();">
<table align="left" width="100%" height="100%" frame="none" rules="none">
<tr valign="top">
<td width="20%"><div class="box">
<div class="time">
<span id="curTime" class="time">00:00</span>
</div>
<div class="date">
<span id="curDate" class="date">01/01/1990</span>
</div>
<!-- <div class="utime">
<span id="curUtime" class="date">01/01/1990</span>
-->
</div>
</div><br>
<input type="submit" name="Set_Time" value="Set Time" onclick="sendTime()"><input id="logdata" type="submit" name="datalog" value="Datalog OFF" onclick="datalog()">
<a href="/ClocheLog.csv" download><input id="download" type="submit" name="download" value="Download Log"></a>
</td>
<td width="10%"><div class="outerWrap">
<div class="inwrap">
<div class="thermometer">
<div class="ring">
<div class="dial-bottom"></div>
</div>
<div id="tc" class="temperatureContainer">
<div id="groundpt" class="pointer ground" style="transform:rotate(12deg);"></div>
<div id="insidept" class="pointer inside" style="transform:rotate(100deg);"></div>
<div id="outsidept" class="pointer outside"></div>
<div id="intext" class="title outside">outside</div>
<div id="outsidetxt" class="temperature outside">
<span id="outsideNum">50</span>°<span id="degree" class="degree"></span>
</div>
<div class="title inside">
inside
</div>
<div id="insidetxt" class="temperature inside">
<span id="insideNum">120</span>°<span id="degree1" class="degree"></span>
</div>
<div class="title ground">
ground
</div>
<div id="groundtxt" class="temperature ground">
<span id="groundNum">20</span>°<span id="degree2" class="degree"></span>
</div>
</div>
</div>
<div class="adjustOffset">
<div class="offsetLabel">Set Temperature Offset: <span id="offset" class="offsetTemp">5</span><span class="offsetTemp">°</span></div>
<div class='scale'>
<p>+0°</p>
<p class="fixOff">+20°</p>
</div>
<!-- <div class="offsetTemperature"><input type="range" id="tmpoff" min="0" max="20" value="5" step="1" onmouseclick="test()"; onmouseup="sendDiff()"; onChange="test()";></div>-->
<div class="offsetTemperature"><input type="range" id="tmpoff" min="0" max="20" value="5" step="1";></div>
</div>
</div>
</div></td>
<td><div id="gauge"/></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</table>
<script >
//Create all the variables for the page elements
var v1 = document.getElementById('logdata'); //Log Button
var v2 = document.getElementById('tmpoff'); // Temperature Differential Slider
var v3 = document.getElementById('offset'); // Temperature Differnetial Text
var v4 = document.getElementById('outsidetxt'); // v2 Outside temperature display inside the gauge
var v5 = document.getElementById('groundtxt'); // v6 Ground Temperature display inside gauge
var v6 = document.getElementById('insidetxt'); // v1 Inside temperature display inside the gauge
var v7 = document.getElementById('insidept'); // v8 Inside Temperature Gauge Pointer
var v8 = document.getElementById('groundpt'); // v7 Ground Temperature Gauge Pointer
var v9 = document.getElementById('outsidept'); // v9 Outside Temperature Gauge Pointer
var v10 = document.getElementById('tmpoff'); // v10 Temperature Differential Slider
var v11 = document.getElementById('offset'); // v11 Temperature Differnetial Text
Icris={};Icris.DoubleGauge=function(n,t){var i=this;i.element=document.getElementById(n);i.element.innerHTML=Icris.GaugeMarkup;i.element.firstChild.setAttribute("height",t);i.element.firstChild.setAttribute("width",t);i.value1=0;i.value2=0;i.OEE=0;i.delta=0;i.setValues=function(n,t){if(n!==null){i.value1=n;var r=n/100*60;i.element.getElementsByClassName("needle1")[0].setAttribute("transform","rotate(-"+r+",0,100)")}t!==null&&(i.value2=t,r=t/100*60,i.element.getElementsByClassName("needle2")[0].setAttribute("transform","rotate("+r+",100,100)"));i.element.getElementsByClassName("aggregated")[0].innerHTML="OEE "+Math.round(parseFloat(i.value1)*parseFloat(i.value2)/100)+" %"};i.animationDelay=250;i.animateRandom=function(n){n!=null&&(i.animationDelay=n);var r=Math.random()>.5,t=Math.random()*10;r?i.value1+=t:i.value1-=t;(i.value1>100||i.value1<0)&&(i.value1=50);i.setValues(i.value1,null);r=Math.random()>.5;t=Math.random()*10;r?i.value2+=t:i.value2-=t;(i.value2>100||i.value2<0)&&(i.value2=50);i.setValues(null,i.value2);setTimeout(i.animateRandom,i.animationDelay)}};Icris.GaugeMarkup='<svg viewBox="0 0 120 120"><g transform= "translate(10,10)"> <rect style="fill:#EEEEEE;stroke-width:0.3;stroke:#000000;stroke-opacity:1" id="rect3680" width="114" height="100" x="-7" y="0" /> <text x="40" y="95" style="font-family:Arial; font-size:4px" class="aggregated">OEE<\/text><path style="fill:none;stroke:#AA0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 0,100 100,100" class="needle1" inkscape:connector-curvature="0" transform="rotate(-60,0,100)" /> <path style="fill:none;stroke:#AA0000;stroke-width:0.4px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 95,100 100,100" inkscape:connector-curvature="0" transform="rotate(-30,0,100)" /> <path style="fill:none;stroke:#AA0000;stroke-width:0.4px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 95,100 100,100" inkscape:connector-curvature="0" transform="rotate(-6,0,100)" /> <path style="fill:none;stroke:#AA0000;stroke-width:0.4px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 95,100 100,100" inkscape:connector-curvature="0" transform="rotate(-12,0,100)" /> <text x="100" y="101" transform="rotate(-12,0,100)" style="font-family:Arial; font-size:4px">20%<\/text> <path style="fill:none;stroke:#AA0000;stroke-width:0.4px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 95,100 100,100" inkscape:connector-curvature="0" transform="rotate(-18,0,100)" /> <path style="fill:none;stroke:#AA0000;stroke-width:0.4px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 95,100 100,100" inkscape:connector-curvature="0" transform="rotate(-24,0,100)" /> <text x="100" y="101" transform="rotate(-24,0,100)" style="font-family:Arial; font-size:4px">40%<\/text> <path style="fill:none;stroke:#AA0000;stroke-width:0.4px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 95,100 100,100" inkscape:connector-curvature="0" transform="rotate(-36,0,100)" /> <text x="100" y="101" transform="rotate(-36,0,100)" style="font-family:Arial; font-size:4px">60%<\/text> <path style="fill:none;stroke:#AA0000;stroke-width:0.4px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 95,100 100,100" inkscape:connector-curvature="0" transform="rotate(-42,0,100)" /> <path style="fill:none;stroke:#AA0000;stroke-width:0.4px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 95,100 100,100" inkscape:connector-curvature="0" transform="rotate(-48,0,100)" /> <text x="100" y="101" transform="rotate(-48,0,100)" style="font-family:Arial; font-size:4px">80%<\/text> <path style="fill:none;stroke:#AA0000;stroke-width:0.4px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 95,100 100,100" inkscape:connector-curvature="0" transform="rotate(-54,0,100)" /> <path style="fill:none;stroke:#AA0000;stroke-width:0.4px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 95,100 100,100" inkscape:connector-curvature="0" transform="rotate(-60,0,100)" /> <text x="100" y="100" transform="rotate(-58,0,100)" style="font-family:Arial; font-size:4px">100%<\/text> <path style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 100,100 0,100" class="needle2" inkscape:connector-curvature="0" transform="rotate(60,100,100)" /> <path style="fill:none;stroke:#000000;stroke-width:0.4px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 0,100 5,100" inkscape:connector-curvature="0" transform="rotate(30,100,100)" /> <text x="-8" y="101" transform="rotate(36,100,100)" style="font-family:Arial; font-size:4px">60%<\/text> <text x="-8" y="101" transform="rotate(12,100,100)" style="font-family:Arial; font-size:4px">20%<\/text> <text x="-8" y="101" transform="rotate(24,100,100)" style="font-family:Arial; font-size:4px">40%<\/text> <text x="-8" y="101" transform="rotate(48,100,100)" style="font-family:Arial; font-size:4px">80%<\/text> <path style="fill:none;stroke:#000000;stroke-width:0.4px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 0,100 5,100" inkscape:connector-curvature="0" transform="rotate(60,100,100)" /> <text x="-10" y="100" transform="rotate(58,100,100)" style="font-family:Arial; font-size:4px">100%<\/text> <path style="fill:none;stroke:#000000;stroke-width:0.4px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 0,100 5,100" inkscape:connector-curvature="0" transform="rotate(6,100,100)" /> <path style="fill:none;stroke:#000000;stroke-width:0.4px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 0,100 5,100" inkscape:connector-curvature="0" transform="rotate(12,100,100)" /> <path style="fill:none;stroke:#000000;stroke-width:0.4px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 0,100 5,100" inkscape:connector-curvature="0" transform="rotate(18,100,100)" /> <path style="fill:none;stroke:#000000;stroke-width:0.4px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 0,100 5,100" inkscape:connector-curvature="0" transform="rotate(24,100,100)" /> <path style="fill:none;stroke:#000000;stroke-width:0.4px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 0,100 5,100" inkscape:connector-curvature="0" transform="rotate(36,100,100)" /> <path style="fill:none;stroke:#000000;stroke-width:0.4px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 0,100 5,100" inkscape:connector-curvature="0" transform="rotate(42,100,100)" /> <path style="fill:none;stroke:#000000;stroke-width:0.4px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 0,100 5,100" inkscape:connector-curvature="0" transform="rotate(48,100,100)" /> <path style="fill:none;stroke:#000000;stroke-width:0.4px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 0,100 5,100" inkscape:connector-curvature="0" transform="rotate(54,100,100)" /> <ellipse style="fill:#ff0000;stroke:#000000;stroke-width:0.1;stroke-opacity:1" id="path4493" cx="0" cy="100" rx="1.3" ry="1.3" /> <ellipse style="fill:#000000;stroke:#000000;stroke-width:0.1;stroke-opacity:1" id="path4495" cx="100" cy="100" rx="1.3" ry="1.3" /> <\/g > <\/svg >';
// v10 : var slider = document.getElementById("myRange");
// V11 var output = document.getElementById("demo");
//output.innerHTML = slider.value;
v10.oninput = function() {
v11.innerHTML = this.value;
}
v10.onmouseup = function(){
sendDiff();
}
function GetParamaters(){
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if(xhttp.readyState == 4) {
var r = xhttp.responseText.split("|");
//}
if(r[0] == "1"){
//turn the log on and change the button label
v1.value = "Datalog ON";
}
if(r[0] == "0"){
//turn the datalog off
v1.value = "Datalog OFF";
}
//var diff = document.getElementById('tmpoff'); v10
//let dispNum = document.getElementById('offset'); v11
v10.value = parseInt(r[1]); //Update Slider with Value
v11.innerHTML = parseInt(r[1]); //Update Text with Value
}
};
xhttp.open("GET", "log?LogStatus=true", true);
xhttp.send();
}
var gauge=new Icris.DoubleGauge('gauge',300);
// gauge.setValues(0, 10);
// gauge.animateRandom(250);
function downloadlog(){
var xhttp = new XMLHttpRequest();
xhttp.open("GET", "getlog?file=/ClocheLog.csv", true);
xhttp.send();
}
function datalog(){
//Best we update the time just to be sure its set.
var date = new Date();
var uTime = date.getTime();
var hrs = date.getHours();
var mins = date.getMinutes();
var secs = date.getSeconds();
var days = date.getDate();
var months = date.getMonth();
var years = date.getFullYear();
var bt1 = document.getElementById('logdata');
var st;
var xhttp = new XMLHttpRequest();
if(bt1.value == "Datalog OFF"){
//turn the log on and change the button label
bt1.value = "Datalog ON";
st = "ON";
}else{
//turn the datalog off
bt1.value = "Datalog OFF";
st = "OFF";
}
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
}
};
//xhttp.open("GET", "setTime?hr="+hrs+"&min=" +mins+"&sec="+secs+"&day="+days+"&month="+months+"&year="+years, true);
// xhttp.open("GET", "log?logcmd="+st, true);
xhttp.open("GET", "log?logcmd="+st+"&hr="+hrs+"&min=" +mins+"&sec="+secs+"&day="+days+"&month="+months+"&year="+years, true);
xhttp.send();
}
//Update the time every second
function showTime(){
var t1 = document.getElementById('curTime'); // Time Location
// var t3 = document.getElementById('curUtime');
var date = new Date();
var nowTime = date.toLocaleTimeString();
t1.innerHTML = nowTime;
// t3.innerHTML = date.getTime();
}
showTime();
setInterval(showTime, 1000); //2000mSeconds update rate
//Update the date every hour, no point in writing it every second
function showDate() {
var t2 = document.getElementById('curDate'); // Date Location
var date = new Date();
var todayDate = date.toDateString();
t2.innerHTML = todayDate;
};
showDate();
setInterval(showDate, 3600000); // update date every hour
//const inputs = [].slice.call(document.querySelectorAll('input'));
var p1 = createRemap(-40,120, -130,130); //Temperature Mapping
var p2 = createRemap(-40,120, -130,130); //Temperature Mapping
var p3 = createRemap(800,442, 0, 100); //Moisture Mapping
var p4 = createRemap(0,100, 0, 100); //Humidity Mapping
var process = 1;
//inputs.forEach(input => input.addEventListener('change', handleUpdate));
//inputs.forEach(input => input.addEventListener('mousemove', handleUpdate));
//inputs.forEach(input => input.addEventListener('change', sendDiff));
//inputs.forEach(input => input.addEventListener('mousemove', test));
function haltUpdates(){
process = 0;
}
function createRemap(inMin, inMax, outMin, outMax) {
return function remaper(x) {
return (x - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;
};
};
//function handleUpdate(e) {
// document.getElementById("outsidetxt").innerHTML = document.getElementById("inside").value+"°";
// document.getElementById("outsidept").style.transform = "rotate("+p2(document.getElementById("outside").value)+"deg)";
// document.getElementById("insidetxt").innerHTML = document.getElementById("outside").value+"°";
// document.getElementById("insidept").style.transform = "rotate("+p1(document.getElementById("inside").value)+"deg)";
//};
function getUpdate(){
// 'xmlhttp' variable will hold the XMLHttpRequest object
var xmlhttp = false;
xmlhttp = new XMLHttpRequest();
xmlhttp.overrideMimeType('text/xml');
// var url = "updateData";
// xmlhttp.open("GET", url, true);
xmlhttp.onreadystatechange = function() {
if(xmlhttp.readyState == 4) {
//v4 = outsidetxt, v5 = groundtxt, v6 = insidetxt, v7 = insidept, v8 = groundpt, v9 = outsidept
//r[0] outside, r[1] ground, r[2] inside, r[3] tempDiff, r[4] setDiff, r[5] soilmoisture, r[6] humidity
var r = xmlhttp.responseText.split("|");
v4.innerHTML = r[0]+"°"; //Outside Temperature
v5.innerHTML = r[1]+"°"; //Ground Temperature
v6.innerHTML = r[2]+"°"; //Inside Temperature
v9.style.transform = "rotate("+p2(r[0])+"deg)";
v8.style.transform = "rotate("+p2(r[1])+"deg)";
v7.style.transform = "rotate("+p2(r[2])+"deg)";
gauge.setValues(p3(r[5]), p4(r[6]));
}
};
var url = "updateData";
xmlhttp.open("GET", url, true);
xmlhttp.send();
}
setInterval(function() {
// Call a function repetatively with 3 Second interval
getUpdate();
}, 3000); //3000mSeconds update rate
function sendDiff() {
var xhttp = new XMLHttpRequest();
var diff = document.getElementById('tmpoff'); // slider
let dispNum = document.getElementById('offset'); // text for displaying the offset temperature
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
dispNum.innerHTML = diff.value;
process = 1; // start monitoring again
}
};
xhttp.open("GET", "setDiff?diff="+diff.value, true);
xhttp.send();
}
function sendTime() {
//getFullYear() Get the year as a four digit number (yyyy)
//getMonth() Get the month as a number (0-11)
//getDate() Get the day as a number (1-31)
//getHours() Get the hour (0-23)
//getMinutes() Get the minute (0-59)
//getSeconds() Get the second (0-59)
//getMilliseconds() Get the millisecond (0-999)
//getTime() Get the time (milliseconds since January 1, 1970)
//getDay() Get the weekday as a number (0-6)
var xhttp = new XMLHttpRequest();
var date = new Date();
var uTime = date.getTime();
var hrs = date.getHours();
var mins = date.getMinutes();
var secs = date.getSeconds();
var days = date.getDate();
var months = date.getMonth();
var years = date.getFullYear();
//setTime(hr,min,sec,day,month,yr); // pass the data to arduino
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
}
};
//var date = new Date();
xhttp.open("GET", "setTime?hr="+hrs+"&min=" +mins+"&sec="+secs+"&day="+days+"&month="+months+"&year="+years, true);
xhttp.send();
}
function test () {
var diff = document.getElementById('tmpoff');
let dispNum = document.getElementById('offset');
//console.log(dispNum.value);
dispNum.innerHTML = diff.value;
//console.log(diff.value);
function textUpdate(n) {
}
}
</script>
<script language="javascript" type="text/JavaScript">
function getLog(log, lines) {
var url = "getLogFile.php?log=" + log + "&lines=" + lines;
request.open("GET", url, true);
request.onreadystatechange = updatePage;
request.send(null);
}
function tail(command,log,lines) {
if (command == "start") {
document.getElementById("watchStart").disabled = true;
document.getElementById("watchStop").disabled = false;
timer = setInterval(function() {getLog(log,lines);},5000);
} else {
document.getElementById("watchStart").disabled = false;
document.getElementById("watchStop").disabled = true;
clearTimeout(timer);
}
}
function updatePage() {
if (request.readyState == 4) {
if (request.status == 200) {
var currentLogValue = request.responseText.split("\n");
eval(currentLogValue);
document.getElementById("log").innerHTML = currentLogValue;
}
}
}
var request = (window.XMLHttpRequest) ? new XMLHttpRequest() : (window.ActiveXObject ? new window.ActiveXObject("Microsoft.XMLHTTP") : false);
</script>
</body>
</html>
)=====";
Debug Messages
Debug messages go here