#include <FastLED.h>
#define MAT_TYPE WS2812B /* Matrix LED type; see FastLED docs for others */
#define MAT_W 32 /* Size (columns) of entire matrix */
#define MAT_H 8 /* and rows */
#define NUM_LEDS 256
#define MAT_PIN 3 /* Data for matrix on D3 on ESP8266 */
#undef MAT_COL_MAJOR /* define if matrix is column-major (that is pixel 1 is in the same column as pixel 0) */
#define MAT_TOP /* define if matrix 0,0 is in top row of display; undef if bottom */
#define MAT_LEFT /* define if matrix 0,0 is on left edge of display; undef if right */
#define MAT_ZIGZAG /* define if matrix zig-zags ---> <--- ---> <---; undef if scanning ---> ---> ---> */
#define BRIGHT 120 /* brightness; min 0 - 255 max -- high brightness requires a hefty power supply! Start low! */
#define FPS 90 /* Refresh rate */
#define colorPin A2
/* MULTI-PANEL CONFIGURATION -- Do not change unless you connect multiple panels -- See README.md */
/* WARNING -- THIS IS CURRENTLY UNTESTED -- DO NOT ENABLE UNLESS YOU FEEL LIKE BEING MY CRASH TEST MANNEQUIN */
#undef MULTIPANEL /* define to enable multi-panel support */
#define PANELS_W 1 /* Number of panels wide */
#define PANELS_H 1 /* Number of panels tall */
#undef PANEL_TOP /* define if first panel is upper-left */
#undef PANEL_ZIGZAG /* define if panels zig-zag */
/* --- DO NOT CHANGE THESE LINES --- */
#ifndef MULTIPANEL
#define PANELS_H 1
#define PANELS_W 1
#undef PANEL_TOP
#undef PANEL_ZIGZAG
#endif
/****************** Digital Rain ***************************/
byte rain[NUM_LEDS];
byte counter = 1;
int speed = 1;
//const int = A0; //Rain color
/******************** Lightning ****************************/
uint8_t max_bright = 255;
uint8_t speedy = 250; // Low value = greater delay, up to 255 for no delay.
uint8_t intensity = 242; // Higher value = more flashes per strike.
/* Display size; can be smaller than matrix size, and if so, you can move the origin.
This allows you to have a small fire display on a large matrix sharing the display
with other stuff. See README at Github. */
const uint16_t rows = MAT_H * PANELS_H;
const uint16_t cols = MAT_W * PANELS_W;
const uint16_t xorg = 0;
const uint16_t yorg = 0;
/* Flare constants */
const uint8_t flarerows = 1; /* number of rows (from bottom) allowed to flare */
const uint8_t maxflare = 4; /* max number of simultaneous flares */
const uint8_t flarechance = 50; /* chance (%) of a new flare (if there's room) */
const uint8_t flaredecay = 14; /* decay rate of flare radiation; 14 is good */
/* This is the map of colors from coolest (black) to hottest. Want blue flames? Go for it! */
const uint32_t colorsRed[] = {
0x000000,
0x100000,
0x300000,
0x600000,
0x800000,
0xA00000,
0xC02000,
0xC04000,
0xC06000,
0xC08000,
0x807080
};
const uint32_t colorsBlue[] = {
0x000022,
0x000035,
0x000053,
0x000082,
0x0000CC,
0x0000FF,
0x3333FF,
0x5C5CFF,
0x9797FF,
0xCACAFF,
0xDDDDFF
};
const uint8_t NCOLORS = (sizeof(colorsRed) / sizeof(colorsRed[0]));
const uint8_t BCOLORS = (sizeof(colorsBlue) / sizeof(colorsBlue[0]));
uint8_t pix[rows][cols];
CRGB matrix[MAT_H * PANELS_H * MAT_W * PANELS_W];
uint8_t nflare = 0;
uint32_t flare[maxflare];
/** pos - convert col/row to pixel position index. This takes into account
the serpentine display, and mirroring the display so that 0,0 is the
bottom left corner and (MAT_W-1,MAT_H-1) is upper right. You may need
to jockey this around if your display is different.
*/
#ifndef MAT_LEFT
#define __MAT_RIGHT
#endif
#ifndef MAT_TOP
#define __MAT_BOTTOM
#endif
#if defined(MAT_COL_MAJOR)
const uint8_t phy_h = MAT_W;
const uint8_t phy_w = MAT_H;
#else
const uint8_t phy_h = MAT_H;
const uint8_t phy_w = MAT_W;
#endif
#if defined(MULTIPANEL)
uint16_t _pos( uint16_t col, uint16_t row ) {
#else
uint16_t pos( uint16_t col, uint16_t row ) {
#endif
#if defined(MAT_COL_MAJOR)
uint16_t phy_x = xorg + (uint16_t) row;
uint16_t phy_y = yorg + (uint16_t) col;
#else
uint16_t phy_x = xorg + (uint16_t) col;
uint16_t phy_y = yorg + (uint16_t) row;
#endif
#if defined(MAT_LEFT) && defined(MAT_ZIGZAG)
if ( ( phy_y & 1 ) == 1 ) {
phy_x = phy_w - phy_x - 1;
}
#elif defined(__MAT_RIGHT) && defined(MAT_ZIGZAG)
if ( ( phy_y & 1 ) == 0 ) {
phy_x = phy_w - phy_x - 1;
}
#elif defined(__MAT_RIGHT)
phy_x = phy_w - phy_x - 1;
#endif
#if defined(MAT_TOP) and defined(MAT_COL_MAJOR)
phy_x = phy_w - phy_x - 1;
#elif defined(MAT_TOP)
phy_y = phy_h - phy_y - 1;
#endif
return phy_x + phy_y * phy_w;
}
#if defined(MULTIPANEL)
uint16_t pos(uint16_t col, uint16_t row) {
#if defined(PANEL_TOP)
uint16_t panel_y = PANELS_H - ( row / MAT_H ) - 1;
#else
uint16_t panel_y = row / MAT_H;
#endif
uint16_t panel_x = col / MAT_W;
#if defined(PANEL_ZIGZAG)
if ( ( panel_y & 1 ) == 1 ) {
panel_x = PANELS_W - panel_x - 1;
}
#endif
uint16_t pindex = panel_x + panel_y * PANELS_W;
return MAT_W * MAT_H * pindex + _pos(col % MAT_W, row % MAT_H);
}
#endif
uint32_t isqrt(uint32_t n) {
if ( n < 2 ) return n;
uint32_t smallCandidate = isqrt(n >> 2) << 1;
uint32_t largeCandidate = smallCandidate + 1;
return (largeCandidate * largeCandidate > n) ? smallCandidate : largeCandidate;
}
// Set pixels to intensity around flare
void glow( int x, int y, int z ) {
int b = z * 10 / flaredecay + 1;
for ( int i = (y - b); i < (y + b); ++i ) {
for ( int j = (x - b); j < (x + b); ++j ) {
if ( i >= 0 && j >= 0 && i < rows && j < cols ) {
int d = ( flaredecay * isqrt((x - j) * (x - j) + (y - i) * (y - i)) + 5 ) / 10;
uint8_t n = 0;
if ( z > d ) n = z - d;
if ( n > pix[i][j] ) { // can only get brighter
pix[i][j] = n;
}
}
}
}
}
void newflare() {
if ( nflare < maxflare && random(1, 101) <= flarechance ) {
int x = random(0, cols);
int y = random(0, flarerows);
int z = NCOLORS - 1;
flare[nflare++] = (z << 16) | (y << 8) | (x & 0xff);
glow( x, y, z );
}
}
unsigned long t = 0; /* keep time */
void make_fire() {
uint16_t i, j;
if ( t > millis() ) return;
t = millis() + (1000 / FPS);
// First, move all existing heat points up the display and fade
for ( i = rows - 1; i > 0; --i ) {
for ( j = 0; j < cols; ++j ) {
uint8_t n = 0;
if ( pix[i - 1][j] > 0 )
n = pix[i - 1][j] - 1;
pix[i][j] = n;
}
}
// Heat the bottom row
for ( j = 0; j < cols; ++j ) {
i = pix[0][j];
if ( i > 0 ) {
pix[0][j] = random(NCOLORS - 6, NCOLORS - 2);
}
}
// flare
for ( i = 0; i < nflare; ++i ) {
int x = flare[i] & 0xff;
int y = (flare[i] >> 8) & 0xff;
int z = (flare[i] >> 16) & 0xff;
glow( x, y, z );
if ( z > 1 ) {
flare[i] = (flare[i] & 0xffff) | ((z - 1) << 16);
} else {
// This flare is out
for ( int j = i + 1; j < nflare; ++j ) {
flare[j - 1] = flare[j];
}
--nflare;
}
}
newflare();
// Set and draw
for ( i = 0; i < rows; ++i ) {
for ( j = 0; j < cols; ++j ) {
matrix[pos(j, i)] = colorsRed[pix[i][j]];
}
}
FastLED.show();
}
void newflareBlue() {
if ( nflare < maxflare && random(1, 101) <= flarechance ) {
int x = random(0, cols);
int y = random(0, flarerows);
int z = BCOLORS - 1;
flare[nflare++] = (z << 16) | (y << 8) | (x & 0xff);
glow( x, y, z );
}
}
void make_fireBlue() {
uint16_t i, j;
if ( t > millis() ) return;
t = millis() + (1000 / FPS);
// First, move all existing heat points up the display and fade
for ( i = rows - 1; i > 0; --i ) {
for ( j = 0; j < cols; ++j ) {
uint8_t n = 0;
if ( pix[i - 1][j] > 0 )
n = pix[i - 1][j] - 1;
pix[i][j] = n;
}
}
// Heat the bottom row
for ( j = 0; j < cols; ++j ) {
i = pix[0][j];
if ( i > 0 ) {
pix[0][j] = random(BCOLORS - 6, BCOLORS - 2);
}
}
// flare
for ( i = 0; i < nflare; ++i ) {
int x = flare[i] & 0xff;
int y = (flare[i] >> 8) & 0xff;
int z = (flare[i] >> 16) & 0xff;
glow( x, y, z );
if ( z > 1 ) {
flare[i] = (flare[i] & 0xffff) | ((z - 1) << 16);
} else {
// This flare is out
for ( int j = i + 1; j < nflare; ++j ) {
flare[j - 1] = flare[j];
}
--nflare;
}
}
newflareBlue();
// Set and draw
for ( i = 0; i < rows; ++i ) {
for ( j = 0; j < cols; ++j ) {
matrix[pos(j, i)] = colorsBlue[pix[i][j]];
}
}
FastLED.show();
}
//Buttons
const int patternSelect = 2; // Button = Pattern Select
int buttonPushCounter = 1; // counter for the number of button presses
int buttonState = 0; // current state of the button
int lastButtonState = 0;
const int Thunderstorm = 4; //Button = Initiate Thunderstorm feature
int thunderstormState = 0;
int lastthunderstormState = 0;
void setup() {
//Serial.begin(9600);
FastLED.addLeds<MAT_TYPE, MAT_PIN, GRB>(matrix, (MAT_H * PANELS_H * MAT_W * PANELS_W));
FastLED.setBrightness(BRIGHT);
FastLED.clear();
FastLED.show();
raininit();
pinMode(patternSelect, INPUT); // Pull-Down by resistor
pinMode(Thunderstorm, INPUT);
pinMode(colorPin, INPUT);
delay(2000);
}
void loop() {
buttonState = digitalRead(patternSelect);
if (buttonState != lastButtonState) {
if (buttonState == HIGH) {
buttonPushCounter++;
if (buttonPushCounter == 5) {
buttonPushCounter = 1;
}
} else {}
}
lastButtonState = buttonState;// save the current state as the last state, for next time through the loop
thunderstormState = digitalRead(Thunderstorm);
if (thunderstormState != lastthunderstormState) {
if (thunderstormState == HIGH) {
buttonPushCounter = 4;
}
} else {}
lastthunderstormState = thunderstormState;
switch (buttonPushCounter) {
case 1:
buttonPushCounter == 1; {
make_fireBlue();
// FastLED.show();
delay(60);
break;
}
case 2:
buttonPushCounter == 2; {
make_fire();
delay(60);
break;
}
case 3:
buttonPushCounter == 3; {
lightning();
FastLED.show();
break;
}
case 4:
buttonPushCounter == 4; {
EVERY_N_MILLISECONDS(100) {
updaterain();
FastLED.show();
}
EVERY_N_MILLISECONDS(20) {
changepattern();
}
FastLED.show();
break;
}
}
}
void changepattern () {
int rand1 = random16 (NUM_LEDS);
int rand2 = random16 (NUM_LEDS);
if ((rain[rand1] == 1) && (rain[rand2] == 0)) { //simple get two random dot 1 and 0 and swap it,
rain[rand1] = 0; //this will not change total number of dots
rain[rand2] = 1;
}
} //changepattern
void raininit() { //init array of dots. run once
for (int i = 0; i < NUM_LEDS; i++) {
if (random8(15) == 0) {
rain[i] = 1; //random8(20) number of dots. decrease for more dots
}
else {
rain[i] = 0;
}
}
} //raininit
void updaterain() {
for (byte i = 0; i < MAT_W; i++) {
for (byte j = 0; j < MAT_H; j++) {
byte layer = rain[XY(i, ((j + speed + MAT_H) % MAT_H))]; //fake scroll based on shift coordinate
// random8(2) add glitchy look
if (layer) {
matrix[XY((MAT_W - 1) - i, (MAT_H - 1) - j)] = CHSV(138, 205, 255);
}
}
}
speed ++;
fadeToBlackBy(matrix, NUM_LEDS, 60);
//blurRows(leds, MAT_W, MAT_H, 16); //if you want
} //updaterain
//uint16_t XY (uint8_t x, uint8_t y) {
// return (y * MAT_W + x);
//}
#define LAST_VISIBLE_LED 255
uint8_t XY (uint8_t x, uint8_t y) {
// any out of bounds address maps to the first hidden pixel
if ( (x >= MAT_W) || (y >= MAT_H) ) {
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,
63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
127, 126, 125, 124, 123, 122, 121, 120, 119, 118, 117, 116, 115, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97, 96,
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
191, 190, 189, 188, 187, 186, 185, 184, 183, 182, 181, 180, 179, 178, 177, 176, 175, 174, 173, 172, 171, 170, 169, 168, 167, 166, 165, 164, 163, 162, 161, 160,
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240, 239, 238, 237, 236, 235, 234, 233, 232, 231, 230, 229, 228, 227, 226, 225, 224
};
uint8_t i = (y * MAT_W) + x;
uint8_t j = XYTable[i];
return j;
}
void color() {
int mappedHue;
mappedHue = map(analogRead(colorPin), 0, 1023, 0, 255);
fill_solid( matrix, NUM_LEDS, CHSV(mappedHue, 255, 255));
FastLED.show();
}
void lightning() {
const uint8_t frequency = 200; // controls the interval between strikes
static uint8_t dimmer = 1;
static uint8_t flashCount = 12;
static uint8_t flashCounter = 0;
static uint32_t delayMillis = 0;
static uint32_t delayStart = 0;
static bool flashing = true;
// bail, if we haven't waited long enough
if (millis() < delayMillis + delayStart)
return;
flashing = !flashing;
if (flashCounter >= flashCount) // if we've finished the current set of flashes, clear the display and wait a bit
{
flashCounter = 0;
fill_solid(matrix, NUM_LEDS, CRGB::Black); // clear the display
delayMillis = random8(frequency) * 50;
delayStart = millis();
return;
}
if (flashCounter == 0) dimmer = 3; // the brightness of the leader is scaled down by a factor of 5
else dimmer = random8(1, 3); // return strokes are brighter than the leader
if (flashing)
{
fill_solid(matrix, NUM_LEDS, CHSV(255, 0, 255));
delayMillis = random8(3, 8);
delayStart = millis();
}
else
{
fill_solid(matrix, NUM_LEDS, CRGB::Black);
delayMillis = 40 + random8(10);
if (flashCount == 0) delayMillis += 30; // longer delay until next flash after the leader
}
//Serial.print(flashCount);
flashCounter++;
}