Page 1 of 1

How to double the bouncy ball effect?

How to double the bouncy ball effect?

Welcome to the Tweaking4All community forums!
When participating, please keep the Forum Rules in mind!

Topics for particular software or systems: Start your topic link with the name of the application or system.
Examples: "MacOS X - Your question", "MS Word - Your Tip or Trick".

Please note that switching to another language when reading a post will not work!
Posts will not have a translated counterpart.




RSS Feed

Home Forums Hardware Arduino How to double the bouncy ball effect?

This topic contains 32 replies, has 2 voices, and was last updated by  ohno 8 months, 1 week ago.

Viewing 15 posts - 1 through 15 (of 33 total)
  • Author
    Posts
  • 7803

    ohno
    Participant

    Just wondering if it is possible to have bouncy balls on both ends of the LED strip simultaneously, instead of just one side?

    What I am trying to do:
    The front of my house has a roofline like this    ^
    I have 300 ws2812b leds connected into one long strip along that roofline.
    It would look cool to have the green bouncy balls on both sides, shooting up toward the roof peak for St Patricks day!

    I can figure out how to do one side, and then the other, thanks to the other posts on this forum.  How could I do both ends simultaneously?

    7807

    hans
    Keymaster

    Hi Ohno!

    Ehm … I guess the question is; do they need to run identical? I mean; symmetric?
    In that case I’d use two strips, one for each side, and set try if both strips can be connected in parallel. Now granted, I never tested this, so it might not work, but I suspect it actually might work.

    If they do not need to be symmetric, then I’d consider getting an additional Arduino. They are pretty cheap these days.
    They could share the same Power Supply … So you’d keep things easy for just a few extra bucks.

    7811

    ohno
    Participant

    First thing, thanks for posting your advanced code.  Also, for giving support for its use.  You are a scholar and a gentleman.

    Your idea is good, keeping it simple.  That would work perfectly to solve my problem, since I do not need the effects to be symmetrical on each side and I have the hardware required.

    But here is why that solution doesn’t quite work for me:

    1. winter in Canada- not a great time for roof work (the *lowest* point of my roof is 16′ from the ground)

    2. I would have to cut my string into two pieces and reverse one string.  This would ruin the other effects, especially cyloning and shooting stars, which are cool to see on 32′ roof line.

    I don’t mean to complain!  Just saying that the KISS principle wont work for me on this project.  I will make a video tonight of the green multicolored bouncy balls- it is a work in progress.

    Is there a way I can get bouncy balls on both ends of one 300 LED, 32′ long string without having to cut apart my permanent installation?  The code you wrote is way above my head already, but I can do some basic editing to make effects run.

    7813

    hans
    Keymaster

    Hi Ohno!

    You’re most welcome, and I like to share the knowledge – I truly wish I could make it my day-time job.
    Having lived in Wisconsin for 13 years (which I actually loved), I can totally relate to not wanting to do this in winter.

    So, point well taken. Let me quickly look at the code and see if I can make it bounce in 2 halves…

    7815

    hans
    Keymaster

    Would you mind if both sides are symmetrical? If you wouldn’t mind then this might work (I have not been able to test this);

    void loop() {
      byte colors[3][3] = { {0xff, 0,0}, 
                            {0xff, 0xff, 0xff}, 
                            {0 , 0 , 0xff} };
      BouncingColoredBalls(3, colors);
    }
    void BouncingColoredBalls(int BallCount, byte colors[][3]) {
      float Gravity = -9.81;
      int StartHeight = 1;
      
      int Halfway = NUM_LEDS/2; // determine number of leds per side
      
      float Height[BallCount];
      float ImpactVelocityStart = sqrt( -2 * Gravity * StartHeight );
      float ImpactVelocity[BallCount];
      float TimeSinceLastBounce[BallCount];
      int Position[BallCount];
      long ClockTimeSinceLastBounce[BallCount];
      float Dampening[BallCount];
      
      for (int i = 0 ; i < BallCount ; i++) {   
        ClockTimeSinceLastBounce[i] = millis();
        Height[i] = StartHeight;
        Position[i] = 0; 
        ImpactVelocity[i] = ImpactVelocityStart;
        TimeSinceLastBounce[i] = 0;
        Dampening[i] = 0.90 - float(i)/pow(BallCount,2); 
      }
      while (true) {
        for (int i = 0 ; i < BallCount ; i++) {
          TimeSinceLastBounce[i] = millis() - ClockTimeSinceLastBounce[i];
          Height[i] = 0.5 * Gravity * pow( TimeSinceLastBounce[i]/1000 , 2.0 ) + ImpactVelocity[i] * TimeSinceLastBounce[i]/1000;
      
          if ( Height[i] < 0 ) {                      
            Height[i] = 0;
            ImpactVelocity[i] = Dampening[i] * ImpactVelocity[i];
            ClockTimeSinceLastBounce[i] = millis();
      
            if ( ImpactVelocity[i] < 0.01 ) {
              ImpactVelocity[i] = ImpactVelocityStart;
            }
          }
          Position[i] = round( Height[i] * (Halfway - 1) / StartHeight); // One side
        }
      
        for (int i = 0 ; i < BallCount ; i++) {
          setPixel(Position[i],colors[i][0],colors[i][1],colors[i][2]);
          setPixel(NUM_LEDS-Position[i],colors[i][0],colors[i][1],colors[i][2]); // mirror to other side
        }
        
        showStrip();
        setAll(0,0,0);
      }
    }

    The idea is to split the entire strip in two virtual strips. One strip counts from 0 to the middle (Halfway variable). The second strip counts from the last LED up to the middle, say we have 10 LEDs, then this would look like this:

    1,2,3,4,5 (middle) 6,7,8,9,10

    now becomes:

    1,2,3,4,5 (middle) 5,4,3,2,1

    So the position of any ball can only exist (initially) in the left half, so we bounce between 0 and Halfway.
    The right side will bounce identical, so we just copy the position of the ball on the left side and just make sure that we start counting from the last LED back to the middle (NUM_LEDS-Position[i]).

    Not sure if this is an acceptable answer … 
    However, when we’d make a function to make both side work individually/independent, then we’d need to repeat a lot of the code and I’d probably need to start experimenting to see how well this would work. But I do like the idea …

    7817

    hans
    Keymaster

    I couldn’t resist, so I did test this slightly modified code.
    Mind you the definition of the HALFLED in the beginning, the principle is the same as the previous code – I just pasted the entire code this time;

    #include "FastLED.h"
    #define NUM_LEDS 60 // Total strip length
    #define HALFLEDS 30 // One half LED count
    CRGB leds[NUM_LEDS];
    #define PIN 6 
    void setup()
    {
      FastLED.addLeds<WS2811, PIN, GRB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
    }
    // *** REPLACE FROM HERE ***
    void loop() {
      byte colors[3][3] = { {0xff, 0,0}, 
                            {0xff, 0xff, 0xff}, 
                            {0 , 0 , 0xff} };
      BouncingColoredBalls(3, colors);
    }
    void BouncingColoredBalls(int BallCount, byte colors[][3]) {
      float Gravity = -9.81;
      int StartHeight = 1;
      
      float Height[BallCount];
      float ImpactVelocityStart = sqrt( -2 * Gravity * StartHeight );
      float ImpactVelocity[BallCount];
      float TimeSinceLastBounce[BallCount];
      int Position[BallCount];
      long ClockTimeSinceLastBounce[BallCount];
      float Dampening[BallCount];
      
      for (int i = 0 ; i < BallCount ; i++) {   
        ClockTimeSinceLastBounce[i] = millis();
        Height[i] = StartHeight;
        Position[i] = 0; 
        ImpactVelocity[i] = ImpactVelocityStart;
        TimeSinceLastBounce[i] = 0;
        Dampening[i] = 0.90 - float(i)/pow(BallCount,2); 
      }
      while (true) {
        for (int i = 0 ; i < BallCount ; i++) {
          TimeSinceLastBounce[i] = millis() - ClockTimeSinceLastBounce[i];
          Height[i] = 0.5 * Gravity * pow( TimeSinceLastBounce[i]/1000 , 2.0 ) + ImpactVelocity[i] * TimeSinceLastBounce[i]/1000;
      
          if ( Height[i] < 0 ) {                      
            Height[i] = 0;
            ImpactVelocity[i] = Dampening[i] * ImpactVelocity[i];
            ClockTimeSinceLastBounce[i] = millis();
      
            if ( ImpactVelocity[i] < 0.01 ) {
              ImpactVelocity[i] = ImpactVelocityStart;
            }
          }
          Position[i] = round( Height[i] * (HALFLEDS - 1) / StartHeight);
        }
      
        for (int i = 0 ; i < BallCount ; i++) {
          setPixel(Position[i],colors[i][0],colors[i][1],colors[i][2]);
          setPixel(NUM_LEDS-Position[i]-1,colors[i][0],colors[i][1],colors[i][2]);
        }
        
        showStrip();
        setAll(0,0,0);
      }
    }
    // *** 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();
    }
    7819

    ohno
    Participant

    Exciting!  I wish I could see my LEDs in the daytime because I will post the video as soon as mother nature allows me to make it.  Your code using two halves is exactly what I was hoping.  I have the Arduino and pc case that powers everything in a kitchen cabinet and I run 25′ of wire for the data line and I inject power from the pc case at both ends of the 32′ led string.  The leds are mounted in aluminum profiles with acrylic milky covers.  Works really well and I don’t have to go outside to change my outdoor led show.  This is the video that made me buy LED’s in the first place:  https://www.youtube.com/watch?v=z5dfpe_-Lgg

    I also built some Arduino controlled relays so I can turn on and off outdoor xmas effects.  So far I have only used it to blink my outdoor LED trees in sequence at xmas.  I only run my led show on holidays.

    And you probably could do this full time if you started up a business.  I am always coming up with ideas, lol.  What I was thinking was installing LEDs on to businesses so they can run holiday or other theme shows.  It is a nicer way to make ppl look than those ungodly bright LED signs everyone is putting up in my city.  With your coding skills I am sure you can make impressive results.  I saw you did a boblight setup on an 80″ tv, I did an ambilight setup on an 80″ tv also with apa102 since the colors on the ws2812b are so far off.  I used 4 pieces of electrical conduit to mount the LEDs on, and I can rotate them to aim the LEDs. 

    7823

    hans
    Keymaster

    That inspirational video is mind blowing …. WOW.

    Well, doing lighting as a fulltime business would work just fine in the US and Canada, but probably not so much in Europe. There will be the occasional client, but not as much as out here (I’m from the Netherlands, lived 13 years in Wisconsin, and right now live in Houston for my job). It kind-a reminds me of the first houses that had an iPad installed to control lighting, music, alarm, etc. Pretty quickly these companies had to compete with cheap Chinese knock-offs. Granted they do not work as well, but … it’s a customer lost.

    I like your video! Nice start! I’ll try to download and convert it so I can permanently embed it here (so it won’t use your DropBox account that much and you can safely remove it later on) – with your permission of course. 

    I do see what you mean with the gaps. Let me take a look and see what the best point would be to make things smoother.

    7825

    hans
    Keymaster

    Did you try changing “float Gravity = -9.81;” to another value?

    I’m beginning to think that it goes a little too fast or that the one-led balls are too small …

    7827

    hans
    Keymaster

    OK, I managed to reduce your video to 4 Mb with HandBrake.
    Just wondering; how did you get the video in the post to begin with hahah … (I assume you just pasted the link?)
    I’ve always used attachments, but I like embedding better.

    7842

    hans
    Keymaster

    For some odd reason I lost your post, while trying to post the video (the 4Mb version):

    So here your text again:

    Hey I’ve got it installed and it is working on both ends! I have a video uploading to my HTPC, 40mb. (edit: reduced to 4Mb with HandBrake, and hosted at Tweaking4All.com).  

    Looks like something is wrong, there are a bunch of gaps between the LEDs. Like the balls are wiffle balls, if you remember those? Maybe it is because to move that fast, some of the LEDs are skipped. eg… instead of the ball going from 1,2,3,4,5,6 it goes 1,3,6 and skips over some LEDs. At the end of the video, I zoomed in so if you pause you can see the gaps. My cousin is coming over for UFC tonight, he has java programing experience.  

    If we figure it out I’ll post the mod. I am guessing that changing the gravity on line 21 may make it run smoother.  

     float Gravity = -9.81;

    7844

    hans
    Keymaster

    Java experience should most certainly help – since Java is very similar to C/C++. 
    I’m curious what he comes up with!

    An alternative might be by using 2 or 3 LEDs as “ball” size, for example the 1e and 3e LED the same color as led number 2 , just more dimmed. You’d have replace the for-loop with for example:

       for (int i = 0 ; i < BallCount ; i++) {
          // Leftside, for each ball, LED 1-2-3
          if(Position[i]-1>=0) { setPixel(Position[i]-1,colors[i][0],colors[i][1],colors[i][2]); }
          setPixel(Position[i],colors[i][0],colors[i][1],colors[i][2]);
          if(Position[i]+1<HALFLEDS) { setPixel(Position[i]+1,colors[i][0],colors[i][1],colors[i][2]); } // Rightside, for each ball, LED 1-2-3
          if(Position[i]-2>=0) { setPixel(NUM_LEDS-Position[i]-2,colors[i][0],colors[i][1],colors[i][2]); }
          setPixel(NUM_LEDS-Position[i]-1,colors[i][0],colors[i][1],colors[i][2]);
          if(Position[i]<HALFLEDS) { setPixel(NUM_LEDS-Position[i],colors[i][0],colors[i][1],colors[i][2]); }
        }

    I tested this and it works as expected. I’m trying to see if I can dim LED 1 and 3 of each ball. This might look better on your house too – much more visible.

    As for the Gravity value:
    The closer to zero (for example -1) the slower it gets. But that will not be as nice.

    Also think about it that in the same time frame, a LED can bounce all the way or just a little bit, limiting the number of lit up LEDs for long distance bounces.

    7846

    hans
    Keymaster

    OK, managed to get faded first and third LED for each ball to work as well,example code below.

    To be clear, the previous example and this example replace this part of the code you tested:

        for (int i = 0 ; i < BallCount ; i++) {
          setPixel(Position[i],colors[i][0],colors[i][1],colors[i][2]);
          setPixel(NUM_LEDS-Position[i]-1,colors[i][0],colors[i][1],colors[i][2]);
        }

    Replace it with (if you’d like I can post the entire code):

        for (int i = 0 ; i < BallCount ; i++) {
          // Leftside; LEDs 1,2,3 for each ball
          if(Position[i]-1>=0) { 
            setPixel(Position[i]-1,colors[i][0],colors[i][1],colors[i][2]); 
            leds[Position[i]-1].fadeLightBy( 220 ); 
          }
          setPixel(Position[i],colors[i][0],colors[i][1],colors[i][2]);
          if(Position[i]+1<HALFLEDS) { 
            setPixel(Position[i]+1,colors[i][0],colors[i][1],colors[i][2]); 
            leds[Position[i]+1].fadeLightBy( 220 ); 
          }
          // Rightside; LEDs 1,2,3 for each ball
          if(Position[i]-2>=0) { 
            setPixel(NUM_LEDS-Position[i]-2,colors[i][0],colors[i][1],colors[i][2]);
            leds[NUM_LEDS-Position[i]-2].fadeLightBy( 220 ); 
          }
          setPixel(NUM_LEDS-Position[i]-1,colors[i][0],colors[i][1],colors[i][2]);
          if(Position[i]<HALFLEDS) { 
            setPixel(NUM_LEDS-Position[i],colors[i][0],colors[i][1],colors[i][2]); 
            leds[NUM_LEDS-Position[i]].fadeLightBy( 220 );
          }
    7848

    hans
    Keymaster

    OK, so I could really not resist and made you this code, where I created a function to draw a ball, any width you’d like.

    Attachments:
    7851

    hans
    Keymaster

    Woops, pressed ENTER too fast.

    So the idea is: a ball in the original bouncing balls code is 1 LED.
    To make it more visible, I added a function “DrawBall”, which draws an individual ball at a given location.
    The position on the LED strand is the center of the ball.

    We additionally pass it the ball colors (red, green, blue), how much the additional LEDs should dim in brightness, and the amount of extra LEDs we’d like to add left and right of the original center LED.

    void DrawBall(int ballPosition, byte red, byte green, byte blue, byte dimming, byte ExtraSidePixelCount);

    For an example of the picture in the previous post;

    DrawBall(Position[i],colors[i][0],colors[i][1],colors[i][2], 220, 2);

    1) The ball is drawn with “Position[i]” as the center.
    2) We use the red, green and blue values “colors[i][0],colors[i][1],colors[i][2]“.
    3) All LEDs, except the center LED, will be dimmed by 220, which seemed like a nice value – but feel free to play with it. As far as I know, 250 will most likely make the LED go OFF.
    4) We add 2 LEDs on the left and on the right of the center LED.

    If I’d want to make it totally perfect then I gradually dim the extra LEDs, instead of taking just one value, but while testing I kind-a already liked it this way.

    Hope this results in a better effect – I assume you’d try with adding 2 LEDs as in the code below, but you could most certainly try more.

    Please note that this will only work with the FastLED library since we need to have the fadeLightBy function:

    The full code:

    #include "FastLED.h"
    #define NUM_LEDS 60 // Total strip length
    #define HALFLEDS 30 // One half LED count
    CRGB leds[NUM_LEDS];
    #define PIN 6 
    void setup()
    {
      FastLED.addLeds<WS2811, PIN, GRB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
    }
    // *** REPLACE FROM HERE ***
    void loop() {
      byte colors[3][3] = { {0xff, 0,0}, 
                            {0xff, 0xff, 0xff}, 
                            {0 , 0 , 0xff} };
      BouncingColoredBalls(3, colors);
    }
    void BouncingColoredBalls(int BallCount, byte colors[][3]) {
      float Gravity = -9.81;
      int StartHeight = 1;
      
      float Height[BallCount];
      float ImpactVelocityStart = sqrt( -2 * Gravity * StartHeight );
      float ImpactVelocity[BallCount];
      float TimeSinceLastBounce[BallCount];
      int Position[BallCount];
      long ClockTimeSinceLastBounce[BallCount];
      float Dampening[BallCount];
      
      for (int i = 0 ; i < BallCount ; i++) {   
        ClockTimeSinceLastBounce[i] = millis();
        Height[i] = StartHeight;
        Position[i] = 0; 
        ImpactVelocity[i] = ImpactVelocityStart;
        TimeSinceLastBounce[i] = 0;
        Dampening[i] = 0.90 - float(i)/pow(BallCount,2); 
      }
      while (true) {
        for (int i = 0 ; i < BallCount ; i++) {
          TimeSinceLastBounce[i] = millis() - ClockTimeSinceLastBounce[i];
          Height[i] = 0.5 * Gravity * pow( TimeSinceLastBounce[i]/1000 , 2.0 ) + ImpactVelocity[i] * TimeSinceLastBounce[i]/1000;
      
          if ( Height[i] < 0 ) {                      
            Height[i] = 0;
            ImpactVelocity[i] = Dampening[i] * ImpactVelocity[i];
            ClockTimeSinceLastBounce[i] = millis();
      
            if ( ImpactVelocity[i] < 0.01 ) {
              ImpactVelocity[i] = ImpactVelocityStart;
            }
          }
          Position[i] = round( Height[i] * (HALFLEDS - 1) / StartHeight);
        }
      
        for (int i = 0 ; i < BallCount ; i++) {
          // Leftside; (chnage 220 for dimming, and 2 for number of extra LEDs on each side)
          DrawBall(Position[i],colors[i][0],colors[i][1],colors[i][2], 220, 2);
          // Rightside;
          DrawBall(NUM_LEDS-Position[i]-1,colors[i][0],colors[i][1],colors[i][2], 220, 2);
        }
        
        showStrip();
        setAll(0,0,0);
      }
    }
    void DrawBall(int ballPosition, byte red, byte green, byte blue, byte dimming, byte ExtraSidePixelCount) {
      // LEDs before the center LED
      for(int counter=1; counter<=ExtraSidePixelCount; counter++) {
        if(ballPosition-counter>=0) { 
          setPixel(ballPosition-counter,red,green,blue); 
          leds[ballPosition-counter].fadeLightBy( dimming ); 
        }
      }
      
      // Center LED    
      setPixel(ballPosition,red,green,blue);
      
      // LEDs after the center LED
      for(int counter=1; counter<=ExtraSidePixelCount; counter++) {    
        if(ballPosition+counter<HALFLEDS) { 
          setPixel(ballPosition+counter,red,green,blue); 
          leds[ballPosition+counter].fadeLightBy( dimming ); 
        }
      }
    }
    // *** 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();
    }
Viewing 15 posts - 1 through 15 (of 33 total)



You must be logged in to reply to this topic.