#include "FastLED.h"
// matrix size
#define WIDTH 8
#define HEIGHT 8
#define CentreX (WIDTH >> 1) - 1
#define CentreY (HEIGHT >> 1) -1
// NUM_LEDS = WIDTH * HEIGHT
#define PIXELPIN 23
#define NUM_LEDS 64
#define LAST_VISIBLE_LED 63
// Fire properties
// this Brighness is used as last scaling factor for the "heat".
// It means it does not only dim the fire but also shift it
// to the orange range.
#define BRIGHTNESS 255
#define FLAMEWIDTH 1 // how big a single flame should be. Bigger value, bigger flame. 0.2 - 2 without fixed borders
#define FLAMECUT 0.5 // roughness of flames in height direction. Big Values - soft flames. 0.2 - 5 without fixed borders
#define FIRST_LAYER_MINIMUM 175 // min "heat" of bottom layer
#define SECOND_LAYER_MINIMUM 25 // min "heat" of second layer
#define FIRESPEED 13 // how fast the perlin noise is parsed
#define FLAMEHEIGHT 72 // the higher the value, the higher the flame. 0-255
// Smoke screen properties
// The smoke screen works best for big fire effects. It effectively cuts of a part of the flames
// from the rest, sometimes; which looks very much fire-like. For small fire effects with low
// LED count in the height, best deactivate it
// speed must be a little different and faster from Firespeed, to be visible.
// Dimmer should be somewhere in the middle for big fires, and low for small fires.
#define SMOKESPEED 15 // how fast the smoke moves
#define SMOKE_EFFECT 112 // effect of smoke: the higher the value, the dimmer the flames. 0-255
#define CLOUDWIDTH 0.5
#define CLOUDHEIGHT 2
#define STEP_DELAY_MS 10
CRGB leds[NUM_LEDS];
// fire palette roughly like matlab "hot" colormap
// position, r, g, b value.
// max value for "position" is BRIGHTNESS
DEFINE_GRADIENT_PALETTE(hot_gp) {
0, 0, 0, 0,
4, 11, 4, 0,
8, 14, 6, 0,
12, 17, 7, 0,
16, 20, 8, 0,
20, 25, 11, 0,
24, 31, 15, 0,
28, 36, 17, 0,
32, 45, 20, 0,
36, 55, 24, 0,
40, 58, 25, 0,
44, 62, 27, 0,
48, 66, 29, 0,
52, 69, 31, 0,
56, 73, 33, 0,
60, 77, 35, 0,
64, 81, 36, 0,
68, 84, 39, 0,
72, 88, 40, 0,
76, 92, 43, 0,
80, 95, 45, 0,
85, 99, 46, 0,
89, 103, 48, 0,
93, 106, 50, 0,
97, 110, 53, 0,
101, 114, 54, 0,
105, 118, 56, 0,
109, 121, 58, 0,
113, 125, 61, 0,
117, 129, 63, 0,
121, 132, 64, 0,
125, 136, 66, 0,
129, 140, 69, 0,
133, 143, 71, 0,
137, 147, 73, 0,
141, 151, 76, 0,
145, 155, 81, 0,
149, 158, 81, 0,
153, 162, 82, 0,
157, 166, 83, 0,
161, 169, 84, 0,
165, 173, 85, 0,
170, 177, 89, 0,
174, 180, 98, 0,
178, 184, 103, 0,
182, 188, 108, 0,
186, 192, 112, 0,
190, 195, 117, 0,
194, 199, 120, 0,
198, 203, 125, 0,
202, 206, 128, 0,
206, 210, 131, 0,
210, 214, 136, 0,
214, 217, 141, 1,
218, 223, 144, 1,
222, 228, 150, 2,
226, 230, 153, 2,
230, 236, 158, 2,
234, 240, 159, 2,
238, 243, 160, 2,
242, 247, 161, 2,
246, 255, 164, 2,
250, 255, 167, 3,
255, 255, 169, 3
};
CRGBPalette32 hotPalette = hot_gp;
extern const uint8_t fire_palette[192];
uint8_t getColorFromPalette(uint8_t heatValue, uint8_t color);
uint8_t XY (uint8_t x, uint8_t y);
// parameters and buffer for the noise array
#define NUM_NOISE_LAYERS 2
// two layers of perlin noise make the fire effect
#define FIRENOISE 0
#define SMOKENOISE 1
uint32_t x_noise[NUM_NOISE_LAYERS];
uint32_t y_noise[NUM_NOISE_LAYERS];
uint32_t z_noise[NUM_NOISE_LAYERS];
uint32_t scale_x[NUM_NOISE_LAYERS];
uint32_t scale_y[NUM_NOISE_LAYERS];
uint8_t noise[NUM_NOISE_LAYERS][WIDTH][HEIGHT];
uint8_t noise2[NUM_NOISE_LAYERS][WIDTH][HEIGHT];
uint8_t heat[NUM_LEDS];
void setup() {
FastLED.addLeds<NEOPIXEL, PIXELPIN>(leds, NUM_LEDS); // Pin für Neopixel
FastLED.setBrightness(BRIGHTNESS);
FastLED.setDither(DISABLE_DITHER);
}
void loop() {
delay(STEP_DELAY_MS);
Fire2018_2();
FastLED.show();
}
void Fire2018_2() {
// some changing values
// these values are produced by perlin noise to add randomness and smooth transitions
// millis are divided by 8 to have only relevant values;
// then only the last 8 bit are used because inoise16 takes 8 bit values.
uint16_t ctrl1 = inoise16(11 * millis(), 0, 0);
uint16_t ctrl2 = inoise16(13 * millis(), 100000, 100000);
uint16_t ctrl = ((ctrl1 + ctrl2) >> 1);
// parameters for the fire heat map
x_noise[FIRENOISE] = 3 * ctrl * FIRESPEED;
y_noise[FIRENOISE] = 20 * millis() * FIRESPEED;
z_noise[FIRENOISE] = 5 * millis() * FIRESPEED;
scale_x[FIRENOISE] = ctrl1 / FLAMEWIDTH;
scale_y[FIRENOISE] = ctrl2 / FLAMECUT;
//calculate the perlin noise data for the fire
for (uint8_t x = 0; x < WIDTH; x++) {
uint32_t xoffset = scale_x[FIRENOISE] * (x - CentreX);
for (uint8_t y = 0; y < HEIGHT; y++) {
uint32_t yoffset = scale_y[FIRENOISE] * (y - CentreY);
uint16_t data = ((inoise16(x_noise[FIRENOISE] + xoffset, y_noise[FIRENOISE] + yoffset, z_noise[FIRENOISE])) + 1);
noise[FIRENOISE][x][y] = data >> 8; // make 8 bit noise from 16 bit data
}
}
// parameters for the smoke map
x_noise[SMOKENOISE] = 3 * ctrl * SMOKESPEED;
y_noise[SMOKENOISE] = 20 * millis() * SMOKESPEED;
z_noise[SMOKENOISE] = 5 * millis() * SMOKESPEED;
scale_x[SMOKENOISE] = ctrl1 / CLOUDWIDTH;
scale_y[SMOKENOISE] = ctrl2 / CLOUDHEIGHT;
//calculate the perlin noise data for the smoke
for (uint8_t x = 0; x < WIDTH; x++) {
uint32_t xoffset = scale_x[SMOKENOISE] * (x - CentreX);
for (uint8_t y = 0; y < HEIGHT; y++) {
uint32_t yoffset = scale_y[SMOKENOISE] * (y - CentreY);
uint16_t data = ((inoise16(x_noise[SMOKENOISE] + xoffset, y_noise[SMOKENOISE] + yoffset, z_noise[SMOKENOISE])) + 1);
noise[SMOKENOISE][x][y] = scale8(data>>8, 255-SMOKE_EFFECT);
}
}
//copy everything one line up
for (uint8_t y = 0; y < HEIGHT - 1; y++) {
for (uint8_t x = 0; x < WIDTH; x++) {
heat[XY(x, y)] = heat[XY(x, y + 1)];
}
}
// draw lowest line - seed the fire where it is brightest and hottest
for (uint8_t x = 0; x < WIDTH; x++) {
heat[XY(x, HEIGHT-1)] = noise[FIRENOISE][x][CentreY];
}
// dim the flames based on FIRENOISE noise.
// if the FIRENOISE noise is strong, the led goes out fast
// if the FIRENOISE noise is weak, the led stays on stronger.
// once the heat is gone, it stays dark.
for (uint8_t y = 0; y < HEIGHT-1; y++) {
for (uint8_t x = 0; x < WIDTH; x++) {
uint8_t dim = noise[FIRENOISE][x][y];
// high value in FLAMEHEIGHT = less dimming = high flames
dim = scale8(dim, (255-FLAMEHEIGHT));
dim = 255 - dim;
heat[XY(x, y)] = scale8(heat[XY(x, y)] , dim);
}
}
for (uint8_t y = 0; y < HEIGHT; y++) {
for (uint8_t x = 0; x < WIDTH; x++) {
// map the colors based on heatmap
// use the heat map to set the color of the LED from the "hot" palette
// whichpalette position brightness blend or not
//leds[XY(x, y)] = ColorFromPalette(hotPalette, heat[XY(x, y)], heat[XY(x, y)], LINEARBLEND);
leds[XY(x, y)] = ColorFromPalette(hotPalette, heat[XY(x, y)], 255, LINEARBLEND);
// dim the result based on SMOKENOISE noise
// this is not saved in the heat map - the flame may dim away and come back
// next iteration.
leds[XY(x, y)].nscale8(noise[SMOKENOISE][x][y]);
}
}
}
/*************************************************************/
/* physical LED layout here */
uint8_t XY (uint8_t x, uint8_t y) {
// any out of bounds address maps to the first hidden pixel
// https://macetech.github.io/FastLED-XY-Map-Generator/
if ( (x >= WIDTH) || (y >= HEIGHT) ) {
return (LAST_VISIBLE_LED +1);
}
const uint8_t XYTable[] = {
0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63
};
uint8_t i = (y * WIDTH) + x;
uint8_t j = XYTable[i];
return j;
}