Millis() Timing goes way off over time.

PaulS:

    while(millis()-previousMillis[i]<=on){         //While current time (millis()) minus Previous time is less than ON TIME

digitalWrite(relayPin[i],HIGH);               //Turn ON the relay(array)
    }



This while loop blocks until the current minus the start time is greater than the on time desired. If that isn't the EXACT same thing delay does, I'll eat my hat.

What if i need to delay for longer than 1-5 minutes at a time? doesn't delay stops my code, for the certain amount of time? within that 1-5 minutes of delay(), i could be turning on a bunch of relays.

unless I am confused about the delay().

I think you should check the Timing section of the Playground. There are many libraries that allow you to setup different functions to be called at different intervals. Check out my SimpleTimer lib or the more advanced Time (which supports different "time sources", even i2c RTCs, with ease).

Arduino Playground - LibraryList

Thanks, I will try it out. I was wondering, for the SimpleTimer function you mentioned. How would i be able to use it in functions? can't seem to initialize it correctly to use for the function i made. or will i need to call it all within the void loop()?
Also, how would i be able to blink at different rates (1sec ON, 3min OFF) for your code:

 timer.setInterval(1000, repeatMe);

since it only has the 1000, and it will blink at a constant rate.

What if i need to delay for longer than 1-5 minutes at a time? doesn't delay stops my code, for the certain amount of time? within that 1-5 minutes of delay(), i could be turning on a bunch of relays.

unless I am confused about the delay().

Yes, delay() stops your code for the specified duration. But, so does your code.

The thing you need to do it to get rid of the while loop(s). I'd recommend getting rid of the Zone function, too. Move the code into loop(). On each pass through loop, you check to see if it is time to turn a pin on, or not. You check to see if it is time to turn a pin off, or not. If it is time, turn the pin on, or off, and note the time that you did that. This becomes the basis for deciding exactly when to do the next thing.

If you were running around the yard turning sprinklers on and off, you wouldn't stand next to a valve for the whole time it is not, staring at your watch to see if it had been on long enough,. You'd turn on the ones that needed to be on (you should have an array of them), and then go sit in the sun and drink beer until it is time to turn them off (at least, that's what I'd do). Not all of them need to be turned on or off at the same time, so you have to check your watch periodically, so that you turn them off at about the right time. But, while the sprinklers are running, you can sit back and drink beer and enjoy the sun, or watch TV, or work on the honey-do list.

With just 4 sprinklers, you wouldn't need a piece of paper to write down the times, but, if you were managing a golf course with 1000 zones, you probably would.

Millis is the function that replaces your watch. The variables, or arrays, replace the paper. Nothing replaces the thinking that needs to occur, but the Arduino can easily decide if enough time has passed to turn a zone on or off, AND it won't drink all your beer while you are playing with the sprinklers.

SimpleTimer is not a function, but a class.

Variable intervals... interesting question. I tried a solution with this example.

#include <SimpleTimer.h>

const short ledPin = 13;

// the timer object
SimpleTimer timer;

void printUptime() {
    Serial.print("Uptime (s): ");
    Serial.println(millis() / 1000);
}

void blinkLed() {
    static boolean ledOn = true;

    if (ledOn) {
        digitalWrite(ledPin, HIGH);
        timer.setTimeout(50, blinkLed);
        //Serial.println(millis());
    }
    else {
        digitalWrite(ledPin, LOW);
        timer.setTimeout(1500, blinkLed);
        //Serial.println(millis());
    }
    
    ledOn = !ledOn;
}

void setup() {
    Serial.begin(9600);
    timer.setInterval(5000, printUptime);
    timer.setTimeout(500, blinkLed);
}

void loop() {
    timer.run();
}

I've been away from serious C++ for too long, what happens when you pass an unsigned long to unsigned long []?

void loop(){
  Zone(5000UL,3600000UL,0,previousMillis);
}

void Zone(unsigned long on, unsigned long off, int i, unsigned long previousMilis[]){
  currentMillis[i] = millis();
  if (currentMillis[i] - previousMillis[i]> off){
    previousMillis[i]=currentMillis[i];
    while(millis()-previousMillis[i]<=on){
      digitalWrite(relayPin[i],HIGH);
    }
    digitalWrite(relayPin[i],LOW);
  }
}

and does it matter if you don't actually use it in the function?

I've been away from serious C++ for too long, what happens when you pass an unsigned long to unsigned long []?

The previousMillis variable is an array. The call is correct.

and does it matter if you don't actually use it in the function?

It's referenced twice in the function.

Okay so he did pass an address to something that expects an address, like I wrote; it has been too long.
Still nowhere in the function is previousMilis[] used.

Still nowhere in the function is previousMilis[] used.

void Zone(unsigned long on, unsigned long off, int i, unsigned long previousMilis[]){
currentMillis[ i ] = millis();
if (currentMillis[ i ] - previousMillis[ i ]> off){
previousMillis[ i ]=currentMillis[ i ];
while(millis()-previousMillis[ i ]<=on){
digitalWrite(relayPin[ i ],HIGH);
}
digitalWrite(relayPin[ i ],LOW);
}
}
Looks like it's used to me...

No code box because you can't highlight in a code box, like you used to be able to do.

Interesting though all this is ...

If I may suggest to the OP:

I don't think turning on timers to water plants is rocket science. You should be able to make it work quite easily.

Looking at the code above:

while(millis()-previousMillis[ i ]<=on){

digitalWrite(relayPin[ i ],HIGH);
    }

You don't need to keep turning the pin on. Once it's on, it's on. You could make it:

digitalWrite(relayPin[ i ],HIGH);  // turn on pin
while(millis()-previousMillis[ i ]<=on)  // my delay technique
 {   }

or just:

digitalWrite(relayPin[ i ],HIGH);  // turn on pin
delay (on);

However this, fairly obviously, only lets you water one lot of plants at once. Does it matter? I doubt the plants will care.

Might also be worth looking at the recent blink series of threads by Morris Dovey.

Paul;

previousMilis

is not

previousMillis

How many times have you seen just 1 character make all the difference? If I had a dollar for every time.....

previousMilis

is not

previousMillis

Until you mentioned that, I was reading this thread with the font size at a reasonable level. With old(er) eyes, the two l's looked just like 1. I had to increase the font size significantly to see that there were indeed two l's in the name used, versus one in the argument.

Sorry about that...

I have View Zoomed to 120% and still it's not always apparent at first glance.
The first PC's I programmed for money on had 9" blazing white on black screens, my 19" flat panel is soooo nice!

In any any case, it doesn't matter because the call and args do match even if most of it is bull teats.

Okay guys!!!

I have listened to everyone who has replied, and removed the while loop and i've rewritten it to this:
I hope it will be understandable.

/* Cosinepi
aeroTimer
*/

unsigned char relayPin[4] = {13,5,6,7}; // Define the led's pin

void Zone(unsigned long on, unsigned long off, int i);

int mode[4]={LOW,LOW,LOW,LOW};
unsigned long previousMillis[4]={0,0,0,0};
unsigned long Sec = 1000L;
unsigned long Min= 60*1000L;
unsigned long Hour=60*Min;
unsigned long currentMillis[4]={0,0,0,0};

void setup()
{
  int i;
  for(i = 0; i < 4; i++)
  {
    pinMode(relayPin[i],OUTPUT);
    digitalWrite(relayPin[i],mode[i]);
  }
}

void loop(){
  Zone(3000UL,8000UL,0);
  Zone(5000UL,1800000UL,1);
  Zone(10000UL,3600000UL,2);
}

void Zone(unsigned long on, unsigned long off, int i){
  currentMillis[i] = millis();
  if (currentMillis[i] - previousMillis[i]>= off && mode[i]==LOW){
    previousMillis[i]=currentMillis[i];
    mode[i]=HIGH;
      digitalWrite(relayPin[i],mode[i]);
  }
  if (currentMillis[i]-previousMillis[i]>= on && mode[i] == HIGH){
    previousMillis[i]=currentMillis[i];
    mode[i]=LOW;
      digitalWrite(relayPin[i],mode[i]);
  }
}

What do you guys think? it is better and more efficient? are there things I should improve?

I am going to make another module to monitor my PH, EC, humidity and TEMP of the rooms and water nutrients. I will need your help soon, so thank you guys in advance!

Looks good. One minor thing - there is no need to have a CurrentMillis array, you can have a single local variable in Zone or indeed, just use millis directly.

Just something to look out for ... What will happen when Millis() overflows --- won't you be waiting another 36 hours or so for previousmillis to catch-up ????

Maybe I am wrong ... has happened before :wink:

Maybe I am wrong ... has happened before :wink:

I'm afraid it's happened again :wink:

That subtractive pattern works fine even when millis overflows.

For more detail check johnwasser's analysis in this thread: http://arduino.cc/forum/index.php/topic,60215.0.html.

Hi

This is how I would have done it...

const unsigned long Sec = 1000L;
const unsigned long Min = 60L * Sec;
const unsigned long Hr = 60L * Min;

//zone 1:
const unsigned long zone_1_Off = 1 * Min; // 1 min
const unsigned long zone_1_On = 5 * Sec; // 5 sec
unsigned long zone_1_Start = 0;
unsigned long zone_1_Stop = zone_1_On;
const int zone_1_Pin = 13;

//zone 2:
const unsigned long zone_2_Off = 30 * Min; // 30 min
const unsigned long zone_2_On = 15 * Sec; // 15 sec
unsigned long zone_2_Start = 0;
unsigned long zone_2_Stop = zone_2_On;
const int zone_2_Pin = 12;


unsigned long myTime = 0;

void setup(){
  pinMode(zone_1_Pin,OUTPUT);
  pinMode(zone_1_Pin,OUTPUT);
}

void loop(){
  myTime = millis();

  // zone 1 logic
  if (zone_1_Start < myTime){digitalWrite(zone_1_Pin,HIGH);}
  if (zone_1_Stop < myTime){
    digitalWrite(zone_1_Pin,LOW);
    zone_1_Start = myTime + zone_1_Off;
    zone_1_Stop = zone_1_Start + zone_1_On;
  }
  
  // zone 2 logic
  if (zone_2_Start < myTime){digitalWrite(zone_2_Pin,HIGH);}
  if (zone_2_Stop < myTime){
    digitalWrite(zone_2_Pin,LOW);
    zone_2_Start = myTime + zone_2_Off;
    zone_2_Stop = zone_2_Start + zone_2_On;
  }
}

-Fletcher

CharlieD:
Just something to look out for ... What will happen when Millis() overflows --- won't you be waiting another 36 hours or so for previousmillis to catch-up ????

Maybe I am wrong ... has happened before :wink:

Think how it works with a clock. Same way with unsigned numbers. What is 1 pm - 11 am? 2 hours.

Windoze calculator if View is set to Scientific allows math done in HEX, DEC, or BIN, but no unsigned's I see. Still, at times it helps seeing what the bits do.

Another coding strategy for controlling the four zones independently/asynchronously...

// uses files attached to http://arduino.cc/forum/index.php/topic,93959.0.html
/*----------------------------------------------------------------------------*/
/* Independent control of zone 1                                              */
/*----------------------------------------------------------------------------*/
time_t z1on,z1off;                     /* Zone 1 on, off durations            */

void zone1off(void)
{  digitalWrite(relayPin1,LOW);        /* Turn off zone flow                  */
   aftr(z1off,zone1on);                /* After off interval, turn it back on */
}                                      /*  end: zone1off()                    */
void zone1on(void);
{  digitalWrite(relayPin1,HIGH);       /* Turn on zone flow                   */
   aftr(z1on,zone1off);                /* After on interval, turn it back off */
}                                      /*  end: zone1on()                     */
void zone1(time_t on,time_t off)
{  z1on = on;                          /* Save on interval                    */
   z1off = off;                        /* Save off interval                   */
   zone1on();                          /* Start with an on cycle              */
}                                      /*  end: zone1()                       */
/*----------------------------------------------------------------------------*/
/* Independent control of zone 2                                              */
/*----------------------------------------------------------------------------*/
time_t z2on,z2off;

void zone2off(void)
{  digitalWrite(relayPin2,LOW);
   aftr(z2off,zone2on);
}
void zone2on(void);
{  digitalWrite(relayPin2,HIGH);
   aftr(z2on,zone2off);
}
void zone2(time_t on,time_t off)
{  z2on = on;
   z2off = off;
   zone2on();
}
/*----------------------------------------------------------------------------*/
/* Independent control of zone 3                                              */
/*----------------------------------------------------------------------------*/
time_t z3on,z3off;

void zone3off(void)
{  digitalWrite(relayPin3,LOW);
   aftr(z3off,zone3on);
}
void zone3on(void);
{  digitalWrite(relayPin3,HIGH);
   aftr(z3on,zone3off);
}
void zone3(time_t on,time_t off)
{  z3on = on;
   z3off = off;
   zone3on();
}
/*----------------------------------------------------------------------------*/
/* Independent control of zone 4                                              */
/*----------------------------------------------------------------------------*/
time_t z4on,z4off;

void zone4off(void)
{  digitalWrite(relayPin4,LOW);
   aftr(z4off,zone4on);
}
void zone4on(void);
{  digitalWrite(relayPin4,HIGH);
   aftr(z4on,zone4off);
}
void zone4(time_t on,time_t off)
{  z4on = on;
   z4off = off;
   zone4on();
}
/*----------------------------------------------------------------------------*/
/* Standard Arduino setup() function                                          */
/*----------------------------------------------------------------------------*/
void setup(void)
{  pinMode(relayPin1,OUTPUT);
   pinMode(relayPin2,OUTPUT);
   pinMode(relayPin3,OUTPUT);
   pinMode(relayPin4,OUTPUT);
   /*-------------------------------------------------------------------------*/
   /* Start the four zones and assign each an 'on' and 'off' duration         */
   /*-------------------------------------------------------------------------*/
   zone1(ss(3),mm(30));                /* 3-second on, 30-minute off          */
   zone2(ss(3),mm(30));
   zone3(ss(3),mm(30));
   zone4(ss(3),mm(30));
}
void loop(void)
{  idle();
}

You'll want to add your pin definitions and customize the zone on and off durations, but shouldn't need to do anything else. :grin:

Edited to repair stupid coding error!