Delay statements are great and all, especially for their simplicity, but they can really put a damper on things when trying to multitask your Arduino. Forget delay and hop on the millis() train!

Why?

It's pretty easy to just throw in a delay statement whenever you need your microcontroller to pause briefly, but it's a real bummer when you are trying to do other things, like monitor a button push. As my Arduino skills have improved, I figured it was time to drop the delay and learn to be able to multitask my Arduino. Yes, it does add a bit more code to your programs, but it, in turn, makes you a more skilled programmer and increases the potential of your Arduino. To do so, we will need to learn how to use the "millis()" command.

How?

You see, delays pause your Arduino's program, making it incapable of doing anything else in that time period. Instead of pausing our entire program for a specified time, we will learn to count how much time has passed before completing an action. This, of course, is accomplished with our good friend "millis()" and a few variable friends to store our data. To make things easy, we'll start with everybody's first sketch, "Blink," but instead we will "Blink without Delay."

First begin like any other program, declaring any necessary pins or variables, such as your LED on pin 13. We'll also need an integer to store the current state of the LED. This will be set to LOW as the initial LED state is off. Then declare a variable "previousMillis" of type "unsigned long." Instead of using "int," unsigned long variables are 32 bits, for variables whose value can become very large—like the potential amount of time we may want to wait until an action is taken.

"previousMillis" will be used to store the last time our LED blinked. "const long" is also 32 bits but will never change, or is constant. We will set this to 1000 and use it as our pause time, measured in milliseconds because we always want to pause for 1000ms. Then, of course, remember to declare your pinMode for your LED as usual.

                    // constants won't change. Used here to set a pin number :
const int ledPin =  13;      // the number of the LED pin

// Variables will change :
int ledState = LOW;             // ledState used to set the LED

// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0;        // will store last time LED was updated

// constants won't change :
const long interval = 1000;           // interval at which to blink (milliseconds)

void setup() {
// set the digital pin as output:
pinMode(ledPin, OUTPUT);
}


Then we move onto the loop! Remember, instead of delaying, we want to count how much time has passed since our last blink, in our case, 1000ms. If the stated time has passed, it's time to change the state of our LED, either from off to on or vice versa.

First, we will set the unsigned long "currentMillis" equal to "millis()" which places the current time count in millis. This will help us determine if the difference between current time and previous time has surpassed 1000ms. To do so we say, "if current time minus the previous time our LED blinked is greater than or equal to our assigned value of 1000ms, store the time of the most recent blink as previousMillis." This will help us remember how long it's been since the last blink the next time around the loop.

Then, if the LED state is LOW, make it HIGH, else, make it LOW. Then digitalWrite the LED HIGH or LOW depending on the previous state.

                    void loop() {
// here is where you'd put code that needs to be running all the time.

// check to see if it's time to blink the LED; that is, if the
// difference between the current time and last time you blinked
// the LED is bigger than the interval at which you want to
unsigned long currentMillis = millis();

if (currentMillis - previousMillis >= interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;

// if the LED is off turn it on and vice-versa:
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}

// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
}
}


Remember to take it slow and break down the code into smaller sections that you can more easily understand. If you don't get it just yet, that's okay—it takes some practice. If you do understand it and get it working, try adding in a second LED to the mix and get them blinking at different rates. More information on this subject can be found on Adafruit Industries website, where Bill Earl has provided a three-part series on multi-tasking your Arduino—even adding motors and addressable LEDs to the mix so check it out! Thanks again for following along!

Other MIT-i Innovations:

Give this project a try for yourself! Get the BOM.

• Warren Winters 2017-11-04

Congratulations on converting to milli’s(), but there are still some issues you should correct in the code you’re recommending to your Readers.

This statement is inefficient:
if (currentMillis - previousMillis >= interval)

The inefficiency is caused by this statement:
previousMillis = currentMillis;

You could have coded this:
Time_To_Do_Work = currentMillis + interval;
- This does the Mathematical work only ONCE during ‘interval’.

Then, the test will be:
if (currentMillis >= Time_To_Do_Work)
-  Without the Mathematical work, you save a few cycles during EVERY loop.  While not an important consideration for this trivial task, you’re trying to show Readers how they SHOULD be writing their own code.

___
The next issue is more serious.

The error is again in this statement:
previousMillis = currentMillis;

As a result of coding like this, your Application’s Time accuracy WILL drift.

It is more accurate to code the statement like this:
Time_To_Do_Work = Time_To_Do_Work + interval;

This coding will allow the Application to take action as soon as possible after the Time ‘interval’ has been reached, or expired.

This is a more accurate way to code the time a Repeating event is scheduled.  For a randomly occurring event, this is the accurate way to schedule the time the next will be required:
Time_To_Do_Work = currentMillis + interval;

So, the best way to code is determined by what the Application’s Time Management requires.

When things get busy, having saved a few cycles each loop might come in handy.

___
The coding approach you’re recommending also missed an opportunity to introduce your Readers to Sub-Routines.

These can greatly increase the readability of code, and decrease errors during Maintenance tasks.

Consider this approach:
if (currentMillis >= Time_To_Do_Work)
{
Update_LED_Status();
}

I would usually code a statement this SIMPLE on a single line, with the various tests aligned with each other (So you can EASILY see the differences).

I would also align the various Actions (Sub-Routine Calls), so that you can see the various actions that the code can perform.

___
Lastly, you could have recommended that Readers investigate coding using the ‘Finite-State Machine’ approach.
https://en.m.wikipedia.org/wiki/Finite-state_machine

• fabelizer 2017-11-18

As I understand it, the addition operation does not adjust for timer overflow as the subtraction operation does. That is why in arduino work the subtraction method is preferred. However, I am certainly not a SME. The timer creep can be adjusted by carefully selecting when your work timer reset occurs, though when functions are included, it can be quite difficult to adjust accurately due to varying timings of execution.