How to Use millis(): Arduino Multi-tasking

As you advance from basic Arduino projects like blinking LEDs, reading simple sensors, and controlling servos, you’ll naturally want to tackle more ambitious and complex projects. This often requires merging elements from different sketches, which can sometimes lead to unexpected issues when trying to make them work together seamlessly.

Unlike personal computers or Raspberry Pi boards, Arduino is a simple microcontroller with no operating system, meaning it can only run one program at a time. This limitation might make you wonder how to manage multiple tasks concurrently on an Arduino.

Fear not! In this blog post, we’ll explore how using the millis() function instead of the delay() function can unlock the true potential of your Arduino projects. By taking a different approach and learning how to create non-blocking code, you’ll be able to manage multiple tasks on an Arduino effectively, even without an operating system. So, let’s dive in and discover how to take your Arduino skills to the next level!

What is millis()?

millis() is a function in the Arduino programming environment that returns the number of milliseconds since the current program started running on the Arduino board. It is commonly used to measure elapsed time or to create non-blocking delays, such as in managing timed events, debouncing buttons, or creating animations without halting the execution of the rest of the code.

What does millis() look like?

The easiest way to understand millis() is to think of it as a stop watch that starts ticking in milliseconds as soon as the board is powered on. Let’s see it counting up in the serial monitor:

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

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

As soon as you upload this sketch, the timing will start. Open the serial monitor to see the clock counting up. Each time through the loop, the current elapsed time will get printed in the window. You’ll notice that because this internal clock counts in milliseconds, the number gets really big, really quick.

So how long can we run this timer? How high can the Arduino’s hardware clock count to? The answer is 4,294,967,296! This means you can run the sketch for almost 50 days straight, more than enough for most Arduino projects. Once it exceeds this number, the timer “overflows” which means it resets back to 0 and starts counting up again.

This hardware clock starts “ticking” every time a sketch starts running, whether or not you’re using it in the code. It runs all by itself in the background so you can tap into it anytime you need to create timed events like blinking LEDs, reading sensors, or anything else that needs to happen at regular intervals. Best of all the millis() function won’t block other code from running like the delay() function does.

How to Use millis()

In order to use millis() for carrying out timed events, you’ll need to set up at least three variables to help you manage and keep track of time:

  • Current Time – We’re going to have to keep our eye on the clock and keep track of the current time. In order to get the current time, we’ll call the millis() function and set it equal to the current time variable. We need to update the current time at least once in the loop() function and this is usually done in the first line of code. So every time through the loop, the current time variable is going to be holding the current value of the millis() function.
  • Previous Time – We need a way to tell how much time has elapsed from the last time an event happened like an LED blinked or a sensor was read. We know how to get the current time but when did the timing period begin? We’ll create a variable to keep track of when the timing started for the period we’re trying to measure.
  • Period – How often do we want an event to happen? Let’s say you want to blink an LED every 250ms, read a sensor every 30 seconds or even sound a tone from a buzzer every second like an audible timer. We need a variable to store this time period.

Keep in mind that the millis() function returns an unsigned long integer value that can get really big depending on how long you run your program. Not just any variable type will work to contain these values. If you’re just getting started with millis() you may not have come across the unsigned long variable type yet. Let’s see how it compares with some of other variable types you may be more familiar with.

What’s an unsigned long variable and why is it used with millis()?

I imagine in your Arduino journey so far, you’ve already used a few different variable data types like int, byte, float, etc. The data type specifies the kind of data and size the variable will be storing so it’s important to choose the right one. For instance, a byte holds data from 0 to 255. This is way too small to use with millis(). Integers (int) can hold numbers form -32,768 to positive 32,767 but this would also get filled up in less than a minute. We could use a long variable type, which holds 32 bits of data from -2 billion-ish to 2 billion-ish. This could work! But since the millis() function measures elapsed time since the program started running, the returned values are always non-negative so there’s a lot of wasted storage space there. That’s where the unsigned identifier comes in. For the most part, variables types are signed by default, which means it can accept both positive and negative values. Unsigned variables accept only positive values so they have a higher range. Using an unsigned long shifts the negative value storage capacity to the positive side, which pretty much doubles what millis() can count to before resetting back to 0. An unsigned long variable can store integer values ranging from 0 to 4,294,967,295 (2^32 – 1) which equals almost 50 days (49.7 to be exact) so it’s the best option for representing elapsed time in milliseconds in most Arduino applications.

Examples Using millis()

In these examples we’re going to start small by trying to blink an LED without using any delay() functions. Then, we’ll add more components, each with their own timing requirements so we have multiple events happening simultaneously!

If you want to build and code along, here’s a circuit diagram of how I have all the components wired up. In the video you’ll notice that the blue LED was originally connected to pin 10 of the Arduino but later on when I added the servo library, it conflicted with the PWM functionality of the pin so I moved it to pin 5. I’ve updated this in the diagram and all the code examples below.

Circuit diagram connecting multiple components like LEDs, a push button and servo to the Arduino to control using millis().

Blink an LED without delay()

Using millis() just to blink an LED may seem like overkill, but this example will get us familiar with using the variables I talked about above plus a very important concept known as a state machine. In addition to tracking time, we need to be able to track whether the LED is on or off and whether or not it’s time to change the LED state based on the blink period you specify. Let’s check out the code and then we’ll go through it in more detail:

 int redPin = 11;  //pin our red LED is connected to

 int ledState = LOW; //used to control red LED state, we're starting with it OFF

 unsigned long currentMillis() = 0; //stores the current time

 unsigned long blinkPreviousMillis = 0; //stores last time red LED was updated

 const unsigned long blinkPeriod = 500; //interval to blink red LED in milliseconds

 void blinkLED() {
  /*if the difference between the current time and last time the LED blinked is greater than or equal to the blink period, we need to blink the LED*/
  if (currentMillis - blinkPreviousMillis >= blinkPeriod) {
   //record the current time the blink happens to blinkPreviousMillis
   blinkPreviousMillis = currentMillis;
   //if the LED is off (LOW) change the LED state to on (HIGH)
   if (ledState == LOW) {
    ledState = HIGH;
   }
   /*if the LED is on (HIGH), turn it off (LOW)*/
   else {
    ledState = LOW;
   }
   //output the state of the red LED
  digitalWrite(redPin, ledState);
  }
 }

 void setup() {
  pinMode(redPin, OUTPUT); //set red LED pin as an output
 }

 void loop() {
  //check millis for the current time
  currentMillis = millis(); 

  blinkLED(); //run the blinkLED function
 }

Let’s take a look at some of the key lines of code in this Arduino sketch and how they work to keep track of time and the state of the LED.

Blink LED Variables with millis()

In this section, I set up the variables we’re going to be using in the sketch.

int ledState = LOW;

I created a variable to manipulate the state of the LED and set it to OFF at the start of the sketch.

unsigned long currentMillis() = 0;

This variable will maintain the current value of the millis() function each time through the loop(). I set it equal to 0 to start with and the hardware clock will begin counting up as soon as the sketch is uploaded.

unsigned long blinkPreviousMillis = 0;

The blinkPreviousMillis variable will act as a timestamp for when the last event occurred. In other words, when the last time the state of the LED was changed. Anytime you perform math with millis, it’s good idea to use the same variable type so blinkPreviousMillis is set as an unsigned long. To start, this variable is set to 0 to mark the beginning of the state that the LED is off.

const unsigned long blinkPeriod = 500;

This is the blink rate we want for the LED. In this case, the LED will blink on and off every 500 milliseconds. Using an unsigned long to contain a small number like 500 may seem a bit excessive so you’ll see some developers use other variable types like int or const int. It depends on how complex the math is that you’ll be using in your sketch. Variables of the same type tend to behave more reliably than mixing them so that’s why more often than not you’ll see any variables dealing with time expressed as unsigned long or const unsigned long. The const portion just tells the Arduino that this value is constant and won’t change.

void blinkLED()

I assume that if you’re interested in learning how to use the millis() function with Arduino, that you want to control more than one component at the same time. I also assume that you want each component to do things at different rates. In this case, I like to organize each component and its actions into its own function that I can then call in the loop() function. This is totally optional and you can choose to keep all your code in the loop() function instead. I’m going to put all the code that has to do with the blinking of the red LED inside its own function called blinkLED().

if (currentMillis – blinkPreviousMillis >= blinkPeriod)

The first thing I do in this function is check whether or not the blinkPeriod of 500ms has elapsed. We do this by subtracting the timestamp that marks the last time the LED blinked, blinkPreviousMillis, from currentMillis, or the current time. If the elapsed time is greater than or equal to the blinkPeriod, then it’s time to change the state of the LED.

blinkPreviousMillis = currentMillis;

Before changing the state of the LED, I like to record the time into blinkPreviousMillis so we have an updated timestamp for future checks against when the last state change happened.

ledState if else statements

The state of the LED is handled using familiar if else statements that checks the current led state and changes it to the opposite state when the blinkPeriod is reached. This is also known as creating a state machine, an important concept when working with millis(). State machines keep track of the current state of a component and changes it according to a trigger. In our case, the trigger is when the blinkPeriod of 500ms is reached.

digitalWrite(redPin, ledState);

So far, we’ve recorded the time that the state change is taking place in blinkPreviousMillis and set the new state of the LED with ledState if else statements within the Arduino. But we haven’t sent this information out to the LED yet. In this last line of code in the blink() function, the Arduino will send either 5V or 0V to the red LED to turn it on or off depending on the value of ledState.

loop() Function

currentMillis = millis();

The first thing I do in the loop() function is check the clock to get the current time. This currentMillis is then used for all the extra functions we create for our other components.

blinkLED();

Then I include blink() and the other functions we’ll be creating in the next examples in the loop() function so they are called.

Upload this sketch to your Arduino and the red LED should start blinking at a rate of 500 ms. Try changing the blinkPeriod value and see how it affects the blink rate just as using a delay() would but without the blocking side effects. Now that we have one component working, let’s add another.

Blink + Fade LEDs with millis()

In addition to blinking an LED, another popular effect is to fade them. Adding these different lighting effects together to your project adds more visual interest. Let’s add this to our example sketch by having the red LED blink at 500ms while having the blue LED fade up every 10ms.

 int redPin = 11;  //pin our red LED is connected to
 int bluePin = 5;  //pin our blue LED is connected to

 int ledState = LOW; //used to control red LED state, we're starting with it OFF
 byte brightness = 0; //starting LED brightness
 byte fadeIncrement = 1; //how much to change the brightness after each period
 
 unsigned long currentMillis() = 0; //stores the current time

 unsigned long blinkPreviousMillis = 0; //stores last time red LED was blinked
 unsigned long fadePreviousMillis = 0; //stores last time blue LED was faded up

 const unsigned long blinkPeriod = 500; //interval to blink red LED in milliseconds
 const unsigned long fadePeriod = 10; //interval to fade blue LED in milliseconds

 void blinkLED() {
  /*if the difference between the current time and last time the LED blinked is greater than or equal to the blink period, we need to blink the LED*/
  if (currentMillis - blinkPreviousMillis >= blinkPeriod) {
   //record the current time the blink happens to blinkPreviousMillis
   blinkPreviousMillis = currentMillis;
   //if the LED is off (LOW) change the LED state to on (HIGH)
   if (ledState == LOW) {
    ledState = HIGH;
   }
   /*if the LED is on (HIGH), turn it off (LOW)*/
   else {
    ledState = LOW;
   }
   //output the state of the red LED
  digitalWrite(redPin, ledState);
  }
 }

 void fadeLED() {
  /*if the difference between the current time and last time the LED faded up is greater than or equal to the fade period, we need to increment the brightness of the LED*/
  if (currentMillis - fadePreviousMillis >= fadePeriod) {
   //record the current time the fade happens to fadePreviousMillis
   fadePreviousMillis = currentMillis;
   //output the brightness of the LED
   analogWrite(bluePin, brightness);
   //increase the brightness by 1 increment for the next period
   brightness += fadeIncrement;
   }
 }

 void setup() {
  pinMode(redPin, OUTPUT); //set red LED pin as an output
  pinMode(bluePin, OUTPUT); //set blue LED pin as an output
 }

 void loop() {
  //check millis for the current time
  currentMillis = millis();

  blinkLED(); //run the blinkLED function
  fadeLED(); //run the fadeLED function
 }

Looking over this Arduino code, you’ll notice some repetitive elements. If each component is doing something at different rates, each one will need its own variable for tracking when the last event happened and the period to check for.

