#include <FastLED.h>
#include <math.h>
#define LED_PIN 2
#define NUM_LEDS 60
#define BRIGHTNESS 180
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];
// Particle behavior constants
const double MAX_SPEED = 375.0;
const double PARTICLE_PREIGNITION_TIME = 0.0;
const double PARTICLE_IGNITION = 0.3;
const double PARTICLE_HOLD_TIME = 0.0;
const double PARTICLE_FADE_TIME = 2.0;
#define MAX_PARTICLES 50
struct Particle {
CRGB color;
CRGB originalColor;
unsigned long birthTime;
double velocity;
double position;
};
Particle particles[MAX_PARTICLES];
int numParticles = 0;
CRGB colorPalette[] = {
CRGB::Red, CRGB::Green, CRGB::Blue, CRGB::Purple,
CRGB::Pink, CRGB::Yellow, CRGB::Magenta
};
const int numColors = sizeof(colorPalette) / sizeof(colorPalette[0]);
void addParticle(CRGB color, double pos, double maxSpeed) {
if (numParticles < MAX_PARTICLES) {
Particle& p = particles[numParticles];
p.position = pos;
p.velocity = ((double)random(0, 1000) / 1000.0) * maxSpeed * 2 - maxSpeed;
p.originalColor = color;
p.color = color;
p.birthTime = millis();
numParticles++;
}
}
void updateParticles() {
for (int i = 0; i < numParticles; i++) {
Particle& p = particles[i];
unsigned long now = millis();
double age = (now - p.birthTime) / 1000.0;
double deltaTime = 0.02;
p.position += p.velocity * deltaTime;
p.velocity -= (2 * p.velocity * deltaTime);
CRGB fadedColor = p.originalColor;
fadedColor.fadeToBlackBy((float)random(0, 101) / 1000.0f * 100);
// White ignition flash
if (age > PARTICLE_PREIGNITION_TIME && age < PARTICLE_IGNITION + PARTICLE_PREIGNITION_TIME) {
p.color = blend(p.originalColor, CRGB::White, 128); // Blend flash
} else {
double fade = 0.0;
if (age < PARTICLE_PREIGNITION_TIME) {
fade = 1.0 - (age / PARTICLE_PREIGNITION_TIME);
} else {
double adjustedAge = age - PARTICLE_PREIGNITION_TIME;
if (adjustedAge < PARTICLE_HOLD_TIME + PARTICLE_IGNITION) {
fade = 0.0;
} else if (adjustedAge > PARTICLE_HOLD_TIME + PARTICLE_IGNITION + PARTICLE_FADE_TIME) {
fade = 1.0;
} else {
adjustedAge -= (PARTICLE_HOLD_TIME + PARTICLE_IGNITION);
fade = adjustedAge / PARTICLE_FADE_TIME;
}
}
fadedColor.fadeToBlackBy((float)(fade * 255));
p.color = fadedColor;
}
if (age > PARTICLE_HOLD_TIME + PARTICLE_IGNITION + PARTICLE_FADE_TIME) {
for (int j = i; j < numParticles - 1; j++) {
particles[j] = particles[j + 1];
}
numParticles--;
i--;
}
}
}
void drawParticles() {
fill_solid(leds, NUM_LEDS, CRGB::Black);
for (int i = 0; i < numParticles; i++) {
Particle& p = particles[i];
int ledIndex = (int)round(p.position);
if (ledIndex >= 0 && ledIndex < NUM_LEDS) {
leds[ledIndex] = p.color;
}
}
}
unsigned long lastExplosionTime = 0;
unsigned long explosionInterval = 0;
void setup() {
Serial.begin(115200);
delay(3000);
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
FastLED.setBrightness(BRIGHTNESS);
FastLED.clear();
FastLED.show();
randomSeed(analogRead(0));
lastExplosionTime = millis();
explosionInterval = random(500, 1701);
}
void loop() {
unsigned long now = millis();
if (now - lastExplosionTime >= explosionInterval) {
CRGB color = colorPalette[random(numColors)];
int iStartPos = random(NUM_LEDS);
int particleCount = random(10, 50);
double multiplier = (double)random(0, 1000) / 1000.0 * 3.0;
for (int i = 0; i < particleCount; i++) {
addParticle(color, iStartPos, MAX_SPEED * (double)random(0, 1000) / 1000.0 * multiplier);
}
lastExplosionTime = now;
explosionInterval = random(500, 1701);
}
updateParticles();
drawParticles();
FastLED.show();
delay(20);
}