limits on delay() ?

HI All,

I'm going to be using my Arduino to trigger a relay that turns on a pump to water my crops. I'll be doing it twice a day. So basically I'm turning on a pin for about 2.5 minutes, then I want it off for 12 hours, on 2.5 min, off 12 hours...
the 12 hour delay looks massive: delay(43200000)
I feel like I shouldn't be using such a huge delay, but I can't find documentation that says it can't be done. Does anyone have any insight on if delay can be used with such large numbers?
Would there be any issues with leaving it on for days, or even weeks at a time (which negates the possibility of milis() instead)?

Thanks

Delays works fine with numbers like that, but bear in mind that the Arduino's clock is not particularly accurate and if you leave it running for days you'll find that the Arduino's idea of the current time gets pretty inaccurate. If you want it to do things based on real-world times over longer than a few hours you probably need to add a real time clock.

the 12 hour delay looks massive: delay(43200000)

Literals are interpreted as ints, in the absence of directives to the contrary. You need to use 43200000UL.

Would there be any issues with leaving it on for days, or even weeks at a time (which negates the possibility of milis() instead)?

Why does running for long periods of time negate the use of millis? In fact, millis() works fine. It will roll over after 49 days, but your watch rolls over after just 24 hours. You don’t have any problem determining the interval from 8:00 PM yesterday to noon today, do you? Neither does the Arduino.

The longest delay time is 4,294,967,295 msec (~49.7 days). You can, of course, do multiple delay() calls to effect longer intervals. As PeterH pointed out, the Arduino’s notion of time may not be spot-on - so it might be interesting to see if that accuracy might be improved by applying a correction factor.

Since the topic of millis() also came up, I’ll share two short functions I’ve written. The first extends millis() to 64-bits

/*----------------------------------------------------------------------------*/
/* Extend millis() for 64-bit up-time                                         */
/*----------------------------------------------------------------------------*/
unsigned long long ms64(void)
{  static union                        /* tl and ts[] occupy same location    */
   {  unsigned long long tl;           /* tl is 64-bit time in msec           */
      unsigned long ts[2];             /* ts[0] is bits 32-63, ts[1] is 0-31  */
   }  u = { 0 };                       /* initialize to count from RESET time */
   unsigned long t = millis();         /* 32-bit time from millis()           */

   if (t < u.ts[1]) ++u.ts[0];         /* If millis() has rolled over, adjust */
   u.ts[1] = t;                        /* Update with new millis() value      */
   return u.tl;                        /* Return 64-bit uptime                */
}                                      /*  end: uptime()                      */

and the second provides me with 64-bit epochal time. The beginning of the epoch can be whenever you want, but I’ve found it convenient to adopt the unix epoch which began January 1,1970 at 00:00:00.000 so I can use the unix time conversion code:

unsigned long long rst_time = 0-0;     /* Epochal time of latest RESET (msec) */
/*----------------------------------------------------------------------------*/
/* Return current time as msec into the epoch                                 */
/*----------------------------------------------------------------------------*/
unsigned long long mstime(void)
{  return rst_time + ms64();           /* Return reset time + uptime          */
}                                      /*  end: mstime()                      */

PaulS:

the 12 hour delay looks massive: delay(43200000)

Literals are interpreted as ints, in the absence of directives to the contrary. You need to use 43200000UL.

The compiler doesn’t gratuitously truncate literal numbers. Try this:

void mydelay (unsigned long foo)
  {
  Serial.println (foo);
  }
  
void setup ()
  {
  Serial.begin (115200);
  mydelay (43200000);
  mydelay (4294967295);
  }
  
void loop () {}

Output:

43200000
4294967295

There is no need to clutter your code with U and UL if the compiler can work out your intent, which it can and has in this case.

PeterH:
...bear in mind that the Arduino's clock is not particularly accurate ...

I agree with this though. Last time I checked my Arduino's clock was running about 1% out. Translate that to 1% of a day, and you get 0.24 hours which is 14.4 minutes. So you probably don't want to lose or gain 14 minutes a day. A real-time clock is definitely a good idea for long intervals, plus you then avoid problems if the power goes off, because they generally have battery backup on them.

The compiler only knows of your intent in those cases by automatic type promotion to the type specified by the function prototype.
"Littering" your code with UL will save a lot of grief when it comes to arithmetic.

Much better style to avoid seeing the large numbers in the first place:

#define SECOND 1000L
#define MINUTE (60*SECOND)
#define HOUR (60*MINUTE)

  ...
  delay (12*HOUR) ;
  ...

few remarks:

  1. I second Mark T's style, as the intention of the code will be understood in 6 months ...

  2. Real Time Clocks also have their inaccuracies:
    From the ds1307 datasheet - http://www.sparkfun.com/datasheets/Components/DS1307.pdf -
    CLOCK ACCURACY
    The accuracy of the clock is dependent upon the accuracy of the crystal and the accuracy of the match between the capacitive load of the oscillator circuit and the capacitive load for which the crystal was trimmed. Additional error will be added by crystal frequency drift caused by temperature shifts. External circuit noise coupled into the oscillator circuit may result in the clock running fast.
    OK, the DS1307 might not be the most accurate but it is one of the most frequent mentioned on this forum.

The DS3231 is more accurate and mentions an accuracy in the order of 1 ppm == 1 second in 10 days, and an crystal aging effect of 5ppm in 10 years.
In short, less than a second per day which is around 11ppm.

Additional bonus for the RTC, they have date functions, leapyears etc.

That's an Uno with a ceramic resonator I guess - they can be desoldered and a 16MHz crystal and SMT caps put in its place (with care), gets you more like 20ppm with the right components - shame the Uno doesn't have a crystal for the ATmega I think

Just for fun - extreme delay:

/* Delay up to 584,558,050 years */
void xdelay(unsigned long long t)
{  unsigned int z = ~0;
   while (t > z)
   {  t -= z;
      delay(z);
   }
   delay(t);
}

now we need a blink without extremeDelay() sketch :slight_smile: