#include <FastLED.h>
// === PINS en LED‑aantallen ===
#define PIN_STRIP1 5
#define PIN_STRIP2 18
#define PIN_STRIP3 19
#define PIN_STRIP4 21
#define LEDS_STRIP1 106
#define LEDS_STRIP2 60
#define LEDS_STRIP3 60
#define LEDS_STRIP4 60
#define NUM_LEDS (LEDS_STRIP1 + LEDS_STRIP2 + LEDS_STRIP3 + LEDS_STRIP4)
#define MAX_SECTION_LEDS 300 // pas aan aan jouw project
// === DEMO TYPES ===
enum DemoType {
DEMO_STATIC,
DEMO_BLINK,
DEMO_FADE,
DEMO_FIRE,
DEMO_THUNDER,
DEMO_RANDOMCOLOR,
DEMO_RAINBOW,
DEMO_TWINKLE,
DEMO_DUALSCAN,
DEMO_BREATH,
DEMO_RANDOMALLCOLOR,
DEMO_SCANSINGLE,
DEMO_THEATERCHASE,
DEMO_SINUS,
DEMO_FLASHSPARKLE,
DEMO_STROBE,
DEMO_BROKENLIGHT,
DEMO_LARSON,
DEMO_COMET,
DEMO_FIREWORKS,
DEMO_KNIGHTRIDER,
DEMO_KNIGHTTALK,
DEMO_WALKING
};
// Section‑achtige struct voor demo‑params (gebruik jouw d->color1 / d->intensity / d->speed)
struct DemoParams {
CRGB color;
uint8_t intensity;
uint8_t speed;
};
// Voor thunder‑state
enum ThunderPhase {
TH_IDLE,
TH_RUMBLE,
TH_STRIKES,
TH_PAUSE
};
// Voor fireworks
struct Spark {
int16_t pos;
int8_t vel;
uint8_t bri;
uint8_t hue;
};
enum FireworksPhase {
IDLE,
EXPLODE,
FADE
};
// === DEMO‑NAAM ARRAY (directe tekst‑lookup) ===
const char* demoNames[] PROGMEM = {
"STATIC",
"BLINK",
"FADE",
"FIRE",
"THUNDER",
"RANDOMCOLOR",
"RAINBOW",
"TWINKLE",
"DUALSCAN",
"BREATH",
"RANDOMALLCOLOR",
"SCANSINGLE",
"THEATERCHASE",
"SINUS",
"FLASHSPARKLE",
"STROBE",
"BROKENLIGHT",
"LARSON",
"COMET",
"FIREWORKS",
"KNIGHTRIDER",
"KNIGHTTALK",
"WALKING"
};
// === MAXIMA ===
#define MAX_SECTIONS 16 // maximum sections (total)
#define MAX_DEMOS 8 // maximum demos per section
// === STRUCTS ===
struct StripSectionDemo {
DemoType type;
uint32_t color1;
uint32_t color2;
uint16_t speed; // ms of factor
uint8_t intensity; // 0=soft, 1=medium, 2=hard
uint8_t number; // aantal LEDs (bv. twinkle count)
uint32_t playTimeMs; // -1 = altijd; >0 = duur in ms
uint32_t startTimeMs;
bool active;
};
struct StripSection {
uint16_t first;
uint16_t count;
StripSectionDemo demos[MAX_DEMOS];
uint8_t numDemos;
uint8_t currentDemo;
uint32_t nextPrintMs; // console refresh
uint32_t state; // voor vuur‑timer
};
struct LedStrip {
uint8_t pin;
uint16_t numLeds;
StripSection sections[MAX_SECTIONS];
uint8_t numSections;
uint32_t nextPrintMs; // console refresh per strip
};
CRGB leds[NUM_LEDS];
LedStrip strips[4];
uint32_t lastConsoleMs = 0;
// === HULPFUNCTIONS: kleur omzetten ===
CRGB u32toCRGB(uint32_t c) {
return CRGB((c >> 16) & 0xFF, (c >> 8) & 0xFF, c & 0xFF);
}
uint32_t crgbToU32(CRGB c) {
return ((uint32_t)c.r << 16) | ((uint32_t)c.g << 8) | c.b;
}
// === LEDSTRIP en SECTIE FUNCTIES ===
LedStrip* addLedStrip(uint8_t pin, uint16_t numLeds) {
static uint8_t stripIdx = 0;
if (stripIdx >= 4) return nullptr;
LedStrip* s = &strips[stripIdx++];
s->pin = pin;
s->numLeds = numLeds;
s->numSections = 0;
s->nextPrintMs = 0;
return s;
}
StripSection* addLedStripSection(LedStrip* strip, uint16_t first, uint16_t count) {
if (strip->numSections >= MAX_SECTIONS) return nullptr;
StripSection* sec = &strip->sections[strip->numSections++];
sec->first = first;
sec->count = count;
sec->numDemos = 0;
sec->currentDemo = 0;
sec->nextPrintMs = 0;
return sec;
}
StripSectionDemo* addLedStripSectionDemo(
StripSection* section,
DemoType type,
uint32_t color1,
uint32_t color2,
uint16_t speed,
uint8_t intensity,
uint8_t number,
uint32_t playTimeMs
) {
if (section->numDemos >= MAX_DEMOS) return nullptr;
StripSectionDemo* d = §ion->demos[section->numDemos++];
d->type = type;
d->color1 = color1;
d->color2 = color2;
d->speed = speed;
d->intensity = intensity;
d->number = number;
d->playTimeMs = playTimeMs;
d->startTimeMs = 0;
d->active = false;
return d;
}
// === SETUP ===
void setup() {
Serial.begin(115200);
Serial.println("Welcome to the test");
delay(1000);
// FastLED: 1 grote array, met offsets per strip
FastLED.addLeds<WS2812B, PIN_STRIP1, GRB>(leds, 0, LEDS_STRIP1);
FastLED.addLeds<WS2812B, PIN_STRIP2, GRB>(leds, LEDS_STRIP1, LEDS_STRIP2);
FastLED.addLeds<WS2812B, PIN_STRIP3, GRB>(leds, LEDS_STRIP1 + LEDS_STRIP2, LEDS_STRIP3);
FastLED.addLeds<WS2812B, PIN_STRIP4, GRB>(leds, LEDS_STRIP1 + LEDS_STRIP2 + LEDS_STRIP3, LEDS_STRIP4);
FastLED.setBrightness(128); // aanpasbaar
// Strip 1 – 106 LEDs
LedStrip* strip1 = addLedStrip(PIN_STRIP1, LEDS_STRIP1);
// Sectie 1: 0–20 (20 pixels)
StripSection* sec1_1 = addLedStripSection(strip1, 0, 20);
addLedStripSectionDemo(sec1_1, DEMO_STATIC, 0xFF0000, 0x000000, 0, 128, 0, 15000); // rood
addLedStripSectionDemo(sec1_1, DEMO_WALKING, 0xFFFFFF, 0, 0, 128, 0, 15000); // loop licht wit.
// Sectie 2: 20–40 (20 pixels)
StripSection* sec1_2 = addLedStripSection(strip1, 20, 20);
addLedStripSectionDemo(sec1_2, DEMO_BLINK, 0xFF0000, 0x000000, 600, 0, 0, 5000); // rood knipperen
addLedStripSectionDemo(sec1_2, DEMO_STROBE, 0xFF0000, 0xFF0000, 200, 0, 0, 5000); // rood knipperen
// Sectie 3: 40–60 (20 pixels)
StripSection* sec1_3 = addLedStripSection(strip1, 40, 20);
addLedStripSectionDemo(sec1_3, DEMO_FADE, 0xFF0000, 0x0000FF, 1000, 0, 0, 20000); // rood → blauw
addLedStripSectionDemo(sec1_3, DEMO_COMET, 0xFF0000, 0x0000FF, 500, 0, 0, 20000); // rood → blauw
// Sectie 4: 60–85 (26 pixels)
/*
StripSection* sec1_4 = addLedStripSection(strip1, 60, 26);
addLedStripSectionDemo(sec1_4, DEMO_BREATH, 0xFF0000, 0x000000, 0, 128, 0, 10000); // adem‑fade rood
addLedStripSectionDemo(sec1_4, DEMO_FIRE, 0, 0, 50, 100, 0, 10000); // vuur
addLedStripSectionDemo(sec1_4, DEMO_FLASHSPARKLE, 0xFFFFFF, 0xFF0000, 50, 100, 0, 10000);
addLedStripSectionDemo(sec1_4, DEMO_LARSON, 0xFFFFFF, 0xFF0000, 50, 100, 0, 10000);
// Sectie 5: 85–106 (22 pixels)
StripSection* sec1_5 = addLedStripSection(strip1, 85, 22);
addLedStripSectionDemo(sec1_5, DEMO_RAINBOW, 0, 0, 6, 0, 0, 15000); // regenboog
addLedStripSectionDemo(sec1_5, DEMO_TWINKLE, 0xFFFFFF, 0x000000, 200, 0, 4, 15000); // wit twinkelen
*/
// Strip 2 – 60 LEDs
LedStrip* strip2 = addLedStrip(PIN_STRIP2, LEDS_STRIP2);
// Sectie 1: 0–20 (20 pixels)
StripSection* sec2_1 = addLedStripSection(strip2, 0, 20);
addLedStripSectionDemo(sec2_1, DEMO_DUALSCAN, 0xFFFF00, 0x000000, 0, 128, 0, 15000); // geel scan
// Sectie 2: 20–40 (20 pixels)
StripSection* sec2_2 = addLedStripSection(strip2, 20, 20);
addLedStripSectionDemo(sec2_2, DEMO_FIREWORKS, 0, 0, 120, 128, 0, 12000); // fireworks
addLedStripSectionDemo(sec2_2, DEMO_KNIGHTRIDER, 0xFF0000, 0x000000, 2, 128, 0, 8000); // Knight Rider
// Sectie 3: 40–60 (20 pixels)
StripSection* sec2_3 = addLedStripSection(strip2, 40, 20);
addLedStripSectionDemo(sec2_3, DEMO_KNIGHTTALK, 0, 0, 100, 128, 0, 8000); // Knight Rider talk
addLedStripSectionDemo(sec2_3, DEMO_THUNDER, 0x0000FF, 0x000000, 700, 0, 0, 45000); // donder
// Strip 3 – 60 LEDs
LedStrip* strip3 = addLedStrip(PIN_STRIP3, LEDS_STRIP3);
// Sectie 1: 0–20 (20 pixels)
StripSection* sec3_1 = addLedStripSection(strip3, 0, 20);
addLedStripSectionDemo(sec3_1, DEMO_RANDOMCOLOR, 0, 0, 0, 128, 0, 15000); // random kleuren
addLedStripSectionDemo(sec3_1, DEMO_SINUS, 0xFF0000, 0x0000FF, 0, 128, 0, 15000); // rood en blauw
addLedStripSectionDemo(sec3_1, DEMO_SINUS, -1, 0, 0, 128, 0, 15000); // random kleuren
// Sectie 2: 20–40 (20 pixels)
StripSection* sec3_2 = addLedStripSection(strip3, 20, 20);
addLedStripSectionDemo(sec3_2, DEMO_RANDOMALLCOLOR, 0, 0, 0, 128, 0, 15000); // random alle kleuren
// Sectie 3: 40–60 (20 pixels)
StripSection* sec3_3 = addLedStripSection(strip3, 40, 20);
addLedStripSectionDemo(sec3_3, DEMO_SCANSINGLE, 0xFF0000, 0x000000, 5, 128, 0, 15000); // scan
addLedStripSectionDemo(sec3_3, DEMO_THEATERCHASE, 0xFF0000, 0x000000, 100, 128, 0, 5000); // theater
// Strip 4 – 60 LEDs (gelijk aan strip 2)
LedStrip* strip4 = addLedStrip(PIN_STRIP4, LEDS_STRIP4);
// Sectie 1: 0–20 (20 pixels)
StripSection* sec4_1 = addLedStripSection(strip4, 0, 20);
addLedStripSectionDemo(sec4_1, DEMO_STATIC, 0x0000FF, 0x000000, 0, 128, 0, 18000); // blauw
addLedStripSectionDemo(sec4_1, DEMO_THEATERCHASE, 0xFF0000, 0x000000, 100, 128, 0, 15000); // theater
// Sectie 2: 20–40 (20 pixels)
StripSection* sec4_2 = addLedStripSection(strip4, 20, 20);
addLedStripSectionDemo(sec4_2, DEMO_BLINK, 0x0000FF, 0x000000, 700, 0, 0, 12000); // blauw knipperen
// Sectie 3: 40–60 (20 pixels)
StripSection* sec4_3 = addLedStripSection(strip4, 40, 20);
addLedStripSectionDemo(sec4_3, DEMO_FADE, 0xFF0000, 0xFFFF00, 1400, 0, 0, 5000); // rood → geel
addLedStripSectionDemo(sec4_3, DEMO_RAINBOW, 0, 0, 5, 0, 0, 5000); // regenboog
addLedStripSectionDemo(sec4_3, DEMO_BROKENLIGHT, 0x00FF00, 0xFFFFFF, 500, 0, 0, 50000); // regenboog
// start alle demo’s
for (uint8_t s = 0; s < 4; s++) {
LedStrip* strip = &strips[s];
for (uint8_t i = 0; i < strip->numSections; i++) {
StripSection* sec = &strip->sections[i];
if (sec->numDemos > 0) {
StripSectionDemo* d = &sec->demos[0];
d->startTimeMs = millis();
d->active = true;
}
}
}
}
CRGB heatColor(uint8_t temperature) {
// Rescale heat from 0-255 to 0-191
byte t192 = scale8(temperature, 191);
// Figure out which third of the spectrum we're in:
if (t192 > 0x80) { // hottest (yellow‑white)
return CRGB(255, 255, scale8(t192, 255));
} else if (t192 > 0x40) { // middle (orange‑red)
return CRGB(255, scale8(t192, 255), 0);
} else { // coolest (dark red)
return CRGB(scale8(t192, 255), 0, 0);
}
}
// === LOOP ===
void loop() {
uint32_t now = millis();
// console update: 1x per seconde
if (now - lastConsoleMs >= 1000) {
lastConsoleMs = now;
for (uint8_t s = 0; s < 4; s++) {
LedStrip* strip = &strips[s];
Serial.printf("Strip %u: ", s+1);
for (uint8_t i = 0; i < strip->numSections; i++) {
StripSection* sec = &strip->sections[i];
StripSectionDemo* d = &sec->demos[sec->currentDemo];
uint32_t dur = d->playTimeMs;
uint32_t elapsed = now - d->startTimeMs;
uint32_t remaining = (dur == (uint32_t)-1 || dur == 0) ? 0 : (dur > elapsed ? dur - elapsed : 0);
uint32_t secEnd = sec->first + sec->count - 1;
// directe tekst‑lookup, geen switch
const char* name = demoNames[(int)d->type];
Serial.printf("%u[%u-%u]:%s[%us]", i+1, sec->first, secEnd, name, remaining/1000);
if (i < strip->numSections-1) Serial.print(" ");
}
Serial.println(); // einde van de strip‑regel
}
}
// update alle secties en demo’s
for (uint8_t s = 0; s < 4; s++) {
LedStrip* strip = &strips[s];
for (uint8_t i = 0; i < strip->numSections; i++) {
StripSection* sec = &strip->sections[i];
StripSectionDemo* d = &sec->demos[sec->currentDemo];
uint32_t offset = strip == &strips[0] ? 0 :
strip == &strips[1] ? LEDS_STRIP1 :
strip == &strips[2] ? LEDS_STRIP1 + LEDS_STRIP2 :
LEDS_STRIP1 + LEDS_STRIP2 + LEDS_STRIP3;
uint32_t now = millis();
uint32_t elapsed = now - d->startTimeMs;
uint32_t dur = d->playTimeMs;
bool finished = false;
if (dur != (uint32_t)-1 && dur != 0 && elapsed >= dur) {
finished = true;
}
// DEMO LOGICA (case in loop)
switch (d->type) {
case DEMO_STATIC: {
CRGB c = u32toCRGB(d->color1);
for (uint16_t j = 0; j < sec->count; j++) {
leds[offset + sec->first + j] = c;
}
break;
}
case DEMO_BLINK: {
uint32_t period = d->speed * 2;
uint32_t phase = elapsed % period;
CRGB c = u32toCRGB(d->color1);
CRGB off = CRGB::Black;
CRGB col = (phase < d->speed) ? c : off;
for (uint16_t j = 0; j < sec->count; j++) {
leds[offset + sec->first + j] = col;
}
break;
}
case DEMO_FADE: {
CRGB c1 = u32toCRGB(d->color1);
CRGB c2 = u32toCRGB(d->color2);
uint32_t period = d->speed * 2;
uint32_t phase = elapsed % period;
uint8_t step = (uint8_t)((phase * 255) / period);
if (phase >= d->speed) step = 255 - step;
CRGB c = blend(c1, c2, step);
for (uint16_t j = 0; j < sec->count; j++) {
leds[offset + sec->first + j] = c;
}
break;
}
case DEMO_FIRE: {
// ECHT vuur per sectie (Fire Flicker, Soft en Intense)
uint16_t count = sec->count;
uint16_t offset = strip == &strips[0] ? 0 :
strip == &strips[1] ? LEDS_STRIP1 :
strip == &strips[2] ? LEDS_STRIP1 + LEDS_STRIP2 :
LEDS_STRIP1 + LEDS_STRIP2 + LEDS_STRIP3;
// Per sectie een eigen heat‑array (max 30 LEDs)
static uint8_t heat[30] = {0};
// Verhoog interne tijdsteller (deltaMs = 16)
sec->state += 16;
// -------------------------
// 1. Afkoelen: laat warmte langzaam afnemen
for (int i = 0; i < count; i++) {
heat[i] = qsub8(heat[i], random8(0, 30));
}
// -------------------------
// 2. Warmte omhoog laten stijgen: verspreiding naar boven
for (int i = count - 1; i >= 2; i--) {
heat[i] = (heat[i - 1] + heat[i - 2] + heat[i - 2]) / 3;
}
// -------------------------
// 3. Nieuwe hitte aan de onderkant toevoegen
if (random8() < 120) {
heat[0] = qadd8(heat[0], random8(160, 255));
}
if (random8() < 80) {
heat[1] = qadd8(heat[1], random8(120, 200));
}
// -------------------------
// 4. Vertaal warmte naar LED‑kleur met heatColor()
for (int i = 0; i < count; i++) {
leds[offset + sec->first + i] = heatColor(heat[i]);
}
// Pas globale intensiteit toe voor zachtere verlichting
nscale8(&leds[offset + sec->first], count, d->intensity);
break;
}
case DEMO_RANDOMCOLOR: {
static uint32_t lastRandom = 0;
if (elapsed - lastRandom > 2000) {
lastRandom = elapsed;
d->color1 = random(0xFFFFFF);
}
CRGB c = u32toCRGB(d->color1);
for (uint16_t j = 0; j < sec->count; j++) {
leds[offset + sec->first + j] = c;
}
break;
}
case DEMO_RAINBOW: {
uint16_t speed = d->speed ? d->speed : 500;
uint8_t hue = (uint8_t)(elapsed / speed);
for (uint16_t j = 0; j < sec->count; j++) {
leds[offset + sec->first + j] = CHSV(hue + j * 5, 255, 255);
}
break;
}
case DEMO_TWINKLE: {
// TWINKLE: echt rustig, met tijd‑afhankelijkheid
uint8_t count = d->number ? d->number : 3; // aantal twinkelende LEDs
uint16_t speed = d->speed ? d->speed : 1000; // interval in ms (1000 = 1x per seconde)
uint32_t frame = elapsed / speed;
// alleen opnieuw kiezen als we in een nieuwe frame zijn
static uint32_t lastFrame = 0;
if (frame != lastFrame) {
lastFrame = frame;
// elke frame 1 keer een nieuwe set LEDs laten oplichten
for (uint16_t j = 0; j < sec->count; j++) {
leds[offset + sec->first + j] = CRGB::Black;
}
for (uint8_t k = 0; k < count; k++) {
uint16_t idx = random(sec->count);
leds[offset + sec->first + idx] = u32toCRGB(d->color1);
}
}
break;
}
case DEMO_DUALSCAN: {
uint16_t speed = d->speed ? d->speed : 100;
uint16_t pos1 = (elapsed / speed) % sec->count;
uint16_t pos2 = (sec->count - 1) - pos1;
for (uint16_t j = 0; j < sec->count; j++) {
leds[offset + sec->first + j] = CRGB::Black;
}
leds[offset + sec->first + pos1] = CRGB::White;
leds[offset + sec->first + pos2] = CRGB::White;
break;
}
case DEMO_BREATH: {
// DEMO_BREATH: soepele adem‑fade over hele sectie
uint16_t count = sec->count;
uint16_t offset = strip == &strips[0] ? 0 :
strip == &strips[1] ? LEDS_STRIP1 :
strip == &strips[2] ? LEDS_STRIP1 + LEDS_STRIP2 :
LEDS_STRIP1 + LEDS_STRIP2 + LEDS_STRIP3;
const uint32_t period = 3000; // 3s cyclus
float phase = float(sec->state % period) / period;
uint8_t bri = sin8(phase * 255) * d->intensity / 255;
CRGB c = u32toCRGB(d->color1);
c.fadeToBlackBy(255 - bri);
for (uint16_t i = 0; i < count; i++) {
leds[offset + sec->first + i] = c;
}
break;
}
case DEMO_RANDOMALLCOLOR: {
static uint32_t ledColors[MAX_SECTION_LEDS];
static uint32_t lastElapsed = 0;
static bool firstRun = true;
// Eerste keer OF tijd springt terug naar 0 → nieuwe random kleuren
if (firstRun || elapsed < lastElapsed) {
firstRun = false;
uint16_t count = min(sec->count, (uint16_t)MAX_SECTION_LEDS);
for (uint16_t j = 0; j < count; j++) {
ledColors[j] = random(0xFFFFFF);
}
}
lastElapsed = elapsed;
// Toon vaste kleuren
uint16_t count = min(sec->count, (uint16_t)MAX_SECTION_LEDS);
for (uint16_t j = 0; j < count; j++) {
leds[offset + sec->first + j] = u32toCRGB(ledColors[j]);
}
break;
}
case DEMO_SCANSINGLE: {
uint16_t count = sec->count;
uint16_t offset = strip == &strips[0] ? 0 :
strip == &strips[1] ? LEDS_STRIP1 :
strip == &strips[2] ? LEDS_STRIP1 + LEDS_STRIP2 :
LEDS_STRIP1 + LEDS_STRIP2 + LEDS_STRIP3;
sec->state += 16;
uint32_t stepTime = map(d->speed, 0, 255, 120, 30);
uint16_t secIdx = s * 4 + i; // max 3*4+3 = 15 → past in pos[16]
uint16_t pos = (sec->state / stepTime) % count;
// Eerst alles zwart maken in dit segment
for (uint16_t j = 0; j < count; j++) {
leds[offset + sec->first + j] = CRGB::Black;
}
// Enkel één LED aanzetten
CRGB c = u32toCRGB(d->color1);
c.nscale8(d->intensity);
leds[offset + sec->first + pos] = c;
break;
}
case DEMO_THEATERCHASE: {
// DEMO_THEATERCHASE
uint16_t count = sec->count;
uint16_t offset = strip == &strips[0] ? 0 :
strip == &strips[1] ? LEDS_STRIP1 :
strip == &strips[2] ? LEDS_STRIP1 + LEDS_STRIP2 :
LEDS_STRIP1 + LEDS_STRIP2 + LEDS_STRIP3;
sec->state += 16;
uint32_t stepTime = map(d->speed, 0, 255, 150, 30);
uint16_t pos = (sec->state / stepTime) % count;
for (uint16_t j = 0; j < count; j++) {
bool isChaseLed = (j == pos) || (j == (pos + 1) % count) || (j == (pos + 2) % count);
leds[offset + sec->first + j] = isChaseLed ? u32toCRGB(d->color1) : CRGB::Black;
}
nscale8(&leds[offset + sec->first], count, d->intensity);
break;
}
case DEMO_SINUS: {
uint16_t count = sec->count;
uint16_t offset = strip == &strips[0] ? 0 :
strip == &strips[1] ? LEDS_STRIP1 :
strip == &strips[2] ? LEDS_STRIP1 + LEDS_STRIP2 :
LEDS_STRIP1 + LEDS_STRIP2 + LEDS_STRIP3;
sec->state += 16;
for (uint16_t j = 0; j < count; j++) {
uint8_t sine = sin8((sec->state + j * 32) % 256);
CRGB c;
if (d->color1 != 0xFFFFFFFF) { // “normale” sinus met twee kleuren
// Interpolatie tussen color1 en color2
uint8_t phase = (sec->state + j * 32) % 256;
CRGB c1 = u32toCRGB(d->color1);
CRGB c2 = u32toCRGB(d->color2);
c = blend(c1, c2, phase);
} else {
// Regenboog‑mode: -1 als color1 → gebruik HSV‑sine
uint8_t baseHue = sec->state / 10;
c = CHSV(baseHue, 255, sine);
}
leds[offset + sec->first + j] = c;
}
nscale8(&leds[offset + sec->first], count, d->intensity);
break;
}
case DEMO_FLASHSPARKLE: {
// DEMO_FLASHSPARKLE
uint16_t count = sec->count;
uint16_t offset = strip == &strips[0] ? 0 :
strip == &strips[1] ? LEDS_STRIP1 :
strip == &strips[2] ? LEDS_STRIP1 + LEDS_STRIP2 :
LEDS_STRIP1 + LEDS_STRIP2 + LEDS_STRIP3;
sec->state += 16;
CRGB c = u32toCRGB(d->color1);
for (uint16_t j = 0; j < count; j++) {
leds[offset + sec->first + j] = c;
}
for (uint8_t k = 0; k < 2; k++) {
if (random8() < d->speed) {
uint16_t flashPos = random(count);
leds[offset + sec->first + flashPos] = CRGB::White;
}
}
nscale8(&leds[offset + sec->first], count, d->intensity);
break;
}
case DEMO_STROBE: {
uint16_t count = sec->count;
uint16_t offset = strip == &strips[0] ? 0 :
strip == &strips[1] ? LEDS_STRIP1 :
strip == &strips[2] ? LEDS_STRIP1 + LEDS_STRIP2 :
LEDS_STRIP1 + LEDS_STRIP2 + LEDS_STRIP3;
// Gebruik dezelfde timing‑logica als DEMO_BLINK, maar veel kortere periode
uint32_t period = d->speed; // bijv. 20–100 ms voor een echte strobe
uint32_t phase = elapsed % period;
CRGB c = u32toCRGB(d->color1);
CRGB off = CRGB::Black;
CRGB col = (phase < (period / 4)) ? c : off; // 25% duty cycle
for (uint16_t j = 0; j < count; j++) {
leds[offset + sec->first + j] = col;
}
break;
}
case DEMO_BROKENLIGHT: {
uint16_t count = sec->count;
uint16_t offset = strip == &strips[0] ? 0 :
strip == &strips[1] ? LEDS_STRIP1 :
strip == &strips[2] ? LEDS_STRIP1 + LEDS_STRIP2 :
LEDS_STRIP1 + LEDS_STRIP2 + LEDS_STRIP3;
// Statics voor hele lamp
static bool isOn = true; // huidige lampstatus
static uint16_t timer = 0; // frames tot volgende verandering
static uint8_t phase = 0; // 0=StableOn,1=ShortOff,2=Flicker
static uint8_t flickersLeft = 0;
static bool initialized = false;
if (!initialized) {
initialized = true;
isOn = true;
phase = 0;
flickersLeft = 0;
// eerste event na 10–20 sec
timer = random16(500, 1000); // 10–20 sec bij 20ms per frame
}
// Timer aftellen
if (timer > 0) {
timer--;
} else {
switch (phase) {
case 0: // Stable On
// Start een nieuwe “defectfase”
phase = 1;
isOn = false;
// korte uitval: 0.5–3 sec
timer = random16(25, 500); // 25–150 frames (~0.5–3 sec)
break;
case 1: // Short Off Phase
// Na uitval: soms direct stabiel aan, soms flikkers
if (random8() < 50) { // 50% kans
// direct stabiel aan
isOn = true;
phase = 0;
timer = random16(500, 2000); // 10–20 sec tot volgende defect
} else {
// start flikkerfase
phase = 2;
flickersLeft = random8(2, 5); // aantal snelle flikkers
isOn = true; // start flikker aan
timer = 1; // eerste flikker meteen
}
break;
case 2: // Flicker Phase
// Snelle aan/uit flikkers
isOn = !isOn;
flickersLeft--;
if (flickersLeft == 0) {
// terug naar stabiel aan
phase = 0;
isOn = true;
timer = random16(500, 1000); // 10–20 sec tot volgende defect
} else {
timer = 1 + random8(0, 2); // korte variatie tussen flikkers
}
break;
}
}
// Zet alle LEDs synchroon
CRGB c = u32toCRGB(d->color1);
c.nscale8(d->intensity);
for (uint16_t j = 0; j < count; j++) {
leds[offset + sec->first + j] = isOn ? c : CRGB::Black;
}
break;
}
case DEMO_LARSON: {
// DEMO_LARSON: demoLarsonScanner
uint16_t count = sec->count;
uint16_t offset = strip == &strips[0] ? 0 :
strip == &strips[1] ? LEDS_STRIP1 :
strip == &strips[2] ? LEDS_STRIP1 + LEDS_STRIP2 :
LEDS_STRIP1 + LEDS_STRIP2 + LEDS_STRIP3;
sec->state += 16;
uint32_t stepTime = map(d->speed, 0, 255, 120, 30);
uint16_t pos = (sec->state / stepTime) % (count * 2);
if (pos >= count) pos = count * 2 - pos - 1;
uint8_t hue = (sec->state / 5) % 256;
for (uint16_t j = 0; j < count; j++) {
int16_t dist = abs(static_cast<int16_t>(j) - static_cast<int16_t>(pos));
uint8_t tail = dist * 20 < 255 ? 255 - dist * 20 : 0;
leds[offset + sec->first + j] = CHSV(hue, 255, tail);
}
nscale8(&leds[offset + sec->first], count, d->intensity);
break;
}
case DEMO_COMET: {
// DEMO_COMET
uint16_t count = sec->count;
uint16_t offset = strip == &strips[0] ? 0 :
strip == &strips[1] ? LEDS_STRIP1 :
strip == &strips[2] ? LEDS_STRIP1 + LEDS_STRIP2 :
LEDS_STRIP1 + LEDS_STRIP2 + LEDS_STRIP3;
sec->state += 16;
uint16_t speed = map(d->speed, 0, 255, 120, 20);
uint16_t head = (sec->state / speed) % count;
uint8_t tailLen = 10;
fadeToBlackBy(&leds[offset + sec->first], count, 40);
leds[offset + sec->first + head] = u32toCRGB(d->color1);
for (uint8_t k = 1; k <= tailLen; k++) {
int16_t pos = static_cast<int16_t>(head) - k;
if (pos < 0) pos += count;
uint8_t bri = 255 - (k * 255 / tailLen);
CRGB c = u32toCRGB(d->color1);
c.nscale8_video(bri);
leds[offset + sec->first + pos] += c;
}
nscale8(&leds[offset + sec->first], count, d->intensity);
break;
}
case DEMO_FIREWORKS: {
// DEMO_FIREWORKS
uint16_t count = sec->count;
uint16_t offset = strip == &strips[0] ? 0 :
strip == &strips[1] ? LEDS_STRIP1 :
strip == &strips[2] ? LEDS_STRIP1 + LEDS_STRIP2 :
LEDS_STRIP1 + LEDS_STRIP2 + LEDS_STRIP3;
static FireworksPhase phase[3] = {IDLE, IDLE, IDLE};
static uint32_t nextEvent[3] = {0, 0, 0};
static Spark sparks[3][10];
static uint8_t sparkCount[3] = {0, 0, 0};
sec->state += 16;
uint32_t now = millis();
uint8_t secIdx = s; // max 3 strips
fadeToBlackBy(&leds[offset + sec->first], count, 30);
switch (phase[secIdx]) {
case IDLE:
if (now > nextEvent[secIdx]) {
phase[secIdx] = EXPLODE;
sparkCount[secIdx] = map(d->speed, 0, 255, 4, 8);
uint16_t center = random(count / 4, count * 3 / 4);
uint8_t baseHue = random8();
for (uint8_t k = 0; k < sparkCount[secIdx]; k++) {
sparks[secIdx][k].pos = center;
sparks[secIdx][k].vel = random(-5, 6);
sparks[secIdx][k].bri = random8(180, 255);
sparks[secIdx][k].hue = baseHue + random8(-20, 20);
}
nextEvent[secIdx] = now + random(120, 250);
}
break;
case EXPLODE:
for (uint8_t k = 0; k < sparkCount[secIdx]; k++) {
sparks[secIdx][k].pos += sparks[secIdx][k].vel;
sparks[secIdx][k].bri = qsub8(sparks[secIdx][k].bri, 18);
if (sparks[secIdx][k].pos >= 0 && sparks[secIdx][k].pos < count) {
leds[offset + sec->first + sparks[secIdx][k].pos] +=
CHSV(sparks[secIdx][k].hue, 220, sparks[secIdx][k].bri);
}
}
if (now > nextEvent[secIdx]) {
phase[secIdx] = FADE;
nextEvent[secIdx] = now + random(700, 1400);
}
break;
case FADE:
if (now > nextEvent[secIdx]) {
phase[secIdx] = IDLE;
nextEvent[secIdx] = now + random(1500, 3500);
}
break;
}
nscale8(&leds[offset + sec->first], count, d->intensity);
break;
}
case DEMO_THUNDER: {
// DEMO_THUNDER
uint16_t count = sec->count;
uint16_t offset = strip == &strips[0] ? 0 :
strip == &strips[1] ? LEDS_STRIP1 :
strip == &strips[2] ? LEDS_STRIP1 + LEDS_STRIP2 :
LEDS_STRIP1 + LEDS_STRIP2 + LEDS_STRIP3;
static struct ThunderState {
ThunderPhase phase = TH_IDLE;
uint32_t nextEvent = 0;
uint8_t flashesLeft = 0;
bool flashOn = false;
} st[3];
uint32_t now = millis();
uint8_t secIdx = s; // max 3 strips
for (uint16_t j = 0; j < count; j++) {
leds[offset + sec->first + j] = CRGB::Black;
}
switch (st[secIdx].phase) {
case TH_IDLE:
if (now >= st[secIdx].nextEvent) {
st[secIdx].phase = TH_RUMBLE;
st[secIdx].nextEvent = now + random(200, 800);
}
break;
case TH_RUMBLE:
if (now >= st[secIdx].nextEvent) {
if (random8() < 80) {
for (uint16_t j = 0; j < count; j++) {
leds[offset + sec->first + j] = CRGB(60, 60, 80);
}
nscale8_video(&leds[offset + sec->first], count, random8(20, 60));
}
if (random8() < 30) {
st[secIdx].phase = TH_STRIKES;
st[secIdx].flashesLeft = random8(2, 5);
st[secIdx].flashOn = false;
st[secIdx].nextEvent = now;
} else {
st[secIdx].nextEvent = now + random(150, 600);
}
}
break;
case TH_STRIKES:
if (now >= st[secIdx].nextEvent) {
if (!st[secIdx].flashOn) {
for (uint16_t j = 0; j < count; j++) {
leds[offset + sec->first + j] = CRGB::White;
}
nscale8_video(&leds[offset + sec->first], count, random8(180, 255));
st[secIdx].flashOn = true;
st[secIdx].nextEvent = now + random(20, 60);
} else {
st[secIdx].flashOn = false;
st[secIdx].flashesLeft--;
if (st[secIdx].flashesLeft == 0) {
st[secIdx].phase = TH_PAUSE;
st[secIdx].nextEvent = now + random(1500, 4000);
} else {
st[secIdx].nextEvent = now + random(60, 200);
}
}
}
break;
case TH_PAUSE:
if (now >= st[secIdx].nextEvent) {
st[secIdx].phase = TH_IDLE;
st[secIdx].nextEvent = now + random(4000, 12000);
}
break;
}
break;
}
case DEMO_KNIGHTRIDER: {
// DEMO_KNIGHTRIDER
uint16_t count = sec->count;
uint16_t offset = strip == &strips[0] ? 0 :
strip == &strips[1] ? LEDS_STRIP1 :
strip == &strips[2] ? LEDS_STRIP1 + LEDS_STRIP2 :
LEDS_STRIP1 + LEDS_STRIP2 + LEDS_STRIP3;
static int16_t pos[10] = {0};
static int8_t dir[10] = {1};
static uint32_t timer[10] = {0};
static bool initialized[10] = {false};
uint8_t secIdx = s * 4 + i;
if (!initialized[secIdx]) {
pos[secIdx] = 0;
dir[secIdx] = 1;
timer[secIdx] = 0;
initialized[secIdx] = true;
}
sec->state += 16;
timer[secIdx] += 16;
uint16_t stepInterval = map(d->speed, 0, 255, 140, 30);
fadeToBlackBy(&leds[offset + sec->first], count, 40);
if (timer[secIdx] >= stepInterval) {
timer[secIdx] = 0;
pos[secIdx] += dir[secIdx];
if (pos[secIdx] <= 0) {
pos[secIdx] = 0;
dir[secIdx] = 1;
} else if (pos[secIdx] >= count - 1) {
pos[secIdx] = count - 1;
dir[secIdx] = -1;
}
}
CRGB c = CRGB::Red;
c.nscale8(d->intensity);
leds[offset + sec->first + pos[secIdx]] += c;
break;
}
case DEMO_WALKING: {
uint16_t offset = strip == &strips[0] ? 0 :
strip == &strips[1] ? LEDS_STRIP1 :
strip == &strips[2] ? LEDS_STRIP1 + LEDS_STRIP2 :
LEDS_STRIP1 + LEDS_STRIP2 + LEDS_STRIP3;
uint16_t speed = d->speed ? d->speed : 200; // default walk speed if not set
uint32_t color = d->color1 ? d->color1 : 0xFFFFFF;
uint16_t count = sec->count;
uint16_t start = offset + sec->first;
// Calculate phase toggle based on elapsed time
uint16_t step = (elapsed / speed) % 2;
// Draw pattern
for (uint16_t j = 0; j < count; j++) {
if (((j + step) % 2) == 0)
leds[start + j] = color; // LED ON
else
leds[start + j] = CRGB::Black; // LED OFF
}
break;
}
case DEMO_KNIGHTTALK: {
// DEMO_KNIGHTTALK
uint16_t count = sec->count;
uint16_t offset = strip == &strips[0] ? 0 :
strip == &strips[1] ? LEDS_STRIP1 :
strip == &strips[2] ? LEDS_STRIP1 + LEDS_STRIP2 :
LEDS_STRIP1 + LEDS_STRIP2 + LEDS_STRIP3;
if (count < 3) break;
static enum {IDLE, EXPAND, CONTRACT} phase[10] = {IDLE};
static int width[10] = {0};
static int maxWidth[10] = {0};
static int8_t dir[10] = {1};
static uint32_t nextEvent[10] = {0};
uint8_t secIdx = s * 4 + i;
uint16_t center = count / 2;
sec->state += 16;
uint32_t now = millis();
fadeToBlackBy(&leds[offset + sec->first], count, count < 5 ? 20 : 40);
leds[offset + sec->first + center] = CRGB::Red;
if (phase[secIdx] == IDLE && now >= nextEvent[secIdx]) {
phase[secIdx] = EXPAND;
int upper = max(4, count / 3);
maxWidth[secIdx] = min((int)random(2, upper + 1), (int)(count - 1));
width[secIdx] = 0;
dir[secIdx] = 1;
}
if (phase[secIdx] != IDLE) {
int step = map(d->speed, 0, 255, 1, 2);
width[secIdx] += dir[secIdx] * step;
for (int k = -width[secIdx]; k <= width[secIdx]; k++) {
int pos = center + k;
if (pos < 0 || pos >= count || k == 0) continue;
int divisor = max(maxWidth[secIdx], 1);
uint8_t bri = 200 - abs(k) * (200 / divisor);
leds[offset + sec->first + pos] += CRGB(bri, 0, 0);
}
if (width[secIdx] >= maxWidth[secIdx]) dir[secIdx] = -1;
if (width[secIdx] <= 0 && dir[secIdx] == -1) {
phase[secIdx] = IDLE;
nextEvent[secIdx] = now + random(400, 1200);
}
}
break;
}
}
if (finished) {
// volgende demo in deze sectie
sec->currentDemo = (sec->currentDemo + 1) % sec->numDemos;
StripSectionDemo* next = &sec->demos[sec->currentDemo];
next->startTimeMs = now;
next->active = true;
}
}
}
FastLED.show();
delay(10); // kleine pauze om CPU te ontlasten; FastLED kan sneller, maar 50 Hz is voldoende
}