Flashing multiple LEDs at the same time

Introduction
A common question is to ask how to flash 2 (or more) LEDs at the same time. Typically the person asking will be using delay and will be experiencing the main problem with delay: it blocks and stops anything else from happening.

This tutorial contains several different approaches to the problem of flashing 2 or more LEDs at the same time with independent timing for each LED. The basic principles for each can all be adapted for other situations requiring multiple timers working independently without blocking. All the ideas presented demonstrate the principles of using millis() for timing, and for collaborative, multi-tasking code.

Further reading
Using millis for timing
Demonstration for several things at the same time

Thanks
In addition to the people who contributed directly to this tutorial the following also gave helpful advice and comments: @killzone_kid , @Koepel , @dlloyd , @anon57585045 , @dougp. Thank you all for your contributions and interest.

12 Likes

C style code
My code below flashes 2 LEDs, one connected to pin 2 and one to Pin 3, each with a suitable resistor (220 Ohms or thereabouts) to 0V. Each LED flashes independently of the other.

Notes
I have used separate functions called from the loop function to illustrate putting code into functions rather than trying to do everything in the loop function.

There are 2 different flash functions, each uses millis() independently for timing. The first, flashEqualOnOffPin2(), flashes the LED on pin 2 with equal on and off times. The second, flashUnequalOnOffPin3(), flashes the LED on pin 3 and allows the on and off times to be different.

Having 2 functions illustrates having non-blocking code called from the loop function. At no point does any part of any function wait for anything, it just checks to see if it is time to change the state of a pin and if it is then it changes the pin then returns, otherwise it just returns having done nothing.

To flash more LEDs copy the relevant flash function, change its name and the pin number, paste it into your code and call it from the loop function.

There is a lot that can be done to improve this code, such as having an array for the pin numbers, having an array for the on and off times for each pin, having one function for flashing everything and passing the required pin number, on and off times to it. Each of these improvements would be worth exploring but each would also complicate the code. My aim was to make the code simple for beginners.

Tested on a Nano Every, should work on most if not all Arduino boards.

If the comments in the code do not adequately explain it then please ask.

Complete sketch

    const int LED_PIN2 = 2;
    const int LED_PIN3 = 3;

    #define LED_ON HIGH
    #define LED_OFF LOW
    
void setup() {
    Serial.begin(115200);
    pinMode(LED_PIN2, OUTPUT);
    pinMode(LED_PIN3, OUTPUT);
    digitalWrite(LED_PIN2, LED_OFF);         // Give the output pins a definite, known state at start up
    digitalWrite(LED_PIN3, LED_OFF);
    Serial.println("Setup complete");
}

void loop() {
    flashEqualOnOffPin2();
    flashUnequalOnOffPin3();
}

void flashEqualOnOffPin2() {
    uint32_t currentMillis = millis();
    static uint32_t lastMillis;
    const uint32_t onOffTime = 500;                 //Duration for LED to be on or off before changing state, in miliseconds
    
    if (currentMillis - lastMillis >= onOffTime) {
        lastMillis += onOffTime;


        
    if (digitalRead(LED_PIN2) == LED_ON) {
            digitalWrite(LED_PIN2, LED_OFF);
        } else {
            digitalWrite(LED_PIN2, LED_ON);
        }
    }
}

void flashUnequalOnOffPin3() {
    uint32_t currentMillis = millis();
    static uint32_t lastMillis;
    const uint32_t onTime = 1750;     //Duration for LED to be on, in miliseconds
    const uint32_t offTime = 350;     //Duration for LED to be off, in miliseconds

        if (digitalRead(LED_PIN3) == LED_ON) {                      // Read the current state of pin 3
            if (currentMillis - lastMillis >= onTime) {             // Pin 3 is high (LED on), check on time and turn LED off if time has expired
                lastMillis += onTime;                               // Add the value of onTime to lastMillis ready to start the next timing interval
                digitalWrite(LED_PIN3, LED_OFF);                    // Turn LED off
            }
     } else {
        if (currentMillis - lastMillis >= offTime) {                // Pin 3 is low (LED off), check off time and turn LED on if time has expired
            lastMillis += offTime;                                  // Add the value of offTime to lastMillis ready to start the next timing interval
            digitalWrite(LED_PIN3, LED_ON);                         // Turn LED on
        }
     }
}

Simulation

Edit: Code modified for clarity, with thanks to @camsysca for suggested improvements.

9 Likes

Here is a basic entry level C++ like class based version

class FlashingLed {
  private:
    byte ledPin;
    unsigned long onTime, offTime, lastMillis;

  public:
    FlashingLed(const byte pin, const unsigned long on, const unsigned long off) : ledPin(pin), onTime(on), offTime(off) {}

    void begin() {
      pinMode(ledPin, OUTPUT);
      digitalWrite(ledPin, HIGH);                   // Turn LED on
      lastMillis = millis();                        // remember when we did so
    }

    void flashWhenNeeded() {
      if (digitalRead(ledPin) == HIGH) {            // if the led is ON a it's the right time, turn it off (HIGH is on)
        if (millis() - lastMillis >= onTime) {      // check on time and turn LED off if time has expired
          lastMillis += onTime;                     // Add the value of onTime to lastMillis ready to start the next timing interval
          digitalWrite(ledPin, LOW);                // Turn LED off
        }
      } else {                                      // LED is off,  if it's the right time, turn it on
        if (millis() - lastMillis >= offTime) {     // check off time and turn LED on if time has expired
          lastMillis += offTime;                    // Add the value of offTime to lastMillis ready to start the next timing interval
          digitalWrite(ledPin, HIGH);               // Turn LED on
        }
      }
    }
};

FlashingLed leds[] = {
  {2, 500, 500},   // pin 2, 500ms ON and 500 ms OFF
  {3, 1750, 350}   // pin 3, 1750ms ON and 350ms OFF
}; 

void setup() {
  Serial.begin(115200);
  Serial.println(F(__FILE__));
  for (auto &l : leds) l.begin();
  Serial.println("Setup complete");
}

void loop() {
  for (auto &l : leds) l.flashWhenNeeded();
}

and a simulation if you want to see the blinking

6 Likes

Here we go

This is a simple class-less C++ sketch for three leds.
My aim was to make the code simple for beginners how starts coding in C++.

/* BLOCK COMMENT
  - Flashing 2 or more LEDs at the same time
  - This sketch may contain traces of C++.
  - In case of indisposition:
  - https://www.learncpp.com/
  - Hardware:
  - Thanks to LarryD
  - https://aws1.discourse-cdn.com/arduino/original/4X/7/e/0/7e0ee1e51f1df32e30893550c85f0dd33244fb0e.jpeg
  - Tested @ Arduino: Mega[X] - UNO [ ] - Nano [ ]
*/
enum Cntrl {Off, On};
enum Port2Led {One, Two, Three};
constexpr byte LedPins[] {9, 10, 11};
struct BLINKLED
{
  const byte Pin;
  const unsigned long Duration[2];  // Off/On time
  unsigned long stamp;
};
BLINKLED blinkLeds[]
{
  {LedPins[One], {250, 500}, 0},
  {LedPins[Two], {500, 500}, 0},
  {LedPins[Three], {500, 250}, 0},
};
void setup()
{
  for (auto blinkLed : blinkLeds) pinMode(blinkLed.Pin, OUTPUT);
}
void loop()
{
  unsigned long currentTime = millis();
  for (auto &blinkLed : blinkLeds) {
    if (currentTime - blinkLed.stamp >= blinkLed.Duration[digitalRead(blinkLed.Pin)])
    {
      blinkLed.stamp = currentTime;
      digitalWrite(blinkLed.Pin, !digitalRead(blinkLed.Pin));
    }
  }
}
3 Likes

An example similar to Perry's, just a bit different:

//LarryD

//********************************************^************************************************
// Version    YY/MM/DD    Description
// 1.00       22/11/22    Running sketch
//
//

//A Sketch to toggle two LEDs using the timing below

//wired so +5V turns LED ON                                                  LED
#define LEDon                              HIGH  //[Output Pin]---[220R]---[A->|-K]---GND
#define LEDoff                             LOW

const byte LED_PIN2                      = 2;
const byte LED_PIN3                      = 3;

//*********************
//Timing stuff
unsigned long LED2Time;
unsigned long LED3Time;

const unsigned long LED2OnOffInterval    = 500;
const unsigned long LED3OnTimeInterval   = 1750;
const unsigned long LED3OffTimeInterval  = 350;

//at powerup, LED3 is OFF
unsigned long LED3Interval               = LED3OnTimeInterval;

//********************************************^************************************************
void setup()
{
  Serial.begin(115200);

  pinMode(LED_PIN2, OUTPUT);
  pinMode(LED_PIN3, OUTPUT);

  //power up conditions
  digitalWrite(LED_PIN2, LEDon);
  digitalWrite(LED_PIN3, LEDon);

  //initialize our 2 TIMERs
  LED2Time = millis();
  LED3Time = millis();

} //END of   setup()


//********************************************^************************************************
void loop()
{
  checkLED2();
  checkLED3();

  //****************************************
  //  Other non blocking code goes here
  //****************************************

} //END of   loop()


//********************************************^************************************************
void checkLED2()
{
  //****************************************               LED2   T I M E R
  //is it time to toggle LED2 ?
  if (millis() - LED2Time >= LED2OnOffInterval)
  {
    //restart the TIMER
    LED2Time = millis();

    //current state of our LED
    byte LED2State = digitalRead(LED_PIN2);

    //toggle the LED
    digitalWrite(LED_PIN2, !LED2State);

  } //END of    this TIMER

} //END of   checkLED2()


//********************************************^************************************************
void checkLED3()
{
  //****************************************               LED3   T I M E R
  //is it time to toggle LED3 ?
  if (millis() - LED3Time >= LED3Interval)
  {
    //restart the TIMER
    LED3Time = millis();

    //current state of our LED
    byte LED3State = digitalRead(LED_PIN3);

    //toggle the LED
    digitalWrite(LED_PIN3, !LED3State);

    //*****************
    //is the LED currently ON ?
    if (!LED3State == LEDon)
    {
      //set this TIMER to the LED ON duration
      LED3Interval = LED3OnTimeInterval;
    }

    //*****************
    //LED is currently OFF
    else
    {
      //set this TIMER to the LED OFF duration
      LED3Interval = LED3OffTimeInterval;
    }

  } //END of    this TIMER

} //END of   checkLED3()


//********************************************^************************************************
2 Likes

An example similar to paulpaulson's sketch:

//TIMERs are in milliseconds or microseconds
//LarryD

//********************************************^************************************************
// Version    YY/MM/DD    Description
// 2.00       22/11/12    Running sketch
// 2.50       22/11/12    Changed some variable names
//

//wired so +5V turns LED ON                                                    LED
#define LEDon                              HIGH    //[Output Pin]---[220R]---[A->|-K]---GND
#define LEDoff                             LOW

const byte redLEDpin                     = 2;
const byte greenLEDpin                   = 3;

const unsigned long redLEDonOffInterval  = 500;

const unsigned long greenLEDonInterval   = 1750;
const unsigned long greenLEDoffInterval  = 350;

//********************************************^************************************************
//a structure that creates TIMER objects
struct makeTimer
{
  //previousTime   = the time this TIMER was (re)started
  //waitInterval   = delay time (ms/us)we are looking for
  //restart        = do we start this TIMER again and again
  //enableFlag     = is this TIMER enabled/allowed to be accessed
  //timeType       = true = millis(), false = micros()
  //
  //**********************
  //For each TIMER object we need to define it:
  //Example:
  //   makeTimer myTimer =           //give the TIMER a name "myTimer"
  //   {
  //     0, 200UL, true, true, true  //previousTime, waitInterval, restart, enableFlag, timeType
  //   };
  // You have access to:
  // Variables: myTimer.previousTime, myTimer.waitInterval, myTimer.restart, myTimer.enableFlag, myTimer.timeType,
  // Functions: myTimer.checkTime(), myTimer.enableTimer(), myTimer.expireTimer()
  //**********************

  unsigned long previousTime;
  unsigned long waitInterval;
  bool          restart;
  bool          enableFlag;
  bool          timeType;

  unsigned long currentTime;

  //******************************************
  //Function to check if this TIMER has expired ex: myTimer.checkTime();
  bool checkTime()
  {
    if (timeType == true)
    {
      currentTime = millis();
    }
    else
    {
      currentTime = micros();
    }

    //has this TIMER expired ?
    if (enableFlag == true && currentTime - previousTime >= waitInterval)
      //Note: if delays of < 2 millis are needed, use micros() and adjust waitInterval as needed
    {
      //should this TIMER start again?
      if (restart)
      {
        //get ready for the next iteration
        previousTime = currentTime;
      }
      //TIMER has expired
      return true;
    }

    //TIMER has not expired or is disabled
    return false;

  } //END of   checkTime()

  //******************************************
  //Function to enable and initialize this TIMER, ex: myTimer.enableTimer();
  void enableTimer()
  {
    enableFlag = true;

    //initialize previousTime to current millis() or micros()
    if (timeType == true)
    {
      previousTime = millis();
    }
    else
    {
      previousTime = micros();
    }

  } //END of   enableTimer()

  //******************************************
  //Function to force this TIMER to expire ex: myTimer.expireTimer();
  void expireTimer()
  {
    //force this TIMER to expire now
    previousTime = currentTime - waitInterval;

  } //END of   expireTimer()

}; //END of   structure “makeTimer”

//********************************************^************************************************

//Let's create and and initialize 2 "makeTimer" objects

//***************************
makeTimer redLEDtimer =                     //create redLEDtimer
{
  0, redLEDonOffInterval, true, true, true  //previousTime, waitInterval, restart, enableFlag, true=millis/false=micros
};

//***************************
makeTimer greenLEDtimer =                   //create greenLEDtimer
{
  0, greenLEDoffInterval, true, true, true  //previousTime, waitInterval, restart, enableFlag, true=millis/false=micros
};


//********************************************^************************************************
void setup()
{
  Serial.begin(115200);

  pinMode(redLEDpin, OUTPUT);
  pinMode(greenLEDpin, OUTPUT);

  //power up conditions
  digitalWrite(redLEDpin, LEDoff);
  digitalWrite(greenLEDpin, LEDoff);

  //initialize our 2 TIMERs
  redLEDtimer.expireTimer();
  greenLEDtimer.expireTimer();

} //END of   setup()


//********************************************^************************************************
void loop()
{
  //***************************
  //is it time to toggle the red LED ?
  if (redLEDtimer.checkTime())
  {
    serviceRedLED();
  }

  //***************************
  //is it time to toggle the green LED ?
  if (greenLEDtimer.checkTime())
  {
    serviceGreenLED();
  }

  //**********************************
  //Put other non-blocking code here
  //**********************************

} //END of    loop()


//********************************************^************************************************
void serviceRedLED()
{
  //current state of our red LED
  byte redLEDstate = digitalRead(redLEDpin);

  //is the LED ON ?
  if (redLEDstate == LEDon)
  {
    digitalWrite(redLEDpin, LEDoff);
  }

  else
  {
    digitalWrite(redLEDpin, LEDon);
  }

} //END of   serviceRedLED()


//********************************************^************************************************
void serviceGreenLED()
{
  //current state of our green LED
  byte greenLEDstate = digitalRead(greenLEDpin);

  //is the LED ON ?
  if (greenLEDstate == LEDon)
  {
    digitalWrite(greenLEDpin, LEDoff);

    //set this TIMER to the LED OFF duration
    greenLEDtimer.waitInterval = greenLEDoffInterval;
  }

  else
  {
    digitalWrite(greenLEDpin, LEDon);

    //set this TIMER to the LED ON duration
    greenLEDtimer.waitInterval = greenLEDonInterval;
  }

} //END of   serviceGreenLED()


//********************************************^************************************************
2 Likes

This is the schematic that accompanies the 5 sketches above.

It is always a good idea to start with a schematic so you have a good idea what to expect when you write your sketch.

Notice we have each LED wired so a HIGH on the output pin will turn ON the LED.


Here is an oscilloscope screen capture of the output pin signals.

This shows clearly the results of running the sketch.

3 Likes

I think the ideas in this thread are worthy of a new example in the IDE, per

Here's my effort:

