Multiple Concurrent Effects on one LED strip

I want to make my ws2811 LED strip to run different animation in different parts of the strip. More specifically I want the outer thirds to do a fire effect and the middle to do a blue and white twinkling effect. I am using an arduino uno. Is this possible? I have searched all over but I must not know the right words to use or its super simple and is just not something i have learned yet. Whatever the reason, I just need help and please be gentle :smiley:

No, it's not "super simple". :astonished:

To start with, you show us the code (read the instructions first!) to do each of these animations without using library functions (except "show") or "delay()" and we can discuss how to combine them. :grinning:

I am using an arduino uno. Is this possible?

Yes.

The sort of thing Paul talked about is known as a state machine and we get asked many times a day about these. So that is the sort of word you should be using to search.

The principles are here:-
http://www.thebox.myzen.co.uk/Tutorial/State_Machine.html
Or Robin2’s several things at once

I have posted an example of this for WS2812 LEDs many times before, here it is again.

// Multiple patterns in a state machine format
// using the FastLED library
// by Mike Cook 2017

#include "FastLED.h"

// first set up the parameters to use in the pattern calling
unsigned long patternInterval [] = { 500, 40, 20, 200, 5 }; // how often each pattern updates
unsigned long lastUpdate [5] ;  // for millis() when last update occurred
boolean patternEnabled [] = {true,true,false,true,true}; // should the pattern be called at all
byte patternState[5]; // state machine variable for patterns - this initialises them to zero

// now set up the LEDs to use
#define NUM_LEDS 64
#define DATA_PIN 3
#define CLOCK_PIN 13


CRGB leds[NUM_LEDS];

// Constants for patterns
// for Fire2012
#define COOLING  20
#define SPARKING 50
#define COLOR_ORDER BGR

// now set up the array of pointers to each pattern
void (*patternPtrs[5])(int index,byte state); //the array of pattern pointers

void setup() {
  //initialises the array of pattern pointers
  patternPtrs[0] = blinkOne; 
  patternPtrs[1] = cylon;
  patternPtrs[2] = fire;
  patternPtrs[3] = colorWipe;
  patternPtrs[4] = rainbowCycle;
  //initialises the FastLED driver you want
  //FastLED.addLeds<APA102,leds, NUM_LEDS); // 13 clock  and 11 data
  FastLED.addLeds<APA102, DATA_PIN, CLOCK_PIN, BGR>(leds, NUM_LEDS); // 13 clock  and 11 data
  FastLED.setBrightness(8);
}

void loop() {
  for(int i = 0; i<5; i++) { // go through all the patterns and see if it is time to call one
    if(patternEnabled[i] && millis() - lastUpdate[i] > patternInterval[i]){
      lastUpdate[i] = millis();
      callPatterns(i, patternState[i]);
    }
  }
}

void callPatterns(int index, byte state) {
  (*patternPtrs[index])(index,state); //calls the pattern at the index of `index` in the array
}

// These are the pattern functions written as a state machine
// this is the Blink program in FastLED's example folder
void blinkOne(int index,byte state) {
  if(state == 0){
    leds[3] = CRGB::Blue;
    FastLED.show();
    patternState[index] = 1; // move on the state machine for the next call
  }
  if(state == 1){
     leds[3] = CRGB::Black;
     FastLED.show();
     patternState[index] = 0;
   }
  }

// this is the Cylon program in FastLED's example folder
// we will use LEDs 8 to 15 to show this
void cylon(int index,byte state) {
  static int i = 8; // replaces the loop index
  if(state == 0){
    leds[i] = CRGB::Red;
    FastLED.show();
    patternState[index] = 1; // move on the state machine for the next call
  }
   if(state == 1){
    // now that we've shown the leds, reset the i'th led to black
    leds[i] = CRGB::Black;
    i++; // increment what was the loop variable
    if(i >= 16){ // we have finished one direction
     patternState[index] = 2;
     i--;
    }
    else {
    patternState[index] = 0;
    }
   }
   // Now go in the other direction only green
   if(state == 2){
     leds[i] = CRGB::Green;
    FastLED.show();
    patternState[index] = 3; // move on the state machine for the next call
   }
  if(state == 3){
    // now that we've shown the leds, reset the i'th led to black
    leds[i] = CRGB::Black;
    i--; // decrement what was the loop variable
    if(i < 8){ // we have finished the return, go back to the start
     patternState[index] = 0;
     i= 8; // ready to start again
    }
    else {
    patternState[index] = 2;
    } 
    // note that this could be better implemented but it has been written like this to keep it close to the original example
    // so you can see what changes have been made 
  }
}

// this is the Fire2012 program in FastLED's example folder
void fire(int index,byte state) {
// using LEDs 16 to 32
// Array of temperature readings at each simulation cell
  const byte startLED = 16; // first LED in section
  const byte numLEDs = 16;
  static byte heat[numLEDs];

  random16_add_entropy( random());
  // Step 1.  Cool down every cell a little
    for( int i = 0; i < numLEDs; i++) {
      heat[i] = qsub8( heat[i],  random8(0, ((COOLING * 10) / NUM_LEDS) + 2));
    }
  
    // Step 2.  Heat from each cell drifts 'up' and diffuses a little
    for( int k= numLEDs - 1; k >= 2; k--) {
      heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
    }
    
    // Step 3.  Randomly ignite new 'sparks' of heat near the bottom
    if( random8() < SPARKING ) {
      int y = random8(7);
      heat[y] = qadd8( heat[y], random8(160,255) );
    }

    // Step 4.  Map from heat cells to LED colors
    for( int j = 0; j < numLEDs; j++) {
        leds[j+startLED] = HeatColor( heat[j]); // transfer heat array to LEDs
    }
    FastLED.show(); // display this frame
  }

//colorWipe  modified from Adafruit example to make it a state machine
// uses LEDs 32 to 40
void colorWipe(int index,byte state) {
  static int i =0; // used as state variable
  static byte firstLED = 32;
  static byte numLEDs = 8;
    leds[i+firstLED] = CRGB::Yellow;
    FastLED.show();
    i++;
  if(i >= numLEDs){
    i = 0;
    for(int j;j<numLEDs; j++) leds[j+firstLED] = CRGB::Black; // blank out strip
  }
}

 // rainbowCycle modified from Adafruit example to make it a state machine
 // uses LEDs 48 to 64
void rainbowCycle(int index,byte state) {
  static uint16_t j=0; // used as state variable
  static byte firstLED = 48;
  static byte numLEDs = 16;
    for(int i=0; i< numLEDs; i++) {
      leds[i+firstLED].setHSV( (((i * 256 / numLEDs) + j) & 255), 255, 255);
    }
    FastLED.show();
    j++;
    if(j >= 256*5) j=0;
}

Hi
I am new to these LEDS I have got a strip of 294 which I have made put in a vertical frame organised as 6 lines of 49 (technically all in one strip).

I have managed to load up some examples and all the 294 leds seem to be working ok.

I would like to setup the fire2012 to have one instance running on each line.

so the first instance would run on leds 0 -48 and the next on 49 -97 ( although preferably 97- 49, as 97 is at the bottom of the frame line 2). Is this possible?

Actually a great start would be to tell me how to wire the ‚ÄúCLOCK_ PIN‚ÄĚ as I have not done it before and cannot find info on wiring it. I am using an Arduino uno

Is this possible?

Yes, perfectly possible.

You just need another variable that defines the range of any instance. But that might not be simple to do if your coding skills are not up to it.

Actually a great start would be to tell me how to wire the "CLOCK_ PIN"

There isn’t one. There is however a data pin if that is what you mean.

and cannot find info on wiring it.

Really?

Hi Thanks for your reply
I meant clock pin but I think that is because your code was written for a different board not the ‚ÄúUNO‚ÄĚ?, anyway nothing happened when I tried to run it on my UNO. I have a version of the Fire 2012 working on my leds and UNO but only as if they are one instance on one strip.

In your code you seem to have a List of conditions [0] - [5] which start at different LEDS but the version of Fire2012 I have seems to callibrate colour and brightness around the NUM_LEDs variable, which It also uses for the
Number of LEDS
Anyway I will carry on plodding through it. I have the following code for fire2012, I was hoping to compare it with yours regarding ‚ÄúState Machine‚ÄĚ the difference being I think I want multiple instances of (Fire 2012) on one strip, on a ‚ÄúUNO‚ÄĚ

Here is the code I have running but only as one instance on one strip

#include <FastLED.h>

#define LED_PIN     5
#define COLOR_ORDER GRB
#define CHIPSET     WS2811
#define NUM_LEDS    294

#define BRIGHTNESS  200
#define FRAMES_PER_SECOND 60

bool gReverseDirection = false;

CRGB leds[NUM_LEDS];

void setup() {
  delay(3000); // sanity delay
  FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
  FastLED.setBrightness( BRIGHTNESS );
}

void loop()
{
  // Add entropy to random number generator; we use a lot of it.
  // random16_add_entropy( random());

  Fire2012(); // run simulation frame
  
  FastLED.show(); // display this frame
  FastLED.delay(1000 / FRAMES_PER_SECOND);
}


// Fire2012 by Mark Kriegsman, July 2012
// as part of "Five Elements" shown here: http://youtu.be/knWiGsmgycY
//// 
// This basic one-dimensional 'fire' simulation works roughly as follows:
// There's a underlying array of 'heat' cells, that model the temperature
// at each point along the line.  Every cycle through the simulation, 
// four steps are performed:
//  1) All cells cool down a little bit, losing heat to the air
//  2) The heat from each cell drifts 'up' and diffuses a little
//  3) Sometimes randomly new 'sparks' of heat are added at the bottom
//  4) The heat from each cell is rendered as a color into the leds array
//     The heat-to-color mapping uses a black-body radiation approximation.
//
// Temperature is in arbitrary units from 0 (cold black) to 255 (white hot).
//
// This simulation scales it self a bit depending on NUM_LEDS; it should look
// "OK" on anywhere from 20 to 100 LEDs without too much tweaking. 
//
// I recommend running this simulation at anywhere from 30-100 frames per second,
// meaning an interframe delay of about 10-35 milliseconds.
//
// Looks best on a high-density LED setup (60+ pixels/meter).
//
//
// There are two main parameters you can play with to control the look and
// feel of your fire: COOLING (used in step 1 above), and SPARKING (used
// in step 3 above).
//
// COOLING: How much does the air cool as it rises?
// Less cooling = taller flames.  More cooling = shorter flames.
// Default 50, suggested range 20-100 
#define COOLING  55

// SPARKING: What chance (out of 255) is there that a new spark will be lit?
// Higher chance = more roaring fire.  Lower chance = more flickery fire.
// Default 120, suggested range 50-200.
#define SPARKING 120


void Fire2012()
{
// Array of temperature readings at each simulation cell
  static byte heat[NUM_LEDS];

  // Step 1.  Cool down every cell a little
    for( int i = 0; i < NUM_LEDS; i++) {
      heat[i] = qsub8( heat[i],  random8(0, ((COOLING * 10) / NUM_LEDS) + 2));
    }
  
    // Step 2.  Heat from each cell drifts 'up' and diffuses a little
    for( int k= NUM_LEDS - 1; k >= 2; k--) {
      heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
    }
    
    // Step 3.  Randomly ignite new 'sparks' of heat near the bottom
    if( random8() < SPARKING ) {
      int y = random8(7);
      heat[y] = qadd8( heat[y], random8(160,255) );
    }

    // Step 4.  Map from heat cells to LED colors
    for( int j = 0; j < NUM_LEDS; j++) {
      CRGB color = HeatColor( heat[j]);
      int pixelnumber;
      if( gReverseDirection ) {
        pixelnumber = (NUM_LEDS-1) - j;
      } else {
        pixelnumber = j;
      }
      leds[pixelnumber] = color;
    }
}

I meant clock pin but I think that is because your code was written for a different board not the "UNO"?,

What do you mean by clock pin then? It is nothing to do with the ws2811, that only has a data pin. It is nothing to do with the processor either so you are not expressing yourself correctly.

being I think I want multiple instances of (Fire 2012) on one strip, on a "UNO"

Yes I know, the code on the other thread was written for a Uno.

anyway nothing happened when I tried to run it on my UNO

Then you have something different on your system. Have you changed my code to reflect the data pin you are using? Have you changed to to reflect the number of LEDs you have?

Hi

Success!

Thanks For your help. Sorry If I did not express myself correctly. Not used much cpp (one module in university)

Having this fire effect looks great side by side, Maybe I should put a video up?

Here is the ‚Äúfire2012‚ÄĚ code by Mark Kriegsman that I edited to make it two dimensional
cut off a couple of routine because the post exceeded the word limit. but it just repeats itself.

#include <FastLED.h>


#define LED_PIN     5
#define COLOR_ORDER GRB
#define CHIPSET     WS2811
#define NUM_LEDS    294
#define NUM_LEDS1   49
#define NUM_LEDS2   98 




#define BRIGHTNESS  200
#define FRAMES_PER_SECOND 60


bool gReverseDirection = false;


CRGB leds[NUM_LEDS];


void setup() {
  delay(3000); // sanity delay
  FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
  FastLED.setBrightness( BRIGHTNESS );
}


void loop()
{
  // Add entropy to random number generator; we use a lot of it.
  // random16_add_entropy( random());


  Fire2012(); // run simulation frame
  Fire20122(); // run simulation frame
  Fire20123(); // run simulation frame
  Fire20124(); // run simulation frame
  Fire20125(); // run simulation frame
  Fire20126(); // run simulation frame
  //FastLED.show(); // display this frame
  FastLED.delay(1000 / FRAMES_PER_SECOND);
}




// Fire2012 by Mark Kriegsman, July 2012 (edited to make two-dimensional Fire)
// as part of "Five Elements" shown here: http://youtu.be/knWiGsmgycY
//// 
// This basic one-dimensional 'fire' simulation works roughly as follows:  
// There's a underlying array of 'heat' cells, that model the temperature
// at each point along the line.  Every cycle through the simulation, 
// four steps are performed:
//  1) All cells cool down a little bit, losing heat to the air
//  2) The heat from each cell drifts 'up' and diffuses a little
//  3) Sometimes randomly new 'sparks' of heat are added at the bottom
//  4) The heat from each cell is rendered as a color into the leds array
//     The heat-to-color mapping uses a black-body radiation approximation.
//
// Temperature is in arbitrary units from 0 (cold black) to 255 (white hot).
//
// This simulation scales it self a bit depending on NUM_LEDS; it should look
// "OK" on anywhere from 20 to 100 LEDs without too much tweaking. 
//
// I recommend running this simulation at anywhere from 30-100 frames per second,
// meaning an interframe delay of about 10-35 milliseconds.
//
// Looks best on a high-density LED setup (60+ pixels/meter).
//
//
// There are two main parameters you can play with to control the look and
// feel of your fire: COOLING (used in step 1 above), and SPARKING (used
// in step 3 above).
//
// COOLING: How much does the air cool as it rises?
// Less cooling = taller flames.  More cooling = shorter flames.
// Default 50, suggested range 20-100 
#define COOLING  55


// SPARKING: What chance (out of 255) is there that a new spark will be lit?
// Higher chance = more roaring fire.  Lower chance = more flickery fire.
// Default 120, suggested range 50-200.
#define SPARKING 120




