Skip to content

Instantly share code, notes, and snippets.

@hsiboy
Created January 9, 2015 16:39
Show Gist options
  • Save hsiboy/f9ef711418b40e259b06 to your computer and use it in GitHub Desktop.
Save hsiboy/f9ef711418b40e259b06 to your computer and use it in GitHub Desktop.
FUNCTION-BASED ANIMATION SEQUENCES FOR WS2812 LED STRIPS Using FastLED library - Author: Dave Morris: http://www.google.com/+DaveMorris128
// FUNCTION-BASED ANIMATION SEQUENCES FOR WS2812 LED STRIPS
// Using FastLED library
// Author: Dave Morris: http://www.google.com/+DaveMorris128
// Version 1.0 (2014-07-31)
//
//
// The following code includes "primitive animations" which are the base effect and
// "aggregate animations" which are combinations of one or more primitive animations
// Feel free to combine different primitives each loop for synergistic results but:
// -If using an aggregate animation make sure your primatives don't clear the buffer each frame (FastLED.clear())
// otherwise one animation will clear out any upstream animations before the loop sends the frame (FastLED.show())
// This code is designed to handle multiple LED strips, each with its own animation....
// e.g.
// void loop()
// {
// Ring(stripA, frame, 30);
// Spark(stripB, frame, 245);
// FastLED.show();
// frame += animateSpeed;
// }
#include "FastLED.h"
#define LED_COUNT 60 //Length of your LED strip
#define MAX_INT_VALUE 65536
#define LED_PIN 13 // hardware SPI pin SCK
#define NUM_LEDS 250
#define COLOR_ORDER RGB
#define LED_TYPE WS2811
#define MAX_BRIGHTNESS 255 // watch the power!
CRGB strip[NUM_LEDS];
uint16_t frame = 0; //I think I might be able to move this variable to the void loop() scope and save some CPU
uint16_t animateSpeed = 100; //Number of frames to increment per loop
uint8_t animation = 10; //Active animation
uint8_t brightness = 50; //Global brightness percentage
void setup()
{
LEDS.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
FastLED.setBrightness(MAX_BRIGHTNESS);
FastLED.clear();
}
void loop()
{
//Each animation adjusts the "targetStrip" specified in its parameter.
//Animations are a function of the current animation frame "frame"
//Once you've applied the animation to the current strip/frame, Its up to the main loop to send the data to the strip(s)
switch(animation)
{
case 0:
RingPair(strip, frame);
break;
case 1:
DoubleChaser(strip,frame);
break;
case 2:
TripleBounce(strip,frame);
break;
case 3:
WaveInt(strip,frame,180);
break;
case 4:
Wave(strip,frame,180);
break;
case 5: //Blue spark (Slow)
Spark(strip,frame,255,188); //Overloaded version of "Spark" with Hue value, 255 for fade is the slowest fade possible. 256 is on/off
delay(2); //Slow things down a bit more for Slow Spark
break;
case 6: //Blue spark (fast)
Spark(strip,frame,246,188); //Overloaded version of "Spark" with Hue value, 246 fade is faster which makes for a sharper dropoff
break;
case 7: //White spark (Slow)
Spark(strip,frame,255); //"Spark" function without hue make a white spark, 255 for fade is the slowest fade possible.
delay(2); //Slow things down a bit more for Slow Spark
break;
case 8: //White spark (fast) //"Spark" function without hue make a white spark, 246 fade is faster which makes for a sharper dropoff
Spark(strip,frame,245);
break;
case 9:
RainbowSpark(strip,frame,240); //240 for dropoff is a pretty sharp fade, good for this animation
break;
default:
delay(100); //Animation OFF
}
FastLED.show(); //All animations are applied!..send the results to the strip(s)
frame += animateSpeed;
}
//#######################################################################################################
//## AGGREGATE ANIMATIONS ##
//#######################################################################################################
void TripleBounce(CRGB strip[], uint16_t frame) //3 chaser animations offset by 120 degrees each
{
FastLED.clear(); //Clear previous buffer
Bounce(strip,frame,0);
Bounce(strip,frame+(MAX_INT_VALUE/3),100);
Bounce(strip,frame+(MAX_INT_VALUE/3)*2,150);
}
void DoubleChaser(CRGB strip[], uint16_t frame) //2 chaser animations offset 180 degrees
{
FastLED.clear(); //Clear previous buffer
frame = frame * 2;
Ring(strip, frame, 0);
Ring(strip, frame + (MAX_INT_VALUE / 2), 150);
}
void RingPair(CRGB strip[], uint16_t frame) //2 rings animations at inverse phases
{
FastLED.clear(); //Clear previous buffer
Ring(strip, frame, 30);
Ring(strip, MAX_INT_VALUE - frame, 150);
}
void RainbowSpark(CRGB targetStrip[], uint16_t animationFrame,uint8_t fade){ //Color spark where hue is function of frame
Spark(targetStrip,animationFrame,fade,animationFrame/255);
delay(20);
}
//#######################################################################################################
//## PRIMATIVE ANIMATION FUNCTIONS ##
//#######################################################################################################
//********************* Bounce ***************************
// Linear "Larson scanner" (or knight rider effect) with anti-aliasing
// Color is determined by "hue"
//*****************************************************************
void Bounce(CRGB targetStrip[], uint16_t animationFrame, uint8_t hue)
{
uint16_t pos16;
if (animationFrame < (MAX_INT_VALUE / 2))
{
pos16 = animationFrame * 2;
}else{
pos16 = MAX_INT_VALUE - ((animationFrame - (MAX_INT_VALUE/2))*2);
}
int position = map(pos16, 0, MAX_INT_VALUE, 0, ((LED_COUNT) * 16));
drawFractionalBar(targetStrip, position, 3, hue,0);
}
//************************ Ring ******************************
// Anti-aliased cyclical chaser, 3 pixels wide
// Color is determined by "hue"
//*****************************************************
void Ring(CRGB targetStrip[], uint16_t animationFrame, uint8_t hue)
{
uint8_t stripLength = sizeof(strip)/sizeof(CRGB);
int pos16 = map(animationFrame, 0, MAX_INT_VALUE, 0, ((stripLength) * 16));
drawFractionalBar(targetStrip, pos16, 3, hue,1);
}
//*************************** Wave [Float Math] *******************************
// Squeezed sine wave
// Uses slow, Arduino sin() function
// Squeezing achieved by using an exponential (^8) sin value
// Color is determined by "hue"
//***********************************************************************************
void Wave(CRGB targetStrip[], uint16_t animationFrame, uint8_t hue){
FastLED.clear(); //Clear previous buffer
float deg;
float value;
uint8_t stripLength = sizeof(strip)/sizeof(CRGB);
for(uint8_t i=0;i<stripLength;i++)
{
deg=float(animationFrame+((MAX_INT_VALUE/stripLength)*i))/(float(MAX_INT_VALUE)) * 360.0;
value = pow(sin(radians(deg)),8); //Squeeeeeeze
if(value>=0){ //Chop sine wave (no negative values)
targetStrip[i] += CHSV(hue,255,value*256);
}
}
}
//*************************** Wave [Integer Math] *******************************
// unadulterated sine wave.
// Uses FastLED sin16() and no float math for efficiency.
// Since im stuck with integer values, exponential wave-forming is not possible (unless i'm wrong???)
// Color is determined by "hue"
//***********************************************************************************
void WaveInt(CRGB targetStrip[], uint16_t animationFrame, uint8_t hue){
FastLED.clear();
uint8_t stripLength = sizeof(strip)/sizeof(CRGB);
uint8_t value;
for(uint8_t i=0;i<stripLength;i++)
{
value=(sin16(animationFrame+((MAX_INT_VALUE/stripLength)*i)) + (MAX_INT_VALUE/2))/256;
if(value>=0){
targetStrip[i] += CHSV(hue,255,value);
}
}
}
//******************************** Color Spark ***********************************
// Color of the sparks is determined by "hue"
// Frequency of sparks is determined by global var "animateSpeed"
// "animateSpeed" var contrained from 1 - 255 (0.4% - 100%)
// "fade" parameter specifies dropoff (next frame brightness = current frame brightness * (x/256)
// fade = 256 means no dropoff, pixels are on or off
// NOTE: this animation doesnt clear the previous buffer because the fade/dropoff is a function of the previous LED state
//***********************************************************************************
void Spark(CRGB targetStrip[], uint16_t animationFrame,uint8_t fade, uint8_t hue){
uint8_t stripLength = sizeof(strip)/sizeof(CRGB);
uint16_t rand = random16();
for(int i=0;i<stripLength;i++)
{
targetStrip[i].nscale8(fade);
}
if(rand < (MAX_INT_VALUE / (256 - (constrain(animateSpeed,1,256))))) ;
{
targetStrip[rand % stripLength].setHSV(hue,255,255);
}
}
//****************************** Spark **********************************
// Same as color spark but no hue value, // in HSV white is any hue with 0 saturation
// Frequency of sparks is a percentage mapped to global var "animateSpeed"
// "animateSpeed" var contrained from 1 - 255 (0.4% - 100%)
// "fade" parameter specifies dropoff (next frame brightness = current frame brightness * (x/256)
// fade = 256 means no dropoff, pixels are on or off
// NOTE: this animation doesnt clear the previous buffer because the fade/dropoff is a function of the previous LED state
//***********************************************************************************
void Spark(CRGB targetStrip[], uint16_t animationFrame,uint8_t fade){
uint8_t stripLength = sizeof(strip)/sizeof(CRGB);
uint16_t rand = random16();
for(int i=0;i<stripLength;i++)
{
targetStrip[i].nscale8(fade);
}
if(rand < (MAX_INT_VALUE / (256 - (constrain(animateSpeed,1,255)))))
{
targetStrip[rand % stripLength].setHSV(0,0,255);
}
}
//Anti-aliasing code care of Mark Kriegsman Google+: https://plus.google.com/112916219338292742137/posts/2VYNQgD38Pw
void drawFractionalBar(CRGB targetStrip[], int pos16, int width, uint8_t hue, bool wrap)
{
uint8_t stripLength = sizeof(strip)/sizeof(CRGB);
uint8_t i = pos16 / 16; // convert from pos to raw pixel number
uint8_t frac = pos16 & 0x0F; // extract the 'factional' part of the position
uint8_t firstpixelbrightness = 255 - (frac * 16);
uint8_t lastpixelbrightness = 255 - firstpixelbrightness;
uint8_t bright;
for (int n = 0; n <= width; n++) {
if (n == 0) {
// first pixel in the bar
bright = firstpixelbrightness;
}
else if (n == width) {
// last pixel in the bar
bright = lastpixelbrightness;
}
else {
// middle pixels
bright = 255;
}
targetStrip[i] += CHSV(hue, 255, bright );
i++;
if (i == stripLength)
{
if (wrap == 1) {
i = 0; // wrap around
}
else{
return;
}
}
}
}
@gandarufu
Copy link

Thanks for your sketch! I would really like to get this to work, but your code has several issues.
First of all in the setup "leds" should be "strip"
LEDS.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);

And as far as adding multiple strips goes, it doesn't work the way you intended it to. For all functions at the bottom you pass along a targetStrip[], however at the beginning of each function you have
uint8_t stripLength = sizeof(strip)/sizeof(CRGB);

Thus your functions are all tied to the one CRGB array called "strip", or did I get this wrong?
Your code does not work with multiple strips, like you suggested
Ring(stripA, frame, 30);
Spark(stripB, frame, 245);

Or am I just interpreting something wrong? Would appredicate your input on this :)

@ayhanbaris
Copy link

nice.

@hsiboy
Copy link
Author

hsiboy commented Nov 10, 2019

This isn't my code, I copied it here for safekeeping.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment