#include <WiFi.h>
#include "time.h"
#include "sntp.h"
#include <ESP32Servo.h>
Servo servo1, servo2;
unsigned int start_wait = 2; // number of seconds to wait for startup to complete
unsigned int test_run_time = 1; // number of minutes for test run
unsigned int charge_time = 180; // number of minutes to charge
unsigned int shutdown_timeout = 5; // number of seconds to wait before shutdown
unsigned int day; // track day of month for generator testing and charging
unsigned int timeout; // vaule used for timeouts
unsigned long batt_time; // timer for battery charger
unsigned long completed_charge_time= 0; // time remaining for charge if power failed while charging
unsigned long new_charge_time = 0; // new charge time if power failed and it is retrying
const int servo1Pin = 18; //pin for generator power servo
const int servo2Pin = 19; //pin for generator start servo
const int mpower = 12; //pin for main power test
const int gpower = 13; //pin for generator power test
const int bpower = 14; //pin for 12V charger enable
bool test_run = false; //is test run being done
bool system_error = false; //has a problem with the generator occurred
bool generator_running = false; //is the generator running
bool power_off = false; //is the main power off
bool stay_on = false; //flag to turn off because power came on
bool charged = false; //flag to show if battery has been charged
bool charge_failed = false; //charge fail flag
String start_time, end_time, status_message;
//set up AP and STA mode
const char* ssid = "Wokwi-GUEST"; //SSid name
const char* password = "";
const char *soft_ap_ssid = "ESP32"; //SSID for ESP32 Access Point
const char *soft_ap_password = "123456789"; //set Password for ESP32 AP
IPAddress local_IP(192, 168, 1, 184); // Set static IP address
IPAddress gateway(192, 168, 1, 1); // Set gateway IP address
IPAddress subnet(255, 255, 255, 0); // Set subnet address
const char* ntpServer1 = "pool.ntp.org";
const char* ntpServer2 = "time.nist.gov";
const long gmtOffset_sec = -4*3600; //Time zone offset from GMT
const int daylightOffset_sec = 3600; //Daylight savings offset
char timeStringBuff[30]; //string to hold time
struct tm timeinfo;
void setup()
{
Serial.begin(115200);
pinMode(mpower, INPUT); //setpin for main power check to input
pinMode(gpower, INPUT); //setpin for generator power check to input
pinMode(bpower, OUTPUT); //setpin for 12V charger power to input
servo1.attach(servo1Pin, 500, 2400);
servo2.attach(servo2Pin, 500, 2400);
servo1.write(90); //center servo
// set notification call-back function
sntp_set_time_sync_notification_cb( timeavailable );
/**
* NTP server address could be aquired via DHCP,
*
* NOTE: This call should be made BEFORE esp32 aquires IP address via DHCP,
* otherwise SNTP option 42 would be rejected by default.
* NOTE: configTime() function call if made AFTER DHCP-client run
* will OVERRIDE aquired NTP server address
*/
sntp_servermode_dhcp(1); // (optional)
/**
* This will set configured ntp servers and constant TimeZone/daylightOffset
* should be OK if your time zone does not need to adjust daylightOffset twice a year,
* in such a case time adjustment won't be handled automagicaly.
*/
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer1, ntpServer2);
WiFi.mode(WIFI_AP_STA); //ESP32 Access point configuration
Serial.println("Creating ESP32 AP");
WiFi.softAP(soft_ap_ssid, soft_ap_password); //Configuring ESP32 access point SSID and password
Serial.print("AP Created with IP address ");
Serial.println(WiFi.softAPIP()); //show the AP IP address
//connect to WiFi
Serial.printf("Connecting to %s ", ssid);
delay(1000);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(" CONNECTED");
}
void loop()
{
while (timeinfo.tm_year + 1900 < 1980) { //loop until time is up to date
// Serial.println(timeinfo.tm_year + 1900);
delay(1000);
printLocalTime(); // it will take some time to sync time :)
}
printLocalTime(); // it will take some time to sync time :)
//put date and time in a string
strftime(timeStringBuff, sizeof(timeStringBuff), "%B %d %Y %H:%M:%S", &timeinfo);
day = timeinfo.tm_mday; //day to check monthly run
//day = 15;
if (day == 15 and !charged and !power_off and !system_error) {
charged = true;
batt_time = millis();
digitalWrite(bpower, HIGH);
status_message = "Battery charging started.";
Serial.println(status_message);
if (charge_failed) {
new_charge_time = completed_charge_time;
charge_failed = false;
}
else new_charge_time = charge_time;
Serial.println(new_charge_time);
while (batt_time + (new_charge_time * 60 * 1000) >= millis() and !charge_failed) {
if (!main_power()) {
status_message = "Main power has failed. Battery charged for ";
status_message = status_message + String (new_charge_time - ((millis() - batt_time) / 60000));
status_message = status_message +" minutes out of ";
status_message = status_message + String (charge_time);
status_message = status_message +" and is incomplete.";
Serial.println(status_message);
charge_failed = true;
completed_charge_time = new_charge_time - ((millis() - batt_time) / 60000) - 40;
break;
}
}
digitalWrite(bpower, LOW);
}
else if (day == 15 and charge_failed and !power_off) {
charged = false;
}
if (!main_power() and !system_error) { // check main power
day = 1;
test_run = false;
power_off = true;
stay_on = true;
status_message = "Main power has failed.";
Serial.println(status_message);
}
else {
if (generator_running) { //power is back or end of test then shutdown
generator_running = false;
power_off = false;
test_run = true;
timeout = 4; //make up to 4 attempts to turn generator off
while (check_power() and timeout > 0) { //look for generator power
generator_off();
delay (5000); //wait 5 seconds and check that generator has stopped
timeout--;
}
if (check_power()){
status_message = "Generator did not stop even after 4 attempts to shut off. Manual interventon is urgently required. Once the issue is resolved, clear the error code to resume normal operation.";
Serial.println(status_message);
system_error = true;
}
}
}
// day = 1;
// check day of month for monthly test run
if (day == 1 and !test_run and !system_error) {
int day1 = timeinfo.tm_mday; //day to check monthly run
// day1 = 1;
if (day1 == 1 and !stay_on) {
test_run = true; //first day of month do test run
status_message = "Running generator test";
Serial.println(status_message);
}
if (!generator_running) {
generator_on();
generator_start();
}
delay(start_wait * 1000); //wait for generator to start
long start_run_time = millis() + (test_run_time * 60 * 1000); //set generator test run time
while ((start_run_time > millis() or stay_on) and !system_error) {
if (!check_power()) { //look for generator power
system_error = true;
stay_on = false;
start_run_time = 0;
if (!generator_running) {
status_message = "Generator did not start. ";
generator_running = true; //set this flag so generator will turn off
}
else status_message = "Generator failed while running. ";
if (day1 == 1) status_message = status_message +"for test";
status_message = status_message + " Please resolve the problem and clear the error code to resume normal operation.";
Serial.println(status_message);
}
else {
if (!generator_running) Serial.println("Generator is running");
generator_running = true;
//Serial.println(main_power());
//Serial.println(power_off);
if (main_power() and power_off) { //check if power returned while looping
Serial.print("Power is on, shutting down in ");
Serial.print(shutdown_timeout);
Serial.println(" seconds");
delay(shutdown_timeout * 1000);
if (main_power()) { //is power still on after timeout
start_run_time = 0; // set exit loop variables
stay_on = false;
}
else {
status_message = "Main power failed again during shutdown timeout. Shutdown aborted";
Serial.println(status_message);
}
}
}
}
}
else if (day == 2 and test_run) {
test_run = false; //reset test run flag
charged = false; //reset charger flag
}
}
void generator_on ()
{
status_message = "Generator turning on";
Serial.println (status_message);
servo1.write(180);
delay(2000);
servo1.write(90);
delay(5000);
}
void generator_off ()
{
status_message = "Generator turning off";
Serial.print (status_message);
Serial.print (" ");
end_time = timeStringBuff;
Serial.println (end_time);
servo1.write(0);
delay(1000);
servo1.write(90);
}
void generator_start ()
{
status_message = "Generator starting";
Serial.print (status_message);
Serial.print (" ");
start_time = timeStringBuff;
Serial.println (start_time);
servo2.write(180);
delay(1000);
servo2.write(90);
}
void generator_stop ()
{
status_message = "Generator shuting down";
Serial.print (status_message);
Serial.print (" ");
end_time = timeStringBuff;
Serial.println (end_time);
servo2.write(0);
delay(1000);
servo2.write(90);
}
bool check_power ()
{
return debounce(gpower);
}
bool main_power ()
{
// Serial.println (digitalRead(mpower));
return debounce(mpower);
}
bool debounce(int btn) {
int i = 0;
int state;
while (i < 5) {
state = digitalRead(btn);
delay (20);
i++;
}
return state;
// Serial.println(btn);
// static uint16_t state = 0;
// state = (state<<1) | digitalRead(btn) | 0xfe00;
// return (state == 0xff00); //High when not pressed
}
void printLocalTime()
{
if(!getLocalTime(&timeinfo)){
Serial.println("Waiting for time update");
return;
}
}
// Callback function (get's called when time adjusts via NTP)
void timeavailable(struct timeval *t)
{
Serial.println("Got time update from NTP!");
printLocalTime();
}