#include <FastLED.h>
#include <math.h> //Needed for some math functions.
#define LED_PIN 2
#define NUM_LEDS 60
#define BRIGHTNESS 180
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];
//Constants - tweak these to change appearance
const double MAX_SPEED = 375.0;
const double NEW_PARTICLE_PROBABILITY = 0.01;
const double PARTICLE_PREIGNITION_TIME = 0.0;
const double PARTICLE_IGNITION = 0.2;
const double PARTICLE_HOLD_TIME = 0.0;
const double PARTICLE_FADE_TIME = 2.0;
// Structure to represent a particle
struct Particle {
CRGB color;
unsigned long birthTime; // milliseconds since start
double velocity;
double position;
};
// Particle array (using a fixed-size array for simplicity on Arduino)
#define MAX_PARTICLES 50 // Limit the number of concurrent particles
Particle particles[MAX_PARTICLES];
int numParticles = 0; // Keep track of how many particles are active
// Function to add a new particle to the system
void addParticle(CRGB color, double pos, double maxSpeed) {
if (numParticles < MAX_PARTICLES) { //Check we haven't exceeded our limit.
Particle& p = particles[numParticles];
p.position = pos;
p.velocity = ((double)random(0, 1000) / 1000.0) * maxSpeed * 2 - maxSpeed; //Random value between -maxSpeed and maxSpeed;
p.color = color;
p.birthTime = millis();
numParticles++;
}
}
// Function to update all particles and remove dead ones
void updateParticles() {
for (int i = 0; i < numParticles; i++) {
Particle& p = particles[i];
unsigned long now = millis();
double age = (now - p.birthTime) / 1000.0; // Age in seconds
// Calculate time since last update (simple version for now)
double deltaTime = 0.02; // Approximate frame time (adjust as needed)
p.position += p.velocity * deltaTime;
p.velocity -= (2 * p.velocity * deltaTime);
// Fade the color. Important to do this with a temporary copy, then overwrite.
CRGB fadedColor = p.color;
// Random amount to fade
fadedColor.fadeToBlackBy((float)random(0, 101) / 1000.0f * 100);
p.color = fadedColor;
//Particle Size Calc
double fade = 0.0;
if (age > PARTICLE_PREIGNITION_TIME && age < PARTICLE_IGNITION + PARTICLE_PREIGNITION_TIME) {
p.color = CRGB::White;
}
else {
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;
}
}
p.color.fadeToBlackBy((float)(fade * 255)); //Faster Fading
}
//Particle Removal
if (age > PARTICLE_HOLD_TIME + PARTICLE_IGNITION + PARTICLE_FADE_TIME) {
// Remove the particle by shifting the rest of the array
for (int j = i; j < numParticles - 1; j++) {
particles[j] = particles[j+1];
}
numParticles--;
i--; // Re-check current index after shifting
}
}
}
// Function to draw the particles on the LED strip
void drawParticles() {
// Clear the LED strip
fill_solid(leds, NUM_LEDS, CRGB::Black);
// Draw each particle
for (int i = 0; i < numParticles; i++) {
Particle& p = particles[i];
int ledIndex = (int)round(p.position); // Get the nearest LED index
if (ledIndex >= 0 && ledIndex < NUM_LEDS) {
leds[ledIndex] = p.color; // Set the LED color to the particle's color
}
}
}
void setup() {
delay(3000); // Sanity delay
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS); // Removed setCorrection()
FastLED.setBrightness( BRIGHTNESS );
FastLED.clear();
FastLED.show();
randomSeed(analogRead(0));
}
void loop() {
//Create the particle effect on each run
if (random(0, 1000) / 1000.0 < NEW_PARTICLE_PROBABILITY) {
//Create the random colour.
CRGB color = CHSV(random(255), 255, 255);
//Get the position.
uint16_t iStartPos = random(NUM_LEDS);
int c = random(10,50);
double multiplier = (double)random(0, 1000) / 1000.0 * 3.0;
for (int i = 1; i < c; i++) {
addParticle(color, iStartPos, MAX_SPEED * (double)random(0, 1000) / 1000.0 * multiplier);
}
}
updateParticles();
drawParticles();
FastLED.show();
delay(20);
}