#include <FastLED.h>
#define LED_PIN 5
#define NUM_LEDS 50
#define BRIGHTNESS 64
#define LED_TYPE WS2811
#define COLOR_ORDER GRB
#define UPDATES_PER_SECOND 100
bool firstTime = true;
// This example shows several ways to set up and use 'palettes' of colors
// with FastLED.
//
// These compact palettes provide an easy way to re-colorize your
// animation on the fly, quickly, easily, and with low overhead.
//
// USING palettes is MUCH simpler in practice than in theory, so first just
// run this sketch, and watch the pretty lights as you then read through
// the code. Although this sketch has eight (or more) different color schemes,
// the entire sketch compiles down to about 6.5K on AVR.
//
// FastLED provides a few pre-configured color palettes, and makes it
// extremely easy to make up your own color schemes with palettes.
//
// Some notes on the more abstract 'theory and practice' of
// FastLED compact palettes are at the bottom of this file.
CRGBPalette16 currentPalette;
TBlendType currentBlending;
extern CRGBPalette16 myRedWhiteBluePalette;
extern const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM;
#define NUM_STRIPS 4
#define NUM_LEDS 60
#define COLOR_ORDER GRB
#define CHIPSET WS2813
#define FRAMES_PER_SECOND 80
#define MAX_BRIGHTNESS 255
//LED EFFECTS
bool gReverseDirection = false;
CRGB leds[NUM_STRIPS][NUM_LEDS];
typedef struct ballsConfigType{
CRGB bgColor;
uint8_t bgBrightness;
uint8_t brightness;
CRGB color;
CRGBPalette16 *palette;
uint8_t paletteIndex;
float gravity;
uint8_t randomNewBall;
uint8_t maxTotalBalls;
uint8_t maxStripBalls;
}ballsConfigType;
CRGBPalette16 rainbowPalette = Rainbow_gp;
ballsConfigType ballsConfig = {{0,0,0}, 128, 255, {0,0,0}, &rainbowPalette, 0, -9.81, 8, 3, 1};
//ballsConfig = {{0,0,0}, 128, 255, {0,0,0}, &rainbowPalette, 0, -9.81, 127, 20, 3};
#define MAX_STRIP_BALLS 5 //Maximun of balls per strip ever, ballsConfig max per strip config should be equal or lower
float StartHeight = 2.5;
bool activeBalls[NUM_STRIPS][MAX_STRIP_BALLS];
CRGB ballsColor[NUM_STRIPS][MAX_STRIP_BALLS];
float Height[NUM_STRIPS][MAX_STRIP_BALLS];
float ImpactVelocity[NUM_STRIPS][MAX_STRIP_BALLS];
double TimeSinceLastBounce[NUM_STRIPS][MAX_STRIP_BALLS];
int Position[NUM_STRIPS][MAX_STRIP_BALLS];
long ClockTimeSinceLastBounce[NUM_STRIPS][MAX_STRIP_BALLS];
float Dampening[NUM_STRIPS][MAX_STRIP_BALLS];
int8_t totalActiveBalls;
void setup() {
Serial.begin(115200);
FastLED.addLeds<CHIPSET,5, COLOR_ORDER>(leds[0], NUM_LEDS).setCorrection( UncorrectedColor );
pinMode(12, OUTPUT);
FastLED.addLeds<CHIPSET,6, COLOR_ORDER>(leds[1], NUM_LEDS).setCorrection( UncorrectedColor );
pinMode(27, OUTPUT);
FastLED.addLeds<CHIPSET,7, COLOR_ORDER>(leds[2], NUM_LEDS).setCorrection( UncorrectedColor );
pinMode(25, OUTPUT);
FastLED.addLeds<CHIPSET,8, COLOR_ORDER>(leds[3], NUM_LEDS).setCorrection( UncorrectedColor );
pinMode(32, OUTPUT);
FastLED.addLeds<CHIPSET,9, COLOR_ORDER>(leds[4], NUM_LEDS).setCorrection( UncorrectedColor );
currentPalette = RainbowColors_p;
currentBlending = LINEARBLEND;
}
void fill_all_strips(CRGB color, int numToFill=NUM_LEDS){
for(uint8_t s=0; s<NUM_STRIPS; s++){
fill_solid(leds[s], numToFill, color);
}
}
void fadeAllToBlackBy(uint8_t fadeBy){
for(uint8_t s=0; s<NUM_STRIPS; s++){
fadeToBlackBy(leds[s], NUM_LEDS, fadeBy);
}
}
CRGB applyBrightnessToRGB(CRGB color, uint8_t brightness){
return color.nscale8(brightness);
if( brightness != 255) {
if( brightness ) {
++brightness; // adjust for rounding
// Now, since brightness is nonzero, we don't need the full scale8_video logic;
// we can just to scale8 and then add one (unless scale8 fixed) to all nonzero inputs.
if( color.red ) {
color.red = scale8_LEAVING_R1_DIRTY( color.red, brightness);
#if !(FASTLED_SCALE8_FIXED==1)
++red1;
#endif
}
if( color.green ) {
color.green = scale8_LEAVING_R1_DIRTY( color.green, brightness);
#if !(FASTLED_SCALE8_FIXED==1)
++green1;
#endif
}
if( color.blue ) {
color.blue = scale8_LEAVING_R1_DIRTY( color.blue, brightness);
#if !(FASTLED_SCALE8_FIXED==1)
++blue1;
#endif
}
cleanup_R1();
} else {
color.red = 0;
color.green = 0;
color.blue = 0;
}
}
return color;
}
void initBuncingBallsEffect(){
FastLED.setBrightness(MAX_BRIGHTNESS);
fill_all_strips(applyBrightnessToRGB(ballsConfig.bgColor, ballsConfig.bgBrightness));
}
CRGB getBallColor(){
uint8_t colorindex = random8();
return ColorFromPalette( *ballsConfig.palette, colorindex, ballsConfig.brightness, LINEARBLEND);
}
void addNewBall(){
if(totalActiveBalls >= ballsConfig.maxTotalBalls){
return;
}
uint8_t ballStrip = random(NUM_STRIPS);
uint8_t a;
for(a=0; a<ballsConfig.maxStripBalls; a++){
if(!activeBalls[ballStrip][a]){
break;
}
}
if(a>=ballsConfig.maxStripBalls){
return;
}
totalActiveBalls++;
activeBalls[ballStrip][a] = true;
ballsColor[ballStrip][a] = getBallColor();
ClockTimeSinceLastBounce[ballStrip][a] = millis();
Height[ballStrip][a] = StartHeight;
Position[ballStrip][a] = 0;
ImpactVelocity[ballStrip][a] = sqrt( -2 * ballsConfig.gravity * StartHeight );
TimeSinceLastBounce[ballStrip][a] = 0;
Dampening[ballStrip][a] = (80 + 10/*random(20)*/)/100.0f;
}
void loop()
{
if(firstTime){
initBuncingBallsEffect();
}
//Add new ball
if(random8() <= ballsConfig.randomNewBall){
addNewBall();
}
fill_all_strips(applyBrightnessToRGB(ballsConfig.bgColor, ballsConfig.bgBrightness));
for(uint8_t s=0; s<NUM_STRIPS; s++){
for(uint8_t a=0; a<MAX_STRIP_BALLS;a++){
//Skip inactive balls
if(!activeBalls[s][a]){
continue;
}
//TimeSinceLastBounce[s][a] = (millis() - ClockTimeSinceLastBounce[s][a]);
//Height[s][a] = 0.5 * ballsConfig.gravity * pow(TimeSinceLastBounce[s][a]/1000, 2.0 ) + ImpactVelocity[s][a] * TimeSinceLastBounce[s][a]/1000;
TimeSinceLastBounce[s][a] = (millis() - ClockTimeSinceLastBounce[s][a])/1000.0f;
Height[s][a] = 0.5 * ballsConfig.gravity * pow(TimeSinceLastBounce[s][a], 2.0 ) + ImpactVelocity[s][a] * TimeSinceLastBounce[s][a];
Serial.print(s); Serial.print(':');
Serial.print(a); Serial.print(':');
Serial.print(TimeSinceLastBounce[s][a]); Serial.print(':');
Serial.print(Height[s][a]); Serial.println(':');
if ( Height[s][a] < 0 ) {
Height[s][a] = 0;
ImpactVelocity[s][a] = Dampening[s][a] * ImpactVelocity[s][a];
ClockTimeSinceLastBounce[s][a] = millis();
if ( ImpactVelocity[s][a] < 0.05 ) {
activeBalls[s][a] = false;
totalActiveBalls--;
continue;
}
}
Position[s][a] = round(Height[s][a] * (NUM_LEDS - 1) / StartHeight);
leds[s][Position[s][a]] = ballsColor[s][a];
}
}
FastLED.show();
FastLED.delay(1000 / FRAMES_PER_SECOND);
//FastLED.delay(200);
firstTime = false;
}
// There are several different palettes of colors demonstrated here.
//
// FastLED provides several 'preset' palettes: RainbowColors_p, RainbowStripeColors_p,
// OceanColors_p, CloudColors_p, LavaColors_p, ForestColors_p, and PartyColors_p.
//
// Additionally, you can manually define your own color palettes, or you can write
// code that creates color palettes on the fly. All are shown here.
void ChangePalettePeriodically()
{
uint8_t secondHand = (millis() / 1000) % 60;
static uint8_t lastSecond = 99;
if( lastSecond != secondHand) {
lastSecond = secondHand;
if( secondHand == 0) { currentPalette = RainbowColors_p; currentBlending = LINEARBLEND; }
if( secondHand == 10) { currentPalette = RainbowStripeColors_p; currentBlending = NOBLEND; }
if( secondHand == 15) { currentPalette = RainbowStripeColors_p; currentBlending = LINEARBLEND; }
if( secondHand == 20) { SetupPurpleAndGreenPalette(); currentBlending = LINEARBLEND; }
if( secondHand == 25) { SetupTotallyRandomPalette(); currentBlending = LINEARBLEND; }
if( secondHand == 30) { SetupBlackAndWhiteStripedPalette(); currentBlending = NOBLEND; }
if( secondHand == 35) { SetupBlackAndWhiteStripedPalette(); currentBlending = LINEARBLEND; }
if( secondHand == 40) { currentPalette = CloudColors_p; currentBlending = LINEARBLEND; }
if( secondHand == 45) { currentPalette = PartyColors_p; currentBlending = LINEARBLEND; }
if( secondHand == 50) { currentPalette = myRedWhiteBluePalette_p; currentBlending = NOBLEND; }
if( secondHand == 55) { currentPalette = myRedWhiteBluePalette_p; currentBlending = LINEARBLEND; }
}
}
// This function fills the palette with totally random colors.
void SetupTotallyRandomPalette()
{
for( int i = 0; i < 16; i++) {
currentPalette[i] = CHSV( random8(), 255, random8());
}
}
// This function sets up a palette of black and white stripes,
// using code. Since the palette is effectively an array of
// sixteen CRGB colors, the various fill_* functions can be used
// to set them up.
void SetupBlackAndWhiteStripedPalette()
{
// 'black out' all 16 palette entries...
fill_solid( currentPalette, 16, CRGB::Black);
// and set every fourth one to white.
currentPalette[0] = CRGB::White;
currentPalette[4] = CRGB::White;
currentPalette[8] = CRGB::White;
currentPalette[12] = CRGB::White;
}
// This function sets up a palette of purple and green stripes.
void SetupPurpleAndGreenPalette()
{
CRGB purple = CHSV( HUE_PURPLE, 255, 255);
CRGB green = CHSV( HUE_GREEN, 255, 255);
CRGB black = CRGB::Black;
currentPalette = CRGBPalette16(
green, green, black, black,
purple, purple, black, black,
green, green, black, black,
purple, purple, black, black );
}
// This example shows how to set up a static color palette
// which is stored in PROGMEM (flash), which is almost always more
// plentiful than RAM. A static PROGMEM palette like this
// takes up 64 bytes of flash.
const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM =
{
CRGB::Red,
CRGB::Gray, // 'white' is too bright compared to red and blue
CRGB::Blue,
CRGB::Black,
CRGB::Red,
CRGB::Gray,
CRGB::Blue,
CRGB::Black,
CRGB::Red,
CRGB::Red,
CRGB::Gray,
CRGB::Gray,
CRGB::Blue,
CRGB::Blue,
CRGB::Black,
CRGB::Black
};
// Additional notes on FastLED compact palettes:
//
// Normally, in computer graphics, the palette (or "color lookup table")
// has 256 entries, each containing a specific 24-bit RGB color. You can then
// index into the color palette using a simple 8-bit (one byte) value.
// A 256-entry color palette takes up 768 bytes of RAM, which on Arduino
// is quite possibly "too many" bytes.
//
// FastLED does offer traditional 256-element palettes, for setups that
// can afford the 768-byte cost in RAM.
//
// However, FastLED also offers a compact alternative. FastLED offers
// palettes that store 16 distinct entries, but can be accessed AS IF
// they actually have 256 entries; this is accomplished by interpolating
// between the 16 explicit entries to create fifteen intermediate palette
// entries between each pair.
//
// So for example, if you set the first two explicit entries of a compact
// palette to Green (0,255,0) and Blue (0,0,255), and then retrieved
// the first sixteen entries from the virtual palette (of 256), you'd get
// Green, followed by a smooth gradient from green-to-blue, and then Blue.