Best of FastLED Discussions
1. Fire2012: FastLED 火灾模拟器
// Fire2012: a basic fire simulation for a one-dimensional string of LEDs
// Mark Kriegsman, July 2012.
//
// Compiled size for Arduino/AVR is about 3,968 bytes.
#include <FastLED.h>
#define LED_PIN 5
#define COLOR_ORDER GRB
#define CHIPSET WS2811
#define NUM_LEDS 50
#define BRIGHTNESS 200
#define FRAMES_PER_SECOND 60
CRGB leds[NUM_LEDS];
void setup() {
delay(3000); // sanity delay
FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
FastLED.setBrightness( BRIGHTNESS );
}
void loop()
{
// Add entropy to random number generator; we use a lot of it.
random16_add_entropy( random());
Fire2012(); // run simulation frame
FastLED.show(); // display this frame
#if defined(FASTLED_VERSION) && (FASTLED_VERSION >= 2001000)
FastLED.delay(1000 / FRAMES_PER_SECOND);
#else
delay(1000 / FRAMES_PER_SECOND);
#endif
}
// 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.
//
//
// 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 55, suggested range 20-100
#define COOLING 55
// 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.
#define SPARKING 120
void Fire2012()
{
// Array of temperature readings at each simulation cell
static byte heat[NUM_LEDS];
// Step 1. Cool down every cell a little
for( int i = 0; i < NUM_LEDS; i++) {
heat[i] = qsub8( heat[i], random8(0, ((COOLING * 10) / NUM_LEDS) + 2));
}
// Step 2. Heat from each cell drifts 'up' and diffuses a little
for( int k= NUM_LEDS - 3; k > 0; 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( int j = 0; j < NUM_LEDS; j++) {
leds[j] = HeatColor( heat[j]);
}
}
// CRGB HeatColor( uint8_t temperature)
// [to be included in the forthcoming FastLED v2.1]
//
// Approximates a 'black body radiation' spectrum for
// a given 'heat' level. This is useful for animations of 'fire'.
// Heat is specified as an arbitrary scale from 0 (cool) to 255 (hot).
// This is NOT a chromatically correct 'black body radiation'
// spectrum, but it's surprisingly close, and it's extremely fast and small.
//
// On AVR/Arduino, this typically takes around 70 bytes of program memory,
// versus 768 bytes for a full 256-entry RGB lookup table.
CRGB HeatColor( uint8_t temperature)
{
CRGB heatcolor;
// Scale 'heat' down from 0-255 to 0-191,
// which can then be easily divided into three
// equal 'thirds' of 64 units each.
uint8_t t192 = scale8_video( temperature, 192);
// calculate a value that ramps up from
// zero to 255 in each 'third' of the scale.
uint8_t heatramp = t192 & 0x3F; // 0..63
heatramp <<= 2; // scale up to 0..252
// now figure out which third of the spectrum we're in:
if( t192 & 0x80) {
// we're in the hottest third
heatcolor.r = 255; // full red
heatcolor.g = 255; // full green
heatcolor.b = heatramp; // ramp up blue
} else if( t192 & 0x40 ) {
// we're in the middle third
heatcolor.r = 255; // full red
heatcolor.g = heatramp; // ramp up green
heatcolor.b = 0; // no blue
} else {
// we're in the coolest third
heatcolor.r = heatramp; // ramp up red
heatcolor.g = 0; // no green
heatcolor.b = 0; // no blue
}
return heatcolor;
}
2. 二维 XY 矩阵示例
#include <FastLED.h>
#define LED_PIN 5
#define COLOR_ORDER GRB
#define CHIPSET WS2811
#define BRIGHTNESS 32
const uint8_t kMatrixWidth = 16;
const uint8_t kMatrixHeight = 16;
const bool kMatrixSerpentineLayout = true;
#define NUM_LEDS (kMatrixWidth * kMatrixHeight)
CRGB leds[NUM_LEDS];
void setup() {
FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
FastLED.setBrightness(BRIGHTNESS);
}
void loop() {
uint32_t ms = millis();
int32_t yHueDelta32 = ((int32_t)cos16(ms * 27) * (350 / kMatrixWidth));
int32_t xHueDelta32 = ((int32_t)cos16(ms * 39) * (310 / kMatrixHeight));
DrawOneFrame(ms / 65536, yHueDelta32 / 32768, xHueDelta32 / 32768);
FastLED.show();
}
void DrawOneFrame(byte startHue8, int8_t yHueDelta8, int8_t xHueDelta8) {
byte lineStartHue = startHue8;
for (byte y = 0; y < kMatrixHeight; y++) {
lineStartHue += yHueDelta8;
byte pixelHue = lineStartHue;
for (byte x = 0; x < kMatrixWidth; x++) {
pixelHue += xHueDelta8;
leds[XY(x, y)] = CHSV(pixelHue, 255, 255);
}
}
}
// Helper function that translates from x, y into an index into the LED array
// Handles both 'row order' and 'serpentine' pixel layouts.
uint16_t XY(uint8_t x, uint8_t y) {
uint16_t i;
if (kMatrixSerpentineLayout == false) {
i = (y * kMatrixWidth) + x;
} else {
if (y & 0x01) {
// Odd rows run backwards
uint8_t reverseX = (kMatrixWidth - 1) - x;
i = (y * kMatrixWidth) + reverseX;
} else {
// Even rows run forwards
i = (y * kMatrixWidth) + x;
}
}
return i;
}
3. 小数像素渲染
#include <FastLED.h>
#define DATA_PIN 6
#define NUM_LEDS 30
CRGB leds[NUM_LEDS];
// Anti-aliased light bar example
// v1 by Mark Kriegsman <kriegsman@tr.org>, November 29, 2013
//
// This example shows the basics of using variable pixel brightness
// as a form of anti-aliasing to animate effects on a sub-pixel level,
// which is useful for effects you want to be particularly "smooth".
//
// This animation shows two bars looping around an LED strip, one moving
// in blocky whole-pixel "integer" steps, and the other one moving
// by smoothly anti-aliased "fractional" (1/16th pixel) steps.
// The use of "16ths" (vs, say, 10ths) is totally arbitrary, but 16ths are
// a good balance of data size, expressive range, and code size and speed.
//
// Notionally, "I" is the Integer Bar, "F" is the Fractional Bar.
int Ipos = NUM_LEDS / 2; // position of the "integer-based bar"
int Idelta = 1; // how many pixels to move the Integer Bar
uint8_t Ihue = 20; // color for Integer Bar
int F16pos = 0; // position of the "fraction-based bar"
int F16delta = 1; // how many 16ths of a pixel to move the Fractional Bar
uint8_t Fhue = 20; // color for Fractional Bar
int Width = 4; // width of each light bar, in whole pixels
int InterframeDelay = 40; //ms
void setup() {
delay(3000); // setup guard
FastLED.addLeds<WS2811, DATA_PIN, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(128);
}
// Draw an "Integer Bar" of light starting at pixel 'pos', with given
// width (in whole pixels) and hue.
// This is not the interesting code.
void drawIntegerBar( int intpos, int width, uint8_t hue)
{
int i = intpos; // start drawing at "I"
for( int n = 0; n < width; n++) {
leds[i] += CHSV( hue, 255, 255);
i++;
if( i == NUM_LEDS) i = 0; // wrap around
}
}
// Draw a "Fractional Bar" of light starting at position 'pos16', which is counted in
// sixteenths of a pixel from the start of the strip. Fractional positions are
// rendered using 'anti-aliasing' of pixel brightness.
// The bar width is specified in whole pixels.
// Arguably, this is the interesting code.
void drawFractionalBar( int pos16, int width, uint8_t hue)
{
int i = pos16 / 16; // convert from pos to raw pixel number
uint8_t frac = pos16 & 0x0F; // extract the 'factional' part of the position
// brightness of the first pixel in the bar is 1.0 - (fractional part of position)
// e.g., if the light bar starts drawing at pixel "57.9", then
// pixel #57 should only be lit at 10% brightness, because only 1/10th of it
// is "in" the light bar:
//
// 57.9 . . . . . . . . . . . . . . . . . 61.9
// v v
// ---+---56----+---57----+---58----+---59----+---60----+---61----+---62---->
// | | X|XXXXXXXXX|XXXXXXXXX|XXXXXXXXX|XXXXXXXX |
// ---+---------+---------+---------+---------+---------+---------+--------->
// 10% 100% 100% 100% 90%
//
// the fraction we get is in 16ths and needs to be converted to 256ths,
// so we multiply by 16. We subtract from 255 because we want a high
// fraction (e.g. 0.9) to turn into a low brightness (e.g. 0.1)
uint8_t firstpixelbrightness = 255 - (frac * 16);
// if the bar is of integer length, the last pixel's brightness is the
// reverse of the first pixel's; see illustration above.
uint8_t lastpixelbrightness = 255 - firstpixelbrightness;
// For a bar of width "N", the code has to consider "N+1" pixel positions,
// which is why the "<= width" below instead of "< width".
uint8_t bright;
for( int n = 0; n <= width; n++) {
if( n == 0) {
// first pixel in the bar
bright = firstpixelbrightness;
} else if( n == width ) {
// last pixel in the bar
bright = lastpixelbrightness;
} else {
// middle pixels
bright = 255;
}
leds[i] += CHSV( hue, 255, bright);
i++;
if( i == NUM_LEDS) i = 0; // wrap around
}
}
void loop()
{
// Update the "Fraction Bar" by 1/16th pixel every time
F16pos += F16delta;
// wrap around at end
// remember that F16pos contains position in "16ths of a pixel"
// so the 'end of the strip' is (NUM_LEDS * 16)
if( F16pos >= (NUM_LEDS * 16)) {
F16pos -= (NUM_LEDS * 16);
}
// For this demo, we want the Integer Bar and the Fraciton Bar
// to move at the same speed down the strip.
// The Fraction Bar moves 1/16th of a pixel each time through the
// loop, so to get the same speed on the strip for the Integer Bar,
// we need to move it by 1 full pixel -- but only every 16 times
// through the loop. 'countdown' is used to tell when it's time
// to advance the Integer Bar position again.
static byte countdown = 0;
if( countdown == 0) {
countdown = 16; // reset countdown
// advance Integer Bar one full pixel now
Ipos += 1;
// wrap around at end
if( Ipos >= NUM_LEDS) {
Ipos -= NUM_LEDS;
}
}
// countdown is decremented every time through the loop
countdown -= 1;
// Draw everything:
// clear the pixel buffer
memset8( leds, 0, NUM_LEDS * sizeof(CRGB));
// draw the Integer Bar, length=4px, hue=180
drawIntegerBar( Ipos, Width, Ihue);
// draw the Fractional Bar, length=4px, hue=180
drawFractionalBar( F16pos, Width, Fhue);
FastLED.show();
delay(InterframeDelay);
}