Interacting with a database
Interacting with a Database
January 6, 2020

Understanding Blink Without Delay

 
 

The Blink Without Delay Example Sketch shows you how to blink an LED without using the delay() function. This is extremely important if you want your Arduino to not completely “stop” and wait for the delay to finish before proceeding on to the next step. Unfortunately, people struggle to understand what the example is doing. Let’s change that. Understanding this concept is the difference between beginner and intermediate Arduino.

Get the code

All code from the video and this post (six full example sketches) can be found here on GitHub (no account or git required).

The problem with delay() in Arduino

Nearly everyone starts their Arduino journey with a sketch that blinks an LED.  You are generally taught to do something that looks like this…

  digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);               // wait for a second
  digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);               // wait for a second

This works great and is often a “Eureka” moment for makers.  However, as your code gets more complicated, you suddenly realize that your Arduino can’t do anything else during that 1 second delay.  It can’t read a temperature or detect a button push. The entire thing essentially grinds to a halt while waiting to flip that LED on or off.

The Solution

Most people head to a forum or Arduino Facebook Group  where they are often hastily told “Use blink without delay.”  Unfortunately, while that’s the correct answer, it’s not very clear how it works.  This can make it nearly impossible to modify the example for your needs.  The entire thing is based around the millis() function that’s built into your Arduino software.  Once you understand what that does, it’s really easy to implement it into nearly any project.

Understanding millis()

Nearly every Blink Without Delay example sketch will have a line that looks like this…

unsigned long currentMillis = millis();

Let’s take that one word at a time.

unsigned – You are telling the Arduino compiler that this value will NEVER be a negative number.  This essentially allows the timer to “count” twice as high as if it were a regular “signed” long variable. All the memory it was going to allocate to store negative numbers can be allocated to storing bigger positive numbers.   More on this below.

long – Long variables are extended size variables for number storage, and store 32 bits (4 bytes), from -2,147,483,648 to 2,147,483,647.  Because this is paired with “unsigned,”  it doesn’t have to worry about those negative numbers.  Unsigned means no – sign.  Therefore an “unsigned long” can use that same 32 bits (4 bytes) to store a number from 0 to 4,294,967,295.

currentMillis – This is just the variable name that you are storing the data in and can be nearly anything you want it to be.

millis() – This is where the real magic happens.  It’s not completely obvious, but in the background, the Arduino starts a counter from 0 from the time the board finishes booting.  This counter will continue counting for 49.7 days until it loops over or the Arduino is rebooted. In general this rollover is not an issue for our maker projects, especially if you use the methods described below. This is further described at the bottom of this article.

Putting millis() into practice

Now that you are armed with this knowledge, let’s put it into practice. I’m going to paste a complete sketch below and then we will walk through it to see how it works.

int red = 2;
int intervalRed = 1000; 
unsigned long previousRed = 0;
int redState = LOW;

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

void loop() {
  unsigned long currentMillis = millis();

  if(currentMillis - previousRed >= intervalRed){
    //save this reading!
    previousRed = currentMillis;

    //figure out if you should turn the LED on or off
    if(redState == LOW){
      redState = HIGH;
    }else{
      redState = LOW;
    }
    digitalWrite(red,redState);
  }
}

 Initial Variables

int red = 2; 
int intervalRed = 1000; 
unsigned long previousRed = 0; 
int redState = LOW;

int red = 2;  – The LED is attached to Digital Pin 2 on the Aduino and I’ve called it “red”

int intervalRed = 1000; – Used to store the interval time that the LED needs to blink. In your old style of coding, you would write delay(1000). Now, that same 1000 is stored in a variable so you can use it later in the sketch.

unsigned long previousRed = 0; – This variable will be used to store the LAST time that you made a change to the state (on or off) of the LED.  Since this is at boot, you’ve never done anything and we will set it to 0. Note that it is an unsigned long just like millis() because we will be storing the millis in this variable. We need it to be able to hold the same amount of information.

int redState = LOW; – This is specific to our example, but since we are flip flopping the LED from on to off (aka HIGH to LOW) we want to save the current state of the LED into a variable.

Setup

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

Nothing fancy here, we are just telling the Arduino to set pin 2 as an output so we can use it to blink the LED.

The Loop

void loop() {
  unsigned long currentMillis = millis();

  if(currentMillis - previousRed >= intervalRed){
    //save this reading!
    previousRed = currentMillis;

    //figure out if you should turn the LED on or off
    if(redState == LOW){
      redState = HIGH;
    }else{
      redState = LOW;
    }
    digitalWrite(red,redState);
  }
}

Let’s do another line by line breakdown of the important parts of this sketch.

unsigned long currentMillis = millis(); – Every time we go through the loop, we want to see how many milliseconds it has been since the arduino has booted and save that value to the variable currentMillis

if(currentMillis – previousRed >= intervalRed){ – This is the most important statement in the sketch.

Let’s look at what this would look like 500 milliseconds after the Arduino booted.

currentMillis(now 500) – previousRed (now 0) >= IntervalRed(always 1000)

Since the Arduino is only 500ms into boot 500-0 IS NOT greater than or equal to 1000, so the arduino will just continue on as if nothing has happened and will skip that 12 line “if” statement completely.

Let’s look at what this would look like 1001 milliseconds after the Arduino booted.

currentMillis(now 1001) – previousRed (now 0) >= IntervalRed(always 1000)

This would equal TRUE because 1001 >= 1000.  Now we’re in business.  Let’s continue to step through the sketch.

previousRed = currentMillis; – Since we have now hit the threshold for flipping the LED, we need to save this time. This time will become the NEW threshold.

From there, essentially if the LED was off we turn it on and vice-versa and start the process all over again.

For the curious

Next time we go through the loop, say at 1700 milliseconds

currentMillis(now 1700) – previousRed (now 1001) >= IntervalRed(always 1000)

Will read false until currentMillis counts all the way up to or above 2101.

There are a total of 6 example sketches if you want to step through various ways of making this work.

The millis() rollover problem

It would really stink if the entire thing crashed after the millis hit the limit of 4,294,967,295 at 49.7 days, but that’s not the case.  This is the reason that we are calculating the DURATION between current time and the last time the LED was flipped.  If we were using the opposite logic, saving WHEN we want the LED to flip next time, you would have this problem.  However, when you subtract two unsigned numbers from each other, it is smart enough to account for this with one exception.  In the extremely rare circumstance that you have very long duration of longer than 49.7 days, this will break. Let’s say that you want to (for some reason) blink an LED once every 2 months, then you would be much better off using something like the RTC (Real Time Clock) module in this sponsored link and dealing with actual time instead of this millis method.

Blinking 3 LEDs using Blink Without Delay

Here is an example of this same logic blinking 3 LEDs.

Dan
Dan

Comments are closed.