Skip to content


Replacing delay() in Arduino Sketches – IsTime() to the rescue!

TipsGraphicThe problem with delay() is that your code is stuck waiting for the time to elapse. What if you wanted to do something more in that time? There are places where you don’t need to do anything with your processor resource but more often then not, you need to be doing something useful. The IsTime() function introduced lets you use all that time to do more things.

This is the first of two related articles. First we discuss the limitations associated with using delay() type functions and introduces the IsTime() solution. The second article will introduce the PolledTimer library which simplifies things even more.

Delay()
In the Arduino blink sketch is a basic microcontroller “hello world” example where its perfectly OK to use the delay() method as there really is nothing else to do.

DelayLoopCodeChunk

In the loop() function of the “blink” sketch we see delay() used twice to wait for one second each. The digitalWrite() functions are turning on and off an LED between these waits. The result is a flashing LED that is on then off for one second. This makes a perfect “hello world” example as its very clear and easy to understand. delay() is used well here as there is nothing else we want to do with the processor.

But what if we want to do other things? Say for example we are reading the state of a switch or some signal, perhaps a temperature reading that we are watching. We want to flash the warning LED on our gizmo but we also need to keep watching the temperature? One idea might be to delay() some, then turn on the LED and make a measurement to check if we still need to flash, then delay() again, then turn off the LED and do measurement check again. The code below is not real, its just a bit of pseudo code for illustration.

Temp

 

As you can see the code above is much more complicated but there is a good reason for this. The code as been separated into two tasks, checking the temperature and flashing the LED. Also note that there is no delay, instead we have a function called flashTime() that indicates when its time to do a LED change (toggle for flashing).

The flashTime() function could just be a delay() and return true. But it could also check how long its been and return true if more then 1 second has elapsed. If it did that, then the loop() function would cycle very fast, there would be no delay(), instead there would be some code that keeps track of time inside the flashTime() method.  You would be checking the temperature very quickly while the LED was flashing. You could do a lot of things in that time.

There are some problems with writing a function like flashTime() for every task you might have. Considering that all these functions would basically be identical except for some timing variables, it should be easy to create one generic function and pass in some variables specific to each task.

int IsTime(timeMark,timeInterfal)
My first solution to having a simple method to keep track of timing was the IsTime() function. Later, I made a class with more features which I’ve included in the PolledTimer library discussed later. IsTime() is shown below.

IsTimeCodeGraphic

Two parameters are passed to IsTime(). “timeMark” is a pointer to a long integer, where the last time mark value is stored. This is the reference time used to measure against. “timeInterval” is an interval time in milliseconds. Its how long you want to wait before IsTime() returns true.

IsTime() might seem a bit complex but its basically using the Arduino millis() method to get a millisecond time value which starts at 0  and counts up once each millisecond. Since the millisecond value could overflow, IsTime() checks for and deals with that.

Update: An astute visitor points out that the roll over check code is not needed. The math would work regardless as the long int would overflow to the same result. I will make this change to my code library.

Modified Blink Example
Here we add the IsTime() code shown above, a few variables and we rewrite the loop() function to use IsTime() function to control the LED flash. The code snippet below contains only the changed loop function. There are some links after the code to a fully functional code file.

Temp2

 

The code comments describe what’s going on in the code. The key here is that we are not using any delay() functions so the loop executes without waiting. We can add other things to the loop now, but we have to use IsTime() rather then delay() as the delay will again stop things from happening and that could include the IsTime() controlled code. This is one of the reasons most multi-tasking operating systems are pre-emptive, but that is too big of a subject to tackle now. This article is already too long…

To add another LED or task to this loop we only need to add the timemark and timeInterval variables for each. Then use the isTime() function again. For example to flash a second LED at a different rate we could do this…

Temp3

Now we are flashing two LEDs, at different rates, and we still have almost 100% of the processor available to do other things!

Article Resources

  • Modified Blink Sketch  This is a text file that will open in your browser. Copy this to a new Arduino sketch to run.

Related Links

Feedback
As always, tell us what you think with a comment. We love to read them. Let us know if there are similar articles out there, I might update this article to add more resource links once they are discovered.

Posted in Arduino, Discovering, Hacks, Ideas, Microcontroller, Projects, Tips.

7 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

  1. Nice article. It si similar approach as in http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay

    And by the way. Because you are using unsigned long the overflow handling is unnecessary. The IsTime function can look like https://gist.github.com/1654676 with same functionality.

  2. admin said

    @ah… Thanks I added those links to the article. I do feel that the overflow handling is necessary. It will occur in about 50 days. If you have a project that runs for months the overflow could cause issues. Its not hard to fix. The only requirement now is that you call the IsTime function once every 50 days and the overflow will not be an issue. Yes its a very unlikely thing, but again, easy to fix.

    Its a surprise that the C# solution was named IsTime() as well. I’ve been using this trick for about 15 years. Its a simple way to get multi-tasking type performance.

  3. Michael said

    Nice idea! I do something cruder for timing the main loop events in my AVC robot Data Bus. I may use this instead. Thanks!

  4. Well even after 50 days it’s not a problem.

    Let’s say that you program is running for 4294967286 ms (which is 10 ms before overflow). And now you will call IsTime function next after 20ms. So the millis function return 10. Without any consideration of overflow you calculate this:

    10 – 4294967286

    And because the overflow (wrap around) nature of unsigned long the result is 20. It’s same result as if you calculate your formula for “overflow correction”:

    (MAX – 4294967286) + 10

    Actually result is 19 because you don’t consider the fact that actually overflow from MAX to 0 takes 1ms but it’s just a detail.

    My point is if you skip the Rollover detection the behavior of IsTime function will be exactly the same.

    PS: The code https://gist.github.com/1654676 is not in C# it’s C. Git just put the “#” character as a link after “C” – a bit unfortunate :)

  5. admin said

    @ah I see your point now. Thank you! I just did not think about how the roll over would happen with the math. I actually did a test to prove it to myself.

  6. the idea is good. but as far i understand it it will not save me much code or headache if i use it instead of doing it manualy setting up two variables for “lastTime” and “interval” and then do a if(millis()-lastTime>interval) { lastTime=millis(); do more stuff here;}

  7. Despite the special handling needed for time-sensitive applications (and that would probably only affect clocks and long-time data loggers) , the IsTime() idea is very useful for anything from button debounce to PWM motor control, POV displays and much more. I always re-write the part of the code that contains delay() if I’m using someone else’s code and never use delay() myself – it never actually made any sense to me to make the uC literally stop for any period of time – there’s always a useful task that can be completed during that time.