Trying to make LEDs light up at random intervals

Status
Not open for further replies.

harald25

Well-known member
Hey.
I have a setup with 5 LEDs. I want these to light up at random intervals, and then fade out.
My approach is to have an array (NextBlink_LEDi[5]). It has one value for each LED, indicating the next time it should light up.
I have a loop that compares the current time with the value in the array to determine if the LED should light up or not.


First I set initial values:
Code:
void Blink()
    {
      ActivePattern = BLINK;
      for (int i = 0; i < 5; i++) {
        Index_LEDi[i] = 0;
        NextBlink_LEDi[i] = millis()+(random16()%5000);
      }
      TotalSteps = 255;
    }

And this is the code that determines if it's time to update the LEDs, and does it:
Code:
void BlinkUpdate()
    {
      //Runs through each LED
      for (int i = 0; i < 5; i++)
      {
        //Checks if time now is greater or eaqual to next blink time
        if (millis() >= NextBlink_LEDi[i])
        {
          
          leds[i] = ColorFromPalette( heatcolorPalette, Index_LEDi[i]);   //Sets the color of the LED to a value in a previously generated palette
          Index_LEDi[i]++;                                                //Increases index so that the next color in the palette will be selected on the next round

          //Checks if we have run through the entire color palette
          if (Index_LEDi[i] > TotalSteps)
          {
            Index_LEDi[i] = 0;                                            //Resets the index to 0
            NextBlink_LEDi[i] = millis()+(random16()%50000);              //Generates a new time for the next blink
          }
        }
      }
      FastLED.show();  
    }

The initial values seem to be working, because the LEDs power on in a new random order every time I reset power.
But then after a LED has run through the color palette it just starts on a new round immediately. It seems like generating a new time for the nest blink doesn't work.

I recorded a short video so you can better understand:
https://youtu.be/unGTJ6aRRFQ



Here is all my code so you can try to run it yourself:
Code:
#include "FastLED.h"

/////////// BLINK PARAMETERS \\\\\\\\\\\\\



/////////// CANDLE PARAMETERS \\\\\\\\\\

// The data-in pin of the NeoPixel
// #define WICK_PIN                6
// Any unconnected pin, to try to generate a random seed
#define UNCONNECTED_PIN         23

// The LED can be in only one of these states at any given time
#define BRIGHT                  0
#define UP                      1
#define DOWN                    2
#define DIM                     3
#define BRIGHT_HOLD             4
#define DIM_HOLD                5

// Percent chance the LED will suddenly fall to minimum brightness
#define INDEX_BOTTOM_PERCENT    10
// Absolute minimum red value (green value is a function of red's value)
#define INDEX_BOTTOM            128
// Minimum red value during "normal" flickering (not a dramatic change)
#define INDEX_MIN               192
// Maximum red value
#define INDEX_MAX               255

// Decreasing brightness will take place over a number of milliseconds in this range
#define DOWN_MIN_MSECS          20
#define DOWN_MAX_MSECS          250
// Increasing brightness will take place over a number of milliseconds in this range
#define UP_MIN_MSECS            20
#define UP_MAX_MSECS            250
// Percent chance the color will hold unchanged after brightening
#define BRIGHT_HOLD_PERCENT     20
// When holding after brightening, hold for a number of milliseconds in this range
#define BRIGHT_HOLD_MIN_MSECS   0
#define BRIGHT_HOLD_MAX_MSECS   100
// Percent chance the color will hold unchanged after dimming
#define DIM_HOLD_PERCENT        5
// When holding after dimming, hold for a number of milliseconds in this range
#define DIM_HOLD_MIN_MSECS      0
#define DIM_HOLD_MAX_MSECS      50

#define MINVAL(A,B)             (((A) < (B)) ? (A) : (B))
#define MAXVAL(A,B)             (((A) > (B)) ? (A) : (B))

byte state;
unsigned long flicker_msecs;
unsigned long flicker_start;
byte index_start;
byte index_end;
unsigned long current_time;

///////////////////      \\\\\\\\\\\\\\\\\\\\\\\

