Impossible? Adding blinking to a WS2811 which is already using delays

Hi guys,

First off, thanks for already being so helpful! Just by reading posts on here I've learned how to use the led strip with my arduino UNO and I've coded a basic (but not for me!) function. So thanks.

Basically what I'm trying to do is create a little timer using the LED. So it starts with 10 leds on the strip lit, then every minute the next (first) in line shuts off. Here is what I have:

#include <FastLED.h>
#define NUM_LEDS 50 
#define DATA_PIN 6
#define COLOR_ORDER RGB
CRGB leds[NUM_LEDS];


void setup() {  

  delay(2000);

   FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
    

     // Turn the LED on
  leds[0] = CRGB(255,50,0);
  FastLED.show();
  leds[3] = CRGB(255,50,0);
  FastLED.show();
  leds[5] = CRGB(255,50,0);
  FastLED.show();
  leds[9] = CRGB(255,50,0);
  FastLED.show();
  leds[11] = CRGB(255,50,0);
  FastLED.show();
  leds[14] = CRGB(255,50,0);
  FastLED.show();
  leds[18] = CRGB(255,50,0);
  FastLED.show();
  leds[21] = CRGB(255,50,0);
  FastLED.show();
  leds[24] = CRGB(255,50,0);
  FastLED.show();
  leds[27] = CRGB(255,50,0);
  FastLED.show();
  

}


void loop() { 

delay(60000);
leds[0] = CRGB(0,0,0);
  FastLED.show();
  delay(60000);
leds[3] = CRGB(0,0,0);
  FastLED.show();delay(60000);
leds[5] = CRGB(0,0,0);
  FastLED.show();delay(60000);
leds[9] = CRGB(0,0,0);
  FastLED.show();delay(60000);
leds[11] = CRGB(0,0,0);
  FastLED.show();delay(60000);
leds[14] = CRGB(0,0,0);
  FastLED.show();delay(60000);
leds[18] = CRGB(0,0,0);
  FastLED.show();delay(60000);
leds[21] = CRGB(0,0,0);
  FastLED.show();delay(60000);
leds[24] = CRGB(0,0,0);
  FastLED.show();delay(60000);
leds[27] = CRGB(0,0,0);
   
}

This is honestly good enough. But it's supposed to be like a little fire where a bit of the flame goes out every minute. So I thought it would look amazing if it flickered. I know you can make a "flickering flame" effect by blinking the led quickly and did so. But when I blink them using delay, that of course messes with the delay countdown I was using. I obviously can't have both.

