Blinking LEDs in an off/on/off sequence with no Delay

Hi. I have a couple of LEDs that I'd like to turn off and on in a sequence, but I'm having trouble finding out how to have multiple on/off states for each. At the moment every tutorial I've found has had a single on/off state. What I what is a set of LEDs to turn on for a second, then off for a second. At the same time a second set of LEDS should be off at the start for 1.33 seconds, flash for .33 of a second, and the go off the remaining .33 seconds of the 2 second sequence. Visually the loop would look like this:

LED1: |||||||||.........
LED2: ............|||...

Right now I'm working off the "millis() for timing" tutorial which, as I said, old allows a single on and off time to be set (I need a no delay solution because I'll be having other LEDs fading and blinking independently). Here's the code I'm using.

// These variables store the flash pattern
// and the current state of the LED
 
int ledPin1 =  4;      // the number of the LED pin
int ledState1 = LOW;             // ledState used to set the LED
unsigned long previousMillis1 = 0;        // will store last time LED was updated
long OnTime1 = 250;           // milliseconds of on-time
long OffTime1 = 750;          // milliseconds of off-time
 
int ledPin2 =  7;      // the number of the LED pin
int ledState2 = LOW;             // ledState used to set the LED
unsigned long previousMillis2 = 0;        // will store last time LED was updated
long OnTime2 = 330;           // milliseconds of on-time
long OffTime2 = 400;          // milliseconds of off-time
 
void setup() 
{
  // set the digital pin as output:
  pinMode(ledPin1, OUTPUT);      
  pinMode(ledPin2, OUTPUT);      
}
 
void loop()
{
  // check to see if it's time to change the state of the LED
  unsigned long currentMillis = millis();
 
  if((ledState1 == HIGH) && (currentMillis - previousMillis1 >= OnTime1))
  {
    ledState1 = LOW;  // Turn it off
    previousMillis1 = currentMillis;  // Remember the time
    digitalWrite(ledPin1, ledState1);  // Update the actual LED
  }
  else if ((ledState1 == LOW) && (currentMillis - previousMillis1 >= OffTime1))
  {
    ledState1 = HIGH;  // turn it on
    previousMillis1 = currentMillis;   // Remember the time
    digitalWrite(ledPin1, ledState1);	  // Update the actual LED
  }
  
  if((ledState2 == HIGH) && (currentMillis - previousMillis2 >= OnTime2))
  {
    ledState2 = LOW;  // Turn it off
    previousMillis2 = currentMillis;  // Remember the time
    digitalWrite(ledPin2, ledState2);  // Update the actual LED
  }
  else if ((ledState2 == LOW) && (currentMillis - previousMillis2 >= OffTime2))
  {
    ledState2 = HIGH;  // turn it on
    previousMillis2 = currentMillis;   // Remember the time
    digitalWrite(ledPin2, ledState2);	  // Update the actual LED
  }
}

Take a look at this example that I wrote to answer a query a while ago

const byte ledPins[] = {3,5,6,9};
const byte NUMBER_OF_LEDS = sizeof(ledPins);
unsigned long startTimes[NUMBER_OF_LEDS] = {};
byte indexes[NUMBER_OF_LEDS] = {0};
const byte MAX_NUMBER_OF_PERIODS = 10;
unsigned long periods[][MAX_NUMBER_OF_PERIODS] =    //periods for each LED.  zero indicates end of sequence
{
  {1000, 2000, 1500, 2500, 0},
  {500, 200, 1000, 2000, 3000, 4000, 0},
  {400, 1000, 1500, 2000, 0},
  {1100, 2200, 0}
};

void setup()
{
  Serial.begin(115200);
  for (int led = 0; led < NUMBER_OF_LEDS; led++)
  {
    pinMode(ledPins[led], OUTPUT);
  }
}

void loop()
{
  unsigned long currentTime = millis();
  for (int led = 0; led < NUMBER_OF_LEDS; led++)  //iterate through the LEDs
  {
    if (currentTime - startTimes[led] >= periods[led][indexes[led]])  //? time to change ?
    {
      digitalWrite(ledPins[led], !digitalRead(ledPins[led]));  //flip led state
      startTimes[led] = currentTime;  //save start time of state
      indexes[led]++;                    //increment index
    }
    if (periods[led][indexes[led]] == 0)  //if next period is zero (end of sequence)
    {
      indexes[led] = 0;        //reset period index for this LED
    }
  }  //end for loop
}

See Multiple Blink Without Delay Example

As usual there are any number of ways to solve this problem. You have a couple already.

If you require that both LEDs are synchronised then here is another which involves minimal code, but still builds on the standard BWD example.

You need to recognise that the patterns for both LEDs share a common divisor of 0.333 seconds.

  1. Start with the standard blink without delay example for a single LED
  2. Set the blink period to 0.33 seconds (333ms)
  3. Remove the code that toggles the single LED alternately on each cycle
  4. Replace it with a counter that increments: 0, 1, 2, 3, 4, 5...
  5. When the counter reaches 6, reset it back to 0
  6. If the counter is <= 2 turn the first LED ON, otherwise OFF
  7. If the counter is 4 turn the second LED ON, otherwise OFF

Basically the code should look something like this (untested)...

unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= 333) {
  digitalWrite(ledPin1, counter <= 2 ? HIGH : LOW);
  digitalWrite(ledPin2, counter == 4 ? HIGH : LOW);
  counter++;
  if (counter == 6) counter = 0;
  previousMillis = currentMillis;
}

Get led 1 blinking on and off at 1 second intervals. Then you have a millis second timer. Then use a variable to count times the state of the led has changed (look up state machines). If variable is > 9 then state equals 0 for led 1, if 18 reset. You might use a for loop. Same for led 2.

I.e focus on the state variable. Blink the state on and off. If state is 1 and variable is a number you want the led to be on then state stays 1. Write state to led pin.

PS beaten to it and with a more elegant solution. I didn’t link the common denominator so thought of 2 timers when 1 would do

+1 to the OP for using code tags
+1 to pmagowan for mentioning state machines (of which this is an example)

@Mark2000 you should definitely read up on state machines as this is the next concept you need to grasp in Arduino programming after correctly realising that you shouldn’t use delay().

UKHeliBob:
Take a look at this example that I wrote to answer a query a while ago

Hi, so I tried this one out and it works to an extent. It doesn't seem to allow for an odd number of instructions for an LED. For instance an LED that is off at the beginning of a sequence and off at the end. It adds an extra blink at the end of the sequence. For example to get my sequence working I needed this:

{1000, 1000, 0},
{475, 50, 1475, 0},

What I tried to mitigate the flashing was add a "1" at the end hoping it would be too fast to be seen:

{1000, 1000, 0},
{475, 50, 1474, 1, 0},

But it's totally visible. Any way to stop this from happening?

This is how I'd do it.

#include "blinker.h"
#include "idlers.h"


blinker  firstLED(13,1000,2000);
blinker  secondLED(16,330,2000);
timeObj  midTimer(1330);

void setup() {
   
   firstLED.setOnOff(true);   // Fire up your first LED
   while(!midTimer.ding()) idle();   // Wait for it to finish it's blink cycle..
   secondLED.setOnOff(true);  // Fire up your second LED.
}

void loop() {
   idle();   // This runs your LEDs in the background. And anything else that's in the background as well.
   // Do whatever else you want. Just NOT delay();
}

You'll need to instal LC_baseTools from the Arduino library manager.

-jim lee

so everybody seems to present ready to use solutions that are pretty complex or hide away functionality inside additional libraries.

somehow I'm asking myself with this slightly changing requirements is this T?

in your second example the leds are blinking

{1000, 1000, 0},
{475, 50, 1475, 0},

LED1: one scond on one second off - repeat

LED2 475 ms on 50 ms off 1475 on - repeat

if you look at his example LED2 is on at the beginning and at the end.
so as the result this means
LED2 is on for 1950 ms

both blinksequences add up to 2000 ms.

if you define a minimum-period of time that can be considered as the shortest ON/OFF-time
let's say base-frame 5 ms.

you can setup your own counter that counts up from zero to 2000 / 5 = 400.
in general: complete period / baseframe

Increment a variable FrameCounter by 1 every 5 ms.

Then you draw your blinkpattern on a sheet of paper and calculate how much time has passed by since
the start of the period
in your above example
ON 1000, OFF 1000 - repeat

1000 / 5 = 200
(1000 + 1000) / 5 = 2000 / 5 = 400 (let's take 399) based on the timepoint "begin of loop")

so you have two if-conditions
at the start of the loop LED1 = ON
if (FrameCounter == 200) => LED1 OFF
if (FrameCounter == 399) => LED1 ON

your second LED2

at start of the loop LED2 ON
475 / 5 = 95

if (FrameCounter == 95) ==> LED2 OFF

(475 + 50) / 5 = 105
if (FrameCounter == 105) ==> LED2 ON

(475 + 50 + 1475) / 5 = 2000 / 5 = 400 (lets take 399)

if (Framecounter = 399) ==>> LED2 ON

Now if you look back at the beginning of the loop
LED2 was turned ON with FrameCounter == 0

With an uneven number of ON/OFF-times per period if you code "ONs" and "OFFs" you get this effect.
If you want to keep the uneven statechanges this would mean instead of coding "ONs" and "OFFs"
you have to code inverting the LED-state.

475 / 5 = 95

if (FrameCounter == 95) ==> invert LED2-state

(475 + 50) / 5 = 105
if (FrameCounter == 105) ==> invert LED2-state

(475 + 50 + 1475) / 5 = 2000 / 5 = 400 (lets take 399)

if (Framecounter = 399) ==> invert LED2-state

I'm not sure if you are a T.

Until this point it was an interesting analysis for me. That's why I wrote a posting.
In my opinion you got enough suggestions. How this can be solved. Now it is your turn to write down the code. All the rest of the questions you can answer by yourself through adding serial-output for debugging purposes.

I'm an artist not a programmer, so I think the details of my request are being misunderstood, because trying a number of these solutions isn't getting the results I want. This is all my fault for somehow not being clear enough in my description, and I thank everyone who's contributed. So, I made an animated GIF to show how the blinkers will be used and how exactly they will look when they go off both in practice and on a timeline. I hope this clears anything up.

Do the LEDs blink fast during their active periods ?
In your animation LED1 seems to do a single flash

uint32_t previousMillis, delayMillis;
const int onMillis = 50;
const int offMillis = 250;
bool ledState;
int counter;

void loop() {
uint32_t currentMillis = millis();
if (currentMillis - previousMillis >= delayMillis) {
  ledState = !ledState;
  digitalWrite(ledPin1, ledState && counter <= 60 ? HIGH : LOW);
  digitalWrite(ledPin2, ledState && counter >= 80 && counter < 99 ? HIGH : LOW);
  delayMillis = ledState ? onMillis : offMillis;
  counter++;
  if (counter == 120) counter = 0;
  previousMillis = currentMillis;
}

Edit: if you can give the required timings for your blink sequence, I can adjust the above.

Too bad you waited so long to reveal the application... it's been covered here before:
https://forum.arduino.cc/index.php?topic=687993.0
...with solutions

UKHeliBob:
Do the LEDs blink fast during their active periods ?
In your animation LED1 seems to do a single flash

They are both solid with red on the timeline being constant off, and green being constant on. There is no flashing.

[quote author=pcbbc link=msg=4710801 date=1597776881Edit: if you can give the required timings for your blink sequence, I can adjust the above.[/quote]

Is there a way to explain how the led pins are set and how the timing is done? Also, every other iteration of the loop the strobe (faster blink) LED turns on twice.

They are both solid with red on the timeline being constant off, and green being constant on. There is no flashing.

Then 2 normal BlinkWithoutDelay sequences with a change of period between on and off will do the job

Mark2000:
They are both solid with red on the timeline being constant off, and green being constant on. There is no flashing.

Hang on, the animation was fast flashing (certainly for led2) and now it’s not?

Well, if you don’t want fast flashing them I’m not sure what was wrong with my solution as posted back in post #3?

the animation was fast flashing (certainly for led2) and now it's not?

Hence my question. It looks like the OP changed the animation in the meantime

And #7 would do that as well, just plug in your pin numbers.

Sigh..

-jim lee

Hi Mark,

what a great honor to support a Star Trek Art Project!

I printed out your picture for easier counting the number of timing lines.
The only thing I need to now is the timebase. With timebase I mean the time that is symbolised by the distance of two lines.
I counted the lines. If I begin at the white dot at the left and count up to last green line of LED 2 I count 75.

I used my stopwatch and measured two seconds for the whole sequence of the animated GIF.

2000 milliseconds / 75 = 26,67 milliseconds.
How about using 27 * 75 = 2025 milliseconds?

Now in exact analogy to your animated GIF there are passing by 27 milliseconds if the white dot runs from line to line.
After each 27 milliseconds a counter is increased by 1

(and make the currentMillis the prevoiusMillis to start a new timeframe)

  if (currentMillis - previousMillis >= 27) {
    FrameCounter++;
    previousMillis = currentMillis;
  }

Your LED1 shall go on at line 14 and go off at line 19

  if (FrameCounter == 14) {
    digitalWrite(LED1_Pin, HIGH);
  }

  if (FrameCounter == 19) {
    digitalWrite(LED1_Pin, LOW);
  }

Your LED2 shall go on at line 45 and go off at line 75

  if (FrameCounter == 45) {
    digitalWrite(LED2_Pin, HIGH);
  }

  if (FrameCounter == 75) {
    digitalWrite(LED2_Pin, LOW);
  }

here is the whole sketch

const int LED1_Pin = 2; // IO-Pin-number LED1 is connected to
const int LED2_Pin = 3; // IO-Pin-number LED2 is connected to

unsigned long currentMillis;
unsigned long previousMillis;

int FramesOfPeriod = 75;

int LED1_ON_Cnt  = 14;
int LED1_OFF_Cnt = 19;

int LED2_ON_Cnt  = 45;
int LED2_OFF_Cnt = 75;

int FrameCounter = 0;

void setup() {
  pinMode(LED1_Pin, OUTPUT);
  pinMode(LED2_Pin, OUTPUT);

  digitalWrite(LED1_Pin, LOW);
  digitalWrite(LED2_Pin, LOW);

  previousMillis = millis();
}

void loop() {
  currentMillis = millis();

  if (currentMillis - previousMillis >= 27) {
    FrameCounter++;
    previousMillis = currentMillis;
  }

  if (FrameCounter == 14) {
    digitalWrite(LED1_Pin, HIGH);
  }

  if (FrameCounter == 19) {
    digitalWrite(LED1_Pin, LOW);
  }


  if (FrameCounter == 45) {
    digitalWrite(LED2_Pin, HIGH);
  }

  if (FrameCounter == 75) {
    digitalWrite(LED2_Pin, LOW);
  }

  if (FrameCounter == FramesOfPeriod) {
    FrameCounter = 0;
  }
}

if you want to tweak the timing you can do so by changing one of the values mentioned above.
Please try this code and report if it works like you expect it.

best regards Stefan

jimLee:
And #7 would do that as well, just plug in your pin numbers.

Sigh..

-jim lee

The problem with many of these solutions, including this one, is that it didn't allow for an odd numbered list of sequences. In a situation with off/on/off when the loop restarted the LED would give off an extra flash. I can take a video of it if you like.

StefanL38:
Hi Mark,

what a great honor to support a Star Trek Art Project!

Thank you very much! It's a running light sequence for a physical model. I'll be using it in two different models, and probably many more after. This code worked out perfectly and was really easy to understand. Thank you, too, to everyone else that offered advice on this!