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