v1.2版本改为通过xbox系列手柄进行遥控,并增加智能模式和手动模式
This commit is contained in:
1 parent
76603d3c31
commit
1a97bc32c3
1839 files changed
+478852
-3
No files matched your search
@@ -0,0 +1,124 @@
|
||||
# FastLED FX: 1D Effects
|
||||
|
||||
This folder contains one-dimensional (strip) visual effects. All effects derive from `fl::Fx1d` and render into a linear `CRGB* leds` buffer using a common `DrawContext`.
|
||||
|
||||
If you’re new to FastLED and C++: think of an effect as a small object you construct once and then call `draw(...)` every frame with the current time and your LED buffer.
|
||||
|
||||
### What’s here
|
||||
- **Cylon (`cylon.h`)**: A single dot sweeps back and forth (Larson scanner). Tunable `fade_amount`, `delay_ms`.
|
||||
- **DemoReel100 (`demoreel100.h`)**: Classic demo with rotating patterns (rainbow, glitter, confetti, sinelon, bpm, juggle). Auto-advances every ~10 seconds.
|
||||
- **Fire2012 (`fire2012.h`)**: Procedural fire simulation with parameters `cooling` and `sparking`, optional reverse direction and palette.
|
||||
- **NoiseWave (`noisewave.h`)**: Smooth noise-driven red/blue waves across the strip.
|
||||
- **Pacifica (`pacifica.h`)**: Gentle blue-green ocean waves layered for depth.
|
||||
- **Pride2015 (`pride2015.h`)**: Ever-changing rainbow animation.
|
||||
- **TwinkleFox (`twinklefox.h`)**: Twinkling holiday-style lights using palettes; compile-time knobs for speed/density and incandescent-style cooling.
|
||||
|
||||
### Common usage
|
||||
- Include the header for the effect you want and construct it with the LED count (e.g., `fl::Cylon fx(num_leds);`).
|
||||
- Each frame (e.g., in your loop), call `fx.draw(fl::Fx::DrawContext(now, leds));`.
|
||||
- Most effects expose small parameters via constructor (e.g., `Fire2012(num_leds, cooling, sparking)`), or have internal timing based on `now` from `DrawContext`.
|
||||
|
||||
### Notes and tips
|
||||
- Effects guard against `nullptr` and zero length, but you should pass a valid `CRGB*` and correct `num_leds` to see output.
|
||||
- Try different frame rates. Many patterns look good at ~50–100 FPS, but 30 FPS is fine for most.
|
||||
- For palette-based effects (e.g., TwinkleFox, Pride2015, DemoReel’s BPM), experiment with `CRGBPalette16` to customize colors.
|
||||
- These 1D effects are lightweight to medium-weight. They depend on FastLED helpers such as `CHSV`, `beatsin*`, `nblend`, palettes, and random noise.
|
||||
|
||||
### Key types referenced
|
||||
- **`fl::Fx1d`**: Base class for 1D effects.
|
||||
- **`fl::Fx::DrawContext`**: Carries `now` (time in ms), `CRGB* leds`, and optional per-frame hints.
|
||||
- **`CRGB` / `CHSV`**: FastLED’s color types.
|
||||
|
||||
Explore the headers to see per-effect parameters and internal logic; everything here is self-contained and ready to drop into your render loop.
|
||||
|
||||
### Examples
|
||||
- Cylon (from `examples/FxCylon/FxCylon.ino`):
|
||||
```cpp
|
||||
#include <FastLED.h>
|
||||
#include "fx/1d/cylon.h"
|
||||
using namespace fl;
|
||||
#define NUM_LEDS 64
|
||||
CRGB leds[NUM_LEDS];
|
||||
Cylon cylon(NUM_LEDS);
|
||||
void setup() {
|
||||
FastLED.addLeds<WS2812, 2, RGB>(leds, NUM_LEDS);
|
||||
}
|
||||
void loop() {
|
||||
cylon.draw(Fx::DrawContext(millis(), leds));
|
||||
FastLED.show();
|
||||
delay(cylon.delay_ms);
|
||||
}
|
||||
```
|
||||
- DemoReel100 (from `examples/FxDemoReel100/FxDemoReel100.ino`):
|
||||
```cpp
|
||||
#include <FastLED.h>
|
||||
#include "fx/1d/demoreel100.h"
|
||||
using namespace fl;
|
||||
#define NUM_LEDS 64
|
||||
CRGB leds[NUM_LEDS];
|
||||
DemoReel100Ptr fx = fl::make_shared<DemoReel100>(NUM_LEDS);
|
||||
void setup() { FastLED.addLeds<WS2811, 3, GRB>(leds, NUM_LEDS); }
|
||||
void loop() {
|
||||
fx->draw(Fx::DrawContext(millis(), leds));
|
||||
FastLED.show();
|
||||
FastLED.delay(1000/120);
|
||||
}
|
||||
```
|
||||
- Fire2012 (from `examples/FxFire2012/FxFire2012.ino`):
|
||||
```cpp
|
||||
#include <FastLED.h>
|
||||
#include "fx/1d/fire2012.h"
|
||||
using namespace fl;
|
||||
#define NUM_LEDS 92
|
||||
CRGB leds[NUM_LEDS];
|
||||
Fire2012Ptr fire = fl::make_shared<Fire2012>(NUM_LEDS, 55, 120, false);
|
||||
void setup() { FastLED.addLeds<WS2811, 5, GRB>(leds, NUM_LEDS); }
|
||||
void loop() {
|
||||
fire->draw(Fx::DrawContext(millis(), leds));
|
||||
FastLED.show();
|
||||
FastLED.delay(1000/30);
|
||||
}
|
||||
```
|
||||
- Pacifica (from `examples/FxPacifica/FxPacifica.ino`):
|
||||
```cpp
|
||||
#include <FastLED.h>
|
||||
#include "fx/1d/pacifica.h"
|
||||
using namespace fl;
|
||||
#define NUM_LEDS 60
|
||||
CRGB leds[NUM_LEDS];
|
||||
Pacifica pacifica(NUM_LEDS);
|
||||
void setup() { FastLED.addLeds<WS2812B, 3, GRB>(leds, NUM_LEDS); }
|
||||
void loop() {
|
||||
pacifica.draw(Fx::DrawContext(millis(), leds));
|
||||
FastLED.show();
|
||||
}
|
||||
```
|
||||
- Pride2015 (from `examples/FxPride2015/FxPride2015.ino`):
|
||||
```cpp
|
||||
#include <FastLED.h>
|
||||
#include "fx/1d/pride2015.h"
|
||||
using namespace fl;
|
||||
#define NUM_LEDS 200
|
||||
CRGB leds[NUM_LEDS];
|
||||
Pride2015 pride(NUM_LEDS);
|
||||
void setup() { FastLED.addLeds<WS2811, 3, GRB>(leds, NUM_LEDS); }
|
||||
void loop() {
|
||||
pride.draw(Fx::DrawContext(millis(), leds));
|
||||
FastLED.show();
|
||||
}
|
||||
```
|
||||
- TwinkleFox (from `examples/FxTwinkleFox/FxTwinkleFox.ino`):
|
||||
```cpp
|
||||
#include <FastLED.h>
|
||||
#include "fx/1d/twinklefox.h"
|
||||
using namespace fl;
|
||||
#define NUM_LEDS 100
|
||||
CRGB leds[NUM_LEDS];
|
||||
TwinkleFox fx(NUM_LEDS);
|
||||
void setup() { FastLED.addLeds<WS2811, 3, GRB>(leds, NUM_LEDS).setRgbw(); }
|
||||
void loop() {
|
||||
EVERY_N_SECONDS(SECONDS_PER_PALETTE) { fx.chooseNextColorPalette(fx.targetPalette); }
|
||||
fx.draw(Fx::DrawContext(millis(), leds));
|
||||
FastLED.show();
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "fx/fx1d.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
FASTLED_SMART_PTR(Cylon);
|
||||
|
||||
/// @brief An animation that moves a single LED back and forth (Larson Scanner
|
||||
/// effect)
|
||||
class Cylon : public Fx1d {
|
||||
public:
|
||||
uint8_t delay_ms;
|
||||
Cylon(uint16_t num_leds, uint8_t fade_amount = 250, uint8_t delay_ms = 10)
|
||||
: Fx1d(num_leds), delay_ms(delay_ms), fade_amount(fade_amount) {}
|
||||
|
||||
void draw(DrawContext context) override {
|
||||
if (context.leds == nullptr || mNumLeds == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
CRGB *leds = context.leds;
|
||||
|
||||
// Set the current LED to the current hue
|
||||
leds[position] = CHSV(hue++, 255, 255);
|
||||
|
||||
// Fade all LEDs
|
||||
for (uint16_t i = 0; i < mNumLeds; i++) {
|
||||
leds[i].nscale8(fade_amount);
|
||||
}
|
||||
|
||||
// Move the position
|
||||
if (reverse) {
|
||||
position--;
|
||||
if (position < 0) {
|
||||
position = 1;
|
||||
reverse = false;
|
||||
}
|
||||
} else {
|
||||
position++;
|
||||
if (position >= int16_t(mNumLeds)) {
|
||||
position = mNumLeds - 2;
|
||||
reverse = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fl::string fxName() const override { return "Cylon"; }
|
||||
|
||||
private:
|
||||
uint8_t hue = 0;
|
||||
|
||||
uint8_t fade_amount;
|
||||
|
||||
bool reverse = false;
|
||||
int16_t position = 0;
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
@@ -0,0 +1,132 @@
|
||||
#pragma once
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "fx/fx1d.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
// FastLED "100-lines-of-code" demo reel, showing just a few
|
||||
// of the kinds of animation patterns you can quickly and easily
|
||||
// compose using FastLED.
|
||||
//
|
||||
// This example also shows one easy way to define multiple
|
||||
// animations patterns and have them automatically rotate.
|
||||
//
|
||||
// -Mark Kriegsman, December 2014
|
||||
|
||||
FASTLED_SMART_PTR(DemoReel100);
|
||||
|
||||
class DemoReel100 : public Fx1d {
|
||||
public:
|
||||
DemoReel100(uint16_t num_leds) : Fx1d(num_leds) {}
|
||||
|
||||
void draw(DrawContext context) override {
|
||||
CRGB *leds = context.leds;
|
||||
if (leds == nullptr || mNumLeds == 0) {
|
||||
return;
|
||||
}
|
||||
if (start_time == 0) {
|
||||
start_time = millis();
|
||||
}
|
||||
|
||||
// Call the current pattern function once, updating the 'leds' array
|
||||
runPattern(leds);
|
||||
|
||||
// do some periodic updates
|
||||
EVERY_N_MILLISECONDS(20) {
|
||||
hue++;
|
||||
} // slowly cycle the "base color" through the rainbow
|
||||
EVERY_N_SECONDS(10) { nextPattern(); } // change patterns periodically
|
||||
}
|
||||
|
||||
fl::string fxName() const override { return "DemoReel100"; }
|
||||
|
||||
private:
|
||||
uint8_t current_pattern_number = 0;
|
||||
uint8_t hue = 0;
|
||||
unsigned long start_time = 0;
|
||||
|
||||
void nextPattern() {
|
||||
// add one to the current pattern number, and wrap around at the end
|
||||
current_pattern_number =
|
||||
(current_pattern_number + 1) % 6; // 6 is the number of patterns
|
||||
}
|
||||
|
||||
void runPattern(CRGB *leds) {
|
||||
switch (current_pattern_number) {
|
||||
case 0:
|
||||
rainbow(leds);
|
||||
break;
|
||||
case 1:
|
||||
rainbowWithGlitter(leds);
|
||||
break;
|
||||
case 2:
|
||||
confetti(leds);
|
||||
break;
|
||||
case 3:
|
||||
sinelon(leds);
|
||||
break;
|
||||
case 4:
|
||||
juggle(leds);
|
||||
break;
|
||||
case 5:
|
||||
bpm(leds);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void rainbow(CRGB *leds) {
|
||||
// FastLED's built-in rainbow generator
|
||||
fill_rainbow(leds, mNumLeds, hue, 7);
|
||||
}
|
||||
|
||||
void rainbowWithGlitter(CRGB *leds) {
|
||||
// built-in FastLED rainbow, plus some random sparkly glitter
|
||||
rainbow(leds);
|
||||
addGlitter(80, leds);
|
||||
}
|
||||
|
||||
void addGlitter(fract8 chanceOfGlitter, CRGB *leds) {
|
||||
if (random8() < chanceOfGlitter) {
|
||||
leds[random16(mNumLeds)] += CRGB::White;
|
||||
}
|
||||
}
|
||||
|
||||
void confetti(CRGB *leds) {
|
||||
// random colored speckles that blink in and fade smoothly
|
||||
fadeToBlackBy(leds, mNumLeds, 10);
|
||||
int pos = random16(mNumLeds);
|
||||
leds[pos] += CHSV(hue + random8(64), 200, 255);
|
||||
}
|
||||
|
||||
void sinelon(CRGB *leds) {
|
||||
// a colored dot sweeping back and forth, with fading trails
|
||||
fadeToBlackBy(leds, mNumLeds, 20);
|
||||
int pos = beatsin16(13, 0, mNumLeds - 1);
|
||||
leds[pos] += CHSV(hue, 255, 192);
|
||||
}
|
||||
|
||||
void bpm(CRGB *leds) {
|
||||
// colored stripes pulsing at a defined Beats-Per-Minute (BPM)
|
||||
uint8_t BeatsPerMinute = 62;
|
||||
CRGBPalette16 palette = PartyColors_p;
|
||||
uint8_t beat = beatsin8(BeatsPerMinute, 64, 255);
|
||||
for (uint16_t i = 0; i < mNumLeds; i++) {
|
||||
leds[i] =
|
||||
ColorFromPalette(palette, hue + (i * 2), beat - hue + (i * 10));
|
||||
}
|
||||
}
|
||||
|
||||
void juggle(CRGB *leds) {
|
||||
// eight colored dots, weaving in and out of sync with each other
|
||||
fadeToBlackBy(leds, mNumLeds, 20);
|
||||
uint8_t dothue = 0;
|
||||
for (uint16_t i = 0; i < 8; i++) {
|
||||
leds[beatsin16(i + 7, 0, mNumLeds - 1)] |= CHSV(dothue, 200, 255);
|
||||
dothue += 32;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
@@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/vector.h"
|
||||
#include "fx/fx1d.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
/// @brief Simple one-dimensional fire animation function
|
||||
// Fire2012 by Mark Kriegsman, July 2012
|
||||
// as part of "Five Elements" shown here: http://youtu.be/knWiGsmgycY
|
||||
////
|
||||
// This basic one-dimensional 'fire' simulation works roughly as follows:
|
||||
// There's a underlying array of 'heat' cells, that model the temperature
|
||||
// at each point along the line. Every cycle through the simulation,
|
||||
// four steps are performed:
|
||||
// 1) All cells cool down a little bit, losing heat to the air
|
||||
// 2) The heat from each cell drifts 'up' and diffuses a little
|
||||
// 3) Sometimes randomly new 'sparks' of heat are added at the bottom
|
||||
// 4) The heat from each cell is rendered as a color into the leds array
|
||||
// The heat-to-color mapping uses a black-body radiation approximation.
|
||||
//
|
||||
// Temperature is in arbitrary units from 0 (cold black) to 255 (white hot).
|
||||
//
|
||||
// This simulation scales it self a bit depending on NUM_LEDS; it should look
|
||||
// "OK" on anywhere from 20 to 100 LEDs without too much tweaking.
|
||||
//
|
||||
// I recommend running this simulation at anywhere from 30-100 frames per
|
||||
// second, meaning an interframe delay of about 10-35 milliseconds.
|
||||
//
|
||||
// Looks best on a high-density LED setup (60+ pixels/meter).
|
||||
//
|
||||
//
|
||||
// There are two main parameters you can play with to control the look and
|
||||
// feel of your fire: COOLING (used in step 1 above), and SPARKING (used
|
||||
// in step 3 above).
|
||||
//
|
||||
// COOLING: How much does the air cool as it rises?
|
||||
// Less cooling = taller flames. More cooling = shorter flames.
|
||||
// Default 50, suggested range 20-100
|
||||
|
||||
// SPARKING: What chance (out of 255) is there that a new spark will be lit?
|
||||
// Higher chance = more roaring fire. Lower chance = more flickery fire.
|
||||
// Default 120, suggested range 50-200.
|
||||
|
||||
FASTLED_SMART_PTR(Fire2012);
|
||||
|
||||
class Fire2012 : public Fx1d {
|
||||
public:
|
||||
Fire2012(uint16_t num_leds, uint8_t cooling = 55, uint8_t sparking = 120,
|
||||
bool reverse_direction = false,
|
||||
const CRGBPalette16 &palette = (const CRGBPalette16 &)HeatColors_p)
|
||||
: Fx1d(num_leds), cooling(cooling), sparking(sparking),
|
||||
reverse_direction(reverse_direction), palette(palette) {
|
||||
heat.resize(num_leds); // Vector elements are default-initialized
|
||||
}
|
||||
|
||||
~Fire2012() {}
|
||||
|
||||
void draw(DrawContext context) override {
|
||||
CRGB *leds = context.leds;
|
||||
if (leds == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1. Cool down every cell a little
|
||||
for (uint16_t i = 0; i < mNumLeds; i++) {
|
||||
heat[i] =
|
||||
qsub8(heat[i], random8(0, ((cooling * 10) / mNumLeds) + 2));
|
||||
}
|
||||
|
||||
// Step 2. Heat from each cell drifts 'up' and diffuses a little
|
||||
for (uint16_t k = mNumLeds - 1; k >= 2; k--) {
|
||||
heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;
|
||||
}
|
||||
|
||||
// Step 3. Randomly ignite new 'sparks' of heat near the bottom
|
||||
if (random8() < sparking) {
|
||||
int y = random8(7);
|
||||
heat[y] = qadd8(heat[y], random8(160, 255));
|
||||
}
|
||||
|
||||
// Step 4. Map from heat cells to LED colors
|
||||
for (uint16_t j = 0; j < mNumLeds; j++) {
|
||||
// Scale the heat value from 0-255 down to 0-240
|
||||
// for best results with color palettes.
|
||||
uint8_t colorindex = scale8(heat[j], 240);
|
||||
CRGB color = ColorFromPalette(palette, colorindex);
|
||||
int pixelnumber;
|
||||
if (reverse_direction) {
|
||||
pixelnumber = (mNumLeds - 1) - j;
|
||||
} else {
|
||||
pixelnumber = j;
|
||||
}
|
||||
leds[pixelnumber] = color;
|
||||
}
|
||||
}
|
||||
|
||||
fl::string fxName() const override { return "Fire2012"; }
|
||||
|
||||
private:
|
||||
fl::vector<uint8_t, fl::allocator_psram<uint8_t>> heat;
|
||||
uint8_t cooling;
|
||||
uint8_t sparking;
|
||||
bool reverse_direction;
|
||||
CRGBPalette16 palette;
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "fx/fx1d.h"
|
||||
#include "noisegen.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
FASTLED_SMART_PTR(NoiseWave);
|
||||
|
||||
class NoiseWave : public Fx1d {
|
||||
public:
|
||||
NoiseWave(uint16_t num_leds)
|
||||
: Fx1d(num_leds), noiseGeneratorRed(500, 14),
|
||||
noiseGeneratorBlue(500, 10) {}
|
||||
|
||||
void draw(DrawContext context) override {
|
||||
if (context.leds == nullptr || mNumLeds == 0) {
|
||||
return;
|
||||
}
|
||||
if (start_time == 0) {
|
||||
start_time = context.now;
|
||||
}
|
||||
|
||||
unsigned long time_now = millis() - start_time;
|
||||
|
||||
for (int32_t i = 0; i < mNumLeds; ++i) {
|
||||
int r = noiseGeneratorRed.LedValue(i, time_now);
|
||||
int b = noiseGeneratorBlue.LedValue(i, time_now + 100000) >> 1;
|
||||
int g = 0;
|
||||
context.leds[i] = CRGB(r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
fl::string fxName() const override { return "NoiseWave"; }
|
||||
|
||||
private:
|
||||
NoiseGenerator noiseGeneratorRed;
|
||||
NoiseGenerator noiseGeneratorBlue;
|
||||
fl::u32 start_time = 0;
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
@@ -0,0 +1,135 @@
|
||||
#pragma once
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "fx/fx1d.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
/// @file pacifica.hpp
|
||||
/// @brief An animation that simulates gentle, blue-green ocean waves
|
||||
/// @example Pacifica.ino
|
||||
|
||||
FASTLED_SMART_PTR(Pacifica);
|
||||
|
||||
class Pacifica : public Fx1d {
|
||||
public:
|
||||
Pacifica(uint16_t num_leds) : Fx1d(num_leds) {}
|
||||
|
||||
void draw(DrawContext context) override;
|
||||
fl::string fxName() const override { return "Pacifica"; }
|
||||
|
||||
private:
|
||||
uint16_t sCIStart1 = 0, sCIStart2 = 0, sCIStart3 = 0, sCIStart4 = 0;
|
||||
fl::u32 sLastms = 0;
|
||||
|
||||
CRGBPalette16 pacifica_palette_1 = {0x000507, 0x000409, 0x00030B, 0x00030D,
|
||||
0x000210, 0x000212, 0x000114, 0x000117,
|
||||
0x000019, 0x00001C, 0x000026, 0x000031,
|
||||
0x00003B, 0x000046, 0x14554B, 0x28AA50};
|
||||
CRGBPalette16 pacifica_palette_2 = {0x000507, 0x000409, 0x00030B, 0x00030D,
|
||||
0x000210, 0x000212, 0x000114, 0x000117,
|
||||
0x000019, 0x00001C, 0x000026, 0x000031,
|
||||
0x00003B, 0x000046, 0x0C5F52, 0x19BE5F};
|
||||
CRGBPalette16 pacifica_palette_3 = {0x000208, 0x00030E, 0x000514, 0x00061A,
|
||||
0x000820, 0x000927, 0x000B2D, 0x000C33,
|
||||
0x000E39, 0x001040, 0x001450, 0x001860,
|
||||
0x001C70, 0x002080, 0x1040BF, 0x2060FF};
|
||||
|
||||
void pacifica_one_layer(CRGB *leds, CRGBPalette16 &p, uint16_t cistart,
|
||||
uint16_t wavescale, uint8_t bri, uint16_t ioff);
|
||||
void pacifica_add_whitecaps(CRGB *leds);
|
||||
void pacifica_deepen_colors(CRGB *leds);
|
||||
};
|
||||
|
||||
void Pacifica::draw(DrawContext ctx) {
|
||||
CRGB *leds = ctx.leds;
|
||||
fl::u32 now = ctx.now;
|
||||
if (leds == nullptr || mNumLeds == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the hue each time through the loop
|
||||
fl::u32 ms = now;
|
||||
fl::u32 deltams = ms - sLastms;
|
||||
sLastms = ms;
|
||||
uint16_t speedfactor1 = beatsin16(3, 179, 269);
|
||||
uint16_t speedfactor2 = beatsin16(4, 179, 269);
|
||||
fl::u32 deltams1 = (deltams * speedfactor1) / 256;
|
||||
fl::u32 deltams2 = (deltams * speedfactor2) / 256;
|
||||
fl::u32 deltams21 = (deltams1 + deltams2) / 2;
|
||||
sCIStart1 += (deltams1 * beatsin88(1011, 10, 13));
|
||||
sCIStart2 -= (deltams21 * beatsin88(777, 8, 11));
|
||||
sCIStart3 -= (deltams1 * beatsin88(501, 5, 7));
|
||||
sCIStart4 -= (deltams2 * beatsin88(257, 4, 6));
|
||||
|
||||
// Clear out the LED array to a dim background blue-green
|
||||
fill_solid(leds, mNumLeds, CRGB(2, 6, 10));
|
||||
|
||||
// Render each of four layers, with different scales and speeds, that vary
|
||||
// over time
|
||||
pacifica_one_layer(leds, pacifica_palette_1, sCIStart1,
|
||||
beatsin16(3, 11 * 256, 14 * 256), beatsin8(10, 70, 130),
|
||||
0 - beat16(301));
|
||||
pacifica_one_layer(leds, pacifica_palette_2, sCIStart2,
|
||||
beatsin16(4, 6 * 256, 9 * 256), beatsin8(17, 40, 80),
|
||||
beat16(401));
|
||||
pacifica_one_layer(leds, pacifica_palette_3, sCIStart3, 6 * 256,
|
||||
beatsin8(9, 10, 38), 0 - beat16(503));
|
||||
pacifica_one_layer(leds, pacifica_palette_3, sCIStart4, 5 * 256,
|
||||
beatsin8(8, 10, 28), beat16(601));
|
||||
|
||||
// Add brighter 'whitecaps' where the waves lines up more
|
||||
pacifica_add_whitecaps(leds);
|
||||
|
||||
// Deepen the blues and greens a bit
|
||||
pacifica_deepen_colors(leds);
|
||||
}
|
||||
|
||||
// Add one layer of waves into the led array
|
||||
void Pacifica::pacifica_one_layer(CRGB *leds, CRGBPalette16 &p,
|
||||
uint16_t cistart, uint16_t wavescale,
|
||||
uint8_t bri, uint16_t ioff) {
|
||||
uint16_t ci = cistart;
|
||||
uint16_t waveangle = ioff;
|
||||
uint16_t wavescale_half = (wavescale / 2) + 20;
|
||||
for (uint16_t i = 0; i < mNumLeds; i++) {
|
||||
waveangle += 250;
|
||||
uint16_t s16 = sin16(waveangle) + 32768;
|
||||
uint16_t cs = scale16(s16, wavescale_half) + wavescale_half;
|
||||
ci += cs;
|
||||
uint16_t sindex16 = sin16(ci) + 32768;
|
||||
uint8_t sindex8 = scale16(sindex16, 240);
|
||||
CRGB c = ColorFromPalette(p, sindex8, bri, LINEARBLEND);
|
||||
leds[i] += c;
|
||||
}
|
||||
}
|
||||
|
||||
// Add extra 'white' to areas where the four layers of light have lined up
|
||||
// brightly
|
||||
void Pacifica::pacifica_add_whitecaps(CRGB *leds) {
|
||||
uint8_t basethreshold = beatsin8(9, 55, 65);
|
||||
uint8_t wave = beat8(7);
|
||||
|
||||
for (uint16_t i = 0; i < mNumLeds; i++) {
|
||||
uint8_t threshold = scale8(sin8(wave), 20) + basethreshold;
|
||||
wave += 7;
|
||||
uint8_t l = leds[i].getAverageLight();
|
||||
if (l > threshold) {
|
||||
uint8_t overage = l - threshold;
|
||||
uint8_t overage2 = qadd8(overage, overage);
|
||||
leds[i] += CRGB(overage, overage2, qadd8(overage2, overage2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deepen the blues and greens
|
||||
void Pacifica::pacifica_deepen_colors(CRGB *leds) {
|
||||
for (uint16_t i = 0; i < mNumLeds; i++) {
|
||||
leds[i].blue = scale8(leds[i].blue, 145);
|
||||
leds[i].green = scale8(leds[i].green, 200);
|
||||
leds[i] |= CRGB(2, 5, 7);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "fx/fx1d.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
/// @file pride2015.hpp
|
||||
/// @brief Animated, ever-changing rainbows (Pride2015 effect)
|
||||
/// @example Pride2015.ino
|
||||
|
||||
// Pride2015
|
||||
// Animated, ever-changing rainbows.
|
||||
// by Mark Kriegsman
|
||||
|
||||
FASTLED_SMART_PTR(Pride2015);
|
||||
|
||||
class Pride2015 : public Fx1d {
|
||||
public:
|
||||
Pride2015(uint16_t num_leds) : Fx1d(num_leds) {}
|
||||
|
||||
void draw(Fx::DrawContext context) override;
|
||||
fl::string fxName() const override { return "Pride2015"; }
|
||||
|
||||
private:
|
||||
uint16_t mPseudotime = 0;
|
||||
uint16_t mLastMillis = 0;
|
||||
uint16_t mHue16 = 0;
|
||||
};
|
||||
|
||||
// This function draws rainbows with an ever-changing,
|
||||
// widely-varying set of parameters.
|
||||
void Pride2015::draw(Fx::DrawContext ctx) {
|
||||
if (ctx.leds == nullptr || mNumLeds == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t sat8 = beatsin88(87, 220, 250);
|
||||
uint8_t brightdepth = beatsin88(341, 96, 224);
|
||||
uint16_t brightnessthetainc16 = beatsin88(203, (25 * 256), (40 * 256));
|
||||
uint8_t msmultiplier = beatsin88(147, 23, 60);
|
||||
|
||||
uint16_t hue16 = mHue16;
|
||||
uint16_t hueinc16 = beatsin88(113, 1, 3000);
|
||||
|
||||
uint16_t ms = millis();
|
||||
uint16_t deltams = ms - mLastMillis;
|
||||
mLastMillis = ms;
|
||||
mPseudotime += deltams * msmultiplier;
|
||||
mHue16 += deltams * beatsin88(400, 5, 9);
|
||||
uint16_t brightnesstheta16 = mPseudotime;
|
||||
|
||||
// set master brightness control
|
||||
for (uint16_t i = 0; i < mNumLeds; i++) {
|
||||
hue16 += hueinc16;
|
||||
uint8_t hue8 = hue16 / 256;
|
||||
|
||||
brightnesstheta16 += brightnessthetainc16;
|
||||
uint16_t b16 = sin16(brightnesstheta16) + 32768;
|
||||
|
||||
uint16_t bri16 = (fl::u32)((fl::u32)b16 * (fl::u32)b16) / 65536;
|
||||
uint8_t bri8 = (fl::u32)(((fl::u32)bri16) * brightdepth) / 65536;
|
||||
bri8 += (255 - brightdepth);
|
||||
|
||||
CRGB newcolor = CHSV(hue8, sat8, bri8);
|
||||
|
||||
uint16_t pixelnumber = (mNumLeds - 1) - i;
|
||||
|
||||
nblend(ctx.leds[pixelnumber], newcolor, 64);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
@@ -0,0 +1,306 @@
|
||||
#pragma once
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/str.h"
|
||||
#include "fx/fx1d.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
/// @file TwinkleFox.hpp
|
||||
/// @brief Twinkling "holiday" lights that fade in and out.
|
||||
/// @example TwinkleFox.ino
|
||||
|
||||
// TwinkleFOX: Twinkling 'holiday' lights that fade in and out.
|
||||
// Colors are chosen from a palette; a few palettes are provided.
|
||||
//
|
||||
// This December 2015 implementation improves on the December 2014 version
|
||||
// in several ways:
|
||||
// - smoother fading, compatible with any colors and any palettes
|
||||
// - easier control of twinkle speed and twinkle density
|
||||
// - supports an optional 'background color'
|
||||
// - takes even less RAM: zero RAM overhead per pixel
|
||||
// - illustrates a couple of interesting techniques (uh oh...)
|
||||
//
|
||||
// The idea behind this (new) implementation is that there's one
|
||||
// basic, repeating pattern that each pixel follows like a waveform:
|
||||
// The brightness rises from 0..255 and then falls back down to 0.
|
||||
// The brightness at any given point in time can be determined as
|
||||
// as a function of time, for example:
|
||||
// brightness = sine( time ); // a sine wave of brightness over time
|
||||
//
|
||||
// So the way this implementation works is that every pixel follows
|
||||
// the exact same wave function over time. In this particular case,
|
||||
// I chose a sawtooth triangle wave (triwave8) rather than a sine wave,
|
||||
// but the idea is the same: brightness = triwave8( time ).
|
||||
//
|
||||
// The triangle wave function is a piecewise linear function that looks like:
|
||||
//
|
||||
// / \\
|
||||
// / \\
|
||||
// / \\
|
||||
// / \\
|
||||
//
|
||||
// Of course, if all the pixels used the exact same wave form, and
|
||||
// if they all used the exact same 'clock' for their 'time base', all
|
||||
// the pixels would brighten and dim at once -- which does not look
|
||||
// like twinkling at all.
|
||||
//
|
||||
// So to achieve random-looking twinkling, each pixel is given a
|
||||
// slightly different 'clock' signal. Some of the clocks run faster,
|
||||
// some run slower, and each 'clock' also has a random offset from zero.
|
||||
// The net result is that the 'clocks' for all the pixels are always out
|
||||
// of sync from each other, producing a nice random distribution
|
||||
// of twinkles.
|
||||
//
|
||||
// The 'clock speed adjustment' and 'time offset' for each pixel
|
||||
// are generated randomly. One (normal) approach to implementing that
|
||||
// would be to randomly generate the clock parameters for each pixel
|
||||
// at startup, and store them in some arrays. However, that consumes
|
||||
// a great deal of precious RAM, and it turns out to be totally
|
||||
// unnessary! If the random number generate is 'seeded' with the
|
||||
// same starting value every time, it will generate the same sequence
|
||||
// of values every time. So the clock adjustment parameters for each
|
||||
// pixel are 'stored' in a pseudo-random number generator! The PRNG
|
||||
// is reset, and then the first numbers out of it are the clock
|
||||
// adjustment parameters for the first pixel, the second numbers out
|
||||
// of it are the parameters for the second pixel, and so on.
|
||||
// In this way, we can 'store' a stable sequence of thousands of
|
||||
// random clock adjustment parameters in literally two bytes of RAM.
|
||||
//
|
||||
// There's a little bit of fixed-point math involved in applying the
|
||||
// clock speed adjustments, which are expressed in eighths. Each pixel's
|
||||
// clock speed ranges from 8/8ths of the system clock (i.e. 1x) to
|
||||
// 23/8ths of the system clock (i.e. nearly 3x).
|
||||
//
|
||||
// On a basic Arduino Uno or Leonardo, this code can twinkle 300+ pixels
|
||||
// smoothly at over 50 updates per seond.
|
||||
//
|
||||
// -Mark Kriegsman, December 2015
|
||||
|
||||
// Adapted for FastLED 3.x in August 2023 by Marlin Unruh
|
||||
|
||||
// TwinkleFox effect parameters
|
||||
// Overall twinkle speed.
|
||||
// 0 (VERY slow) to 8 (VERY fast).
|
||||
// 4, 5, and 6 are recommended, default is 4.
|
||||
#define TWINKLE_SPEED 4
|
||||
|
||||
// Overall twinkle density.
|
||||
// 0 (NONE lit) to 8 (ALL lit at once).
|
||||
// Default is 5.
|
||||
#define TWINKLE_DENSITY 5
|
||||
|
||||
// How often to change color palettes.
|
||||
#define SECONDS_PER_PALETTE 30
|
||||
|
||||
// If AUTO_SELECT_BACKGROUND_COLOR is set to 1,
|
||||
// then for any palette where the first two entries
|
||||
// are the same, a dimmed version of that color will
|
||||
// automatically be used as the background color.
|
||||
#define AUTO_SELECT_BACKGROUND_COLOR 0
|
||||
|
||||
// If COOL_LIKE_INCANDESCENT is set to 1, colors will
|
||||
// fade out slighted 'reddened', similar to how
|
||||
// incandescent bulbs change color as they get dim down.
|
||||
#define COOL_LIKE_INCANDESCENT 1
|
||||
|
||||
FASTLED_SMART_PTR(TwinkleFox);
|
||||
|
||||
class TwinkleFox : public Fx1d {
|
||||
public:
|
||||
CRGBPalette16 targetPalette;
|
||||
CRGBPalette16 currentPalette;
|
||||
|
||||
TwinkleFox(uint16_t num_leds)
|
||||
: Fx1d(num_leds), backgroundColor(CRGB::Black),
|
||||
twinkleSpeed(TWINKLE_SPEED), twinkleDensity(TWINKLE_DENSITY),
|
||||
coolLikeIncandescent(COOL_LIKE_INCANDESCENT),
|
||||
autoSelectBackgroundColor(AUTO_SELECT_BACKGROUND_COLOR) {
|
||||
chooseNextColorPalette(targetPalette);
|
||||
}
|
||||
|
||||
void draw(DrawContext context) override {
|
||||
EVERY_N_MILLISECONDS(10) {
|
||||
nblendPaletteTowardPalette(currentPalette, targetPalette, 12);
|
||||
}
|
||||
drawTwinkleFox(context.leds);
|
||||
}
|
||||
|
||||
void chooseNextColorPalette(CRGBPalette16 &pal);
|
||||
fl::string fxName() const override { return "TwinkleFox"; }
|
||||
|
||||
private:
|
||||
CRGB backgroundColor;
|
||||
uint8_t twinkleSpeed;
|
||||
uint8_t twinkleDensity;
|
||||
bool coolLikeIncandescent;
|
||||
bool autoSelectBackgroundColor;
|
||||
|
||||
void drawTwinkleFox(CRGB *leds) {
|
||||
// "PRNG16" is the pseudorandom number generator
|
||||
// It MUST be reset to the same starting value each time
|
||||
// this function is called, so that the sequence of 'random'
|
||||
// numbers that it generates is (paradoxically) stable.
|
||||
uint16_t PRNG16 = 11337;
|
||||
fl::u32 clock32 = millis();
|
||||
|
||||
CRGB bg = backgroundColor;
|
||||
if (autoSelectBackgroundColor &&
|
||||
currentPalette[0] == currentPalette[1]) {
|
||||
bg = currentPalette[0];
|
||||
uint8_t bglight = bg.getAverageLight();
|
||||
if (bglight > 64) {
|
||||
bg.nscale8_video(16);
|
||||
} else if (bglight > 16) {
|
||||
bg.nscale8_video(64);
|
||||
} else {
|
||||
bg.nscale8_video(86);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t backgroundBrightness = bg.getAverageLight();
|
||||
|
||||
for (uint16_t i = 0; i < mNumLeds; i++) {
|
||||
PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384;
|
||||
uint16_t myclockoffset16 = PRNG16;
|
||||
PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384;
|
||||
uint8_t myspeedmultiplierQ5_3 =
|
||||
((((PRNG16 & 0xFF) >> 4) + (PRNG16 & 0x0F)) & 0x0F) + 0x08;
|
||||
fl::u32 myclock30 =
|
||||
(fl::u32)((clock32 * myspeedmultiplierQ5_3) >> 3) +
|
||||
myclockoffset16;
|
||||
uint8_t myunique8 = PRNG16 >> 8;
|
||||
|
||||
CRGB c = computeOneTwinkle(myclock30, myunique8);
|
||||
|
||||
uint8_t cbright = c.getAverageLight();
|
||||
int16_t deltabright = cbright - backgroundBrightness;
|
||||
if (deltabright >= 32 || (!bg)) {
|
||||
leds[i] = c;
|
||||
} else if (deltabright > 0) {
|
||||
leds[i] = blend(bg, c, deltabright * 8);
|
||||
} else {
|
||||
leds[i] = bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CRGB computeOneTwinkle(fl::u32 ms, uint8_t salt) {
|
||||
uint16_t ticks = ms >> (8 - twinkleSpeed);
|
||||
uint8_t fastcycle8 = ticks;
|
||||
uint16_t slowcycle16 = (ticks >> 8) + salt;
|
||||
slowcycle16 += sin8(slowcycle16);
|
||||
slowcycle16 = (slowcycle16 * 2053) + 1384;
|
||||
uint8_t slowcycle8 = (slowcycle16 & 0xFF) + (slowcycle16 >> 8);
|
||||
|
||||
uint8_t bright = 0;
|
||||
if (((slowcycle8 & 0x0E) / 2) < twinkleDensity) {
|
||||
bright = attackDecayWave8(fastcycle8);
|
||||
}
|
||||
|
||||
uint8_t hue = slowcycle8 - salt;
|
||||
CRGB c;
|
||||
if (bright > 0) {
|
||||
c = ColorFromPalette(currentPalette, hue, bright, NOBLEND);
|
||||
if (coolLikeIncandescent) {
|
||||
coolLikeIncandescentFunction(c, fastcycle8);
|
||||
}
|
||||
} else {
|
||||
c = CRGB::Black;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
uint8_t attackDecayWave8(uint8_t i) {
|
||||
if (i < 86) {
|
||||
return i * 3;
|
||||
} else {
|
||||
i -= 86;
|
||||
return 255 - (i + (i / 2));
|
||||
}
|
||||
}
|
||||
|
||||
void coolLikeIncandescentFunction(CRGB &c, uint8_t phase) {
|
||||
if (phase < 128)
|
||||
return;
|
||||
|
||||
uint8_t cooling = (phase - 128) >> 4;
|
||||
c.g = qsub8(c.g, cooling);
|
||||
c.b = qsub8(c.b, cooling * 2);
|
||||
}
|
||||
};
|
||||
|
||||
// Color palettes
|
||||
// Color palette definitions
|
||||
// A mostly red palette with green accents and white trim.
|
||||
// "CRGB::Gray" is used as white to keep the brightness more uniform.
|
||||
const TProgmemRGBPalette16 RedGreenWhite_p FL_PROGMEM = {
|
||||
CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red,
|
||||
CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Gray, CRGB::Gray,
|
||||
CRGB::Green, CRGB::Green, CRGB::Green, CRGB::Green};
|
||||
|
||||
const TProgmemRGBPalette16 Holly_p FL_PROGMEM = {
|
||||
0x00580c, 0x00580c, 0x00580c, 0x00580c, 0x00580c, 0x00580c,
|
||||
0x00580c, 0x00580c, 0x00580c, 0x00580c, 0x00580c, 0x00580c,
|
||||
0x00580c, 0x00580c, 0x00580c, 0xB00402};
|
||||
|
||||
const TProgmemRGBPalette16 RedWhite_p FL_PROGMEM = {
|
||||
CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Gray, CRGB::Gray,
|
||||
CRGB::Gray, CRGB::Gray, CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red,
|
||||
CRGB::Gray, CRGB::Gray, CRGB::Gray, CRGB::Gray};
|
||||
|
||||
const TProgmemRGBPalette16 BlueWhite_p FL_PROGMEM = {
|
||||
CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue,
|
||||
CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue,
|
||||
CRGB::Blue, CRGB::Gray, CRGB::Gray, CRGB::Gray};
|
||||
|
||||
const TProgmemRGBPalette16 FairyLight_p = {
|
||||
CRGB::FairyLight,
|
||||
CRGB::FairyLight,
|
||||
CRGB::FairyLight,
|
||||
CRGB::FairyLight,
|
||||
CRGB(CRGB::FairyLight).nscale8_constexpr(uint8_t(128)).as_uint32_t(),
|
||||
CRGB(CRGB::FairyLight).nscale8_constexpr(uint8_t(128)).as_uint32_t(),
|
||||
CRGB::FairyLight,
|
||||
CRGB::FairyLight,
|
||||
CRGB(CRGB::FairyLight).nscale8_constexpr(64).as_uint32_t(),
|
||||
CRGB(CRGB::FairyLight).nscale8_constexpr(64).as_uint32_t(),
|
||||
CRGB::FairyLight,
|
||||
CRGB::FairyLight,
|
||||
CRGB::FairyLight,
|
||||
CRGB::FairyLight,
|
||||
CRGB::FairyLight,
|
||||
CRGB::FairyLight};
|
||||
|
||||
const TProgmemRGBPalette16 Snow_p FL_PROGMEM = {
|
||||
0x304048, 0x304048, 0x304048, 0x304048, 0x304048, 0x304048,
|
||||
0x304048, 0x304048, 0x304048, 0x304048, 0x304048, 0x304048,
|
||||
0x304048, 0x304048, 0x304048, 0xE0F0FF};
|
||||
|
||||
const TProgmemRGBPalette16 RetroC9_p FL_PROGMEM = {
|
||||
0xB80400, 0x902C02, 0xB80400, 0x902C02, 0x902C02, 0xB80400,
|
||||
0x902C02, 0xB80400, 0x046002, 0x046002, 0x046002, 0x046002,
|
||||
0x070758, 0x070758, 0x070758, 0x606820};
|
||||
|
||||
const TProgmemRGBPalette16 Ice_p FL_PROGMEM = {
|
||||
0x0C1040, 0x0C1040, 0x0C1040, 0x0C1040, 0x0C1040, 0x0C1040,
|
||||
0x0C1040, 0x0C1040, 0x0C1040, 0x0C1040, 0x0C1040, 0x0C1040,
|
||||
0x182080, 0x182080, 0x182080, 0x5080C0};
|
||||
|
||||
// Add or remove palette names from this list to control which color
|
||||
// palettes are used, and in what order.
|
||||
const TProgmemRGBPalette16 *ActivePaletteList[] = {
|
||||
&RetroC9_p, &BlueWhite_p, &RainbowColors_p, &FairyLight_p,
|
||||
&RedGreenWhite_p, &PartyColors_p, &RedWhite_p, &Snow_p,
|
||||
&Holly_p, &Ice_p};
|
||||
|
||||
void TwinkleFox::chooseNextColorPalette(CRGBPalette16 &pal) {
|
||||
const uint8_t numberOfPalettes =
|
||||
sizeof(ActivePaletteList) / sizeof(ActivePaletteList[0]);
|
||||
static uint8_t whichPalette = -1;
|
||||
whichPalette = addmod8(whichPalette, 1, numberOfPalettes);
|
||||
pal = *(ActivePaletteList[whichPalette]);
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
Reference in new issue
Block a user