Difference between revisions of "NeoPixel Workshop Instructions"
Line 981: | Line 981: | ||
</pre> | </pre> | ||
− | == | + | == Twinkle == |
<pre> | <pre> | ||
+ | #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 6 | ||
+ | #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]); | ||
+ | } | ||
</pre> | </pre> | ||
+ | |||
== Demo Reel.ino == | == Demo Reel.ino == | ||
Revision as of 20:30, 10 November 2017
This is the instruction page for the NeoPixel Workshop ran by Andy Miller on 11/11/17
Contents
Software to install
You will need to install the following programs and drivers
Download and install the Arduino IDE V 1.8.5 (or newer).
Do not download/install the web IDE!
https://www.arduino.cc/en/Main/Software
There are some step by step instructions here: https://www.arduino.cc/en/Guide/HomePage
Download and install the CH340 USB drivers for the Arduino clones:
https://sparks.gogo.co.nz/ch340.html
Note: The CH340 chip is a USB to serial chip used frequently on Arduino clones. Official Arduinos use the FTDI chip. Those drivers are included in the Arduino software install.
Download the FastLED library from:
https://github.com/FastLED/FastLED/releases Current version is 3.1.6
We will show you how to install this library. The FastLED library is used to control NeoPixels! The official page for the FASTLED library is:
https://github.com/FastLED/FastLED/tree/v3.1.6
Code
Color Pallet.ino
#include <FastLED.h> #define LED_PIN 6 #define NUM_LEDS 60 #define BRIGHTNESS 96 #define LED_TYPE WS2811 #define COLOR_ORDER GRB CRGB leds[NUM_LEDS]; #define UPDATES_PER_SECOND 100 // 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; void setup() { delay( 3000 ); // power-up safety delay FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip ); FastLED.setBrightness( BRIGHTNESS ); currentPalette = RainbowColors_p; currentBlending = LINEARBLEND; } void loop() { ChangePalettePeriodically(); static uint8_t startIndex = 0; startIndex = startIndex + 1; /* motion speed */ FillLEDsFromPaletteColors( startIndex); FastLED.show(); FastLED.delay(1000 / UPDATES_PER_SECOND); } void FillLEDsFromPaletteColors( uint8_t colorIndex) { uint8_t brightness = 255; for( int i = 0; i < NUM_LEDS; i++) { leds[i] = ColorFromPalette( currentPalette, colorIndex, brightness, currentBlending); colorIndex += 3; } } 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 };
Demo Reel.ino
#include "FastLED.h" #if defined(FASTLED_VERSION) && (FASTLED_VERSION < 3001000) #warning "Requires FastLED 3.1 or later; check github for latest code." #endif #define DATA_PIN 6 #define LED_TYPE WS2811 #define COLOR_ORDER GRB #define NUM_LEDS 60 CRGB leds[NUM_LEDS]; #define BRIGHTNESS 96 #define FRAMES_PER_SECOND 80 void setup() { delay(3000); // 3 second delay for recovery // tell FastLED about the LED strip configuration FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip); //FastLED.addLeds<LED_TYPE,DATA_PIN,CLK_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip); // set master brightness control FastLED.setBrightness(BRIGHTNESS); } // List of patterns to cycle through. Each is defined as a separate function below. typedef void (*SimplePatternList[])(); SimplePatternList gPatterns = { rainbow, rainbowWithGlitter, confetti, sinelon, juggle, bpm }; uint8_t gCurrentPatternNumber = 0; // Index number of which pattern is current uint8_t gHue = 0; // rotating "base color" used by many of the patterns void loop() { // Call the current pattern function once, updating the 'leds' array gPatterns[gCurrentPatternNumber](); // send the 'leds' array out to the actual LED strip FastLED.show(); // insert a delay to keep the framerate modest FastLED.delay(1000/FRAMES_PER_SECOND); // do some periodic updates EVERY_N_MILLISECONDS( 20 ) { gHue++; } // slowly cycle the "base color" through the rainbow EVERY_N_SECONDS( 10 ) { nextPattern(); } // change patterns periodically } #define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0])) void nextPattern() { // add one to the current pattern number, and wrap around at the end gCurrentPatternNumber = (gCurrentPatternNumber + 1) % ARRAY_SIZE( gPatterns); } void rainbow() { // FastLED's built-in rainbow generator fill_rainbow( leds, NUM_LEDS, gHue, 7); } void rainbowWithGlitter() { // built-in FastLED rainbow, plus some random sparkly glitter rainbow(); addGlitter(80); } void addGlitter( fract8 chanceOfGlitter) { if( random8() < chanceOfGlitter) { leds[ random16(NUM_LEDS) ] += CRGB::White; } } void confetti() { // random colored speckles that blink in and fade smoothly fadeToBlackBy( leds, NUM_LEDS, 10); int pos = random16(NUM_LEDS); leds[pos] += CHSV( gHue + random8(64), 200, 255); } void sinelon() { // a colored dot sweeping back and forth, with fading trails fadeToBlackBy( leds, NUM_LEDS, 20); int pos = beatsin16( 13, 0, NUM_LEDS-1 ); leds[pos] += CHSV( gHue, 255, 192); } void bpm() { // colored stripes pulsing at a defined Beats-Per-Minute (BPM) uint8_t BeatsPerMinute = 62; CRGBPalette16 palette = PartyColors_p; uint8_t beat = beatsin8( BeatsPerMinute, 64, 255); for( int i = 0; i < NUM_LEDS; i++) { //9948 leds[i] = ColorFromPalette(palette, gHue+(i*2), beat-gHue+(i*10)); } } void juggle() { // eight colored dots, weaving in and out of sync with each other fadeToBlackBy( leds, NUM_LEDS, 20); byte dothue = 0; for( int i = 0; i < 8; i++) { leds[beatsin16( i+7, 0, NUM_LEDS-1 )] |= CHSV(dothue, 200, 255); dothue += 32; } }
Fire Nash.ino
#include <Adafruit_NeoPixel.h> #define PIN 6 #define NUM_LEDS 144 // Parameter 1 = number of pixels in strip // Parameter 2 = pin number (most are valid) // Parameter 3 = pixel type flags, add together as needed: // NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) // NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) // NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products) // NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2) Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ800); void setup() { strip.begin(); strip.show(); // Initialize all pixels to 'off' } // *** REPLACE FROM HERE *** void loop() { Fire(55,120,15); } void Fire(int Cooling, int Sparking, int SpeedDelay) { static byte heat[NUM_LEDS]; int cooldown; // Step 1. Cool down every cell a little for( int i = 0; i < NUM_LEDS; i++) { cooldown = random(0, ((Cooling * 10) / NUM_LEDS) + 2); if(cooldown>heat[i]) { heat[i]=0; } else { heat[i]=heat[i]-cooldown; } } // Step 2. Heat from each cell drifts 'up' and diffuses a little for( int k= NUM_LEDS - 1; k >= 2; k--) { heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3; } // Step 3. Randomly ignite new 'sparks' near the bottom if( random(255) < Sparking ) { int y = random(7); heat[y] = heat[y] + random(160,255); //heat[y] = random(160,255); } // Step 4. Convert heat to LED colors for( int j = 0; j < NUM_LEDS; j++) { setPixelHeatColor(j, heat[j] ); } showStrip(); delay(SpeedDelay); } void setPixelHeatColor (int Pixel, byte temperature) { // Scale 'heat' down from 0-255 to 0-191 byte t192 = round((temperature/255.0)*191); // calculate ramp up from byte heatramp = t192 & 0x3F; // 0..63 heatramp <<= 2; // scale up to 0..252 // figure out which third of the spectrum we're in: if( t192 > 0x80) { // hottest setPixel(Pixel, 255, 255, heatramp); } else if( t192 > 0x40 ) { // middle setPixel(Pixel, 255, heatramp, 0); } else { // coolest setPixel(Pixel, heatramp, 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(); }
Neo Test Code.ino
#include <Adafruit_NeoPixel.h> #ifdef __AVR__ #include <avr/power.h> #endif #define PIN 6 #define NUM_LEDS 144 #define BRIGHTNESS 50 Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRBW + NEO_KHZ800); int gamma[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36, 37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50, 51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68, 69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89, 90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114, 115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142, 144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175, 177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213, 215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 }; void setup() { Serial.begin(115200); // This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket #if defined (__AVR_ATtiny85__) if (F_CPU == 16000000) clock_prescale_set(clock_div_1); #endif // End of trinket special code strip.setBrightness(BRIGHTNESS); strip.begin(); strip.show(); // Initialize all pixels to 'off' } void loop() { // Some example procedures showing how to display to the pixels: colorWipe(strip.Color(255, 0, 0), 50); // Red colorWipe(strip.Color(0, 0, 255), 50); // Blue colorWipe(strip.Color(0, 0, 0, 255), 50); // White whiteOverRainbow(20,75,5); pulseWhite(5); // fullWhite(); // delay(2000); rainbowFade2White(3,3,1); } // Fill the dots one after the other with a color void colorWipe(uint32_t c, uint8_t wait) { for(uint16_t i=0; i<strip.numPixels(); i++) { strip.setPixelColor(i, c); strip.show(); delay(wait); } } void pulseWhite(uint8_t wait) { for(int j = 0; j < 256 ; j++){ for(uint16_t i=0; i<strip.numPixels(); i++) { strip.setPixelColor(i, strip.Color(0,0,0, gamma[j] ) ); } delay(wait); strip.show(); } for(int j = 255; j >= 0 ; j--){ for(uint16_t i=0; i<strip.numPixels(); i++) { strip.setPixelColor(i, strip.Color(0,0,0, gamma[j] ) ); } delay(wait); strip.show(); } } void rainbowFade2White(uint8_t wait, int rainbowLoops, int whiteLoops) { float fadeMax = 100.0; int fadeVal = 0; uint32_t wheelVal; int redVal, greenVal, blueVal; for(int k = 0 ; k < rainbowLoops ; k ++){ for(int j=0; j<256; j++) { // 5 cycles of all colors on wheel for(int i=0; i< strip.numPixels(); i++) { wheelVal = Wheel(((i * 256 / strip.numPixels()) + j) & 255); redVal = red(wheelVal) * float(fadeVal/fadeMax); greenVal = green(wheelVal) * float(fadeVal/fadeMax); blueVal = blue(wheelVal) * float(fadeVal/fadeMax); strip.setPixelColor( i, strip.Color( redVal, greenVal, blueVal ) ); } //First loop, fade in! if(k == 0 && fadeVal < fadeMax-1) { fadeVal++; } //Last loop, fade out! else if(k == rainbowLoops - 1 && j > 255 - fadeMax ){ fadeVal--; } strip.show(); delay(wait); } } delay(500); for(int k = 0 ; k < whiteLoops ; k ++){ for(int j = 0; j < 256 ; j++){ for(uint16_t i=0; i < strip.numPixels(); i++) { strip.setPixelColor(i, strip.Color(0,0,0, gamma[j] ) ); } strip.show(); } delay(2000); for(int j = 255; j >= 0 ; j--){ for(uint16_t i=0; i < strip.numPixels(); i++) { strip.setPixelColor(i, strip.Color(0,0,0, gamma[j] ) ); } strip.show(); } } delay(500); } void whiteOverRainbow(uint8_t wait, uint8_t whiteSpeed, uint8_t whiteLength ) { if(whiteLength >= strip.numPixels()) whiteLength = strip.numPixels() - 1; int head = whiteLength - 1; int tail = 0; int loops = 3; int loopNum = 0; static unsigned long lastTime = 0; while(true){ for(int j=0; j<256; j++) { for(uint16_t i=0; i<strip.numPixels(); i++) { if((i >= tail && i <= head) || (tail > head && i >= tail) || (tail > head && i <= head) ){ strip.setPixelColor(i, strip.Color(0,0,0, 255 ) ); } else{ strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255)); } } if(millis() - lastTime > whiteSpeed) { head++; tail++; if(head == strip.numPixels()){ loopNum++; } lastTime = millis(); } if(loopNum == loops) return; head%=strip.numPixels(); tail%=strip.numPixels(); strip.show(); delay(wait); } } } void fullWhite() { for(uint16_t i=0; i<strip.numPixels(); i++) { strip.setPixelColor(i, strip.Color(0,0,0, 255 ) ); } strip.show(); } // Slightly different, this makes the rainbow equally distributed throughout void rainbowCycle(uint8_t wait) { uint16_t i, j; for(j=0; j<256 * 5; j++) { // 5 cycles of all colors on wheel for(i=0; i< strip.numPixels(); i++) { strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255)); } strip.show(); delay(wait); } } void rainbow(uint8_t wait) { uint16_t i, j; for(j=0; j<256; j++) { for(i=0; i<strip.numPixels(); i++) { strip.setPixelColor(i, Wheel((i+j) & 255)); } strip.show(); delay(wait); } } // Input a value 0 to 255 to get a color value. // The colours are a transition r - g - b - back to r. uint32_t Wheel(byte WheelPos) { WheelPos = 255 - WheelPos; if(WheelPos < 85) { return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3,0); } if(WheelPos < 170) { WheelPos -= 85; return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3,0); } WheelPos -= 170; return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0,0); } uint8_t red(uint32_t c) { return (c >> 8); } uint8_t green(uint32_t c) { return (c >> 16); } uint8_t blue(uint32_t c) { return (c); }
RWB 1St Code.ino
#include <Adafruit_NeoPixel.h> #ifdef __AVR__ #include <avr/power.h> #endif #define PIN 6 Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800); void setup() { // This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket #if defined (__AVR_ATtiny85__) if (F_CPU == 16000000) clock_prescale_set(clock_div_1); #endif // End of trinket special code strip.begin(); strip.show(); // Initialize all pixels to 'off' } void loop() { // Some example procedures showing how to display to the pixels: colorWipe(strip.Color(255, 0, 0), 10); // Red colorWipe(strip.Color(127, 127, 127), 10); // White colorWipe(strip.Color(0, 0, 255), 10); // Blue colorWipe(strip.Color(255, 0, 0), 10); // Red colorWipe(strip.Color(127, 127, 127), 10); // White colorWipe(strip.Color(0, 0, 255), 10); // Blue colorWipe(strip.Color(255, 0, 0), 10); // Red colorWipe(strip.Color(127, 127, 127), 10); // White colorWipe(strip.Color(0, 0, 255), 10); // Blue colorWipe(strip.Color(255, 0, 0), 10); // Red colorWipe(strip.Color(127, 127, 127), 10); // White colorWipe(strip.Color(0, 0, 255), 10); // Blue // Send a theater pixel chase in... theaterChase(strip.Color(127, 0, 0), 10); // Red theaterChase(strip.Color(127, 127, 127), 10); // White theaterChase(strip.Color(0, 0, 127), 10); // Blue theaterChase(strip.Color(127, 0, 0), 10); // Red theaterChase(strip.Color(127, 127, 127), 10); // White theaterChase(strip.Color(0, 0, 127), 10); // Blue theaterChase(strip.Color(127, 0, 0), 10); // Red theaterChase(strip.Color(127, 127, 127), 10); // White theaterChase(strip.Color(0, 0, 127), 10); // Blue rainbow(10); rainbowCycle(10); theaterChaseRainbow(10); } // Fill the dots one after the other with a color void colorWipe(uint32_t c, uint8_t wait) { for(uint16_t i=0; i<strip.numPixels(); i++) { strip.setPixelColor(i, c); strip.show(); delay(wait); } } void rainbow(uint8_t wait) { uint16_t i, j; for(j=0; j<128; j++) { for(i=0; i<strip.numPixels(); i++) { strip.setPixelColor(i, Wheel((i+j) & 127)); } strip.show(); delay(wait); } } // Slightly different, this makes the rainbow equally distributed throughout void rainbowCycle(uint8_t wait) { uint16_t i, j; for(j=0; j<127*5; j++) { // 5 cycles of all colors on wheel for(i=0; i< strip.numPixels(); i++) { strip.setPixelColor(i, Wheel(((i * 128 / strip.numPixels()) + j) & 127)); } strip.show(); delay(wait); } } //Theatre-style crawling lights. void theaterChase(uint32_t c, uint8_t wait) { for (int j=0; j<10; j++) { //do 10 cycles of chasing for (int q=0; q < 3; q++) { for (uint16_t i=0; i < strip.numPixels(); i=i+3) { strip.setPixelColor(i+q, c); //turn every third pixel on } strip.show(); delay(wait); for (uint16_t i=0; i < strip.numPixels(); i=i+5) { strip.setPixelColor(i+q, 0); //turn every third pixel off } } } } //Theatre-style crawling lights with rainbow effect void theaterChaseRainbow(uint8_t wait) { for (int j=0; j < 256; j++) { // cycle all 256 colors in the wheel for (int q=0; q < 3; q++) { for (uint16_t i=0; i < strip.numPixels(); i=i+3) { strip.setPixelColor(i+q, Wheel( (i+j) % 255)); //turn every third pixel on } strip.show(); delay(wait); for (uint16_t i=0; i < strip.numPixels(); i=i+3) { strip.setPixelColor(i+q, 0); //turn every third pixel off } } } } // Input a value 0 to 255 to get a color value. // The colours are a transition r - g - b - back to r. uint32_t Wheel(byte WheelPos) { WheelPos = 150 - WheelPos; if(WheelPos < 100) { return strip.Color(150 - WheelPos * 3, 0, WheelPos * 3); } if(WheelPos < 255) { WheelPos -= 100; return strip.Color(100, WheelPos * 3, 125 - WheelPos * 3); } WheelPos -= 190; return strip.Color(WheelPos * 2, 100 - WheelPos * 4, 255); }
Show Me the Blinkey
#include "FastLED.h" #define LED_COUNT 60 #define LED_PIN 6 struct CRGB leds[LED_COUNT]; uint8_t hue = 25; byte idex = 200; byte meteorLength = 10; void setup() { // sanity check delay - allows reprogramming if accidently blowing power w/leds delay(1000); LEDS.addLeds<WS2812, LED_PIN, GRB>(leds, LED_COUNT); LEDS.setBrightness(200); } void loop(){ meteorShower(); } void meteorShower(){ // slide all the pixels down one in the array memmove8( &leds[1], &leds[0], (LED_COUNT - 1) * 3 ); // increment the meteor display frame idex++; // make sure we don't drift into space if ( idex > meteorLength ) { idex = 0; // cycle through hues in each successive meteor tail hue += 32; } // this switch controls the actual meteor animation, i.e., what gets placed in the // first position and then subsequently gets moved down the strip by the memmove above switch ( idex ) { case 0: leds[0] = CRGB(200,200,200); break; case 1: leds[0] = CHSV((hue - 20), 255, 210); break; case 2: leds[0] = CHSV((hue - 22), 255, 180); break; case 3: leds[0] = CHSV((hue - 23), 255, 150); break; case 4: leds[0] = CHSV((hue - 24), 255, 110); break; case 5: leds[0] = CHSV((hue - 25), 255, 90); break; case 6: leds[0] = CHSV((hue - 26), 160, 60); break; case 7: leds[0] = CHSV((hue - 27), 140, 40); break; case 8: leds[0] = CHSV((hue - 28), 120, 20); break; case 9: leds[0] = CHSV((hue - 29), 100, 20); break; default: leds[0] = CRGB::Black; } // show the blinky FastLED.show(); // control the animation speed/frame rate delay(10); }
Strobe
#include <Adafruit_NeoPixel.h> #define PIN 6 #define NUM_LEDS 60 // Parameter 1 = number of pixels in strip // Parameter 2 = pin number (most are valid) // Parameter 3 = pixel type flags, add together as needed: // NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) // NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) // NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products) // NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2) Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ800); void setup() { strip.begin(); strip.show(); // Initialize all pixels to 'off' } // *** REPLACE FROM HERE *** void loop() { // Slower: // Strobe(0xff, 0x77, 0x00, 10, 100, 1000); // Fast: Strobe(0xff, 0xff, 0xff, 10, 50, 1000); } void Strobe(byte red, byte green, byte blue, int StrobeCount, int FlashDelay, int EndPause){ for(int j = 0; j < StrobeCount; j++) { setAll(red,green,blue); showStrip(); delay(FlashDelay); setAll(0,0,0); showStrip(); delay(FlashDelay); } delay(EndPause); } // *** 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(); }
Twinkle
#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 6 #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]); }