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
Flashing 2 or more LEDs at the same time
How to run BUZZER and LED simultaneously
Multitasking arduino code for servo operation
Can I include a library in my own library?
Programming Issue-Buzzer
How To Run Multiple Loop With One or Multiple Buttons?
Controllind solenoid using arduino two modes of operation
Help getting a servo to activate on time for a specific amount of time
Best first 5 topics to read to reach Trust Level 1?
How do sent byte manually in UART communication?
Is there a non blocking delay that's as easy to use as the built in delay function?
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
Boat Project code help
Arduino Multitasaking
The output is coming slowly to serial monitor
Esp8266 play sound engine simulation
The milis collide if the values are the same
Help changing this code to be non-blocking
Controlling relays based on inputs
100 values from a device ... how to best work with them (class, struct)?
Variable types used in state machine
Running 2 functions on ESP8266 (Buzzer and display)
Running 2 functions on ESP8266 (Buzzer and display)
How do I make the backlight turn off without stoping code [SOLVED]
Help with "until" loop
What microcontroller has the most timers
HELP! Additional 8 Relay without delay
Blink without delay, two intervals 240615
Two LEDs fading at half cycle rate
Moquez vous de moi, parce que je me moque un peu des autres (dont les platistes)!
Arduino ATMEGA 2560 serial communication with another ATMEGA2560
Timed servo door for chiken coop
I don't really understand how the millis work
How to Code Millis
Problem with long delay
General Expert Advise needed
Button taking too long to register
Detect state change when running other tasks
Wokwi Simulations for Arduino Built-In Examples
Gave up on millis and using delay instead for two separate things
Initialize variables using the 'for' command
Auto retriggering timer for between 1-15 seconds (1, 10 & 15) and between 200-1000 Cycles ( 200, 800 &1000)
How to use multiple timers?
Timer and LED sequence
Problems with Hex codes randomizing
Can this be done with a state machine
deltaTime and loop() in arduino
Help to continuous run the ultrasonic even the car was turning right or left. Becauses when the car is turning right the ultrasonic is stop scanning h
Multiple state machines
Blink w/o Delay Two Lights 230619
How can i fade LEDs concurrently and at different rates on a PCA9685 chain ?
How to run Two codes at same time
Need help with loop (pushbutton dimming)
Running dual functions at the same call time
Can Serial Monitor Be Used to Monitor Uno Countdown?
Problem to integrate another sound in a project with buzzer
[SOLVED] Relay won't switch in my project
Button to stop passive buzzer (or reset the arduino)
Défi de @J-M-L - discussion
Leds to be turned off immediately when I turn off the switch
MIDI Piano LED strip
Is there a non blocking delay that's as easy to use as the built in delay function?
Getting out of a cycle in a for loop
Having a problem with adding a matrix
Itsy bitsy advancing through led loops help? $?
Mega to Giga Communications
[SOLVED] How to correctly use millis() for delay
Comment utiliser les pins digitals et les pins analogiques afin de controler les LEDS
Is there a non blocking delay that's as easy to use as the built in delay function?
Clubbing 2 or more programs
The blinking LEDs wont run in Sequence 3 codes
Blink without delay Leuchtturm Roter Sand
Multiple tasks from one if-statment
String formatting
Interrupt Arduino Uno rev2 wifi
Delay() vs Millis()
Combine multiple state machines
Help with do/while loops
Aide pour avoir une horloge bien lineaire
How to decrement a variable in every line of a loop..?
Servo code non blocking
How do i multi task with an arduino uno
Programming limit switch?
How to make a loop run for a specific time then switch to another loop and then back and so on
Simultaneous loops
Why does my relay activate with ground?
Re: Discussion re: approaches to providing assistance to programming questions
My school is stupid
Exécution d'instructions en parallèle
Random output to relays
Find peak value of piezo sensor hit, multiple sensors
How to blink a light without blocking other functionality
Structure While
Stepper Motor Position Using Button Presets
A simple Led Blinking class based C++ Program for beginners
Using interrupts on the Arduino Nano
Yet another Finite State Machine introduction
Getting back to Arduino
Car gauges Nextion vs
Probleme de lecture et changement d'animation led neopixel et ezbutton
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
Led button on and off
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
Timer within a timer
Is it possible to turn on pins 4 to 13 all at once?
Clignotement millis intervalles différents
[ITALIANO] Lo spamm bar (Part 3)
Arduino based rc car with object follow
How to run two events independently
How do i get the both motors to work at the same time with a timer?
Stepper with 2 blinking leds
Having an issue making a separate variable increase along with millis()
How to stop a for function if an if statement becomes false
How fast can you loop() if you replace delay() with millis()?
IO program control
Setting variable and overriding for a timer in arduino
Turning relay on and off every 5 seconds
Web Server are laggy, so slow or even can't be opened
Arduino Pico Project - Drop test of CANSAT model satellite
HELP! Changing the melody using buttons while it's playing
Advice on how to control my robot
Need help at coding

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