Add bounce effect to LED code; Add random repeat

Hi everybody!

I'm asking for help in tweaking existing LED code.
(Code provided at the bottom)

I'm new to Arduino coding. It's pretty intimidating in the beginning, but achieving Halloween special effects for the niblings is great motivation. The code I found is from Tweaking4all which provided a kind of plug-and-play "module" guidance, so I had to learn a lot to properly "assemble" the modular pieces into a fully functional sketch. That's done, but the LED effect isn't yet correct for the intended application.

Here's what I hope someone can help me achieve:
I'm using code for an LED effect called Meteor Rain. The existing code causes a streak of light to traverse from one side of an LED strip to the other. The change I need is to make the streak of light bounce back to the starting point. (Imagine the game of Pong). That's it! The other change I need help with is telling the streak of light to repeat at random time intervals so it looks more organic. (I'm intending the streak of light to be little ghost sprite creatures).

The existing code works fine on an Arduino UNO, and an Arduino MEGA 2560. I'm intending to load up the code onto an ATTINY85 Digispark Board.


The ATTINY85 boards are still in the mail, so I can't test the code on them, yet.

I've gotten to the point of getting the code to work on my own, but modifying code is something I'm not able to do yet, and Halloween is fast approaching. If anyone could help, I (and the niblings) would be thrilled!

Note: I intentionally left in the comments of "REPLACE FROM HERE" and "REPLACE TO HERE" to clearly indicate where the Meteor Rain code exists to help a reader quickly distinguish the Meteor Rain code from the supporting code. Probably not necessary, but maybe it helps.

//For more information, reference https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/


#include "FastLED.h"
#define NUM_LEDS 71 
CRGB leds[NUM_LEDS];
#define PIN 6

void setup()
{
  FastLED.addLeds<WS2813, PIN, GRB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
}

// *** REPLACE FROM HERE ***
void loop() {
  meteorRain(0xff,0xff,0xff,5, 90, true, 20);
}

void meteorRain(byte red, byte green, byte blue, byte meteorSize, byte meteorTrailDecay, boolean meteorRandomDecay, int SpeedDelay) {  
  setAll(0,0,0);
  
  for(int i = 0; i < NUM_LEDS+NUM_LEDS; i++) {
    
    
    // fade brightness all LEDs one step
    for(int j=0; j<NUM_LEDS; j++) {
      if( (!meteorRandomDecay) || (random(10)>5) ) {
        fadeToBlack(j, meteorTrailDecay );        
      }
    }
    
    // draw meteor
    for(int j = 0; j < meteorSize; j++) {
      if( ( i-j <NUM_LEDS) && (i-j>=0) ) {
        setPixel(i-j, red, green, blue);
      } 
    }
   
    showStrip();
    delay(SpeedDelay);
  }
}

void fadeToBlack(int ledNo, byte fadeValue) {
 #ifdef ADAFRUIT_NEOPIXEL_H 
    // NeoPixel
    uint32_t oldColor;
    uint8_t r, g, b;
    int value;
    
    oldColor = strip.getPixelColor(ledNo);
    r = (oldColor & 0x00ff0000UL) >> 16;
    g = (oldColor & 0x0000ff00UL) >> 8;
    b = (oldColor & 0x000000ffUL);

    r=(r<=10)? 0 : (int) r-(r*fadeValue/256);
    g=(g<=10)? 0 : (int) g-(g*fadeValue/256);
    b=(b<=10)? 0 : (int) b-(b*fadeValue/256);
    
    strip.setPixelColor(ledNo, r,g,b);
 #endif
 #ifndef ADAFRUIT_NEOPIXEL_H
   // FastLED
   leds[ledNo].fadeToBlackBy( fadeValue );
 #endif  
}
// *** REPLACE TO HERE ***

void showStrip() {
 #ifdef ADAFRUIT_NEOPIXEL_H 
   // NeoPixel
   strip.show();
 #endif
 #ifndef ADAFRUIT_NEOPIXEL_H
   // FastLED
   FastLED.show();
 #endif
}

void setPixel(int Pixel, byte red, byte green, byte blue) {
 #ifdef ADAFRUIT_NEOPIXEL_H 
   // NeoPixel
   strip.setPixelColor(Pixel, strip.Color(red, green, blue));
 #endif
 #ifndef ADAFRUIT_NEOPIXEL_H 
   // FastLED
   leds[Pixel].r = red;
   leds[Pixel].g = green;
   leds[Pixel].b = blue;
 #endif
}

void setAll(byte red, byte green, byte blue) {
  for(int i = 0; i < NUM_LEDS; i++ ) {
    setPixel(i, red, green, blue); 
  }
  showStrip();
}
1 Like

Welcome to the forums! +1 for using code tags with your code.

As for your problem, people will help you with your code, not write code for you. Since all your code is doing is the meteorRain() function. Here's what I would suggest...

Make a copy of that function and call it something like meteorRainReverse(). Edit the code so the meteor moves from the end of the array towards the front. (Hint: for loops can count up or down). Get that working... Since you are using the FlastLED library, their demo code has a "cyclopes" function that moves back and forth that may help you figure it out.

As for the pause, use the random() function and the delay() function....

If/when you get stuck, post your best effort with a description of what it is doing vs. what you want it to do an people will help.

Good luck!

Wow thank you so much for your suggestions!

I really had no idea where to start, but now I know where to focus my attention. I'm following your suggestions, and am also going through the Arduino tutorial to start building my Arduino coding vocabulary. It seems knowing about functions is key for my task. Thank you, now I have a fighting chance to achieve this by the 31st =D

Welcome

It will be hard to achieve what you want with the actual code, as everything is done with strange loops and delays

Here is an example on wokwi : meteor.ino - Wokwi Arduino Simulator

It looks more complex, but it's not using delays and in my opinion it's easier to understand and modify. You could even make it run multiple meteors at the same time.

2 Likes

OMG this is so amazing! The kids are going to absolutely flip when they see this LED effect emerging from the ground! Thank you so much!

I'm definitely studying this code. I think some of it is already making sense. Particularly the InvertDirection part and the "if" "else" parts. I'm going to continue studying both sketches (both seem to have their own advantages and disadvantages) and use this as an opportunity to really start absorbing how Arduino code works. This is gunna be so great, eeee!

Also, funnily, I was just looking for an Arduino simulator. How perfect that you pointed me to one. Yay!

Just thought I'd give an update for future readers: (If some of my numbers are off, please let me know!)

The Digispark ATTINY85 boards arrived. I tried the new Meteor Rain code, and nothing happened. The boards definitely work, because the "Start" example code successfully blinked an LED. After doing some sleuthing, I discovered that the Digispark boards are limited by only 512 bytes of SRAM, meaning it can only reliably handle about 85 RGB addressable LEDs (even worse for WRGB addressable LEDs, I assume). Each addressable LED consumes 3 bytes of SRAM, so 144 LEDs would need 432 bytes of SRAM, leaving not nearly enough for the programming to run, which caused the Digispark to just sit there and do nothing. So my LED strip of 144 LEDs is too much for the Digispark to handle. I was so worried that the code had to be specially tailored for the stripped down nature of the Digispark, but thankfully not.

Ultimately, because I'd like to use all 144 LEDs on each strip, I've purchased some Arduino Nanos. The 2k of SRAM should be enough, I hope. So far so good, more or less.

The two sources that lead me to my conclusion:

Okay so, another issue cropped up (hopefully the last one). The LED animation slows way down if I use the full length (144 LEDs) of the LED strip, even with my MEGA 2560 supplied with 9 volts. The animation only speeds up to what I need when I reduce the LEDs to around 55 LEDs, even after configuring the existing code to animate the LEDs at maximum speed. From what I've read, the two main causes are:

  1. The 800 KHz Kbs data speed limitation, and method of communication of the addressable LEDs cause the animation to slow directly proportionally to the number of LEDs in play

  2. Efficiencies of the code

The best explanation I found for the code efficiency impacting animation speed is way over my head, but maybe someone can help

// Thank you guix!

#include "FastLED.h"
#define NUM_LEDS 144 
CRGB leds[NUM_LEDS];
#define PIN 6

void setup()
{
  FastLED.addLeds<WS2813, PIN, GRB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
  Serial.begin(115200);
}

void loop()
{
  meteorRain( CRGB(255, 255, 255), 5, 50, 5 );
}

void meteorRain( CRGB color, uint8_t size, uint8_t decayAmount, uint8_t speed )  
{
  uint32_t now = millis();
  static uint32_t past = now;

  if ( now - past >= speed )
  {
    past = now;

    static int16_t pos = 0;
    static bool invertDirection = false;

    if ( invertDirection == false )
    {
      if ( pos >= size )
      {
        for ( int16_t i = 0; i <= pos - size; i++ )
        {
          decay( i, decayAmount );
        }
      }

      if ( pos < NUM_LEDS + size - 1 )
      {
        if ( pos < NUM_LEDS )
        {
          leds[pos] = color;

          for ( int16_t i = pos + 1; i < NUM_LEDS; i++ )
          {
            decay( i, decayAmount );
          }
        }
        pos++;
      }
      else
      {
        pos = NUM_LEDS - 1;
        invertDirection = true;
      }
    }

    else
    {
      if ( pos < NUM_LEDS - size )
      {
        for ( int16_t i = pos + size; i < NUM_LEDS; i++ )
        {
          decay( i, decayAmount );
        }
      }

      if ( pos > -size )
      {
        if ( pos >= 0 )
        {
          leds[pos] = color;

          for ( int16_t i = 0; i < pos; i++ )
          {
            decay( i, decayAmount );
          }
        }
        pos--;
      }
      else
      {
        static uint32_t past = 0;
        static uint32_t restart = 0;

        if ( past == 0 )
        {
          past = now;
          restart = random( 2000, 15000 );
          Serial.print( "Restarting in ");
          Serial.print( restart );
          Serial.println( " ms" );
        }

        if ( now - past >= restart )
        {
          past = 0;
          pos = 0;
          invertDirection = false;
        }
      }
    }

    FastLED.show();
  }
}

void decay( uint16_t id, uint8_t value )
{
  if ( 0 )
  {
    leds[id] = CRGB::Black;
  }
  else if ( random( 2 ) )
  {
    leds[id].fadeToBlackBy( value );
  }
}

Maybe you can cut the 144 LEDs strip into 2, 3 or 4 strips, each strip having their own data pin FastLED/MultipleStripsInOneArray.ino at master · FastLED/FastLED · GitHub

There are other, much faster (and more expensive) adressable LEDs like APA102, HD107..

1 Like

Wow you were right! That works! Dividing the data signal among multiple strips speeds the animation way up! Perfect! OMG this is so great. There's a lot of soldering in my future...

I was wondering if something like that was possible, but I just don't yet have the vocabulary to properly search for that. I wouldn't have thought of including "Array" in my search. You saved the day, again!

After removing the void loop () in the multiple strips code, the sketch functions perfectly!

Also, holy cats! You weren't kidding about the faster LEDs being more expensive. Double the cost, at least. But I'll keep those faster LEDs in mind for my future purchases.

Thank you so much! I'm going to post pics later so you can see what you helped bring to life.

1 Like

I wasn't sure, and my calculation was probably wrong... Glad that it helped :slight_smile:

if RAM becomes a problem again
maybe the Seeeduino XIAO 48 Mhz, 32bit, 256KB Flash,32KB SRAM

is a solution

You can use them just like any arduino. I would recommend to attach a reset-button. Sometimes while uploading I experienced the XIAO to hang not responding.
And then a reset will make him respond again.

best regards Stefan

Holy moly that’s a powerful little board! Okay I’ll keep that in mind, thank you!

The main problem with the seeeduino thing is…

All the I/O pins are 3.3V, please do not input more than 3.3V, otherwise, the CPU may be damaged.

so for your smart pixels, you’d need to do a level translation on the logic line that drives them.

It is a darling and powerful little board. Impossibly inexpensive as well.

a7

Finally a use for my level shifters! :grinning_face_with_smiling_eyes:

1 Like

Well, looks like I'll be trying out a M0 board like the Seeeduino XIAO, after all.

When I further tested the signal splitting code configured for three LED strips, and 48 LEDs each LED strip, the animation slowed down to the same speed as if I were sending the signal to just one LED strip of 144 LEDs. So it seems the speed bottle neck for my animation isn't the LED data speed, but the ATMEGA 2560 data speed. Beans.

Sending 4 sets of 36 LEDs sequentially is no faster than sending 144 LEDs. If you look at the docs for the multiple strips example, they talk about it.

Performance - (NOT YET, but coming) - if you could write out 8 strips in parallel, about...

Ah okay, so dividing up the data signal yields zero improvement in speed (when the LED data speed isn't maxed out?). Sorry, but what docs? I don't see any performance discussions.

I remembered I have a (possibly damaged) spare Feather M4 Express board in storage, so I got that out and tried the code. Sure enough, the M4 board animates the LEDs fast enough (just barely), and at the same speed regardless of if I send the data to one 144 LED strip, or three 48 LED strips. I guess the LED data rate isn't an issue in this case.

I'm not sure which attribute of the M4 board is allowing my animation to run at high speed, which confounds my decision on getting an M0 board or an M4 board. But I have a feeling that if the animation is running just fast enough, the M4 is the bare minimum for this animation on this many LEDs. Ugh :weary: I'll have to save up to get enough of those boards, and just use the slower boards this year.

If a higher price is OK you could use a Teensy 4.0
Teensy 4.0 ARM Cortex-M7 at 600 MHz

not much bigger than a XIAO but much more RAM and more calculation-speed
https://www.pjrc.com/store/teensy40_pins.html
The teensy is really fast I guess the maximum bitbanging speed of the teensy is higher than the maximum busspeed of the LEDs. This means dividing the 144 LEDs in 3 or 4 parallel parts that get served on different IO-pins should make it faster.

The concept of using multiple IO-pins will speed up with any microcontroller.
But I don't know if there is a variant of fastled that can do this.

And there is some overhead for manageing how to move the "switched-on"-LEDs through a shared pattern-buffer

best regards Stefan

As previously mentioned, you can use a SPI-based LED Strip such as APA012 (aka Adafruit DotStar). I've clocked those at 20MHz.

Okay that Teensy 4.0 M7 is so powerful! I love it!

For now, I went with a bunch of ItsyBitsy M4 Express boards to get the job done well enough this time around. I might even try overclocking them a little. But LED strips are only going to get more dense in the future, so I'll have to start investing in M7 boards like the Teensy 4.0 M7 you suggested