Fade LED Variables with millis()

There are a few new variables I set up for the fading blue LED. Here’s a run down of the more important ones.

byte brightness = 0;

We’ll start with a brightness of 0 (off) and work our way up from there. I chose to use the byte variable type because it goes from 0 – 255, perfect for controlling the brightness of an LED which lives in the exact same range.

byte fadeIncrement = 1;

For a smooth fade effect, you want to use a smaller number here. In this case, every 10ms the blue LED will fade up by an increment of 1 until it reaches 255.

unsigned long fadePreviousMillis = 0;

Just like we did for the red blinking LED, I set up a similar variable for time-stamping when the blue LED fades up.

const unsigned long fadePeriod = 10;

The blue LED will fade up and get brighter every 10 milliseconds.

void fadeLED()

This new function will contain all the code for controlling the fading effect of our blue LED. At a glance, it looks a lot like the blinkLED() function we wrote before and works in a very similar manner.

if (currentMillis – fadePreviousMillis >= fadePeriod)

Just like we did in the blinkLED() function, the very first thing we do is to check whether or not our 10ms time period has elapsed. If you subtract the previous time the blue LED faded, fadePreviousMillis, from the current time, currentMillis, you’ll get the elapsed time. If it is equal to or greater than the fadePeriod, then it’s time to fade up the blue LED.

fadePreviousMillis = currentMillis;

Before fading up our blue LED, let’s record the time that this change is about to take place in fadePreviousMillis so we can use this to determine the next time the LED has to fade up again in the future. In order to record the time, we check the currentMillis variable which was updated in the loop() function.

analogWrite(bluePin, brightness);

It’s time to push this update from the Arduino to the blue LED which will light up according to the brightness value.

brightness += fadeIncrement;

Now we increment the brightness by 1 so it’s ready for the next fade period. The += operator is just shorthand for brightness = brightness + fadeIncrement; – either syntax works the same. Because we’re using a byte variable type for brightness, the value will reset to 0 after reaching 255.

fadeLED();

Finally, don’t forget to call your new fadeLED() function in the loop()!

Upload this sketch to your Arduino and now you should see both LEDs doing their thing. The red one should be blinking at a rate of 500ms while the blue LED fades up to max before resetting to 0 and starting again.

Blink, Fade & Toggle LEDs with millis()

Let’s add a third timing component to our millis() example! Having a responsive button that can trigger other events is important for interactive projects. Buttons are often one of the first components that we discover delay() getting in the way. Having too many delay() functions in your sketch causes button presses to go uncounted and sensor readings to be missed. Using millis() to time out these readings is a better way to capture them reliably. In addition to our blinking red LED and fading blue LED, let’s use a push button to turn the green LED on.

 int redPin = 11; //pin our red LED is connected to
 int bluePin = 5; //pin our blue LED is connected to
 int greenPin = 9; //pin our green LED is connected to
 int buttonPin = 7; //pin our button is connected to

 int ledState = LOW; //used to control red LED state, we're starting with it OFF
 byte brightness = 0; //starting LED brightness
 byte fadeIncrement = 1; //how much to change the brightness after each period
 int toggleState = LOW; //used to control the toggle state of our green LED, we're starting with it OFF
 
 unsigned long currentMillis() = 0; //stores the current time

 unsigned long blinkPreviousMillis = 0; //stores last time red LED was blinked
 unsigned long fadePreviousMillis = 0; //stores last time blue LED was faded up
 unsigned long buttonPreviousMillis = 0; //stores the last time button read took place

 const unsigned long blinkPeriod = 500; //interval to blink red LED in milliseconds
 const unsigned long fadePeriod = 10; //interval to fade blue LED in milliseconds
 const unsigned long buttonPeriod = 100; //interval to read the button

 void blinkLED() {
  /*if the difference between the current time and last time the LED blinked is greater than or equal to the blink period, we need to blink the LED*/
  if (currentMillis - blinkPreviousMillis >= blinkPeriod) {
   //record the current time the blink happens to blinkPreviousMillis
   blinkPreviousMillis = currentMillis;
   //if the LED is off (LOW) change the LED state to on (HIGH)
   if (ledState == LOW) {
    ledState = HIGH;
   }
   /*if the LED is on (HIGH), turn it off (LOW)*/
   else {
    ledState = LOW;
   }
   //output the state of the red LED
  digitalWrite(redPin, ledState);
  }
 }

 void fadeLED() {
  /*if the difference between the current time and last time the LED faded up is greater than or equal to the fade period, we need to increment the brightness of the LED*/
  if (currentMillis - fadePreviousMillis >= fadePeriod) {
   //record the current time the fade happens to fadePreviousMillis
   fadePreviousMillis = currentMillis;
   //output the brightness of the LED
   analogWrite(bluePin, brightness);
   //increase the brightness by 1 increment for the next period
   brightness += fadeIncrement;
   }
 }

 void toggleLED() {
  /*if the difference between the current time and last time the button was read is greater than or equal to the button period, we need to read the button*/
  if (currentMillis - buttonPreviousMillis >= buttonPeriod) {
   //record the current time the button read happens to buttonPreviousMillis
   buttonPreviousMillis = currentMillis;
   //if the button reads LOW, that means it has been pressed so light up the green LED
   if (digitalRead(buttonPin) == LOW) {
    toggleState = HIGH;
   }
   /*otherwise, keep the green LED off*/
   else {
    toggleState = LOW;
   }
  //output the state of the green LED
  digitalWrite(greenPin, toggleState);
  }
 }

 void setup() {
  pinMode(redPin, OUTPUT); //set red LED pin as an output
  pinMode(bluePin, OUTPUT); //set blue LED pin as an output
  pinMode(greenPin, OUTPUT); //set green LED pin as an output
  pinMode(buttonPin, INPUT_PULLUP); //set button to an input using the internal pull-up resistor
 }

 void loop() {
  //check millis for the current time
  currentMillis = millis();

  blinkLED(); //run the blinkLED function
  fadeLED(); //run the fadeLED function
  toggleLED(); //run the toggleLED function
 }

To add our new button toggling LED component to the mix, we went through pretty much the same steps as the previous two components.

Toggle LED Variables

Here are the new variables that are specific to our new component.

int toggleState = LOW;

Similar to our red blinking LED, this variable will control the state of our green LED. I have it set to OFF at the start of the sketch.

unsigned long buttonPreviousMillis = 0;

We need to track the time when the button was last read so we know if it’s time to read it again.

const unsigned long buttonPeriod = 100;

I want the button to be read every 100ms. Setting this number too high can cause you to miss button presses so experiment a bit until you get the responsiveness you want. This is also true for sensors where readings are used to trigger other events. You want to pick an interval that complements the sensor you’re using. For instance, when using temperature sensors or photoresistors that are read throughout the day, it may make more sense to use a longer interval. Capacitive touch, sound detectors or motion sensors that must react instantly to input should be read at more frequent intervals.

void toggleLED()

There’s not too much different happening in this function that we already didn’t see with the others so let’s run through it real quick. All the code to control the toggling of our LED on and off with a button push is in this function.

if (currentMillis – buttonPreviousMillis >= buttonPeriod)

First we check to see if it’s time to read the button. If the difference between the current time and last time the button was read exceeds the buttonPeriod of 100ms then let’s proceed to read the button.

buttonPreviousMillis = currentMillis;

Let’s timestamp when the reading happens so we’re ready for a future time comparison.

toggleState if else statements

At the moment the button is read, if it happens to be pressed then the toggleState for the green LED is set to HIGH. Otherwise the button hasn’t been pressed and the toggleState is set to LOW.

digitalWrite(greenPin, toggleState);

With our toggleState set, it’s time to push this information out to the green LED. The Arduino will send 5V to turn on the LED if the toggleState value is HIGH and 0V if it’s LOW.

toggleLED();

Finally, include a call to your toggleLED() function in the loop().

Upload this sketch to your Arduino and test the button. While the red LED blinks and the blue one fades, you should be able to push the button any time and light up the green LED. Try much larger values for the buttonPeriod and see what happens. The larger the value, the more button presses the program will miss. I found that 100ms works well.

Blink, Fade & Toggle LEDs with a Servo Sweep using millis()

I’m not done yet! No millis() tutorial is complete without a servo. So far we’ve incorporated many important use cases where switching to millis() becomes necessary to keep our program running seamlessly. Servos are a core component to any robotics, animatronics or moving prop project so being able to incorporate them alongside other components will allow you to build more visually stunning and interactive projects.

 #include <Servo.h> //include servo library

 int redPin = 11; //pin our red LED is connected to
 int bluePin = 5; //pin our blue LED is connected to
 int greenPin = 9; //pin our green LED is connected to
 int buttonPin = 7; //pin our button is connected to
 int servoPin = 6; //pin our servo is connected to

 int ledState = LOW; //used to control red LED state, we're starting with it OFF
 byte brightness = 0; //starting LED brightness
 byte fadeIncrement = 1; //how much to change the brightness after each period
 int toggleState = LOW; //used to control the toggle state of our green LED, we're starting with it OFF
 int servoIncrement = 1; //how much to move the servo horn each period
 int servoPos = 0; //starting servo position

 Servo servo; //create a servo object
 
 unsigned long currentMillis() = 0; //stores the current time

 unsigned long blinkPreviousMillis = 0; //stores last time red LED was blinked
 unsigned long fadePreviousMillis = 0; //stores last time blue LED was faded up
 unsigned long buttonPreviousMillis = 0; //stores the last time button read took place
 unsigned long servoPreviousMillis = 0; //stores the last time the servo horn moved

 const unsigned long blinkPeriod = 500; //interval to blink red LED in milliseconds
 const unsigned long fadePeriod = 10; //interval to fade blue LED in milliseconds
 const unsigned long buttonPeriod = 100; //interval to read the button
 const unsigned long servoPeriod = 15; //interval to move the servo horn

 void blinkLED() {
  /*if the difference between the current time and last time the LED blinked is greater than or equal to the blink period, we need to blink the LED*/
  if (currentMillis - blinkPreviousMillis >= blinkPeriod) {
   //record the current time the blink happens to blinkPreviousMillis
   blinkPreviousMillis = currentMillis;
   //if the LED is off (LOW) change the LED state to on (HIGH)
   if (ledState == LOW) {
    ledState = HIGH;
   }
   /*if the LED is on (HIGH), turn it off (LOW)*/
   else {
    ledState = LOW;
   }
   //output the state of the red LED
  digitalWrite(redPin, ledState);
  }
 }

 void fadeLED() {
  /*if the difference between the current time and last time the LED faded up is greater than or equal to the fade period, we need to increment the brightness of the LED*/
  if (currentMillis - fadePreviousMillis >= fadePeriod) {
   //record the current time the fade happens to fadePreviousMillis
   fadePreviousMillis = currentMillis;
   //output the brightness of the LED
   analogWrite(bluePin, brightness);
   //increase the brightness by 1 increment for the next period
   brightness += fadeIncrement;
   }
 }

 void toggleLED() {
  /*if the difference between the current time and last time the button was read is greater than or equal to the button period, we need to read the button*/
  if (currentMillis - buttonPreviousMillis >= buttonPeriod) {
   //record the current time the button read happens to buttonPreviousMillis
   buttonPreviousMillis = currentMillis;
   //if the button reads LOW, that means it has been pressed so light up the green LED
   if (digitalRead(buttonPin) == LOW) {
    toggleState = HIGH;
   }
   /*otherwise, keep the green LED off*/
   else {
    toggleState = LOW;
   }
  //output the state of the green LED
  digitalWrite(greenPin, toggleState);
  }
 }

 void servoSweep() {
  /*if the difference between the current time and last time the servo horn moved is greater than or equal to the servo period, we need to move the servo horn*/
  if (currentMillis - servoPreviousMillis >= servoPeriod) {
   //record the current time the servo horn movement happens to servoPreviousMillis
   servoPreviousMillis = currentMillis;
   //increment the servo position by 1
   servoPos += servoIncrement;
   /*if the servo position goes above 180, reverse the increment by turning it negative. If the servo position goes below 0 then reverse the neg increment to positive*/
   if ((servoPos > 180) || (servoPos < 0)) {
    servoIncrement = -servoIncrement;
   }
   //output the servo horn position
   servo.write(servoPos);
  }
 }

 void setup() {
  pinMode(redPin, OUTPUT); //set red LED pin as an output
  pinMode(bluePin, OUTPUT); //set blue LED pin as an output
  pinMode(greenPin, OUTPUT); //set green LED pin as an output
  pinMode(buttonPin, INPUT_PULLUP); //set button to an input using the internal pull-up resistor
  servo.attach(servoPin); //attach the servo
 }

 void loop() {
  //check millis for the current time
  currentMillis = millis();

  blinkLED(); //run the blinkLED function
  fadeLED(); //run the fadeLED function
  toggleLED(); //run the toggleLED function
  servoSweep(); //run the servoSweep function
 }

Similar to the fading LED, we added the necessary timing variables and one for the starting servo position and horn increments. Then I put all the code that controls the servo movements in a separate function to call in the loop().

Servo Variables with millis()

Here’s a rundown of the variables that pertain to the servo. I assume you’re familiar with setting up the servo in your code like including the library, setting up the servo object and attaching it to an Arduino pin in the setup() so I’ll skip those lines of code.

int servoIncrement = 1;

For a smooth sweep, I want the servo horn to increment by 1 degree each timing period.

int servoPos = 0;

I’m having the servo start at 0 degrees at the beginning of the sketch and it will work its way up from there.

unsigned long servoPreviousMillis = 0;

This variable will store the last timestamp that the servo horn moved.

const unsigned long servoPeriod = 15;

A period of 15ms will give us a relatively fast sweep. You can play around with this number to get the servo horn moving at the rate you want.

void servoSweep()

This function resembles the fadeLED() function we created earlier in that we’re incrementing through the movement of the servo horn just like we incremented through the brightness of the blue LED. But unlike the brightness that cycles back to 0 because of the byte variable type, we have to set boundaries for the servo movement so it stays within the 0 – 180 degree range.

if (currentMillis – servoPreviousMillis >= servoPeriod)

This bit of math that you’ve seen over and over again is the first important step to determining whether or not an action should take place. Here we determine if the servoPeriod of 15ms has elapsed. If it has, then it’s time to move the servo horn.

servoPreviousMillis = currentMillis;

Because the servo horn is about to move, let’s record the time in servoPreviousMillis so we have an updated value for the next time comparison.

servoPos += servoIncrement;

Here we increase the servo position by 1 degree.

if ((servoPos > 180) || (servoPos < 0))

Unlike fading up our blue LED where we used a byte variable type for brightness that uses the exact same range as PWM values, there’s no variable type that goes from 0 – 180 to match the range of a servo’s rotation. In this case we have to create a boundary that changes the direction of movement when the horn tries to go outside the boundary.

servoIncrement = -servoIncrement;

If the servoPos tries to go to 181, then the servoIncrement reverses to -1. This causes the servo horn to turn in the opposite direction as 1 degree is subtracted during each period. When the servoPos tries to go below 0, then the servoIncrement reverses again to +1 and begins moving towards 180 degrees again.

servo.write(servoPos);

Before we wrap up this function, we have to output the servoPos to the servo.

servoSweep();

Finally, add the call to our newly created servoSweep() function within the loop().

Upload this sketch to your Arduino and watch the servo horn sweep back and forth while each LED is doing its thing. Be sure to test the push button and check how responsive the green LED is.

Ditch the delay()

We now have four different things going on at what looks to be the same time. I encourage you to keep building on to this Arduino sketch with your own components to see how far you can take it – or until the Arduino runs out of memory! We’ve shown that despite its one-line-at-a-time limitation, the Arduino is capable of handling multiple tasks while staying responsive to external events like user inputs or sensor readings.

I hope you’re more inspired to build bigger, more interactive projects by ditching the delay() now that you know how to use millis() to free up the processor for other tasks and implement these tasks as state machines that can operate independently and simultaneously, further enhancing the multi-tasking capabilities of your Arduino projects.

If you get stuck or need more direction, I invite you to join our Engineering Artists community where you can master vital animatronics and robotics skills to accelerate your growth in less time with real-time interactive courses focused on doing along with a motivating community to keep you on track.

Some links in this post are to affiliate sites. If you purchase something through them, I may earn a small commission — which costs you nothing! I am very grateful for your support when you use my links to make a purchase.

Stop Scrolling. Start Creating.

Find out about new courses and live events!
Get my latest guides and reference materials.
Unsubscribe anytime!

    Copyright © 2022 Rachel De Barros • All rights reserved.