/**
* @title Ultimate Merged LED Effects for Arduino (Final Curated Version - v4 Corrected)
* @author A large collaboration of code, merged and adapted by an AI assistant
* @date October 2023
* @brief This is the final, curated collection of 6 LED effects. The "Dual Stereo"
* bug has been definitively fixed. All VU meter patterns now feature a
* "jumping" magenta peak.
*
* @details
* - Core Library: FastLED
* - Total Patterns: 6
* - Pattern Switching: Automatic, every `PATTERN_DURATION_SECONDS`.
* - Music Source: A built-in simulator that triggers a "beat" at random intervals,
* and forces a 400ms silent break every 4 seconds for a dynamic feel.
*/
#include <FastLED.h>
#include <math.h>
// --- CORE SETTINGS ---
#define N_PIXELS 60
#define LED_PIN 2
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
#define BRIGHTNESS 220
#define PATTERN_DURATION_SECONDS 15
// --- HELPER MACROS ---
#define N_PIXELS_HALF (N_PIXELS / 2)
#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))
// ===================================================================================
// GLOBAL VARIABLES
// ===================================================================================
CRGB leds[N_PIXELS];
// --- Pattern Cycling ---
uint8_t g_currentPatternNumber = 0;
// --- Music Simulator (Discrete Beat Model) ---
unsigned long lastBeatTime = 0;
int beatInterval = 200;
// --- Variables for the "drop" effect ---
const int SILENT_BREAK_INTERVAL_MS = 4000; // 4 seconds
const int SILENT_BREAK_DURATION_MS = 400; // 400 milliseconds
bool g_isInSilentBreak = false;
unsigned long g_silentBreakStartTime = 0;
// --- Effect State Variables (controlled by the simulator) ---
int level = 0, peak = 0;
int centerLevel = 0, centerPeak = 0;
int leftLevel = 0, leftPeak = 0;
int rightLevel = 0, rightPeak = 0;
const CRGB PEAK_COLOR = CRGB::Magenta; // The color for all VU meter peaks
// --- For color cycling / palette effects ---
uint8_t g_hue = 0;
CRGB pulse_colors[] = {CRGB::Red, CRGB::Green, CRGB::Yellow, CRGB::Pink};
const uint8_t NUM_PULSE_COLORS = 4;
uint8_t pulse_color_index = 0;
// ===================================================================================
// FORWARD DECLARATIONS of PATTERNS
// ===================================================================================
void pattern_vuCenterOut();
void pattern_vuTopDown();
void pattern_vuDualStereo();
void pattern_vuRainbow();
void pattern_colorPulse();
void pattern_vuComet();
// ===================================================================================
// PATTERN LIST
// ===================================================================================
typedef void (*Pattern)();
Pattern g_patterns[] = {
pattern_vuCenterOut,
pattern_vuTopDown,
pattern_vuDualStereo,
pattern_vuComet,
pattern_vuRainbow,
pattern_colorPulse
};
const uint8_t g_numPatterns = ARRAY_SIZE(g_patterns);
// ===================================================================================
// SETUP
// ===================================================================================
void setup() {
Serial.begin(115200);
delay(2000);
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, N_PIXELS).setCorrection(TypicalLEDStrip);
FastLED.setBrightness(BRIGHTNESS);
randomSeed(analogRead(0));
FastLED.clear();
FastLED.show();
}
// ===================================================================================
// HELPER FUNCTIONS
// ===================================================================================
CRGB getVUColor(int pos, int max_pos) {
uint8_t hue = map(pos, 0, max_pos - 1, 85, 0);
return CHSV(hue, 255, 255);
}
void addGlitter(fract8 chanceOfGlitter) {
if (random8() < chanceOfGlitter) {
leds[random16(N_PIXELS)] += CRGB::White;
}
}
// --- Functions for the new music simulator ---
void triggerBeat() {
level = random(N_PIXELS * 0.7, N_PIXELS + 1);
if (level > peak) peak = level;
centerLevel = random(N_PIXELS * 0.3, (N_PIXELS / 2) + 1);
if (centerLevel > centerPeak) centerPeak = centerLevel;
int half = N_PIXELS / 2;
leftLevel = random(half * 0.6, half + 1);
if (leftLevel > leftPeak) leftPeak = leftLevel;
rightLevel = random(half * 0.6, half + 1);
if (rightLevel > rightPeak) rightPeak = rightLevel;
pulse_color_index = (pulse_color_index + 1) % NUM_PULSE_COLORS;
}
void decayLevels() {
level *= 0.90;
centerLevel *= 0.90;
leftLevel *= 0.90;
rightLevel *= 0.90;
EVERY_N_MILLISECONDS(25) {
if (peak > 0) peak--;
if (centerPeak > 0) centerPeak--;
if (leftPeak > 0) leftPeak--;
if (rightPeak > 0) rightPeak--;
}
}
// ===================================================================================
// MAIN LOOP
// ===================================================================================
void loop() {
EVERY_N_SECONDS(PATTERN_DURATION_SECONDS) {
g_currentPatternNumber = (g_currentPatternNumber + 1) % g_numPatterns;
}
EVERY_N_MILLISECONDS(SILENT_BREAK_INTERVAL_MS) {
g_isInSilentBreak = true;
g_silentBreakStartTime = millis();
}
if (g_isInSilentBreak && (millis() - g_silentBreakStartTime > SILENT_BREAK_DURATION_MS)) {
g_isInSilentBreak = false;
}
if (!g_isInSilentBreak && (millis() - lastBeatTime > beatInterval)) {
lastBeatTime = millis();
beatInterval = random(120, 350);
triggerBeat();
}
decayLevels();
g_hue++;
g_patterns[g_currentPatternNumber]();
FastLED.show();
FastLED.delay(16);
}
// ===================================================================================
// REMAINING PATTERNS
// ===================================================================================
// PATTERN 0: Center-Out VU Meter
void pattern_vuCenterOut() {
FastLED.clear();
for (uint8_t i = 0; i < N_PIXELS_HALF; i++) {
if (i < centerLevel) {
CRGB color = getVUColor(i, N_PIXELS_HALF);
leds[N_PIXELS_HALF - 1 - i] = color;
leds[N_PIXELS_HALF + i] = color;
}
}
if (centerPeak > 0 && centerPeak < N_PIXELS_HALF) {
leds[N_PIXELS_HALF - 1 - centerPeak] = PEAK_COLOR;
leds[N_PIXELS_HALF + centerPeak] = PEAK_COLOR;
}
}
// PATTERN 1: Top-Down "Gravity" VU Meter
void pattern_vuTopDown() {
FastLED.clear();
for (int i = 0; i < N_PIXELS; i++) {
if (i > (N_PIXELS - 1 - level)) {
leds[i] = getVUColor(N_PIXELS - 1 - i, N_PIXELS);
} else if (i == (N_PIXELS - 1 - peak)) {
leds[i] = PEAK_COLOR;
}
}
}
// PATTERN 2: Dual "Stereo" VU Meters
void pattern_vuDualStereo() {
FastLED.clear();
// Left channel (pixels 0 to middle)
for (int i = 0; i < N_PIXELS_HALF; i++) {
if (i < leftLevel) leds[i] = getVUColor(i, N_PIXELS_HALF);
else if (i == leftPeak) leds[i] = PEAK_COLOR;
}
// Right channel (pixels middle to end)
for (int i = 0; i < N_PIXELS_HALF; i++) {
if (i < rightLevel) leds[N_PIXELS_HALF + i] = getVUColor(i, N_PIXELS_HALF);
else if (i == rightPeak) leds[N_PIXELS_HALF + i] = PEAK_COLOR; // DEFINITIVELY FIXED
}
}
// PATTERN 3: Peak Comet Meter
void pattern_vuComet() {
fadeToBlackBy(leds, N_PIXELS, 40);
if (level > 0) leds[level] += CRGB(200, 200, 255);
if (level > 1) leds[level-1] += CRGB(100, 100, 200);
if (peak > 0) leds[peak] = PEAK_COLOR;
}
// PATTERN 4: Normal Rainbow VU
void pattern_vuRainbow() {
FastLED.clear();
for (uint8_t i = 0; i < level; i++) {
leds[i] = CHSV(g_hue + (i * 4), 255, 255);
}
if (peak > 0 && peak < N_PIXELS) leds[peak] = PEAK_COLOR;
}
// PATTERN 5: Dynamic Color Pulse
void pattern_colorPulse() {
fadeToBlackBy(leds, N_PIXELS, 25);
int brightness = map(level, 0, N_PIXELS, 30, 255);
CRGB pulseColor = pulse_colors[pulse_color_index];
pulseColor.nscale8(brightness);
if (level > N_PIXELS * 0.85) {
leds[N_PIXELS_HALF] = pulseColor;
leds[N_PIXELS_HALF - 1] = pulseColor;
leds[N_PIXELS_HALF + 1] = pulseColor;
} else {
leds[N_PIXELS_HALF] = pulseColor;
}
for (int i = N_PIXELS - 1; i > N_PIXELS_HALF; i--) { leds[i] = leds[i - 1]; }
for (int i = 0; i < N_PIXELS_HALF; i++) { leds[i] = leds[i + 1]; }
addGlitter(map(level, 0, N_PIXELS, 0, 80));
}