can you explain in detail what the "webserver" should do?
web server should show 4 Relay status as 4 squares in one line so that off Relay displays in red color and on Relay in green color
Also display total on time for each led under it in bold big black font
how should it show 4 status if you only have 3 relays?!?
anyhow,
this works for 3:
/*
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
2023-04-02 noiasca
*/
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
const char* ssid = "AMGAD60"; // Enter your Wi-Fi network name
const char* password = "Amgad#60"; // Enter your Wi-Fi network password
WebServer server(80);
// 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 r1 = 21; // Output relay
const uint8_t r2 = 19;
const uint8_t r3 = 18;
const uint8_t commonStatePin = 5;
// a class for one LED one button
class Indicator {
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
uint32_t runtimeMillis = 0; // timestamp of last update of counterTotal
uint32_t runtimeTotal = 0; // current total running time
public:
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);
}
// if running, add the current open time to the totalTime
void addTime() {
uint32_t currentMillis = millis();
if (state == 1) {
runtimeTotal = runtimeTotal + (currentMillis - runtimeMillis) / 1000; // add passed time till now
runtimeMillis = currentMillis;
}
}
uint8_t getState() { // when you need the state - write a getter
return state;
}
uint32_t getRuntimeTotal() {
addTime();
return runtimeTotal;
}
uint8_t update(uint32_t currentMillis = millis()) {
uint8_t lastActive = noPin;
uint8_t buttonState = digitalRead(buttonPin);
if (state == 0) {
if (buttonState == LOW) {
state = 1;
digitalWrite(relayPin, HIGH);
runtimeMillis = currentMillis; // start the runtime counter
lastActive = buttonPin;
}
}
else {
if (buttonState == HIGH) {
state = 0;
addTime(); // call before a switch off
digitalWrite(relayPin, LOW);
}
else
lastActive = buttonPin;
}
return lastActive; // returns the button GPIO if it is pressed
}
};
//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]);
void handleRoot() {
String html = "<!doctype html>\n"
"<html lang='en'>\n"
"<head>\n"
"<title>Relay Status</title>\n"
"<meta name='viewport' content='width=device-width'>\n"
"</head>\n"
"<body>\n";
for (int i = 0; i < noOfIndicators; i++) {
String color = indicator[i].getState() == 0 ? "red" : "green";
html += "<div style='display:inline-block;margin:10px;width:100px;height:100px;border:1px solid black;background-color:" + color + ";'><p style='text-align:center;font-size:30px;font-weight:bold;color:black;'>" + String(indicator[i].getRuntimeTotal()) + "s</p></div>\n";
}
html += "</body>\n</html>";
server.send(200, "text/html", html);
}
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 setup() {
Serial.begin(115200);
while (!Serial); // wait for the serial monitor to open
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());
for (auto &i : indicator) i.begin();
// the webserver
server.on("/", handleRoot);
server.on("/favicon.ico", handle204);
server.onNotFound(handleNotFound);
server.begin();
Serial.println("HTTP server started");
pinMode(commonStatePin, OUTPUT);
}
/*
void webServer_neverCalled_and_willNotWorkAnaway() {
// handle root URL request
server.on("/", []() {
String html = "<html><head><title>Relay Status</title><style> .off { background-color: red; } .on { background-color: green; } .time { font-size: xx-large; font-weight: bold; color: black; } </style></head><body>";
html += "<h1>Relay Status</h1>";
for (auto &i : indicator) {
html += "<div class=\"";
if (digitalRead(i.relayPin) == LOW) {
html += "off";
} else {
html += "on";
}
html += "\" style=\"display: inline-block; width: 100px; height: 100px; margin-right: 10px; margin-bottom: 10px; text-align: center;\">";
html += "<h3>Relay " + String(i.relayPin) + "</h3>";
html += "<div class=\"time\">" + String(i.totalTime / 1000.0) + " seconds</div>";
html += "</div>";
}
html += "</body></html>";
server.send(200, "text/html", html);
});
}
*/
void loop() {
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);
server.handleClient(); // loop to handle incoming client requests
}
//
You can't access privat or protected member variables. You need getter for that.
I have rolled back to my previous class because you have deleted to much.
Thanks very much it works fine but the page is not updated until you manual update with f5
and the time calculated only when you manual update ( I mean if you do not update manually while button is pressed then its state stay red and calculated time is 0, when update manually after button released that state still red and no time calculated )
can you please modify the code to calculate time if page updated or not, and fix page to automatic update, also I need to add one square status for
"const uint8_t commonStatePin = 5" with no time inside.
well, that is a new requirement, you didn't state that in your post #64 and is neither in your code.
For sure I could but why don't you do it on your own?
start with the additional box for the common State Pin.
if you have that working and want to make the automatic update working you should search for "Fetch API" and JavaScript and JSON.
here I have a generic webserver which includes some of this technics.
https://werner.rothschopf.net/microcontroller/202108_esp_generic_webserver_en.htm
If I were you I would transmit each relay state and each relay runtime in the JSON and use the JavaScript to update the fields on the page.
Take your time.
If you need further help, post your code so we can see how far you got.
Ok you are completely right so I modify the code and added additional box for common State Pin
and set automatic refresh for the webpage each 5 sec.
I fail to modify code so that Total run time continue to calculate even web page is not refreshing
this is my modified code
/*
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
2023-04-02 noiasca
*/
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
const char* ssid = "AMGAD60"; // Enter your Wi-Fi network name
const char* password = "Amgad#60"; // Enter your Wi-Fi network password
WebServer server(80);
// 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 r1 = 21; // Output relay
const uint8_t r2 = 19;
const uint8_t r3 = 18;
const uint8_t commonStatePin = 5;
// a class for one LED one button
class Indicator {
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
uint32_t runtimeMillis = 0; // timestamp of last update of counterTotal
uint32_t runtimeTotal = 0; // current total running time
public:
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);
}
// if running, add the current open time to the totalTime
void addTime() {
uint32_t currentMillis = millis();
if (state == 1) {
runtimeTotal = runtimeTotal + (currentMillis - runtimeMillis) / 1000; // add passed time till now
runtimeMillis = currentMillis;
}
}
uint8_t getState() { // when you need the state - write a getter
return state;
}
uint32_t getRuntimeTotal() {
addTime();
return runtimeTotal;
}
uint8_t update(uint32_t currentMillis = millis()) {
uint8_t lastActive = noPin;
uint8_t buttonState = digitalRead(buttonPin);
if (state == 0) {
if (buttonState == LOW) {
state = 1;
digitalWrite(relayPin, HIGH);
runtimeMillis = currentMillis; // start the runtime counter
lastActive = buttonPin;
}
}
else {
if (buttonState == HIGH) {
state = 0;
addTime(); // call before a switch off
digitalWrite(relayPin, LOW);
}
else
lastActive = buttonPin;
}
return lastActive; // returns the button GPIO if it is pressed
}
};
//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]);
void handleRoot() {
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='5'>\n"
"</head>\n"
"<body>\n";
for (int i = 0; i < noOfIndicators; i++) {
String color = indicator[i].getState() == 0 ? "red" : "green";
html += "<div style='display:inline-block;margin:10px;width:100px;height:100px;border:1px solid black;background-color:" + color + ";'><p style='text-align:center;font-size:30px;font-weight:bold;color:black;'>" + String(indicator[i].getRuntimeTotal()) + "s</p>/";
html += "<p style='text-align:center;font-size:20px;font-weight:bold;color:black;'>" + String(indicator[i].getRuntimeTotal() / 3600) + "h</p>/";
html += "<p style='text-align:center;font-size:20px;font-weight:bold;color:black;'>" + String(indicator[i].getRuntimeTotal() * 0.888 / 3600.00) + "KW</p></div>";
}
String color = digitalRead(commonStatePin) == LOW ? "red" : "green";
html += "<div style='display:inline-block;margin:10px;width:100px;height:100px;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></div>";
html += "</body>\n</html>";
server.send(200, "text/html", html);
}
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 setup() {
Serial.begin(115200);
while (!Serial); // wait for the serial monitor to open
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());
for (auto &i : indicator) i.begin();
// the webserver
server.on("/", handleRoot);
server.on("/favicon.ico", handle204);
server.onNotFound(handleNotFound);
server.begin();
Serial.println("HTTP server started");
pinMode(commonStatePin, OUTPUT);
}
void loop() {
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);
server.handleClient(); // loop to handle incoming client requests
}
Now I want to modify code so that Total run time continue to calculate even web page is not refreshing
Another issue the first code only one relay is on even you press 2 or three buttons but now two or even 3 relay is on at same time depending on no. of buttons pressed
// refresh at 30 seconds
html += "< meta http-equiv="refresh" content="30" >"
I don't understand what are describing.
Please fullfill the table and define what should happen for each possible state of the buttons:
B1 B2 B3 R1 R2 R3 Common
OFF OFF OFF OFF OFF OFF ON
ON OFF OFF ON OFF OFF OFF
OFF ON OFF OFF ON OFF OFF
OFF OFF ON OFF OFF ON OFF
ON ON OFF
ON OFF ON
OFF ON ON
ON ON ON
(put it in code tags so we can see proper columns)
The button-relay operation:
as it was in my code post #61 and simulation link ==> ESP32Work - Wokwi Arduino and ESP32 Simulator
Switch the relay of first pressed button ==> on ( do not check for other buttons pressed or not )
After first button released switch its relay ==> off - then check for next button if pressed switch its
relay on otherwise check last button if pressed switch its relay on it not switch common relay on
Total on time calculation should run for the on relay only
and this flow chart
Which shows nowhere do you turn off any relay(s) that got turned on.
Which indicates nowhere any place you want the time to be accumulate, or where time is involved in deciding to turn off any relay(s).
So the wokwi you linked worked but doesn't… what? It seems to report the time a relay was closed once you move along to the next.
Which sounds kinda like what you were going for.
a7
I want to modify code in post #68 so that relay & button operation works as wokwi link exactly and the time calculations also same way as wokwi link
next try:
/*
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
*/
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
//#include <credentials.h> // if you have an external file with your credentials you can use it - remove before upload
#ifndef STASSID // either use an external .h file containing STASSID and STAPSK
// // or
#define STASSID "AMGAD60" // ... modify this line to your SSID
#define STAPSK "Amgad#60" // ... and set your WIFI password
#endif
const char* ssid = STASSID;
const char* password = STAPSK;
WebServer server(80);
// this code works fine and calculate total time of each led on
const uint8_t b1 = 4; // Button
const uint8_t b2 = 22; // was 22 - noiasca uses 14
const uint8_t b3 = 15; // was 15
const uint8_t r1 = 21; // Output relay
const uint8_t r2 = 19;
const uint8_t r3 = 18;
const uint8_t commonStatePin = 5;
// a class for one LED one button
class Indicator {
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
uint32_t runtimeMillis = 0; // timestamp of last update of counterTotal
uint32_t runtimeTotal = 0; // current total running time
public:
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);
}
// if running, add the current open time to the totalTime
void addTime() {
uint32_t currentMillis = millis();
if (state == 1) {
runtimeTotal = runtimeTotal + (currentMillis - runtimeMillis) / 1000; // add passed time till now
runtimeMillis = currentMillis;
}
}
uint8_t getState() { // when you need the state - write a getter
return state;
}
uint32_t getRuntimeTotal() {
addTime();
return runtimeTotal;
}
uint8_t update(uint32_t currentMillis = millis()) {
uint8_t buttonState = digitalRead(buttonPin);
if (buttonState == LOW && lastActive == noPin) {
state = 1;
digitalWrite(relayPin, HIGH);
runtimeMillis = currentMillis; // start the runtime counter
lastActive = buttonPin;
}
else if (buttonState == HIGH && lastActive == buttonPin) {
state = 0;
lastActive = noPin;
addTime(); // call before a switch off
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]);
void handleRoot() {
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='5'>\n"
"<link rel='icon' href='data:,'>\n" // avoid favicon request, might not work for Safari
"</head>\n"
"<body>\n";
for (int i = 0; i < noOfIndicators; i++) {
String color = indicator[i].getState() == 0 ? "red" : "green";
html += "<div style='display:inline-block;margin:10px;width:100px;height:100px;border:1px solid black;background-color:" + color + ";'><p style='text-align:center;font-size:30px;font-weight:bold;color:black;'>" + String(indicator[i].getRuntimeTotal()) + "s</p>\n";
html += "<p style='text-align:center;font-size:20px;font-weight:bold;color:black;'>" + String(indicator[i].getRuntimeTotal() / 3600) + "h</p>\n";
html += "<p style='text-align:center;font-size:20px;font-weight:bold;color:black;'>" + String(indicator[i].getRuntimeTotal() * 0.888 / 3600.00) + "KW</p>\n</div>\n";
}
String color = digitalRead(commonStatePin) == LOW ? "red" : "green";
html += "<div style='display:inline-block;margin:10px;width:100px;height:100px;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 += "<p style='font-size:20px'> </p>\n<p style='font-size:20px'> </p>\n"; // not nice but this brings this sqare on the same level.
html += "</div>\n";
html += "</body>\n</html>";
server.send(200, "text/html", html);
}
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 setup() {
Serial.begin(115200);
while (!Serial); // wait for the serial monitor to open
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());
for (auto &i : indicator) i.begin();
// the webserver
server.on("/", handleRoot);
server.on("/favicon.ico", handle204);
server.onNotFound(handleNotFound);
server.begin();
Serial.println("HTTP server started");
pinMode(commonStatePin, OUTPUT);
}
void loop() {
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);
server.handleClient(); // loop to handle incoming client requests
}
//
gives me a:

only one relay can be on at the time.
YES many thanks to noiasca it works in right way
I have to modify code so that RuntimeTotal calculations run continuously even the
web page is not opened .
( RuntimeTotal run only when webpage is opened during refreshing )
Just to be clear on that: the runtime calculation is done in the background.
It doesn't depend on if the webpage is getting refreshed or not.
I had it running now for 8 hours (no webpage refreshing in that time), opened the webpage again and one relay reports 8h!
You are right again
Thanks
I will try now to store the values of runtimeTotal for the 3 relays to the filesystem/LittleFS on ESP32
so when keep values when restarting I hope I could do that
I was afraid of something like that before.
The write cycles are limited for the filesystem.
Think about a stragegy "when" to store data. For example just in case of every "off" condition or on power loss (with additional hardware).
Google for "ESP32 preferences".
I try to add a new button to web page for reset runtimetotal
but I get a lot of erros
Also I add function printLocalTime() to read time and date from ntp for adding a new line in webpage for divide
"String(indicator[i].getRuntimeTotal() * 0.888 / (day of the month*3600.00)"
for each relay but I can not complete it
And I want to shift the contents of webpage to right to add a text description before each line
for example add "Floors" before squares line and add " Total Hours " before second line and so on
I need some help to fix these
My modified code is
/*
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
*/
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include "time.h"
#include "time.h"
#define STASSID "AMGAD60" // ... modify this line to your SSID
#define STAPSK "Amgad#60" // ... and set your WIFI password
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 3600;
const int daylightOffset_sec = 3600;
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 3600;
const int daylightOffset_sec = 3600;
const char* ssid = STASSID;
const char* password = STAPSK;
WebServer server(80);
// this code works fine and calculate total time of each led on
const uint8_t b1 = 4; // Button
const uint8_t b2 = 22; // was 22 - noiasca uses 14
const uint8_t b3 = 15; // was 15
const uint8_t r1 = 21; // Output relay
const uint8_t r2 = 19;
const uint8_t r3 = 18;
const uint8_t commonStatePin = 5;
// a class for one LED one button
class Indicator {
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
uint32_t runtimeMillis = 0; // timestamp of last update of counterTotal
uint32_t runtimeTotal = 0; // current total running time
public:
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);
}
// if running, add the current open time to the totalTime
void addTime() {
uint32_t currentMillis = millis();
if (state == 1) {
runtimeTotal = runtimeTotal + (currentMillis - runtimeMillis) / 1000; // add passed time till now
runtimeMillis = currentMillis;
}
}
uint8_t getState() { // when you need the state - write a getter
return state;
}
uint32_t getRuntimeTotal() {
addTime();
return runtimeTotal;
}
uint8_t update(uint32_t currentMillis = millis()) {
uint8_t buttonState = digitalRead(buttonPin);
if (buttonState == LOW && lastActive == noPin) {
state = 1;
digitalWrite(relayPin, HIGH);
runtimeMillis = currentMillis; // start the runtime counter
lastActive = buttonPin;
}
else if (buttonState == HIGH && lastActive == buttonPin) {
state = 0;
lastActive = noPin;
addTime(); // call before a switch off
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]);
void handleRoot() {
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>\n"
"function resetRuntime() {\n"
" var reset = confirm('Are you sure you want to reset the runtime?');\n"
" if (reset) {\n"
" var xhttp = new XMLHttpRequest();\n"
" xhttp.onreadystatechange = function() {\n"
" if (this.readyState == 4 && this.status == 200) {\n"
" location.reload();\n"
" }\n"
" };\n"
" xhttp.open('GET', '/reset', true);\n"
" xhttp.send();\n"
" }\n"
"}\n"
"</script>\n"
"</head>\n"
"<body>\n";
html += "<button style='font-size:20px' onclick='resetRuntime()'>Reset Runtime</button>\n";
for (int i = 0; i < noOfIndicators; i++) {
String color = indicator[i].getState() == 0 ? "red" : "green";
html += "<div style='display:inline-block;margin:10px;width:100px;height:100px;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 += "<p style='text-align:center;font-size:20px;font-weight:bold;color:black;'><br>" + String(indicator[i].getRuntimeTotal() / 3600) + "H</p>\n";
if(i==0){
html += "<p style='text-align:center;font-size:20px;font-weight:bold;color:black;'>" + String(indicator[i].getRuntimeTotal() * 0.888 / 3600.00) + "KW</p>\n";
html += "<p style='text-align:center;font-size:20px;font-weight:bold;color:black;'>" + String(indicator[i].getRuntimeTotal() * 0.888 / (30*3600.00)) + "KWH</p>\n";
}
}
html += "</body>\n"
"</html>\n";
server.send(200, "text/html", html);
}
void handleReset() {
for (int i = 0; i < noOfIndicators; i++) {
indicator[i].runtimeTotal = 0; // Reset the runtimeTotal for each indicator
}
server.send(200, "text/html", "Runtime reset.");
}
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 printLocalTime()
{
struct tm timeinfo;
if(!getLocalTime(&timeinfo)){
Serial.println("Failed to obtain time");
return;
}
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
}
void setup() {
Serial.begin(115200);
while (!Serial); // wait for the serial monitor to open
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());
for (auto &i : indicator) i.begin();
// the webserver
server.on("/", handleRoot);
server.on("/favicon.ico", handle204);
server.onNotFound(handleNotFound);
server.begin();
Serial.println("HTTP server started");
//init and get the time
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
printLocalTime();
pinMode(commonStatePin, OUTPUT);
}
void loop() {
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);
server.handleClient(); // loop to handle incoming client requests
}
I need some help to fix these
a)
Well I propose you fix that errors.
If you can't fix them on your own, post the complete error messages also.
b1)
what result do you expect when the sketch runs the second month and you devide the total runtimeTotal by 1 because it's the first of the next month ... doesn't make so much sense for me so I'm out ...
b1)
for the offset of your timezone, please use the timezone ... this will also help you with the daylight saving time. See my page:
https://werner.rothschopf.net/microcontroller/202103_arduino_esp32_ntp_en.htm
c)
make a quick mock up / design on paper and post it ... I will have a look at it if can solve that...
If you think carefully, consider how it should like in landscape (for a PC), and in potrait (on a phone).
d) Reset of Runtime
if you need to change a protected variable (like runtimeTotal) you will need a (public) setter.
that's a member function which will changes the protected (or private) variable.
untested:
void resetRuntimteTotal() {
runtimeTotal = 0;
}
Ok I done no. 2. divide by a day of month
and this is the web page I want to reach

Now this is a clean code after modification
// Water pumb ver 6.0
// Only one relay run at same time
// Modify web interface it gets day of month from ntp and divide on it
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include "time.h"
//#include <credentials.h> // if you have an external file with your credentials you can use it - remove before upload
#ifndef STASSID // either use an external .h file containing STASSID and STAPSK
// // or
#define STASSID "AMGAD60" // ... modify this line to your SSID
#define STAPSK "Amgad#60" // ... and set your WIFI password
#endif
const char* ssid = STASSID;
const char* password = STAPSK;
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 3600;
const int daylightOffset_sec = 3600;
WebServer server(80);
// this code works fine and calculate total time of each led on
const uint8_t b1 = 4; // Button
const uint8_t b2 = 22; // was 22 - noiasca uses 14
const uint8_t b3 = 15; // was 15
const uint8_t r1 = 21; // Output relay
const uint8_t r2 = 19;
const uint8_t r3 = 18;
const uint8_t commonStatePin = 5;
// a class for one LED one button
class Indicator {
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
uint32_t runtimeMillis = 0; // timestamp of last update of counterTotal
uint32_t runtimeTotal = 0; // current total running time
public:
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);
}
// if running, add the current open time to the totalTime
void addTime() {
uint32_t currentMillis = millis();
if (state == 1) {
runtimeTotal = runtimeTotal + (currentMillis - runtimeMillis) / 1000; // add passed time till now
runtimeMillis = currentMillis;
}
}
uint8_t getState() { // when you need the state - write a getter
return state;
}
uint32_t getRuntimeTotal() {
addTime();
return runtimeTotal;
}
uint8_t update(uint32_t currentMillis = millis()) {
uint8_t buttonState = digitalRead(buttonPin);
if (buttonState == LOW && lastActive == noPin) {
state = 1;
digitalWrite(relayPin, HIGH);
runtimeMillis = currentMillis; // start the runtime counter
lastActive = buttonPin;
}
else if (buttonState == HIGH && lastActive == buttonPin) {
state = 0;
lastActive = noPin;
addTime(); // call before a switch off
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]);
void handleRoot() {
// get current time from NTP
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
struct tm timeinfo;
if(!getLocalTime(&timeinfo)){
Serial.println("Failed to obtain time");
return;
}
// get day of the month
char dayOfMonth[3];
strftime(dayOfMonth, 3, "%d", &timeinfo);
int day = atoi(dayOfMonth);
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
"</head>\n"
"<body>\n";
for (int i = 0; i < noOfIndicators; i++) {
String color = indicator[i].getState() == 0 ? "red" : "green";
html += "<div style='display:inline-block;margin:10px;width:100px;height:100px;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 += "<p style='text-align:center;font-size:20px;font-weight:bold;color:black;'><br>" + String(indicator[i].getRuntimeTotal() / 3600) + "H</p>\n";
if(i==0){
html += "<p style='text-align:center;font-size:20px;font-weight:bold;color:black;'>" + String(indicator[i].getRuntimeTotal() * 0.888 / 3600.00) + "KW</p>\n";
html += "<p style='text-align:center;font-size:20px;font-weight:bold;color:black;'>" + String(indicator[i].getRuntimeTotal() * 0.888 / (day*3600.00)) + "KW/Day</p>\n</div>\n";
}
else{
html += "<p style='text-align:center;font-size:20px;font-weight:bold;color:black;'>" + String(indicator[i].getRuntimeTotal() * 0.888 / 3600.00) + "KW</p>\n";
html += "<p style='text-align:center;font-size:20px;font-weight:bold;color:black;'>" + String(indicator[i].getRuntimeTotal() * 0.888 / (30*3600.00)) + "KW/Day</p>\n</div>\n";
}
}
String color = digitalRead(commonStatePin) == LOW ? "red" : "green";
html += "<div style='display:inline-block;margin:10px;width:100px;height:100px;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 += "<p style='font-size:40px'> </p>\n<p style='text-align:center;font-size:40px;font-weight:bold;color:black;'> </p>\n<p style='font-size:40px'> </p>\n"; // added empty paragraph with a font size of 20 pixels
html += "</div>\n";
html += "</body>\n</html>";
// Init and get the time
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
server.send(200, "text/html", html);
}
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 setup() {
Serial.begin(115200);
while (!Serial); // wait for the serial monitor to open
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());
for (auto &i : indicator) i.begin();
// the webserver
server.on("/", handleRoot);
server.on("/favicon.ico", handle204);
server.onNotFound(handleNotFound);
server.begin();
Serial.println("HTTP server started");
pinMode(commonStatePin, OUTPUT);
}
void loop() {
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);
// printLocalTime();
server.handleClient(); // loop to handle incoming client requests
}
Now if possible help to modify web page and add reset button to reset RuntimeTotal to 0
other 3 swiches I work on them the idea is to set button to high when disable.
imho you have a table now:
This should be straight forward. Or do you see any reason why this should be no table?
two questions regarding:
if (i == 0) {
html += "<p style='text-align:center;font-size:20px;font-weight:bold;color:black;'>" + String(indicator[i].getRuntimeTotal() * 0.888 / 3600.00) + "KW</p>\n";
html += "<p style='text-align:center;font-size:20px;font-weight:bold;color:black;'>" + String(indicator[i].getRuntimeTotal() * 0.888 / (day * 3600.00)) + "KW/Day</p>\n</div>\n";
}
else {
html += "<p style='text-align:center;font-size:20px;font-weight:bold;color:black;'>" + String(indicator[i].getRuntimeTotal() * 0.888 / 3600.00) + "KW</p>\n";
html += "<p style='text-align:center;font-size:20px;font-weight:bold;color:black;'>" + String(indicator[i].getRuntimeTotal() * 0.888 / (30 * 3600.00)) + "KW/Day</p>\n</div>\n";
}
a) why is indicator 0 handled differently?
b) I still don't get your idea of calculation of KW/day ("per day" or "this day"?!?) If you want a KW/day by using the current day you will need either the start date of the sketch (where all runtimtesTotal began with 0) or the day of the first start of an indicator. For example the sketch starts on Sunday, but one indicator is used on Wednesday the first time. If that indicator has used 1kw on Wednesday (only), what result do you want to see on Thursday as kw/day?
if you need a number "this day" you will need a separate runtime counter for the current day.


