Create code for a semi random FastLED pattern

Hi Folks,

Total newbie here. Ive installed FastLED on my Arduino Uno and have successfully tested my WS2812B string lights. Im trying to create a script that will illuminate 30 random leds (out of a set of 100) at once - wait for 20 seconds - then fade out and fade in 30 new random leds. Id like to use a solid amber color for the leds. Any thoughts would be much appreciated. Thanks!!

Generate 20 random numbers between 0 and 99 (both included) and store them in an array. Be aware that you might get duplicate random numbers so you need to check the array if the number was already used.

The numbers in the array will be an index into your LED array. So you can loop through that array, get the number and use that number to select a LED in your LED array and set the colour.

Fading will be the easiest if you use HSV values instead of RGB.

  1. Fill an array with 100 numbers from 0 to 99
  2. Use a for loop to swap two random array entries as many times as you like up to several thousand
  3. Use the first 30 entries in the array as the index to the LEDs to turn on
  4. Fade out the 30 LEDs after 20 seconds
  5. Repeat steps 2 to 4

There is no need to swap "up to several thousand". Look at the Fisher–Yates shuffle. it's only one swap going through the list and the algorithm produces an unbiased permutation (every permutation is equally likely).

1 Like

True, but you can never do too many swaps and it will all be over in no time anyway :grinning:

My main aim was to avoid the need to check for duplicate numbers

What will be a problem is that using a Uno the result will not be random anyway but that will probably go unnoticed

Fade WS2812 using FastLED.
https://fastled.io/docs/group___color_fades.html

This code will pick 30 random LEDs.

#define NUM_LEDS 100
const byte numRandomLEDs = 30;
byte randomLEDs[numRandomLEDs];

void setup() {
  Serial.begin(115200);
  randomSeed(analogRead(A0)); // See Arduino reference for randomSeed()
}

void loop() {
  // Fill randomLEDs array with random numbers in the range 0 to NUM_LEDS-1
  byte writeIndex = 0;
  while (writeIndex < numRandomLEDs) {
    randomLEDs[writeIndex] = random(0, NUM_LEDS); // returns 0..NUM_LEDS-1
    if (latestEntryIsNotDuplicate(writeIndex)) writeIndex++;
  }
  // Print the contents of randomLEDs array
  Serial.println("Random LEDs are:");
  for (byte i = 0; i < numRandomLEDs; i++) {
    Serial.print(String(randomLEDs[i]) + " ");
  }
  Serial.println();
  delay(1000); // To slow down writing a new set of LEDs to the serial monitor
}

bool latestEntryIsNotDuplicate(byte writeIndex) {
  for (byte i = 0; i < writeIndex; i++) {
    if (randomLEDs[writeIndex] == randomLEDs[i]) return false;
  }
  return true;
}

It produces output like this on the serial monitor:

Random LEDs are:
75 52 37 57 10 12 36 53 49 19 8 17 7 89 21 50 33 95 25 58 54 67 70 23 42 11 28 6 43 32 
Random LEDs are:
56 96 53 36 84 37 75 85 91 3 58 80 44 66 62 65 83 73 50 19 16 76 7 89 27 87 20 79 30 48 
Random LEDs are:
32 17 82 43 56 57 68 59 76 3 99 92 83 11 37 27 13 71 1 14 2 20 30 58 74 97 95 96 6 31 
Random LEDs are:
70 97 54 43 30 55 64 98 50 22 10 84 83 87 79 95 51 34 1 17 93 31 33 6 91 40 86 44 0 11 

You only need to start at the top and make 30 swaps: the first with whatever is stored in 0-99, the second with whatever is in 1-99, ... the thirtieth with whatever is in 29-99.

Thanks so much for your help Dave! Ive uploaded your code to my Arduino but it doesn't seem to do anything. Is there a piece of code missing? Sorry total newbie here.

Nothing in the Serial monitor ?

What have you got the baud rate set to ?
Not 9600 baud by any chance when it should 115200 baud ?

1 Like

Hi Bob - Ive copied Dave Lowthers code verbatim but probably missing some elements.

If you have copied it verbatim them there will be no missing elements

I assume that you have opened the Serial monitor

To double check, as we all make mistakes from time to time, I copied the code from my post above into a blank sketch. I uploaded it to my Uno R3 clone, and it behaved as expected. As @UKHeliBob said, I can only think that you don't have the serial monitor open and set to 115200 baud.

Thanks Dave. Ive selected the 115200 now (my bad). Seems to work great. How can I get the code to activate my LEDs now?

I don't have any experience of using WS2812B or the FastLED library. It would be better for you to look at @xfpd 's post and ask more questions if that doesn't help enough.

Ok will do - thanks again Dave!!

The FastLED "fade" example lets you group the individual WS2812 in the strand (you were selecting 30 random WS2812 to turn on, then fade, then repeat).

I would encourage your exploration of FastLED examples. If you install the library on your IDE, you will have all those examples, "a click away".

After installing the FastLED library, find the examples here:
IDE >> EXAMPLES >> FastLED >> examples

sure, it's fast but not needed

Yes, that was a good idea.

here is something to simulate your requirements.

click to see the code
 /* ============================================
  code is placed under the MIT license
  Copyright (c) 2024 J-M-L
  For the Arduino Forum : https://forum.arduino.cc/u/j-m-l

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
  ===============================================
*/



#include <FastLED.h>

const byte stripPin = 2;
const byte numLeds = 100;
const byte numChosenLeds = 30;
const CRGB amber = CRGB(255, 191, 0);

CRGB leds[numLeds];
byte indexArray[numLeds];

enum {SHUFFLE, LIGHTUP, WAIT, FADE} state = SHUFFLE;
const unsigned long waitTime = 2000ul; // make it 20000ul for 20s, limiting at 2s for demo
const unsigned long fadeStepTime = 10ul;
unsigned long startTime;

void fisherYatesShuffle() {
  for (size_t i = numLeds - 1; i > 0; --i) {
    size_t r = random(0, i + 1); // generate rand numbers from 0 to i
    size_t tmpSwap = indexArray[i];
    indexArray[i] = indexArray[r];
    indexArray[r] = tmpSwap;
  }
}

void animateStrip() {
  switch (state) {
    case SHUFFLE:
      fisherYatesShuffle();
      state = LIGHTUP;
      break;

    case LIGHTUP:
      fill_solid(leds, numLeds, CRGB::Black); // turn them all off
      for (byte i = 0; i < numChosenLeds; i++)leds[indexArray[i]] = amber; // turn the selected ones amber
      FastLED.show();
      startTime = millis();
      state = WAIT;
      break;

    case WAIT:
      if (millis() - startTime >= waitTime) {
        startTime = millis();
        state = FADE;
      }
      break;

    case FADE:
      if (millis() - startTime >= fadeStepTime) {
        fadeToBlackBy(leds, numLeds, 1);
        FastLED.show();

        if (leds[indexArray[0]] == CRGB::Black) { // we totally faded out
          state = SHUFFLE;
        } else {
          startTime = millis();
        }
      }
      break;
  }
}


void setup() {
  randomSeed(analogRead(A0));
  Serial.begin(115200);
  for (byte i = 0; i < numLeds; i++) indexArray[i] = i;
  FastLED.addLeds<WS2812B, stripPin, GRB>(leds, numLeds);
}

void loop() {
  animateStrip();
  // you can do other stuff here as long as it does not take too long
  // ....
}```

You might benefit from understanding better state machines. Here is a small introduction to the topic: Yet another Finite State Machine introduction

I'm using a state machine for the various stages:

  • randomizing which leds will be selected
  • lighting them up
  • waiting 20s (2s only in the simulation)
  • fading to black

and then start again. This way the animation is not blocking and you could do other (non blocking) stuff if needed in the loop().

1 Like

Here's code&sim for doing a shuffle of M choose N pixels.

// https://wokwi.com/projects/408578815245853697
// for https://forum.arduino.cc/t/create-code-for-a-semi-random-fastled-pattern/1299988

// Modified from https://wokwi.com/projects/374907938100932609

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>  // Required for 16 MHz Adafruit Trinket
#endif

#define PIN_NEO_PIXEL 4  // Arduino pin that connects to NeoPixel
#define NUM_PIXELS 100    // The number of LEDs (pixels) on NeoPixel

#define DELAY_INTERVAL 250  // 250ms pause between each pixel

Adafruit_NeoPixel NeoPixel(NUM_PIXELS, PIN_NEO_PIXEL, NEO_GRB + NEO_KHZ800);

byte pixDeck[NUM_PIXELS]; // 255 max

void setup() {
  NeoPixel.begin();  // INITIALIZE NeoPixel strip object (REQUIRED)
  for (int ii = 0; ii < NUM_PIXELS; ++ii){
    pixDeck[ii] = ii;
  }
}

void randomizePixDeck(int nn) {
  for (int ii = 0; ii < nn; ++ii) {
    int jj = random(ii, NUM_PIXELS);
    int save = pixDeck[ii];
    pixDeck[ii] = pixDeck[jj];
    pixDeck[jj] = save;
  }
}

void loop() {
  randomPixes(7);
}

void randomPixes(int num){
  const uint32_t interval = 2000;
  static uint32_t last = -interval;
  uint32_t now = millis();
  if (now - last >= interval) {
    last = now;
    NeoPixel.clear();
    randomizePixDeck(num);
    for (int jj = 0; jj < num; ++jj) {
      NeoPixel.setPixelColor(pixDeck[jj], NeoPixel.Color(255, 0xbf, 0));
    }
    NeoPixel.show();
  }
}

I chose N=7 here because I can count that high quickly enough.

The fading does not seem fully specified, but modifying this non-blocking pattern to add a (50sec?) fade-in-hold-fade-out pattern shouldn't be too terrible.