void Fire2012()
{
// Array of temperature readings at each simulation cell
  static byte heat[NUM_LEDS1];


  // Step 1.  Cool down every cell a little
    for( int i = 0; i < NUM_LEDS1; i++) {
      heat[i] = qsub8( heat[i],  random8(0, ((COOLING * 10) / NUM_LEDS1) + 2));
    }
  
    // Step 2.  Heat from each cell drifts 'up' and diffuses a little
    for( int k= NUM_LEDS1 - 1; k >= 2; k--) {
      heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
    }
    
    // Step 3.  Randomly ignite new 'sparks' of heat near the bottom
    if( random8() < SPARKING ) {
      int y = random8(7);
      heat[y] = qadd8( heat[y], random8(160,255) );
    }


    // Step 4.  Map from heat cells to LED colors
    for( int j = 0; j < NUM_LEDS1; j++) {
      CRGB color = HeatColor( heat[j]);
      int pixelnumber;
      if( gReverseDirection ) {
        pixelnumber = (NUM_LEDS1-1) - j;
      } else {
        pixelnumber = j;
      }
      leds[pixelnumber] = color;
    }
}
void Fire20122()
{
// Array of temperature readings at each simulation cell
  static byte heat[NUM_LEDS1];


  // Step 1.  Cool down every cell a little
    for( int i = 0; i < NUM_LEDS1; i++) {
      heat[i] = qsub8( heat[i],  random8(0, ((COOLING * 10) / NUM_LEDS1) + 2));
    }
  
    // Step 2.  Heat from each cell drifts 'up' and diffuses a little
    for( int k= NUM_LEDS1 - 1; k >= 2; k--) {
      heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
    }
    
    // Step 3.  Randomly ignite new 'sparks' of heat near the bottom
    if( random8() < SPARKING ) {
      int y = random8(7);
      heat[y] = qadd8( heat[y], random8(160,255) );
    }


    // Step 4.  Map from heat cells to LED colors
    for( int j = 0; j < NUM_LEDS1; j++) {
      CRGB color = HeatColor( heat[j]);
      int pixelnumber;
      if( gReverseDirection ) {
        pixelnumber = (NUM_LEDS1-1) - j;
      } else {
        pixelnumber =  97-j;
      }
      leds[pixelnumber] = color;
    }
}
void Fire20123()
{
// Array of temperature readings at each simulation cell
  static byte heat[NUM_LEDS1];


  // Step 1.  Cool down every cell a little
    for( int i = 0; i < NUM_LEDS1; i++) {
      heat[i] = qsub8( heat[i],  random8(0, ((COOLING * 10) / NUM_LEDS1) + 2));
    }
  
    // Step 2.  Heat from each cell drifts 'up' and diffuses a little
    for( int k= NUM_LEDS1 - 1; k >= 2; k--) {
      heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
    }
    
    // Step 3.  Randomly ignite new 'sparks' of heat near the bottom
    if( random8() < SPARKING ) {
      int y = random8(7);
      heat[y] = qadd8( heat[y], random8(160,255) );
    }


    // Step 4.  Map from heat cells to LED colors
    for( int j = 0; j < NUM_LEDS1; j++) {
      CRGB color = HeatColor( heat[j]);
      int pixelnumber;
      if( gReverseDirection ) {
        pixelnumber = (NUM_LEDS1-1) - j;
      } else {
        pixelnumber =  j+98;
      }
      leds[pixelnumber] = color;
    }
}

void Fire2012....etc

Well it is a rather clack handed way of doing things, repeating the function six times and just making minor modifications to each function.

The art of writing a program is to make the code do some work and one rule is that if you write virtually the same code over and over, then there is a simpler way of doing things. Often, as here, this requires you to use arrays to hold different values and passing the array pointer into the function to get the correct range of numbers .