FastLED, Visual Looping issue

Hey all.
Im trying to setup a really simple FastLED sketch with 12 WS2812B leds in a ring.
Im trying to make it visually look like a smooth rotation of 3 LED's. With the middle LED being the brightest/Highest contrast. Colour isn't much of a concern right now but i'd like to be able to change it later.

I've read elsewhere FastLED does not like exceeding the number of LEDs you define. So i tried to create a for loop to compensate for the start and end of each cycle. But i cannot get LED 12 to light at start or LED 0 to light at the end.

I've been trying to get this working and i can't see what i've done wrong.
Simple help would be appreciated.

#include <FastLED.h>
#define DATA_PIN     5
#define NUM_LEDS    12

int DELAYTIME = 300;
int NextLED;
int CurrentLED;
int PreviousLED;  
CRGB leds[NUM_LEDS];

//----------------------------------------------------------------------------------

//----------------------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  FastLED.addLeds<WS2812B, DATA_PIN, RGB>(leds, NUM_LEDS);
}
//----------------------------------------------------------------------------------
void loop() {
 
    for( CurrentLED = 0; CurrentLED <= NUM_LEDS; CurrentLED++) {           

      if (CurrentLED == 0){
        PreviousLED = 12;
      }
      else {
        PreviousLED = CurrentLED - 1;
      }
      
      if (CurrentLED == 12){
        NextLED = 0;
      }
      else{
        NextLED = CurrentLED +1;
      }

      Serial.print("PreviousLED :");
      Serial.println(PreviousLED);
      Serial.print("CurrentLED :");
      Serial.println(CurrentLED);
      Serial.print("NextLED :");
      Serial.println(NextLED); 
      Serial.println("NEXT-------------");  
      
      fill_solid(leds, NUM_LEDS, 0x000000);                  
      leds[PreviousLED] = 0x777040;
      leds[CurrentLED] = 0xFFD700;
      leds[NextLED] = 0x777040;
      delay(10);
      FastLED.show();
      delay(DELAYTIME);
      
    }
    Serial.println("!!LOOP!!");
}

EDIT: BELOW IS THE FINAL WORKING SCRIPT.
Change Lightwidth to the number of LED's you want circling.

#include "FastLED.h"

#define NUM_LEDS  12    // Enter the total number of LEDs on the strip
#define PIN       5     // LED signal Pin

int LightWidth = 3;     //Set the number of active LED's
int delayDuration = 400;


CRGB leds[NUM_LEDS];

void setup() {
  Serial.begin(9600);
  FastLED.addLeds<WS2812B, PIN, GRB>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.setMaxPowerInVoltsAndMilliamps(5, 500);    // limit power of LED strip to 5V, 500mA
  FastLED.clear();                                    // Init LEDs to "off"
}

void loop() {
  //////(int red, int green, int blue, int LightWidth, int delayDuration)
  Beacon(204, 136, 0, LightWidth, delayDuration);
}

void Beacon(int red, int green, int blue, int LightWidth, int delayDuration){
  int LightWidthFix = LightWidth - 1;

  for(int i = 0; i <= ((NUM_LEDS -LightWidth) + LightWidthFix); i++) {
    FastLED.clear();
    FastLED.show();
    ///////////////////////////////////////////Create Primary Leading LED/////////////////////////////////////////////////    
    leds[i].setRGB(red/10, green/10, blue/10); 
    ///////////////////////////////////////////Create Primary Leading LED/////////////////////////////////////////////////  
      
    ///////////////////////////////////////////Create second Trailing LED/////////////////////////////////////////////////
    int j;
    if (i < LightWidthFix ) {
    j = (i + NUM_LEDS) - LightWidthFix;
    leds[j].setRGB(red/10, green/10, blue/10);   
    }
    else{
    j = i -LightWidthFix;      
    leds[j].setRGB(red/10, green/10, blue/10); 
    }    
    ///////////////////////////////////////////Create second Trailing LED/////////////////////////////////////////////////
    
    ///////////////////////////////////////////Fill from Low fix/////////////////////////////////////////////////
    if ((i>0)&&(i<LightWidthFix)){
      Serial.println("FILL LOW");
      for (int l = i; l >= 0; l--) {
        leds[l].setRGB(red, green, blue);
       }
    }
    ///////////////////////////////////////////Fill from Low fix/////////////////////////////////////////////////
    
    ///////////////////////////////////////////Fill To High fix//////////////////////////////////////////////////
    if ((j<NUM_LEDS)&&(j>(NUM_LEDS-LightWidth))){ 
      Serial.println("FILL HIGH");
      for (int l = j; l < NUM_LEDS; l++) {
        leds[l].setRGB(red, green, blue);
       }
    }
    ///////////////////////////////////////////Fill To High fix//////////////////////////////////////////////////
    
    ///////////////////////////////////////////Fill To Main Cycle//////////////////////////////////////////////////
    if ((i>=LightWidthFix)&&(j<=(NUM_LEDS-LightWidth))){ 
      Serial.println("FILL MAIN");
      for (int t = 0; t <= LightWidthFix; t++){
      leds[j+t].setRGB(red, green, blue);
      }
    }
    ///////////////////////////////////////////Fill To Main Cycle//////////////////////////////////////////////////  
    

    FastLED.show();
    delay(delayDuration);
  }

}

