#include <Adafruit_NeoPixel.h>
#include <EEPROM.h>
#define LED_PIN 6
#define NUM_LEDS 60
#define BTN_NEXT 2
#define BTN_PREV 3
#define POT_PIN A0
Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
int currentEffect = 0;
int totalEffects = 25;
int brightness = 100;
bool lastNext = HIGH, lastPrev = HIGH;
unsigned long lastSaveTime = 0;
bool effectChanged = false;
// ==== Utility Functions ====
void showColor(uint32_t color) {
for (int i = 0; i < NUM_LEDS; i++) strip.setPixelColor(i, color);
strip.show();
}
void rainbow(int wait) {
for (long firstPixelHue = 0; firstPixelHue < 5 * 65536; firstPixelHue += 256) {
for (int i = 0; i < strip.numPixels(); i++) {
int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
}
strip.show();
delay(wait);
}
}
void colorWipe(uint32_t color, int wait) {
for (int i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, color);
strip.show();
delay(wait);
}
}
void theaterChase(uint32_t color, int wait) {
for (int a = 0; a < 10; a++) {
for (int b = 0; b < 3; b++) {
strip.clear();
for (int c = b; c < strip.numPixels(); c += 3) {
strip.setPixelColor(c, color);
}
strip.show();
delay(wait);
}
}
}
void pulseColor(uint32_t color) {
for (int i = 0; i < 255; i += 5) {
strip.setBrightness(i);
showColor(color);
delay(5);
}
for (int i = 255; i >= 0; i -= 5) {
strip.setBrightness(i);
showColor(color);
delay(5);
}
}
// ==== Special Effects ====
// Fire Effect 🔥
void fireEffect() {
for (int i = 0; i < NUM_LEDS; i++) {
int flicker = random(120, 255);
strip.setPixelColor(i, strip.Color(flicker, random(0, flicker / 2), 0));
}
strip.show();
delay(50);
}
// Police Lights 🚨
void policeEffect() {
for (int i = 0; i < NUM_LEDS; i++) {
if (i % 2 == 0)
strip.setPixelColor(i, strip.Color(255, 0, 0)); // Red
else
strip.setPixelColor(i, strip.Color(0, 0, 255)); // Blue
}
strip.show();
delay(100);
strip.clear();
strip.show();
delay(100);
}
// Meteor Effect 🌠
void meteorEffect(uint32_t color, int meteorSize, int meteorTrailDecay) {
strip.clear();
for (int i = 0; i < strip.numPixels() + strip.numPixels(); i++) {
for (int j = 0; j < strip.numPixels(); j++) {
if (random(10) > 5) {
uint32_t oldColor = strip.getPixelColor(j);
int r = (uint8_t)(oldColor >> 16);
int g = (uint8_t)(oldColor >> 8);
int b = (uint8_t)oldColor;
r = r - r * meteorTrailDecay / 256;
g = g - g * meteorTrailDecay / 256;
b = b - b * meteorTrailDecay / 256;
strip.setPixelColor(j, r, g, b);
}
}
for (int j = 0; j < meteorSize; j++) {
if ((i - j) < strip.numPixels() && (i - j) >= 0)
strip.setPixelColor(i - j, color);
}
strip.show();
delay(30);
}
}
// Wave Effect 🌊
void waveEffect() {
static uint8_t wave = 0;
for (int i = 0; i < NUM_LEDS; i++) {
int level = (sin((i + wave) * 0.2) + 1) * 127;
strip.setPixelColor(i, strip.Color(0, 0, level));
}
strip.show();
wave++;
delay(30);
}
// Rainbow Cycle 🌈
void rainbowCycle(int wait) {
for (uint16_t j = 0; j < 256 * 5; j++) {
for (uint16_t i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, strip.gamma32(strip.ColorHSV((i * 65536 / strip.numPixels()) + j * 256)));
}
strip.show();
delay(wait);
}
}
// ==== Effect Selector ====
void runEffect(int effect) {
switch (effect) {
case 0: showColor(strip.Color(255, 0, 0)); break;
case 1: showColor(strip.Color(0, 255, 0)); break;
case 2: showColor(strip.Color(0, 0, 255)); break;
case 3: rainbow(10); break;
case 4: colorWipe(strip.Color(255, 255, 0), 30); break;
case 5: colorWipe(strip.Color(0, 255, 255), 30); break;
case 6: theaterChase(strip.Color(255, 0, 0), 50); break;
case 7: theaterChase(strip.Color(0, 255, 0), 50); break;
case 8: theaterChase(strip.Color(0, 0, 255), 50); break;
case 9: pulseColor(strip.Color(255, 0, 255)); break;
case 10: pulseColor(strip.Color(255, 255, 255)); break;
case 11: rainbow(5); break;
case 12: colorWipe(strip.Color(255, 128, 0), 20); break;
case 13: colorWipe(strip.Color(128, 0, 255), 20); break;
case 14: colorWipe(strip.Color(0, 128, 255), 20); break;
case 15: pulseColor(strip.Color(255, 255, 0)); break;
case 16: pulseColor(strip.Color(0, 255, 255)); break;
case 17: theaterChase(strip.Color(255, 255, 255), 30); break;
case 18: rainbow(3); break;
case 19: showColor(strip.Color(random(255), random(255), random(255))); break;
case 20: fireEffect(); break;
case 21: policeEffect(); break;
case 22: meteorEffect(strip.Color(255, 255, 255), 10, 64); break;
case 23: waveEffect(); break;
case 24: rainbowCycle(10); break;
}
}
// ==== Setup ====
void setup() {
pinMode(BTN_NEXT, INPUT_PULLUP);
pinMode(BTN_PREV, INPUT_PULLUP);
strip.begin();
strip.show();
strip.setBrightness(brightness);
// Read last effect from EEPROM
currentEffect = EEPROM.read(0);
if (currentEffect < 0 || currentEffect >= totalEffects) currentEffect = 0;
}
// ==== Loop ====
void loop() {
bool nextState = digitalRead(BTN_NEXT);
bool prevState = digitalRead(BTN_PREV);
brightness = map(analogRead(POT_PIN), 0, 1023, 0, 255);
strip.setBrightness(brightness);
if (nextState == LOW && lastNext == HIGH) {
currentEffect++;
if (currentEffect >= totalEffects) currentEffect = 0;
effectChanged = true;
delay(200);
}
if (prevState == LOW && lastPrev == HIGH) {
currentEffect--;
if (currentEffect < 0) currentEffect = totalEffects - 1;
effectChanged = true;
delay(200);
}
lastNext = nextState;
lastPrev = prevState;
// Save to EEPROM if effect changed
if (effectChanged && millis() - lastSaveTime > 1000) {
EEPROM.update(0, currentEffect);
effectChanged = false;
lastSaveTime = millis();
}
runEffect(currentEffect);
}