Saving times in EEPROM

Hi. I'm working on a project that saves a start and end date that the user can save via a 4x4 keypad.
This will trigger a relay to open or close depending on the time of the day (I also have an RTC).
Since the time needs to be typed in one by one, I've managed to do it so it saves the hours and minutes in the first 8 bits of the EEPROM, one digit each (I'm sure there's more elegant ways to do it).

The problem I'm now having is to retreive these values from the EEPROM, as it's not considering as digits, so I can't compare them with the current time.

Any ideas?

it saves the hours and minutes in the first 8 bits of the EEPROM,

I presume that you mean bytes rather than bits

Please post your code

Please follow the advice on posting code given in posting code

In particular note the advice to Auto format code in the IDE and to use code tags when posting code here as it prevents some combinations of characters in code being interpreted as HTML commands such as italics, bold or a smiley character, all of which render the code useless

Hi UKHeliBob,

I'm not a programmer, just a self-learning hobbist, so my code is most likely to be quite a mess, and not very efficient but here you have it:

#include <EEPROM.h>
#include <Keypad.h>
#include <Wire.h>
#include <RTClib.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 20, 4);

RTC_DS1307 rtc;

String daysOfTheWeek[7] = { "Domingo", "Lunes", "Martes", "Miercoles", "Jueves", "Viernes", "Sabado" };
String monthsNames[12] = { "Ene", "Feb", "Mar", "Abr", "May",  "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic" };

const byte rowsCount = 4;
const byte columsCount = 4;

char keys[rowsCount][columsCount] = {
  { '1', '2', '3', 'A' },
  { '4', '5', '6', 'B' },
  { '7', '8', '9', 'C' },
  { '*', '0', '#', 'D' }
};

const byte rowPins[rowsCount] = { 4, 5, 6, 7 };
const byte columnPins[columsCount] = { 8, 9, 10, 11 };

Keypad keypad = Keypad(makeKeymap(keys), rowPins, columnPins, rowsCount, columsCount);

int rele = 12;
int ledrojo = 3;
int ledverde = 2;
int encendido = 0;
int activo = 1;

void setup() {
  Serial.begin(9600);
  pinMode(rele, OUTPUT);
  pinMode(ledverde, OUTPUT);
  pinMode(ledrojo, OUTPUT);

  lcd.init();                      // initialize the lcd
  lcd.backlight();

#ifndef ESP8266
  while (!Serial); // wait for serial port to connect. Needed for native USB
#endif

  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    abort();
  }

  if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running, let's set the time!");
    // When time needs to be set on a new device, or after a power loss, the
    // following line sets the RTC to the date & time this sketch was compiled
    // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }

}

void loop() {

  if (activo == 1) {
    lcd.setCursor(15, 0);
    lcd.print("*");
    if (encendido == 1) {
      digitalWrite(ledrojo, HIGH);
      digitalWrite(ledverde, LOW);
      digitalWrite(rele, HIGH);
    } else {
      digitalWrite(ledrojo, LOW);
      digitalWrite(ledverde, HIGH);
      digitalWrite(rele, LOW);
    }
  } else {
    digitalWrite(ledrojo, LOW);
    digitalWrite(ledverde, HIGH);
    lcd.setCursor(15, 0);
    lcd.print(" ");
  }

  DateTime now = rtc.now();

  char buf1[] = "DD/MM/YYYY";
  char buf2[] = "hh";
  char buf3[] = "mm";
  char buf4[] = "ss";

  lcd.setCursor(0, 0);
  lcd.print(now.toString(buf1));
  lcd.setCursor(0, 1);
  lcd.print(now.toString(buf2));
  lcd.setCursor(2, 1);
  lcd.print(":");
  lcd.setCursor(3, 1);
  lcd.print(now.toString(buf3));
  lcd.setCursor(5, 1);
  lcd.print(":");
  lcd.setCursor(6, 1);
  lcd.print(now.toString(buf4));

  char key = keypad.getKey();

  //   if (key) {
  //      Serial.println(key);
  //   }

  if (key == '*') {
    lcd.backlight();
    if (activo == 1) {
      activo = 0;
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("PROGRAMA");
      lcd.setCursor(0, 1);
      lcd.print("DESACTIVADO");
      delay(5000);
      lcd.clear();
    } else {
      activo = 1;
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("PROGRAMA");
      lcd.setCursor(0, 1);
      lcd.print("ACTIVADO");
      delay(5000);
      lcd.clear();
    }
    lcd.noBacklight();
  }

  if (key == 'A') {
    setTime("inicio");
    setTime("final");
    lcd.print("PROGRAMA");
    lcd.setCursor(0, 1);
    lcd.print("GUARDADO");
    delay(5000);
    lcd.clear();
    lcd.noBacklight();
  }

  if (key == 'D') {
    checkSetup();
  }
}

void checkSetup() {
  lcd.backlight();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Hora inicio:");
  lcd.setCursor(0, 1);
  lcd.print(EEPROM.read(0));
  lcd.setCursor(1, 1);
  lcd.print(EEPROM.read(1));
  lcd.setCursor(2, 1);
  lcd.print(":");
  lcd.setCursor(3, 1);
  lcd.print(EEPROM.read(2));
  lcd.setCursor(4, 1);
  lcd.print(EEPROM.read(3));
  delay(5000);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Hora final:");
  lcd.setCursor(0, 1);
  lcd.print(EEPROM.read(4));
  lcd.setCursor(1, 1);
  lcd.print(EEPROM.read(5));
  lcd.setCursor(2, 1);
  lcd.print(":");
  lcd.setCursor(3, 1);
  lcd.print(EEPROM.read(6));
  lcd.setCursor(4, 1);
  lcd.print(EEPROM.read(7));
  delay(5000);
  lcd.clear();
  lcd.noBacklight();
}
void setTime(String valor) {

  int h1;
  int h2;
  int m1;
  int m2;
  int key;

  lcd.backlight();
  lcd.clear();
  lcd.setCursor(0, 0);
  if (valor == "inicio") {
    lcd.print("Hora inicio:");
    lcd.setCursor(0, 1);
    lcd.print(EEPROM.read(0));
    lcd.setCursor(1, 1);
    lcd.print(EEPROM.read(1));
    lcd.setCursor(2, 1);
    lcd.print(":");
    lcd.setCursor(3, 1);
    lcd.print(EEPROM.read(2));
    lcd.setCursor(4, 1);
    lcd.print(EEPROM.read(3));
  } else {
    lcd.print("Hora final:");
    lcd.setCursor(0, 1);
    lcd.print(EEPROM.read(4));
    lcd.setCursor(1, 1);
    lcd.print(EEPROM.read(5));
    lcd.setCursor(2, 1);
    lcd.print(":");
    lcd.setCursor(3, 1);
    lcd.print(EEPROM.read(6));
    lcd.setCursor(4, 1);
    lcd.print(EEPROM.read(7));
  }
  lcd.setCursor(0, 1);

  for (int pointer = 0; pointer <= 4; pointer++) {
    lcd.blink();
    lcd.setCursor(pointer, 1);
    if (pointer != 2) {
      key = keypad.waitForKey() - 48;
    }

    if (pointer == 0) {
      if ((0 > key) || (key > 2)) {
        pointer--;
      } else {
        lcd.print(key);
        h1 = key;
      }
    }
    if (pointer == 1) {
      if ((0 > key) || ((h1 == 2) && (key > 3))) {
        pointer--;
      } else {
        lcd.print(key);
        h2 = key;
      }
    }
    if (pointer == 3) {
      if ((0 > key) || (key > 5)) {
        pointer--;
      } else {
        lcd.print(key);
        m1 = key;
      }
    }
    if (pointer == 4) {
      if (0 > key) {
        pointer--;
      } else {
        lcd.print(key);
        m2 = key;
      }
    }
  }
  lcd.clear();
  lcd.noBlink();
  lcd.setCursor(0, 0);
  if (valor == "inicio") {
    EEPROM.write(0, h1);
    EEPROM.write(1, h2);
    EEPROM.write(2, m1);
    EEPROM.write(3, m2);
  }
  if (valor == "final") {
    EEPROM.write(4, h1);
    EEPROM.write(5, h2);
    EEPROM.write(6, m1);
    EEPROM.write(7, m2);
  }
}

At this point of the development, the project does:

  • Display the current date and time
  • When clicking * this will enable/disable the functionality, which is still not coded. When off, the relay is normally closed regardless of the time, setup, etc. On will be a program to turn the relay on an off depending on the time of the day.
  • When clicking A this will take you to a Start and end times setup. This saves a start and end hour and minute in the EEPROM, as you say one digit a byte, not bit.
  • When clicking D this shows the current start and end time values in the EEPROM in a 5 second interval.

Thank you

I would calculate unixtime (seconds since epoch) from the input, easiest format to do calculations and comparisons with.

The time is then stored in a unsigned long integer (32 bits, 4 bytes in size) which can easily be stored in EEPROM.

The time library can help you with this calculation.

The two most used libraries are the Adafruit RTClib and the Time library (I call it TimeLib).

The 'unixtime' or 'epoch' is the number of seconds since 1970. The TimeLib is easier to use with that because it it fully based on the unixtime. It is possible to do calculations with it, without setting it as the new time. That is why I use the TimeLib.

Also the Adafruit RTClib has two functions for it:

  • unixtime(), this returns the unixtime.
  • DateTime( seconds_since_1970), this sets the new time.
    They are used to set the new time or to get the current time.

What do you want to do with that time ?
If you want to display it as text on a display, then you can store it as text. If you want to calculate the time difference, then the Adafruit RTClib is better. If you want to add alarms, then the TimeLib is better, because it has an extension called TimeAlarms.

Hi, thank you both. I will have a look at the TimeLib to see if it can help me, but I'm not 100% sure if the unixtime will serve here, because I want to be able to input hour and minute, but not of a specific day. So for instance, I've input a start time of 14:00 and an end time of 16:30. I want the rele to be open every day at that time. With unixtime, every second since 1970 has a unique ID, right? isn't that a problem to compare "generic" hours?

That is what the TimeAlarms library does. The Adafruit RTClib can not do that.
I don't know how to store settings from the TimeAlarms, perhaps you have to code everything.

Some try to use the benefits of both libraries and use them both in the same sketch. Please don't that. It makes my head spin :confused:

Thank you Koepel!!!! thanks to that advise and a bit of research and trial and error, I've finally managed to fully finish my project!! I'm so excited!

Quick question about the Time library. When less than 10, functions day(), month(), hour(), minute() and second() return a one digit value (for instance month() would return 2 instead of 02). Is there an easy way to make it always two digits?
I've added some code to manually add 0s when the value is less than 10, but I've noticed that at some point, a 0 is added to the end of the time (so it looks like 10:20:160 instead of 10:20:16), so there's some buggy code going on there.

This is the part to display date and time in the LCD:

  lcd.setCursor(0, 0);
  if (day() < 10) {
    lcd.print("0");
    lcd.setCursor(1, 0);
    lcd.print(day());
  } else {
    lcd.print(day());
  }
  lcd.setCursor(2, 0);
  lcd.print("/");
  lcd.setCursor(3, 0);
  if (month() < 10) {
    lcd.print("0");
    lcd.setCursor(4, 0);
    lcd.print(month());
  } else {
    lcd.print(month());
  }
  lcd.setCursor(5, 0);
  lcd.print("/");
  lcd.setCursor(6, 0);
  lcd.print(year());
  lcd.setCursor(0, 1);

  lcd.setCursor(0, 1);
  if (hour() < 10) {
    lcd.print("0");
    lcd.setCursor(1, 1);
    lcd.print(hour());
  } else {
    lcd.print(hour());
  }
  lcd.setCursor(2, 1);
  lcd.print(":");
  lcd.setCursor(3, 1);
  if (minute() < 10) {
    lcd.print("0");
    lcd.setCursor(4, 1);
    lcd.print(minute());
  } else {
    lcd.print(minute());
  }
  lcd.setCursor(5, 1);
  lcd.print(":");
  lcd.setCursor(6, 1);
  if (second() < 10) {
    lcd.print("0");
    lcd.setCursor(7, 1);
    lcd.print(second());
  } else {
    lcd.print(second());
  }

The functions return numbers so they will have either 1 or 2 digits depending on their value. You could consider using sprintf() to format the output like this

#include <Time.h>
#include <TimeLib.h>

void setup()
{
  Serial.begin(115200);
  while (!Serial);
  setTime(23, 59, 50, 10, 2, 2021);
}

void loop()
{
  char buffer[20];
  sprintf(buffer, "%02d/%02d/%04d %02d:%02d:%02d", day(), month(), year(), hour(), minute(), second());
  Serial.println(buffer);
  delay(1000);
}

wow UKHeliBob! that made things way easier!

What's the 20 in between brackets? I thought it was the number of characters so I split it in two variants:

  char fecha[12];
  char hora[8];

  sprintf(fecha, "%02d/%02d/%04d", time.day(), time.month(), time.year());
  sprintf(hora, "%02d:%02d:%02d", time.hour(), time.minute(), time.second());

I first used fecha[10], because 11/02/2021 has 10 digits, but that was kind of merging both variants together (it was something like: 11/02/202111:20:25). It wasn't until I increased it to 12 that it gave me the proper values.

What's the 20 in between brackets?

It is the number of characters in the buffer but you must allow for the terminating zero added by the sprintf() function, so be careful (or generous as I was) when declaring the buffer size. Your original declaration

char hora[8];

was not large enough to hold the output, hence the problem

One last question. So by using your method above, let's say I have a variable fecha that equals = 1430. Although this is a number, it is stored as text. I need to convert it to a number as I need to compare some of them.
But if I do what I've seen somewhere else of adding + 0 to a new integer variable, it gives me a number, yes, but with value 2281. for some reason.
Any idea why is that, and how should I convert the above value to a number?

atoi() should do the job.

thank you wvmarie!! that did indeed do the job! now I believe my code is finally 100% complete :slight_smile:

Not wanting to undo your 100% complete code, but depending on the RTC you have, there may be some user RAM - around 50 or so bytes of it - built into the RTC that you can use to store times in. That way you don't need to worry about wear on the EEPROM.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.