#define NUM_LEDS 5
#define LED_PIN 6
#define BUTTON 0
uint8_t sat = 255;
uint8_t val = 255;
enum pattern { NONE, RAINBOW_CYCLE, CANDLE, HEAT_COLOR, LOVE, BLINK};
uint8_t patternID;
int buttonState;
int lastButtonState;

DEFINE_GRADIENT_PALETTE( love_gp ) {
  0,    255,  255,  255,    //White
  127,  255,  0,    255,    //Pink
  255,  128,  0,    128};   //Purple

DEFINE_GRADIENT_PALETTE( heatmap_gp ) {
  0,     0,  0,  0,   //black
128,   255,  0,  0,   //red
224,   255,255,  0,   //bright yellow
255,   255,255,255 }; //full white

CRGB leds[NUM_LEDS];

class LedsMultitask
{
  public:

    pattern ActivePattern;

    unsigned long Interval;
    unsigned long LastUpdate;

    uint16_t TotalSteps;
    int16_t Index;

    uint8_t Hue1;
    uint8_t Value1;
    uint8_t Saturation1;
    uint8_t ColorDistance;

    uint8_t Index_LEDi[5];
    uint16_t NextBlink_LEDi[5];
    
    CRGBPalette16 heatcolorPalette = heatmap_gp;
    CRGBPalette16 lovePalette = love_gp;

        
    LedsMultitask() {};

    void Update()
    {
      if((millis() - LastUpdate) > Interval) // time to update
      {
        LastUpdate = millis();
        switch (ActivePattern)
        {
          case RAINBOW_CYCLE:
             RainbowCycleUpdate();
             break;
          case CANDLE:
             CandleCycleUpdate();
             break;
          case HEAT_COLOR:
              HeatcolorUpdate();
              break;
          case LOVE:
              LoveUpdate();
              break;
          case BLINK:
              BlinkUpdate();
              break;
          default:
            break;
        }
      }
    }

    void Blink()
    {
      ActivePattern = BLINK;
      for (int i = 0; i < 5; i++) {
        Index_LEDi[i] = 0;
        NextBlink_LEDi[i] = millis()+(random16()%5000);
      }
      TotalSteps = 255;
    }

    // Update the Blink pattern
    void BlinkUpdate()
    {
      //Runs through each LED
      for (int i = 0; i < 5; i++)
      {
        //Checks if time now is greater or eaqual to next blink time
        if (millis() >= NextBlink_LEDi[i])
        {
          
          leds[i] = ColorFromPalette( heatcolorPalette, Index_LEDi[i]);   //Sets the color of the LED to a value in a previously generated palette
          Index_LEDi[i]++;                                                //Increases index so that the next color in the palette will be selected on the next round

          //Checks if we have run through the entire color palette
          if (Index_LEDi[i] > TotalSteps)
          {
            Index_LEDi[i] = 0;                                            //Resets the index to 0
            NextBlink_LEDi[i] = millis()+(random16()%50000);              //Generates a new time for the next blink
          }
        }
      }
      FastLED.show();  
    }
    
    void RainbowCycle()
    {
      ActivePattern = RAINBOW_CYCLE;
      Interval = 100;
      TotalSteps = 255;
      Index = 0;
      Value1 = 255;
      Saturation1 = 255;
      ColorDistance = 100;
    }

    // Update the Rainbow Cycle Pattern
    void RainbowCycleUpdate()
    {
      for (int i = 0; i < NUM_LEDS; i++)
      {
        uint8_t hue = (Index+(i*ColorDistance));
        leds[i] = CHSV(hue, Saturation1, Value1);
      }
      FastLED.show();
      Increment();    
     }


     // CANDLE
     void CandleCycle()
    {
      ActivePattern = CANDLE;
      Interval = 0;
      TotalSteps = 255;
      Index = 0;
      set_color(255);
      index_start = 255;
      index_end = 255;
      state = BRIGHT;
    }

    void CandleCycleUpdate()
    {
      current_time = millis();
    
      switch (state)
        {
        case BRIGHT:
          flicker_msecs = random(DOWN_MAX_MSECS - DOWN_MIN_MSECS) + DOWN_MIN_MSECS;
          flicker_start = current_time;
          index_start = index_end;
          if ((index_start > INDEX_BOTTOM) &&
              (random(100) < INDEX_BOTTOM_PERCENT))
            index_end = random(index_start - INDEX_BOTTOM) + INDEX_BOTTOM;
          else
            index_end = random(index_start - INDEX_MIN) + INDEX_MIN;
    
          state = DOWN;
          break;
        case DIM:
          flicker_msecs = random(UP_MAX_MSECS - UP_MIN_MSECS) + UP_MIN_MSECS;
          flicker_start = current_time;
          index_start = index_end;
          index_end = random(INDEX_MAX - index_start) + INDEX_MIN;
          state = UP;
          break;
        case BRIGHT_HOLD:
        case DIM_HOLD:
          if (current_time >= (flicker_start + flicker_msecs))
            state = (state == BRIGHT_HOLD) ? BRIGHT : DIM;
    
          break;
        case UP:
        case DOWN:
          if (current_time < (flicker_start + flicker_msecs))
            set_color(index_start + ((index_end - index_start) * (((current_time - flicker_start) * 1.0) / flicker_msecs)));
          else
            {
            set_color(index_end);
    
            if (state == DOWN)
              {
              if (random(100) < DIM_HOLD_PERCENT)
                {
                flicker_start = current_time;
                flicker_msecs = random(DIM_HOLD_MAX_MSECS - DIM_HOLD_MIN_MSECS) + DIM_HOLD_MIN_MSECS;
                state = DIM_HOLD;
                }
              else
                state = DIM;
              }
            else
              {
              if (random(100) < BRIGHT_HOLD_PERCENT)
                {
                flicker_start = current_time;
                flicker_msecs = random(BRIGHT_HOLD_MAX_MSECS - BRIGHT_HOLD_MIN_MSECS) + BRIGHT_HOLD_MIN_MSECS;
                state = BRIGHT_HOLD;
                }
              else
                state = BRIGHT;
              }
            }
    
          break;
        }
    }

    void set_color(byte index)
    {
      index = MAXVAL(MINVAL(index, INDEX_MAX), INDEX_BOTTOM);
      if (index >= INDEX_MIN)
      {
        for (int i = 0; i < 5; i++) {
          leds[i] = CRGB(index, (index * 3) / 8, 0);
        }
      }
      else if (index < INDEX_MIN)
      {
        for (int i = 0; i < 5; i++) {
          leds[i] = CRGB(index, (index * 3.25) / 8, 0);
        }
      }
    
      FastLED.show();
    }

    void Heatcolor()
    {
      ActivePattern = HEAT_COLOR;
      Interval = 75;
      TotalSteps = 255;
      Index = 0;
      ColorDistance = 25;
    }
    
    void HeatcolorUpdate()
    {
      for (int i = 0; i < NUM_LEDS; i++)
      {
        uint8_t paletteindex = (Index+(i*ColorDistance));
        leds[i] = ColorFromPalette( heatcolorPalette, paletteindex);
      }
      FastLED.show();
      Increment();
    }

    void Love()
    {
      ActivePattern = LOVE;
      Interval = 75;
      TotalSteps = 255;
      Index = 0;
      ColorDistance = 25;
    }
    
    void LoveUpdate()
    {
      for (int i = 0; i < NUM_LEDS; i++)
      {
        uint8_t paletteindex = (Index+(i*ColorDistance));
        leds[i] = ColorFromPalette( lovePalette, paletteindex);
      }
      FastLED.show();
      Increment();
    }


    // Increment the Index and reset at the end
    void Increment()
    {
      Index++;
      if (Index >= TotalSteps)
      {
        Index = 0;
      }
    }

    void SetHue1(uint8_t hue) {
      Hue1 = hue;
    }
    void SetSaturation1(uint8_t sat) {
      Saturation1 = sat;
    }
    void SetValue1(uint8_t val) {
      Value1 = val;
    }
    void SetInterval(uint8_t inter) {
      Interval = inter;
    }
    void SetColorDistance(uint8_t dist) {
      ColorDistance = dist;
    }
};

LedsMultitask Bordlys;

void setup() {
  FastLED.addLeds<NEOPIXEL, LED_PIN>(leds, NUM_LEDS);
  randomSeed(analogRead(UNCONNECTED_PIN));
  random16_add_entropy( analogRead(UNCONNECTED_PIN) );
  patternID = 0;
  pinMode(BUTTON, INPUT_PULLUP);
  Bordlys.Blink();
  Serial.begin(9600);
}

void loop() {

  
  Bordlys.Update();
  random16_add_entropy( analogRead(UNCONNECTED_PIN) );
  
  
  buttonState = digitalRead(BUTTON);
  
  if (buttonState != lastButtonState)
  {
    if(buttonState == LOW)
    {
      NextPattern();
    }
  }
  delay(10);
  lastButtonState = buttonState;
}

void NextPattern()
{
  patternID ++;
  if(patternID >= 11)
  {
    patternID = 0;
  }

  switch(patternID)
  {  
      // Candle light  
      case 0:
          Bordlys.CandleCycle();
          break;
      // Standard rainbow
      case 1:
          Bordlys.RainbowCycle();
          Bordlys.SetInterval(100);
          Bordlys.SetColorDistance(100);
          Bordlys.SetValue1(255);
          break;
      // fast rainbow   
      case 2:
          Bordlys.SetInterval(10);
          Bordlys.SetValue1(255);
          Bordlys.SetColorDistance(100);
          break;
      // Standard, dimmed rainbow
      case 3:
          Bordlys.SetInterval(100);
          Bordlys.SetValue1(175);
          Bordlys.SetColorDistance(100);
          break;
      // Fast, dimmed rainbow 
      case 4:
          Bordlys.SetInterval(10);
          Bordlys.SetValue1(175);
          Bordlys.SetColorDistance(100);
          break;
      // Low color distance rainbow
      case 5:
          Bordlys.SetInterval(100);
          Bordlys.SetValue1(255);
          Bordlys.SetColorDistance(25);
          break;
      // Low color distance rainbow, fast
      case 6:
          Bordlys.SetInterval(10);
          Bordlys.SetValue1(255);
          Bordlys.SetColorDistance(25);
          break;
       // Low color distance rainbow, dimmed
      case 7:
          Bordlys.SetInterval(100);
          Bordlys.SetValue1(175);
          Bordlys.SetColorDistance(25);
          break;
      // Low color distance rainbow,dimmed, fast
      case 8:
          Bordlys.SetInterval(10);
          Bordlys.SetValue1(175);
          Bordlys.SetColorDistance(25);
          break;
      // Heat colors
      case 9:
          Bordlys.Heatcolor();
          Bordlys.SetInterval(75);
          Bordlys.SetColorDistance(25);
          break;
      // Love
      case 10:
          Bordlys.Love();
          Bordlys.SetInterval(75);
          Bordlys.SetColorDistance(25);
          break;       
      default:
          break;
  }

  //Serial.println(patternID);
}
 
Last edited:
I have not tried it yet, but first thing I would try would change the next blink LEDs array to uint32_t and see if it is simply issue of rolling over at 16 bits
 
I tried changing it to both uint32_t and unsigned long, but that didn't change anything.
I've managed to get som serial output, and I can see that the values of the array NextBlink_LEDi[] never changes, after it is set in Blink().

I'll read some more on your comments in my other post and see if any of your tips from there might help.
 
I figured it out! It was even more trivial than uint32_t vs uint16_t.
This statement was never true:
Code:
if (Index_LEDi[i] > TotalSteps)

Changing it to this fixed the problem:
Code:
if (Index_LEDi[i] >= TotalSteps)


But... I think it's good that you brought up the 32-bit thing, because my NextBlink_LEDi-variable would be overflowed pretty fast as a 16 bit integer.

Thank you a lot for input on Christmas eve :)
And Merry Christmas to you!
 
what works really well is to run through the array, and with a probability p, change it from off to on. If you want 10 % of the LEDs on (on average), then p is 0.1 etc. This keeps the whole set of LEDs working randomly.
 
Status
Not open for further replies.
Back
Top