Every year at christmas time all neighbors put their LED strips out and make their houses glowing as if there no tomorrow. Last year they got me and I wanted to put an LED strip on my balcony as well. But not a simple one! I wanted a programmable RGB LED strip with a microphone to get some sound-2-light effects.
Click here for a video of the strip in action – only blue color for testing purposes. Here is another Video running some patterns at night time.
Building the strip was very simple. At first I bought the following compnents:
- Addressable RGB LED Strip (I got this cheap WS2811 from china but you can use any other) (~ 10 EUR)
- Arduino Nano v3.0 ( ~2.50 EUR )
- Sound module (a complete one or you build one yourself) ( ~ 2.00 EUR )
- 5V Power Supply (The whole configuration uses up to 1 Amp but I bought a supply with 4 Amps to be able to extend the LED strip with more lights later on) ( ~ 5 EUR)
- Cable distribution box (~ 0.5 EUR)
Before putting everything together I developed a prototype on a breadboard. The circuit layout looks like this:
The Arduino can be programmed via USB using ArduinoIDE. I used the FastLED library to rite patterns for the LED strip. Here is my source code:
#include #define NUM_LEDS 50 // number of LEDS on the strip #define DATA_PIN 3 // connection to the strip #define MIC_PIN A3 // input for the microphone // init led array CRGB leds[NUM_LEDS]; // SETUP void setup() { // set mic pin to input pinMode(MIC_PIN, INPUT); // init serial line for debugging Serial.begin(9600); // set WS2811 led strip FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS); } // HELPER FUNCTIONS // all LEDS same color void colorAll(CRGB color) { for (int i=0;i<NUM_LEDS;i++) leds[i] = color; FastLED.show(); } // dims a color to percent CRGB colorPercent(CRGB color, int percent) { if (percent > 100) percent = 100; if (percent < 0) percent = 0; return(CRGB(color.r * percent / 100, color.g * percent / 100, color.b * percent / 100)); } // random color CRGB randomColor() { return(CRGB(random8(), random8(), random8())); } // PATTERNS // Let snow flake fill the strip. void snowFlake(int wait_ms=50) { for (int pos = NUM_LEDS; pos >= 0; pos--) { for (int i = 0; i < pos; i++) { leds[i-1] = CRGB::Black; leds[i] = CRGB::White; FastLED.show(); delay(wait_ms); } } } // random led colors void randomFill(int iterations = 200, int wait_ms = 50) { while (iterations) { for (int i = 0; i < NUM_LEDS; i++) { leds[i] = randomColor(); } FastLED.show(); delay(wait_ms); iterations--; } } // random pixels void randomPixel(int iterations = 5, int wait_ms = 500) { // let leds randomly glow while (iterations) { colorAll(CRGB::Black); for (int k = 0; k < NUM_LEDS/5; k++) { leds[random8(NUM_LEDS)] = randomColor(); FastLED.show(); delay(wait_ms); } iterations--; } } // strobe light void strobe(int iterations = 10, int flash = 50) { // strobe lights while (iterations) { CRGB color = randomColor(); for(int j = 0; j < 10; j++) { colorAll(color); delay(flash); colorAll(CRGB::Black); delay(flash); } delay(1000); iterations--; } } // fades a color in and out void fadeInOut(int iterations = 5, int wait_ms = 10) { while (iterations) { CRGB color = randomColor(); for (int percent = 0; percent <= 100; percent++) { colorAll(colorPercent(color, percent)); delay(wait_ms); } for (int percent = 100; percent >= 0; percent--) { colorAll(colorPercent(color, percent)); delay(wait_ms); } iterations--; } } // Generate rainbow colors across 0-255 positions. CRGB wheelColor(int pos) { if (pos < 85) return(CRGB(pos * 3, 255 - pos * 3, 0)); if (pos < 170) { pos -= 85; return(CRGB(255 - pos * 3, 0, pos * 3)); } pos -= 170; return(CRGB(0, pos * 3, 255 - pos * 3)); } // rainbow colors void rainbow(int iterations = 3, int wait_ms = 20) { while (iterations) { for (int j = 0; j < 256; j++) { for (int i = 0; i < NUM_LEDS; i++) leds[i] = wheelColor((i+j) & 255); FastLED.show(); delay(wait_ms); } iterations --; } } // knight rider animation void knightRider(int iterations = 5, int wait_ms = 50) { while (iterations) { for (int i = 0; i < NUM_LEDS; i++) { leds[i] = CRGB::Red; for (int p = 1; p <= 5; p++) { if (i-p < 0) continue; leds[i-p] = leds[i].nscale8(250-(p*50)); } FastLED.show(); delay(wait_ms); } for (int i = NUM_LEDS; i >= 0; i--) { leds[i] = CRGB::Red; for (int p = 1; p <= 5; p++) { if (i+p > NUM_LEDS) continue; leds[i+p] = leds[i].nscale8(250-(p*50)); } FastLED.show(); delay(wait_ms); } iterations--; } } // dips on the stripe void dip(int iterations = 10, int wait_ms = 50) { while (iterations) { int pos = random8(NUM_LEDS); CRGB color = randomColor(); for (int i = 0; i <= 10; i++) { for (int j = 0; j < i; j++) { if ((i+j)*10 <= 100) { leds[pos+j] = colorPercent(color, 100-(i+j)*10); leds[pos-j] = colorPercent(color, 100-(i+j)*10); FastLED.show(); delay(wait_ms); } } } iterations--; } } // moves randomly around and stays void randomStay(int iterations = 10, int wait_ms = 200) { int oldpos = 0; while (iterations) { CRGB color = randomColor(); int pos = random(NUM_LEDS); if (pos > oldpos) { for (int i = oldpos+1; i < pos+1; i++) { leds[i-1] = CRGB::Black; leds[i] = color; FastLED.show(); delay(wait_ms * abs(pos-i)/NUM_LEDS); } } else { for (int i = oldpos-1; i > pos-1; i--) { leds[i+1] = CRGB::Black; leds[i] = color; FastLED.show(); delay(wait_ms * abs(pos-i)/NUM_LEDS); } } oldpos = pos; delay(1000); iterations--; } } // simulates fireflys void fireflys(int iterations = 200, int wait_ms = 100, int fly_count = 5) { int flys[fly_count]; for (int i = 0; i < fly_count; i++) flys[i] = random8(NUM_LEDS); while (iterations) { for (int f = 0; f < fly_count; f++) { int direction = random8(3); int pos = flys[f]; if (direction < 1 && pos > 0) { leds[pos] = CRGB::Black; pos--; leds[pos] = CRGB::Yellow; } else if (direction > 1 && pos < NUM_LEDS) { leds[pos] = CRGB::Black; pos++; leds[pos] = CRGB::Yellow; } flys[f] = pos; } FastLED.show(); delay(wait_ms); iterations--; } } // Simulates TV turning off void tvOff(int iterations = 10, int wait_ms = 10) { while (iterations) { CRGB color = randomColor(); colorAll(color); for (int i = 0; i < NUM_LEDS/2; i++) { leds[i] = CRGB::Black; leds[NUM_LEDS-i] = CRGB::Black; FastLED.show(); delay(wait_ms); } delay(100); for (int percent = 100; percent >= 0; percent--) { leds[NUM_LEDS/2-1] = colorPercent(color, percent); leds[NUM_LEDS/2] = colorPercent(color, percent); leds[NUM_LEDS/2+1] = colorPercent(color, percent); leds[NUM_LEDS/2+2] = colorPercent(color, percent); FastLED.show(); delay(wait_ms); } delay(2000); iterations--; } } // simulates rocket start void rocket(int iterations = 5) { while (iterations) { colorAll(CRGB::Black); CRGB color = randomColor(); // fired on for (int i = 0; i < 5; i++) { leds[i] = CRGB::Yellow; if (i > 0) leds[i-1] = CRGB::Red; if (i > 1) leds[i-2] = CRGB::Black; FastLED.show(); delay(500); } // start for (int i = 5; i < NUM_LEDS; i++) { leds[i] = color; if (i > 9) leds[i-10] = CRGB::Black; FastLED.show(); delay(5); } // explodes for (int i = 0; i < 20; i++) { for (int j = NUM_LEDS-i; j > NUM_LEDS-i-10; j--) { leds[j] = colorPercent(randomColor(), 100-(i*5)); FastLED.show(); delay(20-i); } leds[NUM_LEDS-i] = CRGB::Black; } iterations--; } } // sound meter void sound2light() { int oldPos = 0; while (true) { int lastval = 1024; for (int runs = 0; runs <= 100; runs++) { int val = analogRead(MIC_PIN); if (val < lastval) lastval = val; } int pos = map(lastval, 1024, 0, 0, NUM_LEDS); if (oldPos > pos) pos = oldPos-1; // First, clear the existing led values FastLED.clear(); for(int led = 0; led < pos; led++) { leds[led] = CRGB::Blue; } FastLED.show(); oldPos = pos; } } // sound lets pixels walk the strip void sound2pixel() { while (true) { int lastval = 1024; for (int runs = 0; runs <= 100; runs++) { int val = analogRead(MIC_PIN); if (val < lastval) lastval = val; } for(int i = NUM_LEDS; i > 0; i--) { leds[i] = leds[i-1]; } if (lastval < 512) leds[0] = CRGB::Blue; else leds[0] = CRGB::Black; FastLED.show(); } } // MAIN void loop() { rocket(); tvOff(); fireflys(); randomStay(); dip(); knightRider(); rainbow(); fadeInOut(); strobe(); randomPixel(); randomFill(); snowFlake(); // demo // sound2light(); // sound2pixel(); }
When I was sure that everything works properly I put the parts into the cable distribution box and used hot glue to fix it.
After everything was in position I closed the box and used isolation tape to protect it from water on the balcony – at least a bit ;-). A separate part of tape was used to secure the USB port. The final box looks like this: