Go Down

### Topic: Millis() Timing goes way off over time. (Read 2745 times)previous topic - next topic

#### PaulS

#15
##### Mar 02, 2012, 11:15 pm
Quote
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.

#### tuxduino

#16
##### Mar 02, 2012, 11:35 pm
SimpleTimer is not a function, but a class.

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

Code: [Select]
`#include <SimpleTimer.h>const short ledPin = 13;// the timer objectSimpleTimer 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();}`

#### GoForSmoke

#17
##### Mar 03, 2012, 01:25 am
I've been away from serious C++ for too long, what happens when you pass an unsigned long to unsigned long []?

Code: [Select]
`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?

Nick Gammon on multitasking Arduinos:
1) http://gammon.com.au/blink
2) http://gammon.com.au/serial
3) http://gammon.com.au/interrupts

#### PaulS

#18
##### Mar 03, 2012, 01:28 am
Quote
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.

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

It's referenced twice in the function.

#### GoForSmoke

#19
##### Mar 03, 2012, 02:11 am
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.

Nick Gammon on multitasking Arduinos:
1) http://gammon.com.au/blink
2) http://gammon.com.au/serial
3) http://gammon.com.au/interrupts

#### PaulS

#20
##### Mar 03, 2012, 02:26 amLast Edit: Mar 03, 2012, 02:28 am by PaulS Reason: 1
Quote
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.

#### Nick Gammon

#21
##### Mar 03, 2012, 05:18 am
Interesting though all this is ...

If I may suggest to the OP:

http://gammon.com.au/blink

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:

Quote
Code: [Select]
`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:

Code: [Select]
`digitalWrite(relayPin[ i ],HIGH);  // turn on pinwhile(millis()-previousMillis[ i ]<=on)  // my delay technique {   }`

or just:

Code: [Select]
`digitalWrite(relayPin[ i ],HIGH);  // turn on pindelay (on);`

However this, fairly obviously, only lets you water one lot of plants at once. Does it matter? I doubt the plants will care.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

#### dxw00d

#22
##### Mar 03, 2012, 08:18 am
Might also be worth looking at the recent blink series of threads by Morris Dovey.

#### GoForSmoke

#23
##### Mar 03, 2012, 11:50 amLast Edit: Mar 03, 2012, 11:53 am by GoForSmoke Reason: 1
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.....
Nick Gammon on multitasking Arduinos:
1) http://gammon.com.au/blink
2) http://gammon.com.au/serial
3) http://gammon.com.au/interrupts

#### PaulS

#24
##### Mar 03, 2012, 01:09 pm
Quote
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...

#### GoForSmoke

#25
##### Mar 03, 2012, 02:17 pm
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.
Nick Gammon on multitasking Arduinos:
1) http://gammon.com.au/blink
2) http://gammon.com.au/serial
3) http://gammon.com.au/interrupts

#### cosinepi

#26
##### Mar 04, 2012, 08:07 am
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.

Code: [Select]
`/* CosinepiaeroTimer*/unsigned char relayPin[4] = {13,5,6,7}; // Define the led's pinvoid 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!

#### wildbill

#27
##### Mar 04, 2012, 12:37 pm
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.

#### CharlieD

#28
##### Mar 04, 2012, 01:46 pm
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 ;-)

#### wildbill

#29
##### Mar 04, 2012, 01:53 pmLast Edit: Mar 04, 2012, 02:16 pm by wildbill Reason: 1
Quote
Maybe I am wrong ... has happened before ;-)

I'm afraid it's happened again

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.

Go Up

Please enter a valid email to subscribe

### Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy