I have an esp32 code that check 4 input buttons and calculate on time for the first active button
I need to modify the code so that we can activate any buttons together at the same time and calculate time for any active buttons ( 1, 2, 3 even 4 are on same time )
please help
/*
https://forum.arduino.cc/t/multi-button-programming/1094488/
3 Outputs activated by button press
keep track of runtime
1 LED to indicate all OFF
ESP32 Webserver
Several examples on wokwi in this context:
https://wokwi.com/projects/357646010902566913
https://wokwi.com/projects/357648835646874625
https://wokwi.com/projects/358001919796202497
by noiasca
2023-04-02 esp32_relay_timer
2023-04-03 esp32_relay_timer2
2023-04-10 esp32_relay_timer4 handle commands, handle javascript, make slider working, make day reset by button press
2023-04-12 esp32_relay_timer5 proposal of reset method
2023-04-15 esp32_relay_timer6 a separate output
2023-04-18 esp32_relay_timer7 new class for the separate indicator
2023-04-22 esp32_relay_timer7 corrected
*/
// write data to file each day and restore at startup
// Read Time From NTP At Startup Then set internal RTC with Time From NTP
// Then Code Takes Its Time From RTC
// all button to run motor from pin 14
#include <WiFi.h>
#include <WebServer.h>
#include "time.h"
#include <string>
#include <iostream>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <time.h>
#include <SPIFFS.h>
#include "FS.h"
#include <SPI.h>
// #define ServerVersion "1.0"
String webpage = "";
bool SPIFFS_present = false;
#ifndef STASSID // either use an external .h file containing STASSID and STAPSK
// // or
#define STASSID "AMGAD60"
#define STAPSK "Amgad#60"
#endif
#define test 14
const char* ssid = STASSID;
const char* password = STAPSK;
const char* ntpServer = "pool.ntp.org";
const long gmtOffsetInSeconds = 10800;
const int daylightOffsetInSeconds = 10800;
const char* fileNameFormat = "/%02d%04d.txt";
unsigned long previousMillis = 0; // Variable to store the previous time
const unsigned long interval = 3600000; // Interval of one hour in milliseconds
bool wifiConnected = false;
bool dataSaved = false;
bool restr = false;
String table1 = "<table><tr><th>Date</th><th>Runtime 1</th><th>Runtime 2</th><th>Runtime 3</th><th>Separate</th><th>Total 1</th><th>Total 2</th><th>Total 3</th><th>Total Separate</th></tr>";
int sum;
int sumd;
WebServer server(6060);
// this code works fine and calculate total time of each led on
const uint8_t b1 = 4; // Button
const uint8_t b2 = 22;
const uint8_t b3 = 15;
const uint8_t b4 = 16;
const uint8_t r1 = 21; // Output relay
const uint8_t r2 = 19;
const uint8_t r3 = 18;
const uint8_t r4 = 23;
const uint8_t commonStatePin = 5; // output (hence will be readed also in some cases)
// a class for one LED one button
class Indicator {
protected:
const uint8_t buttonPin; // the input GPIO, active LOW
const uint8_t relayPin; // the output GPIO, active HIGH
uint8_t state = 0; // switched off or on
bool active = true; // output can be activated by buttonPin (true) or not (false)
uint32_t runtimeMillis = 0; // timestamp of last update of counterTotal
uint32_t runtimeTotal = 0; // current total running time
uint32_t runtimeToday; // todays running time
public:
void setRuntimeTotal(int total) {
runtimeTotal = total;
}
static uint8_t lastActive; // the last pressed button
static constexpr uint8_t noPin = 255; // dummy value for "no button is pressed"
Indicator(uint8_t buttonPin, uint8_t relayPin) : buttonPin{buttonPin}, relayPin{relayPin}
{}
void begin() {
pinMode(buttonPin, INPUT_PULLUP);
pinMode(relayPin, OUTPUT);
}
void setRuntimeToday(uint32_t value) {
runtimeToday = value;
}
// if running, add the current open time to the runtimeTotal and runtimeToday
void addTime() {
uint32_t currentMillis = millis();
if (state == 1) {
runtimeTotal += (currentMillis - runtimeMillis) / 1000; // add passed time till now
runtimeToday += (currentMillis - runtimeMillis) / 1000;
runtimeMillis = currentMillis;
}
}
uint8_t getState() { // when you need the state - write a getter
return state;
}
bool getActive() {
return active;
}
uint32_t getRuntimeTotal() {
addTime();
return runtimeTotal;
}
uint32_t getRuntimeToday() {
addTime();
return runtimeToday;
}
//reset today's runtime - call once at midnight
void resetToday() {
addTime();
runtimeToday = 0;
}
void resetTotal() {
addTime();
runtimeToday = 0;
runtimeTotal = 0;
}
//activate reading of button pin (for example by the webserver)
void activate() {
active = true;
}
//deactivate reading of button pin (for example by the webserver)
void deactivate() {
active = false;
}
virtual uint8_t update(uint32_t currentMillis = millis()) {
uint8_t buttonState = true;
if (active) buttonState = digitalRead(buttonPin); // only accept pressed button in active mode
if (buttonState == LOW && lastActive == noPin) {
state = 1;
// delay(2000);
digitalWrite(relayPin, HIGH);
runtimeMillis = currentMillis; // start the runtime counter
lastActive = relayPin; // changed to relayPin as preparation when the webserver can also activate the relay
}
else if (buttonState == HIGH && lastActive == relayPin) {
state = 0;
lastActive = noPin;
addTime(); // call before a switch off
// delay(2000);
digitalWrite(relayPin, LOW);
}
return lastActive; // returns the button GPIO if it is pressed
}
};
uint8_t Indicator::lastActive = Indicator::noPin; // initialize a static class member
//create 3 indicators (each with one button and one relay/LED)
Indicator indicator[] {
{b1, r1},
{b2, r2},
{b3, r3},
};
constexpr size_t noOfIndicators = sizeof(indicator) / sizeof(indicator[0]);
class IndicatorVersionB : public Indicator {
protected:
const uint8_t commonStatePin; // the input GPIO, active LOW
uint8_t previousState = 0; // needed to detect the state detection
public:
void setRuntimeTotal(int total) {
runtimeTotal = total;
}
IndicatorVersionB(uint8_t buttonPin, uint8_t relayPin, uint8_t commonStatePin) : Indicator(buttonPin, relayPin), commonStatePin{commonStatePin} {}
// modified version for derrived class
uint8_t update(uint32_t currentMillis = millis()) override {
uint8_t buttonState = true;
if (active) buttonState = digitalRead(buttonPin); // only accept pressed button in active mode
if (digitalRead(commonStatePin) == HIGH && (buttonState == LOW && previousState == 0)) {
state = 1;
// delay(2000);
digitalWrite(relayPin, HIGH);
runtimeMillis = currentMillis; // start the runtime counter
}
else if (digitalRead(commonStatePin) == LOW || (buttonState == HIGH && previousState == 1)) { // switched off
state = 0;
addTime(); // call before a switch off
digitalWrite(relayPin, LOW);
}
previousState = state;
return buttonState; // returns the button GPIO if it is pressed
}
};
IndicatorVersionB separate{b4, r4, commonStatePin};
void connectToWiFi() {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print ("IP address: ");
Serial.println(WiFi.localIP());
Serial.println("Connected to WiFi");
wifiConnected = true;
}
void listDir(char* dir, String& recentFileName) {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain time from internal RTC");
return;
}
int recentMonth = timeinfo.tm_mon + 1; // Month is 0-based, so adding 1 to get the actual month
int recentYear = timeinfo.tm_year + 1900; // Year is years since 1900, so adding 1900 to get the actual year
File root = SPIFFS.open(dir);
File recentFile;
File file = root.openNextFile();
while (file) {
String fileName = file.name();
if (fileName.endsWith(".txt") && fileName.length() == 11) {
if (recentFileName.isEmpty()) {
recentFile = file;
recentFileName = fileName;
recentMonth = recentFileName.substring(1, 3).toInt();
recentYear = recentFileName.substring(3, 7).toInt();
} else {
int fileMonth = fileName.substring(1, 3).toInt();
int fileYear = fileName.substring(3, 7).toInt();
if (fileYear > recentYear || (fileYear == recentYear && fileMonth > recentMonth)) {
recentFile = file;
recentFileName = fileName;
recentMonth = fileMonth;
recentYear = fileYear;
}
}
}
/* Serial.print("FILE: ");
Serial.println(fileName);
*/
file = root.openNextFile();
}
root.close();
}
void restart()
{
//delay(3000);
ESP.restart();
}
void saveDataToFile() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain time from internal RTC");
return;
}
char fileName[20];
snprintf(fileName, sizeof(fileName), fileNameFormat, timeinfo.tm_mon + 1, timeinfo.tm_year + 1900);
File file = SPIFFS.open(fileName, FILE_APPEND);
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
String dataLine = String(timeinfo.tm_mday) + "-" + String(timeinfo.tm_mon + 1) + "-" + String(timeinfo.tm_year + 1900) + ",";
dataLine += String(indicator[0].getRuntimeToday()/60)+"," ;
dataLine += String(indicator[1].getRuntimeToday()/60)+"," ;
dataLine += String(indicator[2].getRuntimeToday()/60)+"," ;
dataLine += String(separate.getRuntimeToday()/60)+"," ;
dataLine += String(indicator[0].getRuntimeTotal()/60)+"," ;
dataLine += String(indicator[1].getRuntimeTotal()/60)+"," ;
dataLine += String(indicator[2].getRuntimeTotal()/60)+"," ;
dataLine += String(separate.getRuntimeTotal()/60);
if (file.println(dataLine)) {
Serial.println("Data written successfully");
} else {
Serial.println("Failed to write data to file");
}
file.close();
}
std::vector<String> lastcells() {
String recentFileName;
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain time from internal RTC");
return std::vector<String>();
}
static bool dataLoaded = false;
static std::vector<String> lines; // Store all lines from the file
// Load data from file on startup
if (!dataLoaded) {
// Serial.print("Load data from file on startup ");
listDir("/", recentFileName);
Serial.print("Recent File Name: ");
Serial.println(recentFileName);
if (!recentFileName.isEmpty()) {
Serial.print("Recent File: ");
Serial.println(recentFileName);
// Open the recent file for reading
File recentFileHandle = SPIFFS.open(recentFileName, "r");
if (recentFileHandle) {
// Read all lines from the file
while (recentFileHandle.available()) {
String line = recentFileHandle.readStringUntil('\n');
lines.push_back(line);
}
recentFileHandle.close();
dataLoaded = true;
} else {
Serial.println("Failed to open recent file for reading.");
}
} else {
Serial.println("No recent files found.");
}
}
// Extract the last line and the last 4 values
std::vector<String> lastValues;
if (!lines.empty()) {
String lastLine = lines.back();
// Serial.print("Last Line: ");
// Serial.println(lastLine);
// Extract the values from the last line
std::vector<String> values;
int startIndex = 0;
int endIndex;
while ((endIndex = lastLine.indexOf(',', startIndex)) != -1) {
values.push_back(lastLine.substring(startIndex, endIndex));
startIndex = endIndex + 1;
}
values.push_back(lastLine.substring(startIndex));
// Check if there are at least 9 values in the last line
if (values.size() >= 9) {
lastValues.push_back(values[5]); // inT0
lastValues.push_back(values[6]); // inT1
lastValues.push_back(values[7]); // inT2
lastValues.push_back(values[8]); // sepT
} else {
Serial.println("Insufficient values in the last line.");
}
} else {
Serial.println("No data available.");
}
return lastValues;
}
void handleRoot() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain time from internal RTC");
return;
}
String html = "<!doctype html>\n"
"<html lang='en'>\n"
"<head>\n"
"<title>Relay Status</title>\n"
"<meta name='viewport' content='width=device-width'>\n"
"<meta http-equiv='refresh' content='2'>\n"
"<link rel='icon' href='data:,'>\n" // avoid favicon request, might not work for Safari
"<script src='j.js'></script>\n"
"<style>\n"
"body{margin:0;padding:20px;font-family:sans-serif}*{box-sizing:border-box}.table{width:100%;border-collapse:collapse}.table td,.table th{padding:12px 15px;border:1px solid #ddd;text-align:center;font-size:25px;font-weight:bold;color:black;}.table th{background-color:#46494d;color:#fff}.table1{width:100%;border-collapse:collapse}.table1 td,.table1 th{padding:12px 15px;border:1px solid #ddd;text-align:center;font-size:18px;font-weight:bold;color:black;}.table1 th{background-color:#970000;color:#fff}.switch{position:relative;display:inline-block;width:60px;height:34px}.switch input{opacity:0;width:0;height:0}.slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background-color:#ff0000;-webkit-transition:.4s;transition:.4s}.slider:before{position:absolute;content:"";height:26px;width:26px;left:4px;bottom:4px;background-color:#fff;-webkit-transition:.4s;transition:.4s}input:checked+.slider{background-color:#2196f3}input:focus+.slider{box-shadow:0 0 1px #2196f3}input:checked+.slider:before{-webkit-transform:translateX(26px);-ms-transform:translateX(26px);transform:translateX(26px)}.slider.round{border-radius:34px}.slider.round:before{border-radius:50%}"
"</style>\n"
"</head>\n"
"<body>\n"
"<table class='table'>\n"
"<thead>\n"
"<th>Floor</th>\n"
"<th>Status</th>\n"
"<th>Control</th>\n"
"<th>Total H/M</th>\n"
"<th>Total KW/M</th>\n"
"<th>KW Today</th>\n"
"</thead>\n"
"<tbody>\n";
// these are the "1 of 3 outputs"
for (int i = 0; i < noOfIndicators; i++) {
String color = indicator[i].getState() == 0 ? "red" : "green";
html += "<tr class='colomn1'>";
html += "<td data-label='Floor'>Floor (";
html += i+1;
html += ")</td>";
html += "<td data-label='Status'>";
html += "<div style='display:inline-block;margin:10px;width:200px;height:70px;border:1px solid black;background-color:" + color + ";'><p style='text-align:center;font-size:30px;font-weight:bold;color:black;'>" + String(indicator[i].getRuntimeTotal() / 60) + "M</p>\n";
html += "</div></td>";
html += "<td data-label='Control'>";
html += "<label class='switch'>"; // The Switch
html += "<input type='checkbox' ";
if (indicator[i].getActive() == true)
html += "value='checked' checked='checked' ";
html += "onclick='firecb(this.id)' id='cb";
html += i;
html += "'>";
html += "<span class='slider round'></span>";
html += "</label>";
html += "</td>";
html += "<td data-label='Total H/M'>" + String(indicator[i].getRuntimeTotal() / 3600) + " H</td>";
if (i == 0) {
html += "<td data-label='Total KW/M'>" + String(indicator[i].getRuntimeTotal() * 0.888 / 3600.00) + " KW</td>";
html += "<td data-label='KW Today'>" + String(indicator[i].getRuntimeToday() * 0.888 / 3600.00) + " KW/Day</td>";
}
else {
html += "<td data-label='Total KW/M'>" + String(indicator[i].getRuntimeTotal() * 0.888 / 3600.00) + " KW</td>";
html += "<td data-label='KW Today'>" + String(indicator[i].getRuntimeToday() * 0.888 / 3600.00) + " KW/Day</td>";
}
}
// this is the common state row MAIN
String color = digitalRead(commonStatePin) == LOW ? "red" : "green";
html += "<tr class='colomn4'>";
html += "<td data-label='Floor'>Main</td>";
html += "<td data-label='Status'>";
html += "<div style='display:inline-block;margin:10px;width:200px;height:70px;border:1px solid black;background-color:" + color + ";'><p style='text-align:center;font-size:30px;font-weight:bold;color:black;'>" + String(digitalRead(commonStatePin) == LOW ? "OFF" : "ON") + "</p>\n";
html += "</div></td>";
html += "<td data-label='Control'><a href = 'cmd?cmd=reset' target='i'>Reset M</a></td>"; // dirty call as href
html += "<td data-label='Total H/M'><a href = 'cmdd?cmdd=resetd' target='i'>Reset D</a></td>"; // dirty call as href
html += "</td>";
html += "<td data-label='Total KW/M'>" + String(timeinfo.tm_mday) + "/" + String(timeinfo.tm_mon + 1) + "/" + String(timeinfo.tm_year + 1900) + "</td>";
html += "<td data-label='KW Today'> " + String(timeinfo.tm_hour) + ":" + String(timeinfo.tm_min) + ":" + String(timeinfo.tm_sec) + "</td>";
// this is the separate output MOTOR
color = separate.getState() == 0 ? "red" : "green";
html += "<tr class='colomn4'>";
html += "<td data-label='Floor'>Motor</td>";
html += "<td data-label='Status'>";
html += "<div style='display:inline-block;margin:10px;width:200px;height:70px;border:1px solid black;background-color:" + color + ";'><p style='text-align:center;font-size:30px;font-weight:bold;color:black;'>" + String(separate.getRuntimeTotal()/60 ) + "M</p>\n";
html += "</div></td>";
html += "<td data-label='Control'>";
html += "<label class='switch'>"; // The Switch
html += "<input type='checkbox' ";
if (separate.getActive() == true)
html += "value='checked' checked='checked' ";
html += "onclick='firecb(this.id)' id='resbutton";
html += "'>";
html += "<span class='slider round'></span>";
html += "</label>";
html += "</td>";
html += "<td data-label='Total H/M'>" + String(separate.getRuntimeTotal() / 3600) + " H</td>";
html += "<td data-label='Total KW/M'>" + String(separate.getRuntimeTotal() * 0.888 / 3600.00) + " KW</td>";
html += "<td data-label='KW Today'>" + String(separate.getRuntimeToday() * 0.888 / 3600.00) + " KW/Day</td>";
// a row with Totals
color = digitalRead(b4) == HIGH ? "red" : "green";
sum = indicator[0].getRuntimeTotal() + indicator[1].getRuntimeTotal() + indicator[2].getRuntimeTotal() + separate.getRuntimeTotal();
sumd = indicator[0].getRuntimeToday() + indicator[1].getRuntimeToday() + indicator[2].getRuntimeToday() + separate.getRuntimeToday();
html += "<tr class='colomn4'>";
html += "<td data-label='Floor'>Totals</td>";
html += "<td data-label='Status'>";
html += sum / 60 ;
html += " M</td>";
html += "<td data-label='Control'>";
html += "<div style='display:inline-block;margin:10px;width:200px;height:70px;border:1px solid black;background-color:" + color + ";'><p style='text-align:center;font-size:30px;font-weight:bold;color:black;'>" + "Motor "+ (digitalRead(b4) == LOW ? "Run" : "Stop") + "</p>\n";
html += "</div></td>";
html += "<td data-label='Total H/M'> ";
html += sum / 3600 ;
html += " H</td>";
html += "<td data-label='Total KW/M'> ";
html += sum * 0.888 / 3600.00;
html += " KW</td>";
html += "<td data-label='KW Today'> ";
html += sumd * 0.888 / 3600.00;
html += " KW/Day</td>";
html += "</tr>";
html += "</tbody>\n";
html += "</table>\n";
html += "<iframe name='i' style='display:none'></iframe>\n"; // dirty hack to keep
html += "</div>\n";
html += "<br><br>\n";
for (int i = 0; i < 15; i++){
html += " "; }
html += "<button style='text-align:center;font-size:30px;font-weight:bold;color:black;' onclick = \"window.location.href = 'cmdr?cmdr=rest';\" >RESTART</button>"; // dirty call as href
html += "<br><br>\n";
for (int i = 0; i < 30; i++){
html += " "; }
// html += " ";
html += "<button style='text-align:center;font-size:30px;font-weight:bold;color:black;' onclick = \"window.location.href = 'cmdl?cmdl=moto';\" >RUN MOTOR</button>"; // dirty call as href
html += "<br><br>\n";
for (int i = 0; i < 45; i++){
html += " "; }
html += "<button style='text-align:center;font-size:30px;font-weight:bold;color:black;' onclick = \"window.location.href = 'cmdw?cmdw=data';\" >Dialy Report</button>"; // dirty call as href
html += "<br><br>\n";
for (int i = 0; i < 60; i++){
html += " "; }
html += "<button style='text-align:center;font-size:30px;font-weight:bold;color:black;' onclick = \"window.location.href = 'cmdf?cmdf=files';\" >FILES</button>"; // dirty call as href
html += "</body>\n</html>";
server.send(200, "text/html", html);
}
void handleData()
{
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain time from internal RTC");
return;
}
String html = "<!doctype html>\n"
"<html lang='en'>\n"
"<head>\n"
"<title>Dialy Report</title>\n"
"<meta name='viewport' content='width=device-width'>\n"
"<meta http-equiv='refresh' content='2'>\n"
"<link rel='icon' href='data:,'>\n" // avoid favicon request, might not work for Safari
"<script src='j.js'></script>\n"
"<style>\n"
"body{margin:0;padding:20px;font-family:sans-serif}*{box-sizing:border-box}.table{width:100%;border-collapse:collapse}.table td,.table th{padding:12px 15px;border:1px solid #ddd;text-align:center;font-size:25px;font-weight:bold;color:black;}.table th{background-color:#46494d;color:#fff}.table1{width:100%;border-collapse:collapse}.table1 td,.table1 th{padding:12px 15px;border:1px solid #ddd;text-align:center;font-size:18px;font-weight:bold;color:black;}.table1 th{background-color:#970000;color:#fff}.switch{position:relative;display:inline-block;width:60px;height:34px}.switch input{opacity:0;width:0;height:0}.slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background-color:#ff0000;-webkit-transition:.4s;transition:.4s}.slider:before{position:absolute;content:"";height:26px;width:26px;left:4px;bottom:4px;background-color:#fff;-webkit-transition:.4s;transition:.4s}input:checked+.slider{background-color:#2196f3}input:focus+.slider{box-shadow:0 0 1px #2196f3}input:checked+.slider:before{-webkit-transform:translateX(26px);-ms-transform:translateX(26px);transform:translateX(26px)}.slider.round{border-radius:34px}.slider.round:before{border-radius:50%}"
"</style>\n"
"</head>\n"
"<body>\n"
"<table class='table1'>\n"
"<thead>\n"
"<th>Date</th>\n"
"<th>Floor1-H</th>\n"
"<th>Floor2-H</th>\n"
"<th>Floor3-H</th>\n"
"<th>Motor-H</th>\n"
"<th>Floor1-KW</th>\n"
"<th>Floor2-KW</th>\n"
"<th>Floor3-KW</th>\n"
"<th>Motor-KW</th>\n"
"<th>Total-KW</th>\n"
"</thead>\n"
"<tbody>\n";
html += "<br><br>\n";
html += " ";
html += "<button style='text-align:center;font-size:30px;font-weight:bold;color:black;' onclick = \"window.location.href = '/';\" >HOME</button> "; // dirty call as href
html += "<button style='text-align:center;font-size:30px;font-weight:bold;color:black;' onclick = \"window.location.href = 'cmds?cmds=save';\" >SAVE DATA</button> "; // dirty call as href
html += "<button style='text-align:center;font-size:30px;font-weight:bold;color:black;' onclick = \"window.location.href = 'cmdf?cmdf=files';\" >FILES</button> "; // dirty call as href
html += "<br><br>\n";
String recentFileName;
listDir("/", recentFileName);
// Serial.print("Recent File Name: ");
// Serial.println(recentFileName);
if (!recentFileName.isEmpty()) {
// Serial.print("Recent File: ");
// Serial.println(recentFileName);
static bool dataup = false;
static bool dataLoaded = false;
static std::vector<String> lines; // Store all lines from the file
// Load data from file on startup
if (!dataLoaded) {
// Serial.print("Load data from file on startup ");
listDir("/", recentFileName);
// Serial.print("Recent File Name: ");
// Serial.println(recentFileName);
if (!recentFileName.isEmpty()) {
// Serial.print("Recent File: ");
// Serial.println(recentFileName);
// Open the recent file for reading
File recentFileHandle = SPIFFS.open(recentFileName, "r");
if (recentFileHandle) {
// Read all lines from the file
while (recentFileHandle.available()) {
String line = recentFileHandle.readStringUntil('\n');
lines.push_back(line);
}
recentFileHandle.close();
dataLoaded = true;
} else {
Serial.println("Failed to open recent file for reading.");
}
} else {
Serial.println("No recent files found.");
}
}
// Update the data from file daily at 23:57
if (timeinfo.tm_hour == 23 && timeinfo.tm_min == 57 && !dataup ) {
Serial.print("Updated now DATA ");
listDir("/", recentFileName);
// Serial.print("Recent File Name: ");
// Serial.println(recentFileName);
if (!recentFileName.isEmpty()) {
// Serial.print("Recent File: ");
// Serial.println(recentFileName);
// Open the recent file for reading
File recentFileHandle = SPIFFS.open(recentFileName, "r");
if (recentFileHandle) {
// Read all lines from the file
lines.clear(); // Clear the previous lines
while (recentFileHandle.available()) {
String line = recentFileHandle.readStringUntil('\n');
lines.push_back(line);
}
recentFileHandle.close();
dataup = true;
} else {
Serial.println("Failed to open recent file for reading.");
}
} else {
Serial.println("No recent files found.");
}
}
// Display the data from the file on startup and throughout the day
//Serial.println("OLD DATA");
for (const auto& line : lines) {
// Extract the values from the line
std::vector<String> values;
int startIndex = 0;
int endIndex;
while ((endIndex = line.indexOf(',', startIndex)) != -1) {
values.push_back(line.substring(startIndex, endIndex));
startIndex = endIndex + 1;
}
values.push_back(line.substring(startIndex));
// Display the extracted values
html += "<tr class='colomn1'>";
html += "<td data-label='Date'>" + values[0] + " </td>";
html += "<td data-label='Floor1-H'>" + String(values[1].toInt()/60.00 ) + " </td>";
html += "<td data-label='Floor2-H'>" + String(values[2].toInt()/60.00 ) + " </td>";
html += "<td data-label='Floor3-H'>" + String(values[3].toInt()/60.00 ) + " </td>";
html += "<td data-label='Motor-H'>" + String(values[4].toInt()/60.00 ) + " </td>";
html += "<td data-label='Floor1-KW'>" + String(values[1].toInt() * 0.888/60 ) + " </td>";
html += "<td data-label='Floor2-KW'>" + String(values[2].toInt() * 0.888/60 ) + " </td>";
html += "<td data-label='Floor3-KW'>" + String(values[3].toInt() * 0.888/60 ) + " </td>";
html += "<td data-label='Motor-KW'>" + String(values[4].toInt() * 0.888/60 ) + " </td>";
// sumd = inD0Str.toInt() + inD1Str.toInt() + inD2Str.toInt() + sepDStr.toInt() ;
sumd = values[1].toInt() * 0.888/60 + values[2].toInt() * 0.888/60 + values[3].toInt() * 0.888/60 + values[4].toInt() * 0.888/60 ;
// float multipliedSum = sumd ;
html += "<td data-label='Total-KW'>" + String(sumd) + " </td>";
}
}
html += "</tr>";
html += "</tbody>\n";
html += "</table>\n";
html += "</body>\n</html>";
server.send(200, "text/html", html);
}
void handleCommand() {
// receive command and handle accordingly
// Serial.println("D310 handleCommand");
for (uint8_t i = 0; i < server.args(); i++) {
Serial.print(server.argName(i));
Serial.print(": ");
Serial.println(server.arg(i));
}
if (server.argName(0) == "cmd" && server.arg(0) == "reset") { // the parameter which was sent to this server
Serial.println("D318 reset");
for (auto &i : indicator) i.resetToday(); // reset all today's runtime counter
for (auto &i : indicator) i.resetTotal(); // reset Total runtime counter
separate.resetTotal(); // reset separate Total runtime counter
}
else if (server.argName(0) == "cmdd" && server.arg(0) == "resetd") { // the parameter which was sent to this server
Serial.println("D275 reset");
for (auto &i : indicator) i.resetToday(); // reset all today's runtime counter
separate.resetToday(); // reset separate today's runtime counter
}
else if (server.argName(0) == "cmdl" && server.arg(0) == "moto") { // the parameter which was sent to this server
Serial.println("D285 Motor On");
digitalWrite(test, HIGH); // run motor
}
else if (server.argName(0) == "cmdr" && server.arg(0) == "rest") { // the parameter which was sent to this server
Serial.println("D295 Restart");
restr = true; // Restart
}
else if (server.argName(0) == "cmdf" && server.arg(0) == "files") { // the parameter which was sent to this server
Serial.println("D288 Files Menu");
SPIFFS_dir();
}
else if (server.argName(0) == "cmdw" && server.arg(0) == "data") { // the parameter which was sent to this server
Serial.println("D289 Daily Report");
handleData();
}
else if (server.argName(0) == "cmds" && server.arg(0) == "save") { // the parameter which was sent to this server
Serial.println("D298 Daily Report");
saveDataToFile();
}
else if (server.arg(0) == "resbutton" && server.argName(1) == "v") { // a checkbox sends a value
byte i = server.arg(0).substring(2).toInt(); // we get a "cb2"
Serial.print("D334 cb i:"); Serial.println(i);
for (uint8_t i = 0; i < noOfIndicators; i++){
Serial.print("D344 FORLOOP i:"); Serial.println(i);
if (server.arg(1) == "0")
{ // we get a string not an integer
indicator[i].deactivate();
digitalWrite(test, LOW);
separate.deactivate();
pinMode(commonStatePin , INPUT_PULLUP);
Serial.print("D355 separate.deactivate ");
}
else
{
indicator[i].activate();
separate.activate();
pinMode(commonStatePin, OUTPUT);
Serial.print("D355 separate.activate(); ");
}
}
}
else if (server.argName(0) == "cb" && server.argName(1) == "v") { // a checkbox sends a value
byte i = server.arg(0).substring(2).toInt(); // we get a "cb2"
Serial.print("D324 cb i:"); Serial.println(i);
if (i < noOfIndicators) {
if (server.arg(1) == "0") // we get a string not an integer
indicator[i].deactivate();
else
indicator[i].activate();
}}
server.send(204); // this page doesn't send back content --> 204
}
void handleJs() {
// Output: JavaScript
String message;
message += F("function firecb(c){\n" // c caller (mostly the id/self)
" var request=new XMLHttpRequest();\n"
" var v=0;\n" // if value is initialisied with 0 we get ...
" if (document.getElementById(c).checked) v=1;\n" // ... 1 or 0 if checkbox is checked (better than true or false)
" request.open('GET','cmd?cb='+c+'&v='+v,true);\n"
" request.open('GET','cmdd?cb='+c+'&v='+v,true);\n"
" request.open('GET','cmdl?cb='+c+'&v='+v,true);\n"
" request.open('GET','cmdr?cb='+c+'&v='+v,true);\n"
" request.open('GET','cmdf?cb='+c+'&v='+v,true);\n"
" request.open('GET','cmdw?cb='+c+'&v='+v,true);\n"
" request.open('GET','cmds?cb='+c+'&v='+v,true);\n"
" request.send(null);\n"
"}\n");
server.send(200, "text/javascript", message);
}
void handle204() {
server.send(204); // this page doesn't send back content --> 204
}
void handleNotFound() {
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);
}
void checkRuntimeReset() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain time from internal RTC");
return;
}
int currentDay = timeinfo.tm_mday;
int currentMonth = timeinfo.tm_mon + 1; // Month is 0-based, so adding 1 to get the actual month
int currentYear = timeinfo.tm_year + 1900; // Year is years since 1900, so adding 1900 to get the actual year
static int previousDay = -1; // remember the last day of reset;
if (timeinfo.tm_hour == 23 && timeinfo.tm_min == 59 && previousDay != timeinfo.tm_mday) {
previousDay = timeinfo.tm_mday;
Serial.println("D378 Daily reset of runtime");
for (auto &i : indicator) i.resetToday();
separate.resetToday(); // to be checked if needed
}
}
//=============New Web Pages ==========
void handleSPIFFSPage() {
#define ServerVersion "1.0"
String webpage = "";
bool SPIFFS_present = false;
#include "FS.h"
#include <SPI.h>
}
// All supporting functions from here...
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void File_Download(){ // This gets called twice, the first pass selects the input, the second pass then processes the command line arguments
if (server.args() > 0 ) { // Arguments were received
if (server.hasArg("download")) DownloadFile(server.arg(0));
}
else SelectInput("Enter filename to download","download","download");
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DownloadFile(String filename){
if (SPIFFS_present) {
File download = SPIFFS.open("/"+filename, "r");
if (download) {
server.sendHeader("Content-Type", "text/text");
server.sendHeader("Content-Disposition", "attachment; filename="+filename);
server.sendHeader("Connection", "close");
server.streamFile(download, "application/octet-stream");
download.close();
} else ReportFileNotPresent("download");
} else ReportSPIFFSNotPresent();
}
void File_Upload(){
append_page_header();
webpage += F("<h3>Select File to Upload</h3>");
webpage += F("<FORM action='/fupload' method='post' enctype='multipart/form-data'>");
webpage += F("<input class='buttons' style='width:40%' type='file' name='fupload' id = 'fupload' value=''><br>");
webpage += F("<br><button class='buttons' style='width:10%' type='submit'>Upload File</button><br>");
webpage += F("<a href='/'>[Back]</a><br><br>");
append_page_footer();
server.send(200, "text/html",webpage);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
File UploadFile;
void handleFileUpload(){ // upload a new file to the Filing system
HTTPUpload& uploadfile = server.upload(); // See https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266WebServer/srcv
// For further information on 'status' structure, there are other reasons such as a failed transfer that could be used
if(uploadfile.status == UPLOAD_FILE_START)
{
String filename = uploadfile.filename;
if(!filename.startsWith("/")) filename = "/"+filename;
Serial.print("Upload File Name: "); Serial.println(filename);
SPIFFS.remove(filename); // Remove a previous version, otherwise data is appended the file again
UploadFile = SPIFFS.open(filename, "w"); // Open the file for writing in SPIFFS (create it, if doesn't exist)
}
else if (uploadfile.status == UPLOAD_FILE_WRITE)
{
if(UploadFile) UploadFile.write(uploadfile.buf, uploadfile.currentSize); // Write the received bytes to the file
}
else if (uploadfile.status == UPLOAD_FILE_END)
{
if(UploadFile) // If the file was successfully created
{
UploadFile.close(); // Close the file again
Serial.print("Upload Size: "); Serial.println(uploadfile.totalSize);
webpage = "";
append_page_header();
webpage += F("<h3>File was successfully uploaded</h3>");
webpage += F("<h2>Uploaded File Name: "); webpage += uploadfile.filename+"</h2>";
webpage += F("<h2>File Size: "); webpage += file_size(uploadfile.totalSize) + "</h2><br>";
append_page_footer();
server.send(200,"text/html",webpage);
}
else
{
ReportCouldNotCreateFile("upload");
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SPIFFS_dir(){
if (SPIFFS_present) {
File root = SPIFFS.open("/");
if (root) {
root.rewindDirectory();
SendHTML_Header();
webpage += F("<h3 class='rcorners_m'>ESP32 Directory Contents</h3><br>");
webpage += F("<table align='center'>");
webpage += F("<tr><th>Name/Type</th><th style='width:20%'>Type File/Dir</th><th>File Size</th></tr>");
printDirectory("/",0);
webpage += F("</table>");
SendHTML_Content();
root.close();
}
else
{
SendHTML_Header();
webpage += F("<h3>No Files Found</h3>");
}
append_page_footer();
SendHTML_Content();
SendHTML_Stop(); // Stop is needed because no content length was sent
} else ReportSPIFFSNotPresent();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void printDirectory(const char * dirname, uint8_t levels){
File root = SPIFFS.open(dirname);
if(!root){
return;
}
if(!root.isDirectory()){
return;
}
File file = root.openNextFile();
while(file){
if (webpage.length() > 1000) {
SendHTML_Content();
}
if(file.isDirectory()){
webpage += "<tr><td>"+String(file.isDirectory()?"Dir":"File")+"</td><td>"+String(file.name())+"</td><td></td></tr>";
printDirectory(file.name(), levels-1);
}
else
{
webpage += "<tr><td>"+String(file.name())+"</td>";
webpage += "<td>"+String(file.isDirectory()?"Dir":"File")+"</td>";
int bytes = file.size();
String fsize = "";
if (bytes < 1024) fsize = String(bytes)+" B";
else if(bytes < (1024 * 1024)) fsize = String(bytes/1024.0,3)+" KB";
else if(bytes < (1024 * 1024 * 1024)) fsize = String(bytes/1024.0/1024.0,3)+" MB";
else fsize = String(bytes/1024.0/1024.0/1024.0,3)+" GB";
webpage += "<td>"+fsize+"</td></tr>";
}
file = root.openNextFile();
}
file.close();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void File_Stream(){
if (server.args() > 0 ) { // Arguments were received
if (server.hasArg("stream")) SPIFFS_file_stream(server.arg(0));
}
else SelectInput("Enter a File to Stream","stream","stream");
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SPIFFS_file_stream(String filename) {
if (SPIFFS_present) {
File dataFile = SPIFFS.open("/"+filename, "r"); // Now read data from SPIFFS Card
if (dataFile) {
if (dataFile.available()) { // If data is available and present
String dataType = "application/octet-stream";
if (server.streamFile(dataFile, dataType) != dataFile.size()) {Serial.print(F("Sent less data than expected!")); }
}
dataFile.close(); // close the file:
} else ReportFileNotPresent("Cstream");
} else ReportSPIFFSNotPresent();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void File_Delete(){
if (server.args() > 0 ) { // Arguments were received
if (server.hasArg("delete")) SPIFFS_file_delete(server.arg(0));
}
else SelectInput("Select a File to Delete","delete","delete");
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SPIFFS_file_delete(String filename) { // Delete the file
if (SPIFFS_present) {
SendHTML_Header();
File dataFile = SPIFFS.open("/"+filename, "r"); // Now read data from SPIFFS Card
if (dataFile)
{
if (SPIFFS.remove("/"+filename)) {
Serial.println(F("File deleted successfully"));
webpage += "<h3>File '"+filename+"' has been erased</h3>";
webpage += F("<a href='/delete'>[Back]</a><br><br>");
}
else
{
webpage += F("<h3>File was not deleted - error</h3>");
webpage += F("<a href='delete'>[Back]</a><br><br>");
}
} else ReportFileNotPresent("delete");
append_page_footer();
SendHTML_Content();
SendHTML_Stop();
} else ReportSPIFFSNotPresent();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SendHTML_Header(){
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send(200, "text/html", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
append_page_header();
server.sendContent(webpage);
webpage = "";
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SendHTML_Content(){
server.sendContent(webpage);
webpage = "";
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SendHTML_Stop(){
server.sendContent("");
server.client().stop(); // Stop is needed because no content length was sent
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SelectInput(String heading1, String command, String arg_calling_name){
SendHTML_Header();
webpage += F("<h3>"); webpage += heading1 + "</h3>";
webpage += F("<FORM action='/"); webpage += command + "' method='post'>"; // Must match the calling argument e.g. '/chart' calls '/chart' after selection but with arguments!
webpage += F("<input type='text' name='"); webpage += arg_calling_name; webpage += F("' value=''><br>");
webpage += F("<type='submit' name='"); webpage += arg_calling_name; webpage += F("' value=''><br><br>");
webpage += F("<a href='/'>[Back]</a><br><br>");
append_page_footer();
SendHTML_Content();
SendHTML_Stop();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ReportSPIFFSNotPresent(){
SendHTML_Header();
webpage += F("<h3>No SPIFFS Card present</h3>");
webpage += F("<a href='/'>[Back]</a><br><br>");
append_page_footer();
SendHTML_Content();
SendHTML_Stop();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ReportFileNotPresent(String target){
SendHTML_Header();
webpage += F("<h3>File does not exist</h3>");
webpage += F("<a href='/"); webpage += target + "'>[Back]</a><br><br>";
append_page_footer();
SendHTML_Content();
SendHTML_Stop();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ReportCouldNotCreateFile(String target){
SendHTML_Header();
webpage += F("<h3>Could Not Create Uploaded File (write-protected?)</h3>");
webpage += F("<a href='/"); webpage += target + "'>[Back]</a><br><br>";
append_page_footer();
SendHTML_Content();
SendHTML_Stop();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
String file_size(int bytes){
String fsize = "";
if (bytes < 1024) fsize = String(bytes)+" B";
else if(bytes < (1024*1024)) fsize = String(bytes/1024.0,3)+" KB";
else if(bytes < (1024*1024*1024)) fsize = String(bytes/1024.0/1024.0,3)+" MB";
else fsize = String(bytes/1024.0/1024.0/1024.0,3)+" GB";
return fsize;
}
//===============End of new web pages
//======== Contents of CSS.h====================
void append_page_header() {
webpage = F("<!DOCTYPE html><html>");
webpage += F("<head>");
webpage += F("<title>File Server</title>"); // NOTE: 1em = 16px
webpage += F("<meta name='viewport' content='user-scalable=yes,initial-scale=1.0,width=device-width'>");
webpage += F("<style>");
webpage += F("body{max-width:65%;margin:0 auto;font-family:arial;font-size:105%;text-align:center;color:blue;background-color:#F7F2Fd;}");
webpage += F("ul{list-style-type:none;margin:0.1em;padding:0;border-radius:0.375em;overflow:hidden;background-color:#dcade6;font-size:1em;}");
webpage += F("li{float:left;border-radius:0.375em;border-right:0.06em solid #bbb;}last-child {border-right:none;font-size:85%}");
webpage += F("li a{display: block;border-radius:0.375em;padding:0.44em 0.44em;text-decoration:none;font-size:85%}");
webpage += F("li a:hover{background-color:#EAE3EA;border-radius:0.375em;font-size:85%}");
webpage += F("section {font-size:0.88em;}");
webpage += F("h1{color:white;border-radius:0.5em;font-size:1em;padding:0.2em 0.2em;background:#558ED5;}");
webpage += F("h2{color:orange;font-size:1.0em;}");
webpage += F("h3{font-size:0.8em;}");
webpage += F("table{font-family:arial,sans-serif;font-size:0.9em;border-collapse:collapse;width:85%;}");
webpage += F("th,td {border:0.06em solid #dddddd;text-align:left;padding:0.3em;border-bottom:0.06em solid #dddddd;}");
webpage += F("tr:nth-child(odd) {background-color:#eeeeee;}");
webpage += F(".rcorners_n {border-radius:0.5em;background:#558ED5;padding:0.3em 0.3em;width:20%;color:white;font-size:75%;}");
webpage += F(".rcorners_m {border-radius:0.5em;background:#558ED5;padding:0.3em 0.3em;width:50%;color:white;font-size:75%;}");
webpage += F(".rcorners_w {border-radius:0.5em;background:#558ED5;padding:0.3em 0.3em;width:70%;color:white;font-size:75%;}");
webpage += F(".column{float:left;width:50%;height:45%;}");
webpage += F(".row:after{content:'';display:table;clear:both;}");
webpage += F("*{box-sizing:border-box;}");
webpage += F("footer{background-color:#eedfff; text-align:center;padding:0.3em 0.3em;border-radius:0.375em;font-size:60%;}");
webpage += F("button{border-radius:0.5em;background:#558ED5;padding:0.3em 0.3em;width:20%;color:white;font-size:130%;}");
webpage += F(".buttons {border-radius:0.5em;background:#558ED5;padding:0.3em 0.3em;width:15%;color:white;font-size:80%;}");
webpage += F(".buttonsm{border-radius:0.5em;background:#558ED5;padding:0.3em 0.3em;width:9%; color:white;font-size:70%;}");
webpage += F(".buttonm {border-radius:0.5em;background:#558ED5;padding:0.3em 0.3em;width:15%;color:white;font-size:70%;}");
webpage += F(".buttonw {border-radius:0.5em;background:#558ED5;padding:0.3em 0.3em;width:40%;color:white;font-size:70%;}");
webpage += F("a{font-size:75%;}");
webpage += F("p{font-size:75%;}");
webpage += F("</style></head><body><h1>File Server "); webpage += String(ServerVersion) + "</h1>";
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void append_page_footer(){ // Saves repeating many lines of code for HTML page footers
webpage += F("<ul>");
webpage += F("<li><a href='/'>Home</a></li>"); // Lower Menu bar command entries
webpage += F("<li><a href='/download'>Download</a></li>");
webpage += F("<li><a href='/upload'>Upload</a></li>");
webpage += F("<li><a href='/stream'>Stream</a></li>");
webpage += F("<li><a href='/delete'>Delete</a></li>");
webpage += F("<li><a href='/dir'>Directory</a></li>");
webpage += F("</ul>");
webpage += "<footer>©"+String(char(byte(0x40>>1)))+String(char(byte(0x88>>1)))+String(char(byte(0x5c>>1)))+String(char(byte(0x98>>1)))+String(char(byte(0x5c>>1)));
webpage += String(char((0x84>>1)))+String(char(byte(0xd2>>1)))+String(char(0xe4>>1))+String(char(0xc8>>1))+String(char(byte(0x40>>1)));
webpage += String(char(byte(0x64/2)))+String(char(byte(0x60>>1)))+String(char(byte(0x62>>1)))+String(char(0x70>>1))+"</footer>";
webpage += F("</body></html>");
}
//======== End of CSS.h====================
//======== Contents of Network.h====================
// Adjust the following values to match your needs
// -----------------------------------------------
#define servername "fileserver" // Set your server's logical name here e.g. if 'myserver' then address is http://myserver.local/
IPAddress local_IP(192, 168, 0, 150); // Set your server's fixed IP address here
IPAddress gateway(192, 168, 0, 1); // Set your network Gateway usually your Router base address
IPAddress subnet(255, 255, 255, 0); // Set your network sub-network mask here
IPAddress dns(192,168,0,1); // Set your network DNS usually your Router base address
const char ssid_1[] = "your_SSID1";
const char password_1[] = "your_PASSWORD_for SSID1";
const char ssid_2[] = "your_SSID2";
const char password_2[] = "your_PASSWORD_for SSID2";
const char ssid_3[] = "your_SSID3";
const char password_3[] = "your_PASSWORD_for SSID3";
const char ssid_4[] = "your_SSID4";
const char password_4[] = "your_PASSWORD_for SSID4";
//======== End of Network.h====================
//======== Contents of Sys_Variables.h====================
#ifdef ESP8266
#define SD_CS_pin D8 // The pin on Wemos D1 Mini for SD Card Chip-Select
#else
#define SD_CS_pin 5 // Use pin 5 on MH-T Live ESP32 version of Wemos D1 Mini for SD Card Chip-Select
#endif // Use pin 13 on Wemos ESP32 Pro
//======== End of Sys_Variables.h====================
void setup() {
int value = 0;
pinMode(test, OUTPUT);
digitalWrite(test,HIGH);
Serial.begin(115200);
// rtc.setTime(00, 00, 00, 8, 5, 2040); // 1th Jan 2040 01:00:00
while (!Serial); // wait for the serial monitor to open
connectToWiFi();
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, ntpServer, gmtOffsetInSeconds, daylightOffsetInSeconds);
// Set ESP32 internal RTC from NTP
timeClient.begin();
timeClient.update();
// Wait for NTP response
while (!timeClient.update()) {
Serial.println("Waiting for NTP response...");
delay(1000);
}
// Get the epoch time from NTP
unsigned long epochTime = timeClient.getEpochTime();
// Set the ESP32's internal RTC
struct timeval tv;
tv.tv_sec = epochTime;
tv.tv_usec = 0;
settimeofday(&tv, NULL);
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain time from internal RTC");
return;
}
// Initialize SPIFFS
if (!SPIFFS.begin()) {
Serial.println("Failed to mount SPIFFS");
return;
}
for (auto &i : indicator) i.begin();
separate.begin();
if (!SPIFFS.begin(true)) {
Serial.println("SPIFFS initialisation failed...");
SPIFFS_present = false;
}
else
{
Serial.println(F("SPIFFS initialised... file access enabled..."));
SPIFFS_present = true;
}
// the webserver
// Set up the server routes
server.on("/", handleRoot);
server.on("/favicon.ico", handle204);
server.on("/j.js", handleJs); // javscript
server.on("/cmd", handleCommand); // process commands
server.on("/cmdd", handleCommand); // process commands
server.on("/cmdl", handleCommand); // process commands
server.on("/cmdr", handleCommand); // process commands
server.on("/cmdf", handleCommand); // process commands
server.on("/cmdw", handleCommand); // process commands
server.on("/cmds", handleCommand); // process commands
server.onNotFound(handleNotFound);
// server.on("/spiffs", HTTP_GET, [](){ server.sendHeader("Connection", "close"); handleSPIFFSPage(); });
///////////////////////////// Server Request commands
server.on("/download", File_Download);
server.on("/upload", File_Upload);
server.on("/fupload", HTTP_POST,[](){ server.send(200);}, handleFileUpload);
server.on("/stream", File_Stream);
server.on("/delete", File_Delete);
server.on("/dir", SPIFFS_dir);
///////////////////////////// End of Request commands
server.begin();
Serial.println("HTTP server started");
Serial.print("Current time From Internal RTC: ");
Serial.print(timeinfo.tm_year + 1900);
Serial.print("-");
Serial.print(timeinfo.tm_mon + 1);
Serial.print("-");
Serial.print(timeinfo.tm_mday);
Serial.print(" ");
Serial.print(timeinfo.tm_hour);
Serial.print(":");
Serial.print(timeinfo.tm_min);
Serial.print(":");
Serial.print(timeinfo.tm_sec);
Serial.println();
if (!SPIFFS.begin()) {
Serial.println("Failed to mount SPIFFS");
return;
}
// Print available space
Serial.println("Total space: " + String(SPIFFS.totalBytes()));
Serial.println("Used space: " + String(SPIFFS.usedBytes()));
// Call lastcells() to get the last 4 values
std::vector<String> lastValues = lastcells();
if (!lastValues.empty()) {
/*
Serial.println("Last values from lastcells():");
Serial.print("inT0: ");
Serial.println(lastValues[0]);
Serial.print("inT1: ");
Serial.println(lastValues[1]);
Serial.print("inT2: ");
Serial.println(lastValues[2]);
Serial.print("sepT: ");
Serial.println(lastValues[3]);
*/
// Assign the values to the indicator objects and separate object
indicator[0].setRuntimeTotal(lastValues[0].toInt() * 60);
indicator[1].setRuntimeTotal(lastValues[1].toInt() * 60);
indicator[2].setRuntimeTotal(lastValues[2].toInt() * 60);
separate.setRuntimeTotal(lastValues[3].toInt() * 60);
} else {
Serial.println("No last values available.");
}
pinMode(commonStatePin, OUTPUT);
}
void loop() {
static bool datasa = false;
bool commonState = HIGH;
for (auto &i : indicator) {
if (i.update() != Indicator::noPin) commonState = LOW; // if one button reports active, common Relay has to be switched off
}
digitalWrite(commonStatePin, commonState);
if (!wifiConnected) {
Serial.println("Lost connection to WiFi. Reconnecting...");
connectToWiFi();
}
separate.update();
server.handleClient(); // loop to handle incoming client requests
checkRuntimeReset();
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain time from internal RTC");
return;
}
if (timeinfo.tm_hour == 23 && timeinfo.tm_min == 54 && !datasa) {
saveDataToFile();
datasa=true;
}
if ( restr == true ) restart() ;
/*
// Check if the specified interval has passed
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= 5000) {
previousMillis = currentMillis; // Update the previous time
// Call your function here that needs to be executed every hour
saveDataToFile();
delay(1000);
}
*/
}