Flashing 2 or more 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.

The 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.

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

Complete sketch

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

void setup() {
    Serial.begin(115200);
    pinMode(LED_PIN2, OUTPUT);
    pinMode(LED_PIN3, OUTPUT);
    char fileName[] = {__FILE__};
    Serial.println(fileName);
    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;
            digitalWrite(LED_PIN2, !(digitalRead(LED_PIN2)));     // Chnage the state of pin 2 by reading its current state then writing the opposite state back to the pin
    }
}

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)) {                              // 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, LOW);                       // 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, HIGH);                      // Turn LED on
        }
     }
}
4 Likes

Good intro to the topic.

if I may add (happy to remove if you feel it's confusing)

You code is very much structured as a C program. As we use C++ and for those interested in a class based approach (where I basically took @PerryBebbington code and created a class with it), iit could go like this

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);
      lastMillis = millis() - onTime;
    }

    void flashWhenNeeded() {
      if (digitalRead(ledPin) == HIGH) {           // Read the current state of the led. HIGH is on
        if (millis() - 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(ledPin, LOW);               // Turn LED off
        }
      } else {                                     // LED is off
        if (millis() - 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(ledPin, HIGH);              // Turn LED on
        }
      }
    }
};

FlashingLed leds[] = {{2, 500, 500}, {3, 1750, 250}};

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();
}

you can see the blinking pattern in the simulator

4 Likes

Thank you @J-M-L I like that because it takes what I did and improves it, someone can see the progression from simple but slightly clumsy C to how to construct a class that does the same thing. Nice job!

Good examples.

For new users, these examples might be frightening, however, they always have the option of asking for clarification.

I tend to offer examples that have TIMER code in the loop( ) function.

A new person can look at loop( ) and scan down to see all TIMERs in one glance; TIMERs may have a flag that enables/disables as needed.

A TIMER created using a Class or Structure is beyond many new users abilities and has a potential of turning off the user. :wink:


Just my first impressions. :thinking:

1 Like

yeah, no. your code doesn't work as expected, for example sketch.ino copy - Wokwi ESP32, STM32, Arduino Simulator

Compiled and running on the same Nano Every as my original code, working as expected.

So the simulator is wrong?

No idea! I just copied @J-M-L 's code, complied it and programmed a Nano Every, it worked as expected, no problems at all.

Hello
Here comes 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));
    }
  }
}

Have a nice day and enjoy coding in C++.

2 Likes

Like your sketch too but to new users, this will surely be frightening to them.


new users != beginners

They shall try it, check it and play araound
Trial and error is the best way to learn.

1 Like

:astonished:

There are now 3 different ways to achieve multiple flashing LEDs in this topic, I would hope learners will understand at least one of them!

2 Likes

@PerryBebbington Why do you use "uint32_t" instead of "unsigned long" for a millis() value ?

The References says it is "unsigned long": millis() - Arduino Reference
It is defined as "unsigned long" here and here and here and here and here.

I always use variables of that style rather than int etc because then you know explicitly how many bits are being used. I don't like having the number of bits being implied by the variable type, never understood why it was done like that.

You are now going to tell me that because I have not used the same types as the definition used for millis I risk the code being broken if some implementation of C++ has, for example, an 8 byte unsigned long. I know.

Possibly. Do you mean I should have written


void begin() {
      pinMode(ledPin, OUTPUT);
      lastMillis = millis() - offTime;
    }

It actually would have made more sense

I prefer using uint32_t ... much less typing (only need to type "u" then select (on WokWi).
See Microchip

not really, yes it needs to be revised but there are more problems with on off logic that need to be addressed too

I thought I had copied @PerryBebbington’s code
I’ll have a look when I’m on a larger screen than my phone

The problem is with having it start correctly. Ideally there should be start offset param