FastLED 3.9.15
Loading...
Searching...
No Matches
twinklefox.h
Go to the documentation of this file.
1#pragma once
2
3#include "fl/stl/string.h"
4#include "fl/fx/fx1d.h"
5
6namespace fl {
7
11
12// TwinkleFOX: Twinkling 'holiday' lights that fade in and out.
13// Colors are chosen from a palette; a few palettes are provided.
14//
15// This December 2015 implementation improves on the December 2014 version
16// in several ways:
17// - smoother fading, compatible with any colors and any palettes
18// - easier control of twinkle speed and twinkle density
19// - supports an optional 'background color'
20// - takes even less RAM: zero RAM overhead per pixel
21// - illustrates a couple of interesting techniques (uh oh...)
22//
23// The idea behind this (new) implementation is that there's one
24// basic, repeating pattern that each pixel follows like a waveform:
25// The brightness rises from 0..255 and then falls back down to 0.
26// The brightness at any given point in time can be determined as
27// as a function of time, for example:
28// brightness = sine( time ); // a sine wave of brightness over time
29//
30// So the way this implementation works is that every pixel follows
31// the exact same wave function over time. In this particular case,
32// I chose a sawtooth triangle wave (triwave8) rather than a sine wave,
33// but the idea is the same: brightness = triwave8( time ).
34//
35// The triangle wave function is a piecewise linear function that looks like:
36//
37// / \\
38// / \\
39// / \\
40// / \\
41//
42// Of course, if all the pixels used the exact same wave form, and
43// if they all used the exact same 'clock' for their 'time base', all
44// the pixels would brighten and dim at once -- which does not look
45// like twinkling at all.
46//
47// So to achieve random-looking twinkling, each pixel is given a
48// slightly different 'clock' signal. Some of the clocks run faster,
49// some run slower, and each 'clock' also has a random offset from zero.
50// The net result is that the 'clocks' for all the pixels are always out
51// of sync from each other, producing a nice random distribution
52// of twinkles.
53//
54// The 'clock speed adjustment' and 'time offset' for each pixel
55// are generated randomly. One (normal) approach to implementing that
56// would be to randomly generate the clock parameters for each pixel
57// at startup, and store them in some arrays. However, that consumes
58// a great deal of precious RAM, and it turns out to be totally
59// unnessary! If the random number generate is 'seeded' with the
60// same starting value every time, it will generate the same sequence
61// of values every time. So the clock adjustment parameters for each
62// pixel are 'stored' in a pseudo-random number generator! The PRNG
63// is reset, and then the first numbers out of it are the clock
64// adjustment parameters for the first pixel, the second numbers out
65// of it are the parameters for the second pixel, and so on.
66// In this way, we can 'store' a stable sequence of thousands of
67// random clock adjustment parameters in literally two bytes of RAM.
68//
69// There's a little bit of fixed-point math involved in applying the
70// clock speed adjustments, which are expressed in eighths. Each pixel's
71// clock speed ranges from 8/8ths of the system clock (i.e. 1x) to
72// 23/8ths of the system clock (i.e. nearly 3x).
73//
74// On a basic Arduino Uno or Leonardo, this code can twinkle 300+ pixels
75// smoothly at over 50 updates per seond.
76//
77// -Mark Kriegsman, December 2015
78
79// Adapted for FastLED 3.x in August 2023 by Marlin Unruh
80
81// TwinkleFox effect parameters
82// Overall twinkle speed.
83// 0 (VERY slow) to 8 (VERY fast).
84// 4, 5, and 6 are recommended, default is 4.
85#define TWINKLE_SPEED 4
86
87// Overall twinkle density.
88// 0 (NONE lit) to 8 (ALL lit at once).
89// Default is 5.
90#define TWINKLE_DENSITY 5
91
92// How often to change color palettes.
93#define SECONDS_PER_PALETTE 30
94
95// If AUTO_SELECT_BACKGROUND_COLOR is set to 1,
96// then for any palette where the first two entries
97// are the same, a dimmed version of that color will
98// automatically be used as the background color.
99#define AUTO_SELECT_BACKGROUND_COLOR 0
100
101// If COOL_LIKE_INCANDESCENT is set to 1, colors will
102// fade out slighted 'reddened', similar to how
103// incandescent bulbs change color as they get dim down.
104#define COOL_LIKE_INCANDESCENT 1
105
107
108class TwinkleFox : public Fx1d {
109 public:
110 CRGBPalette16 targetPalette;
111 CRGBPalette16 currentPalette;
112
120
121 void draw(DrawContext context) override {
124 }
125 drawTwinkleFox(context.leds);
126 }
127
128 void chooseNextColorPalette(CRGBPalette16 &pal);
129 fl::string fxName() const override { return "TwinkleFox"; }
130
131 private:
137
139 // "PRNG16" is the pseudorandom number generator
140 // It MUST be reset to the same starting value each time
141 // this function is called, so that the sequence of 'random'
142 // numbers that it generates is (paradoxically) stable.
143 u16 PRNG16 = 11337;
144 fl::u32 clock32 = fl::millis();
145
149 bg = currentPalette[0];
150 u8 bglight = bg.getAverageLight();
151 if (bglight > 64) {
152 bg.nscale8_video(16);
153 } else if (bglight > 16) {
154 bg.nscale8_video(64);
155 } else {
156 bg.nscale8_video(86);
157 }
158 }
159
160 u8 backgroundBrightness = bg.getAverageLight();
161
162 for (u16 i = 0; i < mNumLeds; i++) {
163 PRNG16 = (u16)(PRNG16 * 2053) + 1384;
164 u16 myclockoffset16 = PRNG16;
165 PRNG16 = (u16)(PRNG16 * 2053) + 1384;
166 u8 myspeedmultiplierQ5_3 =
167 ((((PRNG16 & 0xFF) >> 4) + (PRNG16 & 0x0F)) & 0x0F) + 0x08;
168 fl::u32 myclock30 =
169 (fl::u32)((clock32 * myspeedmultiplierQ5_3) >> 3) +
170 myclockoffset16;
171 u8 myunique8 = PRNG16 >> 8;
172
173 CRGB c = computeOneTwinkle(myclock30, myunique8);
174
175 u8 cbright = c.getAverageLight();
176 i16 deltabright = cbright - backgroundBrightness;
177 if (deltabright >= 32 || (!bg)) {
178 leds[i] = c;
179 } else if (deltabright > 0) {
180 leds[i] = blend(bg, c, deltabright * 8);
181 } else {
182 leds[i] = bg;
183 }
184 }
185 }
186
187 CRGB computeOneTwinkle(fl::u32 ms, u8 salt) {
188 u16 ticks = ms >> (8 - twinkleSpeed);
189 u8 fastcycle8 = ticks;
190 u16 slowcycle16 = (ticks >> 8) + salt;
191 slowcycle16 += sin8(slowcycle16);
192 slowcycle16 = (slowcycle16 * 2053) + 1384;
193 u8 slowcycle8 = (slowcycle16 & 0xFF) + (slowcycle16 >> 8);
194
195 u8 bright = 0;
196 if (((slowcycle8 & 0x0E) / 2) < twinkleDensity) {
197 bright = attackDecayWave8(fastcycle8);
198 }
199
200 u8 hue = slowcycle8 - salt;
201 CRGB c;
202 if (bright > 0) {
203 c = ColorFromPalette(currentPalette, hue, bright, NOBLEND);
205 coolLikeIncandescentFunction(c, fastcycle8);
206 }
207 } else {
208 c = CRGB::Black;
209 }
210 return c;
211 }
212
214 if (i < 86) {
215 return i * 3;
216 } else {
217 i -= 86;
218 return 255 - (i + (i / 2));
219 }
220 }
221
223 if (phase < 128)
224 return;
225
226 u8 cooling = (phase - 128) >> 4;
227 c.g = qsub8(c.g, cooling);
228 c.b = qsub8(c.b, cooling * 2);
229 }
230};
231
232// Color palettes
233// Color palette definitions
234// A mostly red palette with green accents and white trim.
235// "CRGB::Gray" is used as white to keep the brightness more uniform.
240
242 0x00580c, 0x00580c, 0x00580c, 0x00580c, 0x00580c, 0x00580c,
243 0x00580c, 0x00580c, 0x00580c, 0x00580c, 0x00580c, 0x00580c,
244 0x00580c, 0x00580c, 0x00580c, 0xB00402};
245
250
255
273
275 0x304048, 0x304048, 0x304048, 0x304048, 0x304048, 0x304048,
276 0x304048, 0x304048, 0x304048, 0x304048, 0x304048, 0x304048,
277 0x304048, 0x304048, 0x304048, 0xE0F0FF};
278
280 0xB80400, 0x902C02, 0xB80400, 0x902C02, 0x902C02, 0xB80400,
281 0x902C02, 0xB80400, 0x046002, 0x046002, 0x046002, 0x046002,
282 0x070758, 0x070758, 0x070758, 0x606820};
283
285 0x0C1040, 0x0C1040, 0x0C1040, 0x0C1040, 0x0C1040, 0x0C1040,
286 0x0C1040, 0x0C1040, 0x0C1040, 0x0C1040, 0x0C1040, 0x0C1040,
287 0x182080, 0x182080, 0x182080, 0x5080C0};
288
289// Add or remove palette names from this list to control which color
290// palettes are used, and in what order.
295
296void TwinkleFox::chooseNextColorPalette(CRGBPalette16 &pal) {
297 const u8 numberOfPalettes =
298 sizeof(ActivePaletteList) / sizeof(ActivePaletteList[0]);
299 static u8 whichPalette = -1; // okay static in header
300 whichPalette = addmod8(whichPalette, 1, numberOfPalettes);
301 pal = *(ActivePaletteList[whichPalette]);
302}
303
304} // namespace fl
fl::CRGB leds[NUM_LEDS]
uint8_t hue
Definition advanced.h:94
Fx1d(u16 numLeds)
Definition fx1d.h:12
u16 mNumLeds
Definition fx.h:53
TwinkleFox(u16 num_leds)
Definition twinklefox.h:113
bool autoSelectBackgroundColor
Definition twinklefox.h:136
u8 attackDecayWave8(u8 i)
Definition twinklefox.h:213
fl::string fxName() const override
Definition twinklefox.h:129
void chooseNextColorPalette(CRGBPalette16 &pal)
Definition twinklefox.h:296
CRGBPalette16 currentPalette
Definition twinklefox.h:111
void coolLikeIncandescentFunction(CRGB &c, u8 phase)
Definition twinklefox.h:222
bool coolLikeIncandescent
Definition twinklefox.h:135
void drawTwinkleFox(fl::span< CRGB > leds)
Definition twinklefox.h:138
CRGBPalette16 targetPalette
Definition twinklefox.h:110
void draw(DrawContext context) override
Definition twinklefox.h:121
CRGB computeOneTwinkle(fl::u32 ms, u8 salt)
Definition twinklefox.h:187
fl::u32 TProgmemRGBPalette16[16]
CRGBPalette16 entries stored in PROGMEM memory.
#define FL_PROGMEM
PROGMEM keyword for storage.
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:1045
unsigned char u8
Definition stdint.h:131
fl::CRGB CRGB
Definition video.h:15
const TProgmemRGBPalette16 * ActivePaletteList[]
Definition twinklefox.h:291
const TProgmemRGBPalette16 Snow_p
Definition twinklefox.h:274
fl::u32 millis()
Universal millisecond timer - returns milliseconds since system startup.
CRGB ColorFromPalette(const CRGBPalette16 &pal, fl::u8 index, fl::u8 brightness, TBlendType blendType)
const TProgmemRGBPalette16 RetroC9_p
Definition twinklefox.h:279
const TProgmemRGBPalette16 BlueWhite_p
Definition twinklefox.h:251
CRGB blend(const CRGB &p1, const CRGB &p2, fract8 amountOfP2)
const TProgmemRGBPalette16 RedGreenWhite_p
Definition twinklefox.h:236
const TProgmemRGBPalette16 RedWhite_p
Definition twinklefox.h:246
const TProgmemRGBPalette16 Ice_p
Definition twinklefox.h:284
const TProgmemRGBPalette16 FairyLight_p
Definition twinklefox.h:256
void nblendPaletteTowardPalette(CRGBPalette16 &current, CRGBPalette16 &target, fl::u8 maxChanges)
const TProgmemRGBPalette16 Holly_p
Definition twinklefox.h:241
Base definition for an LED controller.
Definition crgb.hpp:179
#define FASTLED_SHARED_PTR(type)
Definition shared_ptr.h:535
constexpr CRGB nscale8_constexpr(const CRGB scaledown) const FL_NOEXCEPT
Definition crgb.hpp:106
FASTLED_FORCE_INLINE u8 getAverageLight() const FL_NOEXCEPT
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:655
@ Green
<div style='background:#008000;width:4em;height:4em;'></div>
Definition crgb.h:558
@ Red
<div style='background:#FF0000;width:4em;height:4em;'></div>
Definition crgb.h:622
@ Blue
<div style='background:#0000FF;width:4em;height:4em;'></div>
Definition crgb.h:512
@ Gray
<div style='background:#808080;width:4em;height:4em;'></div>
Definition crgb.h:556
@ Black
<div style='background:#000000;width:4em;height:4em;'></div>
Definition crgb.h:510
FASTLED_FORCE_INLINE CRGB & nscale8_video(u8 scaledown) FL_NOEXCEPT
Scale down a RGB to N/256ths of it's current brightness using "video" dimming rules.
Definition crgb.hpp:72
constexpr u32 as_uint32_t() const FL_NOEXCEPT
Definition crgb.h:134
Representation of an 8-bit RGB pixel (Red, Green, Blue)
Definition crgb.h:38
fl::span< CRGB > leds
#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