So then I read up on arrays and using millis() to avoid such conflict. The issue is that I have been able to find nothing about millis() within millis() -- if that makes any sense. Like, I was thinking (and I'm sorry if this is dumb) that the best thing to do would be to have the lights I want on in an array, then "flicker" them using millis() to turn them on and off every 50-100ms, then have millis() turn off the next light in the array every minute. Does that make sense? Or is it even possible? If I'm using the millis() to flicker the ten lights, can millis() also be turning them off in a pattern, or is that going to end in disaster?

Anyway, I hope that made sense. I just wanted to get an idea of whether or not what I'm trying to do (flicker effect + count down) is even possible or if I'm wasting my time. And if it is possible, whether little dumb me is on the right track, or if there is a way better way to do this.

Thanks!

The issue is that I have been able to find nothing about millis() within millis(

That is because if you do something based on the time given by millis, and then in that function you do things based on the time you in effect have the same sort of blocking code that a delay would give you.
The point of using millis() is that you constantly check, at the top level, if it is time to do any of your tasks. So every time round the loop you check for all things weather they happen ever couple of milliseconds or every couple of minutes or hours.

What you actually want to do is perfectly possible, if you code it right.

The point of using millis() is that you constantly check, at the top level

Okay, I think I understand what you mean. So I'm just creating one "clock" and it keeps track of everything? So it will check the time and blink the lights every 50-100 ms and while checking the time if it ever gets to 1minute, will shut off the first light of the ten, then the same at two minutes and on and on? I think I understand the concept, but the code must be very confusing for something like that.

So I'm just creating one "clock" and it keeps track of everything?

Not quite, you are using the millis() function as your clock and you have a whole bunch of variables that tell you when the next thing is going to happen. You use these variables to check if it is time to do that task or not.

The tasks are simply functions that do one step of a process and then return quickly.

Here is a tutorial I wrote to explain things http://www.thebox.myzen.co.uk/Tutorial/State_Machine.html

Wow, that's an amazing resource, thanks for putting that together! I've gone over it and I think I'm ready to try my hand at what I need for the timing. The only thing that confuses me is that everyone seems to show these examples with leds on their own output, as opposed to an led strip. How would I substitute that?

Instead of controlling a led, control the strip when it is time. Below the idea

if it is time
  update last time that you updated the strip
  set the strip's leds
  show the leds
endif

Yeah, that's what I was going for. I'm working on an example with just a few lights blinking. I figure once I have that I can just increase the speed to to get a flicker then worry about the countdown. I've got some sample lights broken into arrays, and I've got the the arrays working in the loop, but for some reason they aren't moving from blue to red every three seconds, but just staying one color and then flashing the other color for like a nanosecond. I'm probably missing something obvious, but here's where it is:

#include <Chrono.h> 
#include <FastLED.h>
#define NUM_LEDS 50 
#define DATA_PIN 6
#define BRIGHTNESS 10
#define COLOR_ORDER RGB
CRGB leds[NUM_LEDS];

int flicker1[] ={2, 8, 17, 20, 26};

int flicker2[] ={5, 11, 14, 23, 29};

long int ledcolor1 = CRGB::Red;

long int ledcolor2 = CRGB::Blue;

// Instanciate a Chrono object.
Chrono ledChrono; 

void setup() {  

  delay(2000);

   FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);

}




void loop() { 

  for (int i = 0; i < 5; i++) 
  {

    if (ledChrono.hasPassed(3000)){ledChrono.restart();
      // restart the crono so that it triggers again later 
    
    
      leds[flicker2[i]] = ledcolor2;}

      else{
        leds[flicker2[i]] = ledcolor1;
       
      }
      }
      
       FastLED.show();
}

sterretje:
Instead of controlling a led, control the strip when it is time. Below the idea

if it is time

update last time that you updated the strip
  set the strip's leds
  show the leds
endif

Okay, thanks for the advice. I realize now that my last code was blinking for a nanosecond because I wasn't toggling between two colors, just showing one and replacing it for a split second. I've fiddled for a few hours and I think I've got the flicker going. Though it does one light at a time, is that normal? I thought it would "blink" each array rather than going light by light in each array. Perhaps I've made a mistake in the code, though I do like the random feeling of the effect. Here's what I've got:

#include <FastLED.h>
#define NUM_LEDS 50 
#define DATA_PIN 6
#define BRIGHTNESS 10
#define COLOR_ORDER RGB
CRGB leds[NUM_LEDS];

unsigned long previousMillis = 0;
int color;

int flicker1[] ={2, 8, 17, 20, 26, 32, 35, 41};

int flicker2[] ={5, 11, 14, 23, 29, 38, 44, 47};

void setup() {  

  delay(2000);

   FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);

}

void loop()
{
  for (int i = 0; i < 8; i++) {
   static unsigned long lastMillis = 0;
   static bool toggle = false;
   if(millis() - lastMillis > 13UL)
   {
     if(toggle)
     {
       //show colorA
       leds[flicker1[i]] = CRGB(255,50,0);
       leds[flicker2[i]] = CRGB(30,10,0);
     }
     else
     {
       leds[flicker1[i]] = CRGB(30,10,0);
       leds[flicker2[i]] = CRGB(255,50,0);
     }
     toggle = !toggle;
     lastMillis = millis();
     FastLED.show();
     
   }
}

}

Does it look okay? And any idea of how to keep this flicker while adding the last step of completely shutting off a light every 1 minute for the countdown?

Does it look okay?

Sorry, but no.
Don't declare variables in a loop you create a new variable each time round and you can't access previous one of the same name. The variable lastMillis will always be zero.

Try reading the code.

Grumpy_Mike:
Sorry, but no.
Don't declare variables in a loop you create a new variable each time round and you can't access previous one of the same name. The variable lastMillis will always be zero.

Try reading the code.

Yeah, another member pointed that out to me and was very helpful. I hadn't even thought of how silly it was. The code has reached this point:

#include <FastLED.h>
#define NUM_LEDS 50
#define DATA_PIN 6
#define BRIGHTNESS 10
#define COLOR_ORDER RGB
CRGB leds[NUM_LEDS];

int color;

int flicker1[] = {2, 8, 17, 20, 26};



unsigned long startTime = millis();
unsigned long interval = 6000;
unsigned long lastMillis = millis();
uint8_t start_num = 0;
bool toggle = false;

void setup() {

  delay(2000);

  FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);

}

void loop()
{
  for (int i = start_num; i < 5; i++) {

    if (millis() - lastMillis > 25UL)
    {
      if (toggle)
      {
        //show colorA
        leds[flicker1[i]] = CRGB(255, 50, 0);
        
      }
      else
      {
        leds[flicker1[i]] = CRGB(30, 10, 0);
        
      }
      toggle = !toggle;
      lastMillis = millis();
      FastLED.show();

    }
  }

  if (start_num < 5 && millis() - startTime >= interval)
  {
    leds[flicker1[start_num]] = CRGB(0, 0, 0);
    
    startTime = millis();
    FastLED.show();
    ++start_num;
  }

}

It's just about there. The only issue is, as it turns each light off in the countdown, the lights that are still blinking change their blink rate and it becomes almost too fast to see when just a few lights are left. I'm guessing it has something to do with the array because if I do something like this:

void loop()
{
if (millis() - lastMillis > 80UL)
    {
      if (toggle)
      {
        //show colorA
  leds[0] = CRGB(255,50,0);  
  leds[3] = CRGB(255,50,0); 
  leds[6] = CRGB(255,50,0);
  
         
      }
      else
      {
  leds[0] = CRGB(30,50,0);  
  leds[3] = CRGB(30,50,0); 
  leds[6] = CRGB(30,50,0);

        
      }
      
      toggle = !toggle;
      lastMillis = millis();
     FastLED.show();

    }


}

It blinks all lights at the same speed no matter how many are on or off. Is it possible to combine those ideas? To have all lights blinking predictably while the countdown does its work? I figure then you could "randomize" the blink with a variable so that while it may appear random, it doesn't become completely wonky as the lights start to shut off. Or maybe it's just not possible. Either way, thanks for the help!

Just had a quick look at your code and it looks like you need an array of lastMillis variables, one for each LED you are using, not one value for the whole thing.

Grumpy_Mike:
Just had a quick look at your code and it looks like you need an array of lastMillis variables, one for each LED you are using, not one value for the whole thing.

Aha! That sounds brilliant. The only issue is that millis() is still very new to me. How would I even go about matching a millis() variable to each LED?

 unsigned long int lastMillis[7] // for 7 LEDs ( 0 to 6 )

