-
-
Save embedded-creations/00dfedaad7b94cb603ce5aabe1a90ee9 to your computer and use it in GitHub Desktop.
Twinkling 'holiday' lights that fade in and out.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "FastLED.h" | |
#define LED_PIN 3 | |
#define LED_TYPE WS2811 | |
#define COLOR_ORDER RGB | |
#define NUM_LEDS 50 | |
CRGB leds[NUM_LEDS]; | |
// Twinkling 'holiday' lights that fade up and down in brightness. | |
// Colors are chosen from a palette; a few palettes are provided. | |
// | |
// The basic operation is that all pixels stay black until they | |
// are 'seeded' with a relatively dim color. The dim colors | |
// are repeatedly brightened until they reach full brightness, then | |
// are darkened repeatedly until they are fully black again. | |
// | |
// A set of 'directionFlags' is used to track whether a given | |
// pixel is presently brightening up or darkening down. | |
// | |
// For illustration purposes, two implementations of directionFlags | |
// are provided: a simple one-byte-per-pixel flag, and a more | |
// complicated, more compact one-BIT-per-pixel flag. | |
// | |
// Darkening colors accurately is relatively easy: scale down the | |
// existing color channel values. Brightening colors is a bit more | |
// error prone, as there's some loss of precision. If your colors | |
// aren't coming our 'right' at full brightness, try increasing the | |
// STARTING_BRIGHTNESS value. | |
// | |
// -Mark Kriegsman, December 2014 | |
#define MASTER_BRIGHTNESS 96 | |
#define STARTING_BRIGHTNESS 64 | |
#define FADE_IN_SPEED 32 | |
#define FADE_OUT_SPEED 20 | |
#define DENSITY 255 | |
void setup() { | |
delay(3000); | |
FastLED.addLeds<LED_TYPE,LED_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip); | |
FastLED.setBrightness(MASTER_BRIGHTNESS); | |
} | |
void loop() | |
{ | |
chooseColorPalette(); | |
colortwinkles(); | |
FastLED.show(); | |
FastLED.delay(20); | |
} | |
CRGBPalette16 gPalette; | |
//#define APPLY_FIX | |
// Gradient palette "es_vintage_01_gp", originally from | |
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/vintage/tn/es_vintage_01.png.index.html | |
// converted for FastLED with gammas (2.6, 2.2, 2.5) | |
// Size: 32 bytes of program space. | |
const byte es_vintage_01_gp[] PROGMEM = { | |
0, 4, 1, 1, | |
51, 16, 0, 1, | |
76, 97,104, 3, | |
101, 255,131, 19, | |
127, 67, 9, 4, | |
153, 16, 0, 1, | |
229, 4, 1, 1, | |
255, 4, 1, 1}; | |
void load_gradient_palette(const byte* palette) | |
{ | |
byte tcp[72]; //support gradient palettes with up to 18 entries | |
memcpy_P(tcp, (byte*)pgm_read_dword(&palette), 72); | |
gPalette.loadDynamicGradientPalette(tcp); | |
} | |
void chooseColorPalette() | |
{ | |
uint8_t numberOfPalettes = 6; | |
uint8_t secondsPerPalette = 10; | |
uint8_t whichPalette = (millis()/(1000*secondsPerPalette)) % numberOfPalettes; | |
CRGB r(CRGB::Red), b(CRGB::Blue), w(85,85,85), g(CRGB::Green), W(CRGB::White), l(0xE1A024); | |
switch( whichPalette) { | |
case 0: // Vintage | |
load_gradient_palette(es_vintage_01_gp); | |
break; | |
case 1: // Red, Green, and White | |
gPalette = CRGBPalette16( r,r,r,r, r,r,r,r, g,g,g,g, w,w,w,w ); | |
break; | |
case 2: // Blue and White | |
//gPalette = CRGBPalette16( b,b,b,b, b,b,b,b, w,w,w,w, w,w,w,w ); | |
gPalette = CloudColors_p; // Blues and whites! | |
break; | |
case 3: // Rainbow of colors | |
gPalette = RainbowColors_p; | |
break; | |
case 4: // Incandescent "fairy lights" | |
gPalette = CRGBPalette16( l,l,l,l, l,l,l,l, l,l,l,l, l,l,l,l ); | |
break; | |
case 5: // Snow | |
gPalette = CRGBPalette16( W,W,W,W, w,w,w,w, w,w,w,w, w,w,w,w ); | |
break; | |
} | |
} | |
enum { GETTING_DARKER = 0, GETTING_BRIGHTER = 1 }; | |
void colortwinkles() | |
{ | |
// Make each pixel brighter or darker, depending on | |
// its 'direction' flag. | |
brightenOrDarkenEachPixel( FADE_IN_SPEED, FADE_OUT_SPEED); | |
// Now consider adding a new random twinkle | |
if( random8() < DENSITY ) { | |
int pos = random16(NUM_LEDS); | |
if( !leds[pos]) { | |
leds[pos] = ColorFromPalette( gPalette, random8(), STARTING_BRIGHTNESS, NOBLEND); | |
setPixelDirection(pos, GETTING_BRIGHTER); | |
} | |
} | |
} | |
void brightenOrDarkenEachPixel( fract8 fadeUpAmount, fract8 fadeDownAmount) | |
{ | |
for( uint16_t i = 0; i < NUM_LEDS; i++) { | |
if( getPixelDirection(i) == GETTING_DARKER) { | |
// This pixel is getting darker | |
leds[i] = makeDarker( leds[i], fadeDownAmount); | |
} else { | |
// This pixel is getting brighter | |
leds[i] = makeBrighter( leds[i], fadeUpAmount); | |
// now check to see if we've maxxed out the brightness | |
if( leds[i].r == 255 || leds[i].g == 255 || leds[i].b == 255) { | |
// if so, turn around and start getting darker | |
setPixelDirection(i, GETTING_DARKER); | |
} | |
} | |
} | |
} | |
CRGB makeBrighter( const CRGB& color, fract8 howMuchBrighter) | |
{ | |
CRGB incrementalColor = color; | |
incrementalColor.nscale8( howMuchBrighter); | |
#ifdef APPLY_FIX | |
if(!incrementalColor.r && !incrementalColor.g && !incrementalColor.b) | |
incrementalColor = color; | |
#endif | |
return color + incrementalColor; | |
} | |
CRGB makeDarker( const CRGB& color, fract8 howMuchDarker) | |
{ | |
CRGB newcolor = color; | |
newcolor.nscale8( 255 - howMuchDarker); | |
return newcolor; | |
} | |
// For illustration purposes, there are two separate implementations | |
// provided here for the array of 'directionFlags': | |
// - a simple one, which uses one byte (8 bits) of RAM for each pixel, and | |
// - a compact one, which uses just one BIT of RAM for each pixel. | |
// Set this to 1 or 8 to select which implementation | |
// of directionFlags is used. 1=more compact, 8=simpler. | |
#define BITS_PER_DIRECTION_FLAG 1 | |
#if BITS_PER_DIRECTION_FLAG == 8 | |
// Simple implementation of the directionFlags array, | |
// which takes up one byte (eight bits) per pixel. | |
uint8_t directionFlags[NUM_LEDS]; | |
bool getPixelDirection( uint16_t i) { | |
return directionFlags[i]; | |
} | |
void setPixelDirection( uint16_t i, bool dir) { | |
directionFlags[i] = dir; | |
} | |
#endif | |
#if BITS_PER_DIRECTION_FLAG == 1 | |
// Compact (but more complicated) implementation of | |
// the directionFlags array, using just one BIT of RAM | |
// per pixel. This requires a bunch of bit wrangling, | |
// but conserves precious RAM. The cost is a few | |
// cycles and about 100 bytes of flash program memory. | |
uint8_t directionFlags[ (NUM_LEDS+7) / 8]; | |
bool getPixelDirection( uint16_t i) { | |
uint16_t index = i / 8; | |
uint8_t bitNum = i & 0x07; | |
// using Arduino 'bitRead' function; expanded code below | |
return bitRead( directionFlags[index], bitNum); | |
// uint8_t andMask = 1 << bitNum; | |
// return (directionFlags[index] & andMask) != 0; | |
} | |
void setPixelDirection( uint16_t i, bool dir) { | |
uint16_t index = i / 8; | |
uint8_t bitNum = i & 0x07; | |
// using Arduino 'bitWrite' function; expanded code below | |
bitWrite( directionFlags[index], bitNum, dir); | |
// uint8_t orMask = 1 << bitNum; | |
// uint8_t andMask = 255 - orMask; | |
// uint8_t value = directionFlags[index] & andMask; | |
// if( dir ) { | |
// value += orMask; | |
// } | |
// directionFlags[index] = value; | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This adds a palette which breaks the sketch, but there's a fix if you define
APPLY_FIX
Credit to fix to WLED (or WS2812FX) developers, found here (
mode_colortwinkle()
): https://github.com/Aircoookie/WLED/blob/master/wled00/FX.cpp#L2012