The Best Firefly simulator program

Summary - Best Program is by johnwasser, imho.

After deciding on a firefly simulator project to do with my grandson, I experimented with dozens of sketches, modifying tweaking and deciding what looks best. My criteria was random led selection with no "delay" command,and using fading instead of blinking. The best program was submitted by johnwasser ## Karma: 2000+. I just used his code with no modifications. I am making this a new topic because the internet is full of questions and answers about firefly simulation and while I enjoyed my research, there may be someone out there in the future that just wants to download a great program and then just do the diy soldering etc.

anon12459472, in the same link, had a fine program, but I like the fade effect of johnwasser's.

I think this is the correct link to johnwasser's sketch.

Here is a version that uses a list of objects rather than separate lists of pins, states, and timers.

// Firefly

unsigned long CurrentLoopMillis = 0;

class Firefly
{
    const byte LEDPin;
    const unsigned fadeUpRate; // milliseconds per step
    const unsigned fadeDownRate;  // milliseconds per step

    enum states {IDLE, FADE_UP, FADE_DOWN} state;
    unsigned long previousMillis;

  public:
    Firefly(byte pin, unsigned fur = 10, unsigned fdr = 3) :
      LEDPin(pin), fadeUpRate(fur), fadeDownRate(fdr)
    {}

    void begin()
    {
      pinMode(LEDPin, OUTPUT);
    }

    void trigger()
    {
      // Half the time, do nothing
      if (random(2) == 0)
        return;

      // If the LED is already doing something, dont interrupt
      if (state != IDLE)
        return;

      state = FADE_UP;
      previousMillis = millis();
    }

    void update()
    {
      if (state == FADE_UP)
        fadeUp();
      else if (state == FADE_DOWN)
        fadeDown();
    }

    void fadeUp()
    {
      // brightness goes up one step every 10 milliseconds
      unsigned long brightness = (CurrentLoopMillis - previousMillis) / fadeUpRate;

      if (brightness < 256)
        analogWrite(LEDPin, brightness);
      else
      {
        state = FADE_DOWN;
        previousMillis = CurrentLoopMillis;
      }
    }

    void fadeDown()
    {
      // brightness goes down one step every 3 milliseconds
      int brightness = 255 - ((CurrentLoopMillis - previousMillis) / fadeDownRate);

      if (brightness >= 0)
        analogWrite(LEDPin, brightness);
      else
        state = IDLE;
    }
};

const size_t FireflyCount = 6;
Firefly Flies[FireflyCount] = {3, 5, 6, 9, 10, 11};

const unsigned long EventInterval = 1000;

void setup()
{
  for (size_t i = 0; i < FireflyCount; i++)
    Flies[i].begin();
}

void loop()
{
  CurrentLoopMillis = millis();

  // Every second, select a random firefly to blink
  static unsigned long previousTime = 0;
  if (CurrentLoopMillis - previousTime >= EventInterval)
  {
    Flies[random(FireflyCount)].trigger();
  }

  // Update every Firefly every time through loop()
  for (size_t i = 0; i < FireflyCount; i++)
  {
    Flies[i].update();
  }
}

John,
I don't have time right this minute to post 2 videos. I will later if you would like them, one each of your two sketches in action of the leds (Jan '21 and Jan '22).
Jan '21 still remains the best one.

hi,
Out shopping with the wife.
I tried your today 's sketch as I mentioned in the forum. The lights twinkle but don't do the total fade out like your Jan'21 sketch does. I tried adjusting the interval with no success.
I really, really, like your Jan'21 sketch. It is close to perfection.
Opie (Isaac)

That's weird. I must have gotten something wrong but I don't see it. The LED's should fade in over about 2.5 seconds (256 steps at 10 milliseconds per step) and out in about .8 seconds (256 steps at 3 milliseconds per step).

They do exactly what you say.

What I was referring to was fade-out-and-random-off-time.
As it is now they randomly ramp up and ramp down and immediately repeat.

I will send videos in an hour or so. As it is now, my wife prefers the entertainment of Jan'22 Sketch and would use it for an evening patio party. She says that sketch is what fireflies should do.

While I would use Jan'21 Sketch if I wanted to simulate what fireflies actually do.

Sorry for video quality -

Jan '21 Youtube video test of JW's Firefly Sketch as mentioned above:

Jan '22 Youtube video of JW's Firefly Sketch as mentioned above:

I found my mistake.

  // Every second, select a random firefly to blink
  static unsigned long previousTime = 0;
  if (CurrentLoopMillis - previousTime >= EventInterval)
  {
    Flies[random(FireflyCount)].trigger();
  }

Should have been:

  // Every second, select a random firefly to blink
  static unsigned long previousTime = 0;
  if (CurrentLoopMillis - previousTime >= EventInterval)
  {
    previousTime = CurrentLoopMillis;
    Flies[random(FireflyCount)].trigger();
  }

I wasn't restarting the timer so it was starting blinks far too often.

1 Like

OK. Great. The new paragraph of code fixed the problem.

I won't post a new video because now the led action is the same as Jan '21 code.

Since you have posted only the corrected code paragraph above, can you post the entire new updated code Sketch with notation date comment in Sketch heading so that future readers can copy it in one mouse-click?

Great job.
Thanks.
Opie (Isaac)

// Firefly Simulation
// Last update: 26-January-2022

unsigned long CurrentLoopMillis = 0;

class Firefly
{
    const byte LEDPin;
    const unsigned fadeUpRate; // milliseconds per step
    const unsigned fadeDownRate;  // milliseconds per step

    enum states {IDLE, FADE_UP, FADE_DOWN} state;
    unsigned long previousMillis;

    void fadeUp()
    {
      // brightness goes up one step every 10 milliseconds
      unsigned long brightness = (CurrentLoopMillis - previousMillis) / fadeUpRate;

      if (brightness < 256)
        analogWrite(LEDPin, brightness);
      else
      {
        state = FADE_DOWN;
        previousMillis = CurrentLoopMillis;
      }
    }

    void fadeDown()
    {
      // brightness goes down one step every 3 milliseconds
      int brightness = 255 - ((CurrentLoopMillis - previousMillis) / fadeDownRate);

      if (brightness >= 0)
        analogWrite(LEDPin, brightness);
      else
        state = IDLE;
    }

  public:

    Firefly(byte pin, unsigned fur = 10, unsigned fdr = 3) :
      LEDPin(pin), fadeUpRate(fur), fadeDownRate(fdr)
    {}

    void begin()
    {
      pinMode(LEDPin, OUTPUT);
    }

    void trigger()
    {
      // Half the time, do nothing
      if (random(2) == 0)
        return;

      // If the LED is already doing something, dont interrupt
      if (state != IDLE)
        return;

      state = FADE_UP;
      previousMillis = CurrentLoopMillis;
    }

    void update()
    {
      if (state == FADE_UP)
        fadeUp();
      else if (state == FADE_DOWN)
        fadeDown();
    }
};

const size_t FireflyCount = 6;
Firefly Flies[FireflyCount] = {3, 5, 6, 9, 10, 11};

const unsigned long EventInterval = 1000;

void setup()
{
  for (size_t i = 0; i < FireflyCount; i++)
    Flies[i].begin();
}

void loop()
{
  CurrentLoopMillis = millis();

  // Every second, select a random firefly to blink
  static unsigned long previousTime = 0;
  if (CurrentLoopMillis - previousTime >= EventInterval)
  {
    previousTime = CurrentLoopMillis;
    Flies[random(FireflyCount)].trigger();
  }

  // Update every Firefly every time through loop()
  for (size_t i = 0; i < FireflyCount; i++)
  {
    Flies[i].update();
  }
}

John, and others that are interested:

I was investigating increasing the qty to 10 LEDs. I saw the article here:

and bought the WS2811 string of 50 LEDs, 5 volts.
I cut off 10 of them and strung them on a three-wire 40 ft outdoor cable. Since every led was addressable, I only needed a single cable for all LEDs.
But the Sutter sketch was not "perfect" in my judgment compared to J Wasser's.
So I tweaked the parameters so that the WS2811 string performed almost equally to Wasser's.
So now I have two methods for my grandson's project. If I want to go for a long string I will use the WS2811 5v string (10 WS2811 LEDs, cut separately and soldered along the 40 feet cable. ) If I am only needing to cover a small area or one bush outside I will use cheaper individual LEDs and Wasser's program. Perfection!!

I'll try to upload program next.
Opie

Here is the revised program from Instructables project

/* Firefly animation
 *  9/3/18-9/1/19 Carl F Sutter
 *  WS2811 5 volt style Strand connected to Arduino Pin 6
 *  Uses the Adafruit Neopixel Library
 */
 // https://www.instructables.com/No-solder-Fireflies-Lightning-Bugs/
 // revision notes by Shorenicehere/Opie: Feb 2022: R and G are switched colors for some reason, so grb, not rgb
 //See note on line 45 for GRB case for WS2811
 //Also modify line 49 for different lights
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

// Neopixel constants
#define PIN 6        // Arduino pin for the LED strand data line
#define NUMLEDS 10  // number of LEDs total

// App constants
// The RGB values for the LED color 0-255  :Opie note: I had to use grb instead
#define COLOR_R 30  // so this is actually green, WS2811 strands do this mix up I guess
#define COLOR_G 0   // I tried but could not get a green-yellow I liked, so I just used green,  dim at 30.
#define COLOR_B 0

// loop control - these are the main constants to control the animation
// note that the math is in integers right now, so if the step size is greater than the color values, it will not work well
#define NUM_STEPS_UP    20    // the number of steps to fade the color up: Sutter had the values 5, 30, 10, 5 for these four values
#define NUM_STEPS_DOWN  20   // the number of steps to fade the color down: I had to tweak these values to make them match JW's perfect fireflies sketch
#define LOOP_DELAY      20   // the ms delay for each loop
#define BLINK_ODDS      15    // the odds in 10,000 that the loop will start each black pixel

// internal constants and vars - no need to change
#define STATE_BLACK          0
#define STATE_MOVING_UP      1
#define STATE_MOVING_DOWN    2

uint8_t state[NUMLEDS]; 
uint8_t color_r[NUMLEDS]; 
uint8_t color_g[NUMLEDS]; 
uint8_t color_b[NUMLEDS]; 

// Parameter 1 = number of pixels in strip
// Parameter 2 = Arduino pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMLEDS, PIN, NEO_GRB + NEO_KHZ800);

// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across
// pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input
// and minimize distance between Arduino and first pixel.  Avoid connecting
// on a live circuit...if you must, connect GND first.


void setup() {
  // set all the LEDS to the no blink (black) state
  for(int i=0; i<NUMLEDS; i++) {
    state[i] = STATE_BLACK;
    color_r[i] = 0;
    color_g[i] = 0;
    color_b[i] = 0;
  }
  
  // randomize the random number generator
  randomSeed(analogRead(0));  

  // setup the LED strand
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
} // setup


void loop() {
  // step through the LEDS, and update the colors as needed
  for (int i=0; i<NUMLEDS; i++) {
    switch (state[i]) {
      case STATE_BLACK:
        strip.setPixelColor(i,  0x000000);
        if (random(10000) < BLINK_ODDS) state[i] = STATE_MOVING_UP;
        break;

      case STATE_MOVING_UP:
         color_r[i] = min(COLOR_R, color_r[i] + (COLOR_R / NUM_STEPS_UP));
         color_g[i] = min(COLOR_G, color_g[i] + (COLOR_G / NUM_STEPS_UP));
         color_b[i] = min(COLOR_B, color_b[i] + (COLOR_B / NUM_STEPS_UP));
         strip.setPixelColor(i, color_r[i], color_g[i], color_b[i]);
         if ((color_r[i] == COLOR_R) && (color_g[i] == COLOR_G) && (color_b[i] == COLOR_B)) {
           state[i] = STATE_MOVING_DOWN;
         }
        break;

      case STATE_MOVING_DOWN:
         color_r[i] = max(0, color_r[i] - max(1, (COLOR_R / NUM_STEPS_DOWN)));
         color_g[i] = max(0, color_g[i] - max(1, (COLOR_G / NUM_STEPS_DOWN)));
         color_b[i] = max(0, color_b[i] - max(1, (COLOR_B / NUM_STEPS_DOWN)));
         strip.setPixelColor(i, color_r[i], color_g[i], color_b[i]);
         if ((color_r[i] == 0) && (color_g[i] == 0) && (color_b[i] == 0)) {
           state[i] = STATE_BLACK;
         }
        break;
        
      } // switch
    } // led loop
    
    strip.show();
    delay(LOOP_DELAY);
 } // loop

 

Wow what an amazing thread, I have put this into my prox diy projects now.
I will come back when it's done to show results

Wiring for expanded WS2811 scheme, if you choose this method, see pic.

https://ibb.co/qWQM5ZT

A further observation.
I have two methods described above, the second being using addressable pixels such as the WS2811 5v string of 10. The first was using regular LEDs with Wasser's program, in a qty of 6 (PWM.)

After having used both in my yard and fabricating the cables and wires I have come to a conclusion:

  1. WS2811 (or WS2812) are overkill for this Firefly application.
  2. I turned to the Neopixels because of the wiring issues of outdoor stringing.
  3. But, either way, using regular LEDs or Neopixels, the wiring is ugly, but works fine.
  4. The 6 LED limitation with regular LEDs is just fine for Firelies.

So, I am returning to the Wasser program, using 6 LEDs in the cabling method in the picture below. I have two made and have several more to make for gifts for the summer.

https://ibb.co/mb49hXL

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.