Back to the Hippodrome - An Epic Light Show

by NSL

A few years ago we were approached by Noah Larmz for a light show for one of our favorite shows Fantastic Planet. This spawned Noah Larmz Research & Development, eventually we renamed ourselves North Street Labs a more fitting name. We worked hard designing, prototyping and building our first large scale light show sign in just under 2 months - the Fantastic Planet Sign, a wonder of soldering, programming, in-house etched circuits, and amazing team work from all our friends! After a few years having fun and creating things here at NSL, we were approached by a newly formed and renovated venue the RVA Hippodrome to commission another light show - in short time and to be totally designed by us! We were jumping with joy to be involved in yet the creation of another party of epic proportions.

We planned an idea and sketched it with Google Sketchup, they liked the idea and we ran with it! We started buying materials, proving concepts, talking to Chinese and US suppliers. It shouldn't be surprising that the USA is often cheaper for unfinished goods unless you are buying in bulk. Always talk to the local companies, they will often cut you a break if you are a bunch of locals working on a public project with little to no money to be made.

After countless hours of work, lacking sleep because of the Red Bull Creation 2012 competition, and general craziness - we finished the build in time before the bands did their sound testing last week on Sunday August 5th. And the show was a success! Everyone had a great time, Richmond discovered a brand new venue of beautiful design and new technology up there with some of the best we've ever seen. here is a clip we made of the show:

 

Lots of pics of the show:

About the build:

With our previous experience with subwoofer boxes, CNC machining and furniture creation, we designed simple yet elegant light boxes to help distribute the over 750 RGB LEDs that make up this display. Nine full sheets of 1/2" birch plywood and 3 sheets of 1/4" hardboard make up the wooden boxes, the front panels are the only things CNC'd on these boxes.

 

We ordered 44 LED/M 24IC/M LPD8806 strips from CLEN LED through Alibaba, 16 meters of them! With our budget we couldn't afford to make the show awesome enough at Adafruit's prices. Using Alibaba is extremely risky and we were hesitant, but Jack Huang an English Tourism Major working for CLEN LED was a great translator and even knew his LEDs well! You can contact Jack Huang for help here: Mr. Huang. It was a nervous order with only money order or direct bank transfer as the payment options. He assured me everything was well, and he even had them shipped to us in less than a week! They are a good company, the good guys from China, I trust them. They even included a string of 4LED/pixel squares we used in our SIMON game because I accidentally overpaid them ~$20. When you contact them, be frank, courteous and let them know what you are doing, they might actually be interested in your hackerspace project!

 

Each box consists of an 5v 5amp power supply from ebay, 3 have since failed. Do not buy the cheaper ones from china, or even the USA reseller as we did (Also they said 5v 4a on the inside! What a rip-off.) From now on we only buy nice power supplies. There is an Arduino UNO Rev 1 in every box, connected to an RS485 MAX485 breakout board. They receive data from the MASTER by a serially connected RS485 bus. And finally 86 RGB LEDs and 43 LPD8806 ICs in each box, a total of 688 RGB LEDs!

An Arduino MEGA 2560 serves as the brain of the system controlling what each light box does, it is connected to several SparkFun EL Escundo Dos boards, an MSGEQ7 7-band equalizer, an adjustable electret microphone, an RS485 differential transceiver, and a 240w 12v power supply that wasn't nearly as bad as those pesky 5v 5a ones.

 

There is an impressive amount of EL wire too, White, Blue and Red EL Wire total over 300 feet! Between several 12v high output inverters and the EL Escundo Dos boards there is stimulated lightning and strobing lighting towers! The EL wire is attached to "lightning towers" which are actually three Lightning Arrestors welded together to a 1" square tube of steel, which bolt directly to the stage. All the lightning EL Wire is strung from the main controller, up the back of the lightning towers and towards the ceiling/wall/speakers with the help of staples/Gaff tape. The effect is hard to capture but beautiful in person.

 

We had problems finding a good diffusion that wouldn't lose too much light. Baking sheet looked the most promising (especially for the cost - $5 would provide us all we needed) But it wasn't as blindingly bright, looked more like a nice multicolored lamp. We wanted to use a particular ROSCO Gel used in theatrical lighting, but that would cost well over $200, we settled on Privacy Window film two layers thick to achieve the best diffusion. The light is bright, and diffused, but it is NOT dimmed by any means, like any tracing paper/baking sheet caused. We wanted to paint/aluminum foil the interior, but time was against us. For those paying too much attention, one box was not used because of technical issues from a power supply explosion. Again, a nice power supply is EXTREMELY IMPORTANT!

 

The wood was glued, clamped, and pin-nailed over a couple of days. Then slowly the circuits were assembled, wired up, tested. A few problems were found and painfully fixed, pro tip: Make sure soldering on the strips work before removing the adhesive backing protection. Cold soldering joints are easy to do when you are trying not to overheat these strips, we had a few of these. Don't do this, it's irritating to fix if you've already installed them.

Here's a small video we made when we realized it actually worked!

 

Several sub programs were written, combined and reprogrammed to work again. We used the PS2X and EasyTransfer library from Bill Porter, and the LPD8806 library from Adafruit. The PSX controller is one of our favorites to use, there are many buttons to choose from, joysticks to manipulate and more if you dive deep enough. You can get our current working code here. This code relies on a PS2 controller connected to an Arduino and uses TX to communicate to an Arduino on the RX line, as well as using the hardware SPI on the Arduino UNO for controlling the LPD8806 strips. We needed that extra distance, so we used MAX485 transceivers to allow hundreds of feet between connections. If you have any questions, shoot us an email below.

Right now the code above consists of Smart Boxes, but a dumb command center. Below is the code we originally developed for data sending upon the RS485 network, it assumes a smart controller and dumb light towers, all the light towers should know how to do is turn 8 pixels of 24 bit RGB data into 24 bits of data for the 86 RGB LEDs. This is a simple way to control the 16,512 bits of 0s and 1s that make up a single "frame" of the entire light show. However my code below refuses to work, and I settled on the crappy code linked above. If you think you have a tip or suggestion for what is wrong with my code below, shoot me an email:Steve@NorthStreetLabs.org

NON-WORKING CODE BELOW!

MASTER:

#include <EasyTransfer.h>
#include <PS2X_lib.h> //for v1.6
#include "LPD8806.h"
#include "SPI.h"

PS2X ps2x; // create PS2 Controller Class

//right now, the library does NOT support hot pluggable controllers, meaning
//you must always either restart your Arduino after you conect the controller,
//or call config_gamepad(pins) again after connecting the controller.
int error = 0;
byte type = 0;
byte vibrate = 0;
byte ON = 255;
byte OFF = 0;

//create object
EasyTransfer ET;

struct SEND_DATA_STRUCTURE{
//put your variable definitions here for the data you want to send
//THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
byte BOX1[24];
byte BOX2[24];
byte BOX3[24];
byte BOX4[24];
byte BOX5[24];
byte BOX6[24];
byte BOX7[24];
byte BOX8[24];
};

//give a name to the group of data
SEND_DATA_STRUCTURE mydata;

void setup(){
Serial.begin(115200);

error = ps2x.config_gamepad(5,7,6,4, false, false); //setup pins and settings: GamePad(clock, command, attention, data, Pressures?, Rumble?) check for error

if(error == 0){
Serial.println("Found Controller, configured successful");
Serial.println("Try out all the buttons, X will vibrate the controller, faster as you press harder;");
Serial.println("holding L1 or R1 will print out the analog stick values.");
Serial.println("Go to www.billporter.info for updates and to report bugs.");
}

else if(error == 1)
Serial.println("No controller found, check wiring, see readme.txt to enable debug. visit www.billporter.info for troubleshooting tips");

else if(error == 2)
Serial.println("Controller found but not accepting commands. see readme.txt to enable debug. Visit www.billporter.info for troubleshooting tips");

else if(error == 3)
Serial.println("Controller refusing to enter Pressures mode, may not support it. ");

//Serial.print(ps2x.Analog(1), HEX);

type = ps2x.readType();
switch(type) {
case 0:
Serial.println("Unknown Controller type");
break;
case 1:
Serial.println("DualShock Controller Found");
break;
case 2:
Serial.println("GuitarHero Controller Found");
break;
}//END PSX SETUP


//start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc.
ET.begin(details(mydata), &Serial);

pinMode(13, OUTPUT);

randomSeed(analogRead(0));



}

void loop(){

ps2x.read_gamepad(); //read controller

for(int i = 0; i< 24; i++){
mydata.BOX1[i]=255;

}

 


//this is how you access the variables. [name of the group].[variable name]
//mydata.pause = 1;
//send the data
ET.sendData();

delay(300);

for(int i = 0; i< 24; i++){
mydata.BOX1[i]=0;

}

ET.sendData();
delay(300);
}

 

SLAVE:

#include <EasyTransfer.h>
#include "LPD8806.h"
#include "SPI.h"

// Example to control LPD8806-based RGB LED Modules in a strip

/*****************************************************************************/

// Choose which 2 pins you will use for output.
// Can be any valid output pins.

// Set the first variable to the NUMBER of pixels. 32 = 32 pixels in a row
// The LED strips are 32 LEDs per meter but you can extend/cut the strip

int NumLED=86;
// you can also use hardware SPI, for ultra fast writes by leaving out the
// data and clock pin arguments. This will 'fix' the pins to the following:
// on Arduino 168/328 thats data = 11, and clock = pin 13
// on Megas thats data = 51, and clock = 52
LPD8806 strip = LPD8806(NumLED); //fixing to pin 11, 13 with hardware spi
//create object
EasyTransfer ET;

//BEGIN EASYTRANSFER
struct RECEIVE_DATA_STRUCTURE{
//put your variable definitions here for the data you want to receive
//THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
byte BOX1[24];
byte BOX2[24];
byte BOX3[24];
byte BOX4[24];
byte BOX5[24];
byte BOX6[24];
byte BOX7[24];
byte BOX8[24];
};

//give a name to the group of data
RECEIVE_DATA_STRUCTURE mydata;

void setup(){
Serial.begin(115200);
//start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc.
ET.begin(details(mydata), &Serial);

// Start up the LED strip
strip.begin();

// Update the strip, to start they are all 'off'
strip.show();
}

void loop(){
//check and see if a data packet has come in.
if(ET.receiveData()){
//this is how you access the variables. [name of the group].[variable name]
//since we have data, we will blink it out.
for(int i = 0; i< 8; i++){
byte r = mydata.BOX1[i*3]; //set BOX1[i*3] to r variable
byte g = mydata.BOX1[i*3 + 1]; //set BOX1[i*3+1] to g variable
byte b = mydata.BOX1[i*3 + 2]; //so on....
uint32_t c = strip.Color(r, g, b); // create a pixel color to 24bit

/*This is for the LEDs on the LPD8806 strip, each TOWER consists of 86 LEDs.
The LEDs are divided into 8 sections (pixels) of either 11 or 10 rgb LEDs
pixel 0,1,3, 5, 6, 7 are 11 LEDs tall
pixels 2 & 4 are only 10 LEDs tall

so for each BOX1[x], we must send out the 24bit color data for either 11 or 10 LEDs
the code below supposedly does this, but I'm lacking sleep and may have coded wrong.
*/
if(i>=0 && i<2){ //for the first two boxes at 12 LEDs each
for(int x=0; x<12; x++){
strip.setPixelColor(i*11 + x , c); // Set pixel 0,1
}
}
if(i==2){ //for box 2 and 4 at 12 LEDs each
for(int x=22; x<32; x++){
strip.setPixelColor(x , c); // Set pixel 2
}
}
if(i==3){ //for the first two boxes at 12 LEDs each
for(int x=32; x<43; x++){
strip.setPixelColor(x , c); // Set pixel 3
}
}
if(i==4){ //for box 2 and 4 at 12 LEDs each
for(int x=43; x<53; x++){
strip.setPixelColor(x , c); // Set pixel 4
}
}
if(i==5){
for(int x=53; x<64; x++){
strip.setPixelColor(x , c); // Set pixel 5
}
}
if(i==6){
for(int x=64; x<75; x++){
strip.setPixelColor(x , c); // Set pixel 6
}
}
if(i==7){
for(int x=75; x<86; x++){
strip.setPixelColor(x , c); // Set pixel 7
}
} //END update of last LED BOX for TOWER


}//END UPDATE of DATA streamed in from EasyTransfer

}//END major data IF

strip.show(); //update the LED strips with current data

//you should make this delay shorter then your transmit delay or else messages could be lost
delay(1);//wait less time than Master waits?



}

// Create a 24 bit color value from R,G,B
uint32_t Color(byte r, byte g, byte b)
{
uint32_t c;
c = r;
c <<= 8;
c |= g;
c <<= 8;
c |= b;
return c;
}

 

Hope you enjoyed this as much as we did!