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