FastLED 3.7.8
Loading...
Searching...
No Matches
TwinkleFox.ino
Go to the documentation of this file.
1
4
5#include "FastLED.h"
6
7
8#define NUM_LEDS 100
9#define LED_TYPE WS2811
10#define COLOR_ORDER GRB
11#define DATA_PIN 3
12//#define CLK_PIN 4
13#define VOLTS 12
14#define MAX_MA 4000
15
16// TwinkleFOX: Twinkling 'holiday' lights that fade in and out.
17// Colors are chosen from a palette; a few palettes are provided.
18//
19// This December 2015 implementation improves on the December 2014 version
20// in several ways:
21// - smoother fading, compatible with any colors and any palettes
22// - easier control of twinkle speed and twinkle density
23// - supports an optional 'background color'
24// - takes even less RAM: zero RAM overhead per pixel
25// - illustrates a couple of interesting techniques (uh oh...)
26//
27// The idea behind this (new) implementation is that there's one
28// basic, repeating pattern that each pixel follows like a waveform:
29// The brightness rises from 0..255 and then falls back down to 0.
30// The brightness at any given point in time can be determined as
31// as a function of time, for example:
32// brightness = sine( time ); // a sine wave of brightness over time
33//
34// So the way this implementation works is that every pixel follows
35// the exact same wave function over time. In this particular case,
36// I chose a sawtooth triangle wave (triwave8) rather than a sine wave,
37// but the idea is the same: brightness = triwave8( time ).
38//
39// Of course, if all the pixels used the exact same wave form, and
40// if they all used the exact same 'clock' for their 'time base', all
41// the pixels would brighten and dim at once -- which does not look
42// like twinkling at all.
43//
44// So to achieve random-looking twinkling, each pixel is given a
45// slightly different 'clock' signal. Some of the clocks run faster,
46// some run slower, and each 'clock' also has a random offset from zero.
47// The net result is that the 'clocks' for all the pixels are always out
48// of sync from each other, producing a nice random distribution
49// of twinkles.
50//
51// The 'clock speed adjustment' and 'time offset' for each pixel
52// are generated randomly. One (normal) approach to implementing that
53// would be to randomly generate the clock parameters for each pixel
54// at startup, and store them in some arrays. However, that consumes
55// a great deal of precious RAM, and it turns out to be totally
56// unnessary! If the random number generate is 'seeded' with the
57// same starting value every time, it will generate the same sequence
58// of values every time. So the clock adjustment parameters for each
59// pixel are 'stored' in a pseudo-random number generator! The PRNG
60// is reset, and then the first numbers out of it are the clock
61// adjustment parameters for the first pixel, the second numbers out
62// of it are the parameters for the second pixel, and so on.
63// In this way, we can 'store' a stable sequence of thousands of
64// random clock adjustment parameters in literally two bytes of RAM.
65//
66// There's a little bit of fixed-point math involved in applying the
67// clock speed adjustments, which are expressed in eighths. Each pixel's
68// clock speed ranges from 8/8ths of the system clock (i.e. 1x) to
69// 23/8ths of the system clock (i.e. nearly 3x).
70//
71// On a basic Arduino Uno or Leonardo, this code can twinkle 300+ pixels
72// smoothly at over 50 updates per seond.
73//
74// -Mark Kriegsman, December 2015
75
77
78// Overall twinkle speed.
79// 0 (VERY slow) to 8 (VERY fast).
80// 4, 5, and 6 are recommended, default is 4.
81#define TWINKLE_SPEED 4
82
83// Overall twinkle density.
84// 0 (NONE lit) to 8 (ALL lit at once).
85// Default is 5.
86#define TWINKLE_DENSITY 5
87
88// How often to change color palettes.
89#define SECONDS_PER_PALETTE 30
90// Also: toward the bottom of the file is an array
91// called "ActivePaletteList" which controls which color
92// palettes are used; you can add or remove color palettes
93// from there freely.
94
95// Background color for 'unlit' pixels
96// Can be set to CRGB::Black if desired.
97CRGB gBackgroundColor = CRGB::Black;
98// Example of dim incandescent fairy light background color
99// CRGB gBackgroundColor = CRGB(CRGB::FairyLight).nscale8_video(16);
100
101// If AUTO_SELECT_BACKGROUND_COLOR is set to 1,
102// then for any palette where the first two entries
103// are the same, a dimmed version of that color will
104// automatically be used as the background color.
105#define AUTO_SELECT_BACKGROUND_COLOR 0
106
107// If COOL_LIKE_INCANDESCENT is set to 1, colors will
108// fade out slighted 'reddened', similar to how
109// incandescent bulbs change color as they get dim down.
110#define COOL_LIKE_INCANDESCENT 1
111
112
113CRGBPalette16 gCurrentPalette;
114CRGBPalette16 gTargetPalette;
115
116void setup() {
117 delay( 3000 ); //safety startup delay
119 FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS)
120 .setCorrection(TypicalLEDStrip);
121
122 chooseNextColorPalette(gTargetPalette);
123}
124
125
126void loop()
127{
128 EVERY_N_SECONDS( SECONDS_PER_PALETTE ) {
129 chooseNextColorPalette( gTargetPalette );
130 }
131
133 nblendPaletteTowardPalette( gCurrentPalette, gTargetPalette, 12);
134 }
135
136 drawTwinkles( leds);
137
138 FastLED.show();
139}
140
141
142// This function loops over each pixel, calculates the
143// adjusted 'clock' that this pixel should use, and calls
144// "CalculateOneTwinkle" on each pixel. It then displays
145// either the twinkle color of the background color,
146// whichever is brighter.
147void drawTwinkles( CRGBSet& L)
148{
149 // "PRNG16" is the pseudorandom number generator
150 // It MUST be reset to the same starting value each time
151 // this function is called, so that the sequence of 'random'
152 // numbers that it generates is (paradoxically) stable.
153 uint16_t PRNG16 = 11337;
154
155 uint32_t clock32 = millis();
156
157 // Set up the background color, "bg".
158 // if AUTO_SELECT_BACKGROUND_COLOR == 1, and the first two colors of
159 // the current palette are identical, then a deeply faded version of
160 // that color is used for the background color
161 CRGB bg;
162 if( (AUTO_SELECT_BACKGROUND_COLOR == 1) &&
163 (gCurrentPalette[0] == gCurrentPalette[1] )) {
164 bg = gCurrentPalette[0];
165 uint8_t bglight = bg.getAverageLight();
166 if( bglight > 64) {
167 bg.nscale8_video( 16); // very bright, so scale to 1/16th
168 } else if( bglight > 16) {
169 bg.nscale8_video( 64); // not that bright, so scale to 1/4th
170 } else {
171 bg.nscale8_video( 86); // dim, scale to 1/3rd.
172 }
173 } else {
174 bg = gBackgroundColor; // just use the explicitly defined background color
175 }
176
177 uint8_t backgroundBrightness = bg.getAverageLight();
178
179 for( CRGB& pixel: L) {
180 PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number
181 uint16_t myclockoffset16= PRNG16; // use that number as clock offset
182 PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number
183 // use that number as clock speed adjustment factor (in 8ths, from 8/8ths to 23/8ths)
184 uint8_t myspeedmultiplierQ5_3 = ((((PRNG16 & 0xFF)>>4) + (PRNG16 & 0x0F)) & 0x0F) + 0x08;
185 uint32_t myclock30 = (uint32_t)((clock32 * myspeedmultiplierQ5_3) >> 3) + myclockoffset16;
186 uint8_t myunique8 = PRNG16 >> 8; // get 'salt' value for this pixel
187
188 // We now have the adjusted 'clock' for this pixel, now we call
189 // the function that computes what color the pixel should be based
190 // on the "brightness = f( time )" idea.
191 CRGB c = computeOneTwinkle( myclock30, myunique8);
192
193 uint8_t cbright = c.getAverageLight();
194 int16_t deltabright = cbright - backgroundBrightness;
195 if( deltabright >= 32 || (!bg)) {
196 // If the new pixel is significantly brighter than the background color,
197 // use the new color.
198 pixel = c;
199 } else if( deltabright > 0 ) {
200 // If the new pixel is just slightly brighter than the background color,
201 // mix a blend of the new color and the background color
202 pixel = blend( bg, c, deltabright * 8);
203 } else {
204 // if the new pixel is not at all brighter than the background color,
205 // just use the background color.
206 pixel = bg;
207 }
208 }
209}
210
211
212// This function takes a time in pseudo-milliseconds,
213// figures out brightness = f( time ), and also hue = f( time )
214// The 'low digits' of the millisecond time are used as
215// input to the brightness wave function.
216// The 'high digits' are used to select a color, so that the color
217// does not change over the course of the fade-in, fade-out
218// of one cycle of the brightness wave function.
219// The 'high digits' are also used to determine whether this pixel
220// should light at all during this cycle, based on the TWINKLE_DENSITY.
221CRGB computeOneTwinkle( uint32_t ms, uint8_t salt)
222{
223 uint16_t ticks = ms >> (8-TWINKLE_SPEED);
224 uint8_t fastcycle8 = ticks;
225 uint16_t slowcycle16 = (ticks >> 8) + salt;
226 slowcycle16 += sin8( slowcycle16);
227 slowcycle16 = (slowcycle16 * 2053) + 1384;
228 uint8_t slowcycle8 = (slowcycle16 & 0xFF) + (slowcycle16 >> 8);
229
230 uint8_t bright = 0;
231 if( ((slowcycle8 & 0x0E)/2) < TWINKLE_DENSITY) {
232 bright = attackDecayWave8( fastcycle8);
233 }
234
235 uint8_t hue = slowcycle8 - salt;
236 CRGB c;
237 if( bright > 0) {
238 c = ColorFromPalette( gCurrentPalette, hue, bright, NOBLEND);
239 if( COOL_LIKE_INCANDESCENT == 1 ) {
240 coolLikeIncandescent( c, fastcycle8);
241 }
242 } else {
243 c = CRGB::Black;
244 }
245 return c;
246}
247
248
249// This function is like 'triwave8', which produces a
250// symmetrical up-and-down triangle sawtooth waveform, except that this
251// function produces a triangle wave with a faster attack and a slower decay:
252//
253// / \
254// / \
255// / \
256// / \
257//
258
259uint8_t attackDecayWave8( uint8_t i)
260{
261 if( i < 86) {
262 return i * 3;
263 } else {
264 i -= 86;
265 return 255 - (i + (i/2));
266 }
267}
268
269// This function takes a pixel, and if its in the 'fading down'
270// part of the cycle, it adjusts the color a little bit like the
271// way that incandescent bulbs fade toward 'red' as they dim.
272void coolLikeIncandescent( CRGB& c, uint8_t phase)
273{
274 if( phase < 128) return;
275
276 uint8_t cooling = (phase - 128) >> 4;
277 c.g = qsub8( c.g, cooling);
278 c.b = qsub8( c.b, cooling * 2);
279}
280
281// A mostly red palette with green accents and white trim.
282// "CRGB::Gray" is used as white to keep the brightness more uniform.
283const TProgmemRGBPalette16 RedGreenWhite_p FL_PROGMEM =
288
289// A mostly (dark) green palette with red berries.
290#define Holly_Green 0x00580c
291#define Holly_Red 0xB00402
292const TProgmemRGBPalette16 Holly_p FL_PROGMEM =
293{ Holly_Green, Holly_Green, Holly_Green, Holly_Green,
294 Holly_Green, Holly_Green, Holly_Green, Holly_Green,
295 Holly_Green, Holly_Green, Holly_Green, Holly_Green,
296 Holly_Green, Holly_Green, Holly_Green, Holly_Red
297};
298
299// A red and white striped palette
300// "CRGB::Gray" is used as white to keep the brightness more uniform.
301const TProgmemRGBPalette16 RedWhite_p FL_PROGMEM =
306
307// A mostly blue palette with white accents.
308// "CRGB::Gray" is used as white to keep the brightness more uniform.
309const TProgmemRGBPalette16 BlueWhite_p FL_PROGMEM =
314
315// A pure "fairy light" palette with some brightness variations
316#define HALFFAIRY ((CRGB::FairyLight & 0xFEFEFE) / 2)
317#define QUARTERFAIRY ((CRGB::FairyLight & 0xFCFCFC) / 4)
318const TProgmemRGBPalette16 FairyLight_p FL_PROGMEM =
320 HALFFAIRY, HALFFAIRY, CRGB::FairyLight, CRGB::FairyLight,
321 QUARTERFAIRY, QUARTERFAIRY, CRGB::FairyLight, CRGB::FairyLight,
323
324// A palette of soft snowflakes with the occasional bright one
325const TProgmemRGBPalette16 Snow_p FL_PROGMEM =
326{ 0x304048, 0x304048, 0x304048, 0x304048,
327 0x304048, 0x304048, 0x304048, 0x304048,
328 0x304048, 0x304048, 0x304048, 0x304048,
329 0x304048, 0x304048, 0x304048, 0xE0F0FF };
330
331// A palette reminiscent of large 'old-school' C9-size tree lights
332// in the five classic colors: red, orange, green, blue, and white.
333#define C9_Red 0xB80400
334#define C9_Orange 0x902C02
335#define C9_Green 0x046002
336#define C9_Blue 0x070758
337#define C9_White 0x606820
338const TProgmemRGBPalette16 RetroC9_p FL_PROGMEM =
339{ C9_Red, C9_Orange, C9_Red, C9_Orange,
340 C9_Orange, C9_Red, C9_Orange, C9_Red,
341 C9_Green, C9_Green, C9_Green, C9_Green,
342 C9_Blue, C9_Blue, C9_Blue,
343 C9_White
344};
345
346// A cold, icy pale blue palette
347#define Ice_Blue1 0x0C1040
348#define Ice_Blue2 0x182080
349#define Ice_Blue3 0x5080C0
351{
352 Ice_Blue1, Ice_Blue1, Ice_Blue1, Ice_Blue1,
353 Ice_Blue1, Ice_Blue1, Ice_Blue1, Ice_Blue1,
354 Ice_Blue1, Ice_Blue1, Ice_Blue1, Ice_Blue1,
355 Ice_Blue2, Ice_Blue2, Ice_Blue2, Ice_Blue3
356};
357
358
359// Add or remove palette names from this list to control which color
360// palettes are used, and in what order.
361const TProgmemRGBPalette16* ActivePaletteList[] = {
362 &RetroC9_p,
363 &BlueWhite_p,
365 &FairyLight_p,
366 &RedGreenWhite_p,
368 &RedWhite_p,
369 &Snow_p,
370 &Holly_p,
371 &Ice_p
372};
373
374
375// Advance to the next color palette in the list (above).
376void chooseNextColorPalette( CRGBPalette16& pal)
377{
378 const uint8_t numberOfPalettes = sizeof(ActivePaletteList) / sizeof(ActivePaletteList[0]);
379 static uint8_t whichPalette = -1;
380 whichPalette = addmod8( whichPalette, 1, numberOfPalettes);
381
382 pal = *(ActivePaletteList[whichPalette]);
383}
CFastLED FastLED
Global LED strip management instance.
Definition FastLED.cpp:21
central include file for FastLED, defines the CFastLED class/object
void show(uint8_t scale)
Update all our controllers with the current led colors, using the passed in brightness.
Definition FastLED.cpp:59
void setMaxPowerInVoltsAndMilliamps(uint8_t volts, uint32_t milliamps)
Set the maximum power to be used, given in volts and milliamps.
Definition FastLED.h:724
static CLEDController & addLeds(CLEDController *pLed, struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset=0)
Add a CLEDController instance to the world.
Definition FastLED.cpp:47
A version of CPixelView<CRGB> with an included array of CRGB LEDs.
Definition pixelset.h:416
RGB color palette with 16 discrete values.
Definition colorutils.h:933
#define FL_PROGMEM
PROGMEM keyword for storage.
CRGB blend(const CRGB &p1, const CRGB &p2, fract8 amountOfP2)
Computes a new color blended some fraction of the way between two other colors.
@ TypicalLEDStrip
Typical values for SMD5050 LEDs.
Definition color.h:20
LIB8STATIC uint8_t addmod8(uint8_t a, uint8_t b, uint8_t m)
Add two numbers, and calculate the modulo of the sum and a third number, M.
Definition math8.h:388
LIB8STATIC_ALWAYS_INLINE uint8_t qsub8(uint8_t i, uint8_t j)
Subtract one byte from another, saturating at 0x00.
Definition math8.h:99
uint32_t TProgmemRGBPalette16[16]
CRGBPalette16 entries stored in PROGMEM memory.
Definition colorutils.h:584
void nblendPaletteTowardPalette(CRGBPalette16 &current, CRGBPalette16 &target, uint8_t maxChanges)
Alter one palette by making it slightly more like a "target palette".
CRGB ColorFromPalette(const CRGBPalette16 &pal, uint8_t index, uint8_t brightness, TBlendType blendType)
Get a color from a palette.
@ NOBLEND
No interpolation between palette entries.
FASTLED_FORCE_INLINE CRGB & nscale8_video(uint8_t scaledown)
Scale down a RGB to N/256ths of it's current brightness using "video" dimming rules.
Definition crgb.hpp:54
uint8_t g
Green channel value.
Definition crgb.h:33
FASTLED_FORCE_INLINE uint8_t getAverageLight() const
Get the average of the R, G, and B values.
Definition crgb.hpp:119
uint8_t b
Blue channel value.
Definition crgb.h:37
@ Gray
Definition crgb.h:514
@ Green
Definition crgb.h:516
@ Blue
Definition crgb.h:470
@ Red
Definition crgb.h:580
@ FairyLight
Definition crgb.h:613
@ Black
Definition crgb.h:468
const TProgmemRGBPalette16 PartyColors_p
HSV color ramp: blue, purple, pink, red, orange, yellow (and back).
const TProgmemRGBPalette16 RainbowColors_p
HSV Rainbow.
#define EVERY_N_MILLISECONDS(N)
Alias for EVERY_N_MILLIS.
Definition lib8tion.h:1351
#define EVERY_N_SECONDS(N)
Checks whether to execute a block of code every N seconds.
Definition lib8tion.h:1316
#define sin8
Platform-independent alias of the fast sin implementation.
Definition trig8.h:207
Representation of an RGB pixel (Red, Green, Blue)
Definition crgb.h:25