Auto run patterns led based on Mike Cook FastLED sketch

Hello

I am a new here but quite while to know and play with arduino. But State Machine truly hard to understand. :slight_smile:

So I would like to use Mike Cook "Multiple patterns in a state machine format", my goal is having couple pattern led that auto run independently and repeat over and over, but it also have couple button which will show different led pattern when it push on. Just as consideration that the button will be in INPUT_PULLUP.

Here is the code:

// Multiple patterns in a state machine format
// using the FastLED libiary
// 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 origional 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;
}

It's already couple day playing around with no result, so I really appreciated if someone could help and it will really helpful. Hopefully the one who have the code. :slight_smile:

Thanks in advance.

my goal is having couple pattern led that auto run independently and repeat over and over, but it also have couple button which will show different led pattern when it push on

I am not sure what you mean.

As it is all the patterns show on different sections of the LED strip at the same time. Do you only want some of these patterns to run all the time? If so which ones? When you push a button exactly what do you want to happen?

Is it for more patterns to be run? If so what stops these extra patterns from running?

The line

boolean patternEnabled [] = {true,true,false,true,true}; // should the pattern be called at all

Should have false put in the patterns you don’t want to run

When a button is pushed have the code set the pattenEnable pattern set to true.

When you want to stop this function from running then put the pattenEnable pattern set to false.
Suppose this is pattern the third pattern then:-

pattenEnable[2] = true;// run the third pattern in the list
// and to turn it back off
pattenEnable[2] = false;// stop running the third pattern

The code for looking at the push button and deciding what to run should be at the start of the loop function.

Firstly thanks for the reply

Sorry if my previous post not quite clear.

Let's say if there were five patterns and two buttons, I want pattern no. 1-3 run automatically through the whole led strip alternately and loop forever. When the button no. 1&2 push on, it will stop the auto run previously and show pattern no.4&5. And when the button released, the pattern no. 1-3 will run automatically again.

I have tried something like this:

boolean patternEnabled [] = {true, true, true, false, false}; // should the pattern be called at all

the led look strange, it's kind a all the pattern run on the same time, not one by one.

note: I have modified each patterns so the each patterns will run through the led strip, not only specific leds.

any advice would be appreciated.

the led look strange, it's kind a all the pattern run on the same time, not one by one.

Yes that is the whole point of that example code, to show you how to run several things at once. If you want the patterns to run one by one you can do one of two things.

  1. Don't bother with this code and start fresh because normally the example patterns can only run one at a time.

  2. Arrange it so that the patternEnabled array only has one element set to true and all the others to false. Then at intervals change what pattern is run by setting the patternEnabled array.

When the button is pressed set the patternEnabled array to have your patterns 4 and 5 set to true and all the others set to false.

by controlling the patternEnabled array you can have what ever patterns you want running.

Well, thank you. Recently I've tried to modified the sketch, but have no luck to make it work properly. There were no error in the sketch, but the led wont turn on at all.

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

#include "FastLED.h"

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

// now set up the LEDs to use
#define DATA_PIN    5
//#define CLK_PIN   4
#define LED_TYPE    WS2811
#define COLOR_ORDER GRB
#define NUM_LEDS    18
CRGB leds[NUM_LEDS];

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

const int buttonPin1 = 11;    // the number of the pushbutton pin
const int buttonPin2 = 10;    // the number of the pushbutton pin
const int buttonPin3 = 9;    // the number of the pushbutton pin
const int buttonPin4 = 8;    // the number of the pushbutton pin

// Variables will change:
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button
int buttonPushCounter = 0;   // counter for the number of button presses

byte btnRepeatCounter = 1;
byte lastKeyPressed = 0;
int btnRepeatDelay = 150;
unsigned long btnRepeatStart = 0;
unsigned long lastButtonPress = 0;


bool displayMode = true;
int ledMode;

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

void setup() {
  // Setup the Serial port. see http://arduino.cc/en/Serial/IfSerial
  Serial.begin(9600);
  
  pinMode(buttonPin1, INPUT_PULLUP);
  pinMode(buttonPin2, INPUT_PULLUP);
  pinMode(buttonPin3, INPUT_PULLUP);
  pinMode(buttonPin4, INPUT_PULLUP);
  
  //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<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS);
  //  FastLED.addLeds<APA102, DATA_PIN, CLOCK_PIN, BGR>(leds, NUM_LEDS); // 13 clock  and 11 data
  FastLED.setBrightness(8);
}

void loop() {

  checkButton();
  
  /*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();
      ledMode+1;
      //callPatterns(i, patternState[i]);
    }
  }*/

  readMode();

  if (displayMode == true) {
    switch (ledMode) {

      case 0: //pattern 1
        patternEnabled[0] = true;
        patternEnabled [1] = false;
        patternEnabled [2] = false;
        break;

      case 1: //pattern 2
        patternEnabled[1] = true;
        patternEnabled [2] = false;
        patternEnabled [0] = false;
        break;

      case 2: //pattern 3
        patternEnabled[2] = true;
        patternEnabled [0] = false;
        patternEnabled [1] = false;
        break;
    }
  }
}


void checkButton() {

  if (lastKeyPressed == 1) {
    Serial.println("Button 1");
    patternEnabled[3] = true;
  } else {
  }

  if (lastKeyPressed == 2) {
    Serial.println("Button 2");
    patternEnabled[4] = true;
  } else {
  }

  if (lastKeyPressed == 3) {
    Serial.println("Button 3");
  } else {
  }

  if (lastKeyPressed == 4) {
    Serial.println("Button 5");
    
  } else {
  }

  if (lastKeyPressed == 12) {
    Serial.println("Button 4");
  } else {
  }

  lastKeyPressed = readButtons();
}

byte readButtons() {
  byte activeButton = 0;
  byte retVal = 0;
  if ( digitalRead(buttonPin1) == 0 || digitalRead(buttonPin2) == 0 || digitalRead(buttonPin3) == 0 || digitalRead(buttonPin4) == 0) {
    if (digitalRead(buttonPin1) == 0) activeButton = 1;
    else if (digitalRead(buttonPin2) == 0) activeButton = 2;
    else if (digitalRead(buttonPin3) == 0) activeButton = 3;
    else if (digitalRead(buttonPin4) == 0) activeButton = 4;
    if ( digitalRead(buttonPin1) == 0 && digitalRead(buttonPin2) == 0 ) activeButton = 12;
    if (millis() - lastButtonPress >= btnRepeatDelay) {
      btnRepeatStart = millis();
      btnRepeatCounter = 0;
      retVal = activeButton;
    } else if (millis() - btnRepeatStart >= btnRepeatDelay * (btnRepeatCounter + 1) ) {
      btnRepeatCounter++;
      if (btnRepeatCounter > 5) retVal = activeButton;
    }
    lastButtonPress = millis();
  }
  return retVal;
}

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

void readMode() {
  if (displayMode == true) {
      ledMode++;
      if (ledMode > 6) {
        ledMode = 0;
      }
    }
}

Please help, I really need this work but I am noob with state machine :smiley:

Thanks in advance.

I really need this work but I am noob with state machine

What you are doing wrong is little to do with state machines. You are trying to do too much in one step. No professional programmer would try to do that much in a single step. You have to break the problem down into small steps. You get a small step working and then you incorporate the next step into the code and so on until you get to where you want to be.

I would start with just getting the program to do the sequence of patterns you want. If you have trouble with that you post the code and say what it is doing and what you expect it to do.

Then you introduce just one push button and have that either switch off the sequence of patterns when it is held down and resume them when it is released.

Go from there to having another pattern run when the button is held down.

Finally extend that to more buttons and more patterns.

Your readButtons() function and checkButton() functions are all over the place you need to simplify that, which will happen if you follow my advice.

that's great advice,
Ok then et's skip away for the buttons and focus on the sequences. If you don't mind, is there any clue how to make it work? It's seem using 'switch' method didn't work, Should I make another array for the sequence? Or create void for sequence?

If you don't mind, is there any clue how to make it work

It depends on how you want it to operate, to change after a specific time or to change after a specific number of of completed cycles of patterns. So you have to keep track of either time or the number of cycles you have made.

It's seem using 'switch' method didn't work

No a switch statement will work if you use it correctly, as will a sequence of if statements.

When trying to get a program to work you can use Serial.print to see if a program reaches any specific point and also to see the values of the variables you have at that point. Then you can tell if they are the values you are expecting. If they are not you than then track down if this is due to your code or your understanding.

Thank you very much for time, Mike

I'll really appreciate it. I'll try as your suggestion, I'll show you an update if there's an update.

Cheers,