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