Go Down

Topic: Impossible? Adding blinking to a WS2811 which is already using delays (Read 1 time) previous topic - next topic

Despereaux

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:
Code: [Select]

#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!

Delta_G

Quote
about millis() within millis(
Because it doesn't really work that way.  You're just checking to see how long it has been since something happened.  Sometimes that something is the last time you were doing the same thing.  Sometimes that time was when some other thing happened.  Either way, when you record a value to whatever your equivalent of "previousMillis" in the Blink Without Delay example that is the time you saved and you can compare any given time against that to see how long it has been. 

Sometimes you're timing off of multiple events, so you have a number of variables to keep track of the last time a number of different things happened.  But you're still just recording the last time you did something and comparing to the current time to see how long it has been since then.  There's no real trick to it. 

|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

Grumpy_Mike

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

Despereaux

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

Grumpy_Mike

Quote
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

Despereaux

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?


sterretje

Instead of controlling a led, control the strip when it is time. Below the idea
Code: [Select]

if it is time
  update last time that you updated the strip
  set the strip's leds
  show the leds
endif
If you understand an example, use it.
If you don't understand an example, don't use it.

Electronics engineer by trade, software engineer by profession. Trying to get back into electronics after 15 years absence.

Despereaux

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:

Code: [Select]
#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();
}
   



Despereaux

Instead of controlling a led, control the strip when it is time. Below the idea
Code: [Select]

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:

Code: [Select]
#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?

Grumpy_Mike

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

Despereaux

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:

Code: [Select]
#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:

Code: [Select]
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!

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.

Despereaux

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?

Grumpy_Mike

Code: [Select]
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.

Despereaux

Code: [Select]
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?

Code: [Select]
#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;
  }

}

Go Up