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:











