-
-
Save kriegsman/756ea6dcae8e30845b5a to your computer and use it in GitHub Desktop.
| #include "FastLED.h" | |
| #if defined(FASTLED_VERSION) && (FASTLED_VERSION < 3001000) | |
| #warning "Requires FastLED 3.1 or later; check github for latest code." | |
| #endif | |
| #define NUM_LEDS 100 | |
| #define LED_TYPE WS2811 | |
| #define COLOR_ORDER GRB | |
| #define DATA_PIN 3 | |
| //#define CLK_PIN 4 | |
| #define VOLTS 12 | |
| #define MAX_MA 4000 | |
| // TwinkleFOX: Twinkling 'holiday' lights that fade in and out. | |
| // Colors are chosen from a palette; a few palettes are provided. | |
| // | |
| // This December 2015 implementation improves on the December 2014 version | |
| // in several ways: | |
| // - smoother fading, compatible with any colors and any palettes | |
| // - easier control of twinkle speed and twinkle density | |
| // - supports an optional 'background color' | |
| // - takes even less RAM: zero RAM overhead per pixel | |
| // - illustrates a couple of interesting techniques (uh oh...) | |
| // | |
| // The idea behind this (new) implementation is that there's one | |
| // basic, repeating pattern that each pixel follows like a waveform: | |
| // The brightness rises from 0..255 and then falls back down to 0. | |
| // The brightness at any given point in time can be determined as | |
| // as a function of time, for example: | |
| // brightness = sine( time ); // a sine wave of brightness over time | |
| // | |
| // So the way this implementation works is that every pixel follows | |
| // the exact same wave function over time. In this particular case, | |
| // I chose a sawtooth triangle wave (triwave8) rather than a sine wave, | |
| // but the idea is the same: brightness = triwave8( time ). | |
| // | |
| // Of course, if all the pixels used the exact same wave form, and | |
| // if they all used the exact same 'clock' for their 'time base', all | |
| // the pixels would brighten and dim at once -- which does not look | |
| // like twinkling at all. | |
| // | |
| // So to achieve random-looking twinkling, each pixel is given a | |
| // slightly different 'clock' signal. Some of the clocks run faster, | |
| // some run slower, and each 'clock' also has a random offset from zero. | |
| // The net result is that the 'clocks' for all the pixels are always out | |
| // of sync from each other, producing a nice random distribution | |
| // of twinkles. | |
| // | |
| // The 'clock speed adjustment' and 'time offset' for each pixel | |
| // are generated randomly. One (normal) approach to implementing that | |
| // would be to randomly generate the clock parameters for each pixel | |
| // at startup, and store them in some arrays. However, that consumes | |
| // a great deal of precious RAM, and it turns out to be totally | |
| // unnessary! If the random number generate is 'seeded' with the | |
| // same starting value every time, it will generate the same sequence | |
| // of values every time. So the clock adjustment parameters for each | |
| // pixel are 'stored' in a pseudo-random number generator! The PRNG | |
| // is reset, and then the first numbers out of it are the clock | |
| // adjustment parameters for the first pixel, the second numbers out | |
| // of it are the parameters for the second pixel, and so on. | |
| // In this way, we can 'store' a stable sequence of thousands of | |
| // random clock adjustment parameters in literally two bytes of RAM. | |
| // | |
| // There's a little bit of fixed-point math involved in applying the | |
| // clock speed adjustments, which are expressed in eighths. Each pixel's | |
| // clock speed ranges from 8/8ths of the system clock (i.e. 1x) to | |
| // 23/8ths of the system clock (i.e. nearly 3x). | |
| // | |
| // On a basic Arduino Uno or Leonardo, this code can twinkle 300+ pixels | |
| // smoothly at over 50 updates per seond. | |
| // | |
| // -Mark Kriegsman, December 2015 | |
| CRGBArray<NUM_LEDS> leds; | |
| // Overall twinkle speed. | |
| // 0 (VERY slow) to 8 (VERY fast). | |
| // 4, 5, and 6 are recommended, default is 4. | |
| #define TWINKLE_SPEED 4 | |
| // Overall twinkle density. | |
| // 0 (NONE lit) to 8 (ALL lit at once). | |
| // Default is 5. | |
| #define TWINKLE_DENSITY 5 | |
| // How often to change color palettes. | |
| #define SECONDS_PER_PALETTE 30 | |
| // Also: toward the bottom of the file is an array | |
| // called "ActivePaletteList" which controls which color | |
| // palettes are used; you can add or remove color palettes | |
| // from there freely. | |
| // Background color for 'unlit' pixels | |
| // Can be set to CRGB::Black if desired. | |
| CRGB gBackgroundColor = CRGB::Black; | |
| // Example of dim incandescent fairy light background color | |
| // CRGB gBackgroundColor = CRGB(CRGB::FairyLight).nscale8_video(16); | |
| // If AUTO_SELECT_BACKGROUND_COLOR is set to 1, | |
| // then for any palette where the first two entries | |
| // are the same, a dimmed version of that color will | |
| // automatically be used as the background color. | |
| #define AUTO_SELECT_BACKGROUND_COLOR 0 | |
| // If COOL_LIKE_INCANDESCENT is set to 1, colors will | |
| // fade out slighted 'reddened', similar to how | |
| // incandescent bulbs change color as they get dim down. | |
| #define COOL_LIKE_INCANDESCENT 1 | |
| CRGBPalette16 gCurrentPalette; | |
| CRGBPalette16 gTargetPalette; | |
| void setup() { | |
| delay( 3000 ); //safety startup delay | |
| FastLED.setMaxPowerInVoltsAndMilliamps( VOLTS, MAX_MA); | |
| FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS) | |
| .setCorrection(TypicalLEDStrip); | |
| chooseNextColorPalette(gTargetPalette); | |
| } | |
| void loop() | |
| { | |
| EVERY_N_SECONDS( SECONDS_PER_PALETTE ) { | |
| chooseNextColorPalette( gTargetPalette ); | |
| } | |
| EVERY_N_MILLISECONDS( 10 ) { | |
| nblendPaletteTowardPalette( gCurrentPalette, gTargetPalette, 12); | |
| } | |
| drawTwinkles( leds); | |
| FastLED.show(); | |
| } | |
| // This function loops over each pixel, calculates the | |
| // adjusted 'clock' that this pixel should use, and calls | |
| // "CalculateOneTwinkle" on each pixel. It then displays | |
| // either the twinkle color of the background color, | |
| // whichever is brighter. | |
| void drawTwinkles( CRGBSet& L) | |
| { | |
| // "PRNG16" is the pseudorandom number generator | |
| // It MUST be reset to the same starting value each time | |
| // this function is called, so that the sequence of 'random' | |
| // numbers that it generates is (paradoxically) stable. | |
| uint16_t PRNG16 = 11337; | |
| uint32_t clock32 = millis(); | |
| // Set up the background color, "bg". | |
| // if AUTO_SELECT_BACKGROUND_COLOR == 1, and the first two colors of | |
| // the current palette are identical, then a deeply faded version of | |
| // that color is used for the background color | |
| CRGB bg; | |
| if( (AUTO_SELECT_BACKGROUND_COLOR == 1) && | |
| (gCurrentPalette[0] == gCurrentPalette[1] )) { | |
| bg = gCurrentPalette[0]; | |
| uint8_t bglight = bg.getAverageLight(); | |
| if( bglight > 64) { | |
| bg.nscale8_video( 16); // very bright, so scale to 1/16th | |
| } else if( bglight > 16) { | |
| bg.nscale8_video( 64); // not that bright, so scale to 1/4th | |
| } else { | |
| bg.nscale8_video( 86); // dim, scale to 1/3rd. | |
| } | |
| } else { | |
| bg = gBackgroundColor; // just use the explicitly defined background color | |
| } | |
| uint8_t backgroundBrightness = bg.getAverageLight(); | |
| for( CRGB& pixel: L) { | |
| PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number | |
| uint16_t myclockoffset16= PRNG16; // use that number as clock offset | |
| PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number | |
| // use that number as clock speed adjustment factor (in 8ths, from 8/8ths to 23/8ths) | |
| uint8_t myspeedmultiplierQ5_3 = ((((PRNG16 & 0xFF)>>4) + (PRNG16 & 0x0F)) & 0x0F) + 0x08; | |
| uint32_t myclock30 = (uint32_t)((clock32 * myspeedmultiplierQ5_3) >> 3) + myclockoffset16; | |
| uint8_t myunique8 = PRNG16 >> 8; // get 'salt' value for this pixel | |
| // We now have the adjusted 'clock' for this pixel, now we call | |
| // the function that computes what color the pixel should be based | |
| // on the "brightness = f( time )" idea. | |
| CRGB c = computeOneTwinkle( myclock30, myunique8); | |
| uint8_t cbright = c.getAverageLight(); | |
| int16_t deltabright = cbright - backgroundBrightness; | |
| if( deltabright >= 32 || (!bg)) { | |
| // If the new pixel is significantly brighter than the background color, | |
| // use the new color. | |
| pixel = c; | |
| } else if( deltabright > 0 ) { | |
| // If the new pixel is just slightly brighter than the background color, | |
| // mix a blend of the new color and the background color | |
| pixel = blend( bg, c, deltabright * 8); | |
| } else { | |
| // if the new pixel is not at all brighter than the background color, | |
| // just use the background color. | |
| pixel = bg; | |
| } | |
| } | |
| } | |
| // This function takes a time in pseudo-milliseconds, | |
| // figures out brightness = f( time ), and also hue = f( time ) | |
| // The 'low digits' of the millisecond time are used as | |
| // input to the brightness wave function. | |
| // The 'high digits' are used to select a color, so that the color | |
| // does not change over the course of the fade-in, fade-out | |
| // of one cycle of the brightness wave function. | |
| // The 'high digits' are also used to determine whether this pixel | |
| // should light at all during this cycle, based on the TWINKLE_DENSITY. | |
| CRGB computeOneTwinkle( uint32_t ms, uint8_t salt) | |
| { | |
| uint16_t ticks = ms >> (8-TWINKLE_SPEED); | |
| uint8_t fastcycle8 = ticks; | |
| uint16_t slowcycle16 = (ticks >> 8) + salt; | |
| slowcycle16 += sin8( slowcycle16); | |
| slowcycle16 = (slowcycle16 * 2053) + 1384; | |
| uint8_t slowcycle8 = (slowcycle16 & 0xFF) + (slowcycle16 >> 8); | |
| uint8_t bright = 0; | |
| if( ((slowcycle8 & 0x0E)/2) < TWINKLE_DENSITY) { | |
| bright = attackDecayWave8( fastcycle8); | |
| } | |
| uint8_t hue = slowcycle8 - salt; | |
| CRGB c; | |
| if( bright > 0) { | |
| c = ColorFromPalette( gCurrentPalette, hue, bright, NOBLEND); | |
| if( COOL_LIKE_INCANDESCENT == 1 ) { | |
| coolLikeIncandescent( c, fastcycle8); | |
| } | |
| } else { | |
| c = CRGB::Black; | |
| } | |
| return c; | |
| } | |
| // This function is like 'triwave8', which produces a | |
| // symmetrical up-and-down triangle sawtooth waveform, except that this | |
| // function produces a triangle wave with a faster attack and a slower decay: | |
| // | |
| // / \ | |
| // / \ | |
| // / \ | |
| // / \ | |
| // | |
| uint8_t attackDecayWave8( uint8_t i) | |
| { | |
| if( i < 86) { | |
| return i * 3; | |
| } else { | |
| i -= 86; | |
| return 255 - (i + (i/2)); | |
| } | |
| } | |
| // This function takes a pixel, and if its in the 'fading down' | |
| // part of the cycle, it adjusts the color a little bit like the | |
| // way that incandescent bulbs fade toward 'red' as they dim. | |
| void coolLikeIncandescent( CRGB& c, uint8_t phase) | |
| { | |
| if( phase < 128) return; | |
| uint8_t cooling = (phase - 128) >> 4; | |
| c.g = qsub8( c.g, cooling); | |
| c.b = qsub8( c.b, cooling * 2); | |
| } | |
| // A mostly red palette with green accents and white trim. | |
| // "CRGB::Gray" is used as white to keep the brightness more uniform. | |
| const TProgmemRGBPalette16 RedGreenWhite_p FL_PROGMEM = | |
| { CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red, | |
| CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red, | |
| CRGB::Red, CRGB::Red, CRGB::Gray, CRGB::Gray, | |
| CRGB::Green, CRGB::Green, CRGB::Green, CRGB::Green }; | |
| // A mostly (dark) green palette with red berries. | |
| #define Holly_Green 0x00580c | |
| #define Holly_Red 0xB00402 | |
| const TProgmemRGBPalette16 Holly_p FL_PROGMEM = | |
| { Holly_Green, Holly_Green, Holly_Green, Holly_Green, | |
| Holly_Green, Holly_Green, Holly_Green, Holly_Green, | |
| Holly_Green, Holly_Green, Holly_Green, Holly_Green, | |
| Holly_Green, Holly_Green, Holly_Green, Holly_Red | |
| }; | |
| // A red and white striped palette | |
| // "CRGB::Gray" is used as white to keep the brightness more uniform. | |
| const TProgmemRGBPalette16 RedWhite_p FL_PROGMEM = | |
| { CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red, | |
| CRGB::Gray, CRGB::Gray, CRGB::Gray, CRGB::Gray, | |
| CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red, | |
| CRGB::Gray, CRGB::Gray, CRGB::Gray, CRGB::Gray }; | |
| // A mostly blue palette with white accents. | |
| // "CRGB::Gray" is used as white to keep the brightness more uniform. | |
| const TProgmemRGBPalette16 BlueWhite_p FL_PROGMEM = | |
| { CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue, | |
| CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue, | |
| CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue, | |
| CRGB::Blue, CRGB::Gray, CRGB::Gray, CRGB::Gray }; | |
| // A pure "fairy light" palette with some brightness variations | |
| #define HALFFAIRY ((CRGB::FairyLight & 0xFEFEFE) / 2) | |
| #define QUARTERFAIRY ((CRGB::FairyLight & 0xFCFCFC) / 4) | |
| const TProgmemRGBPalette16 FairyLight_p FL_PROGMEM = | |
| { CRGB::FairyLight, CRGB::FairyLight, CRGB::FairyLight, CRGB::FairyLight, | |
| HALFFAIRY, HALFFAIRY, CRGB::FairyLight, CRGB::FairyLight, | |
| QUARTERFAIRY, QUARTERFAIRY, CRGB::FairyLight, CRGB::FairyLight, | |
| CRGB::FairyLight, CRGB::FairyLight, CRGB::FairyLight, CRGB::FairyLight }; | |
| // A palette of soft snowflakes with the occasional bright one | |
| const TProgmemRGBPalette16 Snow_p FL_PROGMEM = | |
| { 0x304048, 0x304048, 0x304048, 0x304048, | |
| 0x304048, 0x304048, 0x304048, 0x304048, | |
| 0x304048, 0x304048, 0x304048, 0x304048, | |
| 0x304048, 0x304048, 0x304048, 0xE0F0FF }; | |
| // A palette reminiscent of large 'old-school' C9-size tree lights | |
| // in the five classic colors: red, orange, green, blue, and white. | |
| #define C9_Red 0xB80400 | |
| #define C9_Orange 0x902C02 | |
| #define C9_Green 0x046002 | |
| #define C9_Blue 0x070758 | |
| #define C9_White 0x606820 | |
| const TProgmemRGBPalette16 RetroC9_p FL_PROGMEM = | |
| { C9_Red, C9_Orange, C9_Red, C9_Orange, | |
| C9_Orange, C9_Red, C9_Orange, C9_Red, | |
| C9_Green, C9_Green, C9_Green, C9_Green, | |
| C9_Blue, C9_Blue, C9_Blue, | |
| C9_White | |
| }; | |
| // A cold, icy pale blue palette | |
| #define Ice_Blue1 0x0C1040 | |
| #define Ice_Blue2 0x182080 | |
| #define Ice_Blue3 0x5080C0 | |
| const TProgmemRGBPalette16 Ice_p FL_PROGMEM = | |
| { | |
| Ice_Blue1, Ice_Blue1, Ice_Blue1, Ice_Blue1, | |
| Ice_Blue1, Ice_Blue1, Ice_Blue1, Ice_Blue1, | |
| Ice_Blue1, Ice_Blue1, Ice_Blue1, Ice_Blue1, | |
| Ice_Blue2, Ice_Blue2, Ice_Blue2, Ice_Blue3 | |
| }; | |
| // Add or remove palette names from this list to control which color | |
| // palettes are used, and in what order. | |
| const TProgmemRGBPalette16* ActivePaletteList[] = { | |
| &RetroC9_p, | |
| &BlueWhite_p, | |
| &RainbowColors_p, | |
| &FairyLight_p, | |
| &RedGreenWhite_p, | |
| &PartyColors_p, | |
| &RedWhite_p, | |
| &Snow_p, | |
| &Holly_p, | |
| &Ice_p | |
| }; | |
| // Advance to the next color palette in the list (above). | |
| void chooseNextColorPalette( CRGBPalette16& pal) | |
| { | |
| const uint8_t numberOfPalettes = sizeof(ActivePaletteList) / sizeof(ActivePaletteList[0]); | |
| static uint8_t whichPalette = -1; | |
| whichPalette = addmod8( whichPalette, 1, numberOfPalettes); | |
| pal = *(ActivePaletteList[whichPalette]); | |
| } |
I get the same errors. Attempting to fix those specific problems leads to a cascade of other issues. Not sure what's up here.
Update: This compiles fine in my local Arduino compiler, just not in the cloud version at create.arduino.cc
Here is a version that compiles for particle.io hardware: https://gist.github.com/edalquist/debd5c83f02e1a08e891678b33f07d00
I have the same problem, and it doesn't compile fine in my local Arduino compiler. I have FastLED installed. What gives?
Just what I was looking for! Thanks for the hard work Mark!
BTW - Compiles just fine with IDE 1.8.5 on Circuit playground and UNO clone
seen
"#define VOLTS 12"
Can I use it on a 5v light set-up?
seen
"#define VOLTS 12"
Can I use it on a 5v light set-up?
Yes, that variable is only used to determine max power that the LED strip will consume. Just change it to #define VOLTS 5
Thanks for this code! https://twitter.com/blairmacintyre/status/1203810336584941574
Is it possible to enter more than 16 points here? We use a matrix with 10x5 pixels and I have too many pixels black. I would like to fill the dark ones with red, green or white. Many thanks for the help...
// A mostly red palette with green accents and white trim.
// "CRGB::Gray" is used as white to keep the brightness more uniform.
const TProgmemRGBPalette16 RedGreenWhite_p FL_PROGMEM =
{ CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red,
CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red,
CRGB::Red, CRGB::Red, CRGB::Gray, CRGB::Gray,
CRGB::Green, CRGB::Green, CRGB::Green, CRGB::Green };
Try increasing “ TWINKLE_DENSITY “ to a higher number, like 7 or 8... that will keep more pixels lit. How’s that?
Try increasing “ TWINKLE_DENSITY “ to a higher number, like 7 or 8... that will keep more pixels lit. How’s that?
Thank you very much for your quick reply!! I will try it out and report whether it worked. Thanks a lot!
Great thank you! That was exactly the solution!! Thanks
Is it possible to ad a button to select the sequenses?
Yes. Lines 128-130 above change the palette automatically every ten seconds.
You could take those lines out, and replace them with code that only changed the palette once for each time that a button was pressed.
It's lovely - thank you for all that work. :)
This worked great, thanks for the Code this has the best hue I have found. I run a new power tap every 100 LEDS back to a 60 watt power supply, but the data line is the same.
There is a bug changing the palette with more than 200 LED's. The LEDs above flicker for a second when the pattern changes. I only wanted the c9 pattern so I commented out the other palette and bumped up the time to 3600 seconds.
First error when trying to compile: CRGBArray<NUM_LEDS> leds; //'CRGBArray' does not name a type
Second error: void drawTwinkles( CRGBSet& L) // variable or field'drwaTwinkles' declared void
Any ideas why?