/*
  BlinkWithoutDelayMore -- Blink without Delay for more LEDs, using Functions

  Turns on and off a light emitting diode (LED) connected to a digital pin,
  without using the delay() function. This means that other code can run at the
  same time without being interrupted by the LED code.

  State diagram: tinyurl.com/BWODMdiag 

  The circuit:
  - Use the onboard LED.
  - Note: Most Arduinos have an on-board LED you can control. On the UNO, MEGA
    and ZERO it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN
    is set to the correct LED pin independent of which board is used.
    If you want to know what pin the on-board LED is connected to on your
    Arduino model, check the Technical Specs of your board at:
    https://www.arduino.cc/en/Main/Products

  created 2005
  by David A. Mellis
  modified 8 Feb 2010
  by Paul Stoffregen
  modified 11 Nov 2013
  by Scott Fitzgerald
  modified 9 Jan 2017
  by Arturo Guadalupi
  modified by 7 Mar 2023
  by David Forrest

  This example code is in the public domain.

  For a tutorial on how this works, please read:
  https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay

  Simulation: https://wokwi.com/projects/358554786853847041

  This was written for the discussion at
  https://forum.arduino.cc/t/the-fact-that-something-has-been-used-for-a-long-time-is-in-no-way-an-argument-for-making-it-the-same-for-another-100-years/1097951/24
  on improving the BlinkWithoutDelay example.

  For other ways of doing multiple things at once please read:
  https://forum.arduino.cc/t/flashing-multiple-leds-at-the-same-time/1065564/
  https://learn.adafruit.com/multi-tasking-the-arduino-part-1/now-for-two-at-once


*/

// constants won't change. Used here to set a pin number:
const int ledPin = LED_BUILTIN;  // the number of the LED pin
const int ledPin2 = 3; // the number of the second 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);
}

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

  checkIfItIsTimeToBlinkAndDoIt();  
  // ^^ this function separates the BlinkWithoutDelay core
  // into a function

  checkIfItIsTimeToBlinkAndDoIt_variable(500);  // function handles a separate LED
}

/////// 

void checkIfItIsTimeToBlinkAndDoIt() {
  // 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 blink the LED.  

  // save the current time
  unsigned long currentMillis = millis();

  // test if it is time yet
  if (currentMillis - previousMillis >= interval) {
    // save the last time you changed the LED
    previousMillis = currentMillis;

    // Toggle the ledState variable:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }
    // set the LED output to match the ledState variable:
    digitalWrite(ledPin, ledState);
  } // end interval check

}

void checkIfItIsTimeToBlinkAndDoIt_variable(unsigned long interval) {
  // 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 blink the LED.
  // If it is not yet time it immediately returns control back to loop()

  // These "local scope" variables mask variables of the same name in other scopes
  // this helps keep the global namespace cleaner and easier to maintain
  const int ledPin = ledPin2 ; // copy from global constant
  // use static for private state variables that will persist across calls
  static bool initialized = false;
  static int ledState = LOW;
  static unsigned long previousMillis ;

  // save the current time
  unsigned long currentMillis = millis();

  if (initialized == false) {  // 
    ledState = LOW;
    pinMode(ledPin, OUTPUT);
    digitalWrite(ledPin, ledState);
    initialized = true;
    previousMillis = millis();
  }

  // test if it is time yet
  if (currentMillis - previousMillis >= interval) {
    // save the last time you changed the LED
    previousMillis = currentMillis;

    // Toggle the ledState variable:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }
    // set the LED output to match the ledState variable:
    digitalWrite(ledPin, ledState);
  } // end interval check

}

(I reserve the right to edit this code to follow the sim in that Wokwi link. )

In contrast with the other example, my schematic follows BWOD by using LED_BUILTIN on 13, while the other is on pin 3.

What I like about my code is that it builds directly from the venerable BWOD, giving a clear path forward from doing one thing without delay to DoingSeveralThingsAtTheSameTime without delay. I think it would be good to highlight this thread, and the other good tutorials on how to solve the same problem in the code's comments, because comparing different ways to do the same thing helps with learning.

Also pointing at more advanced multitasking tutorials would be helpful:

2 Likes