cookbook/advanced/multi-strip.md
Difficulty Level: ⭐⭐⭐ Advanced Time to Complete: 40-50 minutes Prerequisites:
You'll Learn:
Control multiple LED strips independently or in coordination for larger installations and complex effects.
FastLED makes it easy to control multiple LED strips from a single microcontroller.
#define NUM_STRIPS 3
#define LEDS_PER_STRIP 60
CRGB strip1[LEDS_PER_STRIP];
CRGB strip2[LEDS_PER_STRIP];
CRGB strip3[LEDS_PER_STRIP];
void setup() {
FastLED.addLeds<WS2812B, 5, GRB>(strip1, LEDS_PER_STRIP);
FastLED.addLeds<WS2812B, 6, GRB>(strip2, LEDS_PER_STRIP);
FastLED.addLeds<WS2812B, 7, GRB>(strip3, LEDS_PER_STRIP);
}
You can also use an array of arrays for easier management:
#define NUM_STRIPS 4
#define LEDS_PER_STRIP 60
CRGB strips[NUM_STRIPS][LEDS_PER_STRIP];
void setup() {
FastLED.addLeds<WS2812B, 5, GRB>(strips[0], LEDS_PER_STRIP);
FastLED.addLeds<WS2812B, 6, GRB>(strips[1], LEDS_PER_STRIP);
FastLED.addLeds<WS2812B, 7, GRB>(strips[2], LEDS_PER_STRIP);
FastLED.addLeds<WS2812B, 8, GRB>(strips[3], LEDS_PER_STRIP);
}
Apply the same pattern to all strips simultaneously.
void syncedRainbow() {
static uint8_t hue = 0;
// Same pattern on all strips
fill_rainbow(strip1, LEDS_PER_STRIP, hue, 5);
fill_rainbow(strip2, LEDS_PER_STRIP, hue, 5);
fill_rainbow(strip3, LEDS_PER_STRIP, hue, 5);
hue++;
}
void syncedPattern() {
static uint8_t hue = 0;
for (int s = 0; s < NUM_STRIPS; s++) {
fill_rainbow(strips[s], LEDS_PER_STRIP, hue, 5);
}
hue++;
}
Each strip runs a different effect.
void independentEffects() {
// Strip 1: Rainbow
static uint8_t hue1 = 0;
fill_rainbow(strip1, LEDS_PER_STRIP, hue1, 5);
hue1++;
// Strip 2: Solid color breathing
uint8_t brightness = beatsin8(30);
fill_solid(strip2, LEDS_PER_STRIP, CHSV(160, 255, brightness));
// Strip 3: Scanner/Cylon
static uint8_t pos = 0;
fadeToBlackBy(strip3, LEDS_PER_STRIP, 20);
strip3[pos] = CRGB::Red;
pos = (pos + 1) % LEDS_PER_STRIP;
}
Create effects that move from one strip to another.
void waveAcrossStrips() {
static uint8_t pos = 0;
// Calculate position on virtual combined strip
uint16_t totalLEDs = NUM_STRIPS * LEDS_PER_STRIP;
uint16_t virtualPos = map(pos, 0, 255, 0, totalLEDs);
// Clear all
fill_solid(strip1, LEDS_PER_STRIP, CRGB::Black);
fill_solid(strip2, LEDS_PER_STRIP, CRGB::Black);
fill_solid(strip3, LEDS_PER_STRIP, CRGB::Black);
// Determine which strip and position
if (virtualPos < LEDS_PER_STRIP) {
strip1[virtualPos] = CRGB::White;
} else if (virtualPos < LEDS_PER_STRIP * 2) {
strip2[virtualPos - LEDS_PER_STRIP] = CRGB::White;
} else {
strip3[virtualPos - LEDS_PER_STRIP * 2] = CRGB::White;
}
pos++;
}
void generalizedWave() {
static uint8_t phase = 0;
for (int s = 0; s < NUM_STRIPS; s++) {
// Create wave with phase offset per strip
for (int i = 0; i < LEDS_PER_STRIP; i++) {
uint8_t brightness = sin8(phase + (s * 40) + (i * 5));
strips[s][i] = CHSV(160, 255, brightness);
}
}
phase += 2;
}
void alternatingStrips() {
static uint8_t state = 0;
EVERY_N_MILLISECONDS(500) {
state = !state;
}
if (state) {
fill_solid(strip1, LEDS_PER_STRIP, CRGB::Red);
fill_solid(strip2, LEDS_PER_STRIP, CRGB::Black);
fill_solid(strip3, LEDS_PER_STRIP, CRGB::Red);
} else {
fill_solid(strip1, LEDS_PER_STRIP, CRGB::Black);
fill_solid(strip2, LEDS_PER_STRIP, CRGB::Blue);
fill_solid(strip3, LEDS_PER_STRIP, CRGB::Black);
}
}
void chaseAcrossStrips() {
static uint8_t stripIndex = 0;
static uint8_t ledIndex = 0;
// Fade all
fadeToBlackBy(strip1, LEDS_PER_STRIP, 50);
fadeToBlackBy(strip2, LEDS_PER_STRIP, 50);
fadeToBlackBy(strip3, LEDS_PER_STRIP, 50);
// Light current position
switch(stripIndex) {
case 0: strip1[ledIndex] = CRGB::White; break;
case 1: strip2[ledIndex] = CRGB::White; break;
case 2: strip3[ledIndex] = CRGB::White; break;
}
// Advance position
ledIndex++;
if (ledIndex >= LEDS_PER_STRIP) {
ledIndex = 0;
stripIndex = (stripIndex + 1) % NUM_STRIPS;
}
}
Treat multiple strips as one continuous strip.
// Helper function to set LED on virtual strip
void setVirtualLED(uint16_t index, CRGB color) {
uint8_t stripNum = index / LEDS_PER_STRIP;
uint8_t ledNum = index % LEDS_PER_STRIP;
if (stripNum < NUM_STRIPS) {
strips[stripNum][ledNum] = color;
}
}
void virtualStripEffect() {
uint16_t totalLEDs = NUM_STRIPS * LEDS_PER_STRIP;
// Treat as single rainbow
for (uint16_t i = 0; i < totalLEDs; i++) {
uint8_t hue = map(i, 0, totalLEDs, 0, 255);
setVirtualLED(i, CHSV(hue, 255, 255));
}
}
void loop() {
updateAllStrips();
FastLED.show(); // Updates all controllers at once
delay(20);
}
FastLED.show() updates all registered controllers efficiently, so you don't need to call it separately for each strip.
Each LED uses 3 bytes (RGB). Plan accordingly:
Make sure your microcontroller has sufficient RAM.
Choose pins that support hardware features on your platform:
One strip doesn't work:
Strips flicker at different rates:
Memory issues:
Timing issues with many strips:
Create groups of strips for easier management:
void updateStripGroup(int startStrip, int endStrip, CRGB color) {
for (int s = startStrip; s <= endStrip; s++) {
fill_solid(strips[s], LEDS_PER_STRIP, color);
}
}
void groupedEffect() {
updateStripGroup(0, 1, CRGB::Red); // First two strips
updateStripGroup(2, 3, CRGB::Blue); // Next two strips
}
Mirror one strip onto another:
void mirrorStrips() {
// Copy strip1 to strip2 in reverse
for (int i = 0; i < LEDS_PER_STRIP; i++) {
strip2[i] = strip1[LEDS_PER_STRIP - 1 - i];
}
}
Next Steps: