Go Down

Topic: delay() in only one method (Read 2514 times) previous topic - next topic

strange quark

I'm relatively new to Arduino, but sorry if this is a basic question.

Is it possible to get a delay function to only affect one method and not stop my whole program from executing? (ie if I want one method to run every few seconds, but I need the rest of the program to continually execute)

jraskell

delay() is a blocking delay, won't work for what you are trying to accomplish.

You can use millis() to get reasonably accurate timed activities.  Example:

Code: [Select]

unsigned int timer = millis() + 2000;

void setup(){
  Serial.begin(115200);
}

void loop(){
 
  if(millis() > timer){
    //run myRoutine every 2 seconds
    myRoutine();
    timer = millis() + 2000;
  }
 
  //Rest of program here
 
}

void myRoutine(){
  Serial.println(millis());
 
}

tetwin11

I started using Timer1 from the Jesse Tane, http://www.arduino.cc/playground/Code/Timer1 for timing in my project. My entire code is posted in another thread on the forum if you're interested in seeing how I used it http://arduino.cc/forum/index.php/topic,50010.0.html

Matt
Sometimes things are as they appear

Coding Badly

You can use millis() to get reasonably accurate timed activities.  Example:


Your example fails to work correctly as millis approaches the wrap.

jraskell


You can use millis() to get reasonably accurate timed activities.  Example:


Your example fails to work correctly as millis approaches the wrap.



Good point.  Couple minor tweaks can fix that.
1.Change the assignments to timer = millis();
2.Change the if condition to (millis() - timer > 2000)

Coding Badly

#5
Jan 27, 2011, 09:13 pm Last Edit: Jan 28, 2011, 05:33 am by Coding Badly Reason: 1
Quote
2.Change the if condition to (millis() - timer >= 2000)


;)

Graynomad

How about

Code: [Select]
if(millis() % 2000 == 0) {
    myRoutine();
}


Admitedly is causes some arithmatic in every loop but if there's little else to do anyway.

It will "hickup" at the wrap every 49 days but that may not be a problem.

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

Coding Badly

Two additional problems with that method that make it unusable...

1. millis sometimes increments by 2 instead of 1.  If the value goes from 1999 to 2001, the then-clause is skipped.

2. If the code around the if-condition takes 1.5 milliseconds or longer to run, the then-clause may be skipped.  If the code around the if-condition takes more than 2 milliseconds to run, the then-clause will always be skipped.

But, it's good you mentioned the idea so it can be discussed.


Graynomad

Quote
millis sometimes increments by 2 instead of 1.

Ooow, that's a gotcha I didn't know about, is that to accomodate slight mismatches in the timing, sort of a leap year adjustment.

And yes the other code can't take too long or you miss a slot until the next time.

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

Coding Badly

Quote
Ooow, that's a gotcha I didn't know about, is that to accomodate slight mismatches in the timing, sort of a leap year adjustment


Exactly.

The interrupt fires 1024 times a second (when the processor is clocked at 16 MHz).  When the interrupt fires, the millis value is increment (the "1000" part of the value) and a fraction is accumulated (the "24" part of the value).  When the fraction accumulates to a full millisecond, the millis value is incremented.  One increment for the interrupt the second increment for the fraction.

Graynomad

Quote
millis sometimes increments by 2 instead of 1


OK I've been thinking about this. % will then either return 0 or 1, is that the case?

If so then

Code: [Select]
if((millis() % 2000) < 2) {
    myRoutine();
}


Should work.

______
Rob

Rob Gray aka the GRAYnomad www.robgray.com

Andrew


I'm relatively new to Arduino, but sorry if this is a basic question.

Is it possible to get a delay function to only affect one method and not stop my whole program from executing? (ie if I want one method to run every few seconds, but I need the rest of the program to continually execute)


A while ago on the old forum (http://arduino.cc/forum/index.php/topic,48946.msg349959.html#msg349959) I tried to explain this sort of thing in English rather than code - maybe it will help here.

Quote
Imagine you yourself want to physically ring a bell every 30 seconds. There are two ways you could do it. In the first way you ring the bell, then set an alarm clock for 30 seconds time, go to sleep, wake up when the alarm goes off, ring the bell and repeat. You are not able to do anything in that 30 seconds waiting time except wait for the alarm to go off.

The second way you would ring the bell then look at your clock and note what time it is. Then you start getting on with doing other stuff, but look at the clock periodically to see if 30 seconds has gone by yet. If not, just carry on doing other stuff and glancing at the clock. If 30 seconds has gone by, ring the bell and make a note of the new time, then carry on as before.

The first method is equivalent to to using delay() in Arduino code, the second is equivalent to using millis() to note down when you do something and wait for a time interval to pass before you do it again.

millis() tells you how many milliseconds it's been since the Arduino was powered on or reset. So you can note down what millis() returns now, do some stuff, and when you look at millis() again subtract the first reading and it will tell you how much time has gone by.


Andrew

Coding Badly


OK I've been thinking about this. % will then either return 0 or 1, is that the case?

If so then

Code: [Select]
if((millis() % 2000) < 2) {
    myRoutine();
}


Should work.


You're just going to have to give up modulus for this situation...  When millis increments by one, the then-clause fires twice in quick succession.  Take the simplest case... millis returns 1999, 2000, 2001, 20002... The condition is true for both 2000 and 2001.

Graynomad

Bugger, OK I'll move on :)

_____
Rob
Rob Gray aka the GRAYnomad www.robgray.com

Go Up