for( CurrentLED = 0; CurrentLED <= NUM_LEDS; CurrentLED++) { 

What will be the values of CurrentLED produced by this for loop and how many LEDs do you have ?

1 Like

Hey heli bob! Thanks for the super quick reply.

I super misread what you just wrote. Unless you modified it after replying :laughing:

As often happens when writing out the problem i found the solution.

I had forgetten to reference the PreviousLED and NextLED as a zero based index.
Meaning i was waiting for it to reach a 12 when 11 was the max the for loop counted through.

I personally find FastLED really frustrating.
So heres the complete code for anyone who wants it in the future.

#include <FastLED.h>
#define DATA_PIN     5
#define NUM_LEDS    12

int DELAYTIME = 500;
int NextLED;
int CurrentLED;
int PreviousLED;  
CRGB leds[NUM_LEDS];

//----------------------------------------------------------------------------------

//----------------------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  FastLED.addLeds<WS2812B, DATA_PIN, RGB>(leds, NUM_LEDS);
}
//----------------------------------------------------------------------------------
void loop() {
 
    for( CurrentLED = 0; CurrentLED <= (NUM_LEDS-1); CurrentLED++) {           

      if (CurrentLED == 0){
        PreviousLED = 11;
      }
      else {
        PreviousLED = CurrentLED - 1;
      }
      
      if (CurrentLED == 11){
        NextLED = 0;


        
      }
      else{
        NextLED = CurrentLED +1;

      }
      Serial.print("PreviousLED :");
      Serial.println(PreviousLED);
      Serial.print("CurrentLED :");
      Serial.println(CurrentLED);
      Serial.print("NextLED :");
      Serial.println(NextLED); 
      Serial.println("NEXT-------------");  
      
      fill_solid(leds, NUM_LEDS, 0x000000);                  
      leds[PreviousLED] = 0x777040;
      leds[CurrentLED] = 0xFFD700;
      leds[NextLED] = 0x777040;
      
      delay(10);
      FastLED.show();
      delay(DELAYTIME);
      
    }
    Serial.println("!!LOOP!!");
}

I did not modify my reply after posting it and it sounds like you have found the problem but do you understand my questions ?

You should consider not using a for loop and just letting the loop() function increment an index variable. You could use the modulus operator (%) to deal with the rollover back to zero

for( CurrentLED = 0; CurrentLED <= (NUM_LEDS-1); CurrentLED++) {

This will work but this is simpler

for( CurrentLED = 0; CurrentLED < NUM_LEDS); CurrentLED++) {
1 Like

Your sketch from post #4 in Wokwi simulation:

1 Like

@UKHeliBob
I'm trying to avoid building code that's relys on the loop() function so that i can easily seperate it into its own function later. I had a really bad experience building a program before where i'd relied on the main loop in all the wrong places and ended up scrapping the whole thing out of shere annoyance.

for( CurrentLED = 0; CurrentLED < NUM_LEDS); CurrentLED++) {

Yup this is the kind of stuff i miss. Thank again. :+1:

You talk about incrementing an index variable? I don't understand this part.
Isn't that what a for loop does? Creates an array (Index) then loops through the variables within it?

So don't put the code in loop(), put it in a function and call it from loop(). If you are worried about using loop() to do the counting because it blocks operation of the code then that is exactly what the for loop does

Take a look at this example of using loop() to count from 0 to 6 in a non blocking way and note the use of modulus to warp the index value back to zero


void setup()
{
    Serial.begin(115200);
}

void loop()
{
    doCount();
}

void doCount()
{
    static byte index = 0;
    const byte maxCount = 7;
    const int countPeriod = 1000;
    unsigned long currentTime = millis();
    static unsigned long startTime = currentTime;
    if (currentTime - startTime >= countPeriod)
    {
        Serial.println(index);
        index++;
        index = index % maxCount;  //wrap round to zero when max count is reached
        startTime = currentTime;   //save start time for next count period
    }
}

1 Like

I think that your problem was not that you used a loop, but that your code was a blocking, like the current one. You used a for loop with a numerous delay() in it, that is, it cannot be executed simultaneously with other tasks on the controller.

Read the "Blink_without delay" tutorial and rewrite your code using millis instead of for loop and delays.

FastLED has the "EVERY_N_MILLISECONDS" for non-blocking code.
Here is an example: FastLED/DemoReel100.ino at master · FastLED/FastLED · GitHub

This is very usefull to know. My biggest crutch had been looking fastLED documentation with examples that make sense. This has alot but some of the explainations are not the best. I dont even think this references "EVERY_N_MILLISECONDS".

This is nothing than crutch for beginners, who do not understand millis ....

Your problem is not FastLED-specific, and the solution is not to be found in the library's documentation. This is a general principle for writing non-blocking code that applies to any Arduino program.
See the "Blink without delay" tutorial

I know what millis is. I just tend to loose my grasp on where and how add it. Normally only remembering about its usefullness when already half way into a project. Which the requires rewriting / shuffling the code to accept millis into it.

For example where in a for loop should millis be used? If its not blocking does it simply skip steps off the for loop?

A for loop is, by its very nature, a blocking program structure so using millis() within it is quite possibly inappropriate depending on what you are doing

The for loop in this program is not needed at all. All "looping" of your code should be managed by main loop only. And all time control - with millis

I've redone the LED script i was working on by butchering Knightrider LED

Its a much smoother effect and uses many for loops.

Now how would i implement millis into this where the delays should be without breaking the for loops or skipping steps?

Also @UKHeliBob You mentioned modulo earlier for creating a limited count. Could i implement that into the /// Commented out "for" at the bottom of this code? The problem i have is that i + j can be
1 to 3 meaning all withing the 12 LED limit
OR
12 to 2 meaning im exceeding the 12 LED limit

#include "FastLED.h"

#define NUM_LEDS  12    // Enter the total number of LEDs on the strip
#define PIN       5     // LED sig Pin

int LightWidth = 5;     //Set the number of active LED's
int delayDuration = 300;


CRGB leds[NUM_LEDS];

void setup() {
  FastLED.addLeds<WS2812B, PIN, GRB>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.setMaxPowerInVoltsAndMilliamps(5, 500);    // limit power of LED strip to 5V, 500mA
  FastLED.clear();                                    // Init LEDs to "off"
}

void loop() {
  //////(int red, int green, int blue, int LightWidth, int delayDuration)
  Beacon(204, 136, 0, LightWidth, delayDuration);
}

void Beacon(int red, int green, int blue, int LightWidth, int delayDuration){
  int LightWidthFix = LightWidth - 1;

  for(int i = 0; i <= ((NUM_LEDS -LightWidth) + LightWidthFix); i++) {
    FastLED.clear();
    FastLED.show();
    
    leds[i].setRGB(red/10, green/10, blue/10); ///Create Primary LED
    
    if (i < LightWidthFix ) {
    leds[(i + NUM_LEDS) - LightWidthFix].setRGB(red/10, green/10, blue/10); ///Create second Trailing LED  
    }

    else{
    leds[i - LightWidthFix].setRGB(red/10, green/10, blue/10); ///Create second Trailing LED
    }    
    
//      for(int j = 1; j <= LightWidthFix; j++) { ///Fill LEDS between two points
//        leds[i + j].setRGB(red, green, blue);
//      }
      
    
    FastLED.show();
    delay(delayDuration);
  }

}

So is there a way of creating a for loop that is non blocking? using millis? I feel this has gone off topic a bit but its usefull information to know.

Do you have an example of how to iterate through an array without using a for loop?

It seems to me that you didn't understand @UKHeliBob

He wrote: