#include <FastLED.h>
const uint8_t XRES = 32;
const uint8_t YRES = 8;
const uint16_t NUM_LEDS = XRES * YRES;
const uint8_t BRIGHTNESS = 255;
const uint8_t LED_PIN = 6;
CRGB leds[NUM_LEDS];
// map x, y to pixel index
uint16_t pixel(uint8_t x, uint8_t y) {
return NUM_LEDS - XRES - (y * XRES) + x;
}
template <int16_t W, int16_t H>
class FireMatrix {
private:
struct Flare {
int16_t x;
int16_t y;
uint8_t heat;
};
// width of the fire matrix
static const int16_t WIDTH = W > 8 ? W : 8;
// height of the fire matrix
static const int16_t HEIGHT = H > 8 ? H : 8;
// max number of flares
static const uint8_t FLARES = W / 4;
// array of flares
Flare flare[FLARES];
// fire matrix 4 bit heat points
uint8_t heatPoint[WIDTH / 2][HEIGHT];
// number of flares
uint8_t flares = 0;
// store a 4 bit heat point
void putHeat(int16_t x, int16_t y, uint8_t heat) {
uint8_t h;
if (x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) return;
x >>= 1; // divide x by 2
h = heatPoint[x][y];
// if y is odd put heat in lower 4 bits else in upper 4 bits
h = y & 1 ? (h & 0xf0) | (heat & 0xf) : (h & 0xf) | (heat << 4);
heatPoint[x][y] = h;
}
// retrieve a 4 bit heat point
uint8_t getHeat(int16_t x, int16_t y) {
uint8_t h;
if (x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) return 0;
x >>= 1; // divide x by 2
h = heatPoint[x][y];
// if y is odd get heat from lower 4 bits else from upper 4 bits
h = y & 1 ? h & 0xf : (h >> 4) & 0xf;
return h;
}
protected:
// heat-up flare and update heat points
void heatFlare(uint8_t index) {
Flare f = flare[index];
int16_t b = f.heat * 10 / cooling + 1;
for (int16_t x = (f.x - b); x < (f.x + b); x++) {
for (int16_t y = (f.y - b); y < (f.y + b); y++) {
if (x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT) {
int16_t d = (cooling * sqrt16((f.x - x) * (f.x - x) + (f.y - y) * (f.y - y)) + 5) / 10;
uint8_t n = f.heat > d ? f.heat - d : 0;
if (n > getHeat(x, y)) { // can only get brighter
putHeat(x, y, n);
}
}
}
}
}
// cool down flare and delete it from flare array if out
void coolFlare(uint8_t index) {
Flare f = flare[index];
if (f.heat > 0) {
f.heat--;
flare[index] = f;
} else {
// flare is out
for (int16_t i = index + 1; i < flares; i++) {
flare[i - 1] = flare[i];
}
flares--;
}
}
// try to ignite new flare if there is room in flare array
void sparkFlare() {
if (flares < FLARES && random8(100) < sparking) {
Flare f;
f.x = random16(0, WIDTH);
f.y = random16(0, (HEIGHT / 8) + 1);
f.heat = 10;
flare[flares] = f;
flares++;
}
}
public:
// default palette based on FastLED HeatColors
CRGBPalette16 palette = CRGBPalette16(
0x000000, 0x330000, 0x660000, 0x990000, 0xCC0000, 0xFF0000, 0xFF3300, 0xFF6600,
0xFF9900, 0xFFCC00, 0xFFFF00, 0xFFFF33, 0xFFFF66, 0xFFFF99, 0xFFFFCC, 0xFFFFFF
);
// flame cool-off rate, default 16
uint8_t cooling = 16;
// chance of sparking new flame in pecent, default 50
uint8_t sparking = 50;
// call periodicly to update fire matrix
void update() {
int16_t x, y;
// move all existing heat points up the display and cool off
for (y = HEIGHT - 1; y > 0; y--) {
for (x = 0; x < WIDTH; x++) {
uint8_t h = getHeat(x, y - 1);
putHeat(x, y, h > 0 ? h - 1 : 0);
}
}
// heat-up the bottom row
for (x = 0; x < WIDTH; x++) {
putHeat(x, 0, random8(5, 9));
}
// glow and cool off flares
for (x = 0; x < flares; x++) {
// heat-up flare
heatFlare(x);
// cool-down flare
coolFlare(x);
}
// try spark a new flare
sparkFlare();
}
// get color from fire matrix
CRGB color(int16_t x, int16_t y) {
if (x >=0 && x < WIDTH && y >= 0 && y < WIDTH) {
return ColorFromPalette(palette, 24 * getHeat(x, y));
} else {
return CRGB::Black;
}
}
};
uint8_t fps = 60;
FireMatrix<XRES, YRES> fire;
void setup() {
//Serial.begin(115200);
FastLED.addLeds<NEOPIXEL, LED_PIN>(leds, NUM_LEDS);
FastLED.setBrightness(BRIGHTNESS);
FastLED.clear();
FastLED.show();
}
void loop() {
fire.update();
for (uint8_t x = 0; x < XRES; x++) {
for (uint8_t y = 0; y < YRES; y++) {
leds[pixel(x, y)] = fire.color(x, y);
}
}
FastLED.show();
delay(1000 / fps);
}