// then later
for (int i = start_num; i < 5; i++) {

    if (millis() - lastMillis[i] > 25UL)

The same when you set lastMillis, you use an index number to show what value in the array to use. In this case it is i the loop variable.

Grumpy_Mike:

 unsigned long int lastMillis[7] // for 7 LEDs ( 0 to 6 )

// then later
for (int i = start_num; i < 5; i++) {

if (millis() - lastMillis[i] > 25UL)



The same when you set lastMillis, you use an index number to show what value in the array to use. In this case it is i the loop variable.

You did it! All the LEDs blink at the same rate now. But for some reason they start the loop blinking, then, after the first light shuts off, they freeze and are just lit one color, then the next light shuts off, and they blink again, then next, they freeze, next they blink, etc. Somehow the loop to shut off a light every 6 seconds seems to be toggling the loop of blinks with each interval -- if that makes any sense.

Here it is with your code, maybe I input something incorrectly?

#include <FastLED.h>
#define NUM_LEDS 250
#define DATA_PIN 6
#define BRIGHTNESS 10
#define COLOR_ORDER RGB
CRGB leds[NUM_LEDS];

int color;

int flicker1[] = {2, 5, 8, 11, 14, 17, 20};

unsigned long startTime = millis();
unsigned long interval = 6000;
unsigned long int lastMillis[7]; // for 7 LEDs ( 0 to 6 )
uint8_t start_num = 0;
bool toggle = false;

void setup() {

  delay(2000);

  FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);

}

void loop()
{
  for (int i = start_num; i < 7; i++) {

    if (millis() - lastMillis[i] > 50UL)
    {
      if (toggle)
      {
        //show colorA
        leds[flicker1[i]] = CRGB(255, 50, 0);
      
      }
      else
      {
        leds[flicker1[i]] = CRGB(90, 23, 0);
        
      }
      toggle = !toggle;
      lastMillis[i] = millis();
      FastLED.show();

    }
  }

  if (start_num < 7 && millis() - startTime >= interval)
  {
    leds[flicker1[start_num]] = CRGB(0, 0, 0);
  
    startTime = millis();
    FastLED.show();
    ++start_num;
  }

}

This bit:-

if (start_num < 7 && millis() - startTime >= interval)

when it is reached start_num is always the same value. It is what ever it is when the :-

for (int i = start_num; i < 7; i++) {

finishes. So why do you include it in the test?

I will give you a hint. When writing code put your cursor on the right of an { and the matching } will have a line round it. This saves you counting and lets you see the extent of your { } structure.

Try and do this after the { for the :-

for (int i = start_num; i < 7; i++) {

Loop

See if this is where you expect it to be.

Grumpy_Mike:
This bit:-

if (start_num < 7 && millis() - startTime >= interval)

when it is reached start_num is always the same value. It is what ever it is when the :-

for (int i = start_num; i < 7; i++) {

finishes. So why do you include it in the test?

I will give you a hint. When writing code put your cursor on the right of an { and the matching } will have a line round it. This saves you counting and lets you see the extent of your { } structure.

Try and do this after the { for the :-

for (int i = start_num; i < 7; i++) {

Loop

See if this is where you expect it to be.

Thanks for the advice about the start_num, I always like to learn to make things more efficient. But as for the blinking issue, you've got me stumped! I thought I knew what you were getting at. I had it wrapped around both loops. So I moved it out of the countdown loop and wrapped it around only the blinking loop, thinking that would solve the conflict. But that didn't change anything. So then I moved the end bracket } to literally every possible point in the loop, and none of them solved the issue. What have I missed?

I think you missed that you have one too many { } pairs so just moving one } is not going to result in the grouping you want. The code to blank lights has to be within the loop where i is changing.

Grumpy_Mike:
I think you missed that you have one too many { } pairs so just moving one } is not going to result in the grouping you want. The code to blank lights has to be within the loop where i is changing.

Hmm, I hate to just ask for the solution because it's better to learn through experience, but I have to because I've got nothing. I feel like I've moved and removed every {} to every possible position five times and yet I just can't get it to work.

Ok this was a more interesting fault that I could initially tell from the code. The problem was not only with the sequence in which you did things but also with the toggle variable. The times when you thought the LEDs were not blinking they actually were. I could tell that because I put a print statement in the bit that flickered the LEDs and it printed that they were in that toggling routine. The problem was that although that routine was running, because of the state of the toggle variable when the start variable was changed the LEDs were being toggled to the same value as they already were on. This happened when the start variable was odd not even. When it was even it was working fine.

So the solution was to give each LED its own toggle value, that is put it in an array. The lastMillis was not needed so it was removed. However I thought a much better effect was generated when the LEDs toggled at a random rate so I added the
if (random(100)>50)
instead of the fixed toggle, this removed the need for the toggle variable all together.
Note if you are controlling three LEDs at a time, like most WS2811 strips the number of LEDs needs to be reduced to one third of the number. In this case that variable should be the number of controllers, not LEDs.

#include <FastLED.h>
#define NUM_LEDS 250
#define DATA_PIN 6
#define BRIGHTNESS 10
#define COLOR_ORDER RGB
CRGB leds[NUM_LEDS];

int color;

int flicker1[] = {2, 5, 8, 11, 14, 17, 20};

unsigned long startTime = millis();
unsigned long interval = 6000;
unsigned long int lastMillis;
uint8_t start_num = 0;
bool toggle[7];

void setup() {
 // delay(2000); // crap not needed
  //FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
  FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
}

void loop() { 
  if (millis() - lastMillis > 50) { // filcker remaining LEDs
    for (int i = start_num; i < 7; i++) {
      if (random(100)>50) // a better effect?
      //if(toggle[i])
      {
        //show colorA
        leds[flicker1[i]] = CRGB(255, 50, 0);   
      }
      else
      {
        leds[flicker1[i]] = CRGB(90, 23, 0); 
      }
      toggle[i] = !toggle[i]; // not needed if keeping the random flicker    
    }
      lastMillis = millis(); // for next flicker      
      FastLED.show();
  } // end of flicker all remaining LEDs

  if (start_num < 7 && (millis() - startTime >= interval)) { // turn one LED off
    leds[flicker1[start_num]] = CRGB(0, 0, 0);
    startTime = millis();
    FastLED.show();
    start_num++;
    if(start_num >= 7) start_num = 0; // start sequence over
  }
}

Grumpy_Mike:
Ok this was a more interesting fault that I could initially tell from the code. The problem was not only with the sequence in which you did things but also with the toggle variable. The times when you thought the LEDs were not blinking they actually were. I could tell that because I put a print statement in the bit that flickered the LEDs and it printed that they were in that toggling routine. The problem was that although that routine was running, because of the state of the toggle variable when the start variable was changed the LEDs were being toggled to the same value as they already were on. This happened when the start variable was odd not even. When it was even it was working fine.

So the solution was to give each LED its own toggle value, that is put it in an array. The lastMillis was not needed so it was removed. However I thought a much better effect was generated when the LEDs toggled at a random rate so I added the
if (random(100)>50)
instead of the fixed toggle, this removed the need for the toggle variable all together.
Note if you are controlling three LEDs at a time, like most WS2811 strips the number of LEDs needs to be reduced to one third of the number. In this case that variable should be the number of controllers, not LEDs.

#include <FastLED.h>

#define NUM_LEDS 250
#define DATA_PIN 6
#define BRIGHTNESS 10
#define COLOR_ORDER RGB
CRGB leds[NUM_LEDS];

int color;

int flicker1[] = {2, 5, 8, 11, 14, 17, 20};

unsigned long startTime = millis();
unsigned long interval = 6000;
unsigned long int lastMillis;
uint8_t start_num = 0;
bool toggle[7];

void setup() {
// delay(2000); // crap not needed
  //FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
  FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
}

void loop() {
  if (millis() - lastMillis > 50) { // filcker remaining LEDs
    for (int i = start_num; i < 7; i++) {
      if (random(100)>50) // a better effect?
      //if(toggle[i])
      {
        //show colorA
        leds[flicker1[i]] = CRGB(255, 50, 0); 
      }
      else
      {
        leds[flicker1[i]] = CRGB(90, 23, 0);
      }
      toggle[i] = !toggle[i]; // not needed if keeping the random flicker   
    }
      lastMillis = millis(); // for next flicker     
      FastLED.show();
  } // end of flicker all remaining LEDs

if (start_num < 7 && (millis() - startTime >= interval)) { // turn one LED off
    leds[flicker1[start_num]] = CRGB(0, 0, 0);
    startTime = millis();
    FastLED.show();
    start_num++;
    if(start_num >= 7) start_num = 0; // start sequence over
  }
}

You're an absolute genius! I thought it was something with the toggle because of the way they would blink and freeze at the same timing of the toggle. But I don't think I ever would have come up with the solution you did on my own. And I love the random effect, it feels more real. I had planned on something like that but was so stuck on the other thing I couldn't even imagine taking that next step. Thank you so much for all your help and patience with a nooby, I really do appreciate it!