RTC, Nodemcu How to save the alarm time set on the server for trigger relay?

Hello everyone,

i have a Nodemcu, LCD and RTC. which are all setup and working fine. I setup my rtc through serial timer setting which is a diiferent program and the rtc is also working fine and showing correct time on the lcd. the only problem i am facing is that the program does not save (remember) the time i set on the server to trigger the relay.

(The part of the world that i live in faces load shedding in which electricity is not available to us for a few hours.)

Every time the electricity is gone or if I reset the nodemcu or if i switch off the power, I have to enter the on and off hours of the relay again on the web server.

Is there a way in which the program stores the time entered on the web server to repeat daily even if i reset the device?

Please help :slight_smile:

#include <ESP8266WiFi.h>
#include "./DNSServer.h"
#include <ESP8266WebServer.h>
#include <RtcDS3231.h>
#include <Wire.h>                                                                  // must be included here so that Arduino library object file references work
RtcDS3231<TwoWire> Rtc(Wire);                                                      //D1,D2
#include <LiquidCrystal_I2C.h>

const char *ssid = "Garden Lights";                                                //Set your SSID
const char *password = "123456789";                                                //Set your PASSWORD

const byte        DNS_PORT = 53;                                                   // 53 is set as DNS port
IPAddress         apIP(10, 10, 10, 1);                                             // Network Server
DNSServer         dnsServer;                                                       // DNS server object
ESP8266WebServer  webServer(80);                                                   // Webserver object

LiquidCrystal_I2C lcd(0x27, 16, 2);


#define relay1 D0                                                                  //Relay 1



String hr, minut, sec ;
int hr1, minut1, sec1;
String new_time;



/*START OF HMTL CODE*/
String style_detials =                                                                  //This String defines the style attributes for webpage
  "<style type=\"text/css\">"
  " body{"
  "  background-color: #a69695;"
  "}"
  "button{"
  " display: inline-block;"
  "}"
  "#buttons{"
  " text-align: center;"
  "}"

  ".controllButtons{"
  " margin-top: 15px;"
  "margin-left: 5px;"
  "background-color: white;"
  "padding: 10px;"
  "border:1px solid black;"
  "border-radius: 10px;"
  "cursor: pointer;"
  "font-size: 14px;"
  "}"

  ".controllButtons:hover{"
  " background-color: orange;"
  "padding: 10px;"
  "border:1px solid black;"
  "border-radius: 10px;"
  "cursor: pointer;"
  "font-size: 14px;"
  "}"

  "@media only screen and (max-width: 700px) {"
  " button{"
  "  display: block;"
  "}"
  "#buttons{"
  " margin-top: 10%;"
  "margin-left: 35%;"
  "}"
  " .controllButtons{"
  "  margin-top: 15px;"
  "margin-left: 5px;"
  "background-color: white;"
  "padding: 15px;"
  "border:1px solid black;"
  "border-radius: 10px;"
  "cursor: pointer;"
  "font-size: 16px;"
  "}"

  ".controllButtons:hover{"
  " background-color: orange;"
  "padding: 15px;"
  "border:1px solid black;"
  "border-radius: 10px;"
  "cursor: pointer;"
  "font-size: 16px;"
  "}"
  "}"

  "</style>";


String home_screen = "" //Page 1 - Home Screen HTML code
                     "<!DOCTYPE html><html>"
                     "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"
                     + style_detials +
                     "<body><h1>Garden Lights</h1>"
                     "<div id=\"login\">"
                     "  <form action=\"confirmation_screen.php\" method=\"get\">"
                     
                    
                     "  <h5>Please enter Time in 24h format (hh:mm:ss)(21:22:23) for Light One</h5>"
                     "   <div>On Time : <input type=\"numeric\" name=\"code\" autofocus></div>"
                     "   <div>Off Time: <input type=\"numeric\" name=\"code\" autofocus></div>"
                     "   <div id=\"submit\"><input type=\"submit\" value=\"Submit\"></div>"
                     "  </form>"
                     "</div>"
                     "</body></html>";

String confirmation_screen = "" //Page 2 - If Time is Set
                             "<!DOCTYPE html><html>"
                             "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"
                             + style_detials +

                             "<body><h1>Time is now Set</h1>"
                             "</body></html>";



/*END OF HMTL CODE*/


void setup() {

  pinMode(relay1, OUTPUT);
  
  digitalWrite(relay1, HIGH);
  
  Wire.begin(D2, D1);
  lcd.begin();
  Rtc.Begin();


    WiFi.mode(WIFI_AP);                                                                     //Set ESP in AP mode
    WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
    WiFi.softAP(ssid, password);

    dnsServer.start(DNS_PORT, "*", apIP);

 // redirect all requests to the login page
    webServer.onNotFound([]() {
    webServer.sendHeader("Location", String("http://www.gl.com/home_screen.php"), true);
    webServer.send ( 302, "text/plain", "");

  });

    webServer.on("/home_screen.php", []() {
    webServer.send(200, "text/html", home_screen);
  });

  webServer.on("/confirmation_screen.php", []()

  {
    if (webServer.args() > 0) {
    new_time = webServer.arg(0);                                        // Stores the Alarm time input
    webServer.send(200, "text/html", confirmation_screen);}             // when alarm time entered direct user to Confirmaton page
    

    hr = new_time.substring(0, 2);                                      // substring is used to split the data in hour and minute form
    hr1 = hr.toInt();                                                   // toInt is used to convert string into integer
    minut = new_time.substring(3, 5);
    minut1 = minut.toInt();

}
);


webServer.begin();
}

void loop() {
  dnsServer.processNextRequest();
  webServer.handleClient();
  alarm();
}

void alarm ()
{
  RtcDateTime now = Rtc.GetDateTime();
  
  
  lcd.setCursor(0, 0);
  lcd.print(" Time:");
  
  if (now.Hour() <=9){
       lcd.print("0");}
       lcd.print(now.Hour(), DEC);
       lcd.print(':');
  
  if (now.Minute() <=9){
       lcd.print("0");}
       lcd.print(now.Minute(), DEC);
       lcd.print(':');
  
  if (now.Second() <=9){
       lcd.print("0");}
       lcd.print(now.Second(), DEC);
       lcd.setCursor(0, 1);
       lcd.print("LOGIN WWW.GL.COM"); 



  const unsigned long TimeSecs = now.Hour() * 3600UL + now.Minute() * 60UL;         //Convert the Hours And Minutes to Seconds and add them both with current Seconds 
  const unsigned long OnTimeSec =  hr1 * 3600UL + minut1 * 60UL;                    //Convert the On Time to Seconds so that it can be compared to the off time for Relay1 
  const unsigned long OffTimeSec = hr3 * 3600UL + minut3 * 60UL;                    //Convert the Off Time to Seconds so that it can be compared to the on time for Relay1
  
 if (TimeSecs >= OnTimeSec && TimeSecs < OffTimeSec)                 // Compare the current time with the entered time. If the current time is more than the On time set in the webserver and less then the OFf Time set in the webserver then switch on the relay 
  
  {
    digitalWrite(relay1,LOW);
  }
          
    else
    
    {
    digitalWrite(relay1,HIGH);                                       // If the above statement is not true then switch off the relay
    
    }

    }

if the RTC has battery backup it should not loose the time
on first use program the RTC with the time then on subsequent power ups read the time from the RTC

Rtc time is fine, the time which i store in webserver to switch the relay on and off is not stored . For example, if i wish to switch a light on, at a particular time, say 18:00 hrs. It switches the light on. But next day if i reset the device or switch it off and then turn back on or if there is a power outage then i have to reprogram the web server (enter time for relay switching again).

to store information during power outages I would use an FRAM, e.g.

as you enter data it is stored in FRAM
on powerup or reset read the data from FRAM and setup the system

Cant it be done in the software?

new_time = webServer.arg(0);                                        // Stores the Alarm time input

when i first got help on the rtc topic where i used nano and fixed time i used const int just before new time ,

which was like

const int OnHour = 07;     //ON AND OFF HOURS FOR SOLAR & KE RELAY
const int OnMin = 00;
const int OffHour = 19;
const int OffMin = 00;

which stored the hrs and min even if i restarted the program ar switched the nano off.

i would like to do the same thing here if its possible.

please read the above code and advise.

consider

const int OnHour = 07;     //ON AND OFF HOURS FOR SOLAR & KE RELAY
int OnMin = 00;

OnHour is an int constant value 7 which cannot be changed
onMin is an int variable with the initial value 0 but which can be assigned new values
on program startup OnHour is 7 and OnMin is 0

during run OnMin can be assigned a new value e.g.

  OnMin = 23

on a power cycle or reset any updated value is lost and on program startup OnHour is 7 and OnMin is 0

to retain updated values over power cycle or reset the data has to be stored in none volatile storage, e.g. local storage such as EEPROM, FRAM, SD card, disk, etc or via a network to database or similar remote system

some RTCs have a small amount of battery backed RAM on board, e.g. Maxim DS1307
some DS3231 RTC PCBs have a EEPROM Module on board

I suggest adding an FRAM to your system

i am soooooo sorry, i forgot to add that i am using DS3231 module!!!!!!

Please tell me how can i add the relay switching time to eeprom.

in the web server program which i posed in my first post......

thanks in advance

there are a number of PCBs with DS3231 RTC some with EEPROMs some without
could you give details of your PCB?
if your PCB has an onboard EEROM the details should give the device type, e.g. AT24C32 EEPROM which provides 4K x 8-bits of non-volatile memory

its a DS-3231 model no.zs-042

it has 24c32n i can read that. the larger chip on board is ds3231 and the smaller chip is 24c32n

there is a EEPROM library with example of use GitHub - cyberp/AT24Cx: Library for using the Atmels EEPROM AT24C32/AT24C64/AT24C128/AT24C256/AT24C512 in Arduino projects.

I am new to nodemcu and arduino, i know and can understand the language but cant write code without mistakes. So i search for a project online and then tinker it to My needs.

Eeprom is way over my head. Lol

I read the github page but dont know how and where to apply it in my case.

Please help.

there is a simple example of using the RTC and the 24C32 EEPROM on

the 24C32 EEPROM program runs OK on my ds3231 RTC module

Writing into memory...
Memory written
Reading memory: lastminuteengineers.com 
Reading memory: lastminuteengineers.com 
Reading memory: lastminuteengineers.com

to check what I2C devices are available you can run the I2C scanner
https://playground.arduino.cc/Main/I2cScanner/

UNO with RTC module shows

I2C scanner. Scanning ...
 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xA 0xB 0xC 0xD 0xE 0xF 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x2A 0x2B 0x2C 0x2D 0x2E 0x2F 0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x3A 0x3B 0x3C 0x3D 0x3E 0x3F 0x40 0x41 0x42 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4A 0x4B 0x4C 0x4D 0x4E 0x4F 0x50 0x51 0x52 0x53 0x54 0x55 0x56 0x57
Found address: 87 (0x57)
 0x58 0x59 0x5A 0x5B 0x5C 0x5D 0x5E 0x5F 0x60 0x61 0x62 0x63 0x64 0x65 0x66 0x67 0x68
Found address: 104 (0x68)
 0x69 0x6A 0x6B 0x6C 0x6D 0x6E 0x6F 0x70 0x71 0x72 0x73 0x74 0x75 0x76 0x77Done.
Found 2 device(s).

where address 0x57 is the 24C32 EEPROM and 0x68 is the ds3231 RTC

Thanks for the link.... i read the whole article... but still dont know how i can incorporate it into my sketch.... i am trying to read more examples by searching on the web to better understand it. Never used it before. I know a simpler way would be to install lithuim battery(18650) with 4056 charging bms. But want to do it the hard way. Maybe this way i can learn something new for the future. Thanks for helping and sorry if i am bugging you in any way.
And yes the address for the EEPROM is 0x57 if i2c’s (a1,a2,a3)Are not soldiered . Read that from the article lol.

to save your information in EEPROM a simple way is to use a structure
on power up or reset the problem is how do you know there is valid data in EEPROM?
e.g. on first run of the program the EEPROM will be blank or have data from a another program?

  1. the struct should contain some means of checking if the data read from EEPROM is valid
    in the example below the structure starts with text and ends with a CRC check
  2. on power up or reset read the EEPROM and check if the data read is valid by checking the text and the CRC
    (a) if OK continue
    (b) if not write a set of initial default values to the EEPROM
//  24C32 EEPROM - write a struct to EEPROM
//  original code from https://lastminuteengineers.com/ds3231-rtc-arduino-tutorial/

#include <Wire.h>

// struct to hold test data for EEPROM
struct Data {
  char name[11];  // to identify vaid data in FRAM
  char     ch;    // character
  uint16_t i;     // 16bit unsigned int
  float    x;     // real data
  uint8_t  spare; // pack to a 16bit boundary 
  uint8_t  crc;   // CRC or checksum - not implement in this test
}
data;
const struct Data dataDefault ={"testData",' ', 1, 3.14159};  // initial values for EEPROM

// print information in strct
void dataPrint(struct Data data){
    Serial.print("name ");Serial.println(data.name);
    Serial.print("ch= "), Serial.println(data.ch);
    Serial.print("i= "), Serial.println(data.i);
    Serial.print("x= "), Serial.println(data.x);
 }
 
void setup()
{
    Wire.begin(); // initialise the connection
    Serial.begin(115200);
    // read data in EEPROM and check if it is valid - if not write default data
    i2c_eeprom_read_buffer( 0x57, 0, (byte *) &data, sizeof(struct Data));
    // check name - note in real application also read and check CRC
    if( strcmp(data.name, dataDefault.name)) {   // if invalid data in EPROM
        Serial.print("Writing default struct into memory size in bytes ");
        Serial.println(sizeof(struct Data));
        i2c_eeprom_write_page(0x57, 0, (byte *)&dataDefault, sizeof(struct Data));
        delay(100); //add a small delay
        i2c_eeprom_read_buffer( 0x57, 0, (byte *) &data, sizeof(struct Data));
        if( strcmp(data.name, dataDefault.name)) Serial.println("EEPROM write failed!");
        else Serial.println("Memory written OK");
    }
    // write new data to EEPROM, read it back and display - remember EEPROM has limited write cycles
    for(int i=0;i<3;i++) {
        i2c_eeprom_write_page(0x57, 0, (byte *)&data, sizeof(struct Data));
        delay(100); //add a small delay
        i2c_eeprom_read_buffer( 0x57, 0, (byte *) &data, sizeof(struct Data));
        dataPrint(data);
        delay(2000); 
        if(data.ch++> 127)data.ch=' ';  // setup new data
        data.i+=5;
        data.x*=1.01;
   }
}

void loop()
{
}

void i2c_eeprom_write_byte( int deviceaddress, unsigned int eeaddress, byte data ) {
    int rdata = data;
    Wire.beginTransmission(deviceaddress);
    Wire.write((int)(eeaddress >> 8)); // MSB
    Wire.write((int)(eeaddress & 0xFF)); // LSB
    Wire.write(rdata);
    Wire.endTransmission();
}

// WARNING: address is a page address, 6-bit end will wrap around
// also, data can be maximum of about 30 bytes, because the Wire library has a buffer of 32 bytes
void i2c_eeprom_write_page( int deviceaddress, unsigned int eeaddresspage, byte* data, byte length ) {
    Wire.beginTransmission(deviceaddress);
    Wire.write((int)(eeaddresspage >> 8)); // MSB
    Wire.write((int)(eeaddresspage & 0xFF)); // LSB
    byte c;
    for ( c = 0; c < length; c++)
        Wire.write(data[c]);
    Wire.endTransmission();
}

byte i2c_eeprom_read_byte( int deviceaddress, unsigned int eeaddress ) {
    byte rdata = 0xFF;
    Wire.beginTransmission(deviceaddress);
    Wire.write((int)(eeaddress >> 8)); // MSB
    Wire.write((int)(eeaddress & 0xFF)); // LSB
    Wire.endTransmission();
    Wire.requestFrom(deviceaddress,1);
    if (Wire.available()) rdata = Wire.read();
    return rdata;
}

// maybe let's not read more than 30 or 32 bytes at a time!
void i2c_eeprom_read_buffer( int deviceaddress, unsigned int eeaddress, byte *buffer, int length ) {
    Wire.beginTransmission(deviceaddress);
    Wire.write((int)(eeaddress >> 8)); // MSB
    Wire.write((int)(eeaddress & 0xFF)); // LSB
    Wire.endTransmission();
    Wire.requestFrom(deviceaddress,length);
    int c = 0;
    for ( c = 0; c < length; c++ )
        if (Wire.available()) buffer[c] = Wire.read();
}

on first run, e.g. EEPROM is blank

Writing default struct into memory size in bytes 20
Memory written OK
name testData
ch=  
i= 1
x= 3.14
name testData
ch= !
i= 6
x= 3.17
name testData
ch= "
i= 11
x= 3.20

next run reads the data writen in previous run

name testData
ch= "
i= 11
x= 3.20
name testData
ch= #
i= 16
x= 3.24
name testData
ch= $
i= 21
x= 3.27

Still scratching my head ...... lol. But still will try to implement in my code and get back to you as soon as i get back home.

Sorry that i am a slow learner. Have downloaded a few books .. looking at the EEPROM section. Trying to learn from the examples. Will get back to you.

well still no clue....
i now know that eeprom has limited write capabilities. does it apply to read too?

i think that i have to add int hr1, min1, sec1; to the eeprom.

or

if (httpServer.args() > 0) {
    new_time = httpServer.arg(0);                                        // Stores the Alarm time input
    httpServer.send(200, "text/html", confirmation_screen);}             // when alarm time entered direct user to Confirmaton page
    
    hr = new_time.substring(0, 2);                                      // substring is used to split the data in hour and minute form
    hr1 = hr.toInt();                                                   // toInt is used to convert string into integer
    minut = new_time.substring(3, 5);
    minut1 = minut.toInt();
    sec = new_time.substring(6, 8);
    sec1 = sec.toInt();

new_time = httpServer.arg(0);

how can i compare the eeprom entries to check if the time has changed or not so that it can be entered in eeprom? so that it does not write to the memory again and again.

so many questions in my mind.......confused...... arrrrrghh

can't find a single example ..... the books are confusing.

EEPROMs have limited writes so you use them to store system configuration information which is updated one a week, once a day, etc otherwise use an FRAM.

webaddic:
Rtc time is fine, the time which i store in webserver to switch the relay on and off is not stored . For example, if i wish to switch a light on, at a particular time, say 18:00 hrs. It switches the light on. But next day if i reset the device or switch it off and then turn back on or if there is a power outage then i have to reprogram the web server (enter time for relay switching again).

the information you need to store in the EEPROM are the relay on/off times etc which I assume are not changed often (the time itself is stored in the RTC which is battery backed)

summary from post #14
to save your information in EEPROM a simple way is to use a structure
on power up or reset the problem is how do you know there is valid data in EEPROM?
e.g. on first run of the program the EEPROM will be blank or have data from a another program?
1. the struct should contain some means of checking if the data read from EEPROM is valid

  • in the example below the structure starts with text and ends with a CRC check*
    2. on power up or reset read the EEPROM and check if the data read is valid by checking the text and the CRC
  • (a) if OK continue *
  • (b) if not write a set of initial default values to the EEPROM*

therefore on first power up of your software you save default relay on/off times to EEPROM
How do you update the relay on/off times? this is when you have to update the EPPROM

therefore after a power failure you can read the EEPROM to obtain your saved relay on/off time

Time is updated through a webserver.
Through wifi. Once i log into the webserver.

Once installed, time will be updated once in months or years.

(If i switch off and then switch on the device, the relay is off.Which means there is no default time. After the device logs into the webserver and the time is set, then only the relay switches, after the initial power on.)

In post 14 the first code i shared is

Int hr1, min1, sec1

Time is stored in this.

Which is from from the structure and the void setup and loop comes after that.

Hope i answerd your question