How to do calculations involving time. As in time(now) + 5 minutes

Doug101:
int is 16 bit/2-byte or max value of 65,535. Converting from seconds it's about 1.5 months.

Not quite. 65,535 seconds is 1092.35 minutes or 18.20 hours (as I mentioned above) or 0.76 days. Assuming months of 30 days, that's 0.025 months.
45 days (1.5 months) would be 1080 hours or 64,800 minutes or 3,888,000 seconds, off by a factor of about 60 so you convered from minutes instead of seconds..

johnwasser:
Even if it was 68 years, you are not setting your own start date: now() uses January 1st, 1970. 68 years later is 2038. Are you sure your code won't be in use in 22 years?

Wouldn't it be 68 years not 22, if the time isn't set? Not to mention any power cuts or restarts would reset it again.

@johnwasser and @Kounipo

You are making the same mistake I made in an earlier post mixing up seconds and minutes.

Arrrr I did it again.

/// What's wrong with my math. (Here's how I came up with the 136 years.)
unsigned long uses 32 bits/4 bytes or 2^32 - 1 or max value is 4,294,967,295.

If we take 4,294,967,295 seconds and covert to years it's 136.
I think I came up with the 68 years using long with only positive integers.

int is 16 bit/2-byte or max value of 65,535. Converting from seconds it's about 1.5 months.

/////

Should have read

What's wrong with my math. (Here's how I came up with the 136 years.)
unsigned long uses 32 bits/4 bytes or 2^32 - 1 or max value is 4,294,967,295.

If we take 4,294,967,295 MINUTES and covert to years it's 136.
I think I came up with the 68 years using long with only positive integers.

int is 16 bit/2-byte or max value of 65,535. Converting from MINUTES it's about 1.5 months.

But something still doesn't seem right with the math even being of by a factor of 60.

I've using the time converter

For my application (timer to turn outside yard lights off) minutes is fine.

What am I missing?

Doug101:
What am I missing?

You are missing quote tags.. so that we can make sense of what is your answer or comment on previous post and if you have a specific question...

Doug101:
For my application (timer to turn outside yard lights off) minutes is fine.

What am I missing?

As long as you don't mind an error of up to 20% (averaging 10%) on a 5-minute timer then rounding down to the previous minute is fine. If all you want is to turn off a light after about five minutes I would skip the RTC and just use the Arduino's built-in timing:

const int LAMP_COUNT = 4;
const int ButtonPin[LAMP_COUNT] = {2, 3, 4, 5};
const int LampPin[LAMP_COUNT] = {6, 7, 8, 9};
const unsigned long LAMP_ON_INTERVAL = 5UL*60UL*1000UL; // 5 minutes in milliseconds
// Note: global variables are initialized to zero
int ButtonState[LAMP_COUNT];
unsigned long ButtonStateChangeTime[LAMP_COUNT];
unsigned long LampOnTime[LAMP_COUNT];
void setup() {
  for (int i = 0; i < LAMP_COUNT; i++) {
    pinMode(ButtonPin[i], INPUT_PULLUP);
    pinMode(LampPin[i], OUTPUT);
  }
}
void loop() {
  for (int i = 0; i < LAMP_COUNT; i++) {
    ProcessButton(i);
    ProcessLamp(i);
  }
}
void ProcessButton(int index) {
  unsigned long currentTime = millis();
  int newState = digitalRead(ButtonPin[index]);
  // Ignore out-of-range indexes
  if (index < 0 || index >= LAMP_COUNT)
    return;
  // Act on the button if it changed state AND it has been more than
  // five milliseconds since the last change (debounce)
  if (newState != ButtonState[index] &&
      currentTime - ButtonStateChangeTime[index] > 5) {
    // Note: Input pin uses internal pull-up and button connects it to
    // ground so LOW is pressed and HIGH is not pressed
    if (newState == LOW) { // Button Pressed
      // Note: This version extends the time when the button is pushed.  
      // We could add a little code to turn the lamp OFF if the
      // button was pushed while the lamp was already on:
      /*
       * if (LampOnTime[index] != 0) {
       *     // Note: Most relay modules are 'active low' so we use LOW
       *     // to turn the lamp on and HIGH to turn it off.
       *     digitalWrite(LampPin[index], HIGH);  //  lamp Off
       *     LampOnTime[index] = 0;
       * } else {
       */
      // Note: Most relay modules are 'active low' so we use LOW
      // to turn the lamp on and HIGH to turn it off.
      digitalWrite(LampPin[index], LOW);  // Lamp On
      LampOnTime[index] = currentTime;
    }
    ButtonState[index] = newState;
    ButtonStateChangeTime[index] = currentTime;
  }
}
void ProcessLamp(int index) {
  unsigned long currentTime = millis();
  if (LampOnTime[index] != 0 &&
      currentTime - LampOnTime[index] >= LAMP_ON_INTERVAL) {
    // Time to turn the lamp off
    // Note: Most relay modules are 'active low' so we use LOW
    // to turn the lamp on and HIGH to turn it off.
    digitalWrite(LampPin[index], HIGH);  // Lamp Off
    LampOnTime[index] = 0;
  }
}

@J-M-L The what am I missing refers to the math johnwasser and I are doing.

I'm coming up with an unsigned long variable 32 bits can store a value of 136 years.

What's wrong with my math. (Here's how I came up with the 136 years.)
unsigned long uses 32 bits/4 bytes or 2^32 - 1 or max value is 4,294,967,295.

But johnwasser didn't agree. But as I pointed out one is seconds and the other is minutes. But still we are not off by a factor of 60. Just trying to figure out why we are so far apart in our calculation?

Not quite. 65,535 seconds is 1092.35 minutes or 18.20 hours (as I mentioned above) or 0.76 days. Assuming months of 30 days, that's 0.025 months.
45 days (1.5 months) would be 1080 hours or 64,800 minutes or 3,888,000 seconds, off by a factor of about 60 so you convered from minutes instead of seconds..

Can you see why we don't agree?

@johnwasser thanks for the code. (But your took the fun out of it, I wanted to write the code on my own. Now I'm just tweaking what your wrote.

Reason for using RTC or NTP is later I want to tweak the code to turn some lights on at dusk for a few hours, turn sprinklers on/off for watering.

In the future I would like to add 2 seven line LCD displays to show of some of the sensors/switches. (Gate open, a motor/pump left on, etc.) Not sure if I should use a RaspberryPi or Arduinos to control the displays?

Doug101:
@johnwasser thanks for the code. (But your took the fun out of it, I wanted to write the code on my own. Now I'm just tweaking what your wrote.

Reason for using RTC or NTP is later I want to tweak the code to turn some lights on at dusk for a few hours, turn sprinklers on/off for watering.

In the future I would like to add 2 seven line LCD displays to show of some of the sensors/switches. (Gate open, a motor/pump left on, etc.) Not sure if I should use a RaspberryPi or Arduinos to control the displays?

Missing closing ')'

AWOL:
Missing closing ')'

Where is the missing quote?

Doug101:
Where is the missing quote?

I don't know (I hadn't noticed a missing quote, but if you say there is one . . . )
I don't know where the missing closing parenthesis is either.
That's what "missing" means.

AWOL:
I don't know (I hadn't noticed a missing quote, but if you say there is one . . . )
I don't know where the missing closing parenthesis is either.
That's what "missing" means.

Not sure what a missing quote has to do with the question being asked. The questions was why one poster came up with less than a year and I was calculating 136 years for the same max value for a unsigned long variable.

I'm not sure what a missing quote has to do with anything - you're the one who first mentioned a missing quote.

2^32 - 1 is 4 294 967 295

if these are minutes

that's 71582788,25 hours (the 0,25 means 1/4 hours) so 71582788 hours and 15 minutes
71582788 is 2982616,166666666666667 days, the decimal part is actually 4 hours

so we have 2982616 days 4 hours 15 minutes

For the Gregorian calendar the average length of the calendar year (the mean year) across the complete leap cycle of 400 years is 365.2425 but in astronomy, the Julian year is a unit of time; it is defined as 365.25 days of exactly 86400 seconds (SI base unit), totalling exactly 31 557 600 seconds in the Julian astronomical year

So I'll go with the SI unit of 365,25 days

2982616 days is 8165 years and 349,75 days so 349 days and 18 hours

so minutes stored in an unsigned long can represent up to 8165 years 349 days 22 hours and 15 minutes

if these are seconds

4 294 967 295 seconds = 71 582 788 minutes and 0,25 minute = 71582788 minutes and 15 seconds

71582788 minutes = 1193046 hours and 28 minutes

1193046 hours = 49710 days and 6 hours

49710 days = 136 years and 36 days

so seconds stored in an unsigned long can represent up to 136 years 36 days 6 hours 28 minutes and 15 seconds

and because there are 60 seconds in a minute, this is 60 times less (which you can approximate if you look at the years: 8165 years / 60 ~136 years which is what we see

if these are milli seconds
then it's 1000 times less...

4 294 967 295 milli seconds = 4 294 967 seconds and 295 milli seconds
4 294 967 seconds = 71582 minutes and 47 seconds
71582 minutes = 1193 hours and 2 minutes
1193 hours = 49 days and 17 hours

so milli seconds stored in an unsigned long can represent up to 49 days and 17 hours 2 minutes 47 seconds and 295 milli seconds

Now, if you use signed integers, then you get half this,duration

johnwasser:
As long as you don't mind an error of up to 20% (averaging 10%) on a 5-minute timer then rounding down to the previous minute is fine. If all you want is to turn off a light after about five minutes I would skip the RTC and just use the Arduino's built-in timing:

const int LAMP_COUNT = 4;

const int ButtonPin[LAMP_COUNT] = {2, 3, 4, 5};
const int LampPin[LAMP_COUNT] = {6, 7, 8, 9};
const unsigned long LAMP_ON_INTERVAL = 5UL60UL1000UL; // 5 minutes in milliseconds
// Note: global variables are initialized to zero
int ButtonState[LAMP_COUNT];
unsigned long ButtonStateChangeTime[LAMP_COUNT];
unsigned long LampOnTime[LAMP_COUNT];
void setup() {
 for (int i = 0; i < LAMP_COUNT; i++) {
   pinMode(ButtonPin[i], INPUT_PULLUP);
   pinMode(LampPin[i], OUTPUT);
 }
}
void loop() {
 for (int i = 0; i < LAMP_COUNT; i++) {
   ProcessButton(i);
   ProcessLamp(i);
 }
}
void ProcessButton(int index) {
 unsigned long currentTime = millis();
 int newState = digitalRead(ButtonPin[index]);
 // Ignore out-of-range indexes
 if (index < 0 || index >= LAMP_COUNT)
   return;
 // Act on the button if it changed state AND it has been more than
 // five milliseconds since the last change (debounce)
 if (newState != ButtonState[index] &&
     currentTime - ButtonStateChangeTime[index] > 5) {
   // Note: Input pin uses internal pull-up and button connects it to
   // ground so LOW is pressed and HIGH is not pressed
   if (newState == LOW) { // Button Pressed
     // Note: This version extends the time when the button is pushed.  
     // We could add a little code to turn the lamp OFF if the
     // button was pushed while the lamp was already on:
     /*
      * if (LampOnTime[index] != 0) {
      *     // Note: Most relay modules are 'active low' so we use LOW
      *     // to turn the lamp on and HIGH to turn it off.
      *     digitalWrite(LampPin[index], HIGH);  //  lamp Off
      *     LampOnTime[index] = 0;
      * } else {
      */
     // Note: Most relay modules are 'active low' so we use LOW
     // to turn the lamp on and HIGH to turn it off.
     digitalWrite(LampPin[index], LOW);  // Lamp On
     LampOnTime[index] = currentTime;
   }
   ButtonState[index] = newState;
   ButtonStateChangeTime[index] = currentTime;
 }
}
void ProcessLamp(int index) {
 unsigned long currentTime = millis();
 if (LampOnTime[index] != 0 &&
     currentTime - LampOnTime[index] >= LAMP_ON_INTERVAL) {
   // Time to turn the lamp off
   // Note: Most relay modules are 'active low' so we use LOW
   // to turn the lamp on and HIGH to turn it off.
   digitalWrite(LampPin[index], HIGH);  // Lamp Off
   LampOnTime[index] = 0;
 }
}

Nice code.

Question. If I wanted to change the input to detect three different types of button presses, short press (250 msec., 2 short presses in one second, and one long press would that work with the code you posted?

It two people were pressing different buttons at the same time to turn lights one would they interfere with each other?

Look at response # 39 and 40

johnwasser:
Not quite. 65,535 seconds is 1092.35 minutes or 18.20 hours (as I mentioned above) or 0.76 days. Assuming months of 30 days, that's 0.025 months.
45 days (1.5 months) would be 1080 hours or 64,800 minutes or 3,888,000 seconds, off by a factor of about 60 so you convered from minutes instead of seconds..

johnwasser:
It's 18 hours, actually. Even if it was 68 years, you are not setting your own start date: now() uses January 1st, 1970. 68 years later is 2038. Are you sure your code won't be in use in 22 years?

I came up with 136+ years to but johnwasseris coming up with values which are less than a year.

Doug101:
Question. If I wanted to change the input to detect three different types of button presses, short press (250 msec.), 2 short presses in one second, and one long press would that work with the code you posted?

It would definitely require more code and more state information for each button but it's not rocket science.

If it has been a second since the button was first pressed:
If the button is still down you have a long-press.
If the button went back up and stayed up you have a single press.
If the button went up and down again you have a double press.

Doug101:
It two people were pressing different buttons at the same time to turn lights one would they interfere with each other?

No. The buttons each have their own state information and there are no delay() calls or other blocking functions so every button gets processed every time through loop() which should happen hundreds or thousands of times per second.

@johnwasser Perfect.
Is the only time loop code is delayed is when a delay statement is used? Seems to me a for loop looking for multiple button presses over a second would cause a delay too.

Looks like there might be a library for the buttons. Looking at it now.

Doug101:
Is the only time loop code is delayed is when a delay statement is used? Seems to me a for loop looking for multiple button presses over a second would cause a delay too.

You are right. Any loop that is waiting for an event to occur will cause everything else to stop. That's why you NEVER busy-wait for an event or call a function that will not return almost immediately. The loop() HAS TO REPEAT VERY FREQUENTLY (at least 100 times per second) or the buttons will not be responsive.
To 'wait' for an event over that one second interval you record when the button was first pressed so you can use the (now - startTime >= interval) trick to determine when a second has elapsed. This allows all the OTHER buttons to also be checked.

@johnwasser I'm following you.

johnwasser:
You are right. Any loop that is waiting for an event to occur will cause everything else to stop. That's why you NEVER busy-wait for an event or call a function that will not return almost immediately. The loop() HAS TO REPEAT VERY FREQUENTLY (at least 100 times per second) or the buttons will not be responsive.
To 'wait' for an event over that one second interval you record when the button was first pressed so you can use the (now - startTime >= interval) trick to determine when a second has elapsed. This allows all the OTHER buttons to also be checked.

It looks like there's a "Button Library" which might make thinks a bit easier.
http://playground.arduino.cc/Code/Button

I'd like to set up 10 to 15 of my push buttons to work with the four button function.

Doug101:
It looks like there's a "Button Library" which might make thinks a bit easier.
Arduino Playground - HomePage

I'd like to set up 10 to 15 of my push buttons to work with the four button function.

That button library is very simple and will replace:(digitalRead(ButtonPin[i]) == LOW)
with(Buttons[i].pressed())
The "Button by Michael Adams" library that Library Manager knows about is similarly simplistic. It looks like the OneButton library will handle single, double, and long clicks.

One problem with it is that it uses callback functions and passes no identifying information to the callbacks. That means each action on each button requires a separate function. I would prefer to poll the buttons for activity (similar to how my code works) or be able to assign an index to each button that gets passed to the action functions.